[
  {
    "path": ".cargo/config.toml",
    "content": "[build]\nrustflags = [\"--cfg\", \"tokio_unstable\"]\n\n[alias]\nbump-versions = \"run -p upgrade-version --\"\nllm = \"run --package xtask-llm-benchmark --bin llm_benchmark --\"\nci = \"run -p ci --\"\nsmoketest = \"ci smoketests --\"\nsmoketests = \"smoketest\"\n\n[target.x86_64-pc-windows-msvc]\n# Use a different linker. Otherwise, the build fails with some obscure linker error that\n# seems to be a result of us producing a massive PDB file.\n# I (@bfops) tried a variety of other link options besides switching linkers, but this\n# seems to be the only thing that worked.\nlinker = \"lld-link\"\n# Without this, the linker complains that libc functions are undefined -\n# it probably signals to rustc and lld-link that libucrt should be included.\nrustflags = [\"-Ctarget-feature=+crt-static\"]\n"
  },
  {
    "path": ".dockerignore",
    "content": "**/target\n# we do our own version pinning in the Dockerfile\nrust-toolchain.toml\n"
  },
  {
    "path": ".envrc",
    "content": "# Directory environment using https://direnv.net/ and https://github.com/nix-community/nix-direnv.\nuse flake\n"
  },
  {
    "path": ".gitattributes",
    "content": "**/module_bindings/** linguist-generated=true eol=lf\n/docs/llms/** linguist-generated=true\n/docs/llms/*-details.json linguist-generated=false\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "/crates/core/src/db/datastore/traits.rs @cloutiertyler\n/rust-toolchain.toml @cloutiertyler\n/.github/CODEOWNERS @cloutiertyler\nLICENSE @cloutiertyler\nLICENSE.txt @cloutiertyler\n/licenses/ @cloutiertyler\n/crates/client-api-messages/src/websocket.rs @centril @gefjon\n\n/crates/cli/src/ @bfops @cloutiertyler @jdetter\n/tools/ci/ @bfops @cloutiertyler @jdetter\n/tools/upgrade-version/ @bfops @jdetter\n/tools/license-check/ @bfops @jdetter\n/.github/ @bfops @jdetter\n\n/crates/sdk/examples/quickstart-chat/ @gefjon\n/modules/quickstart-chat/ @gefjon\n"
  },
  {
    "path": ".github/Dockerfile",
    "content": "# Minimal Dockerfile that just wraps pre-built binaries, so we can test the server inside docker\nFROM rust:1.93.0\nRUN mkdir -p /stdb/data\nCOPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli /usr/local/bin/\nCOPY ./crates/standalone/config.toml /stdb/data/config.toml\nRUN ln -s /usr/local/bin/spacetimedb-cli /usr/local/bin/spacetime\n"
  },
  {
    "path": ".github/GREMLINS.md",
    "content": "# GREMLINS.md — Who Lives in the Pipes\n\nThis file documents the automated agents and bots that operate on this repository.\n\n## Clawd 🔧\n\n- **What:** Anti-entropy gremlin / CI watchdog\n- **GitHub account:** `clockwork-labs-bot`\n- **Discord channel:** #gremlins (CL - SpacetimeDB)\n- **Powered by:** [OpenClaw](https://github.com/openclaw/openclaw) + Claude\n\n### What Clawd does\n\n- **Monitors CI** — watches for failures, flaky tests, and regressions\n- **Reviews PRs** — comments on obvious bugs (never approves)\n- **Surfaces stale PRs** — finds approved PRs that just need a rebase\n- **Documents testing** — creates and maintains `DEVELOP.md` files explaining CI and test infrastructure\n- **Alerts the team** — posts findings in #gremlins, pings DevOps when something needs attention\n\n### What Clawd does NOT do\n\n- Approve or merge PRs\n- Take any destructive action (delete branches, close PRs, force push)\n- Modify production infrastructure\n\n### Contacting Clawd\n\n- Mention `@Openclaw` in #gremlins on Discord\n- Tag `@clockwork-labs-bot` on GitHub PRs/issues\n\n---\n\n*To add a new gremlin, document it here.*\n"
  },
  {
    "path": ".github/docker-compose.yml",
    "content": "services:\n  node:\n    labels:\n      app: spacetimedb\n    build:\n      context: ../\n      dockerfile: .github/Dockerfile\n    ports:\n      - \"3000:3000\"\n      # Postgres\n      - \"5432:5432\"\n    entrypoint: spacetime start --pg-port 5432\n    privileged: true\n    environment:\n      RUST_BACKTRACE: 1\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "# Description of Changes\n\n<!-- Please describe your change, mention any related tickets, and so on here. -->\n\n# API and ABI breaking changes\n\n<!-- If this is an API or ABI breaking change, please apply the\ncorresponding GitHub label. -->\n\n# Expected complexity level and risk\n\n<!--\nHow complicated do you think these changes are? Grade on a scale from 1 to 5,\nwhere 1 is a trivial change, and 5 is a deep-reaching and complex change.\n\nThis complexity rating applies not only to the complexity apparent in the diff,\nbut also to its interactions with existing and future code.\n\nIf you answered more than a 2, explain what is complex about the PR,\nand what other components it interacts with in potentially concerning ways.  -->\n\n# Testing\n\n<!-- Describe any testing you've done, and any testing you'd like your reviewers to do,\nso that you're confident that all the changes work as expected! -->\n\n- [ ] <!-- maybe a test you want to do -->\n- [ ] <!-- maybe a test you want a reviewer to do, so they can check it off when they're satisfied. -->\n"
  },
  {
    "path": ".github/workflows/attach-artifacts.yml",
    "content": "name: Attach client binaries to release\n\non:\n  workflow_dispatch:\n    inputs:\n      release_tag:\n        description: \"Release tag (e.g. v1.9.0)\"\n        required: true\n\njobs:\n  upload-assets:\n    runs-on: spacetimedb-new-runner-2\n    permissions:\n      contents: write  # needed to modify releases\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Download artifacts from private base URL\n        env:\n          RELEASE_TAG: ${{ github.event.inputs.release_tag }}\n          BASE_URL: ${{ secrets.ARTIFACT_BASE_URL }}\n        run: |\n          set -euo pipefail\n\n          FULL_URL=\"$BASE_URL/$RELEASE_TAG\"\n\n          mkdir -p artifacts\n          cd artifacts\n\n          download() {\n            local filename=\"$1\"\n            if ! wget -q \"${FULL_URL}/${filename}\" -O \"${filename}\"; then\n              echo \"Failed to download ${filename}\"\n              exit 1\n            fi\n          }\n\n          download \"spacetime-aarch64-apple-darwin.tar.gz\"\n          download \"spacetime-aarch64-unknown-linux-gnu.tar.gz\"\n          download \"spacetime-x86_64-apple-darwin.tar.gz\"\n          download \"spacetime-x86_64-pc-windows-msvc.zip\"\n          download \"spacetime-x86_64-unknown-linux-gnu.tar.gz\"\n          download \"spacetimedb-update-aarch64-apple-darwin\"\n          download \"spacetimedb-update-aarch64-unknown-linux-gnu\"\n          download \"spacetimedb-update-x86_64-apple-darwin\"\n          download \"spacetimedb-update-x86_64-pc-windows-msvc.exe\"\n          download \"spacetimedb-update-x86_64-unknown-linux-gnu\"\n\n      - name: Upload artifacts to GitHub Release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          RELEASE_TAG: ${{ github.event.inputs.release_tag }}\n        run: |\n          set -euo pipefail\n\n          cd artifacts\n\n          gh release upload \"$RELEASE_TAG\" ./* \\\n            --repo \"$GITHUB_REPOSITORY\" \\\n            --clobber\n\n"
  },
  {
    "path": ".github/workflows/benchmarks.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n      - jgilles/fix-callgrind-again\n\n  workflow_dispatch:\n    inputs:\n      pr_number:\n        description: 'Pull Request Number'\n        required: false\n        default: ''\n\n  issue_comment:\n    types: [created]\n\nname: Benchmarks\n\nenv:\n  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n  GH_REPO: ${{ github.repository }}\n\njobs:\n  benchmark:\n    name: run criterion benchmarks\n    runs-on: benchmarks-runner\n    # filter for a comment containing 'benchmarks please'\n    if: ${{ github.event_name != 'issue_comment' || (github.event.issue.pull_request && contains(github.event.comment.body, 'benchmarks please')) }}\n    env:\n      PR_NUMBER: ${{ github.event.inputs.pr_number || github.event.issue.number || null }}\n    steps:\n      - name: Clear stdb dir\n        if: always()\n        run: |\n          rm -fr /stdb/*\n\n      - name: Enable CPU boost\n        run: echo \"1\" | sudo tee /sys/devices/system/cpu/cpufreq/boost\n\n      - name: Check membership\n        if: ${{ github.event_name == 'issue_comment' }}\n        env:\n          CONTRIB_ORG: clockworklabs\n          COMMENT_AUTHOR: ${{ github.event.comment.user.login }}\n          ORG_READ_TOKEN: ${{ secrets.ORG_READ_TOKEN }}\n        run: |\n          curl -OL https://github.com/cli/cli/releases/download/v2.37.0/gh_2.37.0_linux_amd64.deb && sudo dpkg -i gh_2.37.0_linux_amd64.deb\n          if [[ $(GH_TOKEN=$ORG_READ_TOKEN gh api --paginate /orgs/{owner}/members --jq 'any(.login == env.COMMENT_AUTHOR)') != true ]]; then\n            gh pr comment $PR_NUMBER -b \"Sorry, you don't have permission to run benchmarks.\"\n            exit 1\n          fi\n\n      - name: Post initial comment\n        run: |\n          if [[ $PR_NUMBER ]]; then\n            comment_parent=issues/$PR_NUMBER\n            comment_update=issues/comments\n          else\n            comment_parent=commits/$GITHUB_SHA\n            comment_update=comments\n          fi\n          comment_body=\"Benchmark in progress...\"\n          comment_id=$(gh api \"/repos/{owner}/{repo}/$comment_parent/comments\" -f body=\"$comment_body\" --jq .id)\n          echo \"COMMENT_UPDATE_URL=/repos/{owner}/{repo}/$comment_update/$comment_id\" >>$GITHUB_ENV\n\n      - name: find PR branch\n        if: ${{ env.PR_NUMBER }}\n        run: echo \"PR_REF=$(gh pr view $PR_NUMBER --json headRefName --jq .headRefName)\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.PR_REF || github.ref }}\n          # if we're on master we want to know what the sha of HEAD~1 is so\n          # that we can compare results from it to HEAD (in the \"Fetch markdown\n          # summary PR\" step). otherwise, we can use a fully shallow checkout\n          fetch-depth: ${{ env.PR_NUMBER && 1 || 2 }}\n\n      - name: Install Rust toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          components: clippy\n          toolchain: stable\n          target: wasm32-unknown-unknown\n          override: true\n\n      - name: Install .NET toolchain\n        uses: actions/setup-dotnet@v3\n        with:\n          global-json-file: global.json\n        env:\n          DOTNET_INSTALL_DIR: ~/.dotnet\n\n      - name: Build\n        working-directory: crates/bench/\n        run: |\n          cargo build --release\n\n      - name: Install latest wasm-opt for module optimisations\n        run: |\n          curl https://github.com/WebAssembly/binaryen/releases/download/version_116/binaryen-version_116-x86_64-linux.tar.gz -L | sudo tar xz -C /usr/local --strip-components=1\n\n      - name: Disable CPU boost\n        run: echo \"0\" | sudo tee /sys/devices/system/cpu/cpufreq/boost\n\n      - name: Branch; run bench\n        run: |\n          if [[ $PR_NUMBER ]]; then\n            BASELINE_NAME=branch\n            RESULTS_NAME=pr-$PR_NUMBER\n            BENCH_FILTER='stdb_raw'\n            echo \"Running benchmarks without sqlite\"\n          else\n            BASELINE_NAME=master\n            RESULTS_NAME=$GITHUB_SHA\n            BENCH_FILTER='(stdb_raw|sqlite)'\n            echo \"Running benchmarks with sqlite\"\n          fi\n          pushd crates/bench\n          rm -rf .spacetime\n          cargo bench --bench generic -- --save-baseline \"$BASELINE_NAME\" \"$BENCH_FILTER\"\n          # sticker price benchmark\n          cargo bench --bench generic -- --save-baseline \"$BASELINE_NAME\" 'stdb_module/.*/disk/update_bulk'\n          cargo bench --bench special -- --save-baseline \"$BASELINE_NAME\"\n          cargo run --bin summarize pack \"$BASELINE_NAME\"\n          popd\n          mkdir criterion-results\n          [[ ! $PR_NUMBER ]] && cp target/criterion/$BASELINE_NAME.json criterion-results/\n          cp target/criterion/$BASELINE_NAME.json criterion-results/$RESULTS_NAME.json\n\n      # this will work for both PR and master\n      - name: Upload criterion results to DO spaces\n        uses: shallwefootball/s3-upload-action@master\n        with:\n          aws_key_id: ${{ secrets.AWS_KEY_ID }}\n          aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}\n          aws_bucket: \"spacetimedb-ci-benchmarks\"\n          source_dir: criterion-results\n          endpoint: https://nyc3.digitaloceanspaces.com\n          destination_dir: benchmarks\n\n      - name: Fetch markdown summary PR\n        run: |\n          if [[ $PR_NUMBER ]]; then\n            OLD=master\n            NEW=pr-$PR_NUMBER\n          else\n            OLD=$(git rev-parse HEAD~1)\n            NEW=$GITHUB_SHA\n          fi\n          echo \"fetching https://benchmarks.spacetimedb.com/compare/$OLD/$NEW\"\n          curl -sS https://benchmarks.spacetimedb.com/compare/$OLD/$NEW > report.md\n\n      - name: Post comment\n        run: |\n          BODY=\"<details><summary>Criterion benchmark results</summary>\n\n          $(cat report.md)\n\n          </details>\"\n\n          gh api \"$COMMENT_UPDATE_URL\" -X PATCH -f body=\"$BODY\"\n\n      - name: Post failure comment\n        if: ${{ failure() && env.COMMENT_UPDATE_URL }}\n        run: |\n          BODY=\"Benchmarking failed. Please check [the workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.\"\n          gh api \"$COMMENT_UPDATE_URL\" -X PATCH -f body=\"$BODY\"\n\n      - name: Clean up\n        if: always()\n        run: |\n          rm -fr /stdb/*\n\n  callgrind_benchmark:\n    name: run callgrind benchmarks\n    # DON'T run on benchmarks-runner, using docker on a self-hosted runner has\n    # been broken for 4 years: https://github.com/actions/runner/issues/434 .\n    # Fortunately, we can run on standard GitHub Actions infra because we don't care\n    # about other stuff running on the machine!\n    # runs-on: benchmarks-runner\n    runs-on: ubuntu-latest\n    timeout-minutes: 20 # on a successful run, runs in 8 minutes\n    container:\n      image: rust:1.93.0\n      options: --privileged\n    # filter for a comment containing 'benchmarks please'\n    if: ${{ github.event_name != 'issue_comment' || (github.event.issue.pull_request && contains(github.event.comment.body, 'benchmarks please')) }}\n    env:\n      PR_NUMBER: ${{ github.event.inputs.pr_number || github.event.issue.number || null }}\n    steps:\n      - name: Clear stdb dir\n        if: always()\n        shell: bash\n        run: |\n          rm -fr /stdb/*\n\n      - name: Install valgrind & iai-callgrind-runner\n        run: |\n          apt-get update\n          apt-get install -y valgrind protobuf-compiler bash sudo curl gh\n          cargo install --git https://github.com/clockworklabs/iai-callgrind.git --branch main iai-callgrind-runner\n          git config --global --add safe.directory /__w/SpacetimeDB/SpacetimeDB\n\n      # can't do this off self hosted:\n      # - name: Enable CPU boost\n      #   shell: bash\n      #   run: echo \"1\" | sudo tee /sys/devices/system/cpu/cpufreq/boost\n\n      - name: Check membership\n        if: ${{ github.event_name == 'issue_comment' }}\n        env:\n          CONTRIB_ORG: clockworklabs\n          COMMENT_AUTHOR: ${{ github.event.comment.user.login }}\n          ORG_READ_TOKEN: ${{ secrets.ORG_READ_TOKEN }}\n        shell: bash\n        run: |\n          curl -OL https://github.com/cli/cli/releases/download/v2.37.0/gh_2.37.0_linux_amd64.deb && sudo dpkg -i gh_2.37.0_linux_amd64.deb\n          if [[ $(GH_TOKEN=$ORG_READ_TOKEN gh api --paginate /orgs/{owner}/members --jq 'any(.login == env.COMMENT_AUTHOR)') != true ]]; then\n            gh pr comment $PR_NUMBER -b \"Sorry, you don't have permission to run benchmarks.\"\n            exit 1\n          fi\n\n      - name: find PR branch\n        if: ${{ env.PR_NUMBER }}\n        shell: bash\n        run: echo \"PR_REF=$(gh pr view $PR_NUMBER --json headRefName --jq .headRefName)\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v3\n        with:\n          ref: ${{ env.PR_REF || github.ref }}\n          # if we're on master we want to know what the sha of HEAD~1 is so\n          # that we can compare results from it to HEAD (in the \"Fetch markdown\n          # summary PR\" step). otherwise, we can use a fully shallow checkout\n          fetch-depth: ${{ env.PR_NUMBER && 1 || 2 }}\n\n      - name: Unbork GitHub Actions state\n        shell: bash\n        run: |\n          echo \"Letting anybody touch our git repo, in order to avoid breaking other jobs\"\n          echo \"This is necessary because we are running as root inside a docker image\"\n          chmod -R a+rw .\n\n      - name: Post initial comment\n        shell: bash\n        run: |\n          set -exo pipefail\n          if [[ $PR_NUMBER ]]; then\n            comment_parent=issues/$PR_NUMBER\n            comment_update=issues/comments\n          else\n            comment_parent=commits/$GITHUB_SHA\n            comment_update=comments\n          fi\n          comment_body=\"Callgrind benchmark in progress...\"\n          comment_id=$(gh api \"/repos/{owner}/{repo}/$comment_parent/comments\" -f body=\"$comment_body\" --jq .id)\n          echo \"COMMENT_UPDATE_URL=/repos/{owner}/{repo}/$comment_update/$comment_id\" >>$GITHUB_ENV\n\n      - name: Install stable toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          components: clippy\n          toolchain: stable\n          target: wasm32-unknown-unknown\n          override: true\n\n      - name: Build\n        working-directory: crates/bench/\n        shell: bash\n        run: |\n          cargo build --release\n\n      - name: Install latest wasm-opt for module optimisations\n        shell: bash\n        run: |\n          curl https://github.com/WebAssembly/binaryen/releases/download/version_116/binaryen-version_116-x86_64-linux.tar.gz -L | sudo tar xz -C /usr/local --strip-components=1\n\n      # leave CPU boost on, doesn't affect callgrind!\n\n      - name: Branch; run bench\n        shell: bash\n        run: |\n          if [[ $PR_NUMBER ]]; then\n            BASELINE_NAME=branch\n            RESULTS_NAME=pr-$PR_NUMBER\n            BENCH_FILTER='(special|stdb_module|stdb_raw)'\n            echo \"Running branch callgrind benchmarks\"\n          else\n            BASELINE_NAME=master\n            RESULTS_NAME=$GITHUB_SHA\n            BENCH_FILTER='.*'\n            echo \"Running master callgrind benchmarks\"\n          fi\n          pushd crates/bench\n          rm -rf .spacetime\n          cargo bench --bench callgrind -- --save-summary pretty-json\n          cargo run --bin summarize pack-callgrind \"$BASELINE_NAME\"\n          popd\n          mkdir callgrind-results\n          [[ ! $PR_NUMBER ]] && cp target/iai/$BASELINE_NAME.json callgrind-results/\n          cp target/iai/$BASELINE_NAME.json callgrind-results/$RESULTS_NAME.json\n\n      # this will work for both PR and master\n      - name: Upload callgrind results to DO spaces\n        uses: shallwefootball/s3-upload-action@master\n        with:\n          aws_key_id: ${{ secrets.AWS_KEY_ID }}\n          aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}\n          aws_bucket: \"spacetimedb-ci-benchmarks\"\n          source_dir: callgrind-results\n          endpoint: https://nyc3.digitaloceanspaces.com\n          destination_dir: callgrind-benchmarks\n\n      - name: Fetch markdown summary PR\n        shell: bash\n        run: |\n          if [[ $PR_NUMBER ]]; then\n            OLD=master\n            NEW=pr-$PR_NUMBER\n          else\n            OLD=$(git rev-parse HEAD~1)\n            NEW=$GITHUB_SHA\n          fi\n          echo \"fetching https://benchmarks.spacetimedb.com/compare_callgrind/$OLD/$NEW\"\n          curl -sS https://benchmarks.spacetimedb.com/compare_callgrind/$OLD/$NEW > report.md\n\n      - name: Post comment\n        shell: bash\n        run: |\n          BODY=\"<details><summary>Callgrind benchmark results</summary>\n          $(cat report.md)\n          </details>\"\n          gh api \"$COMMENT_UPDATE_URL\" -X PATCH -f body=\"$BODY\"\n\n      - name: Post failure comment\n        if: ${{ failure() && env.COMMENT_UPDATE_URL }}\n        shell: bash\n        run: |\n          BODY=\"Benchmarking failed. Please check [the workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.\"\n          gh api \"$COMMENT_UPDATE_URL\" -X PATCH -f body=\"$BODY\"\n\n      - name: Clean up\n        if: always()\n        shell: bash\n        run: |\n          rm -fr /stdb/*\n          echo \"Letting anybody touch our git repo, in order to avoid breaking other jobs\"\n          echo \"This is necessary because we are running as root inside a docker image\"\n          chmod -R a+rw .\n"
  },
  {
    "path": ".github/workflows/check-merge-labels.yml",
    "content": "name: Check merge labels\n\non:\n  pull_request:\n    types: [opened, reopened, synchronize, labeled, unlabeled]\n  merge_group:\n\npermissions: read-all\n\njobs:\n  label_checks:\n    name: Check merge labels\n    runs-on: ubuntu-latest\n    steps:\n      - if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'do not merge')\n        run: |\n          echo \"This is labeled \\\"Do not merge\\\".\"\n          exit 1\n\n      # If we're in a merge queue, the PR has already passed checks these checks before being added to the queue.\n      - if: github.event_name == 'merge_group'\n        run: echo \"Merge group run; skipping merge-label checks.\"\n"
  },
  {
    "path": ".github/workflows/check-pr-base.yml",
    "content": "name: Git tree checks\n\non:\n  pull_request:\n    types: [opened, edited]\n  merge_group:\npermissions: read-all\n\njobs:\n  check_base_ref:\n    name: Based on `master`\n    runs-on: ubuntu-latest\n    steps:\n      - id: not_based_on_master\n        if: |\n          github.event_name == 'pull_request' &&\n          github.event.pull_request.base.ref != 'master'\n        run: |\n          echo \"This PR is not based on master. Please wait until the base PR merges.\"\n          exit 1\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "on:\n  pull_request:\n  push:\n    branches:\n      - master\n  merge_group:\n  workflow_dispatch:\n    inputs:\n      pr_number:\n        description: \"Pull Request Number\"\n        required: false\n        default: \"\"\n\nname: CI\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.inputs.pr_number || format('sha-{0}', github.sha) }}\n  cancel-in-progress: true\n\njobs:\n  smoketests:\n    needs: [lints]\n    name: Smoketests (${{ matrix.name }})\n    strategy:\n      matrix:\n        include:\n          - name: Linux\n            runner: spacetimedb-new-runner-2\n          - name: Windows\n            runner: windows-latest\n    runs-on: ${{ matrix.runner }}\n    timeout-minutes: 120\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n      SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp\n    steps:\n      - name: Find Git ref\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: bash\n        run: |\n          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n          if test -n \"${PR_NUMBER}\"; then\n            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          cache-on-failure: false\n          cache-all-crates: true\n          cache-workspace-crates: true\n          prefix-key: v1\n\n      - uses: actions/setup-dotnet@v4\n        with:\n          global-json-file: global.json\n\n      # nodejs and pnpm are required for the typescript quickstart smoketest\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 18\n\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      # Install emscripten for C++ module compilation tests.\n      - name: Install emscripten (Linux)\n        if: runner.os == 'Linux'\n        shell: bash\n        run: |\n          git clone https://github.com/emscripten-core/emsdk.git ~/emsdk\n          cd ~/emsdk\n          ./emsdk install 4.0.21\n          ./emsdk activate 4.0.21\n\n      - name: Install emscripten (Windows)\n        if: runner.os == 'Windows'\n        shell: pwsh\n        run: |\n          git clone https://github.com/emscripten-core/emsdk.git $env:USERPROFILE\\emsdk\n          cd $env:USERPROFILE\\emsdk\n          .\\emsdk install 4.0.21\n          .\\emsdk activate 4.0.21\n\n      - name: Install psql (Windows)\n        if: runner.os == 'Windows'\n        shell: pwsh\n        run: |\n          # Fail properly if any individual command fails\n          $ErrorActionPreference = 'Stop'\n          $PSNativeCommandUseErrorActionPreference = $true\n          choco install psql -y --no-progress\n          # Check for existence, since `choco` doesn't seem to fail the step if it fails to install..\n          # See https://github.com/clockworklabs/SpacetimeDB/pull/4399 for more background.\n          Get-Command psql\n\n      - name: Update dotnet workloads\n        if: runner.os == 'Windows'\n        run: |\n          # Fail properly if any individual command fails\n          $ErrorActionPreference = 'Stop'\n          $PSNativeCommandUseErrorActionPreference = $true\n\n          cd modules\n          # the sdk-manifests on windows-latest are messed up, so we need to update them\n          dotnet workload config --update-mode manifests\n          dotnet workload update\n\n      - name: Override NuGet packages\n        shell: bash\n        run: |\n          dotnet pack -c Release crates/bindings-csharp/BSATN.Runtime\n          dotnet pack -c Release crates/bindings-csharp/Runtime\n          cd sdks/csharp\n          ./tools~/write-nuget-config.sh ../..\n\n      # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.\n      # ChatGPT suspects that this could be due to different build invocations using the same target dir,\n      # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with\n      # `cargo build --manifest-path` (which apparently build different dependency trees).\n      # However, we've been unable to fix it so... /shrug\n      - name: Check v8 outputs\n        shell: bash\n        run: |\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/debug/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean -p v8 || true\n            cargo build -p v8\n          fi\n\n      - name: Install cargo-nextest\n        uses: taiki-e/install-action@nextest\n\n      # --test-threads=1 eliminates contention in the C# tests where they fight over bindings\n      # build artifacts.\n      # It also seemed to improve performance a fair amount (11m -> 6m)\n      - name: Run smoketests (Linux)\n        if: runner.os == 'Linux'\n        shell: bash\n        run: |\n          if [ -f ~/emsdk/emsdk_env.sh ]; then\n            source ~/emsdk/emsdk_env.sh\n          fi\n          cargo ci smoketests -- --test-threads=1\n\n      # Due to Emscripten PATH issues this was separated to make sure OpenSSL still builds correctly\n      - name: Run smoketests (Windows)\n        if: runner.os == 'Windows'\n        shell: pwsh\n        run: |\n          if (Test-Path \"$env:USERPROFILE\\emsdk\\emsdk_env.ps1\") {\n            & \"$env:USERPROFILE\\emsdk\\emsdk_env.ps1\" | Out-Null\n          }\n          cargo ci smoketests -- --test-threads=1          \n\n      - name: Check for changes\n        run: |\n          tools/check-diff.sh crates/smoketests || {\n              echo 'Error: There is a diff in the smoketests directory.'\n              exit 1\n          }\n\n  test:\n    needs: [lints]\n    name: Test Suite\n    runs-on: spacetimedb-new-runner-2\n\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Find Git ref\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n          if test -n \"${PR_NUMBER}\"; then\n            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the smoketests job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      - uses: actions/setup-dotnet@v3\n        with:\n          global-json-file: global.json\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 22\n\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      # Install cmake and emscripten for C++ module compilation tests.\n      - name: Install cmake and emscripten\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y cmake\n          git clone https://github.com/emscripten-core/emsdk.git ~/emsdk\n          cd ~/emsdk\n          ./emsdk install 4.0.21\n          ./emsdk activate 4.0.21\n\n      - name: Build typescript module sdk\n        working-directory: crates/bindings-typescript\n        run: pnpm build\n\n        # Source emsdk environment to make emcc (Emscripten compiler) available in PATH.\n      - name: Run tests\n        run: |\n          source ~/emsdk/emsdk_env.sh\n          cargo ci test\n\n  lints:\n    name: Lints\n    runs-on: spacetimedb-new-runner-2\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v3\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - run: echo ::add-matcher::.github/workflows/rust_matcher.json\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the smoketests job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      - uses: actions/setup-dotnet@v3\n        with:\n          global-json-file: global.json\n\n      - name: Run ci lint\n        run: cargo ci lint\n\n  wasm_bindings:\n    name: Build and test wasm bindings\n    runs-on: spacetimedb-new-runner-2\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - run: echo ::add-matcher::.github/workflows/rust_matcher.json\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the smoketests job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      - name: Run bindgen tests\n        run: cargo ci wasm-bindings\n\n  publish_checks:\n    name: Check that packages are publishable\n    runs-on: spacetimedb-new-runner-2\n    permissions: read-all\n    steps:\n      - uses: actions/checkout@v3\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - name: Set up Python env\n        run: |\n          test -d venv || python3 -m venv venv\n          venv/bin/pip3 install argparse toml\n      - name: Run checks\n        run: |\n          set -ueo pipefail\n          FAILED=0\n          ROOTS=(spacetimedb spacetimedb-sdk)\n          CRATES=$(venv/bin/python3 tools/find-publish-list.py --recursive --directories --quiet \"${ROOTS[@]}\")\n          for crate_dir in $CRATES; do\n            if ! venv/bin/python3 tools/crate-publish-checks.py \"${crate_dir}\"; then\n              FAILED=$(( $FAILED + 1 ))\n            fi\n          done\n          if [ $FAILED -gt 0 ]; then\n            exit 1\n          fi\n\n  update:\n    name: Test spacetimedb-update flow (${{ matrix.target }})\n    permissions: read-all\n    strategy:\n      matrix:\n        include:\n          - { target: x86_64-unknown-linux-gnu, runner: spacetimedb-new-runner-2 }\n          - { target: aarch64-unknown-linux-gnu, runner: arm-runner }\n          - { target: aarch64-apple-darwin, runner: macos-latest }\n          - { target: x86_64-pc-windows-msvc, runner: windows-latest }\n    runs-on: ${{ matrix.runner }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Install Rust\n        uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Install rust target\n        run: rustup target add ${{ matrix.target }}\n\n      - name: Install packages\n        if: ${{ matrix.runner == 'arm-runner' }}\n        shell: bash\n        run: sudo apt install -y libssl-dev\n\n      - name: Build spacetimedb-update\n        run: cargo build --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update\n        if: runner.os == 'Windows'\n\n      - name: Run self-install\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: bash\n        run: |\n          ROOT_DIR=\"$(mktemp -d)\"\n          # NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`.\n          # My best guess is that, on the GitHub runners, the \"anonymous\" ratelimit is shared by *all* users of that runner (I think this because it\n          # happens very frequently on the `macos-runner`, but we haven't seen it on any others).\n          cargo run --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update -- self-install --root-dir=\"${ROOT_DIR}\" --yes\n          \"${ROOT_DIR}\"/spacetime --root-dir=\"${ROOT_DIR}\" help\n        if: runner.os == 'Windows'\n\n      - name: Test spacetimedb-update\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: cargo ci update-flow --target=${{ matrix.target }} --github-token-auth\n        if: runner.os != 'Windows'\n\n  unreal_engine_tests:\n    name: Unreal Engine Tests\n    # This can't go on e.g. ubuntu-latest because that runner runs out of disk space. ChatGPT suggested that the general solution tends to be to use\n    # a custom runner.\n    runs-on: spacetimedb-new-runner-2\n    # Disable the tests because they are very flaky at the moment.\n    # TODO: Remove this line and re-enable the `if` line just below here.\n    if: false\n    # Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway.\n    # if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}\n    container:\n      image: ghcr.io/epicgames/unreal-engine:dev-5.6\n      credentials:\n        # Note(bfops): I don't think that `github.actor` needs to match the user that the token is for, because I'm using a token for my account and\n        # it seems to be totally happy.\n        # However, the token needs to be for a user that has access to the EpicGames org (see\n        # https://dev.epicgames.com/documentation/en-us/unreal-engine/downloading-source-code-in-unreal-engine?application_version=5.6)\n        username: ${{ github.actor }}\n        password: ${{ secrets.GHCR_TOKEN }}\n      # Run as root because otherwise we get permission denied for various directories inside the container. I tried doing dances to allow it to run\n      # without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle.\n      options: --user 0:0\n    steps:\n      # Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back\n      # commits though.\n      #      - name: Find Git ref\n      #        env:\n      #          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      #        shell: bash\n      #        run: |\n      #          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n      #          if test -n \"${PR_NUMBER}\"; then\n      #            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n      #          else\n      #            GIT_REF=\"${{ github.ref }}\"\n      #          fi\n      #          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - name: Run Unreal Engine tests\n        working-directory: sdks/unreal\n        env:\n          UE_ROOT_PATH: /home/ue4/UnrealEngine\n        run: |\n\n          apt-get update\n          apt-get install -y acl curl ca-certificates\n\n          REPO=\"$GITHUB_WORKSPACE\"\n          # Let ue4 read/write the workspace & tool caches without changing ownership\n          for p in \"$REPO\" \"${RUNNER_TEMP:-/__t}\" \"${RUNNER_TOOL_CACHE:-/__t}\"; do\n            [ -d \"$p\" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX \"$p\" || true\n          done\n\n          # Rust tool caches live under the runner tool cache so they persist\n          export CARGO_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/cargo\"\n          export RUSTUP_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/rustup\"\n          mkdir -p \"$CARGO_HOME\" \"$RUSTUP_HOME\"\n          chown -R ue4:ue4 \"$CARGO_HOME\" \"$RUSTUP_HOME\"\n\n          # Make sure the UE build script is executable (and parents traversable)\n          UE_DIR=\"${UE_ROOT_PATH:-/home/ue4/UnrealEngine}\"\n          chmod a+rx \"$UE_DIR\" \"$UE_DIR/Engine\" \"$UE_DIR/Engine/Build\" \"$UE_DIR/Engine/Build/BatchFiles/Linux\" || true\n          chmod a+rx \"$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh\" || true\n\n          # Run the build & tests as ue4 (who owns the UE tree)\n          sudo -E -H -u ue4 env \\\n            HOME=/home/ue4 \\\n            XDG_CONFIG_HOME=/home/ue4/.config \\\n            CARGO_HOME=\"$CARGO_HOME\" \\\n            RUSTUP_HOME=\"$RUSTUP_HOME\" \\\n            PATH=\"$CARGO_HOME/bin:$PATH\" \\\n            bash -lc '\n              set -euxo pipefail\n              # Install rustup for ue4 if needed (uses the shared caches)\n              if ! command -v cargo >/dev/null 2>&1; then\n                curl -sSf https://sh.rustup.rs | sh -s -- -y\n              fi\n              rustup show >/dev/null\n              git config --global --add safe.directory \"$GITHUB_WORKSPACE\" || true\n\n              cd \"$GITHUB_WORKSPACE/sdks/unreal\"\n              cargo --version\n              cargo test -- --test-threads=1\n            '\n\n  ci_command_docs:\n    name: Check CI command docs\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Find Git ref\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: bash\n        run: |\n          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n          if test -n \"${PR_NUMBER}\"; then\n            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Check for docs change\n        run: cargo ci self-docs --check\n\n  cli_docs:\n    name: Check CLI docs\n    permissions: read-all\n    runs-on: spacetimedb-new-runner-2\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Find Git ref\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: bash\n        run: |\n          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n          if test -n \"${PR_NUMBER}\"; then\n            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 22\n\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      - name: Get pnpm store directory\n        shell: bash\n        run: |\n          echo \"STORE_PATH=$(pnpm store path --silent)\" >> $GITHUB_ENV\n\n      - uses: actions/cache@v4\n        name: Setup pnpm cache\n        with:\n          path: ${{ env.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the smoketests job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      - name: Check for docs change\n        run: |\n          cargo ci cli-docs\n\n  llm_ci_check:\n    name: Verify LLM benchmark is up to date\n    permissions:\n      contents: read\n    runs-on: ubuntu-latest\n    # Disable the tests because they are causing us headaches with merge conflicts and re-runs etc.\n    if: false\n    steps:\n      # Build the tool from master to ensure consistent hash computation\n      # with the llm-benchmark-update workflow (which also uses master's tool).\n      - name: Checkout master (build tool from trusted code)\n        uses: actions/checkout@v4\n        with:\n          ref: master\n          fetch-depth: 1\n\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Install llm-benchmark tool from master\n        run: |\n          cargo install --path tools/xtask-llm-benchmark --locked\n          command -v llm_benchmark\n\n      # Now checkout the PR branch to verify its benchmark files\n      - name: Checkout PR branch\n        uses: actions/checkout@v4\n        with:\n          clean: false\n\n      - name: Run hash check (both langs)\n        run: llm_benchmark ci-check\n\n  unity-testsuite:\n    needs: [lints]\n    # Skip if this is an external contribution.\n    # The license secrets will be empty, so the step would fail anyway.\n    if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}\n    permissions:\n      contents: read\n      checks: write\n    runs-on: spacetimedb-unity-runner\n    timeout-minutes: 30\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Checkout repository\n        id: checkout-stdb\n        uses: actions/checkout@v4\n\n      # Run cheap .NET tests first. If those fail, no need to run expensive Unity tests.\n\n      - name: Setup dotnet\n        uses: actions/setup-dotnet@v3\n        with:\n          global-json-file: global.json\n\n      - name: Override NuGet packages\n        run: |\n          dotnet pack crates/bindings-csharp/BSATN.Runtime\n          dotnet pack crates/bindings-csharp/Runtime\n\n          # Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository\n          # to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if\n          # available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages.\n          # This means that (if version numbers match) we will test the local versions of the C# packages, even\n          # if they're not pushed to NuGet.\n          # See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file.\n          cd sdks/csharp\n          ./tools~/write-nuget-config.sh ../..\n\n      - name: Restore .NET solution\n        working-directory: sdks/csharp\n        run: dotnet restore --configfile NuGet.Config SpacetimeDB.ClientSDK.sln\n\n      # Now, setup the Unity tests.\n      - name: Patch spacetimedb dependency in Cargo.toml\n        working-directory: demo/Blackholio/server-rust\n        run: |\n          sed -i \"s|spacetimedb *=.*|spacetimedb = \\{ path = \\\"../../../crates/bindings\\\" \\}|\" Cargo.toml\n          cat Cargo.toml\n\n      - name: Install Rust toolchain\n        uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the main CI job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.\n      # ChatGPT suspects that this could be due to different build invocations using the same target dir,\n      # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with\n      # `cargo build --manifest-path` (which apparently build different dependency trees).\n      # However, we've been unable to fix it so... /shrug\n      - name: Check v8 outputs\n        run: |\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/release/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean --release -p v8 || true\n            cargo build --release -p v8\n          fi\n\n      - name: Install SpacetimeDB CLI from the local checkout\n        run: |\n          export CARGO_HOME=\"$HOME/.cargo\"\n          echo \"$CARGO_HOME/bin\" >> \"$GITHUB_PATH\"\n          cargo install --force --path crates/cli --locked --message-format=short\n          cargo install --force --path crates/standalone --locked --message-format=short\n          # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules).\n          ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime\n\n      - name: Generate client bindings\n        working-directory: demo/Blackholio/server-rust\n        run: bash ./generate.sh -y\n\n      - name: Check for changes\n        run: |\n          tools/check-diff.sh demo/Blackholio/client-unity/Assets/Scripts/autogen || {\n              echo 'Error: Bindings are dirty. Please run `demo/Blackholio/server-rust/generate.sh`.'\n              exit 1\n          }\n\n      - name: Hydrate Unity SDK DLLs\n        run: cargo ci dlls\n\n      - name: Check Unity meta files\n        uses: DeNA/unity-meta-check@v3\n        with:\n          enable_pr_comment: ${{ github.event_name == 'pull_request' }}\n          target_path: sdks/csharp\n        env:\n          GITHUB_TOKEN: \"${{ secrets.GITHUB_TOKEN }}\"\n\n      - name: Start SpacetimeDB\n        run: |\n          spacetime start &\n          disown\n\n      - name: Publish unity-tests module to SpacetimeDB\n        working-directory: demo/Blackholio/server-rust\n        run: |\n          spacetime logout && spacetime login --server-issued-login local\n          bash ./publish.sh\n\n      - name: Patch com.clockworklabs.spacetimedbsdk dependency in manifest.json\n        working-directory: demo/Blackholio/client-unity/Packages\n        run: |\n          yq e -i '.dependencies[\"com.clockworklabs.spacetimedbsdk\"] = \"file:../../../../sdks/csharp\"' manifest.json\n          cat manifest.json\n\n      - uses: actions/cache@v3\n        with:\n          path: demo/Blackholio/client-unity/Library\n          key: Unity-${{ github.head_ref }}\n          restore-keys: Unity-\n\n      - name: Login to DockerHub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ vars.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_PASSWORD }}\n\n      - name: Run Unity tests\n        uses: game-ci/unity-test-runner@v4\n        with:\n          unityVersion: 2022.3.32f1 # Adjust Unity version to a valid tag\n          projectPath: demo/Blackholio/client-unity # Path to the Unity project subdirectory\n          githubToken: ${{ secrets.GITHUB_TOKEN }}\n          testMode: playmode\n          useHostNetwork: true\n          artifactsPath: \"\"\n        env:\n          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}\n          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}\n          UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}\n\n  csharp-testsuite:\n    needs: [lints]\n    runs-on: spacetimedb-new-runner-2\n    timeout-minutes: 30\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Checkout repository\n        id: checkout-stdb\n        uses: actions/checkout@v4\n\n      # Run cheap .NET tests first. If those fail, no need to run expensive Unity tests.\n\n      - name: Setup dotnet\n        uses: actions/setup-dotnet@v3\n        with:\n          global-json-file: global.json\n\n      - name: Override NuGet packages\n        run: |\n          dotnet pack crates/bindings-csharp/BSATN.Runtime\n          dotnet pack crates/bindings-csharp/Runtime\n\n          # Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository\n          # to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if\n          # available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages.\n          # This means that (if version numbers match) we will test the local versions of the C# packages, even\n          # if they're not pushed to NuGet.\n          # See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file.\n          cd sdks/csharp\n          ./tools~/write-nuget-config.sh ../..\n\n      - name: Restore .NET solution\n        working-directory: sdks/csharp\n        run: dotnet restore --configfile NuGet.Config SpacetimeDB.ClientSDK.sln\n\n      - name: Run .NET tests\n        working-directory: sdks/csharp\n        run: dotnet test -warnaserror --no-restore\n\n      - name: Verify C# formatting\n        working-directory: sdks/csharp\n        run: dotnet format --no-restore --verify-no-changes SpacetimeDB.ClientSDK.sln\n\n      - name: Install Rust toolchain\n        uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the main CI job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.\n      # ChatGPT suspects that this could be due to different build invocations using the same target dir,\n      # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with\n      # `cargo build --manifest-path` (which apparently build different dependency trees).\n      # However, we've been unable to fix it so... /shrug\n      - name: Check v8 outputs\n        run: |\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/debug/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean -p v8 || true\n            cargo build -p v8\n          fi\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/release/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean --release -p v8 || true\n            cargo build --release -p v8\n          fi\n\n      - name: Install SpacetimeDB CLI from the local checkout\n        run: |\n          export CARGO_HOME=\"$HOME/.cargo\"\n          echo \"$CARGO_HOME/bin\" >> \"$GITHUB_PATH\"\n          cargo install --force --path crates/cli --locked --message-format=short\n          cargo install --force --path crates/standalone --features allow_loopback_http_for_tests --locked --message-format=short\n          # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules).\n          ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime\n\n      - name: Check quickstart-chat bindings are up to date\n        working-directory: sdks/csharp\n        run: |\n          bash tools~/gen-quickstart.sh\n          \"${GITHUB_WORKSPACE}\"/tools/check-diff.sh examples~/quickstart-chat || {\n              echo 'Error: quickstart-chat bindings have changed. Please run `sdks/csharp/tools~/gen-quickstart.sh`.'\n              exit 1\n          }\n\n      # TODO: Re-enable this once csharp is using the v2 ws api.\n      # - name: Check client-api bindings are up to date\n      #   working-directory: sdks/csharp\n      #   run: |\n      #     bash tools~/gen-client-api.sh\n      #     \"${GITHUB_WORKSPACE}\"/tools/check-diff.sh src/SpacetimeDB/ClientApi || {\n      #         echo 'Error: Client API bindings are dirty. Please run `sdks/csharp/tools~/gen-client-api.sh`.'\n      #         exit 1\n      #     }\n\n      - name: Start SpacetimeDB\n        run: |\n          spacetime start &\n          disown\n\n      - name: Run regression tests\n        run: |\n          bash sdks/csharp/tools~/run-regression-tests.sh\n          tools/check-diff.sh sdks/csharp/examples~/regression-tests || {\n              echo 'Error: Bindings are dirty. Please run `sdks/csharp/tools~/gen-regression-tests.sh`.'\n              exit 1\n          }\n\n  internal-tests:\n    name: Internal Tests\n    needs: [lints]\n    # Skip if not a PR or a push to master\n    # Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway.\n    if: ${{ (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master'))\n      && (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork) }}\n    permissions:\n      contents: read\n    runs-on: ubuntu-latest\n    env:\n      TARGET_OWNER: clockworklabs\n      TARGET_REPO: SpacetimeDBPrivate\n    steps:\n      - id: dispatch\n        name: Trigger tests\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}\n          script: |\n            const workflowId = 'ci.yml';\n            const targetRef = 'master';\n            const targetOwner = process.env.TARGET_OWNER;\n            const targetRepo = process.env.TARGET_REPO;\n            // Use the ref for pull requests because the head sha is brittle (github does some extra dance where it merges in master).\n            const publicRef = (context.eventName === 'pull_request') ? context.payload.pull_request.head.ref : context.sha;\n            const preDispatch = new Date().toISOString();\n  \n            // Dispatch the workflow in the target repository\n            await github.rest.actions.createWorkflowDispatch({\n              owner: targetOwner,\n              repo: targetRepo,\n              workflow_id: workflowId,\n              ref: targetRef,\n              inputs: { public_ref: publicRef }\n            });\n  \n            const sleep = (ms) => new Promise(r => setTimeout(r, ms));\n  \n            // Find the dispatched run by name\n            let runId = null;\n            for (let attempt = 0; attempt < 20 && !runId; attempt++) { // up to ~10 minutes to locate the run\n              await sleep(5000);\n              const runsResp = await github.rest.actions.listWorkflowRuns({\n                owner: targetOwner,\n                repo: targetRepo,\n                workflow_id: workflowId,\n                event: 'workflow_dispatch',\n                branch: targetRef,\n                per_page: 50,\n              });\n  \n              const expectedName = `CI [public_ref=${publicRef}]`;\n              const candidates = runsResp.data.workflow_runs\n                .filter(r => r.name === expectedName && new Date(r.created_at) >= new Date(preDispatch))\n                .sort((a, b) => new Date(b.created_at) - new Date(a.created_at));\n  \n              if (candidates.length > 0) {\n                runId = candidates[0].id;\n                break;\n              }\n            }\n  \n            if (!runId) {\n              core.setFailed('Failed to locate dispatched run in the private repository.');\n              return;\n            }\n  \n            const runUrl = `https://github.com/${targetOwner}/${targetRepo}/actions/runs/${runId}`;\n            core.info(`View run: ${runUrl}`);\n            core.setOutput('run_id', String(runId));\n            core.setOutput('run_url', runUrl);\n  \n      - name: Wait for Internal Tests to complete\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}\n          script: |\n            const targetOwner = process.env.TARGET_OWNER;\n            const targetRepo = process.env.TARGET_REPO;\n            const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);\n            const runUrl = `${{ steps.dispatch.outputs.run_url }}`;\n            const sleep = (ms) => new Promise(r => setTimeout(r, ms));\n  \n            core.info(`Waiting for workflow result... ${runUrl}`);\n  \n            let conclusion = null;\n            for (let attempt = 0; attempt < 240; attempt++) { // up to ~2 hours\n              const runResp = await github.rest.actions.getWorkflowRun({\n                owner: targetOwner,\n                repo: targetRepo,\n                run_id: runId\n              });\n              const { status, conclusion: c } = runResp.data;\n              if (status === 'completed') {\n                conclusion = c || 'success';\n                break;\n              }\n              await sleep(30000);\n            }\n  \n            if (!conclusion) {\n              core.setFailed('Timed out waiting for private workflow to complete.');\n              return;\n            }\n  \n            if (conclusion !== 'success') {\n              core.setFailed(`Private workflow failed with conclusion: ${conclusion}`);\n            }\n  \n      - name: Cancel invoked run if workflow cancelled\n        if: ${{ cancelled() }}\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}\n          script: |\n            const targetOwner = process.env.TARGET_OWNER;\n            const targetRepo = process.env.TARGET_REPO;\n            const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);\n            if (!runId) return;\n            await github.rest.actions.cancelWorkflowRun({\n              owner: targetOwner,\n              repo: targetRepo,\n              run_id: runId,\n            });\n\n  global_json_policy:\n    name: Verify global.json files are symlinks\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    steps:\n      - name: Find Git ref\n        env:\n          PR_NUMBER: ${{ github.event.inputs.pr_number }}\n        run: |\n          if [ -n \"$PR_NUMBER\" ]; then\n            GIT_REF=\"refs/pull/$PR_NUMBER/merge\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          save-if: false\n          prefix-key: v1\n\n      - name: Check global.json policy\n        run: cargo ci global-json-policy\n\n  warn-python-smoketests:\n    name: Check for Python smoketest edits\n    runs-on: ubuntu-latest\n    if: github.event_name == 'pull_request'\n    permissions:\n      contents: read\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Fail if Python smoketests were modified\n        run: |\n          MERGE_BASE=\"$(git merge-base origin/${{ github.base_ref }} HEAD)\"\n          PYTHON_SMOKETEST_CHANGES=\"$(git diff --name-only \"$MERGE_BASE\" HEAD -- 'smoketests/**.py')\"\n\n          if [ -n \"$PYTHON_SMOKETEST_CHANGES\" ]; then\n            echo \"::error::This PR modifies legacy Python smoketests. Please add new tests to the Rust smoketests in crates/smoketests/ instead.\"\n            echo \"\"\n            echo \"Changed files:\"\n            echo \"$PYTHON_SMOKETEST_CHANGES\"\n            echo \"\"\n            echo \"The Python smoketests are being replaced by Rust smoketests.\"\n            echo \"See crates/smoketests/DEVELOP.md for instructions on adding Rust smoketests.\"\n            exit 1\n          fi\n\n          echo \"No Python smoketest changes detected.\"\n\n  smoketests_mod_rs_complete:\n    name: Check smoketests/mod.rs is complete\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    env:\n      CARGO_TARGET_DIR: ${{ github.workspace }}/target\n    steps:\n      - name: Find Git ref\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        shell: bash\n        run: |\n          PR_NUMBER=\"${{ github.event.inputs.pr_number || null }}\"\n          if test -n \"${PR_NUMBER}\"; then\n            GIT_REF=\"$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )\"\n          else\n            GIT_REF=\"${{ github.ref }}\"\n          fi\n          echo \"GIT_REF=${GIT_REF}\" >>\"$GITHUB_ENV\"\n      - name: Checkout sources\n        uses: actions/checkout@v4\n        with:\n          ref: ${{ env.GIT_REF }}\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          cache-on-failure: false\n          cache-all-crates: true\n          cache-workspace-crates: true\n          prefix-key: v1\n\n      # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.\n      # ChatGPT suspects that this could be due to different build invocations using the same target dir,\n      # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with\n      # `cargo build --manifest-path` (which apparently build different dependency trees).\n      # However, we've been unable to fix it so... /shrug\n      - name: Check v8 outputs\n        shell: bash\n        run: |\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/debug/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean -p v8 || true\n            cargo build -p v8\n          fi\n\n      - name: Verify crates/smoketests/tests/smoketests/mod.rs lists all entries\n        run: |\n          cargo ci smoketests check-mod-list\n"
  },
  {
    "path": ".github/workflows/discord-posts.yml",
    "content": "name: Discord notifications\n\non:\n  pull_request:\n    types: [closed]\n\njobs:\n  discordNotification:\n    runs-on: ubuntu-latest\n    if: github.event.pull_request.merged == true &&\n      github.event.pull_request.base.ref == 'master'\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - name: Set up GitHub CLI\n        run: |\n          curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /usr/share/keyrings/githubcli-archive-keyring.gpg > /dev/null\n          sudo apt-get install -y apt-transport-https\n          echo \"deb [arch=amd64 signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list\n          sudo apt-get update\n          sudo apt-get install -y gh\n\n      - name: Send Discord notification\n        env:\n          DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}\n          PR_TITLE: ${{ github.event.pull_request.title }}\n          PR_NUMBER: ${{ github.event.pull_request.number }}\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          MENTION_ON_FAILURE: ${{ secrets.DEV_OPS_ROLE_ID }}\n          DISCORD_USER_MAP: ${{ secrets.DISCORD_USER_MAP }}\n        run: |\n          message=\"PR merged: [(#${PR_NUMBER}) ${PR_TITLE}](<${PR_URL}>)\"\n          # Note that anything besides success is treated as a failure (e.g. if the check did not run at all, or if it is still pending).\n          FAILED_CHECKS=\"$(\n            gh pr checks \"${{github.event.pull_request.html_url}}\" \\\n              --json 'workflow,state,name' |\n              jq '.[]\n                | select(.workflow != \"Discord notifications\")\n                | select(.state != \"SUCCESS\" and .state != \"NEUTRAL\" and .state != \"SKIPPED\")\n              ' |\n              jq -r '\"\\(.workflow) / \\(.name): \\(.state)\"'\n          )\"\n\n          # Lookup PR author's Discord ID from the provided JSON map (if any)\n          author_discord_id=\"$(\n            jq -r \\\n              --arg u \"${{ github.event.pull_request.user.login }}\" \\\n              '.[$u] // empty' \\\n              <<<\"${DISCORD_USER_MAP}\"\n          )\"\n          if [ -z \"${author_discord_id}\" ]; then\n            echo \"Warning: PR author not found not found in USER_LOOKUP_JSON\"\n          fi\n\n          message+=$'\\n'\n          if [[ -z \"${FAILED_CHECKS}\" ]]; then\n            message+='All checks passed.'\n          else\n            message+=\"${FAILED_CHECKS}\"\n            message+=$'\\n'\n            # This uses special Discord syntax for pinging a particular role.\n            # Note the '&' - this is the difference between pinging a *role* and pinging a *person*.\n            if [[ -n \"${author_discord_id}\" ]]; then\n              message+=\"<@${author_discord_id}> please investigate these failures.\"\n            fi\n            message+=$'\\n'\n            message+=\"(cc <@&${MENTION_ON_FAILURE}> - Releases may be affected)\"\n          fi\n          # Use `jq` to construct the json data blob in the format required by the webhook.\n          data=\"$(jq --null-input --arg msg \"$message\" '.content=$msg')\"\n          curl -X POST -H 'Content-Type: application/json' -d \"$data\" \"${DISCORD_WEBHOOK_URL}\"\n"
  },
  {
    "path": ".github/workflows/docker.yml",
    "content": "name: Docker Image\n\non:\n  push:\n    branches:\n      - master\n      - staging\n      - dev\n    tags:\n      - 'v*'\n\njobs:\n  docker-amd64:\n    runs-on: ubuntu-latest\n    name: Build DockerHub AMD64 Container\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v4\n        with:\n          images: |\n            clockworklabs/spacetimedb\n          tags: |\n            type=ref,event=tag\n            type=sha,prefix=commit-,suffix=-amd64\n          flavor: |\n            latest=false\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n      - name: Cache Docker layers\n        uses: actions/cache@v4\n        with:\n          path: /tmp/.buildx-cache\n          key: ${{ runner.os }}-buildx-${{ github.sha }}\n          restore-keys: |\n            ${{ runner.os }}-buildx-\n      - name: Login to DockerHub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ vars.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_PASSWORD }}\n      - name: Build and push\n        uses: docker/build-push-action@v4\n        with:\n          context: .\n          file: crates/standalone/Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=local,src=/tmp/.buildx-cache\n          cache-to: type=local,dest=/tmp/.buildx-cache-new\n          platforms: linux/amd64\n\n      - name: Merge images\n        run: |\n          ./tools/merge-docker-images.sh clockworklabs/spacetimedb \"commit-${GITHUB_SHA:0:7}\" \"${GITHUB_SHA:0:7}-full\"\n\n        # This ugly bit is necessary if you don't want your cache to grow forever\n        # until it hits GitHub's limit of 5GB.\n        # Temp fix\n        # https://github.com/docker/build-push-action/issues/252\n        # https://github.com/moby/buildkit/issues/1896\n      - name: Move cache\n        run: |\n          rm -rf /tmp/.buildx-cache\n          mv /tmp/.buildx-cache-new /tmp/.buildx-cache\n\n  docker-arm64:\n    runs-on: arm-runner\n    name: Build DockerHub ARM64 Container\n    steps:\n      - name: Install jq\n        run: sudo apt-get install jq -y\n      - name: Prune stale references\n        run: git remote prune origin\n      - name: Checkout\n        uses: actions/checkout@v3\n      - name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v4\n        with:\n          images: |\n            clockworklabs/spacetimedb\n          tags: |\n            type=ref,event=tag\n            type=sha,prefix=commit-,suffix=-arm64\n          flavor: |\n            latest=false\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n      - name: Cache Docker layers\n        uses: actions/cache@v4\n        with:\n          path: /tmp/.buildx-cache\n          key: ${{ runner.os }}-buildx-${{ github.sha }}\n          restore-keys: |\n            ${{ runner.os }}-buildx-\n      - name: Login to DockerHub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ vars.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_PASSWORD }}\n      - name: Build and push\n        uses: docker/build-push-action@v4\n        with:\n          context: .\n          file: crates/standalone/Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=local,src=/tmp/.buildx-cache\n          cache-to: type=local,dest=/tmp/.buildx-cache-new\n          platforms: linux/arm64/v8\n\n      - name: Merge images\n        run: |\n          ./tools/merge-docker-images.sh clockworklabs/spacetimedb \"commit-${GITHUB_SHA:0:7}\" \"${GITHUB_SHA:0:7}-full\"\n\n        # This ugly bit is necessary if you don't want your cache to grow forever\n        # until it hits GitHub's limit of 5GB.\n        # Temp fix\n        # https://github.com/docker/build-push-action/issues/252\n        # https://github.com/moby/buildkit/issues/1896\n      - name: Move cache\n        run: |\n          rm -rf /tmp/.buildx-cache\n          mv /tmp/.buildx-cache-new /tmp/.buildx-cache\n"
  },
  {
    "path": ".github/workflows/docs-publish.yaml",
    "content": "name: Docs / Publish\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches:\n      - docs/release\n\njobs:\n  build:\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v3\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v3\n        with:\n          node-version: '22'\n      \n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      - name: Get pnpm store directory\n        working-directory: sdks/typescript\n        shell: bash\n        run: |\n          echo \"STORE_PATH=$(pnpm store path --silent)\" >> $GITHUB_ENV\n\n      - uses: actions/cache@v4\n        name: Setup pnpm cache\n        with:\n          path: ${{ env.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n\n      - name: Install dependencies\n        working-directory: docs\n        run: pnpm install\n\n      - name: Docusaurus build\n        working-directory: docs\n        run: pnpm build\n\n      - name: Publish docs to S3\n        uses: shallwefootball/s3-upload-action@master\n        with:\n          aws_key_id: ${{ secrets.DOCS_AWS_KEY_ID }}\n          aws_secret_access_key: ${{ secrets.DOCS_AWS_SECRET_ACCESS_KEY}}\n          aws_bucket: spacetimedb-docs\n          source_dir: docs/build\n          destination_dir: 'docs'\n"
  },
  {
    "path": ".github/workflows/docs-test.yaml",
    "content": "name: Docs / Test\npermissions:\n  contents: read\n\non:\n  pull_request:\n\njobs:\n  build:\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v3\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v3\n        with:\n          node-version: '22'\n      \n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      - name: Get pnpm store directory\n        working-directory: sdks/typescript\n        shell: bash\n        run: |\n          echo \"STORE_PATH=$(pnpm store path --silent)\" >> $GITHUB_ENV\n\n      - uses: actions/cache@v4\n        name: Setup pnpm cache\n        with:\n          path: ${{ env.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n\n      - name: Install dependencies\n        working-directory: docs\n        run: pnpm install\n\n      - name: Docusaurus build\n        working-directory: docs\n        run: pnpm build\n"
  },
  {
    "path": ".github/workflows/llm-benchmark-update.yml",
    "content": "name: Update LLM benchmarks\n\non:\n  workflow_dispatch:\n    inputs:\n      pr_number:\n        description: \"Pull Request Number\"\n        required: true\n  issue_comment:\n    types: [created] # only run when the comment is first created\n\npermissions:\n  contents: read\n  pull-requests: write\n  issues: write\n\nconcurrency:\n  group: >-\n    llm-benchmark\n    -${{ github.event_name == 'issue_comment' && github.event.issue.number || inputs.pr_number }}\n    ${{ github.event_name == 'issue_comment' && !startsWith(github.event.comment.body, '/update-llm-benchmark') && '-unrelated-comment' }}\n  cancel-in-progress: true\n\njobs:\n  update-llm-benchmark:\n    # Runnable either with a comment that starts with /update-llm-benchmark\n    # or by manually dispatching\n    if: |\n      (github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/update-llm-benchmark')) ||\n      (github.event_name == 'workflow_dispatch')\n    runs-on: spacetimedb-new-runner\n    container:\n      image: localhost:5000/spacetimedb-ci:latest\n      options: >-\n        --privileged\n    steps:\n      # Here we install the spacetime CLI for faster execution of the tests\n      # SpacetimeDB itself is not under test here, rather it's the docs.\n      # If we want to change that it is possible to have the benchmark compile\n      # SpacetimeDB from source.\n      - name: Install spacetime CLI\n        run: |\n          curl -sSf https://install.spacetimedb.com | sh -s -- -y\n          echo \"$HOME/.local/bin\" >> $GITHUB_PATH\n\n      - name: Load PR info\n        id: pr\n        uses: actions/github-script@v7\n        with:\n          script: |\n            let prNumber;\n            if (context.eventName === 'issue_comment') {\n              prNumber = context.payload.issue.number;\n            } else if (context.eventName === 'workflow_dispatch') {\n              const raw = context.payload.inputs?.pr_number;\n              if (!raw || !/^\\d+$/.test(raw)) {\n                core.setFailed(`Invalid pr_number input: '${raw}'.`);\n                return;\n              }\n              prNumber = Number(raw);\n            } else {\n              core.setFailed(`Unsupported event: ${context.eventName}`);\n              return;\n            }\n\n            const { data: pr } = await github.rest.pulls.get({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              pull_number: prNumber,\n            });\n\n            core.setOutput('number', String(prNumber));\n            core.setOutput('head_ref', pr.head.ref);\n            core.setOutput('head_sha', pr.head.sha);\n            core.setOutput('head_repo_full_name', pr.head.repo.full_name);\n            core.setOutput('head_owner_type', pr.head.repo.owner.type); // \"User\"|\"Organization\"\n            core.setOutput('maintainer_can_modify', String(pr.maintainer_can_modify));\n\n      # If this was kicked off by a comment, ensure that the commenter is\n      # a collaborator on the repo. We don't want unprivileged users to run benchmarks.\n      # Note that the workflow that will be run will be the one that is on the `master`\n      # branch, NOT the one from the PR. This is important so that the PR author can't\n      # sneak in an exfiltration exploit.\n      - name: Check commenter permission\n        if: github.event_name == 'issue_comment'\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const user = context.payload.comment.user.login;\n            const { data } = await github.rest.repos.getCollaboratorPermissionLevel({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              username: user,\n            });\n\n            const allowed = new Set(['admin', 'maintain', 'write', 'triage']);\n            if (!allowed.has(data.permission)) {\n              core.setFailed(`User ${user} has permission '${data.permission}', not allowed to run benchmarks.`);\n            }\n\n      # If the PR is from a fork, we need to be able to have GitHub actions commit back\n      # to the forked repo, so that we can update the benchmark results.\n      # In order to do this we need to ensure that the PR is configured to allow the maintainers\n      # of the SpacetimeDB repo to commit back ot the fork.\n      - name: Check fork pushability (and comment if not)\n        if: steps.pr.outputs.head_repo_full_name != github.repository\n        uses: actions/github-script@v7\n        env:\n          PR_NUMBER: ${{ steps.pr.outputs.number }}\n          HEAD_OWNER_TYPE: ${{ steps.pr.outputs.head_owner_type }}\n          MAINTAINER_CAN_MODIFY: ${{ steps.pr.outputs.maintainer_can_modify }}\n        with:\n          script: |\n            const issue_number = Number(process.env.PR_NUMBER);\n            const headOwnerType = process.env.HEAD_OWNER_TYPE;\n            const canModify = process.env.MAINTAINER_CAN_MODIFY === 'true';\n\n            if (headOwnerType === 'Organization') {\n              await github.rest.issues.createComment({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number,\n                body: [\n                  \"I can’t push benchmark updates to this PR because it comes from an **organization-owned fork**.\",\n                  \"GitHub doesn’t allow granting upstream maintainers push permissions to org-owned forks.\",\n                  \"\",\n                  \"Options:\",\n                  \"- Reopen the PR from a **personal fork** with **Allow edits from maintainers** enabled, or\",\n                  \"- A maintainer can apply the benchmark update on an internal branch.\"\n                ].join(\"\\n\"),\n              });\n              core.setFailed(\"Org-owned fork PR is not pushable by maintainers.\");\n              return;\n            }\n\n            if (!canModify) {\n              await github.rest.issues.createComment({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number,\n                body: [\n                  \"I can’t push benchmark updates to this PR branch until you enable **Allow edits from maintainers**.\",\n                  \"Please check the box on the PR page, then re-comment `/update-llm-benchmark`.\",\n                  \"See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork\"\n                ].join(\"\\n\"),\n              });\n              core.setFailed(\"maintainer_can_modify is false; author must enable 'Allow edits from maintainers'.\");\n            }\n\n      # Run the benchmark that is already checked into master to prevent\n      # an exfiltration attack whereby the PR author tries to sneak in an exploit\n      # and get a maintainer to run the modified benchmark without looking at the\n      # PR first. This ensure that we only ever execute code that is checked into\n      # master.\n      - name: Checkout master (build/install tool from trusted code)\n        uses: actions/checkout@v4\n        with:\n          ref: master\n          fetch-depth: 0\n          persist-credentials: false\n\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      # Ensure we use a user-writable .NET install (not /usr/share/dotnet),\n      # so workload installs don't require sudo.\n      - name: Setup .NET SDK\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: \"8.0.x\"\n\n      - name: Install WASI workload (wasi-experimental)\n        env:\n          DOTNET_MULTILEVEL_LOOKUP: \"0\"\n          DOTNET_CLI_HOME: ${{ runner.temp }}/dotnet-home\n          DOTNET_SKIP_FIRST_TIME_EXPERIENCE: \"1\"\n        run: |\n          dotnet --info\n          dotnet workload install wasi-experimental --skip-manifest-update --disable-parallel\n\n      - name: Install llm-benchmark tool from master\n        run: |\n          cargo install --path tools/xtask-llm-benchmark --locked\n          command -v llm_benchmark\n\n      # Check out the repo on the branch, but ONLY use this code as data!\n      # Never execute code that is on the PR branch.\n      - name: Checkout PR head (branch)\n        uses: actions/checkout@v4\n        with:\n          repository: ${{ steps.pr.outputs.head_repo_full_name }}\n          ref: ${{ steps.pr.outputs.head_sha }}\n          fetch-depth: 0\n          persist-credentials: false\n\n      # Run the benchmark against the PR using the installed tool from the\n      # master branch.\n      - name: Run benchmark (with provider keys)\n        env:\n          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n          # Prevent MSBuild node reuse issues that cause \"Pipe is broken\" errors\n          # when running multiple dotnet publish commands in parallel.\n          # See: https://github.com/dotnet/msbuild/issues/6657\n          MSBUILDDISABLENODEREUSE: \"1\"\n          DOTNET_CLI_USE_MSBUILD_SERVER: \"0\"\n        run: |\n          llm_benchmark ci-quickfix\n          llm_benchmark ci-check\n\n      # Generate failure analysis if there are any failures\n      - name: Generate failure analysis\n        env:\n          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n        run: |\n          llm_benchmark analyze -o docs/llms/docs-benchmark-analysis.md || true\n\n      # Generate PR comment markdown (compares against master baseline)\n      - name: Generate PR comment markdown\n        run: |\n          llm_benchmark ci-comment\n\n      - name: Ensure only docs/llms changed\n        run: |\n          set -euo pipefail\n          CHANGED=\"$(git diff --name-only)\"\n\n          if [ -z \"$CHANGED\" ]; then\n            echo \"No changes.\"\n            exit 0\n          fi\n\n          if echo \"$CHANGED\" | grep -qvE '^docs/llms/'; then\n            echo \"Benchmark produced changes outside docs/llms:\"\n            echo \"$CHANGED\" | grep -vE '^docs/llms/'\n            exit 1\n          fi\n\n      # Comment the benchmark results on the PR\n      - name: Comment benchmark results on PR\n        uses: actions/github-script@v7\n        env:\n          PR_NUMBER: ${{ steps.pr.outputs.number }}\n        with:\n          github-token: ${{ secrets.CLOCKWORK_LABS_BOT_PAT }}\n          script: |\n            const fs = require('fs');\n\n            // Read the pre-generated comment markdown\n            const commentPath = 'docs/llms/docs-benchmark-comment.md';\n            if (!fs.existsSync(commentPath)) {\n              core.setFailed(`Comment file not found: ${commentPath}`);\n              return;\n            }\n            let body = fs.readFileSync(commentPath, 'utf8');\n\n            // Check if failure analysis exists and append it\n            const analysisPath = 'docs/llms/docs-benchmark-analysis.md';\n            if (fs.existsSync(analysisPath)) {\n              const analysis = fs.readFileSync(analysisPath, 'utf8');\n              // Only include if there's meaningful content (not just \"no failures\")\n              if (!analysis.includes('No failures found')) {\n                body += `\\n<details>\\n<summary>Failure Analysis (click to expand)</summary>\\n\\n${analysis}\\n</details>`;\n              }\n            }\n\n            const issue_number = Number(process.env.PR_NUMBER);\n\n            // Always post a new comment\n            console.log(`Posting new comment on PR #${issue_number}...`);\n            try {\n              await github.rest.issues.createComment({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number,\n                body,\n              });\n              console.log('Comment created successfully');\n            } catch (err) {\n              console.error('Failed to post comment:', err.message);\n              console.error('Full error:', JSON.stringify(err, null, 2));\n              throw err;\n            }\n\n      # The benchmarks only modify the docs/llms directory.\n      # Commit the changes.\n      - name: Commit changes\n        run: |\n          git config user.name \"clockwork-labs-bot\"\n          git config user.email \"clockwork-labs-bot@users.noreply.github.com\"\n\n          # Prefer staging only the benchmark output area (adjust as needed)\n          git add docs/llms\n\n          git diff --cached --quiet && exit 0\n          git commit -m \"Update LLM benchmark results\"\n\n      # Here we use the https://github.com/clockwork-labs-bot user's\n      # personal access token to commit back to the PR branch. This is necessary\n      # if we want to be able to push back to external contributor forks.\n      - name: Push back to PR branch (same repo or fork)\n        env:\n          GH_TOKEN: ${{ secrets.CLOCKWORK_LABS_BOT_PAT }}\n        run: |\n          git remote set-url origin \"https://x-access-token:${GH_TOKEN}@github.com/${{ steps.pr.outputs.head_repo_full_name }}.git\"\n          # Fetch and rebase in case branch moved since workflow started (e.g., previous benchmark run)\n          git fetch origin \"${{ steps.pr.outputs.head_ref }}\"\n          if ! git rebase \"origin/${{ steps.pr.outputs.head_ref }}\"; then\n            git rebase --abort\n            echo \"::error::Rebase failed due to conflicts. The PR branch may have been updated during the benchmark run. Please re-run /update-llm-benchmark.\"\n            exit 1\n          fi\n          git push origin \"HEAD:${{ steps.pr.outputs.head_ref }}\"\n"
  },
  {
    "path": ".github/workflows/package.yml",
    "content": "name: Package SpacetimeDB CLI\n\non:\n  push:\n    tags:\n      - '**'\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  build-cli:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          # WARNING - do not upgrade this runner to 24.04 or the self hosted runners because it will break downloads for\n          #   anyone who uses a linux distro that doesn't have glibc >= GLIBC_2.38\n          - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: ubuntu-22.04 }\n          - { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner }\n          # Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964.\n          # - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine }\n          # FIXME: arm musl build. \"JavaScript Actions in Alpine containers are only supported on x64 Linux runners\"\n          # - { name: aarch64 Linux musl, target: aarch64-unknown-linux-musl, runner: arm-runner }\n          - { name: aarch64 macOS, target: aarch64-apple-darwin, runner: macos-latest }\n          - { name: x86_64 macOS, target: x86_64-apple-darwin, runner: macos-latest }\n          - { name: x86_64 Windows, target: x86_64-pc-windows-msvc, runner: windows-latest }\n\n    name: Build CLI for ${{ matrix.name }}\n    runs-on: ${{ matrix.runner }}\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Show arch\n        run: uname -a\n\n      - name: Install musl dependencies\n        # TODO: Should we use `matrix.container == 'alpine'` instead of the `endsWith` check?\n        if: endsWith(matrix.target, '-musl')\n        run: apk add gcc g++ bash curl linux-headers perl git make\n\n      - name: Install Rust\n        uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Install rust target\n        run: rustup target add ${{ matrix.target }}\n\n      - name: Add signtool.exe to PATH\n        if: ${{ runner.os == 'Windows' }}\n        shell: pwsh\n        run: |\n          $root = \"${env:ProgramFiles(x86)}\\Windows Kits\\10\\bin\"\n          $signtool = Get-ChildItem $root -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |\n            Where-Object { $_.FullName -match '\\\\x64\\\\signtool\\.exe$' } |\n            Sort-Object FullName -Descending |\n            Select-Object -First 1\n\n          if (-not $signtool) { throw \"signtool.exe not found under $root\" }\n\n          \"Found: $($signtool.FullName)\"\n          $dir = Split-Path $signtool.FullName\n          Add-Content -Path $env:GITHUB_PATH -Value $dir\n\n      - name: Write certificate file for signing\n        if: ${{ runner.os == 'Windows' }}\n        shell: powershell\n        env:\n          DIGICERT_CERT_B64: ${{ secrets.DIGICERT_CERT_B64 }}\n        run: |\n          [IO.File]::WriteAllBytes(\"digicert.pfx\", [Convert]::FromBase64String($env:DIGICERT_CERT_B64))\n\n      - name: Compile\n        run: |\n          cargo build --release --target ${{ matrix.target }} -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update\n\n      - name: Sign binaries for Windows\n        # Disabled for now since the current flow isn't working.\n        if: false\n        #if: ${{ runner.os == 'Windows' }}\n        shell: powershell\n        env:\n          DIGICERT_KEYPAIR_ALIAS: ${{ secrets.DIGICERT_KEYPAIR_ALIAS }}\n        run: |\n          $ErrorActionPreference = 'Stop'\n          $targetDir = Join-Path $env:GITHUB_WORKSPACE 'target\\x86_64-pc-windows-msvc\\release'\n          $certFile = Join-Path $env:GITHUB_WORKSPACE 'digicert.pfx'\n\n          $signtool = Get-Command signtool.exe -ErrorAction Stop\n\n          $files = @(\n            (Join-Path $targetDir 'spacetimedb-update.exe'),\n            (Join-Path $targetDir 'spacetimedb-cli.exe'),\n            (Join-Path $targetDir 'spacetimedb-standalone.exe')\n          )\n\n          foreach ($file in $files) {\n            & $signtool.Path sign /f $certFile /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $file\n            & $signtool.Path verify /v /pa $file\n          }\n\n      - name: Package (unix)\n        if: ${{ runner.os != 'Windows' }}\n        shell: bash\n        run: |\n          mkdir build\n          cd target/${{matrix.target}}/release\n          cp spacetimedb-update ../../../build/spacetimedb-update-${{matrix.target}}\n          tar -czf ../../../build/spacetime-${{matrix.target}}.tar.gz spacetimedb-{cli,standalone}\n\n      - name: Package (windows)\n        if: ${{ runner.os == 'Windows' }}\n        shell: bash\n        run: |\n          mkdir build\n          cd target/${{matrix.target}}/release\n          cp spacetimedb-update.exe ../../../build/spacetimedb-update-${{matrix.target}}.exe\n          7z a ../../../build/spacetime-${{matrix.target}}.zip spacetimedb-cli.exe spacetimedb-standalone.exe\n\n      - name: Extract branch name\n        shell: bash\n        run: echo \"branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}\" >> $GITHUB_OUTPUT\n        id: extract_branch\n\n      - name: Upload to DO Spaces\n        uses: shallwefootball/s3-upload-action@master\n        with:\n          aws_key_id: ${{ secrets.AWS_KEY_ID }}\n          aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}\n          aws_bucket: ${{ vars.AWS_BUCKET }}\n          source_dir: build\n          endpoint: https://nyc3.digitaloceanspaces.com\n          destination_dir: ${{ steps.extract_branch.outputs.branch }}\n"
  },
  {
    "path": ".github/workflows/pr_approval_check.yml",
    "content": "name: Review Checks\n\n# SECURITY: This workflow uses pull_request_target so that it has write access to\n# set commit statuses on external (fork) PRs. pull_request_target runs in the\n# context of the base branch, which grants the GITHUB_TOKEN write permissions\n# that a regular pull_request event on a fork would not have.\n#\n# IMPORTANT: This workflow must NEVER check out, build, or execute code from the\n# PR branch. Doing so would allow a malicious fork to run arbitrary code with\n# write access to the repository. This workflow only reads PR metadata via the\n# GitHub API, which is safe.\n\non:\n  pull_request_target:\n    types: [opened, synchronize, reopened]\n  pull_request_review:\n    types: [submitted, dismissed]\n  merge_group:\n\npermissions:\n  contents: read\n  pull-requests: read\n  statuses: write\n\nconcurrency:\n  group: pr-approval-check-${{ github.event.pull_request.number || github.sha }}\n  cancel-in-progress: true\n\njobs:\n  publish-approval-status:\n    name: Set approval status\n    runs-on: ubuntu-latest\n\n    # SECURITY: Do not add a checkout step to this job. See comment at the top of this file.\n    steps:\n      - name: Evaluate and publish approval status\n        uses: actions/github-script@v7\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            const contextName = \"PR approval check\";\n\n            let targetSha;\n            let state;\n            let description;\n\n            if (context.eventName === \"merge_group\") {\n              targetSha = process.env.GITHUB_SHA;\n              state = \"success\";\n              description = \"Merge group entry; approvals already satisfied\";\n            } else {\n              const pr = context.payload.pull_request;\n              targetSha = pr.head.sha;\n\n              if (pr.head.repo.fork) {\n                state = \"success\";\n                description = \"Skipped for external PR\";\n              } else if (pr.user.login !== \"clockwork-labs-bot\") {\n                state = \"success\";\n                description = \"PR author is not clockwork-labs-bot\";\n              } else {\n                const result = await github.graphql(\n                  `\n                  query($owner: String!, $repo: String!, $number: Int!) {\n                    repository(owner: $owner, name: $repo) {\n                      pullRequest(number: $number) {\n                        latestOpinionatedReviews(first: 100, writersOnly: true) {\n                          nodes {\n                            state\n                            author {\n                              login\n                            }\n                          }\n                        }\n                      }\n                    }\n                  }\n                  `,\n                  {\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    number: pr.number,\n                  }\n                );\n\n                const effectiveApprovers =\n                  result.repository.pullRequest.latestOpinionatedReviews.nodes\n                    .filter((review) => review.state === \"APPROVED\")\n                    .map((review) => review.author?.login)\n                    .filter(Boolean);\n\n                core.info(\n                  `Latest effective approvers (${effectiveApprovers.length}): ${effectiveApprovers.join(\", \")}`\n                );\n\n                if (effectiveApprovers.length < 2) {\n                  state = \"failure\";\n                  description = \"PRs from clockwork-labs-bot require at least 2 approvals\";\n                } else {\n                  state = \"success\";\n                  description = \"PR has the required number of approvals\";\n                }\n              }\n            }\n\n            core.info(`Publishing status ${state} for ${targetSha}: ${description}`);\n\n            // We need to set a separate commit status for this, because it runs on both\n            // pull_request and pull_request_review events. If we don't set an explicit context,\n            // what happens is that there are sometimes two separate statuses on the same commit -\n            // one from each event type. This leads to weird cases where one copy of the check is failed,\n            // and the other is successful, and the failed one blocks the PR from merging.\n            await github.rest.repos.createCommitStatus({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              sha: targetSha,\n              state,\n              context: contextName,\n              description,\n            });\n"
  },
  {
    "path": ".github/workflows/rust_matcher.json",
    "content": "{\n    \"problemMatcher\": [\n        {\n            \"owner\": \"rust\",\n            \"pattern\": [\n                {\n                    \"regexp\": \"^(warning|warn|error)(\\\\[(.*)\\\\])?: (.*)$\",\n                    \"severity\": 1,\n                    \"message\": 4,\n                    \"code\": 3\n                },\n                {\n                    \"regexp\": \"^([\\\\s->=]*(.*):(\\\\d*):(\\\\d*)|.*)$\",\n                    \"file\": 2,\n                    \"line\": 3,\n                    \"column\": 4\n                }\n            ]\n        }\n    ]\n}\n"
  },
  {
    "path": ".github/workflows/tag-release.yml",
    "content": "on:\n  release:\n    types: [published]\n\njobs:\n  on-release:\n    name: Re-tag latest\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n\n      - name: Login to DockerHub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ vars.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_PASSWORD }}\n \n      - name: Retag the image \n        run: |\n          VERSION=${GITHUB_REF#refs/*/}\n          docker buildx imagetools create clockworklabs/spacetimedb:$VERSION --tag clockworklabs/spacetimedb:latest\n"
  },
  {
    "path": ".github/workflows/typescript-lint.yml",
    "content": "name: TypeScript - Lint\n\non:\n  pull_request:\n  push:\n    branches:\n      - master\n  merge_group:\n\njobs:\n  build:\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 22\n\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      - name: Get pnpm store directory\n        shell: bash\n        run: |\n          echo \"STORE_PATH=$(pnpm store path --silent)\" >> $GITHUB_ENV\n\n      - uses: actions/cache@v4\n        name: Setup pnpm cache\n        with:\n          path: ${{ env.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n\n      - name: Lint\n        run: pnpm lint\n"
  },
  {
    "path": ".github/workflows/typescript-test.yml",
    "content": "name: TypeScript - Tests\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n  merge_group:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || format('sha-{0}', github.sha) }}\n  cancel-in-progress: true\n\njobs:\n  build-and-test:\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 22\n\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n\n      - name: Get pnpm store directory\n        shell: bash\n        working-directory: crates/bindings-typescript\n        run: |\n          echo \"STORE_PATH=$(pnpm store path --silent)\" >> $GITHUB_ENV\n\n      - uses: actions/cache@v4\n        name: Setup pnpm cache\n        with:\n          path: ${{ env.STORE_PATH }}\n          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n          restore-keys: |\n            ${{ runner.os }}-pnpm-store-\n      \n      - name: Build module library and SDK\n        working-directory: crates/bindings-typescript\n        run: pnpm build\n\n      - name: Run module library and SDK tests\n        working-directory: crates/bindings-typescript \n        run: pnpm test\n\n      # - name: Extract SpacetimeDB branch name from file\n      #   id: extract-branch\n      #   run: |\n      #     # Define the path to the branch file\n      #     BRANCH_FILE=\".github/spacetimedb-branch.txt\"\n\n      #     # Default to master if file doesn't exist\n      #     if [ ! -f \"$BRANCH_FILE\" ]; then\n      #       echo \"::notice::No SpacetimeDB branch file found, using 'master'\"\n      #       echo \"branch=master\" >> $GITHUB_OUTPUT\n      #       exit 0\n      #     fi\n\n      #     # Read and trim whitespace from the file\n      #     branch=$(cat \"$BRANCH_FILE\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n\n      #     # Fallback to master if empty\n      #     if [ -z \"$branch\" ]; then\n      #       echo \"::warning::SpacetimeDB branch file is empty, using 'master'\"\n      #       branch=\"master\"\n      #     fi\n\n      #     echo \"branch=$branch\" >> $GITHUB_OUTPUT\n      #     echo \"Using SpacetimeDB branch from file: $branch\"\n\n      - name: Install Rust toolchain\n        uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n\n      - name: Cache Rust dependencies\n        uses: Swatinem/rust-cache@v2\n        with:\n          workspaces: ${{ github.workspace }}\n          shared-key: spacetimedb\n          # Let the main CI job save the cache since it builds the most things\n          save-if: false\n          prefix-key: v1\n\n      # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a.\n      # ChatGPT suspects that this could be due to different build invocations using the same target dir,\n      # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with\n      # `cargo build --manifest-path` (which apparently build different dependency trees).\n      # However, we've been unable to fix it so... /shrug\n      - name: Check v8 outputs\n        run: |\n          find \"${CARGO_TARGET_DIR}\"/ -type f | grep '[/_]v8' || true\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/debug/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean -p v8 || true\n            cargo build -p v8\n          fi\n          if ! [ -f \"${CARGO_TARGET_DIR}\"/release/gn_out/obj/librusty_v8.a ]; then\n            echo \"Could not find v8 output file librusty_v8.a; rebuilding manually.\"\n            cargo clean --release -p v8 || true\n            cargo build --release -p v8\n          fi\n\n      - name: Install SpacetimeDB CLI from the local checkout\n        run: |\n          export CARGO_HOME=\"$HOME/.cargo\"\n          echo \"$CARGO_HOME/bin\" >> \"$GITHUB_PATH\"\n          cargo install --force --path crates/cli --locked --message-format=short\n          cargo install --force --path crates/standalone --locked --message-format=short\n          # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules).\n          ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime\n          # Clear any existing information\n          spacetime server clear -y\n\n      - name: Generate client bindings\n        working-directory: templates/chat-react-ts\n        run: |\n          pnpm generate\n\n      - name: Check for changes\n        working-directory: templates/chat-react-ts\n        run: |\n          \"${GITHUB_WORKSPACE}\"/tools/check-diff.sh src/module_bindings || {\n            echo \"Error: Bindings are dirty. Please generate bindings again and commit them to this branch.\"\n            exit 1\n          }\n\n      # - name: Start SpacetimeDB\n      #   run: |\n      #     spacetime start &\n      #     disown\n\n      # - name: Publish module to SpacetimeDB\n      #   working-directory: SpacetimeDB/templates/quickstart-chat-typescript/spacetimedb\n      #   run: |\n      #     spacetime logout && spacetime login --server-issued-login local\n      #     spacetime publish -s local quickstart-chat -c -y\n\n      # - name: Publish module to SpacetimeDB\n      #   working-directory: SpacetimeDB/templates/quickstart-chat-typescript/spacetimedb\n      #   run: |\n      #     spacetime logs quickstart-chat\n\n      - name: Check that quickstart-chat builds\n        working-directory: templates/chat-react-ts\n        run: pnpm build\n\n      - name: Check that templates build\n        working-directory: templates/\n        run: pnpm -r --filter \"./**\" run build\n\n      - name: Check that subdirectories build\n        working-directory: crates/bindings-typescript\n        run: pnpm -r --filter \"./**\" run build\n\n      # - name: Run quickstart-chat tests\n      #   working-directory: examples/quickstart-chat\n      #   run: pnpm test\n      #\n      # # Run this step always, even if the previous steps fail\n      # - name: Print rows in the user table\n      #   if: always()\n      #   run: spacetime sql quickstart-chat \"SELECT * FROM user\"\n"
  },
  {
    "path": ".github/workflows/upgrade-version-check.yml",
    "content": "name: Upgrade Version Check\n\non:\n  pull_request:\n    types: [opened, synchronize]\n  merge_group:\npermissions: read-all\n\njobs:\n  version_upgrade_check:\n    runs-on: spacetimedb-new-runner-2\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n      - uses: dsherret/rust-toolchain-file@v1\n      - name: Set default rust toolchain\n        run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)\n        # pnpm is required for regenerating the typescript bindings\n      - name: Set up Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n      - uses: pnpm/action-setup@v4\n        with:\n          run_install: true\n      - name: Verify that upgrade-version still works\n        run: cargo bump-versions 123.456.789 --rust-and-cli --csharp --typescript --cpp --accept-snapshots\n      - name: Show diff\n        run: git diff HEAD\n\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.toptal.com/developers/gitignore/api/rust,node,visualstudiocode\n# Edit at https://www.toptal.com/developers/gitignore?templates=rust,node,visualstudiocode\n\nperf.data\nperf.data.old\nflamegraph.html\nflamegraphs/*.svg\nflamegraphs/flamegraph.folded\n\n### MacOS ###\n.DS_Store\n\n### Node ###\n# Logs\npackages/logs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules\njspm_packages\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n.temp\n\n# Docusaurus cache and generated files\n.docusaurus\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n### Node Patch ###\n# Serverless Webpack directories\n.webpack/\n\n# Optional stylelint cache\n\n# SvelteKit build / generate output\n.svelte-kit\n\n### Rust ###\n# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n# for docker images touching host fs\nlinux-cache/\nlinux-rustup/\nlinux-target/\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n### VisualStudioCode ###\n.vscode/*\n.vscode\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n# Local History for Visual Studio Code\n.history/\n\n# Built Visual Studio Code Extensions\n*.vsix\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n# Support for Project snippet scope\n\n# End of https://www.toptal.com/developers/gitignore/api/rust,node,visualstudiocode\n\n\n# Added by cargo\n#\n# already existing elements were commented out\n\n/target\n#Cargo.lock\n\n__pycache__/\n.test_out\n\n## JetBrains\n.idea/\n\n/protobuf\ncs-src/\ncrates/bench/spacetime.svg\ncrates/bench/sqlite.svg\n.vs/\n\n# .NET build artifacts\n**/obj/\n**/bin/\n\n# benchmark files\nout.json\nold.json\nnew.json\n.history.txt\n\n# Keys\n*.pem\n.ok.sql\n\n# Intermediate file generated when updating the external client SDKs' WS format bindings\ncrates/client-api-messages/ws_schema.json\n\n# Test data\n!crates/core/testdata/\n\n# Temporary templates dir for CLI's init command\n/crates/cli/.templates\n\n# C++ build data\nmodules/benchmarks-cpp/build/\nmodules/module-test-cpp/build/\nmodules/sdk-test-cpp/build/\nmodules/sdk-test-connect-disconnect-cpp/build/\nmodules/sdk-test-procedure-cpp/build/\nmodules/sdk-test-view-cpp/build/\n\n# Symlinked output from `nix build`\nresult\n\n# Python venv directories\nvenv/\n\n# Claude Code\n.claude/\n\n# Windows null device artifact\nnul\n\n[Nn][Uu][Gg][Ee][Tt].[Cc][Oo][Nn][Ff][Ii][Gg]\n[Nn][Uu][Gg][Ee][Tt].[Cc][Oo][Nn][Ff][Ii][Gg].meta\n \n# csharp SDK packages\n/sdks/csharp/packages/\n/sdks/csharp/packages.meta\n\n# AI agent config\n.codex\n.claude\n\n# Any local file\n*.local\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules\npnpm-lock.yaml\ndist\ntarget\n.github\ncoverage\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"arrowParens\": \"avoid\",\n  \"jsxSingleQuote\": false,\n  \"trailingComma\": \"es5\",\n  \"endOfLine\": \"auto\",\n  \"printWidth\": 80\n}\n"
  },
  {
    "path": ".rustfmt.toml",
    "content": "max_width = 120\nstyle_edition = \"2021\"\nedition = \"2024\"\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nexclude = [\"crates/smoketests/modules\"]\nmembers = [\n  \"crates/auth\",\n  \"crates/bench\",\n  \"crates/bindings-sys\",\n  \"crates/bindings\",\n  \"crates/bindings-macro\",\n  \"crates/cli\",\n  \"crates/client-api\",\n  \"crates/client-api-messages\",\n  \"crates/commitlog\",\n  \"crates/core\",\n  \"crates/data-structures\",\n  \"crates/datastore\",\n  \"crates/durability\",\n  \"crates/execution\",\n  \"crates/expr\",\n  \"crates/guard\",\n  \"crates/fs-utils\",\n  \"crates/lib\",\n  \"crates/metrics\",\n  \"crates/paths\",\n  \"crates/pg\",\n  \"crates/physical-plan\",\n  \"crates/primitives\",\n  \"crates/query\",\n  \"crates/sats\",\n  \"crates/schema\",\n  \"crates/smoketests\",\n  \"sdks/rust\",\n  \"sdks/unreal\",\n  \"crates/snapshot\",\n  \"crates/sqltest\",\n  \"crates/sql-parser\",\n  \"crates/standalone\",\n  \"crates/subscription\",\n  \"crates/table\",\n  \"crates/testing\",\n  \"crates/update\",\n  \"modules/benchmarks\",\n  \"modules/keynote-benchmarks\",\n  \"modules/perf-test\",\n  \"modules/module-test\",\n  \"templates/basic-rs/spacetimedb\",\n  \"templates/chat-console-rs/spacetimedb\",\n  \"templates/keynote-2/spacetimedb-rust-client\",\n  \"modules/sdk-test\",\n  \"modules/sdk-test-connect-disconnect\",\n  \"modules/sdk-test-procedure\",\n  \"modules/sdk-test-view\",\n  \"modules/sdk-test-view-pk\",\n  \"modules/sdk-test-event-table\",\n  \"sdks/rust/tests/test-client\",\n  \"sdks/rust/tests/test-counter\",\n  \"sdks/rust/tests/connect_disconnect_client\",\n  \"sdks/rust/tests/procedure-client\",\n  \"sdks/rust/tests/view-client\",\n  \"sdks/rust/tests/view-pk-client\",\n  \"sdks/rust/tests/event-table-client\",\n  \"tools/ci\",\n  \"tools/upgrade-version\",\n  \"tools/license-check\",\n  \"tools/replace-spacetimedb\",\n  \"tools/generate-client-api\",\n  \"tools/gen-bindings\",\n  \"tools/xtask-llm-benchmark\",\n  \"crates/bindings-typescript/test-app/server\",\n  \"crates/bindings-typescript/test-react-router-app/server\",\n  \"crates/query-builder\",\n]\ndefault-members = [\"crates/cli\", \"crates/standalone\", \"crates/update\"]\n# cargo feature graph resolver. v3 is default in edition2024 but workspace\n# manifests don't have editions.\nresolver = \"3\"\n\n[profile.release]\nopt-level = 3\ndebug-assertions = false\noverflow-checks = false\nlto = \"thin\"\npanic = 'unwind'\nincremental = false\ncodegen-units = 16\nrpath = false\n\n[profile.dev]\nopt-level = 0\ndebug = true\ndebug-assertions = true\noverflow-checks = true\nlto = false\npanic = 'unwind'\nincremental = true\ncodegen-units = 256\nrpath = false\n\n[profile.bench]\ndebug = true\n\n[profile.test]\nopt-level = 1\ndebug = true\n\n[profile.profiling]\ninherits = \"release\"\ndebug = true\n\n[workspace.package]\nversion = \"2.0.5\"\nedition = \"2024\"\n# update rust-toolchain.toml too!\nrust-version = \"1.93.0\"\n\n[workspace.dependencies]\nspacetimedb = { path = \"crates/bindings\", version = \"=2.0.5\" }\nspacetimedb-auth = { path = \"crates/auth\", version = \"=2.0.5\" }\nspacetimedb-bindings-macro = { path = \"crates/bindings-macro\", version = \"=2.0.5\" }\nspacetimedb-bindings-sys = { path = \"crates/bindings-sys\", version = \"=2.0.5\" }\nspacetimedb-cli = { path = \"crates/cli\", version = \"=2.0.5\" }\nspacetimedb-client-api = { path = \"crates/client-api\", version = \"=2.0.5\" }\nspacetimedb-client-api-messages = { path = \"crates/client-api-messages\", version = \"=2.0.5\" }\nspacetimedb-codegen = { path = \"crates/codegen\", version = \"=2.0.5\" }\nspacetimedb-commitlog = { path = \"crates/commitlog\", version = \"=2.0.5\" }\nspacetimedb-core = { path = \"crates/core\", version = \"=2.0.5\" }\nspacetimedb-data-structures = { path = \"crates/data-structures\", version = \"=2.0.5\" }\nspacetimedb-datastore = { path = \"crates/datastore\", version = \"=2.0.5\" }\nspacetimedb-durability = { path = \"crates/durability\", version = \"=2.0.5\" }\nspacetimedb-execution = { path = \"crates/execution\", version = \"=2.0.5\" }\nspacetimedb-expr = { path = \"crates/expr\", version = \"=2.0.5\" }\nspacetimedb-guard = { path = \"crates/guard\", version = \"=2.0.5\" }\nspacetimedb-lib = { path = \"crates/lib\", default-features = false, version = \"=2.0.5\" }\nspacetimedb-memory-usage = { path = \"crates/memory-usage\", version = \"=2.0.5\", default-features = false }\nspacetimedb-metrics = { path = \"crates/metrics\", version = \"=2.0.5\" }\nspacetimedb-paths = { path = \"crates/paths\", version = \"=2.0.5\" }\nspacetimedb-pg = { path = \"crates/pg\", version = \"=2.0.5\" }\nspacetimedb-physical-plan = { path = \"crates/physical-plan\", version = \"=2.0.5\" }\nspacetimedb-primitives = { path = \"crates/primitives\", version = \"=2.0.5\" }\nspacetimedb-query = { path = \"crates/query\", version = \"=2.0.5\" }\nspacetimedb-sats = { path = \"crates/sats\", version = \"=2.0.5\" }\nspacetimedb-schema = { path = \"crates/schema\", version = \"=2.0.5\" }\nspacetimedb-standalone = { path = \"crates/standalone\", version = \"=2.0.5\" }\nspacetimedb-sql-parser = { path = \"crates/sql-parser\", version = \"=2.0.5\" }\nspacetimedb-table = { path = \"crates/table\", version = \"=2.0.5\" }\nspacetimedb-fs-utils = { path = \"crates/fs-utils\", version = \"=2.0.5\" }\nspacetimedb-snapshot = { path = \"crates/snapshot\", version = \"=2.0.5\" }\nspacetimedb-subscription = { path = \"crates/subscription\", version = \"=2.0.5\" }\nspacetimedb-query-builder = { path = \"crates/query-builder\", version = \"=2.0.5\" }\n\n# Prevent `ahash` from pulling in `getrandom` by disabling default features.\n# Modules use `getrandom02` and we need to prevent an incompatible version\n# from appearing in module dependency graphs.\nahash = { version = \"0.8\", default-features = false, features = [\"std\"] }\nanyhow = \"1.0.68\"\nanymap = \"0.12\"\narrayvec = \"0.7.2\"\nasync-stream = \"0.3.6\"\nasync-trait = \"0.1.68\"\naxum = { version = \"0.7\", features = [\"tracing\"] }\naxum-extra = { version = \"0.9\", features = [\"typed-header\"] }\nbacktrace = \"0.3.66\"\nbase64 = \"0.21.2\"\nbigdecimal = \"0.4.7\"\nbitflags = \"2.3.3\"\nblake3 = \"1.5.1\"\nbrotli = \"3.5\"\nbyte-unit = \"4.0.18\"\nbytemuck = { version = \"1.16.2\", features = [\"must_cast\"] }\nbytes = \"1.10.1\"\nbytestring = { version = \"1.2.0\", features = [\"serde\"] }\ncargo_metadata = \"0.17.0\"\nchrono = { version = \"0.4.24\", default-features = false }\nclap = { version = \"4.2.4\", features = [\"derive\", \"wrap_help\"] }\nclap-markdown = \"0.1.4\"\ncolored = \"2.0.0\"\nconsole = { version = \"0.15.6\" }\nconvert_case = \"0.6.0\"\ncrc32c = \"0.6.4\"\ncriterion = { version = \"0.5.1\", features = [\"async\", \"async_tokio\", \"html_reports\"] }\ncrossbeam-channel = \"0.5\"\ncrossbeam-queue = \"0.3.12\"\ncursive = { version = \"0.20\", default-features = false, features = [\"crossterm-backend\"] }\ndecorum = { version = \"0.3.1\", default-features = false, features = [\"std\"] }\nderive_more = \"0.99\"\ndialoguer = { version = \"0.11\", default-features = false }\ndirs = \"5.0.1\"\nduct = \"0.13.5\"\neither = \"1.9\"\nemail_address = \"0.2.4\"\nenum-as-inner = \"0.6\"\nenum-map = \"2.6.3\"\nenv_logger = \"0.10\"\nethnum = { version = \"1.5.0\", features = [\"serde\"] }\nflate2 = \"1.0.24\"\nflume = { version = \"0.11\", default-features = false, features = [\"async\"] }\nfoldhash = \"0.2.0\"\nfs-err = \"2.9.0\"\nfs_extra = \"1.3.0\"\nfs2 = \"0.4.3\"\nfutures = \"0.3\"\nfutures-channel = \"0.3\"\nfutures-util = \"0.3\"\ngetrandom02 = { package = \"getrandom\", version = \"0.2\" }\ngit2 = \"0.19\"\nglob = \"0.3.1\"\nhashbrown = { version = \"0.16.1\", default-features = false, features = [\"equivalent\", \"inline-more\", \"rayon\", \"serde\"] }\nheaders = \"0.4\"\nheck = \"0.4\"\nhex = \"0.4.3\"\nhome = \"0.5\"\nhostname = \"^0.3\"\nhttp = \"1.0\"\nhttp-body-util= \"0.1.3\"\nhumantime = \"2.3\"\nhyper = \"1.0\"\nhyper-util = { version = \"0.1\", features = [\"tokio\"] }\nignore = \"0.4\"\nimara-diff = \"0.1.3\"\nindexmap = \"2.0.0\"\nindicatif = \"0.17\"\ninsta = { version = \"1.21.0\", features = [\"toml\", \"filters\"] }\nis-terminal = \"0.4\"\nitertools = \"0.12\"\nitoa = \"1\"\njson5 = \"0.4\"\njsonwebtoken = { package = \"spacetimedb-jsonwebtoken\", version = \"9.3.0\" }\njunction = \"1\"\njwks = { package = \"spacetimedb-jwks\", version = \"0.1.3\" }\nlazy_static = \"1.4.0\"\nlean_string = \"0.5.1\"\nlog = \"0.4.17\"\nmemchr = \"2\"\nmimalloc = \"0.1.39\"\nnames = \"0.14\"\nnetstat2 = \"0.11\"\nnohash-hasher = \"0.2\"\nnotify = \"7.0\"\nnix = \"0.30\"\nonce_cell = \"1.16\"\nparking_lot = { version = \"0.12.1\", features = [\"send_guard\", \"arc_lock\"] }\nparse-size = \"1.1.0\"\npaste = \"1.0\"\npercent-encoding = \"2.3\"\npetgraph = { version = \"0.6.5\", default-features = false }\npin-project-lite = \"0.2.9\"\npgwire = { version = \"0.37\", default-features = false, features = [\"server-api\", \"pg-ext-types\"] }\npostgres-types = \"0.2.5\"\npretty_assertions = { version = \"1.4\", features = [\"unstable\"] }\nproc-macro2 = \"1.0\"\nprometheus = \"0.14.0\"\nproptest = \"1.4\"\nproptest-derive = \"0.5\"\nquick-junit = { version = \"0.3.2\" }\nquick-xml = \"0.31\"\nquote = \"1.0.8\"\nrand08 = { package = \"rand\", version = \"0.8\" }\nrand = \"0.9\"\nrand_distr = \"0.5.1\"\nrayon = \"1.8\"\nrayon-core = \"1.11.0\"\nregex = \"1\"\nreqwest = { version = \"0.12\", features = [\"stream\", \"json\"] }\nrolldown = { git = \"https://github.com/rolldown/rolldown.git\", tag = \"v1.0.0-rc.3\" }\nrolldown_common = { git = \"https://github.com/rolldown/rolldown.git\", tag = \"v1.0.0-rc.3\" }\nrolldown_error = { git = \"https://github.com/rolldown/rolldown.git\", tag = \"v1.0.0-rc.3\" }\nrolldown_utils = { git = \"https://github.com/rolldown/rolldown.git\", tag = \"v1.0.0-rc.3\" }\nron = \"0.8\"\nrusqlite = { version = \"0.29.0\", features = [\"bundled\", \"column_decltype\"] }\nrust_decimal = { version = \"1.29.1\", features = [\"db-tokio-postgres\"] }\nrustc-demangle = \"0.1.21\"\nrustc-hash = \"2\"\nrustyline = { version = \"12.0.0\", features = [] }\nscoped-tls = \"1.0.1\"\nscopeguard = \"1.1.0\"\nsecond-stack = \"0.3\"\nself-replace = \"1.5\"\nsemver = \"1\"\nserde = { version = \"1.0.136\", features = [\"derive\"] }\nserde_json = { version = \"1.0.128\", features = [\"raw_value\", \"arbitrary_precision\"] }\nserde_path_to_error = \"0.1.9\"\nserde_with = { version = \"3.3.0\", features = [\"base64\", \"hex\"] }\nserial_test = \"2.0.0\"\nsha1 = \"0.10.1\"\nsha3 = \"0.10.0\"\nsimilar = \"2.3\"\nslab = \"0.4.7\"\nsled = \"0.34.7\"\nsmallvec = { version = \"1.11\", features = [\"union\", \"const_generics\"] }\nsocket2 = \"0.5\"\nsourcemap = \"9.3.1\"\nsqllogictest = \"0.17\"\nsqllogictest-engines = \"0.17\"\nsqlparser = \"0.38.0\"\nstrum = { version = \"0.25.0\", features = [\"derive\"] }\nsyn = { version = \"2\", features = [\"full\", \"extra-traits\"] }\nsyntect = { version = \"5.0.0\", default-features = false, features = [\"default-fancy\"] }\ntabled = \"0.14.0\"\ntar = \"0.4\"\ntempdir = \"0.3.7\"\ntempfile = \"3.20\"\ntermcolor = \"1.2.0\"\ntermtree = \"0.5.1\"\nthin-vec = \"0.2.13\"\nthiserror = \"1.0.37\"\ntokio = { version = \"1.37\", features = [\"full\"] }\ntokio_metrics = { version = \"0.4.0\" }\ntokio-postgres = { version = \"0.7.8\", features = [\"with-chrono-0_4\"] }\ntokio-stream = \"0.1.17\"\ntokio-tungstenite = { version = \"0.27.0\", features = [\"native-tls\", \"url\"] }\ntokio-util = { version = \"0.7.4\", features = [\"time\"] }\ntoml = \"0.8\"\ntoml_edit = \"0.22.22\"\ntower-http = { version = \"0.5\", features = [\"cors\"] }\ntower-layer = \"0.3\"\ntower-service = \"0.3\"\ntracing = \"0.1.37\"\ntracing-appender = \"0.2.2\"\ntracing-core = \"0.1.31\"\ntracing-flame = \"0.2.0\"\ntracing-log = \"0.1.3\"\ntracing-subscriber = { version = \"0.3.17\", features = [\"env-filter\"] }\ntrybuild = \"1\"\ntyped-arena = \"2.0\"\nunicode-ident = \"1.0.12\"\nunicode-normalization = \"0.1.23\"\nurl = \"2.3.1\"\nurlencoding = \"2.1.2\"\nuuid = { version = \"1.18.1\", default-features = false }\n\n# Pinned to a specific version rather than allowing SemVer fuzzy resolution\n# because our flake references the specific version of this library.\n# See librusty_v8.nix for more details.\n# When updating the V8 crate, either update the version and hashes in that file,\n# or ping phoebe @gefjon to do so.\nv8 = \"=145.0.0\"\n# This version is kept in sync with the version of ICU required by v8.\n# When that changes, the `v8::icu::set_common_data_XX()` function is renamed.\ndeno_core_icudata = \"0.77\"\n\nwalkdir = \"2.2.5\"\nwasmbin = \"0.6\"\nwebbrowser = \"1.0.2\"\nwindows-sys = \"0.59\"\nxdg = \"2.5\"\nxmltree = \"0.11\"\ntikv-jemallocator = { version = \"0.6.0\", features = [\"profiling\", \"stats\"] }\ntikv-jemalloc-ctl = { version = \"0.6.0\", features = [\"stats\"] }\njemalloc_pprof = { version = \"0.8\", features = [\"symbolize\", \"flamegraph\"] }\nzstd-framed = { version = \"0.1.1\", features = [\"tokio\"] }\n\n# Vendor the openssl we rely on, rather than depend on a\n# potentially very old system version.\nopenssl = { version = \"0.10\", features = [\"vendored\"] }\n\n[workspace.dependencies.wasmtime]\nversion = \"39\"\ndefault-features = false\nfeatures = [\n  \"addr2line\",\n  \"async\",\n  \"cache\",\n  \"cranelift\",\n  \"demangle\",\n  \"parallel-compilation\",\n  \"runtime\",\n  \"std\",\n]\n\n[workspace.dependencies.wasmtime-internal-fiber]\nversion = \"39\"\ndefault-features = false\nfeatures = [\"std\"]\n\n[workspace.dependencies.tracing-tracy]\nversion = \"0.10.4\"\n# We use the \"ondemand\" feature to allow connecting after the start,\n# and reconnecting, from the tracy client to the database.\n# TODO(George): Need to be able to remove \"broadcast\" in some build configurations.\nfeatures = [\n  \"enable\",\n  \"system-tracing\",\n  \"context-switch-tracing\",\n  \"sampling\",\n  \"code-transfer\",\n  \"broadcast\",\n  \"ondemand\",\n]\n\n[workspace.lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(tokio_unstable)'] }\n\n[workspace.lints.clippy]\n# FIXME: we should work on this lint incrementally\nresult_large_err = \"allow\"\n\n[workspace.metadata]\n# This silences a warning in our Nix flake, which otherwise would be upset that our call to `crateNameFromCargoToml`\n# is running against a file that doesn't have a name in it.\ncrane.name = \"spacetimedb\"\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Use a base image that supports multi-arch\nFROM rust:bookworm AS builder\n\nWORKDIR /usr/src/app\nCOPY . .\n\n# If we're in a git submodule, we'll have a corrupted/nonfunctional .git file instead of a proper .git directory.\n# To make the errors more sane, remove .git entirely.\nRUN if [ -f .git ]; then \\\n      echo \"❌ ERROR: .git is a file (likely a submodule pointer), not a directory.\" >&2; \\\n      echo \"This will cause errors in the build process, because git operations will fail.\" >&2; \\\n      echo \"To address this, replace the .git file with a proper .git directory.\" >&2; \\\n      exit 1; \\\n    fi\n\nRUN cargo build -p spacetimedb-standalone -p spacetimedb-cli --release --locked\n\nFROM rust:bookworm\n\n# Install dependencies\nRUN apt-get update && apt-get install -y \\\n      curl \\\n      ca-certificates \\\n      binaryen \\\n      build-essential \\\n      && rm -rf /var/lib/apt/lists/*\n\n# Determine architecture for .NET installation\nARG TARGETARCH\nENV DOTNET_ARCH=${TARGETARCH}\n\nRUN if [ \"$DOTNET_ARCH\" = \"amd64\" ]; then \\\n        DOTNET_ARCH=\"x64\"; \\\n    elif [ \"$DOTNET_ARCH\" = \"arm64\" ]; then \\\n        DOTNET_ARCH=\"arm64\"; \\\n    else \\\n        echo \"Unsupported architecture: $DOTNET_ARCH\" && exit 1; \\\n    fi && \\\n    curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 8.0 --install-dir /usr/share/dotnet --architecture $DOTNET_ARCH\n\nENV PATH=\"/usr/share/dotnet:${PATH}\"\n\n# Install the experimental WASI workload\nRUN dotnet workload install wasi-experimental\n\n# Install Rust WASM target\nRUN rustup target add wasm32-unknown-unknown\n\n# Copy over SpacetimeDB\nCOPY --from=builder --chmod=755 /usr/src/app/target/release/spacetimedb-standalone /usr/src/app/target/release/spacetimedb-cli /opt/spacetime/\nRUN ln -s /opt/spacetime/spacetimedb-cli /usr/local/bin/spacetime\n\n# Create and switch to a non-root user\nRUN useradd -m spacetime\nUSER spacetime\n\n# Set working directory\nWORKDIR /app\n\n# Expose the necessary port\nEXPOSE 3000\n\n# Define the entrypoint\nENTRYPOINT [\"spacetime\"]\n\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "SPACETIMEDB BUSINESS SOURCE LICENSE AGREEMENT\n\nBusiness Source License 1.1\n\nParameters\n\nLicensor:             Clockwork Laboratories, Inc.\nLicensed Work:        SpacetimeDB 2.0.5\n                      The Licensed Work is\n                      (c) 2023 Clockwork Laboratories, Inc.\n                      \nAdditional Use Grant: You may make use of the Licensed Work provided your\n                      application or service uses the Licensed Work with no\n                      more than one SpacetimeDB instance in production and\n                      provided that you do not use the Licensed Work for a\n                      Database Service.\n\n                      A “Database Service” is a commercial offering that\n                      allows third parties (other than your employees and\n                      contractors) to access the functionality of the\n                      Licensed Work by creating tables whose schemas are\n                      controlled by such third parties.\n\nChange Date:          2031-03-12\n\nChange License:       GNU Affero General Public License v3.0 with a linking\n                      exception\n\nFor information about alternative licensing arrangements for the Software,\nplease visit: https://spacetimedb.com\n\nNotice\n\nThe Business Source License (this document, or the “License”) is not an Open\nSource license. However, the Licensed Work will eventually be made available\nunder an Open Source License, as stated in this License.\n\nLicense text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.\n“Business Source License” is a trademark of MariaDB Corporation Ab.\n\n-----------------------------------------------------------------------------\n\nBase License and Subdirectory Specific Licenses\n\n1. Repository-Wide License\nExcept as provided in Section 2 below, the contents of this repository are licensed under the Business Source License (“BSL”), which includes a change date resulting in a licensing change to the GNU Affero General Public License v3.0 with Linking Exception on that date. See the full text of the BSL and AGPL with Linking Exception in this file below.\n\n2. Subdirectory-Specific Licenses\nCertain subdirectories within this repository are licensed under different terms.\n\nIf a subdirectory contains its own LICENSE or LICENSE.txt file, the terms in that file apply exclusively to all files and subfolders within that subdirectory.\n\nIn the event of any conflict between this base license and a subdirectory’s license, the base license will govern for that subdirectory’s contents.\n\n3. Contributor Acknowledgement\nBy contributing to this repository, you agree that:\n\nYour contributions will be licensed under the license applicable to the directory or subdirectory in which your contribution is made.\n\nIf you contribute to multiple subdirectories, the applicable license for each subdirectory will apply to your contributions in that subdirectory.\n\n4. Reading the Applicable License\nBefore using, modifying, or distributing code from this repository, you must read:\n\nThis base LICENSE.txt file for the overall repository license.\n\nAny LICENSE or LICENSE.txt file in a subdirectory that you intend to use or contribute to.\n\n-----------------------------------------------------------------------------\n\nBusiness Source License 1.1\n\nTerms\n\nThe Licensor hereby grants you the right to copy, modify, create derivative\nworks, redistribute, and make non-production use of the Licensed Work. The\nLicensor may make an Additional Use Grant, above, permitting limited\nproduction use.\n\nEffective on the Change Date, or the fourth anniversary of the first publicly\navailable distribution of a specific version of the Licensed Work under this\nLicense, whichever comes first, the Licensor hereby grants you rights under\nthe terms of the Change License, and the rights granted in the paragraph\nabove terminate.\n\nIf your use of the Licensed Work does not comply with the requirements\ncurrently in effect as described in this License, you must purchase a\ncommercial license from the Licensor, its affiliated entities, or authorized\nresellers, or you must refrain from using the Licensed Work.\n\nAll copies of the original and modified Licensed Work, and derivative works\nof the Licensed Work, are subject to this License. This License applies\nseparately for each version of the Licensed Work and the Change Date may vary\nfor each version of the Licensed Work released by Licensor.\n\nYou must conspicuously display this License on each original or modified copy\nof the Licensed Work. If you receive the Licensed Work in original or\nmodified form from a third party, the terms and conditions set forth in this\nLicense apply to your use of that work.\n\nAny use of the Licensed Work in violation of this License will automatically\nterminate your rights under this License for the current and all other\nversions of the Licensed Work.\n\nThis License does not grant you any right in any trademark or logo of\nLicensor or its affiliates (provided that you may use a trademark or logo of\nLicensor as expressly required by this License).\n\nTO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON\nAN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,\nEXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND\nTITLE.\n\nMariaDB hereby grants you permission to use this License’s text to license\nyour works, and to refer to it using the trademark “Business Source License”,\nas long as you comply with the Covenants of Licensor below.\n\nCovenants of Licensor\n\nIn consideration of the right to use this License’s text and the “Business\nSource License” name and trademark, Licensor covenants to MariaDB, and to all\nother recipients of the licensed work to be provided by Licensor:\n\n1. To specify as the Change License the GPL Version 2.0 or any later version,\n   or a license that is compatible with GPL Version 2.0 or a later version,\n   where “compatible” means that software provided under the Change License can\n   be included in a program with software provided under GPL Version 2.0 or a\n   later version. Licensor may specify additional Change Licenses without\n   limitation.\n\n2. To either: (a) specify an additional grant of rights to use that does not\n   impose any additional restriction on the right granted in this License, as\n   the Additional Use Grant; or (b) insert the text “None”.\n\n3. To specify a Change Date.\n\n4. Not to modify this License in any other way.\n\n-----------------------------------------------------------------------------\n\nCopyright (C) 2023 Clockwork Laboratories, Inc.\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License, version 3, as published \nby the Free Software Foundation.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT  \nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU General Public License for more  \ndetails.\n\nYou should have received a copy of the GNU Affero General Public License \nalong with this program; if not, see <https://www.gnu.org/licenses>.\n\nAdditional permission under GNU GPL version 3 section 7\n\nIf you modify this Program, or any covered work, by linking or combining it \nwith SpacetimeDB (or a modified version of that library), containing parts \ncovered by the terms of the AGPL v3.0, the licensors of this Program grant \nyou additional permission to convey the resulting work.\n\nAdditional permission under GNU AGPL version 3 section 13\n\nIf you modify this Program, or any covered work, by linking or combining it \nwith SpacetimeDB (or a modified version of that library), containing parts \ncovered by the terms of the AGPL v3.0, the licensors of this Program grant \nyou additional permission that, notwithstanding any other provision of this \nLicense, you need not prominently offer all users interacting with your \nmodified version remotely through a computer network an opportunity to \nreceive the Corresponding Source of your version from a network server at no \ncharge, if your version supports such interaction. This permission does not \nwaive or modify any other obligations or terms of the AGPL v3.0, except for \nthe specific requirement set forth in section 13.\n\nA copy of the AGPL v3.0 license is reproduced below.\n\n                      GNU AFFERO GENERAL PUBLIC LICENSE\n                         Version 3, 19 November 2007\n\nCopyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>\nEveryone is permitted to copy and distribute verbatim copies of this license \ndocument, but changing it is not allowed.\n\nPreamble\nThe GNU Affero General Public License is a free, copyleft license for \nsoftware and other kinds of works, specifically designed to ensure \ncooperation with the community in the case of network server software.\n\nThe licenses for most software and other practical works are designed to take\naway your freedom to share and change the works. By contrast, our General \nPublic Licenses are intended to guarantee your freedom to share and change \nall versions of a program--to make sure it remains free software for all its \nusers.\n\nWhen we speak of free software, we are referring to freedom, not price. Our \nGeneral Public Licenses are designed to make sure that you have the freedom \nto distribute copies of free software (and charge for them if you wish), that\nyou receive source code or can get it if you want it, that you can change the\nsoftware or use pieces of it in new free programs, and that you know you can \ndo these things.\n\nDevelopers that use our General Public Licenses protect your rights with two \nsteps: (1) assert copyright on the software, and (2) offer you this License \nwhich gives you legal permission to copy, distribute and/or modify the \nsoftware.\n\nA secondary benefit of defending all users' freedom is that improvements made\nin alternate versions of the program, if they receive widespread use, become \navailable for other developers to incorporate. Many developers of free \nsoftware are heartened and encouraged by the resulting cooperation. However, \nin the case of software used on network servers, this result may fail to come\nabout. The GNU General Public License permits making a modified version and \nletting the public access it on a server without ever releasing its source \ncode to the public.\n\nThe GNU Affero General Public License is designed specifically to ensure \nthat, in such cases, the modified source code becomes available to the \ncommunity. It requires the operator of a network server to provide the source\ncode of the modified version running there to the users of that server. \nTherefore, public use of a modified version, on a publicly accessible server,\ngives the public access to the source code of the modified version.\n\nAn older license, called the Affero General Public License and published by \nAffero, was designed to accomplish similar goals. This is a different \nlicense, not a version of the Affero GPL, but Affero has released a new \nversion of the Affero GPL which permits relicensing under this license.\n\nThe precise terms and conditions for copying, distribution and modification \nfollow.\n\nTERMS AND CONDITIONS\n0. Definitions.\n\"This License\" refers to version 3 of the GNU Affero General Public License.\n\n\"Copyright\" also means copyright-like laws that apply to other kinds of \nworks, such as semiconductor masks.\n\n\"The Program\" refers to any copyrightable work licensed under this License. \nEach licensee is addressed as \"you\". \"Licensees\" and \"recipients\" may be \nindividuals or organizations.\n\nTo \"modify\" a work means to copy from or adapt all or part of the work in a \nfashion requiring copyright permission, other than the making of an exact \ncopy. The resulting work is called a \"modified version\" of the earlier work \nor a work \"based on\" the earlier work.\n\nA \"covered work\" means either the unmodified Program or a work based on the \nProgram.\n\nTo \"propagate\" a work means to do anything with it that, without permission, \nwould make you directly or secondarily liable for infringement under \napplicable copyright law, except executing it on a computer or modifying a \nprivate copy. Propagation includes copying, distribution (with or without \nmodification), making available to the public, and in some countries other \nactivities as well.\n\nTo \"convey\" a work means any kind of propagation that enables other parties \nto make or receive copies. Mere interaction with a user through a computer \nnetwork, with no transfer of a copy, is not conveying.\n\nAn interactive user interface displays \"Appropriate Legal Notices\" to the \nextent that it includes a convenient and prominently visible feature that (1)\ndisplays an appropriate copyright notice, and (2) tells the user that there \nis no warranty for the work (except to the extent that warranties are \nprovided), that licensees may convey the work under this License, and how to \nview a copy of this License. If the interface presents a list of user \ncommands or options, such as a menu, a prominent item in the list meets this \ncriterion.\n\n1. Source Code.\nThe \"source code\" for a work means the preferred form of the work for making \nmodifications to it. \"Object code\" means any non-source form of a work.\n\nA \"Standard Interface\" means an interface that either is an official standard\ndefined by a recognized standards body, or, in the case of interfaces \nspecified for a particular programming language, one that is widely used \namong developers working in that language.\n\nThe \"System Libraries\" of an executable work include anything, other than the\nwork as a whole, that (a) is included in the normal form of packaging a Major\nComponent, but which is not part of that Major Component, and (b) serves only\nto enable use of the work with that Major Component, or to implement a \nStandard Interface for which an implementation is available to the public in \nsource code form. A \"Major Component\", in this context, means a major \nessential component (kernel, window system, and so on) of the specific \noperating system (if any) on which the executable work runs, or a compiler \nused to produce the work, or an object code interpreter used to run it.\n\nThe \"Corresponding Source\" for a work in object code form means all the \nsource code needed to generate, install, and (for an executable work) run the\nobject code and to modify the work, including scripts to control those \nactivities. However, it does not include the work's System Libraries, or \ngeneral-purpose tools or generally available free programs which are used \nunmodified in performing those activities but which are not part of the work.\nFor example, Corresponding Source includes interface definition files \nassociated with source files for the work, and the source code for shared \nlibraries and dynamically linked subprograms that the work is specifically \ndesigned to require, such as by intimate data communication or control flow \nbetween those subprograms and other parts of the work.\n\nThe Corresponding Source need not include anything that users can regenerate \nautomatically from other parts of the Corresponding Source.\n\nThe Corresponding Source for a work in source code form is that same work.\n\n2. Basic Permissions.\nAll rights granted under this License are granted for the term of copyright \non the Program, and are irrevocable provided the stated conditions are met. \nThis License explicitly affirms your unlimited permission to run the \nunmodified Program. The output from running a covered work is covered by this\nLicense only if the output, given its content, constitutes a covered work. \nThis License acknowledges your rights of fair use or other equivalent, as \nprovided by copyright law.\n\nYou may make, run and propagate covered works that you do not convey, without\nconditions so long as your license otherwise remains in force. You may convey\ncovered works to others for the sole purpose of having them make \nmodifications exclusively for you, or provide you with facilities for running\nthose works, provided that you comply with the terms of this License in \nconveying all material for which you do not control copyright. Those thus \nmaking or running the covered works for you must do so exclusively on your \nbehalf, under your direction and control, on terms that prohibit them from \nmaking any copies of your copyrighted material outside their relationship \nwith you.\n\nConveying under any other circumstances is permitted solely under the \nconditions stated below. Sublicensing is not allowed; section 10 makes it \nunnecessary.\n\n3. Protecting Users' Legal Rights From Anti-Circumvention Law.\nNo covered work shall be deemed part of an effective technological measure \nunder any applicable law fulfilling obligations under article 11 of the WIPO \ncopyright treaty adopted on 20 December 1996, or similar laws prohibiting or \nrestricting circumvention of such measures.\n\nWhen you convey a covered work, you waive any legal power to forbid \ncircumvention of technological measures to the extent such circumvention is \neffected by exercising rights under this License with respect to the covered \nwork, and you disclaim any intention to limit operation or modification of \nthe work as a means of enforcing, against the work's users, your or third \nparties' legal rights to forbid circumvention of technological measures.\n\n4. Conveying Verbatim Copies.\nYou may convey verbatim copies of the Program's source code as you receive \nit, in any medium, provided that you conspicuously and appropriately publish \non each copy an appropriate copyright notice; keep intact all notices stating\nthat this License and any non-permissive terms added in accord with section 7\napply to the code; keep intact all notices of the absence of any warranty; \nand give all recipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey, and you \nmay offer support or warranty protection for a fee.\n\n5. Conveying Modified Source Versions.\nYou may convey a work based on the Program, or the modifications to produce \nit from the Program, in the form of source code under the terms of section 4,\nprovided that you also meet all of these conditions:\n\na) The work must carry prominent notices stating that you modified it, and \ngiving a relevant date.\nb) The work must carry prominent notices stating that it is released under \nthis License and any conditions added under section 7. This requirement \nmodifies the requirement in section 4 to \"keep intact all notices\".\nc) You must license the entire work, as a whole, under this License to anyone\nwho comes into possession of a copy. This License will therefore apply, along\nwith any applicable section 7 additional terms, to the whole of the work, and\nall its parts, regardless of how they are packaged. This License gives no \npermission to license the work in any other way, but it does not invalidate \nsuch permission if you have separately received it.\nd) If the work has interactive user interfaces, each must display Appropriate\nLegal Notices; however, if the Program has interactive interfaces that do not\ndisplay Appropriate Legal Notices, your work need not make them do so.\nA compilation of a covered work with other separate and independent works, \nwhich are not by their nature extensions of the covered work, and which are \nnot combined with it such as to form a larger program, in or on a volume of a\nstorage or distribution medium, is called an \"aggregate\" if the compilation \nand its resulting copyright are not used to limit the access or legal rights \nof the compilation's users beyond what the individual works permit. Inclusion\nof a covered work in an aggregate does not cause this License to apply to the\nother parts of the aggregate.\n\n6. Conveying Non-Source Forms.\nYou may convey a covered work in object code form under the terms of sections\n4 and 5, provided that you also convey the machine-readable Corresponding \nSource under the terms of this License, in one of these ways:\n\na) Convey the object code in, or embodied in, a physical product (including a\nphysical distribution medium), accompanied by the Corresponding Source fixed \non a durable physical medium customarily used for software interchange.\nb) Convey the object code in, or embodied in, a physical product (including a\nphysical distribution medium), accompanied by a written offer, valid for at \nleast three years and valid for as long as you offer spare parts or customer \nsupport for that product model, to give anyone who possesses the object code \neither (1) a copy of the Corresponding Source for all the software in the \nproduct that is covered by this License, on a durable physical medium \ncustomarily used for software interchange, for a price no more than your \nreasonable cost of physically performing this conveying of source, or (2) \naccess to copy the Corresponding Source from a network server at no charge.\nc) Convey individual copies of the object code with a copy of the written \noffer to provide the Corresponding Source. This alternative is allowed only \noccasionally and noncommercially, and only if you received the object code \nwith such an offer, in accord with subsection 6b.\nd) Convey the object code by offering access from a designated place (gratis \nor for a charge), and offer equivalent access to the Corresponding Source in \nthe same way through the same place at no further charge. You need not \nrequire recipients to copy the Corresponding Source along with the object \ncode. If the place to copy the object code is a network server, the \nCorresponding Source may be on a different server (operated by you or a third\nparty) that supports equivalent copying facilities, provided you maintain \nclear directions next to the object code saying where to find the \nCorresponding Source. Regardless of what server hosts the Corresponding \nSource, you remain obligated to ensure that it is available for as long as \nneeded to satisfy these requirements.\ne) Convey the object code using peer-to-peer transmission, provided you \ninform other peers where the object code and Corresponding Source of the work\nare being offered to the general public at no charge under subsection 6d.\nA separable portion of the object code, whose source code is excluded from \nthe Corresponding Source as a System Library, need not be included in \nconveying the object code work.\n\nA \"User Product\" is either (1) a \"consumer product\", which means any tangible\npersonal property which is normally used for personal, family, or household \npurposes, or (2) anything designed or sold for incorporation into a dwelling.\nIn determining whether a product is a consumer product, doubtful cases shall \nbe resolved in favor of coverage. For a particular product received by a \nparticular user, \"normally used\" refers to a typical or common use of that \nclass of product, regardless of the status of the particular user or of the \nway in which the particular user actually uses, or expects or is expected to \nuse, the product. A product is a consumer product regardless of whether the \nproduct has substantial commercial, industrial or non-consumer uses, unless \nsuch uses represent the only significant mode of use of the product.\n\n\"Installation Information\" for a User Product means any methods, procedures, \nauthorization keys, or other information required to install and execute \nmodified versions of a covered work in that User Product from a modified \nversion of its Corresponding Source. The information must suffice to ensure \nthat the continued functioning of the modified object code is in no case \nprevented or interfered with solely because modification has been made.\n\nIf you convey an object code work under this section in, or with, or \nspecifically for use in, a User Product, and the conveying occurs as part of \na transaction in which the right of possession and use of the User Product is\ntransferred to the recipient in perpetuity or for a fixed term (regardless of\nhow the transaction is characterized), the Corresponding Source conveyed \nunder this section must be accompanied by the Installation Information. But \nthis requirement does not apply if neither you nor any third party retains \nthe ability to install modified object code on the User Product (for example,\nthe work has been installed in ROM).\n\nThe requirement to provide Installation Information does not include a \nrequirement to continue to provide support service, warranty, or updates for \na work that has been modified or installed by the recipient, or for the User \nProduct in which it has been modified or installed. Access to a network may \nbe denied when the modification itself materially and adversely affects the \noperation of the network or violates the rules and protocols for \ncommunication across the network.\n\nCorresponding Source conveyed, and Installation Information provided, in \naccord with this section must be in a format that is publicly documented (and\nwith an implementation available to the public in source code form), and must\nrequire no special password or key for unpacking, reading or copying.\n\n7. Additional Terms.\n\"Additional permissions\" are terms that supplement the terms of this License \nby making exceptions from one or more of its conditions. Additional \npermissions that are applicable to the entire Program shall be treated as \nthough they were included in this License, to the extent that they are valid \nunder applicable law. If additional permissions apply only to part of the \nProgram, that part may be used separately under those permissions, but the \nentire Program remains governed by this License without regard to the \nadditional permissions.\n\nWhen you convey a copy of a covered work, you may at your option remove any \nadditional permissions from that copy, or from any part of it. (Additional \npermissions may be written to require their own removal in certain cases when\nyou modify the work.) You may place additional permissions on material, added\nby you to a covered work, for which you have or can give appropriate \ncopyright permission.\n\nNotwithstanding any other provision of this License, for material you add to \na covered work, you may (if authorized by the copyright holders of that \nmaterial) supplement the terms of this License with terms:\n\na) Disclaiming warranty or limiting liability differently from the terms of \nsections 15 and 16 of this License; or\nb) Requiring preservation of specified reasonable legal notices or author \nattributions in that material or in the Appropriate Legal Notices displayed \nby works containing it; or\nc) Prohibiting misrepresentation of the origin of that material, or requiring\nthat modified versions of such material be marked in reasonable ways as \ndifferent from the original version; or\nd) Limiting the use for publicity purposes of names of licensors or authors \nof the material; or\ne) Declining to grant rights under trademark law for use of some trade names,\ntrademarks, or service marks; or\nf) Requiring indemnification of licensors and authors of that material by \nanyone who conveys the material (or modified versions of it) with contractual\nassumptions of liability to the recipient, for any liability that these \ncontractual assumptions directly impose on those licensors and authors.\nAll other non-permissive additional terms are considered \"further \nrestrictions\" within the meaning of section 10. If the Program as you \nreceived it, or any part of it, contains a notice stating that it is governed\nby this License along with a term that is a further restriction, you may \nremove that term. If a license document contains a further restriction but \npermits relicensing or conveying under this License, you may add to a covered\nwork material governed by the terms of that license document, provided that \nthe further restriction does not survive such relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you must \nplace, in the relevant source files, a statement of the additional terms that\napply to those files, or a notice indicating where to find the applicable \nterms.\n\nAdditional terms, permissive or non-permissive, may be stated in the form of \na separately written license, or stated as exceptions; the above requirements\napply either way.\n\n8. Termination.\nYou may not propagate or modify a covered work except as expressly provided \nunder this License. Any attempt otherwise to propagate or modify it is void, \nand will automatically terminate your rights under this License (including \nany patent licenses granted under the third paragraph of section 11).\n\nHowever, if you cease all violation of this License, then your license from a\nparticular copyright holder is reinstated (a) provisionally, unless and until\nthe copyright holder explicitly and finally terminates your license, and (b) \npermanently, if the copyright holder fails to notify you of the violation by \nsome reasonable means prior to 60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is reinstated \npermanently if the copyright holder notifies you of the violation by some \nreasonable means, this is the first time you have received notice of \nviolation of this License (for any work) from that copyright holder, and you \ncure the violation prior to 30 days after your receipt of the notice.\n\nTermination of your rights under this section does not terminate the licenses\nof parties who have received copies or rights from you under this License. If\nyour rights have been terminated and not permanently reinstated, you do not \nqualify to receive new licenses for the same material under section 10.\n\n9. Acceptance Not Required for Having Copies.\nYou are not required to accept this License in order to receive or run a copy\nof the Program. Ancillary propagation of a covered work occurring solely as a\nconsequence of using peer-to-peer transmission to receive a copy likewise \ndoes not require acceptance. However, nothing other than this License grants \nyou permission to propagate or modify any covered work. These actions \ninfringe copyright if you do not accept this License. Therefore, by modifying\nor propagating a covered work, you indicate your acceptance of this License \nto do so.\n\n10. Automatic Licensing of Downstream Recipients.\nEach time you convey a covered work, the recipient automatically receives a \nlicense from the original licensors, to run, modify and propagate that work, \nsubject to this License. You are not responsible for enforcing compliance by \nthird parties with this License.\n\nAn \"entity transaction\" is a transaction transferring control of an \norganization, or substantially all assets of one, or subdividing an \norganization, or merging organizations. If propagation of a covered work \nresults from an entity transaction, each party to that transaction who \nreceives a copy of the work also receives whatever licenses to the work the \nparty's predecessor in interest had or could give under the previous \nparagraph, plus a right to possession of the Corresponding Source of the work\nfrom the predecessor in interest, if the predecessor has it or can get it \nwith reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the rights \ngranted or affirmed under this License. For example, you may not impose a \nlicense fee, royalty, or other charge for exercise of rights granted under \nthis License, and you may not initiate litigation (including a cross-claim or\ncounterclaim in a lawsuit) alleging that any patent claim is infringed by \nmaking, using, selling, offering for sale, or importing the Program or any \nportion of it.\n\n11. Patents.\nA \"contributor\" is a copyright holder who authorizes use under this License \nof the Program or a work on which the Program is based. The work thus \nlicensed is called the contributor's \"contributor version\".\n\nA contributor's \"essential patent claims\" are all patent claims owned or \ncontrolled by the contributor, whether already acquired or hereafter \nacquired, that would be infringed by some manner, permitted by this License, \nof making, using, or selling its contributor version, but do not include \nclaims that would be infringed only as a consequence of further modification \nof the contributor version. For purposes of this definition, \"control\" \nincludes the right to grant patent sublicenses in a manner consistent with \nthe requirements of this License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free patent \nlicense under the contributor's essential patent claims, to make, use, sell, \noffer for sale, import and otherwise run, modify and propagate the contents \nof its contributor version.\n\nIn the following three paragraphs, a \"patent license\" is any express \nagreement or commitment, however denominated, not to enforce a patent (such \nas an express permission to practice a patent or covenant not to sue for \npatent infringement). To \"grant\" such a patent license to a party means to \nmake such an agreement or commitment not to enforce a patent against the \nparty.\n\nIf you convey a covered work, knowingly relying on a patent license, and the \nCorresponding Source of the work is not available for anyone to copy, free of\ncharge and under the terms of this License, through a publicly available \nnetwork server or other readily accessible means, then you must either (1) \ncause the Corresponding Source to be so available, or (2) arrange to deprive \nyourself of the benefit of the patent license for this particular work, or \n(3) arrange, in a manner consistent with the requirements of this License, to\nextend the patent license to downstream recipients. \"Knowingly relying\" means\nyou have actual knowledge that, but for the patent license, your conveying \nthe covered work in a country, or your recipient's use of the covered work in\na country, would infringe one or more identifiable patents in that country \nthat you have reason to believe are valid.\n\nIf, pursuant to or in connection with a single transaction or arrangement, \nyou convey, or propagate by procuring conveyance of, a covered work, and \ngrant a patent license to some of the parties receiving the covered work \nauthorizing them to use, propagate, modify or convey a specific copy of the \ncovered work, then the patent license you grant is automatically extended to \nall recipients of the covered work and works based on it.\n\nA patent license is \"discriminatory\" if it does not include within the scope \nof its coverage, prohibits the exercise of, or is conditioned on the \nnon-exercise of one or more of the rights that are specifically granted under\nthis License. You may not convey a covered work if you are a party to an \narrangement with a third party that is in the business of distributing \nsoftware, under which you make payment to the third party based on the extent\nof your activity of conveying the work, and under which the third party \ngrants, to any of the parties who would receive the covered work from you, a \ndiscriminatory patent license (a) in connection with copies of the covered \nwork conveyed by you (or copies made from those copies), or (b) primarily for\nand in connection with specific products or compilations that contain the \ncovered work, unless you entered into that arrangement, or that patent \nlicense was granted, prior to 28 March 2007.\n\nNothing in this License shall be construed as excluding or limiting any \nimplied license or other defenses to infringement that may otherwise be \navailable to you under applicable patent law.\n\n12. No Surrender of Others' Freedom.\nIf conditions are imposed on you (whether by court order, agreement or \notherwise) that contradict the conditions of this License, they do not excuse\nyou from the conditions of this License. If you cannot convey a covered work \nso as to satisfy simultaneously your obligations under this License and any \nother pertinent obligations, then as a consequence you may not convey it at \nall. For example, if you agree to terms that obligate you to collect a \nroyalty for further conveying from those to whom you convey the Program, the \nonly way you could satisfy both those terms and this License would be to \nrefrain entirely from conveying the Program.\n\n13. Remote Network Interaction; Use with the GNU General Public License.\nNotwithstanding any other provision of this License, if you modify the \nProgram, your modified version must prominently offer all users interacting \nwith it remotely through a computer network (if your version supports such \ninteraction) an opportunity to receive the Corresponding Source of your \nversion by providing access to the Corresponding Source from a network server\nat no charge, through some standard or customary means of facilitating \ncopying of software. This Corresponding Source shall include the \nCorresponding Source for any work covered by version 3 of the GNU General \nPublic License that is incorporated pursuant to the following paragraph.\n\nNotwithstanding any other provision of this License, you have permission to \nlink or combine any covered work with a work licensed under version 3 of the \nGNU General Public License into a single combined work, and to convey the \nresulting work. The terms of this License will continue to apply to the part \nwhich is the covered work, but the work with which it is combined will remain\ngoverned by version 3 of the GNU General Public License.\n\n14. Revised Versions of this License.\nThe Free Software Foundation may publish revised and/or new versions of the \nGNU Affero General Public License from time to time. Such new versions will \nbe similar in spirit to the present version, but may differ in detail to \naddress new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program \nspecifies that a certain numbered version of the GNU Affero General Public \nLicense \"or any later version\" applies to it, you have the option of \nfollowing the terms and conditions either of that numbered version or of any \nlater version published by the Free Software Foundation. If the Program does \nnot specify a version number of the GNU Affero General Public License, you \nmay choose any version ever published by the Free Software Foundation.\n\nIf the Program specifies that a proxy can decide which future versions of the\nGNU Affero General Public License can be used, that proxy's public statement \nof acceptance of a version permanently authorizes you to choose that version \nfor the Program.\n\nLater license versions may give you additional or different permissions. \nHowever, no additional obligations are imposed on any author or copyright \nholder as a result of your choosing to follow a later version.\n\n15. Disclaimer of Warranty.\nTHERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE \nLAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR \nOTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, \nEITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE \nENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. \nSHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY \nSERVICING, REPAIR OR CORRECTION.\n\n16. Limitation of Liability.\nIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL \nANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE \nPROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY \nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE \nOR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR\nDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR\nA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH \nHOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n17. Interpretation of Sections 15 and 16.\nIf the disclaimer of warranty and limitation of liability provided above \ncannot be given local legal effect according to their terms, reviewing courts\nshall apply local law that most closely approximates an absolute waiver of \nall civil liability in connection with the Program, unless a warranty or \nassumption of liability accompanies a copy of the Program in return for a \nfee.\n\nEND OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\nIf you develop a new program, and you want it to be of the greatest possible \nuse to the public, the best way to achieve this is to make it free software \nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a \npointer to where the full notice is found.\n\nSpacetimeDB: A database which replaces your server.\nCopyright (C) 2023 Clockwork Laboratories, Inc.\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as\npublished by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\nAlso add information on how to contact you by electronic and paper mail.\n\nIf your software can interact with users remotely through a computer network,\nyou should also make sure that it provides a way for users to get its source.\nFor example, if your program is a web application, its interface could \ndisplay a \"Source\" link that leads users to an archive of the code. There are\nmany ways you could offer source, and different solutions will be better for \ndifferent programs; see section 13 for the specific requirements.\n\nYou should also get your employer (if you work as a programmer) or school, if\nany, to sign a \"copyright disclaimer\" for the program, if necessary. For more\ninformation on this, and how to apply and follow the GNU AGPL, see \n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <a href=\"https://spacetimedb.com#gh-dark-mode-only\" target=\"_blank\">\n\t<img width=\"320\" src=\"./images/dark/logo.svg\" alt=\"SpacetimeDB Logo\">\n    </a>\n    <a href=\"https://spacetimedb.com#gh-light-mode-only\" target=\"_blank\">\n\t<img width=\"320\" src=\"./images/light/logo.svg\" alt=\"SpacetimeDB Logo\">\n    </a>\n</p>\n<p align=\"center\">\n    <a href=\"https://spacetimedb.com#gh-dark-mode-only\" target=\"_blank\">\n        <img width=\"250\" src=\"./images/dark/logo-text.svg\" alt=\"SpacetimeDB\">\n    </a>\n    <a href=\"https://spacetimedb.com#gh-light-mode-only\" target=\"_blank\">\n        <img width=\"250\" src=\"./images/light/logo-text.svg\" alt=\"SpacetimeDB\">\n    </a>\n    <h3 align=\"center\">\n        Development at the speed of light.\n    </h3>\n</p>\n<p align=\"center\">\n    <a href=\"https://github.com/clockworklabs/spacetimedb\"><img src=\"https://img.shields.io/github/v/release/clockworklabs/spacetimedb?color=%23ff00a0&include_prereleases&label=version&sort=semver&style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://github.com/clockworklabs/spacetimedb\"><img src=\"https://img.shields.io/badge/built_with-Rust-dca282.svg?style=flat-square\"></a>\n    &nbsp;\n\t<a href=\"https://github.com/clockworklabs/spacetimedb/actions\"><img src=\"https://img.shields.io/github/actions/workflow/status/clockworklabs/spacetimedb/ci.yml?style=flat-square&branch=master\"></a>\n    &nbsp;\n    <a href=\"https://status.spacetimedb.com\"><img src=\"https://img.shields.io/uptimerobot/ratio/7/m784409192-e472ca350bb615372ededed7?label=cloud%20uptime&style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://hub.docker.com/r/clockworklabs/spacetimedb\"><img src=\"https://img.shields.io/docker/pulls/clockworklabs/spacetimedb?style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://github.com/clockworklabs/spacetimedb/blob/master/LICENSE.txt\"><img src=\"https://img.shields.io/badge/license-BSL_1.1-00bfff.svg?style=flat-square\"></a>\n</p>\n<p align=\"center\">\n    <a href=\"https://crates.io/crates/spacetimedb\"><img src=\"https://img.shields.io/crates/d/spacetimedb?color=e45928&label=Rust%20Crate&style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://www.nuget.org/packages/SpacetimeDB.Runtime\"><img src=\"https://img.shields.io/nuget/dt/spacetimedb.runtime?color=0b6cff&label=NuGet%20Package&style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://www.npmjs.com/package/spacetimedb\"><img src=\"https://img.shields.io/npm/dm/spacetimedb?color=cb0000&label=npm&style=flat-square\"></a>\n</p>\n<p align=\"center\">\n    <a href=\"https://discord.gg/spacetimedb\"><img src=\"https://img.shields.io/discord/1037340874172014652?label=discord&style=flat-square&color=5a66f6\"></a>\n    &nbsp;\n    <a href=\"https://twitter.com/spacetime_db\"><img src=\"https://img.shields.io/badge/twitter-Follow_us-1d9bf0.svg?style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://clockworklabs.io/join\"><img src=\"https://img.shields.io/badge/careers-Join_us-86f7b7.svg?style=flat-square\"></a>\n    &nbsp;\n    <a href=\"https://www.linkedin.com/company/clockworklabs/\"><img src=\"https://img.shields.io/badge/linkedin-Connect_with_us-0a66c2.svg?style=flat-square\"></a>\n</p>\n\n<p align=\"center\">\n    <a href=\"https://discord.gg/spacetimedb\"><img height=\"25\" src=\"./images/social/discord.svg\" alt=\"Discord\"></a>\n    &nbsp;\n    <a href=\"https://twitter.com/spacetime_db\"><img height=\"25\" src=\"./images/social/twitter.svg\" alt=\"Twitter\"></a>\n    &nbsp;\n    <a href=\"https://github.com/clockworklabs/spacetimedb\"><img height=\"25\" src=\"./images/social/github.svg\" alt=\"GitHub\"></a>\n    &nbsp;\n    <a href=\"https://twitch.tv/SpacetimeDB\"><img height=\"25\" src=\"./images/social/twitch.svg\" alt=\"Twitch\"></a>\n    &nbsp;\n    <a href=\"https://youtube.com/@SpacetimeDB\"><img height=\"25\" src=\"./images/social/youtube.svg\" alt=\"YouTube\"></a>\n    &nbsp;\n    <a href=\"https://www.linkedin.com/company/clockwork-labs/\"><img height=\"25\" src=\"./images/social/linkedin.svg\" alt=\"LinkedIn\"></a>\n    &nbsp;\n    <a href=\"https://stackoverflow.com/questions/tagged/spacetimedb\"><img height=\"25\" src=\"./images/social/stackoverflow.svg\" alt=\"StackOverflow\"></a>\n</p>\n\n<br>\n\n## What is SpacetimeDB?\n\nSpacetimeDB is a relational database that is also a server. You upload your application logic directly into the database, and clients connect to it without any server in between.\n\nWrite your schema and business logic as a **module** in [Rust](https://spacetimedb.com/docs/quickstarts/rust), [C#](https://spacetimedb.com/docs/quickstarts/c-sharp), [TypeScript](https://spacetimedb.com/docs/quickstarts/typescript), or [C++](https://spacetimedb.com/docs/quickstarts/c-plus-plus). SpacetimeDB compiles it, runs it inside the database, and automatically synchronizes state to connected clients in real-time.\n\nInstead of deploying a web or game server that sits in between your clients and your database, your clients connect directly to the database and execute your application logic in your module. You can write all of your permission and authorization logic right inside your module just as you would in a normal server.\n\nThis means that you can write your entire application in a single language and deploy it as a single binary. No more separate webserver, no more containers, no more Kubernetes, no more VMs, no more DevOps, no more caching later. Zero infrastructure to manage.\n\n<figure>\n    <img src=\"./images/basic-architecture-diagram.png\" alt=\"SpacetimeDB Architecture\" style=\"width:100%\">\n    <figcaption align=\"center\">\n        <p align=\"center\"><b>SpacetimeDB application architecture</b><br /><sup><sub>(elements in white are provided by SpacetimeDB)</sub></sup></p>\n    </figcaption>\n</figure>\n\nSpacetimeDB is optimized for maximum speed and minimum latency. SpacetimeDB provides all the ACID guarantees of a traditional RDBMS, with all the speed of an optimized web server. All application state is held in memory for fast access, while a commit log on disk provides durability and crash recovery. The entire backend of our MMORPG [BitCraft Online](https://bitcraftonline.com) runs as a single SpacetimeDB module: chat, items, terrain, player positions, everything, synchronized to thousands of players in real-time.\n\n## Quick Start\n\n### 1. Install\n\n```bash\n# macOS / Linux\ncurl -sSf https://install.spacetimedb.com | sh\n\n# Windows (PowerShell)\niwr https://windows.spacetimedb.com -useb | iex\n```\n\n### 2. Log in\n\n```bash\nspacetime login\n```\n\nThis opens a browser to authenticate with GitHub. Your identity is linked to your account so you can publish databases.\n\n### 3. Start developing\n\n```bash\nspacetime dev --template chat-react-ts\n```\n\nThat is it. This creates a project from a template, publishes it to [Maincloud](https://spacetimedb.com/docs/how-to/deploy/maincloud), and watches for file changes, automatically rebuilding and republishing on save. See [pricing](https://spacetimedb.com/pricing) for details.\n\n## How It Works\n\nSpacetimeDB modules define **tables** (your data) and **reducers** (your logic). Clients connect, call reducers, and subscribe to tables. When data changes, SpacetimeDB pushes updates to subscribed clients automatically.\n\n```rust\n// Define a table\n#[spacetimedb::table(accessor = messages, public)]\npub struct Message {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    sender: Identity,\n    text: String,\n}\n\n// Define a reducer (your API endpoint)\n#[spacetimedb::reducer]\npub fn send_message(ctx: &ReducerContext, text: String) {\n    ctx.db.messages().insert(Message {\n        id: 0,\n        sender: ctx.sender,\n        text,\n    });\n}\n```\n\nOn the client side, subscribe and get live updates:\n\n```typescript\nconst [messages] = useTable(tables.message);\n// messages updates automatically when the server state changes.\n// No polling. No refetching.\n```\n\n## Language Support\n\n### Server Modules\n\nWrite your database logic in any of these languages:\n\n| Language | Quickstart |\n|----------|-----------|\n| **Rust** | [Get started](https://spacetimedb.com/docs/quickstarts/rust) |\n| **C#** | [Get started](https://spacetimedb.com/docs/quickstarts/c-sharp) |\n| **TypeScript** | [Get started](https://spacetimedb.com/docs/quickstarts/typescript) |\n| **C++** | [Get started](https://spacetimedb.com/docs/quickstarts/c-plus-plus) |\n\n### Client SDKs\n\nConnect from any of these platforms:\n\n| SDK | Quickstart |\n|-----|-----------|\n| **TypeScript** (React, Next.js, Vue, Svelte, Angular, Node.js, Bun, Deno) | [Get started](https://spacetimedb.com/docs/quickstarts/react) |\n| **Rust** | [Get started](https://spacetimedb.com/docs/quickstarts/rust) |\n| **C#** (standalone and Unity) | [Get started](https://spacetimedb.com/docs/quickstarts/c-sharp) |\n| **C++** (Unreal Engine) | [Get started](https://spacetimedb.com/docs/quickstarts/c-plus-plus) |\n\n## Running with Docker\n\n```bash\ndocker run --rm --pull always -p 3000:3000 clockworklabs/spacetime start\n```\n\n## Building from Source\n\nIf you need features from `master` that have not been released yet:\n\n```bash\n# Prerequisites: Rust toolchain with wasm32-unknown-unknown target\ncurl https://sh.rustup.rs -sSf | sh\n\ngit clone https://github.com/clockworklabs/SpacetimeDB\ncd SpacetimeDB\ncargo build --locked --release -p spacetimedb-standalone -p spacetimedb-update -p spacetimedb-cli\n```\n\nThen install the binaries:\n\n<details>\n<summary>macOS / Linux</summary>\n\n```bash\nmkdir -p ~/.local/bin\nSTDB_VERSION=\"$(./target/release/spacetimedb-cli --version | sed -n 's/.*spacetimedb tool version \\([0-9.]*\\);.*/\\1/p')\"\nmkdir -p ~/.local/share/spacetime/bin/$STDB_VERSION\n\ncp target/release/spacetimedb-update ~/.local/bin/spacetime\ncp target/release/spacetimedb-cli ~/.local/share/spacetime/bin/$STDB_VERSION\ncp target/release/spacetimedb-standalone ~/.local/share/spacetime/bin/$STDB_VERSION\n\n# Add to your shell config if not already present:\nexport PATH=\"$HOME/.local/bin:$PATH\"\n\n# Set the active version:\nspacetime version use $STDB_VERSION\n```\n</details>\n\n<details>\n<summary>Windows (PowerShell)</summary>\n\n```powershell\n$stdbDir = \"$HOME\\AppData\\Local\\SpacetimeDB\"\n$stdbVersion = & \".\\target\\release\\spacetimedb-cli\" --version |\n    Select-String -Pattern 'spacetimedb tool version ([0-9.]+);' |\n    ForEach-Object { $_.Matches.Groups[1].Value }\nNew-Item -ItemType Directory -Path \"$stdbDir\\bin\\$stdbVersion\" -Force | Out-Null\n\nCopy-Item \"target\\release\\spacetimedb-update.exe\" \"$stdbDir\\spacetime.exe\"\nCopy-Item \"target\\release\\spacetimedb-cli.exe\" \"$stdbDir\\bin\\$stdbVersion\\\"\nCopy-Item \"target\\release\\spacetimedb-standalone.exe\" \"$stdbDir\\bin\\$stdbVersion\\\"\n\n# Add to your system PATH: %USERPROFILE%\\AppData\\Local\\SpacetimeDB\n# Then in a new shell:\nspacetime version use $stdbVersion\n```\n</details>\n\nVerify with `spacetime --version`.\n\n## Documentation\n\nFull documentation is available at **[spacetimedb.com/docs](https://spacetimedb.com/docs)**, including:\n\n- [Quickstart guides](https://spacetimedb.com/docs) for every supported language and framework\n- [Core concepts](https://spacetimedb.com/docs/core-concepts): tables, reducers, subscriptions, authentication\n- [Tutorials](https://spacetimedb.com/docs/tutorials/chat-app): chat app, Unity multiplayer, Unreal Engine multiplayer\n- [Deployment guide](https://spacetimedb.com/docs/how-to/deploy/maincloud): publishing to Maincloud\n- [CLI reference](https://spacetimedb.com/docs/reference/cli-reference)\n- [SQL reference](https://spacetimedb.com/docs/reference/sql-reference)\n\n## License\n\nSpacetimeDB is licensed under the [Business Source License 1.1 (BSL)](LICENSE.txt). It converts to the AGPL v3.0 with a linking exception after a few years. The linking exception means you are **not** required to open-source your own code if you use SpacetimeDB. You only need to contribute back changes to SpacetimeDB itself.\n\n**Why did we choose this license?**\nWe chose to license SpacetimeDB under the MariaDB Business Source License for 4 years because we can't compete with AWS while also building our products for them.\n\nWe chose GPLv3 with linking exception as the open source license because we want contributions merged back into mainline (just like Linux), but we don't want to make anyone else open source their own code (i.e. linking exception). \n"
  },
  {
    "path": "clippy.toml",
    "content": "disallowed-macros = [\n    { path = \"std::print\",    reason = \"print blocks on a global mutex for synchronization, and its output cannot be filtered. Use a log macro instead, or apply #[allow(disallowed-macros)] if this is test or CLI code.\" },\n    { path = \"std::println\",  reason = \"println blocks on a global mutex for synchronization, and its output cannot be filtered. Use a log macro instead, or apply #[allow(disallowed-macros)] if this is test or CLI code.\" },\n    { path = \"std::eprint\",   reason = \"eprint blocks on a global mutex for synchronization, and its output cannot be filtered. Use a log macro instead, or apply #[allow(disallowed-macros)] if this is test or CLI code.\" },\n    { path = \"std::eprintln\", reason = \"eprintln blocks on a global mutex for synchronization, and its output cannot be filtered. Use a log macro instead, or apply #[allow(disallowed-macros)] if this is test or CLI code.\" },\n    { path = \"std::dbg\",      reason = \"dbg is a debugging tool and should never be committed into the repository.\" },\n]\n"
  },
  {
    "path": "crates/auth/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-auth\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Auth helpers for SpacetimeDB\"\n\n[dependencies]\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib = { workspace = true, features = [\"serde\"] }\n\nanyhow.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nserde_with.workspace = true\njsonwebtoken.workspace = true\n\n[dev-dependencies]\nserde_json.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/auth/src/identity.rs",
    "content": "pub use jsonwebtoken::errors::Error as JwtError;\npub use jsonwebtoken::errors::ErrorKind as JwtErrorKind;\npub use jsonwebtoken::{DecodingKey, EncodingKey};\nuse serde::Deserializer;\nuse serde::{Deserialize, Serialize};\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_lib::Identity;\nuse std::time::SystemTime;\n\n#[derive(Debug, Clone)]\npub struct ConnectionAuthCtx {\n    pub claims: SpacetimeIdentityClaims,\n    pub jwt_payload: Box<str>,\n}\n\nimpl TryFrom<SpacetimeIdentityClaims> for ConnectionAuthCtx {\n    type Error = anyhow::Error;\n    fn try_from(claims: SpacetimeIdentityClaims) -> Result<Self, Self::Error> {\n        let payload = serde_json::to_string(&claims).map_err(|e| anyhow::anyhow!(\"Failed to serialize claims: {e}\"))?;\n        Ok(ConnectionAuthCtx {\n            claims,\n            jwt_payload: payload.into(),\n        })\n    }\n}\n\n// These are the claims that can be attached to a request/connection.\n#[serde_with::serde_as]\n#[derive(Debug, Serialize, Deserialize, Clone)]\npub struct SpacetimeIdentityClaims {\n    #[serde(rename = \"hex_identity\")]\n    pub identity: Identity,\n    #[serde(rename = \"sub\")]\n    pub subject: Box<str>,\n    #[serde(rename = \"iss\")]\n    pub issuer: Box<str>,\n    #[serde(rename = \"aud\")]\n    pub audience: Box<[Box<str>]>,\n\n    /// The unix timestamp the token was issued at\n    #[serde_as(as = \"serde_with::TimestampSeconds\")]\n    pub iat: SystemTime,\n    #[serde_as(as = \"Option<serde_with::TimestampSeconds>\")]\n    pub exp: Option<SystemTime>,\n\n    #[serde(flatten)]\n    pub extra: Option<HashMap<Box<str>, serde_json::Value>>,\n}\n\nfn deserialize_audience<'de, D>(deserializer: D) -> Result<Box<[Box<str>]>, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    // By using `untagged`, it will try the different options.\n    #[derive(Deserialize)]\n    #[serde(untagged)]\n    enum Audience {\n        Single(Box<str>),\n        Multiple(Box<[Box<str>]>),\n    }\n\n    // Deserialize into the enum\n    let audience = Audience::deserialize(deserializer)?;\n\n    // Convert the enum into a list.\n    Ok(match audience {\n        Audience::Single(s) => [s].into(),\n        Audience::Multiple(v) => v,\n    })\n}\n\n// IncomingClaims are from the token we receive from the client.\n// The signature should be verified already, but further validation is needed to have a SpacetimeIdentityClaims2.\n#[serde_with::serde_as]\n#[derive(Debug, Serialize, Deserialize)]\npub struct IncomingClaims {\n    #[serde(rename = \"hex_identity\")]\n    pub identity: Option<Identity>,\n    #[serde(rename = \"sub\")]\n    pub subject: Box<str>,\n    #[serde(rename = \"iss\")]\n    pub issuer: Box<str>,\n    #[serde(rename = \"aud\", default, deserialize_with = \"deserialize_audience\")]\n    pub audience: Box<[Box<str>]>,\n\n    /// The unix timestamp the token was issued at\n    #[serde_as(as = \"serde_with::TimestampSeconds\")]\n    pub iat: SystemTime,\n    #[serde_as(as = \"Option<serde_with::TimestampSeconds>\")]\n    pub exp: Option<SystemTime>,\n\n    /// All remaining claims from the JWT payload\n    #[serde(flatten)]\n    pub extra: Option<HashMap<Box<str>, serde_json::Value>>,\n}\n\nimpl TryInto<SpacetimeIdentityClaims> for IncomingClaims {\n    type Error = anyhow::Error;\n\n    fn try_into(self) -> anyhow::Result<SpacetimeIdentityClaims> {\n        // The issuer and subject must be less than 128 bytes.\n        if self.issuer.len() > 128 {\n            return Err(anyhow::anyhow!(\"Issuer too long: {:?}\", self.issuer));\n        }\n        if self.subject.len() > 128 {\n            return Err(anyhow::anyhow!(\"Subject too long: {:?}\", self.subject));\n        }\n        // The issuer and subject must be non-empty.\n        if self.issuer.is_empty() {\n            return Err(anyhow::anyhow!(\"Issuer empty\"));\n        }\n        if self.subject.is_empty() {\n            return Err(anyhow::anyhow!(\"Subject empty\"));\n        }\n\n        let computed_identity = Identity::from_claims(&self.issuer, &self.subject);\n        // If an identity is provided, it must match the computed identity.\n        if let Some(token_identity) = self.identity\n            && token_identity != computed_identity\n        {\n            return Err(anyhow::anyhow!(\n                    \"Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}\",\n                ));\n        }\n\n        Ok(SpacetimeIdentityClaims {\n            identity: computed_identity,\n            subject: self.subject,\n            issuer: self.issuer,\n            audience: self.audience,\n            iat: self.iat,\n            exp: self.exp,\n            extra: self.extra,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use serde_json::json;\n    use std::time::UNIX_EPOCH;\n\n    #[test]\n    fn test_deserialize_audience_single_string() {\n        let json_data = json!({\n            \"sub\": \"123\",\n            \"iss\": \"example.com\",\n            \"aud\": \"audience1\",\n            \"iat\": 1693425600,\n            \"exp\": 1693512000\n        });\n\n        let claims: IncomingClaims = serde_json::from_value(json_data).unwrap();\n\n        assert_eq!(claims.audience, [\"audience1\".into()].into());\n        assert_eq!(&*claims.subject, \"123\");\n        assert_eq!(&*claims.issuer, \"example.com\");\n        assert_eq!(claims.iat, UNIX_EPOCH + std::time::Duration::from_secs(1693425600));\n        assert_eq!(\n            claims.exp,\n            Some(UNIX_EPOCH + std::time::Duration::from_secs(1693512000))\n        );\n    }\n\n    #[test]\n    fn test_deserialize_audience_multiple_strings() {\n        let json_data = json!({\n            \"sub\": \"123\",\n            \"iss\": \"example.com\",\n            \"aud\": [\"audience1\", \"audience2\"],\n            \"iat\": 1693425600,\n            \"exp\": 1693512000\n        });\n\n        let claims: IncomingClaims = serde_json::from_value(json_data).unwrap();\n\n        assert_eq!(claims.audience, [\"audience1\".into(), \"audience2\".into()].into());\n        assert_eq!(&*claims.subject, \"123\");\n        assert_eq!(&*claims.issuer, \"example.com\");\n        assert_eq!(claims.iat, UNIX_EPOCH + std::time::Duration::from_secs(1693425600));\n        assert_eq!(\n            claims.exp,\n            Some(UNIX_EPOCH + std::time::Duration::from_secs(1693512000))\n        );\n    }\n\n    #[test]\n    fn test_deserialize_audience_missing_field() {\n        let json_data = json!({\n            \"sub\": \"123\",\n            \"iss\": \"example.com\",\n            \"iat\": 1693425600,\n            \"exp\": 1693512000\n        });\n\n        let claims: IncomingClaims = serde_json::from_value(json_data).unwrap();\n\n        assert!(claims.audience.is_empty()); // Since `default` is used, it should be an empty vector\n        assert_eq!(&*claims.subject, \"123\");\n        assert_eq!(&*claims.issuer, \"example.com\");\n        assert_eq!(claims.iat, UNIX_EPOCH + std::time::Duration::from_secs(1693425600));\n        assert_eq!(\n            claims.exp,\n            Some(UNIX_EPOCH + std::time::Duration::from_secs(1693512000))\n        );\n    }\n}\n"
  },
  {
    "path": "crates/auth/src/lib.rs",
    "content": "pub mod identity;\n"
  },
  {
    "path": "crates/bench/.gitignore",
    "content": ".spacetime/\ntarget/\n"
  },
  {
    "path": "crates/bench/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-bench\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Bench library/utility for SpacetimeDB\"\npublish = false\n\n[[bench]]\nname = \"special\"\nharness = false\n\n[[bench]]\nname = \"generic\"\nharness = false\n\n[[bench]]\nname = \"callgrind\"\nharness = false\n\n[[bench]]\nname = \"subscription\"\nharness = false\n\n[[bench]]\nname = \"delete_table\"\nharness = false\n\n[[bench]]\nname = \"index\"\nharness = false\n\n[[bin]]\nname = \"summarize\"\n\n[lib]\nbench = false\n\n[dependencies]\nspacetimedb-client-api = { path = \"../client-api\" }\nspacetimedb-client-api-messages = { path = \"../client-api-messages\" }\nspacetimedb-core = { path = \"../core\", features = [\"test\"] }\nspacetimedb-data-structures.workspace = true\nspacetimedb-datastore.workspace = true\nspacetimedb-execution = { path = \"../execution\" }\nspacetimedb-lib = { path = \"../lib\" }\nspacetimedb-paths.workspace = true\nspacetimedb-primitives = { path = \"../primitives\" }\nspacetimedb-query = { path = \"../query\" }\nspacetimedb-sats = { path = \"../sats\" }\nspacetimedb-schema = { workspace = true, features = [\"test\"] }\nspacetimedb-standalone = { path = \"../standalone\" }\nspacetimedb-table = { path = \"../table\" }\nspacetimedb-testing = { path = \"../testing\" }\n\nahash.workspace = true\nanyhow.workspace = true\nconvert_case.workspace = true\nanymap.workspace = true\nbyte-unit.workspace = true\nclap.workspace = true\ncriterion.workspace = true\nfutures.workspace = true\nfoldhash.workspace = true\nhashbrown.workspace = true\nlazy_static.workspace = true\nlog.workspace = true\nmimalloc.workspace = true\nrand.workspace = true\nregex.workspace = true\nrusqlite.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nserial_test.workspace = true\nsmallvec.workspace = true\ntempdir.workspace = true\ntokio.workspace = true\ntracing-subscriber.workspace = true\nwalkdir.workspace = true\nitertools.workspace = true\n\n[target.'cfg(not(target_env = \"msvc\"))'.dependencies]\ntikv-jemallocator = { workspace = true }\ntikv-jemalloc-ctl = { workspace = true }\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\n# only try to build these on linux\n\n# also:\n# we've forked iai-callgrind to add custom entrypoint support.\n# FIXME(jgilles): revert to depending on the crates version if we ever get that upstreamed.\n\n# iai-callgrind = \"0.7.2\"\niai-callgrind = { git = \"https://github.com/clockworklabs/iai-callgrind.git\", branch = \"main\" }\niai-callgrind-runner = { git = \"https://github.com/clockworklabs/iai-callgrind.git\", branch = \"main\" }\niai-callgrind-macros = { git = \"https://github.com/clockworklabs/iai-callgrind.git\", branch = \"main\" }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/bench/Dockerfile",
    "content": "# Dockerfile for callgrind benchmarking environment.\n# Set up to run from linux / WSL (running from a windows file system will be extremely slow).\n# See the README for commands to run.\n\n# sync with: ../../rust-toolchain.toml\nFROM rust:1.93.0\n\nRUN apt-get update && \\\n    apt-get install -y valgrind bash && \\\n    rm -rf /var/lib/apt/lists/*\n\nENV CARGO_TARGET_DIR=/projects/SpacetimeDB/linux-target\nENV CARGO_HOME=/projects/SpacetimeDB/linux-cache\nENV RUSTUP_HOME=/projects/SpacetimeDB/linux-rustup\n\nRUN cargo install --git https://github.com/clockworklabs/iai-callgrind.git --branch main iai-callgrind-runner\n"
  },
  {
    "path": "crates/bench/README.md",
    "content": "# spacetimedb-bench\n\n> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n\nBenchmarking suite for SpacetimeDB using [Criterion](https://github.com/bheisler/criterion.rs) and [Callgrind](https://valgrind.org/docs/manual/cl-manual.html) (via [iai-callgrind](https://github.com/clockworklabs/iai-callgrind)). Provides comparisons between the underlying spacetime datastore, spacetime modules, and sqlite.\n\nTo run the criterion benchmarks:\n\n```bash\ncargo bench --bench generic --bench special\n```\n\nTo enable the criterion benchmarks that do one million inserts or updates, set the RUN_ONE_MILLION environment variable:\n\n```bash\nRUN_ONE_MILLION=true cargo bench --bench generic --bench special\n```\n\nTo run the callgrind benchmarks, you need valgrind installed.\nThe easiest way to get it is to use the docker image in this folder. \nThere's a handy bash script:\n```bash\nbash callgrind-docker.sh \n```\nWhich will build the docker image and run the callgrind benchmarks inside of it.\n\nYou can also comment \"benchmarks please\" or \"callgrind please\" on a pull request in the SpacetimeDB repository to run the criterion/callgrind benchmarks on that PR. The results will be posted in a comment on the PR.\n\nThis is coordinated using the benchmarks GitHub Actions: see [`../../.github/workflows/benchmarks.yml`](../../.github/workflows/benchmarks.yml), and\n[`../../.github/workflows/callgrind_benchmarks.yml`](../../.github/workflows/callgrind_benchmarks.yml). \nThese also rely on the benchmarks-viewer application (https://github.com/clockworklabs/benchmarks-viewer).\n\n\n## Caveats\n\nThe criterion benchmarks take a long time to run -- there are a lot of them. See below for information on running select groups of them.\n\nThe callgrind benchmarks only measure a select portion of the codebase. In particular, they do not collect instruction counts in any async code, due to callgrind limitations; they only time the synchronous code that runs a reducer / the client code that calls a reducer. This is most of the code that it takes to run a reducer, including all database modifications. The async code between client and reducer is mostly just handing off data through a couple of channels. Still, if you're interested in measuring that code, you should rely on the criterion benchmarks & `perf`.\n\n\n\n## Criterion benchmarks\nTimings for spacetime modules should be understood as *latencies to call a spacetime reducer without network delays*. They include serialization and deserialization times for any arguments passed. There are also separate benchmarks that measure these times on their own.\n\nThe complete structure (not yet fully implemented) is as follows:\n\n```\n# generic benchmarks:\n\n[db]/[disk]/\n    empty_transaction/\n    insert_1/[schema]/[index_type]/[load]\n    insert_bulk/[schema]/[index_type]/[load]/[count]\n    insert_large_value/[count]\n    iterate/[schema]/[count]/\n    filter/[string, u64]/[index?]/[load]/[count]\n    find_unique/u32/[load]/\n    delete/[index_type]/[load]/[count]\n\n# \"load\" refers to the number of rows in the table at the start of the benchmark.\n\n# db: [stdb_raw, stdb_module, sqlite]\n# disk: [disk, mem]\n# schema: [person, location]\n# index_type: [unique, non_unique, multi_index]\n\n# \"person\" is a schema with 2 ints and a string, \"location\" is a schema with 3 ints.\n\nserialize/\n    bsatn/[schema]/[count]\n    json/[schema]/[count]\n    product_value/[schema]/[count]\n\ndeserialize/\n    bsatn/[schema]/[count]\n    json/[schema]/[count]\n\nstdb_module/\n    print_bulk/[count]\n    large_arguments/64KiB/\n\ndb_game/\n    circles/load=[num rows]\n    ia_loop/load=[num rows]\n```\n\nTypically, you don't want to run all benchmarks at once, there are a lot of them and it will take many minutes.\nYou can pass regexes to the bench script to select what slice of benchmarks you'd like. For example,\n\n```sh\ncargo bench -- 'stdb_raw/.*/insert_bulk'\n```\nWill run all of the `insert_bulk` benchmarks against the raw spacetime backend.\n\nSimilarly, \n```sh\ncargo bench -- 'mem/.*/unique'\n```\nWill run benchmarks involving unique primary keys against all databases, without writing to disc.\n\n## Workload Benches\n\nThe workload benches within the `db_game` module are designed to simulate realistic workloads commonly found in games. \n\nInstead of testing for a small set of values, these benches generate a larger number of rows to more effectively stress the database engine. As consequence, they take more time when run inside `criterion`. \n\nThis approach reduces interference caused by noise and provides the potential for better detection of display improvements or regressions in performance.\n\nTo run the workload benches directly outside `criterion`, you can use the following commands:\n\n```bash\ncargo test --release --package spacetimedb-testing --test standalone_integration_test test_calling_bench_db_ia_loop -- --exact --nocapture\ncargo test --release --package spacetimedb-testing --test standalone_integration_test test_calling_bench_db_circles -- --exact --nocapture\n```\n\n## Pretty report\nTo generate a nicely formatted markdown report, you can use the \"summarize\" binary.\nThis is used on CI (see [`../../.github/workflows/benchmarks.yml`](../../.github/workflows/benchmarks.yml)).\n\nTo generate a report without comparisons, use:\n```bash\ncargo bench --bench generic --bench special -- --save-baseline current\ncargo run --bin summarize markdown-report current\n```\n\nTo compare to another branch, do:\n```bash\ngit checkout master\ncargo bench --bench generic --bench special -- --save-baseline base\ngit checkout high-octane-feature-branch\ncargo bench --bench generic --bench special -- --save-baseline current\ncargo run --bin summarize markdown-report current base\n```\n\nOf course, this will take about an hour, so it might be better to let the CI do it for you.\n\n## Adding more\n\nThere are two ways to write benchmarks:\n\n- Targeted, non-generic benchmarks (`benches/special.rs`)\n- Generic over database backends (`benches/generic.rs`)\n\nSee the following sections for how to write these.\n\n#### Targeted benchmarks\nThese are regular [Criterion.rs](https://github.com/bheisler/criterion.rs) benchmarks. Nothing fancy, do whatever you like with these. Put them in `benches/special.rs`.\n\n#### Generic benchmarks\nGeneric benchmarks live in `benches/generic.rs`. These benchmarks involve two traits:\n\n- [`BenchDatabase`](src/database.rs), which is implemented by different database backends.\n- [`BenchTable`](src/schemas.rs), which is implemented by different benchmark tables.\n\nTo add a new generic benchmark, you'll need to:\n- Add a relevant method to the `BenchDatabase` trait.\n- Implement it for each `BenchDatabase` implementation.\n    - [`SQLite`](src/sqlite.rs) will require you to write it as a SQL query and submit that to sqlite.\n    - [`SpacetimeRaw`](src/spacetime_raw.rs) will require you to execute the query against the Spacetime database backend directly.\n    - [`SpacetimeModule`](src/spacetime_module.rs) will require you to add a reducer to the [`benchmarks`](../../modules/benchmarks/src/lib.rs) crate, and then add logic to invoke your reducer with the correct arguments.\n- Add a benchmark harness that actually invokes your method in `benches/generic.rs`.\n\n\n## Callgrind benchmarks\nThese live in `benches/callgrind.rs` and can only be run on linux, although a docker image is provided above.\nSee that f\n\n\n## Install tools\n\nThere are also some scripts that rely on external tools to extract data from the benchmarks.\n\n### OSX + Linux\n\n- [samply](https://github.com/mstange/samply/)\n\n```bash\ncargo install samply\n```\nRun *any* command to see perf data on Firefox:\n\n```bash\n# Note if the `cargo` command triggers compilation, it will also be captured in the profile.\n# Therefore it is useful to run this after the artifact has already been cached.\nsamply record -r 10000000 cargo bench --bench=subscription --profile=profiling -- full-scan --exact --profile-time=30\n```\n\n### OSX Only\n\n- [cargo-instrument](https://github.com/cmyr/cargo-instruments)\n\n```bash\nbrew install cargo-instruments\n```\n\n```bash\n# ./instruments.sh TEMPLATE BENCHMARK BENCHMARK_FILTER\n./instruments.sh time generic stdb_raw/mem/insert_bulk/location/multi_index/load=0/count=100\n```\n\nWhere `TEMPLATE` is one from \n\n```bash\ncargo instruments --list-templates\n```\n\n\n### Linux only\n\n- [cargo-flamegraph](https://github.com/flamegraph-rs/flamegraph)\n\n```bash\ncargo install flamegraph\n```\n\nYou can generate flamegraphs using `flamegraph.sh` script:\n\n```bash\n# ./flamegraph.sh BENCH_EXECUTABLE FILTER SVG_PATH\n./flamegraph.sh generic stdb_raw/mem/insert_bulk/location/multi_index/load=0/count=100 result.svg\"\n```"
  },
  {
    "path": "crates/bench/benches/callgrind.rs",
    "content": "#[cfg(target_os = \"linux\")]\nmod callgrind_benches {\n\n    /// Benchmarks that run under our iai-callgrind fork (https://github.com/clockworklabs/iai-callgrind)\n    /// Callgrind (https://valgrind.org/docs/manual/cl-manual.html) disassembles linux binaries\n    /// as they run to insert instrumentation code. This allows for benchmarking with minimal variance,\n    /// compared to timing benchmarks.\n    ///\n    /// Note: you CAN'T save state between these benchmarks, since each is run\n    /// in a fresh process.\n    ///\n    /// FIXME(jgilles): many of the spacetime_module benchmarks are currently disabled due to a bad interaction with sled (issue #564).\n    ///\n    /// There is some odd boilerplate in this file that is used to make downstream processing of these benchmarks easier.\n    /// Every benchmark is a struct that implements serde::Deserialize; these structs contain some redundant information.\n    /// Benchmarks are actually dispatched using iai-callgrind's `#[library_benchmark]` macro.\n    /// We pass benchmark configurations as JSON strings to these benchmarks.\n    /// This JSON ends up in iai-callgrind's output file; we parse all relevant information out of it.\n    /// (In the `crate/src/bin/summarize.rs` binary.)\n    use iai_callgrind::{library_benchmark, library_benchmark_group, LibraryBenchmarkConfig};\n    use serde::Deserialize;\n\n    use spacetimedb_bench::{\n        database::BenchDatabase,\n        schemas::{create_partly_identical, create_sequential, u32_u64_str, BenchTable, IndexStrategy, RandomTable},\n        spacetime_raw::SpacetimeRaw,\n        sqlite::SQLite,\n    };\n    use spacetimedb_lib::{AlgebraicType, ProductValue};\n\n    /// A benchmark.\n    ///\n    /// The benchmark information consists of metadata that can be deserialized from JSON.\n    /// The JSON is embedded in the output generated by iai-callgrind, which\n    /// makes writing downstream tools a lot easier.\n    ///\n    // TODO(jgilles): use this infra in the non-callgrind benches as well.\n    trait Benchmark: Deserialize<'static> {\n        fn run_benchmark(self);\n    }\n\n    // ========================= INSERT BULK =========================\n\n    #[derive(Deserialize)]\n    struct InsertBulkBenchmark<DB: BenchDatabase, T: BenchTable + RandomTable> {\n        bench: String,\n        db: String,\n        in_memory: bool,\n        schema: String,\n        indices: IndexStrategy,\n        preload: u32,\n        count: u32,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<(DB, T)>,\n    }\n\n    impl<DB: BenchDatabase, T: BenchTable + RandomTable> Benchmark for InsertBulkBenchmark<DB, T> {\n        fn run_benchmark(self) {\n            assert_eq!(self.bench, \"insert bulk\", \"provided metadata has incorrect bench name\");\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(self.schema, T::name(), \"provided metadata has incorrect db name\");\n\n            let mut db = DB::build(self.in_memory).unwrap();\n\n            let table_id = db.create_table::<T>(self.indices).unwrap();\n            let mut data = create_sequential::<T>(0xdeadbeef, self.count + self.preload, 64);\n            let to_preload = data.split_off(self.count as usize);\n\n            // warm up\n            db.insert_bulk(&table_id, data.clone()).unwrap();\n            db.clear_table(&table_id).unwrap();\n\n            // add preload data\n            db.insert_bulk(&table_id, to_preload).unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                db.insert_bulk(&table_id, data).unwrap();\n            });\n\n            // clean up\n            db.clear_table(&table_id).unwrap();\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::mem_unique_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::mem_btree_each_column_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_unique_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_btree_each_column_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\", \"preload\": 128, \"count\": 64}\"#)]\n    fn insert_bulk_raw_u32_u64_str(metadata: &str) {\n        let bench: InsertBulkBenchmark<SpacetimeRaw, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    #[library_benchmark]\n    #[bench::mem_unique_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::mem_btree_each_column_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_unique_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_btree_each_column_64(r#\"{\"bench\":\"insert bulk\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\", \"preload\": 128, \"count\": 64}\"#)]\n    fn insert_bulk_sqlite_u32_u64_str(metadata: &str) {\n        let bench: InsertBulkBenchmark<SQLite, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = insert_bulk_group;\n        benchmarks = insert_bulk_raw_u32_u64_str, /*insert_bulk_module_u32_u64_str,*/ insert_bulk_sqlite_u32_u64_str\n    );\n\n    // ========================= UPDATE BULK =========================\n\n    #[derive(Deserialize)]\n    struct UpdateBulkBenchmark<DB: BenchDatabase, T: BenchTable + RandomTable> {\n        bench: String,\n        db: String,\n        in_memory: bool,\n        schema: String,\n        indices: IndexStrategy,\n        preload: u32,\n        count: u32,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<(DB, T)>,\n    }\n\n    impl<DB: BenchDatabase, T: BenchTable + RandomTable> Benchmark for UpdateBulkBenchmark<DB, T> {\n        fn run_benchmark(self) {\n            assert_eq!(self.bench, \"update bulk\", \"provided metadata has incorrect bench name\");\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(self.schema, T::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(\n                self.indices,\n                IndexStrategy::Unique0,\n                \"provided metadata has incorrect index strategy\"\n            );\n\n            let mut db = DB::build(self.in_memory).unwrap();\n\n            let table_id = db.create_table::<T>(self.indices).unwrap();\n            let data = create_sequential::<T>(0xdeadbeef, self.preload, 64);\n\n            // warm up\n            db.insert_bulk(&table_id, data).unwrap();\n            db.update_bulk::<T>(&table_id, self.count).unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                db.update_bulk::<T>(&table_id, self.count).unwrap();\n            });\n\n            // clean up\n            db.clear_table(&table_id).unwrap();\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::mem_64(r#\"{\"bench\":\"update bulk\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::mem_1024(r#\"{\"bench\":\"update bulk\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 1024, \"count\": 1024}\"#)]\n    #[bench::disk_64(r#\"{\"bench\":\"update bulk\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_1024(r#\"{\"bench\":\"update bulk\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 1024, \"count\": 1024}\"#)]\n    fn update_bulk_raw_u32_u64_str(metadata: &str) {\n        let bench: UpdateBulkBenchmark<SpacetimeRaw, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    #[library_benchmark]\n    #[bench::mem_64(r#\"{\"bench\":\"update bulk\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::mem_1024(r#\"{\"bench\":\"update bulk\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 1024, \"count\": 1024}\"#)]\n    #[bench::disk_64(r#\"{\"bench\":\"update bulk\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 128, \"count\": 64}\"#)]\n    #[bench::disk_1024(r#\"{\"bench\":\"update bulk\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"preload\": 1024, \"count\": 1024}\"#)]\n    fn update_bulk_sqlite_u32_u64_str(metadata: &str) {\n        let bench: UpdateBulkBenchmark<SQLite, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = update_bulk_group;\n        benchmarks = update_bulk_raw_u32_u64_str, /*update_bulk_module_u32_u64_str,*/ update_bulk_sqlite_u32_u64_str\n    );\n\n    // ========================= ITERATE =========================\n\n    #[derive(Deserialize)]\n    struct IterateBenchmark<DB: BenchDatabase, T: BenchTable + RandomTable> {\n        bench: String,\n        db: String,\n        in_memory: bool,\n        schema: String,\n        indices: IndexStrategy,\n        count: u32,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<(DB, T)>,\n    }\n\n    impl<DB: BenchDatabase, T: BenchTable + RandomTable> Benchmark for IterateBenchmark<DB, T> {\n        fn run_benchmark(self) {\n            assert_eq!(self.bench, \"iterate\", \"provided metadata has incorrect bench name\");\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(self.schema, T::name(), \"provided metadata has incorrect db name\");\n\n            let mut db = DB::build(self.in_memory).unwrap();\n\n            let table_id = db.create_table::<T>(self.indices).unwrap();\n            let data = create_sequential::<T>(0xdeadbeef, self.count, 64);\n\n            // warm up\n            db.insert_bulk(&table_id, data).unwrap();\n            db.iterate(&table_id).unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                db.iterate(&table_id).unwrap();\n            });\n\n            // clean up\n            db.clear_table(&table_id).unwrap();\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::mem_64(\n        r#\"{\"bench\": \"iterate\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 64}\"#\n    )]\n    #[bench::mem_1024(\n        r#\"{\"bench\": \"iterate\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 1024}\"#\n    )]\n    #[bench::disk_64(\n        r#\"{\"bench\": \"iterate\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 64}\"#\n    )]\n    #[bench::disk_1024(\n        r#\"{\"bench\": \"iterate\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 1024}\"#\n    )]\n    fn iterate_raw_u32_u64_str(metadata: &str) {\n        let bench: IterateBenchmark<SpacetimeRaw, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    #[library_benchmark]\n    #[bench::mem_64(\n        r#\"{\"bench\": \"iterate\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 64}\"#\n    )]\n    #[bench::mem_1024(\n        r#\"{\"bench\": \"iterate\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 1024}\"#\n    )]\n    #[bench::disk_64(\n        r#\"{\"bench\": \"iterate\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 64}\"#\n    )]\n    #[bench::disk_1024(\n        r#\"{\"bench\": \"iterate\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"unique_0\", \"count\": 1024}\"#\n    )]\n    fn iterate_sqlite_u32_u64_str(metadata: &str) {\n        let bench: IterateBenchmark<SQLite, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = iterate_group;\n        benchmarks = iterate_raw_u32_u64_str, /*iterate_module_u32_u64_str,*/ iterate_sqlite_u32_u64_str\n    );\n\n    // ========================= FILTER =========================\n\n    #[derive(Deserialize)]\n    struct FilterBenchmark<DB: BenchDatabase, T: BenchTable + RandomTable> {\n        bench: String,\n        db: String,\n        in_memory: bool,\n        schema: String,\n        indices: IndexStrategy,\n        count: u32,\n        preload: u32,\n        // Underscore here cause it's an implementation detail.\n        // The only thing downstream cares about is data_type\n        _column: u16,\n        data_type: String,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<(DB, T)>,\n    }\n\n    impl<DB: BenchDatabase, T: BenchTable + RandomTable> Benchmark for FilterBenchmark<DB, T> {\n        fn run_benchmark(self) {\n            assert_eq!(self.bench, \"filter\", \"provided metadata has incorrect bench name\");\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(self.schema, T::name(), \"provided metadata has incorrect db name\");\n\n            let filter_column_type = match T::product_type().elements[self._column as usize].algebraic_type {\n                AlgebraicType::String => \"string\",\n                AlgebraicType::U32 => \"u32\",\n                AlgebraicType::U64 => \"u64\",\n                _ => unimplemented!(),\n            };\n            assert_eq!(\n                filter_column_type, self.data_type,\n                \"provided metadata has incorrect data type\"\n            );\n\n            let mut db = DB::build(self.in_memory).unwrap();\n\n            let table_id = db.create_table::<T>(self.indices).unwrap();\n            let data = create_partly_identical::<T>(0xdeadbeef, self.count as u64, self.preload as u64);\n\n            // create_partly_identical guarantees that the first `count` elements will be identical\n            let filter_value = data[0].clone().into_product_value().elements[self._column as usize].clone();\n\n            // warm up\n            db.insert_bulk(&table_id, data).unwrap();\n            db.filter::<T>(&table_id, self._column, filter_value.clone()).unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                db.filter::<T>(&table_id, self._column, filter_value.clone()).unwrap();\n            });\n\n            // clean up\n            db.clear_table(&table_id).unwrap();\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::string_mem_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    #[bench::string_mem_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    #[bench::u64_mem_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    #[bench::u64_mem_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    #[bench::string_disk_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    #[bench::string_disk_btree_64_from_128(\n    r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    #[bench::u64_disk_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    #[bench::u64_disk_btree_64_from_128(\n    r#\"{\"bench\": \"filter\", \"db\": \"stdb_raw\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    fn filter_raw_u32_u64_str(metadata: &str) {\n        let bench: FilterBenchmark<SpacetimeRaw, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    #[library_benchmark]\n    // string, btree index\n    #[bench::string_mem_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    // string, btree index\n    #[bench::string_mem_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    // u64, no index\n    #[bench::u64_mem_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    // u64, btree index\n    #[bench::u64_mem_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": true, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    // string, no index\n    #[bench::string_disk_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    // string, btree index\n    #[bench::string_disk_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 2, \"data_type\": \"string\"}\"#\n    )]\n    // u64, no index\n    #[bench::u64_disk_no_index_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"no_index\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    // u64, btree index\n    #[bench::u64_disk_btree_64_from_128(\n        r#\"{\"bench\": \"filter\", \"db\": \"sqlite\", \"in_memory\": false, \"schema\": \"u32_u64_str\", \"indices\": \"btree_each_column\",\n        \"count\": 64, \"preload\": 128, \"_column\": 1, \"data_type\": \"u64\"}\"#\n    )]\n    fn filter_sqlite_u32_u64_str(metadata: &str) {\n        let bench: FilterBenchmark<SQLite, u32_u64_str> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = filter_group;\n        benchmarks = filter_raw_u32_u64_str, /*filter_module_u32_u64_str,*/ filter_sqlite_u32_u64_str\n    );\n\n    // ========================= FIND =========================\n\n    #[derive(Deserialize)]\n    #[expect(unused)] // TODO\n    struct FindBenchmark<DB: BenchDatabase, T: BenchTable + RandomTable> {\n        bench: String,\n        db: String,\n        schema: String,\n        indices: IndexStrategy,\n        preload: u32,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<(DB, T)>,\n    }\n\n    impl<DB: BenchDatabase, T: BenchTable + RandomTable> Benchmark for FindBenchmark<DB, T> {\n        fn run_benchmark(self) {\n            assert_eq!(self.bench, \"find\", \"provided metadata has incorrect bench name\");\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(self.schema, T::name(), \"provided metadata has incorrect db name\");\n            assert_eq!(\n                T::product_type().elements[0].algebraic_type,\n                AlgebraicType::U32,\n                \"primary key in tuple slot 0 must be u32\"\n            );\n\n            let mut db = DB::build(false).unwrap();\n\n            let table_id = db.create_table::<T>(self.indices).unwrap();\n\n            let data = create_sequential::<T>(0xdeadbeef, self.preload, 64);\n\n            let filter_value = data[(self.preload - 1) as usize].clone().into_product_value().elements[0].clone();\n\n            // warm up\n            db.insert_bulk(&table_id, data).unwrap();\n            db.filter::<T>(&table_id, 0, filter_value.clone()).unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                db.filter::<T>(&table_id, 0, filter_value.clone()).unwrap();\n            });\n\n            // clean up\n            db.clear_table(&table_id).unwrap();\n        }\n    }\n\n    // ========================= EMPTY TRANSACTION =========================\n\n    #[derive(Deserialize)]\n    struct EmptyTransactionBenchmark<DB: BenchDatabase> {\n        bench: String,\n        db: String,\n        in_memory: bool,\n        #[serde(skip)]\n        _marker: std::marker::PhantomData<DB>,\n    }\n\n    impl<DB: BenchDatabase> Benchmark for EmptyTransactionBenchmark<DB> {\n        fn run_benchmark(self) {\n            assert_eq!(\n                self.bench, \"empty transaction\",\n                \"provided metadata has incorrect bench name\"\n            );\n            assert_eq!(self.db, DB::name(), \"provided metadata has incorrect db name\");\n\n            let mut db = DB::build(self.in_memory).unwrap();\n\n            // warm up\n            db.empty_transaction().unwrap();\n\n            // measure\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| db.empty_transaction().unwrap());\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::empty_in_mem(r#\"{\"bench\": \"empty transaction\", \"db\": \"stdb_raw\", \"in_memory\": true}\"#)]\n    #[bench::empty_on_disk(r#\"{\"bench\": \"empty transaction\", \"db\": \"stdb_raw\", \"in_memory\": false}\"#)]\n    fn empty_transaction_raw(metadata: &str) {\n        let bench: EmptyTransactionBenchmark<SpacetimeRaw> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    /*\n    #[library_benchmark]\n    #[bench::b1(r#\"{\"bench\": \"empty transaction\", \"db\": \"stdb_module\"}\"#)]\n    fn empty_transaction_module(metadata: &str) {\n        let bench: EmptyTransactionBenchmark<SpacetimeModule> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n    */\n\n    #[library_benchmark]\n    #[bench::empty_in_mem(r#\"{\"bench\": \"empty transaction\", \"db\": \"sqlite\", \"in_memory\": true}\"#)]\n    #[bench::empty_on_disk(r#\"{\"bench\": \"empty transaction\", \"db\": \"sqlite\", \"in_memory\": false}\"#)]\n    fn empty_transaction_sqlite(metadata: &str) {\n        let bench: EmptyTransactionBenchmark<SQLite> = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = empty_transaction_group;\n        benchmarks = empty_transaction_raw, /*empty_transaction_module,*/ empty_transaction_sqlite\n    );\n\n    // ========================= SERIALIZATION =========================\n\n    #[derive(Deserialize)]\n    struct BSatnSerializationBenchmark {\n        bench: String,\n        format: String,\n        count: u32,\n    }\n\n    impl Benchmark for BSatnSerializationBenchmark {\n        fn run_benchmark(self) {\n            assert_eq!(\n                self.bench, \"serialize_product_value\",\n                \"provided metadata has incorrect bench name\"\n            );\n            assert_eq!(self.format, \"bsatn\", \"provided metadata has incorrect format\");\n\n            let buckets = 64;\n            let data = create_sequential::<u32_u64_str>(0xdeadbeef, self.count, buckets)\n                .into_iter()\n                .map(|row| spacetimedb_lib::AlgebraicValue::Product(row.into_product_value()))\n                .collect::<ProductValue>();\n\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                // don't time deallocation: return this!\n                spacetimedb_lib::sats::bsatn::to_vec(&data).unwrap()\n            }); // allocation dropped here\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::b1(r#\"{\"bench\": \"serialize_product_value\", \"format\": \"bsatn\", \"count\": 16}\"#)]\n    #[bench::b2(r#\"{\"bench\": \"serialize_product_value\", \"format\": \"bsatn\", \"count\": 64}\"#)]\n    fn bsatn_serialization(metadata: &str) {\n        let bench: BSatnSerializationBenchmark = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    #[derive(Deserialize)]\n    struct JSONSerializationBenchmark {\n        bench: String,\n        format: String,\n        count: u32,\n    }\n\n    impl Benchmark for JSONSerializationBenchmark {\n        fn run_benchmark(self) {\n            assert_eq!(\n                self.bench, \"serialize_product_value\",\n                \"provided metadata has incorrect bench name\"\n            );\n            assert_eq!(self.format, \"json\", \"provided metadata has incorrect format\");\n\n            let buckets = 64;\n            let data = create_sequential::<u32_u64_str>(0xdeadbeef, self.count, buckets)\n                .into_iter()\n                .map(|row| spacetimedb_lib::AlgebraicValue::Product(row.into_product_value()))\n                .collect::<ProductValue>();\n\n            spacetimedb::callgrind_flag::enable_callgrind_globally(|| {\n                // don't time deallocation: return this!\n                serde_json::to_string(&data).unwrap()\n            }); // allocation dropped here\n        }\n    }\n\n    #[library_benchmark]\n    #[bench::b1(r#\"{\"bench\": \"serialize_product_value\", \"format\": \"json\", \"count\": 16}\"#)]\n    #[bench::b2(r#\"{\"bench\": \"serialize_product_value\", \"format\": \"json\", \"count\": 64}\"#)]\n    fn json_serialization(metadata: &str) {\n        let bench: JSONSerializationBenchmark = serde_json::from_str(metadata).unwrap();\n        bench.run_benchmark();\n    }\n\n    library_benchmark_group!(\n        name = serialize_group;\n        benchmarks = bsatn_serialization, json_serialization\n    );\n\n    // ========================= HARNESS =========================\n    iai_callgrind::main!(\n        config = LibraryBenchmarkConfig::default()\n                    .pass_through_envs([\"HOME\", \"PATH\", \"RUST_LOG\", \"RUST_BACKTRACE\"])\n                    // THE NEXT LINE IS CRITICAL.\n                    // Without this line, this entire file breaks!\n                    .with_custom_entry_point(\"spacetimedb::callgrind_flag::flag\");\n        library_benchmark_groups = insert_bulk_group, update_bulk_group, filter_group,\n                                iterate_group, empty_transaction_group,\n                                serialize_group\n    );\n\n    // have to re-export `main`, it's not marked as `pub` in the macro\n    pub fn run_benches() {\n        main();\n    }\n}\n\nfn main() {\n    #[cfg(target_os = \"linux\")]\n    callgrind_benches::run_benches();\n\n    #[cfg(not(target_os = \"linux\"))]\n    println!(\"Callgrind does not exist for your operating system.\");\n}\n"
  },
  {
    "path": "crates/bench/benches/delete_table.rs",
    "content": "use core::{cmp::Ordering, iter::repeat_with, time::Duration};\nuse criterion::{\n    black_box, criterion_group, criterion_main,\n    measurement::{Measurement as _, WallTime},\n    BenchmarkGroup, Criterion,\n};\nuse itertools::Itertools;\nuse rand::{prelude::*, seq::SliceRandom};\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_datastore::locking_tx_datastore::delete_table;\nuse spacetimedb_sats::layout::Size;\nuse spacetimedb_table::indexes::{PageIndex, PageOffset, RowPointer, SquashedOffset};\nuse std::collections::BTreeSet;\n\nfn time<R>(body: impl FnOnce() -> R) -> Duration {\n    let start = WallTime.start();\n    let ret = body();\n    let end = WallTime.end(start);\n    black_box(ret);\n    end\n}\n\nconst FIXED_ROW_SIZE: Size = Size(4 * 4);\n\nfn gen_row_pointers(iters: u64) -> impl Iterator<Item = RowPointer> {\n    let mut page_index = PageIndex(0);\n    let mut page_offset = PageOffset(0);\n    let iter = repeat_with(move || {\n        page_offset += FIXED_ROW_SIZE;\n        if page_offset >= PageOffset::PAGE_END {\n            // Consumed the page, let's use a new page.\n            page_index.0 += 1;\n            page_offset = PageOffset(0);\n        }\n\n        black_box(RowPointer::new(\n            false,\n            page_index,\n            page_offset,\n            SquashedOffset::COMMITTED_STATE,\n        ))\n    });\n    iter.take(iters as usize)\n}\n\nfn bench_custom(g: &mut BenchmarkGroup<'_, WallTime>, name: &str, run: impl Fn(u64) -> Duration) {\n    g.bench_function(name, |b| b.iter_custom(&run));\n}\n\nfn bench_delete_table<DT: DeleteTable>(c: &mut Criterion) {\n    let name = DT::NAME;\n    let mut g = c.benchmark_group(name);\n    let row_size = black_box(FIXED_ROW_SIZE);\n    let new_dt = || DT::new(row_size);\n    bench_custom(&mut g, \"mixed\", |i| {\n        let mut dt = new_dt();\n        gen_row_pointers(i)\n            .map(|ptr| time(|| dt.contains(ptr)) + time(|| dt.insert(ptr)))\n            .sum()\n    });\n    bench_custom(&mut g, \"mixed_random\", |i| {\n        let mut dt = new_dt();\n        let mut ptrs = gen_row_pointers(i).collect_vec();\n        let mut rng = ThreadRng::default();\n        ptrs.shuffle(&mut rng);\n        ptrs.into_iter()\n            .map(|ptr| time(|| dt.contains(ptr)) + time(|| dt.insert(ptr)))\n            .sum()\n    });\n    bench_custom(&mut g, \"insert\", |i| {\n        let mut dt = new_dt();\n        gen_row_pointers(i).map(|ptr| time(|| dt.insert(ptr))).sum()\n    });\n    bench_custom(&mut g, \"contains_for_half\", |i| {\n        let mut dt = new_dt();\n        gen_row_pointers(i)\n            .enumerate()\n            .map(|(i, ptr)| {\n                if i % 2 == 0 {\n                    black_box(dt.insert(ptr));\n                }\n                time(|| dt.contains(ptr))\n            })\n            .sum()\n    });\n    bench_custom(&mut g, \"contains_for_full\", |i| {\n        let mut dt = new_dt();\n        gen_row_pointers(i)\n            .map(|ptr| {\n                black_box(dt.insert(ptr));\n                time(|| dt.contains(ptr))\n            })\n            .sum()\n    });\n    bench_custom(&mut g, \"remove\", |i| {\n        let mut dt = new_dt();\n        for ptr in gen_row_pointers(i) {\n            black_box(dt.insert(ptr));\n        }\n        gen_row_pointers(i).map(|ptr| time(|| dt.remove(ptr))).sum()\n    });\n    bench_custom(&mut g, \"iter\", |i| {\n        let mut dt = new_dt();\n        for ptr in gen_row_pointers(i) {\n            black_box(dt.insert(ptr));\n        }\n        time(|| dt.iter().count())\n    });\n    g.finish();\n}\n\ntrait DeleteTable {\n    const NAME: &'static str;\n    fn new(fixed_row_size: Size) -> Self;\n    fn contains(&self, ptr: RowPointer) -> bool;\n    fn insert(&mut self, ptr: RowPointer) -> bool;\n    fn remove(&mut self, ptr: RowPointer) -> bool;\n    fn iter(&self) -> impl Iterator<Item = RowPointer>;\n    #[allow(unused)]\n    fn len(&self) -> usize;\n}\n\nstruct DTBTree(BTreeSet<RowPointer>);\n\nimpl DeleteTable for DTBTree {\n    const NAME: &'static str = \"DTBTree\";\n    fn new(_: Size) -> Self {\n        Self(<_>::default())\n    }\n    fn contains(&self, ptr: RowPointer) -> bool {\n        self.0.contains(&ptr)\n    }\n    fn insert(&mut self, ptr: RowPointer) -> bool {\n        self.0.insert(ptr)\n    }\n    fn remove(&mut self, ptr: RowPointer) -> bool {\n        self.0.remove(&ptr)\n    }\n    fn iter(&self) -> impl Iterator<Item = RowPointer> {\n        self.0.iter().copied()\n    }\n    fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\nstruct DTHashSet(HashSet<RowPointer>);\n\nimpl DeleteTable for DTHashSet {\n    const NAME: &'static str = \"DTHashSet\";\n    fn new(_: Size) -> Self {\n        Self(<_>::default())\n    }\n    fn contains(&self, ptr: RowPointer) -> bool {\n        self.0.contains(&ptr)\n    }\n    fn insert(&mut self, ptr: RowPointer) -> bool {\n        self.0.insert(ptr)\n    }\n    fn remove(&mut self, ptr: RowPointer) -> bool {\n        self.0.remove(&ptr)\n    }\n    fn iter(&self) -> impl Iterator<Item = RowPointer> {\n        self.0.iter().copied()\n    }\n    fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\nstruct DTHashSetFH(foldhash::HashSet<RowPointer>);\n\nimpl DeleteTable for DTHashSetFH {\n    const NAME: &'static str = \"DTHashSetFH\";\n    fn new(_: Size) -> Self {\n        Self(<_>::default())\n    }\n    fn contains(&self, ptr: RowPointer) -> bool {\n        self.0.contains(&ptr)\n    }\n    fn insert(&mut self, ptr: RowPointer) -> bool {\n        self.0.insert(ptr)\n    }\n    fn remove(&mut self, ptr: RowPointer) -> bool {\n        self.0.remove(&ptr)\n    }\n    fn iter(&self) -> impl Iterator<Item = RowPointer> {\n        self.0.iter().copied()\n    }\n    fn len(&self) -> usize {\n        self.0.len()\n    }\n}\n\ntype DTPageAndBitSet = delete_table::DeleteTable;\n\nimpl DeleteTable for DTPageAndBitSet {\n    const NAME: &'static str = \"DTPageAndBitSet\";\n    fn new(fixed_row_size: Size) -> Self {\n        Self::new(fixed_row_size)\n    }\n    fn contains(&self, ptr: RowPointer) -> bool {\n        self.contains(ptr)\n    }\n    fn insert(&mut self, ptr: RowPointer) -> bool {\n        self.insert(ptr)\n    }\n    fn remove(&mut self, ptr: RowPointer) -> bool {\n        self.remove(ptr)\n    }\n    fn iter(&self) -> impl Iterator<Item = RowPointer> {\n        self.iter()\n    }\n    fn len(&self) -> usize {\n        self.len()\n    }\n}\n\n#[derive(Clone, Copy)]\nstruct OffsetRange {\n    start: PageOffset,\n    end: PageOffset,\n}\nimpl OffsetRange {\n    fn point(offset: PageOffset) -> Self {\n        Self {\n            start: offset,\n            end: offset,\n        }\n    }\n}\ntype OffsetRanges = SmallVec<[OffsetRange; 4]>;\nstruct DTPageAndOffsetRanges {\n    deleted: Vec<OffsetRanges>,\n    len: usize,\n    fixed_row_size: Size,\n}\n\nfn cmp_start_end<T: Ord>(start: &T, end: &T, needle: &T) -> Ordering {\n    match (start.cmp(needle), end.cmp(needle)) {\n        // start = needle or start < offset <= end => we have a match.\n        (Ordering::Less, Ordering::Equal | Ordering::Greater) | (Ordering::Equal, _) => Ordering::Equal,\n        // start <= end < needle => no match.\n        (Ordering::Less, Ordering::Less) => Ordering::Less,\n        // start <= end > needle => no match.\n        (Ordering::Greater, _) => Ordering::Greater,\n    }\n}\n\n#[inline]\nfn find_range_to_insert_offset(\n    ranges: &OffsetRanges,\n    offset: PageOffset,\n    fixed_row_size: Size,\n) -> Result<(bool, bool, usize), usize> {\n    let mut extend_end = true;\n    let mut exists = false;\n    ranges\n        .binary_search_by(|&OffsetRange { start, end }| {\n            extend_end = true;\n            exists = false;\n            match end.cmp(&offset) {\n                // `end + row_size = offset` => we can just extend `end = offset`.\n                Ordering::Less if end.0 + fixed_row_size.0 == offset.0 => Ordering::Equal,\n                // Cannot extend this range, so let's not find it.\n                Ordering::Less => Ordering::Less,\n                // `offset` is already covered, so don't do anything,\n                // but `end = offset` is a no-op.\n                Ordering::Equal => {\n                    exists = true;\n                    Ordering::Equal\n                }\n                // `end` is greater, but we may be covered by `start` instead.\n                Ordering::Greater => match start.cmp(&offset) {\n                    // `offset` is within the range, so don't do anything.\n                    Ordering::Less | Ordering::Equal => {\n                        exists = true;\n                        Ordering::Equal\n                    }\n                    // `start - row_size = offset` => we can just extend `start = offset`.\n                    Ordering::Greater if start.0 - fixed_row_size.0 == offset.0 => {\n                        extend_end = false;\n                        Ordering::Equal\n                    }\n                    // Range is entirely greater than offset.\n                    Ordering::Greater => Ordering::Greater,\n                },\n            }\n        })\n        .map(|idx| (extend_end, exists, idx))\n}\n\nimpl DeleteTable for DTPageAndOffsetRanges {\n    const NAME: &'static str = \"DTPageAndOffsetRanges\";\n    fn new(fixed_row_size: Size) -> Self {\n        Self {\n            deleted: <_>::default(),\n            len: 0,\n            fixed_row_size,\n        }\n    }\n    fn contains(&self, ptr: RowPointer) -> bool {\n        let page_idx = ptr.page_index().idx();\n        let page_offset = ptr.page_offset();\n        match self.deleted.get(page_idx) {\n            Some(ranges) => ranges\n                .binary_search_by(|r| cmp_start_end(&r.start, &r.end, &page_offset))\n                .is_ok(),\n            _ => false,\n        }\n    }\n    fn insert(&mut self, ptr: RowPointer) -> bool {\n        let fixed_row_size = self.fixed_row_size;\n        let page_idx = ptr.page_index().idx();\n        let page_offset = ptr.page_offset();\n\n        let Some(ranges) = self.deleted.get_mut(page_idx) else {\n            let pages = self.deleted.len();\n            let after = 1 + page_idx;\n            self.deleted.reserve(after - pages);\n            for _ in pages..after {\n                self.deleted.push(SmallVec::new());\n            }\n            self.deleted[page_idx].push(OffsetRange::point(page_offset));\n            self.len += 1;\n            return true;\n        };\n\n        let (extend_end, exists, range_idx) = match find_range_to_insert_offset(ranges, page_offset, fixed_row_size) {\n            Err(range_idx) => {\n                // Not found, so add a point range.\n                ranges.insert(range_idx, OffsetRange::point(page_offset));\n                self.len += 1;\n                return true;\n            }\n            Ok(x) => x,\n        };\n\n        if extend_end {\n            let next = range_idx + 1;\n            let new_end = if let Some(r) = ranges\n                .get(next)\n                .copied()\n                .filter(|r| r.start.0 - fixed_row_size.0 == page_offset.0)\n            {\n                ranges.remove(next);\n                r.end\n            } else {\n                page_offset\n            };\n            ranges[range_idx].end = new_end;\n        } else {\n            let prev = range_idx.saturating_sub(1);\n            if let Some(r) = ranges\n                .get(prev)\n                .copied()\n                .filter(|r| r.end.0 + fixed_row_size.0 == page_offset.0)\n            {\n                ranges[range_idx].start = r.start;\n                ranges.remove(prev);\n            } else {\n                ranges[range_idx].start = page_offset;\n            };\n        }\n\n        let added = !exists;\n        if added {\n            self.len += 1;\n        }\n        added\n    }\n    fn remove(&mut self, ptr: RowPointer) -> bool {\n        let fixed_row_size = self.fixed_row_size;\n        let page_idx = ptr.page_index().idx();\n        let page_offset = ptr.page_offset();\n\n        let Some(ranges) = self.deleted.get_mut(page_idx) else {\n            return false;\n        };\n        let Ok(idx) = ranges.binary_search_by(|r| cmp_start_end(&r.start, &r.end, &page_offset)) else {\n            return false;\n        };\n\n        self.len -= 1;\n\n        let range = &mut ranges[idx];\n        let is_start = range.start == page_offset;\n        let is_end = range.end == page_offset;\n        match (is_start, is_end) {\n            // Remove the point range.\n            (true, true) => drop(ranges.remove(idx)),\n            // Narrow the start.\n            (true, false) => range.start += fixed_row_size,\n            // Narrow the end.\n            (false, true) => range.end -= fixed_row_size,\n            // Split the range.\n            (false, false) => {\n                // Derive the second range, to the right of the hole.\n                let end = range.end;\n                let start = PageOffset(page_offset.0 + fixed_row_size.0);\n                let new = OffsetRange { start, end };\n                // Adjust the first range, to the left of the hole.\n                range.end.0 = page_offset.0 - fixed_row_size.0;\n                // Add the second range.\n                ranges.insert(idx + 1, new);\n            }\n        }\n        true\n    }\n    fn iter(&self) -> impl Iterator<Item = RowPointer> {\n        (0..)\n            .map(PageIndex)\n            .zip(self.deleted.iter())\n            .flat_map(move |(pi, ranges)| {\n                ranges\n                    .iter()\n                    .flat_map(|range| (range.start.0..=range.end.0).step_by(self.fixed_row_size.0 as usize))\n                    .map(PageOffset)\n                    .map(move |po| RowPointer::new(false, pi, po, SquashedOffset::COMMITTED_STATE))\n            })\n    }\n    fn len(&self) -> usize {\n        self.len\n    }\n}\n\ncriterion_group!(\n    delete_table,\n    bench_delete_table::<DTBTree>,\n    bench_delete_table::<DTHashSet>,\n    bench_delete_table::<DTHashSetFH>,\n    bench_delete_table::<DTPageAndBitSet>,\n    bench_delete_table::<DTPageAndOffsetRanges>, // best so far.\n);\ncriterion_main!(delete_table);\n"
  },
  {
    "path": "crates/bench/benches/generic.rs",
    "content": "use criterion::{\n    criterion_group, criterion_main,\n    measurement::{Measurement, WallTime},\n    Bencher, BenchmarkGroup, Criterion,\n};\nuse lazy_static::lazy_static;\nuse spacetimedb_bench::{\n    database::BenchDatabase,\n    schemas::{create_sequential, u32_u64_str, u32_u64_u64, BenchTable, IndexStrategy, RandomTable},\n    spacetime_module, spacetime_raw, sqlite, ResultBench,\n};\nuse spacetimedb_lib::sats::AlgebraicType;\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_testing::modules::{Csharp, Rust};\n\n#[cfg(target_env = \"msvc\")]\n#[global_allocator]\nstatic GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemallocator::Jemalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\n#[global_allocator]\nstatic GLOBAL: Jemalloc = Jemalloc;\n\nlazy_static! {\n    static ref RUN_ONE_MILLION: bool = std::env::var(\"RUN_ONE_MILLION\").is_ok();\n}\n\nfn criterion_benchmark(c: &mut Criterion) {\n    bench_suite::<sqlite::SQLite>(c, true).unwrap();\n    bench_suite::<spacetime_raw::SpacetimeRaw>(c, true).unwrap();\n    bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, true).unwrap();\n    bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, true).unwrap();\n\n    bench_suite::<sqlite::SQLite>(c, false).unwrap();\n    bench_suite::<spacetime_raw::SpacetimeRaw>(c, false).unwrap();\n    bench_suite::<spacetime_module::SpacetimeModule<Rust>>(c, false).unwrap();\n    bench_suite::<spacetime_module::SpacetimeModule<Csharp>>(c, false).unwrap();\n}\n\n#[inline(never)]\nfn bench_suite<DB: BenchDatabase>(c: &mut Criterion, in_memory: bool) -> ResultBench<()> {\n    let mut db = DB::build(in_memory)?;\n    let param_db_name = DB::name();\n    let param_in_memory = if in_memory { \"mem\" } else { \"disk\" };\n    let db_params = format!(\"{param_db_name}/{param_in_memory}\");\n\n    let mut g = c.benchmark_group(&db_params);\n\n    empty(&mut g, &mut db)?;\n\n    table_suite::<DB, u32_u64_str>(&mut g, &mut db)?;\n    table_suite::<DB, u32_u64_u64>(&mut g, &mut db)?;\n\n    Ok(())\n}\n\ntype Group<'a> = BenchmarkGroup<'a, WallTime>;\n\n#[inline(never)]\nfn table_suite<DB: BenchDatabase, T: BenchTable + RandomTable>(g: &mut Group, db: &mut DB) -> ResultBench<()> {\n    // This setup is a compromise between trying to present related benchmarks together,\n    // and not having to deal with nasty reentrant generic dispatching.\n\n    type TableData<TableId> = (IndexStrategy, TableId, String);\n    let mut prep_table = |index_strategy: IndexStrategy| -> ResultBench<TableData<DB::TableId>> {\n        let table_name = T::name();\n        let style_name = index_strategy.name();\n        let table_params = format!(\"{table_name}/{style_name}\");\n        let table_id = db.create_table::<T>(index_strategy)?;\n\n        Ok((index_strategy, table_id, table_params))\n    };\n    let tables: [TableData<DB::TableId>; 2] = [\n        prep_table(IndexStrategy::Unique0)?,\n        //prep_table(IndexStrategy::NoIndex)?,\n        prep_table(IndexStrategy::BTreeEachColumn)?,\n    ];\n\n    for (_, table_id, table_params) in &tables {\n        insert_bulk::<DB, T>(g, table_params, db, table_id, 2048, 256)?;\n        if *RUN_ONE_MILLION {\n            insert_bulk::<DB, T>(g, table_params, db, table_id, 0, 1_000_000)?;\n        }\n    }\n    for (index_strategy, table_id, table_params) in &tables {\n        if *index_strategy == IndexStrategy::Unique0 {\n            // Iterate is unaffected by index strategy, so only run it here\n            iterate::<DB, T>(g, table_params, db, table_id, 256)?;\n            // Update can only be performed with a unique key\n            update_bulk::<DB, T>(g, table_params, db, table_id, 2048, 256)?;\n            if *RUN_ONE_MILLION {\n                update_bulk::<DB, T>(g, table_params, db, table_id, 1_000_000, 1_000_000)?;\n            }\n        } else {\n            // perform \"filter\" benchmarks\n            filter::<DB, T>(g, db, table_id, index_strategy, 2, 2048, 8)?;\n        }\n    }\n\n    Ok(())\n}\n\n/// Custom criterion timing loop. Allows access to a database, which is reset every benchmark iteration.\n///\n/// The prepare closure should restore the database to known state\n/// and prepare any inputs consumed by the benchmark. The time this takes will\n/// not be measured.\n///\n/// You should clear all modified tables after calling this, just to be safe.\n#[inline(never)]\nfn bench_harness<\n    DB: BenchDatabase,\n    INPUT,\n    PREPARE: FnMut(&mut DB) -> ResultBench<INPUT>,\n    ROUTINE: FnMut(&mut DB, INPUT) -> ResultBench<()>,\n>(\n    b: &mut Bencher,\n    db: &mut DB,\n    mut prepare: PREPARE,\n    mut routine: ROUTINE,\n) {\n    b.iter_custom(|n| {\n        let timer = WallTime;\n        let mut elapsed = timer.zero();\n\n        for _ in 0..n {\n            let input = prepare(db).unwrap();\n\n            // only nanoseconds of overhead\n            let start = timer.start();\n            routine(db, input).unwrap();\n            let just_elapsed = timer.end(start);\n\n            elapsed = timer.add(&elapsed, &just_elapsed);\n        }\n        elapsed\n    });\n}\n\n#[inline(never)]\nfn empty<DB: BenchDatabase>(g: &mut Group, db: &mut DB) -> ResultBench<()> {\n    let id = \"empty\".to_string();\n    g.bench_function(&id, |b| {\n        bench_harness(\n            b,\n            db,\n            |_| Ok(()),\n            |db, _| {\n                // not much to do in this one\n                db.empty_transaction()\n            },\n        )\n    });\n    Ok(())\n}\n\n#[inline(never)]\nfn insert_bulk<DB: BenchDatabase, T: BenchTable + RandomTable>(\n    g: &mut Group,\n    table_params: &str,\n    db: &mut DB,\n    table_id: &DB::TableId,\n    load: u32,\n    count: u32,\n) -> ResultBench<()> {\n    let id = format!(\"insert_bulk/{table_params}/load={load}/count={count}\");\n    let data = create_sequential::<T>(0xdeadbeef, load + count, 1000);\n\n    // Each iteration performs one transaction, though it inserts many rows.\n    g.throughput(criterion::Throughput::Elements(1));\n    // FIXME: only for 1_000_000 inserts\n    g.sample_size(10);\n\n    g.bench_function(&id, |b| {\n        bench_harness(\n            b,\n            db,\n            |db| {\n                let mut data = data.clone();\n                db.clear_table(table_id)?;\n                let to_insert = data.split_off(load as usize);\n                if !data.is_empty() {\n                    db.insert_bulk(table_id, data)?;\n                }\n                Ok(to_insert)\n            },\n            |db, to_insert| {\n                db.insert_bulk(table_id, to_insert)?;\n                Ok(())\n            },\n        )\n    });\n    db.clear_table(table_id)?;\n    Ok(())\n}\n\n#[inline(never)]\nfn update_bulk<DB: BenchDatabase, T: BenchTable + RandomTable>(\n    g: &mut Group,\n    table_params: &str,\n    db: &mut DB,\n    table_id: &DB::TableId,\n    load: u32,\n    count: u32,\n) -> ResultBench<()> {\n    let id = format!(\"update_bulk/{table_params}/load={load}/count={count}\");\n    let data = create_sequential::<T>(0xdeadbeef, load, 1000);\n\n    // Each iteration performs one transaction, though it inserts many rows.\n    g.throughput(criterion::Throughput::Elements(1));\n\n    // running a big guy\n    g.sample_size(10);\n\n    g.bench_function(&id, |b| {\n        bench_harness(\n            b,\n            db,\n            |db| {\n                let data = data.clone();\n                db.clear_table(table_id)?;\n                db.insert_bulk(table_id, data)?;\n                Ok(())\n            },\n            |db, _| {\n                db.update_bulk::<T>(table_id, count)?;\n                Ok(())\n            },\n        )\n    });\n    db.clear_table(table_id)?;\n    Ok(())\n}\n\n#[inline(never)]\nfn iterate<DB: BenchDatabase, T: BenchTable + RandomTable>(\n    g: &mut Group,\n    table_params: &str,\n    db: &mut DB,\n    table_id: &DB::TableId,\n    count: u32,\n) -> ResultBench<()> {\n    let id = format!(\"iterate/{table_params}/count={count}\");\n    let data = create_sequential::<T>(0xdeadbeef, count, 1000);\n\n    db.insert_bulk(table_id, data)?;\n\n    // Each iteration performs a single transaction,\n    // though it iterates across many rows.\n    g.throughput(criterion::Throughput::Elements(1));\n\n    g.bench_function(&id, |b| {\n        bench_harness(\n            b,\n            db,\n            |_| Ok(()),\n            |db, _| {\n                db.iterate(table_id)?;\n                Ok(())\n            },\n        )\n    });\n    db.clear_table(table_id)?;\n    Ok(())\n}\n\n#[inline(never)]\nfn filter<DB: BenchDatabase, T: BenchTable + RandomTable>(\n    g: &mut Group,\n    db: &mut DB,\n    table_id: &DB::TableId,\n    index_strategy: &IndexStrategy,\n    col_id: impl Into<ColId>,\n    load: u32,\n    buckets: u32,\n) -> ResultBench<()> {\n    let col_id = col_id.into();\n\n    let filter_column_type = match T::product_type().elements[col_id.idx()].algebraic_type {\n        AlgebraicType::String => \"string\",\n        AlgebraicType::U32 => \"u32\",\n        AlgebraicType::U64 => \"u64\",\n        _ => unimplemented!(),\n    };\n    let mean_result_count = load / buckets;\n    let indexed = match index_strategy {\n        IndexStrategy::BTreeEachColumn => \"index\",\n        IndexStrategy::NoIndex => \"no_index\",\n        _ => unimplemented!(),\n    };\n    let id = format!(\"filter/{filter_column_type}/{indexed}/load={load}/count={mean_result_count}\");\n\n    let data = create_sequential::<T>(0xdeadbeef, load, buckets as u64);\n\n    db.insert_bulk(table_id, data.clone())?;\n\n    // Each iteration performs a single transaction.\n    g.throughput(criterion::Throughput::Elements(1));\n\n    // We loop through all buckets found in the sample data.\n    // This mildly increases variance on the benchmark, but makes \"mean_result_count\" more accurate.\n    // Note that all databases have EXACTLY the same sample data.\n    let mut i = 0;\n\n    g.bench_function(&id, |b| {\n        bench_harness(\n            b,\n            db,\n            |_| {\n                // pick something to look for\n                let value = data[i].clone().into_product_value().elements[col_id.idx()].clone();\n                i = (i + 1) % load as usize;\n                Ok(value)\n            },\n            |db, value| {\n                db.filter::<T>(table_id, col_id, value)?;\n                Ok(())\n            },\n        )\n    });\n    db.clear_table(table_id)?;\n    Ok(())\n}\n\ncriterion_group!(benches, criterion_benchmark);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/bench/benches/index.rs",
    "content": "use core::{any::type_name, hash::BuildHasherDefault, hint::black_box, iter::repeat_with, mem, time::Duration};\nuse criterion::{\n    criterion_group, criterion_main,\n    measurement::{Measurement as _, WallTime},\n    BenchmarkGroup, Criterion,\n};\nuse foldhash::{HashSet, HashSetExt};\nuse hashbrown::{hash_map::Entry, HashMap};\nuse itertools::Itertools as _;\nuse rand::{\n    distr::{Distribution, StandardUniform},\n    Rng,\n};\nuse spacetimedb_lib::AlgebraicValue;\nuse spacetimedb_sats::{layout::Size, product, u256};\nuse spacetimedb_table::indexes::{PageIndex, PageOffset, RowPointer, SquashedOffset};\nuse spacetimedb_table::table_index::uniquemap::UniqueMap;\nuse spacetimedb_table::table_index::Index as _;\nuse spacetimedb_table::table_index::{\n    unique_direct_index::{ToFromUsize, UniqueDirectIndex},\n    KeySize,\n};\nuse std::hash::Hash;\n\nfn time<R>(body: impl FnOnce() -> R) -> Duration {\n    let start = WallTime.start();\n    let ret = body();\n    let end = WallTime.end(start);\n    black_box(ret);\n    end\n}\n\nconst FIXED_ROW_SIZE: Size = Size(4 * 4);\n\nfn gen_row_pointers() -> impl Iterator<Item = RowPointer> {\n    let mut page_index = PageIndex(0);\n    let mut page_offset = PageOffset(0);\n    repeat_with(move || {\n        if page_offset.0 as usize + FIXED_ROW_SIZE.0 as usize >= PageOffset::PAGE_END.0 as usize {\n            // Consumed the page, let's use a new page.\n            page_index.0 += 1;\n            page_offset = PageOffset(0);\n        } else {\n            page_offset += FIXED_ROW_SIZE;\n        }\n\n        black_box(RowPointer::new(\n            false,\n            page_index,\n            page_offset,\n            SquashedOffset::COMMITTED_STATE,\n        ))\n    })\n}\n\nfn bench_custom(g: &mut BenchmarkGroup<'_, WallTime>, name: &str, run: impl Fn(u64) -> Duration) {\n    g.bench_function(name, |b| b.iter_custom(&run));\n}\n\n/// Returns an iterator with the keys `0..n`.\nfn monotonic_keys(n: u64) -> impl Clone + Iterator<Item = MonoKey> {\n    (0 as MonoKey..).take(n as usize)\n}\n\n// Returns a set with `n` distinct random keys.\nfn random_keys<K: Eq + Hash>(n: u64) -> HashSet<K>\nwhere\n    StandardUniform: Distribution<K>,\n{\n    let desired_len = n as usize;\n    let mut set = HashSet::with_capacity(desired_len);\n    let mut iter = rand::random_iter();\n    while set.len() < desired_len {\n        set.insert(iter.next().unwrap());\n    }\n    set\n}\n\n/// Times inserting `keys` to the index.\nfn time_insertions<I: Index>(keys: impl Iterator<Item = I::K>) -> Duration {\n    let mut index = I::new();\n    keys.zip(gen_row_pointers())\n        .map(black_box)\n        .map(|(key, ptr)| time(|| index.insert(key, ptr)))\n        .sum()\n}\n\n/// Times inserting monotonically increasing keys to the index.\n///\n/// The benchmark intentionally times N keys rather than the Nth key.\nfn bench_insert_monotonic<I: Index<K = MonoKey>>(g: &mut BenchmarkGroup<'_, WallTime>) {\n    bench_custom(g, \"insert_monotonic\", |n| time_insertions::<I>(monotonic_keys(n)));\n}\n\n/// Times inserting random keys to the index.\n///\n/// The benchmark intentionally times N keys rather than the Nth key.\nfn bench_insert_random<I: Index>(g: &mut BenchmarkGroup<'_, WallTime>)\nwhere\n    StandardUniform: Distribution<I::K>,\n{\n    bench_custom(g, \"insert_random\", |n| time_insertions::<I>(random_keys(n).into_iter()));\n}\n\n/// Times seeking `keys` in the index.\nfn time_seeks<I: Index>(keys: impl Clone + Iterator<Item = I::K>) -> Duration {\n    // Prepare index with the keys.\n    let mut index = I::new();\n    for (key, ptr) in keys.clone().zip(gen_row_pointers()) {\n        index.insert(key, ptr).unwrap();\n    }\n\n    // Time seeking every K in keys.\n    keys.map(black_box)\n        .map(|i| time(|| black_box(index.seek(i)).next()))\n        .sum()\n}\n\n/// Times seeking all keys index with monotonically increasing keys.\nfn bench_seek_monotonic<I: Index<K = MonoKey>>(g: &mut BenchmarkGroup<'_, WallTime>) {\n    bench_custom(g, \"seek_monotonic\", |n| time_seeks::<I>(monotonic_keys(n)));\n}\n\n/// Times seeking all keys in an index with random keys.\nfn bench_seek_random<I: Index>(g: &mut BenchmarkGroup<'_, WallTime>)\nwhere\n    StandardUniform: Distribution<I::K>,\n{\n    bench_custom(g, \"seek_random\", |n| {\n        let keys = random_keys(n);\n\n        let keys2 = keys.iter().cloned().sorted();\n\n        // Prepare index with the keys.\n        let mut index = I::new();\n        for (key, ptr) in keys2.clone().zip(gen_row_pointers()) {\n            index.insert(key, ptr).unwrap();\n        }\n\n        // Time seeking every K in keys.\n        keys.into_iter()\n            .map(black_box)\n            .map(|i| time(|| black_box(index.seek(i)).next()))\n            .sum()\n    });\n}\n\n/// Times deleting `keys` in the index.\nfn time_deletes<I: Index>(keys: impl Clone + IntoIterator<Item = I::K>) -> Duration {\n    // Prepare index with the keys.\n    let mut index = I::new();\n    for (key, ptr) in keys.clone().into_iter().zip(gen_row_pointers()) {\n        index.insert(key, ptr).unwrap();\n    }\n\n    // Time deleting every K in keys.\n    keys.into_iter().map(black_box).map(|i| time(|| index.delete(i))).sum()\n}\n\n/// Times deleting for one key in an index with monotonically increasing keys.\nfn bench_delete_monotonic<I: Index<K = MonoKey>>(g: &mut BenchmarkGroup<'_, WallTime>) {\n    bench_custom(g, \"delete_monotonic\", |n| time_deletes::<I>(monotonic_keys(n)));\n}\n\n/// Times seeking for one key in an index with monotonically increasing keys.\nfn bench_delete_random<I: Index>(g: &mut BenchmarkGroup<'_, WallTime>)\nwhere\n    StandardUniform: Distribution<I::K>,\n{\n    bench_custom(g, \"delete_random\", |n| time_deletes::<I>(random_keys(n)));\n}\n\nfn bench_index_mono<I: Index<K = MonoKey>>(c: &mut Criterion) {\n    let mut g = c.benchmark_group(type_name::<I>());\n    bench_insert_monotonic::<I>(&mut g);\n    bench_insert_random::<I>(&mut g);\n    bench_seek_monotonic::<I>(&mut g);\n    bench_seek_random::<I>(&mut g);\n    bench_delete_monotonic::<I>(&mut g);\n    bench_delete_random::<I>(&mut g);\n}\n\nfn bench_index_random<I: Index>(c: &mut Criterion)\nwhere\n    StandardUniform: Distribution<I::K>,\n{\n    let mut g = c.benchmark_group(type_name::<I>());\n    bench_insert_random::<I>(&mut g);\n    bench_seek_random::<I>(&mut g);\n    bench_delete_random::<I>(&mut g);\n}\n\ntype MonoKey = u32;\n\ntrait Index: Clone {\n    type K: Eq + Hash + Ord + Clone;\n    fn new() -> Self;\n    fn insert(&mut self, key: Self::K, val: RowPointer) -> Result<(), RowPointer>;\n    fn seek(&self, key: Self::K) -> impl Iterator<Item = RowPointer>;\n    fn delete(&mut self, key: Self::K) -> bool;\n}\n\n#[derive(Clone)]\nstruct IBTree<K: KeySize<MemoStorage: Clone + Default>>(UniqueMap<K>);\nimpl<K: KeySize<MemoStorage: Clone + Default> + Clone + Eq + Hash + Ord> Index for IBTree<K> {\n    type K = K;\n    fn new() -> Self {\n        Self(<_>::default())\n    }\n    fn insert(&mut self, key: Self::K, val: RowPointer) -> Result<(), RowPointer> {\n        self.0.insert(key, val)\n    }\n    fn seek(&self, key: Self::K) -> impl Iterator<Item = RowPointer> {\n        self.0.seek_point(&key)\n    }\n    fn delete(&mut self, key: Self::K) -> bool {\n        self.0.delete(&key, RowPointer(0))\n    }\n}\n\n#[derive(Clone)]\nstruct IAHash<K>(HashMap<K, RowPointer, BuildHasherDefault<ahash::AHasher>>);\nimpl<K: Clone + Eq + Hash + Ord> Index for IAHash<K> {\n    type K = K;\n    fn new() -> Self {\n        Self(<_>::default())\n    }\n    fn insert(&mut self, key: Self::K, val: RowPointer) -> Result<(), RowPointer> {\n        match self.0.entry(key) {\n            Entry::Vacant(e) => {\n                e.insert(val);\n                Ok(())\n            }\n            Entry::Occupied(e) => Err(*e.into_mut()),\n        }\n    }\n    fn seek(&self, key: Self::K) -> impl Iterator<Item = RowPointer> {\n        self.0.get(&key).into_iter().copied()\n    }\n    fn delete(&mut self, key: Self::K) -> bool {\n        self.0.remove(&key).is_some()\n    }\n}\n\n#[derive(Clone)]\nstruct IFoldHash<K>(HashMap<K, RowPointer, foldhash::fast::RandomState>);\nimpl<K: Clone + Eq + Hash + Ord> Index for IFoldHash<K> {\n    type K = K;\n    fn new() -> Self {\n        Self(<_>::default())\n    }\n    fn insert(&mut self, key: Self::K, val: RowPointer) -> Result<(), RowPointer> {\n        match self.0.entry(key) {\n            Entry::Vacant(e) => {\n                e.insert(val);\n                Ok(())\n            }\n            Entry::Occupied(e) => Err(*e.into_mut()),\n        }\n    }\n    fn seek(&self, key: Self::K) -> impl Iterator<Item = RowPointer> {\n        self.0.get(&key).into_iter().copied()\n    }\n    fn delete(&mut self, key: Self::K) -> bool {\n        self.0.remove(&key).is_some()\n    }\n}\n\n#[derive(Clone)]\nstruct IDirectIndex<K>(UniqueDirectIndex<K>);\nimpl<K: KeySize + ToFromUsize + Eq + Hash + Ord> Index for IDirectIndex<K> {\n    type K = K;\n    fn new() -> Self {\n        Self(<_>::default())\n    }\n    fn insert(&mut self, key: Self::K, val: RowPointer) -> Result<(), RowPointer> {\n        self.0.insert(key, val)\n    }\n    fn seek(&self, key: Self::K) -> impl Iterator<Item = RowPointer> {\n        self.0.seek_point(&key)\n    }\n    fn delete(&mut self, key: Self::K) -> bool {\n        self.0.delete(&key, RowPointer(0))\n    }\n}\n\n/* Complex keys */\n\n#[derive(Clone, Copy)]\nstruct U256(u256);\n\nimpl Distribution<U256> for StandardUniform {\n    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> U256 {\n        let (hi, lo) = self.sample(rng);\n        U256(u256::from_words(hi, lo))\n    }\n}\n\nimpl From<U256> for AlgebraicValue {\n    fn from(value: U256) -> AlgebraicValue {\n        AlgebraicValue::U256(Box::new(value.0))\n    }\n}\n\nimpl U256 {\n    fn to_le_bytes(self) -> [u8; 32] {\n        self.0.to_le_bytes()\n    }\n}\n\nmacro_rules! av_enc_type {\n    ($name_av:ident, $name_enc:ident, $sample_ty:ty, ($($sample_pat:ident),* $(,)?)) => {\n        #[allow(non_camel_case_types)]\n        #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]\n        struct $name_av(AlgebraicValue);\n        #[allow(non_camel_case_types)]\n        #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]\n        struct $name_enc([u8; mem::size_of::<$sample_ty>()]);\n\n        impl KeySize for $name_av { type MemoStorage = (); }\n        impl KeySize for $name_enc { type MemoStorage = (); }\n\n        impl Distribution<$name_av> for StandardUniform {\n            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $name_av {\n                let ($($sample_pat,)*): $sample_ty = self.sample(rng);\n                $name_av(product![$($sample_pat),*].into())\n            }\n        }\n\n        impl Distribution<$name_enc> for StandardUniform {\n            #[allow(unused_assignments)]\n            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $name_enc {\n                let ($($sample_pat,)*): $sample_ty = self.sample(rng);\n\n                let mut enc = [0; _];\n                let mut start = 0;\n\n                $(\n                    let size = mem::size_of_val(&$sample_pat);\n                    enc[start..start + size].copy_from_slice(&$sample_pat.to_le_bytes());\n                    start += size;\n                )*\n\n                $name_enc(enc)\n            }\n        }\n    };\n}\n\nav_enc_type!(\n    U16xU256xU32xU256_AV,\n    U16xU256xU32xU256_Enc,\n    (u16, U256, u32, U256),\n    (a, b, c, d)\n);\nav_enc_type!(U16xU256xU32_AV, U16xU256xU32_Enc, (u16, U256, u32), (a, b, c));\n\nav_enc_type!(U256xU32xBool_AV, U256xU32xBool_Enc, (U256, u32, u8), (a, b, c));\nav_enc_type!(U256xU32_AV, U256xU32_Enc, (U256, u32), (a, b));\n\n// This is the index x_z_chunk_index from\n// https://github.com/clockworklabs/BitCraft/blob/f8177345df1e4ec54b91acee94146e75a0525548/BitCraftServer/packages/game/src/messages/components.rs#L224C18-L224C33\nav_enc_type!(I32xI32xU64_AV, I32xI32xU64_Enc, (i32, i32, u64), (a, b, c));\n\ncriterion_group!(\n    delete_table,\n    // Primitive types, both random and monotonic keys:\n    bench_index_mono::<IBTree<u32>>,\n    bench_index_mono::<IAHash<u32>>,\n    bench_index_mono::<IFoldHash<u32>>,\n    bench_index_mono::<IDirectIndex<u32>>,\n    // Complex keys, hash index:\n    bench_index_random::<IFoldHash<U16xU256xU32xU256_AV>>,\n    bench_index_random::<IFoldHash<U16xU256xU32xU256_Enc>>,\n    bench_index_random::<IFoldHash<U16xU256xU32_AV>>,\n    bench_index_random::<IFoldHash<U16xU256xU32_Enc>>,\n    bench_index_random::<IFoldHash<U256xU32xBool_AV>>,\n    bench_index_random::<IFoldHash<U256xU32xBool_Enc>>,\n    bench_index_random::<IFoldHash<U256xU32_AV>>,\n    bench_index_random::<IFoldHash<U256xU32_Enc>>,\n    bench_index_random::<IFoldHash<I32xI32xU64_AV>>,\n    bench_index_random::<IFoldHash<I32xI32xU64_Enc>>,\n    // Complex keys, btree index:\n    bench_index_random::<IBTree<U16xU256xU32xU256_AV>>,\n    bench_index_random::<IBTree<U16xU256xU32xU256_Enc>>,\n    bench_index_random::<IBTree<U16xU256xU32_AV>>,\n    bench_index_random::<IBTree<U16xU256xU32_Enc>>,\n    bench_index_random::<IBTree<U256xU32xBool_AV>>,\n    bench_index_random::<IBTree<U256xU32xBool_Enc>>,\n    bench_index_random::<IBTree<U256xU32_AV>>,\n    bench_index_random::<IBTree<U256xU32_Enc>>,\n    bench_index_random::<IBTree<I32xI32xU64_AV>>,\n    bench_index_random::<IBTree<I32xI32xU64_Enc>>,\n);\ncriterion_main!(delete_table);\n"
  },
  {
    "path": "crates/bench/benches/special.rs",
    "content": "use criterion::async_executor::AsyncExecutor;\nuse criterion::{criterion_group, criterion_main, Criterion, SamplingMode};\nuse spacetimedb_bench::{\n    database::BenchDatabase,\n    schemas::{create_sequential, u32_u64_str, u32_u64_u64, u64_u64_u32, BenchTable, RandomTable},\n    spacetime_module::SpacetimeModule,\n};\nuse spacetimedb_lib::sats::{self, bsatn};\nuse spacetimedb_lib::{bsatn::ToBsatn as _, ProductValue};\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_table::page_pool::PagePool;\nuse spacetimedb_testing::modules::{Csharp, ModuleLanguage, Rust};\nuse std::sync::Arc;\nuse std::sync::OnceLock;\n\n#[cfg(target_env = \"msvc\")]\n#[global_allocator]\nstatic GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemallocator::Jemalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\n#[global_allocator]\nstatic GLOBAL: Jemalloc = Jemalloc;\n\nfn criterion_benchmark(c: &mut Criterion) {\n    serialize_benchmarks::<u32_u64_str>(c);\n    serialize_benchmarks::<u32_u64_u64>(c);\n    serialize_benchmarks::<u64_u64_u32>(c);\n\n    custom_benchmarks::<Rust>(c);\n    custom_benchmarks::<Csharp>(c);\n}\n\nfn custom_benchmarks<L: ModuleLanguage>(c: &mut Criterion) {\n    let db = SpacetimeModule::<L>::build(true).unwrap();\n\n    custom_module_benchmarks(&db, c);\n    custom_db_benchmarks(&db, c);\n}\n\nfn custom_module_benchmarks<L: ModuleLanguage>(m: &SpacetimeModule<L>, c: &mut Criterion) {\n    let mut group = c.benchmark_group(format!(\"special/{}\", SpacetimeModule::<L>::name()));\n\n    let args = sats::product![\"0\".repeat(65536).into_boxed_str()];\n    group.bench_function(\"large_arguments/64KiB\", |b| {\n        b.to_async(m)\n            .iter(|| async { m.module.call_reducer_binary(\"fn_with_1_args\", &args).await.unwrap() })\n    });\n\n    for n in [1u32, 100, 1000] {\n        let args = sats::product![n];\n        group.bench_function(format!(\"print_bulk/lines={n}\"), |b| {\n            b.to_async(m)\n                .iter(|| async { m.module.call_reducer_binary(\"print_many_things\", &args).await.unwrap() })\n        });\n    }\n}\n\nfn custom_db_benchmarks<L: ModuleLanguage>(m: &SpacetimeModule<L>, c: &mut Criterion) {\n    let mut group = c.benchmark_group(format!(\"special/db_game/{}\", L::NAME));\n    // This bench take long, so adjust for it\n    group.sample_size(10);\n    group.sampling_mode(SamplingMode::Flat);\n\n    let init_db: OnceLock<()> = OnceLock::new();\n    for n in [10, 100] {\n        let args = sats::product![n];\n        group.bench_function(format!(\"circles/load={n}\"), |b| {\n            // Initialize outside the benchmark so the db is seed once, to avoid to enlarge the db\n            init_db.get_or_init(|| {\n                m.block_on(async {\n                    m.module\n                        .call_reducer_binary(\"init_game_circles\", &sats::product![100])\n                        .await\n                        .unwrap()\n                });\n            });\n\n            b.to_async(m)\n                .iter(|| async { m.module.call_reducer_binary(\"run_game_circles\", &args).await.unwrap() })\n        });\n    }\n\n    let init_db: OnceLock<()> = OnceLock::new();\n    for n in [10, 100] {\n        let args = sats::product![n];\n        group.bench_function(format!(\"ia_loop/load={n}\"), |b| {\n            // Initialize outside the benchmark so the db is seed once, to avoid `unique` constraints violations\n            init_db.get_or_init(|| {\n                m.block_on(async {\n                    m.module\n                        .call_reducer_binary(\"init_game_ia_loop\", &sats::product![500])\n                        .await\n                        .unwrap();\n                })\n            });\n\n            b.to_async(m)\n                .iter(|| async { m.module.call_reducer_binary(\"run_game_ia_loop\", &args).await.unwrap() })\n        });\n    }\n}\n\nfn serialize_benchmarks<\n    T: BenchTable + RandomTable + for<'a> spacetimedb_lib::de::Deserialize<'a> + for<'a> serde::de::Deserialize<'a>,\n>(\n    c: &mut Criterion,\n) {\n    let name = T::name();\n    let count = 100;\n    let mut group = c.benchmark_group(format!(\"special/serde/serialize/{name}\"));\n    group.throughput(criterion::Throughput::Elements(count));\n\n    let data = create_sequential::<T>(0xdeadbeef, count as u32, 100);\n\n    group.bench_function(format!(\"product_value/count={count}\"), |b| {\n        b.iter_batched(\n            || data.clone(),\n            |data| data.into_iter().map(|row| row.into_product_value()).collect::<Vec<_>>(),\n            criterion::BatchSize::PerIteration,\n        );\n    });\n    // this measures serialization from a ProductValue, not directly (as in, from generated code in the Rust SDK.)\n    let data_pv = &data\n        .into_iter()\n        .map(|row| spacetimedb_lib::AlgebraicValue::Product(row.into_product_value()))\n        .collect::<ProductValue>();\n\n    group.bench_function(format!(\"bsatn/count={count}\"), |b| {\n        b.iter(|| sats::bsatn::to_vec(data_pv).unwrap());\n    });\n    group.bench_function(format!(\"json/count={count}\"), |b| {\n        b.iter(|| serde_json::to_string(data_pv).unwrap());\n    });\n\n    let mut table_schema = TableSchema::from_product_type(T::product_type());\n    table_schema.table_name = TableName::for_test(name);\n    let mut table = spacetimedb_table::table::Table::new(\n        Arc::new(table_schema),\n        spacetimedb_table::indexes::SquashedOffset::COMMITTED_STATE,\n    );\n    let pool = PagePool::new_for_test();\n    let mut blob_store = spacetimedb_table::blob_store::HashMapBlobStore::default();\n\n    let ptrs = data_pv\n        .elements\n        .iter()\n        .map(|row| {\n            table\n                .insert(&pool, &mut blob_store, row.as_product().unwrap())\n                .unwrap()\n                .1\n                .pointer()\n        })\n        .collect::<Vec<_>>();\n    let refs = ptrs\n        .into_iter()\n        .map(|ptr| table.get_row_ref(&blob_store, ptr).unwrap())\n        .collect::<Vec<_>>();\n    group.bench_function(format!(\"bflatn_to_bsatn_slow_path/count={count}\"), |b| {\n        b.iter(|| {\n            let mut buf = Vec::new();\n            for row_ref in &refs {\n                bsatn::to_writer(&mut buf, row_ref).unwrap();\n            }\n            buf\n        })\n    });\n    group.bench_function(format!(\"bflatn_to_bsatn_fast_path/count={count}\"), |b| {\n        b.iter(|| {\n            let mut buf = Vec::new();\n            for row_ref in &refs {\n                row_ref.to_bsatn_extend(&mut buf).unwrap();\n            }\n            buf\n        });\n    });\n\n    group.finish();\n\n    let mut group = c.benchmark_group(format!(\"special/serde/deserialize/{name}\"));\n    group.throughput(criterion::Throughput::Elements(count));\n\n    let data_bin = sats::bsatn::to_vec(&data_pv).unwrap();\n    let data_json = serde_json::to_string(&data_pv).unwrap();\n\n    group.bench_function(format!(\"bsatn/count={count}\"), |b| {\n        b.iter(|| bsatn::from_slice::<Vec<T>>(&data_bin).unwrap());\n    });\n    group.bench_function(format!(\"json/count={count}\"), |b| {\n        b.iter(|| serde_json::from_str::<Vec<T>>(&data_json).unwrap());\n    });\n    // TODO: deserialize benches (needs a typespace)\n}\n\ncriterion_group!(benches, criterion_benchmark);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/bench/benches/subscription.rs",
    "content": "use criterion::{black_box, criterion_group, criterion_main, Criterion};\nuse spacetimedb::client::consume_each_list::ConsumeEachBuffer;\nuse spacetimedb::db::relational_db::RelationalDB;\nuse spacetimedb::error::DBError;\nuse spacetimedb::identity::AuthCtx;\nuse spacetimedb::sql::ast::SchemaViewer;\nuse spacetimedb::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse spacetimedb::subscription::tx::DeltaTx;\nuse spacetimedb::subscription::{collect_table_update, TableUpdateType};\nuse spacetimedb_bench::database::BenchDatabase as _;\nuse spacetimedb_bench::spacetime_raw::SpacetimeRaw;\nuse spacetimedb_client_api_messages::websocket::v1::BsatnFormat;\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_execution::pipelined::PipelinedProject;\nuse spacetimedb_primitives::{col_list, TableId};\nuse spacetimedb_query::compile_subscription;\nuse spacetimedb_sats::{bsatn, product, AlgebraicType, AlgebraicValue};\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemallocator::Jemalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\n#[global_allocator]\nstatic GLOBAL: Jemalloc = Jemalloc;\n\nfn create_table_location(db: &RelationalDB) -> Result<TableId, DBError> {\n    let schema = &[\n        (\"entity_id\", AlgebraicType::U64),\n        (\"chunk_index\", AlgebraicType::U64),\n        (\"x\", AlgebraicType::I32),\n        (\"z\", AlgebraicType::I32),\n        (\"dimension\", AlgebraicType::U32),\n    ];\n    let indexes = &[0.into(), 1.into()];\n\n    // Is necessary to test for both single & multi-column indexes...\n    db.create_table_for_test_mix_indexes(\"location\", schema, indexes, col_list![2, 3, 4])\n}\n\nfn create_table_footprint(db: &RelationalDB) -> Result<TableId, DBError> {\n    let footprint = AlgebraicType::sum([\"A\", \"B\", \"C\", \"D\"].map(|n| (n, AlgebraicType::unit())));\n    let schema = &[\n        (\"entity_id\", AlgebraicType::U64),\n        (\"owner_entity_id\", AlgebraicType::U64),\n        (\"type\", footprint),\n    ];\n    let indexes = &[0.into(), 1.into()];\n    db.create_table_for_test(\"footprint\", schema, indexes)\n}\n\nfn eval(c: &mut Criterion) {\n    let raw = SpacetimeRaw::build(false).unwrap();\n\n    let lhs = create_table_footprint(&raw.db).unwrap();\n    let rhs = create_table_location(&raw.db).unwrap();\n\n    //TODO: Change this to `Workload::ForTest` once `#[cfg(bench)]` is stabilized.\n    let _ = raw\n        .db\n        .with_auto_commit(Workload::Internal, |tx| -> Result<(), DBError> {\n            // 1M rows\n            let mut scratch = Vec::new();\n            for entity_id in 0u64..1_000_000 {\n                let owner = entity_id % 1_000;\n                let footprint = AlgebraicValue::sum(entity_id as u8 % 4, AlgebraicValue::unit());\n                let row = product!(entity_id, owner, footprint);\n\n                scratch.clear();\n                bsatn::to_writer(&mut scratch, &row).unwrap();\n                let _ = raw.db.insert(tx, lhs, &scratch)?;\n            }\n            Ok(())\n        });\n\n    let _ = raw\n        .db\n        .with_auto_commit(Workload::Internal, |tx| -> Result<(), DBError> {\n            // 1000 chunks, 1200 rows per chunk = 1.2M rows\n            let mut scratch = Vec::new();\n            for chunk_index in 0u64..1_000 {\n                for i in 0u64..1200 {\n                    let entity_id = chunk_index * 1200 + i;\n                    let x = 0i32;\n                    let z = entity_id as i32;\n                    let dimension = 0u32;\n                    let row = product!(entity_id, chunk_index, x, z, dimension);\n\n                    scratch.clear();\n                    bsatn::to_writer(&mut scratch, &row).unwrap();\n                    let _ = raw.db.insert(tx, rhs, &scratch)?;\n                }\n            }\n            Ok(())\n        });\n\n    let entity_id = 1_200_000u64;\n    let chunk_index = 5u64;\n    let x = 0i32;\n    let z = 0i32;\n    let dimension = 0u32;\n\n    let footprint = AlgebraicValue::sum(1, AlgebraicValue::unit());\n    let owner = 6u64;\n\n    let _new_lhs_row = product!(entity_id, owner, footprint);\n    let _new_rhs_row = product!(entity_id, chunk_index, x, z, dimension);\n\n    let bsatn_rlb_pool = black_box(BsatnRowListBuilderPool::new());\n\n    // A benchmark runner for the subscription engine.\n    let bench_query = |c: &mut Criterion, name, sql| {\n        c.bench_function(name, |b| {\n            let tx = raw.db.begin_tx(Workload::Subscribe);\n            let auth = AuthCtx::for_testing();\n            let schema_viewer = &SchemaViewer::new(&tx, &auth);\n            let (plans, table_id, table_name, _) = compile_subscription(sql, schema_viewer, &auth).unwrap();\n            let plans = plans\n                .into_iter()\n                .map(|plan| plan.optimize(&auth).unwrap())\n                .map(PipelinedProject::from)\n                .collect::<Vec<_>>();\n            let tx = DeltaTx::from(&tx);\n\n            b.iter(|| {\n                let updates = black_box(collect_table_update::<BsatnFormat>(\n                    &plans,\n                    table_id,\n                    table_name.clone(),\n                    &tx,\n                    TableUpdateType::Subscribe,\n                    &bsatn_rlb_pool,\n                ));\n                if let Ok((updates, _)) = updates {\n                    updates.consume_each_list(&mut |buffer| bsatn_rlb_pool.try_put(buffer));\n                }\n            })\n        });\n    };\n\n    // Join 1M rows on the left with 12K rows on the right.\n    // Note, this should use an index join so as not to read the entire footprint table.\n    let semijoin = format!(\n        r#\"\n        select f.*\n        from footprint f join location l on f.entity_id = l.entity_id\n        where l.chunk_index = {chunk_index}\n        \"#\n    );\n\n    let index_scan_multi = \"select * from location WHERE x = 0 AND z = 10000 AND dimension = 0\";\n\n    bench_query(c, \"footprint-scan\", \"select * from footprint\");\n    bench_query(c, \"footprint-semijoin\", &semijoin);\n    bench_query(c, \"index-scan-multi\", index_scan_multi);\n}\n\ncriterion_group!(benches, eval);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/bench/callgrind-docker.sh",
    "content": "#!/bin/bash\n\n# script to enter iai dockerfile locally\n\nset -exo pipefail\n\nSCRIPT_DIR=\"$(dirname \"$(readlink -f \"$0\")\")\"\ncd \"$SCRIPT_DIR\"\ndocker build . --tag rust-iai-callgrind:latest \ndocker run --privileged -v \"$(realpath $PWD/../..):/projects/SpacetimeDB\" -w /projects/SpacetimeDB/crates/bench rust-iai-callgrind:latest cargo bench --bench callgrind"
  },
  {
    "path": "crates/bench/clippy.toml",
    "content": "# we use println in summarize.rs, don't complain about it\ndisallowed-macros = []\n"
  },
  {
    "path": "crates/bench/flamegraph.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [ \"$#\" -lt \"2\" ] ; then\n  echo \"Usage: $0 <benchmark-exe> <benchmark-filter> <svg-name>\"\n  echo \"E.g.: $0 generic stdb_raw/mem/insert_bulk/location/multi_index/load=0/count=100 result.svg\"\n  exit 1\nfi\n\ncd \"$(dirname \"$0\")\"\n\necho\necho \"Warning(jgilles): this script has not been tested since its last modification, sorry if it is broken.\"\necho\necho cargo bench --no-run\necho\n\ncargo bench --no-run\n\necho\necho cargo flamegraph --bench \"${1}\" --deterministic --notes \"${1};${2}\" -o \"${3}\" -- \"${2}\" --profile-time 10\necho\n\ncargo flamegraph --bench \"${1}\" --deterministic --notes \"${1};${2}\" -o \"${3}\" -- \"${2}\" --profile-time 10\n"
  },
  {
    "path": "crates/bench/hyper_cmp.py",
    "content": "# Mini-tool for comparing benchmark between PR or locally\nimport os\nimport json\nimport argparse\nimport subprocess\nimport shutil\n\n\ndef statDecoder(statDict):\n    return namedtuple('Stat', statDict.keys())(*statDict.values())\n\n\ndef clean(stat):\n    new = {}\n    for (k, v) in stat.items():\n        if k in [\"stddev\", \"command\", \"exit_codes\"]:\n            continue\n        if k == \"times\":\n            v = v[0]\n        if isinstance(v, float):\n            v = round(v, 2)\n        new[k] = v\n    return new\n\n\ndef larger(row: list):\n    return [len(str(x)) for x in row]\n\n\ndef bar_chart(data: list):\n    max_value = max(count for _, count in data.items())\n    increment = max_value / 25\n\n    longest_label_length = max(len(label) for label, _ in data.items())\n\n    for label, count in data.items():\n\n        # The ASCII block elements come in chunks of 8, so we work out how\n        # many fractions of 8 we need.\n        # https://en.wikipedia.org/wiki/Block_Elements\n        bar_chunks, remainder = divmod(int(count * 8 / increment), 8)\n\n        # First draw the full width chunks\n        bar = '█' * bar_chunks\n\n        # Then add the fractional part.  The Unicode code points for\n        # block elements are (8/8), (7/8), (6/8), ... , so we need to\n        # work backwards.\n        if remainder > 0:\n            bar += chr(ord('█') + (8 - remainder))\n\n        # If the bar is empty, add a left one-eighth block\n        bar = bar or '▏'\n\n        print(f'{label.rjust(longest_label_length)} ▏ {count:#4f} {bar}')\n\n\nclass Report:\n    def __init__(self, title: str, header: list, bar: dict, rows: dict):\n        self.title = title\n        size = larger(header)\n        for row in rows:\n            size = size + larger(row)\n        self.header = header\n        self.bar = bar\n        self.rows = rows\n        self.larger = max(size) + 2\n\n\nclass Stat:\n    def __init__(self, results):\n        self.spacetime = clean(results[0])\n        self.sqlite = clean(results[1])\n\n\ndef load_file(named: str):\n    data = open(named).read()\n    return Stat(json.loads(data)['results'])\n\n\ndef print_cell(cell: str, size: int, is_last: bool):\n    spaces = \" \" * (size - len(cell))\n    if is_last:\n        return \"| %s%s |\" % (cell, spaces)\n    else:\n        return \"| %s%s \" % (cell, spaces)\n\n\ndef print_row(row: list, size: int):\n    line = \"\"\n    for (pos, x) in enumerate(row):\n        line += print_cell(str(x), size, pos + 1 == len(row))\n    print(line)\n\n\ndef print_mkdown(report: Report):\n    print(\"###\", report.title)\n    print(\"\\n```bash\")\n    bar_chart(report.bar)\n    print(\"```\\n\")\n    print(\"*Smaller is better.*\")\n    print_row(report.header, report.larger)\n    print_row([\"-\" * report.larger for x in report.header], report.larger)\n\n    for row in report.rows:\n        print_row(row, report.larger)\n\n\ndef pick_winner(a: dict, b: dict, label_a: str, label_b: str):\n    if a[\"mean\"] > b[\"mean\"]:\n        winner = label_b\n        delta = a[\"times\"]\n    else:\n        if a[\"mean\"] == b[\"mean\"]:\n            winner = \"TIE\"\n            delta = a[\"times\"]\n        else:\n            winner = label_a\n            delta = b[\"times\"]\n    return winner, delta\n\n\n# Check Sqlite VS Spacetime\ndef cmp_bench(stat: Stat):\n    winner, delta = pick_winner(stat.spacetime, stat.sqlite, \"Spacetime\", \"Sqlite\")\n\n    header = [\"Stat\", \"Sqlite\", \"Spacetime\", \"Delta\"]\n\n    rows = []\n    for (k, v) in stat.sqlite.items():\n        rows.append([k, v, stat.spacetime[k], round(stat.spacetime[k] - v, 2)])\n\n    bar = dict(SpacetimeDB=stat.spacetime[\"mean\"], Sqlite=stat.sqlite[\"mean\"])\n    return Report(\"Comparing Sqlite VS Spacetime Winner: **%s**\" % winner, header, bar, rows)\n\n\n# Check the progress of Spacetime between branches / PR\ndef improvement_bench(old: Stat, new: Stat):\n    winner, delta = pick_winner(old.spacetime, new.spacetime, \"Old\", \"New\")\n\n    header = [\"Stat\", \"OLD\", \"NEW\", \"Delta\"]\n\n    rows = []\n    for (k, v) in old.spacetime.items():\n        rows.append([k, v, new.spacetime[k], round(v - new.spacetime[k], 2)])\n\n    bar = dict(Old=old.spacetime[\"mean\"], New=new.spacetime[\"mean\"])\n    return Report(\"Improvement of Spacetime. Winner: **%s**\" % winner, header, bar, rows)\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"bench\", choices=['versus', 'pr'], help=\"Select bench\")\n\n    args = vars(parser.parse_args())\n\n    # args = {\"bench\": \"pr\"}\n    cmd = \"./hyperfine.sh insert\"\n    if args[\"bench\"] == \"pr\":\n        subprocess.check_call('./pr_copy.sh \"%s\"' % cmd, shell=True)\n\n        subprocess.check_call(cmd, shell=True, timeout=60 * 5)\n        shutil.copyfile(\"out.json\", \"new.json\")\n\n        old = load_file(\"old.json\")\n        new = load_file(\"new.json\")\n        report = improvement_bench(old, new)\n    else:\n        subprocess.check_call(cmd, shell=True, timeout=60 * 5)\n\n        stat = load_file(\"out.json\")\n        report = cmp_bench(stat)\n\n    print_mkdown(report)\n"
  },
  {
    "path": "crates/bench/instruments.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [ \"$#\" -lt \"3\" ] ; then\n  echo \"Usage: $0 <template> <bench_executable> <bench_filter>\"\n  echo \"E.g.: $0 time generic stdb_raw/mem/insert_bulk/location/multi_index/load=0/count=100\n\"\n  exit 1\nfi\n\necho\necho cargo instruments -t \"${1}\" --bench \"${2}\" -- \"${3}\" --profile-time 10\necho\n\n# Only OSX: Run the benchmark in instruments.app\ncargo instruments -t \"${1}\" --bench \"${2}\" -- \"${3}\" --profile-time 10"
  },
  {
    "path": "crates/bench/src/database.rs",
    "content": "use spacetimedb_lib::AlgebraicValue;\nuse spacetimedb_primitives::ColId;\n\nuse crate::schemas::{BenchTable, IndexStrategy};\nuse crate::ResultBench;\n\n/// A database we can execute a standard benchmark suite against.\n/// Currently implemented for SQLite, raw Spacetime outside a module boundary\n/// (RelationalDB), and Spacetime through the module boundary.\n///\n/// Not all benchmarks have to go through this trait.\npub trait BenchDatabase: Sized {\n    fn name() -> String;\n\n    type TableId: Clone + 'static;\n\n    fn build(in_memory: bool) -> ResultBench<Self>\n    where\n        Self: Sized;\n\n    fn create_table<T: BenchTable>(&mut self, table_style: IndexStrategy) -> ResultBench<Self::TableId>;\n\n    /// Should not drop the table, only delete all the rows.\n    fn clear_table(&mut self, table_id: &Self::TableId) -> ResultBench<()>;\n\n    /// Count the number of rows in the table.\n    fn count_table(&mut self, table_id: &Self::TableId) -> ResultBench<u32>;\n\n    /// Perform an empty transaction.\n    fn empty_transaction(&mut self) -> ResultBench<()>;\n\n    /// Perform a transaction that commits many rows.\n    fn insert_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, rows: Vec<T>) -> ResultBench<()>;\n\n    /// Perform a transaction that updates many rows, without\n    /// sending updated rows across a serialization boundary.\n    /// (e.g. in a module, the updates are generated *inside* the reducer).\n    /// Update logic should be fast, e.g. wrapping integer increment.\n    /// Requires that the table was created with `IndexStrategy::Unique`\n    fn update_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, row_count: u32) -> ResultBench<()>;\n\n    /// Perform a transaction that iterates an entire database table.\n    /// Note: this can be non-generic because none of the implementations use the relevant generic argument.\n    fn iterate(&mut self, table_id: &Self::TableId) -> ResultBench<()>;\n\n    /// Filter the table on the specified column id for the specified value.\n    fn filter<T: BenchTable>(\n        &mut self,\n        table_id: &Self::TableId,\n        col_id: impl Into<ColId>,\n        value: AlgebraicValue,\n    ) -> ResultBench<()>;\n}\n"
  },
  {
    "path": "crates/bench/src/lib.rs",
    "content": "pub mod database;\npub mod schemas;\npub mod spacetime_module;\npub mod spacetime_raw;\npub mod sqlite;\n\npub type ResultBench<T> = Result<T, anyhow::Error>;\n\n#[cfg(test)]\nmod tests {\n    use crate::{\n        database::BenchDatabase,\n        schemas::{create_sequential, u32_u64_str, u32_u64_u64, BenchTable, IndexStrategy, RandomTable},\n        spacetime_module::SpacetimeModule,\n        spacetime_raw::SpacetimeRaw,\n        sqlite::SQLite,\n        ResultBench,\n    };\n    use serial_test::serial;\n    use spacetimedb_testing::modules::{Csharp, Rust};\n    use std::{io, path::Path, sync::Once};\n    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\n    static INIT: Once = Once::new();\n\n    fn prepare_tests() {\n        INIT.call_once(|| {\n            // logs. see SpacetimeDB\\crates\\core\\src\\startup.rs\n            let timer = tracing_subscriber::fmt::time();\n            let format = tracing_subscriber::fmt::format::Format::default()\n                .with_timer(timer)\n                .with_line_number(true)\n                .with_file(true)\n                .with_target(false)\n                .compact();\n            let fmt_layer = tracing_subscriber::fmt::Layer::default()\n                .event_format(format)\n                .with_writer(io::stdout);\n            let env_filter_layer = tracing_subscriber::EnvFilter::from_default_env();\n            tracing_subscriber::Registry::default()\n                .with(fmt_layer)\n                .with(env_filter_layer)\n                .init();\n\n            // Remove cached data from previous runs.\n            // This directory is only reused to speed up runs with Callgrind. In tests, it's fine to wipe it.\n            let mut bench_dot_spacetime = Path::new(env!(\"CARGO_MANIFEST_DIR\")).to_path_buf();\n            bench_dot_spacetime.push(\".spacetime\");\n            if std::fs::metadata(&bench_dot_spacetime).is_ok() {\n                std::fs::remove_dir_all(bench_dot_spacetime)\n                    .expect(\"failed to wipe Spacetimedb/crates/bench/.spacetime\");\n            }\n        });\n    }\n\n    fn basic_invariants<DB: BenchDatabase, T: BenchTable + RandomTable>(\n        index_strategy: IndexStrategy,\n        in_memory: bool,\n    ) -> ResultBench<()> {\n        prepare_tests();\n\n        let mut db = DB::build(in_memory)?;\n        let table_id = db.create_table::<T>(index_strategy)?;\n        assert_eq!(db.count_table(&table_id)?, 0, \"tables should begin empty\");\n\n        // Chosen arbitrarily.\n        let count = 37;\n\n        let sample_data = create_sequential::<T>(0xdeadbeef, count, 100);\n\n        db.insert_bulk(&table_id, sample_data.clone())?;\n        assert_eq!(db.count_table(&table_id)?, count, \"inserted rows should be inserted\");\n\n        db.clear_table(&table_id)?;\n        assert_eq!(\n            db.count_table(&table_id)?,\n            0,\n            \"clearing the table should clear the table\"\n        );\n\n        db.insert_bulk(&table_id, sample_data.clone())?;\n        assert_eq!(\n            db.count_table(&table_id)?,\n            count,\n            \"bulk inserted rows should be bulk inserted\"\n        );\n\n        if index_strategy == IndexStrategy::Unique0 {\n            db.update_bulk::<T>(&table_id, count)?;\n            assert_eq!(\n                db.count_table(&table_id)?,\n                count,\n                \"bulk updated rows should be bulk updated\"\n            );\n        }\n\n        db.clear_table(&table_id)?;\n        assert_eq!(\n            db.count_table(&table_id)?,\n            0,\n            \"clearing the table should clear the table\"\n        );\n        Ok(())\n    }\n\n    fn test_basic_invariants<DB: BenchDatabase>() -> ResultBench<()> {\n        basic_invariants::<DB, u32_u64_str>(IndexStrategy::Unique0, true)?;\n        basic_invariants::<DB, u32_u64_u64>(IndexStrategy::Unique0, true)?;\n        basic_invariants::<DB, u32_u64_str>(IndexStrategy::BTreeEachColumn, true)?;\n        basic_invariants::<DB, u32_u64_u64>(IndexStrategy::BTreeEachColumn, true)?;\n        Ok(())\n    }\n\n    #[test]\n    fn test_basic_invariants_sqlite() -> ResultBench<()> {\n        test_basic_invariants::<SQLite>()\n    }\n\n    #[test]\n    fn test_basic_invariants_spacetime_raw() -> ResultBench<()> {\n        test_basic_invariants::<SpacetimeRaw>()\n    }\n\n    // note: there can only be one #[test] invoking spacetime module stuff.\n    // #[test]s run concurrently and they fight over lockfiles.\n    // so, run the sub-tests here in sequence.\n\n    #[test]\n    #[serial]\n    fn test_basic_invariants_spacetime_module_rust() -> ResultBench<()> {\n        test_basic_invariants::<SpacetimeModule<Rust>>()\n    }\n\n    #[test]\n    #[serial]\n    fn test_basic_invariants_spacetime_module_csharp() -> ResultBench<()> {\n        test_basic_invariants::<SpacetimeModule<Csharp>>()\n    }\n}\n"
  },
  {
    "path": "crates/bench/src/schemas.rs",
    "content": "#![allow(non_camel_case_types)]\n\nuse serde::Deserialize;\nuse spacetimedb_lib::de::Deserialize as SatsDeserializer;\nuse spacetimedb_lib::sats;\nuse spacetimedb_schema::table_name::TableName;\nuse std::fmt::Debug;\nuse std::hash::Hash;\n\npub const BENCH_PKEY_INDEX: u32 = 0;\n\n// the following piece of code must remain synced with `modules/benchmarks/src/synthetic.rs`\n// These are the schemas used for these database tables outside of the benchmark module.\n// It needs to match the schemas used inside the benchmark.\n\n// ---------- SYNCED CODE ----------\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, SatsDeserializer)]\npub struct u32_u64_str {\n    // column 0\n    id: u32,\n    // column 1\n    age: u64,\n    // column 2\n    name: Box<str>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, SatsDeserializer)]\npub struct u32_u64_u64 {\n    // column 0\n    id: u32,\n    // column 1\n    x: u64,\n    // column 2\n    y: u64,\n}\n// ---------- END SYNCED CODE ----------\n\n/// This is a duplicate of [`u32_u64_u64`] with the fields shuffled to minimize interior padding,\n/// used to compare the effects of interior padding on BFLATN -> BSATN serialization.\n///\n/// This type *should not* be used for any benchmarks except `special::serialize_benchmarks`,\n/// as it doesn't have proper implementations in modules or Sqlite.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, SatsDeserializer)]\npub struct u64_u64_u32 {\n    x: u64,\n    y: u64,\n    id: u32,\n}\n\n/// A schema used in the benchmarks.\n/// Schemas should convert to a `ProductType` / `ProductValue` in a canonical way.\n/// We require that, when converted, to a ProductValue:\n/// - column 0 is a u32 (used in many places)\n/// - and column 1 is a u64 (used in `update_bulk`).\npub trait BenchTable: Debug + Clone + PartialEq + Eq + Hash {\n    /// PascalCase name. This is used to name tables.\n    fn name() -> &'static str;\n\n    fn product_type() -> sats::ProductType;\n    /// MUST match product_type.\n    fn into_product_value(self) -> sats::ProductValue;\n\n    /// This should be a tuple like (u32, String, u32).\n    /// Can be inserted with a prepared statement.\n    /// Order must be the same as that used in `product_type`.\n    type SqliteParams: rusqlite::Params;\n    fn into_sqlite_params(self) -> Self::SqliteParams;\n}\n\nimpl BenchTable for u32_u64_str {\n    fn name() -> &'static str {\n        \"u32_u64_str\"\n    }\n\n    fn product_type() -> sats::ProductType {\n        [\n            (\"id\", sats::AlgebraicType::U32),\n            (\"age\", sats::AlgebraicType::U64),\n            (\"name\", sats::AlgebraicType::String),\n        ]\n        .into()\n    }\n    fn into_product_value(self) -> sats::ProductValue {\n        sats::product![self.id, self.age, self.name,]\n    }\n\n    type SqliteParams = (u32, u64, Box<str>);\n    fn into_sqlite_params(self) -> Self::SqliteParams {\n        (self.id, self.age, self.name)\n    }\n}\n\nimpl BenchTable for u32_u64_u64 {\n    fn name() -> &'static str {\n        \"u32_u64_u64\"\n    }\n\n    fn product_type() -> sats::ProductType {\n        [\n            (\"id\", sats::AlgebraicType::U32),\n            (\"x\", sats::AlgebraicType::U64),\n            (\"y\", sats::AlgebraicType::U64),\n        ]\n        .into()\n    }\n    fn into_product_value(self) -> sats::ProductValue {\n        sats::product![self.id, self.x, self.y]\n    }\n\n    type SqliteParams = (u32, u64, u64);\n    fn into_sqlite_params(self) -> Self::SqliteParams {\n        (self.id, self.x, self.y)\n    }\n}\n\nimpl BenchTable for u64_u64_u32 {\n    fn name() -> &'static str {\n        \"u64_u64_u32\"\n    }\n    fn product_type() -> sats::ProductType {\n        [\n            (\"x\", sats::AlgebraicType::U64),\n            (\"y\", sats::AlgebraicType::U64),\n            (\"id\", sats::AlgebraicType::U32),\n        ]\n        .into()\n    }\n    fn into_product_value(self) -> sats::ProductValue {\n        sats::product![self.x, self.y, self.id]\n    }\n\n    type SqliteParams = ();\n    fn into_sqlite_params(self) -> Self::SqliteParams {\n        unimplemented!()\n    }\n}\n\n/// How we configure the indexes for a table used in benchmarks.\n/// TODO(jgilles): this should be more general, but for that we'll need to dynamically generate the modules...\n#[derive(PartialEq, Copy, Clone, Debug, Deserialize)]\npub enum IndexStrategy {\n    /// Unique \"id\" field at index 0\n    #[serde(alias = \"unique_0\")]\n    Unique0,\n    /// No unique field or indexes\n    #[serde(alias = \"no_index\")]\n    NoIndex,\n    /// Non-unique index on all fields\n    #[serde(alias = \"btree_each_column\")]\n    BTreeEachColumn,\n}\n\nimpl IndexStrategy {\n    pub fn name(&self) -> &'static str {\n        match self {\n            IndexStrategy::Unique0 => \"unique_0\",\n            IndexStrategy::NoIndex => \"no_index\",\n            IndexStrategy::BTreeEachColumn => \"btree_each_column\",\n        }\n    }\n}\n\npub fn table_name<T: BenchTable>(style: IndexStrategy) -> TableName {\n    let prefix = style.name();\n    let name = T::name();\n\n    TableName::for_test(&format!(\"{prefix}_{name}\"))\n}\n\n// ---------- data synthesis ----------\n#[derive(Clone)]\npub struct XorShiftLite(pub u64);\nimpl XorShiftLite {\n    fn r#gen(&mut self) -> u64 {\n        let old = self.0;\n        self.0 ^= self.0 << 13;\n        self.0 ^= self.0 >> 7;\n        self.0 ^= self.0 << 17;\n        old\n    }\n}\n\npub trait RandomTable {\n    /// Generate an instance of this table.\n    ///\n    /// `buckets` counts the number of buckets non-unique attributes are intended to fall into.\n    /// e.g. the number of possible names a person can have, or the number of x positions a location can have.\n    ///\n    /// Then in the filter benchmarks, `mean_result_count = table_size / buckets`.\n    ///\n    /// Currently the same number of buckets is used for all attributes.\n    fn r#gen(id: u32, rng: &mut XorShiftLite, buckets: u64) -> Self;\n}\n\nimpl RandomTable for u32_u64_str {\n    fn r#gen(id: u32, rng: &mut XorShiftLite, buckets: u64) -> Self {\n        let name = nth_name(rng.r#gen() % buckets).into();\n        let age = rng.r#gen() % buckets;\n        u32_u64_str { id, name, age }\n    }\n}\n\nimpl RandomTable for u32_u64_u64 {\n    fn r#gen(id: u32, rng: &mut XorShiftLite, buckets: u64) -> Self {\n        let x = rng.r#gen() % buckets;\n        let y = rng.r#gen() % buckets;\n        u32_u64_u64 { id, x, y }\n    }\n}\n\nimpl RandomTable for u64_u64_u32 {\n    fn r#gen(id: u32, rng: &mut XorShiftLite, buckets: u64) -> Self {\n        let x = rng.r#gen() % buckets;\n        let y = rng.r#gen() % buckets;\n        u64_u64_u32 { x, y, id }\n    }\n}\n\npub fn create_sequential<T: RandomTable>(seed: u64, count: u32, buckets: u64) -> Vec<T> {\n    let mut rng = XorShiftLite(seed);\n    (0..count).map(|id| T::r#gen(id, &mut rng, buckets)).collect()\n}\n\n/// Create a table whose first `identical` rows are identical except for their `id` column.\n/// The remainder of the rows in the table are random. The total size of the table is `total`.\n/// Intended for filter benchmarks.\npub fn create_partly_identical<T: RandomTable>(seed: u64, identical: u64, total: u64) -> Vec<T> {\n    // large to avoid duplicates.\n    // technically, overlaps with the identical part of the table can still occur;\n    // in a given row+column, this happens with probability 1 / 2^32, which is negligible.\n    // We use u32::max because sqlite sometimes chokes on large u64s.\n    let buckets = u32::MAX as u64;\n\n    let mut rng = XorShiftLite(seed);\n    let mut result = Vec::with_capacity(total as usize);\n    let mut id = 0;\n    for _ in 0..identical {\n        // clone to preserve rng state\n        let mut rng_ = rng.clone();\n        result.push(T::r#gen(id as u32, &mut rng_, buckets));\n        id += 1;\n    }\n    // advance rng\n    drop(T::r#gen(id as u32, &mut rng, buckets));\n    for _ in identical..total {\n        result.push(T::r#gen(id as u32, &mut rng, buckets));\n        id += 1;\n    }\n    result\n}\n\n/// May contain repeated IDs!\npub fn create_random<T: RandomTable>(seed: u64, count: u32, buckets: u64) -> Vec<T> {\n    let mut rng = XorShiftLite(seed);\n    (0..count)\n        .map(|_| {\n            let id = (rng.r#gen() % (u32::MAX as u64)) as u32;\n            T::r#gen(id, &mut rng, buckets)\n        })\n        .collect()\n}\n\nconst FIRST_NAMES: [&str; 32] = [\n    \"Anthony\",\n    \"Tony\",\n    \"Antonio\",\n    \"Barbara\",\n    \"Charles\",\n    \"Daniel\",\n    \"Danyl\",\n    \"Darkholder Fleshbane\",\n    \"Dan\",\n    \"David\",\n    \"Droog\",\n    \"Elizabeth\",\n    \"Liz\",\n    \"James\",\n    \"Jim\",\n    \"Jimmy\",\n    \"Jennifer\",\n    \"Jen\",\n    \"John\",\n    \"Linda\",\n    \"Lindy\",\n    \"Margaret\",\n    \"Marge\",\n    \"Mary\",\n    \"Michael\",\n    \"Nutmeg\",\n    \"Richard\",\n    \"Dick\",\n    \"Robert\",\n    \"Thomas\",\n    \"Tom\",\n    \"Zanzibar\",\n];\n\nconst LAST_NAMES: [&str; 32] = [\n    \"Anderson\",\n    \"Brown\",\n    \"Carter\",\n    \"Cook\",\n    \"Davis\",\n    \"Frogson\",\n    \"Garcia\",\n    \"Green\",\n    \"Hall\",\n    \"Harris\",\n    \"Hill\",\n    \"Hunch\",\n    \"Jackson\",\n    \"Johnson\",\n    \"Jones\",\n    \"Lewis\",\n    \"Martin\",\n    \"Miller\",\n    \"Moore\",\n    \"Morgan\",\n    \"Robinson\",\n    \"Sanchez\",\n    \"Smith\",\n    \"Taylor\",\n    \"The Destroyer\",\n    \"Thomas\",\n    \"Thompson\",\n    \"Walker\",\n    \"White\",\n    \"Williams\",\n    \"Wilson\",\n    \"Wood\",\n];\n\n/// An injection (input-total one-to-one relation) from u64s to short strings.\n/// Provides some variation in length.\npub fn nth_name(n: u64) -> String {\n    let n = n as usize;\n    let first = n % FIRST_NAMES.len();\n    let last = (n / FIRST_NAMES.len()) % LAST_NAMES.len();\n    let remaining = n / (FIRST_NAMES.len() * LAST_NAMES.len());\n    assert_eq!(\n        n,\n        first + last * FIRST_NAMES.len() + remaining * FIRST_NAMES.len() * LAST_NAMES.len()\n    );\n\n    let first = FIRST_NAMES[first];\n    let last = LAST_NAMES[last];\n    format!(\"{last}, {first} [{remaining}]\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{create_partly_identical, nth_name, XorShiftLite};\n\n    #[test]\n    fn test_nth_name() {\n        let mut rng = XorShiftLite(0xdeadbeef);\n        for n in 0..1000 {\n            let name = nth_name(n);\n            assert_eq!(name, nth_name(n), \"name gen deterministic\");\n            if n == 0 {\n                continue;\n            }\n            // sample some earlier names to make sure we haven't overlapped\n            for _ in 0..30 {\n                let prev = rng.r#gen() % n;\n                assert!(\n                    name != nth_name(prev),\n                    \"names should not repeat, but {}->{} and {}->{}\",\n                    n,\n                    name,\n                    prev,\n                    nth_name(prev)\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_partly_identical() {\n        use crate::schemas::u32_u64_str;\n\n        let identical = 100;\n        let total = 2000;\n\n        let data = create_partly_identical::<u32_u64_str>(0xdeadbeef, identical, total);\n        let p1 = data[0].clone();\n\n        for item in data.iter().take(identical as usize).skip(1) {\n            assert_ne!(p1.id, item.id, \"identical part should still have distinct ids\");\n            assert_eq!(p1.name, item.name, \"names should be identical\");\n            assert_eq!(p1.age, item.age, \"ages should be identical\");\n        }\n        for item in data.iter().take(total as usize).skip(identical as usize) {\n            assert_ne!(p1.id, item.id, \"identical part should still have distinct ids\");\n            assert_ne!(p1.name, item.name, \"names should not be identical\");\n            assert_ne!(p1.age, item.age, \"ages should not be identical\");\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bench/src/spacetime_module.rs",
    "content": "use std::{marker::PhantomData, path::Path};\n\nuse convert_case::{Case, Casing};\nuse spacetimedb::db::{Config, Storage};\nuse spacetimedb_lib::{\n    sats::{product, ArrayValue},\n    AlgebraicValue,\n};\nuse spacetimedb_paths::RootDir;\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_testing::modules::{start_runtime, LoggerRecord, ModuleHandle, ModuleLanguage};\nuse tokio::runtime::Runtime;\n\nuse crate::{\n    database::BenchDatabase,\n    schemas::{table_name, BenchTable},\n    ResultBench,\n};\nuse criterion::async_executor::AsyncExecutor;\n\n/// A benchmark backend that invokes a spacetime module.\n///\n/// This is tightly tied to the file `modules/benchmarks/src/lib.rs`;\n/// all of the implementations of `BenchDatabase` methods just invoke reducers\n/// in that module.\n///\n/// See the doc comment there for information on the formatting expected for\n/// table and reducer names.\npub struct SpacetimeModule<L> {\n    // Module must be dropped BEFORE runtime otherwise there is a deadlock!\n    // In Rust, struct fields are guaranteed to drop in declaration order, so don't reorder this field.\n    pub module: ModuleHandle,\n    runtime: Runtime,\n    lang: PhantomData<L>,\n}\n\n// Note: we use block_on for the methods here. It adds about 70ns of overhead.\n// This isn't currently a problem. Overhead to call an empty reducer is currently 20_000 ns.\n\nimpl<L> AsyncExecutor for &SpacetimeModule<L> {\n    fn block_on<T>(&self, future: impl std::future::Future<Output = T>) -> T {\n        self.runtime.block_on(future)\n    }\n}\n\n// It's easier to do it this way because async traits are a mess.\nimpl<L: ModuleLanguage> BenchDatabase for SpacetimeModule<L> {\n    fn name() -> String {\n        format!(\"stdb_module/{}\", L::NAME)\n    }\n\n    type TableId = TableId;\n\n    fn build(in_memory: bool) -> ResultBench<Self>\n    where\n        Self: Sized,\n    {\n        let runtime = start_runtime();\n        let config = Config {\n            storage: if in_memory { Storage::Memory } else { Storage::Disk },\n            page_pool_max_size: None,\n        };\n\n        let module = runtime.block_on(async {\n            // We keep a saved database at \"crates/bench/.spacetime\".\n            // This is mainly used for caching wasmtime native artifacts.\n            // It's fine that we're constructing this path ad-hoc, as it's just\n            // a path location for tests, not part of our stable directory structure.\n            let path = RootDir(Path::new(env!(\"CARGO_MANIFEST_DIR\")).join(\".spacetime\"));\n            L::get_module().load_module(config, Some(&path)).await\n        });\n\n        let module_info = module.client.module().info;\n        for table in module_info.module_def.tables() {\n            log::trace!(\"SPACETIME_MODULE: LOADED TABLE: {table:?}\");\n        }\n        for reducer in module_info.module_def.reducers() {\n            log::trace!(\"SPACETIME_MODULE: LOADED REDUCER: {reducer:?}\");\n        }\n        Ok(SpacetimeModule {\n            runtime,\n            module,\n            lang: PhantomData,\n        })\n    }\n\n    fn create_table<T: BenchTable>(\n        &mut self,\n        table_style: crate::schemas::IndexStrategy,\n    ) -> ResultBench<Self::TableId> {\n        // Noop. All tables are built into the \"benchmarks\" module.\n        // The module's default CaseConversionPolicy is SnakeCase, which\n        // inserts underscores at letter-digit boundaries (e.g. u32 -> u_32).\n        // We must match that here so reducer/table lookups succeed.\n        let raw = table_name::<T>(table_style);\n        let converted = raw.as_ref().to_case(Case::Snake);\n        Ok(TableId {\n            pascal_case: TableName::for_test(&converted),\n            snake_case: TableName::for_test(&converted),\n        })\n    }\n\n    fn clear_table(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        let SpacetimeModule { runtime, module, .. } = self;\n        runtime.block_on(async move {\n            // FIXME: this doesn't work. delete is unimplemented!!\n            /*\n            let name = format!(\"clear_table_{}\", table_id.snake_case);\n            module.call_reducer_binary(&name, ProductValue::new(&[])).await?;\n            */\n            // workaround for now\n            module.client.module().clear_table(&table_id.pascal_case)?;\n            Ok(())\n        })\n    }\n\n    // Implemented by calling a reducer that logs, then looking for the resulting\n    // message in the log.\n    // This implementation will not work if other people are concurrently interacting with our module.\n    fn count_table(&mut self, table_id: &Self::TableId) -> ResultBench<u32> {\n        let SpacetimeModule { runtime, module, .. } = self;\n\n        let count = runtime.block_on(async move {\n            let name = format!(\"count_{}\", table_id.snake_case);\n            module.call_reducer_binary(&name, &[].into()).await?;\n            let logs = module.read_log(Some(1)).await;\n            let message = serde_json::from_str::<LoggerRecord>(&logs)?;\n            if !message.message.starts_with(\"COUNT: \") {\n                anyhow::bail!(\"Improper count message format: {:?}\", message.message);\n            }\n\n            let count = message.message[\"COUNT: \".len()..].parse::<u32>()?;\n            Ok(count)\n        })?;\n        Ok(count)\n    }\n\n    fn empty_transaction(&mut self) -> ResultBench<()> {\n        let SpacetimeModule { runtime, module, .. } = self;\n\n        runtime.block_on(async move {\n            module.call_reducer_binary(\"empty\", &[].into()).await?;\n            Ok(())\n        })\n    }\n\n    fn insert_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, rows: Vec<T>) -> ResultBench<()> {\n        let rows = rows.into_iter().map(|row| row.into_product_value()).collect();\n        let args = product![ArrayValue::Product(rows)];\n        let SpacetimeModule { runtime, module, .. } = self;\n        let reducer_name = format!(\"insert_bulk_{}\", table_id.snake_case);\n\n        runtime.block_on(async move {\n            module.call_reducer_binary(&reducer_name, &args).await?;\n            Ok(())\n        })\n    }\n\n    fn update_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, row_count: u32) -> ResultBench<()> {\n        let args = product![row_count];\n        let SpacetimeModule { runtime, module, .. } = self;\n        let reducer_name = format!(\"update_bulk_{}\", table_id.snake_case);\n\n        runtime.block_on(async move {\n            module.call_reducer_binary(&reducer_name, &args).await?;\n            Ok(())\n        })\n    }\n\n    fn iterate(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        let SpacetimeModule { runtime, module, .. } = self;\n        let reducer_name = format!(\"iterate_{}\", table_id.snake_case);\n\n        runtime.block_on(async move {\n            module.call_reducer_binary(&reducer_name, &[].into()).await?;\n            Ok(())\n        })\n    }\n\n    fn filter<T: BenchTable>(\n        &mut self,\n        table_id: &Self::TableId,\n        col_id: impl Into<ColId>,\n        value: AlgebraicValue,\n    ) -> ResultBench<()> {\n        let SpacetimeModule { runtime, module, .. } = self;\n\n        let product_type = T::product_type();\n        let column_name = product_type.elements[col_id.into().idx()].name.as_ref().unwrap();\n        let reducer_name = format!(\"filter_{}_by_{}\", table_id.snake_case, column_name);\n\n        runtime.block_on(async move {\n            module.call_reducer_binary(&reducer_name, &[value].into()).await?;\n            Ok(())\n        })\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct TableId {\n    pascal_case: TableName,\n    snake_case: TableName,\n}\n"
  },
  {
    "path": "crates/bench/src/spacetime_raw.rs",
    "content": "use crate::{\n    database::BenchDatabase,\n    schemas::{table_name, BenchTable, IndexStrategy},\n    ResultBench,\n};\nuse spacetimedb::db::relational_db::{tests_utils::TestDB, RelationalDB};\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_primitives::{ColId, IndexId, TableId};\nuse spacetimedb_sats::{bsatn, AlgebraicValue};\nuse spacetimedb_schema::{\n    def::{BTreeAlgorithm, IndexAlgorithm},\n    schema::{IndexSchema, TableSchema},\n};\nuse std::hint::black_box;\nuse tempdir::TempDir;\n\npub type DbResult = (RelationalDB, TempDir, u32);\n\npub struct SpacetimeRaw {\n    pub db: TestDB,\n}\n\nimpl BenchDatabase for SpacetimeRaw {\n    fn name() -> String {\n        \"stdb_raw\".to_owned()\n    }\n    type TableId = TableId;\n\n    fn build(in_memory: bool) -> ResultBench<Self>\n    where\n        Self: Sized,\n    {\n        let db = if in_memory {\n            TestDB::in_memory()\n        } else {\n            TestDB::durable()\n        }?;\n        Ok(Self { db })\n    }\n\n    fn create_table<T: BenchTable>(&mut self, index_strategy: IndexStrategy) -> ResultBench<Self::TableId> {\n        let name = table_name::<T>(index_strategy);\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            let mut table_schema = TableSchema::from_product_type(T::product_type());\n            table_schema.table_name = name.clone();\n            let table_id = self.db.create_table(tx, table_schema)?;\n            self.db.rename_table(tx, table_id, name)?;\n            match index_strategy {\n                IndexStrategy::Unique0 => {\n                    self.db.create_index(\n                        tx,\n                        IndexSchema {\n                            index_id: IndexId::SENTINEL,\n                            table_id,\n                            index_name: \"id\".into(),\n                            index_algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {\n                                columns: ColId(0).into(),\n                            }),\n                            alias: None,\n                        },\n                        true,\n                    )?;\n                }\n                IndexStrategy::NoIndex => (),\n                IndexStrategy::BTreeEachColumn => {\n                    for (i, column) in T::product_type().elements.iter().enumerate() {\n                        self.db.create_index(\n                            tx,\n                            IndexSchema {\n                                index_id: IndexId::SENTINEL,\n                                table_id,\n                                index_name: column.name.clone().unwrap(),\n                                index_algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {\n                                    columns: ColId(i as _).into(),\n                                }),\n                                alias: None,\n                            },\n                            false,\n                        )?;\n                    }\n                }\n            }\n\n            Ok(table_id)\n        })\n    }\n\n    fn clear_table(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            self.db.clear_table(tx, *table_id)?;\n            Ok(())\n        })\n    }\n\n    fn count_table(&mut self, table_id: &Self::TableId) -> ResultBench<u32> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            Ok(self.db.iter_mut(tx, *table_id)?.map(|_| 1u32).sum())\n        })\n    }\n\n    fn empty_transaction(&mut self) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |_tx| Ok(()))\n    }\n\n    fn insert_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, rows: Vec<T>) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            let mut scratch = Vec::new();\n            for row in rows {\n                scratch.clear();\n                bsatn::to_writer(&mut scratch, &row.into_product_value()).unwrap();\n                self.db.insert(tx, *table_id, &scratch)?;\n            }\n            Ok(())\n        })\n    }\n\n    fn update_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, row_count: u32) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            let rows = self\n                .db\n                .iter_mut(tx, *table_id)?\n                .take(row_count as usize)\n                .map(|row| row.to_product_value())\n                .collect::<Vec<_>>();\n\n            assert_eq!(rows.len(), row_count as usize, \"not enough rows found for update_bulk!\");\n            let mut scratch = Vec::new();\n            for mut row in rows {\n                // It would likely be faster to collect a vector of IDs and delete + insert them all at once,\n                // but this implementation is closer to how `update` works in modules.\n                // (update_by_{field} -> spacetimedb::query::update_by_field -> (delete_by_col_eq; insert))\n                let id = self\n                    .db\n                    .iter_by_col_eq_mut(tx, *table_id, 0, &row.elements[0])?\n                    .next()\n                    .expect(\"failed to find row during update!\")\n                    .pointer();\n\n                assert_eq!(\n                    self.db.delete(tx, *table_id, [id]),\n                    1,\n                    \"failed to delete row during update!\"\n                );\n\n                // relies on column 1 being a u64, which is guaranteed by BenchTable\n                if let AlgebraicValue::U64(i) = row.elements[1] {\n                    row.elements[1] = AlgebraicValue::U64(i + 1);\n                } else {\n                    panic!(\"column 1 is not a u64!\");\n                }\n\n                scratch.clear();\n                bsatn::to_writer(&mut scratch, &row).unwrap();\n                self.db.insert(tx, *table_id, &scratch)?;\n            }\n            Ok(())\n        })\n    }\n\n    fn iterate(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            for row in self.db.iter_mut(tx, *table_id)? {\n                black_box(row);\n            }\n            Ok(())\n        })\n    }\n\n    fn filter<T: BenchTable>(\n        &mut self,\n        table_id: &Self::TableId,\n        col_id: impl Into<ColId>,\n        value: AlgebraicValue,\n    ) -> ResultBench<()> {\n        self.db.with_auto_commit(Workload::Internal, |tx| {\n            for row in self.db.iter_by_col_eq_mut(tx, *table_id, col_id, &value)? {\n                black_box(row);\n            }\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "crates/bench/src/sqlite.rs",
    "content": "use crate::{\n    database::BenchDatabase,\n    schemas::{table_name, BenchTable, IndexStrategy},\n    ResultBench,\n};\nuse lazy_static::lazy_static;\nuse rusqlite::Connection;\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_lib::sats::{AlgebraicType, AlgebraicValue, ProductType};\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_schema::table_name::TableName;\nuse std::{\n    fmt::Write,\n    hint::black_box,\n    sync::{Arc, RwLock},\n};\nuse tempdir::TempDir;\n\n/// SQLite benchmark harness.\npub struct SQLite {\n    db: Connection,\n    /// We keep this alive to prevent the temp dir from being deleted.\n    _temp_dir: TempDir,\n}\n\nimpl BenchDatabase for SQLite {\n    fn name() -> String {\n        \"sqlite\".to_owned()\n    }\n\n    fn build(in_memory: bool) -> ResultBench<Self>\n    where\n        Self: Sized,\n    {\n        let temp_dir = TempDir::new(\"sqlite_test\")?;\n        let db = if in_memory {\n            Connection::open_in_memory()?\n        } else {\n            Connection::open(temp_dir.path().join(\"test.db\"))?\n        };\n        // For sqlite benchmarks we should set synchronous to off which more\n        // closely aligns with wal_fsync=false in stdb.\n        db.execute_batch(\"PRAGMA journal_mode = WAL; PRAGMA synchronous = off;\")?;\n\n        Ok(SQLite {\n            db,\n            _temp_dir: temp_dir,\n        })\n    }\n\n    type TableId = TableName;\n\n    /// We derive the SQLite schema from the AlgebraicType of the table.\n    fn create_table<T: BenchTable>(\n        &mut self,\n        index_strategy: crate::schemas::IndexStrategy,\n    ) -> ResultBench<Self::TableId> {\n        let mut statement = String::new();\n        let table_name = table_name::<T>(index_strategy);\n        write!(&mut statement, \"CREATE TABLE {table_name} (\")?;\n        for (i, column) in T::product_type().elements.iter().enumerate() {\n            let column_name = column.name.clone().unwrap();\n            let type_ = match column.algebraic_type {\n                AlgebraicType::U32 | AlgebraicType::U64 => \"INTEGER\",\n                AlgebraicType::String => \"TEXT\",\n                _ => unimplemented!(),\n            };\n            let extra = if index_strategy == IndexStrategy::Unique0 && i == 0 {\n                \" PRIMARY KEY\"\n            } else {\n                \"\"\n            };\n            let comma = if i == 0 { \"\" } else { \", \" };\n            write!(&mut statement, \"{comma}{column_name} {type_}{extra}\")?;\n        }\n        writeln!(&mut statement, \");\")?;\n\n        if index_strategy == IndexStrategy::BTreeEachColumn {\n            for column in T::product_type().elements.iter() {\n                let column_name = column.name.clone().unwrap();\n\n                writeln!(\n                    &mut statement,\n                    \"CREATE INDEX index_{table_name}_{column_name} ON {table_name}({column_name});\"\n                )?;\n            }\n        }\n\n        log::info!(\"SQLITE: `{statement}`\");\n        self.db.execute_batch(&statement)?;\n\n        Ok(table_name)\n    }\n\n    fn clear_table(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        self.db.execute_batch(&format!(\"DELETE FROM {table_id};\"))?;\n        Ok(())\n    }\n\n    fn count_table(&mut self, table_id: &Self::TableId) -> ResultBench<u32> {\n        let rows = self\n            .db\n            .query_row(&format!(\"SELECT COUNT(*) FROM {table_id}\"), (), |row| row.get(0))?;\n        Ok(rows)\n    }\n\n    fn empty_transaction(&mut self) -> ResultBench<()> {\n        let mut begin = self.db.prepare_cached(BEGIN_TRANSACTION)?;\n        let mut commit = self.db.prepare_cached(COMMIT_TRANSACTION)?;\n\n        begin.execute(())?;\n        commit.execute(())?;\n        Ok(())\n    }\n\n    fn insert_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, rows: Vec<T>) -> ResultBench<()> {\n        let statement = memo_query(BenchName::InsertBulk, table_id, || {\n            insert_template(table_id, T::product_type())\n        });\n\n        let mut begin: rusqlite::CachedStatement<'_> = self.db.prepare_cached(BEGIN_TRANSACTION)?;\n        let mut stmt = self.db.prepare_cached(&statement)?;\n        let mut commit = self.db.prepare_cached(COMMIT_TRANSACTION)?;\n\n        begin.execute(())?;\n        for row in rows {\n            stmt.execute(row.into_sqlite_params())?;\n        }\n        commit.execute(())?;\n\n        Ok(())\n    }\n\n    fn update_bulk<T: BenchTable>(&mut self, table_id: &Self::TableId, row_count: u32) -> ResultBench<()> {\n        let mut product = T::product_type();\n        let id_column = product.elements[0].name.take().unwrap();\n        let update_column = product.elements[1].name.take().unwrap();\n        // this relies on IDs having been generated in order...\n        let statement =\n            format!(\"UPDATE {table_id} SET {update_column} = {update_column} + 1 WHERE {id_column} < {row_count}\");\n        let mut begin = self.db.prepare_cached(BEGIN_TRANSACTION)?;\n        let mut stmt = self.db.prepare_cached(&statement)?;\n        let mut commit = self.db.prepare_cached(COMMIT_TRANSACTION)?;\n\n        begin.execute(())?;\n        stmt.execute(())?;\n        commit.execute(())?;\n\n        Ok(())\n    }\n\n    fn iterate(&mut self, table_id: &Self::TableId) -> ResultBench<()> {\n        let statement = format!(\"SELECT * FROM {table_id}\");\n        let mut begin = self.db.prepare_cached(BEGIN_TRANSACTION)?;\n        let mut stmt = self.db.prepare_cached(&statement)?;\n        let mut commit = self.db.prepare_cached(COMMIT_TRANSACTION)?;\n        begin.execute(())?;\n        let iter = stmt.query_map((), |row| {\n            black_box(row);\n            Ok(())\n        })?;\n        for _ in iter {}\n\n        commit.execute(())?;\n\n        Ok(())\n    }\n\n    fn filter<T: BenchTable>(\n        &mut self,\n        table_id: &Self::TableId,\n        col_id: impl Into<ColId>,\n        value: AlgebraicValue,\n    ) -> ResultBench<()> {\n        let statement = memo_query(BenchName::Filter, table_id, || {\n            let column = T::product_type().elements[col_id.into().idx()].name.clone().unwrap();\n            format!(\"SELECT * FROM {table_id} WHERE {column} = ?\")\n        });\n\n        let mut begin = self.db.prepare_cached(BEGIN_TRANSACTION)?;\n        let mut stmt = self.db.prepare_cached(&statement)?;\n        let mut commit = self.db.prepare_cached(COMMIT_TRANSACTION)?;\n\n        begin.execute(())?;\n        match value {\n            AlgebraicValue::String(value) => {\n                for _ in stmt.query_map((&*value,), |row| {\n                    black_box(row);\n                    Ok(())\n                })? {}\n            }\n            AlgebraicValue::U32(value) => {\n                for _ in stmt.query_map((value,), |row| {\n                    black_box(row);\n                    Ok(())\n                })? {}\n            }\n            AlgebraicValue::U64(value) => {\n                for _ in stmt.query_map((value,), |row| {\n                    black_box(row);\n                    Ok(())\n                })? {}\n            }\n            _ => unimplemented!(),\n        }\n\n        commit.execute(())?;\n        Ok(())\n    }\n}\n\n/// Note: The rusqlite transaction API just invokes these statements,\n/// but it doesn't cache them, which significantly penalizes performance.\n/// We use prepare_cache to let sqlite go as fast as possible.\nconst BEGIN_TRANSACTION: &str = \"BEGIN DEFERRED\";\nconst COMMIT_TRANSACTION: &str = \"COMMIT\";\n\n#[derive(PartialEq, Eq, Hash, Clone, Copy)]\nenum BenchName {\n    InsertBulk,\n    Filter,\n}\n\n#[inline(never)]\n/// Reduce latency of query formatting, for queries that are complicated to build.\nfn memo_query<F: FnOnce() -> String>(bench_name: BenchName, table_id: &str, generate_query: F) -> Arc<str> {\n    // fast path\n    let queries = QUERIES.read().unwrap();\n\n    if let Some(bench_queries) = queries.get(&bench_name)\n        && let Some(query) = bench_queries.get(table_id)\n    {\n        return query.clone();\n    }\n\n    // slow path\n    drop(queries);\n    let mut queries = QUERIES.write().unwrap();\n\n    let bench_queries = if let Some(bench_queries) = queries.get_mut(&bench_name) {\n        bench_queries\n    } else {\n        queries.insert(bench_name, HashMap::default());\n        queries.get_mut(&bench_name).unwrap()\n    };\n\n    if let Some(query) = bench_queries.get(table_id) {\n        query.clone()\n    } else {\n        let query = generate_query();\n        bench_queries.insert(table_id.to_string(), (&query[..]).into());\n        bench_queries[table_id].clone()\n    }\n}\n\nlazy_static! {\n    // bench_name -> table_id -> query.\n    // Double hashmap is necessary because of tuple dereferencing problems.\n    static ref QUERIES: RwLock<HashMap<BenchName, HashMap<String, Arc<str>>>> =\n        RwLock::default();\n}\n\n#[inline(never)]\nfn insert_template(table_id: &str, product_type: ProductType) -> String {\n    let mut columns = String::new();\n    let mut args = String::new();\n\n    for (i, elt) in product_type.elements.iter().enumerate() {\n        let comma = if i == 0 { \"\" } else { \", \" };\n\n        let name = elt.name().unwrap();\n        write!(&mut columns, \"{comma}{name}\").unwrap();\n\n        let sqlite_arg_id = i + 1;\n        write!(&mut args, \"{comma}?{sqlite_arg_id}\").unwrap();\n    }\n\n    format!(\"INSERT INTO {table_id}({columns}) VALUES ({args})\")\n}\n"
  },
  {
    "path": "crates/bindings/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Easy support for interacting between SpacetimeDB and Rust.\"\nrust-version.workspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n[lib]\nname = \"spacetimedb\"   # The name of the target.\npath = \"src/lib.rs\"    # The source file of the target.\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n[features]\ndefault = [\"rand\"]\nrand = [\"rand08\"]\nrand08 = [\"dep:rand08\", \"dep:getrandom02\"]\nunstable = [\"spacetimedb-bindings-sys/unstable\"]\n\n[dependencies]\nspacetimedb-bindings-sys.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-bindings-macro.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-query-builder.workspace = true\n\nanyhow.workspace = true\nbytemuck.workspace = true\nbytes.workspace = true\nderive_more.workspace = true\nhttp.workspace = true\nlog.workspace = true\nscoped-tls.workspace = true\n\nrand08 = { workspace = true, optional = true }\n# we depend on getrandom and enable the `custom` feature, so that\n# if someone tries to use rand's ThreadRng, it will fail to link\n# because no one defined __getrandom_custom\ngetrandom02 = { workspace = true, optional = true, features = [\"custom\"] }\nserde_json.workspace = true\n\n[dev-dependencies]\ninsta.workspace = true\ntrybuild.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/bindings/README.md",
    "content": "# SpacetimeDB Rust Module Library\n\n<!-- n.b. This file is used as the top-level library documentation in `src/lib.rs`.\n          Some of the links in this file are not resolved when previewing on GitHub,\n          but *are* resolved when compiled by Rustdoc.\n\n          To run the doctests, run the `./bindings-doctests.sh` script.\n          This works around some issues related to doctests and wasm, but it requires nightly,\n          so it isn't currently run on CI.\n\n          When you add a doc-tested code sample, make sure to copy the boilerplate around\n          some other code sample, the:\n          ```no_run\n          # #[cfg(target_arch = \"wasm32\")] mod demo {\n          // code\n          # }\n          ```\n          This makes sure your samples don't break when tested on non-wasm;\n          but you need to use `./bindings-doctests.sh` to actually test them.\n-->\n\n[SpacetimeDB](https://spacetimedb.com/) allows using the Rust language to write server-side applications called **modules**. Modules, which run inside a relational database, have direct access to database tables, and expose public functions called **reducers** that can be invoked over the network. Clients connect directly to the database to read data.\n\n```text\n    Client Application                          SpacetimeDB\n┌───────────────────────┐                ┌───────────────────────┐\n│                       │                │                       │\n│  ┌─────────────────┐  │    SQL Query   │  ┌─────────────────┐  │\n│  │ Subscribed Data │<─────────────────────│    Database     │  │\n│  └─────────────────┘  │                │  └─────────────────┘  │\n│           │           │                │           ^           │\n│           │           │                │           │           │\n│           v           │                │           v           │\n│  +─────────────────┐  │ call_reducer() │  ┌─────────────────┐  │\n│  │   Client Code   │─────────────────────>│   Module Code   │  │\n│  └─────────────────┘  │                │  └─────────────────┘  │\n│                       │                │                       │\n└───────────────────────┘                └───────────────────────┘\n```\n\nRust modules are written with the the Rust Module Library (this crate). They are built using [cargo](https://doc.rust-lang.org/cargo/) and deployed using the [`spacetime` CLI tool](https://spacetimedb.com/install). Rust modules can import any Rust [crate](https://crates.io/) that supports being compiled to WebAssembly.\n\n(Note: Rust can also be used to write **clients** of SpacetimeDB databases, but this requires using a different library, the SpacetimeDB Rust Client SDK. See the documentation on [clients] for more information.)\n\nThis reference assumes you are familiar with the basics of Rust. If you aren't, check out Rust's [excellent documentation](https://www.rust-lang.org/learn). For a guided introduction to Rust Modules, see the [Rust Module Quickstart](https://spacetimedb.com/docs/modules/rust/quickstart).\n\n## Overview\n\nSpacetimeDB modules have two ways to interact with the outside world: tables and reducers.\n\n- [Tables](#tables) store data and optionally make it readable by [clients]. \n\n- [Reducers](#reducers) are functions that modify data and can be invoked by [clients] over the network. They can read and write data in tables, and write to a private debug log.\n\nThese are the only ways for a SpacetimeDB module to interact with the outside world. Calling functions from `std::net` or `std::fs` inside a reducer will result in runtime errors.\n\nDeclaring tables and reducers is straightforward:\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(accessor = player)]\npub struct Player {\n    id: u32,\n    name: String\n}\n\n#[reducer]\nfn add_person(ctx: &ReducerContext, id: u32, name: String) {\n    log::debug!(\"Inserting {name} with id {id}\");\n    ctx.db.player().insert(Player { id, name });\n}\n# }\n```\n\n\nNote that reducers don't return data directly; they can only modify the database. Clients connect directly to the database and use SQL to query [public](#public-and-private-tables) tables. Clients can also subscribe to a set of rows using SQL queries and receive streaming updates whenever any of those rows change.\n\nTables and reducers in Rust modules can use any type that implements the [`SpacetimeType`] trait.\n\n<!-- TODO: link to client subscriptions / client one-off queries respectively. -->\n\n## Setup\n\nTo create a Rust module, install the [`spacetime` CLI tool](https://spacetimedb.com/install) in your preferred shell. Navigate to your work directory and run the following command:\n\n```text\nspacetime init --lang rust --project-path my-project-directory my-project\n```\n\nThis creates a Cargo project in `my-project-directory` with the following `Cargo.toml`:\n\n```text\n[package]\nname = \"spacetime-module\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = \"1.0.0\"\nlog = \"0.4\"\n```\n<!-- TODO: update `spacetimedb` version there. -->\n\nThis is a standard `Cargo.toml`, with the exception of the line `crate-type = [\"cdylib\"]`.\nThis line is important: it allows the project to be compiled to a WebAssembly module. \n\nThe project's `lib.rs` will contain the following skeleton:\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(_ctx: &ReducerContext) {\n    // Called when the module is initially published\n}\n\n#[spacetimedb::reducer(client_connected)]\npub fn identity_connected(_ctx: &ReducerContext) {\n    // Called everytime a new client connects\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn identity_disconnected(_ctx: &ReducerContext) {\n    // Called everytime a client disconnects\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n# }\n```\n\nThis skeleton declares a [table](#tables), some [reducers](#reducers), and some [lifecycle reducers](#lifecycle-reducers).\n\nTo compile the project, run the following command:\n\n```text\nspacetime build\n```\n\nSpacetimeDB requires a WebAssembly-compatible Rust toolchain. If the `spacetime` cli finds a compatible version of [`rustup`](https://rustup.rs/) that it can run, it will automatically install the `wasm32-unknown-unknown` target and use it to build your application. This can also be done manually using the command:\n\n```text\nrustup target add wasm32-unknown-unknown\n```\n\nIf you are managing your Rust installation in some other way, you will need to install the `wasm32-unknown-unknown` target yourself.\n\nTo build your application and upload it to the public SpacetimeDB network, run:\n\n```text\nspacetime login\n```\n\nAnd then:\n\n```text\nspacetime publish [MY_DATABASE_NAME]\n```\n\nFor example:\n\n```text\nspacetime publish silly_demo_app\n```\n\nWhen you publish your module, a database named `silly_demo_app` will be created with the requested tables, and the module will be installed inside it.\n\nThe output of `spacetime publish` will end with a line:\n```text\nCreated new database with name: <name>, identity: <hex string>\n```\n\nThis name is the human-readable name of the created database, and the hex string is its [`Identity`]. These distinguish the created database from the other databases running on the SpacetimeDB network.  They are used when administering the application, for example using the [`spacetime logs <DATABASE_NAME>`](#the-log-crate) command. You should probably write the database name down in a text file so that you can remember it.\n\nAfter modifying your project, you can run:\n\n`spacetime publish <DATABASE_NAME>`\n\nto update the module attached to your database. Note that SpacetimeDB tries to [automatically migrate](#automatic-migrations) your database schema whenever you run `spacetime publish`.\n\nYou can also generate code for clients of your module using the `spacetime generate` command. See the [client SDK documentation] for more information.\n\n## How it works\n\nUnder the hood, SpacetimeDB modules are WebAssembly modules that import a [specific WebAssembly ABI](https://spacetimedb.com/docs/webassembly-abi) and export a small number of special functions. This is automatically configured when you add the `spacetime` crate as a dependency of your application.\n\nThe SpacetimeDB host is an application that hosts SpacetimeDB databases. [Its source code is available](https://github.com/clockworklabs/SpacetimeDB) under [the Business Source License with an Additional Use Grant](https://github.com/clockworklabs/SpacetimeDB/blob/master/LICENSE.txt). You can run your own host, or you can upload your module to the public SpacetimeDB network. <!-- TODO: want a link to some dashboard for the public network. --> The network will create a database for you and install your module in it to serve client requests.\n\n#### In More Detail: Publishing a Module\n\nThe `spacetime publish [DATABASE_IDENTITY]` command compiles a module and uploads it to a SpacetimeDB host. After this:\n- The host finds the database with the requested `DATABASE_IDENTITY`.\n  - (Or creates a fresh database and identity, if no identity was provided).\n- The host loads the new module and inspects its requested database schema. If there are changes to the schema, the host tries perform an [automatic migration](#automatic-migrations). If the migration fails, publishing fails.\n- The host terminates the old module attached to the database.\n- The host installs the new module into the database. It begins running the module's [lifecycle reducers](#lifecycle-reducers) and [scheduled reducers](#scheduled-reducers), starting with the [`#[init]` reducer](macro@crate::reducer#scheduled-reducers).\n- The host begins allowing clients to call the module's reducers.\n\nFrom the perspective of clients, this process is seamless. Open connections are maintained and subscriptions continue functioning. [Automatic migrations](#automatic-migrations) forbid most table changes except for adding new tables, so client code does not need to be recompiled.\nHowever:\n- Clients may witness a brief interruption in the execution of scheduled reducers (for example, game loops.)\n- New versions of a module may remove or change reducers that were previously present. Client code calling those reducers will receive runtime errors.\n\n\n## Tables\n\nTables are declared using the [`#[table(accessor = table_name)]` macro](macro@crate::table).\n\nThis macro is applied to a Rust struct with named fields. All of the fields of the table must implement [`SpacetimeType`].\n\nThe resulting type is used to store rows of the table. It is normal struct type. Row values are not special -- operations on row types do not, by themselves, modify the table. Instead, a [`ReducerContext`](#reducercontext) is needed to get a handle to the table.\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{table, reducer, ReducerContext, Table, UniqueColumn};\n\n/// A `Person` is a row of the table `person`.\n#[table(accessor = person, public)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    #[index(btree)]\n    name: String,\n}\n\n// `Person` is a normal Rust struct type.\n// Operations on a `Person` do not, by themselves, do anything.\n// The following function does not interact with the database at all.\nfn do_nothing() {\n    // Creating a `Person` DOES NOT modify the database.\n    let mut person = Person { id: 0, name: \"Joe Average\".to_string() };\n    // Updating a `Person` DOES NOT modify the database.\n    person.name = \"Joanna Average\".to_string();\n    // Dropping a `Person` DOES NOT modify the database.\n    drop(person);\n}\n\n// To interact with the database, you need a `ReducerContext`,\n// which is provided as the first parameter of any reducer.\n#[reducer]\nfn do_something(ctx: &ReducerContext) {\n    // `ctx.db.{table_name}()` gets a handle to a database table.\n    let person: &person__TableHandle = ctx.db.person();\n\n    // The following inserts a row into the table:\n    let mut example_person = person.insert(Person { id: 0, name: \"Joe Average\".to_string() });\n\n    // `person` is a COPY of the row stored in the database.\n    // If we update it:\n    example_person.name = \"Joanna Average\".to_string();\n    // Our copy is now updated, but the database's copy is UNCHANGED.\n    // To push our change through, we can call `UniqueColumn::update()`:\n    example_person = person.id().update(example_person);\n    // Now the database and our copy are in sync again.\n    \n    // We can also delete the row in the database using `UniqueColumn::delete()`.\n    person.id().delete(&example_person.id);\n}\n# }\n```\n\n(See [reducers](#reducers) for more information on declaring reducers.)\n\nThis library generates a custom API for each table, depending on the table's name and structure.\n\nAll tables support getting a handle implementing the [`Table`] trait from a [`ReducerContext`], using:\n\n```text\nctx.db.{table_name}()\n```\n\nFor example,\n\n```no_build\nctx.db.person()\n```\n\nThe [`Table`] trait provides:\n- [`Table::insert`]\n- [`Table::try_insert`]\n- [`Table::delete`]\n- [`Table::iter`]\n- [`Table::count`]\n\nTables' [constraints](#unique-and-primary-key-columns) and [indexes](#indexes) generate additional accessors.\n\n<!-- TODO: outline generated methods here, split up by annotations required. -->\n\n#### Public and Private Tables\n\nBy default, tables are considered **private**. This means that they are only readable by the database owner and by reducers. Reducers run inside the database, so clients cannot see private tables at all.\n\nUsing the [`#[table(accessor = table_name, public)]`](macro@crate::table) flag makes a table public. **Public** tables are readable by all clients. They can still only be modified by reducers. \n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::table;\n\n// The `enemies` table can be read by all connected clients.\n#[table(accessor = enemy, public)]\npub struct Enemy {\n    /* ... */\n}\n\n// The `loot_items` table is invisible to clients, but not to reducers.\n#[table(accessor = loot_item)]\npub struct LootItem {\n    /* ... */\n}\n# }\n```\n\n(Note that, when run by the module owner, the `spacetime sql <SQL_QUERY>` command can also read private tables. This is for debugging convenience. Only the module owner can see these tables. This is determined by the `Identity` stored by the `spacetime login` command. Run `spacetime login show` to print your current logged-in `Identity`.)\n\nTo learn how to subscribe to a public table, see the [client SDK documentation](https://spacetimedb.com/docs/sdks). <!-- TODO: more specific link. -->\n\n#### Unique and Primary Key Columns\n\nColumns of a table (that is, fields of a [`#[table]`](macro@crate::table) struct) can be annotated with [`#[unique]`](macro@crate::table#unique) or [`#[primary_key]`](macro@crate::table#primary_key). Multiple columns can be `#[unique]`, but only one can be `#[primary_key]`. For example:\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::table;\n\ntype SSN = String;\ntype Email = String;\n\n#[table(accessor = citizen)]\npub struct Citizen {\n    #[primary_key]\n    id: u64,\n    #[unique]\n    ssn: SSN,\n    #[unique]\n    email: Email,\n    name: String,\n}\n# }\n```\n\nEvery row in the table `Person` must have unique entries in the `id`, `ssn`, and `email` columns. Attempting to insert multiple `Person`s with the same `id`, `ssn`, or `email` will fail. (Either via panic, with [`Table::insert`], or via a `Result::Err`, with [`Table::try_insert`].)\n\nAny `#[unique]` or `#[primary_key]` column supports getting a [`UniqueColumn`] from a [`ReducerContext`] using:\n\n```text\nctx.db.{table}().{unique_column}()\n```\n\nFor example, \n\n```no_build\nctx.db.person().ssn()\n```\n\n[`UniqueColumn`] provides:\n- [`UniqueColumn::find`]\n- [`UniqueColumn::delete`]\n- [`UniqueColumn::update`]\n<!-- TODO: \"current limitations\" try_update -->\n\nNotice that updating a row is only possible if a row has a unique column -- there is no `update` method in the base [`Table`] trait. SpacetimeDB has no notion of rows having an \"identity\" aside from their unique / primary keys.\n\nThe `#[primary_key]` annotation implies `#[unique]` annotation, but avails additional methods in the [client]-side SDKs.\n\nIt is not currently possible to mark a group of fields as collectively unique.\n\nFiltering on unique columns is only supported for a limited number of types.\n\n#### Auto-inc columns\n\nColumns can be marked [`#[auto_inc]`](macro@crate::table#auto_inc). This can only be used on integer types (`i32`, `u8`, etc.)\n\nWhen inserting into a table with an `#[auto_inc]` column, if the annotated column is set to zero (`0`), the database will automatically overwrite that zero with an atomically increasing value.\n\n[`Table::insert`] and [`Table::try_insert`] return rows with `#[auto_inc]` columns set to the values that were actually written into the database.\n\n**Note**: The `auto_inc` number generator is not transactional. See the [SEQUENCE] section for more details.\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(accessor = example)]\nstruct Example {\n    #[auto_inc]\n    field: u32\n}\n\n#[reducer]\nfn insert_auto_inc_example(ctx: &ReducerContext) {\n    for i in 1..=10 {\n        // These will have distinct, unique values\n        // at rest in the database, since they\n        // are inserted with the sentinel value 0.\n        let actual = ctx.db.example().insert(Example { field: 0 });\n        assert!(actual.field != 0);\n    }\n}\n# }\n```\n\n`auto_inc` is often combined with `unique` or `primary_key` to automatically assign unique integer identifiers to rows.\n\n#### Default Values\n\nColumns can be marked with [`#[default(value)]`](macro@crate::table#default) to specify a default value. This is primarily used for [automatic migrations](#automatic-migrations) when adding new columns to existing tables.\n\nWhen you republish a module with a new column that has a default value, existing rows are automatically populated with that default. New columns must be added at the **end** of the table definition.\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::table;\n\n#[table(accessor = player)]\nstruct Player {\n    id: u64,\n    name: String,\n    // New columns added with defaults for migration\n    #[default(0)]\n    score: u32,\n    #[default(true)]\n    is_active: bool,\n}\n# }\n```\n\nThe `#[default(value)]` attribute accepts a const-evaluable Rust expression. The value must be usable in a `const` context, which means you cannot use methods like `.to_string()` for `String` defaults. Only primitive types, enums, and other const-constructible types can have defaults.\n\n**Constraints**: `#[default]` cannot be combined with `#[primary_key]`, `#[unique]`, or `#[auto_inc]`, as these constraints require the database to manage column values.\n\n#### Indexes\n\nSpacetimeDB supports both single- and multi-column [B-Tree](https://en.wikipedia.org/wiki/B-tree) indexes.\n\nIndexes are declared using the syntax:\n\n[`#[table(..., index(accessor = my_index, btree(columns = [a, b, c]))]`](macro@crate::table#index).\n\nFor example:\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::table;\n\n#[table(accessor = paper, index(accessor = url_and_country, btree(columns = [url, country])))]\nstruct Paper {\n    url: String,\n    country: String,\n    venue: String\n} \n\n# }\n```\n\nMultiple indexes can be declared, separated by commas.\n\nSingle-column indexes can also be declared using the\n\n[`#[index(btree)]`](macro@crate::table#indexbtree)\n\ncolumn attribute.\n\nFor example:\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::table;\n\n#[table(accessor = paper)]\nstruct Paper {\n    url: String,\n    country: String,\n    #[index(btree)]\n    venue: String\n} \n# }\n```\n\n\nAny index supports getting a [`RangedIndex`] using [`ctx`](crate::ReducerContext)`.db.{table}().{index}()`. For example, `ctx.db.person().name()`.\n    \n[`RangedIndex`] provides:\n - [`RangedIndex::filter`]\n - [`RangedIndex::delete`]\n\nOnly types which implement [`FilterableValue`](trait@crate::FilterableValue) may be used as index keys.\n\n## Reducers\n\nReducers are declared using the [`#[reducer]` macro](macro@crate::reducer).\n\n`#[reducer]` is always applied to top level Rust functions. Arguments of reducers must implement [`SpacetimeType`]. Reducers can either return nothing, or return a `Result<(), E>`, where `E` implements [`std::fmt::Display`].\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{reducer, ReducerContext};\nuse std::fmt;\n\n#[reducer]\nfn give_player_item(\n    ctx: &ReducerContext,\n    player_id: u64,\n    item_id: u64\n) -> Result<(), String> {\n    /* ... */\n    # Ok(())\n}\n# }\n```\n\nEvery reducer runs inside a [database transaction](https://en.wikipedia.org/wiki/Database_transaction). <!-- TODO: specific transaction level guarantees. --> This means that reducers will not observe the effects of other reducers modifying the database while they run. If a reducer fails, all of its changes to the database will automatically be rolled back. Reducers can fail by [panicking](::std::panic!) or by returning an `Err`.\n\n#### The `ReducerContext` Type\n\nReducers have access to a special [`ReducerContext`] parameter. This parameter allows reading and writing the database attached to a module. It also provides some additional functionality, like generating random numbers and scheduling future operations.\n\n[`ReducerContext`] provides access to the database tables via [the `.db` field](ReducerContext#structfield.db). The [`#[table]`](macro@crate::table) macro generates traits that add accessor methods to this field.\n\n<!-- TODO: this seems to work sometimes, but not always... Sometimes the links to downstream trait implementations aren't generated for some reason. Maybe it only works in the same cargo workspace?\n\nYes, that's the case. Dammit. TODO: check if this changes someday.\n\nTo see all of the available methods on `ctx.db`, run `cargo doc` in your module's directory, and navigate to the `spacetimedb::Local` struct in the generated documentation. This will be at the path:\n- `[your_project_directory]/target/doc/spacetimedb/struct.Local.html`\n-->\n\n#### The `log` crate\n\nSpacetimeDB Rust modules have built-in support for the [log crate](log). All modules automatically install a suitable logger when they are first loaded by SpacetimeDB. (At time of writing, this happens [here](https://github.com/clockworklabs/SpacetimeDB/blob/e9e287b8aab638ba6e8bf9c5d41d632db041029c/crates/bindings/src/logger.rs)). Log macros can be used anywhere in module code, and log outputs of a running module can be inspected using the `spacetime logs` command:\n\n```text\nspacetime logs <DATABASE_IDENTITY>\n```\n\n<!-- TODO: we seem to have removed `println`, is that still hidden somewhere? -->\n\n#### Lifecycle Reducers\n\nA small group of reducers are called at set points in the module lifecycle. These are used to initialize\nthe database and respond to client connections. See [Lifecycle Reducers](macro@crate::reducer#lifecycle-reducers).\n\n#### Scheduled Reducers\n\nReducers can schedule other reducers to run asynchronously. This allows calling the reducers at a particular time, or at repeating intervals. This can be used to implement timers, game loops, and maintenance tasks. See [Scheduled Reducers](macro@crate::reducer#scheduled-reducers).\n\n## Views\n\nViews are declared using the [`#[view]` macro](macro@crate::view).\n\nViews are read‑only functions that compute and return results from your tables.\n`#[view]` is always applied to top level Rust functions.\nViews must be declared as `public`, with an explicit `name`, and do not accept user parameters beyond the context type.\nViews can return either `Option<T>` or `Vec<T>` where `T` can be a table type or any product type in general.\n\n```no_run\n# #[cfg(target_arch = \"wasm32\")] mod demo {\nuse spacetimedb::{table, view, ViewContext, AnonymousViewContext, SpacetimeType};\nuse spacetimedb_lib::Identity;\n\n#[table(accessor = player)]\nstruct Player {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    #[unique]\n    identity: Identity,\n    name: String,\n}\n\n#[table(accessor = player_level)]\nstruct PlayerLevel {\n    #[unique]\n    player_id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[derive(SpacetimeType)]\nstruct PlayerAndLevel {\n    id: u64,\n    identity: Identity,\n    name: String,\n    level: u64,\n}\n\n// At-most-one row: return Option<T>\n#[view(accessor = my_player, public)]\nfn my_player(ctx: &ViewContext) -> Option<Player> {\n    ctx.db.player().identity().find(ctx.sender())\n}\n\n// Multiple rows: return Vec<T>\n#[view(accessor = players_for_level, public)]\nfn players_for_level(ctx: &AnonymousViewContext) -> Vec<PlayerAndLevel> {\n    ctx.db\n        .player_level()\n        .level()\n        .filter(2u64)\n        .map(|player| {\n            ctx.db\n                .player()\n                .id()\n                .find(player.player_id)\n                .map(|p| PlayerAndLevel {\n                    id: p.id,\n                    identity: p.identity,\n                    name: p.name,\n                    level: player.level,\n                })\n        })\n        .collect()\n}\n# }\n```\n\nViews can be queried and subscribed to like normal tables and are updated atomically in realtime.\n\n```sql\nSELECT * FROM my_player;\nSELECT * FROM players_for_level;\n```\n\n#### `ViewContext` and `AnonymousViewContext`\n\nViews must take a `&ViewContext` or an `&AnonymousViewContext` as their first parameter.\nLike reducers, these types allow reading from the database attached to a module via the `.db` field.\nHowever, unlike reducers, they do not allow writing to the database.\nBoth types expose read-only portions of the `Table` and `Index` accessors generated by the [`#[table]` macro](macro@crate::table).\n\n## Automatic migrations\n\nWhen you `spacetime publish` a module that has already been published using `spacetime publish <DATABASE_NAME_OR_IDENTITY>`,\nSpacetimeDB attempts to automatically migrate your existing database to the new schema. (The \"schema\" is just the collection\nof tables and reducers you've declared in your code, together with the types they depend on.) This form of migration is limited and only supports a few kinds of changes.\nOn the plus side, automatic migrations usually don't break clients. The situations that may break clients are documented below.\n\nThe following changes are always allowed and never breaking:\n\n<!-- TODO: everything here should be smoke-tested. -->\n\n- ✅ **Adding tables**. Non-updated clients will not be able to see the new tables.\n- ✅ **Adding indexes**.\n- ✅ **Adding or removing `#[auto_inc]` annotations.**\n- ✅ **Changing tables from private to public**.\n- ✅ **Adding reducers**.\n- ✅ **Removing `#[unique]`  annotations.**\n\nThe following changes are allowed, but may break clients:\n\n- ⚠️ **Adding new columns to the end of a table with a default value**. The new column must be added at the end of the table definition and must have a default value specified. Non-updated clients will not be aware of the new column.\n- ⚠️ **Changing or removing reducers**. Clients that attempt to call the old version of a changed reducer will receive runtime errors.\n- ⚠️ **Changing tables from public to private**. Clients that are subscribed to a newly-private table will receive runtime errors.\n- ⚠️ **Removing `#[primary_key]` annotations**. Non-updated clients will still use the old `#[primary_key]` as a unique key in their local cache, which can result in non-deterministic behavior when updates are received.\n- ⚠️ **Removing indexes**. This is only breaking in some situations.\n  The specific problem is subscription queries <!-- TODO: clientside link --> involving semijoins, such as:\n    ```sql\n    SELECT Employee.*\n    FROM Employee JOIN Dept\n    ON Employee.DeptName = Dept.DeptName\n    )\n    ```\n    For performance reasons, SpacetimeDB will only allow this kind of subscription query if there are indexes on `Employee.DeptName` and `Dept.DeptName`. Removing either of these indexes will invalidate this subscription query, resulting in client-side runtime errors.\n\nThe following changes are forbidden without a manual migration:\n\n- ❌ **Removing tables**.\n- ❌ **Removing or modifying existing columns**. This includes changing the type, renaming, or reordering columns.\n- ❌ **Adding columns without a default value**. New columns must have a default value so existing rows can be populated.\n- ❌ **Adding columns in the middle of a table**. New columns must be added at the end of the table definition.\n- ❌ **Changing whether a table is used for [scheduling](#scheduled-reducers).** <!-- TODO: update this if we ever actually implement it... -->\n- ❌ **Adding `#[unique]` or `#[primary_key]` constraints.** This could result in existing tables being in an invalid state.\n\nCurrently, manual migration support is limited. The `spacetime publish --delete-data <DATABASE_IDENTITY>` command can be used to **COMPLETELY DELETE** and reinitialize your database, but naturally it should be used with EXTREME CAUTION.\n\n[macro library]: https://github.com/clockworklabs/SpacetimeDB/tree/master/crates/bindings-macro\n[module library]: https://github.com/clockworklabs/SpacetimeDB/tree/master/crates/lib\n[demo]: /#demo\n[client]: https://spacetimedb.com/docs/#client\n[clients]: https://spacetimedb.com/docs/#client\n[client SDK documentation]: https://spacetimedb.com/docs/#client\n[host]: https://spacetimedb.com/docs/#host\n[SEQUENCE]: https://spacetimedb.com/docs/appendix#sequence\n"
  },
  {
    "path": "crates/bindings/bindings-doctests.sh",
    "content": "#!/bin/env bash\n\n# Script to run doctests.\n# Note: if you get `cannot find type thing__TableHandle in this scope`, that\n# means you forgot to properly wrap your doctest.\n# See the top comment of README.md.\n\nset -exo pipefail\n\n# Test doctests\nrustup run nightly cargo test --doc --target wasm32-unknown-unknown -Zdoctest-xcompile\n# Make sure they also work outside wasm (use the proper boilerplate)\ncargo test --doc\n# And look for broken links\nRUSTDOCFLAGS=\"-D warnings\" cargo doc"
  },
  {
    "path": "crates/bindings/src/client_visibility_filter.rs",
    "content": "/// A row-level security filter,\n/// which can be registered using the [`macro@crate::client_visibility_filter`] attribute.\n#[non_exhaustive]\npub enum Filter {\n    /// A SQL query. Rows that match this query will be made visible to clients.\n    ///\n    /// The query must be of the form `SELECT * FROM table` or `SELECT table.* from table`,\n    /// followed by any number of `JOIN` clauses and a `WHERE` clause.\n    /// If the query includes any `JOIN`s, it must be in the form `SELECT table.* FROM table`.\n    /// In any case, the query must select all of the columns from a single table, and nothing else.\n    ///\n    /// SQL queries are not checked for syntactic or semantic validity\n    /// until they are processed by the SpacetimeDB host.\n    /// This means that errors in queries used as [`macro@crate::client_visibility_filter`] rules\n    /// will be reported during `spacetime publish`, not at compile time.\n    Sql(&'static str),\n}\n\nimpl Filter {\n    #[doc(hidden)]\n    pub fn sql_text(&self) -> &'static str {\n        let Filter::Sql(sql) = self;\n        sql\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/http.rs",
    "content": "//! Types and utilities for performing HTTP requests in [procedures](crate::procedure).\n//!\n//! Perform an HTTP request using methods on [`crate::ProcedureContext::http`],\n//! which is of type [`HttpClient`].\n//! The [`get`](HttpClient::get) helper can be used for simple `GET` requests,\n//! while [`send`](HttpClient::send) allows more complex requests with headers, bodies and other methods.\n\nuse bytes::Bytes;\n\nuse crate::{\n    rt::{read_bytes_source_as, read_bytes_source_into},\n    IterBuf,\n};\nuse spacetimedb_lib::{bsatn, http as st_http, TimeDuration};\n\npub type Request<T = Body> = http::Request<T>;\n\npub type Response<T = Body> = http::Response<T>;\n\n/// Allows performing HTTP requests via [`HttpClient::send`] and [`HttpClient::get`].\n///\n/// Access an `HttpClient` from within [procedures](crate::procedure)\n/// via [the `http` field of the `ProcedureContext`](crate::ProcedureContext::http).\n#[non_exhaustive]\npub struct HttpClient {}\n\nimpl HttpClient {\n    /// Send the HTTP request `request` and wait for its response.\n    ///\n    /// For simple `GET` requests with no headers, use [`HttpClient::get`] instead.\n    ///\n    /// Include a [`Timeout`] in the [`Request::extensions`] via [`http::request::RequestBuilder::extension`]\n    /// to impose a timeout on the request.\n    /// All HTTP requests in SpacetimeDB are subject to a maximum timeout of 500 milliseconds.\n    /// All other extensions in `request` are ignored.\n    ///\n    /// The returned [`Response`] may have a status code other than 200 OK.\n    /// Callers should inspect [`Response::status`] to handle errors returned from the remote server.\n    /// This method returns `Err(err)` only when a connection could not be initiated or was dropped,\n    /// e.g. due to DNS resolution failure or an unresponsive server.\n    ///\n    /// # Example\n    ///\n    /// Send a `POST` request with the header `Content-Type: text/plain`, a string body,\n    /// and a timeout of 100 milliseconds, then treat the response as a string and log it:\n    ///\n    /// ```norun\n    /// # use spacetimedb::{procedure, ProcedureContext, http::Timeout};\n    /// # use std::time::Duration;\n    /// # #[procedure]\n    /// # fn post_somewhere(ctx: &mut ProcedureContext) {\n    /// let request = Request::builder()\n    ///     .uri(\"https://some-remote-host.invalid/upload\")\n    ///     .method(\"POST\")\n    ///     .header(\"Content-Type\", \"text/plain\")\n    ///     // Set a timeout of 100 ms, further restricting the default timeout.\n    ///     .extension(Timeout::from(Duration::from_millis(100)))\n    ///     .body(\"This is the body of the HTTP request\")\n    ///     .expect(\"Building `Request` object failed\");\n    ///\n    /// match ctx.http.send(request) {\n    ///     Err(err) => {\n    ///         log::error!(\"HTTP request failed: {err}\");\n    ///     },\n    ///     Ok(response) => {\n    ///         let (parts, body) = response.into_parts();\n    ///         log::info!(\n    ///             \"Got response with status {}, body {}\",\n    ///             parts.status,\n    ///             body.into_string_lossy(),\n    ///         );\n    ///     }\n    /// }\n    /// # }\n    ///\n    /// ```\n    pub fn send<B: Into<Body>>(&self, request: http::Request<B>) -> Result<Response, Error> {\n        let (request, body) = request.map(Into::into).into_parts();\n        let request = convert_request(request);\n        let request = bsatn::to_vec(&request).expect(\"Failed to BSATN-serialize `spacetimedb_lib::http::Request`\");\n\n        match spacetimedb_bindings_sys::procedure::http_request(&request, &body.into_bytes()) {\n            Ok((response_source, body_source)) => {\n                let response = read_bytes_source_as::<st_http::Response>(response_source);\n                let response = convert_response(response).expect(\"Invalid http response returned from host\");\n                let body = if body_source == spacetimedb_bindings_sys::raw::BytesSource::INVALID {\n                    // Empty response body — host returns INVALID source for empty bytes\n                    Body::from_bytes(Vec::<u8>::new())\n                } else {\n                    let mut buf = IterBuf::take();\n                    read_bytes_source_into(body_source, &mut buf);\n                    Body::from_bytes(buf.clone())\n                };\n\n                Ok(http::Response::from_parts(response, body))\n            }\n            Err(err_source) => {\n                let message = read_bytes_source_as::<String>(err_source);\n                Err(Error { message })\n            }\n        }\n    }\n\n    /// Send a `GET` request to `uri` with no headers and wait for the response.\n    ///\n    /// # Example\n    ///\n    /// Send a `GET` request, then treat the response as a string and log it:\n    ///\n    /// ```no_run\n    /// # use spacetimedb::{procedure, ProcedureContext};\n    /// # #[procedure]\n    /// # fn get_from_somewhere(ctx: &mut ProcedureContext) {\n    /// match ctx.http.get(\"https://some-remote-host.invalid/download\") {\n    ///     Err(err) => {\n    ///         log::error!(\"HTTP request failed: {err}\");\n    ///     }\n    ///     Ok(response) => {\n    ///         let (parts, body) = response.into_parts();\n    ///         log::info!(\n    ///             \"Got response with status {}, body {}\",\n    ///             parts.status,\n    ///             body.into_string_lossy(),\n    ///         );\n    ///     }\n    /// }\n    /// # }\n    /// ```\n    pub fn get(&self, uri: impl TryInto<http::Uri, Error: Into<http::Error>>) -> Result<Response, Error> {\n        self.send(\n            http::Request::builder()\n                .method(http::Method::GET)\n                .uri(uri)\n                .body(Body::empty())?,\n        )\n    }\n}\n\nfn convert_request(parts: http::request::Parts) -> st_http::Request {\n    let http::request::Parts {\n        method,\n        uri,\n        version,\n        headers,\n        mut extensions,\n        ..\n    } = parts;\n\n    let timeout = extensions.remove::<Timeout>();\n    if !extensions.is_empty() {\n        log::warn!(\"Converting HTTP `Request` with unrecognized extensions\");\n    }\n    st_http::Request {\n        method: match method {\n            http::Method::GET => st_http::Method::Get,\n            http::Method::HEAD => st_http::Method::Head,\n            http::Method::POST => st_http::Method::Post,\n            http::Method::PUT => st_http::Method::Put,\n            http::Method::DELETE => st_http::Method::Delete,\n            http::Method::CONNECT => st_http::Method::Connect,\n            http::Method::OPTIONS => st_http::Method::Options,\n            http::Method::TRACE => st_http::Method::Trace,\n            http::Method::PATCH => st_http::Method::Patch,\n            _ => st_http::Method::Extension(method.to_string()),\n        },\n        headers: headers\n            .into_iter()\n            .map(|(k, v)| (k.map(|k| k.as_str().into()), v.as_bytes().into()))\n            .collect(),\n        timeout: timeout.map(Into::into),\n        uri: uri.to_string(),\n        version: match version {\n            http::Version::HTTP_09 => st_http::Version::Http09,\n            http::Version::HTTP_10 => st_http::Version::Http10,\n            http::Version::HTTP_11 => st_http::Version::Http11,\n            http::Version::HTTP_2 => st_http::Version::Http2,\n            http::Version::HTTP_3 => st_http::Version::Http3,\n            _ => unreachable!(\"Unknown HTTP version: {version:?}\"),\n        },\n    }\n}\n\nfn convert_response(response: st_http::Response) -> http::Result<http::response::Parts> {\n    let st_http::Response { headers, version, code } = response;\n\n    let (mut response, ()) = http::Response::new(()).into_parts();\n    response.version = match version {\n        st_http::Version::Http09 => http::Version::HTTP_09,\n        st_http::Version::Http10 => http::Version::HTTP_10,\n        st_http::Version::Http11 => http::Version::HTTP_11,\n        st_http::Version::Http2 => http::Version::HTTP_2,\n        st_http::Version::Http3 => http::Version::HTTP_3,\n    };\n    response.status = http::StatusCode::from_u16(code)?;\n    response.headers = headers\n        .into_iter()\n        .map(|(k, v)| Ok((k.into_string().try_into()?, v.into_vec().try_into()?)))\n        .collect::<http::Result<_>>()?;\n    Ok(response)\n}\n\n/// Represents the body of an HTTP request or response.\npub struct Body {\n    inner: BodyInner,\n}\n\nimpl Body {\n    /// Treat the body as a sequence of bytes.\n    pub fn into_bytes(self) -> Bytes {\n        match self.inner {\n            BodyInner::Bytes(bytes) => bytes,\n        }\n    }\n\n    /// Convert the body into a [`String`], erroring if it is not valid UTF-8.\n    pub fn into_string(self) -> Result<String, std::string::FromUtf8Error> {\n        String::from_utf8(self.into_bytes().into())\n    }\n\n    /// Convert the body into a [`String`], replacing invalid UTF-8 with\n    /// `U+FFFD REPLACEMENT CHARACTER`, which looks like this: �.\n    ///\n    /// See [`String::from_utf8_lossy`] for more details on the conversion.\n    pub fn into_string_lossy(self) -> String {\n        self.into_string()\n            .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned())\n    }\n\n    /// Construct a `Body` consisting of `bytes`.\n    pub fn from_bytes(bytes: impl Into<Bytes>) -> Body {\n        Body {\n            inner: BodyInner::Bytes(bytes.into()),\n        }\n    }\n\n    /// An empty body, suitable for a `GET` request.\n    pub fn empty() -> Body {\n        ().into()\n    }\n\n    /// Is `self` exactly zero bytes?\n    pub fn is_empty(&self) -> bool {\n        match &self.inner {\n            BodyInner::Bytes(bytes) => bytes.is_empty(),\n        }\n    }\n}\n\nimpl Default for Body {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nmacro_rules! impl_body_from_bytes {\n    ($bytes:ident : $t:ty => $conv:expr) => {\n        impl From<$t> for Body {\n            fn from($bytes: $t) -> Body {\n                Body::from_bytes($conv)\n            }\n        }\n    };\n    ($t:ty) => {\n        impl_body_from_bytes!(bytes : $t => bytes);\n    };\n}\n\nimpl_body_from_bytes!(String);\nimpl_body_from_bytes!(Vec<u8>);\nimpl_body_from_bytes!(Box<[u8]>);\nimpl_body_from_bytes!(&'static [u8]);\nimpl_body_from_bytes!(&'static str);\nimpl_body_from_bytes!(_unit: () => Bytes::new());\n\nenum BodyInner {\n    Bytes(Bytes),\n}\n\n/// An HTTP extension to specify a timeout for requests made by a procedure running in a SpacetimeDB database.\n///\n/// Pass an instance of this type to [`http::request::Builder::extension`] to set a timeout on a request.\n///\n/// This timeout applies to the entire request,\n/// from when the headers are first sent to when the response body is fully downloaded.\n/// This is sometimes called a total timeout, the sum of the connect timeout and the read timeout.\n#[derive(Clone, Copy, PartialEq, Eq)]\npub struct Timeout(pub TimeDuration);\n\nimpl From<TimeDuration> for Timeout {\n    fn from(timeout: TimeDuration) -> Timeout {\n        Timeout(timeout)\n    }\n}\n\nimpl From<Timeout> for TimeDuration {\n    fn from(Timeout(timeout): Timeout) -> TimeDuration {\n        timeout\n    }\n}\n\n/// An error that may arise from an HTTP call.\n#[derive(Clone, Debug)]\npub struct Error {\n    /// A string message describing the error.\n    ///\n    /// It would be nice if we could store a more interesting object here,\n    /// ideally a type-erased `dyn Trait` cause,\n    /// rather than just a string, similar to how `anyhow` does.\n    /// This is not possible because we need to serialize `Error` for transport to WASM,\n    /// meaning it must have a concrete static type.\n    /// `reqwest::Error`, which is the source for these,\n    /// is type-erased enough that the best we can do (at least, the best we can do easily)\n    /// is to eagerly string-ify the error.\n    message: String,\n}\n\nimpl std::fmt::Display for Error {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        let Error { message } = self;\n        f.write_str(message)\n    }\n}\n\nimpl std::error::Error for Error {}\n\nimpl From<http::Error> for Error {\n    fn from(err: http::Error) -> Self {\n        Error {\n            message: err.to_string(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\n// ^ if you are working on docs, go read the top comment of README.md please.\n\nuse core::cell::{Cell, LazyCell, OnceCell, RefCell};\nuse core::ops::Deref;\nuse spacetimedb_lib::bsatn;\nuse std::rc::Rc;\n\n#[cfg(feature = \"unstable\")]\nmod client_visibility_filter;\n#[cfg(feature = \"unstable\")]\npub mod http;\npub mod log_stopwatch;\nmod logger;\n#[cfg(feature = \"rand08\")]\nmod rng;\n#[doc(hidden)]\npub mod rt;\n#[doc(hidden)]\npub mod table;\n\n#[doc(hidden)]\npub use spacetimedb_query_builder as query_builder;\n\n#[cfg(feature = \"unstable\")]\npub use client_visibility_filter::Filter;\npub use log;\n#[cfg(feature = \"rand\")]\npub use rand08 as rand;\n#[cfg(feature = \"rand\")]\nuse rand08::RngCore;\n#[cfg(feature = \"rand08\")]\npub use rng::StdbRng;\npub use sats::SpacetimeType;\n#[doc(hidden)]\npub use spacetimedb_bindings_macro::__TableHelper;\npub use spacetimedb_bindings_sys as sys;\npub use spacetimedb_lib;\npub use spacetimedb_lib::db::raw_def::v10::CaseConversionPolicy;\npub use spacetimedb_lib::de::{Deserialize, DeserializeOwned};\npub use spacetimedb_lib::sats;\npub use spacetimedb_lib::ser::Serialize;\npub use spacetimedb_lib::AlgebraicValue;\npub use spacetimedb_lib::ConnectionId;\n// `FilterableValue` re-exported purely for rustdoc.\npub use spacetimedb_lib::FilterableValue;\npub use spacetimedb_lib::Identity;\npub use spacetimedb_lib::ScheduleAt;\npub use spacetimedb_lib::TimeDuration;\npub use spacetimedb_lib::Timestamp;\npub use spacetimedb_lib::Uuid;\npub use spacetimedb_primitives::TableId;\npub use sys::Errno;\npub use table::{\n    AutoIncOverflow, PointIndex, PointIndexReadOnly, RangedIndex, RangedIndexReadOnly, Table, TryInsertError,\n    UniqueColumn, UniqueColumnReadOnly, UniqueConstraintViolation,\n};\n\npub type ReducerResult = core::result::Result<(), Box<str>>;\n\npub type ProcedureResult = Vec<u8>;\n\npub use spacetimedb_bindings_macro::duration;\n\n/// Generates code for registering a row-level security rule.\n///\n/// This attribute must be applied to a `const` binding of type [`Filter`].\n/// It will be interpreted as a filter on the table to which it applies, for all client queries.\n/// If a module contains multiple `client_visibility_filter`s for the same table,\n/// they will be unioned together as if by SQL `OR`,\n/// so that any row permitted by at least one filter is visible.\n///\n/// The `const` binding's identifier must be unique within the module.\n///\n/// The query follows the same syntax as a subscription query.\n///\n/// ## Example:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{client_visibility_filter, Filter};\n///\n/// /// Players can only see what's in their chunk\n/// #[client_visibility_filter]\n/// const PLAYERS_SEE_ENTITIES_IN_SAME_CHUNK: Filter = Filter::Sql(\"\n///     SELECT * FROM LocationState WHERE chunk_index IN (\n///         SELECT chunk_index FROM LocationState WHERE entity_id IN (\n///             SELECT entity_id FROM UserState WHERE identity = :sender\n///         )\n///     )\n/// \");\n/// # }\n/// ```\n///\n/// Queries are not checked for syntactic or semantic validity\n/// until they are processed by the SpacetimeDB host.\n/// This means that errors in queries, such as syntax errors, type errors or unknown tables,\n/// will be reported during `spacetime publish`, not at compile time.\n#[cfg(feature = \"unstable\")]\n#[doc(inline, hidden)] // TODO: RLS filters are currently unimplemented, and are not enforced.\npub use spacetimedb_bindings_macro::client_visibility_filter;\n\n/// Declare a module-level setting.\n///\n/// Apply this attribute to a `const` item whose name is a known setting:\n///\n/// ```ignore\n/// use spacetimedb::CaseConversionPolicy;\n///\n/// #[spacetimedb::settings]\n/// const CASE_CONVERSION_POLICY: CaseConversionPolicy = CaseConversionPolicy::SnakeCase;\n/// ```\n///\n/// # Known Settings\n///\n/// | Const Name | Type | Default | Description |\n/// |---|---|---|---|\n/// | `CASE_CONVERSION_POLICY` | [`CaseConversionPolicy`] | `SnakeCase` | How identifiers are converted to canonical names |\n///\n/// # Errors\n///\n/// - Unknown setting name: compile error listing known settings\n/// - Duplicate setting: linker error (duplicate symbol)\n#[doc(inline)]\npub use spacetimedb_bindings_macro::settings;\n\n/// Declares a table with a particular row type.\n///\n/// This attribute is applied to a struct type with named fields.\n/// This derives [`Serialize`], [`Deserialize`], [`SpacetimeType`], and [`Debug`] for the annotated struct.\n///\n/// Elements of the struct type are NOT automatically inserted into any global table.\n/// They are regular structs, with no special behavior.\n/// In particular, modifying them does not automatically modify the database!\n///\n/// Instead, a type implementing [`Table<Row = Self>`] is generated. This can be looked up in a [`ReducerContext`]\n/// using `ctx.db.{table_name}()`. This type represents a handle to a database table, and can be used to\n/// iterate and modify the table's elements. It is a view of the entire table -- the entire set of rows at the time of the reducer call.\n///\n/// # Example\n///\n/// ```ignore\n/// use spacetimedb::{table, ReducerContext};\n///\n/// #[table(accessor = user, public,\n///         index(accessor = popularity_and_username, btree(columns = [popularity, username])),\n/// )]\n/// pub struct User {\n///     #[auto_inc]\n///     #[primary_key]\n///     pub id: u32,\n///     #[unique]\n///     pub username: String,\n///     #[index(btree)]\n///     pub popularity: u32,\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     // Use the name of the table to get a struct\n///     // implementing `spacetimedb::Table<Row = User>`.\n///     let user: user__TableHandle = ctx.db.user();\n///\n///     // You can use methods from `spacetimedb::Table`\n///     // on the table.\n///     log::debug!(\"User count: {}\", user.count());\n///     for user in user.iter() {\n///         log::debug!(\"{:?}\", user);\n///     }\n///\n///     // For every `#[index(btree)]`, the table has an extra method\n///     // for getting a corresponding `spacetimedb::BTreeIndex`.\n///     let by_popularity: RangedIndex<_, (u32,), _> =\n///         user.popularity();\n///     for popular_user in by_popularity.filter(95..) {\n///         log::debug!(\"Popular user: {:?}\", popular_user);\n///     }\n///\n///     // There are similar methods for multi-column indexes.\n///     let by_popularity_and_username: RangedIndex<_, (u32, String), _> = user.popularity_and_username();\n///     for popular_user in by_popularity.filter((100, \"a\"..)) {\n///         log::debug!(\"Popular user whose name starts with 'a': {:?}\", popular_user);\n///     }\n///\n///     // For every `#[unique]` or `#[primary_key]` field,\n///     // the table has an extra method that allows getting a\n///     // corresponding `spacetimedb::UniqueColumn`.\n///     let by_username: spacetimedb::UniqueColumn<_, String, _> = user.id();\n///     by_username.delete(&\"test_user\".to_string());\n/// }\n/// ```\n///\n/// See [`Table`], [`RangedIndex`], and [`UniqueColumn`] for more information on the methods available on these types.\n///\n/// # Browsing generated documentation\n///\n/// The `#[table]` macro generates different APIs depending on the contents of your table.\n///\n/// To browse the complete generated API for your tables, run `cargo doc` in your SpacetimeDB module project. Navigate to `[YOUR PROJECT/target/doc/spacetime_module/index.html` in your file explorer, and right click -> open it in a web browser.\n///\n/// For the example above, we would see three items:\n/// - A struct `User`. This is the struct you declared. It stores rows of the table `user`.\n/// - A struct `user__TableHandle`. This is an opaque handle that allows you to interact with the table `user`.\n/// - A trait `user` containing a single `fn user(&self) -> user__TableHandle`.\n///   This trait is implemented for the `db` field of a [`ReducerContext`], allowing you to get a\n///   `user__TableHandle` using `ctx.db.user()`.\n///\n/// # Macro arguments\n///\n/// The `#[table(...)]` attribute accepts any number of the following arguments, separated by commas.\n///\n/// Multiple `table` annotations can be present on the same type. This will generate\n/// multiple tables of the same row type, but with different names.\n///\n/// ### `name`\n///\n/// Specify the name of the table in the database. The name can be any valid Rust identifier.\n///\n/// The table name is used to get a handle to the table from a [`ReducerContext`].\n/// For a table *table*, use `ctx.db.{table}()` to do this.\n/// For example:\n/// ```ignore\n///  #[table(accessor = user)]\n///  pub struct User {\n///      #[auto_inc]\n///      #[primary_key]\n///      pub id: u32,\n///      #[unique]\n///      pub username: String,\n///      #[index(btree)]\n///      pub popularity: u32,\n///  }\n///  #[reducer]\n///  fn demo(ctx: &ReducerContext) {\n///      let user: user__TableHandle = ctx.db.user();\n///  }\n///  ```\n///\n/// ### `public` and `private`\n///\n/// Tables are private by default. This means that clients cannot read their contents\n/// or see that they exist.\n///\n/// If you'd like to make your table publicly accessible by clients,\n/// put `public` in the macro arguments (e.g.\n/// `#[spacetimedb::table(public)]`). You can also specify `private` if\n/// you'd like to be specific.\n///\n/// This is fully separate from Rust's module visibility\n/// system; `pub struct` or `pub(crate) struct` do not affect the table visibility, only\n/// the visibility of the items in your own source code.\n///\n/// ### `index(...)`\n///\n/// You can specify an index on one or more of the table's columns with the syntax:\n/// `index(accessor = my_index, btree(columns = [a, b, c]))`\n///\n/// You can also just put `#[index(btree)]` on the field itself if you only need\n/// a single-column index; see column attributes below.\n///\n/// A table may declare any number of indexes.\n///\n/// You can use indexes to efficiently [`filter`](crate::RangedIndex::filter) and\n/// [`delete`](crate::RangedIndex::delete) rows. This is encapsulated in the struct [`RangedIndex`].\n///\n/// For a table *table* and an index *index*, use:\n/// ```text\n/// ctx.db.{table}().{index}()\n/// ```\n/// to get a [`RangedIndex`] for a [`ReducerContext`].\n///\n/// For example:\n/// ```ignore\n/// let by_id_and_username: spacetimedb::RangedIndex<_, (u32, String), _> =\n///     ctx.db.user().by_id_and_username();\n/// ```\n///\n/// ### `scheduled(reducer_name)`\n///\n/// Used to declare a [scheduled reducer](macro@crate::reducer#scheduled-reducers).\n///\n/// The annotated struct type must have at least the following fields:\n/// - `scheduled_id: u64`\n/// - [`scheduled_at: ScheduleAt`](crate::ScheduleAt)\n///\n/// # Column (field) attributes\n///\n/// ### `#[auto_inc]`\n///\n/// Creates an auto-increment constraint.\n///\n/// When a row is inserted with the annotated field set to `0` (zero),\n/// the sequence is incremented, and this value is used instead.\n///\n/// Can only be used on numeric types.\n///\n/// May be combined with indexes or unique constraints.\n///\n/// Note that using `#[auto_inc]` on a field does not also imply `#[primary_key]` or `#[unique]`.\n/// If those semantics are desired, those attributes should also be used.\n///\n/// When `#[auto_inc]` is combined with a unique key,\n/// be wary not to manually insert values larger than the allocated sequence value.\n/// In this case, the sequence will eventually catch up, allocate a value that's already present,\n/// and cause a unique constraint violation.\n///\n/// ### `#[unique]`\n///\n/// Creates an unique constraint and index for the annotated field.\n///\n/// You can [`find`](crate::UniqueColumn::find), [`update`](crate::UniqueColumn::update),\n/// and [`delete`](crate::UniqueColumn::delete) rows by their unique columns.\n/// This is encapsulated in the struct [`UniqueColumn`].\n///\n/// For a table *table* and a column *column*, use:\n/// ```text\n/// ctx.db.{table}().{column}()`\n/// ```\n/// to get a [`UniqueColumn`] from a [`ReducerContext`].\n///\n/// For example:\n/// ```ignore\n/// let by_username: spacetimedb::UniqueColumn<_, String, _> = ctx.db.user().username();\n/// ```\n///\n/// When there is a unique column constraint on the table, insertion can fail if a uniqueness constraint is violated.\n/// If we insert two rows which have the same value of a unique column, the second will fail.\n/// This will be via a panic with [`Table::insert`] or via a `Result::Err` with [`Table::try_insert`].\n///\n/// For example:\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{\n///     table,\n///     reducer,\n///     ReducerContext,\n///     // Make sure to import the `Table` trait to use `insert` or `try_insert`.\n///     Table\n/// };\n///\n/// type CountryCode = String;\n///\n/// #[table(accessor = country)]\n/// struct Country {\n///     #[unique]\n///     code: CountryCode,\n///     national_bird: String\n/// }\n///\n/// #[reducer]\n/// fn insert_unique_demo(ctx: &ReducerContext) {\n///     let result = ctx.db.country().try_insert(Country {\n///         code: \"AU\".into(), national_bird: \"Emu\".into()\n///     });\n///     assert!(result.is_ok());\n///\n///     let result = ctx.db.country().try_insert(Country {\n///         code: \"AU\".into(), national_bird: \"Great Egret\".into()\n///         // Whoops, this was Austria's national bird, not Australia's.\n///         // We should have used the country code \"AT\", not \"AU\".\n///     });\n///     // since there's already a country in the database with the code \"AU\",\n///     // SpacetimeDB gives us an error.\n///     assert!(result.is_err());\n///\n///     // The following line would panic, since we use `insert` rather than `try_insert`.\n///     // let result = ctx.db.country().insert(Country { code: \"CN\".into(), national_bird: \"Blue Magpie\".into() });\n///\n///     // If we wanted to *update* the row for Australia, we can use the `update` method of `UniqueIndex`.\n///     // The following line will succeed:\n///     ctx.db.country().code().update(Country {\n///         code: \"AU\".into(), national_bird: \"Australian Emu\".into()\n///     });\n/// }\n/// # }\n/// ```\n///\n/// ### `#[primary_key]`\n///\n/// Implies `#[unique]`. Also generates additional methods client-side for handling updates to the table.\n/// <!-- TODO: link to client-side documentation. -->\n///\n/// ### `#[index(btree)]`\n///\n/// Creates a single-column index with the specified algorithm.\n///\n/// It is an error to specify this attribute together with `#[unique]`.\n/// Unique constraints implicitly create a unique index, which is accessed using the [`UniqueColumn`] struct instead of the\n/// [`RangedIndex`] struct.\n///\n/// The created index has the same name as the column.\n///\n/// For a table *table* and an indexed *column*, use:\n/// ```text\n/// ctx.db.{table}().{column}()\n/// ```\n/// to get a [`RangedIndex`] from a [`ReducerContext`].\n///\n/// For example:\n///\n/// ```ignore\n/// ctx.db.cities().latitude()\n/// ```\n///\n/// # Generated code\n///\n/// For each `[table(accessor = {name})]` annotation on a type `{T}`, generates a struct\n/// `{name}__TableHandle` implementing [`Table<Row={T}>`](crate::Table), and a trait that allows looking up such a\n/// `{name}Handle` in a [`ReducerContext`].\n///\n/// The struct `{name}__TableHandle` is public and lives next to the row struct.\n/// Users are encouraged not to write the name of this table handle struct,\n/// or to store table handles in variables; operate through a `ReducerContext` instead.\n///\n/// For each named index declaration, add a method to `{name}__TableHandle` for getting a corresponding\n/// [`RangedIndex`].\n///\n/// For each field  with a `#[unique]` or `#[primary_key]` annotation,\n/// add a method to `{name}Handle` for getting a corresponding [`UniqueColumn`].\n///\n/// The following pseudocode illustrates the general idea. Curly braces are used to indicate templated\n/// names.\n///\n/// ```ignore\n/// use spacetimedb::{RangedIndex, UniqueColumn, Table, DbView};\n///\n/// // This generated struct is hidden and cannot be directly accessed.\n/// struct {name}__TableHandle { /* ... */ };\n///\n/// // It is a table handle.\n/// impl Table for {name}__TableHandle {\n///     type Row = {T};\n///     /* ... */\n/// }\n///\n/// // It can be looked up in a `ReducerContext`,\n/// // using `ctx.db().{name}()`.\n/// trait {name} {\n///     fn {name}(&self) -> Row = {T}>;\n/// }\n/// impl {name} for <ReducerContext as DbContext>::DbView { /* ... */ }\n///\n/// // Once looked up, it can be used to look up indexes.\n/// impl {name}Handle {\n///     // For each `#[unique]` or `#[primary_key]` field `{field}` of type `{F}`:\n///     fn {field}(&self) -> UniqueColumn<_, {F}, _> { /* ... */ };\n///\n///     // For each named index `{index}` on fields of type `{(F1, ..., FN)}`:\n///     fn {index}(&self) -> RangedIndex<_, {(F1, ..., FN)}, _>;\n/// }\n/// ```\n///\n/// [`Table<Row = Self>`]: `Table`\n#[doc(inline)]\npub use spacetimedb_bindings_macro::table;\n\n/// Marks a function as a spacetimedb reducer.\n///\n/// A reducer is a function with read/write access to the database\n/// that can be invoked remotely by [clients].\n///\n/// Each reducer call runs in its own database transaction,\n/// and its updates to the database are only committed if the reducer returns successfully.\n///\n/// The first argument of a reducer is always a [`&ReducerContext`]. This context object\n/// allows accessing the database and viewing information about the caller, among other things.\n///\n/// After this, a reducer can take any number of arguments.\n/// These arguments must implement the [`SpacetimeType`], [`Serialize`], and [`Deserialize`] traits.\n/// All of these traits can be derived at once by marking a type with `#[derive(SpacetimeType)]`.\n///\n/// Reducers may return either `()` or `Result<(), E>` where [`E: std::fmt::Display`](std::fmt::Display).\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{reducer, SpacetimeType, ReducerContext};\n/// use log::info;\n/// use std::fmt;\n///\n/// #[reducer]\n/// pub fn hello_world(context: &ReducerContext) {\n///     info!(\"Hello, World!\");\n/// }\n///\n/// #[reducer]\n/// pub fn add_person(context: &ReducerContext, name: String, age: u16) {\n///     // add a \"person\" to the database.\n/// }\n///\n/// #[derive(SpacetimeType, Debug)]\n/// struct Coordinates {\n///     x: f32,\n///     y: f32,\n/// }\n///\n/// enum AddPlaceError {\n///     InvalidCoordinates(Coordinates),\n///     InvalidName(String),\n/// }\n///\n/// impl fmt::Display for AddPlaceError {\n///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n///         match self {\n///             AddPlaceError::InvalidCoordinates(coords) => {\n///                 write!(f, \"invalid coordinates: {coords:?}\")\n///             },\n///             AddPlaceError::InvalidName(name) => {\n///                 write!(f, \"invalid name: {name:?}\")\n///             },\n///         }\n///     }\n/// }\n///\n/// #[reducer]\n/// pub fn add_place(\n///     context: &ReducerContext,\n///     name: String,\n///     x: f32,\n///     y: f32,\n///     area: f32,\n/// ) -> Result<(), AddPlaceError> {\n///     // ... add a place to the database...\n///     # Ok(())\n/// }\n/// # }\n/// ```\n///\n/// Reducers may fail by returning a [`Result::Err`](std::result::Result) or by [panicking](std::panic!).\n/// Failures will abort the active database transaction.\n/// Any changes to the database made by the failed reducer call will be rolled back.\n///\n/// Reducers are limited in their ability to interact with the outside world.\n/// They do not directly return data aside from errors, and have no access to any\n/// network or filesystem interfaces.\n/// Calling methods from [`std::io`], [`std::net`], or [`std::fs`]\n/// inside a reducer will result in runtime errors.\n///\n/// Reducers can communicate information to the outside world in two ways:\n/// - They can modify tables in the database.\n///   See the `#[table]`(#table) macro documentation for information on how to declare and use tables.\n/// - They can call logging macros from the [`log`] crate.\n///   This writes to a private debug log attached to the database.\n///   Run `spacetime logs <DATABASE_IDENTITY>` to browse these.\n///\n/// Reducers are permitted to call other reducers, simply by passing their `ReducerContext` as the first argument.\n/// This is a regular function call, and does not involve any network communication. The callee will run within the\n/// caller's transaction, and any changes made by the callee will be committed or rolled back with the caller.\n///\n/// # Lifecycle Reducers\n///\n/// You can specify special lifecycle reducers that are run at set points in\n/// the module's lifecycle. You can have one of each per module.\n///\n/// These reducers cannot be called manually\n/// and may not have any parameters except for `ReducerContext`.\n///\n/// ### The `init` reducer\n///\n/// This reducer is marked with `#[spacetimedb::reducer(init)]`. It is run the first time a module is published\n/// and any time the database is cleared. (It does not have to be named `init`.)\n///\n/// If an error occurs when initializing, the module will not be published.\n///\n/// This reducer can be used to configure any static data tables used by your module. It can also be used to start running [scheduled reducers](#scheduled-reducers).\n///\n/// ### The `client_connected` reducer\n///\n/// This reducer is marked with `#[spacetimedb::reducer(client_connected)]`. It is run when a client connects to the SpacetimeDB module.\n/// Their identity can be found in the sender value of the `ReducerContext`.\n///\n/// If an error occurs in the reducer, the client will be disconnected.\n///\n/// ### The `client_disconnected` reducer\n///\n/// This reducer is marked with `#[spacetimedb::reducer(client_disconnected)]`. It is run when a client disconnects from the SpacetimeDB module.\n/// Their identity can be found in the sender value of the `ReducerContext`.\n///\n/// If an error occurs in the disconnect reducer,\n/// the client is still recorded as disconnected.\n///\n// TODO(docs): Move these docs to be on `table`, rather than `reducer`. This will reduce duplication with procedure docs.\n/// # Scheduled reducers\n///\n/// In addition to life cycle annotations, reducers can be made **scheduled**.\n/// This allows calling the reducers at a particular time, or in a loop.\n/// This can be used for game loops.\n///\n/// The scheduling information for a reducer is stored in a table.\n/// This table has two mandatory fields:\n/// - A primary key that identifies scheduled reducer calls.\n/// - A [`ScheduleAt`] field that says when to call the reducer.\n///\n/// Managing timers with a scheduled table is as simple as inserting or deleting rows from the table.\n/// This makes scheduling transactional in SpacetimeDB. If a reducer A first schedules B but then errors for some other reason, B will not be scheduled to run.\n///\n/// A [`ScheduleAt`] can be created from a [`spacetimedb::Timestamp`](crate::Timestamp), in which case the reducer will be scheduled once,\n/// or from a [`std::time::Duration`], in which case the reducer will be scheduled in a loop. In either case the conversion can be performed using [`Into::into`].\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, reducer, ReducerContext, Timestamp, TimeDuration, ScheduleAt, Table};\n/// use log::debug;\n///\n/// // First, we declare the table with scheduling information.\n///\n/// #[table(accessor = send_message_schedule, scheduled(send_message))]\n/// struct SendMessageSchedule {\n///     // Mandatory fields:\n///     // ============================\n///\n///     /// An identifier for the scheduled reducer call.\n///     #[primary_key]\n///     #[auto_inc]\n///     scheduled_id: u64,\n///\n///     /// Information about when the reducer should be called.\n///     scheduled_at: ScheduleAt,\n///\n///     // In addition to the mandatory fields, any number of fields can be added.\n///     // These can be used to provide extra information to the scheduled reducer.\n///\n///     // Custom fields:\n///     // ============================\n///\n///     /// The text of the scheduled message to send.\n///     text: String,\n/// }\n///\n/// // Then, we declare the scheduled reducer.\n/// // The first argument of the reducer should be, as always, a `&ReducerContext`.\n/// // The second argument should be a row of the scheduling information table.\n///\n/// #[reducer]\n/// fn send_message(ctx: &ReducerContext, arg: SendMessageSchedule) -> Result<(), String> {\n///     let message_to_send = arg.text;\n///\n///     // ... send the message ...\n///\n///     Ok(())\n/// }\n///\n/// // Now, we want to actually start scheduling reducers.\n/// // It's convenient to do this inside the `init` reducer.\n/// #[reducer(init)]\n/// fn init(ctx: &ReducerContext) {\n///\n///     let current_time = ctx.timestamp;\n///\n///     let ten_seconds = TimeDuration::from_micros(10_000_000);\n///\n///     let future_timestamp: Timestamp = ctx.timestamp + ten_seconds;\n///     ctx.db.send_message_schedule().insert(SendMessageSchedule {\n///         scheduled_id: 1,\n///         text:\"I'm a bot sending a message one time\".to_string(),\n///\n///         // Creating a `ScheduleAt` from a `Timestamp` results in the reducer\n///         // being called once, at exactly the time `future_timestamp`.\n///         scheduled_at: future_timestamp.into()\n///     });\n///\n///     let loop_duration: TimeDuration = ten_seconds;\n///     ctx.db.send_message_schedule().insert(SendMessageSchedule {\n///         scheduled_id: 0,\n///         text:\"I'm a bot sending a message every 10 seconds\".to_string(),\n///\n///         // Creating a `ScheduleAt` from a `Duration` results in the reducer\n///         // being called in a loop, once every `loop_duration`.\n///         scheduled_at: loop_duration.into()\n///     });\n/// }\n/// # }\n/// ```\n///\n/// Scheduled reducers are called on a best-effort basis and may be slightly delayed in their execution\n/// when a database is under heavy load.\n///\n/// ### Restricting scheduled reducers\n///\n/// Scheduled reducers are normal reducers, and may still be called by clients.\n/// If a scheduled reducer should only be called by the scheduler,\n/// consider beginning it with a check that the caller `Identity` is the module:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{reducer, ReducerContext};\n///\n/// # #[derive(spacetimedb::SpacetimeType)] struct ScheduledArgs {}\n///\n/// #[reducer]\n/// fn scheduled(ctx: &ReducerContext, args: ScheduledArgs) -> Result<(), String> {\n///     if ctx.sender() != ctx.identity() {\n///         return Err(\"Reducer `scheduled` may not be invoked by clients, only via scheduling.\".into());\n///     }\n///     // Reducer body...\n///     # Ok(())\n/// }\n/// # }\n/// ```\n///\n/// <!-- TODO: SLAs? -->\n///\n/// [`&ReducerContext`]: `ReducerContext`\n/// [clients]: https://spacetimedb.com/docs/#client\n#[doc(inline)]\npub use spacetimedb_bindings_macro::reducer;\n\n/// Marks a function as a SpacetimeDB procedure.\n///\n/// A procedure is a function that runs within the database and can be invoked remotely by [clients],\n/// but unlike a [`reducer`], a  procedure is not automatically transactional.\n/// This allows procedures to perform certain side-effecting operations,\n/// but also means that module developers must be more careful not to corrupt the database state\n/// when execution aborts or operations fail.\n///\n/// When in doubt, prefer writing [`reducer`]s unless you need to perform an operation only available to procedures.\n///\n/// The first argument of a procedure is always `&mut ProcedureContext`.\n/// The [`ProcedureContext`] exposes information about the caller and allows side-effecting operations.\n///\n/// After this, a procedure can take any number of arguments.\n/// These arguments must implement the [`SpacetimeType`], [`Serialize`], and [`Deserialize`] traits.\n/// All of these traits can be derived at once by marking a type with `#[derive(SpacetimeType)]`.\n///\n/// A procedure may return any type that implements [`SpacetimeType`], [`Serialize`] and [`Deserialize`].\n/// Unlike [reducer]s, SpacetimeDB does not assign any special semantics to [`Result`] return values.\n///\n/// If a procedure returns successfully (as opposed to panicking), its return value will be sent to the calling client.\n/// If a procedure panics, its panic message will be sent to the calling client instead.\n/// Procedure arguments and return values are not otherwise broadcast to clients.\n///\n/// ```no_run\n/// # use spacetimedb::{procedure, SpacetimeType, ProcedureContext, Timestamp};\n/// #[procedure]\n/// fn return_value(ctx: &mut ProcedureContext, arg: MyArgument) -> MyReturnValue {\n///     MyReturnValue {\n///         a: format!(\"Hello, {}\", ctx.sender()),\n///         b: ctx.timestamp,\n///     }\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// struct MyArgument {\n///     val: u32,\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// struct MyReturnValue {\n///     a: String,\n///     b: Timestamp,\n/// }\n/// ```\n///\n/// # Blocking operations\n///\n/// Procedures are allowed to perform certain operations which take time.\n/// During the execution of these operations, the procedure's execution will be suspended,\n/// allowing other database operations to run in parallel.\n///\n/// Procedures must not hold open a transaction while performing a blocking operation.\n// TODO(procedure-http): add example with an HTTP request.\n// TODO(procedure-transaction): document obtaining and using a transaction within a procedure.\n///\n/// # Scheduled procedures\n// TODO(docs): after moving scheduled reducer docs into table section, link there.\n///\n/// Like [reducer]s, procedures can be made **scheduled**.\n/// This allows calling procedures at a particular time, or in a loop.\n/// It also allows reducers to enqueue procedure runs.\n///\n/// Scheduled procedures are called on a best-effort basis and may be slightly delayed in their execution\n/// when a database is under heavy load.\n///\n/// [clients]: https://spacetimedb.com/docs/#client\n// TODO(procedure-async): update docs and examples with `async`-ness.\n#[doc(inline)]\n#[cfg(feature = \"unstable\")]\npub use spacetimedb_bindings_macro::procedure;\n\n/// Marks a function as a spacetimedb view.\n///\n/// A view is a function with read-only access to the database.\n///\n/// The first argument of a view is always a [`&ViewContext`] or [`&AnonymousViewContext`].\n/// The former can only read from the database whereas latter can also access info about the caller.\n///\n/// After this, a view can take any number of arguments just like reducers.\n/// These arguments must implement the [`SpacetimeType`], [`Serialize`], and [`Deserialize`] traits.\n/// All of these traits can be derived at once by marking a type with `#[derive(SpacetimeType)]`.\n///\n/// Views return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`.\n///\n/// ```no_run\n/// # mod demo {\n/// use spacetimedb::{view, table, AnonymousViewContext, SpacetimeType, ViewContext};\n/// use spacetimedb_lib::Identity;\n///\n/// #[table(accessor = player)]\n/// struct Player {\n///     #[auto_inc]\n///     #[primary_key]\n///     id: u64,\n///\n///     #[unique]\n///     identity: Identity,\n///\n///     #[index(btree)]\n///     level: u32,\n/// }\n///\n/// impl Player {\n///     fn merge(self, location: Location) -> PlayerAndLocation {\n///         PlayerAndLocation {\n///             player_id: self.id,\n///             level: self.level,\n///             x: location.x,\n///             y: location.y,\n///         }\n///     }\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// struct PlayerId {\n///     id: u64,\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// struct PlayerCount {\n///     count: u64,\n/// }\n///\n/// #[table(accessor = location, index(accessor = coordinates, btree(columns = [x, y])))]\n/// struct Location {\n///     #[unique]\n///     player_id: u64,\n///     x: u64,\n///     y: u64,\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// struct PlayerAndLocation {\n///     player_id: u64,\n///     level: u32,\n///     x: u64,\n///     y: u64,\n/// }\n///\n/// // A view that selects at most one row from a table\n/// #[view(accessor = my_player, public)]\n/// fn my_player(ctx: &ViewContext) -> Option<Player> {\n///     ctx.db.player().identity().find(ctx.sender())\n/// }\n///\n/// // An example of column projection\n/// #[view(accessor = my_player_id, public)]\n/// fn my_player_id(ctx: &ViewContext) -> Option<PlayerId> {\n///     ctx.db.player().identity().find(ctx.sender()).map(|Player { id, .. }| PlayerId { id })\n/// }\n///\n/// // A view that counts the number of rows in a table\n/// #[view(accessor = player_count, public)]\n/// fn player_count(ctx: &AnonymousViewContext) -> Option<PlayerCount> {\n///     Some(PlayerCount {\n///         count: ctx.db.player().count(),\n///     })\n/// }\n///\n/// // An example that is analogous to a semijoin in sql\n/// #[view(accessor = players_at_coordinates, public)]\n/// fn players_at_coordinates(ctx: &AnonymousViewContext) -> Vec<Player> {\n///     ctx\n///         .db\n///         .location()\n///         .coordinates()\n///         .filter((3u64, 5u64))\n///         .filter_map(|location| ctx.db.player().id().find(location.player_id))\n///         .collect()\n/// }\n///\n/// // An example of a join that combines fields from two different tables\n/// #[view(accessor = players_with_coordinates, public)]\n/// fn players_with_coordinates(ctx: &AnonymousViewContext) -> Vec<PlayerAndLocation> {\n///     ctx\n///         .db\n///         .location()\n///         .coordinates()\n///         .filter((3u64, 5u64))\n///         .filter_map(|location| ctx\n///             .db\n///             .player()\n///             .id()\n///             .find(location.player_id)\n///             .map(|player| player.merge(location))\n///         )\n///         .collect()\n/// }\n/// # }\n/// ```\n///\n/// Just like reducers, views are limited in their ability to interact with the outside world.\n/// They have no access to any network or filesystem interfaces.\n/// Calling methods from [`std::io`], [`std::net`], or [`std::fs`] will result in runtime errors.\n///\n/// Views are callable by reducers and other views simply by passing their `ViewContext`..\n/// This is a regular function call.\n/// The callee will run within the caller's transaction.\n///\n///\n/// [`&ViewContext`]: `ViewContext`\n/// [`&AnonymousViewContext`]: `AnonymousViewContext`\n#[doc(inline)]\npub use spacetimedb_bindings_macro::view;\n\npub struct QueryBuilder {}\npub use query_builder::{Query, RawQuery};\n\n/// One of two possible types that can be passed as the first argument to a `#[view]`.\n/// The other is [`ViewContext`].\n/// Use this type if the view does not depend on the caller's identity.\npub struct AnonymousViewContext {\n    pub db: LocalReadOnly,\n    pub from: QueryBuilder,\n}\n\nimpl Default for AnonymousViewContext {\n    fn default() -> Self {\n        Self {\n            db: LocalReadOnly {},\n            from: QueryBuilder {},\n        }\n    }\n}\n/// One of two possible types that can be passed as the first argument to a `#[view]`.\n/// The other is [`AnonymousViewContext`].\n/// Use this type if the view depends on the caller's identity.\npub struct ViewContext {\n    sender: Identity,\n    pub db: LocalReadOnly,\n    pub from: QueryBuilder,\n}\n\nimpl ViewContext {\n    pub fn new(sender: Identity) -> Self {\n        Self {\n            sender,\n            db: LocalReadOnly {},\n            from: QueryBuilder {},\n        }\n    }\n\n    /// The `Identity` of the client that invoked the view.\n    pub fn sender(&self) -> Identity {\n        self.sender\n    }\n\n    /// Obtain an [`AnonymousViewContext`] by dropping `sender`.\n    pub fn as_anonymous(&self) -> AnonymousViewContext {\n        AnonymousViewContext::default()\n    }\n}\n\n/// The context that any reducer is provided with.\n///\n/// This must be the first argument of the reducer. Clients of the module will\n/// only see arguments after the `ReducerContext`.\n///\n/// Includes information about the client calling the reducer and the time of invocation,\n/// as well as a view into the module's database.\n///\n/// If the crate was compiled with the `rand` feature, also includes faculties for random\n/// number generation.\n///\n/// Implements the `DbContext` trait for accessing views into a database.\n#[non_exhaustive]\npub struct ReducerContext {\n    /// The `Identity` of the client that invoked the reducer.\n    sender: Identity,\n\n    /// The time at which the reducer was started.\n    pub timestamp: Timestamp,\n\n    /// The `ConnectionId` of the client that invoked the reducer.\n    ///\n    /// Will be `None` for certain reducers invoked automatically by the host,\n    /// including `init` and scheduled reducers.\n    connection_id: Option<ConnectionId>,\n\n    sender_auth: AuthCtx,\n\n    /// Allows accessing the local database attached to a module.\n    ///\n    /// This slightly strange type appears to have no methods, but that is misleading.\n    /// The `#[table]` macro uses the trait system to add table accessors to this type.\n    /// These are generated methods that allow you to access specific tables.\n    ///\n    /// For a table named *table*, use `ctx.db.{table}()` to get a handle.\n    /// For example:\n    /// ```no_run\n    /// # mod demo { // work around doctest+index issue\n    /// # #![cfg(target_arch = \"wasm32\")]\n    /// use spacetimedb::{table, reducer, ReducerContext};\n    ///\n    /// #[table(accessor = book)]\n    /// #[derive(Debug)]\n    /// struct Book {\n    ///     #[primary_key]\n    ///     id: u64,\n    ///     isbn: String,\n    ///     name: String,\n    ///     #[index(btree)]\n    ///     author: String\n    /// }\n    ///\n    /// #[reducer]\n    /// fn find_books_by(ctx: &ReducerContext, author: String) {\n    ///     let book: &book__TableHandle = ctx.db.book();\n    ///\n    ///     log::debug!(\"looking up books by {author}...\");\n    ///     for book in book.author().filter(&author) {\n    ///         log::debug!(\"- {book:?}\");\n    ///     }\n    /// }\n    /// # }\n    /// ```\n    /// See the [`#[table]`](macro@crate::table) macro for more information.\n    pub db: Local,\n\n    #[cfg(feature = \"rand08\")]\n    rng: std::cell::OnceCell<StdbRng>,\n    /// A counter used for generating UUIDv7 values.\n    /// **Note:** must be 0..=u32::MAX\n    #[cfg(feature = \"rand\")]\n    counter_uuid: Cell<u32>,\n}\n\nimpl ReducerContext {\n    #[doc(hidden)]\n    pub fn __dummy() -> Self {\n        Self {\n            db: Local {},\n            sender: Identity::__dummy(),\n            timestamp: Timestamp::UNIX_EPOCH,\n            connection_id: None,\n            sender_auth: AuthCtx::internal(),\n            #[cfg(feature = \"rand08\")]\n            rng: std::cell::OnceCell::new(),\n            #[cfg(feature = \"rand\")]\n            counter_uuid: Cell::new(0),\n        }\n    }\n\n    #[doc(hidden)]\n    fn new(db: Local, sender: Identity, connection_id: Option<ConnectionId>, timestamp: Timestamp) -> Self {\n        Self {\n            db,\n            sender,\n            timestamp,\n            connection_id,\n            sender_auth: AuthCtx::from_connection_id_opt(connection_id),\n            #[cfg(feature = \"rand08\")]\n            rng: std::cell::OnceCell::new(),\n            #[cfg(feature = \"rand\")]\n            counter_uuid: Cell::new(0),\n        }\n    }\n\n    /// The `Identity` of the client that invoked the reducer.\n    pub fn sender(&self) -> Identity {\n        self.sender\n    }\n\n    /// The `ConnectionId` of the client that invoked the reducer.\n    ///\n    /// Will be `None` for certain reducers invoked automatically by the host,\n    /// including `init` and scheduled reducers.\n    pub fn connection_id(&self) -> Option<ConnectionId> {\n        self.connection_id\n    }\n\n    /// Returns the authorization information for the caller of this reducer.\n    pub fn sender_auth(&self) -> &AuthCtx {\n        &self.sender_auth\n    }\n\n    /// Read the current module's [`Identity`].\n    pub fn identity(&self) -> Identity {\n        // Hypothetically, we *could* read the module identity out of the system tables.\n        // However, this would be:\n        // - Onerous, because we have no tooling to inspect the system tables from module code.\n        // - Slow (at least relatively),\n        //   because it would involve multiple host calls which hit the datastore,\n        //   as compared to a single host call which does not.\n        // As such, we've just defined a host call\n        // which reads the module identity out of the `InstanceEnv`.\n        Identity::from_byte_array(spacetimedb_bindings_sys::identity())\n    }\n\n    /// Create an anonymous (no sender) read-only view context\n    pub fn as_anonymous_read_only(&self) -> AnonymousViewContext {\n        AnonymousViewContext::default()\n    }\n\n    /// Create a sender-bound read-only view context using this reducer's caller.\n    pub fn as_read_only(&self) -> ViewContext {\n        ViewContext::new(self.sender)\n    }\n\n    ///  Create a new random [`Uuid`] `v4` using the built-in RNG.\n    /// # Example\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{reducer, ReducerContext, Uuid};\n    ///\n    /// #[reducer]\n    /// fn generate_uuid_v4(ctx: &ReducerContext) -> Uuid {\n    ///     let uuid = ctx.new_uuid_v4();\n    ///     log::info!(uuid);\n    /// }\n    /// # }\n    /// ```\n    #[cfg(feature = \"rand\")]\n    pub fn new_uuid_v4(&self) -> anyhow::Result<Uuid> {\n        let mut bytes = [0u8; 16];\n        self.rng().try_fill_bytes(&mut bytes)?;\n        Ok(Uuid::from_random_bytes_v4(bytes))\n    }\n\n    /// Create a new sortable [`Uuid`] `v7` using the built-in RNG, counter and timestamp.\n    ///\n    /// # Example\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{reducer, ReducerContext, Uuid};\n    ///\n    /// #[reducer]\n    /// fn generate_uuid_v7(ctx: &ReducerContext) -> Result<Uuid, Box<dyn std::error::Error>> {\n    ///     let uuid = ctx.new_uuid_v7()?;\n    ///     log::info!(uuid);\n    /// }\n    /// # }\n    /// ```\n    #[cfg(feature = \"rand\")]\n    pub fn new_uuid_v7(&self) -> anyhow::Result<Uuid> {\n        let mut random_bytes = [0u8; 4];\n        self.rng().try_fill_bytes(&mut random_bytes)?;\n        Uuid::from_counter_v7(&self.counter_uuid, self.timestamp, &random_bytes)\n    }\n}\n\n#[cfg(feature = \"unstable\")]\n/// The context that an anonymous transaction\n/// in [`ProcedureContext::with_tx`] is provided with.\n///\n/// Includes information about the client starting the transaction\n/// and the time of the procedure/reducer,\n/// as well as a view into the module's database.\n///\n/// If the crate was compiled with the `rand` feature, also includes faculties for random\n/// number generation.\n///\n/// Implements the `DbContext` trait for accessing views into a database.\npub struct TxContext(ReducerContext);\n\n#[cfg(feature = \"unstable\")]\nimpl AsRef<ReducerContext> for TxContext {\n    fn as_ref(&self) -> &ReducerContext {\n        &self.0\n    }\n}\n\n#[cfg(feature = \"unstable\")]\nimpl Deref for TxContext {\n    type Target = ReducerContext;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\n/// The context that any procedure is provided with.\n///\n/// Each procedure must accept `&mut ProcedureContext` as its first argument.\n///\n/// Includes information about the client calling the procedure and the time of invocation,\n/// and exposes methods for running transactions and performing side-effecting operations.\n#[non_exhaustive]\n#[cfg(feature = \"unstable\")]\npub struct ProcedureContext {\n    /// The `Identity` of the client that invoked the procedure.\n    sender: Identity,\n\n    /// The time at which the procedure was started.\n    pub timestamp: Timestamp,\n\n    /// The `ConnectionId` of the client that invoked the procedure.\n    ///\n    /// Will be `None` for certain scheduled procedures.\n    connection_id: Option<ConnectionId>,\n\n    /// Methods for performing HTTP requests.\n    pub http: crate::http::HttpClient,\n    // TODO: Change rng?\n    // Complex and requires design because we may want procedure RNG to behave differently from reducer RNG,\n    // as it could actually be seeded by OS randomness rather than a deterministic source.\n    #[cfg(feature = \"rand08\")]\n    rng: std::cell::OnceCell<StdbRng>,\n    /// A counter used for generating UUIDv7 values.\n    /// **Note:** must be 0..=u32::MAX\n    // Disabled when compiling without `rand`, as both v4 and v7 UUIDs have random components.\n    #[cfg(feature = \"rand\")]\n    counter_uuid: Cell<u32>,\n}\n\n#[cfg(feature = \"unstable\")]\nimpl ProcedureContext {\n    fn new(sender: Identity, connection_id: Option<ConnectionId>, timestamp: Timestamp) -> Self {\n        Self {\n            sender,\n            timestamp,\n            connection_id,\n            http: http::HttpClient {},\n            #[cfg(feature = \"rand08\")]\n            rng: std::cell::OnceCell::new(),\n            #[cfg(feature = \"rand\")]\n            counter_uuid: Cell::new(0),\n        }\n    }\n\n    /// The `Identity` of the client that invoked the procedure.\n    pub fn sender(&self) -> Identity {\n        self.sender\n    }\n\n    /// The `ConnectionId` of the client that invoked the procedure.\n    ///\n    /// Will be `None` for certain scheduled procedures.\n    pub fn connection_id(&self) -> Option<ConnectionId> {\n        self.connection_id\n    }\n\n    /// Read the current module's [`Identity`].\n    pub fn identity(&self) -> Identity {\n        // Hypothetically, we *could* read the module identity out of the system tables.\n        // However, this would be:\n        // - Onerous, because we have no tooling to inspect the system tables from module code.\n        // - Slow (at least relatively),\n        //   because it would involve multiple host calls which hit the datastore,\n        //   as compared to a single host call which does not.\n        // As such, we've just defined a host call\n        // which reads the module identity out of the `InstanceEnv`.\n        Identity::from_byte_array(spacetimedb_bindings_sys::identity())\n    }\n\n    /// Suspend execution until approximately `Timestamp`.\n    ///\n    /// This will update `self.timestamp` to the new time after execution resumes.\n    ///\n    /// Actual time suspended may not be exactly equal to `duration`.\n    /// Callers should read `self.timestamp` after resuming to determine the new time.\n    ///\n    /// ```no_run\n    /// # use std::time::Duration;\n    /// # use spacetimedb::{procedure, ProcedureContext};\n    /// # #[procedure]\n    /// # fn sleep_one_second(ctx: &mut ProcedureContext) {\n    /// let prev_time = ctx.timestamp;\n    /// let target = prev_time + Duration::from_secs(1);\n    /// ctx.sleep_until(target);\n    /// let new_time = ctx.timestamp;\n    /// let actual_delta = new_time.duration_since(prev_time).unwrap();\n    /// log::info!(\"Slept from {prev_time} to {new_time}, a total of {actual_delta:?}\");\n    /// # }\n    /// ```\n    // TODO(procedure-sleep-until): remove this method\n    #[cfg(feature = \"unstable\")]\n    pub fn sleep_until(&mut self, timestamp: Timestamp) {\n        let new_time = sys::procedure::sleep_until(timestamp.to_micros_since_unix_epoch());\n        let new_time = Timestamp::from_micros_since_unix_epoch(new_time);\n        self.timestamp = new_time;\n    }\n\n    /// Acquire a mutable transaction\n    /// and execute `body` with read-write access to the database.\n    ///\n    /// If `body` panics, the transaction will be rolled back,\n    /// and the calling procedure terminated.\n    /// Otherwise, the transaction will be committed and its mutations persisted.\n    /// Prefer calling [`Self::try_with_tx`]\n    /// and returning a `Result` for signaling errors\n    /// to allow the calling procedure to handle the transaction's failure.\n    ///\n    /// Regardless of the transaction's success or failure,\n    /// the return value of `body` is not persisted to the commitlog\n    /// or broadcast to subscribed clients.\n    /// Clients attribute mutations performed by this transaction to `Event::UnknownTransaction`.\n    ///\n    /// If the transaction fails to commit after `body` returns,\n    /// e.g., due to a conflict with a concurrent transaction,\n    /// this method will re-invoke `body` with a new transaction in order to retry.\n    /// The transaction will be retried at most once.\n    /// If it fails to commit a second time, this method will panic.\n    ///\n    /// Because `body` may be run multiple times,\n    /// and is expected to perform the same set of database operations\n    /// and return the same result on each invocation,\n    /// callers should avoid writing to any captured mutable state within `body`,\n    /// This includes interior mutability through types like [`std::cell::Cell`].\n    #[cfg(feature = \"unstable\")]\n    pub fn with_tx<T>(&mut self, body: impl Fn(&TxContext) -> T) -> T {\n        use core::convert::Infallible;\n        match self.try_with_tx::<T, Infallible>(|tx| Ok(body(tx))) {\n            Ok(v) => v,\n            Err(e) => match e {},\n        }\n    }\n\n    /// Acquire a mutable transaction\n    /// and execute `body` with read-write access to the database.\n    ///\n    /// When `body` returns `Ok`,\n    /// the transaction will be committed and its mutations persisted.\n    /// When `body` returns `Err`,\n    /// the transaction will be rolled back and its mutations discarded.\n    ///\n    /// If `body` panics, the transaction will be rolled back,\n    /// and the calling procedure terminated.\n    /// Prefer returning `Result` for signaling errors\n    /// to allow the calling procedure to handle the transaction's failure.\n    ///\n    /// Regardless of the transaction's success or failure,\n    /// the return value of `body` is not persisted to the commitlog\n    /// or broadcast to subscribed clients.\n    /// Clients attribute mutations performed by this transaction to `Event::UnknownTransaction`.\n    ///\n    /// If the transaction fails to commit after `body` returns,\n    /// e.g., due to a conflict with a concurrent transaction,\n    /// this method will re-invoke `body` with a new transaction in order to retry.\n    /// The transaction will be retried at most once.\n    /// If it fails to commit a second time, this method will panic.\n    ///\n    /// Because `body` may be run multiple times,\n    /// and is expected to perform the same set of database operations\n    /// and return the same result on each invocation,\n    /// callers should avoid writing to any captured mutable state within `body`,\n    /// This includes interior mutability through types like [`std::cell::Cell`].\n    #[cfg(feature = \"unstable\")]\n    pub fn try_with_tx<T, E>(&mut self, body: impl Fn(&TxContext) -> Result<T, E>) -> Result<T, E> {\n        let abort = || {\n            sys::procedure::procedure_abort_mut_tx()\n                .expect(\"should have a pending mutable anon tx as `procedure_start_mut_tx` preceded\")\n        };\n\n        let run = || {\n            // Start the transaction.\n\n            use core::mem;\n            let timestamp = sys::procedure::procedure_start_mut_tx().expect(\n                \"holding `&mut ProcedureContext`, so should not be in a tx already; called manually elsewhere?\",\n            );\n            let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp);\n\n            // We've resumed, so let's do the work, but first prepare the context.\n            let tx = ReducerContext::new(Local {}, self.sender, self.connection_id, timestamp);\n            let tx = TxContext(tx);\n\n            // Guard the execution of `body` with a scope-guard that `abort`s on panic.\n            // Wasmtime now supports unwinding, so we need to protect against that.\n            // We're not using `scopeguard::guard` here to avoid an extra dependency.\n            struct DoOnDrop<F: Fn()>(F);\n            impl<F: Fn()> Drop for DoOnDrop<F> {\n                fn drop(&mut self) {\n                    (self.0)();\n                }\n            }\n            let abort_guard = DoOnDrop(abort);\n            let res = body(&tx);\n            // Defuse the bomb.\n            mem::forget(abort_guard);\n            res\n        };\n\n        let mut res = run();\n\n        // Commit or roll back?\n        match res {\n            Ok(_) if sys::procedure::procedure_commit_mut_tx().is_err() => {\n                // Tried to commit, but couldn't. Retry once.\n                log::warn!(\"committing anonymous transaction failed\");\n                // NOTE(procedure,centril): there's no actual guarantee that `body`\n                // does the exact same as the time before, as the timestamps differ\n                // and due to interior mutability.\n                res = run();\n                match res {\n                    Ok(_) => sys::procedure::procedure_commit_mut_tx().expect(\"transaction retry failed again\"),\n                    Err(_) => abort(),\n                }\n            }\n            Ok(_) => {}\n            Err(_) => abort(),\n        }\n\n        res\n    }\n\n    ///  Create a new random [`Uuid`] `v4` using the built-in RNG.\n    /// # Example\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{procedure, ProcedureContext, Uuid};\n    ///\n    /// #[procedure]\n    /// fn generate_uuid_v4(ctx: &ProcedureContext) -> Uuid {\n    ///     let uuid = ctx.new_uuid_v4();\n    ///     log::info!(uuid);\n    ///     uuid\n    /// }\n    /// # }\n    /// ```\n    #[cfg(all(feature = \"unstable\", feature = \"rand\"))]\n    pub fn new_uuid_v4(&self) -> anyhow::Result<Uuid> {\n        let mut bytes = [0u8; 16];\n        self.rng().try_fill_bytes(&mut bytes)?;\n        Ok(Uuid::from_random_bytes_v4(bytes))\n    }\n\n    /// Create a new sortable [`Uuid`] `v7` using the built-in RNG, counter and timestamp.\n    ///\n    /// # Example\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{procedure, ProcedureContext, Uuid};\n    ///\n    /// #[procedure]\n    /// fn generate_uuid_v7(ctx: &ProcedureContext) -> Result<Uuid, Box<dyn std::error::Error>> {\n    ///     let uuid = ctx.new_uuid_v7()?;\n    ///     log::info!(uuid);\n    ///     Ok(uuid)\n    /// }\n    /// # }\n    /// ```\n    #[cfg(all(feature = \"unstable\", feature = \"rand\"))]\n    pub fn new_uuid_v7(&self) -> anyhow::Result<Uuid> {\n        let mut random_bytes = [0u8; 4];\n        self.rng().try_fill_bytes(&mut random_bytes)?;\n        Uuid::from_counter_v7(&self.counter_uuid, self.timestamp, &random_bytes)\n    }\n}\n\n/// A handle on a database with a particular table schema.\npub trait DbContext {\n    /// A view into the tables of a database.\n    ///\n    /// This type is specialized on the database's particular schema.\n    ///\n    /// Methods on the `DbView` type will allow querying tables defined by the module.\n    type DbView;\n\n    /// Get a view into the tables.\n    ///\n    /// This method is provided for times when a programmer wants to be generic over the `DbContext` type.\n    /// Concrete-typed code is expected to read the `.db` field off the particular `DbContext` implementor.\n    /// Currently, being this generic is only meaningful in clients,\n    /// as `ReducerContext` is the only implementor of `DbContext` within modules.\n    fn db(&self) -> &Self::DbView;\n}\n\nimpl DbContext for AnonymousViewContext {\n    type DbView = LocalReadOnly;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n}\n\nimpl DbContext for ReducerContext {\n    type DbView = Local;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n}\n\n#[cfg(feature = \"unstable\")]\nimpl DbContext for TxContext {\n    type DbView = Local;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n}\n\nimpl DbContext for ViewContext {\n    type DbView = LocalReadOnly;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n}\n\n// `ProcedureContext` is *not* a `DbContext`\n// but a `TxContext` derived from it is.\n\n/// Allows accessing the local database attached to the module.\n///\n/// This slightly strange type appears to have no methods, but that is misleading.\n/// The `#[table]` macro uses the trait system to add table accessors to this type.\n/// These are generated methods that allow you to access specific tables.\n#[non_exhaustive]\npub struct Local {}\n\n/// The [JWT] of an [`AuthCtx`].\n///\n/// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n#[non_exhaustive]\npub struct JwtClaims {\n    payload: String,\n    parsed: OnceCell<serde_json::Value>,\n    audience: OnceCell<Vec<String>>,\n}\n\n/// Authentication information for the caller of a reducer.\n#[derive(Clone)]\npub struct AuthCtx {\n    is_internal: bool,\n    // NOTE(jsdt): cannot directly use a `LazyCell` without making this struct generic,\n    // which would cause `ReducerContext` to become generic as well.\n    jwt: Rc<dyn Deref<Target = Option<JwtClaims>>>,\n}\n\nimpl AuthCtx {\n    /// Creates an [`AuthCtx`] both for cases where there's a [`ConnectionId`]\n    /// and for when there isn't.\n    fn from_connection_id_opt(conn_id: Option<ConnectionId>) -> Self {\n        conn_id.map(Self::from_connection_id).unwrap_or_else(Self::internal)\n    }\n\n    fn new(is_internal: bool, jwt_fn: impl FnOnce() -> Option<JwtClaims> + 'static) -> Self {\n        AuthCtx {\n            is_internal,\n            jwt: Rc::new(LazyCell::new(jwt_fn)),\n        }\n    }\n\n    /// Creates an [`AuthCtx`] for an internal call, with no [JWT].\n    /// This represents a scheduled reducer.\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    pub fn internal() -> AuthCtx {\n        Self::new(true, || None)\n    }\n\n    /// Creates an [`AuthCtx`] using the json claims from a [JWT].\n    /// This can be used to write unit tests.\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    pub fn from_jwt_payload(jwt_payload: String) -> AuthCtx {\n        Self::new(false, move || Some(JwtClaims::new(jwt_payload)))\n    }\n\n    /// Creates an [`AuthCtx`] that reads the [JWT] for the given connection id.\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    fn from_connection_id(connection_id: ConnectionId) -> AuthCtx {\n        Self::new(false, move || rt::get_jwt(connection_id).map(JwtClaims::new))\n    }\n\n    /// Returns whether this reducer was spawned from inside the database.\n    pub fn is_internal(&self) -> bool {\n        self.is_internal\n    }\n\n    /// Checks if there is a [JWT] without loading it.\n    /// If [`AuthCtx::is_internal`] returns true, this will return false.\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    pub fn has_jwt(&self) -> bool {\n        self.jwt.is_some()\n    }\n\n    /// Loads the [JWT].\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    pub fn jwt(&self) -> Option<&JwtClaims> {\n        self.jwt.as_ref().deref().as_ref()\n    }\n}\n\nimpl JwtClaims {\n    fn new(jwt: String) -> Self {\n        Self {\n            payload: jwt,\n            parsed: OnceCell::new(),\n            audience: OnceCell::new(),\n        }\n    }\n\n    fn get_parsed(&self) -> &serde_json::Value {\n        self.parsed\n            .get_or_init(|| serde_json::from_str(&self.payload).expect(\"Failed to parse JWT payload\"))\n    }\n\n    /// Returns the tokens subject, from the sub claim.\n    pub fn subject(&self) -> &str {\n        self.get_parsed()\n            .get(\"sub\")\n            .expect(\"Missing 'sub' claim\")\n            .as_str()\n            .expect(\"Token 'sub' claim is not a string\")\n    }\n\n    /// Returns the issuer for these credentials, from the iss claim.\n    pub fn issuer(&self) -> &str {\n        self.get_parsed().get(\"iss\").unwrap().as_str().unwrap()\n    }\n\n    fn extract_audience(&self) -> Vec<String> {\n        let Some(aud) = self.get_parsed().get(\"aud\") else {\n            return Vec::new();\n        };\n        match aud {\n            serde_json::Value::String(s) => vec![s.clone()],\n            serde_json::Value::Array(arr) => arr.iter().filter_map(|v| v.as_str().map(String::from)).collect(),\n            _ => panic!(\"Unexpected type for 'aud' claim in JWT\"),\n        }\n    }\n\n    /// Returns the audience for these credentials, from the aud claim.\n    pub fn audience(&self) -> &[String] {\n        self.audience.get_or_init(|| self.extract_audience())\n    }\n\n    /// Returns the identity for these credentials, which is\n    /// based on the iss and sub claims.\n    pub fn identity(&self) -> Identity {\n        Identity::from_claims(self.issuer(), self.subject())\n    }\n\n    /// Get the whole JWT payload as a json string.\n    ///\n    /// This method is intended for parsing custom claims,\n    /// beyond the methods offered by [`JwtClaims`].\n    pub fn raw_payload(&self) -> &str {\n        &self.payload\n    }\n}\n/// The read-only version of [`Local`]\n#[non_exhaustive]\npub struct LocalReadOnly {}\n\n// #[cfg(target_arch = \"wasm32\")]\n// #[global_allocator]\n// static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;\n\n// This should guarantee in most cases that we don't have to reallocate an iterator\n// buffer, unless there's a single row that serializes to >1 MiB.\nconst DEFAULT_BUFFER_CAPACITY: usize = spacetimedb_primitives::ROW_ITER_CHUNK_SIZE * 2;\n\n/// Queries and returns the `table_id` associated with the given (table) `name`.\n///\n/// Panics if the table does not exist.\n#[doc(hidden)]\npub fn table_id_from_name(table_name: &str) -> TableId {\n    sys::table_id_from_name(table_name).unwrap_or_else(|_| {\n        panic!(\"Failed to get table with name: {table_name}\");\n    })\n}\n\nthread_local! {\n    /// A global pool of buffers used for iteration.\n    // This gets optimized away to a normal global since wasm32 doesn't have threads by default.\n    static ITER_BUFS: RefCell<Vec<Vec<u8>>> = const { RefCell::new(Vec::new()) };\n}\n\nstruct IterBuf {\n    buf: Vec<u8>,\n}\n\nimpl IterBuf {\n    /// Take a buffer from the pool of buffers for row iterators, if one exists. Otherwise, allocate a new one.\n    fn take() -> Self {\n        let buf = ITER_BUFS\n            .with_borrow_mut(|v| v.pop())\n            .unwrap_or_else(|| Vec::with_capacity(DEFAULT_BUFFER_CAPACITY));\n        Self { buf }\n    }\n\n    fn serialize<T: Serialize + ?Sized>(val: &T) -> Result<Self, bsatn::EncodeError> {\n        let mut buf = IterBuf::take();\n        buf.serialize_into(val)?;\n        Ok(buf)\n    }\n\n    #[inline]\n    fn serialize_into<T: Serialize + ?Sized>(&mut self, val: &T) -> Result<(), bsatn::EncodeError> {\n        bsatn::to_writer(&mut **self, val)\n    }\n}\n\nimpl Drop for IterBuf {\n    fn drop(&mut self) {\n        self.buf.clear();\n        let buf = std::mem::take(&mut self.buf);\n        ITER_BUFS.with_borrow_mut(|v| v.push(buf));\n    }\n}\n\nimpl AsRef<[u8]> for IterBuf {\n    fn as_ref(&self) -> &[u8] {\n        &self.buf\n    }\n}\n\nimpl std::ops::Deref for IterBuf {\n    type Target = Vec<u8>;\n    fn deref(&self) -> &Self::Target {\n        &self.buf\n    }\n}\nimpl std::ops::DerefMut for IterBuf {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.buf\n    }\n}\n\n#[cfg(feature = \"unstable\")]\n#[macro_export]\nmacro_rules! volatile_nonatomic_schedule_immediate {\n    ($($args:tt)*) => {\n        $crate::__volatile_nonatomic_schedule_immediate_impl!([] [$($args)*])\n    };\n}\n\n#[cfg(feature = \"unstable\")]\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! __volatile_nonatomic_schedule_immediate_impl {\n    ([$repeater:path] [($($args:tt)*)]) => {\n        $crate::__volatile_nonatomic_schedule_immediate_impl!(@process_args $repeater, ($($args)*))\n    };\n    ([$($cur:tt)*] [$next:tt $($rest:tt)*]) => {\n        $crate::__volatile_nonatomic_schedule_immediate_impl!([$($cur)* $next] [$($rest)*])\n    };\n    (@process_args $repeater:path, ($($args:expr),* $(,)?)) => {\n        $crate::__volatile_nonatomic_schedule_immediate_impl!(@call $repeater, ($($args),*))\n    };\n    (@call $repeater:path, ($($args:expr),*)) => {\n        if false {\n            let _ = $repeater(&$crate::ReducerContext::__dummy(), $($args,)*);\n        } else {\n            $crate::rt::volatile_nonatomic_schedule_immediate::<_, _, $repeater>($repeater, ($($args,)*))\n        }\n    };\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_single_audience() {\n        let example_payload = r#\"\n        {\n          \"iss\": \"https://securetoken.google.com/my-project-id\",\n          \"aud\": \"my-project-id\",\n          \"auth_time\": 1695560000,\n          \"user_id\": \"abc123XYZ\",\n          \"sub\": \"abc123XYZ\",\n          \"iat\": 1695560100,\n          \"exp\": 1695563700,\n          \"email\": \"user@example.com\",\n          \"email_verified\": true,\n          \"firebase\": {\n            \"identities\": {\n              \"email\": [\"user@example.com\"]\n            },\n            \"sign_in_provider\": \"password\"\n          },\n          \"name\": \"Jane Doe\",\n          \"picture\": \"https://lh3.googleusercontent.com/a-/profile.jpg\"\n        }\n        \"#;\n        let auth = AuthCtx::from_jwt_payload(example_payload.to_string());\n        let audience = auth.jwt().unwrap().audience();\n        assert_eq!(audience.len(), 1);\n        assert_eq!(audience, &[\"my-project-id\".to_string()]);\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/log_stopwatch.rs",
    "content": "/// TODO(this PR): docs\npub struct LogStopwatch {\n    stopwatch_id: u32,\n}\n\nimpl LogStopwatch {\n    pub fn new(name: &str) -> Self {\n        let name = name.as_bytes();\n        let id = unsafe { spacetimedb_bindings_sys::raw::console_timer_start(name.as_ptr(), name.len()) };\n        Self { stopwatch_id: id }\n    }\n\n    pub fn end(self) {\n        // just drop self\n    }\n}\n\nimpl std::ops::Drop for LogStopwatch {\n    fn drop(&mut self) {\n        unsafe {\n            spacetimedb_bindings_sys::raw::console_timer_end(self.stopwatch_id);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/logger.rs",
    "content": "//! Defines our panic hook and that `log` will log to the console.\n\nuse crate::sys;\nuse std::sync::Mutex;\nuse std::{fmt, panic};\n\n/// Registers the panic hook to our own.\n#[unsafe(no_mangle)]\nextern \"C\" fn __preinit__00_panic_hook() {\n    panic::set_hook(Box::new(panic_hook));\n}\n\n/// Our own panic hook logging to the console.\nfn panic_hook(info: &panic::PanicHookInfo) {\n    // Try to look into some string types we know (`&'static str` and `String`).\n    let msg = match info.payload().downcast_ref::<&'static str>() {\n        Some(s) => *s,\n        None => match info.payload().downcast_ref::<String>() {\n            Some(s) => &s[..],\n            None => \"Box<dyn Any>\",\n        },\n    };\n\n    // Log the panic message to the console.\n    let location = info.location();\n    sys::console_log(\n        sys::LogLevel::Panic,\n        None,\n        location.map(|l| l.file()),\n        location.map(|l| l.line()),\n        msg,\n    )\n}\n\nstruct Logger {\n    // `Mutex` is fine here because WASM is single-threaded;\n    // this is actually basically a `RefCell`\n    buf: Mutex<String>,\n}\n\nconst MAX_BUF_SIZE: usize = 0x4000; // 16 KiB\n\nimpl log::Log for Logger {\n    fn enabled(&self, _metadata: &log::Metadata) -> bool {\n        true\n    }\n\n    fn log(&self, record: &log::Record) {\n        // Translate `log`'s levels to `sys`.\n        let level = match record.metadata().level() {\n            log::Level::Error => sys::LogLevel::Error,\n            log::Level::Warn => sys::LogLevel::Warn,\n            log::Level::Info => sys::LogLevel::Info,\n            log::Level::Debug => sys::LogLevel::Debug,\n            log::Level::Trace => sys::LogLevel::Trace,\n        };\n\n        // Write the message to the buffer.\n        let buf = &mut *self.buf.lock().unwrap();\n        buf.clear();\n        fmt::write(buf, *record.args()).unwrap();\n\n        // Log the buffer to the console.\n        sys::console_log(level, Some(record.target()), record.file(), record.line(), buf);\n\n        // If we allocated above `MAX_BUF_SIZE`, make sure we shrink below it.\n        buf.shrink_to(MAX_BUF_SIZE);\n    }\n\n    fn flush(&self) {}\n}\n\n/// Stores the buffer used for logging.\nstatic LOGGER: Logger = Logger {\n    buf: Mutex::new(String::new()),\n};\n\n/// Registers our logger unless already set.\n/// The maximum level is `Trace`.\n#[unsafe(no_mangle)]\nextern \"C\" fn __preinit__15_init_log() {\n    // if the user wants to set their own logger, that's fine\n    if log::set_logger(&LOGGER).is_ok() {\n        log::set_max_level(log::LevelFilter::Trace);\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/rng.rs",
    "content": "#[cfg(feature = \"unstable\")]\nuse crate::ProcedureContext;\nuse crate::{rand, ReducerContext};\nuse core::cell::UnsafeCell;\nuse core::marker::PhantomData;\nuse rand::distributions::{Distribution, Standard};\nuse rand::rngs::StdRng;\nuse rand::{RngCore, SeedableRng};\nuse spacetimedb_lib::Timestamp;\n\nimpl ReducerContext {\n    /// Generates a random value.\n    ///\n    /// Similar to [`rand::random()`], but using [`StdbRng`] instead.\n    ///\n    /// See also [`ReducerContext::rng()`].\n    pub fn random<T>(&self) -> T\n    where\n        Standard: Distribution<T>,\n    {\n        Standard.sample(&mut self.rng())\n    }\n\n    /// Retrieve the random number generator for this reducer transaction,\n    /// seeded by the timestamp of the reducer call.\n    ///\n    /// If you only need a single random value, you can use [`ReducerContext::random()`].\n    ///\n    /// # Examples\n    ///\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{reducer, ReducerContext};\n    /// use rand::Rng;\n    ///\n    /// #[spacetimedb::reducer]\n    /// fn rng_demo(ctx: &spacetimedb::ReducerContext) {\n    ///     // Can be used in method chaining style:\n    ///     let digit = ctx.rng().gen_range(0..=9);\n    ///\n    ///     // Or, cache locally for reuse:\n    ///     let mut rng = ctx.rng();\n    ///     let floats: Vec<f32> = rng.sample_iter(rand::distributions::Standard).collect();\n    /// }\n    /// # }\n    /// ```\n    ///\n    /// For more information, see [`StdbRng`] and [`rand::Rng`].\n    pub fn rng(&self) -> &StdbRng {\n        self.rng.get_or_init(|| StdbRng::seed_from_ts(self.timestamp))\n    }\n}\n\n#[cfg(feature = \"unstable\")]\nimpl ProcedureContext {\n    /// Generates a random value.\n    ///\n    /// Similar to [`rand::random()`], but using [`StdbRng`] instead.\n    ///\n    /// See also [`ProcedureContext::rng()`].\n    #[cfg(feature = \"unstable\")]\n    pub fn random<T>(&self) -> T\n    where\n        Standard: Distribution<T>,\n    {\n        Standard.sample(&mut self.rng())\n    }\n\n    /// Retrieve the random number generator for this procedure transaction,\n    /// seeded by the timestamp of the procedure call.\n    ///\n    /// If you only need a single random value, you can use [`ProcedureContext::random()`].\n    ///\n    /// # Examples\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{procedure, ProcedureContext};\n    /// use rand::Rng;\n    ///\n    /// #[spacetimedb::procedure]\n    /// fn rng_demo(ctx: &spacetimedb::ProcedureContext) {\n    ///     // Can be used in method chaining style:\n    ///     let digit = ctx.rng().gen_range(0..=9);\n    ///\n    ///     // Or, cache locally for reuse:\n    ///     let mut rng = ctx.rng();\n    ///    let floats: Vec<f32> = rng.sample_iter(rand::distributions::Standard).collect();\n    /// }\n    /// # }\n    /// ```\n    ///\n    /// For more information, see [`StdbRng`] and [`rand::Rng`].\n    #[cfg(feature = \"unstable\")]\n    pub fn rng(&self) -> &StdbRng {\n        self.rng.get_or_init(|| StdbRng::seed_from_ts(self.timestamp))\n    }\n}\n\n/// A reference to the random number generator for this reducer call.\n///\n/// An instance can be obtained via [`ReducerContext::rng()`]. Import\n/// [`rand::Rng`] in order to access many useful random algorithms.\n///\n/// `StdbRng` uses the same PRNG as `rand`'s [`StdRng`]. Note, however, that\n/// because it is seeded from a publicly-known timestamp, it is not\n/// cryptographically secure.\n///\n/// You may be looking for a level of reproducibility that's finer-grained\n/// than \"if it happens at the exact same time, you get the same result\" --\n/// if so, just seed an [`StdRng`] directly, or use another rng like those\n/// listed [here](https://rust-random.github.io/book/guide-rngs.html).\n/// Just note that you must not store any state, including an rng, in a global\n/// variable or any other in-WASM side channel. Any and all state persisted\n/// across reducer calls _must_ be stored in the database.\npub struct StdbRng {\n    // Comments in the rand crate claim RefCell can have an overhead of up to 15%,\n    // and so they use an UnsafeCell instead:\n    // <https://docs.rs/rand/0.8.5/src/rand/rngs/thread.rs.html#20-32>\n    // This is safe as long as no method on `StdRngCell` is re-entrant.\n    rng: UnsafeCell<StdRng>,\n\n    // !Send + !Sync\n    _marker: PhantomData<*mut ()>,\n}\n\nimpl StdbRng {\n    /// Seeds a [`StdbRng`] from a timestamp.\n    fn seed_from_ts(timestamp: Timestamp) -> Self {\n        Self {\n            rng: StdRng::seed_from_u64(timestamp.to_micros_since_unix_epoch() as u64).into(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl RngCore for StdbRng {\n    fn next_u32(&mut self) -> u32 {\n        (&*self).next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        (&*self).next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        (&*self).fill_bytes(dest)\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand08::Error> {\n        (&*self).try_fill_bytes(dest)\n    }\n}\n\nimpl RngCore for &StdbRng {\n    #[inline(always)]\n    fn next_u32(&mut self) -> u32 {\n        // SAFETY: We must make sure to stop using `rng` before anyone else\n        // creates another mutable reference\n        let rng = unsafe { &mut *self.rng.get() };\n        rng.next_u32()\n    }\n\n    #[inline(always)]\n    fn next_u64(&mut self) -> u64 {\n        // SAFETY: We must make sure to stop using `rng` before anyone else\n        // creates another mutable reference\n        let rng = unsafe { &mut *self.rng.get() };\n        rng.next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        // SAFETY: We must make sure to stop using `rng` before anyone else\n        // creates another mutable reference\n        let rng = unsafe { &mut *self.rng.get() };\n        rng.fill_bytes(dest)\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand08::Error> {\n        // SAFETY: We must make sure to stop using `rng` before anyone else\n        // creates another mutable reference\n        let rng = unsafe { &mut *self.rng.get() };\n        rng.try_fill_bytes(dest)\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/rt.rs",
    "content": "#![deny(unsafe_op_in_unsafe_fn)]\n\nuse crate::query_builder::{FromWhere, HasCols, LeftSemiJoin, RawQuery, RightSemiJoin, Table as QbTable};\nuse crate::table::IndexAlgo;\nuse crate::{sys, AnonymousViewContext, IterBuf, ReducerContext, ReducerResult, SpacetimeType, Table, ViewContext};\nuse spacetimedb_lib::bsatn::EncodeError;\nuse spacetimedb_lib::db::raw_def::v10::{\n    CaseConversionPolicy, ExplicitNames as RawExplicitNames, RawModuleDefV10Builder,\n};\npub use spacetimedb_lib::db::raw_def::v9::Lifecycle as LifecycleReducer;\nuse spacetimedb_lib::db::raw_def::v9::{RawIndexAlgorithm, TableType, ViewResultHeader};\nuse spacetimedb_lib::de::{self, Deserialize, DeserializeOwned, Error as _, SeqProductAccess};\nuse spacetimedb_lib::sats::typespace::TypespaceBuilder;\nuse spacetimedb_lib::sats::{impl_deserialize, impl_serialize, ProductTypeElement};\nuse spacetimedb_lib::ser::{Serialize, SerializeSeqProduct};\nuse spacetimedb_lib::{bsatn, AlgebraicType, ConnectionId, Identity, ProductType, RawModuleDef, Timestamp};\nuse spacetimedb_primitives::*;\nuse std::convert::Infallible;\nuse std::fmt;\nuse std::marker::PhantomData;\nuse std::sync::{Mutex, OnceLock};\npub use sys::raw::{BytesSink, BytesSource};\n\n#[cfg(feature = \"unstable\")]\nuse crate::{ProcedureContext, ProcedureResult};\n\npub trait IntoVec<T> {\n    fn into_vec(self) -> Vec<T>;\n}\n\nimpl<T> IntoVec<T> for Vec<T> {\n    fn into_vec(self) -> Vec<T> {\n        self\n    }\n}\n\nimpl<T> IntoVec<T> for Option<T> {\n    fn into_vec(self) -> Vec<T> {\n        self.into_iter().collect()\n    }\n}\n\n/// The `sender` invokes `reducer` at `timestamp` and provides it with the given `args`.\n///\n/// Returns an invalid buffer on success\n/// and otherwise the error is written into the fresh one returned.\npub fn invoke_reducer<'a, A: Args<'a>>(\n    reducer: impl Reducer<'a, A>,\n    ctx: &ReducerContext,\n    args: &'a [u8],\n) -> Result<(), Box<str>> {\n    // Deserialize the arguments from a bsatn encoding.\n    let SerDeArgs(args) = bsatn::from_slice(args).expect(\"unable to decode args\");\n\n    reducer.invoke(ctx, args)\n}\n\n#[cfg(feature = \"unstable\")]\npub fn invoke_procedure<'a, A: Args<'a>, Ret: IntoProcedureResult>(\n    procedure: impl Procedure<'a, A, Ret>,\n    ctx: &mut ProcedureContext,\n    args: &'a [u8],\n) -> ProcedureResult {\n    // Deserialize the arguments from a bsatn encoding.\n    let SerDeArgs(args) = bsatn::from_slice(args).expect(\"unable to decode args\");\n\n    let res = procedure.invoke(ctx, args);\n\n    res.to_result()\n}\n\n/// A trait for types representing the *execution logic* of a reducer.\n#[expect(clippy::duplicated_attributes, reason = \"false positive\")]\n#[diagnostic::on_unimplemented(\n    message = \"invalid reducer signature\",\n    label = \"this reducer signature is not valid\",\n    note = \"\",\n    note = \"reducer signatures must match the following pattern:\",\n    note = \"    `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\",\n    note = \"where each `Ti` type implements `SpacetimeType`.\",\n    note = \"\"\n)]\npub trait Reducer<'de, A: Args<'de>> {\n    fn invoke(&self, ctx: &ReducerContext, args: A) -> ReducerResult;\n}\n\n/// Invoke a caller-specific view.\n/// Returns a BSATN encoded `Vec` of rows.\npub fn invoke_view<'a, A: Args<'a>, T: ViewReturn>(\n    view: impl View<'a, A, T>,\n    ctx: ViewContext,\n    args: &'a [u8],\n) -> Vec<u8> {\n    // Deserialize the arguments from a bsatn encoding.\n    let SerDeArgs(args) = bsatn::from_slice(args).expect(\"unable to decode args\");\n    let retn = view.invoke(&ctx, args);\n    let mut buf = IterBuf::take();\n    retn.to_writer(&mut buf).expect(\"unable to encode view return value\");\n    std::mem::take(&mut *buf)\n}\n/// A trait for types representing the execution logic of a caller-specific view.\n#[expect(clippy::duplicated_attributes, reason = \"false positive\")]\n#[diagnostic::on_unimplemented(\n    message = \"invalid view signature\",\n    label = \"this view signature is not valid\",\n    note = \"\",\n    note = \"view signatures must match:\",\n    note = \"    `Fn(&ViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\",\n    note = \"where each `Ti` implements `SpacetimeType`.\",\n    note = \"\"\n)]\npub trait View<'de, A: Args<'de>, T: ViewReturn> {\n    fn invoke(&self, ctx: &ViewContext, args: A) -> T;\n}\n\n/// Invoke an anonymous view.\n/// Returns a BSATN encoded `Vec` of rows.\npub fn invoke_anonymous_view<'a, A: Args<'a>, T: ViewReturn>(\n    view: impl AnonymousView<'a, A, T>,\n    ctx: AnonymousViewContext,\n    args: &'a [u8],\n) -> Vec<u8> {\n    // Deserialize the arguments from a bsatn encoding.\n    let SerDeArgs(args) = bsatn::from_slice(args).expect(\"unable to decode args\");\n    let retn = view.invoke(&ctx, args);\n    let mut buf = IterBuf::take();\n    retn.to_writer(&mut buf).expect(\"unable to encode view return value\");\n    std::mem::take(&mut *buf)\n}\n/// A trait for types representing the execution logic of an anonymous view.\n#[expect(clippy::duplicated_attributes, reason = \"false positive\")]\n#[diagnostic::on_unimplemented(\n    message = \"invalid anonymous view signature\",\n    label = \"this view signature is not valid\",\n    note = \"\",\n    note = \"anonymous view signatures must match:\",\n    note = \"    `Fn(&AnonymousViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\",\n    note = \"where each `Ti` implements `SpacetimeType`.\",\n    note = \"\"\n)]\npub trait AnonymousView<'de, A: Args<'de>, T: ViewReturn> {\n    fn invoke(&self, ctx: &AnonymousViewContext, args: A) -> T;\n}\n\n/// A trait for types that can *describe* a callable function such as a reducer or view.\npub trait FnInfo: ExplicitNames {\n    /// The type of function to invoke.\n    type Invoke;\n\n    #[cfg_attr(\n        feature = \"unstable\",\n        doc = \"One of [`FnKindReducer`], [`FnKindProcedure`] or [`FnKindView`].\"\n    )]\n    #[cfg_attr(not(feature = \"unstable\"), doc = \"Either [`FnKindReducer`] or [`FnKindView`].\")]\n    ///\n    /// Used as a type argument to [`ExportFunctionForScheduledTable`] and [`scheduled_typecheck`].\n    /// See <https://willcrichton.net/notes/defeating-coherence-rust/> for details on this technique.\n    type FnKind;\n\n    /// The name of the function.\n    const NAME: &'static str;\n\n    /// The lifecycle of the function, if there is one.\n    const LIFECYCLE: Option<LifecycleReducer> = None;\n\n    /// A description of the parameter names of the function.\n    const ARG_NAMES: &'static [Option<&'static str>];\n\n    /// The function to invoke.\n    const INVOKE: Self::Invoke;\n\n    /// The return type of this function.\n    /// Currently only implemented for views.\n    fn return_type(_ts: &mut impl TypespaceBuilder) -> Option<AlgebraicType> {\n        None\n    }\n}\n\n#[cfg(feature = \"unstable\")]\npub trait Procedure<'de, A: Args<'de>, Ret: IntoProcedureResult> {\n    fn invoke(&self, ctx: &mut ProcedureContext, args: A) -> Ret;\n}\n\n/// A trait of types representing the arguments of a reducer, procedure or view.\n///\n/// This does not include the context first argument,\n/// only the client-provided args.\n/// As such, the same trait can be used for all sorts of exported functions.\npub trait Args<'de>: Sized {\n    /// How many arguments does the reducer accept?\n    const LEN: usize;\n\n    /// Deserialize the arguments from the sequence `prod` which knows when there are next elements.\n    fn visit_seq_product<A: SeqProductAccess<'de>>(prod: A) -> Result<Self, A::Error>;\n\n    /// Serialize the arguments in `self` into the sequence `prod` according to the type `S`.\n    fn serialize_seq_product<S: SerializeSeqProduct>(&self, prod: &mut S) -> Result<(), S::Error>;\n\n    /// Returns the schema of the args for this function provided a `typespace`.\n    fn schema<I: FnInfo>(typespace: &mut impl TypespaceBuilder) -> ProductType;\n}\n\n/// A trait of types representing the result of executing a reducer.\n#[diagnostic::on_unimplemented(\n    message = \"`{Self}` is not a valid reducer return type\",\n    note = \"reducers cannot return values -- you can only return `()` or `Result<(), impl Display>`\"\n)]\npub trait IntoReducerResult {\n    /// Convert the result into form where there is no value\n    /// and the error message is a string.\n    fn into_result(self) -> Result<(), Box<str>>;\n}\nimpl IntoReducerResult for () {\n    #[inline]\n    fn into_result(self) -> Result<(), Box<str>> {\n        Ok(self)\n    }\n}\nimpl<E: fmt::Display> IntoReducerResult for Result<(), E> {\n    #[inline]\n    fn into_result(self) -> Result<(), Box<str>> {\n        self.map_err(|e| e.to_string().into())\n    }\n}\n\n#[cfg(feature = \"unstable\")]\n#[diagnostic::on_unimplemented(\n    message = \"The procedure return type `{Self}` does not implement `SpacetimeType`\",\n    note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\"\n)]\npub trait IntoProcedureResult: SpacetimeType + Serialize {\n    #[inline]\n    fn to_result(&self) -> ProcedureResult {\n        bsatn::to_vec(&self).expect(\"Failed to serialize procedure result\")\n    }\n}\n#[cfg(feature = \"unstable\")]\nimpl<T: SpacetimeType + Serialize> IntoProcedureResult for T {}\n\n#[diagnostic::on_unimplemented(\n    message = \"the first argument of a reducer must be `&ReducerContext`\",\n    label = \"first argument must be `&ReducerContext`\"\n)]\npub trait ReducerContextArg {\n    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerContextArg>::_ITEM\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\nimpl ReducerContextArg for &ReducerContext {}\n\n/// A trait of types that can be an argument of a reducer.\n#[diagnostic::on_unimplemented(\n    message = \"the reducer argument `{Self}` does not implement `SpacetimeType`\",\n    note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\"\n)]\npub trait ReducerArg {\n    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerArg>::_ITEM\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\nimpl<T: SpacetimeType> ReducerArg for T {}\n\n#[cfg(feature = \"unstable\")]\n#[diagnostic::on_unimplemented(\n    message = \"the first argument of a procedure must be `&mut ProcedureContext`\",\n    label = \"first argument must be `&mut ProcedureContext`\"\n)]\npub trait ProcedureContextArg {\n    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerContextArg>::_ITEM\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\n#[cfg(feature = \"unstable\")]\nimpl ProcedureContextArg for &mut ProcedureContext {}\n\n/// A trait of types that can be an argument of a procedure.\n#[cfg(feature = \"unstable\")]\n#[diagnostic::on_unimplemented(\n    message = \"the procedure argument `{Self}` does not implement `SpacetimeType`\",\n    note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\"\n)]\npub trait ProcedureArg {\n    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerArg>::_ITEM\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\n#[cfg(feature = \"unstable\")]\nimpl<T: SpacetimeType> ProcedureArg for T {}\n\n#[diagnostic::on_unimplemented(\n    message = \"The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext`\"\n)]\npub trait ViewContextArg {\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\nimpl ViewContextArg for ViewContext {}\nimpl ViewContextArg for AnonymousViewContext {}\n\n/// A trait of types that can be an argument of a view.\n#[diagnostic::on_unimplemented(\n    message = \"the view argument `{Self}` does not implement `SpacetimeType`\",\n    note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\"\n)]\npub trait ViewArg {\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\nimpl<T: SpacetimeType> ViewArg for T {}\n\n/// A trait of types that can be the return type of a view.\n#[diagnostic::on_unimplemented(message = \"Views must return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`\")]\npub trait ViewReturn {\n    #[doc(hidden)]\n    const _ITEM: () = ();\n\n    fn to_writer(self, w: &mut Vec<u8>) -> Result<(), EncodeError>;\n}\n\nimpl<T: SpacetimeType + Serialize> ViewReturn for Vec<T> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        bsatn::to_writer(buf, &ViewResultHeader::RowData)?;\n        bsatn::to_writer(buf, &self)\n    }\n}\n\nimpl<T: SpacetimeType + Serialize> ViewReturn for Option<T> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        bsatn::to_writer(buf, &ViewResultHeader::RowData)?;\n        bsatn::to_writer(buf, self.as_slice())\n    }\n}\n\nimpl<T: SpacetimeType + Serialize> ViewReturn for RawQuery<T> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        bsatn::to_writer(buf, &ViewResultHeader::RawSql(self.sql().to_string()))\n    }\n}\n\nimpl<T: HasCols + SpacetimeType + Serialize> ViewReturn for QbTable<T> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        self.build().to_writer(buf)\n    }\n}\n\nimpl<T: HasCols + SpacetimeType + Serialize> ViewReturn for FromWhere<T> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        self.build().to_writer(buf)\n    }\n}\n\nimpl<L: HasCols + SpacetimeType + Serialize> ViewReturn for LeftSemiJoin<L> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        self.build().to_writer(buf)\n    }\n}\n\nimpl<R: HasCols + SpacetimeType + Serialize, L: HasCols> ViewReturn for RightSemiJoin<R, L> {\n    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {\n        self.build().to_writer(buf)\n    }\n}\n\n/// Map the correct dispatcher based on the `Ctx` type\npub struct ViewKind<Ctx> {\n    _marker: PhantomData<Ctx>,\n}\n\npub trait ViewKindTrait {\n    type InvokeFn;\n}\n\nimpl ViewKindTrait for ViewKind<ViewContext> {\n    type InvokeFn = ViewFn;\n}\n\nimpl ViewKindTrait for ViewKind<AnonymousViewContext> {\n    type InvokeFn = AnonymousFn;\n}\n\n/// Invoke the correct dispatcher based on the `Ctx` type\npub struct ViewDispatcher<Ctx> {\n    _marker: PhantomData<Ctx>,\n}\n\nimpl ViewDispatcher<ViewContext> {\n    #[inline]\n    pub fn invoke<'a, A, T, V>(view: V, ctx: ViewContext, args: &'a [u8]) -> Vec<u8>\n    where\n        A: Args<'a>,\n        T: ViewReturn,\n        V: View<'a, A, T>,\n    {\n        invoke_view(view, ctx, args)\n    }\n}\n\nimpl ViewDispatcher<AnonymousViewContext> {\n    #[inline]\n    pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec<u8>\n    where\n        A: Args<'a>,\n        T: ViewReturn,\n        V: AnonymousView<'a, A, T>,\n    {\n        invoke_anonymous_view(view, ctx, args)\n    }\n}\n\n/// Register the correct dispatcher based on the `Ctx` type\npub struct ViewRegistrar<Ctx> {\n    _marker: PhantomData<Ctx>,\n}\n\nimpl ViewRegistrar<ViewContext> {\n    #[inline]\n    pub fn register<'a, A, I, T, V>(view: V)\n    where\n        A: Args<'a>,\n        T: ViewReturn,\n        I: FnInfo<Invoke = ViewFn>,\n        V: View<'a, A, T>,\n    {\n        register_view::<A, I, T>(view)\n    }\n}\n\nimpl ViewRegistrar<AnonymousViewContext> {\n    #[inline]\n    pub fn register<'a, A, I, T, V>(view: V)\n    where\n        A: Args<'a>,\n        T: ViewReturn,\n        I: FnInfo<Invoke = AnonymousFn>,\n        V: AnonymousView<'a, A, T>,\n    {\n        register_anonymous_view::<A, I, T>(view)\n    }\n}\n\n/// Assert that a reducer type-checks with a given type.\npub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>)\nwhere\n    Row: SpacetimeType + Serialize + Deserialize<'de>,\n{\n    core::mem::forget(_x);\n}\n\n/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for reducers.\npub struct FnKindReducer {\n    _never: Infallible,\n}\n\n#[cfg(feature = \"unstable\")]\n/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for procedures.\n///\n/// Holds the procedure's return type in order to avoid an error due to an unconstrained type argument.\npub struct FnKindProcedure<Ret> {\n    _never: Infallible,\n    _ret_ty: PhantomData<fn() -> Ret>,\n}\n\n/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for views.\n///\n/// Because views are never scheduled, we don't need to distinguish between anonymous or sender-identity views,\n/// or to include their return type.\npub struct FnKindView {\n    _never: Infallible,\n}\n\n/// Trait bound for [`scheduled_typecheck`], which the [`crate::table`] macro generates to typecheck scheduled functions.\n///\n/// The `FnKind` parameter here is a coherence-defeating marker, which Will Crichton calls a \"tacit parameter.\"\n/// See <https://willcrichton.net/notes/defeating-coherence-rust/> for details on this technique.\n#[cfg_attr(\n    feature = \"unstable\",\n    doc = \"It will be one of [`FnKindReducer`] or [`FnKindProcedure`] in modules that compile successfully.\"\n)]\n#[cfg_attr(\n    not(feature = \"unstable\"),\n    doc = \"It will be [`FnKindReducer`] in modules that compile successfully.\"\n)]\n///\n/// It may be [`FnKindView`], but that will always fail to typecheck, as views cannot be used as scheduled functions.\n#[diagnostic::on_unimplemented(\n    message = \"invalid signature for scheduled table reducer or procedure\",\n    note = \"views cannot be scheduled\",\n    note = \"the scheduled function must take `{TableRow}` as its sole argument\",\n    note = \"e.g: `fn scheduled_reducer(ctx: &ReducerContext, arg: {TableRow})`\",\n    // note = \"or `fn scheduled_procedure(ctx: &mut ProcedureContext, arg: {TableRow})`\"\n)]\npub trait ExportFunctionForScheduledTable<'de, TableRow, FnKind> {}\nimpl<'de, TableRow: SpacetimeType + Serialize + Deserialize<'de>, F: Reducer<'de, (TableRow,)>>\n    ExportFunctionForScheduledTable<'de, TableRow, FnKindReducer> for F\n{\n}\n\n#[cfg(feature = \"unstable\")]\nimpl<\n        'de,\n        TableRow: SpacetimeType + Serialize + Deserialize<'de>,\n        Ret: SpacetimeType + Serialize + Deserialize<'de>,\n        F: Procedure<'de, (TableRow,), Ret>,\n    > ExportFunctionForScheduledTable<'de, TableRow, FnKindProcedure<Ret>> for F\n{\n}\n\n// the macro generates <T as SpacetimeType>::make_type::<DummyTypespace>\npub struct DummyTypespace;\nimpl TypespaceBuilder for DummyTypespace {\n    fn add(\n        &mut self,\n        _: std::any::TypeId,\n        _: Option<&'static str>,\n        _: impl FnOnce(&mut Self) -> spacetimedb_lib::AlgebraicType,\n    ) -> spacetimedb_lib::AlgebraicType {\n        unreachable!()\n    }\n}\n\n#[diagnostic::on_unimplemented(\n    message = \"the column type `{Self}` does not implement `SpacetimeType`\",\n    note = \"table column types all must implement `SpacetimeType`\",\n    note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\"\n)]\npub trait TableColumn {\n    // a little hack used in the macro to make error messages nicer. it generates <T as TableColumn>::_ITEM\n    #[doc(hidden)]\n    const _ITEM: () = ();\n}\nimpl<T: SpacetimeType> TableColumn for T {}\n\n/// Assert that the primary_key column of a scheduled table is a u64.\npub const fn assert_scheduled_table_primary_key<T: ScheduledTablePrimaryKey>() {}\n\nmod sealed {\n    pub trait Sealed {}\n}\n#[diagnostic::on_unimplemented(\n    message = \"scheduled table primary key must be a `u64`\",\n    label = \"should be `u64`, not `{Self}`\"\n)]\npub trait ScheduledTablePrimaryKey: sealed::Sealed {}\nimpl sealed::Sealed for u64 {}\nimpl ScheduledTablePrimaryKey for u64 {}\n\n/// Used in the last type parameter of `Reducer` to indicate that the\n/// context argument *should* be passed to the reducer logic.\npub struct ContextArg;\n\n/// A visitor providing a deserializer for a type `A: Args`.\nstruct ArgsVisitor<A> {\n    _marker: PhantomData<A>,\n}\n\nimpl<'de, A: Args<'de>> de::ProductVisitor<'de> for ArgsVisitor<A> {\n    type Output = A;\n\n    fn product_name(&self) -> Option<&str> {\n        None\n    }\n    fn product_len(&self) -> usize {\n        A::LEN\n    }\n    fn product_kind(&self) -> de::ProductKind {\n        de::ProductKind::ReducerArgs\n    }\n    fn visit_seq_product<Acc: SeqProductAccess<'de>>(self, prod: Acc) -> Result<Self::Output, Acc::Error> {\n        A::visit_seq_product(prod)\n    }\n    fn visit_named_product<Acc: de::NamedProductAccess<'de>>(self, _prod: Acc) -> Result<Self::Output, Acc::Error> {\n        Err(Acc::Error::named_products_not_supported())\n    }\n}\n\nmacro_rules! impl_reducer_procedure_view {\n    ($($T1:ident $(, $T:ident)*)?) => {\n        impl_reducer_procedure_view!(@impl $($T1 $(, $T)*)?);\n        $(impl_reducer_procedure_view!($($T),*);)?\n    };\n    (@impl $($T:ident),*) => {\n        // Implement `Args` for the tuple type `($($T,)*)`.\n        impl<'de, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Args<'de> for ($($T,)*) {\n            const LEN: usize = impl_reducer_procedure_view!(@count $($T)*);\n            #[allow(non_snake_case)]\n            #[allow(unused)]\n            fn visit_seq_product<Acc: SeqProductAccess<'de>>(mut prod: Acc) -> Result<Self, Acc::Error> {\n                let vis = ArgsVisitor { _marker: PhantomData::<Self> };\n                // Counts the field number; only relevant for errors.\n                let i = 0;\n                // For every element in the product, deserialize.\n                $(\n                    let $T = prod.next_element::<$T>()?.ok_or_else(|| de::Error::missing_field(i, None, &vis))?;\n                    let i = i + 1;\n                )*\n                Ok(($($T,)*))\n            }\n\n            #[allow(non_snake_case)]\n            fn serialize_seq_product<Ser: SerializeSeqProduct>(&self, _prod: &mut Ser) -> Result<(), Ser::Error> {\n                // For every element in the product, serialize.\n                let ($($T,)*) = self;\n                $(_prod.serialize_element($T)?;)*\n                Ok(())\n            }\n\n            #[inline]\n            #[allow(non_snake_case, irrefutable_let_patterns)]\n            fn schema<Info: FnInfo>(_typespace: &mut impl TypespaceBuilder) -> ProductType {\n                // Extract the names of the arguments.\n                let [.., $($T),*] = Info::ARG_NAMES else { panic!() };\n                ProductType::new(vec![\n                        $(ProductTypeElement {\n                            name: $T.map(Into::into),\n                            algebraic_type: <$T>::make_type(_typespace),\n                        }),*\n                ].into())\n            }\n        }\n\n                // Implement `Reducer<..., ContextArg>` for the tuple type `($($T,)*)`.\n        impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Reducer<'de, ($($T,)*)> for Func\n        where\n            Func: Fn(&ReducerContext, $($T),*) -> Ret,\n            Ret: IntoReducerResult\n        {\n            #[allow(non_snake_case)]\n            fn invoke(&self, ctx: &ReducerContext, args: ($($T,)*)) -> Result<(), Box<str>> {\n                let ($($T,)*) = args;\n                self(ctx, $($T),*).into_result()\n            }\n        }\n\n        #[cfg(feature = \"unstable\")]\n        impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Procedure<'de, ($($T,)*), Ret> for Func\n        where\n            Func: Fn(&mut ProcedureContext, $($T),*) -> Ret,\n            Ret: IntoProcedureResult,\n        {\n            #[allow(non_snake_case)]\n            fn invoke(&self, ctx: &mut ProcedureContext, args: ($($T,)*)) -> Ret {\n                let ($($T,)*) = args;\n                self(ctx, $($T),*)\n            }\n        }\n\n        // Implement `View<..., ViewContext>` for the tuple type `($($T,)*)`.\n        impl<'de, Func, Retn, $($T),*>\n            View<'de, ($($T,)*), Retn> for Func\n        where\n            $($T: SpacetimeType + Deserialize<'de> + Serialize,)*\n            Func: Fn(&ViewContext, $($T),*) -> Retn,\n            Retn: ViewReturn,\n        {\n            #[allow(non_snake_case)]\n            fn invoke(&self, ctx: &ViewContext, args: ($($T,)*)) -> Retn {\n                let ($($T,)*) = args;\n                self(ctx, $($T),*)\n            }\n        }\n\n        // Implement `View<..., AnonymousViewContext>` for the tuple type `($($T,)*)`.\n        impl<'de, Func, Retn, $($T),*>\n            AnonymousView<'de, ($($T,)*), Retn> for Func\n        where\n            $($T: SpacetimeType + Deserialize<'de> + Serialize,)*\n            Func: Fn(&AnonymousViewContext, $($T),*) -> Retn,\n            Retn: ViewReturn,\n        {\n            #[allow(non_snake_case)]\n            fn invoke(&self, ctx: &AnonymousViewContext, args: ($($T,)*)) -> Retn {\n                let ($($T,)*) = args;\n                self(ctx, $($T),*)\n            }\n        }\n    };\n    // Counts the number of elements in the tuple.\n    (@count $($T:ident)*) => {\n        0 $(+ impl_reducer_procedure_view!(@drop $T 1))*\n    };\n    (@drop $a:tt $b:tt) => { $b };\n}\n\nimpl_reducer_procedure_view!(\n    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF\n);\n\n/// Provides deserialization and serialization for any type `A: Args`.\nstruct SerDeArgs<A>(A);\nimpl_deserialize!(\n    [A: Args<'de>] SerDeArgs<A>,\n    de => de.deserialize_product(ArgsVisitor { _marker: PhantomData }).map(Self)\n);\nimpl_serialize!(['de, A: Args<'de>] SerDeArgs<A>, (self, ser) => {\n    let mut prod = ser.serialize_seq_product(A::LEN)?;\n    self.0.serialize_seq_product(&mut prod)?;\n    prod.end()\n});\n\n/// A trait for types that can *describe* a row-level security policy.\npub trait RowLevelSecurityInfo {\n    /// The SQL expression for the row-level security policy.\n    const SQL: &'static str;\n}\n\n/// A function which will be registered by [`register_describer`] into [`DESCRIBERS`],\n/// which will be called by [`__describe_module__`] to construct a module definition.\n///\n/// May be a closure over static data, so that e.g.\n/// [`register_row_level_security`] doesn't need to take a type parameter.\n/// Permitted by the type system to be a [`FnMut`] mutable closure,\n/// since [`DESCRIBERS`] is in a [`Mutex`] anyways,\n/// but will likely cause weird misbehaviors if a non-idempotent function is used.\ntrait DescriberFn: FnMut(&mut ModuleBuilder) + Send + 'static {}\nimpl<F: FnMut(&mut ModuleBuilder) + Send + 'static> DescriberFn for F {}\n\n/// Registers into `DESCRIBERS` a function `f` to modify the module builder.\nfn register_describer(f: impl DescriberFn) {\n    DESCRIBERS.lock().unwrap().push(Box::new(f))\n}\n\n/// Registers a describer for the `SpacetimeType` `T`.\npub fn register_reftype<T: SpacetimeType>() {\n    register_describer(|module| {\n        T::make_type(&mut module.inner);\n    })\n}\n\n/// Registers a describer for the `TableType` `T`.\npub fn register_table<T: Table>() {\n    register_describer(|module| {\n        let product_type_ref = *T::Row::make_type(&mut module.inner).as_ref().unwrap();\n        if let Some(schedule) = T::SCHEDULE {\n            module.inner.add_schedule(\n                T::TABLE_NAME,\n                schedule.scheduled_at_column,\n                schedule.reducer_or_procedure_name,\n            );\n        }\n\n        let mut table = module\n            .inner\n            .build_table(T::TABLE_NAME, product_type_ref)\n            .with_type(TableType::User)\n            .with_access(T::TABLE_ACCESS)\n            .with_event(T::IS_EVENT);\n\n        for &col in T::UNIQUE_COLUMNS {\n            table = table.with_unique_constraint(col);\n        }\n        for index in T::INDEXES {\n            table = table.with_index(index.algo.into(), index.source_name, index.accessor_name);\n        }\n        if let Some(primary_key) = T::PRIMARY_KEY {\n            table = table.with_primary_key(primary_key);\n        }\n        for &col in T::SEQUENCES {\n            table = table.with_column_sequence(col);\n        }\n        for col in T::get_default_col_values().iter_mut() {\n            table = table.with_default_column_value(col.col_id, col.value.clone())\n        }\n\n        table.finish();\n\n        module.inner.add_explicit_names(T::explicit_names());\n    })\n}\n\nimpl From<IndexAlgo<'_>> for RawIndexAlgorithm {\n    fn from(algo: IndexAlgo<'_>) -> RawIndexAlgorithm {\n        match algo {\n            IndexAlgo::BTree { columns } => RawIndexAlgorithm::BTree {\n                columns: columns.iter().copied().collect(),\n            },\n            IndexAlgo::Hash { columns } => RawIndexAlgorithm::Hash {\n                columns: columns.iter().copied().collect(),\n            },\n            IndexAlgo::Direct { column } => RawIndexAlgorithm::Direct { column: column.into() },\n        }\n    }\n}\n\n/// Registers a describer for the reducer `I` with arguments `A`.\npub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {\n    register_describer(|module| {\n        let params = A::schema::<I>(&mut module.inner);\n        if let Some(lifecycle) = I::LIFECYCLE {\n            module.inner.add_lifecycle_reducer(lifecycle, I::NAME, params);\n        } else {\n            module.inner.add_reducer(I::NAME, params);\n        }\n        module.reducers.push(I::INVOKE);\n\n        module.inner.add_explicit_names(I::explicit_names());\n    })\n}\n\n#[cfg(feature = \"unstable\")]\npub fn register_procedure<'a, A, Ret, I>(_: impl Procedure<'a, A, Ret>)\nwhere\n    A: Args<'a>,\n    Ret: SpacetimeType + Serialize,\n    I: FnInfo<Invoke = ProcedureFn>,\n{\n    register_describer(|module| {\n        let params = A::schema::<I>(&mut module.inner);\n        let ret_ty = <Ret as SpacetimeType>::make_type(&mut module.inner);\n        module.inner.add_procedure(I::NAME, params, ret_ty);\n        module.procedures.push(I::INVOKE);\n\n        module.inner.add_explicit_names(I::explicit_names());\n    })\n}\n\n/// Registers a describer for the view `I` with arguments `A` and return type `Vec<T>`.\npub fn register_view<'a, A, I, T>(_: impl View<'a, A, T>)\nwhere\n    A: Args<'a>,\n    I: FnInfo<Invoke = ViewFn>,\n    T: ViewReturn,\n{\n    register_describer(|module| {\n        let params = A::schema::<I>(&mut module.inner);\n        let return_type = I::return_type(&mut module.inner).unwrap();\n        module\n            .inner\n            .add_view(I::NAME, module.views.len(), true, false, params, return_type);\n        module.views.push(I::INVOKE);\n\n        module.inner.add_explicit_names(I::explicit_names());\n    })\n}\n\n/// Registers a describer for the anonymous view `I` with arguments `A` and return type `Vec<T>`.\npub fn register_anonymous_view<'a, A, I, T>(_: impl AnonymousView<'a, A, T>)\nwhere\n    A: Args<'a>,\n    I: FnInfo<Invoke = AnonymousFn>,\n    T: ViewReturn,\n{\n    register_describer(|module| {\n        let params = A::schema::<I>(&mut module.inner);\n        let return_type = I::return_type(&mut module.inner).unwrap();\n        module\n            .inner\n            .add_view(I::NAME, module.views_anon.len(), true, true, params, return_type);\n        module.views_anon.push(I::INVOKE);\n\n        module.inner.add_explicit_names(I::explicit_names());\n    })\n}\n\n/// Registers a row-level security policy.\npub fn register_row_level_security(sql: &'static str) {\n    register_describer(|module| {\n        module.inner.add_row_level_security(sql);\n    })\n}\n\n/// Set the case conversion policy for this module.\n///\n/// This is called by the `#[spacetimedb::settings]` attribute macro.\n/// Do not call directly; use the attribute instead:\n///\n/// ```ignore\n/// #[spacetimedb::settings]\n/// const CASE_CONVERSION_POLICY: CaseConversionPolicy = CaseConversionPolicy::SnakeCase;\n/// ```\n#[doc(hidden)]\npub fn register_case_conversion_policy(policy: CaseConversionPolicy) {\n    register_describer(move |module| {\n        module.inner.set_case_conversion_policy(policy);\n    })\n}\n\n/// A builder for a module.\n#[derive(Default)]\npub struct ModuleBuilder {\n    /// The module definition.\n    inner: RawModuleDefV10Builder,\n    /// The reducers of the module.\n    reducers: Vec<ReducerFn>,\n    /// The procedures of the module.\n    #[cfg(feature = \"unstable\")]\n    procedures: Vec<ProcedureFn>,\n    /// The client specific views of the module.\n    views: Vec<ViewFn>,\n    /// The anonymous views of the module.\n    views_anon: Vec<AnonymousFn>,\n}\n\n// Not actually a mutex; because WASM is single-threaded this basically just turns into a refcell.\nstatic DESCRIBERS: Mutex<Vec<Box<dyn DescriberFn>>> = Mutex::new(Vec::new());\n\n/// A reducer function takes in `(ReducerContext, Args)`\n/// and returns a result with a possible error message.\npub type ReducerFn = fn(&ReducerContext, &[u8]) -> ReducerResult;\nstatic REDUCERS: OnceLock<Vec<ReducerFn>> = OnceLock::new();\n\n#[cfg(feature = \"unstable\")]\npub type ProcedureFn = fn(&mut ProcedureContext, &[u8]) -> ProcedureResult;\n#[cfg(feature = \"unstable\")]\nstatic PROCEDURES: OnceLock<Vec<ProcedureFn>> = OnceLock::new();\n\n/// A view function takes in `(ViewContext, Args)` and returns a Vec of bytes.\npub type ViewFn = fn(ViewContext, &[u8]) -> Vec<u8>;\nstatic VIEWS: OnceLock<Vec<ViewFn>> = OnceLock::new();\n\n/// An anonymous view function takes in `(AnonymousViewContext, Args)` and returns a Vec of bytes.\npub type AnonymousFn = fn(AnonymousViewContext, &[u8]) -> Vec<u8>;\nstatic ANONYMOUS_VIEWS: OnceLock<Vec<AnonymousFn>> = OnceLock::new();\n\n/// Called by the host when the module is initialized\n/// to describe the module into a serialized form that is returned.\n///\n/// This is also the module's opportunity to ready `__call_reducer__`\n/// (by writing the set of `REDUCERS`).\n///\n/// To `description`, a BSATN-encoded ModuleDef` should be written,.\n/// For the time being, the definition of `ModuleDef` is not stabilized,\n/// as it is being changed by the schema proposal.\n///\n/// The `ModuleDef` is used to define tables, constraints, indexes, reducers, etc.\n/// This affords the module the opportunity\n/// to define and, to a limited extent, alter the schema at initialization time,\n/// including when modules are updated (re-publishing).\n/// After initialization, the module cannot alter the schema.\n#[unsafe(no_mangle)]\nextern \"C\" fn __describe_module__(description: BytesSink) {\n    // Collect the `module`.\n    let mut module = ModuleBuilder::default();\n    for describer in &mut *DESCRIBERS.lock().unwrap() {\n        describer(&mut module)\n    }\n\n    // Serialize the module to bsatn.\n    let module_def = module.inner.finish();\n    let module_def = RawModuleDef::V10(module_def);\n    let bytes = bsatn::to_vec(&module_def).expect(\"unable to serialize typespace\");\n\n    // Write the sets of reducers, procedures and views.\n    REDUCERS.set(module.reducers).ok().unwrap();\n    #[cfg(feature = \"unstable\")]\n    PROCEDURES.set(module.procedures).ok().unwrap();\n    VIEWS.set(module.views).ok().unwrap();\n    ANONYMOUS_VIEWS.set(module.views_anon).ok().unwrap();\n\n    // Write the bsatn data into the sink.\n    write_to_sink(description, &bytes);\n}\n\n// TODO(1.0): update `__call_reducer__` docs + for `BytesSink`.\n\n/// Called by the host to execute a reducer\n/// when the `sender` calls the reducer identified by `id` at `timestamp` with `args`.\n///\n/// The `sender_{0-3}` are the pieces of a `[u8; 32]` (`u256`) representing the sender's `Identity`.\n/// They are encoded as follows (assuming `identity.to_byte_array(): [u8; 32]`):\n/// - `sender_0` contains bytes `[0 ..8 ]`.\n/// - `sender_1` contains bytes `[8 ..16]`.\n/// - `sender_2` contains bytes `[16..24]`.\n/// - `sender_3` contains bytes `[24..32]`.\n///\n/// Note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.\n///\n/// The `conn_id_{0-1}` are the pieces of a `[u8; 16]` (`u128`) representing the callers's [`ConnectionId`].\n/// They are encoded as follows (assuming `conn_id.as_le_byte_array(): [u8; 16]`):\n/// - `conn_id_0` contains bytes `[0 ..8 ]`.\n/// - `conn_id_1` contains bytes `[8 ..16]`.\n///\n/// Again, note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.\n///\n/// The `args` is a `BytesSource`, registered on the host side,\n/// which can be read with `bytes_source_read`.\n/// The contents of the buffer are the BSATN-encoding of the arguments to the reducer.\n/// In the case of empty arguments, `args` will be 0, that is, invalid.\n///\n/// The `error` is a `BytesSink`, registered on the host side,\n/// which can be written to with `bytes_sink_write`.\n/// When `error` is written to,\n/// it is expected that `HOST_CALL_FAILURE` is returned.\n/// Otherwise, `0` should be returned, i.e., the reducer completed successfully.\n/// Note that in the future, more failure codes could be supported.\n#[unsafe(no_mangle)]\nextern \"C\" fn __call_reducer__(\n    id: usize,\n    sender_0: u64,\n    sender_1: u64,\n    sender_2: u64,\n    sender_3: u64,\n    conn_id_0: u64,\n    conn_id_1: u64,\n    timestamp: u64,\n    args: BytesSource,\n    error: BytesSink,\n) -> i16 {\n    // Piece together `sender_i` into an `Identity`.\n    let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3);\n\n    // Piece together `conn_id_i` into a `ConnectionId`.\n    // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.\n    let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1);\n\n    // Assemble the `ReducerContext`.\n    let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64);\n    let ctx = ReducerContext::new(crate::Local {}, sender, conn_id, timestamp);\n\n    // Fetch reducer function.\n    let reducers = REDUCERS.get().unwrap();\n    // Dispatch to it with the arguments read.\n    let res = with_read_args(args, |args| reducers[id](&ctx, args));\n    // Convert any error message to an error code and writes to the `error` sink.\n    convert_err_to_errno(res, error)\n}\n\n/// Reconstruct the `sender_i` args to [`__call_reducer__`] and [`__call_procedure__`] into an [`Identity`].\nfn reconstruct_sender_identity(sender_0: u64, sender_1: u64, sender_2: u64, sender_3: u64) -> Identity {\n    let sender = [sender_0, sender_1, sender_2, sender_3];\n    let sender: [u8; 32] = bytemuck::must_cast(sender);\n    Identity::from_byte_array(sender) // The LITTLE-ENDIAN constructor.\n}\n\n/// Reconstruct the `conn_id_i` args to [`__call_reducer__`] and [`__call_procedure__`] into a [`ConnectionId`].\n///\n/// The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.\nfn reconstruct_connection_id(conn_id_0: u64, conn_id_1: u64) -> Option<ConnectionId> {\n    // Piece together `conn_id_i` into a `ConnectionId`.\n    // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.\n    let conn_id = [conn_id_0, conn_id_1];\n    let conn_id: [u8; 16] = bytemuck::must_cast(conn_id);\n    let conn_id = ConnectionId::from_le_byte_array(conn_id); // The LITTLE-ENDIAN constructor.\n    (conn_id != ConnectionId::ZERO).then_some(conn_id)\n}\n\n/// If `res` is `Err`, write the message to `out` and return non-zero.\n/// If `res` is `Ok`, return zero.\n///\n/// Called by [`__call_reducer__`] and [`__call_procedure__`]\n/// to convert the user-returned `Result` into a low-level errno return.\nfn convert_err_to_errno(res: Result<(), Box<str>>, out: BytesSink) -> i16 {\n    match res {\n        Ok(()) => 0,\n        Err(msg) => {\n            write_to_sink(out, msg.as_bytes());\n            errno::HOST_CALL_FAILURE.get() as i16\n        }\n    }\n}\n\n/// Called by the host to execute a procedure\n/// when the `sender` calls the procedure identified by `id` at `timestamp` with `args`.\n///\n/// The `sender_{0-3}` are the pieces of a `[u8; 32]` (`u256`) representing the sender's `Identity`.\n/// They are encoded as follows (assuming `identity.to_byte_array(): [u8; 32]`):\n/// - `sender_0` contains bytes `[0 ..8 ]`.\n/// - `sender_1` contains bytes `[8 ..16]`.\n/// - `sender_2` contains bytes `[16..24]`.\n/// - `sender_3` contains bytes `[24..32]`.\n///\n/// Note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.\n///\n/// The `conn_id_{0-1}` are the pieces of a `[u8; 16]` (`u128`) representing the callers's [`ConnectionId`].\n/// They are encoded as follows (assuming `conn_id.as_le_byte_array(): [u8; 16]`):\n/// - `conn_id_0` contains bytes `[0 ..8 ]`.\n/// - `conn_id_1` contains bytes `[8 ..16]`.\n///\n/// Again, note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.\n///\n/// The `args` is a `BytesSource`, registered on the host side,\n/// which can be read with `bytes_source_read`.\n/// The contents of the buffer are the BSATN-encoding of the arguments to the reducer.\n/// In the case of empty arguments, `args` will be 0, that is, invalid.\n///\n/// The `result_sink` is a `BytesSink`, registered on the host side,\n/// which can be written to with `bytes_sink_write`.\n/// Procedures are expected to always write to this sink\n/// the BSATN-serialized bytes of a value of the procedure's return type.\n///\n/// Procedures always return the error 0. All other return values are reserved.\n#[cfg(feature = \"unstable\")]\n#[unsafe(no_mangle)]\nextern \"C\" fn __call_procedure__(\n    id: usize,\n    sender_0: u64,\n    sender_1: u64,\n    sender_2: u64,\n    sender_3: u64,\n    conn_id_0: u64,\n    conn_id_1: u64,\n    timestamp: u64,\n    args: BytesSource,\n    result_sink: BytesSink,\n) -> i16 {\n    // Piece together `sender_i` into an `Identity`.\n    let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3);\n\n    // Piece together `conn_id_i` into a `ConnectionId`.\n    let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1);\n\n    let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64);\n\n    // Assemble the `ProcedureContext`.\n    let mut ctx = ProcedureContext::new(sender, conn_id, timestamp);\n\n    // Grab the list of procedures, which is populated by the preinit functions.\n    let procedures = PROCEDURES.get().unwrap();\n\n    // Deserialize the args and pass them to the actual procedure.\n    let res = with_read_args(args, |args| procedures[id](&mut ctx, args));\n\n    // Write the result bytes to the `result_sink`.\n    write_to_sink(result_sink, &res);\n\n    // Return 0 for no error. Procedures always either trap or return 0.\n    0\n}\n\n/// Called by the host to execute an anonymous view.\n///\n/// The `args` is a `BytesSource`, registered on the host side,\n/// which can be read with `bytes_source_read`.\n/// The contents of the buffer are the BSATN-encoding of the arguments to the view.\n/// In the case of empty arguments, `args` will be 0, that is, invalid.\n///\n/// The output of the view is written to a `BytesSink`,\n/// registered on the host side, with `bytes_sink_write`.\n///\n/// Note, a previous version of the abi used a different return format for views.\n/// We used to write the return rows of the view directly to the sink.\n/// However the current version first writes a [`ViewResultHeader`].\n/// This is to distinguish between views that return rows vs ones that return queries.\n///\n/// The current abi is identified by a return code of 2.\n/// The previous abi, which we still support, is identified by a return code of 0.\n#[unsafe(no_mangle)]\nextern \"C\" fn __call_view_anon__(id: usize, args: BytesSource, sink: BytesSink) -> i16 {\n    let views = ANONYMOUS_VIEWS.get().unwrap();\n    write_to_sink(\n        sink,\n        &with_read_args(args, |args| views[id](AnonymousViewContext::default(), args)),\n    );\n    2\n}\n\n/// Called by the host to execute a view when the `sender` calls the view identified by `id` with `args`.\n/// See [`__call_reducer__`] for more commentary on the arguments.\n///\n/// The `args` is a `BytesSource`, registered on the host side,\n/// which can be read with `bytes_source_read`.\n/// The contents of the buffer are the BSATN-encoding of the arguments to the view.\n/// In the case of empty arguments, `args` will be 0, that is, invalid.\n///\n/// The output of the view is written to a `BytesSink`,\n/// registered on the host side, with `bytes_sink_write`.\n///\n/// Note, a previous version of the abi used a different return format for views.\n/// We used to write the return rows of the view directly to the sink.\n/// However the current version first writes a [`ViewResultHeader`].\n/// This is to distinguish between views that return rows vs ones that return queries.\n///\n/// The current abi is identified by a return code of 2.\n/// The previous abi, which we still support, is identified by a return code of 0.\n#[unsafe(no_mangle)]\nextern \"C\" fn __call_view__(\n    id: usize,\n    sender_0: u64,\n    sender_1: u64,\n    sender_2: u64,\n    sender_3: u64,\n    args: BytesSource,\n    sink: BytesSink,\n) -> i16 {\n    // Piece together `sender_i` into an `Identity`.\n    let sender = [sender_0, sender_1, sender_2, sender_3];\n    let sender: [u8; 32] = bytemuck::must_cast(sender);\n    let sender = Identity::from_byte_array(sender); // The LITTLE-ENDIAN constructor.\n\n    let views = VIEWS.get().unwrap();\n\n    write_to_sink(\n        sink,\n        &with_read_args(args, |args| views[id](ViewContext::new(sender), args)),\n    );\n    2\n}\n\n/// Run `logic` with `args` read from the host into a `&[u8]`.\nfn with_read_args<R>(args: BytesSource, logic: impl FnOnce(&[u8]) -> R) -> R {\n    if args == BytesSource::INVALID {\n        return logic(&[]);\n    }\n\n    // Steal an iteration row buffer.\n    // These were not meant for this purpose,\n    // but it's likely we have one sitting around being unused at this point,\n    // so use it to avoid allocating a temporary buffer if possible.\n    // And if we do allocate a temporary buffer now, it will likely be reused later.\n    let mut buf = IterBuf::take();\n\n    // Read `args` and run `logic`.\n    read_bytes_source_into(args, &mut buf);\n    logic(&buf)\n}\n\nconst NO_SPACE: u16 = errno::NO_SPACE.get();\nconst NO_SUCH_BYTES: u16 = errno::NO_SUCH_BYTES.get();\n\n/// Look up the jwt associated with `connection_id`.\npub fn get_jwt(connection_id: ConnectionId) -> Option<String> {\n    let mut buf = IterBuf::take();\n    let source = sys::get_jwt(connection_id.as_le_byte_array())?;\n    if source == BytesSource::INVALID {\n        return None;\n    }\n    read_bytes_source_into(source, &mut buf);\n    Some(std::str::from_utf8(&buf).unwrap().to_string())\n}\n\n/// Read `source` from the host fully into `buf`.\npub(crate) fn read_bytes_source_into(source: BytesSource, buf: &mut Vec<u8>) {\n    const INVALID: i16 = NO_SUCH_BYTES as i16;\n\n    // For reducer arguments, the `buf` will almost certainly already be large enough,\n    // as it comes from `IterBuf`, which start at 64KiB.\n    // But reading the remaining length and calling `buf.reserve` is a negligible cost,\n    // and in the future we may want to use this method to read other `BytesSource`s into other buffers.\n    // I (pgoldman 2025-09-26) also value having it as an example of correct usage of `bytes_source_remaining_length`.\n    let len = {\n        let mut len = 0;\n        let ret = unsafe { sys::raw::bytes_source_remaining_length(source, &raw mut len) };\n        match ret {\n            0 => len,\n            INVALID => panic!(\"invalid source passed\"),\n            _ => unreachable!(),\n        }\n    };\n    buf.reserve(buf.len().saturating_sub(len as usize));\n\n    // Because we've reserved space in our buffer already, this loop should be unnecessary.\n    // We expect the first call to `bytes_source_read` to always return `-1`.\n    // I (pgoldman 2025-09-26) am leaving the loop here because there's no downside to it,\n    // and in the future we may want to support `BytesSource`s which don't have a known length ahead of time\n    // (i.e. put arbitrary streams in `BytesSource` on the host side rather than just `Bytes` buffers),\n    // at which point the loop will become useful again.\n    loop {\n        // Write into the spare capacity of the buffer.\n        let buf_ptr = buf.spare_capacity_mut();\n        let spare_len = buf_ptr.len();\n        let mut buf_len = buf_ptr.len();\n        let buf_ptr = buf_ptr.as_mut_ptr().cast();\n        let ret = unsafe { sys::raw::bytes_source_read(source, buf_ptr, &mut buf_len) };\n        if ret <= 0 {\n            // SAFETY: `bytes_source_read` just appended `buf_len` bytes to `buf`.\n            unsafe { buf.set_len(buf.len() + buf_len) };\n        }\n        match ret {\n            // Host side source exhausted, we're done.\n            -1 => break,\n            // Wrote the entire spare capacity.\n            // Need to reserve more space in the buffer.\n            0 if spare_len == buf_len => buf.reserve(1024),\n            // Host didn't write as much as possible.\n            // Try to read some more.\n            // The host will likely not trigger this branch (current host doesn't),\n            // but a module should be prepared for it.\n            0 => {}\n            INVALID => panic!(\"invalid source passed\"),\n            _ => unreachable!(),\n        }\n    }\n}\n\n/// Write `buf` to `sink`.\nfn write_to_sink(sink: BytesSink, mut buf: &[u8]) {\n    loop {\n        let len = &mut buf.len();\n        match unsafe { sys::raw::bytes_sink_write(sink, buf.as_ptr(), len) } {\n            0 => {\n                // Set `buf` to remainder and bail if it's empty.\n                (_, buf) = buf.split_at(*len);\n                if buf.is_empty() {\n                    break;\n                }\n            }\n            NO_SUCH_BYTES => panic!(\"invalid sink passed\"),\n            NO_SPACE => panic!(\"no space left at sink\"),\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[macro_export]\n#[doc(hidden)]\nmacro_rules! __make_register_reftype {\n    ($ty:ty, $name:literal) => {\n        const _: () = {\n            #[unsafe(export_name = concat!(\"__preinit__20_register_describer_\", $name))]\n            extern \"C\" fn __register_describer() {\n                $crate::rt::register_reftype::<$ty>()\n            }\n        };\n    };\n}\n\n#[cfg(feature = \"unstable\")]\n#[doc(hidden)]\npub fn volatile_nonatomic_schedule_immediate<'de, A: Args<'de>, R: Reducer<'de, A>, R2: FnInfo<Invoke = ReducerFn>>(\n    _reducer: R,\n    args: A,\n) {\n    let arg_bytes = bsatn::to_vec(&SerDeArgs(args)).unwrap();\n\n    // Schedule the reducer.\n    sys::volatile_nonatomic_schedule_immediate(R2::NAME, &arg_bytes)\n}\n\n/// Read `source` completely into a temporary buffer, then BSATN-deserialize it as a `T`.\n///\n/// Panics if the bytes from `source` fail to deserialize as `T`.\n/// The type name of `T` will be included in the panic message.\n#[cfg_attr(not(feature = \"unstable\"), allow(unused))]\npub(crate) fn read_bytes_source_as<T: DeserializeOwned + 'static>(source: BytesSource) -> T {\n    let mut buf = IterBuf::take();\n    read_bytes_source_into(source, &mut buf);\n    bsatn::from_slice::<T>(&buf)\n        .unwrap_or_else(|err| panic!(\"Failed to BSATN-deserialize `{}`: {err:#?}\", std::any::type_name::<T>()))\n}\n\npub trait ExplicitNames {\n    fn explicit_names() -> RawExplicitNames {\n        RawExplicitNames::default()\n    }\n}\n"
  },
  {
    "path": "crates/bindings/src/table.rs",
    "content": "use crate::{bsatn, rt::ExplicitNames, sys, DeserializeOwned, IterBuf, Serialize, SpacetimeType, TableId};\nuse core::borrow::Borrow;\nuse core::convert::Infallible;\nuse core::fmt;\nuse core::marker::PhantomData;\npub use spacetimedb_lib::db::raw_def::v9::TableAccess;\nuse spacetimedb_lib::{\n    buffer::{BufReader, Cursor, DecodeError},\n    AlgebraicValue,\n};\nuse spacetimedb_lib::{FilterableValue, IndexScanRangeBoundsTerminator};\npub use spacetimedb_primitives::{ColId, IndexId};\n\n/// Implemented for every `TableHandle` struct generated by the [`table`](macro@crate::table) macro.\n/// Contains methods that are present for every table, regardless of what unique constraints\n/// and indexes are present.\n///\n/// To get a `TableHandle`\n// TODO: should we rename this `TableHandle`? Documenting this, I think that's much clearer.\npub trait Table: TableInternal + ExplicitNames {\n    /// The type of rows stored in this table.\n    type Row: SpacetimeType + Serialize + DeserializeOwned + Sized + 'static;\n\n    /// Returns the number of rows in this table.\n    ///\n    /// This reads datastore metadata, so it runs in constant time.\n    /// It also takes into account modifications by the current transaction.\n    fn count(&self) -> u64 {\n        count::<Self>()\n    }\n\n    /// Iterate over all rows of the table.\n    ///\n    /// For large tables, this can be a slow operation!\n    /// Prefer [filtering](RangedIndex::filter) a [`RangedIndex`] or [finding](UniqueColumn::find) a [`UniqueColumn`] if\n    /// possible.\n    ///\n    /// (This keeps track of changes made to the table since the start of this reducer invocation. For example, if rows have been deleted since the start of this reducer invocation, those rows will not be returned by `iter`. Similarly, inserted rows WILL be returned.)\n    #[inline]\n    fn iter(&self) -> impl Iterator<Item = Self::Row> {\n        let table_id = Self::table_id();\n        let iter = sys::datastore_table_scan_bsatn(table_id).expect(\"datastore_table_scan_bsatn() call failed\");\n        TableIter::new(iter)\n    }\n\n    /// Inserts `row` into the table.\n    ///\n    /// The return value is the inserted row, with any auto-incrementing columns replaced with computed values.\n    /// The `insert` method always returns the inserted row,\n    /// even when the table contains no auto-incrementing columns.\n    ///\n    /// (The returned row is a copy of the row in the database.\n    /// Modifying this copy does not directly modify the database.\n    /// See [`UniqueColumn::update`] if you want to update the row.)\n    ///\n    /// May panic if inserting the row violates any constraints.\n    /// Callers which intend to handle constraint violation errors should instead use [`Self::try_insert`].\n    ///\n    /// Inserting an exact duplicate of a row already present in the table is a no-op,\n    /// as SpacetimeDB is a set-semantic database.\n    /// This is true even for tables with unique constraints;\n    /// inserting an exact duplicate of an already-present row will not panic.\n    #[track_caller]\n    fn insert(&self, row: Self::Row) -> Self::Row {\n        self.try_insert(row).unwrap_or_else(|e| panic!(\"{e}\"))\n    }\n\n    /// The error type for this table for unique constraint violations. Will either be\n    /// [`UniqueConstraintViolation`] if the table has any unique constraints, or [`Infallible`]\n    /// otherwise.\n    type UniqueConstraintViolation: MaybeError<UniqueConstraintViolation>;\n\n    /// The error type for this table for auto-increment overflows. Will either be\n    /// [`AutoIncOverflow`] if the table has any auto-incrementing columns, or [`Infallible`]\n    /// otherwise.\n    type AutoIncOverflow: MaybeError<AutoIncOverflow>;\n\n    /// Counterpart to [`Self::insert`] which allows handling failed insertions.\n    ///\n    /// For tables with constraints, this method returns an `Err` when the insertion fails rather than panicking.\n    /// For tables without any constraints, [`Self::UniqueConstraintViolation`] and [`Self::AutoIncOverflow`]\n    /// will be [`std::convert::Infallible`], and this will be a more-verbose [`Self::insert`].\n    ///\n    /// Inserting an exact duplicate of a row already present in the table is a no-op and returns `Ok`,\n    /// as SpacetimeDB is a set-semantic database.\n    /// This is true even for tables with unique constraints;\n    /// inserting an exact duplicate of an already-present row will return `Ok`.\n    #[track_caller]\n    fn try_insert(&self, row: Self::Row) -> Result<Self::Row, TryInsertError<Self>> {\n        insert::<Self>(row, IterBuf::take())\n    }\n\n    /// Deletes a row equal to `row` from the table.\n    ///\n    /// Returns `true` if the row was present and has been deleted,\n    /// or `false` if the row was not present and therefore the tables have not changed.\n    ///\n    /// Unlike [`Self::insert`], there is no need to return the deleted row,\n    /// as it must necessarily have been exactly equal to the `row` argument.\n    /// No analogue to auto-increment placeholders exists for deletions.\n    ///\n    /// May panic if deleting the row violates any constraints.\n    fn delete(&self, row: Self::Row) -> bool {\n        // Note that as of writing deletion is infallible, but future work may define new constraints,\n        // e.g. foreign keys, which cause deletion to fail in some cases.\n        // If and when these new constraints are added,\n        // we should define `Self::ForeignKeyViolation`,\n        // analogous to [`Self::UniqueConstraintViolation`].\n\n        let relation = std::slice::from_ref(&row);\n        let buf = IterBuf::serialize(relation).unwrap();\n        let count = sys::datastore_delete_all_by_eq_bsatn(Self::table_id(), &buf).unwrap();\n        count > 0\n    }\n\n    // Re-integrates the BSATN of the `generated_cols` into `row`.\n    #[doc(hidden)]\n    fn integrate_generated_columns(row: &mut Self::Row, generated_cols: &[u8]);\n}\n\n#[doc(hidden)]\n#[inline]\npub fn count<Tbl: Table>() -> u64 {\n    sys::datastore_table_row_count(Tbl::table_id()).expect(\"datastore_table_row_count() call failed\")\n}\n\n#[doc(hidden)]\npub trait TableInternal: Sized {\n    const TABLE_NAME: &'static str;\n    const TABLE_ACCESS: TableAccess = TableAccess::Private;\n    const UNIQUE_COLUMNS: &'static [u16];\n    const INDEXES: &'static [IndexDesc<'static>];\n    const PRIMARY_KEY: Option<u16> = None;\n    const SEQUENCES: &'static [u16];\n    const SCHEDULE: Option<ScheduleDesc<'static>> = None;\n    const IS_EVENT: bool = false;\n\n    /// Returns the ID of this table.\n    fn table_id() -> TableId;\n\n    fn get_default_col_values() -> Vec<ColumnDefault>;\n}\n\n/// Describe a named index with an index type over a set of columns identified by their IDs.\n#[derive(Clone, Copy)]\npub struct IndexDesc<'a> {\n    pub source_name: &'a str,\n    pub accessor_name: &'a str,\n    pub algo: IndexAlgo<'a>,\n}\n\n#[derive(Clone, Copy)]\npub enum IndexAlgo<'a> {\n    BTree { columns: &'a [u16] },\n    Hash { columns: &'a [u16] },\n    Direct { column: u16 },\n}\n\npub struct ScheduleDesc<'a> {\n    pub reducer_or_procedure_name: &'a str,\n    pub scheduled_at_column: u16,\n}\n\n#[derive(Debug, Clone)]\npub struct ColumnDefault {\n    pub col_id: u16,\n    pub value: AlgebraicValue,\n}\n\n/// A row operation was attempted that would violate a unique constraint.\n// TODO: add column name for better error message\n#[derive(Debug)]\n#[non_exhaustive]\npub struct UniqueConstraintViolation;\n\nimpl fmt::Display for UniqueConstraintViolation {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"duplicate unique column\")\n    }\n}\n\nimpl std::error::Error for UniqueConstraintViolation {}\n\n/// An auto-inc column overflowed its data type.\n#[derive(Debug)]\n#[non_exhaustive]\n// TODO: add column name for better error message\npub struct AutoIncOverflow;\n\nimpl fmt::Display for AutoIncOverflow {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"auto-inc sequence overflowed its column type\")\n    }\n}\n\nimpl std::error::Error for AutoIncOverflow {}\n\n/// The error type returned from [`Table::try_insert()`], signalling a constraint violation.\npub enum TryInsertError<Tbl: Table> {\n    /// A [`UniqueConstraintViolation`].\n    ///\n    /// Returned from [`Table::try_insert`] if an attempted insertion\n    /// has the same value in a unique column as an already-present row.\n    ///\n    /// This variant is only possible if the table has at least one unique column,\n    /// and is otherwise [`std::convert::Infallible`].\n    UniqueConstraintViolation(Tbl::UniqueConstraintViolation),\n\n    /// An [`AutoIncOverflow`].\n    ///\n    /// Returned from [`Table::try_insert`] if an attempted insertion\n    /// advances an auto-inc sequence past the bounds of the column type.\n    ///\n    /// This variant is only possible if the table has at least one auto-inc column,\n    /// and is otherwise [`std::convert::Infallible`].\n    AutoIncOverflow(Tbl::AutoIncOverflow),\n}\n\nimpl<Tbl: Table> fmt::Debug for TryInsertError<Tbl> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"TryInsertError::<{}>::\", Tbl::TABLE_NAME)?;\n        match self {\n            Self::UniqueConstraintViolation(e) => fmt::Debug::fmt(e, f),\n            Self::AutoIncOverflow(e) => fmt::Debug::fmt(e, f),\n        }\n    }\n}\n\nimpl<Tbl: Table> fmt::Display for TryInsertError<Tbl> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"insertion error on table `{}`:\", Tbl::TABLE_NAME)?;\n        match self {\n            Self::UniqueConstraintViolation(e) => fmt::Display::fmt(e, f),\n            Self::AutoIncOverflow(e) => fmt::Display::fmt(e, f),\n        }\n    }\n}\n\nimpl<Tbl: Table> std::error::Error for TryInsertError<Tbl> {\n    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n        Some(match self {\n            Self::UniqueConstraintViolation(e) => e,\n            Self::AutoIncOverflow(e) => e,\n        })\n    }\n}\n\nimpl<Tbl: Table> From<TryInsertError<Tbl>> for String {\n    fn from(err: TryInsertError<Tbl>) -> Self {\n        err.to_string()\n    }\n}\n\n#[doc(hidden)]\npub trait MaybeError<E = Self>: std::error::Error + Send + Sync + Sized + 'static {\n    fn get() -> Option<Self>;\n}\n\nimpl<E> MaybeError<E> for Infallible {\n    fn get() -> Option<Self> {\n        None\n    }\n}\n\nimpl MaybeError for UniqueConstraintViolation {\n    fn get() -> Option<Self> {\n        Some(UniqueConstraintViolation)\n    }\n}\n\nimpl MaybeError for AutoIncOverflow {\n    fn get() -> Option<AutoIncOverflow> {\n        Some(AutoIncOverflow)\n    }\n}\n\npub trait Column {\n    type Table: Table;\n    type ColType: SpacetimeType + Serialize + DeserializeOwned;\n    const COLUMN_NAME: &'static str;\n    fn get_field(row: &<Self::Table as Table>::Row) -> &Self::ColType;\n}\n\n/// A marker trait for columns that are the primary key of their table.\n///\n/// This is used to restrict [`UniqueColumn::update`] to only work on primary key columns.\npub trait PrimaryKey {}\n\n/// A handle to a unique index on a column.\n/// Available for `#[unique]` and `#[primary_key]` columns.\n///\n/// For a table *table* with a column *column*, use `ctx.db.{table}().{column}()`\n/// to get a `UniqueColumn` from a [`ReducerContext`](crate::ReducerContext).\n///\n/// Example:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, UniqueColumn, ReducerContext, DbContext};\n///\n/// #[table(accessor = user)]\n/// struct User {\n///     #[primary_key]\n///     id: u32,\n///     #[unique]\n///     username: String,\n///     dog_count: u64\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     let user = ctx.db().user();\n///\n///     let by_id: UniqueColumn<_, u32, _> = user.id();\n///\n///     let mut example_user: User = by_id.find(357).unwrap();\n///     example_user.dog_count += 5;\n///     by_id.update(example_user);\n///\n///     let by_username: UniqueColumn<_, String, _> = user.username();\n///     by_username.delete(&\"Evil Bob\".to_string());\n/// }\n/// # }\n/// ```\n///\n/// <!-- TODO: do we need integer type suffixes on literal arguments, like for RangedIndex? -->\npub struct UniqueColumn<Tbl, ColType, Col> {\n    _marker: PhantomData<(Tbl, ColType, Col)>,\n}\n\nimpl<Tbl: Table, Col: Index + Column<Table = Tbl>> UniqueColumn<Tbl, Col::ColType, Col> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    /// Finds and returns the row where the value in the unique column matches the supplied `col_val`,\n    /// or `None` if no such row is present in the database state.\n    //\n    // TODO: consider whether we should accept the sought value by ref or by value.\n    // Should be consistent with the implementors of `IndexScanRangeBounds` (see below).\n    // By-value makes passing `Copy` fields more convenient,\n    // whereas by-ref makes passing `!Copy` fields more performant.\n    // Can we do something smart with `std::borrow::Borrow`?\n    #[inline]\n    pub fn find(&self, col_val: impl Borrow<Col::ColType>) -> Option<Tbl::Row>\n    where\n        for<'a> &'a Col::ColType: FilterableValue,\n    {\n        find::<Tbl, Col>(col_val.borrow())\n    }\n\n    /// Deletes the row where the value in the unique column matches the supplied `col_val`,\n    /// if any such row is present in the database state.\n    ///\n    /// Returns `true` if a row with the specified `col_val` was previously present and has been deleted,\n    /// or `false` if no such row was present.\n    #[inline]\n    pub fn delete(&self, col_val: impl Borrow<Col::ColType>) -> bool {\n        self._delete(col_val.borrow()).0\n    }\n\n    fn _delete(&self, col_val: &Col::ColType) -> (bool, IterBuf) {\n        let index_id = Col::index_id();\n        let point = IterBuf::serialize(col_val).unwrap();\n        let n_del = sys::datastore_delete_by_index_scan_point_bsatn(index_id, &point).unwrap_or_else(|e| {\n            panic!(\"unique: unexpected error from datastore_delete_by_index_scan_point_bsatn: {e}\")\n        });\n\n        (n_del > 0, point)\n    }\n\n    /// Deletes the row where the value in the unique column matches that in the corresponding field of `new_row`, and\n    /// then inserts the `new_row`.\n    ///\n    /// Returns the new row as actually inserted, with computed values substituted for any auto-inc placeholders.\n    ///\n    /// This method can only be called on primary key columns, not any unique column.\n    /// This prevents confusion regarding what constitutes a row update vs. a delete+insert.\n    /// To perform this operation for a non-primary unique column, call\n    /// `.delete(key)` followed by `.insert(row)`.\n    ///\n    /// # Panics\n    /// Panics if no row was previously present with the matching value in the unique column,\n    /// or if either the delete or the insertion would violate a constraint.\n    #[track_caller]\n    pub fn update(&self, new_row: Tbl::Row) -> Tbl::Row\n    where\n        Col: PrimaryKey,\n    {\n        let buf = IterBuf::take();\n        update::<Tbl>(Col::index_id(), new_row, buf)\n    }\n\n    /// Inserts `new_row` into the table, first checking for an existing\n    /// row with a matching value in the unique column and deleting it if present.\n    ///\n    /// Be careful: in case of a constraint violation, this method will return Err,\n    /// but the previous row will be deleted. If you propagate the error, SpacetimeDB will\n    /// rollback the transaction and the old row will be restored. If you ignore the error,\n    /// the old row will be lost.\n    #[track_caller]\n    #[doc(alias = \"try_upsert\")]\n    #[cfg(feature = \"unstable\")]\n    pub fn try_insert_or_update(&self, new_row: Tbl::Row) -> Result<Tbl::Row, TryInsertError<Tbl>> {\n        let col_val = Col::get_field(&new_row);\n        // If the row doesn't exist, delete will return false, which we ignore.\n        let _ = self.delete(col_val);\n\n        // Then, insert the new row.\n        let buf = IterBuf::take();\n        insert::<Tbl>(new_row, buf)\n    }\n\n    /// Inserts `new_row` into the table, first checking for an existing\n    /// row with a matching value in the unique column and deleting it if present.\n    ///\n    /// # Panics\n    /// Panics if either the delete or the insertion would violate a constraint.\n    #[track_caller]\n    #[doc(alias = \"upsert\")]\n    #[cfg(feature = \"unstable\")]\n    pub fn insert_or_update(&self, new_row: Tbl::Row) -> Tbl::Row {\n        self.try_insert_or_update(new_row).unwrap_or_else(|e| panic!(\"{e}\"))\n    }\n}\n\n#[inline]\nfn find<Tbl: Table, Col: Index + Column<Table = Tbl>>(col_val: &Col::ColType) -> Option<Tbl::Row> {\n    // Find the row with a match.\n    let index_id = Col::index_id();\n    let point = IterBuf::serialize(col_val).unwrap();\n\n    let iter = sys::datastore_index_scan_point_bsatn(index_id, &point)\n        .unwrap_or_else(|e| panic!(\"unique: unexpected error from `datastore_index_scan_point_bsatn`: {e}\"));\n    let mut iter = TableIter::new_with_buf(iter, point);\n\n    // We will always find either 0 or 1 rows here due to the unique constraint.\n    let row = iter.next();\n    assert!(\n        iter.is_exhausted(),\n        \"`datastore_index_scan_point_bsatn` on unique field cannot return >1 rows\"\n    );\n    row\n}\n\n/// A read-only handle to a unique (single-column) index.\n///\n/// This is the read-only version of [`UniqueColumn`].\n/// It mirrors [`UniqueColumn`] but only exposes read APIs.\n/// It cannot insert or delete rows.\n/// It is used by `{table}__ViewHandle` to keep view code read-only at compile time.\n///\n/// Note, the `Tbl` generic is the read-write table handle `{table}__TableHandle`.\n/// This is because read-only indexes still need [`Table`] metadata.\n/// The view handle itself deliberately does not implement `Table`.\npub struct UniqueColumnReadOnly<Tbl, ColType, Col> {\n    _marker: PhantomData<(Tbl, ColType, Col)>,\n}\n\nimpl<Tbl: Table, Col: Index + Column<Table = Tbl>> UniqueColumnReadOnly<Tbl, Col::ColType, Col> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    #[inline]\n    pub fn find(&self, col_val: impl Borrow<Col::ColType>) -> Option<Tbl::Row>\n    where\n        for<'a> &'a Col::ColType: FilterableValue,\n    {\n        find::<Tbl, Col>(col_val.borrow())\n    }\n}\n\n/// Information about the `index_id` of an index\n/// and the number of columns the index indexes.\npub trait Index {\n    /// The number of columns the index indexes.\n    ///\n    /// Used to determine whether a scan for e.g., `(a, b)`,\n    /// is actually a point scan or whether there's a suffix, e.g., `(c, d)`.\n    const NUM_COLS_INDEXED: usize;\n\n    /// Determine the `IndexId` of this index.\n    ///\n    /// For generated implementations,\n    /// this results in a *memoized* syscall to determine the index,\n    /// based on the hard coded name of the index.\n    fn index_id() -> IndexId;\n}\n\n/// Marks an index as only having point query capabilities.\n///\n/// This applies to Hash indices but not BTree and Direct indices.\npub trait IndexIsPointed: Index {}\n\n/// A handle to a Hash index on a table.\n///\n/// To get one of these from a `ReducerContext`, use:\n/// ```text\n/// ctx.db.{table}().{index}()\n/// ```\n/// for a table *table* and an index *index*.\n///\n/// Example:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, PointIndex, ReducerContext, DbContext};\n///\n/// #[table(accessor = user,\n///     index(accessor = dogs_and_name, hash(columns = [dogs, name])))]\n/// struct User {\n///     id: u32,\n///     name: String,\n///     /// Number of dogs owned by the user.\n///     dogs: u64\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     let by_dogs_and_name: PointIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n/// }\n/// # }\n/// ```\n///\n/// For single-column indexes, use the name of the column:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, PointIndex, ReducerContext, DbContext};\n///\n/// #[table(accessor = user)]\n/// struct User {\n///     id: u32,\n///     username: String,\n///     #[index(btree)]\n///     dogs: u64\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     let by_dogs: PointIndex<_, (u64,), _> = ctx.db().user().dogs();\n/// }\n/// # }\n/// ```\n///\npub struct PointIndex<Tbl: Table, IndexType, Idx: Index> {\n    _marker: PhantomData<(Tbl, IndexType, Idx)>,\n}\n\nimpl<Tbl: Table, IndexType, Idx: IndexIsPointed> PointIndex<Tbl, IndexType, Idx> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    /// Returns an iterator over all rows in the database state\n    /// where the indexed column(s) equal `point`.\n    ///\n    /// Unlike for ranged indices,\n    /// this method only accepts a `point` and not any prefix or range.\n    ///\n    /// For example:\n    ///\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{table, ReducerContext, PointIndex};\n    ///\n    /// #[table(accessor = user,\n    ///     index(accessor = dogs_and_name, hash(columns = [dogs, name])))]\n    /// struct User {\n    ///     id: u32,\n    ///     name: String,\n    ///     dogs: u64\n    /// }\n    ///\n    /// fn demo(ctx: &ReducerContext) {\n    ///     let by_dogs_and_name: PointIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n    ///\n    ///     // Find user with exactly 25 dogs and exactly the name \"Joseph\".\n    ///     for user in by_dogs_and_name.filter((25u64, \"Joseph\")) {\n    ///         /* ... */\n    ///     }\n    ///\n    ///     // You can also pass arguments by reference if desired.\n    ///     for user in by_dogs_and_name.filter((&25u64, &\"Joseph\".to_string())) {\n    ///         /* ... */\n    ///     }\n    /// }\n    /// # }\n    /// ```\n    pub fn filter<P, K>(&self, point: P) -> impl Iterator<Item = Tbl::Row> + use<P, K, Tbl, IndexType, Idx>\n    where\n        P: WithPointArg<K>,\n    {\n        filter_point::<Tbl, Idx, K>(point)\n    }\n\n    /// Deletes all rows in the database state\n    /// where the indexed column(s) equal `point`.\n    ///\n    /// Unlike for ranged indices,\n    /// this method only accepts a `point` and not any prefix or range.\n    ///\n    /// For example:\n    ///\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{table, ReducerContext, PointIndex};\n    ///\n    /// #[table(accessor = user,\n    ///     index(accessor = dogs_and_name, hash(columns = [dogs, name])))]\n    /// struct User {\n    ///     id: u32,\n    ///     name: String,\n    ///     dogs: u64\n    /// }\n    ///\n    /// fn demo(ctx: &ReducerContext) {\n    ///     let by_dogs_and_name: PointIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n    ///\n    ///     // Delete users with exactly 25 dogs, and exactly the name \"Joseph\".\n    ///     by_dogs_and_name.delete((25u64, \"Joseph\"));\n    ///\n    ///     // You can also pass arguments by reference if desired.\n    ///     by_dogs_and_name.delete((&25u64, &\"Joseph\".to_string()));\n    /// }\n    /// # }\n    /// ```\n    ///\n    /// May panic if deleting any one of the rows would violate a constraint,\n    /// though at present no such constraints exist.\n    pub fn delete<P, K>(&self, point: P) -> u64\n    where\n        P: WithPointArg<K>,\n    {\n        let index_id = Idx::index_id();\n        point.with_point_arg(|point| {\n            sys::datastore_delete_by_index_scan_point_bsatn(index_id, point)\n                .unwrap_or_else(|e| panic!(\"unexpected error from `datastore_delete_by_index_scan_point_bsatn`: {e}\"))\n                .into()\n        })\n    }\n}\n\n/// Scans `Tbl` for `point` using the index `Idx`.\n///\n/// The type parameter `K` is either `()` or [`SingleBound`]\n/// and is used to workaround the orphan rule.\nfn filter_point<Tbl, Idx, K>(point: impl WithPointArg<K>) -> impl Iterator<Item = Tbl::Row>\nwhere\n    Tbl: Table,\n    Idx: IndexIsPointed,\n{\n    let index_id = Idx::index_id();\n    let iter = point.with_point_arg(|point| {\n        sys::datastore_index_scan_point_bsatn(index_id, point)\n            .unwrap_or_else(|e| panic!(\"unexpected error from `datastore_index_scan_point_bsatn`: {e}\"))\n    });\n    TableIter::new(iter)\n}\n\n/// A read-only handle to a Hash index.\n///\n/// This is the read-only version of [`PointIndex`].\n/// It mirrors [`PointIndex`] but exposes only `.filter(..)`, not `.delete(..)`.\n/// It is used by `{table}__ViewHandle` to keep view code read-only at compile time.\n///\n/// Note, the `Tbl` generic is the read-write table handle `{table}__TableHandle`.\n/// This is because read-only indexes still need [`Table`] metadata.\n/// The view handle itself deliberately does not implement `Table`.\npub struct PointIndexReadOnly<Tbl: Table, IndexType, Idx: Index> {\n    _marker: PhantomData<(Tbl, IndexType, Idx)>,\n}\n\nimpl<Tbl: Table, IndexType, Idx: IndexIsPointed> PointIndexReadOnly<Tbl, IndexType, Idx> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    pub fn filter<P, K>(&self, point: P) -> impl Iterator<Item = Tbl::Row> + use<P, K, Tbl, IndexType, Idx>\n    where\n        P: WithPointArg<K>,\n    {\n        filter_point::<Tbl, Idx, K>(point)\n    }\n}\n\n/// Trait used for running point index scans.\n///\n/// The type parameter `K` is either `()` or [`SingleBound`]\n/// and is used to workaround the orphan rule.\npub trait WithPointArg<K = ()> {\n    /// Runs `run` with the BSATN-serialized point to pass to the index scan.\n    // TODO(perf, centril): once we have stable specialization,\n    // just use `to_le_bytes` internally instead.\n    #[doc(hidden)]\n    fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R;\n}\n\nimpl<Arg: FilterableValue> WithPointArg<SingleBound> for Arg {\n    fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {\n        run(&IterBuf::serialize(self).unwrap())\n    }\n}\n\nmacro_rules! impl_with_point_arg {\n    ($($arg:ident),+) => {\n        impl<$($arg: FilterableValue),+> WithPointArg for ($($arg,)+) {\n            fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {\n                // We can assume here that we have a point bound.\n                let mut data = IterBuf::take();\n\n                // Destructure the argument tuple into variables with the same names as their types.\n                #[allow(non_snake_case)]\n                let ($($arg,)+) = self;\n\n                // For each part in the tuple queried, serialize it into the `data` buffer.\n                Ok(())\n                    $(.and_then(|()| data.serialize_into($arg)))+\n                    .unwrap();\n\n                run(&*data)\n            }\n        }\n    };\n}\n\nimpl_with_point_arg!(A);\nimpl_with_point_arg!(A, B);\nimpl_with_point_arg!(A, B, C);\nimpl_with_point_arg!(A, B, C, D);\nimpl_with_point_arg!(A, B, C, D, E);\nimpl_with_point_arg!(A, B, C, D, E, F);\n\n/// Marks an index as having range query capabilities.\n///\n/// This applies to BTree and Direct indices but not Hash indices.\npub trait IndexIsRanged: Index {}\n\n/// A handle to a B-Tree or Direct index on a table.\n///\n/// To get one of these from a `ReducerContext`, use:\n/// ```text\n/// ctx.db.{table}().{index}()\n/// ```\n/// for a table *table* and an index *index*.\n///\n/// Example:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, RangedIndex, ReducerContext, DbContext};\n///\n/// #[table(accessor = user,\n///     index(accessor = dogs_and_name, btree(columns = [dogs, name])))]\n/// struct User {\n///     id: u32,\n///     name: String,\n///     /// Number of dogs owned by the user.\n///     dogs: u64\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     let by_dogs_and_name: RangedIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n/// }\n/// # }\n/// ```\n///\n/// For single-column indexes, use the name of the column:\n///\n/// ```no_run\n/// # #[cfg(target_arch = \"wasm32\")] mod demo {\n/// use spacetimedb::{table, RangedIndex, ReducerContext, DbContext};\n///\n/// #[table(accessor = user)]\n/// struct User {\n///     id: u32,\n///     username: String,\n///     #[index(btree)]\n///     dogs: u64\n/// }\n///\n/// fn demo(ctx: &ReducerContext) {\n///     let by_dogs: RangedIndex<_, (u64,), _> = ctx.db().user().dogs();\n/// }\n/// # }\n/// ```\n///\npub struct RangedIndex<Tbl: Table, IndexType, Idx: IndexIsRanged> {\n    _marker: PhantomData<(Tbl, IndexType, Idx)>,\n}\n\nimpl<Tbl: Table, IndexType, Idx: IndexIsRanged> RangedIndex<Tbl, IndexType, Idx> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    /// Returns an iterator over all rows in the database state where the indexed column(s) match the bounds `b`.\n    ///\n    /// This method accepts a variable numbers of arguments using the [`IndexScanRangeBounds`] trait.\n    /// This depends on the type of the B-Tree index. `b` may be:\n    /// - A value for the first indexed column.\n    /// - A range of values for the first indexed column.\n    /// - A tuple of values for any prefix of the indexed columns, optionally terminated by a range for the next.\n    ///\n    /// For example:\n    ///\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{table, ReducerContext, RangedIndex};\n    ///\n    /// #[table(accessor = user,\n    ///     index(accessor = dogs_and_name, btree(columns = [dogs, name])))]\n    /// struct User {\n    ///     id: u32,\n    ///     name: String,\n    ///     dogs: u64\n    /// }\n    ///\n    /// fn demo(ctx: &ReducerContext) {\n    ///     let by_dogs_and_name: RangedIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n    ///\n    ///     // Find user with exactly 25 dogs.\n    ///     for user in by_dogs_and_name.filter(25u64) { // The `u64` is required, see below.\n    ///         /* ... */\n    ///     }\n    ///\n    ///     // Find user with at least 25 dogs.\n    ///     for user in by_dogs_and_name.filter(25u64..) {\n    ///         /* ... */\n    ///     }\n    ///\n    ///     // Find user with exactly 25 dogs, and a name beginning with \"J\".\n    ///     for user in by_dogs_and_name.filter((25u64, \"J\"..\"K\")) {\n    ///         /* ... */\n    ///     }\n    ///\n    ///     // Find user with exactly 25 dogs, and exactly the name \"Joseph\".\n    ///     for user in by_dogs_and_name.filter((25u64, \"Joseph\")) {\n    ///         /* ... */\n    ///     }\n    ///\n    ///     // You can also pass arguments by reference if desired.\n    ///     for user in by_dogs_and_name.filter((&25u64, &\"Joseph\".to_string())) {\n    ///         /* ... */\n    ///     }\n    /// }\n    /// # }\n    /// ```\n    ///\n    /// **NOTE:** An unfortunate interaction between Rust's trait solver and integer literal defaulting rules means that you must specify the types of integer literals passed to `filter` and `find` methods via the suffix syntax, like `21u32`.\n    ///\n    /// If you don't, you'll see a compiler error like:\n    /// > ```text\n    /// > error[E0271]: type mismatch resolving `<i32 as FilterableValue>::Column == u32`\n    /// >    --> modules/rust-wasm-test/src/lib.rs:356:48\n    /// >     |\n    /// > 356 |     for person in ctx.db.person().age().filter(21) {\n    /// >     |                                         ------ ^^ expected `u32`, found `i32`\n    /// >     |                                         |\n    /// >     |                                         required by a bound introduced by this call\n    /// >     |\n    /// >     = note: required for `i32` to implement `IndexScanRangeBounds<(u32,), SingleBound>`\n    /// > note: required by a bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n    /// >     |\n    /// > 410 |     pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row>\n    /// >     |            ------ required by a bound in this associated function\n    /// > 411 |     where\n    /// > 412 |         B: IndexScanRangeBounds<IndexType, K>,\n    /// >     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n    /// > ```\n    /// <!-- TODO: check if that error is up to date! -->\n    pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> + use<B, K, Tbl, IndexType, Idx>\n    where\n        B: IndexScanRangeBounds<IndexType, K>,\n    {\n        filter::<Tbl, Idx, IndexType, B, K>(b)\n    }\n\n    /// Deletes all rows in the database state where the indexed column(s) match the bounds `b`.\n    ///\n    /// This method accepts a variable numbers of arguments using the [`IndexScanRangeBounds`] trait.\n    /// This depends on the type of the B-Tree index. `b` may be:\n    /// - A value for the first indexed column.\n    /// - A range of values for the first indexed column.\n    /// - A tuple of values for any prefix of the indexed columns, optionally terminated by a range for the next.\n    ///\n    /// For example:\n    ///\n    /// ```no_run\n    /// # #[cfg(target_arch = \"wasm32\")] mod demo {\n    /// use spacetimedb::{table, ReducerContext, RangedIndex};\n    ///\n    /// #[table(accessor = user,\n    ///     index(accessor = dogs_and_name, btree(columns = [dogs, name])))]\n    /// struct User {\n    ///     id: u32,\n    ///     name: String,\n    ///     dogs: u64\n    /// }\n    ///\n    /// fn demo(ctx: &ReducerContext) {\n    ///     let by_dogs_and_name: RangedIndex<_, (u64, String), _> = ctx.db.user().dogs_and_name();\n    ///\n    ///     // Delete users with exactly 25 dogs.\n    ///     by_dogs_and_name.delete(25u64); // The `u64` is required, see below.\n    ///\n    ///     // Delete users with at least 25 dogs.\n    ///     by_dogs_and_name.delete(25u64..);\n    ///\n    ///     // Delete users with exactly 25 dogs, and a name beginning with \"J\".\n    ///     by_dogs_and_name.delete((25u64, \"J\"..\"K\"));\n    ///\n    ///     // Delete users with exactly 25 dogs, and exactly the name \"Joseph\".\n    ///     by_dogs_and_name.delete((25u64, \"Joseph\"));\n    ///\n    ///     // You can also pass arguments by reference if desired.\n    ///     by_dogs_and_name.delete((&25u64, &\"Joseph\".to_string()));\n    /// }\n    /// # }\n    /// ```\n    ///\n    /// **NOTE:** An unfortunate interaction between Rust's trait solver and integer literal defaulting rules means that you must specify the types of integer literals passed to `filter` and `find` methods via the suffix syntax, like `21u32`.\n    ///\n    /// If you don't, you'll see a compiler error like:\n    /// > ```text\n    /// > error[E0271]: type mismatch resolving `<i32 as FilterableValue>::Column == u32`\n    /// >    --> modules/rust-wasm-test/src/lib.rs:356:48\n    /// >     |\n    /// > 356 |     for person in ctx.db.person().age().filter(21) {\n    /// >     |                                         ------ ^^ expected `u32`, found `i32`\n    /// >     |                                         |\n    /// >     |                                         required by a bound introduced by this call\n    /// >     |\n    /// >     = note: required for `i32` to implement `IndexScanRangeBounds<(u32,), SingleBound>`\n    /// > note: required by a bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n    /// >     |\n    /// > 410 |     pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row>\n    /// >     |            ------ required by a bound in this associated function\n    /// > 411 |     where\n    /// > 412 |         B: IndexScanRangeBounds<IndexType, K>,\n    /// >     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n    /// > ```\n    ///\n    /// May panic if deleting any one of the rows would violate a constraint,\n    /// though at present no such constraints exist.\n    pub fn delete<B, K>(&self, b: B) -> u64\n    where\n        B: IndexScanRangeBounds<IndexType, K>,\n    {\n        let index_id = Idx::index_id();\n        if const { is_point_scan::<Idx, B, _, _>() } {\n            b.with_point_arg(|point| {\n                sys::datastore_delete_by_index_scan_point_bsatn(index_id, point)\n                    .unwrap_or_else(|e| {\n                        panic!(\"unexpected error from `datastore_delete_by_index_scan_point_bsatn`: {e}\")\n                    })\n                    .into()\n            })\n        } else {\n            let args = b.get_range_args();\n            let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();\n            sys::datastore_delete_by_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)\n                .unwrap_or_else(|e| panic!(\"unexpected error from `datastore_delete_by_index_scan_range_bsatn`: {e}\"))\n                .into()\n        }\n    }\n}\n\n/// Performs a ranged scan using the range arguments `B` in `Tbl` using `Idx`.\n///\n/// The type parameter `K` is either `()` or [`SingleBound`]\n/// and is used to workaround the orphan rule.\nfn filter<Tbl, Idx, IndexType, B, K>(b: B) -> impl Iterator<Item = Tbl::Row>\nwhere\n    Tbl: Table,\n    Idx: Index,\n    B: IndexScanRangeBounds<IndexType, K>,\n{\n    let index_id = Idx::index_id();\n\n    let iter = if const { is_point_scan::<Idx, B, _, _>() } {\n        b.with_point_arg(|point| {\n            sys::datastore_index_scan_point_bsatn(index_id, point)\n                .unwrap_or_else(|e| panic!(\"unexpected error from `datastore_index_scan_point_bsatn`: {e}\"))\n        })\n    } else {\n        let args = b.get_range_args();\n        let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();\n        sys::datastore_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)\n            .unwrap_or_else(|e| panic!(\"unexpected error from `datastore_index_scan_range_bsatn`: {e}\"))\n    };\n\n    TableIter::new(iter)\n}\n\n/// A read-only handle to a B-tree or Direct index.\n///\n/// This is the read-only version of [`RangedIndex`].\n/// It mirrors [`RangedIndex`] but exposes only `.filter(..)`, not `.delete(..)`.\n/// It is used by `{table}__ViewHandle` to keep view code read-only at compile time.\n///\n/// Note, the `Tbl` generic is the read-write table handle `{table}__TableHandle`.\n/// This is because read-only indexes still need [`Table`] metadata.\n/// The view handle itself deliberately does not implement `Table`.\npub struct RangedIndexReadOnly<Tbl: Table, IndexType, Idx: Index> {\n    _marker: PhantomData<(Tbl, IndexType, Idx)>,\n}\n\nimpl<Tbl: Table, IndexType, Idx: Index> RangedIndexReadOnly<Tbl, IndexType, Idx> {\n    #[doc(hidden)]\n    pub const __NEW: Self = Self { _marker: PhantomData };\n\n    pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> + use<B, K, Tbl, IndexType, Idx>\n    where\n        B: IndexScanRangeBounds<IndexType, K>,\n    {\n        filter::<Tbl, Idx, IndexType, B, K>(b)\n    }\n}\n\n/// Returns whether `B` is a point scan on `I`.\n///\n/// The type parameter `K` is either `()` or [`SingleBound`]\n/// and is used to workaround the orphan rule.\nconst fn is_point_scan<I: Index, B: IndexScanRangeBounds<T, K>, T, K>() -> bool {\n    B::POINT && B::COLS_PROVIDED == I::NUM_COLS_INDEXED\n}\n\n/// Trait used for overloading methods on [`RangedIndex`].\n/// See [`RangedIndex`] for more information.\n///\n/// The type parameter `K` is either `()` or [`SingleBound`]\n/// and is used to workaround the orphan rule.\npub trait IndexScanRangeBounds<T, K = ()> {\n    /// True if no range occurs in this range bounds.\n    #[doc(hidden)]\n    const POINT: bool;\n\n    /// The number of columns mentioned in this range bounds.\n    /// For `(42, 12..24)` it's `2`.\n    #[doc(hidden)]\n    const COLS_PROVIDED: usize;\n\n    // TODO(perf, centril): once we have stable specialization,\n    // just use `to_le_bytes` internally instead.\n    #[doc(hidden)]\n    fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R;\n\n    #[doc(hidden)]\n    fn get_range_args(&self) -> IndexScanRangeArgs;\n}\n\n#[doc(hidden)]\n/// Arguments to one of the ranged-index-scan-related host-/sys-calls.\n///\n/// All pointers passed into the syscall are packed into a single buffer, `data`,\n/// with slices taken at the appropriate offsets, to save allocatons in WASM.\npub struct IndexScanRangeArgs {\n    data: IterBuf,\n    prefix_elems: usize,\n    rstart_idx: usize,\n    // None if rstart and rend are the same\n    rend_idx: Option<usize>,\n}\n\nimpl IndexScanRangeArgs {\n    /// Get slices into `self.data` for the prefix, range start and range end.\n    pub(crate) fn args_for_syscall(&self) -> (&[u8], ColId, &[u8], &[u8]) {\n        let prefix = &self.data[..self.rstart_idx];\n        let (rstart, rend) = if let Some(rend_idx) = self.rend_idx {\n            (&self.data[self.rstart_idx..rend_idx], &self.data[rend_idx..])\n        } else {\n            let elem = &self.data[self.rstart_idx..];\n            (elem, elem)\n        };\n        (prefix, ColId::from(self.prefix_elems), rstart, rend)\n    }\n}\n\n// Implement `IndexScanRangeBounds` for all the different index column types\n// and filter argument types we support.\nmacro_rules! impl_index_scan_range_bounds {\n    // In the first pattern, we accept two Prolog-style lists of type variables,\n    // the first of which we use for the column types in the index,\n    // and the second for the arguments supplied to the filter function.\n    // We do our \"outer recursion\" to visit the sublists of these two lists,\n    // at each step implementing the trait for indexes of that many columns.\n    //\n    // There's also an \"inner recursion\" later on, which, given a fixed number of columns,\n    // implements the trait with the arguments being all the prefixes of that list.\n    (($ColTerminator:ident $(, $ColPrefix:ident)*), ($ArgTerminator:ident $(, $ArgPrefix:ident)*)) => {\n        // Implement the trait for all arguments N-column indexes.\n        // The \"inner recursion\" described above happens in here.\n        impl_index_scan_range_bounds!(@inner_recursion (), ($ColTerminator $(, $ColPrefix)*), ($ArgTerminator $(, $ArgPrefix)*));\n\n        // Recurse on the suffix of the two lists, to implement the trait for all arguments to (N - 1)-column indexes.\n        impl_index_scan_range_bounds!(($($ColPrefix),*), ($($ArgPrefix),*));\n    };\n    // Base case for the previous \"outer recursion.\"\n    ((), ()) => {};\n\n    // The recursive case for the inner loop.\n    //\n    // When we start this recursion, `$ColUnused` will be empty,\n    // so we'll implement N-element queries on N-column indexes.\n    // The next call will move one type name from `($ColTerminator, $ColPrefix)` into `$ColUnused`,\n    // so we'll implement (N - 1)-element queries on N-column indexes.\n    // And so on.\n    (@inner_recursion ($($ColUnused:ident),*), ($ColTerminator:ident $(, $ColPrefix:ident)+), ($ArgTerminator:ident $(, $ArgPrefix:ident)+)) => {\n        // Emit the actual `impl IndexScanRangeBounds` form for M-element queries on N-column indexes.\n        impl_index_scan_range_bounds!(@emit_impl ($($ColUnused),*), ($ColTerminator $(,$ColPrefix)*), ($ArgTerminator $(, $ArgPrefix)*));\n        // Recurse, to implement for (M - 1)-element queries on N-column indexes.\n        impl_index_scan_range_bounds!(@inner_recursion ($($ColUnused,)* $ColTerminator), ($($ColPrefix),*), ($($ArgPrefix),*));\n    };\n    // Base case for the inner recursive loop, when there is only one column remaining.\n    // Implement the trait for both single-element tuples of arguments,\n    // and for an argument passed outside of a tuple.\n    //\n    // As in the following `@emit_impl` case:\n    // - `$ColUnused` are the types of the ignored suffix of the indexed columns.\n    // - `$ColTerminator` is the type of the queried indexed column,\n    //   which may have a range supplied as its argument.\n    // - `$ArgTerminator` is the type of the argument provided for the queried column.\n    //   More precisely it is the \"inner\" type, like `i32` or `&str`,\n    //   which may be wrapped in a range like `std::ops::Range<$ArgTerminator>`.\n    // - `Term` (not a meta-variable) is the type of the range wrapped around the `$ArgTerminator`.\n    (@inner_recursion ($($ColUnused:ident),*), ($ColTerminator:ident), ($ArgTerminator:ident)) => {\n        // Implementation for one-element tuples: defer to the implementation for bare values.\n        impl<\n            $($ColUnused,)*\n            $ColTerminator,\n            Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,\n            $ArgTerminator: FilterableValue<Column = $ColTerminator>,\n        > IndexScanRangeBounds<($ColTerminator, $($ColUnused,)*)> for (Term,) {\n            const POINT: bool = Term::POINT;\n            const COLS_PROVIDED: usize = 1;\n\n            fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {\n                IndexScanRangeBounds::<($ColTerminator, $($ColUnused,)*), SingleBound>::with_point_arg(&self.0, run)\n            }\n\n            fn get_range_args(&self) -> IndexScanRangeArgs {\n                IndexScanRangeBounds::<($ColTerminator, $($ColUnused,)*), SingleBound>::get_range_args(&self.0)\n            }\n        }\n        // Implementation for bare values: serialize the value as the terminating bounds.\n        impl<\n            $($ColUnused,)*\n            $ColTerminator,\n            Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,\n            $ArgTerminator: FilterableValue<Column = $ColTerminator>,\n        > IndexScanRangeBounds<($ColTerminator, $($ColUnused,)*), SingleBound> for Term {\n            const POINT: bool = Term::POINT;\n            const COLS_PROVIDED: usize = 1;\n\n            fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {\n                // We can assume here that we have a point bound.\n                run(&IterBuf::serialize(self.point()).unwrap())\n            }\n\n            fn get_range_args(&self) -> IndexScanRangeArgs {\n                let mut data = IterBuf::take();\n                let rend_idx = self.bounds().serialize_into(&mut data);\n                IndexScanRangeArgs { data, prefix_elems: 0, rstart_idx: 0, rend_idx }\n            }\n        }\n    };\n\n    // - `$ColUnused` are the types of the ignored suffix of the indexed columns.\n    // - `$ColTerminator` is the type of the last queried indexed column,\n    //   which may have a range supplied as its argument.\n    // - `$ColPrefix` are the types of the queried prefix of the indexed columns,\n    //   which must have single values supplied as their arguments.\n    // - `$ArgTerminator` is the type of the argument provided for the last queried column.\n    //   More precisely it is the \"inner\" type, like `i32` or `&str`,\n    //   which may be wrapped in a range like `std::ops::Range<$ArgTerminator>`.\n    // - `Term` (not a meta-variable) is the type of the range wrapped around the `$ArgTerminator`.\n    // - `$ArgPrefix` are the types of the arguments provided for the queried prefix columns.\n    (@emit_impl ($($ColUnused:ident),*), ($ColTerminator:ident $(, $ColPrefix:ident)+), ($ArgTerminator:ident $(, $ArgPrefix:ident)+)) => {\n        impl<\n            $($ColUnused,)*\n            $ColTerminator,\n            $($ColPrefix,)*\n            Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,\n            $ArgTerminator: FilterableValue<Column = $ColTerminator>,\n            $($ArgPrefix: FilterableValue<Column = $ColPrefix>,)+\n        > IndexScanRangeBounds<\n            ($($ColPrefix,)+\n             $ColTerminator,\n             $($ColUnused,)*)\n          > for ($($ArgPrefix,)+ Term,) {\n            const POINT: bool = Term::POINT;\n            const COLS_PROVIDED: usize = 1 + impl_index_scan_range_bounds!(@count $($ColPrefix)+);\n\n            fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {\n                // We can assume here that we have a point bound.\n                let mut data = IterBuf::take();\n\n                // Destructure the argument tuple into variables with the same names as their types.\n                #[allow(non_snake_case)]\n                let ($($ArgPrefix,)+ term,) = self;\n\n                // For each part in the tuple queried, serialize it into the `data` buffer.\n                Ok(())\n                    $(.and_then(|()| data.serialize_into($ArgPrefix)))+\n                    .and_then(|()| data.serialize_into(term.point()))\n                    .unwrap();\n\n                run(&*data)\n            }\n\n            fn get_range_args(&self) -> IndexScanRangeArgs {\n                let mut data = IterBuf::take();\n\n                // Get the number of prefix elements.\n                let prefix_elems = impl_index_scan_range_bounds!(@count $($ColPrefix)+);\n\n                // Destructure the argument tuple into variables with the same names as their types.\n                #[allow(non_snake_case)]\n                let ($($ArgPrefix,)+ term,) = self;\n\n                // For each prefix queried, serialize it into the `data` buffer.\n                Ok(())\n                    $(.and_then(|()| data.serialize_into($ArgPrefix)))+\n                    .unwrap();\n\n                // Remember the separator between the prefix and the terminator,\n                // so that we can slice them separately and pass them to the appropriate filter host call.\n                let rstart_idx = data.len();\n\n                // Serialize the terminating range,\n                // and get the info required to separately slice the lower and upper bounds of that range\n                // since the host call takes those as separate slices.\n                let rend_idx = term.bounds().serialize_into(&mut data);\n                IndexScanRangeArgs { data, prefix_elems, rstart_idx, rend_idx }\n            }\n        }\n    };\n\n    // Counts the number of elements in the tuple.\n    (@count $($T:ident)*) => {\n        0 $(+ impl_index_scan_range_bounds!(@drop $T 1))*\n    };\n    (@drop $a:tt $b:tt) => { $b };\n}\n\npub struct SingleBound;\n\nimpl_index_scan_range_bounds!(\n    (ColA, ColB, ColC, ColD, ColE, ColF),\n    (ArgA, ArgB, ArgC, ArgD, ArgE, ArgF)\n);\n\n// Single-column indexes\n// impl<T> IndexScanRangeBounds<(T,)> for Range<T> {}\n// impl<T> IndexScanRangeBounds<(T,)> for T {}\n\n// // Two-column indexes\n// impl<T, U> IndexScanRangeBounds<(T, U)> for Range<T> {}\n// impl<T, U> IndexScanRangeBounds<(T, U)> for T {}\n// impl<T, U> IndexScanRangeBounds<(T, U)> for (T, Range<U>) {}\n// impl<T, U> IndexScanRangeBounds<(T, U)> for (T, U) {}\n\n// // Three-column indexes\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for Range<T> {}\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for T {}\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for (T, Range<U>) {}\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for (T, U) {}\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for (T, U, Range<V>) {}\n// impl<T, U, V> IndexScanRangeBounds<(T, U, V)> for (T, U, V) {}\n\n/// A trait for types that can have a sequence based on them.\n/// This is used for auto-inc columns to determine if an insertion of a row\n/// will require the column to be updated in the row.\npub trait SequenceTrigger: Sized {\n    /// Is this value one that will trigger a sequence, if any,\n    /// when used as a column value.\n    /// For numeric types, this is `0`.\n    fn is_sequence_trigger(&self) -> bool;\n    /// Should invoke `BufReader::get_{Self}`, for example `BufReader::get_u32`.\n    fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError>;\n    /// Read a generated column from the slice, if this row was a sequence trigger.\n    #[inline(always)]\n    fn maybe_decode_into(&mut self, gen_cols: &mut &[u8]) {\n        if self.is_sequence_trigger() {\n            *self = Self::decode(gen_cols).unwrap_or_else(|_| sequence_decode_error())\n        }\n    }\n}\n\n#[cold]\n#[inline(never)]\nfn sequence_decode_error() -> ! {\n    unreachable!(\"a row was a sequence trigger but there was no generated column for it.\")\n}\n\nmacro_rules! impl_seq_trigger {\n    ($($get:ident($t:ty),)*) => {\n        $(\n            impl SequenceTrigger for $t {\n                #[inline(always)]\n                fn is_sequence_trigger(&self) -> bool { *self == 0 }\n                #[inline(always)]\n                fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {\n                    reader.$get()\n                }\n            }\n        )*\n    };\n}\n\nimpl_seq_trigger!(\n    get_u8(u8),\n    get_i8(i8),\n    get_u16(u16),\n    get_i16(i16),\n    get_u32(u32),\n    get_i32(i32),\n    get_u64(u64),\n    get_i64(i64),\n    get_u128(u128),\n    get_i128(i128),\n);\n\nimpl SequenceTrigger for crate::sats::i256 {\n    #[inline(always)]\n    fn is_sequence_trigger(&self) -> bool {\n        *self == Self::ZERO\n    }\n    #[inline(always)]\n    fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {\n        reader.get_i256()\n    }\n}\n\nimpl SequenceTrigger for crate::sats::u256 {\n    #[inline(always)]\n    fn is_sequence_trigger(&self) -> bool {\n        *self == Self::ZERO\n    }\n    #[inline(always)]\n    fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {\n        reader.get_u256()\n    }\n}\n\n/// Insert a row of type `T` into the table identified by `table_id`.\n#[track_caller]\nfn insert<T: Table>(mut row: T::Row, mut buf: IterBuf) -> Result<T::Row, TryInsertError<T>> {\n    let table_id = T::table_id();\n    // Encode the row as bsatn into the buffer `buf`.\n    buf.clear();\n    buf.serialize_into(&row).unwrap();\n\n    // Insert row into table.\n    // When table has an auto-incrementing column, we must re-decode the changed `buf`.\n    let res = sys::datastore_insert_bsatn(table_id, &mut buf).map(|gen_cols| {\n        // Let the caller handle any generated columns written back by `sys::datastore_insert_bsatn` to `buf`.\n        T::integrate_generated_columns(&mut row, gen_cols);\n        row\n    });\n    res.map_err(|e| {\n        let err = match e {\n            sys::Errno::UNIQUE_ALREADY_EXISTS => {\n                T::UniqueConstraintViolation::get().map(TryInsertError::UniqueConstraintViolation)\n            }\n            sys::Errno::AUTO_INC_OVERFLOW => T::AutoIncOverflow::get().map(TryInsertError::AutoIncOverflow),\n            _ => None,\n        };\n        err.unwrap_or_else(|| panic!(\"unexpected insertion error: {e}\"))\n    })\n}\n\n/// Update a row of type `T` to `row` using the index identified by `index_id`.\n#[track_caller]\nfn update<T: Table>(index_id: IndexId, mut row: T::Row, mut buf: IterBuf) -> T::Row {\n    let table_id = T::table_id();\n    // Encode the row as bsatn into the buffer `buf`.\n    buf.clear();\n    buf.serialize_into(&row).unwrap();\n\n    // Insert row into table.\n    // When table has an auto-incrementing column, we must re-decode the changed `buf`.\n    let res = sys::datastore_update_bsatn(table_id, index_id, &mut buf).map(|gen_cols| {\n        // Let the caller handle any generated columns written back by `sys::datastore_update_bsatn` to `buf`.\n        T::integrate_generated_columns(&mut row, gen_cols);\n        row\n    });\n\n    // TODO(centril): introduce a `TryUpdateError`.\n    res.unwrap_or_else(|e| panic!(\"unexpected update error: {e}\"))\n}\n\n/// A table iterator which yields values of the `TableType` corresponding to the table.\nstruct TableIter<T: DeserializeOwned> {\n    /// The underlying source of our `Buffer`s.\n    inner: sys::RowIter,\n\n    /// The current position in the buffer, from which `deserializer` can read.\n    reader: Cursor<IterBuf>,\n\n    _marker: PhantomData<T>,\n}\n\nimpl<T: DeserializeOwned> TableIter<T> {\n    #[inline]\n    fn new(iter: sys::RowIter) -> Self {\n        TableIter::new_with_buf(iter, IterBuf::take())\n    }\n\n    #[inline]\n    fn new_with_buf(iter: sys::RowIter, mut buf: IterBuf) -> Self {\n        buf.clear();\n        TableIter {\n            inner: iter,\n            reader: Cursor::new(buf),\n            _marker: PhantomData,\n        }\n    }\n\n    fn is_exhausted(&self) -> bool {\n        (&self.reader).remaining() == 0 && self.inner.is_exhausted()\n    }\n}\n\nimpl<T: DeserializeOwned> Iterator for TableIter<T> {\n    type Item = T;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            // If we currently have some bytes in the buffer to still decode, do that.\n            if (&self.reader).remaining() > 0 {\n                let row = bsatn::from_reader(&mut &self.reader).expect(\"Failed to decode row!\");\n                return Some(row);\n            }\n\n            // Don't fetch the next chunk if there is none.\n            if self.inner.is_exhausted() {\n                return None;\n            }\n\n            // Otherwise, try to fetch the next chunk while reusing the buffer.\n            self.reader.buf.clear();\n            self.reader.pos.set(0);\n            self.inner.read(&mut self.reader.buf);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings/tests/deps.rs",
    "content": "//! Snapshot testing for the dependency tree of the `bindings` crate - we want\n//! to make sure we don't unknowingly add a bunch of dependencies here,\n//! slowing down compilation for every spacetime module.\n\n#[test]\nfn deptree_snapshot() -> std::io::Result<()> {\n    let cmd_common = \"cargo tree -p spacetimedb -e no-dev --color never --target wasm32-unknown-unknown\";\n    let cmd = &format!(\"{cmd_common} -f {{lib}}\");\n    let deps_tree = run_cmd(cmd);\n    let all_deps = run_cmd(&format!(\"{cmd_common} --prefix none --no-dedupe\"));\n    let mut all_deps = all_deps.lines().collect::<Vec<_>>();\n    all_deps.sort();\n    all_deps.dedup();\n    let num_deps = all_deps.len();\n\n    insta::assert_snapshot!(\n        \"spacetimedb_bindings_dependencies\",\n        format!(\"total crates: {num_deps}\\n{deps_tree}\"),\n        cmd\n    );\n\n    let cmd = &format!(\"{cmd_common} -d --depth 0\");\n    insta::assert_snapshot!(\"duplicate_deps\", run_cmd(cmd), cmd);\n\n    Ok(())\n}\n\n// runs a command string, split on spaces\n#[track_caller]\nfn run_cmd(cmd: &str) -> String {\n    let mut args = cmd.split(' ');\n    let output = std::process::Command::new(args.next().unwrap())\n        .args(args)\n        .stdout(std::process::Stdio::piped())\n        .output()\n        .unwrap();\n    assert!(output.status.success());\n    String::from_utf8(output.stdout).unwrap()\n}\n"
  },
  {
    "path": "crates/bindings/tests/snapshots/deps__duplicate_deps.snap",
    "content": "---\nsource: crates/bindings/tests/deps.rs\nexpression: cargo tree -p spacetimedb -e no-dev -d --depth 0\n---\nheck v0.4.1\n\nheck v0.5.0\n"
  },
  {
    "path": "crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap",
    "content": "---\nsource: crates/bindings/tests/deps.rs\nexpression: \"cargo tree -p spacetimedb -e no-dev --color never --target wasm32-unknown-unknown -f {lib}\"\n---\ntotal crates: 73\nspacetimedb\n├── anyhow\n├── bytemuck\n├── bytes\n├── derive_more\n│   ├── convert_case\n│   ├── proc_macro2\n│   │   └── unicode_ident\n│   ├── quote\n│   │   └── proc_macro2 (*)\n│   └── syn\n│       ├── proc_macro2 (*)\n│       ├── quote (*)\n│       └── unicode_ident\n│   [build-dependencies]\n│   └── rustc_version\n│       └── semver\n├── getrandom\n│   └── cfg_if\n├── http\n│   ├── bytes\n│   ├── fnv\n│   └── itoa\n├── log\n├── rand\n│   ├── rand_chacha\n│   │   ├── ppv_lite86\n│   │   │   └── zerocopy\n│   │   └── rand_core\n│   │       └── getrandom (*)\n│   └── rand_core (*)\n├── scoped_tls\n├── serde_json\n│   ├── itoa\n│   ├── memchr\n│   ├── ryu\n│   └── serde_core\n├── spacetimedb_bindings_macro\n│   ├── heck\n│   ├── humantime\n│   ├── proc_macro2 (*)\n│   ├── quote (*)\n│   ├── spacetimedb_primitives\n│   │   ├── bitflags\n│   │   ├── either\n│   │   ├── enum_as_inner\n│   │   │   ├── heck\n│   │   │   ├── proc_macro2 (*)\n│   │   │   ├── quote (*)\n│   │   │   └── syn (*)\n│   │   ├── itertools\n│   │   │   └── either\n│   │   └── nohash_hasher\n│   └── syn (*)\n├── spacetimedb_bindings_sys\n│   └── spacetimedb_primitives\n│       ├── bitflags\n│       ├── either\n│       ├── enum_as_inner (*)\n│       ├── itertools\n│       │   └── either\n│       └── nohash_hasher\n├── spacetimedb_lib\n│   ├── anyhow\n│   ├── bitflags\n│   ├── blake3\n│   │   ├── arrayref\n│   │   ├── arrayvec\n│   │   ├── cfg_if\n│   │   └── constant_time_eq\n│   │   [build-dependencies]\n│   │   └── cc\n│   │       ├── find_msvc_tools\n│   │       └── shlex\n│   ├── chrono\n│   │   └── num_traits\n│   │       [build-dependencies]\n│   │       └── autocfg\n│   ├── derive_more (*)\n│   ├── enum_as_inner (*)\n│   ├── hex\n│   ├── itertools (*)\n│   ├── log\n│   ├── spacetimedb_bindings_macro (*)\n│   ├── spacetimedb_primitives (*)\n│   ├── spacetimedb_sats\n│   │   ├── anyhow\n│   │   ├── arrayvec\n│   │   ├── bitflags\n│   │   ├── bytemuck\n│   │   ├── bytes\n│   │   ├── chrono (*)\n│   │   ├── decorum\n│   │   │   ├── approx\n│   │   │   │   └── num_traits (*)\n│   │   │   └── num_traits (*)\n│   │   ├── derive_more (*)\n│   │   ├── enum_as_inner (*)\n│   │   ├── ethnum\n│   │   │   └── serde\n│   │   │       └── serde_core\n│   │   ├── hex\n│   │   ├── itertools (*)\n│   │   ├── lean_string\n│   │   │   ├── castaway\n│   │   │   │   └── rustversion\n│   │   │   ├── itoa\n│   │   │   └── ryu\n│   │   ├── second_stack\n│   │   ├── sha3\n│   │   │   ├── digest\n│   │   │   │   ├── block_buffer\n│   │   │   │   │   └── generic_array\n│   │   │   │   │       └── typenum\n│   │   │   │   │       [build-dependencies]\n│   │   │   │   │       └── version_check\n│   │   │   │   └── crypto_common\n│   │   │   │       ├── generic_array (*)\n│   │   │   │       └── typenum\n│   │   │   └── keccak\n│   │   ├── smallvec\n│   │   ├── spacetimedb_bindings_macro (*)\n│   │   ├── spacetimedb_primitives (*)\n│   │   ├── thiserror\n│   │   │   └── thiserror_impl\n│   │   │       ├── proc_macro2 (*)\n│   │   │       ├── quote (*)\n│   │   │       └── syn (*)\n│   │   └── uuid\n│   └── thiserror (*)\n├── spacetimedb_primitives (*)\n└── spacetimedb_query_builder\n    └── spacetimedb_lib (*)\n"
  },
  {
    "path": "crates/bindings/tests/ui/reducers.rs",
    "content": "use spacetimedb::ReducerContext;\n\nstruct Test;\n\n#[spacetimedb::reducer]\nfn bad_type(_ctx: &ReducerContext, _a: Test) {}\n\n#[spacetimedb::reducer]\nfn bad_return_type(_ctx: &ReducerContext) -> Test {\n    Test\n}\n\n#[spacetimedb::reducer]\nfn lifetime<'a>(_ctx: &ReducerContext, _a: &'a str) {}\n\n#[spacetimedb::reducer]\nfn type_param<T>() {}\n\n#[spacetimedb::reducer]\nfn const_param<const X: u8>() {}\n\n#[spacetimedb::reducer]\nfn missing_ctx(_a: u8) {}\n\n#[spacetimedb::reducer]\nfn ctx_by_val(_ctx: ReducerContext, _a: u8) {}\n\n#[spacetimedb::table(accessor = scheduled_table_missing_rows, scheduled(scheduled_table_missing_rows_reducer))]\nstruct ScheduledTableMissingRows {\n    x: u8,\n    y: u8,\n}\n\n// #[spacetimedb::reducer]\n// fn scheduled_table_missing_rows_reducer(_ctx: &ReducerContext, _: &ScheduledTableMissingRows) {}\n\n#[spacetimedb::table(accessor = scheduled_table, scheduled(scheduled_table_reducer))]\nstruct ScheduledTable {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    scheduled_at: spacetimedb::ScheduleAt,\n    x: u8,\n    y: u8,\n}\n\n#[spacetimedb::reducer]\nfn scheduled_table_reducer(_ctx: &ReducerContext, _x: u8, _y: u8) {}\n\nfn main() {}\n"
  },
  {
    "path": "crates/bindings/tests/ui/reducers.stderr",
    "content": "error: type parameters are not allowed on reducers\n  --> tests/ui/reducers.rs:17:15\n   |\n17 | fn type_param<T>() {}\n   |               ^\n\nerror: const parameters are not allowed on reducers\n  --> tests/ui/reducers.rs:20:16\n   |\n20 | fn const_param<const X: u8>() {}\n   |                ^^^^^^^^^^^\n\nerror: scheduled table missing required columns; add these to your struct:\n       #[primary_key]\n       #[auto_inc]\n       scheduled_id: u64,\n       scheduled_at: spacetimedb::ScheduleAt,\n  --> tests/ui/reducers.rs:28:63\n   |\n28 | #[spacetimedb::table(accessor = scheduled_table_missing_rows, scheduled(scheduled_table_missing_rows_reducer))]\n   |                                                               ^^^^^^^^^\n\nerror[E0277]: invalid reducer signature\n --> tests/ui/reducers.rs:6:4\n  |\n5 | #[spacetimedb::reducer]\n  | ----------------------- required by a bound introduced by this call\n6 | fn bad_type(_ctx: &ReducerContext, _a: Test) {}\n  |    ^^^^^^^^ this reducer signature is not valid\n  |\n  = help: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, Test) {bad_type}`\n  = note:\n  = note: reducer signatures must match the following pattern:\n  = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n  = note: where each `Ti` type implements `SpacetimeType`.\n  = note:\nnote: required by a bound in `register_reducer`\n --> src/rt.rs\n  |\n  | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {\n  |                                                                                 ^^^^^^^^^^^^^^ required by this bound in `register_reducer`\n\nerror[E0277]: the reducer argument `Test` does not implement `SpacetimeType`\n --> tests/ui/reducers.rs:6:40\n  |\n6 | fn bad_type(_ctx: &ReducerContext, _a: Test) {}\n  |                                        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `SpacetimeType` is not implemented for `Test`\n --> tests/ui/reducers.rs:3:1\n  |\n3 | struct Test;\n  | ^^^^^^^^^^^\n  = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\n  = help: the following other types implement trait `SpacetimeType`:\n            &T\n            ()\n            AlgebraicType\n            AlgebraicTypeRef\n            Arc<T>\n            ArrayType\n            Box<T>\n            ColumnAttribute\n          and $N others\n  = note: required for `Test` to implement `ReducerArg`\n\nerror[E0277]: invalid reducer signature\n --> tests/ui/reducers.rs:6:4\n  |\n5 | #[spacetimedb::reducer]\n  | ----------------------- required by a bound introduced by this call\n6 | fn bad_type(_ctx: &ReducerContext, _a: Test) {}\n  |    ^^^^^^^^ this reducer signature is not valid\n  |\n  = help: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, Test) {bad_type}`\n  = note:\n  = note: reducer signatures must match the following pattern:\n  = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n  = note: where each `Ti` type implements `SpacetimeType`.\n  = note:\nnote: required by a bound in `invoke_reducer`\n --> src/rt.rs\n  |\n  | pub fn invoke_reducer<'a, A: Args<'a>>(\n  |        -------------- required by a bound in this function\n  |     reducer: impl Reducer<'a, A>,\n  |                   ^^^^^^^^^^^^^^ required by this bound in `invoke_reducer`\n\nerror[E0277]: invalid reducer signature\n --> tests/ui/reducers.rs:9:4\n  |\n8 | #[spacetimedb::reducer]\n  | ----------------------- required by a bound introduced by this call\n9 | fn bad_return_type(_ctx: &ReducerContext) -> Test {\n  |    ^^^^^^^^^^^^^^^ this reducer signature is not valid\n  |\n  = help: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext) -> Test {bad_return_type}`\n  = note:\n  = note: reducer signatures must match the following pattern:\n  = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n  = note: where each `Ti` type implements `SpacetimeType`.\n  = note:\nnote: required by a bound in `register_reducer`\n --> src/rt.rs\n  |\n  | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {\n  |                                                                                 ^^^^^^^^^^^^^^ required by this bound in `register_reducer`\n\nerror[E0277]: `Test` is not a valid reducer return type\n --> tests/ui/reducers.rs:9:46\n  |\n9 | fn bad_return_type(_ctx: &ReducerContext) -> Test {\n  |                                              ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `IntoReducerResult` is not implemented for `Test`\n --> tests/ui/reducers.rs:3:1\n  |\n3 | struct Test;\n  | ^^^^^^^^^^^\n  = note: reducers cannot return values -- you can only return `()` or `Result<(), impl Display>`\nhelp: the following other types implement trait `IntoReducerResult`\n --> src/rt.rs\n  |\n  | impl IntoReducerResult for () {\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()`\n...\n  | impl<E: fmt::Display> IntoReducerResult for Result<(), E> {\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Result<(), E>`\n\nerror[E0277]: invalid reducer signature\n --> tests/ui/reducers.rs:9:4\n  |\n8 | #[spacetimedb::reducer]\n  | ----------------------- required by a bound introduced by this call\n9 | fn bad_return_type(_ctx: &ReducerContext) -> Test {\n  |    ^^^^^^^^^^^^^^^ this reducer signature is not valid\n  |\n  = help: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext) -> Test {bad_return_type}`\n  = note:\n  = note: reducer signatures must match the following pattern:\n  = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n  = note: where each `Ti` type implements `SpacetimeType`.\n  = note:\nnote: required by a bound in `invoke_reducer`\n --> src/rt.rs\n  |\n  | pub fn invoke_reducer<'a, A: Args<'a>>(\n  |        -------------- required by a bound in this function\n  |     reducer: impl Reducer<'a, A>,\n  |                   ^^^^^^^^^^^^^^ required by this bound in `invoke_reducer`\n\nerror[E0277]: invalid reducer signature\n  --> tests/ui/reducers.rs:23:4\n   |\n22 | #[spacetimedb::reducer]\n   | ----------------------- required by a bound introduced by this call\n23 | fn missing_ctx(_a: u8) {}\n   |    ^^^^^^^^^^^ this reducer signature is not valid\n   |\n   = help: the trait `Reducer<'_, _>` is not implemented for fn item `fn(u8) {missing_ctx}`\n   = note:\n   = note: reducer signatures must match the following pattern:\n   = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n   = note: where each `Ti` type implements `SpacetimeType`.\n   = note:\nnote: required by a bound in `register_reducer`\n  --> src/rt.rs\n   |\n   | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {\n   |                                                                                 ^^^^^^^^^^^^^^ required by this bound in `register_reducer`\n\nerror[E0277]: the first argument of a reducer must be `&ReducerContext`\n  --> tests/ui/reducers.rs:23:20\n   |\n23 | fn missing_ctx(_a: u8) {}\n   |                    ^^ first argument must be `&ReducerContext`\n   |\n   = help: the trait `ReducerContextArg` is not implemented for `u8`\nhelp: the trait `ReducerContextArg` is implemented for `&ReducerContext`\n  --> src/rt.rs\n   |\n   | impl ReducerContextArg for &ReducerContext {}\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0277]: invalid reducer signature\n  --> tests/ui/reducers.rs:23:4\n   |\n22 | #[spacetimedb::reducer]\n   | ----------------------- required by a bound introduced by this call\n23 | fn missing_ctx(_a: u8) {}\n   |    ^^^^^^^^^^^ this reducer signature is not valid\n   |\n   = help: the trait `Reducer<'_, _>` is not implemented for fn item `fn(u8) {missing_ctx}`\n   = note:\n   = note: reducer signatures must match the following pattern:\n   = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n   = note: where each `Ti` type implements `SpacetimeType`.\n   = note:\nnote: required by a bound in `invoke_reducer`\n  --> src/rt.rs\n   |\n   | pub fn invoke_reducer<'a, A: Args<'a>>(\n   |        -------------- required by a bound in this function\n   |     reducer: impl Reducer<'a, A>,\n   |                   ^^^^^^^^^^^^^^ required by this bound in `invoke_reducer`\n\nerror[E0277]: invalid reducer signature\n  --> tests/ui/reducers.rs:26:4\n   |\n25 | #[spacetimedb::reducer]\n   | ----------------------- required by a bound introduced by this call\n26 | fn ctx_by_val(_ctx: ReducerContext, _a: u8) {}\n   |    ^^^^^^^^^^ this reducer signature is not valid\n   |\n   = help: the trait `Reducer<'_, _>` is not implemented for fn item `fn(ReducerContext, u8) {ctx_by_val}`\n   = note:\n   = note: reducer signatures must match the following pattern:\n   = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n   = note: where each `Ti` type implements `SpacetimeType`.\n   = note:\nnote: required by a bound in `register_reducer`\n  --> src/rt.rs\n   |\n   | pub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {\n   |                                                                                 ^^^^^^^^^^^^^^ required by this bound in `register_reducer`\n\nerror[E0277]: the first argument of a reducer must be `&ReducerContext`\n  --> tests/ui/reducers.rs:26:21\n   |\n26 | fn ctx_by_val(_ctx: ReducerContext, _a: u8) {}\n   |                     ^^^^^^^^^^^^^^ first argument must be `&ReducerContext`\n   |\n   = help: the trait `ReducerContextArg` is not implemented for `ReducerContext`\nhelp: the trait `ReducerContextArg` is implemented for `&ReducerContext`\n  --> src/rt.rs\n   |\n   | impl ReducerContextArg for &ReducerContext {}\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0277]: invalid reducer signature\n  --> tests/ui/reducers.rs:26:4\n   |\n25 | #[spacetimedb::reducer]\n   | ----------------------- required by a bound introduced by this call\n26 | fn ctx_by_val(_ctx: ReducerContext, _a: u8) {}\n   |    ^^^^^^^^^^ this reducer signature is not valid\n   |\n   = help: the trait `Reducer<'_, _>` is not implemented for fn item `fn(ReducerContext, u8) {ctx_by_val}`\n   = note:\n   = note: reducer signatures must match the following pattern:\n   = note:     `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\n   = note: where each `Ti` type implements `SpacetimeType`.\n   = note:\nnote: required by a bound in `invoke_reducer`\n  --> src/rt.rs\n   |\n   | pub fn invoke_reducer<'a, A: Args<'a>>(\n   |        -------------- required by a bound in this function\n   |     reducer: impl Reducer<'a, A>,\n   |                   ^^^^^^^^^^^^^^ required by this bound in `invoke_reducer`\n\nerror[E0593]: function is expected to take 2 arguments, but it takes 3 arguments\n  --> tests/ui/reducers.rs:37:60\n   |\n37 | #[spacetimedb::table(accessor = scheduled_table, scheduled(scheduled_table_reducer))]\n   | -----------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^---\n   | |                                                          |\n   | |                                                          expected function that takes 2 arguments\n   | required by a bound introduced by this call\n...\n48 | fn scheduled_table_reducer(_ctx: &ReducerContext, _x: u8, _y: u8) {}\n   | ----------------------------------------------------------------- takes 3 arguments\n   |\n   = note: required for `for<'a> fn(&'a ReducerContext, u8, u8) {scheduled_table_reducer}` to implement `Reducer<'_, (ScheduledTable,)>`\n   = note: required for `for<'a> fn(&'a ReducerContext, u8, u8) {scheduled_table_reducer}` to implement `ExportFunctionForScheduledTable<'_, ScheduledTable, FnKindReducer>`\nnote: required by a bound in `scheduled_typecheck`\n  --> src/rt.rs\n   |\n   | pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>)\n   |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `scheduled_typecheck`\n"
  },
  {
    "path": "crates/bindings/tests/ui/tables.rs",
    "content": "struct Test;\n\n#[spacetimedb::table(accessor = table)]\nstruct Table {\n    x: Test,\n}\n\n#[spacetimedb::table(accessor = type_param)]\nstruct TypeParam<T> {\n    t: T,\n}\n\n#[spacetimedb::table(accessor = const_param)]\nstruct ConstParam<const X: u8> {}\n\n#[derive(spacetimedb::SpacetimeType)]\nstruct Alpha {\n    beta: u8,\n}\n\n#[spacetimedb::table(accessor = delta)]\nstruct Delta {\n    #[unique]\n    #[index(btree)]\n    compound_a: Alpha,\n    #[index(btree)]\n    compound_b: Alpha,\n}\n\n#[spacetimedb::reducer]\nfn bad_filter_on_index(ctx: &spacetimedb::ReducerContext) {\n    ctx.db.delta().compound_a().find(Alpha { beta: 0 });\n    ctx.db.delta().compound_b().filter(Alpha { beta: 1 });\n}\n\nfn main() {}\n"
  },
  {
    "path": "crates/bindings/tests/ui/tables.stderr",
    "content": "error: type parameters are not allowed on tables\n --> tests/ui/tables.rs:9:18\n  |\n9 | struct TypeParam<T> {\n  |                  ^\n\nerror: const parameters are not allowed on tables\n  --> tests/ui/tables.rs:14:19\n   |\n14 | struct ConstParam<const X: u8> {}\n   |                   ^^^^^^^^^^^\n\nerror[E0277]: the trait bound `Test: SpacetimeType` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `SpacetimeType` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\n  = help: the following other types implement trait `SpacetimeType`:\n            &T\n            ()\n            AlgebraicType\n            AlgebraicTypeRef\n            Alpha\n            Arc<T>\n            ArrayType\n            Box<T>\n          and $N others\n\nerror[E0277]: the trait bound `Test: Deserialize<'de>` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n3 | #[spacetimedb::table(accessor = table)]\n  | --------------------------------------- required by a bound introduced by this call\n4 | struct Table {\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `Deserialize<'de>` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = help: the following other types implement trait `Deserialize<'de>`:\n            &'de [u8]\n            &'de str\n            ()\n            (T0, T1)\n            (T0, T1, T2)\n            (T0, T1, T2, T3)\n            (T0, T1, T2, T3, T4)\n            (T0, T1, T2, T3, T4, T5)\n          and $N others\nnote: required by a bound in `spacetimedb::spacetimedb_lib::de::SeqProductAccess::next_element`\n --> $WORKSPACE/crates/sats/src/de.rs\n  |\n  |     fn next_element<T: Deserialize<'de>>(&mut self) -> Result<Option<T>, Self::Error> {\n  |                        ^^^^^^^^^^^^^^^^ required by this bound in `SeqProductAccess::next_element`\n\nerror[E0277]: the trait bound `Test: Deserialize<'de>` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n3 | #[spacetimedb::table(accessor = table)]\n  | --------------------------------------- required by a bound introduced by this call\n4 | struct Table {\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `Deserialize<'de>` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = help: the following other types implement trait `Deserialize<'de>`:\n            &'de [u8]\n            &'de str\n            ()\n            (T0, T1)\n            (T0, T1, T2)\n            (T0, T1, T2, T3)\n            (T0, T1, T2, T3, T4)\n            (T0, T1, T2, T3, T4, T5)\n          and $N others\nnote: required by a bound in `spacetimedb::spacetimedb_lib::de::SeqProductAccess::validate_next_element`\n --> $WORKSPACE/crates/sats/src/de.rs\n  |\n  |     fn validate_next_element<T: Deserialize<'de>>(&mut self) -> Result<Option<()>, Self::Error> {\n  |                                 ^^^^^^^^^^^^^^^^ required by this bound in `SeqProductAccess::validate_next_element`\n\nerror[E0277]: the trait bound `Test: Deserialize<'_>` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `Deserialize<'_>` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = help: the following other types implement trait `Deserialize<'de>`:\n            &'de [u8]\n            &'de str\n            ()\n            (T0, T1)\n            (T0, T1, T2)\n            (T0, T1, T2, T3)\n            (T0, T1, T2, T3, T4)\n            (T0, T1, T2, T3, T4, T5)\n          and $N others\nnote: required by a bound in `get_field_value`\n --> $WORKSPACE/crates/sats/src/de.rs\n  |\n  |     fn get_field_value<T: Deserialize<'de>>(&mut self) -> Result<T, Self::Error> {\n  |                           ^^^^^^^^^^^^^^^^ required by this bound in `NamedProductAccess::get_field_value`\n\nerror[E0277]: the trait bound `Test: Deserialize<'_>` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `Deserialize<'_>` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = help: the following other types implement trait `Deserialize<'de>`:\n            &'de [u8]\n            &'de str\n            ()\n            (T0, T1)\n            (T0, T1, T2)\n            (T0, T1, T2, T3)\n            (T0, T1, T2, T3, T4)\n            (T0, T1, T2, T3, T4, T5)\n          and $N others\nnote: required by a bound in `validate_field_value`\n --> $WORKSPACE/crates/sats/src/de.rs\n  |\n  |     fn validate_field_value<T: Deserialize<'de>>(&mut self) -> Result<(), Self::Error> {\n  |                                ^^^^^^^^^^^^^^^^ required by this bound in `NamedProductAccess::validate_field_value`\n\nerror[E0277]: the trait bound `Test: Serialize` is not satisfied\n --> tests/ui/tables.rs:5:8\n  |\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `Serialize` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = help: the following other types implement trait `Serialize`:\n            &T\n            ()\n            (T0, T1)\n            (T0, T1, T2)\n            (T0, T1, T2, T3)\n            (T0, T1, T2, T3, T4)\n            (T0, T1, T2, T3, T4, T5)\n            (T0, T1, T2, T3, T4, T5, T6)\n          and $N others\nnote: required by a bound in `spacetimedb::spacetimedb_lib::ser::SerializeNamedProduct::serialize_element`\n --> $WORKSPACE/crates/sats/src/ser.rs\n  |\n  |     fn serialize_element<T: Serialize + ?Sized>(&mut self, name: Option<&str>, elem: &T) -> Result<(), Self::Error>;\n  |                             ^^^^^^^^^ required by this bound in `SerializeNamedProduct::serialize_element`\n\nerror[E0277]: the column type `Test` does not implement `SpacetimeType`\n --> tests/ui/tables.rs:5:8\n  |\n5 |     x: Test,\n  |        ^^^^ unsatisfied trait bound\n  |\nhelp: the trait `SpacetimeType` is not implemented for `Test`\n --> tests/ui/tables.rs:1:1\n  |\n1 | struct Test;\n  | ^^^^^^^^^^^\n  = note: table column types all must implement `SpacetimeType`\n  = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\n  = help: the following other types implement trait `SpacetimeType`:\n            &T\n            ()\n            AlgebraicType\n            AlgebraicTypeRef\n            Alpha\n            Arc<T>\n            ArrayType\n            Box<T>\n          and $N others\n  = note: required for `Test` to implement `TableColumn`\n\nerror[E0277]: `&'a Alpha` cannot appear as an argument to an index filtering operation\n  --> tests/ui/tables.rs:32:33\n   |\n32 |     ctx.db.delta().compound_a().find(Alpha { beta: 0 });\n   |                                 ^^^^ should be an integer type, `bool`, `String`, `&str`, `Identity`, `Uuid`, `ConnectionId`, `Hash` or a no-payload enum which derives `SpacetimeType`, not `&'a Alpha`\n   |\n   = help: the trait `for<'a> FilterableValue` is not implemented for `&'a Alpha`\n   = note: The allowed set of types are limited to integers, bool, strings, `Identity`, `Uuid`, `ConnectionId`, `Hash` and no-payload enums which derive `SpacetimeType`,\n   = help: the following other types implement trait `FilterableValue`:\n             &ConnectionId\n             &FunctionVisibility\n             &Identity\n             &Lifecycle\n             &TableAccess\n             &TableType\n             &bool\n             &ethnum::int::I256\n           and $N others\nnote: required by a bound in `UniqueColumn::<Tbl, <Col as spacetimedb::table::Column>::ColType, Col>::find`\n  --> src/table.rs\n   |\n   |     pub fn find(&self, col_val: impl Borrow<Col::ColType>) -> Option<Tbl::Row>\n   |            ---- required by a bound in this associated function\n   |     where\n   |         for<'a> &'a Col::ColType: FilterableValue,\n   |                                   ^^^^^^^^^^^^^^^ required by this bound in `UniqueColumn::<Tbl, <Col as Column>::ColType, Col>::find`\n\nerror[E0277]: the trait bound `Alpha: IndexScanRangeBounds<(Alpha,), SingleBound>` is not satisfied\n  --> tests/ui/tables.rs:33:40\n   |\n33 |     ctx.db.delta().compound_b().filter(Alpha { beta: 1 });\n   |                                 ------ ^^^^^^^^^^^^^^^^^ unsatisfied trait bound\n   |                                 |\n   |                                 required by a bound introduced by this call\n   |\nhelp: the trait `FilterableValue` is not implemented for `Alpha`\n  --> tests/ui/tables.rs:17:1\n   |\n17 | struct Alpha {\n   | ^^^^^^^^^^^^\n   = help: the following other types implement trait `FilterableValue`:\n             &ConnectionId\n             &FunctionVisibility\n             &Identity\n             &Lifecycle\n             &TableAccess\n             &TableType\n             &bool\n             &ethnum::int::I256\n           and $N others\n   = note: required for `Alpha` to implement `IndexScanRangeBounds<(Alpha,), SingleBound>`\nnote: required by a bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n  --> src/table.rs\n   |\n   |     pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> + use<B, K, Tbl, IndexType, Idx>\n   |            ------ required by a bound in this associated function\n   |     where\n   |         B: IndexScanRangeBounds<IndexType, K>,\n   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RangedIndex::<Tbl, IndexType, Idx>::filter`\n"
  },
  {
    "path": "crates/bindings/tests/ui/views-more.rs",
    "content": "use spacetimedb::{table, view, Identity, Query, ViewContext};\n\n#[table(accessor = player_info)]\nstruct PlayerInfo {\n    #[unique]\n    identity: Identity,\n    #[index(btree)]\n    weight: u32,\n    age: u8,\n}\n/// Comparing incompatible types in `where` condition: u8 != u32 implicitly\n#[view(accessor = view_bad_where_int_types_implicit, public)]\nfn view_bad_where_int_types_implicit(ctx: &ViewContext) -> impl Query<PlayerInfo> {\n    ctx.from.player_info().r#where(|a| a.age.eq(4200)).build()\n}\n\nfn main() {}\n"
  },
  {
    "path": "crates/bindings/tests/ui/views-more.stderr",
    "content": "error: literal out of range for `u8`\n  --> tests/ui/views-more.rs:14:49\n   |\n14 |     ctx.from.player_info().r#where(|a| a.age.eq(4200)).build()\n   |                                                 ^^^^\n   |\n   = note: the literal `4200` does not fit into the type `u8` whose range is `0..=255`\n   = note: `#[deny(overflowing_literals)]` on by default\n"
  },
  {
    "path": "crates/bindings/tests/ui/views.rs",
    "content": "use spacetimedb::{reducer, table, view, AnonymousViewContext, Identity, Query, ReducerContext, ViewContext};\n\n#[table(accessor = test)]\nstruct Test {\n    #[unique]\n    id: u32,\n    #[index(btree)]\n    x: u32,\n}\n\n#[reducer]\nfn view_handle_no_iter(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: ViewHandle does not expose `iter()`\n    for _ in read_only.db.test().iter() {}\n}\n\n#[reducer]\nfn view_handle_no_insert(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: ViewHandle does not expose `insert()`\n    read_only.db.test().insert(Test { id: 0, x: 0 });\n}\n\n#[reducer]\nfn view_handle_no_try_insert(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: ViewHandle does not expose `try_insert()`\n    read_only.db.test().try_insert(Test { id: 0, x: 0 });\n}\n\n#[reducer]\nfn view_handle_no_delete(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: ViewHandle does not expose `delete()`\n    read_only.db.test().delete(Test { id: 0, x: 0 });\n}\n\n#[reducer]\nfn read_only_unqiue_index_no_delete(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: unique read-only index does not expose `delete()`\n    read_only.db.test().id().delete(&0);\n}\n\n#[reducer]\nfn read_only_unqiue_index_no_update(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: unique read-only index does not expose `update()`\n    read_only.db.test().id().update(Test { id: 0, x: 0 });\n}\n\n#[reducer]\nfn read_only_btree_index_no_delete(ctx: &ReducerContext) {\n    let read_only = ctx.as_read_only();\n    // Should not compile: read-only btree index does not expose `delete()`\n    read_only.db.test().x().delete(0u32..);\n}\n\n#[table(accessor = player)]\nstruct Player {\n    #[unique]\n    identity: Identity,\n}\n\nstruct NotSpacetimeType {}\n\n/// Private views not allowed; must be `#[view(public, ...)]`\n#[view(accessor = view_def_no_public)]\nfn view_def_no_public(_: &ViewContext) -> Vec<Player> {\n    vec![]\n}\n\n/// Duplicate `public`\n#[view(accessor = view_def_dup_public, public, public)]\nfn view_def_dup_public() -> Vec<Player> {\n    vec![]\n}\n\n/// Duplicate `name`\n#[view(accessor = view_def_dup_name, name = view_def_dup_name, public)]\nfn view_def_dup_name() -> Vec<Player> {\n    vec![]\n}\n\n/// Unsupported attribute arg\n#[view(accessor = view_def_unsupported_arg, public, anonymous)]\nfn view_def_unsupported_arg() -> Vec<Player> {\n    vec![]\n}\n\n/// A `ViewContext` is required\n#[view(accessor = view_def_no_context, public)]\nfn view_def_no_context() -> Vec<Player> {\n    vec![]\n}\n\n/// A `ViewContext` is required\n#[view(accessor = view_def_wrong_context, public)]\nfn view_def_wrong_context(_: &ReducerContext) -> Vec<Player> {\n    vec![]\n}\n\n/// Must pass the `ViewContext` by ref\n#[view(accessor = view_def_pass_context_by_value, public)]\nfn view_def_pass_context_by_value(_: ViewContext) -> Vec<Player> {\n    vec![]\n}\n\n/// The view context must be the first parameter\n#[view(accessor = view_def_wrong_context_position, public)]\nfn view_def_wrong_context_position(_: &u32, _: &ViewContext) -> Vec<Player> {\n    vec![]\n}\n\n/// Must return `Vec<T>` or `Option<T>` where `T` is a SpacetimeType\n#[view(accessor = view_def_no_return, public)]\nfn view_def_no_return(_: &ViewContext) {}\n\n/// Must return `Vec<T>` or `Option<T>` where `T` is a SpacetimeType\n#[view(accessor = view_def_wrong_return, public)]\nfn view_def_wrong_return(_: &ViewContext) -> Player {\n    Player {\n        identity: Identity::ZERO,\n    }\n}\n\n/// Must return `Vec<T>` or `Option<T>` where `T` is a SpacetimeType\n#[view(accessor = view_def_returns_not_a_spacetime_type, public)]\nfn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option<NotSpacetimeType> {\n    None\n}\n\n/// Cannot use a view as a scheduled function\n#[view(accessor = sched_table_view, public)]\nfn sched_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec<PlayerInfo> {\n    vec![]\n}\n\n#[table(accessor = player_info)]\nstruct PlayerInfo {\n    #[unique]\n    identity: Identity,\n    #[index(btree)]\n    weight: u32,\n    age: u8,\n}\n\n/// Comparing incompatible types in `where` condition: Identity != int\n#[view(accessor = view_bad_where, public)]\nfn view_bad_where(ctx: &ViewContext) -> impl Query<Player> {\n    ctx.from.player().r#where(|a| a.identity.eq(42)).build()\n}\n\n/// Comparing incompatible types in `where` condition: u8 != u32\n#[view(accessor = view_bad_where_int_types, public)]\nfn view_bad_where_int_types(ctx: &ViewContext) -> impl Query<PlayerInfo> {\n    ctx.from.player_info().r#where(|a| a.age.eq(4200u32)).build()\n}\n\n/// Joining incompatible types\n/// -- weight is u32, identity is Identity\n#[view(accessor = view_bad_join, public)]\nfn view_bad_join(ctx: &ViewContext) -> impl Query<PlayerInfo> {\n    ctx.from\n        .player_info()\n        .left_semijoin(ctx.from.player(), |a, b| a.weight.eq(b.identity))\n        .build()\n}\n\n/// Joining non-index columns\n/// -- age is not indexed\n#[view(accessor = view_join_non_indexed_column, public)]\nfn view_join_non_indexed_column(ctx: &ViewContext) -> impl Query<PlayerInfo> {\n    ctx.from\n        .player()\n        .right_semijoin(ctx.from.player_info(), |a, b| a.identity.eq(b.age))\n        .build()\n}\n\n/// Right join returns right table's type\n/// -- should be PlayerInfo, not Player\n#[view(accessor = view_right_join_wrong_return_type, public)]\nfn view_right_join_wrong_return_type(ctx: &ViewContext) -> impl Query<Player> {\n    ctx.from\n        .player()\n        .right_semijoin(ctx.from.player_info(), |a, b| a.identity.eq(b.identity))\n        .build()\n}\n\n/// Using non-existent table\n/// -- xyz table does not exist\n#[view(accessor = view_nonexistent_table, public)]\nfn view_nonexistent_table(ctx: &ViewContext) -> impl Query<T> {\n    ctx.from.xyz().build()\n}\n\nfn main() {}\n"
  },
  {
    "path": "crates/bindings/tests/ui/views.stderr",
    "content": "error: views must be `public`, e.g. `#[view(public)]`\n  --> tests/ui/views.rs:69:1\n   |\n69 | #[view(accessor = view_def_no_public)]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n   |\n   = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror: `public` already specified\n  --> tests/ui/views.rs:75:48\n   |\n75 | #[view(accessor = view_def_dup_public, public, public)]\n   |                                                ^^^^^^\n\nerror: expected string literal\n  --> tests/ui/views.rs:81:45\n   |\n81 | #[view(accessor = view_def_dup_name, name = view_def_dup_name, public)]\n   |                                             ^^^^^^^^^^^^^^^^^\n\nerror: expected one of: `name`, `public`, `accessor`\n  --> tests/ui/views.rs:87:53\n   |\n87 | #[view(accessor = view_def_unsupported_arg, public, anonymous)]\n   |                                                     ^^^^^^^^^\n\nerror: Views must always have a context parameter: `&ViewContext` or `&AnonymousViewContext`\n  --> tests/ui/views.rs:94:1\n   |\n94 | fn view_def_no_context() -> Vec<Player> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: The first parameter of a view must be a context parameter: `&ViewContext` or `&AnonymousViewContext`; passed by reference\n   --> tests/ui/views.rs:106:38\n    |\n106 | fn view_def_pass_context_by_value(_: ViewContext) -> Vec<Player> {\n    |                                      ^^^^^^^^^^^\n\nerror: Views do not take parameters other than `&ViewContext` or `&AnonymousViewContext`\n   --> tests/ui/views.rs:112:1\n    |\n112 | fn view_def_wrong_context_position(_: &u32, _: &ViewContext) -> Vec<Player> {\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: views must return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`\n   --> tests/ui/views.rs:118:1\n    |\n118 | fn view_def_no_return(_: &ViewContext) {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: Views do not take parameters other than `&ViewContext` or `&AnonymousViewContext`\n   --> tests/ui/views.rs:136:1\n    |\n136 | fn sched_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec<PlayerInfo> {\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0425]: cannot find type `ScheduledTable` in this scope\n   --> tests/ui/views.rs:136:45\n    |\n136 | fn sched_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec<PlayerInfo> {\n    |                                             ^^^^^^^^^^^^^^ not found in this scope\n\nerror[E0425]: cannot find type `T` in this scope\n   --> tests/ui/views.rs:194:60\n    |\n194 | fn view_nonexistent_table(ctx: &ViewContext) -> impl Query<T> {\n    |                                                            ^ not found in this scope\n    |\nhelp: you might be missing a type parameter\n    |\n194 | fn view_nonexistent_table<T>(ctx: &ViewContext) -> impl Query<T> {\n    |                          +++\n\nerror[E0425]: cannot find type `T` in this scope\n   --> tests/ui/views.rs:194:60\n    |\n194 | fn view_nonexistent_table(ctx: &ViewContext) -> impl Query<T> {\n    |                                                            ^ not found in this scope\n\nerror[E0277]: the trait bound `spacetimedb::rt::ViewKind<ReducerContext>: ViewKindTrait` is not satisfied\n  --> tests/ui/views.rs:99:1\n   |\n99 | #[view(accessor = view_def_wrong_context, public)]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ViewKindTrait` is not implemented for `spacetimedb::rt::ViewKind<ReducerContext>`\n   |\nhelp: the following other types implement trait `ViewKindTrait`\n  --> src/rt.rs\n   |\n   | impl ViewKindTrait for ViewKind<ViewContext> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `spacetimedb::rt::ViewKind<ViewContext>`\n...\n   | impl ViewKindTrait for ViewKind<AnonymousViewContext> {\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `spacetimedb::rt::ViewKind<AnonymousViewContext>`\n   = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0276]: impl has stricter requirements than trait\n  --> tests/ui/views.rs:99:1\n   |\n99 | #[view(accessor = view_def_wrong_context, public)]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `spacetimedb::rt::ViewKind<ReducerContext>: ViewKindTrait`\n   |\n   = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0599]: no method named `iter` found for reference `&test__ViewHandle` in the current scope\n  --> tests/ui/views.rs:15:34\n   |\n15 |     for _ in read_only.db.test().iter() {}\n   |                                  ^^^^ method not found in `&test__ViewHandle`\n   |\n   = help: items from traits can only be used if the trait is implemented and in scope\n   = note: the following traits define an item `iter`, perhaps you need to implement one of them:\n           candidate #1: `bitflags::traits::Flags`\n           candidate #2: `spacetimedb::Table`\n\nerror[E0599]: no method named `insert` found for reference `&test__ViewHandle` in the current scope\n  --> tests/ui/views.rs:22:25\n   |\n22 |     read_only.db.test().insert(Test { id: 0, x: 0 });\n   |                         ^^^^^^ method not found in `&test__ViewHandle`\n   |\n   = help: items from traits can only be used if the trait is implemented and in scope\n   = note: the following traits define an item `insert`, perhaps you need to implement one of them:\n           candidate #1: `bitflags::traits::Flags`\n           candidate #2: `ppv_lite86::types::Vec2`\n           candidate #3: `ppv_lite86::types::Vec4`\n           candidate #4: `similar::algorithms::DiffHook`\n           candidate #5: `spacetimedb::Table`\n\nerror[E0599]: no method named `try_insert` found for reference `&test__ViewHandle` in the current scope\n  --> tests/ui/views.rs:29:25\n   |\n29 |     read_only.db.test().try_insert(Test { id: 0, x: 0 });\n   |                         ^^^^^^^^^^\n   |\n   = help: items from traits can only be used if the trait is implemented and in scope\n   = note: the following traits define an item `try_insert`, perhaps you need to implement one of them:\n           candidate #1: `http::header::map::into_header_name::Sealed`\n           candidate #2: `spacetimedb::Table`\nhelp: there is a method `try_into` with a similar name, but with different arguments\n  --> $RUST/core/src/convert/mod.rs\n   |\n   |     fn try_into(self) -> Result<T, Self::Error>;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror[E0599]: no method named `delete` found for reference `&test__ViewHandle` in the current scope\n  --> tests/ui/views.rs:36:25\n   |\n36 |     read_only.db.test().delete(Test { id: 0, x: 0 });\n   |                         ^^^^^^ method not found in `&test__ViewHandle`\n   |\n   = help: items from traits can only be used if the trait is implemented and in scope\n   = note: the following traits define an item `delete`, perhaps you need to implement one of them:\n           candidate #1: `similar::algorithms::DiffHook`\n           candidate #2: `spacetimedb::Table`\n\nerror[E0599]: no method named `delete` found for struct `UniqueColumnReadOnly<Tbl, ColType, Col>` in the current scope\n  --> tests/ui/views.rs:43:30\n   |\n43 |     read_only.db.test().id().delete(&0);\n   |                              ^^^^^^ method not found in `UniqueColumnReadOnly<test__TableHandle, u32, id>`\n\nerror[E0599]: no method named `update` found for struct `UniqueColumnReadOnly<Tbl, ColType, Col>` in the current scope\n  --> tests/ui/views.rs:50:30\n   |\n50 |     read_only.db.test().id().update(Test { id: 0, x: 0 });\n   |                              ^^^^^^ method not found in `UniqueColumnReadOnly<test__TableHandle, u32, id>`\n\nerror[E0599]: no method named `delete` found for struct `RangedIndexReadOnly<Tbl, IndexType, Idx>` in the current scope\n  --> tests/ui/views.rs:57:29\n   |\n57 |     read_only.db.test().x().delete(0u32..);\n   |                             ^^^^^^ method not found in `RangedIndexReadOnly<test__TableHandle, (u32,), x>`\n\nerror[E0599]: no function or associated item named `register` found for struct `ViewRegistrar<ReducerContext>` in the current scope\n  --> tests/ui/views.rs:99:1\n   |\n99 | #[view(accessor = view_def_wrong_context, public)]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewRegistrar<ReducerContext>`\n   |\n   = note: the function or associated item was found for\n           - `ViewRegistrar<AnonymousViewContext>`\n           - `ViewRegistrar<ViewContext>`\n   = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext`\n   --> tests/ui/views.rs:100:31\n    |\n100 | fn view_def_wrong_context(_: &ReducerContext) -> Vec<Player> {\n    |                               ^^^^^^^^^^^^^^ the trait `ViewContextArg` is not implemented for `ReducerContext`\n    |\nhelp: the following other types implement trait `ViewContextArg`\n   --> src/rt.rs\n    |\n    | impl ViewContextArg for ViewContext {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ViewContext`\n    | impl ViewContextArg for AnonymousViewContext {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `AnonymousViewContext`\n\nerror[E0599]: no function or associated item named `invoke` found for struct `ViewDispatcher<ReducerContext>` in the current scope\n  --> tests/ui/views.rs:99:1\n   |\n99 | #[view(accessor = view_def_wrong_context, public)]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `ViewDispatcher<ReducerContext>`\n   |\n   = note: the function or associated item was found for\n           - `ViewDispatcher<AnonymousViewContext>`\n           - `ViewDispatcher<ViewContext>`\n   = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: invalid view signature\n   --> tests/ui/views.rs:121:1\n    |\n121 | #[view(accessor = view_def_wrong_return, public)]\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid\n    |\n    = help: the trait `spacetimedb::rt::View<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a ViewContext) -> Player {view_def_wrong_return}`\n    = note:\n    = note: view signatures must match:\n    = note:     `Fn(&ViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\n    = note: where each `Ti` implements `SpacetimeType`.\n    = note:\nnote: required by a bound in `ViewRegistrar::<ViewContext>::register`\n   --> src/rt.rs\n    |\n    |     pub fn register<'a, A, I, T, V>(view: V)\n    |            -------- required by a bound in this associated function\n...\n    |         V: View<'a, A, T>,\n    |            ^^^^^^^^^^^^^^ required by this bound in `ViewRegistrar::<ViewContext>::register`\n    = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: Views must return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`\n   --> tests/ui/views.rs:122:46\n    |\n122 | fn view_def_wrong_return(_: &ViewContext) -> Player {\n    |                                              ^^^^^^ unsatisfied trait bound\n    |\nhelp: the trait `ViewReturn` is not implemented for `Player`\n   --> tests/ui/views.rs:61:1\n    |\n 61 | struct Player {\n    | ^^^^^^^^^^^^^\n    = help: the following other types implement trait `ViewReturn`:\n              FromWhere<T>\n              LeftSemiJoin<L>\n              Option<T>\n              RawQuery<T>\n              RightSemiJoin<R, L>\n              Vec<T>\n              spacetimedb::spacetimedb_query_builder::Table<T>\n\nerror[E0277]: invalid view signature\n   --> tests/ui/views.rs:121:1\n    |\n121 | #[view(accessor = view_def_wrong_return, public)]\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid\n    |\n    = help: the trait `spacetimedb::rt::View<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a ViewContext) -> Player {view_def_wrong_return}`\n    = note:\n    = note: view signatures must match:\n    = note:     `Fn(&ViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\n    = note: where each `Ti` implements `SpacetimeType`.\n    = note:\nnote: required by a bound in `ViewDispatcher::<ViewContext>::invoke`\n   --> src/rt.rs\n    |\n    |     pub fn invoke<'a, A, T, V>(view: V, ctx: ViewContext, args: &'a [u8]) -> Vec<u8>\n    |            ------ required by a bound in this associated function\n...\n    |         V: View<'a, A, T>,\n    |            ^^^^^^^^^^^^^^ required by this bound in `ViewDispatcher::<ViewContext>::invoke`\n    = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: invalid anonymous view signature\n   --> tests/ui/views.rs:129:1\n    |\n129 | #[view(accessor = view_def_returns_not_a_spacetime_type, public)]\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid\n    |\n    = help: the trait `AnonymousView<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a AnonymousViewContext) -> Option<NotSpacetimeType> {view_def_returns_not_a_spacetime_type}`\n    = note:\n    = note: anonymous view signatures must match:\n    = note:     `Fn(&AnonymousViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\n    = note: where each `Ti` implements `SpacetimeType`.\n    = note:\nnote: required by a bound in `ViewRegistrar::<AnonymousViewContext>::register`\n   --> src/rt.rs\n    |\n    |     pub fn register<'a, A, I, T, V>(view: V)\n    |            -------- required by a bound in this associated function\n...\n    |         V: AnonymousView<'a, A, T>,\n    |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewRegistrar::<AnonymousViewContext>::register`\n    = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied\n   --> tests/ui/views.rs:130:71\n    |\n130 | fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option<NotSpacetimeType> {\n    |                                                                       ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound\n    |\nhelp: the trait `SpacetimeType` is not implemented for `NotSpacetimeType`\n   --> tests/ui/views.rs:66:1\n    |\n 66 | struct NotSpacetimeType {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^\n    = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\n    = help: the following other types implement trait `SpacetimeType`:\n              &T\n              ()\n              AlgebraicType\n              AlgebraicTypeRef\n              Arc<T>\n              ArrayType\n              Box<T>\n              ColumnAttribute\n            and $N others\n    = note: required for `Option<NotSpacetimeType>` to implement `ViewReturn`\n\nerror[E0277]: the trait bound `NotSpacetimeType: Serialize` is not satisfied\n   --> tests/ui/views.rs:130:71\n    |\n130 | fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option<NotSpacetimeType> {\n    |                                                                       ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound\n    |\nhelp: the trait `Serialize` is not implemented for `NotSpacetimeType`\n   --> tests/ui/views.rs:66:1\n    |\n 66 | struct NotSpacetimeType {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^\n    = help: the following other types implement trait `Serialize`:\n              &T\n              ()\n              (T0, T1)\n              (T0, T1, T2)\n              (T0, T1, T2, T3)\n              (T0, T1, T2, T3, T4)\n              (T0, T1, T2, T3, T4, T5)\n              (T0, T1, T2, T3, T4, T5, T6)\n            and $N others\n    = note: required for `Option<NotSpacetimeType>` to implement `ViewReturn`\n\nerror[E0277]: invalid anonymous view signature\n   --> tests/ui/views.rs:129:1\n    |\n129 | #[view(accessor = view_def_returns_not_a_spacetime_type, public)]\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this view signature is not valid\n    |\n    = help: the trait `AnonymousView<'_, _, _>` is not implemented for fn item `for<'a> fn(&'a AnonymousViewContext) -> Option<NotSpacetimeType> {view_def_returns_not_a_spacetime_type}`\n    = note:\n    = note: anonymous view signatures must match:\n    = note:     `Fn(&AnonymousViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`\n    = note: where each `Ti` implements `SpacetimeType`.\n    = note:\nnote: required by a bound in `ViewDispatcher::<AnonymousViewContext>::invoke`\n   --> src/rt.rs\n    |\n    |     pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec<u8>\n    |            ------ required by a bound in this associated function\n...\n    |         V: AnonymousView<'a, A, T>,\n    |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ViewDispatcher::<AnonymousViewContext>::invoke`\n    = note: this error originates in the attribute macro `view` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: the trait bound `NotSpacetimeType: SpacetimeType` is not satisfied\n   --> tests/ui/views.rs:130:71\n    |\n130 | fn view_def_returns_not_a_spacetime_type(_: &AnonymousViewContext) -> Option<NotSpacetimeType> {\n    |                                                                       ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound\n    |\nhelp: the trait `SpacetimeType` is not implemented for `NotSpacetimeType`\n   --> tests/ui/views.rs:66:1\n    |\n 66 | struct NotSpacetimeType {}\n    | ^^^^^^^^^^^^^^^^^^^^^^^\n    = note: if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\n    = help: the following other types implement trait `SpacetimeType`:\n              &T\n              ()\n              AlgebraicType\n              AlgebraicTypeRef\n              Arc<T>\n              ArrayType\n              Box<T>\n              ColumnAttribute\n            and $N others\n    = note: required for `Option<NotSpacetimeType>` to implement `SpacetimeType`\n\nerror[E0277]: the trait bound `{integer}: RHS<Player, spacetimedb::Identity>` is not satisfied\n   --> tests/ui/views.rs:152:49\n    |\n152 |     ctx.from.player().r#where(|a| a.identity.eq(42)).build()\n    |                                              -- ^^ the trait `RHS<Player, spacetimedb::Identity>` is not implemented for `{integer}`\n    |                                              |\n    |                                              required by a bound introduced by this call\n    |\n    = help: the following other types implement trait `RHS<T, V>`:\n              `f32` implements `RHS<T, f32>`\n              `f64` implements `RHS<T, f64>`\n              `i128` implements `RHS<T, i128>`\n              `i16` implements `RHS<T, i16>`\n              `i32` implements `RHS<T, i32>`\n              `i64` implements `RHS<T, i64>`\n              `i8` implements `RHS<T, i8>`\n              `u128` implements `RHS<T, u128>`\n            and $N others\nnote: required by a bound in `Col::<T, V>::eq`\n   --> $WORKSPACE/crates/query-builder/src/table.rs\n    |\n    |     pub fn eq<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n    |                  ^^^^^^^^^ required by this bound in `Col::<T, V>::eq`\n\nerror[E0277]: the trait bound `u32: RHS<PlayerInfo, u8>` is not satisfied\n   --> tests/ui/views.rs:158:49\n    |\n158 |     ctx.from.player_info().r#where(|a| a.age.eq(4200u32)).build()\n    |                                              -- ^^^^^^^ the trait `RHS<PlayerInfo, u8>` is not implemented for `u32`\n    |                                              |\n    |                                              required by a bound introduced by this call\n    |\nhelp: the trait `RHS<PlayerInfo, u8>` is not implemented for `u32`\n      but trait `RHS<PlayerInfo, u32>` is implemented for it\n   --> $WORKSPACE/crates/query-builder/src/expr.rs\n    |\n    | impl_rhs!(u32, |v: u32| v.to_string());\n    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    = help: for that trait implementation, expected `u32`, found `u8`\nnote: required by a bound in `Col::<T, V>::eq`\n   --> $WORKSPACE/crates/query-builder/src/table.rs\n    |\n    |     pub fn eq<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n    |                  ^^^^^^^^^ required by this bound in `Col::<T, V>::eq`\n    = note: this error originates in the macro `impl_rhs` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0308]: mismatched types\n   --> tests/ui/views.rs:167:62\n    |\n167 |         .left_semijoin(ctx.from.player(), |a, b| a.weight.eq(b.identity))\n    |                                                           -- ^^^^^^^^^^ expected `IxCol<Player, u32>`, found `IxCol<Player, Identity>`\n    |                                                           |\n    |                                                           arguments to this method are incorrect\n    |\n    = note: expected struct `IxCol<Player, u32>`\n               found struct `IxCol<Player, spacetimedb::Identity>`\nnote: method defined here\n   --> $WORKSPACE/crates/query-builder/src/join.rs\n    |\n    |     pub fn eq<R: HasIxCols>(self, rhs: IxCol<R, V>) -> IxJoinEq<T, R, V> {\n    |            ^^\n\nerror[E0609]: no field `age` on type `&PlayerInfoIxCols`\n   --> tests/ui/views.rs:177:72\n    |\n177 |         .right_semijoin(ctx.from.player_info(), |a, b| a.identity.eq(b.age))\n    |                                                                        ^^^ unknown field\n    |\n    = note: available fields are: `identity`, `weight`\n\nerror[E0599]: no method named `xyz` found for struct `QueryBuilder` in the current scope\n   --> tests/ui/views.rs:195:14\n    |\n195 |     ctx.from.xyz().build()\n    |              ^^^ method not found in `QueryBuilder`\n"
  },
  {
    "path": "crates/bindings/tests/ui.rs",
    "content": "#[test]\nfn ui() {\n    let t = trybuild::TestCases::new();\n    t.compile_fail(\"tests/ui/*.rs\");\n}\n"
  },
  {
    "path": "crates/bindings-cpp/.gitignore",
    "content": "build*/\n"
  },
  {
    "path": "crates/bindings-cpp/ARCHITECTURE.md",
    "content": "# SpacetimeDB C++ Bindings Architecture\n\n## Overview\n\nThe SpacetimeDB C++ bindings provides a sophisticated compile-time/runtime hybrid system for building database modules in C++ that compile to WebAssembly (WASM) and run inside the SpacetimeDB database. This document describes the architectural components, type registration flow, and key differences from other language SDKs.\n\n## Core Architecture Principles\n\n### 1. Hybrid Compile-Time/Runtime System\n- **Compile-time validation**: C++20 concepts and static assertions catch constraint violations before compilation\n- **Runtime registration**: __preinit__ functions execute during WASM module load to register types and metadata\n- **Nominal type system**: Types identified by their declared names, not structural analysis\n- **Error detection**: Multi-layer validation system from compile-time through module publishing\n\n### 2. Outcome<T> - Rust-Like Error Handling\nThe SDK provides `Outcome<T>`, a type-safe error handling mechanism matching Rust's `Result<T, E>` pattern:\n- **Outcome<void>** (type alias `ReducerResult`): Used by reducers, can return success (`Ok()`) or error (`Err(message)`)\n- **Outcome<T>**: Useful for methods used by Reducers to return a value or an error message\n- **No exceptions**: Errors are handled via return values, not C++ exceptions\n- **Graceful error handling**: Reducer errors are caught by the runtime, rolled back, and reported to the caller without crashing\n- **Serializable**: Error messages are automatically serialized and sent to clients\n\n### 2. Priority-Ordered Initialization System\nThe SDK uses a numbered __preinit__ function system to ensure correct initialization order:\n\n```\n__preinit__01_ - Clear global state (first)\n__preinit__10_ - Field registration\n__preinit__19_ - Auto-increment integration and scheduled reducers\n__preinit__20_ - Table and lifecycle reducer registration\n__preinit__21_ - Field constraints\n__preinit__25_ - Row level security filters\n__preinit__30_ - User reducers\n__preinit__40_ - Views\n__preinit__50_ - Procedures\n__preinit__99_ - Type validation and error detection (last)\n```\n\n## Error Handling: Outcome<T> System\n\n### Overview\n\nThe C++ bindings uses `Outcome<T>` for type-safe, exception-free error handling that matches Rust's `Result<T, E>` pattern (where `E` is always `std::string`).\n\n### Type Aliases and Core Types\n\n```cpp\n// For reducers - cannot fail with a value, only with an error message\nusing ReducerResult = Outcome<void>;\n```\n\n### Reducer Error Handling (ReducerResult / Outcome<void>)\n\n**Creating Results**:\n```cpp\n#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\nstruct User {\n    Identity identity;\n    std::optional<std::string> name;\n    bool online;\n};\nSPACETIMEDB_STRUCT(User, identity, name, online);\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKey(user, identity);\n\n\nSPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) {\n    // Validation with early error return\n    if (name.empty()) {\n        return Err(\"Name cannot be empty\");\n    }\n    if (name.length() > 255) {\n        return Err(\"Name is too long\");\n    }\n    \n    // Success path\n    ctx.db[user].insert(User{ctx.sender(), name, false});\n    return Ok();  // No value needed - just success\n}\n```\n\n**Checking Results**:\n```cpp\nSPACETIMEDB_REDUCER(call_other_logic, ReducerContext ctx) {\n    // Note: In practice, reducers don't call other reducers directly\n    // But if implementing error-handling helper functions:\n    auto result = validate_something();\n    \n    if (result.is_err()) {\n        return Err(result.error());  // Propagate error\n    }\n    \n    // Continue on success\n    return Ok();\n}\n```\n\n**Error Semantics**:\n- When `Err()` is returned:\n  - The reducer transaction is **rolled back** (not committed to the log)\n  - The error message is captured and returned to the caller\n  - No database changes are persisted\n  - No WASM crash or panic occurs\n- When `Ok()` is returned:\n  - All database mutations are committed\n  - The transaction is logged\n  - Success is reported to the caller\n\n### Procedure Error Handling\n\n**Key Difference from Reducers**:\n- Procedures return raw `T` (not `Outcome<T>`)\n- On error, procedures can use LOG_PANIC() or LOG_FATAL() to end the host call which uses std:abort() behind the scenes\n- Return values are sent directly to the caller\n\n### Outcome<T> API Reference\n\n```cpp\n// Creating success outcomes\nOutcome<T>::Ok(value)      // Outcome<T> - with a value\nOk()                       // Outcome<void> - without a value  \nOk(value)                  // Helper - type deduced from value\n\n// Creating error outcomes\nOutcome<T>::Err(message)   // Outcome<T> - with error message\nErr(message)               // Outcome<void> - with error message\nErr<T>(message)            // Helper - explicit type specification\n\n// Checking results\noutcome.is_ok()            // bool - true if success\noutcome.is_err()           // bool - true if error\n\n// Accessing values/errors\noutcome.value()            // T& or T&& - get success value (UB if error)\noutcome.error()            // const std::string& - get error message (UB if success)\n```\n\n### Design Rationale\n\n**Why not exceptions?**\n- WASM modules have limited error handling facilities, the latest WASM allows for them but requires GC\n- Exceptions add code size and complexity\n- Explicit error returns fit better with BSATN serialization\n- Matches Rust SDK's error handling pattern\n\n**Why separate ReducerResult and Outcome<T>?**\n- Reducers need rollback semantics (transactions)\n- ReducerResult provides clearer intent for reducer code\n- Outcome<T> is more flexible for general operations\n\n---\n\n## Detailed Type Registration Flow\n\n### Phase 1: Compile-Time Validation\n\n**Location**: Template instantiation during compilation\n\n**Components**:\n- **C++20 Concepts** (`table_with_constraints.h`):\n  ```cpp\n  template<typename T>\n  concept FilterableValue = \n      std::integral<T> ||\n      std::same_as<T, std::string> ||\n      std::same_as<T, Identity> ||\n      // ... other filterable types\n  \n  template<typename T>\n  concept AutoIncrementable = \n      std::same_as<T, int8_t> ||\n      std::same_as<T, uint32_t> ||\n      // ... integer types only\n  ```\n\n- **Static Assertions** in FIELD_ macros:\n  ```cpp\n  #define FIELD_Unique(table_name, field_name) \\\n      static_assert([]() constexpr { \\\n          using FieldType = decltype(std::declval<TableType>().field_name); \\\n          static_assert(FilterableValue<FieldType>, \\\n              \"Field cannot have Unique constraint - type is not filterable.\"); \\\n          return true; \\\n      }(), \"Constraint validation for \" #table_name \".\" #field_name);\n  ```\n\n**Validation Coverage**:\n- ✅ AutoIncrement constraints (only integer types)\n- ✅ Index/Unique/PrimaryKey constraints (only filterable types)\n- ✅ Type compatibility with BSATN serialization\n- ✅ Template parameter validation\n\n**Error Output**: Clear compile-time error messages with specific guidance\n\n### Phase 2: Runtime Registration (__preinit__ functions)\n\n**Location**: WASM module load, before any user code executes\n\n#### 2.1 Global State Initialization (__preinit__01_)\n```cpp\nextern \"C\" __attribute__((export_name(\"__preinit__01_clear_global_state\")))\nvoid __preinit__01_clear_global_state() {\n    ClearV9Module();  // Reset module definition and handler registries\n    getModuleTypeRegistration().clear();  // Reset type registry and error state\n}\n```\n\n#### 2.2 Component Registration (__preinit__10-30_)\nGenerated by macros during preprocessing:\n\n**Table Registration** (__preinit__20_):\n```cpp\nSPACETIMEDB_TABLE(User, users, Public)\n// Generates:\nextern \"C\" __attribute__((export_name(\"__preinit__20_register_table_User_line_42\")))\nvoid __preinit__20_register_table_User_line_42() {\n    SpacetimeDB::Module::RegisterTable<User>(\"users\", true);\n}\n```\n\n**Field Constraints** (__preinit__21_):\n```cpp\nFIELD_PrimaryKey(users, id);\n// Generates:\nextern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_users_id_line_43\")))\nvoid __preinit__21_field_constraint_users_id_line_43() {\n    getV9Builder().AddFieldConstraint<User>(\"users\", \"id\", FieldConstraint::PrimaryKey);\n}\n```\n\n**Auto-Increment Integration Registration** (__preinit__19_):\nAuto-increment fields require special handling during `insert()` operations. When SpacetimeDB processes an auto-increment insert, it returns only the generated column values (not the full row) in BSATN format. The C++ bindings uses a registry-based integration system to properly handle these generated values and update the user's row object.\n\n```cpp\nFIELD_PrimaryKeyAutoInc(users, id);\n// Generates both constraint registration AND auto-increment integration:\n\n// 1. Auto-increment integration function (unique per field via __LINE__)\nnamespace SpacetimeDB { namespace detail {\n    static void autoinc_integrate_47(User& row, SpacetimeDB::bsatn::Reader& reader) {\n        using FieldType = decltype(std::declval<User>().id);\n        FieldType generated_value = SpacetimeDB::bsatn::deserialize<FieldType>(reader);\n        row.id = generated_value;  // Update field with generated ID\n    }\n}}\n\n// 2. Registration function to register the integrator\nextern \"C\" __attribute__((export_name(\"__preinit__19_autoinc_register_47\")))\nvoid __preinit__19_autoinc_register_47() {\n    SpacetimeDB::detail::get_autoinc_integrator<User>() = \n        &SpacetimeDB::detail::autoinc_integrate_47;\n}\n```\n\n**Runtime Integration Process**:\nWhen `insert()` is called on a table with auto-increment fields:\n1. The logic in the bindings serializes and sends the row to SpacetimeDB\n2. SpacetimeDB processes the insert and generates the auto-increment value(s)\n3. SpacetimeDB returns a buffer containing only the generated column values in BSATN format\n4. SDK calls the registered integrator function to update the original row with generated values\n5. `insert()` returns the updated row with the correct generated ID\n\nThis system enables users to immediately access generated IDs:\n```cpp\nstruct User {\n    uint64_t id;\n    std::optional<std::string> name;\n};\nSPACETIMEDB_STRUCT(User, id, name);\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKeyAutoInc(user, id);\n\nSPACETIMEDB_REDUCER(create_user2, ReducerContext ctx, std::string name) {\n    User new_user{0, name};  // id=0 will be auto-generated\n    User inserted_user = ctx.db[user].insert(new_user);  // Returns user with generated ID\n    LOG_INFO(\"Created user with ID: \" + std::to_string(inserted_user.id));\n    return Ok();  // Must return ReducerResult\n}\n```\n\n**Reducer Registration** (__preinit__30_):\n```cpp\nSPACETIMEDB_REDUCER(add_user, ReducerContext ctx, std::string name) {\n    if (name.empty()) {\n        return Err(\"Name cannot be empty\");  // Return error - rolled back\n    }\n    ctx.db[user].insert(User{0, name});\n    return Ok();  // Success - transaction committed\n}\n// Generates registration function that captures parameter types, creates dispatch handler,\n// and wraps return value in ReducerResult (Outcome<void>)\n```\n\n#### 2.3 Multiple Primary Key Detection\nDuring constraint registration, track primary keys per table:\n```cpp\n// In V9Builder::AddFieldConstraint\nif (constraint == FieldConstraint::PrimaryKey) {\n    if (table_has_primary_key[table_name]) {\n        SetMultiplePrimaryKeyError(table_name);  // Set global error flag\n    }\n    table_has_primary_key[table_name] = true;\n}\n```\n\n### Phase 3: Type System Registration\n\n**Component**: ModuleTypeRegistration system (`module_type_registration.h`)\n\n**Core Principle**: Only user-defined structs and enums get registered in the typespace. Primitives, arrays, Options, and special types are always inlined.\n\n**Architecture Note**: V9Builder serves as the registration coordinator but delegates all type processing to the ModuleTypeRegistration system. This separation ensures a single, unified type registration pathway.\n\n**Registration Flow**:\n```cpp\nclass ModuleTypeRegistration {\n    AlgebraicType registerType(const bsatn::AlgebraicType& bsatn_type,\n                              const std::string& explicit_name = \"\",\n                              const std::type_info* cpp_type = nullptr) {\n        // 1. Check if primitive → return inline\n        if (isPrimitive(bsatn_type)) return convertPrimitive(bsatn_type);\n        \n        // 2. Check if array → return inline Array with recursive element processing\n        if (bsatn_type.tag() == bsatn::AlgebraicTypeTag::Array) \n            return convertArray(bsatn_type);\n        \n        // 3. Check if Option → return inline Sum structure\n        if (isOptionType(bsatn_type)) return convertOption(bsatn_type);\n        \n        // 4. Check if special type → return inline Product structure\n        if (isSpecialType(bsatn_type)) return convertSpecialType(bsatn_type);\n        \n        // 5. User-defined type → register in typespace, return Ref\n        return registerUserDefinedType(bsatn_type, explicit_name, cpp_type);\n    }\n};\n```\n\n**Circular Reference Detection**:\n```cpp\n// Track types currently being registered\nstd::unordered_set<std::string> types_being_registered_;\n\nAlgebraicType registerUserDefinedType(...) {\n    if (types_being_registered_.contains(type_name)) {\n        setError(\"Circular reference detected in type: \" + type_name);\n        return createErrorType();\n    }\n    types_being_registered_.insert(type_name);\n    // ... process type ...\n    types_being_registered_.erase(type_name);\n}\n```\n\n### Phase 4: Validation and Error Detection (__preinit__99_)\n\n**Location**: Final preinit function - runs after all registration is complete\n\n**Error Detection**:\n```cpp\nextern \"C\" __attribute__((export_name(\"__preinit__99_validate_types\")))\nvoid __preinit__99_validate_types() {\n    // 1. Check for circular reference errors\n    if (g_circular_ref_error) {\n        createErrorModule(\"ERROR_CIRCULAR_REFERENCE_\" + g_circular_ref_type_name);\n        return;\n    }\n    \n    // 2. Check for multiple primary key errors\n    if (g_multiple_primary_key_error) {\n        createErrorModule(\"ERROR_MULTIPLE_PRIMARY_KEYS_\" + g_multiple_primary_key_table_name);\n        return;\n    }\n    \n    // 3. Check for type registration errors\n    if (getModuleTypeRegistration().hasError()) {\n        createErrorModule(\"ERROR_TYPE_REGISTRATION_\" + sanitize(error_message));\n        return;\n    }\n}\n```\n\n**Error Module Creation**: When errors are detected, the normal module is replaced with a special error module containing an invalid type reference. When SpacetimeDB tries to resolve the type, it fails with an error message that includes the descriptive error type name.\n\n### Phase 5: Module Description Export\n\n**Function**: `__describe_module__()` - Called by SpacetimeDB after preinit functions complete\n\n**Process**:\n1. Serialize the completed V9 module definition\n2. Include typespace (all registered types)\n3. Include tables with constraints\n4. Include reducers with parameter types\n5. Include named type exports\n6. Return binary module description\n\n## Namespace Qualification System\n\n### Overview\nThe C++ bindings provides a unique compile-time namespace qualification system for enum types, allowing better organization in generated client code without affecting server-side C++ usage.\n\n### Architecture Components\n\n#### 1. Compile-Time Namespace Storage\n**Location**: `enum_macro.h` - namespace_info template specialization\n\n```cpp\nnamespace SpacetimeDB::detail {\n    // Primary template - no namespace by default\n    template<typename T>\n    struct namespace_info {\n        static constexpr const char* value = nullptr;\n    };\n}\n\n// SPACETIMEDB_NAMESPACE macro creates specialization\n#define SPACETIMEDB_NAMESPACE(EnumType, NamespacePrefix) \\\n    namespace SpacetimeDB::detail { \\\n        template<> \\\n        struct namespace_info<EnumType> { \\\n            static constexpr const char* value = NamespacePrefix; \\\n        }; \\\n    }\n```\n\n#### 2. LazyTypeRegistrar Integration\n**Location**: `module_type_registration.h` - Compile-time namespace detection\n\n```cpp\ntemplate<typename T>\nclass LazyTypeRegistrar {\n    static bsatn::AlgebraicType getOrRegister(...) {\n        std::string qualified_name = type_name;\n        \n        // Compile-time check for namespace information\n        if constexpr (requires { SpacetimeDB::detail::namespace_info<T>::value; }) {\n            constexpr const char* namespace_prefix = \n                SpacetimeDB::detail::namespace_info<T>::value;\n            if (namespace_prefix != nullptr) {\n                qualified_name = std::string(namespace_prefix) + \".\" + type_name;\n            }\n        }\n        \n        // Register with qualified name\n        type_index_ = getModuleTypeRegistration().registerAndGetIndex(\n            algebraic_type, qualified_name, &typeid(T));\n    }\n};\n```\n\n#### 3. Type Registration with Namespaces\nWhen an enum with namespace qualification is registered:\n1. SPACETIMEDB_ENUM defines the enum and its BSATN traits\n2. SPACETIMEDB_NAMESPACE adds compile-time metadata\n3. LazyTypeRegistrar detects namespace at compile-time\n4. Type is registered with qualified name (e.g., \"Auth.UserRole\")\n5. Client generators recognize the namespace structure\n\n### Design Rationale\n\n**Why Separate Macros?**\n- Clean separation of concerns: enum definition vs. namespace qualification\n- Optional feature - enums work without namespaces\n- Non-intrusive - doesn't modify the enum type itself\n- Compile-time only - zero runtime overhead\n\n**Why Template Specialization?**\n- Type-safe association between enum and namespace\n- Compile-time resolution - no runtime lookups\n- Works with C++20 concepts and if constexpr\n- No memory overhead - constexpr strings\n\n### Comparison with Other Approaches\n\n**Alternative 1: Preinit Runtime Modification** (Rejected)\n- Would require modifying types after registration\n- Complex synchronization with type registry\n- Runtime overhead for namespace lookup\n\n**Alternative 2: Embedded in SPACETIMEDB_ENUM** (Rejected)\n- Would complicate the macro syntax\n- Makes namespace mandatory rather than optional\n- Harder to add namespaces to existing code\n\n**Current Approach Benefits**:\n- Clean, modular design\n- Zero runtime cost\n- Optional and backwards-compatible\n- Easy to understand and maintain\n\n## Key Differences from Rust and C# SDKs\n\n### 1. Type Registration Approach\n\n**Rust bindings**:\n- Derive macros automatically generate type registration code\n- Compile-time code generation using procedural macros\n- Direct integration with Rust's type system\n- Option types automatically inlined by macro system\n\n**C# bindings**:\n- Reflection-based runtime type discovery\n- Attribute-based configuration\n- Dynamic type registration during module initialization\n- .NET type system integration\n\n**C++ bindings**:\n- Template-based compile-time validation with runtime registration\n- Macro-generated __preinit__ functions for ordered initialization\n- Manual type registration via SPACETIMEDB_STRUCT macros\n- Hybrid approach combining compile-time safety with runtime flexibility\n\n### 2. Constraint Validation\n\n**Rust bindings**:\n- Procedural macros generate compile-time validation\n- Type system automatically enforces valid constraints\n- No runtime constraint checking needed\n\n**C# bindings**:\n- Runtime validation using reflection\n- Attributes specify constraints, validated during registration\n- Dynamic error reporting\n\n**C++ bindings**:\n- **Three-layer validation system**:\n  1. **Compile-time**: C++20 concepts and static assertions\n  2. **Registration-time**: Multiple primary key detection\n  3. **Module load**: preinit_99_ comprehensive validation\n- Most sophisticated error detection of all SDKs\n\n### 3. Error Handling Strategy\n\n**Rust bindings**:\n- Result<T, E> for operation errors with rich error types\n- Compile-time errors prevent building invalid modules\n- Type system prevents most runtime errors\n- Standard Rust error messages\n\n**C# bindings**:\n- Runtime exceptions with detailed error messages\n- Graceful error handling with exception propagation\n- .NET debugging tools integration\n\n**C++ bindings** - Two-Tier System:\n1. **Reducer errors** (ReducerResult / Outcome<void>):\n   - Return `Ok()` on success (transaction committed)\n   - Return `Err(message)` on failure (transaction rolled back)\n   - Exceptions not used for normal error cases\n   - Matches Rust's Result<(), E> pattern\n   \n2. **Type registration errors**:\n   - Invalid modules replaced with special error modules\n   - Error type names embed descriptive information\n   - SpacetimeDB server provides clear error messages\n   - Comprehensive error categorization and reporting\n\n**Outcome<T> Type**:\n- Type-safe, exception-free error handling\n- Serializable to binary format for client transmission\n- Works in WASM environment without exception infrastructure\n- API matches Rust Result pattern: `is_ok()`, `is_err()`, `value()`, `error()`\n\n### 4. Type System Philosophy\n\n**Rust bindings**: \n- \"If it compiles, it works\" - maximum compile-time validation\n- Leverages Rust's ownership and type system\n- Minimal runtime overhead\n\n**C# bindings**:\n- \"Flexibility with safety\" - runtime validation with rich error messages\n- Leverages .NET reflection and attributes\n- Dynamic type discovery\n\n**C++ bindings**:\n- **\"Validate early, validate often\"** - multi-layer validation system\n- Combines C++20 compile-time features with runtime checks\n- Nominal type system with explicit registration\n- Optimized for catching errors at the earliest possible phase\n\n## Memory Management and Performance\n\n### Compile-Time Optimizations\n- Template specialization eliminates runtime overhead\n- Constexpr evaluations reduce WASM binary size\n- Zero-cost abstractions for type-safe database access\n\n### Runtime Efficiency\n- Minimal allocation during type registration\n- Efficient binary serialization with BSATN\n- Optimized field accessors with index caching\n\n### WASM Constraints\n- 16MB initial memory limit (configurable)\n- No dynamic memory growth during module registration\n- Careful memory management in preinit functions\n\n## Development Workflow Integration\n\n### Error Detection Timeline\n```\nDeveloper writes code\n    ↓\nC++ compilation → Compile-time validation (concepts, static_assert)\n    ↓\nEmscripten WASM build → Template instantiation validation\n    ↓\nModule publishing → Runtime validation (__preinit__99_)\n    ↓\nSpacetimeDB loading → Server-side validation and error reporting\n```\n\n### Debugging Support\n- **Compile-time**: Clear error messages with field/constraint guidance\n- **Build-time**: Template instantiation error reporting\n- **Runtime**: Comprehensive logging with error categorization\n- **Server-side**: Descriptive error module names for easy diagnosis\n\n## Future Architecture Considerations\n\n### Potential Improvements\n1. **Unified Validation**: Move more validation to compile-time using concepts\n2. **Better Error Recovery**: Partial module loading with isolated error handling\n3. **Performance Optimization**: Reduce template instantiation overhead\n4. **Enhanced Debugging**: Source location tracking for runtime errors\n\n### Scalability\n- Type registration system scales linearly with module complexity\n- Preinit function count grows with table/reducer count but remains manageable\n- Memory usage is predictable and bounded\n\n## Related Documentation\n\n- [Type System Details](README.md#features)\n- [Constraint Validation Tests](tests/type-isolation-test/)\n- [API Reference](REFERENCE.md)\n- [Quick Start Guide](QUICKSTART.md)\n"
  },
  {
    "path": "crates/bindings-cpp/CMakeLists.txt",
    "content": "# SpacetimeDB C++ Module Library CMake Configuration\n\ncmake_minimum_required(VERSION 3.15)\nproject(SpacetimeDBCppModuleLibrary \n    VERSION 1.12.0\n    LANGUAGES CXX)\n\n# Generate version header from template\nconfigure_file(\n    ${CMAKE_CURRENT_SOURCE_DIR}/include/spacetimedb/version.h.in\n    ${CMAKE_CURRENT_BINARY_DIR}/include/spacetimedb/version.h\n    @ONLY\n)\n\n# Module Library source files\nset(LIBRARY_SOURCES\n    src/abi/module_exports.cpp\n    src/abi/wasi_shims.cpp\n    src/internal/Module.cpp\n    src/internal/AlgebraicType.cpp  # Required for V9 autogen types\n    src/internal/v9_builder.cpp  # V9 incremental module builder\n    src/internal/v10_builder.cpp  # V10 facade over module definition assembly\n    src/internal/module_type_registration.cpp  # Unified type registration system\n)\n\nadd_library(spacetimedb_cpp_library STATIC)\nadd_library(spacetimedb::spacetimedb_cpp_library ALIAS spacetimedb_cpp_library)\n\ntarget_sources(spacetimedb_cpp_library PRIVATE ${LIBRARY_SOURCES})\n\n# Require C++20 for consumers of this library without forcing global flags\ntarget_compile_features(spacetimedb_cpp_library PUBLIC cxx_std_20)\n\n# Set include directories\ntarget_include_directories(spacetimedb_cpp_library\n    PUBLIC\n        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>\n        $<INSTALL_INTERFACE:include>\n)\n\n# Create an alias target for better namespacing\nadd_library(spacetimedb::spacetimedb_cpp_library ALIAS spacetimedb_cpp_library)\n\n# Set compile options if building for WASM\nif(CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")\n    target_compile_options(spacetimedb_cpp_library PRIVATE \n        -O2                    # Optimize for performance\n        -fno-exceptions        # Disable exceptions for WASM compatibility\n        -ffunction-sections    # Place each function in its own section\n        -fdata-sections        # Place each data item in its own section\n        -Wall -Wextra          # Enable warnings\n    )\n    # Note: -g0 should be set in the final executable's linker flags, not here\n    # This allows debugging the library during development if needed\nendif()\n\n# Export compile commands for better IDE support (top-level only)\nif(PROJECT_IS_TOP_LEVEL)\n    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\nendif()\n\n# ---- Tests ----\n# Default: ON only when building this project directly; OFF when used via FetchContent/add_subdirectory\nif(CMAKE_VERSION VERSION_LESS 3.21)\n    # Fallback heuristic for older CMake\n    set(_is_top_level FALSE)\n    if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)\n        set(_is_top_level TRUE)\n    endif()\nelse()\n    set(_is_top_level ${PROJECT_IS_TOP_LEVEL})\nendif()\n\noption(BUILD_TESTS \"Build the test suite\" ${_is_top_level})\n\nif(BUILD_TESTS AND NOT CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")\n    enable_testing()\n    \n    # Add test executable\n    add_executable(test_bsatn tests/main.cpp tests/module_library_unit_tests.cpp)\n    \n    # Link against the module library\n    target_link_libraries(test_bsatn PRIVATE spacetimedb_cpp_library)\n    \n    # Set C++20 standard for tests\n    target_compile_features(test_bsatn PRIVATE cxx_std_20)\n    \n    # Add test to CTest\n    add_test(NAME bsatn_tests COMMAND test_bsatn)\n    \n    # Add verbose test variant\n    add_test(NAME bsatn_tests_verbose COMMAND test_bsatn -v)\n    \n    # Set test properties\n    set_tests_properties(bsatn_tests PROPERTIES \n        TIMEOUT 30\n        LABELS \"unit\"\n    )\n    \n    set_tests_properties(bsatn_tests_verbose PROPERTIES \n        TIMEOUT 30\n        LABELS \"unit;verbose\"\n    )\nendif()\n"
  },
  {
    "path": "crates/bindings-cpp/DEVELOP.md",
    "content": "# C++ Bindings Development Roadmap: Leveraging Modern C++ Standards\n\nThis document explores how upgrading to C++23 and C++26 could fundamentally transform the SpacetimeDB C++ bindings, moving from runtime registration to compile-time type system integration and eliminating most macro usage.\n\n## Current Architecture: Why So Many Macros?\n\n### The Fundamental Problem: No Compile-Time Reflection in C++20\n\nWithout reflection, the compiler cannot:\n- Enumerate struct fields automatically\n- Determine field types and names\n- Discover which types should be tables\n- Generate serialization code\n- Build type relationships\n\nThis forces us into a multi-layer macro system with runtime registration:\n\n### Layer 1: SPACETIMEDB_STRUCT - Manual Field Enumeration\n\n```cpp\nstruct User {\n    uint32_t id;\n    std::string name;\n    std::string email;\n};\nSPACETIMEDB_STRUCT(User, id, name, email)\n```\n\n**Why we need it:**\n- C++ has no way to iterate over struct fields at compile-time\n- We must manually list every field for serialization\n- The macro generates a `bsatn_traits<User>` specialization that knows how to serialize/deserialize\n\n**What it generates:**\n```cpp\ntemplate<> struct bsatn_traits<User> {\n    static AlgebraicType algebraic_type() {\n        // Builds Product type with fields [id, name, email]\n    }\n    static void serialize(Writer& w, const User& val) {\n        w.write(val.id); w.write(val.name); w.write(val.email);\n    }\n};\n```\n\n**Why it can't be compile-time:** We can't discover the fields without listing them explicitly.\n\n### Layer 2: SPACETIMEDB_TABLE - Runtime Table Registration\n\n```cpp\nSPACETIMEDB_TABLE(User, users, Public)\n```\n\n**Why we need it separately from STRUCT:**\n- A struct might be used as a table, a reducer parameter, or a nested type\n- Not all structs are tables - some are just data types\n- Table registration needs additional metadata (name, access level)\n\n**What it generates:**\n```cpp\nextern \"C\" __attribute__((export_name(\"__preinit__20_register_table_User\")))\nvoid __preinit__20_register_table_User() {\n    Module::RegisterTable<User>(\"users\", true);\n}\n// Plus a tag type for clean syntax: ctx.db[users]\n```\n\n**Why it must be runtime:** The module schema must be built dynamically when the WASM module loads, as we can't generate a complete module description at compile-time.\n\n### Layer 3: FIELD_ Macros - Constraint Registration\n\n```cpp\nFIELD_PrimaryKey(users, id);\nFIELD_Unique(users, email);\nFIELD_Index(users, name);\n```\n\n**Why we need separate macros per constraint:**\n- Each field can have multiple constraints\n- Constraints must be registered AFTER the table (priority ordering)\n- Some constraints need compile-time validation (C++20 concepts)\n- Can't be part of SPACETIMEDB_TABLE because we need the table to exist first\n\n**What they generate:**\n```cpp\n// Compile-time validation\nstatic_assert(FilterableValue<decltype(User::id)>, \"Primary keys must be filterable\");\n\n// Runtime registration\nextern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_users_id\")))\nvoid __preinit__21_field_constraint_users_id() {\n    AddFieldConstraint<User>(\"users\", \"id\", FieldConstraint::PrimaryKey);\n}\n```\n\n**Why the split:** Compile-time validation via concepts, but runtime registration for module schema.\n\n### Layer 4: __preinit__ Priority System - Ordered Initialization\n\n```cpp\n__preinit__01_ - Clear global state\n__preinit__10_ - Field registration  \n__preinit__19_ - Auto-increment integration and scheduled reducers\n__preinit__20_ - Table and lifecycle reducer registration\n__preinit__21_ - Field constraints (must come after tables)\n__preinit__25_ - Row level security filters\n__preinit__30_ - User reducers\n__preinit__40_ - Views\n__preinit__50_ - Procedures\n__preinit__99_ - Type validation and error detection\n```\n\n**Why we need priority ordering:**\n- Tables must exist before constraints can be added\n- Types must be registered before they're referenced\n- Validation must happen after everything else\n- WebAssembly's linear memory model requires deterministic initialization\n\n**Why it's runtime:** WASM modules are initialized linearly, and we need to build the module schema during this initialization phase.\n\n### Layer 5: Namespace Qualification - Compile-Time Metadata\n\n```cpp\nSPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member)\nSPACETIMEDB_NAMESPACE(UserRole, \"Auth\")  // Separate macro for namespace\n```\n\n**Why we need a separate macro:**\n- Namespace is optional metadata, not core to the enum definition\n- Must work with existing enum definitions without modification\n- Needs to associate compile-time string with type\n\n**What it generates:**\n```cpp\nnamespace SpacetimeDB::detail {\n    template<> struct namespace_info<UserRole> {\n        static constexpr const char* value = \"Auth\";\n    };\n}\n```\n\n**Why it's compile-time but still needs a macro:**\n- C++20 has no way to attach metadata to types without explicit specialization\n- Template specialization requires knowing the type name\n- LazyTypeRegistrar uses `if constexpr` to detect namespace at compile-time\n\n### Layer 6: __describe_module__ - Final Runtime Assembly\n\n```cpp\nextern \"C\" const uint8_t* __describe_module__() {\n    // Serialize the complete module built by __preinit__ functions\n    return Module::serialize();\n}\n```\n\n**Why it's needed:**\n- SpacetimeDB server calls this to get the module schema\n- Must return a binary description of all tables, types, reducers\n- Can only be built after all __preinit__ functions have run\n\n**The fundamental limitation:** Without compile-time reflection, we cannot know at compile-time:\n- Which types are tables\n- What fields each struct has\n- What constraints apply to each field\n- The complete type dependency graph\n- What namespace qualifications are applied\n\n### The Cascade Effect\n\nThis creates a cascade of limitations:\n\n1. **No automatic serialization** → Need SPACETIMEDB_STRUCT macro\n2. **No type discovery** → Need explicit SPACETIMEDB_TABLE macro  \n3. **No field introspection** → Need separate FIELD_ macros\n4. **No compile-time module generation** → Need runtime __preinit__ system\n5. **No static validation** → Need runtime validation in __preinit__99\n\nEach macro exists because C++20 lacks the reflection capabilities to do this work automatically. The runtime registration exists because we can't build a complete module description at compile-time without knowing what types exist and their relationships.\n\n## Current Architecture Limitations (Summary)\n\nThe current C++20 SDK relies on:\n- **Runtime registration** via `__preinit__` functions (because no compile-time type discovery)\n- **Heavy macro usage** for type and table registration (because no reflection)\n- **Runtime error detection** in `__preinit__99_validate_types` (because incomplete compile-time info)\n- **Manual serialization** through SPACETIMEDB_STRUCT macros (because no field enumeration)\n- **String-based type identification** requiring explicit registration (because no compile-time type identity)\n- **Outcome<void> error handling** for reducer transactions (workaround for lack of first-class error types)\n\n## C++23 Improvements\n\n### 1. Deducing `this` for Zero-Cost Field Accessors\n\n**Current approach:**\n```cpp\ntemplate<typename TableType, typename FieldType>\nclass TypedFieldAccessor : public TableAccessor<TableType> {\n    FieldType TableType::*member_ptr_;\n    // Complex inheritance hierarchy\n};\n```\n\n**C++23 approach:**\n```cpp\nstruct TableAccessor {\n    template<typename Self>\n    auto filter(this Self&& self, auto&& predicate) {\n        // Deduce table type from self, no inheritance needed\n        return self.table_.filter(std::forward<decltype(predicate)>(predicate));\n    }\n};\n```\n\n**Benefits:**\n- Eliminate accessor class hierarchy\n- Perfect forwarding throughout\n- Reduced template instantiation overhead\n\n### 2. `if consteval` for Hybrid Compile/Runtime Validation\n\n**Current approach:**\n```cpp\n// Static assertions in macros\nstatic_assert(FilterableValue<FieldType>, \"Error message\");\n// Plus runtime validation in __preinit__99\n```\n\n**C++23 approach:**\n```cpp\ntemplate<typename T>\nconstexpr auto validate_constraint() {\n    if consteval {\n        // Compile-time path: full validation\n        static_assert(FilterableValue<T>);\n        return compile_time_type_id<T>();\n    } else {\n        // Runtime fallback for dynamic types\n        return runtime_type_registry::get<T>();\n    }\n}\n```\n\n**Benefits:**\n- Single validation function works at compile-time or runtime\n- Better error messages with source locations\n- Gradual migration path from runtime to compile-time\n\n### 3. `std::expected` for Error Propagation\n\n**Current approach:**\n```cpp\n// Global error flags\nstatic bool g_multiple_primary_key_error = false;\nstatic std::string g_multiple_primary_key_table_name = \"\";\n```\n\n**C++23 approach:**\n```cpp\ntemplate<typename T>\nusing RegistrationResult = std::expected<TypeId, RegistrationError>;\n\nconstexpr RegistrationResult register_table() {\n    if (/* multiple primary keys detected */)\n        return std::unexpected(RegistrationError::MultiplePrimaryKeys);\n    return TypeId{...};\n}\n```\n\n**Benefits:**\n- Type-safe error handling\n- Composable error propagation\n- No global state needed\n\n### 4. `constexpr std::unique_ptr` for Compile-Time Type Trees\n\n**Current approach:**\n```cpp\n// Runtime type tree building\nstd::vector<AlgebraicType> types;\ntypes.push_back(...);\n```\n\n**C++23 approach:**\n```cpp\nconstexpr auto build_type_tree() {\n    std::unique_ptr<TypeNode> root = std::make_unique<TypeNode>();\n    // Build entire type hierarchy at compile time\n    return root;\n}\n\nconstexpr auto type_tree = build_type_tree();\n```\n\n**Benefits:**\n- Complete type system known at compile-time\n- Zero runtime allocation\n- Enables compile-time validation of entire module\n\n### 5. `std::ranges` for Cleaner Type Processing\n\n**Current approach:**\n```cpp\n// Manual iteration and filtering\nstd::vector<AlgebraicType> types;\nfor (const auto& type : all_types) {\n    if (isPrimitive(type)) continue;\n    if (isOptionType(type)) continue;\n    types.push_back(processType(type));\n}\n```\n\n**C++23 approach:**\n```cpp\n// Declarative pipeline with ranges\nauto process_types() {\n    return all_types \n        | std::views::filter(not_primitive)\n        | std::views::filter(not_option)\n        | std::views::transform(processType)\n        | std::ranges::to<std::vector>();\n}\n\n// Better: lazy evaluation for type checking\nauto valid_types = registered_types\n    | std::views::filter([](auto& t) { return validate_type(t).has_value(); });\n```\n\n**Benefits:**\n- More declarative and readable type processing\n- Lazy evaluation reduces memory usage\n- Composable validation pipelines\n- Better separation of filtering logic from processing\n\n### 6. `std::mdspan` for Table Data Access\n\n**Current approach:**\n```cpp\n// Custom iterators and accessors\nfor (const auto& row : table) { }\n```\n\n**C++23 approach:**\n```cpp\ntemplate<typename T>\nusing TableView = std::mdspan<T, std::extents<size_t, std::dynamic_extent, field_count<T>()>>;\n\n// Direct columnar access\nauto names = table_view[std::full_extent, name_column];\n```\n\n**Benefits:**\n- Standard library support for multi-dimensional access\n- Optimized for columnar operations\n- Compatible with parallel algorithms\n\n## C++26 Transformative Features\n\n> **Note**: The C++26 examples below are based on current proposals (particularly P2996 for reflection, which uses the `^` reification operator). The final C++26 standard may differ significantly as proposals evolve through the standardization process. These examples are illustrative of the *capabilities* that reflection would enable, not necessarily the exact syntax that will be standardized.\n\n### 1. Static Reflection (P2996) - Complete Macro Elimination\n\n**Current approach:**\n```cpp\nSPACETIMEDB_STRUCT(User, id, name, email)\nSPACETIMEDB_TABLE(User, users, Public)\nFIELD_PrimaryKey(users, id);\nSPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member)\nSPACETIMEDB_NAMESPACE(UserRole, \"Auth\")  // Separate macro for namespace\n```\n\n**Illustrative C++26 approach** (exact syntax TBD in standardization):\n```cpp\n// Natural C++ attributes replace macros\nstruct [[spacetimedb::table(\"users\", public)]] User {\n    [[spacetimedb::primary_key]] uint32_t id;\n    [[spacetimedb::unique]] std::string email;\n    std::string name;\n};\n\nenum class [[spacetimedb::namespace(\"Auth\")]] UserRole {\n    Admin, Moderator, Member\n};\n\n// Automatic registration via reflection (pseudocode - actual API TBD)\ntemplate<typename T> requires has_spacetimedb_table_attr<T>\nconsteval void register_table() {\n    for (constexpr auto member : reflect_members_of<T>()) {\n        if constexpr (has_attribute<member, spacetimedb::primary_key>) {\n            register_primary_key<T>(member.name(), member.type());\n        }\n    }\n}\n\n// Automatic namespace detection via reflection\ntemplate<typename T>\nconsteval std::string get_qualified_name() {\n    if constexpr (has_attribute<T, spacetimedb::namespace>) {\n        return get_namespace_attribute<T>() + \".\" + get_type_name<T>();\n    }\n    return get_type_name<T>();\n}\n```\n\n**Benefits:**\n- **Zero macros needed**\n- Natural C++ syntax with attributes\n- Complete type information at compile-time\n- Automatic serialization without SPACETIMEDB_STRUCT\n\n### 2. Contracts for Constraint Validation\n\n**Current approach:**\n```cpp\nstatic_assert(FilterableValue<FieldType>, \"Field cannot have Index constraint\");\n```\n\n**C++26 approach:**\n```cpp\ntemplate<typename T>\nvoid add_index_constraint(T TableType::*field)\n    [[pre: FilterableValue<T>]]\n    [[pre: !has_existing_index(field)]]\n{\n    // Contract violations become compile-time or runtime errors\n    // depending on evaluation context\n}\n```\n\n**Benefits:**\n- Declarative constraint specification\n- Automatic error messages from contract violations\n- Works for both compile-time and runtime validation\n\n### 3. Pattern Matching for Type Dispatch\n\n**Current approach:**\n```cpp\nswitch(type.tag()) {\n    case AlgebraicTypeTag::Product: ...\n    case AlgebraicTypeTag::Sum: ...\n    // Manual casting and handling\n}\n```\n\n**C++26 approach:**\n```cpp\ninspect(type) {\n    <Product> p => serialize_product(p),\n    <Sum> s => serialize_sum(s),\n    <Array> [auto elem_type] => serialize_array(elem_type),\n    <Option> opt => serialize_option(opt),\n    _ => throw InvalidType{}\n}\n```\n\n**Benefits:**\n- Type-safe pattern matching\n- Exhaustiveness checking\n- Cleaner type handling code\n\n### 4. Compile-Time Type Registration via Reflection\n\n**Current approach:**\n```cpp\nextern \"C\" __attribute__((export_name(\"__preinit__20_register_table_User\")))\nvoid __preinit__20_register_table_User() {\n    Module::RegisterTable<User>(\"users\", true);\n}\n```\n\n**C++26 approach:**\n```cpp\n// Automatic discovery and registration at compile time\ntemplate<typename... Tables>\nconsteval auto generate_module_descriptor() {\n    ModuleDescriptor desc;\n    (reflect_and_register<Tables>(desc), ...);\n    return desc;\n}\n\n// Single compile-time constant contains entire module\nconstexpr auto module = generate_module_descriptor<\n    discover_tables_via_reflection()...  // Find all types with [[spacetimedb::table]]\n>();\n\n// Single runtime export\nextern \"C\" const uint8_t* __describe_module__() {\n    return module.serialize();  // Already computed at compile-time\n}\n```\n\n**Benefits:**\n- **No __preinit__ functions needed**\n- Entire module structure known at compile-time\n- Single binary blob computed during compilation\n- Zero runtime registration overhead\n\n### 5. Improved `constexpr` Allocation\n\n**Current approach:**\n```cpp\n// Runtime type vector building\nstd::vector<AlgebraicType> types;\n```\n\n**C++26 approach:**\n```cpp\nconstexpr auto build_typespace() {\n    std::vector<AlgebraicType> types;\n    // Fully constexpr vector operations\n    for (auto type : reflect_all_types()) {\n        types.push_back(analyze_type(type));\n    }\n    return types;\n}\n\nconstexpr auto typespace = build_typespace();\n```\n\n**Benefits:**\n- Complete typespace computed at compile-time\n- No runtime allocation or registration\n- Enables full compile-time validation\n\n### 5b. First-Class Error Type Support\n\n**Current approach** (workaround using Outcome<void>):\n```cpp\nSPACETIMEDB_REDUCER(process, ReducerContext ctx, uint32_t id) {\n    if (id == 0) {\n        return Err(\"Invalid ID\");  // Error represented as string\n    }\n    ctx.db[users].insert({id});\n    return Ok();  // No value, just success\n}\n```\n\n**C++26 potential approach** (with better Result types):\n```cpp\n// Could use std::expected with richer error information\ntemplate<typename T, typename E = std::string>\nusing Result = std::expected<T, E>;\n\nSPACETIMEDB_REDUCER(process, ReducerContext ctx, uint32_t id) {\n    if (id == 0) {\n        return std::unexpected(ProcessError::InvalidId);  // Type-safe errors\n    }\n    ctx.db[users].insert({id});\n    return {};  // Clear success value\n}\n```\n\n**Benefits:**\n- Richer error types beyond string messages\n- Better IDE support for error handling\n- Cleaner syntax for success/error distinction\n- Compile-time error case exhaustiveness checking\n\n### 6. Reflection-Based Serialization\n\n**Current approach:**\n```cpp\n// Manual field listing in SPACETIMEDB_STRUCT macro\nSPACETIMEDB_STRUCT(MyType, field1, field2, field3)\n```\n\n**C++26 approach:**\n```cpp\ntemplate<typename T>\nconstexpr void serialize(Writer& w, const T& obj) {\n    [:expand(^T::members()):] >> [&]<auto member> {\n        w.write(obj.[:member:]);\n    };\n}\n\n// Automatic serialization for any struct, no macros needed\n```\n\n**Benefits:**\n- Automatic serialization for all types\n- No manual field enumeration\n- Works with any struct/class\n\n## Migration Strategy\n\n### Phase 1: C++23 Adoption\n1. Replace class hierarchies with deducing `this`\n2. Introduce `std::expected` for error handling\n3. Use `if consteval` for hybrid validation\n4. Implement `constexpr` type tree building\n5. Gradually reduce macro usage\n\n### Phase 2: C++26 Revolution\n1. Replace all macros with reflection\n2. Eliminate __preinit__ system entirely\n3. Move to compile-time module generation\n4. Implement pattern matching for type dispatch\n5. Use contracts for all constraint validation\n\n## Expected Outcomes\n\n### With C++23:\n- **30-50% reduction** in macro usage\n- **Faster compilation** through reduced template instantiation\n- **Better error messages** with `std::expected`\n- **Cleaner API** with deducing `this`\n\n### With C++26:\n- **100% macro elimination**\n- **Zero runtime registration overhead**\n- **Compile-time module validation**\n- **Natural C++ syntax** without SDK-specific constructs\n- **Full IDE support** (no macro magic hiding semantics)\n\n## Performance Implications\n\n> **Disclaimer**: The following performance projections are educated estimates based on similar language features and SDK patterns. Actual performance gains will depend on:\n> - Compiler optimization capabilities\n> - Final C++26 feature specifications\n> - WASM code generation characteristics\n> - Specific module structure (table count, field count, etc.)\n\n### Compile-Time Performance\n- **C++23**: Slightly increased due to more `constexpr` evaluation (estimated +5-15%)\n- **C++26**: Significantly increased initially due to reflection-based analysis, but:\n  - Module structure computed once during build and cached\n  - No runtime registration code to compile\n  - Potential for better code generation optimization\n  - Long-term: compiler improvements may negate initial increases\n\n### Runtime Performance\n- **C++23**: ~5-15% improvement from reduced indirection and better inlining\n- **C++26**: ~20-40% improvement from eliminating registration entirely\n  - Actual gains depend on whether compiler can optimize registration code away\n  - May be limited by WASM constraints\n\n### WASM Binary Size\n- **C++23**: ~5-10% reduction (less template instantiation)\n- **C++26**: ~15-25% reduction (no registration code)\n  - Note: Reflection metadata may add some size overhead if not stripped\n  - Net reduction depends on compile-time constant optimization\n\n## Conclusion\n\nC++26's static reflection will enable a significant paradigm shift from runtime registration to compile-time module generation. The SpacetimeDB C++ bindings could achieve **zero-overhead abstractions** with no macros and no runtime registration - just pure, standard C++.\n\nThe journey through C++23 provides valuable incremental improvements:\n- Cleaner APIs with `deducing this`\n- Better error handling with `std::expected`  \n- Hybrid validation with `if consteval`\n- More efficient code generation\n\nC++26's reflection capabilities will allow us to achieve compile-time type safety and module generation with natural C++ syntax, making the C++ bindings substantially more ergonomic and performant than currently possible.\n\n**Important caveats:**\n- C++26 reflection is still in proposal stage; final syntax and capabilities may differ\n- Compiler support for these features will take time after standardization\n- WASM toolchain support (Emscripten) will need updates\n- Migration from C++20 code will require careful planning\n- Not all performance gains may materialize depending on compiler optimizations and WASM constraints"
  },
  {
    "path": "crates/bindings-cpp/QUICKSTART.md",
    "content": "# SpacetimeDB C++ Module Quickstart\n\nThis guide will walk you through creating your first SpacetimeDB module in C++. We'll build a simple chat server to demonstrate the core concepts.\n\n## What is a SpacetimeDB Module?\n\nA SpacetimeDB module is C++ code that gets compiled to WebAssembly and runs inside the database. Instead of the traditional architecture (database → app server → clients), SpacetimeDB lets you write your entire backend logic that runs **inside** the database itself, giving you microsecond latency and automatic real-time sync to clients.\n\nModules consist of four main components:\n\n- **Tables**: Database tables defined as C++ structs\n- **Reducers**: Functions that modify data and can be called by clients\n- **Views**: Read-only query functions that return data (std::vector<T> or std::optional<T>) to clients\n- **Procedures**: Pure functions that return values and can optionally access the database via transactions \n\n## Prerequisites\n\nBefore we begin, make sure you have:\n\n- [SpacetimeDB CLI](https://spacetimedb.com/install) installed\n- [Emscripten SDK (emsdk)](https://emscripten.org/docs/getting_started/downloads.html) for WebAssembly compilation\n- CMake 3.16+ \n- A C++20 compatible compiler\n\n## Creating Your First Module\n\n### Step 1: Initialize the Project\n\nCreate a new C++ module using the SpacetimeDB CLI:\n\n```bash\nspacetime init --lang cpp my-chat-module\ncd my-chat-module\n```\n\nThis creates a project with the following structure:\n```\nmy-chat-module/spacetimedb/\n├── CMakeLists.txt\n├── src/\n    └── lib.cpp\n└── .gitignore\n```\n\n### Step 2: Define Your Data Structures\n\nOpen `lib.cpp` and replace the generated code with our chat server implementation:\n\n```cpp\n#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Define a User table to store connected users\nstruct User {\n    Identity identity;      // SpacetimeDB's built-in user identity type\n    std::optional<std::string> name;  // User's display name (optional)\n    bool online;           // Whether the user is currently connected\n};\n\n// Register the struct for BSATN serialization\nSPACETIMEDB_STRUCT(User, identity, name, online)\n\n// Register as a public table with identity as primary key\nSPACETIMEDB_TABLE(User, user, Public)\nFIELD_PrimaryKey(user, identity);\n\n// Define a Message table to store chat messages\nstruct Message {\n    Identity sender;       // Who sent the message\n    Timestamp sent;        // When the message was sent\n    std::string text;      // Message content\n};\n\nSPACETIMEDB_STRUCT(Message, sender, sent, text)\nSPACETIMEDB_TABLE(Message, message, Public)\n```\n\n### Step 3: Add Helper Functions and Reducers\n\nFirst, add validation helper functions:\n\n```cpp\n// Validate that a name is not empty, return an Outcome which houses a error as std::string\nOutcome<std::string> validate_name(const std::string& name) {\n    if (name.empty()) {\n        return Err<std::string>(\"Names must not be empty\");\n    }\n    return Ok(name);\n}\n\n// Validate that a message is not empty, return an Outcome which houses a error as std::string\nOutcome<std::string> validate_message(const std::string& text) {\n    if (text.empty()) {\n        return Err<std::string>(\"Messages must not be empty\");\n    }\n    return Ok(text);\n}\n```\n\nNow add the reducers (functions that clients can call to modify the database):\n\n```cpp\n// Called when a user sets their name\nSPACETIMEDB_REDUCER(set_name, ReducerContext ctx, std::string name) {\n    auto validated = validate_name(name);\n    if (validated.is_err()) {\n        return Err(validated.error());\n    }\n    \n    // Find and update the user by identity (primary key)\n    auto user_row = ctx.db[user_identity].find(ctx.sender());\n    if (user_row.has_value()) {\n        auto user = user_row.value();\n        user.name = validated.value();\n        ctx.db[user_identity].update(user);\n        return Ok();\n    }\n    \n    return Err(\"Cannot set name for unknown user\");\n}\n\n// Called when a user sends a message\nSPACETIMEDB_REDUCER(send_message, ReducerContext ctx, std::string text) {\n    auto validated = validate_message(text);\n    if (validated.is_err()) {\n        return Err(validated.error());\n    }\n    \n    Message msg{ctx.sender(), ctx.timestamp, validated.value()};\n    ctx.db[message].insert(msg);\n    return Ok();\n}\n```\n\n### Step 4: Add Lifecycle Reducers\n\nLifecycle reducers are special functions called automatically by SpacetimeDB:\n\n```cpp\n// Called when a client connects\nSPACETIMEDB_CLIENT_CONNECTED(client_connected, ReducerContext ctx) {\n    auto user_row = ctx.db[user_identity].find(ctx.sender());\n    if (user_row.has_value()) {\n        auto user = user_row.value();\n        user.online = true;\n        ctx.db[user_identity].update(user);\n    } else {\n        User new_user{ctx.sender(), std::nullopt, true};\n        ctx.db[user].insert(new_user);\n    }\n    return Ok();\n}\n\n// Called when a client disconnects  \nSPACETIMEDB_CLIENT_DISCONNECTED(client_disconnected, ReducerContext ctx) {\n    auto user_row = ctx.db[user_identity].find(ctx.sender());\n    if (user_row.has_value()) {\n        auto user = user_row.value();\n        user.online = false;\n        ctx.db[user_identity].update(user);\n    } else {\n        LOG_WARN(\"Disconnect event for unknown user\");\n    }\n    return Ok();\n}\n```\n\n### Step 5: Build Your Module\n\nBuild the module using the provided CMake configuration:\n\n```bash\nspacetime build -p ./spacetimedb\n```\n\nThis compiles your C++ code to WebAssembly, producing `build/lib.wasm`.\n\n### Step 6: Publish to SpacetimeDB\n\nStart your local SpacetimeDB instance:\n\n```bash\nspacetime start\n```\n\nPublish your module:\n\n```bash\nspacetime publish . my-chat-db\n```\n\n### Step 7: Test Your Module\n\nYou can test your reducers using the CLI:\n\n```bash\n# Set a user's name\nspacetime call my-chat-db set_name \"Alice\"\n\n# Send a message\nspacetime call my-chat-db send_message \"Hello, world!\"\n\n# View all users\nspacetime sql my-chat-db \"SELECT * FROM user\"\n\n# View all messages\nspacetime sql my-chat-db \"SELECT * FROM message\"\n```\n\n## Key Concepts Explained\n\n### Tables vs. Structs\n\n- **Structs** are just data types - they need `SPACETIMEDB_STRUCT` for serialization\n- **Tables** are database tables created with `SPACETIMEDB_TABLE` and store data persistently\n- The same struct can be used for multiple tables or just as a data type\n\n### Database Access Pattern\n\nSpacetimeDB C++ uses a unique accessor pattern:\n- `ctx.db[tableName]` - Access table for iteration and basic operations, eg. ctx.db[user]\n- `ctx.db[tableName_fieldName]` - Access indexed fields for optimized operations, eg. ctx.db[user_id]\n\n```cpp\n// Table access\nctx.db[user].insert(new_user);\n\n// Field accessor (for indexed fields, e.g., user_identity = table 'user' + field 'identity')\nctx.db[user_identity].delete_by_key(identity);\n```\n\n### Constraints and Indexes\n\nConstraints are applied **after** table registration using `FIELD_` macros:\n\n```cpp\nSPACETIMEDB_TABLE(User, user, Public)\nFIELD_PrimaryKey(user, identity);       // Primary key\nFIELD_Unique(user, email);              // Unique constraint\nFIELD_Index(user, age);                 // Index for fast queries\n```\n\n### Public vs. Private Tables\n\n- **Public tables**: Automatically synced to subscribed clients\n- **Private tables**: Only accessible by reducers, not synced to clients\n\n## Next Steps\n\nNow that you have a basic chat server:\n\n1. **Add more features**: User roles, message editing, channels\n2. **Add constraints**: Unique usernames, message length limits\n3. **Explore indexing**: For fast queries on large datasets\n4. **Try scheduled reducers**: For periodic cleanup or notifications\n5. **Generate client code**: Use `spacetime generate` to create TypeScript, C#, or Rust clients\n\n## Advanced Example: Adding Indexes\n\nYou can add an index to make querying messages by sender more efficient:\n\n```cpp\nSPACETIMEDB_TABLE(Message, message, Public)\nFIELD_Index(message, sender);  // Add index on sender for faster queries\n\n// Now you can efficiently query by sender using the field accessor:\nSPACETIMEDB_REDUCER(get_user_messages, ReducerContext ctx, Identity user_identity) {\n    for (const auto& msg : ctx.db[message_sender].filter(user_identity)) {\n        LOG_INFO(\"Message: \" + msg.text);\n    }\n    return Ok();\n}\n```\n\n## Troubleshooting\n\n**Build errors**: Ensure you have the latest Emscripten SDK and are using `emcmake cmake`\n\n**Module not found**: Check that SpacetimeDB is running\n\n**Type errors**: Remember that C++ types need exact matches - use `uint32_t`, not `int`\n\n**Constraint violations**: Constraints are enforced by the database - duplicate primary keys will cause reducers to fail\n\n## Example Projects\n\nFor more complex examples, see:\n- [`modules/sdk-test-cpp/src/lib.cpp`](../../modules/sdk-test-cpp/src/lib.cpp) - Comprehensive type and operation testing\n- [`modules/module-test-cpp/src/lib.cpp`](../../modules/module-test-cpp/src/lib.cpp) - Advanced indexing and enum examples\n\n---\n\nReady to learn more? Check out the [C++ Reference Documentation](REFERENCE.md) for detailed API information.\n"
  },
  {
    "path": "crates/bindings-cpp/README.md",
    "content": "# SpacetimeDB C++ Module Library\n\nThe SpacetimeDB C++ Module Library provides a modern C++20 API for building SpacetimeDB modules that run inside the database as WebAssembly.\n\n## Current State\n\nThis library provides a production-ready C++ bindings for SpacetimeDB with complete type system support:\n\n### ✅ Features\n- Module compilation and publishing to SpacetimeDB\n- All lifecycle reducers (init, client_connected, client_disconnected)\n- User-defined reducers with unlimited parameters\n- Table registration with constraints (PrimaryKey, Unique, AutoInc)\n- Insert, update and delete operations\n- All primitive types (u8-u256, i8-i256, bool, f32, f64, string)\n- All special types (Identity, ConnectionId, Timestamp, TimeDuration, Uuid, Result<>)\n- Vector types for all primitives and special types\n- Optional types (std::optional<T>)\n- Custom struct serialization via BSATN\n- Complex enum support with proper variant names\n- Enhanced logging system with file/line info\n\n### 🏗️ Architecture\n- **Hybrid Compile-Time/Runtime System**: C++20 concepts for compile-time validation with __preinit__ runtime registration\n- **V9 Type Registration System**: Unified type registration with comprehensive error detection and circular reference prevention\n- **Nominal Type System**: Types identified by their declared names with explicit registration via SPACETIMEDB_STRUCT macros\n- **Multi-Layer Validation**: Static assertions, runtime constraint checking, and error module replacement strategy\n\nSee [ARCHITECTURE.md](ARCHITECTURE.md) for detailed technical documentation.\n\n### ✅ Advanced Features Available\n- **Btree indexes**: Full support with `FIELD_Index` macros and optimized queries\n- **Range queries**: Complete range query system with `range_from()`, `range_to()`, `range_inclusive()`, etc.\n- **Client visibility filters**: Row-level security with `SPACETIMEDB_CLIENT_VISIBILITY_FILTER` macro\n- **Scheduled reducers**: `SPACETIMEDB_SCHEDULE` macro for time-based execution\n- **Procedures**: Pure functions with return values using `SPACETIMEDB_PROCEDURE` macro\n- **Views**: Read-only query functions with `SPACETIMEDB_VIEW` macro\n- **Field accessor patterns**: Efficient indexed operations with `ctx.db[table_field]`\n\nSee the working examples in `modules/*-cpp/src/lib.cpp` for comprehensive feature usage.\n\n## Features\n\n- **Modern C++20 API**: Uses concepts, structured bindings, and other C++20 features\n- **BSATN Serialization**: Binary Serialization And Type Notation for efficient data transfer\n- **Automatic Field Registration**: Tables register their fields using SPACETIMEDB_STRUCT macro\n- **Unified Reducer System**: Single macro for all reducer types with automatic lifecycle detection\n- **Type-Safe Database Access**: Template-based table accessors with compile-time type checking\n- **Memory Safety**: WASI shims for safe memory operations in WebAssembly environment\n- **Enhanced Logging**: Multiple log levels with file/line information\n- **Namespace Support**: Clean namespace qualification for enums with just 2 lines of code\n\n## Prerequisites\n\n- Emscripten SDK (emsdk)\n- CMake 3.16+\n- C++20 compatible compiler\n\n## Quick Start\n\n### Option 1: Using spacetime init (Recommended)\n\n```bash\n# Create a new C++ project\nspacetime init --lang cpp my-project\ncd my-project\n\n# Build and publish\nspacetime build -p ./spacetimedb\nspacetime publish -p ./spacetimedb my-database\n```\n\n### Option 2: Manual Setup\n\nFor existing projects, add the following to your C++ module:\n\n```cpp\n#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Define a table structure\nstruct User {\n    Identity identity;\n    std::string name;\n    std::string email;\n};\n\n// Register BSATN serialization\nSPACETIMEDB_STRUCT(User, identity, name, email)\n\n// Register as a table\nSPACETIMEDB_TABLE(User, users, Public)\n\n// Add constraints using FIELD_ macros\nFIELD_PrimaryKey(users, identity);\nFIELD_Unique(users, email);\n\n// Define an enum with namespace qualification\nSPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member)\nSPACETIMEDB_NAMESPACE(UserRole, \"Auth\")  // Will be \"Auth.UserRole\" in client code\n\n// User-defined reducer\nSPACETIMEDB_REDUCER(add_user, ReducerContext ctx, std::string name, std::string email) {\n    User user{ctx.sender(), name, email}; // id will be auto-generated\n    ctx.db[users].insert(user);\n    LOG_INFO(\"Added user: \" + name);\n    return Ok();\n}\n\n// Delete user by id (using primary key)\nSPACETIMEDB_REDUCER(delete_user, ReducerContext ctx) {\n    ctx.db[users_identity].delete_by_key(ctx.sender());\n    return Ok();\n}\n\n// Lifecycle reducers (optional)\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    LOG_INFO(\"Module initialized\");\n    return Ok();\n}\n\nSPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {\n    LOG_INFO(\"Client connected: \" + ctx.sender().to_hex_string());\n    return Ok();\n}\n\nSPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {\n    LOG_INFO(\"Client disconnected: \" + ctx.sender().to_hex_string());\n    return Ok();\n}\n\n// Define a view for querying data (finds the calling user)\nSPACETIMEDB_VIEW(std::optional<User>, find_my_user, Public, ViewContext ctx) {\n    // Use indexed field to find user by their identity\n    return ctx.db[users_identity].find(ctx.sender());\n}\n\n// Define a procedure (pure function with return value)\nSPACETIMEDB_PROCEDURE(uint32_t, add_numbers, ProcedureContext ctx, uint32_t a, uint32_t b) {\n    return a + b;\n}\n```\n\n## Building Modules\n\n### Build Steps\n\n```bash\n# Navigate to your module directory\ncd modules/your-module\n\n# Build the project\nspacetime build -p ./spacetimedb\n\n# Publish to SpacetimeDB\nspacetime publish --bin-path ./spacetimedb/build/lib.wasm your-database-name\n# Or use the directory (auto-detects build/lib.wasm)\nspacetime publish ./spacetimedb your-database-name\n```\n\n#### Custom Module Source\n\nTo build a different source file:\n\n```bash\n# Build a specific test module\nemcmake cmake -B build -DMODULE_SOURCE=src/test_module.cpp -DOUTPUT_NAME=test_module .\ncmake --build build\n# This creates build/test_module.wasm\n```\n\n## API Reference\n\n### Macros\n\n#### Table Definition\n- `SPACETIMEDB_TABLE(Type, table_name, Public/Private)` - Register a table\n- `SPACETIMEDB_STRUCT(Type, field1, field2, ...)` - Register type for BSATN serialization\n\n#### Enum Definition\n- `SPACETIMEDB_ENUM(EnumName, Value1, Value2, ...)` - Define a simple enum\n- `SPACETIMEDB_ENUM(EnumName, (Variant1, Type1), (Variant2, Type2), ...)` - Define an enum with payloads\n- `SPACETIMEDB_NAMESPACE(EnumName, \"Namespace\")` - Add namespace qualification to an enum\n\n#### Reducers\n- `SPACETIMEDB_REDUCER(name, ReducerContext ctx, ...)` - User-defined reducer\n  - Returns `ReducerResult` (alias for `Outcome<void>`)\n  - Use `return Ok();` for success or `return Err(\"message\");` for errors\n  - Failed reducers (Err) trigger transaction rollback\n- `SPACETIMEDB_INIT(name, ReducerContext ctx)` - Module initialization reducer (optional)\n- `SPACETIMEDB_CLIENT_CONNECTED(name, ReducerContext ctx)` - Client connection reducer (optional)\n- `SPACETIMEDB_CLIENT_DISCONNECTED(name, ReducerContext ctx)` - Client disconnection reducer (optional)\n\n#### Views\n- `SPACETIMEDB_VIEW(return_type, name, Public/Private, ViewContext ctx)` - Read-only query function\n- `SPACETIMEDB_VIEW(return_type, name, Public/Private, AnonymousViewContext ctx)` - Anonymous view (no sender identity)\n- Note: Views currently only support the context parameter (no additional parameters yet)\n\n#### Procedures\n- `SPACETIMEDB_PROCEDURE(return_type, name, ProcedureContext ctx, ...)` - Pure function that returns a value\n  - Returns the type directly (not wrapped in Outcome)\n  - Can return any SpacetimeType (primitives, structs, enums, Unit, etc.)\n  - Database access requires explicit transactions (use `ctx.WithTx()` or `ctx.TryWithTx()`)\n  - Always public (no access control)\n\n#### Field Constraints (applied after table registration)\n- `FIELD_PrimaryKey(table_name, field)` - Primary key constraint\n- `FIELD_PrimaryKeyAutoInc(table_name, field)` - Auto-incrementing primary key\n- `FIELD_Unique(table_name, field)` - Unique constraint\n- `FIELD_UniqueAutoInc(table_name, field)` - Auto-incrementing unique field\n- `FIELD_Index(table_name, field)` - Index for faster queries\n- `FIELD_IndexAutoInc(table_name, field)` - Auto-incrementing indexed field\n- `FIELD_AutoInc(table_name, field)` - Auto-increment without other constraints\n\n### Logging\n\n```cpp\nLOG_DEBUG(\"Debug message\");\nLOG_INFO(\"Info message\");\nLOG_WARN(\"Warning message\");\nLOG_ERROR(\"Error message\");\nLOG_PANIC(\"Fatal error message\");\n\n// With timing\n{\n    LogStopwatch timer(\"Operation name\");\n    // ... code to time ...\n} // Automatically logs duration\n```\n\n## Architecture\n\nThe library uses a sophisticated hybrid compile-time/runtime architecture:\n\n- **Compile-Time Validation** (`table_with_constraints.h`): C++20 concepts and static assertions for constraint validation\n- **Module Type Registration System** (`internal/module_type_registration.h`): Unified type registration with error detection and circular reference prevention\n- **Priority-Ordered Initialization** (`internal/Module.cpp`): __preinit__ functions with numbered priorities ensure correct registration order\n- **Error Detection System** (`internal/Module.cpp`): Multi-layer validation with error module replacement for clear diagnostics\n- **BSATN Serialization** (`bsatn/`): Binary serialization system with algebraic type support for all data types\n- **Database Interface** (`database.h`, `table_with_constraints.h`): Type-safe table access with optimized field accessors\n- **Reducer System** (`reducer_macros.h`): Unified macro system for all reducer types with parameter type capture\n- **Logging** (`logger.h`): Comprehensive logging with source location tracking\n\nFor detailed technical documentation, see [ARCHITECTURE.md](ARCHITECTURE.md).\n\n**Note on Architecture Documentation**: ARCHITECTURE.md contains references to some legacy implementation details. The current implementation is streamlined and production-ready.\n\n## Limitations\n\n1. **Type System**\n   - Very large type combinations may exceed WASM memory limits\n   - Complex recursive type references require careful ordering\n\n2. **Database Operations**\n   - Index-based operations use field accessors: `ctx.db[table_field].delete_by_key(value)`\n   - Table constraints are declared and enforced by server\n   - Supports insert, delete, and update operations through field accessors\n\n3. **Advanced Features**  \n   - **Btree indexes**: `FIELD_Index` creates btree indexes for efficient range queries\n   - **Range queries**: Full support for `range_from()`, `range_to()`, `range_inclusive()`, etc.\n   - **Client visibility filters**: Row-level security via `SPACETIMEDB_CLIENT_VISIBILITY_FILTER`\n   - **Limited migrations**: Only adding tables supported automatically\n   - **SQL execution**: Available via CLI (`spacetime sql`) but not within modules\n\n## Examples\n\nSee the `modules/*-cpp/src/` directory for example modules:\n- `lib.cpp` - Comprehensive working module with all primitive types, tables, and reducers\n- Full equivalence with Rust and C# SDK test modules\n- Examples of all constraint types and database operations\n\n## Contributing\n\nThis library is part of the SpacetimeDB project. Please see the main repository for contribution guidelines.\n\n"
  },
  {
    "path": "crates/bindings-cpp/REFERENCE.md",
    "content": "# SpacetimeDB C++ Module Reference\n\nComplete API reference for building SpacetimeDB modules in C++.\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Setup](#setup)\n- [Core Concepts](#core-concepts)\n- [Tables](#tables)\n- [Reducers](#reducers)\n- [Database Operations](#database-operations)\n- [Types and Serialization](#types-and-serialization)\n- [Constraints and Indexing](#constraints-and-indexing)\n- [Special Types](#special-types)\n- [Logging](#logging)\n- [Build System](#build-system)\n- [Examples](#examples)\n\n## Overview\n\nSpacetimeDB allows writing server-side applications in C++ that compile to WebAssembly and run inside the database. This eliminates the traditional application server layer, providing microsecond latency and automatic real-time synchronization to clients.\n\n### Module Structure\n\nA SpacetimeDB C++ module consists of:\n\n```cpp\n#include <spacetimedb.h>\nusing namespace SpacetimeDB;\n\n// 1. Data structures (structs/enums)\nstruct MyData { \n    uint64_t id;\n    /* other fields */ \n};\nSPACETIMEDB_STRUCT(MyData, id /*, other field names */);\n\n// 2. Tables (persistent storage)\nSPACETIMEDB_TABLE(MyData, my_table, Public);\n\n// 3. Constraints (applied after table registration)\nFIELD_PrimaryKey(my_table, id);\n\n// 4. Reducers (functions clients can call)\nSPACETIMEDB_REDUCER(my_function, ReducerContext ctx /*, parameters */) {\n    // Your logic here\n    return Ok();\n}\n```\n\n## Setup\n\n### Project Initialization\n\n```bash\n# Create new project\nspacetime init --lang cpp my-module\ncd my-module\n\n# Build and publish\nspacetime build -p ./spacetimedb\nspacetime publish -p ./spacetimedb my-database\n```\n\n### Manual Setup\n\nFor existing projects, ensure your `CMakeLists.txt` includes, use `spacetime init` to generate one for you:\n\n```cmake\ncmake_minimum_required(VERSION 3.16)\nproject(my-module)\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# Set path to SpacetimeDB C++ library\nset(SPACETIMEDB_CPP_LIBRARY_PATH \"path/to/crates/bindings-cpp\")\n# Or FetchContent from SpacetimeDB\n\nadd_executable(lib src/lib.cpp)\ntarget_include_directories(lib PRIVATE ${SPACETIMEDB_CPP_LIBRARY_PATH}/include)\n\nadd_subdirectory(${SPACETIMEDB_CPP_LIBRARY_PATH} spacetimedb_cpp_library)\ntarget_link_libraries(lib PRIVATE spacetimedb_cpp_library)\n\n# Emscripten settings for WASM\nif(CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")\n    set_target_properties(lib PROPERTIES\n        SUFFIX \".wasm\"\n        LINK_FLAGS \"-s STANDALONE_WASM=1 ...\"\n    )\nendif()\n```\n\n## Core Concepts\n\n### Tables\n\nTables are persistent data storage defined as C++ structs:\n\n```cpp\nstruct User {\n    uint32_t id;\n    std::string name;\n    bool active;\n};\n```\n\n### Reducers\n\nReducers are functions that modify data and can be called by clients:\n\n```cpp\nSPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) {\n    User user{0, name, true};\n    ctx.db[user].insert(user);\n    return Ok();\n}\n```\n\n### Transactions\n\nAll reducer calls run in transactions. If a reducer fails, all changes are rolled back.\n\n## Tables\n\n### Defining Tables\n\nTables require two steps: struct registration and table registration.\n\n```cpp\n// Step 1: Register struct for serialization\nstruct Product {\n    uint32_t id;\n    std::string name;\n    double price;\n    std::optional<std::string> description;\n};\nSPACETIMEDB_STRUCT(Product, id, name, price, description);\n\n// Step 2: Register as table\nSPACETIMEDB_TABLE(Product, products, Public);\n```\n\n### Table Visibility\n\n- **Public**: `SPACETIMEDB_TABLE(Type, name, Public)` - Synced to subscribed clients\n- **Private**: `SPACETIMEDB_TABLE(Type, name, Private)` - Only accessible by reducers\n\n### Multiple Tables per Type\n\nThe same struct can be used for multiple tables:\n\n```cpp\nSPACETIMEDB_STRUCT(LogEntry, timestamp, message, level);\nSPACETIMEDB_TABLE(LogEntry, error_logs, Private);\nSPACETIMEDB_TABLE(LogEntry, debug_logs, Private);\nSPACETIMEDB_TABLE(LogEntry, audit_logs, Public);\n```\n\n## Reducers\n\n### Basic Reducers\n\n```cpp\nSPACETIMEDB_REDUCER(function_name, ReducerContext ctx, /* parameters */) {\n    // Reducer logic\n    return Ok();\n}\n```\n\n**Parameters:**\n- First parameter must always be `ReducerContext ctx`\n- Additional parameters are passed by clients\n- All parameter types must be registered with `SPACETIMEDB_STRUCT`\n- Reducer must return `ReducerResult` (Outcome<void>): either `Ok()` on success or `Err(message)` on error\n\n### Lifecycle Reducers\n\nSpecial reducers called automatically by SpacetimeDB:\n\n```cpp\n// Called when module is first published\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    // Initialize data, create default records\n    LOG_INFO(\"Module initialized\");\n    return Ok();\n}\n\n// Called when a client connects\nSPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {\n    LOG_INFO(\"Client connected\");\n    // ctx.sender() contains the client's Identity\n    return Ok();\n}\n\n// Called when a client disconnects\nSPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {\n    LOG_INFO(\"Client disconnected\");\n    // Update user status, cleanup resources\n    return Ok();\n}\n```\n\n### Reducer Context\n\nThe `ReducerContext` provides access to:\n\n```cpp\nSPACETIMEDB_REDUCER(example, ReducerContext ctx, /* params */) {\n    // Database access\n    ctx.db[table_name].insert(record);\n    \n    // Client identity\n    Identity client = ctx.sender();\n    \n    // Current timestamp\n    Timestamp now = ctx.timestamp;\n    \n    // Random number generation\n    uint64_t random = ctx.rng().next_u64();\n    int dice_roll = ctx.random<int>();\n\n    return Ok();\n}\n```\n\n## Database Operations\n\n### Table Access Patterns\n\nSpacetimeDB C++ uses two access patterns:\n\n#### 1. Table Access (`ctx.db[table_name]`)\n\nFor basic operations and iteration:\n\n```cpp\n// Insert (basic)\nUser user{0, \"Alice\", true};\nctx.db[user].insert(user);\n\n// Insert with auto-increment callback\n// If user table has FIELD_PrimaryKeyAutoInc(user, id)\nUser user_with_autoinc{0, \"Bob\", true};  // id=0 will be auto-generated\nUser inserted_user = ctx.db[user].insert(user_with_autoinc);\nLOG_INFO(\"Created user with auto-generated ID: \" + std::to_string(inserted_user.id));\n\n// Iterate all rows\nfor (const auto& user : ctx.db[user]) {\n    LOG_INFO(\"User: \" + user.name);\n}\n\n// Update (requires full row replacement)\nfor (auto& user : ctx.db[user]) {\n    if (user.name == \"Alice\") {\n        user.active = false;\n        ctx.db[users].update(user);\n        break;\n    }\n}\n```\n\n#### 2. Field Access (`ctx.db[table_field]`)\n\nFor indexed operations (requires constraints):\n\n```cpp\n// Table with primary key\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKey(user, id);\n\n// Efficient operations using primary key\nSPACETIMEDB_REDUCER(delete_user, ReducerContext ctx, uint32_t user_id) {\n    ctx.db[user_id].delete_by_key(user_id);\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(update_user, ReducerContext ctx, uint32_t user_id, std::string new_name) {\n    User updated_user{user_id, new_name, true};\n    ctx.db[user_id].update(updated_user);\n    return Ok();\n}\n```\n\n### Supported Operations\n\n| Operation | Table Access | Field Access (Indexed) |\n|-----------|--------------|------------------------|\n| Insert | `insert(row)` | - |\n| Delete | Manual iteration | `delete_by_key(key)` |\n| Update | `update(row)` | `update(row)` |\n| Query | Iteration | `filter(value)` |\n\n## Random Number Generation\n\nThe C++ bindings provides deterministic random number generation through the `ReducerContext`. The RNG is seeded with the reducer's timestamp, ensuring reproducible behavior across SpacetimeDB instances.\n\n### Basic Usage\n\n```cpp\nSPACETIMEDB_REDUCER(dice_game, ReducerContext ctx, std::string player, uint32_t sides) {\n    // Get the RNG instance (lazily initialized)\n    auto& rng = ctx.rng();\n    \n    // Generate random numbers\n    uint32_t dice_roll = rng.gen_range(1u, sides);\n    uint64_t large_number = rng.next_u64();\n    float probability = rng.gen_float();  // [0, 1)\n    bool coin_flip = rng.gen_bool();\n    \n    return Ok();\n}\n```\n\n### Core Methods\n\n#### `ctx.rng()`\nReturns the random number generator instance for this reducer call.\n\n### RNG Methods\n\n#### Basic Generation\n```cpp\nauto& rng = ctx.rng();\n\n// Generate raw random bits\nuint32_t bits32 = rng.next_u32();\nuint64_t bits64 = rng.next_u64();\n\n// Generate typed values\nint value = rng.gen<int>();\nfloat value = rng.gen<float>();    // [0, 1)\ndouble value = rng.gen<double>();  // [0, 1)\nbool value = rng.gen<bool>();\n```\n\n#### Range Generation\n```cpp\n// Integer ranges [min, max] (inclusive)\nint dice = rng.gen_range(1, 6);\nuint64_t large = rng.gen_range(1000000u, 9999999u);\n\n// Floating point ranges [min, max)\nfloat speed = rng.gen_range(0.5f, 2.0f);\ndouble precision = rng.gen_range(0.0, 100.0);\n```\n\n#### Utility Methods\n```cpp\n// Fill buffer with random bytes\nstd::vector<uint8_t> buffer(32);\nrng.fill_bytes(buffer);\n\nuint8_t raw_buffer[16];\nrng.fill_bytes(raw_buffer, 16);\n\n// Shuffle containers\nstd::vector<std::string> deck = {\"Ace\", \"King\", \"Queen\", \"Jack\"};\nrng.shuffle(deck.begin(), deck.end());\n\n// Sample random element\nstd::vector<int> options = {10, 20, 30, 40};\nint choice = rng.sample(options);  // Returns one of the elements\n```\n\n### Deterministic Behavior\n\nThe RNG is seeded with the reducer's timestamp in microseconds:\n- **Same timestamp = Same sequence**: Reducers called at the exact same microsecond will generate identical random sequences\n- **Different timestamps = Different sequences**: Even microsecond differences produce completely different random sequences\n- **Reproducible across instances**: The same reducer call will generate the same random values on any SpacetimeDB instance\n\n### Example: Casino Game\n\n```cpp\nstruct GameResult {\n    uint32_t id;\n    std::string player;\n    std::string game_type;\n    std::string result;\n    uint32_t payout;\n    Timestamp timestamp;\n};\n\nSPACETIMEDB_TABLE(GameResult, game_results, Public)\nSPACETIMEDB_STRUCT(GameResult, player, game_type, result, payout, timestamp)\n\nSPACETIMEDB_REDUCER(play_slots, ReducerContext ctx, std::string player_name) {\n    auto& rng = ctx.rng();\n    \n    // Generate three slot symbols\n    std::vector<std::string> symbols = {\"🍒\", \"🍋\", \"🔔\", \"⭐\", \"💎\"};\n    std::string slot1 = rng.sample(symbols);\n    std::string slot2 = rng.sample(symbols);\n    std::string slot3 = rng.sample(symbols);\n    \n    // Check for wins\n    uint32_t payout = 0;\n    std::string result = slot1 + \" \" + slot2 + \" \" + slot3;\n    \n    if (slot1 == slot2 && slot2 == slot3) {\n        if (slot1 == \"💎\") payout = 1000;      // Jackpot!\n        else if (slot1 == \"⭐\") payout = 500;   // Stars\n        else payout = 100;                      // Three of a kind\n    } else if (slot1 == slot2 || slot2 == slot3 || slot1 == slot3) {\n        payout = 10;  // Pair\n    }\n    \n    // Record the result\n    GameResult game{0, player_name, \"slots\", result, payout, ctx.timestamp};\n    ctx.db[game_results].insert(game);\n    return Ok();\n}\n```\n\n### Technical Details\n\n- **Algorithm**: Uses C++20's `std::mt19937_64` (64-bit Mersenne Twister)\n- **Seeding**: Timestamp microseconds since Unix epoch\n- **Thread Safety**: Each reducer call gets its own RNG instance\n- **Performance**: Lazy initialization - RNG is only created when first accessed\n- **Memory**: Uses `std::shared_ptr` to maintain copyability of `ReducerContext`\n\n## Types and Serialization\n\n### Primitive Types\n\nAll standard C++ numeric types are supported:\n\n```cpp\nstruct AllTypes {\n    // Integers\n    uint8_t u8_field;\n    uint16_t u16_field;\n    uint32_t u32_field;\n    uint64_t u64_field;\n    int8_t i8_field;\n    int16_t i16_field;\n    int32_t i32_field;\n    int64_t i64_field;\n    \n    // Large integers (SpacetimeDB types)\n    SpacetimeDB::u128 u128_field;\n    SpacetimeDB::u256 u256_field;\n    SpacetimeDB::i128 i128_field;\n    SpacetimeDB::i256 i256_field;\n    \n    // Floating point\n    float f32_field;\n    double f64_field;\n    \n    // Other\n    bool bool_field;\n    std::string string_field;\n};\nSPACETIMEDB_STRUCT(AllTypes, u8_field, u16_field, /* ... all fields ... */);\n```\n\n### Container Types\n\n```cpp\nstruct WithContainers {\n    std::vector<uint32_t> numbers;\n    std::vector<std::string> names;\n    std::optional<std::string> description;\n    std::optional<std::vector<uint32_t>> optional_numbers;\n};\nSPACETIMEDB_STRUCT(WithContainers, numbers, names, description, optional_numbers);\n```\n\n### Custom Enums\n\n#### Simple Enums (Unit Variants)\n\n```cpp\n// Using SPACETIMEDB_ENUM macro (recommended)\nSPACETIMEDB_ENUM(StatusType, Pending, Active, Inactive)\n\n// Manual implementation for complex cases\nenum class Priority : uint8_t { Low = 0, Medium = 1, High = 2 };\n\nnamespace SpacetimeDB::bsatn {\ntemplate<>\nstruct bsatn_traits<Priority> {\n    static AlgebraicType algebraic_type() {\n        return Internal::LazyTypeRegistrar<Priority>::getOrRegister([]() {\n            SumTypeBuilder builder;\n            builder.with_unit_variant(\"Low\");\n            builder.with_unit_variant(\"Medium\");\n            builder.with_unit_variant(\"High\");\n            return AlgebraicType::make_sum(builder.build());\n        }, \"Priority\");\n    }\n    \n    static void serialize(Writer& writer, const Priority& value) {\n        writer.write_u8(static_cast<uint8_t>(value));\n    }\n    \n    static Priority deserialize(Reader& reader) {\n        return static_cast<Priority>(reader.read_u8());\n    }\n};\n}\n```\n\n#### Variant Enums (With Payloads)\n\n```cpp\nstruct ErrorDetail { std::string message; };\nSPACETIMEDB_STRUCT(ErrorDetail, message);\n\n// Enum with different payload types\nSPACETIMEDB_ENUM(ResultTypes,\n    (Success, uint32_t),\n    (Error, ErrorDetail),\n    (Pending, std::monostate)  // Unit variant\n)\n```\n\n::: Note that underlying the variant enum is the std:variant which requires each type to be unique.\n\n```cpp\n// Workaround: C++ std::variant can't have duplicate types, so we create unique empty types\n// for each unit variant instead of using Unit multiple times (like C# SDK does)\nSPACETIMEDB_UNIT_TYPE(TestFFoo)\nSPACETIMEDB_UNIT_TYPE(TestFBar)\n\n// TestF enum - variant enum matching Rust: Foo, Bar, Baz(String)\nSPACETIMEDB_ENUM(TestF,\n    (Foo, TestFFoo),\n    (Bar, TestFBar),\n    (Baz, std::string)\n)\n```\n\n### Namespace Qualification for Enums\n\nAdd namespace qualification to enums for better organization in generated client code:\n\n```cpp\n// Define the enum normally\nSPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member)\n\n// Add namespace qualification (separate macro)\nSPACETIMEDB_NAMESPACE(UserRole, \"Auth\")  // Will be \"Auth.UserRole\" in client code\n\n// Multiple enums can share the same namespace\nSPACETIMEDB_ENUM(Permission, Read, Write, Execute, Delete)\nSPACETIMEDB_NAMESPACE(Permission, \"Auth\")  // Will be \"Auth.Permission\"\n\n\nstruct ConnectionInfo { std::string message; };\nSPACETIMEDB_STRUCT(ConnectionInfo, message);\n\n// Works with variant enums too\nSPACETIMEDB_ENUM(NetworkEvent,\n    (Connected, ConnectionInfo),\n    (Disconnected, std::string),\n    (Error, ErrorDetail)\n)\nSPACETIMEDB_NAMESPACE(NetworkEvent, \"Network\")  // Will be \"Network.NetworkEvent\"\n```\n\n**How It Works**:\n- The `SPACETIMEDB_NAMESPACE` macro adds compile-time metadata to qualify the type name\n- Client code generators recognize the namespace and organize types accordingly\n- The namespace prefix appears in generated TypeScript, C#, and Rust client code\n- Server-side C++ code continues to use the unqualified name\n\n**Benefits**:\n- Better organization of related types in client code\n- Avoids naming conflicts in large projects\n- Clearer API structure for client developers\n- No runtime overhead - purely compile-time feature\n\n### Custom Structs\n\n```cpp\nstruct AddressDetail {\n    std::string street;\n    std::string city;\n    std::string country;\n};\nSPACETIMEDB_STRUCT(AddressDetail, street, city, country);\n\nstruct Person {\n    uint32_t id;\n    std::string name;\n    AddressDetail address;  // Nested struct\n    std::vector<std::string> hobbies;\n};\nSPACETIMEDB_STRUCT(Person, id, name, address, hobbies);\n```\n\n## Constraints and Indexing\n\n### Constraint Types\n\nConstraints are applied **after** table registration using `FIELD_` macros:\n\n```cpp\nstruct User {\n    uint64_t id;\n    std::string email;\n    uint32_t age;\n    std::string city;\n    uint64_t sequence_num;\n    uint64_t order_id;\n};\nSPACETIMEDB_STRUCT(User, id, email, age, city, sequence_num, order_id);\nSPACETIMEDB_TABLE(User, user, Public);\n\n// Primary key (unique + clustered btree index)\nFIELD_PrimaryKey(user, id);\n\n// Auto-incrementing primary key  \nFIELD_PrimaryKeyAutoInc(user, id);\n\n// Unique constraint (creates btree index)\nFIELD_Unique(user, email);\n\n// Auto-incrementing unique field\nFIELD_UniqueAutoInc(user, sequence_num);\n\n// Btree index for fast queries and range operations\nFIELD_Index(user, age);\n\n// Auto-incrementing indexed field\nFIELD_IndexAutoInc(user, order_id);\n\n// Multi-column btree index\nFIELD_NamedMultiColumnIndex(user, age_city_idx, age, city);\n```\n\n### Constraint Requirements\n\n| Constraint Type | Allowed Types |\n|-----------------|---------------|\n| PrimaryKey | Integers, bool, string, Identity, ConnectionId, Timestamp, enums |\n| Unique | Same as PrimaryKey |\n| Index | Same as PrimaryKey |\n| AutoInc | Integer types only |\n\n### Auto-Increment Callbacks\n\nWhen using auto-increment fields, the `insert()` method automatically returns the row with the generated ID populated. This enables immediate access to generated values without requiring additional lookups.\n\n```cpp\nstruct User {\n    uint64_t id;\n    std::string name;\n    bool is_active;\n};\nSPACETIMEDB_STRUCT(User, id, name, is_active);\n// Table with auto-increment ID\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKeyAutoInc(user, id);\n\nSPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) {\n    User user{0, name, true};  // id=0 is placeholder - will be auto-generated\n    \n    // insert() returns the user with the generated ID\n    User created_user = ctx.db[user_id].insert(user);\n    \n    // Generated ID is immediately available\n    LOG_INFO(\"Created user \" + name + \" with ID: \" + std::to_string(created_user.id));\n    \n    // Can use the ID for related operations\n    if (created_user.id > 1000) {\n        LOG_INFO(\"High-value user created\");\n    }\n    return Ok();\n}\n\n// Works with all auto-increment constraint types\nFIELD_UniqueAutoInc(orders, order_number);\nFIELD_IndexAutoInc(events, sequence);\nFIELD_AutoInc(logs, entry_id);\n```\n\n**How It Works**:\n1. SpacetimeDB generates the auto-increment value server-side\n2. Only the generated column values are returned (not the full row)\n3. The SDK automatically integrates the generated values back into your row object\n4. The `insert()` method returns the complete row with generated fields populated\n\n**Multiple Auto-Increment Fields**:\nIf a table has multiple auto-increment fields, all generated values are integrated:\n\n```cpp\nstruct LogEntry {\n    uint32_t id;          // Auto-increment primary key\n    uint64_t sequence;    // Auto-increment sequence number\n    std::string message;\n};\nSPACETIMEDB_STRUCT(LogEntry, id, sequence, message);\nSPACETIMEDB_TABLE(LogEntry, log_entry, Private);\nFIELD_PrimaryKeyAutoInc(log_entry, id);\nFIELD_UniqueAutoInc(log_entry, sequence);\n\nSPACETIMEDB_REDUCER(log_message, ReducerContext ctx, std::string msg) {\n    LogEntry entry{0, 0, msg};  // Both id and sequence will be generated\n    LogEntry created = ctx.db[log_entry_id].insert(entry);\n    \n    LOG_INFO(\"Log entry \" + std::to_string(created.id) + \n             \" with sequence \" + std::to_string(created.sequence));\n    return Ok();\n}\n```\n\n### Using Indexed Fields\n\n```cpp\nstruct User {\n    uint64_t id;\n    std::string name;\n    std::string email;\n    uint32_t age;\n};\nSPACETIMEDB_STRUCT(User, id, name, email, age)\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKeyAutoInc(user, id);\nFIELD_Unique(user, email);\nFIELD_Index(user, age);\n\n// Primary key access\nSPACETIMEDB_REDUCER(get_user, ReducerContext ctx, uint32_t in_user_id) {\n    // Efficient O(log n) lookup\n    auto user_opt = ctx.db[user_id].find(in_user_id);\n    if (user_opt.has_value()) {\n        LOG_INFO(\"Found user: \" + user_opt->name);\n    }\n    return Ok();\n}\n\n// Unique field access\nSPACETIMEDB_REDUCER(find_by_email, ReducerContext ctx, std::string email) {\n    auto user_opt = ctx.db[user_email].find(email);\n    if (user_opt.has_value()) {\n        LOG_INFO(\"User with email \" + email + \": \" + user_opt->name);\n    }\n    return Ok();\n}\n\n// Non-unique index\nSPACETIMEDB_REDUCER(users_by_age, ReducerContext ctx, uint32_t age) {\n    for (const auto& user : ctx.db[user_age].filter(age)) {\n        LOG_INFO(\"User age \" + std::to_string(age) + \": \" + user.name);\n    }\n    return Ok();\n}\n```\n\n### Range Queries\n\nBtree indexes support efficient range queries using the C++ range query system:\n\n```cpp\n#include <spacetimedb/range_queries.h>\n\nstruct ProductItem {\n    uint32_t id;\n    std::string name;\n    int64_t price;\n    std::string category;\n};\nSPACETIMEDB_STRUCT(ProductItem, id, name, price, category);\nSPACETIMEDB_TABLE(ProductItem, product_item, Public);\nFIELD_Index(product_item, price);\nFIELD_Index(product_item, category);\n\nSPACETIMEDB_REDUCER(products_in_price_range, ReducerContext ctx, int64_t min_price, int64_t max_price) {\n    // Create range objects\n    auto price_range = range_inclusive(min_price, max_price);  // min_price..=max_price\n    auto expensive_items = range_from(100);                 // >= 100\n    auto cheap_items = range_to(50);                        // < 50\n    \n    // Use indexed field accessor for efficient queries\n    for (const auto& product : ctx.db[product_item_price].filter(price_range)) {\n        LOG_INFO(\"Product in range: \" + product.name + \" - $\" + std::to_string(product.price));\n    }\n    return Ok();\n}\n\nstruct User {\n    uint64_t id;\n    std::string name;\n    std::string email;\n    uint32_t age;\n};\nSPACETIMEDB_STRUCT(User, id, name, email, age)\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKeyAutoInc(user, id);\nFIELD_Unique(user, email);\nFIELD_Index(user, age);\nFIELD_Index(user, name);\n\n// All range construction patterns\nSPACETIMEDB_REDUCER(demonstrate_ranges, ReducerContext ctx) {\n    auto range1 = range_from(25);                    // 25..  (>= 25)\n    auto range2 = range_to(30);                      // ..30  (< 30)\n    auto range3 = range(20, 35);                     // 20..35 (>= 20, < 35)\n    auto range4 = range_inclusive(20, 35);           // 20..=35 (>= 20, <= 35)\n    auto range5 = range_to_inclusive(30);            // ..=30 (>= 30)\n    auto range6 = range_full<int>();                 // .. (all values)\n    \n    // Check if value is in range\n    bool in_range = range4.contains(25);  // true\n    \n    // Range queries work with any indexed type\n    auto name_range = range(std::string(\"A\"), std::string(\"M\")); // Names A-L\n    for (const auto& user : ctx.db[users_name].filter(name_range)) {\n        LOG_INFO(\"User: \" + user.name);\n    }\n    return Ok();\n}\n```\n\n## Client Visibility Filters\n\nControl what data clients can see using row-level security:\n\n```cpp\n// Define structs first\nstruct UserData {\n    uint32_t id;\n    Identity owner_identity;\n    std::string private_info;\n    bool is_sensitive;\n};\nSPACETIMEDB_STRUCT(UserData, id, owner_identity, private_info, is_sensitive);\nSPACETIMEDB_TABLE(UserData, user_data, Public);\n\nstruct Post {\n    uint32_t id;\n    Identity author_identity;\n    std::string content;\n    bool is_public;\n    Timestamp created_at;\n};\nSPACETIMEDB_STRUCT(Post, id, author_identity, content, is_public, created_at);\nSPACETIMEDB_TABLE(Post, posts, Public);\n\nstruct Message {\n    uint32_t id;\n    Identity sender_identity;\n    std::string content;\n    Timestamp timestamp;\n};\nSPACETIMEDB_STRUCT(Message, id, sender_identity, content, timestamp);\nSPACETIMEDB_TABLE(Message, messages, Public);\n\n// Define visibility filters (must match table names)\n// Only show users their own data\nSPACETIMEDB_CLIENT_VISIBILITY_FILTER(user_data_filter, \n    \"SELECT * FROM user_data WHERE owner_identity = current_user_identity()\"\n);\n\n// Show public posts and user's own private posts\nSPACETIMEDB_CLIENT_VISIBILITY_FILTER(posts_filter,\n    \"SELECT * FROM posts WHERE is_public = true OR author_identity = current_user_identity()\"\n);\n\n// Time-based visibility (only show recent messages)\nSPACETIMEDB_CLIENT_VISIBILITY_FILTER(recent_messages,\n    \"SELECT * FROM messages WHERE timestamp > (current_timestamp() - INTERVAL '1 day')\"\n);\n```\n\n**Available SQL functions for filters:**\n- `current_user_identity()` - Get the calling client's Identity\n- `current_timestamp()` - Get current server timestamp\n- Standard SQL operators and functions\n- Table and column references\n\n## Special Types\n\nSpacetimeDB provides built-in types for common use cases:\n\n### Identity\n\nRepresents a unique user/client identity:\n\n```cpp\nstruct User {\n    Identity id;        // Unique across all users\n    std::string name;\n};\nSPACETIMEDB_STRUCT(User, id, name);\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKey(user, id);\n\nSPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) {\n    User user{ctx.sender(), name};  // ctx.sender() is the calling client's identity\n    ctx.db[user_id].insert(user);\n    return Ok();\n}\n```\n\n### ConnectionId\n\nRepresents a specific client connection:\n\n```cpp\nstruct Session {\n    ConnectionId connection;\n    Identity user;\n    Timestamp login_time;\n};\n```\n\n### Timestamp\n\nRepresents a point in time:\n\n```cpp\nstruct SystemEvent {\n    uint32_t id;\n    std::string event_name;\n    Timestamp occurred_at;\n};\nSPACETIMEDB_STRUCT(SystemEvent, id, event_name, occurred_at);\nSPACETIMEDB_TABLE(SystemEvent, system_event, Public);\nFIELD_PrimaryKeyAutoInc(system_event, id);\n\nSPACETIMEDB_REDUCER(log_event, ReducerContext ctx, std::string event_name) {\n    SystemEvent event{0, event_name, ctx.timestamp};  // Current time\n    ctx.db[system_event].insert(event);\n    LOG_INFO(\"Event logged: \" + event_name);\n    return Ok();\n}\n```\n\n### TimeDuration\n\nRepresents a duration of time:\n\n```cpp\nstruct Task {\n    uint32_t id;\n    std::string name;\n    TimeDuration estimated_duration;\n    Timestamp created_at;\n};\nSPACETIMEDB_STRUCT(Task, id, name, estimated_duration, created_at);\nSPACETIMEDB_TABLE(Task, task, Public);\nFIELD_PrimaryKeyAutoInc(task, id);\n\nSPACETIMEDB_REDUCER(create_task, ReducerContext ctx, std::string name, uint64_t hours) {\n    // Create durations in different ways\n    auto duration = TimeDuration::from_hours(hours);\n    // Other duration constructors:\n    // auto one_hour = TimeDuration::from_hours(1);\n    // auto five_minutes = TimeDuration::from_secs(5 * 60);\n    // auto milliseconds = TimeDuration::from_millis(5000);\n    \n    Task new_task{0, name, duration, ctx.timestamp};\n    ctx.db[task].insert(new_task);\n    LOG_INFO(\"Task created: \" + name);\n    return Ok();\n}\n```\n\n### Scheduled Reducers\n\nSchedule reducers to run automatically at specified times:\n\n```cpp\n// Define a table to store scheduled tasks\nstruct ScheduledTask {\n    uint32_t id;\n    ScheduleAt run_at;         // When to execute\n    std::string task_data;\n    Timestamp created_at;\n};\nSPACETIMEDB_STRUCT(ScheduledTask, id, run_at, task_data, created_at);\nSPACETIMEDB_TABLE(ScheduledTask, scheduled_task, Private);\nFIELD_PrimaryKeyAutoInc(scheduled_task, id);\n\n// Register the table for scheduling (column 1 = run_at field, index 0-based)\nSPACETIMEDB_SCHEDULE(scheduled_task, 1, process_scheduled_task);\n\n// The scheduled reducer - called automatically when tasks are due\nSPACETIMEDB_REDUCER(process_scheduled_task, ReducerContext ctx, ScheduledTask task) {\n    LOG_INFO(\"Processing scheduled task: \" + task.task_data);\n    \n    // Process the task...\n    \n    // Optionally schedule another task\n    auto next_run = ScheduleAt(TimeDuration::from_hours(24)); // Run in 24 hours\n    ScheduledTask next_task{0, next_run, \"Daily cleanup\", ctx.timestamp};\n    ctx.db[scheduled_task].insert(next_task);\n    return Ok();\n}\n\n// Create scheduled tasks from other reducers\nSPACETIMEDB_REDUCER(schedule_reminder, ReducerContext ctx, std::string message, uint64_t delay_seconds) {\n    auto run_time = ScheduleAt(TimeDuration::from_seconds(delay_seconds));\n    ScheduledTask reminder{0, run_time, message, ctx.timestamp};\n    ctx.db[scheduled_task].insert(reminder);\n    \n    LOG_INFO(\"Reminder scheduled for \" + std::to_string(delay_seconds) + \" seconds\");\n    return Ok();\n}\n\n// ScheduleAt can be created with TimeDuration (relative) or Timestamp (absolute)\nSPACETIMEDB_REDUCER(schedule_examples, ReducerContext ctx) {\n    // Relative scheduling (from now)\n    auto in_one_hour = ScheduleAt(TimeDuration::from_hours(1));\n    auto in_five_minutes = ScheduleAt(TimeDuration::from_seconds(5 * 60));\n    \n    // Absolute scheduling (specific time)\n    auto specific_time_millis = Timestamp::from_millis_since_epoch(1640995200000);  // Jan 1, 2022\n    auto absolute_schedule = ScheduleAt(specific_time_millis);\n    \n    // Schedule tasks\n    ScheduledTask hourly{0, in_one_hour, \"Hourly task\", ctx.timestamp};\n    ScheduledTask quick{0, in_five_minutes, \"Quick task\", ctx.timestamp};\n    ScheduledTask absolute{0, absolute_schedule, \"New Year task\", ctx.timestamp};\n    ctx.db[scheduled_task].insert(hourly);\n    ctx.db[scheduled_task].insert(quick);\n    ctx.db[scheduled_task].insert(absolute);\n    return Ok();\n}\n```\n\n**Key points about scheduled reducers:**\n- Must have a table with a `ScheduleAt` field\n- Use `SPACETIMEDB_SCHEDULE(table_name, column_index, reducer_name)` to register\n- The scheduled reducer receives the entire row as parameter\n- Column index is 0-based (0 = first field, 1 = second field, etc.)\n- Scheduled reducers run with module identity, not client identity\n\n## Logging\n\nSpacetimeDB provides structured logging:\n\n```cpp\nSPACETIMEDB_REDUCER(example, ReducerContext ctx) {\n    LOG_DEBUG(\"Debug information\");\n    LOG_INFO(\"General information\");\n    LOG_WARN(\"Warning message\");\n    LOG_ERROR(\"Error occurred\");\n    LOG_PANIC(\"Fatal error\");  // Terminates reducer\n    \n    // With timing\n    {\n        LogStopwatch timer(std::string_view(\"Database operation\"));\n        // ... time-consuming operation ...\n    } // Automatically logs duration\n    return Ok();\n}\n```\n\n## Build System\n\n### CMake Configuration\n\nThe C++ bindings uses CMake with Emscripten for WebAssembly compilation (`spacetime init` will generate one for you):\n\n```cmake\n# Basic configuration\ncmake_minimum_required(VERSION 3.16)\nproject(my-module)\nset(CMAKE_CXX_STANDARD 20)\n\n# Module source (defaults to src/lib.cpp)\nif(NOT DEFINED MODULE_SOURCE)\n    set(MODULE_SOURCE \"src/lib.cpp\")\nendif()\n\n# Output name (defaults to \"lib\")\nif(NOT DEFINED OUTPUT_NAME)\n    set(OUTPUT_NAME \"lib\")\nendif()\n\n# Link SpacetimeDB library\nset(SPACETIMEDB_CPP_LIBRARY_PATH \"path/to/bindings-cpp\")\nadd_executable(${OUTPUT_NAME} ${MODULE_SOURCE})\ntarget_include_directories(${OUTPUT_NAME} PRIVATE ${SPACETIMEDB_CPP_LIBRARY_PATH}/include)\nadd_subdirectory(${SPACETIMEDB_CPP_LIBRARY_PATH} spacetimedb_cpp_library)\ntarget_link_libraries(${OUTPUT_NAME} PRIVATE spacetimedb_cpp_library)\n```\n\n### Build Commands\n\n```bash\n# With spacetime\nspacetime build -p .\n\n# Manual build\nemcmake cmake -B build .\ncmake --build build\n\n# Publishing\nspacetime publish . my-database\n# or manual\nspacetime publish --bin-path build/lib.wasm my-database\n```\n\n### Emscripten Settings\n\nThe build system automatically configures:\n- WebAssembly output format\n- Required exports for SpacetimeDB\n- Memory settings optimized for database operations\n- Exception handling disabled for WASM compatibility\n\n## Examples\n\n### Complete User Management System\n\n```cpp\n#include <spacetimedb.h>\nusing namespace SpacetimeDB;\n\n// User data structure\nstruct User {\n    uint32_t id;\n    Identity identity;\n    std::string username;\n    std::string email;\n    Timestamp created_at;\n    bool active;\n};\nSPACETIMEDB_STRUCT(User, id, identity, username, email, created_at, active);\n\n// User table with constraints\nSPACETIMEDB_TABLE(User, user, Public);\nFIELD_PrimaryKeyAutoInc(user, id);\nFIELD_Unique(user, identity);\nFIELD_Unique(user, username);\nFIELD_Unique(user, email);\nFIELD_Index(user, active);\n\n// Register new user\nSPACETIMEDB_REDUCER(register_user, ReducerContext ctx, std::string username, std::string email) {\n    // Check if user already exists\n    auto user_opt = ctx.db[user_identity].find(ctx.sender());\n    if (user_opt && user_opt->active) {\n        return Err(\"User already registered\");\n    }\n    \n    User new_user{0, ctx.sender(), username, email, ctx.timestamp, true};\n    ctx.db[user].insert(new_user);\n    LOG_INFO(\"User registered: \" + username);\n    return Ok();\n}\n\n// Update user profile\nSPACETIMEDB_REDUCER(update_profile, ReducerContext ctx, std::string new_username) {\n    auto user_opt = ctx.db[user_identity].find(ctx.sender());\n    if (user_opt && user_opt->active) {\n        User updated_user = *user_opt;\n        updated_user.username = new_username;\n        ctx.db[user_id].update(updated_user);\n        LOG_INFO(\"Profile updated\");\n        return Ok();\n    }\n    return Err(\"User not found or inactive\");\n}\n\n// Deactivate user\nSPACETIMEDB_REDUCER(deactivate_user, ReducerContext ctx) {\n    auto user_opt = ctx.db[user_identity].find(ctx.sender());\n    if (user_opt && user_opt->active) {\n        User updated_user = *user_opt;\n        updated_user.active = false;\n        ctx.db[user_id].update(updated_user);\n        LOG_INFO(\"User deactivated\");\n        return Ok();\n    }\n    return Ok();\n}\n\n// Admin: List all active user\nSPACETIMEDB_REDUCER(list_active_users, ReducerContext ctx) {\n    for (const auto& user : ctx.db[user_active].filter(true)) {\n        LOG_INFO(\"Active user: \" + user.username + \" (\" + user.email + \")\");\n    }\n    return Ok();\n}\n\n// Lifecycle: Track connections\nSPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {\n    LOG_INFO(\"Client connected\");\n    return Ok();\n}\n\nSPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {\n    LOG_INFO(\"Client disconnected\");\n    return Ok();\n}\n```\n\n### Advanced: Chat System with Channels\n\n```cpp\n// Channel structure\nstruct Channel {\n    uint32_t id;\n    std::string name;\n    std::string description;\n    Identity owner;\n    bool public_channel;\n};\nSPACETIMEDB_STRUCT(Channel, id, name, description, owner, public_channel);\nSPACETIMEDB_TABLE(Channel, channels, Public);\nFIELD_PrimaryKeyAutoInc(channels, id);\nFIELD_Unique(channels, name);\n\n// Message structure\nstruct Message {\n    uint32_t id;\n    uint32_t channel_id;\n    Identity sender;\n    std::string content;\n    Timestamp timestamp;\n};\nSPACETIMEDB_STRUCT(Message, id, channel_id, sender, content, timestamp);\nSPACETIMEDB_TABLE(Message, messages, Public);\nFIELD_PrimaryKeyAutoInc(messages, id);\nFIELD_Index(messages, channel_id);\nFIELD_Index(messages, sender);\n\n// Create channel\nSPACETIMEDB_REDUCER(create_channel, ReducerContext ctx, std::string name, std::string description, bool is_public) {\n    Channel channel{0, name, description, ctx.sender(), is_public};\n    ctx.db[channels].insert(channel);\n    LOG_INFO(\"Channel created: \" + name);\n    return Ok();\n}\n\n// Send message to channel\nSPACETIMEDB_REDUCER(send_message, ReducerContext ctx, uint32_t channel_id, std::string content) {\n    // Verify channel exists\n    bool channel_exists = false;\n    for (const auto& channel : ctx.db[channels]) {\n        if (channel.id == channel_id) {\n            channel_exists = true;\n            break;\n        }\n    }\n    \n    if (!channel_exists) {\n        LOG_ERROR(\"Channel not found\");\n        return Err(\"Channel not found\");\n    }\n    \n    Message message{0, channel_id, ctx.sender(), content, ctx.timestamp};\n    ctx.db[messages].insert(message);\n    return Ok();\n}\n\n// Get channel history\nSPACETIMEDB_REDUCER(get_channel_history, ReducerContext ctx, uint32_t channel_id) {\n    for (const auto& message : ctx.db[messages_channel_id].filter(channel_id)) {\n        LOG_INFO(\"Message: \" + message.content);\n    }\n    return Ok();\n}\n```\n\n## Error Handling\n\n### Validation Patterns\n\n```cpp\nSPACETIMEDB_REDUCER(update_age, ReducerContext ctx, uint32_t new_age) {\n    if (new_age > 150) {\n        LOG_ERROR(\"Invalid age: \" + std::to_string(new_age));\n        return Err(\"Invalid age value\");\n    }\n    \n    // Find and update user...\n    return Ok();\n}\n```\n\n---\n\nThis completes the C++ reference documentation. For more examples and advanced patterns, see the working modules in [`modules/sdk-test-cpp`](../../modules/sdk-test-cpp) and [`modules/module-test-cpp`](../../modules/module-test-cpp).\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/abi/FFI.h",
    "content": "#ifndef SPACETIMEDB_FFI_H\n#define SPACETIMEDB_FFI_H\n\n#include <cstdint>\n#include <cstddef>\n#include \"spacetimedb/abi/opaque_types.h\"\n#include \"spacetimedb/abi/abi.h\"\n\n/**\n * @file FFI.h\n * @brief SpacetimeDB Foreign Function Interface (FFI) layer for C++ modules\n * \n * This file re-exports the raw ABI functions with additional type aliases\n * and convenience functions. Since we now use C# style opaque types that\n * are ABI-compatible, no conversion is needed.\n * \n * Organization:\n * - Raw C ABI with opaque types is in abi.h\n * - This file provides type aliases and convenience functions\n * \n * Key Features:\n * - Type-safe opaque types prevent mixing TableId with IndexId etc.\n * - Full BSATN integration for all data operations\n * - Modern iterator API with proper resource management\n * - Comprehensive error handling with Status codes\n * \n * Note: WASI shims for C++ standard library support are provided separately\n * in the module library implementation.\n */\n\n// ========================================================================\n// C++ TYPE ALIASES AND CONVENIENCE FUNCTIONS\n// ========================================================================\n\nnamespace SpacetimeDB {\nnamespace FFI {\n\nusing LogLevel = ::SpacetimeDB::LogLevel;\nusing IndexType = ::SpacetimeDB::IndexType;\n\n// Re-export all functions from the raw ABI\n// Since we now use ABI-compatible opaque types, no conversion is needed\nusing ::table_id_from_name;\nusing ::index_id_from_name;\nusing ::datastore_table_row_count;\nusing ::datastore_table_scan_bsatn;\nusing ::datastore_index_scan_range_bsatn;\nusing ::datastore_index_scan_point_bsatn;\nusing ::datastore_btree_scan_bsatn;\nusing ::row_iter_bsatn_advance;\nusing ::row_iter_bsatn_close;\nusing ::datastore_insert_bsatn;\nusing ::datastore_update_bsatn;\nusing ::datastore_delete_by_index_scan_range_bsatn;\nusing ::datastore_delete_by_index_scan_point_bsatn;\nusing ::datastore_delete_by_btree_scan_bsatn;\nusing ::datastore_delete_all_by_eq_bsatn;\nusing ::bytes_source_read;\nusing ::bytes_source_remaining_length;\nusing ::bytes_sink_write;\nusing ::console_log;\nusing ::console_timer_start;\nusing ::console_timer_end;\n\n// ===== Scheduling =====\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\nusing ::volatile_nonatomic_schedule_immediate;\n#endif\n\n// ===== Identity =====\nusing ::identity;\n\n// ===== JWT =====\nusing ::get_jwt;\n\n// ===== Procedure Transactions =====\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\nusing ::procedure_start_mut_tx;\nusing ::procedure_commit_mut_tx;\nusing ::procedure_abort_mut_tx;\n#endif\n\n// ===== Module Export Helpers =====\n\n// Helper for __describe_module__ implementation\ninline void describe_module(BytesSink description) {\n    ::__describe_module__(description);\n}\n\n// Helper for __call_reducer__ implementation\ninline int16_t call_reducer(\n    uint32_t id,\n    uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n    uint64_t conn_id_0, uint64_t conn_id_1,\n    uint64_t timestamp, \n    BytesSource args, \n    BytesSink error) {\n    return ::__call_reducer__(id, sender_0, sender_1, sender_2, sender_3,\n                             conn_id_0, conn_id_1, timestamp, \n                             args, error);\n}\n\n// Utility functions for common operations\nnamespace Utils {\n\n// Helper to write data to a BytesSink\ninline void write_bytes_to_sink(BytesSink sink_handle, const uint8_t* data, size_t len) {\n    size_t buffer_len = len;\n    Status status = bytes_sink_write(sink_handle, data, &buffer_len);\n    if (is_error(status)) {\n        // In a real implementation, this would use the SDK's exception system\n        // For now, we just ignore errors in this utility function\n    }\n}\n\n// Helper to read all data from a BytesSource\ninline bool read_all_from_source(BytesSource source_handle, uint8_t* buffer, size_t* buffer_len) {\n    int16_t result = bytes_source_read(source_handle, buffer, buffer_len);\n    return result >= 0;\n}\n\n} // namespace Utils\n\n// Additional status codes\nnamespace StatusCode {\n    using namespace ::SpacetimeDB::StatusCode;\n    constexpr Status EXHAUSTED{16};\n}\n\n// Custom wrapper for simplified logging\ninline void console_log(const uint8_t* message, size_t message_len, LogLevel level) {\n    ::console_log(level, nullptr, 0, nullptr, 0, 0, message, message_len);\n}\n\n} // namespace FFI\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_FFI_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/abi/abi.h",
    "content": "#ifndef SPACETIMEDB_ABI_H\n#define SPACETIMEDB_ABI_H\n\n#include <cstdint>\n#include <cstddef>\n#include \"opaque_types.h\"\n\n/**\n * @file abi.h\n * @brief Raw C ABI interface for SpacetimeDB modules\n * \n * This file contains the raw C-compatible ABI declarations.\n * These functions use only C-compatible types and are suitable\n * for extern \"C\" linkage.\n */\n\n// ========================================================================\n// SECTION 1: IMPORT DECLARATIONS - Functions provided by SpacetimeDB host\n// ========================================================================\n\n// Macro for declaring imported functions from the SpacetimeDB host\n// Most stable syscalls are in spacetime_10.0\n#define STDB_IMPORT(name) \\\n    __attribute__((import_module(\"spacetime_10.0\"), import_name(#name))) extern\n\n// BytesSource length query is in spacetime_10.1\n#define STDB_IMPORT_10_1(name) \\\n    __attribute__((import_module(\"spacetime_10.1\"), import_name(#name))) extern\n\n// Procedure-specific syscalls are in spacetime_10.3\n#define STDB_IMPORT_10_3(name) \\\n    __attribute__((import_module(\"spacetime_10.3\"), import_name(#name))) extern\n\n// Point scan delete is in spacetime_10.4\n#define STDB_IMPORT_10_4(name) \\\n    __attribute__((import_module(\"spacetime_10.4\"), import_name(#name))) extern\n\n// Import opaque types into global namespace for C compatibility\nusing SpacetimeDB::Status;\nusing SpacetimeDB::TableId;\nusing SpacetimeDB::IndexId;\nusing SpacetimeDB::ColId;\nusing SpacetimeDB::IndexType;\nusing SpacetimeDB::LogLevel;\nusing SpacetimeDB::BytesSink;\nusing SpacetimeDB::BytesSource;\nusing SpacetimeDB::RowIter;\nusing SpacetimeDB::ConsoleTimerId;\n\n// Disable warnings about C-linkage with user-defined types\n// This is safe because our opaque types are single-field structs\n// which have the same ABI as their underlying type\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wreturn-type-c-linkage\"\n\nextern \"C\" {\n\n// ===== Table and Index Management =====\nSTDB_IMPORT(table_id_from_name)\nStatus table_id_from_name(const uint8_t* name_ptr, size_t name_len, TableId* out);\n\nSTDB_IMPORT(index_id_from_name)\nStatus index_id_from_name(const uint8_t* name_ptr, size_t name_len, IndexId* out);\n\n// ===== Table Operations =====\nSTDB_IMPORT(datastore_table_row_count)\nStatus datastore_table_row_count(TableId table_id, uint64_t* out);\n\nSTDB_IMPORT(datastore_table_scan_bsatn)\nStatus datastore_table_scan_bsatn(TableId table_id, RowIter* out);\n\n// ===== Index Scanning =====\nSTDB_IMPORT(datastore_index_scan_range_bsatn)\nStatus datastore_index_scan_range_bsatn(\n    IndexId index_id, const uint8_t* prefix_ptr, size_t prefix_len, ColId prefix_elems,\n    const uint8_t* rstart_ptr, size_t rstart_len, const uint8_t* rend_ptr, size_t rend_len, \n    RowIter* out);\n\nSTDB_IMPORT_10_4(datastore_index_scan_point_bsatn)\nStatus datastore_index_scan_point_bsatn(\n    IndexId index_id, const uint8_t* point_ptr, size_t point_len,\n    RowIter* out);\n\n// Deprecated: Use datastore_index_scan_range_bsatn instead\nSTDB_IMPORT(datastore_btree_scan_bsatn)\nStatus datastore_btree_scan_bsatn(\n    IndexId index_id, const uint8_t* prefix_ptr, size_t prefix_len, ColId prefix_elems,\n    const uint8_t* rstart_ptr, size_t rstart_len, const uint8_t* rend_ptr, size_t rend_len, \n    RowIter* out);\n\n// ===== Row Iterator Operations =====\nSTDB_IMPORT(row_iter_bsatn_advance)\nint16_t row_iter_bsatn_advance(RowIter iter, uint8_t* buffer_ptr, size_t* buffer_len_ptr);\n\nSTDB_IMPORT(row_iter_bsatn_close)\nStatus row_iter_bsatn_close(RowIter iter);\n\n// ===== Data Manipulation =====\nSTDB_IMPORT(datastore_insert_bsatn)\nStatus datastore_insert_bsatn(TableId table_id, uint8_t* row_ptr, size_t* row_len_ptr);\n\nSTDB_IMPORT(datastore_update_bsatn)\nStatus datastore_update_bsatn(TableId table_id, IndexId index_id, uint8_t* row_ptr, size_t* row_len_ptr);\n\n// ===== Delete Operations =====\nSTDB_IMPORT(datastore_delete_by_index_scan_range_bsatn)\nStatus datastore_delete_by_index_scan_range_bsatn(\n    IndexId index_id, const uint8_t* prefix_ptr, size_t prefix_len, ColId prefix_elems,\n    const uint8_t* rstart_ptr, size_t rstart_len, const uint8_t* rend_ptr, size_t rend_len, \n    uint32_t* out);\n\nSTDB_IMPORT_10_4(datastore_delete_by_index_scan_point_bsatn)\nStatus datastore_delete_by_index_scan_point_bsatn(\n    IndexId index_id, const uint8_t* point_ptr, size_t point_len,\n    uint32_t* out);\n\nSTDB_IMPORT(datastore_delete_by_btree_scan_bsatn)\nStatus datastore_delete_by_btree_scan_bsatn(\n    IndexId index_id, const uint8_t* prefix_ptr, size_t prefix_len, ColId prefix_elems,\n    const uint8_t* rstart_ptr, size_t rstart_len, const uint8_t* rend_ptr, size_t rend_len, \n    uint32_t* out);\n\nSTDB_IMPORT(datastore_delete_all_by_eq_bsatn)\nStatus datastore_delete_all_by_eq_bsatn(\n    TableId table_id, const uint8_t* rel_ptr, size_t rel_len,\n    uint32_t* out);\n\n// ===== Bytes Source/Sink Operations =====\nSTDB_IMPORT(bytes_source_read)\nint16_t bytes_source_read(BytesSource source, uint8_t* buffer_ptr, size_t* buffer_len_ptr);\n\nSTDB_IMPORT_10_1(bytes_source_remaining_length)\nint16_t bytes_source_remaining_length(BytesSource source, uint32_t* out);\n\nSTDB_IMPORT(bytes_sink_write)\nStatus bytes_sink_write(BytesSink sink, const uint8_t* buffer_ptr, size_t* buffer_len_ptr);\n\n// ===== Console/Logging Operations =====\nSTDB_IMPORT(console_log)\nvoid console_log(\n    LogLevel level, const uint8_t* target_ptr, size_t target_len,\n    const uint8_t* filename_ptr, size_t filename_len, uint32_t line_number,\n    const uint8_t* message_ptr, size_t message_len);\n\nSTDB_IMPORT(console_timer_start)\nConsoleTimerId console_timer_start(const uint8_t* name_ptr, size_t name_len);\n\nSTDB_IMPORT(console_timer_end)\nStatus console_timer_end(ConsoleTimerId timer_id);\n\n// ===== Scheduling =====\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\nSTDB_IMPORT(volatile_nonatomic_schedule_immediate)\nvoid volatile_nonatomic_schedule_immediate(\n    const uint8_t* name_ptr, size_t name_len, const uint8_t* args_ptr, size_t args_len);\n#endif\n\n// ===== Identity =====\nSTDB_IMPORT(identity)\nvoid identity(uint8_t* id_ptr);\n\n// ===== JWT (spacetime_10.2) =====\n#define STDB_IMPORT_10_2(name) \\\n    __attribute__((import_module(\"spacetime_10.2\"), import_name(#name))) extern\n\nSTDB_IMPORT_10_2(get_jwt)\nStatus get_jwt(const uint8_t* connection_id_ptr, BytesSource* out);\n\n// ===== Procedure Transactions (spacetime_10.3) =====\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\nSTDB_IMPORT_10_3(procedure_start_mut_tx)\nStatus procedure_start_mut_tx(int64_t* out);\n\nSTDB_IMPORT_10_3(procedure_commit_mut_tx)\nStatus procedure_commit_mut_tx();\n\nSTDB_IMPORT_10_3(procedure_abort_mut_tx)\nStatus procedure_abort_mut_tx();\n\n// ===== Procedure HTTP (spacetime_10.3) =====\nSTDB_IMPORT_10_3(procedure_http_request)\nStatus procedure_http_request(\n    const uint8_t* request_ptr, size_t request_len,\n    const uint8_t* body_ptr, size_t body_len,\n    BytesSource out[2]);\n#endif\n\n} // extern \"C\"\n\n// ========================================================================\n// SECTION 2: EXPORT DECLARATIONS - Functions modules provide to SpacetimeDB\n// ========================================================================\n\n// Macro for declaring exported functions that the module provides\n#define STDB_EXPORT(name) __attribute__((export_name(#name)))\n\nextern \"C\" {\n\n// ===== Required Module Exports =====\nSTDB_EXPORT(__describe_module__)\nvoid __describe_module__(BytesSink description);\n\nSTDB_EXPORT(__call_reducer__)\nint16_t __call_reducer__(\n    uint32_t id,\n    uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n    uint64_t conn_id_0, uint64_t conn_id_1,\n    uint64_t timestamp, \n    BytesSource args, \n    BytesSink error);\n\n// ========================================================================\n// WASI SHIMS\n// ========================================================================\n\n// This indicates that WASI shims are provided by the SpacetimeDB C++ Module Library\n// When this is defined, modules can safely use the C++ standard library\n// The actual WASI function declarations come from system headers (wasi/api.h)\n// Our implementations are in wasi_shims.cpp\n#define SPACETIMEDB_HAS_WASI_SHIMS 1\n\n} // extern \"C\"\n\n#pragma clang diagnostic pop\n\n#endif // SPACETIMEDB_ABI_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/abi/opaque_types.h",
    "content": "#ifndef SPACETIMEDB_OPAQUE_TYPES_H\n#define SPACETIMEDB_OPAQUE_TYPES_H\n\n#include <cstdint>\n#include <functional>\n#include <unordered_map>\n#include <string>\n\n/**\n * @file opaque_types.h\n * @brief Type-safe opaque wrappers for SpacetimeDB handle types\n * \n * This file provides opaque type wrappers matching the C# implementation\n * to prevent accidental mixing of different handle types and improve type safety.\n * \n * These types use C# style single-field structs for direct ABI compatibility,\n * allowing them to be passed directly to C imports without conversion.\n * \n * Benefits:\n * - Prevents mixing TableId with IndexId, etc.\n * - Makes APIs more self-documenting\n * - Catches type errors at compile time\n * - Zero runtime overhead (optimized away)\n * - Direct ABI compatibility with C imports\n */\n\nnamespace SpacetimeDB {\n\n// Macro to define opaque typedef with C# style single-field struct\n// This ensures ABI compatibility - struct with single field has same\n// memory layout and calling convention as the field itself\n#define SPACETIMEDB_OPAQUE_TYPEDEF(Name, UnderlyingType)                      \\\n    struct Name {                                                              \\\n        UnderlyingType inner;                                                  \\\n                                                                              \\\n        /* Constructors */                                                     \\\n        constexpr Name() : inner(0) {}                                        \\\n        constexpr explicit Name(UnderlyingType v) : inner(v) {}               \\\n                                                                              \\\n        /* Conversion operators for backward compatibility */                  \\\n        constexpr explicit operator UnderlyingType() const { return inner; }   \\\n        constexpr UnderlyingType get() const { return inner; }                \\\n \\\n                                                                              \\\n        /* Comparison operators */                                             \\\n        constexpr bool operator==(const Name& other) const {                  \\\n            return inner == other.inner;                                       \\\n        }                                                                      \\\n        constexpr bool operator!=(const Name& other) const {                  \\\n            return inner != other.inner;                                       \\\n        }                                                                      \\\n        constexpr bool operator<(const Name& other) const {                   \\\n            return inner < other.inner;                                        \\\n        }                                                                      \\\n        constexpr bool operator<=(const Name& other) const {                  \\\n            return inner <= other.inner;                                       \\\n        }                                                                      \\\n        constexpr bool operator>(const Name& other) const {                   \\\n            return inner > other.inner;                                        \\\n        }                                                                      \\\n        constexpr bool operator>=(const Name& other) const {                  \\\n            return inner >= other.inner;                                       \\\n        }                                                                      \\\n                                                                              \\\n        /* Check if valid (non-zero) */                                        \\\n        constexpr bool is_valid() const { return inner != 0; }                \\\n        constexpr explicit operator bool() const { return is_valid(); }        \\\n    };                                                                         \\\n                                                                              \\\n    /* Hash support for unordered containers */                               \\\n    inline std::size_t hash_value(const Name& id) {                          \\\n        return std::hash<UnderlyingType>{}(id.inner);                        \\\n    }\n\n// Define opaque types matching C# bindings\nSPACETIMEDB_OPAQUE_TYPEDEF(Status, uint16_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(TableId, uint32_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(IndexId, uint32_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(ColId, uint16_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(IndexType, uint8_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(LogLevel, uint8_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(BytesSink, uint32_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(BytesSource, uint32_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(RowIter, uint32_t)\nSPACETIMEDB_OPAQUE_TYPEDEF(ConsoleTimerId, uint32_t)\n\n// Common invalid/sentinel values\nnamespace Invalid {\n    constexpr TableId TABLE_ID{0};\n    constexpr IndexId INDEX_ID{0};\n    constexpr RowIter ROW_ITER{0xFFFFFFFF};\n    constexpr BytesSource BYTES_SOURCE{0xFFFFFFFF};\n    constexpr BytesSink BYTES_SINK{0xFFFFFFFF};\n    constexpr ConsoleTimerId CONSOLE_TIMER{0};\n}\n\n// Define all status codes in one place using X-macro pattern\n#define SPACETIMEDB_STATUS_CODES(X) \\\n    X(OK, 0) \\\n    X(HOST_CALL_FAILURE, 1) \\\n    X(NOT_IN_TRANSACTION, 2) \\\n    X(BSATN_DECODE_ERROR, 3) \\\n    X(NO_SUCH_TABLE, 4) \\\n    X(NO_SUCH_INDEX, 5) \\\n    X(NO_SUCH_ITER, 6) \\\n    X(NO_SUCH_CONSOLE_TIMER, 7) \\\n    X(NO_SUCH_BYTES, 8) \\\n    X(NO_SPACE, 9) \\\n    X(BUFFER_TOO_SMALL, 11) \\\n    X(UNIQUE_ALREADY_EXISTS, 12) \\\n    X(SCHEDULE_AT_DELAY_TOO_LONG, 13) \\\n    X(INDEX_NOT_UNIQUE, 14) \\\n    X(NO_SUCH_ROW, 15) \\\n    X(AUTO_INC_OVERFLOW, 16) \\\n    X(NO_SUCH_REDUCER, 999) \\\n    X(UNKNOWN, 0xFFFF)\n\n// Status code constants generated from the X-macro\nnamespace StatusCode {\n    // Generate constants\n    #define X(name, value) constexpr Status name{value};\n    SPACETIMEDB_STATUS_CODES(X)\n    #undef X\n    \n    // Generate string lookup function\n    inline const char* to_string(Status status) {\n        switch (status.inner) {\n            #define X(name, value) case value: return #name;\n            SPACETIMEDB_STATUS_CODES(X)\n            #undef X\n            default: return \"UNKNOWN_ERROR\";\n        }\n    }\n}\n\n// Log level constants\nnamespace LogLevelValue {\n    constexpr LogLevel ERROR{0};\n    constexpr LogLevel WARN{1};\n    constexpr LogLevel INFO{2};\n    constexpr LogLevel DEBUG{3};\n    constexpr LogLevel TRACE{4};\n    constexpr LogLevel PANIC{101};\n}\n\n// Index type constants\nnamespace IndexTypeValue {\n    constexpr IndexType BTREE{0};\n    constexpr IndexType HASH{1};\n}\n\n// Helper functions\ninline bool is_ok(Status status) { \n    return status == StatusCode::OK; \n}\n\ninline bool is_error(Status status) { \n    return status != StatusCode::OK; \n}\n\n// Format status with both name and numeric code\ninline std::string format_status(Status status) {\n    return std::string(StatusCode::to_string(status)) + \" (\" + std::to_string(status.inner) + \")\";\n}\n\n// Enable std::hash for opaque types\n} // namespace SpacetimeDB\n\n// Specializations for std::hash\nnamespace std {\n    template<> struct hash<SpacetimeDB::TableId> {\n        size_t operator()(const SpacetimeDB::TableId& id) const {\n            return hash<uint32_t>{}(id.inner);\n        }\n    };\n    \n    template<> struct hash<SpacetimeDB::IndexId> {\n        size_t operator()(const SpacetimeDB::IndexId& id) const {\n            return hash<uint32_t>{}(id.inner);\n        }\n    };\n    \n    template<> struct hash<SpacetimeDB::RowIter> {\n        size_t operator()(const SpacetimeDB::RowIter& iter) const {\n            return hash<uint32_t>{}(iter.inner);\n        }\n    };\n    \n    template<> struct hash<SpacetimeDB::ConsoleTimerId> {\n        size_t operator()(const SpacetimeDB::ConsoleTimerId& id) const {\n            return hash<uint32_t>{}(id.inner);\n        }\n    };\n}\n\n#endif // SPACETIMEDB_OPAQUE_TYPES_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/auth_ctx.h",
    "content": "#ifndef SPACETIMEDB_AUTH_CTX_H\n#define SPACETIMEDB_AUTH_CTX_H\n\n#include \"spacetimedb/jwt_claims.h\"\n#include \"spacetimedb/bsatn/types.h\"\n#include \"spacetimedb/abi/FFI.h\"\n#include \"spacetimedb/abi/opaque_types.h\"\n#include <memory>\n#include <optional>\n#include <functional>\n#include <vector>\n#include <array>\n\nnamespace SpacetimeDB {\n\n// Forward declarations\nstruct ConnectionId;\n\n/**\n * @brief Authentication context for a reducer call.\n * \n * Provides access to the JWT claims for the connection that triggered the reducer,\n * if any. Reducers can be called from internal sources (scheduled reducers, init, etc.)\n * or from external connections (with potential JWT authentication).\n * \n * This class uses lazy loading - the JWT is only fetched and parsed when accessed.\n */\nclass AuthCtx {\nprivate:\n    bool is_internal_;\n    mutable std::shared_ptr<std::optional<JwtClaims>> jwt_;\n    std::function<std::optional<JwtClaims>()> jwt_loader_;\n\n    // Private constructor used by factory methods\n    AuthCtx(bool is_internal, std::function<std::optional<JwtClaims>()> loader);\n\npublic:\n    /**\n     * @brief Creates an AuthCtx from an optional ConnectionId.\n     * \n     * If the connection_id is present, creates an AuthCtx that will load the JWT.\n     * If the connection_id is absent, creates an internal AuthCtx.\n     * \n     * @param connection_id Optional connection ID\n     * @param sender The identity of the caller (already derived from JWT claims by the host)\n     * @return An AuthCtx based on the connection_id\n     */\n    static AuthCtx from_connection_id_opt(std::optional<ConnectionId> connection_id, Identity sender);\n\n    /**\n     * @brief Creates an AuthCtx for an internal (non-connection-based) reducer call.\n     * \n     * Internal calls include scheduled reducers, init reducers, and other\n     * database-initiated operations.\n     * \n     * @return An AuthCtx representing an internal call\n     */\n    static AuthCtx internal();\n\n    /**\n     * @brief Creates an AuthCtx from a JWT payload string.\n     * \n     * This is primarily used for testing purposes, allowing you to create\n     * an AuthCtx with specific JWT claims without needing a real connection.\n     * \n     * Note: The Identity must be computed by calling the host function,\n     * as we cannot compute Blake3 hashes in WASM.\n     * \n     * @param jwt_payload The raw JWT payload (JSON claims)\n     * @param identity The identity derived from the JWT's issuer and subject\n     * @return An AuthCtx with the provided JWT\n     */\n    static AuthCtx from_jwt_payload(std::string jwt_payload, Identity identity);\n\n    /**\n     * @brief Creates an AuthCtx that reads the JWT for the given connection ID.\n     * \n     * The JWT will be lazily loaded from the host when first accessed.\n     * The identity parameter is the sender's identity, already derived from\n     * JWT claims by the host (using Blake3 hashing).\n     * \n     * @param connection_id The connection ID to load the JWT for\n     * @param sender The identity of the caller (already derived from JWT claims by the host)\n     * @return An AuthCtx that will load the JWT on demand\n     */\n    static AuthCtx from_connection_id(ConnectionId connection_id, Identity sender);\n\n    /**\n     * @brief Returns whether this reducer was spawned from inside the database.\n     * \n     * @return true if this is an internal call (scheduled, init, etc.)\n     */\n    bool is_internal() const { return is_internal_; }\n\n    /**\n     * @brief Checks if there is a JWT without loading it.\n     * \n     * If is_internal() returns true, this will return false.\n     * \n     * @return true if a JWT is available\n     */\n    bool has_jwt() const;\n\n    /**\n     * @brief Gets the JWT claims, loading them if necessary.\n     * \n     * This will fetch the JWT from the host on the first call and cache it.\n     * \n     * @return An optional containing the JwtClaims if available\n     */\n    const std::optional<JwtClaims>& get_jwt() const;\n\n    /**\n     * @brief Gets the caller's identity.\n     * \n     * For internal calls, this returns the database's identity.\n     * For external calls, this returns the identity derived from the JWT\n     * (based on the issuer and subject claims).\n     * \n     * @return The caller's Identity\n     */\n    Identity get_caller_identity() const;\n};\n\n// ============================================================================\n// INLINE IMPLEMENTATIONS\n// ============================================================================\n\nconstexpr uint16_t ERROR_BUFFER_TOO_SMALL = 11;\n\ninline AuthCtx::AuthCtx(bool is_internal, std::function<std::optional<JwtClaims>()> loader)\n    : is_internal_(is_internal), jwt_loader_(std::move(loader)) {}\n\ninline AuthCtx AuthCtx::from_connection_id_opt(std::optional<ConnectionId> connection_id, Identity sender) {\n    if (connection_id.has_value()) {\n        return from_connection_id(*connection_id, std::move(sender));\n    } else {\n        return internal();\n    }\n}\n\ninline AuthCtx AuthCtx::internal() {\n    return AuthCtx(true, []() -> std::optional<JwtClaims> { return std::nullopt; });\n}\n\ninline AuthCtx AuthCtx::from_jwt_payload(std::string jwt_payload, Identity identity) {\n    return AuthCtx(false, [payload = std::move(jwt_payload), id = std::move(identity)]() mutable -> std::optional<JwtClaims> {\n        return JwtClaims(std::move(payload), std::move(id));\n    });\n}\n\ninline AuthCtx AuthCtx::from_connection_id(ConnectionId connection_id, Identity sender) {\n    return AuthCtx(false, [connection_id, sender]() -> std::optional<JwtClaims> {\n        // Call the host FFI to get the JWT\n        BytesSource jwt_source;\n        \n        // Convert ConnectionId to byte array (little-endian)\n        std::array<uint8_t, 16> conn_id_bytes;\n        for (int i = 0; i < 8; ++i) {\n            conn_id_bytes[i] = (connection_id.id.low >> (i * 8)) & 0xFF;\n        }\n        for (int i = 0; i < 8; ++i) {\n            conn_id_bytes[8 + i] = (connection_id.id.high >> (i * 8)) & 0xFF;\n        }\n        \n        Status status = FFI::get_jwt(conn_id_bytes.data(), &jwt_source);\n        if (status != Status(0) || jwt_source == BytesSource{0}) {\n            return std::nullopt;\n        }\n        \n        // Read the JWT payload from the BytesSource\n        std::vector<uint8_t> buffer;\n        buffer.resize(4096); // Start with 4KB buffer\n        \n        size_t buffer_len = buffer.size();\n        int16_t result = bytes_source_read(jwt_source, buffer.data(), &buffer_len);\n        \n        while (result == ERROR_BUFFER_TOO_SMALL) {\n            buffer.resize(buffer.size() * 2);\n            buffer_len = buffer.size();\n            result = bytes_source_read(jwt_source, buffer.data(), &buffer_len);\n        }\n        \n        if (result < 0) {\n            return std::nullopt;\n        }\n        \n        // Convert bytes to string\n        std::string jwt_payload(buffer.begin(), buffer.begin() + buffer_len);\n        \n        // Use the provided sender identity (already computed by host from JWT claims)\n        return JwtClaims(std::move(jwt_payload), sender);\n    });\n}\n\ninline bool AuthCtx::has_jwt() const {\n    if (is_internal_) {\n        return false;\n    }\n    \n    // Load the JWT if not already loaded, then check if it has a value\n    // This ensures has_jwt() and get_jwt() are consistent\n    return get_jwt().has_value();\n}\n\ninline const std::optional<JwtClaims>& AuthCtx::get_jwt() const {\n    if (!jwt_) {\n        jwt_ = std::make_shared<std::optional<JwtClaims>>(jwt_loader_());\n    }\n    return *jwt_;\n}\n\ninline Identity AuthCtx::get_caller_identity() const {\n    if (is_internal_) {\n        // Return database identity for internal calls\n        std::array<uint8_t, 32> identity_bytes;\n        FFI::identity(identity_bytes.data());\n        return Identity(identity_bytes);\n    }\n    \n    const auto& jwt = get_jwt();\n    if (jwt.has_value()) {\n        return jwt->get_identity();\n    }\n    \n    // No JWT, return database identity as fallback\n    std::array<uint8_t, 32> identity_bytes;\n    FFI::identity(identity_bytes.data());\n    return Identity(identity_bytes);\n}\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_AUTH_CTX_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/DEVELOP.md",
    "content": "# Core bsatn library\n\nThese files here are being used by both the Unreal C++ Client SDK and the Module Library.\nThese files are the main files. The SDK do not use these files directly becouse a user might move the SDK plugin to the unreal engine plugin direcory or the unreal project directory.\nAll changed made in one of the files should copied over to the SDK.\n\nSDK bsatn path: \"crates\\sdk-unreal\\SpacetimeDBSdk\\Source\\SpacetimeDBSdk\\Public\\BSATN\""
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/README.md",
    "content": "# BSATN C++ Library\n\nThis directory contains a self-contained C++ implementation of BSATN (Binary SpacetimeDB Algebraic Type Notation) serialization.\n\n## For C++ Client Usage\n\nThe BSATN library can be used independently for serialization/deserialization without any SpacetimeDB module dependencies. \n\n### What you need:\n- All headers in this directory\n- Standard C++ library (C++20)\n- No external dependencies\n\n### What you DON'T need:\n- `ITypeRegistrar.h` - This is only for modules, not clients\n- Type registration functionality\n- Any files from outside this directory\n\n### Basic usage:\n\n```cpp\n#include \"bsatn/bsatn.h\"\n\n// Define your struct\nstruct MyData {\n    uint32_t id;\n    std::string name;\n};\n\n// Define serialization traits\nSPACETIMEDB_STRUCT(MyData, id, name)\n\n// Serialize\nMyData data{42, \"example\"};\nstd::vector<uint8_t> buffer;\nSpacetimeDB::bsatn::Writer writer(buffer);\nSpacetimeDB::bsatn::serialize(writer, data);\n\n// Deserialize\nSpacetimeDB::bsatn::Reader reader(buffer);\nauto result = SpacetimeDB::bsatn::deserialize<MyData>(reader);\n```\n\n## Architecture Notes\n\n- **ITypeRegistrar.h**: Interface for optional type registration. Clients can ignore this - it's only used by SpacetimeDB modules. The interface is kept here to avoid circular dependencies while maintaining clean architecture.\n\n- **No external dependencies**: All files only include other BSATN headers or standard C++ library headers.\n\n- **Special types**: The library includes SpacetimeDB special types (Identity, ConnectionId, Timestamp, TimeDuration) that serialize with specific tags for compatibility.\n\n## File Structure\n\n- Core: `reader.h`, `writer.h`, `serialization.h`\n- Type system: `algebraic_type.h`, `traits.h`, `primitive_traits.h`\n- Special types: `types.h`, `timestamp.h`, `time_duration.h`, `special_types.h`\n- Utilities: `size_calculator.h`, `sum_type.h`\n- Module-only: `ITypeRegistrar.h` (can be ignored by clients)"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/algebraic_type.h",
    "content": "#ifndef SPACETIMEDB_BSATN_ALGEBRAIC_TYPE_H\n#define SPACETIMEDB_BSATN_ALGEBRAIC_TYPE_H\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <memory>\n#include <variant>\n#include <optional>\n#include <stdexcept>\n#include <type_traits>\n\nnamespace SpacetimeDB::bsatn {\n\n// Forward declarations\nclass AlgebraicType;\nstruct SumTypeSchema;\nstruct ProductType;\nstruct ProductTypeElement;\nstruct SumTypeVariant;\nstruct ArrayType;\n\n/**\n * Represents the tag for different algebraic types in SpacetimeDB's type system.\n * This mirrors the Rust/C# implementation for compatibility.\n */\nenum class AlgebraicTypeTag : uint8_t {\n    Ref = 0,      // Reference to another type\n    Sum = 1,      // Sum type (tagged union/enum)\n    Product = 2,  // Product type (struct/tuple)\n    Array = 3,    // Array type\n    String = 4,   // UTF-8 string\n    Bool = 5,     // Boolean\n    I8 = 6,       // Signed 8-bit integer\n    U8 = 7,       // Unsigned 8-bit integer\n    I16 = 8,      // Signed 16-bit integer\n    U16 = 9,      // Unsigned 16-bit integer\n    I32 = 10,     // Signed 32-bit integer\n    U32 = 11,     // Unsigned 32-bit integer\n    I64 = 12,     // Signed 64-bit integer\n    U64 = 13,     // Unsigned 64-bit integer\n    I128 = 14,    // Signed 128-bit integer\n    U128 = 15,    // Unsigned 128-bit integer\n    I256 = 16,    // Signed 256-bit integer\n    U256 = 17,    // Unsigned 256-bit integer\n    F32 = 18,     // 32-bit floating point\n    F64 = 19      // 64-bit floating point\n};\n\n// ============================================================================\n// HELPER TEMPLATE FOR DEEP COPYING UNIQUE_PTR MEMBERS\n// ============================================================================\n\n/**\n * RAII helper for deep copying unique_ptr members.\n * Eliminates duplicate copy constructor patterns.\n */\ntemplate<typename T>\nstd::unique_ptr<T> deep_copy_ptr(const std::unique_ptr<T>& ptr) {\n    return ptr ? ptr->copy() : nullptr;\n}\n\n// ============================================================================\n// TYPE ELEMENT STRUCTURES\n// ============================================================================\n\n/**\n * Represents an element in a ProductType.\n * Stores a complete AlgebraicType object to eliminate ambiguity.\n */\nstruct ProductTypeElement {\n    std::optional<std::string> name;\n    std::unique_ptr<AlgebraicType> algebraic_type;\n    \n    ProductTypeElement(std::optional<std::string> n, AlgebraicType type);\n    ProductTypeElement(const ProductTypeElement& other);\n    ProductTypeElement(ProductTypeElement&& other) = default;\n    ProductTypeElement& operator=(const ProductTypeElement& other);\n    ProductTypeElement& operator=(ProductTypeElement&& other) = default;\n};\n\n/**\n * Represents a variant in a SumType.\n * Stores a complete AlgebraicType object to eliminate ambiguity.\n */\nstruct SumTypeVariant {\n    std::string name;\n    std::unique_ptr<AlgebraicType> algebraic_type;\n    \n    SumTypeVariant(std::string n, AlgebraicType type);\n    SumTypeVariant(const SumTypeVariant& other);\n    SumTypeVariant(SumTypeVariant&& other) = default;\n    SumTypeVariant& operator=(const SumTypeVariant& other);\n    SumTypeVariant& operator=(SumTypeVariant&& other) = default;\n};\n\n/**\n * Represents a sum type (tagged union/enum).\n * Each variant has a name and can contain data.\n */\nstruct SumTypeSchema {\n    std::vector<SumTypeVariant> variants;\n    \n    explicit SumTypeSchema(std::vector<SumTypeVariant> v) : variants(std::move(v)) {}\n};\n\n/**\n * Represents a product type (struct/tuple).\n * Contains ordered elements (fields).\n */\nstruct ProductType {\n    std::vector<ProductTypeElement> elements;\n    \n    explicit ProductType(std::vector<ProductTypeElement> elems) \n        : elements(std::move(elems)) {}\n    \n    // Helper to create a product type for a C++ struct\n    template<typename T>\n    static ProductType make();\n};\n\n/**\n * Represents an array type.\n * Contains the complete type of elements in the array.\n */\nstruct ArrayType {\n    std::unique_ptr<AlgebraicType> element_type;\n    \n    explicit ArrayType(AlgebraicType elem_type);\n    ArrayType(const ArrayType& other);\n    ArrayType(ArrayType&& other) = default;\n    ArrayType& operator=(const ArrayType& other);\n    ArrayType& operator=(ArrayType&& other) = default;\n};\n\n// ============================================================================\n// MAIN ALGEBRAIC TYPE CLASS\n// ============================================================================\n\n/**\n * @brief The main algebraic type representation for SpacetimeDB's type system.\n * \n * AlgebraicType is a tagged union that represents all possible types in SpacetimeDB.\n * It supports both primitive types (integers, floats, strings, etc.) and composite\n * types (products/structs, sums/enums, arrays, and references).\n * \n * This type system is designed to be:\n * - Compatible with multiple languages (Rust, C#, C++)\n * - Serializable via BSATN (Binary Sparse Algebraic Type Notation)\n * - Type-safe with compile-time verification\n * \n * @example Creating primitive types:\n * @code\n * auto int_type = AlgebraicType::primitive<AlgebraicTypeTag::I32>();\n * auto string_type = AlgebraicType::primitive<AlgebraicTypeTag::String>();\n * @endcode\n * \n * @example Creating composite types:\n * @code\n * // Create an array type\n * auto array_type = AlgebraicType::Array(AlgebraicType::primitive<AlgebraicTypeTag::I32>());\n * @endcode\n */\nclass AlgebraicType {\npublic:\n    /**\n     * Internal data storage for type-specific information.\n     */\n    using DataType = std::variant<\n        uint32_t,                          // Ref - type reference\n        std::unique_ptr<SumTypeSchema>,    // Sum type\n        std::unique_ptr<ProductType>,      // Product type\n        std::unique_ptr<ArrayType>,        // Array type\n        std::monostate                     // Primitive types (no additional data)\n    >;\n\nprivate:\n    AlgebraicTypeTag tag_;\n    DataType data_;\n\npublic:\n    // Constructor accessible for copy operations and internal use\n    AlgebraicType(AlgebraicTypeTag tag, DataType data) : tag_(tag), data_(std::move(data)) {}\n    // -------------------------------------------------------------------------\n    // PRIMITIVE TYPE FACTORY (TEMPLATED - REPLACES 16 INDIVIDUAL METHODS)\n    // -------------------------------------------------------------------------\n    \n    /**\n     * Template factory for primitive types.\n     * Replaces all individual primitive factory methods (Bool(), I8(), etc.)\n     * \n     * @tparam Tag The AlgebraicTypeTag for the primitive type\n     * @return AlgebraicType instance for the specified primitive\n     * \n     * @example\n     * auto bool_type = AlgebraicType::primitive<AlgebraicTypeTag::Bool>();\n     * auto int_type = AlgebraicType::primitive<AlgebraicTypeTag::I32>();\n     */\n    template<AlgebraicTypeTag Tag>\n    static AlgebraicType primitive() {\n        static_assert(\n            static_cast<uint8_t>(Tag) >= static_cast<uint8_t>(AlgebraicTypeTag::String),\n            \"primitive<Tag>() can only be used for primitive types\"\n        );\n        return AlgebraicType(Tag, std::monostate{});\n    }\n    \n    // Convenience aliases for ALL primitives (required by other files)\n    static AlgebraicType Bool() { return primitive<AlgebraicTypeTag::Bool>(); }\n    static AlgebraicType I8() { return primitive<AlgebraicTypeTag::I8>(); }\n    static AlgebraicType U8() { return primitive<AlgebraicTypeTag::U8>(); }\n    static AlgebraicType I16() { return primitive<AlgebraicTypeTag::I16>(); }\n    static AlgebraicType U16() { return primitive<AlgebraicTypeTag::U16>(); }\n    static AlgebraicType I32() { return primitive<AlgebraicTypeTag::I32>(); }\n    static AlgebraicType U32() { return primitive<AlgebraicTypeTag::U32>(); }\n    static AlgebraicType I64() { return primitive<AlgebraicTypeTag::I64>(); }\n    static AlgebraicType U64() { return primitive<AlgebraicTypeTag::U64>(); }\n    static AlgebraicType I128() { return primitive<AlgebraicTypeTag::I128>(); }\n    static AlgebraicType U128() { return primitive<AlgebraicTypeTag::U128>(); }\n    static AlgebraicType I256() { return primitive<AlgebraicTypeTag::I256>(); }\n    static AlgebraicType U256() { return primitive<AlgebraicTypeTag::U256>(); }\n    static AlgebraicType F32() { return primitive<AlgebraicTypeTag::F32>(); }\n    static AlgebraicType F64() { return primitive<AlgebraicTypeTag::F64>(); }\n    static AlgebraicType String() { return primitive<AlgebraicTypeTag::String>(); }\n    \n    // -------------------------------------------------------------------------\n    // COMPOSITE TYPE FACTORIES\n    // -------------------------------------------------------------------------\n    \n    static AlgebraicType Ref(uint32_t type_id) {\n        return AlgebraicType(AlgebraicTypeTag::Ref, type_id);\n    }\n    \n    static AlgebraicType make_ref(uint32_t type_id) {\n        return Ref(type_id);\n    }\n    \n    static AlgebraicType make_product(std::unique_ptr<ProductType> product_type) {\n        return AlgebraicType(AlgebraicTypeTag::Product, std::move(product_type));\n    }\n    \n    static AlgebraicType make_sum(std::unique_ptr<SumTypeSchema> sum_type) {\n        return AlgebraicType(AlgebraicTypeTag::Sum, std::move(sum_type));\n    }\n    \n    static AlgebraicType Array(AlgebraicType elem_type) {\n        return AlgebraicType(AlgebraicTypeTag::Array, \n                           std::make_unique<ArrayType>(std::move(elem_type)));\n    }\n    \n    /**\n     * Creates a unit type (empty product).\n     * This represents std::monostate or Rust's () unit type.\n     */\n    static AlgebraicType Unit() {\n        return make_product(std::make_unique<ProductType>(std::vector<ProductTypeElement>{}));\n    }\n    \n    /**\n     * Creates an Option type with simplified logic.\n     * Represents a sum type with \"some\" and \"none\" variants.\n     */\n    static AlgebraicType Option(uint32_t some_type_ref) {\n        if (some_type_ref == 0xFFFFFFFF) {\n            return create_unit_option();\n        }\n        return create_typed_option(some_type_ref);\n    }\n    \n    static AlgebraicType Product(std::vector<std::pair<std::string, uint32_t>> fields) {\n        std::vector<ProductTypeElement> elements;\n        elements.reserve(fields.size());\n        for (auto& [name, type_ref] : fields) {\n            elements.emplace_back(std::move(name), Ref(type_ref));\n        }\n        return make_product(std::make_unique<ProductType>(std::move(elements)));\n    }\n    \n    // -------------------------------------------------------------------------\n    // ACCESSORS AND TYPE CHECKING\n    // -------------------------------------------------------------------------\n    \n    AlgebraicTypeTag tag() const { return tag_; }\n    const DataType& data() const { return data_; }\n    \n    /**\n     * Template-based type checking.\n     * Replaces all individual is_*() methods with a single template.\n     * \n     * @tparam Tag The AlgebraicTypeTag to check against\n     * @return true if this type has the specified tag\n     * \n     * @example\n     * if (type.is<AlgebraicTypeTag::I32>()) { ... }\n     * if (type.is<AlgebraicTypeTag::Array>()) { ... }\n     */\n    template<AlgebraicTypeTag Tag>\n    bool is() const { return tag_ == Tag; }\n    \n    // Convenience methods for most common checks\n    bool is_ref() const { return is<AlgebraicTypeTag::Ref>(); }\n    bool is_sum() const { return is<AlgebraicTypeTag::Sum>(); }\n    bool is_product() const { return is<AlgebraicTypeTag::Product>(); }\n    bool is_array() const { return is<AlgebraicTypeTag::Array>(); }\n    bool is_primitive() const { \n        return static_cast<uint8_t>(tag_) >= static_cast<uint8_t>(AlgebraicTypeTag::String);\n    }\n    \n    // -------------------------------------------------------------------------\n    // DATA ACCESSOR METHODS\n    // -------------------------------------------------------------------------\n    \n    uint32_t as_ref() const {\n        if (!is_ref()) std::abort(); // Type is not a Ref\n        return std::get<uint32_t>(data_);\n    }\n    \n    const SumTypeSchema& as_sum() const {\n        if (!is_sum()) std::abort(); // Type is not a Sum\n        return *std::get<std::unique_ptr<SumTypeSchema>>(data_);\n    }\n    \n    const ProductType& as_product() const {\n        if (!is_product()) std::abort(); // Type is not a Product\n        return *std::get<std::unique_ptr<ProductType>>(data_);\n    }\n    \n    const ArrayType& as_array() const {\n        if (!is_array()) std::abort(); // Type is not an Array\n        return *std::get<std::unique_ptr<ArrayType>>(data_);\n    }\n    \n    // -------------------------------------------------------------------------\n    // COPY METHOD (SIMPLIFIED WITH VISITOR PATTERN)\n    // -------------------------------------------------------------------------\n    \n    std::unique_ptr<AlgebraicType> copy() const {\n        return std::visit([this](const auto& data) -> std::unique_ptr<AlgebraicType> {\n            using DataT = std::decay_t<decltype(data)>;\n            \n            if constexpr (std::is_same_v<DataT, std::monostate>) {\n                // Primitive types\n                return std::make_unique<AlgebraicType>(tag_, std::monostate{});\n            } else if constexpr (std::is_same_v<DataT, uint32_t>) {\n                // Ref types\n                return std::make_unique<AlgebraicType>(Ref(data));\n            } else if constexpr (std::is_same_v<DataT, std::unique_ptr<SumTypeSchema>>) {\n                // Sum types\n                std::vector<SumTypeVariant> new_variants;\n                new_variants.reserve(data->variants.size());\n                for (const auto& variant : data->variants) {\n                    new_variants.push_back(variant);  // Uses copy constructor\n                }\n                return std::make_unique<AlgebraicType>(AlgebraicType(\n                    AlgebraicTypeTag::Sum,\n                    std::make_unique<SumTypeSchema>(std::move(new_variants))\n                ));\n            } else if constexpr (std::is_same_v<DataT, std::unique_ptr<ProductType>>) {\n                // Product types\n                std::vector<ProductTypeElement> new_elements;\n                new_elements.reserve(data->elements.size());\n                for (const auto& elem : data->elements) {\n                    new_elements.push_back(elem);  // Uses copy constructor\n                }\n                return std::make_unique<AlgebraicType>(AlgebraicType(\n                    AlgebraicTypeTag::Product,\n                    std::make_unique<ProductType>(std::move(new_elements))\n                ));\n            } else if constexpr (std::is_same_v<DataT, std::unique_ptr<ArrayType>>) {\n                // Array types\n                return std::make_unique<AlgebraicType>(AlgebraicType(\n                    AlgebraicTypeTag::Array,\n                    std::make_unique<ArrayType>(*data)  // Uses copy constructor\n                ));\n            }\n        }, data_);\n    }\n\nprivate:\n    // Helper methods for Option factory\n    static AlgebraicType create_unit_option() {\n        std::vector<SumTypeVariant> variants;\n        variants.emplace_back(\"some\", Unit());\n        variants.emplace_back(\"none\", Unit());\n        return make_sum(std::make_unique<SumTypeSchema>(std::move(variants)));\n    }\n    \n    static AlgebraicType create_typed_option(uint32_t some_type_ref) {\n        std::vector<SumTypeVariant> variants;\n        variants.emplace_back(\"some\", Ref(some_type_ref));\n        variants.emplace_back(\"none\", Unit());\n        return make_sum(std::make_unique<SumTypeSchema>(std::move(variants)));\n    }\n};\n\n// ============================================================================\n// ALGEBRAIC TYPE TRAITS\n// ============================================================================\n\n/**\n * Trait for getting the AlgebraicType of a C++ type.\n * Specialized for primitive and container types.\n * \n * @example\n * auto type = algebraic_type_of<int32_t>::get();\n * auto array_type = algebraic_type_of<std::vector<int32_t>>::get();\n */\ntemplate<typename T>\nstruct algebraic_type_of {\n    static AlgebraicType get();\n};\n\n// Helper macro to reduce repetitive primitive type specializations\n#define SPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(cpp_type, tag_value) \\\n    template<> struct algebraic_type_of<cpp_type> { \\\n        static AlgebraicType get() { return AlgebraicType::primitive<AlgebraicTypeTag::tag_value>(); } \\\n    }\n\n// Primitive type specializations\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(bool, Bool);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(char, U8);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(int8_t, I8);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(int16_t, I16);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(int32_t, I32);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(int64_t, I64);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(uint8_t, U8);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(uint16_t, U16);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(uint32_t, U32);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(uint64_t, U64);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(float, F32);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(double, F64);\nSPACETIMEDB_DEFINE_ALGEBRAIC_TYPE(std::string, String);\n\n#undef SPACETIMEDB_DEFINE_ALGEBRAIC_TYPE\n\n// std::monostate represents a unit type (used in std::variant for empty variants)\ntemplate<>\nstruct algebraic_type_of<std::monostate> {\n    static AlgebraicType get() {\n        return AlgebraicType::Unit();\n    }\n};\n\n// Container type specializations (properly implemented, no TODOs)\ntemplate<typename T> \nstruct algebraic_type_of<std::vector<T>> {\n    static AlgebraicType get() {\n        return AlgebraicType::Array(algebraic_type_of<T>::get());\n    }\n};\n\ntemplate<typename T> \nstruct algebraic_type_of<std::optional<T>> {\n    static AlgebraicType get() {\n        // Create proper Option type with the inner type\n        AlgebraicType inner_type = algebraic_type_of<T>::get();\n        \n        std::vector<SumTypeVariant> variants;\n        variants.emplace_back(\"some\", std::move(inner_type));\n        variants.emplace_back(\"none\", AlgebraicType::Unit());\n        \n        return AlgebraicType::make_sum(std::make_unique<SumTypeSchema>(std::move(variants)));\n    }\n};\n\n// ============================================================================\n// INLINE IMPLEMENTATIONS\n// ============================================================================\n\n// ProductTypeElement implementations\ninline ProductTypeElement::ProductTypeElement(std::optional<std::string> n, AlgebraicType type)\n    : name(std::move(n)), algebraic_type(std::make_unique<AlgebraicType>(std::move(type))) {}\n\ninline ProductTypeElement::ProductTypeElement(const ProductTypeElement& other)\n    : name(other.name), algebraic_type(deep_copy_ptr(other.algebraic_type)) {}\n\ninline ProductTypeElement& ProductTypeElement::operator=(const ProductTypeElement& other) {\n    if (this != &other) {\n        name = other.name;\n        algebraic_type = deep_copy_ptr(other.algebraic_type);\n    }\n    return *this;\n}\n\n// SumTypeVariant implementations\ninline SumTypeVariant::SumTypeVariant(std::string n, AlgebraicType type)\n    : name(std::move(n)), algebraic_type(std::make_unique<AlgebraicType>(std::move(type))) {}\n\ninline SumTypeVariant::SumTypeVariant(const SumTypeVariant& other)\n    : name(other.name), algebraic_type(deep_copy_ptr(other.algebraic_type)) {}\n\ninline SumTypeVariant& SumTypeVariant::operator=(const SumTypeVariant& other) {\n    if (this != &other) {\n        name = other.name;\n        algebraic_type = deep_copy_ptr(other.algebraic_type);\n    }\n    return *this;\n}\n\n// ArrayType implementations\ninline ArrayType::ArrayType(AlgebraicType elem_type)\n    : element_type(std::make_unique<AlgebraicType>(std::move(elem_type))) {}\n\ninline ArrayType::ArrayType(const ArrayType& other)\n    : element_type(deep_copy_ptr(other.element_type)) {}\n\ninline ArrayType& ArrayType::operator=(const ArrayType& other) {\n    if (this != &other) {\n        element_type = deep_copy_ptr(other.element_type);\n    }\n    return *this;\n}\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_ALGEBRAIC_TYPE_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/bsatn.h",
    "content": "#ifndef SPACETIMEDB_BSATN_MAIN_H // Changed guard to avoid conflict if old bsatn.h was somehow included\n#define SPACETIMEDB_BSATN_MAIN_H\n\n/**\n * @file bsatn.h\n * @brief Main include file for the BSATN (Binary SpacetimeDB Abstract Type Notation) library components.\n * @details This file includes the core components for BSATN serialization and deserialization:\n *          - bsatn::Reader\n *          - bsatn::Writer\n *          - Placeholder types for 128-bit integers.\n *          - Generic bsatn::serialize and bsatn::deserialize<T> free function templates and their overloads/specializations.\n *          - Algebraic type system for type metadata\n *          - Serialization traits and interfaces\n *          - Type registry for managing types\n *          - Sum type support for discriminated unions\n */\n\n// Forward declarations\nnamespace SpacetimeDB {\n    namespace bsatn {\n        class Reader;\n        class Writer;\n    }\n}\n\n// Core includes\n#include \"reader.h\"              // Defines Reader for deserialization\n#include \"writer.h\"              // Defines Writer for serialization\n#include \"algebraic_type.h\"      // AlgebraicType system (matches Rust/C#)\n#include \"type_extensions.h\"     // Extended and special types (merged from special_types.h and extended_types.h) - must come before traits.h\n#include \"traits.h\"              // Serialization traits and interfaces (includes struct macros)\n#include \"primitive_traits.h\"    // Primitive type specializations (bool, int, float, string)\n#include \"serialization.h\"       // Main serialize/deserialize functions with C++20 concepts\n#include \"sum_type.h\"            // SumType and Option (renamed from Sum)\n#include \"size_calculator.h\"     // Size calculation without serialization\n#include \"types_impl.h\"          // Implementation of BSATN methods for SpacetimeDB types\n\n// BSATN (Binary SpacetimeDB Algebraic Type Notation) C++ Implementation\n//\n// This provides a complete serialization system compatible with Rust and C#:\n//\n// 🔹 Core Types:\n//   - AlgebraicType: Type metadata system\n//   - ProductType: Structs/tuples (renamed elements for consistency)\n//   - SumType<T...>: Discriminated unions (renamed from Sum)\n//   - Option<T>: Optional values\n//\n// 🔹 Usage:\n//   serialize(writer, value);           // Serialize any supported type\n//   auto obj = deserialize<T>(reader);  // Deserialize to specific type\n//   SPACETIMEDB_STRUCT(MyType, field1, field2);  // Enable struct serialization\n//\n// 🔹 Features:\n//   ✅ All primitive types (bool, integers, floats, string)\n//   ✅ Containers (vector, optional)\n//   ✅ User-defined structs via macro\n//   ✅ Sum types and discriminated unions\n//   ✅ Special SpacetimeDB types (Identity, Timestamp, etc.)\n//   ✅ Type registry for metadata\n//   ✅ Cross-language compatibility\n//\n// Note: Legacy SpacetimeDB::bsatn namespace is available for backward compatibility.\n\n#endif // SPACETIMEDB_BSATN_MAIN_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/primitive_traits.h",
    "content": "#ifndef SPACETIMEDB_BSATN_PRIMITIVE_TRAITS_H\n#define SPACETIMEDB_BSATN_PRIMITIVE_TRAITS_H\n\n/**\n * @file primitive_traits.h\n * @brief BSATN trait specializations for primitive types\n * \n * This file provides bsatn_traits specializations for all primitive types\n * supported by SpacetimeDB. These specializations delegate to the existing\n * serialize functions in writer.h and deserializer struct in reader.h.\n */\n\n#include \"traits.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include \"algebraic_type.h\"\n#include <string>\n#include <type_traits>\n\nnamespace SpacetimeDB::bsatn {\n\n// Forward declarations of serialize functions from writer.h\ninline void serialize(Writer& w, bool value);\ninline void serialize(Writer& w, uint8_t value);\ninline void serialize(Writer& w, uint16_t value);\ninline void serialize(Writer& w, uint32_t value);\ninline void serialize(Writer& w, uint64_t value);\ninline void serialize(Writer& w, int8_t value);\ninline void serialize(Writer& w, int16_t value);\ninline void serialize(Writer& w, int32_t value);\ninline void serialize(Writer& w, int64_t value);\ninline void serialize(Writer& w, float value);\ninline void serialize(Writer& w, double value);\ninline void serialize(Writer& w, const std::string& value);\n\n// =========================================================================\n// Boolean Type\n// =========================================================================\n\ntemplate<>\nstruct bsatn_traits<bool> {\n    static void serialize(Writer& writer, bool value) {\n        writer.write_bool(value);\n    }\n    \n    static bool deserialize(Reader& reader) {\n        return reader.read_bool();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::Bool();\n    }\n};\n\n// =========================================================================\n// Signed Integer Types\n// =========================================================================\n\ntemplate<>\nstruct bsatn_traits<int8_t> {\n    static void serialize(Writer& writer, int8_t value) {\n        writer.write_i8(value);\n    }\n    \n    static int8_t deserialize(Reader& reader) {\n        return reader.read_i8();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I8();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<int16_t> {\n    static void serialize(Writer& writer, int16_t value) {\n        writer.write_i16_le(value);\n    }\n    \n    static int16_t deserialize(Reader& reader) {\n        return reader.read_i16_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I16();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<int32_t> {\n    static void serialize(Writer& writer, int32_t value) {\n        writer.write_i32_le(value);\n    }\n    \n    static int32_t deserialize(Reader& reader) {\n        return reader.read_i32_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I32();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<int64_t> {\n    static void serialize(Writer& writer, int64_t value) {\n        writer.write_i64_le(value);\n    }\n    \n    static int64_t deserialize(Reader& reader) {\n        return reader.read_i64_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I64();\n    }\n};\n\n// =========================================================================\n// Unsigned Integer Types\n// =========================================================================\n\ntemplate<>\nstruct bsatn_traits<uint8_t> {\n    static void serialize(Writer& writer, uint8_t value) {\n        writer.write_u8(value);\n    }\n    \n    static uint8_t deserialize(Reader& reader) {\n        return reader.read_u8();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U8();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<uint16_t> {\n    static void serialize(Writer& writer, uint16_t value) {\n        writer.write_u16_le(value);\n    }\n    \n    static uint16_t deserialize(Reader& reader) {\n        return reader.read_u16_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U16();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<uint32_t> {\n    static void serialize(Writer& writer, uint32_t value) {\n        writer.write_u32_le(value);\n    }\n    \n    static uint32_t deserialize(Reader& reader) {\n        return reader.read_u32_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U32();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<uint64_t> {\n    static void serialize(Writer& writer, uint64_t value) {\n        writer.write_u64_le(value);\n    }\n    \n    static uint64_t deserialize(Reader& reader) {\n        return reader.read_u64_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U64();\n    }\n};\n\n// =========================================================================\n// Floating Point Types\n// =========================================================================\n\ntemplate<>\nstruct bsatn_traits<float> {\n    static void serialize(Writer& writer, float value) {\n        writer.write_f32_le(value);\n    }\n    \n    static float deserialize(Reader& reader) {\n        return reader.read_f32_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::F32();\n    }\n};\n\ntemplate<>\nstruct bsatn_traits<double> {\n    static void serialize(Writer& writer, double value) {\n        writer.write_f64_le(value);\n    }\n    \n    static double deserialize(Reader& reader) {\n        return reader.read_f64_le();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::F64();\n    }\n};\n\n// =========================================================================\n// String Type\n// =========================================================================\n\ntemplate<>\nstruct bsatn_traits<std::string> {\n    static void serialize(Writer& writer, const std::string& value) {\n        writer.write_string(value);\n    }\n    \n    static std::string deserialize(Reader& reader) {\n        return reader.read_string();\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::String();\n    }\n};\n\n// Note: Platform-specific type aliases (like 'int' vs 'int32_t') are handled\n// by the existing type system. On most platforms, 'int' is an alias for 'int32_t'\n// and 'unsigned int' is an alias for 'uint32_t', so they use the same specializations.\n\n// =========================================================================\n// Generic Enum Type Support\n// =========================================================================\n\n/**\n * Generic enum trait specialization that works for any enum type.\n * Enums are serialized as their underlying type (typically uint32_t).\n * \n * Usage:\n *   enum class MyEnum : uint8_t { Zero, One, Two };\n *   // Automatically supported for BSATN serialization\n */\ntemplate<typename T>\nrequires std::is_enum_v<T>\nstruct bsatn_traits<T> {\n    static void serialize(Writer& writer, const T& value) {\n        using underlying = std::underlying_type_t<T>;\n        // Delegate to the underlying type's serialization\n        bsatn_traits<underlying>::serialize(writer, static_cast<underlying>(value));\n    }\n    \n    static T deserialize(Reader& reader) {\n        using underlying = std::underlying_type_t<T>;\n        // Delegate to the underlying type's deserialization\n        return static_cast<T>(bsatn_traits<underlying>::deserialize(reader));\n    }\n    \n    static AlgebraicType algebraic_type() {\n        using underlying = std::underlying_type_t<T>;\n        return bsatn_traits<underlying>::algebraic_type();\n    }\n};\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_PRIMITIVE_TRAITS_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/reader.h",
    "content": "#ifndef SPACETIMEDB_BSATN_READER_H\n#define SPACETIMEDB_BSATN_READER_H\n\n#include <vector>\n#include <string>\n#include <cstdint>\n#include <stdexcept>\n#include <optional>\n#include <functional>\n#include <span>\n#include <type_traits>\n#include <cstring>\n#include <variant>\n// uint128_placeholder.h removed - types are in spacetimedb/types.h\n#include \"types.h\"\n\nnamespace SpacetimeDB::bsatn {\n\n    class Reader;\n    template<typename T> T deserialize(Reader& r);\n    template<typename T> struct bsatn_traits;\n\n    // Helper traits\n    template<typename> struct is_std_optional : std::false_type {};\n    template<typename T> struct is_std_optional<std::optional<T>> : std::true_type {};\n    template<typename T> constexpr bool is_std_optional_v = is_std_optional<T>::value;\n\n    template<typename> struct is_std_vector : std::false_type {};\n    template<typename T> struct is_std_vector<std::vector<T>> : std::true_type {};\n    template<typename T> constexpr bool is_std_vector_v = is_std_vector<T>::value;\n\n    class Reader {\n    public:\n        // Constructors - using uint8_t consistently\n        Reader(const uint8_t* data, size_t size) : current_ptr(data), end_ptr(data + size) {}\n        Reader(std::span<const uint8_t> data) : current_ptr(data.data()), end_ptr(data.data() + data.size()) {}\n        Reader(const std::vector<uint8_t>& data) : current_ptr(data.data()), end_ptr(data.data() + data.size()) {}\n        \n\n        // Template method for reading primitive types (reduces implementation duplication)\n        template<typename T>\n        T read_primitive_le() {\n            static_assert(std::is_arithmetic_v<T>, \"read_primitive_le only works with arithmetic types\");\n            check_available(sizeof(T));\n            T val;\n            std::memcpy(&val, current_ptr, sizeof(T));\n            advance(sizeof(T));\n            return val;\n        }\n\n        // Public API methods (delegates to template where possible)\n        inline bool read_bool() {\n            check_available(1);\n            uint8_t val = *current_ptr;\n            advance(1);\n            if (val > 1) {\n                std::abort(); // Invalid bool value in BSATN deserialization\n            }\n            return val != 0;\n        }\n        inline uint8_t read_u8() {\n            check_available(1);\n            uint8_t val = *current_ptr;\n            advance(1);\n            return val;\n        }\n        uint16_t read_u16_le() { return read_primitive_le<uint16_t>(); }\n        uint32_t read_u32_le() { return read_primitive_le<uint32_t>(); }\n        uint64_t read_u64_le() { return read_primitive_le<uint64_t>(); }\n        inline SpacetimeDB::u128 read_u128_le() {\n            uint64_t low = read_u64_le();\n            uint64_t high = read_u64_le();\n            return SpacetimeDB::u128(high, low);\n        }\n        inline SpacetimeDB::u256_placeholder read_u256_le() {\n            check_available(32);\n            SpacetimeDB::u256_placeholder val;\n            std::memcpy(val.data.data(), current_ptr, 32);\n            advance(32);\n            return val;\n        }\n\n        int8_t read_i8() { return static_cast<int8_t>(read_u8()); }\n        int16_t read_i16_le() { return static_cast<int16_t>(read_u16_le()); }\n        int32_t read_i32_le() { return static_cast<int32_t>(read_u32_le()); }\n        int64_t read_i64_le() { return static_cast<int64_t>(read_u64_le()); }\n        inline SpacetimeDB::i128 read_i128_le() {\n            uint64_t low = read_u64_le();\n            int64_t high = static_cast<int64_t>(read_u64_le());\n            return SpacetimeDB::i128(high, low);\n        }\n        inline SpacetimeDB::i256_placeholder read_i256_le() {\n            check_available(32);\n            SpacetimeDB::i256_placeholder val;\n            std::memcpy(val.data.data(), current_ptr, 32);\n            advance(32);\n            return val;\n        }\n\n        float read_f32_le() { return read_primitive_le<float>(); }\n        double read_f64_le() { return read_primitive_le<double>(); }\n\n        inline std::string read_string() {\n            uint32_t len = read_u32_le();\n            check_available(len);\n            std::string result(reinterpret_cast<const char*>(current_ptr), len);\n            advance(len);\n            return result;\n        }\n        inline std::vector<uint8_t> read_bytes() {\n            uint32_t len = read_u32_le();\n            check_available(len);\n            std::vector<uint8_t> result(current_ptr, current_ptr + len);\n            advance(len);\n            return result;\n        }\n        inline std::vector<uint8_t> read_fixed_bytes(size_t count) {\n            check_available(count);\n            std::vector<uint8_t> result(current_ptr, current_ptr + count);\n            advance(count);\n            return result;\n        }\n\n        template<typename T>\n        std::optional<T> read_optional() {\n            uint8_t tag = read_u8();\n            if (tag == 0) {\n                return std::nullopt;\n            } else if (tag == 1) {\n                return SpacetimeDB::bsatn::deserialize<T>(*this);\n            } else {\n                std::abort(); // Invalid optional tag in BSATN deserialization\n            }\n        }\n\n        template<typename T>\n        std::vector<T> read_vector() {\n            uint32_t size = read_u32_le();\n            std::vector<T> result;\n            result.reserve(size);\n            for (uint32_t i = 0; i < size; ++i) {\n                result.push_back(SpacetimeDB::bsatn::deserialize<T>(*this));\n            }\n            return result;\n        }\n\n        inline std::vector<uint8_t> read_vector_byte() {\n            return read_bytes();\n        }\n\n        // Deserialize a type using C++20 concepts for better error messages\n        template<typename T>\n            requires requires(Reader& r) { SpacetimeDB::bsatn::deserialize<T>(r); }\n        T deserialize_type() {\n            return SpacetimeDB::bsatn::deserialize<T>(*this);\n        }\n\n        inline bool is_eos() const {\n            return current_ptr >= end_ptr;\n        }\n        inline size_t remaining_bytes() const {\n            return (current_ptr <= end_ptr) ? (end_ptr - current_ptr) : 0;\n        }\n\n    private:\n        inline void check_available(size_t num_bytes) const {\n            if (current_ptr + num_bytes > end_ptr) {\n                std::abort(); // BSATN Reader: Not enough bytes remaining\n            }\n        }\n        inline void advance(size_t num_bytes) {\n            current_ptr += num_bytes;\n        }\n\n        const uint8_t* current_ptr;\n        const uint8_t* end_ptr;\n    };\n\n    // Type trait for deserializing types - primary template\n    template<typename T, typename = void>\n    struct deserializer {\n        static T deserialize(Reader& r) {\n            // Default: try bsatn_traits\n            return bsatn_traits<T>::deserialize(r);\n        }\n    };\n    \n    // Specializations for primitive types\n    template<> struct deserializer<bool> {\n        static bool deserialize(Reader& r) { return r.read_bool(); }\n    };\n    template<> struct deserializer<uint8_t> {\n        static uint8_t deserialize(Reader& r) { return r.read_u8(); }\n    };\n    template<> struct deserializer<uint16_t> {\n        static uint16_t deserialize(Reader& r) { return r.read_u16_le(); }\n    };\n    template<> struct deserializer<uint32_t> {\n        static uint32_t deserialize(Reader& r) { return r.read_u32_le(); }\n    };\n    template<> struct deserializer<uint64_t> {\n        static uint64_t deserialize(Reader& r) { return r.read_u64_le(); }\n    };\n    template<> struct deserializer<int8_t> {\n        static int8_t deserialize(Reader& r) { return r.read_i8(); }\n    };\n    template<> struct deserializer<int16_t> {\n        static int16_t deserialize(Reader& r) { return r.read_i16_le(); }\n    };\n    template<> struct deserializer<int32_t> {\n        static int32_t deserialize(Reader& r) { return r.read_i32_le(); }\n    };\n    template<> struct deserializer<int64_t> {\n        static int64_t deserialize(Reader& r) { return r.read_i64_le(); }\n    };\n    template<> struct deserializer<float> {\n        static float deserialize(Reader& r) { return r.read_f32_le(); }\n    };\n    template<> struct deserializer<double> {\n        static double deserialize(Reader& r) { return r.read_f64_le(); }\n    };\n    template<> struct deserializer<std::string> {\n        static std::string deserialize(Reader& r) { return r.read_string(); }\n    };\n    template<> struct deserializer<std::vector<uint8_t>> {\n        static std::vector<uint8_t> deserialize(Reader& r) { return r.read_bytes(); }\n    };\n    \n    // Specializations for container types\n    template<typename T>\n    struct deserializer<std::optional<T>> {\n        static std::optional<T> deserialize(Reader& r) {\n            return r.read_optional<T>();\n        }\n    };\n    \n    template<typename T>\n    struct deserializer<std::vector<T>, std::enable_if_t<!std::is_same_v<T, uint8_t>>> {\n        static std::vector<T> deserialize(Reader& r) {\n            return r.read_vector<T>();\n        }\n    };\n    \n    // Specializations for SpacetimeDB types\n    template<> struct deserializer<SpacetimeDB::Identity> {\n        static SpacetimeDB::Identity deserialize(Reader& r) {\n            SpacetimeDB::Identity id;\n            id.bsatn_deserialize(r);\n            return id;\n        }\n    };\n    template<> struct deserializer<SpacetimeDB::ConnectionId> {\n        static SpacetimeDB::ConnectionId deserialize(Reader& r) {\n            SpacetimeDB::ConnectionId conn;\n            conn.bsatn_deserialize(r);\n            return conn;\n        }\n    };\n    \n    // Generic deserialize function - now much cleaner!\n    template<typename T>\n    inline T deserialize(Reader& r) {\n        return deserializer<T>::deserialize(r);\n    }\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_READER_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/result.h",
    "content": "#pragma once\n\n/**\n * @file result.h\n * @brief Result<T, E> type for SpacetimeDB C++ bindings\n * \n * This header provides a Result type that represents either success (ok) or failure (err).\n * It is a structural sum type compatible with SpacetimeDB's type system.\n */\n\n#include <variant>\n#include <optional>\n#include <string>\n#include <cstdlib>\n#include \"reader.h\"\n#include \"writer.h\"\n\nnamespace SpacetimeDB {\n\n/**\n * @brief A Result type that represents either success (ok) or failure (err).\n * \n * This is a structural sum type compatible with SpacetimeDB's type system.\n * It uses lowercase variant names (\"ok\" and \"err\") to match the Rust/C# implementations.\n * \n * The internal representation uses std::variant<T, E> where:\n * - index 0 (holding T) = BSATN tag 0 = \"ok\" variant\n * - index 1 (holding E) = BSATN tag 1 = \"err\" variant\n * \n * @tparam T The type of the success value\n * @tparam E The type of the error value\n */\ntemplate<typename T, typename E>\nclass Result {\nprivate:\n    // Tag 0 = ok (index 0), Tag 1 = err (index 1)\n    std::variant<T, E> value_;\n    \npublic:\n    // ======================================================================\n    // Constructors and Factory Methods\n    // ======================================================================\n    \n    /**\n     * @brief Default constructor - creates an ok Result with default-constructed value.\n     * This is required for use in table structs where SPACETIMEDB_STRUCT needs\n     * to create temporary instances. Only available when T is default-constructible.\n     */\n    Result() : value_(std::in_place_index<0>) {}\n    \n    /**\n     * @brief Create a successful Result containing a value.\n     * @param value The success value (moved into the Result)\n     * @return A Result in the ok state\n     */\n    static Result ok(T value) {\n        Result r;\n        r.value_ = std::move(value);\n        return r;\n    }\n    \n    /**\n     * @brief Create a failed Result containing an error.\n     * @param error The error value (moved into the Result)\n     * @return A Result in the err state\n     */\n    static Result err(E error) {\n        Result r;\n        r.value_ = std::move(error);\n        return r;\n    }\n    \n    // ======================================================================\n    // State Checking\n    // ======================================================================\n    \n    /**\n     * @brief Check if this Result contains a success value.\n     * @return true if this is ok, false if err\n     */\n    bool is_ok() const {\n        return value_.index() == 0;\n    }\n    \n    /**\n     * @brief Check if this Result contains an error value.\n     * @return true if this is err, false if ok\n     */\n    bool is_err() const {\n        return value_.index() == 1;\n    }\n    \n    // ======================================================================\n    // Value Accessors (with panic on wrong variant)\n    // ======================================================================\n    \n    /**\n     * @brief Get the success value, aborting if this is err.\n     * @return Reference to the success value\n     * @note Calls std::abort() if this Result is err\n     */\n    const T& unwrap() const {\n        if (is_err()) {\n            std::abort(); // Called unwrap() on an err Result\n        }\n        return std::get<0>(value_);\n    }\n    \n    T& unwrap() {\n        if (is_err()) {\n            std::abort(); // Called unwrap() on an err Result\n        }\n        return std::get<0>(value_);\n    }\n    \n    /**\n     * @brief Get the error value, aborting if this is ok.\n     * @return Reference to the error value\n     * @note Calls std::abort() if this Result is ok\n     */\n    const E& unwrap_err() const {\n        if (is_ok()) {\n            std::abort(); // Called unwrap_err() on an ok Result\n        }\n        return std::get<1>(value_);\n    }\n    \n    E& unwrap_err() {\n        if (is_ok()) {\n            std::abort(); // Called unwrap_err() on an ok Result\n        }\n        return std::get<1>(value_);\n    }\n    \n    // ======================================================================\n    // Safe Value Accessors (returning optional)\n    // ======================================================================\n    \n    /**\n     * @brief Get the success value if ok, otherwise nullopt.\n     * @return Optional containing the success value, or nullopt if err\n     */\n    std::optional<T> ok_value() const {\n        if (is_ok()) {\n            return std::get<0>(value_);\n        }\n        return std::nullopt;\n    }\n    \n    /**\n     * @brief Get the error value if err, otherwise nullopt.\n     * @return Optional containing the error value, or nullopt if ok\n     */\n    std::optional<E> err_value() const {\n        if (is_err()) {\n            return std::get<1>(value_);\n        }\n        return std::nullopt;\n    }\n    \n    // ======================================================================\n    // Transformations\n    // ======================================================================\n    \n    /**\n     * @brief Get the success value or a default value.\n     * @param default_value The value to return if this is err\n     * @return The success value if ok, otherwise default_value\n     */\n    T unwrap_or(T default_value) const {\n        return is_ok() ? std::get<0>(value_) : default_value;\n    }\n    \n    // ======================================================================\n    // BSATN Serialization\n    // ======================================================================\n    \n    void bsatn_serialize(bsatn::Writer& writer) const {\n        // Write variant tag (0 = ok, 1 = err) - directly from variant index\n        writer.write_u8(static_cast<uint8_t>(value_.index()));\n        \n        // Write variant data\n        if (is_ok()) {\n            bsatn::bsatn_traits<T>::serialize(writer, std::get<0>(value_));\n        } else {\n            bsatn::bsatn_traits<E>::serialize(writer, std::get<1>(value_));\n        }\n    }\n    \n    static Result bsatn_deserialize(bsatn::Reader& reader) {\n        uint8_t tag = reader.read_u8();\n        \n        if (tag == 0) {\n            // ok variant (tag 0 = variant index 0)\n            T value = bsatn::bsatn_traits<T>::deserialize(reader);\n            return Result::ok(std::move(value));\n        } else if (tag == 1) {\n            // err variant (tag 1 = variant index 1)\n            E error = bsatn::bsatn_traits<E>::deserialize(reader);\n            return Result::err(std::move(error));\n        } else {\n            std::abort(); // Invalid Result variant tag\n        }\n    }\n};\n\n} // namespace SpacetimeDB\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/schedule_at.h",
    "content": "#ifndef SPACETIMEDB_BSATN_SCHEDULE_AT_H\n#define SPACETIMEDB_BSATN_SCHEDULE_AT_H\n\n#include \"timestamp.h\"\n#include \"time_duration.h\"\n#include <stdexcept>\n\nnamespace SpacetimeDB {\n\n/**\n * ScheduleAt represents when a scheduled reducer should execute.\n * This is a sum type with two variants:\n * - Interval(TimeDuration): Execute at regular intervals\n * - Time(Timestamp): Execute at a specific time\n * \n * Special type matching Rust's ScheduleAt enum.\n * This enables scheduled reducers functionality in SpacetimeDB.\n */\nclass ScheduleAt {\npublic:\n    enum class Variant : uint8_t {\n        Interval = 0,  // Regular interval (TimeDuration)\n        Time = 1       // Specific time (Timestamp)\n    };\n    \nprivate:\n    Variant variant;\n    union {\n        TimeDuration interval_value;\n        Timestamp time_value;\n    };\n    \npublic:\n    // Constructors\n    ScheduleAt() : variant(Variant::Interval), interval_value(TimeDuration::from_seconds(0)) {}\n    \n    ScheduleAt(const TimeDuration& dur) \n        : variant(Variant::Interval), interval_value(dur) {}\n    \n    ScheduleAt(const Timestamp& ts) \n        : variant(Variant::Time), time_value(ts) {}\n    \n    // Copy constructor\n    ScheduleAt(const ScheduleAt& other) : variant(other.variant) {\n        switch (variant) {\n            case Variant::Interval:\n                new (&interval_value) TimeDuration(other.interval_value);\n                break;\n            case Variant::Time:\n                new (&time_value) Timestamp(other.time_value);\n                break;\n        }\n    }\n    \n    // Assignment operator\n    ScheduleAt& operator=(const ScheduleAt& other) {\n        if (this != &other) {\n            this->~ScheduleAt(); // Destroy current value\n            variant = other.variant;\n            switch (variant) {\n                case Variant::Interval:\n                    new (&interval_value) TimeDuration(other.interval_value);\n                    break;\n                case Variant::Time:\n                    new (&time_value) Timestamp(other.time_value);\n                    break;\n            }\n        }\n        return *this;\n    }\n    \n    // Move constructor\n    ScheduleAt(ScheduleAt&& other) noexcept : variant(other.variant) {\n        switch (variant) {\n            case Variant::Interval:\n                new (&interval_value) TimeDuration(std::move(other.interval_value));\n                break;\n            case Variant::Time:\n                new (&time_value) Timestamp(std::move(other.time_value));\n                break;\n        }\n    }\n    \n    // Move assignment\n    ScheduleAt& operator=(ScheduleAt&& other) noexcept {\n        if (this != &other) {\n            this->~ScheduleAt();\n            variant = other.variant;\n            switch (variant) {\n                case Variant::Interval:\n                    new (&interval_value) TimeDuration(std::move(other.interval_value));\n                    break;\n                case Variant::Time:\n                    new (&time_value) Timestamp(std::move(other.time_value));\n                    break;\n            }\n        }\n        return *this;\n    }\n    \n    // Destructor\n    ~ScheduleAt() {\n        switch (variant) {\n            case Variant::Interval:\n                interval_value.~TimeDuration();\n                break;\n            case Variant::Time:\n                time_value.~Timestamp();\n                break;\n        }\n    }\n    \n    // Accessors\n    Variant get_variant() const { return variant; }\n    bool is_interval() const { return variant == Variant::Interval; }\n    bool is_time() const { return variant == Variant::Time; }\n    \n    const TimeDuration& get_interval() const {\n        if (variant != Variant::Interval) {\n            static TimeDuration default_duration;\n            return default_duration;\n        }\n        return interval_value;\n    }\n    \n    const Timestamp& get_time() const {\n        if (variant != Variant::Time) {\n            static Timestamp default_timestamp;\n            return default_timestamp;\n        }\n        return time_value;\n    }\n    \n    // Static factory methods\n    static ScheduleAt interval(const TimeDuration& dur) {\n        return ScheduleAt(dur);\n    }\n    \n    static ScheduleAt time(const Timestamp& ts) {\n        return ScheduleAt(ts);\n    }\n    \n    // Comparison operators\n    bool operator==(const ScheduleAt& other) const {\n        if (variant != other.variant) return false;\n        switch (variant) {\n            case Variant::Interval:\n                return interval_value == other.interval_value;\n            case Variant::Time:\n                return time_value == other.time_value;\n        }\n        return false;\n    }\n    \n    bool operator!=(const ScheduleAt& other) const {\n        return !(*this == other);\n    }\n    \n    // BSATN serialization (implemented below)\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n    void bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader);\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_BSATN_SCHEDULE_AT_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/schedule_at_impl.h",
    "content": "#ifndef SPACETIMEDB_BSATN_SCHEDULE_AT_IMPL_H\n#define SPACETIMEDB_BSATN_SCHEDULE_AT_IMPL_H\n\n#include \"schedule_at.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include \"traits.h\"\n#include \"type_extensions.h\"  // For special type constants\n\nnamespace SpacetimeDB {\n\n// ScheduleAt BSATN implementation\ninline void ScheduleAt::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    // Write variant tag (as u8)\n    writer.write_u8(static_cast<uint8_t>(variant));\n    \n    // Write payload based on variant\n    switch (variant) {\n        case Variant::Interval:\n            interval_value.bsatn_serialize(writer);\n            break;\n        case Variant::Time:\n            time_value.bsatn_serialize(writer);\n            break;\n    }\n}\n\ninline void ScheduleAt::bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader) {\n    // Read variant tag\n    uint8_t variant_tag = reader.read_u8();\n    \n    // Destroy current union content\n    this->~ScheduleAt();\n    \n    // Initialize based on variant\n    switch (variant_tag) {\n        case 0: // Interval\n            variant = Variant::Interval;\n            new (&interval_value) TimeDuration();\n            interval_value.bsatn_deserialize(reader);\n            break;\n        case 1: // Time\n            variant = Variant::Time;\n            new (&time_value) Timestamp();\n            time_value.bsatn_deserialize(reader);\n            break;\n        default:\n            // Default to Interval variant with 0 duration\n            variant = Variant::Interval;\n            new (&interval_value) TimeDuration();\n    }\n}\n\n} // namespace SpacetimeDB\n\nnamespace SpacetimeDB::bsatn {\n\n// Explicit specialization for ScheduleAt to handle BSATN serialization\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::ScheduleAt> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::ScheduleAt& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::ScheduleAt deserialize(Reader& reader) {\n        ::SpacetimeDB::ScheduleAt result; // Default constructor\n        result.bsatn_deserialize(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        // ScheduleAt is a special Sum type with Interval and Time variants\n        // Use the existing TimeDuration and Timestamp algebraic_type specializations\n        // to ensure proper inlining like the legacy path\n        \n        std::vector<SumTypeVariant> variants;\n        \n        // Interval variant: Use TimeDuration's algebraic_type() for proper inlining\n        AlgebraicType interval_type = bsatn_traits<::SpacetimeDB::TimeDuration>::algebraic_type();\n        variants.emplace_back(\"Interval\", std::move(interval_type));\n        \n        // Time variant: Use Timestamp's algebraic_type() for proper inlining\n        AlgebraicType time_type = bsatn_traits<::SpacetimeDB::Timestamp>::algebraic_type();\n        variants.emplace_back(\"Time\", std::move(time_type));\n        \n        auto sum_type = std::make_unique<SumTypeSchema>(std::move(variants));\n        return AlgebraicType::make_sum(std::move(sum_type));\n    }\n};\n\n} // namespace SpacetimeDB::bsatn\n\n// BSATN serializer template specialization for ScheduleAt\nnamespace SpacetimeDB {\n\n// ScheduleAt serialization - sum type with variant tag + payload\ntemplate<>\nstruct BsatnSerializer<ScheduleAt> {\n    static void serialize(std::vector<uint8_t>& buffer, const ScheduleAt& value) {\n        // Write variant tag\n        buffer.push_back(static_cast<uint8_t>(value.get_variant()));\n        \n        // Write payload based on variant\n        switch (value.get_variant()) {\n            case ScheduleAt::Variant::Interval:\n                BsatnSerializer<TimeDuration>::serialize(buffer, value.get_interval());\n                break;\n            case ScheduleAt::Variant::Time:\n                BsatnSerializer<Timestamp>::serialize(buffer, value.get_time());\n                break;\n        }\n    }\n    \n    static ScheduleAt deserialize(const uint8_t* data, size_t& offset) {\n        // Read variant tag\n        uint8_t variant_tag = data[offset++];\n        \n        switch (variant_tag) {\n            case 0: { // Interval\n                TimeDuration dur = BsatnSerializer<TimeDuration>::deserialize(data, offset);\n                return ScheduleAt::interval(dur);\n            }\n            case 1: { // Time\n                Timestamp ts = BsatnSerializer<Timestamp>::deserialize(data, offset);\n                return ScheduleAt::time(ts);\n            }\n            default:\n                return ScheduleAt::interval(TimeDuration::from_seconds(0));\n        }\n    }\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_BSATN_SCHEDULE_AT_IMPL_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/serialization.h",
    "content": "#ifndef SPACETIMEDB_BSATN_SERIALIZATION_H\n#define SPACETIMEDB_BSATN_SERIALIZATION_H\n\n/**\n * @file serialization.h\n * @brief Core serialization and deserialization functions for BSATN\n * \n * This header provides the main entry points for BSATN serialization\n * using C++20 concepts for better type safety and error messages.\n */\n\n#include \"traits.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include \"size_calculator.h\"\n#include <concepts>\n#include <type_traits>\n\nnamespace SpacetimeDB::bsatn {\n\n/**\n * @brief Concept for types that can be serialized to BSATN\n */\ntemplate<typename T>\nconcept Serializable = requires(Writer& w, const T& v) {\n    bsatn_traits<T>::serialize(w, v);\n};\n\n/**\n * @brief Concept for types that can be deserialized from BSATN\n */\ntemplate<typename T>\nconcept Deserializable = requires(Reader& r) {\n    { bsatn_traits<T>::deserialize(r) } -> std::same_as<T>;\n};\n\n/**\n * @brief Serialize a value to BSATN format\n * \n * @tparam T Type to serialize (must satisfy Serializable concept)\n * @param writer The writer to serialize to\n * @param value The value to serialize\n * \n * @example\n * @code\n * Writer writer;\n * MyStruct data{42, \"hello\"};\n * serialize(writer, data);\n * auto bytes = writer.take_buffer();\n * @endcode\n */\ntemplate<Serializable T>\ninline void serialize(Writer& writer, const T& value) {\n    bsatn_traits<T>::serialize(writer, value);\n}\n\n/**\n * @brief Deserialize a value from BSATN format\n * \n * @tparam T Type to deserialize (must satisfy Deserializable concept)\n * @param reader The reader to deserialize from\n * @return The deserialized value\n * \n * @example\n * @code\n * Reader reader(bytes);\n * auto data = deserialize<MyStruct>(reader);\n * @endcode\n */\ntemplate<Deserializable T>\ninline T deserialize(Reader& reader) {\n    return bsatn_traits<T>::deserialize(reader);\n}\n\n/**\n * @brief Serialize multiple values at once\n * \n * Uses C++20 parameter packs with concepts for type safety.\n * \n * @example\n * @code\n * Writer writer;\n * serialize_all(writer, 42, \"hello\", true, 3.14);\n * @endcode\n */\ntemplate<typename... Args>\n    requires (Serializable<Args> && ...)\ninline void serialize_all(Writer& writer, const Args&... args) {\n    (serialize(writer, args), ...);\n}\n\n/**\n * @brief Helper to serialize to a byte vector\n * \n * @tparam T Type to serialize\n * @param value The value to serialize\n * @return Vector of bytes containing serialized data\n */\ntemplate<Serializable T>\ninline std::vector<uint8_t> to_bytes(const T& value) {\n    Writer writer;\n    serialize(writer, value);\n    return writer.take_buffer();\n}\n\n/**\n * @brief Helper to deserialize from a byte vector\n * \n * @tparam T Type to deserialize\n * @param bytes The byte vector to deserialize from\n * @return The deserialized value\n */\ntemplate<Deserializable T>\ninline T from_bytes(const std::vector<uint8_t>& bytes) {\n    Reader reader(bytes);\n    return deserialize<T>(reader);\n}\n\n// Note: size_calculator.h must be included for these concepts to work\n// It defines has_static_size<T> and static_size_v<T>\n\n/**\n * @brief Concept for types that have a static size\n * \n * This requires that size_calculator.h has been included to provide\n * the has_static_size template.\n */\ntemplate<typename T>\nconcept HasStaticSize = requires {\n    typename std::enable_if_t<std::is_class_v<has_static_size<T>>>;\n    { has_static_size<T>::value } -> std::convertible_to<bool>;\n} && has_static_size<T>::value == true;\n\n/**\n * @brief Get the static BSATN size of a type at compile time\n * \n * @tparam T Type with static size\n * @return The size in bytes\n */\ntemplate<typename T>\n    requires HasStaticSize<T>\nconsteval size_t static_bsatn_size() {\n    return has_static_size<T>::value;\n}\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_SERIALIZATION_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/size_calculator.h",
    "content": "#ifndef SPACETIMEDB_BSATN_SIZE_CALCULATOR_H\n#define SPACETIMEDB_BSATN_SIZE_CALCULATOR_H\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <type_traits>\n#include <array>\n#include \"writer.h\"  // For Writer\n\nnamespace SpacetimeDB::bsatn {\n\n// Forward declaration\ntemplate<typename T> struct bsatn_traits;\n\n/**\n * A Writer-compatible class that only counts bytes without storing them.\n * Used for size calculation via bsatn_traits::serialize().\n */\nclass SizeWriter {\nprivate:\n    size_t size_ = 0;\n\npublic:\n    size_t size() const { return size_; }\n    \n    // Writer interface implementation\n    void write_u8(uint8_t) { size_ += 1; }\n    void write_u16_le(uint16_t) { size_ += 2; }\n    void write_u32_le(uint32_t) { size_ += 4; }\n    void write_u64_le(uint64_t) { size_ += 8; }\n    void write_u128_le(const std::array<uint8_t, 16>&) { size_ += 16; }\n    void write_u256_le(const std::array<uint8_t, 32>&) { size_ += 32; }\n    \n    void write_i8(int8_t) { size_ += 1; }\n    void write_i16_le(int16_t) { size_ += 2; }\n    void write_i32_le(int32_t) { size_ += 4; }\n    void write_i64_le(int64_t) { size_ += 8; }\n    void write_i128_le(const std::array<uint8_t, 16>&) { size_ += 16; }\n    void write_i256_le(const std::array<uint8_t, 32>&) { size_ += 32; }\n    \n    void write_f32_le(float) { size_ += 4; }\n    void write_f64_le(double) { size_ += 8; }\n    \n    void write_bool(bool) { size_ += 1; }\n    \n    void write_string(const std::string& s) {\n        size_ += 4 + s.length();  // Length prefix + data\n    }\n    \n    void write_bytes(const std::vector<uint8_t>& bytes) {\n        size_ += 4 + bytes.size();  // Length prefix + data\n    }\n    \n    void write_bytes(const void*, size_t len) {\n        size_ += 4 + len;  // Length prefix + data\n    }\n    \n    // No-op methods for size calculation\n    std::vector<uint8_t> take_buffer() { return {}; }\n    void clear() { size_ = 0; }\n};\n\n/**\n * Calculate the serialized size of values without actually serializing.\n * This matches Rust's CountWriter functionality.\n */\nclass SizeCalculator {\nprivate:\n    size_t size_ = 0;\n    \npublic:\n    size_t size() const { return size_; }\n    \n    void add_bool() { size_ += 1; }\n    void add_u8() { size_ += 1; }\n    void add_u16() { size_ += 2; }\n    void add_u32() { size_ += 4; }\n    void add_u64() { size_ += 8; }\n    void add_u128() { size_ += 16; }\n    void add_u256() { size_ += 32; }\n    \n    void add_i8() { size_ += 1; }\n    void add_i16() { size_ += 2; }\n    void add_i32() { size_ += 4; }\n    void add_i64() { size_ += 8; }\n    void add_i128() { size_ += 16; }\n    void add_i256() { size_ += 32; }\n    \n    void add_f32() { size_ += 4; }\n    void add_f64() { size_ += 8; }\n    \n    void add_string(const std::string& s) {\n        size_ += 4 + s.length();  // Length prefix + data\n    }\n    \n    void add_bytes(size_t len) {\n        size_ += 4 + len;  // Length prefix + data\n    }\n    \n    template<typename T>\n    void add_vector(const std::vector<T>& vec) {\n        size_ += 4;  // Length prefix\n        for (const auto& item : vec) {\n            add_value(item);\n        }\n    }\n    \n    template<typename T>\n    void add_optional(const std::optional<T>& opt) {\n        size_ += 1;  // Tag byte\n        if (opt.has_value()) {\n            add_value(*opt);\n        }\n    }\n    \n    // Generic value addition\n    template<typename T>\n    void add_value(const T& value);\n};\n\n/**\n * Trait to determine if a type has a static (compile-time known) size.\n * This matches Rust's static_bsatn_size functionality.\n */\ntemplate<typename T>\nstruct has_static_size : std::false_type {};\n\ntemplate<> struct has_static_size<bool> : std::true_type { static constexpr size_t value = 1; };\ntemplate<> struct has_static_size<uint8_t> : std::true_type { static constexpr size_t value = 1; };\ntemplate<> struct has_static_size<uint16_t> : std::true_type { static constexpr size_t value = 2; };\ntemplate<> struct has_static_size<uint32_t> : std::true_type { static constexpr size_t value = 4; };\ntemplate<> struct has_static_size<uint64_t> : std::true_type { static constexpr size_t value = 8; };\ntemplate<> struct has_static_size<int8_t> : std::true_type { static constexpr size_t value = 1; };\ntemplate<> struct has_static_size<int16_t> : std::true_type { static constexpr size_t value = 2; };\ntemplate<> struct has_static_size<int32_t> : std::true_type { static constexpr size_t value = 4; };\ntemplate<> struct has_static_size<int64_t> : std::true_type { static constexpr size_t value = 8; };\ntemplate<> struct has_static_size<float> : std::true_type { static constexpr size_t value = 4; };\ntemplate<> struct has_static_size<double> : std::true_type { static constexpr size_t value = 8; };\n\ntemplate<typename T>\ninline constexpr bool has_static_size_v = has_static_size<T>::value;\n\ntemplate<typename T>\ninline constexpr size_t static_size_v = has_static_size<T>::value;\n\n/**\n * Calculate the BSATN serialized size of a value.\n * Similar to Rust's to_len() function.\n */\ntemplate<typename T>\nsize_t bsatn_len(const T& value) {\n    if constexpr (has_static_size_v<T>) {\n        return static_size_v<T>;\n    } else {\n        SizeCalculator calc;\n        calc.add_value(value);\n        return calc.size();\n    }\n}\n\n/**\n * Optimized serialization that pre-allocates the exact size needed.\n * Similar to Rust's to_bsatn_vec().\n */\ntemplate<typename T>\nstd::vector<uint8_t> to_bsatn_vec(const T& value) {\n    size_t size = bsatn_len(value);\n    std::vector<uint8_t> result;\n    result.reserve(size);\n    \n    // Use existing Writer to serialize\n    Writer writer;\n    serialize(writer, value);\n    return writer.take_buffer();\n}\n\n/**\n * Extend an existing vector with BSATN data.\n * Similar to Rust's to_bsatn_extend().\n */\ntemplate<typename T>\nvoid to_bsatn_extend(std::vector<uint8_t>& vec, const T& value) {\n    size_t old_size = vec.size();\n    size_t add_size = bsatn_len(value);\n    vec.reserve(old_size + add_size);\n    \n    // Serialize directly into the vector\n    // (Would need custom Writer that appends to existing vector)\n    auto temp = to_bsatn_vec(value);\n    vec.insert(vec.end(), temp.begin(), temp.end());\n}\n\n/**\n * Type trait to check if a type is primitive (no padding).\n * Similar to Rust's IsPrimitiveType.\n */\ntemplate<typename T>\nstruct is_primitive_type : std::false_type {};\n\ntemplate<> struct is_primitive_type<bool> : std::true_type {};\ntemplate<> struct is_primitive_type<uint8_t> : std::true_type {};\ntemplate<> struct is_primitive_type<uint16_t> : std::true_type {};\ntemplate<> struct is_primitive_type<uint32_t> : std::true_type {};\ntemplate<> struct is_primitive_type<uint64_t> : std::true_type {};\ntemplate<> struct is_primitive_type<int8_t> : std::true_type {};\ntemplate<> struct is_primitive_type<int16_t> : std::true_type {};\ntemplate<> struct is_primitive_type<int32_t> : std::true_type {};\ntemplate<> struct is_primitive_type<int64_t> : std::true_type {};\ntemplate<> struct is_primitive_type<float> : std::true_type {};\ntemplate<> struct is_primitive_type<double> : std::true_type {};\n\ntemplate<typename T>\ninline constexpr bool is_primitive_type_v = is_primitive_type<T>::value;\n\n// Implementation of SizeCalculator::add_value\ntemplate<typename T>\nvoid SizeCalculator::add_value(const T& value) {\n    if constexpr (std::is_same_v<T, bool>) {\n        add_bool();\n    } else if constexpr (std::is_same_v<T, uint8_t>) {\n        add_u8();\n    } else if constexpr (std::is_same_v<T, uint16_t>) {\n        add_u16();\n    } else if constexpr (std::is_same_v<T, uint32_t>) {\n        add_u32();\n    } else if constexpr (std::is_same_v<T, uint64_t>) {\n        add_u64();\n    } else if constexpr (std::is_same_v<T, int8_t>) {\n        add_i8();\n    } else if constexpr (std::is_same_v<T, int16_t>) {\n        add_i16();\n    } else if constexpr (std::is_same_v<T, int32_t>) {\n        add_i32();\n    } else if constexpr (std::is_same_v<T, int64_t>) {\n        add_i64();\n    } else if constexpr (std::is_same_v<T, float>) {\n        add_f32();\n    } else if constexpr (std::is_same_v<T, double>) {\n        add_f64();\n    } else if constexpr (std::is_same_v<T, std::string>) {\n        add_string(value);\n    } else {\n        // For custom types, use a SizeWriter to calculate size via bsatn_traits\n        SizeWriter size_writer;\n        bsatn_traits<T>::serialize(size_writer, value);\n        size_ += size_writer.size();\n    }\n}\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_SIZE_CALCULATOR_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/sum_type.h",
    "content": "#ifndef SPACETIMEDB_BSATN_SUM_TYPE_H\n#define SPACETIMEDB_BSATN_SUM_TYPE_H\n\n#include <variant>\n#include <cstdint>\n#include <type_traits>\n#include \"traits.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include \"algebraic_type.h\"\n\nnamespace SpacetimeDB {\nnamespace bsatn {\n\n/**\n * @brief A sum type (tagged union) implementation for BSATN serialization\n * \n * This class wraps std::variant to provide a sum type that can be serialized\n * and deserialized using BSATN format. It's similar to Rust's enum types\n * with data payloads.\n * \n * @tparam Ts... The types that can be stored in this sum type\n */\ntemplate<typename... Ts>\nclass SumType {\npublic:\n    using VariantType = std::variant<Ts...>;\n    \nprivate:\n    VariantType value_;\n    \npublic:\n    // Constructors\n    SumType() = default;\n    \n    template<typename T>\n    SumType(T&& val) : value_(std::forward<T>(val)) {}\n    \n    // Assignment operators\n    template<typename T>\n    SumType& operator=(T&& val) {\n        value_ = std::forward<T>(val);\n        return *this;\n    }\n    \n    // Get the current tag (variant index)\n    uint8_t tag() const {\n        return static_cast<uint8_t>(value_.index());\n    }\n    \n    // Check if the sum type holds a specific type\n    template<typename T>\n    bool is() const {\n        return std::holds_alternative<T>(value_);\n    }\n    \n    // Get the value as a specific type (throws if wrong type)\n    template<typename T>\n    T& get() {\n        return std::get<T>(value_);\n    }\n    \n    template<typename T>\n    const T& get() const {\n        return std::get<T>(value_);\n    }\n    \n    // Get the value as a specific type (returns nullptr if wrong type)\n    template<typename T>\n    T* get_if() {\n        return std::get_if<T>(&value_);\n    }\n    \n    template<typename T>\n    const T* get_if() const {\n        return std::get_if<T>(&value_);\n    }\n    \n    // Visit the sum type with a visitor\n    template<typename Visitor>\n    auto visit(Visitor&& vis) {\n        return std::visit(std::forward<Visitor>(vis), value_);\n    }\n    \n    template<typename Visitor>\n    auto visit(Visitor&& vis) const {\n        return std::visit(std::forward<Visitor>(vis), value_);\n    }\n    \n    // Access the underlying variant\n    VariantType& variant() { return value_; }\n    const VariantType& variant() const { return value_; }\n};\n\n// Helper to serialize a variant value at a specific index\ntemplate<size_t I, typename... Ts>\nvoid serialize_variant_at_index(Writer& writer, const std::variant<Ts...>& var) {\n    if constexpr (I < sizeof...(Ts)) {\n        if (var.index() == I) {\n            serialize(writer, std::get<I>(var));\n        } else {\n            serialize_variant_at_index<I + 1>(writer, var);\n        }\n    }\n}\n\n// Helper to deserialize a variant value at a specific index  \ntemplate<size_t I, typename... Ts>\nvoid deserialize_variant_at_index(Reader& reader, std::variant<Ts...>& var, uint8_t tag) {\n    if constexpr (I < sizeof...(Ts)) {\n        if (tag == I) {\n            using T = std::variant_alternative_t<I, std::variant<Ts...>>;\n            var = deserialize<T>(reader);\n        } else {\n            deserialize_variant_at_index<I + 1>(reader, var, tag);\n        }\n    } else {\n        std::abort(); // Invalid sum type tag\n    }\n}\n\n// BSATN traits specialization for SumType\ntemplate<typename... Ts>\nstruct bsatn_traits<SumType<Ts...>> {\n    using sum_type = SumType<Ts...>;\n    \n    static AlgebraicType algebraic_type() {\n        // Reuse the canonical std::variant sum-shape implementation.\n        return bsatn_traits<std::variant<Ts...>>::algebraic_type();\n    }\n};\n\n// Serialization for SumType\ntemplate<typename... Ts>\nvoid serialize(Writer& writer, const SumType<Ts...>& value) {\n    // Write the tag byte\n    writer.write_u8(value.tag());\n    \n    // Write the variant data\n    serialize_variant_at_index<0>(writer, value.variant());\n}\n\n// Deserialization for SumType\ntemplate<typename... Ts>\nSumType<Ts...> deserialize(Reader& reader, std::type_identity<SumType<Ts...>>) {\n    // Read the tag byte\n    uint8_t tag = reader.read_u8();\n    \n    // Check tag is valid\n    if (tag >= sizeof...(Ts)) {\n        std::abort(); // Invalid sum type tag\n    }\n    \n    // Deserialize the appropriate variant\n    SumType<Ts...> result;\n    deserialize_variant_at_index<0>(reader, result.variant(), tag);\n    \n    return result;\n}\n\n} // namespace bsatn\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_BSATN_SUM_TYPE_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/time_duration.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <chrono>\n\n// Forward declarations for BSATN\nnamespace SpacetimeDB {\nnamespace bsatn {\n    class Writer;\n    class Reader;\n}\n}\n\nnamespace SpacetimeDB {\n\n/**\n * Represents a duration of time with microsecond precision.\n * This corresponds to SpacetimeDB's TimeDuration type.\n */\nclass TimeDuration {\nprivate:\n    int64_t micros_;\n\npublic:\n    // Constructors\n    explicit TimeDuration(int64_t micros = 0) : micros_(micros) {}\n    \n    // Factory methods\n    static TimeDuration from_micros(int64_t micros) { return TimeDuration(micros); }\n    static TimeDuration from_millis(int64_t millis) { return TimeDuration(millis * 1000); }\n    static TimeDuration from_seconds(int64_t seconds) { return TimeDuration(seconds * 1000000); }\n    static TimeDuration from_minutes(int64_t minutes) { return TimeDuration(minutes * 60000000); }\n    static TimeDuration from_hours(int64_t hours) { return TimeDuration(hours * 3600000000); }\n    \n    // Conversion from std::chrono\n    template<typename Rep, typename Period>\n    static TimeDuration from_chrono(std::chrono::duration<Rep, Period> duration) {\n        auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();\n        return TimeDuration(static_cast<int64_t>(micros));\n    }\n    \n    // Getters\n    int64_t micros() const { return micros_; }\n    int64_t millis() const { return micros_ / 1000; }\n    int64_t seconds() const { return micros_ / 1000000; }\n    \n    // Conversion to std::chrono\n    std::chrono::microseconds to_chrono() const {\n        return std::chrono::microseconds(micros_);\n    }\n    \n    // Arithmetic operations\n    TimeDuration operator+(const TimeDuration& other) const {\n        return TimeDuration(micros_ + other.micros_);\n    }\n    \n    TimeDuration operator-(const TimeDuration& other) const {\n        return TimeDuration(micros_ - other.micros_);\n    }\n    \n    TimeDuration operator*(int64_t scalar) const {\n        return TimeDuration(micros_ * scalar);\n    }\n    \n    TimeDuration operator/(int64_t scalar) const {\n        return TimeDuration(micros_ / scalar);\n    }\n    \n    // Comparison operators\n    bool operator==(const TimeDuration& other) const { return micros_ == other.micros_; }\n    bool operator!=(const TimeDuration& other) const { return micros_ != other.micros_; }\n    bool operator<(const TimeDuration& other) const { return micros_ < other.micros_; }\n    bool operator<=(const TimeDuration& other) const { return micros_ <= other.micros_; }\n    bool operator>(const TimeDuration& other) const { return micros_ > other.micros_; }\n    bool operator>=(const TimeDuration& other) const { return micros_ >= other.micros_; }\n    \n    // Absolute value\n    TimeDuration abs() const {\n        return TimeDuration(micros_ >= 0 ? micros_ : -micros_);\n    }\n    \n    // Convert to string representation (to match Rust TimeDuration::Display format exactly)\n    std::string to_string() const {\n        // Rust format: \"{sign}{seconds}.{microseconds:06}\"\n        // Always includes sign prefix: \"+\" for positive, \"-\" for negative\n        \n        int64_t micros = micros_;\n        const char* sign = (micros < 0) ? \"-\" : \"+\";\n        int64_t pos_micros = (micros < 0) ? -micros : micros;\n        \n        int64_t seconds = pos_micros / 1000000;\n        int64_t remaining_micros = pos_micros % 1000000;\n        \n        char buffer[32];\n        snprintf(buffer, sizeof(buffer), \"%s%lld.%06lld\", \n                 sign, (long long)seconds, (long long)remaining_micros);\n        \n        return std::string(buffer);\n    }\n    \n    // BSATN serialization (implemented in time_duration_bsatn.h)\n    void bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const;\n    static TimeDuration bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader);\n};\n\n// Convenience functions\ninline TimeDuration operator*(int64_t scalar, const TimeDuration& duration) {\n    return duration * scalar;\n}\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// BSATN Implementation\n// =============================================================================\n\n#include \"writer.h\"\n#include \"reader.h\"\n\nnamespace SpacetimeDB {\n\n// TimeDuration BSATN implementation\ninline void TimeDuration::bsatn_serialize(bsatn::Writer& writer) const {\n    writer.write_u64_le(static_cast<uint64_t>(micros_));\n}\n\ninline TimeDuration TimeDuration::bsatn_deserialize(bsatn::Reader& reader) {\n    uint64_t micros = reader.read_u64_le();\n    return TimeDuration(static_cast<int64_t>(micros));\n}\n\n} // namespace SpacetimeDB\n\n\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/timestamp.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <chrono>\n#include <ctime>\n#include <string>\n#include \"time_duration.h\"\n\n// Forward declarations\nnamespace SpacetimeDB {\nnamespace bsatn {\n    class Writer;\n    class Reader;\n}\n\n/**\n * Represents a point in time as microseconds since the Unix epoch.\n * This corresponds to SpacetimeDB's Timestamp type.\n */\nclass Timestamp {\nprivate:\n    int64_t micros_since_epoch_;\n\npublic:\n    // Constructors\n    explicit Timestamp(int64_t micros_since_epoch = 0) : micros_since_epoch_(micros_since_epoch) {}\n    \n    // Factory methods\n    static Timestamp from_micros_since_epoch(int64_t micros) {\n        return Timestamp(micros);\n    }\n    \n    static Timestamp from_millis_since_epoch(int64_t millis) {\n        return Timestamp(millis * 1000);\n    }\n    \n    static Timestamp from_seconds_since_epoch(int64_t seconds) {\n        return Timestamp(seconds * 1000000);\n    }\n    \n    // Get current timestamp\n    static Timestamp now() {\n        auto now = std::chrono::system_clock::now();\n        auto duration = now.time_since_epoch();\n        auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();\n        return Timestamp(micros);\n    }\n    \n    // Unix epoch (January 1, 1970 00:00:00 UTC)\n    static Timestamp unix_epoch() {\n        return Timestamp(0);\n    }\n    \n    // Conversion from std::chrono\n    static Timestamp from_chrono(std::chrono::system_clock::time_point tp) {\n        auto duration = tp.time_since_epoch();\n        auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();\n        return Timestamp(micros);\n    }\n    \n    // Getters\n    int64_t micros_since_epoch() const { return micros_since_epoch_; }\n    int64_t millis_since_epoch() const { return micros_since_epoch_ / 1000; }\n    int64_t seconds_since_epoch() const { return micros_since_epoch_ / 1000000; }\n    \n    // Conversion to std::chrono\n    std::chrono::system_clock::time_point to_chrono() const {\n        return std::chrono::system_clock::time_point(\n            std::chrono::microseconds(micros_since_epoch_)\n        );\n    }\n    \n    // Arithmetic operations\n    Timestamp operator+(const TimeDuration& duration) const {\n        return Timestamp(micros_since_epoch_ + duration.micros());\n    }\n    \n    Timestamp operator-(const TimeDuration& duration) const {\n        return Timestamp(micros_since_epoch_ - duration.micros());\n    }\n    \n    TimeDuration operator-(const Timestamp& other) const {\n        return TimeDuration::from_micros(micros_since_epoch_ - other.micros_since_epoch_);\n    }\n    \n    // Comparison operators\n    bool operator==(const Timestamp& other) const { return micros_since_epoch_ == other.micros_since_epoch_; }\n    bool operator!=(const Timestamp& other) const { return micros_since_epoch_ != other.micros_since_epoch_; }\n    bool operator<(const Timestamp& other) const { return micros_since_epoch_ < other.micros_since_epoch_; }\n    bool operator<=(const Timestamp& other) const { return micros_since_epoch_ <= other.micros_since_epoch_; }\n    bool operator>(const Timestamp& other) const { return micros_since_epoch_ > other.micros_since_epoch_; }\n    bool operator>=(const Timestamp& other) const { return micros_since_epoch_ >= other.micros_since_epoch_; }\n    \n    // Duration since another timestamp\n    TimeDuration duration_since(const Timestamp& earlier) const {\n        if (*this < earlier) {\n            return TimeDuration::from_micros(0);  // Return zero for negative durations\n        }\n        return TimeDuration::from_micros(micros_since_epoch_ - earlier.micros_since_epoch_);\n    }\n    \n    // Convert to string representation (ISO 8601 format to match Rust)\n    std::string to_string() const {\n        // Convert microseconds to seconds and fractional microseconds\n        std::time_t seconds = micros_since_epoch_ / 1000000;\n        int64_t remaining_micros = micros_since_epoch_ % 1000000;\n        \n        // Handle negative timestamps\n        if (micros_since_epoch_ < 0 && remaining_micros != 0) {\n            seconds -= 1;\n            remaining_micros = 1000000 + remaining_micros;\n        }\n        \n        // Convert to UTC time\n#if defined(_MSC_VER) && !defined(__EMSCRIPTEN__)\n        // Use gmtime_s on Windows (not available in Emscripten)\n        std::tm utc_time_buf;\n        errno_t err = gmtime_s(&utc_time_buf, &seconds);\n        std::tm* utc_time = (err == 0) ? &utc_time_buf : nullptr;\n#else\n        // Use standard gmtime for WASM/Emscripten and other platforms\n        std::tm* utc_time = std::gmtime(&seconds);\n#endif\n        if (!utc_time) {\n            // Fallback for invalid timestamps\n            return \"1970-01-01T00:00:00.000000+00:00\";\n        }\n        \n        // Format as ISO 8601 with microseconds\n        char buffer[64];\n        std::strftime(buffer, sizeof(buffer), \"%Y-%m-%dT%H:%M:%S\", utc_time);\n        \n        // Add microseconds and timezone\n        std::string result(buffer);\n        char micros_buffer[16];\n        snprintf(micros_buffer, sizeof(micros_buffer), \".%06lld+00:00\", (long long)remaining_micros);\n        result += micros_buffer;\n        \n        return result;\n    }\n    \n    // BSATN serialization\n    void bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const;\n    static Timestamp bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader);\n};\n\n// Convenience operators\ninline Timestamp operator+(const TimeDuration& duration, const Timestamp& timestamp) {\n    return timestamp + duration;\n}\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// BSATN Implementation\n// =============================================================================\n\n#include \"writer.h\"\n#include \"reader.h\"\n#include \"traits.h\"\n#include \"algebraic_type.h\"\n\nnamespace SpacetimeDB {\n\n// Timestamp BSATN implementation\ninline void Timestamp::bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const {\n    writer.write_i64_le(micros_since_epoch_);\n}\n\ninline Timestamp Timestamp::bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader) {\n    int64_t micros = reader.read_i64_le();\n    return Timestamp(micros);\n}\n\n} // namespace SpacetimeDB\n\n// Note: bsatn_traits specialization for Timestamp is defined in type_extensions.h\n// to ensure consistent handling with other special types like TimeDuration"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/traits.h",
    "content": "#ifndef SPACETIMEDB_BSATN_TRAITS_H\n#define SPACETIMEDB_BSATN_TRAITS_H\n\n#include <type_traits>\n#include <concepts>\n#include <variant>\n#include <optional>\n#include <vector>\n#include <string>\n#include <cctype>\n\n// Then custom headers\n#include \"algebraic_type.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include \"types.h\"  // For u128, u256, i128, i256\n#include \"type_extensions.h\"  // For special type constants\n\nnamespace SpacetimeDB::bsatn {\n\n// ============================================================================\n// CORE TYPES AND CONCEPTS\n// ============================================================================\n\n/**\n * @brief C++20 concept checking if a type has BSATN serialization method\n * \n * **FOR DEVELOPERS:** This concept is satisfied automatically by SPACETIMEDB_STRUCT!\n * You never implement bsatn_serialize() manually.\n * \n * @section what_devs_write **What You Actually Write:**\n * ```cpp\n * struct MyType {\n *     int value;\n *     std::string name;\n * };\n * SPACETIMEDB_STRUCT(MyType, value, name)  // This generates bsatn_serialize() for you!\n * ```\n * \n * @section what_macro_generates **What SPACETIMEDB_STRUCT Generates:**\n * The macro creates a bsatn_traits specialization with serialize() that effectively does:\n * ```cpp\n * // Generated code (you don't write this):\n * static void serialize(Writer& w, const MyType& v) {\n *     bsatn::serialize(w, v.value);\n *     bsatn::serialize(w, v.name);\n * }\n * ```\n * \n * @section for_contributors **For SDK Contributors:**\n * This concept enables the default bsatn_traits implementation to detect if a type\n * has member-based serialization. Used internally by primitive types and special cases.\n * \n * @tparam T The type to check for serialization capability\n */\ntemplate<typename T>\nconcept HasMemberSerialize = requires(const T& t, Writer& w) {\n    { t.bsatn_serialize(w) } -> std::same_as<void>;\n};\n\n/**\n * @brief C++20 concept checking if a type has BSATN deserialization method\n * \n * **FOR DEVELOPERS:** This concept is satisfied automatically by SPACETIMEDB_STRUCT!\n * You never implement bsatn_deserialize() manually.\n * \n * @section what_devs_write **What You Actually Write:**\n * ```cpp\n * struct MyType {\n *     int value;\n *     std::string name;\n * };\n * SPACETIMEDB_STRUCT(MyType, value, name)  // This generates bsatn_deserialize() for you!\n * ```\n * \n * @section what_macro_generates **What SPACETIMEDB_STRUCT Generates:**\n * The macro creates a bsatn_traits specialization with deserialize() that effectively does:\n * ```cpp\n * // Generated code (you don't write this):\n * static MyType deserialize(Reader& r) {\n *     MyType v;\n *     v.value = bsatn::deserialize<int>(r);\n *     v.name = bsatn::deserialize<std::string>(r);\n *     return v;\n * }\n * ```\n * \n * @section for_contributors **For SDK Contributors:**\n * This concept enables the default bsatn_traits implementation to detect if a type\n * has static deserialization. Used internally by primitive types and special cases.\n * \n * @tparam T The type to check for deserialization capability\n */\ntemplate<typename T>\nconcept HasStaticDeserialize = requires(Reader& r) {\n    { T::bsatn_deserialize(r) } -> std::same_as<T>;\n};\n\n/**\n * @brief C++20 concept checking if a type has SpacetimeDB schema metadata\n * \n * **FOR DEVELOPERS:** This concept is satisfied automatically by SPACETIMEDB_STRUCT!\n * The macro generates the algebraic_type_of specialization for you.\n * \n * @section what_devs_write **What You Actually Write:**\n * ```cpp\n * struct Player {\n *     uint32_t id;\n *     std::string name;\n *     uint32_t score;\n * };\n * SPACETIMEDB_STRUCT(Player, id, name, score)  // Generates schema metadata!\n * SPACETIMEDB_TABLE(Player, players, Public)    // Uses the generated metadata\n * ```\n * \n * @section what_macro_generates **What SPACETIMEDB_STRUCT Generates:**\n * The macro creates an algebraic_type_of specialization that registers the type\n * with field names and types in SpacetimeDB's schema system.\n * \n * @section for_contributors **For SDK Contributors:**\n * This concept ensures types can provide SpacetimeDB schema information.\n * The algebraic_type_of template is specialized by SPACETIMEDB_STRUCT and SPACETIMEDB_ENUM.\n * \n * @tparam T The type to check for schema metadata\n * @note All types in SpacetimeDB tables must satisfy this (automatic with macros)\n */\ntemplate<typename T>\nconcept HasAlgebraicType = requires {\n    { algebraic_type_of<T>::get() } -> std::same_as<AlgebraicType>;\n};\n\n/**\n * @brief Internal template for BSATN serialization traits\n * \n * **IMPORTANT FOR DEVELOPERS:** You typically do NOT use this template directly!\n * Instead, use the SPACETIMEDB_STRUCT macro which auto-generates all serialization code.\n * \n * This template is internal infrastructure that:\n * - Defines how types are serialized/deserialized in BSATN format\n * - Is automatically specialized by SPACETIMEDB_STRUCT and SPACETIMEDB_ENUM macros\n * - Already has specializations for primitives and standard containers (vector, optional, variant)\n * \n * @tparam T The type to serialize/deserialize\n * \n * @section user_approach **What Developers Actually Write**\n * \n * ```cpp\n * // 1. Define your struct\n * struct User {\n *     uint32_t id;\n *     std::string name;\n *     bool is_active;\n * };\n * \n * // 2. Use SPACETIMEDB_STRUCT macro - this auto-generates EVERYTHING\n * SPACETIMEDB_STRUCT(User, id, name, is_active)\n * ```\n * \n * The macro automatically generates:\n * - `bsatn_traits<User>::serialize()` method\n * - `bsatn_traits<User>::deserialize()` method  \n * - `bsatn_traits<User>::algebraic_type()` for schema registration\n * - All necessary template specializations\n * \n * You **never** need to write `bsatn_serialize()` or `bsatn_deserialize()` manually!\n * \n * @section internals **How It Works (For SDK Contributors)**\n * \n * The SPACETIMEDB_STRUCT macro expands to a bsatn_traits<T> specialization that:\n * - Calls `bsatn::serialize(writer, obj.field)` for each field in order\n * - Calls `bsatn::deserialize<FieldType>(reader)` for each field in order\n * - Registers the type with LazyTypeRegistrar for circular reference detection\n * \n * @example What SPACETIMEDB_STRUCT(User, id, name) generates:\n * @code\n * template<> struct bsatn_traits<User> {\n *     static void serialize(Writer& w, const User& v) {\n *         bsatn::serialize(w, v.id);\n *         bsatn::serialize(w, v.name);\n *     }\n *     static User deserialize(Reader& r) {\n *         User v;\n *         v.id = bsatn::deserialize<uint32_t>(r);\n *         v.name = bsatn::deserialize<std::string>(r);\n *         return v;\n *     }\n *     static AlgebraicType algebraic_type() { ...  }\n * };\n * @endcode\n * \n * @note For third-party types, manually specialize bsatn_traits if needed (advanced use case)\n * @see SPACETIMEDB_STRUCT macro for user-facing API\n * @see SPACETIMEDB_ENUM macro for enum serialization\n */\ntemplate<typename T>\nstruct bsatn_traits {\n    static void serialize(Writer& writer, const T& value) {\n        if constexpr (HasMemberSerialize<T>) {\n            value.bsatn_serialize(writer);\n        } else {\n            static_assert(sizeof(T) == 0, \"Type must implement bsatn_serialize or specialize bsatn_traits\");\n        }\n    }\n    \n    static T deserialize(Reader& reader) {\n        if constexpr (HasStaticDeserialize<T>) {\n            return T::bsatn_deserialize(reader);\n        } else {\n            static_assert(sizeof(T) == 0, \"Type must implement bsatn_deserialize or specialize bsatn_traits\");\n        }\n    }\n    \n    static AlgebraicType algebraic_type() {\n        if constexpr (HasAlgebraicType<T>) {\n            return SpacetimeDB::bsatn::algebraic_type_of<T>::get();\n        } else {\n            static_assert(sizeof(T) == 0, \"Type must have algebraic_type_of specialization\");\n        }\n    }\n};\n\n// Primitive type specializations are in primitive_traits.h\n// Special types (u128, u256, i128, i256, Identity, etc.) are in type_extensions.h\n\n// ============================================================================\n// BUILDER HELPERS\n// ============================================================================\n\n/**\n * Helper for building product types with named fields.\n * Only used by SPACETIMEDB_STRUCT macro.\n */\nclass ProductTypeBuilder {\nprivate:\n    std::vector<SpacetimeDB::bsatn::ProductTypeElement> elements_;\n    \npublic:\n    template<typename T>\n    ProductTypeBuilder& with_field(const std::string& name) {\n        elements_.emplace_back(name, bsatn_traits<T>::algebraic_type());\n        return *this;\n    }\n    \n    std::unique_ptr<SpacetimeDB::bsatn::ProductType> build() {\n        return std::make_unique<SpacetimeDB::bsatn::ProductType>(std::move(elements_));\n    }\n};\n\n/**\n * Simplified helper for building sum types (enums).\n * Only used by SPACETIMEDB_ENUM macro.\n */\nclass SumTypeBuilder {\nprivate:\n    std::vector<SpacetimeDB::bsatn::SumTypeVariant> variants_;\n    \npublic:\n    SumTypeBuilder& with_unit_variant(const std::string& name) {\n        variants_.emplace_back(name, AlgebraicType::Unit());\n        return *this;\n    }\n    \n    std::unique_ptr<SpacetimeDB::bsatn::SumTypeSchema> build() {\n        return std::make_unique<SpacetimeDB::bsatn::SumTypeSchema>(std::move(variants_));\n    }\n};\n\n// Special type detection is in type_extensions.h\n\n// ============================================================================\n// CONTAINER TYPE TRAITS\n// ============================================================================\n\n/**\n * @brief Internal specialization for std::vector<T> serialization\n * \n * **FOR DEVELOPERS:** You don't call this directly - it's used automatically when\n * you put `std::vector<T>` in a struct field and use SPACETIMEDB_STRUCT.\n * \n * @section usage **How Developers Use Vectors**\n * \n * ```cpp\n * struct Player {\n *     uint32_t id;\n *     std::vector<std::string> inventory;  // Vector field\n *     std::vector<uint32_t> scores;        // Another vector field\n * };\n * SPACETIMEDB_STRUCT(Player, id, inventory, scores)  // Auto-handles vectors!\n * \n * SPACETIMEDB_TABLE(Player, players, Public)\n * ```\n * \n * The SPACETIMEDB_STRUCT macro automatically handles vector serialization.\n * You never need to manually serialize vectors!\n * \n * @section nested **Nested Vectors Work Automatically**\n * \n * ```cpp\n * struct GameBoard {\n *     std::vector<std::vector<int>> grid;  // 2D grid\n * };\n * SPACETIMEDB_STRUCT(GameBoard, grid)  // Just works!\n * ```\n * \n * @tparam T The element type (must be BSATN-serializable)\n * @note This is SDK infrastructure - use SPACETIMEDB_STRUCT in your code\n */\ntemplate<typename T>\nstruct bsatn_traits<std::vector<T>> {\n    static void serialize(Writer& writer, const std::vector<T>& value) {\n        writer.write_u32_le(static_cast<uint32_t>(value.size()));\n        for (const auto& item : value) {\n            SpacetimeDB::bsatn::serialize(writer, item);\n        }\n    }\n    \n    static std::vector<T> deserialize(Reader& reader) {\n        uint32_t len = reader.read_u32_le();\n        std::vector<T> result;\n        result.reserve(len);\n        for (uint32_t i = 0; i < len; ++i) {\n            result.push_back(bsatn_traits<T>::deserialize(reader));\n        }\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        // Arrays are ALWAYS inlined, never registered in typespace\n        auto elem_type = bsatn_traits<T>::algebraic_type();\n        \n        // Special types should always be inlined\n        if (is_special_type(elem_type)) {\n            return AlgebraicType::Array(std::move(elem_type));\n        }\n        \n        // Return Array type INLINE - matches Rust behavior\n        return AlgebraicType::Array(std::move(elem_type));\n    }\n};\n\n/**\n * @brief Internal specialization for std::optional<T> serialization\n * \n * **FOR DEVELOPERS:** You don't call this directly - it's used automatically when\n * you put `std::optional<T>` in a struct field and use SPACETIMEDB_STRUCT.\n * \n * @section usage **How Developers Use Optional Fields**\n * \n * ```cpp\n * struct User {\n *     uint32_t id;\n *     std::string name;\n *     std::optional<std::string> email;  // Optional field\n *     std::optional<std::string> phone;  // Optional field\n * };\n * SPACETIMEDB_STRUCT(User, id, name, email, phone)  // Auto-handles optionals!\n * \n * SPACETIMEDB_TABLE(User, users, Public)\n * \n * // In a reducer:\n * SPACETIMEDB_REDUCER(add_user, ReducerContext ctx, std::string name) {\n *     User user{0, name, \"alice@example.com\", std::nullopt};  // email present, phone absent\n *     ctx.db[users].insert(user);\n * }\n * ```\n * \n * The SPACETIMEDB_STRUCT macro automatically handles optional serialization.\n * \n * @section format **Wire Format (For Reference)**\n * \n * Optionals are serialized as:\n * - 1-byte discriminant: 0 = Some(value), 1 = None\n * - If Some: followed by the serialized value\n * - If None: no additional bytes\n * \n * @section examples **Examples**\n * \n * ```cpp\n * std::optional<uint32_t> has_value = 42;\n * // Serialized: [00] [2A 00 00 00]\n * //              ^Some  ^value=42\n * \n * std::optional<uint32_t> empty;\n * // Serialized: [01]\n * //              ^None\n * ```\n * \n * @tparam T The wrapped type (must be BSATN-serializable)\n * @note This is SDK infrastructure - use SPACETIMEDB_STRUCT in your code\n */\ntemplate<typename T>\nstruct bsatn_traits<std::optional<T>> {\n    static void serialize(Writer& writer, const std::optional<T>& value) {\n        if (value.has_value()) {\n            writer.write_u8(0); // Some = 0 (SpacetimeDB convention)\n            bsatn_traits<T>::serialize(writer, *value);\n        } else {\n            writer.write_u8(1); // None = 1 (SpacetimeDB convention)\n        }\n    }\n    \n    static std::optional<T> deserialize(Reader& reader) {\n        uint8_t tag = reader.read_u8();\n        if (tag == 0) { // Some\n            return bsatn_traits<T>::deserialize(reader);\n        } else if (tag == 1) { // None\n            return std::nullopt;\n        } else {\n            std::abort(); // Invalid optional tag in BSATN deserialization\n        }\n    }\n    \n    static AlgebraicType algebraic_type() {\n        // Options are ALWAYS inlined, never registered as separate types\n        auto inner_type = bsatn_traits<T>::algebraic_type();\n        \n        // Create Option variants: Some(T) and None\n        std::vector<SpacetimeDB::bsatn::SumTypeVariant> variants;\n        variants.emplace_back(\"some\", std::move(inner_type));\n        variants.emplace_back(\"none\", AlgebraicType::Unit());\n        \n        return AlgebraicType::make_sum(\n            std::make_unique<SpacetimeDB::bsatn::SumTypeSchema>(std::move(variants))\n        );\n    }\n};\n\n// ============================================================================\n// MONOSTATE (UNIT TYPE) TRAITS\n// ============================================================================\n\n/**\n * @brief BSATN traits for std::monostate (Unit type)\n * \n * std::monostate is used as a placeholder type for unit variants in enums.\n * It serializes as nothing (0 bytes) and always deserializes to the same value.\n * \n * Example usage:\n * ```cpp\n * SPACETIMEDB_ENUM(Result,\n *     (Ok, uint32_t),\n *     (Err, std::string),\n *     (Empty, Unit)  // Unit is std::monostate\n * )\n * ```\n */\ntemplate<>\nstruct bsatn_traits<std::monostate> {\n    static void serialize(Writer& writer, const std::monostate& value) {\n        // Unit type serializes as nothing (0 bytes)\n        (void)writer;\n        (void)value;\n    }\n    \n    static std::monostate deserialize(Reader& reader) {\n        // Unit type deserializes as default-constructed monostate\n        (void)reader;\n        return std::monostate{};\n    }\n    \n    static AlgebraicType algebraic_type() {\n        // Unit type in SpacetimeDB\n        return AlgebraicType::Unit();\n    }\n};\n\n// ============================================================================\n// VARIANT TYPE TRAITS\n// ============================================================================\n\n/**\n * @brief Helper template to find the index of a type within a std::variant\n * \n * This compile-time utility recursively searches through variant alternatives\n * to find the index position of a specific type T.\n * \n * @tparam Variant The std::variant type to search\n * @tparam T The type to find within the variant\n * @tparam Index Current search index (defaults to 0)\n * \n * @example\n * @code\n * using MyVariant = std::variant<int, std::string, double>;\n * static_assert(variant_index_of<MyVariant, std::string>::value == 1);\n * static_assert(variant_index_of<MyVariant, double>::value == 2);\n * @endcode\n */\ntemplate<typename Variant, typename T, std::size_t Index = 0>\nstruct variant_index_of;\n\ntemplate<typename T, typename First, typename... Rest, std::size_t Index>\nstruct variant_index_of<std::variant<First, Rest...>, T, Index> \n    : variant_index_of<std::variant<Rest...>, T, Index + 1> {};\n\ntemplate<typename T, typename... Rest, std::size_t Index>\nstruct variant_index_of<std::variant<T, Rest...>, T, Index> \n    : std::integral_constant<std::size_t, Index> {};\n\n/**\n * @brief Internal specialization for std::variant serialization\n * \n * **FOR DEVELOPERS:** You typically should NOT use std::variant directly in SpacetimeDB!\n * Instead, use **SPACETIMEDB_ENUM** which provides named variants and better ergonomics.\n * \n * @section recommended **Recommended Approach: Use SPACETIMEDB_ENUM**\n * \n * ```cpp\n * // DON'T: Use raw std::variant\n * // std::variant<int, std::string> message_data;\n * \n * // DO: Use SPACETIMEDB_ENUM with named variants\n * SPACETIMEDB_ENUM(MessageData,\n *     (Number, int),\n *     (Text, std::string)\n * )\n * \n * struct Message {\n *     uint32_t id;\n *     MessageData content;  // Named variants!\n * };\n * SPACETIMEDB_STRUCT(Message, id, content)\n * \n * // Usage:\n * Message msg{1, std::string(\"Hello\")};\n * if (msg.content.index() == 1) {\n *     auto& text = std::get<std::string>(msg.content.value);\n * }\n * ```\n * \n * @section fallback **When You Might Use std::variant**\n * \n * Only use std::variant directly if:\n * - The variant is purely internal (not in table schema)\n * \n * ```cpp\n * struct InternalState {\n *     std::variant<int, std::string> temp_data;  // OK for internal use\n * };\n * SPACETIMEDB_STRUCT(InternalState, temp_data)  // Auto-handled\n * ```\n * \n * @section format **Wire Format**\n * \n * Variants are serialized as:\n * - 1-byte tag (0 = first type, 1 = second type, etc.)\n * - Followed by the serialized value of the active alternative\n * \n * @tparam Ts... The alternative types (each must be BSATN-serializable)\n * @warning **Type order matters!** Reordering alternatives breaks wire format compatibility\n * @note This is SDK infrastructure - use SPACETIMEDB_ENUM for user-facing code\n * @see SPACETIMEDB_ENUM for the recommended way to define sum types\n */\ntemplate<typename... Ts>\nstruct bsatn_traits<std::variant<Ts...>> {\n    using variant_t = std::variant<Ts...>;\n    \n    static void serialize(Writer& writer, const variant_t& value) {\n        writer.write_u8(static_cast<uint8_t>(value.index()));\n        std::visit([&writer](const auto& v) {\n            using value_type = std::decay_t<decltype(v)>;\n            if constexpr (!std::is_same_v<value_type, std::monostate>) {\n                SpacetimeDB::bsatn::serialize(writer, v);\n            }\n        }, value);\n    }\n    \n    static variant_t deserialize(Reader& reader) {\n        uint8_t tag = reader.read_u8();\n        return deserialize_variant<0>(reader, tag);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        std::vector<SpacetimeDB::bsatn::SumTypeVariant> variants;\n        build_variants<0, Ts...>(variants);\n        return AlgebraicType::make_sum(\n            std::make_unique<SpacetimeDB::bsatn::SumTypeSchema>(std::move(variants))\n        );\n    }\n    \nprivate:\n    template<std::size_t Index>\n    static variant_t deserialize_variant(Reader& reader, uint8_t tag) {\n        if constexpr (Index < sizeof...(Ts)) {\n            if (tag == Index) {\n                using type = std::variant_alternative_t<Index, variant_t>;\n                if constexpr (std::is_same_v<type, std::monostate>) {\n                    return type{};\n                } else {\n                    return SpacetimeDB::bsatn::deserialize<type>(reader);\n                }\n            }\n            return deserialize_variant<Index + 1>(reader, tag);\n        } else {\n            std::abort(); // Invalid variant tag\n        }\n    }\n    \n    template<std::size_t Index, typename First, typename... Rest>\n    static void build_variants(std::vector<SpacetimeDB::bsatn::SumTypeVariant>& variants) {\n        if constexpr (std::is_same_v<First, std::monostate>) {\n            variants.emplace_back(\"variant_\" + std::to_string(Index), AlgebraicType::Unit());\n        } else {\n            variants.emplace_back(\"variant_\" + std::to_string(Index), bsatn_traits<First>::algebraic_type());\n        }\n        if constexpr (sizeof...(Rest) > 0) {\n            build_variants<Index + 1, Rest...>(variants);\n        }\n    }\n};\n\n// ============================================================================\n// FIELD REGISTRATION HELPERS\n// ============================================================================\n\n// Helper to get the AlgebraicType for field types\ntemplate<typename T>\ninline AlgebraicType get_field_algebraic_type() {\n    return bsatn_traits<T>::algebraic_type();\n}\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_TRAITS_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/type_extensions.h",
    "content": "#ifndef SPACETIMEDB_BSATN_TYPE_EXTENSIONS_H\n#define SPACETIMEDB_BSATN_TYPE_EXTENSIONS_H\n\n/**\n * @file type_extensions.h\n * @brief Extended type support for SpacetimeDB BSATN serialization\n * \n * This header combines functionality from special_types.h and extended_types.h\n * It provides:\n * - Special type tags and identification functions\n * - BSATN trait specializations for extended types\n * - Support for large integers, container types, and SpacetimeDB core types\n */\n\nnamespace SpacetimeDB::bsatn {\n\n// =============================================================================\n// SPECIAL TYPE CONSTANTS - Must be defined early for use in other headers\n// =============================================================================\n\n// Special type field tags\nconstexpr const char* IDENTITY_TAG = \"__identity__\";\nconstexpr const char* CONNECTION_ID_TAG = \"__connection_id__\";\nconstexpr const char* TIMESTAMP_TAG = \"__timestamp_micros_since_unix_epoch__\";\nconstexpr const char* TIME_DURATION_TAG = \"__time_duration_micros__\";\nconstexpr const char* UUID_TAG = \"__uuid__\";\n\n} // namespace SpacetimeDB::bsatn\n\n// Now include headers that may use these constants\n#include \"algebraic_type.h\"\n#include \"types.h\"\n#include \"timestamp.h\"\n#include \"time_duration.h\"\n#include \"uuid.h\"\n#include <string_view>\n#include <algorithm>\n#include <optional>\n#include <vector>\n\nnamespace SpacetimeDB::bsatn {\n\n// Forward declarations\nclass Reader;\nclass Writer;\ntemplate<typename T> struct bsatn_traits;\n\n// Helper to detect optional types\ntemplate<typename T>\nstruct is_optional : std::false_type {};\ntemplate<typename T>\nstruct is_optional<std::optional<T>> : std::true_type {};\n\n// Helper to detect vector types\ntemplate<typename T>\nstruct is_vector : std::false_type {};\ntemplate<typename T>\nstruct is_vector<std::vector<T>> : std::true_type {};\n\n// Helper to get primitive type tags\ntemplate<typename T>\nconstexpr uint32_t get_primitive_type_tag() {\n    if constexpr (std::is_same_v<T, bool>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::Bool);\n    } else if constexpr (std::is_same_v<T, uint8_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::U8);\n    } else if constexpr (std::is_same_v<T, uint16_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::U16);\n    } else if constexpr (std::is_same_v<T, uint32_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::U32);\n    } else if constexpr (std::is_same_v<T, uint64_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::U64);\n    } else if constexpr (std::is_same_v<T, int8_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::I8);\n    } else if constexpr (std::is_same_v<T, int16_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::I16);\n    } else if constexpr (std::is_same_v<T, int32_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::I32);\n    } else if constexpr (std::is_same_v<T, int64_t>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::I64);\n    } else if constexpr (std::is_same_v<T, float>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::F32);\n    } else if constexpr (std::is_same_v<T, double>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::F64);\n    } else if constexpr (std::is_same_v<T, std::string>) {\n        return static_cast<uint32_t>(AlgebraicTypeTag::String);\n    } else {\n        return 0; // Not a primitive type\n    }\n}\n\n// =============================================================================\n// SPECIAL TYPE TAGS AND IDENTIFICATION\n// =============================================================================\n\n/**\n * Special type tags used by SpacetimeDB to identify built-in types.\n * These match the Rust implementation exactly.\n * Note: Constants are defined at the top of this file before includes.\n */\n\n/**\n * Enumeration of special types recognized by SpacetimeDB.\n */\nenum class SpecialTypeKind {\n    None,\n    Identity,\n    ConnectionId,\n    Timestamp,\n    TimeDuration,\n    Unit,        // Empty Product type\n    Never,       // Empty Sum type\n    ScheduleAt,  // Sum with Interval and Time variants\n    Option,      // Sum with some and none variants\n    Uuid,        // UUID type\n    Result       // Sum with ok and err variants\n};\n\n/**\n * Check if a ProductType represents a special SpacetimeDB type.\n * Special types include field-tagged types and structural types.\n */\ninline bool is_special_product_type(const ProductType& product) {\n    // Unit type: empty product\n    if (product.elements.size() == 0) {\n        return true;\n    }\n    \n    // Field-tagged special types\n    if (product.elements.size() == 1 && product.elements[0].name.has_value()) {\n        const std::string& tag = *product.elements[0].name;\n        return tag == IDENTITY_TAG ||\n               tag == CONNECTION_ID_TAG ||\n               tag == UUID_TAG ||\n               tag == TIMESTAMP_TAG ||\n               tag == TIME_DURATION_TAG;\n    }\n    \n    return false;\n}\n\n/**\n * Check if a SumType represents a special SpacetimeDB type.\n */\ninline bool is_special_sum_type(const SumTypeSchema& sum) {\n    // Never type: empty sum\n    if (sum.variants.size() == 0) {\n        return true;\n    }\n    \n    // Two-variant sum types: ScheduleAt, Option, and Result\n    if (sum.variants.size() == 2) {\n        bool has_interval = false, has_time = false;\n        bool has_some = false, has_none = false;\n        bool has_ok = false, has_err = false;\n        \n        for (const auto& variant : sum.variants) {\n            if (variant.name == \"Interval\") has_interval = true;\n            else if (variant.name == \"Time\") has_time = true;\n            else if (variant.name == \"some\") has_some = true;\n            else if (variant.name == \"none\") has_none = true;\n            else if (variant.name == \"ok\") has_ok = true;\n            else if (variant.name == \"err\") has_err = true;\n        }\n        \n        // ScheduleAt type: sum with Interval and Time variants\n        if (has_interval && has_time) {\n            return true;\n        }\n        \n        // Option type: sum with some and none variants\n        if (has_some && has_none) {\n            return true;\n        }\n        \n        // Result type: sum with ok and err variants\n        if (has_ok && has_err) {\n            return true;\n        }\n    }\n    \n    return false;\n}\n\n/**\n * Check if an AlgebraicType represents a special SpacetimeDB type.\n */\ninline bool is_special_type(const AlgebraicType& type) {\n    if (type.tag() == AlgebraicTypeTag::Product) {\n        return is_special_product_type(type.as_product());\n    } else if (type.tag() == AlgebraicTypeTag::Sum) {\n        return is_special_sum_type(type.as_sum());\n    }\n    return false;\n}\n\n\n/**\n * Get the kind of special type represented by an AlgebraicType.\n */\ninline SpecialTypeKind get_special_type_kind(const AlgebraicType& type) {\n    if (type.tag() == AlgebraicTypeTag::Product) {\n        const auto& product = type.as_product();\n        \n        // Unit type\n        if (product.elements.size() == 0) {\n            return SpecialTypeKind::Unit;\n        }\n        \n        // Field-tagged types\n        if (product.elements.size() == 1 && product.elements[0].name.has_value()) {\n            const std::string& tag = *product.elements[0].name;\n            if (tag == IDENTITY_TAG) return SpecialTypeKind::Identity;\n            if (tag == CONNECTION_ID_TAG) return SpecialTypeKind::ConnectionId;\n            if (tag == TIMESTAMP_TAG) return SpecialTypeKind::Timestamp;\n            if (tag == TIME_DURATION_TAG) return SpecialTypeKind::TimeDuration;\n            if (tag == UUID_TAG) return SpecialTypeKind::Uuid;\n        }\n    } else if (type.tag() == AlgebraicTypeTag::Sum) {\n        const auto& sum = type.as_sum();\n        \n        // Never type\n        if (sum.variants.size() == 0) {\n            return SpecialTypeKind::Never;\n        }\n        \n        // Two-variant sum types: ScheduleAt and Option\n        if (sum.variants.size() == 2) {\n            bool has_interval = false, has_time = false;\n            bool has_some = false, has_none = false;\n            \n            for (const auto& variant : sum.variants) {\n                if (variant.name == \"Interval\") has_interval = true;\n                else if (variant.name == \"Time\") has_time = true;\n                else if (variant.name == \"some\") has_some = true;\n                else if (variant.name == \"none\") has_none = true;\n            }\n            \n            // ScheduleAt type\n            if (has_interval && has_time) {\n                return SpecialTypeKind::ScheduleAt;\n            }\n            \n            // Option type\n            if (has_some && has_none) {\n                return SpecialTypeKind::Option;\n            }\n        }\n    }\n    \n    return SpecialTypeKind::None;\n}\n\n/**\n * Create a special type ProductType with the given tag and data type.\n */\ninline std::unique_ptr<ProductType> make_special_type(const char* tag, AlgebraicType data_type) {\n    std::vector<ProductTypeElement> elements;\n    elements.emplace_back(tag, std::move(data_type));\n    return std::make_unique<ProductType>(std::move(elements));\n}\n\n/**\n * @brief Factory functions for SpacetimeDB special types.\n * \n * These types are represented as ProductTypes with a single specially-tagged field.\n * The tag identifies the semantic meaning of the type.\n */\nnamespace special_types {\n    \n    /**\n     * @brief Create an Identity type (256-bit identifier).\n     * \n     * Identity is represented as an array of 32 bytes (U8[32]).\n     * \n     * @return AlgebraicType representing an Identity\n     * @todo Integrate with type registry for proper type indexing\n     */\n    inline AlgebraicType identity() {\n        // Identity uses U256 as inner type (matches Rust implementation)\n        // CRITICAL: Pass the actual U256 type, not a Ref to it - special types are inlined!\n        auto product = make_special_type(IDENTITY_TAG, AlgebraicType::U256());\n        return AlgebraicType::make_product(std::move(product));\n    }\n    \n    /**\n     * @brief Create a ConnectionId type (64-bit connection identifier).\n     * \n     * @return AlgebraicType representing a ConnectionId\n     * @todo Integrate with type registry for proper type indexing\n     */\n    inline AlgebraicType connection_id() {\n        // ConnectionId uses U128 as inner type (matches Rust implementation)\n        // CRITICAL: Pass the actual U128 type, not a Ref to it - special types are inlined!\n        auto product = make_special_type(CONNECTION_ID_TAG, AlgebraicType::U128());\n        return AlgebraicType::make_product(std::move(product));\n    }\n    \n    /**\n     * @brief Create a Timestamp type (microseconds since Unix epoch).\n     * \n     * @return AlgebraicType representing a Timestamp\n     * @todo Integrate with type registry for proper type indexing\n     */\n    inline AlgebraicType timestamp() {\n        // Timestamp uses I64 as inner type (matches Rust implementation)\n        // CRITICAL: Pass the actual I64 type, not a Ref to it - special types are inlined!\n        auto product = make_special_type(TIMESTAMP_TAG, AlgebraicType::I64());\n        return AlgebraicType::make_product(std::move(product));\n    }\n    \n    /**\n     * @brief Create a TimeDuration type (duration in microseconds).\n     * \n     * @return AlgebraicType representing a TimeDuration\n     * @todo Integrate with type registry for proper type indexing\n     */\n    inline AlgebraicType time_duration() {\n        // TimeDuration uses I64 as inner type (matches Rust implementation)\n        // CRITICAL: Pass the actual I64 type, not a Ref to it - special types are inlined!\n        auto product = make_special_type(TIME_DURATION_TAG, AlgebraicType::I64());\n        return AlgebraicType::make_product(std::move(product));\n    }\n    \n    /**\n     * @brief Create a Uuid type (128-bit universally unique identifier).\n     * \n     * @return AlgebraicType representing a Uuid\n     * @todo Integrate with type registry for proper type indexing\n     */\n    inline AlgebraicType uuid() {\n        // Uuid uses U128 as inner type (matches Rust implementation)\n        // CRITICAL: Pass the actual U128 type, not a Ref to it - special types are inlined!\n        auto product = make_special_type(UUID_TAG, AlgebraicType::U128());\n        return AlgebraicType::make_product(std::move(product));\n    }\n    \n} // namespace special_types\n\n// =============================================================================\n// LARGE INTEGER TYPES (u128, i128)\n// =============================================================================\n\n/**\n * BSATN serialization for 128-bit unsigned integer\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::u128> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::u128& value) {\n        // Serialize as 16 bytes in little-endian order\n        writer.write_u64_le(value.low);\n        writer.write_u64_le(value.high);\n    }\n    \n    static ::SpacetimeDB::u128 deserialize(Reader& reader) {\n        uint64_t low = reader.read_u64_le();\n        uint64_t high = reader.read_u64_le();\n        return ::SpacetimeDB::u128(high, low);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U128();\n    }\n};\n\n/**\n * BSATN serialization for 128-bit signed integer\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::i128> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::i128& value) {\n        // Serialize as 16 bytes in little-endian order\n        writer.write_u64_le(value.low);\n        writer.write_u64_le(static_cast<uint64_t>(value.high));\n    }\n    \n    static ::SpacetimeDB::i128 deserialize(Reader& reader) {\n        uint64_t low = reader.read_u64_le();\n        uint64_t high = reader.read_u64_le();\n        return ::SpacetimeDB::i128(static_cast<int64_t>(high), low);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I128();\n    }\n};\n\n// u256 and i256 also need bsatn_traits specializations that call their methods\n\n/**\n * BSATN serialization for 256-bit unsigned integer\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::u256> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::u256& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::u256 deserialize(Reader& reader) {\n        ::SpacetimeDB::u256 result;\n        result.bsatn_deserialize(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U256();\n    }\n};\n\n/**\n * BSATN serialization for 256-bit signed integer\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::i256> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::i256& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::i256 deserialize(Reader& reader) {\n        ::SpacetimeDB::i256 result;\n        result.bsatn_deserialize(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::I256();\n    }\n};\n\n// =============================================================================\n// SPACETIMEDB CORE TYPES (Identity, ConnectionId, Timestamp, TimeDuration, Uuid)\n// =============================================================================\n\n/**\n * BSATN serialization for Identity\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::Identity> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::Identity& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::Identity deserialize(Reader& reader) {\n        ::SpacetimeDB::Identity result;\n        result.bsatn_deserialize(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return special_types::identity();\n    }\n};\n\n/**\n * BSATN serialization for ConnectionId\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::ConnectionId> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::ConnectionId& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::ConnectionId deserialize(Reader& reader) {\n        ::SpacetimeDB::ConnectionId result;\n        result.bsatn_deserialize(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return special_types::connection_id();\n    }\n};\n\n/**\n * BSATN serialization for Timestamp\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::Timestamp> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::Timestamp& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::Timestamp deserialize(Reader& reader) {\n        return ::SpacetimeDB::Timestamp::bsatn_deserialize(reader);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return special_types::timestamp();\n    }\n};\n\n/**\n * BSATN serialization for Uuid\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::Uuid> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::Uuid& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::Uuid deserialize(Reader& reader) {\n        return ::SpacetimeDB::Uuid::bsatn_deserialize(reader);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return special_types::uuid();\n    }\n};\n\n/**\n * BSATN serialization for TimeDuration\n */\ntemplate<>\nstruct bsatn_traits<::SpacetimeDB::TimeDuration> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::TimeDuration& value) {\n        value.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::TimeDuration deserialize(Reader& reader) {\n        return ::SpacetimeDB::TimeDuration::bsatn_deserialize(reader);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return special_types::time_duration();\n    }\n};\n\n// =============================================================================\n// CONTAINER TYPES\n// =============================================================================\n\n// BSATN serialization for Result<T, E>\ntemplate<typename T, typename E>\nstruct bsatn_traits<::SpacetimeDB::Result<T, E>> {\n    static void serialize(Writer& writer, const ::SpacetimeDB::Result<T, E>& result) {\n        result.bsatn_serialize(writer);\n    }\n    \n    static ::SpacetimeDB::Result<T, E> deserialize(Reader& reader) {\n        return ::SpacetimeDB::Result<T, E>::bsatn_deserialize(reader);\n    }\n    \n    static AlgebraicType algebraic_type() {\n        // Return sum type with \"ok\" and \"err\" variants\n        std::vector<SumTypeVariant> variants;\n        variants.emplace_back(\"ok\", bsatn_traits<T>::algebraic_type());\n        variants.emplace_back(\"err\", bsatn_traits<E>::algebraic_type());\n        return AlgebraicType::make_sum(std::make_unique<SumTypeSchema>(std::move(variants)));\n    }\n};\n\n// =============================================================================\n// STANDARD LIBRARY CONTAINER TYPES\n// =============================================================================\n\n// Note: bsatn_traits specializations for std::optional<T> and std::vector<T>\n// are now defined in traits.h (as of this consolidation fix).\n// This file contains the special type trait definitions and helpers.\n\n// Convenience type aliases for SpacetimeDB vectors\nusing VecTimeDuration = std::vector<::SpacetimeDB::TimeDuration>;\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_TYPE_EXTENSIONS_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/types.h",
    "content": "#pragma once\n\n/**\n * SpacetimeDB C++ bindings - Extended Type System\n * \n * This header provides complete support for all SpacetimeDB types including:\n * - Special types: Identity, ConnectionId, Timestamp, TimeDuration, Uuid\n * - Large integers: u128, i128, u256, i256\n * - Container types: Option<T>, Vec<T>, Result<T, E>\n * - BSATN serialization for all types\n * \n * All five SpacetimeDB special types are fully implemented and tested:\n * - Identity: U256 with __identity__ tag\n * - ConnectionId: U128 with __connection_id__ tag  \n * - Timestamp: I64 with __timestamp_micros_since_unix_epoch__ tag\n * - TimeDuration: I64 with __time_duration_micros__ tag\n * - Uuid: U128 with __uuid__ tag\n * \n * Result<T, E> is a structural sum type with two variants:\n * - ok(T): Contains success value (tag 0)\n * - err(E): Contains error value (tag 1)\n */\n\n#include <cstdint>\n#include <array>\n#include <vector>\n#include <optional>\n#include <string>\n#include <cstring>\n#include <type_traits>\n#include <stdexcept>  // For std::runtime_error\n#include <algorithm>  // For std::copy\n#include <sstream>    // For std::ostringstream\n#include <iomanip>    // For std::hex, std::setfill, std::setw\n\n// Forward declarations for BSATN\nnamespace SpacetimeDB {\nnamespace bsatn {\n    class Writer;\n    class Reader;\n}\n\n// Forward declarations\nclass Timestamp;\nclass TimeDuration;\nclass Uuid;\nstruct u128;\ntemplate<typename T, typename E> class Result;\n\n// =============================================================================\n// IDENTITY TYPE\n// =============================================================================\n\n#define IDENTITY_SIZE 32\n\nclass Identity {\npublic:\n    static constexpr size_t SIZE = IDENTITY_SIZE;\n    using ByteArray = std::array<uint8_t, IDENTITY_SIZE>;\n    \nprivate:\n    std::array<uint8_t, IDENTITY_SIZE> value;\n    \npublic:\n    // Constructors\n    Identity();\n    Identity(const std::array<uint8_t, IDENTITY_SIZE>& bytes);\n    \n    // Accessors\n    const std::array<uint8_t, IDENTITY_SIZE>& get_bytes() const;\n    const ByteArray& to_byte_array() const { return value; }\n    std::string to_hex_string() const;\n    \n    // Convert to string (using hex representation)\n    std::string to_string() const {\n        return to_hex_string();\n    }\n    \n    // Operators\n    bool operator==(const Identity& other) const;\n    bool operator!=(const Identity& other) const;\n    bool operator<(const Identity& other) const;\n    \n    // BSATN serialization (implemented below)\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n    void bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader);\n};\n\n\n// =============================================================================\n// TYPE ALIASES FOR COMMON SPACETIMEDB TYPES\n// =============================================================================\n\n// Option<T> is just std::optional<T> with some helper methods\ntemplate<typename T>\nclass Option : public std::optional<T> {\npublic:\n    using std::optional<T>::optional;\n    \n    // Helper static methods for clarity\n    static Option<T> some(const T& value) {\n        return Option<T>(value);\n    }\n    \n    static Option<T> none() {\n        return Option<T>(std::nullopt);\n    }\n    \n    // Allow implicit conversion from std::optional\n    Option(const std::optional<T>& opt) : std::optional<T>(opt) {}\n    Option(std::optional<T>&& opt) : std::optional<T>(std::move(opt)) {}\n};\n\n// Vec<T> is just std::vector<T>\ntemplate<typename T>\nusing Vec = std::vector<T>;\n\n// =============================================================================\n// LARGE INTEGER TYPES\n// =============================================================================\n\n// 128-bit unsigned integer\nstruct u128 {\n    uint64_t low;\n    uint64_t high;\n    \n    u128() : low(0), high(0) {}\n    u128(uint64_t l) : low(l), high(0) {}\n    u128(uint64_t h, uint64_t l) : low(l), high(h) {}\n    \n    // Factory method for clarity (high, low parameter order)\n    static u128 from_u64(uint64_t high, uint64_t low) {\n        return u128(high, low);\n    }\n    \n    bool operator==(const u128& other) const {\n        return low == other.low && high == other.high;\n    }\n    \n    bool operator!=(const u128& other) const {\n        return !(*this == other);\n    }\n    \n    // Convert to string using proper decimal conversion\n    std::string to_string() const {\n        if (high == 0) {\n            return std::to_string(low);\n        }\n        \n        // Convert u128 to decimal string using byte-level long division\n        // Treat the u128 as a 16-byte array in little-endian order\n        uint8_t bytes[16];\n        std::memcpy(bytes, &low, 8);\n        std::memcpy(bytes + 8, &high, 8);\n        \n        std::string result;\n        \n        // Repeatedly divide by 10\n        while (true) {\n            int remainder = 0;\n            bool all_zero = true;\n            \n            // Divide the 16-byte number by 10 (from most significant to least significant)\n            for (int i = 15; i >= 0; --i) {\n                int temp = remainder * 256 + bytes[i];\n                bytes[i] = temp / 10;\n                remainder = temp % 10;\n                if (bytes[i] != 0) all_zero = false;\n            }\n            \n            // Add the remainder digit to result (building in reverse)\n            result = static_cast<char>('0' + remainder) + result;\n            \n            if (all_zero) break;\n        }\n        \n        return result;\n    }\n    \n    // BSATN serialization\n    static void serialize(std::vector<uint8_t>& buffer, const u128& value) {\n        // Little-endian serialization\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value.low >> (i * 8)) & 0xFF);\n        }\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value.high >> (i * 8)) & 0xFF);\n        }\n    }\n    \n    static u128 deserialize(const uint8_t* data) {\n        u128 result;\n        result.low = 0;\n        result.high = 0;\n        for (int i = 0; i < 8; ++i) {\n            result.low |= static_cast<uint64_t>(data[i]) << (i * 8);\n        }\n        for (int i = 0; i < 8; ++i) {\n            result.high |= static_cast<uint64_t>(data[8 + i]) << (i * 8);\n        }\n        return result;\n    }\n};\n\n// 128-bit signed integer\nstruct i128 {\n    uint64_t low;\n    int64_t high;\n    \n    i128() : low(0), high(0) {}\n    i128(int64_t l) : low(static_cast<uint64_t>(l)), high(l < 0 ? -1 : 0) {}\n    i128(int64_t h, uint64_t l) : low(l), high(h) {}\n    \n    // Factory method for clarity (high, low parameter order)\n    static i128 from_i64(int64_t high, uint64_t low) {\n        return i128(high, low);\n    }\n    \n    bool operator==(const i128& other) const {\n        return low == other.low && high == other.high;\n    }\n    \n    bool operator!=(const i128& other) const {\n        return !(*this == other);\n    }\n    \n    // Convert to string using proper decimal conversion\n    std::string to_string() const {\n        // Handle negative values by converting to positive and adding minus sign\n        bool is_negative = (high < 0);\n        \n        // Convert to unsigned for calculation\n        u128 abs_value;\n        if (is_negative) {\n            // Two's complement: flip bits and add 1\n            abs_value.low = ~low + 1;\n            abs_value.high = ~static_cast<uint64_t>(high);\n            if (abs_value.low == 0) {\n                abs_value.high++; // Handle carry\n            }\n        } else {\n            abs_value.low = low;\n            abs_value.high = static_cast<uint64_t>(high);\n        }\n        \n        std::string result = abs_value.to_string();\n        return is_negative ? \"-\" + result : result;\n    }\n    \n    // BSATN serialization\n    static void serialize(std::vector<uint8_t>& buffer, const i128& value) {\n        // Little-endian serialization\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value.low >> (i * 8)) & 0xFF);\n        }\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value.high >> (i * 8)) & 0xFF);\n        }\n    }\n    \n    static i128 deserialize(const uint8_t* data) {\n        i128 result;\n        result.low = 0;\n        result.high = 0;\n        for (int i = 0; i < 8; ++i) {\n            result.low |= static_cast<uint64_t>(data[i]) << (i * 8);\n        }\n        uint64_t high_unsigned = 0;\n        for (int i = 0; i < 8; ++i) {\n            high_unsigned |= static_cast<uint64_t>(data[8 + i]) << (i * 8);\n        }\n        result.high = static_cast<int64_t>(high_unsigned);\n        return result;\n    }\n};\n\n// 256-bit unsigned integer\nstruct u256 {\n    std::array<uint8_t, 32> data;\n    \n    u256() { data.fill(0); }\n    \n    explicit u256(const uint8_t* bytes) {\n        std::memcpy(data.data(), bytes, 32);\n    }\n    \n    // Constructor from 4 uint64_t values (big-endian order: word3 is most significant)\n    u256(uint64_t word3, uint64_t word2, uint64_t word1, uint64_t word0) {\n        // Store in little-endian byte order\n        for (int i = 0; i < 8; ++i) {\n            data[i] = (word0 >> (i * 8)) & 0xFF;\n            data[8 + i] = (word1 >> (i * 8)) & 0xFF;\n            data[16 + i] = (word2 >> (i * 8)) & 0xFF;\n            data[24 + i] = (word3 >> (i * 8)) & 0xFF;\n        }\n    }\n    \n    bool operator==(const u256& other) const {\n        return data == other.data;\n    }\n    \n    bool operator!=(const u256& other) const {\n        return !(*this == other);\n    }\n    \n    // Convert to string using arbitrary precision decimal conversion\n    std::string to_string() const {\n        // Simple approach: use byte-by-byte division by 10\n        // Make a copy of the data for manipulation\n        std::array<uint8_t, 32> temp_data = data;\n        std::string result;\n        \n        // Check if all zeros\n        bool all_zero = true;\n        for (int i = 0; i < 32; ++i) {\n            if (temp_data[i] != 0) {\n                all_zero = false;\n                break;\n            }\n        }\n        if (all_zero) return \"0\";\n        \n        // Repeatedly divide by 10\n        while (true) {\n            // Divide the 256-bit number by 10\n            int remainder = 0;\n            bool all_zero_now = true;\n            \n            // Process from most significant byte to least significant\n            for (int i = 31; i >= 0; --i) {\n                int temp = remainder * 256 + temp_data[i];\n                temp_data[i] = temp / 10;\n                remainder = temp % 10;\n                if (temp_data[i] != 0) all_zero_now = false;\n            }\n            \n            // Add the remainder digit to result (in reverse)\n            result = std::to_string(remainder) + result;\n            \n            if (all_zero_now) break;\n        }\n        \n        return result;\n    }\n    \n    // BSATN serialization (already in little-endian)\n    static void serialize(std::vector<uint8_t>& buffer, const u256& value) {\n        buffer.insert(buffer.end(), value.data.begin(), value.data.end());\n    }\n    \n    static u256 deserialize(const uint8_t* bytes) {\n        return u256(bytes);\n    }\n    \n    // BSATN methods (implemented in types_bsatn.h)\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n    void bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader);\n};\n\n// 256-bit signed integer\nstruct i256 {\n    std::array<uint8_t, 32> data;\n    \n    i256() { data.fill(0); }\n    \n    explicit i256(const uint8_t* bytes) {\n        std::memcpy(data.data(), bytes, 32);\n    }\n    \n    // Constructor from 4 uint64_t values (big-endian order: word3 is most significant)\n    i256(uint64_t word3, uint64_t word2, uint64_t word1, uint64_t word0) {\n        // Store in little-endian byte order\n        for (int i = 0; i < 8; ++i) {\n            data[i] = (word0 >> (i * 8)) & 0xFF;\n            data[8 + i] = (word1 >> (i * 8)) & 0xFF;\n            data[16 + i] = (word2 >> (i * 8)) & 0xFF;\n            data[24 + i] = (word3 >> (i * 8)) & 0xFF;\n        }\n    }\n    \n    bool operator==(const i256& other) const {\n        return data == other.data;\n    }\n    \n    bool operator!=(const i256& other) const {\n        return !(*this == other);\n    }\n    \n    // Convert to string using arbitrary precision decimal conversion\n    std::string to_string() const {\n        // Check if this is a negative number (MSB set)\n        bool is_negative = (data[31] & 0x80) != 0;\n        \n        if (is_negative) {\n            // Create positive version using two's complement\n            i256 positive_version;\n            int carry = 1;\n            for (int i = 0; i < 32; ++i) {\n                int temp = (~data[i] & 0xFF) + carry;\n                positive_version.data[i] = temp & 0xFF;\n                carry = temp >> 8;\n            }\n            \n            // Convert positive version to string and add minus sign\n            u256 as_u256;\n            std::copy(positive_version.data.begin(), positive_version.data.end(), as_u256.data.begin());\n            return \"-\" + as_u256.to_string();\n        } else {\n            // Convert as unsigned\n            u256 as_u256;\n            std::copy(data.begin(), data.end(), as_u256.data.begin());\n            return as_u256.to_string();\n        }\n    }\n    \n    // BSATN serialization (already in little-endian)\n    static void serialize(std::vector<uint8_t>& buffer, const i256& value) {\n        buffer.insert(buffer.end(), value.data.begin(), value.data.end());\n    }\n    \n    static i256 deserialize(const uint8_t* bytes) {\n        return i256(bytes);\n    }\n    \n    // BSATN methods (implemented in types_bsatn.h)\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n    void bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader);\n};\n\n// Type aliases with uppercase names for compatibility\nusing U128 = u128;\nusing I128 = i128;\nusing U256 = u256;\nusing I256 = i256;\n\n// =============================================================================\n// CONNECTION ID TYPE (defined after u128)\n// =============================================================================\n\n/**\n * ConnectionId represents a connection identifier in SpacetimeDB.\n * \n * IMPORTANT: ConnectionId uses u128 type to match Rust implementation.\n * This was fixed from the previous uint64_t implementation that caused\n * runtime serialization crashes.\n * \n * Special type tag: \"__connection_id__\"\n * Underlying type: U128 (AlgebraicTypeTag = 15)\n * Serialization: write_u128_le/read_u128_le\n */\nstruct ConnectionId {\n    u128 id;\n    \n    ConnectionId() : id() {}\n    explicit ConnectionId(uint64_t connection_id) : id(connection_id) {}\n    explicit ConnectionId(const u128& connection_id) : id(connection_id) {}\n    \n    bool operator==(const ConnectionId& other) const { return id == other.id; }\n    bool operator!=(const ConnectionId& other) const { return id != other.id; }\n    bool operator<(const ConnectionId& other) const { \n        return (id.high < other.id.high) || (id.high == other.id.high && id.low < other.id.low);\n    }\n    \n    // Convert to string as hex representation (32 hex chars for 16 bytes)\n    std::string to_string() const {\n        std::ostringstream oss;\n        oss << std::hex << std::setfill('0');\n        \n        // Output as 32-character hex string (little-endian byte order)\n        // Convert low 64 bits first\n        for (int i = 0; i < 8; ++i) {\n            uint8_t byte = (id.low >> (i * 8)) & 0xFF;\n            oss << std::setw(2) << static_cast<unsigned int>(byte);\n        }\n        \n        // Convert high 64 bits\n        for (int i = 0; i < 8; ++i) {\n            uint8_t byte = (id.high >> (i * 8)) & 0xFF;\n            oss << std::setw(2) << static_cast<unsigned int>(byte);\n        }\n        \n        return oss.str();\n    }\n    \n    // BSATN serialization (implemented below)\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n    void bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader);\n};\n\n// Placeholder type aliases for BSATN compatibility\nusing u256_placeholder = u256;\nusing i256_placeholder = i256;\n\n// Address is an alias for Identity in the current implementation\nclass Identity;  // Forward declaration\nusing Address = Identity;\n\n// =============================================================================\n// SCHEDULE AT TYPE FORWARD DECLARATION\n// =============================================================================\n\n// ScheduleAt is defined in schedule_at.h to avoid circular dependencies\nclass ScheduleAt;\n\n// =============================================================================\n// TIME DURATION TYPE\n// =============================================================================\n\n// Time duration is defined in time_duration.h\n/*\nstruct TimeDuration {\n    uint64_t micros;\n    \n    TimeDuration() : micros(0) {}\n    explicit TimeDuration(uint64_t us) : micros(us) {}\n    \n    // Constructor from seconds and nanoseconds\n    TimeDuration(uint64_t seconds, uint32_t nanos) \n        : micros(seconds * 1000000 + nanos / 1000) {}\n    \n    static TimeDuration from_micros(uint64_t us) { return TimeDuration(us); }\n    static TimeDuration from_millis(uint64_t ms) { return TimeDuration(ms * 1000); }\n    static TimeDuration from_seconds(uint64_t s) { return TimeDuration(s * 1000000); }\n    \n    uint64_t to_micros() const { return micros; }\n    uint64_t to_millis() const { return micros / 1000; }\n    uint64_t to_seconds() const { return micros / 1000000; }\n    \n    TimeDuration operator+(const TimeDuration& other) const {\n        return TimeDuration(micros + other.micros);\n    }\n    \n    TimeDuration operator-(const TimeDuration& other) const {\n        return TimeDuration(micros - other.micros);\n    }\n    \n    bool operator==(const TimeDuration& other) const {\n        return micros == other.micros;\n    }\n    \n    bool operator!=(const TimeDuration& other) const {\n        return !(*this == other);\n    }\n    \n    bool operator<(const TimeDuration& other) const {\n        return micros < other.micros;\n    }\n    \n    // BSATN serialization (as u64)\n    static void serialize(std::vector<uint8_t>& buffer, const TimeDuration& value) {\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value.micros >> (i * 8)) & 0xFF);\n        }\n    }\n    \n    static TimeDuration deserialize(const uint8_t* data) {\n        uint64_t micros = 0;\n        for (int i = 0; i < 8; ++i) {\n            micros |= static_cast<uint64_t>(data[i]) << (i * 8);\n        }\n        return TimeDuration(micros);\n    }\n    \n    // BSATN serialization methods are implemented via bsatn_traits in type_extensions.h\n    // void bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const;\n    // static TimeDuration bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader);\n};\n*/\n\n// Timestamp operations with TimeDuration are already defined in timestamp.h\n// We'll use the existing Timestamp class operations\n\n// =============================================================================\n// BSATN SERIALIZATION TRAITS\n// =============================================================================\n\n// Primary template for BSATN serialization\ntemplate<typename T>\nstruct BsatnSerializer {\n    static void serialize(std::vector<uint8_t>& buffer, const T& value);\n    static T deserialize(const uint8_t* data, size_t& offset);\n};\n\n// Specializations for primitive types\ntemplate<>\nstruct BsatnSerializer<uint8_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const uint8_t& value) {\n        buffer.push_back(value);\n    }\n    \n    static uint8_t deserialize(const uint8_t* data, size_t& offset) {\n        return data[offset++];\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<uint16_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const uint16_t& value) {\n        buffer.push_back(value & 0xFF);\n        buffer.push_back((value >> 8) & 0xFF);\n    }\n    \n    static uint16_t deserialize(const uint8_t* data, size_t& offset) {\n        uint16_t value = data[offset] | (data[offset + 1] << 8);\n        offset += 2;\n        return value;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<uint32_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const uint32_t& value) {\n        for (int i = 0; i < 4; ++i) {\n            buffer.push_back((value >> (i * 8)) & 0xFF);\n        }\n    }\n    \n    static uint32_t deserialize(const uint8_t* data, size_t& offset) {\n        uint32_t value = 0;\n        for (int i = 0; i < 4; ++i) {\n            value |= static_cast<uint32_t>(data[offset++]) << (i * 8);\n        }\n        return value;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<uint64_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const uint64_t& value) {\n        for (int i = 0; i < 8; ++i) {\n            buffer.push_back((value >> (i * 8)) & 0xFF);\n        }\n    }\n    \n    static uint64_t deserialize(const uint8_t* data, size_t& offset) {\n        uint64_t value = 0;\n        for (int i = 0; i < 8; ++i) {\n            value |= static_cast<uint64_t>(data[offset++]) << (i * 8);\n        }\n        return value;\n    }\n};\n\n// Signed integers\ntemplate<>\nstruct BsatnSerializer<int8_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const int8_t& value) {\n        buffer.push_back(static_cast<uint8_t>(value));\n    }\n    \n    static int8_t deserialize(const uint8_t* data, size_t& offset) {\n        return static_cast<int8_t>(data[offset++]);\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<int16_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const int16_t& value) {\n        BsatnSerializer<uint16_t>::serialize(buffer, static_cast<uint16_t>(value));\n    }\n    \n    static int16_t deserialize(const uint8_t* data, size_t& offset) {\n        return static_cast<int16_t>(BsatnSerializer<uint16_t>::deserialize(data, offset));\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<int32_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const int32_t& value) {\n        BsatnSerializer<uint32_t>::serialize(buffer, static_cast<uint32_t>(value));\n    }\n    \n    static int32_t deserialize(const uint8_t* data, size_t& offset) {\n        return static_cast<int32_t>(BsatnSerializer<uint32_t>::deserialize(data, offset));\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<int64_t> {\n    static void serialize(std::vector<uint8_t>& buffer, const int64_t& value) {\n        BsatnSerializer<uint64_t>::serialize(buffer, static_cast<uint64_t>(value));\n    }\n    \n    static int64_t deserialize(const uint8_t* data, size_t& offset) {\n        return static_cast<int64_t>(BsatnSerializer<uint64_t>::deserialize(data, offset));\n    }\n};\n\n// Boolean\ntemplate<>\nstruct BsatnSerializer<bool> {\n    static void serialize(std::vector<uint8_t>& buffer, const bool& value) {\n        buffer.push_back(value ? 1 : 0);\n    }\n    \n    static bool deserialize(const uint8_t* data, size_t& offset) {\n        return data[offset++] != 0;\n    }\n};\n\n// Floating point\ntemplate<>\nstruct BsatnSerializer<float> {\n    static void serialize(std::vector<uint8_t>& buffer, const float& value) {\n        uint32_t bits;\n        std::memcpy(&bits, &value, sizeof(float));\n        BsatnSerializer<uint32_t>::serialize(buffer, bits);\n    }\n    \n    static float deserialize(const uint8_t* data, size_t& offset) {\n        uint32_t bits = BsatnSerializer<uint32_t>::deserialize(data, offset);\n        float value;\n        std::memcpy(&value, &bits, sizeof(float));\n        return value;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<double> {\n    static void serialize(std::vector<uint8_t>& buffer, const double& value) {\n        uint64_t bits;\n        std::memcpy(&bits, &value, sizeof(double));\n        BsatnSerializer<uint64_t>::serialize(buffer, bits);\n    }\n    \n    static double deserialize(const uint8_t* data, size_t& offset) {\n        uint64_t bits = BsatnSerializer<uint64_t>::deserialize(data, offset);\n        double value;\n        std::memcpy(&value, &bits, sizeof(double));\n        return value;\n    }\n};\n\n// String\ntemplate<>\nstruct BsatnSerializer<std::string> {\n    static void serialize(std::vector<uint8_t>& buffer, const std::string& value) {\n        BsatnSerializer<uint32_t>::serialize(buffer, static_cast<uint32_t>(value.length()));\n        buffer.insert(buffer.end(), value.begin(), value.end());\n    }\n    \n    static std::string deserialize(const uint8_t* data, size_t& offset) {\n        uint32_t len = BsatnSerializer<uint32_t>::deserialize(data, offset);\n        std::string result(reinterpret_cast<const char*>(data + offset), len);\n        offset += len;\n        return result;\n    }\n};\n\n// Large integers\ntemplate<>\nstruct BsatnSerializer<u128> {\n    static void serialize(std::vector<uint8_t>& buffer, const u128& value) {\n        u128::serialize(buffer, value);\n    }\n    \n    static u128 deserialize(const uint8_t* data, size_t& offset) {\n        u128 result = u128::deserialize(data + offset);\n        offset += 16;\n        return result;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<i128> {\n    static void serialize(std::vector<uint8_t>& buffer, const i128& value) {\n        i128::serialize(buffer, value);\n    }\n    \n    static i128 deserialize(const uint8_t* data, size_t& offset) {\n        i128 result = i128::deserialize(data + offset);\n        offset += 16;\n        return result;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<u256> {\n    static void serialize(std::vector<uint8_t>& buffer, const u256& value) {\n        u256::serialize(buffer, value);\n    }\n    \n    static u256 deserialize(const uint8_t* data, size_t& offset) {\n        u256 result = u256::deserialize(data + offset);\n        offset += 32;\n        return result;\n    }\n};\n\ntemplate<>\nstruct BsatnSerializer<i256> {\n    static void serialize(std::vector<uint8_t>& buffer, const i256& value) {\n        i256::serialize(buffer, value);\n    }\n    \n    static i256 deserialize(const uint8_t* data, size_t& offset) {\n        i256 result = i256::deserialize(data + offset);\n        offset += 32;\n        return result;\n    }\n};\n\n// TimeDuration serializer commented out since TimeDuration is defined in time_duration.h\n/*\ntemplate<>\nstruct BsatnSerializer<TimeDuration> {\n    static void serialize(std::vector<uint8_t>& buffer, const TimeDuration& value) {\n        TimeDuration::serialize(buffer, value);\n    }\n    \n    static TimeDuration deserialize(const uint8_t* data, size_t& offset) {\n        TimeDuration result = TimeDuration::deserialize(data + offset);\n        offset += 8;\n        return result;\n    }\n};\n*/\n\n// Identity and ConnectionId serializers are already defined in their respective headers\n\n// Option<T> serialization (handles both Option<T> and std::optional<T>)\ntemplate<typename T>\nstruct BsatnSerializer<std::optional<T>> {\n    static void serialize(std::vector<uint8_t>& buffer, const std::optional<T>& value) {\n        if (value.has_value()) {\n            buffer.push_back(0); // Some tag\n            BsatnSerializer<T>::serialize(buffer, *value);\n        } else {\n            buffer.push_back(1); // None tag\n        }\n    }\n    \n    static std::optional<T> deserialize(const uint8_t* data, size_t& offset) {\n        uint8_t tag = data[offset++];\n        if (tag == 0) {\n            return BsatnSerializer<T>::deserialize(data, offset);\n        } else {\n            return std::nullopt;\n        }\n    }\n};\n\n// Option<T> serialization (forward to std::optional)\ntemplate<typename T>\nstruct BsatnSerializer<Option<T>> {\n    static void serialize(std::vector<uint8_t>& buffer, const Option<T>& value) {\n        BsatnSerializer<std::optional<T>>::serialize(buffer, value);\n    }\n    \n    static Option<T> deserialize(const uint8_t* data, size_t& offset) {\n        return Option<T>(BsatnSerializer<std::optional<T>>::deserialize(data, offset));\n    }\n};\n\n// Vec<T> serialization\ntemplate<typename T>\nstruct BsatnSerializer<std::vector<T>> {\n    static void serialize(std::vector<uint8_t>& buffer, const std::vector<T>& value) {\n        BsatnSerializer<uint32_t>::serialize(buffer, static_cast<uint32_t>(value.size()));\n        for (const auto& item : value) {\n            BsatnSerializer<T>::serialize(buffer, item);\n        }\n    }\n    \n    static std::vector<T> deserialize(const uint8_t* data, size_t& offset) {\n        uint32_t len = BsatnSerializer<uint32_t>::deserialize(data, offset);\n        std::vector<T> result;\n        result.reserve(len);\n        for (uint32_t i = 0; i < len; ++i) {\n            result.push_back(BsatnSerializer<T>::deserialize(data, offset));\n        }\n        return result;\n    }\n};\n\n// ScheduleAt serialization is defined in schedule_at_impl.h\n\n// =============================================================================\n// TYPE REGISTRATION FOR EXTENDED TYPES\n// =============================================================================\n\n// Type registration for large integers and time types will be handled in\n// the AlgebraicType system. Option<T> and Vec<T> need special handling.\n\n// =============================================================================\n// INLINE IMPLEMENTATIONS THAT REQUIRE COMPLETE TYPES\n// =============================================================================\n\n// TimeDuration BSATN methods will be provided as free functions in a separate header\n// that includes both types.h and bsatn.h\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// BSATN Implementation\n// =============================================================================\n// The implementation of BSATN serialization methods has been moved to types_impl.h\n// to avoid circular dependencies with reader.h and writer.h.\n// Include spacetimedb/bsatn/types_impl.h after including reader.h and writer.h\n// to get the implementations."
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/types_impl.h",
    "content": "#ifndef SPACETIMEDB_BSATN_TYPES_IMPL_H\n#define SPACETIMEDB_BSATN_TYPES_IMPL_H\n\n// This file contains the implementation of BSATN serialization methods\n// for SpacetimeDB types. It must be included after reader.h and writer.h\n// to avoid circular dependencies.\n\n#include \"types.h\"\n#include \"reader.h\"\n#include \"writer.h\"\n#include <sstream>    // For std::ostringstream\n#include <iomanip>    // For std::hex, std::setfill, std::setw\n\nnamespace SpacetimeDB {\n\n// Identity method implementations\ninline Identity::Identity() {\n    value.fill(0);\n}\n\ninline Identity::Identity(const std::array<uint8_t, IDENTITY_SIZE>& bytes) : value(bytes) {\n}\n\ninline const std::array<uint8_t, IDENTITY_SIZE>& Identity::get_bytes() const {\n    return value;\n}\n\ninline std::string Identity::to_hex_string() const {\n    std::ostringstream oss;\n    oss << std::hex << std::setfill('0');\n    \n    // Identity is stored in little-endian format (from syscall),\n    // but needs to be displayed in big-endian (most significant byte first)\n    // to match Rust's to_hex() which uses to_be_bytes()\n    for (int i = IDENTITY_SIZE - 1; i >= 0; --i) {\n        oss << std::setw(2) << static_cast<unsigned int>(value[i]);\n    }\n    \n    return oss.str();\n}\n\ninline bool Identity::operator==(const Identity& other) const {\n    return value == other.value;\n}\n\ninline bool Identity::operator!=(const Identity& other) const {\n    return !(*this == other);\n}\n\ninline bool Identity::operator<(const Identity& other) const {\n    return value < other.value;\n}\n\n// Identity BSATN implementation\ninline void Identity::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    // Write raw bytes without length prefix for fixed-size Identity\n    for (size_t i = 0; i < this->value.size(); ++i) {\n        writer.write_u8(this->value[i]);\n    }\n}\n\ninline void Identity::bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader) {\n    std::vector<uint8_t> bytes = reader.read_fixed_bytes(IDENTITY_SIZE);\n    if (bytes.size() == IDENTITY_SIZE) {\n        std::copy(bytes.begin(), bytes.end(), this->value.data());\n    } else {\n        std::abort();\n    }\n}\n\n// ConnectionId BSATN implementation\ninline void ConnectionId::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    writer.write_u128_le(this->id);\n}\n\ninline void ConnectionId::bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader) {\n    this->id = reader.read_u128_le();\n}\n\n// u256 BSATN implementation\ninline void u256::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    writer.write_u256_le(*this);\n}\n\ninline void u256::bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader) {\n    std::vector<uint8_t> bytes = reader.read_fixed_bytes(sizeof(this->data));\n    if (bytes.size() == sizeof(this->data)) {\n        std::copy(bytes.begin(), bytes.end(), this->data.data());\n    } else {\n        std::abort();\n    }\n}\n\n// i256 BSATN implementation\ninline void i256::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    writer.write_i256_le(*this);\n}\n\ninline void i256::bsatn_deserialize(::SpacetimeDB::bsatn::Reader& reader) {\n    std::vector<uint8_t> bytes = reader.read_fixed_bytes(sizeof(this->data));\n    if (bytes.size() == sizeof(this->data)) {\n        std::copy(bytes.begin(), bytes.end(), this->data.data());\n    } else {\n        std::abort();\n    }\n}\n\n// ScheduleAt BSATN implementation is in schedule_at_impl.h\n\n// Note: u128 and i128 have their own serialize/deserialize static methods in types.h,\n// not bsatn_serialize/deserialize member functions\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_BSATN_TYPES_IMPL_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/uuid.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <array>\n#include <string>\n#include <optional>\n#include <cstring>\n#include <sstream>\n#include <iomanip>\n#include \"types.h\"\n#include \"timestamp.h\"\n\n// Forward declarations\nnamespace SpacetimeDB {\nnamespace bsatn {\n    class Writer;\n    class Reader;\n}\n\n/**\n * A universally unique identifier (UUID).\n * \n * Supports UUID Nil, Max, V4 (random), and V7 (timestamp + counter + random).\n * \n * This type corresponds to SpacetimeDB's Uuid special type, represented as a\n * product type with a single __uuid__ field containing a u128 value.\n */\nclass Uuid {\npublic:\n    /// UUID version enumeration\n    enum class Version {\n        Nil,   ///< The \"nil\" UUID (all zeros)\n        V4,    ///< Version 4: Random\n        V7,    ///< Version 7: Timestamp + counter + random\n        Max    ///< The \"max\" UUID (all ones)\n    };\n\nprivate:\n    u128 __uuid__;  // Internal representation matches SpacetimeDB special type tag\n\npublic:\n    // =========================================================================\n    // Constructors\n    // =========================================================================\n    \n    /// Default constructor creates NIL UUID\n    Uuid() : __uuid__(0, 0) {}\n    \n    /// Construct from u128 value\n    explicit Uuid(u128 value) : __uuid__(value) {}\n    \n    /// Construct from high and low 64-bit values\n    Uuid(uint64_t high, uint64_t low) : __uuid__(high, low) {}\n    \n    // =========================================================================\n    // Constants\n    // =========================================================================\n    \n    // =========================================================================\n    // Constants\n    // =========================================================================\n    \n    /// The nil UUID (all zeros)\n    static Uuid nil() {\n        return Uuid(0, 0);\n    }\n    \n    /// The max UUID (all ones)\n    static Uuid max() {\n        return Uuid(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);\n    }\n    \n    // =========================================================================\n    // Factory Methods\n    // =========================================================================\n    \n    /// Create a UUID from a u128 value\n    static Uuid from_u128(u128 value) {\n        return Uuid(value);\n    }\n    \n    /// Create a UUID from high and low 64-bit values\n    static Uuid from_u64(uint64_t high, uint64_t low) {\n        return Uuid(high, low);\n    }\n    \n    /**\n     * Create a UUID v4 from explicit random bytes.\n     * \n     * This method assumes the bytes are already sufficiently random.\n     * It only sets the appropriate bits for the UUID version and variant.\n     * \n     * @param random_bytes Exactly 16 random bytes\n     * @return A UUID v4\n     */\n    static Uuid from_random_bytes_v4(const std::array<uint8_t, 16>& random_bytes) {\n        std::array<uint8_t, 16> bytes = random_bytes;\n        \n        // Set version bits (version 4)\n        bytes[6] = (bytes[6] & 0x0F) | 0x40;\n        \n        // Set variant bits (RFC 4122)\n        bytes[8] = (bytes[8] & 0x3F) | 0x80;\n        \n        return from_bytes_be(bytes);\n    }\n    \n    /**\n     * Generate a UUID v7 using a monotonic counter, a timestamp, and random bytes.\n     * \n     * The UUID v7 is structured as follows:\n     * ```\n     * ┌───────────────────────────────────────────────┬───────────────────┐\n     * | B0  | B1  | B2  | B3  | B4  | B5              |         B6        |\n     * ├───────────────────────────────────────────────┼───────────────────┤\n     * |                 unix_ts_ms                    |      version 7    |\n     * └───────────────────────────────────────────────┴───────────────────┘\n     * ┌──────────────┬─────────┬──────────────────┬───────────────────────┐\n     * | B7           | B8      | B9  | B10 | B11  | B12 | B13 | B14 | B15 |\n     * ├──────────────┼─────────┼──────────────────┼───────────────────────┤\n     * | counter_high | variant |    counter_low   |        random         |\n     * └──────────────┴─────────┴──────────────────┴───────────────────────┘\n     * ```\n     * \n     * @param counter Reference to monotonic counter (31 bits, wraps around)\n     * @param now Current timestamp\n     * @param random_bytes Exactly 4 random bytes for entropy\n     * @return A UUID v7\n     */\n    static Uuid from_counter_v7(uint32_t& counter, const Timestamp& now, const std::array<uint8_t, 4>& random_bytes) {\n        // Get timestamp in milliseconds since Unix epoch\n        int64_t ts_ms = now.millis_since_epoch();\n        \n        // Monotonic counter value (31 bits) - wrap around on overflow\n        uint32_t counter_val = counter;\n        counter = (counter + 1) & 0x7FFF'FFFF;\n        \n        std::array<uint8_t, 16> bytes = {0};\n        \n        // unix_ts_ms (48 bits, big-endian)\n        bytes[0] = static_cast<uint8_t>((ts_ms >> 40) & 0xFF);\n        bytes[1] = static_cast<uint8_t>((ts_ms >> 32) & 0xFF);\n        bytes[2] = static_cast<uint8_t>((ts_ms >> 24) & 0xFF);\n        bytes[3] = static_cast<uint8_t>((ts_ms >> 16) & 0xFF);\n        bytes[4] = static_cast<uint8_t>((ts_ms >> 8) & 0xFF);\n        bytes[5] = static_cast<uint8_t>(ts_ms & 0xFF);\n        \n        // Version 7 (4 bits in high nibble of byte 6)\n        bytes[6] = 0x70;\n        \n        // Counter bits (31 bits split across bytes 7, 9, 10, 11)\n        bytes[7] = static_cast<uint8_t>((counter_val >> 23) & 0xFF);\n        bytes[9] = static_cast<uint8_t>((counter_val >> 15) & 0xFF);\n        bytes[10] = static_cast<uint8_t>((counter_val >> 7) & 0xFF);\n        bytes[11] = static_cast<uint8_t>((counter_val & 0x7F) << 1);\n        \n        // Variant (RFC 4122, 2 bits in high bits of byte 8)\n        bytes[8] = 0x80;\n        \n        // Random bytes (32 bits)\n        bytes[12] |= random_bytes[0] & 0x7F;\n        bytes[13] = random_bytes[1];\n        bytes[14] = random_bytes[2];\n        bytes[15] = random_bytes[3];\n        \n        return from_bytes_be(bytes);\n    }\n    \n    /**\n     * Parse a UUID from a string representation.\n     * \n     * Supports standard hyphenated format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n     * Both uppercase and lowercase hex digits are accepted.\n     * \n     * @param s UUID string\n     * @return Parsed UUID, or std::nullopt if parsing fails\n     */\n    static std::optional<Uuid> parse_str(const std::string& s) {\n        // Expected format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (36 characters)\n        if (s.length() != 36) {\n            return std::nullopt;\n        }\n        \n        // Check hyphens are in the right positions\n        if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') {\n            return std::nullopt;\n        }\n        \n        std::array<uint8_t, 16> bytes;\n        int byte_idx = 0;\n        \n        // Parse each hex pair\n        for (size_t i = 0; i < s.length() && byte_idx < 16; ++i) {\n            if (s[i] == '-') continue;\n            \n            // Parse two hex digits\n            if (i + 1 >= s.length()) {\n                return std::nullopt;\n            }\n            \n            char high_char = s[i];\n            char low_char = s[i + 1];\n            \n            auto hex_to_int = [](char c) -> int {\n                if (c >= '0' && c <= '9') return c - '0';\n                if (c >= 'a' && c <= 'f') return c - 'a' + 10;\n                if (c >= 'A' && c <= 'F') return c - 'A' + 10;\n                return -1;\n            };\n            \n            int high = hex_to_int(high_char);\n            int low = hex_to_int(low_char);\n            \n            if (high < 0 || low < 0) {\n                return std::nullopt;\n            }\n            \n            bytes[byte_idx++] = static_cast<uint8_t>((high << 4) | low);\n            ++i; // Skip the second character of the pair\n        }\n        \n        if (byte_idx != 16) {\n            return std::nullopt;\n        }\n        \n        return from_bytes_be(bytes);\n    }\n    \n    // =========================================================================\n    // Accessors\n    // =========================================================================\n    \n    /// Get the underlying u128 value\n    u128 as_u128() const {\n        return __uuid__;\n    }\n    \n    /**\n     * Get the version of this UUID.\n     * \n     * @return UUID version, or std::nullopt if version is not recognized\n     */\n    std::optional<Version> get_version() const {\n        // Special cases for NIL and MAX\n        if (*this == nil()) return Version::Nil;\n        if (*this == max()) return Version::Max;\n        \n        // Extract version from byte 6 (high nibble)\n        std::array<uint8_t, 16> bytes = to_bytes_be();\n        uint8_t version = (bytes[6] >> 4) & 0x0F;\n        \n        switch (version) {\n            case 4: return Version::V4;\n            case 7: return Version::V7;\n            default: return std::nullopt;\n        }\n    }\n    \n    /**\n     * Extract the 31-bit monotonic counter from a UUID v7.\n     * \n     * Intended for testing and debugging.\n     * \n     * @return The counter value (0 to 2^31-1)\n     */\n    int32_t get_counter() const {\n        std::array<uint8_t, 16> bytes = to_bytes_be();\n        \n        uint32_t high = bytes[7];        // bits 30..23\n        uint32_t mid1 = bytes[9];        // bits 22..15\n        uint32_t mid2 = bytes[10];       // bits 14..7\n        uint32_t low = bytes[11] >> 1;   // bits 6..0\n        \n        // Reconstruct 31-bit counter\n        return static_cast<int32_t>((high << 23) | (mid1 << 15) | (mid2 << 7) | low);\n    }\n    \n    // =========================================================================\n    // String Conversion\n    // =========================================================================\n    \n    /**\n     * Convert UUID to standard string representation.\n     * \n     * Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (lowercase hex)\n     * \n     * @return UUID string\n     */\n    std::string to_string() const {\n        std::array<uint8_t, 16> bytes = to_bytes_be();\n        \n        std::ostringstream oss;\n        oss << std::hex << std::setfill('0');\n        \n        // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n        for (int i = 0; i < 16; ++i) {\n            oss << std::setw(2) << static_cast<unsigned>(bytes[i]);\n            if (i == 3 || i == 5 || i == 7 || i == 9) {\n                oss << '-';\n            }\n        }\n        \n        return oss.str();\n    }\n    \n    // =========================================================================\n    // Comparison Operators\n    // =========================================================================\n    \n    bool operator==(const Uuid& other) const {\n        return __uuid__ == other.__uuid__;\n    }\n    \n    bool operator!=(const Uuid& other) const {\n        return __uuid__ != other.__uuid__;\n    }\n    \n    bool operator<(const Uuid& other) const {\n        // Compare as unsigned integers\n        if (__uuid__.high != other.__uuid__.high) {\n            return __uuid__.high < other.__uuid__.high;\n        }\n        return __uuid__.low < other.__uuid__.low;\n    }\n    \n    bool operator<=(const Uuid& other) const {\n        return *this < other || *this == other;\n    }\n    \n    bool operator>(const Uuid& other) const {\n        return !(*this <= other);\n    }\n    \n    bool operator>=(const Uuid& other) const {\n        return !(*this < other);\n    }\n    \n    // =========================================================================\n    // BSATN Serialization\n    // =========================================================================\n    \n    void bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const;\n    static Uuid bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader);\n\nprivate:\n    // =========================================================================\n    // Internal Helpers\n    // =========================================================================\n    \n    /// Convert UUID to big-endian byte array\n    std::array<uint8_t, 16> to_bytes_be() const {\n        std::array<uint8_t, 16> bytes;\n        \n        // u128 is stored in native byte order, convert to big-endian\n        // Big-endian: most significant byte first\n        // high comes first (bytes 0-7), then low (bytes 8-15)\n        for (int i = 0; i < 8; ++i) {\n            bytes[7 - i] = static_cast<uint8_t>((__uuid__.high >> (i * 8)) & 0xFF);\n        }\n        for (int i = 0; i < 8; ++i) {\n            bytes[15 - i] = static_cast<uint8_t>((__uuid__.low >> (i * 8)) & 0xFF);\n        }\n        \n        return bytes;\n    }\n    \n    /// Create UUID from big-endian byte array\n    static Uuid from_bytes_be(const std::array<uint8_t, 16>& bytes) {\n        u128 value;\n        \n        // Convert big-endian bytes to u128\n        value.low = 0;\n        value.high = 0;\n        \n        // high comes from bytes 0-7, low from bytes 8-15\n        for (int i = 0; i < 8; ++i) {\n            value.high |= static_cast<uint64_t>(bytes[7 - i]) << (i * 8);\n        }\n        for (int i = 0; i < 8; ++i) {\n            value.low |= static_cast<uint64_t>(bytes[15 - i]) << (i * 8);\n        }\n        \n        return Uuid(value);\n    }\n};\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// BSATN Implementation\n// =============================================================================\n\n#include \"writer.h\"\n#include \"reader.h\"\n#include \"traits.h\"\n#include \"algebraic_type.h\"\n\nnamespace SpacetimeDB {\n\n// Uuid BSATN implementation\n// UUIDs are serialized as a product with a single __uuid__ field containing a u128\ninline void Uuid::bsatn_serialize(SpacetimeDB::bsatn::Writer& writer) const {\n    // Serialize the u128 value directly (little-endian)\n    writer.write_u64_le(__uuid__.low);\n    writer.write_u64_le(__uuid__.high);\n}\n\ninline Uuid Uuid::bsatn_deserialize(SpacetimeDB::bsatn::Reader& reader) {\n    // Deserialize the u128 value directly (little-endian)\n    uint64_t low = reader.read_u64_le();\n    uint64_t high = reader.read_u64_le();\n    return Uuid(high, low);\n}\n\n} // namespace SpacetimeDB\n\n// Note: bsatn_traits specialization for Uuid is defined in type_extensions.h\n// to ensure consistent handling with other special types\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/bsatn/writer.h",
    "content": "#ifndef SPACETIMEDB_BSATN_WRITER_H\n#define SPACETIMEDB_BSATN_WRITER_H\n\n#include <vector>\n#include <string>\n#include <cstdint>\n#include <stdexcept>\n#include <optional>\n#include <functional>\n#include <type_traits>\n#include <variant>\n#include <concepts>\n#include <span>\n// uint128_placeholder.h removed - types are in spacetimedb/types.h\n#include \"types.h\"\n\nnamespace SpacetimeDB::bsatn {\n\n    // Forward declarations\n    template<typename T> struct bsatn_traits;\n    template<typename T> void serialize(class Writer& w, const T& value);\n\n    class Writer {\n    private:\n        std::vector<uint8_t> buffer;  // Internal buffer when not using external\n        std::vector<uint8_t>* buffer_;  // Pointer to external buffer (if any)\n        \n    public:\n        Writer() : buffer_(nullptr) {}\n        explicit Writer(std::vector<uint8_t>& buffer) : buffer_(&buffer) {}\n\n        // Template method for writing primitive types using C++20 concepts\n        template<typename T>\n            requires (std::is_arithmetic_v<T> && sizeof(T) > 1)\n        void write_primitive_le(T value) {\n            write_bytes_raw(&value, sizeof(T));\n        }\n\n        // Public API methods (delegates to template where possible)\n        inline void write_bool(bool value) {\n            auto& target_buffer = buffer_ ? *buffer_ : buffer;\n            target_buffer.push_back(value ? 1 : 0);\n        }\n        inline void write_u8(uint8_t value) {\n            auto& target_buffer = buffer_ ? *buffer_ : buffer;\n            target_buffer.push_back(value);\n        }\n        void write_u16_le(uint16_t value) { write_primitive_le(value); }\n        void write_u32_le(uint32_t value) { write_primitive_le(value); }\n        void write_u64_le(uint64_t value) { write_primitive_le(value); }\n        inline void write_u128_le(const SpacetimeDB::u128& value) {\n            write_u64_le(value.low);\n            write_u64_le(value.high);\n        }\n        void write_u256_le(const SpacetimeDB::u256_placeholder& value) {\n            write_bytes_raw(value.data.data(), value.data.size());\n        }\n\n        void write_i8(int8_t value) { write_u8(static_cast<uint8_t>(value)); }\n        void write_i16_le(int16_t value) { write_u16_le(static_cast<uint16_t>(value)); }\n        void write_i32_le(int32_t value) { write_u32_le(static_cast<uint32_t>(value)); }\n        void write_i64_le(int64_t value) { write_u64_le(static_cast<uint64_t>(value)); }\n        inline void write_i128_le(const SpacetimeDB::i128& value) {\n            write_u64_le(value.low);\n            write_u64_le(static_cast<uint64_t>(value.high));\n        }\n        void write_i256_le(const SpacetimeDB::i256_placeholder& value) {\n            write_bytes_raw(value.data.data(), value.data.size());\n        }\n\n        void write_f32_le(float value) { write_primitive_le(value); }\n        void write_f64_le(double value) { write_primitive_le(value); }\n\n        inline void write_string(const std::string& value) {\n            write_u32_le(static_cast<uint32_t>(value.size()));\n            write_bytes_raw(value.data(), value.size());\n        }\n        inline void write_bytes(const std::vector<uint8_t>& value) {\n            write_u32_le(static_cast<uint32_t>(value.size()));\n            write_bytes_raw(value.data(), value.size());\n        }\n\n        template<typename T>\n        void write_optional(const std::optional<T>& opt_value) {\n            // SpacetimeDB uses non-standard Option discriminants:\n            // Some = 0, None = 1 (reversed from standard Rust)\n            if (opt_value.has_value()) {\n                write_u8(0);  // Some = 0 (SpacetimeDB convention)\n                serialize(*this, *opt_value);\n            }\n            else {\n                write_u8(1);  // None = 1 (SpacetimeDB convention)\n            }\n        }\n\n        template<typename T>\n        void write_vector(const std::vector<T>& vec) {\n            write_u32_le(static_cast<uint32_t>(vec.size()));\n            for (const auto& item : vec) {\n                serialize(*this, item);\n            }\n        }\n\n        inline void write_vector_byte(const std::vector<uint8_t>& vec) {\n            write_bytes(vec);\n        }\n\n        // Generic serialize member function\n        template<typename T>\n        void serialize_member(const T& value) {\n            serialize(*this, value);\n        }\n        \n        void write_vec_len(size_t len) {\n            // Write length as varint\n            write_u32_le(static_cast<uint32_t>(len));\n        }\n\n        const std::vector<uint8_t>& get_buffer() const { \n            return buffer_ ? *buffer_ : buffer; \n        }\n        \n        std::vector<uint8_t>&& take_buffer() { \n            return std::move(buffer_ ? *buffer_ : buffer); \n        }\n        \n        // Public method to write raw bytes without length prefix\n        // This is needed for already-serialized data that has its own length encoding\n        inline void write_raw_bytes(const std::vector<uint8_t>& data) {\n            auto& target_buffer = buffer_ ? *buffer_ : buffer;\n            target_buffer.insert(target_buffer.end(), data.begin(), data.end());\n        }\n        \n        inline void write_raw_bytes(const uint8_t* data, size_t size) {\n            auto& target_buffer = buffer_ ? *buffer_ : buffer;\n            target_buffer.insert(target_buffer.end(), data, data + size);\n        }\n\n    private:\n        inline void write_bytes_raw(const void* data, size_t size) {\n            const uint8_t* bytes = static_cast<const uint8_t*>(data);\n            auto& target_buffer = buffer_ ? *buffer_ : buffer;\n            target_buffer.insert(target_buffer.end(), bytes, bytes + size);\n        }\n    };\n\n    // Helper to detect if type has bsatn_serialize method\n    template<typename T, typename = void>\n    struct has_bsatn_serialize : std::false_type {};\n    \n    template<typename T>\n    struct has_bsatn_serialize<T, std::void_t<decltype(std::declval<const T&>().bsatn_serialize(std::declval<Writer&>()))>> \n        : std::true_type {};\n\n    // Generic serialize implementation for types with bsatn_serialize\n    template<typename T>\n    inline void serialize(Writer& w, const T& value) {\n        if constexpr (has_bsatn_serialize<T>::value) {\n            value.bsatn_serialize(w);\n        } else {\n            // Fall back to bsatn_traits\n            bsatn_traits<T>::serialize(w, value);\n        }\n    }\n\n    // Inline implementations of serialize functions\n    inline void serialize(Writer& w, bool value) {\n        w.write_bool(value);\n    }\n\n    inline void serialize(Writer& w, uint8_t value) {\n        w.write_u8(value);\n    }\n\n    inline void serialize(Writer& w, uint16_t value) {\n        w.write_u16_le(value);\n    }\n\n    inline void serialize(Writer& w, uint32_t value) {\n        w.write_u32_le(value);\n    }\n\n    inline void serialize(Writer& w, uint64_t value) {\n        w.write_u64_le(value);\n    }\n\n    inline void serialize(Writer& w, const SpacetimeDB::u128& value) {\n        w.write_u128_le(value);\n    }\n\n    inline void serialize(Writer& w, const SpacetimeDB::u256_placeholder& value) {\n        w.write_u256_le(value);\n    }\n\n    inline void serialize(Writer& w, int8_t value) {\n        w.write_i8(value);\n    }\n\n    inline void serialize(Writer& w, int16_t value) {\n        w.write_i16_le(value);\n    }\n\n    inline void serialize(Writer& w, int32_t value) {\n        w.write_i32_le(value);\n    }\n\n    inline void serialize(Writer& w, int64_t value) {\n        w.write_i64_le(value);\n    }\n\n    inline void serialize(Writer& w, const SpacetimeDB::i128& value) {\n        w.write_i128_le(value);\n    }\n\n    inline void serialize(Writer& w, const SpacetimeDB::i256_placeholder& value) {\n        w.write_i256_le(value);\n    }\n\n    inline void serialize(Writer& w, float value) {\n        w.write_f32_le(value);\n    }\n\n    inline void serialize(Writer& w, double value) {\n        w.write_f64_le(value);\n    }\n\n    inline void serialize(Writer& w, const std::string& value) {\n        w.write_string(value);\n    }\n\n    inline void serialize(Writer& w, const std::vector<uint8_t>& value) {\n        w.write_bytes(value);\n    }\n\n    inline void serialize(Writer&, std::monostate) {\n        // Nothing to serialize for monostate\n    }\n\n    // SDK type serialization - removed to avoid circular dependency\n    // Types with bsatn_serialize methods will be handled by the generic serialize function\n\n    template<typename T>\n    inline void serialize(Writer& w, const std::optional<T>& opt_value) {\n        w.write_optional(opt_value);\n    }\n\n    template<typename T>\n    inline void serialize(Writer& w, const std::vector<T>& vec) {\n        w.write_vector(vec);\n    }\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_BSATN_WRITER_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/client_visibility_filter.h",
    "content": "#pragma once\n\n#include <string>\n\nnamespace SpacetimeDB {\n\n/// A row-level security filter,\n/// which can be registered using the SPACETIMEDB_CLIENT_VISIBILITY_FILTER macro.\n///\n/// Currently, the only valid value for a filter is a Filter::Sql.\n/// This is a filter written as a SQL query. Rows that match this query will be made visible to clients.\n///\n/// The query must be of the form `SELECT * FROM table` or `SELECT table.* from table`,\n/// followed by any number of `JOIN` clauses and a `WHERE` clause.\n/// If the query includes any `JOIN`s, it must be in the form `SELECT table.* FROM table`.\n/// In any case, the query must select all of the columns from a single table, and nothing else.\n///\n/// SQL queries are not checked for syntactic or semantic validity\n/// until they are processed by the SpacetimeDB host.\n/// This means that errors in queries used as SPACETIMEDB_CLIENT_VISIBILITY_FILTER rules\n/// will be reported during `spacetime publish`, not at compile time.\nclass Filter {\nprivate:\n    const char* sql_text_;\n\npublic:\n    /// Create a SQL-based client visibility filter\n    static Filter Sql(const char* sql) {\n        return Filter(sql);\n    }\n\n    /// Get the SQL text for this filter\n    const char* sql_text() const {\n        return sql_text_;\n    }\n\nprivate:\n    explicit Filter(const char* sql) : sql_text_(sql) {}\n};\n\n} // namespace SpacetimeDB"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/database.h",
    "content": "#ifndef SPACETIMEDB_DATABASE_H\n#define SPACETIMEDB_DATABASE_H\n\n#include \"table.h\"\n// Removed: internal/field_metadata.h (consolidated into field_registration.h)  \n#include \"abi/FFI.h\"\n#include \"bsatn/bsatn.h\"\n#include \"bsatn/traits.h\"\n#include \"logger.h\"\n#include <type_traits>\n#include <string>\n#include <stdexcept>\n#include <string_view>\n#include <optional>\n#include <vector>\n\n// Include field-based accessor system (SpacetimeDB standard pattern)\n// #include \"field_accessors.h\" // Removed - field accessors are now in table_with_constraints.h\n\n// Forward declarations to avoid circular includes\nnamespace SpacetimeDB {\nnamespace Internal {\n    class Module;\n}\n\n// Field constraint flags - must match Rust's ColumnAttribute bits  \nenum class FieldConstraint : uint32_t {\n    None = 0,\n    Indexed = 0b0001,                         // 1\n    AutoInc = 0b0010,                         // 2\n    Unique = Indexed | 0b0100,                // 5 (Indexed + Unique bit)\n    PrimaryKey = Unique | 0b1000,             // 13 (Unique + PrimaryKey bit)\n    Identity = Unique | AutoInc,              // 7 (Unique + AutoInc)\n    PrimaryKeyAuto = PrimaryKey | AutoInc,    // 15 (PrimaryKey + AutoInc)\n    NotNull = 1 << 4                          // 16 (not used in Rust but kept for future)\n};\n\ninline FieldConstraint operator|(FieldConstraint a, FieldConstraint b) {\n    return static_cast<FieldConstraint>(\n        static_cast<uint32_t>(a) | static_cast<uint32_t>(b)\n    );\n}\n\nconstexpr bool has_constraint(FieldConstraint field, FieldConstraint constraint) {\n    return (static_cast<uint32_t>(field) & static_cast<uint32_t>(constraint)) != 0;\n}\n\n// Forward declaration for tag-based accessors\ntemplate<typename T>\nstruct TableTag;\n\n\n// Forward declaration for field tags\ntemplate<typename TableType, typename FieldType, FieldConstraint Constraint, bool IsEventTable>\nstruct FieldTag;\n\n// Forward declarations for typed field accessors\ntemplate<typename TableType, typename FieldType>\nclass TypedPrimaryKeyAccessor;\n\ntemplate<typename TableType, typename FieldType>\nclass TypedUniqueAccessor;\n\ntemplate<typename TableType, typename FieldType>\nclass TypedIndexedAccessor;\n\ntemplate<typename TableType, typename FieldType>\nclass TypedRegularAccessor;\n\n// Forward declaration for multi-column index support\ntemplate<typename TableType>\nstruct MultiColumnIndexTag;\n\ntemplate<typename TableType>\nclass TypedMultiColumnIndexAccessor;\n\n// Constraint system definitions for primary key operations\n\n// Field constraint info structure\nstruct FieldConstraintInfo {\n    const char* field_name;\n    FieldConstraint constraints;\n    const char* index_name = nullptr;  // For named indexes\n    std::vector<const char*> column_names;  // For multi-column indexes\n    \n    // Constructor for basic constraints\n    FieldConstraintInfo(const char* name, FieldConstraint c) \n        : field_name(name), constraints(c), index_name(nullptr) {}\n    \n    // Constructor for named indexes\n    FieldConstraintInfo(const char* name, FieldConstraint c, const char* idx_name) \n        : field_name(name), constraints(c), index_name(idx_name) {}\n    \n    // Constructor for multi-column indexes\n    FieldConstraintInfo(std::initializer_list<const char*> columns, FieldConstraint c, const char* idx_name)\n        : field_name(nullptr), constraints(c), index_name(idx_name), column_names(columns) {}\n};\n\n// Table accessor that resolves table names at runtime with constraint-aware operations\ntemplate<typename T>\nclass TableAccessor {\nprotected:\n    mutable std::optional<TableId> table_id_;\n    std::string table_name_;\n    \n    TableId resolve_table_id() const {\n        if (!table_id_.has_value()) {\n            // Resolve table ID from name\n            \n            // Use the provided table name or lookup from module metadata\n            std::string name_to_use = table_name_;\n            \n            if (name_to_use.empty()) {\n                // For now, require explicit table names\n                // TODO: Add runtime table name lookup\n                LOG_FATAL(\"Table name is required\");\n            }\n            \n            TableId id;\n            Status status = ::table_id_from_name(\n                reinterpret_cast<const uint8_t*>(name_to_use.c_str()),\n                name_to_use.length(),\n                &id\n            );\n            \n            if (SpacetimeDB::is_error(status)) {\n                LOG_FATAL(\"Table not found: \" + name_to_use);\n            }\n            table_id_ = id;\n        }\n        return *table_id_;\n    }\n    \n    \npublic:\n    // Constructor that accepts a table name\n    TableAccessor() = default;\n    explicit TableAccessor(const std::string& table_name) : table_name_(table_name) {}\n    \n    // Insert a row and return it with any auto-generated fields\n    T insert(const T& row) const {\n        return get_table().insert(row);\n    }\n    \n    // Count all rows in the table\n    uint64_t count() const {\n        return get_table().count();\n    }\n    \n    // Delete rows matching a value\n    uint32_t delete_by_value(const T& value) const {\n        return get_table().delete_by_value(value);\n    }\n    \n    // Internal helper used by typed field accessors for atomic updates\n    uint32_t update_by_value(const T& old_value, const T& new_value) const {\n        // Delegate to Table<T> for atomic delete + insert\n        uint32_t deleted_count = get_table().delete_by_value(old_value);\n        if (deleted_count > 0) {\n            for (uint32_t i = 0; i < deleted_count; ++i) {\n                get_table().insert(new_value);\n            }\n        }\n        return deleted_count;\n    }\n\npublic:\n    // Get or create cached Table<T> instance\n    SpacetimeDB::Table<T> get_table() const {\n        return SpacetimeDB::Table<T>(resolve_table_id());\n    }\n    \n    // Iterate over all rows in the table\n    SpacetimeDB::Table<T> table() const {\n        return get_table();\n    }\n    \n    // Range-based for loop support\n    auto begin() const { return get_table().begin(); }\n    auto end() const { return get_table().end(); }\n};\n\n/**\n * @brief Database context with name-based table accessors (RECOMMENDED API)\n * \n * DatabaseContext provides the recommended interface for all table operations\n * in C++ modules. It automatically handles table ID resolution and provides\n * a reliable wrapper around the low-level Table API.\n * \n * @note This is the RECOMMENDED way to perform table operations. Direct Table\n *       construction has known issues with insert operations that can cause crashes.\n * \n * Example usage:\n * @code\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id, std::string name)\n * {\n *     // ALWAYS use DatabaseContext through ctx.db\n *     auto users = ctx.db.table<User>(\"users\");\n *     \n *     // All operations work reliably\n *     User new_user = users.insert({id, name, 30});\n *     uint64_t count = users.count();\n *     \n *     for (const auto& user : users) {\n *         LOG_INFO_F(\"User: %s\", user.name.c_str());\n *     }\n * }\n * @endcode\n */\n// Database context with name-based table accessors\nclass DatabaseContext {\npublic:\n    // Generic table accessor method (type-only, requires explicit table name later)\n    template<typename T>\n    TableAccessor<T> table() const {\n        return TableAccessor<T>{};\n    }\n    \n    // Name-based accessor that returns a configured table accessor\n    template<typename T>\n    TableAccessor<T> table(const char* name) const {\n        return TableAccessor<T>(std::string(name));\n    }\n    \n    // String overload\n    template<typename T>\n    TableAccessor<T> table(const std::string& name) const {\n        return table<T>(name.c_str());\n    }\n    \n    // Tag-based accessor using operator[] (SpacetimeDB standard)\n    template<typename Tag>\n    TableAccessor<typename Tag::type> operator[](const Tag&) const {\n        return TableAccessor<typename Tag::type>(std::string(Tag::__table_name_internal));\n    }\n    \n    // Field tag accessor - NEW: ctx.db[simple_table.id] syntax\n    // Overloaded for each field constraint type\n    template<typename TableType, typename FieldType, bool IsEventTable>\n    TypedPrimaryKeyAccessor<TableType, FieldType> operator[](const FieldTag<TableType, FieldType, FieldConstraint::PrimaryKey, IsEventTable>& field_tag) const {\n        return TypedPrimaryKeyAccessor<TableType, FieldType>(field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    TypedUniqueAccessor<TableType, FieldType> operator[](const FieldTag<TableType, FieldType, FieldConstraint::Unique, IsEventTable>& field_tag) const {\n        return TypedUniqueAccessor<TableType, FieldType>(field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    TypedIndexedAccessor<TableType, FieldType> operator[](const FieldTag<TableType, FieldType, FieldConstraint::Indexed, IsEventTable>& field_tag) const {\n        return TypedIndexedAccessor<TableType, FieldType>(field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    TypedRegularAccessor<TableType, FieldType> operator[](const FieldTag<TableType, FieldType, FieldConstraint::None, IsEventTable>& field_tag) const {\n        return TypedRegularAccessor<TableType, FieldType>(field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    // Multi-column index accessor - NEW: ctx.db[score.by_player_and_level] syntax\n    template<typename TableType>\n    TypedMultiColumnIndexAccessor<TableType> operator[](const MultiColumnIndexTag<TableType>& index_tag) const {\n        return TypedMultiColumnIndexAccessor<TableType>(index_tag.table_name, index_tag.index_name, index_tag.column_list);\n    }\n};\n\n\n} // namespace SpacetimeDB\n\n// Use spacetimedb namespace for consistency\nnamespace spacetimedb {\n    template<typename T>\n    using TableAccessor = SpacetimeDB::TableAccessor<T>;\n}\n\n#endif // SPACETIMEDB_DATABASE_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/enum_macro.h",
    "content": "#pragma once\n\n#include \"spacetimedb/bsatn/traits.h\"\n#include \"spacetimedb/bsatn/sum_type.h\"\n#include \"spacetimedb/macros.h\"\n#include \"spacetimedb/internal/module_type_registration.h\"\n\n/**\n * @file enum_macro.h\n * @brief Unified SpacetimeDB Enum Macros for C++ bindings\n * \n * Provides SPACETIMEDB_ENUM macro with automatic syntax detection to create\n * SpacetimeDB-compatible enum types. The macro supports two distinct patterns:\n * \n * **1. Simple Unit Enums** (C-style enums with named constants):\n * ```cpp\n * SPACETIMEDB_ENUM(Direction, North, South, East, West)\n * ```\n * Generated code:\n * - `enum class Direction : uint8_t { North = 0, South = 1, East = 2, West = 3 }`\n * - Efficient: 1 byte per value\n * - Use when: Variants carry no data\n * \n * **2. Variant Enums** (Rust-style enums with associated data):\n * ```cpp\n * SPACETIMEDB_ENUM(Result, (Ok, uint32_t), (Err, std::string))\n * ```\n * Generated code:\n * - `struct Result` with `std::variant<uint32_t, std::string>`\n * - Tagged union with named accessors\n * - Use when: Variants carry different types of data\n * \n * @section syntax_detection Automatic Syntax Detection\n * \n * The macro automatically detects which pattern you're using:\n * - Simple enum: `SPACETIMEDB_ENUM(Name, Variant1, Variant2, ...)`\n * - Variant enum: `SPACETIMEDB_ENUM(Name, (Variant1, Type1), (Variant2, Type2), ...)`\n * \n * The presence of parentheses `(name, type)` triggers variant enum generation.\n * \n * @section simple_examples Simple Enum Examples\n * \n * ```cpp\n * // Game state enum\n * SPACETIMEDB_ENUM(GameState, Lobby, Playing, Paused, Ended)\n * \n * // Usage:\n * GameState state = GameState::Playing;\n * if (state == GameState::Lobby) { ... }\n * ```\n * \n * ```cpp\n * // Boolean-like enum\n * SPACETIMEDB_ENUM(Status, Active, Inactive)\n * ```\n * \n * @section variant_examples Variant Enum Examples\n * \n * ```cpp\n * // Message types with different payloads\n * SPACETIMEDB_ENUM(Message,\n *     (Text, std::string),\n *     (Image, std::vector<uint8_t>),\n *     (Audio, std::vector<uint8_t>)\n * )\n * \n * // Usage:\n * Message msg = std::string(\"Hello\");  // Implicit construction\n * if (msg.index() == 0) {\n *     auto& text = std::get<std::string>(msg.value);\n * }\n * ```\n * \n * ```cpp\n * // Result type with success/error variants\n * SPACETIMEDB_ENUM(ApiResult,\n *     (Success, UserData),\n *     (NotFound, Unit),\n *     (Error, std::string)\n * )\n * ```\n * \n * @section namespace_qual Namespace Qualification\n * \n * For type reuse across modules, add namespace prefixes:\n * \n * ```cpp\n * SPACETIMEDB_ENUM(Status, Active, Inactive)\n * SPACETIMEDB_NAMESPACE(Status, \"MyModule\")  // Registers as \"MyModule.Status\"\n * ```\n * \n * @note Simple enums are more efficient (1 byte) than variant enums (std::variant overhead)\n * @warning Variant enum alternative order is part of the wire format - don't reorder!\n * \n * @ingroup sdk_macros\n */\n\n// =============================================================================\n// HELPER MACROS FOR VARIANT EXTRACTION\n// =============================================================================\n\n// Extract type from (name, type) pair\n#define VARIANT_TYPE(pair) VARIANT_TYPE_IMPL pair\n#define VARIANT_TYPE_IMPL(name, type) type\n\n// Extract name from (name, type) pair  \n#define VARIANT_NAME(pair) VARIANT_NAME_IMPL pair\n#define VARIANT_NAME_IMPL(name, type) #name\n\n// Separator macros\n#define COMMA() ,\n#define EMPTY()\n\n// =============================================================================\n// UNIFIED ENUM SYSTEM\n// =============================================================================\n\n/**\n * @brief Unified enum macro with automatic syntax detection\n * \n * These macros automatically detect whether you're defining a simple unit enum\n * or a variant enum based on the syntax used, then routes to the appropriate\n * implementation.\n * \n * @param EnumName The name of the enum type to create\n * @param ... Either:\n *   - Simple: List of variant names (e.g., `Red, Green, Blue`)\n *   - Variant: List of (name, type) pairs (e.g., `(Ok, int), (Err, std::string)`)\n * \n * **Decision Tree:**\n * \n * Q: Do your variants carry data?\n * - **No** → Use simple syntax: `SPACETIMEDB_ENUM(Name, Var1, Var2, ...)`\n *   - Generated: `enum class Name : uint8_t`\n *   - Size: 1 byte\n *   - Example: `SPACETIMEDB_ENUM(Color, Red, Green, Blue)`\n * \n * - **Yes** → Use variant syntax: `SPACETIMEDB_ENUM(Name, (Var1, Type1), (Var2, Type2), ...)`\n *   - Generated: `struct Name` with `std::variant<Type1, Type2, ...>`\n *   - Size: sizeof(largest type) + discriminant\n *   - Example: `SPACETIMEDB_ENUM(Result, (Ok, uint32_t), (Err, std::string))`\n * \n * @example Simple enum (no data):\n * @code\n * // Define direction enum\n * SPACETIMEDB_ENUM(Direction, North, South, East, West)\n * \n * // Define table struct that uses the enum\n * struct Player {\n *     uint32_t id;\n *     std::string name;\n *     Direction facing;\n * };\n * SPACETIMEDB_STRUCT(Player, id, name, facing)\n * SPACETIMEDB_TABLE(Player, players, Public)\n * FIELD_PrimaryKey(players, id)\n * \n * // Usage in reducer\n * SPACETIMEDB_REDUCER(void, move_player, ReducerContext ctx, Direction dir) {\n *     if (dir == Direction::North) {\n *         // Move north\n *     }\n * }\n * @endcode\n * \n * @example Variant enum (with data):\n * @code\n * // Define event enum with different payload types\n * SPACETIMEDB_ENUM(GameEvent,\n *     (PlayerJoined, uint32_t),      // player_id\n *     (ChatMessage, std::string),     // message text\n *     (PlayerLeft, Unit)              // no data\n * )\n * \n * // Define table struct that uses the enum\n * struct EventLog {\n *     uint32_t id;\n *     Timestamp timestamp;\n *     GameEvent event;\n * };\n * SPACETIMEDB_STRUCT(EventLog, id, timestamp, event)\n * SPACETIMEDB_TABLE(EventLog, events, Public)\n * FIELD_PrimaryKeyAutoInc(events, id)\n * \n * // Usage in reducer\n * SPACETIMEDB_REDUCER(void, log_event, ReducerContext ctx, GameEvent event) {\n *     if (event.index() == 0) {  // PlayerJoined\n *         auto player_id = std::get<uint32_t>(event.value);\n *         LOG_INFO(\"Player \" + std::to_string(player_id) + \" joined\");\n *     }\n * }\n * @endcode\n * \n * @note The macro uses compile-time detection to determine which implementation to use\n * @note Simple enums serialize as 1 byte; variant enums serialize as tag + data\n */\n\n// Detect if first argument is parenthesized (name, type) pair\n#define SPACETIMEDB_IS_PARENTHESIZED(x) SPACETIMEDB_IS_PARENTHESIZED_IMPL(SPACETIMEDB_IS_PARENTHESIZED_PROBE x)\n#define SPACETIMEDB_IS_PARENTHESIZED_IMPL(...) SPACETIMEDB_IS_PARENTHESIZED_GET_SECOND(__VA_ARGS__, 0)\n#define SPACETIMEDB_IS_PARENTHESIZED_GET_SECOND(a, b, ...) b\n#define SPACETIMEDB_IS_PARENTHESIZED_PROBE(...) ~, 1\n\n// Route to appropriate implementation\n#define SPACETIMEDB_ENUM(EnumName, ...) \\\n    SPACETIMEDB_CONCAT(SPACETIMEDB_ENUM_, \\\n        SPACETIMEDB_IS_PARENTHESIZED(SPACETIMEDB_FIRST_ARG(__VA_ARGS__))) \\\n    (EnumName, __VA_ARGS__)\n\n#define SPACETIMEDB_ENUM_0 SPACETIMEDB_ENUM_SIMPLE\n#define SPACETIMEDB_ENUM_1 SPACETIMEDB_ENUM_VARIANT\n#define SPACETIMEDB_FIRST_ARG(first, ...) first\n\n// =============================================================================\n// SIMPLE ENUM IMPLEMENTATION (Unit Variants)\n// =============================================================================\n\n/**\n * @brief Creates efficient enum class with uint8_t underlying type\n * \n * Generates named Sum type variants for SpacetimeDB compatibility.\n * Used internally by SPACETIMEDB_ENUM for simple syntax.\n */\n#define SPACETIMEDB_ENUM_SIMPLE(EnumName, ...) \\\n    enum class EnumName : uint8_t { \\\n        SPACETIMEDB_ENUM_ASSIGN_VALUES(__VA_ARGS__) \\\n    }; \\\n    \\\n    namespace SpacetimeDB::bsatn { \\\n    template<> \\\n    struct bsatn_traits<EnumName> { \\\n        static AlgebraicType algebraic_type() { \\\n            return SpacetimeDB::Internal::LazyTypeRegistrar<EnumName>::getOrRegister( \\\n                []() -> AlgebraicType { \\\n                    SumTypeBuilder builder; \\\n                    SPACETIMEDB_ENUM_ADD_VARIANTS(__VA_ARGS__) \\\n                    return AlgebraicType::make_sum(builder.build()); \\\n                }, \\\n                #EnumName \\\n            ); \\\n        } \\\n        \\\n        static void serialize(Writer& writer, const EnumName& value) { \\\n            writer.write_u8(static_cast<uint8_t>(value)); \\\n        } \\\n        \\\n        static EnumName deserialize(Reader& reader) { \\\n            uint8_t tag = reader.read_u8(); \\\n            return static_cast<EnumName>(tag); \\\n        } \\\n    }; \\\n    template<> \\\n    struct algebraic_type_of<EnumName> { \\\n        static AlgebraicType get() { \\\n            return bsatn_traits<EnumName>::algebraic_type(); \\\n        } \\\n    }; \\\n    } \\\n    \\\n    SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(EnumName)\n\n// Auto-assign enum values (0, 1, 2, etc.) - supports up to 10 variants\n#define SPACETIMEDB_ENUM_ASSIGN_VALUES(...) \\\n    SPACETIMEDB_ENUM_DISPATCH_COUNT(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)(__VA_ARGS__)\n\n#define SPACETIMEDB_ENUM_DISPATCH_COUNT(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) SPACETIMEDB_ENUM_ASSIGN_##N\n\n#define SPACETIMEDB_ENUM_ASSIGN_1(a) a = 0\n#define SPACETIMEDB_ENUM_ASSIGN_2(a, b) a = 0, b = 1\n#define SPACETIMEDB_ENUM_ASSIGN_3(a, b, c) a = 0, b = 1, c = 2\n#define SPACETIMEDB_ENUM_ASSIGN_4(a, b, c, d) a = 0, b = 1, c = 2, d = 3\n#define SPACETIMEDB_ENUM_ASSIGN_5(a, b, c, d, e) a = 0, b = 1, c = 2, d = 3, e = 4\n#define SPACETIMEDB_ENUM_ASSIGN_6(a, b, c, d, e, f) a = 0, b = 1, c = 2, d = 3, e = 4, f = 5\n#define SPACETIMEDB_ENUM_ASSIGN_7(a, b, c, d, e, f, g) a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6\n#define SPACETIMEDB_ENUM_ASSIGN_8(a, b, c, d, e, f, g, h) a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7\n#define SPACETIMEDB_ENUM_ASSIGN_9(a, b, c, d, e, f, g, h, i) a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7, i = 8\n#define SPACETIMEDB_ENUM_ASSIGN_10(a, b, c, d, e, f, g, h, i, j) a = 0, b = 1, c = 2, d = 3, e = 4, f = 5, g = 6, h = 7, i = 8, j = 9\n\n// Register variants in SumTypeBuilder\n#define SPACETIMEDB_ENUM_ADD_VARIANTS(...) \\\n    SPACETIMEDB_FOR_EACH_VARIANT(SPACETIMEDB_ENUM_ADD_UNIT_VARIANT, EMPTY, __VA_ARGS__)\n\n#define SPACETIMEDB_ENUM_ADD_UNIT_VARIANT(name) \\\n    builder.with_unit_variant(#name);\n\n// =============================================================================\n// VARIANT ENUM IMPLEMENTATION (Data Variants)\n// =============================================================================\n\n/**\n * @brief Creates flexible std::variant struct with named variants\n * \n * Supports data-carrying variants with explicit type specifications.\n * Used internally by SPACETIMEDB_ENUM for complex syntax.\n */\n#define SPACETIMEDB_ENUM_VARIANT(EnumName, ...) \\\n    struct EnumName { \\\n        using variant_type = std::variant<SPACETIMEDB_ENUM_VARIANT_TYPES(__VA_ARGS__)>; \\\n        variant_type value; \\\n        \\\n        static constexpr const char* variant_names[] = { \\\n            FOR_EACH_VARIANT(VARIANT_NAME, COMMA, __VA_ARGS__) \\\n        }; \\\n        \\\n        EnumName() = default; \\\n        EnumName(const variant_type& v) : value(v) {} \\\n        EnumName(variant_type&& v) : value(std::move(v)) {} \\\n        \\\n        template<typename T, typename = std::enable_if_t< \\\n            !std::is_same_v<std::decay_t<T>, EnumName> && \\\n            !std::is_same_v<std::decay_t<T>, variant_type>>> \\\n        EnumName(T&& t) : value(std::forward<T>(t)) {} \\\n        \\\n        size_t index() const { return value.index(); } \\\n        const char* variant_name() const { \\\n            return (index() < sizeof(variant_names)/sizeof(variant_names[0])) ? \\\n                variant_names[index()] : \"unknown\"; \\\n        } \\\n    }; \\\n    \\\n    namespace SpacetimeDB::bsatn { \\\n    template<> \\\n    struct bsatn_traits<EnumName> { \\\n        static AlgebraicType algebraic_type() { \\\n            return SpacetimeDB::Internal::LazyTypeRegistrar<EnumName>::getOrRegister( \\\n                []() -> AlgebraicType { \\\n                    std::vector<SumTypeVariant> variants; \\\n                    SpacetimeDB::named_variant_helper<0, SPACETIMEDB_ENUM_VARIANT_TYPES(__VA_ARGS__)>::add_variants( \\\n                        variants, EnumName::variant_names); \\\n                    auto sum_type = std::make_unique<SumTypeSchema>(std::move(variants)); \\\n                    return AlgebraicType::make_sum(std::move(sum_type)); \\\n                }, \\\n                #EnumName \\\n            ); \\\n        } \\\n        \\\n        static void serialize(Writer& writer, const EnumName& enum_value) { \\\n            writer.write_u8(static_cast<uint8_t>(enum_value.value.index())); \\\n            std::visit([&](const auto& v) { \\\n                SpacetimeDB::bsatn::serialize(writer, v); \\\n            }, enum_value.value); \\\n        } \\\n        \\\n        static EnumName deserialize(Reader& reader) { \\\n            uint8_t tag = reader.read_u8(); \\\n            return EnumName{SpacetimeDB::named_variant_helper<0, SPACETIMEDB_ENUM_VARIANT_TYPES(__VA_ARGS__)>:: \\\n                template deserialize_variant<typename EnumName::variant_type>(tag, reader)}; \\\n        } \\\n    }; \\\n    template<> \\\n    struct algebraic_type_of<EnumName> { \\\n        static AlgebraicType get() { \\\n            return bsatn_traits<EnumName>::algebraic_type(); \\\n        } \\\n    }; \\\n    } \\\n    \\\n    SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(EnumName)\n\n// Extract variant types from (name, type) pairs\n#define SPACETIMEDB_ENUM_VARIANT_TYPES(...) FOR_EACH_VARIANT(VARIANT_TYPE, COMMA, __VA_ARGS__)\n\n// Type alias for unit variants (no data)\nusing Unit = std::monostate;\n\n// =============================================================================\n// VARIANT HELPER TEMPLATES\n// =============================================================================\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Recursive helper for variant type processing\n * \n * Builds variant lists with compile-time names and handles serialization.\n * Used internally by SPACETIMEDB_ENUM_VARIANT.\n */\ntemplate<size_t I, typename... Types>\nstruct named_variant_helper;\n\n// Recursion base case\ntemplate<size_t I>\nstruct named_variant_helper<I> {\n    static void add_variants(std::vector<bsatn::SumTypeVariant>& variants, \n                            const char* const* names) {\n        (void)variants; (void)names; // End recursion\n    }\n    \n    template<typename Variant>\n    static Variant deserialize_variant(size_t index, bsatn::Reader& reader) {\n        (void)index;\n        (void)reader;\n        std::abort(); // Invalid variant index\n    }\n};\n\n// Recursive case\ntemplate<size_t I, typename T, typename... Rest>\nstruct named_variant_helper<I, T, Rest...> {\n    static void add_variants(std::vector<bsatn::SumTypeVariant>& variants,\n                            const char* const* names) {\n        const char* name = names[I];\n        \n        // Trigger type registration if needed (bottom-up dependency resolution)\n        bsatn::AlgebraicType variant_type = bsatn::bsatn_traits<T>::algebraic_type();\n        \n        variants.emplace_back(name, std::move(variant_type));\n        \n        // Continue with remaining types\n        named_variant_helper<I + 1, Rest...>::add_variants(variants, names);\n    }\n    \n    template<typename Variant>\n    static Variant deserialize_variant(size_t index, bsatn::Reader& reader) {\n        if (index == I) {\n            return Variant{bsatn::deserialize<T>(reader)};\n        } else {\n            return named_variant_helper<I + 1, Rest...>::template deserialize_variant<Variant>(index, reader);\n        }\n    }\n};\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// COMPILE-TIME NAMESPACE STORAGE\n// =============================================================================\n\nnamespace SpacetimeDB::detail {\n    // Template to store namespace information for types\n    template<typename T>\n    struct namespace_info {\n        static constexpr const char* value = nullptr;\n    };\n}\n\n/**\n * @brief Add namespace qualification to an enum type for module reusability\n * \n * This macro registers a namespace prefix with an enum type, allowing the same\n * enum name to be used across different modules without conflicts. The namespace\n * is stored at compile-time and applied during type registration.\n * \n * **Use Cases:**\n * - Sharing enum definitions across multiple modules\n * - Avoiding name conflicts in large projects\n * - Organizing types into logical namespaces\n * \n * @param EnumType The C++ enum type (already defined with SPACETIMEDB_ENUM)\n * @param NamespacePrefix The namespace prefix as a string literal (e.g., \"MyModule\")\n * \n * @example Basic namespace qualification:\n * @code\n * // Module A\n * SPACETIMEDB_ENUM(Status, Active, Inactive, Banned)\n * SPACETIMEDB_NAMESPACE(Status, \"PlayerSystem\")\n * // Registered as: \"PlayerSystem.Status\"\n * \n * // Module B (can reuse the same enum name)\n * SPACETIMEDB_ENUM(Status, Pending, Approved, Rejected)\n * SPACETIMEDB_NAMESPACE(Status, \"OrderSystem\")\n * // Registered as: \"OrderSystem.Status\"\n * @endcode\n * \n * @example Nested namespaces:\n * @code\n * SPACETIMEDB_ENUM(EventType, Create, Update, Delete)\n * SPACETIMEDB_NAMESPACE(EventType, \"Game.Combat\")\n * // Registered as: \"Game.Combat.EventType\"\n * @endcode\n * \n * @example Without namespace (global scope):\n * @code\n * SPACETIMEDB_ENUM(GlobalState, Initializing, Running, Shutdown)\n * // No SPACETIMEDB_NAMESPACE call → registered as just \"GlobalState\"\n * @endcode\n * \n * @note The namespace does NOT affect C++ code - it only applies to SpacetimeDB's type registry\n * @note Call this macro immediately after the SPACETIMEDB_ENUM definition\n * @warning Changing the namespace after data is stored will break schema compatibility\n */\n#define SPACETIMEDB_NAMESPACE(EnumType, NamespacePrefix) \\\n    namespace SpacetimeDB::detail { \\\n        template<> \\\n        struct namespace_info<EnumType> { \\\n            static constexpr const char* value = NamespacePrefix; \\\n        }; \\\n    }\n\n\n\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/error_handling.h",
    "content": "#pragma once\n\n#include <optional>\n#include <variant>\n#include <string>\n#include <concepts>\n#include <type_traits>\n#include <source_location>\n\nnamespace SpacetimeDB {\n\n// =============================================================================\n// Phase 3: C++20 Error Handling System - Closing the Gap with Rust\n// =============================================================================\n\n/// Error types for database operations\nenum class DatabaseError {\n    ConstraintViolation,\n    DuplicateKey,\n    NotFound,\n    SerializationError,\n    ConnectionError,\n    Unknown\n};\n\n/// Error information with C++20 source location support\nstruct ErrorInfo {\n    DatabaseError error_type;\n    std::string message;\n    std::source_location location;\n    \n    ErrorInfo(DatabaseError type, std::string msg, \n              std::source_location loc = std::source_location::current())\n        : error_type(type), message(std::move(msg)), location(loc) {}\n};\n\n/// Result type similar to Rust's Result<T, E> - using std::variant since std::expected is C++23\ntemplate<typename T>\nusing DatabaseResult = std::variant<T, ErrorInfo>;\n\n/// Helper concepts for database operations\ntemplate<typename T>\nconcept DatabaseType = requires(T t) {\n    // Must be serializable and have comparison operators\n    requires std::copyable<T>;\n    requires std::equality_comparable<T>;\n};\n\n// =============================================================================\n// Result helper functions (Rust-inspired API)\n// =============================================================================\n\n/// Check if result is successful\ntemplate<typename T>\nconstexpr bool is_ok(const DatabaseResult<T>& result) {\n    return std::holds_alternative<T>(result);\n}\n\n/// Check if result is an error  \ntemplate<typename T>\nconstexpr bool is_error(const DatabaseResult<T>& result) {\n    return std::holds_alternative<ErrorInfo>(result);\n}\n\n/// Get value from successful result (throws if error)\ntemplate<typename T>\nconstexpr T& get_value(DatabaseResult<T>& result) {\n    if (is_error(result)) {\n        std::abort(); // Attempted to get value from error result\n    }\n    return std::get<T>(result);\n}\n\n/// Get value from successful result (const version)\ntemplate<typename T>\nconstexpr const T& get_value(const DatabaseResult<T>& result) {\n    if (is_error(result)) {\n        std::abort(); // Attempted to get value from error result\n    }\n    return std::get<T>(result);\n}\n\n/// Get error from failed result\ntemplate<typename T>\nconstexpr const ErrorInfo& get_error(const DatabaseResult<T>& result) {\n    if (is_ok(result)) {\n        std::abort(); // Attempted to get error from successful result\n    }\n    return std::get<ErrorInfo>(result);\n}\n\n/// Unwrap result or provide default value\ntemplate<typename T>\nconstexpr T unwrap_or(const DatabaseResult<T>& result, T default_value) {\n    if (is_ok(result)) {\n        return get_value(result);\n    }\n    return default_value;\n}\n\n/// Convert result to optional (loses error information)\ntemplate<typename T>\nconstexpr std::optional<T> to_optional(const DatabaseResult<T>& result) {\n    if (is_ok(result)) {\n        return get_value(result);\n    }\n    return std::nullopt;\n}\n\n// =============================================================================\n// Upsert result types for insert_or_update operations\n// =============================================================================\n\nenum class UpsertAction {\n    Inserted,  // Row was newly inserted\n    Updated    // Existing row was updated\n};\n\n/// Result of an insert_or_update operation\ntemplate<typename T>\nstruct UpsertResult {\n    T value;\n    UpsertAction action;\n    \n    UpsertResult(T val, UpsertAction act) : value(std::move(val)), action(act) {}\n    \n    bool was_inserted() const { return action == UpsertAction::Inserted; }\n    bool was_updated() const { return action == UpsertAction::Updated; }\n};\n\n// =============================================================================\n// Enhanced table accessor base class with error handling\n// =============================================================================\n\n/// Base class providing error-safe database operations\ntemplate<typename TableType>\nrequires DatabaseType<TableType>\nclass ErrorSafeTableAccessor {\npublic:\n    /// Try to insert a row, returning result instead of throwing\n    /// Rust equivalent: ctx.db.table().try_insert(row)\n    DatabaseResult<TableType> try_insert(const TableType& row) const {\n        // Without exceptions, we just call perform_insert\n        // If there's an error, it will abort\n        auto result = perform_insert(row);\n        return DatabaseResult<TableType>(std::in_place_index<0>, result);\n    }\n    \n    /// Insert or update a row based on primary key\n    /// Rust equivalent: ctx.db.table().id().try_insert_or_update(row) \n    DatabaseResult<UpsertResult<TableType>> insert_or_update(const TableType& row) const {\n        // Without exceptions, we directly perform the operations\n        auto existing = find_by_primary_key(row);\n        \n        if (existing) {\n            // Update existing row\n            auto updated = perform_update(row);\n            return DatabaseResult<UpsertResult<TableType>>(\n                std::in_place_index<0>,\n                UpsertResult<TableType>(updated, UpsertAction::Updated)\n            );\n        } else {\n            // Insert new row\n            auto inserted = perform_insert(row);\n            return DatabaseResult<UpsertResult<TableType>>(\n                std::in_place_index<0>,\n                UpsertResult<TableType>(inserted, UpsertAction::Inserted)\n            );\n        }\n    }\n    \n    /// Try to delete a row, returning whether it was found and deleted\n    DatabaseResult<bool> try_delete(const TableType& row) const {\n        // Without exceptions, we directly perform the delete\n        bool deleted = perform_delete(row);\n        return DatabaseResult<bool>(std::in_place_index<0>, deleted);\n    }\n    \nprotected:\n    // Pure virtual methods to be implemented by derived classes\n    virtual TableType perform_insert(const TableType& row) const = 0;\n    virtual TableType perform_update(const TableType& row) const = 0;\n    virtual bool perform_delete(const TableType& row) const = 0;\n    virtual std::optional<TableType> find_by_primary_key(const TableType& row) const = 0;\n};\n\n// =============================================================================\n// Convenient macros for error handling patterns\n// =============================================================================\n\n/// Try an operation and return early if it fails (Rust-inspired ? operator)\n#define TRY_DB_OP(result) \\\n    ([&]() { \\\n        auto _temp_result = (result); \\\n        if (is_error(_temp_result)) { \\\n            return DatabaseResult<decltype(get_value(_temp_result))>( \\\n                std::in_place_index<1>, get_error(_temp_result)); \\\n        } \\\n        return _temp_result; \\\n    }())\n\n/// Log error information with C++20 source location\n#define LOG_DB_ERROR(result) \\\n    do { \\\n        if (is_error(result)) { \\\n            const auto& err = get_error(result); \\\n            LOG_ERROR_F(\"[%s:%d in %s] DB Error: %s\", \\\n                       err.location.file_name(), err.location.line(), \\\n                       err.location.function_name(), err.message.c_str()); \\\n        } \\\n    } while(0)\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// Usage Examples (for documentation) \n// =============================================================================\n\n/*\n// Basic error handling - Rust style\nauto result = ctx.db[users].try_insert(new_user);\nif (is_ok(result)) {\n    auto user = get_value(result);\n    LOG_INFO_F(\"Inserted user: %s\", user.name.c_str());\n} else {\n    auto error = get_error(result);\n    LOG_ERROR_F(\"Insert failed: %s\", error.message.c_str());\n}\n\n// Insert or update pattern - major missing feature\nauto upsert_result = ctx.db[users].insert_or_update(user);\nif (is_ok(upsert_result)) {\n    auto upsert = get_value(upsert_result);\n    if (upsert.was_inserted()) {\n        LOG_INFO(\"Created new user\");\n    } else {\n        LOG_INFO(\"Updated existing user\");\n    }\n}\n\n// Chain operations with error propagation\nDatabaseResult<User> create_user_safe(const std::string& name) {\n    User new_user{0, name, \"default@example.com\"};\n    \n    // Try insert, propagate error if it fails\n    auto insert_result = TRY_DB_OP(ctx.db[users].try_insert(new_user));\n    \n    // If we get here, insert succeeded\n    LOG_INFO(\"User created successfully\");\n    return insert_result;\n}\n\n// Convert to optional for simpler handling\nauto user_opt = to_optional(ctx.db[users].try_insert(new_user));\nif (user_opt) {\n    // Success - use user_opt.value()\n} else {\n    // Failed - error info is lost\n}\n*/"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/http.h",
    "content": "#ifndef SPACETIMEDB_HTTP_H\n#define SPACETIMEDB_HTTP_H\n\n#pragma once\n\n#include <string>\n#include <vector>\n#include <optional>\n#include <cstdint>\n#include \"spacetimedb/bsatn/time_duration.h\"\n#include \"spacetimedb/outcome.h\"\n\n/**\n * @file http.h\n * @brief HTTP request support for SpacetimeDB procedures\n *\n * This module provides types and functionality for making outbound HTTP requests\n * from within SpacetimeDB procedures. The actual network I/O is performed by the\n * SpacetimeDB host using reqwest (Rust), ensuring security and resource management.\n *\n * IMPORTANT LIMITATIONS:\n * - HTTP requests CANNOT be performed inside with_tx() or try_with_tx()\n * - The host will reject HTTP requests with WOULD_BLOCK_TRANSACTION\n * - All timeouts are clamped to a maximum of 500ms by the host\n * - No external HTTP library dependencies (host does actual HTTP)\n *\n * Example usage:\n * @code\n * SPACETIMEDB_PROCEDURE(std::string, fetch_data, ProcedureContext ctx) {\n *     auto result = ctx.http.get(\"http://api.example.com/data\");\n *     \n *     if (result.is_ok()) {\n *         auto& response = result.value();\n *         return Ok(response.body.to_string_utf8_lossy());\n *     } else {\n *         return Err(\"HTTP error: \" + result.error());\n *     }\n * }\n * @endcode\n *\n * @ingroup sdk_runtime\n */\n\nnamespace SpacetimeDB {\n\n/**\n * @brief HTTP method (e.g., GET, POST, PUT, DELETE)\n *\n * Supports all standard HTTP methods plus custom extension methods.\n * Extension methods are any string value not matching a standard method.\n */\nstruct HttpMethod {\n    std::string value;\n    \n    /// Standard HTTP methods\n    static HttpMethod get() { return HttpMethod{\"GET\"}; }\n    static HttpMethod head() { return HttpMethod{\"HEAD\"}; }\n    static HttpMethod post() { return HttpMethod{\"POST\"}; }\n    static HttpMethod put() { return HttpMethod{\"PUT\"}; }\n    // DELETE cannot be named \"delete\" in C++; provide snake_case aliases\n    static HttpMethod del() { return HttpMethod{\"DELETE\"}; }\n    static HttpMethod http_delete() { return HttpMethod{\"DELETE\"}; }\n    static HttpMethod connect() { return HttpMethod{\"CONNECT\"}; }\n    static HttpMethod options() { return HttpMethod{\"OPTIONS\"}; }\n    static HttpMethod trace() { return HttpMethod{\"TRACE\"}; }\n    static HttpMethod patch() { return HttpMethod{\"PATCH\"}; }\n    \n    /// Create a custom/extension HTTP method\n    explicit HttpMethod(std::string v) : value(std::move(v)) {}\n};\n\n/**\n * @brief HTTP protocol version\n */\nenum class HttpVersion : uint8_t {\n    Http09,  ///< HTTP/0.9\n    Http10,  ///< HTTP/1.0\n    Http11,  ///< HTTP/1.1 (default)\n    Http2,   ///< HTTP/2\n    Http3,   ///< HTTP/3\n};\n\n/**\n * @brief HTTP header name/value pair\n *\n * Header values are always treated as raw bytes. The is_sensitive flag\n * is a local-only hint and is not transmitted to the host (sensitive\n * headers have their names redacted in wire format).\n */\nstruct HttpHeader {\n    std::string name;\n    std::vector<uint8_t> value;\n    bool is_sensitive = false;\n    \n    /**\n     * @brief Create header from string name and string value\n     * @param n Header name\n     * @param v Header value (converted to ASCII bytes)\n     * @param sensitive If true, header name will be redacted in logs/wire format\n     */\n    HttpHeader(std::string n, std::string v, bool sensitive = false)\n        : name(std::move(n))\n        , value(v.begin(), v.end())\n        , is_sensitive(sensitive) \n    {}\n    \n    /**\n     * @brief Create header from string name and byte value\n     * @param n Header name\n     * @param v Header value as raw bytes\n     * @param sensitive If true, header name will be redacted in logs/wire format\n     */\n    HttpHeader(std::string n, std::vector<uint8_t> v, bool sensitive = false)\n        : name(std::move(n))\n        , value(std::move(v))\n        , is_sensitive(sensitive) \n    {}\n};\n\n/**\n * @brief HTTP request/response body\n *\n * Bodies are always treated as raw bytes. Use helper methods for UTF-8 text.\n */\nstruct HttpBody {\n    std::vector<uint8_t> bytes;\n    \n    /// Create an empty body\n    static HttpBody empty() { \n        return HttpBody{std::vector<uint8_t>()}; \n    }\n    \n    /// Create body from UTF-8 string\n    static HttpBody from_string(const std::string& s) {\n        return HttpBody{std::vector<uint8_t>(s.begin(), s.end())};\n    }\n    \n    /// Get body bytes\n    std::vector<uint8_t> to_bytes() const {\n        return bytes;\n    }\n    \n    /// Convert body to UTF-8 string (lossy conversion)\n    std::string to_string_utf8_lossy() const {\n        return std::string(bytes.begin(), bytes.end());\n    }\n    \n    /// Check if body is empty\n    bool is_empty() const {\n        return bytes.empty();\n    }\n};\n\n/**\n * @brief HTTP request to be executed by the SpacetimeDB host\n *\n * Use designated initializers (C++20) to construct requests:\n * @code\n * HttpRequest request{\n *     .uri = \"http://example.com/api\",\n *     .method = HttpMethod::post(),\n *     .headers = {HttpHeader{\"Content-Type\", \"application/json\"}},\n *     .body = HttpBody::from_string(\"{\\\"key\\\": \\\"value\\\"}\"),\n *     .timeout = TimeDuration::from_millis(100)\n * };\n * @endcode\n *\n * The host clamps all timeouts to a maximum of 500ms.\n */\nstruct HttpRequest {\n    std::string uri;\n    HttpMethod method = HttpMethod::get();\n    std::vector<HttpHeader> headers;\n    HttpBody body = HttpBody::empty();\n    HttpVersion version = HttpVersion::Http11;\n    std::optional<TimeDuration> timeout;\n};\n\n/**\n * @brief HTTP response returned by the SpacetimeDB host\n *\n * A non-2xx status code is still returned as a successful response; callers should\n * inspect status_code to handle application-level errors from the remote server.\n */\nstruct HttpResponse {\n    uint16_t status_code;\n    HttpVersion version;\n    std::vector<HttpHeader> headers;\n    HttpBody body;\n};\n\n/**\n * @brief HTTP client for making outbound HTTP requests via the host\n *\n * Available from ProcedureContext.http\n *\n * Returns Outcome<HttpResponse> where:\n * - Ok(response): Request succeeded (including non-2xx status codes)\n * - Err(message): Transport error (DNS, connection, timeout)\n *\n * IMPORTANT: Do NOT call inside WithTx() or TryWithTx()\n * The host will reject HTTP requests while a transaction is open\n * and return WOULD_BLOCK_TRANSACTION error.\n */\nclass HttpClient {\npublic:\n    /**\n     * @brief Send a simple GET request\n     *\n     * @param uri The request URI\n     * @param timeout Optional timeout (clamped to 500ms by host)\n     * @return Outcome<HttpResponse> - Ok if response received, Err if transport failed\n     *\n     * @code\n     * auto result = ctx.http.get(\"http://localhost:3000/v1/database/schema\");\n     * if (result.is_ok()) {\n     *     auto& response = result.value();\n     *     return Ok(response.body.to_string_utf8_lossy());\n     * } else {\n     *     return Err(\"HTTP error: \" + result.error());\n     * }\n     * @endcode\n     */\n    Outcome<HttpResponse> get(\n        const std::string& uri, \n        std::optional<TimeDuration> timeout = std::nullopt\n    ) {\n        HttpRequest request{\n            .uri = uri,\n            .method = HttpMethod::get(),\n            .headers = {},\n            .body = HttpBody::empty(),\n            .version = HttpVersion::Http11,\n            .timeout = timeout\n        };\n        return send(request);\n    }\n    \n    /**\n     * @brief Send an HTTP request\n     *\n     * @param request The HTTP request to send\n     * @return Outcome<HttpResponse> - Ok if response received, Err if transport failed\n     *\n     * This method does not throw for expected failures; errors are returned as Outcome::Err.\n     *\n     * Example with POST:\n     * @code\n     * auto request = HttpRequest{\n     *     .uri = \"https://api.example.com/upload\",\n     *     .method = HttpMethod::post(),\n     *     .headers = {HttpHeader{\"Content-Type\", \"text/plain\"}},\n     *     .body = HttpBody::from_string(\"This is the request body\"),\n     *     .timeout = TimeDuration::from_millis(100)\n     * };\n     *\n     * auto result = ctx.http.send(request);\n     * if (result.is_ok()) {\n     *     auto& response = result.value();\n     *     return Ok(\"Status: \" + std::to_string(response.status_code));\n     * } else {\n     *     return Err(\"Error: \" + result.error());\n     * }\n     * @endcode\n     *\n     * Example handling 404:\n     * @code\n     * auto result = ctx.http.get(\"https://example.com/missing\");\n     * if (!result.is_ok()) {\n     *     // Transport error (DNS failure, connection drop, timeout, etc.)\n     *     return Err(\"Transport error: \" + result.error());\n     * }\n     *\n     * auto& response = result.value();\n     * if (response.status_code != 200) {\n     *     // Application-level HTTP error response\n     *     return Err(\"HTTP status: \" + std::to_string(response.status_code));\n     * }\n     *\n     * return Ok(response.body.to_string_utf8_lossy());\n     * @endcode\n     *\n     * Example showing transaction blocking:\n     * @code\n     * // ✗ WRONG: This will fail with WOULD_BLOCK_TRANSACTION\n     * ctx.WithTx([](TxContext& tx) {\n     *     auto result = ctx.http.get(\"https://example.com/\");\n     *     // ERROR: HTTP blocked in transaction\n     * });\n     *\n     * // ✓ CORRECT: HTTP before transaction\n     * auto api_result = ctx.http.get(\"https://example.com/\");\n     * if (api_result.is_ok()) {\n     *     ctx.WithTx([&api_result](TxContext& tx) {\n     *         // Use api_result data here\n     *     });\n     * }\n     * @endcode\n     */\n    Outcome<HttpResponse> send(const HttpRequest& request) {\n        #ifndef SPACETIMEDB_UNSTABLE_FEATURES\n        return Err<HttpResponse>(\"HTTP requests require SPACETIMEDB_UNSTABLE_FEATURES to be enabled\");\n        #else\n        // Implemented in http_client_impl.h to avoid circular dependencies\n        return SendImpl(request);\n        #endif\n    }\n\nprivate:\n    Outcome<HttpResponse> SendImpl(const HttpRequest& request);\n};\n\n} // namespace SpacetimeDB\n\n// Include implementation after class definition to avoid circular dependencies\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\n#include \"spacetimedb/http_client_impl.h\"\n#endif\n\n#endif // SPACETIMEDB_HTTP_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/http_client_impl.h",
    "content": "#ifndef SPACETIMEDB_HTTP_CLIENT_IMPL_H\n#define SPACETIMEDB_HTTP_CLIENT_IMPL_H\n\n#pragma once\n\n#include \"spacetimedb/http_wire.h\"\n#include \"spacetimedb/http_convert.h\"\n#include \"spacetimedb/abi/abi.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"spacetimedb/internal/Module.h\"\n\nnamespace SpacetimeDB {\n\ninline Outcome<HttpResponse> HttpClient::SendImpl(const HttpRequest& request) {\n    // Convert user-facing request to wire format\n    wire::HttpRequest wire_request = convert::to_wire(request);\n    \n    // Serialize wire request to BSATN\n    bsatn::Writer writer;\n    bsatn::serialize(writer, wire_request);\n    std::vector<uint8_t> request_bytes = writer.take_buffer();\n    \n    // Prepare body bytes\n    const std::vector<uint8_t>& body_bytes = request.body.bytes;\n    \n    // Call host function\n    // Note: For empty body, we need to pass a valid pointer, not null\n    const uint8_t* body_ptr = body_bytes.empty() ? reinterpret_cast<const uint8_t*>(\"\") : body_bytes.data();\n    \n    BytesSource out[2] = {BytesSource{0}, BytesSource{0}};\n    Status status = procedure_http_request(\n        request_bytes.data(), request_bytes.size(),\n        body_ptr, body_bytes.size(),\n        out\n    );\n    \n    // Check for errors\n    if (status.inner != 0) {\n        // HTTP_ERROR (21) means the HTTP call failed - error message is in out[0]\n        if (status.inner == 21) {\n            // Read error message from out[0]\n            std::vector<uint8_t> error_bytes = Internal::ConsumeBytes(out[0]);\n            \n            LOG_INFO(\"HTTP: Error bytes: \" + std::to_string(error_bytes.size()));\n            \n            // Decode BSATN string\n            bsatn::Reader reader(error_bytes.data(), error_bytes.size());\n            std::string error_message = bsatn::deserialize<std::string>(reader);\n            \n            LOG_INFO(\"HTTP: Error message: \" + error_message);\n            \n            return Err<HttpResponse>(std::move(error_message));\n        }\n        \n        // Other errors (WOULD_BLOCK_TRANSACTION, etc.)\n        if (status.inner == 17) {\n            return Err<HttpResponse>(\"HTTP requests are blocked inside transactions. Call HTTP before with_tx() or try_with_tx().\");\n        }\n        \n        LOG_INFO(\"HTTP: Unknown error code: \" + std::to_string(status.inner));\n        return Err<HttpResponse>(\"HTTP request failed with status code: \" + std::to_string(status.inner));\n    }\n    \n    // Success - decode response from out[0] and body from out[1]\n    std::vector<uint8_t> response_bytes = Internal::ConsumeBytes(out[0]);\n    std::vector<uint8_t> response_body_bytes = Internal::ConsumeBytes(out[1]);\n    \n    // Decode wire response\n    bsatn::Reader response_reader(response_bytes.data(), response_bytes.size());\n    wire::HttpResponse wire_response = bsatn::deserialize<wire::HttpResponse>(response_reader);\n    \n    // Convert wire response to user-facing type\n    HttpResponse response = convert::from_wire(wire_response);\n    \n    // Set the body\n    response.body = HttpBody{std::move(response_body_bytes)};\n    \n    return Ok(std::move(response));\n}\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_HTTP_CLIENT_IMPL_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/http_convert.h",
    "content": "#ifndef SPACETIMEDB_HTTP_CONVERT_H\n#define SPACETIMEDB_HTTP_CONVERT_H\n\n#pragma once\n\n#include \"spacetimedb/http.h\"\n#include \"spacetimedb/http_wire.h\"\n\n/**\n * @file http_convert.h\n * @brief Conversion functions between user-facing HTTP types and BSATN wire types\n *\n * This module provides bidirectional conversion between:\n * - User-facing types (http.h): HttpMethod, HttpVersion, HttpHeader, etc.\n * - Wire types (http_wire.h): wire::HttpMethod, wire::HttpVersion, etc.\n *\n * The wire types are used for BSATN serialization when communicating with the\n * SpacetimeDB host. User code should never interact with wire types directly.\n *\n * Note: The `is_sensitive` flag from HttpHeader is NOT preserved in the wire format.\n * It's a local-only hint and is lost during conversion to wire format. When converting\n * back from wire format, all headers are marked as non-sensitive.\n *\n * @ingroup sdk_internal\n */\n\nnamespace SpacetimeDB {\nnamespace convert {\n\n// ==================== HttpMethod Conversions ====================\n\n/**\n * @brief Convert user-facing HttpMethod to wire format\n *\n * Standard methods (GET, POST, etc.) map to unit enum variants.\n * Non-standard methods are stored in the Extension variant.\n */\ninline wire::HttpMethod to_wire(const HttpMethod& method) {\n    wire::HttpMethod result;\n    \n    // Check for standard methods\n    if (method.value == \"GET\") {\n        result.tag = wire::HttpMethod::Tag::Get;\n    } else if (method.value == \"HEAD\") {\n        result.tag = wire::HttpMethod::Tag::Head;\n    } else if (method.value == \"POST\") {\n        result.tag = wire::HttpMethod::Tag::Post;\n    } else if (method.value == \"PUT\") {\n        result.tag = wire::HttpMethod::Tag::Put;\n    } else if (method.value == \"DELETE\") {\n        result.tag = wire::HttpMethod::Tag::Delete;\n    } else if (method.value == \"CONNECT\") {\n        result.tag = wire::HttpMethod::Tag::Connect;\n    } else if (method.value == \"OPTIONS\") {\n        result.tag = wire::HttpMethod::Tag::Options;\n    } else if (method.value == \"TRACE\") {\n        result.tag = wire::HttpMethod::Tag::Trace;\n    } else if (method.value == \"PATCH\") {\n        result.tag = wire::HttpMethod::Tag::Patch;\n    } else {\n        // Non-standard method - store in Extension variant\n        result.tag = wire::HttpMethod::Tag::Extension;\n        result.extension = method.value;\n    }\n    \n    return result;\n}\n\n/**\n * @brief Convert wire format HttpMethod to user-facing type\n */\ninline HttpMethod from_wire(const wire::HttpMethod& method) {\n    switch (method.tag) {\n        case wire::HttpMethod::Tag::Get:\n            return HttpMethod::get();\n        case wire::HttpMethod::Tag::Head:\n            return HttpMethod::head();\n        case wire::HttpMethod::Tag::Post:\n            return HttpMethod::post();\n        case wire::HttpMethod::Tag::Put:\n            return HttpMethod::put();\n        case wire::HttpMethod::Tag::Delete:\n            return HttpMethod::del();\n        case wire::HttpMethod::Tag::Connect:\n            return HttpMethod::connect();\n        case wire::HttpMethod::Tag::Options:\n            return HttpMethod::options();\n        case wire::HttpMethod::Tag::Trace:\n            return HttpMethod::trace();\n        case wire::HttpMethod::Tag::Patch:\n            return HttpMethod::patch();\n        case wire::HttpMethod::Tag::Extension:\n            return HttpMethod{method.extension};\n        default:\n            // Should never happen, but default to GET for safety\n            return HttpMethod::get();\n    }\n}\n\n// ==================== HttpVersion Conversions ====================\n\n/**\n * @brief Convert user-facing HttpVersion to wire format\n */\ninline wire::HttpVersion to_wire(HttpVersion version) {\n    wire::HttpVersion result;\n    \n    switch (version) {\n        case HttpVersion::Http09:\n            result.tag = wire::HttpVersion::Tag::Http09;\n            break;\n        case HttpVersion::Http10:\n            result.tag = wire::HttpVersion::Tag::Http10;\n            break;\n        case HttpVersion::Http11:\n            result.tag = wire::HttpVersion::Tag::Http11;\n            break;\n        case HttpVersion::Http2:\n            result.tag = wire::HttpVersion::Tag::Http2;\n            break;\n        case HttpVersion::Http3:\n            result.tag = wire::HttpVersion::Tag::Http3;\n            break;\n    }\n    \n    return result;\n}\n\n/**\n * @brief Convert wire format HttpVersion to user-facing type\n */\ninline HttpVersion from_wire(const wire::HttpVersion& version) {\n    switch (version.tag) {\n        case wire::HttpVersion::Tag::Http09:\n            return HttpVersion::Http09;\n        case wire::HttpVersion::Tag::Http10:\n            return HttpVersion::Http10;\n        case wire::HttpVersion::Tag::Http11:\n            return HttpVersion::Http11;\n        case wire::HttpVersion::Tag::Http2:\n            return HttpVersion::Http2;\n        case wire::HttpVersion::Tag::Http3:\n            return HttpVersion::Http3;\n        default:\n            // Should never happen, default to HTTP/1.1\n            return HttpVersion::Http11;\n    }\n}\n\n// ==================== HttpHeader Conversions ====================\n\n/**\n * @brief Convert user-facing HttpHeader to wire format HttpHeaderPair\n *\n * WARNING: The is_sensitive flag is LOST during this conversion.\n * The wire format does not preserve sensitivity information.\n */\ninline wire::HttpHeaderPair to_wire(const HttpHeader& header) {\n    wire::HttpHeaderPair result;\n    result.name = header.name;\n    result.value = header.value;\n    return result;\n}\n\n/**\n * @brief Convert wire format HttpHeaderPair to user-facing HttpHeader\n *\n * The resulting header will have is_sensitive=false, as the wire format\n * does not preserve sensitivity information.\n */\ninline HttpHeader from_wire(const wire::HttpHeaderPair& pair) {\n    return HttpHeader{pair.name, pair.value, false};\n}\n\n// ==================== HttpHeaders (collection) Conversions ====================\n\n/**\n * @brief Convert user-facing header vector to wire format HttpHeaders\n */\ninline wire::HttpHeaders to_wire_headers(const std::vector<HttpHeader>& headers) {\n    wire::HttpHeaders result;\n    result.entries.reserve(headers.size());\n    \n    for (const auto& header : headers) {\n        result.entries.push_back(to_wire(header));\n    }\n    \n    return result;\n}\n\n/**\n * @brief Convert wire format HttpHeaders to user-facing header vector\n */\ninline std::vector<HttpHeader> from_wire_headers(const wire::HttpHeaders& headers) {\n    std::vector<HttpHeader> result;\n    result.reserve(headers.entries.size());\n    \n    for (const auto& pair : headers.entries) {\n        result.push_back(from_wire(pair));\n    }\n    \n    return result;\n}\n\n// ==================== HttpRequest Conversions ====================\n\n/**\n * @brief Convert user-facing HttpRequest to wire format\n *\n * Note: The body field is NOT included in the wire HttpRequest struct.\n * The body bytes are passed separately via ConsumeBytes().\n */\ninline wire::HttpRequest to_wire(const HttpRequest& request) {\n    wire::HttpRequest result;\n    result.method = to_wire(request.method);\n    result.headers = to_wire_headers(request.headers);\n    result.timeout = request.timeout;\n    result.uri = request.uri;\n    result.version = to_wire(request.version);\n    return result;\n}\n\n/**\n * @brief Convert wire format HttpRequest to user-facing type\n *\n * Note: The returned HttpRequest will have an empty body.\n * The body bytes are received separately via ConsumeBytes() and must be set manually.\n */\ninline HttpRequest from_wire(const wire::HttpRequest& request) {\n    HttpRequest result;\n    result.method = from_wire(request.method);\n    result.headers = from_wire_headers(request.headers);\n    result.timeout = request.timeout;\n    result.uri = request.uri;\n    result.version = from_wire(request.version);\n    result.body = HttpBody::empty(); // Body is received separately\n    return result;\n}\n\n// ==================== HttpResponse Conversions ====================\n\n/**\n * @brief Convert user-facing HttpResponse to wire format\n *\n * Note: The body field is NOT included in the wire HttpResponse struct.\n * The body bytes are passed separately via ConsumeBytes().\n */\ninline wire::HttpResponse to_wire(const HttpResponse& response) {\n    wire::HttpResponse result;\n    result.headers = to_wire_headers(response.headers);\n    result.version = to_wire(response.version);\n    result.code = response.status_code;\n    return result;\n}\n\n/**\n * @brief Convert wire format HttpResponse to user-facing type\n *\n * Note: The returned HttpResponse will have an empty body.\n * The body bytes are received separately via ConsumeBytes() and must be set manually.\n */\ninline HttpResponse from_wire(const wire::HttpResponse& response) {\n    HttpResponse result;\n    result.headers = from_wire_headers(response.headers);\n    result.version = from_wire(response.version);\n    result.status_code = response.code;\n    result.body = HttpBody::empty(); // Body is received separately\n    return result;\n}\n\n} // namespace convert\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_HTTP_CONVERT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/http_wire.h",
    "content": "#ifndef SPACETIMEDB_HTTP_WIRE_H\n#define SPACETIMEDB_HTTP_WIRE_H\n\n#pragma once\n\n#include <string>\n#include <vector>\n#include <optional>\n#include <cstdint>\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"spacetimedb/bsatn/traits.h\"\n#include \"spacetimedb/bsatn/time_duration.h\"\n\n/**\n * @file http_wire.h\n * @brief BSATN wire format types for HTTP requests/responses\n *\n * These types mirror the Rust types in `spacetimedb_lib::http` and are used for\n * BSATN encoding/decoding when communicating with the SpacetimeDB host.\n *\n * CRITICAL: Field order MUST match Rust exactly for BSATN compatibility!\n *\n * These types are internal implementation details. User code should use the types\n * in http.h instead. Conversion functions in http_conversions.h handle the mapping.\n *\n * @warning Do NOT change the field order or layout of these types without coordinating\n *          with the Rust side. Breaking BSATN compatibility will cause runtime failures.\n *\n * @ingroup sdk_internal\n */\n\nnamespace SpacetimeDB {\nnamespace wire {\n\n/**\n * @brief Wire format for HTTP method\n *\n * Matches Rust: `spacetimedb_lib::http::Method`\n *\n * BSATN enum representation:\n * - Standard methods (Get, Head, Post, etc.) are represented as unit variants (no payload)\n * - Extension(String) is represented as a variant with a String payload\n */\nstruct HttpMethod {\n    enum class Tag : uint8_t {\n        Get = 0,\n        Head = 1,\n        Post = 2,\n        Put = 3,\n        Delete = 4,\n        Connect = 5,\n        Options = 6,\n        Trace = 7,\n        Patch = 8,\n        Extension = 9,\n    };\n\n    Tag tag;\n    std::string extension; // Only valid when tag == Extension\n};\n\n/**\n * @brief Wire format for HTTP version\n *\n * Matches Rust: `spacetimedb_lib::http::Version`\n *\n * BSATN enum representation (unit variants only):\n * - Http09 = 0\n * - Http10 = 1\n * - Http11 = 2\n * - Http2 = 3\n * - Http3 = 4\n */\nstruct HttpVersion {\n    enum class Tag : uint8_t {\n        Http09 = 0,\n        Http10 = 1,\n        Http11 = 2,\n        Http2 = 3,\n        Http3 = 4,\n    };\n\n    Tag tag;\n};\n\n/**\n * @brief Wire format for a single HTTP header name/value pair\n *\n * Matches Rust: `spacetimedb_lib::http::HttpHeaderPair`\n *\n * Field order: name, value (MUST match Rust!)\n *\n * Note: The `is_sensitive` flag from the user-facing HttpHeader type is NOT transmitted.\n * It's a local-only hint and is not part of the wire format.\n */\nstruct HttpHeaderPair {\n    std::string name;           // Field 0: Header name (valid HTTP header name)\n    std::vector<uint8_t> value; // Field 1: Header value bytes\n};\n\n/**\n * @brief Wire format for HTTP headers collection\n *\n * Matches Rust: `spacetimedb_lib::http::Headers`\n *\n * Field order: entries (MUST match Rust!)\n *\n * BSATN representation:\n * - Single field `entries` which is a Vec<HttpHeaderPair>\n * - Headers with the same name appear as multiple entries\n */\nstruct HttpHeaders {\n    std::vector<HttpHeaderPair> entries; // Field 0: Array of header pairs\n};\n\n/**\n * @brief Wire format for HTTP request\n *\n * Matches Rust: `spacetimedb_lib::http::Request`\n *\n * Field order (CRITICAL - MUST match Rust exactly!):\n * 0. method: HttpMethod\n * 1. headers: HttpHeaders\n * 2. timeout: Option<TimeDuration>\n * 3. uri: String\n * 4. version: HttpVersion\n *\n * Note: The request body is NOT part of this struct. It's passed separately\n * to the host via the ConsumeBytes() mechanism.\n */\nstruct HttpRequest {\n    HttpMethod method;                      // Field 0\n    HttpHeaders headers;                    // Field 1\n    std::optional<TimeDuration> timeout;    // Field 2\n    std::string uri;                        // Field 3\n    HttpVersion version;                    // Field 4\n};\n\n/**\n * @brief Wire format for HTTP response\n *\n * Matches Rust: `spacetimedb_lib::http::Response`\n *\n * Field order (CRITICAL - MUST match Rust exactly!):\n * 0. headers: HttpHeaders\n * 1. version: HttpVersion\n * 2. code: u16\n *\n * Note: The response body is NOT part of this struct. It's received separately\n * from the host via the ConsumeBytes() mechanism.\n */\nstruct HttpResponse {\n    HttpHeaders headers;  // Field 0\n    HttpVersion version;  // Field 1\n    uint16_t code;        // Field 2: HTTP status code\n};\n\n} // namespace wire\n} // namespace SpacetimeDB\n\n// ==================== BSATN Serialization Traits ====================\n\nnamespace SpacetimeDB::bsatn {\n\n// Forward declarations for recursive types\ntemplate<> struct bsatn_traits<wire::HttpMethod>;\ntemplate<> struct bsatn_traits<wire::HttpVersion>;\ntemplate<> struct bsatn_traits<wire::HttpHeaderPair>;\ntemplate<> struct bsatn_traits<wire::HttpHeaders>;\ntemplate<> struct bsatn_traits<wire::HttpRequest>;\ntemplate<> struct bsatn_traits<wire::HttpResponse>;\n\n// HttpMethod enum serialization\ntemplate<>\nstruct bsatn_traits<wire::HttpMethod> {\n    static void serialize(Writer& writer, const wire::HttpMethod& value) {\n        // Encode tag as u8\n        writer.write_u8(static_cast<uint8_t>(value.tag));\n        \n        // If Extension variant, encode the string payload\n        if (value.tag == wire::HttpMethod::Tag::Extension) {\n            bsatn::serialize(writer, value.extension);\n        }\n    }\n\n    static wire::HttpMethod deserialize(Reader& reader) {\n        wire::HttpMethod result;\n        result.tag = static_cast<wire::HttpMethod::Tag>(reader.read_u8());\n        \n        // If Extension variant, decode the string payload\n        if (result.tag == wire::HttpMethod::Tag::Extension) {\n            result.extension = bsatn::deserialize<std::string>(reader);\n        }\n        \n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U8();\n    }\n};\n\n// HttpVersion enum serialization (simple tag-only enum)\ntemplate<>\nstruct bsatn_traits<wire::HttpVersion> {\n    static void serialize(Writer& writer, const wire::HttpVersion& value) {\n        writer.write_u8(static_cast<uint8_t>(value.tag));\n    }\n\n    static wire::HttpVersion deserialize(Reader& reader) {\n        wire::HttpVersion result;\n        result.tag = static_cast<wire::HttpVersion::Tag>(reader.read_u8());\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        return AlgebraicType::U8();\n    }\n};\n\n// HttpHeaderPair struct serialization\ntemplate<>\nstruct bsatn_traits<wire::HttpHeaderPair> {\n    static void serialize(Writer& writer, const wire::HttpHeaderPair& value) {\n        // Field 0: name (String)\n        bsatn::serialize(writer, value.name);\n        // Field 1: value (Vec<u8>)\n        bsatn::serialize(writer, value.value);\n    }\n\n    static wire::HttpHeaderPair deserialize(Reader& reader) {\n        wire::HttpHeaderPair result;\n        // Field 0: name\n        result.name = bsatn::deserialize<std::string>(reader);\n        // Field 1: value\n        result.value = bsatn::deserialize<std::vector<uint8_t>>(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        ProductTypeBuilder builder;\n        builder.with_field<std::string>(\"name\");\n        builder.with_field<std::vector<uint8_t>>(\"value\");\n        return AlgebraicType::make_product(builder.build());\n    }\n};\n\n// HttpHeaders struct serialization\ntemplate<>\nstruct bsatn_traits<wire::HttpHeaders> {\n    static void serialize(Writer& writer, const wire::HttpHeaders& value) {\n        // Field 0: entries (Vec<HttpHeaderPair>)\n        bsatn::serialize(writer, value.entries);\n    }\n\n    static wire::HttpHeaders deserialize(Reader& reader) {\n        wire::HttpHeaders result;\n        // Field 0: entries\n        result.entries = bsatn::deserialize<std::vector<wire::HttpHeaderPair>>(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        ProductTypeBuilder builder;\n        builder.with_field<std::vector<wire::HttpHeaderPair>>(\"entries\");\n        return AlgebraicType::make_product(builder.build());\n    }\n};\n\n// HttpRequest struct serialization\ntemplate<>\nstruct bsatn_traits<wire::HttpRequest> {\n    static void serialize(Writer& writer, const wire::HttpRequest& value) {\n        // Field 0: method\n        bsatn::serialize(writer, value.method);\n        // Field 1: headers\n        bsatn::serialize(writer, value.headers);\n        // Field 2: timeout\n        bsatn::serialize(writer, value.timeout);\n        // Field 3: uri\n        bsatn::serialize(writer, value.uri);\n        // Field 4: version\n        bsatn::serialize(writer, value.version);\n    }\n\n    static wire::HttpRequest deserialize(Reader& reader) {\n        wire::HttpRequest result;\n        // Field 0: method\n        result.method = bsatn::deserialize<wire::HttpMethod>(reader);\n        // Field 1: headers\n        result.headers = bsatn::deserialize<wire::HttpHeaders>(reader);\n        // Field 2: timeout\n        result.timeout = bsatn::deserialize<std::optional<TimeDuration>>(reader);\n        // Field 3: uri\n        result.uri = bsatn::deserialize<std::string>(reader);\n        // Field 4: version\n        result.version = bsatn::deserialize<wire::HttpVersion>(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        ProductTypeBuilder builder;\n        builder.with_field<wire::HttpMethod>(\"method\");\n        builder.with_field<wire::HttpHeaders>(\"headers\");\n        builder.with_field<std::optional<TimeDuration>>(\"timeout\");\n        builder.with_field<std::string>(\"uri\");\n        builder.with_field<wire::HttpVersion>(\"version\");\n        return AlgebraicType::make_product(builder.build());\n    }\n};\n\n// HttpResponse struct serialization\ntemplate<>\nstruct bsatn_traits<wire::HttpResponse> {\n    static void serialize(Writer& writer, const wire::HttpResponse& value) {\n        // Field 0: headers\n        bsatn::serialize(writer, value.headers);\n        // Field 1: version\n        bsatn::serialize(writer, value.version);\n        // Field 2: code\n        bsatn::serialize(writer, value.code);\n    }\n\n    static wire::HttpResponse deserialize(Reader& reader) {\n        wire::HttpResponse result;\n        // Field 0: headers\n        result.headers = bsatn::deserialize<wire::HttpHeaders>(reader);\n        // Field 1: version\n        result.version = bsatn::deserialize<wire::HttpVersion>(reader);\n        // Field 2: code\n        result.code = bsatn::deserialize<uint16_t>(reader);\n        return result;\n    }\n    \n    static AlgebraicType algebraic_type() {\n        ProductTypeBuilder builder;\n        builder.with_field<wire::HttpHeaders>(\"headers\");\n        builder.with_field<wire::HttpVersion>(\"version\");\n        builder.with_field<uint16_t>(\"code\");\n        return AlgebraicType::make_product(builder.build());\n    }\n};\n\n} // namespace SpacetimeDB::bsatn\n\n#endif // SPACETIMEDB_HTTP_WIRE_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/index_iterator.h",
    "content": "#ifndef SPACETIMEDB_INDEX_ITERATOR_H\n#define SPACETIMEDB_INDEX_ITERATOR_H\n\n/**\n * @file index_iterator.h\n * @brief Iterator for traversing indexed fields in SpacetimeDB tables\n * \n * IndexIterator provides efficient access to rows matching specific values or ranges\n * on indexed fields. Developers use indexed fields through the high-level `filter()` API\n * on field accessors (created by FIELD_Index macro), which internally returns an IndexIterator.\n * \n * The filter() API provides a clean, intuitive interface for index-based queries without\n * requiring manual index ID management.\n * \n * @example Basic usage with exact value matching:\n * @code\n * // Declare indexed field\n * FIELD_Index(person, age);\n * \n * // In a view or reducer, query persons with age 25\n * // The filter() method returns an IndexIteratorRange for clean syntax\n * for (const auto& person : ctx.db[person_age].filter(25u)) {\n *     // Process person aged 25...\n * }\n * @endcode\n * \n * @example Range queries for filtering within bounds:\n * @code\n * // Query persons between ages 25-30 (inclusive)\n * auto age_range = range_inclusive(uint8_t(25), uint8_t(30));\n * for (const auto& person : ctx.db[person_age].filter(age_range)) {\n *     // Process persons in age range...\n * }\n * \n * // Query persons 18 and older\n * auto adult_range = range_from(uint8_t(18));\n * for (const auto& person : ctx.db[person_age].filter(adult_range)) {\n *     // Process adult persons...\n * }\n * \n * // Query persons under 30\n * auto young_range = range_to(uint8_t(30));\n * size_t count = ctx.db[person_age].filter(young_range).size();\n * @endcode\n * \n * @see range_from, range_to, range_inclusive, range_to_inclusive, range_full for range construction\n * @see FIELD_Index for declaring indexed fields\n * @note IndexIterator is typically used indirectly through ctx.db[field_accessor].filter()\n */\n\n#include \"spacetimedb/bsatn/types.h\"\n#include <spacetimedb/bsatn/bsatn.h>\n#include <spacetimedb/abi/FFI.h>\n#include <spacetimedb/bsatn/reader.h>\n#include <spacetimedb/bsatn/writer.h>\n#include <spacetimedb/bsatn/traits.h>\n#include <spacetimedb/logger.h>\n#include <spacetimedb/range_queries.h>\n\n#include <string>\n#include <vector>\n#include <tuple>\n#include <stdexcept>\n#include <optional>\n#include <algorithm>\n#include <utility>\n#include <memory>\n\nnamespace SpacetimeDB {\n\n// =============================================================================\n// Type traits and tags for query detection\n// =============================================================================\n\n/// Tag types for constructor disambiguation\nstruct exact_match_tag {};\nstruct prefix_match_tag {};\n\n/// Detect if a type is std::tuple\ntemplate<typename T>\nstruct is_tuple : std::false_type {};\n\ntemplate<typename... Args>\nstruct is_tuple<std::tuple<Args...>> : std::true_type {};\n\ntemplate<typename T>\ninline constexpr bool is_tuple_v = is_tuple<T>::value;\n\n// =============================================================================\n// IndexIterator - Efficient index-based iteration\n// =============================================================================\n\ntemplate<typename T>\nclass IndexIterator {\n    static_assert(std::is_same_v<T, std::remove_cv_t<T>>, \n                  \"IndexIterator requires non-const, non-volatile type\");\n\npublic:\n    // STL iterator type definitions\n    using iterator_category = std::input_iterator_tag;\n    using value_type = T;\n    using difference_type = std::ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n\n    // Constructors\n    IndexIterator() noexcept : iter_handle_(Invalid::ROW_ITER), is_end_(true) {}\n    \n    /**\n     * @brief Create iterator for exact value match on an index\n     * \n     * Efficiently finds all rows where the indexed field exactly matches the given value.\n     * Uses btree index scanning for O(log n) lookup + O(k) iteration over k matching rows.\n     * \n     * @tparam FieldType The type of the indexed field (must match index column type)\n     * @param index_id The index to scan (from table index declaration)\n     * @param value The exact value to match\n     * \n     * @note This constructor is typically called internally by ctx.db[field_accessor].filter(value).\n     *       Developers should use the filter() API rather than constructing IndexIterator directly.\n     * \n     * @example How developers use indexed queries (via filter API):\n     * @code\n     * SPACETIMEDB_TABLE(Player, players, Public);\n     * FIELD_Index(players, level);  // Creates level index\n     * \n     * // In a view - find all level 0 players using filter()\n     * for (const auto& player : ctx.db[players_level].filter(0u)) {\n     *     LOG_INFO(\"Found level 0 player: \" + player.name);\n     * }\n     * @endcode\n     */\n    template<typename FieldType>\n    IndexIterator(IndexId index_id, const FieldType& value) {\n        // Serialize the exact value for point scan\n        SpacetimeDB::bsatn::Writer point_writer;\n        SpacetimeDB::bsatn::serialize(point_writer, value);\n        auto point_buffer = point_writer.take_buffer();\n\n        // Use optimized point scan for exact value matches\n        Status status = FFI::datastore_index_scan_point_bsatn(\n            index_id,\n            point_buffer.data(), point_buffer.size(),\n            &iter_handle_\n        );\n        \n        if (status != StatusCode::OK) {\n            std::abort(); // IndexIterator: datastore_index_scan_point_bsatn failed\n        }\n        advance();\n    }\n    \n    /**\n     * @brief Helper to serialize first N-1 elements of a tuple\n     */\n    template<typename Tuple, std::size_t... Is>\n    static void serialize_tuple_prefix(SpacetimeDB::bsatn::Writer& writer, const Tuple& tuple, std::index_sequence<Is...>) {\n        (serialize(writer, std::get<Is>(tuple)), ...);\n    }\n\n    /**\n     * @brief Helper to serialize Range as rstart/rend bounds\n     * Converts a Range<T> to the binary format expected by datastore_index_scan_range_bsatn\n     */\n    template<typename RangeT>\n    static std::vector<uint8_t> serialize_range_start(const RangeT& range) {\n        SpacetimeDB::bsatn::Writer writer;\n        \n        if (range.start) {\n            // Inclusive bound\n            writer.write_u8(0);  // BoundVariant::Inclusive\n            serialize(writer, *range.start);\n        } else {\n            // Unbounded - use Unbounded variant\n            writer.write_u8(2);  // BoundVariant::Unbounded\n        }\n        \n        return writer.take_buffer();\n    }\n\n    template<typename RangeT>\n    static std::vector<uint8_t> serialize_range_end(const RangeT& range) {\n        SpacetimeDB::bsatn::Writer writer;\n        \n        if (range.end) {\n            // Exclusive or Inclusive based on bound type\n            uint8_t variant = (range.bound_type == RangeBound::Inclusive) ? 0 : 1;\n            writer.write_u8(variant);  // BoundVariant::Inclusive(0) or Exclusive(1)\n            serialize(writer, *range.end);\n        } else {\n            // Unbounded\n            writer.write_u8(2);  // BoundVariant::Unbounded\n        }\n        \n        return writer.take_buffer();\n    }\n\n    /**\n     * @brief Create iterator for prefix-only match (N-1 columns specified)\n     * \n     * Finds all rows where the first N-1 indexed columns match, regardless of the last column.\n     * Useful for queries like \"find all scores for player 123 at any level\".\n     * \n     * @tparam PrefixType The type of the first indexed column\n     * @param index_id The multi-column index to scan\n     * @param prefix_value Value to match for the prefix column\n     * \n     * @example Prefix match - find all scores for a player:\n     * @code\n     * FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)\n     * \n     * // Find all scores for player 123 (any level)\n     * auto scores = ctx.db[score_by_player_and_level].filter(uint32_t(123));\n     * @endcode\n     */\n    template<typename PrefixType>\n    IndexIterator(prefix_match_tag, IndexId index_id, const PrefixType& prefix_value)\n        requires (!is_tuple_v<PrefixType> && !is_range_v<PrefixType>)\n    {\n        // Serialize prefix value\n        SpacetimeDB::bsatn::Writer prefix_writer;\n        serialize(prefix_writer, prefix_value);\n        auto prefix_buffer = prefix_writer.take_buffer();\n\n        // Create unbounded range for the remaining columns\n        SpacetimeDB::bsatn::Writer rstart_writer, rend_writer;\n        rstart_writer.write_u8(2);  // Unbounded\n        rend_writer.write_u8(2);    // Unbounded\n        auto rstart_buffer = rstart_writer.take_buffer();\n        auto rend_buffer = rend_writer.take_buffer();\n\n        // Call FFI with prefix_elems = 1 (only the first column)\n        Status status = FFI::datastore_index_scan_range_bsatn(\n            index_id,\n            prefix_buffer.data(), prefix_buffer.size(), ColId{1},\n            rstart_buffer.data(), rstart_buffer.size(),\n            rend_buffer.data(), rend_buffer.size(),\n            &iter_handle_\n        );\n        \n        if (status != StatusCode::OK) {\n            std::abort(); // IndexIterator: prefix-only match failed\n        }\n        advance();\n    }\n\n    /**\n     * @brief Create iterator for prefix match with range on last column\n     * \n     * Finds all rows where the first N-1 columns match exactly and the last column\n     * falls within the specified range.\n     * \n     * @example Range on last column - find scores for a player at specific levels:\n     * @code\n     * FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)\n     * \n     * // Find scores for player 123 at levels 1-10\n     * auto scores = ctx.db[score_by_player_and_level].filter(\n     *     std::make_tuple(uint32_t(123), range_inclusive(1u, 10u))\n     * );\n     * @endcode\n     */\n    template<typename PrefixType, typename RangeType>\n    IndexIterator(IndexId index_id, const std::tuple<PrefixType, RangeType>& values)\n        requires (is_range_v<RangeType>)\n    {\n        // Serialize prefix value\n        SpacetimeDB::bsatn::Writer prefix_writer;\n        serialize(prefix_writer, std::get<0>(values));\n        auto prefix_buffer = prefix_writer.take_buffer();\n\n        // Serialize range as start/end bounds\n        const auto& range = std::get<1>(values);\n        auto rstart_buffer = serialize_range_start(range);\n        auto rend_buffer = serialize_range_end(range);\n\n        // Call FFI with prefix_elems = 1 (only the prefix column)\n        Status status = FFI::datastore_index_scan_range_bsatn(\n            index_id,\n            prefix_buffer.data(), prefix_buffer.size(), ColId{1},\n            rstart_buffer.data(), rstart_buffer.size(),\n            rend_buffer.data(), rend_buffer.size(),\n            &iter_handle_\n        );\n        \n        if (status != StatusCode::OK) {\n            std::abort(); // IndexIterator: prefix+range match failed\n        }\n        advance();\n    }\n\n    /**\n     * @brief Create iterator for multi-column exact match\n     * \n     * Efficiently finds all rows where all indexed columns exactly match the tuple values.\n     * \n     * @tparam FieldTypes The types of the indexed fields\n     * @param index_id The multi-column index to scan\n     * @param values Tuple of values to match (one per column)\n     * \n     * @example Multi-column exact match:\n     * @code\n     * FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)\n     * \n     * // Find exact score for player 123 at level 5\n     * auto iter = IndexIterator<Score>(index_id, std::make_tuple(uint32_t(123), uint32_t(5)));\n     * @endcode\n     */\n    template<typename... FieldTypes>\n    IndexIterator(IndexId index_id, const std::tuple<FieldTypes...>& values) \n        requires (sizeof...(FieldTypes) > 1 && sizeof...(FieldTypes) <= 6)\n    {\n        constexpr std::size_t N = sizeof...(FieldTypes);\n        constexpr std::size_t prefix_count = N - 1;\n        \n        // Serialize first N-1 elements into prefix buffer\n        SpacetimeDB::bsatn::Writer prefix_writer;\n        serialize_tuple_prefix(prefix_writer, values, std::make_index_sequence<prefix_count>{});\n        auto prefix_buffer = prefix_writer.take_buffer();\n        \n        // Serialize the last element as both start and end bounds (exact match)\n        SpacetimeDB::bsatn::Writer bound_writer;\n        bound_writer.write_u8(0);  // Bound::Included\n        serialize(bound_writer, std::get<N - 1>(values));  // Last element only\n        auto bound_buffer = bound_writer.take_buffer();\n\n        // Call FFI with prefix_elems = N-1 (as per C# pattern)\n        Status status = FFI::datastore_index_scan_range_bsatn(\n            index_id,\n            prefix_buffer.data(), prefix_buffer.size(), ColId{static_cast<uint16_t>(prefix_count)},\n            bound_buffer.data(), bound_buffer.size(),  // Last value as start\n            bound_buffer.data(), bound_buffer.size(),  // Last value as end\n            &iter_handle_\n        );\n        \n        if (status != StatusCode::OK) {\n            std::abort(); // IndexIterator: multi-column exact match failed\n        }\n        advance();\n    }\n    \n    /**\n     * @brief Create iterator for range query on an index\n     * \n     * Efficiently iterates over rows where the indexed field falls within a specified range.\n     * Supports inclusive and exclusive bounds, unbounded ranges, and custom types.\n     * \n     * @tparam FieldType The type of the indexed field\n     * @param index_id The index to scan\n     * @param range The range specification (start, end, bound type)\n     * \n     * @note This constructor is typically called internally by ctx.db[field_accessor].filter(range).\n     *       Developers should use the filter() API with range helper functions rather than\n     *       constructing IndexIterator or Range objects directly.\n     * \n     * @example How developers use range queries (via filter API):\n     * @code\n     * FIELD_Index(person, age);\n     * \n     * // Find persons aged 25-30 using range_inclusive()\n     * auto age_range = range_inclusive(uint8_t(25), uint8_t(30));\n     * for (const auto& person : ctx.db[person_age].filter(age_range)) {\n     *     LOG_INFO(\"Person in range: \" + person.name);\n     * }\n     * \n     * // Find persons 18 and older using range_from()\n     * auto adult_range = range_from(uint8_t(18));\n     * size_t adult_count = ctx.db[person_age].filter(adult_range).size();\n     * \n     * // Find persons under 30 using range_to()\n     * auto young_range = range_to(uint8_t(30));\n     * for (const auto& person : ctx.db[person_age].filter(young_range)) {\n     *     LOG_INFO(\"Young person: \" + person.name);\n     * }\n     * @endcode\n     * \n     * @see range_from, range_to, range_inclusive, range_to_inclusive for creating ranges\n     */\n    template<typename FieldType>\n    IndexIterator(IndexId index_id, const Range<FieldType>& range) {\n        std::vector<uint8_t> start_buffer;\n        std::vector<uint8_t> end_buffer;\n\n        // Serialize range bounds if present\n        if (range.start.has_value()) {\n            SpacetimeDB::bsatn::Writer start_writer;\n            start_writer.write_u8(0);\n            SpacetimeDB::bsatn::serialize(start_writer, range.start.value());\n            start_buffer = start_writer.take_buffer();\n        } else {\n            start_buffer.push_back(2); // Bound::Unbounded tag\n        }\n        \n        if (range.end.has_value()) {\n            SpacetimeDB::bsatn::Writer end_writer;\n            uint8_t end_tag = (range.bound_type == RangeBound::Inclusive) ? 0 : 1;\n            end_writer.write_u8(end_tag);\n            SpacetimeDB::bsatn::serialize(end_writer, range.end.value());\n            end_buffer = end_writer.take_buffer();\n        } else {\n            end_buffer.push_back(2); // Bound::Unbounded tag\n        }\n        \n        // Call range scan with no prefix (range queries on single column)\n        Status status = FFI::datastore_index_scan_range_bsatn(\n            index_id,\n            nullptr, 0, ColId{0},  // no prefix for range queries\n            start_buffer.empty() ? nullptr : start_buffer.data(), start_buffer.size(),\n            end_buffer.empty() ? nullptr : end_buffer.data(), end_buffer.size(),\n            &iter_handle_\n        );\n        \n        if (status != StatusCode::OK) {\n            std::abort(); // IndexIterator: datastore_index_scan_range_bsatn failed\n        }\n        \n        // Apply inclusive/exclusive bounds during iteration\n        bound_type_ = range.bound_type;\n        // Note: bounds are handled by the btree scan itself\n        \n        advance();\n    }\n\n    ~IndexIterator() noexcept {\n        if (iter_handle_ != Invalid::ROW_ITER && !ffi_exhausted_) {\n            FFI::row_iter_bsatn_close(iter_handle_);\n        }\n    }\n\n    // Move-only semantics\n    IndexIterator(const IndexIterator&) = delete;\n    IndexIterator& operator=(const IndexIterator&) = delete;\n    \n    IndexIterator(IndexIterator&& other) noexcept : IndexIterator() {\n        swap(*this, other);\n    }\n    \n    IndexIterator& operator=(IndexIterator&& other) noexcept {\n        if (this != &other) {\n            IndexIterator temp(std::move(other));\n            swap(*this, temp);\n        }\n        return *this;\n    }\n    \n    friend void swap(IndexIterator& a, IndexIterator& b) noexcept {\n        using std::swap;\n        swap(a.iter_handle_, b.iter_handle_);\n        swap(a.row_buffer_, b.row_buffer_);\n        swap(a.current_batch_, b.current_batch_);\n        swap(a.current_index_, b.current_index_);\n        swap(a.current_row_, b.current_row_);\n        swap(a.is_valid_, b.is_valid_);\n        swap(a.is_end_, b.is_end_);\n        swap(a.ffi_exhausted_, b.ffi_exhausted_);\n        swap(a.bound_type_, b.bound_type_);\n    }\n\n    // Iterator operations\n    const T& operator*() const {\n        if (!is_valid_) std::abort();\n        return current_row_;\n    }\n    \n    const T* operator->() const { return &**this; }\n    \n    IndexIterator& operator++() {\n        advance();\n        return *this;\n    }\n    \n    bool operator==(const IndexIterator& other) const noexcept {\n        return is_valid_ == other.is_valid_;\n    }\n    \n    bool operator!=(const IndexIterator& other) const noexcept {\n        return !(*this == other);\n    }\n    \n    // Range-based for loop support\n    // Returns self to support: for (auto& row : ctx.db[field].filter(value)) { ... }\n    // This works because the temporary returned from filter() stays alive for the loop\n    IndexIterator& begin() noexcept { return *this; }\n    IndexIterator end() const noexcept { return IndexIterator(); }\n    \n    // Explicitly allow const iteration - needed for range-based for loops\n    // The iterator itself is const-iterable (doesn't modify internal state during iteration)\n    const IndexIterator& cbegin() const noexcept { return *this; }\n    const IndexIterator cend() const noexcept { return IndexIterator(); }\n    \n    /**\n     * @brief Collect all remaining results into a vector\n     * \n     * Convenient method to materialize all matching rows from the iterator\n     * into a std::vector without manual iteration.\n     * \n     * @return Vector containing all matching rows\n     */\n    std::vector<T> collect() {\n        std::vector<T> results;\n        while (is_valid_) {\n            results.push_back(std::move(current_row_));\n            advance();\n        }\n        return results;\n    }\n\nprivate:\n    RowIter iter_handle_ = Invalid::ROW_ITER;\n    std::vector<uint8_t> row_buffer_;\n    std::vector<T> current_batch_;\n    size_t current_index_ = 0;\n    T current_row_;\n    bool is_valid_ = false;\n    bool is_end_ = false;\n    bool ffi_exhausted_ = false;\n    \n    // For handling inclusive/exclusive bounds\n    RangeBound bound_type_ = RangeBound::Exclusive;\n    // Note: end_value_ tracking would require knowing the field type\n    // For now we rely on btree scan to handle bounds correctly\n    \n    // Constants for performance tuning\n    static constexpr size_t INITIAL_ROW_BUFFER_SIZE = 4096;\n    static constexpr size_t MAX_ROW_BUFFER_SIZE = 1024 * 1024;\n    static constexpr size_t TYPICAL_BATCH_SIZE = 32;\n    static constexpr int16_t ITER_EXHAUSTED = -1;\n    static constexpr int16_t ITER_OK = 0;\n    static constexpr uint16_t ERROR_BUFFER_TOO_SMALL = 3;\n    \n    // Helper to serialize tuple elements without treating tuple as a type\n    template<typename... FieldTypes, std::size_t... Is>\n    static void serialize_tuple_elements(SpacetimeDB::bsatn::Writer& writer, \n                                        const std::tuple<FieldTypes...>& values,\n                                        std::index_sequence<Is...>) {\n        (SpacetimeDB::bsatn::serialize(writer, std::get<Is>(values)), ...);\n    }\n    \n    void advance() {\n        if (is_end_) {\n            is_valid_ = false;\n            return;\n        }\n        \n        // Try current batch first\n        if (current_index_ < current_batch_.size()) {\n            current_row_ = std::move(current_batch_[current_index_++]);\n            is_valid_ = true;\n            return;\n        }\n        \n        // Need new batch - but check if FFI is already exhausted\n        if (ffi_exhausted_) {\n            is_end_ = true;\n            is_valid_ = false;\n            return;\n        }\n        \n        // Fetch new batch\n        fetch_batch();\n        \n        // Try again with new batch\n        if (current_index_ < current_batch_.size()) {\n            current_row_ = std::move(current_batch_[current_index_++]);\n            is_valid_ = true;\n        } else {\n            is_end_ = true;\n            is_valid_ = false;\n        }\n    }\n    \n    void fetch_batch() {\n        row_buffer_.resize(INITIAL_ROW_BUFFER_SIZE);\n        size_t buffer_len = row_buffer_.size();\n        \n        int16_t ret = FFI::row_iter_bsatn_advance(iter_handle_, \n                                                  row_buffer_.data(), \n                                                  &buffer_len);\n        \n        if (ret == ITER_EXHAUSTED) {\n            ffi_exhausted_ = true;\n            if (buffer_len > 0) {\n                row_buffer_.resize(buffer_len);\n                deserialize_batch(buffer_len);\n            }\n            return;\n        }\n        \n        if (ret == ERROR_BUFFER_TOO_SMALL) {\n            if (buffer_len > MAX_ROW_BUFFER_SIZE) {\n                std::abort(); // Buffer size exceeds maximum\n            }\n            row_buffer_.resize(buffer_len);\n            ret = FFI::row_iter_bsatn_advance(iter_handle_, \n                                             row_buffer_.data(), \n                                             &buffer_len);\n        }\n        \n        if (ret > 0) {\n            std::abort(); // IndexIterator: row_iter_bsatn_advance failed\n        }\n        \n        row_buffer_.resize(buffer_len);\n        deserialize_batch(buffer_len);\n    }\n    \n    void deserialize_batch(size_t buffer_len) {\n        current_batch_.clear();\n        current_batch_.reserve(TYPICAL_BATCH_SIZE);\n        current_index_ = 0;\n        \n        if (buffer_len == 0) return;\n        \n        SpacetimeDB::bsatn::Reader reader(row_buffer_.data(), buffer_len);\n        while (!reader.is_eos()) {\n            // Without exceptions, deserialization failures will abort\n            current_batch_.emplace_back(SpacetimeDB::bsatn::deserialize<T>(reader));\n        }\n    }\n};\n\n// =============================================================================\n// Range Wrapper for clean range-based for loops\n// =============================================================================\n\n/**\n * @brief Lightweight wrapper to make IndexIterator work with range-based for loops\n * \n * Provides the range interface while holding the move-only IndexIterator,\n * allowing clean syntax: for (auto& row : ctx.db[field].filter(value)) { ... }\n */\ntemplate<typename T>\nclass IndexIteratorRange {\nprivate:\n    struct Iterator {\n        std::shared_ptr<IndexIterator<T>> iter;\n        bool is_end = true;\n\n        Iterator() = default;\n        Iterator(std::shared_ptr<IndexIterator<T>> it, bool end) noexcept\n            : iter(std::move(it)), is_end(end) {}\n\n        const T& operator*() const noexcept { return **iter; }\n        const T* operator->() const noexcept { return iter->operator->(); }\n\n        Iterator& operator++() {\n            ++(*iter);\n            if (*iter == IndexIterator<T>()) {\n                is_end = true;\n                iter.reset();\n            }\n            return *this;\n        }\n\n        bool operator==(const Iterator& other) const noexcept {\n            if (is_end && other.is_end) return true;\n            return is_end == other.is_end && iter == other.iter;\n        }\n\n        bool operator!=(const Iterator& other) const noexcept { return !(*this == other); }\n    };\n\n    std::shared_ptr<IndexIterator<T>> iter_;\n    \npublic:\n    explicit IndexIteratorRange(IndexIterator<T>&& it) noexcept\n        : iter_(std::make_shared<IndexIterator<T>>(std::move(it))) {}\n\n    Iterator begin() {\n        if (!iter_ || *iter_ == IndexIterator<T>()) {\n            return Iterator(nullptr, true);\n        }\n        return Iterator(iter_, false);\n    }\n\n    Iterator end() { return Iterator(nullptr, true); }\n\n    /**\n     * @brief Materialize all remaining results into a vector\n     * \n     * Convenience method to collect all matching rows without manual iteration.\n     * \n     * @return Vector containing all matching rows\n     */\n    std::vector<T> collect() {\n        if (!iter_) return {};\n        return iter_->collect();\n    }\n\n    /**\n     * @brief Count all remaining results\n     *\n     * Note: This consumes the iterator, just like iterating in a for-loop.\n     */\n    size_t size() {\n        size_t count = 0;\n        for (auto it = begin(); it != end(); ++it) {\n            ++count;\n        }\n        return count;\n    }\n\n    /**\n     * @brief Alias for size()\n     *\n     * Note: This consumes the iterator, just like iterating in a for-loop.\n     */\n    size_t count() { return size(); }\n};\n} // namespace SpacetimeDB\n\n\n#endif // SPACETIMEDB_INDEX_ITERATOR_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/Module.h",
    "content": "#ifndef SPACETIMEDB_MODULE_H\n#define SPACETIMEDB_MODULE_H\n\n#include <string>\n#include <vector>\n#include <optional>\n#include \"../bsatn/types.h\"\n#include \"../reducer_macros.h\"\n#include \"../reducer_context.h\"\n#include \"../abi/opaque_types.h\"\n#include <spacetimedb/abi/FFI.h>  // For StatusCode\n#include \"autogen/TableAccess.g.h\"  // For TableAccess enum\n#include \"autogen/Lifecycle.g.h\"  // For Lifecycle enum\n#include \"autogen/CaseConversionPolicy.g.h\"  // For CaseConversionPolicy\n\nnamespace SpacetimeDB {\nnamespace bsatn {\nclass Writer;  // Forward declaration\n}\n\nnamespace Internal {\n\n// Forward declarations are handled by includes\n\n// Module class - singleton pattern similar to C# static class\nclass Module {\nprivate:\n    Module() = default;\n    Module(const Module&) = delete;\n    Module& operator=(const Module&) = delete;\n    \npublic:\n    static Module& Instance() {\n        static Module instance;\n        return instance;\n    }\n    \n    // Initialize module (called once)\n    \n    // Module description for FFI (matching existing signature)\n    static void __describe_module__(BytesSink sink);\n    static std::vector<uint8_t> SerializeModuleDef();\n    \n    // Reducer invocation for FFI (matching existing signature)\n    static Status __call_reducer__(\n        uint32_t id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        uint64_t conn_id_0, uint64_t conn_id_1,\n        Timestamp timestamp,\n        BytesSource args_source,\n        BytesSink error_sink\n    );\n    \n    // View invocation for FFI (with sender)\n    static int16_t __call_view__(\n        uint32_t id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        BytesSource args_source,\n        BytesSink result_sink\n    );\n    \n    // View invocation for FFI (anonymous - no sender)\n    static int16_t __call_view_anon__(\n        uint32_t id,\n        BytesSource args_source,\n        BytesSink result_sink\n    );\n    \n    // Procedure invocation for FFI\n    static int16_t __call_procedure__(\n        uint32_t id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        uint64_t conn_id_0, uint64_t conn_id_1,\n        uint64_t timestamp_microseconds,\n        BytesSource args_source,\n        BytesSink result_sink\n    );\n    \n    // Internal registration methods (inline to avoid linking issues)\n    template<typename T>\n    static void RegisterTableInternal(const char* name, bool is_public, bool is_event = false) {\n        // Forward declaration - implementation will be included at end of file\n        RegisterTableInternalImpl<T>(name, is_public, is_event);\n    }\n    \n    template<typename Func>\n    static void RegisterReducerInternal(const std::string& name, Func func) {\n        // Forward declaration - implementation will be included at end of file  \n        RegisterReducerInternalImpl<Func>(name, func);\n    }\n    \n    // Implementation methods (will be defined after including Module_impl.h)\n    template<typename T>\n    static void RegisterTableInternalImpl(const char* name, bool is_public, bool is_event = false);\n\npublic:\n    // These need to be public for macro access\n    template<typename Func>\n    static void RegisterReducerInternalImpl(const std::string& name, Func func);\n    \n    template<typename Func>\n    static void RegisterReducerInternalWithNames(const std::string& name, Func func, const std::vector<std::string>& param_names);\n    \nprivate:\n    \npublic:\n    // Registration support routed through the V10 module-definition builder.\n    static void RegisterClientVisibilityFilter(const char* sql);\n    static void SetCaseConversionPolicy(CaseConversionPolicy policy);\n    static void RegisterExplicitTableName(const std::string& source_name, const std::string& canonical_name);\n    static void RegisterExplicitFunctionName(const std::string& source_name, const std::string& canonical_name);\n    static void RegisterExplicitIndexName(const std::string& source_name, const std::string& canonical_name);\n};\n\n// Helper functions for module description\nstd::vector<uint8_t> ConsumeBytes(BytesSource source);\nvoid WriteBytes(BytesSink sink, const std::vector<uint8_t>& bytes);\n\nvoid SetTableIsEventFlag(const std::string& table_name, bool is_event);\nbool GetTableIsEventFlag(const std::string& table_name);\n\n} // namespace Internal\n\n// Public alias to mirror C# API shape (`SpacetimeDB.CaseConversionPolicy`).\nusing CaseConversionPolicy = Internal::CaseConversionPolicy;\n\n// Public API similar to C# Module class\nclass Module {\npublic:\n    // Table registration\n    template<typename T>\n    static void RegisterTable(const char* name, bool is_public = true, bool is_event = false) {\n        Internal::Module::RegisterTableInternal<T>(name, is_public, is_event);\n    }\n    \n    // Reducer registration\n    template<typename Func>\n    static void RegisterReducer(const char* name, Func func) {\n        Internal::Module::RegisterReducerInternal(name, func);\n    }\n    \n    // Client visibility filter (similar to C# / Rust)\n    static void RegisterClientVisibilityFilter(const char* sql) {\n        Internal::Module::RegisterClientVisibilityFilter(sql);\n    }\n    \n    // Module metadata (future extension)\n    static void SetMetadata([[maybe_unused]] const char* name, [[maybe_unused]] const char* version) {\n        // TODO: Implement module metadata\n    }\n\n    static void SetCaseConversionPolicy(CaseConversionPolicy policy) {\n        Internal::Module::SetCaseConversionPolicy(policy);\n    }\n\n    static void RegisterExplicitTableName(const char* source_name, const char* canonical_name) {\n        Internal::Module::RegisterExplicitTableName(source_name, canonical_name);\n    }\n\n    static void RegisterExplicitFunctionName(const char* source_name, const char* canonical_name) {\n        Internal::Module::RegisterExplicitFunctionName(source_name, canonical_name);\n    }\n\n    static void RegisterExplicitIndexName(const char* source_name, const char* canonical_name) {\n        Internal::Module::RegisterExplicitIndexName(source_name, canonical_name);\n    }\n};\n\n// Global registration functions for X-Macro support\ntemplate<typename T>\nvoid register_table_impl(const char* name, bool is_public) {\n    Internal::Module::RegisterTableInternal<T>(name, is_public);\n}\n\ntemplate<typename Func>\nvoid register_reducer_impl(const std::string& name, Func func) {\n    Internal::Module::RegisterReducerInternal(name, func);\n}\n\n// Initialize module - no-op; preinit functions handle registration.\ninline void initialize_module() {\n    // No-op.\n}\n\n// Write module definition (for FFI)\ninline void spacetimedb_write_module_def(uint32_t sink) {\n    BytesSink bs{sink};\n    Internal::Module::__describe_module__(bs);\n}\n\n// Call reducer (for FFI)\ninline int16_t spacetimedb_call_reducer(uint32_t id, uint32_t args, \n                                       uint64_t sender_0, uint64_t sender_1, \n                                       uint64_t sender_2, uint64_t sender_3) {\n    // Create a simple timestamp\n    Timestamp ts(0);\n    BytesSource args_source{args};\n    BytesSink error_sink{0};  // Null sink for now\n    \n    auto status = Internal::Module::__call_reducer__(\n        id, sender_0, sender_1, sender_2, sender_3, \n        0, 0, ts, args_source, error_sink);\n    \n    return is_ok(status) ? 0 : -1;\n}\n\n} // namespace SpacetimeDB\n\n// Include the template implementations\n#include \"Module_impl.h\"\n\n#endif // SPACETIMEDB_MODULE_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/Module_impl.h",
    "content": "#ifndef SPACETIMEDB_MODULE_IMPL_H\n#define SPACETIMEDB_MODULE_IMPL_H\n\n/**\n * SpacetimeDB C++ bindings - Module Implementation\n * \n * Core module system implementation with optimized type handling, \n * table registration, and reducer management.\n * \n * Architecture:\n * - Type traits for compile-time type detection\n * - BSATN serialization utilities\n * - Unified table and reducer registration\n * - V9 builder integration for constraints\n */\n\n#include \"Module.h\"\n#include \"field_registration.h\"\n#include \"../table_with_constraints.h\"\n#include \"../outcome.h\"\n#include <spacetimedb/abi/FFI.h>\n#include \"bsatn_adapters.h\"\n#include <spacetimedb/bsatn/algebraic_type.h>\n#include <spacetimedb/bsatn/traits.h>\n#include <spacetimedb/bsatn/type_extensions.h>\n#include \"autogen/Lifecycle.g.h\"\n#include \"v10_builder.h\"\n#include <cstring>\n#include <cstdio>\n#include <vector>\n#include <string>\n#include <optional>\n#include <functional>\n#include <utility>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n// =============================================================================\n// TYPE TRAITS\n// =============================================================================\n\n// Detect unit structs\ntemplate<typename T>\nstruct is_unit_struct_type {\nprivate:\n    template<typename U>\n    static auto test(int) -> decltype(U::__is_unit_type__, std::bool_constant<U::__is_unit_type__>{});\n    template<typename>\n    static std::false_type test(...);\npublic:\n    static constexpr bool value = decltype(test<T>(0))::value;\n};\n\ntemplate<typename T>\ninline constexpr bool is_unit_struct_v = is_unit_struct_type<T>::value;\n\n// Detect vector types\ntemplate<typename T>\nstruct is_vector_type : std::false_type {};\n\ntemplate<typename T, typename Alloc>\nstruct is_vector_type<std::vector<T, Alloc>> : std::true_type {};\n\n// Detect optional types\ntemplate<typename T>\nstruct is_optional : std::false_type {};\n\ntemplate<typename T>\nstruct is_optional<std::optional<T>> : std::true_type {};\n\n// Check for big integer types\ntemplate<typename T>\nconstexpr bool is_big_integer_v = std::is_same_v<T, ::SpacetimeDB::u128> ||\n                                   std::is_same_v<T, ::SpacetimeDB::i128> ||\n                                   std::is_same_v<T, ::SpacetimeDB::u256> ||\n                                   std::is_same_v<T, ::SpacetimeDB::i256>;\n\n// Check if type needs registry registration\ntemplate<typename T>\nconstexpr bool needs_type_registration_v = (!std::is_arithmetic_v<T> && \n                                          !std::is_same_v<T, std::string> &&\n                                          !is_big_integer_v<T>) ||\n                                          requires { bsatn::bsatn_traits<T>::algebraic_type(); };\n\n// Get BSATN tag for primitive types\ntemplate<typename T>\nstruct type_id {\n    static constexpr uint8_t value = []() {\n        if constexpr (std::is_same_v<T, bool>) return 4;\n        else if constexpr (std::is_same_v<T, uint8_t>) return 5;\n        else if constexpr (std::is_same_v<T, int8_t>) return 6;\n        else if constexpr (std::is_same_v<T, uint16_t>) return 7;\n        else if constexpr (std::is_same_v<T, int16_t>) return 8;\n        else if constexpr (std::is_same_v<T, uint32_t>) return 9;\n        else if constexpr (std::is_same_v<T, int32_t>) return 10;\n        else if constexpr (std::is_same_v<T, uint64_t>) return 11;\n        else if constexpr (std::is_same_v<T, int64_t>) return 12;\n        else if constexpr (std::is_same_v<T, float>) return 17;\n        else if constexpr (std::is_same_v<T, double>) return 18;\n        else if constexpr (std::is_same_v<T, std::string>) return 19;\n        else return 0; // Complex type\n    }();\n};\n\n// Check for BSATN traits\ntemplate<typename T, typename = void>\nstruct has_bsatn_traits : std::false_type {};\n\ntemplate<typename T>\nstruct has_bsatn_traits<T, std::void_t<decltype(bsatn::bsatn_traits<T>::deserialize(std::declval<bsatn::Reader&>()))>> : std::true_type {};\n\ntemplate<typename T>\nconstexpr bool has_bsatn_traits_v = has_bsatn_traits<T>::value;\n\n// Check if type can be written inline\ntemplate<typename T>\nconstexpr bool is_basic_inlineable_v = \n    std::is_arithmetic_v<T> || std::is_same_v<T, std::string> ||\n    std::is_same_v<T, Identity> || std::is_same_v<T, ConnectionId> || \n    std::is_same_v<T, Timestamp> || std::is_same_v<T, TimeDuration> ||\n    std::is_same_v<T, u128> || std::is_same_v<T, u256> ||\n    std::is_same_v<T, i128> || std::is_same_v<T, i256>;\n\n// =============================================================================\n// BINARY I/O UTILITIES\n// =============================================================================\n\ninline void write_u32(std::vector<uint8_t>& buf, uint32_t val) {\n    bsatn::Writer writer(buf);\n    writer.write_u32_le(val);\n}\n\ninline void write_string(std::vector<uint8_t>& buf, const std::string& str) {\n    bsatn::Writer writer(buf);\n    writer.write_string(str);\n}\n\ninline uint8_t read_u8(uint32_t source) {\n    BytesSource src{source};\n    BytesSourceReader reader(src);\n    return reader.read_u8();\n}\n\ninline uint32_t read_u32(uint32_t source) {\n    BytesSource src{source};\n    BytesSourceReader reader(src);\n    return reader.read_u32_le();\n}\n\n// =============================================================================\n// TABLE REGISTRATION\n// =============================================================================\n\n// Unified table registration - single implementation\ntemplate<typename T>\nvoid Module::RegisterTableInternalImpl(const char* name, bool is_public, bool is_event) {\n    // V10 registration entrypoint.\n    SetTableIsEventFlag(name, is_event);\n    getV10Builder().RegisterTable<T>(name, is_public, is_event);\n}\n\n// =============================================================================\n// ARGUMENT DESERIALIZATION\n// =============================================================================\n\ntemplate<typename T>\nT read_arg(uint32_t source) {\n    BytesSource src{source};\n    \n    if constexpr (is_unit_struct_v<T>) {\n        return T{};\n    } else if constexpr (!needs_type_registration_v<T>) {\n        BytesSourceReader reader(src);\n        if constexpr (std::is_same_v<T, bool>) {\n            return reader.read_bool();\n        } else if constexpr (std::is_same_v<T, uint8_t>) {\n            return reader.read_u8();\n        } else if constexpr (std::is_same_v<T, uint16_t>) {\n            return reader.read_u16_le();\n        } else if constexpr (std::is_same_v<T, uint32_t>) {\n            return reader.read_u32_le();\n        } else if constexpr (std::is_same_v<T, uint64_t>) {\n            return reader.read_u64_le();\n        } else if constexpr (std::is_same_v<T, int8_t>) {\n            return reader.read_i8();\n        } else if constexpr (std::is_same_v<T, int16_t>) {\n            return reader.read_i16_le();\n        } else if constexpr (std::is_same_v<T, int32_t>) {\n            return reader.read_i32_le();\n        } else if constexpr (std::is_same_v<T, int64_t>) {\n            return reader.read_i64_le();\n        } else if constexpr (std::is_same_v<T, float>) {\n            return reader.read_f32_le();\n        } else if constexpr (std::is_same_v<T, double>) {\n            return reader.read_f64_le();\n        } else if constexpr (std::is_same_v<T, std::string>) {\n            return reader.read_string();\n        } else {\n            static_assert(!sizeof(T), \"Unsupported primitive type\");\n        }\n    } else if constexpr (has_bsatn_traits_v<T>) {\n        std::vector<uint8_t> buffer;\n        constexpr size_t CHUNK_SIZE = 256;\n        uint8_t chunk[CHUNK_SIZE];\n        \n        while (true) {\n            size_t requested = CHUNK_SIZE;\n            size_t actual = requested;\n            FFI::bytes_source_read(src, chunk, &actual);\n            \n            if (actual == 0) break;\n            buffer.insert(buffer.end(), chunk, chunk + actual);\n            if (actual < requested) break;\n        }\n        \n        bsatn::Reader reader(buffer);\n        return bsatn::bsatn_traits<T>::deserialize(reader);\n    } else {\n        static_assert(std::is_default_constructible_v<T>, \n                      \"Type must be default constructible or have BSATN traits\");\n        return T{};\n    }\n}\n\n// Read multiple arguments as tuple\ntemplate<typename... Args>\nauto read_args_tuple(uint32_t args_source) {\n    BytesSource src{args_source};\n    \n    // Handle single unit struct specially\n    if constexpr (sizeof...(Args) == 1) {\n        using FirstArg = std::tuple_element_t<0, std::tuple<Args...>>;\n        if constexpr (is_unit_struct_v<FirstArg>) {\n            return std::make_tuple(FirstArg{});\n        }\n    }\n    \n    if constexpr ((has_bsatn_traits_v<Args> || ...)) {\n        std::vector<uint8_t> buffer;\n        constexpr size_t CHUNK_SIZE = 256;\n        uint8_t chunk[CHUNK_SIZE];\n        \n        while (true) {\n            size_t requested = CHUNK_SIZE;\n            size_t actual = requested;\n            FFI::bytes_source_read(src, chunk, &actual);\n            \n            if (actual == 0) break;\n            buffer.insert(buffer.end(), chunk, chunk + actual);\n            if (actual < requested) break;\n        }\n        \n        bsatn::Reader reader(buffer);\n        return std::make_tuple(bsatn::bsatn_traits<Args>::deserialize(reader)...);\n    } else {\n        BytesSourceReader reader(src);\n        return std::make_tuple([&reader]() -> Args {\n            if constexpr (std::is_same_v<Args, uint32_t>) {\n                return reader.read_u32_le();\n            } else if constexpr (std::is_same_v<Args, int32_t>) {\n                return reader.read_i32_le();\n            } else if constexpr (std::is_same_v<Args, uint64_t>) {\n                return reader.read_u64_le();\n            } else if constexpr (std::is_same_v<Args, int64_t>) {\n                return reader.read_i64_le();\n            } else if constexpr (std::is_same_v<Args, bool>) {\n                return reader.read_bool();\n            } else if constexpr (std::is_same_v<Args, std::string>) {\n                return reader.read_string();\n            } else {\n                return bsatn::bsatn_traits<Args>::deserialize(reader);\n            }\n        }()...);\n    }\n}\n\n// =============================================================================\n// REDUCER WRAPPERS\n// =============================================================================\n\n// Lifecycle reducer wrapper\ntemplate<typename Func>\nvoid builtin_reducer_wrapper(Func func, ReducerContext& ctx, \n                           uint64_t sender_0, uint64_t sender_1, \n                           uint64_t sender_2, uint64_t sender_3) {\n    std::array<uint8_t, 32> senderBytes{};\n    memcpy(senderBytes.data(), &sender_0, sizeof(uint64_t));\n    memcpy(senderBytes.data() + 8, &sender_1, sizeof(uint64_t));\n    memcpy(senderBytes.data() + 16, &sender_2, sizeof(uint64_t));\n    memcpy(senderBytes.data() + 24, &sender_3, sizeof(uint64_t));\n    Identity sender(senderBytes);\n    \n    if constexpr (std::is_invocable_v<Func, ReducerContext>) {\n        func(ctx);\n    } else if constexpr (std::is_invocable_v<Func, ReducerContext, Identity>) {\n        func(ctx, sender);\n    }\n}\n\n// Generic reducer wrapper\ntemplate<typename... Args>\nvoid spacetimedb_reducer_wrapper(void (*func)(ReducerContext, Args...), \n                                ReducerContext& ctx, uint32_t args_source) {\n    if constexpr (sizeof...(Args) == 0) {\n        func(ctx);\n    } else {\n        auto args_tuple = read_args_tuple<Args...>(args_source);\n        std::apply([&](auto&&... args) {\n            func(ctx, std::forward<decltype(args)>(args)...);\n        }, args_tuple);\n    }\n}\n\n// =============================================================================\n// TYPE SERIALIZATION\n// =============================================================================\n\n// Write algebraic type inline (for special types and primitives)\ntemplate<typename T>\nvoid write_algebraic_type_inline(std::vector<uint8_t>& buf) {\n    if constexpr (is_optional<T>::value) {\n        using inner_type = typename T::value_type;\n        \n        buf.push_back(1); // AlgebraicType::Sum\n        write_u32(buf, 2); // 2 variants\n        \n        buf.push_back(0); // Has name\n        write_string(buf, \"some\");\n        write_algebraic_type_inline<inner_type>(buf);\n        \n        buf.push_back(0); // Has name\n        write_string(buf, \"none\");\n        buf.push_back(2); // AlgebraicType::Product (empty)\n        write_u32(buf, 0); // 0 elements\n        \n    } else if constexpr (is_vector_type<T>::value) {\n        using element_type = typename T::value_type;\n        buf.push_back(3); // AlgebraicType::Array\n        write_algebraic_type_inline<element_type>(buf);\n        \n    } else if constexpr (std::is_same_v<T, int32_t>) {\n        buf.push_back(10);\n    } else if constexpr (std::is_same_v<T, uint32_t>) {\n        buf.push_back(11);\n    } else if constexpr (std::is_same_v<T, std::string>) {\n        buf.push_back(4);\n    } else if constexpr (std::is_same_v<T, bool>) {\n        buf.push_back(5);\n    } else if constexpr (std::is_same_v<T, int8_t>) {\n        buf.push_back(6);\n    } else if constexpr (std::is_same_v<T, uint8_t>) {\n        buf.push_back(7);\n    } else if constexpr (std::is_same_v<T, int16_t>) {\n        buf.push_back(8);\n    } else if constexpr (std::is_same_v<T, uint16_t>) {\n        buf.push_back(9);\n    } else if constexpr (std::is_same_v<T, int64_t>) {\n        buf.push_back(12);\n    } else if constexpr (std::is_same_v<T, uint64_t>) {\n        buf.push_back(13);\n    } else if constexpr (std::is_same_v<T, float>) {\n        buf.push_back(18);\n    } else if constexpr (std::is_same_v<T, double>) {\n        buf.push_back(19);\n    } else if constexpr (std::is_same_v<T, u128>) {\n        buf.push_back(15);\n    } else if constexpr (std::is_same_v<T, u256>) {\n        buf.push_back(17);\n    } else if constexpr (std::is_same_v<T, i128>) {\n        buf.push_back(14);\n    } else if constexpr (std::is_same_v<T, i256>) {\n        buf.push_back(16);\n    } else if constexpr (std::is_same_v<T, Identity>) {\n        buf.push_back(2); // AlgebraicType::Product\n        write_u32(buf, 1); // 1 field\n        buf.push_back(0); // Some (has name)\n        write_string(buf, bsatn::IDENTITY_TAG);\n        buf.push_back(17); // AlgebraicType::U256\n    } else if constexpr (std::is_same_v<T, ConnectionId>) {\n        buf.push_back(2); // AlgebraicType::Product\n        write_u32(buf, 1); // 1 field\n        buf.push_back(0); // Some (has name)\n        write_string(buf, bsatn::CONNECTION_ID_TAG);\n        buf.push_back(15); // AlgebraicType::U128\n    } else if constexpr (std::is_same_v<T, Timestamp>) {\n        buf.push_back(2); // AlgebraicType::Product\n        write_u32(buf, 1); // 1 field\n        buf.push_back(0); // Some (has name)\n        write_string(buf, bsatn::TIMESTAMP_TAG);\n        buf.push_back(12); // AlgebraicType::I64\n    } else if constexpr (std::is_same_v<T, TimeDuration>) {\n        buf.push_back(2); // AlgebraicType::Product\n        write_u32(buf, 1); // 1 field\n        buf.push_back(0); // Some (has name)\n        write_string(buf, bsatn::TIME_DURATION_TAG);\n        buf.push_back(12); // AlgebraicType::I64\n    } else if constexpr (std::is_enum_v<T>) {\n        buf.push_back(4); // String fallback for complex enums\n    } else {\n        buf.push_back(4); // AlgebraicType::String fallback\n    }\n}\n\n// =============================================================================\n// REDUCER REGISTRATION\n// =============================================================================\n\ninline std::optional<Lifecycle> get_lifecycle_for_name(const std::string& name) {\n    if (name == \"init\") return Lifecycle::Init;\n    if (name == \"client_connected\") return Lifecycle::OnConnect;\n    if (name == \"client_disconnected\") return Lifecycle::OnDisconnect;\n    return std::nullopt;\n}\n\ntemplate<typename Func>\nvoid Module::RegisterReducerInternalImpl(const std::string& name, Func func) {\n    auto lifecycle = get_lifecycle_for_name(name);\n    if (lifecycle.has_value()) {\n        getV10Builder().RegisterLifecycleReducer(name, func, lifecycle.value());\n    } else {\n        getV10Builder().RegisterReducer(name, func, std::vector<std::string>{});\n    }\n}\n\ntemplate<typename Func>\nvoid Module::RegisterReducerInternalWithNames(const std::string& name, Func func, const std::vector<std::string>& param_names) {\n    auto lifecycle = get_lifecycle_for_name(name);\n    if (lifecycle.has_value()) {\n        getV10Builder().RegisterLifecycleReducer(name, func, lifecycle.value());\n    } else {\n        getV10Builder().RegisterReducer(name, func, param_names);\n    }\n}\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ntemplate<typename T>\nvoid register_table_type(const char* name, bool is_public) {\n    Module::RegisterTableInternalImpl<T>(name, is_public);\n}\n\n// Optimized parameter name parsing\ninline std::vector<std::string> parse_parameter_names(const std::string& params_str) {\n    std::vector<std::string> names;\n    \n    size_t pos = 0;\n    bool first_param = true;\n    \n    while (pos < params_str.length()) {\n        size_t comma_pos = params_str.find(',', pos);\n        if (comma_pos == std::string::npos) comma_pos = params_str.length();\n        \n        std::string param = params_str.substr(pos, comma_pos - pos);\n        \n        // Skip ReducerContext\n        if (first_param) {\n            first_param = false;\n            pos = comma_pos + 1;\n            continue;\n        }\n        \n        // Trim whitespace\n        size_t start = param.find_first_not_of(\" \\t\");\n        if (start != std::string::npos) {\n            param = param.substr(start);\n            size_t end = param.find_last_not_of(\" \\t\");\n            if (end != std::string::npos) {\n                param = param.substr(0, end + 1);\n            }\n        }\n        \n        // Extract parameter name\n        size_t last_space = param.find_last_of(\" \\t\");\n        if (last_space != std::string::npos) {\n            std::string param_name = param.substr(last_space + 1);\n            size_t name_end = param_name.find_first_of(\"&*[]\");\n            if (name_end != std::string::npos) {\n                param_name = param_name.substr(0, name_end);\n            }\n            names.push_back(std::move(param_name));\n        }\n        \n        pos = comma_pos + 1;\n    }\n    \n    return names;\n}\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_MODULE_IMPL_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/README.md",
    "content": "# SpacetimeDB C++ Module Library Internal API\n\nThis directory contains the internal implementation of the SpacetimeDB C++ Module Library, matching the design of the C# bindings.\n\n## Overview\n\nThe Internal API provides:\n- **Module Definition Management**: Automatic generation of module definitions using autogenerated types\n- **Type Registration**: Type-safe registration system for tables and reducers\n- **FFI Wrappers**: Safe wrappers around the SpacetimeDB ABI\n- **Table Operations**: Type-safe CRUD operations on tables\n- **Reducer System**: Framework for implementing reducers with proper serialization\n\n## Directory Structure\n\n- `autogen/` - Autogenerated types from Rust definitions (RawModuleDefV9, RawTableDefV9, etc.)\n- `Module.h/cpp` - Main module management and registration\n \n- `FFI.h/cpp` - Foreign Function Interface definitions\n\n## Key Components\n\n### 1. Module Registration\n\nThe `Module` class is a singleton that manages:\n- Table registration\n- Reducer registration\n- Type registration\n- Module description generation\n- Reducer invocation\n\n### 2. Autogenerated Types\n\nAll module definition types are generated from the Rust codebase:\n- `RawModuleDefV9` - Main module definition\n- `RawTableDefV9` - Table definitions\n- `RawReducerDefV9` - Reducer definitions\n- `TableAccess`, `TableType`, etc. - Enums and supporting types\n\n### 3. Table Implementation\n\nTables are implemented using the `SPACETIMEDB_TABLE` macro system:\n1. Define row types with field registration using `SPACETIMEDB_MODULE_STRUCT` (modules) or `SPACETIMEDB_STRUCT` (clients)\n2. Register tables with `SPACETIMEDB_TABLE(Type, \"name\", Access, ...constraints)`\n3. Use `ctx.db.table<T>(\"name\")` for table operations (insert, delete)\n4. Tables are automatically registered during module initialization via `__preinit__` functions\n\n### 4. Reducer Implementation\n\nReducers are implemented using unified macro system:\n1. `SPACETIMEDB_REDUCER(name, ReducerContext ctx, ...params)` - User-defined reducers\n2. `SPACETIMEDB_INIT(name, ReducerContext ctx)` - Module initialization\n3. `SPACETIMEDB_CLIENT_CONNECTED(name, ReducerContext ctx)` - Client connection handler\n4. `SPACETIMEDB_CLIENT_DISCONNECTED(name, ReducerContext ctx)` - Client disconnection handler\n5. The macros handle serialization, registration, and ABI integration automatically\n\n## Usage Example\n\n```cpp\n// Define row type with field registration\nstruct Person {\n    uint32_t id;\n    std::string name;\n    uint8_t age;\n};\nSPACETIMEDB_MODULE_STRUCT(Person, id, name, age);\n\n// Define table with constraints\nSPACETIMEDB_TABLE(Person, \"people\", Public,\n    PrimaryKeyAutoInc(id),\n    Index(name)\n);\n\n// Define reducer with ReducerContext\nSPACETIMEDB_REDUCER(add_person, ReducerContext ctx, std::string name, uint8_t age) {\n    // Access table and insert record\n    Person person{0, name, age}; // id will be auto-generated\n    ctx.db.table<Person>(\"people\").insert(person);\n    LOG_INFO(\"Added person: \" + name);\n}\n\n// Lifecycle reducers\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    LOG_INFO(\"Module initialized!\");\n    return Ok();\n}\n\nSPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {\n    LOG_INFO(\"Client connected!\");\n    return Ok();\n}\n```\n\n## Migration from Old API\n\nSee `MIGRATION_GUIDE.md` for detailed steps on migrating from the manual module definition approach to this new Internal API.\n\n## Future Improvements\n\n- Automatic BSATN serialization generation\n- Macro-based table/reducer definitions\n- Query builder API\n- Index management"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/AlgebraicType.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include <variant>\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"../forward_declarations.h\"\n\nnamespace SpacetimeDB::Internal {\n\n// AlgebraicType is special - it uses pointers to break circular dependencies\n// This is a custom implementation due to circular references between:\n// AlgebraicType -> SumType -> SumTypeVariant -> AlgebraicType\n// AlgebraicType -> ProductType -> ProductTypeElement -> AlgebraicType\nclass AlgebraicType {\npublic:\n    enum class Tag : uint8_t {\n        Ref = 0,\n        Sum = 1, \n        Product = 2,\n        Array = 3,\n        String = 4,\n        Bool = 5,\n        I8 = 6,\n        U8 = 7,\n        I16 = 8,\n        U16 = 9,\n        I32 = 10,\n        U32 = 11,\n        I64 = 12,\n        U64 = 13,\n        I128 = 14,\n        U128 = 15,\n        I256 = 16,\n        U256 = 17,\n        F32 = 18,\n        F64 = 19\n    };\n\n    using DataType = std::variant<\n        uint32_t,                                          // Ref\n        std::unique_ptr<SpacetimeDB::Internal::SumType>,   // Sum\n        std::unique_ptr<SpacetimeDB::Internal::ProductType>, // Product \n        std::unique_ptr<SpacetimeDB::Internal::AlgebraicType>, // Array element type\n        std::monostate  // All primitive types (String, Bool, I8, U8, etc.)\n    >;\n\nprivate:\n    Tag tag_;\n    DataType data_;\n\npublic:\n    AlgebraicType();\n    AlgebraicType(Tag primitive_tag);\n    AlgebraicType(const AlgebraicType& other);\n    AlgebraicType& operator=(const AlgebraicType& other);\n    \n    Tag get_tag() const { return tag_; }\n    \n    template<size_t Index>\n    const auto& get() const { return std::get<Index>(data_); }\n\n    template<size_t Index>\n    auto& get() { return std::get<Index>(data_); }\n\n    template<size_t Index, typename T>\n    void set(T&& value);\n\n    template<size_t Index>\n    bool is() const { return tag_ == static_cast<Tag>(Index); }\n\n    template<typename Visitor>\n    auto visit(Visitor&& visitor) const -> decltype(auto) {\n        return std::visit(std::forward<Visitor>(visitor), data_);\n    }\n\n    template<typename Visitor>  \n    auto visit(Visitor&& visitor) -> decltype(auto) {\n        return std::visit(std::forward<Visitor>(visitor), data_);\n    }\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n\n    bool operator==(const AlgebraicType& other) const;\n    bool operator!=(const AlgebraicType& other) const { return !(*this == other); }\n};\n\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/CaseConversionPolicy.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class CaseConversionPolicy : uint8_t {\n    None = 0,\n    SnakeCase = 1,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/ExplicitNameEntry.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"NameMapping.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ExplicitNameEntry_Function_Wrapper) {\n    SpacetimeDB::Internal::NameMapping value;\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, value);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(value)\n};\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ExplicitNameEntry_Index_Wrapper) {\n    SpacetimeDB::Internal::NameMapping value;\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, value);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(value)\n};\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(ExplicitNameEntry, SpacetimeDB::Internal::NameMapping, ExplicitNameEntry_Function_Wrapper, ExplicitNameEntry_Index_Wrapper)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/ExplicitNames.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ExplicitNameEntry.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ExplicitNames) {\n    std::vector<SpacetimeDB::Internal::ExplicitNameEntry> entries;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, entries);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(entries)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/FunctionVisibility.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class FunctionVisibility : uint8_t {\n    Private = 0,\n    ClientCallable = 1,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/IndexType.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class IndexType : uint8_t {\n    BTree = 0,\n    Hash = 1,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/Lifecycle.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class Lifecycle : uint8_t {\n    Init = 0,\n    OnConnect = 1,\n    OnDisconnect = 2,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/MiscModuleExport.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"TypeAlias.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(MiscModuleExport, SpacetimeDB::Internal::TypeAlias)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/NameMapping.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(NameMapping) {\n    std::string source_name;\n    std::string canonical_name;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, canonical_name);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, canonical_name)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/ProductType.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ProductTypeElement.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ProductType) {\n    std::vector<SpacetimeDB::Internal::ProductTypeElement> elements;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, elements);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(elements)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/ProductTypeElement.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ProductTypeElement) {\n    std::optional<std::string> name;\n    SpacetimeDB::Internal::AlgebraicType algebraic_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, algebraic_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, algebraic_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawColumnDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawColumnDefV8) {\n    std::string col_name;\n    SpacetimeDB::Internal::AlgebraicType col_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, col_name);\n        ::SpacetimeDB::bsatn::serialize(writer, col_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(col_name, col_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawColumnDefaultValueV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawColumnDefaultValueV10) {\n    uint16_t col_id;\n    std::vector<uint8_t> value;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, col_id);\n        ::SpacetimeDB::bsatn::serialize(writer, value);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(col_id, value)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawColumnDefaultValueV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawColumnDefaultValueV9) {\n    std::string table;\n    uint16_t col_id;\n    std::vector<uint8_t> value;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, table);\n        ::SpacetimeDB::bsatn::serialize(writer, col_id);\n        ::SpacetimeDB::bsatn::serialize(writer, value);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(table, col_id, value)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDataV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawUniqueConstraintDataV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(RawConstraintDataV9, SpacetimeDB::Internal::RawUniqueConstraintDataV9)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawConstraintDataV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawConstraintDefV10) {\n    std::optional<std::string> source_name;\n    SpacetimeDB::Internal::RawConstraintDataV9 data;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, data);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, data)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawConstraintDefV8) {\n    std::string constraint_name;\n    uint8_t constraints;\n    std::vector<uint16_t> columns;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, constraint_name);\n        ::SpacetimeDB::bsatn::serialize(writer, constraints);\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(constraint_name, constraints, columns)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawConstraintDataV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawConstraintDefV9) {\n    std::optional<std::string> name;\n    SpacetimeDB::Internal::RawConstraintDataV9 data;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, data);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, data)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexAlgorithm.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\n// BTree algorithm data\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmBTreeData) {\n    std::vector<uint16_t> columns;\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(columns)\n};\n\n// Hash algorithm data (not currently used)\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmHashData) {\n    std::vector<uint16_t> columns;\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(columns)\n};\n\n// Direct algorithm data (not currently used)\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmDirectData) {\n    uint16_t column;\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, column);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(column)\n};\n\n// RawIndexAlgorithm tagged enum with data variants\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(RawIndexAlgorithm, \n    SpacetimeDB::Internal::RawIndexAlgorithmBTreeData,\n    SpacetimeDB::Internal::RawIndexAlgorithmHashData,\n    SpacetimeDB::Internal::RawIndexAlgorithmDirectData\n)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawIndexAlgorithm.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexDefV10) {\n    std::optional<std::string> source_name;\n    std::optional<std::string> accessor_name;\n    SpacetimeDB::Internal::RawIndexAlgorithm algorithm;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, accessor_name);\n        ::SpacetimeDB::bsatn::serialize(writer, algorithm);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, accessor_name, algorithm)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"IndexType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexDefV8) {\n    std::string index_name;\n    bool is_unique;\n    SpacetimeDB::Internal::IndexType index_type;\n    std::vector<uint16_t> columns;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, index_name);\n        ::SpacetimeDB::bsatn::serialize(writer, is_unique);\n        ::SpacetimeDB::bsatn::serialize(writer, index_type);\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(index_name, is_unique, index_type, columns)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawIndexAlgorithm.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexDefV9) {\n    std::optional<std::string> name;\n    std::optional<std::string> accessor_name;\n    SpacetimeDB::Internal::RawIndexAlgorithm algorithm;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, accessor_name);\n        ::SpacetimeDB::bsatn::serialize(writer, algorithm);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, accessor_name, algorithm)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawLifeCycleReducerDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"Lifecycle.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawLifeCycleReducerDefV10) {\n    SpacetimeDB::Internal::Lifecycle lifecycle_spec;\n    std::string function_name;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, lifecycle_spec);\n        ::SpacetimeDB::bsatn::serialize(writer, function_name);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(lifecycle_spec, function_name)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawMiscModuleExportV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawViewDefV9.g.h\"\n#include \"RawColumnDefaultValueV9.g.h\"\n#include \"RawProcedureDefV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(RawMiscModuleExportV9, SpacetimeDB::Internal::RawColumnDefaultValueV9, SpacetimeDB::Internal::RawProcedureDefV9, SpacetimeDB::Internal::RawViewDefV9)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDef.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawModuleDefV8.g.h\"\n#include \"RawModuleDefV9.g.h\"\n#include \"RawModuleDefV10.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(RawModuleDef, SpacetimeDB::Internal::RawModuleDefV8, SpacetimeDB::Internal::RawModuleDefV9, SpacetimeDB::Internal::RawModuleDefV10)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawModuleDefV10Section.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawModuleDefV10) {\n    std::vector<SpacetimeDB::Internal::RawModuleDefV10Section> sections;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, sections);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(sections)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDefV10Section.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawViewDefV10.g.h\"\n#include \"CaseConversionPolicy.g.h\"\n#include \"RawScheduleDefV10.g.h\"\n#include \"RawTableDefV10.g.h\"\n#include \"Typespace.g.h\"\n#include \"RawReducerDefV10.g.h\"\n#include \"RawProcedureDefV10.g.h\"\n#include \"RawTypeDefV10.g.h\"\n#include \"RawLifeCycleReducerDefV10.g.h\"\n#include \"RawRowLevelSecurityDefV9.g.h\"\n#include \"ExplicitNames.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_TAGGED_ENUM(RawModuleDefV10Section, SpacetimeDB::Internal::Typespace, std::vector<SpacetimeDB::Internal::RawTypeDefV10>, std::vector<SpacetimeDB::Internal::RawTableDefV10>, std::vector<SpacetimeDB::Internal::RawReducerDefV10>, std::vector<SpacetimeDB::Internal::RawProcedureDefV10>, std::vector<SpacetimeDB::Internal::RawViewDefV10>, std::vector<SpacetimeDB::Internal::RawScheduleDefV10>, std::vector<SpacetimeDB::Internal::RawLifeCycleReducerDefV10>, std::vector<SpacetimeDB::Internal::RawRowLevelSecurityDefV9>, SpacetimeDB::Internal::CaseConversionPolicy, SpacetimeDB::Internal::ExplicitNames)\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ReducerDef.g.h\"\n#include \"MiscModuleExport.g.h\"\n#include \"Typespace.g.h\"\n#include \"TableDesc.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawModuleDefV8) {\n    SpacetimeDB::Internal::Typespace typespace;\n    std::vector<SpacetimeDB::Internal::TableDesc> tables;\n    std::vector<SpacetimeDB::Internal::ReducerDef> reducers;\n    std::vector<SpacetimeDB::Internal::MiscModuleExport> misc_exports;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, typespace);\n        ::SpacetimeDB::bsatn::serialize(writer, tables);\n        ::SpacetimeDB::bsatn::serialize(writer, reducers);\n        ::SpacetimeDB::bsatn::serialize(writer, misc_exports);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(typespace, tables, reducers, misc_exports)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawTableDefV9.g.h\"\n#include \"RawTypeDefV9.g.h\"\n#include \"RawMiscModuleExportV9.g.h\"\n#include \"Typespace.g.h\"\n#include \"RawReducerDefV9.g.h\"\n#include \"RawRowLevelSecurityDefV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawModuleDefV9) {\n    SpacetimeDB::Internal::Typespace typespace;\n    std::vector<SpacetimeDB::Internal::RawTableDefV9> tables;\n    std::vector<SpacetimeDB::Internal::RawReducerDefV9> reducers;\n    std::vector<SpacetimeDB::Internal::RawTypeDefV9> types;\n    std::vector<SpacetimeDB::Internal::RawMiscModuleExportV9> misc_exports;\n    std::vector<SpacetimeDB::Internal::RawRowLevelSecurityDefV9> row_level_security;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, typespace);\n        ::SpacetimeDB::bsatn::serialize(writer, tables);\n        ::SpacetimeDB::bsatn::serialize(writer, reducers);\n        ::SpacetimeDB::bsatn::serialize(writer, types);\n        ::SpacetimeDB::bsatn::serialize(writer, misc_exports);\n        ::SpacetimeDB::bsatn::serialize(writer, row_level_security);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(typespace, tables, reducers, types, misc_exports, row_level_security)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawProcedureDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"FunctionVisibility.g.h\"\n#include \"ProductType.g.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawProcedureDefV10) {\n    std::string source_name;\n    SpacetimeDB::Internal::ProductType params;\n    SpacetimeDB::Internal::AlgebraicType return_type;\n    SpacetimeDB::Internal::FunctionVisibility visibility;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, return_type);\n        ::SpacetimeDB::bsatn::serialize(writer, visibility);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, params, return_type, visibility)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawProcedureDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ProductType.g.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawProcedureDefV9) {\n    std::string name;\n    SpacetimeDB::Internal::ProductType params;\n    SpacetimeDB::Internal::AlgebraicType return_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, return_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, params, return_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawReducerDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n#include \"FunctionVisibility.g.h\"\n#include \"ProductType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawReducerDefV10) {\n    std::string source_name;\n    SpacetimeDB::Internal::ProductType params;\n    SpacetimeDB::Internal::FunctionVisibility visibility;\n    SpacetimeDB::Internal::AlgebraicType ok_return_type;\n    SpacetimeDB::Internal::AlgebraicType err_return_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, visibility);\n        ::SpacetimeDB::bsatn::serialize(writer, ok_return_type);\n        ::SpacetimeDB::bsatn::serialize(writer, err_return_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, params, visibility, ok_return_type, err_return_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawReducerDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"Lifecycle.g.h\"\n#include \"ProductType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawReducerDefV9) {\n    std::string name;\n    SpacetimeDB::Internal::ProductType params;\n    std::optional<SpacetimeDB::Internal::Lifecycle> lifecycle;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, lifecycle);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, params, lifecycle)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawRowLevelSecurityDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawRowLevelSecurityDefV9) {\n    std::string sql;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, sql);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(sql)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScheduleDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawScheduleDefV10) {\n    std::optional<std::string> source_name;\n    std::string table_name;\n    uint16_t schedule_at_col;\n    std::string function_name;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, table_name);\n        ::SpacetimeDB::bsatn::serialize(writer, schedule_at_col);\n        ::SpacetimeDB::bsatn::serialize(writer, function_name);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, table_name, schedule_at_col, function_name)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScheduleDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawScheduleDefV9) {\n    std::optional<std::string> name;\n    std::string reducer_name;\n    uint16_t scheduled_at_column;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, reducer_name);\n        ::SpacetimeDB::bsatn::serialize(writer, scheduled_at_column);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, reducer_name, scheduled_at_column)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScopedTypeNameV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawScopedTypeNameV10) {\n    std::vector<std::string> scope;\n    std::string source_name;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, scope);\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(scope, source_name)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScopedTypeNameV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawScopedTypeNameV9) {\n    std::vector<std::string> scope;\n    std::string name;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, scope);\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(scope, name)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawSequenceDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawSequenceDefV10) {\n    std::optional<std::string> source_name;\n    uint16_t column;\n    std::optional<SpacetimeDB::I128> start;\n    std::optional<SpacetimeDB::I128> min_value;\n    std::optional<SpacetimeDB::I128> max_value;\n    SpacetimeDB::I128 increment;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, column);\n        ::SpacetimeDB::bsatn::serialize(writer, start);\n        ::SpacetimeDB::bsatn::serialize(writer, min_value);\n        ::SpacetimeDB::bsatn::serialize(writer, max_value);\n        ::SpacetimeDB::bsatn::serialize(writer, increment);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, column, start, min_value, max_value, increment)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawSequenceDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawSequenceDefV8) {\n    std::string sequence_name;\n    uint16_t col_pos;\n    SpacetimeDB::I128 increment;\n    std::optional<SpacetimeDB::I128> start;\n    std::optional<SpacetimeDB::I128> min_value;\n    std::optional<SpacetimeDB::I128> max_value;\n    SpacetimeDB::I128 allocated;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, sequence_name);\n        ::SpacetimeDB::bsatn::serialize(writer, col_pos);\n        ::SpacetimeDB::bsatn::serialize(writer, increment);\n        ::SpacetimeDB::bsatn::serialize(writer, start);\n        ::SpacetimeDB::bsatn::serialize(writer, min_value);\n        ::SpacetimeDB::bsatn::serialize(writer, max_value);\n        ::SpacetimeDB::bsatn::serialize(writer, allocated);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(sequence_name, col_pos, increment, start, min_value, max_value, allocated)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawSequenceDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawSequenceDefV9) {\n    std::optional<std::string> name;\n    uint16_t column;\n    std::optional<SpacetimeDB::I128> start;\n    std::optional<SpacetimeDB::I128> min_value;\n    std::optional<SpacetimeDB::I128> max_value;\n    SpacetimeDB::I128 increment;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, column);\n        ::SpacetimeDB::bsatn::serialize(writer, start);\n        ::SpacetimeDB::bsatn::serialize(writer, min_value);\n        ::SpacetimeDB::bsatn::serialize(writer, max_value);\n        ::SpacetimeDB::bsatn::serialize(writer, increment);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, column, start, min_value, max_value, increment)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTableDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawIndexDefV10.g.h\"\n#include \"TableType.g.h\"\n#include \"TableAccess.g.h\"\n#include \"RawColumnDefaultValueV10.g.h\"\n#include \"RawConstraintDefV10.g.h\"\n#include \"RawSequenceDefV10.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawTableDefV10) {\n    std::string source_name;\n    uint32_t product_type_ref;\n    std::vector<uint16_t> primary_key;\n    std::vector<SpacetimeDB::Internal::RawIndexDefV10> indexes;\n    std::vector<SpacetimeDB::Internal::RawConstraintDefV10> constraints;\n    std::vector<SpacetimeDB::Internal::RawSequenceDefV10> sequences;\n    SpacetimeDB::Internal::TableType table_type;\n    SpacetimeDB::Internal::TableAccess table_access;\n    std::vector<SpacetimeDB::Internal::RawColumnDefaultValueV10> default_values;\n    bool is_event;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, product_type_ref);\n        ::SpacetimeDB::bsatn::serialize(writer, primary_key);\n        ::SpacetimeDB::bsatn::serialize(writer, indexes);\n        ::SpacetimeDB::bsatn::serialize(writer, constraints);\n        ::SpacetimeDB::bsatn::serialize(writer, sequences);\n        ::SpacetimeDB::bsatn::serialize(writer, table_type);\n        ::SpacetimeDB::bsatn::serialize(writer, table_access);\n        ::SpacetimeDB::bsatn::serialize(writer, default_values);\n        ::SpacetimeDB::bsatn::serialize(writer, is_event);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, product_type_ref, primary_key, indexes, constraints, sequences, table_type, table_access, default_values, is_event)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTableDefV8.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawSequenceDefV8.g.h\"\n#include \"RawColumnDefV8.g.h\"\n#include \"RawConstraintDefV8.g.h\"\n#include \"RawIndexDefV8.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawTableDefV8) {\n    std::string table_name;\n    std::vector<SpacetimeDB::Internal::RawColumnDefV8> columns;\n    std::vector<SpacetimeDB::Internal::RawIndexDefV8> indexes;\n    std::vector<SpacetimeDB::Internal::RawConstraintDefV8> constraints;\n    std::vector<SpacetimeDB::Internal::RawSequenceDefV8> sequences;\n    std::string table_type;\n    std::string table_access;\n    std::optional<std::string> scheduled;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, table_name);\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n        ::SpacetimeDB::bsatn::serialize(writer, indexes);\n        ::SpacetimeDB::bsatn::serialize(writer, constraints);\n        ::SpacetimeDB::bsatn::serialize(writer, sequences);\n        ::SpacetimeDB::bsatn::serialize(writer, table_type);\n        ::SpacetimeDB::bsatn::serialize(writer, table_access);\n        ::SpacetimeDB::bsatn::serialize(writer, scheduled);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(table_name, columns, indexes, constraints, sequences, table_type, table_access, scheduled)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTableDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawScheduleDefV9.g.h\"\n#include \"RawSequenceDefV9.g.h\"\n#include \"TableType.g.h\"\n#include \"TableAccess.g.h\"\n#include \"RawConstraintDefV9.g.h\"\n#include \"RawIndexDefV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawTableDefV9) {\n    std::string name;\n    uint32_t product_type_ref;\n    std::vector<uint16_t> primary_key;\n    std::vector<SpacetimeDB::Internal::RawIndexDefV9> indexes;\n    std::vector<SpacetimeDB::Internal::RawConstraintDefV9> constraints;\n    std::vector<SpacetimeDB::Internal::RawSequenceDefV9> sequences;\n    std::optional<SpacetimeDB::Internal::RawScheduleDefV9> schedule;\n    SpacetimeDB::Internal::TableType table_type;\n    SpacetimeDB::Internal::TableAccess table_access;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, product_type_ref);\n        ::SpacetimeDB::bsatn::serialize(writer, primary_key);\n        ::SpacetimeDB::bsatn::serialize(writer, indexes);\n        ::SpacetimeDB::bsatn::serialize(writer, constraints);\n        ::SpacetimeDB::bsatn::serialize(writer, sequences);\n        ::SpacetimeDB::bsatn::serialize(writer, schedule);\n        ::SpacetimeDB::bsatn::serialize(writer, table_type);\n        ::SpacetimeDB::bsatn::serialize(writer, table_access);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, product_type_ref, primary_key, indexes, constraints, sequences, schedule, table_type, table_access)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTypeDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawScopedTypeNameV10.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawTypeDefV10) {\n    SpacetimeDB::Internal::RawScopedTypeNameV10 source_name;\n    uint32_t ty;\n    bool custom_ordering;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, ty);\n        ::SpacetimeDB::bsatn::serialize(writer, custom_ordering);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, ty, custom_ordering)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTypeDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawScopedTypeNameV9.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawTypeDefV9) {\n    SpacetimeDB::Internal::RawScopedTypeNameV9 name;\n    uint32_t ty;\n    bool custom_ordering;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, ty);\n        ::SpacetimeDB::bsatn::serialize(writer, custom_ordering);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, ty, custom_ordering)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawUniqueConstraintDataV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawUniqueConstraintDataV9) {\n    std::vector<uint16_t> columns;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, columns);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(columns)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawViewDefV10.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n#include \"ProductType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawViewDefV10) {\n    std::string source_name;\n    uint32_t index;\n    bool is_public;\n    bool is_anonymous;\n    SpacetimeDB::Internal::ProductType params;\n    SpacetimeDB::Internal::AlgebraicType return_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, source_name);\n        ::SpacetimeDB::bsatn::serialize(writer, index);\n        ::SpacetimeDB::bsatn::serialize(writer, is_public);\n        ::SpacetimeDB::bsatn::serialize(writer, is_anonymous);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, return_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(source_name, index, is_public, is_anonymous, params, return_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/RawViewDefV9.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ProductType.g.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawViewDefV9) {\n    std::string name;\n    uint32_t index;\n    bool is_public;\n    bool is_anonymous;\n    SpacetimeDB::Internal::ProductType params;\n    SpacetimeDB::Internal::AlgebraicType return_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, index);\n        ::SpacetimeDB::bsatn::serialize(writer, is_public);\n        ::SpacetimeDB::bsatn::serialize(writer, is_anonymous);\n        ::SpacetimeDB::bsatn::serialize(writer, params);\n        ::SpacetimeDB::bsatn::serialize(writer, return_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, index, is_public, is_anonymous, params, return_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/ReducerDef.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"ProductTypeElement.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(ReducerDef) {\n    std::string name;\n    std::vector<SpacetimeDB::Internal::ProductTypeElement> args;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, args);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, args)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/SumType.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"SumTypeVariant.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(SumType) {\n    std::vector<SpacetimeDB::Internal::SumTypeVariant> variants;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, variants);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(variants)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/SumTypeVariant.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(SumTypeVariant) {\n    std::optional<std::string> name;\n    SpacetimeDB::Internal::AlgebraicType algebraic_type;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, algebraic_type);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, algebraic_type)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/TableAccess.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class TableAccess : uint8_t {\n    Public = 0,\n    Private = 1,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/TableDesc.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"RawTableDefV8.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(TableDesc) {\n    SpacetimeDB::Internal::RawTableDefV8 schema;\n    uint32_t data;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, schema);\n        ::SpacetimeDB::bsatn::serialize(writer, data);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(schema, data)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/TableType.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nenum class TableType : uint8_t {\n    System = 0,\n    User = 1,\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/TypeAlias.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(TypeAlias) {\n    std::string name;\n    uint32_t ty;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, name);\n        ::SpacetimeDB::bsatn::serialize(writer, ty);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(name, ty)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen/Typespace.g.h",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include \"../autogen_base.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"AlgebraicType.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\nSPACETIMEDB_INTERNAL_PRODUCT_TYPE(Typespace) {\n    std::vector<SpacetimeDB::Internal::AlgebraicType> types;\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n        ::SpacetimeDB::bsatn::serialize(writer, types);\n    }\n    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(types)\n};\n} // namespace SpacetimeDB::Internal\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/autogen_base.h",
    "content": "#pragma once\n\n// Autogenerated Type Support\n// This file provides base classes and macros exclusively for the autogenerated\n// internal types used in the V9 module definition system.\n\n#include <variant>\n#include <cstdint>\n#include <tuple>\n#include \"../bsatn/bsatn.h\"\n\nnamespace SpacetimeDB::Internal {\n\n// =============================================================================\n// TAGGED ENUM BASE CLASS (for autogenerated sum types)\n// =============================================================================\n\ntemplate<typename... Variants>\nclass TaggedEnumBase {\n    static_assert(sizeof...(Variants) > 0, \"TaggedEnum must have at least one variant\");\n\nprotected:\n    std::variant<Variants...> data_;\n\npublic:\n    // Default constructor - initializes to first variant\n    TaggedEnumBase() : data_{} {}\n\n    // Get current tag (variant index)\n    uint8_t get_tag() const noexcept {\n        return static_cast<uint8_t>(data_.index());\n    }\n\n    // Get variant by index (const)\n    template<size_t Index>\n    const auto& get() const {\n        return std::get<Index>(data_);\n    }\n\n    // Get variant by index (mutable)  \n    template<size_t Index>\n    auto& get() {\n        return std::get<Index>(data_);\n    }\n\n    // Set variant by index\n    template<size_t Index, typename T>\n    void set(T&& value) {\n        data_.template emplace<Index>(std::forward<T>(value));\n    }\n\n    // Check if currently holds specific index\n    template<size_t Index>\n    bool is() const noexcept {\n        return data_.index() == Index;\n    }\n\n    // Visit pattern - used for serialization\n    template<typename Visitor>\n    auto visit(Visitor&& visitor) const {\n        return std::visit(std::forward<Visitor>(visitor), data_);\n    }\n\n    // Equality\n    bool operator==(const TaggedEnumBase& other) const noexcept {\n        return data_ == other.data_;\n    }\n\n    bool operator!=(const TaggedEnumBase& other) const noexcept {\n        return !(*this == other);\n    }\n};\n\n} // namespace SpacetimeDB::Internal\n\n// =============================================================================\n// MACROS FOR AUTOGENERATED INTERNAL TYPES\n// =============================================================================\n\n// Macro for internal tagged enums (sum types)\n// Generates a class that inherits from TaggedEnumBase and adds BSATN serialization\n#define SPACETIMEDB_INTERNAL_TAGGED_ENUM(TypeName, ...) \\\n    class TypeName : public ::SpacetimeDB::Internal::TaggedEnumBase<__VA_ARGS__> { \\\n    private: \\\n        using Base = ::SpacetimeDB::Internal::TaggedEnumBase<__VA_ARGS__>; \\\n    public: \\\n        using Base::Base; \\\n        using Base::get_tag; \\\n        using Base::get; \\\n        using Base::set; \\\n        using Base::is; \\\n        using Base::visit; \\\n        using Base::operator==; \\\n        using Base::operator!=; \\\n        \\\n        void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const { \\\n            /* Write tag (variant index) */ \\\n            writer.write_u8(this->get_tag()); \\\n            /* Write variant data based on current tag */ \\\n            this->visit([&writer](const auto& variant) { \\\n                using VariantType = std::decay_t<decltype(variant)>; \\\n                if constexpr (!std::is_same_v<VariantType, std::monostate>) { \\\n                    /* Non-unit variant - serialize the data */ \\\n                    ::SpacetimeDB::bsatn::serialize(writer, variant); \\\n                } \\\n                /* Unit variant - write nothing */ \\\n            }); \\\n        } \\\n    };\n\n// Macro for internal product types (structs)\n// Just creates a simple struct - no base class needed\n#define SPACETIMEDB_INTERNAL_PRODUCT_TYPE(TypeName) \\\n    struct TypeName\n\n// Public aliases used by generated non-internal code paths.\n#ifndef SPACETIMEDB_PRODUCT_TYPE\n#define SPACETIMEDB_PRODUCT_TYPE(TypeName) SPACETIMEDB_INTERNAL_PRODUCT_TYPE(TypeName)\n#endif\n\n#ifndef SPACETIMEDB_TAGGED_ENUM\n#define SPACETIMEDB_TAGGED_ENUM(TypeName, ...) SPACETIMEDB_INTERNAL_TAGGED_ENUM(TypeName, __VA_ARGS__)\n#endif\n\n// Macro for product type equality - generates == and != operators\n// This generates a simple equality comparison using std::tie\n#define SPACETIMEDB_PRODUCT_TYPE_EQUALITY(...) \\\n    bool operator==(const auto& other) const noexcept { \\\n        return std::tie(__VA_ARGS__) == std::tie(other.__VA_ARGS__); \\\n    } \\\n    bool operator!=(const auto& other) const noexcept { \\\n        return !(*this == other); \\\n    }\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/bsatn_adapters.h",
    "content": "#pragma once\n\n#include <spacetimedb/bsatn/writer.h>\n#include <spacetimedb/bsatn/reader.h>\n#include <spacetimedb/abi/FFI.h>\n#include <vector>\n#include <cstdint>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n/**\n * Adapter class that allows using BSATN Reader with BytesSource\n * \n * This is needed because FFI uses BytesSource handles while our\n * BSATN system uses Reader objects. This adapter bridges the gap.\n */\nclass BytesSourceReader {\nprivate:\n    BytesSource source_;\n    \npublic:\n    explicit BytesSourceReader(BytesSource source) : source_(source) {}\n    \n    void read_bytes(uint8_t* buffer, size_t len) {\n        size_t bytes_read = len;\n        FFI::bytes_source_read(source_, buffer, &bytes_read);\n        if (bytes_read != len) {\n            std::abort(); // Failed to read expected number of bytes\n        }\n    }\n    \n    uint8_t read_u8() {\n        uint8_t value;\n        read_bytes(&value, 1);\n        return value;\n    }\n    \n    uint16_t read_u16_le() {\n        uint8_t bytes[2];\n        read_bytes(bytes, 2);\n        return bytes[0] | (static_cast<uint16_t>(bytes[1]) << 8);\n    }\n    \n    uint32_t read_u32_le() {\n        uint8_t bytes[4];\n        read_bytes(bytes, 4);\n        return bytes[0] | \n               (static_cast<uint32_t>(bytes[1]) << 8) |\n               (static_cast<uint32_t>(bytes[2]) << 16) |\n               (static_cast<uint32_t>(bytes[3]) << 24);\n    }\n    \n    uint64_t read_u64_le() {\n        uint8_t bytes[8];\n        read_bytes(bytes, 8);\n        uint64_t result = 0;\n        for (int i = 0; i < 8; ++i) {\n            result |= static_cast<uint64_t>(bytes[i]) << (i * 8);\n        }\n        return result;\n    }\n    \n    int8_t read_i8() { return static_cast<int8_t>(read_u8()); }\n    int16_t read_i16_le() { return static_cast<int16_t>(read_u16_le()); }\n    int32_t read_i32_le() { return static_cast<int32_t>(read_u32_le()); }\n    int64_t read_i64_le() { return static_cast<int64_t>(read_u64_le()); }\n    \n    float read_f32_le() {\n        uint32_t bits = read_u32_le();\n        float value;\n        std::memcpy(&value, &bits, sizeof(float));\n        return value;\n    }\n    \n    double read_f64_le() {\n        uint64_t bits = read_u64_le();\n        double value;\n        std::memcpy(&value, &bits, sizeof(double));\n        return value;\n    }\n    \n    std::string read_string() {\n        uint32_t len = read_u32_le();\n        std::string result(len, '\\0');\n        read_bytes(reinterpret_cast<uint8_t*>(&result[0]), len);\n        return result;\n    }\n    \n    std::vector<uint8_t> read_fixed_bytes(size_t len) {\n        std::vector<uint8_t> result(len);\n        read_bytes(result.data(), len);\n        return result;\n    }\n    \n    // For compatibility with BSATN Reader interface\n    bool read_bool() { return read_u8() != 0; }\n    \n    // Big integer support\n    ::SpacetimeDB::u128 read_u128_le() {\n        uint64_t low = read_u64_le();\n        uint64_t high = read_u64_le();\n        return ::SpacetimeDB::u128(high, low);\n    }\n    \n    ::SpacetimeDB::i128 read_i128_le() {\n        uint64_t low = read_u64_le();\n        uint64_t high = read_u64_le();\n        return ::SpacetimeDB::i128(static_cast<int64_t>(high), low);\n    }\n    \n    ::SpacetimeDB::u256 read_u256_le() {\n        ::SpacetimeDB::u256 result;\n        read_bytes(result.data.data(), 32);\n        return result;\n    }\n    \n    ::SpacetimeDB::i256 read_i256_le() {\n        ::SpacetimeDB::i256 result;\n        read_bytes(result.data.data(), 32);\n        return result;\n    }\n};\n\n} // namespace Internal\n} // namespace SpacetimeDB"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/buffer_pool.h",
    "content": "#pragma once\n\n#include <vector>\n#include <cstdint>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n/**\n * @brief Thread-local buffer pool for reducing allocations\n * \n * This implements the same strategy as Rust's IterBuf:\n * - Maintains a pool of reusable buffers\n * - Buffers are returned to the pool when no longer needed\n * - After warmup, typical operations have zero allocations\n * \n * The pool is thread-local (though WASM is single-threaded anyway).\n * Default buffer size is 64 KiB, matching Rust's DEFAULT_BUFFER_CAPACITY.\n */\n\n// Match Rust: ROW_ITER_CHUNK_SIZE * 2 = 32*1024 * 2 = 64 KiB\nconstexpr size_t DEFAULT_BUFFER_CAPACITY = 64 * 1024;\n\n/**\n * @brief Get the thread-local buffer pool\n * \n * Implemented as a function with a static local to ensure\n * initialization before first use.\n */\ninline std::vector<std::vector<uint8_t>>& get_buffer_pool() {\n    thread_local std::vector<std::vector<uint8_t>> pool;\n    return pool;\n}\n\n/**\n * @brief RAII wrapper for pooled buffers\n * \n * Usage:\n * ```cpp\n * // Temporary buffer - returns to pool on destruction\n * {\n *     IterBuf buf = IterBuf::take();\n *     buf.reserve(1024);\n *     // ... use buffer ...\n * }  // Buffer automatically returned to pool\n * \n * // Transfer ownership - buffer NOT returned to pool\n * std::vector<uint8_t> owned = IterBuf::take().release();\n * ```\n */\nclass IterBuf {\n    std::vector<uint8_t> buffer_;\n    bool released_;  // Track if ownership was transferred\n    \npublic:\n    /**\n     * @brief Take a buffer from the pool, or allocate a new one\n     * \n     * After warmup, this will typically reuse a pooled buffer\n     * with 64 KiB pre-allocated capacity.\n     */\n    static IterBuf take() {\n        auto& pool = get_buffer_pool();\n        if (!pool.empty()) {\n            auto buf = std::move(pool.back());\n            pool.pop_back();\n            return IterBuf(std::move(buf));\n        }\n        \n        // First time or pool exhausted - allocate new buffer\n        std::vector<uint8_t> buf;\n        buf.reserve(DEFAULT_BUFFER_CAPACITY);\n        return IterBuf(std::move(buf));\n    }\n    \n    /**\n     * @brief Destructor - return buffer to pool if not released\n     */\n    ~IterBuf() {\n        if (!released_) {\n            buffer_.clear();  // Clear contents but keep capacity\n            get_buffer_pool().push_back(std::move(buffer_));\n        }\n    }\n    \n    // No copy - only move\n    IterBuf(const IterBuf&) = delete;\n    IterBuf& operator=(const IterBuf&) = delete;\n    \n    // Move constructor and assignment\n    IterBuf(IterBuf&& other) noexcept \n        : buffer_(std::move(other.buffer_))\n        , released_(other.released_) {\n        other.released_ = true;  // Prevent other from returning to pool\n    }\n    \n    IterBuf& operator=(IterBuf&& other) noexcept {\n        if (this != &other) {\n            // Return our current buffer to pool if we have one\n            if (!released_) {\n                buffer_.clear();\n                get_buffer_pool().push_back(std::move(buffer_));\n            }\n            \n            buffer_ = std::move(other.buffer_);\n            released_ = other.released_;\n            other.released_ = true;\n        }\n        return *this;\n    }\n    \n    /**\n     * @brief Get mutable reference to the buffer\n     * \n     * Use this for operations that need to modify the buffer\n     * while keeping it in the pool-managed scope.\n     */\n    std::vector<uint8_t>& get() {\n        return buffer_;\n    }\n    \n    /**\n     * @brief Get const reference to the buffer\n     */\n    const std::vector<uint8_t>& get() const {\n        return buffer_;\n    }\n    \n    /**\n     * @brief Release ownership of the buffer\n     * \n     * The buffer will NOT be returned to the pool when this IterBuf\n     * is destroyed. Use this when you need to transfer ownership.\n     */\n    std::vector<uint8_t> release() {\n        released_ = true;\n        return std::move(buffer_);\n    }\n    \n    // Convenience methods for common operations\n    \n    void clear() {\n        buffer_.clear();\n    }\n    \n    void reserve(size_t capacity) {\n        buffer_.reserve(capacity);\n    }\n    \n    size_t size() const {\n        return buffer_.size();\n    }\n    \n    size_t capacity() const {\n        return buffer_.capacity();\n    }\n    \n    void resize(size_t size) {\n        buffer_.resize(size);\n    }\n    \n    uint8_t* data() {\n        return buffer_.data();\n    }\n    \n    const uint8_t* data() const {\n        return buffer_.data();\n    }\n    \n    // Iterator support\n    auto begin() { return buffer_.begin(); }\n    auto end() { return buffer_.end(); }\n    auto begin() const { return buffer_.begin(); }\n    auto end() const { return buffer_.end(); }\n    \nprivate:\n    explicit IterBuf(std::vector<uint8_t>&& buf) \n        : buffer_(std::move(buf))\n        , released_(false) {\n    }\n};\n\n} // namespace Internal\n} // namespace SpacetimeDB\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/debug.h",
    "content": "#pragma once\n\n#include <cstdio>\n\n/**\n * @file debug.h\n * @brief Conditional debugging macros for SpacetimeDB C++ bindings\n * \n * This header provides conditional debugging output that can be enabled/disabled\n * at compile time to reduce runtime overhead and output clutter.\n */\n\n// Control debug output with compile-time flag\n#ifdef SPACETIMEDB_DEBUG\n    #define STDB_DEBUG_ENABLED 1\n#else\n    #define STDB_DEBUG_ENABLED 0\n#endif\n\n/**\n * @brief Main debug macro - outputs to stderr with [STDB] prefix\n * \n * Usage: STDB_DEBUG(\"Type %s registered with index %u\", type_name.c_str(), index);\n * \n * When SPACETIMEDB_DEBUG is not defined, this compiles to nothing (zero overhead).\n */\n#if STDB_DEBUG_ENABLED\n    #define STDB_DEBUG(fmt, ...) \\\n        fprintf(stderr, \"[STDB] \" fmt \"\\n\", ##__VA_ARGS__)\n#else\n    #define STDB_DEBUG(fmt, ...) ((void)0)\n#endif\n\n/**\n * @brief Verbose debug macro for detailed tracing\n * \n * Usage: STDB_VERBOSE(\"Processing field %zu: %s\", i, field_name);\n * \n * Even more detailed than STDB_DEBUG, only enabled with SPACETIMEDB_VERBOSE.\n */\n#ifdef SPACETIMEDB_VERBOSE\n    #define STDB_VERBOSE(fmt, ...) \\\n        fprintf(stderr, \"[STDB:VERBOSE] \" fmt \"\\n\", ##__VA_ARGS__)\n#else\n    #define STDB_VERBOSE(fmt, ...) ((void)0)\n#endif\n\n/**\n * @brief Error output (always enabled)\n * \n * Usage: STDB_ERROR(\"Failed to register type: %s\", error_msg);\n * \n * Always outputs regardless of debug flags.\n */\n#define STDB_ERROR(fmt, ...) \\\n    fprintf(stderr, \"[STDB:ERROR] \" fmt \"\\n\", ##__VA_ARGS__)\n\n/**\n * @brief Warning output (always enabled)\n * \n * Usage: STDB_WARN(\"Type %s already registered\", type_name);\n * \n * Always outputs regardless of debug flags.\n */\n#define STDB_WARN(fmt, ...) \\\n    fprintf(stderr, \"[STDB:WARN] \" fmt \"\\n\", ##__VA_ARGS__)\n\n/**\n * @brief Conditional debug macro for specific subsystems\n * \n * Usage: STDB_DEBUG_TYPE(\"Registered enum %s\", enum_name);\n * \n * Can be independently controlled with SPACETIMEDB_DEBUG_TYPE.\n */\n#ifdef SPACETIMEDB_DEBUG_TYPE\n    #define STDB_DEBUG_TYPE(fmt, ...) \\\n        fprintf(stderr, \"[STDB:TYPE] \" fmt \"\\n\", ##__VA_ARGS__)\n#else\n    #define STDB_DEBUG_TYPE(fmt, ...) ((void)0)\n#endif\n\n// Usage example in code:\n// #define SPACETIMEDB_DEBUG  // Enable general debugging\n// #include \"debug.h\"\n// \n// void registerType() {\n//     STDB_DEBUG(\"Starting type registration\");\n//     STDB_VERBOSE(\"Detailed step-by-step trace\");\n//     STDB_ERROR(\"Something went wrong\");\n//     STDB_WARN(\"Non-fatal issue\");\n// }"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/field_registration.h",
    "content": "#ifndef SPACETIMEDB_FIELD_REGISTRATION_H\n#define SPACETIMEDB_FIELD_REGISTRATION_H\n\n#include <cstdint>\n#include <cstddef>\n#include <string>\n#include <vector>\n#include <optional>\n#include <variant>\n#include <type_traits>\n#include \"../macros.h\"\n#include <functional>\n#include <map>\n#include <typeinfo>\n#include \"spacetimedb/bsatn/types.h\"\n#include \"spacetimedb/bsatn/timestamp.h\"\n#include \"spacetimedb/bsatn/algebraic_type.h\"\n#include \"spacetimedb/bsatn/writer.h\"\n#include \"spacetimedb/bsatn/traits.h\"\n\nnamespace SpacetimeDB {\n\n// Helper templates for type detection\ntemplate<typename T>\nstruct is_vector : std::false_type {};\ntemplate<typename T>\nstruct is_vector<std::vector<T>> : std::true_type {};\n\ntemplate<typename T>\nstruct is_optional : std::false_type {};\ntemplate<typename T>\nstruct is_optional<std::optional<T>> : std::true_type {};\n\n// =============================================================================\n// Field Registration System\n// =============================================================================\n// \n// This system provides runtime metadata about struct fields for SpacetimeDB\n// table registration. It bridges C++ compile-time type information to \n// SpacetimeDB's runtime type system.\n//\n// Primary use cases:\n// - Table schema generation from C++ structs\n// - Field constraint application\n// - Type validation\n// - Cross-language compatibility\n//\n// Note: For most use cases, prefer SPACETIMEDB_STRUCT from traits.h\n// which provides both serialization and field registration in one macro.\n// =============================================================================\n\n// -----------------------------------------------------------------------------\n// Type System Mapping\n// -----------------------------------------------------------------------------\n\n// Type traits for BSATN type mapping - simplified to use AlgebraicTypeTag directly\ntemplate<typename T> \nstruct bsatn_type_id { \n    static constexpr bool is_primitive = false;\n    static constexpr uint8_t value = 0; \n};\n\n// Primitive type specializations\ntemplate<> struct bsatn_type_id<bool> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Bool); \n};\ntemplate<> struct bsatn_type_id<uint8_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U8); \n};\ntemplate<> struct bsatn_type_id<uint16_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U16); \n};\ntemplate<> struct bsatn_type_id<uint32_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U32); \n};\ntemplate<> struct bsatn_type_id<uint64_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U64); \n};\ntemplate<> struct bsatn_type_id<int8_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I8); \n};\ntemplate<> struct bsatn_type_id<int16_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I16); \n};\ntemplate<> struct bsatn_type_id<int32_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I32); \n};\ntemplate<> struct bsatn_type_id<int64_t> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I64); \n};\ntemplate<> struct bsatn_type_id<float> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::F32); \n};\ntemplate<> struct bsatn_type_id<double> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::F64); \n};\ntemplate<> struct bsatn_type_id<std::string> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::String); \n};\n\n// SpacetimeDB special types\ntemplate<> struct bsatn_type_id<Identity> { \n    static constexpr bool is_primitive = false;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Product); \n};\ntemplate<> struct bsatn_type_id<ConnectionId> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U64); \n};\ntemplate<> struct bsatn_type_id<Timestamp> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U64); \n};\ntemplate<> struct bsatn_type_id<SpacetimeDB::u128> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U128); \n};\ntemplate<> struct bsatn_type_id<SpacetimeDB::i128> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I128); \n};\ntemplate<> struct bsatn_type_id<SpacetimeDB::u256> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::U256); \n};\ntemplate<> struct bsatn_type_id<SpacetimeDB::i256> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::I256); \n};\ntemplate<> struct bsatn_type_id<ScheduleAt> { \n    static constexpr bool is_primitive = false;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Sum); \n};\n\n// Container types\ntemplate<typename T> \nstruct bsatn_type_id<std::vector<T>> { \n    static constexpr bool is_primitive = false;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Array); \n};\n\ntemplate<typename T> \nstruct bsatn_type_id<std::optional<T>> { \n    static constexpr bool is_primitive = false;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Sum); \n};\n\n// Special case for byte arrays\ntemplate<> struct bsatn_type_id<std::vector<uint8_t>> { \n    static constexpr bool is_primitive = true;\n    static constexpr uint8_t value = static_cast<uint8_t>(bsatn::AlgebraicTypeTag::Array); \n};\n\n// -----------------------------------------------------------------------------\n// Field Descriptor System\n// -----------------------------------------------------------------------------\n\n// Field descriptor for runtime reflection\nstruct FieldDescriptor {\n    std::string name;\n    size_t offset;\n    size_t size;\n    std::function<void(std::vector<uint8_t>&)> write_type;      // Writes AlgebraicType (legacy)\n    std::function<bsatn::AlgebraicType()> get_algebraic_type;   // Returns AlgebraicType for type registry\n    std::function<void(std::vector<uint8_t>&, const void*)> serialize;  // Serializes value\n    std::function<std::string()> get_type_name;  // Returns the type name for complex types\n};\n\n// Table descriptor\nstruct TableDescriptor {\n    std::vector<FieldDescriptor> fields;\n};\n\n// Global registry for table descriptors\ninline std::map<const std::type_info*, TableDescriptor>& get_table_descriptors() {\n    static std::map<const std::type_info*, TableDescriptor> descriptors;\n    return descriptors;\n}\n\n// -----------------------------------------------------------------------------\n// Type Writing Utilities\n// -----------------------------------------------------------------------------\n\n// Forward declaration\ntemplate<typename T>\nvoid write_field_type(std::vector<uint8_t>& buf);\n\n// Utility functions for writing BSATN data\ninline void write_u32(std::vector<uint8_t>& buf, uint32_t val) {\n    buf.push_back(val & 0xFF);\n    buf.push_back((val >> 8) & 0xFF);\n    buf.push_back((val >> 16) & 0xFF);\n    buf.push_back((val >> 24) & 0xFF);\n}\n\ninline void write_string(std::vector<uint8_t>& buf, const std::string& str) {\n    write_u32(buf, str.length());\n    buf.insert(buf.end(), str.begin(), str.end());\n}\n\n// Unified type writer using modern C++ features\ntemplate<typename T>\nvoid write_field_type(std::vector<uint8_t>& buf) {\n    using Tag = bsatn::AlgebraicTypeTag;\n    \n    if constexpr (std::is_enum_v<T>) {\n        // Enums are serialized as u32\n        buf.push_back(static_cast<uint8_t>(Tag::U32));\n    } \n    else if constexpr (bsatn_type_id<T>::is_primitive) {\n        // Primitive types\n        buf.push_back(bsatn_type_id<T>::value);\n    }\n    else if constexpr (is_vector<T>::value) {\n        // Vector types\n        buf.push_back(static_cast<uint8_t>(Tag::Array));\n        write_field_type<typename T::value_type>(buf);\n    }\n    else if constexpr (is_optional<T>::value) {\n        // Optional types\n        buf.push_back(static_cast<uint8_t>(Tag::Sum));\n        write_u32(buf, 2);  // 2 variants\n        \n        // Variant 0: Some\n        buf.push_back(0);  // Some tag\n        write_string(buf, \"some\");\n        write_field_type<typename T::value_type>(buf);\n        \n        // Variant 1: None\n        buf.push_back(0);  // Some tag\n        write_string(buf, \"none\");\n        buf.push_back(static_cast<uint8_t>(Tag::Product));  // Unit type\n        write_u32(buf, 0);  // 0 fields\n    }\n    else if constexpr (std::is_same_v<T, Identity>) {\n        // Identity is array of 32 bytes\n        buf.push_back(static_cast<uint8_t>(Tag::Array));\n        buf.push_back(static_cast<uint8_t>(Tag::U8));\n    }\n    else {\n        // Custom struct types\n        auto& descriptors = get_table_descriptors();\n        auto it = descriptors.find(&typeid(T));\n        \n        if (it != descriptors.end()) {\n            // Write as Product type\n            buf.push_back(static_cast<uint8_t>(Tag::Product));\n            write_u32(buf, it->second.fields.size());\n            \n            for (const auto& field : it->second.fields) {\n                buf.push_back(0);  // Some (field name present)\n                write_string(buf, field.name);\n                field.write_type(buf);\n            }\n        } else {\n            // Fallback - empty product\n            buf.push_back(static_cast<uint8_t>(Tag::Product));\n            write_u32(buf, 0);\n        }\n    }\n}\n\n// Universal serialize_value function using BSATN\ntemplate<typename T>\nvoid serialize_value(std::vector<uint8_t>& buf, const T& val) {\n    bsatn::Writer writer;\n    bsatn::serialize(writer, val);\n    const auto& serialized = writer.get_buffer();\n    buf.insert(buf.end(), serialized.begin(), serialized.end());\n}\n\n// -----------------------------------------------------------------------------\n// Helper Functions\n// -----------------------------------------------------------------------------\n\n// Forward declaration for recursive BSATN size calculation\ntemplate<typename T>\nconstexpr size_t calculate_bsatn_size();\n\n// Helper function to get correct BSATN serialization size (not C++ sizeof with padding)\ntemplate<typename T>\nconstexpr size_t get_field_size() {\n    // CRITICAL: Unit types should have size = 0, not sizeof() = 1\n    if constexpr (requires { T::__is_unit_type__; } && T::__is_unit_type__) {\n        return 0;  // Unit types serialize as 0 bytes\n    } else if constexpr (std::is_same_v<T, std::monostate>) {\n        return 0;  // std::monostate is also a unit type\n    } else {\n        // For complex types, calculate actual BSATN size, not C++ sizeof()\n        return calculate_bsatn_size<T>();\n    }\n}\n\n// Calculate BSATN serialization size for any type\ntemplate<typename T>\nconstexpr size_t calculate_bsatn_size() {\n    // Unit types\n    if constexpr ((requires { T::__is_unit_type__; } && T::__is_unit_type__) || \n                  std::is_same_v<T, std::monostate>) {\n        return 0;\n    }\n    // Primitive types - use their natural sizes (no padding in BSATN)\n    else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>) {\n        return 1;\n    }\n    else if constexpr (std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t>) {\n        return 2;\n    }\n    else if constexpr (std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> || std::is_same_v<T, float>) {\n        return 4;\n    }\n    else if constexpr (std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t> || std::is_same_v<T, double>) {\n        return 8;\n    }\n    // For complex types (structs), we'd need runtime field information\n    // For now, fall back to sizeof() but this should be improved\n    else {\n        return sizeof(T);\n    }\n}\n\n// -----------------------------------------------------------------------------\n// Field Registration Macros\n// -----------------------------------------------------------------------------\n\n// Primary macro for registering a field with auto-initialization\n#define REGISTER_FIELD(struct_type, field_name, field_type) \\\n    __attribute__((export_name(\"__preinit__10_field_\" #struct_type \"_\" #field_name))) \\\n    extern \"C\" void CONCAT(_preinit_register_field_, CONCAT(struct_type, field_name))() { \\\n        SpacetimeDB::FieldDescriptor desc; \\\n        desc.name = #field_name; \\\n        desc.offset = offsetof(struct_type, field_name); \\\n        desc.size = SpacetimeDB::get_field_size<field_type>(); \\\n        desc.write_type = [](std::vector<uint8_t>& buf) { \\\n            SpacetimeDB::write_field_type<field_type>(buf); \\\n        }; \\\n        desc.get_algebraic_type = []() { \\\n            return SpacetimeDB::bsatn::bsatn_traits<field_type>::algebraic_type(); \\\n        }; \\\n        desc.serialize = [](std::vector<uint8_t>& buf, const void* obj) { \\\n            const struct_type* typed_obj = static_cast<const struct_type*>(obj); \\\n            SpacetimeDB::serialize_value(buf, typed_obj->field_name); \\\n        }; \\\n        desc.get_type_name = []() -> std::string { \\\n            /* UNIFIED REGISTRY: Type names handled by demangling */ \\\n            return demangle_cpp_type_name(typeid(field_type).name()); \\\n        }; \\\n        SpacetimeDB::get_table_descriptors()[&typeid(struct_type)].fields.push_back(desc); \\\n    }\n\n// -----------------------------------------------------------------------------\n// Field Registrar Template\n// -----------------------------------------------------------------------------\n\n// Template for on-demand field registration - used by Module_impl.h\ntemplate<typename T>\nstruct field_registrar {\n    static void register_fields() {\n        // Default implementation - no fields to register\n        // Specialized by SPACETIMEDB_STRUCT macro in traits.h\n    }\n};\n\n// Specialization to prevent direct registration of std::variant as table types\ntemplate<typename... Ts>\nstruct field_registrar<std::variant<Ts...>> {\n    static void register_fields() {\n        static_assert(sizeof...(Ts) == 0, \n            \"std::variant types cannot be used directly. \"\n            \"Use SPACETIMEDB_VARIANT_ENUM macro to create variant types with named variants. \"\n            \"Example: SPACETIMEDB_VARIANT_ENUM(MyEnum, (VariantA, uint32_t), (VariantB, std::string))\");\n        \n        // This should never execute due to static_assert, but provide runtime error as backup\n        std::abort(); // Cannot use std::variant directly\n    }\n};\n\n// -----------------------------------------------------------------------------\n// Legacy Support\n// -----------------------------------------------------------------------------\n// These are kept for backward compatibility but should not be used in new code.\n// Use SPACETIMEDB_STRUCT from traits.h instead.\n\n// Use unified macro system from macros.h\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_FIELD_REGISTRATION_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/forward_declarations.h",
    "content": "// Forward declarations for Internal types to break circular dependencies\n#pragma once\n\nnamespace SpacetimeDB::Internal {\n    class AlgebraicType;\n    struct SumType;\n    struct ProductType;\n    struct SumTypeVariant;\n    struct ProductTypeElement;\n} // namespace SpacetimeDB::Internal"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/module_type_registration.h",
    "content": "#ifndef SPACETIMEDB_MODULE_TYPE_REGISTRATION_H\n#define SPACETIMEDB_MODULE_TYPE_REGISTRATION_H\n\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <string>\n#include <typeinfo>\n#include <cstdlib>\n#include \"../bsatn/bsatn.h\"\n\n#if defined(__has_include)\n#if __has_include(<cxxabi.h>) && !defined(_MSC_VER)\n#include <cxxabi.h>\n#define SPACETIMEDB_HAS_CXA_DEMANGLE 1\n#else\n#define SPACETIMEDB_HAS_CXA_DEMANGLE 0\n#endif\n#else\n#define SPACETIMEDB_HAS_CXA_DEMANGLE 0\n#endif\n\n// Forward declarations\nnamespace SpacetimeDB {\nnamespace Internal {\n    class AlgebraicType;\n}\nnamespace detail {\n    // Forward declaration for namespace storage\n    template<typename T>\n    struct namespace_info;\n}\n}\n\n// Helper function to demangle C++ type names - inline implementation for template usage\ninline std::string demangle_cpp_type_name(const char* name) {\n#if SPACETIMEDB_HAS_CXA_DEMANGLE\n    int status = 0;\n    std::unique_ptr<char, void(*)(void*)> demangled(\n        abi::__cxa_demangle(name, nullptr, nullptr, &status),\n        std::free\n    );\n    return (status == 0 && demangled) ? std::string(demangled.get()) : std::string(name);\n#else\n    return std::string(name);\n#endif\n}\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n/**\n * ModuleTypeRegistration - Single unified type registration system for module definitions\n * \n * Core principles:\n * - Only user-defined structs and enums get registered in the types array\n * - Primitives, arrays, Options, and special types are always inlined\n * - Every registered type gets a name and RawTypeDefV10 export\n * - Single entry point: registerType()\n * \n * Type handling:\n * - Primitives (Bool, U8, I32, etc.) → Return inline, never registered\n * - Arrays → Return inline with recursive element processing\n * - Options → Return inline Sum structure\n * - Special types (Identity, etc.) → Return inline Product structure\n * - User structs/enums → Register in typespace, return Ref\n */\nclass ModuleTypeRegistration {\nprivate:\n    // Cache of type name -> typespace index (built from V10Builder type defs)\n    std::unordered_map<std::string, uint32_t> type_name_cache_;\n    \n    // Track types currently being registered to detect cycles\n    std::unordered_set<std::string> types_being_registered_;\n    \n    // Error state - set when we detect validation errors\n    bool has_error_ = false;\n    std::string error_message_;\n    std::string error_type_description_;  // Stores the type structure for debugging\n    \n\npublic:\n    /**\n     * THE ONLY type registration function - single entry point for all types\n     * \n     * @param bsatn_type The type to process\n     * @param explicit_name Optional explicit name for the type\n     * @param cpp_type Optional C++ type info for name extraction\n     * @return AlgebraicType - either inline (primitives/arrays/special) or Ref to registered type\n     */\n    ::SpacetimeDB::Internal::AlgebraicType registerType(const bsatn::AlgebraicType& bsatn_type,\n                                                        const std::string& explicit_name = \"\",\n                                                        const std::type_info* cpp_type = nullptr);\n    \n    /**\n     * Register a type immediately by name (called by enum macros)\n     * This registers the type the first time its algebraic_type() is called\n     */\n    void registerTypeByName(const std::string& type_name, \n                            const bsatn::AlgebraicType& algebraic_type,\n                            const std::type_info* cpp_type);\n    \n    /**\n     * Register a type and return its typespace index\n     * Used by simple enums to get a Ref they can return\n     */\n    uint32_t registerAndGetIndex(const bsatn::AlgebraicType& bsatn_type,\n                                 const std::string& type_name,\n                                 const std::type_info* cpp_type = nullptr);\n    \n    /**\n     * Check if any errors occurred during type registration\n     */\n    bool hasError() const { return has_error_; }\n    \n    /**\n     * Get the error message if an error occurred\n     */\n    const std::string& getErrorMessage() const { return error_message_; }\n    \n    /**\n     * Get the error type description if an error occurred\n     */\n    const std::string& getErrorTypeDescription() const { return error_type_description_; }\n    \n    /**\n     * Add namespace qualification to an existing registered type\n     * \n     * This is called by SPACETIMEDB_NAMESPACE macros during preinit to modify\n     * the registered type name with a namespace prefix.\n     * \n     * @tparam T The C++ type to modify\n     * @param namespace_prefix The namespace prefix to prepend (e.g., \"Namespace\")\n     */\n    template<typename T>\n    void set_type_namespace(const std::string& namespace_prefix) {\n        // Get the type name that was registered\n        std::string original_name = demangle_cpp_type_name(typeid(T).name());\n        \n        // Find the type in our cache\n        auto it = type_name_cache_.find(original_name);\n        if (it != type_name_cache_.end()) {\n            uint32_t type_index = it->second;\n            std::string qualified_name = namespace_prefix + \".\" + original_name;\n            \n            // Update the cache with the new name\n            type_name_cache_.erase(it);\n            type_name_cache_[qualified_name] = type_index;\n            \n            // Update the actual type name in the module definition\n            updateTypeNameInModule(type_index, qualified_name);\n        }\n    }\n    \n    /**\n     * Clear all registration state - used to reset between module builds\n     */\n    void clear() {\n        type_name_cache_.clear();\n        types_being_registered_.clear();\n        has_error_ = false;\n        error_message_.clear();\n        error_type_description_.clear();\n    }\n\nprivate:\n    /**\n     * Check if a type is a primitive (tags 0-19)\n     */\n    bool isPrimitive(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Check if a type is a special type (Identity, ConnectionId, etc.)\n     */\n    bool isSpecialType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Check if a type is an Option (Sum with \"some\" and \"none\" variants)\n     */\n    bool isOptionType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Check if a type is Result (Sum with \"ok\" and \"err\" variants)\n     */\n    bool isResultType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Check if a type is ScheduleAt (Sum with \"Interval\" and \"Time\" variants)\n     */\n    bool isScheduleAtType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Check if a type is Unit (empty Product)\n     */\n    bool isUnitType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Extract a clean type name from C++ type_info\n     */\n    std::string extractTypeName(const std::type_info* cpp_type) const;\n    \n    /**\n     * Parse namespace and name from qualified name (e.g., \"Namespace.TestC\" -> [\"Namespace\"], \"TestC\")\n     */\n    std::pair<std::vector<std::string>, std::string> parseNamespaceAndName(const std::string& qualified_name) const;\n    \n    /**\n     * Convert a primitive BSATN type to internal AlgebraicType\n     */\n    ::SpacetimeDB::Internal::AlgebraicType convertPrimitive(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Convert an array type, recursively processing the element\n     */\n    ::SpacetimeDB::Internal::AlgebraicType convertArray(const bsatn::AlgebraicType& type);\n    \n    /**\n     * Convert a special type to its inline Product structure\n     */\n    ::SpacetimeDB::Internal::AlgebraicType convertSpecialType(const bsatn::AlgebraicType& type);\n    \n    /**\n     * Convert an Option or ScheduleAt to its inline Sum structure\n     */\n    ::SpacetimeDB::Internal::AlgebraicType convertInlineSum(const bsatn::AlgebraicType& type);\n    \n    /**\n     * Convert a Unit type to its inline Product structure\n     */\n    ::SpacetimeDB::Internal::AlgebraicType convertUnitType() const;\n    \n    /**\n     * Register a complex user-defined type (struct or enum)\n     */\n    ::SpacetimeDB::Internal::AlgebraicType registerComplexType(const bsatn::AlgebraicType& type,\n                                                               const std::string& type_name);\n    \n    /**\n     * Process a Product type, recursively registering field types\n     */\n    ::SpacetimeDB::Internal::AlgebraicType processProduct(const bsatn::AlgebraicType& type);\n    \n    /**\n     * Process a Sum type, recursively registering variant types\n     */\n    ::SpacetimeDB::Internal::AlgebraicType processSum(const bsatn::AlgebraicType& type);\n    \n    /**\n     * Describe a type structure for error messages\n     */\n    std::string describeType(const bsatn::AlgebraicType& type) const;\n    \n    /**\n     * Update the type name in the actual module definition\n     * \n     * This modifies the RawTypeDefV9 entry in the module to use the new\n     * namespace-qualified name for client generation.\n     * \n     * @param type_index The index of the type to update\n     * @param new_name The new qualified name to use\n     */\n    void updateTypeNameInModule(uint32_t type_index, const std::string& new_name);\n    \n};\n\n// Global V9 type registration instance\nextern std::unique_ptr<ModuleTypeRegistration> g_module_type_registration;\n\n// Initialize the V9 type registration (called once at module startup)\nvoid initializeModuleTypeRegistration();\n\n// Get the global V9 type registration\nModuleTypeRegistration& getModuleTypeRegistration();\n\n} // namespace Internal\n\nnamespace Internal {\n\n// Thread-local storage for tracking the chain of types being registered\n// This is used to detect circular references during type building\nextern thread_local std::vector<std::string> g_type_registration_chain;\n\n// Global flag to indicate circular reference error (set during type building)\nextern bool g_circular_ref_error;\nextern std::string g_circular_ref_type_name;\n\n/**\n * @brief Template helper to abstract the lazy type registration pattern\n * \n * This eliminates code duplication between enums and structs that all follow\n * the same pattern:\n * - Static variable to cache the type index\n * - One-time registration on first call\n * - Return Ref to the registered type\n * \n * Benefits of this abstraction:\n * - Reduces code duplication by ~15 lines per type\n * - Consistent registration behavior across all user-defined types\n * - Better error handling and validation\n * - Thread-safe initialization\n * - Cleaner macro implementations\n * \n * @tparam T The C++ type being registered\n */\ntemplate<typename T>\nclass LazyTypeRegistrar {\nprivate:\n    static inline uint32_t type_index_ = 0xFFFFFFFF;\n    \npublic:\n    /**\n     * Get or register a type with lazy initialization\n     * \n     * This method uses lazy initialization to register a type only when first needed.\n     * The registration is thread-safe and cached for subsequent calls.\n     * \n     * @tparam BuilderFunc Function type that builds the AlgebraicType\n     * @param build_func Function that constructs the AlgebraicType for this type\n     * @param type_name Explicit name for the type (typically from #Type macro)\n     * @return AlgebraicType::Ref to the registered type\n     * \n     * @note The build_func should be a lambda that constructs the AlgebraicType\n     *       without any side effects, as it may be called during registration.\n     */\n    template<typename BuilderFunc>\n    static bsatn::AlgebraicType getOrRegister(BuilderFunc&& build_func, \n                                              const std::string& type_name) {\n        // Check if already registered (fast path for subsequent calls)\n        if (type_index_ != 0xFFFFFFFF) {\n            return bsatn::AlgebraicType::make_ref(type_index_);\n        }\n        \n        // Check if this type has namespace information and build qualified name\n        std::string qualified_name = type_name;\n        if constexpr (requires { SpacetimeDB::detail::namespace_info<T>::value; }) {\n            constexpr const char* namespace_prefix = SpacetimeDB::detail::namespace_info<T>::value;\n            if (namespace_prefix != nullptr) {\n                qualified_name = std::string(namespace_prefix) + \".\" + type_name;\n            }\n        }\n        \n        // CRITICAL: Check for circular references BEFORE building the type\n        // This prevents infinite recursion during type construction\n        for (const auto& type_in_chain : g_type_registration_chain) {\n            if (type_in_chain == qualified_name) {\n                // Circular reference detected!\n                fprintf(stderr, \"\\n\\n[CIRCULAR REFERENCE DETECTED]\\n\");\n                fprintf(stderr, \"ERROR: Circular reference detected for type '%s'\\n\", qualified_name.c_str());\n                fprintf(stderr, \"  Registration chain: \");\n                for (const auto& t : g_type_registration_chain) {\n                    fprintf(stderr, \"%s -> \", t.c_str());\n                }\n                fprintf(stderr, \"%s (circular!)\\n\", qualified_name.c_str());\n                fprintf(stderr, \"Setting g_circular_ref_error = true\\n\");\n                fprintf(stderr, \"[END CIRCULAR REFERENCE DETECTION]\\n\\n\");\n                fflush(stderr);\n                \n                // Set global error flag for preinit_99 to handle\n                g_circular_ref_error = true;\n                g_circular_ref_type_name = qualified_name;\n                \n                // Return a simple primitive type to break the infinite recursion\n                // This prevents any invalid references from being created\n                // The error will be handled in preinit_99\n                return bsatn::AlgebraicType::U32();  // Return a safe primitive type\n            }\n        }\n        \n        // Add this type to the registration chain\n        g_type_registration_chain.push_back(qualified_name);\n        \n        // Slow path: first registration\n        // Build the algebraic type using the provided function\n        bsatn::AlgebraicType algebraic_type = build_func();\n        \n        // Remove from chain after successful building\n        g_type_registration_chain.pop_back();\n        \n        // Check if circular reference was detected during type building\n        if (g_circular_ref_error) {\n            // Don't register anything - just return the safe type\n            // The error will be handled in preinit_99\n            return algebraic_type;\n        }\n        \n        // Register with V9 system and cache the index using the qualified name\n        type_index_ = getModuleTypeRegistration().registerAndGetIndex(\n            algebraic_type, qualified_name, &typeid(T));\n        \n        return bsatn::AlgebraicType::make_ref(type_index_);\n    }\n    \n    /**\n     * Check if this type has been registered yet\n     * @return true if the type has been registered and cached\n     */\n    static bool isRegistered() {\n        return type_index_ != 0xFFFFFFFF;\n    }\n    \n    /**\n     * Get the cached type index (only valid if isRegistered() returns true)\n     * @return The cached type index\n     * @warning Only call this if isRegistered() returns true\n     */\n    static uint32_t getTypeIndex() {\n        return type_index_;\n    }\n    \n    /**\n     * Force reset the registration state (for testing purposes only)\n     * @warning This should not be used in production code\n     */\n    static void resetForTesting() {\n        type_index_ = 0xFFFFFFFF;\n    }\n};\n\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_MODULE_TYPE_REGISTRATION_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/runtime_registration.h",
    "content": "#ifndef SPACETIMEDB_RUNTIME_REGISTRATION_H\n#define SPACETIMEDB_RUNTIME_REGISTRATION_H\n\n#include <functional>\n#include <optional>\n#include <string>\n#include <vector>\n#include \"../abi/opaque_types.h\"\n#include \"autogen/Lifecycle.g.h\"\n\nnamespace SpacetimeDB {\n\nstruct ReducerContext;\nstruct ViewContext;\nstruct AnonymousViewContext;\nstruct ProcedureContext;\n\nnamespace Internal {\n\nvoid RegisterReducerHandler(const std::string& name,\n                           std::function<void(ReducerContext&, BytesSource)> handler,\n                           std::optional<Lifecycle> lifecycle = std::nullopt);\nvoid RegisterViewHandler(const std::string& name,\n                        std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler);\nvoid RegisterAnonymousViewHandler(const std::string& name,\n                                 std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler);\nvoid RegisterProcedureHandler(const std::string& name,\n                             std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler);\nsize_t GetViewHandlerCount();\nsize_t GetAnonymousViewHandlerCount();\nsize_t GetProcedureHandlerCount();\nstd::vector<uint8_t> ConsumeBytes(BytesSource source);\nvoid SetMultiplePrimaryKeyError(const std::string& table_name);\nvoid SetConstraintRegistrationError(const std::string& code, const std::string& details);\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_RUNTIME_REGISTRATION_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/template_utils.h",
    "content": "#ifndef SPACETIMEDB_TEMPLATE_UTILS_H\n#define SPACETIMEDB_TEMPLATE_UTILS_H\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n#include <vector>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\ntemplate<typename T>\nstruct function_traits;\n\ntemplate<typename R, typename... Args>\nstruct function_traits<R(*)(Args...)> {\n    static constexpr size_t arity = sizeof...(Args);\n    using result_type = R;\n\n    template<size_t N>\n    using arg_t = typename std::tuple_element<N, std::tuple<Args...>>::type;\n};\n\ntemplate<typename T>\nstd::vector<T> view_result_to_vec(std::vector<T>&& vec) {\n    return std::move(vec);\n}\n\ntemplate<typename T>\nstd::vector<T> view_result_to_vec(const std::vector<T>& vec) {\n    return vec;\n}\n\ntemplate<typename T>\nstd::vector<T> view_result_to_vec(std::optional<T>&& opt) {\n    std::vector<T> result;\n    if (opt.has_value()) {\n        result.push_back(std::move(*opt));\n    }\n    return result;\n}\n\ntemplate<typename T>\nstd::vector<T> view_result_to_vec(const std::optional<T>& opt) {\n    std::vector<T> result;\n    if (opt.has_value()) {\n        result.push_back(*opt);\n    }\n    return result;\n}\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_TEMPLATE_UTILS_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/v10_builder.h",
    "content": "#ifndef SPACETIMEDB_V10_BUILDER_H\n#define SPACETIMEDB_V10_BUILDER_H\n\n#include <functional>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n#include <cstdio>\n#include \"../bsatn/bsatn.h\"\n#include \"../database.h\"\n#include \"autogen/CaseConversionPolicy.g.h\"\n#include \"autogen/ExplicitNameEntry.g.h\"\n#include \"autogen/NameMapping.g.h\"\n#include \"autogen/AlgebraicType.g.h\"\n#include \"autogen/SumType.g.h\"\n#include \"autogen/ProductType.g.h\"\n#include \"autogen/ProductTypeElement.g.h\"\n#include \"autogen/RawModuleDefV10.g.h\"\n#include \"autogen/Typespace.g.h\"\n#include \"autogen/RawTableDefV10.g.h\"\n#include \"autogen/RawReducerDefV10.g.h\"\n#include \"autogen/RawProcedureDefV10.g.h\"\n#include \"autogen/RawViewDefV10.g.h\"\n#include \"autogen/RawScheduleDefV10.g.h\"\n#include \"autogen/RawLifeCycleReducerDefV10.g.h\"\n#include \"autogen/RawColumnDefaultValueV10.g.h\"\n#include \"autogen/RawRowLevelSecurityDefV9.g.h\"\n#include \"autogen/RawTypeDefV10.g.h\"\n#include \"field_registration.h\"\n#include \"buffer_pool.h\"\n#include \"runtime_registration.h\"\n#include \"template_utils.h\"\n#include \"module_type_registration.h\"\n\nnamespace SpacetimeDB {\n\nvoid fail_reducer(std::string message);\n\nnamespace Internal {\n\nclass V10Builder {\npublic:\n    V10Builder() = default;\n\n    void Clear();\n\n    template<typename T>\n    void RegisterTable(const std::string& table_name, bool is_public, bool is_event = false) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping table registration '%s' because circular reference error is set\\n\", table_name.c_str());\n            return;\n        }\n        SpacetimeDB::field_registrar<T>::register_fields();\n        auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n        auto it = descriptor_map.find(&typeid(T));\n        if (it == descriptor_map.end()) {\n            SetConstraintRegistrationError(\n                \"TABLE_NO_FIELD_DESCRIPTORS\",\n                \"table='\" + table_name + \"' has no registered field descriptors\");\n            return;\n        }\n\n        std::vector<bsatn::ProductTypeElement> elements;\n        const auto& field_descs = it->second.fields;\n        for (const auto& field_desc : field_descs) {\n            bsatn::AlgebraicType field_type = field_desc.get_algebraic_type();\n            std::string field_type_name = field_desc.get_type_name ? field_desc.get_type_name() : \"\";\n            if (!field_type_name.empty() && field_type.tag() == bsatn::AlgebraicTypeTag::Sum) {\n                const auto& sum = field_type.as_sum();\n                bool is_option = (sum.variants.size() == 2 && sum.variants[0].name == \"some\" && sum.variants[1].name == \"none\");\n                bool is_schedule_at = (sum.variants.size() == 2 && sum.variants[0].name == \"Interval\" && sum.variants[1].name == \"Time\");\n                bool is_result = (sum.variants.size() == 2 && sum.variants[0].name == \"ok\" && sum.variants[1].name == \"err\");\n                if (!is_option && !is_schedule_at && !is_result) {\n                    size_t last_colon = field_type_name.rfind(\"::\");\n                    if (last_colon != std::string::npos) {\n                        field_type_name = field_type_name.substr(last_colon + 2);\n                    }\n                    getModuleTypeRegistration().registerTypeByName(field_type_name, field_type, nullptr);\n                }\n            }\n            elements.emplace_back(std::make_optional(field_desc.name), std::move(field_type));\n        }\n\n        bsatn::ProductType bsatn_product(std::move(elements));\n        bsatn::AlgebraicType table_type = bsatn::AlgebraicType::make_product(\n            std::make_unique<bsatn::ProductType>(std::move(bsatn_product)));\n        AlgebraicType registered_type = getModuleTypeRegistration().registerType(table_type, \"\", &typeid(T));\n        if (registered_type.get_tag() != AlgebraicType::Tag::Ref) {\n            SetConstraintRegistrationError(\n                \"TABLE_TYPE_NOT_REF\",\n                \"table='\" + table_name + \"' did not register as a named Ref type\");\n            return;\n        }\n\n        RawTableDefV10 table_def{\n            table_name,\n            registered_type.get<0>(),\n            {},\n            {},\n            {},\n            {},\n            TableType::User,\n            is_public ? TableAccess::Public : TableAccess::Private,\n            column_defaults_by_table_[table_name],\n            is_event,\n        };\n        UpsertTable(table_def);\n        SetTableIsEventFlag(table_name, is_event);\n    }\n\n    template<typename T>\n    void AddFieldConstraint(const std::string& table_name,\n                            const std::string& field_name,\n                            FieldConstraint constraint) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping field constraint registration '%s.%s' because circular reference error is set\\n\",\n                        table_name.c_str(), field_name.c_str());\n            return;\n        }\n        SpacetimeDB::field_registrar<T>::register_fields();\n        auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n        auto it = descriptor_map.find(&typeid(T));\n        if (it == descriptor_map.end()) {\n            SetConstraintRegistrationError(\n                \"NO_FIELD_DESCRIPTORS\",\n                \"table='\" + table_name + \"' field='\" + field_name + \"' has no registered field descriptors\");\n            return;\n        }\n\n        uint16_t field_idx = 0;\n        bool field_found = false;\n        for (const auto& field_desc : it->second.fields) {\n            if (field_desc.name == field_name) {\n                field_found = true;\n                break;\n            }\n            field_idx++;\n        }\n        if (!field_found) {\n            SetConstraintRegistrationError(\n                \"FIELD_NOT_FOUND\",\n                \"table='\" + table_name + \"' field='\" + field_name + \"' was not found\");\n            return;\n        }\n\n        auto table_it = FindTable(table_name);\n        if (table_it == tables_.end()) {\n            SetConstraintRegistrationError(\n                \"TABLE_NOT_FOUND\",\n                \"table='\" + table_name + \"' was not registered before applying field constraints\");\n            return;\n        }\n\n        int constraint_bits = static_cast<int>(constraint);\n        if (constraint_bits & 0b1000) {\n            if (!table_it->primary_key.empty()) {\n                SetMultiplePrimaryKeyError(table_name);\n                return;\n            }\n            table_it->primary_key.push_back(field_idx);\n            table_it->constraints.push_back(CreateUniqueConstraint(table_name, field_name, field_idx));\n            table_it->indexes.push_back(CreateBTreeIndex(table_name, table_name + \"_\" + field_name + \"_idx_btree\", {field_idx}, field_name));\n        } else if ((constraint_bits & 0b0100) && !(constraint_bits & 0b1000)) {\n            table_it->constraints.push_back(CreateUniqueConstraint(table_name, field_name, field_idx));\n            table_it->indexes.push_back(CreateBTreeIndex(table_name, table_name + \"_\" + field_name + \"_idx_btree\", {field_idx}, field_name));\n        } else if ((constraint_bits & 0b0001) && !(constraint_bits & 0b1100)) {\n            table_it->indexes.push_back(CreateBTreeIndex(table_name, table_name + \"_\" + field_name + \"_idx_btree\", {field_idx}, field_name));\n        }\n\n        if (constraint_bits & static_cast<int>(FieldConstraint::AutoInc)) {\n            RawSequenceDefV10 seq_def;\n            // Defer sequence naming to host-side canonical generation for Rust/C# parity.\n            seq_def.source_name = std::nullopt;\n            seq_def.column = field_idx;\n            seq_def.start = std::nullopt;\n            seq_def.increment = SpacetimeDB::I128(1);\n            seq_def.min_value = std::nullopt;\n            seq_def.max_value = std::nullopt;\n            table_it->sequences.push_back(std::move(seq_def));\n        }\n    }\n\n    template<typename T>\n    void AddMultiColumnIndex(const std::string& table_name,\n                             const std::string& index_name,\n                             const std::vector<std::string>& field_names) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping multi-column index registration '%s.%s' because circular reference error is set\\n\",\n                        table_name.c_str(), index_name.c_str());\n            return;\n        }\n        if (field_names.empty()) {\n            SetConstraintRegistrationError(\n                \"MULTI_INDEX_EMPTY\",\n                \"table='\" + table_name + \"' index='\" + index_name + \"' has no fields\");\n            return;\n        }\n        SpacetimeDB::field_registrar<T>::register_fields();\n        auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n        auto it = descriptor_map.find(&typeid(T));\n        if (it == descriptor_map.end()) {\n            SetConstraintRegistrationError(\n                \"NO_FIELD_DESCRIPTORS\",\n                \"table='\" + table_name + \"' index='\" + index_name + \"' has no registered field descriptors\");\n            return;\n        }\n\n        auto table_it = FindTable(table_name);\n        if (table_it == tables_.end()) {\n            SetConstraintRegistrationError(\n                \"TABLE_NOT_FOUND\",\n                \"table='\" + table_name + \"' index='\" + index_name + \"' references an unknown table\");\n            return;\n        }\n\n        std::vector<uint16_t> field_indexes;\n        for (const std::string& field_name : field_names) {\n            uint16_t field_idx = 0;\n            bool found = false;\n            for (const auto& field_desc : it->second.fields) {\n                if (field_desc.name == field_name) {\n                    field_indexes.push_back(field_idx);\n                    found = true;\n                    break;\n                }\n                field_idx++;\n            }\n            if (!found) {\n                SetConstraintRegistrationError(\n                    \"FIELD_NOT_FOUND\",\n                    \"table='\" + table_name + \"' index='\" + index_name + \"' field='\" + field_name + \"' was not found\");\n                return;\n            }\n        }\n\n        std::string generated_name = table_name + \"_\" + field_names[0];\n        for (size_t i = 1; i < field_names.size(); ++i) {\n            generated_name += \"_\" + field_names[i];\n        }\n        generated_name += \"_idx_btree\";\n        table_it->indexes.push_back(CreateBTreeIndex(table_name, generated_name, field_indexes, index_name));\n    }\n\n    template<typename T>\n    void AddColumnDefault(const std::string& table_name,\n                          const std::string& field_name,\n                          const std::vector<uint8_t>& serialized_value) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping default-value registration '%s.%s' because circular reference error is set\\n\",\n                        table_name.c_str(), field_name.c_str());\n            return;\n        }\n        auto table_it = FindTable(table_name);\n        if (table_it == tables_.end()) {\n            SetConstraintRegistrationError(\n                \"TABLE_NOT_FOUND\",\n                \"table='\" + table_name + \"' default field='\" + field_name + \"' references an unknown table\");\n            return;\n        }\n\n        SpacetimeDB::field_registrar<T>::register_fields();\n        auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n        auto it = descriptor_map.find(&typeid(T));\n        if (it == descriptor_map.end()) {\n            SetConstraintRegistrationError(\n                \"NO_FIELD_DESCRIPTORS\",\n                \"table='\" + table_name + \"' default field='\" + field_name + \"' has no registered field descriptors\");\n            return;\n        }\n\n        bool field_found = false;\n        const auto& fields = it->second.fields;\n        for (uint16_t i = 0; i < fields.size(); ++i) {\n            if (fields[i].name == field_name) {\n                field_found = true;\n                for (uint16_t pk_col : table_it->primary_key) {\n                    if (pk_col == i) {\n                        SetConstraintRegistrationError(\n                            \"DEFAULT_ON_PRIMARY_KEY\",\n                            \"table='\" + table_name + \"' field='\" + field_name + \"' cannot have default on primary key\");\n                        return;\n                    }\n                }\n                for (const auto& constraint : table_it->constraints) {\n                    if (constraint.data.get_tag() == 0) {\n                        const auto& unique_data = constraint.data.get<0>();\n                        if (unique_data.columns.size() == 1 && unique_data.columns[0] == i) {\n                            SetConstraintRegistrationError(\n                                \"DEFAULT_ON_UNIQUE\",\n                                \"table='\" + table_name + \"' field='\" + field_name + \"' cannot have default on unique field\");\n                            return;\n                        }\n                    }\n                }\n                for (const auto& sequence : table_it->sequences) {\n                    if (sequence.column == i) {\n                        SetConstraintRegistrationError(\n                            \"DEFAULT_ON_AUTOINC\",\n                            \"table='\" + table_name + \"' field='\" + field_name + \"' cannot have default on autoincrement field\");\n                        return;\n                    }\n                }\n                column_defaults_by_table_[table_name].push_back(RawColumnDefaultValueV10{i, serialized_value});\n                table_it->default_values = column_defaults_by_table_[table_name];\n                break;\n            }\n        }\n        if (!field_found) {\n            SetConstraintRegistrationError(\n                \"FIELD_NOT_FOUND\",\n                \"table='\" + table_name + \"' default field='\" + field_name + \"' was not found\");\n        }\n    }\n\n    template<typename Func>\n    void RegisterReducer(const std::string& reducer_name,\n                         Func func,\n                         const std::vector<std::string>& param_names) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping reducer registration '%s' because circular reference error is set\\n\",\n                        reducer_name.c_str());\n            return;\n        }\n        using traits = function_traits<Func>;\n        static_assert(traits::arity > 0, \"Reducer must have at least one parameter (ReducerContext)\");\n        if constexpr (traits::arity > 0) {\n            using FirstParamType = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<0>>>;\n            static_assert(std::is_same_v<FirstParamType, ReducerContext>,\n                \"First parameter of reducer must be ReducerContext\");\n        }\n\n        std::function<void(ReducerContext&, BytesSource)> handler;\n        if constexpr (traits::arity == 1) {\n            handler = [func](ReducerContext& ctx, BytesSource) {\n                auto result = func(ctx);\n                if (result.is_err()) {\n                    ::SpacetimeDB::fail_reducer(result.error());\n                }\n            };\n        } else {\n            handler = [func](ReducerContext& ctx, BytesSource args_source) {\n                std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n                []<std::size_t... Js>(std::index_sequence<Js...>, Func fn, ReducerContext& ctx_inner, const std::vector<uint8_t>& bytes) {\n                    bsatn::Reader reader(bytes.data(), bytes.size());\n                    auto args = std::make_tuple(bsatn::deserialize<typename traits::template arg_t<Js + 1>>(reader)...);\n                    std::apply([&ctx_inner, fn](auto&&... unpacked) {\n                        auto result = fn(ctx_inner, std::forward<decltype(unpacked)>(unpacked)...);\n                        if (result.is_err()) {\n                            ::SpacetimeDB::fail_reducer(result.error());\n                        }\n                    }, args);\n                }(std::make_index_sequence<traits::arity - 1>{}, func, ctx, args_bytes);\n            };\n        }\n        RegisterReducerHandler(reducer_name, handler, std::nullopt);\n\n        ProductType params;\n        if constexpr (traits::arity > 1) {\n            auto& type_reg = getModuleTypeRegistration();\n            []<std::size_t... Is>(std::index_sequence<Is...>,\n                                  ProductType& out_params,\n                                  const std::vector<std::string>& names,\n                                  ModuleTypeRegistration& reg) {\n                (([]<std::size_t I>(ProductType& p,\n                                    const std::vector<std::string>& n,\n                                    ModuleTypeRegistration& r) {\n                    using param_type = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<I + 1>>>;\n                    auto bsatn_type = bsatn::bsatn_traits<param_type>::algebraic_type();\n                    AlgebraicType internal_type = r.registerType(bsatn_type, \"\", &typeid(param_type));\n                    std::string param_name = (I < n.size()) ? n[I] : (\"arg\" + std::to_string(I));\n                    p.elements.emplace_back(std::make_optional(param_name), std::move(internal_type));\n                }.template operator()<Is>(out_params, names, reg)), ...);\n            }(std::make_index_sequence<traits::arity - 1>{}, params, param_names, type_reg);\n        }\n\n        RawReducerDefV10 reducer_def{\n            reducer_name,\n            std::move(params),\n            FunctionVisibility::ClientCallable,\n            MakeUnitAlgebraicType(),\n            MakeStringAlgebraicType(),\n        };\n        UpsertReducer(reducer_def);\n    }\n\n    template<typename Func>\n    void RegisterLifecycleReducer(const std::string& reducer_name, Func func, Lifecycle lifecycle) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping lifecycle reducer registration '%s' because circular reference error is set\\n\",\n                        reducer_name.c_str());\n            return;\n        }\n        using traits = function_traits<Func>;\n        static_assert(traits::arity > 0, \"Reducer must have at least one parameter (ReducerContext)\");\n        if constexpr (traits::arity > 0) {\n            using FirstParamType = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<0>>>;\n            static_assert(std::is_same_v<FirstParamType, ReducerContext>,\n                \"First parameter of reducer must be ReducerContext\");\n        }\n\n        std::function<void(ReducerContext&, BytesSource)> handler;\n        if constexpr (traits::arity == 1) {\n            handler = [func](ReducerContext& ctx, BytesSource) {\n                auto result = func(ctx);\n                if (result.is_err()) {\n                    ::SpacetimeDB::fail_reducer(result.error());\n                }\n            };\n        } else {\n            handler = [func](ReducerContext& ctx, BytesSource args_source) {\n                std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n                []<std::size_t... Js>(std::index_sequence<Js...>, Func fn, ReducerContext& ctx_inner, const std::vector<uint8_t>& bytes) {\n                    bsatn::Reader reader(bytes.data(), bytes.size());\n                    auto args = std::make_tuple(bsatn::deserialize<typename traits::template arg_t<Js + 1>>(reader)...);\n                    std::apply([&ctx_inner, fn](auto&&... unpacked) {\n                        auto result = fn(ctx_inner, std::forward<decltype(unpacked)>(unpacked)...);\n                        if (result.is_err()) {\n                            ::SpacetimeDB::fail_reducer(result.error());\n                        }\n                    }, args);\n                }(std::make_index_sequence<traits::arity - 1>{}, func, ctx, args_bytes);\n            };\n        }\n        RegisterReducerHandler(reducer_name, handler, lifecycle);\n\n        RawReducerDefV10 reducer_def{\n            reducer_name,\n            ProductType{},\n            FunctionVisibility::Private,\n            MakeUnitAlgebraicType(),\n            MakeStringAlgebraicType(),\n        };\n        UpsertReducer(reducer_def);\n        UpsertLifecycleReducer(RawLifeCycleReducerDefV10{lifecycle, reducer_name});\n    }\n\n    template<typename Func>\n    void RegisterView(const std::string& view_name,\n                      Func func,\n                      bool is_public,\n                      const std::vector<std::string>& param_names = {}) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping view registration '%s' because circular reference error is set\\n\",\n                        view_name.c_str());\n            return;\n        }\n        (void)param_names;\n        using traits = function_traits<Func>;\n        using ContextType = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<0>>>;\n        using ReturnType = typename traits::result_type;\n        static_assert(traits::arity > 0, \"View must have at least one parameter (ViewContext or AnonymousViewContext)\");\n        static_assert(std::is_same_v<ContextType, ViewContext> || std::is_same_v<ContextType, AnonymousViewContext>,\n            \"First parameter of view must be ViewContext or AnonymousViewContext\");\n\n        if constexpr (std::is_same_v<ContextType, ViewContext>) {\n            std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler =\n                [func](ViewContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n                    (void)args_source;\n                    auto result = func(ctx);\n                    auto result_vec = view_result_to_vec(std::move(result));\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result_vec);\n                    }\n                    return buf.release();\n                };\n            RegisterViewHandler(view_name, handler);\n        } else {\n            std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler =\n                [func](AnonymousViewContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n                    (void)args_source;\n                    auto result = func(ctx);\n                    auto result_vec = view_result_to_vec(std::move(result));\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result_vec);\n                    }\n                    return buf.release();\n                };\n            RegisterAnonymousViewHandler(view_name, handler);\n        }\n\n        auto& type_reg = getModuleTypeRegistration();\n        auto bsatn_return = bsatn::bsatn_traits<ReturnType>::algebraic_type();\n        AlgebraicType return_type = type_reg.registerType(bsatn_return, \"\", &typeid(ReturnType));\n        bool is_anonymous = std::is_same_v<ContextType, AnonymousViewContext>;\n        uint32_t index = static_cast<uint32_t>(is_anonymous ? (GetAnonymousViewHandlerCount() - 1) : (GetViewHandlerCount() - 1));\n\n        RawViewDefV10 view_def{\n            view_name,\n            index,\n            is_public,\n            is_anonymous,\n            ProductType{},\n            return_type,\n        };\n        UpsertView(view_def);\n    }\n\n    template<typename Func>\n    void RegisterProcedure(const std::string& procedure_name,\n                           Func func,\n                           const std::vector<std::string>& param_names = {}) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping procedure registration '%s' because circular reference error is set\\n\",\n                        procedure_name.c_str());\n            return;\n        }\n        using traits = function_traits<Func>;\n        using ReturnType = typename traits::result_type;\n        static_assert(traits::arity > 0, \"Procedure must have at least one parameter (ProcedureContext)\");\n        if constexpr (traits::arity > 0) {\n            using FirstParamType = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<0>>>;\n            static_assert(std::is_same_v<FirstParamType, ProcedureContext>,\n                \"First parameter of procedure must be ProcedureContext\");\n        }\n\n        std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler;\n        if constexpr (traits::arity == 1) {\n            handler = [func](ProcedureContext& ctx, BytesSource) -> std::vector<uint8_t> {\n                auto result = func(ctx);\n                IterBuf buf = IterBuf::take();\n                {\n                    bsatn::Writer writer(buf.get());\n                    bsatn::serialize(writer, result);\n                }\n                return buf.release();\n            };\n        } else {\n            handler = [func](ProcedureContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n                std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n                return []<std::size_t... Js>(std::index_sequence<Js...>, Func fn, ProcedureContext& ctx_inner, const std::vector<uint8_t>& bytes) -> std::vector<uint8_t> {\n                    bsatn::Reader reader(bytes.data(), bytes.size());\n                    auto args = std::make_tuple(bsatn::deserialize<typename traits::template arg_t<Js + 1>>(reader)...);\n                    auto result = std::apply([&ctx_inner, fn](auto&&... unpacked) {\n                        return fn(ctx_inner, std::forward<decltype(unpacked)>(unpacked)...);\n                    }, args);\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result);\n                    }\n                    return buf.release();\n                }(std::make_index_sequence<traits::arity - 1>{}, func, ctx, args_bytes);\n            };\n        }\n        RegisterProcedureHandler(procedure_name, handler);\n\n        auto& type_reg = getModuleTypeRegistration();\n        auto bsatn_return = bsatn::bsatn_traits<ReturnType>::algebraic_type();\n        AlgebraicType return_type = type_reg.registerType(bsatn_return, \"\", &typeid(ReturnType));\n\n        ProductType params;\n        if constexpr (traits::arity > 1) {\n            []<std::size_t... Is>(std::index_sequence<Is...>,\n                                  ProductType& out_params,\n                                  const std::vector<std::string>& names,\n                                  ModuleTypeRegistration& reg) {\n                (([]<std::size_t I>(ProductType& p,\n                                    const std::vector<std::string>& n,\n                                    ModuleTypeRegistration& r) {\n                    using param_type = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<I + 1>>>;\n                    auto bsatn_type = bsatn::bsatn_traits<param_type>::algebraic_type();\n                    AlgebraicType internal_type = r.registerType(bsatn_type, \"\", &typeid(param_type));\n                    std::string param_name = (I < n.size()) ? n[I] : (\"arg\" + std::to_string(I));\n                    p.elements.emplace_back(std::make_optional(param_name), std::move(internal_type));\n                }.template operator()<Is>(out_params, names, reg)), ...);\n            }(std::make_index_sequence<traits::arity - 1>{}, params, param_names, type_reg);\n        }\n\n        RawProcedureDefV10 procedure_def{\n            procedure_name,\n            std::move(params),\n            return_type,\n            FunctionVisibility::ClientCallable,\n        };\n        UpsertProcedure(procedure_def);\n    }\n\n    void RegisterSchedule(const std::string& table_name, uint16_t scheduled_at_column, const std::string& reducer_name) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping schedule registration for table '%s' because circular reference error is set\\n\",\n                        table_name.c_str());\n            return;\n        }\n        std::optional<std::string> schedule_name = table_name + \"_sched\";\n        auto it = std::find_if(schedules_.begin(), schedules_.end(), [&](const auto& schedule) {\n            return schedule.table_name == table_name;\n        });\n        RawScheduleDefV10 schedule{schedule_name, table_name, scheduled_at_column, reducer_name};\n        if (it == schedules_.end()) {\n            schedules_.push_back(std::move(schedule));\n        } else {\n            *it = std::move(schedule);\n        }\n    }\n\n    void RegisterRowLevelSecurity(const std::string& sql_query) {\n        row_level_security_.push_back(RawRowLevelSecurityDefV9{sql_query});\n    }\n\n    void SetTableIsEventFlag(const std::string& table_name, bool is_event);\n    bool GetTableIsEventFlag(const std::string& table_name) const;\n\n    void SetCaseConversionPolicy(CaseConversionPolicy policy) {\n        case_conversion_policy_ = policy;\n    }\n\n    void RegisterExplicitTableName(const std::string& source_name, const std::string& canonical_name);\n    void RegisterExplicitFunctionName(const std::string& source_name, const std::string& canonical_name);\n    void RegisterExplicitIndexName(const std::string& source_name, const std::string& canonical_name);\n\n    RawModuleDefV10 BuildModuleDef() const;\n    Typespace& GetTypespace() { return typespace_; }\n    const Typespace& GetTypespace() const { return typespace_; }\n    std::vector<RawTypeDefV10>& GetTypeDefs() { return types_; }\n    const std::vector<RawTypeDefV10>& GetTypeDefs() const { return types_; }\n    std::vector<RawTableDefV10>& GetTables() { return tables_; }\n    const std::vector<RawTableDefV10>& GetTables() const { return tables_; }\n    std::vector<RawReducerDefV10>& GetReducers() { return reducers_; }\n    const std::vector<RawReducerDefV10>& GetReducers() const { return reducers_; }\n    const std::optional<CaseConversionPolicy>& GetCaseConversionPolicy() const { return case_conversion_policy_; }\n    const std::vector<ExplicitNameEntry>& GetExplicitNames() const { return explicit_names_; }\n\nprivate:\n    std::vector<RawTableDefV10>::iterator FindTable(const std::string& table_name) {\n        return std::find_if(tables_.begin(), tables_.end(), [&](const auto& table) { return table.source_name == table_name; });\n    }\n    void UpsertTable(const RawTableDefV10& table);\n    void UpsertLifecycleReducer(const RawLifeCycleReducerDefV10& lifecycle);\n    void UpsertReducer(const RawReducerDefV10& reducer);\n    void UpsertProcedure(const RawProcedureDefV10& procedure);\n    void UpsertView(const RawViewDefV10& view);\n    RawIndexDefV10 CreateBTreeIndex(const std::string& table_name,\n                                    const std::string& source_name,\n                                    const std::vector<uint16_t>& columns,\n                                    const std::string& accessor_name) const;\n    RawConstraintDefV10 CreateUniqueConstraint(const std::string& table_name,\n                                               const std::string& field_name,\n                                               uint16_t field_idx) const;\n    static AlgebraicType MakeUnitAlgebraicType();\n    static AlgebraicType MakeStringAlgebraicType();\n\n    std::vector<std::pair<std::string, bool>> table_is_event_;\n    std::optional<CaseConversionPolicy> case_conversion_policy_;\n    std::vector<ExplicitNameEntry> explicit_names_;\n    std::unordered_map<std::string, std::vector<RawColumnDefaultValueV10>> column_defaults_by_table_;\n    std::vector<RawTableDefV10> tables_;\n    std::vector<RawReducerDefV10> reducers_;\n    std::vector<RawProcedureDefV10> procedures_;\n    std::vector<RawViewDefV10> views_;\n    std::vector<RawScheduleDefV10> schedules_;\n    std::vector<RawLifeCycleReducerDefV10> lifecycle_reducers_;\n    std::vector<RawRowLevelSecurityDefV9> row_level_security_;\n    Typespace typespace_{};\n    std::vector<RawTypeDefV10> types_;\n};\n\nextern std::unique_ptr<V10Builder> g_v10_builder;\n\nvoid initializeV10Builder();\nV10Builder& getV10Builder();\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_V10_BUILDER_H\n\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/internal/v9_builder.h",
    "content": "#ifndef SPACETIMEDB_V9_BUILDER_H\n#define SPACETIMEDB_V9_BUILDER_H\n\n#include <memory>\n#include <vector>\n#include <optional>\n#include <typeinfo>\n#include <cstring>\n#include <tuple>\n#include \"autogen/RawModuleDefV9.g.h\"\n#include \"autogen/AlgebraicType.g.h\"\n#include \"autogen/RawTableDefV9.g.h\"\n#include \"autogen/RawReducerDefV9.g.h\"\n#include \"autogen/RawConstraintDefV9.g.h\"\n#include \"autogen/RawSequenceDefV9.g.h\"\n#include \"autogen/RawScheduleDefV9.g.h\"\n#include \"autogen/RawTypeDefV9.g.h\"\n#include \"autogen/RawIndexDefV9.g.h\"\n#include \"autogen/RawConstraintDataV9.g.h\"\n#include \"autogen/RawIndexAlgorithm.g.h\"  // Contains RawIndexAlgorithmBTreeData\n#include \"autogen/RawUniqueConstraintDataV9.g.h\"\n#include \"autogen/ProductType.g.h\"\n#include \"autogen/Lifecycle.g.h\"\n#include \"autogen/RawColumnDefaultValueV9.g.h\"\n#include \"autogen/RawMiscModuleExportV9.g.h\"\n#include \"autogen/RawViewDefV9.g.h\"\n#include \"../bsatn/bsatn.h\"\n#include \"../database.h\"  // For FieldConstraintInfo\n#include \"field_registration.h\"  // For get_table_descriptors\n#include \"module_type_registration.h\"  // For getModuleTypeRegistration\n#include \"../reducer_error.h\"  // For Outcome\n#include \"buffer_pool.h\"  // For IterBuf\n#include \"runtime_registration.h\"\n#include \"template_utils.h\"\n\nnamespace SpacetimeDB {\n\n// Forward declare fail_reducer from reducer_error.h for use in templates\nvoid fail_reducer(std::string message);\n\nnamespace Internal {\n\n// Forward declare the global V9 module accessor (defined in v9_builder.cpp)\nRawModuleDefV9& GetV9Module();\nvoid ClearV9CompatModuleState();\n\n// External global flags for circular reference detection (defined in module_type_registration.cpp)\nextern bool g_circular_ref_error;\nextern std::string g_circular_ref_type_name;\n\n/**\n * V9Builder - Builds a RawModuleDefV9 structure during module registration\n * \n * This builder now uses the unified ModuleTypeRegistration system for all type handling.\n * It focuses solely on building tables, reducers, and module structure.\n * \n * Type registration principles:\n * - Only user-defined structs/enums get registered (have entries in types array)\n * - Primitives, arrays, Options, special types are always inlined\n * - Single entry point for types: registerType() -> ModuleTypeRegistration\n */\nclass V9Builder {\npublic:\n    V9Builder();\n    \n    /**\n     * Register a type using the unified type registration system\n     * Delegates to ModuleTypeRegistration::registerType()\n     * \n     * @param bsatn_type The type to register\n     * @param explicit_name Optional explicit name for the type\n     * @param cpp_type Optional C++ type info\n     * @return AlgebraicType - either inline or Ref to registered type\n     */\n    AlgebraicType registerType(const bsatn::AlgebraicType& bsatn_type,\n                               const std::string& explicit_name = \"\",\n                               const std::type_info* cpp_type = nullptr);\n    \n    /**\n     * Register a table with all its constraints and metadata\n     * This is the main entry point from SPACETIMEDB_TABLE macro\n     * \n     * @tparam T The table struct type\n     * @param table_name The name of the table\n     * @param is_public Whether the table is public\n     */\n    template<typename T>\n    void RegisterTable(const std::string& table_name, \n                       bool is_public);\n    \n    /**\n     * Add a field constraint to a table after it has been registered\n     * This is called by FIELD_ macros to add constraints separately\n     * \n     * @tparam T The table struct type\n     * @param table_name The name of the table\n     * @param field_name The name of the field\n     * @param constraint The constraint to add\n     */\n    template<typename T>\n    void AddFieldConstraint(const std::string& table_name,\n                           const std::string& field_name,\n                           FieldConstraint constraint);\n    \n    /**\n     * Add a multi-column index to a table after it has been registered\n     * This is called by FIELD_NamedMultiColumnIndex macro\n     * \n     * @tparam T The table struct type\n     * @param table_name The name of the table\n     * @param index_name The name of the index\n     * @param field_names The field names in the index\n     */\n    template<typename T>\n    void AddMultiColumnIndex(const std::string& table_name,\n                            const std::string& index_name,\n                            const std::vector<std::string>& field_names);\n    \n    /**\n     * Add a column default value to a table after it has been registered\n     * This is called by FIELD_Default macro\n     * \n     * @tparam T The table struct type\n     * @param table_name The name of the table\n     * @param field_name The name of the field\n     * @param serialized_value The BSATN-serialized default value\n     */\n    template<typename T>\n    void AddColumnDefault(const std::string& table_name,\n                         const std::string& field_name,\n                         const std::vector<uint8_t>& serialized_value);\n    \n    /**\n     * Register a reducer function with C++20 concepts\n     * This is the main entry point from REGISTER_REDUCER macro\n     * \n     * @tparam Func The reducer function type\n     * @param reducer_name The name of the reducer\n     * @param func The reducer function pointer\n     */\n    template<typename Func>\n    void RegisterReducer(const std::string& reducer_name, Func func);\n    \n    /**\n     * Register a reducer function with explicit parameter names\n     * This overload is used when parameter names are available\n     * \n     * @tparam Func The reducer function type\n     * @param reducer_name The name of the reducer\n     * @param func The reducer function pointer\n     * @param param_names The names of the parameters (excluding ReducerContext)\n     */\n    template<typename Func>\n    void RegisterReducer(const std::string& reducer_name, Func func,\n                        const std::vector<std::string>& param_names);\n    \n    /**\n     * Register a lifecycle reducer function\n     * \n     * @tparam Func The reducer function type\n     * @param reducer_name The name of the reducer\n     * @param func The reducer function pointer\n     * @param lifecycle The lifecycle type (Init, OnConnect, OnDisconnect)\n     */\n    template<typename Func>\n    void RegisterLifecycleReducer(const std::string& reducer_name, Func func,\n                                 Lifecycle lifecycle);\n    \n    /**\n     * Register a view function\n     * \n     * Views provide read-only query capabilities with caller-specific or anonymous contexts.\n     * The template detects whether the view takes ViewContext or AnonymousViewContext.\n     * \n     * @tparam Func The view function type (must take ViewContext or AnonymousViewContext)\n     * @param view_name The name of the view\n     * @param func The view function pointer\n     * @param is_public Whether the view is publicly accessible\n     * @param param_names The names of parameters (currently empty - parameters disabled)\n     */\n    template<typename Func>\n    void RegisterView(const std::string& view_name, Func func,\n                     bool is_public,\n                     const std::vector<std::string>& param_names = {});\n    \n    /**\n     * Register a procedure function\n     * \n     * Procedures can return arbitrary values and perform computations.\n     * \n     * Procedures are always public (no access control).\n     * \n     * @tparam Func The procedure function type\n     * @param procedure_name The name of the procedure\n     * @param func The procedure function pointer\n     * @param param_names The names of parameters\n     */\n    template<typename Func>\n    void RegisterProcedure(const std::string& procedure_name,\n                          Func func,\n                          const std::vector<std::string>& param_names = {});\n    \n    /**\n     * Register a schedule for a table to automatically call a reducer\n     * when the scheduled_at field indicates it's time.\n     * \n     * @param table_name The name of the table containing scheduled data\n     * @param scheduled_at_column The column index of the ScheduleAt field (0-based)\n     * @param reducer_name The name of the reducer to call\n     */\n    void RegisterSchedule(const std::string& table_name, \n                         uint16_t scheduled_at_column,\n                         const std::string& reducer_name);\n    \n    /**\n     * Register a row level security (RLS) policy for client visibility filtering.\n     * These are collected and added to the module's row_level_security field.\n     * \n     * @param sql_query The SQL query that defines the visibility filter\n     */\n    void RegisterRowLevelSecurity(const std::string& sql_query);\n    \n    /**\n     * Add a complete V9 table definition with type registration and metadata.\n     * This method handles the complete table addition including type registration.\n     */\n    void AddV9Table(const std::string& table_name,\n                       const bsatn::AlgebraicType& table_type,\n                       const std::type_info* cpp_type,\n                       bool is_public,\n                       const std::vector<uint16_t>& primary_key,\n                       const std::vector<RawIndexDefV9>& indexes,\n                       const std::vector<RawConstraintDefV9>& constraints,\n                       const std::vector<RawSequenceDefV9>& sequences,\n                       const std::optional<RawScheduleDefV9>& schedule);\n    \n    /**\n     * Add a complete V9 reducer definition with parameter type registration.\n     * This method handles the complete reducer addition including parameter type registration.\n     */\n    void AddV9Reducer(const std::string& reducer_name,\n                         const std::vector<bsatn::AlgebraicType>& param_types,\n                         const std::vector<std::string>& param_names,\n                         const std::vector<const std::type_info*>& param_cpp_types,\n                         const std::vector<std::string>& param_type_names,\n                         std::optional<Lifecycle> lifecycle);\n    \n    /**\n     * Serialize the module definition to binary.\n     */\n    std::vector<uint8_t> serialize() const;\n    \nprivate:\n    // Store pending schedules to be applied when tables are registered\n    struct PendingSchedule {\n        std::string table_name;\n        uint16_t scheduled_at_column;\n        std::string reducer_name;\n    };\n    std::map<std::string, PendingSchedule> pending_schedules_;\n    \n    // Helper to find existing table by name in the module\n    RawTableDefV9* findTableByName(const std::string& table_name);\n    \n    /**\n     * Get field name from Product type structure.\n     */\n    std::string getFieldName(const bsatn::AlgebraicType& table_type, uint16_t column_index) const;\n    \n    /**\n     * Generate constraints for primary key.\n     */\n    std::vector<RawConstraintDefV9> generateConstraintsForPrimaryKey(\n        const std::string& table_name,\n        const bsatn::AlgebraicType& table_type,\n        const std::vector<uint16_t>& primary_key) const;\n        \n    /**\n     * Generate indexes for primary key.\n     */\n    std::vector<RawIndexDefV9> generateIndexesForPrimaryKey(\n        const std::string& table_name,\n        const bsatn::AlgebraicType& table_type,\n        const std::vector<uint16_t>& primary_key) const;\n    \n    /**\n     * Helper to create a BTree index for a field\n     */\n    RawIndexDefV9 createBTreeIndex(const std::string& table_name,\n                                   const std::string& field_name,\n                                   uint16_t field_idx) const;\n    \n    /**\n     * Helper to create a unique constraint for a field\n     */\n    RawConstraintDefV9 createUniqueConstraint(const std::string& table_name,\n                                              const std::string& field_name,\n                                              uint16_t field_idx) const;\n    \n    /**\n     * @brief Common implementation for reducer registration\n     * \n     * This helper eliminates ~80% code duplication between RegisterReducer and\n     * RegisterLifecycleReducer by consolidating the common parameter extraction,\n     * handler creation, and registration logic.\n     * \n     * @tparam Func The function pointer type of the reducer\n     * @param reducer_name Name of the reducer\n     * @param func The reducer function\n     * @param param_names Optional parameter names  \n     * @param lifecycle Optional lifecycle type (nullopt for regular reducers)\n     */\n    template<typename Func>\n    void RegisterReducerCommon(const std::string& reducer_name, \n                               Func func,\n                               const std::vector<std::string>& param_names,\n                               std::optional<Lifecycle> lifecycle);\n};\n\n// Template implementation for RegisterTable\ntemplate<typename T>\nvoid V9Builder::RegisterTable(const std::string& table_name, \n                              bool is_public) {\n    // RegisterTable implementation\n    \n    // First, ensure field registration happens\n    SpacetimeDB::field_registrar<T>::register_fields();\n    \n    // Check if circular reference was detected during field registration\n    if (g_circular_ref_error) {\n        // Circular reference detected - don't register the table\n        // preinit_99 will handle creating the error module\n        fprintf(stdout, \"DEBUG: Circular reference detected in table '%s', skipping registration\\n\", \n                table_name.c_str());\n        return;\n    }\n    \n    // Get field descriptors for the table type\n    auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n    auto it = descriptor_map.find(&typeid(T));\n    if (it == descriptor_map.end()) {\n        // No descriptors registered for this type - this shouldn't happen if SPACETIMEDB_STRUCT was used\n        return;\n    }\n    const auto& field_descs = it->second.fields;\n    \n    // Build a vector of BSATN ProductTypeElements\n    std::vector<bsatn::ProductTypeElement> elements;\n    \n    // First collect all field types with their names and register enum types\n    for (const auto& field_desc : field_descs) {\n        bsatn::AlgebraicType field_type = field_desc.get_algebraic_type();\n        std::string field_type_name = field_desc.get_type_name ? field_desc.get_type_name() : \"\";\n        \n        // For enum types (Sum types), register them by name first\n        // This ensures they get proper type names in the V9 system\n        if (!field_type_name.empty() && field_type.tag() == bsatn::AlgebraicTypeTag::Sum) {\n            // Check if it's not an Option type (which should be inlined)\n            const auto& sum = field_type.as_sum();\n            bool is_option = (sum.variants.size() == 2 && \n                             sum.variants[0].name == \"some\" && \n                             sum.variants[1].name == \"none\");\n            \n            // Check if it's ScheduleAt (which should be inlined)\n            bool is_schedule_at = (sum.variants.size() == 2 && \n                                  sum.variants[0].name == \"Interval\" && \n                                  sum.variants[1].name == \"Time\");\n            \n            // Check if it's a Result type (which should be inlined)\n            bool is_result = (sum.variants.size() == 2 && \n                             sum.variants[0].name == \"ok\" && \n                             sum.variants[1].name == \"err\");\n            \n            if (!is_option && !is_schedule_at && !is_result) {\n                // This is a user-defined enum, register it by name\n                // Strip namespace from type name if present\n                size_t last_colon = field_type_name.rfind(\"::\");\n                if (last_colon != std::string::npos) {\n                    field_type_name = field_type_name.substr(last_colon + 2);\n                }\n                //fprintf(stdout, \"DEBUG: Registering enum type '%s' for field '%s'\\n\", \n                //        field_type_name.c_str(), field_desc.name.c_str());\n                getModuleTypeRegistration().registerTypeByName(field_type_name, field_type, nullptr);\n            }\n        }\n        \n        elements.emplace_back(\n            std::make_optional(field_desc.name),\n            std::move(field_type)\n        );\n    }\n    \n    // Create the BSATN Product type with the elements\n    bsatn::ProductType bsatn_product(std::move(elements));\n    \n    // Create the BSATN AlgebraicType with Product type\n    bsatn::AlgebraicType table_type = bsatn::AlgebraicType::make_product(\n        std::make_unique<bsatn::ProductType>(std::move(bsatn_product)));\n    \n    // Process constraints to create V9 structures\n    std::vector<uint16_t> primary_key;\n    std::vector<RawIndexDefV9> indexes;\n    std::vector<RawConstraintDefV9> v9_constraints;\n    std::vector<RawSequenceDefV9> sequences;\n    \n    // Constraints and indexes will be added later by FIELD_ macros via AddFieldConstraint\n    // For now, start with empty vectors\n    \n    // Check if there's a pending schedule for this table\n    std::optional<RawScheduleDefV9> schedule = std::nullopt;\n    auto schedule_it = pending_schedules_.find(table_name);\n    if (schedule_it != pending_schedules_.end()) {\n        RawScheduleDefV9 schedule_def;\n        schedule_def.scheduled_at_column = schedule_it->second.scheduled_at_column;\n        schedule_def.reducer_name = schedule_it->second.reducer_name;\n        schedule = schedule_def;\n        \n        // Remove the pending schedule since we've used it\n        pending_schedules_.erase(schedule_it);\n    }\n    \n    // Add the complete V9 table definition\n    AddV9Table(table_name, table_type, &typeid(T), is_public,\n               primary_key, indexes, v9_constraints, sequences, schedule);\n}\n\n// Template implementation for AddFieldConstraint\ntemplate<typename T>\nvoid V9Builder::AddFieldConstraint(const std::string& table_name,\n                                   const std::string& field_name,\n                                   FieldConstraint constraint) {\n    // AddFieldConstraint implementation\n    \n    // Find the existing table by name\n    RawTableDefV9* table = findTableByName(table_name);\n    if (!table) {\n        fprintf(stderr, \"ERROR: Table '%s' not found when trying to add constraint to field '%s'\\n\",\n                table_name.c_str(), field_name.c_str());\n        return;\n    }\n    \n    // Get field descriptors to find the field index\n    SpacetimeDB::field_registrar<T>::register_fields();\n    auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n    auto it = descriptor_map.find(&typeid(T));\n    if (it == descriptor_map.end()) {\n        fprintf(stderr, \"ERROR: No field descriptors found for table %s\\n\", table_name.c_str());\n        return;\n    }\n    \n    const auto& field_descs = it->second.fields;\n    uint16_t field_idx = 0;\n    bool field_found = false;\n    \n    // Find the field index\n    for (const auto& field_desc : field_descs) {\n        if (field_desc.name == field_name) {\n            field_found = true;\n            break;\n        }\n        field_idx++;\n    }\n    \n    if (!field_found) {\n        fprintf(stderr, \"ERROR: Field '%s' not found in table '%s'\\n\", \n                field_name.c_str(), table_name.c_str());\n        return;\n    }\n    \n    // Add constraint based on type\n    int constraint_bits = static_cast<int>(constraint);\n    \n    // Check for PrimaryKey (has specific bit 0b1000)\n    if (constraint_bits & 0b1000) {  // PrimaryKey-specific bit\n        // Validate that there isn't already a primary key\n        if (!table->primary_key.empty()) {\n            // Set the error flag instead of crashing - this will be handled by preinit_99\n            SetMultiplePrimaryKeyError(table_name);\n            return; // Exit early to avoid adding the conflicting primary key\n        }\n        table->primary_key.push_back(field_idx);\n        \n        // PrimaryKey implies Unique constraint and index\n        table->constraints.push_back(createUniqueConstraint(table_name, field_name, field_idx));\n        table->indexes.push_back(createBTreeIndex(table_name, field_name, field_idx));\n        \n        //fprintf(stdout, \"DEBUG: Added PrimaryKey constraint and index for %s.%s\\n\", \n        //        table_name.c_str(), field_name.c_str());\n    }\n    // Check for Unique (has bit 0b0100, but not PrimaryKey)\n    else if ((constraint_bits & 0b0100) && !(constraint_bits & 0b1000)) {\n        table->constraints.push_back(createUniqueConstraint(table_name, field_name, field_idx));\n        table->indexes.push_back(createBTreeIndex(table_name, field_name, field_idx));\n        \n        //fprintf(stdout, \"DEBUG: Added Unique constraint and index for %s.%s\\n\", \n        //        table_name.c_str(), field_name.c_str());\n    }\n    // Check for plain Index (has bit 0b0001, but not Unique or PrimaryKey bits)\n    else if ((constraint_bits & 0b0001) && !(constraint_bits & 0b1100)) {\n        // Just create an index, no constraint\n        table->indexes.push_back(createBTreeIndex(table_name, field_name, field_idx));\n        \n        //fprintf(stdout, \"DEBUG: Added Index for %s.%s\\n\", \n        //        table_name.c_str(), field_name.c_str());\n    }\n    \n    // Check for AutoInc\n    if (constraint_bits & static_cast<int>(FieldConstraint::AutoInc)) {\n        RawSequenceDefV9 seq_def;\n        seq_def.name = table_name + \"_\" + field_name + \"_seq\";\n        seq_def.column = field_idx;\n        seq_def.start = std::nullopt;\n        seq_def.increment = SpacetimeDB::I128(1);\n        seq_def.min_value = std::nullopt;\n        seq_def.max_value = std::nullopt;\n        table->sequences.push_back(std::move(seq_def));\n        \n        //fprintf(stdout, \"DEBUG: Added AutoInc sequence for %s.%s\\n\", \n        //        table_name.c_str(), field_name.c_str());\n    }\n}\n\n// Template implementation for AddMultiColumnIndex\ntemplate<typename T>\nvoid V9Builder::AddMultiColumnIndex(const std::string& table_name,\n                                    const std::string& index_name,\n                                    const std::vector<std::string>& field_names) {\n    //fprintf(stdout, \"DEBUG: Adding multi-column index '%s' to table '%s' with %zu fields\\n\", \n    //        index_name.c_str(), table_name.c_str(), field_names.size());\n    \n    // Find the existing table\n    RawTableDefV9* table = findTableByName(table_name);\n    if (!table) {\n        fprintf(stderr, \"ERROR: Table '%s' not found for multi-column index '%s'\\n\",\n                table_name.c_str(), index_name.c_str());\n        return;\n    }\n    \n    // Get field descriptors to find the field index\n    SpacetimeDB::field_registrar<T>::register_fields();\n    auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n    auto it = descriptor_map.find(&typeid(T));\n    if (it == descriptor_map.end()) {\n        fprintf(stderr, \"ERROR: No field descriptors found for table %s\\n\", table_name.c_str());\n        return;\n    }\n    \n    const auto& field_descs = it->second.fields;\n    std::vector<uint16_t> field_indexes;\n    \n    // Find field indices for each field name\n    for (const std::string& field_name : field_names) {\n        uint16_t field_idx = 0;\n        bool field_found = false;\n        \n        for (const auto& field_desc : field_descs) {\n            if (field_desc.name == field_name) {\n                field_indexes.push_back(field_idx);\n                field_found = true;\n                //fprintf(stdout, \"DEBUG: Field '%s' -> index %u\\n\", field_name.c_str(), field_idx);\n                break;\n            }\n            field_idx++;\n        }\n        \n        if (!field_found) {\n            fprintf(stderr, \"ERROR: Field '%s' not found in table '%s' for multi-column index\\n\",\n                    field_name.c_str(), table_name.c_str());\n            return;\n        }\n    }\n    \n    // Create the multi-column BTree algorithm\n    RawIndexAlgorithmBTreeData btree_data;\n    btree_data.columns = field_indexes;\n    \n    RawIndexAlgorithm algorithm;\n    algorithm.set<0>(btree_data);  // Set BTree variant\n    \n    // Create the index definition with both the user-provided name and generated btree name\n    RawIndexDefV9 index_def;\n    std::string generated_name = table_name + \"_\" + field_names[0];\n    for (size_t i = 1; i < field_names.size(); ++i) {\n        generated_name += \"_\" + field_names[i];\n    }\n    generated_name += \"_idx_btree\";  // Generated btree index name\n    index_def.name = generated_name;\n    index_def.accessor_name = index_name;  // User-provided index name for access\n    index_def.algorithm = algorithm;\n    \n    // Add to table's indexes\n    table->indexes.push_back(std::move(index_def));\n    \n    //fprintf(stdout, \"DEBUG: Successfully added multi-column index '%s' -> '%s' with %zu fields\\n\",\n    //        index_name.c_str(), generated_name.c_str(), field_indexes.size());\n}\n\n// Template implementation for AddColumnDefault\ntemplate<typename T>\nvoid V9Builder::AddColumnDefault(const std::string& table_name,\n                                 const std::string& field_name,\n                                 const std::vector<uint8_t>& serialized_value) {\n    // Find the existing table\n    RawTableDefV9* table = findTableByName(table_name);\n    if (!table) {\n        fprintf(stderr, \"ERROR: Table '%s' not found for default value on field '%s'\\n\",\n                table_name.c_str(), field_name.c_str());\n        return;\n    }\n    \n    // Get field descriptors to find the field index\n    SpacetimeDB::field_registrar<T>::register_fields();\n    auto& descriptor_map = SpacetimeDB::get_table_descriptors();\n    auto it = descriptor_map.find(&typeid(T));\n    if (it == descriptor_map.end()) {\n        fprintf(stderr, \"ERROR: No field descriptors found for table %s\\n\", table_name.c_str());\n        return;\n    }\n    \n    const auto& field_descs = it->second.fields;\n    uint16_t field_idx = 0;\n    bool field_found = false;\n    \n    // Find the field index\n    for (const auto& field_desc : field_descs) {\n        if (field_desc.name == field_name) {\n            field_found = true;\n            break;\n        }\n        field_idx++;\n    }\n    \n    if (!field_found) {\n        fprintf(stderr, \"ERROR: Field '%s' not found in table '%s'\\n\",\n                field_name.c_str(), table_name.c_str());\n        return;\n    }\n    \n    // Validate: default values cannot be used with primary_key, unique, or auto_inc\n    // Check if this column is in the primary key\n    for (uint16_t pk_col : table->primary_key) {\n        if (pk_col == field_idx) {\n            std::string error_msg = \"ERROR: Field \" + table_name + \".\" + field_name + \n                        \" has primary_key constraint - cannot have default value\";\n            fprintf(stderr, \"%s\", error_msg.c_str());\n            // fprintf(stderr, \"ERROR:  has primary_key constraint - cannot have default value\",\n            //          table_name.c_str(), field_name.c_str());\n            return;\n        }\n    }\n    \n    // Check if this column has a unique constraint\n    for (const auto& constraint : table->constraints) {\n        if (constraint.data.get_tag() == 0) {  // Unique constraint variant\n            const auto& unique_data = constraint.data.get<0>();\n            if (unique_data.columns.size() == 1 && unique_data.columns[0] == field_idx) {\n                fprintf(stderr, \"ERROR: Field %s.%s has unique constraint - cannot have default value\",\n                        table_name.c_str(), field_name.c_str());\n                return;\n            }\n        }\n    }\n    \n    // Check if this column has an auto_inc sequence\n    for (const auto& sequence : table->sequences) {\n        if (sequence.column == field_idx) {\n            fprintf(stderr, \"ERROR: Field %s.%s has auto_inc constraint - cannot have default value\",\n                    table_name.c_str(), field_name.c_str());\n            return;\n        }\n    }\n    \n    // Create the column default value structure\n    RawColumnDefaultValueV9 col_default;\n    col_default.table = table_name;\n    col_default.col_id = field_idx;\n    col_default.value = serialized_value;\n    \n    // Create the misc export entry with ColumnDefaultValue variant (variant 0)\n    RawMiscModuleExportV9 export_entry;\n    export_entry.set<0>(col_default);\n    \n    // Add to the module's misc_exports\n    GetV9Module().misc_exports.push_back(export_entry);\n}\n\n// Helper to extract T from Outcome<T>\ntemplate<typename T>\nstruct outcome_inner_type;\n\ntemplate<typename T>\nstruct outcome_inner_type<Outcome<T>> {\n    using type = T;\n};\n\n\n// Template implementation for RegisterReducerCommon - shared logic\ntemplate<typename Func>\nvoid V9Builder::RegisterReducerCommon(const std::string& reducer_name, \n                                      Func func,\n                                      const std::vector<std::string>& param_names,\n                                      std::optional<Lifecycle> lifecycle) {\n    // Skip reducer registration if circular reference was detected\n    if (g_circular_ref_error) {\n        fprintf(stdout, \"DEBUG: Skipping reducer '%s' registration due to circular reference error\\n\", \n                reducer_name.c_str());\n        return;\n    }\n    \n    using traits = function_traits<Func>;\n    \n    // Validate that the reducer has at least one parameter (ReducerContext)\n    static_assert(traits::arity > 0, \n        \"Reducer must have at least one parameter (ReducerContext)\");\n    \n    // Only validate the first parameter type if we have parameters\n    // This prevents template instantiation errors when arity is 0\n    if constexpr (traits::arity > 0) {\n        using FirstParamType = std::remove_cv_t<std::remove_reference_t<\n            typename traits::template arg_t<0>>>;\n        \n        static_assert(std::is_same_v<FirstParamType, ReducerContext>,\n            \"First parameter of reducer must be ReducerContext\");\n    }\n    \n    // Build vectors of parameter information\n    std::vector<bsatn::AlgebraicType> param_types;\n    std::vector<const std::type_info*> param_cpp_types;\n    std::vector<std::string> param_type_names;\n    \n    // Extract parameter types (skip ReducerContext at index 0)\n    if constexpr (traits::arity > 1) {\n        []<std::size_t... Is>(std::index_sequence<Is...>, \n                              std::vector<bsatn::AlgebraicType>& types,\n                              std::vector<const std::type_info*>& cpp_types) {\n            (([]<std::size_t I>(std::vector<bsatn::AlgebraicType>& types_inner,\n                                std::vector<const std::type_info*>& cpp_types_inner) {\n                if constexpr (I > 0) {  // Skip the first parameter (ReducerContext)\n                    using param_type = typename traits::template arg_t<I>;\n                    types_inner.push_back(bsatn::bsatn_traits<param_type>::algebraic_type());\n                    cpp_types_inner.push_back(&typeid(param_type));\n                }\n            }.template operator()<Is>(types, cpp_types)), ...);\n        }(std::make_index_sequence<traits::arity>{}, param_types, param_cpp_types);\n    }\n    \n    // Create the handler wrapper\n    std::function<void(ReducerContext&, BytesSource)> handler;\n    \n    if constexpr (traits::arity == 1) {\n        // Only ReducerContext parameter\n        handler = [func](ReducerContext& ctx, BytesSource) {\n            // Call the reducer and check the result\n            auto result = func(ctx);\n            if (result.is_err()) {\n                // Reducer returned an error - store it for the caller\n                fail_reducer(result.error());\n            }\n        };\n    } else {\n        // Has additional parameters\n        handler = [func](ReducerContext& ctx, BytesSource args_source) {\n            std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n            \n            []<std::size_t... Js>(std::index_sequence<Js...>, \n                                  Func fn,\n                                  ReducerContext& ctx_inner,\n                                  const std::vector<uint8_t>& bytes) {\n                if constexpr (sizeof...(Js) > 0) {\n                    bsatn::Reader reader(bytes.data(), bytes.size());\n                    auto args = std::make_tuple(\n                        bsatn::deserialize<typename traits::template arg_t<Js + 1>>(reader)...\n                    );\n                    \n                    std::apply([&ctx_inner, fn](auto&&... args) {\n                        // Call the reducer and check the result\n                        auto result = fn(ctx_inner, std::forward<decltype(args)>(args)...);\n                        if (result.is_err()) {\n                            // Reducer returned an error - store it for the caller\n                            fail_reducer(result.error());\n                        }\n                    }, args);\n                }\n            }(std::make_index_sequence<traits::arity - 1>{}, func, ctx, args_bytes);\n        };\n    }\n    \n    // Generate parameter names if not provided\n    std::vector<std::string> actual_param_names = param_names;\n    if (actual_param_names.size() < param_types.size()) {\n        actual_param_names.resize(param_types.size());\n        for (size_t i = param_names.size(); i < param_types.size(); ++i) {\n            actual_param_names[i] = \"arg\" + std::to_string(i);\n        }\n    }\n    \n    // Add the complete V9 reducer definition\n    AddV9Reducer(reducer_name, param_types, actual_param_names, \n                 param_cpp_types, param_type_names, lifecycle);\n    \n    // Register the handler for runtime dispatch\n    RegisterReducerHandler(reducer_name, handler, lifecycle);\n}\n\n// Template implementation for RegisterReducer using C++20 features\ntemplate<typename Func>\nvoid V9Builder::RegisterReducer(const std::string& reducer_name, Func func) {\n    // Call the overload with empty parameter names (for backwards compatibility)\n    RegisterReducer(reducer_name, func, std::vector<std::string>{});\n}\n\n// Template implementation for RegisterReducer with explicit parameter names\ntemplate<typename Func>\nvoid V9Builder::RegisterReducer(const std::string& reducer_name, Func func,\n                                const std::vector<std::string>& param_names) {\n    // Use the common helper function\n    RegisterReducerCommon(reducer_name, func, param_names, std::nullopt);\n}\n\n// Template implementation for RegisterLifecycleReducer \ntemplate<typename Func>\nvoid V9Builder::RegisterLifecycleReducer(const std::string& reducer_name, Func func,\n                                         Lifecycle lifecycle) {\n    // Generate default parameter names and use the common helper\n    std::vector<std::string> empty_names;\n    RegisterReducerCommon(reducer_name, func, empty_names, lifecycle);\n}\n\n// Template implementation for RegisterView\ntemplate<typename Func>\nvoid V9Builder::RegisterView(const std::string& view_name, Func func,\n                             bool is_public,\n                             const std::vector<std::string>& param_names) {\n    // TODO: Remove this when parameters are supported - param_names will be used\n    // Parameters are currently disabled - suppress warning\n    (void)param_names;\n    \n    // Skip view registration if circular reference was detected\n    if (g_circular_ref_error) {\n        fprintf(stdout, \"DEBUG: Skipping view '%s' registration due to circular reference error\\n\", \n                view_name.c_str());\n        return;\n    }\n    \n    using traits = function_traits<Func>;\n    \n    // Validate that the view has at least one parameter (ViewContext or AnonymousViewContext)\n    static_assert(traits::arity > 0, \n        \"View must have at least one parameter (ViewContext or AnonymousViewContext)\");\n    \n    // Determine the context type and register accordingly\n    if constexpr (traits::arity > 0) {\n        using ContextType = std::remove_cv_t<std::remove_reference_t<\n            typename traits::template arg_t<0>>>;\n        \n        // Extract return type\n        using ReturnType = typename traits::result_type;\n        \n        // Build the AlgebraicType for the return type\n        auto& type_reg = getModuleTypeRegistration();\n        bsatn::AlgebraicType bsatn_return_type = bsatn::algebraic_type_of<ReturnType>::get();\n        AlgebraicType return_algebraic_type = type_reg.registerType(bsatn_return_type, \"\", &typeid(ReturnType));\n        \n        // TODO: When parameters are supported, extract parameter types and build ProductType:\n        // Build params (empty for now since parameters are disabled)\n        // std::vector<ProductTypeElement> param_elements;\n        // if constexpr (traits::arity > 1) {\n        //     []<std::size_t... Is>(std::index_sequence<Is...>, \n        //                           std::vector<ProductTypeElement>& elements,\n        //                           const std::vector<std::string>& names,\n        //                           ModuleTypeRegistration& type_reg_inner) {\n        //         (([]<std::size_t I>(std::vector<ProductTypeElement>& elems,\n        //                             const std::vector<std::string>& n,\n        //                             ModuleTypeRegistration& tr) {\n        //             if constexpr (I > 0) {  // Skip the first parameter (ViewContext/AnonymousViewContext)\n        //                 using param_type = typename traits::template arg_t<I>;\n        //                 bsatn::AlgebraicType param_bsatn = bsatn::algebraic_type_of<param_type>::get();\n        //                 AlgebraicType param_alg = tr.registerType(param_bsatn, \"\", &typeid(param_type));\n        //                 std::string param_name = (I-1 < n.size()) ? n[I-1] : (\"arg\" + std::to_string(I-1));\n        //                 elems.emplace_back(std::make_optional(param_name), std::move(param_alg));\n        //             }\n        //         }.template operator()<Is>(elements, names, type_reg_inner)), ...);\n        //     }(std::make_index_sequence<traits::arity>{}, param_elements, param_names, type_reg);\n        // }\n        // ProductType params(std::move(param_elements));\n        ProductType params;\n        \n        if constexpr (std::is_same_v<ContextType, ViewContext>) {\n            // Register with ViewContext (has sender)\n            std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler =\n                [func](ViewContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n                    // TODO: When parameters are supported, deserialize args_source:\n                    // For now, views don't have parameters (args_source is unused)\n                    // std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n                    // bsatn::Reader reader(args_bytes.data(), args_bytes.size());\n                    // auto args = std::make_tuple(\n                    //     bsatn::deserialize<typename traits::template arg_t<1>>(reader),\n                    //     bsatn::deserialize<typename traits::template arg_t<2>>(reader),\n                    //     ...\n                    // );\n                    // auto result = std::apply([&ctx, func](auto&&... args) {\n                    //     return func(ctx, std::forward<decltype(args)>(args)...);\n                    // }, args);\n                    (void)args_source;\n                    \n                    // Call the view function - returns raw type directly\n                    auto result = func(ctx);\n                    \n                    // Convert result to vector format (Option<T> -> Vec<T>)\n                    auto result_vec = view_result_to_vec(std::move(result));\n                    \n                    // Serialize using pooled buffer\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result_vec);\n                    }  // Destroy Writer before releasing buffer\n                    return buf.release();\n                };\n            \n            RegisterViewHandler(view_name, handler);\n            \n            // Add view definition to module's misc_exports\n            uint32_t view_index = static_cast<uint32_t>(GetViewHandlerCount());\n            RawViewDefV9 view_def{\n                view_name,\n                view_index - 1,  // Index is 0-based, we just added one\n                is_public,  // is_public (from parameter)\n                false,  // is_anonymous\n                params,\n                return_algebraic_type\n            };\n            RawMiscModuleExportV9 export_entry;\n            export_entry.set<2>(view_def);  // Index 2 = View variant\n            GetV9Module().misc_exports.push_back(export_entry);\n            \n        } else if constexpr (std::is_same_v<ContextType, AnonymousViewContext>) {\n            // Register with AnonymousViewContext (no sender)\n            std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler =\n                [func](AnonymousViewContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n                    // TODO: When parameters are supported, deserialize args_source (same pattern as ViewContext above)\n                    // For now, views don't have parameters (args_source is unused)\n                    (void)args_source;\n                    \n                    // Call the view function - returns raw type directly\n                    auto result = func(ctx);\n                    \n                    // Convert result to vector format (Option<T> -> Vec<T>)\n                    auto result_vec = view_result_to_vec(std::move(result));\n                    \n                    // Serialize using pooled buffer\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result_vec);\n                    }  // Destroy Writer before releasing buffer\n                    return buf.release();\n                };\n            \n            RegisterAnonymousViewHandler(view_name, handler);\n            \n            // Add view definition to module's misc_exports\n            uint32_t view_index = static_cast<uint32_t>(GetAnonymousViewHandlerCount());\n            RawViewDefV9 view_def{\n                view_name,\n                view_index - 1,  // Index is 0-based, we just added one\n                is_public,  // is_public (from parameter)\n                true,  // is_anonymous\n                params,\n                return_algebraic_type\n            };\n            RawMiscModuleExportV9 export_entry;\n            export_entry.set<2>(view_def);  // Index 2 = View variant\n            GetV9Module().misc_exports.push_back(export_entry);\n            \n        } else {\n            static_assert(std::is_same_v<ContextType, ViewContext> || \n                         std::is_same_v<ContextType, AnonymousViewContext>,\n                \"First parameter of view must be ViewContext or AnonymousViewContext\");\n        }\n    }\n}\n\n// Template implementation for RegisterProcedure\ntemplate<typename Func>\nvoid V9Builder::RegisterProcedure(const std::string& procedure_name,\n                                  Func func,\n                                  const std::vector<std::string>& param_names) {\n    // Skip procedure registration if circular reference was detected\n    if (g_circular_ref_error) {\n        fprintf(stdout, \"DEBUG: Skipping procedure '%s' registration due to circular reference error\\n\", \n                procedure_name.c_str());\n        return;\n    }\n    \n    using traits = function_traits<Func>;\n    \n    // Validate that the procedure has at least one parameter (ProcedureContext)\n    static_assert(traits::arity > 0, \n        \"Procedure must have at least one parameter (ProcedureContext)\");\n    \n    // Validate first parameter is ProcedureContext\n    if constexpr (traits::arity > 0) {\n        using FirstParamType = std::remove_cv_t<std::remove_reference_t<\n            typename traits::template arg_t<0>>>;\n        \n        static_assert(std::is_same_v<FirstParamType, ProcedureContext>,\n            \"First parameter of procedure must be ProcedureContext\");\n    }\n    \n    // Procedures return raw T\n    using ReturnType = typename traits::result_type;\n    \n    // Build the AlgebraicType for the return type\n    auto& type_reg = getModuleTypeRegistration();\n    bsatn::AlgebraicType bsatn_return_type = bsatn::algebraic_type_of<ReturnType>::get();\n    AlgebraicType return_algebraic_type = type_reg.registerType(bsatn_return_type, \"\", &typeid(ReturnType));\n    \n    // Build parameter types (skip ProcedureContext at index 0)\n    std::vector<ProductTypeElement> param_elements;\n    if constexpr (traits::arity > 1) {\n        []<std::size_t... Is>(std::index_sequence<Is...>, \n                              std::vector<ProductTypeElement>& elements,\n                              const std::vector<std::string>& names,\n                              ModuleTypeRegistration& type_reg_inner) {\n            (([]<std::size_t I>(std::vector<ProductTypeElement>& elems,\n                                const std::vector<std::string>& n,\n                                ModuleTypeRegistration& tr) {\n                if constexpr (I > 0) {  // Skip the first parameter (ProcedureContext)\n                    using param_type = typename traits::template arg_t<I>;\n                    bsatn::AlgebraicType param_bsatn = bsatn::algebraic_type_of<param_type>::get();\n                    AlgebraicType param_alg = tr.registerType(param_bsatn, \"\", &typeid(param_type));\n                    std::string param_name = (I-1 < n.size()) ? n[I-1] : (\"arg\" + std::to_string(I-1));\n                    elems.emplace_back(std::make_optional(param_name), std::move(param_alg));\n                }\n            }.template operator()<Is>(elements, names, type_reg_inner)), ...);\n        }(std::make_index_sequence<traits::arity>{}, param_elements, param_names, type_reg);\n    }\n    ProductType params(std::move(param_elements));\n    \n    // Create handler that wraps the procedure function\n    std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler;\n    \n    if constexpr (traits::arity == 1) {\n        // Only ProcedureContext parameter\n        handler = [func](ProcedureContext& ctx, BytesSource) -> std::vector<uint8_t> {\n            // Procedures return raw T\n            // Use LOG_PANIC() for errors - procedures cannot return errors gracefully\n            auto result = func(ctx);\n            \n            // Serialize using pooled buffer\n            IterBuf buf = IterBuf::take();\n            {\n                bsatn::Writer writer(buf.get());\n                bsatn::serialize(writer, result);\n            }  // Destroy Writer before releasing buffer\n            return buf.release();\n        };\n    } else {\n        // Has additional parameters\n        handler = [func](ProcedureContext& ctx, BytesSource args_source) -> std::vector<uint8_t> {\n            std::vector<uint8_t> args_bytes = ConsumeBytes(args_source);\n            \n            return []<std::size_t... Js>(std::index_sequence<Js...>, \n                                        Func fn,\n                                        ProcedureContext& ctx_inner,\n                                        const std::vector<uint8_t>& bytes) -> std::vector<uint8_t> {\n                if constexpr (sizeof...(Js) > 0) {\n                    bsatn::Reader reader(bytes.data(), bytes.size());\n                    auto args = std::make_tuple(\n                        bsatn::deserialize<typename traits::template arg_t<Js + 1>>(reader)...\n                    );\n                    \n                    // Procedures return raw T\n                    // Use LOG_PANIC() for errors - procedures cannot return errors gracefully\n                    auto result = std::apply([&ctx_inner, fn](auto&&... args) {\n                        return fn(ctx_inner, std::forward<decltype(args)>(args)...);\n                    }, args);\n                    \n                    // Serialize using pooled buffer\n                    IterBuf buf = IterBuf::take();\n                    {\n                        bsatn::Writer writer(buf.get());\n                        bsatn::serialize(writer, result);\n                    }  // Destroy Writer before releasing buffer\n                    return buf.release();\n                } else {\n                    return std::vector<uint8_t>();\n                }\n            }(std::make_index_sequence<traits::arity - 1>{}, func, ctx, args_bytes);\n        };\n    }\n    \n    RegisterProcedureHandler(procedure_name, handler);\n    \n    // Add procedure definition to module's misc_exports\n    RawProcedureDefV9 procedure_def{\n        procedure_name,\n        params,\n        return_algebraic_type\n    };\n    RawMiscModuleExportV9 export_entry;\n    export_entry.set<1>(procedure_def);  // Index 1 = Procedure variant (0=ColumnDefaultValue, 1=Procedure, 2=View)\n    GetV9Module().misc_exports.push_back(export_entry);\n}\n\n// Global V9Builder instance for the module\nextern std::unique_ptr<V9Builder> g_v9_builder;\n\n// Initialize the V9 builder (called once at module startup)\nvoid initializeV9Builder();\n\n// Get the global V9 builder\nV9Builder& getV9Builder();\n\n// =============================================================================\n// Helper Functions (used internally by RegisterTable and RegisterReducer)\n// =============================================================================\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_V9_BUILDER_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/jwt_claims.h",
    "content": "#ifndef SPACETIMEDB_JWT_CLAIMS_H\n#define SPACETIMEDB_JWT_CLAIMS_H\n\n#include \"spacetimedb/bsatn/types.h\"\n#include <string>\n#include <vector>\n#include <memory>\n#include <optional>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Represents the claims from a JSON Web Token (JWT).\n * \n * This class provides lazy parsing of JWT claims, parsing specific fields\n * on demand. It follows the same pattern as the Rust and C# implementations.\n * \n * The Identity is provided in the constructor because computing it requires\n * Blake3 hashing, which is done on the host side.\n */\nclass JwtClaims {\nprivate:\n    std::string payload_;\n    Identity identity_;\n    mutable std::optional<std::string> subject_;\n    mutable std::optional<std::string> issuer_;\n    mutable std::optional<std::vector<std::string>> audience_;\n\n    // Helper to extract a string claim from JSON\n    static std::optional<std::string> extract_string_claim(const std::string& json, const std::string& key);\n    \n    // Helper to extract the audience claim (can be string or array)\n    static std::vector<std::string> extract_audience_claim(const std::string& json);\n\npublic:\n    /**\n     * @brief Constructs a JwtClaims from a JWT payload and its associated Identity.\n     * \n     * The Identity must be provided because computing it requires Blake3 hashing,\n     * which is performed on the host side.\n     * \n     * @param jwt_payload The raw JWT payload (JSON claims)\n     * @param identity The identity derived from the JWT's issuer and subject\n     */\n    JwtClaims(std::string jwt_payload, Identity identity);\n\n    /**\n     * @brief Returns the token's subject from the 'sub' claim.\n     * \n     * @return The subject string, or empty string if missing/invalid\n     */\n    const std::string& subject() const;\n\n    /**\n     * @brief Returns the issuer for these credentials from the 'iss' claim.\n     * \n     * @return The issuer string, or empty string if missing/invalid\n     */\n    const std::string& issuer() const;\n\n    /**\n     * @brief Returns the audience for these credentials from the 'aud' claim.\n     * \n     * The audience can be either a single string or an array of strings.\n     * This method returns a vector that will contain either 0, 1, or multiple strings.\n     * \n     * @return A vector of audience strings\n     */\n    const std::vector<std::string>& audience() const;\n\n    /**\n     * @brief Returns the identity for these credentials.\n     * \n     * The identity is based on the 'iss' and 'sub' claims and is computed\n     * using Blake3 hashing on the host side.\n     * \n     * @return The identity\n     */\n    const Identity& get_identity() const { return identity_; }\n\n    /**\n     * @brief Returns the whole JWT payload as a JSON string.\n     * \n     * @return The raw JWT payload\n     */\n    const std::string& raw_payload() const { return payload_; }\n};\n\n// ============================================================================\n// INLINE IMPLEMENTATIONS\n// ============================================================================\n\n// Simple JSON string extraction helper\n// Finds \"key\":\"value\" and returns the value\ninline std::optional<std::string> JwtClaims::extract_string_claim(const std::string& json, const std::string& key) {\n    // Look for \"key\":\n    std::string search = \"\\\"\" + key + \"\\\":\";\n    size_t pos = json.find(search);\n    if (pos == std::string::npos) {\n        return std::nullopt;\n    }\n    \n    pos += search.length();\n    \n    // Skip whitespace\n    while (pos < json.length() && (json[pos] == ' ' || json[pos] == '\\t' || json[pos] == '\\n')) {\n        pos++;\n    }\n    \n    // Check if the value is a string (starts with \")\n    if (pos >= json.length() || json[pos] != '\"') {\n        return std::nullopt;\n    }\n    \n    pos++; // Skip opening quote\n    size_t end_pos = pos;\n    \n    // Find closing quote, handling escaped quotes\n    while (end_pos < json.length()) {\n        if (json[end_pos] == '\"' && (end_pos == pos || json[end_pos - 1] != '\\\\')) {\n            break;\n        }\n        end_pos++;\n    }\n    \n    if (end_pos >= json.length()) {\n        return std::nullopt;\n    }\n    \n    return json.substr(pos, end_pos - pos);\n}\n\n// Extract audience claim - can be string or array\ninline std::vector<std::string> JwtClaims::extract_audience_claim(const std::string& json) {\n    std::vector<std::string> result;\n    \n    // Look for \"aud\":\n    std::string search = \"\\\"aud\\\":\";\n    size_t pos = json.find(search);\n    if (pos == std::string::npos) {\n        return result; // No audience claim\n    }\n    \n    pos += search.length();\n    \n    // Skip whitespace\n    while (pos < json.length() && (json[pos] == ' ' || json[pos] == '\\t' || json[pos] == '\\n')) {\n        pos++;\n    }\n    \n    if (pos >= json.length()) {\n        return result;\n    }\n    \n    // Check if it's an array or a single string\n    if (json[pos] == '[') {\n        // Array case\n        pos++; // Skip [\n        \n        while (pos < json.length()) {\n            // Skip whitespace\n            while (pos < json.length() && (json[pos] == ' ' || json[pos] == '\\t' || json[pos] == '\\n' || json[pos] == ',')) {\n                pos++;\n            }\n            \n            if (pos >= json.length() || json[pos] == ']') {\n                break;\n            }\n            \n            if (json[pos] == '\"') {\n                pos++; // Skip opening quote\n                size_t end_pos = pos;\n                \n                // Find closing quote\n                while (end_pos < json.length() && json[end_pos] != '\"') {\n                    if (json[end_pos] == '\\\\') {\n                        end_pos++; // Skip escaped character\n                    }\n                    end_pos++;\n                }\n                \n                if (end_pos < json.length()) {\n                    result.push_back(json.substr(pos, end_pos - pos));\n                    pos = end_pos + 1;\n                }\n            } else {\n                break; // Invalid format\n            }\n        }\n    } else if (json[pos] == '\"') {\n        // Single string case\n        pos++; // Skip opening quote\n        size_t end_pos = pos;\n        \n        while (end_pos < json.length() && json[end_pos] != '\"') {\n            if (json[end_pos] == '\\\\') {\n                end_pos++; // Skip escaped character\n            }\n            end_pos++;\n        }\n        \n        if (end_pos < json.length()) {\n            result.push_back(json.substr(pos, end_pos - pos));\n        }\n    }\n    \n    return result;\n}\n\ninline JwtClaims::JwtClaims(std::string jwt_payload, Identity identity)\n    : payload_(std::move(jwt_payload)), identity_(std::move(identity)) {}\n\ninline const std::string& JwtClaims::subject() const {\n    if (!subject_.has_value()) {\n        subject_ = extract_string_claim(payload_, \"sub\");\n        if (!subject_.has_value()) {\n            // Return empty string on error instead of throwing\n            static const std::string empty;\n            return empty;\n        }\n    }\n    return *subject_;\n}\n\ninline const std::string& JwtClaims::issuer() const {\n    if (!issuer_.has_value()) {\n        issuer_ = extract_string_claim(payload_, \"iss\");\n        if (!issuer_.has_value()) {\n            // Return empty string on error instead of throwing\n            static const std::string empty;\n            return empty;\n        }\n    }\n    return *issuer_;\n}\n\ninline const std::vector<std::string>& JwtClaims::audience() const {\n    if (!audience_.has_value()) {\n        audience_ = extract_audience_claim(payload_);\n    }\n    return *audience_;\n}\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_JWT_CLAIMS_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/logger.h",
    "content": "#ifndef SPACETIMEDB_LIBRARY_LOGGING_H\n#define SPACETIMEDB_LIBRARY_LOGGING_H\n\n#include \"spacetimedb/abi/FFI.h\" // For LogLevel\n#include <string>\n#include <string_view>\n#include <chrono>\n#include <memory>\n#include <cstring>\n#include <cstdio>\n\nnamespace SpacetimeDB {\n\n// LogLevel is now from opaque_types.h, imported via FFI.h\n\n// Compile-time filename extraction to avoid runtime filesystem operations\nnamespace detail {\n    constexpr const char* extract_filename(const char* path) {\n        const char* file = path;\n        while (*path) {\n            if (*path == '/' || *path == '\\\\') {\n                file = path + 1;\n            }\n            ++path;\n        }\n        return file;\n    }\n    \n    // Simple printf-style formatting\n    template<typename... Args>\n    std::string format(const char* fmt, Args... args) {\n        // Get required size\n        int size = std::snprintf(nullptr, 0, fmt, args...);\n        if (size <= 0) { return \"\"; }\n        \n        // Format string\n        std::string result(size + 1, '\\0');\n        std::snprintf(result.data(), result.size(), fmt, args...);\n        result.resize(size); // Remove null terminator from std::string\n        return result;\n    }\n}\n\n// Compile-time macro for extracting just the filename\n#define STDB_FILENAME ::SpacetimeDB::detail::extract_filename(__FILE__)\n\n// Default log level - can be overridden at compile time\n#ifndef STDB_LOG_LEVEL\n    #ifdef NDEBUG\n        #define STDB_LOG_LEVEL ::SpacetimeDB::LogLevelValue::INFO\n    #else\n        #define STDB_LOG_LEVEL ::SpacetimeDB::LogLevelValue::DEBUG\n    #endif\n#endif\n\n/**\n * @brief Optimized logging with caller information using string_view.\n * @param level The severity level of the message.\n * @param message The message to log.\n * @param target The function/method name.\n * @param filename The source file name (just filename, not full path).\n * @param line_number The source line number.\n * @ingroup sdk_runtime sdk_logging\n */\ninline void log_with_caller_info(LogLevel level, std::string_view message, \n                                std::string_view target = \"\", \n                                std::string_view filename = \"\", \n                                uint32_t line_number = 0) {\n    FFI::console_log(level,\n                   reinterpret_cast<const uint8_t*>(target.data()), target.length(),\n                   reinterpret_cast<const uint8_t*>(filename.data()), filename.length(),\n                   line_number,\n                   reinterpret_cast<const uint8_t*>(message.data()), message.length());\n}\n\n/**\n * @brief Simple logging without caller information (optimized).\n * @param level The severity level of the message.\n * @param message The message to log.\n * @ingroup sdk_runtime sdk_logging\n */\ninline void log(LogLevel level, std::string_view message) {\n    log_with_caller_info(level, message, \"\", \"\", 0);\n}\n\n// Legacy overloads for backward compatibility\ninline void log_with_caller_info(LogLevel level, const std::string& message, \n                                const char* target = nullptr, \n                                const char* filename = nullptr, \n                                uint32_t line_number = 0) {\n    log_with_caller_info(level, std::string_view(message), \n                        target ? std::string_view(target) : std::string_view(\"\"),\n                        filename ? std::string_view(filename) : std::string_view(\"\"),\n                        line_number);\n}\n\ninline void log(LogLevel level, const std::string& message) {\n    log(level, std::string_view(message));\n}\n\n// Optimized logging macros with compile-time level filtering\n#define LOG_ERROR(message) \\\n    do { \\\n        if constexpr (STDB_LOG_LEVEL >= ::SpacetimeDB::LogLevelValue::ERROR) { \\\n            ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::ERROR, (message), __func__, STDB_FILENAME, __LINE__); \\\n        } \\\n    } while(0)\n\n#define LOG_WARN(message) \\\n    do { \\\n        if constexpr (STDB_LOG_LEVEL >= ::SpacetimeDB::LogLevelValue::WARN) { \\\n            ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::WARN, (message), __func__, STDB_FILENAME, __LINE__); \\\n        } \\\n    } while(0)\n\n#define LOG_INFO(message) \\\n    do { \\\n        if constexpr (STDB_LOG_LEVEL >= ::SpacetimeDB::LogLevelValue::INFO) { \\\n            ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::INFO, (message), __func__, STDB_FILENAME, __LINE__); \\\n        } \\\n    } while(0)\n\n#define LOG_DEBUG(message) \\\n    do { \\\n        if constexpr (STDB_LOG_LEVEL >= ::SpacetimeDB::LogLevelValue::DEBUG) { \\\n            ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::DEBUG, (message), __func__, STDB_FILENAME, __LINE__); \\\n        } \\\n    } while(0)\n\n#define LOG_TRACE(message) \\\n    do { \\\n        if constexpr (STDB_LOG_LEVEL >= ::SpacetimeDB::LogLevelValue::TRACE) { \\\n            ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::TRACE, (message), __func__, STDB_FILENAME, __LINE__); \\\n        } \\\n    } while(0)\n\n// Printf-style logging macros for convenience\n#define LOG_ERROR_F(fmt, ...) LOG_ERROR(::SpacetimeDB::detail::format(fmt, ##__VA_ARGS__))\n#define LOG_WARN_F(fmt, ...) LOG_WARN(::SpacetimeDB::detail::format(fmt, ##__VA_ARGS__))\n#define LOG_INFO_F(fmt, ...) LOG_INFO(::SpacetimeDB::detail::format(fmt, ##__VA_ARGS__))\n#define LOG_DEBUG_F(fmt, ...) LOG_DEBUG(::SpacetimeDB::detail::format(fmt, ##__VA_ARGS__))\n#define LOG_TRACE_F(fmt, ...) LOG_TRACE(::SpacetimeDB::detail::format(fmt, ##__VA_ARGS__))\n\n// Special panic macro that logs as ERROR level matching to LOG_FATAL\n// ERROR level is used due to PANIC logging a stack trace which won't work correctly from C++\n#define LOG_PANIC(message) \\\n    do { \\\n        ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::ERROR, (message), __func__, STDB_FILENAME, __LINE__); \\\n        __builtin_trap(); \\\n    } while(0)\n\n// Fatal error macro that logs and aborts (for exception-free code)\n#define LOG_FATAL(message) \\\n    do { \\\n        ::SpacetimeDB::log_with_caller_info(::SpacetimeDB::LogLevelValue::ERROR, (message), __func__, STDB_FILENAME, __LINE__); \\\n        __builtin_trap(); \\\n    } while(0)\n\n// Convenience functions using string_view (inline for optimization)\ninline void log_error(std::string_view message) { log(LogLevelValue::ERROR, message); }\ninline void log_warn(std::string_view message) { log(LogLevelValue::WARN, message); }\ninline void log_info(std::string_view message) { log(LogLevelValue::INFO, message); }\ninline void log_debug(std::string_view message) { log(LogLevelValue::DEBUG, message); }\ninline void log_trace(std::string_view message) { log(LogLevelValue::TRACE, message); }\ninline void log_panic(std::string_view message) { log(LogLevelValue::ERROR, message); } // Panic logs as PANIC\n\n// Legacy overloads for backward compatibility\ninline void log_error(const std::string& message) { log_error(std::string_view(message)); }\ninline void log_warn(const std::string& message) { log_warn(std::string_view(message)); }\ninline void log_info(const std::string& message) { log_info(std::string_view(message)); }\ninline void log_debug(const std::string& message) { log_debug(std::string_view(message)); }\ninline void log_trace(const std::string& message) { log_trace(std::string_view(message)); }\n\n/**\n * @brief Optimized RAII performance measurement utility.\n * \n * This class provides automatic performance timing with SpacetimeDB's\n * console timer system. The timer starts when constructed and automatically\n * ends when the object is destroyed (RAII pattern).\n * \n * Improvements:\n * - Uses string_view to avoid allocations\n * - Supports retrieving elapsed time\n * - Better move semantics\n * - Inline implementation for better optimization\n * \n * Example usage:\n * @code\n * {\n *     LogStopwatch timer(\"database_operation\");\n *     // ... perform database operations ...\n *     // Timer automatically ends when timer goes out of scope\n * }\n * @endcode\n * \n * @ingroup sdk_runtime sdk_logging\n */\nclass LogStopwatch {\npublic:\n    /**\n     * @brief Start a performance timer with the given name.\n     * @param name The name of the operation being timed.\n     */\n    explicit LogStopwatch(std::string_view name) : ended_(false) {\n        timer_id_ = FFI::console_timer_start(\n            reinterpret_cast<const uint8_t*>(name.data()),\n            name.length()\n        );\n        start_time_ = std::chrono::steady_clock::now();\n    }\n    \n    // Constructor accepting std::string for backward compatibility\n    explicit LogStopwatch(const std::string& name) : LogStopwatch(std::string_view(name)) {}\n    \n    /**\n     * @brief Destructor automatically ends the timer.\n     */\n    ~LogStopwatch() {\n        if (!ended_) {\n            end();\n        }\n    }\n    \n    /**\n     * @brief Manually end the timer (optional - destructor will do this automatically).\n     */\n    void end() {\n        if (!ended_) {\n            auto status = FFI::console_timer_end(timer_id_);\n            ended_ = true;\n            end_time_ = std::chrono::steady_clock::now();\n            // TODO: Add error handling when we implement exception system\n            (void)status; // Suppress unused variable warning for now\n        }\n    }\n    \n    /**\n     * @brief Get elapsed time in microseconds.\n     * @return Elapsed time or 0 if timer hasn't ended.\n     */\n    uint64_t elapsed_microseconds() const {\n        if (ended_) {\n            return std::chrono::duration_cast<std::chrono::microseconds>(\n                end_time_ - start_time_).count();\n        }\n        // Return current elapsed time if not ended\n        auto now = std::chrono::steady_clock::now();\n        return std::chrono::duration_cast<std::chrono::microseconds>(\n            now - start_time_).count();\n    }\n    \n    /**\n     * @brief Get elapsed time in milliseconds.\n     * @return Elapsed time or 0 if timer hasn't ended.\n     */\n    uint64_t elapsed_milliseconds() const {\n        return elapsed_microseconds() / 1000;\n    }\n    \n    // Disable copy construction and assignment\n    LogStopwatch(const LogStopwatch&) = delete;\n    LogStopwatch& operator=(const LogStopwatch&) = delete;\n    \n    // Allow move construction and assignment\n    LogStopwatch(LogStopwatch&& other) noexcept\n        : timer_id_(other.timer_id_), \n          ended_(other.ended_),\n          start_time_(other.start_time_),\n          end_time_(other.end_time_) {\n        other.ended_ = true; // Prevent double-ending\n    }\n    \n    LogStopwatch& operator=(LogStopwatch&& other) noexcept {\n        if (this != &other) {\n            if (!ended_) {\n                end(); // End current timer\n            }\n            timer_id_ = other.timer_id_;\n            ended_ = other.ended_;\n            start_time_ = other.start_time_;\n            end_time_ = other.end_time_;\n            other.ended_ = true; // Prevent double-ending\n        }\n        return *this;\n    }\n\nprivate:\n    ConsoleTimerId timer_id_;\n    bool ended_;\n    std::chrono::steady_clock::time_point start_time_;\n    std::chrono::steady_clock::time_point end_time_;\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_LIBRARY_LOGGING_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/macros.h",
    "content": "#ifndef SPACETIMEDB_MACROS_H\n#define SPACETIMEDB_MACROS_H\n\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"spacetimedb/reducer_context.h\"\n#include \"spacetimedb/table.h\"\n#include \"spacetimedb/bsatn/traits.h\"\n\n#include <string>\n#include <vector>\n#include <sstream>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n/**\n * @brief Helper function to parse parameter names from stringified parameter list\n * \n * This is used internally by SPACETIMEDB_REDUCER and SPACETIMEDB_PROCEDURE macros to\n * extract parameter names from the stringified __VA_ARGS__ (which excludes the context).\n * \n * The macros separate ctx_param from __VA_ARGS__, so this function receives only\n * the non-context parameters.\n * \n * @param param_list The stringified parameter list (e.g., \"uint32_t id, std::string name\")\n * @return Vector of parameter names\n */\ninline std::vector<std::string> parseParameterNames(const std::string& param_list) {\n    std::vector<std::string> param_names;\n    \n    // Handle empty parameter list\n    if (param_list.empty()) {\n        return param_names;\n    }\n    \n    // Split by comma\n    std::istringstream stream(param_list);\n    std::string param;\n    \n    while (std::getline(stream, param, ',')) {\n        // Trim whitespace\n        param.erase(0, param.find_first_not_of(\" \\t\\n\\r\"));\n        param.erase(param.find_last_not_of(\" \\t\\n\\r\") + 1);\n        \n        // Skip empty parameters\n        if (param.empty()) {\n            continue;\n        }\n        \n        // Extract the parameter name (last word before end or default value)\n        // Handle cases like:\n        // - \"int x\"\n        // - \"const std::string& name\"\n        // - \"std::vector<int> values\"\n        // - \"MyType* ptr\"\n        // - \"int x = 5\"\n        \n        // Remove default value if present\n        size_t eq_pos = param.find('=');\n        std::string decl = (eq_pos != std::string::npos) ? param.substr(0, eq_pos) : param;\n        \n        // Trim trailing whitespace\n        decl.erase(decl.find_last_not_of(\" \\t\\n\\r\") + 1);\n        \n        // Find the last word (parameter name)\n        size_t last_space = decl.find_last_of(\" \\t&*\");\n        if (last_space != std::string::npos && last_space + 1 < decl.length()) {\n            std::string name = decl.substr(last_space + 1);\n            if (!name.empty()) {\n                param_names.push_back(name);\n            }\n        }\n    }\n    \n    return param_names;\n}\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n// =============================================================================\n// UNIFIED HELPER MACROS FOR SPACETIMEDB TYPE REGISTRATION\n// =============================================================================\n\n/**\n * @brief Common CONCAT macros - unified from multiple files\n * \n * These eliminate duplication of CONCAT macro definitions across files.\n */\n#ifndef SPACETIMEDB_CONCAT_IMPL\n#define SPACETIMEDB_CONCAT_IMPL(a, b) a##b\n#define SPACETIMEDB_CONCAT(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)\n#endif\n\n/**\n * @brief Helper for stringifying values (used in export names)\n */\n#ifndef SPACETIMEDB_STRINGIFY_IMPL\n#define SPACETIMEDB_STRINGIFY_IMPL(x) #x  \n#define SPACETIMEDB_STRINGIFY(x) SPACETIMEDB_STRINGIFY_IMPL(x)\n#endif\n\n/**\n * @brief Helper for stringifying each argument in a variadic list\n * \n * Converts each identifier to a string literal. Used by multi-column index macros.\n * Example: SPACETIMEDB_STRINGIFY_EACH(x, y, z) -> \"x\", \"y\", \"z\"\n */\n#define SPACETIMEDB_STRINGIFY_ARG(x) #x\n#define SPACETIMEDB_STRINGIFY_EACH(...) SPACETIMEDB_FOR_EACH_STRINGIFY(__VA_ARGS__)\n\n/**\n * @brief FOR_EACH macro implementation for stringifying (up to 6 arguments)\n * \n * Supports up to 6 arguments (matching multi-column index limit).\n * Uses a different name than the existing SPACETIMEDB_FOR_EACH_ARG to avoid conflicts.\n */\n#define SPACETIMEDB_GET_STRINGIFY_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_1(a) SPACETIMEDB_STRINGIFY_ARG(a)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_2(a, b) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_3(a, b, c) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_4(a, b, c, d) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_5(a, b, c, d, e) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d), SPACETIMEDB_STRINGIFY_ARG(e)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY_6(a, b, c, d, e, f) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d), SPACETIMEDB_STRINGIFY_ARG(e), SPACETIMEDB_STRINGIFY_ARG(f)\n#define SPACETIMEDB_FOR_EACH_STRINGIFY(...) \\\n    SPACETIMEDB_GET_STRINGIFY_MACRO(__VA_ARGS__, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_6, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_5, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_4, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_3, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_2, \\\n        SPACETIMEDB_FOR_EACH_STRINGIFY_1)(__VA_ARGS__)\n\n/**\n * @brief Compatibility aliases for files that use different macro names\n * \n * This provides backward compatibility while centralizing macro definitions.\n * Files can gradually migrate to the SPACETIMEDB_* prefixed versions.\n */\n\n// Non-prefixed compatibility aliases\n#ifndef CONCAT_IMPL\n#define CONCAT_IMPL(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)\n#define CONCAT(a, b) SPACETIMEDB_CONCAT(a, b)\n#endif\n\n#ifndef STRINGIFY_IMPL  \n#define STRINGIFY_IMPL(x) SPACETIMEDB_STRINGIFY_IMPL(x)\n#define STRINGIFY(x) SPACETIMEDB_STRINGIFY(x)\n#endif\n\n// SPACETIMEDB_PASTE compatibility alias (some files use PASTE instead of CONCAT)\n#ifndef SPACETIMEDB_PASTE_IMPL\n#define SPACETIMEDB_PASTE_IMPL(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)\n#define SPACETIMEDB_PASTE(a, b) SPACETIMEDB_CONCAT(a, b)\n#endif\n\n// TypeRegistry system removed - use ModuleTypeRegistration instead\n/*\n// Register a C++ type name in the global TypeRegistry for reducer parameter lookup\n// This replaces the old TypeNameRegistry approach with a unified one.\n// Used by all macros that need reducer parameter support.\n#define SPACETIMEDB_REGISTER_CPP_TYPE_NAME(Type) \\\n    namespace { \\\n        struct Type##_cpp_name_registrar { \\\n            Type##_cpp_name_registrar() { \\\n                ::SpacetimeDB::TypeRegistry::global_instance().register_cpp_type_name<Type>(#Type); \\\n            } \\\n        }; \\\n        static Type##_cpp_name_registrar Type##_cpp_name_registrar_instance; \\\n    }\n\n// Register a type in the active TypeRegistry with proper naming\n// This is the single registration point that eliminates triple registration.\n// Only registers if a registry is available and not already registered.\n#define SPACETIMEDB_REGISTER_TYPE_IN_REGISTRY(Type, algebraic_type) \\\n    do { \\\n        static bool Type##_v9_registered = false; \\\n        if (!Type##_v9_registered) { \\\n            Type##_v9_registered = true; \\\n            std::string type_name = #Type; \\\n            ::SpacetimeDB::Internal::getModuleTypeRegistration().registerTypeByName(type_name, algebraic_type, &typeid(Type)); \\\n        } \\\n    } while(0)\n*/\n\n/**\n * @brief Updated registration using ModuleTypeRegistration system\n * \n * This is the new registration point using the ModuleTypeRegistration system.\n */\n#define SPACETIMEDB_REGISTER_TYPE_IN_V9(Type, algebraic_type) \\\n    do { \\\n        static bool Type##_v9_registered = false; \\\n        if (!Type##_v9_registered) { \\\n            Type##_v9_registered = true; \\\n            std::string type_name = #Type; \\\n            /* Register immediately with name and structure */ \\\n            ::SpacetimeDB::Internal::getModuleTypeRegistration().registerTypeByName(type_name, algebraic_type, &typeid(Type)); \\\n        } \\\n    } while(0)\n\n/**\n * @brief Complete type registration (registry + global name registration)\n * \n * This combines both registry registration and global name registration\n * in a single, atomic operation. Use this in algebraic_type() methods.\n */\n#define SPACETIMEDB_REGISTER_TYPE_COMPLETE(Type, algebraic_type) \\\n    SPACETIMEDB_REGISTER_TYPE_IN_V9(Type, algebraic_type)\n\n/**\n * @brief Generate a default field_registrar specialization that does nothing\n * \n * Most types don't need field registration (only table types do).\n * This provides a clean default implementation.\n */\n#define SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(Type) \\\n    template<> \\\n    struct ::SpacetimeDB::field_registrar<Type> { \\\n        static void register_fields() { \\\n            /* Default: no field registration needed */ \\\n        } \\\n    };\n\n/**\n * @brief Generate a field_registrar that actually registers field descriptors\n * \n * This version is used by SPACETIMEDB_STRUCT to ensure fields\n * are properly registered for table types.\n */\n#define SPACETIMEDB_GENERATE_FIELD_REGISTRAR_WITH_FIELDS(Type, ...) \\\n    template<> \\\n    struct ::SpacetimeDB::field_registrar<Type> { \\\n        static void register_fields() { \\\n            static bool registered = false; \\\n            if (registered) return; \\\n            registered = true; \\\n            SPACETIMEDB_REGISTER_FIELD_DESCRIPTORS(Type, __VA_ARGS__) \\\n        } \\\n    };\n\n/**\n * @brief Generate the complete registration bundle for a type\n * \n * This combines all the registration pieces that most macros need:\n * - BSATN algebraic_type_of specialization\n * - Empty field_registrar specialization  \n * \n * Use this for simple types that don't need custom field registration.\n * Note: Type name registration is now handled automatically by ModuleTypeRegistration.\n */\n#define SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE(Type) \\\n    template<> \\\n    struct ::SpacetimeDB::bsatn::algebraic_type_of<Type> { \\\n        static ::SpacetimeDB::bsatn::AlgebraicType get() { \\\n            return ::SpacetimeDB::bsatn::bsatn_traits<Type>::algebraic_type(); \\\n        } \\\n    }; \\\n    SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(Type)\n\n/**\n * @brief Generate the complete registration bundle with field registration\n * \n * This version includes actual field registration for table types.\n * Note: Type name registration is now handled automatically by ModuleTypeRegistration.\n */\n#define SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE_WITH_FIELDS(Type, ...) \\\n    template<> \\\n    struct ::SpacetimeDB::bsatn::algebraic_type_of<Type> { \\\n        static ::SpacetimeDB::bsatn::AlgebraicType get() { \\\n            return ::SpacetimeDB::bsatn::bsatn_traits<Type>::algebraic_type(); \\\n        } \\\n    }; \\\n    SPACETIMEDB_GENERATE_FIELD_REGISTRAR_WITH_FIELDS(Type, __VA_ARGS__)\n\n/**\n * @brief Helper to check if a type is already registered (for optimization)\n * \n * This can be used to avoid expensive operations if a type is already registered.\n */\n#define SPACETIMEDB_IS_TYPE_REGISTERED(Type) \\\n    ([]() { \\\n        static bool Type##_checked = false; \\\n        return Type##_checked; \\\n    }())\n\n/**\n * @brief Mark a type as registered (for optimization)\n */\n#define SPACETIMEDB_MARK_TYPE_REGISTERED(Type) \\\n    do { \\\n        static bool Type##_checked = false; \\\n        Type##_checked = true; \\\n    } while(0)\n\n/**\n * @brief Unified preinit function generator\n * \n * This consolidates the pattern used by all table/reducer registration macros:\n * - Generates unique export name with priority, category, name, and line number\n * - Creates the preinit function with proper C linkage\n * \n * @param priority Registration order (20 for tables, 30 for reducers)\n * @param category Type of registration (register_table, reducer, etc.)\n * @param name Specific name of the item being registered\n * @param registration_body The actual registration code\n */\n#define SPACETIMEDB_GENERATE_PREINIT_FUNCTION(priority, category, name, registration_body) \\\n    __attribute__((export_name(\"__preinit__\" #priority \"_\" #category \"_\" #name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    extern \"C\" void SPACETIMEDB_CONCAT(__preinit__, SPACETIMEDB_CONCAT(priority, SPACETIMEDB_CONCAT(_, SPACETIMEDB_CONCAT(category, SPACETIMEDB_CONCAT(_, SPACETIMEDB_CONCAT(name, SPACETIMEDB_CONCAT(_line_, __LINE__)))))))() { \\\n        registration_body \\\n    }\n\n/**\n * @brief Unified table registration pattern\n * \n * Consolidates the common table registration logic used by SPACETIMEDB_TABLE macro.\n * \n * @param type The table struct type\n * @param name_str Table name string  \n * @param access_enum Public/Private access\n * @param constraint_list Vector initializer for field constraints\n */\n// SPACETIMEDB_REGISTER_TABLE_TYPE macro removed - replaced by V9Builder system\n\n/**\n * @brief Unified reducer registration pattern\n * \n * Consolidates the common reducer registration logic used by SPACETIMEDB_REDUCER macro.\n * \n * @param function_name Name of the reducer function\n * @param function_signature Full parameter list for the function\n */\n#define SPACETIMEDB_REGISTER_REDUCER_FUNCTION(function_name, function_signature) \\\n    void function_name function_signature; \\\n    SPACETIMEDB_GENERATE_PREINIT_FUNCTION(30, reducer, function_name, \\\n        SpacetimeDB::Internal::register_reducer_func_with_params(std::string(#function_name), function_name, #function_signature); \\\n    ) \\\n    void function_name function_signature\n\n/**\n * @brief Unified lifecycle reducer registration\n * \n * Consolidates the pattern used by SPACETIMEDB_INIT, SPACETIMEDB_CLIENT_CONNECTED, etc.\n * \n * @param lifecycle_type Type of lifecycle (init, client_connected, client_disconnected)\n * @param function_name Name of the reducer function  \n * @param function_signature Full parameter list for the function\n * @param register_call The specific registration call for this lifecycle type\n */\n#define SPACETIMEDB_REGISTER_LIFECYCLE_REDUCER(lifecycle_type, function_name, function_signature, register_call) \\\n    void function_name function_signature; \\\n    SPACETIMEDB_GENERATE_PREINIT_FUNCTION(20, reducer, lifecycle_type, register_call) \\\n    void function_name function_signature\n\n// =============================================================================\n// VARIADIC ARGUMENT PROCESSING UTILITIES\n// =============================================================================\n\n// Consolidated variadic macro system (supports up to 50 arguments)\n// Used by SPACETIMEDB_STRUCT and SPACETIMEDB_VARIANT_ENUM\n#define SPACETIMEDB_GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,NAME,...) NAME\n\n#define SPACETIMEDB_FOR_EACH_ARG(MACRO, obj, extra, ...) \\\n    SPACETIMEDB_GET_MACRO(__VA_ARGS__, \\\n        SPACETIMEDB_FE_50, SPACETIMEDB_FE_49, SPACETIMEDB_FE_48, SPACETIMEDB_FE_47, SPACETIMEDB_FE_46, \\\n        SPACETIMEDB_FE_45, SPACETIMEDB_FE_44, SPACETIMEDB_FE_43, SPACETIMEDB_FE_42, SPACETIMEDB_FE_41, \\\n        SPACETIMEDB_FE_40, SPACETIMEDB_FE_39, SPACETIMEDB_FE_38, SPACETIMEDB_FE_37, SPACETIMEDB_FE_36, \\\n        SPACETIMEDB_FE_35, SPACETIMEDB_FE_34, SPACETIMEDB_FE_33, SPACETIMEDB_FE_32, SPACETIMEDB_FE_31, \\\n        SPACETIMEDB_FE_30, SPACETIMEDB_FE_29, SPACETIMEDB_FE_28, SPACETIMEDB_FE_27, SPACETIMEDB_FE_26, \\\n        SPACETIMEDB_FE_25, SPACETIMEDB_FE_24, SPACETIMEDB_FE_23, SPACETIMEDB_FE_22, SPACETIMEDB_FE_21, \\\n        SPACETIMEDB_FE_20, SPACETIMEDB_FE_19, SPACETIMEDB_FE_18, SPACETIMEDB_FE_17, SPACETIMEDB_FE_16, \\\n        SPACETIMEDB_FE_15, SPACETIMEDB_FE_14, SPACETIMEDB_FE_13, SPACETIMEDB_FE_12, SPACETIMEDB_FE_11, \\\n        SPACETIMEDB_FE_10, SPACETIMEDB_FE_9, SPACETIMEDB_FE_8, SPACETIMEDB_FE_7, SPACETIMEDB_FE_6, \\\n        SPACETIMEDB_FE_5, SPACETIMEDB_FE_4, SPACETIMEDB_FE_3, SPACETIMEDB_FE_2, SPACETIMEDB_FE_1) \\\n    (MACRO, obj, extra, __VA_ARGS__)\n\n#define SPACETIMEDB_FE_1(MACRO, obj, extra, X) MACRO(obj, extra, X)\n#define SPACETIMEDB_FE_2(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_1(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_3(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_2(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_4(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_3(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_5(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_4(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_6(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_5(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_7(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_6(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_8(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_7(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_9(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_8(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_10(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_9(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_11(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_10(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_12(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_11(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_13(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_12(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_14(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_13(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_15(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_14(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_16(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_15(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_17(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_16(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_18(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_17(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_19(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_18(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_20(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_19(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_21(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_20(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_22(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_21(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_23(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_22(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_24(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_23(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_25(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_24(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_26(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_25(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_27(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_26(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_28(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_27(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_29(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_28(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_30(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_29(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_31(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_30(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_32(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_31(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_33(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_32(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_34(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_33(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_35(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_34(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_36(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_35(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_37(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_36(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_38(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_37(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_39(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_38(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_40(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_39(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_41(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_40(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_42(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_41(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_43(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_42(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_44(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_43(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_45(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_44(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_46(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_45(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_47(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_46(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_48(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_47(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_49(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_48(MACRO, obj, extra, __VA_ARGS__)\n#define SPACETIMEDB_FE_50(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_49(MACRO, obj, extra, __VA_ARGS__)\n\n// =============================================================================\n// SPECIALIZED FOR_EACH MACROS FOR DIFFERENT USE CASES\n// =============================================================================\n\n/**\n * @brief FOR_EACH_VARIANT - specialized macro for enum variant processing\n * \n * This adapts the unified SPACETIMEDB_FOR_EACH_ARG to use separator-based syntax\n * instead of obj-extra syntax. Used by enum_macro.h for variant processing.\n */\n#define SPACETIMEDB_FOR_EACH_VARIANT(macro, sep, ...) \\\n    SPACETIMEDB_FOR_EACH_VARIANT_IMPL(macro, sep, ##__VA_ARGS__)\n\n#define SPACETIMEDB_FOR_EACH_VARIANT_IMPL(macro, sep, ...) \\\n    SPACETIMEDB_GET_MACRO(__VA_ARGS__, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_50, SPACETIMEDB_FOR_EACH_VARIANT_FE_49, SPACETIMEDB_FOR_EACH_VARIANT_FE_48, SPACETIMEDB_FOR_EACH_VARIANT_FE_47, SPACETIMEDB_FOR_EACH_VARIANT_FE_46, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_45, SPACETIMEDB_FOR_EACH_VARIANT_FE_44, SPACETIMEDB_FOR_EACH_VARIANT_FE_43, SPACETIMEDB_FOR_EACH_VARIANT_FE_42, SPACETIMEDB_FOR_EACH_VARIANT_FE_41, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_40, SPACETIMEDB_FOR_EACH_VARIANT_FE_39, SPACETIMEDB_FOR_EACH_VARIANT_FE_38, SPACETIMEDB_FOR_EACH_VARIANT_FE_37, SPACETIMEDB_FOR_EACH_VARIANT_FE_36, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_35, SPACETIMEDB_FOR_EACH_VARIANT_FE_34, SPACETIMEDB_FOR_EACH_VARIANT_FE_33, SPACETIMEDB_FOR_EACH_VARIANT_FE_32, SPACETIMEDB_FOR_EACH_VARIANT_FE_31, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_30, SPACETIMEDB_FOR_EACH_VARIANT_FE_29, SPACETIMEDB_FOR_EACH_VARIANT_FE_28, SPACETIMEDB_FOR_EACH_VARIANT_FE_27, SPACETIMEDB_FOR_EACH_VARIANT_FE_26, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_25, SPACETIMEDB_FOR_EACH_VARIANT_FE_24, SPACETIMEDB_FOR_EACH_VARIANT_FE_23, SPACETIMEDB_FOR_EACH_VARIANT_FE_22, SPACETIMEDB_FOR_EACH_VARIANT_FE_21, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_20, SPACETIMEDB_FOR_EACH_VARIANT_FE_19, SPACETIMEDB_FOR_EACH_VARIANT_FE_18, SPACETIMEDB_FOR_EACH_VARIANT_FE_17, SPACETIMEDB_FOR_EACH_VARIANT_FE_16, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_15, SPACETIMEDB_FOR_EACH_VARIANT_FE_14, SPACETIMEDB_FOR_EACH_VARIANT_FE_13, SPACETIMEDB_FOR_EACH_VARIANT_FE_12, SPACETIMEDB_FOR_EACH_VARIANT_FE_11, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_10, SPACETIMEDB_FOR_EACH_VARIANT_FE_9, SPACETIMEDB_FOR_EACH_VARIANT_FE_8, SPACETIMEDB_FOR_EACH_VARIANT_FE_7, SPACETIMEDB_FOR_EACH_VARIANT_FE_6, \\\n        SPACETIMEDB_FOR_EACH_VARIANT_FE_5, SPACETIMEDB_FOR_EACH_VARIANT_FE_4, SPACETIMEDB_FOR_EACH_VARIANT_FE_3, SPACETIMEDB_FOR_EACH_VARIANT_FE_2, SPACETIMEDB_FOR_EACH_VARIANT_FE_1) \\\n    (macro, sep, ##__VA_ARGS__)\n\n// Adapter macros that apply macro + separator pattern (instead of macro-obj-extra pattern)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_1(macro, sep, X) macro(X)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_2(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_1(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_3(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_2(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_4(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_3(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_5(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_4(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_6(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_5(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_7(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_6(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_8(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_7(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_9(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_8(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_10(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_9(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_11(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_10(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_12(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_11(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_13(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_12(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_14(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_13(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_15(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_14(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_16(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_15(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_17(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_16(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_18(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_17(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_19(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_18(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_20(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_19(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_21(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_20(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_22(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_21(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_23(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_22(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_24(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_23(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_25(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_24(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_26(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_25(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_27(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_26(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_28(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_27(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_29(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_28(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_30(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_29(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_31(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_30(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_32(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_31(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_33(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_32(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_34(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_33(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_35(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_34(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_36(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_35(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_37(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_36(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_38(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_37(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_39(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_38(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_40(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_39(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_41(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_40(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_42(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_41(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_43(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_42(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_44(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_43(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_45(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_44(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_46(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_45(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_47(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_46(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_48(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_47(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_49(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_48(macro, sep, __VA_ARGS__)\n#define SPACETIMEDB_FOR_EACH_VARIANT_FE_50(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_49(macro, sep, __VA_ARGS__)\n\n// Compatibility aliases for files that expect non-prefixed names\n#define FOR_EACH_VARIANT SPACETIMEDB_FOR_EACH_VARIANT\n\n// =============================================================================\n// TABLE MACROS\n// =============================================================================\n\n// Enhanced table macro with optional scheduling support\n// Usage: SPACETIMEDB_TABLE(MyStruct, \"table_name\", Public)\n//    or: SPACETIMEDB_TABLE(MyStruct, \"table_name\", Private)\n// REMOVED: SPACETIMEDB_TABLE is now defined in field_metadata.h\n// #define SPACETIMEDB_TABLE(...) \\\n//     SPACETIMEDB_PASTE(SPACETIMEDB_TABLE_, SPACETIMEDB_NARGS(__VA_ARGS__))(__VA_ARGS__)\n\n// All table macros have been moved to field_metadata.h to avoid duplicates.\n// The SPACETIMEDB_TABLE macro in field_metadata.h supports:\n//   - SPACETIMEDB_TABLE(Type, table_name, Public)\n//   - SPACETIMEDB_TABLE(Type, table_name, Private)\n//   - Multiple tables using the same struct type\n\n// All reducer macros have been moved to reducer_macros.h\n// This includes SPACETIMEDB_REDUCER, SPACETIMEDB_INIT, \n// SPACETIMEDB_CLIENT_CONNECTED, and SPACETIMEDB_CLIENT_DISCONNECTED\n\n// =============================================================================\n// CONSTRAINT AND FIELD ATTRIBUTES\n// =============================================================================\n// Use SPACETIMEDB_TABLE with constraint macros for field attributes:\n//   SPACETIMEDB_TABLE(MyTable, \"my_table\", Public,\n//       PrimaryKey(id), Unique(email), Index(name)\n//   )\n\n// =============================================================================\n// VISIBILITY FILTER MACRO\n// =============================================================================\n\n/**\n * @brief Set module case conversion policy using a fixed preinit registration symbol.\n *\n * Defining this macro more than once in a module intentionally causes a duplicate symbol error.\n */\n#define SPACETIMEDB_SETTING_CASE_CONVERSION(policy_enum_value) \\\n    extern \"C\" __attribute__((export_name(\"__preinit__18_module_settings_case_conversion_policy\"))) \\\n    void __spacetimedb_preinit_case_conversion_policy() { \\\n        SpacetimeDB::Module::SetCaseConversionPolicy(policy_enum_value); \\\n    }\n\n/**\n * @brief Register a client visibility filter with the SpacetimeDB module system\n * \n * This macro registers a SQL string as a client visibility filter.\n * \n * @param filter_name The unique name for this filter\n * @param sql_query The SQL query string for the filter\n * \n * Usage:\n *    SPACETIMEDB_CLIENT_VISIBILITY_FILTER(user_owns_data, \"SELECT * FROM user_data WHERE owner_id = current_user_identity()\")\n */\n#define SPACETIMEDB_CLIENT_VISIBILITY_FILTER(filter_name, sql_query) \\\n    __attribute__((export_name(\"__preinit__25_register_row_level_security_\" #filter_name))) \\\n    extern \"C\" void __register_client_visibility_filter_##filter_name() { \\\n        SpacetimeDB::Module::RegisterClientVisibilityFilter(sql_query); \\\n    }\n\n// =============================================================================\n// STRUCT TYPE MACROS  \n// =============================================================================\n\n/**\n * @brief Enable BSATN serialization for unit struct types\n * \n * Unit structs are structs with no fields, similar to std::monostate.\n * They serialize/deserialize as empty (0 bytes).\n * \n * Usage:\n *   struct UnitType {};\n *   SPACETIMEDB_UNIT_STRUCT(UnitType)\n */\n#define SPACETIMEDB_UNIT_STRUCT(Type) \\\n    struct Type { \\\n        static constexpr bool __is_unit_type__ = true; \\\n        Type() = default; \\\n        Type(std::monostate) {} \\\n        operator std::monostate() const { return std::monostate{}; } \\\n    }; \\\n    template<> \\\n    struct SpacetimeDB::bsatn::bsatn_traits<Type> { \\\n        static void serialize(SpacetimeDB::bsatn::Writer& w, const Type& v) { \\\n            /* Unit struct serializes as empty */ \\\n        } \\\n        static Type deserialize(SpacetimeDB::bsatn::Reader& r) { \\\n            return Type{}; \\\n        } \\\n        static SpacetimeDB::bsatn::AlgebraicType algebraic_type() { \\\n            return SpacetimeDB::Internal::LazyTypeRegistrar<Type>::getOrRegister( \\\n                []() -> SpacetimeDB::bsatn::AlgebraicType { \\\n                    return SpacetimeDB::bsatn::AlgebraicType::make_product( \\\n                        std::make_unique<SpacetimeDB::bsatn::ProductType>(std::vector<SpacetimeDB::bsatn::ProductTypeElement>{}) \\\n                    ); \\\n                }, \\\n                #Type \\\n            ); \\\n        } \\\n    }; \\\n    SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE(Type)\n\n/**\n * @brief Enable BSATN serialization for struct types with fields\n * \n * Generates complete serialization support for structs by serializing\n * each field in the order specified. This macro must be called after\n * struct definition and before SPACETIMEDB_TABLE.\n * \n * @param Type The struct type name\n * @param ... List of field names (not types) in the order they appear in the struct\n * \n * @example Basic struct with table registration:\n * @code\n * struct Player {\n *     uint32_t id;\n *     std::string name;\n *     uint8_t level;\n * };\n * SPACETIMEDB_STRUCT(Player, id, name, level)\n * SPACETIMEDB_TABLE(Player, players, Public)\n * FIELD_PrimaryKey(players, id)\n * @endcode\n * \n * @example Struct with enum field:\n * @code\n * SPACETIMEDB_ENUM(GameState, Lobby, Playing, Ended)\n * \n * struct Match {\n *     uint32_t match_id;\n *     GameState state;\n *     Timestamp created_at;\n * };\n * SPACETIMEDB_STRUCT(Match, match_id, state, created_at)\n * SPACETIMEDB_TABLE(Match, matches, Public)\n * @endcode\n * \n * @example Struct used in reducer (no table):\n * @code\n * struct Vector2 {\n *     float x;\n *     float y;\n * };\n * SPACETIMEDB_STRUCT(Vector2, x, y)\n * \n * SPACETIMEDB_REDUCER(move_player, ReducerContext ctx, Vector2 delta) {\n *     // Use Vector2 as a parameter type\n *     return Ok();\n * }\n * @endcode\n * \n * @note Field order must match struct definition order\n * @note All fields must support BSATN serialization\n * @see SPACETIMEDB_TABLE for registering structs as database tables\n * @see SPACETIMEDB_UNIT_STRUCT for zero-field structs\n */\n#define SPACETIMEDB_STRUCT(Type, ...) \\\n    template<> \\\n    struct SpacetimeDB::bsatn::bsatn_traits<Type> { \\\n        static void serialize(SpacetimeDB::bsatn::Writer& w, const Type& v) { \\\n            SPACETIMEDB_SERIALIZE_FIELDS(v, w, __VA_ARGS__) \\\n        } \\\n        static Type deserialize(SpacetimeDB::bsatn::Reader& r) { \\\n            Type v; \\\n            SPACETIMEDB_DESERIALIZE_FIELDS(v, r, __VA_ARGS__) \\\n            return v; \\\n        } \\\n        static SpacetimeDB::bsatn::AlgebraicType algebraic_type() { \\\n            return SpacetimeDB::Internal::LazyTypeRegistrar<Type>::getOrRegister( \\\n                []() -> SpacetimeDB::bsatn::AlgebraicType { \\\n                    SpacetimeDB::bsatn::ProductTypeBuilder builder; \\\n                    SPACETIMEDB_REGISTER_FIELDS(Type, builder, __VA_ARGS__) \\\n                    return SpacetimeDB::bsatn::AlgebraicType::make_product(builder.build()); \\\n                }, \\\n                #Type \\\n            ); \\\n        } \\\n    }; \\\n    SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE_WITH_FIELDS(Type, __VA_ARGS__)\n\n// Field processing helper macros (used by SPACETIMEDB_STRUCT)\n#define SPACETIMEDB_SERIALIZE_FIELD(obj, writer, field) \\\n    SpacetimeDB::bsatn::serialize(writer, obj.field);\n    \n#define SPACETIMEDB_DESERIALIZE_FIELD(obj, reader, field) \\\n    obj.field = SpacetimeDB::bsatn::deserialize<decltype(obj.field)>(reader);\n    \n#define SPACETIMEDB_REGISTER_FIELD(Type, builder, field) \\\n    builder.with_field<decltype(Type::field)>(#field);\n\n#define SPACETIMEDB_SERIALIZE_FIELDS(obj, writer, ...) \\\n    SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_SERIALIZE_FIELD, obj, writer, __VA_ARGS__)\n    \n#define SPACETIMEDB_DESERIALIZE_FIELDS(obj, reader, ...) \\\n    SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_DESERIALIZE_FIELD, obj, reader, __VA_ARGS__)\n    \n#define SPACETIMEDB_REGISTER_FIELDS(Type, builder, ...) \\\n    SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_REGISTER_FIELD, Type, builder, __VA_ARGS__)\n\n// Field descriptor registration for runtime reflection\n#define SPACETIMEDB_REGISTER_FIELD_DESCRIPTOR(Type, dummy, field) \\\n    { \\\n        ::SpacetimeDB::FieldDescriptor desc; \\\n        desc.name = #field; \\\n        desc.offset = offsetof(Type, field); \\\n        desc.size = sizeof(decltype(Type::field)); \\\n        desc.write_type = [](std::vector<uint8_t>& buf) { \\\n            ::SpacetimeDB::write_field_type<decltype(Type::field)>(buf); \\\n        }; \\\n        desc.get_algebraic_type = []() { \\\n            return ::SpacetimeDB::bsatn::bsatn_traits<decltype(Type::field)>::algebraic_type(); \\\n        }; \\\n        desc.serialize = [](std::vector<uint8_t>& buf, const void* obj) { \\\n            const Type* typed_obj = static_cast<const Type*>(obj); \\\n            ::SpacetimeDB::serialize_value(buf, typed_obj->field); \\\n        }; \\\n        desc.get_type_name = []() -> std::string { \\\n            return demangle_cpp_type_name(typeid(decltype(Type::field)).name()); \\\n        }; \\\n        ::SpacetimeDB::get_table_descriptors()[&typeid(Type)].fields.push_back(desc); \\\n    }\n\n#define SPACETIMEDB_REGISTER_FIELD_DESCRIPTORS(Type, ...) \\\n    SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_REGISTER_FIELD_DESCRIPTOR, Type, dummy, __VA_ARGS__)\n\n/**\n * @brief Define a unit type (empty struct) with BSATN serialization support\n * \n * Creates an empty struct and generates the necessary BSATN traits for it.\n * Useful for creating unique wrapper types for unit variants in enums,\n * since std::variant requires all types to be unique.\n * \n * This is the C++ equivalent of C#'s `[Type] public partial record UnitType { }`\n * \n * @param TypeName The name of the unit type to create\n * \n * @example Creating unit types for enum variants:\n * @code\n * // Define unique types for unit variants\n * SPACETIMEDB_UNIT_TYPE(FooVariant)\n * SPACETIMEDB_UNIT_TYPE(BarVariant)\n * \n * // Use in variant enum\n * SPACETIMEDB_ENUM(MyEnum,\n *     (Foo, FooVariant),\n *     (Bar, BarVariant),\n *     (Baz, std::string)\n * )\n * @endcode\n */\n#define SPACETIMEDB_UNIT_TYPE(TypeName) \\\n    struct TypeName {}; \\\n    \\\n    namespace SpacetimeDB::bsatn { \\\n    template<> \\\n    struct bsatn_traits<TypeName> { \\\n        static void serialize(Writer& writer, const TypeName&) { \\\n            /* Unit type: serialize nothing */ \\\n        } \\\n        static TypeName deserialize(Reader& reader) { \\\n            /* Unit type: deserialize nothing, return default */ \\\n            return TypeName{}; \\\n        } \\\n        static AlgebraicType algebraic_type() { \\\n            /* Unit type is a product type with no fields */ \\\n            return AlgebraicType::Unit(); \\\n        } \\\n    }; \\\n    }\n\n\n#endif // SPACETIMEDB_MACROS_H\n\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/outcome.h",
    "content": "#ifndef SPACETIMEDB_OUTCOME_H\n#define SPACETIMEDB_OUTCOME_H\n\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <variant>\n\nnamespace SpacetimeDB {\n\n// Internal distinct error type to avoid std::variant<T, std::string> when T == std::string.\nstruct OutcomeError {\n    std::string msg;\n};\n\n// Forward declaration\ntemplate <typename T>\nclass Outcome;\n\n// ==================== Outcome<T> ====================\n\ntemplate <typename T>\nclass [[nodiscard]] Outcome {\nprivate:\n    // index 0 = Ok(T), index 1 = Err(OutcomeError)\n    std::variant<T, OutcomeError> value_;\n\n    // Private constructors\n    explicit Outcome(T value)\n        : value_(std::in_place_index<0>, std::move(value)) {}\n\n    explicit Outcome(OutcomeError error)\n        : value_(std::in_place_index<1>, std::move(error)) {}\n\npublic:\n    using value_type = T;\n\n    // ---- Factories ----\n    static Outcome Ok(T value) {\n        return Outcome(std::move(value));\n    }\n\n    static Outcome Err(std::string error) {\n        return Outcome(OutcomeError{std::move(error)});\n    }\n\n    static Outcome Err(const char* error) {\n        return Outcome(OutcomeError{std::string(error)});\n    }\n\n    // ---- State ----\n    bool is_ok() const { return value_.index() == 0; }\n    bool is_err() const { return value_.index() == 1; }\n\n    // ---- Accessors ----\n    // Precondition: is_ok()\n    T& value() & { return std::get<0>(value_); }\n    const T& value() const & { return std::get<0>(value_); }\n    T&& value() && { return std::get<0>(std::move(value_)); }\n\n    // Precondition: is_err()\n    const std::string& error() const { return std::get<1>(value_).msg; }\n\n    // Optional convenience: like Rust's unwrap_or\n    template <typename U>\n    T value_or(U&& fallback) const & {\n        return is_ok() ? std::get<0>(value_) : T(std::forward<U>(fallback));\n    }\n};\n\n// ==================== Outcome<void> specialization ====================\n\ntemplate <>\nclass [[nodiscard]] Outcome<void> {\nprivate:\n    std::optional<OutcomeError> error_;\n\n    explicit Outcome(std::nullopt_t) : error_(std::nullopt) {}\n    explicit Outcome(OutcomeError error) : error_(std::move(error)) {}\n\npublic:\n    using value_type = void;\n\n    // ---- Factories ----\n    static Outcome Ok() {\n        return Outcome(std::nullopt);\n    }\n\n    static Outcome Err(std::string error) {\n        return Outcome(OutcomeError{std::move(error)});\n    }\n\n    static Outcome Err(const char* error) {\n        return Outcome(OutcomeError{std::string(error)});\n    }\n\n    // ---- State ----\n    bool is_ok() const { return !error_.has_value(); }\n    bool is_err() const { return error_.has_value(); }\n\n    // Precondition: is_err()\n    const std::string& error() const { return error_->msg; }\n};\n\n// ==================== Free helper functions ====================\n\n// Ok() for void\ninline Outcome<void> Ok() {\n    return Outcome<void>::Ok();\n}\n\n// Err() for void\ninline Outcome<void> Err(std::string msg) {\n    return Outcome<void>::Err(std::move(msg));\n}\n\ninline Outcome<void> Err(const char* msg) {\n    return Outcome<void>::Err(msg);\n}\n\n// Ok(value) for T\ntemplate <typename T>\ninline Outcome<std::decay_t<T>> Ok(T&& value) {\n    return Outcome<std::decay_t<T>>::Ok(std::forward<T>(value));\n}\n\n// Err<T>(msg) for T\ntemplate <typename T>\ninline Outcome<T> Err(std::string msg) {\n    return Outcome<T>::Err(std::move(msg));\n}\n\ntemplate <typename T>\ninline Outcome<T> Err(const char* msg) {\n    return Outcome<T>::Err(msg);\n}\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_OUTCOME_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/procedure_context.h",
    "content": "#ifndef SPACETIMEDB_PROCEDURE_CONTEXT_H\n#define SPACETIMEDB_PROCEDURE_CONTEXT_H\n\n#include <spacetimedb/bsatn/types.h> // For Identity\n#include <spacetimedb/bsatn/timestamp.h> // For Timestamp\n#include <spacetimedb/bsatn/uuid.h> // For Uuid\n#include <spacetimedb/tx_context.h> // For TxContext\n#include <spacetimedb/abi/FFI.h> // For transaction syscalls\n#include <spacetimedb/random.h> // For StdbRng\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\n#include <spacetimedb/http.h> // For HttpClient\n#endif\n#include <cstdint>\n#include <functional>\n#include <stdexcept>\n#include <type_traits>\n#include <memory>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Context for procedures\n * \n * ProcedureContext provides access to call metadata (sender, timestamp, connection)\n * but does NOT have direct database access. This is a key difference from ReducerContext.\n * \n * Features:\n * - Pure computations with return values\n * - Database access via explicit transactions (ctx.WithTx() or ctx.TryWithTx())\n * - HTTP requests via ctx.http (when SPACETIMEDB_UNSTABLE_FEATURES enabled)\n * - UUID generation (ctx.new_uuid_v4(), ctx.new_uuid_v7())\n * \n * Key differences from ReducerContext:\n * - NO db field (database operations require explicit transactions)\n * - Has connection_id (procedures track which connection called them)\n * - Has rng() method for UUID generation\n * \n * Example usage (pure function):\n * @code\n * SPACETIMEDB_PROCEDURE(uint32_t, add_numbers, ProcedureContext ctx, uint32_t a, uint32_t b) {\n *     return a + b;\n * }\n * @endcode\n * \n * Example with transactions:\n * @code\n * SPACETIMEDB_PROCEDURE(Unit, insert_item, ProcedureContext ctx, Item item) {\n *     ctx.WithTx([&item](TxContext& tx) {\n *         tx.db[items].insert(item);\n *     });\n *     return Unit{};\n * }\n * @endcode\n */\nstruct ProcedureContext {\nprivate:\n    // Caller's identity - who invoked this procedure\n    Identity sender_;\n\npublic:\n    // Timestamp when the procedure was invoked\n    Timestamp timestamp;\n\n    // Connection ID for the caller\n    // Used to track which client connection initiated this procedure\n    ConnectionId connection_id;\n\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\n    // HTTP client for making external requests\n    // IMPORTANT: HTTP calls are NOT allowed inside transactions!\n    // Always call HTTP before with_tx() or try_with_tx()\n    HttpClient http;\n#endif\n\nprivate:\n    // Lazily initialized RNG for UUID generation\n    mutable std::shared_ptr<StdbRng> rng_instance;\n    \n    // Monotonic counter for UUID v7 generation (31 bits, wraps around)\n    mutable uint32_t counter_uuid_ = 0;\n\npublic:\n    ProcedureContext() = default;\n    \n    ProcedureContext(Identity s, Timestamp t, ConnectionId conn_id)\n        : sender_(s), timestamp(t), connection_id(conn_id) {}\n\n    Identity sender() const {\n        return sender_;\n    }\n\n    /**\n     * @brief Read the current module's Identity\n     * \n     * Returns the Identity (database address) of the module instance.\n     * This is useful for constructing URLs or making API calls to the module's own endpoints.\n     * \n     * Example:\n     * @code\n     * auto module_id = ctx.identity();\n     * std::string url = \"http://localhost:3000/v1/database/\" + \n     *                   module_id.to_hex() + \"/schema?version=9\";\n     * @endcode\n     */\n    Identity identity() const {\n        std::array<uint8_t, 32> id_bytes;\n        ::identity(id_bytes.data());\n        return Identity(id_bytes);\n    }\n\n    /**\n     * @brief Get the random number generator for this procedure call\n     * \n     * Lazily initialized and seeded with the timestamp.\n     */\n    StdbRng& rng() const {\n        if (!rng_instance) {\n            rng_instance = std::make_shared<StdbRng>(timestamp);\n        }\n        return *rng_instance;\n    }\n\n    /**\n     * Generate a new random UUID v4.\n     * \n     * Creates a random UUID using the procedure's RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_PROCEDURE(Uuid, generate_uuid_v4, ProcedureContext ctx) {\n     *     return ctx.new_uuid_v4();\n     * }\n     * @endcode\n     * \n     * @return A new UUID v4\n     */\n    Uuid new_uuid_v4() const {\n        // Get 16 random bytes from the context RNG\n        std::array<uint8_t, 16> random_bytes;\n        rng().fill_bytes(random_bytes.data(), 16);\n        \n        // Generate UUID v4\n        return Uuid::from_random_bytes_v4(random_bytes);\n    }\n\n    /**\n     * Generate a new UUID v7.\n     * \n     * Creates a time-ordered UUID with the procedure's timestamp, a monotonic counter,\n     * and random bytes from the procedure's RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_PROCEDURE(Uuid, generate_uuid_v7, ProcedureContext ctx) {\n     *     return ctx.new_uuid_v7();\n     * }\n     * @endcode\n     * \n     * @return A new UUID v7\n     */\n    Uuid new_uuid_v7() const {\n        // Get 4 random bytes from the context RNG\n        std::array<uint8_t, 4> random_bytes;\n        rng().fill_bytes(random_bytes.data(), 4);\n        \n        // Generate UUID v7 with timestamp and counter\n        return Uuid::from_counter_v7(counter_uuid_, timestamp, random_bytes);\n    }\n\n#ifdef SPACETIMEDB_UNSTABLE_FEATURES\n    /**\n     * @brief Execute a callback within a database transaction\n     * \n     * Starts a mutable transaction, executes the callback, and commits on success.\n     * If the callback panics (via LOG_PANIC), the transaction is automatically rolled back.\n     * \n     * The callback receives a TxContext with database access. All database operations\n     * performed within the callback are part of the transaction.\n     * \n     * Usage:\n     * @code\n     * ctx.with_tx([&](TxContext& tx) {\n     *     tx.db.users().insert(User{\"alice\"});\n     *     tx.db.posts().insert(Post{\"hello world\"});\n     *     // Both inserts commit together\n     * });\n     * @endcode\n     * \n     * @param body Callback to execute within the transaction\n     * @return The return value of the callback\n     */\n    template<typename Func>\n    auto with_tx(Func&& body) -> decltype(body(std::declval<TxContext&>())) {\n        using ResultType = decltype(body(std::declval<TxContext&>()));\n        \n        // Start transaction\n        int64_t tx_timestamp;\n        Status status = ::procedure_start_mut_tx(&tx_timestamp);\n        if (is_error(status)) {\n            LOG_PANIC(\"Failed to start transaction\");\n        }\n        \n        // Create a ReducerContext for this transaction\n        // Note: connection_id converted to std::optional\n        ReducerContext reducer_ctx(\n            sender(),\n            std::optional<ConnectionId>(connection_id),\n            Timestamp::from_micros_since_epoch(tx_timestamp)\n        );\n        \n        // Create transaction context wrapping the reducer context\n        TxContext tx{reducer_ctx};\n        \n        // Execute callback\n        if constexpr (std::is_void_v<ResultType>) {\n            body(tx);\n            \n            // Commit transaction\n            status = ::procedure_commit_mut_tx();\n            if (is_error(status)) {\n                LOG_PANIC(\"Failed to commit transaction\");\n            }\n        } else {\n            ResultType result = body(tx);\n            \n            // Commit transaction\n            status = ::procedure_commit_mut_tx();\n            if (is_error(status)) {\n                LOG_PANIC(\"Failed to commit transaction\");\n            }\n            \n            return result;\n        }\n    }\n    \n    /**\n     * @brief Execute a callback within a database transaction, with explicit rollback control\n     * \n     * Similar to with_tx(), but allows the callback to indicate whether to commit or rollback.\n     * The callback should return true to commit, false to rollback.\n     * \n     * Usage:\n     * @code\n     * bool success = ctx.try_with_tx([&](TxContext& tx) -> bool {\n     *     tx.db.users().insert(User{\"alice\"});\n     *     if (some_condition) {\n     *         return false; // Rollback\n     *     }\n     *     return true; // Commit\n     * });\n     * @endcode\n     * \n     * @param body Callback that returns true to commit, false to rollback\n     * @return The return value of the callback\n     */\n    template<typename Func>\n    auto try_with_tx(Func&& body) -> decltype(body(std::declval<TxContext&>())) {\n        using ResultType = decltype(body(std::declval<TxContext&>()));\n        \n        // Start transaction\n        int64_t tx_timestamp;\n        Status status = ::procedure_start_mut_tx(&tx_timestamp);\n        if (is_error(status)) {\n            LOG_PANIC(\"Failed to start transaction\");\n        }\n        \n        // Create a ReducerContext for this transaction\n        ReducerContext reducer_ctx(\n            sender(),\n            std::optional<ConnectionId>(connection_id),\n            Timestamp::from_micros_since_epoch(tx_timestamp)\n        );\n        \n        // Create transaction context wrapping the reducer context\n        TxContext tx{reducer_ctx};\n        \n        // Execute callback\n        ResultType result = body(tx);\n        \n        // For bool results, use the value to decide commit/rollback\n        // For other types, always commit (caller can use LOG_PANIC to abort)\n        if constexpr (std::is_same_v<ResultType, bool>) {\n            if (result) {\n                status = ::procedure_commit_mut_tx();\n                if (is_error(status)) {\n                    LOG_PANIC(\"Failed to commit transaction\");\n                }\n            } else {\n                status = ::procedure_abort_mut_tx();\n                if (is_error(status)) {\n                    LOG_PANIC(\"Failed to rollback transaction\");\n                }\n            }\n        } else {\n            // For non-bool returns, always commit\n            status = ::procedure_commit_mut_tx();\n            if (is_error(status)) {\n                LOG_PANIC(\"Failed to commit transaction\");\n            }\n        }\n        \n        return result;\n    }\n#endif\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_PROCEDURE_CONTEXT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/procedure_macros.h",
    "content": "#pragma once\n\n#include \"spacetimedb/procedure_context.h\"\n#include \"spacetimedb/internal/Module.h\"\n#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/macros.h\"  // For CONCAT\n#include \"spacetimedb/error_handling.h\"\n#include <string>\n#include <vector>\n#include <type_traits>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n/**\n * @brief Type trait to validate procedure return types\n * \n * Procedures can return any SpacetimeType, unlike views which are restricted\n * to std::vector<T> or std::optional<T>. This includes primitives, structs,\n * enums, or any custom type that implements the SpacetimeType concept.\n * \n */\ntemplate<typename T>\nstruct is_valid_procedure_return_type : std::integral_constant<bool, bsatn::Serializable<T>> {};\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n/**\n * @brief Macro for defining SpacetimeDB procedures\n * \n * Procedures are functions that can return arbitrary values (unlike reducers which return void).\n * They are always public (no access control like reducers).\n * \n * Features:\n * - Pure computations with return values\n * - Database access via explicit transactions (ctx.WithTx() or ctx.TryWithTx())\n * - HTTP requests via ctx.http (when SPACETIMEDB_UNSTABLE_FEATURES enabled)\n * - UUID generation (ctx.new_uuid_v4(), ctx.new_uuid_v7())\n * - Return type directly\n * \n * Key differences from reducers:\n * - NO direct db field (must use ctx.WithTx() for database operations)\n * - Has connection_id (procedures track which connection called them)\n * - Can return any SpacetimeType\n * \n * @param return_type The return type - can be any SpacetimeType (primitive, struct, enum, etc.)\n * @param procedure_name The name of the procedure function\n * @param ctx_param Must be ProcedureContext ctx\n * @param ... Additional parameters (optional) - any SpacetimeType\n * \n * Examples:\n * @code\n * // Pure computation\n * SPACETIMEDB_PROCEDURE(uint32_t, add_numbers, ProcedureContext ctx, uint32_t a, uint32_t b) {\n *     return a + b;\n * }\n * \n * // With database transaction\n * SPACETIMEDB_PROCEDURE(Unit, insert_item, ProcedureContext ctx, Item item) {\n *     ctx.WithTx([&item](TxContext& tx) {\n *         tx.db[items].insert(item);\n *     });\n *     return Unit{};\n * }\n * \n * // Return struct\n * struct ReturnStruct {\n *     uint32_t a;\n *     std::string b;\n * };\n * SPACETIMEDB_STRUCT(ReturnStruct, a, b)\n * \n * SPACETIMEDB_PROCEDURE(ReturnStruct, make_struct, ProcedureContext ctx, uint32_t a, std::string b) {\n *     return ReturnStruct{a, b};\n * }\n * \n * // UUID generation\n * SPACETIMEDB_PROCEDURE(Uuid, generate_uuid, ProcedureContext ctx) {\n *     return ctx.new_uuid_v7();\n * }\n * @endcode\n */\n#define SPACETIMEDB_PROCEDURE(return_type, procedure_name, ctx_param, ...) \\\n    /* Validate return type at compile-time */ \\\n    static_assert(::SpacetimeDB::Internal::is_valid_procedure_return_type<return_type>::value, \\\n        \"Procedure return type must be a SpacetimeType (implement Serializable trait)\"); \\\n    \\\n    /* Forward declaration with optional parameters */ \\\n    return_type procedure_name(ctx_param __VA_OPT__(,) __VA_ARGS__); \\\n    \\\n    /* Preinit registration function */ \\\n    /* Procedures run at priority 50 to ensure views are registered first */ \\\n    __attribute__((export_name(\"__preinit__50_proc_\" #procedure_name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_proc_, procedure_name)() { \\\n        /* Parse parameter names from the stringified parameter list */ \\\n        std::string param_list = #__VA_ARGS__; \\\n        std::vector<std::string> param_names = \\\n            SpacetimeDB::Internal::parseParameterNames(param_list); \\\n        \\\n        /* Register the procedure with the V10Builder system */ \\\n        /* Note: Procedures are always public (no is_public parameter) */ \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterProcedure( \\\n            #procedure_name, procedure_name, param_names); \\\n    } \\\n    \\\n    /* The actual procedure function definition */ \\\n    return_type procedure_name(ctx_param __VA_OPT__(,) __VA_ARGS__)\n\n#define SPACETIMEDB_PROCEDURE_NAMED(return_type, procedure_name, canonical_name, ctx_param, ...) \\\n    static_assert(::SpacetimeDB::Internal::is_valid_procedure_return_type<return_type>::value, \\\n        \"Procedure return type must be a SpacetimeType (implement Serializable trait)\"); \\\n    return_type procedure_name(ctx_param __VA_OPT__(,) __VA_ARGS__); \\\n    __attribute__((export_name(\"__preinit__50_proc_\" #procedure_name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_proc_, procedure_name)() { \\\n        std::string param_list = #__VA_ARGS__; \\\n        std::vector<std::string> param_names = \\\n            SpacetimeDB::Internal::parseParameterNames(param_list); \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterProcedure( \\\n            #procedure_name, procedure_name, param_names); \\\n        SpacetimeDB::Module::RegisterExplicitFunctionName(#procedure_name, canonical_name); \\\n    } \\\n    return_type procedure_name(ctx_param __VA_OPT__(,) __VA_ARGS__)\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/random.h",
    "content": "#ifndef SPACETIMEDB_RANDOM_H\n#define SPACETIMEDB_RANDOM_H\n\n#include <random>\n#include <limits>\n#include <memory>\n#include <spacetimedb/bsatn/timestamp.h>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Deterministic random number generator for SpacetimeDB reducers\n * \n * StdbRng provides a cryptographically-strong random number generator that is\n * seeded with the reducer's timestamp, ensuring:\n * - **Deterministic** behavior: Same inputs always produce same random sequence\n * - **Reproducible** tests: Reducer execution can be replayed exactly\n * - **Consensus-safe**: All nodes generate identical random values\n * \n * The RNG uses the Mersenne Twister algorithm (std::mt19937_64) seeded with\n * the reducer's timestamp in microseconds since Unix epoch.\n * \n * @warning **DO NOT use for cryptographic purposes!**\n * This RNG is deterministic and predictable. For security-sensitive operations,\n * use a cryptographic RNG from a trusted source.\n * \n * @example Basic usage in a reducer:\n * @code\n * SPACETIMEDB_REDUCER(void, spawn_enemy, ReducerContext ctx) {\n *     // Get the deterministic RNG for this reducer call\n *     auto& rng = ctx.rng();\n *     \n *     // Generate random enemy stats\n *     uint32_t health = rng.gen_range(50u, 100u);\n *     uint32_t attack = rng.gen_range(10u, 25u);\n *     float speed = rng.gen_range(1.0f, 3.0f);\n *     \n *     ctx.db[enemies].insert(Enemy{health, attack, speed});\n * }\n * @endcode\n * \n * @note The RNG is lazily initialized on first use to avoid overhead for reducers\n *       that don't need random numbers\n * \n * @ingroup sdk_runtime\n */\nclass StdbRng {\nprivate:\n    // Use mt19937_64 for 64-bit Mersenne Twister (same quality as Rust's StdRng)\n    mutable std::mt19937_64 engine;\n    mutable bool initialized = false;\n    Timestamp seed_timestamp;\n    \n    void ensure_initialized() const {\n        if (!initialized) {\n            // Seed with timestamp's microseconds since Unix epoch\n            // This matches the Rust implementation's approach\n            uint64_t seed = static_cast<uint64_t>(seed_timestamp.micros_since_epoch());\n            engine.seed(seed);\n            initialized = true;\n        }\n    }\n    \npublic:\n    // Constructor that takes a timestamp for seeding\n    explicit StdbRng(Timestamp ts) : seed_timestamp(ts) {}\n    \n    /**\n     * @brief Generate a random 32-bit unsigned integer\n     * \n     * Produces a uniformly distributed random value across the full uint32_t range [0, 2^32-1].\n     * \n     * @return Random uint32_t value\n     * \n     * @example\n     * @code\n     * auto& rng = ctx.rng();\n     * uint32_t dice_roll = (rng.next_u32() % 6) + 1;  // 1-6\n     * uint32_t random_id = rng.next_u32();  // Full range\n     * @endcode\n     */\n    uint32_t next_u32() const {\n        ensure_initialized();\n        return static_cast<uint32_t>(engine() & 0xFFFFFFFF);\n    }\n    \n    /**\n     * @brief Generate a random 64-bit unsigned integer\n     * \n     * Produces a uniformly distributed random value across the full uint64_t range [0, 2^64-1].\n     * \n     * @return Random uint64_t value\n     * \n     * @example\n     * @code\n     * auto& rng = ctx.rng();\n     * uint64_t large_random = rng.next_u64();\n     * uint64_t timestamp_noise = rng.next_u64() % 1000;  // 0-999\n     * @endcode\n     */\n    uint64_t next_u64() const {\n        ensure_initialized();\n        return engine();\n    }\n    \n    /**\n     * @brief Generate a random value in the specified range [min, max]\n     * \n     * Produces a uniformly distributed random value within the inclusive range [min, max].\n     * Works with both integral and floating-point types.\n     * \n     * @tparam T Type of the range bounds (integral or floating-point)\n     * @param min Lower bound (inclusive)\n     * @param max Upper bound (inclusive)\n     * @return Random value in [min, max]\n     * \n     * @example Integer ranges:\n     * @code\n     * auto& rng = ctx.rng();\n     * \n     * // Dice rolls\n     * int d6 = rng.gen_range(1, 6);      // 1-6 inclusive\n     * int d20 = rng.gen_range(1, 20);    // 1-20 inclusive\n     * \n     * // Game stats\n     * uint32_t damage = rng.gen_range(10u, 50u);  // 10-50 damage\n     * int temperature = rng.gen_range(-10, 35);   // -10°C to 35°C\n     * @endcode\n     * \n     * @example Floating-point ranges:\n     * @code\n     * // Spawn position in game world\n     * float x = rng.gen_range(0.0f, 100.0f);\n     * float y = rng.gen_range(0.0f, 100.0f);\n     * \n     * // Physics simulation\n     * double velocity = rng.gen_range(0.5, 2.5);\n     * float friction = rng.gen_range(0.1f, 0.9f);\n     * @endcode\n     * \n     * @note For integral types, both bounds are inclusive\n     * @note For floating-point types, the distribution is continuous in [min, max]\n     */\n    template<typename T>\n    T gen_range(T min, T max) const {\n        ensure_initialized();\n        if constexpr (std::is_integral_v<T>) {\n            std::uniform_int_distribution<T> dist(min, max);\n            return dist(engine);\n        } else {\n            std::uniform_real_distribution<T> dist(min, max);\n            return dist(engine);\n        }\n    }\n    \n    // Generate a random integer of type T\n    template<typename T>\n    T gen() const {\n        ensure_initialized();\n        if constexpr (std::is_same_v<T, bool>) {\n            return engine() & 1;\n        } else if constexpr (std::is_integral_v<T>) {\n            if constexpr (std::is_unsigned_v<T>) {\n                if constexpr (sizeof(T) <= 4) {\n                    return static_cast<T>(next_u32());\n                } else {\n                    return static_cast<T>(next_u64());\n                }\n            } else {\n                // For signed types, use the full range\n                std::uniform_int_distribution<T> dist(\n                    std::numeric_limits<T>::min(),\n                    std::numeric_limits<T>::max()\n                );\n                return dist(engine);\n            }\n        } else if constexpr (std::is_floating_point_v<T>) {\n            // Generate float in [0, 1)\n            if constexpr (std::is_same_v<T, float>) {\n                return static_cast<float>(next_u32()) / static_cast<float>(UINT32_MAX);\n            } else {\n                return static_cast<double>(next_u64()) / static_cast<double>(UINT64_MAX);\n            }\n        }\n    }\n    \n    /**\n     * @brief Fill a buffer with random bytes\n     * \n     * Generates random bytes and writes them to the provided buffer.\n     * Each byte is uniformly distributed in [0, 255].\n     * \n     * @param dest Pointer to destination buffer\n     * @param count Number of bytes to generate\n     * \n     * @example Generate random data for UUID:\n     * @code\n     * std::array<uint8_t, 16> uuid_bytes;\n     * ctx.rng().fill_bytes(uuid_bytes.data(), 16);\n     * Uuid id = Uuid::from_random_bytes_v4(uuid_bytes);\n     * @endcode\n     * \n     * @example Generate random salt:\n     * @code\n     * uint8_t salt[32];\n     * ctx.rng().fill_bytes(salt, 32);\n     * @endcode\n     * \n     * @warning This is NOT cryptographically secure! Do not use for\n     *          security-sensitive operations like password hashing or encryption keys.\n     */\n    void fill_bytes(uint8_t* dest, size_t count) const {\n        ensure_initialized();\n        for (size_t i = 0; i < count; i++) {\n            dest[i] = static_cast<uint8_t>(engine() & 0xFF);\n        }\n    }\n    \n    /**\n     * @brief Fill a vector with random bytes\n     * \n     * Convenience overload that fills an existing std::vector<uint8_t> with random data.\n     * The vector size must be pre-allocated.\n     * \n     * @param dest Vector to fill (must be pre-sized)\n     * \n     * @example\n     * @code\n     * std::vector<uint8_t> random_data(256);  // Pre-allocate 256 bytes\n     * ctx.rng().fill_bytes(random_data);      // Fill with random values\n     * @endcode\n     */\n    void fill_bytes(std::vector<uint8_t>& dest) const {\n        fill_bytes(dest.data(), dest.size());\n    }\n    \n    // Generate a random float in [0, 1)\n    float gen_float() const {\n        return gen<float>();\n    }\n    \n    // Generate a random double in [0, 1)\n    double gen_double() const {\n        return gen<double>();\n    }\n    \n    // Generate a random boolean\n    bool gen_bool() const {\n        return gen<bool>();\n    }\n    \n    /**\n     * @brief Randomly shuffle a container using Fisher-Yates algorithm\n     * \n     * Performs an in-place random permutation of the elements in [first, last).\n     * Each permutation is equally likely (uniform distribution).\n     * \n     * @tparam RandomIt Random access iterator type\n     * @param first Iterator to the beginning of the range\n     * @param last Iterator to the end of the range\n     * \n     * @example Shuffle a deck of cards:\n     * @code\n     * std::vector<Card> deck = create_deck();\n     * ctx.rng().shuffle(deck.begin(), deck.end());\n     * // deck is now randomly permuted\n     * @endcode\n     * \n     * @example Randomize player turn order:\n     * @code\n     * std::vector<PlayerId> players = {1, 2, 3, 4};\n     * ctx.rng().shuffle(players.begin(), players.end());\n     * for (auto player_id : players) {\n     *     // Process players in random order\n     * }\n     * @endcode\n     */\n    template<typename RandomIt>\n    void shuffle(RandomIt first, RandomIt last) const {\n        ensure_initialized();\n        std::shuffle(first, last, engine);\n    }\n    \n    /**\n     * @brief Select a random element from a container\n     * \n     * Returns a random element from the container with uniform probability.\n     * Each element has an equal chance of being selected.\n     * \n     * @tparam Container Container type (must support operator[] and size())\n     * @param container The container to sample from\n     * @return Random element from the container\n     * \n     * @example Select random enemy type:\n     * @code\n     * std::vector<std::string> enemy_types = {\"goblin\", \"orc\", \"troll\", \"dragon\"};\n     * std::string enemy = ctx.rng().sample(enemy_types);\n     * LOG_INFO(\"Spawned: \" + enemy);\n     * @endcode\n     * \n     * @example Random loot drop:\n     * @code\n     * std::vector<Item> loot_table = {common_item, rare_item, epic_item};\n     * Item dropped = ctx.rng().sample(loot_table);\n     * @endcode\n     * \n     * @warning Undefined behavior if container is empty! Check size before calling.\n     * @note Returns by value (copy) - use references in container if needed\n     */\n    template<typename Container>\n    auto sample(const Container& container) const -> decltype(container[0]) {\n        ensure_initialized();\n        if (container.empty()) {\n            // Return first element for empty container (undefined behavior)\n            // In production, you should check container size before calling sample\n            return container[0];\n        }\n        std::uniform_int_distribution<size_t> dist(0, container.size() - 1);\n        return container[dist(engine)];\n    }\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_RANDOM_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/range_queries.h",
    "content": "#pragma once\n\n#include <concepts>\n#include <optional>\n#include <type_traits>\n\nnamespace SpacetimeDB {\n\n// =============================================================================\n// C++20 Concepts-based Range Query System\n// =============================================================================\n\n/// Concept defining types that can be used in range queries\ntemplate<typename T>\nconcept Rangeable = std::totally_ordered<T> && std::copyable<T> && std::destructible<T>;\n\n/// Range bound types for different query patterns\nenum class RangeBound {\n    Exclusive,  // 25..30 (excludes end)\n    Inclusive   // 25..=30 (includes end) \n};\n\n/// Core Range type supporting all Rust range patterns\ntemplate<Rangeable T>\nstruct Range {\n    std::optional<T> start;  // None for ..30\n    std::optional<T> end;    // None for 25..\n    RangeBound bound_type = RangeBound::Exclusive;\n    \n    // Constructors for different range types\n    Range() = default;\n    \n    Range(std::optional<T> start_val, std::optional<T> end_val, RangeBound bound = RangeBound::Exclusive)\n        : start(start_val), end(end_val), bound_type(bound) {}\n    \n    // Copy/move constructors\n    Range(const Range&) = default;\n    Range(Range&&) = default;\n    Range& operator=(const Range&) = default;\n    Range& operator=(Range&&) = default;\n    \n    // Check if value is in range\n    constexpr bool contains(const T& value) const {\n        bool within_start = !start || value >= *start;\n        bool within_end;\n        \n        if (!end) {\n            within_end = true;  // No upper bound\n        } else {\n            within_end = (bound_type == RangeBound::Inclusive) \n                        ? (value <= *end)\n                        : (value < *end);\n        }\n        \n        return within_start && within_end;\n    }\n    \n    // Debug string representation\n    std::string to_string() const {\n        std::string result;\n        \n        if (start) {\n            result += std::to_string(*start);\n        }\n        \n        result += (bound_type == RangeBound::Inclusive) ? \"..=\" : \"..\";\n        \n        if (end) {\n            result += std::to_string(*end);\n        }\n        \n        return result;\n    }\n};\n\n// =============================================================================\n// Factory functions for different range patterns (Rust-like syntax)\n// =============================================================================\n\n/// Create range from start to end (exclusive): 25..30\ntemplate<Rangeable T>\nconstexpr auto range(T start, T end) -> Range<T> {\n    return Range<T>(start, end, RangeBound::Exclusive);\n}\n\n/// Create inclusive range from start to end: 25..=30  \ntemplate<Rangeable T>\nconstexpr auto range_inclusive(T start, T end) -> Range<T> {\n    return Range<T>(start, end, RangeBound::Inclusive);\n}\n\n/// Create range from start with no upper bound: 25..\ntemplate<Rangeable T>\nconstexpr auto range_from(T start) -> Range<T> {\n    return Range<T>(start, std::nullopt, RangeBound::Exclusive);\n}\n\n/// Create range with no lower bound to end (exclusive): ..30\ntemplate<Rangeable T>  \nconstexpr auto range_to(T end) -> Range<T> {\n    return Range<T>(std::nullopt, end, RangeBound::Exclusive);\n}\n\n/// Create inclusive range with no lower bound to end: ..=30\ntemplate<Rangeable T>\nconstexpr auto range_to_inclusive(T end) -> Range<T> {\n    return Range<T>(std::nullopt, end, RangeBound::Inclusive);\n}\n\n/// Create unbounded range (all values): ..\ntemplate<Rangeable T>\nconstexpr auto range_full() -> Range<T> {\n    return Range<T>();\n}\n\n// =============================================================================\n// Type trait to detect Range types\n// =============================================================================\n\ntemplate<typename T>\nstruct is_range : std::false_type {};\n\ntemplate<Rangeable T>\nstruct is_range<Range<T>> : std::true_type {};\n\ntemplate<typename T>\nconstexpr bool is_range_v = is_range<T>::value;\n\n// Concept for range types\ntemplate<typename T>\nconcept RangeType = is_range_v<T>;\n\n// =============================================================================\n// Integration with field accessors\n// =============================================================================\n\n/// Extend field accessor base class with range query support\ntemplate<typename Derived, typename FieldType>\nrequires Rangeable<FieldType>\nclass RangeQueryAccessor {\npublic:\n    /// Filter by exact value (existing functionality)\n    auto filter(const FieldType& value) {\n        return static_cast<Derived*>(this)->filter_impl(value);\n    }\n    \n    /// Filter by range (NEW - major feature addition)\n    auto filter(const Range<FieldType>& range) {\n        return static_cast<Derived*>(this)->filter_range_impl(range);\n    }\n    \n    /// Delete all rows in range (NEW)\n    uint32_t delete_range(const Range<FieldType>& range) {\n        return static_cast<Derived*>(this)->delete_range_impl(range);\n    }\n    \n    /// Count rows in range (NEW)\n    size_t count_range(const Range<FieldType>& range) {\n        return static_cast<Derived*>(this)->count_range_impl(range);\n    }\n};\n\n// =============================================================================\n// Convenient operator overloads for natural syntax\n// =============================================================================\n\n/// Enable range construction with custom operators\nnamespace range_operators {\n    /// Placeholder type for range construction\n    struct RangeStart {};\n    struct RangeEnd {};\n    \n    constexpr RangeStart range_start{};\n    constexpr RangeEnd range_end{};\n}\n\n} // namespace SpacetimeDB\n\n// =============================================================================\n// Usage Examples (for documentation)\n// =============================================================================\n\n/*\n// Rust patterns                    C++ equivalent\n// ---------------                  --------------\n// ctx.db.person().age().filter(25..)     → ctx.db[person_age].filter(range_from(25))\n// ctx.db.person().age().filter(..30)     → ctx.db[person_age].filter(range_to(30))  \n// ctx.db.person().age().filter(25..30)   → ctx.db[person_age].filter(range(25, 30))\n// ctx.db.person().age().filter(25..=30)  → ctx.db[person_age].filter(range_inclusive(25, 30))\n// ctx.db.person().age().filter(..)       → ctx.db[person_age].filter(range_full<uint8_t>())\n\nExample usage in a reducer:\n\nSPACETIMEDB_REDUCER(test_range_queries, ReducerContext ctx) {\n    using namespace SpacetimeDB;\n    \n    // Find all people aged 25 and above\n    auto adults = ctx.db[person_age].filter(range_from(25));\n    \n    // Find people aged 18-25 (exclusive end) \n    auto young_adults = ctx.db[person_age].filter(range(18, 25));\n    \n    // Find people up to and including age 65\n    auto working_age = ctx.db[person_age].filter(range_to_inclusive(65));\n    \n    // Delete all people in a specific age range\n    uint32_t deleted = ctx.db[person_age].delete_range(range(100, 150));\n    \n    // Count people in range\n    size_t count = ctx.db[person_age].count_range(range_from(50));\n}\n*/"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/readonly_database_context.h",
    "content": "#ifndef SPACETIMEDB_READONLY_DATABASE_CONTEXT_H\n#define SPACETIMEDB_READONLY_DATABASE_CONTEXT_H\n\n#include \"readonly_table_accessor.h\"\n#include \"readonly_field_accessors.h\"\n#include \"database.h\"\n#include <string>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Read-only database context for views\n * \n * ReadOnlyDatabaseContext provides a read-only interface to the database\n * for use in views. It prevents all mutation operations at compile-time.\n * \n * Key differences from DatabaseContext:\n * - No insert/update/delete operations\n * - No direct table iteration (prevents inefficient full table scans)\n * - Table data accessible ONLY through indexed field accessors\n * - Enforces efficient query patterns using indexes\n * \n * This is a completely separate type from DatabaseContext (no inheritance)\n * to match Rust's LocalReadOnly vs Local pattern.\n * \n * Example usage:\n * @code\n * SPACETIMEDB_VIEW(std::vector<User>, get_adults, Public, ViewContext ctx) {\n *     // Can only access via indexed fields\n *     std::vector<User> adults;\n *     for (const auto& user : ctx.db[user_age].filter(range_from(18u))) {\n *         adults.push_back(user);\n *     }\n *     return Ok(adults);\n * }\n * @endcode\n */\nclass ReadOnlyDatabaseContext {\npublic:\n    // Generic table accessor method (type-only, requires explicit table name later)\n    template<typename T>\n    ReadOnlyTableAccessor<T> table() const {\n        return ReadOnlyTableAccessor<T>{};\n    }\n    \n    // Name-based accessor that returns a configured table accessor\n    template<typename T>\n    ReadOnlyTableAccessor<T> table(const char* name) const {\n        return ReadOnlyTableAccessor<T>(std::string(name));\n    }\n    \n    // String overload\n    template<typename T>\n    ReadOnlyTableAccessor<T> table(const std::string& name) const {\n        return table<T>(name.c_str());\n    }\n    \n    // Tag-based accessor using operator[] (SpacetimeDB standard)\n    template<typename Tag>\n    ReadOnlyTableAccessor<typename Tag::type> operator[](const Tag&) const {\n        static_assert(!Tag::__is_event_internal,\n            \"Event tables are not accessible from views.\");\n        return ReadOnlyTableAccessor<typename Tag::type>(std::string(Tag::__table_name_internal));\n    }\n    \n    // Field tag accessors - read-only versions\n    // These return read-only field accessors that only support querying, not mutation\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    ReadOnlyPrimaryKeyAccessor<TableType, FieldType> operator[](\n        const FieldTag<TableType, FieldType, FieldConstraint::PrimaryKey, IsEventTable>& field_tag) const {\n        static_assert(!IsEventTable,\n            \"Event tables are not accessible from views.\");\n        return ReadOnlyPrimaryKeyAccessor<TableType, FieldType>(\n            field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    ReadOnlyUniqueAccessor<TableType, FieldType> operator[](\n        const FieldTag<TableType, FieldType, FieldConstraint::Unique, IsEventTable>& field_tag) const {\n        static_assert(!IsEventTable,\n            \"Event tables are not accessible from views.\");\n        return ReadOnlyUniqueAccessor<TableType, FieldType>(\n            field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    ReadOnlyIndexedAccessor<TableType, FieldType> operator[](\n        const FieldTag<TableType, FieldType, FieldConstraint::Indexed, IsEventTable>& field_tag) const {\n        static_assert(!IsEventTable,\n            \"Event tables are not accessible from views.\");\n        return ReadOnlyIndexedAccessor<TableType, FieldType>(\n            field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n    \n    template<typename TableType, typename FieldType, bool IsEventTable>\n    ReadOnlyRegularAccessor<TableType, FieldType> operator[](\n        const FieldTag<TableType, FieldType, FieldConstraint::None, IsEventTable>& field_tag) const {\n        static_assert(!IsEventTable,\n            \"Event tables are not accessible from views.\");\n        return ReadOnlyRegularAccessor<TableType, FieldType>(\n            field_tag.table_name, field_tag.field_name, field_tag.member_ptr);\n    }\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_READONLY_DATABASE_CONTEXT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/readonly_field_accessors.h",
    "content": "#ifndef SPACETIMEDB_READONLY_FIELD_ACCESSORS_H\n#define SPACETIMEDB_READONLY_FIELD_ACCESSORS_H\n\n#include \"table.h\"\n#include \"abi/FFI.h\"\n#include \"index_iterator.h\"\n#include \"range_queries.h\"\n#include \"logger.h\"\n#include <string>\n#include <vector>\n#include <optional>\n#include <type_traits>\n\nnamespace SpacetimeDB {\n\n// Forward declare to avoid circular dependency\nnamespace detail {\n    std::vector<std::string> get_index_patterns(const std::string& table_name, \n                                                const std::string& field_name,\n                                                FieldConstraint constraint_type);\n}\n\n/**\n * @brief Base class for read-only field accessors\n * \n * Provides common functionality for all read-only field accessors.\n * Read-only accessors only support querying operations, no mutations.\n */\ntemplate<typename TableType, typename FieldType>\nclass ReadOnlyFieldAccessorBase {\nprotected:\n    const char* table_name_;\n    const char* field_name_;\n    FieldType TableType::*member_ptr_;\n    mutable std::optional<TableId> table_id_;\n    mutable std::optional<IndexId> index_id_;\n    \n    TableId resolve_table_id() const {\n        if (!table_id_.has_value()) {\n            TableId id;\n            Status status = ::table_id_from_name(\n                reinterpret_cast<const uint8_t*>(table_name_),\n                std::strlen(table_name_),\n                &id\n            );\n            if (SpacetimeDB::is_error(status)) {\n                LOG_FATAL(std::string(\"Table not found: \") + table_name_);\n            }\n            table_id_ = id;\n        }\n        return *table_id_;\n    }\n    \n    IndexId resolve_index_with_patterns(const std::vector<std::string>& patterns) const {\n        if (index_id_.has_value()) {\n            return *index_id_;\n        }\n        \n        for (const auto& pattern : patterns) {\n            IndexId id;\n            Status status = ::index_id_from_name(\n                reinterpret_cast<const uint8_t*>(pattern.c_str()),\n                pattern.length(),\n                &id\n            );\n            if (is_ok(status)) {\n                index_id_ = id;\n                return id;\n            }\n        }\n        return IndexId{0};\n    }\n    \n    [[nodiscard]] virtual IndexId get_index_id() const = 0;\n    \n    FieldType get_field_value(const TableType& row) const {\n        return row.*member_ptr_;\n    }\n    \npublic:\n    ReadOnlyFieldAccessorBase(const char* table_name, const char* field_name, \n                              FieldType TableType::*member_ptr)\n        : table_name_(table_name), field_name_(field_name), member_ptr_(member_ptr) {}\n    \n    virtual ~ReadOnlyFieldAccessorBase() = default;\n    \n    // ❌ DELETED: No mutations allowed in views\n    bool insert(const TableType& row) const = delete;\n    bool delete_by_value(const FieldType& value) const = delete;\n    bool update(const TableType& new_row) const = delete;\n    uint32_t delete_all(const FieldType& value) const = delete;\n};\n\n/**\n * @brief Read-only accessor for primary key fields\n * \n * Allows only:\n * - find(key) -> std::optional<TableType>\n */\ntemplate<typename TableType, typename FieldType>\nclass ReadOnlyPrimaryKeyAccessor : public ReadOnlyFieldAccessorBase<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::PrimaryKey\n        );\n        IndexId id = this->resolve_index_with_patterns({patterns[0], patterns[1], patterns[2]});\n        if (id.inner == 0) {\n            LOG_FATAL(std::string(\"Failed to resolve index for primary key field: \") + \n                     this->table_name_ + \".\" + this->field_name_);\n        }\n        return id;\n    }\n    \npublic:\n    using ReadOnlyFieldAccessorBase<TableType, FieldType>::ReadOnlyFieldAccessorBase;\n    \n    /**\n     * Find a single row by primary key value\n     * Returns std::nullopt if not found\n     */\n    std::optional<TableType> find(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            IndexIterator<TableType> iter(index_id, value);\n            if (iter != IndexIterator<TableType>()) {\n                return *iter;\n            }\n        }\n        return std::nullopt;\n    }\n    \n    /**\n     * Try to get a row by primary key value\n     * Alias for find() for consistency with writable accessor\n     */\n    std::optional<TableType> try_get(const FieldType& value) const {\n        return find(value);\n    }\n};\n\n/**\n * @brief Read-only accessor for unique fields\n * \n * Allows only:\n * - find(key) -> std::optional<TableType>\n */\ntemplate<typename TableType, typename FieldType>\nclass ReadOnlyUniqueAccessor : public ReadOnlyFieldAccessorBase<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::Unique\n        );\n        IndexId id = this->resolve_index_with_patterns({patterns[0], patterns[1], patterns[2]});\n        if (id.inner == 0) {\n            LOG_FATAL(std::string(\"Failed to resolve index for unique field: \") + \n                     this->table_name_ + \".\" + this->field_name_);\n        }\n        return id;\n    }\n    \npublic:\n    using ReadOnlyFieldAccessorBase<TableType, FieldType>::ReadOnlyFieldAccessorBase;\n    \n    /**\n     * Find a single row by unique field value\n     * Returns std::nullopt if not found\n     */\n    std::optional<TableType> find(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            IndexIterator<TableType> iter(index_id, value);\n            if (iter != IndexIterator<TableType>()) {\n                return *iter;\n            }\n        }\n        return std::nullopt;\n    }\n};\n\n/**\n * @brief Read-only accessor for indexed (non-unique) fields\n * \n * Allows only:\n * - filter(value) -> lazy IndexIterator over matching rows\n * - filter(range) -> lazy IndexIterator over range-matched rows\n * \n * IndexIterator supports both traditional and range-based for loops.\n * Results are evaluated lazily without materializing all matches.\n * Call .collect() to materialize results into a std::vector if needed.\n */\ntemplate<typename TableType, typename FieldType>\nclass ReadOnlyIndexedAccessor : public ReadOnlyFieldAccessorBase<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::Indexed\n        );\n        if (patterns.size() >= 2) {\n            return this->resolve_index_with_patterns({patterns[0], patterns[1]});\n        } else if (patterns.size() == 1) {\n            return this->resolve_index_with_patterns({patterns[0]});\n        }\n        LOG_FATAL(std::string(\"Failed to resolve index for indexed field: \") + \n                 this->table_name_ + \".\" + this->field_name_);\n        return IndexId{0};\n    }\n    \npublic:\n    using ReadOnlyFieldAccessorBase<TableType, FieldType>::ReadOnlyFieldAccessorBase;\n    \n    /**\n     * Filter rows by exact field value using index\n     * Returns lazy IndexIterator - results evaluated during iteration\n     * Always use via range-based for loop for clean syntax\n     * \n     * Example:\n     * for (const auto& person : ctx.db[person_age].filter(25u)) {\n     *     // process persons aged 25 - no materialization overhead\n     * }\n     * \n     * To materialize all matching rows into a vector:\n     * auto all_aged_25 = ctx.db[person_age].filter(25u).collect();\n     */\n    IndexIteratorRange<TableType> filter(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>(index_id, value));\n        }\n        return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n    }\n    \n    /**\n     * Filter rows by range using index\n     * Returns lazy IndexIterator - results evaluated during iteration\n     * Always use via range-based for loop for clean syntax\n     * \n     * Example:\n     * for (const auto& person : ctx.db[person_age].filter(range_from(18u))) {\n     *     // process persons aged 18+ - no materialization overhead\n     * }\n     */\n    template<typename RangeType>\n    std::enable_if_t<is_range_v<RangeType>, IndexIteratorRange<TableType>>\n    filter(const RangeType& range) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>(index_id, range));\n        }\n        return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n    }\n};\n\n/**\n * @brief Read-only accessor for regular (non-indexed) fields\n * \n * Regular fields have no index, so they cannot be queried efficiently in views.\n * All methods are deleted to enforce this at compile-time.\n */\ntemplate<typename TableType, typename FieldType>\nclass ReadOnlyRegularAccessor {\nprivate:\n    const char* table_name_;\n    const char* field_name_;\n    FieldType TableType::*member_ptr_;\n    \npublic:\n    ReadOnlyRegularAccessor(const char* table_name, const char* field_name, \n                           FieldType TableType::*member_ptr)\n        : table_name_(table_name), field_name_(field_name), member_ptr_(member_ptr) {}\n    \n    // ❌ DELETED: Regular fields have no index, cannot be queried in views\n    // Views must use indexed, unique, or primary key fields for queries\n    auto filter(const FieldType& value) const = delete;\n    std::optional<TableType> find(const FieldType& value) const = delete;\n    \n    // ❌ DELETED: No mutations allowed in views\n    bool insert(const TableType& row) const = delete;\n    bool delete_by_value(const FieldType& value) const = delete;\n    bool update(const TableType& new_row) const = delete;\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_READONLY_FIELD_ACCESSORS_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/readonly_table_accessor.h",
    "content": "#ifndef SPACETIMEDB_READONLY_TABLE_ACCESSOR_H\n#define SPACETIMEDB_READONLY_TABLE_ACCESSOR_H\n\n#include \"table.h\"\n#include \"abi/FFI.h\"\n#include \"logger.h\"\n#include <string>\n#include <optional>\n#include <type_traits>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Read-only table accessor for views\n * \n * ReadOnlyTableAccessor provides compile-time read-only access to tables.\n * Unlike TableAccessor, it:\n * - Deletes all mutation operations (insert/update/delete)\n * - Deletes direct iteration methods (begin/end/collect)\n * - Forces use of indexed field accessors for efficient queries\n * \n * This design enforces two critical properties for views:\n * 1. Read-only access (no mutations)\n * 2. Efficient queries (no full table scans)\n * \n * Table data is ONLY accessible through indexed field accessors:\n * - ctx.db[table_field].filter(range) for indexed fields\n * - ctx.db[table_field].find(key) for unique/primary key fields\n * - ctx.db[table].count() for counting (doesn't require iteration)\n * \n * Example usage:\n * @code\n * // ✅ Allowed - query via indexed field\n * for (const auto& person : ctx.db[person_age].filter(range_from(18u))) {\n *     process(person);\n * }\n * \n * // ✅ Allowed - count doesn't require iteration\n * uint64_t total = ctx.db[person].count();\n * \n * // ❌ Compile error - no direct iteration\n * for (const auto& person : ctx.db[person]) { }  // DELETED\n * \n * // ❌ Compile error - no collect()\n * auto all = ctx.db[person].collect();  // DELETED\n * \n * // ❌ Compile error - no mutations\n * ctx.db[person].insert(new_person);  // DELETED\n * @endcode\n */\ntemplate<typename T>\nclass ReadOnlyTableAccessor {\nprotected:\n    mutable std::optional<TableId> table_id_;\n    std::string table_name_;\n    \n    TableId resolve_table_id() const {\n        if (!table_id_.has_value()) {\n            std::string name_to_use = table_name_;\n            \n            if (name_to_use.empty()) {\n                LOG_FATAL(\"Table name is required\");\n            }\n            \n            TableId id;\n            Status status = ::table_id_from_name(\n                reinterpret_cast<const uint8_t*>(name_to_use.c_str()),\n                name_to_use.length(),\n                &id\n            );\n            \n            if (SpacetimeDB::is_error(status)) {\n                LOG_FATAL(\"Table not found: \" + name_to_use);\n            }\n            table_id_ = id;\n        }\n        return *table_id_;\n    }\n    \npublic:\n    // Constructor that accepts a table name\n    ReadOnlyTableAccessor() = default;\n    explicit ReadOnlyTableAccessor(const std::string& table_name) : table_name_(table_name) {}\n    \n    // ✅ ALLOWED: Count rows (doesn't require iteration)\n    uint64_t count() const {\n        TableId table_id = resolve_table_id();\n        uint64_t out_count;\n        Status status = ::datastore_table_row_count(table_id, &out_count);\n        if (SpacetimeDB::is_error(status)) {\n            LOG_FATAL(\"Failed to count rows in table: \" + table_name_);\n        }\n        return out_count;\n    }\n    \n    // ❌ DELETED: No direct iteration - prevents inefficient full table scans\n    // Views must use indexed field accessors: ctx.db[table_field].filter(range)\n    auto begin() const = delete;\n    auto end() const = delete;\n    \n    // ❌ DELETED: No collect() - prevents inefficient full table scans\n    // Views must use indexed field accessors to retrieve data\n    std::vector<T> collect() const = delete;\n    \n    // ❌ DELETED: No table() method - would bypass read-only protection\n    SpacetimeDB::Table<T> table() const = delete;\n    SpacetimeDB::Table<T> get_table() const = delete;\n    \n    // ❌ DELETED: No mutations allowed in views\n    T insert(const T& row) const = delete;\n    uint32_t delete_by_value(const T& value) const = delete;\n    uint32_t update_by_value(const T& old_value, const T& new_value) const = delete;\n};\n\n} // namespace SpacetimeDB\n\n// Use spacetimedb namespace for consistency\nnamespace spacetimedb {\n    template<typename T>\n    using ReadOnlyTableAccessor = SpacetimeDB::ReadOnlyTableAccessor<T>;\n}\n\n#endif // SPACETIMEDB_READONLY_TABLE_ACCESSOR_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/reducer_context.h",
    "content": "#ifndef REDUCER_CONTEXT_H\n#define REDUCER_CONTEXT_H\n\n#include <spacetimedb/bsatn/types.h> // For Identity, ConnectionId\n#include <spacetimedb/bsatn/timestamp.h> // For Timestamp\n#include <spacetimedb/bsatn/uuid.h> // For Uuid\n#include <spacetimedb/random.h> // For StdbRng\n#include <spacetimedb/auth_ctx.h> // For AuthCtx\n#include <optional>\n#include <array>\n#include <memory>\n\n// Include database for DatabaseContext\n#include <spacetimedb/database.h>\n\nnamespace SpacetimeDB {\n\n// Enhanced ReducerContext with database access - matches Rust pattern\nstruct ReducerContext {\nprivate:\n    Identity sender_;\n\npublic:\n    // Core fields - sender is exposed via sender() like Rust, other fields remain directly accessible\n    std::optional<ConnectionId> connection_id;\n    Timestamp timestamp;\n    \n    // Database context with name-based access\n    DatabaseContext db;\n    \nprivate:\n    // Authentication context with lazy JWT loading (private like in Rust)\n    AuthCtx sender_auth_;\n    \n    // Lazily initialized RNG (similar to Rust's OnceCell pattern)\n    // Using shared_ptr to make ReducerContext copyable\n    mutable std::shared_ptr<StdbRng> rng_instance;\n    \n    // Monotonic counter for UUID v7 generation (31 bits, wraps around)\n    mutable uint32_t counter_uuid_ = 0;\n    \npublic:\n    Identity sender() const {\n        return sender_;\n    }\n\n    // Returns the authorization information for the caller of this reducer\n    const AuthCtx& sender_auth() const {\n        return sender_auth_;\n    }\n    \n    // Get the random number generator for this reducer call\n    // Lazily initialized and seeded with the timestamp\n    StdbRng& rng() const {\n        if (!rng_instance) {\n            rng_instance = std::make_unique<StdbRng>(timestamp);\n        }\n        return *rng_instance;\n    }\n\n    Identity identity() const {\n        std::array<uint8_t, 32> buffer;\n        ::identity(buffer.data());\n        return Identity(buffer);\n    }\n    \n    /**\n     * Generate a new random UUID v4.\n     * \n     * Creates a random UUID using the reducer's deterministic RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_REDUCER(void, create_session, ReducerContext ctx) {\n     *     Uuid session_id = ctx.new_uuid_v4();\n     *     ctx.db[sessions].insert(Session{session_id});\n     * }\n     * @endcode\n     * \n     * @return A new UUID v4\n     */\n    Uuid new_uuid_v4() const {\n        // Get 16 random bytes from the context RNG\n        std::array<uint8_t, 16> random_bytes;\n        rng().fill_bytes(random_bytes.data(), 16);\n        \n        // Generate UUID v4\n        return Uuid::from_random_bytes_v4(random_bytes);\n    }\n    \n    /**\n     * Generate a new UUID v7.\n     * \n     * Creates a time-ordered UUID with the reducer's timestamp, a monotonic counter,\n     * and random bytes from the reducer's deterministic RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_REDUCER(void, create_user, ReducerContext ctx, std::string name) {\n     *     Uuid user_id = ctx.new_uuid_v7();\n     *     ctx.db[users].insert(User{user_id, name});\n     * }\n     * @endcode\n     * \n     * @return A new UUID v7\n     */\n    Uuid new_uuid_v7() const {\n        // Get 4 random bytes from the context RNG\n        std::array<uint8_t, 4> random_bytes;\n        rng().fill_bytes(random_bytes.data(), 4);\n        \n        // Generate UUID v7 with timestamp and counter\n        return Uuid::from_counter_v7(counter_uuid_, timestamp, random_bytes);\n    }\n\n    // Constructors\n    ReducerContext() : sender_auth_(AuthCtx::internal()) {}\n    \n    ReducerContext(Identity s, std::optional<ConnectionId> cid, Timestamp ts)\n        : sender_(s), connection_id(cid), timestamp(ts), \n          sender_auth_(AuthCtx::from_connection_id_opt(cid, s)) {}\n};\n\n} // namespace SpacetimeDB\n\n#endif // REDUCER_CONTEXT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/reducer_error.h",
    "content": "#ifndef SPACETIMEDB_REDUCER_ERROR_H\n#define SPACETIMEDB_REDUCER_ERROR_H\n\n#include \"spacetimedb/outcome.h\"\n#include <string>\n#include <optional>\n\n/**\n * @file reducer_error.h\n * @brief Graceful error handling for reducers using Outcome<T> pattern\n *\n * This module provides reducer-specific error handling utilities using the Outcome type.\n * For the general Outcome<T> type definition, see outcome.h.\n *\n * Modern usage (recommended):\n * @code\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {\n *     if (id == 0) {\n *         return Err(\"ID must be non-zero\");\n *     }\n *     // ... rest of logic\n *     return Ok();\n * }\n * @endcode\n *\n * Legacy usage (still supported):\n * @code\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {\n *     if (id == 0) {\n *         fail_reducer(\"ID must be non-zero\");\n *         return Ok();  // Or just return;\n *     }\n *     // ... rest of logic\n *     return Ok();\n * }\n * @endcode\n *\n * When a reducer returns Err():\n * - The transaction is rolled back (not committed to the log)\n * - The error message is captured and returned to the caller\n * - No database changes are persisted\n * - No WASM crash or panic occurs\n *\n * @ingroup sdk_runtime\n */\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Type alias for reducer return type, matching Rust's ReducerResult\n * \n * Reducers use Outcome<void> for their return type. The Ok() and Err() helper\n * functions from outcome.h can be used directly:\n * \n * @code\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {\n *     if (id == 0) {\n *         return Err(\"ID must be non-zero\");\n *     }\n *     // ... rest of logic\n *     return Ok();\n * }\n * @endcode\n */\nusing ReducerResult = Outcome<void>;\n\nnamespace Internal {\n    /**\n     * Thread-local error state for the current reducer invocation.\n     * This is cleared at the start of each reducer call and checked at the end.\n     */\n    extern thread_local std::optional<std::string> g_reducer_error_message;\n\n    /**\n     * Clear the error state. Called automatically at the start of each reducer.\n     * @internal\n     */\n    inline void clear_reducer_error() {\n        g_reducer_error_message = std::nullopt;\n    }\n\n    /**\n     * Check if the current reducer has failed.\n     * @internal\n     */\n    inline bool has_reducer_error() {\n        return g_reducer_error_message.has_value();\n    }\n\n    /**\n     * Get the error message if one exists.\n     * @internal\n     */\n    inline std::string get_reducer_error() {\n        return g_reducer_error_message.value_or(std::string());\n    }\n}\n\n/**\n * @brief Fail the current reducer with an error message.\n *\n * This function marks the current reducer invocation as failed.\n * The transaction will be rolled back and the error message will be\n * returned to the caller. Failed transactions are NOT committed to the\n * log and will not appear in temporal queries or transaction history.\n *\n * After calling this function, the reducer should return immediately\n * to avoid executing additional logic on inconsistent state.\n *\n * @param message A descriptive error message explaining why the reducer failed\n *\n * @code\n * if (amount <= 0) {\n *     SpacetimeDB::fail_reducer(\"Amount must be positive\");\n *     return Ok();  // Or just return;\n * }\n *\n * // Alternative using Outcome pattern:\n * if (amount <= 0) {\n *     return Err(\"Amount must be positive\");\n * }\n * @endcode\n *\n * @note This is thread-safe and works in WASM single-threaded environments.\n * @note This does NOT throw exceptions or cause panics.\n */\ninline void fail_reducer(std::string message) {\n    Internal::g_reducer_error_message = std::move(message);\n}\n\n/**\n * @brief Fail the current reducer with a formatted error message.\n *\n * Convenience function for creating formatted error messages.\n *\n * @tparam Args Variadic template arguments for formatting\n * @param format Printf-style format string\n * @param args Arguments to format into the message\n *\n * @code\n * fail_reducer_fmt(\"Tracker %u not found\", tracker_id);\n * fail_reducer_fmt(\"Invalid coordinates (%.2f, %.2f)\", lat, lon);\n * @endcode\n */\ntemplate<typename... Args>\ninline void fail_reducer_fmt(const char* format, Args... args) {\n    // Get required size\n    int size = std::snprintf(nullptr, 0, format, args...);\n    if (size <= 0) {\n        fail_reducer(\"Error formatting failure message\");\n        return;\n    }\n\n    // Format string\n    std::string result(size + 1, '\\0');\n    std::snprintf(result.data(), result.size(), format, args...);\n    result.resize(size); // Remove null terminator\n\n    fail_reducer(std::move(result));\n}\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_REDUCER_ERROR_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/reducer_macros.h",
    "content": "#pragma once\n\n#include \"spacetimedb/bsatn/types.h\"\n#include \"spacetimedb/reducer_context.h\"\n#include \"spacetimedb/internal/Module.h\"\n#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/macros.h\"\n\n#include <string>\n#include <vector>\n#include <sstream>\n\n// Note: parseParameterNames() is now in macros.h for reuse across reducers and views\n\n/**\n * @brief Unified SPACETIMEDB_REDUCER macro for defining SpacetimeDB reducers\n * \n * This macro provides a clean, consistent syntax for defining reducers with\n * automatic registration in the SpacetimeDB module system.\n * \n * Reducers now return SpacetimeDB::ReducerResult to support Result-based\n * error handling matching Rust's Result<(), E> pattern.\n * \n * @usage\n * ```cpp\n * // Reducer with no extra parameters\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx) {\n *     if (some_error) {\n *         return Err(\"Error message\");\n *     }\n *     ctx.db.table<MyTable>(\"my_table\").insert(MyTable{});\n *     return Ok();\n * }\n * \n * // Reducer with parameters\n * SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id, std::string name) {\n *     if (id == 0) {\n *         return Err(\"ID must be non-zero\");\n *     }\n *     ctx.db.table<MyTable>(\"my_table\").insert(MyTable{id, name});\n *     return Ok();\n * }\n * ```\n * \n * @param name The name of the reducer function\n * @param ctx_param Must be ReducerContext ctx\n * @param ... Additional parameters (optional)\n * \n * @details\n * The macro generates:\n * 1. A function declaration and definition with ReducerResult return type\n * 2. A preinit registration function that registers the reducer with SpacetimeDB\n * \n * The first parameter must always be `ReducerContext ctx`. Additional parameters\n * can be any types that support BSATN serialization.\n */\n#undef SPACETIMEDB_REDUCER\n#define SPACETIMEDB_REDUCER(name, ctx_param, ...) \\\n    /* Forward declaration - returns ReducerResult */ \\\n    SpacetimeDB::ReducerResult name(ctx_param __VA_OPT__(,) __VA_ARGS__); \\\n    \\\n    /* Preinit registration function */ \\\n    /* This function is called during module initialization to register the reducer */ \\\n    __attribute__((export_name(\"__preinit__30_reducer_\" #name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_, name)() { \\\n        /* Parse parameter names from the stringified parameter list */ \\\n        std::string param_list = #__VA_ARGS__; \\\n        std::vector<std::string> param_names = \\\n            SpacetimeDB::Internal::parseParameterNames(param_list); \\\n        /* Register the reducer with the unified V9Builder system */ \\\n        SpacetimeDB::Internal::getV10Builder().RegisterReducer(#name, name, param_names); \\\n    } \\\n    \\\n    /* The actual reducer function definition - returns ReducerResult */ \\\n    SpacetimeDB::ReducerResult name(ctx_param __VA_OPT__(,) __VA_ARGS__)\n\n#define SPACETIMEDB_REDUCER_NAMED(name, canonical_name, ctx_param, ...) \\\n    SpacetimeDB::ReducerResult name(ctx_param __VA_OPT__(,) __VA_ARGS__); \\\n    __attribute__((export_name(\"__preinit__30_reducer_\" #name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_, name)() { \\\n        std::string param_list = #__VA_ARGS__; \\\n        std::vector<std::string> param_names = \\\n            SpacetimeDB::Internal::parseParameterNames(param_list); \\\n        SpacetimeDB::Internal::getV10Builder().RegisterReducer(#name, name, param_names); \\\n        SpacetimeDB::Module::RegisterExplicitFunctionName(#name, canonical_name); \\\n    } \\\n    SpacetimeDB::ReducerResult name(ctx_param __VA_OPT__(,) __VA_ARGS__)\n\n// -----------------------------------------------------------------------------\n// Lifecycle Reducer Macros\n// -----------------------------------------------------------------------------\n\n// Use unified macro system from macro_helpers.h\n\n/**\n * @brief Macro for defining an init reducer\n * \n * Init reducers are called when the module is first initialized.\n * They require an explicit ReducerContext parameter and return ReducerResult.\n * \n * @usage\n * ```cpp\n * SPACETIMEDB_INIT(my_init, ReducerContext ctx) {\n *     ctx.db.table<MyTable>().insert({...});\n *     return Ok();\n * }\n * ```\n */\n#ifdef SPACETIMEDB_INIT\n#undef SPACETIMEDB_INIT\n#endif\n#define SPACETIMEDB_INIT(function_name, ctx_param) \\\n    SpacetimeDB::ReducerResult function_name(ctx_param); \\\n    __attribute__((export_name(\"__preinit__20_reducer_init\"))) \\\n    extern \"C\" void CONCAT(_preinit_register_init_reducer_, function_name)() { \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterLifecycleReducer(#function_name, function_name, ::SpacetimeDB::Internal::Lifecycle::Init); \\\n    } \\\n    SpacetimeDB::ReducerResult function_name(ctx_param)\n\n/**\n * @brief Macro for defining a client_connected reducer\n * \n * Client connected reducers require an explicit ReducerContext parameter and return ReducerResult.\n * \n * @usage\n * ```cpp\n * SPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {\n *     LOG_INFO(\"Client connected: \" + ctx.sender().to_hex());\n *     return Ok();\n * }\n * ```\n */\n#ifdef SPACETIMEDB_CLIENT_CONNECTED\n#undef SPACETIMEDB_CLIENT_CONNECTED\n#endif\n#define SPACETIMEDB_CLIENT_CONNECTED(function_name, ctx_param) \\\n    SpacetimeDB::ReducerResult function_name(ctx_param); \\\n    __attribute__((export_name(\"__preinit__20_reducer_client_connected\"))) \\\n    extern \"C\" void CONCAT(_preinit_register_client_connected_, function_name)() { \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterLifecycleReducer(#function_name, function_name, ::SpacetimeDB::Internal::Lifecycle::OnConnect); \\\n    } \\\n    SpacetimeDB::ReducerResult function_name(ctx_param)\n\n/**\n * @brief Macro for defining a client_disconnected reducer\n * \n * Client disconnected reducers require an explicit ReducerContext parameter and return ReducerResult.\n * \n * @usage\n * ```cpp\n * SPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {\n *     LOG_INFO(\"Client disconnected: \" + ctx.sender().to_hex());\n *     return Ok();\n * }\n * ```\n */\n#ifdef SPACETIMEDB_CLIENT_DISCONNECTED\n#undef SPACETIMEDB_CLIENT_DISCONNECTED\n#endif\n#define SPACETIMEDB_CLIENT_DISCONNECTED(function_name, ctx_param) \\\n    SpacetimeDB::ReducerResult function_name(ctx_param); \\\n    __attribute__((export_name(\"__preinit__20_reducer_client_disconnected\"))) \\\n    extern \"C\" void CONCAT(_preinit_register_client_disconnected_, function_name)() { \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterLifecycleReducer(#function_name, function_name, ::SpacetimeDB::Internal::Lifecycle::OnDisconnect); \\\n    } \\\n    SpacetimeDB::ReducerResult function_name(ctx_param)\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/schedule_reducer.h",
    "content": "#ifndef SPACETIMEDB_SCHEDULE_REDUCER_H\n#define SPACETIMEDB_SCHEDULE_REDUCER_H\n\n// ============================================================================\n// DEPRECATED: This file contains unused dead code\n// ============================================================================\n// This file documented a runtime registration API for scheduled reducers that\n// was never implemented. The actual C++ scheduled table API uses:\n// - SPACETIMEDB_SCHEDULE(table_name, column_index, reducer_name) macro\n// - ScheduleAt type for scheduled_at columns\n// - See table_with_constraints.h for the real implementation\n// - See modules/module-test-cpp/src/lib.cpp for working examples\n//\n// All code below is commented out pending deletion.\n// ============================================================================\n\n/*\n#include \"spacetimedb/bsatn/types.h\"\n#include <chrono>\n#include <string>\n\nnamespace SpacetimeDB {\n\nstruct Duration {\n    uint64_t milliseconds;\n    \n    Duration(uint64_t ms) : milliseconds(ms) {}\n    \n    static Duration from_seconds(uint64_t seconds) {\n        return Duration(seconds * 1000);\n    }\n    \n    static Duration from_minutes(uint64_t minutes) {\n        return Duration(minutes * 60 * 1000);\n    }\n    \n    static Duration from_hours(uint64_t hours) {\n        return Duration(hours * 60 * 60 * 1000);\n    }\n    \n    static Duration from_milliseconds(uint64_t ms) {\n        return Duration(ms);\n    }\n    \n    template<typename Rep, typename Period>\n    static Duration from_chrono(std::chrono::duration<Rep, Period> duration) {\n        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);\n        return Duration(static_cast<uint64_t>(ms.count()));\n    }\n};\n\nclass ScheduleReducer {\npublic:\n    static void register_scheduled([[maybe_unused]] const char* reducer_name, [[maybe_unused]] Duration interval) {\n        #ifdef DEBUG\n        SpacetimeDB::Log::debug(\"Scheduling reducer\", reducer_name, \"with interval\", \n                                std::to_string(interval.milliseconds), \"ms\");\n        #endif\n    }\n    \n    static void register_scheduled_at([[maybe_unused]] const char* reducer_name) {\n        #ifdef DEBUG\n        SpacetimeDB::Log::debug(\"Registering scheduled_at reducer\", reducer_name);\n        #endif\n    }\n    \n    static bool validate_cron_expression(const std::string& cron_expr) {\n        if (cron_expr.empty()) return false;\n        \n        int spaces = 0;\n        for (char c : cron_expr) {\n            if (c == ' ') spaces++;\n        }\n        \n        return spaces == 4;\n    }\n};\n\n} // namespace SpacetimeDB\n\nnamespace SpacetimeDB {\n    using Duration = SpacetimeDB::Duration;\n    using ScheduleReducer = SpacetimeDB::ScheduleReducer;\n}\n*/\n\n#endif // SPACETIMEDB_SCHEDULE_REDUCER_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/table.h",
    "content": "#ifndef SPACETIMEDB_LIBRARY_TABLE_H\n#define SPACETIMEDB_LIBRARY_TABLE_H\n\n/**\n * @file table.h\n * @brief Core table operations for SpacetimeDB C++ bindings\n * \n * Provides type-safe Table<T> class and TableIterator<T> for CRUD operations\n * on SpacetimeDB tables with efficient batch iteration and STL compatibility.\n */\n\n#include \"spacetimedb/bsatn/types.h\"\n#include <spacetimedb/bsatn/bsatn.h>\n#include <spacetimedb/abi/FFI.h>\n#include <spacetimedb/bsatn/reader.h>\n#include <spacetimedb/bsatn/writer.h>\n#include <spacetimedb/bsatn/traits.h>\n#include <spacetimedb/logger.h>\n\n#include <string>\n#include <vector>\n#include <stdexcept>\n#include <optional>\n#include <algorithm>\n#include <utility>\n\nnamespace SpacetimeDB {\n\n// Forward declarations\ntemplate<typename T> class Table;\ntemplate<typename T> class TableIterator;\n\n// =============================================================================\n// Insert Error Handling\n// =============================================================================\n\n/**\n * Error types for insert operations\n */\nenum class InsertErrorType {\n    UniqueConstraintViolation,\n    AutoIncOverflow,\n    Other\n};\n\n/**\n * Insert error details\n */\nstruct InsertError {\n    InsertErrorType type;\n    Status status_code;\n    std::string message;\n    \n    InsertError(InsertErrorType t, Status s, const std::string& msg) \n        : type(t), status_code(s), message(msg) {}\n};\n\n/**\n * Result type for try_insert operations\n */\ntemplate<typename T>\nclass InsertResult {\nprivate:\n    std::variant<T, InsertError> result_;\n    \npublic:\n    InsertResult(T&& value) : result_(std::move(value)) {}\n    InsertResult(const InsertError& error) : result_(error) {}\n    \n    bool is_ok() const { return std::holds_alternative<T>(result_); }\n    bool is_err() const { return !is_ok(); }\n    \n    const T& ok() const { return std::get<T>(result_); }\n    T&& take_ok() { return std::move(std::get<T>(result_)); }\n    \n    const InsertError& err() const { return std::get<InsertError>(result_); }\n};\n\n// =============================================================================\n// Implementation Details\n// =============================================================================\n\nnamespace detail {\n    // Performance tuning constants\n    constexpr size_t INITIAL_ROW_BUFFER_SIZE = 128 * 1024; // default to 128KB like C#\n    constexpr size_t MAX_ROW_BUFFER_SIZE = 1024 * 1024;\n    constexpr size_t TYPICAL_BATCH_SIZE = 32;\n    constexpr size_t AUTO_INCREMENT_BUFFER_SPACE = 1024;\n    \n    // FFI result codes\n    constexpr int16_t ITER_EXHAUSTED = -1;\n    constexpr int16_t ITER_OK = 0;\n    constexpr uint16_t ERROR_BUFFER_TOO_SMALL = 11;\n    \n    /**\n     * Auto-Increment Integration System\n     * \n     * This system enables automatic integration of generated auto-increment values\n     * back into user row objects after insert operations. The system uses function\n     * pointers registered during module initialization to handle the integration\n     * per struct type.\n     */\n    \n    /** Function pointer type for auto-increment integration callbacks */\n    template<typename T>\n    using AutoIncIntegratorFn = void(*)(T&, SpacetimeDB::bsatn::Reader&);\n    \n    /** Registry to store auto-increment integrators per type */\n    template<typename T>\n    inline AutoIncIntegratorFn<T>& get_autoinc_integrator() {\n        static AutoIncIntegratorFn<T> integrator = nullptr;\n        return integrator;\n    }\n    \n    /**\n     * Integrate auto-increment values into a row object.\n     * \n     * This function is called automatically by Table::insert() when SpacetimeDB\n     * returns generated auto-increment values. It looks up the registered integrator\n     * for the struct type and calls it to update the row with generated values.\n     * \n     * @param row The row object to update with generated values\n     * @param reader BSATN reader containing the generated column values\n     */\n    template<typename T>\n    void integrate_autoinc(T& row, SpacetimeDB::bsatn::Reader& reader) {\n        auto integrator = get_autoinc_integrator<T>();\n        if (integrator) {\n            integrator(row, reader);\n        }\n        // If no integrator registered, do nothing (no auto-increment fields)\n    }\n    \n    // Error handling utilities\n    inline std::string format_error(const std::string& context, \n                                   const std::string& operation, \n                                   int code) {\n        return context + \": \" + operation + \" failed with code \" + std::to_string(code);\n    }\n    \n    inline void check_buffer_size(size_t size) {\n        if (size > MAX_ROW_BUFFER_SIZE) {\n            LOG_FATAL(\"Buffer size exceeds maximum limit\");\n        }\n    }\n    \n    // Generic error handler for FFI operations\n    template<typename Op>\n    inline void handle_ffi_error(Status status, \n                                [[maybe_unused]] const std::string& context, \n                                [[maybe_unused]] Op operation) {\n        if (is_error(status)) {\n            LOG_FATAL(\"FFI operation failed: \" + context);\n        }\n    }\n}\n\n// =============================================================================\n// TableIterator - Efficient batch iteration\n// =============================================================================\n\ntemplate<typename T>\nclass TableIterator {\n    static_assert(std::is_same_v<T, std::remove_cv_t<T>>, \n                  \"TableIterator requires non-const, non-volatile type\");\n\npublic:\n    // STL iterator type definitions\n    using iterator_category = std::input_iterator_tag;\n    using value_type = T;\n    using difference_type = std::ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n\n    // Constructors\n    TableIterator() noexcept : iter_handle_(Invalid::ROW_ITER), is_end_(true) {}\n    \n    explicit TableIterator(TableId table_id) {\n        Status status = FFI::datastore_table_scan_bsatn(table_id, &iter_handle_);\n        detail::handle_ffi_error(status, \"TableIterator\", \"datastore_table_scan_bsatn\");\n        advance();\n    }\n\n    ~TableIterator() noexcept {\n        if (iter_handle_ != Invalid::ROW_ITER && !ffi_exhausted_) {\n            FFI::row_iter_bsatn_close(iter_handle_);\n        }\n    }\n\n    // Move-only semantics\n    TableIterator(const TableIterator&) = delete;\n    TableIterator& operator=(const TableIterator&) = delete;\n    \n    TableIterator(TableIterator&& other) noexcept : TableIterator() {\n        swap(*this, other);\n    }\n    \n    TableIterator& operator=(TableIterator&& other) noexcept {\n        if (this != &other) {\n            TableIterator temp(std::move(other));\n            swap(*this, temp);\n        }\n        return *this;\n    }\n    \n    friend void swap(TableIterator& a, TableIterator& b) noexcept {\n        using std::swap;\n        swap(a.iter_handle_, b.iter_handle_);\n        swap(a.row_buffer_, b.row_buffer_);\n        swap(a.current_batch_, b.current_batch_);\n        swap(a.current_index_, b.current_index_);\n        swap(a.current_row_, b.current_row_);\n        swap(a.is_valid_, b.is_valid_);\n        swap(a.is_end_, b.is_end_);\n        swap(a.ffi_exhausted_, b.ffi_exhausted_);\n    }\n\n    // Iterator operations\n\n    // Returns mutable reference to current row\n    T& operator*() {\n        if (!is_valid_) LOG_FATAL(\"Attempted to dereference invalid iterator\");\n        return current_row_;\n    }\n\n    const T& operator*() const {\n        if (!is_valid_) LOG_FATAL(\"Attempted to dereference invalid iterator\");\n        return current_row_;\n    }\n    \n    const T* operator->() const { return &**this; }\n    \n    TableIterator& operator++() {\n        advance();\n        return *this;\n    }\n    \n    bool operator==(const TableIterator& other) const noexcept {\n        return is_valid_ == other.is_valid_;\n    }\n    \n    bool operator!=(const TableIterator& other) const noexcept {\n        return !(*this == other);\n    }\n\nprivate:\n    RowIter iter_handle_ = Invalid::ROW_ITER;\n    std::vector<uint8_t> row_buffer_;\n    std::vector<T> current_batch_;\n    size_t current_index_ = 0;\n    T current_row_;\n    bool is_valid_ = false;\n    bool is_end_ = false;\n    bool ffi_exhausted_ = false;  // Track when FFI iterator is exhausted\n    \n    void advance() {\n        if (is_end_) {\n            is_valid_ = false;\n            return;\n        }\n        \n        // Try current batch first\n        if (current_index_ < current_batch_.size()) {\n            current_row_ = std::move(current_batch_[current_index_++]);\n            is_valid_ = true;\n            return;\n        }\n        \n        // Need new batch - but check if FFI is already exhausted\n        if (ffi_exhausted_) {\n            // FFI iterator exhausted and we've consumed all rows\n            is_end_ = true;\n            is_valid_ = false;\n            return;\n        }\n        \n        // Fetch new batch\n        fetch_batch();\n        \n        // Try again with new batch\n        if (current_index_ < current_batch_.size()) {\n            current_row_ = std::move(current_batch_[current_index_++]);\n            is_valid_ = true;\n        } else {\n            // No rows in batch and FFI is exhausted\n            is_end_ = true;\n            is_valid_ = false;\n        }\n    }\n    \n    void fetch_batch() {\n        row_buffer_.resize(detail::INITIAL_ROW_BUFFER_SIZE);\n        size_t buffer_len = row_buffer_.size();\n        \n        int16_t ret = FFI::row_iter_bsatn_advance(iter_handle_, \n                                                  row_buffer_.data(), \n                                                  &buffer_len);\n        \n        if (ret == detail::ITER_EXHAUSTED) {\n            // Iterator is exhausted, but there might be a final batch of data\n            ffi_exhausted_ = true;\n            if (buffer_len > 0) {\n                // Resize buffer to actual data size and deserialize the final batch\n                row_buffer_.resize(buffer_len);\n                deserialize_batch(buffer_len);\n            }\n            // Don't set is_end_ here! We may have multiple rows in this batch\n            return;\n        }\n        \n        if (ret == detail::ERROR_BUFFER_TOO_SMALL) {\n            detail::check_buffer_size(buffer_len);\n            row_buffer_.resize(buffer_len);\n            ret = FFI::row_iter_bsatn_advance(iter_handle_, \n                                             row_buffer_.data(), \n                                             &buffer_len);\n        }\n        \n        if (ret > 0) {\n            LOG_FATAL(\"TableIterator::advance failed with error: \" + std::to_string(ret));\n        }\n        \n        // CRITICAL: Resize buffer to actual data size before deserializing!\n        row_buffer_.resize(buffer_len);\n        deserialize_batch(buffer_len);\n    }\n    \n    void deserialize_batch(size_t buffer_len) {\n        current_batch_.clear();\n        current_batch_.reserve(detail::TYPICAL_BATCH_SIZE);\n        current_index_ = 0;\n        \n        if (buffer_len == 0) return;\n        \n        SpacetimeDB::bsatn::Reader reader(row_buffer_.data(), buffer_len);\n        while (!reader.is_eos()) {\n            // Without exceptions, deserialization failures will abort\n            current_batch_.emplace_back(SpacetimeDB::bsatn::deserialize<T>(reader));\n        }\n    }\n};\n\n// =============================================================================\n// Table - Type-safe table interface\n// =============================================================================\n\ntemplate<typename T>\nclass Table {\n    static_assert(std::is_same_v<T, std::remove_cv_t<T>>, \n                  \"Table requires non-const, non-volatile row type\");\n\npublic:\n    explicit Table(TableId table_id) noexcept : table_id_(table_id) {}\n\n    // -------------------------------------------------------------------------\n    // Core CRUD Operations\n    // -------------------------------------------------------------------------\n    \n    /**\n     * Insert a row and return it with auto-generated fields populated.\n     * \n     * For tables with auto-increment fields (defined with FIELD_PrimaryKeyAutoInc, \n     * FIELD_UniqueAutoInc, FIELD_IndexAutoInc, or FIELD_AutoInc), this method \n     * automatically integrates the generated values back into the returned row.\n     * \n     * @param row_data The row to insert. Auto-increment fields can be set to 0 \n     *                 or any placeholder value - they will be overwritten.\n     * @return The inserted row with all auto-increment fields populated with \n     *         their generated values.\n     * \n     * @example\n     * // Table with auto-increment ID\n     * SPACETIMEDB_TABLE(User, users, Public);\n     * FIELD_PrimaryKeyAutoInc(users, id);\n     * \n     * // In a reducer\n     * User user{0, \"Alice\", true};  // id=0 is placeholder\n     * User inserted = ctx.db[users].insert(user);\n     * LOG_INFO(\"Created user with ID: \" + std::to_string(inserted.id));\n     * \n     * @note The integration system uses registry-based callbacks registered during\n     *       module initialization (__preinit__19_) to handle the auto-increment \n     *       value integration automatically.\n     */\n    T insert(const T& row_data) {\n        auto result = try_insert(row_data);\n        if (result.is_err()) {\n            const auto& error = result.err();\n            // Convert to LOG_FATAL to maintain current behavior\n            LOG_FATAL(\"Table::insert failed: \" + error.message);\n        }\n        return result.take_ok();\n    }\n    \n    /**\n     * Insert a row and return Result-like type instead of throwing on error.\n     * \n     * This method provides the same functionality as insert() but returns\n     * an InsertResult<T> that contains either the successfully inserted row\n     * (with auto-generated fields populated) or an InsertError with details\n     * about what went wrong.\n     * \n     * @param row_data The row to insert\n     * @return InsertResult<T> containing either the inserted row or error details\n     * \n     * @example\n     * auto result = ctx.db[users].try_insert(User{0, \"Alice\", true});\n     * if (result.is_ok()) {\n     *     const auto& user = result.ok();\n     *     LOG_INFO(\"Created user with ID: \" + std::to_string(user.id));\n     * } else {\n     *     const auto& error = result.err();\n     *     LOG_INFO(\"Insert failed: \" + error.message);\n     * }\n     */\n    InsertResult<T> try_insert(const T& row_data) {\n        SpacetimeDB::bsatn::Writer writer;\n        SpacetimeDB::bsatn::serialize(writer, row_data);\n        auto buffer_vec = writer.get_buffer();\n        \n        // Prepare buffer with extra space for auto-increment writeback\n        const size_t original_len = buffer_vec.size();\n        const size_t extra_space = detail::AUTO_INCREMENT_BUFFER_SPACE;\n        std::vector<uint8_t> buffer(buffer_vec.begin(), buffer_vec.end());\n        buffer.resize(original_len + extra_space);\n        \n        size_t buffer_len = original_len;\n        Status status = ::datastore_insert_bsatn(table_id_, buffer.data(), &buffer_len);\n        \n        // Instead of calling detail::handle_ffi_error (which LOG_FATALs), \n        // handle errors and return appropriate InsertError\n        if (is_error(status)) {\n            InsertErrorType error_type;\n            std::string message;\n            \n            // Map status codes to our error types\n            if (status == StatusCode::UNIQUE_ALREADY_EXISTS) {\n                error_type = InsertErrorType::UniqueConstraintViolation;\n                message = \"Unique constraint violation\";\n            } else if (status == StatusCode::AUTO_INC_OVERFLOW) {\n                error_type = InsertErrorType::AutoIncOverflow;\n                message = \"Auto increment overflow\";\n            } else {\n                error_type = InsertErrorType::Other;\n                message = \"Insert failed with status: \" + std::string(StatusCode::to_string(status));\n            }\n            \n            return InsertResult<T>(InsertError(error_type, status, message));\n        }\n        \n        // Success path - same as current insert()\n        if (buffer_len == 0) {\n            // No auto-generated fields, return the original row\n            return InsertResult<T>(T(row_data));\n        }\n        \n        // The buffer contains ONLY the generated column values in BSATN format\n        T updated_row = row_data;\n        SpacetimeDB::bsatn::Reader reader(buffer.data(), buffer_len);\n        detail::integrate_autoinc(updated_row, reader);\n        \n        return InsertResult<T>(std::move(updated_row));\n    }\n   \n\n    /**\n     * Delete all rows matching the given values\n     */\n    uint32_t delete_all_by_eq(const std::vector<T>& rows) {\n        if (rows.empty()) return 0;\n        \n        SpacetimeDB::bsatn::Writer writer;\n        writer.write_u32_le(static_cast<uint32_t>(rows.size()));\n        for (const auto& row : rows) {\n            SpacetimeDB::bsatn::serialize(writer, row);\n        }\n        \n        auto buffer = writer.take_buffer();\n        uint32_t deleted = 0;\n        Status status = FFI::datastore_delete_all_by_eq_bsatn(\n            table_id_, buffer.data(), buffer.size(), &deleted);\n        detail::handle_ffi_error(status, \"Table::delete_all_by_eq\", \n                               \"datastore_delete_all_by_eq_bsatn\");\n        \n        return deleted;\n    }\n    \n    /**\n     * Delete a single row by value\n     */\n    bool delete_by_value(const T& row) {\n        return delete_all_by_eq({row}) > 0;\n    }\n    \n    /**\n     * Update a row using a unique index\n     */\n    std::optional<T> update_by_index(IndexId index_id, const T& row) {\n        SpacetimeDB::bsatn::Writer writer;\n        SpacetimeDB::bsatn::serialize(writer, row);\n        auto buffer_vec = writer.get_buffer();\n        \n        // Prepare buffer with extra space for auto-increment writeback\n        const size_t original_len = buffer_vec.size();\n        const size_t extra_space = detail::AUTO_INCREMENT_BUFFER_SPACE;\n        std::vector<uint8_t> buffer(buffer_vec.begin(), buffer_vec.end());\n        buffer.resize(original_len + extra_space);\n        \n        size_t buffer_len = original_len;\n        Status status = FFI::datastore_update_bsatn(\n            table_id_, index_id, buffer.data(), &buffer_len);\n        \n        if (status == StatusCode::NO_SUCH_ROW) {\n            return std::nullopt;\n        }\n        \n        if (status == StatusCode::INDEX_NOT_UNIQUE) {\n            LOG_FATAL(\"Update failed: index is not unique\");\n        }\n        \n        if (status == StatusCode::NO_SUCH_INDEX) {\n            LOG_FATAL(\"Update failed: index does not exist\");\n        }\n        \n        detail::handle_ffi_error(status, \"Table::update_by_index\", \n                               \"datastore_update_bsatn\");\n        \n        // Handle the case where buffer_len might be 0 (no auto-increment fields)\n        if (buffer_len == 0) {\n            // No auto-generated fields, return the original row\n            return row;\n        }\n        \n        // Return the updated row with any auto-generated fields\n        buffer.resize(buffer_len);\n        SpacetimeDB::bsatn::Reader reader(buffer.data(), buffer_len);\n        return SpacetimeDB::bsatn::deserialize<T>(reader);\n    }\n\n    // -------------------------------------------------------------------------\n    // Iteration Support\n    // -------------------------------------------------------------------------\n    \n    TableIterator<T> begin() { return TableIterator<T>(table_id_); }\n    TableIterator<T> end() { return TableIterator<T>(); }\n\n    // -------------------------------------------------------------------------\n    // Table Metadata\n    // -------------------------------------------------------------------------\n    \n    uint64_t count() {\n        uint64_t result = 0;\n        Status status = FFI::datastore_table_row_count(table_id_, &result);\n        detail::handle_ffi_error(status, \"Table::count\", \"datastore_table_row_count\");\n        return result;\n    }\n    \n    bool empty() { return count() == 0; }\n    \n    TableId get_table_id() const noexcept { return table_id_; }\n\nprivate:\n    TableId table_id_;\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_LIBRARY_TABLE_H"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/table_with_constraints.h",
    "content": "#pragma once\n\n#include \"internal/Module.h\"\n#include \"internal/field_registration.h\"\n#include \"internal/autogen/RawScheduleDefV9.g.h\"\n#include \"internal/v10_builder.h\"\n#include \"macros.h\"\n#include \"error_handling.h\" // For DatabaseResult, DatabaseError, UpsertResult\n#include \"index_iterator.h\" // For IndexIterator\n#include \"range_queries.h\"  // For Range types and is_range_v\n#include <string>\n#include <vector>\n#include <cstdint>\n#include <cstring>\n#include <optional>\n#include <type_traits>\n#include <concepts>\n\nnamespace SpacetimeDB {\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\nnamespace detail {\n    // Common index name patterns for different constraint types\n    inline std::vector<std::string> get_index_patterns(const std::string& table_name, \n                                                       const std::string& field_name,\n                                                       FieldConstraint constraint_type) {\n        // Check for primary key (with or without auto-increment)\n        if (has_constraint(constraint_type, FieldConstraint::PrimaryKey)) {\n            return {\n                table_name + \"_\" + field_name + \"_idx_btree\",\n                table_name + \"_\" + field_name + \"_idx\",\n                \"btree_\" + table_name + \"_\" + field_name\n            };\n        }\n        // Check for unique (with or without auto-increment)\n        else if (has_constraint(constraint_type, FieldConstraint::Unique)) {\n            return {\n                table_name + \"_\" + field_name + \"_idx_btree\",\n                table_name + \"_\" + field_name + \"_unique_idx\",\n                \"btree_\" + table_name + \"_\" + field_name\n            };\n        }\n        // Check for indexed (with or without auto-increment)\n        else if (has_constraint(constraint_type, FieldConstraint::Indexed)) {\n            return {\n                table_name + \"_\" + field_name + \"_idx_btree\",  // Database-generated pattern (most likely)\n                table_name + \"_\" + field_name + \"_idx\",\n                \"idx_\" + table_name + \"_\" + field_name\n            };\n        }\n        return {};\n    }\n\n    struct TableMacroOptions {\n        bool is_event = false;\n        const char* canonical_name = nullptr;\n    };\n\n    constexpr TableMacroOptions MakeTableMacroOptions() {\n        return TableMacroOptions{};\n    }\n\n    constexpr TableMacroOptions MakeTableMacroOptions(bool is_event) {\n        TableMacroOptions options;\n        options.is_event = is_event;\n        return options;\n    }\n\n    constexpr TableMacroOptions MakeTableMacroOptions(const char* canonical_name) {\n        TableMacroOptions options;\n        options.canonical_name = canonical_name;\n        return options;\n    }\n\n    constexpr TableMacroOptions MakeTableMacroOptions(bool is_event, const char* canonical_name) {\n        TableMacroOptions options;\n        options.is_event = is_event;\n        options.canonical_name = canonical_name;\n        return options;\n    }\n}\n\n// =============================================================================\n// Core Table Tag System\n// =============================================================================\n\n/**\n * @brief Base class for table tag types\n * \n * Each table gets a tag type that acts as an alias for clean syntax:\n * ctx.db[person].insert(...) instead of ctx.db.table<Person>(\"person\")\n */\ntemplate<typename T>\nstruct TableTag {\n    using type = T;\n    static constexpr const char* name = nullptr;\n    static constexpr bool __is_event_internal = false;\n    \n    static std::vector<FieldConstraintInfo> get_constraints() {\n        return {};\n    }\n    \n    constexpr TableTag() = default;\n};\n\n// =============================================================================\n// Table Registration\n// =============================================================================\n\n// =============================================================================\n// Main Table Registration Macro\n// =============================================================================\n\n/**\n * @brief Register a table with the SpacetimeDB module\n * \n * Usage: SPACETIMEDB_TABLE(Type, table_name, Public)\n * Creates: Database table named \"table_name\" and tag variable 'table_name'\n * \n * Note: Constraints must be added using FIELD_ macros after the table declaration:\n *   SPACETIMEDB_TABLE(User, users, Public)\n *   FIELD_PrimaryKeyAutoInc(users, id)\n *   FIELD_Unique(users, email)\n */\n#define SPACETIMEDB_TABLE_IMPL(type, table_name, access_enum, options_expr) \\\n    extern \"C\" __attribute__((export_name(\"__preinit__20_register_table_\" #type \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__20_register_table_, SPACETIMEDB_PASTE(type, SPACETIMEDB_PASTE(_line_, __LINE__)))() { \\\n        auto _spacetimedb_table_options = (options_expr); \\\n        bool is_public = (access_enum == SpacetimeDB::Internal::TableAccess::Public); \\\n        SpacetimeDB::Module::RegisterTable<type>(#table_name, is_public, _spacetimedb_table_options.is_event); \\\n        if (_spacetimedb_table_options.canonical_name != nullptr) { \\\n            SpacetimeDB::Module::RegisterExplicitTableName(#table_name, _spacetimedb_table_options.canonical_name); \\\n        } \\\n    } \\\n    struct SPACETIMEDB_PASTE(table_name, _tag_type) : SpacetimeDB::TableTag<type> { \\\n        static constexpr const char* __table_name_internal = #table_name; \\\n        static constexpr bool __is_event_internal = (options_expr).is_event; \\\n    }; \\\n    constexpr SPACETIMEDB_PASTE(table_name, _tag_type) table_name{};\n\n#define SPACETIMEDB_TABLE_3(type, table_name, access_enum) \\\n    SPACETIMEDB_TABLE_IMPL(type, table_name, access_enum, SpacetimeDB::detail::MakeTableMacroOptions())\n\n#define SPACETIMEDB_TABLE_4(type, table_name, access_enum, arg4) \\\n    SPACETIMEDB_TABLE_IMPL(type, table_name, access_enum, SpacetimeDB::detail::MakeTableMacroOptions(arg4))\n\n#define SPACETIMEDB_TABLE_5(type, table_name, access_enum, arg4, arg5) \\\n    SPACETIMEDB_TABLE_IMPL(type, table_name, access_enum, SpacetimeDB::detail::MakeTableMacroOptions(arg4, arg5))\n\n#define SPACETIMEDB_TABLE_SELECT(_1, _2, _3, _4, _5, NAME, ...) NAME\n#define SPACETIMEDB_TABLE(...) \\\n    SPACETIMEDB_TABLE_SELECT(__VA_ARGS__, SPACETIMEDB_TABLE_5, SPACETIMEDB_TABLE_4, SPACETIMEDB_TABLE_3)(__VA_ARGS__)\n\n/**\n * @brief Schedule a table for automatic reducer execution\n */\n#define SPACETIMEDB_SCHEDULE(table_name, scheduled_at_column_index, reducer_name) \\\n    extern \"C\" __attribute__((export_name(\"__preinit__19_schedule_\" #table_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__19_schedule_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_line_, __LINE__)))() { \\\n        SpacetimeDB::Internal::getV10Builder().RegisterSchedule(#table_name, scheduled_at_column_index, #reducer_name); \\\n    }\n\n// =============================================================================\n// Field Tag System\n// =============================================================================\n\ntemplate<typename TableType, typename FieldType, SpacetimeDB::FieldConstraint Constraint, bool IsEventTable = false>\nstruct FieldTag {\n    const char* field_name;\n    const char* table_name;\n    FieldType TableType::*member_ptr;\n    \n    static constexpr SpacetimeDB::FieldConstraint constraint = Constraint;\n    using table_type = TableType;\n    using field_type = FieldType;\n    \n    constexpr FieldTag(const char* table, const char* field, FieldType TableType::*ptr) \n        : field_name(field), table_name(table), member_ptr(ptr) {}\n};\n\ntemplate<typename TableType, typename FieldType, bool IsEventTable = false>\nusing PrimaryKeyFieldTag = FieldTag<TableType, FieldType, SpacetimeDB::FieldConstraint::PrimaryKey, IsEventTable>;\n\ntemplate<typename TableType, typename FieldType, bool IsEventTable = false>  \nusing UniqueFieldTag = FieldTag<TableType, FieldType, SpacetimeDB::FieldConstraint::Unique, IsEventTable>;\n\ntemplate<typename TableType, typename FieldType, bool IsEventTable = false>\nusing IndexedFieldTag = FieldTag<TableType, FieldType, SpacetimeDB::FieldConstraint::Indexed, IsEventTable>;\n\n// =============================================================================\n// Multi-Column Index Tag System\n// =============================================================================\n\ntemplate<typename TableType>\nstruct MultiColumnIndexTag {\n    const char* table_name;\n    const char* index_name;\n    const char* column_list;  // List of column names like \"player_id_level\"\n    \n    constexpr MultiColumnIndexTag(const char* table, const char* index, const char* columns)\n        : table_name(table), index_name(index), column_list(columns) {}\n};\n\n// =============================================================================\n// Constraint Concepts\n// =============================================================================\n\ntemplate<typename T>\nconcept FilterableValue = \n    std::integral<T> ||\n    std::same_as<T, std::string> ||\n    std::same_as<T, SpacetimeDB::Identity> ||\n    std::same_as<T, SpacetimeDB::ConnectionId> ||\n    std::same_as<T, SpacetimeDB::Timestamp> ||\n    std::same_as<T, SpacetimeDB::Uuid> ||\n    std::same_as<T, SpacetimeDB::I128> ||\n    std::same_as<T, SpacetimeDB::U128> ||\n    std::same_as<T, SpacetimeDB::I256> ||\n    std::same_as<T, SpacetimeDB::U256> ||\n    std::same_as<T, SpacetimeDB::i256> ||\n    std::same_as<T, SpacetimeDB::u256> ||\n    std::is_enum_v<T>;\n\ntemplate<typename T>\nconcept AutoIncrementable = \n    std::same_as<T, int8_t> ||\n    std::same_as<T, int16_t> ||\n    std::same_as<T, int32_t> ||\n    std::same_as<T, int64_t> ||\n    std::same_as<T, uint8_t> ||\n    std::same_as<T, uint16_t> ||\n    std::same_as<T, uint32_t> ||\n    std::same_as<T, uint64_t> ||\n    std::same_as<T, SpacetimeDB::I128> ||\n    std::same_as<T, SpacetimeDB::U128> ||\n    std::same_as<T, SpacetimeDB::i256> ||\n    std::same_as<T, SpacetimeDB::u256>;\n\n// =============================================================================\n// Unified Field Accessor Base Class\n// =============================================================================\n\ntemplate<typename TableType, typename FieldType>\nclass TypedFieldAccessor : public SpacetimeDB::TableAccessor<TableType> {\nprotected:\n    std::string_view field_name_;\n    FieldType TableType::*member_ptr_;\n    mutable std::optional<IndexId> cached_index_id_;\n    \n    [[nodiscard]] constexpr FieldType get_field_value(const TableType& row) const {\n        return row.*member_ptr_;\n    }\n    \n    [[nodiscard]] IndexId resolve_index_with_patterns(std::initializer_list<std::string> patterns) const {\n        if (cached_index_id_) {\n            return *cached_index_id_;\n        }\n        \n        for (const auto& pattern : patterns) {\n            IndexId id;\n            Status result = ::index_id_from_name(\n                reinterpret_cast<const uint8_t*>(pattern.data()),\n                pattern.length(),\n                &id\n            );\n            \n            if (is_ok(result)) {\n                cached_index_id_ = id;\n                return id;\n            }\n        }\n        \n        return IndexId{0}; // Invalid ID\n    }\n    \n    // Common index-based delete operation\n    [[nodiscard]] uint32_t delete_by_index_scan(const FieldType& value, bool exact_match = true) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner == 0) {\n            return 0; // No index available\n        }\n        \n        SpacetimeDB::bsatn::Writer bound_writer;\n        bound_writer.write_u8(0); // Bound::Included\n        SpacetimeDB::bsatn::serialize(bound_writer, value);\n        auto bound_buffer = bound_writer.get_buffer();\n        \n        uint32_t deleted_count = 0;\n        Status status;\n        \n        if (exact_match) {\n            // Exact match for primary/unique keys\n            status = ::datastore_delete_by_index_scan_range_bsatn(\n                index_id,\n                nullptr, 0, ColId{0},\n                bound_buffer.data(), bound_buffer.size(),\n                bound_buffer.data(), bound_buffer.size(),\n                &deleted_count\n            );\n        } else {\n            // Prefix match for indexed fields\n            status = ::datastore_delete_by_index_scan_range_bsatn(\n                index_id,\n                bound_buffer.data(), bound_buffer.size(), ColId{1},\n                nullptr, 0,\n                nullptr, 0,\n                &deleted_count\n            );\n        }\n        \n        return is_ok(status) ? deleted_count : 0;\n    }\n    \n    // Common index-based update operation\n    bool update_by_index(const TableType& new_row) const {\n        IndexId index_id = get_index_id();\n        auto result = this->get_table().update_by_index(index_id, new_row);\n        return result.has_value();\n    }\n    \n    // Must be implemented by derived classes\n    virtual IndexId get_index_id() const = 0;\n    \npublic:\n    using table_type = TableType;\n    using field_type = FieldType;\n    \n    TypedFieldAccessor(const char* table_name, const char* field_name, FieldType TableType::*ptr) \n        : SpacetimeDB::TableAccessor<TableType>(table_name), \n          field_name_(field_name), \n          member_ptr_(ptr) {}\n    \n    [[nodiscard]] constexpr std::string_view field_name() const { return field_name_; }\n    [[nodiscard]] constexpr auto member_pointer() const { return member_ptr_; }\n    \n    // Common operations available to all field types\n    // std::vector<TableType> filter(const FieldType& value) const {\n    //     return this->get_table().filter([this, &value](const TableType& row) {\n    //         return get_field_value(row) == value;\n    //     });\n    // }\n\n    uint32_t delete_by_value(const FieldType& value) const {\n        return delete_by_index_scan(value, true);\n    }\n    \n    // DatabaseResult<TableType> try_insert(const TableType& row) const {\n    //     // Without exceptions, we just call insert and return success\n    //     // If there's a constraint violation, insert will abort\n    //     this->insert(row);\n    //     return DatabaseResult<TableType>(std::in_place_index<0>, row);\n    // }\n};\n\n// =============================================================================\n// Specialized Field Accessors (Minimal Duplication)\n// =============================================================================\n\ntemplate<typename TableType, typename FieldType>\nclass TypedPrimaryKeyAccessor : public TypedFieldAccessor<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::PrimaryKey\n        );\n        IndexId id = this->resolve_index_with_patterns({patterns[0], patterns[1], patterns[2]});\n        \n        if (id.inner == 0) {\n            std::abort(); // Failed to resolve index ID for primary key field\n        }\n        return id;\n    }\n    \npublic:\n    using TypedFieldAccessor<TableType, FieldType>::TypedFieldAccessor;\n    \n    [[nodiscard]] std::optional<TableType> find(const FieldType& key_value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            // Use efficient index-based iteration\n            IndexIterator<TableType> iter(index_id, key_value);\n            if (iter != IndexIterator<TableType>()) {\n                return *iter;\n            } else {\n                return std::nullopt;\n            }\n        }\n        return std::nullopt;\n        // // Fallback to iteration\n        // return SpacetimeDB::TableAccessor<TableType>::find([&](const TableType& row) {\n        //     return this->get_field_value(row) == key_value;\n        // });\n    }\n\n    bool delete_by_key(const FieldType& key_value) const {\n        uint32_t count = this->delete_by_index_scan(key_value, true);\n        if (count > 0) return true;\n\n        return false;\n        // // Fallback to iteration\n        // return this->delete_where_primary_key([&](const TableType& row) {\n        //     return this->get_field_value(row) == key_value;\n        // });\n    }\n    \n    bool update(const TableType& new_row) const {\n        if (this->update_by_index(new_row)) return true;\n        \n        // Fallback\n        FieldType key_val = this->get_field_value(new_row);\n        auto existing = find(key_val);\n        if (existing) {\n            this->update_by_value(*existing, new_row);\n            return true;\n        }\n        return false;\n    }\n    \n    TableType try_insert_or_update(const TableType& row) const {\n        FieldType key_val = this->get_field_value(row);\n        auto existing = find(key_val);\n        if (existing) {\n            auto _ = update(row);\n            return row;\n        } else {\n            return this->insert(row);\n        }\n    }\n};\n\ntemplate<typename TableType, typename FieldType>\nclass TypedUniqueAccessor : public TypedFieldAccessor<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::Unique\n        );\n        IndexId id = this->resolve_index_with_patterns({patterns[0], patterns[1], patterns[2]});\n        \n        if (id.inner == 0) {\n            std::abort(); // Failed to resolve index ID for unique field\n        }\n        return id;\n    }\n    \npublic:\n    using TypedFieldAccessor<TableType, FieldType>::TypedFieldAccessor;\n    \n    std::optional<TableType> find(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner != 0) {\n            // Use efficient index-based iteration\n            IndexIterator<TableType> iter(index_id, value);\n            if (iter != IndexIterator<TableType>()) {\n                return *iter;\n            } else {\n                return std::nullopt;\n            }\n        }\n        return std::nullopt;\n\n        // return SpacetimeDB::TableAccessor<TableType>::find([&](const TableType& row) {\n        //     return this->get_field_value(row) == value;\n        // });\n    }\n    \n    bool delete_by_value(const FieldType& value) const {\n        uint32_t count = this->delete_by_index_scan(value, true);\n        if (count > 0) return true;\n        \n        // Fallback\n        auto match = find(value);\n        return match ? SpacetimeDB::TableAccessor<TableType>::delete_by_value(*match) > 0 : false;\n    }\n    \n    bool update(const TableType& new_row) const {\n        if (this->update_by_index(new_row)) return true;\n        \n        // Fallback\n        FieldType field_val = this->get_field_value(new_row);\n        auto existing = find(field_val);\n        if (existing) {\n            this->TableAccessor<TableType>::update_by_value(*existing, new_row);\n            return true;\n        }\n        return false;\n    }\n};\n\ntemplate<typename TableType, typename FieldType>\nclass TypedIndexedAccessor : public TypedFieldAccessor<TableType, FieldType> {\nprivate:\n    [[nodiscard]] IndexId get_index_id() const override {\n        auto patterns = detail::get_index_patterns(\n            std::string(this->table_name_), \n            std::string(this->field_name_),\n            FieldConstraint::Indexed\n        );\n        if (patterns.size() >= 2) {\n            return this->resolve_index_with_patterns({patterns[0], patterns[1]});\n        } else if (patterns.size() == 1) {\n            return this->resolve_index_with_patterns({patterns[0]});\n        }\n        return IndexId{0};\n    }\n    \npublic:\n    using TypedFieldAccessor<TableType, FieldType>::TypedFieldAccessor;\n    \n    /**\n     * @brief Filter rows by exact field value using index\n     * \n     * Returns lazy IndexIterator - results are evaluated incrementally during iteration\n     * without materializing all matches in memory.\n     * \n     * @param value The field value to match exactly\n     * @return IndexIterator supporting range-based for loops\n     * \n     * @example Clean range-based for loop (no materialization):\n     * @code\n     * for (const auto& row : ctx.db[table_field].filter(value)) {\n     *     // Process matching rows one at a time\n     * }\n     * @endcode\n     * \n     * @example Materialize all results when needed:\n     * @code\n     * auto all_matches = ctx.db[table_field].filter(value).collect();\n     * @endcode\n     */\n    IndexIteratorRange<TableType> filter(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        \n        if (index_id.inner != 0) {\n            // Use efficient index-based iteration\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>(index_id, value));\n        }\n\n        return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n    }\n    \n    /**\n     * @brief Filter rows by range using index\n     * \n     * Returns lazy IndexIterator - results are evaluated incrementally during iteration\n     * without materializing all matches in memory.\n     * \n     * @tparam RangeType Range type (range_from, range_to, range_inclusive, etc.)\n     * @param range The range bounds to match\n     * @return IndexIterator supporting range-based for loops\n     * \n     * @example Query rows in a range:\n     * @code\n     * auto age_range = range_from(uint8_t(18));\n     * for (const auto& row : ctx.db[person_age].filter(age_range)) {\n     *     // Process persons aged 18+\n     * }\n     * @endcode\n     */\n    template<typename RangeType>\n    std::enable_if_t<is_range_v<RangeType>, IndexIteratorRange<TableType>>\n    filter(const RangeType& range) const {\n        IndexId index_id = get_index_id();\n        \n        if (index_id.inner != 0) {\n            // Use efficient index-based iteration with range\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>(index_id, range));\n        }\n\n        return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n    }\n    \n    uint32_t delete_all(const FieldType& value) const {\n        IndexId index_id = get_index_id();\n        if (index_id.inner == 0) {\n            return 0; // No index available\n        }\n        \n        // Serialize the value to search for\n        SpacetimeDB::bsatn::Writer writer;\n        SpacetimeDB::bsatn::serialize(writer, value);\n        auto buffer = writer.get_buffer();\n        \n        uint32_t deleted_count = 0;\n        Status status = ::datastore_delete_by_index_scan_point_bsatn(\n            index_id,\n            buffer.data(), buffer.size(),\n            &deleted_count\n        );\n        \n        return is_ok(status) ? deleted_count : 0;\n    }\n};\n\n// =============================================================================\n// Multi-Column Index Accessor\n// =============================================================================\n\ntemplate<typename TableType>\nclass TypedMultiColumnIndexAccessor : public TableAccessor<TableType> {\nprivate:\n    std::string table_name_;\n    std::string index_name_;  // This is the accessor name like \"by_player_and_level\"\n    std::string column_list_;  // This is the column list like \"player_id_level\"\n    mutable std::optional<IndexId> cached_index_id_;\n    \n    IndexId resolve_index_id() const {\n        if (cached_index_id_) {\n            return *cached_index_id_;\n        }\n\n        // Resolve by the deterministic generated source name\n        const std::string canonical_source_name = table_name_ + \"_\" + column_list_ + \"_idx_btree\";\n        IndexId id{0};\n        if (!is_ok(::index_id_from_name(\n                reinterpret_cast<const uint8_t*>(canonical_source_name.data()),\n                canonical_source_name.length(),\n                &id))) {\n            id = IndexId{0};\n        }\n\n        cached_index_id_ = id;\n        return id;\n    }\n    \npublic:\n    TypedMultiColumnIndexAccessor(const char* table_name, const char* index_name, const char* column_list)\n        : TableAccessor<TableType>(table_name), \n          table_name_(table_name),\n          index_name_(index_name),\n          column_list_(column_list) {}\n    \n    // Exact match on all columns (template method - types deduced from call)\n    template<typename... FieldTypes>\n    IndexIteratorRange<TableType> filter(const std::tuple<FieldTypes...>& values) const \n        requires (sizeof...(FieldTypes) > 0 && sizeof...(FieldTypes) <= 6)\n    {\n        IndexId id = resolve_index_id();\n        \n        if (id.inner == 0) {\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n        }\n        \n        return IndexIteratorRange<TableType>(IndexIterator<TableType>(id, values));\n    }\n    \n    // Prefix-only match: find all rows where first N-1 columns match\n    template<typename FirstColType>\n    IndexIteratorRange<TableType> filter(const FirstColType& prefix_value) const \n        requires (!is_tuple_v<FirstColType>)\n    {\n        IndexId id = resolve_index_id();\n        \n        if (id.inner == 0) {\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n        }\n        \n        // Use prefix_match_tag to disambiguate constructor\n        return IndexIteratorRange<TableType>(IndexIterator<TableType>(prefix_match_tag{}, id, prefix_value));\n    }\n    \n    // Prefix + range match: find rows where first N-1 columns match and last is in range\n    template<typename FirstColType, typename RangeType>\n    IndexIteratorRange<TableType> filter(const std::tuple<FirstColType, RangeType>& values) const \n        requires (is_range_v<RangeType>)\n    {\n        IndexId id = resolve_index_id();\n        \n        if (id.inner == 0) {\n            return IndexIteratorRange<TableType>(IndexIterator<TableType>());\n        }\n        \n        return IndexIteratorRange<TableType>(IndexIterator<TableType>(id, values));\n    }\n};\n\n// =============================================================================\n// Auto-Increment Integration Helper\n// =============================================================================\n\n// Helper macro to register auto-increment integration function\n// Creates a unique function and registers it for the struct type\n#define SPACETIMEDB_AUTOINC_INTEGRATION_IMPL(StructType, field_name) \\\n    namespace SpacetimeDB { namespace detail { \\\n        static void SPACETIMEDB_PASTE(autoinc_integrate_, __LINE__)(StructType& row, SpacetimeDB::bsatn::Reader& reader) { \\\n            using FieldType = decltype(std::declval<StructType>().field_name); \\\n            FieldType generated_value = SpacetimeDB::bsatn::deserialize<FieldType>(reader); \\\n            row.field_name = generated_value; \\\n        } \\\n    }} \\\n    extern \"C\" __attribute__((export_name(\"__preinit__19_autoinc_register_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__19_autoinc_register_, __LINE__)() { \\\n        SpacetimeDB::detail::get_autoinc_integrator<StructType>() = \\\n            &SpacetimeDB::detail::SPACETIMEDB_PASTE(autoinc_integrate_, __LINE__); \\\n    }\n\n// =============================================================================\n// Field Constraint Registration Macros\n// =============================================================================\n\n#define FIELD_PrimaryKey(table_name, field_name) \\\n    static constexpr SpacetimeDB::PrimaryKeyFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                      decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                      SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, ::SpacetimeDB::FieldConstraint::PrimaryKey); \\\n    }\n\n#define FIELD_Unique(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(FilterableValue<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have Unique constraint - type is not filterable.\"); \\\n        return true; \\\n    }(), \"Constraint validation for \" #table_name \".\" #field_name); \\\n    static constexpr SpacetimeDB::UniqueFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                  decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                  SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, ::SpacetimeDB::FieldConstraint::Unique); \\\n    }\n\n#define FIELD_Index(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(FilterableValue<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have Index constraint - type is not filterable.\"); \\\n        return true; \\\n    }(), \"Constraint validation for \" #table_name \".\" #field_name); \\\n    static constexpr SpacetimeDB::IndexedFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                   decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                   SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, ::SpacetimeDB::FieldConstraint::Indexed); \\\n    }\n\n#define FIELD_PrimaryKeyAutoInc(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(AutoIncrementable<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have AutoIncrement constraint - type is not auto-incrementable.\"); \\\n        return true; \\\n    }(), \"AutoIncrement validation for \" #table_name \".\" #field_name); \\\n    static constexpr SpacetimeDB::PrimaryKeyFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                      decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                      SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, static_cast<::SpacetimeDB::FieldConstraint>( \\\n                static_cast<int>(::SpacetimeDB::FieldConstraint::PrimaryKey) | static_cast<int>(::SpacetimeDB::FieldConstraint::AutoInc))); \\\n    } \\\n    SPACETIMEDB_AUTOINC_INTEGRATION_IMPL(typename std::remove_cv_t<decltype(table_name)>::type, field_name)\n\n#define FIELD_UniqueAutoInc(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(FilterableValue<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have Unique constraint - type is not filterable.\"); \\\n        static_assert(AutoIncrementable<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have AutoIncrement constraint - type is not auto-incrementable.\"); \\\n        return true; \\\n    }(), \"Constraint validation for \" #table_name \".\" #field_name); \\\n    static constexpr SpacetimeDB::UniqueFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                  decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                  SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, static_cast<::SpacetimeDB::FieldConstraint>( \\\n                static_cast<int>(::SpacetimeDB::FieldConstraint::Unique) | static_cast<int>(::SpacetimeDB::FieldConstraint::AutoInc))); \\\n    } \\\n    SPACETIMEDB_AUTOINC_INTEGRATION_IMPL(typename std::remove_cv_t<decltype(table_name)>::type, field_name)\n\n#define FIELD_IndexAutoInc(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(FilterableValue<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have Index constraint - type is not filterable.\"); \\\n        static_assert(AutoIncrementable<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have AutoIncrement constraint - type is not auto-incrementable.\"); \\\n        return true; \\\n    }(), \"Constraint validation for \" #table_name \".\" #field_name); \\\n    static constexpr SpacetimeDB::IndexedFieldTag<typename std::remove_cv_t<decltype(table_name)>::type, \\\n                                                   decltype(std::declval<typename std::remove_cv_t<decltype(table_name)>::type>().field_name), \\\n                                                   SPACETIMEDB_PASTE(table_name, _tag_type)::__is_event_internal> \\\n    table_name##_##field_name { #table_name, #field_name, &std::remove_cv_t<decltype(table_name)>::type::field_name }; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, static_cast<::SpacetimeDB::FieldConstraint>( \\\n                static_cast<int>(::SpacetimeDB::FieldConstraint::Indexed) | static_cast<int>(::SpacetimeDB::FieldConstraint::AutoInc))); \\\n    }\n\n#define FIELD_AutoInc(table_name, field_name) \\\n    static_assert([]() constexpr { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        using FieldType = decltype(std::declval<TableType>().field_name); \\\n        static_assert(AutoIncrementable<FieldType>, \\\n            \"Field '\" #field_name \"' cannot have AutoIncrement constraint - type is not auto-incrementable.\"); \\\n        return true; \\\n    }(), \"AutoIncrement validation for \" #table_name \".\" #field_name); \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_constraint_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_constraint_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddFieldConstraint<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #field_name, ::SpacetimeDB::FieldConstraint::AutoInc); \\\n    } \\\n    SPACETIMEDB_AUTOINC_INTEGRATION_IMPL(typename std::remove_cv_t<decltype(table_name)>::type, field_name)\n\n#define SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name) \\\n    extern \"C\" __attribute__((export_name(\"__preinit__18_explicit_index_name_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__18_explicit_index_name_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Module::RegisterExplicitIndexName(#table_name \"_\" #field_name \"_idx_btree\", canonical_name); \\\n    }\n\n#define FIELD_PrimaryKey_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_PrimaryKey(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n#define FIELD_Unique_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_Unique(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n#define FIELD_Index_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_Index(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n#define FIELD_PrimaryKeyAutoInc_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_PrimaryKeyAutoInc(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n#define FIELD_UniqueAutoInc_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_UniqueAutoInc(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n#define FIELD_IndexAutoInc_NAMED(table_name, field_name, canonical_name) \\\n    FIELD_IndexAutoInc(table_name, field_name) \\\n    SPACETIMEDB_REGISTER_EXPLICIT_SINGLE_COLUMN_INDEX_NAME(table_name, field_name, canonical_name)\n\n// Helper to join field names with underscores at compile time\n#define SPACETIMEDB_JOIN_FIELDS(...) SPACETIMEDB_JOIN_FIELDS_IMPL(__VA_ARGS__)\n#define SPACETIMEDB_JOIN_FIELDS_IMPL(...) SPACETIMEDB_GET_JOIN_MACRO(__VA_ARGS__, \\\n    SPACETIMEDB_JOIN_6, SPACETIMEDB_JOIN_5, SPACETIMEDB_JOIN_4, \\\n    SPACETIMEDB_JOIN_3, SPACETIMEDB_JOIN_2, SPACETIMEDB_JOIN_1)(__VA_ARGS__)\n#define SPACETIMEDB_GET_JOIN_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME\n#define SPACETIMEDB_JOIN_1(a) #a\n#define SPACETIMEDB_JOIN_2(a,b) #a \"_\" #b\n#define SPACETIMEDB_JOIN_3(a,b,c) #a \"_\" #b \"_\" #c\n#define SPACETIMEDB_JOIN_4(a,b,c,d) #a \"_\" #b \"_\" #c \"_\" #d\n#define SPACETIMEDB_JOIN_5(a,b,c,d,e) #a \"_\" #b \"_\" #c \"_\" #d \"_\" #e\n#define SPACETIMEDB_JOIN_6(a,b,c,d,e,f) #a \"_\" #b \"_\" #c \"_\" #d \"_\" #e \"_\" #f\n\n// Multi-column index registration macro (unnamed canonical mapping)\n#define FIELD_MultiColumnIndex(table_name, index_name, ...) \\\n    static constexpr auto table_name##_##index_name = SpacetimeDB::MultiColumnIndexTag< \\\n        typename std::remove_cv_t<decltype(table_name)>::type \\\n    >{#table_name, #index_name, SPACETIMEDB_JOIN_FIELDS(__VA_ARGS__)}; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_multi_index_\" #table_name \"_\" #index_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_multi_index_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(index_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddMultiColumnIndex<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #index_name, {SPACETIMEDB_STRINGIFY_EACH(__VA_ARGS__)}); \\\n    }\n\n#define FIELD_MultiColumnIndex_NAMED(table_name, index_name, canonical_name, ...) \\\n    static constexpr auto table_name##_##index_name = SpacetimeDB::MultiColumnIndexTag< \\\n        typename std::remove_cv_t<decltype(table_name)>::type \\\n    >{#table_name, #index_name, SPACETIMEDB_JOIN_FIELDS(__VA_ARGS__)}; \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_multi_index_\" #table_name \"_\" #index_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_multi_index_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(index_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        SpacetimeDB::Internal::getV10Builder().AddMultiColumnIndex<typename std::remove_cv_t<decltype(table_name)>::type>( \\\n            #table_name, #index_name, {SPACETIMEDB_STRINGIFY_EACH(__VA_ARGS__)}); \\\n        SpacetimeDB::Module::RegisterExplicitIndexName( \\\n            #table_name \"_\" SPACETIMEDB_JOIN_FIELDS(__VA_ARGS__) \"_idx_btree\", \\\n            canonical_name); \\\n    }\n\n// Compatibility alias retained for migration.\n#define FIELD_NamedMultiColumnIndex(table_name, index_name, ...) \\\n    FIELD_MultiColumnIndex(table_name, index_name, __VA_ARGS__)\n\n#define FIELD_Default(table_name, field_name, default_value) \\\n    extern \"C\" __attribute__((export_name(\"__preinit__21_field_default_\" #table_name \"_\" #field_name \"_line_\" SPACETIMEDB_STRINGIFY(__LINE__)))) \\\n    void SPACETIMEDB_PASTE(__preinit__21_field_default_, SPACETIMEDB_PASTE(table_name, SPACETIMEDB_PASTE(_, SPACETIMEDB_PASTE(field_name, SPACETIMEDB_PASTE(_line_, __LINE__)))))() { \\\n        using TableType = typename std::remove_cv_t<decltype(table_name)>::type; \\\n        \\\n        /* Serialize the default value to BSATN bytes */ \\\n        auto serialized = SpacetimeDB::bsatn::to_bytes(default_value); \\\n        \\\n        SpacetimeDB::Internal::getV10Builder().AddColumnDefault<TableType>( \\\n            #table_name, \\\n            #field_name, \\\n            serialized \\\n        ); \\\n    }\n} // namespace SpacetimeDB\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/tx_context.h",
    "content": "#ifndef SPACETIMEDB_TX_CONTEXT_H\n#define SPACETIMEDB_TX_CONTEXT_H\n\n#include <spacetimedb/reducer_context.h>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Transaction context for procedures\n * \n * TxContext wraps a ReducerContext to provide transactional database access.\n * It's analogous to Rust's TxContext which is passed to closures in\n * `ctx.with_tx()` and `ctx.try_with_tx()`.\n * \n * Design: Mimics Rust's Deref trait for consistent API\n * =====================================================\n * In Rust, TxContext implements Deref<Target=ReducerContext>, which means:\n *   - Reducers use: ctx.db.table()\n *   - Transactions use: tx.db.table()  (Deref auto-dereferences)\n * \n * C++ doesn't support operator. overloading, so we explicitly expose\n * ReducerContext fields as public references to achieve the same ergonomics:\n *   - Reducers use: ctx.db[table]\n *   - Transactions use: tx.db[table]  (same syntax!)\n * \n * Tradeoff: TxContext is 40 bytes instead of 8 bytes (storing 5 references),\n * but this is negligible as TxContext is stack-allocated and short-lived.\n * The consistent API is worth the minor memory cost.\n * \n * All database operations are part of an anonymous transaction:\n * - Transaction commits when the callback returns successfully\n * - Transaction rolls back if the callback throws or returns error\n * \n * Example usage:\n * @code\n * SPACETIMEDB_PROCEDURE(void, insert_user, ProcedureContext ctx, std::string name) {\n *     ctx.with_tx([&](TxContext& tx) {\n *         // Access authentication (same as in reducers)\n *         if (tx.sender_auth().has_jwt()) {\n *             auto jwt = tx.sender_auth().get_jwt();\n *             // ...\n *         }\n *         // Database operations here are transactional (same syntax as reducers)\n *         tx.db[users].insert(User{name});\n *     });\n * }\n * @endcode\n */\nstruct TxContext {\nprivate:\n    ReducerContext& ctx_;\n    \npublic:\n    // Public references to ReducerContext fields for consistent API with Rust\n    // In Rust, Deref makes tx.db work the same as ctx.db\n    // In C++, we explicitly expose references where possible and provide\n    // accessors for fields exposed as methods on ReducerContext.\n    DatabaseContext& db;\n    const Timestamp& timestamp;\n    const std::optional<ConnectionId>& connection_id;\n    \n    // Constructor - initializes all reference members\n    explicit TxContext(ReducerContext& ctx) \n        : ctx_(ctx), \n          db(ctx.db),\n          timestamp(ctx.timestamp),\n          connection_id(ctx.connection_id) {}\n    \n    // Access to ReducerContext methods\n    Identity sender() const { return ctx_.sender(); }\n    const AuthCtx& sender_auth() const { return ctx_.sender_auth(); }\n    Identity identity() const { return ctx_.identity(); }\n    StdbRng& rng() const { return ctx_.rng(); }\n    \n    /**\n     * Generate a new random UUID v4.\n     * \n     * Creates a random UUID using the transaction's deterministic RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_PROCEDURE(void, create_session, ProcedureContext ctx) {\n     *     ctx.with_tx([&](TxContext& tx) {\n     *         Uuid session_id = tx.new_uuid_v4();\n     *         tx.db[sessions].insert(Session{session_id});\n     *     });\n     * }\n     * @endcode\n     * \n     * @return A new UUID v4\n     */\n    Uuid new_uuid_v4() const { return ctx_.new_uuid_v4(); }\n    \n    /**\n     * Generate a new UUID v7.\n     * \n     * Creates a time-ordered UUID with the transaction's timestamp, a monotonic counter,\n     * and random bytes from the transaction's deterministic RNG.\n     * \n     * Example:\n     * @code\n     * SPACETIMEDB_PROCEDURE(void, create_user, ProcedureContext ctx, std::string name) {\n     *     ctx.with_tx([&](TxContext& tx) {\n     *         Uuid user_id = tx.new_uuid_v7();\n     *         tx.db[users].insert(User{user_id, name});\n     *     });\n     * }\n     * @endcode\n     * \n     * @return A new UUID v7\n     */\n    Uuid new_uuid_v7() const { return ctx_.new_uuid_v7(); }\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_TX_CONTEXT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/version.h.in",
    "content": "/**\n * @file version.h\n * @brief SpacetimeDB C++ Bindings Version Information\n * \n * This file is auto-generated by CMake from version.h.in.\n * Do not edit this file directly - it will be overwritten during the build.\n * Version numbers are managed by the upgrade-version tool.\n */\n\n#pragma once\n\nnamespace spacetimedb {\n\n/**\n * @brief Major version number\n * \n * Incremented for breaking API changes.\n */\nconstexpr int VERSION_MAJOR = @PROJECT_VERSION_MAJOR@;\n\n/**\n * @brief Minor version number\n * \n * Incremented for new features in a backwards-compatible manner.\n */\nconstexpr int VERSION_MINOR = @PROJECT_VERSION_MINOR@;\n\n/**\n * @brief Patch version number\n * \n * Incremented for backwards-compatible bug fixes.\n */\nconstexpr int VERSION_PATCH = @PROJECT_VERSION_PATCH@;\n\n/**\n * @brief Full version string\n * \n * Format: \"MAJOR.MINOR.PATCH\"\n */\nconstexpr const char* VERSION_STRING = \"@PROJECT_VERSION@\";\n\n} // namespace spacetimedb\n\n// C-style macros for preprocessor checks\n#define SPACETIMEDB_VERSION_MAJOR @PROJECT_VERSION_MAJOR@\n#define SPACETIMEDB_VERSION_MINOR @PROJECT_VERSION_MINOR@\n#define SPACETIMEDB_VERSION_PATCH @PROJECT_VERSION_PATCH@\n#define SPACETIMEDB_VERSION_STRING \"@PROJECT_VERSION@\"\n\n// Convenience macro for version checking in preprocessor\n#define SPACETIMEDB_VERSION_AT_LEAST(major, minor, patch) \\\n    ((SPACETIMEDB_VERSION_MAJOR > (major)) || \\\n     (SPACETIMEDB_VERSION_MAJOR == (major) && SPACETIMEDB_VERSION_MINOR > (minor)) || \\\n     (SPACETIMEDB_VERSION_MAJOR == (major) && SPACETIMEDB_VERSION_MINOR == (minor) && SPACETIMEDB_VERSION_PATCH >= (patch)))\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/view_context.h",
    "content": "#ifndef SPACETIMEDB_VIEW_CONTEXT_H\n#define SPACETIMEDB_VIEW_CONTEXT_H\n\n#include <spacetimedb/bsatn/types.h> // For Identity\n#include <spacetimedb/bsatn/timestamp.h> // For Timestamp\n#include <spacetimedb/readonly_database_context.h> // For ReadOnlyDatabaseContext\n#include <array>\n\nnamespace SpacetimeDB {\n\n/**\n * @brief Context for views with caller identity\n * \n * ViewContext provides read-only database access along with the identity\n * of the caller who invoked the view. Use this when the view needs to\n * filter or customize results based on who is calling it.\n * \n * Key differences from ReducerContext:\n * - db is ReadOnlyDatabaseContext (no mutations allowed)\n * - No connection_id (views are stateless, don't track connections)\n * - No rng() method (views should be deterministic)\n * \n * Example usage:\n * @code\n * SPACETIMEDB_VIEW(std::vector<Item>, get_my_items, Public, ViewContext ctx) {\n *     std::vector<Item> my_items;\n *     // Filter by caller's identity using indexed field\n *     for (const auto& item : ctx.db[item_owner].filter(ctx.sender())) {\n *         my_items.push_back(item);\n *     }\n *     return Ok(my_items);\n * }\n * @endcode\n */\nstruct ViewContext {\nprivate:\n    // Caller's identity - who invoked this view\n    Identity sender_;\n\npublic:\n    // Read-only database access - no mutations allowed\n    ReadOnlyDatabaseContext db;\n    \n    // Constructors\n    ViewContext() = default;\n    \n    explicit ViewContext(Identity s)\n        : sender_(s) {}\n\n    Identity sender() const { return sender_; }\n};\n\n/**\n * @brief Context for anonymous views without caller identity\n * \n * AnonymousViewContext provides read-only database access without\n * exposing the caller's identity. Use this for views that return\n * the same data regardless of who calls them.\n * \n * This is more efficient than ViewContext as it doesn't require\n * identity information to be passed from the host.\n * \n * Key differences from ViewContext:\n * - No sender field (caller identity not available)\n * - Otherwise identical functionality\n * \n * Example usage:\n * @code\n * SPACETIMEDB_VIEW(std::optional<uint64_t>, count_users, Public, AnonymousViewContext ctx) {\n *     return Ok(std::optional<uint64_t>(ctx.db[user].count()));\n * }\n * @endcode\n */\nstruct AnonymousViewContext {\n    // Read-only database access - no mutations allowed\n    ReadOnlyDatabaseContext db;\n    \n    // Constructors\n    AnonymousViewContext() = default;\n};\n\n} // namespace SpacetimeDB\n\n#endif // SPACETIMEDB_VIEW_CONTEXT_H\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb/view_macros.h",
    "content": "#pragma once\n\n#include \"spacetimedb/view_context.h\"\n#include \"spacetimedb/internal/Module.h\"\n#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/macros.h\"  // For parseParameterNames\n#include \"spacetimedb/error_handling.h\"\n#include <string>\n#include <vector>\n#include <type_traits>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n// Note: parseParameterNames is in macros.h and works for any context type\n// (ReducerContext, ViewContext, or AnonymousViewContext)\n\n/**\n * @brief Type trait to validate view return types\n * \n * Views must return std::vector<T> or std::optional<T> where T is a SpacetimeType.\n * This is enforced at compile-time via static_assert in the macro.\n */\ntemplate<typename T>\nstruct is_valid_view_return_type : std::false_type {};\n\n// Specialization for std::vector<T> where T is Serializable\ntemplate<typename T>\nstruct is_valid_view_return_type<std::vector<T>> \n    : std::integral_constant<bool, bsatn::Serializable<T>> {};\n\n// Specialization for std::optional<T> where T is Serializable\ntemplate<typename T>\nstruct is_valid_view_return_type<std::optional<T>>\n    : std::integral_constant<bool, bsatn::Serializable<T>> {};\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n/**\n * @brief Macro for defining SpacetimeDB views\n * \n * CRITICAL: Return type must be explicit (no arrow notation in C++ macros).\n * Views return the specified return_type directly.\n * \n * NOTE: Additional parameters are temporarily disabled as the SpacetimeDB host\n * doesn't fully support parameterized views yet. Only the context parameter is allowed.\n * \n * Allowed return types:\n * - std::vector<T> where T is a SpacetimeType\n * - std::optional<T> where T is a SpacetimeType\n * \n * @param return_type The return type (e.g., std::vector<Person>, std::optional<Person>)\n * @param view_name The name of the view function\n * @param access_enum Must be Public (Private views not yet supported)\n * @param ctx_param Must be either ViewContext ctx or AnonymousViewContext ctx\n * \n * Example:\n * @code\n * SPACETIMEDB_VIEW(std::vector<Person>, get_adults, Public, ViewContext ctx) {\n *     std::vector<Person> adults;\n *     for (const auto& person : ctx.db[person_age].filter(range_from(18u))) {\n *         adults.push_back(person);\n *     }\n *     return adults;\n * }\n * \n * SPACETIMEDB_VIEW(std::optional<uint64_t>, count_people, Public, AnonymousViewContext ctx) {\n *     return std::optional<uint64_t>(ctx.db[person].count());\n * }\n * \n * // TODO: Future with parameters:\n * // SPACETIMEDB_VIEW(std::vector<Person>, search_by_age, Public, ViewContext ctx, uint32_t min_age, uint32_t max_age) {\n * //     std::vector<Person> results;\n * //     for (const auto& person : ctx.db[person_age].filter(range_between(min_age, max_age))) {\n * //         results.push_back(person);\n * //     }\n * //     return Ok(results);\n * // }\n * @endcode\n */\n/* TODO: When parameters are supported, change signature to:\n * #define SPACETIMEDB_VIEW(return_type, view_name, access_enum, ctx_param, ...)\n */\n#define SPACETIMEDB_VIEW(return_type, view_name, access_enum, ctx_param) \\\n    /* Compile-time assertion that views must be Public for now */ \\\n    static_assert(access_enum == SpacetimeDB::Internal::TableAccess::Public, \\\n        \"Views must be Public - Private views are not yet supported\"); \\\n    \\\n    /* Validate return type at compile-time */ \\\n    static_assert(::SpacetimeDB::Internal::is_valid_view_return_type<return_type>::value, \\\n        \"View return type must be std::vector<T> or std::optional<T> where T is a SpacetimeType\"); \\\n    \\\n    /* TODO: When parameters are supported, forward declaration becomes: */ \\\n    /* return_type view_name(ctx_param, __VA_ARGS__); */ \\\n    return_type view_name(ctx_param); \\\n    \\\n    /* Preinit registration function */ \\\n    /* Views run at priority 40 to ensure tables/reducers are registered first */ \\\n    __attribute__((export_name(\"__preinit__40_view_\" #view_name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_view_, view_name)() { \\\n        /* Convert access_enum to bool (matching SPACETIMEDB_TABLE pattern) */ \\\n        bool is_public = (access_enum == SpacetimeDB::Internal::TableAccess::Public); \\\n        \\\n        /* TODO: When parameters are supported, uncomment: */ \\\n        /* std::vector<std::string> param_names = parseParameterNames(#__VA_ARGS__); */ \\\n        std::vector<std::string> param_names; \\\n        \\\n        /* Register the view with the V10Builder system */ \\\n        /* RegisterView validates ctx_param is ViewContext or AnonymousViewContext */ \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterView<decltype(&view_name)>( \\\n            #view_name, view_name, is_public, param_names); \\\n    } \\\n    \\\n    /* TODO: When parameters are supported, function definition becomes: */ \\\n    /* return_type view_name(ctx_param, __VA_ARGS__) */ \\\n    return_type view_name(ctx_param)\n\n#define SPACETIMEDB_VIEW_NAMED(return_type, view_name, canonical_name, access_enum, ctx_param) \\\n    static_assert(access_enum == SpacetimeDB::Internal::TableAccess::Public, \\\n        \"Views must be Public - Private views are not yet supported\"); \\\n    static_assert(::SpacetimeDB::Internal::is_valid_view_return_type<return_type>::value, \\\n        \"View return type must be std::vector<T> or std::optional<T> where T is a SpacetimeType\"); \\\n    return_type view_name(ctx_param); \\\n    __attribute__((export_name(\"__preinit__40_view_\" #view_name))) \\\n    extern \"C\" void CONCAT(_spacetimedb_preinit_register_view_, view_name)() { \\\n        bool is_public = (access_enum == SpacetimeDB::Internal::TableAccess::Public); \\\n        std::vector<std::string> param_names; \\\n        ::SpacetimeDB::Internal::getV10Builder().RegisterView<decltype(&view_name)>( \\\n            #view_name, view_name, is_public, param_names); \\\n        SpacetimeDB::Module::RegisterExplicitFunctionName(#view_name, canonical_name); \\\n    } \\\n    return_type view_name(ctx_param)\n"
  },
  {
    "path": "crates/bindings-cpp/include/spacetimedb.h",
    "content": "#pragma once\n\n/**\n * SpacetimeDB C++20 Module API - Single Include Header\n * \n * Complete, optimized API for building SpacetimeDB modules with support for:\n * - Tables with field constraints\n * - BSATN serialization\n * - Reducers (user-defined and lifecycle)\n * - Database operations\n * - Logging and debugging\n * \n * =============================================================================\n * USAGE EXAMPLES\n * =============================================================================\n * \n * Table Definition:\n *    struct User {\n *        uint32_t id;\n *        std::string username;\n *        std::string email;\n *    };\n *    SPACETIMEDB_STRUCT(User, id, username, email)\n *    SPACETIMEDB_TABLE(User, \"users\", Public,\n *        PrimaryKeyAutoInc(id),\n *        Unique(email)\n *    )\n * \n * Lifecycle Reducers:\n *    SPACETIMEDB_INIT(init_module, ReducerContext ctx) {\n *        LOG_INFO(\"Module initialized!\");\n *        return Ok();\n *    }\n * \n * User Reducers:\n *    void add_user(ReducerContext ctx, std::string username, std::string email) {\n *        User user{0, username, email}; // id auto-generated\n *        ctx.db.table<User>(\"users\").insert(user);\n *    }\n *    REGISTER_REDUCER(add_user, add_user)\n */\n\n// =============================================================================\n// STANDARD LIBRARY DEPENDENCIES\n// =============================================================================\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <array>\n#include <string_view>\n#include <concepts>\n#include <functional>\n#include <type_traits>\n#include <span>\n\n// =============================================================================\n// CORE SPACETIMEDB TYPE SYSTEM\n// =============================================================================\n\n// Core types and BSATN support\n#include \"spacetimedb/bsatn/types.h\"\n#include \"spacetimedb/bsatn/timestamp.h\"\n#include \"spacetimedb/bsatn/time_duration.h\"\n#include \"spacetimedb/bsatn/uuid.h\"\n#include \"spacetimedb/bsatn/result.h\"\n#include \"spacetimedb/bsatn/writer.h\"\n#include \"spacetimedb/bsatn/reader.h\"\n#include \"spacetimedb/bsatn/algebraic_type.h\"\n#include \"spacetimedb/bsatn/traits.h\"\n#include \"spacetimedb/bsatn/sum_type.h\"\n#include \"spacetimedb/bsatn/schedule_at.h\"\n\n// Autogenerated types for module definition\n#include \"spacetimedb/internal/autogen/SumType.g.h\"  // Complete SumType definition first\n#include \"spacetimedb/internal/autogen/RawModuleDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawTableDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/Typespace.g.h\"\n#include \"spacetimedb/internal/autogen/ProductType.g.h\"\n#include \"spacetimedb/internal/autogen/ProductTypeElement.g.h\"\n#include \"spacetimedb/internal/autogen/TableAccess.g.h\"\n#include \"spacetimedb/internal/autogen/TableType.g.h\"\n\n// =============================================================================\n// MODULE SYSTEM INFRASTRUCTURE\n// =============================================================================\n\n// Core module infrastructure\n#include \"spacetimedb/internal/Module.h\"\n#include \"spacetimedb/internal/field_registration.h\"\n\n// Logging system\n#include \"spacetimedb/logger.h\"\n\n// =============================================================================\n// TABLE AND CONSTRAINT SYSTEM\n// =============================================================================\n\n// Type extensions for special types\n#include \"spacetimedb/bsatn/type_extensions.h\"\n\n// Table operations and constraint support\n#include \"spacetimedb/table.h\"\n#include \"spacetimedb/database.h\"\n#include \"spacetimedb/table_with_constraints.h\"\n\n// Range queries\n#include \"spacetimedb/range_queries.h\"\n\n// =============================================================================\n// REDUCER SYSTEM\n// =============================================================================\n\n// Reducer context and macros\n#include \"spacetimedb/reducer_context.h\"\n#include \"spacetimedb/reducer_macros.h\"\n#include \"spacetimedb/reducer_error.h\"\n\n// Client visibility filters\n#include \"spacetimedb/client_visibility_filter.h\"\n\n// =============================================================================\n// PROCEDURE SYSTEM\n// =============================================================================\n\n// Procedure context and macros\n#include \"spacetimedb/procedure_macros.h\"\n\n// =============================================================================\n// VIEW SYSTEM\n// =============================================================================\n\n// View context and macros\n#include \"spacetimedb/view_macros.h\"\n\n// =============================================================================\n// CONVENIENCE ALIASES AND COMPATIBILITY\n// =============================================================================\n\n// Convenience aliases for table access levels\nnamespace SpacetimeDB {\n    // Re-export table access constants for convenience\n    constexpr auto Public = Internal::TableAccess::Public;\n    constexpr auto Private = Internal::TableAccess::Private;\n}\n\n// =============================================================================\n// UNIFIED MACRO SYSTEM\n// =============================================================================\n\n// Use unified macro system\n#include \"spacetimedb/macros.h\"\n\n\n// =============================================================================\n// MODULE DEFINITION UTILITIES\n// =============================================================================\n\n// Forward declarations for module system compatibility\nnamespace spacetimedb {\n    // Template specialization support\n    template<typename T> struct table_metadata;\n    \n    namespace internal {\n        /**\n         * @brief Serialize the current module definition to BSATN\n         * @return Serialized module definition\n         */\n        inline std::vector<uint8_t> serialize_module_def() {\n            return SpacetimeDB::Internal::Module::SerializeModuleDef();\n        }\n    }\n\n    // =============================================================================\n    // CONVENIENCE TYPE ALIASES\n    // =============================================================================\n    \n    // Vector type aliases for SpacetimeDB types\n    using VecTimeDuration = std::vector<::SpacetimeDB::TimeDuration>;\n}\n\n// =============================================================================\n// DOCUMENTATION AND USAGE NOTES\n// =============================================================================\n\n/**\n * CONSTRAINT TYPES REFERENCE:\n * \n * Basic Constraints:\n * - PrimaryKey(field) - Primary key constraint (unique, non-null)\n * - Unique(field) - Unique constraint (allows null)\n * - Index(field) - Index for fast lookups\n * - AutoInc(field) - Auto-incrementing field\n * \n * Combination Constraints:\n * - PrimaryKeyAutoInc(field) - Primary key with auto-increment\n * - UniqueAutoInc(field) - Unique field with auto-increment  \n * - IndexAutoInc(field) - Indexed field with auto-increment\n * \n * LIFECYCLE REDUCERS:\n * - SPACETIMEDB_INIT(name, ReducerContext ctx) - Called when module is initialized\n * - SPACETIMEDB_CLIENT_CONNECTED(name, ReducerContext ctx) - Called when client connects\n * - SPACETIMEDB_CLIENT_DISCONNECTED(name, ReducerContext ctx) - Called when client disconnects\n * \n * LOGGING LEVELS:\n * - LOG_DEBUG(msg) - Debug information\n * - LOG_INFO(msg) - General information\n * - LOG_WARN(msg) - Warning messages\n * - LOG_ERROR(msg) - Error messages\n * - LOG_PANIC(msg) - Critical errors (may terminate)\n */\n\n// =============================================================================\n// BSATN IMPLEMENTATION INCLUDES\n// =============================================================================\n\n// Include BSATN implementation files after all headers are defined\n#include \"spacetimedb/bsatn/types_impl.h\"\n#include \"spacetimedb/bsatn/schedule_at_impl.h\"\n#include \"spacetimedb/enum_macro.h\"\n"
  },
  {
    "path": "crates/bindings-cpp/src/abi/module_exports.cpp",
    "content": "#include \"spacetimedb/abi/FFI.h\"\n#include \"spacetimedb/internal/autogen/SumType.g.h\"  // Complete SumType definition before Module.h\n#include \"spacetimedb/internal/Module.h\"  // Use new Module API\n#include \"spacetimedb/bsatn/timestamp.h\"     // For Timestamp\n\n#include <vector>\n#include <cstddef> // For size_t\n#include <string>  // For std::string in error handling\n#include <iostream> // For temporary error logging if needed\n\n// Export definitions - these implement the declarations from abi.h\n\nextern \"C\" {\n\n    STDB_EXPORT(__describe_module__)\n    void __describe_module__(SpacetimeDB::BytesSink description) {\n        // Use the new Module API directly with opaque type\n        SpacetimeDB::Internal::Module::__describe_module__(description);\n    }\n\n    STDB_EXPORT(__call_reducer__)\n    int16_t __call_reducer__(\n        uint32_t reducer_id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        uint64_t conn_id_0, uint64_t conn_id_1,\n        uint64_t timestamp_us,\n        SpacetimeDB::BytesSource args,\n        SpacetimeDB::BytesSink error\n    ) {\n        // Create timestamp - already in microseconds\n        SpacetimeDB::Timestamp ts(timestamp_us);\n        \n        // Call Module's implementation with opaque types\n        auto result = SpacetimeDB::Internal::Module::__call_reducer__(\n            reducer_id,\n            sender_0, sender_1, sender_2, sender_3,\n            conn_id_0, conn_id_1,\n            ts,\n            args,\n            error\n        );\n        \n        // Convert Status to int16_t\n        if (result == SpacetimeDB::StatusCode::OK) {\n            return 0;\n        } else if (result == SpacetimeDB::StatusCode::NO_SUCH_REDUCER) {\n            return -1;\n        } else if (result == SpacetimeDB::StatusCode::HOST_CALL_FAILURE) {\n            return 1;\n        } else {\n            return -4;\n        }\n    }\n\n    STDB_EXPORT(__call_view__)\n    int16_t __call_view__(\n        uint32_t view_id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        SpacetimeDB::BytesSource args,\n        SpacetimeDB::BytesSink result\n    ) {\n        return SpacetimeDB::Internal::Module::__call_view__(\n            view_id,\n            sender_0, sender_1, sender_2, sender_3,\n            args,\n            result\n        );\n    }\n\n    STDB_EXPORT(__call_view_anon__)\n    int16_t __call_view_anon__(\n        uint32_t view_id,\n        SpacetimeDB::BytesSource args,\n        SpacetimeDB::BytesSink result\n    ) {\n        return SpacetimeDB::Internal::Module::__call_view_anon__(\n            view_id,\n            args,\n            result\n        );\n    }\n\n    STDB_EXPORT(__call_procedure__)\n    int16_t __call_procedure__(\n        uint32_t id,\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n        uint64_t conn_id_0, uint64_t conn_id_1,\n        uint64_t timestamp_microseconds,\n        SpacetimeDB::BytesSource args_source,\n        SpacetimeDB::BytesSink result_sink\n    ) {\n        return SpacetimeDB::Internal::Module::__call_procedure__(\n            id,\n            sender_0, sender_1, sender_2, sender_3,\n            conn_id_0, conn_id_1,\n            timestamp_microseconds,\n            args_source,\n            result_sink\n        );\n    }\n\n} // extern \"C\"\n"
  },
  {
    "path": "crates/bindings-cpp/src/abi/wasi_shims.cpp",
    "content": "// WASI shims for SpacetimeDB C++ modules\n// This file provides stub implementations of WASI functions to enable\n// C++ standard library usage without requiring actual WASI support\n\n#include <cstdint>\n#include <cstddef>\n\n// SpacetimeDB imports we need for console output\n// Import from spacetime_10.0 module as required by SpacetimeDB ABI\nextern \"C\" __attribute__((import_module(\"spacetime_10.0\"), import_name(\"console_log\")))\nvoid console_log(uint8_t log_level, const uint8_t* target, uint32_t target_len,\n                 const uint8_t* filename, uint32_t filename_len, uint32_t line_number,\n                 const uint8_t* message, uint32_t message_len);\n\n// Helper macro for string literals\n#define CSTR(s) (uint8_t*)s, sizeof(s) - 1\n\n// WASI types\nstruct __wasi_ciovec_t {\n    const uint8_t* buf;\n    size_t buf_len;\n};\n\ntypedef uint32_t __wasi_fd_t;\ntypedef uint32_t __wasi_size_t;\ntypedef uint32_t __wasi_errno_t;\n\n// File descriptors\n#define STDIN_FILENO 0\n#define STDOUT_FILENO 1\n#define STDERR_FILENO 2\n\n// WASI error codes\n#define __WASI_ERRNO_SUCCESS 0\n#define __WASI_ERRNO_BADF 8\n\n// Macro to define WASI shim functions that always succeed\n#define WASI_SHIM(name, params) \\\n    extern \"C\" __wasi_errno_t __wasi_##name params { return __WASI_ERRNO_SUCCESS; }\n\n// Environment functions\nWASI_SHIM(environ_get, (int32_t, int32_t))\nWASI_SHIM(environ_sizes_get, (int32_t, int32_t))\n\n// Clock functions\nWASI_SHIM(clock_time_get, (int32_t, int64_t, int32_t))\n\n// File descriptor functions\nWASI_SHIM(fd_advise, (int32_t, int64_t, int64_t, int32_t))\nWASI_SHIM(fd_allocate, (int32_t, int64_t, int64_t))\nWASI_SHIM(fd_close, (int32_t))\nWASI_SHIM(fd_datasync, (int32_t))\nWASI_SHIM(fd_fdstat_get, (int32_t, int32_t))\nWASI_SHIM(fd_fdstat_set_flags, (int32_t, int32_t))\nWASI_SHIM(fd_fdstat_set_rights, (int32_t, int64_t, int64_t))\nWASI_SHIM(fd_filestat_get, (int32_t, int32_t))\nWASI_SHIM(fd_filestat_set_size, (int32_t, int64_t))\nWASI_SHIM(fd_filestat_set_times, (int32_t, int64_t, int64_t, int32_t))\nWASI_SHIM(fd_pread, (int32_t, int32_t, int32_t, int64_t, int32_t))\nWASI_SHIM(fd_prestat_dir_name, (int32_t, int32_t, int32_t))\nWASI_SHIM(fd_pwrite, (int32_t, int32_t, int32_t, int64_t, int32_t))\nWASI_SHIM(fd_read, (int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(fd_readdir, (int32_t, int32_t, int32_t, int64_t, int32_t))\nWASI_SHIM(fd_renumber, (int32_t, int32_t))\nWASI_SHIM(fd_seek, (int32_t, int64_t, int32_t, int32_t))\nWASI_SHIM(fd_sync, (int32_t))\nWASI_SHIM(fd_tell, (int32_t, int32_t))\n\n// Path functions\nWASI_SHIM(path_create_directory, (int32_t, int32_t, int32_t))\nWASI_SHIM(path_filestat_get, (int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(path_filestat_set_times, (int32_t, int32_t, int32_t, int32_t, int64_t, int64_t, int32_t))\nWASI_SHIM(path_link, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(path_open, (int32_t, int32_t, int32_t, int32_t, int32_t, int64_t, int64_t, int32_t, int32_t))\nWASI_SHIM(path_readlink, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(path_remove_directory, (int32_t, int32_t, int32_t))\nWASI_SHIM(path_rename, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(path_symlink, (int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(path_unlink_file, (int32_t, int32_t, int32_t))\n\n// Other functions\nWASI_SHIM(poll_oneoff, (int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(sched_yield, ())\nWASI_SHIM(random_get, (int32_t, int32_t))\nWASI_SHIM(sock_accept, (int32_t, int32_t, int32_t))\nWASI_SHIM(sock_recv, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(sock_send, (int32_t, int32_t, int32_t, int32_t, int32_t))\nWASI_SHIM(sock_shutdown, (int32_t, int32_t))\n\n// Special handling for args_sizes_get and args_get\n// We provide a dummy executable name to avoid issues\nextern \"C\" {\nconst char executable_name[] = \"stdb.wasm\";\n\n__wasi_errno_t __wasi_args_sizes_get(__wasi_size_t* argc, __wasi_size_t* argv_buf_size) {\n    *argc = 1;\n    *argv_buf_size = sizeof(executable_name);\n    return __WASI_ERRNO_SUCCESS;\n}\n\n__wasi_errno_t __wasi_args_get(uint8_t** argv, uint8_t* argv_buf) {\n    argv[0] = argv_buf;\n    __builtin_memcpy(argv_buf, executable_name, sizeof(executable_name));\n    return __WASI_ERRNO_SUCCESS;\n}\n\n// Clock resolution should be non-zero\n__wasi_errno_t __wasi_clock_res_get(int32_t, uint64_t* timestamp) {\n    *timestamp = 1;\n    return __WASI_ERRNO_SUCCESS;\n}\n\n// Special handling for fd_write to avoid infinite loops\n// Redirect output to console_log\n__wasi_errno_t __wasi_fd_write(__wasi_fd_t fd, const __wasi_ciovec_t* iovs,\n                        size_t iovs_len, __wasi_size_t* retptr0) {\n    *retptr0 = 0;\n    \n    // Concatenate all iovs into a single buffer to avoid multiple log lines\n    size_t total_len = 0;\n    for (size_t i = 0; i < iovs_len; i++) {\n        total_len += iovs[i].buf_len;\n    }\n    \n    // Skip if nothing to write\n    if (total_len == 0) {\n        return __WASI_ERRNO_SUCCESS;\n    }\n    \n    // Allocate a temporary buffer (on stack if small, heap if large)\n    constexpr size_t STACK_BUFFER_SIZE = 1024;\n    uint8_t stack_buffer[STACK_BUFFER_SIZE];\n    uint8_t* buffer = stack_buffer;\n    bool heap_allocated = false;\n    \n    if (total_len > STACK_BUFFER_SIZE) {\n        buffer = new uint8_t[total_len];\n        heap_allocated = true;\n    }\n    \n    // Copy all iovs into the buffer\n    size_t offset = 0;\n    for (size_t i = 0; i < iovs_len; i++) {\n        if (iovs[i].buf_len > 0) {\n            __builtin_memcpy(buffer + offset, iovs[i].buf, iovs[i].buf_len);\n            offset += iovs[i].buf_len;\n            *retptr0 += iovs[i].buf_len;\n        }\n    }\n    \n    // Make a single console_log call with the complete message\n    uint8_t log_level = (fd == STDERR_FILENO) ? 1 : 2; // 1=WARN, 2=INFO\n    console_log(log_level, CSTR(\"wasi\"), CSTR(__FILE__), __LINE__, \n               buffer, offset);\n    \n    // Clean up heap allocation if needed\n    if (heap_allocated) {\n        delete[] buffer;\n    }\n    \n    return __WASI_ERRNO_SUCCESS;\n}\n\n// fd_prestat_get returns BADF to indicate end of iteration\n__wasi_errno_t __wasi_fd_prestat_get(int32_t, int32_t) {\n    return __WASI_ERRNO_BADF;\n}\n\n// Actually exit on proc_exit\n[[noreturn]] void __wasi_proc_exit(int32_t code) {\n    // In a WASM module, we can't actually exit\n    // Mark the exit code as used (could be useful for debugging)\n    (void)code;\n    // Just spin forever\n    while (true) {}\n}\n\n// Additional sock_accept for compatibility\n__wasi_errno_t sock_accept(int32_t, int32_t, int32_t) {\n    return __WASI_ERRNO_SUCCESS;\n}\n\n} // extern \"C\"\n\n// Emscripten-specific shims\nextern \"C\" {\n\n// Emscripten runtime functions that might be imported\nvoid emscripten_notify_memory_growth(int32_t) {\n    // No-op - memory growth is handled by the runtime\n}\n\n} // extern \"C\""
  },
  {
    "path": "crates/bindings-cpp/src/internal/AlgebraicType.cpp",
    "content": "// Implementation for AlgebraicType to handle circular dependencies\n#include \"spacetimedb/internal/autogen/AlgebraicType.g.h\"\n#include \"spacetimedb/internal/autogen/SumType.g.h\"\n#include \"spacetimedb/internal/autogen/ProductType.g.h\"\n#include \"spacetimedb/internal/autogen/SumTypeVariant.g.h\"\n#include \"spacetimedb/internal/autogen/ProductTypeElement.g.h\"\n\nnamespace SpacetimeDB::Internal {\n\n// Default constructor - String type\nAlgebraicType::AlgebraicType() : tag_(Tag::String), data_(std::in_place_index<4>, std::monostate{}) {}\n\n// Constructor for primitive types using in_place_index\nAlgebraicType::AlgebraicType(Tag primitive_tag) : tag_(primitive_tag) {\n    switch (primitive_tag) {\n        case Tag::Ref: \n            data_.emplace<0>(uint32_t{0}); \n            break;\n        case Tag::Sum: \n            data_.emplace<1>(std::unique_ptr<SpacetimeDB::Internal::SumType>{}); \n            break;\n        case Tag::Product: \n            data_.emplace<2>(std::unique_ptr<SpacetimeDB::Internal::ProductType>{}); \n            break;\n        case Tag::Array: \n            data_.emplace<3>(std::unique_ptr<SpacetimeDB::Internal::AlgebraicType>{}); \n            break;\n        default: \n            // All primitive types use index 4 with monostate\n            data_.emplace<4>(std::monostate{}); \n            break;\n    }\n}\n\n// Copy constructor - must handle unique_ptr deep copy manually\nAlgebraicType::AlgebraicType(const AlgebraicType& other) : tag_(other.tag_) {\n    // Deep copy for pointer types, direct copy for others\n    switch (tag_) {\n        case Tag::Ref:\n            data_.emplace<0>(std::get<0>(other.data_));\n            break;\n        case Tag::Sum: {\n            auto& ptr = std::get<1>(other.data_);\n            if (ptr) {\n                data_.emplace<1>(std::make_unique<SpacetimeDB::Internal::SumType>(*ptr));\n            } else {\n                data_.emplace<1>(std::unique_ptr<SpacetimeDB::Internal::SumType>{});\n            }\n            break;\n        }\n        case Tag::Product: {\n            auto& ptr = std::get<2>(other.data_);\n            if (ptr) {\n                data_.emplace<2>(std::make_unique<SpacetimeDB::Internal::ProductType>(*ptr));\n            } else {\n                data_.emplace<2>(std::unique_ptr<SpacetimeDB::Internal::ProductType>{});\n            }\n            break;\n        }\n        case Tag::Array: {\n            auto& ptr = std::get<3>(other.data_);\n            if (ptr) {\n                data_.emplace<3>(std::make_unique<SpacetimeDB::Internal::AlgebraicType>(*ptr));\n            } else {\n                data_.emplace<3>(std::unique_ptr<SpacetimeDB::Internal::AlgebraicType>{});\n            }\n            break;\n        }\n        default: \n            // All primitive types use index 4 with monostate\n            data_.emplace<4>(std::monostate{});\n            break;\n    }\n}\n\n// Assignment operator\nAlgebraicType& AlgebraicType::operator=(const AlgebraicType& other) {\n    if (this != &other) {\n        AlgebraicType temp(other);\n        std::swap(tag_, temp.tag_);\n        std::swap(data_, temp.data_);\n    }\n    return *this;\n}\n\n// Template setters by index\ntemplate<size_t Index, typename T>\nvoid AlgebraicType::set(T&& value) {\n    // For primitive types, always use index 4\n    if (Index >= 4) {\n        tag_ = static_cast<Tag>(Index);\n        data_.template emplace<4>(std::monostate{});\n    } else {\n        tag_ = static_cast<Tag>(Index);\n        data_.template emplace<Index>(std::forward<T>(value));\n    }\n}\n\n// BSATN serialization (only serialization needed for Internal types)\nvoid AlgebraicType::bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {\n    writer.write_u8(static_cast<uint8_t>(tag_));\n    //fprintf(stdout, \"DEBUG: AlgebraicType serializing tag=%d START\\n\", static_cast<int>(tag_));\n    \n    // Serialize data based on tag\n    switch (tag_) {\n        case Tag::Ref:\n            // Write the type reference\n            writer.write_u32_le(std::get<0>(data_));\n            //fprintf(stdout, \"  Ref to type %u\\n\", std::get<0>(data_));\n            break;\n            \n        case Tag::Sum: {\n            // Serialize the SumType\n            const auto& sum_ptr = std::get<1>(data_);\n            if (sum_ptr) {\n                sum_ptr->bsatn_serialize(writer);\n            } else {\n                // Write empty sum (0 variants)\n                writer.write_u32_le(0);\n            }\n            break;\n        }\n        \n        case Tag::Product: {\n            // Serialize the ProductType\n            const auto& product_ptr = std::get<2>(data_);\n            if (product_ptr) {\n                //fprintf(stdout, \"  Product with %zu elements\\n\", product_ptr->elements.size());\n                product_ptr->bsatn_serialize(writer);\n            } else {\n                // Write empty product (0 elements)\n                //fprintf(stdout, \"  Empty Product (nullptr) - writing 0 elements\\n\");\n                writer.write_u32_le(0);\n            }\n            break;\n        }\n        \n        case Tag::Array: {\n            // Serialize the element type\n            const auto& elem_ptr = std::get<3>(data_);\n            if (elem_ptr) {\n                elem_ptr->bsatn_serialize(writer);\n            } else {\n                // Write String as default element type\n                writer.write_u8(static_cast<uint8_t>(Tag::String));\n            }\n            break;\n        }\n        \n        default:\n            // Primitive types - no additional data to write\n            //fprintf(stdout, \"DEBUG: AlgebraicType primitive tag=%d END\\n\", static_cast<int>(tag_));\n            break;\n    }\n    //fprintf(stdout, \"DEBUG: AlgebraicType serializing tag=%d COMPLETE\\n\", static_cast<int>(tag_));\n}\n\n// Equality\nbool AlgebraicType::operator==(const AlgebraicType& other) const {\n    return tag_ == other.tag_ && data_ == other.data_;\n}\n\n// Explicit template instantiations for all possible indices\ntemplate void AlgebraicType::set<0, uint32_t>(uint32_t&&);\ntemplate void AlgebraicType::set<0, uint32_t&>(uint32_t&);  // For lvalue references\ntemplate void AlgebraicType::set<1, std::unique_ptr<SumType>>(std::unique_ptr<SumType>&&);\ntemplate void AlgebraicType::set<2, std::unique_ptr<ProductType>>(std::unique_ptr<ProductType>&&);\ntemplate void AlgebraicType::set<3, std::unique_ptr<AlgebraicType>>(std::unique_ptr<AlgebraicType>&&);\ntemplate void AlgebraicType::set<4, std::monostate>(std::monostate&&);\n\n} // namespace SpacetimeDB::Internal"
  },
  {
    "path": "crates/bindings-cpp/src/internal/Module.cpp",
    "content": "// SpacetimeDB C++ bindings - V9 Serialization Implementation\n// Clean implementation for simplified type registration and serialization\n\n#include \"spacetimedb.h\"\n#include \"spacetimedb/internal/Module.h\"\n#include \"spacetimedb/internal/buffer_pool.h\"\n#include \"spacetimedb/internal/autogen/RawModuleDef.g.h\"\n#include \"spacetimedb/internal/autogen/RawModuleDefV10.g.h\"\n#include \"spacetimedb/internal/autogen/RawTypeDefV10.g.h\"\n#include \"spacetimedb/internal/v9_builder.h\"\n#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/internal/module_type_registration.h\"\n#include \"spacetimedb/abi/FFI.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"spacetimedb/bsatn/writer.h\"\n#include \"spacetimedb/reducer_error.h\"\n#include \"spacetimedb/view_context.h\"\n#include \"spacetimedb/procedure_context.h\"\n#include <cstring>\n#include <vector>\n#include <functional>\n#include <cctype>\n\nnamespace SpacetimeDB {\n\n// Global V9 module structure - accessible from preinit functions\nnamespace Internal {\n    // Thread-local reducer error message storage\n    thread_local std::optional<std::string> g_reducer_error_message = std::nullopt;\n\n    // Global reducer handler storage for runtime dispatch\n    struct ReducerHandler {\n        std::string name;\n        std::function<void(ReducerContext&, BytesSource)> handler;\n        std::optional<Lifecycle> lifecycle;\n    };\n    static std::vector<ReducerHandler> g_reducer_handlers;\n    \n    // Global view handler storage for runtime dispatch\n    struct ViewHandler {\n        std::string name;\n        std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler;\n    };\n    static std::vector<ViewHandler> g_view_handlers;\n    \n    struct AnonymousViewHandler {\n        std::string name;\n        std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler;\n    };\n    static std::vector<AnonymousViewHandler> g_view_anon_handlers;\n    \n    // Global procedure handler storage for runtime dispatch\n    struct ProcedureHandler {\n        std::string name;\n        std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler;\n    };\n    static std::vector<ProcedureHandler> g_procedure_handlers;\n    \n    /**\n     * @brief View result header for serializing view return values\n     * \n     * This enum is serialized before the actual view data to indicate\n     * the type of result being returned.\n     */\n    enum class ViewResultHeader : uint8_t {\n        RowData = 0,  // Followed by BSATN-encoded Vec<RowType>\n        RawSql = 1,   // Followed by SQL string (future use)\n    };\n    \n    // Global error flag for multiple primary key detection\n    static bool g_multiple_primary_key_error = false;\n    static std::string g_multiple_primary_key_table_name = \"\";\n    static bool g_constraint_registration_error = false;\n    static std::string g_constraint_registration_error_code = \"\";\n    static std::string g_constraint_registration_error_details = \"\";\n    \n    // External global flags for circular reference detection (defined in module_type_registration.cpp)\n    extern bool g_circular_ref_error;\n    extern std::string g_circular_ref_type_name;\n    \n    // Function to set the multiple primary key error flag\n    void SetMultiplePrimaryKeyError(const std::string& table_name) {\n        g_multiple_primary_key_error = true;\n        g_multiple_primary_key_table_name = table_name;\n        fprintf(stderr, \"ERROR: Multiple primary keys detected in table '%s'\\n\", table_name.c_str());\n    }\n\n    void SetConstraintRegistrationError(const std::string& code, const std::string& details) {\n        g_constraint_registration_error = true;\n        g_constraint_registration_error_code = code;\n        g_constraint_registration_error_details = details;\n        fprintf(stderr, \"ERROR: Constraint registration failed [%s] %s\\n\", code.c_str(), details.c_str());\n    }\n    \n    // Register a reducer handler (called by V9Builder during registration)\n    void RegisterReducerHandler(const std::string& name, \n                                std::function<void(ReducerContext&, BytesSource)> handler,\n                                std::optional<Lifecycle> lifecycle) {\n        g_reducer_handlers.push_back({name, handler, lifecycle});\n    }\n    \n    // Register a view handler (called by V9Builder during registration)\n    void RegisterViewHandler(const std::string& name,\n                            std::function<std::vector<uint8_t>(ViewContext&, BytesSource)> handler) {\n        g_view_handlers.push_back({name, handler});\n    }\n    \n    // Register an anonymous view handler (called by V9Builder during registration)\n    void RegisterAnonymousViewHandler(const std::string& name,\n                                     std::function<std::vector<uint8_t>(AnonymousViewContext&, BytesSource)> handler) {\n        g_view_anon_handlers.push_back({name, handler});\n    }\n    \n    // Register a procedure handler (called by V9Builder during registration)\n    void RegisterProcedureHandler(const std::string& name,\n                                  std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler) {\n        g_procedure_handlers.push_back({name, handler});\n    }\n    \n    // Get the number of registered view handlers\n    size_t GetViewHandlerCount() {\n        return g_view_handlers.size();\n    }\n    \n    size_t GetAnonymousViewHandlerCount() {\n        return g_view_anon_handlers.size();\n    }\n    \n    // Get the number of registered procedure handlers\n    size_t GetProcedureHandlerCount() {\n        return g_procedure_handlers.size();\n    }\n    \n    void SetTableIsEventFlag(const std::string& table_name, bool is_event) {\n        getV10Builder().SetTableIsEventFlag(table_name, is_event);\n    }\n\n    bool GetTableIsEventFlag(const std::string& table_name) {\n        return getV10Builder().GetTableIsEventFlag(table_name);\n    }\n    \n    // Clear registration state - called at module initialization.\n    void ClearModuleRegistrationState() {\n        ClearV9CompatModuleState();\n        g_reducer_handlers.clear();  // Also clear reducer handlers\n        g_view_handlers.clear();  // Clear view handlers\n        g_view_anon_handlers.clear();  // Clear anonymous view handlers\n        g_procedure_handlers.clear();  // Clear procedure handlers\n        g_multiple_primary_key_error = false;  // Reset error flag\n        g_multiple_primary_key_table_name = \"\";  // Reset error table name\n        g_constraint_registration_error = false;\n        g_constraint_registration_error_code = \"\";\n        g_constraint_registration_error_details = \"\";\n        getV10Builder().Clear();\n    }\n    \n    \n    // Legacy DeferredRegistry system removed - all registration now handled by V9Builder\n\n// Clear global state preinit function - runs before anything else\n// The number 01 ensures this runs first to clear any leftover state\nextern \"C\" __attribute__((export_name(\"__preinit__01_clear_global_state\")))\nvoid __preinit__01_clear_global_state() {\n    ClearModuleRegistrationState();\n    // Also clear module type registration state\n    auto& type_reg = getModuleTypeRegistration();\n    type_reg.clear();\n}\n\n// Validation preinit function - runs after tables (20) and reducers (30)\n// The number 99 ensures this runs last, just before __describe_module__\nextern \"C\" __attribute__((export_name(\"__preinit__99_validate_types\")))\nvoid __preinit__99_validate_types() {\n    // fprintf(stdout, \"[PREINIT_99] Starting validation\\n  g_circular_ref_error = %s\", \n    //     g_circular_ref_error ? \"true\" : \"false\");\n    // if (g_circular_ref_error) {\n    //     fprintf(stdout, \"\\n  g_circular_ref_type_name = %s\", g_circular_ref_type_name.c_str());\n    // }\n    // fflush(stdout);\n    \n    // Check if circular reference error occurred during type building\n    if (g_circular_ref_error) {\n        // Circular reference error detected - create a special error module\n        \n        // Clear the module state to start fresh.\n        getV10Builder().Clear();\n        \n        // Also clear the V9 type registration to remove any partial registrations\n        auto& type_reg = getModuleTypeRegistration();\n        type_reg.clear();\n        \n        // Create the error type name that indicates the circular reference\n        std::string error_type_name = \"ERROR_CIRCULAR_REFERENCE_\" + g_circular_ref_type_name;\n        \n        // Add a single named type export that points to a non-existent typespace index\n        // This will cause SpacetimeDB to error when it tries to resolve the type\n        RawTypeDefV10 error_type;\n        error_type.source_name.scope = {};\n        error_type.source_name.source_name = error_type_name;\n        error_type.ty = 999999; // Invalid typespace index - will cause an error\n        error_type.custom_ordering = false;\n        \n        getV10Builder().GetTypeDefs().push_back(error_type);\n        \n        // Don't add anything to the typespace - this ensures the reference is invalid\n        // The server will fail with an error message that includes our error type name\n        \n        // Also log to stderr for debugging\n        fprintf(stderr, \"\\n[CIRCULAR REFERENCE ERROR] Module cleared and replaced with error type: %s\\n\", error_type_name.c_str());\n        fprintf(stderr, \"  Type '%s' contains a circular reference to itself\\n\", g_circular_ref_type_name.c_str());\n        fflush(stderr);\n        return; // Exit early, don't check other errors\n    }\n    \n    // Check if multiple primary key error occurred during constraint registration\n    if (g_multiple_primary_key_error) {\n        // Multiple primary key error detected - create a special error module\n        \n        // Clear the module state to start fresh.\n        getV10Builder().Clear();\n        \n        // Create the error type name\n        std::string error_type_name = \"ERROR_MULTIPLE_PRIMARY_KEYS_\" + g_multiple_primary_key_table_name;\n        \n        // Add a single named type export that points to a non-existent typespace index\n        // This will cause SpacetimeDB to error when it tries to resolve the type\n        RawTypeDefV10 error_type;\n        error_type.source_name.scope = {};\n        error_type.source_name.source_name = error_type_name;\n        error_type.ty = 999999; // Invalid typespace index - will cause an error\n        error_type.custom_ordering = false;\n        \n        getV10Builder().GetTypeDefs().push_back(error_type);\n        \n        // Don't add anything to the typespace - this ensures the reference is invalid\n        // The server will fail with an error message that includes our error type name\n        \n        // Also log to stderr for debugging\n        fprintf(stderr, \"\\n[CONSTRAINT ERROR] Module cleared and replaced with error type: %s\\n\", error_type_name.c_str());\n        fprintf(stderr, \"Original error: Multiple primary keys detected in table '%s'\\n\\n\", g_multiple_primary_key_table_name.c_str());\n        fflush(stderr);\n        \n        return; // Exit early, don't check type registration errors\n    }\n\n    if (g_constraint_registration_error) {\n        getV10Builder().Clear();\n\n        std::string error_type_name = \"ERROR_CONSTRAINT_REGISTRATION_\" + g_constraint_registration_error_code;\n        for (char& c : error_type_name) {\n            if (!std::isalnum(c) && c != '_') {\n                c = '_';\n            }\n        }\n\n        RawTypeDefV10 error_type;\n        error_type.source_name.scope = {};\n        error_type.source_name.source_name = error_type_name;\n        error_type.ty = 999999;\n        error_type.custom_ordering = false;\n        getV10Builder().GetTypeDefs().push_back(error_type);\n\n        fprintf(stderr, \"\\n[CONSTRAINT REGISTRATION ERROR] Module cleared and replaced with error type: %s\\n\", error_type_name.c_str());\n        fprintf(stderr, \"Original error: %s\\n\\n\", g_constraint_registration_error_details.c_str());\n        fflush(stderr);\n        return;\n    }\n    \n    // Check if any errors occurred during type registration\n    auto& type_reg = getModuleTypeRegistration();\n    if (type_reg.hasError()) {\n        // Type registration detected an error - create a special error module\n        const std::string& error = type_reg.getErrorMessage();\n        \n        // Clear the module state to start fresh.\n        getV10Builder().Clear();\n        \n        // Create an error type name that embeds the error message and type structure\n        std::string error_type_name;\n        if (error.find(\"Recursive type reference\") != std::string::npos) {\n            // Extract the type name from the error message\n            size_t start = error.find(\"'\");\n            size_t end = error.rfind(\"'\");\n            std::string problematic_type = \"unknown\";\n            if (start != std::string::npos && end != std::string::npos && end > start) {\n                problematic_type = error.substr(start + 1, end - start - 1);\n            }\n            error_type_name = \"ERROR_RECURSIVE_TYPE_\" + problematic_type;\n        } else if (error.find(\"Missing type name\") != std::string::npos) {\n            // Get the type description and sanitize it for use in the error name\n            std::string type_desc = type_reg.getErrorTypeDescription();\n            \n            // Replace problematic characters with underscores\n            for (char& c : type_desc) {\n                if (!std::isalnum(c) && c != '_') {\n                    c = '_';\n                }\n            }\n            \n            // Limit length to avoid overly long names\n            if (type_desc.length() > 100) {\n                type_desc = type_desc.substr(0, 100);\n            }\n            \n            error_type_name = \"ERROR_MISSING_TYPE_NAME_\" + type_desc;\n        } else {\n            error_type_name = \"ERROR_TYPE_REGISTRATION_FAILED\";\n        }\n        \n        // Add a single named type export that points to a non-existent typespace index\n        // This will cause SpacetimeDB to error when it tries to resolve the type\n        RawTypeDefV10 error_type;\n        error_type.source_name.scope = {};\n        error_type.source_name.source_name = error_type_name;\n        error_type.ty = 999999; // Invalid typespace index - will cause an error\n        error_type.custom_ordering = false;\n        \n        getV10Builder().GetTypeDefs().push_back(error_type);\n        \n        // Don't add anything to the typespace - this ensures the reference is invalid\n        // The server will fail with an error message that includes our error type name\n        \n        // Also log to stderr for debugging\n        fprintf(stderr, \"\\n[TYPE ERROR] Module cleared and replaced with error type: %s\\n\", error_type_name.c_str());\n        fprintf(stderr, \"Original error: %s\\n\\n\", error.c_str());\n        fflush(stderr);\n    }\n    //#define DEBUG_TYPE_REGISTRATION\n    // Type validation passed - log statistics only in debug mode\n    #ifdef DEBUG_TYPE_REGISTRATION\n    else {\n        fprintf(stderr, \"[Type Validation] OK - %zu types, %zu tables, %zu reducers\\n\",\n                getV10Builder().GetTypespace().types.size(),\n                getV10Builder().GetTables().size(),\n                getV10Builder().GetReducers().size());\n    }\n    #endif\n}\n\n\nstd::vector<uint8_t> Internal::Module::SerializeModuleDef() {\n    RawModuleDefV10 v10_module = getV10Builder().BuildModuleDef();\n    RawModuleDef versioned_module;\n    versioned_module.set<2>(std::move(v10_module));\n\n    std::vector<uint8_t> buffer;\n    bsatn::Writer writer(buffer);\n    versioned_module.bsatn_serialize(writer);\n    return buffer;\n}\n\n// FFI export - V10 serialization\nvoid Internal::Module::__describe_module__(BytesSink sink) {\n    // The preinit functions should have already been called by SpacetimeDB\n    // Including our validation preinit which checks for recursive types\n    std::vector<uint8_t> buffer = SerializeModuleDef();\n\n    // Now try to write using FFI directly\n    if (!buffer.empty()) {\n        size_t bytes_to_write = buffer.size();\n        FFI::bytes_sink_write(sink, buffer.data(), &bytes_to_write);\n    }\n}\n\n// Helper to consume all bytes from a BytesSource\nstd::vector<uint8_t> ConsumeBytes(BytesSource source) {\n    if (source.inner == 0) {\n        return {};\n    }\n    \n    // Take a buffer from the pool (typically 64 KiB pre-allocated)\n    IterBuf iter_buf = IterBuf::take();\n    \n    // Get the remaining length to reserve exact buffer size\n    uint32_t remaining_len = 0;\n    auto ret = FFI::bytes_source_remaining_length(source, &remaining_len);\n    if (ret != 0) {\n        // If we can't get the length, fall back to incremental reading\n        // This shouldn't happen with current host implementation\n        constexpr size_t CHUNK_SIZE = 1024;\n        iter_buf.reserve(CHUNK_SIZE);\n        \n        while (true) {\n            size_t chunk_size = CHUNK_SIZE;\n            size_t old_size = iter_buf.size();\n            iter_buf.resize(old_size + chunk_size);\n            \n            ret = FFI::bytes_source_read(source, iter_buf.data() + old_size, &chunk_size);\n            iter_buf.resize(old_size + chunk_size);  // Resize to actual bytes read\n            \n            if (ret == -1) {  // EXHAUSTED\n                break;\n            } else if (ret != 0) {  // Error\n                fprintf(stderr, \"ERROR: Failed to read from BytesSource: %d\\n\", ret);\n                break;\n            }\n        }\n        return iter_buf.release();\n    }\n    \n    // Reserve exact size needed (often no-op since pool buffer is 64 KiB)\n    iter_buf.resize(remaining_len);  // Resize to exact size BEFORE reading\n    \n    // Read all bytes - should complete in one call since we have capacity\n    size_t bytes_read = 0;\n    while (bytes_read < remaining_len) {\n        size_t chunk_size = remaining_len - bytes_read;\n        ret = FFI::bytes_source_read(source, iter_buf.data() + bytes_read, &chunk_size);\n        bytes_read += chunk_size;\n        \n        if (ret == -1) {  // EXHAUSTED\n            break;\n        } else if (ret != 0) {  // Error\n            fprintf(stderr, \"ERROR: Failed to read from BytesSource: %d\\n\", ret);\n            break;\n        }\n    }\n    \n    // Resize to actual bytes read if different (shouldn't normally happen)\n    if (bytes_read != remaining_len) {\n        iter_buf.resize(bytes_read);\n    }\n    \n    // Release ownership - caller takes the buffer, won't return to pool\n    return iter_buf.release();\n}\n\n// Helper to write bytes to a BytesSink\nvoid WriteBytes(BytesSink sink, const std::vector<uint8_t>& bytes) {\n    if (sink.inner == 0 || bytes.empty()) {\n        return;\n    }\n    \n    size_t bytes_to_write = bytes.size();\n    FFI::bytes_sink_write(sink, bytes.data(), &bytes_to_write);\n}\n\nStatus Module::__call_reducer__(\n    uint32_t id,\n    uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n    uint64_t conn_id_0, uint64_t conn_id_1,\n    Timestamp timestamp,\n    BytesSource args_source,\n    BytesSink error_sink\n) {\n    // Clear any previous error state\n    SpacetimeDB::Internal::clear_reducer_error();\n\n    // Check if reducer ID is valid\n    if (id >= g_reducer_handlers.size()) {\n        fprintf(stderr, \"ERROR: Invalid reducer ID %u (have %zu reducers)\\n\", \n                id, g_reducer_handlers.size());\n        \n        // Write error message\n        std::string error = \"Invalid reducer ID: \" + std::to_string(id);\n        WriteBytes(error_sink, std::vector<uint8_t>(error.begin(), error.end()));\n        return StatusCode::NO_SUCH_REDUCER;\n    }\n    \n    // Create reducer context\n    std::array<uint8_t, 32> sender_bytes{};\n    // Pack the 4 uint64_t parts into 32 bytes\n    std::memcpy(sender_bytes.data(), &sender_0, 8);\n    std::memcpy(sender_bytes.data() + 8, &sender_1, 8);\n    std::memcpy(sender_bytes.data() + 16, &sender_2, 8);\n    std::memcpy(sender_bytes.data() + 24, &sender_3, 8);\n    \n    Identity sender_identity(sender_bytes);\n    \n    // Create connection ID if provided\n    std::optional<ConnectionId> connection_id;\n    if (conn_id_0 != 0 || conn_id_1 != 0) {\n        // ConnectionId is 128-bit (two 64-bit parts)\n        connection_id = ConnectionId(u128(conn_id_1, conn_id_0));\n    }\n    \n    ReducerContext ctx(sender_identity, connection_id, timestamp);\n    \n    // Get the handler\n    const auto& handler_info = g_reducer_handlers[id];\n    \n    // Call the reducer handler\n    handler_info.handler(ctx, args_source);\n    \n    // Check if the reducer failed gracefully\n    if (SpacetimeDB::Internal::has_reducer_error()) {\n        std::string error_msg = SpacetimeDB::Internal::get_reducer_error();\n        WriteBytes(error_sink, std::vector<uint8_t>(error_msg.begin(), error_msg.end()));\n        return StatusCode::HOST_CALL_FAILURE;\n    }\n\n    return StatusCode::OK;\n}\n\n// Dispatch function for views with ViewContext (has sender)\nint16_t Module::__call_view__(\n    uint32_t id,\n    uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n    BytesSource args_source,\n    BytesSink result_sink\n) {\n    // Check if view ID is valid\n    if (id >= g_view_handlers.size()) {\n        fprintf(stderr, \"ERROR: Invalid view ID %u (have %zu views)\\n\", \n                id, g_view_handlers.size());\n        return -1;  // NO_SUCH_VIEW\n    }\n    \n    // Create sender identity from the 4 uint64_t parts\n    std::array<uint8_t, 32> sender_bytes{};\n    std::memcpy(sender_bytes.data(), &sender_0, 8);\n    std::memcpy(sender_bytes.data() + 8, &sender_1, 8);\n    std::memcpy(sender_bytes.data() + 16, &sender_2, 8);\n    std::memcpy(sender_bytes.data() + 24, &sender_3, 8);\n    \n    Identity sender_identity(sender_bytes);\n    \n    // Create view context\n    ViewContext ctx(sender_identity);\n    \n    // Get the handler\n    const auto& handler_info = g_view_handlers[id];\n    \n    // Call the view handler - returns serialized result data\n    std::vector<uint8_t> result_data = handler_info.handler(ctx, args_source);\n    \n    // Serialize ViewResultHeader::RowData followed by the result\n    std::vector<uint8_t> full_result;\n    \n    // Write the header (RowData = 0)\n    ViewResultHeader header = ViewResultHeader::RowData;\n    bsatn::Writer header_writer(full_result);\n    header_writer.write_u8(static_cast<uint8_t>(header));\n    \n    // Append the actual result data\n    full_result.insert(full_result.end(), result_data.begin(), result_data.end());\n    \n    // Write to the result sink\n    WriteBytes(result_sink, full_result);\n    \n    return 2;  // Success with data\n}\n\n// Dispatch function for views with AnonymousViewContext (no sender)\nint16_t Module::__call_view_anon__(\n    uint32_t id,\n    BytesSource args_source,\n    BytesSink result_sink\n) {\n    // Check if view ID is valid\n    if (id >= g_view_anon_handlers.size()) {\n        fprintf(stderr, \"ERROR: Invalid anonymous view ID %u (have %zu anonymous views)\\n\", \n                id, g_view_anon_handlers.size());\n        return -1;  // NO_SUCH_VIEW\n    }\n    \n    // Create anonymous view context\n    AnonymousViewContext ctx;\n    \n    // Get the handler\n    const auto& handler_info = g_view_anon_handlers[id];\n    \n    // Call the view handler - returns serialized result data\n    std::vector<uint8_t> result_data = handler_info.handler(ctx, args_source);\n    \n    // Serialize ViewResultHeader::RowData followed by the result\n    std::vector<uint8_t> full_result;\n    \n    // Write the header (RowData = 0)\n    ViewResultHeader header = ViewResultHeader::RowData;\n    bsatn::Writer header_writer(full_result);\n    header_writer.write_u8(static_cast<uint8_t>(header));\n    \n    // Append the actual result data\n    full_result.insert(full_result.end(), result_data.begin(), result_data.end());\n    \n    // Write to the result sink\n    WriteBytes(result_sink, full_result);\n    \n    return 2;  // Success with data\n}\n\n// Dispatch function for procedures\nint16_t Module::__call_procedure__(\n    uint32_t id,\n    uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\n    uint64_t conn_id_0, uint64_t conn_id_1,\n    uint64_t timestamp_microseconds,\n    BytesSource args_source,\n    BytesSink result_sink\n) {\n    // Check if procedure ID is valid\n    if (id >= g_procedure_handlers.size()) {\n        fprintf(stderr, \"ERROR: Invalid procedure ID %u (have %zu procedures)\\n\", \n                id, g_procedure_handlers.size());\n        return -1;  // NO_SUCH_PROCEDURE\n    }\n    \n    // Create sender identity from the 4 uint64_t parts\n    std::array<uint8_t, 32> sender_bytes{};\n    std::memcpy(sender_bytes.data(), &sender_0, 8);\n    std::memcpy(sender_bytes.data() + 8, &sender_1, 8);\n    std::memcpy(sender_bytes.data() + 16, &sender_2, 8);\n    std::memcpy(sender_bytes.data() + 24, &sender_3, 8);\n    \n    Identity sender_identity(sender_bytes);\n    \n    // Create timestamp from microseconds\n    Timestamp timestamp = Timestamp::from_micros_since_epoch(\n        static_cast<int64_t>(timestamp_microseconds));\n    \n    // Create connection ID from the two 64-bit parts (full 128-bit value)\n    ConnectionId connection_id;\n    if (conn_id_0 != 0 || conn_id_1 != 0) {\n        connection_id = ConnectionId(u128(conn_id_1, conn_id_0));\n    }\n    \n    // Create procedure context\n    ProcedureContext ctx(sender_identity, timestamp, connection_id);\n    \n    // Get the handler\n    const auto& handler_info = g_procedure_handlers[id];\n    \n    // Call the procedure handler - this may trap if there's an error\n    std::vector<uint8_t> result_data = handler_info.handler(ctx, args_source);\n    \n    // If we got here, procedure succeeded - write result\n    WriteBytes(result_sink, result_data);\n    \n    return 0;  // Success (StatusCode::OK)\n}\n\nvoid Module::SetCaseConversionPolicy(CaseConversionPolicy policy) {\n    getV10Builder().SetCaseConversionPolicy(policy);\n}\n\nvoid Module::RegisterClientVisibilityFilter(const char* sql) {\n    if (sql == nullptr || sql[0] == '\\0') {\n        return;\n    }\n    getV10Builder().RegisterRowLevelSecurity(sql);\n}\n\nvoid Module::RegisterExplicitTableName(const std::string& source_name, const std::string& canonical_name) {\n    getV10Builder().RegisterExplicitTableName(source_name, canonical_name);\n}\n\nvoid Module::RegisterExplicitFunctionName(const std::string& source_name, const std::string& canonical_name) {\n    getV10Builder().RegisterExplicitFunctionName(source_name, canonical_name);\n}\n\nvoid Module::RegisterExplicitIndexName(const std::string& source_name, const std::string& canonical_name) {\n    getV10Builder().RegisterExplicitIndexName(source_name, canonical_name);\n}\n}\n}\n\n\n \n\n"
  },
  {
    "path": "crates/bindings-cpp/src/internal/module_type_registration.cpp",
    "content": "#include \"spacetimedb/internal/module_type_registration.h\"\n#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/internal/autogen/AlgebraicType.g.h\"\n#include \"spacetimedb/internal/autogen/RawTypeDefV10.g.h\"\n#include \"spacetimedb/internal/autogen/ProductType.g.h\"\n#include \"spacetimedb/internal/autogen/SumType.g.h\"\n#include \"spacetimedb/logger.h\"  // For LOG_ERROR, LOG_PANIC\n#include <memory>\n#include <algorithm>\n#include <cstdio>\n#include <stdexcept>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\n// Use the correct namespaces - these types are already in the correct namespace\n\n// Global module type registration instance\nstd::unique_ptr<ModuleTypeRegistration> g_module_type_registration;\n\n// Thread-local storage for tracking the chain of types being registered\nthread_local std::vector<std::string> g_type_registration_chain;\n\n// Global flag to indicate circular reference error (set during type building)\nbool g_circular_ref_error = false;\nstd::string g_circular_ref_type_name;\n\nvoid initializeModuleTypeRegistration() {\n    g_module_type_registration = std::make_unique<ModuleTypeRegistration>();\n    // Clear any previous error state\n    g_circular_ref_error = false;\n    g_circular_ref_type_name.clear();\n    g_type_registration_chain.clear();\n}\n\nModuleTypeRegistration& getModuleTypeRegistration() {\n    if (!g_module_type_registration) {\n        initializeModuleTypeRegistration();\n    }\n    return *g_module_type_registration;\n}\n\n// Function moved outside Internal namespace - see end of file\n\nAlgebraicType ModuleTypeRegistration::registerType(const bsatn::AlgebraicType& bsatn_type,\n                                               const std::string& explicit_name,\n                                               const std::type_info* cpp_type) {\n    \n    // 1. PRIMITIVES - always inline, never registered\n    if (isPrimitive(bsatn_type)) {\n        return convertPrimitive(bsatn_type);\n    }\n    \n    // 2. REFS - already registered, convert and return\n    if (bsatn_type.tag() == bsatn::AlgebraicTypeTag::Ref) {\n        AlgebraicType result(AlgebraicType::Tag::Ref);\n        result.set<0>(bsatn_type.as_ref());\n        return result;\n    }\n    \n    // 3. ARRAYS - always inline with recursive element processing\n    if (bsatn_type.tag() == bsatn::AlgebraicTypeTag::Array) {\n        return convertArray(bsatn_type);\n    }\n    \n    // 4. UNIT TYPES - empty products should be inlined as Unit ONLY if unnamed\n    // Named unit structs (like UnitStruct) should be registered as named types\n    if (isUnitType(bsatn_type)) {\n        if (!explicit_name.empty()) {\n            // This is a named unit struct like UnitStruct - do NOT inline\n            // Fall through to register as named type\n        } else {\n            // Unnamed unit type - inline it\n            return convertUnitType();\n        }\n    }\n    \n    // 5. SPECIAL TYPES - always inline\n    if (isSpecialType(bsatn_type)) {\n        return convertSpecialType(bsatn_type);\n    }\n    \n    // Check if this might be ScheduleAt being registered with wrong name\n    if (bsatn_type.tag() == bsatn::AlgebraicTypeTag::Sum) {\n        const auto& sum = bsatn_type.as_sum();\n        if (sum.variants.size() == 2 &&\n            sum.variants[0].name == \"Interval\" &&\n            sum.variants[1].name == \"Time\") {\n            // This is ScheduleAt - inline it.\n            return convertInlineSum(bsatn_type);\n        }\n    }\n    \n    // 6. OPTIONS - always inline\n    if (isOptionType(bsatn_type)) {\n        return convertInlineSum(bsatn_type);\n    }\n    \n    // 7. RESULTS - always inline\n    if (isResultType(bsatn_type)) {\n        return convertInlineSum(bsatn_type);\n    }\n    \n    // 8. SCHEDULE_AT - always inline\n    if (isScheduleAtType(bsatn_type)) {\n        return convertInlineSum(bsatn_type);\n    }\n    \n    // ============================================================\n    // ONLY USER-DEFINED STRUCTS AND ENUMS GET REGISTERED BELOW\n    // ============================================================\n    \n    // 7. DETERMINE TYPE NAME\n    std::string type_name = explicit_name;\n    if (type_name.empty() && cpp_type) {\n        type_name = extractTypeName(cpp_type);\n    } else if (!type_name.empty()) {\n        // Even if we have an explicit name, ensure it doesn't contain namespace separators\n        // This fixes issues with special types like SpacetimeDB::ScheduleAt\n        size_t last_colon = type_name.rfind(\"::\");\n        if (last_colon != std::string::npos) {\n            type_name = type_name.substr(last_colon + 2);\n        }\n    }\n    \n    if (type_name.empty()) {\n        // Complex types MUST have proper names - this is a validation error\n        error_type_description_ = describeType(bsatn_type);\n#if !SPACETIMEDB_HAS_CXA_DEMANGLE\n        error_message_ =\n            \"Missing type name for complex type on toolchain without demangling support. \"\n            \"Provide an explicit type name: \" +\n            error_type_description_;\n#else\n        error_message_ = \"Missing type name for complex type: \" + error_type_description_;\n#endif\n        has_error_ = true;\n        \n        // Return a dummy type to avoid crashing\n        return AlgebraicType(AlgebraicType::Tag::U8);\n    }\n    \n    // 8. DETECT RECURSIVE TYPE REGISTRATION (CYCLE DETECTION)\n    if (types_being_registered_.find(type_name) != types_being_registered_.end()) {\n        // Set error state for validation preinit to report\n        error_message_ = \"Recursive type reference detected: '\" + type_name + \"' is referencing itself\";\n        has_error_ = true;\n        \n        // Return a dummy type to break the infinite recursion\n        // This allows the validation preinit to run and report the error cleanly\n        return AlgebraicType(AlgebraicType::Tag::U8);\n    }\n    \n    // 9. CHECK IF ALREADY REGISTERED\n    // Check cache first\n    auto cache_it = type_name_cache_.find(type_name);\n    if (cache_it != type_name_cache_.end()) {\n        AlgebraicType result(AlgebraicType::Tag::Ref);\n        result.set<0>(cache_it->second);\n        return result;\n    }\n    \n    // Check module's types array\n    for (const auto& type_def : getV10Builder().GetTypeDefs()) {\n        if (type_def.source_name.source_name == type_name) {\n            uint32_t typespace_index = type_def.ty;\n            type_name_cache_[type_name] = typespace_index;\n            AlgebraicType result(AlgebraicType::Tag::Ref);\n            result.set<0>(typespace_index);\n            return result;\n        }\n    }\n    \n    // 10. REGISTER NEW COMPLEX TYPE\n    return registerComplexType(bsatn_type, type_name);\n}\n\nuint32_t ModuleTypeRegistration::registerAndGetIndex(const bsatn::AlgebraicType& bsatn_type,\n                                                 const std::string& type_name,\n                                                 const std::type_info* cpp_type) {\n    // Check if already registered in cache\n    auto cache_it = type_name_cache_.find(type_name);\n    if (cache_it != type_name_cache_.end()) {\n        return cache_it->second;\n    }\n    \n    // Register the type and get the Internal::AlgebraicType result\n    AlgebraicType result = registerType(bsatn_type, type_name, cpp_type);\n    \n    // If it's a Ref, return the index\n    if (result.get_tag() == AlgebraicType::Tag::Ref) {\n        uint32_t index = result.get<0>();\n        type_name_cache_[type_name] = index;\n        return index;\n    }\n    \n    // This shouldn't happen for enums - they should always register as complex types\n    fprintf(stderr, \"ERROR: Enum '%s' did not register as a complex type\\n\", type_name.c_str());\n    return 0;\n}\n\nvoid ModuleTypeRegistration::registerTypeByName(const std::string& type_name, \n                                            const bsatn::AlgebraicType& algebraic_type,\n                                            [[maybe_unused]] const std::type_info* cpp_type) {\n    // Check if already registered in cache\n    auto cache_it = type_name_cache_.find(type_name);\n    if (cache_it != type_name_cache_.end()) {\n        return;\n    }\n    \n    // Check if already registered in module's types array\n    for (const auto& type_def : getV10Builder().GetTypeDefs()) {\n        if (type_def.source_name.source_name == type_name) {\n            uint32_t typespace_index = type_def.ty;\n            type_name_cache_[type_name] = typespace_index;\n            return;\n        }\n    }\n\n    // Register by name using the same full-fidelity path as other complex type registration.\n    // This avoids lossy conversion of nested fields/variants.\n    auto result = registerType(algebraic_type, type_name, cpp_type);\n    if (result.get_tag() != AlgebraicType::Tag::Ref) {\n        fprintf(stderr, \"ERROR: Failed to register named complex type '%s'\\n\", type_name.c_str());\n    }\n}\n\nbool ModuleTypeRegistration::isPrimitive(const bsatn::AlgebraicType& type) const {\n    auto tag = static_cast<uint32_t>(type.tag());\n    // Use range check: String (4) to F64 (19) covers all primitive types\n    return tag >= static_cast<uint32_t>(bsatn::AlgebraicTypeTag::String) &&\n           tag <= static_cast<uint32_t>(bsatn::AlgebraicTypeTag::F64);\n}\n\nbool ModuleTypeRegistration::isSpecialType(const bsatn::AlgebraicType& type) const {\n    if (type.tag() != bsatn::AlgebraicTypeTag::Product) {\n        return false;\n    }\n    \n    const auto& product = type.as_product();\n    if (product.elements.size() != 1) {\n        return false;\n    }\n    \n    const auto& field_name = product.elements[0].name;\n    return field_name == \"__identity__\" ||\n           field_name == \"__connection_id__\" ||\n           field_name == \"__timestamp_micros_since_unix_epoch__\" ||\n           field_name == \"__time_duration_micros__\" ||\n           field_name == \"__uuid__\";\n}\n\nbool ModuleTypeRegistration::isOptionType(const bsatn::AlgebraicType& type) const {\n    if (type.tag() != bsatn::AlgebraicTypeTag::Sum) {\n        return false;\n    }\n    \n    const auto& sum = type.as_sum();\n    return sum.variants.size() == 2 &&\n           sum.variants[0].name == \"some\" &&\n           sum.variants[1].name == \"none\";\n}\n\nbool ModuleTypeRegistration::isResultType(const bsatn::AlgebraicType& type) const {\n    if (type.tag() != bsatn::AlgebraicTypeTag::Sum) {\n        return false;\n    }\n    \n    const auto& sum = type.as_sum();\n    return sum.variants.size() == 2 &&\n           sum.variants[0].name == \"ok\" &&\n           sum.variants[1].name == \"err\";\n}\n\nbool ModuleTypeRegistration::isScheduleAtType(const bsatn::AlgebraicType& type) const {\n    if (type.tag() != bsatn::AlgebraicTypeTag::Sum) {\n        return false;\n    }\n    \n    const auto& sum = type.as_sum();\n    return sum.variants.size() == 2 &&\n           sum.variants[0].name == \"Interval\" &&\n           sum.variants[1].name == \"Time\";\n}\n\nbool ModuleTypeRegistration::isUnitType(const bsatn::AlgebraicType& type) const {\n    if (type.tag() != bsatn::AlgebraicTypeTag::Product) {\n        return false;\n    }\n    \n    const auto& product = type.as_product();\n    // Unit type is an empty Product (no fields)\n    return product.elements.empty();\n}\n\nAlgebraicType ModuleTypeRegistration::convertUnitType() const {\n    // Create an empty Product type (Unit)\n    auto product = std::make_unique<ProductType>();\n    // No elements - empty Product\n    \n    AlgebraicType result(AlgebraicType::Tag::Product);\n    result.set<2>(std::move(product));\n    return result;\n}\n\nstd::string ModuleTypeRegistration::extractTypeName(const std::type_info* cpp_type) const {\n#if !SPACETIMEDB_HAS_CXA_DEMANGLE\n    (void)cpp_type;\n    return \"\";\n#else\n    std::string demangled = demangle_cpp_type_name(cpp_type->name());\n    \n    // Extract simple name (last component after ::)\n    size_t last_colon = demangled.rfind(\"::\");\n    if (last_colon != std::string::npos) {\n        demangled = demangled.substr(last_colon + 2);\n    }\n    \n    // Remove template parameters\n    size_t template_start = demangled.find('<');\n    if (template_start != std::string::npos) {\n        demangled = demangled.substr(0, template_start);\n    }\n    \n    return demangled;\n#endif\n}\n\nstd::pair<std::vector<std::string>, std::string> ModuleTypeRegistration::parseNamespaceAndName(const std::string& qualified_name) const {\n    std::vector<std::string> scope;\n    std::string name;\n    \n    // Look for namespace separator (dot)\n    size_t last_dot = qualified_name.rfind('.');\n    if (last_dot != std::string::npos) {\n        // Split into namespace and name\n        std::string namespace_part = qualified_name.substr(0, last_dot);\n        name = qualified_name.substr(last_dot + 1);\n        \n        // Split namespace into scope components (in case of nested namespaces like \"A.B.C\")\n        size_t start = 0;\n        size_t pos = 0;\n        while ((pos = namespace_part.find('.', start)) != std::string::npos) {\n            std::string component = namespace_part.substr(start, pos - start);\n            if (!component.empty()) {\n                scope.push_back(component);\n            }\n            start = pos + 1;\n        }\n        // Add the last component\n        std::string last_component = namespace_part.substr(start);\n        if (!last_component.empty()) {\n            scope.push_back(last_component);\n        }\n        \n        // fprintf(stdout, \"DEBUG: Parsed namespace '%s' -> scope=[\", qualified_name.c_str());\n        // for (size_t i = 0; i < scope.size(); ++i) {\n        //     if (i > 0) fprintf(stdout, \", \");\n        //     fprintf(stdout, \"\\\"%s\\\"\", scope[i].c_str());\n        // }\n        // fprintf(stdout, \"], name=\\\"%s\\\"\\n\", name.c_str());\n    } else {\n        // No namespace, just use the full name\n        name = qualified_name;\n        // scope remains empty\n    }\n    \n    return std::make_pair(scope, name);\n}\n\nAlgebraicType ModuleTypeRegistration::convertPrimitive(const bsatn::AlgebraicType& type) const {\n    switch (type.tag()) {\n        case bsatn::AlgebraicTypeTag::Bool:\n            return AlgebraicType(AlgebraicType::Tag::Bool);\n        case bsatn::AlgebraicTypeTag::U8:\n            return AlgebraicType(AlgebraicType::Tag::U8);\n        case bsatn::AlgebraicTypeTag::U16:\n            return AlgebraicType(AlgebraicType::Tag::U16);\n        case bsatn::AlgebraicTypeTag::U32:\n            return AlgebraicType(AlgebraicType::Tag::U32);\n        case bsatn::AlgebraicTypeTag::U64:\n            return AlgebraicType(AlgebraicType::Tag::U64);\n        case bsatn::AlgebraicTypeTag::U128:\n            return AlgebraicType(AlgebraicType::Tag::U128);\n        case bsatn::AlgebraicTypeTag::U256:\n            return AlgebraicType(AlgebraicType::Tag::U256);\n        case bsatn::AlgebraicTypeTag::I8:\n            return AlgebraicType(AlgebraicType::Tag::I8);\n        case bsatn::AlgebraicTypeTag::I16:\n            return AlgebraicType(AlgebraicType::Tag::I16);\n        case bsatn::AlgebraicTypeTag::I32:\n            return AlgebraicType(AlgebraicType::Tag::I32);\n        case bsatn::AlgebraicTypeTag::I64:\n            return AlgebraicType(AlgebraicType::Tag::I64);\n        case bsatn::AlgebraicTypeTag::I128:\n            return AlgebraicType(AlgebraicType::Tag::I128);\n        case bsatn::AlgebraicTypeTag::I256:\n            return AlgebraicType(AlgebraicType::Tag::I256);\n        case bsatn::AlgebraicTypeTag::F32:\n            return AlgebraicType(AlgebraicType::Tag::F32);\n        case bsatn::AlgebraicTypeTag::F64:\n            return AlgebraicType(AlgebraicType::Tag::F64);\n        case bsatn::AlgebraicTypeTag::String:\n            return AlgebraicType(AlgebraicType::Tag::String);\n        default:\n            // Unknown primitive - use U8 as fallback\n            return AlgebraicType(AlgebraicType::Tag::U8);\n    }\n}\n\nAlgebraicType ModuleTypeRegistration::convertArray(const bsatn::AlgebraicType& type) {\n    const auto& array = type.as_array();\n    \n    // Recursively process element type\n    AlgebraicType elem_type = registerType(*array.element_type);\n    \n    // Create inline array\n    AlgebraicType result(AlgebraicType::Tag::Array);\n    result.set<3>(std::make_unique<AlgebraicType>(std::move(elem_type)));\n    return result;\n}\n\nAlgebraicType ModuleTypeRegistration::convertSpecialType(const bsatn::AlgebraicType& type) {\n    // Special types are inlined as Product structures\n    auto product = std::make_unique<ProductType>();\n    \n    for (const auto& field : type.as_product().elements) {\n        ProductTypeElement elem;\n        elem.name = field.name;\n        \n        // Recursively process field type (should be a primitive)\n        elem.algebraic_type = registerType(*field.algebraic_type);\n        product->elements.push_back(std::move(elem));\n    }\n    \n    AlgebraicType result(AlgebraicType::Tag::Product);\n    result.set<2>(std::move(product));\n    return result;\n}\n\nAlgebraicType ModuleTypeRegistration::convertInlineSum(const bsatn::AlgebraicType& type) {\n    // Options and ScheduleAt are inlined as Sum structures\n    auto sum = std::make_unique<SumType>();\n    \n    for (const auto& variant : type.as_sum().variants) {\n        SumTypeVariant v;\n        v.name = variant.name;\n        \n        // Recursively process variant type\n        v.algebraic_type = registerType(*variant.algebraic_type);\n        sum->variants.push_back(std::move(v));\n    }\n    \n    AlgebraicType result(AlgebraicType::Tag::Sum);\n    result.set<1>(std::move(sum));\n    return result;\n}\n\nAlgebraicType ModuleTypeRegistration::registerComplexType(const bsatn::AlgebraicType& type,\n                                                      const std::string& type_name) {\n    // Mark this type as being registered (for cycle detection)\n    types_being_registered_.insert(type_name);\n    \n    // Reserve space in typespace\n    uint32_t typespace_index = getV10Builder().GetTypespace().types.size();\n    \n    // Debug logging (disabled in production)\n    #ifdef DEBUG_TYPE_REGISTRATION\n    fprintf(stdout, \"[Type] Registering '%s' at index %u\\n\", type_name.c_str(), typespace_index);\n    #endif\n    \n    // Process the type based on its kind\n    AlgebraicType processed_type;\n    \n    if (type.tag() == bsatn::AlgebraicTypeTag::Product) {\n        processed_type = processProduct(type);\n    } else if (type.tag() == bsatn::AlgebraicTypeTag::Sum) {\n        processed_type = processSum(type);\n    } else {\n        // This shouldn't happen in normal usage\n        fprintf(stderr, \"[Warning] Unexpected type tag %d for '%s'\\n\", \n                static_cast<int>(type.tag()), type_name.c_str());\n        // Fallback\n        processed_type = convertPrimitive(type);\n    }\n    \n    // Add to typespace\n    getV10Builder().GetTypespace().types.push_back(processed_type);\n    \n    // Create RawTypeDefV9 export with namespace support\n    RawTypeDefV10 type_def;\n    \n    // Parse namespace from type name\n    auto [scope, simple_name] = parseNamespaceAndName(type_name);\n    type_def.source_name.scope = scope;\n    type_def.source_name.source_name = simple_name;\n    type_def.ty = typespace_index;\n    type_def.custom_ordering = true; // Complex types need custom ordering\n    \n    // Add to module's types array\n    getV10Builder().GetTypeDefs().push_back(type_def);\n    \n    // Update cache\n    type_name_cache_[type_name] = typespace_index;\n    \n    // Remove from types being registered (cycle detection cleanup)\n    types_being_registered_.erase(type_name);\n    \n    // Return Ref to the registered type\n    AlgebraicType result(AlgebraicType::Tag::Ref);\n    result.set<0>(typespace_index);\n    return result;\n}\n\nAlgebraicType ModuleTypeRegistration::processProduct(const bsatn::AlgebraicType& type) {\n    auto product = std::make_unique<ProductType>();\n    \n    for (const auto& field : type.as_product().elements) {\n        ProductTypeElement elem;\n        elem.name = field.name;\n        \n        // Recursively register/process field type\n        // For nested complex types, we need to ensure they're registered with proper names\n        // The registerType call will handle primitives, arrays, special types, and complex types\n        elem.algebraic_type = registerType(*field.algebraic_type);\n        product->elements.push_back(std::move(elem));\n    }\n    \n    AlgebraicType result(AlgebraicType::Tag::Product);\n    result.set<2>(std::move(product));\n    return result;\n}\n\nAlgebraicType ModuleTypeRegistration::processSum(const bsatn::AlgebraicType& type) {\n    auto sum = std::make_unique<SumType>();\n    \n    for (const auto& variant : type.as_sum().variants) {\n        SumTypeVariant v;\n        v.name = variant.name;\n        \n        // Recursively register/process variant type\n        v.algebraic_type = registerType(*variant.algebraic_type);\n        sum->variants.push_back(std::move(v));\n    }\n    \n    AlgebraicType result(AlgebraicType::Tag::Sum);\n    result.set<1>(std::move(sum));\n    return result;\n}\n\nstd::string ModuleTypeRegistration::describeType(const bsatn::AlgebraicType& type) const {\n    switch (type.tag()) {\n        case bsatn::AlgebraicTypeTag::Bool: return \"Bool\";\n        case bsatn::AlgebraicTypeTag::U8: return \"U8\";\n        case bsatn::AlgebraicTypeTag::U16: return \"U16\";\n        case bsatn::AlgebraicTypeTag::U32: return \"U32\";\n        case bsatn::AlgebraicTypeTag::U64: return \"U64\";\n        case bsatn::AlgebraicTypeTag::U128: return \"U128\";\n        case bsatn::AlgebraicTypeTag::U256: return \"U256\";\n        case bsatn::AlgebraicTypeTag::I8: return \"I8\";\n        case bsatn::AlgebraicTypeTag::I16: return \"I16\";\n        case bsatn::AlgebraicTypeTag::I32: return \"I32\";\n        case bsatn::AlgebraicTypeTag::I64: return \"I64\";\n        case bsatn::AlgebraicTypeTag::I128: return \"I128\";\n        case bsatn::AlgebraicTypeTag::I256: return \"I256\";\n        case bsatn::AlgebraicTypeTag::F32: return \"F32\";\n        case bsatn::AlgebraicTypeTag::F64: return \"F64\";\n        case bsatn::AlgebraicTypeTag::String: return \"String\";\n        \n        case bsatn::AlgebraicTypeTag::Array: {\n            const auto& array = type.as_array();\n            return \"Array<\" + describeType(*array.element_type) + \">\";\n        }\n        \n        case bsatn::AlgebraicTypeTag::Product: {\n            const auto& product = type.as_product();\n            if (product.elements.empty()) {\n                return \"Product{}\";\n            }\n            \n            std::string desc = \"Product{\";\n            bool first = true;\n            for (const auto& elem : product.elements) {\n                if (!first) desc += \", \";\n                first = false;\n                \n                if (elem.name.has_value()) {\n                    desc += elem.name.value() + \": \";\n                }\n                desc += describeType(*elem.algebraic_type);\n            }\n            desc += \"}\";\n            return desc;\n        }\n        \n        case bsatn::AlgebraicTypeTag::Sum: {\n            const auto& sum = type.as_sum();\n            if (sum.variants.empty()) {\n                return \"Sum{}\";\n            }\n            \n            // Check if it's an Option\n            if (isOptionType(type)) {\n                return \"Option<\" + describeType(*sum.variants[0].algebraic_type) + \">\";\n            }\n            \n            std::string desc = \"Sum{\";\n            bool first = true;\n            for (const auto& variant : sum.variants) {\n                if (!first) desc += \" | \";\n                first = false;\n                \n                desc += variant.name + \": \" + describeType(*variant.algebraic_type);\n            }\n            desc += \"}\";\n            return desc;\n        }\n        \n        case bsatn::AlgebraicTypeTag::Ref:\n            return \"Ref(\" + std::to_string(type.as_ref()) + \")\";\n            \n        default:\n            return \"Unknown(tag=\" + std::to_string(static_cast<int>(type.tag())) + \")\";\n    }\n}\n\nvoid ModuleTypeRegistration::updateTypeNameInModule(uint32_t type_index, const std::string& new_name) {\n    auto& type_defs = getV10Builder().GetTypeDefs();\n    \n    // Check if the type index is valid\n    if (type_index >= type_defs.size()) {\n        fprintf(stderr, \"ERROR: Invalid type index %u for namespace update (max: %zu)\\n\", \n                type_index, type_defs.size());\n        return;\n    }\n    \n    // Parse the new name to extract namespace and name parts\n    auto [scope, name] = parseNamespaceAndName(new_name);\n    \n    // Update the type definition's scoped name\n    type_defs[type_index].source_name.scope = scope;\n    type_defs[type_index].source_name.source_name = name;\n    \n}\n\n// processOptionInnerType function removed - no longer needed\n// Options now use the same LazyTypeRegistrar pattern as other types\n\n} // namespace Internal\n\n} // namespace SpacetimeDB\n\n"
  },
  {
    "path": "crates/bindings-cpp/src/internal/v10_builder.cpp",
    "content": "#include \"spacetimedb/internal/v10_builder.h\"\n#include \"spacetimedb/internal/autogen/AlgebraicType.g.h\"\n#include \"spacetimedb/internal/autogen/ProductType.g.h\"\n#include \"spacetimedb/internal/autogen/ProductTypeElement.g.h\"\n#include \"spacetimedb/internal/autogen/RawModuleDefV10Section.g.h\"\n#include \"spacetimedb/internal/autogen/RawTypeDefV10.g.h\"\n#include \"spacetimedb/internal/autogen/RawScopedTypeNameV10.g.h\"\n#include \"spacetimedb/internal/autogen/FunctionVisibility.g.h\"\n#include \"spacetimedb/internal/autogen/ExplicitNames.g.h\"\n#include <algorithm>\n#include <unordered_set>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\nstd::unique_ptr<V10Builder> g_v10_builder;\n\nvoid initializeV10Builder() {\n    g_v10_builder = std::make_unique<V10Builder>();\n}\n\nV10Builder& getV10Builder() {\n    if (!g_v10_builder) {\n        initializeV10Builder();\n    }\n    return *g_v10_builder;\n}\n\nvoid V10Builder::Clear() {\n    typespace_ = Typespace{};\n    types_.clear();\n    table_is_event_.clear();\n    case_conversion_policy_.reset();\n    explicit_names_.clear();\n    column_defaults_by_table_.clear();\n    tables_.clear();\n    reducers_.clear();\n    procedures_.clear();\n    views_.clear();\n    schedules_.clear();\n    lifecycle_reducers_.clear();\n    row_level_security_.clear();\n}\n\nvoid V10Builder::SetTableIsEventFlag(const std::string& table_name, bool is_event) {\n    for (auto& entry : table_is_event_) {\n        if (entry.first == table_name) {\n            entry.second = is_event;\n            for (auto& table : tables_) {\n                if (table.source_name == table_name) {\n                    table.is_event = is_event;\n                    break;\n                }\n            }\n            return;\n        }\n    }\n    table_is_event_.emplace_back(table_name, is_event);\n}\n\nbool V10Builder::GetTableIsEventFlag(const std::string& table_name) const {\n    for (const auto& entry : table_is_event_) {\n        if (entry.first == table_name) {\n            return entry.second;\n        }\n    }\n    return false;\n}\n\nvoid V10Builder::RegisterExplicitTableName(const std::string& source_name, const std::string& canonical_name) {\n    ExplicitNameEntry entry;\n    entry.set<0>(NameMapping{source_name, canonical_name});\n    explicit_names_.push_back(std::move(entry));\n}\n\nvoid V10Builder::RegisterExplicitFunctionName(const std::string& source_name, const std::string& canonical_name) {\n    ExplicitNameEntry entry;\n    entry.set<1>(NameMapping{source_name, canonical_name});\n    explicit_names_.push_back(std::move(entry));\n}\n\nvoid V10Builder::RegisterExplicitIndexName(const std::string& source_name, const std::string& canonical_name) {\n    ExplicitNameEntry entry;\n    entry.set<2>(NameMapping{source_name, canonical_name});\n    explicit_names_.push_back(std::move(entry));\n}\n\nAlgebraicType V10Builder::MakeUnitAlgebraicType() {\n    AlgebraicType unit(AlgebraicType::Tag::Product);\n    unit.set<2>(std::make_unique<ProductType>());\n    return unit;\n}\n\nAlgebraicType V10Builder::MakeStringAlgebraicType() {\n    return AlgebraicType(AlgebraicType::Tag::String);\n}\n\nvoid V10Builder::UpsertTable(const RawTableDefV10& table) {\n    auto it = std::find_if(tables_.begin(), tables_.end(), [&](const auto& existing) {\n        return existing.source_name == table.source_name;\n    });\n    if (it == tables_.end()) {\n        tables_.push_back(table);\n    } else {\n        *it = table;\n    }\n}\n\nvoid V10Builder::UpsertLifecycleReducer(const RawLifeCycleReducerDefV10& lifecycle) {\n    auto it = std::find_if(lifecycle_reducers_.begin(), lifecycle_reducers_.end(), [&](const auto& existing) {\n        return existing.function_name == lifecycle.function_name;\n    });\n    if (it == lifecycle_reducers_.end()) {\n        lifecycle_reducers_.push_back(lifecycle);\n    } else {\n        *it = lifecycle;\n    }\n}\n\nvoid V10Builder::UpsertReducer(const RawReducerDefV10& reducer) {\n    auto it = std::find_if(reducers_.begin(), reducers_.end(), [&](const auto& existing) {\n        return existing.source_name == reducer.source_name;\n    });\n    if (it == reducers_.end()) {\n        reducers_.push_back(reducer);\n    } else {\n        *it = reducer;\n    }\n}\n\nvoid V10Builder::UpsertProcedure(const RawProcedureDefV10& procedure) {\n    auto it = std::find_if(procedures_.begin(), procedures_.end(), [&](const auto& existing) {\n        return existing.source_name == procedure.source_name;\n    });\n    if (it == procedures_.end()) {\n        procedures_.push_back(procedure);\n    } else {\n        *it = procedure;\n    }\n}\n\nvoid V10Builder::UpsertView(const RawViewDefV10& view) {\n    auto it = std::find_if(views_.begin(), views_.end(), [&](const auto& existing) {\n        return existing.source_name == view.source_name;\n    });\n    if (it == views_.end()) {\n        views_.push_back(view);\n    } else {\n        *it = view;\n    }\n}\n\nRawIndexDefV10 V10Builder::CreateBTreeIndex(const std::string& table_name,\n                                            const std::string& source_name,\n                                            const std::vector<uint16_t>& columns,\n                                            const std::string& accessor_name) const {\n    RawIndexAlgorithmBTreeData btree_data;\n    btree_data.columns = columns;\n    RawIndexAlgorithm algorithm;\n    algorithm.set<0>(btree_data);\n    (void)table_name;\n    return RawIndexDefV10{\n        source_name,\n        accessor_name,\n        std::move(algorithm),\n    };\n}\n\nRawConstraintDefV10 V10Builder::CreateUniqueConstraint(const std::string& table_name,\n                                                       const std::string& field_name,\n                                                       uint16_t field_idx) const {\n    RawUniqueConstraintDataV9 unique_data;\n    unique_data.columns = {field_idx};\n    RawConstraintDataV9 constraint_data;\n    constraint_data.set<0>(unique_data);\n    (void)table_name;\n    (void)field_name;\n    return RawConstraintDefV10{\n        std::nullopt,\n        std::move(constraint_data),\n    };\n}\n\nRawModuleDefV10 V10Builder::BuildModuleDef() const {\n    RawModuleDefV10 v10_module;\n\n    std::vector<RawTypeDefV10> types = types_;\n\n    std::vector<RawReducerDefV10> reducers = reducers_;\n    std::vector<RawProcedureDefV10> procedures = procedures_;\n\n    std::unordered_set<std::string> internal_functions;\n    for (const auto& lifecycle : lifecycle_reducers_) {\n        internal_functions.insert(lifecycle.function_name);\n    }\n    for (const auto& schedule : schedules_) {\n        internal_functions.insert(schedule.function_name);\n    }\n    for (auto& reducer : reducers) {\n        if (internal_functions.find(reducer.source_name) != internal_functions.end()) {\n            reducer.visibility = FunctionVisibility::Private;\n        }\n    }\n    for (auto& procedure : procedures) {\n        if (internal_functions.find(procedure.source_name) != internal_functions.end()) {\n            procedure.visibility = FunctionVisibility::Private;\n        }\n    }\n\n    RawModuleDefV10Section section_typespace;\n    section_typespace.set<0>(typespace_);\n    v10_module.sections.push_back(section_typespace);\n\n    if (!types.empty()) {\n        RawModuleDefV10Section section_types;\n        section_types.set<1>(std::move(types));\n        v10_module.sections.push_back(std::move(section_types));\n    }\n    if (!tables_.empty()) {\n        RawModuleDefV10Section section_tables;\n        section_tables.set<2>(tables_);\n        v10_module.sections.push_back(std::move(section_tables));\n    }\n    if (!reducers.empty()) {\n        RawModuleDefV10Section section_reducers;\n        section_reducers.set<3>(std::move(reducers));\n        v10_module.sections.push_back(std::move(section_reducers));\n    }\n    if (!procedures.empty()) {\n        RawModuleDefV10Section section_procedures;\n        section_procedures.set<4>(std::move(procedures));\n        v10_module.sections.push_back(std::move(section_procedures));\n    }\n    if (!views_.empty()) {\n        RawModuleDefV10Section section_views;\n        section_views.set<5>(views_);\n        v10_module.sections.push_back(std::move(section_views));\n    }\n    if (!schedules_.empty()) {\n        RawModuleDefV10Section section_schedules;\n        section_schedules.set<6>(schedules_);\n        v10_module.sections.push_back(std::move(section_schedules));\n    }\n    if (!lifecycle_reducers_.empty()) {\n        RawModuleDefV10Section section_lifecycle;\n        section_lifecycle.set<7>(lifecycle_reducers_);\n        v10_module.sections.push_back(std::move(section_lifecycle));\n    }\n    if (case_conversion_policy_.has_value()) {\n        RawModuleDefV10Section section_case_policy;\n        section_case_policy.set<9>(case_conversion_policy_.value());\n        v10_module.sections.push_back(std::move(section_case_policy));\n    }\n    if (!explicit_names_.empty()) {\n        RawModuleDefV10Section section_explicit_names;\n        section_explicit_names.set<10>(ExplicitNames{explicit_names_});\n        v10_module.sections.push_back(std::move(section_explicit_names));\n    }\n    if (!row_level_security_.empty()) {\n        RawModuleDefV10Section section_rls;\n        section_rls.set<8>(row_level_security_);\n        v10_module.sections.push_back(std::move(section_rls));\n    }\n\n    return v10_module;\n}\n\n} // namespace Internal\n} // namespace SpacetimeDB\n"
  },
  {
    "path": "crates/bindings-cpp/src/internal/v9_builder.cpp",
    "content": "// Include autogen types first to ensure complete type definitions\n#include \"spacetimedb/internal/autogen/SumType.g.h\"\n#include \"spacetimedb/internal/autogen/SumTypeVariant.g.h\"\n#include \"spacetimedb/internal/autogen/ProductType.g.h\"\n#include \"spacetimedb/internal/autogen/ProductTypeElement.g.h\"\n#include \"spacetimedb/internal/autogen/AlgebraicType.g.h\"\n#include \"spacetimedb/internal/autogen/TableType.g.h\"\n#include \"spacetimedb/internal/autogen/TableAccess.g.h\"\n#include \"spacetimedb/internal/autogen/RawIndexDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawTypeDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawConstraintDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawConstraintDataV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawUniqueConstraintDataV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawIndexAlgorithm.g.h\"\n#include \"spacetimedb/internal/autogen/RawTableDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawReducerDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawSequenceDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawScheduleDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/RawRowLevelSecurityDefV9.g.h\"\n#include \"spacetimedb/internal/autogen/Lifecycle.g.h\"\n\n// Then include the main headers\n#include \"spacetimedb/internal/v9_builder.h\"\n#include \"spacetimedb/internal/module_type_registration.h\"\n#include \"spacetimedb/internal/debug.h\"\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"spacetimedb/internal/Module_impl.h\"\n#include <functional>\n#include <cstdio>\n#include <memory>\n#include <algorithm>\n#include <set>\n\nnamespace SpacetimeDB {\nnamespace Internal {\n\nstatic RawModuleDefV9 g_v9_compat_module;\n\nRawModuleDefV9& GetV9Module() {\n    return g_v9_compat_module;\n}\n\nvoid ClearV9CompatModuleState() {\n    g_v9_compat_module = RawModuleDefV9{};\n}\n\n// demangle_cpp_type_name function removed - using global version from module_type_registration.cpp\n\n// Global V9Builder instance\nstd::unique_ptr<V9Builder> g_v9_builder;\n\nvoid initializeV9Builder() {\n    g_v9_builder = std::make_unique<V9Builder>();\n    // Also initialize the type registration system\n    initializeModuleTypeRegistration();\n}\n\nV9Builder& getV9Builder() {\n    if (!g_v9_builder) {\n        STDB_DEBUG(\"Initializing V9Builder\");\n        initializeV9Builder();\n    }\n    return *g_v9_builder;\n}\n\n// Constructor\nV9Builder::V9Builder() {\n    // No TypeRegistry anymore - everything goes through ModuleTypeRegistration\n}\n\n/**\n * Register a type using the unified type registration system\n * This is now just a thin wrapper around the ModuleTypeRegistration system\n */\nAlgebraicType V9Builder::registerType(const bsatn::AlgebraicType& bsatn_type,\n                                      const std::string& explicit_name,\n                                      const std::type_info* cpp_type) {\n    return getModuleTypeRegistration().registerType(bsatn_type, explicit_name, cpp_type);\n}\n\nvoid V9Builder::AddV9Table(const std::string& table_name,\n                                  const bsatn::AlgebraicType& table_type,\n                                  const std::type_info* cpp_type,\n                                  bool is_public,\n                                  const std::vector<uint16_t>& primary_key,\n                                  const std::vector<RawIndexDefV9>& indexes,\n                                  const std::vector<RawConstraintDefV9>& constraints,\n                                  const std::vector<RawSequenceDefV9>& sequences,\n                                  const std::optional<RawScheduleDefV9>& schedule) {\n    \n    // Register the table type using the unified system\n    // Use empty string to let the system extract the struct name from cpp_type\n    AlgebraicType registered_type = registerType(table_type, \"\", cpp_type);\n    \n    // Extract the typespace index from the registered type\n    uint32_t type_ref;\n    if (registered_type.get_tag() == AlgebraicType::Tag::Ref) {\n        type_ref = registered_type.get<0>();\n    } else {\n        // This shouldn't happen for a table type - tables should always be complex types\n        fprintf(stderr, \"ERROR: Table '%s' did not register as a complex type\\n\", table_name.c_str());\n        type_ref = 0;\n    }\n    \n    // RegisterTable now handles all constraint and index generation,\n    // so we just use what was passed in directly\n    \n    // Create the table definition\n    RawTableDefV9 table_def;\n    table_def.name = table_name;\n    table_def.product_type_ref = type_ref;\n    table_def.primary_key = primary_key;\n    table_def.indexes = indexes;  // Use indexes passed from RegisterTable\n    table_def.constraints = constraints;  // Use constraints passed from RegisterTable\n    table_def.sequences = sequences;\n    table_def.schedule = schedule;\n    table_def.table_type = TableType::User;  // User-defined table\n    table_def.table_access = is_public ? TableAccess::Public : TableAccess::Private;\n    \n    // Check if table already exists to prevent duplicates\n    auto& module = GetV9Module();\n    for (const auto& existing_table : module.tables) {\n        if (existing_table.name == table_name) {\n            fprintf(stdout, \"DEBUG: Table '%s' already registered, skipping duplicate registration\\n\", \n                       table_name.c_str());\n            return;\n        }\n    }\n    \n    // Debug: Show all existing table names before adding new one\n    //fprintf(stdout, \"DEBUG: Adding table '%s'. Existing tables: [\", table_name.c_str());\n    // for (size_t i = 0; i < module.tables.size(); ++i) {\n    //     if (i > 0) fprintf(stdout, \", \");\n    //     fprintf(stdout, \"'%s'\", module.tables[i].name.c_str());\n    // }\n    // fprintf(stdout, \"]\\n\");\n\n    // Add table to the global V9 module\n    module.tables.push_back(table_def);\n    //fprintf(stdout, \"DEBUG: Added table '%s' to V9 module (type_ref=%u) with %zu indexes, %zu constraints\\n\", \n    //           table_name.c_str(), type_ref, indexes.size(), constraints.size());\n    \n    // // Debug: Print index names\n    // for (const auto& idx : indexes) {\n    //     if (idx.name.has_value()) {\n    //         fprintf(stdout, \"  Index: %s\\n\", idx.name.value().c_str());\n    //     }\n    // }\n    \n    // // Debug: Print constraint names  \n    // for (const auto& constraint : constraints) {\n    //     if (constraint.name.has_value()) {\n    //         fprintf(stdout, \"  Constraint: %s\\n\", constraint.name.value().c_str());\n    //     }\n    // }\n}\n\nvoid V9Builder::AddV9Reducer(const std::string& reducer_name,\n                                   const std::vector<bsatn::AlgebraicType>& param_types,\n                                   const std::vector<std::string>& param_names,\n                                   const std::vector<const std::type_info*>& param_cpp_types,\n                                   const std::vector<std::string>& param_type_names,\n                                   std::optional<Lifecycle> lifecycle) {\n    // Create the reducer definition\n    RawReducerDefV9 reducer_def;\n    reducer_def.name = reducer_name;\n    \n    // Build the params ProductType\n    ProductType params;\n    for (size_t i = 0; i < param_types.size(); ++i) {\n        ProductTypeElement elem;\n        elem.name = i < param_names.size() ? std::make_optional(param_names[i]) : std::nullopt;\n        \n        // Register the parameter type with proper C++ type info and name\n        const std::type_info* cpp_type = i < param_cpp_types.size() ? param_cpp_types[i] : nullptr;\n        const std::string& type_name = i < param_type_names.size() ? param_type_names[i] : \"\";\n        \n        // Register the parameter type using the unified system\n        elem.algebraic_type = registerType(param_types[i], type_name, cpp_type);\n        \n        params.elements.push_back(elem);\n    }\n    \n    reducer_def.params = params;\n    reducer_def.lifecycle = lifecycle;\n    \n    // Add directly to the global V9 module\n    GetV9Module().reducers.push_back(reducer_def);\n    //fprintf(stdout, \"DEBUG: Added reducer '%s' to V9 module\\n\", reducer_name.c_str());\n}\n\n// Helper function to get field name from Product type structure\nstd::string V9Builder::getFieldName(const bsatn::AlgebraicType& table_type, uint16_t column_index) const {\n    if (table_type.tag() != bsatn::AlgebraicTypeTag::Product) {\n        return \"column\" + std::to_string(column_index);\n    }\n    \n    const auto& product = table_type.as_product();\n    if (column_index >= product.elements.size()) {\n        return \"column\" + std::to_string(column_index);\n    }\n    \n    const auto& element = product.elements[column_index];\n    if (element.name.has_value()) {\n        return element.name.value();\n    }\n    \n    return \"column\" + std::to_string(column_index);\n}\n\n// Helper function to generate constraints for primary key\nstd::vector<RawConstraintDefV9> V9Builder::generateConstraintsForPrimaryKey(\n    const std::string& table_name,\n    const bsatn::AlgebraicType& table_type,\n    const std::vector<uint16_t>& primary_key) const {\n    \n    std::vector<RawConstraintDefV9> constraints;\n    \n    if (primary_key.empty()) {\n        return constraints;  // No primary key, no constraints\n    }\n    \n    // Create separate unique constraint for each primary key column\n    for (uint16_t col_idx : primary_key) {\n        // Generate field name for this primary key column\n        std::string field_name = getFieldName(table_type, col_idx);\n        std::string constraint_name = table_name + \"_\" + field_name + \"_key\";\n        \n        // Create unique constraint data for this single column\n        RawUniqueConstraintDataV9 unique_data;\n        unique_data.columns = {col_idx};  // Single column constraint\n        \n        // Create constraint data (tagged enum) and set the variant\n        RawConstraintDataV9 constraint_data;\n        constraint_data.set<0>(unique_data);\n        \n        // Create constraint definition\n        RawConstraintDefV9 constraint_def;\n        constraint_def.name = constraint_name;\n        constraint_def.data = constraint_data;\n        \n        constraints.push_back(constraint_def);\n    }\n    \n    return constraints;\n}\n\n// Helper function to generate indexes for primary key\nstd::vector<RawIndexDefV9> V9Builder::generateIndexesForPrimaryKey(\n    const std::string& table_name,\n    const bsatn::AlgebraicType& table_type,\n    const std::vector<uint16_t>& primary_key) const {\n    \n    std::vector<RawIndexDefV9> indexes;\n    \n    if (primary_key.empty()) {\n        return indexes;  // No primary key, no indexes\n    }\n    \n    // Generate field name for the primary key column\n    std::string field_name = getFieldName(table_type, primary_key[0]);\n    std::string index_name = table_name + \"_\" + field_name + \"_idx_btree\";\n    \n    // Create BTree algorithm data\n    RawIndexAlgorithmBTreeData btree_data;\n    btree_data.columns = primary_key;\n    \n    // Create index algorithm (tagged enum) and set the BTree variant (index 0)\n    RawIndexAlgorithm algorithm;\n    algorithm.set<0>(btree_data);\n    \n    // Create index definition\n    RawIndexDefV9 index_def;\n    index_def.name = index_name;\n    index_def.accessor_name = field_name;\n    index_def.algorithm = algorithm;\n    \n    indexes.push_back(index_def);\n    \n    return indexes;\n}\n\nvoid V9Builder::RegisterSchedule(const std::string& table_name,\n                                 uint16_t scheduled_at_column,\n                                 const std::string& reducer_name) {\n    // Store the schedule to be applied when the table is registered\n    PendingSchedule schedule;\n    schedule.table_name = table_name;\n    schedule.scheduled_at_column = scheduled_at_column;\n    schedule.reducer_name = reducer_name;\n    \n    pending_schedules_[table_name] = schedule;\n    \n    // fprintf(stdout, \"DEBUG: Registered schedule for table '%s', column %u, reducer '%s'\\n\",\n    //         table_name.c_str(), scheduled_at_column, reducer_name.c_str());\n}\n\nvoid V9Builder::RegisterRowLevelSecurity(const std::string& sql_query) {\n    // Create an RLS definition and add it to the global module\n    RawRowLevelSecurityDefV9 rls_def;\n    rls_def.sql = sql_query;\n    \n    // Add directly to the global V9 module\n    GetV9Module().row_level_security.push_back(rls_def);\n    \n    // fprintf(stdout, \"DEBUG: Registered RLS policy: %s\\n\", sql_query.c_str());\n}\n\n// =============================================================================\n// End of V9Builder Implementation\n// =============================================================================\n\n\n// Helper to find existing table by name in the module\nRawTableDefV9* V9Builder::findTableByName(const std::string& table_name) {\n    auto& module = GetV9Module();\n    for (auto& table : module.tables) {\n        if (table.name == table_name) {\n            return &table;\n        }\n    }\n    return nullptr;\n}\n\n// Helper to create a BTree index for a field\nRawIndexDefV9 V9Builder::createBTreeIndex(const std::string& table_name,\n                                          const std::string& field_name,\n                                          uint16_t field_idx) const {\n    RawIndexAlgorithmBTreeData btree_data;\n    btree_data.columns = {field_idx};\n    \n    RawIndexAlgorithm algorithm;\n    algorithm.set<0>(btree_data);  // Set BTree variant\n    \n    RawIndexDefV9 index_def;\n    index_def.name = table_name + \"_\" + field_name + \"_idx_btree\";\n    index_def.accessor_name = field_name;\n    index_def.algorithm = algorithm;\n    \n    return index_def;\n}\n\n// Helper to create a unique constraint for a field\nRawConstraintDefV9 V9Builder::createUniqueConstraint(const std::string& table_name,\n                                                     const std::string& field_name,\n                                                     uint16_t field_idx) const {\n    RawUniqueConstraintDataV9 unique_data;\n    unique_data.columns = {field_idx};\n    \n    RawConstraintDataV9 constraint_data;\n    constraint_data.set<0>(unique_data);  // Set the Unique variant\n    \n    RawConstraintDefV9 constraint_def;\n    constraint_def.name = table_name + \"_\" + field_name + \"_key\";\n    constraint_def.data = std::move(constraint_data);\n    \n    return constraint_def;\n}\n\n\n} // namespace Internal\n} // namespace SpacetimeDB\n\n"
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/README.md",
    "content": "# SpacetimeDB SDK Client Comparison Tool\n\nCompares client code generated from Rust and C++ bindings modules to validate type system equivalence and catch regressions.\n\n## Quick Start\n\n```bash\n./run_client_comparison.sh\n```\n\nThis builds the C++ module, regenerates clients, and runs both client and module schema comparisons.\n\n## How It Works\n\n**3-Step Process:**\n1. **Build & Regenerate**: Compiles `lib.cpp` and generates fresh C++ client\n2. **Compare**: Runs client code and module schema comparisons in parallel  \n3. **Summarize**: Shows statistics and generates detailed analysis files\n\n**Prerequisites:**\n- SpacetimeDB CLI: `cargo build --release -p spacetimedb-cli`\n\n**Output:**\n- `client_diff_analysis.txt` - Detailed client code differences\n- `module_diff_analysis.txt` - Module schema comparison\n- Console summaries with key metrics\n\n## Scripts\n\n### `run_client_comparison.sh` (Main)\nComplete workflow: build → regenerate → compare → summarize\n\n### `scripts/compare_clients.sh`\nClient code comparison with intelligent filtering:\n- Ignores paths, version comments, whitespace\n- Shows only meaningful code differences\n- Provides accurate file statistics\n\n### `scripts/compare_modules.sh`\nModule schema comparison:\n- Publishes WASM to temporary databases\n- Extracts schemas via `spacetime describe --json`\n- Compares typespace, tables, reducers, named types\n\n### `scripts/regenerate_cpp_client.sh`\nRebuilds C++ client only (no comparison)\n\n## Understanding Results\n\n**Client Comparison:**\n- **File counts**: Total files generated by each SDK\n- **Identical files**: Files with no meaningful differences (after filtering)\n- **Difference types**: Type representations, imports, structure flattening\n\n**Module Schema:**\n- **Typespace**: Core type definitions and relationships\n- **Common differences**: Registration order, duplicates, special type handling\n\n## Usage\n\n**Standard workflow:**\n```bash\ncd crates/bindings-cpp/tests/client-comparison\n./run_client_comparison.sh\n```\n\n**Update baselines:**\n```bash\n# Rust baseline\ncd rust-sdk-test\nspacetime generate --lang rust --out-dir . --module-path ../../../modules/sdk-test\n\n# Rust baseline\n./scripts/regenerate_rust_client.sh\n\n# C++ baseline  \n./scripts/regenerate_cpp_client.sh\n```\n\n## Troubleshooting\n\n- **\"CLI not found\"**: Build CLI with `cargo build --release -p spacetimedb-cli`\n- **\"Client generation failed\"**: Check if lib.cpp publishes successfully\n- **\"No differences\"**: Normal if no changes were made\n\nThe tool validates C++ bindings functionality by comparing generated client code and module schemas against the Rust SDK baseline."
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/check_tables.py",
    "content": "#!/usr/bin/env python3\nimport json\nfrom collections import Counter\n\n# Load both schemas\nwith open('rust-module-schema.json', 'r') as f:\n    rust_schema = json.load(f)\n\nwith open('cpp-module-schema.json', 'r') as f:\n    cpp_schema = json.load(f)\n\n# Count actual tables\nrust_tables = rust_schema.get('tables', [])\ncpp_tables = cpp_schema.get('tables', [])\n\nprint(f'Actual table counts:')\nprint(f'  Rust: {len(rust_tables)} tables')\nprint(f'  C++: {len(cpp_tables)} tables')\n\n# Count all duplicate table entries (tables can appear multiple times for different accessors)\nrust_table_names = [t['name'] for t in rust_tables]\ncpp_table_names = [t['name'] for t in cpp_tables]\n\nrust_counts = Counter(rust_table_names)\ncpp_counts = Counter(cpp_table_names)\n\nprint(f'\\nTotal table entries (including duplicates):')\nprint(f'  Rust: {sum(rust_counts.values())} entries')\nprint(f'  C++: {sum(cpp_counts.values())} entries')\n\n# Check for tables that appear different number of times\nprint(f'\\nTables with different occurrence counts:')\nall_tables = set(rust_counts.keys()) | set(cpp_counts.keys())\ndiff_found = False\nfor table in sorted(all_tables):\n    rust_count = rust_counts.get(table, 0)\n    cpp_count = cpp_counts.get(table, 0)\n    if rust_count != cpp_count:\n        print(f'  {table}: Rust={rust_count}, C++={cpp_count}, diff={cpp_count-rust_count}')\n        diff_found = True\n\nif not diff_found:\n    print('  None - all tables appear the same number of times')\n\n# Check unique table names\nprint(f'\\nUnique table names:')\nprint(f'  Rust: {len(set(rust_table_names))} unique tables')\nprint(f'  C++: {len(set(cpp_table_names))} unique tables')\n\n# Check if table counts suggest duplicates\nprint(f'\\nTables appearing more than once:')\nfor table, count in rust_counts.items():\n    if count > 1:\n        print(f'  Rust: {table} appears {count} times')\n        \nfor table, count in cpp_counts.items():\n    if count > 1:\n        print(f'  C++: {table} appears {count} times')"
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/run_client_comparison.sh",
    "content": "#!/bin/bash\n\n# Combined script to regenerate C++ SDK client and run comparison\n# This ensures we're always comparing against the most current C++ module\n#\n# Usage:\n#   ./run_client_comparison.sh          # Smart rebuild (only if sources changed)\n#   ./run_client_comparison.sh --force  # Force rebuild even if sources unchanged\n\nset -e  # Exit on any error\n\n# Check for force flag\nFORCE_REBUILD=false\nif [ \"$1\" = \"--force\" ] || [ \"$1\" = \"-f\" ]; then\n    FORCE_REBUILD=true\n    echo \"Force rebuild requested...\"\nfi\n\necho \"Regenerating SDK clients and running comparison...\"\necho \"==================================================\"\n\n# STEP 1a: Regenerate Rust client (with smart rebuild detection)\necho \"\"\necho \"STEP 1a: Regenerating Rust SDK client...\"\necho \"========================================\"\n\nif [ ! -f scripts/regenerate_rust_client.sh ]; then\n    echo \"ERROR: scripts/regenerate_rust_client.sh not found!\"\n    echo \"Make sure you're running this from the client-comparison directory.\"\n    exit 1\nfi\n\n# Pass force flag to regenerate script if needed\nif [ \"$FORCE_REBUILD\" = true ]; then\n    # Remove existing Rust client to force rebuild\n    RUST_DIR=\"rust-sdk-test\"\n    if [ -d \"$RUST_DIR\" ]; then\n        echo \"Removing existing Rust client to force rebuild...\"\n        rm -rf \"$RUST_DIR\"\n    fi\nfi\n\n./scripts/regenerate_rust_client.sh\n\n# STEP 1b: Regenerate C++ client (with smart rebuild detection)\necho \"\"\necho \"STEP 1b: Regenerating C++ SDK client...\"\necho \"=======================================\"\n\nif [ ! -f scripts/regenerate_cpp_client.sh ]; then\n    echo \"ERROR: scripts/regenerate_cpp_client.sh not found!\"\n    echo \"Make sure you're running this from the client-comparison directory.\"\n    exit 1\nfi\n\n# Pass force flag to regenerate script if needed\nif [ \"$FORCE_REBUILD\" = true ]; then\n    # Temporarily remove the WASM to force rebuild\n    WASM_PATH=\"../../../../modules/sdk-test-cpp/build/lib.wasm\"\n    if [ -f \"$WASM_PATH\" ]; then\n        echo \"Removing existing WASM to force rebuild...\"\n        rm \"$WASM_PATH\"\n    fi\nfi\n\n./scripts/regenerate_cpp_client.sh\n\n# STEP 2: Run comparisons in parallel\necho \"\"\necho \"STEP 2: Running comparisons...\"\necho \"==============================\"\n\n# Check if comparison scripts exist\nif [ ! -f scripts/compare_clients.sh ]; then\n    echo \"ERROR: scripts/compare_clients.sh not found!\"\n    echo \"Make sure you're running this from the client-comparison directory.\"\n    exit 1\nfi\n\nif [ ! -f scripts/compare_modules.sh ]; then\n    echo \"ERROR: scripts/compare_modules.sh not found!\"\n    echo \"Make sure you're running this from the client-comparison directory.\"\n    exit 1\nfi\n\necho \"Running client and module comparisons in parallel...\"\n\n# Run both comparisons in parallel\n./scripts/compare_clients.sh &\nCLIENT_PID=$!\n\n./scripts/compare_modules.sh &\nMODULE_PID=$!\n\n# Wait for both to complete\necho \"Waiting for client comparison to complete...\"\nwait $CLIENT_PID\nCLIENT_EXIT=$?\n\necho \"Waiting for module comparison to complete...\"\nwait $MODULE_PID  \nMODULE_EXIT=$?\n\n# Check if both succeeded\nif [ $CLIENT_EXIT -ne 0 ]; then\n    echo \"❌ Client comparison failed!\"\nfi\n\nif [ $MODULE_EXIT -ne 0 ]; then\n    echo \"❌ Module comparison failed!\"\nfi\n\nif [ $CLIENT_EXIT -eq 0 ] && [ $MODULE_EXIT -eq 0 ]; then\n    echo \"✅ Both comparisons completed successfully!\"\nelse\n    echo \"⚠️  One or more comparisons failed - check output above\"\nfi\n\n# STEP 3: Show results summary\necho \"\"\necho \"STEP 3: Results Summary\"\necho \"=======================\"\n\n# Client comparison results\nif [ -f client_diff_analysis.txt ]; then\n    echo \"\"\n    echo \"📊 Client Comparison Summary:\"\n    echo \"$(grep \"Total files compared:\" client_diff_analysis.txt)\"\n    echo \"$(grep \"Identical files (ignoring version):\" client_diff_analysis.txt)\"\n    echo \"$(grep \"Different files:\" client_diff_analysis.txt)\"\n    \n    # Extract file counts\n    RUST_COUNT=$(grep \"Rust SDK files:\" client_diff_analysis.txt | cut -d: -f2 | tr -d ' ')\n    CPP_COUNT=$(grep \"C++ SDK files:\" client_diff_analysis.txt | cut -d: -f2 | tr -d ' ')\n    \n    if [ \"$RUST_COUNT\" = \"$CPP_COUNT\" ]; then\n        echo \"File count: ✅ Perfect match ($CPP_COUNT files each)\"\n    else\n        echo \"File count: ⚠️  Mismatch (Rust: $RUST_COUNT, C++: $CPP_COUNT)\"\n    fi\n    \n    echo \"\"\n    echo \"📁 Client analysis: client_diff_analysis.txt ($(du -h client_diff_analysis.txt | cut -f1))\"\nelse\n    echo \"❌ Client analysis file not found!\"\nfi\n\n# Module comparison results\nif [ -f module_diff_analysis.txt ]; then\n    echo \"\"\n    echo \"🔧 Module Schema Summary:\"\n    RUST_TYPES=$(grep \"Rust SDK:\" module_diff_analysis.txt | grep \"named types\" | cut -d: -f2 | cut -d' ' -f2)\n    CPP_TYPES=$(grep \"C++ SDK:\" module_diff_analysis.txt | grep \"named types\" | cut -d: -f2 | cut -d' ' -f3)\n    \n    echo \"Rust schema: $RUST_TYPES named types\"\n    echo \"C++ schema: $CPP_TYPES named types\"\n    \n    if [ \"$RUST_TYPES\" = \"$CPP_TYPES\" ]; then\n        echo \"Schema types: ✅ Perfect match ($CPP_TYPES types each)\"\n    else\n        echo \"Schema types: ⚠️  Mismatch (difference: $((CPP_TYPES - RUST_TYPES)))\"\n    fi\n    \n    echo \"\"\n    echo \"📁 Module analysis: module_diff_analysis.txt ($(du -h module_diff_analysis.txt | cut -f1))\"\nelse\n    echo \"❌ Module analysis file not found!\"\nfi\n\necho \"\"\necho \"🎉 Regeneration and comparison complete!\"\necho \"\"\necho \"Usage tips:\"\necho \"  • ./run_client_comparison.sh          # Smart rebuild (only if sources changed)\"\necho \"  • ./run_client_comparison.sh --force  # Force rebuild even if unchanged\"\necho \"\"\necho \"Next steps:\"\necho \"  • Review client_diff_analysis.txt for client generation differences\"\necho \"  • Review module_diff_analysis.txt for schema-level differences\"\necho \"  • Focus on remaining type resolution issues if any\"\necho \"  • Check for any new regressions or improvements\""
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/scripts/compare_clients.sh",
    "content": "#!/bin/bash\n\n# Script to compare Rust and C++ module library generated clients\n# Ignores version comment lines for cleaner comparison\n\nOUTPUT_FILE=\"client_diff_analysis.txt\"\nRUST_DIR=\"rust-sdk-test\"\nCPP_DIR=\"cpp-sdk-test\"\n\necho \"SpacetimeDB Module Library Client Generation Comparison\" > $OUTPUT_FILE\necho \"=======================================================\" >> $OUTPUT_FILE\necho \"Generated on: $(date)\" >> $OUTPUT_FILE\necho \"Comparing: $RUST_DIR vs $CPP_DIR\" >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\n# File count comparison\necho \"FILE COUNT COMPARISON\" >> $OUTPUT_FILE\necho \"--------------------\" >> $OUTPUT_FILE\nRUST_COUNT=$(find $RUST_DIR -name \"*.rs\" | wc -l)\nCPP_COUNT=$(find $CPP_DIR -name \"*.rs\" | wc -l)\necho \"Rust module library files: $RUST_COUNT\" >> $OUTPUT_FILE\necho \"C++ module library files:  $CPP_COUNT\" >> $OUTPUT_FILE\necho \"Difference:     $((CPP_COUNT - RUST_COUNT))\" >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\n# Files only in one directory\necho \"FILES UNIQUE TO EACH MODULE LIBRARY\" >> $OUTPUT_FILE\necho \"-----------------------------------\" >> $OUTPUT_FILE\necho \"Files only in Rust module library:\" >> $OUTPUT_FILE\ncomm -23 <(cd $RUST_DIR && find . -name \"*.rs\" | sort) <(cd $CPP_DIR && find . -name \"*.rs\" | sort) >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\necho \"Files only in C++ module library:\" >> $OUTPUT_FILE\ncomm -13 <(cd $RUST_DIR && find . -name \"*.rs\" | sort) <(cd $CPP_DIR && find . -name \"*.rs\" | sort) >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\n# Common files\necho \"COMMON FILES COMPARISON\" >> $OUTPUT_FILE\necho \"----------------------\" >> $OUTPUT_FILE\nCOMMON_FILES=$(comm -12 <(cd $RUST_DIR && find . -name \"*.rs\" | sort) <(cd $CPP_DIR && find . -name \"*.rs\" | sort))\necho \"Number of common files: $(echo \"$COMMON_FILES\" | wc -l)\" >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\n# Function to get meaningful diff content only\nget_meaningful_diff() {\n    local file1=\"$1\"\n    local file2=\"$2\"\n    \n    # Create temp files without version comment lines\n    local temp1=$(mktemp)\n    local temp2=$(mktemp)\n    \n    # Remove version comment line (line starting with \"// This was generated using\")\n    grep -v \"^// This was generated using spacetimedb cli version\" \"$file1\" > \"$temp1\"\n    grep -v \"^// This was generated using spacetimedb cli version\" \"$file2\" > \"$temp2\"\n    \n    # Check if files are identical after filtering version comments\n    if diff -q \"$temp1\" \"$temp2\" > /dev/null 2>&1; then\n        # Files are identical after filtering\n        rm \"$temp1\" \"$temp2\"\n        return 1  # No meaningful differences\n    else\n        # Files have differences - extract meaningful change lines\n        diff -u \"$temp1\" \"$temp2\" 2>/dev/null | grep -E '^[+-]' | grep -v -E '^[+-]{3}' | head -20\n        rm \"$temp1\" \"$temp2\"\n        return 0  # Has meaningful differences\n    fi\n}\n\n# Detailed diff of common files\necho \"DETAILED DIFFS OF COMMON FILES\" >> $OUTPUT_FILE\necho \"==============================\" >> $OUTPUT_FILE\necho \"(Version comment lines are ignored)\" >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\nIDENTICAL_COUNT=0\nDIFFERENT_COUNT=0\n\nfor file in $COMMON_FILES; do\n    # Get meaningful differences for this file\n    meaningful_diff=$(get_meaningful_diff \"$RUST_DIR/$file\" \"$CPP_DIR/$file\")\n    diff_result=$?\n    \n    if [ $diff_result -eq 0 ] && [ -n \"$meaningful_diff\" ]; then\n        # File has meaningful differences\n        ((DIFFERENT_COUNT++))\n        echo \"\" >> $OUTPUT_FILE\n        echo \"DIFF: $file\" >> $OUTPUT_FILE\n        echo \"$(printf '=%.0s' {1..50})\" >> $OUTPUT_FILE\n        echo \"$meaningful_diff\" >> $OUTPUT_FILE\n        echo \"\" >> $OUTPUT_FILE\n    else\n        # File is identical after filtering\n        ((IDENTICAL_COUNT++))\n    fi\ndone\n\n# Statistics summary with accurate counts\necho \"STATISTICS SUMMARY\" >> $OUTPUT_FILE\necho \"==================\" >> $OUTPUT_FILE\necho \"Total files compared: $(echo \"$COMMON_FILES\" | wc -l)\" >> $OUTPUT_FILE\necho \"Identical files (ignoring version): $IDENTICAL_COUNT\" >> $OUTPUT_FILE\necho \"Different files: $DIFFERENT_COUNT\" >> $OUTPUT_FILE\necho \"\" >> $OUTPUT_FILE\n\necho \"Analysis complete. Results written to: $OUTPUT_FILE\"\necho \"File size: $(du -h $OUTPUT_FILE | cut -f1)\"\necho \"\"\necho \"Summary:\"\necho \"  - Identical files (ignoring version): $IDENTICAL_COUNT\"\necho \"  - Different files: $DIFFERENT_COUNT\""
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/scripts/compare_modules.sh",
    "content": "#!/bin/bash\n# compare_modules.sh - Compare module schemas between Rust and C++ SDKs\n\nset -e\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &> /dev/null && pwd)\"\nPARENT_DIR=\"$(dirname \"$SCRIPT_DIR\")\"\ncd \"$PARENT_DIR\"\n\n# Colors for output\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nYELLOW='\\033[1;33m'\nNC='\\033[0m' # No Color\n\n# Detect available Python command (cross-platform)\ndetect_python() {\n    local python_cmd=\"\"\n    \n    # Try different Python commands in order of preference\n    for cmd in python3 python py; do\n        if command -v \"$cmd\" >/dev/null 2>&1; then\n            # Test if the command actually works and has json module\n            if \"$cmd\" -c \"import json; import sys; print('OK')\" >/dev/null 2>&1; then\n                python_cmd=\"$cmd\"\n                break\n            fi\n        fi\n    done\n    \n    if [ -z \"$python_cmd\" ]; then\n        echo \"❌ Error: No working Python installation found (tried: python3, python, py)\" >&2\n        echo \"   Please install Python 3.x with json module support\" >&2\n        exit 1\n    fi\n    \n    echo \"$python_cmd\"\n}\n\n# Set the Python command to use\nPYTHON_CMD=$(detect_python)\necho \"Using Python command: $PYTHON_CMD\"\n\necho \"Comparing module schemas between Rust and C++ SDKs...\"\necho \"==================================================\"\n\n# Function to get module schema from WASM\nget_module_schema() {\n    local sdk_name=\"$1\"\n    local output_file=\"$2\"\n    local wasm_path=\"$3\"\n    \n    echo \"Getting $sdk_name module schema from WASM...\"\n    \n    # Use the same WASM file that client generation uses  \n    local temp_db\n    if [ \"$sdk_name\" = \"Rust\" ]; then\n        temp_db=\"rust-schema-temp\"\n    else\n        temp_db=\"cpp-schema-temp\"\n    fi\n    \n    echo \"  Publishing WASM to temporary database: $temp_db from $wasm_path\"\n    if spacetime publish --bin-path \"$wasm_path\" \"$temp_db\" -c -y >/dev/null 2>&1; then\n        echo \"  Retrieving schema from published module...\"\n        if spacetime describe --json \"$temp_db\" > \"$output_file\" 2>/dev/null; then\n            echo \"✅ $sdk_name schema retrieved successfully from WASM\"\n            # Pretty print the JSON for better comparison\n            $PYTHON_CMD -m json.tool \"$output_file\" > \"${output_file}.tmp\" 2>/dev/null && mv \"${output_file}.tmp\" \"$output_file\" || true\n        else\n            echo \"❌ Failed to get $sdk_name schema from published module\"\n            return 1\n        fi\n    else\n        echo \"❌ Failed to publish $sdk_name WASM to temporary database\"\n        return 1\n    fi\n}\n\n# Function to extract and analyze schema sections\nanalyze_schema_section() {\n    local schema_file=\"$1\"\n    local section_name=\"$2\"\n    local output_prefix=\"$3\"\n    \n    case \"$section_name\" in\n        \"typespace\")\n            # Extract first types array (line 3)\n            sed -n '3,/^    ],$/p' \"$schema_file\" > \"${output_prefix}_typespace.json\"\n            ;;\n        \"tables\")\n            # Extract tables section\n            sed -n '/\"tables\":/,/^    ],$/p' \"$schema_file\" > \"${output_prefix}_tables.json\"\n            ;;\n        \"reducers\")\n            # Extract reducers section\n            sed -n '/\"reducers\":/,/^    ],$/p' \"$schema_file\" > \"${output_prefix}_reducers.json\"\n            ;;\n        \"named_types\")\n            # Extract last types array (after reducers)\n            awk '/\"types\":/ && ++count==2,/^}$/' \"$schema_file\" > \"${output_prefix}_named_types.json\"\n            ;;\n    esac\n}\n\n# Function to count items in a section\ncount_section_items() {\n    local file=\"$1\"\n    local pattern=\"$2\"\n    \n    if [ -f \"$file\" ]; then\n        grep -c \"$pattern\" \"$file\" 2>/dev/null || echo \"0\"\n    else\n        echo \"0\"\n    fi\n}\n\n# Function to get meaningful diff content only\nget_meaningful_diff() {\n    local file1=\"$1\"\n    local file2=\"$2\"\n    \n    # Create temporary files with version comments filtered out\n    local temp1=$(mktemp)\n    local temp2=$(mktemp)\n    \n    # Filter out version-specific content and normalize spacing\n    grep -v -E \"(commit [a-f0-9]+|version [0-9]+\\.[0-9]+\\.[0-9]+)\" \"$file1\" | sed 's/[[:space:]]*$//' > \"$temp1\" 2>/dev/null || true\n    grep -v -E \"(commit [a-f0-9]+|version [0-9]+\\.[0-9]+\\.[0-9]+)\" \"$file2\" | sed 's/[[:space:]]*$//' > \"$temp2\" 2>/dev/null || true\n    \n    # Get diff content, filter to only +/- lines, remove diff headers, limit output\n    diff -u \"$temp1\" \"$temp2\" 2>/dev/null | grep -E '^[+-]' | grep -v -E '^[+-]{3}' | head -50\n    \n    # Cleanup\n    rm -f \"$temp1\" \"$temp2\"\n}\n\n# Function to find differences in named items\nfind_differences() {\n    local rust_file=\"$1\"\n    local cpp_file=\"$2\"\n    local item_pattern=\"$3\"\n    \n    # Extract names from both files\n    local rust_names=$(mktemp)\n    local cpp_names=$(mktemp)\n    \n    grep -o \"$item_pattern\" \"$rust_file\" 2>/dev/null | sort | uniq > \"$rust_names\" || true\n    grep -o \"$item_pattern\" \"$cpp_file\" 2>/dev/null | sort | uniq > \"$cpp_names\" || true\n    \n    echo \"Only in Rust:\"\n    comm -23 \"$rust_names\" \"$cpp_names\" | head -10 | sed 's/^/  - /'\n    \n    echo \"Only in C++:\"\n    comm -13 \"$rust_names\" \"$cpp_names\" | head -10 | sed 's/^/  - /'\n    \n    # Cleanup\n    rm -f \"$rust_names\" \"$cpp_names\"\n}\n\n# Paths - use the same WASM files that client generation uses\nRUST_WASM_PATH=$(realpath \"../../../../target/wasm32-unknown-unknown/release/sdk_test_module.wasm\")\nCPP_WASM_PATH=$(realpath \"../../../../modules/sdk-test-cpp/build/lib.wasm\")\n\nRUST_SCHEMA=\"rust-module-schema.json\"  \nCPP_SCHEMA=\"cpp-module-schema.json\"\nANALYSIS_FILE=\"module_diff_analysis.txt\"\n\n# Get schemas from WASM files\necho\necho \"Step 1: Retrieving module schemas from WASM...\"\necho \"==============================================\"\n\n# Check if WASM files exist and get schemas\nRUST_AVAILABLE=false\nCPP_AVAILABLE=false\n\n# Check if we have an existing Rust schema file or need to regenerate\nif [ -f \"$RUST_SCHEMA\" ]; then\n    echo \"✅ Using existing Rust schema from $RUST_SCHEMA\"\n    RUST_AVAILABLE=true\nelif [ ! -f \"$RUST_WASM_PATH\" ]; then\n    echo \"❌ Rust WASM not found at: $RUST_WASM_PATH\"\n    echo \"   And no existing rust-module-schema.json found\"\n    echo \"   Build it with: cargo build --target wasm32-unknown-unknown --release -p sdk-test-module\"\nelse\n    if get_module_schema \"Rust\" \"$RUST_SCHEMA\" \"$RUST_WASM_PATH\"; then\n        RUST_AVAILABLE=true\n    else\n        echo \"❌ Failed to get Rust module schema\"\n    fi\nfi\n\n# Check if we have an existing C++ schema file or need to regenerate\nif [ -f \"$CPP_SCHEMA\" ]; then\n    echo \"✅ Using existing C++ schema from $CPP_SCHEMA\"\n    CPP_AVAILABLE=true\nelif [ ! -f \"$CPP_WASM_PATH\" ]; then\n    echo \"❌ C++ WASM not found at: $CPP_WASM_PATH\"\n    echo \"   And no existing cpp-module-schema.json found\"\n    echo \"   Build it with: cmake --build build\"\nelse\n    if get_module_schema \"C++\" \"$CPP_SCHEMA\" \"$CPP_WASM_PATH\"; then\n        CPP_AVAILABLE=true\n    else\n        echo \"❌ Failed to get C++ module schema\"\n    fi\nfi\n\n# Exit only if neither schema is available\nif [ \"$RUST_AVAILABLE\" = false ] && [ \"$CPP_AVAILABLE\" = false ]; then\n    echo \"❌ Cannot continue - no module schemas available\"\n    exit 1\nfi\n\necho\necho \"Step 2: Analyzing schemas...\"\necho \"============================\"\n\n# Extract sections for analysis\nfor section in typespace tables reducers named_types; do\n    if [ \"$RUST_AVAILABLE\" = true ]; then\n        analyze_schema_section \"$RUST_SCHEMA\" \"$section\" \"rust\"\n    fi\n    if [ \"$CPP_AVAILABLE\" = true ]; then\n        analyze_schema_section \"$CPP_SCHEMA\" \"$section\" \"cpp\"\n    fi\ndone\n\n# Start analysis file\n{\n    echo \"SpacetimeDB Module Schema Comparison\"\n    echo \"====================================\"\n    echo \"Generated on: $(date)\"\n    echo \"Comparing: Rust SDK vs C++ SDK module schemas\"\n    echo\n    \n    # Basic file info\n    echo \"SCHEMA FILE SIZES\"\n    echo \"-----------------\"\n    if [ \"$RUST_AVAILABLE\" = true ]; then\n        echo \"Rust schema: $(wc -c < \"$RUST_SCHEMA\" 2>/dev/null || echo \"N/A\") bytes\"\n    else\n        echo \"Rust schema: Not available\"\n    fi\n    if [ \"$CPP_AVAILABLE\" = true ] && [ -f \"$CPP_SCHEMA\" ]; then\n        echo \"C++ schema:  $(wc -c < \"$CPP_SCHEMA\" 2>/dev/null || echo \"N/A\") bytes\"\n    else\n        echo \"C++ schema:  Not available\"\n    fi\n    if [ \"$RUST_AVAILABLE\" = true ] && [ \"$CPP_AVAILABLE\" = true ]; then\n        echo \"Difference:  $(($(wc -c < \"$CPP_SCHEMA\") - $(wc -c < \"$RUST_SCHEMA\"))) bytes\"\n    fi\n    echo\n    \n    echo \"==================================================================\"\n    echo \"SECTION 1: TYPESPACE (Anonymous types used internally)\"\n    echo \"==================================================================\"\n    echo\n    \n    rust_typespace=0\n    cpp_typespace=0\n    \n    # Count types in the typespace.types array\n    if [ \"$RUST_AVAILABLE\" = true ] && [ -f \"$RUST_SCHEMA\" ]; then\n        rust_typespace=$($PYTHON_CMD -c \"import json; data=json.load(open('$RUST_SCHEMA')); ts=data.get('typespace', {}); types=ts.get('types', []); print(len(types))\" 2>/dev/null || echo \"0\")\n        echo \"- Rust SDK: $rust_typespace types\"\n    else\n        echo \"- Rust SDK: Not available\"\n    fi\n    \n    if [ \"$CPP_AVAILABLE\" = true ] && [ -f \"$CPP_SCHEMA\" ]; then\n        cpp_typespace=$($PYTHON_CMD -c \"import json; data=json.load(open('$CPP_SCHEMA')); ts=data.get('typespace', {}); types=ts.get('types', []); print(len(types))\" 2>/dev/null || echo \"0\")\n        echo \"- C++ SDK:  $cpp_typespace types\"\n    else\n        echo \"- C++ SDK:  Not available\"\n    fi\n    \n    if [ \"$RUST_AVAILABLE\" = true ] && [ \"$CPP_AVAILABLE\" = true ]; then\n        echo \"- Difference: $((cpp_typespace - rust_typespace))\"\n    fi\n    echo\n    \n    # Analyze type patterns in typespace\n    if [ \"$CPP_AVAILABLE\" = true ] || [ \"$RUST_AVAILABLE\" = true ]; then\n        echo \"Type patterns in typespace:\"\n        \n        if [ -f \"rust_typespace.json\" ]; then\n            rust_products=$(grep -c '\"Product\":' rust_typespace.json 2>/dev/null || echo \"0\")\n            rust_sums=$(grep -c '\"Sum\":' rust_typespace.json 2>/dev/null || echo \"0\")\n        else\n            rust_products=\"N/A\"\n            rust_sums=\"N/A\"\n        fi\n        \n        if [ -f \"cpp_typespace.json\" ]; then\n            cpp_products=$(grep -c '\"Product\":' cpp_typespace.json 2>/dev/null || echo \"0\")\n            cpp_sums=$(grep -c '\"Sum\":' cpp_typespace.json 2>/dev/null || echo \"0\")\n        else\n            cpp_products=\"N/A\"\n            cpp_sums=\"N/A\"\n        fi\n        \n        echo \"- Product types: Rust=$rust_products, C++=$cpp_products\"\n        echo \"- Sum types: Rust=$rust_sums, C++=$cpp_sums\"\n        echo\n    fi\n    \n    echo \"==================================================================\"\n    echo \"SECTION 2: TABLES\"\n    echo \"==================================================================\"\n    echo\n    # Count tables by counting table objects (look for '\"name\":' at the table level, not field level)\n    # Each table starts with {\"name\": so count those\n    rust_tables=$($PYTHON_CMD -c \"import json; data=json.load(open('$RUST_SCHEMA')); print(len(data.get('tables', [])))\" 2>/dev/null || echo \"0\")\n    cpp_tables=$($PYTHON_CMD -c \"import json; data=json.load(open('$CPP_SCHEMA')); print(len(data.get('tables', [])))\" 2>/dev/null || echo \"0\")\n    echo \"Table counts:\"\n    echo \"- Rust SDK: $rust_tables tables\"\n    echo \"- C++ SDK:  $cpp_tables tables\"\n    echo \"- Difference: $((cpp_tables - rust_tables))\"\n    \n    if [ \"$rust_tables\" -eq \"$cpp_tables\" ] && [ \"$rust_tables\" -gt \"0\" ]; then\n        echo \"✅ Table count matches!\"\n    elif [ \"$rust_tables\" -ne \"$cpp_tables\" ]; then\n        echo \"⚠️  Table count mismatch!\"\n        echo\n        echo \"Table differences:\"\n        find_differences \"$RUST_SCHEMA\" \"$CPP_SCHEMA\" '\"name\": \"[^\"]*\"'\n    fi\n    echo\n    \n    echo \"==================================================================\"\n    echo \"SECTION 3: REDUCERS\"\n    echo \"==================================================================\"\n    echo\n    # Count reducers properly using Python\n    rust_reducers=$($PYTHON_CMD -c \"import json; data=json.load(open('$RUST_SCHEMA')); print(len(data.get('reducers', [])))\" 2>/dev/null || echo \"0\")\n    cpp_reducers=$($PYTHON_CMD -c \"import json; data=json.load(open('$CPP_SCHEMA')); print(len(data.get('reducers', [])))\" 2>/dev/null || echo \"0\")\n    echo \"Reducer counts:\"\n    echo \"- Rust SDK: $rust_reducers reducers\"\n    echo \"- C++ SDK:  $cpp_reducers reducers\"\n    echo \"- Difference: $((cpp_reducers - rust_reducers))\"\n    \n    if [ \"$rust_reducers\" -eq \"$cpp_reducers\" ] && [ \"$rust_reducers\" -gt \"0\" ]; then\n        echo \"✅ Reducer count matches!\"\n    elif [ \"$rust_reducers\" -ne \"$cpp_reducers\" ]; then\n        echo \"⚠️  Reducer count mismatch!\"\n        echo\n        echo \"Reducer differences:\"\n        # Extract reducer names from both schemas\n        sed -n '/\"reducers\":/,/^    ],$/p' \"$RUST_SCHEMA\" > rust_reducers_section.tmp\n        sed -n '/\"reducers\":/,/^    ],$/p' \"$CPP_SCHEMA\" > cpp_reducers_section.tmp\n        find_differences \"rust_reducers_section.tmp\" \"cpp_reducers_section.tmp\" '\"name\": \"[^\"]*\"'\n        rm -f rust_reducers_section.tmp cpp_reducers_section.tmp\n    fi\n    echo\n    \n    echo \"==================================================================\"\n    echo \"SECTION 4: NAMED TYPES (User-defined types)\"\n    echo \"==================================================================\"\n    echo\n    \n    # Count named types in the typespace.types array (these are the actual named/anonymous types)\n    rust_named_types=$($PYTHON_CMD -c \"import json; data=json.load(open('$RUST_SCHEMA')); ts=data.get('typespace', {}); types=ts.get('types', []); print(len(types))\" 2>/dev/null || echo \"0\")\n    cpp_named_types=$($PYTHON_CMD -c \"import json; data=json.load(open('$CPP_SCHEMA')); ts=data.get('typespace', {}); types=ts.get('types', []); print(len(types))\" 2>/dev/null || echo \"0\")\n    \n    echo \"Named type counts:\"\n    echo \"- Rust SDK: $rust_named_types named types\"\n    echo \"- C++ SDK:  $cpp_named_types named types\"\n    echo \"- Difference: $((cpp_named_types - rust_named_types))\"\n    \n    if [ \"$rust_named_types\" -ne \"$cpp_named_types\" ]; then\n        echo\n        echo \"Named type differences:\"\n        # Extract just the type names for comparison\n        grep -A2 '\"name\":' rust_named_types.json | grep '\"name\":' | grep -o '\"[^\"]*\"$' | sort > rust_type_names.tmp\n        grep -A2 '\"name\":' cpp_named_types.json | grep '\"name\":' | grep -o '\"[^\"]*\"$' | sort > cpp_type_names.tmp\n        \n        echo \"Only in Rust:\"\n        comm -23 rust_type_names.tmp cpp_type_names.tmp | head -10 | sed 's/^/  - /'\n        \n        echo \"Only in C++:\"\n        comm -13 rust_type_names.tmp cpp_type_names.tmp | head -10 | sed 's/^/  - /'\n        \n        rm -f rust_type_names.tmp cpp_type_names.tmp\n    fi\n    echo\n    \n    # Analyze specific types\n    echo \"CRITICAL TYPE ANALYSIS\"\n    echo \"=====================\"\n    echo\n    \n    # Look for specific patterns that might indicate issues\n    echo \"Type index examples (showing potential misalignment):\"\n    echo \"Rust:\"\n    grep -A3 '\"ByteStruct\"\\|\"EnumWithPayload\"\\|\"UnitStruct\"' rust_named_types.json 2>/dev/null | grep -E '\"name\":|\"ty\":' | head -6\n    echo\n    echo \"C++:\"\n    grep -A3 '\"ByteStruct\"\\|\"EnumWithPayload\"\\|\"UnitStruct\"' cpp_named_types.json 2>/dev/null | grep -E '\"name\":|\"ty\":' | head -6\n    echo\n    \n    # Check for potential duplicate registrations\n    echo \"Checking for duplicate type names:\"\n    echo \"Rust duplicates:\"\n    grep '\"name\":' rust_named_types.json | grep -o '\"[^\"]*\"$' | sort | uniq -c | grep -v '^ *1 ' | head -5\n    echo \"C++ duplicates:\"\n    grep '\"name\":' cpp_named_types.json | grep -o '\"[^\"]*\"$' | sort | uniq -c | grep -v '^ *1 ' | head -5\n    echo\n    \n    echo \"==================================================================\"\n    echo \"SUMMARY\"\n    echo \"==================================================================\"\n    echo\n    \n    # Overall summary\n    total_rust=$((rust_typespace + rust_tables + rust_reducers + rust_named_types))\n    total_cpp=$((cpp_typespace + cpp_tables + cpp_reducers + cpp_named_types))\n    \n    echo \"Total counts across all sections:\"\n    echo \"- Rust SDK: $total_rust items\"\n    echo \"- C++ SDK:  $total_cpp items\"\n    echo \"- Difference: $((total_cpp - total_rust))\"\n    echo\n    \n    echo \"Key findings:\"\n    if [ \"$((cpp_typespace - rust_typespace))\" -gt 0 ]; then\n        echo \"⚠️  C++ has $((cpp_typespace - rust_typespace)) extra anonymous types in typespace\"\n    fi\n    \n    if [ \"$rust_tables\" -ne \"$cpp_tables\" ]; then\n        echo \"⚠️  Table count differs by $((cpp_tables - rust_tables))\"\n    fi\n    \n    if [ \"$rust_reducers\" -ne \"$cpp_reducers\" ]; then\n        echo \"⚠️  Reducer count differs by $((cpp_reducers - rust_reducers))\"\n    fi\n    \n    if [ \"$((cpp_named_types - rust_named_types))\" -ne 0 ]; then\n        echo \"⚠️  Named type count differs by $((cpp_named_types - rust_named_types))\"\n    fi\n    \n    if [ \"$rust_tables\" -eq \"$cpp_tables\" ] && [ \"$rust_reducers\" -eq \"$cpp_reducers\" ]; then\n        echo \"✅ Table and reducer counts match perfectly\"\n    fi\n    \n} > \"$ANALYSIS_FILE\"\n\n# Clean up temporary files\nrm -f rust_*.json cpp_*.json 2>/dev/null\n\necho \"✅ Module schema analysis complete!\"\necho \necho -e \"${GREEN}📊 Summary:${NC}\"\n\n# Quick summary from the analysis\nrust_named=$(grep \"Rust SDK:.*named types\" \"$ANALYSIS_FILE\" | tail -1 | grep -o '[0-9]\\+' | head -1)\ncpp_named=$(grep \"C\\+\\+ SDK:.*named types\" \"$ANALYSIS_FILE\" | tail -1 | grep -o '[0-9]\\+' | head -1)\n\nif [ -n \"$rust_named\" ] && [ -n \"$cpp_named\" ]; then\n    echo \"  • Rust schema: $rust_named named types\"\n    echo \"  • C++ schema: $cpp_named named types\"\n    \n    if [ \"$rust_named\" -eq \"$cpp_named\" ]; then\n        echo -e \"  • ${GREEN}✅ Named type count matches${NC}\"\n    else\n        echo -e \"  • ${YELLOW}⚠️  Named type count differs by $((cpp_named - rust_named))${NC}\"  \n    fi\nfi\n\necho\necho -e \"${BLUE}📁 Detailed analysis: $ANALYSIS_FILE${NC}\"\necho -e \"   File size: $(ls -lh \"$ANALYSIS_FILE\" | awk '{print $5}')\"\necho"
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/scripts/regenerate_cpp_client.sh",
    "content": "#!/bin/bash\n\n# Script to regenerate C++ SDK client from latest lib.cpp build\n# This ensures we're always comparing against the most current C++ module\n\nset -e  # Exit on any error\n# Detect the correct emcmake command for cross-platform compatibility\ndetect_emcmake_command() {\n    if command -v emcmake >/dev/null 2>&1; then\n        echo \"emcmake\"\n    elif command -v emcmake.bat >/dev/null 2>&1; then\n        echo \"emcmake.bat\"\n    elif [ -n \"$EMSDK\" ] && [ -f \"$EMSDK/emcmake.bat\" ]; then\n        echo \"$EMSDK/emcmake.bat\"\n    else\n        echo \"emcmake\"  # fallback, will fail with clear error\n    fi\n}\n\nEMCMAKE_CMD=$(detect_emcmake_command)\necho \"Using emcmake command: $EMCMAKE_CMD\"\n\nCPP_DIR=\"cpp-sdk-test\"\nSDK_TEST_DIR=$(realpath \"../../../../modules/sdk-test-cpp\")\nWASM_PATH=\"$SDK_TEST_DIR/build/lib.wasm\"\nCLI_PATH=$(realpath \"../../../../target/release/spacetimedb-cli\")\nNUM_CORES=16\n\n# Check if rebuild is needed\ncheck_needs_rebuild() {\n    local wasm_file=\"$1\"\n    local source_dir=\"$2\"\n    local lib_dir=\"../..\" # Path to bindings-cpp\n    \n    # If WASM doesn't exist, we need to build\n    if [ ! -f \"$wasm_file\" ]; then\n        return 0  # true - needs rebuild\n    fi\n    \n    # Find the newest source file in sdk-test-cpp\n    local newest_src=$(find \"$source_dir/src\" \"$source_dir/include\" -type f \\( -name \"*.cpp\" -o -name \"*.h\" \\) -printf '%T@\\n' 2>/dev/null | sort -n | tail -1)\n    \n    # Find the newest library file in bindings-cpp\n    local newest_lib=$(find \"$lib_dir/include\" \"$lib_dir/src\" -type f \\( -name \"*.cpp\" -o -name \"*.h\" \\) -printf '%T@\\n' 2>/dev/null | sort -n | tail -1)\n    \n    # Get WASM modification time\n    local wasm_time=$(stat -c %Y \"$wasm_file\" 2>/dev/null || echo 0)\n    \n    # Check if any source is newer than WASM\n    if [ -n \"$newest_src\" ] && [ $(echo \"$newest_src\" | cut -d. -f1) -gt $wasm_time ]; then\n        return 0  # true - needs rebuild\n    fi\n    \n    # Check if any library file is newer than WASM\n    if [ -n \"$newest_lib\" ] && [ $(echo \"$newest_lib\" | cut -d. -f1) -gt $wasm_time ]; then\n        return 0  # true - needs rebuild\n    fi\n    \n    return 1  # false - no rebuild needed\n}\n\necho \"Regenerating C++ SDK client from latest lib.cpp build...\"\necho \"=========================================================\"\n\n# Check if lib.cpp needs rebuilding\nif check_needs_rebuild \"$WASM_PATH\" \"$SDK_TEST_DIR\"; then\n    echo \"Source files changed - rebuilding lib.cpp with $NUM_CORES cores...\"\n    echo \"-----------------------------------------\"\n    cd \"$SDK_TEST_DIR\"\n    \n    # Check if build directory exists, if not configure it\n    if [ ! -d \"build\" ]; then\n        echo \"Configuring build directory...\"\n        $EMCMAKE_CMD cmake -B build -DMODULE_SOURCE=src/lib.cpp -DOUTPUT_NAME=lib\n    fi\n    \n    # Build with parallel compilation\n    echo \"Building lib.wasm...\"\n    cmake --build build -j$NUM_CORES\n    \n    if [ ! -f \"$WASM_PATH\" ]; then\n        echo \"ERROR: Build succeeded but lib.wasm not found at $WASM_PATH\"\n        echo \"Build may have produced a different output file.\"\n        exit 1\n    fi\n    \n    echo \"✅ lib.cpp built successfully!\"\n    echo \"WASM size: $(du -h \"$WASM_PATH\" | cut -f1)\"\n    echo \"\"\n    \n    # Return to the client-comparison directory\n    cd - > /dev/null\nelse\n    echo \"✅ No source changes detected - using existing lib.wasm\"\n    echo \"WASM size: $(du -h \"$WASM_PATH\" | cut -f1)\"\n    echo \"\"\nfi\n\nif [ ! -f \"$CLI_PATH\" ]; then\n    echo \"ERROR: SpacetimeDB CLI not found at $CLI_PATH\"\n    echo \"Please build the CLI first, from the project root:\"\n    echo \"  cargo build --release -p spacetimedb-cli\"\n    exit 1\nfi\n\n# Clear existing C++ client directory\necho \"Clearing existing C++ client directory...\"\nif [ -d \"$CPP_DIR\" ]; then\n    rm -rf \"$CPP_DIR\"/*\n    echo \"Cleared $CPP_DIR/\"\nelse\n    mkdir -p \"$CPP_DIR\"\n    echo \"Created $CPP_DIR/\"\nfi\n\n# Generate new C++ client\necho \"Generating new C++ client from lib.wasm...\"\ncd \"$CPP_DIR\"\n\"$CLI_PATH\" generate --lang rust --out-dir . --bin-path \"$WASM_PATH\" >/dev/null 2>&1\n\nif [ $? -eq 0 ]; then\n    echo \"\"\n    echo \"✅ C++ SDK client regenerated successfully!\"\n    echo \"Files generated: $(find . -name \"*.rs\" | wc -l)\"\n    echo \"\"\n    echo \"Next steps:\"\n    echo \"  1. Run ./compare_clients.sh to generate new comparison\"\n    echo \"  2. Review client_diff_analysis.txt for updated differences\"\nelse\n    echo \"\"\n    echo \"❌ Client generation failed!\"\n    echo \"Check the error messages above for details.\"\n    exit 1\nfi"
  },
  {
    "path": "crates/bindings-cpp/tests/client-comparison/scripts/regenerate_rust_client.sh",
    "content": "#!/bin/bash\n\n# Script to regenerate Rust SDK client from latest sdk-test module\n# This ensures we're always comparing against the most current Rust module\n\nset -e  # Exit on any error\n\nRUST_DIR=\"rust-sdk-test\"\nSDK_TEST_DIR=$(realpath \"../../../../modules/sdk-test\")\nCLI_PATH=$(realpath \"../../../../target/release/spacetimedb-cli\")\n\n# Check if rebuild is needed\ncheck_needs_rebuild() {\n    local source_dir=\"$1\"\n    local target_dir=\"$2\"\n    \n    # If target directory doesn't exist or is empty, we need to build\n    if [ ! -d \"$target_dir\" ] || [ -z \"$(ls -A \"$target_dir\" 2>/dev/null)\" ]; then\n        return 0  # true - needs rebuild\n    fi\n    \n    # Find the newest source file in sdk-test\n    local newest_src=$(find \"$source_dir/src\" -type f \\( -name \"*.rs\" -o -name \"*.toml\" \\) -printf '%T@\\n' 2>/dev/null | sort -n | tail -1)\n    \n    # Find the oldest generated file in rust-sdk-test\n    local oldest_gen=$(find \"$target_dir\" -type f -name \"*.rs\" -printf '%T@\\n' 2>/dev/null | sort -n | head -1)\n    \n    # Check if any source is newer than generated files\n    if [ -n \"$newest_src\" ] && [ -n \"$oldest_gen\" ] && [ $(echo \"$newest_src\" | cut -d. -f1) -gt $(echo \"$oldest_gen\" | cut -d. -f1) ]; then\n        return 0  # true - needs rebuild\n    fi\n    \n    return 1  # false - no rebuild needed\n}\n\necho \"Regenerating Rust SDK client from latest sdk-test module...\"\necho \"==========================================================\"\n\n# Check if sdk-test needs rebuilding\nif check_needs_rebuild \"$SDK_TEST_DIR\" \"$RUST_DIR\"; then\n    echo \"Source files changed - regenerating Rust client...\"\n    echo \"--------------------------------------------------\"\nelse\n    echo \"✅ No source changes detected - using existing Rust client\"\n    if [ -d \"$RUST_DIR\" ]; then\n        echo \"Files present: $(find \"$RUST_DIR\" -name \"*.rs\" | wc -l)\"\n    fi\n    echo \"\"\n    exit 0\nfi\n\nif [ ! -f \"$CLI_PATH\" ]; then\n    echo \"ERROR: SpacetimeDB CLI not found at $CLI_PATH\"\n    echo \"Please build the CLI first, from the project root:\"\n    echo \"  cargo build --release -p spacetimedb-cli\"\n    exit 1\nfi\n\nif [ ! -d \"$SDK_TEST_DIR\" ]; then\n    echo \"ERROR: SDK test module not found at $SDK_TEST_DIR\"\n    echo \"Please ensure the sdk-test module exists.\"\n    exit 1\nfi\n\n# Clear existing Rust client directory\necho \"Clearing existing Rust client directory...\"\nif [ -d \"$RUST_DIR\" ]; then\n    rm -rf \"$RUST_DIR\"/*\n    echo \"Cleared $RUST_DIR/\"\nelse\n    mkdir -p \"$RUST_DIR\"\n    echo \"Created $RUST_DIR/\"\nfi\n\n# Generate new Rust client\necho \"Generating new Rust client from sdk-test module...\"\ncd \"$RUST_DIR\"\n\"$CLI_PATH\" generate --lang rust --out-dir . --module-path \"$SDK_TEST_DIR\" >/dev/null 2>&1\n\nif [ $? -eq 0 ]; then\n    echo \"\"\n    echo \"✅ Rust SDK client regenerated successfully!\"\n    echo \"Files generated: $(find . -name \"*.rs\" | wc -l)\"\n    echo \"\"\n    echo \"Next steps:\"\n    echo \"  1. Run ./compare_clients.sh to generate new comparison\"\n    echo \"  2. Review client_diff_analysis.txt for updated differences\"\nelse\n    echo \"\"\n    echo \"❌ Client generation failed!\"\n    echo \"Check the error messages above for details.\"\n    exit 1\nfi"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/.gitignore",
    "content": "# Build directories\nlibrary_build/\ntest_modules/build_*/\n\n# Test output files\ntest_log.txt\ntest_summary_live.txt\ntest_summary.txt\n\n# Results directory\nresults/"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/CMakeLists.module.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(module)\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# Module source file\nif(NOT DEFINED MODULE_SOURCE)\n    message(FATAL_ERROR \"MODULE_SOURCE must be defined\")\nendif()\n\n# Output name\nif(NOT DEFINED OUTPUT_NAME)\n    set(OUTPUT_NAME \"module\")\nendif()\n\n# Library directory must be provided\nif(NOT DEFINED SPACETIMEDB_LIBRARY_DIR)\n    message(FATAL_ERROR \"SPACETIMEDB_LIBRARY_DIR must be defined\")\nendif()\n\nif(NOT DEFINED SPACETIMEDB_INCLUDE_DIR)\n    message(FATAL_ERROR \"SPACETIMEDB_INCLUDE_DIR must be defined\")\nendif()\n\n# Create the module executable\nadd_executable(${OUTPUT_NAME} ${MODULE_SOURCE})\n\n# Include directories\ntarget_include_directories(${OUTPUT_NAME} PRIVATE ${SPACETIMEDB_INCLUDE_DIR})\n\n# Link the pre-built library\ntarget_link_directories(${OUTPUT_NAME} PRIVATE ${SPACETIMEDB_LIBRARY_DIR})\ntarget_link_libraries(${OUTPUT_NAME} PRIVATE spacetimedb_cpp_library)\n\n# Emscripten specific settings\nif(CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")\n  # Export list as a single variable (no extra quoting per-platform)\n  set(EXPORTED_FUNCS \"['_malloc','_free','___describe_module__','___call_reducer__']\")\n\n  # Produce a standalone .wasm (no JS harness) with no entry point\n  target_link_options(${OUTPUT_NAME} PRIVATE\n    \"SHELL:-sSTANDALONE_WASM=1\"\n    \"SHELL:-sWASM=1\"\n    \"SHELL:--no-entry\"\n    # Exports\n    \"SHELL:-sEXPORTED_FUNCTIONS=${EXPORTED_FUNCS}\"\n    # Keep this ON for correctness; you can flip to 0 temporarily to probe\n    \"SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=1\"\n    # Trim runtime\n    \"SHELL:-sFILESYSTEM=0\"\n    \"SHELL:-sDISABLE_EXCEPTION_CATCHING=1\"\n    \"SHELL:-sALLOW_MEMORY_GROWTH=0\"\n    \"SHELL:-sINITIAL_MEMORY=16MB\"\n    \"SHELL:-sSUPPORT_LONGJMP=0\"\n    \"SHELL:-sSUPPORT_ERRNO=0\"\n    # C++20 and O2 at link step (Emscripten accepts them here too)\n    \"SHELL:-std=c++20\"\n    \"SHELL:-O2\"\n  )\n\n  # Name the output lib.wasm\n  set_target_properties(${OUTPUT_NAME} PROPERTIES OUTPUT_NAME \"lib\" SUFFIX \".wasm\")\nendif()"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(type-isolation-tests)\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# Build the SpacetimeDB library once\nset(SPACETIMEDB_CPP_LIBRARY_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/../..\")\n\n# Build the library as a static library\nadd_subdirectory(${SPACETIMEDB_CPP_LIBRARY_PATH} ${CMAKE_CURRENT_BINARY_DIR}/spacetimedb_lib)\n\n# Export the library path for modules to use\nset(SPACETIMEDB_LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/spacetimedb_lib CACHE PATH \"Path to built SpacetimeDB library\")\nset(SPACETIMEDB_INCLUDE_DIR ${SPACETIMEDB_CPP_LIBRARY_PATH}/include CACHE PATH \"Path to SpacetimeDB includes\")"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/CMakeLists_module.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(module)\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n# Module source file\nif(NOT DEFINED MODULE_SOURCE)\n    message(FATAL_ERROR \"MODULE_SOURCE must be defined\")\nendif()\n\n# Output name\nif(NOT DEFINED OUTPUT_NAME)\n    set(OUTPUT_NAME \"module\")\nendif()\n\n# Library directory must be provided\nif(NOT DEFINED SPACETIMEDB_LIBRARY_DIR)\n    message(FATAL_ERROR \"SPACETIMEDB_LIBRARY_DIR must be defined\")\nendif()\n\nif(NOT DEFINED SPACETIMEDB_INCLUDE_DIR)\n    message(FATAL_ERROR \"SPACETIMEDB_INCLUDE_DIR must be defined\")\nendif()\n\n# Create the module executable\nadd_executable(${OUTPUT_NAME} ${MODULE_SOURCE})\n\n# Include directories\ntarget_include_directories(${OUTPUT_NAME} PRIVATE ${SPACETIMEDB_INCLUDE_DIR})\n\n# Link the pre-built library\ntarget_link_directories(${OUTPUT_NAME} PRIVATE ${SPACETIMEDB_LIBRARY_DIR})\ntarget_link_libraries(${OUTPUT_NAME} PRIVATE spacetimedb_cpp_library)\n\n# Emscripten specific settings\nif(CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")\n  # Export list as a single variable (no extra quoting per-platform)\n  set(EXPORTED_FUNCS \"['_malloc','_free','___describe_module__','___call_reducer__']\")\n\n  # Produce a standalone .wasm (no JS harness) with no entry point\n  target_link_options(lib PRIVATE\n    \"SHELL:-sSTANDALONE_WASM=1\"\n    \"SHELL:-sWASM=1\"\n    \"SHELL:--no-entry\"\n    # Exports\n    \"SHELL:-sEXPORTED_FUNCTIONS=${EXPORTED_FUNCS}\"\n    # Keep this ON for correctness; you can flip to 0 temporarily to probe\n    \"SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=1\"\n    # Trim runtime\n    \"SHELL:-sFILESYSTEM=0\"\n    \"SHELL:-sDISABLE_EXCEPTION_CATCHING=1\"\n    \"SHELL:-sALLOW_MEMORY_GROWTH=0\"\n    \"SHELL:-sINITIAL_MEMORY=16MB\"\n    \"SHELL:-sSUPPORT_LONGJMP=0\"\n    \"SHELL:-sSUPPORT_ERRNO=0\"\n    # C++20 and O2 at link step (Emscripten accepts them here too)\n    \"SHELL:-std=c++20\"\n    \"SHELL:-O2\"\n  )\n\n  # Name the output lib.wasm\n  set_target_properties(lib PROPERTIES OUTPUT_NAME \"lib\" SUFFIX \".wasm\")\nendif()"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/README.md",
    "content": "# SpacetimeDB C++ Type Isolation Test Suite\n\nComprehensive test framework for systematically testing individual C++ types with the SpacetimeDB C++ library. Features parallel builds, real-time progress monitoring, and automated constraint validation testing.\n\n## Quick Start\n\n```bash\n# Run default V10 regression checks (16-way parallel build)\n./run_type_isolation_test.sh\n\n# Run with custom parallelism\n./run_type_isolation_test.sh 8\n\n# Explicit V10 regression mode (same as default)\n./run_type_isolation_test.sh --v10-regression\n\n# Run broader legacy/full suite (V9 check path)\n./run_type_isolation_test.sh --v9\n\n# Monitor progress in real-time (separate terminal)\nwatch -n 1 cat test_summary_live.txt\n```\n\n## Features\n\n- **Parallel Execution**: Configurable parallelism (default: 16 concurrent builds)\n- **Live Progress Monitoring**: Real-time status table with build/publish progress\n- **Optimized Builds**: Pre-builds library once, 2-3x faster than individual builds\n- **Smart Error Capture**: Clean error messages (up to 250 characters) in status table\n- **Auto-discovery**: Automatically tests all `.cpp` files in `test_modules/`\n- **State Recovery**: Resumes from previous state if interrupted\n\n## Test Categories\n\n### Basic Types (module01-12)\n- **Primitives**: U8-U256, I8-I256, F32/F64, Bool, String\n- **Special Types**: Identity, Timestamp, ConnectionId\n- **Complex Types**: Enums, structs, vectors, optionals\n- **Constraints**: Unique constraints and indexes\n\n### Constraint Validation (error_*)\nTests that **intentionally fail** to validate error detection:\n- `error_autoinc_non_integer`: Compile-time error for AutoIncrement on non-integers\n- `error_invalid_index`: Compile-time error for Index on non-filterable types  \n- `error_multiple_pk`: Runtime error for multiple primary keys\n- `error_non_spacetimedb_type`: Compile-time error for unsupported types\n- `error_circular_ref`: Runtime error for circular type references\n- `error_scheduled_id_pk`: Runtime error for invalid scheduled tables\n\n### Debug & Edge Cases (debug_*, test_*)\n- Type combinations and interactions\n- Reducer and table scenarios\n- Specific debugging modules\n\n## Output\n\n- **`test_summary_live.txt`**: Live status table\n- **`test_log.txt`**: Detailed build/publish log with timestamps\n- **`test_modules/build_*/`**: Individual build directories\n- **`library_build/`**: Pre-built shared library\n\n## Status Table Format\n\n```\nModule                    | Build | Publish | WASM Size | Error\n--------------------------|-------|---------|-----------|------------------\ndebug_large_struct        | ✅    | ✅      | 299KB     | \nerror_multiple_pk         | ✅    | ❌      | 306KB     | Multiple primary keys\nmodule01_basic_unsigned   | 🔨    | ⏳      | -         | \n```\n\n**Status Indicators:** ⏳ Pending, 🔨 Building, 📤 Publishing, ✅ Passed, ❌ Failed, ⏭️ Skipped\n\n## Performance\n\n- **Total time**: ~1 minute for 57 modules\n- **Library pre-build**: ~10 seconds  \n- **Module builds**: 5-13 seconds each (vs 20-30 without optimization)\n- **Current success rate**: 89% (51/57 passing, 6 intentional failures)\n\n## Requirements\n\n- SpacetimeDB CLI and server\n- Emscripten (emcc, emcmake)\n- CMake 3.16+, C++20 compiler\n- Bash 4.0+ with associative arrays\n\n## Usage\n\n**Standard workflow:**\n```bash\ncd crates/bindings-cpp/tests/type-isolation-test\n./run_type_isolation_test.sh\n```\n\n**Monitor progress:**\n```bash\nwatch -n 1 cat test_summary_live.txt\n```\n\n**Check detailed errors:**\n```bash\ngrep \"error\" test_log.txt\n```\n\n## Troubleshooting\n\n**Server issues:**\n```bash\ncurl -s http://127.0.0.1:3000/health  # Check server\nspacetime start                        # Start if needed\n```\n\n**Build failures:**\n- Check error messages in live table (250 char limit)\n- Review `test_log.txt` for full details\n- Build artifacts in `test_modules/build_<module>/`\n\n**Stuck updates:**\n```bash\npkill -f update_table_from_log  # Kill orphaned processes\n./run_type_isolation_test.sh    # Restart\n```\n\n## Adding Tests\n\n1. Create `.cpp` file in `test_modules/` following naming: `test_<category>_<specific>.cpp`\n2. Include minimal code isolating the type being tested\n3. Run test suite to verify\n\n**Basic template:**\n```cpp\n#include <spacetimedb.h>\nusing namespace SpacetimeDB;\n\nstruct TestTable { /* fields */ };\nSPACETIMEDB_STRUCT(TestTable, /* field list */)\nSPACETIMEDB_TABLE(TestTable, test_table, Public)\n\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    LOG_INFO(\"Test module initialized\");\n    return Ok();\n}\n```\n\n## Integration\n\n**CI/CD example:**\n```bash\n./run_type_isolation_test.sh\nSUCCESS_RATE=$(grep \"Success rate:\" test_summary_live.txt | grep -o '[0-9]*%')\n[[ \"${SUCCESS_RATE}\" == \"89%\" ]] || exit 1  # Expected rate with intentional failures\n```\n\nThe test suite validates the C++ bindings type system by testing individual types in isolation, ensuring reliable constraint validation and error detection.\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/run_type_isolation_test.sh",
    "content": "#!/bin/bash\n\n# Main test runner with live table updates\n# Uses test_log.txt for events and test_summary_live.txt for the table\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nTMP_DIR=\"$SCRIPT_DIR/tmp\"\ncd \"$SCRIPT_DIR\"\n\n# Detect the correct emcmake command for cross-platform compatibility\ndetect_emcmake_command() {\n    if command -v emcmake >/dev/null 2>&1; then\n        echo \"emcmake\"\n    elif command -v emcmake.bat >/dev/null 2>&1; then\n        echo \"emcmake.bat\"\n    elif [ -n \"$EMSDK\" ] && [ -f \"$EMSDK/emcmake.bat\" ]; then\n        echo \"$EMSDK/emcmake.bat\"\n    else\n        echo \"emcmake\"  # fallback, will fail with clear error\n    fi\n}\n\nEMCMAKE_CMD=$(detect_emcmake_command)\necho \"Using emcmake command: $EMCMAKE_CMD\"\n\n# Parse arguments\nMAX_PARALLEL=16\nTEST_MODE=\"v10-regression\"\nfor arg in \"$@\"; do\n    case \"$arg\" in\n        --v10-regression)\n            TEST_MODE=\"v10-regression\"\n            ;;\n        --v9)\n            TEST_MODE=\"v9\"\n            ;;\n        ''|*[!0-9]*)\n            ;;\n        *)\n            MAX_PARALLEL=\"$arg\"\n            ;;\n    esac\ndone\n\n# Clear previous state\necho \"Starting fresh test run...\"\nrm -f test_log.txt test_summary_live.txt\ntouch test_log.txt\n\n# Ensure spacetime is running\ncheck_server_running() {\n    curl -s http://127.0.0.1:3000/health >/dev/null 2>&1\n}\n\nif ! check_server_running; then\n    echo \"Starting SpacetimeDB server...\"\n    nohup spacetime start > \"$TMP_DIR/spacetime.log\" 2>&1 &\n    \n    echo \"Waiting for server to start...\"\n    for i in {1..30}; do\n        if check_server_running; then\n            echo \"Server started successfully!\"\n            break\n        fi\n        sleep 1\n        if [ $i -eq 30 ]; then\n            echo \"Error: Server failed to start after 30 seconds\"\n            exit 1\n        fi\n    done\nelse\n    echo \"SpacetimeDB server already running\"\nfi\n\n# Discover modules\ndeclare -a MODULES\nfor cpp_file in test_modules/*.cpp; do\n    if [ -f \"$cpp_file\" ]; then\n        module_name=$(basename \"${cpp_file%.cpp}\")\n        MODULES+=(\"$module_name\")\n    fi\ndone\n\n# Optional focused regression selection for V10 behavior checks\nif [ \"$TEST_MODE\" = \"v10-regression\" ]; then\n    declare -a FILTERED_MODULES=()\n    for module in \"${MODULES[@]}\"; do\n        case \"$module\" in\n            test_multicolumn_index_valid|error_multicolumn_missing_field|error_default_missing_field|error_circular_ref)\n                FILTERED_MODULES+=(\"$module\")\n                ;;\n        esac\n    done\n    MODULES=(\"${FILTERED_MODULES[@]}\")\nfi\n\n# Sort modules\nIFS=$'\\n' MODULES=($(sort <<<\"${MODULES[*]}\"))\nunset IFS\n\necho \"=========================================\"\necho \"Testing ${#MODULES[@]} modules\"\necho \"Mode: ${TEST_MODE}\"\necho \"Parallelism: ${MAX_PARALLEL} (use ./run_type_isolation_test.sh <num> to change)\"\necho \"Use --v9 to run the broader legacy/full module suite.\"\necho \"=========================================\"\necho \"Monitor table with: watch -n 1 cat test_summary_live.txt\"\necho \"Monitor log with: tail -f test_log.txt\"\necho \"=========================================\"\n\n# Create results directory\nmkdir -p results\nmkdir -p \"$TMP_DIR\"\n\n# Function to write log entry (always write to script dir)\nwrite_log() {\n    echo \"$(date +%s)|$1|$2|$3\" >> \"$SCRIPT_DIR/test_log.txt\"\n}\n\n# Start the table updater in background\necho \"Starting table updater...\"\n./update_table_from_log.sh &\nTABLE_UPDATER_PID=$!\n\n# Array to track build job PIDs\ndeclare -a BUILD_PIDS=()\n\n# Give it a second to start\nsleep 1\n\n# Pre-build the SpacetimeDB library once\nNUM_CORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)\necho \"=========================================\"\necho \"Pre-building SpacetimeDB library (using $NUM_CORES cores)...\"\necho \"=========================================\"\n\nwrite_log \"LIBRARY\" \"build\" \"start\"\n\nLIBRARY_BUILD_DIR=\"library_build\"\nrm -rf \"$LIBRARY_BUILD_DIR\"\nmkdir -p \"$LIBRARY_BUILD_DIR\"\ncd \"$LIBRARY_BUILD_DIR\"\n\nLIBRARY_ERROR_FILE=\"$TMP_DIR/library_build_error.txt\"\nif ! $EMCMAKE_CMD cmake ../../../ > \"$LIBRARY_ERROR_FILE\" 2>&1; then\n    write_log \"LIBRARY\" \"build\" \"fail\"\n    ERROR_MSG=$(grep -E \"(error:|Error:|ERROR:)\" \"$LIBRARY_ERROR_FILE\" 2>/dev/null | head -5 | tr '\\n' ' ')\n    if [ -z \"$ERROR_MSG\" ]; then\n        ERROR_MSG=$(tail -20 \"$LIBRARY_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n    fi\n    ERROR_MSG=\"${ERROR_MSG:0:500}\"\n    write_log \"LIBRARY\" \"error\" \"${ERROR_MSG:-Failed to configure library build}\"\n    echo \"❌ Failed to configure library build\"\n    echo \"Error details:\"\n    tail -30 \"$LIBRARY_ERROR_FILE\"\n    echo \"=========================================\"\n    echo \"Library build failed. Updating table with error details...\"\n    # Force table updater to process the error\n    sleep 3  # Give more time for table updater to process\n    # Send SIGUSR1 to force immediate update (if the script supports it)\n    kill -USR1 $TABLE_UPDATER_PID 2>/dev/null || true\n    sleep 1  # Wait for final update\n    echo \"Check test_summary_live.txt for error details.\"\n    kill $TABLE_UPDATER_PID 2>/dev/null\n    exit 1\nfi\n\n# Use all available cores for parallel compilation (already set above)\nif ! cmake --build . -j$NUM_CORES > \"$LIBRARY_ERROR_FILE\" 2>&1; then\n    # Extract more comprehensive error messages FIRST\n    ERROR_MSG=$(grep -E \"(error:|Error:|ERROR:|undefined reference|fatal error)\" \"$LIBRARY_ERROR_FILE\" 2>/dev/null | head -10 | tr '\\n' ' ')\n    if [ -z \"$ERROR_MSG\" ]; then\n        ERROR_MSG=$(tail -50 \"$LIBRARY_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n    fi\n    ERROR_MSG=\"${ERROR_MSG:0:800}\"  # Allow more space for library errors\n    \n    # Write to log BEFORE any output\n    write_log \"LIBRARY\" \"build\" \"fail\"\n    write_log \"LIBRARY\" \"error\" \"${ERROR_MSG:-Failed to build library}\"\n    \n    # Force flush the log file\n    sync\n    \n    echo \"❌ Failed to build library\"\n    echo \"Error details:\"\n    tail -50 \"$LIBRARY_ERROR_FILE\"\n    echo \"=========================================\"\n    echo \"Library build failed. Updating table with error details...\"\n    \n    # Give table updater more time to read the log and update\n    sleep 4  # Increased sleep time\n    \n    echo \"Check test_summary_live.txt for error details.\"\n    \n    # Gracefully terminate table updater\n    kill -TERM $TABLE_UPDATER_PID 2>/dev/null\n    wait $TABLE_UPDATER_PID 2>/dev/null\n    \n    exit 1\nfi\nrm -f \"$LIBRARY_ERROR_FILE\"\n\ncd ..\nwrite_log \"LIBRARY\" \"build\" \"pass\"\necho \"✅ Library built successfully\"\n\n# Create the expected directory structure and copy the library\nmkdir -p \"$LIBRARY_BUILD_DIR/spacetimedb_lib\"\ncp \"$LIBRARY_BUILD_DIR/libspacetimedb_cpp_library.a\" \"$LIBRARY_BUILD_DIR/spacetimedb_lib/\"\n\necho \"=========================================\"\n\n# Export library paths for module builds\nexport SPACETIMEDB_LIBRARY_DIR=\"$(pwd)/$LIBRARY_BUILD_DIR/spacetimedb_lib\"\nexport SPACETIMEDB_INCLUDE_DIR=\"$(pwd)/../../include\"\n\nget_expected_failure_marker() {\n    local module=$1\n    case \"$module\" in\n        error_multicolumn_missing_field)\n            echo \"ERROR_CONSTRAINT_REGISTRATION_FIELD_NOT_FOUND\"\n            ;;\n        error_default_missing_field)\n            echo \"ERROR_CONSTRAINT_REGISTRATION_FIELD_NOT_FOUND\"\n            ;;\n        error_circular_ref)\n            echo \"ERROR_CIRCULAR_REFERENCE_\"\n            ;;\n        *)\n            echo \"\"\n            ;;\n    esac\n}\n\n# Function to publish module in background\npublish_module() {\n    local module=$1\n    local wasm=\"test_modules/build_${module}/lib.wasm\"\n    \n    if [ ! -f \"$wasm\" ]; then\n        write_log \"$module\" \"publish\" \"skip\"\n        return\n    fi\n    \n    # Get size\n    local size=$(stat -c%s \"$wasm\" 2>/dev/null || echo \"0\")\n    local size_kb=$((size / 1024))\n    write_log \"$module\" \"size\" \"${size_kb}KB\"\n    \n    # Update to publishing\n    write_log \"$module\" \"publish\" \"start\"\n    \n    local db_name=$(echo \"testmod-${module}\" | sed 's/_/-/g')\n    echo \"  📤 Publishing $module as $db_name...\"\n    local PUBLISH_ERROR_FILE=\"$TMP_DIR/publish_error_${module}.txt\"\n    timeout 60 spacetime publish --bin-path \"$wasm\" -c \"$db_name\" -y >\"$PUBLISH_ERROR_FILE\" 2>&1\n    local publish_exit=$?\n\n    local expected_marker\n    expected_marker=$(get_expected_failure_marker \"$module\")\n\n    local has_publish_error=0\n    if grep -q \"Error: Errors occurred:\" \"$PUBLISH_ERROR_FILE\" 2>/dev/null || \\\n       grep -q \"HTTP status server error\" \"$PUBLISH_ERROR_FILE\" 2>/dev/null || \\\n       grep -q \"invalid ref:\" \"$PUBLISH_ERROR_FILE\" 2>/dev/null; then\n        has_publish_error=1\n    fi\n\n    local publish_success=0\n    if [ $publish_exit -eq 0 ] && [ $has_publish_error -eq 0 ]; then\n        publish_success=1\n    fi\n\n    if [ -n \"$expected_marker\" ]; then\n        if [ $publish_success -eq 0 ] && grep -q \"$expected_marker\" \"$PUBLISH_ERROR_FILE\" 2>/dev/null; then\n            write_log \"$module\" \"publish\" \"pass\"\n            write_log \"$module\" \"error\" \"Expected publish failure validated: $expected_marker\"\n            echo \"  ✅ Expected publish failure validated: $module ($expected_marker)\"\n            rm -f \"$PUBLISH_ERROR_FILE\"\n            return\n        fi\n\n        write_log \"$module\" \"publish\" \"fail\"\n        local EXPECTED_MSG=\"Expected publish failure with marker '$expected_marker'\"\n        ERROR_MSG=$(sed -n '/Error:/,+10p' \"$PUBLISH_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n        if [ -z \"$ERROR_MSG\" ]; then\n            ERROR_MSG=$(tail -n 15 \"$PUBLISH_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n        fi\n        ERROR_MSG=\"$EXPECTED_MSG | Actual: ${ERROR_MSG:0:400}\"\n        write_log \"$module\" \"error\" \"$ERROR_MSG\"\n        echo \"  ❌ Expected failure marker missing for $module\"\n        rm -f \"$PUBLISH_ERROR_FILE\"\n        return\n    fi\n\n    if [ $publish_success -eq 1 ]; then\n        write_log \"$module\" \"publish\" \"pass\"\n        echo \"  ✅ Published $module\"\n        rm -f \"$PUBLISH_ERROR_FILE\"\n    else\n        write_log \"$module\" \"publish\" \"fail\"\n        # Capture more comprehensive error message\n        # Get the full error starting from \"Error:\" line and including several lines after\n        ERROR_MSG=$(sed -n '/Error:/,+10p' \"$PUBLISH_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n        if [ -z \"$ERROR_MSG\" ]; then\n            # Try getting the last 15 lines which usually contain the actual error\n            ERROR_MSG=$(tail -n 15 \"$PUBLISH_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n        fi\n        if [ -z \"$ERROR_MSG\" ]; then\n            # Last resort - get the whole file if it's small\n            ERROR_MSG=$(cat \"$PUBLISH_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ')\n        fi\n        # Truncate to 500 chars (much more than before)\n        ERROR_MSG=\"${ERROR_MSG:0:500}\"\n        write_log \"$module\" \"error\" \"${ERROR_MSG:-Publish failed}\"\n        echo \"  ❌ Publish failed: $module\"\n        rm -f \"$PUBLISH_ERROR_FILE\"\n    fi\n}\n\n# Function to build a module\nbuild_module() {\n    local module=$1\n    local count=$2\n    local total=$3\n    \n    echo \"[$count/$total] Building $module...\"\n    \n    # Log build start\n    write_log \"$module\" \"build\" \"start\"\n    \n    # Create build directory under test_modules\n    BUILD_DIR=\"test_modules/build_${module}\"\n    rm -rf \"$BUILD_DIR\"\n    mkdir -p \"$BUILD_DIR\"\n    \n    cd \"$BUILD_DIR\"\n    \n    # Try to build - use the module-specific CMake with pre-built library\n    BUILD_ERROR_FILE=\"$TMP_DIR/build_error_${module}.txt\"\n    \n    # Copy the module CMakeLists\n    cp ../../CMakeLists.module.txt CMakeLists.txt\n    \n    if $EMCMAKE_CMD cmake . \\\n        -DMODULE_SOURCE=\"../../test_modules/${module}.cpp\" \\\n        -DOUTPUT_NAME=\"${module}\" \\\n        -DSPACETIMEDB_LIBRARY_DIR=\"$SPACETIMEDB_LIBRARY_DIR\" \\\n        -DSPACETIMEDB_INCLUDE_DIR=\"$SPACETIMEDB_INCLUDE_DIR\" > /dev/null 2>\"$BUILD_ERROR_FILE\"; then\n        \n        if cmake --build . > /dev/null 2>\"$BUILD_ERROR_FILE\"; then\n            cd ../..\n            write_log \"$module\" \"build\" \"pass\"\n            echo \"  ✅ Build successful: $module\"\n            rm -f \"$BUILD_ERROR_FILE\"\n            # Start publish in background\n            publish_module \"$module\" &\n        else\n            cd ../..\n            write_log \"$module\" \"build\" \"fail\"\n            # Capture first 1000 chars of error\n            ERROR_MSG=$(head -n 10 \"$BUILD_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ' | cut -c1-1000)\n            write_log \"$module\" \"error\" \"${ERROR_MSG:-Build failed}\"\n            echo \"  ❌ Build failed: $module\"\n            rm -f \"$BUILD_ERROR_FILE\"\n        fi\n    else\n        cd ../..\n        write_log \"$module\" \"build\" \"fail\"\n        # Capture first 1000 chars of error\n        ERROR_MSG=$(head -n 10 \"$BUILD_ERROR_FILE\" 2>/dev/null | tr '\\n' ' ' | cut -c1-1000)\n        write_log \"$module\" \"error\" \"${ERROR_MSG:-CMake failed}\"\n        echo \"  ❌ CMake configuration failed: $module\"\n        rm -f \"$BUILD_ERROR_FILE\"\n    fi\n}\n\n# Parallel build management with configurable parallelism\necho \"Building modules with parallelism of $MAX_PARALLEL...\"\n\n# Build modules maintaining constant parallelism\nCOUNT=0\nTOTAL=${#MODULES[@]}\nfor module in \"${MODULES[@]}\"; do\n    ((COUNT++))\n    \n    # Wait if we have max parallel jobs running\n    while (( $(jobs -r | wc -l) >= MAX_PARALLEL )); do\n        sleep 0.2\n    done\n    \n    # Start new build job\n    build_module \"$module\" \"$COUNT\" \"$TOTAL\" &\n    BUILD_PIDS+=($!)\n    \n    # Small delay to avoid race conditions\n    sleep 0.05\ndone\n\n# Wait for all remaining builds to complete (excluding table updater)\nfor pid in \"${BUILD_PIDS[@]}\"; do\n    wait $pid 2>/dev/null\ndone\n\necho \"\"\necho \"Waiting for any remaining background jobs...\"\n# Give a moment for any publish jobs to complete\nsleep 2\n\n# Check if there are any publish jobs still running (excluding table updater)\nfor i in {1..10}; do\n    # Count background jobs excluding the table updater\n    JOB_COUNT=$(jobs -p | grep -v \"^$TABLE_UPDATER_PID$\" | wc -l)\n    if [ $JOB_COUNT -eq 0 ]; then\n        break\n    fi\n    sleep 1\ndone\n\n# Signal completion\nwrite_log \"COMPLETE\" \"COMPLETE\" \"COMPLETE\"\n\n# Wait a bit for table updater to finish\nsleep 2\n\n# Kill table updater\nkill $TABLE_UPDATER_PID 2>/dev/null\n\necho\necho \"=========================================\"\necho \"Test Complete!\"\necho \"=========================================\"\necho \"Final table in: test_summary_live.txt\"\necho \"Log file: test_log.txt\"\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_constraint_simple.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Simple test with just one primary key constraint\nstruct SimpleConstraintTest {\n    uint32_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(SimpleConstraintTest, id, data)\nSPACETIMEDB_TABLE(SimpleConstraintTest, simple_constraint_test, SpacetimeDB::Public)\nFIELD_PrimaryKey(simple_constraint_test, id);\n\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    LOG_INFO(\"Simple constraint test initialized\");\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_simple_constraint, SpacetimeDB::ReducerContext ctx) {\n    LOG_INFO(\"Testing simple constraint\");\n    SimpleConstraintTest test{1, \"Test data\"};\n    ctx.db[simple_constraint_test].insert(test);\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_large_struct.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// ISOLATION TEST: Large struct causing client codegen issues\n// Test if EveryPrimitiveStruct alone causes the \"non-special product or sum type\" error\n\n// Recreate the problematic large struct\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// Simple table using the large struct\nstruct TestTable { \n    EveryPrimitiveStruct s; \n    int32_t id;\n};\nSPACETIMEDB_STRUCT(TestTable, s, id)\nSPACETIMEDB_TABLE(TestTable, test_table, Public)\n\n// Simple reducer without problematic parameters\nSPACETIMEDB_REDUCER(test_basic, ReducerContext ctx)\n{\n    LOG_INFO(\"Basic reducer called\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_minimal_fail.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n\n// The problematic pattern: SimpleEnum used in multiple contexts using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// Direct table using SimpleEnum\nstruct DirectTable { SimpleEnum e; };\nSPACETIMEDB_STRUCT(DirectTable, e)\nSPACETIMEDB_TABLE(DirectTable, direct_table, Public)\n\n// NO reducers - just the table registration conflict"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_optional_large_struct.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// ISOLATION TEST: std::optional<LargeStruct> causing client codegen issues\n// Testing if optional wrapper around large struct causes the error\n\n// The large struct (we know this alone works)\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// TEST: Optional wrapper around large struct - does this cause client codegen error?\nstruct OptionLargeStruct { \n    std::optional<EveryPrimitiveStruct> s; \n};\nSPACETIMEDB_STRUCT(OptionLargeStruct, s)\nSPACETIMEDB_TABLE(OptionLargeStruct, option_large_struct, Public)\n\n// Simple reducer without problematic parameters\nSPACETIMEDB_REDUCER(test_basic, ReducerContext ctx)\n{\n    LOG_INFO(\"Basic reducer called\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_simple_enum.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n\n// Simple enum using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// Complex enum using new unified syntax\nSPACETIMEDB_ENUM(TestEnum,\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\n// Table that uses SimpleEnum directly\nstruct SimpleEnumTable { \n    SimpleEnum e; \n    int32_t id;\n};\nSPACETIMEDB_STRUCT(SimpleEnumTable, e, id)\nSPACETIMEDB_TABLE(SimpleEnumTable, simple_enum_table, Public)\n\n// Table that uses TestEnum (containing vector<SimpleEnum>)\nstruct TestEnumTable { \n    TestEnum te;\n    int32_t id;\n};\nSPACETIMEDB_STRUCT(TestEnumTable, te, id)\nSPACETIMEDB_TABLE(TestEnumTable, test_enum_table, Public)\n\n// Reducer that uses SimpleEnum as parameter - this might be the trigger\nSPACETIMEDB_REDUCER(insert_enum, ReducerContext ctx, SimpleEnum e, int32_t id)\n{\n    ctx.db.table<SimpleEnumTable>(\"simple_enum_table\").insert(SimpleEnumTable{e, id});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Minimal test for debugging special type vectors\n\n\n// Single table with vector of Identity\nstruct DebugIdentityVec { \n    std::vector<Identity> ids; \n};\nSPACETIMEDB_STRUCT(DebugIdentityVec, ids)\nSPACETIMEDB_TABLE(DebugIdentityVec, debug_identity_vec, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special_constraints.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// ISOLATION TEST: Test constraints on special types\n// Based on working test_special_minimal, add constraints to see if they cause WASM traps\n\n// Working baseline: Simple special type table\nstruct TestIdentity { Identity i; };\nSPACETIMEDB_STRUCT(TestIdentity, i)\nSPACETIMEDB_TABLE(TestIdentity, test_identity, Public)\n\n// TEST: Add Unique constraint on Identity - does this cause WASM trap?\nstruct UniqueIdentity { Identity i; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueIdentity, i, data)\nSPACETIMEDB_TABLE(UniqueIdentity, unique_identity, Public)\nFIELD_Unique(unique_identity, i)\n\n// TEST: Add PrimaryKey constraint on Identity - does this cause WASM trap?\nstruct PkIdentity { Identity i; int32_t data; };\nSPACETIMEDB_STRUCT(PkIdentity, i, data)  \nSPACETIMEDB_TABLE(PkIdentity, pk_identity, Public)\nFIELD_PrimaryKey(pk_identity, i)\n\n// Simple reducer without special type parameters\nSPACETIMEDB_REDUCER(test_basic, ReducerContext ctx)\n{\n    LOG_INFO(\"Basic reducer called\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special_reducers.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// ISOLATION TEST: Test direct special type reducer parameters\n// Based on working debug_special_constraints, add direct special type parameters\n\n// Working baseline: Simple special type tables (we know these work)\nstruct TestIdentity { Identity i; };\nSPACETIMEDB_STRUCT(TestIdentity, i)\nSPACETIMEDB_TABLE(TestIdentity, test_identity, Public)\n\nstruct TestConnectionId { ConnectionId c; };\nSPACETIMEDB_STRUCT(TestConnectionId, c)  \nSPACETIMEDB_TABLE(TestConnectionId, test_connection_id, Public)\n\n// Parameter wrappers to avoid direct special type parameters (causes WASM traps)\nstruct IdentityParam { Identity i; };\nSPACETIMEDB_STRUCT(IdentityParam, i)\n\nstruct ConnectionIdParam { ConnectionId c; };\nSPACETIMEDB_STRUCT(ConnectionIdParam, c)\n\n// TEST: Use wrapped special type parameters to avoid WASM trap\nSPACETIMEDB_REDUCER(insert_identity, ReducerContext ctx, IdentityParam param)\n{\n    ctx.db.table<TestIdentity>(\"test_identity\").insert(TestIdentity{param.i});\n}\n\nSPACETIMEDB_REDUCER(insert_connection_id, ReducerContext ctx, ConnectionIdParam param)\n{\n    ctx.db.table<TestConnectionId>(\"test_connection_id\").insert(TestConnectionId{param.c});\n}\n\n// Control: basic reducer without special type parameters (known to work)\nSPACETIMEDB_REDUCER(test_basic, ReducerContext ctx)\n{\n    LOG_INFO(\"Basic reducer called\");\n}\n\n// TEST: Direct special type parameters (expected to cause WASM trap)\nSPACETIMEDB_REDUCER(insert_direct_identity, ReducerContext ctx, Identity i)\n{\n    ctx.db.table<TestIdentity>(\"test_identity\").insert(TestIdentity{i});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_connection_id, ReducerContext ctx, ConnectionId c)\n{\n    ctx.db.table<TestConnectionId>(\"test_connection_id\").insert(TestConnectionId{c});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_timestamp, ReducerContext ctx, Timestamp t)\n{\n    LOG_INFO(\"Received timestamp parameter\");\n}\n\nSPACETIMEDB_REDUCER(insert_direct_time_duration, ReducerContext ctx, TimeDuration d)\n{\n    LOG_INFO(\"Received duration parameter\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_trace.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// Simple table with optional field\nstruct DebugTable {\n    uint32_t id;\n    std::optional<int32_t> maybe_value;\n};\n\nSPACETIMEDB_STRUCT(DebugTable, id, maybe_value)\nSPACETIMEDB_TABLE(DebugTable, debug_table, Public)\n\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_vector_only.cpp",
    "content": "#include <spacetimedb.h>\n#include <vector>\n\nusing namespace SpacetimeDB;\n\n// Minimal test - just one vector variant to see how it's serialized\n\nSPACETIMEDB_ENUM(SimpleVectorEnum,\n    (Bytes, std::vector<uint8_t>)\n)\n\nstruct VectorTable { \n    SimpleVectorEnum e; \n};\nSPACETIMEDB_STRUCT(VectorTable, e)\nSPACETIMEDB_TABLE(VectorTable, vector_table, Public)\n\nSPACETIMEDB_REDUCER(insert_test, ReducerContext ctx)\n{\n    std::vector<uint8_t> bytes = {1, 2, 3};\n    SimpleVectorEnum e = bytes;\n    ctx.db.table<VectorTable>(\"vector_table\").insert(VectorTable{e});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_vector_only_simple.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n\n// SimpleEnum defined but ONLY used in vector context using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// Variant enum with vector<SimpleEnum> - this triggers the problem using new unified syntax\nSPACETIMEDB_ENUM(VectorEnum,\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\n// Table using VectorEnum (which contains vector<SimpleEnum>)\nstruct VectorTable { VectorEnum ve; };\nSPACETIMEDB_STRUCT(VectorTable, ve)\nSPACETIMEDB_TABLE(VectorTable, vector_table, Public)\n\n// NO direct SimpleEnum usage - only through vector"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_autoinc_non_integer.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test auto-increment on non-integer column types\n// AutoInc should only work on integer types (uint32_t, uint64_t, etc.)\n\n// AutoInc on string field - INVALID\nstruct StringAutoInc {\n    std::string id;  // Strings can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(StringAutoInc, id, data)\nSPACETIMEDB_TABLE(StringAutoInc, string_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(string_autoinc_table, id);  // ERROR: AutoInc on string!\n\n// AutoInc on float field - INVALID\nstruct FloatAutoInc {\n    float id;  // Floats can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(FloatAutoInc, id, data)\nSPACETIMEDB_TABLE(FloatAutoInc, float_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(float_autoinc_table, id);  // ERROR: AutoInc on float!\n\n// AutoInc on double field - INVALID\nstruct DoubleAutoInc {\n    double id;  // Doubles can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(DoubleAutoInc, id, data)\nSPACETIMEDB_TABLE(DoubleAutoInc, double_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(double_autoinc_table, id);  // ERROR: AutoInc on double!\n\n// AutoInc on bool field - INVALID\nstruct BoolAutoInc {\n    bool id;  // Bools can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(BoolAutoInc, id, data)\nSPACETIMEDB_TABLE(BoolAutoInc, bool_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(bool_autoinc_table, id);  // ERROR: AutoInc on bool!\n\n// AutoInc on Identity field - INVALID (Identity is not an integer)\nstruct IdentityAutoInc {\n    Identity id;  // Identity can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(IdentityAutoInc, id, data)\nSPACETIMEDB_TABLE(IdentityAutoInc, identity_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(identity_autoinc_table, id);  // ERROR: AutoInc on Identity!\n\n// AutoInc on struct field - INVALID\nstruct NestedStruct {\n    uint32_t x;\n    uint32_t y;\n};\nSPACETIMEDB_STRUCT(NestedStruct, x, y)\n\nstruct StructAutoInc {\n    NestedStruct id;  // Structs can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(StructAutoInc, id, data)\nSPACETIMEDB_TABLE(StructAutoInc, struct_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(struct_autoinc_table, id);  // ERROR: AutoInc on struct!\n\n// AutoInc on vector field - INVALID\nstruct VectorAutoInc {\n    std::vector<uint32_t> id;  // Vectors can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(VectorAutoInc, id, data)\nSPACETIMEDB_TABLE(VectorAutoInc, vector_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(vector_autoinc_table, id);  // ERROR: AutoInc on vector!\n\n// AutoInc on optional field - INVALID\nstruct OptionalAutoInc {\n    std::optional<uint32_t> id;  // Optionals can't auto-increment!\n    std::string data;\n};\nSPACETIMEDB_STRUCT(OptionalAutoInc, id, data)\nSPACETIMEDB_TABLE(OptionalAutoInc, optional_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(optional_autoinc_table, id);  // ERROR: AutoInc on optional!\n\n// Valid AutoInc tables for comparison\nstruct ValidU32AutoInc {\n    uint32_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidU32AutoInc, id, data)\nSPACETIMEDB_TABLE(ValidU32AutoInc, valid_u32_autoinc, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(valid_u32_autoinc, id);  // Correct: AutoInc on uint32_t\n\nstruct ValidU64AutoInc {\n    uint64_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidU64AutoInc, id, data)\nSPACETIMEDB_TABLE(ValidU64AutoInc, valid_u64_autoinc, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(valid_u64_autoinc, id);  // Correct: AutoInc on uint64_t\n\nstruct ValidI32AutoInc {\n    int32_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidI32AutoInc, id, data)\nSPACETIMEDB_TABLE(ValidI32AutoInc, valid_i32_autoinc, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(valid_i32_autoinc, id);  // Correct: AutoInc on int32_t\n\nstruct ValidI64AutoInc {\n    int64_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidI64AutoInc, id, data)\nSPACETIMEDB_TABLE(ValidI64AutoInc, valid_i64_autoinc, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(valid_i64_autoinc, id);  // Correct: AutoInc on int64_t\n\n// Test reducer\nSPACETIMEDB_REDUCER(test_autoinc_types, SpacetimeDB::ReducerContext ctx)\n{\n    LOG_INFO(\"Testing auto-increment on non-integer types - should fail validation\");\n    \n    // These should all fail if validation works\n    StringAutoInc string_ai{\"\", \"String AutoInc\"};\n    ctx.db[string_autoinc_table].insert(string_ai);\n    \n    FloatAutoInc float_ai{0.0f, \"Float AutoInc\"};\n    ctx.db[float_autoinc_table].insert(float_ai);\n    \n    // Valid inserts\n    ValidU32AutoInc valid_u32{0, \"Valid U32\"};\n    ctx.db[valid_u32_autoinc].insert(valid_u32);\n    \n    ValidU64AutoInc valid_u64{0, \"Valid U64\"};\n    ctx.db[valid_u64_autoinc].insert(valid_u64);\n    return Ok();\n}\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx)\n{\n    LOG_INFO(\"Auto-increment on non-integer types test\");\n    LOG_INFO(\"AutoInc should only work on integer types\");\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_circular_ref.cpp",
    "content": "#include <spacetimedb.h>\n#include <vector>\n#include <optional>\n// Test circular references: A → B → C → A\n// This should be caught by the type system\n\n\n\n\n// StructC references StructA (completing the circle)\nstruct StructA {\n    uint32_t id;\n    std::vector<StructA> a_ref; //(circular!)\n};\n\n// Register the structs\nSPACETIMEDB_STRUCT(StructA, id, a_ref)\n\n// Try to use them as tables\nSPACETIMEDB_TABLE(StructA, struct_a, SpacetimeDB::Public)\n\n// Test reducer\nSPACETIMEDB_REDUCER(test_circular_ref, SpacetimeDB::ReducerContext ctx)\n{\n    LOG_INFO(\"This should never execute - circular references should be detected\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_default_missing_field.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\nstruct BadDefaultRow {\n    uint32_t id;\n    uint32_t player;\n};\nSPACETIMEDB_STRUCT(BadDefaultRow, id, player)\nSPACETIMEDB_TABLE(BadDefaultRow, bad_default_row, Public)\nFIELD_PrimaryKey(bad_default_row, id)\n\n// Negative test: \"missing_col\" does not exist in BadDefaultRow.\nFIELD_Default(bad_default_row, missing_col, uint32_t(7))\n\nSPACETIMEDB_REDUCER(insert_bad_default_row, ReducerContext ctx, uint32_t id, uint32_t player)\n{\n    ctx.db[bad_default_row].insert(BadDefaultRow{id, player});\n    return Ok();\n}\n\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_invalid_index.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test FIELD_ macro validation for index constraints on non-filterable types\n\n// Complex struct that can't be indexed\nstruct ComplexData {\n    uint32_t x;\n    uint32_t y;\n    std::string label;\n};\nSPACETIMEDB_STRUCT(ComplexData, x, y, label)\n\n// Table with struct field - define without constraints in SPACETIMEDB_TABLE\nstruct UniqueOnStruct {\n    uint32_t id;\n    ComplexData data;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(UniqueOnStruct, id, data, name)\n\n// Register table without any constraints\nSPACETIMEDB_TABLE(UniqueOnStruct, unique_struct_table, SpacetimeDB::Public)\n\n// Now add constraints using FIELD_ macros - this should fail at compile time!\nFIELD_PrimaryKey(unique_struct_table, id)           // OK: Any type can be primary key\nFIELD_Unique(unique_struct_table, data)             // ERROR: ComplexData is not filterable!\nFIELD_Index(unique_struct_table, name)              // OK: String is filterable\n\n// Table with vector field\nstruct UniqueOnVector {\n    uint32_t id;\n    std::vector<uint32_t> items;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(UniqueOnVector, id, items, name)\nSPACETIMEDB_TABLE(UniqueOnVector, unique_vector_table, SpacetimeDB::Public)\n\n// Add field constraints\nFIELD_PrimaryKey(unique_vector_table, id)\nFIELD_Unique(unique_vector_table, items)            // ERROR: Vector is not filterable!\n\n// Table with optional field\nstruct UniqueOnOptional {\n    uint32_t id;\n    std::optional<uint32_t> maybe_value;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(UniqueOnOptional, id, maybe_value, name)\nSPACETIMEDB_TABLE(UniqueOnOptional, unique_optional_table, SpacetimeDB::Public)\n\n// Add field constraints\nFIELD_PrimaryKey(unique_optional_table, id)\nFIELD_Unique(unique_optional_table, maybe_value)    // ERROR: Optional is not filterable!\n\n// Table with float field\nstruct UniqueOnFloat {\n    uint32_t id;\n    float value;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(UniqueOnFloat, id, value, name)\nSPACETIMEDB_TABLE(UniqueOnFloat, unique_float_table, SpacetimeDB::Public)\n\n// Add field constraints\nFIELD_PrimaryKey(unique_float_table, id)\nFIELD_Unique(unique_float_table, value)             // ERROR: Float is not filterable!\n\n// Table with double field\nstruct IndexOnDouble {\n    uint32_t id;\n    double value;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(IndexOnDouble, id, value, name)\nSPACETIMEDB_TABLE(IndexOnDouble, index_double_table, SpacetimeDB::Public)\n\n// Add field constraints\nFIELD_PrimaryKey(index_double_table, id)\nFIELD_Index(index_double_table, value)              // ERROR: Double is not filterable!\n\n// Table with ScheduleAt field\nstruct UniqueOnScheduleAt {\n    uint32_t id;\n    ScheduleAt schedule;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(UniqueOnScheduleAt, id, schedule, name)\nSPACETIMEDB_TABLE(UniqueOnScheduleAt, unique_schedule_table, SpacetimeDB::Public)\n\n// Add field constraints\nFIELD_PrimaryKey(unique_schedule_table, id)\nFIELD_Unique(unique_schedule_table, schedule)       // ERROR: ScheduleAt is not filterable!\n\n// Valid indexed tables for comparison\nstruct ValidUniqueInt {\n    uint32_t id;\n    uint32_t unique_code;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(ValidUniqueInt, id, unique_code, name)\nSPACETIMEDB_TABLE(ValidUniqueInt, valid_unique_int_table, SpacetimeDB::Public)\n\n// These should all work fine\nFIELD_PrimaryKey(valid_unique_int_table, id)\nFIELD_Unique(valid_unique_int_table, unique_code)   // OK: Integer is filterable\n\nstruct ValidIndexString {\n    uint32_t id;\n    std::string indexed_name;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidIndexString, id, indexed_name, data)\nSPACETIMEDB_TABLE(ValidIndexString, valid_index_string_table, SpacetimeDB::Public)\n\nFIELD_PrimaryKey(valid_index_string_table, id)\nFIELD_Index(valid_index_string_table, indexed_name) // OK: String is filterable\n\nstruct ValidUniqueIdentity {\n    uint32_t id;\n    Identity user_id;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(ValidUniqueIdentity, id, user_id, name)\nSPACETIMEDB_TABLE(ValidUniqueIdentity, valid_unique_identity_table, SpacetimeDB::Public)\n\nFIELD_PrimaryKey(valid_unique_identity_table, id)\nFIELD_Unique(valid_unique_identity_table, user_id)  // OK: Identity is filterable\n\nstruct ValidIndexTimestamp {\n    uint32_t id;\n    Timestamp created_at;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidIndexTimestamp, id, created_at, data)\nSPACETIMEDB_TABLE(ValidIndexTimestamp, valid_index_timestamp_table, SpacetimeDB::Public)\n\nFIELD_PrimaryKey(valid_index_timestamp_table, id)\nFIELD_Index(valid_index_timestamp_table, created_at) // OK: Timestamp is filterable\n\nstruct ValidUniqueBool {\n    uint32_t id;\n    bool is_active;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(ValidUniqueBool, id, is_active, data)\nSPACETIMEDB_TABLE(ValidUniqueBool, valid_unique_bool_table, SpacetimeDB::Public)\n\nFIELD_PrimaryKey(valid_unique_bool_table, id)\nFIELD_Unique(valid_unique_bool_table, is_active)    // OK: Bool is filterable (though unusual)\n\n// Test reducer\nSPACETIMEDB_REDUCER(test_field_macro_validation, SpacetimeDB::ReducerContext ctx)\n{\n    LOG_INFO(\"Testing FIELD_ macro validation\");\n    \n    // If this code runs, it means validation failed!\n    // The module should fail to compile due to FIELD_ macro validation\n    \n    ComplexData complex{1, 2, \"Complex\"};\n    UniqueOnStruct bad_unique{1, complex, \"Bad unique\"};\n    ctx.db[unique_struct_table].insert(bad_unique);\n    \n    ValidUniqueInt valid_int{1, 100, \"Valid unique int\"};\n    ctx.db[valid_unique_int_table].insert(valid_int);\n    \n    ValidIndexString valid_string{1, \"indexed\", \"Valid index string\"};\n    ctx.db[valid_index_string_table].insert(valid_string);\n    return Ok();\n}\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx)\n{\n    LOG_INFO(\"FIELD_ macro validation test\");\n    LOG_INFO(\"This module should FAIL to compile if validation is working\");\n    LOG_INFO(\"Errors expected for:\");\n    LOG_INFO(\"- FIELD_Unique on ComplexData\");\n    LOG_INFO(\"- FIELD_Unique on vector\");\n    LOG_INFO(\"- FIELD_Unique on optional\");\n    LOG_INFO(\"- FIELD_Unique on float\");\n    LOG_INFO(\"- FIELD_Index on double\");\n    LOG_INFO(\"- FIELD_Unique on ScheduleAt\");\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_multicolumn_missing_field.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\nstruct BadIndexRow {\n    uint32_t id;\n    uint32_t player;\n};\nSPACETIMEDB_STRUCT(BadIndexRow, id, player)\nSPACETIMEDB_TABLE(BadIndexRow, bad_index_row, Public)\nFIELD_PrimaryKey(bad_index_row, id)\n\n// Negative test: \"round\" does not exist in BadIndexRow.\nFIELD_MultiColumnIndex(bad_index_row, by_player_round, player, round)\n\nSPACETIMEDB_REDUCER(insert_bad_index_row, ReducerContext ctx, uint32_t id, uint32_t player)\n{\n    ctx.db[bad_index_row].insert(BadIndexRow{id, player});\n    return Ok();\n}\n\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_multiple_pk.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test multiple primary keys in a single table\n// SpacetimeDB should only allow ONE primary key per table\n\n// Table with two PrimaryKey fields - INVALID\nstruct DoublePrimaryKey {\n    uint32_t id1;\n    uint32_t id2;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(DoublePrimaryKey, id1, id2, data)\nSPACETIMEDB_TABLE(DoublePrimaryKey, double_pk_table, SpacetimeDB::Public)\nFIELD_PrimaryKey(double_pk_table, id1);\nFIELD_PrimaryKey(double_pk_table, id2);  // ERROR: Two primary keys!\n\n// Table with PrimaryKey and PrimaryKeyAutoInc - INVALID\nstruct MixedPrimaryKey {\n    uint32_t manual_id;\n    uint64_t auto_id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(MixedPrimaryKey, manual_id, auto_id, data)\nSPACETIMEDB_TABLE(MixedPrimaryKey, mixed_pk_table, SpacetimeDB::Public)\nFIELD_PrimaryKey(mixed_pk_table, manual_id);\nFIELD_PrimaryKeyAutoInc(mixed_pk_table, auto_id);  // ERROR: Two primary keys of different types!\n\n// Table with multiple PrimaryKeyAutoInc - INVALID\nstruct DoubleAutoInc {\n    uint64_t id1;\n    uint64_t id2;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(DoubleAutoInc, id1, id2, data)\nSPACETIMEDB_TABLE(DoubleAutoInc, double_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(double_autoinc_table, id1);\nFIELD_PrimaryKeyAutoInc(double_autoinc_table, id2);  // ERROR: Two auto-increment primary keys!\n\n// Table with three primary keys - VERY INVALID\nstruct TriplePrimaryKey {\n    uint32_t id1;\n    uint32_t id2;\n    uint32_t id3;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(TriplePrimaryKey, id1, id2, id3, data)\nSPACETIMEDB_TABLE(TriplePrimaryKey, triple_pk_table, SpacetimeDB::Public)\nFIELD_PrimaryKey(triple_pk_table, id1);\nFIELD_PrimaryKey(triple_pk_table, id2);\nFIELD_PrimaryKey(triple_pk_table, id3);  // ERROR: Three primary keys!\n\n// Valid table for comparison - single primary key\nstruct SinglePrimaryKey {\n    uint32_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(SinglePrimaryKey, id, data)\nSPACETIMEDB_TABLE(SinglePrimaryKey, single_pk_table, SpacetimeDB::Public)\nFIELD_PrimaryKey(single_pk_table, id);  // Correct: Single primary key\n\n// Valid table with auto-increment\nstruct SingleAutoInc {\n    uint64_t id;\n    std::string data;\n};\nSPACETIMEDB_STRUCT(SingleAutoInc, id, data)\nSPACETIMEDB_TABLE(SingleAutoInc, single_autoinc_table, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(single_autoinc_table, id);  // Correct: Single auto-increment primary key\n\n// Test reducer\nSPACETIMEDB_REDUCER(test_multiple_pks, SpacetimeDB::ReducerContext ctx)\n{\n    LOG_INFO(\"Testing multiple primary keys - should fail validation\");\n    \n    // Try to insert into double PK table\n    DoublePrimaryKey double_pk{1, 2, \"Double PK\"};\n    ctx.db[double_pk_table].insert(double_pk);\n    \n    // Try to insert into mixed PK table\n    MixedPrimaryKey mixed_pk{1, 0, \"Mixed PK\"};\n    ctx.db[mixed_pk_table].insert(mixed_pk);\n    \n    // Insert into valid tables\n    SinglePrimaryKey single_pk{1, \"Valid single PK\"};\n    ctx.db[single_pk_table].insert(single_pk);\n    \n    SingleAutoInc single_auto{0, \"Valid auto-inc\"};\n    ctx.db[single_autoinc_table].insert(single_auto);\n    return Ok();\n}\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx)\n{\n    LOG_INFO(\"Multiple primary keys test - should fail validation\");\n    LOG_INFO(\"SpacetimeDB allows only ONE primary key per table\");\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_non_spacetimedb_type.cpp",
    "content": "#include <spacetimedb.h>\n#include <memory>\n#include <thread>\n#include <atomic>\n\n// Test types that don't have BSATN serialization support\n// These should be caught as compilation errors\n\n// Custom type without SPACETIMEDB_STRUCT macro\nstruct UnsupportedType {\n    int x;\n    int y;\n};\n// Intentionally NOT adding: SPACETIMEDB_STRUCT(UnsupportedType, x, y)\n\n// Type with unsupported member (std::thread)\nstruct ThreadContainingType {\n    uint32_t id;\n    std::thread worker;  // std::thread cannot be serialized\n};\n// This would fail even with SPACETIMEDB_STRUCT\n\n// Type with raw pointer (not serializable)\nstruct RawPointerType {\n    uint32_t id;\n    int* data;  // Raw pointers cannot be serialized\n};\n// SPACETIMEDB_STRUCT(RawPointerType, id, data) // Would fail\n\n// Type with std::unique_ptr (not serializable)\nstruct SmartPointerType {\n    uint32_t id;\n    std::unique_ptr<int> value;  // Smart pointers cannot be serialized\n};\n// SPACETIMEDB_STRUCT(SmartPointerType, id, value) // Would fail\n\n// Type with std::atomic (not serializable)\nstruct AtomicType {\n    uint32_t id;\n    std::atomic<int> counter;  // Atomics cannot be serialized\n};\n// SPACETIMEDB_STRUCT(AtomicType, id, counter) // Would fail\n\n// Valid type for comparison\nstruct ValidType {\n    uint32_t id;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(ValidType, id, name)\n\n// Try to use unsupported type as table - should fail\nSPACETIMEDB_TABLE(UnsupportedType, unsupported_table, SpacetimeDB::Public)\n\n// Try to use unsupported type as reducer argument - should fail\nSPACETIMEDB_REDUCER(test_unsupported_arg, SpacetimeDB::ReducerContext ctx, UnsupportedType arg)\n{\n    LOG_INFO(\"This should never compile - UnsupportedType lacks BSATN traits\");\n    return Ok();\n}\n\n// Valid reducer for comparison\nSPACETIMEDB_REDUCER(test_valid_arg, SpacetimeDB::ReducerContext ctx, ValidType arg)\n{\n    LOG_INFO(\"Valid type works fine: \" + arg.name);\n    return Ok();\n}\n\n// Try to use type with unsupported members in a struct\nstruct ComplexBadType {\n    uint32_t id;\n    UnsupportedType unsupported;  // Contains type without BSATN\n};\nSPACETIMEDB_STRUCT(ComplexBadType, id, unsupported)  // Should fail - nested type lacks traits\nSPACETIMEDB_TABLE(ComplexBadType, complex_bad_table, SpacetimeDB::Public)\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx)\n{\n    LOG_INFO(\"Non-SpacetimeDB type test - should fail compilation\");\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/error_scheduled_id_pk.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test scheduled table without primary key on scheduled_id\n// This should be caught as an error\n\n// Incorrect scheduled table - scheduled_id is NOT a primary key\nstruct BadScheduledTable {\n    uint64_t scheduled_id;  // Should be PrimaryKeyAutoInc but isn't\n    SpacetimeDB::ScheduleAt scheduled_at;\n    std::string message;\n};\nSPACETIMEDB_STRUCT(BadScheduledTable, scheduled_id, scheduled_at, message)\nSPACETIMEDB_TABLE(BadScheduledTable, bad_scheduled, SpacetimeDB::Public)  // Missing PrimaryKeyAutoInc(scheduled_id)\nSPACETIMEDB_SCHEDULE(bad_scheduled, 1, process_bad_schedule)  // This should fail!\n\n// Another incorrect variant - has primary key but on wrong field\nstruct WrongPkScheduledTable {\n    uint64_t scheduled_id;\n    SpacetimeDB::ScheduleAt scheduled_at;\n    std::string message;\n};\nSPACETIMEDB_STRUCT(WrongPkScheduledTable, scheduled_id, scheduled_at, message)\nSPACETIMEDB_TABLE(WrongPkScheduledTable, wrong_pk_scheduled, SpacetimeDB::Public)\nFIELD_PrimaryKey(wrong_pk_scheduled, message)  // Primary key on wrong field!\nSPACETIMEDB_SCHEDULE(wrong_pk_scheduled, 1, process_wrong_pk_schedule)\n\n// Yet another incorrect variant - has unique constraint instead of primary key\nstruct UniqueScheduledTable {\n    uint64_t scheduled_id;\n    SpacetimeDB::ScheduleAt scheduled_at;\n    std::string message;\n};\nSPACETIMEDB_STRUCT(UniqueScheduledTable, scheduled_id, scheduled_at, message)\nSPACETIMEDB_TABLE(UniqueScheduledTable, unique_scheduled, SpacetimeDB::Public)\nFIELD_Unique(unique_scheduled, scheduled_id)  // Unique instead of PrimaryKey!\nSPACETIMEDB_SCHEDULE(unique_scheduled, 1, process_unique_schedule)\n\n// Correct scheduled table for comparison\nstruct GoodScheduledTable {\n    uint64_t scheduled_id;\n    SpacetimeDB::ScheduleAt scheduled_at;\n    std::string message;\n};\nSPACETIMEDB_STRUCT(GoodScheduledTable, scheduled_id, scheduled_at, message)\nSPACETIMEDB_TABLE(GoodScheduledTable, good_scheduled, SpacetimeDB::Public)\nFIELD_PrimaryKeyAutoInc(good_scheduled, scheduled_id)  // Correct!\nSPACETIMEDB_SCHEDULE(good_scheduled, 1, process_good_schedule)\n\n// Scheduled reducers\nSPACETIMEDB_REDUCER(process_bad_schedule, SpacetimeDB::ReducerContext ctx, BadScheduledTable arg)\n{\n    LOG_INFO(\"Bad schedule executed: \" + arg.message);\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(process_wrong_pk_schedule, SpacetimeDB::ReducerContext ctx, WrongPkScheduledTable arg)\n{\n    LOG_INFO(\"Wrong PK schedule executed: \" + arg.message);\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(process_unique_schedule, SpacetimeDB::ReducerContext ctx, UniqueScheduledTable arg)\n{\n    LOG_INFO(\"Unique schedule executed: \" + arg.message);\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(process_good_schedule, SpacetimeDB::ReducerContext ctx, GoodScheduledTable arg)\n{\n    LOG_INFO(\"Good schedule executed: \" + arg.message);\n    return Ok();\n}\n\n// Test reducer to schedule tasks\nSPACETIMEDB_REDUCER(test_schedule_tables, SpacetimeDB::ReducerContext ctx)\n{\n    LOG_INFO(\"Testing scheduled tables - this should fail if validation works\");\n    \n    // Try to insert into bad scheduled table\n    BadScheduledTable bad{0, ScheduleAt::time(ctx.timestamp + TimeDuration(1000000)), \"Bad schedule\"};\n    ctx.db[bad_scheduled].insert(bad);\n    \n    // Try to insert into wrong PK scheduled table\n    WrongPkScheduledTable wrong{0, ScheduleAt::time(ctx.timestamp + TimeDuration(1000000)), \"Wrong PK\"};\n    ctx.db[wrong_pk_scheduled].insert(wrong);\n    \n    // Try to insert into unique scheduled table\n    UniqueScheduledTable unique{0, ScheduleAt::time(ctx.timestamp + TimeDuration(1000000)), \"Unique instead of PK\"};\n    ctx.db[unique_scheduled].insert(unique);\n    \n    // Insert into good scheduled table (should work)\n    GoodScheduledTable good{0, ScheduleAt::time(ctx.timestamp + TimeDuration(1000000)), \"Good schedule\"};\n    ctx.db[good_scheduled].insert(good);\n    return Ok();\n}\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx)\n{\n    LOG_INFO(\"Scheduled table PK test module initialized\");\n    LOG_INFO(\"This tests that scheduled_id must be a primary key\");\n    LOG_INFO(\"Should fail if scheduled_id is not PrimaryKeyAutoInc\");\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module01_basic_unsigned.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 1: Basic unsigned integers (U8, U16, U32)\n// Testing if basic unsigned integer types cause WASM issues\n\n\n// OneU8 table\nstruct OneU8 { uint8_t n; };\nSPACETIMEDB_STRUCT(OneU8, n)\nSPACETIMEDB_TABLE(OneU8, one_u8, Public)\n\n// OneU16 table  \nstruct OneU16 { uint16_t n; };\nSPACETIMEDB_STRUCT(OneU16, n)\nSPACETIMEDB_TABLE(OneU16, one_u16, Public)\n\n// OneU32 table\nstruct OneU32 { uint32_t n; };\nSPACETIMEDB_STRUCT(OneU32, n)\nSPACETIMEDB_TABLE(OneU32, one_u32, Public)\n\n// VecU8 table\nstruct VecU8 { std::vector<uint8_t> n; };\nSPACETIMEDB_STRUCT(VecU8, n)\nSPACETIMEDB_TABLE(VecU8, vec_u8, Public)\n\n// VecU16 table\nstruct VecU16 { std::vector<uint16_t> n; };\nSPACETIMEDB_STRUCT(VecU16, n)\nSPACETIMEDB_TABLE(VecU16, vec_u16, Public)\n\n// VecU32 table\nstruct VecU32 { std::vector<uint32_t> n; };\nSPACETIMEDB_STRUCT(VecU32, n)\nSPACETIMEDB_TABLE(VecU32, vec_u32, Public)\n\n// UniqueU8 table\nstruct UniqueU8 { uint8_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU8, n, data)\nSPACETIMEDB_TABLE(UniqueU8, unique_u8, Public)\nFIELD_Unique(unique_u8, n)\n\n// UniqueU16 table\nstruct UniqueU16 { uint16_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU16, n, data)\nSPACETIMEDB_TABLE(UniqueU16, unique_u16, Public)\nFIELD_Unique(unique_u16, n)\n\n// UniqueU32 table\nstruct UniqueU32 { uint32_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU32, n, data)\nSPACETIMEDB_TABLE(UniqueU32, unique_u32, Public)\nFIELD_Unique(unique_u32, n)\n\n// PkU8 table\nstruct PkU8 { uint8_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU8, n, data)\nSPACETIMEDB_TABLE(PkU8, pk_u8, Public)\nFIELD_PrimaryKey(pk_u8, n)\n\n// PkU16 table\nstruct PkU16 { uint16_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU16, n, data)\nSPACETIMEDB_TABLE(PkU16, pk_u16, Public)\nFIELD_PrimaryKey(pk_u16, n)\n\n// PkU32 table\nstruct PkU32 { uint32_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU32, n, data)\nSPACETIMEDB_TABLE(PkU32, pk_u32, Public)\nFIELD_PrimaryKey(pk_u32, n)\n\n// Reducers for basic unsigned integers\nSPACETIMEDB_REDUCER(insert_one_u8, ReducerContext ctx, uint8_t n)\n{\n    ctx.db.table<OneU8>(\"one_u8\").insert(OneU8{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_u16, ReducerContext ctx, uint16_t n)\n{\n    ctx.db.table<OneU16>(\"one_u16\").insert(OneU16{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_u32, ReducerContext ctx, uint32_t n)\n{\n    ctx.db.table<OneU32>(\"one_u32\").insert(OneU32{n});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module02_large_unsigned.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 2: Large unsigned integers (U64, U128, U256)\n// Testing if large unsigned integer types cause WASM issues\n\n\n// OneU64 table\nstruct OneU64 { uint64_t n; };\nSPACETIMEDB_STRUCT(OneU64, n)\nSPACETIMEDB_TABLE(OneU64, one_u64, Public)\n\n// OneU128 table\nstruct OneU128 { u128 n; };\nSPACETIMEDB_STRUCT(OneU128, n)\nSPACETIMEDB_TABLE(OneU128, one_u128, Public)\n\n// OneU256 table\nstruct OneU256 { u256 n; };\nSPACETIMEDB_STRUCT(OneU256, n)\nSPACETIMEDB_TABLE(OneU256, one_u256, Public)\n\n// VecU64 table\nstruct VecU64 { std::vector<uint64_t> n; };\nSPACETIMEDB_STRUCT(VecU64, n)\nSPACETIMEDB_TABLE(VecU64, vec_u64, Public)\n\n// VecU128 table\nstruct VecU128 { std::vector<u128> n; };\nSPACETIMEDB_STRUCT(VecU128, n)\nSPACETIMEDB_TABLE(VecU128, vec_u128, Public)\n\n// VecU256 table\nstruct VecU256 { std::vector<u256> n; };\nSPACETIMEDB_STRUCT(VecU256, n)\nSPACETIMEDB_TABLE(VecU256, vec_u256, Public)\n\n// UniqueU64 table\nstruct UniqueU64 { uint64_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU64, n, data)\nSPACETIMEDB_TABLE(UniqueU64, unique_u64, Public)\nFIELD_Unique(unique_u64, n)\n\n// UniqueU128 table\nstruct UniqueU128 { u128 n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU128, n, data)\nSPACETIMEDB_TABLE(UniqueU128, unique_u128, Public)\nFIELD_Unique(unique_u128, n)\n\n// UniqueU256 table\nstruct UniqueU256 { u256 n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueU256, n, data)\nSPACETIMEDB_TABLE(UniqueU256, unique_u256, Public)\nFIELD_Unique(unique_u256, n)\n\n// PkU64 table\nstruct PkU64 { uint64_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU64, n, data)\nSPACETIMEDB_TABLE(PkU64, pk_u64, Public)\nFIELD_PrimaryKey(pk_u64, n)\n\n// PkU128 table\nstruct PkU128 { u128 n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU128, n, data)\nSPACETIMEDB_TABLE(PkU128, pk_u128, Public)\nFIELD_PrimaryKey(pk_u128, n)\n\n// PkU256 table\nstruct PkU256 { u256 n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU256, n, data)\nSPACETIMEDB_TABLE(PkU256, pk_u256, Public)\nFIELD_PrimaryKey(pk_u256, n)\n\n// Reducers for large unsigned integers\nSPACETIMEDB_REDUCER(insert_one_u64, ReducerContext ctx, uint64_t n)\n{\n    ctx.db.table<OneU64>(\"one_u64\").insert(OneU64{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_u128, ReducerContext ctx, u128 n)\n{\n    ctx.db.table<OneU128>(\"one_u128\").insert(OneU128{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_u256, ReducerContext ctx, u256 n)\n{\n    ctx.db.table<OneU256>(\"one_u256\").insert(OneU256{n});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module03_basic_signed.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 3: Basic signed integers (I8, I16, I32)\n// Testing if basic signed integer types cause WASM issues\n\n\n// OneI8 table\nstruct OneI8 { int8_t n; };\nSPACETIMEDB_STRUCT(OneI8, n)\nSPACETIMEDB_TABLE(OneI8, one_i8, Public)\n\n// OneI16 table\nstruct OneI16 { int16_t n; };\nSPACETIMEDB_STRUCT(OneI16, n)\nSPACETIMEDB_TABLE(OneI16, one_i16, Public)\n\n// OneI32 table\nstruct OneI32 { int32_t n; };\nSPACETIMEDB_STRUCT(OneI32, n)\nSPACETIMEDB_TABLE(OneI32, one_i32, Public)\n\n// VecI8 table\nstruct VecI8 { std::vector<int8_t> n; };\nSPACETIMEDB_STRUCT(VecI8, n)\nSPACETIMEDB_TABLE(VecI8, vec_i8, Public)\n\n// VecI16 table\nstruct VecI16 { std::vector<int16_t> n; };\nSPACETIMEDB_STRUCT(VecI16, n)\nSPACETIMEDB_TABLE(VecI16, vec_i16, Public)\n\n// VecI32 table\nstruct VecI32 { std::vector<int32_t> n; };\nSPACETIMEDB_STRUCT(VecI32, n)\nSPACETIMEDB_TABLE(VecI32, vec_i32, Public)\n\n// UniqueI8 table\nstruct UniqueI8 { int8_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI8, n, data)\nSPACETIMEDB_TABLE(UniqueI8, unique_i8, Public)\nFIELD_Unique(unique_i8, n)\n\n// UniqueI16 table\nstruct UniqueI16 { int16_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI16, n, data)\nSPACETIMEDB_TABLE(UniqueI16, unique_i16, Public)\nFIELD_Unique(unique_i16, n)\n\n// UniqueI32 table\nstruct UniqueI32 { int32_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI32, n, data)\nSPACETIMEDB_TABLE(UniqueI32, unique_i32, Public)\nFIELD_Unique(unique_i32, n)\n\n// PkI8 table\nstruct PkI8 { int8_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI8, n, data)\nSPACETIMEDB_TABLE(PkI8, pk_i8, Public)\nFIELD_PrimaryKey(pk_i8, n)\n\n// PkI16 table\nstruct PkI16 { int16_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI16, n, data)\nSPACETIMEDB_TABLE(PkI16, pk_i16, Public)\nFIELD_PrimaryKey(pk_i16, n)\n\n// PkI32 table\nstruct PkI32 { int32_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI32, n, data)\nSPACETIMEDB_TABLE(PkI32, pk_i32, Public)\nFIELD_PrimaryKey(pk_i32, n)\n\n// Reducers for basic signed integers\nSPACETIMEDB_REDUCER(insert_one_i8, ReducerContext ctx, int8_t n)\n{\n    ctx.db.table<OneI8>(\"one_i8\").insert(OneI8{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_i16, ReducerContext ctx, int16_t n)\n{\n    ctx.db.table<OneI16>(\"one_i16\").insert(OneI16{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_i32, ReducerContext ctx, int32_t n)\n{\n    ctx.db.table<OneI32>(\"one_i32\").insert(OneI32{n});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module04_large_signed.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 4: Large signed integers (I64, I128, I256)\n// Testing if large signed integer types cause WASM issues\n\n\n// OneI64 table\nstruct OneI64 { int64_t n; };\nSPACETIMEDB_STRUCT(OneI64, n)\nSPACETIMEDB_TABLE(OneI64, one_i64, Public)\n\n// OneI128 table\nstruct OneI128 { i128 n; };\nSPACETIMEDB_STRUCT(OneI128, n)\nSPACETIMEDB_TABLE(OneI128, one_i128, Public)\n\n// OneI256 table\nstruct OneI256 { i256 n; };\nSPACETIMEDB_STRUCT(OneI256, n)\nSPACETIMEDB_TABLE(OneI256, one_i256, Public)\n\n// VecI64 table\nstruct VecI64 { std::vector<int64_t> n; };\nSPACETIMEDB_STRUCT(VecI64, n)\nSPACETIMEDB_TABLE(VecI64, vec_i64, Public)\n\n// VecI128 table\nstruct VecI128 { std::vector<i128> n; };\nSPACETIMEDB_STRUCT(VecI128, n)\nSPACETIMEDB_TABLE(VecI128, vec_i128, Public)\n\n// VecI256 table\nstruct VecI256 { std::vector<i256> n; };\nSPACETIMEDB_STRUCT(VecI256, n)\nSPACETIMEDB_TABLE(VecI256, vec_i256, Public)\n\n// UniqueI64 table\nstruct UniqueI64 { int64_t n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI64, n, data)\nSPACETIMEDB_TABLE(UniqueI64, unique_i64, Public)\nFIELD_Unique(unique_i64, n)\n\n// UniqueI128 table\nstruct UniqueI128 { i128 n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI128, n, data)\nSPACETIMEDB_TABLE(UniqueI128, unique_i128, Public)\nFIELD_Unique(unique_i128, n)\n\n// UniqueI256 table\nstruct UniqueI256 { i256 n; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueI256, n, data)\nSPACETIMEDB_TABLE(UniqueI256, unique_i256, Public)\nFIELD_Unique(unique_i256, n)\n\n// PkI64 table\nstruct PkI64 { int64_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI64, n, data)\nSPACETIMEDB_TABLE(PkI64, pk_i64, Public)\nFIELD_PrimaryKey(pk_i64, n)\n\n// PkI128 table\nstruct PkI128 { i128 n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI128, n, data)\nSPACETIMEDB_TABLE(PkI128, pk_i128, Public)\nFIELD_PrimaryKey(pk_i128, n)\n\n// PkI256 table\nstruct PkI256 { i256 n; int32_t data; };\nSPACETIMEDB_STRUCT(PkI256, n, data)\nSPACETIMEDB_TABLE(PkI256, pk_i256, Public)\nFIELD_PrimaryKey(pk_i256, n)\n\n// Reducers for large signed integers\nSPACETIMEDB_REDUCER(insert_one_i64, ReducerContext ctx, int64_t n)\n{\n    ctx.db.table<OneI64>(\"one_i64\").insert(OneI64{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_i128, ReducerContext ctx, i128 n)\n{\n    ctx.db.table<OneI128>(\"one_i128\").insert(OneI128{n});\n}\n\nSPACETIMEDB_REDUCER(insert_one_i256, ReducerContext ctx, i256 n)\n{\n    ctx.db.table<OneI256>(\"one_i256\").insert(OneI256{n});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module05_float_bool.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 5: Floating point and boolean types\n// Testing if float/bool types cause WASM issues\n\n\n// OneBool table\nstruct OneBool { bool b; };\nSPACETIMEDB_STRUCT(OneBool, b)\nSPACETIMEDB_TABLE(OneBool, one_bool, Public)\n\n// OneF32 table\nstruct OneF32 { float f; };\nSPACETIMEDB_STRUCT(OneF32, f)\nSPACETIMEDB_TABLE(OneF32, one_f32, Public)\n\n// OneF64 table\nstruct OneF64 { double f; };\nSPACETIMEDB_STRUCT(OneF64, f)\nSPACETIMEDB_TABLE(OneF64, one_f64, Public)\n\n// VecBool table\nstruct VecBool { std::vector<bool> b; };\nSPACETIMEDB_STRUCT(VecBool, b)\nSPACETIMEDB_TABLE(VecBool, vec_bool, Public)\n\n// VecF32 table\nstruct VecF32 { std::vector<float> f; };\nSPACETIMEDB_STRUCT(VecF32, f)\nSPACETIMEDB_TABLE(VecF32, vec_f32, Public)\n\n// VecF64 table\nstruct VecF64 { std::vector<double> f; };\nSPACETIMEDB_STRUCT(VecF64, f)\nSPACETIMEDB_TABLE(VecF64, vec_f64, Public)\n\n// UniqueBool table\nstruct UniqueBool { bool b; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueBool, b, data)\nSPACETIMEDB_TABLE(UniqueBool, unique_bool, Public)\nFIELD_Unique(unique_bool, b)\n\n// PkBool table\nstruct PkBool { bool b; int32_t data; };\nSPACETIMEDB_STRUCT(PkBool, b, data)\nSPACETIMEDB_TABLE(PkBool, pk_bool, Public)\nFIELD_PrimaryKey(pk_bool, b)\n\n// Reducers for float and bool types\nSPACETIMEDB_REDUCER(insert_one_bool, ReducerContext ctx, bool b)\n{\n    ctx.db.table<OneBool>(\"one_bool\").insert(OneBool{b});\n}\n\nSPACETIMEDB_REDUCER(insert_one_f32, ReducerContext ctx, float f)\n{\n    ctx.db.table<OneF32>(\"one_f32\").insert(OneF32{f});\n}\n\nSPACETIMEDB_REDUCER(insert_one_f64, ReducerContext ctx, double f)\n{\n    ctx.db.table<OneF64>(\"one_f64\").insert(OneF64{f});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module06_string.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Module 6: String and text types\n// Testing if string types cause WASM issues\n\n\n// OneString table\nstruct OneString { std::string s; };\nSPACETIMEDB_STRUCT(OneString, s)\nSPACETIMEDB_TABLE(OneString, one_string, Public)\n\n// VecString table\nstruct VecString { std::vector<std::string> s; };\nSPACETIMEDB_STRUCT(VecString, s)\nSPACETIMEDB_TABLE(VecString, vec_string, Public)\n\n// UniqueString table\nstruct UniqueString { std::string s; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueString, s, data)\nSPACETIMEDB_TABLE(UniqueString, unique_string, Public)\nFIELD_Unique(unique_string, s)\n\n// PkString table\nstruct PkString { std::string s; int32_t data; };\nSPACETIMEDB_STRUCT(PkString, s, data)\nSPACETIMEDB_TABLE(PkString, pk_string, Public)\nFIELD_PrimaryKey(pk_string, s)\n\n// Reducer for string types\nSPACETIMEDB_REDUCER(insert_one_string, ReducerContext ctx, std::string s)\n{\n    ctx.db.table<OneString>(\"one_string\").insert(OneString{s});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module07_special_types.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 7: Special types (Identity, ConnectionId, Timestamp)\n// Testing if special SpacetimeDB types cause WASM issues\n\n\n// OneIdentity table\nstruct OneIdentity { Identity i; };\nSPACETIMEDB_STRUCT(OneIdentity, i)\nSPACETIMEDB_TABLE(OneIdentity, one_identity, Public)\n\n// OneConnectionId table\nstruct OneConnectionId { ConnectionId a; };\nSPACETIMEDB_STRUCT(OneConnectionId, a)\nSPACETIMEDB_TABLE(OneConnectionId, one_connection_id, Public)\n\n// OneTimestamp table\nstruct OneTimestamp { Timestamp t; };\nSPACETIMEDB_STRUCT(OneTimestamp, t)\nSPACETIMEDB_TABLE(OneTimestamp, one_timestamp, Public)\n\n// VecIdentity table\nstruct VecIdentity { std::vector<Identity> i; };\nSPACETIMEDB_STRUCT(VecIdentity, i)\nSPACETIMEDB_TABLE(VecIdentity, vec_identity, Public)\n\n// VecConnectionId table\nstruct VecConnectionId { std::vector<ConnectionId> a; };\nSPACETIMEDB_STRUCT(VecConnectionId, a)\nSPACETIMEDB_TABLE(VecConnectionId, vec_connection_id, Public)\n\n// VecTimestamp table\nstruct VecTimestamp { std::vector<Timestamp> t; };\nSPACETIMEDB_STRUCT(VecTimestamp, t)\nSPACETIMEDB_TABLE(VecTimestamp, vec_timestamp, Public)\n\n// UniqueIdentity table\nstruct UniqueIdentity { Identity i; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueIdentity, i, data)\nSPACETIMEDB_TABLE(UniqueIdentity, unique_identity, Public)\nFIELD_Unique(unique_identity, i)\n\n// UniqueConnectionId table\nstruct UniqueConnectionId { ConnectionId a; int32_t data; };\nSPACETIMEDB_STRUCT(UniqueConnectionId, a, data)\nSPACETIMEDB_TABLE(UniqueConnectionId, unique_connection_id, Public)\nFIELD_Unique(unique_connection_id, a)\n\n// PkIdentity table\nstruct PkIdentity { Identity i; int32_t data; };\nSPACETIMEDB_STRUCT(PkIdentity, i, data)\nSPACETIMEDB_TABLE(PkIdentity, pk_identity, Public)\nFIELD_PrimaryKey(pk_identity, i)\n\n// PkConnectionId table\nstruct PkConnectionId { ConnectionId a; int32_t data; };\nSPACETIMEDB_STRUCT(PkConnectionId, a, data)\nSPACETIMEDB_TABLE(PkConnectionId, pk_connection_id, Public)\nFIELD_PrimaryKey(pk_connection_id, a)\n\n// Users table\nstruct Users {\n    Identity identity;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(Users, identity, name)\nSPACETIMEDB_TABLE(Users, users, Public)\nFIELD_PrimaryKey(users, identity)\n\n// Parameter wrappers to avoid direct special type parameters (causes WASM traps)\nstruct IdentityParam { Identity i; };\nSPACETIMEDB_STRUCT(IdentityParam, i)\n\nstruct ConnectionIdParam { ConnectionId a; };\nSPACETIMEDB_STRUCT(ConnectionIdParam, a)\n\nstruct TimestampParam { Timestamp t; };\nSPACETIMEDB_STRUCT(TimestampParam, t)\n\n// Reducers for special types (using wrapped parameters)\nSPACETIMEDB_REDUCER(insert_one_identity, ReducerContext ctx, IdentityParam param)\n{\n    ctx.db.table<OneIdentity>(\"one_identity\").insert(OneIdentity{param.i});\n}\n\nSPACETIMEDB_REDUCER(insert_one_connection_id, ReducerContext ctx, ConnectionIdParam param)\n{\n    ctx.db.table<OneConnectionId>(\"one_connection_id\").insert(OneConnectionId{param.a});\n}\n\nSPACETIMEDB_REDUCER(insert_one_timestamp, ReducerContext ctx, TimestampParam param)\n{\n    ctx.db.table<OneTimestamp>(\"one_timestamp\").insert(OneTimestamp{param.t});\n}\n\n// TEST: Direct special type parameters (should cause WASM trap when published)\nSPACETIMEDB_REDUCER(insert_direct_identity, ReducerContext ctx, Identity i)\n{\n    ctx.db.table<OneIdentity>(\"one_identity\").insert(OneIdentity{i});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_connection_id, ReducerContext ctx, ConnectionId c)\n{\n    ctx.db.table<OneConnectionId>(\"one_connection_id\").insert(OneConnectionId{c});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_timestamp, ReducerContext ctx, Timestamp t)\n{\n    LOG_INFO(\"Direct timestamp reducer called\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module08_enums.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <variant>\n\nusing namespace SpacetimeDB;\n\n// Module 8: Enums and enum tables\n// Testing if enum types cause WASM issues\n\n\n// SimpleEnum - basic enum using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// EnumWithPayload - variant enum with payloads using new unified syntax\nSPACETIMEDB_ENUM(EnumWithPayload,\n    (U8, uint8_t),\n    (U16, uint16_t),\n    (U32, uint32_t),\n    (U64, uint64_t),\n    (U128, SpacetimeDB::u128),\n    (U256, SpacetimeDB::u256),\n    (I8, int8_t),\n    (I16, int16_t),\n    (I32, int32_t),\n    (I64, int64_t),\n    (I128, SpacetimeDB::i128),\n    (I256, SpacetimeDB::i256),\n    (Bool, bool),\n    (F32, float),\n    (F64, double),\n    (Str, std::string),\n    (Identity, SpacetimeDB::Identity),\n    (ConnectionId, SpacetimeDB::ConnectionId),\n    (Timestamp, SpacetimeDB::Timestamp),\n    (Bytes, std::vector<uint8_t>),\n    (Ints, std::vector<int32_t>),\n    (Strings, std::vector<std::string>),\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\n// OneSimpleEnum table\nstruct OneSimpleEnum { SimpleEnum e; };\nSPACETIMEDB_STRUCT(OneSimpleEnum, e)\nSPACETIMEDB_TABLE(OneSimpleEnum, one_simple_enum, Public)\n\n// OneEnumWithPayload table\nstruct OneEnumWithPayload { EnumWithPayload e; };\nSPACETIMEDB_STRUCT(OneEnumWithPayload, e)\nSPACETIMEDB_TABLE(OneEnumWithPayload, one_enum_with_payload, Public)\n\n// VecSimpleEnum table\nstruct VecSimpleEnum { std::vector<SimpleEnum> e; };\nSPACETIMEDB_STRUCT(VecSimpleEnum, e)\nSPACETIMEDB_TABLE(VecSimpleEnum, vec_simple_enum, Public)\n\n// VecEnumWithPayload table\nstruct VecEnumWithPayload { std::vector<EnumWithPayload> e; };\nSPACETIMEDB_STRUCT(VecEnumWithPayload, e)\nSPACETIMEDB_TABLE(VecEnumWithPayload, vec_enum_with_payload, Public)\n\n// PkSimpleEnum table\nstruct PkSimpleEnum { SimpleEnum a; int32_t data; };\nSPACETIMEDB_STRUCT(PkSimpleEnum, a, data)\nSPACETIMEDB_TABLE(PkSimpleEnum, pk_simple_enum, Public)\nFIELD_PrimaryKey(pk_simple_enum, a)\n\n// IndexedSimpleEnum table\nstruct IndexedSimpleEnum {\n    SimpleEnum n;\n};\nSPACETIMEDB_STRUCT(IndexedSimpleEnum, n)\nSPACETIMEDB_TABLE(IndexedSimpleEnum, indexed_simple_enum, Public)\nFIELD_Index(indexed_simple_enum, n)\n\n// Reducers for enum types\nSPACETIMEDB_REDUCER(insert_one_simple_enum, ReducerContext ctx, SimpleEnum e)\n{\n    ctx.db.table<OneSimpleEnum>(\"one_simple_enum\").insert(OneSimpleEnum{e});\n}\n\nSPACETIMEDB_REDUCER(insert_one_enum_with_payload, ReducerContext ctx, EnumWithPayload e)\n{\n    ctx.db.table<OneEnumWithPayload>(\"one_enum_with_payload\").insert(OneEnumWithPayload{e});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module09_structs.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <variant>\n\nusing namespace SpacetimeDB;\n\n// Module 9: Structs and complex types\n// Testing if struct types cause WASM issues\n\n\n// Need enums for struct fields using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\nSPACETIMEDB_ENUM(EnumWithPayload,\n    (U8, uint8_t),\n    (U16, uint16_t),\n    (U32, uint32_t),\n    (U64, uint64_t),\n    (U128, SpacetimeDB::u128),\n    (U256, SpacetimeDB::u256),\n    (I8, int8_t),\n    (I16, int16_t),\n    (I32, int32_t),\n    (I64, int64_t),\n    (I128, SpacetimeDB::i128),\n    (I256, SpacetimeDB::i256),\n    (Bool, bool),\n    (F32, float),\n    (F64, double),\n    (Str, std::string),\n    (Identity, SpacetimeDB::Identity),\n    (ConnectionId, SpacetimeDB::ConnectionId),\n    (Timestamp, SpacetimeDB::Timestamp),\n    (Bytes, std::vector<uint8_t>),\n    (Ints, std::vector<int32_t>),\n    (Strings, std::vector<std::string>),\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\n// UnitStruct\nstruct UnitStruct {\n    uint8_t dummy = 0;\n};\nSPACETIMEDB_STRUCT(UnitStruct, dummy)\n\n// ByteStruct\nstruct ByteStruct {\n    uint8_t b;\n};\nSPACETIMEDB_STRUCT(ByteStruct, b)\n\n// EveryPrimitiveStruct\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// EveryVecStruct\nstruct EveryVecStruct {\n    std::vector<uint8_t> a;\n    std::vector<uint16_t> b;\n    std::vector<uint32_t> c;\n    std::vector<uint64_t> d;\n    std::vector<u128> e;\n    std::vector<u256> f;\n    std::vector<int8_t> g;\n    std::vector<int16_t> h;\n    std::vector<int32_t> i;\n    std::vector<int64_t> j;\n    std::vector<i128> k;\n    std::vector<i256> l;\n    std::vector<bool> m;\n    std::vector<float> n;\n    std::vector<double> o;\n    std::vector<std::string> p;\n    std::vector<Identity> q;\n    std::vector<ConnectionId> r;\n    std::vector<Timestamp> s;\n    std::vector<TimeDuration> t;\n    std::vector<SimpleEnum> u;\n};\nSPACETIMEDB_STRUCT(EveryVecStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)\n\n// OneUnitStruct table\nstruct OneUnitStruct { UnitStruct s; };\nSPACETIMEDB_STRUCT(OneUnitStruct, s)\nSPACETIMEDB_TABLE(OneUnitStruct, one_unit_struct, Public)\n\n// OneByteStruct table\nstruct OneByteStruct { ByteStruct s; };\nSPACETIMEDB_STRUCT(OneByteStruct, s)\nSPACETIMEDB_TABLE(OneByteStruct, one_byte_struct, Public)\n\n// OneEveryPrimitiveStruct table\nstruct OneEveryPrimitiveStruct { EveryPrimitiveStruct s; };\nSPACETIMEDB_STRUCT(OneEveryPrimitiveStruct, s)\nSPACETIMEDB_TABLE(OneEveryPrimitiveStruct, one_every_primitive_struct, Public)\n\n// OneEveryVecStruct table\nstruct OneEveryVecStruct { EveryVecStruct s; };\nSPACETIMEDB_STRUCT(OneEveryVecStruct, s)\nSPACETIMEDB_TABLE(OneEveryVecStruct, one_every_vec_struct, Public)\n\n// VecUnitStruct table\nstruct VecUnitStruct { std::vector<UnitStruct> s; };\nSPACETIMEDB_STRUCT(VecUnitStruct, s)\nSPACETIMEDB_TABLE(VecUnitStruct, vec_unit_struct, Public)\n\n// VecByteStruct table\nstruct VecByteStruct { std::vector<ByteStruct> s; };\nSPACETIMEDB_STRUCT(VecByteStruct, s)\nSPACETIMEDB_TABLE(VecByteStruct, vec_byte_struct, Public)\n\n// VecEveryPrimitiveStruct table\nstruct VecEveryPrimitiveStruct { std::vector<EveryPrimitiveStruct> s; };\nSPACETIMEDB_STRUCT(VecEveryPrimitiveStruct, s)\nSPACETIMEDB_TABLE(VecEveryPrimitiveStruct, vec_every_primitive_struct, Public)\n\n// VecEveryVecStruct table\nstruct VecEveryVecStruct { std::vector<EveryVecStruct> s; };\nSPACETIMEDB_STRUCT(VecEveryVecStruct, s)\nSPACETIMEDB_TABLE(VecEveryVecStruct, vec_every_vec_struct, Public)\n\n// LargeTable\nstruct LargeTable {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    SimpleEnum q;\n    EnumWithPayload r;\n    UnitStruct s;\n    ByteStruct t;\n    EveryPrimitiveStruct u;\n    EveryVecStruct v;\n};\nSPACETIMEDB_STRUCT(LargeTable, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)\nSPACETIMEDB_TABLE(LargeTable, large_table, Public)\n\n// Reducers for struct types\nSPACETIMEDB_REDUCER(insert_one_unit_struct, ReducerContext ctx, UnitStruct s)\n{\n    ctx.db.table<OneUnitStruct>(\"one_unit_struct\").insert(OneUnitStruct{s});\n}\n\nSPACETIMEDB_REDUCER(insert_one_byte_struct, ReducerContext ctx, ByteStruct s)\n{\n    ctx.db.table<OneByteStruct>(\"one_byte_struct\").insert(OneByteStruct{s});\n}\n\nSPACETIMEDB_REDUCER(insert_one_every_primitive_struct, ReducerContext ctx, EveryPrimitiveStruct s)\n{\n    ctx.db.table<OneEveryPrimitiveStruct>(\"one_every_primitive_struct\").insert(OneEveryPrimitiveStruct{s});\n}\n\nSPACETIMEDB_REDUCER(insert_one_every_vec_struct, ReducerContext ctx, EveryVecStruct s)\n{\n    ctx.db.table<OneEveryVecStruct>(\"one_every_vec_struct\").insert(OneEveryVecStruct{s});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module10_vectors.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 10: Vector/array types (already covered in other modules, adding nested)\n// Testing if nested vector types cause WASM issues\n\n\n// Tables that hold other table structs  \nstruct OneU8 { uint8_t n; };\nSPACETIMEDB_STRUCT(OneU8, n)\n\nstruct VecU8 { std::vector<uint8_t> n; };\nSPACETIMEDB_STRUCT(VecU8, n)\n\n// TableHoldsTable - nested table structures\nstruct TableHoldsTable {\n    OneU8 a;\n    VecU8 b;\n};\nSPACETIMEDB_STRUCT(TableHoldsTable, a, b)\nSPACETIMEDB_TABLE(TableHoldsTable, table_holds_table, Public)\n\n// Test some additional vector types not covered elsewhere\nstruct VecVecU8 { std::vector<std::vector<uint8_t>> n; };\nSPACETIMEDB_STRUCT(VecVecU8, n)\nSPACETIMEDB_TABLE(VecVecU8, vec_vec_u8, Public)\n\nstruct VecVecString { std::vector<std::vector<std::string>> s; };\nSPACETIMEDB_STRUCT(VecVecString, s)\nSPACETIMEDB_TABLE(VecVecString, vec_vec_string, Public)\n\n// Reducers for nested vector types\nSPACETIMEDB_REDUCER(insert_table_holds_table, ReducerContext ctx, OneU8 a, VecU8 b)\n{\n    ctx.db.table<TableHoldsTable>(\"table_holds_table\").insert(TableHoldsTable{a, b});\n}\n\n// Direct vector parameter reducers\nSPACETIMEDB_REDUCER(insert_vec_u8, ReducerContext ctx, std::vector<uint8_t> n)\n{\n    LOG_INFO(\"Received vector<uint8_t> parameter\");\n}\n\nSPACETIMEDB_REDUCER(insert_vec_vec_u8, ReducerContext ctx, std::vector<std::vector<uint8_t>> n)\n{\n    ctx.db.table<VecVecU8>(\"vec_vec_u8\").insert(VecVecU8{n});\n}\n\nSPACETIMEDB_REDUCER(insert_vec_vec_string, ReducerContext ctx, std::vector<std::vector<std::string>> s)\n{\n    ctx.db.table<VecVecString>(\"vec_vec_string\").insert(VecVecString{s});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module11_optional.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <optional>\n#include <variant>\n\nusing namespace SpacetimeDB;\n\n// Module 11: Optional types\n// Testing if optional types cause WASM issues\n\n\n// Need enums for optional enum test using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// Need struct for optional struct test\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// CRITICAL FIX: Add a direct table using EveryPrimitiveStruct to ensure it gets registered by name\n// This prevents it from being inlined when used in Options\nSPACETIMEDB_TABLE(EveryPrimitiveStruct, every_primitive_direct, Public)\n\n// OptionI32 table\nstruct OptionI32 { std::optional<int32_t> n; };\nSPACETIMEDB_STRUCT(OptionI32, n)\nSPACETIMEDB_TABLE(OptionI32, option_i32, Public)\n\n// OptionString table\nstruct OptionString { std::optional<std::string> s; };\nSPACETIMEDB_STRUCT(OptionString, s)\nSPACETIMEDB_TABLE(OptionString, option_string, Public)\n\n// OptionIdentity table\nstruct OptionIdentity { std::optional<Identity> i; };\nSPACETIMEDB_STRUCT(OptionIdentity, i)\nSPACETIMEDB_TABLE(OptionIdentity, option_identity, Public)\n\n// OptionSimpleEnum table\nstruct OptionSimpleEnum { std::optional<SimpleEnum> e; };\nSPACETIMEDB_STRUCT(OptionSimpleEnum, e)\nSPACETIMEDB_TABLE(OptionSimpleEnum, option_simple_enum, Public)\n\n// OptionEveryPrimitiveStruct table\nstruct OptionEveryPrimitiveStruct { std::optional<EveryPrimitiveStruct> s; };\nSPACETIMEDB_STRUCT(OptionEveryPrimitiveStruct, s)\nSPACETIMEDB_TABLE(OptionEveryPrimitiveStruct, option_every_primitive_struct, Public)\n\n// Complex nested optional type\nstruct OptionVecOptionI32 { std::optional<std::vector<std::optional<int32_t>>> v; };\nSPACETIMEDB_STRUCT(OptionVecOptionI32, v)\nSPACETIMEDB_TABLE(OptionVecOptionI32, option_vec_option_i32, Public)\n\n// Parameter wrappers to avoid direct std::optional parameters (causes WASM traps)\nstruct OptionalI32Param { std::optional<int32_t> n; };\nSPACETIMEDB_STRUCT(OptionalI32Param, n)\n\nstruct OptionalStringParam { std::optional<std::string> s; };\nSPACETIMEDB_STRUCT(OptionalStringParam, s)\n\nstruct OptionalIdentityParam { std::optional<Identity> i; };\nSPACETIMEDB_STRUCT(OptionalIdentityParam, i)\n\n// Reducers for optional types (using wrapped parameters)\nSPACETIMEDB_REDUCER(insert_option_i32, ReducerContext ctx, OptionalI32Param param)\n{\n    ctx.db.table<OptionI32>(\"option_i32\").insert(OptionI32{param.n});\n}\n\nSPACETIMEDB_REDUCER(insert_option_string, ReducerContext ctx, OptionalStringParam param)\n{\n    ctx.db.table<OptionString>(\"option_string\").insert(OptionString{param.s});\n}\n\nSPACETIMEDB_REDUCER(insert_option_identity, ReducerContext ctx, OptionalIdentityParam param)\n{\n    ctx.db.table<OptionIdentity>(\"option_identity\").insert(OptionIdentity{param.i});\n}\n\n// TEST: Direct optional parameters (should cause WASM trap when published)\nSPACETIMEDB_REDUCER(insert_direct_option_i32, ReducerContext ctx, std::optional<int32_t> n)\n{\n    ctx.db.table<OptionI32>(\"option_i32\").insert(OptionI32{n});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_option_string, ReducerContext ctx, std::optional<std::string> s)\n{\n    ctx.db.table<OptionString>(\"option_string\").insert(OptionString{s});\n}\n\nSPACETIMEDB_REDUCER(insert_direct_option_every_primitive_struct, ReducerContext ctx, std::optional<EveryPrimitiveStruct> s)\n{\n    ctx.db.table<OptionEveryPrimitiveStruct>(\"option_every_primitive_struct\").insert(OptionEveryPrimitiveStruct{s});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/module12_constraints.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n// Module 12: Constraint tables (Indexes, etc.)\n// Testing if constraint-based tables cause WASM issues\n\n\n// IndexedTable\nstruct IndexedTable {\n    uint32_t player_id;\n};\nSPACETIMEDB_STRUCT(IndexedTable, player_id)\nSPACETIMEDB_TABLE(IndexedTable, indexed_table, Private)\nFIELD_Index(indexed_table, player_id)\n\n// IndexedTable2 with multiple indexes\nstruct IndexedTable2 {\n    uint32_t player_id;\n    float player_snazz;\n};\nSPACETIMEDB_STRUCT(IndexedTable2, player_id, player_snazz)\nSPACETIMEDB_TABLE(IndexedTable2, indexed_table_2, Private)\nFIELD_Index(indexed_table_2, player_id)\n// Note: float fields cannot be indexed - removing this constraint\n// FIELD_Index(indexed_table_2, player_snazz)  // Would fail compile-time validation\n\n// BTreeU32 table with index\nstruct BTreeU32 {\n    uint32_t n;\n    int32_t data;\n};\nSPACETIMEDB_STRUCT(BTreeU32, n, data)\nSPACETIMEDB_TABLE(BTreeU32, btree_u32, Public)\nFIELD_Index(btree_u32, n)\n\n// PkU32Two - additional primary key table\nstruct PkU32Two { uint32_t n; int32_t data; };\nSPACETIMEDB_STRUCT(PkU32Two, n, data)\nSPACETIMEDB_TABLE(PkU32Two, pk_u32_two, Public)\nFIELD_PrimaryKey(pk_u32_two, n)\n\n// Reducers for constraint tables\nSPACETIMEDB_REDUCER(insert_indexed_table, ReducerContext ctx, uint32_t player_id)\n{\n    ctx.db.table<IndexedTable>(\"indexed_table\").insert(IndexedTable{player_id});\n}\n\nSPACETIMEDB_REDUCER(insert_indexed_table_2, ReducerContext ctx, uint32_t player_id, float player_snazz)\n{\n    ctx.db.table<IndexedTable2>(\"indexed_table_2\").insert(IndexedTable2{player_id, player_snazz});\n}\n\nSPACETIMEDB_REDUCER(insert_btree_u32, ReducerContext ctx, uint32_t n, int32_t data)\n{\n    ctx.db.table<BTreeU32>(\"btree_u32\").insert(BTreeU32{n, data});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_complex_reducer_only.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n\n// Complex struct with special types\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// Simple table without the complex struct\nstruct SimpleTable {\n    int32_t id;\n    std::string value;\n};\nSPACETIMEDB_STRUCT(SimpleTable, id, value)\nSPACETIMEDB_TABLE(SimpleTable, simple_table, Public)\n\n// Reducer that uses the complex struct as parameter\nSPACETIMEDB_REDUCER(insert_with_complex, ReducerContext ctx, EveryPrimitiveStruct data)\n{\n    SimpleTable row;\n    row.id = data.i;  // Use the int32_t field as id\n    row.value = data.p;  // Use the string field as value\n    ctx.db.table<SimpleTable>(\"simple_table\").insert(row);\n}\n\n// Another reducer without complex struct\nSPACETIMEDB_REDUCER(insert_simple, ReducerContext ctx, int32_t id, std::string value)\n{\n    SimpleTable row;\n    row.id = id;\n    row.value = value;\n    ctx.db.table<SimpleTable>(\"simple_table\").insert(row);\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_complex_table_only.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n\n// Complex struct with special types\nstruct EveryPrimitiveStruct {\n    uint8_t a;\n    uint16_t b;\n    uint32_t c;\n    uint64_t d;\n    u128 e;\n    u256 f;\n    int8_t g;\n    int16_t h;\n    int32_t i;\n    int64_t j;\n    i128 k;\n    i256 l;\n    bool m;\n    float n;\n    double o;\n    std::string p;\n    Identity q;\n    ConnectionId r;\n    Timestamp s;\n    TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// Table with the complex struct as a field\nstruct ComplexTable {\n    int32_t id;\n    EveryPrimitiveStruct data;\n};\nSPACETIMEDB_STRUCT(ComplexTable, id, data)\nSPACETIMEDB_TABLE(ComplexTable, complex_table, Public)\n\n// Reducer that doesn't use the complex struct as parameter\nSPACETIMEDB_REDUCER(insert_default, ReducerContext ctx, int32_t id)\n{\n    ComplexTable row;\n    row.id = id;\n    // Initialize struct fields explicitly\n    row.data.a = 1;\n    row.data.b = 2;\n    row.data.c = 3;\n    row.data.d = 4;\n    row.data.e = u128{5};\n    row.data.f = u256();  // Default constructor\n    row.data.g = 7;\n    row.data.h = 8;\n    row.data.i = 9;\n    row.data.j = 10;\n    row.data.k = i128{11};\n    row.data.l = i256();  // Default constructor\n    row.data.m = true;\n    row.data.n = 14.0f;\n    row.data.o = 15.0;\n    row.data.p = \"test\";\n    row.data.q = Identity();  // Default constructor\n    row.data.r = ConnectionId{u128{17}};\n    row.data.s = Timestamp::now();\n    row.data.t = TimeDuration{100};\n    ctx.db.table<ComplexTable>(\"complex_table\").insert(row);\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_connectionid_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with just ConnectionId\nstruct TestConnectionId {\n    uint32_t id;\n    ConnectionId conn_id;\n};\nSPACETIMEDB_STRUCT(TestConnectionId, id, conn_id)\nSPACETIMEDB_TABLE(TestConnectionId, test_connection_id, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_debug_optional.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <optional>\n#include <cstdio>\n\nusing namespace SpacetimeDB;\n\n// Global constructor to run before __preinit__\nstruct DebugInit {\n    DebugInit() {\n        fprintf(stdout, \"DEBUG: DebugInit constructor called\\n\");\n    }\n};\nstatic DebugInit debug_init;\n\n\n// Table with optional field - will this trigger the crash?\nstruct OptionalTable {\n    uint32_t id;\n    std::optional<int32_t> maybe_value;\n};\n\n// First register the BSATN struct\nSPACETIMEDB_STRUCT(OptionalTable, id, maybe_value)\n\n// Global constructor after BSATN registration\nstruct AfterBsatn {\n    AfterBsatn() {\n        fprintf(stdout, \"DEBUG: AfterBsatn constructor called\\n\");\n    }\n};\nstatic AfterBsatn after_bsatn;\n\n// Then register the table\nSPACETIMEDB_TABLE(OptionalTable, optional_table, Public)\n\n// Global constructor after TABLE registration\nstruct AfterTable {\n    AfterTable() {\n        fprintf(stdout, \"DEBUG: AfterTable constructor called\\n\");\n    }\n};\nstatic AfterTable after_table;"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_enum_table_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <variant>\n\nusing namespace SpacetimeDB;\n\n// Test: EnumWithPayload in table but NOT as reducer parameter\n// This isolates whether the issue is with table storage or reducer params\n\n\n// SimpleEnum - basic enum using new unified syntax\nSPACETIMEDB_ENUM(SimpleEnum, Zero, One, Two)\n\n// EnumWithPayload - with all the problematic vector types using new unified syntax\nSPACETIMEDB_ENUM(EnumWithPayload,\n    (U8, uint8_t),\n    (U16, uint16_t),\n    (U32, uint32_t),\n    (U64, uint64_t),\n    (U128, SpacetimeDB::u128),\n    (U256, SpacetimeDB::u256),\n    (I8, int8_t),\n    (I16, int16_t),\n    (I32, int32_t),\n    (I64, int64_t),\n    (I128, SpacetimeDB::i128),\n    (I256, SpacetimeDB::i256),\n    (Bool, bool),\n    (F32, float),\n    (F64, double),\n    (Str, std::string),\n    (Identity, SpacetimeDB::Identity),\n    (ConnectionId, SpacetimeDB::ConnectionId),\n    (Timestamp, SpacetimeDB::Timestamp),\n    (Bytes, std::vector<uint8_t>),\n    (Ints, std::vector<int32_t>),\n    (Strings, std::vector<std::string>),\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\n// Table with EnumWithPayload - this should work fine\nstruct EnumTable { \n    EnumWithPayload e; \n    int32_t id;\n};\nSPACETIMEDB_STRUCT(EnumTable, e, id)\nSPACETIMEDB_TABLE(EnumTable, enum_table, Public)\n\n// Reducers that DON'T use EnumWithPayload as parameters\n// Instead use simple types only\n\nSPACETIMEDB_REDUCER(insert_simple, ReducerContext ctx, int32_t id)\n{\n    // Create an EnumWithPayload and insert it\n    EnumWithPayload e = uint8_t{42}; // U8 variant\n    ctx.db.table<EnumTable>(\"enum_table\").insert(EnumTable{e, id});\n}\n\nSPACETIMEDB_REDUCER(insert_bytes, ReducerContext ctx, int32_t id)\n{\n    // Create Bytes variant and insert\n    std::vector<uint8_t> bytes = {1, 2, 3, 4};\n    EnumWithPayload e = bytes; // Bytes variant  \n    ctx.db.table<EnumTable>(\"enum_table\").insert(EnumTable{e, id});\n}\n\nSPACETIMEDB_REDUCER(query_all, ReducerContext ctx)\n{\n    auto table = ctx.db.table<EnumTable>(\"enum_table\");\n    for (auto& row : table) {\n        // Process the EnumWithPayload in table storage\n        LOG_INFO(\"Found enum table row\");\n    }\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_enum_vector_payloads.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test enum variants containing vectors - the missing pattern from lib.cpp\nSPACETIMEDB_ENUM(SimpleEnum, A, B, C)\n\n// Critical test: Enum with vector payloads (especially vector of enums)\nSPACETIMEDB_ENUM(EnumWithVectorPayloads,\n    (Bytes, std::vector<uint8_t>),\n    (Ints, std::vector<int32_t>),\n    (Strings, std::vector<std::string>),\n    (SimpleEnums, std::vector<SimpleEnum>)  // Vector of enums in enum variant!\n)\n\n// Table using the complex enum\nstruct TableWithComplexEnum {\n    EnumWithVectorPayloads complex_enum;\n};\nSPACETIMEDB_STRUCT(TableWithComplexEnum, complex_enum)\nSPACETIMEDB_TABLE(TableWithComplexEnum, table_with_complex_enum, Public)\n\n// Even more complex: Vector of enums that contain vectors\nstruct TableWithVectorOfComplexEnums {\n    std::vector<EnumWithVectorPayloads> vec_of_complex_enums;\n};\nSPACETIMEDB_STRUCT(TableWithVectorOfComplexEnums, vec_of_complex_enums)\nSPACETIMEDB_TABLE(TableWithVectorOfComplexEnums, table_with_vector_of_complex_enums, Public)\n\n// Test reducers\nSPACETIMEDB_REDUCER(test_complex_enum, ReducerContext ctx, EnumWithVectorPayloads e)\n{\n    ctx.db[table_with_complex_enum].insert(TableWithComplexEnum{e});\n}\n\nSPACETIMEDB_REDUCER(test_vector_complex_enum, ReducerContext ctx, std::vector<EnumWithVectorPayloads> vec)\n{\n    ctx.db[table_with_vector_of_complex_enums].insert(TableWithVectorOfComplexEnums{vec});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_identity_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with just Identity\nstruct TestIdentity {\n    uint32_t id;\n    Identity identity;\n};\nSPACETIMEDB_STRUCT(TestIdentity, id, identity)\nSPACETIMEDB_TABLE(TestIdentity, test_identity, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_massive_reducer.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Recreate the problematic pattern from lib.cpp\nSPACETIMEDB_ENUM(SimpleEnum, A, B, C)\n\nSPACETIMEDB_ENUM(EnumWithPayload,\n    (U8, uint8_t),\n    (U16, uint16_t),\n    (U32, uint32_t),\n    (U64, uint64_t),\n    (U128, SpacetimeDB::u128),\n    (U256, SpacetimeDB::u256),\n    (I8, int8_t),\n    (I16, int16_t),\n    (I32, int32_t),\n    (I64, int64_t),\n    (I128, SpacetimeDB::i128),\n    (I256, SpacetimeDB::i256),\n    (Bool, bool),\n    (F32, float),\n    (F64, double),\n    (Str, std::string),\n    (Identity, SpacetimeDB::Identity),\n    (ConnectionId, SpacetimeDB::ConnectionId),\n    (Timestamp, SpacetimeDB::Timestamp),\n    (Bytes, std::vector<uint8_t>),\n    (Ints, std::vector<int32_t>),\n    (Strings, std::vector<std::string>),\n    (SimpleEnums, std::vector<SimpleEnum>)\n)\n\nSPACETIMEDB_UNIT_STRUCT(UnitStruct)\n\nstruct ByteStruct { uint8_t b; };\nSPACETIMEDB_STRUCT(ByteStruct, b)\n\nstruct EveryPrimitiveStruct {\n    uint8_t a; uint16_t b; uint32_t c; uint64_t d; u128 e; u256 f;\n    int8_t g; int16_t h; int32_t i; int64_t j; i128 k; i256 l;\n    bool m; float n; double o; std::string p;\n    Identity q; ConnectionId r; Timestamp s; TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\nstruct EveryVecStruct {\n    std::vector<uint8_t> a; std::vector<uint16_t> b; std::vector<uint32_t> c; std::vector<uint64_t> d;\n    std::vector<u128> e; std::vector<u256> f; std::vector<int8_t> g; std::vector<int16_t> h;\n    std::vector<int32_t> i; std::vector<int64_t> j; std::vector<i128> k; std::vector<i256> l;\n    std::vector<bool> m; std::vector<float> n; std::vector<double> o; std::vector<std::string> p;\n    std::vector<Identity> q; std::vector<ConnectionId> r; std::vector<Timestamp> s; std::vector<TimeDuration> t;\n};\nSPACETIMEDB_STRUCT(EveryVecStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// The monster struct that combines everything\nstruct LargeTable {\n    uint8_t a; uint16_t b; uint32_t c; uint64_t d; u128 e; u256 f;\n    int8_t g; int16_t h; int32_t i; int64_t j; i128 k; i256 l;\n    bool m; float n; double o; std::string p;\n    SimpleEnum q; EnumWithPayload r; UnitStruct s; ByteStruct t;\n    EveryPrimitiveStruct u; EveryVecStruct v;\n};\nSPACETIMEDB_STRUCT(LargeTable, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)\nSPACETIMEDB_TABLE(LargeTable, large_table, Public)\n\n// The problematic reducer with 22 parameters including complex nested structures\nSPACETIMEDB_REDUCER(insert_large_table, ReducerContext ctx,\n    uint8_t a, uint16_t b, uint32_t c, uint64_t d, u128 e, u256 f,\n    int8_t g, int16_t h, int32_t i, int64_t j, i128 k, i256 l,\n    bool m, float n, double o, std::string p,\n    SimpleEnum q, EnumWithPayload r, UnitStruct s, ByteStruct t,\n    EveryPrimitiveStruct u, EveryVecStruct v)\n{\n    ctx.db[large_table].insert(LargeTable{\n        a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v\n    });\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_minimal_special.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Minimal test - just one table with Identity and one reducer with Identity parameter\n\n// Simple Identity table\nstruct SimpleIdentityTable { Identity id; };\nSPACETIMEDB_STRUCT(SimpleIdentityTable, id)\nSPACETIMEDB_TABLE(SimpleIdentityTable, simple_identity_table, Public)\n\n// Single reducer with direct Identity parameter\nSPACETIMEDB_REDUCER(test_identity_reducer, ReducerContext ctx, Identity i)\n{\n    ctx.db.table<SimpleIdentityTable>(\"simple_identity_table\").insert(SimpleIdentityTable{i});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_mixed_types.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Table with working type (Identity)\nstruct TestIdentity {\n    uint32_t id;\n    Identity identity;\n};\nSPACETIMEDB_STRUCT(TestIdentity, id, identity)\nSPACETIMEDB_TABLE(TestIdentity, test_identity, Public)\n\n// Table with failing type (u128)  \nstruct TestU128 {\n    uint32_t id;\n    u128 value;\n};\nSPACETIMEDB_STRUCT(TestU128, id, value)\nSPACETIMEDB_TABLE(TestU128, test_u128, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_multicolumn_index_valid.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\nstruct ScoreRow {\n    uint32_t id;\n    uint32_t player;\n    uint32_t round;\n    int32_t score;\n};\nSPACETIMEDB_STRUCT(ScoreRow, id, player, round, score)\nSPACETIMEDB_TABLE(ScoreRow, score_row, Public)\nFIELD_PrimaryKey(score_row, id)\nFIELD_MultiColumnIndex(score_row, by_player_round, player, round)\n\nSPACETIMEDB_REDUCER(insert_score, ReducerContext ctx, uint32_t id, uint32_t player, uint32_t round, int32_t score)\n{\n    ctx.db[score_row].insert(ScoreRow{id, player, round, score});\n    return Ok();\n}\n\n"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_nested_optionals.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test the problematic nested optional patterns from lib.cpp\n\n// Large struct to use in optional\nstruct EveryPrimitiveStruct {\n    uint8_t a; uint16_t b; uint32_t c; uint64_t d; u128 e; u256 f;\n    int8_t g; int16_t h; int32_t i; int64_t j; i128 k; i256 l;\n    bool m; float n; double o; std::string p;\n    Identity q; ConnectionId r; Timestamp s; TimeDuration t;\n};\nSPACETIMEDB_STRUCT(EveryPrimitiveStruct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)\n\n// The problematic nested optional: optional vector of optional integers\nstruct OptionVecOptionI32 { \n    std::optional<std::vector<std::optional<int32_t>>> v; \n};\nSPACETIMEDB_STRUCT(OptionVecOptionI32, v)\nSPACETIMEDB_TABLE(OptionVecOptionI32, option_vec_option_i32, Public)\n\n// Optional complex struct\nstruct OptionEveryPrimitiveStruct { \n    std::optional<EveryPrimitiveStruct> s; \n};\nSPACETIMEDB_STRUCT(OptionEveryPrimitiveStruct, s)\nSPACETIMEDB_TABLE(OptionEveryPrimitiveStruct, option_every_primitive_struct, Public)\n\n// Test reducers with nested optional parameters\nSPACETIMEDB_REDUCER(insert_option_vec_option_i32, ReducerContext ctx, std::optional<std::vector<std::optional<int32_t>>> v)\n{\n    ctx.db[option_vec_option_i32].insert(OptionVecOptionI32{v});\n}\n\nSPACETIMEDB_REDUCER(insert_option_every_primitive_struct, ReducerContext ctx, std::optional<EveryPrimitiveStruct> s)\n{\n    ctx.db[option_every_primitive_struct].insert(OptionEveryPrimitiveStruct{s});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_debug.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// MINIMAL TEST: Just optional in table field, NO reducer parameters\n// This isolates whether issue is in table registration vs reducer parameter processing\n\nstruct OptionalTable {\n    std::optional<int32_t> maybe_value;\n};\nSPACETIMEDB_STRUCT(OptionalTable, maybe_value)\nSPACETIMEDB_TABLE(OptionalTable, optional_table, Public)\n\n// Simple reducer that doesn't use optional as parameter\nSPACETIMEDB_REDUCER(test_basic, ReducerContext ctx)\n{\n    LOG_INFO(\"Basic reducer called\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_reducer_only.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// Test: Optional in reducer only (no table fields)\n\n\n// Struct with optional field (used as reducer parameter)\nstruct OptionalParam {\n    int32_t id;\n    std::optional<int32_t> opt_value;\n};\nSPACETIMEDB_STRUCT(OptionalParam, id, opt_value)\n\n// Simple non-optional table\nstruct SimpleTable {\n    int32_t id;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(SimpleTable, id, value)\nSPACETIMEDB_TABLE(SimpleTable, simple_table, Public)\n\n// Reducer that uses struct with optional as parameter\nSPACETIMEDB_REDUCER(insert_with_optional, ReducerContext ctx, OptionalParam param)\n{\n    SimpleTable row;\n    row.id = param.id;\n    // Use the optional value or default to 0\n    row.value = param.opt_value.value_or(0);\n    ctx.db.table<SimpleTable>(\"simple_table\").insert(row);\n}\n\n// Another reducer without optional\nSPACETIMEDB_REDUCER(insert_direct, ReducerContext ctx, int32_t id, int32_t value)\n{\n    SimpleTable row;\n    row.id = id;\n    row.value = value;\n    ctx.db.table<SimpleTable>(\"simple_table\").insert(row);\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_simple.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with optional field\nstruct SimpleOptional {\n    uint32_t id;\n    std::optional<int32_t> maybe_value;\n};\n\nSPACETIMEDB_STRUCT(SimpleOptional, id, maybe_value)\nSPACETIMEDB_TABLE(SimpleOptional, simple_optional, Public)\n\n// Parameter wrapper to avoid direct optional parameter\nstruct OptionalParam {\n    uint32_t id;\n    std::optional<int32_t> value;\n};\nSPACETIMEDB_STRUCT(OptionalParam, id, value)\n\n// Reducer to insert optional value (using struct wrapper instead of direct optional parameter)\nSPACETIMEDB_REDUCER(insert_optional, ReducerContext ctx, OptionalParam param)\n{\n    ctx.db.table<SimpleOptional>(\"simple_optional\").insert(SimpleOptional{param.id, param.value});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_table_only.cpp",
    "content": "#include <spacetimedb.h>\n#include <optional>\n\nusing namespace SpacetimeDB;\n\n// Test: Optional in table only (no reducer parameters)\n\n\n// Simple optional table\nstruct OptionalI32Table {\n    std::optional<int32_t> value;\n};\nSPACETIMEDB_STRUCT(OptionalI32Table, value)\nSPACETIMEDB_TABLE(OptionalI32Table, optional_i32_table, Public)\n\n// Reducer that doesn't use optional as parameter\nSPACETIMEDB_REDUCER(insert_value, ReducerContext ctx, int32_t v)\n{\n    OptionalI32Table row;\n    row.value = v;  // Convert to optional\n    ctx.db.table<OptionalI32Table>(\"optional_i32_table\").insert(row);\n}\n\nSPACETIMEDB_REDUCER(insert_none, ReducerContext ctx)\n{\n    OptionalI32Table row;\n    row.value = std::nullopt;\n    ctx.db.table<OptionalI32Table>(\"optional_i32_table\").insert(row);\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_simple_table.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    LOG_INFO(\"Test simple table initialized\");\n    return Ok();\n}\n\n// Simplest possible table - no optional fields\nstruct SimpleTable {\n    uint32_t id;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(SimpleTable, id, value)\nSPACETIMEDB_TABLE(SimpleTable, simple_table, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_single_special_vector.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test: Single vector of special type to isolate the exact error\n// Start with just std::vector<Identity> to pinpoint the issue\n\n\n// Test just one vector of Identity\nstruct TestVecIdentity { std::vector<Identity> identities; };\nSPACETIMEDB_STRUCT(TestVecIdentity, identities)\nSPACETIMEDB_TABLE(TestVecIdentity, test_vec_identity, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_special_minimal.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Minimal test: Just the three basic special types together\n// Testing if combination of Identity + ConnectionId + Timestamp causes issues\n\n\n// Test 1: Individual tables (should work based on isolated tests)\nstruct TestIdentity { Identity i; };\nSPACETIMEDB_STRUCT(TestIdentity, i)\nSPACETIMEDB_TABLE(TestIdentity, test_identity, Public)\n\nstruct TestConnectionId { ConnectionId c; };  \nSPACETIMEDB_STRUCT(TestConnectionId, c)\nSPACETIMEDB_TABLE(TestConnectionId, test_connection_id, Public)\n\nstruct TestTimestamp { Timestamp t; };\nSPACETIMEDB_STRUCT(TestTimestamp, t)  \nSPACETIMEDB_TABLE(TestTimestamp, test_timestamp, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_special_vectors.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Test: Vectors of special types\n// Testing if std::vector<Identity>, std::vector<ConnectionId>, std::vector<Timestamp> cause issues\n\n\n// Test vectors of special types\nstruct VecIdentity { std::vector<Identity> identities; };\nSPACETIMEDB_STRUCT(VecIdentity, identities)\nSPACETIMEDB_TABLE(VecIdentity, vec_identity, Public)\n\nstruct VecConnectionId { std::vector<ConnectionId> connections; };\nSPACETIMEDB_STRUCT(VecConnectionId, connections)\nSPACETIMEDB_TABLE(VecConnectionId, vec_connection_id, Public)\n\nstruct VecTimestamp { std::vector<Timestamp> timestamps; };\nSPACETIMEDB_STRUCT(VecTimestamp, timestamps)\nSPACETIMEDB_TABLE(VecTimestamp, vec_timestamp, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_timeduration_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with just TimeDuration\nstruct TestTimeDuration {\n    uint32_t id;\n    TimeDuration duration;\n};\nSPACETIMEDB_STRUCT(TestTimeDuration, id, duration)\nSPACETIMEDB_TABLE(TestTimeDuration, test_time_duration, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_timestamp_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with just Timestamp\nstruct TestTimestamp {\n    uint32_t id;\n    Timestamp timestamp;\n};\nSPACETIMEDB_STRUCT(TestTimestamp, id, timestamp)\nSPACETIMEDB_TABLE(TestTimestamp, test_timestamp, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_u128_only.cpp",
    "content": "#include <spacetimedb.h>\n// Removed: enhanced_database.h (unused functionality)\n\nusing namespace SpacetimeDB;\n\n\n// Simple table with just u128\nstruct TestU128 {\n    uint32_t id;\n    u128 value;\n};\nSPACETIMEDB_STRUCT(TestU128, id, value)\nSPACETIMEDB_TABLE(TestU128, test_u128, Public)"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unified_enum.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n\n// Test 1: Simple enum (should route to SPACETIMEDB_ENUM_SIMPLE)\nSPACETIMEDB_ENUM(SimpleTestEnum, Red, Green, Blue)\n\n// Test 2: Complex enum using unified syntax\nSPACETIMEDB_ENUM(ComplexTestEnum, (Number, uint32_t), (Text, std::string))\n\n// Test table with simple enum\nstruct TestTable {\n    uint32_t id;\n    SimpleTestEnum color;\n};\nSPACETIMEDB_STRUCT(TestTable, id, color)\nSPACETIMEDB_TABLE(TestTable, test_table, Public)\n\n// Test reducer with complex enum parameter\nSPACETIMEDB_REDUCER(test_complex_enum, ReducerContext ctx, ComplexTestEnum value) {\n    LOG_INFO(\"Complex enum test completed\");\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_isolation.cpp",
    "content": "// Isolate the exact unit type serialization issue\n#include \"spacetimedb.h\"\n#include <cstdint>\n\nusing namespace SpacetimeDB;\n\nSPACETIMEDB_UNIT_STRUCT(TestUnit)\n\n// Test 1: Just unit + primitive in struct (this should work)\nstruct UnitPlusInt {\n    TestUnit unit;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(UnitPlusInt, unit, value)\nSPACETIMEDB_TABLE(UnitPlusInt, unit_plus_int, Public)\n\n// Test 2: Two units + primitive (this might fail)\nSPACETIMEDB_UNIT_STRUCT(SecondUnit)\nstruct TwoUnits {\n    TestUnit unit1;\n    SecondUnit unit2;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(TwoUnits, unit1, unit2, value)\nSPACETIMEDB_TABLE(TwoUnits, two_units, Public)\n\n// Test 3: Nested struct with unit (this is where we expect failure)\nstruct NestedUnitTest {\n    UnitPlusInt nested;\n    TestUnit another_unit;\n};\nSPACETIMEDB_STRUCT(NestedUnitTest, nested, another_unit)\nSPACETIMEDB_TABLE(NestedUnitTest, nested_unit_test, Public)\n\n// Test different insertion patterns\nSPACETIMEDB_REDUCER(test_step1_simple, ReducerContext ctx) {\n    TestUnit unit{};\n    ctx.db[unit_plus_int].insert({unit, 100});\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_step2_two_units, ReducerContext ctx) {\n    TestUnit unit1{};\n    SecondUnit unit2{};\n    ctx.db[two_units].insert({unit1, unit2, 200});\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_step3_nested_fail, ReducerContext ctx) {\n    TestUnit unit{};\n    UnitPlusInt simple{unit, 300};\n    NestedUnitTest nested{simple, unit};\n    // This should fail with size mismatch\n    ctx.db[nested_unit_test].insert(nested);\n    return Ok();\n}\n\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    // Start with simple cases that should work\n    TestUnit unit{};\n    ctx.db[unit_plus_int].insert({unit, 100});\n    \n    SecondUnit unit2{};\n    ctx.db[two_units].insert({unit, unit2, 200});\n    \n    // This line should cause the failure:\n    UnitPlusInt simple{unit, 300};\n    NestedUnitTest nested{simple, unit};\n    ctx.db[nested_unit_test].insert(nested);\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_progression.cpp",
    "content": "// Progressive unit type testing to isolate the exact failure\n#include \"spacetimedb.h\"\n#include <cstdint>\n\nusing namespace SpacetimeDB;\n\n// Test 1: Single unit (we know this works)\nSPACETIMEDB_UNIT_STRUCT(TestUnit)\n\n// Test 2: Multiple units\nSPACETIMEDB_UNIT_STRUCT(Unit1)\nSPACETIMEDB_UNIT_STRUCT(Unit2)\n\n// Test 3: Unit in simple struct\nstruct SimpleStructWithUnit {\n    TestUnit unit;\n    int32_t data;\n};\nSPACETIMEDB_STRUCT(SimpleStructWithUnit, unit, data)\n\n// Test 4: Multiple units in struct\nstruct StructWithMultipleUnits {\n    Unit1 unit1;\n    Unit2 unit2;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(StructWithMultipleUnits, unit1, unit2, value)\n\n// Test 5: Table with unit field\nstruct TableWithUnit {\n    TestUnit unit;\n    int32_t id;\n};\nSPACETIMEDB_STRUCT(TableWithUnit, unit, id)\nSPACETIMEDB_TABLE(TableWithUnit, table_with_unit, Public)\n\n// Test 6: Table with multiple units  \nstruct TableWithMultipleUnits {\n    Unit1 unit1;\n    Unit2 unit2;\n    int32_t id;\n};\nSPACETIMEDB_STRUCT(TableWithMultipleUnits, unit1, unit2, id)\nSPACETIMEDB_TABLE(TableWithMultipleUnits, table_with_multiple_units, Public)\n\n// Test 7: Nested struct with units\nstruct NestedUnit {\n    SimpleStructWithUnit nested;\n    TestUnit unit;\n};\nSPACETIMEDB_STRUCT(NestedUnit, nested, unit)\nSPACETIMEDB_TABLE(NestedUnit, nested_unit_table, Public)\n\n// Reducers to test each step\nSPACETIMEDB_REDUCER(test_single_unit, ReducerContext ctx, TestUnit unit) {\n    ctx.db[table_with_unit].insert({unit, 1});\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_multiple_units, ReducerContext ctx, Unit1 u1, Unit2 u2) {\n    ctx.db[table_with_multiple_units].insert({u1, u2, 2});\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_struct_with_unit, ReducerContext ctx, SimpleStructWithUnit s) {\n    ctx.db[table_with_unit].insert({s.unit, s.data});\n    return Ok();\n}\n\nSPACETIMEDB_REDUCER(test_nested_units, ReducerContext ctx, NestedUnit nested) {\n    ctx.db[nested_unit_table].insert(nested);\n    return Ok();\n}\n\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    TestUnit test_unit{};\n    Unit1 unit1{};\n    Unit2 unit2{};\n    \n    // Test basic operations\n    ctx.db[table_with_unit].insert({test_unit, 100});\n    ctx.db[table_with_multiple_units].insert({unit1, unit2, 200});\n    \n    SimpleStructWithUnit simple{test_unit, 300};\n    NestedUnit nested{simple, test_unit};\n    ctx.db[nested_unit_table].insert(nested);\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_simple.cpp",
    "content": "// Simplified test module for unit struct types\n#include \"spacetimedb.h\"\n#include <cstdint>\n\nusing namespace SpacetimeDB;\n\n// Define a single unit struct\nSPACETIMEDB_UNIT_STRUCT(BasicUnit)\n\n// Simple table with unit field\nstruct SimpleTable {\n    uint32_t id;\n    BasicUnit unit;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(SimpleTable, id, unit, value)\nSPACETIMEDB_TABLE(SimpleTable, simple_table, Public)\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    BasicUnit unit{};\n    ctx.db[simple_table].insert({1, unit, 100});\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_struct.cpp",
    "content": "// Test module for unit struct types\n#include \"spacetimedb.h\"\n#include <cstdint>\n\nusing namespace SpacetimeDB;\n\n// Define various unit structs using the macro\nSPACETIMEDB_UNIT_STRUCT(BasicUnit)\nSPACETIMEDB_UNIT_STRUCT(AnotherUnit)\nSPACETIMEDB_UNIT_STRUCT(ThirdUnit)\n\n// Removed monostate struct - causes issues\n\n// Regular struct that contains unit types\nstruct StructWithUnits {\n    int32_t id;\n    BasicUnit basic;\n    AnotherUnit another;\n    std::string name;\n};\nSPACETIMEDB_STRUCT(StructWithUnits, id, basic, another, name)\n\n// Nested struct with units\nstruct NestedWithUnits {\n    StructWithUnits nested;\n    ThirdUnit third;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(NestedWithUnits, nested, third, value)\n\n// Table with unit fields\nstruct TableWithUnits {\n    uint32_t id_field;\n    BasicUnit unit_field;\n    int32_t data;\n};\nSPACETIMEDB_STRUCT(TableWithUnits, id_field, unit_field, data)\nSPACETIMEDB_TABLE(TableWithUnits, table_with_units, Public)\n\n// Another table with nested units\nstruct ComplexTable {\n    uint32_t key_field;\n    StructWithUnits complex_field;\n    std::string description;\n};\nSPACETIMEDB_STRUCT(ComplexTable, key_field, complex_field, description)\nSPACETIMEDB_TABLE(ComplexTable, complex_table, Public)\n\n// Table that is just units and primitives\nstruct SimpleUnitTable {\n    BasicUnit unit1;\n    AnotherUnit unit2;\n    int32_t value;\n};\nSPACETIMEDB_STRUCT(SimpleUnitTable, unit1, unit2, value)\nSPACETIMEDB_TABLE(SimpleUnitTable, simple_unit_table, Public)\n\n// Reducers that use unit types\n\n// Reducer with unit parameter\nSPACETIMEDB_REDUCER(reducer_with_unit_param, ReducerContext ctx, BasicUnit unit_param, int32_t value) {\n    // Insert into table\n    ctx.db[table_with_units].insert({\n        static_cast<uint32_t>(value),\n        unit_param,\n        value * 2\n    });\n    return Ok();\n}\n\n// Reducer with struct containing units\nSPACETIMEDB_REDUCER(reducer_with_struct_param, ReducerContext ctx, StructWithUnits struct_param) {\n    // Insert into complex table\n    ctx.db[complex_table].insert({\n        static_cast<uint32_t>(struct_param.id),\n        struct_param,\n        \"From reducer\"\n    });\n    return Ok();\n}\n\n// Reducer with multiple unit parameters\nSPACETIMEDB_REDUCER(reducer_multiple_units, ReducerContext ctx, \n                    BasicUnit unit1, AnotherUnit unit2, ThirdUnit unit3, int32_t id) {\n    // Insert simple unit table\n    ctx.db[simple_unit_table].insert({\n        unit1,\n        unit2,\n        id\n    });\n    return Ok();\n}\n\n// Reducer returning unit type in struct\nSPACETIMEDB_REDUCER(reducer_nested_units, ReducerContext ctx, NestedWithUnits nested) {\n    // Access nested units\n    ctx.db[complex_table].insert({\n        static_cast<uint32_t>(nested.value),\n        nested.nested,\n        \"Nested units\"\n    });\n    return Ok();\n}\n\n// Init reducer\nSPACETIMEDB_INIT(init, ReducerContext ctx) {\n    // Create some initial data with units\n    BasicUnit basic{};\n    AnotherUnit another{};\n    \n    ctx.db[table_with_units].insert({1, basic, 100});\n    \n    StructWithUnits s{42, basic, another, \"initial\"};\n    ctx.db[complex_table].insert({1, s, \"Initial entry\"});\n    \n    ctx.db[simple_unit_table].insert({basic, another, 999});\n    return Ok();\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/test_modules/test_wrapped_special.cpp",
    "content": "#include <spacetimedb.h>\n\nusing namespace SpacetimeDB;\n\n// Control test - same functionality but with wrapped parameters (should work)\n\n// Simple Identity table\nstruct SimpleIdentityTable { Identity id; };\nSPACETIMEDB_STRUCT(SimpleIdentityTable, id)\nSPACETIMEDB_TABLE(SimpleIdentityTable, simple_identity_table, Public)\n\n// Parameter wrapper struct\nstruct IdentityParam { Identity id; };\nSPACETIMEDB_STRUCT(IdentityParam, id)\n\n// Reducer with wrapped Identity parameter (should work)\nSPACETIMEDB_REDUCER(test_identity_reducer, ReducerContext ctx, IdentityParam param)\n{\n    ctx.db.table<SimpleIdentityTable>(\"simple_identity_table\").insert(SimpleIdentityTable{param.id});\n}"
  },
  {
    "path": "crates/bindings-cpp/tests/type-isolation-test/update_table_from_log.sh",
    "content": "#!/bin/bash\n\n# Table updater - reads from test_log.txt and updates test_summary_live.txt\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\ncd \"$SCRIPT_DIR\"\n\n# Initialize module arrays\ndeclare -a MODULES\ndeclare -A BUILD_STATUS\ndeclare -A PUBLISH_STATUS\ndeclare -A WASM_SIZE\ndeclare -A ERROR_MSG\n\n# Initialize library status\nLIBRARY_STATUS=\"⏳\"\nLIBRARY_ERROR=\"\"\n\n# Discover all modules\nfor cpp_file in test_modules/*.cpp; do\n    if [ -f \"$cpp_file\" ]; then\n        module=$(basename \"${cpp_file%.cpp}\")\n        MODULES+=(\"$module\")\n        BUILD_STATUS[\"$module\"]=\"⏳\"\n        PUBLISH_STATUS[\"$module\"]=\"⏳\"\n        WASM_SIZE[\"$module\"]=\"-\"\n        ERROR_MSG[\"$module\"]=\"\"\n    fi\ndone\n\n# Process existing log to reconstruct current state (if log exists)\nif [ -f test_log.txt ]; then\n    while IFS='|' read -r timestamp module field value; do\n        # Skip empty lines\n        [ -z \"$module\" ] && continue\n        \n        # Handle library status\n        if [ \"$module\" = \"LIBRARY\" ]; then\n            case \"$field\" in\n                \"build\")\n                    case \"$value\" in\n                        \"start\") LIBRARY_STATUS=\"🔨\" ;;\n                        \"pass\") LIBRARY_STATUS=\"✅\" ;;\n                        \"fail\") LIBRARY_STATUS=\"❌\" ;;\n                    esac\n                    ;;\n                \"error\")\n                    LIBRARY_ERROR=\"$value\"\n                    ;;\n            esac\n            continue\n        fi\n        \n        # Update status based on field\n        case \"$field\" in\n            \"build\")\n                case \"$value\" in\n                    \"start\") BUILD_STATUS[\"$module\"]=\"🔨\" ;;\n                    \"pass\") BUILD_STATUS[\"$module\"]=\"✅\" ;;\n                    \"fail\") BUILD_STATUS[\"$module\"]=\"❌\" ;;\n                esac\n                ;;\n            \"publish\")\n                case \"$value\" in\n                    \"start\") PUBLISH_STATUS[\"$module\"]=\"📤\" ;;\n                    \"pass\") PUBLISH_STATUS[\"$module\"]=\"✅\" ;;\n                    \"fail\") PUBLISH_STATUS[\"$module\"]=\"❌\" ;;\n                    \"skip\") PUBLISH_STATUS[\"$module\"]=\"⏭️\" ;;\n                esac\n                ;;\n            \"size\")\n                WASM_SIZE[\"$module\"]=\"$value\"\n                ;;\n            \"error\")\n                ERROR_MSG[\"$module\"]=\"$value\"\n                ;;\n        esac\n    done < test_log.txt\nfi\n\n# Sort modules\nIFS=$'\\n' MODULES=($(sort <<<\"${MODULES[*]}\"))\nunset IFS\n\n# Function to render table\nrender_table() {\n    {\n        echo \"============================================================\"\n        echo \"SpacetimeDB C++ Library Type Isolation Test - Live Progress\"\n        echo \"Generated: $(date)\"\n        echo \"Last Update: $(date +%H:%M:%S)\"\n        echo \"============================================================\"\n        echo\n        echo \"Test Environment:\"\n        echo \"  Directory: $SCRIPT_DIR\"\n        echo \"  Total Modules: ${#MODULES[@]}\"\n        echo\n        echo \"============================================================\"\n        echo \"Live Test Status Table:\"\n        echo \"============================================================\"\n        echo\n        printf \"%-27s | %-10s | %-12s | %-10s | %s\\n\" \"Module\" \"Build\" \"Publish\" \"WASM Size\" \"Error\"\n        echo \"----------------------------|-----------|-------------|-----------|------------------------------------\"\n        \n        # Show library build status first\n        # Truncate library error if too long (for table display)\n        local lib_error_display=\"$LIBRARY_ERROR\"\n        if [ ${#lib_error_display} -gt 200 ]; then\n            lib_error_display=\"${lib_error_display:0:197}...\"\n        fi\n        printf \"%-27s | %-10s | %-12s | %-10s | %s\\n\" \"📚 Library\" \"$LIBRARY_STATUS\" \"-\" \"-\" \"$lib_error_display\"\n        echo \"----------------------------|-----------|-------------|-----------|------------------------------------\"\n        \n        for module in \"${MODULES[@]}\"; do\n            build=\"${BUILD_STATUS[$module]}\"\n            publish=\"${PUBLISH_STATUS[$module]}\"\n            size=\"${WASM_SIZE[$module]}\"\n            error=\"${ERROR_MSG[$module]}\"\n            \n            # Truncate error if too long (but show more context for table display)\n            if [ ${#error} -gt 250 ]; then\n                error=\"${error:0:247}...\"\n            fi\n            \n            printf \"%-27s | %-10s | %-12s | %-10s | %s\\n\" \"$module\" \"$build\" \"$publish\" \"$size\" \"$error\"\n        done\n        \n        echo\n        echo \"============================================================\"\n        echo \"Legend:\"\n        echo \"  ⏳ = Pending    🔨 = Building    📤 = Publishing\"\n        echo \"  ✅ = Passed     ❌ = Failed      ⏭️  = Skipped\"\n        echo \"============================================================\"\n        \n        # Progress counter\n        local completed=0\n        local building=0\n        local publishing=0\n        local total=${#MODULES[@]}\n        \n        for module in \"${MODULES[@]}\"; do\n            case \"${BUILD_STATUS[$module]}\" in\n                \"✅\"|\"❌\") ((completed++)) ;;\n                \"🔨\") ((building++)) ;;\n            esac\n            case \"${PUBLISH_STATUS[$module]}\" in\n                \"📤\") ((publishing++)) ;;\n            esac\n        done\n        \n        echo\n        echo \"Progress: $completed/$total builds complete, $building building, $publishing publishing\"\n        \n        # Add statistics if all complete\n        if [ $completed -eq $total ]; then\n            echo\n            echo \"============================================================\"\n            echo \"Final Statistics:\"\n            echo \"============================================================\"\n            \n            local passed=0\n            local build_fail=0\n            local publish_fail=0\n            \n            for module in \"${MODULES[@]}\"; do\n                if [ \"${BUILD_STATUS[$module]}\" = \"✅\" ] && [ \"${PUBLISH_STATUS[$module]}\" = \"✅\" ]; then\n                    ((passed++))\n                elif [ \"${BUILD_STATUS[$module]}\" = \"❌\" ]; then\n                    ((build_fail++))\n                elif [ \"${PUBLISH_STATUS[$module]}\" = \"❌\" ]; then\n                    ((publish_fail++))\n                fi\n            done\n            \n            echo \"Total modules: $total\"\n            echo \"Fully passed: $passed\"\n            echo \"Build failures: $build_fail\"\n            echo \"Publish failures: $publish_fail\"\n            echo \"Success rate: $(( passed * 100 / total ))%\"\n            echo \"============================================================\"\n        fi\n    } > test_summary_live.txt\n}\n\n# Track last processed line - count lines already in log\nif [ -f test_log.txt ]; then\n    LAST_LINE=$(wc -l < test_log.txt)\nelse\n    LAST_LINE=0\nfi\n\n# Initial render\nrender_table\n\n# Continuously read log and update table\nwhile true; do\n    # Check if log file exists\n    if [ ! -f test_log.txt ]; then\n        sleep 0.5\n        continue\n    fi\n    \n    # Read new lines from log\n    CURRENT_LINES=$(wc -l < test_log.txt)\n    \n    if [ $CURRENT_LINES -gt $LAST_LINE ]; then\n        # Process new lines - use process substitution to avoid subshell\n        while IFS='|' read -r timestamp module field value; do\n            # Skip empty lines\n            [ -z \"$module\" ] && continue\n            \n            # Check for completion signal\n            if [ \"$module\" = \"COMPLETE\" ]; then\n                render_table\n                exit 0\n            fi\n            \n            # Handle library status\n            if [ \"$module\" = \"LIBRARY\" ]; then\n                case \"$field\" in\n                    \"build\")\n                        case \"$value\" in\n                            \"start\") LIBRARY_STATUS=\"🔨\" ;;\n                            \"pass\") LIBRARY_STATUS=\"✅\" ;;\n                            \"fail\") LIBRARY_STATUS=\"❌\" ;;\n                        esac\n                        ;;\n                    \"error\")\n                        LIBRARY_ERROR=\"$value\"\n                        ;;\n                esac\n            else\n                # Update status based on field\n                case \"$field\" in\n                    \"build\")\n                        case \"$value\" in\n                            \"start\") BUILD_STATUS[\"$module\"]=\"🔨\" ;;\n                            \"pass\") BUILD_STATUS[\"$module\"]=\"✅\" ;;\n                            \"fail\") BUILD_STATUS[\"$module\"]=\"❌\" ;;\n                        esac\n                        ;;\n                    \"publish\")\n                        case \"$value\" in\n                            \"start\") PUBLISH_STATUS[\"$module\"]=\"📤\" ;;\n                            \"pass\") PUBLISH_STATUS[\"$module\"]=\"✅\" ;;\n                            \"fail\") PUBLISH_STATUS[\"$module\"]=\"❌\" ;;\n                            \"skip\") PUBLISH_STATUS[\"$module\"]=\"⏭️\" ;;\n                        esac\n                        ;;\n                    \"size\")\n                        WASM_SIZE[\"$module\"]=\"$value\"\n                        ;;\n                    \"error\")\n                        # Store full error for log\n                        ERROR_MSG[\"$module\"]=\"$value\"\n                        \n                        # Extract cleaner error for table display\n                        # Look for key error patterns and extract the important part\n                        clean_error=\"\"\n                        if [[ \"$value\" =~ \"Error: Errors occurred:\" ]]; then\n                            # Extract the main error message after \"Error: Errors occurred:\"\n                            # Get the part between \"Error: Errors occurred:\" and \"Caused by:\"\n                            clean_error=$(echo \"$value\" | sed 's/.*Error: Errors occurred: //' | sed 's/Caused by:.*//' | sed 's/[[:space:]]*$//' | head -1)\n                            if [ -n \"$clean_error\" ]; then\n                                clean_error=\"Error: $clean_error\"\n                            fi\n                        elif [[ \"$value\" =~ \"error: static assertion failed:\" ]]; then\n                            # Extract the assertion message with more context\n                            clean_error=$(echo \"$value\" | grep -o 'error: static assertion failed: [^.]*' | head -1)\n                        elif [[ \"$value\" =~ \"error:\" ]]; then\n                            # Generic error - try to extract first error line with more context\n                            clean_error=$(echo \"$value\" | grep -o \"error:[^|]*\" | head -1 | sed 's/[[:space:]]*$//')\n                        elif [[ \"$value\" =~ \"Error:\" ]]; then\n                            # Spacetime error - extract the main message with more context\n                            clean_error=$(echo \"$value\" | sed 's/.*\\(Error:[^|]*\\).*/\\1/' | sed 's/[[:space:]]*$//' | head -1)\n                        fi\n                        \n                        # Use clean error if we extracted one\n                        if [ -n \"$clean_error\" ]; then\n                            ERROR_MSG[\"$module\"]=\"$clean_error\"\n                        fi\n                        ;;\n                esac\n            fi\n        done < <(tail -n +$((LAST_LINE + 1)) test_log.txt)\n        \n        # Update last processed line\n        LAST_LINE=$CURRENT_LINES\n        \n        # Render updated table\n        render_table\n    fi\n    \n    # Small delay to avoid CPU spinning\n    sleep 0.2\ndone"
  },
  {
    "path": "crates/bindings-csharp/.config/dotnet-tools.json",
    "content": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"csharpier\": {\n      \"version\": \"0.29.0\",\n      \"commands\": [\n        \"dotnet-csharpier\"\n      ],\n      \"rollForward\": false\n    },\n    \"verify.tool\": {\n      \"version\": \"0.6.0\",\n      \"commands\": [\n        \"dotnet-verify\"\n      ],\n      \"rollForward\": false\n    }\n  }\n}"
  },
  {
    "path": "crates/bindings-csharp/.editorconfig",
    "content": "root = true\n\n[*.cs]\ncsharp_using_directive_placement = inside_namespace:warning\ncsharp_style_namespace_declarations = file_scoped:warning\ncsharp_prefer_simple_using_statement = true:warning\ncsharp_prefer_braces = true:silent\ncsharp_style_prefer_method_group_conversion = true:silent\ncsharp_style_prefer_top_level_statements = true:silent\ncsharp_style_prefer_primary_constructors = true:warning\ncsharp_style_expression_bodied_methods = when_on_single_line:silent\ncsharp_style_expression_bodied_constructors = when_on_single_line:silent\ncsharp_style_expression_bodied_operators = when_on_single_line:silent\ncsharp_style_expression_bodied_properties = when_on_single_line:silent\ncsharp_style_expression_bodied_indexers = when_on_single_line:silent\ncsharp_style_expression_bodied_accessors = when_on_single_line:silent\ncsharp_style_expression_bodied_lambdas = when_on_single_line:silent\ncsharp_style_expression_bodied_local_functions = when_on_single_line:silent\ncsharp_indent_labels = one_less_than_current\ncsharp_style_inlined_variable_declaration = true:warning\ncsharp_style_deconstructed_variable_declaration = true:suggestion\ncsharp_style_var_for_built_in_types = true:warning\ncsharp_style_var_when_type_is_apparent = true:warning\ncsharp_style_var_elsewhere = true:suggestion\ncsharp_style_prefer_extended_property_pattern = true:suggestion\ncsharp_style_prefer_not_pattern = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_prefer_pattern_matching = true:warning\ncsharp_style_prefer_switch_expression = true:warning\ncsharp_style_conditional_delegate_call = true:warning\ncsharp_style_prefer_readonly_struct_member = true:warning\ncsharp_style_prefer_readonly_struct = true:warning\ncsharp_prefer_static_anonymous_function = true:suggestion\ncsharp_prefer_static_local_function = true:suggestion\ncsharp_space_around_binary_operators = before_and_after\n\n[*.{cs,vb}]\n#### Naming styles ####\n\n# Naming rules\n\ndotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion\ndotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface\ndotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i\n\ndotnet_naming_rule.types_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.types_should_be_pascal_case.symbols = types\ndotnet_naming_rule.types_should_be_pascal_case.style = pascal_case\n\ndotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members\ndotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case\n\n# Symbol specifications\n\ndotnet_naming_symbols.interface.applicable_kinds = interface\ndotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.interface.required_modifiers = \n\ndotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum\ndotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.types.required_modifiers = \n\ndotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method\ndotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.non_field_members.required_modifiers = \n\n# Naming styles\n\ndotnet_naming_style.begins_with_i.required_prefix = I\ndotnet_naming_style.begins_with_i.required_suffix = \ndotnet_naming_style.begins_with_i.word_separator = \ndotnet_naming_style.begins_with_i.capitalization = pascal_case\n\ndotnet_naming_style.pascal_case.required_prefix = \ndotnet_naming_style.pascal_case.required_suffix = \ndotnet_naming_style.pascal_case.word_separator = \ndotnet_naming_style.pascal_case.capitalization = pascal_case\n\ndotnet_naming_style.pascal_case.required_prefix = \ndotnet_naming_style.pascal_case.required_suffix = \ndotnet_naming_style.pascal_case.word_separator = \ndotnet_naming_style.pascal_case.capitalization = pascal_case\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion\ndotnet_style_prefer_auto_properties = true:silent\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_prefer_simplified_boolean_expressions = true:suggestion\ndotnet_style_prefer_conditional_expression_over_assignment = true:silent\ndotnet_style_prefer_conditional_expression_over_return = true:silent\ndotnet_style_explicit_tuple_names = true:suggestion\ndotnet_style_operator_placement_when_wrapping = beginning_of_line\ntab_width = 4\nindent_size = 4\ndotnet_style_qualification_for_event = false:silent\ndotnet_style_qualification_for_method = false:silent\ndotnet_style_qualification_for_property = false:silent\ndotnet_style_qualification_for_field = false:silent\ndotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent\ndotnet_code_quality_unused_parameters = all:suggestion\ndotnet_style_require_accessibility_modifiers = all:silent\ndotnet_style_namespace_match_folder = true:suggestion\n\n# https://csharpier.com/docs/IntegratingWithLinters\ndotnet_diagnostic.IDE0055.severity = none\n"
  },
  {
    "path": "crates/bindings-csharp/.gitignore",
    "content": "bin\nobj\n*.received.*\n.vs\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <AssemblyName>SpacetimeDB.BSATN.Codegen</AssemblyName>\n    <Version>2.0.4</Version>\n    <Title>SpacetimeDB BSATN Codegen</Title>\n    <Description>The SpacetimeDB BSATN Codegen implements the Roslyn incremental generators for BSATN serialization/deserialization in C#.</Description>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <!-- We're going to include this analyzer in the Runtime package instead of publishing separately. -->\n    <IsPackable>false</IsPackable>\n    <IsRoslynComponent>true</IsRoslynComponent>\n    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <RootNamespace>SpacetimeDB.Codegen</RootNamespace>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.3.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- dev-dependency to add internal C# classes and attributes not included in .NET Standard -->\n    <PackageReference Include=\"PolySharp\" Version=\"1.14.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Codegen/Diag.cs",
    "content": "namespace SpacetimeDB.Codegen;\n\nusing System.Collections.Immutable;\nusing System.Linq.Expressions;\nusing System.Runtime.CompilerServices;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing static SpacetimeDB.Codegen.Utils;\n\npublic record ErrorDescriptorGroup(string Tag, string Name)\n{\n    private int idCounter = 0;\n\n    public string NextId() => $\"{Tag}{++idCounter:D4}\";\n}\n\n// Roslyn `Diagnostics` requires declaration using old-school string format (\"foo {0} bar {1}\")\n// which is not very type-safe when you want to pass arguments from arbitrary places in the codebase.\n//\n// This helper class allows us to define diagnostics in a type-safe way by using LINQ expressions\n// to extract the format and arguments from a normal-looking lambda.\npublic class ErrorDescriptor<TContext>\n{\n    private readonly DiagnosticDescriptor descriptor;\n    private readonly Func<TContext, Location?> toLocation;\n    private readonly Func<TContext, object[]> makeFormatArgs;\n\n    public ErrorDescriptor(\n        ErrorDescriptorGroup group,\n        string title,\n        Expression<Func<TContext, FormattableString>> interpolate,\n        Func<TContext, Location?> toLocation\n    )\n    {\n        this.toLocation = toLocation;\n        if (\n            interpolate.Body\n                is not MethodCallExpression\n                {\n                    Method:\n                    {\n                        DeclaringType: var declaringType,\n                        Name: nameof(FormattableStringFactory.Create)\n                    },\n                    Arguments: [\n                        ConstantExpression { Value: string messageFormat },\n                        NewArrayExpression args,\n                    ]\n                }\n            || declaringType != typeof(FormattableStringFactory)\n        )\n        {\n            throw new InvalidOperationException(\n                $\"Expected an interpolated string as a lambda body but got {interpolate.Body}.\"\n            );\n        }\n        descriptor = new(\n            id: group.NextId(),\n            title: title,\n            messageFormat: messageFormat,\n            category: group.Name,\n            defaultSeverity: DiagnosticSeverity.Error,\n            isEnabledByDefault: true\n        );\n        makeFormatArgs = Expression\n            .Lambda<Func<TContext, object[]>>(args, interpolate.Parameters)\n            .Compile();\n    }\n\n    public ErrorDescriptor(\n        ErrorDescriptorGroup group,\n        string title,\n        Expression<Func<TContext, FormattableString>> interpolate,\n        Func<TContext, SyntaxToken> toLocation\n    )\n        : this(group, title, interpolate, ctx => toLocation(ctx).GetLocation()) { }\n\n    public ErrorDescriptor(\n        ErrorDescriptorGroup group,\n        string title,\n        Expression<Func<TContext, FormattableString>> interpolate,\n        Func<TContext, SyntaxNode> toLocation\n    )\n        : this(group, title, interpolate, ctx => toLocation(ctx).GetLocation()) { }\n\n    public ErrorDescriptor(\n        ErrorDescriptorGroup group,\n        string title,\n        Expression<Func<TContext, FormattableString>> interpolate,\n        Func<TContext, ISymbol> toLocation\n    )\n        : this(group, title, interpolate, ctx => toLocation(ctx).Locations.FirstOrDefault()) { }\n\n    public ErrorDescriptor(\n        ErrorDescriptorGroup group,\n        string title,\n        Expression<Func<TContext, FormattableString>> interpolate,\n        Func<TContext, AttributeData> toLocation\n    )\n        : this(\n            group,\n            title,\n            interpolate,\n            ctx =>\n                toLocation(ctx).ApplicationSyntaxReference is { } r\n                    ? r.SyntaxTree.GetLocation(r.Span)\n                    : null\n        ) { }\n\n    public Diagnostic ToDiag(TContext ctx) =>\n        Diagnostic.Create(descriptor, toLocation(ctx), makeFormatArgs(ctx));\n}\n\n/// <summary>\n/// No-op error descriptor placeholder.\n///\n/// <para>Error descriptors must have strong ID to avoid breaking semver, since they are used for diagnostic suppression by users.</para>\n/// <para>To ensure this, we cannot reorder to delete unused diagnostics - instead, we need to put some placeholders where they used to be.</para>\n/// <para>This class serves that purpose - it's a no-op error descriptor that you can instantiate just to reserve said ID.</para>\n/// </summary>\npublic sealed class UnusedErrorDescriptor\n{\n    public UnusedErrorDescriptor(ErrorDescriptorGroup group)\n    {\n        group.NextId();\n    }\n}\n\ninternal static class ErrorDescriptor\n{\n    private static readonly ErrorDescriptorGroup group = new(\"BSATN\", \"SpacetimeDB.BSATN\");\n\n    public static readonly ErrorDescriptor<(\n        ISymbol member,\n        ITypeSymbol type,\n        Exception e\n    )> UnsupportedType =\n        new(\n            group,\n            \"Unsupported type\",\n            ctx => $\"BSATN implementation for {ctx.type} is not found: {ctx.e.Message}\",\n            ctx => ctx.member\n        );\n\n    public static readonly ErrorDescriptor<(\n        EqualsValueClauseSyntax equalsValue,\n        EnumMemberDeclarationSyntax enumMember,\n        EnumDeclarationSyntax @enum\n    )> EnumWithExplicitValues =\n        new(\n            group,\n            \"[SpacetimeDB.Type] enums cannot have explicit values\",\n            ctx =>\n                $\"{ctx.@enum.Identifier}.{ctx.enumMember.Identifier} has an explicit value {ctx.equalsValue.Value} which is not allowed in SpacetimeDB enums.\",\n            ctx => ctx.equalsValue\n        );\n\n    public static readonly ErrorDescriptor<EnumDeclarationSyntax> EnumTooManyVariants =\n        new(\n            group,\n            \"[SpacetimeDB.Type] enums are limited to 256 variants\",\n            @enum =>\n                $\"{@enum.Identifier} has {@enum.Members.Count} variants which is more than the allowed 256 variants for SpacetimeDB enums.\",\n            @enum => @enum.Members[256]\n        );\n\n    public static readonly ErrorDescriptor<INamedTypeSymbol> TaggedEnumInlineTuple =\n        new(\n            group,\n            \"Tagged enum variants must be declared with inline tuples\",\n            baseType =>\n                $\"{baseType} does not have the expected format SpacetimeDB.TaggedEnum<(TVariant1 v1, ..., TVariantN vN)>.\",\n            baseType => baseType\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> TaggedEnumField =\n        new(\n            group,\n            \"Tagged enums cannot have instance fields\",\n            field =>\n                $\"{field.Name} is an instance field, which are not permitted inside SpacetimeDB tagged enums.\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<TypeParameterListSyntax> TypeParams =\n        new(\n            group,\n            \"Type parameters are not yet supported\",\n            typeParams => $\"Type parameters {typeParams} are not supported in SpacetimeDB types.\",\n            typeParams => typeParams\n        );\n}\n\n// This class is used to collect diagnostics during parsing and return them as a combined result.\n//\n// It's necessary because Roslyn doesn't let incremental generators emit diagnostics while parsing,\n// only while emitting - which means we need to collect them during parsing and store in some data\n// structure (this one) and report them later, when we have the emitter context.\n//\n// Diagnostics are not kept inside the parsed data structure to make the main data cacheable even\n// when the diagnostic locations and details change between reruns.\npublic record ParseResult<T>(T? Parsed, EquatableArray<Diagnostic> Diag)\n    where T : IEquatable<T>;\n\npublic class DiagReporter\n{\n    private readonly ImmutableArray<Diagnostic>.Builder builder =\n        ImmutableArray.CreateBuilder<Diagnostic>();\n\n    public void Report<TContext>(ErrorDescriptor<TContext> descriptor, TContext ctx)\n    {\n        builder.Add(descriptor.ToDiag(ctx));\n    }\n\n    private DiagReporter() { }\n\n    private static readonly ErrorDescriptor<(Location location, Exception e)> InternalError =\n        new(\n            new(\"STDBINT\", \"SpacetimeDB.Internal\"),\n            \"Internal SpacetimeDB codegen error\",\n            ctx => $\"An internal error occurred during codegen: {ctx.e.Message}\",\n            ctx => ctx.location\n        );\n\n    public static ParseResult<T> With<T>(Location location, Func<DiagReporter, T> build)\n        where T : IEquatable<T>\n    {\n        var reporter = new DiagReporter();\n        T? parsed;\n        try\n        {\n            parsed = build(reporter);\n        }\n        // Catch any unexpected exceptions not covered by proper diagnostics.\n        // This is the last resort to prevent the generator from crashing and being skipped altogether.\n        // Instead, it will limit the damage to skipping one particular syntax node.\n        catch (Exception e)\n        {\n            reporter.Report(InternalError, (location, e));\n            parsed = default;\n        }\n        return new(parsed, new(reporter.builder.ToImmutable()));\n    }\n\n    public static ParseResult<T> With<T>(SyntaxNode node, Func<DiagReporter, T> build)\n        where T : IEquatable<T>\n    {\n        return With(node.GetLocation(), build);\n    }\n}\n\npublic static class DiagExtensions\n{\n    public static ParseResult<T> ParseWithDiags<T>(\n        this GeneratorAttributeSyntaxContext context,\n        Func<DiagReporter, T> build\n    )\n        where T : IEquatable<T>\n    {\n        return DiagReporter.With(context.TargetNode, build);\n    }\n\n    public static IncrementalValuesProvider<T> ReportDiagnostics<T>(\n        this IncrementalValuesProvider<ParseResult<T>> diagnosticHolders,\n        IncrementalGeneratorInitializationContext context\n    )\n        where T : IEquatable<T>\n    {\n        context.RegisterSourceOutput(\n            diagnosticHolders.SelectMany((result, ct) => result.Diag),\n            (context, diag) => context.ReportDiagnostic(diag)\n        );\n        return diagnosticHolders\n            .Select((result, ct) => result.Parsed!)\n            .Where(parsed => parsed is not null);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Codegen/Type.cs",
    "content": "namespace SpacetimeDB.Codegen;\n\n// Generate code to implement serialization to the BSATN format (https://spacetimedb.com/docs/bsatn).\n// C# doesn't support static methods in interfaces, so instead we declare a zero-sized `struct` type that implements\n// the serialization interface (IReadWrite) for us.\n//\n// See BSATN.Runtime for the support code referenced by code generation,\n// and see Codegen.Tests/fixtures/*/snapshots for examples of generated code.\n// Also, if you set <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> in a csproj,\n// you can find the generated code in obj/Debug/*/generated/SpacetimeDB.BSATN.Codegen.\n\nusing System.Collections.Immutable;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing static Utils;\n\n/// <summary>\n/// The type of a member of one of the types we are generating code for.\n///\n/// Knows how to serialize and deserialize the member.\n///\n/// Also knows how to compare the member for equality and compute its hash code.\n/// We can't just use Equals and GetHashCode for this, because they implement reference\n/// equality for arrays and Lists.\n///\n/// (It would be nice to be able to dynamically build EqualityComparers at runtime\n/// to do these operations, but this seems to require either (A) reflective calls\n/// or (B) instantiating generics at runtime. These are (A) slow and (B) very slow\n/// when compiling under IL2CPP. Instead, we just inline the needed loops to compute\n/// the relevant values. This is very simple for IL2CPP to optimize.\n/// That's good, since Equals and GetHashCode for BSATN types are used in hot parts\n/// of the codebase.)\n/// </summary>\n/// <param name=\"Name\">The name of the type</param>\n/// <param name=\"BSATNName\">The name of the BSATN struct for the type.</param>\npublic abstract record TypeUse(string Name, string BSATNName)\n{\n    internal static string BSATN_FIELD_SUFFIX = \"RW\";\n\n    /// <summary>\n    /// The name of the static field containing an IReadWrite in the IReadWrite struct associated with this type.\n    /// We make sure this is different from the field name so that collisions cannot occur.\n    /// </summary>\n    public static string BsatnFieldSuffix => $\"{BSATN_FIELD_SUFFIX}\";\n\n    /// <summary>\n    /// Parse a type use for a member.\n    /// </summary>\n    /// <param name=\"member\">The member name. Only used for reporting parsing failures.</param>\n    /// <param name=\"typeSymbol\">The type we are using. May not be the type of the member: this method is called recursively.</param>\n    /// <param name=\"diag\"></param>\n    /// <returns></returns>\n    public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter diag)\n    {\n        if (typeSymbol.SpecialType == SpecialType.System_Void)\n        {\n            // Treat void as equivalent to Unit type\n            return new ReferenceUse(\"SpacetimeDB.Unit\", \"SpacetimeDB.Unit.BSATN\");\n        }\n\n        var type = SymbolToName(typeSymbol);\n        string typeInfo;\n\n        try\n        {\n            typeInfo = GetTypeInfo(typeSymbol);\n        }\n        catch (UnresolvedTypeException)\n        {\n            // If it's an unresolved type, this error will have been already highlighted by .NET itself, no need to add noise.\n            // Just add some dummy type to avoid further errors.\n            // Note that we just use `object` here because emitting the unresolved type's name again would produce more of said noise.\n            typeInfo = \"SpacetimeDB.BSATN.Unsupported<object>\";\n        }\n        catch (Exception e)\n        {\n            diag.Report(ErrorDescriptor.UnsupportedType, (member, typeSymbol, e));\n            // dummy BSATN implementation to produce fewer noisy errors\n            typeInfo = $\"SpacetimeDB.BSATN.Unsupported<{type}>\";\n        }\n\n        return typeSymbol switch\n        {\n            ITypeParameterSymbol => new ReferenceUse(type, typeInfo),\n            IArrayTypeSymbol { ElementType: var elementType } => new ArrayUse(\n                type,\n                typeInfo,\n                Parse(member, elementType, diag)\n            ),\n            INamedTypeSymbol named => named.OriginalDefinition.ToString() switch\n            {\n                \"SpacetimeDB.Result<T, E>\" or \"SpacetimeDB.Result<T,E>\" => new ResultUse(\n                    type,\n                    typeInfo,\n                    Parse(member, named.TypeArguments[0], diag),\n                    Parse(member, named.TypeArguments[1], diag)\n                ),\n                \"System.Collections.Generic.List<T>\" => new ListUse(\n                    type,\n                    typeInfo,\n                    Parse(member, named.TypeArguments[0], diag)\n                ),\n                \"System.Nullable<T>\" => new NullableUse(type, typeInfo),\n                _ => named.IsValueType\n                    ? (\n                        named.TypeKind == Microsoft.CodeAnalysis.TypeKind.Enum\n                            ? new EnumUse(type, typeInfo)\n                            : new ValueUse(type, typeInfo)\n                    )\n                    : new ReferenceUse(type, typeInfo),\n            },\n            _ => throw new InvalidOperationException($\"Unsupported type {type}\"),\n        };\n    }\n\n    /// <summary>\n    /// Get the name of the BSATN struct for this type.\n    /// </summary>\n    public virtual string ToBSATNString()\n    {\n        return this.BSATNName;\n    }\n\n    public virtual string ToBSATNString2()\n    {\n        return this.BSATNName;\n    }\n\n    /// <summary>\n    /// Get a statement that declares outVar and assigns (inVar1 logically-equals inVar2) to it.\n    /// logically-equals:\n    /// - recursively compares lists and arrays by sequence equality.\n    /// - is the same as .Equals( ) for everything else.\n    ///\n    /// This can't be an expression because some types need to use loops.\n    /// </summary>\n    /// <param name=\"inVar1\">A variable of type `Type` that we want to hash.</param>\n    /// <param name=\"inVar2\">A variable of type `Type` that we want to hash.</param>\n    /// <param name=\"outVar\">The variable to declare and store the `Equals` bool in.</param>\n    /// <param name=\"level\">Iteration level counter. You don't need to set this.</param>\n    /// <returns></returns>\n    public abstract string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    );\n\n    /// <summary>\n    /// Get a statement that declares outVar and assigns the hash code of inVar to it.\n    ///\n    /// This can't be an expression because some types need to use loops.\n    /// </summary>\n    /// <param name=\"inVar\">A variable of type `Type` that we want to hash.</param>\n    /// <param name=\"outVar\">The variable to declare and store the hash in.</param>\n    /// <param name=\"level\">Iteration level counter. You don't need to set this.</param>\n    /// <returns></returns>\n    public abstract string GetHashCodeStatement(string inVar, string outVar, int level = 0);\n}\n\n/// <summary>\n/// A use of a Result&lt;T, E&gt; type.\n/// </summary>\npublic sealed record ResultUse : TypeUse\n{\n    public TypeUse Ok { get; }\n    public TypeUse Err { get; }\n\n    public string TypeName { get; }\n\n    public ResultUse(string typeName, string typeInfo, TypeUse ok, TypeUse err)\n        : base(typeName, typeInfo)\n    {\n        Ok = ok;\n        Err = err;\n        TypeName = typeName;\n    }\n\n    public override string ToBSATNString()\n    {\n        return $\"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>\";\n    }\n\n    public override string ToBSATNString2()\n    {\n        return $\"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>\";\n    }\n\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    ) => $\"var {outVar} = {inVar1} == {inVar2};\";\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>\n        $\"var {outVar} = {inVar}.GetHashCode();\";\n}\n\n/// <summary>\n/// A use of an enum type.\n/// (This is a C# enum, not one of our tagged enums.)\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\npublic record EnumUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)\n{\n    // We just use `==` here, rather than `.Equals`, because\n    // C# enums don't provide a `bool Equals(Self other)`, and\n    // using `.Equals(object other)` allocates, which we want to avoid.\n    //\n    // We could instead generate custom .Equals for enums -- except that requires\n    // partial enums, and I'm not sure such things exist.\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    ) => $\"var {outVar} = {inVar1} == {inVar2};\";\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>\n        $\"var {outVar} = {inVar}.GetHashCode();\";\n}\n\n/// <summary>\n/// A use of a value type (that is not an enum).\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\npublic record ValueUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)\n{\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    ) => $\"var {outVar} = {inVar1}.Equals({inVar2});\";\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>\n        $\"var {outVar} = {inVar}.GetHashCode();\";\n}\n\n/// <summary>\n/// A use of a nullable value type (e.g. <c>int?</c>, <c>MyStruct?</c>).\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\npublic record NullableUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)\n{\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    ) => $\"var {outVar} = System.Nullable.Equals({inVar1}, {inVar2});\";\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>\n        $\"var {outVar} = {inVar}.GetHashCode();\";\n}\n\n/// <summary>\n/// A use of a reference type.\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\npublic record ReferenceUse(string Type, string TypeInfo) : TypeUse(Type, TypeInfo)\n{\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    ) => $\"var {outVar} = {inVar1} == null ? {inVar2} == null : {inVar1}.Equals({inVar2});\";\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>\n        $\"var {outVar} = {inVar} == null ? 0 : {inVar}.GetHashCode();\";\n}\n\n/// <summary>\n/// A use of an array type.\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\n/// <param name=\"ElementType\"></param>\npublic record ArrayUse(string Type, string TypeInfo, TypeUse ElementType) : TypeUse(Type, TypeInfo)\n{\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    )\n    {\n        var iterVar = $\"___i{level}\";\n        var innerOutVar = $\"___out{level + 1}\";\n\n        return $$\"\"\"\n            var {{outVar}} = true;\n            if ({{inVar1}} == null || {{inVar2}} == null) {\n                {{outVar}} = {{inVar1}} == {{inVar2}};\n            } else if ({{inVar1}}.Length != {{inVar2}}.Length) {\n                {{outVar}} = false;\n            } else {\n                for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Length; {{iterVar}}++) {\n                    {{ElementType.EqualsStatement(\n                $\"{inVar1}[{iterVar}]\",\n                $\"{inVar2}[{iterVar}]\",\n                innerOutVar,\n                level + 1\n            )}}\n                    if (!{{innerOutVar}}) {\n                        {{outVar}} = false;\n                        break;\n                    }\n                }\n            }\n            \"\"\";\n    }\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0)\n    {\n        var iterVar = $\"___i{level}\";\n        var innerHashCode = $\"___hc{level}\";\n        var innerOutVar = $\"___out{level + 1}\";\n\n        return $$\"\"\"\n            var {{outVar}} = 0;\n            if ({{inVar}} != null) {\n                var {{innerHashCode}} = new System.HashCode();\n                for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Length; {{iterVar}}++) {\n                    {{ElementType.GetHashCodeStatement(\n                $\"{inVar}[{iterVar}]\",\n                innerOutVar,\n                level + 1\n            )}}\n                    {{innerHashCode}}.Add({{innerOutVar}});\n                }\n                {{outVar}} = {{innerHashCode}}.ToHashCode();\n            }\n            \"\"\";\n    }\n}\n\n/// <summary>\n/// A use of a list type.\n/// </summary>\n/// <param name=\"Type\"></param>\n/// <param name=\"TypeInfo\"></param>\n/// <param name=\"ElementType\"></param>\npublic record ListUse(string Type, string TypeInfo, TypeUse ElementType) : TypeUse(Type, TypeInfo)\n{\n    public override string EqualsStatement(\n        string inVar1,\n        string inVar2,\n        string outVar,\n        int level = 0\n    )\n    {\n        var iterVar = $\"___i{level}\";\n        // needed to avoid warnings on list re-reference.\n        var innerTmp1 = $\"___tmpA{level}\";\n        var innerTmp2 = $\"___tmpB{level}\";\n        var innerOutVar = $\"___out{level + 1}\";\n\n        return $$\"\"\"\n            var {{outVar}} = true;\n            if ({{inVar1}} == null || {{inVar2}} == null) {\n                {{outVar}} = {{inVar1}} == {{inVar2}};\n            } else if ({{inVar1}}.Count != {{inVar2}}.Count) {\n                {{outVar}} = false;\n            } else {\n                for (int {{iterVar}} = 0; {{iterVar}} < {{inVar1}}.Count; {{iterVar}}++) {\n                    var {{innerTmp1}} = {{inVar1}}[{{iterVar}}];\n                    var {{innerTmp2}} = {{inVar2}}[{{iterVar}}];\n                    {{ElementType.EqualsStatement(innerTmp1, innerTmp2, innerOutVar, level + 1)}}\n                    if (!{{innerOutVar}}) {\n                        {{outVar}} = false;\n                        break;\n                    }\n                }\n            }\n            \"\"\";\n    }\n\n    public override string GetHashCodeStatement(string inVar, string outVar, int level = 0)\n    {\n        var iterVar = $\"___i{level}\";\n        var innerTmp = $\"___tmp{level}\";\n        var innerHashCode = $\"___hc{level}\";\n        var innerOutVar = $\"___out{level + 1}\";\n\n        return $$\"\"\"\n            var {{outVar}} = 0;\n            if ({{inVar}} != null) {\n                var {{innerHashCode}} = new System.HashCode();\n                for (int {{iterVar}} = 0; {{iterVar}} < {{inVar}}.Count; {{iterVar}}++) {\n                    var {{innerTmp}} = {{inVar}}[{{iterVar}}];\n                    {{ElementType.GetHashCodeStatement(innerTmp, innerOutVar, level + 1)}}\n                    {{innerHashCode}}.Add({{innerOutVar}});\n                }\n                {{outVar}} = {{innerHashCode}}.ToHashCode();\n            }\n            \"\"\";\n    }\n}\n\n/// <summary>\n/// A declaration of a member of a product or sum type.\n/// </summary>\n/// <param name=\"Name\">The name of the member.</param>\n/// <param name=\"Type\">Type information relevant to the member.</param>\npublic record MemberDeclaration(\n    string Name,\n    // TODO: rename to `Type` once I've checked uses\n    TypeUse Type\n)\n{\n    public MemberDeclaration(ISymbol member, ITypeSymbol type, DiagReporter diag)\n        : this(member.Name, TypeUse.Parse(member, type, diag)) { }\n\n    public MemberDeclaration(IFieldSymbol field, DiagReporter diag)\n        : this(field, field.Type, diag) { }\n\n    public string Identifier => EscapeIdentifier(Name);\n\n    public static string GenerateBsatnFields(\n        Accessibility visibility,\n        IEnumerable<MemberDeclaration> members\n    )\n    {\n        var visStr = SyntaxFacts.GetText(visibility);\n        return string.Join(\n            \"\\n        \",\n            members.Select(m =>\n                $\"{visStr} static readonly {m.Type.ToBSATNString()} {m.Identifier}{TypeUse.BsatnFieldSuffix} = new();\"\n            )\n        );\n    }\n\n    public static string GenerateDefs(IEnumerable<MemberDeclaration> members) =>\n        string.Join(\n            \",\\n                \",\n            // we can't use nameof(m.Type.BsatnFieldName) because the bsatn field name differs from the logical name\n            // assigned in the type.\n            members.Select(m =>\n                $\"new(\\\"{m.Name}\\\", {m.Identifier}{TypeUse.BsatnFieldSuffix}.GetAlgebraicType(registrar))\"\n            )\n        );\n}\n\npublic enum TypeKind\n{\n    Product,\n    Sum,\n}\n\npublic abstract record BaseTypeDeclaration<M>\n    where M : MemberDeclaration, IEquatable<M>\n{\n    public readonly Scope Scope;\n    public readonly string ShortName;\n    public readonly string FullName;\n    public readonly TypeKind Kind;\n    public readonly EquatableArray<M> Members;\n\n    /// <summary>\n    /// Returns the escaped version of ShortName for use in generated C# code where the type name\n    /// appears as an identifier (e.g., in IEquatable&lt;T&gt; or as a base type reference).\n    /// </summary>\n    public string ShortNameIdentifier => EscapeIdentifier(ShortName);\n\n    protected abstract M ConvertMember(int index, IFieldSymbol field, DiagReporter diag);\n\n    public BaseTypeDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n    {\n        var typeSyntax = (TypeDeclarationSyntax)context.TargetNode;\n        var type = (INamedTypeSymbol)context.TargetSymbol;\n\n        // Note: we could use naively use `type.GetMembers()` to get all fields of the type,\n        // but some users add their own fields in extra partial declarations like this:\n        //\n        // ```csharp\n        // [SpacetimeDB.Type]\n        // partial class MyType\n        // {\n        //     public int TableField;\n        // }\n        //\n        // partial class MyType\n        // {\n        //     public int ExtraField;\n        // }\n        // ```\n        //\n        // In this scenario, only fields declared inside the declaration with the `[SpacetimeDB.Type]` attribute\n        // should be considered as BSATN fields, and others are expected to be ignored.\n        //\n        // To achieve this, we need to walk over the annotated type syntax node, collect the field names,\n        // and look up the resolved field symbols only for those fields.\n        var fields = typeSyntax\n            .Members.OfType<FieldDeclarationSyntax>()\n            .SelectMany(f => f.Declaration.Variables)\n            .Select(v => type.GetMembers(v.Identifier.Text).OfType<IFieldSymbol>().Single())\n            .Where(f => !f.IsStatic);\n\n        // Check if type implements generic `SpacetimeDB.TaggedEnum<Variants>` and, if so, extract the `Variants` type.\n        if (type.BaseType?.OriginalDefinition.ToString() == \"SpacetimeDB.TaggedEnum<Variants>\")\n        {\n            Kind = TypeKind.Sum;\n\n            if (\n                type.BaseType.TypeArguments.FirstOrDefault()\n                is not INamedTypeSymbol { IsTupleType: true, TupleElements: var taggedEnumVariants }\n            )\n            {\n                diag.Report(ErrorDescriptor.TaggedEnumInlineTuple, type.BaseType);\n            }\n\n            if (fields.FirstOrDefault() is { } field)\n            {\n                diag.Report(ErrorDescriptor.TaggedEnumField, field);\n            }\n\n            fields = taggedEnumVariants;\n        }\n        else\n        {\n            Kind = TypeKind.Product;\n        }\n\n        if (typeSyntax.TypeParameterList is { } typeParams)\n        {\n            diag.Report(ErrorDescriptor.TypeParams, typeParams);\n        }\n\n        Scope = new(typeSyntax);\n        ShortName = type.Name;\n        FullName = SymbolToName(type);\n        Members = new(\n            fields.Select((field, index) => ConvertMember(index, field, diag)).ToImmutableArray()\n        );\n    }\n\n    public static string JoinOrValue(\n        string join,\n        IEnumerable<String> stringArray,\n        string resultIfArrayEmpty\n    )\n    {\n        if (stringArray.Any())\n        {\n            return string.Join(join, stringArray);\n        }\n        return resultIfArrayEmpty;\n    }\n\n    public Scope.Extensions ToExtensions()\n    {\n        string read,\n            write,\n            getHashCode;\n\n        var extensions = new Scope.Extensions(Scope, FullName);\n\n        var bsatnDecls = Members.Cast<MemberDeclaration>();\n\n        extensions.BaseTypes.Add($\"System.IEquatable<{ShortNameIdentifier}>\");\n\n        if (Kind is TypeKind.Sum)\n        {\n            extensions.Contents.Append(\n                string.Join(\n                    \"\\n\",\n                    Members.Select(m =>\n                        // C# puts field names in the same namespace as records themselves, and will complain about clashes if they match.\n                        // To avoid this, we append an underscore to the field name.\n                        // In most cases the field name shouldn't matter anyway as you'll idiomatically use pattern matching to extract the value.\n                        $$\"\"\"\n                            public sealed record {{m.Identifier}}({{m.Type.Name}} {{m.Identifier}}_) : {{ShortNameIdentifier}}\n                            {\n                                public override string ToString() =>\n                                    $\"{{m.Name}}({ SpacetimeDB.BSATN.StringUtil.GenericToString({{m.Identifier}}_) })\";\n                            }\n                        \n                        \"\"\"\n                    )\n                )\n            );\n\n            read = $$\"\"\"\n                    return reader.ReadByte() switch {\n                        {{string.Join(\n                            \"\\n            \",\n                            bsatnDecls.Select((m, i) =>\n                                $\"{i} => new {m.Identifier}({m.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader)),\"\n                            )\n                        )}}\n                        _ => throw new System.InvalidOperationException(\"Invalid tag value, this state should be unreachable.\")\n                    };\n            \"\"\";\n\n            write = $$\"\"\"\n            switch (value) {\n            {{string.Join(\n                \"\\n\",\n                bsatnDecls.Select((m, i) => $\"\"\"\n                                                            case {m.Identifier}(var inner):\n                                                                writer.Write((byte){i});\n                                                                {m.Identifier}{TypeUse.BsatnFieldSuffix}.Write(writer, inner);\n                                                                break;\n                                                \"\"\"))}}\n                        }\n            \"\"\";\n\n            getHashCode = $$\"\"\"\n            switch (this) {\n            {{string.Join(\n                    \"\\n\",\n                    bsatnDecls\n                    .Select(member =>\n                    {\n                        var hashName = $\"___hash{member.Name}\";\n\n                        return $\"\"\"\n                                case {member.Identifier}(var inner):\n                                    {member.Type.GetHashCodeStatement(\"inner\", hashName)}\n                                    return {hashName};\n                        \"\"\";\n                    }))}}\n                    default:\n                        return 0;\n                    }\n            \"\"\";\n        }\n        else\n        {\n            extensions.BaseTypes.Add(\"SpacetimeDB.BSATN.IStructuralReadWrite\");\n\n            extensions.Contents.Append(\n                $$\"\"\"\n                public void ReadFields(System.IO.BinaryReader reader) {\n            {{string.Join(\n                    \"\\n\",\n                    bsatnDecls.Select(m => $\"        {m.Identifier} = BSATN.{m.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader);\")\n                )}}\n                }\n\n                public void WriteFields(System.IO.BinaryWriter writer) {\n            {{string.Join(\n                    \"\\n\",\n                    bsatnDecls.Select(m => $\"        BSATN.{m.Identifier}{TypeUse.BsatnFieldSuffix}.Write(writer, {m.Identifier});\")\n                )}}\n                }\n\n                object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer() {\n                    return new BSATN();\n                }\n\n            \"\"\"\n            );\n\n            // escaping hell\n            var start = \"{{\";\n            var end = \"}}\";\n\n            extensions.Contents.Append(\n                $$\"\"\"\n                public override string ToString() =>\n                    $\"{{ShortName}} {{start}} {{string.Join(\n                        \", \",\n                        bsatnDecls.Select(m => $$\"\"\"{{m.Name}} = {SpacetimeDB.BSATN.StringUtil.GenericToString({{m.Identifier}})}\"\"\")\n                    )}} {{end}}\";\n            \"\"\"\n            );\n\n            // Directly allocating the result object here (instead of calling e.g. IStructuralReadWrite.Read<T>, which does the same thing)\n            // avoids generics; we've found that generics often result in reflective code being generated.\n            // Using simple code here hopefully helps IL2CPP and Mono do this faster.\n            read = $$\"\"\"\n                    var ___result = new {{FullName}}();\n                    ___result.ReadFields(reader);\n                    return ___result;\n                \"\"\";\n\n            write = \"value.WriteFields(writer);\";\n\n            var declHashName = (MemberDeclaration decl) => $\"___hash{decl.Name}\";\n\n            getHashCode = $$\"\"\"\n                {{string.Join(\"\\n\", bsatnDecls.Select(decl => decl.Type.GetHashCodeStatement(decl.Identifier, declHashName(decl))))}}\n                return {{JoinOrValue(\n                    \" ^\\n            \",\n                    bsatnDecls.Select(declHashName),\n                    \"0\" // if there are no members, the hash is 0.\n                )}};\n                \"\"\";\n        }\n\n        extensions.Contents.Append(\n            $$\"\"\"\n\n                public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<{{FullName}}>\n                {\n                    {{MemberDeclaration.GenerateBsatnFields(Accessibility.Internal, bsatnDecls)}}\n\n                    public {{FullName}} Read(System.IO.BinaryReader reader) {\n                        {{read}}\n                    }\n\n                    public void Write(System.IO.BinaryWriter writer, {{FullName}} value) {\n                        {{write}}\n                    }\n\n                    public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(SpacetimeDB.BSATN.ITypeRegistrar registrar) =>\n                        registrar.RegisterType<{{FullName}}>(_ => new SpacetimeDB.BSATN.AlgebraicType.{{Kind}}(new SpacetimeDB.BSATN.AggregateElement[] {\n                            {{MemberDeclaration.GenerateDefs(Members)}}\n                        }));\n\n                    SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<{{FullName}}>.GetAlgebraicType(SpacetimeDB.BSATN.ITypeRegistrar registrar) =>\n                        GetAlgebraicType(registrar);\n                }\n                \n                public override int GetHashCode()\n                {\n                    {{getHashCode}}\n                }\n            \n            \"\"\"\n        );\n\n        if (!Scope.IsRecord)\n        {\n            // If we are a reference type, various equality methods need to take nullable references.\n            // If we are a value type, everything is pleasantly by-value.\n            var fullNameMaybeRef = $\"{FullName}{(Scope.IsStruct ? \"\" : \"?\")}\";\n            var declEqualsName = (MemberDeclaration decl) => $\"___eq{decl.Name}\";\n\n            extensions.Contents.Append(\n                $$\"\"\"\n\n            #nullable enable\n                public bool Equals({{fullNameMaybeRef}} that)\n                {\n                    {{(Scope.IsStruct ? \"\" : \"if (((object?)that) == null) { return false; }\\n        \")}}\n                    {{string.Join(\"\\n\", bsatnDecls.Select(decl => decl.Type.EqualsStatement($\"this.{decl.Identifier}\", $\"that.{decl.Identifier}\", declEqualsName(decl))))}}\n                    return {{JoinOrValue(\n                        \" &&\\n        \",\n                        bsatnDecls.Select(declEqualsName),\n                        \"true\" // if there are no elements, the structs are equal :)\n                    )}};\n                }\n\n                public override bool Equals(object? that) {\n                    if (that == null) {\n                        return false;\n                    }\n                    var that_ = that as {{FullName}}{{(Scope.IsStruct ? \"?\" : \"\")}};\n                    if (((object?)that_) == null) {\n                        return false;\n                    }\n                    return Equals(that_);\n                }\n\n                public static bool operator == ({{fullNameMaybeRef}} this_, {{fullNameMaybeRef}} that) {\n                    if (((object?)this_) == null || ((object?)that) == null) {\n                        return object.Equals(this_, that);\n                    }\n                    return this_.Equals(that);\n                }\n\n                public static bool operator != ({{fullNameMaybeRef}} this_, {{fullNameMaybeRef}} that) {\n                    if (((object?)this_) == null || ((object?)that) == null) {\n                        return !object.Equals(this_, that);\n                    }\n                    return !this_.Equals(that);\n                }\n            #nullable restore\n            \"\"\"\n            );\n        }\n\n        return extensions;\n    }\n}\n\nrecord TypeDeclaration : BaseTypeDeclaration<MemberDeclaration>\n{\n    public TypeDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n        : base(context, diag) { }\n\n    protected override MemberDeclaration ConvertMember(\n        int index,\n        IFieldSymbol field,\n        DiagReporter diag\n    ) => new(field, diag);\n}\n\n[Generator]\npublic class Type : IIncrementalGenerator\n{\n    public void Initialize(IncrementalGeneratorInitializationContext context)\n    {\n        context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                \"SpacetimeDB.TypeAttribute\",\n                // for enums, we just do diagnostics, nothing else\n                predicate: (node, ct) => node is EnumDeclarationSyntax,\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag =>\n                    {\n                        var enumType = (EnumDeclarationSyntax)context.TargetNode;\n\n                        // Ensure variants are contiguous as SATS enums don't support explicit tags.\n                        foreach (var variant in enumType.Members)\n                        {\n                            if (variant.EqualsValue is { } equalsValue)\n                            {\n                                diag.Report(\n                                    ErrorDescriptor.EnumWithExplicitValues,\n                                    (equalsValue, variant, enumType)\n                                );\n                            }\n                        }\n\n                        // Ensure all enums fit in `byte` as that's what SATS uses for tags.\n                        if (enumType.Members.Count > 256)\n                        {\n                            diag.Report(ErrorDescriptor.EnumTooManyVariants, enumType);\n                        }\n\n                        // Unused empty type.\n                        return default(ValueTuple);\n                    })\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Type.ParseEnum\");\n\n        context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                \"SpacetimeDB.TypeAttribute\",\n                // parse anything except enums here (they're handled above)\n                predicate: (node, ct) => node is not EnumDeclarationSyntax,\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new TypeDeclaration(context, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Type.Parse\")\n            .Select((type, ct) => type.ToExtensions())\n            .WithTrackingName(\"SpacetimeDB.Type.GenerateExtensions\")\n            .RegisterSourceOutputs(context);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Codegen/Utils.cs",
    "content": "namespace SpacetimeDB.Codegen;\n\nusing System.Collections;\nusing System.Collections.Immutable;\nusing System.Text;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing static System.Collections.StructuralComparisons;\n\npublic static class Utils\n{\n    // Even `ImmutableArray<T>` is not deeply equatable, which makes it a common\n    // pain point for source generators as they must use only cacheable types.\n    // As a result, everyone builds their own `EquatableArray<T>` type.\n    public readonly record struct EquatableArray<T>(ImmutableArray<T> Array) : IEnumerable<T>\n        where T : IEquatable<T>\n    {\n        public int Length => Array.Length;\n        public T this[int index] => Array[index];\n\n        public bool Equals(EquatableArray<T> other) => Array.SequenceEqual(other.Array);\n\n        public override int GetHashCode() => StructuralEqualityComparer.GetHashCode(Array);\n\n        public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)Array).GetEnumerator();\n\n        IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)Array).GetEnumerator();\n    }\n\n    private static readonly SymbolDisplayFormat SymbolFormat = SymbolDisplayFormat\n        .FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)\n        .AddMemberOptions(SymbolDisplayMemberOptions.IncludeContainingType)\n        .AddMiscellaneousOptions(\n            SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier\n                | SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers\n        );\n\n    public static string SymbolToName(ISymbol symbol)\n    {\n        return symbol.ToDisplayString(SymbolFormat);\n    }\n\n    public static string EscapeIdentifier(string name)\n    {\n        if (name.Length > 0 && name[0] == '@')\n        {\n            return name;\n        }\n\n        var kind = SyntaxFacts.GetKeywordKind(name);\n        var contextualKind = SyntaxFacts.GetContextualKeywordKind(name);\n        return kind != SyntaxKind.None || contextualKind != SyntaxKind.None ? $\"@{name}\" : name;\n    }\n\n    public static void RegisterSourceOutputs(\n        this IncrementalValuesProvider<Scope.Extensions> methods,\n        IncrementalGeneratorInitializationContext context\n    )\n    {\n        context.RegisterSourceOutput(\n            methods,\n            (context, method) =>\n            {\n                // Unfortunately, Roslyn doesn't expose its list of valid hintName characters\n                // (https://github.com/dotnet/roslyn/blob/a69841b8ca9751bee0fe9fdeedc705e198e195d9/src/Compilers/Core/Portable/SourceGeneration/AdditionalSourcesCollection.cs#L43-L66)\n                // but it does complain if you try to use invalid one. Let's do a conservative cleanup.\n                var name = string.Concat(\n                    method.FullName.Select(c =>\n                        SyntaxFacts.IsIdentifierPartCharacter(c) || c == '.' ? c : '_'\n                    )\n                );\n                context.AddSource(\n                    $\"{name}.cs\",\n                    $\"\"\"\n                    // <auto-generated />\n                    #nullable enable\n\n                    {method}\n                    \"\"\"\n                );\n            }\n        );\n    }\n\n    public static string MakeRwTypeParam(string typeParam) => typeParam + \"RW\";\n\n    public class UnresolvedTypeException(INamedTypeSymbol type)\n        : InvalidOperationException($\"Could not resolve type {type}\") { }\n\n    /// <summary>\n    /// Return whether a type is a nullable, non-value type.\n    /// For example, `string?`.\n    /// </summary>\n    /// <param name=\"type\"></param>\n    /// <returns></returns>\n    public static bool IsNullableReferenceType(ITypeSymbol type) =>\n        // We need to distinguish handle nullable reference types specially:\n        // compiler expands something like `int?` to `System.Nullable<int>` with the nullable annotation set to `Annotated`\n        // while something like `string?` is expanded to `string` with the nullable annotation set to `Annotated`.\n        type.NullableAnnotation == NullableAnnotation.Annotated\n        && type.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T;\n\n    /// <summary>\n    /// Get the BSATN struct name for a type.\n    /// </summary>\n    /// <param name=\"type\"></param>\n    /// <returns></returns>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    /// <exception cref=\"UnresolvedTypeException\"></exception>\n    public static string GetTypeInfo(ITypeSymbol type)\n    {\n        if (IsNullableReferenceType(type))\n        {\n            // If we're here, then this is a nullable reference type like `string?` and the original definition is `string`.\n            type = type.WithNullableAnnotation(NullableAnnotation.None);\n            return $\"SpacetimeDB.BSATN.RefOption<{type}, {GetTypeInfo(type)}>\";\n        }\n\n        return type switch\n        {\n            ITypeParameterSymbol typeParameter => MakeRwTypeParam(typeParameter.Name),\n            INamedTypeSymbol namedType => type.SpecialType switch\n            {\n                SpecialType.System_Boolean => \"SpacetimeDB.BSATN.Bool\",\n                SpecialType.System_SByte => \"SpacetimeDB.BSATN.I8\",\n                SpecialType.System_Byte => \"SpacetimeDB.BSATN.U8\",\n                SpecialType.System_Int16 => \"SpacetimeDB.BSATN.I16\",\n                SpecialType.System_UInt16 => \"SpacetimeDB.BSATN.U16\",\n                SpecialType.System_Int32 => \"SpacetimeDB.BSATN.I32\",\n                SpecialType.System_UInt32 => \"SpacetimeDB.BSATN.U32\",\n                SpecialType.System_Int64 => \"SpacetimeDB.BSATN.I64\",\n                SpecialType.System_UInt64 => \"SpacetimeDB.BSATN.U64\",\n                SpecialType.System_Single => \"SpacetimeDB.BSATN.F32\",\n                SpecialType.System_Double => \"SpacetimeDB.BSATN.F64\",\n                SpecialType.System_String => \"SpacetimeDB.BSATN.String\",\n                SpecialType.None => GetTypeInfoForNamedType(namedType),\n                _ => throw new InvalidOperationException(\n                    $\"Unsupported special type {type} ({type.SpecialType})\"\n                ),\n            },\n            IArrayTypeSymbol { ElementType: var elementType } => elementType.SpecialType\n            == SpecialType.System_Byte\n                ? \"SpacetimeDB.BSATN.ByteArray\"\n                : $\"SpacetimeDB.BSATN.Array<{elementType}, {GetTypeInfo(elementType)}>\",\n            _ => throw new InvalidOperationException($\"Unsupported type {type}\"),\n        };\n\n        static string GetTypeInfoForNamedType(INamedTypeSymbol type)\n        {\n            if (type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Error)\n            {\n                throw new UnresolvedTypeException(type);\n            }\n            if (type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Enum)\n            {\n                if (\n                    !type.GetAttributes()\n                        .Any(a => a.AttributeClass?.ToString() == \"SpacetimeDB.TypeAttribute\")\n                )\n                {\n                    throw new InvalidOperationException(\n                        $\"Enum {type} does not have a [SpacetimeDB.Type] attribute\"\n                    );\n                }\n                return $\"SpacetimeDB.BSATN.Enum<{SymbolToName(type)}>\";\n            }\n            var result = type.OriginalDefinition.ToString() switch\n            {\n                // {U/I}{128/256} are not treated by C# as regular primitives, so we need to match them by type name.\n                \"System.Int128\" => \"SpacetimeDB.BSATN.I128\",\n                \"System.UInt128\" => \"SpacetimeDB.BSATN.U128\",\n                \"SpacetimeDB.I128\" => \"SpacetimeDB.BSATN.I128Stdb\",\n                \"SpacetimeDB.U128\" => \"SpacetimeDB.BSATN.U128Stdb\",\n                \"SpacetimeDB.I256\" => \"SpacetimeDB.BSATN.I256\",\n                \"SpacetimeDB.U256\" => \"SpacetimeDB.BSATN.U256\",\n                \"System.Collections.Generic.List<T>\" => $\"SpacetimeDB.BSATN.List\",\n                // If we're here, then this is nullable *value* type like `int?`.\n                \"System.Nullable<T>\" => $\"SpacetimeDB.BSATN.ValueOption\",\n                var name when name.StartsWith(\"System.\") => throw new InvalidOperationException(\n                    $\"Unsupported system type {name}\"\n                ),\n                _ => $\"{SymbolToName(type)}.BSATN\",\n            };\n            if (type.IsGenericType)\n            {\n                result =\n                    $\"{result}<{string.Join(\", \", type.TypeArguments.Select(SymbolToName).Concat(type.TypeArguments.Select(GetTypeInfo)))}>\";\n            }\n\n            return result;\n        }\n    }\n\n    // Polyfill for .NET methods from .NET Standard 2.1+:\n    private static StringBuilder AppendJoin<T>(\n        this StringBuilder sb,\n        string separator,\n        IEnumerable<T> values\n    )\n    {\n        var first = true;\n        foreach (var value in values)\n        {\n            if (!first)\n            {\n                sb.Append(separator);\n            }\n            first = false;\n            sb.Append(value);\n        }\n        return sb;\n    }\n\n    private static object? ResolveConstant(TypedConstant constant, System.Type targetType)\n    {\n        if (constant.Kind == TypedConstantKind.Array)\n        {\n            // We can't use LINQ ToArray() here because it doesn't support dynamic Type\n            // and will build `object[]` instead of the desired `T[]`.\n            var elementType = targetType.GetElementType();\n            var array = Array.CreateInstance(elementType, constant.Values.Length);\n            for (var i = 0; i < constant.Values.Length; i++)\n            {\n                array.SetValue(ResolveConstant(constant.Values[i], elementType), i);\n            }\n            return array;\n        }\n        return constant.Value;\n    }\n\n    public static T ParseAs<T>(this AttributeData attrData, System.Type? type = null)\n        where T : Attribute\n    {\n        type ??= typeof(T);\n\n        // For now only support attributes with a single constructor.\n        //\n        // Proper overload resolution is complicated due to implicit casts\n        // (in particular, enums are represented as integers in the attribute data),\n        // which prevent APIs like `Activator.CreateInstance` from finding the constructor.\n        //\n        // Expand logic in the future if it ever becomes actually necessary.\n        var ctor = type.GetConstructors().Single();\n\n        var ctorArgs = attrData\n            .ConstructorArguments.Zip(\n                ctor.GetParameters().Select(param => param.ParameterType),\n                ResolveConstant\n            )\n            .ToArray();\n        var attr = (T)ctor.Invoke(ctorArgs);\n        foreach (var arg in attrData.NamedArguments)\n        {\n            var prop = type.GetProperty(arg.Key);\n            prop.SetValue(attr, ResolveConstant(arg.Value, prop.PropertyType));\n        }\n        return attr;\n    }\n\n    // Borrowed & modified code for generating in-place extensions for partial structs/classes/etc. Source:\n    // https://andrewlock.net/creating-a-source-generator-part-5-finding-a-type-declarations-namespace-and-type-hierarchy/\n\n    public readonly record struct Scope\n    {\n        // Reversed list of typescopes, from innermost to outermost.\n        private readonly EquatableArray<TypeScope> typeScopes;\n\n        // Reversed list of namespaces, from innermost to outermost.\n        private readonly EquatableArray<string> namespaces;\n\n        public Scope(MemberDeclarationSyntax? node)\n        {\n            var typeScopes_ = ImmutableArray.CreateBuilder<TypeScope>();\n            // Keep looping while we're in a supported nested type\n            while (node is TypeDeclarationSyntax type)\n            {\n                // Record the parent type keyword (class/struct etc), name, and constraints\n                typeScopes_.Add(\n                    new TypeScope(\n                        Keyword: type.Keyword.ValueText,\n                        Name: type.Identifier.ToString() + type.TypeParameterList,\n                        Constraints: type.ConstraintClauses.ToString()\n                    )\n                ); // set the child link (null initially)\n\n                // Move to the next outer type\n                node = type.Parent as MemberDeclarationSyntax;\n            }\n            typeScopes = new(typeScopes_.ToImmutable());\n\n            // We've now reached the outermost type, so we can determine the namespace\n            var namespaces_ = ImmutableArray.CreateBuilder<string>();\n            while (node is BaseNamespaceDeclarationSyntax ns)\n            {\n                namespaces_.Add(ns.Name.ToString());\n                node = node.Parent as MemberDeclarationSyntax;\n            }\n            namespaces = new(namespaces_.ToImmutable());\n        }\n\n        /// <returns>Whether this Scope is a struct declaration.</returns>\n        public bool IsStruct\n        {\n            get => typeScopes[0].Keyword == \"struct\";\n        }\n\n        /// <returns>Whether this Scope is a record declaration.</returns>\n        public bool IsRecord\n        {\n            get => typeScopes[0].Keyword == \"record\";\n        }\n\n        public readonly record struct TypeScope(string Keyword, string Name, string Constraints);\n\n        public sealed record Extensions(Scope Scope, string FullName)\n        {\n            public readonly StringBuilder Contents = new();\n            public readonly List<string> BaseTypes = [];\n            public readonly List<string> ExtraAttrs = [];\n\n            public override string ToString()\n            {\n                var sb = new StringBuilder();\n\n                // Join all namespaces into a single namespace statement, starting with the outermost.\n                if (Scope.namespaces.Length > 0)\n                {\n                    sb.Append(\"namespace \")\n                        .AppendJoin(\".\", Scope.namespaces.Reverse())\n                        .AppendLine(\" {\");\n                }\n\n                // Loop through the full parent type hierarchy, starting with the outermost.\n                foreach (\n                    var (i, typeScope) in Scope.typeScopes.Select((ts, i) => (i, ts)).Reverse()\n                )\n                {\n                    if (i == 0)\n                    {\n                        foreach (var extraAttr in ExtraAttrs)\n                        {\n                            sb.AppendLine(extraAttr);\n                        }\n                    }\n\n                    sb.Append(\"partial \")\n                        .Append(typeScope.Keyword) // e.g. class/struct/record\n                        .Append(' ')\n                        .Append(typeScope.Name) // e.g. Outer/Generic<T>\n                        .Append(' ');\n\n                    if (i == 0 && BaseTypes.Count > 0)\n                    {\n                        sb.Append(\" : \").AppendJoin(\", \", BaseTypes);\n                    }\n\n                    if (typeScope.Constraints.Length > 0)\n                    {\n                        sb.Append(' ').Append(typeScope.Constraints);\n                    }\n                    sb.AppendLine(\" {\");\n                }\n\n                sb.AppendLine();\n                sb.Append(Contents);\n                sb.AppendLine();\n\n                // We need to \"close\" each of the parent types, so write\n                // the required number of '}'\n                foreach (var typeScope in Scope.typeScopes)\n                {\n                    sb.Append(\"} // \").AppendLine(typeScope.Name);\n                }\n\n                // Close the namespace, if we had one\n                if (Scope.namespaces.Length > 0)\n                {\n                    sb.AppendLine(\"} // namespace\");\n                }\n\n                return sb.ToString();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/.gitignore",
    "content": "/bin.meta\n/obj.meta\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/Attrs.cs",
    "content": "﻿namespace SpacetimeDB;\n\nusing System.Runtime.CompilerServices;\n\n[AttributeUsage(\n    AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Enum,\n    Inherited = false,\n    AllowMultiple = false\n)]\npublic sealed class TypeAttribute : Attribute { }\n\n// This could be an interface, but using `record` forces C# to check that it can\n// only be applied on types that are records themselves.\npublic abstract record TaggedEnum<Variants>\n    where Variants : struct, ITuple { }\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs",
    "content": "namespace SpacetimeDB.BSATN;\n\npublic interface ITypeRegistrar\n{\n    AlgebraicType.Ref RegisterType<T>(Func<AlgebraicType.Ref, AlgebraicType> type);\n}\n\n[SpacetimeDB.Type]\npublic partial struct AggregateElement(string name, AlgebraicType algebraicType)\n{\n    public string? Name = name;\n\n    public AlgebraicType AlgebraicType = algebraicType;\n}\n\n[SpacetimeDB.Type]\npublic partial record AlgebraicType\n    : SpacetimeDB.TaggedEnum<(\n        int Ref,\n        AggregateElement[] Sum,\n        AggregateElement[] Product,\n        AlgebraicType Array,\n        Unit String,\n        Unit Bool,\n        Unit I8,\n        Unit U8,\n        Unit I16,\n        Unit U16,\n        Unit I32,\n        Unit U32,\n        Unit I64,\n        Unit U64,\n        Unit I128,\n        Unit U128,\n        Unit I256,\n        Unit U256,\n        Unit F32,\n        Unit F64\n    )>\n{\n    public static readonly AlgebraicType Unit = new Product([]);\n    public const string QueryBuilderProductTypeTag = \"__query__\";\n\n    // Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as an Option<T>.\n    internal static AlgebraicType MakeOption(AlgebraicType someType) =>\n        new Sum([new(\"some\", someType), new(\"none\", Unit)]);\n\n    // Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as a Result<T, E>.\n    internal static AlgebraicType MakeResult(AlgebraicType okType, AlgebraicType errType) =>\n        new Sum([new(\"ok\", okType), new(\"err\", errType)]);\n\n    // Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as Query<T>.\n    public static AlgebraicType MakeQueryBuilderProductType(Ref rowProductTypeRef) =>\n        new Product([new(QueryBuilderProductTypeTag, rowProductTypeRef)]);\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/I128.cs",
    "content": "// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nnamespace SpacetimeDB;\n\nusing System.Numerics;\nusing System.Runtime.InteropServices;\n\n/// <summary>Represents a 128-bit signed integer.</summary>\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct I128 : IEquatable<I128>, IComparable, IComparable<I128>\n{\n    internal const int Size = 16;\n\n#if BIGENDIAN\n    private readonly ulong _upper;\n    private readonly ulong _lower;\n#else\n    private readonly ulong _lower;\n    private readonly ulong _upper;\n#endif\n\n    /// <summary>Initializes a new instance of the <see cref=\"I128\" /> struct.</summary>\n    /// <param name=\"upper\">The upper 64-bits of the 128-bit value.</param>\n    /// <param name=\"lower\">The lower 64-bits of the 128-bit value.</param>\n    public I128(ulong upper, ulong lower)\n    {\n        _upper = upper;\n        _lower = lower;\n    }\n\n    internal ulong Lower => _lower;\n\n    internal ulong Upper => _upper;\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is I128 other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a I128\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(I128 value)\n    {\n        if (this < value)\n        {\n            return -1;\n        }\n        else if (this > value)\n        {\n            return 1;\n        }\n        else\n        {\n            return 0;\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)\" />\n    public static bool operator <(I128 left, I128 right)\n    {\n        if (IsNegative(left) == IsNegative(right))\n        {\n            return (left._upper < right._upper)\n                || ((left._upper == right._upper) && (left._lower < right._lower));\n        }\n        else\n        {\n            return IsNegative(left);\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)\" />\n    public static bool operator >(I128 left, I128 right)\n    {\n        if (IsNegative(left) == IsNegative(right))\n        {\n            return (left._upper > right._upper)\n                || ((left._upper == right._upper) && (left._lower > right._lower));\n        }\n        else\n        {\n            return IsNegative(right);\n        }\n    }\n\n    /// <inheritdoc cref=\"INumberBase{TSelf}.IsNegative(TSelf)\" />\n    public static bool IsNegative(I128 value) => (long)value._upper < 0;\n\n    private BigInteger AsBigInt() =>\n        new(\n            MemoryMarshal.AsBytes(stackalloc[] { this }),\n            isUnsigned: false,\n            isBigEndian: !BitConverter.IsLittleEndian\n        );\n\n    /// <inheritdoc cref=\"object.ToString()\" />\n    public override string ToString() => AsBigInt().ToString();\n\n    /// <summary>Implicitly converts a <see cref=\"int\" /> value to a 128-bit signed integer.</summary>\n    /// <param name=\"value\">The value to convert.</param>\n    /// <returns><paramref name=\"value\" /> converted to a 128-bit signed integer.</returns>\n    public static implicit operator I128(int value)\n    {\n        long lower = value;\n        return new I128((ulong)(lower >> 63), (ulong)lower);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/I256.cs",
    "content": "// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nnamespace SpacetimeDB;\n\nusing System;\nusing System.Numerics;\nusing System.Runtime.InteropServices;\n\n/// <summary>Represents a 256-bit signed integer.</summary>\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct I256 : IEquatable<I256>, IComparable, IComparable<I256>\n{\n    internal const int Size = 32;\n\n#if BIGENDIAN\n    private readonly U128 _upper;\n    private readonly U128 _lower;\n#else\n    private readonly U128 _lower;\n    private readonly U128 _upper;\n#endif\n\n    /// <summary>Initializes a new instance of the <see cref=\"I256\" /> struct.</summary>\n    /// <param name=\"upper\">The upper 128-bits of the 256-bit value.</param>\n    /// <param name=\"lower\">The lower 128-bits of the 256-bit value.</param>\n    public I256(U128 upper, U128 lower)\n    {\n        _upper = upper;\n        _lower = lower;\n    }\n\n    internal U128 Lower => _lower;\n\n    internal U128 Upper => _upper;\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is I256 other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a I256\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(I256 value)\n    {\n        if (this < value)\n        {\n            return -1;\n        }\n        else if (this > value)\n        {\n            return 1;\n        }\n        else\n        {\n            return 0;\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)\" />\n    public static bool operator <(I256 left, I256 right)\n    {\n        if (IsNegative(left) == IsNegative(right))\n        {\n            return (left._upper < right._upper)\n                || ((left._upper == right._upper) && (left._lower < right._lower));\n        }\n        else\n        {\n            return IsNegative(left);\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)\" />\n    public static bool operator >(I256 left, I256 right)\n    {\n        if (IsNegative(left) == IsNegative(right))\n        {\n            return (left._upper > right._upper)\n                || ((left._upper == right._upper) && (left._lower > right._lower));\n        }\n        else\n        {\n            return IsNegative(right);\n        }\n    }\n\n    /// <inheritdoc cref=\"INumberBase{TSelf}.IsNegative(TSelf)\" />\n\n    public static bool IsNegative(I256 value) => (long)value._upper.Upper < 0;\n\n    private BigInteger AsBigInt() =>\n        new(\n            MemoryMarshal.AsBytes(stackalloc[] { this }),\n            isUnsigned: false,\n            isBigEndian: !BitConverter.IsLittleEndian\n        );\n\n    /// <inheritdoc cref=\"object.ToString()\" />\n    public override string ToString() => AsBigInt().ToString();\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs",
    "content": "namespace SpacetimeDB.BSATN;\n\nusing System.Text;\n\n/// <summary>\n/// Implemented by product types marked with [SpacetimeDB.Type].\n/// All rows in SpacetimeDB are product types, so this is also implemented by all row types.\n/// </summary>\npublic interface IStructuralReadWrite\n{\n    /// <summary>\n    /// Initialize this value from the reader.\n    /// The reader is assumed to store a <see href=\"https://spacetimedb.com/docs/bsatn\">BSATN-encoded</see>\n    /// value of this type.\n    /// Advances the reader to the first byte past the end of the read value.\n    /// Throws an exception if this would advance past the end of the reader.\n    /// </summary>\n    /// <param name=\"reader\"></param>\n    void ReadFields(BinaryReader reader);\n\n    /// <summary>\n    /// Write the fields of this type to the writer.\n    /// Throws an exception if the underlying writer throws.\n    /// Throws if this value is malformed (i.e. has null values for fields that\n    /// are not explicitly marked nullable.)\n    /// </summary>\n    /// <param name=\"writer\"></param>\n    void WriteFields(BinaryWriter writer);\n\n    /// <summary>\n    /// Get an IReadWrite implementation that can read values of this type.\n    /// In Rust, this would return <c>IReadWrite&lt;Self&gt;</c>, but the C# type system\n    /// has no equivalent Self type -- that is, you can't use the implementing type in type signatures\n    /// in an interface. So, you have to manually downcast.\n    /// A typical invocation looks like: <c>(IReadWrite&lt;Row&gt;) new Row().GetSerializer()</c>\n    ///\n    /// This is an instance method because of limitations of C# interfaces.\n    /// (C# 11 has static virtual interface members, but Unity does not support C# 11.)\n    /// This method always works, whether or not the row it is called on is correctly initialized.\n    /// The returned serializer has nothing to do with the row GetSerializer is called on -- it returns\n    /// new rows and does not modify or interact with the original row.\n    ///\n    /// Using the resulting serializer rather than <c>Read&lt;T&gt;</c> is usually faster in Mono/IL2CPP.\n    /// This is because we manually monomorphise the code to read rows in our automatically-generated\n    /// implementation of IReadWrite. This allows rows to be initialized with new() rather than reflection\n    /// in the compiled code.\n    /// </summary>\n    /// <returns>An <c>IReadWrite&lt;T&gt;</c> for <c>T : IStructuralReadWrite</c>.</returns>\n    object GetSerializer();\n\n    /// <summary>\n    /// Read a row from the reader.\n    /// This method usually uses Activator.CreateInstance to create the resulting row;\n    /// if this is too slow, prefer using GetSerializer.\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"reader\"></param>\n    /// <returns></returns>\n    static T Read<T>(BinaryReader reader)\n        where T : IStructuralReadWrite, new()\n    {\n        // TODO: use `RuntimeHelpers.GetUninitializedObject` as an optimisation here.\n        // We tried but couldn't do this because BitCraft relies on being able\n        // to add and initialize custom fields on autogenerated classes.\n        var result = new T();\n        result.ReadFields(reader);\n        return result;\n    }\n\n    public static byte[] ToBytes<RW, T>(RW rw, T value)\n        where RW : IReadWrite<T>\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        rw.Write(writer, value);\n        return stream.ToArray();\n    }\n\n    public static byte[] ToBytes<T>(T value)\n        where T : IStructuralReadWrite\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        value.WriteFields(writer);\n        return stream.ToArray();\n    }\n}\n\n/// <summary>\n/// Interface for types that know how to serialize another type.\n/// We auto-generate an implementation of <c>IReadWrite&lt;T&gt;</c> for all\n/// types marked with <c>[SpacetimeDB.Type]</c>. For a type <c>T</c>, this implementation\n/// is accessible at <c>T.BSATN</c>. The implementation is always a zero-sized struct.\n/// </summary>\n/// <typeparam name=\"T\"></typeparam>\npublic interface IReadWrite<T>\n{\n    /// <summary>\n    /// Read a BSATN-encoded value of type T from the reader.\n    /// Throws on end-of-stream or if the stream is malformed.\n    /// Advances the reader to the first byte past the end of the encoded value.\n    /// </summary>\n    /// <param name=\"reader\"></param>\n    /// <returns></returns>\n    T Read(BinaryReader reader);\n\n    /// <summary>\n    /// Write a BSATN-encoded value of type T to the writer.\n    /// </summary>\n    void Write(BinaryWriter writer, T value);\n\n    /// <summary>\n    /// Get metadata for this type. Used in module initialization.\n    /// </summary>\n    /// <param name=\"registrar\"></param>\n    /// <returns></returns>\n    AlgebraicType GetAlgebraicType(ITypeRegistrar registrar);\n}\n\n/// <summary>\n/// Serializer for enums.\n/// </summary>\n/// <typeparam name=\"T\"></typeparam>\npublic readonly struct Enum<T> : IReadWrite<T>\n    where T : struct, Enum\n{\n    /// <summary>\n    /// Map from tag -> value, implemented as an array.\n    /// Note: the [Type] macro rejects enums with explicitly set values (see Codegen.Tests),\n    /// so this array is guaranteed to be continuous and indexed starting from 0.\n    /// </summary>\n    private static readonly T[] TagToValue = Enum.GetValues(typeof(T)).Cast<T>().ToArray();\n\n    public T Read(BinaryReader reader)\n    {\n        var tag = reader.ReadByte();\n        try\n        {\n            return TagToValue[tag];\n        }\n        catch\n        {\n            throw new ArgumentOutOfRangeException(\n                $\"Tag {tag} is out of range of enum {typeof(T).Name}\"\n            );\n        }\n    }\n\n    public void Write(BinaryWriter writer, T value)\n    {\n        // Previously this was: `if (Enum.IsDefined(typeof(T), value))`.\n        // This was quite expensive because:\n        //   1. It uses reflection\n        //   2. It allocates\n        //   3. It is called on each row when decoding\n        //\n        // However, enum values are guaranteed to be sequential and zero based.\n        // Hence we only ever need to do an upper bound check.\n        // See `SpacetimeDB.Type.ParseEnum` for the syntax analysis.\n        //\n        // Note: this actually still uses reflection and allocates.\n        // It's hard to figure out how to avoid this without custom-generating a writer for each enum type.\n        var tag = Convert.ToByte(value);\n        if (tag < TagToValue.Length)\n        {\n            writer.Write(tag);\n        }\n        else\n        {\n            throw new ArgumentOutOfRangeException(\n                $\"Value {value} is out of range for enum {typeof(T).Name}\"\n            );\n        }\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        registrar.RegisterType<T>(\n            (_) =>\n                new AlgebraicType.Sum(\n                    Enum.GetNames(typeof(T))\n                        .Select(name => new AggregateElement(name, AlgebraicType.Unit))\n                        .ToArray()\n                )\n        );\n}\n\npublic readonly struct RefOption<Inner, InnerRW> : IReadWrite<Inner?>\n    where Inner : class\n    where InnerRW : IReadWrite<Inner>, new()\n{\n    private static readonly InnerRW innerRW = new();\n\n    public Inner? Read(BinaryReader reader) => reader.ReadBoolean() ? null : innerRW.Read(reader);\n\n    public void Write(BinaryWriter writer, Inner? value)\n    {\n        writer.Write(value is null);\n        if (value is not null)\n        {\n            innerRW.Write(writer, value);\n        }\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        AlgebraicType.MakeOption(innerRW.GetAlgebraicType(registrar));\n\n    // Return a List BSATN serializer that can serialize this option as an array\n    public static List<Inner, InnerRW> GetListSerializer()\n    {\n        return new List<Inner, InnerRW>();\n    }\n}\n\n// This implementation is nearly identical to RefOption. The only difference is the constraint on T.\n// Yes, this is dumb, but apparently you can't have *really* generic `T?` because,\n// despite identical bodies, compiler will desugar it to very different\n// types based on whether the constraint makes it a reference type or a value type.\npublic readonly struct ValueOption<Inner, InnerRW> : IReadWrite<Inner?>\n    where Inner : struct\n    where InnerRW : IReadWrite<Inner>, new()\n{\n    private static readonly InnerRW innerRW = new();\n\n    public Inner? Read(BinaryReader reader) => reader.ReadBoolean() ? null : innerRW.Read(reader);\n\n    public void Write(BinaryWriter writer, Inner? value)\n    {\n        writer.Write(!value.HasValue);\n        if (value.HasValue)\n        {\n            innerRW.Write(writer, value.Value);\n        }\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        AlgebraicType.MakeOption(innerRW.GetAlgebraicType(registrar));\n\n    // Return a List BSATN serializer that can serialize this option as an array\n    public static List<Inner, InnerRW> GetListSerializer()\n    {\n        return new List<Inner, InnerRW>();\n    }\n}\n\npublic readonly struct Bool : IReadWrite<bool>\n{\n    public bool Read(BinaryReader reader) => reader.ReadBoolean();\n\n    public void Write(BinaryWriter writer, bool value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.Bool(default);\n}\n\npublic readonly struct U8 : IReadWrite<byte>\n{\n    public byte Read(BinaryReader reader) => reader.ReadByte();\n\n    public void Write(BinaryWriter writer, byte value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U8(default);\n}\n\npublic readonly struct U16 : IReadWrite<ushort>\n{\n    public ushort Read(BinaryReader reader) => reader.ReadUInt16();\n\n    public void Write(BinaryWriter writer, ushort value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U16(default);\n}\n\npublic readonly struct U32 : IReadWrite<uint>\n{\n    public uint Read(BinaryReader reader) => reader.ReadUInt32();\n\n    public void Write(BinaryWriter writer, uint value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U32(default);\n}\n\npublic readonly struct U64 : IReadWrite<ulong>\n{\n    public ulong Read(BinaryReader reader) => reader.ReadUInt64();\n\n    public void Write(BinaryWriter writer, ulong value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U64(default);\n}\n\npublic readonly struct U128Stdb : IReadWrite<SpacetimeDB.U128>\n{\n    public SpacetimeDB.U128 Read(BinaryReader reader)\n    {\n        var lower = reader.ReadUInt64();\n        var upper = reader.ReadUInt64();\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, SpacetimeDB.U128 value)\n    {\n        writer.Write(value.Lower);\n        writer.Write(value.Upper);\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U128(default);\n}\n\n#if NET7_0_OR_GREATER\npublic readonly struct U128 : IReadWrite<UInt128>\n{\n    public UInt128 Read(BinaryReader reader)\n    {\n        var lower = reader.ReadUInt64();\n        var upper = reader.ReadUInt64();\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, UInt128 value)\n    {\n        writer.Write((ulong)value);\n        writer.Write((ulong)(value >> 64));\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U128(default);\n}\n#endif\n\npublic readonly struct U256 : IReadWrite<SpacetimeDB.U256>\n{\n    public SpacetimeDB.U256 Read(BinaryReader reader)\n    {\n        var bsatn = new U128Stdb();\n        var lower = bsatn.Read(reader);\n        var upper = bsatn.Read(reader);\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, SpacetimeDB.U256 value)\n    {\n        var bsatn = new U128Stdb();\n        bsatn.Write(writer, value.Lower);\n        bsatn.Write(writer, value.Upper);\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.U256(default);\n}\n\npublic readonly struct I8 : IReadWrite<sbyte>\n{\n    public sbyte Read(BinaryReader reader) => reader.ReadSByte();\n\n    public void Write(BinaryWriter writer, sbyte value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I8(default);\n}\n\npublic readonly struct I16 : IReadWrite<short>\n{\n    public short Read(BinaryReader reader) => reader.ReadInt16();\n\n    public void Write(BinaryWriter writer, short value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I16(default);\n}\n\npublic readonly struct I32 : IReadWrite<int>\n{\n    public int Read(BinaryReader reader) => reader.ReadInt32();\n\n    public void Write(BinaryWriter writer, int value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I32(default);\n}\n\npublic readonly struct I64 : IReadWrite<long>\n{\n    public long Read(BinaryReader reader) => reader.ReadInt64();\n\n    public void Write(BinaryWriter writer, long value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I64(default);\n}\n\npublic readonly struct I128Stdb : IReadWrite<SpacetimeDB.I128>\n{\n    public SpacetimeDB.I128 Read(BinaryReader reader)\n    {\n        var lower = reader.ReadUInt64();\n        var upper = reader.ReadUInt64();\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, SpacetimeDB.I128 value)\n    {\n        writer.Write(value.Lower);\n        writer.Write(value.Upper);\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I128(default);\n}\n\n#if NET7_0_OR_GREATER\npublic readonly struct I128 : IReadWrite<Int128>\n{\n    public Int128 Read(BinaryReader reader)\n    {\n        var lower = reader.ReadUInt64();\n        var upper = reader.ReadUInt64();\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, Int128 value)\n    {\n        writer.Write((long)value);\n        writer.Write((long)(value >> 64));\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I128(default);\n}\n#endif\n\npublic readonly struct I256 : IReadWrite<SpacetimeDB.I256>\n{\n    public SpacetimeDB.I256 Read(BinaryReader reader)\n    {\n        var bsatn = new U128Stdb();\n        var lower = bsatn.Read(reader);\n        var upper = bsatn.Read(reader);\n        return new(upper, lower);\n    }\n\n    public void Write(BinaryWriter writer, SpacetimeDB.I256 value)\n    {\n        var bsatn = new U128Stdb();\n        bsatn.Write(writer, value.Lower);\n        bsatn.Write(writer, value.Upper);\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.I256(default);\n}\n\npublic readonly struct F32 : IReadWrite<float>\n{\n    public float Read(BinaryReader reader) => reader.ReadSingle();\n\n    public void Write(BinaryWriter writer, float value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.F32(default);\n}\n\npublic readonly struct F64 : IReadWrite<double>\n{\n    public double Read(BinaryReader reader) => reader.ReadDouble();\n\n    public void Write(BinaryWriter writer, double value) => writer.Write(value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.F64(default);\n}\n\nreadonly struct Enumerable<Element, ElementRW> : IReadWrite<IEnumerable<Element>>\n    where ElementRW : IReadWrite<Element>, new()\n{\n    private static readonly ElementRW elementRW = new();\n\n    public IEnumerable<Element> Read(BinaryReader reader)\n    {\n        var count = reader.ReadInt32();\n        for (var i = 0; i < count; i++)\n        {\n            yield return elementRW.Read(reader);\n        }\n    }\n\n    public void Write(BinaryWriter writer, IEnumerable<Element> value)\n    {\n        writer.Write(value.Count());\n        foreach (var element in value)\n        {\n            elementRW.Write(writer, element);\n        }\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.Array(elementRW.GetAlgebraicType(registrar));\n}\n\npublic readonly struct Array<Element, ElementRW> : IReadWrite<Element[]>\n    where ElementRW : IReadWrite<Element>, new()\n{\n    private static readonly Enumerable<Element, ElementRW> enumerable = new();\n    private static readonly ElementRW elementRW = new();\n\n    public Element[] Read(BinaryReader reader)\n    {\n        // Don't use Enumerable here: save an allocation and pre-allocate the output.\n        var count = reader.ReadInt32();\n        var result = new Element[count];\n        for (var i = 0; i < count; i++)\n        {\n            result[i] = elementRW.Read(reader);\n        }\n        return result;\n    }\n\n    public void Write(BinaryWriter writer, Element[] value) => enumerable.Write(writer, value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        enumerable.GetAlgebraicType(registrar);\n}\n\n// Special case for byte arrays that can be dealt with more efficiently.\npublic readonly struct ByteArray : IReadWrite<byte[]>\n{\n    public static readonly ByteArray Instance = new();\n\n    public byte[] Read(BinaryReader reader) => reader.ReadBytes(reader.ReadInt32());\n\n    public void Write(BinaryWriter writer, byte[] value)\n    {\n        writer.Write(value.Length);\n        writer.Write(value);\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.Array(new AlgebraicType.U8(default));\n}\n\n// String is a special case of byte array with extra checks.\npublic readonly struct String : IReadWrite<string>\n{\n    public string Read(BinaryReader reader) =>\n        Encoding.UTF8.GetString(ByteArray.Instance.Read(reader));\n\n    public void Write(BinaryWriter writer, string value)\n    {\n        if (value is null)\n        {\n            throw new ArgumentNullException(\n                nameof(value),\n                \"Cannot serialize a null string as BSATN String. To serialize a null string you must use a nullable string (string?) so it is encoded as a BSATN option.\"\n            );\n        }\n\n        ByteArray.Instance.Write(writer, Encoding.UTF8.GetBytes(value));\n    }\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        new AlgebraicType.String(default);\n}\n\npublic readonly struct List<Element, ElementRW> : IReadWrite<List<Element>>\n    where ElementRW : IReadWrite<Element>, new()\n{\n    private static readonly Enumerable<Element, ElementRW> enumerable = new();\n    private static readonly ElementRW elementRW = new();\n\n    public List<Element> Read(BinaryReader reader)\n    {\n        // Don't use Enumerable here: save an allocation and pre-allocate the output.\n        var count = reader.ReadInt32();\n        var result = new List<Element>(count);\n        for (var i = 0; i < count; i++)\n        {\n            result.Add(elementRW.Read(reader));\n        }\n        return result;\n    }\n\n    public void Write(BinaryWriter writer, List<Element> value) => enumerable.Write(writer, value);\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n        enumerable.GetAlgebraicType(registrar);\n}\n\n// This is a dummy type, mainly used by codegen as a diagnostics placeholder to\n// reduce amount of noisy compilation errors when a used type is not supported by BSATN.\npublic readonly struct Unsupported<T> : IReadWrite<T>\n{\n    private static readonly NotSupportedException Exception =\n        new($\"Type {typeof(T)} is not supported by BSATN.\");\n\n    public T Read(BinaryReader reader) => throw Exception;\n\n    public void Write(BinaryWriter writer, T value) => throw Exception;\n\n    public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => throw Exception;\n}\n\n/// <summary>\n/// Support methods for converting <c>[SpacetimeDB.Type]</c>s to strings.\n/// </summary>\npublic static class StringUtil\n{\n    /// <summary>\n    /// Convert an arbitrary object to a string:\n    /// - Printing <c>null</c> instead of empty string for null objects\n    /// - Quoting strings\n    /// - Printing list contents as <c>$\"[ {list[0].ToString()} {list[1].ToString()} {...} {list[n-1].ToString()} ]\"</c>,\n    ///     printing at most 16 elements of the list, with an ellipsis in the middle if there\n    ///     are more. (This is to prevent crashing Unity if you accidentally print a large array, say.)\n    ///\n    /// This is NOT a deep pretty-printer: it only pretty-prints the object given, relying on <c>ToString()</c>\n    /// to print sub-objects. However, objects marked with <c>[SpacetimeDB.Type]</c> use this method as part of\n    /// generated code to implement deep pretty-printing in their <c>ToString()</c> implementations.\n    /// </summary>\n    /// <param name=\"obj\"></param>\n    /// <returns></returns>\n    public static string GenericToString(object? obj)\n    {\n        if (obj == null)\n        {\n            return \"null\";\n        }\n\n        var str = obj as string;\n        if (str != null)\n        {\n            return ToStringLiteral(str);\n        }\n\n        // Casting to IList means if a user implements IList for some\n        // [SpacetimeDB.Type], it will get printed as a list.\n        // Shrug.\n        var list = obj as System.Collections.IList;\n        if (list != null)\n        {\n            return GenericListToString(list);\n        }\n\n        return obj.ToString()!;\n    }\n\n    internal static string ToStringLiteral(string input)\n    {\n        var literal = new StringBuilder(input.Length + 2);\n        literal.Append('\\\"');\n        foreach (var c in input)\n        {\n            switch (c)\n            {\n                case '\\\"':\n                    literal.Append(\"\\\\\\\"\");\n                    break;\n                case '\\\\':\n                    literal.Append(@\"\\\\\");\n                    break;\n                case '\\0':\n                    literal.Append(@\"\\0\");\n                    break;\n                case '\\a':\n                    literal.Append(@\"\\a\");\n                    break;\n                case '\\b':\n                    literal.Append(@\"\\b\");\n                    break;\n                case '\\f':\n                    literal.Append(@\"\\f\");\n                    break;\n                case '\\n':\n                    literal.Append(@\"\\n\");\n                    break;\n                case '\\r':\n                    literal.Append(@\"\\r\");\n                    break;\n                case '\\t':\n                    literal.Append(@\"\\t\");\n                    break;\n                case '\\v':\n                    literal.Append(@\"\\v\");\n                    break;\n                default:\n                    if (c is >= (char)0x20 and <= (char)0x7e)\n                    {\n                        // ASCII printable character\n                        literal.Append(c);\n                    }\n                    else if (\n                        Char.GetUnicodeCategory(c) == System.Globalization.UnicodeCategory.Control\n                    )\n                    {\n                        // As UTF16 escaped character\n                        literal.Append(@\"\\u\");\n                        literal.Append(((int)c).ToString(\"x4\"));\n                    }\n                    else\n                    {\n                        // Something else\n                        literal.Append(c);\n                    }\n                    break;\n            }\n        }\n        literal.Append('\"');\n        return literal.ToString();\n    }\n\n    internal static string GenericListToString(System.Collections.IList list)\n    {\n        StringBuilder result = new();\n        result.Append(\"[ \");\n\n        // avoid debug-dumping huge lists.\n        if (list.Count <= 16)\n        {\n            for (var i = 0; i < list.Count; i++)\n            {\n                result.Append(GenericToString(list[i]));\n                if (i < list.Count - 1)\n                {\n                    result.Append(\", \");\n                }\n            }\n        }\n        else\n        {\n            for (var i = 0; i < 8; i++)\n            {\n                result.Append(GenericToString(list[i]));\n                result.Append(\", \");\n            }\n            result.Append(\"..., \");\n            for (var i = list.Count - 8; i < list.Count; i++)\n            {\n                result.Append(GenericToString(list[i]));\n                if (i < list.Count - 1)\n                {\n                    result.Append(\", \");\n                }\n            }\n        }\n\n        if (list.Count > 0)\n        {\n            result.Append(' ');\n        }\n        result.Append(']');\n\n        return result.ToString();\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/U128.cs",
    "content": "// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nnamespace SpacetimeDB;\n\nusing System.Buffers.Binary;\nusing System.Numerics;\nusing System.Runtime.InteropServices;\n\n/// <summary>Represents a 128-bit unsigned integer.</summary>\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct U128 : IEquatable<U128>, IComparable, IComparable<U128>\n{\n    internal const int Size = 16;\n\n#if BIGENDIAN\n    private readonly ulong _upper;\n    private readonly ulong _lower;\n#else\n    private readonly ulong _lower;\n    private readonly ulong _upper;\n#endif\n\n    /// <summary>Initializes a new instance of the <see cref=\"U128\" /> struct.</summary>\n    /// <param name=\"upper\">The upper 64-bits of the 128-bit value.</param>\n    /// <param name=\"lower\">The lower 64-bits of the 128-bit value.</param>\n    public U128(ulong upper, ulong lower)\n    {\n        _upper = upper;\n        _lower = lower;\n    }\n\n    internal ulong Lower => _lower;\n\n    internal ulong Upper => _upper;\n\n    /// Returns a <see cref=\"U128\" /> from a big-endian byte array.\n    public static U128 FromBytesBigEndian(ReadOnlySpan<byte> bytes)\n    {\n        if (bytes.Length != Size)\n        {\n            throw new ArgumentException(\n                $\"Byte array must be exactly {Size} bytes long.\",\n                nameof(bytes)\n            );\n        }\n\n        var upper = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(0, 8));\n        var lower = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8));\n\n        return new U128(upper, lower);\n    }\n\n    public byte[] ToBytesBigEndian()\n    {\n        var bytes = new byte[Size];\n\n        BinaryPrimitives.WriteUInt64BigEndian(bytes.AsSpan(0, 8), _upper);\n        BinaryPrimitives.WriteUInt64BigEndian(bytes.AsSpan(8, 8), _lower);\n\n        return bytes;\n    }\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is U128 other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a U128\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(U128 value)\n    {\n        if (this < value)\n        {\n            return -1;\n        }\n        else if (this > value)\n        {\n            return 1;\n        }\n        else\n        {\n            return 0;\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)\" />\n    public static bool operator <(U128 left, U128 right)\n    {\n        return (left._upper < right._upper)\n            || (left._upper == right._upper) && (left._lower < right._lower);\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)\" />\n    public static bool operator >(U128 left, U128 right)\n    {\n        return (left._upper > right._upper)\n            || (left._upper == right._upper) && (left._lower > right._lower);\n    }\n\n    private BigInteger AsBigInt() =>\n        new(\n            MemoryMarshal.AsBytes(stackalloc[] { this }),\n            isUnsigned: false,\n            isBigEndian: !BitConverter.IsLittleEndian\n        );\n\n    /// <inheritdoc cref=\"object.ToString()\" />\n    public override string ToString() => AsBigInt().ToString();\n\n    public static U128 FromGuid(Guid guid)\n    {\n        Span<byte> bytes = stackalloc byte[16];\n        guid.TryWriteBytes(bytes);\n        return FromBytesBigEndian(bytes);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/U256.cs",
    "content": "namespace SpacetimeDB;\n\nusing System;\nusing System.Numerics;\nusing System.Runtime.InteropServices;\n\n/// <summary>Represents a 128-bit unsigned integer.</summary>\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct U256 : IEquatable<U256>, IComparable, IComparable<U256>\n{\n    internal const int Size = 32;\n\n#if BIGENDIAN\n    private readonly U128 _upper;\n    private readonly U128 _lower;\n#else\n    private readonly U128 _lower;\n    private readonly U128 _upper;\n#endif\n\n    /// <summary>Initializes a new instance of the <see cref=\"U256\" /> struct.</summary>\n    /// <param name=\"upper\">The upper 128-bits of the 256-bit value.</param>\n    /// <param name=\"lower\">The lower 128-bits of the 256-bit value.</param>\n    public U256(U128 upper, U128 lower)\n    {\n        _upper = upper;\n        _lower = lower;\n    }\n\n    internal U128 Lower => _lower;\n\n    internal U128 Upper => _upper;\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is U256 other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a U256\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(U256 value)\n    {\n        if (this < value)\n        {\n            return -1;\n        }\n        else if (this > value)\n        {\n            return 1;\n        }\n        else\n        {\n            return 0;\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)\" />\n    public static bool operator <(U256 left, U256 right)\n    {\n        return (left._upper < right._upper)\n            || (left._upper == right._upper) && (left._lower < right._lower);\n    }\n\n    /// <inheritdoc cref=\"IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)\" />\n    public static bool operator >(U256 left, U256 right)\n    {\n        return (left._upper > right._upper)\n            || (left._upper == right._upper) && (left._lower > right._lower);\n    }\n\n    private BigInteger AsBigInt() =>\n        new(\n            MemoryMarshal.AsBytes(stackalloc[] { this }),\n            isUnsigned: false,\n            isBigEndian: !BitConverter.IsLittleEndian\n        );\n\n    /// <inheritdoc cref=\"object.ToString()\" />\n    public override string ToString() => AsBigInt().ToString();\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN/Uuid.cs",
    "content": "namespace SpacetimeDB;\n\nusing System.Buffers.Binary;\nusing System.Runtime.InteropServices;\nusing SpacetimeDB.BSATN;\n\n/// <summary>\n/// A universally unique identifier (UUID).\n///\n/// Generate `NIL`, `MAX`, random (`v4`), and time-ordered (`v7`) UUIDs.\n/// </summary>\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct Uuid : IEquatable<Uuid>, IComparable, IComparable<Uuid>\n{\n    private readonly U128 value;\n\n    public Uuid(U128 val) => value = val;\n\n    /// <summary>\n    /// The nil <see cref=\"Uuid\"/> (all bits set to zero).\n    /// </summary>\n    public static readonly Uuid NIL = new(new U128());\n\n    /// <summary>\n    /// The max <see cref=\"Uuid\"/> (all bits set to one).\n    /// </summary>\n    public static readonly Uuid MAX = new(new U128(ulong.MaxValue, ulong.MaxValue));\n\n    /// <summary>\n    /// Create a <see cref=\"Uuid\"/> `v4` from explicit random bytes.\n    /// </summary>\n    /// <remarks>\n    /// This method assumes the provided bytes are already sufficiently random;\n    /// it will only set the appropriate bits for the UUID version and variant.\n    /// </remarks>\n    /// <param name=\"randomBytes\">\n    /// 16 random bytes used for entropy.\n    /// </param>\n    /// <example>\n    /// <code>\n    /// var randomBytes = new byte[16];\n    /// RandomNumberGenerator.Fill(randomBytes);\n    /// var uuid = Uuid.FromRandomBytesV4(randomBytes);\n    /// Console.WriteLine(uuid);\n    /// // Output: 166a036f-90f3-714a-9731-c8814b70e326\n    /// </code>\n    /// </example>\n    /// <exception cref=\"ArgumentException\">\n    /// Thrown if <paramref name=\"randomBytes\"/> is not exactly 16 bytes long.\n    /// </exception>\n    public static Uuid FromRandomBytesV4(ReadOnlySpan<byte> randomBytes)\n    {\n        if (randomBytes.Length != 16)\n        {\n            throw new ArgumentException(\"Must be 16 bytes\", nameof(randomBytes));\n        }\n\n        Span<byte> bytes = stackalloc byte[16];\n        randomBytes.CopyTo(bytes);\n        bytes[6] = (byte)((bytes[6] & 0x0F) | 0x40); // version 4\n        bytes[8] = (byte)((bytes[8] & 0x3F) | 0x80); // variant RFC 4122\n\n        return new(U128.FromBytesBigEndian(bytes));\n    }\n\n    /// <summary>\n    /// Generate a <see cref=\"Uuid\"/> `v7` using a monotonic counter from 0 to 2^31-1,\n    /// a Unix timestamp in milliseconds, and 4 random bytes.\n    ///\n    /// <example>\n    /// <code>\n    /// int counter = 1;\n    /// var now = new Timestamp(1_686_000_000_000);\n    /// byte[] random = { 0, 0, 0, 0 };\n    ///\n    /// Guid uuid = UuidV7.FromCounterV7(ref counter, now, random);\n    ///\n    /// Console.WriteLine(uuid);\n    /// // \"0000647e-5180-7000-8000-000200000000\"\n    /// </code>\n    /// </example>\n    /// </summary>\n    /// <remarks>\n    ///\n    /// The <see cref=\"Uuid\"/> `v7` is structured as follows:\n    ///\n    /// <code>\n    /// ┌───────────────────────────────────────────────┬───────────────────┐\n    /// | B0  | B1  | B2  | B3  | B4  | B5              |         B6        |\n    /// ├───────────────────────────────────────────────┼───────────────────┤\n    /// |                 unix_ts_ms                    |      version 7    |\n    /// └───────────────────────────────────────────────┴───────────────────┘\n    /// ┌──────────────┬─────────┬──────────────────┬───────────────────────┐\n    /// | B7           | B8      | B9  | B10 | B11  | B12 | B13 | B14 | B15 |\n    /// ├──────────────┼─────────┼──────────────────┼───────────────────────┤\n    /// | counter_high | variant |    counter_low   |        random         |\n    /// └──────────────┴─────────┴──────────────────┴───────────────────────┘\n    /// </code>\n    /// </remarks>\n    /// <param name=\"counter\">\n    /// Monotonic counter value (31 bits). Must be non-negative.\n    /// The counter is incremented and wraps on overflow.\n    /// </param>\n    /// <param name=\"now\">\n    /// Current time.\n    /// </param>\n    /// <param name=\"randomBytes\">\n    /// 4 random bytes used for entropy.\n    /// </param>\n    /// <exception cref=\"InvalidOperationException\">\n    /// Thrown if the counter value is negative.\n    /// </exception>\n    /// <exception cref=\"ArgumentException\">\n    /// Thrown if <paramref name=\"randomBytes\"/> is not exactly 4 bytes long, or <paramref name=\"now\"/> is  before unix epoch.\n    /// </exception>\n    /// <returns>\n    /// A <see cref=\"Uuid\"/> `v7`.\n    /// </returns>\n    public static Uuid FromCounterV7(\n        ref int counter,\n        Timestamp now,\n        ReadOnlySpan<byte> randomBytes // must be length 4\n    )\n    {\n        if (randomBytes.Length != 4)\n        {\n            throw new ArgumentException(\"randomBytes must be exactly 4 bytes\", nameof(randomBytes));\n        }\n\n        if (counter < 0)\n        {\n            throw new InvalidOperationException(\"uuid counter must be non-negative\");\n        }\n\n        if (now.MicrosecondsSinceUnixEpoch < 0)\n        {\n            throw new ArgumentException(\"timestamp before unix epoch\", nameof(now));\n        }\n        var unixTsMs = now.MicrosecondsSinceUnixEpoch / 1_000;\n\n        // monotonic 31-bit\n        var counterVal = counter;\n        counter = (counter + 1) & 0x7FFF_FFFF;\n\n        Span<byte> bytes = stackalloc byte[16];\n\n        // unix_ts_ms (48 bits, big-endian)\n        var ts = unixTsMs & 0x0000_FFFF_FFFF_FFFFL;\n        bytes[0] = (byte)(ts >> 40);\n        bytes[1] = (byte)(ts >> 32);\n        bytes[2] = (byte)(ts >> 24);\n        bytes[3] = (byte)(ts >> 16);\n        bytes[4] = (byte)(ts >> 8);\n        bytes[5] = (byte)ts;\n\n        // version (7)\n        bytes[6] = 0x70;\n\n        // counter bits\n        bytes[7] = (byte)((counterVal >> 23) & 0xFF);\n        bytes[9] = (byte)((counterVal >> 15) & 0xFF);\n        bytes[10] = (byte)((counterVal >> 7) & 0xFF);\n        bytes[11] = (byte)((counterVal & 0x7F) << 1);\n\n        // variant (RFC 4122)\n        bytes[8] = 0x80;\n\n        // random bytes\n        bytes[12] |= (byte)(randomBytes[0] & 0x7F);\n        bytes[13] = randomBytes[1];\n        bytes[14] = randomBytes[2];\n        bytes[15] = randomBytes[3];\n\n        return new(U128.FromBytesBigEndian(bytes));\n    }\n\n    public enum UuidVersion\n    {\n        Nil,\n        V4,\n        V7,\n        Max,\n    }\n\n    /// <summary>\n    /// Extract the 31-bit monotonic counter from a <see cref=\"Uuid\"/> `v7`.\n    /// Intended for testing.\n    /// </summary>\n    public int GetCounter()\n    {\n        var bytes = value.ToBytesBigEndian();\n\n        uint high = bytes[7];\n        uint mid1 = bytes[9];\n        uint mid2 = bytes[10];\n        var low = (uint)(bytes[11] >> 1);\n\n        // Reconstruct 31-bit counter\n        return (int)((high << 23) | (mid1 << 15) | (mid2 << 7) | low);\n    }\n\n    /// <summary>\n    /// Returns the <see cref=\"UuidVersion\"/> of this <see cref=\"Uuid\"/>.\n    ///\n    /// Throws <see cref=\"InvalidOperationException\"/> if the <see cref=\"Uuid\"/> version is unknown.\n    /// </summary>\n    public UuidVersion GetVersion()\n    {\n        var bytes = value.ToBytesBigEndian();\n        var version = (bytes[6] >> 4) & 0x0F;\n\n        return version switch\n        {\n            4 => UuidVersion.V4,\n            7 => UuidVersion.V7,\n            _ => this == Uuid.NIL ? UuidVersion.Nil\n            : this == Uuid.MAX ? UuidVersion.Max\n            : throw new InvalidOperationException(\"Unknown UUID version\"),\n        };\n    }\n\n    private static void GuidToBigEndianBytes(ReadOnlySpan<byte> guidBytes, Span<byte> be)\n    {\n        // Guid’s weird internal layout (mixed-endian)\n\n        // time_low (4 bytes) — little-endian → reverse\n        be[0] = guidBytes[3];\n        be[1] = guidBytes[2];\n        be[2] = guidBytes[1];\n        be[3] = guidBytes[0];\n\n        // time_mid (2 bytes) — little-endian\n        be[4] = guidBytes[5];\n        be[5] = guidBytes[4];\n\n        // time_hi_and_version (2 bytes) — little-endian\n        be[6] = guidBytes[7];\n        be[7] = guidBytes[6];\n\n        // last 8 bytes already big-endian\n        guidBytes[8..].CopyTo(be[8..]);\n    }\n\n    public static Uuid FromGuid(Guid guid)\n    {\n        // Guid is `mixed-endian`, so we need to fixup\n        Span<byte> gb = stackalloc byte[16];\n        guid.TryWriteBytes(gb);\n\n        Span<byte> bytes = stackalloc byte[16];\n        GuidToBigEndianBytes(gb, bytes);\n\n        return new Uuid(U128.FromBytesBigEndian(bytes));\n    }\n\n    /// <summary>\n    /// Converts this instance to a <see cref=\"Guid\"/>, in `big-endian`.\n    /// </summary>\n    public Guid ToGuid()\n    {\n        // Guid is `mixed-endian`, so we need to fixup\n        Span<byte> be = stackalloc byte[16];\n        BinaryPrimitives.WriteUInt64BigEndian(be[..8], value.Upper);\n        BinaryPrimitives.WriteUInt64BigEndian(be[8..], value.Lower);\n\n        Span<byte> gb = stackalloc byte[16];\n        GuidToBigEndianBytes(be, gb);\n\n        return new Guid(gb);\n    }\n\n    /// <summary>\n    /// Parses a UUID from its string representation.\n    /// </summary>\n    /// <example>\n    /// <code>\n    /// var s = \"01888d6e-5c00-7000-8000-000000000000\";\n    /// var uuid = Uuid.Parse(s);\n    /// Console.WriteLine(uuid.ToString() == s); // True\n    /// </code>\n    /// </example>\n    public static Uuid Parse(string s)\n    {\n        var guid = new Guid(s);\n\n        return Uuid.FromGuid(guid);\n    }\n\n    public override readonly string ToString()\n    {\n        return ToGuid().ToString();\n    }\n\n    public readonly int CompareTo(Uuid other) => value.CompareTo(other.value);\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is Uuid other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a Uuid\", nameof(value));\n        }\n    }\n\n    public static bool operator <(Uuid l, Uuid r) => l.CompareTo(r) < 0;\n\n    public static bool operator >(Uuid l, Uuid r) => l.CompareTo(r) > 0;\n\n    public readonly partial struct BSATN : IReadWrite<Uuid>\n    {\n        public Uuid Read(BinaryReader reader) => new(new SpacetimeDB.BSATN.U128Stdb().Read(reader));\n\n        public void Write(BinaryWriter writer, Uuid value) =>\n            new SpacetimeDB.BSATN.U128Stdb().Write(writer, value.value);\n\n        // --- / auto-generated ---\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product(\n                [\n                    // Using this specific name here is important.\n                    new(\"__uuid__\", new AlgebraicType.U128(default)),\n                ]\n            );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <AssemblyName>SpacetimeDB.BSATN.Runtime</AssemblyName>\n    <Version>2.0.4</Version>\n    <Title>SpacetimeDB BSATN Runtime</Title>\n    <Description>The SpacetimeDB BSATN Runtime implements APIs for BSATN serialization/deserialization in C#.</Description>\n    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <!-- Note: the binary produced by this package is used in Unity too, which is limited to .NET Standard 2.1. -->\n    <TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>\n    <RootNamespace>SpacetimeDB</RootNamespace>\n    <!-- You can enable this when debugging codegen problems. Outputs in obj/debug/[version]/generated. -->\n    <!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> -->\n  </PropertyGroup>\n\n  <ItemGroup>\n    <!-- We want to build BSATN.Codegen both to include it in our NuGet package but also we want it to transform [SpacetimeDB.Type] usages in BSATN.Runtime code itself. -->\n    <ProjectReference\n      Include=\"../BSATN.Codegen/BSATN.Codegen.csproj\"\n      OutputItemType=\"Analyzer\"\n      ReferenceOutputAssembly=\"false\"\n    />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Include=\"../Runtime/README.md\" Pack=\"true\" PackagePath=\"\" />\n    <!-- We want all users who depends on BSATN.Runtime to automatically get the Roslyn codegen component as well. -->\n    <None\n      Include=\"../BSATN.Codegen/bin/$(Configuration)/netstandard2.0/SpacetimeDB.BSATN.Codegen.dll\"\n      Pack=\"true\"\n      PackagePath=\"analyzers/dotnet/cs\"\n    />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- dev-dependency to add internal C# classes and attributes not included in .NET Standard -->\n    <PackageReference Include=\"PolySharp\" Version=\"1.14.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/Builtins.cs",
    "content": "namespace SpacetimeDB;\n\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing SpacetimeDB.BSATN;\n\ninternal static class Util\n{\n    public static Span<byte> AsBytes<T>(ref T val)\n        where T : struct => MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref val, 1));\n\n    /// <summary>\n    /// Convert this object to a BIG-ENDIAN hex string.\n    ///\n    /// Big endian is almost always the correct convention here. It puts the most significant bytes\n    /// of the number at the lowest indexes of the resulting string; assuming the string is printed\n    /// with low indexes to the left, this will result in the correct hex number being displayed.\n    ///\n    /// (This might be wrong if the string is printed after, say, a unicode right-to-left marker.\n    /// But, well, what can you do.)\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"val\"></param>\n    /// <returns></returns>\n    public static string ToHexBigEndian<T>(T val)\n        where T : struct\n    {\n        var bytes = AsBytes(ref val);\n        // If host is little-endian, reverse the bytes.\n        // Note that this reverses our stack copy of `val`, not the original value, and doesn't require heap `byte[]` allocation.\n        if (BitConverter.IsLittleEndian)\n        {\n            bytes.Reverse();\n        }\n#if NET5_0_OR_GREATER\n        return Convert.ToHexString(bytes);\n#else\n        // Similar to `Convert.ToHexString`, but that method is not available in .NET Standard\n        // which we need to target for Unity support.\n        return BitConverter.ToString(bytes.ToArray()).Replace(\"-\", \"\");\n#endif\n    }\n\n    /// <summary>\n    /// Convert the passed byte array to a value of type T, optionally reversing it before performing the conversion.\n    /// If the input is not reversed, it is treated as having the native endianness of the host system.\n    /// (The endianness of the host system can be checked via System.BitConverter.IsLittleEndian.)\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"source\"></param>\n    /// <param name=\"littleEndian\"></param>\n    /// <returns></returns>\n    public static T Read<T>(ReadOnlySpan<byte> source, bool littleEndian)\n        where T : struct\n    {\n        var expectedSize = Marshal.SizeOf<T>();\n        if (source.Length != expectedSize)\n        {\n            throw new ArgumentException(\n                $\"Error while reading {typeof(T).FullName}: expected source span to be {expectedSize} bytes long, but was {source.Length} bytes.\"\n            );\n        }\n\n        var result = MemoryMarshal.Read<T>(source);\n\n        if (littleEndian != BitConverter.IsLittleEndian)\n        {\n            AsBytes(ref result).Reverse();\n        }\n\n        return result;\n    }\n\n    /// <summary>\n    /// Convert a hex string to a byte array.\n    /// </summary>\n    /// <param name=\"hex\"></param>\n    /// <returns></returns>\n    public static byte[] StringToByteArray(string hex)\n    {\n#if NET5_0_OR_GREATER\n        return Convert.FromHexString(hex);\n#else\n        // Manual implementation for .NET Standard compatibility.\n        Debug.Assert(\n            hex.Length % 2 == 0,\n            $\"Expected input string (\\\"{hex}\\\") to be of even length\"\n        );\n\n        var NumberChars = hex.Length;\n        var bytes = new byte[NumberChars / 2];\n        for (var i = 0; i < NumberChars; i += 2)\n        {\n            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);\n        }\n        return bytes;\n#endif\n    }\n\n    // Similarly, we need some constants that are not available in .NET Standard.\n    public const long TicksPerMicrosecond = 10;\n    public const long MicrosecondsPerSecond = 1_000_000;\n}\n\n// The following types are \"special\" types: they has a special (Ref-less) AlgebraicType representations.\n// See `spacetimedb-sats::AlgebraicType::is_valid_for_client_type_[use|generate]` for more information.\n// We don't use [Type] here; instead we manually implement the serialization stuff that would be generated by\n// [Type] so that we can override GetAlgebraicType to return types in a special, Ref-less form.\npublic readonly partial struct Unit\n{\n    public readonly struct BSATN : IReadWrite<Unit>\n    {\n        public Unit Read(BinaryReader reader) => default;\n\n        public void Write(BinaryWriter writer, Unit value) { }\n\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product([]);\n    }\n}\n\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct ConnectionId\n    : IEquatable<ConnectionId>,\n        IComparable,\n        IComparable<ConnectionId>\n{\n    private readonly U128 value;\n\n    internal ConnectionId(U128 v) => value = v;\n\n    /// <summary>\n    /// Create a ConnectionId from a LITTLE-ENDIAN byte array.\n    ///\n    /// If you are parsing a ConnectionId from a string, you probably want FromHexString instead,\n    /// or, failing that, FromBigEndian.\n    ///\n    /// Returns null if the resulting ConnectionId is the default.\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    public static ConnectionId? From(ReadOnlySpan<byte> bytes)\n    {\n        var id = Util.Read<ConnectionId>(bytes, littleEndian: true);\n        return id == default ? null : id;\n    }\n\n    /// <summary>\n    /// Create a ConnectionId from a BIG-ENDIAN byte array.\n    ///\n    /// This method is the correct choice if you have converted the bytes of a hexadecimal-formatted ConnectionId\n    /// to a byte array in the following way:\n    ///\n    /// \"0xb0b1b2...\"\n    /// ->\n    /// [0xb0, 0xb1, 0xb2, ...]\n    ///\n    /// Returns null if the resulting ConnectionId is the default.\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    public static ConnectionId? FromBigEndian(ReadOnlySpan<byte> bytes)\n    {\n        var id = Util.Read<ConnectionId>(bytes, littleEndian: false);\n        return id == default ? null : id;\n    }\n\n    /// <summary>\n    /// Create a ConnectionId from a hex string.\n    /// </summary>\n    /// <param name=\"hex\"></param>\n    /// <returns></returns>\n    public static ConnectionId? FromHexString(string hex)\n    {\n        if (hex.Length != 32)\n        {\n            throw new ArgumentException(\n                $\"Expected ConnectionId hex string to be 32 characters long, but was {hex.Length}.\",\n                nameof(hex)\n            );\n        }\n\n        return FromBigEndian(Util.StringToByteArray(hex));\n    }\n\n    public static ConnectionId Random()\n    {\n        var random = new Random();\n        var id = new ConnectionId();\n        random.NextBytes(Util.AsBytes(ref id));\n        return id;\n    }\n\n    // --- auto-generated ---\n    public readonly struct BSATN : IReadWrite<ConnectionId>\n    {\n        public ConnectionId Read(BinaryReader reader) =>\n            new(new SpacetimeDB.BSATN.U128Stdb().Read(reader));\n\n        public void Write(BinaryWriter writer, ConnectionId value) =>\n            new SpacetimeDB.BSATN.U128Stdb().Write(writer, value.value);\n\n        // --- / auto-generated ---\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product(\n                [\n                    // Using this specific name here is important.\n                    new(\"__connection_id__\", new AlgebraicType.U128(default)),\n                ]\n            );\n        // --- / customized ---\n    }\n\n    public override string ToString() => Util.ToHexBigEndian(value);\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is ConnectionId other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a ConnectionId\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(ConnectionId connectionId) => this.value.CompareTo(connectionId.value);\n}\n\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct Identity : IEquatable<Identity>, IComparable, IComparable<Identity>\n{\n    private readonly U256 value;\n\n    internal Identity(U256 val) => value = val;\n\n    /// <summary>\n    /// Create an Identity from a LITTLE-ENDIAN byte array.\n    ///\n    /// If you are parsing an Identity from a string, you probably want FromHexString instead,\n    /// or, failing that, FromBigEndian.\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    public Identity(ReadOnlySpan<byte> bytes) => this = From(bytes);\n\n    /// <summary>\n    /// Create an Identity from a LITTLE-ENDIAN byte array.\n    ///\n    /// If you are parsing an Identity from a string, you probably want FromHexString instead,\n    /// or, failing that, FromBigEndian.\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    public static Identity From(ReadOnlySpan<byte> bytes) =>\n        Util.Read<Identity>(bytes, littleEndian: true);\n\n    /// <summary>\n    /// Create an Identity from a BIG-ENDIAN byte array.\n    ///\n    /// This method is the correct choice if you have converted the bytes of a hexadecimal-formatted `Identity`\n    /// to a byte array in the following way:\n    ///\n    /// \"0xb0b1b2...\"\n    /// ->\n    /// [0xb0, 0xb1, 0xb2, ...]\n    /// </summary>\n    /// <param name=\"bytes\"></param>\n    public static Identity FromBigEndian(ReadOnlySpan<byte> bytes) =>\n        Util.Read<Identity>(bytes, littleEndian: false);\n\n    /// <summary>\n    /// Create an Identity from a hex string.\n    /// </summary>\n    /// <param name=\"hex\"></param>\n    /// <returns></returns>\n    public static Identity FromHexString(string hex)\n    {\n        if (hex.Length != 64)\n        {\n            throw new ArgumentException(\n                $\"Expected Identity hex string to be 64 characters long, but was {hex.Length}.\",\n                nameof(hex)\n            );\n        }\n\n        return FromBigEndian(Util.StringToByteArray(hex));\n    }\n\n    // --- auto-generated ---\n    public readonly struct BSATN : IReadWrite<Identity>\n    {\n        public Identity Read(BinaryReader reader) => new(new SpacetimeDB.BSATN.U256().Read(reader));\n\n        public void Write(BinaryWriter writer, Identity value) =>\n            new SpacetimeDB.BSATN.U256().Write(writer, value.value);\n\n        // --- / auto-generated ---\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product(\n                [\n                    // Using this specific name here is important.\n                    new(\"__identity__\", new AlgebraicType.U256(default)),\n                ]\n            );\n        // --- / customized ---\n    }\n\n    // This must be explicitly implemented, otherwise record will generate a new implementation.\n    public override string ToString() => Util.ToHexBigEndian(value);\n\n    /// <inheritdoc cref=\"IComparable.CompareTo(object)\" />\n    public int CompareTo(object? value)\n    {\n        if (value is Identity other)\n        {\n            return CompareTo(other);\n        }\n        else if (value is null)\n        {\n            return 1;\n        }\n        else\n        {\n            throw new ArgumentException(\"Argument must be a Identity\", nameof(value));\n        }\n    }\n\n    /// <inheritdoc cref=\"IComparable{T}.CompareTo(T)\" />\n    public int CompareTo(Identity identity) => this.value.CompareTo(identity.value);\n}\n\n/// <summary>\n/// A timestamp that represents a unique moment in time (in the Earth's reference frame).\n///\n/// This type may be converted to/from a DateTimeOffset, but the conversion can lose precision.\n/// This type has less precision than DateTimeOffset (units of microseconds rather than units of 100ns).\n/// </summary>\n[StructLayout(LayoutKind.Sequential)] // we should be able to use it in FFI\npublic record struct Timestamp(long MicrosecondsSinceUnixEpoch)\n    : IStructuralReadWrite,\n        IComparable<Timestamp>\n{\n    public static implicit operator DateTimeOffset(Timestamp t) =>\n        DateTimeOffset.UnixEpoch.AddTicks(t.MicrosecondsSinceUnixEpoch * Util.TicksPerMicrosecond);\n\n    public static implicit operator Timestamp(DateTimeOffset offset) =>\n        new Timestamp(offset.Subtract(DateTimeOffset.UnixEpoch).Ticks / Util.TicksPerMicrosecond);\n\n    // For backwards-compatibility.\n    public readonly DateTimeOffset ToStd() => this;\n\n    // Should be consistent with Rust implementation of Display.\n    public override readonly string ToString()\n    {\n        var date = ToStd();\n\n        return date.ToString(\"yyyy-MM-dd'T'HH:mm:ss.ffffffK\");\n    }\n\n    public static readonly Timestamp UNIX_EPOCH = new(0);\n\n    public static Timestamp FromTimeDurationSinceUnixEpoch(TimeDuration timeDuration) =>\n        new Timestamp(timeDuration.Microseconds);\n\n    public readonly TimeDuration ToTimeDurationSinceUnixEpoch() => TimeDurationSince(UNIX_EPOCH);\n\n    public static Timestamp FromTimeSpanSinceUnixEpoch(TimeSpan timeSpan) =>\n        FromTimeDurationSinceUnixEpoch((TimeDuration)timeSpan);\n\n    public readonly TimeSpan ToTimeSpanSinceUnixEpoch() => (TimeSpan)ToTimeDurationSinceUnixEpoch();\n\n    public readonly TimeDuration TimeDurationSince(Timestamp earlier) =>\n        new TimeDuration(checked(MicrosecondsSinceUnixEpoch - earlier.MicrosecondsSinceUnixEpoch));\n\n    public static Timestamp operator +(Timestamp point, TimeDuration interval) =>\n        new Timestamp(checked(point.MicrosecondsSinceUnixEpoch + interval.Microseconds));\n\n    public static Timestamp operator -(Timestamp point, TimeDuration interval) =>\n        new Timestamp(checked(point.MicrosecondsSinceUnixEpoch - interval.Microseconds));\n\n    public readonly int CompareTo(Timestamp that)\n    {\n        return this.MicrosecondsSinceUnixEpoch.CompareTo(that.MicrosecondsSinceUnixEpoch);\n    }\n\n    public static bool operator <(Timestamp l, Timestamp r)\n    {\n        return l.CompareTo(r) == -1;\n    }\n\n    public static bool operator >(Timestamp l, Timestamp r)\n    {\n        return l.CompareTo(r) == 1;\n    }\n\n    // --- auto-generated ---\n\n    public void ReadFields(BinaryReader reader)\n    {\n        MicrosecondsSinceUnixEpoch = BSATN.MicrosecondsSinceUnixEpoch.Read(reader);\n    }\n\n    public readonly void WriteFields(BinaryWriter writer)\n    {\n        BSATN.MicrosecondsSinceUnixEpoch.Write(writer, MicrosecondsSinceUnixEpoch);\n    }\n\n    readonly object IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public readonly partial struct BSATN : IReadWrite<Timestamp>\n    {\n        internal static readonly I64 MicrosecondsSinceUnixEpoch = new();\n\n        public Timestamp Read(BinaryReader reader) => IStructuralReadWrite.Read<Timestamp>(reader);\n\n        public void Write(BinaryWriter writer, Timestamp value)\n        {\n            value.WriteFields(writer);\n        }\n\n        // --- / auto-generated ---\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product(\n                // Using this specific name here is important.\n                [new(\"__timestamp_micros_since_unix_epoch__\", new AlgebraicType.I64(default))]\n            );\n        // --- / customized ---\n    }\n}\n\n/// <summary>\n/// A duration that represents an interval between two events (in a particular reference frame).\n///\n/// This type may be converted to/from a TimeSpan, but the conversion can lose precision.\n/// This type has less precision than TimeSpan (units of microseconds rather than units of 100ns).\n/// </summary>\n[StructLayout(LayoutKind.Sequential)]\npublic record struct TimeDuration(long Microseconds) : IStructuralReadWrite\n{\n    public static readonly TimeDuration ZERO = new(0);\n\n    /// <summary>\n    /// Returns a <see cref=\"TimeDuration\"/> that represents a specified number of <paramref name=\"milliseconds\"/>, accurate to the nearest microsecond.\n    /// </summary>\n    public static TimeDuration FromMilliseconds(double milliseconds) =>\n        new((long)(milliseconds * 1000L));\n\n    /// <summary>\n    /// Returns a <see cref=\"TimeDuration\"/> that represents a specified number of <paramref name=\"seconds\"/>, accurate to the nearest microsecond.\n    /// </summary>\n    public static TimeDuration FromSeconds(double seconds) =>\n        new((long)(seconds * Util.MicrosecondsPerSecond));\n\n    /// <summary>\n    /// Returns a <see cref=\"TimeDuration\"/> that represents a specified number of 60-second <paramref name=\"minutes\"/>.\n    /// </summary>\n    public static TimeDuration FromMinutes(double minutes) => FromSeconds(minutes * 60);\n\n    /// <summary>\n    /// Returns a <see cref=\"TimeDuration\"/> that represents a specified number of 60-minute <paramref name=\"hours\"/>.\n    /// </summary>\n    public static TimeDuration FromHours(double hours) => FromMinutes(hours * 60);\n\n    /// <summary>\n    /// Returns a <see cref=\"TimeDuration\"/> that represents a specified number of 24-hour <paramref name=\"days\"/>.\n    /// </summary>\n    public static TimeDuration FromDays(double days) => FromHours(days * 24);\n\n    public static implicit operator TimeSpan(TimeDuration d) =>\n        new(d.Microseconds * Util.TicksPerMicrosecond);\n\n    public static implicit operator TimeDuration(TimeSpan timeSpan) =>\n        new(timeSpan.Ticks / Util.TicksPerMicrosecond);\n\n    public static TimeDuration operator +(TimeDuration lhs, TimeDuration rhs) =>\n        new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds));\n\n    public static TimeDuration operator -(TimeDuration lhs, TimeDuration rhs) =>\n        new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds));\n\n    // For backwards-compatibility.\n    public readonly TimeSpan ToStd() => this;\n\n    // Should be consistent with Rust implementation of Display.\n    public override readonly string ToString()\n    {\n        var sign = Microseconds < 0 ? \"-\" : \"+\";\n        var pos = Math.Abs(Microseconds);\n        var secs = pos / Util.MicrosecondsPerSecond;\n        var microsRemaining = pos % Util.MicrosecondsPerSecond;\n        return $\"{sign}{secs}.{microsRemaining:D6}\";\n    }\n\n    // --- auto-generated ---\n    public void ReadFields(BinaryReader reader)\n    {\n        Microseconds = BSATN.__time_duration_micros__.Read(reader);\n    }\n\n    public readonly void WriteFields(BinaryWriter writer)\n    {\n        BSATN.__time_duration_micros__.Write(writer, Microseconds);\n    }\n\n    readonly object IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public readonly partial struct BSATN : IReadWrite<TimeDuration>\n    {\n        internal static readonly I64 __time_duration_micros__ = new();\n\n        public TimeDuration Read(BinaryReader reader) =>\n            IStructuralReadWrite.Read<TimeDuration>(reader);\n\n        public void Write(BinaryWriter writer, TimeDuration value)\n        {\n            value.WriteFields(writer);\n        }\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Product directly, not a Ref, because this is a special type.\n            new AlgebraicType.Product(\n                // Using this specific name here is important.\n                [new(\"__time_duration_micros__\", new AlgebraicType.I64(default))]\n            );\n        // --- / customized ---\n    }\n}\n\npublic partial record ScheduleAt : TaggedEnum<(TimeDuration Interval, Timestamp Time)>\n{\n    public static implicit operator ScheduleAt(TimeDuration duration) => new Interval(duration);\n\n    public static implicit operator ScheduleAt(Timestamp time) => new Time(time);\n\n    public static implicit operator ScheduleAt(TimeSpan duration) => new Interval(duration);\n\n    public static implicit operator ScheduleAt(DateTimeOffset time) => new Time(time);\n\n    public static long ToMicroseconds(TimeSpan interval) => ((TimeDuration)interval).Microseconds;\n\n    public static TimeSpan TimeSpanFromMicroseconds(long intervalMicros) =>\n        (TimeSpan)(new TimeDuration(intervalMicros));\n\n    public static long ToMicrosecondsSinceUnixEpoch(DateTimeOffset time) =>\n        ((Timestamp)time).MicrosecondsSinceUnixEpoch;\n\n    public static DateTimeOffset DateTimeOffsetFromMicrosSinceUnixEpoch(\n        long microsSinceUnixEpoch\n    ) => (DateTimeOffset)(new Timestamp(microsSinceUnixEpoch));\n\n    // --- auto-generated ---\n    private ScheduleAt() { }\n\n    internal enum @enum : byte\n    {\n        Interval,\n        Time,\n    }\n\n    public sealed record Interval(TimeDuration Interval_) : ScheduleAt;\n\n    public sealed record Time(Timestamp Time_) : ScheduleAt;\n\n    public readonly partial struct BSATN : IReadWrite<ScheduleAt>\n    {\n        internal static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new();\n        internal static readonly TimeDuration.BSATN Interval = new();\n        internal static readonly Timestamp.BSATN Time = new();\n\n        public ScheduleAt Read(BinaryReader reader) =>\n            __enumTag.Read(reader) switch\n            {\n                @enum.Interval => new Interval(Interval.Read(reader)),\n                @enum.Time => new Time(Time.Read(reader)),\n                _ => throw new InvalidOperationException(\n                    \"Invalid tag value, this state should be unreachable.\"\n                ),\n            };\n\n        public void Write(BinaryWriter writer, ScheduleAt value)\n        {\n            switch (value)\n            {\n                case Interval(var inner):\n                    __enumTag.Write(writer, @enum.Interval);\n                    Interval.Write(writer, inner);\n                    break;\n\n                case Time(var inner):\n                    __enumTag.Write(writer, @enum.Time);\n                    Time.Write(writer, inner);\n                    break;\n            }\n        }\n\n        // --- / auto-generated ---\n\n        // --- customized ---\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            // Return a Sum directly, not a Ref, because this is a special type.\n            new AlgebraicType.Sum(\n                [\n                    // Using these specific names here is important.\n                    new(\"Interval\", Interval.GetAlgebraicType(registrar)),\n                    new(\"Time\", Time.GetAlgebraicType(registrar)),\n                ]\n            );\n        // --- / customized ---\n    }\n}\n\npublic partial record Result<T, E> : TaggedEnum<(T Ok, E Err)>\n{\n    public static implicit operator Result<T, E>(T value) => new OkR(value);\n\n    public static implicit operator Result<T, E>(E error) => new ErrR(error);\n\n    public TResult Match<TResult>(Func<T, TResult> onOk, Func<E, TResult> onErr) =>\n        this switch\n        {\n            OkR(var v) => onOk(v),\n            ErrR(var e) => onErr(e),\n            _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n        };\n\n    public static Result<T, E> Ok(T value) => new OkR(value);\n\n    public static Result<T, E> Err(E error) => new ErrR(error);\n\n    public T UnwrapOrThrow()\n    {\n        return this switch\n        {\n            OkR(var v) => v,\n            ErrR(var e) when e is not null => throw new Exception(e.ToString()),\n            ErrR(_) => throw new InvalidOperationException(\n                \"Result failed without an error object.\"\n            ),\n            _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n        };\n    }\n\n    public T UnwrapOr(T defaultValue) =>\n        this switch\n        {\n            OkR(var v) => v,\n            _ => defaultValue,\n        };\n\n    public T UnwrapOrElse(Func<E, T> f) =>\n        this switch\n        {\n            OkR(var v) => v,\n            ErrR(var e) => f(e),\n            _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n        };\n\n    // ----- auto-generated -----\n\n    private Result() { }\n\n    internal enum @enum : byte\n    {\n        Ok,\n        Err,\n    }\n\n    public sealed record OkR(T Value) : Result<T, E>;\n\n    public sealed record ErrR(E Error) : Result<T, E>;\n\n    private enum Variant : byte\n    {\n        Ok = 0,\n        Err = 1,\n    }\n\n    public readonly struct BSATN<OkRW, ErrRW> : IReadWrite<Result<T, E>>\n        where OkRW : struct, IReadWrite<T>\n        where ErrRW : struct, IReadWrite<E>\n    {\n        private static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new();\n        private static readonly OkRW okRW = new();\n        private static readonly ErrRW errRW = new();\n\n        public Result<T, E> Read(BinaryReader reader) =>\n            __enumTag.Read(reader) switch\n            {\n                @enum.Ok => new OkR(okRW.Read(reader)),\n                @enum.Err => new ErrR(errRW.Read(reader)),\n                _ => throw new InvalidOperationException(),\n            };\n\n        public void Write(BinaryWriter writer, Result<T, E> value)\n        {\n            switch (value)\n            {\n                case OkR(var v):\n                    __enumTag.Write(writer, @enum.Ok);\n                    okRW.Write(writer, v);\n                    break;\n\n                case ErrR(var e):\n                    __enumTag.Write(writer, @enum.Err);\n                    errRW.Write(writer, e);\n                    break;\n            }\n        }\n\n        public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>\n            AlgebraicType.MakeResult(\n                okRW.GetAlgebraicType(registrar),\n                errRW.GetAlgebraicType(registrar)\n            );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/Db.cs",
    "content": "﻿namespace SpacetimeDB;\n\npublic abstract record DbContext<DbView>(DbView Db)\n    where DbView : class, new()\n{\n    public DbContext()\n        : this(new DbView()) { }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/HttpWireTypes.cs",
    "content": "namespace SpacetimeDB;\n\nusing System.ComponentModel;\n\n// NOTE: These types define the stable BSATN wire format for the procedure_http_request ABI.\n// They must match `spacetimedb_lib::http::{Request, Response}` exactly (field order + types),\n// because the host BSATN-decodes these bytes directly and may trap on mismatch.\n// Do not reorder fields or extend these types; add a new versioned ABI instead.\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial record HttpMethodWire\n    : TaggedEnum<(\n        Unit Get,\n        Unit Head,\n        Unit Post,\n        Unit Put,\n        Unit Delete,\n        Unit Connect,\n        Unit Options,\n        Unit Trace,\n        Unit Patch,\n        string Extension\n    )>;\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic enum HttpVersionWire : byte\n{\n    Http09,\n    Http10,\n    Http11,\n    Http2,\n    Http3,\n}\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial struct HttpHeaderPairWire\n{\n    public string Name;\n    public byte[] Value;\n}\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial struct HttpHeadersWire\n{\n    public HttpHeaderPairWire[] Entries;\n}\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial struct HttpTimeoutWire\n{\n    public TimeDuration Timeout;\n}\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial struct HttpRequestWire\n{\n    public HttpMethodWire Method;\n    public HttpHeadersWire Headers;\n    public HttpTimeoutWire? Timeout;\n    public string Uri;\n    public HttpVersionWire Version;\n}\n\n[Type]\n[EditorBrowsable(EditorBrowsableState.Never)]\npublic partial struct HttpResponseWire\n{\n    public HttpHeadersWire Headers;\n    public HttpVersionWire Version;\n    public ushort Code;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/Internal/ByteArrayComparer.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System.Runtime.CompilerServices;\n\n// Note: this utility struct is used by the C# client SDK so it needs to be public.\npublic readonly struct ByteArrayComparer : IEqualityComparer<byte[]>\n{\n    public static readonly ByteArrayComparer Instance = new();\n\n    public bool Equals(byte[]? left, byte[]? right)\n    {\n        if (ReferenceEquals(left, right))\n        {\n            return true;\n        }\n\n        if (left is null || right is null || left.Length != right.Length)\n        {\n            return false;\n        }\n\n        return EqualsUnvectorized(left, right);\n    }\n\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    static bool EqualsUnvectorized(byte[] left, byte[] right)\n    {\n        for (var i = 0; i < left.Length; i++)\n        {\n            if (left[i] != right[i])\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public int GetHashCode(byte[] obj)\n    {\n        var hash = 17;\n        foreach (var b in obj)\n        {\n            hash = hash * 31 + b;\n        }\n        return hash;\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/QueryBuilder.cs",
    "content": "#nullable enable\n\nnamespace SpacetimeDB;\n\nusing System;\nusing System.Globalization;\n\npublic readonly struct SqlLiteral<T>\n{\n    internal string Sql { get; }\n\n    internal SqlLiteral(string sql)\n    {\n        Sql = sql;\n    }\n\n    public override string ToString() => Sql;\n}\n\npublic static class SqlLit\n{\n    public static SqlLiteral<string> String(ReadOnlySpan<char> value) =>\n        new(SqlFormat.FormatStringLiteral(value));\n\n    public static SqlLiteral<bool> Bool(bool value) => new(value ? \"TRUE\" : \"FALSE\");\n\n    public static SqlLiteral<sbyte> Int(sbyte value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<byte> Int(byte value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<short> Int(short value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<ushort> Int(ushort value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<int> Int(int value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<uint> Int(uint value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<long> Int(long value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<ulong> Int(ulong value) =>\n        new(value.ToString(CultureInfo.InvariantCulture));\n\n    public static SqlLiteral<U128> Int(U128 value) => new(value.ToString());\n\n    public static SqlLiteral<Identity> Identity(Identity value) =>\n        new(SqlFormat.FormatHexLiteral(value.ToString()));\n\n    public static SqlLiteral<ConnectionId> ConnectionId(ConnectionId value) =>\n        new(SqlFormat.FormatHexLiteral(value.ToString()));\n\n    public static SqlLiteral<Uuid> Uuid(Uuid value) =>\n        new(SqlFormat.FormatHexLiteral(value.ToString()));\n}\n\npublic interface IQuery<TRow>\n{\n    string ToSql();\n}\n\npublic readonly struct BoolExpr<TRow>\n{\n    public string Sql { get; }\n\n    public BoolExpr(string sql)\n    {\n        Sql = sql;\n    }\n\n    public BoolExpr<TRow> And(BoolExpr<TRow> other) => new($\"({Sql} AND {other.Sql})\");\n\n    public BoolExpr<TRow> Or(BoolExpr<TRow> other) => new($\"({Sql} OR {other.Sql})\");\n\n    public BoolExpr<TRow> Not() => new($\"(NOT {Sql})\");\n\n    public override string ToString() => Sql;\n\n    public static implicit operator BoolExpr<TRow>(bool value) => new(value ? \"TRUE\" : \"FALSE\");\n\n    public static implicit operator BoolExpr<TRow>(Col<TRow, bool> col) => col.Eq(true);\n\n    public static implicit operator BoolExpr<TRow>(IxCol<TRow, bool> col) => col.Eq(true);\n}\n\ninternal static class QueryPredicate\n{\n    internal static BoolExpr<TRow> ToBoolExpr<TRow>(object value) =>\n        value switch\n        {\n            BoolExpr<TRow> expr => expr,\n            Col<TRow, bool> col => col.Eq(true),\n            IxCol<TRow, bool> col => col.Eq(true),\n            bool b => new BoolExpr<TRow>(b ? \"TRUE\" : \"FALSE\"),\n            _ => throw new ArgumentException(\n                $\"Unsupported predicate type '{value.GetType().Name}'. Expected BoolExpr<{typeof(TRow).Name}> or a boolean column.\",\n                nameof(value)\n            ),\n        };\n}\n\npublic readonly struct IxJoinEq<TLeftRow, TRightRow>\n{\n    internal string LeftRefSql { get; }\n    internal string RightRefSql { get; }\n\n    internal IxJoinEq(string leftRefSql, string rightRefSql)\n    {\n        LeftRefSql = leftRefSql;\n        RightRefSql = rightRefSql;\n    }\n}\n\npublic readonly struct Col<TRow, TValue>\n    where TValue : notnull\n{\n    private readonly string tableName;\n    private readonly string columnName;\n\n    public Col(string tableName, string columnName)\n    {\n        this.tableName = tableName;\n        this.columnName = columnName;\n    }\n\n    internal string RefSql =>\n        $\"{SqlFormat.QuoteIdent(tableName)}.{SqlFormat.QuoteIdent(columnName)}\";\n\n    public BoolExpr<TRow> Eq(SqlLiteral<TValue> value) => new($\"({RefSql} = {value.Sql})\");\n\n    public BoolExpr<TRow> Eq(Col<TRow, TValue> other) => new($\"({RefSql} = {other.RefSql})\");\n\n    public BoolExpr<TRow> Neq(SqlLiteral<TValue> value) => new($\"({RefSql} <> {value.Sql})\");\n\n    public BoolExpr<TRow> Neq(Col<TRow, TValue> other) => new($\"({RefSql} <> {other.RefSql})\");\n\n    public BoolExpr<TRow> Lt(SqlLiteral<TValue> value) => new($\"({RefSql} < {value.Sql})\");\n\n    public BoolExpr<TRow> Lte(SqlLiteral<TValue> value) => new($\"({RefSql} <= {value.Sql})\");\n\n    public BoolExpr<TRow> Gt(SqlLiteral<TValue> value) => new($\"({RefSql} > {value.Sql})\");\n\n    public BoolExpr<TRow> Gte(SqlLiteral<TValue> value) => new($\"({RefSql} >= {value.Sql})\");\n\n    public BoolExpr<TRow> Lt(Col<TRow, TValue> other) => new($\"({RefSql} < {other.RefSql})\");\n\n    public BoolExpr<TRow> Lte(Col<TRow, TValue> other) => new($\"({RefSql} <= {other.RefSql})\");\n\n    public BoolExpr<TRow> Gt(Col<TRow, TValue> other) => new($\"({RefSql} > {other.RefSql})\");\n\n    public BoolExpr<TRow> Gte(Col<TRow, TValue> other) => new($\"({RefSql} >= {other.RefSql})\");\n\n    public override string ToString() => RefSql;\n}\n\npublic readonly struct IxCol<TRow, TValue>\n    where TValue : notnull\n{\n    private readonly string tableName;\n    private readonly string columnName;\n\n    public IxCol(string tableName, string columnName)\n    {\n        this.tableName = tableName;\n        this.columnName = columnName;\n    }\n\n    internal string RefSql =>\n        $\"{SqlFormat.QuoteIdent(tableName)}.{SqlFormat.QuoteIdent(columnName)}\";\n\n    public BoolExpr<TRow> Eq(SqlLiteral<TValue> value) => new($\"({RefSql} = {value.Sql})\");\n\n    public IxJoinEq<TRow, TOtherRow> Eq<TOtherRow>(IxCol<TOtherRow, TValue> other) =>\n        new(RefSql, other.RefSql);\n\n    public BoolExpr<TRow> Neq(SqlLiteral<TValue> value) => new($\"({RefSql} <> {value.Sql})\");\n\n    public override string ToString() => RefSql;\n}\n\npublic sealed class Table<TRow, TCols, TIxCols> : IQuery<TRow>\n{\n    private readonly string tableName;\n    private readonly TCols cols;\n    private readonly TIxCols ixCols;\n\n    public Table(string tableName, TCols cols, TIxCols ixCols)\n    {\n        this.tableName = tableName;\n        this.cols = cols;\n        this.ixCols = ixCols;\n    }\n\n    internal string TableRefSql => SqlFormat.QuoteIdent(tableName);\n\n    internal TCols Cols => cols;\n\n    internal TIxCols IxCols => ixCols;\n\n    public string ToSql() => $\"SELECT * FROM {SqlFormat.QuoteIdent(tableName)}\";\n\n    public FromWhere<TRow, TCols, TIxCols> Where<TPredicate>(Func<TCols, TPredicate> predicate) =>\n        new(this, QueryPredicate.ToBoolExpr<TRow>(predicate(cols)!));\n\n    public FromWhere<TRow, TCols, TIxCols> Where<TPredicate>(\n        Func<TCols, TIxCols, TPredicate> predicate\n    ) => new(this, QueryPredicate.ToBoolExpr<TRow>(predicate(cols, ixCols)!));\n\n    public FromWhere<TRow, TCols, TIxCols> Filter<TPredicate>(Func<TCols, TPredicate> predicate) =>\n        Where(predicate);\n\n    public FromWhere<TRow, TCols, TIxCols> Filter<TPredicate>(\n        Func<TCols, TIxCols, TPredicate> predicate\n    ) => Where(predicate);\n\n    public LeftSemiJoin<TRow, TCols, TIxCols, TRightRow, TRightCols, TRightIxCols> LeftSemijoin<\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    >(\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        Func<TIxCols, TRightIxCols, IxJoinEq<TRow, TRightRow>> on\n    ) => new(this, right, on(ixCols, right.ixCols), whereExpr: null);\n\n    public RightSemiJoin<TRow, TCols, TIxCols, TRightRow, TRightCols, TRightIxCols> RightSemijoin<\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    >(\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        Func<TIxCols, TRightIxCols, IxJoinEq<TRow, TRightRow>> on\n    ) => new(this, right, on(ixCols, right.ixCols), leftWhereExpr: null);\n}\n\npublic sealed class FromWhere<TRow, TCols, TIxCols> : IQuery<TRow>\n{\n    private readonly Table<TRow, TCols, TIxCols> table;\n    private readonly BoolExpr<TRow> expr;\n\n    internal FromWhere(Table<TRow, TCols, TIxCols> table, BoolExpr<TRow> expr)\n    {\n        this.table = table;\n        this.expr = expr;\n    }\n\n    public FromWhere<TRow, TCols, TIxCols> Where<TPredicate>(Func<TCols, TPredicate> predicate) =>\n        new(table, expr.And(QueryPredicate.ToBoolExpr<TRow>(predicate(table.Cols)!)));\n\n    public FromWhere<TRow, TCols, TIxCols> Where<TPredicate>(\n        Func<TCols, TIxCols, TPredicate> predicate\n    ) =>\n        new(table, expr.And(QueryPredicate.ToBoolExpr<TRow>(predicate(table.Cols, table.IxCols)!)));\n\n    public FromWhere<TRow, TCols, TIxCols> Filter<TPredicate>(Func<TCols, TPredicate> predicate) =>\n        Where(predicate);\n\n    public FromWhere<TRow, TCols, TIxCols> Filter<TPredicate>(\n        Func<TCols, TIxCols, TPredicate> predicate\n    ) => Where(predicate);\n\n    public string ToSql() => $\"{table.ToSql()} WHERE {expr.Sql}\";\n\n    public LeftSemiJoin<TRow, TCols, TIxCols, TRightRow, TRightCols, TRightIxCols> LeftSemijoin<\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    >(\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        Func<TIxCols, TRightIxCols, IxJoinEq<TRow, TRightRow>> on\n    ) => new(table, right, on(table.IxCols, right.IxCols), expr);\n\n    public RightSemiJoin<TRow, TCols, TIxCols, TRightRow, TRightCols, TRightIxCols> RightSemijoin<\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    >(\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        Func<TIxCols, TRightIxCols, IxJoinEq<TRow, TRightRow>> on\n    ) => new(table, right, on(table.IxCols, right.IxCols), expr);\n}\n\npublic sealed class LeftSemiJoin<\n    TLeftRow,\n    TLeftCols,\n    TLeftIxCols,\n    TRightRow,\n    TRightCols,\n    TRightIxCols\n> : IQuery<TLeftRow>\n{\n    private readonly Table<TLeftRow, TLeftCols, TLeftIxCols> left;\n    private readonly Table<TRightRow, TRightCols, TRightIxCols> right;\n    private readonly string leftJoinRefSql;\n    private readonly string rightJoinRefSql;\n    private readonly BoolExpr<TLeftRow>? whereExpr;\n\n    internal LeftSemiJoin(\n        Table<TLeftRow, TLeftCols, TLeftIxCols> left,\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        IxJoinEq<TLeftRow, TRightRow> join,\n        BoolExpr<TLeftRow>? whereExpr\n    )\n    {\n        this.left = left;\n        this.right = right;\n        leftJoinRefSql = join.LeftRefSql;\n        rightJoinRefSql = join.RightRefSql;\n        this.whereExpr = whereExpr;\n    }\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TLeftCols, BoolExpr<TLeftRow>> predicate)\n    {\n        var extra = predicate(left.Cols);\n        BoolExpr<TLeftRow>? next = whereExpr.HasValue ? whereExpr.Value.And(extra) : extra;\n        return new(\n            left,\n            right,\n            new IxJoinEq<TLeftRow, TRightRow>(leftJoinRefSql, rightJoinRefSql),\n            next\n        );\n    }\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TLeftCols, TLeftIxCols, BoolExpr<TLeftRow>> predicate)\n    {\n        var extra = predicate(left.Cols, left.IxCols);\n        BoolExpr<TLeftRow>? next = whereExpr.HasValue ? whereExpr.Value.And(extra) : extra;\n        return new(\n            left,\n            right,\n            new IxJoinEq<TLeftRow, TRightRow>(leftJoinRefSql, rightJoinRefSql),\n            next\n        );\n    }\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TLeftCols, Col<TLeftRow, bool>> predicate) =>\n        Where(cols => predicate(cols).Eq(true));\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TLeftCols, BoolExpr<TLeftRow>> predicate) => Where(predicate);\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TLeftCols, Col<TLeftRow, bool>> predicate) => Where(predicate);\n\n    public LeftSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TLeftCols, TLeftIxCols, BoolExpr<TLeftRow>> predicate) => Where(predicate);\n\n    public string ToSql()\n    {\n        var whereClause = whereExpr.HasValue ? $\" WHERE {whereExpr.Value.Sql}\" : string.Empty;\n        return $\"SELECT {left.TableRefSql}.* FROM {left.TableRefSql} JOIN {right.TableRefSql} ON {leftJoinRefSql} = {rightJoinRefSql}{whereClause}\";\n    }\n}\n\npublic sealed class RightSemiJoin<\n    TLeftRow,\n    TLeftCols,\n    TLeftIxCols,\n    TRightRow,\n    TRightCols,\n    TRightIxCols\n> : IQuery<TRightRow>\n{\n    private readonly Table<TLeftRow, TLeftCols, TLeftIxCols> left;\n    private readonly Table<TRightRow, TRightCols, TRightIxCols> right;\n    private readonly string leftJoinRefSql;\n    private readonly string rightJoinRefSql;\n    private readonly BoolExpr<TLeftRow>? leftWhereExpr;\n    private readonly BoolExpr<TRightRow>? rightWhereExpr;\n\n    internal RightSemiJoin(\n        Table<TLeftRow, TLeftCols, TLeftIxCols> left,\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        IxJoinEq<TLeftRow, TRightRow> join,\n        BoolExpr<TLeftRow>? leftWhereExpr,\n        BoolExpr<TRightRow>? rightWhereExpr\n    )\n    {\n        this.left = left;\n        this.right = right;\n        leftJoinRefSql = join.LeftRefSql;\n        rightJoinRefSql = join.RightRefSql;\n        this.leftWhereExpr = leftWhereExpr;\n        this.rightWhereExpr = rightWhereExpr;\n    }\n\n    internal RightSemiJoin(\n        Table<TLeftRow, TLeftCols, TLeftIxCols> left,\n        Table<TRightRow, TRightCols, TRightIxCols> right,\n        IxJoinEq<TLeftRow, TRightRow> join,\n        BoolExpr<TLeftRow>? leftWhereExpr\n    )\n        : this(left, right, join, leftWhereExpr, rightWhereExpr: null) { }\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TRightCols, BoolExpr<TRightRow>> predicate)\n    {\n        var extra = predicate(right.Cols);\n        BoolExpr<TRightRow>? nextRight = rightWhereExpr.HasValue\n            ? rightWhereExpr.Value.And(extra)\n            : extra;\n        return new(\n            left,\n            right,\n            new IxJoinEq<TLeftRow, TRightRow>(leftJoinRefSql, rightJoinRefSql),\n            leftWhereExpr,\n            nextRight\n        );\n    }\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TRightCols, TRightIxCols, BoolExpr<TRightRow>> predicate)\n    {\n        var extra = predicate(right.Cols, right.IxCols);\n        BoolExpr<TRightRow>? nextRight = rightWhereExpr.HasValue\n            ? rightWhereExpr.Value.And(extra)\n            : extra;\n        return new(\n            left,\n            right,\n            new IxJoinEq<TLeftRow, TRightRow>(leftJoinRefSql, rightJoinRefSql),\n            leftWhereExpr,\n            nextRight\n        );\n    }\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Where(Func<TRightCols, Col<TRightRow, bool>> predicate) =>\n        Where(cols => predicate(cols).Eq(true));\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TRightCols, BoolExpr<TRightRow>> predicate) => Where(predicate);\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TRightCols, Col<TRightRow, bool>> predicate) => Where(predicate);\n\n    public RightSemiJoin<\n        TLeftRow,\n        TLeftCols,\n        TLeftIxCols,\n        TRightRow,\n        TRightCols,\n        TRightIxCols\n    > Filter(Func<TRightCols, TRightIxCols, BoolExpr<TRightRow>> predicate) => Where(predicate);\n\n    public string ToSql()\n    {\n        var whereClause = string.Empty;\n\n        if (leftWhereExpr.HasValue && rightWhereExpr.HasValue)\n        {\n            whereClause = $\" WHERE {leftWhereExpr.Value.Sql} AND {rightWhereExpr.Value.Sql}\";\n        }\n        else if (leftWhereExpr.HasValue)\n        {\n            whereClause = $\" WHERE {leftWhereExpr.Value.Sql}\";\n        }\n        else if (rightWhereExpr.HasValue)\n        {\n            whereClause = $\" WHERE {rightWhereExpr.Value.Sql}\";\n        }\n\n        return $\"SELECT {right.TableRefSql}.* FROM {left.TableRefSql} JOIN {right.TableRefSql} ON {leftJoinRefSql} = {rightJoinRefSql}{whereClause}\";\n    }\n}\n\npublic static class QueryBuilderExtensions\n{\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Eq(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Neq(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Lt(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Lte(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Gt(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Gte(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, bool> col, bool value) =>\n        col.Eq(SqlLit.Bool(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, bool> col, bool value) =>\n        col.Neq(SqlLit.Bool(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, sbyte> col, sbyte value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, byte> col, byte value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, short> col, short value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, short> col, short value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, short> col, short value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, short> col, short value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, short> col, short value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, short> col, short value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, ushort> col, ushort value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, int> col, int value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, int> col, int value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, int> col, int value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, int> col, int value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, int> col, int value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, int> col, int value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, uint> col, uint value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, long> col, long value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, long> col, long value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, long> col, long value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, long> col, long value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, long> col, long value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, long> col, long value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, ulong> col, ulong value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lt<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Lt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Lte<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Lte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gt<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Gt(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Gte<TRow>(this Col<TRow, U128> col, U128 value) =>\n        col.Gte(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, Identity> col, Identity value) =>\n        col.Eq(SqlLit.Identity(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, Identity> col, Identity value) =>\n        col.Neq(SqlLit.Identity(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, ConnectionId> col, ConnectionId value) =>\n        col.Eq(SqlLit.ConnectionId(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, ConnectionId> col, ConnectionId value) =>\n        col.Neq(SqlLit.ConnectionId(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this Col<TRow, Uuid> col, Uuid value) =>\n        col.Eq(SqlLit.Uuid(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this Col<TRow, Uuid> col, Uuid value) =>\n        col.Neq(SqlLit.Uuid(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, string> col, ReadOnlySpan<char> value) =>\n        col.Eq(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(\n        this IxCol<TRow, string> col,\n        ReadOnlySpan<char> value\n    ) => col.Neq(SqlLit.String(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, bool> col, bool value) =>\n        col.Eq(SqlLit.Bool(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, bool> col, bool value) =>\n        col.Neq(SqlLit.Bool(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, sbyte> col, sbyte value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, sbyte> col, sbyte value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, byte> col, byte value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, byte> col, byte value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, short> col, short value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, short> col, short value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, ushort> col, ushort value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, ushort> col, ushort value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, int> col, int value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, int> col, int value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, uint> col, uint value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, uint> col, uint value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, long> col, long value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, long> col, long value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, ulong> col, ulong value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, ulong> col, ulong value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, U128> col, U128 value) =>\n        col.Eq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, U128> col, U128 value) =>\n        col.Neq(SqlLit.Int(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, Identity> col, Identity value) =>\n        col.Eq(SqlLit.Identity(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, Identity> col, Identity value) =>\n        col.Neq(SqlLit.Identity(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, ConnectionId> col, ConnectionId value) =>\n        col.Eq(SqlLit.ConnectionId(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(\n        this IxCol<TRow, ConnectionId> col,\n        ConnectionId value\n    ) => col.Neq(SqlLit.ConnectionId(value));\n\n    public static BoolExpr<TRow> Eq<TRow>(this IxCol<TRow, Uuid> col, Uuid value) =>\n        col.Eq(SqlLit.Uuid(value));\n\n    public static BoolExpr<TRow> Neq<TRow>(this IxCol<TRow, Uuid> col, Uuid value) =>\n        col.Neq(SqlLit.Uuid(value));\n}\n\ninternal static class SqlFormat\n{\n    public static string QuoteIdent(string ident)\n    {\n        ident ??= string.Empty;\n        return $\"\\\"{ident.Replace(\"\\\"\", \"\\\"\\\"\")}\\\"\";\n    }\n\n    private static string EscapeString(string s) => s.Replace(\"'\", \"''\");\n\n    public static string FormatStringLiteral(ReadOnlySpan<char> value) =>\n        $\"'{EscapeString(value.ToString())}'\";\n\n    public static string FormatHexLiteral(string hex)\n    {\n        if (hex is null)\n        {\n            throw new ArgumentNullException(nameof(hex));\n        }\n\n        var s = hex;\n        if (s.StartsWith(\"0x\", StringComparison.OrdinalIgnoreCase))\n        {\n            s = s.Substring(2);\n        }\n\n        s = s.Replace(\"-\", string.Empty);\n\n        for (var i = 0; i < s.Length; i++)\n        {\n            var c = s[i];\n            var isHex = c is >= '0' and <= '9' or >= 'a' and <= 'f' or >= 'A' and <= 'F';\n            if (!isHex)\n            {\n                throw new ArgumentOutOfRangeException(nameof(hex), $\"Invalid hex character '{c}'.\");\n            }\n        }\n\n        return $\"0x{s}\";\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime/package.json",
    "content": "{\n\t\"name\": \"com.clockworklabs.spacetimedbsdk.runtime\",\n\t\"displayName\": \"SpacetimeDB BSATN Runtime\",\n\t\"version\": \"0.8.0\",\n\t\"description\": \"The SpacetimeDB Client SDK is a software development kit (SDK) designed to interact with and manipulate SpacetimeDB modules..\",\n\t\"keywords\": [],\n\t\"author\": {\n\t  \"name\": \"Clockwork Labs\",\n\t  \"email\": \"ingvar@clockworklabs.io\",\n\t  \"url\": \"https://spacetimedb.com\"\n\t},\n\t\"repository\": {\n\t  \"type\": \"git\",\n\t  \"url\": \"https://github.com/clockworklabs/SpacetimeDB\"\n\t},\n\t\"license\": \"\"\n  }\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime.Tests/BSATN.Runtime.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <IsPackable>false</IsPackable>\n    <IsTestProject>true</IsTestProject>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <TargetFrameworks>net8.0</TargetFrameworks>\n    <RootNamespace>SpacetimeDB</RootNamespace>\n    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"CsCheck\" Version=\"4.1.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.6.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.9.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.8.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../BSATN.Runtime/BSATN.Runtime.csproj\" />\n    <ProjectReference\n      Include=\"../BSATN.Codegen/BSATN.Codegen.csproj\"\n      OutputItemType=\"Analyzer\"\n      ReferenceOutputAssembly=\"false\"\n    />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs",
    "content": "namespace SpacetimeDB;\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Security.Cryptography;\nusing CsCheck;\nusing SpacetimeDB.BSATN;\nusing Xunit;\n\npublic static partial class BSATNRuntimeTests\n{\n    [Fact]\n    public static void ConnectionIdRoundtrips()\n    {\n        var str = \"00112233445566778899AABBCCDDEEFF\";\n        var connId = ConnectionId.FromHexString(str);\n\n        Assert.NotNull(connId);\n        Assert.Equal(connId.ToString(), str);\n\n        var bytes = Convert.FromHexString(str);\n\n        var connId2 = ConnectionId.FromBigEndian(bytes);\n        Assert.Equal(connId2, connId);\n\n        Array.Reverse(bytes);\n        var connId3 = ConnectionId.From(bytes);\n        Assert.Equal(connId3, connId);\n\n        var memoryStream = new MemoryStream();\n        var bsatn = new ConnectionId.BSATN();\n        using (var writer = new BinaryWriter(memoryStream))\n        {\n            if (connId is { } connIdNotNull)\n            {\n                bsatn.Write(writer, connIdNotNull);\n            }\n            else\n            {\n                Assert.Fail(\"Impossible\");\n            }\n        }\n\n        var littleEndianBytes = memoryStream.ToArray();\n        var reader = new BinaryReader(new MemoryStream(littleEndianBytes));\n        var connId4 = bsatn.Read(reader);\n        Assert.Equal(connId4, connId);\n\n        // Note: From = FromLittleEndian\n        var connId5 = ConnectionId.From(littleEndianBytes);\n        Assert.Equal(connId5, connId);\n    }\n\n    static readonly Gen<string> genHex = Gen.String[Gen.Char[\"0123456789abcdef\"], 0, 128];\n\n    [Fact]\n    public static void ConnectionIdLengthCheck()\n    {\n        genHex.Sample(s =>\n        {\n            if (s.Length == 32)\n            {\n                return;\n            }\n            Assert.ThrowsAny<Exception>(() => ConnectionId.FromHexString(s));\n        });\n        Gen.Byte.Array[0, 64]\n            .Sample(arr =>\n            {\n                if (arr.Length == 16)\n                {\n                    return;\n                }\n                Assert.ThrowsAny<Exception>(() => ConnectionId.FromBigEndian(arr));\n                Assert.ThrowsAny<Exception>(() => ConnectionId.From(arr));\n            });\n    }\n\n    [Fact]\n    public static void IdentityRoundtrips()\n    {\n        var str = \"00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF\";\n        var ident = Identity.FromHexString(str);\n\n        Assert.Equal(ident.ToString(), str);\n\n        // We can't use this in the implementation because it isn't available\n        // in Unity's .NET. But we can use it in tests.\n        var bytes = Convert.FromHexString(str);\n\n        var ident2 = Identity.FromBigEndian(bytes);\n        Assert.Equal(ident2, ident);\n\n        Array.Reverse(bytes);\n        var ident3 = Identity.From(bytes);\n        Assert.Equal(ident3, ident);\n\n        var memoryStream = new MemoryStream();\n        var bsatn = new Identity.BSATN();\n        using (var writer = new BinaryWriter(memoryStream))\n        {\n            bsatn.Write(writer, ident);\n        }\n\n        var littleEndianBytes = memoryStream.ToArray();\n        var reader = new BinaryReader(new MemoryStream(littleEndianBytes));\n        var ident4 = bsatn.Read(reader);\n        Assert.Equal(ident4, ident);\n\n        // Note: From = FromLittleEndian\n        var ident5 = Identity.From(littleEndianBytes);\n        Assert.Equal(ident5, ident);\n    }\n\n    [Fact]\n    public static void IdentityLengthCheck()\n    {\n        genHex.Sample(s =>\n        {\n            if (s.Length == 64)\n            {\n                return;\n            }\n            Assert.ThrowsAny<Exception>(() => Identity.FromHexString(s));\n        });\n        Gen.Byte.Array[0, 64]\n            .Sample(arr =>\n            {\n                if (arr.Length == 32)\n                {\n                    return;\n                }\n                Assert.ThrowsAny<Exception>(() => Identity.FromBigEndian(arr));\n                Assert.ThrowsAny<Exception>(() => Identity.From(arr));\n            });\n    }\n\n    [Fact]\n    public static void NonHexStrings()\n    {\n        // n.b. 32 chars long\n        Assert.ThrowsAny<Exception>(\n            () => ConnectionId.FromHexString(\"these are not hex characters....\")\n        );\n    }\n\n    [Fact]\n    public static void TimestampConversionChecks()\n    {\n        var us = 1737582793990639L;\n\n        var time = ScheduleAt.DateTimeOffsetFromMicrosSinceUnixEpoch(us);\n        Assert.Equal(ScheduleAt.ToMicrosecondsSinceUnixEpoch(time), us);\n\n        var interval = ScheduleAt.TimeSpanFromMicroseconds(us);\n        Assert.Equal(ScheduleAt.ToMicroseconds(interval), us);\n\n        var stamp = new Timestamp(us);\n        var dto = (DateTimeOffset)stamp;\n        var stamp_ = (Timestamp)dto;\n        Assert.Equal(stamp, stamp_);\n\n        var duration = new TimeDuration(us);\n        var timespan = (TimeSpan)duration;\n        var duration_ = (TimeDuration)timespan;\n        Assert.Equal(duration, duration_);\n\n        var newIntervalUs = 333L;\n        var newInterval = new TimeDuration(newIntervalUs);\n        var laterStamp = stamp + newInterval;\n        Assert.Equal(laterStamp.MicrosecondsSinceUnixEpoch, us + newIntervalUs);\n        Assert.Equal(laterStamp.TimeDurationSince(stamp), newInterval);\n\n#pragma warning disable CS1718\n        Assert.True(stamp == stamp);\n#pragma warning restore CS1718\n        Assert.False(stamp == laterStamp);\n        Assert.True(stamp < laterStamp);\n        Assert.False(laterStamp < stamp);\n        Assert.Equal(-1, stamp.CompareTo(laterStamp));\n        Assert.Equal(+1, laterStamp.CompareTo(stamp));\n    }\n\n    [Fact]\n    public static void ConnectionIdComparableChecks()\n    {\n        var str = \"00112233445566778899AABBCCDDEEFF\";\n        var strHigh = \"00001111222233334444555566667777\";\n        var strLow = \"FFFFEEEEDDDDCCCCBBBBAAAA99998888\";\n\n        var connIdA = ConnectionId.FromHexString(str);\n        var connIdB = ConnectionId.FromHexString(str);\n        var connIdHigh = ConnectionId.FromHexString(strHigh);\n        var connIdLow = ConnectionId.FromHexString(strLow);\n\n        Assert.NotNull(connIdA);\n        Assert.NotNull(connIdB);\n        Assert.NotNull(connIdHigh);\n        Assert.NotNull(connIdLow);\n\n        Assert.Equal(0, connIdA.Value.CompareTo(connIdB.Value));\n        Assert.Equal(+1, connIdA.Value.CompareTo(connIdHigh.Value));\n        Assert.Equal(-1, connIdA.Value.CompareTo(connIdLow.Value));\n\n        var notAConnId = new uint();\n\n        Assert.ThrowsAny<Exception>(() => connIdA.Value.CompareTo(notAConnId));\n    }\n\n    [Fact]\n    public static void IdentityComparableChecks()\n    {\n        var str = \"00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF\";\n        var strHigh = \"0000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF\";\n        var strLow = \"FFFFEEEEDDDDCCCCBBBBAAAA9999888877776666555544443333222211110000\";\n\n        var identityA = Identity.FromHexString(str);\n        var identityB = Identity.FromHexString(str);\n        var identityHigh = Identity.FromHexString(strHigh);\n        var identityLow = Identity.FromHexString(strLow);\n\n        Assert.Equal(0, identityA.CompareTo(identityB));\n        Assert.Equal(+1, identityA.CompareTo(identityHigh));\n        Assert.Equal(-1, identityA.CompareTo(identityLow));\n\n        var notAnIdentity = new uint();\n\n        Assert.ThrowsAny<Exception>(() => identityA.CompareTo(notAnIdentity));\n    }\n\n    [Type]\n    public partial class BasicDataClass\n    {\n        public int X;\n        public string Y = \"\";\n        public int? Z;\n        public string? W;\n\n        public BasicDataClass() { }\n\n        public BasicDataClass((int x, string y, int? z, string? w) data)\n        {\n            X = data.x;\n            Y = data.y;\n            Z = data.z;\n            W = data.w;\n        }\n    }\n\n    [Type]\n    public partial struct BasicDataStruct((int x, string y, int? z, string? w) data)\n    {\n        public int X = data.x;\n        public string Y = data.y;\n        public int? Z = data.z;\n        public string? W = data.w;\n    }\n\n    [Type]\n    public partial record BasicDataRecord\n    {\n        public int X;\n        public string Y = \"\";\n        public int? Z;\n        public string? W;\n\n        public BasicDataRecord() { }\n\n        public BasicDataRecord((int x, string y, int? z, string? w) data)\n        {\n            X = data.x;\n            Y = data.y;\n            Z = data.z;\n            W = data.w;\n        }\n    }\n\n    static readonly Gen<int> GenSmallInt = Gen.Int[-5, 5];\n    static readonly Gen<string> GenSmallString = Gen.String[Gen.Char.AlphaNumeric, 0, 2];\n    static readonly Gen<int?> GenNullableInt = Gen.Nullable<int>(GenSmallInt);\n    static readonly Gen<string?> GenNullableString = Gen.Null<string>(GenSmallString);\n\n    static readonly Gen<(int X, string Y, int? Z, string? W)> GenBasic = Gen.Select(\n        GenSmallInt,\n        GenSmallString,\n        GenNullableInt,\n        GenNullableString,\n        (x, y, z, w) => (x, y, z, w)\n    );\n    static readonly Gen<(\n        (int X, string Y, int? Z, string? W) c1,\n        (int X, string Y, int? Z, string? W) c2\n    )> GenTwoBasic = Gen.Select(GenBasic, GenBasic, (c1, c2) => (c1, c2));\n\n    /// <summary>\n    /// Count collisions when comparing hashcodes of non-equal structures.\n    /// </summary>\n    struct CollisionCounter\n    {\n        private uint Comparisons;\n        private uint Collisions;\n\n        public void Add(bool collides)\n        {\n            Comparisons += 1;\n            if (collides)\n            {\n                Collisions += 1;\n            }\n        }\n\n        public readonly double CollisionFraction\n        {\n            get => (double)Collisions / (double)Comparisons;\n        }\n\n        public readonly void AssertCollisionsLessThan(double fraction)\n        {\n            Assert.True(\n                CollisionFraction < fraction,\n                $\"Expected {fraction} portion of collisions, but got {CollisionFraction} = {Collisions} / {Comparisons}\"\n            );\n        }\n    }\n\n    static void TestRoundTrip<T, BSATN>(Gen<T> gen, BSATN serializer)\n        where BSATN : IReadWrite<T>\n    {\n        gen.Sample(\n            (value) =>\n            {\n                var stream = new MemoryStream();\n                var writer = new BinaryWriter(stream);\n                serializer.Write(writer, value);\n                stream.Seek(0, SeekOrigin.Begin);\n                var reader = new BinaryReader(stream);\n                var result = serializer.Read(reader);\n                Assert.Equal(value, result);\n            },\n            iter: 10_000\n        );\n    }\n\n    [Fact]\n    public static void GeneratedProductRoundTrip()\n    {\n        TestRoundTrip(\n            GenBasic.Select(value => new BasicDataClass(value)),\n            new BasicDataClass.BSATN()\n        );\n        TestRoundTrip(\n            GenBasic.Select(value => new BasicDataRecord(value)),\n            new BasicDataRecord.BSATN()\n        );\n        TestRoundTrip(\n            GenBasic.Select(value => new BasicDataStruct(value)),\n            new BasicDataStruct.BSATN()\n        );\n    }\n\n    [Fact]\n    public static void GeneratedProductEqualsWorks()\n    {\n        CollisionCounter collisionCounter = new();\n\n        GenTwoBasic.Sample(\n            example =>\n            {\n                var class1 = new BasicDataClass(example.c1);\n                var class2 = new BasicDataClass(example.c2);\n\n                var struct1 = new BasicDataStruct(example.c1);\n                var struct2 = new BasicDataStruct(example.c2);\n\n                var record1 = new BasicDataRecord(example.c1);\n                var record2 = new BasicDataRecord(example.c2);\n\n                if (example.c1 == example.c2)\n                {\n                    Assert.Equal(class1, class2);\n                    Assert.True(class1 == class2);\n                    Assert.False(class1 != class2);\n                    Assert.Equal(class1.ToString(), class2.ToString());\n                    Assert.Equal(class1.GetHashCode(), class2.GetHashCode());\n\n                    Assert.Equal(struct1, struct2);\n                    Assert.True(struct1 == struct2);\n                    Assert.False(struct1 != struct2);\n                    Assert.Equal(struct1.ToString(), struct2.ToString());\n                    Assert.Equal(struct1.GetHashCode(), struct2.GetHashCode());\n\n                    Assert.Equal(record1, record2);\n                    Assert.True(record1 == record2);\n                    Assert.False(record1 != record2);\n                    Assert.Equal(record1.ToString(), record2.ToString());\n                    Assert.Equal(record1.GetHashCode(), record2.GetHashCode());\n\n                    // hash code should not depend on the type of object.\n                    Assert.Equal(class1.GetHashCode(), record1.GetHashCode());\n                    Assert.Equal(record1.GetHashCode(), struct1.GetHashCode());\n                }\n                else\n                {\n                    Assert.NotEqual(class1, class2);\n                    Assert.False(class1 == class2);\n                    Assert.True(class1 != class2);\n                    Assert.NotEqual(class1.ToString(), class2.ToString());\n\n                    Assert.NotEqual(struct1, struct2);\n                    Assert.False(struct1 == struct2);\n                    Assert.True(struct1 != struct2);\n                    Assert.NotEqual(struct1.ToString(), struct2.ToString());\n\n                    Assert.NotEqual(record1, record2);\n                    Assert.False(record1 == record2);\n                    Assert.True(record1 != record2);\n                    Assert.NotEqual(record1.ToString(), record2.ToString());\n\n                    // hash code should not depend on the type of object.\n                    Assert.Equal(class1.GetHashCode(), record1.GetHashCode());\n                    Assert.Equal(record1.GetHashCode(), struct1.GetHashCode());\n\n                    collisionCounter.Add(class1.GetHashCode() == class2.GetHashCode());\n                }\n            },\n            iter: 10_000\n        );\n        collisionCounter.AssertCollisionsLessThan(0.05);\n    }\n\n    [Type]\n    public partial record BasicEnum\n        : TaggedEnum<(\n            int X,\n            string Y,\n            int? Z,\n            string? T,\n            BasicDataClass U,\n            BasicDataStruct V,\n            BasicDataRecord W\n        )> { }\n\n    static readonly Gen<BasicEnum> GenBasicEnum = Gen.SelectMany<int, BasicEnum>(\n        Gen.Int[0, 7],\n        tag =>\n        {\n            return tag switch\n            {\n                0 => GenSmallInt.Select(v => new BasicEnum.X(v)),\n                1 => GenSmallString.Select(v => new BasicEnum.Y(v)),\n                2 => GenNullableInt.Select(v => new BasicEnum.Z(v)),\n                3 => GenNullableString.Select(v => new BasicEnum.T(v)),\n                4 => GenBasic.Select(v => new BasicEnum.U(new BasicDataClass(v))),\n                5 => GenBasic.Select(v => new BasicEnum.V(new BasicDataStruct(v))),\n                _ => GenBasic.Select(v => new BasicEnum.W(new BasicDataRecord(v))),\n            };\n        }\n    );\n    static readonly Gen<(BasicEnum e1, BasicEnum e2)> GenTwoBasicEnum = Gen.Select(\n        GenBasicEnum,\n        GenBasicEnum,\n        (e1, e2) => (e1, e2)\n    );\n\n    [Fact]\n    public static void GeneratedSumRoundTrip()\n    {\n        TestRoundTrip(GenBasicEnum, new BasicEnum.BSATN());\n    }\n\n    [Fact]\n    public static void GeneratedSumEqualsWorks()\n    {\n        CollisionCounter collisionCounter = new();\n\n        GenTwoBasicEnum.Sample(\n            example =>\n            {\n                var equal = example switch\n                {\n                    (BasicEnum.X(var v1), BasicEnum.X(var v2)) => v1.Equals(v2),\n                    (BasicEnum.Y(var v1), BasicEnum.Y(var v2)) => v1.Equals(v2),\n                    (BasicEnum.Z(var v1), BasicEnum.Z(var v2)) => v1.Equals(v2),\n                    (BasicEnum.T(var v1), BasicEnum.T(var v2)) => v1 == null\n                        ? v2 == null\n                        : v1.Equals(v2),\n                    (BasicEnum.U(var v1), BasicEnum.U(var v2)) => v1.Equals(v2),\n                    (BasicEnum.V(var v1), BasicEnum.V(var v2)) => v1.Equals(v2),\n                    (BasicEnum.W(var v1), BasicEnum.W(var v2)) => v1.Equals(v2),\n                    _ => false,\n                };\n\n                if (equal)\n                {\n                    Assert.Equal(example.e1, example.e2);\n                    Assert.True(example.e1 == example.e2);\n                    Assert.False(example.e1 != example.e2);\n                    Assert.Equal(example.e1.ToString(), example.e2.ToString());\n                    Assert.Equal(example.e1.GetHashCode(), example.e2.GetHashCode());\n                }\n                else\n                {\n                    Assert.NotEqual(example.e1, example.e2);\n                    Assert.False(example.e1 == example.e2);\n                    Assert.True(example.e1 != example.e2);\n                    Assert.NotEqual(example.e1.ToString(), example.e2.ToString());\n                    collisionCounter.Add(example.e1.GetHashCode() == example.e2.GetHashCode());\n                }\n            },\n            iter: 10_000\n        );\n        collisionCounter.AssertCollisionsLessThan(0.05);\n    }\n\n    [Type]\n    public partial class ContainsList\n    {\n        public List<BasicEnum?>? TheList = [];\n\n        public ContainsList() { }\n\n        public ContainsList(List<BasicEnum?>? theList)\n        {\n            TheList = theList;\n        }\n    }\n\n    static readonly Gen<ContainsList> GenContainsList = GenBasicEnum\n        .Null()\n        .List[0, 2]\n        .Null()\n        .Select(list => new ContainsList(list));\n    static readonly Gen<(ContainsList e1, ContainsList e2)> GenTwoContainsList = Gen.Select(\n        GenContainsList,\n        GenContainsList,\n        (e1, e2) => (e1, e2)\n    );\n\n    [Fact]\n    public static void GeneratedListRoundTrip()\n    {\n        TestRoundTrip(GenContainsList, new ContainsList.BSATN());\n    }\n\n    [Fact]\n    public static void GeneratedListEqualsWorks()\n    {\n        CollisionCounter collisionCounter = new();\n        GenTwoContainsList.Sample(\n            example =>\n            {\n                var equal =\n                    example.e1.TheList == null\n                        ? example.e2.TheList == null\n                        : (\n                            example.e2.TheList == null\n                                ? false\n                                : example.e1.TheList.SequenceEqual(example.e2.TheList)\n                        );\n\n                if (equal)\n                {\n                    Assert.Equal(example.e1, example.e2);\n                    Assert.True(example.e1 == example.e2);\n                    Assert.False(example.e1 != example.e2);\n                    Assert.Equal(example.e1.ToString(), example.e2.ToString());\n                    Assert.Equal(example.e1.GetHashCode(), example.e2.GetHashCode());\n                }\n                else\n                {\n                    Assert.NotEqual(example.e1, example.e2);\n                    Assert.False(example.e1 == example.e2);\n                    Assert.True(example.e1 != example.e2);\n                    Assert.NotEqual(example.e1.ToString(), example.e2.ToString());\n                    collisionCounter.Add(example.e1.GetHashCode() == example.e2.GetHashCode());\n                }\n            },\n            iter: 10_000\n        );\n        collisionCounter.AssertCollisionsLessThan(0.05);\n    }\n\n    [Type]\n    public partial class ContainsNestedList\n    {\n        public List<BasicEnum[][]> TheList = [];\n\n        public ContainsNestedList() { }\n\n        public ContainsNestedList(List<BasicEnum[][]> theList)\n        {\n            TheList = theList;\n        }\n    }\n\n    // For the serialization test, forbid nulls.\n    static readonly Gen<ContainsNestedList> GenContainsNestedListNoNulls = GenBasicEnum\n        .Array[0, 2]\n        .Array[0, 2]\n        .List[0, 2]\n        .Select(list => new ContainsNestedList(list));\n\n    [Fact]\n    public static void GeneratedNestedListRoundTrip()\n    {\n        TestRoundTrip(GenContainsNestedListNoNulls, new ContainsNestedList.BSATN());\n    }\n\n    // However, for the equals + hashcode test, throw in some nulls, just to be paranoid.\n    // The user might have constructed a bad one of these in-memory.\n\n#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.\n    static readonly Gen<ContainsNestedList> GenContainsNestedList = GenBasicEnum\n        .Null()\n        .Array[0, 2]\n        .Null()\n        .Array[0, 2]\n        .Null()\n        .List[0, 2]\n        .Select(list => new ContainsNestedList(list));\n#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.\n\n    static readonly Gen<(ContainsNestedList e1, ContainsNestedList e2)> GenTwoContainsNestedList =\n        Gen.Select(GenContainsNestedList, GenContainsNestedList, (e1, e2) => (e1, e2));\n\n    class EnumerableEqualityComparer<T>(EqualityComparer<T> equalityComparer)\n        : EqualityComparer<IEnumerable<T>>\n    {\n        private readonly EqualityComparer<T> EqualityComparer = equalityComparer;\n\n        public override bool Equals(IEnumerable<T>? x, IEnumerable<T>? y) =>\n            x == null ? y == null : (y == null ? false : x.SequenceEqual(y, EqualityComparer));\n\n        public override int GetHashCode([DisallowNull] IEnumerable<T> obj)\n        {\n            var hashCode = 0;\n            foreach (var item in obj)\n            {\n                if (item != null)\n                {\n                    hashCode ^= EqualityComparer.GetHashCode(item);\n                }\n            }\n            return hashCode;\n        }\n    }\n\n    [Type]\n    enum Banana\n    {\n        Cavendish,\n        LadyFinger,\n        RedBanana,\n        Manzano,\n        BlueJava,\n        GreenPlantain,\n        YellowPlantain,\n        PisangRaja,\n    }\n\n    [Fact]\n    public static void EnumSerializationWorks()\n    {\n        var serializer = new Enum<Banana>();\n        var bananas = new Banana[]\n        {\n            Banana.Cavendish,\n            Banana.LadyFinger,\n            Banana.RedBanana,\n            Banana.Manzano,\n            Banana.BlueJava,\n            Banana.GreenPlantain,\n            Banana.YellowPlantain,\n            Banana.PisangRaja,\n        };\n        for (var i = 0; i < bananas.Length; i++)\n        {\n            var stream = new MemoryStream();\n            var writer = new BinaryWriter(stream);\n            var banana = bananas[i];\n            serializer.Write(writer, banana);\n\n            stream.Seek(0, SeekOrigin.Begin);\n            var tag = new BinaryReader(stream).ReadByte();\n            Assert.Equal(tag, i);\n\n            stream.Seek(0, SeekOrigin.Begin);\n            var newBanana = serializer.Read(new BinaryReader(stream));\n            Assert.Equal(banana, newBanana);\n        }\n    }\n\n    [Fact]\n    public static void GeneratedNestedListEqualsWorks()\n    {\n        var equalityComparer = new EnumerableEqualityComparer<IEnumerable<IEnumerable<BasicEnum>>>(\n            new EnumerableEqualityComparer<IEnumerable<BasicEnum>>(\n                new EnumerableEqualityComparer<BasicEnum>(EqualityComparer<BasicEnum>.Default)\n            )\n        );\n        CollisionCounter collisionCounter = new();\n        GenTwoContainsNestedList.Sample(\n            example =>\n            {\n                var equal = equalityComparer.Equals(example.e1.TheList, example.e2.TheList);\n\n                if (equal)\n                {\n                    Assert.Equal(example.e1, example.e2);\n                    Assert.True(example.e1 == example.e2);\n                    Assert.False(example.e1 != example.e2);\n                    Assert.Equal(example.e1.ToString(), example.e2.ToString());\n                    Assert.Equal(example.e1.GetHashCode(), example.e2.GetHashCode());\n                }\n                else\n                {\n                    Assert.NotEqual(example.e1, example.e2);\n                    Assert.False(example.e1 == example.e2);\n                    Assert.True(example.e1 != example.e2);\n                    Assert.NotEqual(example.e1.ToString(), example.e2.ToString());\n                    collisionCounter.Add(example.e1.GetHashCode() == example.e2.GetHashCode());\n                }\n            },\n            iter: 10_000\n        );\n        collisionCounter.AssertCollisionsLessThan(0.05);\n    }\n\n    [Fact]\n    public static void GeneratedToString()\n    {\n        Assert.Equal(\"\\\"\\\"\", BSATN.StringUtil.GenericToString(\"\"));\n        Assert.Equal(\"null\", BSATN.StringUtil.GenericToString(null));\n        Assert.Equal(\"[ ]\", BSATN.StringUtil.GenericToString(new List<int?> { }));\n        Assert.Equal(\n            \"[ null, null, 3 ]\",\n            BSATN.StringUtil.GenericToString(new List<int?> { null, null, 3 })\n        );\n        Assert.Equal(\n            \"[ null, null, \\\"hi\\\" ]\",\n            BSATN.StringUtil.GenericToString(new List<string?> { null, null, \"hi\" })\n        );\n        Assert.Equal(\n            \"[ null, null, X(1) ]\",\n            BSATN.StringUtil.GenericToString(\n                new List<BasicEnum?> { null, null, new BasicEnum.X(1) }\n            )\n        );\n        Assert.Equal(\n            \"[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]\",\n            BSATN.StringUtil.GenericToString(\n                new List<int?> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }\n            )\n        );\n        Assert.Equal(\n            \"[ 0, 1, 2, 3, 4, 5, 6, 7, ..., 9, 10, 11, 12, 13, 14, 15, 16 ]\",\n            BSATN.StringUtil.GenericToString(\n                new List<int?> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }\n            )\n        );\n        Assert.Equal(\n            \"[ 0, 1, 2, 3, 4, 5, 6, 7, ..., 10, 11, 12, 13, 14, 15, 16, 17 ]\",\n            BSATN.StringUtil.GenericToString(\n                new List<int?> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }\n            )\n        );\n        Assert.Equal(\"X(1)\", new BasicEnum.X(1).ToString());\n        Assert.Equal(\"Y(\\\"hi\\\")\", new BasicEnum.Y(\"hi\").ToString());\n        Assert.Equal(\"Y(null)\", new BasicEnum.Y(null!).ToString());\n        Assert.Equal(\"Z(1)\", new BasicEnum.Z(1).ToString());\n        Assert.Equal(\"Z(null)\", new BasicEnum.Z(null).ToString());\n        Assert.Equal(\"T(null)\", new BasicEnum.T(null).ToString());\n        Assert.Equal(\"T(\\\"\\\")\", new BasicEnum.T(\"\").ToString());\n        // There is unfortunately some stuttering if the variant and the stored data have the same name. Shrug.\n        Assert.Equal(\n            \"U(BasicDataClass { X = 1, Y = \\\"hi\\\", Z = null, W = null })\",\n            new BasicEnum.U(new BasicDataClass((1, \"hi\", null, null))).ToString()\n        );\n        Assert.Equal(\n            \"V(BasicDataStruct { X = 1, Y = \\\"hi\\\", Z = null, W = null })\",\n            new BasicEnum.V(new BasicDataStruct((1, \"hi\", null, null))).ToString()\n        );\n        Assert.Equal(\n            \"W(BasicDataRecord { X = 1, Y = \\\"hi\\\", Z = null, W = null })\",\n            new BasicEnum.W(new BasicDataRecord((1, \"hi\", null, null))).ToString()\n        );\n        Assert.Equal(\n            \"ContainsList { TheList = [ X(1), Y(\\\"hi\\\"), W(BasicDataRecord { X = 1, Y = \\\"hi\\\", Z = null, W = null }) ] }\",\n            new ContainsList(\n                [\n                    new BasicEnum.X(1),\n                    new BasicEnum.Y(\"hi\"),\n                    new BasicEnum.W(new BasicDataRecord((1, \"hi\", null, null))),\n                ]\n            ).ToString()\n        );\n#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.\n        Assert.Equal(\n            \"ContainsNestedList { TheList = [ [ [ X(1), null ], null ], null ] }\",\n            new ContainsNestedList(\n                [\n                    [\n                        [new BasicEnum.X(1), null],\n                        null,\n                    ],\n                    null,\n                ]\n            ).ToString()\n        );\n#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.\n    }\n\n    [Fact]\n    public static void NonNullableStringSerializationRejectsNull()\n    {\n        var stream = new MemoryStream();\n        var writer = new BinaryWriter(stream);\n\n        var serializer = new BSATN.String();\n        var ex = Assert.Throws<ArgumentNullException>(() => serializer.Write(writer, null!));\n        Assert.Contains(\"nullable string\", ex.Message, StringComparison.OrdinalIgnoreCase);\n    }\n\n    [Fact]\n    public static void NullableStringOptionRoundTripsNull()\n    {\n        var stream = new MemoryStream();\n        var writer = new BinaryWriter(stream);\n        var serializer = new BSATN.RefOption<string, BSATN.String>();\n\n        serializer.Write(writer, null);\n\n        stream.Seek(0, SeekOrigin.Begin);\n        var reader = new BinaryReader(stream);\n        var value = serializer.Read(reader);\n        Assert.Null(value);\n    }\n\n    [Type]\n    partial struct ContainsEnum\n    {\n        public Banana TheBanana;\n        public int BananaCount;\n    }\n\n    static readonly Gen<(Banana, int)> GenContainsEnum = Gen.Select(\n        Gen.Enum<Banana>(),\n        Gen.Int[0, 3]\n    );\n    static readonly Gen<((Banana, int), (Banana, int))> GenTwoContainsEnum = Gen.Select(\n        GenContainsEnum,\n        GenContainsEnum\n    );\n\n    [Fact]\n    public static void GeneratedEnumEqualsWorks()\n    {\n        GenTwoContainsEnum.Sample(\n            example =>\n            {\n                var ((b1, c1), (b2, c2)) = example;\n                var struct1 = new ContainsEnum { TheBanana = b1, BananaCount = c1 };\n                var struct2 = new ContainsEnum { TheBanana = b2, BananaCount = c2 };\n\n                if ((b1, c1) == (b2, c2))\n                {\n                    Assert.True(struct1.Equals(struct2));\n                    Assert.Equal(struct1, struct2);\n                }\n                else\n                {\n                    Assert.False(struct1.Equals(struct2));\n                    Assert.NotEqual(struct1, struct2);\n                }\n            },\n            iter: 10_000\n        );\n    }\n\n    [Fact]\n    public static void UUidRoundTrip()\n    {\n        var u1 = Uuid.NIL;\n        var s = u1.ToString();\n        var u2 = Uuid.Parse(s);\n        Assert.Equal(u1, u2);\n        Assert.Equal(u1.ToGuid(), u2.ToGuid());\n        Assert.Equal(s, u2.ToString());\n    }\n\n    [Fact]\n    public static void UuidToString()\n    {\n        foreach (\n            var uuid in new[]\n            {\n                Uuid.NIL,\n                new Uuid(new U128(0x0102030405060708UL, 0x090A0B0C0D0E0F10UL)),\n                Uuid.MAX,\n            }\n        )\n        {\n            var s = uuid.ToString();\n            var uuid2 = Uuid.Parse(s);\n            Assert.Equal(uuid, uuid2);\n            var g = new Guid(s);\n            Assert.Equal(s, g.ToString()); // same canonical form\n        }\n    }\n\n    [Fact]\n    public static void WrapAround()\n    {\n        // Check wraparound behavior\n        var counter = int.MaxValue;\n        var ts = Timestamp.UNIX_EPOCH;\n\n        _ = Uuid.FromCounterV7(ref counter, ts, new byte[4]);\n\n        Assert.Equal(0, counter);\n    }\n\n    [Fact]\n    public static void NegativeTimestampThrows()\n    {\n        var counter = 0;\n        var ts = new Timestamp(-1);\n\n        var ex = Assert.Throws<ArgumentException>(\n            () => Uuid.FromCounterV7(ref counter, ts, new byte[4])\n        );\n\n        Assert.Contains(\"timestamp\", ex.Message, StringComparison.OrdinalIgnoreCase);\n    }\n\n    [Fact]\n    public static void UuidOrdered()\n    {\n        var u1 = new Uuid(new U128(1, 0));\n        var u2 = new Uuid(new U128(2, 0));\n\n        Assert.True(u1 < u2);\n        Assert.True(u2 > u1);\n        Assert.Equal(u1, u1);\n        Assert.NotEqual(u1, u2);\n\n        // Check we start from zero\n        var counter = 0;\n        var ts = Timestamp.UNIX_EPOCH;\n\n        var uStart = Uuid.FromCounterV7(ref counter, ts, new byte[4]);\n        Assert.Equal(0, uStart.GetCounter());\n\n        // Check ordering across many UUIDs\n        const int total = 10_000_000;\n        counter = int.MaxValue - total;\n\n        var uuids = Enumerable\n            .Range(0, 1000)\n            .Select(_ =>\n            {\n                var bytes = new byte[4];\n                RandomNumberGenerator.Fill(bytes);\n                return Uuid.FromCounterV7(ref counter, ts, bytes);\n            })\n            .ToList();\n\n        for (var i = 0; i < uuids.Count - 1; i++)\n        {\n            var a = uuids[i];\n            var b = uuids[i + 1];\n\n            Assert.Equal(Uuid.UuidVersion.V7, a.GetVersion());\n\n            Assert.True(a < b, $\"UUIDs are not ordered at {i}: {a} !< {b}\");\n            Assert.True(\n                a.GetCounter() < b.GetCounter(),\n                $\"UUID counters are not ordered at {i}: {a.GetCounter()} !< {b.GetCounter()}\"\n            );\n        }\n    }\n\n    [Fact]\n    public static void UuidVersion()\n    {\n        var u = Uuid.NIL;\n        Assert.Equal(Uuid.UuidVersion.Nil, u.GetVersion());\n\n        u = Uuid.MAX;\n        Assert.Equal(Uuid.UuidVersion.Max, u.GetVersion());\n\n        var randomBytes = new byte[16];\n        RandomNumberGenerator.Fill(randomBytes);\n        u = Uuid.FromRandomBytesV4(randomBytes);\n        Assert.Equal(Uuid.UuidVersion.V4, u.GetVersion());\n\n        var counter = 0;\n        u = Uuid.FromCounterV7(ref counter, Timestamp.UNIX_EPOCH, randomBytes.AsSpan()[..4]);\n        Assert.Equal(Uuid.UuidVersion.V7, u.GetVersion());\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen/Codegen.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <AssemblyName>SpacetimeDB.Codegen</AssemblyName>\n    <Version>2.0.4</Version>\n    <Title>SpacetimeDB Module Codegen</Title>\n    <Description>The SpacetimeDB Codegen implements the Roslyn incremental generators for writing SpacetimeDB modules in C#.</Description>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <!-- We're going to include this analyzer in the Runtime package instead of publishing separately. -->\n    <IsPackable>false</IsPackable>\n    <IsRoslynComponent>true</IsRoslynComponent>\n    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <RootNamespace>SpacetimeDB.Codegen</RootNamespace>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <!-- This analyzer uses some shared helpers from the BSATN.Codegen one, so include it as a regular dependency -->\n    <!-- (*not* with OutputItemType=analyzer because we don't want to use that source generator on source of Codegen itself) -->\n    <ProjectReference Include=\"../BSATN.Codegen/BSATN.Codegen.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.3.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- dev-dependency to add internal C# classes and attributes not included in .NET Standard -->\n    <PackageReference Include=\"PolySharp\" Version=\"1.14.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!-- reuse attribute and adjacent types from the runtime -->\n    <Compile Include=\"../Runtime/Attrs.cs\" />\n  </ItemGroup>\n\n  <!-- Local project references (used by SpacetimeDB/modules/*-cs) don't use NuGet resolution. -->\n  <!-- We need a different hack to include BSATN.Codegen as a transitive analyzer dependency. https://github.com/dotnet/roslyn/issues/47275#issuecomment-685482999 -->\n  <ItemGroup>\n    <TargetPathWithTargetPlatformMoniker\n      Include=\"$(ProjectDir)/$(OutputPath)/SpacetimeDB.BSATN.Codegen.dll\"\n      IncludeRuntimeDependency=\"false\"\n    />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen/Diag.cs",
    "content": "namespace SpacetimeDB.Codegen;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\ninternal static class ErrorDescriptor\n{\n    private static readonly ErrorDescriptorGroup group = new(\"STDB\", \"SpacetimeDB\");\n\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ReducerReturnType =\n        new(\n            group,\n            \"[SpacetimeDB.Reducer] methods must return void\",\n            method =>\n                $\"Reducer method {method.Identifier} returns {method.ReturnType} instead of void.\",\n            method => method.ReturnType\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> AutoIncNotInteger =\n        new(\n            group,\n            \"AutoInc fields must be of integer type\",\n            field =>\n                $\"Field {field.Name} is marked as AutoInc but it has a non-integer type {field.Type}.\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> UniqueNotEquatable =\n        new(\n            group,\n            \"Unique fields must be equatable\",\n            field =>\n                $\"Field {field.Name} is marked as Unique but it has a type {field.Type} which is not an equatable primitive.\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<AttributeData> EmptyIndexColumns =\n        new(\n            group,\n            \"Index attribute must specify Columns\",\n            _ => $\"Index attribute doesn't specify columns.\",\n            attr => attr\n        );\n\n    public static readonly ErrorDescriptor<TypeDeclarationSyntax> InvalidTableVisibility =\n        new(\n            group,\n            \"Table row visibility must be public or internal\",\n            table => $\"Table {table.Identifier} and its parent types must be public or internal.\",\n            table => table.Identifier\n        );\n\n    public static readonly ErrorDescriptor<TypeDeclarationSyntax> TableTaggedEnum =\n        new(\n            group,\n            \"Tables cannot be tagged enums\",\n            table => $\"Table {table.Identifier} is a tagged enum, which is not allowed.\",\n            table => table.BaseList!\n        );\n\n    public static readonly ErrorDescriptor<(\n        string kind,\n        string exportName,\n        IEnumerable<string> fullNames\n    )> DuplicateExport =\n        new(\n            group,\n            \"Duplicate exports\",\n            ctx =>\n                $\"{ctx.kind} with the same export name {ctx.exportName} is registered in multiple places: {string.Join(\", \", ctx.fullNames)}\",\n            ctx => Location.None\n        );\n\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ReducerContextParam =\n        new(\n            group,\n            \"Reducers must have a first argument of type ReducerContext\",\n            method =>\n                $\"Reducer method {method.Identifier} does not have a ReducerContext parameter.\",\n            method => method.ParameterList\n        );\n\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ProcedureContextParam =\n        new(\n            group,\n            \"Procedures must have a first argument of type ProcedureContext\",\n            method =>\n                $\"Procedure method {method.Identifier} does not have a ProcedureContext parameter.\",\n            method => method.ParameterList\n        );\n\n    public static readonly ErrorDescriptor<(\n        MethodDeclarationSyntax method,\n        string prefix\n    )> ReducerReservedPrefix =\n        new(\n            group,\n            \"Reducer method has a reserved name prefix\",\n            ctx =>\n                $\"Reducer method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.\",\n            ctx => ctx.method.Identifier\n        );\n\n    public static readonly ErrorDescriptor<(\n        MethodDeclarationSyntax method,\n        string prefix\n    )> ProcedureReservedPrefix =\n        new(\n            group,\n            \"Procedure method has a reserved name prefix\",\n            ctx =>\n                $\"Procedure method {ctx.method.Identifier} starts with '{ctx.prefix}', which is a reserved prefix.\",\n            ctx => ctx.method.Identifier\n        );\n\n    public static readonly UnusedErrorDescriptor IncompatibleTableSchedule = new(group);\n\n    public static readonly ErrorDescriptor<(\n        ReducerKind kind,\n        IEnumerable<string> fullNames\n    )> DuplicateSpecialReducer =\n        new(\n            group,\n            \"Multiple reducers of the same kind\",\n            ctx =>\n                $\"Several reducers are assigned to the same lifecycle kind {ctx.kind}: {string.Join(\", \", ctx.fullNames)}\",\n            ctx => Location.None\n        );\n\n    public static readonly ErrorDescriptor<(\n        AttributeData attr,\n        string message\n    )> InvalidScheduledDeclaration =\n        new(group, \"Invalid scheduled table declaration\", ctx => $\"{ctx.message}\", ctx => ctx.attr);\n\n    public static readonly ErrorDescriptor<AttributeData> UnexpectedIndexColumns =\n        new(\n            group,\n            \"Index attribute on a field must not specify Columns\",\n            _ =>\n                $\"Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter.\",\n            attr => attr\n        );\n\n    public static readonly ErrorDescriptor<(\n        AttributeData attr,\n        string columnName,\n        string typeName\n    )> UnknownColumn =\n        new(\n            group,\n            \"Unknown column\",\n            ctx => $\"Could not find the specified column {ctx.columnName} in {ctx.typeName}.\",\n            ctx => ctx.attr\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> ClientVisibilityNotFilter =\n        new(\n            group,\n            \"ClientVisibilityFilters must be Filters\",\n            field =>\n                $\"Field {field.Name} is marked as ClientVisibilityFilter but it has type {field.Type} which is not SpacetimeDB.Filter\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> ClientVisibilityNotPublicStaticReadonly =\n        new(\n            group,\n            \"ClientVisibilityFilters must be public static readonly\",\n            field =>\n                $\"Field {field.Name} is marked as [ClientVisibilityFilter] but it is not public static readonly\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> IncompatibleDefaultAttributesCombination =\n        new(\n            group,\n            \"Invalid Combination: AutoInc, Unique or PrimaryKey cannot have a Default value\",\n            field =>\n                $\"Field {field.Name} contains a default value and has a AutoInc, Unique or PrimaryKey attributes, which is not allowed.\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> InvalidDefaultValueType =\n        new(\n            group,\n            \"Invalid Default Value Type\",\n            field => $\"Default value for field {field.Name} cannot be converted to provided type\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> InvalidDefaultValueFormat =\n        new(\n            group,\n            \"Invalid Default Value Format\",\n            field => $\"Default value for field {field.Name} has invalid format for provided type \",\n            field => field\n        );\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewContextParam =\n        new(\n            group,\n            \"Views must start with ViewContext or AnonymousViewContext\",\n            method =>\n                $\"View method {method.Identifier} must have a first parameter of type ViewContext or AnonymousViewContext.\",\n            method => method.ParameterList\n        );\n\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewMustHaveName =\n        new(\n            group,\n            \"Views must have an explicit name.\",\n            method => $\"View '{method.Identifier}' must have an explicit name.\",\n            method => method\n        );\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewInvalidReturn =\n        new(\n            group,\n            \"Views must return T?, List<T>, or IQuery<T>\",\n            method => $\"View '{method.Identifier}' must return T?, List<T>, or IQuery<T>\",\n            method => method.ReturnType\n        );\n\n    // TODO: Remove once Views support Private: Views must be Public currently\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewMustBePublic =\n        new(\n            group,\n            \"Views must be public\",\n            method =>\n                $\"View '{method.Identifier}' must have Public = true. Views are always public in SpacetimeDB.\",\n            method => method\n        );\n\n    // TODO: Remove once Views support arguments: Views must have no arguments beyond the context.\n    public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewArgsUnsupported =\n        new(\n            group,\n            \"Views must have no arguments beyond the context.\",\n            method =>\n                $\"View '{method.Identifier}' must have no arguments beyond the context. This is a temporary limitation.\",\n            method => method\n        );\n\n    public static readonly ErrorDescriptor<IFieldSymbol> SettingsMustBeConstCaseConversionPolicy =\n        new(\n            group,\n            \"[SpacetimeDB.Settings] field must be a const CaseConversionPolicy\",\n            field =>\n                $\"Settings field {field.Name} must be declared as 'public const SpacetimeDB.CaseConversionPolicy ...'.\",\n            field => field\n        );\n\n    public static readonly ErrorDescriptor<IEnumerable<string>> DuplicateSettings =\n        new(\n            group,\n            \"Multiple [SpacetimeDB.Settings] declarations\",\n            fullNames =>\n                $\"[SpacetimeDB.Settings] is declared multiple times: {string.Join(\", \", fullNames)}\",\n            _ => Location.None\n        );\n\n    public static readonly ErrorDescriptor<AttributeData> TableLevelIndexMissingAccessor =\n        new(\n            group,\n            \"Table-level index attributes must specify Accessor\",\n            _ =>\n                $\"Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.\",\n            attr => attr\n        );\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen/Module.cs",
    "content": "namespace SpacetimeDB.Codegen;\n\nusing System;\nusing System.Collections.Immutable;\nusing System.Linq;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing SpacetimeDB.Internal;\nusing static Utils;\n\n/// <summary>\n/// Represents column attributes parsed from field attributes in table classes.\n/// Used to track metadata like primary keys, unique constraints, and default values.\n/// </summary>\n/// <param name=\"Mask\">Bitmask representing the column attributes (PrimaryKey, Unique, etc.)</param>\n/// <param name=\"Table\">Optional table name if the attribute is table-specific</param>\n/// <param name=\"Value\">Optional value for attributes like Default that carry additional data</param>\nreadonly record struct ColumnAttr(ColumnAttrs Mask, string? Table = null, string? Value = null)\n{\n    // Maps attribute type names to their corresponding attribute types\n    private static readonly ImmutableDictionary<string, System.Type> AttrTypes = ImmutableArray\n        .Create(\n            typeof(AutoIncAttribute),\n            typeof(PrimaryKeyAttribute),\n            typeof(UniqueAttribute),\n            typeof(DefaultAttribute)\n        )\n        .ToImmutableDictionary(t => t.FullName!);\n\n    /// <summary>\n    /// Parses a Roslyn AttributeData into a ColumnAttr instance.\n    /// </summary>\n    /// <param name=\"attrData\">The attribute data to parse</param>\n    /// <returns>A ColumnAttr instance representing the parsed attribute, or default if the attribute type is not recognized</returns>\n    public static ColumnAttr Parse(AttributeData attrData)\n    {\n        if (\n            attrData.AttributeClass is not { } attrClass\n            || !AttrTypes.TryGetValue(attrClass.ToString(), out var attrType)\n        )\n        {\n            return default;\n        }\n\n        // Special handling for DefaultAttribute as it contains an additional value\n        if (attrClass.ToString() == typeof(DefaultAttribute).FullName)\n        {\n            var defaultAttr = attrData.ParseAs<DefaultAttribute>(attrType);\n            return new(defaultAttr.Mask, defaultAttr.Table, defaultAttr.Value);\n        }\n\n        // Handle standard column attributes (PrimaryKey, Unique, AutoInc)\n        var attr = attrData.ParseAs<ColumnAttribute>(attrType);\n        return new(attr.Mask, attr.Table);\n    }\n}\n\nrecord SettingsDeclaration\n{\n    public readonly string FullName;\n    public readonly string? CaseConversionPolicy;\n\n    private static readonly string[] CaseConversionPolicyTypeNames =\n    [\n        \"SpacetimeDB.CaseConversionPolicy\",\n        \"SpacetimeDB.Internal.CaseConversionPolicy\", // backward compat\n    ];\n\n    public SettingsDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n    {\n        var fieldSymbol = (IFieldSymbol)context.TargetSymbol;\n        FullName = SymbolToName(fieldSymbol);\n\n        if (!fieldSymbol.IsConst)\n        {\n            diag.Report(ErrorDescriptor.SettingsMustBeConstCaseConversionPolicy, fieldSymbol);\n            return;\n        }\n        if (!CaseConversionPolicyTypeNames.Contains(fieldSymbol.Type.ToString()))\n        {\n            diag.Report(ErrorDescriptor.SettingsMustBeConstCaseConversionPolicy, fieldSymbol);\n            return;\n        }\n        if (fieldSymbol.ConstantValue is null)\n        {\n            diag.Report(ErrorDescriptor.SettingsMustBeConstCaseConversionPolicy, fieldSymbol);\n            return;\n        }\n\n        try\n        {\n            var n = Convert.ToInt32(fieldSymbol.ConstantValue);\n            CaseConversionPolicy = n switch\n            {\n                0 => \"None\",\n                1 => \"SnakeCase\",\n                2 => \"CamelCase\",\n                3 => \"PascalCase\",\n                _ => null,\n            };\n        }\n        catch\n        {\n            CaseConversionPolicy = null;\n        }\n\n        if (CaseConversionPolicy is null)\n        {\n            diag.Report(ErrorDescriptor.SettingsMustBeConstCaseConversionPolicy, fieldSymbol);\n        }\n    }\n}\n\n/// <summary>\n/// Represents a reference to a column in a table, combining its index and name.\n/// Used to maintain references to columns for indexing and querying purposes.\n/// </summary>\n/// <param name=\"Index\">The zero-based index of the column in the table</param>\n/// <param name=\"Name\">The name of the column as defined in the source code</param>\nrecord ColumnRef(int Index, string Name);\n\n/// <summary>\n/// Represents the declaration of a column in a table.\n/// Contains metadata and attributes for the column, including its type, constraints, and indexes.\n/// </summary>\nrecord ColumnDeclaration : MemberDeclaration\n{\n    public readonly EquatableArray<ColumnAttr> Attrs;\n    public readonly EquatableArray<TableIndex> Indexes;\n    public readonly bool IsEquatable;\n    public readonly string FullTableName;\n    public readonly int ColumnIndex;\n    public readonly string? ColumnDefaultValue;\n\n    // A helper to combine multiple column attributes into a single mask.\n    // Note: it doesn't check the table names, this is left up to the caller.\n    private static ColumnAttrs CombineColumnAttrs(IEnumerable<ColumnAttr> attrs) =>\n        attrs.Aggregate(ColumnAttrs.UnSet, (mask, attr) => mask | attr.Mask);\n\n    public ColumnDeclaration(string tableName, int index, IFieldSymbol field, DiagReporter diag)\n        : base(field, diag)\n    {\n        FullTableName = tableName;\n        ColumnIndex = index;\n\n        Attrs = new(\n            field\n                .GetAttributes()\n                .Select(ColumnAttr.Parse)\n                .Where(a => a.Mask != ColumnAttrs.UnSet)\n                .GroupBy(\n                    a => a.Table,\n                    (key, group) => new ColumnAttr(CombineColumnAttrs(group), key)\n                )\n                .ToImmutableArray()\n        );\n\n        Indexes = new(\n            field\n                .GetAttributes()\n                .Where(TableIndex.CanParse)\n                .Select(a => new TableIndex(new ColumnRef(index, field.Name), a, diag))\n                .ToImmutableArray()\n        );\n\n        ColumnDefaultValue = field\n            .GetAttributes()\n            .Select(ColumnAttr.Parse)\n            .Where(a => a.Mask == ColumnAttrs.Default)\n            .Select(a => a.Value)\n            .ToList()\n            .FirstOrDefault();\n\n        var type = field.Type;\n\n        var isInteger = type.SpecialType switch\n        {\n            SpecialType.System_Byte\n            or SpecialType.System_SByte\n            or SpecialType.System_Int16\n            or SpecialType.System_UInt16\n            or SpecialType.System_Int32\n            or SpecialType.System_UInt32\n            or SpecialType.System_Int64\n            or SpecialType.System_UInt64 => true,\n            SpecialType.None => type.ToString()\n                is \"System.Int128\"\n                    or \"System.UInt128\"\n                    or \"SpacetimeDB.I128\"\n                    or \"SpacetimeDB.U128\"\n                    or \"SpacetimeDB.I256\"\n                    or \"SpacetimeDB.U256\",\n            _ => false,\n        };\n\n        var attrs = CombineColumnAttrs(Attrs);\n\n        if (attrs.HasFlag(ColumnAttrs.AutoInc) && !isInteger)\n        {\n            diag.Report(ErrorDescriptor.AutoIncNotInteger, field);\n        }\n\n        // Check whether this is a sum type without a payload.\n        var isAllUnitEnum = false;\n        if (type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Enum)\n        {\n            isAllUnitEnum = true;\n        }\n        else if (type.BaseType?.OriginalDefinition.ToString() == \"SpacetimeDB.TaggedEnum<Variants>\")\n        {\n            if (\n                type.BaseType.TypeArguments.FirstOrDefault() is INamedTypeSymbol\n                {\n                    IsTupleType: true,\n                    TupleElements: var taggedEnumVariants\n                }\n            )\n            {\n                isAllUnitEnum = taggedEnumVariants.All(\n                    (field) => field.Type.ToString() == \"SpacetimeDB.Unit\"\n                );\n            }\n        }\n\n        IsEquatable =\n            (\n                isInteger\n                || isAllUnitEnum\n                || type.SpecialType switch\n                {\n                    SpecialType.System_String or SpecialType.System_Boolean => true,\n                    SpecialType.None => type.ToString()\n                        is \"SpacetimeDB.ConnectionId\"\n                            or \"SpacetimeDB.Identity\"\n                            or \"SpacetimeDB.Uuid\",\n                    _ => false,\n                }\n            )\n            && type.NullableAnnotation != NullableAnnotation.Annotated;\n\n        if (attrs.HasFlag(ColumnAttrs.Unique) && !IsEquatable)\n        {\n            diag.Report(ErrorDescriptor.UniqueNotEquatable, field);\n        }\n\n        if (\n            attrs.HasFlag(ColumnAttrs.Default)\n            && (\n                attrs.HasFlag(ColumnAttrs.AutoInc)\n                || attrs.HasFlag(ColumnAttrs.PrimaryKey)\n                || attrs.HasFlag(ColumnAttrs.Unique)\n            )\n        )\n        {\n            diag.Report(ErrorDescriptor.IncompatibleDefaultAttributesCombination, field);\n        }\n    }\n\n    public ColumnAttrs GetAttrs(TableAccessor tableAccessor) =>\n        CombineColumnAttrs(Attrs.Where(x => x.Table == null || x.Table == tableAccessor.Name));\n\n    // For the `TableDesc` constructor.\n    public string GenerateColumnDef() =>\n        $\"new (nameof({Identifier}), BSATN.{Identifier}{TypeUse.BsatnFieldSuffix}.GetAlgebraicType(registrar))\";\n}\n\nrecord Scheduled(string ReducerName, int ScheduledAtColumn);\n\nrecord TableAccessor\n{\n    public readonly string Name;\n    public readonly string? CanonicalName;\n    public readonly bool IsPublic;\n    public readonly bool IsEvent;\n    public readonly Scheduled? Scheduled;\n\n    public string Identifier => EscapeIdentifier(Name);\n\n    public TableAccessor(TableDeclaration table, AttributeData data, DiagReporter diag)\n    {\n        var attr = data.ParseAs<TableAttribute>();\n\n        Name = attr.Accessor ?? table.ShortName;\n        CanonicalName = attr.Name;\n        IsPublic = attr.Public;\n        IsEvent = attr.Event;\n        if (\n            attr.Scheduled is { } reducer\n            && table.GetColumnIndex(data, attr.ScheduledAt, diag) is { } scheduledAtIndex\n        )\n        {\n            try\n            {\n                Scheduled = new(reducer, scheduledAtIndex);\n                if (\n                    table.GetPrimaryKey(this) is not { } pk\n                    || table.Members[pk].Type.Name != \"ulong\"\n                )\n                {\n                    throw new InvalidOperationException(\n                        $\"{Name} is a scheduled table but doesn't have a primary key of type `ulong`.\"\n                    );\n                }\n                if (\n                    table.Members[Scheduled.ScheduledAtColumn].Type.Name != \"SpacetimeDB.ScheduleAt\"\n                )\n                {\n                    throw new InvalidOperationException(\n                        $\"{Name}.{attr.ScheduledAt} is marked with `ScheduledAt`, but doesn't have the expected type `SpacetimeDB.ScheduleAt`.\"\n                    );\n                }\n            }\n            catch (Exception e)\n            {\n                diag.Report(ErrorDescriptor.InvalidScheduledDeclaration, (data, e.Message));\n            }\n        }\n    }\n}\n\nenum TableIndexType\n{\n    BTree,\n}\n\n/// <summary>\n/// Represents an index on a database table accessor, used to optimize queries.\n/// Supports B-tree indexing (and potentially other types in the future).\n/// </summary>\nrecord TableIndex\n{\n    public readonly EquatableArray<ColumnRef> Columns;\n    public readonly string? Table;\n    public readonly string AccessorName;\n    public readonly string? CanonicalName;\n    public readonly TableIndexType Type;\n\n    public string AccessorIdentifier => EscapeIdentifier(AccessorName);\n\n    // See: bindings_sys::index_id_from_name for documentation of this format.\n    // Guaranteed not to contain quotes, so does not need to be escaped when embedded in a string.\n    private readonly string StandardNameSuffix;\n\n    /// <summary>\n    /// Primary constructor that initializes all fields.\n    /// Other constructors delegate to this one to avoid code duplication.\n    /// </summary>\n    /// <param name=\"accessorName\">Name to use when accessing this index. If null, will be generated from column names.</param>\n    /// <param name=\"canonicalName\">Explicit canonical name override for this index, if any.</param>\n    /// <param name=\"columns\">The columns that make up this index.</param>\n    /// <param name=\"tableName\">The name of the table this index belongs to, if any.</param>\n    /// <param name=\"type\">The type of index (currently only B-tree is supported).</param>\n    private TableIndex(\n        string? accessorName,\n        string? canonicalName,\n        ImmutableArray<ColumnRef> columns,\n        string? tableName,\n        TableIndexType type\n    )\n    {\n        Columns = new(columns);\n        Table = tableName;\n        var columnNames = string.Join(\"_\", columns.Select(c => c.Name));\n        AccessorName = accessorName ?? columnNames;\n        CanonicalName = canonicalName;\n        Type = type;\n        StandardNameSuffix = $\"_{columnNames}_idx_{Type.ToString().ToLower()}\";\n    }\n\n    /// <summary>\n    /// Creates a B-tree index on a single column with auto-generated name.\n    /// </summary>\n    /// <param name=\"col\">The column to index.</param>\n    public TableIndex(ColumnRef col)\n        : this(\n            null,\n            null,\n            ImmutableArray.Create(col),\n            null,\n            TableIndexType.BTree // this might become hash in the future\n        ) { }\n\n    /// <summary>\n    /// Creates an index with the given attribute and columns.\n    /// Used internally by other constructors that parse attributes.\n    /// </summary>\n    private TableIndex(\n        global::SpacetimeDB.Index.BTreeAttribute attr,\n        ImmutableArray<ColumnRef> columns\n    )\n        : this(attr.Accessor, attr.Name, columns, attr.Table, TableIndexType.BTree) { }\n\n    /// <summary>\n    /// Creates an index from a table declaration and attribute data.\n    /// Validates the index configuration and reports any errors through the diag reporter.\n    /// </summary>\n    private TableIndex(\n        TableDeclaration table,\n        global::SpacetimeDB.Index.BTreeAttribute attr,\n        AttributeData data,\n        DiagReporter diag\n    )\n        : this(\n            attr,\n            attr.Columns.Select(name => new ColumnRef(\n                    table.GetColumnIndex(data, name, diag) ?? -1,\n                    name\n                ))\n                .Where(c => c.Index != -1)\n                .ToImmutableArray()\n        )\n    {\n        if (string.IsNullOrWhiteSpace(attr.Accessor))\n        {\n            diag.Report(ErrorDescriptor.TableLevelIndexMissingAccessor, data);\n        }\n\n        if (attr.Columns.Length == 0)\n        {\n            diag.Report(ErrorDescriptor.EmptyIndexColumns, data);\n        }\n    }\n\n    /// <summary>\n    /// Creates an index by parsing attribute data from a table declaration.\n    /// </summary>\n    public TableIndex(TableDeclaration table, AttributeData data, DiagReporter diag)\n        : this(table, data.ParseAs<global::SpacetimeDB.Index.BTreeAttribute>(), data, diag) { }\n\n    /// <summary>\n    /// Creates an index for a single column with attribute data.\n    /// Validates that no additional columns were specified in the attribute.\n    /// </summary>\n    private TableIndex(\n        ColumnRef column,\n        global::SpacetimeDB.Index.BTreeAttribute attr,\n        AttributeData data,\n        DiagReporter diag\n    )\n        : this(attr, ImmutableArray.Create(column))\n    {\n        if (attr.Columns.Length != 0)\n        {\n            diag.Report(ErrorDescriptor.UnexpectedIndexColumns, data);\n        }\n    }\n\n    /// <summary>\n    /// Creates an index for a single column by parsing attribute data.\n    /// </summary>\n    public TableIndex(ColumnRef col, AttributeData data, DiagReporter diag)\n        : this(col, data.ParseAs<global::SpacetimeDB.Index.BTreeAttribute>(), data, diag) { }\n\n    // `FullName` and Roslyn have different ways of representing nested types in full names -\n    // one uses a `Parent+Child` syntax, the other uses `Parent.Child`.\n    // Manually fixup one to the other.\n    private static readonly string BTreeAttrName =\n        typeof(global::SpacetimeDB.Index.BTreeAttribute).FullName.Replace('+', '.');\n\n    public static bool CanParse(AttributeData data) =>\n        data.AttributeClass?.ToString() == BTreeAttrName;\n\n    public string GenerateIndexDef(TableAccessor tableAccessor) =>\n        $$\"\"\"\n            new(\n                SourceName: \"{{StandardIndexName(tableAccessor)}}\",\n                AccessorName: \"{{AccessorName}}\",\n                Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.{{Type}}([{{string.Join(\n                    \", \",\n                    Columns.Select(c => c.Index)\n                )}}])\n            )\n            \"\"\";\n\n    public string StandardIndexName(TableAccessor tableAccessor) =>\n        tableAccessor.Name + StandardNameSuffix;\n}\n\n/// <summary>\n/// Represents a table declaration in a module.\n/// Handles table metadata, accessors, indexes, and column declarations for code generation.\n/// </summary>\nrecord TableDeclaration : BaseTypeDeclaration<ColumnDeclaration>\n{\n    public readonly Accessibility Visibility;\n    public readonly EquatableArray<TableAccessor> TableAccessors;\n    public readonly EquatableArray<TableIndex> Indexes;\n\n    private readonly bool isRowStruct;\n\n    public int? GetColumnIndex(AttributeData attrContext, string name, DiagReporter diag)\n    {\n        var index = Members\n            .Select((col, i) => (col, i))\n            .FirstOrDefault(pair => pair.col.Name == name);\n        if (index.col is null)\n        {\n            diag.Report(ErrorDescriptor.UnknownColumn, (attrContext, name, ShortName));\n            return null;\n        }\n        return index.i;\n    }\n\n    public TableDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n        : base(context, diag)\n    {\n        var typeSyntax = (TypeDeclarationSyntax)context.TargetNode;\n\n        isRowStruct = ((INamedTypeSymbol)context.TargetSymbol).IsValueType;\n\n        if (Kind is TypeKind.Sum)\n        {\n            diag.Report(ErrorDescriptor.TableTaggedEnum, typeSyntax);\n        }\n\n        var container = context.TargetSymbol;\n        Visibility = container.DeclaredAccessibility;\n        while (container != null)\n        {\n            switch (container.DeclaredAccessibility)\n            {\n                case Accessibility.ProtectedAndInternal:\n                case Accessibility.NotApplicable:\n                case Accessibility.Internal:\n                case Accessibility.Public:\n                    if (Visibility < container.DeclaredAccessibility)\n                    {\n                        Visibility = container.DeclaredAccessibility;\n                    }\n                    break;\n                default:\n                    diag.Report(ErrorDescriptor.InvalidTableVisibility, typeSyntax);\n                    throw new Exception(\n                        \"Table row type visibility must be public or internal, including containing types.\"\n                    );\n            }\n\n            container = container.ContainingType;\n        }\n\n        TableAccessors = new(\n            context.Attributes.Select(a => new TableAccessor(this, a, diag)).ToImmutableArray()\n        );\n        Indexes = new(\n            context\n                .TargetSymbol.GetAttributes()\n                .Where(TableIndex.CanParse)\n                .Select(a => new TableIndex(this, a, diag))\n                .ToImmutableArray()\n        );\n    }\n\n    protected override ColumnDeclaration ConvertMember(\n        int index,\n        IFieldSymbol field,\n        DiagReporter diag\n    ) => new(FullName, index, field, diag);\n\n    public IEnumerable<string> GenerateTableAccessorFilters(TableAccessor tableAccessor)\n    {\n        var vis = SyntaxFacts.GetText(Visibility);\n        var globalName = $\"global::{FullName}\";\n\n        var uniqueIndexBase = isRowStruct ? \"UniqueIndex\" : \"RefUniqueIndex\";\n\n        foreach (var ct in GetConstraints(tableAccessor, ColumnAttrs.Unique))\n        {\n            var f = ct.Col;\n            if (!f.IsEquatable)\n            {\n                // Skip - we already emitted diagnostic for this during parsing, and generated code would\n                // only produce a lot of noisy typechecking errors.\n                continue;\n            }\n            var standardIndexName = ct.ToIndex().StandardIndexName(tableAccessor);\n            var updateMethod = ct.Attr.HasFlag(ColumnAttrs.PrimaryKey)\n                ? $\"public {globalName} Update({globalName} row) => DoUpdate(row);\"\n                : \"\";\n            yield return $$\"\"\"\n                {{vis}} sealed class {{f.Identifier}}UniqueIndex : {{uniqueIndexBase}}<{{tableAccessor.Identifier}}, {{globalName}}, {{f.Type.Name}}, {{f.Type.BSATNName}}> {\n                    internal {{f.Identifier}}UniqueIndex() : base(\"{{standardIndexName}}\") {}\n                    // Important: don't move this to the base class.\n                    // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n                    // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n                    public {{globalName}}? Find({{f.Type.Name}} key) => FindSingle(key);\n                    {{updateMethod}}\n                }\n                {{vis}} {{f.Identifier}}UniqueIndex {{f.Identifier}} => new();\n                \"\"\";\n        }\n\n        foreach (var index in GetIndexes(tableAccessor))\n        {\n            var name = index.AccessorName;\n            var identifierName = index.AccessorIdentifier;\n\n            // Skip bad declarations. Empty name means no columns, which we have already reported with a meaningful error.\n            // Emitting this will result in further compilation errors due to missing property name.\n            if (name == \"\")\n            {\n                continue;\n            }\n\n            var members = index.Columns.Select(c => Members[c.Index]).ToArray();\n            var standardIndexName = index.StandardIndexName(tableAccessor);\n\n            yield return $$\"\"\"\n                    {{vis}} sealed class {{identifierName}}Index() : SpacetimeDB.Internal.IndexBase<{{globalName}}>(\"{{standardIndexName}}\") {\n                \"\"\";\n\n            for (var n = 0; n < members.Length; n++)\n            {\n                var types = string.Join(\n                    \", \",\n                    members.Take(n + 1).Select(m => $\"{m.Type.Name}, {m.Type.BSATNName}\")\n                );\n                var scalars = members.Take(n).Select(m => $\"{m.Type.Name} {m.Identifier}\");\n                var lastScalar = $\"{members[n].Type.Name} {members[n].Identifier}\";\n                var lastBounds =\n                    $\"global::SpacetimeDB.Bound<{members[n].Type.Name}> {members[n].Identifier}\";\n                var argsScalar = string.Join(\", \", scalars.Append(lastScalar));\n                var argsBounds = string.Join(\", \", scalars.Append(lastBounds));\n                string argName;\n                if (n > 0)\n                {\n                    argName = \"f\";\n                    argsScalar = $\"({argsScalar}) f\";\n                    argsBounds = $\"({argsBounds}) f\";\n                }\n                else\n                {\n                    argName = members[0].Identifier;\n                }\n\n                yield return $$\"\"\"\n                        public IEnumerable<{{globalName}}> Filter({{argsScalar}}) =>\n                            DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<{{types}}>({{argName}}));\n\n                        public ulong Delete({{argsScalar}}) =>\n                            DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<{{types}}>({{argName}}));\n\n                        public IEnumerable<{{globalName}}> Filter({{argsBounds}}) =>\n                            DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<{{types}}>({{argName}}));\n\n                        public ulong Delete({{argsBounds}}) =>\n                            DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<{{types}}>({{argName}}));\n                    \n                    \"\"\";\n            }\n\n            yield return $\"}}\\n {vis} {identifierName}Index {identifierName} => new();\\n\";\n        }\n    }\n\n    private IEnumerable<string> GenerateReadOnlyAccessorFilters(TableAccessor tableAccessor)\n    {\n        var vis = SyntaxFacts.GetText(Visibility);\n        var globalName = $\"global::{FullName}\";\n\n        var uniqueIndexBase = isRowStruct\n            ? \"global::SpacetimeDB.Internal.ReadOnlyUniqueIndex\"\n            : \"global::SpacetimeDB.Internal.ReadOnlyRefUniqueIndex\";\n\n        foreach (var ct in GetConstraints(tableAccessor, ColumnAttrs.Unique))\n        {\n            var f = ct.Col;\n            if (!f.IsEquatable)\n            {\n                continue;\n            }\n\n            var standardIndexName = ct.ToIndex().StandardIndexName(tableAccessor);\n\n            yield return $$$\"\"\"\n                public sealed class {{{f.Identifier}}}Index\n                    : {{{uniqueIndexBase}}}<\n                          global::SpacetimeDB.Internal.ViewHandles.{{{tableAccessor.Identifier}}}ReadOnly,\n                          {{{globalName}}},\n                          {{{f.Type.Name}}},\n                          {{{f.Type.BSATNName}}}>\n                {\n                    internal {{{f.Identifier}}}Index() : base(\"{{{standardIndexName}}}\") { }\n\n                    public {{{globalName}}}? Find({{{f.Type.Name}}} key) => FindSingle(key);\n                }\n\n                public {{{f.Identifier}}}Index {{{f.Identifier}}} => new();\n                \"\"\";\n        }\n\n        foreach (var index in GetIndexes(tableAccessor))\n        {\n            if (string.IsNullOrEmpty(index.AccessorName))\n            {\n                continue;\n            }\n\n            var members = index.Columns.Select(c => Members[c.Index]).ToArray();\n            var standardIndexName = index.StandardIndexName(tableAccessor);\n            var name = index.AccessorName;\n            var identifierName = index.AccessorIdentifier;\n\n            var blocks = new List<string>\n            {\n                $$$\"\"\"\n                    public sealed class {{{identifierName}}}Index\n                    : global::SpacetimeDB.Internal.ReadOnlyIndexBase<{{{globalName}}}>\n                    {\n                    internal {{{identifierName}}}Index() : base(\"{{{standardIndexName}}}\") {}\n                    \"\"\",\n            };\n\n            for (var n = 0; n < members.Length; n++)\n            {\n                var declaringMembers = members.Take(n + 1).ToArray();\n                var types = string.Join(\n                    \", \",\n                    declaringMembers.Select(m => $\"{m.Type.Name}, {m.Type.BSATNName}\")\n                );\n                var scalarArgs = string.Join(\n                    \", \",\n                    declaringMembers.Select(m => $\"{m.Type.Name} {m.Identifier}\")\n                );\n                var boundsArgs = string.Join(\n                    \", \",\n                    declaringMembers\n                        .Take(n)\n                        .Select(m => $\"{m.Type.Name} {m.Identifier}\")\n                        .Append(\n                            $\"global::SpacetimeDB.Bound<{declaringMembers[^1].Type.Name}> {declaringMembers[^1].Identifier}\"\n                        )\n                );\n\n                var ctorArg = n == 0 ? declaringMembers[0].Identifier : \"f\";\n\n                if (n > 0)\n                {\n                    scalarArgs = $\"({scalarArgs}) f\";\n                    boundsArgs = $\"({boundsArgs}) f\";\n                }\n\n                blocks.Add(\n                    $$$\"\"\"\n                    public IEnumerable<{{{globalName}}}> Filter({{{scalarArgs}}}) =>\n                        DoFilter(new global::SpacetimeDB.Internal.BTreeIndexBounds<{{{types}}}>({{{ctorArg}}}));\n\n                    public IEnumerable<{{{globalName}}}> Filter({{{boundsArgs}}}) =>\n                        DoFilter(new global::SpacetimeDB.Internal.BTreeIndexBounds<{{{types}}}>({{{ctorArg}}}));\n                    \"\"\"\n                );\n            }\n\n            blocks.Add($\"}}\\n{vis} {identifierName}Index {identifierName} => new();\");\n            yield return string.Join(\"\\n\", blocks);\n        }\n    }\n\n    /// <summary>\n    /// Represents a generated accessor for a table, providing different access patterns\n    /// and visibility levels for the underlying table data.\n    /// </summary>\n    /// <param name=\"tableAccessorName\">Name of the generated accessor type</param>\n    /// <param name=\"tableName\">Fully qualified name of the table type</param>\n    /// <param name=\"tableAccessor\">C# source code for the accessor implementation</param>\n    /// <param name=\"getter\">C# property getter for accessing the accessor</param>\n    public record struct GeneratedTableAccessor(\n        string tableAccessorName,\n        string tableName,\n        string tableAccessor,\n        string getter\n    );\n\n    /// <summary>\n    /// Generates accessor implementations for all table accessors defined in this table declaration.\n    /// Each accessor represents a different way to access or filter the table's data.\n    /// </summary>\n    /// <returns>Collection of Accessor records containing generated code for each accessor</returns>\n    public IEnumerable<GeneratedTableAccessor> GenerateTableAccessors()\n    {\n        // Don't try to generate accessors if this table is a sum type.\n        // We already emitted a diagnostic, and attempting to generate accessors will only result in more noisy errors.\n        if (Kind is TypeKind.Sum)\n        {\n            yield break;\n        }\n        foreach (var v in TableAccessors)\n        {\n            var autoIncFields = Members.Where(m => m.GetAttrs(v).HasFlag(ColumnAttrs.AutoInc));\n\n            var globalName = $\"global::{FullName}\";\n            var accessorIdentifier = v.Identifier;\n            var iTable =\n                $\"global::SpacetimeDB.Internal.ITableView<{accessorIdentifier}, {globalName}>\";\n            yield return new(\n                v.Name,\n                globalName,\n                $$$\"\"\"\n            {{{SyntaxFacts.GetText(Visibility)}}} readonly struct {{{accessorIdentifier}}} : {{{iTable}}} {\n                public static {{{globalName}}} ReadGenFields(System.IO.BinaryReader reader, {{{globalName}}} row) {\n                    {{{string.Join(\n                        \"\\n\",\n                        autoIncFields.Select(m =>\n                            $$\"\"\"\n                            if (row.{{m.Identifier}} == default)\n                            {\n                                row.{{m.Identifier}} = {{globalName}}.BSATN.{{m.Identifier}}{{TypeUse.BsatnFieldSuffix}}.Read(reader);\n                            }\n                            \"\"\"\n                        )\n                    )}}}\n                    return row;\n                }\n\n                public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new (\n                    SourceName: nameof({{{accessorIdentifier}}}),\n                    ProductTypeRef: (uint) new {{{globalName}}}.BSATN().GetAlgebraicType(registrar).Ref_,\n                    PrimaryKey: [{{{GetPrimaryKey(v)?.ToString() ?? \"\"}}}],\n                    Indexes: [\n                        {{{string.Join(\n                            \",\\n\",\n                            GetConstraints(v, ColumnAttrs.Unique)\n                            .Select(c => c.ToIndex())\n                            .Concat(GetIndexes(v))\n                            .Select(b => b.GenerateIndexDef(v))\n                        )}}}\n                    ],\n                    Constraints: {{{GenConstraintList(v, ColumnAttrs.Unique, $\"{iTable}.MakeUniqueConstraint\")}}},\n                    Sequences: {{{GenConstraintList(v, ColumnAttrs.AutoInc, $\"{iTable}.MakeSequence\")}}},\n                    TableType: SpacetimeDB.Internal.TableType.User,\n                    TableAccess: SpacetimeDB.Internal.TableAccess.{{{(v.IsPublic ? \"Public\" : \"Private\")}}},\n                    DefaultValues: [],\n                    IsEvent: {{{(v.IsEvent ? \"true\" : \"false\")}}}\n                );\n\n                public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => {{{(\n                        v.Scheduled is { } scheduled\n                        ? $\"{iTable}.MakeSchedule(\\\"{scheduled.ReducerName}\\\", {scheduled.ScheduledAtColumn})\"\n                        : \"null\"\n                    )}}};\n\n                /// <summary>\n                /// Returns the number of rows in this table.\n                ///\n                /// This reads datastore metadata, so it runs in constant time.\n                /// It also takes into account modifications by the current transaction.\n                /// </summary>\n                public ulong Count => {{{iTable}}}.DoCount();\n                public IEnumerable<{{{globalName}}}> Iter() => {{{iTable}}}.DoIter();\n                public {{{globalName}}} Insert({{{globalName}}} row) => {{{iTable}}}.DoInsert(row);\n                public bool Delete({{{globalName}}} row) => {{{iTable}}}.DoDelete(row);\n\n                {{{string.Join(\"\\n\", GenerateTableAccessorFilters(v))}}}\n            }\n            \"\"\",\n                $\"{SyntaxFacts.GetText(Visibility)} global::SpacetimeDB.Internal.TableHandles.{accessorIdentifier} {accessorIdentifier} => new();\"\n            );\n        }\n    }\n\n    public record struct GeneratedReadOnlyAccessor(\n        string tableAccessorName,\n        string tableName,\n        string readOnlyAccessor,\n        string readOnlyGetter\n    );\n\n    public IEnumerable<GeneratedReadOnlyAccessor> GenerateReadOnlyAccessors()\n    {\n        if (Kind is TypeKind.Sum)\n        {\n            yield break;\n        }\n\n        foreach (var accessor in TableAccessors)\n        {\n            var globalName = $\"global::{FullName}\";\n            var accessorIdentifier = accessor.Identifier;\n\n            var readOnlyIndexDecls = string.Join(\"\\n\", GenerateReadOnlyAccessorFilters(accessor));\n            var visibility = SyntaxFacts.GetText(Visibility);\n            yield return new(\n                accessor.Name,\n                globalName,\n                $$$\"\"\"\n                {{{visibility}}} sealed class {{{accessorIdentifier}}}ReadOnly\n                    : global::SpacetimeDB.Internal.ReadOnlyTableView<{{{globalName}}}>\n                {\n                    internal {{{accessorIdentifier}}}ReadOnly() : base(\"{{{accessor.Name}}}\") { }\n\n                    /// <summary>\n                    /// Returns the number of rows in this table.\n                    ///\n                    /// This reads datastore metadata, so it runs in constant time.\n                    /// It also takes into account modifications by the current transaction.\n                    /// </summary>\n                    public ulong Count => DoCount();\n\n                    {{{readOnlyIndexDecls}}}\n                }\n                \"\"\",\n                $\"{visibility} global::SpacetimeDB.Internal.ViewHandles.{accessorIdentifier}ReadOnly {accessorIdentifier} => new();\"\n            );\n        }\n    }\n\n    public IEnumerable<string> GenerateQueryBuilderMembers()\n    {\n        if (Kind is TypeKind.Sum)\n        {\n            yield break;\n        }\n\n        var vis = SyntaxFacts.GetText(Visibility);\n        var globalRowName = $\"global::{FullName}\";\n\n        foreach (var accessor in TableAccessors)\n        {\n            var accessorIdentifier = accessor.Identifier;\n            var tableName = accessor.Name;\n            var colsTypeName = $\"{accessorIdentifier}Cols\";\n            var ixColsTypeName = $\"{accessorIdentifier}IxCols\";\n\n            string ColDecl(ColumnDeclaration col)\n            {\n                var typeName = col.Type.Name;\n                var isNullable = typeName.EndsWith(\"?\", StringComparison.Ordinal);\n                var valueTypeName = isNullable ? typeName[..^1] : typeName;\n                var colType = isNullable ? \"global::SpacetimeDB.Col\" : \"global::SpacetimeDB.Col\";\n                return $\"public readonly {colType}<{globalRowName}, {valueTypeName}> {col.Identifier};\";\n            }\n\n            string ColInit(ColumnDeclaration col)\n            {\n                var typeName = col.Type.Name;\n                var isNullable = typeName.EndsWith(\"?\", StringComparison.Ordinal);\n                var valueTypeName = isNullable ? typeName[..^1] : typeName;\n                var colType = isNullable ? \"global::SpacetimeDB.Col\" : \"global::SpacetimeDB.Col\";\n                return $\"{col.Identifier} = new {colType}<{globalRowName}, {valueTypeName}>(tableName, \\\"{col.Name}\\\");\";\n            }\n\n            var colsDecls = string.Join(\"\\n        \", Members.Select(ColDecl));\n            var colsInits = string.Join(\"\\n            \", Members.Select(ColInit));\n\n            var ixPositions = new global::System.Collections.Generic.HashSet<int>();\n            foreach (var c in GetConstraints(accessor, ColumnAttrs.PrimaryKey | ColumnAttrs.Unique))\n            {\n                ixPositions.Add(c.Pos);\n            }\n\n            foreach (var ix in GetIndexes(accessor))\n            {\n                foreach (var colRef in ix.Columns.Array)\n                {\n                    ixPositions.Add(colRef.Index);\n                }\n            }\n\n            var ixMembers = Members\n                .Select((m, i) => (m, i))\n                .Where(pair => ixPositions.Contains(pair.i))\n                .Select(pair => pair.m)\n                .ToArray();\n\n            string IxColDecl(ColumnDeclaration col)\n            {\n                var typeName = col.Type.Name;\n                var isNullable = typeName.EndsWith(\"?\", StringComparison.Ordinal);\n                var valueTypeName = isNullable ? typeName[..^1] : typeName;\n                var colType = isNullable\n                    ? \"global::SpacetimeDB.IxCol\"\n                    : \"global::SpacetimeDB.IxCol\";\n                return $\"public readonly {colType}<{globalRowName}, {valueTypeName}> {col.Identifier};\";\n            }\n\n            string IxColInit(ColumnDeclaration col)\n            {\n                var typeName = col.Type.Name;\n                var isNullable = typeName.EndsWith(\"?\", StringComparison.Ordinal);\n                var valueTypeName = isNullable ? typeName[..^1] : typeName;\n                var colType = isNullable\n                    ? \"global::SpacetimeDB.IxCol\"\n                    : \"global::SpacetimeDB.IxCol\";\n                return $\"{col.Identifier} = new {colType}<{globalRowName}, {valueTypeName}>(tableName, \\\"{col.Name}\\\");\";\n            }\n\n            var ixColsDecls = string.Join(\"\\n        \", ixMembers.Select(IxColDecl));\n            var ixColsInits = string.Join(\"\\n            \", ixMembers.Select(IxColInit));\n\n            yield return $$\"\"\"\n                {{vis}} readonly struct {{colsTypeName}}\n                {\n                    {{colsDecls}}\n\n                    internal {{colsTypeName}}(string tableName)\n                    {\n                        {{colsInits}}\n                    }\n                }\n\n                {{vis}} readonly struct {{ixColsTypeName}}\n                {\n                    {{ixColsDecls}}\n\n                    internal {{ixColsTypeName}}(string tableName)\n                    {\n                        {{ixColsInits}}\n                    }\n                }\n\n                public readonly partial struct QueryBuilder\n                {\n                    {{vis}} global::SpacetimeDB.Table<{{globalRowName}}, {{colsTypeName}}, {{ixColsTypeName}}> {{accessorIdentifier}}() =>\n                        new(\"{{tableName}}\", new {{colsTypeName}}(\"{{tableName}}\"), new {{ixColsTypeName}}(\"{{tableName}}\"));\n                }\n                \"\"\";\n        }\n    }\n\n    /// <summary>\n    /// Represents a default value for a table field, used during table creation.\n    /// </summary>\n    /// <param name=\"tableName\">Name of the table containing the field</param>\n    /// <param name=\"columnId\">Index of the column in the table</param>\n    /// <param name=\"value\">String representation of the default value</param>\n    /// <param name=\"BSATNTypeName\">BSATN Type name of the default value</param>\n    public record struct FieldDefaultValue(\n        string tableName,\n        string columnId,\n        string value,\n        string BSATNTypeName\n    );\n\n    /// <summary>\n    /// Generates default values for table fields with the [Default] attribute.\n    /// These values are used when creating new rows without explicit values for the corresponding fields.\n    /// </summary>\n    /// <returns>Collection of default values for fields that specify them</returns>\n    public IEnumerable<FieldDefaultValue> GenerateDefaultValues()\n    {\n        if (Kind is TypeKind.Sum)\n        {\n            yield break;\n        }\n\n        foreach (var tableAccessor in TableAccessors)\n        {\n            var members = string.Join(\", \", Members.Select(m => m.Name));\n            var fieldsWithDefaultValues = Members.Where(m =>\n                m.GetAttrs(tableAccessor).HasFlag(ColumnAttrs.Default)\n            );\n            var defaultValueAttributes = string.Join(\n                \", \",\n                Members\n                    .Where(m => m.GetAttrs(tableAccessor).HasFlag(ColumnAttrs.Default))\n                    .Select(m => m.Attrs.FirstOrDefault(a => a.Mask == ColumnAttrs.Default))\n            );\n\n            var withDefaultValues =\n                fieldsWithDefaultValues as ColumnDeclaration[] ?? fieldsWithDefaultValues.ToArray();\n            foreach (var fieldsWithDefaultValue in withDefaultValues)\n            {\n                if (\n                    fieldsWithDefaultValue.ColumnDefaultValue != null\n                    && fieldsWithDefaultValue.Type.BSATNName != \"\"\n                )\n                {\n                    // For enums, we'll need to wrap the default value in the enum type.\n                    if (fieldsWithDefaultValue.Type.BSATNName.StartsWith(\"SpacetimeDB.BSATN.Enum\"))\n                    {\n                        yield return new FieldDefaultValue(\n                            tableAccessor.Name,\n                            fieldsWithDefaultValue.ColumnIndex.ToString(),\n                            $\"({fieldsWithDefaultValue.Type.Name}){fieldsWithDefaultValue.ColumnDefaultValue}\",\n                            fieldsWithDefaultValue.Type.BSATNName\n                        );\n                    }\n                    else\n                    {\n                        yield return new FieldDefaultValue(\n                            tableAccessor.Name,\n                            fieldsWithDefaultValue.ColumnIndex.ToString(),\n                            fieldsWithDefaultValue.ColumnDefaultValue,\n                            fieldsWithDefaultValue.Type.BSATNName\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    public record Constraint(ColumnDeclaration Col, int Pos, ColumnAttrs Attr)\n    {\n        public TableIndex ToIndex() => new(new ColumnRef(Pos, Col.Name));\n    }\n\n    public IEnumerable<Constraint> GetConstraints(\n        TableAccessor tableAccessor,\n        ColumnAttrs filterByAttr = ~ColumnAttrs.UnSet\n    ) =>\n        Members\n            // Important: the position must be stored here, before filtering.\n            .Select((col, pos) => new Constraint(col, pos, col.GetAttrs(tableAccessor)))\n            .Where(c => c.Attr.HasFlag(filterByAttr));\n\n    public IEnumerable<TableIndex> GetIndexes(TableAccessor tableAccessor) =>\n        Indexes\n            .Concat(Members.SelectMany(m => m.Indexes))\n            .Where(i => i.Table == null || i.Table == tableAccessor.Name);\n\n    // Reimplementation of V8 -> V9 constraint conversion in Rust.\n    // See https://github.com/clockworklabs/SpacetimeDB/blob/13a800e9f88cbe885b98eab9e45b0fcfd3ab7014/crates/schema/src/def/validate/v8.rs#L74-L78\n    // and https://github.com/clockworklabs/SpacetimeDB/blob/13a800e9f88cbe885b98eab9e45b0fcfd3ab7014/crates/lib/src/db/raw_def/v8.rs#L460-L510\n    private string GenConstraintList(\n        TableAccessor tableAccessor,\n        ColumnAttrs filterByAttr,\n        string makeConstraintFn\n    ) =>\n        $$\"\"\"\n        [\n            {{string.Join(\n                \",\\n\",\n                GetConstraints(tableAccessor, filterByAttr)\n                    .Select(pair => $\"{makeConstraintFn}({pair.Pos})\")\n            )}}\n        ]\n        \"\"\";\n\n    internal int? GetPrimaryKey(TableAccessor tableAccessor) =>\n        GetConstraints(tableAccessor, ColumnAttrs.PrimaryKey)\n            .Select(c => (int?)c.Pos)\n            .SingleOrDefault();\n}\n\n/// <summary>\n/// Represents a view method declaration in a module.\n/// </summary>\nrecord ViewDeclaration\n{\n    public readonly string Name;\n    public readonly string? CanonicalName;\n    public readonly string FullName;\n    public readonly bool IsAnonymous;\n    public readonly bool IsPublic;\n    public readonly bool ReturnsQuery;\n    public readonly TypeUse ReturnType;\n    public readonly TypeUse? QueryRowType;\n    public readonly EquatableArray<MemberDeclaration> Parameters;\n    public readonly Scope Scope;\n\n    public ViewDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n    {\n        var methodSyntax = (MethodDeclarationSyntax)context.TargetNode;\n        var method = (IMethodSymbol)context.TargetSymbol;\n        var attr = context.Attributes.Single().ParseAs<ViewAttribute>();\n        var hasContextParam = method.Parameters.Length > 0;\n        var firstParamType = hasContextParam ? method.Parameters[0].Type : null;\n        var isAnonymousContext = firstParamType?.Name == \"AnonymousViewContext\";\n        var hasArguments = method.Parameters.Length > 1;\n\n        if (string.IsNullOrEmpty(attr.Accessor))\n        {\n            diag.Report(ErrorDescriptor.ViewMustHaveName, methodSyntax);\n        }\n        // TODO: Remove once Views support Private: Views must be Public currently\n        if (!attr.Public)\n        {\n            diag.Report(ErrorDescriptor.ViewMustBePublic, methodSyntax);\n        }\n        if (hasArguments)\n        {\n            diag.Report(ErrorDescriptor.ViewArgsUnsupported, methodSyntax);\n        }\n\n        Name = attr.Accessor ?? method.Name;\n        CanonicalName = attr.Name;\n        FullName = SymbolToName(method);\n        IsPublic = attr.Public;\n        IsAnonymous = isAnonymousContext;\n\n        ReturnsQuery = false;\n        INamedTypeSymbol? iquery = null;\n        if (\n            method.ReturnType is INamedTypeSymbol\n            {\n                Name: \"IQuery\",\n                ContainingNamespace: { Name: \"SpacetimeDB\" },\n                TypeArguments: [var _]\n            } directIQuery\n        )\n        {\n            iquery = directIQuery;\n        }\n        else\n        {\n            iquery = method\n                .ReturnType.AllInterfaces.OfType<INamedTypeSymbol>()\n                .FirstOrDefault(i =>\n                    i\n                        is {\n                            Name: \"IQuery\",\n                            ContainingNamespace: { Name: \"SpacetimeDB\" },\n                            TypeArguments.Length: 1\n                        }\n                );\n        }\n\n        if (iquery is { TypeArguments: [var queryRowType] })\n        {\n            ReturnsQuery = true;\n            var rowType = TypeUse.Parse(method, queryRowType, diag);\n            QueryRowType = rowType;\n            ReturnType = rowType;\n        }\n        else\n        {\n            QueryRowType = null;\n            ReturnType = TypeUse.Parse(method, method.ReturnType, diag);\n        }\n        Scope = new Scope(methodSyntax.Parent as MemberDeclarationSyntax);\n\n        if (method.Parameters.Length == 0)\n        {\n            diag.Report(ErrorDescriptor.ViewContextParam, methodSyntax);\n        }\n        else if (\n            method.Parameters[0].Type\n            is not INamedTypeSymbol { Name: \"ViewContext\" or \"AnonymousViewContext\" }\n        )\n        {\n            diag.Report(ErrorDescriptor.ViewContextParam, methodSyntax);\n        }\n\n        // Validate return type: must be List<T>, T?, or IQuery<T>.\n        if (\n            !ReturnsQuery\n            && !ReturnType.BSATNName.Contains(\"SpacetimeDB.BSATN.ValueOption\")\n            && !ReturnType.BSATNName.Contains(\"SpacetimeDB.BSATN.RefOption\")\n            && !ReturnType.BSATNName.Contains(\"SpacetimeDB.BSATN.List\")\n        )\n        {\n            diag.Report(ErrorDescriptor.ViewInvalidReturn, methodSyntax);\n        }\n\n        Parameters = new(\n            method\n                .Parameters.Skip(1)\n                .Select(p => new MemberDeclaration(p, p.Type, diag))\n                .ToImmutableArray()\n        );\n    }\n\n    public string GenerateViewDef(uint Index)\n    {\n        var returnTypeExpr = ReturnsQuery\n            ? $\"global::SpacetimeDB.BSATN.AlgebraicType.MakeQueryBuilderProductType(new {QueryRowType!.BSATNName}().GetAlgebraicType(registrar))\"\n            : $\"new {ReturnType.BSATNName}().GetAlgebraicType(registrar)\";\n        return $$$\"\"\"\n            new global::SpacetimeDB.Internal.RawViewDefV10(\n                SourceName: \"{{{Name}}}\",\n                Index: {{{Index}}},\n                IsPublic: {{{IsPublic.ToString().ToLower()}}},\n                IsAnonymous: {{{IsAnonymous.ToString().ToLower()}}},\n                Params: [{{{MemberDeclaration.GenerateDefs(Parameters)}}}],\n                ReturnType: {{{returnTypeExpr}}}\n            );\n            \"\"\";\n    }\n\n    /// <summary>\n    /// Generates the class responsible for evaluating a view.\n    /// If this is an anonymous view, the index corresponds to the position of this dispatcher in the `viewDispatchers` list of `RegisterView`.\n    /// Otherwise it corresponds to the position of this dispatcher in the `anonymousViewDispatchers` list of `RegisterAnonymousView`.\n    /// </summary>\n    public string GenerateDispatcherClass(uint index)\n    {\n        var paramReads = string.Join(\n            \"\\n                        \",\n            Parameters.Select(p =>\n                $\"var {p.Identifier} = {p.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader);\"\n            )\n        );\n\n        var makeViewDefMethod = IsAnonymous ? \"MakeAnonymousViewDef\" : \"MakeViewDef\";\n\n        var interfaceName = IsAnonymous\n            ? \"global::SpacetimeDB.Internal.IAnonymousView\"\n            : \"global::SpacetimeDB.Internal.IView\";\n        var interfaceContext = IsAnonymous\n            ? \"global::SpacetimeDB.Internal.IAnonymousViewContext\"\n            : \"global::SpacetimeDB.Internal.IViewContext\";\n        var concreteContext = IsAnonymous\n            ? \"SpacetimeDB.AnonymousViewContext\"\n            : \"SpacetimeDB.ViewContext\";\n\n        var isOption =\n            ReturnType.BSATNName.Contains(\"SpacetimeDB.BSATN.ValueOption\")\n            || ReturnType.BSATNName.Contains(\"SpacetimeDB.BSATN.RefOption\");\n\n        var writeOutput =\n            ReturnsQuery\n                ? $$$\"\"\"\n                        var header = new global::SpacetimeDB.Internal.ViewResultHeader.RawSql(returnValue.ToSql());\n                        var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n                        using var output = new System.IO.MemoryStream();\n                        using var writer = new System.IO.BinaryWriter(output);\n                        headerRW.Write(writer, header);\n                        return output.ToArray();\n                    \"\"\"\n            : isOption\n                ? $$$\"\"\"\n                        var listSerializer = {{{ReturnType.BSATNName}}}.GetListSerializer();\n                        var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n                        var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n                        var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n                        using var output = new System.IO.MemoryStream();\n                        using var writer = new System.IO.BinaryWriter(output);\n                        headerRW.Write(writer, header);\n                        listSerializer.Write(writer, listValue);\n                        return output.ToArray();\n                    \"\"\"\n            : $$$\"\"\"\n                    {{{ReturnType.BSATNName}}} returnRW = new();\n                    var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n                    var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n                    using var output = new System.IO.MemoryStream();\n                    using var writer = new System.IO.BinaryWriter(output);\n                    headerRW.Write(writer, header);\n                    returnRW.Write(writer, returnValue);\n                    return output.ToArray();            \n                \"\"\";\n\n        var invocationArgs =\n            Parameters.Length == 0\n                ? \"\"\n                : \", \" + string.Join(\", \", Parameters.Select(p => p.Identifier));\n        return $$$\"\"\"\n            sealed class {{{Name}}}ViewDispatcher : {{{interfaceName}}} {\n                {{{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Parameters)}}}\n                \n                public SpacetimeDB.Internal.RawViewDefV10 {{{makeViewDefMethod}}}(SpacetimeDB.BSATN.ITypeRegistrar registrar)\n                    => {{{GenerateViewDef(index)}}}\n\n                public byte[] Invoke(\n                    System.IO.BinaryReader reader,\n                    {{{interfaceContext}}} ctx\n                ) {\n                    try {\n                        {{{paramReads}}}\n                        var returnValue = {{{FullName}}}(({{{concreteContext}}})ctx{{{invocationArgs}}});\n                        {{{writeOutput}}}\n                    } catch (System.Exception e) {\n                        global::SpacetimeDB.Log.Error(\"Error in view '{{{Name}}}': \" + e);\n                        throw;\n                    }\n                }\n            }\n            \"\"\";\n    }\n}\n\n/// <summary>\n/// Represents a reducer method declaration in a module.\n/// </summary>\nrecord ReducerDeclaration\n{\n    public readonly string Name;\n    public readonly string? CanonicalName;\n    public readonly ReducerKind Kind;\n    public readonly string FullName;\n    public readonly EquatableArray<MemberDeclaration> Args;\n    public readonly Scope Scope;\n    private readonly bool HasWrongSignature;\n\n    public string Identifier => EscapeIdentifier(Name);\n\n    public ReducerDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n    {\n        var methodSyntax = (MethodDeclarationSyntax)context.TargetNode;\n        var method = (IMethodSymbol)context.TargetSymbol;\n        var attr = context.Attributes.Single().ParseAs<ReducerAttribute>();\n\n        if (!method.ReturnsVoid)\n        {\n            diag.Report(ErrorDescriptor.ReducerReturnType, methodSyntax);\n        }\n\n        if (\n            method.Parameters.FirstOrDefault()?.Type\n            is not INamedTypeSymbol { Name: \"ReducerContext\" }\n        )\n        {\n            diag.Report(ErrorDescriptor.ReducerContextParam, methodSyntax);\n            HasWrongSignature = true;\n        }\n\n        Name = method.Name;\n        if (Name.Length >= 2)\n        {\n            var prefix = Name[..2];\n            if (prefix is \"__\" or \"on\" or \"On\")\n            {\n                diag.Report(ErrorDescriptor.ReducerReservedPrefix, (methodSyntax, prefix));\n            }\n        }\n\n        Kind = attr.Kind;\n        CanonicalName = attr.Name;\n        FullName = SymbolToName(method);\n        Args = new(\n            method\n                .Parameters.Skip(1)\n                .Select(p => new MemberDeclaration(p, p.Type, diag))\n                .ToImmutableArray()\n        );\n        Scope = new Scope(methodSyntax.Parent as MemberDeclarationSyntax);\n    }\n\n    public string GenerateClass()\n    {\n        var invocation = HasWrongSignature\n            ? \"throw new System.InvalidOperationException()\"\n            : $\"{FullName}({string.Join(\n                \", \",\n                Args.Select(a => $\"{a.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader)\")\n                    .Prepend(\"(SpacetimeDB.ReducerContext)ctx\")\n            )})\";\n\n        return $$\"\"\"\n             class {{Identifier}}: SpacetimeDB.Internal.IReducer {\n                 {{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args)}}\n\n                 public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new (\n                     SourceName: nameof({{Identifier}}),\n                     Params: [{{MemberDeclaration.GenerateDefs(Args)}}],\n                     Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                     OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                     ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n                 );\n\n                 public SpacetimeDB.Internal.Lifecycle? Lifecycle => {{Kind switch\n        {\n            ReducerKind.Init => \"SpacetimeDB.Internal.Lifecycle.Init\",\n            ReducerKind.ClientConnected => \"SpacetimeDB.Internal.Lifecycle.OnConnect\",\n            ReducerKind.ClientDisconnected => \"SpacetimeDB.Internal.Lifecycle.OnDisconnect\",\n            _ => \"null\"\n        }}};\n\n                 public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) {\n                     {{invocation}};\n                 }\n             }\n             \"\"\";\n    }\n\n    public Scope.Extensions GenerateSchedule()\n    {\n        var extensions = new Scope.Extensions(Scope, FullName);\n\n        // Mark the API as unstable. We use name `STDB_UNSTABLE` because:\n        // 1. It's a close equivalent of the `unstable` Cargo feature in Rust.\n        // 2. Our diagnostic IDs use either BSATN or STDB prefix depending on the package.\n        // 3. We don't expect to mark individual experimental features with numeric IDs, so we don't use the standard 1234 suffix.\n        extensions.Contents.Append(\n            $$\"\"\"\n            [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n            public static void VolatileNonatomicScheduleImmediate{{Name}}({{string.Join(\n                \", \",\n                Args.Select(a => $\"{a.Type.Name} {a.Identifier}\")\n            )}}) {\n                using var stream = new MemoryStream();\n                using var writer = new BinaryWriter(stream);\n                {{string.Join(\n                    \"\\n\",\n                    Args.Select(a => $\"new {a.Type.ToBSATNString()}().Write(writer, {a.Identifier});\")\n                )}}\n                SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(nameof({{Identifier}}), stream);\n            }\n            \"\"\"\n        );\n\n        return extensions;\n    }\n}\n\n/// <summary>\n/// Represents a procedure method declaration in a module.\n/// </summary>\nrecord ProcedureDeclaration\n{\n    public readonly string Name;\n    public readonly string? CanonicalName;\n    public readonly string FullName;\n    public readonly EquatableArray<MemberDeclaration> Args;\n    public readonly Scope Scope;\n    private readonly bool HasWrongSignature;\n    public readonly TypeUse ReturnType;\n    private readonly bool HasTxWrapper;\n    private readonly TypeUse? TxPayloadType;\n    private readonly bool TxPayloadIsUnit;\n\n    public string Identifier => EscapeIdentifier(Name);\n\n    public ProcedureDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag)\n    {\n        var methodSyntax = (MethodDeclarationSyntax)context.TargetNode;\n        var method = (IMethodSymbol)context.TargetSymbol;\n        var attr = context.Attributes.Single().ParseAs<ProcedureAttribute>();\n\n        if (\n            method.Parameters.FirstOrDefault()?.Type\n            is not INamedTypeSymbol { Name: \"ProcedureContext\" }\n        )\n        {\n            diag.Report(ErrorDescriptor.ProcedureContextParam, methodSyntax);\n            HasWrongSignature = true;\n        }\n\n        Name = method.Name;\n        if (Name.Length >= 2)\n        {\n            var prefix = Name[..2];\n            if (prefix is \"__\" or \"on\" or \"On\")\n            {\n                diag.Report(ErrorDescriptor.ProcedureReservedPrefix, (methodSyntax, prefix));\n            }\n        }\n\n        ReturnType = TypeUse.Parse(method, method.ReturnType, diag);\n\n        if (\n            method.ReturnType\n                is INamedTypeSymbol\n                {\n                    Name: \"TxOutcome\",\n                    ContainingType: { Name: \"ProcedureContext\" }\n                } txOutcome\n            && txOutcome.TypeArguments.Length == 1\n        )\n        {\n            HasTxWrapper = true;\n            TxPayloadType = TypeUse.Parse(method, txOutcome.TypeArguments[0], diag);\n            TxPayloadIsUnit = TxPayloadType.BSATNName == \"SpacetimeDB.BSATN.Unit\";\n        }\n        else if (\n            method.ReturnType\n                is INamedTypeSymbol\n                {\n                    Name: \"TxResult\",\n                    ContainingType: { Name: \"ProcedureContext\" }\n                } txResult\n            && txResult.TypeArguments.Length == 2\n        )\n        {\n            HasTxWrapper = true;\n            TxPayloadType = TypeUse.Parse(method, txResult.TypeArguments[0], diag);\n            TxPayloadIsUnit = TxPayloadType.BSATNName == \"SpacetimeDB.BSATN.Unit\";\n        }\n\n        CanonicalName = attr.Name;\n\n        FullName = SymbolToName(method);\n        Args = new(\n            method\n                .Parameters.Skip(1)\n                .Select(p => new MemberDeclaration(p, p.Type, diag))\n                .ToImmutableArray()\n        );\n        Scope = new Scope(methodSyntax.Parent as MemberDeclarationSyntax);\n    }\n\n    public string GenerateClass()\n    {\n        var invocationArgs =\n            Args.Length == 0 ? \"\" : \", \" + string.Join(\", \", Args.Select(a => a.Identifier));\n        var invocation = $\"{FullName}((SpacetimeDB.ProcedureContext)ctx{invocationArgs})\";\n\n        var txPayload = TxPayloadType ?? ReturnType;\n        var txPayloadIsUnit = TxPayloadIsUnit;\n\n        string[] bodyLines;\n\n        if (HasWrongSignature)\n        {\n            bodyLines = new[]\n            {\n                \"throw new System.InvalidOperationException(\\\"Invalid procedure signature.\\\");\",\n            };\n        }\n        else if (HasTxWrapper)\n        {\n            var successLines = txPayloadIsUnit\n                ? new[] { \"return System.Array.Empty<byte>();\" }\n                : new[]\n                {\n                    \"using var output = new MemoryStream();\",\n                    \"using var writer = new BinaryWriter(output);\",\n                    \"__txReturnRW.Write(writer, outcome.Value!);\",\n                    \"return output.ToArray();\",\n                };\n\n            bodyLines = new[]\n            {\n                $\"var outcome = {invocation};\",\n                \"if (!outcome.IsSuccess)\",\n                \"{\",\n                \"    throw outcome.Error ?? new System.InvalidOperationException(\\\"Transaction failed.\\\");\",\n                \"}\",\n            }\n                .Concat(successLines)\n                .ToArray();\n        }\n        else if (ReturnType.Name == \"SpacetimeDB.Unit\")\n        {\n            bodyLines = new[] { $\"{invocation};\", \"return System.Array.Empty<byte>();\" };\n        }\n        else\n        {\n            var serializer = $\"new {ReturnType.ToBSATNString()}()\";\n            bodyLines = new[]\n            {\n                $\"var result = {invocation};\",\n                \"using var output = new MemoryStream();\",\n                \"using var writer = new BinaryWriter(output);\",\n                $\"{serializer}.Write(writer, result);\",\n                \"return output.ToArray();\",\n            };\n        }\n\n        var invokeBody = string.Join(\"\\n\", bodyLines.Select(line => $\"                    {line}\"));\n        var paramReads =\n            Args.Length == 0\n                ? string.Empty\n                : string.Join(\n                    \"\\n\",\n                    Args.Select(a =>\n                        $\"                    var {a.Identifier} = {a.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader);\"\n                    )\n                ) + \"\\n\";\n\n        var returnTypeExpr = HasTxWrapper\n            ? (\n                txPayloadIsUnit\n                    ? \"SpacetimeDB.BSATN.AlgebraicType.Unit\"\n                    : $\"new {txPayload.ToBSATNString2()}().GetAlgebraicType(registrar)\"\n            )\n            : (\n                ReturnType.Name == \"SpacetimeDB.Unit\"\n                    ? \"SpacetimeDB.BSATN.AlgebraicType.Unit\"\n                    : $\"new {ReturnType.ToBSATNString2()}().GetAlgebraicType(registrar)\"\n            );\n\n        var classFields = MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args);\n        if (HasTxWrapper && !txPayloadIsUnit)\n        {\n            classFields +=\n                $\"\\n        private {txPayload.BSATNName} __txReturnRW = new {txPayload.BSATNName}();\";\n        }\n\n        return $$$\"\"\"\n            class {{{Identifier}}} : SpacetimeDB.Internal.IProcedure {\n                {{{classFields}}}\n\n                public SpacetimeDB.Internal.RawProcedureDefV10 MakeProcedureDef(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new(\n                    SourceName: nameof({{{Identifier}}}),\n                    Params: [{{{MemberDeclaration.GenerateDefs(Args)}}}],\n                    ReturnType: {{{returnTypeExpr}}},\n                    Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable\n                );\n\n                public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx) {\n                    {{{paramReads}}}{{{invokeBody}}}\n                }\n            }\n            \"\"\";\n    }\n\n    public Scope.Extensions GenerateSchedule()\n    {\n        var extensions = new Scope.Extensions(Scope, FullName);\n\n        // Mark the API as unstable. We use name `STDB_UNSTABLE` because:\n        // 1. It's a close equivalent of the `unstable` Cargo feature in Rust.\n        // 2. Our diagnostic IDs use either BSATN or STDB prefix depending on the package.\n        // 3. We don't expect to mark individual experimental features with numeric IDs, so we don't use the standard 1234 suffix.\n        extensions.Contents.Append(\n            $$\"\"\"\n            [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n            public static void VolatileNonatomicScheduleImmediate{{Name}}({{string.Join(\n                \", \",\n                Args.Select(a => $\"{a.Type.Name} {a.Identifier}\")\n            )}}) {\n                using var stream = new MemoryStream();\n                using var writer = new BinaryWriter(stream);\n                {{string.Join(\n                    \"\\n\",\n                    Args.Select(a => $\"new {a.Type.ToBSATNString()}().Write(writer, {a.Identifier});\")\n                )}}\n                SpacetimeDB.Internal.ProcedureExtensions.VolatileNonatomicScheduleImmediate(nameof({{Identifier}}), stream);\n            }\n            \"\"\"\n        );\n\n        return extensions;\n    }\n}\n\nrecord ClientVisibilityFilterDeclaration\n{\n    public readonly string FullName;\n\n    public string GlobalName => $\"global::{FullName}\";\n\n    public ClientVisibilityFilterDeclaration(\n        GeneratorAttributeSyntaxContext context,\n        DiagReporter diag\n    )\n    {\n        var fieldSymbol = (IFieldSymbol)context.TargetSymbol;\n\n        if (\n            !fieldSymbol.IsStatic\n            || !fieldSymbol.IsReadOnly\n            || fieldSymbol.DeclaredAccessibility != Accessibility.Public\n        )\n        {\n            diag.Report(ErrorDescriptor.ClientVisibilityNotPublicStaticReadonly, fieldSymbol);\n        }\n\n        if (fieldSymbol.Type.ToString() is not \"SpacetimeDB.Filter\")\n        {\n            diag.Report(ErrorDescriptor.ClientVisibilityNotFilter, fieldSymbol);\n        }\n\n        FullName = SymbolToName(fieldSymbol);\n    }\n}\n\n[Generator]\npublic class Module : IIncrementalGenerator\n{\n    private static string EscapeStringLiteral(string s) =>\n        s.Replace(\"\\\\\", \"\\\\\\\\\")\n            .Replace(\"\\\"\", \"\\\\\\\"\")\n            .Replace(\"\\r\", \"\\\\r\")\n            .Replace(\"\\n\", \"\\\\n\")\n            .Replace(\"\\t\", \"\\\\t\");\n\n    /// <summary>\n    /// Collects distinct items from a source sequence, ensuring no duplicate export names exist.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of items being collected</typeparam>\n    /// <param name=\"kind\">The category/type of items being collected (used for error messages)</param>\n    /// <param name=\"context\">The incremental generator context for reporting diagnostics</param>\n    /// <param name=\"source\">The source sequence of items to process</param>\n    /// <param name=\"toExportName\">Function to get the export name for an item (used for deduplication)</param>\n    /// <param name=\"toFullName\">Function to get the full name of an item (used for error messages)</param>\n    /// <returns>An incremental value provider containing the distinct items</returns>\n    private static IncrementalValueProvider<EquatableArray<T>> CollectDistinct<T>(\n        string kind,\n        IncrementalGeneratorInitializationContext context,\n        IncrementalValuesProvider<T> source,\n        Func<T, string> toExportName,\n        Func<T, string> toFullName\n    )\n        where T : IEquatable<T>\n    {\n        var results = source\n            .Collect()\n            .Select(\n                (collected, ct) =>\n                    DiagReporter.With(\n                        Location.None,\n                        diag =>\n                        {\n                            var grouped = collected\n                                .GroupBy(toExportName)\n                                // Sort tables and reducers by name to match Rust behaviour.\n                                // Not really important outside of testing, but for testing\n                                // it matters because we commit module-bindings\n                                // so they need to match 1:1 between different langs.\n                                .OrderBy(g => g.Key);\n\n                            foreach (var group in grouped.Where(group => group.Count() > 1))\n                            {\n                                diag.Report(\n                                    ErrorDescriptor.DuplicateExport,\n                                    (kind, group.Key, group.Select(toFullName))\n                                );\n                            }\n\n                            return new EquatableArray<T>(\n                                // Only return first item from each group.\n                                // We already reported duplicates ourselves, and don't want MSBuild to produce lots of duplicate errors too.\n                                grouped.Select(Enumerable.First).ToImmutableArray()\n                            );\n                        }\n                    )\n            );\n\n        context.RegisterSourceOutput(\n            results,\n            (context, results) =>\n            {\n                foreach (var result in results.Diag)\n                {\n                    context.ReportDiagnostic(result);\n                }\n            }\n        );\n\n        return results\n            .Select((result, ct) => result.Parsed)\n            .WithTrackingName($\"SpacetimeDB.{kind}.Collect\");\n    }\n\n    public void Initialize(IncrementalGeneratorInitializationContext context)\n    {\n        var settings = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                fullyQualifiedMetadataName: typeof(SettingsAttribute).FullName,\n                predicate: (node, ct) => true,\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new SettingsDeclaration(context, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Settings.Parse\");\n\n        var settingsArray = CollectDistinct(\n            \"Settings\",\n            context,\n            settings,\n            s => s.FullName,\n            s => s.FullName\n        );\n\n        var tables = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                fullyQualifiedMetadataName: typeof(TableAttribute).FullName,\n                predicate: (node, ct) => true, // already covered by attribute restrictions\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new TableDeclaration(context, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Table.Parse\");\n\n        tables\n            .Select((t, ct) => t.ToExtensions())\n            .WithTrackingName(\"SpacetimeDB.Table.GenerateExtensions\")\n            .RegisterSourceOutputs(context);\n\n        var viewDeclarations = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                fullyQualifiedMetadataName: typeof(ViewAttribute).FullName!,\n                predicate: (node, _) => node is MethodDeclarationSyntax,\n                transform: (ctx, _) => ctx.ParseWithDiags(diag => new ViewDeclaration(ctx, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.View.Parse\");\n\n        var views = CollectDistinct(\n            \"View\",\n            context,\n            viewDeclarations,\n            v => v.Name,\n            v => v.FullName\n        );\n\n        var tableDecls = CollectDistinct(\n            \"TableDecl\",\n            context,\n            tables,\n            t => t.FullName,\n            t => t.FullName\n        );\n\n        var reducers = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                fullyQualifiedMetadataName: typeof(ReducerAttribute).FullName,\n                predicate: (node, ct) => true, // already covered by attribute restrictions\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new ReducerDeclaration(context, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Reducer.Parse\");\n\n        reducers\n            .Select((r, ct) => r.GenerateSchedule())\n            .WithTrackingName(\"SpacetimeDB.Reducer.GenerateSchedule\")\n            .RegisterSourceOutputs(context);\n\n        context.RegisterSourceOutput(\n            reducers\n                .Where(r => r.Kind != ReducerKind.UserDefined)\n                .Collect()\n                .SelectMany(\n                    (reducers, ct) =>\n                        reducers\n                            .GroupBy(r => r.Kind)\n                            .Where(group => group.Count() > 1)\n                            .Select(group =>\n                                ErrorDescriptor.DuplicateSpecialReducer.ToDiag(\n                                    (group.Key, group.Select(r => r.FullName))\n                                )\n                            )\n                ),\n            (ctx, diag) => ctx.ReportDiagnostic(diag)\n        );\n\n        var addReducers = CollectDistinct(\n            \"Reducer\",\n            context,\n            reducers\n                .Select((r, ct) => (r.Name, r.FullName, r.CanonicalName, Class: r.GenerateClass()))\n                .WithTrackingName(\"SpacetimeDB.Reducer.GenerateClass\"),\n            r => r.Name,\n            r => r.FullName\n        );\n\n        var procedures = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n                fullyQualifiedMetadataName: typeof(ProcedureAttribute).FullName,\n                predicate: (node, ct) => true, // already covered by attribute restrictions\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new ProcedureDeclaration(context, diag))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.Procedure.Parse\");\n\n        procedures\n            .Select((p, ct) => p.GenerateSchedule())\n            .WithTrackingName(\"SpacetimeDB.Procedure.GenerateSchedule\")\n            .RegisterSourceOutputs(context);\n\n        var addProcedures = CollectDistinct(\n            \"Procedure\",\n            context,\n            procedures\n                .Select((p, ct) => (p.Name, p.FullName, p.CanonicalName, Class: p.GenerateClass()))\n                .WithTrackingName(\"SpacetimeDB.Procedure.GenerateClass\"),\n            p => p.Name,\n            p => p.FullName\n        );\n\n        var tableAccessors = CollectDistinct(\n            \"Table\",\n            context,\n            tables\n                .SelectMany((t, ct) => t.GenerateTableAccessors())\n                .WithTrackingName(\"SpacetimeDB.Table.GenerateTableAccessors\"),\n            v => v.tableAccessorName,\n            v => v.tableName\n        );\n\n        var readOnlyAccessors = CollectDistinct(\n            \"TableReadOnly\",\n            context,\n            tables\n                .SelectMany((t, ct) => t.GenerateReadOnlyAccessors())\n                .WithTrackingName(\"SpacetimeDB.Table.GenerateReadOnlyAccessors\"),\n            v => v.tableAccessorName + \"ReadOnly\",\n            v => v.tableName\n        );\n\n        var rlsFilters = context\n            .SyntaxProvider.ForAttributeWithMetadataName(\n#pragma warning disable STDB_UNSTABLE\n                fullyQualifiedMetadataName: typeof(ClientVisibilityFilterAttribute).FullName,\n#pragma warning restore STDB_UNSTABLE\n                predicate: (node, ct) => true,\n                transform: (context, ct) =>\n                    context.ParseWithDiags(diag => new ClientVisibilityFilterDeclaration(\n                        context,\n                        diag\n                    ))\n            )\n            .ReportDiagnostics(context)\n            .WithTrackingName(\"SpacetimeDB.ClientVisibilityFilter.Parse\");\n\n        var rlsFiltersArray = CollectDistinct(\n            \"ClientVisibilityFilter\",\n            context,\n            rlsFilters,\n            (f) => f.FullName,\n            (f) => f.FullName\n        );\n\n        var columnDefaultValues = CollectDistinct(\n            \"ColumnDefaultValues\",\n            context,\n            tables\n                .SelectMany((t, ct) => t.GenerateDefaultValues())\n                .WithTrackingName(\"SpacetimeDB.Table.GenerateDefaultValues\"),\n            v => v.tableName + \"_\" + v.columnId,\n            v => v.tableName + \"_\" + v.columnId\n        );\n\n        // Register the generated source code with the compilation context as part of module publishing\n        // Once the compilation is complete, the generated code will be used to create tables and reducers in the database\n        context.RegisterSourceOutput(\n            tableAccessors\n                .Combine(settingsArray)\n                .Combine(tableDecls)\n                .Combine(addReducers)\n                .Combine(addProcedures)\n                .Combine(readOnlyAccessors)\n                .Combine(views)\n                .Combine(rlsFiltersArray)\n                .Combine(columnDefaultValues),\n            (context, tuple) =>\n            {\n                var (\n                    (\n                        (\n                            (\n                                (\n                                    (((tableAccessors, settings), tableDecls), addReducers),\n                                    addProcedures\n                                ),\n                                readOnlyAccessors\n                            ),\n                            views\n                        ),\n                        rlsFilters\n                    ),\n                    columnDefaultValues\n                ) = tuple;\n\n                if (settings.Array.Length > 1)\n                {\n                    context.ReportDiagnostic(\n                        ErrorDescriptor.DuplicateSettings.ToDiag(\n                            settings.Array.Select(s => s.FullName)\n                        )\n                    );\n                }\n\n                var settingsRegistration =\n                    settings.Array.Length == 1\n                    && settings.Array[0].CaseConversionPolicy is { } policyName\n                        ? $\"SpacetimeDB.Internal.Module.SetCaseConversionPolicy(SpacetimeDB.CaseConversionPolicy.{policyName});\"\n                        : string.Empty;\n\n                var explicitTableRegistrations = string.Join(\n                    \"\\n\",\n                    tableDecls.Array.SelectMany(t =>\n                        t.TableAccessors.Where(a => !string.IsNullOrEmpty(a.CanonicalName))\n                            .Select(a =>\n                                $\"SpacetimeDB.Internal.Module.RegisterExplicitTableName(\\\"{EscapeStringLiteral(a.Name)}\\\", \\\"{EscapeStringLiteral(a.CanonicalName!)}\\\");\"\n                            )\n                    )\n                );\n\n                var explicitFunctionRegistrations = string.Join(\n                    \"\\n\",\n                    addReducers\n                        .Array.Where(r => !string.IsNullOrEmpty(r.CanonicalName))\n                        .Select(r =>\n                            $\"SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\\\"{EscapeStringLiteral(r.Name)}\\\", \\\"{EscapeStringLiteral(r.CanonicalName!)}\\\");\"\n                        )\n                        .Concat(\n                            addProcedures\n                                .Array.Where(p => !string.IsNullOrEmpty(p.CanonicalName))\n                                .Select(p =>\n                                    $\"SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\\\"{EscapeStringLiteral(p.Name)}\\\", \\\"{EscapeStringLiteral(p.CanonicalName!)}\\\");\"\n                                )\n                        )\n                        .Concat(\n                            views\n                                .Array.Where(v => !string.IsNullOrEmpty(v.CanonicalName))\n                                .Select(v =>\n                                    $\"SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\\\"{EscapeStringLiteral(v.Name)}\\\", \\\"{EscapeStringLiteral(v.CanonicalName!)}\\\");\"\n                                )\n                        )\n                );\n\n                var explicitIndexRegistrations = string.Join(\n                    \"\\n\",\n                    tableDecls.Array.SelectMany(t =>\n                        t.TableAccessors.SelectMany(a =>\n                            t.GetIndexes(a)\n                                .Where(ix => !string.IsNullOrEmpty(ix.CanonicalName))\n                                .Select(ix =>\n                                    $\"SpacetimeDB.Internal.Module.RegisterExplicitIndexName(\\\"{EscapeStringLiteral(ix.StandardIndexName(a))}\\\", \\\"{EscapeStringLiteral(ix.CanonicalName!)}\\\");\"\n                                )\n                        )\n                    )\n                );\n\n                var preRegistrationLines = new[]\n                {\n                    settingsRegistration,\n                    explicitTableRegistrations,\n                    explicitFunctionRegistrations,\n                    explicitIndexRegistrations,\n                }\n                    .Where(s => !string.IsNullOrWhiteSpace(s))\n                    .ToArray();\n\n                var preRegistrations =\n                    preRegistrationLines.Length == 0\n                        ? string.Empty\n                        : \"\\n                          \"\n                            + string.Join(\"\\n                          \", preRegistrationLines)\n                            + \"\\n\";\n\n                var queryBuilderMembers = string.Join(\n                    \"\\n\",\n                    tableDecls.Array.SelectMany(t => t.GenerateQueryBuilderMembers())\n                );\n                // Don't generate the FFI boilerplate if there are no tables or reducers.\n                if (\n                    tableAccessors.Array.IsEmpty\n                    && addReducers.Array.IsEmpty\n                    && addProcedures.Array.IsEmpty\n                )\n                {\n                    return;\n                }\n                context.AddSource(\n                    \"FFI.cs\",\n                    $$\"\"\"\n                    // <auto-generated />\n                    #nullable enable\n                    // The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\\Internal\\Module.cs as an empty partial type.\n                    // This is needed so every module build doesn't generate a full LocalReadOnly type, but just adds on to the existing.\n                    // We extend it here with generated table accessors, and just need to suppress the duplicate-type warning.\n                    #pragma warning disable CS0436\n                    #pragma warning disable STDB_UNSTABLE\n\n                    using System.Diagnostics.CodeAnalysis;\n                    using System.Runtime.CompilerServices;\n                    using System.Runtime.InteropServices;\n                    using Internal = SpacetimeDB.Internal;\n                    using TxContext = SpacetimeDB.Internal.TxContext;\n\n                    namespace SpacetimeDB {\n                        {{queryBuilderMembers}}\n                        public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext {\n                            public readonly Identity Sender;\n                            public readonly ConnectionId? ConnectionId;\n                            public readonly Random Rng;\n                            public readonly Timestamp Timestamp;\n                            public readonly AuthCtx SenderAuth;\n                            // **Note:** must be 0..=u32::MAX\n                            internal int CounterUuid;\n                            // We need this property to be non-static for parity with client SDK.\n                            public Identity Identity => Internal.IReducerContext.GetIdentity();\n\n                            internal ReducerContext(Identity identity, ConnectionId? connectionId, Random random,\n                                            Timestamp time, AuthCtx? senderAuth = null)\n                            {\n                                Sender = identity;\n                                ConnectionId = connectionId;\n                                Rng = random;\n                                Timestamp = time;\n                                SenderAuth = senderAuth ?? AuthCtx.BuildFromSystemTables(connectionId, identity);\n                                CounterUuid = 0;\n                            }\n                            /// <summary>\n                            /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n                            /// </summary>\n                            /// <remarks>\n                            /// This method fills the random bytes using the context RNG.\n                            /// </remarks>\n                            /// <example>\n                            /// <code>\n                            /// var uuid = ctx.NewUuidV4();\n                            /// Log.Info(uuid);\n                            /// </code>\n                            /// </example>\n                            public Uuid NewUuidV4()\n                            {\n                                var bytes = new byte[16];\n                                Rng.NextBytes(bytes);\n                                return Uuid.FromRandomBytesV4(bytes);\n                            }\n\n                            /// <summary>\n                            /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n                            /// and timestamp.\n                            /// </summary>\n                            /// <returns>\n                            /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n                            /// and suitable for use as a primary key or for ordered storage.\n                            /// </returns>\n                            /// <exception cref=\"Exception\">\n                            /// Thrown if <see cref=\"Uuid\"/> generation fails.\n                            /// </exception>\n                            /// <example>\n                            /// <code>\n                            /// [SpacetimeDB.Reducer]\n                            /// public static Guid GenerateUuidV7(ReducerContext ctx)\n                            /// {\n                            ///     Guid uuid = ctx.NewUuidV7();\n                            ///     Log.Info(uuid);\n                            /// }\n                            /// </code>\n                            /// </example>\n                            public Uuid NewUuidV7()\n                            {\n                                var bytes = new byte[4];\n                                Rng.NextBytes(bytes);\n                                return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n                            }\n                        }\n                        \n                        public sealed partial class ProcedureContext : global::SpacetimeDB.ProcedureContextBase {\n                            private readonly Local _db = new();\n\n                            internal ProcedureContext(Identity identity, ConnectionId? connectionId, Random random, Timestamp time)\n                                : base(identity, connectionId, random, time) {}\n\n                            protected override global::SpacetimeDB.LocalBase CreateLocal() => _db;\n                            protected override global::SpacetimeDB.ProcedureTxContextBase CreateTxContext(Internal.TxContext inner) =>\n                                _cached ??= new ProcedureTxContext(inner);\n\n                            private ProcedureTxContext? _cached;\n\n                            [Experimental(\"STDB_UNSTABLE\")]\n                            public Local Db => _db;\n                            \n                            [Experimental(\"STDB_UNSTABLE\")]\n                            public TResult WithTx<TResult>(Func<ProcedureTxContext, TResult> body) =>\n                                base.WithTx(tx => body((ProcedureTxContext)tx));\n                            \n                            [Experimental(\"STDB_UNSTABLE\")]\n                            public TxOutcome<TResult> TryWithTx<TResult, TError>(\n                                Func<ProcedureTxContext, Result<TResult, TError>> body)\n                                where TError : Exception =>\n                                base.TryWithTx(tx => body((ProcedureTxContext)tx));\n\n                            /// <summary>\n                            /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n                            /// </summary>\n                            /// <remarks>\n                            /// This method fills the random bytes using the context RNG.\n                            /// </remarks>\n                            /// <example>\n                            /// <code>\n                            /// var uuid = ctx.NewUuidV4();\n                            /// Log.Info(uuid);\n                            /// </code>\n                            /// </example>\n                            public Uuid NewUuidV4()\n                            {\n                                var bytes = new byte[16];\n                                Rng.NextBytes(bytes);\n                                return Uuid.FromRandomBytesV4(bytes);\n                            }\n\n                            /// <summary>\n                            /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n                            /// and timestamp.\n                            /// </summary>\n                            /// <returns>\n                            /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n                            /// and suitable for use as a primary key or for ordered storage.\n                            /// </returns>\n                            /// <exception cref=\"Exception\">\n                            /// Thrown if UUID generation fails.\n                            /// </exception>\n                            /// <example>\n                            /// <code>\n                            /// [SpacetimeDB.Procedure]\n                            /// public static Guid GenerateUuidV7(ReducerContext ctx)\n                            /// {\n                            ///     Guid uuid = ctx.NewUuidV7();\n                            ///     Log.Info(uuid);\n                            /// }\n                            /// </code>\n                            /// </example>\n                            public Uuid NewUuidV7()\n                            {\n                                var bytes = new byte[4];\n                                Rng.NextBytes(bytes);\n                                return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n                            }\n                        }\n\n                        [Experimental(\"STDB_UNSTABLE\")]\n                        public sealed class ProcedureTxContext : global::SpacetimeDB.ProcedureTxContextBase {\n                            internal ProcedureTxContext(Internal.TxContext inner) : base(inner) {}\n\n                            public new Local Db => (Local)base.Db;\n                        }\n\n                        public sealed class Local : global::SpacetimeDB.LocalBase {\n                            {{string.Join(\"\\n\", tableAccessors.Select(v => v.getter))}}\n                        }\n                        \n                        public sealed record ViewContext : DbContext<Internal.LocalReadOnly>, Internal.IViewContext \n                        {\n                            public Identity Sender { get; }\n\n                            public QueryBuilder From => default;\n                        \n                            internal ViewContext(Identity sender, Internal.LocalReadOnly db)\n                                : base(db)\n                            {\n                                Sender = sender;\n                            }\n                        }\n                        \n                        public sealed record AnonymousViewContext : DbContext<Internal.LocalReadOnly>, Internal.IAnonymousViewContext \n                        {\n                            public QueryBuilder From => default;\n\n                            internal AnonymousViewContext(Internal.LocalReadOnly db)\n                                : base(db) { }\n                        }\n                    }\n                    \n                    namespace SpacetimeDB.Internal.TableHandles {\n                        {{string.Join(\"\\n\", tableAccessors.Select(v => v.tableAccessor))}}\n                    }\n                    \n                    {{string.Join(\"\\n\",\n                        views.Array.Where(v => !v.IsAnonymous)\n                            .Select((v, i) => v.GenerateDispatcherClass((uint)i))\n                            .Concat(\n                                views.Array.Where(v => v.IsAnonymous)\n                                    .Select((v, i) => v.GenerateDispatcherClass((uint)i))\n                            )\n                    )}}\n                        \n                    namespace SpacetimeDB.Internal.ViewHandles {\n                        {{string.Join(\"\\n\", readOnlyAccessors.Array.Select(v => v.readOnlyAccessor))}}\n                    }\n                    \n                    namespace SpacetimeDB.Internal {\n                        public sealed partial class LocalReadOnly {\n                            {{string.Join(\"\\n\", readOnlyAccessors.Select(v => v.readOnlyGetter))}}\n                        }\n                    }\n                    \n                    static class ModuleRegistration {\n                        {{string.Join(\"\\n\", addReducers.Select(r => r.Class))}}\n                        \n                        {{string.Join(\"\\n\", addProcedures.Select(r => r.Class))}}\n\n                        public static List<T> ToListOrEmpty<T>(T? value) where T : struct\n                                => value is null ? new List<T>() : new List<T> { value.Value };\n\n                        public static List<T> ToListOrEmpty<T>(T? value) where T : class\n                                => value is null ? new List<T>() : new List<T> { value };\n\n                    #if EXPERIMENTAL_WASM_AOT\n                        // In AOT mode we're building a library.\n                        // Main method won't be called automatically, so we need to export it as a preinit function.\n                        [UnmanagedCallersOnly(EntryPoint = \"__preinit__10_init_csharp\")]\n                    #else\n                        // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer.\n                        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(SpacetimeDB.Internal.Module))]\n                    #endif\n                        public static void Main() {\n                          SpacetimeDB.Internal.Module.SetReducerContextConstructor((identity, connectionId, random, time) => new SpacetimeDB.ReducerContext(identity, connectionId, random, time));\n                          SpacetimeDB.Internal.Module.SetViewContextConstructor(identity => new SpacetimeDB.ViewContext(identity, new SpacetimeDB.Internal.LocalReadOnly()));\n                          SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor(() => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly()));\n                          SpacetimeDB.Internal.Module.SetProcedureContextConstructor((identity, connectionId, random, time) => new SpacetimeDB.ProcedureContext(identity, connectionId, random, time));{{preRegistrations}}\n                          var __memoryStream = new MemoryStream();\n                          var __writer = new BinaryWriter(__memoryStream);\n\n                            {{string.Join(\n                                \"\\n\",\n                                addReducers.Select(r =>\n                                    $\"SpacetimeDB.Internal.Module.RegisterReducer<{EscapeIdentifier(r.Name)}>();\"\n                                )\n                            )}}\n                            {{string.Join(\n                                \"\\n\",\n                                addProcedures.Select(r =>\n                                    $\"SpacetimeDB.Internal.Module.RegisterProcedure<{EscapeIdentifier(r.Name)}>();\"\n                                )\n                            )}}\n\n                            // IMPORTANT: The order in which we register views matters.\n                            // It must correspond to the order in which we call `GenerateDispatcherClass`.\n                            // See the comment on `GenerateDispatcherClass` for more explanation.\n                            {{string.Join(\"\\n\",\n                                views.Array.Where(v => !v.IsAnonymous)\n                                    .Select(v => $\"SpacetimeDB.Internal.Module.RegisterView<{v.Name}ViewDispatcher>();\")\n                                    .Concat(\n                                        views.Array.Where(v => v.IsAnonymous)\n                                            .Select(v => $\"SpacetimeDB.Internal.Module.RegisterAnonymousView<{v.Name}ViewDispatcher>();\")\n                                    )\n                            )}}                            \n\n                            {{string.Join(\n                                \"\\n\",\n                                tableAccessors.Select(t => $\"SpacetimeDB.Internal.Module.RegisterTable<{t.tableName}, SpacetimeDB.Internal.TableHandles.{EscapeIdentifier(t.tableAccessorName)}>();\")\n                            )}}\n                            {{string.Join(\n                                \"\\n\",\n                                rlsFilters.Select(f => $\"SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter({f.GlobalName});\")\n                            )}}\n                            {{string.Join(\n                                \"\\n\",\n                                columnDefaultValues.Select(d =>\n                                    \"{\\n\"\n                                         + $\"var value = new {d.BSATNTypeName}();\\n\"\n                                         + \"__memoryStream.Position = 0;\\n\"\n                                         + \"__memoryStream.SetLength(0);\\n\"\n                                         + $\"value.Write(__writer, {d.value});\\n\"\n                                         + \"var array = __memoryStream.ToArray();\\n\"\n                                         + $\"SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\\\"{d.tableName}\\\", {d.columnId}, array);\"\n                                         + \"\\n}\\n\")\n                            )}}\n                        }\n\n                    // Exports only work from the main assembly, so we need to generate forwarding methods.\n                    #if EXPERIMENTAL_WASM_AOT\n                        [UnmanagedCallersOnly(EntryPoint = \"__describe_module__\")]\n                        public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d);\n\n                        [UnmanagedCallersOnly(EntryPoint = \"__call_reducer__\")]\n                        public static SpacetimeDB.Internal.Errno __call_reducer__(\n                            uint id,\n                            ulong sender_0,\n                            ulong sender_1,\n                            ulong sender_2,\n                            ulong sender_3,\n                            ulong conn_id_0,\n                            ulong conn_id_1,\n                            SpacetimeDB.Timestamp timestamp,\n                            SpacetimeDB.Internal.BytesSource args,\n                            SpacetimeDB.Internal.BytesSink error\n                        ) => SpacetimeDB.Internal.Module.__call_reducer__(\n                            id,\n                            sender_0,\n                            sender_1,\n                            sender_2,\n                            sender_3,\n                            conn_id_0,\n                            conn_id_1,\n                            timestamp,\n                            args,\n                            error\n                        );\n                        \n                        [UnmanagedCallersOnly(EntryPoint = \"__call_procedure__\")]\n                        public static SpacetimeDB.Internal.Errno __call_procedure__(\n                            uint id,\n                            ulong sender_0,\n                            ulong sender_1,\n                            ulong sender_2,\n                            ulong sender_3,\n                            ulong conn_id_0,\n                            ulong conn_id_1,\n                            SpacetimeDB.Timestamp timestamp,\n                            SpacetimeDB.Internal.BytesSource args,\n                            SpacetimeDB.Internal.BytesSink result_sink\n                        ) => SpacetimeDB.Internal.Module.__call_procedure__(\n                            id,\n                            sender_0,\n                            sender_1,\n                            sender_2,\n                            sender_3,\n                            conn_id_0,\n                            conn_id_1,\n                            timestamp,\n                            args,\n                            result_sink\n                        );\n                        \n                        [UnmanagedCallersOnly(EntryPoint = \"__call_view__\")]\n                        public static SpacetimeDB.Internal.Errno __call_view__(\n                            uint id,\n                            ulong sender_0,\n                            ulong sender_1,\n                            ulong sender_2,\n                            ulong sender_3,\n                            SpacetimeDB.Internal.BytesSource args,\n                            SpacetimeDB.Internal.BytesSink sink\n                        ) => SpacetimeDB.Internal.Module.__call_view__(\n                            id,\n                            sender_0,\n                            sender_1,\n                            sender_2,\n                            sender_3,\n                            args,\n                            sink\n                        );\n\n                        [UnmanagedCallersOnly(EntryPoint = \"__call_view_anon__\")]\n                        public static SpacetimeDB.Internal.Errno __call_view_anon__(\n                            uint id,\n                            SpacetimeDB.Internal.BytesSource args,\n                            SpacetimeDB.Internal.BytesSink sink\n                        ) => SpacetimeDB.Internal.Module.__call_view_anon__(\n                            id,\n                            args,\n                            sink\n                        );                                                \n                    #endif\n                    }\n                    \n                    #pragma warning restore STDB_UNSTABLE\n                    #pragma warning restore CS0436\n                    \"\"\"\n                );\n            }\n        );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen/README.md",
    "content": "# SpacetimeDB.Codegen\n\nThis project contains Roslyn [incremental source generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md) that augment types and tables with static methods for self-describing and registration. They look for different attributes to know which types to augment:\n\n- `[SpacetimeDB.Type]` - generates a `GetSatsTypeInfo()` static method that registers this type with the runtime and returns a `TypeInfo` object. It supports only `struct`s for now to explicitly forbid infinitely recursive types and to make the implementation simpler, as it doesn't need to deal with type references - each table is registered as an entirely self-contained type together with its nested structs if any. This is unlikely to be a problem in common scenarios, but it will be optimised in the future.\n\n  All the nested fields will be added to the product type. Because it's not possible to implement static extension methods on 3rd-party types (including built-ins) in C#, the codegen is responsible for manually routing different types to their `TypeInfo` descriptors. See various static `TypeInfo` properties and helper methods on `SpacetimeDB.BSATN.AlgebraicType` (`Runtime/AlgebraicType.cs`) and routing logic in `Utils.GetTypeInfo` (`Codegen/Utils.cs`) for more details.\n\n  Also, for the same reason - absence of static extension methods in C# - the codegen expects that your struct, as well as any of its parents, is `partial` so methods can be added from extra source files generated by the codegen.\n\n- `[SpacetimeDB.Type]` - also supports emulation of tagged enums in C#. For that, the struct needs to inherit a marker interface `SpacetimeDB.TaggedEnum<Variants>` where `Variants` is a named tuple of all possible variants, e.g.:\n\n  ```csharp\n  [SpacetimeDB.Type]\n  partial record Option<T> : SpacetimeDB.TaggedEnum<(T Some, Unit None)>;\n  ```\n\n  will generate inherited records `Option.Some(T Some_)` and `Option.None(Unit None_)`. It allows\n  you to use tagged enums in C# in a similar way to Rust enums by leveraging C# pattern-matching\n  on any instance of `Option<T>`.\n\n- `[SpacetimeDB.Table]` - generates code to register this table in the `FFI` upon startup so that they can be enumerated by the `__describe_module__` FFI API. It implies `[SpacetimeDB.Type]`, so you must not specify both attributes on the same struct.\n\n  The fields can be marked with `[SpacetimeDB.ColumnAttrs]` and those will be detected by the codegen and passed on to the runtime as well. Example:\n\n  ```csharp\n  [SpacetimeDB.Table]\n  public partial struct Person\n  {\n      [SpacetimeDB.Column(ColumnAttrs.Identity)]\n      public int Id;\n      public string Name;\n  }\n  ```\n\n- `[SpacetimeDB.Reducer]` - generates code to register a static function as a SpacetimeDB reducer in the `FFI` upon startup and creates a wrapper that will parse SATS binary blob into individual arguments and invoke the underlying function for the `__call_reducer__` FFI API.\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <IsPackable>false</IsPackable>\n    <IsTestProject>true</IsTestProject>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <RootNamespace>SpacetimeDB.Codegen.Tests</RootNamespace>\n    <DefaultItemExcludes>$(DefaultItemExcludes);fixtures/**/*</DefaultItemExcludes>\n  </PropertyGroup>\n\n  <!-- A helper that allows to auto-accept all snapshots during test run via `dotnet test /p:AutoVerify=true`. -->\n  <PropertyGroup Condition=\"'$(AutoVerify)'=='true'\">\n    <DefineConstants>$(DefineConstants);AUTO_VERIFY</DefineConstants>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"CSharpier.Core\" Version=\"0.28.2\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.Common\" Version=\"4.10.0\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\" Version=\"4.10.0\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.Workspaces.MSBuild\" Version=\"4.10.0\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging\" Version=\"8.0.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.6.0\" />\n    <PackageReference Include=\"Verify.SourceGenerators\" Version=\"2.4.3\" />\n    <PackageReference Include=\"Verify.XUnit\" Version=\"26.4.5\" />\n    <PackageReference Include=\"xunit\" Version=\"2.9.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.8.1\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../Codegen/Codegen.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/TestInit.cs",
    "content": "namespace SpacetimeDB.Codegen.Tests;\n\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing Microsoft.CodeAnalysis;\n\n// Global Verify setup for all tests we might have.\nstatic class TestInit\n{\n    // A custom Diagnostic converter that pretty-prints the error with the source code snippet and squiggly underline.\n    // TODO: upstream this?\n    class DiagConverter : WriteOnlyJsonConverter<Diagnostic>\n    {\n        public override void Write(VerifyJsonWriter writer, Diagnostic diag)\n        {\n            writer.WriteStartObject();\n            // Pretty-print the error with the source code snippet.\n            var loc = diag.Location;\n            if (loc.SourceTree is { } source)\n            {\n                var comment = new StringBuilder().AppendLine();\n                var lineSpan = loc.GetLineSpan();\n                var lines = source.GetText().Lines;\n                const int contextLines = 1;\n                var startLine = Math.Max(lineSpan.StartLinePosition.Line - contextLines, 0);\n                var endLine = Math.Min(\n                    lineSpan.EndLinePosition.Line + contextLines,\n                    lines.Count - 1\n                );\n                for (var lineIdx = startLine; lineIdx <= endLine; lineIdx++)\n                {\n                    var line = lines[lineIdx];\n                    // print the source line\n                    comment.AppendLine(line.ToString());\n                    // print squiggly line highlighting the location\n                    if (line.Span.Intersection(loc.SourceSpan) is { } intersection)\n                    {\n                        comment\n                            .Append(' ', intersection.Start - line.Start)\n                            .Append('^', intersection.Length)\n                            .AppendLine();\n                    }\n                }\n                writer.WriteComment(comment.ToString());\n            }\n            else\n            {\n                // Write location only when we don't have the source code snippet to make snapshots more stable.\n                writer.WriteMember(diag, diag.Location, nameof(diag.Location));\n            }\n            writer.WriteMember(diag, diag.GetMessage(), \"Message\");\n            writer.WriteMember(diag, diag.Severity, nameof(diag.Severity));\n            writer.WriteMember(diag, diag.Descriptor, nameof(diag.Descriptor));\n            writer.WriteEndObject();\n        }\n    }\n\n    [ModuleInitializer]\n    public static void Initialize()\n    {\n        if (Environment.GetEnvironmentVariable(\"CI\") is null)\n        {\n            // Don't show the full diff in the console when running tests locally as Verify will open the diff tool automatically.\n            VerifierSettings.OmitContentFromException();\n        }\n        // Default diff order is weird and causes new lines to look like deleted and old as inserted.\n        Environment.SetEnvironmentVariable(\"DiffEngine_TargetOnLeft\", \"true\");\n        VerifySourceGenerators.Initialize();\n        // Format code for more readable snapshots and to avoid diffs on whitespace changes.\n        VerifierSettings.AddScrubber(\n            \"cs\",\n            (sb) =>\n            {\n                var unformattedCode = sb.ToString();\n                sb.Clear();\n                var result = CSharpier.CodeFormatter.Format(\n                    unformattedCode,\n                    new() { IncludeGenerated = true, EndOfLine = CSharpier.EndOfLine.LF }\n                );\n                sb.Append(result.Code);\n                // Print errors in the end so that their line numbers are still meaningful.\n                if (result.CompilationErrors.Any())\n                {\n                    sb.AppendLine();\n                    sb.AppendLine(\"// Generated code produced compilation errors:\");\n                    foreach (var diag in result.CompilationErrors)\n                    {\n                        sb.Append(\"// \").AppendLine(diag.ToString());\n                    }\n                }\n            },\n            ScrubberLocation.Last\n        );\n        VerifierSettings.AddExtraSettings(settings =>\n        {\n            settings.Converters.Insert(0, new DiagConverter());\n        });\n#if AUTO_VERIFY\n        VerifierSettings.AutoVerify();\n#endif\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/Tests.cs",
    "content": "namespace SpacetimeDB.Codegen.Tests;\n\nusing System.Collections.Immutable;\nusing System.Runtime.CompilerServices;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.MSBuild;\nusing Microsoft.CodeAnalysis.Text;\n\npublic static class GeneratorSnapshotTests\n{\n    // Note that we can't use assembly path here because it will be put in some deep nested folder.\n    // Instead, to get the test project directory, we can use the `CallerFilePath` attribute which will magically give us path to the current file.\n    static string GetProjectDir([CallerFilePath] string path = \"\") => Path.GetDirectoryName(path)!;\n\n    record struct StepOutput(string Key, IncrementalStepRunReason Reason, object Value);\n\n    class Fixture\n    {\n        private readonly string projectDir;\n        private readonly CSharpCompilation sampleCompilation;\n\n        public Fixture(string projectDir, CSharpCompilation sampleCompilation)\n        {\n            this.projectDir = projectDir;\n            this.sampleCompilation = sampleCompilation;\n        }\n\n        public CSharpCompilation SampleCompilation => sampleCompilation;\n\n        public static async Task<Fixture> Compile(string name)\n        {\n            var projectDir = Path.Combine(GetProjectDir(), \"fixtures\", name);\n            using var workspace = MSBuildWorkspace.Create();\n            var sampleProject = await workspace.OpenProjectAsync($\"{projectDir}/{name}.csproj\");\n            var compilation = await sampleProject.GetCompilationAsync();\n            return new(projectDir, (CSharpCompilation)compilation!);\n        }\n\n        public Task Verify(string fileName, object target) =>\n            Verifier.Verify(target).UseDirectory($\"{projectDir}/snapshots\").UseFileName(fileName);\n\n        private static CSharpGeneratorDriver CreateDriver(\n            IIncrementalGenerator generator,\n            LanguageVersion languageVersion\n        )\n        {\n            return CSharpGeneratorDriver.Create(\n                [generator.AsSourceGenerator()],\n                driverOptions: new(\n                    disabledOutputs: IncrementalGeneratorOutputKind.None,\n                    trackIncrementalGeneratorSteps: true\n                ),\n                // Make sure that generated files are parsed with the same language version.\n                parseOptions: new(languageVersion)\n            );\n        }\n\n        private async Task<IEnumerable<SyntaxTree>> RunAndCheckGenerator(\n            IIncrementalGenerator generator\n        )\n        {\n            var driver = CreateDriver(generator, sampleCompilation.LanguageVersion);\n\n            // Store the new driver instance - it contains the results and the cache.\n            var driverAfterGen = driver.RunGenerators(sampleCompilation);\n            var genResult = driverAfterGen.GetRunResult();\n\n            // Verify the generated code against the snapshots.\n            await Verify(generator.GetType().Name, genResult);\n\n            CheckCacheWorking(sampleCompilation, driverAfterGen);\n\n            return genResult.GeneratedTrees;\n        }\n\n        public GeneratorDriverRunResult RunGeneratorAndGetResult(IIncrementalGenerator generator)\n        {\n            var driver = CreateDriver(generator, sampleCompilation.LanguageVersion);\n            return driver.RunGenerators(sampleCompilation).GetRunResult();\n        }\n\n        public async Task<CSharpCompilation> RunAndCheckGenerators(\n            params IIncrementalGenerator[] generators\n        ) =>\n            sampleCompilation.AddSyntaxTrees(\n                (await Task.WhenAll(generators.Select(RunAndCheckGenerator))).SelectMany(output =>\n                    output\n                )\n            );\n    }\n\n    private static void CheckCacheWorking(\n        CSharpCompilation sampleCompilation,\n        GeneratorDriver driverAfterGen\n    )\n    {\n        // Run again with a driver containing the cache and a trivially modified code to verify that the cache is working.\n        var modifiedCompilation = sampleCompilation\n            .RemoveAllSyntaxTrees()\n            .AddSyntaxTrees(\n                sampleCompilation.SyntaxTrees.Select(tree =>\n                    tree.WithChangedText(\n                        SourceText.From(\n                            string.Join(\n                                \"\\n\",\n                                tree.GetText().Lines.Select(line => $\"{line} // Modified\")\n                            )\n                        )\n                    )\n                )\n            );\n\n        var driverAfterRegen = driverAfterGen.RunGenerators(modifiedCompilation);\n\n        var regenSteps = driverAfterRegen\n            .GetRunResult()\n            .Results.SelectMany(result => result.TrackedSteps)\n            .Where(step => step.Key.StartsWith(\"SpacetimeDB.\"))\n            .SelectMany(step =>\n                step.Value.SelectMany(value => value.Outputs)\n                    .Select(output => new StepOutput(step.Key, output.Reason, output.Value))\n            )\n            .ToImmutableArray();\n\n        // Ensure that we have tracked steps at all.\n        Assert.NotEmpty(regenSteps);\n\n        // Ensure that all steps were cached.\n        Assert.Empty(\n            regenSteps.Where(step =>\n                step.Reason\n                    is not (IncrementalStepRunReason.Cached or IncrementalStepRunReason.Unchanged)\n            )\n        );\n    }\n\n    static IEnumerable<Diagnostic> GetCompilationErrors(Compilation compilation)\n    {\n        return compilation\n            .Emit(Stream.Null)\n            .Diagnostics.Where(diag => diag.Severity != DiagnosticSeverity.Hidden)\n            // The order of diagnostics is not predictable, sort them by location to make the test deterministic.\n            .OrderBy(diag => diag.GetMessage() + diag.Location.ToString());\n    }\n\n    static void AssertGeneratedCodeDoesNotUseInternalBound(CSharpCompilation compilation)\n    {\n        var generatedText = string.Join(\n            \"\\n\\n\",\n            compilation.SyntaxTrees.Select(tree => tree.GetText().ToString())\n        );\n\n        Assert.DoesNotContain(\"global::SpacetimeDB.Internal.Bound<\", generatedText);\n        Assert.Contains(\"global::SpacetimeDB.Bound<\", generatedText);\n    }\n\n    static void AssertPublicBoundIsAvailableInRuntime(Compilation compilation)\n    {\n        var bound = compilation.GetTypeByMetadataName(\"SpacetimeDB.Bound`1\");\n        Assert.NotNull(bound);\n        Assert.Equal(Accessibility.Public, bound!.DeclaredAccessibility);\n    }\n\n    static void AssertRuntimeDoesNotDefineLocal(Compilation compilation)\n    {\n        var runtimeAssembly = compilation\n            .References.Select(r => compilation.GetAssemblyOrModuleSymbol(r))\n            .OfType<IAssemblySymbol>()\n            .FirstOrDefault(a => a.Name == \"SpacetimeDB.Runtime\");\n\n        Assert.NotNull(runtimeAssembly);\n\n        // These types are generated per-module by SpacetimeDB.Codegen.Module.\n        // If Runtime defines any of them too, user projects can hit CS0436 warnings.\n        var codegenOwnedTypes = new[]\n        {\n            \"SpacetimeDB.Local\",\n            \"SpacetimeDB.ProcedureContext\",\n            \"SpacetimeDB.ProcedureTxContext\",\n            \"SpacetimeDB.ReducerContext\",\n            \"SpacetimeDB.ViewContext\",\n            \"SpacetimeDB.AnonymousViewContext\",\n        };\n\n        foreach (var name in codegenOwnedTypes)\n        {\n            Assert.Null(runtimeAssembly!.GetTypeByMetadataName(name));\n        }\n    }\n\n    static void AssertNoCs0436Diagnostics(Compilation compilation)\n    {\n        var diagnostics = compilation\n            .Emit(Stream.Null)\n            .Diagnostics.Where(diag => diag.Severity != DiagnosticSeverity.Hidden);\n\n        Assert.DoesNotContain(diagnostics, d => d.Id == \"CS0436\");\n    }\n\n    [Fact]\n    public static async Task TypeGeneratorOnClient()\n    {\n        var fixture = await Fixture.Compile(\"client\");\n\n        var compilationAfterGen = await fixture.RunAndCheckGenerators(\n            new SpacetimeDB.Codegen.Type()\n        );\n\n        Assert.Empty(GetCompilationErrors(compilationAfterGen));\n    }\n\n    [Fact]\n    public static async Task TypeAndModuleGeneratorsOnServer()\n    {\n        var fixture = await Fixture.Compile(\"server\");\n\n        var compilationAfterGen = await fixture.RunAndCheckGenerators(\n            new SpacetimeDB.Codegen.Type(),\n            new SpacetimeDB.Codegen.Module()\n        );\n\n        Assert.Empty(GetCompilationErrors(compilationAfterGen));\n\n        AssertPublicBoundIsAvailableInRuntime(compilationAfterGen);\n        AssertRuntimeDoesNotDefineLocal(compilationAfterGen);\n        AssertGeneratedCodeDoesNotUseInternalBound(compilationAfterGen);\n\n        // Regression guard for user-reported warning spam:\n        // make sure a downstream \"user\" file that references SpacetimeDB.Local doesn't trigger CS0436.\n        var userCode =\n            \"namespace User; public sealed class UseLocal { public SpacetimeDB.Local Db; }\";\n        var userTree = CSharpSyntaxTree.ParseText(\n            userCode,\n            new CSharpParseOptions(compilationAfterGen.LanguageVersion)\n        );\n        var compilationWithUserCode = compilationAfterGen.AddSyntaxTrees(userTree);\n        AssertNoCs0436Diagnostics(compilationWithUserCode);\n    }\n\n    [Fact]\n    public static async Task SettingsAndExplicitNames()\n    {\n        var fixture = await Fixture.Compile(\"explicitnames\");\n\n        var compilationAfterGen = await fixture.RunAndCheckGenerators(\n            new SpacetimeDB.Codegen.Type(),\n            new SpacetimeDB.Codegen.Module()\n        );\n\n        Assert.Empty(GetCompilationErrors(compilationAfterGen));\n\n        AssertPublicBoundIsAvailableInRuntime(compilationAfterGen);\n        AssertRuntimeDoesNotDefineLocal(compilationAfterGen);\n        AssertGeneratedCodeDoesNotUseInternalBound(compilationAfterGen);\n    }\n\n    [Fact]\n    public static async Task CSharpKeywordIdentifiersAreEscapedInGeneratedCode()\n    {\n        var fixture = await Fixture.Compile(\"server\");\n\n        const string source = \"\"\"\n            using SpacetimeDB;\n\n            [SpacetimeDB.Table]\n            public partial struct KeywordTable\n            {\n                [SpacetimeDB.PrimaryKey]\n                public ulong @class;\n\n                public int @params;\n            }\n\n            [SpacetimeDB.Table(Accessor = \"event\")]\n            public partial struct AccessorKeywordTable\n            {\n                [SpacetimeDB.PrimaryKey]\n                [SpacetimeDB.Index.BTree(Accessor = \"params\")]\n                public int Id;\n            }\n\n            [SpacetimeDB.Table]\n            public partial struct @class\n            {\n                [SpacetimeDB.PrimaryKey]\n                public int Id;\n            }\n\n            public static partial class KeywordApis\n            {\n                [SpacetimeDB.Reducer]\n                public static void KeywordReducer(ReducerContext ctx, int @params, string @class)\n                {\n                    _ = @params;\n                    _ = @class;\n                }\n\n                [SpacetimeDB.Reducer]\n                public static void @class(ReducerContext ctx)\n                {\n                }\n\n                [SpacetimeDB.Procedure]\n                public static int KeywordProcedure(ProcedureContext ctx, int @params, int @class)\n                {\n                    return @params + @class;\n                }\n\n                [SpacetimeDB.Procedure]\n                public static void @params(ProcedureContext ctx)\n                {\n                }\n            }\n            \"\"\";\n\n        var parseOptions = new CSharpParseOptions(fixture.SampleCompilation.LanguageVersion);\n        var tree = CSharpSyntaxTree.ParseText(source, parseOptions, path: \"KeywordNames.cs\");\n        var compilation = fixture.SampleCompilation.AddSyntaxTrees(tree);\n\n        var driver = CSharpGeneratorDriver.Create(\n            [\n                new SpacetimeDB.Codegen.Type().AsSourceGenerator(),\n                new SpacetimeDB.Codegen.Module().AsSourceGenerator(),\n            ],\n            driverOptions: new(\n                disabledOutputs: IncrementalGeneratorOutputKind.None,\n                trackIncrementalGeneratorSteps: true\n            ),\n            parseOptions: parseOptions\n        );\n\n        var runResult = driver.RunGenerators(compilation).GetRunResult();\n        var compilationAfterGen = compilation.AddSyntaxTrees(runResult.GeneratedTrees);\n\n        Assert.Empty(GetCompilationErrors(compilationAfterGen));\n    }\n\n    [Fact]\n    public static async Task TestDiagnostics()\n    {\n        var fixture = await Fixture.Compile(\"diag\");\n\n        var compilationAfterGen = await fixture.RunAndCheckGenerators(\n            new SpacetimeDB.Codegen.Type(),\n            new SpacetimeDB.Codegen.Module()\n        );\n\n        // Unlike in regular tests, we don't expect this compilation to succeed - it's supposed to be full of errors.\n        // We already reported the useful ones from the generator, but let's snapshot those emitted by the compiler as well.\n        // This way we can notice when they get particularly noisy and improve our codegen for the case of a broken code.\n        await fixture.Verify(\"ExtraCompilationErrors\", GetCompilationErrors(compilationAfterGen));\n\n        AssertPublicBoundIsAvailableInRuntime(compilationAfterGen);\n        AssertRuntimeDoesNotDefineLocal(compilationAfterGen);\n        AssertGeneratedCodeDoesNotUseInternalBound(compilationAfterGen);\n    }\n\n    [Fact]\n    public static async Task ViewInvalidReturnHighlightsReturnType()\n    {\n        var fixture = await Fixture.Compile(\"diag\");\n\n        var runResult = fixture.RunGeneratorAndGetResult(new SpacetimeDB.Codegen.Module());\n\n        var method = fixture\n            .SampleCompilation.SyntaxTrees.Select(tree => new\n            {\n                Tree = tree,\n                Root = tree.GetRoot(),\n            })\n            .SelectMany(entry =>\n                entry\n                    .Root.DescendantNodes()\n                    .OfType<MethodDeclarationSyntax>()\n                    .Select(method => new\n                    {\n                        entry.Tree,\n                        entry.Root,\n                        Method = method,\n                    })\n            )\n            .Single(entry => entry.Method.Identifier.Text == \"ViewDefIEnumerableReturnFromIter\");\n\n        var returnTypeSpan = method.Method.ReturnType.Span;\n        var diagnostics = runResult\n            .Results.SelectMany(result => result.Diagnostics)\n            .Where(d => d.Id == \"STDB0024\")\n            .ToList();\n        var diagnostic = diagnostics.FirstOrDefault(d =>\n            d.GetMessage().Contains(\"ViewDefIEnumerableReturnFromIter\")\n            && d.Location.SourceTree == method.Tree\n        );\n\n        Assert.NotNull(diagnostic);\n\n        Assert.Equal(returnTypeSpan, diagnostic!.Location.SourceSpan);\n\n        var returnTypeText = method\n            .Root.ToFullString()\n            .Substring(returnTypeSpan.Start, returnTypeSpan.Length);\n        Assert.Contains(\"IEnumerable\", returnTypeText);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/Directory.Build.props",
    "content": "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Nullable>enable</Nullable>\n\n    <IsPackable>false</IsPackable>\n    <DefaultItemExcludes>$(DefaultItemExcludes);snapshots/**/*</DefaultItemExcludes>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/Lib.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing SpacetimeDB;\n\n#pragma warning disable CA1050 // Declare types in namespaces - this is a test fixture, no need for a namespace.\n\n[SpacetimeDB.Type]\npublic partial struct CustomStruct\n{\n    public const int IGNORE_ME = 0;\n    public static readonly string IGNORE_ME_TOO = \"\";\n    public int IntField;\n    public string StringField;\n}\n\n[SpacetimeDB.Type]\npublic partial struct CustomClass\n{\n    public const int IGNORE_ME = 0;\n    public static readonly string IGNORE_ME_TOO = \"\";\n    public int IntField;\n    public string StringField;\n}\n\n[StructLayout(LayoutKind.Auto)]\npublic partial struct CustomClass\n{\n    public int IgnoreExtraFields;\n}\n\n[SpacetimeDB.Type]\npublic enum CustomEnum\n{\n    EnumVariant1,\n    EnumVariant2,\n}\n\nnamespace System.Runtime.CompilerServices\n{\n    internal static class IsExternalInit { } // https://stackoverflow.com/a/64749403/1484415\n\n    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]\n    internal sealed class ModuleInitializerAttribute : Attribute { }\n}\n\n[SpacetimeDB.Type]\npublic partial record CustomTaggedEnum\n    : SpacetimeDB.TaggedEnum<(int IntVariant, string StringVariant)>;\n\n[SpacetimeDB.Type]\npublic partial struct PublicTable\n{\n    public int Id;\n    public byte ByteField;\n    public ushort UshortField;\n    public uint UintField;\n    public ulong UlongField;\n    public U128 U128Field;\n    public U256 U256Field;\n    public sbyte SbyteField;\n    public short ShortField;\n    public int IntField;\n    public long LongField;\n    public I128 I128Field;\n    public I256 I256Field;\n    public bool BoolField;\n    public float FloatField;\n    public double DoubleField;\n    public string StringField;\n    public Identity IdentityField;\n    public ConnectionId ConnectionIdField;\n    public CustomStruct CustomStructField;\n    public CustomClass CustomClassField;\n    public CustomEnum CustomEnumField;\n    public CustomTaggedEnum CustomTaggedEnumField;\n    public List<int> ListField;\n    public int? NullableValueField;\n    public string? NullableReferenceField;\n}\n\ninternal static class PublicTableViewRegressions\n{\n    [global::System.Runtime.CompilerServices.ModuleInitializer]\n    internal static void Initialize()\n    {\n        ValidatePublicTableQuerySql();\n        ValidatePublicTableViewSql();\n        ValidateFindPublicTableByIdentitySql();\n    }\n\n    private sealed class PublicTableCols\n    {\n        public Col<PublicTable, int> Id { get; }\n\n        public PublicTableCols(string tableName)\n        {\n            Id = new Col<PublicTable, int>(tableName, \"Id\");\n        }\n    }\n\n    private sealed class PublicTableIxCols\n    {\n        public IxCol<PublicTable, int> Id { get; }\n\n        public PublicTableIxCols(string tableName)\n        {\n            Id = new IxCol<PublicTable, int>(tableName, \"Id\");\n        }\n    }\n\n    private static Table<PublicTable, PublicTableCols, PublicTableIxCols> MakeTable()\n    {\n        const string tableName = \"PublicTable\";\n        return new Table<PublicTable, PublicTableCols, PublicTableIxCols>(\n            tableName,\n            new PublicTableCols(tableName),\n            new PublicTableIxCols(tableName)\n        );\n    }\n\n    private static string BuildPublicTableQuerySql() =>\n        MakeTable().Where(cols => cols.Id.Eq(0)).ToSql();\n\n    private static string BuildPublicTableViewSql()\n    {\n        var cols = new PublicTableCols(\"PublicTable\");\n        return MakeTable().Where(_ => cols.Id.Eq(0)).ToSql();\n    }\n\n    private static string BuildFindPublicTableByIdentitySql()\n    {\n        var table = MakeTable();\n        return table.Where(cols => cols.Id.Eq(0)).ToSql();\n    }\n\n    /// <summary>\n    /// Mirrors Module.PublicTableQuery server view to ensure the generated SQL stays in sync.\n    /// </summary>\n    [Conditional(\"DEBUG\")]\n    public static void ValidatePublicTableQuerySql()\n    {\n        var sql = BuildPublicTableQuerySql();\n        Debug.Assert(\n            sql == \"SELECT * FROM \\\"PublicTable\\\" WHERE (\\\"PublicTable\\\".\\\"Id\\\" = 0)\",\n            $\"Unexpected SQL produced for public_table_query: {sql}\"\n        );\n    }\n\n    /// <summary>\n    /// Mirrors Module.PublicTableByIdentity (public_table_view) returning Option<PublicTable>.\n    /// </summary>\n    [Conditional(\"DEBUG\")]\n    public static void ValidatePublicTableViewSql()\n    {\n        var sql = BuildPublicTableViewSql();\n        Debug.Assert(\n            sql == \"SELECT * FROM \\\"PublicTable\\\" WHERE (\\\"PublicTable\\\".\\\"Id\\\" = 0)\",\n            $\"Unexpected SQL produced for public_table_view: {sql}\"\n        );\n    }\n\n    /// <summary>\n    /// Mirrors Module.FindPublicTableByIdentity anonymous view.\n    /// </summary>\n    [Conditional(\"DEBUG\")]\n    public static void ValidateFindPublicTableByIdentitySql()\n    {\n        var sql = BuildFindPublicTableByIdentitySql();\n        Debug.Assert(\n            sql == \"SELECT * FROM \\\"PublicTable\\\" WHERE (\\\"PublicTable\\\".\\\"Id\\\" = 0)\",\n            $\"Unexpected SQL produced for find_public_table__by_identity: {sql}\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/client.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <!-- We want to ensure that BSATN.Codegen on its own produces code compatible with Unity. -->\n    <!-- This means limiting project to .NET Standard 2.1 and C# 9. -->\n    <TargetFramework>netstandard2.1</TargetFramework>\n    <LangVersion>9</LangVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../../BSATN.Runtime/BSATN.Runtime.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/snapshots/Type#CustomClass.verified.cs",
    "content": "﻿//HintName: CustomClass.cs\n// <auto-generated />\n#nullable enable\n\npartial struct CustomClass : System.IEquatable<CustomClass>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntField = BSATN.IntFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomClass {{ IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomClass>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n\n        public CustomClass Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomClass();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomClass value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomClass>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomClass>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        return ___hashIntField ^ ___hashStringField;\n    }\n\n#nullable enable\n    public bool Equals(CustomClass that)\n    {\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        return ___eqIntField && ___eqStringField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomClass?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomClass this_, CustomClass that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomClass this_, CustomClass that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomClass\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/snapshots/Type#CustomStruct.verified.cs",
    "content": "﻿//HintName: CustomStruct.cs\n// <auto-generated />\n#nullable enable\n\npartial struct CustomStruct\n    : System.IEquatable<CustomStruct>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntField = BSATN.IntFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomStruct {{ IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomStruct>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n\n        public CustomStruct Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomStruct();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomStruct value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomStruct>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomStruct>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        return ___hashIntField ^ ___hashStringField;\n    }\n\n#nullable enable\n    public bool Equals(CustomStruct that)\n    {\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        return ___eqIntField && ___eqStringField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomStruct?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomStruct this_, CustomStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomStruct this_, CustomStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomStruct\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/snapshots/Type#CustomTaggedEnum.verified.cs",
    "content": "﻿//HintName: CustomTaggedEnum.cs\n// <auto-generated />\n#nullable enable\n\npartial record CustomTaggedEnum : System.IEquatable<CustomTaggedEnum>\n{\n    public sealed record IntVariant(int IntVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"IntVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(IntVariant_)})\";\n    }\n\n    public sealed record StringVariant(string StringVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"StringVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(StringVariant_)})\";\n    }\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomTaggedEnum>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntVariantRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringVariantRW = new();\n\n        public CustomTaggedEnum Read(System.IO.BinaryReader reader)\n        {\n            return reader.ReadByte() switch\n            {\n                0 => new IntVariant(IntVariantRW.Read(reader)),\n                1 => new StringVariant(StringVariantRW.Read(reader)),\n                _\n                    => throw new System.InvalidOperationException(\n                        \"Invalid tag value, this state should be unreachable.\"\n                    )\n            };\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomTaggedEnum value)\n        {\n            switch (value)\n            {\n                case IntVariant(var inner):\n                    writer.Write((byte)0);\n                    IntVariantRW.Write(writer, inner);\n                    break;\n                case StringVariant(var inner):\n                    writer.Write((byte)1);\n                    StringVariantRW.Write(writer, inner);\n                    break;\n            }\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomTaggedEnum>(_ => new SpacetimeDB.BSATN.AlgebraicType.Sum(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntVariant\", IntVariantRW.GetAlgebraicType(registrar)),\n                    new(\"StringVariant\", StringVariantRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomTaggedEnum>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        switch (this)\n        {\n            case IntVariant(var inner):\n                var ___hashIntVariant = inner.GetHashCode();\n                return ___hashIntVariant;\n            case StringVariant(var inner):\n                var ___hashStringVariant = inner == null ? 0 : inner.GetHashCode();\n                return ___hashStringVariant;\n            default:\n                return 0;\n        }\n    }\n} // CustomTaggedEnum\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/client/snapshots/Type#PublicTable.verified.cs",
    "content": "﻿//HintName: PublicTable.cs\n// <auto-generated />\n#nullable enable\n\npartial struct PublicTable : System.IEquatable<PublicTable>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Id = BSATN.IdRW.Read(reader);\n        ByteField = BSATN.ByteFieldRW.Read(reader);\n        UshortField = BSATN.UshortFieldRW.Read(reader);\n        UintField = BSATN.UintFieldRW.Read(reader);\n        UlongField = BSATN.UlongFieldRW.Read(reader);\n        U128Field = BSATN.U128FieldRW.Read(reader);\n        U256Field = BSATN.U256FieldRW.Read(reader);\n        SbyteField = BSATN.SbyteFieldRW.Read(reader);\n        ShortField = BSATN.ShortFieldRW.Read(reader);\n        IntField = BSATN.IntFieldRW.Read(reader);\n        LongField = BSATN.LongFieldRW.Read(reader);\n        I128Field = BSATN.I128FieldRW.Read(reader);\n        I256Field = BSATN.I256FieldRW.Read(reader);\n        BoolField = BSATN.BoolFieldRW.Read(reader);\n        FloatField = BSATN.FloatFieldRW.Read(reader);\n        DoubleField = BSATN.DoubleFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n        IdentityField = BSATN.IdentityFieldRW.Read(reader);\n        ConnectionIdField = BSATN.ConnectionIdFieldRW.Read(reader);\n        CustomStructField = BSATN.CustomStructFieldRW.Read(reader);\n        CustomClassField = BSATN.CustomClassFieldRW.Read(reader);\n        CustomEnumField = BSATN.CustomEnumFieldRW.Read(reader);\n        CustomTaggedEnumField = BSATN.CustomTaggedEnumFieldRW.Read(reader);\n        ListField = BSATN.ListFieldRW.Read(reader);\n        NullableValueField = BSATN.NullableValueFieldRW.Read(reader);\n        NullableReferenceField = BSATN.NullableReferenceFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdRW.Write(writer, Id);\n        BSATN.ByteFieldRW.Write(writer, ByteField);\n        BSATN.UshortFieldRW.Write(writer, UshortField);\n        BSATN.UintFieldRW.Write(writer, UintField);\n        BSATN.UlongFieldRW.Write(writer, UlongField);\n        BSATN.U128FieldRW.Write(writer, U128Field);\n        BSATN.U256FieldRW.Write(writer, U256Field);\n        BSATN.SbyteFieldRW.Write(writer, SbyteField);\n        BSATN.ShortFieldRW.Write(writer, ShortField);\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.LongFieldRW.Write(writer, LongField);\n        BSATN.I128FieldRW.Write(writer, I128Field);\n        BSATN.I256FieldRW.Write(writer, I256Field);\n        BSATN.BoolFieldRW.Write(writer, BoolField);\n        BSATN.FloatFieldRW.Write(writer, FloatField);\n        BSATN.DoubleFieldRW.Write(writer, DoubleField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n        BSATN.IdentityFieldRW.Write(writer, IdentityField);\n        BSATN.ConnectionIdFieldRW.Write(writer, ConnectionIdField);\n        BSATN.CustomStructFieldRW.Write(writer, CustomStructField);\n        BSATN.CustomClassFieldRW.Write(writer, CustomClassField);\n        BSATN.CustomEnumFieldRW.Write(writer, CustomEnumField);\n        BSATN.CustomTaggedEnumFieldRW.Write(writer, CustomTaggedEnumField);\n        BSATN.ListFieldRW.Write(writer, ListField);\n        BSATN.NullableValueFieldRW.Write(writer, NullableValueField);\n        BSATN.NullableReferenceFieldRW.Write(writer, NullableReferenceField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"PublicTable {{ Id = {SpacetimeDB.BSATN.StringUtil.GenericToString(Id)}, ByteField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ByteField)}, UshortField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UshortField)}, UintField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UintField)}, UlongField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UlongField)}, U128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(U128Field)}, U256Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(U256Field)}, SbyteField = {SpacetimeDB.BSATN.StringUtil.GenericToString(SbyteField)}, ShortField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ShortField)}, IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, LongField = {SpacetimeDB.BSATN.StringUtil.GenericToString(LongField)}, I128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(I128Field)}, I256Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(I256Field)}, BoolField = {SpacetimeDB.BSATN.StringUtil.GenericToString(BoolField)}, FloatField = {SpacetimeDB.BSATN.StringUtil.GenericToString(FloatField)}, DoubleField = {SpacetimeDB.BSATN.StringUtil.GenericToString(DoubleField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)}, IdentityField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IdentityField)}, ConnectionIdField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ConnectionIdField)}, CustomStructField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomStructField)}, CustomClassField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomClassField)}, CustomEnumField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomEnumField)}, CustomTaggedEnumField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomTaggedEnumField)}, ListField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ListField)}, NullableValueField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableValueField)}, NullableReferenceField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableReferenceField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<PublicTable>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IdRW = new();\n        internal static readonly SpacetimeDB.BSATN.U8 ByteFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U16 UshortFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 UintFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U64 UlongFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U128Stdb U128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U256 U256FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I8 SbyteFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I16 ShortFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I64 LongFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I128Stdb I128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I256 I256FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.Bool BoolFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.F32 FloatFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.F64 DoubleFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n        internal static readonly SpacetimeDB.Identity.BSATN IdentityFieldRW = new();\n        internal static readonly SpacetimeDB.ConnectionId.BSATN ConnectionIdFieldRW = new();\n        internal static readonly CustomStruct.BSATN CustomStructFieldRW = new();\n        internal static readonly CustomClass.BSATN CustomClassFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.Enum<CustomEnum> CustomEnumFieldRW = new();\n        internal static readonly CustomTaggedEnum.BSATN CustomTaggedEnumFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.List<int, SpacetimeDB.BSATN.I32> ListFieldRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableValueFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableReferenceFieldRW = new();\n\n        public PublicTable Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new PublicTable();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, PublicTable value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<PublicTable>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Id\", IdRW.GetAlgebraicType(registrar)),\n                    new(\"ByteField\", ByteFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UshortField\", UshortFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UintField\", UintFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UlongField\", UlongFieldRW.GetAlgebraicType(registrar)),\n                    new(\"U128Field\", U128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"U256Field\", U256FieldRW.GetAlgebraicType(registrar)),\n                    new(\"SbyteField\", SbyteFieldRW.GetAlgebraicType(registrar)),\n                    new(\"ShortField\", ShortFieldRW.GetAlgebraicType(registrar)),\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"LongField\", LongFieldRW.GetAlgebraicType(registrar)),\n                    new(\"I128Field\", I128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"I256Field\", I256FieldRW.GetAlgebraicType(registrar)),\n                    new(\"BoolField\", BoolFieldRW.GetAlgebraicType(registrar)),\n                    new(\"FloatField\", FloatFieldRW.GetAlgebraicType(registrar)),\n                    new(\"DoubleField\", DoubleFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar)),\n                    new(\"IdentityField\", IdentityFieldRW.GetAlgebraicType(registrar)),\n                    new(\"ConnectionIdField\", ConnectionIdFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomStructField\", CustomStructFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomClassField\", CustomClassFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomEnumField\", CustomEnumFieldRW.GetAlgebraicType(registrar)),\n                    new(\n                        \"CustomTaggedEnumField\",\n                        CustomTaggedEnumFieldRW.GetAlgebraicType(registrar)\n                    ),\n                    new(\"ListField\", ListFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableValueField\", NullableValueFieldRW.GetAlgebraicType(registrar)),\n                    new(\n                        \"NullableReferenceField\",\n                        NullableReferenceFieldRW.GetAlgebraicType(registrar)\n                    )\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<PublicTable>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashId = Id.GetHashCode();\n        var ___hashByteField = ByteField.GetHashCode();\n        var ___hashUshortField = UshortField.GetHashCode();\n        var ___hashUintField = UintField.GetHashCode();\n        var ___hashUlongField = UlongField.GetHashCode();\n        var ___hashU128Field = U128Field.GetHashCode();\n        var ___hashU256Field = U256Field.GetHashCode();\n        var ___hashSbyteField = SbyteField.GetHashCode();\n        var ___hashShortField = ShortField.GetHashCode();\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashLongField = LongField.GetHashCode();\n        var ___hashI128Field = I128Field.GetHashCode();\n        var ___hashI256Field = I256Field.GetHashCode();\n        var ___hashBoolField = BoolField.GetHashCode();\n        var ___hashFloatField = FloatField.GetHashCode();\n        var ___hashDoubleField = DoubleField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        var ___hashIdentityField = IdentityField.GetHashCode();\n        var ___hashConnectionIdField = ConnectionIdField.GetHashCode();\n        var ___hashCustomStructField = CustomStructField.GetHashCode();\n        var ___hashCustomClassField = CustomClassField.GetHashCode();\n        var ___hashCustomEnumField = CustomEnumField.GetHashCode();\n        var ___hashCustomTaggedEnumField =\n            CustomTaggedEnumField == null ? 0 : CustomTaggedEnumField.GetHashCode();\n        var ___hashListField = 0;\n        if (ListField != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < ListField.Count; ___i0++)\n            {\n                var ___tmp0 = ListField[___i0];\n                var ___out1 = ___tmp0.GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashListField = ___hc0.ToHashCode();\n        }\n        var ___hashNullableValueField = NullableValueField.GetHashCode();\n        var ___hashNullableReferenceField =\n            NullableReferenceField == null ? 0 : NullableReferenceField.GetHashCode();\n        return ___hashId\n            ^ ___hashByteField\n            ^ ___hashUshortField\n            ^ ___hashUintField\n            ^ ___hashUlongField\n            ^ ___hashU128Field\n            ^ ___hashU256Field\n            ^ ___hashSbyteField\n            ^ ___hashShortField\n            ^ ___hashIntField\n            ^ ___hashLongField\n            ^ ___hashI128Field\n            ^ ___hashI256Field\n            ^ ___hashBoolField\n            ^ ___hashFloatField\n            ^ ___hashDoubleField\n            ^ ___hashStringField\n            ^ ___hashIdentityField\n            ^ ___hashConnectionIdField\n            ^ ___hashCustomStructField\n            ^ ___hashCustomClassField\n            ^ ___hashCustomEnumField\n            ^ ___hashCustomTaggedEnumField\n            ^ ___hashListField\n            ^ ___hashNullableValueField\n            ^ ___hashNullableReferenceField;\n    }\n\n#nullable enable\n    public bool Equals(PublicTable that)\n    {\n        var ___eqId = this.Id.Equals(that.Id);\n        var ___eqByteField = this.ByteField.Equals(that.ByteField);\n        var ___eqUshortField = this.UshortField.Equals(that.UshortField);\n        var ___eqUintField = this.UintField.Equals(that.UintField);\n        var ___eqUlongField = this.UlongField.Equals(that.UlongField);\n        var ___eqU128Field = this.U128Field.Equals(that.U128Field);\n        var ___eqU256Field = this.U256Field.Equals(that.U256Field);\n        var ___eqSbyteField = this.SbyteField.Equals(that.SbyteField);\n        var ___eqShortField = this.ShortField.Equals(that.ShortField);\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqLongField = this.LongField.Equals(that.LongField);\n        var ___eqI128Field = this.I128Field.Equals(that.I128Field);\n        var ___eqI256Field = this.I256Field.Equals(that.I256Field);\n        var ___eqBoolField = this.BoolField.Equals(that.BoolField);\n        var ___eqFloatField = this.FloatField.Equals(that.FloatField);\n        var ___eqDoubleField = this.DoubleField.Equals(that.DoubleField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        var ___eqIdentityField = this.IdentityField.Equals(that.IdentityField);\n        var ___eqConnectionIdField = this.ConnectionIdField.Equals(that.ConnectionIdField);\n        var ___eqCustomStructField = this.CustomStructField.Equals(that.CustomStructField);\n        var ___eqCustomClassField = this.CustomClassField.Equals(that.CustomClassField);\n        var ___eqCustomEnumField = this.CustomEnumField == that.CustomEnumField;\n        var ___eqCustomTaggedEnumField =\n            this.CustomTaggedEnumField == null\n                ? that.CustomTaggedEnumField == null\n                : this.CustomTaggedEnumField.Equals(that.CustomTaggedEnumField);\n        var ___eqListField = true;\n        if (this.ListField == null || that.ListField == null)\n        {\n            ___eqListField = this.ListField == that.ListField;\n        }\n        else if (this.ListField.Count != that.ListField.Count)\n        {\n            ___eqListField = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.ListField.Count; ___i0++)\n            {\n                var ___tmpA0 = this.ListField[___i0];\n                var ___tmpB0 = that.ListField[___i0];\n                var ___out1 = ___tmpA0.Equals(___tmpB0);\n                if (!___out1)\n                {\n                    ___eqListField = false;\n                    break;\n                }\n            }\n        }\n        var ___eqNullableValueField = System.Nullable.Equals(\n            this.NullableValueField,\n            that.NullableValueField\n        );\n        var ___eqNullableReferenceField =\n            this.NullableReferenceField == null\n                ? that.NullableReferenceField == null\n                : this.NullableReferenceField.Equals(that.NullableReferenceField);\n        return ___eqId\n            && ___eqByteField\n            && ___eqUshortField\n            && ___eqUintField\n            && ___eqUlongField\n            && ___eqU128Field\n            && ___eqU256Field\n            && ___eqSbyteField\n            && ___eqShortField\n            && ___eqIntField\n            && ___eqLongField\n            && ___eqI128Field\n            && ___eqI256Field\n            && ___eqBoolField\n            && ___eqFloatField\n            && ___eqDoubleField\n            && ___eqStringField\n            && ___eqIdentityField\n            && ___eqConnectionIdField\n            && ___eqCustomStructField\n            && ___eqCustomClassField\n            && ___eqCustomEnumField\n            && ___eqCustomTaggedEnumField\n            && ___eqListField\n            && ___eqNullableValueField\n            && ___eqNullableReferenceField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as PublicTable?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(PublicTable this_, PublicTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(PublicTable this_, PublicTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // PublicTable\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/.gitattributes",
    "content": "/snapshots/*.verified.cs linguist-generated=true eol=lf\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs",
    "content": "using System.Collections.Generic;\nusing System.ComponentModel;\nusing SpacetimeDB;\n\npublic enum LocalEnum { }\n\n[SpacetimeDB.Type]\npublic partial struct TestUnsupportedType\n{\n    public DateTime UnsupportedSpecialType;\n    public Exception UnsupportedSystemType;\n    public UnresolvedType UnresolvedType;\n    public LocalEnum UnsupportedEnum;\n}\n\n[SpacetimeDB.Type]\npublic enum TestEnumWithExplicitValues\n{\n    EnumVariant1 = 1,\n    EnumVariant2 = 2,\n}\n\n[SpacetimeDB.Type]\npublic enum TestEnumWithTooManyVariants\n{\n    EnumVariant1,\n    EnumVariant2,\n    EnumVariant3,\n    EnumVariant4,\n    EnumVariant5,\n    EnumVariant6,\n    EnumVariant7,\n    EnumVariant8,\n    EnumVariant9,\n    EnumVariant10,\n    EnumVariant11,\n    EnumVariant12,\n    EnumVariant13,\n    EnumVariant14,\n    EnumVariant15,\n    EnumVariant16,\n    EnumVariant17,\n    EnumVariant18,\n    EnumVariant19,\n    EnumVariant20,\n    EnumVariant21,\n    EnumVariant22,\n    EnumVariant23,\n    EnumVariant24,\n    EnumVariant25,\n    EnumVariant26,\n    EnumVariant27,\n    EnumVariant28,\n    EnumVariant29,\n    EnumVariant30,\n    EnumVariant31,\n    EnumVariant32,\n    EnumVariant33,\n    EnumVariant34,\n    EnumVariant35,\n    EnumVariant36,\n    EnumVariant37,\n    EnumVariant38,\n    EnumVariant39,\n    EnumVariant40,\n    EnumVariant41,\n    EnumVariant42,\n    EnumVariant43,\n    EnumVariant44,\n    EnumVariant45,\n    EnumVariant46,\n    EnumVariant47,\n    EnumVariant48,\n    EnumVariant49,\n    EnumVariant50,\n    EnumVariant51,\n    EnumVariant52,\n    EnumVariant53,\n    EnumVariant54,\n    EnumVariant55,\n    EnumVariant56,\n    EnumVariant57,\n    EnumVariant58,\n    EnumVariant59,\n    EnumVariant60,\n    EnumVariant61,\n    EnumVariant62,\n    EnumVariant63,\n    EnumVariant64,\n    EnumVariant65,\n    EnumVariant66,\n    EnumVariant67,\n    EnumVariant68,\n    EnumVariant69,\n    EnumVariant70,\n    EnumVariant71,\n    EnumVariant72,\n    EnumVariant73,\n    EnumVariant74,\n    EnumVariant75,\n    EnumVariant76,\n    EnumVariant77,\n    EnumVariant78,\n    EnumVariant79,\n    EnumVariant80,\n    EnumVariant81,\n    EnumVariant82,\n    EnumVariant83,\n    EnumVariant84,\n    EnumVariant85,\n    EnumVariant86,\n    EnumVariant87,\n    EnumVariant88,\n    EnumVariant89,\n    EnumVariant90,\n    EnumVariant91,\n    EnumVariant92,\n    EnumVariant93,\n    EnumVariant94,\n    EnumVariant95,\n    EnumVariant96,\n    EnumVariant97,\n    EnumVariant98,\n    EnumVariant99,\n    EnumVariant100,\n    EnumVariant101,\n    EnumVariant102,\n    EnumVariant103,\n    EnumVariant104,\n    EnumVariant105,\n    EnumVariant106,\n    EnumVariant107,\n    EnumVariant108,\n    EnumVariant109,\n    EnumVariant110,\n    EnumVariant111,\n    EnumVariant112,\n    EnumVariant113,\n    EnumVariant114,\n    EnumVariant115,\n    EnumVariant116,\n    EnumVariant117,\n    EnumVariant118,\n    EnumVariant119,\n    EnumVariant120,\n    EnumVariant121,\n    EnumVariant122,\n    EnumVariant123,\n    EnumVariant124,\n    EnumVariant125,\n    EnumVariant126,\n    EnumVariant127,\n    EnumVariant128,\n    EnumVariant129,\n    EnumVariant130,\n    EnumVariant131,\n    EnumVariant132,\n    EnumVariant133,\n    EnumVariant134,\n    EnumVariant135,\n    EnumVariant136,\n    EnumVariant137,\n    EnumVariant138,\n    EnumVariant139,\n    EnumVariant140,\n    EnumVariant141,\n    EnumVariant142,\n    EnumVariant143,\n    EnumVariant144,\n    EnumVariant145,\n    EnumVariant146,\n    EnumVariant147,\n    EnumVariant148,\n    EnumVariant149,\n    EnumVariant150,\n    EnumVariant151,\n    EnumVariant152,\n    EnumVariant153,\n    EnumVariant154,\n    EnumVariant155,\n    EnumVariant156,\n    EnumVariant157,\n    EnumVariant158,\n    EnumVariant159,\n    EnumVariant160,\n    EnumVariant161,\n    EnumVariant162,\n    EnumVariant163,\n    EnumVariant164,\n    EnumVariant165,\n    EnumVariant166,\n    EnumVariant167,\n    EnumVariant168,\n    EnumVariant169,\n    EnumVariant170,\n    EnumVariant171,\n    EnumVariant172,\n    EnumVariant173,\n    EnumVariant174,\n    EnumVariant175,\n    EnumVariant176,\n    EnumVariant177,\n    EnumVariant178,\n    EnumVariant179,\n    EnumVariant180,\n    EnumVariant181,\n    EnumVariant182,\n    EnumVariant183,\n    EnumVariant184,\n    EnumVariant185,\n    EnumVariant186,\n    EnumVariant187,\n    EnumVariant188,\n    EnumVariant189,\n    EnumVariant190,\n    EnumVariant191,\n    EnumVariant192,\n    EnumVariant193,\n    EnumVariant194,\n    EnumVariant195,\n    EnumVariant196,\n    EnumVariant197,\n    EnumVariant198,\n    EnumVariant199,\n    EnumVariant200,\n    EnumVariant201,\n    EnumVariant202,\n    EnumVariant203,\n    EnumVariant204,\n    EnumVariant205,\n    EnumVariant206,\n    EnumVariant207,\n    EnumVariant208,\n    EnumVariant209,\n    EnumVariant210,\n    EnumVariant211,\n    EnumVariant212,\n    EnumVariant213,\n    EnumVariant214,\n    EnumVariant215,\n    EnumVariant216,\n    EnumVariant217,\n    EnumVariant218,\n    EnumVariant219,\n    EnumVariant220,\n    EnumVariant221,\n    EnumVariant222,\n    EnumVariant223,\n    EnumVariant224,\n    EnumVariant225,\n    EnumVariant226,\n    EnumVariant227,\n    EnumVariant228,\n    EnumVariant229,\n    EnumVariant230,\n    EnumVariant231,\n    EnumVariant232,\n    EnumVariant233,\n    EnumVariant234,\n    EnumVariant235,\n    EnumVariant236,\n    EnumVariant237,\n    EnumVariant238,\n    EnumVariant239,\n    EnumVariant240,\n    EnumVariant241,\n    EnumVariant242,\n    EnumVariant243,\n    EnumVariant244,\n    EnumVariant245,\n    EnumVariant246,\n    EnumVariant247,\n    EnumVariant248,\n    EnumVariant249,\n    EnumVariant250,\n    EnumVariant251,\n    EnumVariant252,\n    EnumVariant253,\n    EnumVariant254,\n    EnumVariant255,\n    EnumVariant256,\n    EnumVariant257,\n}\n\n[SpacetimeDB.Type]\npublic partial record TestTaggedEnumInlineTuple : SpacetimeDB.TaggedEnum<ValueTuple<int>>\n{\n    public int ForbiddenTaggedEnumField;\n}\n\n[SpacetimeDB.Type]\npublic partial record TestTaggedEnumField : SpacetimeDB.TaggedEnum<(int X, int Y)>\n{\n    public int ForbiddenField;\n}\n\n[SpacetimeDB.Type]\npublic partial struct TestTypeParams<T>\n{\n    public T Field;\n}\n\npublic static partial class Reducers\n{\n    [SpacetimeDB.Reducer]\n    public static int TestReducerReturnType(ReducerContext ctx) => 0;\n\n    [SpacetimeDB.Reducer]\n    public static void TestReducerWithoutContext() { }\n\n    [SpacetimeDB.Reducer(ReducerKind.Init)]\n    public static void TestDuplicateReducerKind1(ReducerContext ctx) { }\n\n    [SpacetimeDB.Reducer(ReducerKind.Init)]\n    public static void TestDuplicateReducerKind2(ReducerContext ctx) { }\n\n    [SpacetimeDB.Reducer]\n    public static void TestDuplicateReducerName(ReducerContext ctx) { }\n\n    public static partial class InAnotherNamespace\n    {\n        [SpacetimeDB.Reducer]\n        public static void TestDuplicateReducerName(ReducerContext ctx) { }\n    }\n\n    [SpacetimeDB.Reducer]\n    public static void OnReducerWithReservedPrefix(ReducerContext ctx) { }\n\n    [SpacetimeDB.Reducer]\n    public static void __ReducerWithReservedPrefix(ReducerContext ctx) { }\n}\n\n[SpacetimeDB.Table]\npublic partial struct TestAutoIncNotInteger\n{\n    [AutoInc]\n    public float AutoIncField;\n\n    [Unique]\n    [AutoInc]\n    public string IdentityField;\n}\n\n[SpacetimeDB.Table]\npublic partial struct TestUniqueNotEquatable\n{\n    [Unique]\n    public int? UniqueField;\n\n    [PrimaryKey]\n    public TestEnumWithExplicitValues PrimaryKeyField;\n}\n\n[SpacetimeDB.Table]\npublic partial record TestTableTaggedEnum : SpacetimeDB.TaggedEnum<(int X, int Y)> { }\n\n[SpacetimeDB.Table]\npublic partial struct TestDuplicateTableName { }\n\npublic static partial class InAnotherNamespace\n{\n    [SpacetimeDB.Table]\n    public partial struct TestDuplicateTableName { }\n}\n\n[SpacetimeDB.Table]\npublic partial struct TestDefaultFieldValues\n{\n    [Unique]\n    public int? UniqueField;\n\n    [Default(\"A default string set by attribute\")]\n    public string DefaultString = \"\";\n\n    [Default(true)]\n    public bool DefaultBool = false;\n\n    [Default((sbyte)2)]\n    public sbyte DefaultI8 = 1;\n\n    [Default((byte)2)]\n    public byte DefaultU8 = 1;\n\n    [Default((short)2)]\n    public short DefaultI16 = 1;\n\n    [Default((ushort)2)]\n    public ushort DefaultU16 = 1;\n\n    [Default(2)]\n    public int DefaultI32 = 1;\n\n    [Default(2U)]\n    public uint DefaultU32 = 1U;\n\n    [Default(2L)]\n    public long DefaultI64 = 1L;\n\n    [Default(2UL)]\n    public ulong DefaultU64 = 1UL;\n\n    [Default(0x02)]\n    public int DefaultHex = 1;\n\n    [Default(0b00000010)]\n    public int DefaultBin = 1;\n\n    [Default(2.0f)]\n    public float DefaultF32 = 1.0f;\n\n    [Default(2.0)]\n    public double DefaultF64 = 1.0;\n\n    [Default(MyEnum.SetByAttribute)]\n    public MyEnum DefaultEnum = MyEnum.SetByInitalization;\n\n    [Default(null!)]\n    public MyStruct? DefaultNull = new MyStruct(1);\n}\n\n[SpacetimeDB.Type]\npublic enum MyEnum\n{\n    Default,\n    SetByInitalization,\n    SetByAttribute,\n}\n\n[SpacetimeDB.Type]\npublic partial struct MyStruct\n{\n    public int x;\n\n    public MyStruct(int x)\n    {\n        this.x = x;\n    }\n}\n\n[SpacetimeDB.Table]\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithoutColumns\")]\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithEmptyColumns\", Columns = [])]\n[SpacetimeDB.Index.BTree(Accessor = \"TestUnknownColumns\", Columns = [\"UnknownColumn\"])]\n[SpacetimeDB.Index.BTree(Columns = [\"SelfIndexingColumn\"])]\n[SpacetimeDB.Index.BTree(\n    Name = \"TestCanonicalNameWithoutAccessor\",\n    Columns = [\"SecondaryIndexingColumn\"]\n)]\npublic partial struct TestIndexIssues\n{\n    [SpacetimeDB.Index.BTree(Accessor = \"TestUnexpectedColumns\", Columns = [\"UnexpectedColumn\"])]\n    public int SelfIndexingColumn;\n\n    public int SecondaryIndexingColumn;\n}\n\n[SpacetimeDB.Table(\n    Accessor = \"TestScheduleWithoutPrimaryKey\",\n    Scheduled = \"DummyScheduledReducer\",\n    ScheduledAt = nameof(ScheduleAtCorrectType)\n)]\n[SpacetimeDB.Table(\n    Accessor = \"TestScheduleWithWrongPrimaryKeyType\",\n    Scheduled = \"DummyScheduledReducer\",\n    ScheduledAt = nameof(ScheduleAtCorrectType)\n)]\n[SpacetimeDB.Table(Accessor = \"TestScheduleWithoutScheduleAt\", Scheduled = \"DummyScheduledReducer\")]\n[SpacetimeDB.Table(\n    Accessor = \"TestScheduleWithWrongScheduleAtType\",\n    Scheduled = \"DummyScheduledReducer\",\n    ScheduledAt = nameof(ScheduleAtWrongType)\n)]\n[SpacetimeDB.Table(\n    Accessor = \"TestScheduleWithMissingScheduleAtField\",\n    Scheduled = \"DummyScheduledReducer\",\n    ScheduledAt = \"MissingField\"\n)]\npublic partial struct TestScheduleIssues\n{\n    [SpacetimeDB.PrimaryKey(Table = \"TestScheduleWithWrongPrimaryKeyType\")]\n    public string IdWrongType;\n\n    [SpacetimeDB.PrimaryKey(Table = \"TestScheduleWithoutScheduleAt\")]\n    [SpacetimeDB.PrimaryKey(Table = \"TestScheduleWithWrongScheduleAtType\")]\n    public int IdCorrectType;\n\n    public int ScheduleAtWrongType;\n    public ScheduleAt ScheduleAtCorrectType;\n\n    [SpacetimeDB.Reducer]\n    public static void DummyScheduledReducer(ReducerContext ctx, TestScheduleIssues table) { }\n}\n\n[SpacetimeDB.Table]\npublic partial struct Player\n{\n    [Unique]\n    public Identity Identity;\n}\n\npublic struct NotSpacetimeType { }\n\npublic partial class Module\n{\n#pragma warning disable STDB_UNSTABLE // Enable ClientVisibilityFilter\n\n    // Invalid: not public static readonly\n    [SpacetimeDB.ClientVisibilityFilter]\n    private Filter MY_FILTER = new Filter.Sql(\"SELECT * FROM TestAutoIncNotInteger\");\n\n    // Invalid: not public static readonly\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static Filter MY_SECOND_FILTER = new Filter.Sql(\"SELECT * FROM TestAutoIncNotInteger\");\n\n    // Invalid: not a Filter\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static readonly string MY_THIRD_FILTER = \"SELECT * FROM TestAutoIncNotInteger\";\n\n#pragma warning restore STDB_UNSTABLE // Disable ClientVisibilityFilter\n\n    // Valid Filter, but [ClientVisibilityFilter] is disabled\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static readonly Filter MY_FOURTH_FILTER = new Filter.Sql(\n        \"SELECT * FROM TestAutoIncNotInteger\"\n    );\n\n    // Invalid: View definition missing Public=true\n    [SpacetimeDB.View(Accessor = \"view_def_no_public\")]\n    public static List<Player> ViewDefNoPublic(ViewContext ctx)\n    {\n        return new List<Player>();\n    }\n\n    // Invalid: View definition with missing context type\n    [SpacetimeDB.View(Accessor = \"view_def_no_context\", Public = true)]\n    public static List<Player> ViewDefNoContext()\n    {\n        return new List<Player>();\n    }\n\n    // Invalid: View definition with wrong context type\n    [SpacetimeDB.View(Accessor = \"view_def_wrong_context\", Public = true)]\n    public static List<Player> ViewDefWrongContext(ReducerContext ctx)\n    {\n        return new List<Player>();\n    }\n\n    // Invalid: View that performs Insert\n    [SpacetimeDB.View(Accessor = \"view_no_insert\", Public = true)]\n    public static Player? ViewNoInsert(ViewContext ctx)\n    {\n        ctx.Db.Player.Insert(new Player { Identity = new() });\n        return new Player { Identity = new() };\n    }\n\n    // Invalid: View that performs Delete\n    [SpacetimeDB.View(Accessor = \"view_no_delete\", Public = true)]\n    public static Player? ViewNoDelete(ViewContext ctx)\n    {\n        ctx.Db.Player.Delete(new Player { Identity = new() });\n        return null;\n    }\n\n    // TODO: Investigate why void return breaks the FFI generation\n    // // Invalid: Void return type is not List<T> or T?\n    // [SpacetimeDB.View(Accessor = \"view_def_no_return\", Public = true)]\n    // public static void ViewDefNoReturn(ViewContext ctx)\n    // {\n    //     return;\n    // }\n\n    // Invalid: Wrong return type is not List<T> or T?\n    [SpacetimeDB.View(Accessor = \"view_def_wrong_return\", Public = true)]\n    public static Player ViewDefWrongReturn(ViewContext ctx)\n    {\n        return new Player { Identity = new() };\n    }\n\n    // Invalid: IEnumerable<T> return type (from Iter()) is not List<T> or T?\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_iter\", Public = true)]\n    public static IEnumerable<Player> ViewDefIEnumerableReturnFromIter(ViewContext ctx)\n    {\n        return ctx.Db.Player.Iter();\n    }\n\n    // Invalid: IEnumerable<T> return type (from Filter()) is not List<T> or T?\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_filter\", Public = true)]\n    public static IEnumerable<TestScheduleIssues> ViewDefIEnumerableReturnFromFilter(\n        ViewContext ctx\n    )\n    {\n        return ctx.Db.TestIndexIssues.TestUnexpectedColumns.Filter(0);\n    }\n\n    // Invalid: Returns type that is not a SpacetimeType\n    [SpacetimeDB.View(Accessor = \"view_def_returns_not_a_spacetime_type\", Public = true)]\n    public static NotSpacetimeType? ViewDefReturnsNotASpacetimeType(AnonymousViewContext ctx)\n    {\n        return new NotSpacetimeType();\n    }\n\n    [SpacetimeDB.View(Accessor = \"view_def_no_anon_identity\", Public = true)]\n    public static Player? ViewDefNoAnonIdentity(AnonymousViewContext ctx)\n    {\n        ctx.GetIdentity();\n        return null;\n    }\n\n    [SpacetimeDB.View(Accessor = \"view_def_no_iter\", Public = true)]\n    public static Player? ViewDefNoIter(AnonymousViewContext ctx)\n    {\n        ctx.Db.Player.Iter();\n        return null;\n    }\n\n    [SpacetimeDB.View(Accessor = \"view_def_index_no_mutation\", Public = true)]\n    public static Player? ViewDefIndexNoMutation(AnonymousViewContext ctx)\n    {\n        ctx.Db.Player.Identity.Delete(0);\n        return null;\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/diag.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../../BSATN.Runtime/BSATN.Runtime.csproj\" />\n    <ProjectReference Include=\"../../../Runtime/Runtime.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt",
    "content": "﻿[\n  {/*\n    {\n        ctx.GetIdentity();\n            ^^^^^^^^^^^\n        return null;\n*/\n    Message: 'AnonymousViewContext' does not contain a definition for 'GetIdentity' and no accessible extension method 'GetIdentity' accepting a first argument of type 'AnonymousViewContext' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\nSpacetimeDB.Internal.Module.RegisterTable<global::TestUniqueNotEquatable, SpacetimeDB.Internal.TableHandles.TestUniqueNotEquatable>();\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FILTER);\n                                                                                  ^^^^^^^^^\nSpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FOURTH_FILTER);\n*/\n    Message: 'Module.MY_FILTER' is inaccessible due to its protection level,\n    Severity: Error,\n    Descriptor: {\n      Id: CS0122,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0122),\n      MessageFormat: '{0}' is inaccessible due to its protection level,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        ctx.Db.Player.Identity.Delete(0);\n                               ^^^^^^\n        return null;\n*/\n    Message: 'PlayerReadOnly.IdentityIndex' does not contain a definition for 'Delete' and no accessible extension method 'Delete' accepting a first argument of type 'PlayerReadOnly.IdentityIndex' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        ctx.Db.Player.Delete(new Player { Identity = new() });\n                      ^^^^^^\n        return null;\n*/\n    Message: 'PlayerReadOnly' does not contain a definition for 'Delete' and no accessible extension method 'Delete' accepting a first argument of type 'PlayerReadOnly' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        ctx.Db.Player.Insert(new Player { Identity = new() });\n                      ^^^^^^\n        return new Player { Identity = new() };\n*/\n    Message: 'PlayerReadOnly' does not contain a definition for 'Insert' and no accessible extension method 'Insert' accepting a first argument of type 'PlayerReadOnly' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        return ctx.Db.Player.Iter();\n                             ^^^^\n    }\n*/\n    Message: 'PlayerReadOnly' does not contain a definition for 'Iter' and no accessible extension method 'Iter' accepting a first argument of type 'PlayerReadOnly' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        ctx.Db.Player.Iter();\n                      ^^^^\n        return null;\n*/\n    Message: 'PlayerReadOnly' does not contain a definition for 'Iter' and no accessible extension method 'Iter' accepting a first argument of type 'PlayerReadOnly' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    // Valid Filter, but [ClientVisibilityFilter] is disabled\n    [SpacetimeDB.ClientVisibilityFilter]\n     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    public static readonly Filter MY_FOURTH_FILTER = new Filter.Sql(\n*/\n    Message: 'SpacetimeDB.ClientVisibilityFilterAttribute' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.,\n    Severity: Error,\n    Descriptor: {\n      Id: STDB_UNSTABLE,\n      Title: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204),\n      MessageFormat: '{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.,\n      Category: Compiler,\n      DefaultSeverity: Warning,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        CustomObsolete\n      ]\n    }\n  },\n  {/*\n\npartial struct TestTypeParams<T>  : System.IEquatable<TestTypeParams>, SpacetimeDB.BSATN.IStructuralReadWrite {\n                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n*/\n    Message: 'TestTypeParams<T>' does not implement interface member 'IEquatable<TestTypeParams>.Equals(TestTypeParams?)',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0535,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0535),\n      MessageFormat: '{0}' does not implement interface member '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\nvar ___hashUnsupportedSystemType = UnsupportedSystemType == null ? 0 : UnsupportedSystemType.GetHashCode();\nvar ___hashUnresolvedType = UnresolvedType == null ? 0 : UnresolvedType.GetHashCode();\n                                                                        ^^^^^^^^^^^\nvar ___hashUnsupportedEnum = UnsupportedEnum.GetHashCode();\n*/\n    Message: 'UnresolvedType' does not contain a definition for 'GetHashCode' and no accessible extension method 'GetHashCode' accepting a first argument of type 'UnresolvedType' could be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS1061,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),\n      MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n[SpacetimeDB.Table]\npublic partial struct TestDefaultFieldValues\n                      ^^^^^^^^^^^^^^^^^^^^^^\n{\n*/\n    Message: A 'struct' with field initializers must include an explicitly declared constructor.,\n    Severity: Error,\n    Descriptor: {\n      Id: CS8983,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS8983),\n      MessageFormat: A 'struct' with field initializers must include an explicitly declared constructor.,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n            \n            var returnValue = Module.ViewDefWrongContext((SpacetimeDB.ViewContext)ctx);\n                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n                SpacetimeDB.BSATN.List<Player, Player.BSATN> returnRW = new();\n*/\n    Message: Argument 1: cannot convert from 'SpacetimeDB.ViewContext' to 'SpacetimeDB.ReducerContext',\n    Severity: Error,\n    Descriptor: {\n      Id: CS1503,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1503),\n      MessageFormat: Argument {0}: cannot convert from '{1}' to '{2}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\nSpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_SECOND_FILTER);\nSpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_THIRD_FILTER);\n                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n        {\n*/\n    Message: Argument 1: cannot convert from 'string' to 'SpacetimeDB.Filter',\n    Severity: Error,\n    Descriptor: {\n      Id: CS1503,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1503),\n      MessageFormat: Argument {0}: cannot convert from '{1}' to '{2}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        return ctx.Db.TestIndexIssues.TestUnexpectedColumns.Filter(0);\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    }\n*/\n    Message: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<TestIndexIssues>' to 'System.Collections.Generic.IEnumerable<TestScheduleIssues>'. An explicit conversion exists (are you missing a cast?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS0266,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0266),\n      MessageFormat: Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n            \n            var returnValue = Module.ViewDefNoContext((SpacetimeDB.ViewContext)ctx);\n                                     ^^^^^^^^^^^^^^^^\n                SpacetimeDB.BSATN.List<Player, Player.BSATN> returnRW = new();\n*/\n    Message: No overload for method 'ViewDefNoContext' takes 1 arguments,\n    Severity: Error,\n    Descriptor: {\n      Id: CS1501,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1501),\n      MessageFormat: No overload for method '{0}' takes {1} arguments,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    public global::SpacetimeDB.Table<global::InAnotherNamespace.TestDuplicateTableName, TestDuplicateTableNameCols, TestDuplicateTableNameIxCols> TestDuplicateTableName() =>\n        new(\"TestDuplicateTableName\", new TestDuplicateTableNameCols(\"TestDuplicateTableName\"), new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\"));\n                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n    Message: The call is ambiguous between the following methods or properties: 'TestDuplicateTableNameCols.TestDuplicateTableNameCols(string)' and 'TestDuplicateTableNameCols.TestDuplicateTableNameCols(string)',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0121,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0121),\n      MessageFormat: The call is ambiguous between the following methods or properties: '{0}' and '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    public global::SpacetimeDB.Table<global::TestDuplicateTableName, TestDuplicateTableNameCols, TestDuplicateTableNameIxCols> TestDuplicateTableName() =>\n        new(\"TestDuplicateTableName\", new TestDuplicateTableNameCols(\"TestDuplicateTableName\"), new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\"));\n                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n    Message: The call is ambiguous between the following methods or properties: 'TestDuplicateTableNameCols.TestDuplicateTableNameCols(string)' and 'TestDuplicateTableNameCols.TestDuplicateTableNameCols(string)',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0121,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0121),\n      MessageFormat: The call is ambiguous between the following methods or properties: '{0}' and '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    public global::SpacetimeDB.Table<global::InAnotherNamespace.TestDuplicateTableName, TestDuplicateTableNameCols, TestDuplicateTableNameIxCols> TestDuplicateTableName() =>\n        new(\"TestDuplicateTableName\", new TestDuplicateTableNameCols(\"TestDuplicateTableName\"), new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\"));\n                                                                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n    Message: The call is ambiguous between the following methods or properties: 'TestDuplicateTableNameIxCols.TestDuplicateTableNameIxCols(string)' and 'TestDuplicateTableNameIxCols.TestDuplicateTableNameIxCols(string)',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0121,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0121),\n      MessageFormat: The call is ambiguous between the following methods or properties: '{0}' and '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    public global::SpacetimeDB.Table<global::TestDuplicateTableName, TestDuplicateTableNameCols, TestDuplicateTableNameIxCols> TestDuplicateTableName() =>\n        new(\"TestDuplicateTableName\", new TestDuplicateTableNameCols(\"TestDuplicateTableName\"), new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\"));\n                                                                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n    Message: The call is ambiguous between the following methods or properties: 'TestDuplicateTableNameIxCols.TestDuplicateTableNameIxCols(string)' and 'TestDuplicateTableNameIxCols.TestDuplicateTableNameIxCols(string)',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0121,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0121),\n      MessageFormat: The call is ambiguous between the following methods or properties: '{0}' and '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n}\npublic readonly struct TestDuplicateTableNameCols\n                       ^^^^^^^^^^^^^^^^^^^^^^^^^^\n{\n*/\n    Message: The namespace 'SpacetimeDB' already contains a definition for 'TestDuplicateTableNameCols',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0101,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0101),\n      MessageFormat: The namespace '{1}' already contains a definition for '{0}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n\npublic readonly struct TestDuplicateTableNameIxCols\n                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n{\n*/\n    Message: The namespace 'SpacetimeDB' already contains a definition for 'TestDuplicateTableNameIxCols',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0101,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0101),\n      MessageFormat: The namespace '{1}' already contains a definition for '{0}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    Params: [],\n    ReturnType: new SpacetimeDB.BSATN.ValueOption<NotSpacetimeType, NotSpacetimeType.BSATN>().GetAlgebraicType(registrar)\n                                                                                     ^^^^^\n);\n*/\n    Message: The type name 'BSATN' does not exist in the type 'NotSpacetimeType',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0426,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0426),\n      MessageFormat: The type name '{0}' does not exist in the type '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n            var returnValue = Module.ViewDefReturnsNotASpacetimeType((SpacetimeDB.AnonymousViewContext)ctx);\n                var listSerializer = SpacetimeDB.BSATN.ValueOption<NotSpacetimeType, NotSpacetimeType.BSATN>.GetListSerializer();\n                                                                                                      ^^^^^\n    var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n*/\n    Message: The type name 'BSATN' does not exist in the type 'NotSpacetimeType',\n    Severity: Error,\n    Descriptor: {\n      Id: CS0426,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0426),\n      MessageFormat: The type name '{0}' does not exist in the type '{1}',\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    {\n        internal static readonly TRW FieldRW = new();\n                                 ^^^\n\n*/\n    Message: The type or namespace name 'TRW' could not be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS0246,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0246),\n      MessageFormat: The type or namespace name '{0}' could not be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n    public Exception UnsupportedSystemType;\n    public UnresolvedType UnresolvedType;\n           ^^^^^^^^^^^^^^\n    public LocalEnum UnsupportedEnum;\n*/\n    Message: The type or namespace name 'UnresolvedType' could not be found (are you missing a using directive or an assembly reference?),\n    Severity: Error,\n    Descriptor: {\n      Id: CS0246,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0246),\n      MessageFormat: The type or namespace name '{0}' could not be found (are you missing a using directive or an assembly reference?),\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n{\n    public global::SpacetimeDB.Table<global::TestDuplicateTableName, TestDuplicateTableNameCols, TestDuplicateTableNameIxCols> TestDuplicateTableName() =>\n                                                                                                                               ^^^^^^^^^^^^^^^^^^^^^^\n        new(\"TestDuplicateTableName\", new TestDuplicateTableNameCols(\"TestDuplicateTableName\"), new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\"));\n*/\n    Message: Type 'QueryBuilder' already defines a member called 'TestDuplicateTableName' with the same parameter types,\n    Severity: Error,\n    Descriptor: {\n      Id: CS0111,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0111),\n      MessageFormat: Type '{1}' already defines a member called '{0}' with the same parameter types,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n\n    internal TestDuplicateTableNameCols(string tableName)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^\n    {\n*/\n    Message: Type 'TestDuplicateTableNameCols' already defines a member called 'TestDuplicateTableNameCols' with the same parameter types,\n    Severity: Error,\n    Descriptor: {\n      Id: CS0111,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0111),\n      MessageFormat: Type '{1}' already defines a member called '{0}' with the same parameter types,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n\n    internal TestDuplicateTableNameIxCols(string tableName)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    {\n*/\n    Message: Type 'TestDuplicateTableNameIxCols' already defines a member called 'TestDuplicateTableNameIxCols' with the same parameter types,\n    Severity: Error,\n    Descriptor: {\n      Id: CS0111,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0111),\n      MessageFormat: Type '{1}' already defines a member called '{0}' with the same parameter types,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  },\n  {/*\n\npartial struct TestTypeParams<T>  : System.IEquatable<TestTypeParams>, SpacetimeDB.BSATN.IStructuralReadWrite {\n                                                      ^^^^^^^^^^^^^^\n\n*/\n    Message: Using the generic type 'TestTypeParams<T>' requires 1 type arguments,\n    Severity: Error,\n    Descriptor: {\n      Id: CS0305,\n      Title: ,\n      HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0305),\n      MessageFormat: Using the generic {1} '{0}' requires {2} type arguments,\n      Category: Compiler,\n      DefaultSeverity: Error,\n      IsEnabledByDefault: true,\n      CustomTags: [\n        Compiler,\n        Telemetry,\n        NotConfigurable\n      ]\n    }\n  }\n]"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs",
    "content": "﻿//HintName: FFI.cs\n// <auto-generated />\n#nullable enable\n// The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\\Internal\\Module.cs as an empty partial type.\n// This is needed so every module build doesn't generate a full LocalReadOnly type, but just adds on to the existing.\n// We extend it here with generated table accessors, and just need to suppress the duplicate-type warning.\n#pragma warning disable CS0436\n#pragma warning disable STDB_UNSTABLE\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing Internal = SpacetimeDB.Internal;\nusing TxContext = SpacetimeDB.Internal.TxContext;\n\nnamespace SpacetimeDB\n{\n    public readonly struct TestDuplicateTableNameCols\n    {\n        internal TestDuplicateTableNameCols(string tableName) { }\n    }\n\n    public readonly struct TestDuplicateTableNameIxCols\n    {\n        internal TestDuplicateTableNameIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::InAnotherNamespace.TestDuplicateTableName,\n            TestDuplicateTableNameCols,\n            TestDuplicateTableNameIxCols\n        > TestDuplicateTableName() =>\n            new(\n                \"TestDuplicateTableName\",\n                new TestDuplicateTableNameCols(\"TestDuplicateTableName\"),\n                new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\")\n            );\n    }\n\n    public readonly struct PlayerCols\n    {\n        public readonly global::SpacetimeDB.Col<global::Player, SpacetimeDB.Identity> Identity;\n\n        internal PlayerCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.Col<global::Player, SpacetimeDB.Identity>(\n                tableName,\n                \"Identity\"\n            );\n        }\n    }\n\n    public readonly struct PlayerIxCols\n    {\n        internal PlayerIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<global::Player, PlayerCols, PlayerIxCols> Player() =>\n            new(\"Player\", new PlayerCols(\"Player\"), new PlayerIxCols(\"Player\"));\n    }\n\n    public readonly struct TestAutoIncNotIntegerCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestAutoIncNotInteger, float> AutoIncField;\n        public readonly global::SpacetimeDB.Col<\n            global::TestAutoIncNotInteger,\n            string\n        > IdentityField;\n\n        internal TestAutoIncNotIntegerCols(string tableName)\n        {\n            AutoIncField = new global::SpacetimeDB.Col<global::TestAutoIncNotInteger, float>(\n                tableName,\n                \"AutoIncField\"\n            );\n            IdentityField = new global::SpacetimeDB.Col<global::TestAutoIncNotInteger, string>(\n                tableName,\n                \"IdentityField\"\n            );\n        }\n    }\n\n    public readonly struct TestAutoIncNotIntegerIxCols\n    {\n        internal TestAutoIncNotIntegerIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestAutoIncNotInteger,\n            TestAutoIncNotIntegerCols,\n            TestAutoIncNotIntegerIxCols\n        > TestAutoIncNotInteger() =>\n            new(\n                \"TestAutoIncNotInteger\",\n                new TestAutoIncNotIntegerCols(\"TestAutoIncNotInteger\"),\n                new TestAutoIncNotIntegerIxCols(\"TestAutoIncNotInteger\")\n            );\n    }\n\n    public readonly struct TestDefaultFieldValuesCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, int> UniqueField;\n        public readonly global::SpacetimeDB.Col<\n            global::TestDefaultFieldValues,\n            string\n        > DefaultString;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, bool> DefaultBool;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, sbyte> DefaultI8;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, byte> DefaultU8;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, short> DefaultI16;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, ushort> DefaultU16;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, int> DefaultI32;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, uint> DefaultU32;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, long> DefaultI64;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, ulong> DefaultU64;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, int> DefaultHex;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, int> DefaultBin;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, float> DefaultF32;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, double> DefaultF64;\n        public readonly global::SpacetimeDB.Col<global::TestDefaultFieldValues, MyEnum> DefaultEnum;\n        public readonly global::SpacetimeDB.Col<\n            global::TestDefaultFieldValues,\n            MyStruct\n        > DefaultNull;\n\n        internal TestDefaultFieldValuesCols(string tableName)\n        {\n            UniqueField = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, int>(\n                tableName,\n                \"UniqueField\"\n            );\n            DefaultString = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, string>(\n                tableName,\n                \"DefaultString\"\n            );\n            DefaultBool = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, bool>(\n                tableName,\n                \"DefaultBool\"\n            );\n            DefaultI8 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, sbyte>(\n                tableName,\n                \"DefaultI8\"\n            );\n            DefaultU8 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, byte>(\n                tableName,\n                \"DefaultU8\"\n            );\n            DefaultI16 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, short>(\n                tableName,\n                \"DefaultI16\"\n            );\n            DefaultU16 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, ushort>(\n                tableName,\n                \"DefaultU16\"\n            );\n            DefaultI32 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, int>(\n                tableName,\n                \"DefaultI32\"\n            );\n            DefaultU32 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, uint>(\n                tableName,\n                \"DefaultU32\"\n            );\n            DefaultI64 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, long>(\n                tableName,\n                \"DefaultI64\"\n            );\n            DefaultU64 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, ulong>(\n                tableName,\n                \"DefaultU64\"\n            );\n            DefaultHex = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, int>(\n                tableName,\n                \"DefaultHex\"\n            );\n            DefaultBin = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, int>(\n                tableName,\n                \"DefaultBin\"\n            );\n            DefaultF32 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, float>(\n                tableName,\n                \"DefaultF32\"\n            );\n            DefaultF64 = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, double>(\n                tableName,\n                \"DefaultF64\"\n            );\n            DefaultEnum = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, MyEnum>(\n                tableName,\n                \"DefaultEnum\"\n            );\n            DefaultNull = new global::SpacetimeDB.Col<global::TestDefaultFieldValues, MyStruct>(\n                tableName,\n                \"DefaultNull\"\n            );\n        }\n    }\n\n    public readonly struct TestDefaultFieldValuesIxCols\n    {\n        internal TestDefaultFieldValuesIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestDefaultFieldValues,\n            TestDefaultFieldValuesCols,\n            TestDefaultFieldValuesIxCols\n        > TestDefaultFieldValues() =>\n            new(\n                \"TestDefaultFieldValues\",\n                new TestDefaultFieldValuesCols(\"TestDefaultFieldValues\"),\n                new TestDefaultFieldValuesIxCols(\"TestDefaultFieldValues\")\n            );\n    }\n\n    public readonly struct TestDuplicateTableNameCols\n    {\n        internal TestDuplicateTableNameCols(string tableName) { }\n    }\n\n    public readonly struct TestDuplicateTableNameIxCols\n    {\n        internal TestDuplicateTableNameIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestDuplicateTableName,\n            TestDuplicateTableNameCols,\n            TestDuplicateTableNameIxCols\n        > TestDuplicateTableName() =>\n            new(\n                \"TestDuplicateTableName\",\n                new TestDuplicateTableNameCols(\"TestDuplicateTableName\"),\n                new TestDuplicateTableNameIxCols(\"TestDuplicateTableName\")\n            );\n    }\n\n    public readonly struct TestIndexIssuesCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestIndexIssues, int> SelfIndexingColumn;\n        public readonly global::SpacetimeDB.Col<\n            global::TestIndexIssues,\n            int\n        > SecondaryIndexingColumn;\n\n        internal TestIndexIssuesCols(string tableName)\n        {\n            SelfIndexingColumn = new global::SpacetimeDB.Col<global::TestIndexIssues, int>(\n                tableName,\n                \"SelfIndexingColumn\"\n            );\n            SecondaryIndexingColumn = new global::SpacetimeDB.Col<global::TestIndexIssues, int>(\n                tableName,\n                \"SecondaryIndexingColumn\"\n            );\n        }\n    }\n\n    public readonly struct TestIndexIssuesIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::TestIndexIssues, int> SelfIndexingColumn;\n        public readonly global::SpacetimeDB.IxCol<\n            global::TestIndexIssues,\n            int\n        > SecondaryIndexingColumn;\n\n        internal TestIndexIssuesIxCols(string tableName)\n        {\n            SelfIndexingColumn = new global::SpacetimeDB.IxCol<global::TestIndexIssues, int>(\n                tableName,\n                \"SelfIndexingColumn\"\n            );\n            SecondaryIndexingColumn = new global::SpacetimeDB.IxCol<global::TestIndexIssues, int>(\n                tableName,\n                \"SecondaryIndexingColumn\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestIndexIssues,\n            TestIndexIssuesCols,\n            TestIndexIssuesIxCols\n        > TestIndexIssues() =>\n            new(\n                \"TestIndexIssues\",\n                new TestIndexIssuesCols(\"TestIndexIssues\"),\n                new TestIndexIssuesIxCols(\"TestIndexIssues\")\n            );\n    }\n\n    public readonly struct TestScheduleWithoutPrimaryKeyCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, string> IdWrongType;\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, int> IdCorrectType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            int\n        > ScheduleAtWrongType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            SpacetimeDB.ScheduleAt\n        > ScheduleAtCorrectType;\n\n        internal TestScheduleWithoutPrimaryKeyCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n            IdCorrectType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n            ScheduleAtWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"ScheduleAtWrongType\"\n            );\n            ScheduleAtCorrectType = new global::SpacetimeDB.Col<\n                global::TestScheduleIssues,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduleAtCorrectType\");\n        }\n    }\n\n    public readonly struct TestScheduleWithoutPrimaryKeyIxCols\n    {\n        internal TestScheduleWithoutPrimaryKeyIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestScheduleIssues,\n            TestScheduleWithoutPrimaryKeyCols,\n            TestScheduleWithoutPrimaryKeyIxCols\n        > TestScheduleWithoutPrimaryKey() =>\n            new(\n                \"TestScheduleWithoutPrimaryKey\",\n                new TestScheduleWithoutPrimaryKeyCols(\"TestScheduleWithoutPrimaryKey\"),\n                new TestScheduleWithoutPrimaryKeyIxCols(\"TestScheduleWithoutPrimaryKey\")\n            );\n    }\n\n    public readonly struct TestScheduleWithWrongPrimaryKeyTypeCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, string> IdWrongType;\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, int> IdCorrectType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            int\n        > ScheduleAtWrongType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            SpacetimeDB.ScheduleAt\n        > ScheduleAtCorrectType;\n\n        internal TestScheduleWithWrongPrimaryKeyTypeCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n            IdCorrectType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n            ScheduleAtWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"ScheduleAtWrongType\"\n            );\n            ScheduleAtCorrectType = new global::SpacetimeDB.Col<\n                global::TestScheduleIssues,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduleAtCorrectType\");\n        }\n    }\n\n    public readonly struct TestScheduleWithWrongPrimaryKeyTypeIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::TestScheduleIssues, string> IdWrongType;\n\n        internal TestScheduleWithWrongPrimaryKeyTypeIxCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.IxCol<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestScheduleIssues,\n            TestScheduleWithWrongPrimaryKeyTypeCols,\n            TestScheduleWithWrongPrimaryKeyTypeIxCols\n        > TestScheduleWithWrongPrimaryKeyType() =>\n            new(\n                \"TestScheduleWithWrongPrimaryKeyType\",\n                new TestScheduleWithWrongPrimaryKeyTypeCols(\"TestScheduleWithWrongPrimaryKeyType\"),\n                new TestScheduleWithWrongPrimaryKeyTypeIxCols(\"TestScheduleWithWrongPrimaryKeyType\")\n            );\n    }\n\n    public readonly struct TestScheduleWithoutScheduleAtCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, string> IdWrongType;\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, int> IdCorrectType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            int\n        > ScheduleAtWrongType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            SpacetimeDB.ScheduleAt\n        > ScheduleAtCorrectType;\n\n        internal TestScheduleWithoutScheduleAtCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n            IdCorrectType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n            ScheduleAtWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"ScheduleAtWrongType\"\n            );\n            ScheduleAtCorrectType = new global::SpacetimeDB.Col<\n                global::TestScheduleIssues,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduleAtCorrectType\");\n        }\n    }\n\n    public readonly struct TestScheduleWithoutScheduleAtIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::TestScheduleIssues, int> IdCorrectType;\n\n        internal TestScheduleWithoutScheduleAtIxCols(string tableName)\n        {\n            IdCorrectType = new global::SpacetimeDB.IxCol<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestScheduleIssues,\n            TestScheduleWithoutScheduleAtCols,\n            TestScheduleWithoutScheduleAtIxCols\n        > TestScheduleWithoutScheduleAt() =>\n            new(\n                \"TestScheduleWithoutScheduleAt\",\n                new TestScheduleWithoutScheduleAtCols(\"TestScheduleWithoutScheduleAt\"),\n                new TestScheduleWithoutScheduleAtIxCols(\"TestScheduleWithoutScheduleAt\")\n            );\n    }\n\n    public readonly struct TestScheduleWithWrongScheduleAtTypeCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, string> IdWrongType;\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, int> IdCorrectType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            int\n        > ScheduleAtWrongType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            SpacetimeDB.ScheduleAt\n        > ScheduleAtCorrectType;\n\n        internal TestScheduleWithWrongScheduleAtTypeCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n            IdCorrectType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n            ScheduleAtWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"ScheduleAtWrongType\"\n            );\n            ScheduleAtCorrectType = new global::SpacetimeDB.Col<\n                global::TestScheduleIssues,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduleAtCorrectType\");\n        }\n    }\n\n    public readonly struct TestScheduleWithWrongScheduleAtTypeIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::TestScheduleIssues, int> IdCorrectType;\n\n        internal TestScheduleWithWrongScheduleAtTypeIxCols(string tableName)\n        {\n            IdCorrectType = new global::SpacetimeDB.IxCol<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestScheduleIssues,\n            TestScheduleWithWrongScheduleAtTypeCols,\n            TestScheduleWithWrongScheduleAtTypeIxCols\n        > TestScheduleWithWrongScheduleAtType() =>\n            new(\n                \"TestScheduleWithWrongScheduleAtType\",\n                new TestScheduleWithWrongScheduleAtTypeCols(\"TestScheduleWithWrongScheduleAtType\"),\n                new TestScheduleWithWrongScheduleAtTypeIxCols(\"TestScheduleWithWrongScheduleAtType\")\n            );\n    }\n\n    public readonly struct TestScheduleWithMissingScheduleAtFieldCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, string> IdWrongType;\n        public readonly global::SpacetimeDB.Col<global::TestScheduleIssues, int> IdCorrectType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            int\n        > ScheduleAtWrongType;\n        public readonly global::SpacetimeDB.Col<\n            global::TestScheduleIssues,\n            SpacetimeDB.ScheduleAt\n        > ScheduleAtCorrectType;\n\n        internal TestScheduleWithMissingScheduleAtFieldCols(string tableName)\n        {\n            IdWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, string>(\n                tableName,\n                \"IdWrongType\"\n            );\n            IdCorrectType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"IdCorrectType\"\n            );\n            ScheduleAtWrongType = new global::SpacetimeDB.Col<global::TestScheduleIssues, int>(\n                tableName,\n                \"ScheduleAtWrongType\"\n            );\n            ScheduleAtCorrectType = new global::SpacetimeDB.Col<\n                global::TestScheduleIssues,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduleAtCorrectType\");\n        }\n    }\n\n    public readonly struct TestScheduleWithMissingScheduleAtFieldIxCols\n    {\n        internal TestScheduleWithMissingScheduleAtFieldIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestScheduleIssues,\n            TestScheduleWithMissingScheduleAtFieldCols,\n            TestScheduleWithMissingScheduleAtFieldIxCols\n        > TestScheduleWithMissingScheduleAtField() =>\n            new(\n                \"TestScheduleWithMissingScheduleAtField\",\n                new TestScheduleWithMissingScheduleAtFieldCols(\n                    \"TestScheduleWithMissingScheduleAtField\"\n                ),\n                new TestScheduleWithMissingScheduleAtFieldIxCols(\n                    \"TestScheduleWithMissingScheduleAtField\"\n                )\n            );\n    }\n\n    public readonly struct TestUniqueNotEquatableCols\n    {\n        public readonly global::SpacetimeDB.Col<global::TestUniqueNotEquatable, int> UniqueField;\n        public readonly global::SpacetimeDB.Col<\n            global::TestUniqueNotEquatable,\n            TestEnumWithExplicitValues\n        > PrimaryKeyField;\n\n        internal TestUniqueNotEquatableCols(string tableName)\n        {\n            UniqueField = new global::SpacetimeDB.Col<global::TestUniqueNotEquatable, int>(\n                tableName,\n                \"UniqueField\"\n            );\n            PrimaryKeyField = new global::SpacetimeDB.Col<\n                global::TestUniqueNotEquatable,\n                TestEnumWithExplicitValues\n            >(tableName, \"PrimaryKeyField\");\n        }\n    }\n\n    public readonly struct TestUniqueNotEquatableIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<\n            global::TestUniqueNotEquatable,\n            TestEnumWithExplicitValues\n        > PrimaryKeyField;\n\n        internal TestUniqueNotEquatableIxCols(string tableName)\n        {\n            PrimaryKeyField = new global::SpacetimeDB.IxCol<\n                global::TestUniqueNotEquatable,\n                TestEnumWithExplicitValues\n            >(tableName, \"PrimaryKeyField\");\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::TestUniqueNotEquatable,\n            TestUniqueNotEquatableCols,\n            TestUniqueNotEquatableIxCols\n        > TestUniqueNotEquatable() =>\n            new(\n                \"TestUniqueNotEquatable\",\n                new TestUniqueNotEquatableCols(\"TestUniqueNotEquatable\"),\n                new TestUniqueNotEquatableIxCols(\"TestUniqueNotEquatable\")\n            );\n    }\n\n    public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext\n    {\n        public readonly Identity Sender;\n        public readonly ConnectionId? ConnectionId;\n        public readonly Random Rng;\n        public readonly Timestamp Timestamp;\n        public readonly AuthCtx SenderAuth;\n\n        // **Note:** must be 0..=u32::MAX\n        internal int CounterUuid;\n\n        // We need this property to be non-static for parity with client SDK.\n        public Identity Identity => Internal.IReducerContext.GetIdentity();\n\n        internal ReducerContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time,\n            AuthCtx? senderAuth = null\n        )\n        {\n            Sender = identity;\n            ConnectionId = connectionId;\n            Rng = random;\n            Timestamp = time;\n            SenderAuth = senderAuth ?? AuthCtx.BuildFromSystemTables(connectionId, identity);\n            CounterUuid = 0;\n        }\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if <see cref=\"Uuid\"/> generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Reducer]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    public sealed partial class ProcedureContext : global::SpacetimeDB.ProcedureContextBase\n    {\n        private readonly Local _db = new();\n\n        internal ProcedureContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time\n        )\n            : base(identity, connectionId, random, time) { }\n\n        protected override global::SpacetimeDB.LocalBase CreateLocal() => _db;\n\n        protected override global::SpacetimeDB.ProcedureTxContextBase CreateTxContext(\n            Internal.TxContext inner\n        ) => _cached ??= new ProcedureTxContext(inner);\n\n        private ProcedureTxContext? _cached;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public Local Db => _db;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TResult WithTx<TResult>(Func<ProcedureTxContext, TResult> body) =>\n            base.WithTx(tx => body((ProcedureTxContext)tx));\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TxOutcome<TResult> TryWithTx<TResult, TError>(\n            Func<ProcedureTxContext, Result<TResult, TError>> body\n        )\n            where TError : Exception => base.TryWithTx(tx => body((ProcedureTxContext)tx));\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if UUID generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Procedure]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    [Experimental(\"STDB_UNSTABLE\")]\n    public sealed class ProcedureTxContext : global::SpacetimeDB.ProcedureTxContextBase\n    {\n        internal ProcedureTxContext(Internal.TxContext inner)\n            : base(inner) { }\n\n        public new Local Db => (Local)base.Db;\n    }\n\n    public sealed class Local : global::SpacetimeDB.LocalBase\n    {\n        public global::SpacetimeDB.Internal.TableHandles.Player Player => new();\n        public global::SpacetimeDB.Internal.TableHandles.TestAutoIncNotInteger TestAutoIncNotInteger =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestDefaultFieldValues TestDefaultFieldValues =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestDuplicateTableName TestDuplicateTableName =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestIndexIssues TestIndexIssues => new();\n        public global::SpacetimeDB.Internal.TableHandles.TestScheduleWithMissingScheduleAtField TestScheduleWithMissingScheduleAtField =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestScheduleWithoutPrimaryKey TestScheduleWithoutPrimaryKey =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestScheduleWithoutScheduleAt TestScheduleWithoutScheduleAt =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongPrimaryKeyType TestScheduleWithWrongPrimaryKeyType =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongScheduleAtType TestScheduleWithWrongScheduleAtType =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.TestUniqueNotEquatable TestUniqueNotEquatable =>\n            new();\n    }\n\n    public sealed record ViewContext : DbContext<Internal.LocalReadOnly>, Internal.IViewContext\n    {\n        public Identity Sender { get; }\n\n        public QueryBuilder From => default;\n\n        internal ViewContext(Identity sender, Internal.LocalReadOnly db)\n            : base(db)\n        {\n            Sender = sender;\n        }\n    }\n\n    public sealed record AnonymousViewContext\n        : DbContext<Internal.LocalReadOnly>,\n            Internal.IAnonymousViewContext\n    {\n        public QueryBuilder From => default;\n\n        internal AnonymousViewContext(Internal.LocalReadOnly db)\n            : base(db) { }\n    }\n}\n\nnamespace SpacetimeDB.Internal.TableHandles\n{\n    public readonly struct Player : global::SpacetimeDB.Internal.ITableView<Player, global::Player>\n    {\n        public static global::Player ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::Player row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(Player),\n                ProductTypeRef: (uint)new global::Player.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"Player_Identity_idx_btree\",\n                        AccessorName: \"Identity\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        Player,\n                        global::Player\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<Player, global::Player>.DoCount();\n\n        public IEnumerable<global::Player> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<Player, global::Player>.DoIter();\n\n        public global::Player Insert(global::Player row) =>\n            global::SpacetimeDB.Internal.ITableView<Player, global::Player>.DoInsert(row);\n\n        public bool Delete(global::Player row) =>\n            global::SpacetimeDB.Internal.ITableView<Player, global::Player>.DoDelete(row);\n\n        public sealed class IdentityUniqueIndex\n            : UniqueIndex<Player, global::Player, SpacetimeDB.Identity, SpacetimeDB.Identity.BSATN>\n        {\n            internal IdentityUniqueIndex()\n                : base(\"Player_Identity_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::Player? Find(SpacetimeDB.Identity key) => FindSingle(key);\n        }\n\n        public IdentityUniqueIndex Identity => new();\n    }\n\n    public readonly struct TestAutoIncNotInteger\n        : global::SpacetimeDB.Internal.ITableView<\n            TestAutoIncNotInteger,\n            global::TestAutoIncNotInteger\n        >\n    {\n        public static global::TestAutoIncNotInteger ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestAutoIncNotInteger row\n        )\n        {\n            if (row.AutoIncField == default)\n            {\n                row.AutoIncField = global::TestAutoIncNotInteger.BSATN.AutoIncFieldRW.Read(reader);\n            }\n            if (row.IdentityField == default)\n            {\n                row.IdentityField = global::TestAutoIncNotInteger.BSATN.IdentityFieldRW.Read(\n                    reader\n                );\n            }\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestAutoIncNotInteger),\n                ProductTypeRef: (uint)\n                    new global::TestAutoIncNotInteger.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestAutoIncNotInteger_IdentityField_idx_btree\",\n                        AccessorName: \"IdentityField\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestAutoIncNotInteger,\n                        global::TestAutoIncNotInteger\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestAutoIncNotInteger,\n                        global::TestAutoIncNotInteger\n                    >.MakeSequence(0),\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestAutoIncNotInteger,\n                        global::TestAutoIncNotInteger\n                    >.MakeSequence(1)\n                ],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestAutoIncNotInteger,\n                global::TestAutoIncNotInteger\n            >.DoCount();\n\n        public IEnumerable<global::TestAutoIncNotInteger> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestAutoIncNotInteger,\n                global::TestAutoIncNotInteger\n            >.DoIter();\n\n        public global::TestAutoIncNotInteger Insert(global::TestAutoIncNotInteger row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestAutoIncNotInteger,\n                global::TestAutoIncNotInteger\n            >.DoInsert(row);\n\n        public bool Delete(global::TestAutoIncNotInteger row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestAutoIncNotInteger,\n                global::TestAutoIncNotInteger\n            >.DoDelete(row);\n\n        public sealed class IdentityFieldUniqueIndex\n            : UniqueIndex<\n                TestAutoIncNotInteger,\n                global::TestAutoIncNotInteger,\n                string,\n                SpacetimeDB.BSATN.String\n            >\n        {\n            internal IdentityFieldUniqueIndex()\n                : base(\"TestAutoIncNotInteger_IdentityField_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::TestAutoIncNotInteger? Find(string key) => FindSingle(key);\n        }\n\n        public IdentityFieldUniqueIndex IdentityField => new();\n    }\n\n    public readonly struct TestDefaultFieldValues\n        : global::SpacetimeDB.Internal.ITableView<\n            TestDefaultFieldValues,\n            global::TestDefaultFieldValues\n        >\n    {\n        public static global::TestDefaultFieldValues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestDefaultFieldValues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestDefaultFieldValues),\n                ProductTypeRef: (uint)\n                    new global::TestDefaultFieldValues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestDefaultFieldValues_UniqueField_idx_btree\",\n                        AccessorName: \"UniqueField\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestDefaultFieldValues,\n                        global::TestDefaultFieldValues\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDefaultFieldValues,\n                global::TestDefaultFieldValues\n            >.DoCount();\n\n        public IEnumerable<global::TestDefaultFieldValues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDefaultFieldValues,\n                global::TestDefaultFieldValues\n            >.DoIter();\n\n        public global::TestDefaultFieldValues Insert(global::TestDefaultFieldValues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDefaultFieldValues,\n                global::TestDefaultFieldValues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestDefaultFieldValues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDefaultFieldValues,\n                global::TestDefaultFieldValues\n            >.DoDelete(row);\n    }\n\n    public readonly struct TestDuplicateTableName\n        : global::SpacetimeDB.Internal.ITableView<\n            TestDuplicateTableName,\n            global::TestDuplicateTableName\n        >\n    {\n        public static global::TestDuplicateTableName ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestDuplicateTableName row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestDuplicateTableName),\n                ProductTypeRef: (uint)\n                    new global::TestDuplicateTableName.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes: [],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDuplicateTableName,\n                global::TestDuplicateTableName\n            >.DoCount();\n\n        public IEnumerable<global::TestDuplicateTableName> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDuplicateTableName,\n                global::TestDuplicateTableName\n            >.DoIter();\n\n        public global::TestDuplicateTableName Insert(global::TestDuplicateTableName row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDuplicateTableName,\n                global::TestDuplicateTableName\n            >.DoInsert(row);\n\n        public bool Delete(global::TestDuplicateTableName row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestDuplicateTableName,\n                global::TestDuplicateTableName\n            >.DoDelete(row);\n    }\n\n    public readonly struct TestIndexIssues\n        : global::SpacetimeDB.Internal.ITableView<TestIndexIssues, global::TestIndexIssues>\n    {\n        public static global::TestIndexIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestIndexIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestIndexIssues),\n                ProductTypeRef: (uint)\n                    new global::TestIndexIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestIndexIssues__idx_btree\",\n                        AccessorName: \"TestIndexWithoutColumns\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([])\n                    ),\n                    new(\n                        SourceName: \"TestIndexIssues__idx_btree\",\n                        AccessorName: \"TestIndexWithEmptyColumns\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([])\n                    ),\n                    new(\n                        SourceName: \"TestIndexIssues__idx_btree\",\n                        AccessorName: \"TestUnknownColumns\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([])\n                    ),\n                    new(\n                        SourceName: \"TestIndexIssues_SelfIndexingColumn_idx_btree\",\n                        AccessorName: \"SelfIndexingColumn\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    ),\n                    new(\n                        SourceName: \"TestIndexIssues_SecondaryIndexingColumn_idx_btree\",\n                        AccessorName: \"SecondaryIndexingColumn\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    ),\n                    new(\n                        SourceName: \"TestIndexIssues_SelfIndexingColumn_idx_btree\",\n                        AccessorName: \"TestUnexpectedColumns\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestIndexIssues,\n                global::TestIndexIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestIndexIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestIndexIssues,\n                global::TestIndexIssues\n            >.DoIter();\n\n        public global::TestIndexIssues Insert(global::TestIndexIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestIndexIssues,\n                global::TestIndexIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestIndexIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestIndexIssues,\n                global::TestIndexIssues\n            >.DoDelete(row);\n\n        public sealed class TestIndexWithoutColumnsIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues__idx_btree\"\n            ) { }\n\n        public TestIndexWithoutColumnsIndex TestIndexWithoutColumns => new();\n\n        public sealed class TestIndexWithEmptyColumnsIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues__idx_btree\"\n            ) { }\n\n        public TestIndexWithEmptyColumnsIndex TestIndexWithEmptyColumns => new();\n\n        public sealed class TestUnknownColumnsIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues__idx_btree\"\n            ) { }\n\n        public TestUnknownColumnsIndex TestUnknownColumns => new();\n\n        public sealed class SelfIndexingColumnIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues_SelfIndexingColumn_idx_btree\"\n            )\n        {\n            public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public ulong Delete(int SelfIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SelfIndexingColumn\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public ulong Delete(global::SpacetimeDB.Bound<int> SelfIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n        }\n\n        public SelfIndexingColumnIndex SelfIndexingColumn => new();\n\n        public sealed class SecondaryIndexingColumnIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues_SecondaryIndexingColumn_idx_btree\"\n            )\n        {\n            public IEnumerable<global::TestIndexIssues> Filter(int SecondaryIndexingColumn) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n\n            public ulong Delete(int SecondaryIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SecondaryIndexingColumn\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n\n            public ulong Delete(global::SpacetimeDB.Bound<int> SecondaryIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n        }\n\n        public SecondaryIndexingColumnIndex SecondaryIndexingColumn => new();\n\n        public sealed class TestUnexpectedColumnsIndex()\n            : SpacetimeDB.Internal.IndexBase<global::TestIndexIssues>(\n                \"TestIndexIssues_SelfIndexingColumn_idx_btree\"\n            )\n        {\n            public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public ulong Delete(int SelfIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SelfIndexingColumn\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public ulong Delete(global::SpacetimeDB.Bound<int> SelfIndexingColumn) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n        }\n\n        public TestUnexpectedColumnsIndex TestUnexpectedColumns => new();\n    }\n\n    public readonly struct TestScheduleWithMissingScheduleAtField\n        : global::SpacetimeDB.Internal.ITableView<\n            TestScheduleWithMissingScheduleAtField,\n            global::TestScheduleIssues\n        >\n    {\n        public static global::TestScheduleIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestScheduleIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestScheduleWithMissingScheduleAtField),\n                ProductTypeRef: (uint)\n                    new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes: [],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithMissingScheduleAtField,\n                global::TestScheduleIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestScheduleIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithMissingScheduleAtField,\n                global::TestScheduleIssues\n            >.DoIter();\n\n        public global::TestScheduleIssues Insert(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithMissingScheduleAtField,\n                global::TestScheduleIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithMissingScheduleAtField,\n                global::TestScheduleIssues\n            >.DoDelete(row);\n    }\n\n    public readonly struct TestScheduleWithoutPrimaryKey\n        : global::SpacetimeDB.Internal.ITableView<\n            TestScheduleWithoutPrimaryKey,\n            global::TestScheduleIssues\n        >\n    {\n        public static global::TestScheduleIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestScheduleIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestScheduleWithoutPrimaryKey),\n                ProductTypeRef: (uint)\n                    new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes: [],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutPrimaryKey,\n                global::TestScheduleIssues\n            >.MakeSchedule(\"DummyScheduledReducer\", 3);\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutPrimaryKey,\n                global::TestScheduleIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestScheduleIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutPrimaryKey,\n                global::TestScheduleIssues\n            >.DoIter();\n\n        public global::TestScheduleIssues Insert(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutPrimaryKey,\n                global::TestScheduleIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutPrimaryKey,\n                global::TestScheduleIssues\n            >.DoDelete(row);\n    }\n\n    public readonly struct TestScheduleWithoutScheduleAt\n        : global::SpacetimeDB.Internal.ITableView<\n            TestScheduleWithoutScheduleAt,\n            global::TestScheduleIssues\n        >\n    {\n        public static global::TestScheduleIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestScheduleIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestScheduleWithoutScheduleAt),\n                ProductTypeRef: (uint)\n                    new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [1],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestScheduleWithoutScheduleAt_IdCorrectType_idx_btree\",\n                        AccessorName: \"IdCorrectType\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestScheduleWithoutScheduleAt,\n                        global::TestScheduleIssues\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutScheduleAt,\n                global::TestScheduleIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestScheduleIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutScheduleAt,\n                global::TestScheduleIssues\n            >.DoIter();\n\n        public global::TestScheduleIssues Insert(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutScheduleAt,\n                global::TestScheduleIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithoutScheduleAt,\n                global::TestScheduleIssues\n            >.DoDelete(row);\n\n        public sealed class IdCorrectTypeUniqueIndex\n            : UniqueIndex<\n                TestScheduleWithoutScheduleAt,\n                global::TestScheduleIssues,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdCorrectTypeUniqueIndex()\n                : base(\"TestScheduleWithoutScheduleAt_IdCorrectType_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::TestScheduleIssues? Find(int key) => FindSingle(key);\n\n            public global::TestScheduleIssues Update(global::TestScheduleIssues row) =>\n                DoUpdate(row);\n        }\n\n        public IdCorrectTypeUniqueIndex IdCorrectType => new();\n    }\n\n    public readonly struct TestScheduleWithWrongPrimaryKeyType\n        : global::SpacetimeDB.Internal.ITableView<\n            TestScheduleWithWrongPrimaryKeyType,\n            global::TestScheduleIssues\n        >\n    {\n        public static global::TestScheduleIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestScheduleIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestScheduleWithWrongPrimaryKeyType),\n                ProductTypeRef: (uint)\n                    new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [0],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestScheduleWithWrongPrimaryKeyType_IdWrongType_idx_btree\",\n                        AccessorName: \"IdWrongType\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestScheduleWithWrongPrimaryKeyType,\n                        global::TestScheduleIssues\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues\n            >.MakeSchedule(\"DummyScheduledReducer\", 3);\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestScheduleIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues\n            >.DoIter();\n\n        public global::TestScheduleIssues Insert(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues\n            >.DoDelete(row);\n\n        public sealed class IdWrongTypeUniqueIndex\n            : UniqueIndex<\n                TestScheduleWithWrongPrimaryKeyType,\n                global::TestScheduleIssues,\n                string,\n                SpacetimeDB.BSATN.String\n            >\n        {\n            internal IdWrongTypeUniqueIndex()\n                : base(\"TestScheduleWithWrongPrimaryKeyType_IdWrongType_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::TestScheduleIssues? Find(string key) => FindSingle(key);\n\n            public global::TestScheduleIssues Update(global::TestScheduleIssues row) =>\n                DoUpdate(row);\n        }\n\n        public IdWrongTypeUniqueIndex IdWrongType => new();\n    }\n\n    public readonly struct TestScheduleWithWrongScheduleAtType\n        : global::SpacetimeDB.Internal.ITableView<\n            TestScheduleWithWrongScheduleAtType,\n            global::TestScheduleIssues\n        >\n    {\n        public static global::TestScheduleIssues ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestScheduleIssues row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestScheduleWithWrongScheduleAtType),\n                ProductTypeRef: (uint)\n                    new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [1],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestScheduleWithWrongScheduleAtType_IdCorrectType_idx_btree\",\n                        AccessorName: \"IdCorrectType\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestScheduleWithWrongScheduleAtType,\n                        global::TestScheduleIssues\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues\n            >.MakeSchedule(\"DummyScheduledReducer\", 2);\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues\n            >.DoCount();\n\n        public IEnumerable<global::TestScheduleIssues> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues\n            >.DoIter();\n\n        public global::TestScheduleIssues Insert(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues\n            >.DoInsert(row);\n\n        public bool Delete(global::TestScheduleIssues row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues\n            >.DoDelete(row);\n\n        public sealed class IdCorrectTypeUniqueIndex\n            : UniqueIndex<\n                TestScheduleWithWrongScheduleAtType,\n                global::TestScheduleIssues,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdCorrectTypeUniqueIndex()\n                : base(\"TestScheduleWithWrongScheduleAtType_IdCorrectType_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::TestScheduleIssues? Find(int key) => FindSingle(key);\n\n            public global::TestScheduleIssues Update(global::TestScheduleIssues row) =>\n                DoUpdate(row);\n        }\n\n        public IdCorrectTypeUniqueIndex IdCorrectType => new();\n    }\n\n    public readonly struct TestUniqueNotEquatable\n        : global::SpacetimeDB.Internal.ITableView<\n            TestUniqueNotEquatable,\n            global::TestUniqueNotEquatable\n        >\n    {\n        public static global::TestUniqueNotEquatable ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::TestUniqueNotEquatable row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestUniqueNotEquatable),\n                ProductTypeRef: (uint)\n                    new global::TestUniqueNotEquatable.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [1],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"TestUniqueNotEquatable_UniqueField_idx_btree\",\n                        AccessorName: \"UniqueField\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    ),\n                    new(\n                        SourceName: \"TestUniqueNotEquatable_PrimaryKeyField_idx_btree\",\n                        AccessorName: \"PrimaryKeyField\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestUniqueNotEquatable,\n                        global::TestUniqueNotEquatable\n                    >.MakeUniqueConstraint(0),\n                    global::SpacetimeDB.Internal.ITableView<\n                        TestUniqueNotEquatable,\n                        global::TestUniqueNotEquatable\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestUniqueNotEquatable,\n                global::TestUniqueNotEquatable\n            >.DoCount();\n\n        public IEnumerable<global::TestUniqueNotEquatable> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestUniqueNotEquatable,\n                global::TestUniqueNotEquatable\n            >.DoIter();\n\n        public global::TestUniqueNotEquatable Insert(global::TestUniqueNotEquatable row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestUniqueNotEquatable,\n                global::TestUniqueNotEquatable\n            >.DoInsert(row);\n\n        public bool Delete(global::TestUniqueNotEquatable row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                TestUniqueNotEquatable,\n                global::TestUniqueNotEquatable\n            >.DoDelete(row);\n\n        public sealed class PrimaryKeyFieldUniqueIndex\n            : UniqueIndex<\n                TestUniqueNotEquatable,\n                global::TestUniqueNotEquatable,\n                TestEnumWithExplicitValues,\n                SpacetimeDB.BSATN.Enum<TestEnumWithExplicitValues>\n            >\n        {\n            internal PrimaryKeyFieldUniqueIndex()\n                : base(\"TestUniqueNotEquatable_PrimaryKeyField_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::TestUniqueNotEquatable? Find(TestEnumWithExplicitValues key) =>\n                FindSingle(key);\n\n            public global::TestUniqueNotEquatable Update(global::TestUniqueNotEquatable row) =>\n                DoUpdate(row);\n        }\n\n        public PrimaryKeyFieldUniqueIndex PrimaryKeyField => new();\n    }\n}\n\nsealed class view_def_ienumerable_return_from_filterViewDispatcher\n    : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_ienumerable_return_from_filter\",\n            Index: 0,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.Unsupported<System.Collections.Generic.IEnumerable<TestScheduleIssues>>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefIEnumerableReturnFromFilter(\n                (SpacetimeDB.ViewContext)ctx\n            );\n            SpacetimeDB.BSATN.Unsupported<System.Collections.Generic.IEnumerable<TestScheduleIssues>> returnRW =\n                new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\n                \"Error in view 'view_def_ienumerable_return_from_filter': \" + e\n            );\n            throw;\n        }\n    }\n}\n\nsealed class view_def_ienumerable_return_from_iterViewDispatcher\n    : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_ienumerable_return_from_iter\",\n            Index: 1,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.Unsupported<System.Collections.Generic.IEnumerable<Player>>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefIEnumerableReturnFromIter((SpacetimeDB.ViewContext)ctx);\n            SpacetimeDB.BSATN.Unsupported<System.Collections.Generic.IEnumerable<Player>> returnRW =\n                new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\n                \"Error in view 'view_def_ienumerable_return_from_iter': \" + e\n            );\n            throw;\n        }\n    }\n}\n\nsealed class view_def_no_contextViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_no_context\",\n            Index: 2,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.List<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefNoContext((SpacetimeDB.ViewContext)ctx);\n            SpacetimeDB.BSATN.List<Player, Player.BSATN> returnRW = new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_no_context': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_no_publicViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_no_public\",\n            Index: 3,\n            IsPublic: false,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.List<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefNoPublic((SpacetimeDB.ViewContext)ctx);\n            SpacetimeDB.BSATN.List<Player, Player.BSATN> returnRW = new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_no_public': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_wrong_contextViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_wrong_context\",\n            Index: 4,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.List<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefWrongContext((SpacetimeDB.ViewContext)ctx);\n            SpacetimeDB.BSATN.List<Player, Player.BSATN> returnRW = new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_wrong_context': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_wrong_returnViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_wrong_return\",\n            Index: 5,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new Player.BSATN().GetAlgebraicType(registrar)\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefWrongReturn((SpacetimeDB.ViewContext)ctx);\n            Player.BSATN returnRW = new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_wrong_return': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_no_deleteViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_no_delete\",\n            Index: 6,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewNoDelete((SpacetimeDB.ViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                Player,\n                Player.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_no_delete': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_no_insertViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_no_insert\",\n            Index: 7,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewNoInsert((SpacetimeDB.ViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                Player,\n                Player.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_no_insert': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_index_no_mutationViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeAnonymousViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_index_no_mutation\",\n            Index: 0,\n            IsPublic: true,\n            IsAnonymous: true,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IAnonymousViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefIndexNoMutation((SpacetimeDB.AnonymousViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                Player,\n                Player.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_index_no_mutation': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_no_anon_identityViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeAnonymousViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_no_anon_identity\",\n            Index: 1,\n            IsPublic: true,\n            IsAnonymous: true,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IAnonymousViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefNoAnonIdentity((SpacetimeDB.AnonymousViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                Player,\n                Player.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_no_anon_identity': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_no_iterViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeAnonymousViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_no_iter\",\n            Index: 2,\n            IsPublic: true,\n            IsAnonymous: true,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<Player, Player.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IAnonymousViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefNoIter((SpacetimeDB.AnonymousViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                Player,\n                Player.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'view_def_no_iter': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class view_def_returns_not_a_spacetime_typeViewDispatcher\n    : global::SpacetimeDB.Internal.IAnonymousView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeAnonymousViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"view_def_returns_not_a_spacetime_type\",\n            Index: 3,\n            IsPublic: true,\n            IsAnonymous: true,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<\n                NotSpacetimeType,\n                NotSpacetimeType.BSATN\n            >().GetAlgebraicType(registrar)\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IAnonymousViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.ViewDefReturnsNotASpacetimeType(\n                (SpacetimeDB.AnonymousViewContext)ctx\n            );\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                NotSpacetimeType,\n                NotSpacetimeType.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\n                \"Error in view 'view_def_returns_not_a_spacetime_type': \" + e\n            );\n            throw;\n        }\n    }\n}\n\nnamespace SpacetimeDB.Internal.ViewHandles\n{\n    public sealed class PlayerReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::Player>\n    {\n        internal PlayerReadOnly()\n            : base(\"Player\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdentityIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.PlayerReadOnly,\n                global::Player,\n                SpacetimeDB.Identity,\n                SpacetimeDB.Identity.BSATN\n            >\n        {\n            internal IdentityIndex()\n                : base(\"Player_Identity_idx_btree\") { }\n\n            public global::Player? Find(SpacetimeDB.Identity key) => FindSingle(key);\n        }\n\n        public IdentityIndex Identity => new();\n    }\n\n    public sealed class TestAutoIncNotIntegerReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestAutoIncNotInteger>\n    {\n        internal TestAutoIncNotIntegerReadOnly()\n            : base(\"TestAutoIncNotInteger\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdentityFieldIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.TestAutoIncNotIntegerReadOnly,\n                global::TestAutoIncNotInteger,\n                string,\n                SpacetimeDB.BSATN.String\n            >\n        {\n            internal IdentityFieldIndex()\n                : base(\"TestAutoIncNotInteger_IdentityField_idx_btree\") { }\n\n            public global::TestAutoIncNotInteger? Find(string key) => FindSingle(key);\n        }\n\n        public IdentityFieldIndex IdentityField => new();\n    }\n\n    public sealed class TestDefaultFieldValuesReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestDefaultFieldValues>\n    {\n        internal TestDefaultFieldValuesReadOnly()\n            : base(\"TestDefaultFieldValues\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n    }\n\n    public sealed class TestDuplicateTableNameReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestDuplicateTableName>\n    {\n        internal TestDuplicateTableNameReadOnly()\n            : base(\"TestDuplicateTableName\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n    }\n\n    public sealed class TestIndexIssuesReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestIndexIssues>\n    {\n        internal TestIndexIssuesReadOnly()\n            : base(\"TestIndexIssues\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class TestIndexWithoutColumnsIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal TestIndexWithoutColumnsIndex()\n                : base(\"TestIndexIssues__idx_btree\") { }\n        }\n\n        public TestIndexWithoutColumnsIndex TestIndexWithoutColumns => new();\n\n        public sealed class TestIndexWithEmptyColumnsIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal TestIndexWithEmptyColumnsIndex()\n                : base(\"TestIndexIssues__idx_btree\") { }\n        }\n\n        public TestIndexWithEmptyColumnsIndex TestIndexWithEmptyColumns => new();\n\n        public sealed class TestUnknownColumnsIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal TestUnknownColumnsIndex()\n                : base(\"TestIndexIssues__idx_btree\") { }\n        }\n\n        public TestUnknownColumnsIndex TestUnknownColumns => new();\n\n        public sealed class SelfIndexingColumnIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal SelfIndexingColumnIndex()\n                : base(\"TestIndexIssues_SelfIndexingColumn_idx_btree\") { }\n\n            public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SelfIndexingColumn\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n        }\n\n        public SelfIndexingColumnIndex SelfIndexingColumn => new();\n\n        public sealed class SecondaryIndexingColumnIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal SecondaryIndexingColumnIndex()\n                : base(\"TestIndexIssues_SecondaryIndexingColumn_idx_btree\") { }\n\n            public IEnumerable<global::TestIndexIssues> Filter(int SecondaryIndexingColumn) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SecondaryIndexingColumn\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SecondaryIndexingColumn\n                    )\n                );\n        }\n\n        public SecondaryIndexingColumnIndex SecondaryIndexingColumn => new();\n\n        public sealed class TestUnexpectedColumnsIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::TestIndexIssues>\n        {\n            internal TestUnexpectedColumnsIndex()\n                : base(\"TestIndexIssues_SelfIndexingColumn_idx_btree\") { }\n\n            public IEnumerable<global::TestIndexIssues> Filter(int SelfIndexingColumn) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n\n            public IEnumerable<global::TestIndexIssues> Filter(\n                global::SpacetimeDB.Bound<int> SelfIndexingColumn\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        SelfIndexingColumn\n                    )\n                );\n        }\n\n        public TestUnexpectedColumnsIndex TestUnexpectedColumns => new();\n    }\n\n    public sealed class TestScheduleWithMissingScheduleAtFieldReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestScheduleIssues>\n    {\n        internal TestScheduleWithMissingScheduleAtFieldReadOnly()\n            : base(\"TestScheduleWithMissingScheduleAtField\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n    }\n\n    public sealed class TestScheduleWithoutPrimaryKeyReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestScheduleIssues>\n    {\n        internal TestScheduleWithoutPrimaryKeyReadOnly()\n            : base(\"TestScheduleWithoutPrimaryKey\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n    }\n\n    public sealed class TestScheduleWithoutScheduleAtReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestScheduleIssues>\n    {\n        internal TestScheduleWithoutScheduleAtReadOnly()\n            : base(\"TestScheduleWithoutScheduleAt\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdCorrectTypeIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithoutScheduleAtReadOnly,\n                global::TestScheduleIssues,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdCorrectTypeIndex()\n                : base(\"TestScheduleWithoutScheduleAt_IdCorrectType_idx_btree\") { }\n\n            public global::TestScheduleIssues? Find(int key) => FindSingle(key);\n        }\n\n        public IdCorrectTypeIndex IdCorrectType => new();\n    }\n\n    public sealed class TestScheduleWithWrongPrimaryKeyTypeReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestScheduleIssues>\n    {\n        internal TestScheduleWithWrongPrimaryKeyTypeReadOnly()\n            : base(\"TestScheduleWithWrongPrimaryKeyType\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdWrongTypeIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithWrongPrimaryKeyTypeReadOnly,\n                global::TestScheduleIssues,\n                string,\n                SpacetimeDB.BSATN.String\n            >\n        {\n            internal IdWrongTypeIndex()\n                : base(\"TestScheduleWithWrongPrimaryKeyType_IdWrongType_idx_btree\") { }\n\n            public global::TestScheduleIssues? Find(string key) => FindSingle(key);\n        }\n\n        public IdWrongTypeIndex IdWrongType => new();\n    }\n\n    public sealed class TestScheduleWithWrongScheduleAtTypeReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestScheduleIssues>\n    {\n        internal TestScheduleWithWrongScheduleAtTypeReadOnly()\n            : base(\"TestScheduleWithWrongScheduleAtType\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdCorrectTypeIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithWrongScheduleAtTypeReadOnly,\n                global::TestScheduleIssues,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdCorrectTypeIndex()\n                : base(\"TestScheduleWithWrongScheduleAtType_IdCorrectType_idx_btree\") { }\n\n            public global::TestScheduleIssues? Find(int key) => FindSingle(key);\n        }\n\n        public IdCorrectTypeIndex IdCorrectType => new();\n    }\n\n    public sealed class TestUniqueNotEquatableReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::TestUniqueNotEquatable>\n    {\n        internal TestUniqueNotEquatableReadOnly()\n            : base(\"TestUniqueNotEquatable\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class PrimaryKeyFieldIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.TestUniqueNotEquatableReadOnly,\n                global::TestUniqueNotEquatable,\n                TestEnumWithExplicitValues,\n                SpacetimeDB.BSATN.Enum<TestEnumWithExplicitValues>\n            >\n        {\n            internal PrimaryKeyFieldIndex()\n                : base(\"TestUniqueNotEquatable_PrimaryKeyField_idx_btree\") { }\n\n            public global::TestUniqueNotEquatable? Find(TestEnumWithExplicitValues key) =>\n                FindSingle(key);\n        }\n\n        public PrimaryKeyFieldIndex PrimaryKeyField => new();\n    }\n}\n\nnamespace SpacetimeDB.Internal\n{\n    public sealed partial class LocalReadOnly\n    {\n        public global::SpacetimeDB.Internal.ViewHandles.PlayerReadOnly Player => new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestAutoIncNotIntegerReadOnly TestAutoIncNotInteger =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestDefaultFieldValuesReadOnly TestDefaultFieldValues =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestDuplicateTableNameReadOnly TestDuplicateTableName =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestIndexIssuesReadOnly TestIndexIssues =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithMissingScheduleAtFieldReadOnly TestScheduleWithMissingScheduleAtField =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithoutPrimaryKeyReadOnly TestScheduleWithoutPrimaryKey =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithoutScheduleAtReadOnly TestScheduleWithoutScheduleAt =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithWrongPrimaryKeyTypeReadOnly TestScheduleWithWrongPrimaryKeyType =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestScheduleWithWrongScheduleAtTypeReadOnly TestScheduleWithWrongScheduleAtType =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.TestUniqueNotEquatableReadOnly TestUniqueNotEquatable =>\n            new();\n    }\n}\n\nstatic class ModuleRegistration\n{\n    class __ReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(__ReducerWithReservedPrefix),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.__ReducerWithReservedPrefix((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class DummyScheduledReducer : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly TestScheduleIssues.BSATN tableRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(DummyScheduledReducer),\n                Params: [new(\"table\", tableRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            TestScheduleIssues.DummyScheduledReducer(\n                (SpacetimeDB.ReducerContext)ctx,\n                tableRW.Read(reader)\n            );\n        }\n    }\n\n    class OnReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(OnReducerWithReservedPrefix),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.OnReducerWithReservedPrefix((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class TestDuplicateReducerKind1 : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestDuplicateReducerKind1),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.TestDuplicateReducerKind1((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class TestDuplicateReducerKind2 : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestDuplicateReducerKind2),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.TestDuplicateReducerKind2((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class TestDuplicateReducerName : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestDuplicateReducerName),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.TestDuplicateReducerName((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class TestReducerReturnType : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestReducerReturnType),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.TestReducerReturnType((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class TestReducerWithoutContext : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(TestReducerWithoutContext),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            throw new System.InvalidOperationException();\n        }\n    }\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : struct => value is null ? new List<T>() : new List<T> { value.Value };\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : class => value is null ? new List<T>() : new List<T> { value };\n\n#if EXPERIMENTAL_WASM_AOT\n    // In AOT mode we're building a library.\n    // Main method won't be called automatically, so we need to export it as a preinit function.\n    [UnmanagedCallersOnly(EntryPoint = \"__preinit__10_init_csharp\")]\n#else\n    // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer.\n    [DynamicDependency(\n        DynamicallyAccessedMemberTypes.PublicMethods,\n        typeof(SpacetimeDB.Internal.Module)\n    )]\n#endif\n    public static void Main()\n    {\n        SpacetimeDB.Internal.Module.SetReducerContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ReducerContext(identity, connectionId, random, time)\n        );\n        SpacetimeDB.Internal.Module.SetViewContextConstructor(\n            identity => new SpacetimeDB.ViewContext(\n                identity,\n                new SpacetimeDB.Internal.LocalReadOnly()\n            )\n        );\n        SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor(\n            () => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly())\n        );\n        SpacetimeDB.Internal.Module.SetProcedureContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ProcedureContext(identity, connectionId, random, time)\n        );\n        SpacetimeDB.Internal.Module.RegisterExplicitIndexName(\n            \"TestIndexIssues_SecondaryIndexingColumn_idx_btree\",\n            \"TestCanonicalNameWithoutAccessor\"\n        );\n\n        var __memoryStream = new MemoryStream();\n        var __writer = new BinaryWriter(__memoryStream);\n\n        SpacetimeDB.Internal.Module.RegisterReducer<__ReducerWithReservedPrefix>();\n        SpacetimeDB.Internal.Module.RegisterReducer<DummyScheduledReducer>();\n        SpacetimeDB.Internal.Module.RegisterReducer<OnReducerWithReservedPrefix>();\n        SpacetimeDB.Internal.Module.RegisterReducer<TestDuplicateReducerKind1>();\n        SpacetimeDB.Internal.Module.RegisterReducer<TestDuplicateReducerKind2>();\n        SpacetimeDB.Internal.Module.RegisterReducer<TestDuplicateReducerName>();\n        SpacetimeDB.Internal.Module.RegisterReducer<TestReducerReturnType>();\n        SpacetimeDB.Internal.Module.RegisterReducer<TestReducerWithoutContext>();\n\n        // IMPORTANT: The order in which we register views matters.\n        // It must correspond to the order in which we call `GenerateDispatcherClass`.\n        // See the comment on `GenerateDispatcherClass` for more explanation.\n        SpacetimeDB.Internal.Module.RegisterView<view_def_ienumerable_return_from_filterViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_def_ienumerable_return_from_iterViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_def_no_contextViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_def_no_publicViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_def_wrong_contextViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_def_wrong_returnViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_no_deleteViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<view_no_insertViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterAnonymousView<view_def_index_no_mutationViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterAnonymousView<view_def_no_anon_identityViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterAnonymousView<view_def_no_iterViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterAnonymousView<view_def_returns_not_a_spacetime_typeViewDispatcher>();\n\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::Player,\n            SpacetimeDB.Internal.TableHandles.Player\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestAutoIncNotInteger,\n            SpacetimeDB.Internal.TableHandles.TestAutoIncNotInteger\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestDefaultFieldValues,\n            SpacetimeDB.Internal.TableHandles.TestDefaultFieldValues\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestDuplicateTableName,\n            SpacetimeDB.Internal.TableHandles.TestDuplicateTableName\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestIndexIssues,\n            SpacetimeDB.Internal.TableHandles.TestIndexIssues\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestScheduleIssues,\n            SpacetimeDB.Internal.TableHandles.TestScheduleWithMissingScheduleAtField\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestScheduleIssues,\n            SpacetimeDB.Internal.TableHandles.TestScheduleWithoutPrimaryKey\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestScheduleIssues,\n            SpacetimeDB.Internal.TableHandles.TestScheduleWithoutScheduleAt\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestScheduleIssues,\n            SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongPrimaryKeyType\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestScheduleIssues,\n            SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongScheduleAtType\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::TestUniqueNotEquatable,\n            SpacetimeDB.Internal.TableHandles.TestUniqueNotEquatable\n        >();\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FILTER);\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FOURTH_FILTER);\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_SECOND_FILTER);\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_THIRD_FILTER);\n        {\n            var value = new SpacetimeDB.BSATN.String();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, \"A default string set by attribute\");\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                1,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.U64();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                10,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I32();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                11,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I32();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                12,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.F32();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                13,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.F64();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                14,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.Enum<MyEnum>();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, (MyEnum)2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                15,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.ValueOption<MyStruct, MyStruct.BSATN>();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, null);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                16,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.Bool();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, true);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                2,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I8();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                3,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.U8();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                4,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I16();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                5,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.U16();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                6,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I32();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                7,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.U32();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                8,\n                array\n            );\n        }\n\n        {\n            var value = new SpacetimeDB.BSATN.I64();\n            __memoryStream.Position = 0;\n            __memoryStream.SetLength(0);\n            value.Write(__writer, 2);\n            var array = __memoryStream.ToArray();\n            SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\n                \"TestDefaultFieldValues\",\n                9,\n                array\n            );\n        }\n    }\n\n    // Exports only work from the main assembly, so we need to generate forwarding methods.\n#if EXPERIMENTAL_WASM_AOT\n    [UnmanagedCallersOnly(EntryPoint = \"__describe_module__\")]\n    public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) =>\n        SpacetimeDB.Internal.Module.__describe_module__(d);\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_reducer__\")]\n    public static SpacetimeDB.Internal.Errno __call_reducer__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink error\n    ) =>\n        SpacetimeDB.Internal.Module.__call_reducer__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            error\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_procedure__\")]\n    public static SpacetimeDB.Internal.Errno __call_procedure__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink result_sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_procedure__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            result_sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view__\")]\n    public static SpacetimeDB.Internal.Errno __call_view__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_view__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            args,\n            sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view_anon__\")]\n    public static SpacetimeDB.Internal.Errno __call_view_anon__(\n        uint id,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink);\n#endif\n}\n\n#pragma warning restore STDB_UNSTABLE\n#pragma warning restore CS0436\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#InAnotherNamespace.TestDuplicateTableName.verified.cs",
    "content": "﻿//HintName: InAnotherNamespace.TestDuplicateTableName.cs\n// <auto-generated />\n#nullable enable\n\npartial class InAnotherNamespace\n{\n    partial struct TestDuplicateTableName\n        : System.IEquatable<TestDuplicateTableName>,\n            SpacetimeDB.BSATN.IStructuralReadWrite\n    {\n        public void ReadFields(System.IO.BinaryReader reader) { }\n\n        public void WriteFields(System.IO.BinaryWriter writer) { }\n\n        object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n        {\n            return new BSATN();\n        }\n\n        public override string ToString() => $\"TestDuplicateTableName {{  }}\";\n\n        public readonly partial struct BSATN\n            : SpacetimeDB.BSATN.IReadWrite<InAnotherNamespace.TestDuplicateTableName>\n        {\n            public InAnotherNamespace.TestDuplicateTableName Read(System.IO.BinaryReader reader)\n            {\n                var ___result = new InAnotherNamespace.TestDuplicateTableName();\n                ___result.ReadFields(reader);\n                return ___result;\n            }\n\n            public void Write(\n                System.IO.BinaryWriter writer,\n                InAnotherNamespace.TestDuplicateTableName value\n            )\n            {\n                value.WriteFields(writer);\n            }\n\n            public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n                SpacetimeDB.BSATN.ITypeRegistrar registrar\n            ) =>\n                registrar.RegisterType<InAnotherNamespace.TestDuplicateTableName>(\n                    _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                        new SpacetimeDB.BSATN.AggregateElement[] { }\n                    )\n                );\n\n            SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<InAnotherNamespace.TestDuplicateTableName>.GetAlgebraicType(\n                SpacetimeDB.BSATN.ITypeRegistrar registrar\n            ) => GetAlgebraicType(registrar);\n        }\n\n        public override int GetHashCode()\n        {\n            return 0;\n        }\n\n#nullable enable\n        public bool Equals(InAnotherNamespace.TestDuplicateTableName that)\n        {\n            return true;\n        }\n\n        public override bool Equals(object? that)\n        {\n            if (that == null)\n            {\n                return false;\n            }\n            var that_ = that as InAnotherNamespace.TestDuplicateTableName?;\n            if (((object?)that_) == null)\n            {\n                return false;\n            }\n            return Equals(that_);\n        }\n\n        public static bool operator ==(\n            InAnotherNamespace.TestDuplicateTableName this_,\n            InAnotherNamespace.TestDuplicateTableName that\n        )\n        {\n            if (((object?)this_) == null || ((object?)that) == null)\n            {\n                return object.Equals(this_, that);\n            }\n            return this_.Equals(that);\n        }\n\n        public static bool operator !=(\n            InAnotherNamespace.TestDuplicateTableName this_,\n            InAnotherNamespace.TestDuplicateTableName that\n        )\n        {\n            if (((object?)this_) == null || ((object?)that) == null)\n            {\n                return !object.Equals(this_, that);\n            }\n            return !this_.Equals(that);\n        }\n#nullable restore\n    } // TestDuplicateTableName\n} // InAnotherNamespace\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Player.verified.cs",
    "content": "﻿//HintName: Player.cs\n// <auto-generated />\n#nullable enable\n\npartial struct Player : System.IEquatable<Player>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Identity = BSATN.IdentityRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdentityRW.Write(writer, Identity);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"Player {{ Identity = {SpacetimeDB.BSATN.StringUtil.GenericToString(Identity)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<Player>\n    {\n        internal static readonly SpacetimeDB.Identity.BSATN IdentityRW = new();\n\n        public Player Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new Player();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, Player value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<Player>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Identity\", IdentityRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<Player>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIdentity = Identity.GetHashCode();\n        return ___hashIdentity;\n    }\n\n#nullable enable\n    public bool Equals(Player that)\n    {\n        var ___eqIdentity = this.Identity.Equals(that.Identity);\n        return ___eqIdentity;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as Player?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(Player this_, Player that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(Player this_, Player that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // Player\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.InAnotherNamespace.TestDuplicateReducerName.verified.cs",
    "content": "﻿//HintName: Reducers.InAnotherNamespace.TestDuplicateReducerName.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    partial class InAnotherNamespace\n    {\n        [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n        public static void VolatileNonatomicScheduleImmediateTestDuplicateReducerName()\n        {\n            using var stream = new MemoryStream();\n            using var writer = new BinaryWriter(stream);\n\n            SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n                nameof(TestDuplicateReducerName),\n                stream\n            );\n        }\n    } // InAnotherNamespace\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.OnReducerWithReservedPrefix.verified.cs",
    "content": "﻿//HintName: Reducers.OnReducerWithReservedPrefix.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateOnReducerWithReservedPrefix()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(OnReducerWithReservedPrefix),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.TestDuplicateReducerKind1.verified.cs",
    "content": "﻿//HintName: Reducers.TestDuplicateReducerKind1.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateTestDuplicateReducerKind1()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(TestDuplicateReducerKind1),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.TestDuplicateReducerKind2.verified.cs",
    "content": "﻿//HintName: Reducers.TestDuplicateReducerKind2.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateTestDuplicateReducerKind2()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(TestDuplicateReducerKind2),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.TestDuplicateReducerName.verified.cs",
    "content": "﻿//HintName: Reducers.TestDuplicateReducerName.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateTestDuplicateReducerName()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(TestDuplicateReducerName),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.TestReducerReturnType.verified.cs",
    "content": "﻿//HintName: Reducers.TestReducerReturnType.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateTestReducerReturnType()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(TestReducerReturnType),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.TestReducerWithoutContext.verified.cs",
    "content": "﻿//HintName: Reducers.TestReducerWithoutContext.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateTestReducerWithoutContext()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(TestReducerWithoutContext),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#Reducers.__ReducerWithReservedPrefix.verified.cs",
    "content": "﻿//HintName: Reducers.__ReducerWithReservedPrefix.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediate__ReducerWithReservedPrefix()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(__ReducerWithReservedPrefix),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestAutoIncNotInteger.verified.cs",
    "content": "﻿//HintName: TestAutoIncNotInteger.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestAutoIncNotInteger\n    : System.IEquatable<TestAutoIncNotInteger>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        AutoIncField = BSATN.AutoIncFieldRW.Read(reader);\n        IdentityField = BSATN.IdentityFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.AutoIncFieldRW.Write(writer, AutoIncField);\n        BSATN.IdentityFieldRW.Write(writer, IdentityField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestAutoIncNotInteger {{ AutoIncField = {SpacetimeDB.BSATN.StringUtil.GenericToString(AutoIncField)}, IdentityField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IdentityField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestAutoIncNotInteger>\n    {\n        internal static readonly SpacetimeDB.BSATN.F32 AutoIncFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String IdentityFieldRW = new();\n\n        public TestAutoIncNotInteger Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestAutoIncNotInteger();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestAutoIncNotInteger value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestAutoIncNotInteger>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"AutoIncField\", AutoIncFieldRW.GetAlgebraicType(registrar)),\n                        new(\"IdentityField\", IdentityFieldRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestAutoIncNotInteger>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashAutoIncField = AutoIncField.GetHashCode();\n        var ___hashIdentityField = IdentityField == null ? 0 : IdentityField.GetHashCode();\n        return ___hashAutoIncField ^ ___hashIdentityField;\n    }\n\n#nullable enable\n    public bool Equals(TestAutoIncNotInteger that)\n    {\n        var ___eqAutoIncField = this.AutoIncField.Equals(that.AutoIncField);\n        var ___eqIdentityField =\n            this.IdentityField == null\n                ? that.IdentityField == null\n                : this.IdentityField.Equals(that.IdentityField);\n        return ___eqAutoIncField && ___eqIdentityField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestAutoIncNotInteger?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestAutoIncNotInteger this_, TestAutoIncNotInteger that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestAutoIncNotInteger this_, TestAutoIncNotInteger that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestAutoIncNotInteger\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestDefaultFieldValues.verified.cs",
    "content": "﻿//HintName: TestDefaultFieldValues.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestDefaultFieldValues\n    : System.IEquatable<TestDefaultFieldValues>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        UniqueField = BSATN.UniqueFieldRW.Read(reader);\n        DefaultString = BSATN.DefaultStringRW.Read(reader);\n        DefaultBool = BSATN.DefaultBoolRW.Read(reader);\n        DefaultI8 = BSATN.DefaultI8RW.Read(reader);\n        DefaultU8 = BSATN.DefaultU8RW.Read(reader);\n        DefaultI16 = BSATN.DefaultI16RW.Read(reader);\n        DefaultU16 = BSATN.DefaultU16RW.Read(reader);\n        DefaultI32 = BSATN.DefaultI32RW.Read(reader);\n        DefaultU32 = BSATN.DefaultU32RW.Read(reader);\n        DefaultI64 = BSATN.DefaultI64RW.Read(reader);\n        DefaultU64 = BSATN.DefaultU64RW.Read(reader);\n        DefaultHex = BSATN.DefaultHexRW.Read(reader);\n        DefaultBin = BSATN.DefaultBinRW.Read(reader);\n        DefaultF32 = BSATN.DefaultF32RW.Read(reader);\n        DefaultF64 = BSATN.DefaultF64RW.Read(reader);\n        DefaultEnum = BSATN.DefaultEnumRW.Read(reader);\n        DefaultNull = BSATN.DefaultNullRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.UniqueFieldRW.Write(writer, UniqueField);\n        BSATN.DefaultStringRW.Write(writer, DefaultString);\n        BSATN.DefaultBoolRW.Write(writer, DefaultBool);\n        BSATN.DefaultI8RW.Write(writer, DefaultI8);\n        BSATN.DefaultU8RW.Write(writer, DefaultU8);\n        BSATN.DefaultI16RW.Write(writer, DefaultI16);\n        BSATN.DefaultU16RW.Write(writer, DefaultU16);\n        BSATN.DefaultI32RW.Write(writer, DefaultI32);\n        BSATN.DefaultU32RW.Write(writer, DefaultU32);\n        BSATN.DefaultI64RW.Write(writer, DefaultI64);\n        BSATN.DefaultU64RW.Write(writer, DefaultU64);\n        BSATN.DefaultHexRW.Write(writer, DefaultHex);\n        BSATN.DefaultBinRW.Write(writer, DefaultBin);\n        BSATN.DefaultF32RW.Write(writer, DefaultF32);\n        BSATN.DefaultF64RW.Write(writer, DefaultF64);\n        BSATN.DefaultEnumRW.Write(writer, DefaultEnum);\n        BSATN.DefaultNullRW.Write(writer, DefaultNull);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestDefaultFieldValues {{ UniqueField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UniqueField)}, DefaultString = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultString)}, DefaultBool = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultBool)}, DefaultI8 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultI8)}, DefaultU8 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultU8)}, DefaultI16 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultI16)}, DefaultU16 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultU16)}, DefaultI32 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultI32)}, DefaultU32 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultU32)}, DefaultI64 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultI64)}, DefaultU64 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultU64)}, DefaultHex = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultHex)}, DefaultBin = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultBin)}, DefaultF32 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultF32)}, DefaultF64 = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultF64)}, DefaultEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultEnum)}, DefaultNull = {SpacetimeDB.BSATN.StringUtil.GenericToString(DefaultNull)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestDefaultFieldValues>\n    {\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > UniqueFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String DefaultStringRW = new();\n        internal static readonly SpacetimeDB.BSATN.Bool DefaultBoolRW = new();\n        internal static readonly SpacetimeDB.BSATN.I8 DefaultI8RW = new();\n        internal static readonly SpacetimeDB.BSATN.U8 DefaultU8RW = new();\n        internal static readonly SpacetimeDB.BSATN.I16 DefaultI16RW = new();\n        internal static readonly SpacetimeDB.BSATN.U16 DefaultU16RW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 DefaultI32RW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 DefaultU32RW = new();\n        internal static readonly SpacetimeDB.BSATN.I64 DefaultI64RW = new();\n        internal static readonly SpacetimeDB.BSATN.U64 DefaultU64RW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 DefaultHexRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 DefaultBinRW = new();\n        internal static readonly SpacetimeDB.BSATN.F32 DefaultF32RW = new();\n        internal static readonly SpacetimeDB.BSATN.F64 DefaultF64RW = new();\n        internal static readonly SpacetimeDB.BSATN.Enum<MyEnum> DefaultEnumRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            MyStruct,\n            MyStruct.BSATN\n        > DefaultNullRW = new();\n\n        public TestDefaultFieldValues Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestDefaultFieldValues();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestDefaultFieldValues value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestDefaultFieldValues>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"UniqueField\", UniqueFieldRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultString\", DefaultStringRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultBool\", DefaultBoolRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultI8\", DefaultI8RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultU8\", DefaultU8RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultI16\", DefaultI16RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultU16\", DefaultU16RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultI32\", DefaultI32RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultU32\", DefaultU32RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultI64\", DefaultI64RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultU64\", DefaultU64RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultHex\", DefaultHexRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultBin\", DefaultBinRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultF32\", DefaultF32RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultF64\", DefaultF64RW.GetAlgebraicType(registrar)),\n                        new(\"DefaultEnum\", DefaultEnumRW.GetAlgebraicType(registrar)),\n                        new(\"DefaultNull\", DefaultNullRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestDefaultFieldValues>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashUniqueField = UniqueField.GetHashCode();\n        var ___hashDefaultString = DefaultString == null ? 0 : DefaultString.GetHashCode();\n        var ___hashDefaultBool = DefaultBool.GetHashCode();\n        var ___hashDefaultI8 = DefaultI8.GetHashCode();\n        var ___hashDefaultU8 = DefaultU8.GetHashCode();\n        var ___hashDefaultI16 = DefaultI16.GetHashCode();\n        var ___hashDefaultU16 = DefaultU16.GetHashCode();\n        var ___hashDefaultI32 = DefaultI32.GetHashCode();\n        var ___hashDefaultU32 = DefaultU32.GetHashCode();\n        var ___hashDefaultI64 = DefaultI64.GetHashCode();\n        var ___hashDefaultU64 = DefaultU64.GetHashCode();\n        var ___hashDefaultHex = DefaultHex.GetHashCode();\n        var ___hashDefaultBin = DefaultBin.GetHashCode();\n        var ___hashDefaultF32 = DefaultF32.GetHashCode();\n        var ___hashDefaultF64 = DefaultF64.GetHashCode();\n        var ___hashDefaultEnum = DefaultEnum.GetHashCode();\n        var ___hashDefaultNull = DefaultNull.GetHashCode();\n        return ___hashUniqueField\n            ^ ___hashDefaultString\n            ^ ___hashDefaultBool\n            ^ ___hashDefaultI8\n            ^ ___hashDefaultU8\n            ^ ___hashDefaultI16\n            ^ ___hashDefaultU16\n            ^ ___hashDefaultI32\n            ^ ___hashDefaultU32\n            ^ ___hashDefaultI64\n            ^ ___hashDefaultU64\n            ^ ___hashDefaultHex\n            ^ ___hashDefaultBin\n            ^ ___hashDefaultF32\n            ^ ___hashDefaultF64\n            ^ ___hashDefaultEnum\n            ^ ___hashDefaultNull;\n    }\n\n#nullable enable\n    public bool Equals(TestDefaultFieldValues that)\n    {\n        var ___eqUniqueField = System.Nullable.Equals(this.UniqueField, that.UniqueField);\n        var ___eqDefaultString =\n            this.DefaultString == null\n                ? that.DefaultString == null\n                : this.DefaultString.Equals(that.DefaultString);\n        var ___eqDefaultBool = this.DefaultBool.Equals(that.DefaultBool);\n        var ___eqDefaultI8 = this.DefaultI8.Equals(that.DefaultI8);\n        var ___eqDefaultU8 = this.DefaultU8.Equals(that.DefaultU8);\n        var ___eqDefaultI16 = this.DefaultI16.Equals(that.DefaultI16);\n        var ___eqDefaultU16 = this.DefaultU16.Equals(that.DefaultU16);\n        var ___eqDefaultI32 = this.DefaultI32.Equals(that.DefaultI32);\n        var ___eqDefaultU32 = this.DefaultU32.Equals(that.DefaultU32);\n        var ___eqDefaultI64 = this.DefaultI64.Equals(that.DefaultI64);\n        var ___eqDefaultU64 = this.DefaultU64.Equals(that.DefaultU64);\n        var ___eqDefaultHex = this.DefaultHex.Equals(that.DefaultHex);\n        var ___eqDefaultBin = this.DefaultBin.Equals(that.DefaultBin);\n        var ___eqDefaultF32 = this.DefaultF32.Equals(that.DefaultF32);\n        var ___eqDefaultF64 = this.DefaultF64.Equals(that.DefaultF64);\n        var ___eqDefaultEnum = this.DefaultEnum == that.DefaultEnum;\n        var ___eqDefaultNull = System.Nullable.Equals(this.DefaultNull, that.DefaultNull);\n        return ___eqUniqueField\n            && ___eqDefaultString\n            && ___eqDefaultBool\n            && ___eqDefaultI8\n            && ___eqDefaultU8\n            && ___eqDefaultI16\n            && ___eqDefaultU16\n            && ___eqDefaultI32\n            && ___eqDefaultU32\n            && ___eqDefaultI64\n            && ___eqDefaultU64\n            && ___eqDefaultHex\n            && ___eqDefaultBin\n            && ___eqDefaultF32\n            && ___eqDefaultF64\n            && ___eqDefaultEnum\n            && ___eqDefaultNull;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestDefaultFieldValues?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestDefaultFieldValues this_, TestDefaultFieldValues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestDefaultFieldValues this_, TestDefaultFieldValues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestDefaultFieldValues\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestDuplicateTableName.verified.cs",
    "content": "﻿//HintName: TestDuplicateTableName.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestDuplicateTableName\n    : System.IEquatable<TestDuplicateTableName>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader) { }\n\n    public void WriteFields(System.IO.BinaryWriter writer) { }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() => $\"TestDuplicateTableName {{  }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestDuplicateTableName>\n    {\n        public TestDuplicateTableName Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestDuplicateTableName();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestDuplicateTableName value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestDuplicateTableName>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[] { }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestDuplicateTableName>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        return 0;\n    }\n\n#nullable enable\n    public bool Equals(TestDuplicateTableName that)\n    {\n        return true;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestDuplicateTableName?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestDuplicateTableName this_, TestDuplicateTableName that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestDuplicateTableName this_, TestDuplicateTableName that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestDuplicateTableName\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIndexIssues.verified.cs",
    "content": "﻿//HintName: TestIndexIssues.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestIndexIssues\n    : System.IEquatable<TestIndexIssues>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        SelfIndexingColumn = BSATN.SelfIndexingColumnRW.Read(reader);\n        SecondaryIndexingColumn = BSATN.SecondaryIndexingColumnRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.SelfIndexingColumnRW.Write(writer, SelfIndexingColumn);\n        BSATN.SecondaryIndexingColumnRW.Write(writer, SecondaryIndexingColumn);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestIndexIssues {{ SelfIndexingColumn = {SpacetimeDB.BSATN.StringUtil.GenericToString(SelfIndexingColumn)}, SecondaryIndexingColumn = {SpacetimeDB.BSATN.StringUtil.GenericToString(SecondaryIndexingColumn)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestIndexIssues>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 SelfIndexingColumnRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 SecondaryIndexingColumnRW = new();\n\n        public TestIndexIssues Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestIndexIssues();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestIndexIssues value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestIndexIssues>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"SelfIndexingColumn\", SelfIndexingColumnRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"SecondaryIndexingColumn\",\n                            SecondaryIndexingColumnRW.GetAlgebraicType(registrar)\n                        )\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestIndexIssues>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashSelfIndexingColumn = SelfIndexingColumn.GetHashCode();\n        var ___hashSecondaryIndexingColumn = SecondaryIndexingColumn.GetHashCode();\n        return ___hashSelfIndexingColumn ^ ___hashSecondaryIndexingColumn;\n    }\n\n#nullable enable\n    public bool Equals(TestIndexIssues that)\n    {\n        var ___eqSelfIndexingColumn = this.SelfIndexingColumn.Equals(that.SelfIndexingColumn);\n        var ___eqSecondaryIndexingColumn = this.SecondaryIndexingColumn.Equals(\n            that.SecondaryIndexingColumn\n        );\n        return ___eqSelfIndexingColumn && ___eqSecondaryIndexingColumn;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestIndexIssues?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestIndexIssues this_, TestIndexIssues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestIndexIssues this_, TestIndexIssues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestIndexIssues\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestScheduleIssues.DummyScheduledReducer.verified.cs",
    "content": "﻿//HintName: TestScheduleIssues.DummyScheduledReducer.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestScheduleIssues\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateDummyScheduledReducer(\n        TestScheduleIssues table\n    )\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new TestScheduleIssues.BSATN().Write(writer, table);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(DummyScheduledReducer),\n            stream\n        );\n    }\n} // TestScheduleIssues\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestScheduleIssues.verified.cs",
    "content": "﻿//HintName: TestScheduleIssues.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestScheduleIssues\n    : System.IEquatable<TestScheduleIssues>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IdWrongType = BSATN.IdWrongTypeRW.Read(reader);\n        IdCorrectType = BSATN.IdCorrectTypeRW.Read(reader);\n        ScheduleAtWrongType = BSATN.ScheduleAtWrongTypeRW.Read(reader);\n        ScheduleAtCorrectType = BSATN.ScheduleAtCorrectTypeRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdWrongTypeRW.Write(writer, IdWrongType);\n        BSATN.IdCorrectTypeRW.Write(writer, IdCorrectType);\n        BSATN.ScheduleAtWrongTypeRW.Write(writer, ScheduleAtWrongType);\n        BSATN.ScheduleAtCorrectTypeRW.Write(writer, ScheduleAtCorrectType);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestScheduleIssues {{ IdWrongType = {SpacetimeDB.BSATN.StringUtil.GenericToString(IdWrongType)}, IdCorrectType = {SpacetimeDB.BSATN.StringUtil.GenericToString(IdCorrectType)}, ScheduleAtWrongType = {SpacetimeDB.BSATN.StringUtil.GenericToString(ScheduleAtWrongType)}, ScheduleAtCorrectType = {SpacetimeDB.BSATN.StringUtil.GenericToString(ScheduleAtCorrectType)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestScheduleIssues>\n    {\n        internal static readonly SpacetimeDB.BSATN.String IdWrongTypeRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 IdCorrectTypeRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 ScheduleAtWrongTypeRW = new();\n        internal static readonly SpacetimeDB.ScheduleAt.BSATN ScheduleAtCorrectTypeRW = new();\n\n        public TestScheduleIssues Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestScheduleIssues();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestScheduleIssues value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestScheduleIssues>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"IdWrongType\", IdWrongTypeRW.GetAlgebraicType(registrar)),\n                        new(\"IdCorrectType\", IdCorrectTypeRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"ScheduleAtWrongType\",\n                            ScheduleAtWrongTypeRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\n                            \"ScheduleAtCorrectType\",\n                            ScheduleAtCorrectTypeRW.GetAlgebraicType(registrar)\n                        )\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestScheduleIssues>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIdWrongType = IdWrongType == null ? 0 : IdWrongType.GetHashCode();\n        var ___hashIdCorrectType = IdCorrectType.GetHashCode();\n        var ___hashScheduleAtWrongType = ScheduleAtWrongType.GetHashCode();\n        var ___hashScheduleAtCorrectType =\n            ScheduleAtCorrectType == null ? 0 : ScheduleAtCorrectType.GetHashCode();\n        return ___hashIdWrongType\n            ^ ___hashIdCorrectType\n            ^ ___hashScheduleAtWrongType\n            ^ ___hashScheduleAtCorrectType;\n    }\n\n#nullable enable\n    public bool Equals(TestScheduleIssues that)\n    {\n        var ___eqIdWrongType =\n            this.IdWrongType == null\n                ? that.IdWrongType == null\n                : this.IdWrongType.Equals(that.IdWrongType);\n        var ___eqIdCorrectType = this.IdCorrectType.Equals(that.IdCorrectType);\n        var ___eqScheduleAtWrongType = this.ScheduleAtWrongType.Equals(that.ScheduleAtWrongType);\n        var ___eqScheduleAtCorrectType =\n            this.ScheduleAtCorrectType == null\n                ? that.ScheduleAtCorrectType == null\n                : this.ScheduleAtCorrectType.Equals(that.ScheduleAtCorrectType);\n        return ___eqIdWrongType\n            && ___eqIdCorrectType\n            && ___eqScheduleAtWrongType\n            && ___eqScheduleAtCorrectType;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestScheduleIssues?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestScheduleIssues this_, TestScheduleIssues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestScheduleIssues this_, TestScheduleIssues that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestScheduleIssues\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestTableTaggedEnum.verified.cs",
    "content": "﻿//HintName: TestTableTaggedEnum.cs\n// <auto-generated />\n#nullable enable\n\npartial record TestTableTaggedEnum : System.IEquatable<TestTableTaggedEnum>\n{\n    public sealed record X(int X_) : TestTableTaggedEnum\n    {\n        public override string ToString() =>\n            $\"X({SpacetimeDB.BSATN.StringUtil.GenericToString(X_)})\";\n    }\n\n    public sealed record Y(int Y_) : TestTableTaggedEnum\n    {\n        public override string ToString() =>\n            $\"Y({SpacetimeDB.BSATN.StringUtil.GenericToString(Y_)})\";\n    }\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestTableTaggedEnum>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 XRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 YRW = new();\n\n        public TestTableTaggedEnum Read(System.IO.BinaryReader reader)\n        {\n            return reader.ReadByte() switch\n            {\n                0 => new X(XRW.Read(reader)),\n                1 => new Y(YRW.Read(reader)),\n                _\n                    => throw new System.InvalidOperationException(\n                        \"Invalid tag value, this state should be unreachable.\"\n                    )\n            };\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestTableTaggedEnum value)\n        {\n            switch (value)\n            {\n                case X(var inner):\n                    writer.Write((byte)0);\n                    XRW.Write(writer, inner);\n                    break;\n                case Y(var inner):\n                    writer.Write((byte)1);\n                    YRW.Write(writer, inner);\n                    break;\n            }\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestTableTaggedEnum>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Sum(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"X\", XRW.GetAlgebraicType(registrar)),\n                        new(\"Y\", YRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestTableTaggedEnum>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        switch (this)\n        {\n            case X(var inner):\n                var ___hashX = inner.GetHashCode();\n                return ___hashX;\n            case Y(var inner):\n                var ___hashY = inner.GetHashCode();\n                return ___hashY;\n            default:\n                return 0;\n        }\n    }\n} // TestTableTaggedEnum\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestUniqueNotEquatable.verified.cs",
    "content": "﻿//HintName: TestUniqueNotEquatable.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestUniqueNotEquatable\n    : System.IEquatable<TestUniqueNotEquatable>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        UniqueField = BSATN.UniqueFieldRW.Read(reader);\n        PrimaryKeyField = BSATN.PrimaryKeyFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.UniqueFieldRW.Write(writer, UniqueField);\n        BSATN.PrimaryKeyFieldRW.Write(writer, PrimaryKeyField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestUniqueNotEquatable {{ UniqueField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UniqueField)}, PrimaryKeyField = {SpacetimeDB.BSATN.StringUtil.GenericToString(PrimaryKeyField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestUniqueNotEquatable>\n    {\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > UniqueFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.Enum<TestEnumWithExplicitValues> PrimaryKeyFieldRW =\n            new();\n\n        public TestUniqueNotEquatable Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestUniqueNotEquatable();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestUniqueNotEquatable value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestUniqueNotEquatable>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"UniqueField\", UniqueFieldRW.GetAlgebraicType(registrar)),\n                        new(\"PrimaryKeyField\", PrimaryKeyFieldRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestUniqueNotEquatable>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashUniqueField = UniqueField.GetHashCode();\n        var ___hashPrimaryKeyField = PrimaryKeyField.GetHashCode();\n        return ___hashUniqueField ^ ___hashPrimaryKeyField;\n    }\n\n#nullable enable\n    public bool Equals(TestUniqueNotEquatable that)\n    {\n        var ___eqUniqueField = System.Nullable.Equals(this.UniqueField, that.UniqueField);\n        var ___eqPrimaryKeyField = this.PrimaryKeyField == that.PrimaryKeyField;\n        return ___eqUniqueField && ___eqPrimaryKeyField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestUniqueNotEquatable?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestUniqueNotEquatable this_, TestUniqueNotEquatable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestUniqueNotEquatable this_, TestUniqueNotEquatable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestUniqueNotEquatable\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt",
    "content": "﻿{\n  Diagnostics: [\n    {/*\n    [AutoInc]\n    public float AutoIncField;\n                 ^^^^^^^^^^^^\n\n*/\n      Message: Field AutoIncField is marked as AutoInc but it has a non-integer type float.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0002,\n        Title: AutoInc fields must be of integer type,\n        MessageFormat: Field {0} is marked as AutoInc but it has a non-integer type {1}.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [AutoInc]\n    public string IdentityField;\n                  ^^^^^^^^^^^^^\n}\n*/\n      Message: Field IdentityField is marked as AutoInc but it has a non-integer type string.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0002,\n        Title: AutoInc fields must be of integer type,\n        MessageFormat: Field {0} is marked as AutoInc but it has a non-integer type {1}.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [Unique]\n    public int? UniqueField;\n                ^^^^^^^^^^^\n\n*/\n      Message: Field UniqueField is marked as Unique but it has a type int? which is not an equatable primitive.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0003,\n        Title: Unique fields must be equatable,\n        MessageFormat: Field {0} is marked as Unique but it has a type {1} which is not an equatable primitive.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Table]\npublic partial record TestTableTaggedEnum : SpacetimeDB.TaggedEnum<(int X, int Y)> { }\n                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n*/\n      Message: Table TestTableTaggedEnum is a tagged enum, which is not allowed.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0006,\n        Title: Tables cannot be tagged enums,\n        MessageFormat: Table {0} is a tagged enum, which is not allowed.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [Unique]\n    public int? UniqueField;\n                ^^^^^^^^^^^\n\n*/\n      Message: Field UniqueField is marked as Unique but it has a type int? which is not an equatable primitive.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0003,\n        Title: Unique fields must be equatable,\n        MessageFormat: Field {0} is marked as Unique but it has a type {1} which is not an equatable primitive.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n{\n    [SpacetimeDB.Index.BTree(Accessor = \"TestUnexpectedColumns\", Columns = [\"UnexpectedColumn\"])]\n     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    public int SelfIndexingColumn;\n*/\n      Message: Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0015,\n        Title: Index attribute on a field must not specify Columns,\n        MessageFormat: Index attribute on a field applies directly to that field, so it doesn't accept the Columns parameter.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Table]\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithoutColumns\")]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithEmptyColumns\", Columns = [])]\n*/\n      Message: Index attribute doesn't specify columns.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0004,\n        Title: Index attribute must specify Columns,\n        MessageFormat: Index attribute doesn't specify columns.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithoutColumns\")]\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithEmptyColumns\", Columns = [])]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n[SpacetimeDB.Index.BTree(Accessor = \"TestUnknownColumns\", Columns = [\"UnknownColumn\"])]\n*/\n      Message: Index attribute doesn't specify columns.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0004,\n        Title: Index attribute must specify Columns,\n        MessageFormat: Index attribute doesn't specify columns.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Index.BTree(Accessor = \"TestIndexWithEmptyColumns\", Columns = [])]\n[SpacetimeDB.Index.BTree(Accessor = \"TestUnknownColumns\", Columns = [\"UnknownColumn\"])]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n[SpacetimeDB.Index.BTree(Columns = [\"SelfIndexingColumn\"])]\n*/\n      Message: Could not find the specified column UnknownColumn in TestIndexIssues.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0016,\n        Title: Unknown column,\n        MessageFormat: Could not find the specified column {0} in {1}.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Index.BTree(Accessor = \"TestUnknownColumns\", Columns = [\"UnknownColumn\"])]\n[SpacetimeDB.Index.BTree(Columns = [\"SelfIndexingColumn\"])]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n[SpacetimeDB.Index.BTree(\n*/\n      Message: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0029,\n        Title: Table-level index attributes must specify Accessor,\n        MessageFormat: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Index.BTree(Columns = [\"SelfIndexingColumn\"])]\n[SpacetimeDB.Index.BTree(\n ^^^^^^^^^^^^^^^^^^^^^^^^\n    Name = \"TestCanonicalNameWithoutAccessor\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    Columns = [\"SecondaryIndexingColumn\"]\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n)]\n^\npublic partial struct TestIndexIssues\n*/\n      Message: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0029,\n        Title: Table-level index attributes must specify Accessor,\n        MessageFormat: Index attribute on a table declaration must specify Accessor. Field-level index attributes may omit Accessor and default to the field name.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n\n[SpacetimeDB.Table(\n ^^^^^^^^^^^^^^^^^^\n    Accessor = \"TestScheduleWithoutPrimaryKey\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    Scheduled = \"DummyScheduledReducer\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ScheduledAt = nameof(ScheduleAtCorrectType)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n)]\n^\n[SpacetimeDB.Table(\n*/\n      Message: TestScheduleWithoutPrimaryKey is a scheduled table but doesn't have a primary key of type `ulong`.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0014,\n        Title: Invalid scheduled table declaration,\n        MessageFormat: {0},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n)]\n[SpacetimeDB.Table(\n ^^^^^^^^^^^^^^^^^^\n    Accessor = \"TestScheduleWithWrongPrimaryKeyType\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    Scheduled = \"DummyScheduledReducer\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ScheduledAt = nameof(ScheduleAtCorrectType)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n)]\n^\n[SpacetimeDB.Table(Accessor = \"TestScheduleWithoutScheduleAt\", Scheduled = \"DummyScheduledReducer\")]\n*/\n      Message: TestScheduleWithWrongPrimaryKeyType is a scheduled table but doesn't have a primary key of type `ulong`.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0014,\n        Title: Invalid scheduled table declaration,\n        MessageFormat: {0},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n)]\n[SpacetimeDB.Table(Accessor = \"TestScheduleWithoutScheduleAt\", Scheduled = \"DummyScheduledReducer\")]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n[SpacetimeDB.Table(\n*/\n      Message: Could not find the specified column ScheduledAt in TestScheduleIssues.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0016,\n        Title: Unknown column,\n        MessageFormat: Could not find the specified column {0} in {1}.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Table(Accessor = \"TestScheduleWithoutScheduleAt\", Scheduled = \"DummyScheduledReducer\")]\n[SpacetimeDB.Table(\n ^^^^^^^^^^^^^^^^^^\n    Accessor = \"TestScheduleWithWrongScheduleAtType\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    Scheduled = \"DummyScheduledReducer\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ScheduledAt = nameof(ScheduleAtWrongType)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n)]\n^\n[SpacetimeDB.Table(\n*/\n      Message: TestScheduleWithWrongScheduleAtType is a scheduled table but doesn't have a primary key of type `ulong`.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0014,\n        Title: Invalid scheduled table declaration,\n        MessageFormat: {0},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n)]\n[SpacetimeDB.Table(\n ^^^^^^^^^^^^^^^^^^\n    Accessor = \"TestScheduleWithMissingScheduleAtField\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    Scheduled = \"DummyScheduledReducer\",\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    ScheduledAt = \"MissingField\"\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n)]\n^\npublic partial struct TestScheduleIssues\n*/\n      Message: Could not find the specified column MissingField in TestScheduleIssues.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0016,\n        Title: Unknown column,\n        MessageFormat: Could not find the specified column {0} in {1}.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    // Invalid: View definition missing Public=true\n    [SpacetimeDB.View(Accessor = \"view_def_no_public\")]\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    public static List<Player> ViewDefNoPublic(ViewContext ctx)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    {\n^^^^^\n        return new List<Player>();\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    }\n^^^^^\n\n*/\n      Message: View 'ViewDefNoPublic' must have Public = true. Views are always public in SpacetimeDB.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0025,\n        Title: Views must be public,\n        MessageFormat: View '{0}' must have Public = true. Views are always public in SpacetimeDB.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_no_context\", Public = true)]\n    public static List<Player> ViewDefNoContext()\n                                               ^^\n    {\n*/\n      Message: View method ViewDefNoContext must have a first parameter of type ViewContext or AnonymousViewContext.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0022,\n        Title: Views must start with ViewContext or AnonymousViewContext,\n        MessageFormat: View method {0} must have a first parameter of type ViewContext or AnonymousViewContext.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_wrong_context\", Public = true)]\n    public static List<Player> ViewDefWrongContext(ReducerContext ctx)\n                                                  ^^^^^^^^^^^^^^^^^^^^\n    {\n*/\n      Message: View method ViewDefWrongContext must have a first parameter of type ViewContext or AnonymousViewContext.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0022,\n        Title: Views must start with ViewContext or AnonymousViewContext,\n        MessageFormat: View method {0} must have a first parameter of type ViewContext or AnonymousViewContext.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_wrong_return\", Public = true)]\n    public static Player ViewDefWrongReturn(ViewContext ctx)\n                  ^^^^^^\n    {\n*/\n      Message: View 'ViewDefWrongReturn' must return T?, List<T>, or IQuery<T>,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0024,\n        Title: Views must return T?, List<T>, or IQuery<T>,\n        MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_iter\", Public = true)]\n    public static IEnumerable<Player> ViewDefIEnumerableReturnFromIter(ViewContext ctx)\n                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    {\n*/\n      Message: BSATN implementation for System.Collections.Generic.IEnumerable<Player> is not found: Unsupported system type System.Collections.Generic.IEnumerable<T>,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0001,\n        Title: Unsupported type,\n        MessageFormat: BSATN implementation for {0} is not found: {1},\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_iter\", Public = true)]\n    public static IEnumerable<Player> ViewDefIEnumerableReturnFromIter(ViewContext ctx)\n                  ^^^^^^^^^^^^^^^^^^^\n    {\n*/\n      Message: View 'ViewDefIEnumerableReturnFromIter' must return T?, List<T>, or IQuery<T>,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0024,\n        Title: Views must return T?, List<T>, or IQuery<T>,\n        MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_filter\", Public = true)]\n    public static IEnumerable<TestScheduleIssues> ViewDefIEnumerableReturnFromFilter(\n                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n        ViewContext ctx\n*/\n      Message: BSATN implementation for System.Collections.Generic.IEnumerable<TestScheduleIssues> is not found: Unsupported system type System.Collections.Generic.IEnumerable<T>,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0001,\n        Title: Unsupported type,\n        MessageFormat: BSATN implementation for {0} is not found: {1},\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.View(Accessor = \"view_def_ienumerable_return_from_filter\", Public = true)]\n    public static IEnumerable<TestScheduleIssues> ViewDefIEnumerableReturnFromFilter(\n                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n        ViewContext ctx\n*/\n      Message: View 'ViewDefIEnumerableReturnFromFilter' must return T?, List<T>, or IQuery<T>,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0024,\n        Title: Views must return T?, List<T>, or IQuery<T>,\n        MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.Reducer]\n    public static int TestReducerReturnType(ReducerContext ctx) => 0;\n                  ^^^\n\n*/\n      Message: Reducer method TestReducerReturnType returns int instead of void.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0001,\n        Title: [SpacetimeDB.Reducer] methods must return void,\n        MessageFormat: Reducer method {0} returns {1} instead of void.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.Reducer]\n    public static void TestReducerWithoutContext() { }\n                                                ^^\n\n*/\n      Message: Reducer method TestReducerWithoutContext does not have a ReducerContext parameter.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0008,\n        Title: Reducers must have a first argument of type ReducerContext,\n        MessageFormat: Reducer method {0} does not have a ReducerContext parameter.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.Reducer]\n    public static void OnReducerWithReservedPrefix(ReducerContext ctx) { }\n                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n*/\n      Message: Reducer method OnReducerWithReservedPrefix starts with 'On', which is a reserved prefix.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0010,\n        Title: Reducer method has a reserved name prefix,\n        MessageFormat: Reducer method {0} starts with '{1}', which is a reserved prefix.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.Reducer]\n    public static void __ReducerWithReservedPrefix(ReducerContext ctx) { }\n                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n      Message: Reducer method __ReducerWithReservedPrefix starts with '__', which is a reserved prefix.,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0010,\n        Title: Reducer method has a reserved name prefix,\n        MessageFormat: Reducer method {0} starts with '{1}', which is a reserved prefix.,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {\n      Location: ,\n      Message: Several reducers are assigned to the same lifecycle kind Init: Reducers.TestDuplicateReducerKind1, Reducers.TestDuplicateReducerKind2,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0013,\n        Title: Multiple reducers of the same kind,\n        MessageFormat: Several reducers are assigned to the same lifecycle kind {0}: {1},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {\n      Location: ,\n      Message: Reducer with the same export name TestDuplicateReducerName is registered in multiple places: Reducers.TestDuplicateReducerName, Reducers.InAnotherNamespace.TestDuplicateReducerName,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0007,\n        Title: Duplicate exports,\n        MessageFormat: {0} with the same export name {1} is registered in multiple places: {2},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {\n      Location: ,\n      Message: Table with the same export name TestDuplicateTableName is registered in multiple places: global::TestDuplicateTableName, global::InAnotherNamespace.TestDuplicateTableName,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0007,\n        Title: Duplicate exports,\n        MessageFormat: {0} with the same export name {1} is registered in multiple places: {2},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {\n      Location: ,\n      Message: TableReadOnly with the same export name TestDuplicateTableNameReadOnly is registered in multiple places: global::TestDuplicateTableName, global::InAnotherNamespace.TestDuplicateTableName,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0007,\n        Title: Duplicate exports,\n        MessageFormat: {0} with the same export name {1} is registered in multiple places: {2},\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.ClientVisibilityFilter]\n    private Filter MY_FILTER = new Filter.Sql(\"SELECT * FROM TestAutoIncNotInteger\");\n                   ^^^^^^^^^\n\n*/\n      Message: Field MY_FILTER is marked as [ClientVisibilityFilter] but it is not public static readonly,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0018,\n        Title: ClientVisibilityFilters must be public static readonly,\n        MessageFormat: Field {0} is marked as [ClientVisibilityFilter] but it is not public static readonly,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static Filter MY_SECOND_FILTER = new Filter.Sql(\"SELECT * FROM TestAutoIncNotInteger\");\n                         ^^^^^^^^^^^^^^^^\n\n*/\n      Message: Field MY_SECOND_FILTER is marked as [ClientVisibilityFilter] but it is not public static readonly,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0018,\n        Title: ClientVisibilityFilters must be public static readonly,\n        MessageFormat: Field {0} is marked as [ClientVisibilityFilter] but it is not public static readonly,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static readonly string MY_THIRD_FILTER = \"SELECT * FROM TestAutoIncNotInteger\";\n                                  ^^^^^^^^^^^^^^^\n\n*/\n      Message: Field MY_THIRD_FILTER is marked as ClientVisibilityFilter but it has type string which is not SpacetimeDB.Filter,\n      Severity: Error,\n      Descriptor: {\n        Id: STDB0017,\n        Title: ClientVisibilityFilters must be Filters,\n        MessageFormat: Field {0} is marked as ClientVisibilityFilter but it has type {1} which is not SpacetimeDB.Filter,\n        Category: SpacetimeDB,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    }\n  ]\n}"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type#MyStruct.verified.cs",
    "content": "﻿//HintName: MyStruct.cs\n// <auto-generated />\n#nullable enable\n\npartial struct MyStruct : System.IEquatable<MyStruct>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        x = BSATN.xRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.xRW.Write(writer, x);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"MyStruct {{ x = {SpacetimeDB.BSATN.StringUtil.GenericToString(x)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<MyStruct>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 xRW = new();\n\n        public MyStruct Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new MyStruct();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, MyStruct value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<MyStruct>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"x\", xRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<MyStruct>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashx = x.GetHashCode();\n        return ___hashx;\n    }\n\n#nullable enable\n    public bool Equals(MyStruct that)\n    {\n        var ___eqx = this.x.Equals(that.x);\n        return ___eqx;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as MyStruct?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(MyStruct this_, MyStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(MyStruct this_, MyStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // MyStruct\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type#TestTaggedEnumField.verified.cs",
    "content": "﻿//HintName: TestTaggedEnumField.cs\n// <auto-generated />\n#nullable enable\n\npartial record TestTaggedEnumField : System.IEquatable<TestTaggedEnumField>\n{\n    public sealed record X(int X_) : TestTaggedEnumField\n    {\n        public override string ToString() =>\n            $\"X({SpacetimeDB.BSATN.StringUtil.GenericToString(X_)})\";\n    }\n\n    public sealed record Y(int Y_) : TestTaggedEnumField\n    {\n        public override string ToString() =>\n            $\"Y({SpacetimeDB.BSATN.StringUtil.GenericToString(Y_)})\";\n    }\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestTaggedEnumField>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 XRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 YRW = new();\n\n        public TestTaggedEnumField Read(System.IO.BinaryReader reader)\n        {\n            return reader.ReadByte() switch\n            {\n                0 => new X(XRW.Read(reader)),\n                1 => new Y(YRW.Read(reader)),\n                _\n                    => throw new System.InvalidOperationException(\n                        \"Invalid tag value, this state should be unreachable.\"\n                    )\n            };\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestTaggedEnumField value)\n        {\n            switch (value)\n            {\n                case X(var inner):\n                    writer.Write((byte)0);\n                    XRW.Write(writer, inner);\n                    break;\n                case Y(var inner):\n                    writer.Write((byte)1);\n                    YRW.Write(writer, inner);\n                    break;\n            }\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestTaggedEnumField>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Sum(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"X\", XRW.GetAlgebraicType(registrar)),\n                        new(\"Y\", YRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestTaggedEnumField>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        switch (this)\n        {\n            case X(var inner):\n                var ___hashX = inner.GetHashCode();\n                return ___hashX;\n            case Y(var inner):\n                var ___hashY = inner.GetHashCode();\n                return ___hashY;\n            default:\n                return 0;\n        }\n    }\n} // TestTaggedEnumField\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type#TestTaggedEnumInlineTuple.verified.cs",
    "content": "﻿//HintName: TestTaggedEnumInlineTuple.cs\n// <auto-generated />\n#nullable enable\n\npartial record TestTaggedEnumInlineTuple : System.IEquatable<TestTaggedEnumInlineTuple>\n{\n    public sealed record Item1(int Item1_) : TestTaggedEnumInlineTuple\n    {\n        public override string ToString() =>\n            $\"Item1({SpacetimeDB.BSATN.StringUtil.GenericToString(Item1_)})\";\n    }\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestTaggedEnumInlineTuple>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 Item1RW = new();\n\n        public TestTaggedEnumInlineTuple Read(System.IO.BinaryReader reader)\n        {\n            return reader.ReadByte() switch\n            {\n                0 => new Item1(Item1RW.Read(reader)),\n                _\n                    => throw new System.InvalidOperationException(\n                        \"Invalid tag value, this state should be unreachable.\"\n                    )\n            };\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestTaggedEnumInlineTuple value)\n        {\n            switch (value)\n            {\n                case Item1(var inner):\n                    writer.Write((byte)0);\n                    Item1RW.Write(writer, inner);\n                    break;\n            }\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestTaggedEnumInlineTuple>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Sum(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"Item1\", Item1RW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestTaggedEnumInlineTuple>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        switch (this)\n        {\n            case Item1(var inner):\n                var ___hashItem1 = inner.GetHashCode();\n                return ___hashItem1;\n            default:\n                return 0;\n        }\n    }\n} // TestTaggedEnumInlineTuple\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type#TestTypeParams_T_.verified.cs",
    "content": "﻿//HintName: TestTypeParams_T_.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestTypeParams<T>\n    : System.IEquatable<TestTypeParams>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Field = BSATN.FieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.FieldRW.Write(writer, Field);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestTypeParams {{ Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(Field)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestTypeParams<T>>\n    {\n        internal static readonly TRW FieldRW = new();\n\n        public TestTypeParams<T> Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestTypeParams<T>();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestTypeParams<T> value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestTypeParams<T>>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"Field\", FieldRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<\n            TestTypeParams<T>\n        >.GetAlgebraicType(SpacetimeDB.BSATN.ITypeRegistrar registrar) =>\n            GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashField = Field == null ? 0 : Field.GetHashCode();\n        return ___hashField;\n    }\n\n#nullable enable\n    public bool Equals(TestTypeParams<T> that)\n    {\n        var ___eqField = this.Field == null ? that.Field == null : this.Field.Equals(that.Field);\n        return ___eqField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestTypeParams<T>?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestTypeParams<T> this_, TestTypeParams<T> that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestTypeParams<T> this_, TestTypeParams<T> that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestTypeParams<T>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type#TestUnsupportedType.verified.cs",
    "content": "﻿//HintName: TestUnsupportedType.cs\n// <auto-generated />\n#nullable enable\n\npartial struct TestUnsupportedType\n    : System.IEquatable<TestUnsupportedType>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        UnsupportedSpecialType = BSATN.UnsupportedSpecialTypeRW.Read(reader);\n        UnsupportedSystemType = BSATN.UnsupportedSystemTypeRW.Read(reader);\n        UnresolvedType = BSATN.UnresolvedTypeRW.Read(reader);\n        UnsupportedEnum = BSATN.UnsupportedEnumRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.UnsupportedSpecialTypeRW.Write(writer, UnsupportedSpecialType);\n        BSATN.UnsupportedSystemTypeRW.Write(writer, UnsupportedSystemType);\n        BSATN.UnresolvedTypeRW.Write(writer, UnresolvedType);\n        BSATN.UnsupportedEnumRW.Write(writer, UnsupportedEnum);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"TestUnsupportedType {{ UnsupportedSpecialType = {SpacetimeDB.BSATN.StringUtil.GenericToString(UnsupportedSpecialType)}, UnsupportedSystemType = {SpacetimeDB.BSATN.StringUtil.GenericToString(UnsupportedSystemType)}, UnresolvedType = {SpacetimeDB.BSATN.StringUtil.GenericToString(UnresolvedType)}, UnsupportedEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(UnsupportedEnum)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<TestUnsupportedType>\n    {\n        internal static readonly SpacetimeDB.BSATN.Unsupported<System.DateTime> UnsupportedSpecialTypeRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.Unsupported<System.Exception> UnsupportedSystemTypeRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.Unsupported<object> UnresolvedTypeRW = new();\n        internal static readonly SpacetimeDB.BSATN.Unsupported<LocalEnum> UnsupportedEnumRW = new();\n\n        public TestUnsupportedType Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new TestUnsupportedType();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, TestUnsupportedType value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<TestUnsupportedType>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\n                            \"UnsupportedSpecialType\",\n                            UnsupportedSpecialTypeRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\n                            \"UnsupportedSystemType\",\n                            UnsupportedSystemTypeRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\"UnresolvedType\", UnresolvedTypeRW.GetAlgebraicType(registrar)),\n                        new(\"UnsupportedEnum\", UnsupportedEnumRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<TestUnsupportedType>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashUnsupportedSpecialType = UnsupportedSpecialType.GetHashCode();\n        var ___hashUnsupportedSystemType =\n            UnsupportedSystemType == null ? 0 : UnsupportedSystemType.GetHashCode();\n        var ___hashUnresolvedType = UnresolvedType == null ? 0 : UnresolvedType.GetHashCode();\n        var ___hashUnsupportedEnum = UnsupportedEnum.GetHashCode();\n        return ___hashUnsupportedSpecialType\n            ^ ___hashUnsupportedSystemType\n            ^ ___hashUnresolvedType\n            ^ ___hashUnsupportedEnum;\n    }\n\n#nullable enable\n    public bool Equals(TestUnsupportedType that)\n    {\n        var ___eqUnsupportedSpecialType = this.UnsupportedSpecialType.Equals(\n            that.UnsupportedSpecialType\n        );\n        var ___eqUnsupportedSystemType =\n            this.UnsupportedSystemType == null\n                ? that.UnsupportedSystemType == null\n                : this.UnsupportedSystemType.Equals(that.UnsupportedSystemType);\n        var ___eqUnresolvedType =\n            this.UnresolvedType == null\n                ? that.UnresolvedType == null\n                : this.UnresolvedType.Equals(that.UnresolvedType);\n        var ___eqUnsupportedEnum = this.UnsupportedEnum == that.UnsupportedEnum;\n        return ___eqUnsupportedSpecialType\n            && ___eqUnsupportedSystemType\n            && ___eqUnresolvedType\n            && ___eqUnsupportedEnum;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as TestUnsupportedType?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(TestUnsupportedType this_, TestUnsupportedType that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(TestUnsupportedType this_, TestUnsupportedType that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // TestUnsupportedType\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Type.verified.txt",
    "content": "﻿{\n  Diagnostics: [\n    {/*\n{\n    EnumVariant1 = 1,\n                 ^^^\n    EnumVariant2 = 2,\n*/\n      Message: TestEnumWithExplicitValues.EnumVariant1 has an explicit value 1 which is not allowed in SpacetimeDB enums.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0002,\n        Title: [SpacetimeDB.Type] enums cannot have explicit values,\n        MessageFormat: {0}.{1} has an explicit value {2} which is not allowed in SpacetimeDB enums.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    EnumVariant1 = 1,\n    EnumVariant2 = 2,\n                 ^^^\n}\n*/\n      Message: TestEnumWithExplicitValues.EnumVariant2 has an explicit value 2 which is not allowed in SpacetimeDB enums.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0002,\n        Title: [SpacetimeDB.Type] enums cannot have explicit values,\n        MessageFormat: {0}.{1} has an explicit value {2} which is not allowed in SpacetimeDB enums.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    EnumVariant256,\n    EnumVariant257,\n    ^^^^^^^^^^^^^^\n}\n*/\n      Message: TestEnumWithTooManyVariants has 257 variants which is more than the allowed 256 variants for SpacetimeDB enums.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0003,\n        Title: [SpacetimeDB.Type] enums are limited to 256 variants,\n        MessageFormat: {0} has {1} variants which is more than the allowed 256 variants for SpacetimeDB enums.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n{\n    public DateTime UnsupportedSpecialType;\n                    ^^^^^^^^^^^^^^^^^^^^^^\n    public Exception UnsupportedSystemType;\n*/\n      Message: BSATN implementation for System.DateTime is not found: Unsupported special type System.DateTime (System_DateTime),\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0001,\n        Title: Unsupported type,\n        MessageFormat: BSATN implementation for {0} is not found: {1},\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    public DateTime UnsupportedSpecialType;\n    public Exception UnsupportedSystemType;\n                     ^^^^^^^^^^^^^^^^^^^^^\n    public UnresolvedType UnresolvedType;\n*/\n      Message: BSATN implementation for System.Exception is not found: Unsupported system type System.Exception,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0001,\n        Title: Unsupported type,\n        MessageFormat: BSATN implementation for {0} is not found: {1},\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n    public UnresolvedType UnresolvedType;\n    public LocalEnum UnsupportedEnum;\n                     ^^^^^^^^^^^^^^^\n}\n*/\n      Message: BSATN implementation for LocalEnum is not found: Enum LocalEnum does not have a [SpacetimeDB.Type] attribute,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0001,\n        Title: Unsupported type,\n        MessageFormat: BSATN implementation for {0} is not found: {1},\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n{\n    public int ForbiddenTaggedEnumField;\n               ^^^^^^^^^^^^^^^^^^^^^^^^\n}\n*/\n      Message: ForbiddenTaggedEnumField is an instance field, which are not permitted inside SpacetimeDB tagged enums.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0005,\n        Title: Tagged enums cannot have instance fields,\n        MessageFormat: {0} is an instance field, which are not permitted inside SpacetimeDB tagged enums.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n{\n    public int ForbiddenField;\n               ^^^^^^^^^^^^^^\n}\n*/\n      Message: ForbiddenField is an instance field, which are not permitted inside SpacetimeDB tagged enums.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0005,\n        Title: Tagged enums cannot have instance fields,\n        MessageFormat: {0} is an instance field, which are not permitted inside SpacetimeDB tagged enums.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    },\n    {/*\n[SpacetimeDB.Type]\npublic partial struct TestTypeParams<T>\n                                    ^^^\n{\n*/\n      Message: Type parameters <T> are not supported in SpacetimeDB types.,\n      Severity: Error,\n      Descriptor: {\n        Id: BSATN0006,\n        Title: Type parameters are not yet supported,\n        MessageFormat: Type parameters {0} are not supported in SpacetimeDB types.,\n        Category: SpacetimeDB.BSATN,\n        DefaultSeverity: Error,\n        IsEnabledByDefault: true\n      }\n    }\n  ]\n}"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/Lib.cs",
    "content": "using SpacetimeDB;\n\n#pragma warning disable CA1050 // Declare types in namespaces - this is a test fixture, no need for a namespace.\n\npublic static partial class Module\n{\n    [SpacetimeDB.Settings]\n    public const CaseConversionPolicy CASE_CONVERSION_POLICY = CaseConversionPolicy.SnakeCase;\n}\n\n[SpacetimeDB.Type]\npublic partial struct DemoType\n{\n    public int A;\n}\n\n[SpacetimeDB.Table(Accessor = \"DemoTable\", Name = \"canonical_table\", Public = true)]\npublic partial struct DemoTable\n{\n    [SpacetimeDB.PrimaryKey]\n    [SpacetimeDB.Index.BTree(Accessor = \"ById\", Name = \"canonical_index\")]\n    public int Id;\n\n    public int Value;\n}\n\npublic static partial class Reducers\n{\n    [SpacetimeDB.Reducer(Name = \"canonical_reducer\")]\n    public static void DemoReducer(ReducerContext ctx, int value)\n    {\n        ctx.Db.DemoTable.Insert(new DemoTable { Id = value, Value = value });\n    }\n\n    [SpacetimeDB.Procedure(Name = \"canonical_procedure\")]\n    public static void DemoProcedure(ProcedureContext ctx) { }\n\n    [SpacetimeDB.View(Accessor = \"demo_view\", Name = \"canonical_view\", Public = true)]\n    public static List<DemoTable> DemoView(ViewContext ctx)\n    {\n        return new List<DemoTable>();\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/explicitnames.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../../BSATN.Runtime/BSATN.Runtime.csproj\" />\n    <ProjectReference Include=\"../../../Runtime/Runtime.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#DemoTable.verified.cs",
    "content": "﻿//HintName: DemoTable.cs\n// <auto-generated />\n#nullable enable\n\npartial struct DemoTable : System.IEquatable<DemoTable>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Id = BSATN.IdRW.Read(reader);\n        Value = BSATN.ValueRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdRW.Write(writer, Id);\n        BSATN.ValueRW.Write(writer, Value);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"DemoTable {{ Id = {SpacetimeDB.BSATN.StringUtil.GenericToString(Id)}, Value = {SpacetimeDB.BSATN.StringUtil.GenericToString(Value)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<DemoTable>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IdRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 ValueRW = new();\n\n        public DemoTable Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new DemoTable();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, DemoTable value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<DemoTable>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Id\", IdRW.GetAlgebraicType(registrar)),\n                    new(\"Value\", ValueRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<DemoTable>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashId = Id.GetHashCode();\n        var ___hashValue = Value.GetHashCode();\n        return ___hashId ^ ___hashValue;\n    }\n\n#nullable enable\n    public bool Equals(DemoTable that)\n    {\n        var ___eqId = this.Id.Equals(that.Id);\n        var ___eqValue = this.Value.Equals(that.Value);\n        return ___eqId && ___eqValue;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as DemoTable?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(DemoTable this_, DemoTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(DemoTable this_, DemoTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // DemoTable\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#FFI.verified.cs",
    "content": "﻿//HintName: FFI.cs\n// <auto-generated />\n#nullable enable\n// The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\\Internal\\Module.cs as an empty partial type.\n// This is needed so every module build doesn't generate a full LocalReadOnly type, but just adds on to the existing.\n// We extend it here with generated table accessors, and just need to suppress the duplicate-type warning.\n#pragma warning disable CS0436\n#pragma warning disable STDB_UNSTABLE\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing Internal = SpacetimeDB.Internal;\nusing TxContext = SpacetimeDB.Internal.TxContext;\n\nnamespace SpacetimeDB\n{\n    public readonly struct DemoTableCols\n    {\n        public readonly global::SpacetimeDB.Col<global::DemoTable, int> Id;\n        public readonly global::SpacetimeDB.Col<global::DemoTable, int> Value;\n\n        internal DemoTableCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.Col<global::DemoTable, int>(tableName, \"Id\");\n            Value = new global::SpacetimeDB.Col<global::DemoTable, int>(tableName, \"Value\");\n        }\n    }\n\n    public readonly struct DemoTableIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::DemoTable, int> Id;\n\n        internal DemoTableIxCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.IxCol<global::DemoTable, int>(tableName, \"Id\");\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::DemoTable,\n            DemoTableCols,\n            DemoTableIxCols\n        > DemoTable() =>\n            new(\"DemoTable\", new DemoTableCols(\"DemoTable\"), new DemoTableIxCols(\"DemoTable\"));\n    }\n\n    public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext\n    {\n        public readonly Identity Sender;\n        public readonly ConnectionId? ConnectionId;\n        public readonly Random Rng;\n        public readonly Timestamp Timestamp;\n        public readonly AuthCtx SenderAuth;\n\n        // **Note:** must be 0..=u32::MAX\n        internal int CounterUuid;\n\n        // We need this property to be non-static for parity with client SDK.\n        public Identity Identity => Internal.IReducerContext.GetIdentity();\n\n        internal ReducerContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time,\n            AuthCtx? senderAuth = null\n        )\n        {\n            Sender = identity;\n            ConnectionId = connectionId;\n            Rng = random;\n            Timestamp = time;\n            SenderAuth = senderAuth ?? AuthCtx.BuildFromSystemTables(connectionId, identity);\n            CounterUuid = 0;\n        }\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if <see cref=\"Uuid\"/> generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Reducer]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    public sealed partial class ProcedureContext : global::SpacetimeDB.ProcedureContextBase\n    {\n        private readonly Local _db = new();\n\n        internal ProcedureContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time\n        )\n            : base(identity, connectionId, random, time) { }\n\n        protected override global::SpacetimeDB.LocalBase CreateLocal() => _db;\n\n        protected override global::SpacetimeDB.ProcedureTxContextBase CreateTxContext(\n            Internal.TxContext inner\n        ) => _cached ??= new ProcedureTxContext(inner);\n\n        private ProcedureTxContext? _cached;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public Local Db => _db;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TResult WithTx<TResult>(Func<ProcedureTxContext, TResult> body) =>\n            base.WithTx(tx => body((ProcedureTxContext)tx));\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TxOutcome<TResult> TryWithTx<TResult, TError>(\n            Func<ProcedureTxContext, Result<TResult, TError>> body\n        )\n            where TError : Exception => base.TryWithTx(tx => body((ProcedureTxContext)tx));\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if UUID generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Procedure]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    [Experimental(\"STDB_UNSTABLE\")]\n    public sealed class ProcedureTxContext : global::SpacetimeDB.ProcedureTxContextBase\n    {\n        internal ProcedureTxContext(Internal.TxContext inner)\n            : base(inner) { }\n\n        public new Local Db => (Local)base.Db;\n    }\n\n    public sealed class Local : global::SpacetimeDB.LocalBase\n    {\n        public global::SpacetimeDB.Internal.TableHandles.DemoTable DemoTable => new();\n    }\n\n    public sealed record ViewContext : DbContext<Internal.LocalReadOnly>, Internal.IViewContext\n    {\n        public Identity Sender { get; }\n\n        public QueryBuilder From => default;\n\n        internal ViewContext(Identity sender, Internal.LocalReadOnly db)\n            : base(db)\n        {\n            Sender = sender;\n        }\n    }\n\n    public sealed record AnonymousViewContext\n        : DbContext<Internal.LocalReadOnly>,\n            Internal.IAnonymousViewContext\n    {\n        public QueryBuilder From => default;\n\n        internal AnonymousViewContext(Internal.LocalReadOnly db)\n            : base(db) { }\n    }\n}\n\nnamespace SpacetimeDB.Internal.TableHandles\n{\n    public readonly struct DemoTable\n        : global::SpacetimeDB.Internal.ITableView<DemoTable, global::DemoTable>\n    {\n        public static global::DemoTable ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::DemoTable row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(DemoTable),\n                ProductTypeRef: (uint)\n                    new global::DemoTable.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [0],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"DemoTable_Id_idx_btree\",\n                        AccessorName: \"Id\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    ),\n                    new(\n                        SourceName: \"DemoTable_Id_idx_btree\",\n                        AccessorName: \"ById\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        DemoTable,\n                        global::DemoTable\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Public,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<DemoTable, global::DemoTable>.DoCount();\n\n        public IEnumerable<global::DemoTable> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<DemoTable, global::DemoTable>.DoIter();\n\n        public global::DemoTable Insert(global::DemoTable row) =>\n            global::SpacetimeDB.Internal.ITableView<DemoTable, global::DemoTable>.DoInsert(row);\n\n        public bool Delete(global::DemoTable row) =>\n            global::SpacetimeDB.Internal.ITableView<DemoTable, global::DemoTable>.DoDelete(row);\n\n        public sealed class IdUniqueIndex\n            : UniqueIndex<DemoTable, global::DemoTable, int, SpacetimeDB.BSATN.I32>\n        {\n            internal IdUniqueIndex()\n                : base(\"DemoTable_Id_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::DemoTable? Find(int key) => FindSingle(key);\n\n            public global::DemoTable Update(global::DemoTable row) => DoUpdate(row);\n        }\n\n        public IdUniqueIndex Id => new();\n\n        public sealed class ByIdIndex()\n            : SpacetimeDB.Internal.IndexBase<global::DemoTable>(\"DemoTable_Id_idx_btree\")\n        {\n            public IEnumerable<global::DemoTable> Filter(int Id) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(Id));\n\n            public ulong Delete(int Id) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(Id));\n\n            public IEnumerable<global::DemoTable> Filter(global::SpacetimeDB.Bound<int> Id) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(Id));\n\n            public ulong Delete(global::SpacetimeDB.Bound<int> Id) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(Id));\n        }\n\n        public ByIdIndex ById => new();\n    }\n}\n\nsealed class demo_viewViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"demo_view\",\n            Index: 0,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.List<DemoTable, DemoTable.BSATN>().GetAlgebraicType(\n                registrar\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Reducers.DemoView((SpacetimeDB.ViewContext)ctx);\n            SpacetimeDB.BSATN.List<DemoTable, DemoTable.BSATN> returnRW = new();\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            returnRW.Write(writer, returnValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'demo_view': \" + e);\n            throw;\n        }\n    }\n}\n\nnamespace SpacetimeDB.Internal.ViewHandles\n{\n    public sealed class DemoTableReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::DemoTable>\n    {\n        internal DemoTableReadOnly()\n            : base(\"DemoTable\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.DemoTableReadOnly,\n                global::DemoTable,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdIndex()\n                : base(\"DemoTable_Id_idx_btree\") { }\n\n            public global::DemoTable? Find(int key) => FindSingle(key);\n        }\n\n        public IdIndex Id => new();\n\n        public sealed class ByIdIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::DemoTable>\n        {\n            internal ByIdIndex()\n                : base(\"DemoTable_Id_idx_btree\") { }\n\n            public IEnumerable<global::DemoTable> Filter(int Id) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        Id\n                    )\n                );\n\n            public IEnumerable<global::DemoTable> Filter(global::SpacetimeDB.Bound<int> Id) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<int, SpacetimeDB.BSATN.I32>(\n                        Id\n                    )\n                );\n        }\n\n        public ByIdIndex ById => new();\n    }\n}\n\nnamespace SpacetimeDB.Internal\n{\n    public sealed partial class LocalReadOnly\n    {\n        public global::SpacetimeDB.Internal.ViewHandles.DemoTableReadOnly DemoTable => new();\n    }\n}\n\nstatic class ModuleRegistration\n{\n    class DemoReducer : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly SpacetimeDB.BSATN.I32 valueRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(DemoReducer),\n                Params: [new(\"value\", valueRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.DemoReducer((SpacetimeDB.ReducerContext)ctx, valueRW.Read(reader));\n        }\n    }\n\n    class DemoProcedure : SpacetimeDB.Internal.IProcedure\n    {\n        public SpacetimeDB.Internal.RawProcedureDefV10 MakeProcedureDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(DemoProcedure),\n                Params: [],\n                ReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable\n            );\n\n        public byte[] Invoke(BinaryReader reader, SpacetimeDB.Internal.IProcedureContext ctx)\n        {\n            Reducers.DemoProcedure((SpacetimeDB.ProcedureContext)ctx);\n            return System.Array.Empty<byte>();\n        }\n    }\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : struct => value is null ? new List<T>() : new List<T> { value.Value };\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : class => value is null ? new List<T>() : new List<T> { value };\n\n#if EXPERIMENTAL_WASM_AOT\n    // In AOT mode we're building a library.\n    // Main method won't be called automatically, so we need to export it as a preinit function.\n    [UnmanagedCallersOnly(EntryPoint = \"__preinit__10_init_csharp\")]\n#else\n    // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer.\n    [DynamicDependency(\n        DynamicallyAccessedMemberTypes.PublicMethods,\n        typeof(SpacetimeDB.Internal.Module)\n    )]\n#endif\n    public static void Main()\n    {\n        SpacetimeDB.Internal.Module.SetReducerContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ReducerContext(identity, connectionId, random, time)\n        );\n        SpacetimeDB.Internal.Module.SetViewContextConstructor(\n            identity => new SpacetimeDB.ViewContext(\n                identity,\n                new SpacetimeDB.Internal.LocalReadOnly()\n            )\n        );\n        SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor(\n            () => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly())\n        );\n        SpacetimeDB.Internal.Module.SetProcedureContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ProcedureContext(identity, connectionId, random, time)\n        );\n        SpacetimeDB.Internal.Module.SetCaseConversionPolicy(\n            SpacetimeDB.CaseConversionPolicy.SnakeCase\n        );\n        SpacetimeDB.Internal.Module.RegisterExplicitTableName(\"DemoTable\", \"canonical_table\");\n        SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\n            \"DemoReducer\",\n            \"canonical_reducer\"\n        );\n        SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\n            \"DemoProcedure\",\n            \"canonical_procedure\"\n        );\n        SpacetimeDB.Internal.Module.RegisterExplicitFunctionName(\"demo_view\", \"canonical_view\");\n        SpacetimeDB.Internal.Module.RegisterExplicitIndexName(\n            \"DemoTable_Id_idx_btree\",\n            \"canonical_index\"\n        );\n\n        var __memoryStream = new MemoryStream();\n        var __writer = new BinaryWriter(__memoryStream);\n\n        SpacetimeDB.Internal.Module.RegisterReducer<DemoReducer>();\n        SpacetimeDB.Internal.Module.RegisterProcedure<DemoProcedure>();\n\n        // IMPORTANT: The order in which we register views matters.\n        // It must correspond to the order in which we call `GenerateDispatcherClass`.\n        // See the comment on `GenerateDispatcherClass` for more explanation.\n        SpacetimeDB.Internal.Module.RegisterView<demo_viewViewDispatcher>();\n\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::DemoTable,\n            SpacetimeDB.Internal.TableHandles.DemoTable\n        >();\n    }\n\n    // Exports only work from the main assembly, so we need to generate forwarding methods.\n#if EXPERIMENTAL_WASM_AOT\n    [UnmanagedCallersOnly(EntryPoint = \"__describe_module__\")]\n    public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) =>\n        SpacetimeDB.Internal.Module.__describe_module__(d);\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_reducer__\")]\n    public static SpacetimeDB.Internal.Errno __call_reducer__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink error\n    ) =>\n        SpacetimeDB.Internal.Module.__call_reducer__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            error\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_procedure__\")]\n    public static SpacetimeDB.Internal.Errno __call_procedure__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink result_sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_procedure__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            result_sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view__\")]\n    public static SpacetimeDB.Internal.Errno __call_view__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_view__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            args,\n            sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view_anon__\")]\n    public static SpacetimeDB.Internal.Errno __call_view_anon__(\n        uint id,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink);\n#endif\n}\n\n#pragma warning restore STDB_UNSTABLE\n#pragma warning restore CS0436\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#Reducers.DemoProcedure.verified.cs",
    "content": "﻿//HintName: Reducers.DemoProcedure.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateDemoProcedure()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.ProcedureExtensions.VolatileNonatomicScheduleImmediate(\n            nameof(DemoProcedure),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Module#Reducers.DemoReducer.verified.cs",
    "content": "﻿//HintName: Reducers.DemoReducer.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateDemoReducer(int value)\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new SpacetimeDB.BSATN.I32().Write(writer, value);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(DemoReducer),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/explicitnames/snapshots/Type#DemoType.verified.cs",
    "content": "﻿//HintName: DemoType.cs\n// <auto-generated />\n#nullable enable\n\npartial struct DemoType : System.IEquatable<DemoType>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        A = BSATN.ARW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.ARW.Write(writer, A);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"DemoType {{ A = {SpacetimeDB.BSATN.StringUtil.GenericToString(A)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<DemoType>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 ARW = new();\n\n        public DemoType Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new DemoType();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, DemoType value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<DemoType>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"A\", ARW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<DemoType>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashA = A.GetHashCode();\n        return ___hashA;\n    }\n\n#nullable enable\n    public bool Equals(DemoType that)\n    {\n        var ___eqA = this.A.Equals(that.A);\n        return ___eqA;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as DemoType?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(DemoType this_, DemoType that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(DemoType this_, DemoType that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // DemoType\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/Lib.cs",
    "content": "using System.Runtime.InteropServices;\nusing SpacetimeDB;\n\n#pragma warning disable CA1050 // Declare types in namespaces - this is a test fixture, no need for a namespace.\n#pragma warning disable STDB_UNSTABLE // Enable experimental SpacetimeDB features\n\n[SpacetimeDB.Type]\npublic partial struct CustomStruct\n{\n    public const int IGNORE_ME = 0;\n    public static readonly string IGNORE_ME_TOO = \"\";\n    public int IntField;\n    public string StringField;\n    public int? NullableIntField;\n    public string? NullableStringField;\n}\n\n[SpacetimeDB.Type]\npublic partial class CustomClass\n{\n    public const int IGNORE_ME = 0;\n    public static readonly string IGNORE_ME_TOO = \"\";\n    public int IntField = 0;\n    public string StringField = \"\";\n    public int? NullableIntField;\n    public string? NullableStringField;\n}\n\n[SpacetimeDB.Type]\npublic partial class CustomRecord\n{\n    public const int IGNORE_ME = 0;\n    public static readonly string IGNORE_ME_TOO = \"\";\n    public int IntField = 0;\n    public string StringField = \"\";\n    public int? NullableIntField;\n    public string? NullableStringField;\n}\n\n[StructLayout(LayoutKind.Auto)]\npublic partial class CustomClass\n{\n    public int IgnoreExtraFields;\n}\n\n[SpacetimeDB.Type]\npublic partial class CustomNestedClass\n{\n    public CustomClass NestedClass = new();\n    public CustomClass? NestedNullableClass = null;\n    public CustomEnum NestedEnum = CustomEnum.EnumVariant1;\n    public CustomEnum? NestedNullableEnum = null;\n    public CustomTaggedEnum NestedTaggedEnum = new CustomTaggedEnum.NullableIntVariant(null);\n    public CustomTaggedEnum? NestedNullableTaggedEnum = null;\n    public CustomRecord NestedCustomRecord = new();\n    public CustomRecord? NestedNullableCustomRecord = null;\n}\n\n[SpacetimeDB.Type]\npublic partial class ContainsNestedLists\n{\n    public List<int> IntList = [];\n    public List<string> StringList = [];\n    public int[] IntArray = [];\n    public string[] StringArray = [];\n    public List<int[][]> IntArrayArrayList = [];\n    public List<List<int>>[] IntListListArray = [];\n    public List<string[][]> StringArrayArrayList = [];\n    public List<List<string>>[] StringListListArray = [];\n}\n\n[SpacetimeDB.Type]\npublic partial class EmptyClass { }\n\n[SpacetimeDB.Type]\npublic partial struct EmptyStruct { }\n\n[SpacetimeDB.Type]\npublic partial record EmptyRecord { }\n\n[SpacetimeDB.Type]\npublic enum CustomEnum\n{\n    EnumVariant1,\n    EnumVariant2,\n}\n\n[SpacetimeDB.Type]\npublic partial record CustomTaggedEnum\n    : SpacetimeDB.TaggedEnum<(\n        int IntVariant,\n        string StringVariant,\n        int? NullableIntVariant,\n        string? NullableStringVariant\n    )>;\n\n[SpacetimeDB.Table(Event = true)]\npublic partial class PrivateTable { }\n\n[SpacetimeDB.Table(Public = true)]\npublic partial struct PublicTable\n{\n    [SpacetimeDB.AutoInc]\n    [SpacetimeDB.PrimaryKey]\n    public int Id;\n\n    public byte ByteField;\n    public ushort UshortField;\n    public uint UintField;\n    public ulong UlongField;\n    public UInt128 UInt128Field;\n    public U128 U128Field;\n    public U256 U256Field;\n    public sbyte SbyteField;\n    public short ShortField;\n    public int IntField;\n    public long LongField;\n    public Int128 Int128Field;\n    public I128 I128Field;\n    public I256 I256Field;\n    public bool BoolField;\n    public float FloatField;\n    public double DoubleField;\n    public string StringField;\n    public Identity IdentityField;\n    public ConnectionId ConnectionIdField;\n    public CustomStruct CustomStructField;\n    public CustomClass CustomClassField;\n    public CustomEnum CustomEnumField;\n    public CustomTaggedEnum CustomTaggedEnumField;\n    public List<int> ListField;\n    public int? NullableValueField;\n    public string? NullableReferenceField;\n}\n\npublic static partial class Reducers\n{\n    [SpacetimeDB.Reducer]\n    public static void InsertData(ReducerContext ctx, PublicTable data)\n    {\n        ctx.Db.PublicTable.Insert(data);\n        Log.Info(\"New list\");\n        foreach (var item in ctx.Db.PublicTable.Iter())\n        {\n            Log.Info($\"Item: {item.StringField}\");\n        }\n    }\n\n    [SpacetimeDB.Reducer]\n    public static void ScheduleImmediate(ReducerContext ctx, PublicTable data)\n    {\n        VolatileNonatomicScheduleImmediateInsertData(data);\n    }\n}\n\nnamespace Test\n{\n    namespace NestingNamespaces\n    {\n        public static partial class AndClasses\n        {\n            [SpacetimeDB.Reducer]\n            public static void InsertData2(ReducerContext ctx, PublicTable data)\n            {\n                ctx.Db.PublicTable.Insert(data);\n            }\n        }\n    }\n}\n\npublic static partial class Timers\n{\n    [SpacetimeDB.Table(Scheduled = nameof(SendScheduledMessage))]\n    public partial struct SendMessageTimer\n    {\n        [PrimaryKey]\n        [AutoInc]\n        public ulong ScheduledId;\n        public ScheduleAt ScheduledAt;\n        public string Text;\n    }\n\n    [SpacetimeDB.Reducer]\n    public static void SendScheduledMessage(ReducerContext ctx, SendMessageTimer arg)\n    {\n        // verify that fields were auto-added\n        ulong id = arg.ScheduledId;\n        SpacetimeDB.ScheduleAt scheduleAt = arg.ScheduledAt;\n        string text = arg.Text;\n    }\n\n    [SpacetimeDB.Reducer(ReducerKind.Init)]\n    public static void Init(ReducerContext ctx)\n    {\n        ctx.Db.SendMessageTimer.Insert(\n            new SendMessageTimer\n            {\n                Text = \"bot sending a message\",\n                ScheduledAt = ctx.Timestamp + new TimeDuration(10_000_000),\n            }\n        );\n    }\n}\n\n[SpacetimeDB.Table(Accessor = \"MultiTable1\", Public = true)]\n[SpacetimeDB.Table(Accessor = \"MultiTable2\")]\npublic partial struct MultiTableRow\n{\n    [SpacetimeDB.Index.BTree(Table = \"MultiTable1\")]\n    public string Name;\n\n    [SpacetimeDB.AutoInc]\n    [SpacetimeDB.PrimaryKey(Table = \"MultiTable1\")]\n    public uint Foo;\n\n    [SpacetimeDB.Unique(Table = \"MultiTable2\")]\n    public uint Bar;\n\n    [SpacetimeDB.Reducer]\n    public static void InsertMultiData(ReducerContext ctx, MultiTableRow data)\n    {\n        // Verify that we have both tables generated on the context.\n        ctx.Db.MultiTable1.Insert(data);\n        ctx.Db.MultiTable2.Insert(data);\n    }\n}\n\n[SpacetimeDB.Table]\n[SpacetimeDB.Index.BTree(Accessor = \"Location\", Columns = [\"X\", \"Y\", \"Z\"])]\npartial struct BTreeMultiColumn\n{\n    public uint X;\n    public uint Y;\n    public uint Z;\n}\n\n[SpacetimeDB.Table]\n[SpacetimeDB.Index.BTree(Accessor = \"Location\", Columns = [\"X\", \"Y\"])]\npartial struct BTreeViews\n{\n    [SpacetimeDB.PrimaryKey]\n    public Identity Id;\n\n    public uint X;\n    public uint Y;\n\n    [SpacetimeDB.Index.BTree]\n    public string Faction;\n}\n\n[SpacetimeDB.Table]\npartial struct RegressionMultipleUniqueIndexesHadSameName\n{\n    [SpacetimeDB.Unique]\n    public uint Unique1;\n\n    [SpacetimeDB.Unique]\n    public uint Unique2;\n}\n\n/// <summary>\n/// These used to cause conflicts when generating the BSATN struct for a type.\n/// </summary>\n[SpacetimeDB.Type]\npartial struct FormerlyForbiddenFieldNames\n{\n    public uint Read;\n    public uint Write;\n    public uint GetAlgebraicType;\n}\n\npublic class Module\n{\n    [SpacetimeDB.ClientVisibilityFilter]\n    public static readonly Filter ALL_PUBLIC_TABLES = new Filter.Sql(\"SELECT * FROM PublicTable\");\n\n    [SpacetimeDB.View(Accessor = \"public_table_view\", Public = true)]\n    public static PublicTable? PublicTableByIdentity(ViewContext ctx)\n    {\n        return (PublicTable?)ctx.Db.PublicTable.Id.Find(0);\n    }\n\n    [SpacetimeDB.View(Accessor = \"public_table_query\", Public = true)]\n    public static IQuery<PublicTable> PublicTableQuery(ViewContext ctx)\n    {\n        return ctx.From.PublicTable().Where(cols => cols.Id.Eq(0));\n    }\n\n    [SpacetimeDB.View(Accessor = \"find_public_table__by_identity\", Public = true)]\n    public static PublicTable? FindPublicTableByIdentity(AnonymousViewContext ctx)\n    {\n        return (PublicTable?)ctx.Db.PublicTable.Id.Find(0);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/server.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../../BSATN.Runtime/BSATN.Runtime.csproj\" />\n    <ProjectReference Include=\"../../../Runtime/Runtime.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#BTreeMultiColumn.verified.cs",
    "content": "﻿//HintName: BTreeMultiColumn.cs\n// <auto-generated />\n#nullable enable\n\npartial struct BTreeMultiColumn\n    : System.IEquatable<BTreeMultiColumn>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        X = BSATN.XRW.Read(reader);\n        Y = BSATN.YRW.Read(reader);\n        Z = BSATN.ZRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.XRW.Write(writer, X);\n        BSATN.YRW.Write(writer, Y);\n        BSATN.ZRW.Write(writer, Z);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"BTreeMultiColumn {{ X = {SpacetimeDB.BSATN.StringUtil.GenericToString(X)}, Y = {SpacetimeDB.BSATN.StringUtil.GenericToString(Y)}, Z = {SpacetimeDB.BSATN.StringUtil.GenericToString(Z)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<BTreeMultiColumn>\n    {\n        internal static readonly SpacetimeDB.BSATN.U32 XRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 YRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 ZRW = new();\n\n        public BTreeMultiColumn Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new BTreeMultiColumn();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, BTreeMultiColumn value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<BTreeMultiColumn>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"X\", XRW.GetAlgebraicType(registrar)),\n                        new(\"Y\", YRW.GetAlgebraicType(registrar)),\n                        new(\"Z\", ZRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<BTreeMultiColumn>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashX = X.GetHashCode();\n        var ___hashY = Y.GetHashCode();\n        var ___hashZ = Z.GetHashCode();\n        return ___hashX ^ ___hashY ^ ___hashZ;\n    }\n\n#nullable enable\n    public bool Equals(BTreeMultiColumn that)\n    {\n        var ___eqX = this.X.Equals(that.X);\n        var ___eqY = this.Y.Equals(that.Y);\n        var ___eqZ = this.Z.Equals(that.Z);\n        return ___eqX && ___eqY && ___eqZ;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as BTreeMultiColumn?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(BTreeMultiColumn this_, BTreeMultiColumn that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(BTreeMultiColumn this_, BTreeMultiColumn that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // BTreeMultiColumn\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#BTreeViews.verified.cs",
    "content": "﻿//HintName: BTreeViews.cs\n// <auto-generated />\n#nullable enable\n\npartial struct BTreeViews : System.IEquatable<BTreeViews>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Id = BSATN.IdRW.Read(reader);\n        X = BSATN.XRW.Read(reader);\n        Y = BSATN.YRW.Read(reader);\n        Faction = BSATN.FactionRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdRW.Write(writer, Id);\n        BSATN.XRW.Write(writer, X);\n        BSATN.YRW.Write(writer, Y);\n        BSATN.FactionRW.Write(writer, Faction);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"BTreeViews {{ Id = {SpacetimeDB.BSATN.StringUtil.GenericToString(Id)}, X = {SpacetimeDB.BSATN.StringUtil.GenericToString(X)}, Y = {SpacetimeDB.BSATN.StringUtil.GenericToString(Y)}, Faction = {SpacetimeDB.BSATN.StringUtil.GenericToString(Faction)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<BTreeViews>\n    {\n        internal static readonly SpacetimeDB.Identity.BSATN IdRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 XRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 YRW = new();\n        internal static readonly SpacetimeDB.BSATN.String FactionRW = new();\n\n        public BTreeViews Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new BTreeViews();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, BTreeViews value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<BTreeViews>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Id\", IdRW.GetAlgebraicType(registrar)),\n                    new(\"X\", XRW.GetAlgebraicType(registrar)),\n                    new(\"Y\", YRW.GetAlgebraicType(registrar)),\n                    new(\"Faction\", FactionRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<BTreeViews>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashId = Id.GetHashCode();\n        var ___hashX = X.GetHashCode();\n        var ___hashY = Y.GetHashCode();\n        var ___hashFaction = Faction == null ? 0 : Faction.GetHashCode();\n        return ___hashId ^ ___hashX ^ ___hashY ^ ___hashFaction;\n    }\n\n#nullable enable\n    public bool Equals(BTreeViews that)\n    {\n        var ___eqId = this.Id.Equals(that.Id);\n        var ___eqX = this.X.Equals(that.X);\n        var ___eqY = this.Y.Equals(that.Y);\n        var ___eqFaction =\n            this.Faction == null ? that.Faction == null : this.Faction.Equals(that.Faction);\n        return ___eqId && ___eqX && ___eqY && ___eqFaction;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as BTreeViews?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(BTreeViews this_, BTreeViews that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(BTreeViews this_, BTreeViews that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // BTreeViews\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs",
    "content": "﻿//HintName: FFI.cs\n// <auto-generated />\n#nullable enable\n// The runtime already defines SpacetimeDB.Internal.LocalReadOnly in Runtime\\Internal\\Module.cs as an empty partial type.\n// This is needed so every module build doesn't generate a full LocalReadOnly type, but just adds on to the existing.\n// We extend it here with generated table accessors, and just need to suppress the duplicate-type warning.\n#pragma warning disable CS0436\n#pragma warning disable STDB_UNSTABLE\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing Internal = SpacetimeDB.Internal;\nusing TxContext = SpacetimeDB.Internal.TxContext;\n\nnamespace SpacetimeDB\n{\n    internal readonly struct BTreeMultiColumnCols\n    {\n        public readonly global::SpacetimeDB.Col<global::BTreeMultiColumn, uint> X;\n        public readonly global::SpacetimeDB.Col<global::BTreeMultiColumn, uint> Y;\n        public readonly global::SpacetimeDB.Col<global::BTreeMultiColumn, uint> Z;\n\n        internal BTreeMultiColumnCols(string tableName)\n        {\n            X = new global::SpacetimeDB.Col<global::BTreeMultiColumn, uint>(tableName, \"X\");\n            Y = new global::SpacetimeDB.Col<global::BTreeMultiColumn, uint>(tableName, \"Y\");\n            Z = new global::SpacetimeDB.Col<global::BTreeMultiColumn, uint>(tableName, \"Z\");\n        }\n    }\n\n    internal readonly struct BTreeMultiColumnIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint> X;\n        public readonly global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint> Y;\n        public readonly global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint> Z;\n\n        internal BTreeMultiColumnIxCols(string tableName)\n        {\n            X = new global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint>(tableName, \"X\");\n            Y = new global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint>(tableName, \"Y\");\n            Z = new global::SpacetimeDB.IxCol<global::BTreeMultiColumn, uint>(tableName, \"Z\");\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        internal global::SpacetimeDB.Table<\n            global::BTreeMultiColumn,\n            BTreeMultiColumnCols,\n            BTreeMultiColumnIxCols\n        > BTreeMultiColumn() =>\n            new(\n                \"BTreeMultiColumn\",\n                new BTreeMultiColumnCols(\"BTreeMultiColumn\"),\n                new BTreeMultiColumnIxCols(\"BTreeMultiColumn\")\n            );\n    }\n\n    internal readonly struct BTreeViewsCols\n    {\n        public readonly global::SpacetimeDB.Col<global::BTreeViews, SpacetimeDB.Identity> Id;\n        public readonly global::SpacetimeDB.Col<global::BTreeViews, uint> X;\n        public readonly global::SpacetimeDB.Col<global::BTreeViews, uint> Y;\n        public readonly global::SpacetimeDB.Col<global::BTreeViews, string> Faction;\n\n        internal BTreeViewsCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.Col<global::BTreeViews, SpacetimeDB.Identity>(\n                tableName,\n                \"Id\"\n            );\n            X = new global::SpacetimeDB.Col<global::BTreeViews, uint>(tableName, \"X\");\n            Y = new global::SpacetimeDB.Col<global::BTreeViews, uint>(tableName, \"Y\");\n            Faction = new global::SpacetimeDB.Col<global::BTreeViews, string>(tableName, \"Faction\");\n        }\n    }\n\n    internal readonly struct BTreeViewsIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::BTreeViews, SpacetimeDB.Identity> Id;\n        public readonly global::SpacetimeDB.IxCol<global::BTreeViews, uint> X;\n        public readonly global::SpacetimeDB.IxCol<global::BTreeViews, uint> Y;\n        public readonly global::SpacetimeDB.IxCol<global::BTreeViews, string> Faction;\n\n        internal BTreeViewsIxCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.IxCol<global::BTreeViews, SpacetimeDB.Identity>(\n                tableName,\n                \"Id\"\n            );\n            X = new global::SpacetimeDB.IxCol<global::BTreeViews, uint>(tableName, \"X\");\n            Y = new global::SpacetimeDB.IxCol<global::BTreeViews, uint>(tableName, \"Y\");\n            Faction = new global::SpacetimeDB.IxCol<global::BTreeViews, string>(\n                tableName,\n                \"Faction\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        internal global::SpacetimeDB.Table<\n            global::BTreeViews,\n            BTreeViewsCols,\n            BTreeViewsIxCols\n        > BTreeViews() =>\n            new(\"BTreeViews\", new BTreeViewsCols(\"BTreeViews\"), new BTreeViewsIxCols(\"BTreeViews\"));\n    }\n\n    public readonly struct MultiTable1Cols\n    {\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, string> Name;\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, uint> Foo;\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, uint> Bar;\n\n        internal MultiTable1Cols(string tableName)\n        {\n            Name = new global::SpacetimeDB.Col<global::MultiTableRow, string>(tableName, \"Name\");\n            Foo = new global::SpacetimeDB.Col<global::MultiTableRow, uint>(tableName, \"Foo\");\n            Bar = new global::SpacetimeDB.Col<global::MultiTableRow, uint>(tableName, \"Bar\");\n        }\n    }\n\n    public readonly struct MultiTable1IxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::MultiTableRow, string> Name;\n        public readonly global::SpacetimeDB.IxCol<global::MultiTableRow, uint> Foo;\n\n        internal MultiTable1IxCols(string tableName)\n        {\n            Name = new global::SpacetimeDB.IxCol<global::MultiTableRow, string>(tableName, \"Name\");\n            Foo = new global::SpacetimeDB.IxCol<global::MultiTableRow, uint>(tableName, \"Foo\");\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::MultiTableRow,\n            MultiTable1Cols,\n            MultiTable1IxCols\n        > MultiTable1() =>\n            new(\n                \"MultiTable1\",\n                new MultiTable1Cols(\"MultiTable1\"),\n                new MultiTable1IxCols(\"MultiTable1\")\n            );\n    }\n\n    public readonly struct MultiTable2Cols\n    {\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, string> Name;\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, uint> Foo;\n        public readonly global::SpacetimeDB.Col<global::MultiTableRow, uint> Bar;\n\n        internal MultiTable2Cols(string tableName)\n        {\n            Name = new global::SpacetimeDB.Col<global::MultiTableRow, string>(tableName, \"Name\");\n            Foo = new global::SpacetimeDB.Col<global::MultiTableRow, uint>(tableName, \"Foo\");\n            Bar = new global::SpacetimeDB.Col<global::MultiTableRow, uint>(tableName, \"Bar\");\n        }\n    }\n\n    public readonly struct MultiTable2IxCols\n    {\n        internal MultiTable2IxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::MultiTableRow,\n            MultiTable2Cols,\n            MultiTable2IxCols\n        > MultiTable2() =>\n            new(\n                \"MultiTable2\",\n                new MultiTable2Cols(\"MultiTable2\"),\n                new MultiTable2IxCols(\"MultiTable2\")\n            );\n    }\n\n    public readonly struct PrivateTableCols\n    {\n        internal PrivateTableCols(string tableName) { }\n    }\n\n    public readonly struct PrivateTableIxCols\n    {\n        internal PrivateTableIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::PrivateTable,\n            PrivateTableCols,\n            PrivateTableIxCols\n        > PrivateTable() =>\n            new(\n                \"PrivateTable\",\n                new PrivateTableCols(\"PrivateTable\"),\n                new PrivateTableIxCols(\"PrivateTable\")\n            );\n    }\n\n    public readonly struct PublicTableCols\n    {\n        public readonly global::SpacetimeDB.Col<global::PublicTable, int> Id;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, byte> ByteField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, ushort> UshortField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, uint> UintField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, ulong> UlongField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, System.UInt128> UInt128Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.U128> U128Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.U256> U256Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, sbyte> SbyteField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, short> ShortField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, int> IntField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, long> LongField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, System.Int128> Int128Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.I128> I128Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.I256> I256Field;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, bool> BoolField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, float> FloatField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, double> DoubleField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, string> StringField;\n        public readonly global::SpacetimeDB.Col<\n            global::PublicTable,\n            SpacetimeDB.Identity\n        > IdentityField;\n        public readonly global::SpacetimeDB.Col<\n            global::PublicTable,\n            SpacetimeDB.ConnectionId\n        > ConnectionIdField;\n        public readonly global::SpacetimeDB.Col<\n            global::PublicTable,\n            CustomStruct\n        > CustomStructField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, CustomClass> CustomClassField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, CustomEnum> CustomEnumField;\n        public readonly global::SpacetimeDB.Col<\n            global::PublicTable,\n            CustomTaggedEnum\n        > CustomTaggedEnumField;\n        public readonly global::SpacetimeDB.Col<\n            global::PublicTable,\n            System.Collections.Generic.List<int>\n        > ListField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, int> NullableValueField;\n        public readonly global::SpacetimeDB.Col<global::PublicTable, string> NullableReferenceField;\n\n        internal PublicTableCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.Col<global::PublicTable, int>(tableName, \"Id\");\n            ByteField = new global::SpacetimeDB.Col<global::PublicTable, byte>(\n                tableName,\n                \"ByteField\"\n            );\n            UshortField = new global::SpacetimeDB.Col<global::PublicTable, ushort>(\n                tableName,\n                \"UshortField\"\n            );\n            UintField = new global::SpacetimeDB.Col<global::PublicTable, uint>(\n                tableName,\n                \"UintField\"\n            );\n            UlongField = new global::SpacetimeDB.Col<global::PublicTable, ulong>(\n                tableName,\n                \"UlongField\"\n            );\n            UInt128Field = new global::SpacetimeDB.Col<global::PublicTable, System.UInt128>(\n                tableName,\n                \"UInt128Field\"\n            );\n            U128Field = new global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.U128>(\n                tableName,\n                \"U128Field\"\n            );\n            U256Field = new global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.U256>(\n                tableName,\n                \"U256Field\"\n            );\n            SbyteField = new global::SpacetimeDB.Col<global::PublicTable, sbyte>(\n                tableName,\n                \"SbyteField\"\n            );\n            ShortField = new global::SpacetimeDB.Col<global::PublicTable, short>(\n                tableName,\n                \"ShortField\"\n            );\n            IntField = new global::SpacetimeDB.Col<global::PublicTable, int>(tableName, \"IntField\");\n            LongField = new global::SpacetimeDB.Col<global::PublicTable, long>(\n                tableName,\n                \"LongField\"\n            );\n            Int128Field = new global::SpacetimeDB.Col<global::PublicTable, System.Int128>(\n                tableName,\n                \"Int128Field\"\n            );\n            I128Field = new global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.I128>(\n                tableName,\n                \"I128Field\"\n            );\n            I256Field = new global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.I256>(\n                tableName,\n                \"I256Field\"\n            );\n            BoolField = new global::SpacetimeDB.Col<global::PublicTable, bool>(\n                tableName,\n                \"BoolField\"\n            );\n            FloatField = new global::SpacetimeDB.Col<global::PublicTable, float>(\n                tableName,\n                \"FloatField\"\n            );\n            DoubleField = new global::SpacetimeDB.Col<global::PublicTable, double>(\n                tableName,\n                \"DoubleField\"\n            );\n            StringField = new global::SpacetimeDB.Col<global::PublicTable, string>(\n                tableName,\n                \"StringField\"\n            );\n            IdentityField = new global::SpacetimeDB.Col<global::PublicTable, SpacetimeDB.Identity>(\n                tableName,\n                \"IdentityField\"\n            );\n            ConnectionIdField = new global::SpacetimeDB.Col<\n                global::PublicTable,\n                SpacetimeDB.ConnectionId\n            >(tableName, \"ConnectionIdField\");\n            CustomStructField = new global::SpacetimeDB.Col<global::PublicTable, CustomStruct>(\n                tableName,\n                \"CustomStructField\"\n            );\n            CustomClassField = new global::SpacetimeDB.Col<global::PublicTable, CustomClass>(\n                tableName,\n                \"CustomClassField\"\n            );\n            CustomEnumField = new global::SpacetimeDB.Col<global::PublicTable, CustomEnum>(\n                tableName,\n                \"CustomEnumField\"\n            );\n            CustomTaggedEnumField = new global::SpacetimeDB.Col<\n                global::PublicTable,\n                CustomTaggedEnum\n            >(tableName, \"CustomTaggedEnumField\");\n            ListField = new global::SpacetimeDB.Col<\n                global::PublicTable,\n                System.Collections.Generic.List<int>\n            >(tableName, \"ListField\");\n            NullableValueField = new global::SpacetimeDB.Col<global::PublicTable, int>(\n                tableName,\n                \"NullableValueField\"\n            );\n            NullableReferenceField = new global::SpacetimeDB.Col<global::PublicTable, string>(\n                tableName,\n                \"NullableReferenceField\"\n            );\n        }\n    }\n\n    public readonly struct PublicTableIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<global::PublicTable, int> Id;\n\n        internal PublicTableIxCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.IxCol<global::PublicTable, int>(tableName, \"Id\");\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::PublicTable,\n            PublicTableCols,\n            PublicTableIxCols\n        > PublicTable() =>\n            new(\n                \"PublicTable\",\n                new PublicTableCols(\"PublicTable\"),\n                new PublicTableIxCols(\"PublicTable\")\n            );\n    }\n\n    internal readonly struct RegressionMultipleUniqueIndexesHadSameNameCols\n    {\n        public readonly global::SpacetimeDB.Col<\n            global::RegressionMultipleUniqueIndexesHadSameName,\n            uint\n        > Unique1;\n        public readonly global::SpacetimeDB.Col<\n            global::RegressionMultipleUniqueIndexesHadSameName,\n            uint\n        > Unique2;\n\n        internal RegressionMultipleUniqueIndexesHadSameNameCols(string tableName)\n        {\n            Unique1 = new global::SpacetimeDB.Col<\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint\n            >(tableName, \"Unique1\");\n            Unique2 = new global::SpacetimeDB.Col<\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint\n            >(tableName, \"Unique2\");\n        }\n    }\n\n    internal readonly struct RegressionMultipleUniqueIndexesHadSameNameIxCols\n    {\n        internal RegressionMultipleUniqueIndexesHadSameNameIxCols(string tableName) { }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        internal global::SpacetimeDB.Table<\n            global::RegressionMultipleUniqueIndexesHadSameName,\n            RegressionMultipleUniqueIndexesHadSameNameCols,\n            RegressionMultipleUniqueIndexesHadSameNameIxCols\n        > RegressionMultipleUniqueIndexesHadSameName() =>\n            new(\n                \"RegressionMultipleUniqueIndexesHadSameName\",\n                new RegressionMultipleUniqueIndexesHadSameNameCols(\n                    \"RegressionMultipleUniqueIndexesHadSameName\"\n                ),\n                new RegressionMultipleUniqueIndexesHadSameNameIxCols(\n                    \"RegressionMultipleUniqueIndexesHadSameName\"\n                )\n            );\n    }\n\n    public readonly struct SendMessageTimerCols\n    {\n        public readonly global::SpacetimeDB.Col<global::Timers.SendMessageTimer, ulong> ScheduledId;\n        public readonly global::SpacetimeDB.Col<\n            global::Timers.SendMessageTimer,\n            SpacetimeDB.ScheduleAt\n        > ScheduledAt;\n        public readonly global::SpacetimeDB.Col<global::Timers.SendMessageTimer, string> Text;\n\n        internal SendMessageTimerCols(string tableName)\n        {\n            ScheduledId = new global::SpacetimeDB.Col<global::Timers.SendMessageTimer, ulong>(\n                tableName,\n                \"ScheduledId\"\n            );\n            ScheduledAt = new global::SpacetimeDB.Col<\n                global::Timers.SendMessageTimer,\n                SpacetimeDB.ScheduleAt\n            >(tableName, \"ScheduledAt\");\n            Text = new global::SpacetimeDB.Col<global::Timers.SendMessageTimer, string>(\n                tableName,\n                \"Text\"\n            );\n        }\n    }\n\n    public readonly struct SendMessageTimerIxCols\n    {\n        public readonly global::SpacetimeDB.IxCol<\n            global::Timers.SendMessageTimer,\n            ulong\n        > ScheduledId;\n\n        internal SendMessageTimerIxCols(string tableName)\n        {\n            ScheduledId = new global::SpacetimeDB.IxCol<global::Timers.SendMessageTimer, ulong>(\n                tableName,\n                \"ScheduledId\"\n            );\n        }\n    }\n\n    public readonly partial struct QueryBuilder\n    {\n        public global::SpacetimeDB.Table<\n            global::Timers.SendMessageTimer,\n            SendMessageTimerCols,\n            SendMessageTimerIxCols\n        > SendMessageTimer() =>\n            new(\n                \"SendMessageTimer\",\n                new SendMessageTimerCols(\"SendMessageTimer\"),\n                new SendMessageTimerIxCols(\"SendMessageTimer\")\n            );\n    }\n\n    public sealed record ReducerContext : DbContext<Local>, Internal.IReducerContext\n    {\n        public readonly Identity Sender;\n        public readonly ConnectionId? ConnectionId;\n        public readonly Random Rng;\n        public readonly Timestamp Timestamp;\n        public readonly AuthCtx SenderAuth;\n\n        // **Note:** must be 0..=u32::MAX\n        internal int CounterUuid;\n\n        // We need this property to be non-static for parity with client SDK.\n        public Identity Identity => Internal.IReducerContext.GetIdentity();\n\n        internal ReducerContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time,\n            AuthCtx? senderAuth = null\n        )\n        {\n            Sender = identity;\n            ConnectionId = connectionId;\n            Rng = random;\n            Timestamp = time;\n            SenderAuth = senderAuth ?? AuthCtx.BuildFromSystemTables(connectionId, identity);\n            CounterUuid = 0;\n        }\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if <see cref=\"Uuid\"/> generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Reducer]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    public sealed partial class ProcedureContext : global::SpacetimeDB.ProcedureContextBase\n    {\n        private readonly Local _db = new();\n\n        internal ProcedureContext(\n            Identity identity,\n            ConnectionId? connectionId,\n            Random random,\n            Timestamp time\n        )\n            : base(identity, connectionId, random, time) { }\n\n        protected override global::SpacetimeDB.LocalBase CreateLocal() => _db;\n\n        protected override global::SpacetimeDB.ProcedureTxContextBase CreateTxContext(\n            Internal.TxContext inner\n        ) => _cached ??= new ProcedureTxContext(inner);\n\n        private ProcedureTxContext? _cached;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public Local Db => _db;\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TResult WithTx<TResult>(Func<ProcedureTxContext, TResult> body) =>\n            base.WithTx(tx => body((ProcedureTxContext)tx));\n\n        [Experimental(\"STDB_UNSTABLE\")]\n        public TxOutcome<TResult> TryWithTx<TResult, TError>(\n            Func<ProcedureTxContext, Result<TResult, TError>> body\n        )\n            where TError : Exception => base.TryWithTx(tx => body((ProcedureTxContext)tx));\n\n        /// <summary>\n        /// Create a new random <see cref=\"Uuid\"/> `v4` using the built-in RNG.\n        /// </summary>\n        /// <remarks>\n        /// This method fills the random bytes using the context RNG.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// var uuid = ctx.NewUuidV4();\n        /// Log.Info(uuid);\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV4()\n        {\n            var bytes = new byte[16];\n            Rng.NextBytes(bytes);\n            return Uuid.FromRandomBytesV4(bytes);\n        }\n\n        /// <summary>\n        /// Create a new sortable <see cref=\"Uuid\"/> `v7` using the built-in RNG, monotonic counter,\n        /// and timestamp.\n        /// </summary>\n        /// <returns>\n        /// A newly generated <see cref=\"Uuid\"/> `v7` that is monotonically ordered\n        /// and suitable for use as a primary key or for ordered storage.\n        /// </returns>\n        /// <exception cref=\"Exception\">\n        /// Thrown if UUID generation fails.\n        /// </exception>\n        /// <example>\n        /// <code>\n        /// [SpacetimeDB.Procedure]\n        /// public static Guid GenerateUuidV7(ReducerContext ctx)\n        /// {\n        ///     Guid uuid = ctx.NewUuidV7();\n        ///     Log.Info(uuid);\n        /// }\n        /// </code>\n        /// </example>\n        public Uuid NewUuidV7()\n        {\n            var bytes = new byte[4];\n            Rng.NextBytes(bytes);\n            return Uuid.FromCounterV7(ref CounterUuid, Timestamp, bytes);\n        }\n    }\n\n    [Experimental(\"STDB_UNSTABLE\")]\n    public sealed class ProcedureTxContext : global::SpacetimeDB.ProcedureTxContextBase\n    {\n        internal ProcedureTxContext(Internal.TxContext inner)\n            : base(inner) { }\n\n        public new Local Db => (Local)base.Db;\n    }\n\n    public sealed class Local : global::SpacetimeDB.LocalBase\n    {\n        internal global::SpacetimeDB.Internal.TableHandles.BTreeMultiColumn BTreeMultiColumn =>\n            new();\n        internal global::SpacetimeDB.Internal.TableHandles.BTreeViews BTreeViews => new();\n        public global::SpacetimeDB.Internal.TableHandles.MultiTable1 MultiTable1 => new();\n        public global::SpacetimeDB.Internal.TableHandles.MultiTable2 MultiTable2 => new();\n        public global::SpacetimeDB.Internal.TableHandles.PrivateTable PrivateTable => new();\n        public global::SpacetimeDB.Internal.TableHandles.PublicTable PublicTable => new();\n        internal global::SpacetimeDB.Internal.TableHandles.RegressionMultipleUniqueIndexesHadSameName RegressionMultipleUniqueIndexesHadSameName =>\n            new();\n        public global::SpacetimeDB.Internal.TableHandles.SendMessageTimer SendMessageTimer => new();\n    }\n\n    public sealed record ViewContext : DbContext<Internal.LocalReadOnly>, Internal.IViewContext\n    {\n        public Identity Sender { get; }\n\n        public QueryBuilder From => default;\n\n        internal ViewContext(Identity sender, Internal.LocalReadOnly db)\n            : base(db)\n        {\n            Sender = sender;\n        }\n    }\n\n    public sealed record AnonymousViewContext\n        : DbContext<Internal.LocalReadOnly>,\n            Internal.IAnonymousViewContext\n    {\n        public QueryBuilder From => default;\n\n        internal AnonymousViewContext(Internal.LocalReadOnly db)\n            : base(db) { }\n    }\n}\n\nnamespace SpacetimeDB.Internal.TableHandles\n{\n    internal readonly struct BTreeMultiColumn\n        : global::SpacetimeDB.Internal.ITableView<BTreeMultiColumn, global::BTreeMultiColumn>\n    {\n        public static global::BTreeMultiColumn ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::BTreeMultiColumn row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(BTreeMultiColumn),\n                ProductTypeRef: (uint)\n                    new global::BTreeMultiColumn.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"BTreeMultiColumn_X_Y_Z_idx_btree\",\n                        AccessorName: \"Location\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0, 1, 2])\n                    )\n                ],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                BTreeMultiColumn,\n                global::BTreeMultiColumn\n            >.DoCount();\n\n        public IEnumerable<global::BTreeMultiColumn> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                BTreeMultiColumn,\n                global::BTreeMultiColumn\n            >.DoIter();\n\n        public global::BTreeMultiColumn Insert(global::BTreeMultiColumn row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                BTreeMultiColumn,\n                global::BTreeMultiColumn\n            >.DoInsert(row);\n\n        public bool Delete(global::BTreeMultiColumn row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                BTreeMultiColumn,\n                global::BTreeMultiColumn\n            >.DoDelete(row);\n\n        internal sealed class LocationIndex()\n            : SpacetimeDB.Internal.IndexBase<global::BTreeMultiColumn>(\n                \"BTreeMultiColumn_X_Y_Z_idx_btree\"\n            )\n        {\n            public IEnumerable<global::BTreeMultiColumn> Filter(uint X) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public ulong Delete(uint X) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                global::SpacetimeDB.Bound<uint> X\n            ) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public ulong Delete(global::SpacetimeDB.Bound<uint> X) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public IEnumerable<global::BTreeMultiColumn> Filter((uint X, uint Y) f) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, uint Y) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                (uint X, global::SpacetimeDB.Bound<uint> Y) f\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, global::SpacetimeDB.Bound<uint> Y) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter((uint X, uint Y, uint Z) f) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, uint Y, uint Z) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                (uint X, uint Y, global::SpacetimeDB.Bound<uint> Z) f\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, uint Y, global::SpacetimeDB.Bound<uint> Z) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n        }\n\n        internal LocationIndex Location => new();\n    }\n\n    internal readonly struct BTreeViews\n        : global::SpacetimeDB.Internal.ITableView<BTreeViews, global::BTreeViews>\n    {\n        public static global::BTreeViews ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::BTreeViews row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(BTreeViews),\n                ProductTypeRef: (uint)\n                    new global::BTreeViews.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [0],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"BTreeViews_Id_idx_btree\",\n                        AccessorName: \"Id\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    ),\n                    new(\n                        SourceName: \"BTreeViews_X_Y_idx_btree\",\n                        AccessorName: \"Location\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1, 2])\n                    ),\n                    new(\n                        SourceName: \"BTreeViews_Faction_idx_btree\",\n                        AccessorName: \"Faction\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([3])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        BTreeViews,\n                        global::BTreeViews\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<BTreeViews, global::BTreeViews>.DoCount();\n\n        public IEnumerable<global::BTreeViews> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<BTreeViews, global::BTreeViews>.DoIter();\n\n        public global::BTreeViews Insert(global::BTreeViews row) =>\n            global::SpacetimeDB.Internal.ITableView<BTreeViews, global::BTreeViews>.DoInsert(row);\n\n        public bool Delete(global::BTreeViews row) =>\n            global::SpacetimeDB.Internal.ITableView<BTreeViews, global::BTreeViews>.DoDelete(row);\n\n        internal sealed class IdUniqueIndex\n            : UniqueIndex<\n                BTreeViews,\n                global::BTreeViews,\n                SpacetimeDB.Identity,\n                SpacetimeDB.Identity.BSATN\n            >\n        {\n            internal IdUniqueIndex()\n                : base(\"BTreeViews_Id_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::BTreeViews? Find(SpacetimeDB.Identity key) => FindSingle(key);\n\n            public global::BTreeViews Update(global::BTreeViews row) => DoUpdate(row);\n        }\n\n        internal IdUniqueIndex Id => new();\n\n        internal sealed class LocationIndex()\n            : SpacetimeDB.Internal.IndexBase<global::BTreeViews>(\"BTreeViews_X_Y_idx_btree\")\n        {\n            public IEnumerable<global::BTreeViews> Filter(uint X) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public ulong Delete(uint X) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public IEnumerable<global::BTreeViews> Filter(global::SpacetimeDB.Bound<uint> X) =>\n                DoFilter(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public ulong Delete(global::SpacetimeDB.Bound<uint> X) =>\n                DoDelete(new SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(X));\n\n            public IEnumerable<global::BTreeViews> Filter((uint X, uint Y) f) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, uint Y) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeViews> Filter(\n                (uint X, global::SpacetimeDB.Bound<uint> Y) f\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public ulong Delete((uint X, global::SpacetimeDB.Bound<uint> Y) f) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n        }\n\n        internal LocationIndex Location => new();\n\n        internal sealed class FactionIndex()\n            : SpacetimeDB.Internal.IndexBase<global::BTreeViews>(\"BTreeViews_Faction_idx_btree\")\n        {\n            public IEnumerable<global::BTreeViews> Filter(string Faction) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Faction\n                    )\n                );\n\n            public ulong Delete(string Faction) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Faction\n                    )\n                );\n\n            public IEnumerable<global::BTreeViews> Filter(\n                global::SpacetimeDB.Bound<string> Faction\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Faction\n                    )\n                );\n\n            public ulong Delete(global::SpacetimeDB.Bound<string> Faction) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Faction\n                    )\n                );\n        }\n\n        internal FactionIndex Faction => new();\n    }\n\n    public readonly struct MultiTable1\n        : global::SpacetimeDB.Internal.ITableView<MultiTable1, global::MultiTableRow>\n    {\n        public static global::MultiTableRow ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::MultiTableRow row\n        )\n        {\n            if (row.Foo == default)\n            {\n                row.Foo = global::MultiTableRow.BSATN.FooRW.Read(reader);\n            }\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(MultiTable1),\n                ProductTypeRef: (uint)\n                    new global::MultiTableRow.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [1],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"MultiTable1_Foo_idx_btree\",\n                        AccessorName: \"Foo\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    ),\n                    new(\n                        SourceName: \"MultiTable1_Name_idx_btree\",\n                        AccessorName: \"Name\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        MultiTable1,\n                        global::MultiTableRow\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        MultiTable1,\n                        global::MultiTableRow\n                    >.MakeSequence(1)\n                ],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Public,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable1, global::MultiTableRow>.DoCount();\n\n        public IEnumerable<global::MultiTableRow> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable1, global::MultiTableRow>.DoIter();\n\n        public global::MultiTableRow Insert(global::MultiTableRow row) =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable1, global::MultiTableRow>.DoInsert(\n                row\n            );\n\n        public bool Delete(global::MultiTableRow row) =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable1, global::MultiTableRow>.DoDelete(\n                row\n            );\n\n        public sealed class FooUniqueIndex\n            : UniqueIndex<MultiTable1, global::MultiTableRow, uint, SpacetimeDB.BSATN.U32>\n        {\n            internal FooUniqueIndex()\n                : base(\"MultiTable1_Foo_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::MultiTableRow? Find(uint key) => FindSingle(key);\n\n            public global::MultiTableRow Update(global::MultiTableRow row) => DoUpdate(row);\n        }\n\n        public FooUniqueIndex Foo => new();\n\n        public sealed class NameIndex()\n            : SpacetimeDB.Internal.IndexBase<global::MultiTableRow>(\"MultiTable1_Name_idx_btree\")\n        {\n            public IEnumerable<global::MultiTableRow> Filter(string Name) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Name\n                    )\n                );\n\n            public ulong Delete(string Name) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Name\n                    )\n                );\n\n            public IEnumerable<global::MultiTableRow> Filter(\n                global::SpacetimeDB.Bound<string> Name\n            ) =>\n                DoFilter(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Name\n                    )\n                );\n\n            public ulong Delete(global::SpacetimeDB.Bound<string> Name) =>\n                DoDelete(\n                    new SpacetimeDB.Internal.BTreeIndexBounds<string, SpacetimeDB.BSATN.String>(\n                        Name\n                    )\n                );\n        }\n\n        public NameIndex Name => new();\n    }\n\n    public readonly struct MultiTable2\n        : global::SpacetimeDB.Internal.ITableView<MultiTable2, global::MultiTableRow>\n    {\n        public static global::MultiTableRow ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::MultiTableRow row\n        )\n        {\n            if (row.Foo == default)\n            {\n                row.Foo = global::MultiTableRow.BSATN.FooRW.Read(reader);\n            }\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(MultiTable2),\n                ProductTypeRef: (uint)\n                    new global::MultiTableRow.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"MultiTable2_Bar_idx_btree\",\n                        AccessorName: \"Bar\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([2])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        MultiTable2,\n                        global::MultiTableRow\n                    >.MakeUniqueConstraint(2)\n                ],\n                Sequences:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        MultiTable2,\n                        global::MultiTableRow\n                    >.MakeSequence(1)\n                ],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable2, global::MultiTableRow>.DoCount();\n\n        public IEnumerable<global::MultiTableRow> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable2, global::MultiTableRow>.DoIter();\n\n        public global::MultiTableRow Insert(global::MultiTableRow row) =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable2, global::MultiTableRow>.DoInsert(\n                row\n            );\n\n        public bool Delete(global::MultiTableRow row) =>\n            global::SpacetimeDB.Internal.ITableView<MultiTable2, global::MultiTableRow>.DoDelete(\n                row\n            );\n\n        public sealed class BarUniqueIndex\n            : UniqueIndex<MultiTable2, global::MultiTableRow, uint, SpacetimeDB.BSATN.U32>\n        {\n            internal BarUniqueIndex()\n                : base(\"MultiTable2_Bar_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::MultiTableRow? Find(uint key) => FindSingle(key);\n        }\n\n        public BarUniqueIndex Bar => new();\n    }\n\n    public readonly struct PrivateTable\n        : global::SpacetimeDB.Internal.ITableView<PrivateTable, global::PrivateTable>\n    {\n        public static global::PrivateTable ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::PrivateTable row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(PrivateTable),\n                ProductTypeRef: (uint)\n                    new global::PrivateTable.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [],\n                Indexes: [],\n                Constraints: [],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: true\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<PrivateTable, global::PrivateTable>.DoCount();\n\n        public IEnumerable<global::PrivateTable> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<PrivateTable, global::PrivateTable>.DoIter();\n\n        public global::PrivateTable Insert(global::PrivateTable row) =>\n            global::SpacetimeDB.Internal.ITableView<PrivateTable, global::PrivateTable>.DoInsert(\n                row\n            );\n\n        public bool Delete(global::PrivateTable row) =>\n            global::SpacetimeDB.Internal.ITableView<PrivateTable, global::PrivateTable>.DoDelete(\n                row\n            );\n    }\n\n    public readonly struct PublicTable\n        : global::SpacetimeDB.Internal.ITableView<PublicTable, global::PublicTable>\n    {\n        public static global::PublicTable ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::PublicTable row\n        )\n        {\n            if (row.Id == default)\n            {\n                row.Id = global::PublicTable.BSATN.IdRW.Read(reader);\n            }\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(PublicTable),\n                ProductTypeRef: (uint)\n                    new global::PublicTable.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [0],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"PublicTable_Id_idx_btree\",\n                        AccessorName: \"Id\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        PublicTable,\n                        global::PublicTable\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        PublicTable,\n                        global::PublicTable\n                    >.MakeSequence(0)\n                ],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Public,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<PublicTable, global::PublicTable>.DoCount();\n\n        public IEnumerable<global::PublicTable> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<PublicTable, global::PublicTable>.DoIter();\n\n        public global::PublicTable Insert(global::PublicTable row) =>\n            global::SpacetimeDB.Internal.ITableView<PublicTable, global::PublicTable>.DoInsert(row);\n\n        public bool Delete(global::PublicTable row) =>\n            global::SpacetimeDB.Internal.ITableView<PublicTable, global::PublicTable>.DoDelete(row);\n\n        public sealed class IdUniqueIndex\n            : UniqueIndex<PublicTable, global::PublicTable, int, SpacetimeDB.BSATN.I32>\n        {\n            internal IdUniqueIndex()\n                : base(\"PublicTable_Id_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::PublicTable? Find(int key) => FindSingle(key);\n\n            public global::PublicTable Update(global::PublicTable row) => DoUpdate(row);\n        }\n\n        public IdUniqueIndex Id => new();\n    }\n\n    internal readonly struct RegressionMultipleUniqueIndexesHadSameName\n        : global::SpacetimeDB.Internal.ITableView<\n            RegressionMultipleUniqueIndexesHadSameName,\n            global::RegressionMultipleUniqueIndexesHadSameName\n        >\n    {\n        public static global::RegressionMultipleUniqueIndexesHadSameName ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::RegressionMultipleUniqueIndexesHadSameName row\n        )\n        {\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(RegressionMultipleUniqueIndexesHadSameName),\n                ProductTypeRef: (uint)\n                    new global::RegressionMultipleUniqueIndexesHadSameName.BSATN()\n                        .GetAlgebraicType(registrar)\n                        .Ref_,\n                PrimaryKey: [],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"RegressionMultipleUniqueIndexesHadSameName_Unique1_idx_btree\",\n                        AccessorName: \"Unique1\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    ),\n                    new(\n                        SourceName: \"RegressionMultipleUniqueIndexesHadSameName_Unique2_idx_btree\",\n                        AccessorName: \"Unique2\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        RegressionMultipleUniqueIndexesHadSameName,\n                        global::RegressionMultipleUniqueIndexesHadSameName\n                    >.MakeUniqueConstraint(0),\n                    global::SpacetimeDB.Internal.ITableView<\n                        RegressionMultipleUniqueIndexesHadSameName,\n                        global::RegressionMultipleUniqueIndexesHadSameName\n                    >.MakeUniqueConstraint(1)\n                ],\n                Sequences: [],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() => null;\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName\n            >.DoCount();\n\n        public IEnumerable<global::RegressionMultipleUniqueIndexesHadSameName> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName\n            >.DoIter();\n\n        public global::RegressionMultipleUniqueIndexesHadSameName Insert(\n            global::RegressionMultipleUniqueIndexesHadSameName row\n        ) =>\n            global::SpacetimeDB.Internal.ITableView<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName\n            >.DoInsert(row);\n\n        public bool Delete(global::RegressionMultipleUniqueIndexesHadSameName row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName\n            >.DoDelete(row);\n\n        internal sealed class Unique1UniqueIndex\n            : UniqueIndex<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal Unique1UniqueIndex()\n                : base(\"RegressionMultipleUniqueIndexesHadSameName_Unique1_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::RegressionMultipleUniqueIndexesHadSameName? Find(uint key) =>\n                FindSingle(key);\n        }\n\n        internal Unique1UniqueIndex Unique1 => new();\n\n        internal sealed class Unique2UniqueIndex\n            : UniqueIndex<\n                RegressionMultipleUniqueIndexesHadSameName,\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal Unique2UniqueIndex()\n                : base(\"RegressionMultipleUniqueIndexesHadSameName_Unique2_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::RegressionMultipleUniqueIndexesHadSameName? Find(uint key) =>\n                FindSingle(key);\n        }\n\n        internal Unique2UniqueIndex Unique2 => new();\n    }\n\n    public readonly struct SendMessageTimer\n        : global::SpacetimeDB.Internal.ITableView<SendMessageTimer, global::Timers.SendMessageTimer>\n    {\n        public static global::Timers.SendMessageTimer ReadGenFields(\n            System.IO.BinaryReader reader,\n            global::Timers.SendMessageTimer row\n        )\n        {\n            if (row.ScheduledId == default)\n            {\n                row.ScheduledId = global::Timers.SendMessageTimer.BSATN.ScheduledIdRW.Read(reader);\n            }\n            return row;\n        }\n\n        public static SpacetimeDB.Internal.RawTableDefV10 MakeTableDesc(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(SendMessageTimer),\n                ProductTypeRef: (uint)\n                    new global::Timers.SendMessageTimer.BSATN().GetAlgebraicType(registrar).Ref_,\n                PrimaryKey: [0],\n                Indexes:\n                [\n                    new(\n                        SourceName: \"SendMessageTimer_ScheduledId_idx_btree\",\n                        AccessorName: \"ScheduledId\",\n                        Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0])\n                    )\n                ],\n                Constraints:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        SendMessageTimer,\n                        global::Timers.SendMessageTimer\n                    >.MakeUniqueConstraint(0)\n                ],\n                Sequences:\n                [\n                    global::SpacetimeDB.Internal.ITableView<\n                        SendMessageTimer,\n                        global::Timers.SendMessageTimer\n                    >.MakeSequence(0)\n                ],\n                TableType: SpacetimeDB.Internal.TableType.User,\n                TableAccess: SpacetimeDB.Internal.TableAccess.Private,\n                DefaultValues: [],\n                IsEvent: false\n            );\n\n        public static SpacetimeDB.Internal.RawScheduleDefV10? MakeScheduleDesc() =>\n            global::SpacetimeDB.Internal.ITableView<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer\n            >.MakeSchedule(\"SendScheduledMessage\", 1);\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count =>\n            global::SpacetimeDB.Internal.ITableView<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer\n            >.DoCount();\n\n        public IEnumerable<global::Timers.SendMessageTimer> Iter() =>\n            global::SpacetimeDB.Internal.ITableView<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer\n            >.DoIter();\n\n        public global::Timers.SendMessageTimer Insert(global::Timers.SendMessageTimer row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer\n            >.DoInsert(row);\n\n        public bool Delete(global::Timers.SendMessageTimer row) =>\n            global::SpacetimeDB.Internal.ITableView<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer\n            >.DoDelete(row);\n\n        public sealed class ScheduledIdUniqueIndex\n            : UniqueIndex<\n                SendMessageTimer,\n                global::Timers.SendMessageTimer,\n                ulong,\n                SpacetimeDB.BSATN.U64\n            >\n        {\n            internal ScheduledIdUniqueIndex()\n                : base(\"SendMessageTimer_ScheduledId_idx_btree\") { }\n\n            // Important: don't move this to the base class.\n            // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based\n            // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another.\n            public global::Timers.SendMessageTimer? Find(ulong key) => FindSingle(key);\n\n            public global::Timers.SendMessageTimer Update(global::Timers.SendMessageTimer row) =>\n                DoUpdate(row);\n        }\n\n        public ScheduledIdUniqueIndex ScheduledId => new();\n    }\n}\n\nsealed class public_table_queryViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"public_table_query\",\n            Index: 0,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: global::SpacetimeDB.BSATN.AlgebraicType.MakeQueryBuilderProductType(\n                new PublicTable.BSATN().GetAlgebraicType(registrar)\n            )\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.PublicTableQuery((SpacetimeDB.ViewContext)ctx);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RawSql(\n                returnValue.ToSql()\n            );\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'public_table_query': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class public_table_viewViewDispatcher : global::SpacetimeDB.Internal.IView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"public_table_view\",\n            Index: 1,\n            IsPublic: true,\n            IsAnonymous: false,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<\n                PublicTable,\n                PublicTable.BSATN\n            >().GetAlgebraicType(registrar)\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.PublicTableByIdentity((SpacetimeDB.ViewContext)ctx);\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                PublicTable,\n                PublicTable.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'public_table_view': \" + e);\n            throw;\n        }\n    }\n}\n\nsealed class find_public_table__by_identityViewDispatcher\n    : global::SpacetimeDB.Internal.IAnonymousView\n{\n    public SpacetimeDB.Internal.RawViewDefV10 MakeAnonymousViewDef(\n        SpacetimeDB.BSATN.ITypeRegistrar registrar\n    ) =>\n        new global::SpacetimeDB.Internal.RawViewDefV10(\n            SourceName: \"find_public_table__by_identity\",\n            Index: 0,\n            IsPublic: true,\n            IsAnonymous: true,\n            Params: [],\n            ReturnType: new SpacetimeDB.BSATN.ValueOption<\n                PublicTable,\n                PublicTable.BSATN\n            >().GetAlgebraicType(registrar)\n        );\n\n    public byte[] Invoke(\n        System.IO.BinaryReader reader,\n        global::SpacetimeDB.Internal.IAnonymousViewContext ctx\n    )\n    {\n        try\n        {\n            var returnValue = Module.FindPublicTableByIdentity(\n                (SpacetimeDB.AnonymousViewContext)ctx\n            );\n            var listSerializer = SpacetimeDB.BSATN.ValueOption<\n                PublicTable,\n                PublicTable.BSATN\n            >.GetListSerializer();\n            var listValue = ModuleRegistration.ToListOrEmpty(returnValue);\n            var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);\n            var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();\n            using var output = new System.IO.MemoryStream();\n            using var writer = new System.IO.BinaryWriter(output);\n            headerRW.Write(writer, header);\n            listSerializer.Write(writer, listValue);\n            return output.ToArray();\n        }\n        catch (System.Exception e)\n        {\n            global::SpacetimeDB.Log.Error(\"Error in view 'find_public_table__by_identity': \" + e);\n            throw;\n        }\n    }\n}\n\nnamespace SpacetimeDB.Internal.ViewHandles\n{\n    internal sealed class BTreeMultiColumnReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::BTreeMultiColumn>\n    {\n        internal BTreeMultiColumnReadOnly()\n            : base(\"BTreeMultiColumn\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class LocationIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::BTreeMultiColumn>\n        {\n            internal LocationIndex()\n                : base(\"BTreeMultiColumn_X_Y_Z_idx_btree\") { }\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(uint X) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(\n                        X\n                    )\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                global::SpacetimeDB.Bound<uint> X\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(\n                        X\n                    )\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter((uint X, uint Y) f) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                (uint X, global::SpacetimeDB.Bound<uint> Y) f\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter((uint X, uint Y, uint Z) f) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeMultiColumn> Filter(\n                (uint X, uint Y, global::SpacetimeDB.Bound<uint> Z) f\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n        }\n\n        internal LocationIndex Location => new();\n    }\n\n    internal sealed class BTreeViewsReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::BTreeViews>\n    {\n        internal BTreeViewsReadOnly()\n            : base(\"BTreeViews\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.BTreeViewsReadOnly,\n                global::BTreeViews,\n                SpacetimeDB.Identity,\n                SpacetimeDB.Identity.BSATN\n            >\n        {\n            internal IdIndex()\n                : base(\"BTreeViews_Id_idx_btree\") { }\n\n            public global::BTreeViews? Find(SpacetimeDB.Identity key) => FindSingle(key);\n        }\n\n        public IdIndex Id => new();\n\n        public sealed class LocationIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::BTreeViews>\n        {\n            internal LocationIndex()\n                : base(\"BTreeViews_X_Y_idx_btree\") { }\n\n            public IEnumerable<global::BTreeViews> Filter(uint X) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(\n                        X\n                    )\n                );\n\n            public IEnumerable<global::BTreeViews> Filter(global::SpacetimeDB.Bound<uint> X) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<uint, SpacetimeDB.BSATN.U32>(\n                        X\n                    )\n                );\n\n            public IEnumerable<global::BTreeViews> Filter((uint X, uint Y) f) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n\n            public IEnumerable<global::BTreeViews> Filter(\n                (uint X, global::SpacetimeDB.Bound<uint> Y) f\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        uint,\n                        SpacetimeDB.BSATN.U32,\n                        uint,\n                        SpacetimeDB.BSATN.U32\n                    >(f)\n                );\n        }\n\n        internal LocationIndex Location => new();\n\n        public sealed class FactionIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::BTreeViews>\n        {\n            internal FactionIndex()\n                : base(\"BTreeViews_Faction_idx_btree\") { }\n\n            public IEnumerable<global::BTreeViews> Filter(string Faction) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        string,\n                        SpacetimeDB.BSATN.String\n                    >(Faction)\n                );\n\n            public IEnumerable<global::BTreeViews> Filter(\n                global::SpacetimeDB.Bound<string> Faction\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        string,\n                        SpacetimeDB.BSATN.String\n                    >(Faction)\n                );\n        }\n\n        internal FactionIndex Faction => new();\n    }\n\n    public sealed class MultiTable1ReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::MultiTableRow>\n    {\n        internal MultiTable1ReadOnly()\n            : base(\"MultiTable1\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class FooIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.MultiTable1ReadOnly,\n                global::MultiTableRow,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal FooIndex()\n                : base(\"MultiTable1_Foo_idx_btree\") { }\n\n            public global::MultiTableRow? Find(uint key) => FindSingle(key);\n        }\n\n        public FooIndex Foo => new();\n\n        public sealed class NameIndex\n            : global::SpacetimeDB.Internal.ReadOnlyIndexBase<global::MultiTableRow>\n        {\n            internal NameIndex()\n                : base(\"MultiTable1_Name_idx_btree\") { }\n\n            public IEnumerable<global::MultiTableRow> Filter(string Name) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        string,\n                        SpacetimeDB.BSATN.String\n                    >(Name)\n                );\n\n            public IEnumerable<global::MultiTableRow> Filter(\n                global::SpacetimeDB.Bound<string> Name\n            ) =>\n                DoFilter(\n                    new global::SpacetimeDB.Internal.BTreeIndexBounds<\n                        string,\n                        SpacetimeDB.BSATN.String\n                    >(Name)\n                );\n        }\n\n        public NameIndex Name => new();\n    }\n\n    public sealed class MultiTable2ReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::MultiTableRow>\n    {\n        internal MultiTable2ReadOnly()\n            : base(\"MultiTable2\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class BarIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.MultiTable2ReadOnly,\n                global::MultiTableRow,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal BarIndex()\n                : base(\"MultiTable2_Bar_idx_btree\") { }\n\n            public global::MultiTableRow? Find(uint key) => FindSingle(key);\n        }\n\n        public BarIndex Bar => new();\n    }\n\n    public sealed class PrivateTableReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::PrivateTable>\n    {\n        internal PrivateTableReadOnly()\n            : base(\"PrivateTable\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n    }\n\n    public sealed class PublicTableReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::PublicTable>\n    {\n        internal PublicTableReadOnly()\n            : base(\"PublicTable\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class IdIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.PublicTableReadOnly,\n                global::PublicTable,\n                int,\n                SpacetimeDB.BSATN.I32\n            >\n        {\n            internal IdIndex()\n                : base(\"PublicTable_Id_idx_btree\") { }\n\n            public global::PublicTable? Find(int key) => FindSingle(key);\n        }\n\n        public IdIndex Id => new();\n    }\n\n    internal sealed class RegressionMultipleUniqueIndexesHadSameNameReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::RegressionMultipleUniqueIndexesHadSameName>\n    {\n        internal RegressionMultipleUniqueIndexesHadSameNameReadOnly()\n            : base(\"RegressionMultipleUniqueIndexesHadSameName\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class Unique1Index\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.RegressionMultipleUniqueIndexesHadSameNameReadOnly,\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal Unique1Index()\n                : base(\"RegressionMultipleUniqueIndexesHadSameName_Unique1_idx_btree\") { }\n\n            public global::RegressionMultipleUniqueIndexesHadSameName? Find(uint key) =>\n                FindSingle(key);\n        }\n\n        public Unique1Index Unique1 => new();\n\n        public sealed class Unique2Index\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.RegressionMultipleUniqueIndexesHadSameNameReadOnly,\n                global::RegressionMultipleUniqueIndexesHadSameName,\n                uint,\n                SpacetimeDB.BSATN.U32\n            >\n        {\n            internal Unique2Index()\n                : base(\"RegressionMultipleUniqueIndexesHadSameName_Unique2_idx_btree\") { }\n\n            public global::RegressionMultipleUniqueIndexesHadSameName? Find(uint key) =>\n                FindSingle(key);\n        }\n\n        public Unique2Index Unique2 => new();\n    }\n\n    public sealed class SendMessageTimerReadOnly\n        : global::SpacetimeDB.Internal.ReadOnlyTableView<global::Timers.SendMessageTimer>\n    {\n        internal SendMessageTimerReadOnly()\n            : base(\"SendMessageTimer\") { }\n\n        /// <summary>\n        /// Returns the number of rows in this table.\n        ///\n        /// This reads datastore metadata, so it runs in constant time.\n        /// It also takes into account modifications by the current transaction.\n        /// </summary>\n        public ulong Count => DoCount();\n\n        public sealed class ScheduledIdIndex\n            : global::SpacetimeDB.Internal.ReadOnlyUniqueIndex<\n                global::SpacetimeDB.Internal.ViewHandles.SendMessageTimerReadOnly,\n                global::Timers.SendMessageTimer,\n                ulong,\n                SpacetimeDB.BSATN.U64\n            >\n        {\n            internal ScheduledIdIndex()\n                : base(\"SendMessageTimer_ScheduledId_idx_btree\") { }\n\n            public global::Timers.SendMessageTimer? Find(ulong key) => FindSingle(key);\n        }\n\n        public ScheduledIdIndex ScheduledId => new();\n    }\n}\n\nnamespace SpacetimeDB.Internal\n{\n    public sealed partial class LocalReadOnly\n    {\n        internal global::SpacetimeDB.Internal.ViewHandles.BTreeMultiColumnReadOnly BTreeMultiColumn =>\n            new();\n        internal global::SpacetimeDB.Internal.ViewHandles.BTreeViewsReadOnly BTreeViews => new();\n        public global::SpacetimeDB.Internal.ViewHandles.MultiTable1ReadOnly MultiTable1 => new();\n        public global::SpacetimeDB.Internal.ViewHandles.MultiTable2ReadOnly MultiTable2 => new();\n        public global::SpacetimeDB.Internal.ViewHandles.PrivateTableReadOnly PrivateTable => new();\n        public global::SpacetimeDB.Internal.ViewHandles.PublicTableReadOnly PublicTable => new();\n        internal global::SpacetimeDB.Internal.ViewHandles.RegressionMultipleUniqueIndexesHadSameNameReadOnly RegressionMultipleUniqueIndexesHadSameName =>\n            new();\n        public global::SpacetimeDB.Internal.ViewHandles.SendMessageTimerReadOnly SendMessageTimer =>\n            new();\n    }\n}\n\nstatic class ModuleRegistration\n{\n    class Init : SpacetimeDB.Internal.IReducer\n    {\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(Init),\n                Params: [],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => SpacetimeDB.Internal.Lifecycle.Init;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Timers.Init((SpacetimeDB.ReducerContext)ctx);\n        }\n    }\n\n    class InsertData : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly PublicTable.BSATN dataRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(InsertData),\n                Params: [new(\"data\", dataRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.InsertData((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader));\n        }\n    }\n\n    class InsertData2 : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly PublicTable.BSATN dataRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(InsertData2),\n                Params: [new(\"data\", dataRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Test.NestingNamespaces.AndClasses.InsertData2(\n                (SpacetimeDB.ReducerContext)ctx,\n                dataRW.Read(reader)\n            );\n        }\n    }\n\n    class InsertMultiData : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly MultiTableRow.BSATN dataRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(InsertMultiData),\n                Params: [new(\"data\", dataRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            MultiTableRow.InsertMultiData((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader));\n        }\n    }\n\n    class ScheduleImmediate : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly PublicTable.BSATN dataRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(ScheduleImmediate),\n                Params: [new(\"data\", dataRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Reducers.ScheduleImmediate((SpacetimeDB.ReducerContext)ctx, dataRW.Read(reader));\n        }\n    }\n\n    class SendScheduledMessage : SpacetimeDB.Internal.IReducer\n    {\n        private static readonly Timers.SendMessageTimer.BSATN argRW = new();\n\n        public SpacetimeDB.Internal.RawReducerDefV10 MakeReducerDef(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            new(\n                SourceName: nameof(SendScheduledMessage),\n                Params: [new(\"arg\", argRW.GetAlgebraicType(registrar))],\n                Visibility: SpacetimeDB.Internal.FunctionVisibility.ClientCallable,\n                OkReturnType: SpacetimeDB.BSATN.AlgebraicType.Unit,\n                ErrReturnType: new SpacetimeDB.BSATN.AlgebraicType.String(default)\n            );\n\n        public SpacetimeDB.Internal.Lifecycle? Lifecycle => null;\n\n        public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx)\n        {\n            Timers.SendScheduledMessage((SpacetimeDB.ReducerContext)ctx, argRW.Read(reader));\n        }\n    }\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : struct => value is null ? new List<T>() : new List<T> { value.Value };\n\n    public static List<T> ToListOrEmpty<T>(T? value)\n        where T : class => value is null ? new List<T>() : new List<T> { value };\n\n#if EXPERIMENTAL_WASM_AOT\n    // In AOT mode we're building a library.\n    // Main method won't be called automatically, so we need to export it as a preinit function.\n    [UnmanagedCallersOnly(EntryPoint = \"__preinit__10_init_csharp\")]\n#else\n    // Prevent trimming of FFI exports that are invoked from C and not visible to C# trimmer.\n    [DynamicDependency(\n        DynamicallyAccessedMemberTypes.PublicMethods,\n        typeof(SpacetimeDB.Internal.Module)\n    )]\n#endif\n    public static void Main()\n    {\n        SpacetimeDB.Internal.Module.SetReducerContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ReducerContext(identity, connectionId, random, time)\n        );\n        SpacetimeDB.Internal.Module.SetViewContextConstructor(\n            identity => new SpacetimeDB.ViewContext(\n                identity,\n                new SpacetimeDB.Internal.LocalReadOnly()\n            )\n        );\n        SpacetimeDB.Internal.Module.SetAnonymousViewContextConstructor(\n            () => new SpacetimeDB.AnonymousViewContext(new SpacetimeDB.Internal.LocalReadOnly())\n        );\n        SpacetimeDB.Internal.Module.SetProcedureContextConstructor(\n            (identity, connectionId, random, time) =>\n                new SpacetimeDB.ProcedureContext(identity, connectionId, random, time)\n        );\n        var __memoryStream = new MemoryStream();\n        var __writer = new BinaryWriter(__memoryStream);\n\n        SpacetimeDB.Internal.Module.RegisterReducer<Init>();\n        SpacetimeDB.Internal.Module.RegisterReducer<InsertData>();\n        SpacetimeDB.Internal.Module.RegisterReducer<InsertData2>();\n        SpacetimeDB.Internal.Module.RegisterReducer<InsertMultiData>();\n        SpacetimeDB.Internal.Module.RegisterReducer<ScheduleImmediate>();\n        SpacetimeDB.Internal.Module.RegisterReducer<SendScheduledMessage>();\n\n        // IMPORTANT: The order in which we register views matters.\n        // It must correspond to the order in which we call `GenerateDispatcherClass`.\n        // See the comment on `GenerateDispatcherClass` for more explanation.\n        SpacetimeDB.Internal.Module.RegisterView<public_table_queryViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterView<public_table_viewViewDispatcher>();\n        SpacetimeDB.Internal.Module.RegisterAnonymousView<find_public_table__by_identityViewDispatcher>();\n\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::BTreeMultiColumn,\n            SpacetimeDB.Internal.TableHandles.BTreeMultiColumn\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::BTreeViews,\n            SpacetimeDB.Internal.TableHandles.BTreeViews\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::MultiTableRow,\n            SpacetimeDB.Internal.TableHandles.MultiTable1\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::MultiTableRow,\n            SpacetimeDB.Internal.TableHandles.MultiTable2\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::PrivateTable,\n            SpacetimeDB.Internal.TableHandles.PrivateTable\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::PublicTable,\n            SpacetimeDB.Internal.TableHandles.PublicTable\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::RegressionMultipleUniqueIndexesHadSameName,\n            SpacetimeDB.Internal.TableHandles.RegressionMultipleUniqueIndexesHadSameName\n        >();\n        SpacetimeDB.Internal.Module.RegisterTable<\n            global::Timers.SendMessageTimer,\n            SpacetimeDB.Internal.TableHandles.SendMessageTimer\n        >();\n        SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(\n            global::Module.ALL_PUBLIC_TABLES\n        );\n    }\n\n    // Exports only work from the main assembly, so we need to generate forwarding methods.\n#if EXPERIMENTAL_WASM_AOT\n    [UnmanagedCallersOnly(EntryPoint = \"__describe_module__\")]\n    public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) =>\n        SpacetimeDB.Internal.Module.__describe_module__(d);\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_reducer__\")]\n    public static SpacetimeDB.Internal.Errno __call_reducer__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink error\n    ) =>\n        SpacetimeDB.Internal.Module.__call_reducer__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            error\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_procedure__\")]\n    public static SpacetimeDB.Internal.Errno __call_procedure__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        SpacetimeDB.Timestamp timestamp,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink result_sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_procedure__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            conn_id_0,\n            conn_id_1,\n            timestamp,\n            args,\n            result_sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view__\")]\n    public static SpacetimeDB.Internal.Errno __call_view__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) =>\n        SpacetimeDB.Internal.Module.__call_view__(\n            id,\n            sender_0,\n            sender_1,\n            sender_2,\n            sender_3,\n            args,\n            sink\n        );\n\n    [UnmanagedCallersOnly(EntryPoint = \"__call_view_anon__\")]\n    public static SpacetimeDB.Internal.Errno __call_view_anon__(\n        uint id,\n        SpacetimeDB.Internal.BytesSource args,\n        SpacetimeDB.Internal.BytesSink sink\n    ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink);\n#endif\n}\n\n#pragma warning restore STDB_UNSTABLE\n#pragma warning restore CS0436\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#MultiTableRow.InsertMultiData.verified.cs",
    "content": "﻿//HintName: MultiTableRow.InsertMultiData.cs\n// <auto-generated />\n#nullable enable\n\npartial struct MultiTableRow\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateInsertMultiData(MultiTableRow data)\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new MultiTableRow.BSATN().Write(writer, data);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(InsertMultiData),\n            stream\n        );\n    }\n} // MultiTableRow\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#MultiTableRow.verified.cs",
    "content": "﻿//HintName: MultiTableRow.cs\n// <auto-generated />\n#nullable enable\n\npartial struct MultiTableRow\n    : System.IEquatable<MultiTableRow>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Name = BSATN.NameRW.Read(reader);\n        Foo = BSATN.FooRW.Read(reader);\n        Bar = BSATN.BarRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.NameRW.Write(writer, Name);\n        BSATN.FooRW.Write(writer, Foo);\n        BSATN.BarRW.Write(writer, Bar);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"MultiTableRow {{ Name = {SpacetimeDB.BSATN.StringUtil.GenericToString(Name)}, Foo = {SpacetimeDB.BSATN.StringUtil.GenericToString(Foo)}, Bar = {SpacetimeDB.BSATN.StringUtil.GenericToString(Bar)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<MultiTableRow>\n    {\n        internal static readonly SpacetimeDB.BSATN.String NameRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 FooRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 BarRW = new();\n\n        public MultiTableRow Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new MultiTableRow();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, MultiTableRow value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<MultiTableRow>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Name\", NameRW.GetAlgebraicType(registrar)),\n                    new(\"Foo\", FooRW.GetAlgebraicType(registrar)),\n                    new(\"Bar\", BarRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<MultiTableRow>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashName = Name == null ? 0 : Name.GetHashCode();\n        var ___hashFoo = Foo.GetHashCode();\n        var ___hashBar = Bar.GetHashCode();\n        return ___hashName ^ ___hashFoo ^ ___hashBar;\n    }\n\n#nullable enable\n    public bool Equals(MultiTableRow that)\n    {\n        var ___eqName = this.Name == null ? that.Name == null : this.Name.Equals(that.Name);\n        var ___eqFoo = this.Foo.Equals(that.Foo);\n        var ___eqBar = this.Bar.Equals(that.Bar);\n        return ___eqName && ___eqFoo && ___eqBar;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as MultiTableRow?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(MultiTableRow this_, MultiTableRow that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(MultiTableRow this_, MultiTableRow that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // MultiTableRow\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#PrivateTable.verified.cs",
    "content": "﻿//HintName: PrivateTable.cs\n// <auto-generated />\n#nullable enable\n\npartial class PrivateTable : System.IEquatable<PrivateTable>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader) { }\n\n    public void WriteFields(System.IO.BinaryWriter writer) { }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() => $\"PrivateTable {{  }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<PrivateTable>\n    {\n        public PrivateTable Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new PrivateTable();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, PrivateTable value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<PrivateTable>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[] { }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<PrivateTable>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        return 0;\n    }\n\n#nullable enable\n    public bool Equals(PrivateTable? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        return true;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as PrivateTable;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(PrivateTable? this_, PrivateTable? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(PrivateTable? this_, PrivateTable? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // PrivateTable\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#PublicTable.verified.cs",
    "content": "﻿//HintName: PublicTable.cs\n// <auto-generated />\n#nullable enable\n\npartial struct PublicTable : System.IEquatable<PublicTable>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Id = BSATN.IdRW.Read(reader);\n        ByteField = BSATN.ByteFieldRW.Read(reader);\n        UshortField = BSATN.UshortFieldRW.Read(reader);\n        UintField = BSATN.UintFieldRW.Read(reader);\n        UlongField = BSATN.UlongFieldRW.Read(reader);\n        UInt128Field = BSATN.UInt128FieldRW.Read(reader);\n        U128Field = BSATN.U128FieldRW.Read(reader);\n        U256Field = BSATN.U256FieldRW.Read(reader);\n        SbyteField = BSATN.SbyteFieldRW.Read(reader);\n        ShortField = BSATN.ShortFieldRW.Read(reader);\n        IntField = BSATN.IntFieldRW.Read(reader);\n        LongField = BSATN.LongFieldRW.Read(reader);\n        Int128Field = BSATN.Int128FieldRW.Read(reader);\n        I128Field = BSATN.I128FieldRW.Read(reader);\n        I256Field = BSATN.I256FieldRW.Read(reader);\n        BoolField = BSATN.BoolFieldRW.Read(reader);\n        FloatField = BSATN.FloatFieldRW.Read(reader);\n        DoubleField = BSATN.DoubleFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n        IdentityField = BSATN.IdentityFieldRW.Read(reader);\n        ConnectionIdField = BSATN.ConnectionIdFieldRW.Read(reader);\n        CustomStructField = BSATN.CustomStructFieldRW.Read(reader);\n        CustomClassField = BSATN.CustomClassFieldRW.Read(reader);\n        CustomEnumField = BSATN.CustomEnumFieldRW.Read(reader);\n        CustomTaggedEnumField = BSATN.CustomTaggedEnumFieldRW.Read(reader);\n        ListField = BSATN.ListFieldRW.Read(reader);\n        NullableValueField = BSATN.NullableValueFieldRW.Read(reader);\n        NullableReferenceField = BSATN.NullableReferenceFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IdRW.Write(writer, Id);\n        BSATN.ByteFieldRW.Write(writer, ByteField);\n        BSATN.UshortFieldRW.Write(writer, UshortField);\n        BSATN.UintFieldRW.Write(writer, UintField);\n        BSATN.UlongFieldRW.Write(writer, UlongField);\n        BSATN.UInt128FieldRW.Write(writer, UInt128Field);\n        BSATN.U128FieldRW.Write(writer, U128Field);\n        BSATN.U256FieldRW.Write(writer, U256Field);\n        BSATN.SbyteFieldRW.Write(writer, SbyteField);\n        BSATN.ShortFieldRW.Write(writer, ShortField);\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.LongFieldRW.Write(writer, LongField);\n        BSATN.Int128FieldRW.Write(writer, Int128Field);\n        BSATN.I128FieldRW.Write(writer, I128Field);\n        BSATN.I256FieldRW.Write(writer, I256Field);\n        BSATN.BoolFieldRW.Write(writer, BoolField);\n        BSATN.FloatFieldRW.Write(writer, FloatField);\n        BSATN.DoubleFieldRW.Write(writer, DoubleField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n        BSATN.IdentityFieldRW.Write(writer, IdentityField);\n        BSATN.ConnectionIdFieldRW.Write(writer, ConnectionIdField);\n        BSATN.CustomStructFieldRW.Write(writer, CustomStructField);\n        BSATN.CustomClassFieldRW.Write(writer, CustomClassField);\n        BSATN.CustomEnumFieldRW.Write(writer, CustomEnumField);\n        BSATN.CustomTaggedEnumFieldRW.Write(writer, CustomTaggedEnumField);\n        BSATN.ListFieldRW.Write(writer, ListField);\n        BSATN.NullableValueFieldRW.Write(writer, NullableValueField);\n        BSATN.NullableReferenceFieldRW.Write(writer, NullableReferenceField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"PublicTable {{ Id = {SpacetimeDB.BSATN.StringUtil.GenericToString(Id)}, ByteField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ByteField)}, UshortField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UshortField)}, UintField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UintField)}, UlongField = {SpacetimeDB.BSATN.StringUtil.GenericToString(UlongField)}, UInt128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(UInt128Field)}, U128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(U128Field)}, U256Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(U256Field)}, SbyteField = {SpacetimeDB.BSATN.StringUtil.GenericToString(SbyteField)}, ShortField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ShortField)}, IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, LongField = {SpacetimeDB.BSATN.StringUtil.GenericToString(LongField)}, Int128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(Int128Field)}, I128Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(I128Field)}, I256Field = {SpacetimeDB.BSATN.StringUtil.GenericToString(I256Field)}, BoolField = {SpacetimeDB.BSATN.StringUtil.GenericToString(BoolField)}, FloatField = {SpacetimeDB.BSATN.StringUtil.GenericToString(FloatField)}, DoubleField = {SpacetimeDB.BSATN.StringUtil.GenericToString(DoubleField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)}, IdentityField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IdentityField)}, ConnectionIdField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ConnectionIdField)}, CustomStructField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomStructField)}, CustomClassField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomClassField)}, CustomEnumField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomEnumField)}, CustomTaggedEnumField = {SpacetimeDB.BSATN.StringUtil.GenericToString(CustomTaggedEnumField)}, ListField = {SpacetimeDB.BSATN.StringUtil.GenericToString(ListField)}, NullableValueField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableValueField)}, NullableReferenceField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableReferenceField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<PublicTable>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IdRW = new();\n        internal static readonly SpacetimeDB.BSATN.U8 ByteFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U16 UshortFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 UintFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U64 UlongFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U128 UInt128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U128Stdb U128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.U256 U256FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I8 SbyteFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I16 ShortFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I64 LongFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I128 Int128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I128Stdb I128FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.I256 I256FieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.Bool BoolFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.F32 FloatFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.F64 DoubleFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n        internal static readonly SpacetimeDB.Identity.BSATN IdentityFieldRW = new();\n        internal static readonly SpacetimeDB.ConnectionId.BSATN ConnectionIdFieldRW = new();\n        internal static readonly CustomStruct.BSATN CustomStructFieldRW = new();\n        internal static readonly CustomClass.BSATN CustomClassFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.Enum<CustomEnum> CustomEnumFieldRW = new();\n        internal static readonly CustomTaggedEnum.BSATN CustomTaggedEnumFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.List<int, SpacetimeDB.BSATN.I32> ListFieldRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableValueFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableReferenceFieldRW = new();\n\n        public PublicTable Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new PublicTable();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, PublicTable value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<PublicTable>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"Id\", IdRW.GetAlgebraicType(registrar)),\n                    new(\"ByteField\", ByteFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UshortField\", UshortFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UintField\", UintFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UlongField\", UlongFieldRW.GetAlgebraicType(registrar)),\n                    new(\"UInt128Field\", UInt128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"U128Field\", U128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"U256Field\", U256FieldRW.GetAlgebraicType(registrar)),\n                    new(\"SbyteField\", SbyteFieldRW.GetAlgebraicType(registrar)),\n                    new(\"ShortField\", ShortFieldRW.GetAlgebraicType(registrar)),\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"LongField\", LongFieldRW.GetAlgebraicType(registrar)),\n                    new(\"Int128Field\", Int128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"I128Field\", I128FieldRW.GetAlgebraicType(registrar)),\n                    new(\"I256Field\", I256FieldRW.GetAlgebraicType(registrar)),\n                    new(\"BoolField\", BoolFieldRW.GetAlgebraicType(registrar)),\n                    new(\"FloatField\", FloatFieldRW.GetAlgebraicType(registrar)),\n                    new(\"DoubleField\", DoubleFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar)),\n                    new(\"IdentityField\", IdentityFieldRW.GetAlgebraicType(registrar)),\n                    new(\"ConnectionIdField\", ConnectionIdFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomStructField\", CustomStructFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomClassField\", CustomClassFieldRW.GetAlgebraicType(registrar)),\n                    new(\"CustomEnumField\", CustomEnumFieldRW.GetAlgebraicType(registrar)),\n                    new(\n                        \"CustomTaggedEnumField\",\n                        CustomTaggedEnumFieldRW.GetAlgebraicType(registrar)\n                    ),\n                    new(\"ListField\", ListFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableValueField\", NullableValueFieldRW.GetAlgebraicType(registrar)),\n                    new(\n                        \"NullableReferenceField\",\n                        NullableReferenceFieldRW.GetAlgebraicType(registrar)\n                    )\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<PublicTable>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashId = Id.GetHashCode();\n        var ___hashByteField = ByteField.GetHashCode();\n        var ___hashUshortField = UshortField.GetHashCode();\n        var ___hashUintField = UintField.GetHashCode();\n        var ___hashUlongField = UlongField.GetHashCode();\n        var ___hashUInt128Field = UInt128Field.GetHashCode();\n        var ___hashU128Field = U128Field.GetHashCode();\n        var ___hashU256Field = U256Field.GetHashCode();\n        var ___hashSbyteField = SbyteField.GetHashCode();\n        var ___hashShortField = ShortField.GetHashCode();\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashLongField = LongField.GetHashCode();\n        var ___hashInt128Field = Int128Field.GetHashCode();\n        var ___hashI128Field = I128Field.GetHashCode();\n        var ___hashI256Field = I256Field.GetHashCode();\n        var ___hashBoolField = BoolField.GetHashCode();\n        var ___hashFloatField = FloatField.GetHashCode();\n        var ___hashDoubleField = DoubleField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        var ___hashIdentityField = IdentityField.GetHashCode();\n        var ___hashConnectionIdField = ConnectionIdField.GetHashCode();\n        var ___hashCustomStructField = CustomStructField.GetHashCode();\n        var ___hashCustomClassField = CustomClassField == null ? 0 : CustomClassField.GetHashCode();\n        var ___hashCustomEnumField = CustomEnumField.GetHashCode();\n        var ___hashCustomTaggedEnumField =\n            CustomTaggedEnumField == null ? 0 : CustomTaggedEnumField.GetHashCode();\n        var ___hashListField = 0;\n        if (ListField != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < ListField.Count; ___i0++)\n            {\n                var ___tmp0 = ListField[___i0];\n                var ___out1 = ___tmp0.GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashListField = ___hc0.ToHashCode();\n        }\n        var ___hashNullableValueField = NullableValueField.GetHashCode();\n        var ___hashNullableReferenceField =\n            NullableReferenceField == null ? 0 : NullableReferenceField.GetHashCode();\n        return ___hashId\n            ^ ___hashByteField\n            ^ ___hashUshortField\n            ^ ___hashUintField\n            ^ ___hashUlongField\n            ^ ___hashUInt128Field\n            ^ ___hashU128Field\n            ^ ___hashU256Field\n            ^ ___hashSbyteField\n            ^ ___hashShortField\n            ^ ___hashIntField\n            ^ ___hashLongField\n            ^ ___hashInt128Field\n            ^ ___hashI128Field\n            ^ ___hashI256Field\n            ^ ___hashBoolField\n            ^ ___hashFloatField\n            ^ ___hashDoubleField\n            ^ ___hashStringField\n            ^ ___hashIdentityField\n            ^ ___hashConnectionIdField\n            ^ ___hashCustomStructField\n            ^ ___hashCustomClassField\n            ^ ___hashCustomEnumField\n            ^ ___hashCustomTaggedEnumField\n            ^ ___hashListField\n            ^ ___hashNullableValueField\n            ^ ___hashNullableReferenceField;\n    }\n\n#nullable enable\n    public bool Equals(PublicTable that)\n    {\n        var ___eqId = this.Id.Equals(that.Id);\n        var ___eqByteField = this.ByteField.Equals(that.ByteField);\n        var ___eqUshortField = this.UshortField.Equals(that.UshortField);\n        var ___eqUintField = this.UintField.Equals(that.UintField);\n        var ___eqUlongField = this.UlongField.Equals(that.UlongField);\n        var ___eqUInt128Field = this.UInt128Field.Equals(that.UInt128Field);\n        var ___eqU128Field = this.U128Field.Equals(that.U128Field);\n        var ___eqU256Field = this.U256Field.Equals(that.U256Field);\n        var ___eqSbyteField = this.SbyteField.Equals(that.SbyteField);\n        var ___eqShortField = this.ShortField.Equals(that.ShortField);\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqLongField = this.LongField.Equals(that.LongField);\n        var ___eqInt128Field = this.Int128Field.Equals(that.Int128Field);\n        var ___eqI128Field = this.I128Field.Equals(that.I128Field);\n        var ___eqI256Field = this.I256Field.Equals(that.I256Field);\n        var ___eqBoolField = this.BoolField.Equals(that.BoolField);\n        var ___eqFloatField = this.FloatField.Equals(that.FloatField);\n        var ___eqDoubleField = this.DoubleField.Equals(that.DoubleField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        var ___eqIdentityField = this.IdentityField.Equals(that.IdentityField);\n        var ___eqConnectionIdField = this.ConnectionIdField.Equals(that.ConnectionIdField);\n        var ___eqCustomStructField = this.CustomStructField.Equals(that.CustomStructField);\n        var ___eqCustomClassField =\n            this.CustomClassField == null\n                ? that.CustomClassField == null\n                : this.CustomClassField.Equals(that.CustomClassField);\n        var ___eqCustomEnumField = this.CustomEnumField == that.CustomEnumField;\n        var ___eqCustomTaggedEnumField =\n            this.CustomTaggedEnumField == null\n                ? that.CustomTaggedEnumField == null\n                : this.CustomTaggedEnumField.Equals(that.CustomTaggedEnumField);\n        var ___eqListField = true;\n        if (this.ListField == null || that.ListField == null)\n        {\n            ___eqListField = this.ListField == that.ListField;\n        }\n        else if (this.ListField.Count != that.ListField.Count)\n        {\n            ___eqListField = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.ListField.Count; ___i0++)\n            {\n                var ___tmpA0 = this.ListField[___i0];\n                var ___tmpB0 = that.ListField[___i0];\n                var ___out1 = ___tmpA0.Equals(___tmpB0);\n                if (!___out1)\n                {\n                    ___eqListField = false;\n                    break;\n                }\n            }\n        }\n        var ___eqNullableValueField = System.Nullable.Equals(\n            this.NullableValueField,\n            that.NullableValueField\n        );\n        var ___eqNullableReferenceField =\n            this.NullableReferenceField == null\n                ? that.NullableReferenceField == null\n                : this.NullableReferenceField.Equals(that.NullableReferenceField);\n        return ___eqId\n            && ___eqByteField\n            && ___eqUshortField\n            && ___eqUintField\n            && ___eqUlongField\n            && ___eqUInt128Field\n            && ___eqU128Field\n            && ___eqU256Field\n            && ___eqSbyteField\n            && ___eqShortField\n            && ___eqIntField\n            && ___eqLongField\n            && ___eqInt128Field\n            && ___eqI128Field\n            && ___eqI256Field\n            && ___eqBoolField\n            && ___eqFloatField\n            && ___eqDoubleField\n            && ___eqStringField\n            && ___eqIdentityField\n            && ___eqConnectionIdField\n            && ___eqCustomStructField\n            && ___eqCustomClassField\n            && ___eqCustomEnumField\n            && ___eqCustomTaggedEnumField\n            && ___eqListField\n            && ___eqNullableValueField\n            && ___eqNullableReferenceField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as PublicTable?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(PublicTable this_, PublicTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(PublicTable this_, PublicTable that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // PublicTable\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Reducers.InsertData.verified.cs",
    "content": "﻿//HintName: Reducers.InsertData.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateInsertData(PublicTable data)\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new PublicTable.BSATN().Write(writer, data);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(InsertData),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Reducers.ScheduleImmediate.verified.cs",
    "content": "﻿//HintName: Reducers.ScheduleImmediate.cs\n// <auto-generated />\n#nullable enable\n\npartial class Reducers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateScheduleImmediate(PublicTable data)\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new PublicTable.BSATN().Write(writer, data);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(ScheduleImmediate),\n            stream\n        );\n    }\n} // Reducers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#RegressionMultipleUniqueIndexesHadSameName.verified.cs",
    "content": "﻿//HintName: RegressionMultipleUniqueIndexesHadSameName.cs\n// <auto-generated />\n#nullable enable\n\npartial struct RegressionMultipleUniqueIndexesHadSameName\n    : System.IEquatable<RegressionMultipleUniqueIndexesHadSameName>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Unique1 = BSATN.Unique1RW.Read(reader);\n        Unique2 = BSATN.Unique2RW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.Unique1RW.Write(writer, Unique1);\n        BSATN.Unique2RW.Write(writer, Unique2);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"RegressionMultipleUniqueIndexesHadSameName {{ Unique1 = {SpacetimeDB.BSATN.StringUtil.GenericToString(Unique1)}, Unique2 = {SpacetimeDB.BSATN.StringUtil.GenericToString(Unique2)} }}\";\n\n    public readonly partial struct BSATN\n        : SpacetimeDB.BSATN.IReadWrite<RegressionMultipleUniqueIndexesHadSameName>\n    {\n        internal static readonly SpacetimeDB.BSATN.U32 Unique1RW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 Unique2RW = new();\n\n        public RegressionMultipleUniqueIndexesHadSameName Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new RegressionMultipleUniqueIndexesHadSameName();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(\n            System.IO.BinaryWriter writer,\n            RegressionMultipleUniqueIndexesHadSameName value\n        )\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<RegressionMultipleUniqueIndexesHadSameName>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"Unique1\", Unique1RW.GetAlgebraicType(registrar)),\n                        new(\"Unique2\", Unique2RW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<RegressionMultipleUniqueIndexesHadSameName>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashUnique1 = Unique1.GetHashCode();\n        var ___hashUnique2 = Unique2.GetHashCode();\n        return ___hashUnique1 ^ ___hashUnique2;\n    }\n\n#nullable enable\n    public bool Equals(RegressionMultipleUniqueIndexesHadSameName that)\n    {\n        var ___eqUnique1 = this.Unique1.Equals(that.Unique1);\n        var ___eqUnique2 = this.Unique2.Equals(that.Unique2);\n        return ___eqUnique1 && ___eqUnique2;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as RegressionMultipleUniqueIndexesHadSameName?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(\n        RegressionMultipleUniqueIndexesHadSameName this_,\n        RegressionMultipleUniqueIndexesHadSameName that\n    )\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(\n        RegressionMultipleUniqueIndexesHadSameName this_,\n        RegressionMultipleUniqueIndexesHadSameName that\n    )\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // RegressionMultipleUniqueIndexesHadSameName\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Test.NestingNamespaces.AndClasses.InsertData2.verified.cs",
    "content": "﻿//HintName: Test.NestingNamespaces.AndClasses.InsertData2.cs\n// <auto-generated />\n#nullable enable\n\nnamespace Test.NestingNamespaces\n{\n    partial class AndClasses\n    {\n        [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n        public static void VolatileNonatomicScheduleImmediateInsertData2(PublicTable data)\n        {\n            using var stream = new MemoryStream();\n            using var writer = new BinaryWriter(stream);\n            new PublicTable.BSATN().Write(writer, data);\n            SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n                nameof(InsertData2),\n                stream\n            );\n        }\n    } // AndClasses\n} // namespace\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.Init.verified.cs",
    "content": "﻿//HintName: Timers.Init.cs\n// <auto-generated />\n#nullable enable\n\npartial class Timers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateInit()\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(nameof(Init), stream);\n    }\n} // Timers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs",
    "content": "﻿//HintName: Timers.SendMessageTimer.cs\n// <auto-generated />\n#nullable enable\n\npartial class Timers\n{\n    partial struct SendMessageTimer\n        : System.IEquatable<SendMessageTimer>,\n            SpacetimeDB.BSATN.IStructuralReadWrite\n    {\n        public void ReadFields(System.IO.BinaryReader reader)\n        {\n            ScheduledId = BSATN.ScheduledIdRW.Read(reader);\n            ScheduledAt = BSATN.ScheduledAtRW.Read(reader);\n            Text = BSATN.TextRW.Read(reader);\n        }\n\n        public void WriteFields(System.IO.BinaryWriter writer)\n        {\n            BSATN.ScheduledIdRW.Write(writer, ScheduledId);\n            BSATN.ScheduledAtRW.Write(writer, ScheduledAt);\n            BSATN.TextRW.Write(writer, Text);\n        }\n\n        object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n        {\n            return new BSATN();\n        }\n\n        public override string ToString() =>\n            $\"SendMessageTimer {{ ScheduledId = {SpacetimeDB.BSATN.StringUtil.GenericToString(ScheduledId)}, ScheduledAt = {SpacetimeDB.BSATN.StringUtil.GenericToString(ScheduledAt)}, Text = {SpacetimeDB.BSATN.StringUtil.GenericToString(Text)} }}\";\n\n        public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<Timers.SendMessageTimer>\n        {\n            internal static readonly SpacetimeDB.BSATN.U64 ScheduledIdRW = new();\n            internal static readonly SpacetimeDB.ScheduleAt.BSATN ScheduledAtRW = new();\n            internal static readonly SpacetimeDB.BSATN.String TextRW = new();\n\n            public Timers.SendMessageTimer Read(System.IO.BinaryReader reader)\n            {\n                var ___result = new Timers.SendMessageTimer();\n                ___result.ReadFields(reader);\n                return ___result;\n            }\n\n            public void Write(System.IO.BinaryWriter writer, Timers.SendMessageTimer value)\n            {\n                value.WriteFields(writer);\n            }\n\n            public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n                SpacetimeDB.BSATN.ITypeRegistrar registrar\n            ) =>\n                registrar.RegisterType<Timers.SendMessageTimer>(\n                    _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                        new SpacetimeDB.BSATN.AggregateElement[]\n                        {\n                            new(\"ScheduledId\", ScheduledIdRW.GetAlgebraicType(registrar)),\n                            new(\"ScheduledAt\", ScheduledAtRW.GetAlgebraicType(registrar)),\n                            new(\"Text\", TextRW.GetAlgebraicType(registrar))\n                        }\n                    )\n                );\n\n            SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<Timers.SendMessageTimer>.GetAlgebraicType(\n                SpacetimeDB.BSATN.ITypeRegistrar registrar\n            ) => GetAlgebraicType(registrar);\n        }\n\n        public override int GetHashCode()\n        {\n            var ___hashScheduledId = ScheduledId.GetHashCode();\n            var ___hashScheduledAt = ScheduledAt == null ? 0 : ScheduledAt.GetHashCode();\n            var ___hashText = Text == null ? 0 : Text.GetHashCode();\n            return ___hashScheduledId ^ ___hashScheduledAt ^ ___hashText;\n        }\n\n#nullable enable\n        public bool Equals(Timers.SendMessageTimer that)\n        {\n            var ___eqScheduledId = this.ScheduledId.Equals(that.ScheduledId);\n            var ___eqScheduledAt =\n                this.ScheduledAt == null\n                    ? that.ScheduledAt == null\n                    : this.ScheduledAt.Equals(that.ScheduledAt);\n            var ___eqText = this.Text == null ? that.Text == null : this.Text.Equals(that.Text);\n            return ___eqScheduledId && ___eqScheduledAt && ___eqText;\n        }\n\n        public override bool Equals(object? that)\n        {\n            if (that == null)\n            {\n                return false;\n            }\n            var that_ = that as Timers.SendMessageTimer?;\n            if (((object?)that_) == null)\n            {\n                return false;\n            }\n            return Equals(that_);\n        }\n\n        public static bool operator ==(Timers.SendMessageTimer this_, Timers.SendMessageTimer that)\n        {\n            if (((object?)this_) == null || ((object?)that) == null)\n            {\n                return object.Equals(this_, that);\n            }\n            return this_.Equals(that);\n        }\n\n        public static bool operator !=(Timers.SendMessageTimer this_, Timers.SendMessageTimer that)\n        {\n            if (((object?)this_) == null || ((object?)that) == null)\n            {\n                return !object.Equals(this_, that);\n            }\n            return !this_.Equals(that);\n        }\n#nullable restore\n    } // SendMessageTimer\n} // Timers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendScheduledMessage.verified.cs",
    "content": "﻿//HintName: Timers.SendScheduledMessage.cs\n// <auto-generated />\n#nullable enable\n\npartial class Timers\n{\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    public static void VolatileNonatomicScheduleImmediateSendScheduledMessage(\n        Timers.SendMessageTimer arg\n    )\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        new Timers.SendMessageTimer.BSATN().Write(writer, arg);\n        SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(\n            nameof(SendScheduledMessage),\n            stream\n        );\n    }\n} // Timers\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#ContainsNestedLists.verified.cs",
    "content": "﻿//HintName: ContainsNestedLists.cs\n// <auto-generated />\n#nullable enable\n\npartial class ContainsNestedLists\n    : System.IEquatable<ContainsNestedLists>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntList = BSATN.IntListRW.Read(reader);\n        StringList = BSATN.StringListRW.Read(reader);\n        IntArray = BSATN.IntArrayRW.Read(reader);\n        StringArray = BSATN.StringArrayRW.Read(reader);\n        IntArrayArrayList = BSATN.IntArrayArrayListRW.Read(reader);\n        IntListListArray = BSATN.IntListListArrayRW.Read(reader);\n        StringArrayArrayList = BSATN.StringArrayArrayListRW.Read(reader);\n        StringListListArray = BSATN.StringListListArrayRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntListRW.Write(writer, IntList);\n        BSATN.StringListRW.Write(writer, StringList);\n        BSATN.IntArrayRW.Write(writer, IntArray);\n        BSATN.StringArrayRW.Write(writer, StringArray);\n        BSATN.IntArrayArrayListRW.Write(writer, IntArrayArrayList);\n        BSATN.IntListListArrayRW.Write(writer, IntListListArray);\n        BSATN.StringArrayArrayListRW.Write(writer, StringArrayArrayList);\n        BSATN.StringListListArrayRW.Write(writer, StringListListArray);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"ContainsNestedLists {{ IntList = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntList)}, StringList = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringList)}, IntArray = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntArray)}, StringArray = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringArray)}, IntArrayArrayList = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntArrayArrayList)}, IntListListArray = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntListListArray)}, StringArrayArrayList = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringArrayArrayList)}, StringListListArray = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringListListArray)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<ContainsNestedLists>\n    {\n        internal static readonly SpacetimeDB.BSATN.List<int, SpacetimeDB.BSATN.I32> IntListRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.List<\n            string,\n            SpacetimeDB.BSATN.String\n        > StringListRW = new();\n        internal static readonly SpacetimeDB.BSATN.Array<int, SpacetimeDB.BSATN.I32> IntArrayRW =\n            new();\n        internal static readonly SpacetimeDB.BSATN.Array<\n            string,\n            SpacetimeDB.BSATN.String\n        > StringArrayRW = new();\n        internal static readonly SpacetimeDB.BSATN.List<\n            int[][],\n            SpacetimeDB.BSATN.Array<int[], SpacetimeDB.BSATN.Array<int, SpacetimeDB.BSATN.I32>>\n        > IntArrayArrayListRW = new();\n        internal static readonly SpacetimeDB.BSATN.Array<\n            System.Collections.Generic.List<System.Collections.Generic.List<int>>,\n            SpacetimeDB.BSATN.List<\n                System.Collections.Generic.List<int>,\n                SpacetimeDB.BSATN.List<int, SpacetimeDB.BSATN.I32>\n            >\n        > IntListListArrayRW = new();\n        internal static readonly SpacetimeDB.BSATN.List<\n            string[][],\n            SpacetimeDB.BSATN.Array<\n                string[],\n                SpacetimeDB.BSATN.Array<string, SpacetimeDB.BSATN.String>\n            >\n        > StringArrayArrayListRW = new();\n        internal static readonly SpacetimeDB.BSATN.Array<\n            System.Collections.Generic.List<System.Collections.Generic.List<string>>,\n            SpacetimeDB.BSATN.List<\n                System.Collections.Generic.List<string>,\n                SpacetimeDB.BSATN.List<string, SpacetimeDB.BSATN.String>\n            >\n        > StringListListArrayRW = new();\n\n        public ContainsNestedLists Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new ContainsNestedLists();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, ContainsNestedLists value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<ContainsNestedLists>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"IntList\", IntListRW.GetAlgebraicType(registrar)),\n                        new(\"StringList\", StringListRW.GetAlgebraicType(registrar)),\n                        new(\"IntArray\", IntArrayRW.GetAlgebraicType(registrar)),\n                        new(\"StringArray\", StringArrayRW.GetAlgebraicType(registrar)),\n                        new(\"IntArrayArrayList\", IntArrayArrayListRW.GetAlgebraicType(registrar)),\n                        new(\"IntListListArray\", IntListListArrayRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"StringArrayArrayList\",\n                            StringArrayArrayListRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\n                            \"StringListListArray\",\n                            StringListListArrayRW.GetAlgebraicType(registrar)\n                        )\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<ContainsNestedLists>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntList = 0;\n        if (IntList != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < IntList.Count; ___i0++)\n            {\n                var ___tmp0 = IntList[___i0];\n                var ___out1 = ___tmp0.GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashIntList = ___hc0.ToHashCode();\n        }\n        var ___hashStringList = 0;\n        if (StringList != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < StringList.Count; ___i0++)\n            {\n                var ___tmp0 = StringList[___i0];\n                var ___out1 = ___tmp0 == null ? 0 : ___tmp0.GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashStringList = ___hc0.ToHashCode();\n        }\n        var ___hashIntArray = 0;\n        if (IntArray != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < IntArray.Length; ___i0++)\n            {\n                var ___out1 = IntArray[___i0].GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashIntArray = ___hc0.ToHashCode();\n        }\n        var ___hashStringArray = 0;\n        if (StringArray != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < StringArray.Length; ___i0++)\n            {\n                var ___out1 = StringArray[___i0] == null ? 0 : StringArray[___i0].GetHashCode();\n                ___hc0.Add(___out1);\n            }\n            ___hashStringArray = ___hc0.ToHashCode();\n        }\n        var ___hashIntArrayArrayList = 0;\n        if (IntArrayArrayList != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < IntArrayArrayList.Count; ___i0++)\n            {\n                var ___tmp0 = IntArrayArrayList[___i0];\n                var ___out1 = 0;\n                if (___tmp0 != null)\n                {\n                    var ___hc1 = new System.HashCode();\n                    for (int ___i1 = 0; ___i1 < ___tmp0.Length; ___i1++)\n                    {\n                        var ___out2 = 0;\n                        if (___tmp0[___i1] != null)\n                        {\n                            var ___hc2 = new System.HashCode();\n                            for (int ___i2 = 0; ___i2 < ___tmp0[___i1].Length; ___i2++)\n                            {\n                                var ___out3 = ___tmp0[___i1][___i2].GetHashCode();\n                                ___hc2.Add(___out3);\n                            }\n                            ___out2 = ___hc2.ToHashCode();\n                        }\n                        ___hc1.Add(___out2);\n                    }\n                    ___out1 = ___hc1.ToHashCode();\n                }\n                ___hc0.Add(___out1);\n            }\n            ___hashIntArrayArrayList = ___hc0.ToHashCode();\n        }\n        var ___hashIntListListArray = 0;\n        if (IntListListArray != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < IntListListArray.Length; ___i0++)\n            {\n                var ___out1 = 0;\n                if (IntListListArray[___i0] != null)\n                {\n                    var ___hc1 = new System.HashCode();\n                    for (int ___i1 = 0; ___i1 < IntListListArray[___i0].Count; ___i1++)\n                    {\n                        var ___tmp1 = IntListListArray[___i0][___i1];\n                        var ___out2 = 0;\n                        if (___tmp1 != null)\n                        {\n                            var ___hc2 = new System.HashCode();\n                            for (int ___i2 = 0; ___i2 < ___tmp1.Count; ___i2++)\n                            {\n                                var ___tmp2 = ___tmp1[___i2];\n                                var ___out3 = ___tmp2.GetHashCode();\n                                ___hc2.Add(___out3);\n                            }\n                            ___out2 = ___hc2.ToHashCode();\n                        }\n                        ___hc1.Add(___out2);\n                    }\n                    ___out1 = ___hc1.ToHashCode();\n                }\n                ___hc0.Add(___out1);\n            }\n            ___hashIntListListArray = ___hc0.ToHashCode();\n        }\n        var ___hashStringArrayArrayList = 0;\n        if (StringArrayArrayList != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < StringArrayArrayList.Count; ___i0++)\n            {\n                var ___tmp0 = StringArrayArrayList[___i0];\n                var ___out1 = 0;\n                if (___tmp0 != null)\n                {\n                    var ___hc1 = new System.HashCode();\n                    for (int ___i1 = 0; ___i1 < ___tmp0.Length; ___i1++)\n                    {\n                        var ___out2 = 0;\n                        if (___tmp0[___i1] != null)\n                        {\n                            var ___hc2 = new System.HashCode();\n                            for (int ___i2 = 0; ___i2 < ___tmp0[___i1].Length; ___i2++)\n                            {\n                                var ___out3 =\n                                    ___tmp0[___i1][___i2] == null\n                                        ? 0\n                                        : ___tmp0[___i1][___i2].GetHashCode();\n                                ___hc2.Add(___out3);\n                            }\n                            ___out2 = ___hc2.ToHashCode();\n                        }\n                        ___hc1.Add(___out2);\n                    }\n                    ___out1 = ___hc1.ToHashCode();\n                }\n                ___hc0.Add(___out1);\n            }\n            ___hashStringArrayArrayList = ___hc0.ToHashCode();\n        }\n        var ___hashStringListListArray = 0;\n        if (StringListListArray != null)\n        {\n            var ___hc0 = new System.HashCode();\n            for (int ___i0 = 0; ___i0 < StringListListArray.Length; ___i0++)\n            {\n                var ___out1 = 0;\n                if (StringListListArray[___i0] != null)\n                {\n                    var ___hc1 = new System.HashCode();\n                    for (int ___i1 = 0; ___i1 < StringListListArray[___i0].Count; ___i1++)\n                    {\n                        var ___tmp1 = StringListListArray[___i0][___i1];\n                        var ___out2 = 0;\n                        if (___tmp1 != null)\n                        {\n                            var ___hc2 = new System.HashCode();\n                            for (int ___i2 = 0; ___i2 < ___tmp1.Count; ___i2++)\n                            {\n                                var ___tmp2 = ___tmp1[___i2];\n                                var ___out3 = ___tmp2 == null ? 0 : ___tmp2.GetHashCode();\n                                ___hc2.Add(___out3);\n                            }\n                            ___out2 = ___hc2.ToHashCode();\n                        }\n                        ___hc1.Add(___out2);\n                    }\n                    ___out1 = ___hc1.ToHashCode();\n                }\n                ___hc0.Add(___out1);\n            }\n            ___hashStringListListArray = ___hc0.ToHashCode();\n        }\n        return ___hashIntList\n            ^ ___hashStringList\n            ^ ___hashIntArray\n            ^ ___hashStringArray\n            ^ ___hashIntArrayArrayList\n            ^ ___hashIntListListArray\n            ^ ___hashStringArrayArrayList\n            ^ ___hashStringListListArray;\n    }\n\n#nullable enable\n    public bool Equals(ContainsNestedLists? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        var ___eqIntList = true;\n        if (this.IntList == null || that.IntList == null)\n        {\n            ___eqIntList = this.IntList == that.IntList;\n        }\n        else if (this.IntList.Count != that.IntList.Count)\n        {\n            ___eqIntList = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.IntList.Count; ___i0++)\n            {\n                var ___tmpA0 = this.IntList[___i0];\n                var ___tmpB0 = that.IntList[___i0];\n                var ___out1 = ___tmpA0.Equals(___tmpB0);\n                if (!___out1)\n                {\n                    ___eqIntList = false;\n                    break;\n                }\n            }\n        }\n        var ___eqStringList = true;\n        if (this.StringList == null || that.StringList == null)\n        {\n            ___eqStringList = this.StringList == that.StringList;\n        }\n        else if (this.StringList.Count != that.StringList.Count)\n        {\n            ___eqStringList = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.StringList.Count; ___i0++)\n            {\n                var ___tmpA0 = this.StringList[___i0];\n                var ___tmpB0 = that.StringList[___i0];\n                var ___out1 = ___tmpA0 == null ? ___tmpB0 == null : ___tmpA0.Equals(___tmpB0);\n                if (!___out1)\n                {\n                    ___eqStringList = false;\n                    break;\n                }\n            }\n        }\n        var ___eqIntArray = true;\n        if (this.IntArray == null || that.IntArray == null)\n        {\n            ___eqIntArray = this.IntArray == that.IntArray;\n        }\n        else if (this.IntArray.Length != that.IntArray.Length)\n        {\n            ___eqIntArray = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.IntArray.Length; ___i0++)\n            {\n                var ___out1 = this.IntArray[___i0].Equals(that.IntArray[___i0]);\n                if (!___out1)\n                {\n                    ___eqIntArray = false;\n                    break;\n                }\n            }\n        }\n        var ___eqStringArray = true;\n        if (this.StringArray == null || that.StringArray == null)\n        {\n            ___eqStringArray = this.StringArray == that.StringArray;\n        }\n        else if (this.StringArray.Length != that.StringArray.Length)\n        {\n            ___eqStringArray = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.StringArray.Length; ___i0++)\n            {\n                var ___out1 =\n                    this.StringArray[___i0] == null\n                        ? that.StringArray[___i0] == null\n                        : this.StringArray[___i0].Equals(that.StringArray[___i0]);\n                if (!___out1)\n                {\n                    ___eqStringArray = false;\n                    break;\n                }\n            }\n        }\n        var ___eqIntArrayArrayList = true;\n        if (this.IntArrayArrayList == null || that.IntArrayArrayList == null)\n        {\n            ___eqIntArrayArrayList = this.IntArrayArrayList == that.IntArrayArrayList;\n        }\n        else if (this.IntArrayArrayList.Count != that.IntArrayArrayList.Count)\n        {\n            ___eqIntArrayArrayList = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.IntArrayArrayList.Count; ___i0++)\n            {\n                var ___tmpA0 = this.IntArrayArrayList[___i0];\n                var ___tmpB0 = that.IntArrayArrayList[___i0];\n                var ___out1 = true;\n                if (___tmpA0 == null || ___tmpB0 == null)\n                {\n                    ___out1 = ___tmpA0 == ___tmpB0;\n                }\n                else if (___tmpA0.Length != ___tmpB0.Length)\n                {\n                    ___out1 = false;\n                }\n                else\n                {\n                    for (int ___i1 = 0; ___i1 < ___tmpA0.Length; ___i1++)\n                    {\n                        var ___out2 = true;\n                        if (___tmpA0[___i1] == null || ___tmpB0[___i1] == null)\n                        {\n                            ___out2 = ___tmpA0[___i1] == ___tmpB0[___i1];\n                        }\n                        else if (___tmpA0[___i1].Length != ___tmpB0[___i1].Length)\n                        {\n                            ___out2 = false;\n                        }\n                        else\n                        {\n                            for (int ___i2 = 0; ___i2 < ___tmpA0[___i1].Length; ___i2++)\n                            {\n                                var ___out3 = ___tmpA0[___i1][___i2].Equals(___tmpB0[___i1][___i2]);\n                                if (!___out3)\n                                {\n                                    ___out2 = false;\n                                    break;\n                                }\n                            }\n                        }\n                        if (!___out2)\n                        {\n                            ___out1 = false;\n                            break;\n                        }\n                    }\n                }\n                if (!___out1)\n                {\n                    ___eqIntArrayArrayList = false;\n                    break;\n                }\n            }\n        }\n        var ___eqIntListListArray = true;\n        if (this.IntListListArray == null || that.IntListListArray == null)\n        {\n            ___eqIntListListArray = this.IntListListArray == that.IntListListArray;\n        }\n        else if (this.IntListListArray.Length != that.IntListListArray.Length)\n        {\n            ___eqIntListListArray = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.IntListListArray.Length; ___i0++)\n            {\n                var ___out1 = true;\n                if (this.IntListListArray[___i0] == null || that.IntListListArray[___i0] == null)\n                {\n                    ___out1 = this.IntListListArray[___i0] == that.IntListListArray[___i0];\n                }\n                else if (this.IntListListArray[___i0].Count != that.IntListListArray[___i0].Count)\n                {\n                    ___out1 = false;\n                }\n                else\n                {\n                    for (int ___i1 = 0; ___i1 < this.IntListListArray[___i0].Count; ___i1++)\n                    {\n                        var ___tmpA1 = this.IntListListArray[___i0][___i1];\n                        var ___tmpB1 = that.IntListListArray[___i0][___i1];\n                        var ___out2 = true;\n                        if (___tmpA1 == null || ___tmpB1 == null)\n                        {\n                            ___out2 = ___tmpA1 == ___tmpB1;\n                        }\n                        else if (___tmpA1.Count != ___tmpB1.Count)\n                        {\n                            ___out2 = false;\n                        }\n                        else\n                        {\n                            for (int ___i2 = 0; ___i2 < ___tmpA1.Count; ___i2++)\n                            {\n                                var ___tmpA2 = ___tmpA1[___i2];\n                                var ___tmpB2 = ___tmpB1[___i2];\n                                var ___out3 = ___tmpA2.Equals(___tmpB2);\n                                if (!___out3)\n                                {\n                                    ___out2 = false;\n                                    break;\n                                }\n                            }\n                        }\n                        if (!___out2)\n                        {\n                            ___out1 = false;\n                            break;\n                        }\n                    }\n                }\n                if (!___out1)\n                {\n                    ___eqIntListListArray = false;\n                    break;\n                }\n            }\n        }\n        var ___eqStringArrayArrayList = true;\n        if (this.StringArrayArrayList == null || that.StringArrayArrayList == null)\n        {\n            ___eqStringArrayArrayList = this.StringArrayArrayList == that.StringArrayArrayList;\n        }\n        else if (this.StringArrayArrayList.Count != that.StringArrayArrayList.Count)\n        {\n            ___eqStringArrayArrayList = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.StringArrayArrayList.Count; ___i0++)\n            {\n                var ___tmpA0 = this.StringArrayArrayList[___i0];\n                var ___tmpB0 = that.StringArrayArrayList[___i0];\n                var ___out1 = true;\n                if (___tmpA0 == null || ___tmpB0 == null)\n                {\n                    ___out1 = ___tmpA0 == ___tmpB0;\n                }\n                else if (___tmpA0.Length != ___tmpB0.Length)\n                {\n                    ___out1 = false;\n                }\n                else\n                {\n                    for (int ___i1 = 0; ___i1 < ___tmpA0.Length; ___i1++)\n                    {\n                        var ___out2 = true;\n                        if (___tmpA0[___i1] == null || ___tmpB0[___i1] == null)\n                        {\n                            ___out2 = ___tmpA0[___i1] == ___tmpB0[___i1];\n                        }\n                        else if (___tmpA0[___i1].Length != ___tmpB0[___i1].Length)\n                        {\n                            ___out2 = false;\n                        }\n                        else\n                        {\n                            for (int ___i2 = 0; ___i2 < ___tmpA0[___i1].Length; ___i2++)\n                            {\n                                var ___out3 =\n                                    ___tmpA0[___i1][___i2] == null\n                                        ? ___tmpB0[___i1][___i2] == null\n                                        : ___tmpA0[___i1][___i2].Equals(___tmpB0[___i1][___i2]);\n                                if (!___out3)\n                                {\n                                    ___out2 = false;\n                                    break;\n                                }\n                            }\n                        }\n                        if (!___out2)\n                        {\n                            ___out1 = false;\n                            break;\n                        }\n                    }\n                }\n                if (!___out1)\n                {\n                    ___eqStringArrayArrayList = false;\n                    break;\n                }\n            }\n        }\n        var ___eqStringListListArray = true;\n        if (this.StringListListArray == null || that.StringListListArray == null)\n        {\n            ___eqStringListListArray = this.StringListListArray == that.StringListListArray;\n        }\n        else if (this.StringListListArray.Length != that.StringListListArray.Length)\n        {\n            ___eqStringListListArray = false;\n        }\n        else\n        {\n            for (int ___i0 = 0; ___i0 < this.StringListListArray.Length; ___i0++)\n            {\n                var ___out1 = true;\n                if (\n                    this.StringListListArray[___i0] == null\n                    || that.StringListListArray[___i0] == null\n                )\n                {\n                    ___out1 = this.StringListListArray[___i0] == that.StringListListArray[___i0];\n                }\n                else if (\n                    this.StringListListArray[___i0].Count != that.StringListListArray[___i0].Count\n                )\n                {\n                    ___out1 = false;\n                }\n                else\n                {\n                    for (int ___i1 = 0; ___i1 < this.StringListListArray[___i0].Count; ___i1++)\n                    {\n                        var ___tmpA1 = this.StringListListArray[___i0][___i1];\n                        var ___tmpB1 = that.StringListListArray[___i0][___i1];\n                        var ___out2 = true;\n                        if (___tmpA1 == null || ___tmpB1 == null)\n                        {\n                            ___out2 = ___tmpA1 == ___tmpB1;\n                        }\n                        else if (___tmpA1.Count != ___tmpB1.Count)\n                        {\n                            ___out2 = false;\n                        }\n                        else\n                        {\n                            for (int ___i2 = 0; ___i2 < ___tmpA1.Count; ___i2++)\n                            {\n                                var ___tmpA2 = ___tmpA1[___i2];\n                                var ___tmpB2 = ___tmpB1[___i2];\n                                var ___out3 =\n                                    ___tmpA2 == null ? ___tmpB2 == null : ___tmpA2.Equals(___tmpB2);\n                                if (!___out3)\n                                {\n                                    ___out2 = false;\n                                    break;\n                                }\n                            }\n                        }\n                        if (!___out2)\n                        {\n                            ___out1 = false;\n                            break;\n                        }\n                    }\n                }\n                if (!___out1)\n                {\n                    ___eqStringListListArray = false;\n                    break;\n                }\n            }\n        }\n        return ___eqIntList\n            && ___eqStringList\n            && ___eqIntArray\n            && ___eqStringArray\n            && ___eqIntArrayArrayList\n            && ___eqIntListListArray\n            && ___eqStringArrayArrayList\n            && ___eqStringListListArray;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as ContainsNestedLists;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(ContainsNestedLists? this_, ContainsNestedLists? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(ContainsNestedLists? this_, ContainsNestedLists? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // ContainsNestedLists\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#CustomClass.verified.cs",
    "content": "﻿//HintName: CustomClass.cs\n// <auto-generated />\n#nullable enable\n\npartial class CustomClass : System.IEquatable<CustomClass>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntField = BSATN.IntFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n        NullableIntField = BSATN.NullableIntFieldRW.Read(reader);\n        NullableStringField = BSATN.NullableStringFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n        BSATN.NullableIntFieldRW.Write(writer, NullableIntField);\n        BSATN.NullableStringFieldRW.Write(writer, NullableStringField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomClass {{ IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)}, NullableIntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableIntField)}, NullableStringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableStringField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomClass>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableIntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableStringFieldRW = new();\n\n        public CustomClass Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomClass();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomClass value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomClass>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableIntField\", NullableIntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableStringField\", NullableStringFieldRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomClass>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        var ___hashNullableIntField = NullableIntField.GetHashCode();\n        var ___hashNullableStringField =\n            NullableStringField == null ? 0 : NullableStringField.GetHashCode();\n        return ___hashIntField\n            ^ ___hashStringField\n            ^ ___hashNullableIntField\n            ^ ___hashNullableStringField;\n    }\n\n#nullable enable\n    public bool Equals(CustomClass? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        var ___eqNullableIntField = System.Nullable.Equals(\n            this.NullableIntField,\n            that.NullableIntField\n        );\n        var ___eqNullableStringField =\n            this.NullableStringField == null\n                ? that.NullableStringField == null\n                : this.NullableStringField.Equals(that.NullableStringField);\n        return ___eqIntField\n            && ___eqStringField\n            && ___eqNullableIntField\n            && ___eqNullableStringField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomClass;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomClass? this_, CustomClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomClass? this_, CustomClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomClass\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#CustomNestedClass.verified.cs",
    "content": "﻿//HintName: CustomNestedClass.cs\n// <auto-generated />\n#nullable enable\n\npartial class CustomNestedClass\n    : System.IEquatable<CustomNestedClass>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        NestedClass = BSATN.NestedClassRW.Read(reader);\n        NestedNullableClass = BSATN.NestedNullableClassRW.Read(reader);\n        NestedEnum = BSATN.NestedEnumRW.Read(reader);\n        NestedNullableEnum = BSATN.NestedNullableEnumRW.Read(reader);\n        NestedTaggedEnum = BSATN.NestedTaggedEnumRW.Read(reader);\n        NestedNullableTaggedEnum = BSATN.NestedNullableTaggedEnumRW.Read(reader);\n        NestedCustomRecord = BSATN.NestedCustomRecordRW.Read(reader);\n        NestedNullableCustomRecord = BSATN.NestedNullableCustomRecordRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.NestedClassRW.Write(writer, NestedClass);\n        BSATN.NestedNullableClassRW.Write(writer, NestedNullableClass);\n        BSATN.NestedEnumRW.Write(writer, NestedEnum);\n        BSATN.NestedNullableEnumRW.Write(writer, NestedNullableEnum);\n        BSATN.NestedTaggedEnumRW.Write(writer, NestedTaggedEnum);\n        BSATN.NestedNullableTaggedEnumRW.Write(writer, NestedNullableTaggedEnum);\n        BSATN.NestedCustomRecordRW.Write(writer, NestedCustomRecord);\n        BSATN.NestedNullableCustomRecordRW.Write(writer, NestedNullableCustomRecord);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomNestedClass {{ NestedClass = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedClass)}, NestedNullableClass = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedNullableClass)}, NestedEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedEnum)}, NestedNullableEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedNullableEnum)}, NestedTaggedEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedTaggedEnum)}, NestedNullableTaggedEnum = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedNullableTaggedEnum)}, NestedCustomRecord = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedCustomRecord)}, NestedNullableCustomRecord = {SpacetimeDB.BSATN.StringUtil.GenericToString(NestedNullableCustomRecord)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomNestedClass>\n    {\n        internal static readonly CustomClass.BSATN NestedClassRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            CustomClass,\n            CustomClass.BSATN\n        > NestedNullableClassRW = new();\n        internal static readonly SpacetimeDB.BSATN.Enum<CustomEnum> NestedEnumRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            CustomEnum,\n            SpacetimeDB.BSATN.Enum<CustomEnum>\n        > NestedNullableEnumRW = new();\n        internal static readonly CustomTaggedEnum.BSATN NestedTaggedEnumRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            CustomTaggedEnum,\n            CustomTaggedEnum.BSATN\n        > NestedNullableTaggedEnumRW = new();\n        internal static readonly CustomRecord.BSATN NestedCustomRecordRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            CustomRecord,\n            CustomRecord.BSATN\n        > NestedNullableCustomRecordRW = new();\n\n        public CustomNestedClass Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomNestedClass();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomNestedClass value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomNestedClass>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"NestedClass\", NestedClassRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"NestedNullableClass\",\n                            NestedNullableClassRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\"NestedEnum\", NestedEnumRW.GetAlgebraicType(registrar)),\n                        new(\"NestedNullableEnum\", NestedNullableEnumRW.GetAlgebraicType(registrar)),\n                        new(\"NestedTaggedEnum\", NestedTaggedEnumRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"NestedNullableTaggedEnum\",\n                            NestedNullableTaggedEnumRW.GetAlgebraicType(registrar)\n                        ),\n                        new(\"NestedCustomRecord\", NestedCustomRecordRW.GetAlgebraicType(registrar)),\n                        new(\n                            \"NestedNullableCustomRecord\",\n                            NestedNullableCustomRecordRW.GetAlgebraicType(registrar)\n                        )\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomNestedClass>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashNestedClass = NestedClass == null ? 0 : NestedClass.GetHashCode();\n        var ___hashNestedNullableClass =\n            NestedNullableClass == null ? 0 : NestedNullableClass.GetHashCode();\n        var ___hashNestedEnum = NestedEnum.GetHashCode();\n        var ___hashNestedNullableEnum = NestedNullableEnum.GetHashCode();\n        var ___hashNestedTaggedEnum = NestedTaggedEnum == null ? 0 : NestedTaggedEnum.GetHashCode();\n        var ___hashNestedNullableTaggedEnum =\n            NestedNullableTaggedEnum == null ? 0 : NestedNullableTaggedEnum.GetHashCode();\n        var ___hashNestedCustomRecord =\n            NestedCustomRecord == null ? 0 : NestedCustomRecord.GetHashCode();\n        var ___hashNestedNullableCustomRecord =\n            NestedNullableCustomRecord == null ? 0 : NestedNullableCustomRecord.GetHashCode();\n        return ___hashNestedClass\n            ^ ___hashNestedNullableClass\n            ^ ___hashNestedEnum\n            ^ ___hashNestedNullableEnum\n            ^ ___hashNestedTaggedEnum\n            ^ ___hashNestedNullableTaggedEnum\n            ^ ___hashNestedCustomRecord\n            ^ ___hashNestedNullableCustomRecord;\n    }\n\n#nullable enable\n    public bool Equals(CustomNestedClass? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        var ___eqNestedClass =\n            this.NestedClass == null\n                ? that.NestedClass == null\n                : this.NestedClass.Equals(that.NestedClass);\n        var ___eqNestedNullableClass =\n            this.NestedNullableClass == null\n                ? that.NestedNullableClass == null\n                : this.NestedNullableClass.Equals(that.NestedNullableClass);\n        var ___eqNestedEnum = this.NestedEnum == that.NestedEnum;\n        var ___eqNestedNullableEnum = System.Nullable.Equals(\n            this.NestedNullableEnum,\n            that.NestedNullableEnum\n        );\n        var ___eqNestedTaggedEnum =\n            this.NestedTaggedEnum == null\n                ? that.NestedTaggedEnum == null\n                : this.NestedTaggedEnum.Equals(that.NestedTaggedEnum);\n        var ___eqNestedNullableTaggedEnum =\n            this.NestedNullableTaggedEnum == null\n                ? that.NestedNullableTaggedEnum == null\n                : this.NestedNullableTaggedEnum.Equals(that.NestedNullableTaggedEnum);\n        var ___eqNestedCustomRecord =\n            this.NestedCustomRecord == null\n                ? that.NestedCustomRecord == null\n                : this.NestedCustomRecord.Equals(that.NestedCustomRecord);\n        var ___eqNestedNullableCustomRecord =\n            this.NestedNullableCustomRecord == null\n                ? that.NestedNullableCustomRecord == null\n                : this.NestedNullableCustomRecord.Equals(that.NestedNullableCustomRecord);\n        return ___eqNestedClass\n            && ___eqNestedNullableClass\n            && ___eqNestedEnum\n            && ___eqNestedNullableEnum\n            && ___eqNestedTaggedEnum\n            && ___eqNestedNullableTaggedEnum\n            && ___eqNestedCustomRecord\n            && ___eqNestedNullableCustomRecord;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomNestedClass;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomNestedClass? this_, CustomNestedClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomNestedClass? this_, CustomNestedClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomNestedClass\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#CustomRecord.verified.cs",
    "content": "﻿//HintName: CustomRecord.cs\n// <auto-generated />\n#nullable enable\n\npartial class CustomRecord : System.IEquatable<CustomRecord>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntField = BSATN.IntFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n        NullableIntField = BSATN.NullableIntFieldRW.Read(reader);\n        NullableStringField = BSATN.NullableStringFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n        BSATN.NullableIntFieldRW.Write(writer, NullableIntField);\n        BSATN.NullableStringFieldRW.Write(writer, NullableStringField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomRecord {{ IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)}, NullableIntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableIntField)}, NullableStringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableStringField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomRecord>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableIntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableStringFieldRW = new();\n\n        public CustomRecord Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomRecord();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomRecord value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomRecord>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableIntField\", NullableIntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableStringField\", NullableStringFieldRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomRecord>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        var ___hashNullableIntField = NullableIntField.GetHashCode();\n        var ___hashNullableStringField =\n            NullableStringField == null ? 0 : NullableStringField.GetHashCode();\n        return ___hashIntField\n            ^ ___hashStringField\n            ^ ___hashNullableIntField\n            ^ ___hashNullableStringField;\n    }\n\n#nullable enable\n    public bool Equals(CustomRecord? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        var ___eqNullableIntField = System.Nullable.Equals(\n            this.NullableIntField,\n            that.NullableIntField\n        );\n        var ___eqNullableStringField =\n            this.NullableStringField == null\n                ? that.NullableStringField == null\n                : this.NullableStringField.Equals(that.NullableStringField);\n        return ___eqIntField\n            && ___eqStringField\n            && ___eqNullableIntField\n            && ___eqNullableStringField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomRecord;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomRecord? this_, CustomRecord? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomRecord? this_, CustomRecord? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomRecord\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#CustomStruct.verified.cs",
    "content": "﻿//HintName: CustomStruct.cs\n// <auto-generated />\n#nullable enable\n\npartial struct CustomStruct\n    : System.IEquatable<CustomStruct>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        IntField = BSATN.IntFieldRW.Read(reader);\n        StringField = BSATN.StringFieldRW.Read(reader);\n        NullableIntField = BSATN.NullableIntFieldRW.Read(reader);\n        NullableStringField = BSATN.NullableStringFieldRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.IntFieldRW.Write(writer, IntField);\n        BSATN.StringFieldRW.Write(writer, StringField);\n        BSATN.NullableIntFieldRW.Write(writer, NullableIntField);\n        BSATN.NullableStringFieldRW.Write(writer, NullableStringField);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"CustomStruct {{ IntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(IntField)}, StringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(StringField)}, NullableIntField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableIntField)}, NullableStringField = {SpacetimeDB.BSATN.StringUtil.GenericToString(NullableStringField)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomStruct>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableIntFieldRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableStringFieldRW = new();\n\n        public CustomStruct Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new CustomStruct();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomStruct value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomStruct>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntField\", IntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"StringField\", StringFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableIntField\", NullableIntFieldRW.GetAlgebraicType(registrar)),\n                    new(\"NullableStringField\", NullableStringFieldRW.GetAlgebraicType(registrar))\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomStruct>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashIntField = IntField.GetHashCode();\n        var ___hashStringField = StringField == null ? 0 : StringField.GetHashCode();\n        var ___hashNullableIntField = NullableIntField.GetHashCode();\n        var ___hashNullableStringField =\n            NullableStringField == null ? 0 : NullableStringField.GetHashCode();\n        return ___hashIntField\n            ^ ___hashStringField\n            ^ ___hashNullableIntField\n            ^ ___hashNullableStringField;\n    }\n\n#nullable enable\n    public bool Equals(CustomStruct that)\n    {\n        var ___eqIntField = this.IntField.Equals(that.IntField);\n        var ___eqStringField =\n            this.StringField == null\n                ? that.StringField == null\n                : this.StringField.Equals(that.StringField);\n        var ___eqNullableIntField = System.Nullable.Equals(\n            this.NullableIntField,\n            that.NullableIntField\n        );\n        var ___eqNullableStringField =\n            this.NullableStringField == null\n                ? that.NullableStringField == null\n                : this.NullableStringField.Equals(that.NullableStringField);\n        return ___eqIntField\n            && ___eqStringField\n            && ___eqNullableIntField\n            && ___eqNullableStringField;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as CustomStruct?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(CustomStruct this_, CustomStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(CustomStruct this_, CustomStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // CustomStruct\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#CustomTaggedEnum.verified.cs",
    "content": "﻿//HintName: CustomTaggedEnum.cs\n// <auto-generated />\n#nullable enable\n\npartial record CustomTaggedEnum : System.IEquatable<CustomTaggedEnum>\n{\n    public sealed record IntVariant(int IntVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"IntVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(IntVariant_)})\";\n    }\n\n    public sealed record StringVariant(string StringVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"StringVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(StringVariant_)})\";\n    }\n\n    public sealed record NullableIntVariant(int? NullableIntVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"NullableIntVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(NullableIntVariant_)})\";\n    }\n\n    public sealed record NullableStringVariant(string? NullableStringVariant_) : CustomTaggedEnum\n    {\n        public override string ToString() =>\n            $\"NullableStringVariant({SpacetimeDB.BSATN.StringUtil.GenericToString(NullableStringVariant_)})\";\n    }\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomTaggedEnum>\n    {\n        internal static readonly SpacetimeDB.BSATN.I32 IntVariantRW = new();\n        internal static readonly SpacetimeDB.BSATN.String StringVariantRW = new();\n        internal static readonly SpacetimeDB.BSATN.ValueOption<\n            int,\n            SpacetimeDB.BSATN.I32\n        > NullableIntVariantRW = new();\n        internal static readonly SpacetimeDB.BSATN.RefOption<\n            string,\n            SpacetimeDB.BSATN.String\n        > NullableStringVariantRW = new();\n\n        public CustomTaggedEnum Read(System.IO.BinaryReader reader)\n        {\n            return reader.ReadByte() switch\n            {\n                0 => new IntVariant(IntVariantRW.Read(reader)),\n                1 => new StringVariant(StringVariantRW.Read(reader)),\n                2 => new NullableIntVariant(NullableIntVariantRW.Read(reader)),\n                3 => new NullableStringVariant(NullableStringVariantRW.Read(reader)),\n                _\n                    => throw new System.InvalidOperationException(\n                        \"Invalid tag value, this state should be unreachable.\"\n                    )\n            };\n        }\n\n        public void Write(System.IO.BinaryWriter writer, CustomTaggedEnum value)\n        {\n            switch (value)\n            {\n                case IntVariant(var inner):\n                    writer.Write((byte)0);\n                    IntVariantRW.Write(writer, inner);\n                    break;\n                case StringVariant(var inner):\n                    writer.Write((byte)1);\n                    StringVariantRW.Write(writer, inner);\n                    break;\n                case NullableIntVariant(var inner):\n                    writer.Write((byte)2);\n                    NullableIntVariantRW.Write(writer, inner);\n                    break;\n                case NullableStringVariant(var inner):\n                    writer.Write((byte)3);\n                    NullableStringVariantRW.Write(writer, inner);\n                    break;\n            }\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<CustomTaggedEnum>(_ => new SpacetimeDB.BSATN.AlgebraicType.Sum(\n                new SpacetimeDB.BSATN.AggregateElement[]\n                {\n                    new(\"IntVariant\", IntVariantRW.GetAlgebraicType(registrar)),\n                    new(\"StringVariant\", StringVariantRW.GetAlgebraicType(registrar)),\n                    new(\"NullableIntVariant\", NullableIntVariantRW.GetAlgebraicType(registrar)),\n                    new(\n                        \"NullableStringVariant\",\n                        NullableStringVariantRW.GetAlgebraicType(registrar)\n                    )\n                }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<CustomTaggedEnum>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        switch (this)\n        {\n            case IntVariant(var inner):\n                var ___hashIntVariant = inner.GetHashCode();\n                return ___hashIntVariant;\n            case StringVariant(var inner):\n                var ___hashStringVariant = inner == null ? 0 : inner.GetHashCode();\n                return ___hashStringVariant;\n            case NullableIntVariant(var inner):\n                var ___hashNullableIntVariant = inner.GetHashCode();\n                return ___hashNullableIntVariant;\n            case NullableStringVariant(var inner):\n                var ___hashNullableStringVariant = inner == null ? 0 : inner.GetHashCode();\n                return ___hashNullableStringVariant;\n            default:\n                return 0;\n        }\n    }\n} // CustomTaggedEnum\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#EmptyClass.verified.cs",
    "content": "﻿//HintName: EmptyClass.cs\n// <auto-generated />\n#nullable enable\n\npartial class EmptyClass : System.IEquatable<EmptyClass>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader) { }\n\n    public void WriteFields(System.IO.BinaryWriter writer) { }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() => $\"EmptyClass {{  }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<EmptyClass>\n    {\n        public EmptyClass Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new EmptyClass();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, EmptyClass value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<EmptyClass>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[] { }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<EmptyClass>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        return 0;\n    }\n\n#nullable enable\n    public bool Equals(EmptyClass? that)\n    {\n        if (((object?)that) == null)\n        {\n            return false;\n        }\n\n        return true;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as EmptyClass;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(EmptyClass? this_, EmptyClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(EmptyClass? this_, EmptyClass? that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // EmptyClass\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#EmptyRecord.verified.cs",
    "content": "﻿//HintName: EmptyRecord.cs\n// <auto-generated />\n#nullable enable\n\npartial record EmptyRecord : System.IEquatable<EmptyRecord>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader) { }\n\n    public void WriteFields(System.IO.BinaryWriter writer) { }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() => $\"EmptyRecord {{  }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<EmptyRecord>\n    {\n        public EmptyRecord Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new EmptyRecord();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, EmptyRecord value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<EmptyRecord>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[] { }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<EmptyRecord>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        return 0;\n    }\n} // EmptyRecord\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#EmptyStruct.verified.cs",
    "content": "﻿//HintName: EmptyStruct.cs\n// <auto-generated />\n#nullable enable\n\npartial struct EmptyStruct : System.IEquatable<EmptyStruct>, SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader) { }\n\n    public void WriteFields(System.IO.BinaryWriter writer) { }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() => $\"EmptyStruct {{  }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<EmptyStruct>\n    {\n        public EmptyStruct Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new EmptyStruct();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, EmptyStruct value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<EmptyStruct>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                new SpacetimeDB.BSATN.AggregateElement[] { }\n            ));\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<EmptyStruct>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        return 0;\n    }\n\n#nullable enable\n    public bool Equals(EmptyStruct that)\n    {\n        return true;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as EmptyStruct?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(EmptyStruct this_, EmptyStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(EmptyStruct this_, EmptyStruct that)\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // EmptyStruct\n"
  },
  {
    "path": "crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Type#FormerlyForbiddenFieldNames.verified.cs",
    "content": "﻿//HintName: FormerlyForbiddenFieldNames.cs\n// <auto-generated />\n#nullable enable\n\npartial struct FormerlyForbiddenFieldNames\n    : System.IEquatable<FormerlyForbiddenFieldNames>,\n        SpacetimeDB.BSATN.IStructuralReadWrite\n{\n    public void ReadFields(System.IO.BinaryReader reader)\n    {\n        Read = BSATN.ReadRW.Read(reader);\n        Write = BSATN.WriteRW.Read(reader);\n        GetAlgebraicType = BSATN.GetAlgebraicTypeRW.Read(reader);\n    }\n\n    public void WriteFields(System.IO.BinaryWriter writer)\n    {\n        BSATN.ReadRW.Write(writer, Read);\n        BSATN.WriteRW.Write(writer, Write);\n        BSATN.GetAlgebraicTypeRW.Write(writer, GetAlgebraicType);\n    }\n\n    object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()\n    {\n        return new BSATN();\n    }\n\n    public override string ToString() =>\n        $\"FormerlyForbiddenFieldNames {{ Read = {SpacetimeDB.BSATN.StringUtil.GenericToString(Read)}, Write = {SpacetimeDB.BSATN.StringUtil.GenericToString(Write)}, GetAlgebraicType = {SpacetimeDB.BSATN.StringUtil.GenericToString(GetAlgebraicType)} }}\";\n\n    public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<FormerlyForbiddenFieldNames>\n    {\n        internal static readonly SpacetimeDB.BSATN.U32 ReadRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 WriteRW = new();\n        internal static readonly SpacetimeDB.BSATN.U32 GetAlgebraicTypeRW = new();\n\n        public FormerlyForbiddenFieldNames Read(System.IO.BinaryReader reader)\n        {\n            var ___result = new FormerlyForbiddenFieldNames();\n            ___result.ReadFields(reader);\n            return ___result;\n        }\n\n        public void Write(System.IO.BinaryWriter writer, FormerlyForbiddenFieldNames value)\n        {\n            value.WriteFields(writer);\n        }\n\n        public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) =>\n            registrar.RegisterType<FormerlyForbiddenFieldNames>(\n                _ => new SpacetimeDB.BSATN.AlgebraicType.Product(\n                    new SpacetimeDB.BSATN.AggregateElement[]\n                    {\n                        new(\"Read\", ReadRW.GetAlgebraicType(registrar)),\n                        new(\"Write\", WriteRW.GetAlgebraicType(registrar)),\n                        new(\"GetAlgebraicType\", GetAlgebraicTypeRW.GetAlgebraicType(registrar))\n                    }\n                )\n            );\n\n        SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite<FormerlyForbiddenFieldNames>.GetAlgebraicType(\n            SpacetimeDB.BSATN.ITypeRegistrar registrar\n        ) => GetAlgebraicType(registrar);\n    }\n\n    public override int GetHashCode()\n    {\n        var ___hashRead = Read.GetHashCode();\n        var ___hashWrite = Write.GetHashCode();\n        var ___hashGetAlgebraicType = GetAlgebraicType.GetHashCode();\n        return ___hashRead ^ ___hashWrite ^ ___hashGetAlgebraicType;\n    }\n\n#nullable enable\n    public bool Equals(FormerlyForbiddenFieldNames that)\n    {\n        var ___eqRead = this.Read.Equals(that.Read);\n        var ___eqWrite = this.Write.Equals(that.Write);\n        var ___eqGetAlgebraicType = this.GetAlgebraicType.Equals(that.GetAlgebraicType);\n        return ___eqRead && ___eqWrite && ___eqGetAlgebraicType;\n    }\n\n    public override bool Equals(object? that)\n    {\n        if (that == null)\n        {\n            return false;\n        }\n        var that_ = that as FormerlyForbiddenFieldNames?;\n        if (((object?)that_) == null)\n        {\n            return false;\n        }\n        return Equals(that_);\n    }\n\n    public static bool operator ==(\n        FormerlyForbiddenFieldNames this_,\n        FormerlyForbiddenFieldNames that\n    )\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return object.Equals(this_, that);\n        }\n        return this_.Equals(that);\n    }\n\n    public static bool operator !=(\n        FormerlyForbiddenFieldNames this_,\n        FormerlyForbiddenFieldNames that\n    )\n    {\n        if (((object?)this_) == null || ((object?)that) == null)\n        {\n            return !object.Equals(this_, that);\n        }\n        return !this_.Equals(that);\n    }\n#nullable restore\n} // FormerlyForbiddenFieldNames\n"
  },
  {
    "path": "crates/bindings-csharp/Directory.Build.props",
    "content": "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Authors>RReverser</Authors>\n    <Company>Clockwork Labs</Company>\n    <Product>SpacetimeDB</Product>\n    <Copyright>2024</Copyright>\n    <PackageProjectUrl>https://spacetimedb.com/</PackageProjectUrl>\n    <PackageIcon>logo.png</PackageIcon>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <RepositoryType>git</RepositoryType>\n    <RepositoryUrl>https://github.com/clockworklabs/SpacetimeDB</RepositoryUrl>\n    <PackageLicenseFile>LICENSE</PackageLicenseFile>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <LangVersion>latest</LangVersion>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n    <AnalysisLevel>latest-Minimum</AnalysisLevel>\n    <AnalysisModeStyle>Recommended</AnalysisModeStyle>\n    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>\n  </PropertyGroup>\n\n  <!-- Detect unused usings; see https://github.com/dotnet/roslyn/issues/41640. -->\n  <PropertyGroup>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <NoWarn>$(NoWarn);CS1591;CS1574</NoWarn>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"$(MSBuildThisFileDirectory)/logo.png\" Pack=\"true\" PackagePath=\"\" />\n    <None Include=\"$(MSBuildThisFileDirectory)/LICENSE\" Pack=\"true\" PackagePath=\"\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/README.md",
    "content": "# SpacetimeDB\n\nThese projects contain the SpacetimeDB SATS typesystem, codegen and runtime bindings for SpacetimeDB WebAssembly modules.\n\nPlease refer to documentation inside `Codegen` and `Runtime` folders for more details.\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Attrs.cs",
    "content": "namespace SpacetimeDB\n{\n    namespace Internal\n    {\n        [Flags]\n        public enum ColumnAttrs : byte\n        {\n            UnSet = 0b0000,\n            Indexed = 0b0001,\n            AutoInc = 0b0010,\n            Unique = Indexed | 0b0100,\n            Identity = Unique | AutoInc,\n            PrimaryKey = Unique | 0b1000,\n            PrimaryKeyAuto = PrimaryKey | AutoInc,\n            Default = 0b0001_0000,\n        }\n\n        [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]\n        public abstract class ColumnAttribute : Attribute\n        {\n            public string? Table { get; init; }\n            internal abstract ColumnAttrs Mask { get; }\n        }\n    }\n\n    /// <summary>\n    /// Generates code for registering a row-level security rule.\n    ///\n    /// This attribute must be applied to a <c>static</c> field of type <c>Filter</c>.\n    /// It will be interpreted as a filter on the table to which it applies, for all client queries.\n    /// If a module contains multiple <c>client_visibility_filter</c>s for the same table,\n    /// they will be unioned together as if by SQL <c>OR</c>,\n    /// so that any row permitted by at least one filter is visible.\n    ///\n    /// The query follows the same syntax as a subscription query.\n    /// See the <see href=\"https://spacetimedb.com/docs/sql\">SQL reference</see> for more information.\n    ///\n    /// This is an experimental feature and subject to change in the future.\n    /// </summary>\n    [System.Diagnostics.CodeAnalysis.Experimental(\"STDB_UNSTABLE\")]\n    [AttributeUsage(AttributeTargets.Field)]\n    public sealed class ClientVisibilityFilterAttribute : Attribute { }\n\n    [AttributeUsage(AttributeTargets.Field)]\n    public sealed class SettingsAttribute : Attribute { }\n\n    /// <summary>\n    /// Registers a type as the row structure of a SpacetimeDB table, enabling codegen for it.\n    ///\n    /// <para>\n    /// Multiple [Table] attributes per type are supported. This is useful to reuse row types.\n    /// Each attribute instance must have a unique name and will create a SpacetimeDB table.\n    /// </para>\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true)]\n    public sealed class TableAttribute : Attribute\n    {\n        /// <summary>\n        /// This identifier is used to name the SpacetimeDB table on the host as well as the\n        /// table handle structures generated to access the table from within a reducer call.\n        ///\n        /// <para>Defaults to the <c>nameof</c> of the target type.</para>\n        /// </summary>\n        public string? Accessor { get; init; }\n\n        public string? Name { get; init; }\n\n        /// <summary>\n        /// Set to <c>true</c> to make the table visible to everyone.\n        ///\n        /// <para>Defaults to the table only being visible to its owner.</para>\n        /// </summary>\n        public bool Public { get; init; } = false;\n\n        public bool Event { get; init; } = false;\n\n        /// <summary>\n        /// If set, the name of the reducer that will be invoked when the scheduled time is reached.\n        /// </summary>\n        public string? Scheduled { get; init; }\n\n        /// <summary>\n        /// The name of the column that will be used to store the scheduled time.\n        ///\n        /// <para>Defaults to <c>ScheduledAt</c>.</para>\n        /// </summary>\n        public string ScheduledAt { get; init; } = \"ScheduledAt\";\n    }\n\n    /// <summary>\n    /// Registers a method as a SpacetimeDB view, enabling codegen for it.\n    /// Views are pure, read-only queries that run with a view context instead of a reducer context.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Method, Inherited = false)]\n    public sealed class ViewAttribute : Attribute\n    {\n        /// <summary>\n        /// Views must have an explicit name.\n        /// </summary>\n        public string? Accessor { get; init; }\n\n        public string? Name { get; init; }\n\n        /// <summary>\n        /// Marks the view as callable by any client. Leave false to restrict to the module owner.\n        /// </summary>\n        public bool Public { get; init; } = false;\n    }\n\n    [AttributeUsage(\n        AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Field,\n        AllowMultiple = true\n    )]\n    public abstract class Index : Attribute\n    {\n        public string? Table { get; init; }\n\n        public string? Accessor { get; init; }\n\n        public string? Name { get; init; }\n\n        public sealed class BTreeAttribute : Index\n        {\n            public string[] Columns { get; init; } = [];\n        }\n    }\n\n    public sealed class AutoIncAttribute : Internal.ColumnAttribute\n    {\n        internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.AutoInc;\n    }\n\n    public sealed class PrimaryKeyAttribute : Internal.ColumnAttribute\n    {\n        internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.PrimaryKey;\n    }\n\n    public sealed class UniqueAttribute : Internal.ColumnAttribute\n    {\n        internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.Unique;\n    }\n\n    /// <summary>\n    /// Specifies a default value for a table column.\n    /// If a column is added to an existing table while republishing of a module,\n    /// the specified default value will be used to populate existing rows.\n    /// </summary>\n    /// <remarks>\n    /// Updates existing instances of the <see cref=\"DefaultAttribute\"/> class with the specified default value during republishing of a module.\n    /// </remarks>\n    /// <param name=\"value\">The default value for the column.</param>\n    [AttributeUsage(AttributeTargets.Field)]\n    public sealed class DefaultAttribute(object value) : Internal.ColumnAttribute\n    {\n        /// <summary>\n        /// The default value for the column.\n        /// </summary>\n        public string Value\n        {\n            get\n            {\n                if (value is null)\n                {\n                    return \"null\";\n                }\n                if (value is bool)\n                {\n                    return value.ToString()?.ToLower()!;\n                }\n                var str = value.ToString();\n                if (value is string)\n                {\n                    str = $\"\\\"{str}\\\"\";\n                }\n                return str!;\n            }\n        }\n\n        internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.Default;\n    }\n\n    public enum ReducerKind\n    {\n        /// <summary>\n        /// The default reducer kind, no need to specify this explicitly.\n        /// </summary>\n        UserDefined,\n        Init,\n        ClientConnected,\n        ClientDisconnected,\n    }\n\n    [AttributeUsage(AttributeTargets.Method, Inherited = false)]\n    public sealed class ReducerAttribute(ReducerKind kind = ReducerKind.UserDefined) : Attribute\n    {\n        public ReducerKind Kind => kind;\n\n        public string? Name { get; init; }\n    }\n\n    [AttributeUsage(AttributeTargets.Method, Inherited = false)]\n    public sealed class ProcedureAttribute() : Attribute\n    {\n        public string? Name { get; init; }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/AuthCtx.cs",
    "content": "namespace SpacetimeDB;\n\nusing System;\n\npublic sealed class AuthCtx\n{\n    private readonly bool _isInternal;\n    private readonly Lazy<JwtClaims?> _jwtLazy;\n\n    private AuthCtx(bool isInternal, Func<JwtClaims?> jwtFactory)\n    {\n        _isInternal = isInternal;\n        _jwtLazy = new Lazy<JwtClaims?>(() => jwtFactory?.Invoke());\n    }\n\n    /// <summary>\n    /// Create an AuthCtx for an internal call, with no JWT.\n    /// </summary>\n    private static AuthCtx Internal()\n    {\n        return new AuthCtx(isInternal: true, jwtFactory: () => null);\n    }\n\n    /// <summary>\n    /// Create an AuthCtx by looking up the credentials for a connection id in system tables.\n    ///\n    /// Ideally this would not be part of the public API.\n    /// This should only be called inside of a reducer.\n    /// </summary>\n    public static AuthCtx BuildFromSystemTables(ConnectionId? connectionId, Identity identity)\n    {\n        if (connectionId == null)\n        {\n            return Internal();\n        }\n        return FromConnectionId(connectionId.Value, identity);\n    }\n\n    /// <summary>\n    /// Create an AuthCtx that reads JWT for a given connection ID.\n    /// </summary>\n    private static AuthCtx FromConnectionId(ConnectionId connectionId, Identity identity)\n    {\n        return new AuthCtx(\n            isInternal: false,\n            jwtFactory: () =>\n            {\n                var result = SpacetimeDB.Internal.FFI.get_jwt(ref connectionId, out var source);\n                SpacetimeDB.Internal.FFI.CheckedStatus.Marshaller.ConvertToManaged(result);\n                var bytes = SpacetimeDB.Internal.Module.Consume(source);\n                if (bytes == null || bytes.Length == 0)\n                {\n                    return null;\n                }\n                var jwt = System.Text.Encoding.UTF8.GetString(bytes);\n                return jwt != null ? new JwtClaims(jwt, identity) : null;\n            }\n        );\n    }\n\n    /// <summary>\n    /// True if this reducer was spawned from inside the database.\n    /// </summary>\n    public bool IsInternal => _isInternal;\n\n    /// <summary>\n    /// Check if there is a JWT present.\n    /// If IsInternal is true, this will be false.\n    /// </summary>\n    public bool HasJwt\n    {\n        get\n        {\n            if (_isInternal)\n            {\n                return false;\n            }\n\n            // At this point we do load the bytes.\n            return _jwtLazy.Value != null;\n        }\n    }\n\n    /// <summary>\n    /// Load and get the JwtClaims.\n    /// </summary>\n    public JwtClaims? Jwt => _jwtLazy.Value;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Exceptions.cs",
    "content": "namespace SpacetimeDB;\n\nusing SpacetimeDB.Internal;\n\npublic abstract class StdbException : Exception\n{\n    public abstract override string Message { get; }\n}\n\npublic class NotInTransactionException : StdbException\n{\n    public override string Message => \"ABI call can only be made while in a transaction\";\n}\n\npublic class BsatnDecodeException : StdbException\n{\n    public override string Message => \"Couldn't decode the BSATN to the expected type\";\n}\n\npublic class NoSuchTableException : StdbException\n{\n    public override string Message => \"No such table\";\n}\n\npublic class NoSuchIndexException : StdbException\n{\n    public override string Message => \"No such index\";\n}\n\npublic class IndexNotUniqueException : StdbException\n{\n    public override string Message => \"The index was not unique\";\n}\n\npublic class NoSuchRowException : StdbException\n{\n    public override string Message => \"The row was not found, e.g., in an update call\";\n}\n\npublic class UniqueConstraintViolationException : StdbException\n{\n    public override string Message => \"Value with given unique identifier already exists\";\n}\n\npublic class ScheduleAtDelayTooLongException : StdbException\n{\n    public override string Message => \"Specified delay in scheduling row was too long\";\n}\n\npublic class BufferTooSmallException : StdbException\n{\n    public override string Message => \"The provided buffer is not large enough to store the data\";\n}\n\npublic class NoSuchIterException : StdbException\n{\n    public override string Message => \"The provided row iterator does not exist\";\n}\n\npublic class NoSuchLogStopwatch : StdbException\n{\n    public override string Message => \"The provided stopwatch does not exist\";\n}\n\npublic class NoSuchBytesException : StdbException\n{\n    public override string Message => \"The provided bytes source or sink does not exist\";\n}\n\npublic class NoSpaceException : StdbException\n{\n    public override string Message => \"The provided bytes sink has no more room left\";\n}\n\npublic class AutoIncOverflowException : StdbException\n{\n    public override string Message => \"The auto-increment sequence overflowed\";\n}\n\npublic class TransactionWouldBlockException : StdbException\n{\n    public override string Message => \"Attempted operation while another transaction is open\";\n}\n\npublic class TransactionNotAnonymousException : StdbException\n{\n    public override string Message => \"The transaction is not anonymous\";\n}\n\npublic class TransactionIsReadOnlyException : StdbException\n{\n    public override string Message => \"The transaction is read-only\";\n}\n\npublic class TransactionIsMutableException : StdbException\n{\n    public override string Message =>\n        \"ABI call can only be made while inside a read-only transaction\";\n}\n\npublic class HttpException : StdbException\n{\n    public override string Message => \"HTTP request failed\";\n}\n\npublic class UnknownException : StdbException\n{\n    private readonly Errno code;\n\n    internal UnknownException(Errno code) => this.code = code;\n\n    public override string Message => $\"SpacetimeDB error code {code}\";\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Filter.cs",
    "content": "namespace SpacetimeDB;\n\n/// <summary>\n/// A row-level security filter,\n/// which can be registered using the <c>[SpacetimeDB.ClientVisibilityFilter]</c> attribute.\n///\n/// Currently, the only valid value for a filter is a <c>Filter.Sql</c>.\n/// This is a filter written as a SQL query. Rows that match this query will be made visible to clients.\n///\n/// The query must be of the form `SELECT * FROM table` or `SELECT table.* from table`,\n/// followed by any number of `JOIN` clauses and a `WHERE` clause.\n/// If the query includes any `JOIN`s, it must be in the form `SELECT table.* FROM table`.\n/// In any case, the query must select all of the columns from a single table, and nothing else.\n///\n/// SQL queries are not checked for syntactic or semantic validity\n/// until they are processed by the SpacetimeDB host.\n/// This means that errors in queries used as <c>[SpacetimeDB.ClientVisibilityFilter]</c> rules\n/// will be reported during <c>spacetime publish</c>, not at compile time.\n/// </summary>\n// Note: _Unused is needed because C# doesn't support single-element named tuples :/\n[Type]\npublic partial record Filter : TaggedEnum<(string Sql, Unit _Unused)> { }\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Http.cs",
    "content": "namespace SpacetimeDB;\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing Internal;\nusing SpacetimeDB.BSATN;\n\npublic enum HttpVersion : byte\n{\n    Http09,\n    Http10,\n    Http11,\n    Http2,\n    Http3,\n}\n\n/// <summary>\n/// Represents an HTTP method (e.g. GET, POST).\n/// </summary>\n/// <remarks>\n/// Unknown methods are supported by providing an arbitrary string value.\n/// </remarks>\npublic readonly record struct HttpMethod(string Value)\n{\n    public static readonly HttpMethod Get = new(\"GET\");\n    public static readonly HttpMethod Head = new(\"HEAD\");\n    public static readonly HttpMethod Post = new(\"POST\");\n    public static readonly HttpMethod Put = new(\"PUT\");\n    public static readonly HttpMethod Delete = new(\"DELETE\");\n    public static readonly HttpMethod Connect = new(\"CONNECT\");\n    public static readonly HttpMethod Options = new(\"OPTIONS\");\n    public static readonly HttpMethod Trace = new(\"TRACE\");\n    public static readonly HttpMethod Patch = new(\"PATCH\");\n}\n\n/// <summary>\n/// Represents an HTTP header name/value pair.\n/// </summary>\n/// <remarks>\n/// Multiple headers with the same name are permitted.\n/// The <c>IsSensitive</c> flag is a local-only hint and is not transmitted to the host.\n/// </remarks>\npublic readonly record struct HttpHeader(string Name, byte[] Value, bool IsSensitive = false)\n{\n    public HttpHeader(string name, string value)\n        : this(name, Encoding.ASCII.GetBytes(value), false) { }\n}\n\n/// <summary>\n/// Represents the body of an HTTP request or response.\n/// </summary>\n/// <remarks>\n/// Bodies are treated as raw bytes. Use <see cref=\"ToStringUtf8Lossy\"/> when interpreting a body as UTF-8 text.\n/// </remarks>\npublic readonly record struct HttpBody(byte[] Bytes)\n{\n    public static HttpBody Empty => new(Array.Empty<byte>());\n\n    public byte[] ToBytes() => Bytes;\n\n    public string ToStringUtf8Lossy() => Encoding.UTF8.GetString(Bytes);\n\n    public static HttpBody FromString(string s) => new(Encoding.UTF8.GetBytes(s));\n}\n\n/// <summary>\n/// Represents an HTTP request to be executed by the SpacetimeDB host from within a procedure.\n/// </summary>\n/// <remarks>\n/// The request body is stored separately from the request metadata in the host ABI.\n/// </remarks>\npublic sealed class HttpRequest\n{\n    /// <summary>Request URI.</summary>\n    /// <remarks>Must not be null or empty.</remarks>\n    public required string Uri { get; init; }\n\n    /// <summary>HTTP method to use (e.g. GET, POST).</summary>\n    public HttpMethod Method { get; init; } = HttpMethod.Get;\n\n    /// <summary>HTTP headers to include with the request.</summary>\n    public List<HttpHeader> Headers { get; init; } = new();\n\n    /// <summary>Request body bytes.</summary>\n    public HttpBody Body { get; init; } = HttpBody.Empty;\n\n    /// <summary>HTTP version to report in the request metadata.</summary>\n    public HttpVersion Version { get; init; } = HttpVersion.Http11;\n\n    /// <summary>\n    /// Optional timeout for the request.\n    /// </summary>\n    /// <remarks>\n    /// The SpacetimeDB host clamps all timeouts to a maximum of 500ms.\n    /// </remarks>\n    public TimeSpan? Timeout { get; init; }\n}\n\n/// <summary>\n/// Represents an HTTP response returned by the SpacetimeDB host.\n/// </summary>\n/// <remarks>\n/// A non-2xx status code is still returned as a successful response; callers should inspect\n/// <see cref=\"StatusCode\"/> to handle application-level errors from the remote server.\n/// </remarks>\npublic readonly record struct HttpResponse(\n    ushort StatusCode,\n    HttpVersion Version,\n    List<HttpHeader> Headers,\n    HttpBody Body\n);\n\n/// <summary>\n/// Error returned when the SpacetimeDB host could not execute an HTTP request.\n/// </summary>\n/// <remarks>\n/// This indicates a failure to perform the request (e.g. DNS failure, connection error, timeout),\n/// not an application-level HTTP error response (which is represented by <see cref=\"HttpResponse.StatusCode\"/>).\n/// </remarks>\npublic sealed class HttpError(string message) : Exception(message)\n{\n    private readonly string message = message;\n\n    public override string Message => message;\n}\n\n/// <summary>\n/// Allows a procedure to perform outbound HTTP requests via the host.\n/// </summary>\n/// <remarks>\n/// This API is available from <c>ProcedureContext.Http</c>.\n///\n/// The request metadata (method/headers/timeout/uri/version) is encoded using a stable wire format\n/// and executed by the SpacetimeDB host. The request body is sent separately as raw bytes.\n///\n/// <para>\n/// <b>Transaction limitation:</b> HTTP requests cannot be performed while a mutable transaction is open.\n/// If called inside <c>WithTx</c>, the host will reject the call (<c>WOULD_BLOCK_TRANSACTION</c>).\n/// </para>\n///\n/// <para>\n/// <b>Timeouts:</b> The host clamps all HTTP timeouts to a maximum of 500ms.\n/// </para>\n///\n/// <para>\n/// The returned response may have any HTTP status code (including non-2xx). This is still considered a\n/// successful HTTP exchange; <see cref=\"Send\"/> only returns an error when the request could not be\n/// initiated or completed (e.g. DNS failure, connection failure, timeout).\n/// </para>\n/// </remarks>\npublic sealed class HttpClient\n{\n    private static readonly TimeSpan MaxTimeout = TimeSpan.FromMilliseconds(500);\n\n    /// <summary>\n    /// Send a simple <c>GET</c> request to <paramref name=\"uri\"/> with no headers.\n    /// </summary>\n    /// <param name=\"uri\">The request URI.</param>\n    /// <param name=\"timeout\">\n    /// Optional timeout for the request. The host clamps timeouts to a maximum of 500ms.\n    /// </param>\n    /// <returns>\n    /// <c>Ok(HttpResponse)</c> when a response was received (regardless of HTTP status code),\n    /// or <c>Err(HttpError)</c> if the request failed to execute.\n    /// </returns>\n    /// <example>\n    /// <code>\n    /// [SpacetimeDB.Procedure]\n    /// public static string FetchSchema(ProcedureContext ctx)\n    /// {\n    ///     var result = ctx.Http.Get(\"http://localhost:3000/v1/database/schema\");\n    ///     if (!result.IsSuccess)\n    ///     {\n    ///         return $\"ERR {result.Error}\";\n    ///     }\n    ///\n    ///     var response = result.Value!;\n    ///     return response.Body.ToStringUtf8Lossy();\n    /// }\n    /// </code>\n    /// </example>\n    public Result<HttpResponse, HttpError> Get(string uri, TimeSpan? timeout = null) =>\n        Send(\n            new HttpRequest\n            {\n                Uri = uri,\n                Method = HttpMethod.Get,\n                Body = HttpBody.Empty,\n                Timeout = timeout,\n            }\n        );\n\n    /// <summary>\n    /// Send an HTTP request described by <paramref name=\"request\"/> and wait for its response.\n    /// </summary>\n    /// <param name=\"request\">\n    /// Request metadata (method, headers, uri, version, optional timeout) plus a request body.\n    /// </param>\n    /// <returns>\n    /// <c>Ok(HttpResponse)</c> when a response was received (including non-2xx status codes),\n    /// or <c>Err(HttpError)</c> when the host could not perform the request.\n    /// </returns>\n    /// <remarks>\n    /// This method does not throw for expected failures; errors are returned as <c>Result.Err</c>.\n    /// </remarks>\n    /// <example>\n    /// <code>\n    /// [SpacetimeDB.Procedure]\n    /// public static string PostSomething(ProcedureContext ctx)\n    /// {\n    ///     var request = new HttpRequest\n    ///     {\n    ///         Uri = \"https://some-remote-host.invalid/upload\",\n    ///         Method = new HttpMethod(\"POST\"),\n    ///         Headers = new()\n    ///         {\n    ///             new HttpHeader(\"Content-Type\", \"text/plain\"),\n    ///         },\n    ///         Body = HttpBody.FromString(\"This is the body of the HTTP request\"),\n    ///         Timeout = TimeSpan.FromMilliseconds(100),\n    ///     };\n    ///\n    ///     var result = ctx.Http.Send(request);\n    ///     if (!result.IsSuccess)\n    ///     {\n    ///         return $\"ERR {result.Error}\";\n    ///     }\n    ///\n    ///     var response = result.Value!;\n    ///     return $\"OK status={response.StatusCode} body={response.Body.ToStringUtf8Lossy()}\";\n    /// }\n    /// </code>\n    /// </example>\n    /// <example>\n    /// <code>\n    /// [SpacetimeDB.Procedure]\n    /// public static string FetchMay404(ProcedureContext ctx)\n    /// {\n    ///     var result = ctx.Http.Get(\"https://example.invalid/missing\");\n    ///     if (!result.IsSuccess)\n    ///     {\n    ///         // DNS failure, connection drop, timeout, etc.\n    ///         return $\"ERR transport: {result.Error}\";\n    ///     }\n    ///\n    ///     var response = result.Value!;\n    ///     if (response.StatusCode != 200)\n    ///     {\n    ///         // Application-level HTTP error response.\n    ///         return $\"ERR http status={response.StatusCode}\";\n    ///     }\n    ///\n    ///     return $\"OK {response.Body.ToStringUtf8Lossy()}\";\n    /// }\n    /// </code>\n    /// </example>\n    /// <example>\n    /// <code>\n    /// [SpacetimeDB.Procedure]\n    /// public static void DontDoThis(ProcedureContext ctx)\n    /// {\n    ///     ctx.WithTx(tx =>\n    ///     {\n    ///         // The host rejects this with WOULD_BLOCK_TRANSACTION.\n    ///         var _ = ctx.Http.Get(\"https://example.invalid/\");\n    ///         return 0;\n    ///     });\n    /// }\n    /// </code>\n    /// </example>\n    public Result<HttpResponse, HttpError> Send(HttpRequest request)\n    {\n        // The host syscall expects BSATN-encoded spacetimedb_lib::http::Request bytes.\n        // A mismatch in the wire layout can cause the host to trap during BSATN decode,\n        // so the C# `Http*Wire` types must remain in lockstep with the Rust definitions.\n        try\n        {\n            if (string.IsNullOrEmpty(request.Uri))\n            {\n                return Result<HttpResponse, HttpError>.Err(\n                    new HttpError(\"URI must not be null or empty\")\n                );\n            }\n\n            // The host clamps all HTTP timeouts to a maximum of 500ms.\n            // Clamp here as well to keep C# behavior aligned with the Rust docs and to reduce surprises.\n            var timeout = request.Timeout;\n            if (timeout is not null)\n            {\n                if (timeout.Value < TimeSpan.Zero)\n                {\n                    return Result<HttpResponse, HttpError>.Err(\n                        new HttpError(\"Timeout must not be negative\")\n                    );\n                }\n\n                if (timeout.Value > MaxTimeout)\n                {\n                    timeout = MaxTimeout;\n                }\n            }\n\n            var requestWire = new HttpRequestWire\n            {\n                Method = ToWireMethod(request.Method),\n                Headers = new HttpHeadersWire\n                {\n                    Entries = request.Headers.Select(ToWireHeader).ToArray(),\n                },\n                Timeout = timeout is null\n                    ? null\n                    : new HttpTimeoutWire { Timeout = (TimeDuration)timeout.Value },\n                Uri = request.Uri,\n                Version = ToWireVersion(request.Version),\n            };\n\n            var requestBytes = IStructuralReadWrite.ToBytes(\n                new HttpRequestWire.BSATN(),\n                requestWire\n            );\n            var bodyBytes = request.Body.ToBytes();\n\n            var status = FFI.procedure_http_request(\n                requestBytes,\n                (uint)requestBytes.Length,\n                bodyBytes,\n                (uint)bodyBytes.Length,\n                out var out_\n            );\n\n            switch (status)\n            {\n                case Errno.OK:\n                {\n                    var responseWireBytes = out_.A.Consume();\n                    var responseWire = FromBytes(new HttpResponseWire.BSATN(), responseWireBytes);\n\n                    var body = new HttpBody(out_.B.Consume());\n                    var (statusCode, version, headers) = FromWireResponse(responseWire);\n\n                    return Result<HttpResponse, HttpError>.Ok(\n                        new HttpResponse(statusCode, version, headers, body)\n                    );\n                }\n                case Errno.HTTP_ERROR:\n                {\n                    var errorWireBytes = out_.A.Consume();\n                    var err = FromBytes(new SpacetimeDB.BSATN.String(), errorWireBytes);\n                    return Result<HttpResponse, HttpError>.Err(new HttpError(err));\n                }\n                case Errno.WOULD_BLOCK_TRANSACTION:\n                    return Result<HttpResponse, HttpError>.Err(\n                        new HttpError(\n                            \"HTTP requests cannot be performed while a mutable transaction is open (WOULD_BLOCK_TRANSACTION).\"\n                        )\n                    );\n                default:\n                    return Result<HttpResponse, HttpError>.Err(\n                        new HttpError(FFI.ErrnoHelpers.ToException(status).ToString())\n                    );\n            }\n        }\n        // Important: avoid throwing across the procedure boundary.\n        // Throwing here would trap the module (and fail the whole procedure invocation).\n        // Convert all unexpected failures (including decode errors / unexpected errno) into Result.Err instead.\n        catch (Exception ex)\n        {\n            return Result<HttpResponse, HttpError>.Err(new HttpError(ex.ToString()));\n        }\n    }\n\n    private static T FromBytes<T>(IReadWrite<T> rw, byte[] bytes)\n    {\n        using var ms = new MemoryStream(bytes);\n        using var reader = new BinaryReader(ms);\n        var value = rw.Read(reader);\n        if (ms.Position != ms.Length)\n        {\n            throw new InvalidOperationException(\n                \"Unrecognized extra bytes while decoding BSATN value\"\n            );\n        }\n        return value;\n    }\n\n    private static HttpMethodWire ToWireMethod(HttpMethod method)\n    {\n        var m = method.Value;\n        return m switch\n        {\n            \"GET\" => new HttpMethodWire.Get(default),\n            \"HEAD\" => new HttpMethodWire.Head(default),\n            \"POST\" => new HttpMethodWire.Post(default),\n            \"PUT\" => new HttpMethodWire.Put(default),\n            \"DELETE\" => new HttpMethodWire.Delete(default),\n            \"CONNECT\" => new HttpMethodWire.Connect(default),\n            \"OPTIONS\" => new HttpMethodWire.Options(default),\n            \"TRACE\" => new HttpMethodWire.Trace(default),\n            \"PATCH\" => new HttpMethodWire.Patch(default),\n            _ => new HttpMethodWire.Extension(m),\n        };\n    }\n\n    private static HttpVersionWire ToWireVersion(HttpVersion version) =>\n        version switch\n        {\n            HttpVersion.Http09 => HttpVersionWire.Http09,\n            HttpVersion.Http10 => HttpVersionWire.Http10,\n            HttpVersion.Http11 => HttpVersionWire.Http11,\n            HttpVersion.Http2 => HttpVersionWire.Http2,\n            HttpVersion.Http3 => HttpVersionWire.Http3,\n            _ => throw new ArgumentOutOfRangeException(nameof(version)),\n        };\n\n    private static HttpHeaderPairWire ToWireHeader(HttpHeader header) =>\n        new() { Name = header.Name, Value = header.Value };\n\n    private static (\n        ushort statusCode,\n        HttpVersion version,\n        List<HttpHeader> headers\n    ) FromWireResponse(HttpResponseWire responseWire)\n    {\n        var version = responseWire.Version switch\n        {\n            HttpVersionWire.Http09 => HttpVersion.Http09,\n            HttpVersionWire.Http10 => HttpVersion.Http10,\n            HttpVersionWire.Http11 => HttpVersion.Http11,\n            HttpVersionWire.Http2 => HttpVersion.Http2,\n            HttpVersionWire.Http3 => HttpVersion.Http3,\n            _ => throw new InvalidOperationException(\"Invalid HTTP version returned from host\"),\n        };\n\n        var headers = responseWire\n            .Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false))\n            .ToList();\n\n        return (responseWire.Code, version, headers);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/.gitattributes",
    "content": "/Autogen/*.cs linguist-generated=true\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/CaseConversionPolicy.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    public enum CaseConversionPolicy\n    {\n        None,\n        SnakeCase,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/ExplicitNameEntry.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record ExplicitNameEntry : SpacetimeDB.TaggedEnum<(\n        NameMapping Table,\n        NameMapping Function,\n        NameMapping Index\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/ExplicitNames.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class ExplicitNames\n    {\n        [DataMember(Name = \"entries\")]\n        public System.Collections.Generic.List<ExplicitNameEntry> Entries;\n\n        public ExplicitNames(System.Collections.Generic.List<ExplicitNameEntry> Entries)\n        {\n            this.Entries = Entries;\n        }\n\n        public ExplicitNames()\n        {\n            this.Entries = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/FunctionVisibility.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public enum FunctionVisibility\n    {\n        Private,\n        ClientCallable,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/IndexType.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public enum IndexType\n    {\n        BTree,\n        Hash,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/Lifecycle.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public enum Lifecycle\n    {\n        Init,\n        OnConnect,\n        OnDisconnect,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/MiscModuleExport.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record MiscModuleExport : SpacetimeDB.TaggedEnum<(\n        TypeAlias TypeAlias,\n        SpacetimeDB.Unit _Reserved\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/NameMapping.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class NameMapping\n    {\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n        [DataMember(Name = \"canonical_name\")]\n        public string CanonicalName;\n\n        public NameMapping(\n            string SourceName,\n            string CanonicalName\n        )\n        {\n            this.SourceName = SourceName;\n            this.CanonicalName = CanonicalName;\n        }\n\n        public NameMapping()\n        {\n            this.SourceName = \"\";\n            this.CanonicalName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawColumnDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawColumnDefV8\n    {\n        [DataMember(Name = \"col_name\")]\n        public string ColName;\n        [DataMember(Name = \"col_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ColType;\n\n        public RawColumnDefV8(\n            string ColName,\n            SpacetimeDB.BSATN.AlgebraicType ColType\n        )\n        {\n            this.ColName = ColName;\n            this.ColType = ColType;\n        }\n\n        public RawColumnDefV8()\n        {\n            this.ColName = \"\";\n            this.ColType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawColumnDefaultValueV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawColumnDefaultValueV10\n    {\n        [DataMember(Name = \"col_id\")]\n        public ushort ColId;\n        [DataMember(Name = \"value\")]\n        public System.Collections.Generic.List<byte> Value;\n\n        public RawColumnDefaultValueV10(\n            ushort ColId,\n            System.Collections.Generic.List<byte> Value\n        )\n        {\n            this.ColId = ColId;\n            this.Value = Value;\n        }\n\n        public RawColumnDefaultValueV10()\n        {\n            this.Value = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawColumnDefaultValueV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawColumnDefaultValueV9\n    {\n        [DataMember(Name = \"table\")]\n        public string Table;\n        [DataMember(Name = \"col_id\")]\n        public ushort ColId;\n        [DataMember(Name = \"value\")]\n        public System.Collections.Generic.List<byte> Value;\n\n        public RawColumnDefaultValueV9(\n            string Table,\n            ushort ColId,\n            System.Collections.Generic.List<byte> Value\n        )\n        {\n            this.Table = Table;\n            this.ColId = ColId;\n            this.Value = Value;\n        }\n\n        public RawColumnDefaultValueV9()\n        {\n            this.Table = \"\";\n            this.Value = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawConstraintDataV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record RawConstraintDataV9 : SpacetimeDB.TaggedEnum<(\n        RawUniqueConstraintDataV9 Unique,\n        SpacetimeDB.Unit _Reserved\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawConstraintDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawConstraintDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string? SourceName;\n        [DataMember(Name = \"data\")]\n        public RawConstraintDataV9 Data;\n\n        public RawConstraintDefV10(\n            string? SourceName,\n            RawConstraintDataV9 Data\n        )\n        {\n            this.SourceName = SourceName;\n            this.Data = Data;\n        }\n\n        public RawConstraintDefV10()\n        {\n            this.Data = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawConstraintDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawConstraintDefV8\n    {\n        [DataMember(Name = \"constraint_name\")]\n        public string ConstraintName;\n        [DataMember(Name = \"constraints\")]\n        public byte Constraints;\n        [DataMember(Name = \"columns\")]\n        public System.Collections.Generic.List<ushort> Columns;\n\n        public RawConstraintDefV8(\n            string ConstraintName,\n            byte Constraints,\n            System.Collections.Generic.List<ushort> Columns\n        )\n        {\n            this.ConstraintName = ConstraintName;\n            this.Constraints = Constraints;\n            this.Columns = Columns;\n        }\n\n        public RawConstraintDefV8()\n        {\n            this.ConstraintName = \"\";\n            this.Columns = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawConstraintDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawConstraintDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string? Name;\n        [DataMember(Name = \"data\")]\n        public RawConstraintDataV9 Data;\n\n        public RawConstraintDefV9(\n            string? Name,\n            RawConstraintDataV9 Data\n        )\n        {\n            this.Name = Name;\n            this.Data = Data;\n        }\n\n        public RawConstraintDefV9()\n        {\n            this.Data = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawIndexAlgorithm.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record RawIndexAlgorithm : SpacetimeDB.TaggedEnum<(\n        System.Collections.Generic.List<ushort> BTree,\n        System.Collections.Generic.List<ushort> Hash,\n        ushort Direct\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawIndexDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawIndexDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string? SourceName;\n        [DataMember(Name = \"accessor_name\")]\n        public string? AccessorName;\n        [DataMember(Name = \"algorithm\")]\n        public RawIndexAlgorithm Algorithm;\n\n        public RawIndexDefV10(\n            string? SourceName,\n            string? AccessorName,\n            RawIndexAlgorithm Algorithm\n        )\n        {\n            this.SourceName = SourceName;\n            this.AccessorName = AccessorName;\n            this.Algorithm = Algorithm;\n        }\n\n        public RawIndexDefV10()\n        {\n            this.Algorithm = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawIndexDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawIndexDefV8\n    {\n        [DataMember(Name = \"index_name\")]\n        public string IndexName;\n        [DataMember(Name = \"is_unique\")]\n        public bool IsUnique;\n        [DataMember(Name = \"index_type\")]\n        public IndexType IndexType;\n        [DataMember(Name = \"columns\")]\n        public System.Collections.Generic.List<ushort> Columns;\n\n        public RawIndexDefV8(\n            string IndexName,\n            bool IsUnique,\n            IndexType IndexType,\n            System.Collections.Generic.List<ushort> Columns\n        )\n        {\n            this.IndexName = IndexName;\n            this.IsUnique = IsUnique;\n            this.IndexType = IndexType;\n            this.Columns = Columns;\n        }\n\n        public RawIndexDefV8()\n        {\n            this.IndexName = \"\";\n            this.Columns = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawIndexDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawIndexDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string? Name;\n        [DataMember(Name = \"accessor_name\")]\n        public string? AccessorName;\n        [DataMember(Name = \"algorithm\")]\n        public RawIndexAlgorithm Algorithm;\n\n        public RawIndexDefV9(\n            string? Name,\n            string? AccessorName,\n            RawIndexAlgorithm Algorithm\n        )\n        {\n            this.Name = Name;\n            this.AccessorName = AccessorName;\n            this.Algorithm = Algorithm;\n        }\n\n        public RawIndexDefV9()\n        {\n            this.Algorithm = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawLifeCycleReducerDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawLifeCycleReducerDefV10\n    {\n        [DataMember(Name = \"lifecycle_spec\")]\n        public Lifecycle LifecycleSpec;\n        [DataMember(Name = \"function_name\")]\n        public string FunctionName;\n\n        public RawLifeCycleReducerDefV10(\n            Lifecycle LifecycleSpec,\n            string FunctionName\n        )\n        {\n            this.LifecycleSpec = LifecycleSpec;\n            this.FunctionName = FunctionName;\n        }\n\n        public RawLifeCycleReducerDefV10()\n        {\n            this.FunctionName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawMiscModuleExportV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record RawMiscModuleExportV9 : SpacetimeDB.TaggedEnum<(\n        RawColumnDefaultValueV9 ColumnDefaultValue,\n        RawProcedureDefV9 Procedure,\n        RawViewDefV9 View\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawModuleDef.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record RawModuleDef : SpacetimeDB.TaggedEnum<(\n        RawModuleDefV8 V8BackCompat,\n        RawModuleDefV9 V9,\n        RawModuleDefV10 V10\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawModuleDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawModuleDefV10\n    {\n        [DataMember(Name = \"sections\")]\n        public System.Collections.Generic.List<RawModuleDefV10Section> Sections;\n\n        public RawModuleDefV10(System.Collections.Generic.List<RawModuleDefV10Section> Sections)\n        {\n            this.Sections = Sections;\n        }\n\n        public RawModuleDefV10()\n        {\n            this.Sections = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawModuleDefV10Section.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public partial record RawModuleDefV10Section : SpacetimeDB.TaggedEnum<(\n        Typespace Typespace,\n        System.Collections.Generic.List<RawTypeDefV10> Types,\n        System.Collections.Generic.List<RawTableDefV10> Tables,\n        System.Collections.Generic.List<RawReducerDefV10> Reducers,\n        System.Collections.Generic.List<RawProcedureDefV10> Procedures,\n        System.Collections.Generic.List<RawViewDefV10> Views,\n        System.Collections.Generic.List<RawScheduleDefV10> Schedules,\n        System.Collections.Generic.List<RawLifeCycleReducerDefV10> LifeCycleReducers,\n        System.Collections.Generic.List<RawRowLevelSecurityDefV9> RowLevelSecurity,\n        SpacetimeDB.CaseConversionPolicy CaseConversionPolicy,\n        ExplicitNames ExplicitNames\n    )>;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawModuleDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawModuleDefV8\n    {\n        [DataMember(Name = \"typespace\")]\n        public Typespace Typespace;\n        [DataMember(Name = \"tables\")]\n        public System.Collections.Generic.List<TableDesc> Tables;\n        [DataMember(Name = \"reducers\")]\n        public System.Collections.Generic.List<ReducerDef> Reducers;\n        [DataMember(Name = \"misc_exports\")]\n        public System.Collections.Generic.List<MiscModuleExport> MiscExports;\n\n        public RawModuleDefV8(\n            Typespace Typespace,\n            System.Collections.Generic.List<TableDesc> Tables,\n            System.Collections.Generic.List<ReducerDef> Reducers,\n            System.Collections.Generic.List<MiscModuleExport> MiscExports\n        )\n        {\n            this.Typespace = Typespace;\n            this.Tables = Tables;\n            this.Reducers = Reducers;\n            this.MiscExports = MiscExports;\n        }\n\n        public RawModuleDefV8()\n        {\n            this.Typespace = new();\n            this.Tables = new();\n            this.Reducers = new();\n            this.MiscExports = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawModuleDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawModuleDefV9\n    {\n        [DataMember(Name = \"typespace\")]\n        public Typespace Typespace;\n        [DataMember(Name = \"tables\")]\n        public System.Collections.Generic.List<RawTableDefV9> Tables;\n        [DataMember(Name = \"reducers\")]\n        public System.Collections.Generic.List<RawReducerDefV9> Reducers;\n        [DataMember(Name = \"types\")]\n        public System.Collections.Generic.List<RawTypeDefV9> Types;\n        [DataMember(Name = \"misc_exports\")]\n        public System.Collections.Generic.List<RawMiscModuleExportV9> MiscExports;\n        [DataMember(Name = \"row_level_security\")]\n        public System.Collections.Generic.List<RawRowLevelSecurityDefV9> RowLevelSecurity;\n\n        public RawModuleDefV9(\n            Typespace Typespace,\n            System.Collections.Generic.List<RawTableDefV9> Tables,\n            System.Collections.Generic.List<RawReducerDefV9> Reducers,\n            System.Collections.Generic.List<RawTypeDefV9> Types,\n            System.Collections.Generic.List<RawMiscModuleExportV9> MiscExports,\n            System.Collections.Generic.List<RawRowLevelSecurityDefV9> RowLevelSecurity\n        )\n        {\n            this.Typespace = Typespace;\n            this.Tables = Tables;\n            this.Reducers = Reducers;\n            this.Types = Types;\n            this.MiscExports = MiscExports;\n            this.RowLevelSecurity = RowLevelSecurity;\n        }\n\n        public RawModuleDefV9()\n        {\n            this.Typespace = new();\n            this.Tables = new();\n            this.Reducers = new();\n            this.Types = new();\n            this.MiscExports = new();\n            this.RowLevelSecurity = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawProcedureDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawProcedureDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ReturnType;\n        [DataMember(Name = \"visibility\")]\n        public FunctionVisibility Visibility;\n\n        public RawProcedureDefV10(\n            string SourceName,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            SpacetimeDB.BSATN.AlgebraicType ReturnType,\n            FunctionVisibility Visibility\n        )\n        {\n            this.SourceName = SourceName;\n            this.Params = Params;\n            this.ReturnType = ReturnType;\n            this.Visibility = Visibility;\n        }\n\n        public RawProcedureDefV10()\n        {\n            this.SourceName = \"\";\n            this.Params = new();\n            this.ReturnType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawProcedureDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawProcedureDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ReturnType;\n\n        public RawProcedureDefV9(\n            string Name,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            SpacetimeDB.BSATN.AlgebraicType ReturnType\n        )\n        {\n            this.Name = Name;\n            this.Params = Params;\n            this.ReturnType = ReturnType;\n        }\n\n        public RawProcedureDefV9()\n        {\n            this.Name = \"\";\n            this.Params = new();\n            this.ReturnType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawReducerDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawReducerDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"visibility\")]\n        public FunctionVisibility Visibility;\n        [DataMember(Name = \"ok_return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType OkReturnType;\n        [DataMember(Name = \"err_return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ErrReturnType;\n\n        public RawReducerDefV10(\n            string SourceName,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            FunctionVisibility Visibility,\n            SpacetimeDB.BSATN.AlgebraicType OkReturnType,\n            SpacetimeDB.BSATN.AlgebraicType ErrReturnType\n        )\n        {\n            this.SourceName = SourceName;\n            this.Params = Params;\n            this.Visibility = Visibility;\n            this.OkReturnType = OkReturnType;\n            this.ErrReturnType = ErrReturnType;\n        }\n\n        public RawReducerDefV10()\n        {\n            this.SourceName = \"\";\n            this.Params = new();\n            this.OkReturnType = null!;\n            this.ErrReturnType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawReducerDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawReducerDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"lifecycle\")]\n        public Lifecycle? Lifecycle;\n\n        public RawReducerDefV9(\n            string Name,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            Lifecycle? Lifecycle\n        )\n        {\n            this.Name = Name;\n            this.Params = Params;\n            this.Lifecycle = Lifecycle;\n        }\n\n        public RawReducerDefV9()\n        {\n            this.Name = \"\";\n            this.Params = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawRowLevelSecurityDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawRowLevelSecurityDefV9\n    {\n        [DataMember(Name = \"sql\")]\n        public string Sql;\n\n        public RawRowLevelSecurityDefV9(string Sql)\n        {\n            this.Sql = Sql;\n        }\n\n        public RawRowLevelSecurityDefV9()\n        {\n            this.Sql = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawScheduleDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string? SourceName;\n        [DataMember(Name = \"table_name\")]\n        public string TableName;\n        [DataMember(Name = \"schedule_at_col\")]\n        public ushort ScheduleAtCol;\n        [DataMember(Name = \"function_name\")]\n        public string FunctionName;\n\n        public RawScheduleDefV10(\n            string? SourceName,\n            string TableName,\n            ushort ScheduleAtCol,\n            string FunctionName\n        )\n        {\n            this.SourceName = SourceName;\n            this.TableName = TableName;\n            this.ScheduleAtCol = ScheduleAtCol;\n            this.FunctionName = FunctionName;\n        }\n\n        public RawScheduleDefV10()\n        {\n            this.TableName = \"\";\n            this.FunctionName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawScheduleDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawScheduleDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string? Name;\n        [DataMember(Name = \"reducer_name\")]\n        public string ReducerName;\n        [DataMember(Name = \"scheduled_at_column\")]\n        public ushort ScheduledAtColumn;\n\n        public RawScheduleDefV9(\n            string? Name,\n            string ReducerName,\n            ushort ScheduledAtColumn\n        )\n        {\n            this.Name = Name;\n            this.ReducerName = ReducerName;\n            this.ScheduledAtColumn = ScheduledAtColumn;\n        }\n\n        public RawScheduleDefV9()\n        {\n            this.ReducerName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawScopedTypeNameV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawScopedTypeNameV10\n    {\n        [DataMember(Name = \"scope\")]\n        public System.Collections.Generic.List<string> Scope;\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n\n        public RawScopedTypeNameV10(\n            System.Collections.Generic.List<string> Scope,\n            string SourceName\n        )\n        {\n            this.Scope = Scope;\n            this.SourceName = SourceName;\n        }\n\n        public RawScopedTypeNameV10()\n        {\n            this.Scope = new();\n            this.SourceName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawScopedTypeNameV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawScopedTypeNameV9\n    {\n        [DataMember(Name = \"scope\")]\n        public System.Collections.Generic.List<string> Scope;\n        [DataMember(Name = \"name\")]\n        public string Name;\n\n        public RawScopedTypeNameV9(\n            System.Collections.Generic.List<string> Scope,\n            string Name\n        )\n        {\n            this.Scope = Scope;\n            this.Name = Name;\n        }\n\n        public RawScopedTypeNameV9()\n        {\n            this.Scope = new();\n            this.Name = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawSequenceDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawSequenceDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string? SourceName;\n        [DataMember(Name = \"column\")]\n        public ushort Column;\n        [DataMember(Name = \"start\")]\n        public I128? Start;\n        [DataMember(Name = \"min_value\")]\n        public I128? MinValue;\n        [DataMember(Name = \"max_value\")]\n        public I128? MaxValue;\n        [DataMember(Name = \"increment\")]\n        public I128 Increment;\n\n        public RawSequenceDefV10(\n            string? SourceName,\n            ushort Column,\n            I128? Start,\n            I128? MinValue,\n            I128? MaxValue,\n            I128 Increment\n        )\n        {\n            this.SourceName = SourceName;\n            this.Column = Column;\n            this.Start = Start;\n            this.MinValue = MinValue;\n            this.MaxValue = MaxValue;\n            this.Increment = Increment;\n        }\n\n        public RawSequenceDefV10()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawSequenceDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawSequenceDefV8\n    {\n        [DataMember(Name = \"sequence_name\")]\n        public string SequenceName;\n        [DataMember(Name = \"col_pos\")]\n        public ushort ColPos;\n        [DataMember(Name = \"increment\")]\n        public I128 Increment;\n        [DataMember(Name = \"start\")]\n        public I128? Start;\n        [DataMember(Name = \"min_value\")]\n        public I128? MinValue;\n        [DataMember(Name = \"max_value\")]\n        public I128? MaxValue;\n        [DataMember(Name = \"allocated\")]\n        public I128 Allocated;\n\n        public RawSequenceDefV8(\n            string SequenceName,\n            ushort ColPos,\n            I128 Increment,\n            I128? Start,\n            I128? MinValue,\n            I128? MaxValue,\n            I128 Allocated\n        )\n        {\n            this.SequenceName = SequenceName;\n            this.ColPos = ColPos;\n            this.Increment = Increment;\n            this.Start = Start;\n            this.MinValue = MinValue;\n            this.MaxValue = MaxValue;\n            this.Allocated = Allocated;\n        }\n\n        public RawSequenceDefV8()\n        {\n            this.SequenceName = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawSequenceDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawSequenceDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string? Name;\n        [DataMember(Name = \"column\")]\n        public ushort Column;\n        [DataMember(Name = \"start\")]\n        public I128? Start;\n        [DataMember(Name = \"min_value\")]\n        public I128? MinValue;\n        [DataMember(Name = \"max_value\")]\n        public I128? MaxValue;\n        [DataMember(Name = \"increment\")]\n        public I128 Increment;\n\n        public RawSequenceDefV9(\n            string? Name,\n            ushort Column,\n            I128? Start,\n            I128? MinValue,\n            I128? MaxValue,\n            I128 Increment\n        )\n        {\n            this.Name = Name;\n            this.Column = Column;\n            this.Start = Start;\n            this.MinValue = MinValue;\n            this.MaxValue = MaxValue;\n            this.Increment = Increment;\n        }\n\n        public RawSequenceDefV9()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawTableDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n        [DataMember(Name = \"product_type_ref\")]\n        public uint ProductTypeRef;\n        [DataMember(Name = \"primary_key\")]\n        public System.Collections.Generic.List<ushort> PrimaryKey;\n        [DataMember(Name = \"indexes\")]\n        public System.Collections.Generic.List<RawIndexDefV10> Indexes;\n        [DataMember(Name = \"constraints\")]\n        public System.Collections.Generic.List<RawConstraintDefV10> Constraints;\n        [DataMember(Name = \"sequences\")]\n        public System.Collections.Generic.List<RawSequenceDefV10> Sequences;\n        [DataMember(Name = \"table_type\")]\n        public TableType TableType;\n        [DataMember(Name = \"table_access\")]\n        public TableAccess TableAccess;\n        [DataMember(Name = \"default_values\")]\n        public System.Collections.Generic.List<RawColumnDefaultValueV10> DefaultValues;\n        [DataMember(Name = \"is_event\")]\n        public bool IsEvent;\n\n        public RawTableDefV10(\n            string SourceName,\n            uint ProductTypeRef,\n            System.Collections.Generic.List<ushort> PrimaryKey,\n            System.Collections.Generic.List<RawIndexDefV10> Indexes,\n            System.Collections.Generic.List<RawConstraintDefV10> Constraints,\n            System.Collections.Generic.List<RawSequenceDefV10> Sequences,\n            TableType TableType,\n            TableAccess TableAccess,\n            System.Collections.Generic.List<RawColumnDefaultValueV10> DefaultValues,\n            bool IsEvent\n        )\n        {\n            this.SourceName = SourceName;\n            this.ProductTypeRef = ProductTypeRef;\n            this.PrimaryKey = PrimaryKey;\n            this.Indexes = Indexes;\n            this.Constraints = Constraints;\n            this.Sequences = Sequences;\n            this.TableType = TableType;\n            this.TableAccess = TableAccess;\n            this.DefaultValues = DefaultValues;\n            this.IsEvent = IsEvent;\n        }\n\n        public RawTableDefV10()\n        {\n            this.SourceName = \"\";\n            this.PrimaryKey = new();\n            this.Indexes = new();\n            this.Constraints = new();\n            this.Sequences = new();\n            this.DefaultValues = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV8.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawTableDefV8\n    {\n        [DataMember(Name = \"table_name\")]\n        public string TableName;\n        [DataMember(Name = \"columns\")]\n        public System.Collections.Generic.List<RawColumnDefV8> Columns;\n        [DataMember(Name = \"indexes\")]\n        public System.Collections.Generic.List<RawIndexDefV8> Indexes;\n        [DataMember(Name = \"constraints\")]\n        public System.Collections.Generic.List<RawConstraintDefV8> Constraints;\n        [DataMember(Name = \"sequences\")]\n        public System.Collections.Generic.List<RawSequenceDefV8> Sequences;\n        [DataMember(Name = \"table_type\")]\n        public string TableType;\n        [DataMember(Name = \"table_access\")]\n        public string TableAccess;\n        [DataMember(Name = \"scheduled\")]\n        public string? Scheduled;\n\n        public RawTableDefV8(\n            string TableName,\n            System.Collections.Generic.List<RawColumnDefV8> Columns,\n            System.Collections.Generic.List<RawIndexDefV8> Indexes,\n            System.Collections.Generic.List<RawConstraintDefV8> Constraints,\n            System.Collections.Generic.List<RawSequenceDefV8> Sequences,\n            string TableType,\n            string TableAccess,\n            string? Scheduled\n        )\n        {\n            this.TableName = TableName;\n            this.Columns = Columns;\n            this.Indexes = Indexes;\n            this.Constraints = Constraints;\n            this.Sequences = Sequences;\n            this.TableType = TableType;\n            this.TableAccess = TableAccess;\n            this.Scheduled = Scheduled;\n        }\n\n        public RawTableDefV8()\n        {\n            this.TableName = \"\";\n            this.Columns = new();\n            this.Indexes = new();\n            this.Constraints = new();\n            this.Sequences = new();\n            this.TableType = \"\";\n            this.TableAccess = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawTableDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawTableDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"product_type_ref\")]\n        public uint ProductTypeRef;\n        [DataMember(Name = \"primary_key\")]\n        public System.Collections.Generic.List<ushort> PrimaryKey;\n        [DataMember(Name = \"indexes\")]\n        public System.Collections.Generic.List<RawIndexDefV9> Indexes;\n        [DataMember(Name = \"constraints\")]\n        public System.Collections.Generic.List<RawConstraintDefV9> Constraints;\n        [DataMember(Name = \"sequences\")]\n        public System.Collections.Generic.List<RawSequenceDefV9> Sequences;\n        [DataMember(Name = \"schedule\")]\n        public RawScheduleDefV9? Schedule;\n        [DataMember(Name = \"table_type\")]\n        public TableType TableType;\n        [DataMember(Name = \"table_access\")]\n        public TableAccess TableAccess;\n\n        public RawTableDefV9(\n            string Name,\n            uint ProductTypeRef,\n            System.Collections.Generic.List<ushort> PrimaryKey,\n            System.Collections.Generic.List<RawIndexDefV9> Indexes,\n            System.Collections.Generic.List<RawConstraintDefV9> Constraints,\n            System.Collections.Generic.List<RawSequenceDefV9> Sequences,\n            RawScheduleDefV9? Schedule,\n            TableType TableType,\n            TableAccess TableAccess\n        )\n        {\n            this.Name = Name;\n            this.ProductTypeRef = ProductTypeRef;\n            this.PrimaryKey = PrimaryKey;\n            this.Indexes = Indexes;\n            this.Constraints = Constraints;\n            this.Sequences = Sequences;\n            this.Schedule = Schedule;\n            this.TableType = TableType;\n            this.TableAccess = TableAccess;\n        }\n\n        public RawTableDefV9()\n        {\n            this.Name = \"\";\n            this.PrimaryKey = new();\n            this.Indexes = new();\n            this.Constraints = new();\n            this.Sequences = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawTypeDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawTypeDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public RawScopedTypeNameV10 SourceName;\n        [DataMember(Name = \"ty\")]\n        public uint Ty;\n        [DataMember(Name = \"custom_ordering\")]\n        public bool CustomOrdering;\n\n        public RawTypeDefV10(\n            RawScopedTypeNameV10 SourceName,\n            uint Ty,\n            bool CustomOrdering\n        )\n        {\n            this.SourceName = SourceName;\n            this.Ty = Ty;\n            this.CustomOrdering = CustomOrdering;\n        }\n\n        public RawTypeDefV10()\n        {\n            this.SourceName = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawTypeDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawTypeDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public RawScopedTypeNameV9 Name;\n        [DataMember(Name = \"ty\")]\n        public uint Ty;\n        [DataMember(Name = \"custom_ordering\")]\n        public bool CustomOrdering;\n\n        public RawTypeDefV9(\n            RawScopedTypeNameV9 Name,\n            uint Ty,\n            bool CustomOrdering\n        )\n        {\n            this.Name = Name;\n            this.Ty = Ty;\n            this.CustomOrdering = CustomOrdering;\n        }\n\n        public RawTypeDefV9()\n        {\n            this.Name = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawUniqueConstraintDataV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawUniqueConstraintDataV9\n    {\n        [DataMember(Name = \"columns\")]\n        public System.Collections.Generic.List<ushort> Columns;\n\n        public RawUniqueConstraintDataV9(System.Collections.Generic.List<ushort> Columns)\n        {\n            this.Columns = Columns;\n        }\n\n        public RawUniqueConstraintDataV9()\n        {\n            this.Columns = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawViewDefV10.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawViewDefV10\n    {\n        [DataMember(Name = \"source_name\")]\n        public string SourceName;\n        [DataMember(Name = \"index\")]\n        public uint Index;\n        [DataMember(Name = \"is_public\")]\n        public bool IsPublic;\n        [DataMember(Name = \"is_anonymous\")]\n        public bool IsAnonymous;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ReturnType;\n\n        public RawViewDefV10(\n            string SourceName,\n            uint Index,\n            bool IsPublic,\n            bool IsAnonymous,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            SpacetimeDB.BSATN.AlgebraicType ReturnType\n        )\n        {\n            this.SourceName = SourceName;\n            this.Index = Index;\n            this.IsPublic = IsPublic;\n            this.IsAnonymous = IsAnonymous;\n            this.Params = Params;\n            this.ReturnType = ReturnType;\n        }\n\n        public RawViewDefV10()\n        {\n            this.SourceName = \"\";\n            this.Params = new();\n            this.ReturnType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/RawViewDefV9.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RawViewDefV9\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"index\")]\n        public uint Index;\n        [DataMember(Name = \"is_public\")]\n        public bool IsPublic;\n        [DataMember(Name = \"is_anonymous\")]\n        public bool IsAnonymous;\n        [DataMember(Name = \"params\")]\n        public List<SpacetimeDB.BSATN.AggregateElement> Params;\n        [DataMember(Name = \"return_type\")]\n        public SpacetimeDB.BSATN.AlgebraicType ReturnType;\n\n        public RawViewDefV9(\n            string Name,\n            uint Index,\n            bool IsPublic,\n            bool IsAnonymous,\n            List<SpacetimeDB.BSATN.AggregateElement> Params,\n            SpacetimeDB.BSATN.AlgebraicType ReturnType\n        )\n        {\n            this.Name = Name;\n            this.Index = Index;\n            this.IsPublic = IsPublic;\n            this.IsAnonymous = IsAnonymous;\n            this.Params = Params;\n            this.ReturnType = ReturnType;\n        }\n\n        public RawViewDefV9()\n        {\n            this.Name = \"\";\n            this.Params = new();\n            this.ReturnType = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/ReducerDef.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class ReducerDef\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"args\")]\n        public System.Collections.Generic.List<SpacetimeDB.BSATN.AggregateElement> Args;\n\n        public ReducerDef(\n            string Name,\n            System.Collections.Generic.List<SpacetimeDB.BSATN.AggregateElement> Args\n        )\n        {\n            this.Name = Name;\n            this.Args = Args;\n        }\n\n        public ReducerDef()\n        {\n            this.Name = \"\";\n            this.Args = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/TableAccess.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public enum TableAccess\n    {\n        Public,\n        Private,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/TableDesc.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TableDesc\n    {\n        [DataMember(Name = \"schema\")]\n        public RawTableDefV8 Schema;\n        [DataMember(Name = \"data\")]\n        public uint Data;\n\n        public TableDesc(\n            RawTableDefV8 Schema,\n            uint Data\n        )\n        {\n            this.Schema = Schema;\n            this.Data = Data;\n        }\n\n        public TableDesc()\n        {\n            this.Schema = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/TableType.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    public enum TableType\n    {\n        System,\n        User,\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/TypeAlias.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TypeAlias\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"ty\")]\n        public uint Ty;\n\n        public TypeAlias(\n            string Name,\n            uint Ty\n        )\n        {\n            this.Name = Name;\n            this.Ty = Ty;\n        }\n\n        public TypeAlias()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Autogen/Typespace.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Internal\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Typespace\n    {\n        [DataMember(Name = \"types\")]\n        public System.Collections.Generic.List<SpacetimeDB.BSATN.AlgebraicType> Types;\n\n        public Typespace(System.Collections.Generic.List<SpacetimeDB.BSATN.AlgebraicType> Types)\n        {\n            this.Types = Types;\n        }\n\n        public Typespace()\n        {\n            this.Types = new();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Bounds.cs",
    "content": "using System.IO;\nusing SpacetimeDB.BSATN;\n\nnamespace SpacetimeDB\n{\n    public readonly struct Bound<T>(T min, T max)\n    {\n        public T Min => min;\n        public T Max => max;\n\n        public static implicit operator Bound<T>(T value) => new(value, value);\n\n        public static implicit operator Bound<T>((T min, T max) span) => new(span.min, span.max);\n    }\n}\n\nnamespace SpacetimeDB.Internal\n{\n    enum BoundVariant : byte\n    {\n        Inclusive,\n        Exclusive,\n        Unbounded,\n    }\n\n    public interface IBTreeIndexBounds\n    {\n        ushort PrefixElems { get; }\n        void Prefix(BinaryWriter w);\n        void RStart(BinaryWriter w);\n        void REnd(BinaryWriter w);\n    }\n\n    public readonly struct Bound<T>(T min, T max)\n    {\n        public T Min => min;\n        public T Max => max;\n\n        public static implicit operator Bound<T>(T value) => new(value, value);\n\n        public static implicit operator Bound<T>((T min, T max) span) => new(span.min, span.max);\n\n        public static implicit operator SpacetimeDB.Bound<T>(Bound<T> value) =>\n            new(value.Min, value.Max);\n\n        public static implicit operator Bound<T>(SpacetimeDB.Bound<T> value) =>\n            new(value.Min, value.Max);\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW>(SpacetimeDB.Bound<T> t) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n    {\n        public ushort PrefixElems => 0;\n\n        public void Prefix(BinaryWriter _) { }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new TRW().Write(w, t.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new TRW().Write(w, t.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW>((T t, SpacetimeDB.Bound<U> u) b)\n        : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n    {\n        public ushort PrefixElems => 1;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new URW().Write(w, b.u.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new URW().Write(w, b.u.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW>(\n        (T t, U u, SpacetimeDB.Bound<V> v) b\n    ) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n    {\n        public ushort PrefixElems => 2;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new VRW().Write(w, b.v.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new VRW().Write(w, b.v.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW>(\n        (T t, U u, V v, SpacetimeDB.Bound<W> w) b\n    ) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n    {\n        public ushort PrefixElems => 3;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new WRW().Write(w, b.w.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new WRW().Write(w, b.w.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW>(\n        (T t, U u, V v, W w, SpacetimeDB.Bound<X> x) b\n    ) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n    {\n        public ushort PrefixElems => 4;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new XRW().Write(w, b.x.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new XRW().Write(w, b.x.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW, Y, YRW>(\n        (T t, U u, V v, W w, X x, SpacetimeDB.Bound<Y> y) b\n    ) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n        where YRW : struct, IReadWrite<Y>\n    {\n        public ushort PrefixElems => 5;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n            new XRW().Write(w, b.x);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new YRW().Write(w, b.y.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new YRW().Write(w, b.y.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<T, TRW, U, URW, V, VRW, W, WRW, X, XRW, Y, YRW, Z, ZRW>(\n        (T t, U u, V v, W w, X x, Y y, SpacetimeDB.Bound<Z> z) b\n    ) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n        where YRW : struct, IReadWrite<Y>\n        where ZRW : struct, IReadWrite<Z>\n    {\n        public ushort PrefixElems => 6;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n            new XRW().Write(w, b.x);\n            new YRW().Write(w, b.y);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new ZRW().Write(w, b.z.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new ZRW().Write(w, b.z.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<\n        T,\n        TRW,\n        U,\n        URW,\n        V,\n        VRW,\n        W,\n        WRW,\n        X,\n        XRW,\n        Y,\n        YRW,\n        Z,\n        ZRW,\n        A,\n        ARW\n    >((T t, U u, V v, W w, X x, Y y, Z z, SpacetimeDB.Bound<A> a) b) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n        where YRW : struct, IReadWrite<Y>\n        where ZRW : struct, IReadWrite<Z>\n        where ARW : struct, IReadWrite<A>\n    {\n        public ushort PrefixElems => 7;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n            new XRW().Write(w, b.x);\n            new YRW().Write(w, b.y);\n            new ZRW().Write(w, b.z);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new ARW().Write(w, b.a.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new ARW().Write(w, b.a.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<\n        T,\n        TRW,\n        U,\n        URW,\n        V,\n        VRW,\n        W,\n        WRW,\n        X,\n        XRW,\n        Y,\n        YRW,\n        Z,\n        ZRW,\n        A,\n        ARW,\n        B,\n        BRW\n    >((T t, U u, V v, W w, X x, Y y, Z z, A a, SpacetimeDB.Bound<B> b) b) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n        where YRW : struct, IReadWrite<Y>\n        where ZRW : struct, IReadWrite<Z>\n        where ARW : struct, IReadWrite<A>\n        where BRW : struct, IReadWrite<B>\n    {\n        public ushort PrefixElems => 8;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n            new XRW().Write(w, b.x);\n            new YRW().Write(w, b.y);\n            new ZRW().Write(w, b.z);\n            new ARW().Write(w, b.a);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new BRW().Write(w, b.b.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new BRW().Write(w, b.b.Max);\n        }\n    }\n\n    public readonly struct BTreeIndexBounds<\n        T,\n        TRW,\n        U,\n        URW,\n        V,\n        VRW,\n        W,\n        WRW,\n        X,\n        XRW,\n        Y,\n        YRW,\n        Z,\n        ZRW,\n        A,\n        ARW,\n        B,\n        BRW,\n        C,\n        CRW\n    >((T t, U u, V v, W w, X x, Y y, Z z, A a, B b, SpacetimeDB.Bound<C> c) b) : IBTreeIndexBounds\n        where TRW : struct, IReadWrite<T>\n        where URW : struct, IReadWrite<U>\n        where VRW : struct, IReadWrite<V>\n        where WRW : struct, IReadWrite<W>\n        where XRW : struct, IReadWrite<X>\n        where YRW : struct, IReadWrite<Y>\n        where ZRW : struct, IReadWrite<Z>\n        where ARW : struct, IReadWrite<A>\n        where BRW : struct, IReadWrite<B>\n        where CRW : struct, IReadWrite<C>\n    {\n        public ushort PrefixElems => 9;\n\n        public void Prefix(BinaryWriter w)\n        {\n            new TRW().Write(w, b.t);\n            new URW().Write(w, b.u);\n            new VRW().Write(w, b.v);\n            new WRW().Write(w, b.w);\n            new XRW().Write(w, b.x);\n            new YRW().Write(w, b.y);\n            new ZRW().Write(w, b.z);\n            new ARW().Write(w, b.a);\n            new BRW().Write(w, b.b);\n        }\n\n        public void RStart(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new CRW().Write(w, b.c.Min);\n        }\n\n        public void REnd(BinaryWriter w)\n        {\n            w.Write((byte)BoundVariant.Inclusive);\n            new CRW().Write(w, b.c.Max);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/FFI.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System.Runtime.InteropServices;\nusing System.Runtime.InteropServices.Marshalling;\n\n// This type is outside of the hidden `FFI` class because for now we need to do some public\n// forwarding in the codegen for `__describe_module__` and `__call_reducer__` exports which both\n// use this type.\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct BytesSource(uint Handle)\n{\n    public static readonly BytesSource INVALID = new(0);\n}\n\n// This type is outside of the hidden `FFI` class because for now we need to do some public\n// forwarding in the codegen for `__describe_module__` and `__call_reducer__` exports which both\n// use this type.\n[StructLayout(LayoutKind.Sequential)]\npublic readonly record struct BytesSink(uint Handle) { }\n\npublic enum Errno : short\n{\n    EXHAUSTED = -1,\n    OK = 0,\n    HOST_CALL_FAILURE = 1,\n    NOT_IN_TRANSACTION = 2,\n    BSATN_DECODE_ERROR = 3,\n    NO_SUCH_TABLE = 4,\n    NO_SUCH_INDEX = 5,\n    NO_SUCH_ITER = 6,\n    NO_SUCH_CONSOLE_TIMER = 7,\n    NO_SUCH_BYTES = 8,\n    NO_SPACE = 9,\n    BUFFER_TOO_SMALL = 11,\n    UNIQUE_ALREADY_EXISTS = 12,\n    SCHEDULE_AT_DELAY_TOO_LONG = 13,\n    INDEX_NOT_UNIQUE = 14,\n    NO_SUCH_ROW = 15,\n    AUTO_INC_OVERFLOW = 16,\n    WOULD_BLOCK_TRANSACTION = 17,\n    TRANSACTION_NOT_ANONYMOUS = 18,\n    TRANSACTION_IS_READ_ONLY = 19,\n    TRANSACTION_IS_MUT = 20,\n    HTTP_ERROR = 21,\n}\n\n#pragma warning disable IDE1006 // Naming Styles - Not applicable to FFI stuff.\ninternal static partial class FFI\n{\n    // For now this must match the name of the `.c` file (`bindings.c`).\n    // In the future C# will allow to specify Wasm import namespace in\n    // `LibraryImport` directly.\n    const string StdbNamespace10_0 =\n#if EXPERIMENTAL_WASM_AOT\n        \"spacetime_10.0\"\n#else\n        \"bindings\"\n#endif\n    ;\n\n    const string StdbNamespace10_1 =\n#if EXPERIMENTAL_WASM_AOT\n        \"spacetime_10.1\"\n#else\n        \"bindings\"\n#endif\n    ;\n\n    const string StdbNamespace10_2 =\n#if EXPERIMENTAL_WASM_AOT\n        \"spacetime_10.2\"\n#else\n        \"bindings\"\n#endif\n    ;\n\n    const string StdbNamespace10_3 =\n#if EXPERIMENTAL_WASM_AOT\n        \"spacetime_10.3\"\n#else\n        \"bindings\"\n#endif\n    ;\n\n    const string StdbNamespace10_4 =\n#if EXPERIMENTAL_WASM_AOT\n        \"spacetime_10.4\"\n#else\n        \"bindings\"\n#endif\n    ;\n\n    [NativeMarshalling(typeof(Marshaller))]\n    public struct CheckedStatus\n    {\n        // This custom marshaller takes care of checking the status code\n        // returned from the host and throwing an exception if it's not 0.\n        // The only reason it doesn't return `void` is because the C# compiler\n        // doesn't treat `void` as a real type and doesn't allow it to be returned\n        // from custom marshallers, so we resort to an empty struct instead.\n        [CustomMarshaller(\n            typeof(CheckedStatus),\n            MarshalMode.ManagedToUnmanagedOut,\n            typeof(Marshaller)\n        )]\n        internal static class Marshaller\n        {\n            public static CheckedStatus ConvertToManaged(Errno status)\n            {\n                ErrnoHelpers.ThrowIfError(status);\n                return default;\n            }\n        }\n    }\n\n    internal static class ErrnoHelpers\n    {\n        public static void ThrowIfError(Errno status)\n        {\n            if (status == Errno.OK)\n            {\n                return;\n            }\n\n            throw ToException(status);\n        }\n\n        public static Exception ToException(Errno status) =>\n            status switch\n            {\n                Errno.NOT_IN_TRANSACTION => new NotInTransactionException(),\n                Errno.BSATN_DECODE_ERROR => new BsatnDecodeException(),\n                Errno.NO_SUCH_TABLE => new NoSuchTableException(),\n                Errno.NO_SUCH_INDEX => new NoSuchIndexException(),\n                Errno.NO_SUCH_ITER => new NoSuchIterException(),\n                Errno.NO_SUCH_CONSOLE_TIMER => new NoSuchLogStopwatch(),\n                Errno.NO_SUCH_BYTES => new NoSuchBytesException(),\n                Errno.NO_SPACE => new NoSpaceException(),\n                Errno.BUFFER_TOO_SMALL => new BufferTooSmallException(),\n                Errno.UNIQUE_ALREADY_EXISTS => new UniqueConstraintViolationException(),\n                Errno.INDEX_NOT_UNIQUE => new IndexNotUniqueException(),\n                Errno.NO_SUCH_ROW => new NoSuchRowException(),\n                Errno.AUTO_INC_OVERFLOW => new AutoIncOverflowException(),\n                Errno.WOULD_BLOCK_TRANSACTION => new TransactionWouldBlockException(),\n                Errno.TRANSACTION_NOT_ANONYMOUS => new TransactionNotAnonymousException(),\n                Errno.TRANSACTION_IS_READ_ONLY => new TransactionIsReadOnlyException(),\n                Errno.TRANSACTION_IS_MUT => new TransactionIsMutableException(),\n                Errno.HTTP_ERROR => new HttpException(),\n                _ => new UnknownException(status),\n            };\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct TableId\n    {\n        private readonly uint table_id;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct IndexId\n    {\n        private readonly uint index_id;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct ColId(ushort col_id)\n    {\n        private readonly ushort col_id = col_id;\n\n        public static explicit operator ushort(ColId col_id) => col_id.col_id;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct IndexType\n    {\n        private readonly byte index_type;\n    }\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly record struct RowIter(uint Handle)\n    {\n        public static readonly RowIter INVALID = new(0);\n    }\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus table_id_from_name(\n        [In] byte[] name,\n        uint name_len,\n        out TableId out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus index_id_from_name(\n        [In] byte[] name,\n        uint name_len,\n        out IndexId out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_table_row_count(TableId table_id, out ulong out_);\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_table_scan_bsatn(\n        TableId table_id,\n        out RowIter out_\n    );\n\n    [LibraryImport(StdbNamespace10_4)]\n    public static partial CheckedStatus datastore_index_scan_point_bsatn(\n        IndexId index_id,\n        ReadOnlySpan<byte> point,\n        uint point_len,\n        out RowIter out_\n    );\n\n    [LibraryImport(StdbNamespace10_4)]\n    public static partial CheckedStatus datastore_delete_by_index_scan_point_bsatn(\n        IndexId index_id,\n        ReadOnlySpan<byte> point,\n        uint point_len,\n        out uint out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_index_scan_range_bsatn(\n        IndexId index_id,\n        ReadOnlySpan<byte> prefix,\n        uint prefix_len,\n        ColId prefix_elems,\n        ReadOnlySpan<byte> rstart,\n        uint rstart_len,\n        ReadOnlySpan<byte> rend,\n        uint rend_len,\n        out RowIter out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial Errno row_iter_bsatn_advance(\n        RowIter iter_handle,\n        [MarshalUsing(CountElementName = nameof(buffer_len))] [Out] byte[] buffer,\n        ref uint buffer_len\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus row_iter_bsatn_close(RowIter iter_handle);\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_insert_bsatn(\n        TableId table_id,\n        Span<byte> row,\n        ref uint row_len\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_update_bsatn(\n        TableId table_id,\n        IndexId index_id,\n        Span<byte> row,\n        ref uint row_len\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_delete_by_index_scan_range_bsatn(\n        IndexId index_id,\n        ReadOnlySpan<byte> prefix,\n        uint prefix_len,\n        ColId prefix_elems,\n        ReadOnlySpan<byte> rstart,\n        uint rstart_len,\n        ReadOnlySpan<byte> rend,\n        uint rend_len,\n        out uint out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus datastore_delete_all_by_eq_bsatn(\n        TableId table_id,\n        [In] byte[] relation,\n        uint relation_len,\n        out uint out_\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial Errno bytes_source_read(\n        BytesSource source,\n        Span<byte> buffer,\n        ref uint buffer_len\n    );\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus bytes_sink_write(\n        BytesSink sink,\n        ReadOnlySpan<byte> buffer,\n        ref uint buffer_len\n    );\n\n    public enum LogLevel : byte\n    {\n        Error = 0,\n        Warn = 1,\n        Info = 2,\n        Debug = 3,\n        Trace = 4,\n        Panic = 5,\n    }\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial void console_log(\n        LogLevel level,\n        [In] byte[] target,\n        uint target_len,\n        [In] byte[] filename,\n        uint filename_len,\n        uint line_number,\n        [In] byte[] message,\n        uint message_len\n    );\n\n    [NativeMarshalling(typeof(ConsoleTimerIdMarshaller))]\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct ConsoleTimerId\n    {\n        private readonly uint timer_id;\n\n        private ConsoleTimerId(uint id)\n        {\n            timer_id = id;\n        }\n\n        //LayoutKind.Sequential is apparently not enough for this struct to be returnable in PInvoke, so we need a custom marshaller unfortunately\n        [CustomMarshaller(\n            typeof(ConsoleTimerId),\n            MarshalMode.Default,\n            typeof(ConsoleTimerIdMarshaller)\n        )]\n        internal static class ConsoleTimerIdMarshaller\n        {\n            public static ConsoleTimerId ConvertToManaged(uint id) => new ConsoleTimerId(id);\n\n            public static uint ConvertToUnmanaged(ConsoleTimerId id) => id.timer_id;\n        }\n    }\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial ConsoleTimerId console_timer_start([In] byte[] name, uint name_len);\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial CheckedStatus console_timer_end(ConsoleTimerId stopwatch_id);\n\n    [LibraryImport(StdbNamespace10_0)]\n    public static partial void volatile_nonatomic_schedule_immediate(\n        [In] byte[] name,\n        uint name_len,\n        [In] byte[] args,\n        uint args_len\n    );\n\n    // Note #1: our Identity type has the same layout as a fixed-size 32-byte little-endian buffer,\n    // so instead of working around C#'s lack of fixed-size arrays, we just accept the pointer to\n    // the Identity itself. In this regard it's different from Rust declaration, but is still\n    // functionally the same.\n    // Note #2: we can't use `LibraryImport` here due to https://github.com/dotnet/runtime/issues/98616\n    // which prevents source-generated PInvokes from working with types from other assemblies, and\n    // `Identity` lives in another assembly (`BSATN.Runtime`). Luckily, `DllImport` is enough here.\n#pragma warning disable SYSLIB1054 // Suppress \"Use 'LibraryImportAttribute' instead of 'DllImportAttribute'\" warning.\n    [DllImport(StdbNamespace10_0)]\n    public static extern void identity(out Identity dest);\n#pragma warning restore SYSLIB1054\n\n    [DllImport(StdbNamespace10_1)]\n    public static extern Errno bytes_source_remaining_length(BytesSource source, ref uint len);\n\n    [DllImport(StdbNamespace10_2)]\n    public static extern Errno get_jwt(ref ConnectionId connectionId, out BytesSource source);\n\n    [LibraryImport(StdbNamespace10_3, EntryPoint = \"procedure_start_mut_tx\")]\n    public static partial Errno procedure_start_mut_tx(out long micros);\n\n    [LibraryImport(StdbNamespace10_3, EntryPoint = \"procedure_commit_mut_tx\")]\n    public static partial Errno procedure_commit_mut_tx();\n\n    [LibraryImport(StdbNamespace10_3, EntryPoint = \"procedure_abort_mut_tx\")]\n    public static partial Errno procedure_abort_mut_tx();\n\n    [StructLayout(LayoutKind.Sequential)]\n    public readonly struct BytesSourcePair\n    {\n        public readonly BytesSource A;\n        public readonly BytesSource B;\n    }\n\n    [LibraryImport(StdbNamespace10_3, EntryPoint = \"procedure_http_request\")]\n    public static partial Errno procedure_http_request(\n        ReadOnlySpan<byte> request,\n        uint request_len,\n        ReadOnlySpan<byte> body,\n        uint body_len,\n        out BytesSourcePair out_\n    );\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/IIndex.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Text;\nusing SpacetimeDB.BSATN;\n\npublic abstract class IndexBase<Row>\n    where Row : IStructuralReadWrite, new()\n{\n    internal readonly FFI.IndexId indexId;\n\n    public IndexBase(string name)\n    {\n        var name_bytes = System.Text.Encoding.UTF8.GetBytes(name);\n        FFI.index_id_from_name(name_bytes, (uint)name_bytes.Length, out indexId);\n    }\n\n    private static void ToParams<Bounds>(\n        Bounds bounds,\n        out FFI.ColId prefixElems,\n        out ReadOnlySpan<byte> prefix,\n        out ReadOnlySpan<byte> rstart,\n        out ReadOnlySpan<byte> rend\n    )\n        where Bounds : IBTreeIndexBounds\n    {\n        prefixElems = new FFI.ColId(bounds.PrefixElems);\n\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        bounds.Prefix(w);\n        var prefix_idx = (int)s.Length;\n        bounds.RStart(w);\n        var rstart_idx = (int)s.Length;\n        bounds.REnd(w);\n        var rend_idx = (int)s.Length;\n\n        var bytes = s.GetBuffer().AsSpan();\n        prefix = bytes[..prefix_idx];\n        rstart = bytes[prefix_idx..rstart_idx];\n        rend = bytes[rstart_idx..rend_idx];\n    }\n\n    protected IEnumerable<Row> DoFilter<Bounds>(Bounds bounds)\n        where Bounds : IBTreeIndexBounds => new RawTableIter<Bounds>(indexId, bounds).Parse();\n\n    protected uint DoDelete<Bounds>(Bounds bounds)\n        where Bounds : IBTreeIndexBounds\n    {\n        ToParams(bounds, out var prefixElems, out var prefix, out var rstart, out var rend);\n        FFI.datastore_delete_by_index_scan_range_bsatn(\n            indexId,\n            prefix,\n            (uint)prefix.Length,\n            prefixElems,\n            rstart,\n            (uint)rstart.Length,\n            rend,\n            (uint)rend.Length,\n            out var out_\n        );\n        return out_;\n    }\n\n    private class RawTableIter<Bounds>(FFI.IndexId indexId, Bounds bounds) : RawTableIterBase<Row>\n        where Bounds : IBTreeIndexBounds\n    {\n        protected override void IterStart(out FFI.RowIter handle)\n        {\n            ToParams(bounds, out var prefixElems, out var prefix, out var rstart, out var rend);\n            FFI.datastore_index_scan_range_bsatn(\n                indexId,\n                prefix,\n                (uint)prefix.Length,\n                prefixElems,\n                rstart,\n                (uint)rstart.Length,\n                rend,\n                (uint)rend.Length,\n                out handle\n            );\n        }\n    }\n}\n\npublic abstract class ReadOnlyIndexBase<Row>(string name) : IndexBase<Row>(name)\n    where Row : IStructuralReadWrite, new()\n{\n    protected IEnumerable<Row> Filter<Bounds>(Bounds bounds)\n        where Bounds : IBTreeIndexBounds => DoFilter(bounds);\n}\n\npublic abstract class UniqueIndex<Handle, Row, T, RW>(string name) : IndexBase<Row>(name)\n    where Handle : ITableView<Handle, Row>\n    where Row : struct, IStructuralReadWrite\n    where RW : struct, BSATN.IReadWrite<T>\n{\n    private static BTreeIndexBounds<T, RW> ToBounds(T key) => new(key);\n\n    private sealed class RawPointIter(FFI.IndexId indexId, byte[] point) : RawTableIterBase<Row>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_index_scan_point_bsatn(indexId, point, (uint)point.Length, out handle);\n    }\n\n    protected IEnumerable<Row> DoFilter(T key) => DoFilter(ToBounds(key));\n\n    public bool Delete(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n        FFI.datastore_delete_by_index_scan_point_bsatn(\n            indexId,\n            point,\n            (uint)point.Length,\n            out var numDeleted\n        );\n        return numDeleted > 0;\n    }\n\n    protected Row? FindSingle(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n\n        using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();\n        if (!e.MoveNext())\n        {\n            return null;\n        }\n\n        var row = e.Current;\n        if (e.MoveNext())\n        {\n            throw new InvalidOperationException(\"Unique index point scan returned >1 rows\");\n        }\n\n        return row;\n    }\n\n    protected Row DoUpdate(Row row)\n    {\n        // Insert the row.\n        var bytes = IStructuralReadWrite.ToBytes(row);\n        var bytes_len = (uint)bytes.Length;\n        FFI.datastore_update_bsatn(ITableView<Handle, Row>.tableId, indexId, bytes, ref bytes_len);\n\n        return ITableView<Handle, Row>.IntegrateGeneratedColumns(row, bytes, bytes_len);\n    }\n}\n\npublic abstract class RefUniqueIndex<Handle, Row, T, RW>(string name) : IndexBase<Row>(name)\n    where Handle : ITableView<Handle, Row>\n    where Row : class, IStructuralReadWrite, new()\n    where RW : struct, BSATN.IReadWrite<T>\n{\n    private static BTreeIndexBounds<T, RW> ToBounds(T key) => new(key);\n\n    private sealed class RawPointIter(FFI.IndexId indexId, byte[] point) : RawTableIterBase<Row>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_index_scan_point_bsatn(indexId, point, (uint)point.Length, out handle);\n    }\n\n    protected IEnumerable<Row> DoFilter(T key) => DoFilter(ToBounds(key));\n\n    public bool Delete(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n        FFI.datastore_delete_by_index_scan_point_bsatn(\n            indexId,\n            point,\n            (uint)point.Length,\n            out var numDeleted\n        );\n        return numDeleted > 0;\n    }\n\n    protected Row? FindSingle(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n\n        using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();\n        if (!e.MoveNext())\n        {\n            return null;\n        }\n\n        var row = e.Current;\n        if (e.MoveNext())\n        {\n            throw new InvalidOperationException(\"Unique index point scan returned >1 rows\");\n        }\n\n        return row;\n    }\n\n    protected Row DoUpdate(Row row)\n    {\n        // Insert the row.\n        var bytes = IStructuralReadWrite.ToBytes(row);\n        var bytes_len = (uint)bytes.Length;\n        FFI.datastore_update_bsatn(ITableView<Handle, Row>.tableId, indexId, bytes, ref bytes_len);\n\n        return ITableView<Handle, Row>.IntegrateGeneratedColumns(row, bytes, bytes_len);\n    }\n}\n\npublic abstract class ReadOnlyUniqueIndex<Handle, Row, T, RW>(string name)\n    : ReadOnlyIndexBase<Row>(name)\n    where Handle : ReadOnlyTableView<Row>\n    where Row : struct, IStructuralReadWrite\n    where RW : struct, BSATN.IReadWrite<T>\n{\n    private static BTreeIndexBounds<T, RW> ToBounds(T key) => new(key);\n\n    private sealed class RawPointIter(FFI.IndexId indexId, byte[] point) : RawTableIterBase<Row>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_index_scan_point_bsatn(indexId, point, (uint)point.Length, out handle);\n    }\n\n    protected IEnumerable<Row> Filter(T key) => Filter(ToBounds(key));\n\n    protected Row? FindSingle(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n\n        using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();\n        if (!e.MoveNext())\n        {\n            return null;\n        }\n\n        var row = e.Current;\n        if (e.MoveNext())\n        {\n            throw new InvalidOperationException(\"Unique index point scan returned >1 rows\");\n        }\n\n        return row;\n    }\n}\n\npublic abstract class ReadOnlyRefUniqueIndex<Handle, Row, T, RW>(string name)\n    : ReadOnlyIndexBase<Row>(name)\n    where Handle : ReadOnlyTableView<Row>\n    where Row : class, IStructuralReadWrite, new()\n    where RW : struct, BSATN.IReadWrite<T>\n{\n    private static BTreeIndexBounds<T, RW> ToBounds(T key) => new(key);\n\n    private sealed class RawPointIter(FFI.IndexId indexId, byte[] point) : RawTableIterBase<Row>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_index_scan_point_bsatn(indexId, point, (uint)point.Length, out handle);\n    }\n\n    protected IEnumerable<Row> Filter(T key) => Filter(ToBounds(key));\n\n    protected Row? FindSingle(T key)\n    {\n        using var s = new MemoryStream();\n        using var w = new BinaryWriter(s);\n        new RW().Write(w, key);\n        var point = s.ToArray();\n\n        using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();\n        if (!e.MoveNext())\n        {\n            return null;\n        }\n\n        var row = e.Current;\n        if (e.MoveNext())\n        {\n            throw new InvalidOperationException(\"Unique index point scan returned >1 rows\");\n        }\n\n        return row;\n    }\n}\n\npublic abstract class ReadOnlyTableView<Row>\n    where Row : IStructuralReadWrite, new()\n{\n    private readonly FFI.TableId tableId;\n\n    private sealed class TableIter(FFI.TableId tableId) : RawTableIterBase<Row>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_table_scan_bsatn(tableId, out handle);\n    }\n\n    protected ReadOnlyTableView(string tableName)\n    {\n        var nameBytes = Encoding.UTF8.GetBytes(tableName);\n        FFI.table_id_from_name(nameBytes, (uint)nameBytes.Length, out tableId);\n    }\n\n    protected ulong DoCount()\n    {\n        FFI.datastore_table_row_count(tableId, out var count);\n        return count;\n    }\n\n    protected IEnumerable<Row> DoIter() => new TableIter(tableId).Parse();\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/IReducer.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System.Text;\nusing SpacetimeDB.BSATN;\n\npublic interface IReducerContext\n{\n    public static Identity GetIdentity()\n    {\n        FFI.identity(out var identity);\n        return identity;\n    }\n}\n\npublic interface IReducer\n{\n    RawReducerDefV10 MakeReducerDef(ITypeRegistrar registrar);\n\n    Lifecycle? Lifecycle { get; }\n\n    // This one is not static because we need to be able to store IReducer in a list.\n    void Invoke(BinaryReader reader, IReducerContext args);\n\n    public static void VolatileNonatomicScheduleImmediate(string name, MemoryStream args)\n    {\n        var name_bytes = Encoding.UTF8.GetBytes(name);\n        var args_bytes = args.ToArray();\n\n        FFI.volatile_nonatomic_schedule_immediate(\n            name_bytes,\n            (uint)name_bytes.Length,\n            args_bytes,\n            (uint)args_bytes.Length\n        );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/ITable.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System.Buffers;\nusing SpacetimeDB.BSATN;\n\ninternal abstract class RawTableIterBase<T>\n    where T : IStructuralReadWrite, new()\n{\n    public sealed class Enumerator(FFI.RowIter handle) : IDisposable\n    {\n        private const int InitialBufferSize = 1024;\n        private byte[]? buffer = ArrayPool<byte>.Shared.Rent(InitialBufferSize);\n        public ArraySegment<byte> Current { get; private set; } = ArraySegment<byte>.Empty;\n\n        public bool MoveNext()\n        {\n            if (handle == FFI.RowIter.INVALID)\n            {\n                return false;\n            }\n\n            if (buffer is null)\n            {\n                return false;\n            }\n\n            uint buffer_len;\n            while (true)\n            {\n                var requested_len = (uint)buffer.Length;\n                buffer_len = requested_len;\n                var ret = FFI.row_iter_bsatn_advance(handle, buffer, ref buffer_len);\n                if (ret == Errno.EXHAUSTED)\n                {\n                    handle = FFI.RowIter.INVALID;\n                    if (buffer_len == requested_len)\n                    {\n                        buffer_len = 0;\n                    }\n                }\n                // On success, the only way `buffer_len == 0` is for the iterator to be exhausted.\n                // This happens when the host iterator was empty from the start.\n                System.Diagnostics.Debug.Assert(!(ret == Errno.OK && buffer_len == 0));\n                switch (ret)\n                {\n                    // Iterator advanced and may also be `EXHAUSTED`.\n                    // When `OK`, we'll need to advance the iterator in the next call to `MoveNext`.\n                    // In both cases, update `Current` to point at the valid range in the scratch `buffer`.\n                    case Errno.EXHAUSTED\n                    or Errno.OK:\n                        Current = new ArraySegment<byte>(buffer, 0, (int)buffer_len);\n                        return buffer_len != 0;\n                    // Couldn't find the iterator, error!\n                    case Errno.NO_SUCH_ITER:\n                        throw new NoSuchIterException();\n                    // The scratch `buffer` is too small to fit a row / chunk.\n                    // Grow `buffer` and try again.\n                    // The `buffer_len` will have been updated with the necessary size.\n                    case Errno.BUFFER_TOO_SMALL:\n                        ArrayPool<byte>.Shared.Return(buffer);\n                        buffer = ArrayPool<byte>.Shared.Rent((int)buffer_len);\n                        continue;\n                    default:\n                        throw new UnknownException(ret);\n                }\n            }\n        }\n\n        public void Dispose()\n        {\n            if (handle != FFI.RowIter.INVALID)\n            {\n                FFI.row_iter_bsatn_close(handle);\n                handle = FFI.RowIter.INVALID;\n            }\n\n            if (buffer is not null)\n            {\n                ArrayPool<byte>.Shared.Return(buffer);\n                buffer = null;\n            }\n        }\n\n        public void Reset()\n        {\n            throw new NotImplementedException();\n        }\n    }\n\n    protected abstract void IterStart(out FFI.RowIter handle);\n\n    // Note: using the GetEnumerator() duck-typing protocol instead of IEnumerable to avoid extra boxing.\n    public Enumerator GetEnumerator()\n    {\n        IterStart(out var handle);\n        return new(handle);\n    }\n\n    public IEnumerable<T> Parse()\n    {\n        foreach (var chunk in this)\n        {\n            using var stream = new MemoryStream(\n                chunk.Array!,\n                chunk.Offset,\n                chunk.Count,\n                writable: false,\n                publiclyVisible: true\n            );\n            using var reader = new BinaryReader(stream);\n            while (stream.Position < stream.Length)\n            {\n                yield return IStructuralReadWrite.Read<T>(reader);\n            }\n        }\n    }\n}\n\npublic interface ITableView<View, T>\n    where View : ITableView<View, T>\n    where T : IStructuralReadWrite, new()\n{\n    // These are the methods that codegen needs to implement.\n    static abstract RawTableDefV10 MakeTableDesc(ITypeRegistrar registrar);\n\n    static abstract RawScheduleDefV10? MakeScheduleDesc();\n\n    static abstract T ReadGenFields(BinaryReader reader, T row);\n\n    // These are static helpers that codegen can use.\n\n    private class RawTableIter(FFI.TableId tableId) : RawTableIterBase<T>\n    {\n        protected override void IterStart(out FFI.RowIter handle) =>\n            FFI.datastore_table_scan_bsatn(tableId, out handle);\n    }\n\n    private static readonly string tableName = typeof(View).Name;\n\n    // Note: this must be Lazy to ensure that we don't try to get the tableId during startup, before the module is initialized.\n    private static readonly Lazy<FFI.TableId> tableId_ =\n        new(() =>\n        {\n            var name_bytes = System.Text.Encoding.UTF8.GetBytes(tableName);\n            FFI.table_id_from_name(name_bytes, (uint)name_bytes.Length, out var out_);\n            return out_;\n        });\n\n    internal static FFI.TableId tableId => tableId_.Value;\n\n    ulong Count { get; }\n\n    IEnumerable<T> Iter();\n\n    T Insert(T row);\n\n    bool Delete(T row);\n\n    protected static ulong DoCount()\n    {\n        FFI.datastore_table_row_count(tableId, out var count);\n        return count;\n    }\n\n    protected static IEnumerable<T> DoIter() => new RawTableIter(tableId).Parse();\n\n    protected static T DoInsert(T row)\n    {\n        // Insert the row.\n        var bytes = IStructuralReadWrite.ToBytes(row);\n        var bytes_len = (uint)bytes.Length;\n        FFI.datastore_insert_bsatn(tableId, bytes, ref bytes_len);\n\n        return IntegrateGeneratedColumns(row, bytes, bytes_len);\n    }\n\n    // Writes back any generated column values.\n    static T IntegrateGeneratedColumns(T row, byte[] bytes, uint gen_len)\n    {\n        using var stream = new MemoryStream(bytes, 0, (int)gen_len);\n        using var reader = new BinaryReader(stream);\n        return View.ReadGenFields(reader, row);\n    }\n\n    protected static bool DoDelete(T row)\n    {\n        using var stream = new MemoryStream();\n        using var writer = new BinaryWriter(stream);\n        // `datastore_delete_all_by_eq_bsatn` expects an array-like BSATN.\n        // Write a length of 1 without actually wrapping the `row` into array\n        // (annoyingly, that would require passing `TRW` through a bunch of APIs).\n        writer.Write(1U);\n        row.WriteFields(writer);\n        FFI.datastore_delete_all_by_eq_bsatn(\n            tableId,\n            stream.GetBuffer(),\n            (uint)stream.Length,\n            out var out_\n        );\n        return out_ > 0;\n    }\n\n    protected static RawScheduleDefV10 MakeSchedule(string reducerName, ushort colIndex) =>\n        new(\n            SourceName: null,\n            TableName: tableName,\n            ScheduleAtCol: colIndex,\n            FunctionName: reducerName\n        );\n\n    protected static RawSequenceDefV10 MakeSequence(ushort colIndex) =>\n        new(\n            SourceName: null,\n            Column: colIndex,\n            Start: null,\n            MinValue: null,\n            MaxValue: null,\n            Increment: 1\n        );\n\n    protected static RawConstraintDefV10 MakeUniqueConstraint(ushort colIndex) =>\n        new(SourceName: null, Data: new RawConstraintDataV9.Unique(new([colIndex])));\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/IView.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing SpacetimeDB.BSATN;\n\npublic interface IView\n{\n    RawViewDefV10 MakeViewDef(ITypeRegistrar registrar);\n\n    // This one is not static because we need to be able to store IView in a list.\n    byte[] Invoke(BinaryReader reader, IViewContext args);\n}\n\npublic interface IAnonymousView\n{\n    RawViewDefV10 MakeAnonymousViewDef(ITypeRegistrar registrar);\n\n    // This one is not static because we need to be able to store IAnonymousView in a list.\n    byte[] Invoke(BinaryReader reader, IAnonymousViewContext args);\n}\n\npublic interface IViewContext\n{\n    public static Identity GetIdentity()\n    {\n        FFI.identity(out var identity);\n        return identity;\n    }\n}\n\npublic interface IAnonymousViewContext { }\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Module.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing SpacetimeDB;\nusing SpacetimeDB.BSATN;\n\npartial class RawModuleDefV10\n{\n    private readonly Typespace typespace = new();\n    private readonly List<RawTypeDefV10> typeDefs = [];\n    private readonly List<RawTableDefV10> tableDefs = [];\n    private readonly List<RawScheduleDefV10> scheduleDefs = [];\n    private readonly List<RawReducerDefV10> reducerDefs = [];\n    private readonly List<RawLifeCycleReducerDefV10> lifecycleReducerDefs = [];\n    private readonly List<RawProcedureDefV10> procedureDefs = [];\n    private readonly List<RawViewDefV10> viewDefs = [];\n    private readonly List<RawRowLevelSecurityDefV9> rowLevelSecurityDefs = [];\n    private readonly Dictionary<string, List<RawColumnDefaultValueV10>> defaultValuesByTable =\n        new(StringComparer.Ordinal);\n\n    private SpacetimeDB.CaseConversionPolicy? caseConversionPolicy = null;\n    private readonly List<ExplicitNameEntry> explicitNames = [];\n\n    // Note: this intends to generate a valid identifier, but it's not guaranteed to be unique as it's not proper mangling.\n    // Fix it up to a different mangling scheme if it causes problems.\n    private static string GetFriendlyName(Type type) =>\n        type.IsGenericType\n            ? $\"{type.Name.Remove(type.Name.IndexOf('`'))}_{string.Join(\"_\", type.GetGenericArguments().Select(GetFriendlyName))}\"\n            : type.Name;\n\n    private static RawScopedTypeNameV10 MakeScopedTypeName(Type type) =>\n        new(new List<string>(), GetFriendlyName(type));\n\n    internal AlgebraicType.Ref RegisterType<T>(Func<AlgebraicType.Ref, AlgebraicType> makeType)\n    {\n        var typeList = typespace.Types;\n        var typeRef = new AlgebraicType.Ref(typeList.Count);\n        // Put a dummy self-reference just so that we get stable index even if `makeType` recursively adds more types.\n        typeList.Add(typeRef);\n        typeList[typeRef.Ref_] = makeType(typeRef);\n        typeDefs.Add(\n            new RawTypeDefV10(\n                SourceName: MakeScopedTypeName(typeof(T)),\n                Ty: (uint)typeRef.Ref_,\n                CustomOrdering: true\n            )\n        );\n        return typeRef;\n    }\n\n    internal void RegisterReducer(RawReducerDefV10 reducer, Lifecycle? lifecycle)\n    {\n        reducerDefs.Add(reducer);\n        if (lifecycle is { } lifecycleSpec)\n        {\n            lifecycleReducerDefs.Add(\n                new RawLifeCycleReducerDefV10(lifecycleSpec, reducer.SourceName)\n            );\n            reducer.Visibility = FunctionVisibility.Private;\n        }\n    }\n\n    internal void RegisterProcedure(RawProcedureDefV10 procedure) => procedureDefs.Add(procedure);\n\n    internal void RegisterTable(RawTableDefV10 table, RawScheduleDefV10? schedule)\n    {\n        tableDefs.Add(table);\n        if (schedule is { } scheduleDef)\n        {\n            scheduleDefs.Add(scheduleDef);\n        }\n    }\n\n    internal void RegisterView(RawViewDefV10 view) => viewDefs.Add(view);\n\n    internal void RegisterRowLevelSecurity(RawRowLevelSecurityDefV9 rls) =>\n        rowLevelSecurityDefs.Add(rls);\n\n    internal void RegisterTableDefaultValue(string table, ushort colId, byte[] value)\n    {\n        if (!defaultValuesByTable.TryGetValue(table, out var defaults))\n        {\n            defaults = [];\n            defaultValuesByTable.Add(table, defaults);\n        }\n        defaults.Add(new RawColumnDefaultValueV10(colId, new List<byte>(value)));\n    }\n\n    internal void SetCaseConversionPolicy(SpacetimeDB.CaseConversionPolicy policy) =>\n        caseConversionPolicy = policy;\n\n    internal void RegisterExplicitTableName(string sourceName, string canonicalName) =>\n        explicitNames.Add(new ExplicitNameEntry.Table(new NameMapping(sourceName, canonicalName)));\n\n    internal void RegisterExplicitFunctionName(string sourceName, string canonicalName) =>\n        explicitNames.Add(\n            new ExplicitNameEntry.Function(new NameMapping(sourceName, canonicalName))\n        );\n\n    internal void RegisterExplicitIndexName(string sourceName, string canonicalName) =>\n        explicitNames.Add(new ExplicitNameEntry.Index(new NameMapping(sourceName, canonicalName)));\n\n    internal RawModuleDefV10 BuildModuleDefinition()\n    {\n        var builtTables = new List<RawTableDefV10>(tableDefs.Count);\n        foreach (var table in tableDefs)\n        {\n            defaultValuesByTable.TryGetValue(table.SourceName, out var defaults);\n            builtTables.Add(\n                new RawTableDefV10(\n                    SourceName: table.SourceName,\n                    ProductTypeRef: table.ProductTypeRef,\n                    PrimaryKey: table.PrimaryKey,\n                    Indexes: table.Indexes,\n                    Constraints: table.Constraints,\n                    Sequences: table.Sequences,\n                    TableType: table.TableType,\n                    TableAccess: table.TableAccess,\n                    DefaultValues: defaults is null\n                        ? []\n                        : new List<RawColumnDefaultValueV10>(defaults),\n                    IsEvent: table.IsEvent\n                )\n            );\n        }\n\n        var internalFunctions = lifecycleReducerDefs\n            .Select(l => l.FunctionName)\n            .Concat(scheduleDefs.Select(s => s.FunctionName))\n            .ToHashSet(StringComparer.Ordinal);\n\n        foreach (var reducer in reducerDefs)\n        {\n            if (internalFunctions.Contains(reducer.SourceName))\n            {\n                reducer.Visibility = FunctionVisibility.Private;\n            }\n        }\n\n        foreach (var procedure in procedureDefs)\n        {\n            if (internalFunctions.Contains(procedure.SourceName))\n            {\n                procedure.Visibility = FunctionVisibility.Private;\n            }\n        }\n\n        var sections = new List<RawModuleDefV10Section>\n        {\n            new RawModuleDefV10Section.Typespace(typespace),\n        };\n\n        if (typeDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Types(typeDefs));\n        }\n        if (builtTables.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Tables(builtTables));\n        }\n        if (reducerDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Reducers(reducerDefs));\n        }\n        if (procedureDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Procedures(procedureDefs));\n        }\n        if (viewDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Views(viewDefs));\n        }\n        if (scheduleDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.Schedules(scheduleDefs));\n        }\n        if (lifecycleReducerDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.LifeCycleReducers(lifecycleReducerDefs));\n        }\n        // TODO: Add sections for Event tables and Case conversion policy (mirrors Rust `raw_def/v10.rs` TODO).\n        if (caseConversionPolicy is { } policy)\n        {\n            sections.Add(new RawModuleDefV10Section.CaseConversionPolicy(policy));\n        }\n        if (explicitNames.Count > 0)\n        {\n            sections.Add(\n                new RawModuleDefV10Section.ExplicitNames(\n                    new ExplicitNames(new List<ExplicitNameEntry>(explicitNames))\n                )\n            );\n        }\n        if (rowLevelSecurityDefs.Count > 0)\n        {\n            sections.Add(new RawModuleDefV10Section.RowLevelSecurity(rowLevelSecurityDefs));\n        }\n\n        Sections = sections;\n        return this;\n    }\n}\n\npublic static class Module\n{\n    private static readonly RawModuleDefV10 moduleDef = new();\n\n    private static readonly List<IReducer> reducers = [];\n    private static readonly List<IProcedure> procedures = [];\n    private static readonly List<IView> viewDispatchers = [];\n    private static readonly List<IAnonymousView> anonymousViewDispatchers = [];\n\n    private static Func<\n        Identity,\n        ConnectionId?,\n        Random,\n        Timestamp,\n        IReducerContext\n    >? newReducerContext = null;\n    private static Func<Identity, IViewContext>? newViewContext = null;\n    private static Func<IAnonymousViewContext>? newAnonymousViewContext = null;\n\n    private static Func<\n        Identity,\n        ConnectionId?,\n        Random,\n        Timestamp,\n        IProcedureContext\n    >? newProcedureContext = null;\n\n    public static void SetReducerContextConstructor(\n        Func<Identity, ConnectionId?, Random, Timestamp, IReducerContext> ctor\n    ) => newReducerContext = ctor;\n\n    public static void SetProcedureContextConstructor(\n        Func<Identity, ConnectionId?, Random, Timestamp, IProcedureContext> ctor\n    ) => newProcedureContext = ctor;\n\n    public static void SetViewContextConstructor(Func<Identity, IViewContext> ctor) =>\n        newViewContext = ctor;\n\n    public static void SetAnonymousViewContextConstructor(Func<IAnonymousViewContext> ctor) =>\n        newAnonymousViewContext = ctor;\n\n    public readonly struct TypeRegistrar() : ITypeRegistrar\n    {\n        private readonly Dictionary<Type, AlgebraicType.Ref> types = [];\n\n        // Registers type in the module definition.\n        //\n        // To avoid issues with self-recursion during registration as well as unnecessary construction\n        // of algebraic types for types that have already been registered, we accept a factory\n        // returning an AlgebraicType instead of the AlgebraicType itself.\n        //\n        // The factory callback will be called with the allocated type reference that can be used for\n        // e.g. self-recursion even before the algebraic type itself is constructed.\n        public AlgebraicType.Ref RegisterType<T>(Func<AlgebraicType.Ref, AlgebraicType> makeType)\n        {\n            // Store for the closure access.\n            var types = this.types;\n            if (types.TryGetValue(typeof(T), out var existingTypeRef))\n            {\n                return existingTypeRef;\n            }\n            return moduleDef.RegisterType<T>(typeRef =>\n            {\n                // Store the type reference in the dictionary so that we can resolve it later and to avoid infinite recursion inside `makeType`.\n                types.Add(typeof(T), typeRef);\n                return makeType(typeRef);\n            });\n        }\n    }\n\n    static readonly TypeRegistrar typeRegistrar = new();\n\n    public static void RegisterReducer<R>()\n        where R : IReducer, new()\n    {\n        var reducer = new R();\n        reducers.Add(reducer);\n        moduleDef.RegisterReducer(reducer.MakeReducerDef(typeRegistrar), reducer.Lifecycle);\n    }\n\n    public static void RegisterProcedure<P>()\n        where P : IProcedure, new()\n    {\n        var procedure = new P();\n        procedures.Add(procedure);\n        moduleDef.RegisterProcedure(procedure.MakeProcedureDef(typeRegistrar));\n    }\n\n    public static void RegisterTable<T, View>()\n        where T : IStructuralReadWrite, new()\n        where View : ITableView<View, T>, new()\n    {\n        moduleDef.RegisterTable(View.MakeTableDesc(typeRegistrar), View.MakeScheduleDesc());\n    }\n\n    public static void RegisterView<TDispatcher>()\n        where TDispatcher : IView, new()\n    {\n        var dispatcher = new TDispatcher();\n        var def = dispatcher.MakeViewDef(typeRegistrar);\n        viewDispatchers.Add(dispatcher);\n        moduleDef.RegisterView(def);\n    }\n\n    public static void RegisterAnonymousView<TDispatcher>()\n        where TDispatcher : IAnonymousView, new()\n    {\n        var dispatcher = new TDispatcher();\n        var def = dispatcher.MakeAnonymousViewDef(typeRegistrar);\n        anonymousViewDispatchers.Add(dispatcher);\n        moduleDef.RegisterView(def);\n    }\n\n    public static void RegisterClientVisibilityFilter(Filter rlsFilter)\n    {\n        if (rlsFilter is Filter.Sql(var rlsSql))\n        {\n            moduleDef.RegisterRowLevelSecurity(new RawRowLevelSecurityDefV9 { Sql = rlsSql });\n        }\n        else\n        {\n            throw new Exception($\"Unimplemented row level security type: {rlsFilter}\");\n        }\n    }\n\n    public static void RegisterTableDefaultValue(string table, ushort colId, byte[] value) =>\n        moduleDef.RegisterTableDefaultValue(table, colId, value);\n\n    public static void SetCaseConversionPolicy(SpacetimeDB.CaseConversionPolicy policy) =>\n        moduleDef.SetCaseConversionPolicy(policy);\n\n    public static void RegisterExplicitTableName(string sourceName, string canonicalName) =>\n        moduleDef.RegisterExplicitTableName(sourceName, canonicalName);\n\n    public static void RegisterExplicitFunctionName(string sourceName, string canonicalName) =>\n        moduleDef.RegisterExplicitFunctionName(sourceName, canonicalName);\n\n    public static void RegisterExplicitIndexName(string sourceName, string canonicalName) =>\n        moduleDef.RegisterExplicitIndexName(sourceName, canonicalName);\n\n    public static byte[] Consume(this BytesSource source)\n    {\n        if (source == BytesSource.INVALID)\n        {\n            return [];\n        }\n\n        var len = (uint)0;\n        var ret = FFI.bytes_source_remaining_length(source, ref len);\n        switch (ret)\n        {\n            case Errno.OK:\n                break;\n            case Errno.NO_SUCH_BYTES:\n                throw new NoSuchBytesException();\n            default:\n                throw new UnknownException(ret);\n        }\n\n        var buffer = new byte[len];\n        var written = 0U;\n        // Because we've reserved space in our buffer already, this loop should be unnecessary.\n        // We expect the first call to `bytes_source_read` to always return `-1`.\n        // I (pgoldman 2025-09-26) am leaving the loop here because there's no downside to it,\n        // and in the future we may want to support `BytesSource`s which don't have a known length ahead of time\n        // (i.e. put arbitrary streams in `BytesSource` on the host side rather than just `Bytes` buffers),\n        // at which point the loop will become useful again.\n        while (true)\n        {\n            // Write into the spare capacity of the buffer.\n            var spare = buffer.AsSpan((int)written);\n            var buf_len = (uint)spare.Length;\n            ret = FFI.bytes_source_read(source, spare, ref buf_len);\n            written += buf_len;\n            switch (ret)\n            {\n                // Host side source exhausted, we're done.\n                case Errno.EXHAUSTED:\n                    Array.Resize(ref buffer, (int)written);\n                    return buffer;\n                // Wrote the entire spare capacity.\n                // Need to reserve more space in the buffer.\n                case Errno.OK when written == buffer.Length:\n                    Array.Resize(ref buffer, buffer.Length + 1024);\n                    break;\n                // Host didn't write as much as possible.\n                // Try to read some more.\n                // The host will likely not trigger this branch (current host doesn't),\n                // but a module should be prepared for it.\n                case Errno.OK:\n                    break;\n                case Errno.NO_SUCH_BYTES:\n                    throw new NoSuchBytesException();\n                default:\n                    throw new UnknownException(ret);\n            }\n        }\n    }\n\n    private static void Write(this BytesSink sink, byte[] bytes)\n    {\n        var start = 0U;\n        while (start != bytes.Length)\n        {\n            var written = (uint)bytes.Length;\n            var buffer = bytes.AsSpan((int)start);\n            FFI.bytes_sink_write(sink, buffer, ref written);\n            start += written;\n        }\n    }\n\n#pragma warning disable IDE1006 // Naming Styles - methods below are meant for FFI.\n\n    public static void __describe_module__(BytesSink description)\n    {\n        try\n        {\n            var module = moduleDef.BuildModuleDefinition();\n            RawModuleDef versioned = new RawModuleDef.V10(module);\n            var moduleBytes = IStructuralReadWrite.ToBytes(new RawModuleDef.BSATN(), versioned);\n            description.Write(moduleBytes);\n        }\n        catch (Exception e)\n        {\n            Log.Error($\"Error while describing the module: {e}\");\n        }\n    }\n\n    public static Errno __call_reducer__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        Timestamp timestamp,\n        BytesSource args,\n        BytesSink error\n    )\n    {\n        try\n        {\n            var senderIdentity = Identity.From(\n                MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray()\n            );\n            var connectionId = ConnectionId.From(\n                MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray()\n            );\n            var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch);\n            var time = timestamp.ToStd();\n\n            var ctx = newReducerContext!(senderIdentity, connectionId, random, time);\n\n            using var stream = new MemoryStream(args.Consume());\n            using var reader = new BinaryReader(stream);\n            reducers[(int)id].Invoke(reader, ctx);\n            if (stream.Position != stream.Length)\n            {\n                throw new Exception(\"Unrecognised extra bytes in the reducer arguments\");\n            }\n            return Errno.OK; /* no exception */\n        }\n        catch (Exception e)\n        {\n            var error_str = e.Message ?? e.GetType().FullName;\n            var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str);\n            error.Write(error_bytes);\n            return Errno.HOST_CALL_FAILURE;\n        }\n    }\n\n    public static Errno __call_procedure__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        ulong conn_id_0,\n        ulong conn_id_1,\n        Timestamp timestamp,\n        BytesSource args,\n        BytesSink resultSink\n    )\n    {\n        try\n        {\n            var sender = Identity.From(\n                MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray()\n            );\n            var connectionId = ConnectionId.From(\n                MemoryMarshal.AsBytes([conn_id_0, conn_id_1]).ToArray()\n            );\n            var random = new Random((int)timestamp.MicrosecondsSinceUnixEpoch);\n            var time = timestamp.ToStd();\n\n            var ctx = newProcedureContext!(sender, connectionId, random, time);\n\n            using var stream = new MemoryStream(args.Consume());\n            using var reader = new BinaryReader(stream);\n            var bytes = procedures[(int)id].Invoke(reader, ctx);\n            if (stream.Position != stream.Length)\n            {\n                throw new Exception(\"Unrecognised extra bytes in the procedure arguments\");\n            }\n            resultSink.Write(bytes);\n\n            return Errno.OK;\n        }\n        catch (Exception e)\n        {\n            // Host contract __call_procedure__ must either return Errno.OK or trap.\n            // Returning other errno values here can put the host/runtime in an unexpected state,\n            // so we log and rethrow to trap on any exception.\n            Log.Error($\"Error while invoking procedure: {e}\");\n            throw;\n        }\n    }\n\n    /// <summary>\n    /// Called by the host to execute a view when the sender calls the view identified by <paramref name=\"id\" />.\n    /// </summary>\n    /// <remarks>\n    /// <para>\n    /// The sender identity is passed as 4 <see cref=\"ulong\" /> values (<paramref name=\"sender_0\" /> through\n    /// <paramref name=\"sender_3\" />) representing a little-endian <see cref=\"SpacetimeDB.Identity\" />.\n    /// </para>\n    /// <para>\n    /// <paramref name=\"args\" /> is a host-registered <see cref=\"BytesSource\" /> containing the BSATN-encoded\n    /// view arguments. For empty arguments, <paramref name=\"args\" /> will be invalid.\n    /// </para>\n    /// <para>\n    /// The view output is written to <paramref name=\"rows\" />, a host-registered <see cref=\"BytesSink\" />.\n    /// </para>\n    /// <para>\n    /// Note: a previous view ABI wrote the return rows directly to the sink.\n    /// The current ABI writes a BSATN-encoded <see cref=\"ViewResultHeader\" /> first, in order to distinguish\n    /// between views that return row data and views that return queries.\n    /// </para>\n    /// <para>\n    /// The current ABI is identified by returning error code <c>2</c>.\n    /// </para>\n    /// </remarks>\n    public static Errno __call_view__(\n        uint id,\n        ulong sender_0,\n        ulong sender_1,\n        ulong sender_2,\n        ulong sender_3,\n        BytesSource args,\n        BytesSink rows\n    )\n    {\n        try\n        {\n            var sender = Identity.From(\n                MemoryMarshal.AsBytes([sender_0, sender_1, sender_2, sender_3]).ToArray()\n            );\n            var ctx = newViewContext!(sender);\n            using var stream = new MemoryStream(args.Consume());\n            using var reader = new BinaryReader(stream);\n            var bytes = viewDispatchers[(int)id].Invoke(reader, ctx);\n            rows.Write(bytes);\n            return (Errno)2;\n        }\n        catch (Exception e)\n        {\n            Log.Error($\"Error while invoking view: {e}\");\n            return Errno.HOST_CALL_FAILURE;\n        }\n    }\n\n    /// <summary>\n    /// Called by the host to execute an anonymous view.\n    /// </summary>\n    /// <remarks>\n    /// <para>\n    /// <paramref name=\"args\" /> is a host-registered <see cref=\"BytesSource\" /> containing the BSATN-encoded\n    /// view arguments. For empty arguments, <paramref name=\"args\" /> will be invalid.\n    /// </para>\n    /// <para>\n    /// The view output is written to <paramref name=\"rows\" />, a host-registered <see cref=\"BytesSink\" />.\n    /// </para>\n    /// <para>\n    /// Note: a previous view ABI wrote the return rows directly to the sink.\n    /// The current ABI writes a BSATN-encoded <see cref=\"ViewResultHeader\" /> first, in order to distinguish\n    /// between views that return row data and views that return queries.\n    /// </para>\n    /// <para>\n    /// The current ABI is identified by returning error code <c>2</c>.\n    /// </para>\n    /// </remarks>\n    public static Errno __call_view_anon__(uint id, BytesSource args, BytesSink rows)\n    {\n        try\n        {\n            var ctx = newAnonymousViewContext!();\n            using var stream = new MemoryStream(args.Consume());\n            using var reader = new BinaryReader(stream);\n            var bytes = anonymousViewDispatchers[(int)id].Invoke(reader, ctx);\n            rows.Write(bytes);\n            return (Errno)2;\n        }\n        catch (Exception e)\n        {\n            Log.Error($\"Error while invoking anonymous view: {e}\");\n            return Errno.HOST_CALL_FAILURE;\n        }\n    }\n}\n\n/// <summary>\n/// Read-write database access for procedure contexts.\n/// The code generator will extend this partial class with table accessors.\n/// </summary>\npublic partial class Local\n{\n    // Intentionally empty – generated code adds table handles here.\n}\n\n/// <summary>\n/// Read-only database access for view contexts.\n/// The code generator will extend this partial class to add table accessors.\n/// </summary>\npublic sealed partial class LocalReadOnly\n{\n    // This class is intentionally empty - the code generator will add\n    // read-only table accessors for each table in the module.\n    // Example generated code:\n    // public Internal.ViewHandles.UserReadOnly User => new();\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/Procedure.cs",
    "content": "namespace SpacetimeDB.Internal;\n\nusing System;\nusing System.IO;\nusing System.Text;\nusing SpacetimeDB.BSATN;\n\n/// <summary>\n/// Represents a procedure that can be registered and invoked by the module runtime.\n/// </summary>\npublic interface IProcedure\n{\n    /// <summary>\n    /// Creates a procedure definition for registration with the module system.\n    /// </summary>\n    RawProcedureDefV10 MakeProcedureDef(ITypeRegistrar registrar);\n\n    /// <summary>\n    /// Invokes the procedure with the given arguments and context.\n    /// </summary>\n    byte[] Invoke(BinaryReader reader, IProcedureContext ctx);\n}\n\n/// <summary>\n/// Represents the context for a procedure call.\n/// </summary>\npublic interface IProcedureContext\n{\n    /// <summary>\n    /// Gets the identity of the current procedure caller.\n    /// </summary>\n    /// <returns>The identity of the caller.</returns>\n    public static Identity GetIdentity()\n    {\n        FFI.identity(out var identity);\n        return identity;\n    }\n}\n\n/// <summary>\n/// Internal interface for procedure context with additional functionality.\n/// </summary>\npublic interface IInternalProcedureContext : IProcedureContext\n{\n    TxContext EnterTxContext(long timestampMicros);\n    void ExitTxContext();\n}\n\n/// <summary>\n/// Provides utility methods for procedure-related functionality.\n/// </summary>\npublic static class ProcedureExtensions\n{\n    /// <summary>\n    /// Schedules an immediate volatile, non-atomic procedure call.\n    /// </summary>\n    public static void VolatileNonatomicScheduleImmediate(string name, MemoryStream args)\n    {\n        var name_bytes = Encoding.UTF8.GetBytes(name);\n        var args_bytes = args.ToArray();\n\n        try\n        {\n            FFI.volatile_nonatomic_schedule_immediate(\n                name_bytes,\n                (uint)name_bytes.Length,\n                args_bytes,\n                (uint)args_bytes.Length\n            );\n        }\n        catch (Exception ex)\n        {\n            Log.Error($\"Failed to schedule procedure {name}: {ex}\");\n            throw;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/TxContext.cs",
    "content": "namespace SpacetimeDB.Internal;\n\npublic sealed class TxContext(\n    Local db,\n    Identity sender,\n    ConnectionId? connectionId,\n    Timestamp timestamp,\n    AuthCtx senderAuth,\n    Random rng\n)\n{\n    public Local Db { get; } = db;\n    public Identity Sender { get; } = sender;\n    public ConnectionId? ConnectionId { get; } = connectionId;\n    public Timestamp Timestamp { get; } = timestamp;\n    public AuthCtx SenderAuth { get; } = senderAuth;\n    public Random Rng { get; } = rng;\n\n    public TxContext WithTimestamp(Timestamp ts) =>\n        new(Db, Sender, ConnectionId, ts, SenderAuth, Rng);\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Internal/ViewResultHeader.cs",
    "content": "namespace SpacetimeDB.Internal;\n\n[SpacetimeDB.Type]\npublic partial record ViewResultHeader\n    : SpacetimeDB.TaggedEnum<(SpacetimeDB.Unit RowData, string RawSql)>;\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/JwtClaims.cs",
    "content": "namespace SpacetimeDB;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text.Json;\n\npublic sealed class JwtClaims\n{\n    private readonly string _payload;\n    private readonly Lazy<JsonDocument> _parsed;\n    private readonly Lazy<List<string>> _audience;\n\n    public Identity Identity { get; }\n\n    /// <summary>\n    /// Create a JwtClaims from a raw JWT payload (JSON claims) and its associated Identity.\n    ///\n    /// This only takes an Identity because the Blake3 hash package on nuget wraps rust code.\n    /// We should not expose this constructor publicly, but it is needed for AuthCtx.\n    /// </summary>\n    internal JwtClaims(string jwt, Identity identity)\n    {\n        _payload = jwt ?? throw new ArgumentNullException(nameof(jwt));\n        _parsed = new Lazy<JsonDocument>(() => JsonDocument.Parse(_payload));\n        _audience = new Lazy<List<string>>(ExtractAudience);\n        Identity = identity;\n    }\n\n    private JsonDocument Parsed => _parsed.Value;\n\n    private JsonElement RootElement => Parsed.RootElement;\n\n    public string Subject\n    {\n        get\n        {\n            if (\n                RootElement.TryGetProperty(\"sub\", out var sub)\n                && sub.ValueKind == JsonValueKind.String\n            )\n            {\n                return sub.GetString()!;\n            }\n\n            throw new InvalidOperationException(\"JWT missing or invalid 'sub' claim\");\n        }\n    }\n\n    public string Issuer\n    {\n        get\n        {\n            if (\n                RootElement.TryGetProperty(\"iss\", out var iss)\n                && iss.ValueKind == JsonValueKind.String\n            )\n            {\n                return iss.GetString()!;\n            }\n\n            throw new InvalidOperationException(\"JWT missing or invalid 'iss' claim\");\n        }\n    }\n\n    private List<string> ExtractAudience()\n    {\n        if (!RootElement.TryGetProperty(\"aud\", out var aud))\n        {\n            return [];\n        }\n\n        return aud.ValueKind switch\n        {\n            JsonValueKind.String => new List<string> { aud.GetString()! },\n            JsonValueKind.Array => aud.EnumerateArray()\n                .Where(e => e.ValueKind == JsonValueKind.String)\n                .Select(e => e.GetString()!)\n                .ToList(),\n            _ => throw new InvalidOperationException(\"Unexpected type for 'aud' claim in JWT\"),\n        };\n    }\n\n    public IReadOnlyList<string> Audience => _audience.Value;\n\n    // TODO: Should this be exposed as a JsonDocument, since that it in the stdlib?\n    public string RawPayload => _payload;\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Log.cs",
    "content": "﻿namespace SpacetimeDB;\n\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing SpacetimeDB.Internal;\n\npublic static class Log\n{\n    /// <summary>\n    /// Write an error message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Debug(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Debug,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write a trace message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Trace(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Trace,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write an info message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Info(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Info,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write a warning message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Warn(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Warn,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write an error message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Error(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Error,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write an exception message to module log\n    /// </summary>\n    /// <param name=\"message\">Message to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Exception(\n        string message,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            message,\n            FFI.LogLevel.Error,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    /// <summary>\n    /// Write an exception message and stacktrace to module log\n    /// </summary>\n    /// <param name=\"exception\">Exception to log</param>\n    /// <param name=\"RESERVED_target\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_filename\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    /// <param name=\"RESERVED_lineNumber\"><b>!!! DO NOT USE !!!</b> Value for this parameter will be automatically generated at compile time. Providing this parameter could lead to undefined behavior</param>\n    public static void Exception(\n        Exception exception,\n        [CallerMemberName] string RESERVED_target = \"\",\n        [CallerFilePath] string RESERVED_filename = \"\",\n        [CallerLineNumber] uint RESERVED_lineNumber = 0\n    ) =>\n        LogInternal(\n            exception.ToString(),\n            FFI.LogLevel.Error,\n            RESERVED_target,\n            RESERVED_filename,\n            RESERVED_lineNumber\n        );\n\n    private static void LogInternal(\n        string text,\n        FFI.LogLevel level,\n        string target,\n        string filename,\n        uint lineNumber\n    )\n    {\n        var target_bytes = Encoding.UTF8.GetBytes(target);\n        var filename_bytes = Encoding.UTF8.GetBytes(filename);\n        var text_bytes = Encoding.UTF8.GetBytes(text);\n\n        FFI.console_log(\n            level,\n            target_bytes,\n            (uint)target_bytes.Length,\n            filename_bytes,\n            (uint)filename_bytes.Length,\n            lineNumber,\n            text_bytes,\n            (uint)text_bytes.Length\n        );\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/LogStopwatch.cs",
    "content": "﻿namespace SpacetimeDB;\n\nusing System.Text;\nusing SpacetimeDB.Internal;\n\npublic sealed class LogStopwatch : IDisposable\n{\n    private readonly FFI.ConsoleTimerId StopwatchId;\n    private bool WasStopped;\n\n    public LogStopwatch(string name)\n    {\n        var name_bytes = Encoding.UTF8.GetBytes(name);\n        StopwatchId = FFI.console_timer_start(name_bytes, (uint)name_bytes.Length);\n    }\n\n    void IDisposable.Dispose()\n    {\n        if (!WasStopped)\n        {\n            End();\n        }\n    }\n\n    public void End()\n    {\n        FFI.console_timer_end(StopwatchId);\n        WasStopped = true;\n    }\n}\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/ProcedureContext.cs",
    "content": "namespace SpacetimeDB;\n\nusing System.Diagnostics.CodeAnalysis;\n\n#pragma warning disable STDB_UNSTABLE\npublic abstract class ProcedureContextBase(\n    Identity sender,\n    ConnectionId? connectionId,\n    Random random,\n    Timestamp time\n) : Internal.IInternalProcedureContext\n{\n    public static Identity Identity => Internal.IProcedureContext.GetIdentity();\n    public Identity Sender { get; } = sender;\n    public ConnectionId? ConnectionId { get; } = connectionId;\n    public Random Rng { get; } = random;\n    public Timestamp Timestamp { get; private set; } = time;\n    public AuthCtx SenderAuth { get; } = AuthCtx.BuildFromSystemTables(connectionId, sender);\n\n    // NOTE: The host rejects procedure HTTP requests while a mut transaction is open\n    // (WOULD_BLOCK_TRANSACTION). Avoid calling `Http.*` inside WithTx.\n    public HttpClient Http { get; } = new();\n\n    // **Note:** must be 0..=u32::MAX\n    protected int CounterUuid = 0;\n    private Internal.TxContext? txContext;\n    private ProcedureTxContextBase? cachedUserTxContext;\n\n    protected abstract ProcedureTxContextBase CreateTxContext(Internal.TxContext inner);\n    protected internal abstract LocalBase CreateLocal();\n\n    private protected ProcedureTxContextBase RequireTxContext()\n    {\n        var inner =\n            txContext\n            ?? throw new InvalidOperationException(\"Transaction context was not initialised.\");\n        cachedUserTxContext ??= CreateTxContext(inner);\n        cachedUserTxContext.Refresh(inner);\n        return cachedUserTxContext;\n    }\n\n    public Internal.TxContext EnterTxContext(long timestampMicros)\n    {\n        var timestamp = new Timestamp(timestampMicros);\n        Timestamp = timestamp;\n        txContext =\n            txContext?.WithTimestamp(timestamp)\n            ?? new Internal.TxContext(\n                CreateLocal(),\n                Sender,\n                ConnectionId,\n                timestamp,\n                SenderAuth,\n                Rng\n            );\n        return txContext;\n    }\n\n    public void ExitTxContext() => txContext = null;\n\n    public readonly struct TxOutcome<TResult>(bool isSuccess, TResult? value, Exception? error)\n    {\n        public bool IsSuccess { get; } = isSuccess;\n        public TResult? Value { get; } = value;\n        public Exception? Error { get; } = error;\n\n        public static TxOutcome<TResult> Success(TResult value) => new(true, value, null);\n\n        public static TxOutcome<TResult> Failure(Exception error) => new(false, default, error);\n\n        public TResult UnwrapOrThrow() =>\n            IsSuccess\n                ? Value!\n                : throw (\n                    Error\n                    ?? new InvalidOperationException(\"Transaction failed without an error object.\")\n                );\n\n        public TResult UnwrapOrThrow(Func<Exception> fallbackFactory) =>\n            IsSuccess ? Value! : throw (Error ?? fallbackFactory());\n    }\n\n    [Experimental(\"STDB_UNSTABLE\")]\n    public TResult WithTx<TResult>(Func<ProcedureTxContextBase, TResult> body) =>\n        TryWithTx(tx => Result<TResult, Exception>.Ok(body(tx))).UnwrapOrThrow();\n\n    [Experimental(\"STDB_UNSTABLE\")]\n    public TxOutcome<TResult> TryWithTx<TResult, TError>(\n        Func<ProcedureTxContextBase, Result<TResult, TError>> body\n    )\n        where TError : Exception\n    {\n        try\n        {\n            var result = RunWithRetry(body);\n\n            return result switch\n            {\n                Result<TResult, TError>.OkR(var value) => TxOutcome<TResult>.Success(value),\n                Result<TResult, TError>.ErrR(var error) => TxOutcome<TResult>.Failure(error),\n                _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n            };\n        }\n        catch (Exception ex)\n        {\n            return TxOutcome<TResult>.Failure(ex);\n        }\n    }\n\n    // Private transaction management methods (Rust-like encapsulation)\n    private long StartMutTx()\n    {\n        var status = Internal.FFI.procedure_start_mut_tx(out var micros);\n        Internal.FFI.ErrnoHelpers.ThrowIfError(status);\n        return micros;\n    }\n\n    private void CommitMutTx()\n    {\n        var status = Internal.FFI.procedure_commit_mut_tx();\n        Internal.FFI.ErrnoHelpers.ThrowIfError(status);\n    }\n\n    private void AbortMutTx()\n    {\n        var status = Internal.FFI.procedure_abort_mut_tx();\n        Internal.FFI.ErrnoHelpers.ThrowIfError(status);\n    }\n\n    private bool CommitMutTxWithRetry(Func<bool> retryBody)\n    {\n        try\n        {\n            CommitMutTx();\n            return true;\n        }\n        catch (TransactionNotAnonymousException)\n        {\n            return false;\n        }\n        catch (StdbException)\n        {\n            Log.Warn(\"Committing anonymous transaction failed; retrying once.\");\n            if (retryBody())\n            {\n                CommitMutTx();\n                return true;\n            }\n            return false;\n        }\n    }\n\n    private Result<TResult, TError> RunWithRetry<TResult, TError>(\n        Func<ProcedureTxContextBase, Result<TResult, TError>> body\n    )\n        where TError : Exception\n    {\n        var result = RunOnce(body);\n        if (result is Result<TResult, TError>.ErrR)\n        {\n            return result;\n        }\n\n        bool Retry()\n        {\n            result = RunOnce(body);\n            return result is Result<TResult, TError>.OkR;\n        }\n\n        if (!CommitMutTxWithRetry(Retry))\n        {\n            return result;\n        }\n\n        return result;\n    }\n\n    private Result<TResult, TError> RunOnce<TResult, TError>(\n        Func<ProcedureTxContextBase, Result<TResult, TError>> body\n    )\n        where TError : Exception\n    {\n        var micros = StartMutTx();\n        using var guard = new AbortGuard(AbortMutTx);\n        EnterTxContext(micros);\n        var txCtx = RequireTxContext();\n\n        Result<TResult, TError> result;\n        try\n        {\n            result = body(txCtx);\n        }\n        catch (Exception)\n        {\n            throw;\n        }\n\n        if (result is Result<TResult, TError>.OkR)\n        {\n            guard.Disarm();\n            return result;\n        }\n\n        AbortMutTx();\n        guard.Disarm();\n        return result;\n    }\n\n    private sealed class AbortGuard(Action abort) : IDisposable\n    {\n        private readonly Action abort = abort;\n        private bool disarmed;\n\n        public void Disarm() => disarmed = true;\n\n        public void Dispose()\n        {\n            if (!disarmed)\n            {\n                abort();\n            }\n        }\n    }\n}\n\npublic abstract class ProcedureTxContextBase(Internal.TxContext inner)\n{\n    internal Internal.TxContext Inner { get; private set; } = inner;\n\n    internal void Refresh(Internal.TxContext inner) => Inner = inner;\n\n    public LocalBase Db => (LocalBase)Inner.Db;\n    public Identity Sender => Inner.Sender;\n    public ConnectionId? ConnectionId => Inner.ConnectionId;\n    public Timestamp Timestamp => Inner.Timestamp;\n    public AuthCtx SenderAuth => Inner.SenderAuth;\n    public Random Rng => Inner.Rng;\n}\n\npublic abstract class LocalBase : Internal.Local { }\n\ninternal sealed partial class RuntimeProcedureContext(\n    Identity sender,\n    ConnectionId? connectionId,\n    Random random,\n    Timestamp timestamp\n) : ProcedureContextBase(sender, connectionId, random, timestamp)\n{\n    private readonly RuntimeLocal _db = new();\n\n    protected internal override LocalBase CreateLocal() => _db;\n\n    protected override ProcedureTxContextBase CreateTxContext(Internal.TxContext inner) =>\n        _cached ??= new RuntimeProcedureTxContext(inner);\n\n    private RuntimeProcedureTxContext? _cached;\n}\n\ninternal sealed class RuntimeProcedureTxContext : ProcedureTxContextBase\n{\n    internal RuntimeProcedureTxContext(Internal.TxContext inner)\n        : base(inner) { }\n\n    public new RuntimeLocal Db => (RuntimeLocal)base.Db;\n}\n\ninternal sealed class RuntimeLocal : LocalBase { }\n\n#pragma warning restore STDB_UNSTABLE\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/README.md",
    "content": "# SpacetimeDB.Runtime\n\nThis project contains the core SpacetimeDB SATS typesystem, attributes for the codegen as well as runtime bindings for SpacetimeDB WebAssembly modules.\n\nThe runtime bindings are currently implementing via `Wasi.Sdk` package, which is a .NET implementation of the [WASI](https://wasi.dev/) standard. This is likely to change in the future.\n\nWhile not really documented, it allows to build raw WebAssembly modules with custom bindings as well, which is what we're using here. The process is somewhat complicated, but here are the steps:\n\n- `bindings.c` declares raw C bindings to the SpacetimeDB FFI _imports_ and marks them with attributes like `__attribute__((import_module(\"spacetime\"), import_name(\"_insert\")))` that make them WebAssembly imports. (unfortunately, function name duplication is currently unavoidable)\n- `bindings.c` implements a bunch of Mono-compatible wrappers that convert between Mono types and raw types expected by the SpacetimeDB FFI and invoke corresponding raw bindings.\n- `Runtime.cs` declares corresponding functions with compatible signatures for Mono-compatible wrappers to attach to. It marks them all with `[MethodImpl(MethodImplOptions.InternalCall)]`.\n- `bindings.c` attaches all those Mono-compatible wrappers to their C# declarations in a `mono_stdb_attach_bindings` function.\n- `bindings.c` adds FFI-compatible _exports_ that search for a method by assembly name, namespace, class name and a method name in the Mono runtime and invoke it. Those exports are marked with attributes like `__attribute__((export_name(\"__call_reducer__\")))` so that they're exported from Wasm by the linker.\n- Finally, `bindings.c` implements no-op shims for all the WASI APIs so that they're linked internally and not attempted to be imported from the runtime itself.\n\nThe result is a WebAssembly module FFI-compatible with SpacetimeDB and with no WASI imports, which is what we need.\n\n## Regenerating RawModuleDef\nTo regenenerate the `Autogen` folder, run:\n\n```sh\ncargo run -p spacetimedb-codegen --example regen-csharp-moduledef\n```\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/Runtime.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <AssemblyName>SpacetimeDB.Runtime</AssemblyName>\n    <Version>2.0.4</Version>\n    <Title>SpacetimeDB Module Runtime</Title>\n    <Description>The SpacetimeDB Runtime implements the database runtime bindings for writing SpacetimeDB modules in C#.</Description>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <RootNamespace>SpacetimeDB</RootNamespace>\n    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <!-- Note: BSATN.Codegen is included in the BSATN.Runtime NuGet package. -->\n    <ProjectReference Include=\"../BSATN.Runtime/BSATN.Runtime.csproj\" />\n    <!-- ...But we also need BSATN.Codegen to transform `[SpacetimeDB.Type]` usages in source of Runtime itself. -->\n    <!-- For that, it has to be included as an explicit private dependency. -->\n    <ProjectReference Include=\"../BSATN.Codegen/BSATN.Codegen.csproj\" OutputItemType=\"Analyzer\" ReferenceOutputAssembly=\"false\" />\n    <!-- Codegen package must be built before this one so that it's included in NuGet Runtime package, but we don't depend on it. -->\n    <!-- (*not* with OutputItemType=analyzer because we don't want to use that source generator on source of Runtime itself) -->\n    <ProjectReference Include=\"../Codegen/Codegen.csproj\" ReferenceOutputAssembly=\"false\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Include=\"README.md\" Pack=\"true\" PackagePath=\"\" />\n    <None Include=\"build/*\" Pack=\"true\" PackagePath=\"build\" />\n    <None Include=\"bindings.c\" Pack=\"true\" PackagePath=\"\" />\n    <None Include=\"driver.h\" Pack=\"true\" PackagePath=\"\" />\n    <!-- We want all users who depends on Runtime to automatically get the Roslyn codegen component as well. -->\n    <None Include=\"../Codegen/bin/$(Configuration)/netstandard2.0/SpacetimeDB.Codegen.dll\" Pack=\"true\" PackagePath=\"analyzers/dotnet/cs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <UpToDateCheckInput Include=\"bindings.c\" />\n    <UpToDateCheckInput Include=\"driver.h\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/bindings.c",
    "content": "#include <assert.h>\r\n// #include <mono/metadata/appdomain.h>\r\n// #include <mono/metadata/object.h>\r\n#include <stdbool.h>\r\n#include <stdint.h>\r\n#include <unistd.h>\r\n\r\n#ifndef EXPERIMENTAL_WASM_AOT\r\n#include \"driver.h\"\r\n#endif\r\n\r\n#define OPAQUE_TYPEDEF(name, T) \\\r\n  typedef struct name {         \\\r\n    T inner;                    \\\r\n  } name\r\n\r\nOPAQUE_TYPEDEF(Status, uint16_t);\r\nOPAQUE_TYPEDEF(TableId, uint32_t);\r\nOPAQUE_TYPEDEF(IndexId, uint32_t);\r\nOPAQUE_TYPEDEF(ColId, uint16_t);\r\nOPAQUE_TYPEDEF(IndexType, uint8_t);\r\nOPAQUE_TYPEDEF(LogLevel, uint8_t);\r\nOPAQUE_TYPEDEF(BytesSink, uint32_t);\r\nOPAQUE_TYPEDEF(BytesSource, uint32_t);\r\nOPAQUE_TYPEDEF(RowIter, uint32_t);\r\nOPAQUE_TYPEDEF(ConsoleTimerId, uint32_t);\r\n\r\n#define CSTR(s) (uint8_t*)s, sizeof(s) - 1\r\n\r\n#define STDB_EXTERN(name) \\\r\n  __attribute__((import_module(SPACETIME_MODULE_VERSION), import_name(#name))) extern\r\n\r\n#ifndef EXPERIMENTAL_WASM_AOT\r\n#define IMPORT(ret, name, params, args)    \\\r\n  STDB_EXTERN(name) ret name##_imp params; \\\r\n  ret name params { return name##_imp args; }\r\n#else\r\n#define IMPORT(ret, name, params, args) STDB_EXTERN(name) ret name params;\r\n#endif\r\n\r\n#define SPACETIME_MODULE_VERSION \"spacetime_10.0\"\r\nIMPORT(Status, table_id_from_name,\r\n       (const uint8_t* name, uint32_t name_len, TableId* id),\r\n       (name, name_len, id));\r\nIMPORT(Status, index_id_from_name,\r\n       (const uint8_t* name, uint32_t name_len, IndexId* id),\r\n       (name, name_len, id));\r\nIMPORT(Status, datastore_table_row_count,\r\n       (TableId table_id, uint64_t* count),\r\n       (table_id, count));\r\nIMPORT(Status, datastore_table_scan_bsatn,\r\n       (TableId table_id, RowIter* iter),\r\n       (table_id, iter));\r\nIMPORT(Status, datastore_index_scan_range_bsatn,\r\n       (IndexId index_id, const uint8_t* prefix, uint32_t prefix_len, ColId prefix_elems,\r\n        const uint8_t* rstart, uint32_t rstart_len, const uint8_t* rend, uint32_t rend_len, RowIter* iter),\r\n       (index_id, prefix, prefix_len, prefix_elems, rstart, rstart_len, rend, rend_len, iter));\r\nIMPORT(Status, datastore_btree_scan_bsatn,\r\n       (IndexId index_id, const uint8_t* prefix, uint32_t prefix_len, ColId prefix_elems,\r\n        const uint8_t* rstart, uint32_t rstart_len, const uint8_t* rend, uint32_t rend_len, RowIter* iter),\r\n       (index_id, prefix, prefix_len, prefix_elems, rstart, rstart_len, rend, rend_len, iter));\r\nIMPORT(int16_t, row_iter_bsatn_advance,\r\n       (RowIter iter, uint8_t* buffer_ptr, size_t* buffer_len_ptr),\r\n       (iter, buffer_ptr, buffer_len_ptr));\r\nIMPORT(uint16_t, row_iter_bsatn_close, (RowIter iter), (iter));\r\nIMPORT(Status, datastore_insert_bsatn, (TableId table_id, uint8_t* row_ptr, size_t* row_len_ptr),\r\n       (table_id, row_ptr, row_len_ptr));\r\nIMPORT(Status, datastore_update_bsatn, (TableId table_id, IndexId index_id, uint8_t* row_ptr, size_t* row_len_ptr),\r\n       (table_id, index_id, row_ptr, row_len_ptr));\r\nIMPORT(Status, datastore_delete_by_index_scan_range_bsatn,\r\n       (IndexId index_id, const uint8_t* prefix, uint32_t prefix_len, ColId prefix_elems,\r\n        const uint8_t* rstart, uint32_t rstart_len, const uint8_t* rend, uint32_t rend_len, uint32_t* num_deleted),\r\n       (index_id, prefix, prefix_len, prefix_elems, rstart, rstart_len, rend, rend_len, num_deleted));\r\nIMPORT(Status, datastore_delete_by_btree_scan_bsatn,\r\n       (IndexId index_id, const uint8_t* prefix, uint32_t prefix_len, ColId prefix_elems,\r\n        const uint8_t* rstart, uint32_t rstart_len, const uint8_t* rend, uint32_t rend_len, uint32_t* num_deleted),\r\n       (index_id, prefix, prefix_len, prefix_elems, rstart, rstart_len, rend, rend_len, num_deleted));\r\nIMPORT(Status, datastore_delete_all_by_eq_bsatn,\r\n       (TableId table_id, const uint8_t* rel_ptr, uint32_t rel_len,\r\n        uint32_t* num_deleted),\r\n       (table_id, rel_ptr, rel_len, num_deleted));\r\nIMPORT(int16_t, bytes_source_read, (BytesSource source, uint8_t* buffer_ptr, size_t* buffer_len_ptr),\r\n       (source, buffer_ptr, buffer_len_ptr));\r\nIMPORT(uint16_t, bytes_sink_write, (BytesSink sink, const uint8_t* buffer_ptr, size_t* buffer_len_ptr),\r\n       (sink, buffer_ptr, buffer_len_ptr));\r\nIMPORT(void, console_log,\r\n       (LogLevel level, const uint8_t* target_ptr, uint32_t target_len,\r\n        const uint8_t* filename_ptr, uint32_t filename_len, uint32_t line_number,\r\n        const uint8_t* message_ptr, uint32_t message_len),\r\n       (level, target_ptr, target_len, filename_ptr, filename_len, line_number,\r\n        message_ptr, message_len));\r\nIMPORT(ConsoleTimerId, console_timer_start,\r\n       (const uint8_t* name, size_t name_len),\r\n       (name, name_len));\r\nIMPORT(Status, console_timer_end,\r\n       (ConsoleTimerId stopwatch_id),\r\n       (stopwatch_id));\r\nIMPORT(void, volatile_nonatomic_schedule_immediate,\r\n       (const uint8_t* name, size_t name_len, const uint8_t* args, size_t args_len),\r\n       (name, name_len, args, args_len));\r\nIMPORT(void, identity, (void* id_ptr), (id_ptr));\r\n#undef SPACETIME_MODULE_VERSION\r\n\r\n#define SPACETIME_MODULE_VERSION \"spacetime_10.4\"\r\nIMPORT(Status, datastore_index_scan_point_bsatn,\r\n       (IndexId index_id, const uint8_t* point, uint32_t point_len, RowIter* iter),\r\n       (index_id, point, point_len, iter));\r\nIMPORT(Status, datastore_delete_by_index_scan_point_bsatn,\r\n       (IndexId index_id, const uint8_t* point, uint32_t point_len, uint32_t* num_deleted),\r\n       (index_id, point, point_len, num_deleted));\r\n#undef SPACETIME_MODULE_VERSION\r\n\r\n#define SPACETIME_MODULE_VERSION \"spacetime_10.1\"\r\nIMPORT(int16_t, bytes_source_remaining_length, (BytesSource source, uint32_t* out), (source, out));\r\n#undef SPACETIME_MODULE_VERSION\r\n\r\n#define SPACETIME_MODULE_VERSION \"spacetime_10.2\"\r\nIMPORT(int16_t, get_jwt, (const uint8_t* connection_id_ptr, BytesSource* bytes_ptr), (connection_id_ptr, bytes_ptr));\r\n#undef SPACETIME_MODULE_VERSION\r\n\r\n#define SPACETIME_MODULE_VERSION \"spacetime_10.3\"\r\nIMPORT(uint16_t, procedure_start_mut_tx, (int64_t* micros), (micros));\r\nIMPORT(uint16_t, procedure_commit_mut_tx, (void), ());\r\nIMPORT(uint16_t, procedure_abort_mut_tx, (void), ());\r\nIMPORT(uint16_t, procedure_http_request,\r\n       (const uint8_t* request_ptr, uint32_t request_len,\r\n        const uint8_t* body_ptr, uint32_t body_len,\r\n        BytesSource* out),\r\n       (request_ptr, request_len, body_ptr, body_len, out));\r\n#undef SPACETIME_MODULE_VERSION\r\n\r\n#ifndef EXPERIMENTAL_WASM_AOT\r\nstatic MonoClass* ffi_class;\r\n\r\n#define CEXPORT(name) __attribute__((export_name(#name))) name\r\n\r\n#define PREINIT(priority, name) void CEXPORT(__preinit__##priority##_##name)()\r\n\r\nPREINIT(10, startup) {\r\n  // mono_wasm_load_runtime(\"\", 0);\r\n  // ^ not enough because it doesn't reach to assembly with Main function\r\n  // so module descriptor remains unpopulated. Invoke actual _start instead.\r\n  extern void _start();\r\n  _start();\r\n\r\n  ffi_class = mono_wasm_assembly_find_class(\r\n      mono_wasm_assembly_load(\"SpacetimeDB.Runtime.dll\"),\r\n      \"SpacetimeDB.Internal\", \"Module\");\r\n  assert(ffi_class &&\r\n         \"FFI export class (SpacetimeDB.Internal.Module) not found\");\r\n}\r\n\r\n#define EXPORT_WITH_MONO_RES(ret, res_code, name, params, args...)            \\\r\n  static MonoMethod* ffi_method_##name;                                       \\\r\n  PREINIT(20, find_##name) {                                                  \\\r\n    ffi_method_##name = mono_wasm_assembly_find_method(ffi_class, #name, -1); \\\r\n    assert(ffi_method_##name && \"FFI export method not found\");               \\\r\n  }                                                                           \\\r\n  ret CEXPORT(name) params {                                                  \\\r\n    MonoObject* res;                                                          \\\r\n    mono_wasm_invoke_method_ref(ffi_method_##name, NULL, (void*[]){args},     \\\r\n                                NULL, &res);                                  \\\r\n    res_code                                                                  \\\r\n  }\r\n\r\n#define EXPORT(ret, name, params, args...)                                             \\\r\n  EXPORT_WITH_MONO_RES(ret, return *(ret*)mono_object_unbox(res);, name, params, args) \\\r\n\r\n#define EXPORT_VOID(name, params, args...)                                    \\\r\n  EXPORT_WITH_MONO_RES(void, return;, name, params, args)                      \\\r\n\r\nEXPORT_VOID(__describe_module__, (BytesSink description), &description);\r\n\r\nEXPORT(int16_t, __call_reducer__,\r\n       (uint32_t id,\r\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\r\n        uint64_t conn_id_0, uint64_t conn_id_1,\r\n        uint64_t timestamp, BytesSource args, BytesSink error),\r\n       &id,\r\n       &sender_0, &sender_1, &sender_2, &sender_3,\r\n       &conn_id_0, &conn_id_1,\r\n       &timestamp, &args, &error);\r\n\r\nEXPORT(int16_t, __call_procedure__,\r\n       (uint32_t id,\r\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\r\n        uint64_t conn_id_0, uint64_t conn_id_1,\r\n        uint64_t timestamp, BytesSource args, BytesSink result_sink),\r\n       &id,\r\n       &sender_0, &sender_1, &sender_2, &sender_3,\r\n       &conn_id_0, &conn_id_1,\r\n       &timestamp, &args, &result_sink);\r\n\r\nEXPORT(int16_t, __call_view__,\r\n       (uint32_t id,\r\n        uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,\r\n        BytesSource args, BytesSink rows),\r\n       &id,\r\n       &sender_0, &sender_1, &sender_2, &sender_3,\r\n       &args, &rows);\r\n\r\nEXPORT(int16_t, __call_view_anon__,\r\n       (uint32_t id, BytesSource args, BytesSink rows),\r\n       &id, &args, &rows);\r\n#endif\r\n\r\n// Shims to avoid dependency on WASI in the generated Wasm file.\r\n\r\n#include <stdlib.h>\r\n#include <wasi/api.h>\r\n\r\n// Ignore warnings about anonymous parameters, this is to avoid having\r\n// to write `int arg0`, `int arg1`, etc. for every function.\r\n#pragma clang diagnostic ignored \"-Wc2x-extensions\"\r\n\r\n// Based on\r\n// https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/sources/__wasilibc_real.c,\r\n\r\n#define WASI_NAME(name) __imported_wasi_snapshot_preview1_##name\r\n\r\n// Shim for WASI calls that always unconditionally succeeds.\r\n// This is suitable for most (but not all) WASI functions used by .NET.\r\n#define WASI_SHIM(name, params) \\\r\n  int32_t WASI_NAME(name) params { return 0; }\r\n\r\nWASI_SHIM(environ_get, (int32_t, int32_t));\r\nWASI_SHIM(environ_sizes_get, (int32_t, int32_t));\r\nWASI_SHIM(clock_time_get, (int32_t, int64_t, int32_t));\r\nWASI_SHIM(fd_advise, (int32_t, int64_t, int64_t, int32_t));\r\nWASI_SHIM(fd_allocate, (int32_t, int64_t, int64_t));\r\nWASI_SHIM(fd_close, (int32_t));\r\nWASI_SHIM(fd_datasync, (int32_t));\r\nWASI_SHIM(fd_fdstat_get, (int32_t, int32_t));\r\nWASI_SHIM(fd_fdstat_set_flags, (int32_t, int32_t));\r\nWASI_SHIM(fd_fdstat_set_rights, (int32_t, int64_t, int64_t));\r\nWASI_SHIM(fd_filestat_get, (int32_t, int32_t));\r\nWASI_SHIM(fd_filestat_set_size, (int32_t, int64_t));\r\nWASI_SHIM(fd_filestat_set_times, (int32_t, int64_t, int64_t, int32_t));\r\nWASI_SHIM(fd_pread, (int32_t, int32_t, int32_t, int64_t, int32_t));\r\nWASI_SHIM(fd_prestat_dir_name, (int32_t, int32_t, int32_t));\r\nWASI_SHIM(fd_pwrite, (int32_t, int32_t, int32_t, int64_t, int32_t));\r\nWASI_SHIM(fd_read, (int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(fd_readdir, (int32_t, int32_t, int32_t, int64_t, int32_t));\r\nWASI_SHIM(fd_renumber, (int32_t, int32_t));\r\nWASI_SHIM(fd_seek, (int32_t, int64_t, int32_t, int32_t));\r\nWASI_SHIM(fd_sync, (int32_t));\r\nWASI_SHIM(fd_tell, (int32_t, int32_t));\r\nWASI_SHIM(path_create_directory, (int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_filestat_get, (int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_filestat_set_times,\r\n          (int32_t, int32_t, int32_t, int32_t, int64_t, int64_t, int32_t));\r\nWASI_SHIM(path_link,\r\n          (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_open, (int32_t, int32_t, int32_t, int32_t, int32_t, int64_t,\r\n                      int64_t, int32_t, int32_t));\r\nWASI_SHIM(path_readlink,\r\n          (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_remove_directory, (int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_rename, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_symlink, (int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(path_unlink_file, (int32_t, int32_t, int32_t));\r\nWASI_SHIM(poll_oneoff, (int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(sched_yield, ());\r\nWASI_SHIM(random_get, (int32_t, int32_t));\r\nWASI_SHIM(sock_accept, (int32_t, int32_t, int32_t));\r\nWASI_SHIM(sock_recv, (int32_t, int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(sock_send, (int32_t, int32_t, int32_t, int32_t, int32_t));\r\nWASI_SHIM(sock_shutdown, (int32_t, int32_t));\r\n\r\n// Mono retrieves executable name via argv[0], so we need to shim it with\r\n// some dummy name instead of returning an empty argv[] array to avoid\r\n// assertion failures.\r\nconst char executable_name[] = \"stdb.wasm\";\r\n\r\nint32_t WASI_NAME(args_sizes_get)(__wasi_size_t* argc,\r\n                                  __wasi_size_t* argv_buf_size) {\r\n  *argc = 1;\r\n  *argv_buf_size = sizeof(executable_name);\r\n  return 0;\r\n}\r\n\r\nint32_t WASI_NAME(args_get)(uint8_t** argv, uint8_t* argv_buf) {\r\n  argv[0] = argv_buf;\r\n  __builtin_memcpy(argv_buf, executable_name, sizeof(executable_name));\r\n  return 0;\r\n}\r\n\r\n// Clock resolution should be non-zero.\r\nint32_t WASI_NAME(clock_res_get)(int32_t, uint64_t* timestamp) {\r\n  *timestamp = 1;\r\n  return 0;\r\n}\r\n\r\n// For `fd_write`, we need to at least collect and report sum of sizes.\r\n// If we report size 0, the caller will assume that the write failed and will\r\n// try again, which will result in an infinite loop.\r\nint32_t WASI_NAME(fd_write)(__wasi_fd_t fd, const __wasi_ciovec_t* iovs,\r\n                            size_t iovs_len, __wasi_size_t* retptr0) {\r\n  for (size_t i = 0; i < iovs_len; i++) {\r\n    // Note: this will produce ugly broken output, but there's not much we can\r\n    // do about it until we have proper line-buffered WASI writer in the core.\r\n    // It's better than nothing though.\r\n    console_log((LogLevel){fd == STDERR_FILENO ? /*WARN*/ 1 : /*INFO*/\r\n                                2},\r\n                 CSTR(\"wasi\"), CSTR(__FILE__), __LINE__, iovs[i].buf,\r\n                 iovs[i].buf_len);\r\n    *retptr0 += iovs[i].buf_len;\r\n  }\r\n  return 0;\r\n}\r\n\r\n// BADF indicates end of iteration for preopens; we must return it instead of\r\n// \"success\" to prevent infinite loop.\r\nint32_t WASI_NAME(fd_prestat_get)(int32_t, int32_t) {\r\n  return __WASI_ERRNO_BADF;\r\n}\r\n\r\n// Actually exit runtime on `proc_exit`.\r\n_Noreturn void WASI_NAME(proc_exit)(int32_t code) { exit(code); }\r\n\r\n// There is another rogue import of sock_accept somewhere in .NET that doesn't\r\n// match the scheme above.\r\n// Maybe this one?\r\n// https://github.com/dotnet/runtime/blob/085ddb7f9b26f01ae1b6842db7eacb6b4042e031/src/mono/mono/component/mini-wasi-debugger.c#L12-L14\r\n\r\nint32_t sock_accept(int32_t, int32_t, int32_t) { return 0; }\r\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.props",
    "content": "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n\n  <PropertyGroup>\n    <WasmSingleFileBundle>true</WasmSingleFileBundle>\n    <TrimMode>full</TrimMode>\n    <InvariantGlobalization>true</InvariantGlobalization>\n    <EventSourceSupport>false</EventSourceSupport>\n    <UseSystemResourceKeys>true</UseSystemResourceKeys>\n    <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(EXPERIMENTAL_WASM_AOT)' == '1'\">\n    <OutputType>Library</OutputType>\n    <NativeLib>Shared</NativeLib>\n    <DefineConstants>$(DefineConstants);EXPERIMENTAL_WASM_AOT</DefineConstants>\n    <MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>\n    <UseAppHost>false</UseAppHost>\n    <SpacetimeNamespace>spacetime_10.0</SpacetimeNamespace>\n    <RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json</RestoreAdditionalProjectSources>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(EXPERIMENTAL_WASM_AOT)' != '1'\">\n    <!-- needs to be exe for initializers to be embedded correctly -->\n    <OutputType>Exe</OutputType>\n    <!-- conditional due to https://github.com/dotnet/runtime/issues/86186 which is not fixed in NAOT-LLVM -->\n    <DebuggerSupport>false</DebuggerSupport>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.targets",
    "content": "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n\n  <ItemGroup Condition=\"'$(EXPERIMENTAL_WASM_AOT)' == '1'\">\n    <NativeLibrary Include=\"$(MSBuildThisFileDirectory)..\\bindings.c\" />\n    <UnmanagedEntryPointsAssembly Include=\"SpacetimeDB.Runtime\" />\n\n    <WasmImport Include=\"$(SpacetimeNamespace)!table_id_from_name\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!index_id_from_name\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_table_row_count\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_table_scan_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_index_scan_range_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_btree_scan_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!row_iter_bsatn_advance\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!row_iter_bsatn_close\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_insert_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_delete_by_index_scan_range_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_delete_by_btree_scan_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!datastore_delete_all_by_eq_bsatn\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!bytes_source_read\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!bytes_sink_write\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!console_log\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!console_timer_start\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!console_timer_end\" />\n    <WasmImport Include=\"$(SpacetimeNamespace)!volatile_nonatomic_schedule_immediate\" />\n\n    <PackageReference Include=\"Microsoft.DotNet.ILCompiler.LLVM\" Version=\"8.0.0-*\" />\n    <PackageReference Include=\"runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM\" Version=\"8.0.0-*\" />\n    <PackageReference Include=\"Microsoft.NET.ILLink.Tasks\" Version=\"8.0.0-*\" Condition=\"'$(ILLinkTargetsPath)' == ''\" />\n\n    <CustomLinkerArg Include=\"-DEXPERIMENTAL_WASM_AOT\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(EXPERIMENTAL_WASM_AOT)' != '1'\">\n    <NativeFileReference Include=\"$(MSBuildThisFileDirectory)..\\bindings.c\" />\n    <_WasmNativeFileForLinking Include=\"@(NativeFileReference)\" />\n  </ItemGroup>\n\n  <!-- Auto-download WASI SDK on the first run. -->\n  <!-- Adapted from https://github.com/dotnet/dotnet-wasi-sdk/blob/2dbb00c779180873d3ed985e59e431f56404d8da/src/Wasi.Sdk/build/Wasi.Sdk.targets#L245-L262 -->\n  <!-- Executes before the errors in https://github.com/dotnet/runtime/blob/57fd56a99d4c97ac2f95fe84640f2a3f653f4dd7/src/mono/wasi/build/WasiApp.Native.targets#L41-L50. -->\n  <!-- TODO: remove when https://github.com/dotnet/runtime/issues/82788 is resolved. -->\n  <Target Name=\"ObtainWasiSdk\" BeforeTargets=\"_SetupWasiSdk;CheckWasmSdks\">\n    <PropertyGroup>\n      <WasiSdkVersion>24</WasiSdkVersion>\n\n      <WasiSdkDownloadTempFile>$([System.IO.Path]::Combine($(IntermediateOutputPath), \"wasi-sdk.$(WasiSdkVersion).tar.gz\"))</WasiSdkDownloadTempFile>\n\n      <WasiSdkArch Condition=\"$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64\">x86_64</WasiSdkArch>\n      <WasiSdkArch Condition=\"$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == Arm64\">arm64</WasiSdkArch>\n\n      <WasiSdkOS Condition=\"$([MSBuild]::IsOSPlatform('Windows'))\">windows</WasiSdkOS>\n      <WasiSdkOS Condition=\"$([MSBuild]::IsOSPlatform('Linux'))\">linux</WasiSdkOS>\n      <WasiSdkOS Condition=\"$([MSBuild]::IsOSPlatform('OSX'))\">macos</WasiSdkOS>\n\n      <WasiSdkUrl>https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(WasiSdkVersion)/wasi-sdk-$(WasiSdkVersion).0-$(WasiSdkArch)-$(WasiSdkOS).tar.gz</WasiSdkUrl>\n\n      <WasiSdkRoot>$([System.IO.Path]::Combine($([System.Environment]::GetFolderPath(SpecialFolder.UserProfile)), '.wasi-sdk', \"wasi-sdk-$(WasiSdkVersion)\"))</WasiSdkRoot>\n      <WASI_SDK_PATH>$(WasiSdkRoot)</WASI_SDK_PATH>\n      <WasiSysRoot>$([System.IO.Path]::Combine($(WasiSdkRoot), 'share', 'wasi-sysroot'))</WasiSysRoot>\n      <WasiClang>$([System.IO.Path]::Combine($(WasiSdkRoot), 'bin', 'clang'))</WasiClang>\n      <WasiClang Condition=\"$([MSBuild]::IsOSPlatform('Windows'))\">$(WasiClang).exe</WasiClang>\n    </PropertyGroup>\n\n    <DownloadFile\n            SourceUrl=\"$(WasiSdkUrl)\"\n            DestinationFolder=\"$(IntermediateOutputPath)\"\n            DestinationFileName=\"$([System.IO.Path]::GetFileName('$(WasiSdkDownloadTempFile)'))\"\n            Condition=\"!Exists('$(WasiClang)')\" />\n\n    <Message Importance=\"high\" Text=\"Extracting $(WasiSdkDownloadTempFile) to $(WasiSdkRoot)...\" Condition=\"!Exists('$(WasiClang)')\" />\n    <MakeDir Directories=\"$(WasiSdkRoot)\" />\n    <!-- Windows 10+ has tar built in, so this should work cross-platform -->\n    <Exec Command=\"tar -xf &quot;$(WasiSdkDownloadTempFile)&quot; -C &quot;$(WasiSdkRoot)&quot; --strip-components=1\" Condition=\"!Exists('$(WasiClang)')\" />\n  </Target>\n\n</Project>\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime/driver.h",
    "content": "// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\n#include <mono/metadata/assembly.h>\n#include <mono/metadata/loader.h>\n#include <mono/metadata/object.h>\n\nvoid mono_wasm_load_runtime(const char *unused, int debug_level);\nint mono_wasm_add_assembly(const char *name, const unsigned char *data,\n                           unsigned int size);\nMonoAssembly *mono_wasm_assembly_load(const char *name);\nMonoMethod *mono_wasi_assembly_get_entry_point(MonoAssembly *assembly);\nMonoClass *mono_wasm_assembly_find_class(MonoAssembly *assembly,\n                                         const char *namespace,\n                                         const char *name);\nMonoMethod *mono_wasm_assembly_find_method(MonoClass *klass, const char *name,\n                                           int arguments);\nvoid mono_wasm_invoke_method_ref(MonoMethod *method, MonoObject **this_arg_in,\n                                 void *params[], MonoObject **_out_exc,\n                                 MonoObject **out_result);\nint mono_unbox_int(MonoObject *obj);\nvoid add_assembly(const char *base_dir, const char *name);\n\nMonoArray *mono_wasm_obj_array_new(int size);\nvoid mono_wasm_obj_array_set(MonoArray *array, int idx, MonoObject *obj);\nMonoArray *mono_wasm_string_array_new(int size);\nMonoString *mono_wasm_string_from_js(const char *str);\nchar *mono_wasm_string_get_utf8(MonoString *str);\n\nMonoMethod *lookup_dotnet_method(const char *assembly_name,\n                                 const char *namespace, const char *type_name,\n                                 const char *method_name, int num_params);\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime.Tests/JwtClaimsTest.cs",
    "content": "namespace Runtime.Tests;\r\n\r\nusing SpacetimeDB;\r\n\r\npublic class JwtClaimsTest\r\n{\r\n    [Fact]\r\n    public void TestSubject()\r\n    {\r\n        var jwt = new JwtClaims(\r\n            \"{\\\"sub\\\":\\\"123\\\",\\\"name\\\":\\\"John Doe\\\",\\\"iss\\\":\\\"example.com\\\"}\",\r\n            Identity.FromHexString(\r\n                \"c200ef884364c1a99be0298dc68f2004e6b97c09d1b1658b7db22f51fb662059\"\r\n            )\r\n        );\r\n        Assert.Equal(\"123\", jwt.Subject);\r\n    }\r\n}\r\n"
  },
  {
    "path": "crates/bindings-csharp/Runtime.Tests/Runtime.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <TargetFramework>net8.0</TargetFramework>\r\n    <ImplicitUsings>enable</ImplicitUsings>\r\n    <Nullable>enable</Nullable>\r\n\r\n    <IsPackable>false</IsPackable>\r\n    <IsTestProject>true</IsTestProject>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"coverlet.collector\" Version=\"6.0.0\" />\r\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.8.0\" />\r\n    <PackageReference Include=\"xunit\" Version=\"2.5.3\" />\r\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.5.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Using Include=\"Xunit\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Runtime\\Runtime.csproj\" />\n  </ItemGroup>\r\n\r\n</Project>\r\n"
  },
  {
    "path": "crates/bindings-csharp/SpacetimeSharpSATS.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.0.31903.59\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Codegen\", \"Codegen\\Codegen.csproj\", \"{1E37FF71-567A-4AC8-947E-117098F01142}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Runtime\", \"Runtime\\Runtime.csproj\", \"{003DDE57-BB32-49F0-B94E-88F2D0414D19}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"BSATN.Codegen\", \"BSATN.Codegen\\BSATN.Codegen.csproj\", \"{771D5368-E850-4441-8B82-90B38BF3DD9E}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"BSATN.Runtime\", \"BSATN.Runtime\\BSATN.Runtime.csproj\", \"{A3AFB5AD-15DE-46CC-ACEE-E5819C07BE58}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Codegen.Tests\", \"Codegen.Tests\\Codegen.Tests.csproj\", \"{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"config\", \"config\", \"{12907A52-3915-43D6-B4D0-94E2938CB647}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\t.editorconfig = .editorconfig\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"sdk-test-connect-disconnect-cs\", \"..\\..\\modules\\sdk-test-connect-disconnect-cs\\sdk-test-connect-disconnect-cs.csproj\", \"{5393711C-44B0-4752-B8D0-852C73D6866F}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"sdk-test-cs\", \"..\\..\\modules\\sdk-test-cs\\sdk-test-cs.csproj\", \"{40F1C615-EDD9-463F-A012-B232F6710FA5}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"module-test-cs\", \"..\\..\\modules\\module-test-cs\\module-test-cs.csproj\", \"{FDACD960-168E-44F9-B036-2E29EA391BE7}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"sdk-tests\", \"sdk-tests\", \"{D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"benchmarks-cs\", \"..\\..\\modules\\benchmarks-cs\\benchmarks-cs.csproj\", \"{50E1AAE1-C42C-4C2F-B708-5190B0362165}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"BSATN.Runtime.Tests\", \"BSATN.Runtime.Tests\\BSATN.Runtime.Tests.csproj\", \"{FCF18E21-FB59-4A4D-A9ED-B85D2874E536}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{1E37FF71-567A-4AC8-947E-117098F01142}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{1E37FF71-567A-4AC8-947E-117098F01142}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{1E37FF71-567A-4AC8-947E-117098F01142}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{1E37FF71-567A-4AC8-947E-117098F01142}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{003DDE57-BB32-49F0-B94E-88F2D0414D19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{003DDE57-BB32-49F0-B94E-88F2D0414D19}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{003DDE57-BB32-49F0-B94E-88F2D0414D19}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{003DDE57-BB32-49F0-B94E-88F2D0414D19}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{771D5368-E850-4441-8B82-90B38BF3DD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{771D5368-E850-4441-8B82-90B38BF3DD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{771D5368-E850-4441-8B82-90B38BF3DD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{771D5368-E850-4441-8B82-90B38BF3DD9E}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{A3AFB5AD-15DE-46CC-ACEE-E5819C07BE58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{A3AFB5AD-15DE-46CC-ACEE-E5819C07BE58}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{A3AFB5AD-15DE-46CC-ACEE-E5819C07BE58}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{A3AFB5AD-15DE-46CC-ACEE-E5819C07BE58}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{5393711C-44B0-4752-B8D0-852C73D6866F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{5393711C-44B0-4752-B8D0-852C73D6866F}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{40F1C615-EDD9-463F-A012-B232F6710FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{40F1C615-EDD9-463F-A012-B232F6710FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{FDACD960-168E-44F9-B036-2E29EA391BE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{FDACD960-168E-44F9-B036-2E29EA391BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{50E1AAE1-C42C-4C2F-B708-5190B0362165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{50E1AAE1-C42C-4C2F-B708-5190B0362165}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{FCF18E21-FB59-4A4D-A9ED-B85D2874E536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{FCF18E21-FB59-4A4D-A9ED-B85D2874E536}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{FCF18E21-FB59-4A4D-A9ED-B85D2874E536}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{FCF18E21-FB59-4A4D-A9ED-B85D2874E536}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(NestedProjects) = preSolution\r\n\t\t{5393711C-44B0-4752-B8D0-852C73D6866F} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}\r\n\t\t{40F1C615-EDD9-463F-A012-B232F6710FA5} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}\r\n\t\t{FDACD960-168E-44F9-B036-2E29EA391BE7} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}\r\n\t\t{50E1AAE1-C42C-4C2F-B708-5190B0362165} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {8A5DE392-1C9D-4806-B6C7-EDD4D33C5D1E}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "crates/bindings-macro/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-bindings-macro\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Easy support for interacting between SpacetimeDB and Rust.\"\nrust-version.workspace = true\n\n[lib]\nproc-macro = true\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n[dependencies]\nspacetimedb-primitives.workspace = true\n\nhumantime.workspace = true\nproc-macro2.workspace = true\nquote.workspace = true\nsyn.workspace = true\nheck.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/bindings-macro/src/lib.rs",
    "content": "//! Defines procedural macros like `#[spacetimedb::table]`,\n//! simplifying writing SpacetimeDB modules in Rust.\n\n// DO NOT WRITE (public) DOCS IN THIS MODULE.\n// Docs should be written in the `spacetimedb` crate (i.e. `bindings/`) at reexport sites\n// using `#[doc(inline)]`.\n// We do this so that links to library traits, structs, etc can resolve correctly.\n//\n// (private documentation for the macro authors is totally fine here and you SHOULD write that!)\n\nmod procedure;\n\n#[proc_macro_attribute]\npub fn procedure(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    cvt_attr::<ItemFn>(args, item, quote!(), |args, original_function| {\n        let args = procedure::ProcedureArgs::parse(args)?;\n        procedure::procedure_impl(args, original_function)\n    })\n}\nmod reducer;\n\n#[proc_macro_attribute]\npub fn reducer(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    cvt_attr::<ItemFn>(args, item, quote!(), |args, original_function| {\n        let args = reducer::ReducerArgs::parse(args)?;\n        reducer::reducer_impl(args, original_function)\n    })\n}\nmod sats;\nmod table;\n\n#[proc_macro_attribute]\npub fn table(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    // put this on the struct so we don't get unknown attribute errors\n    let derive_table_helper: syn::Attribute = derive_table_helper_attr();\n\n    ok_or_compile_error(|| {\n        let item = TokenStream::from(item);\n        let mut derive_input: syn::DeriveInput = syn::parse2(item.clone())?;\n\n        // Add `derive(__TableHelper)` only if it's not already in the attributes of the `derive_input.`\n        // If multiple `#[table]` attributes are applied to the same `struct` item,\n        // this will ensure that we don't emit multiple conflicting implementations\n        // for traits like `SpacetimeType`, `Serialize` and `Deserialize`.\n        //\n        // We need to push at the end, rather than the beginning,\n        // because rustc expands attribute macros (including derives) top-to-bottom,\n        // and we need *all* `#[table]` attributes *before* the `derive(__TableHelper)`.\n        // This way, the first `table` will insert a `derive(__TableHelper)`,\n        // and all subsequent `#[table]`s on the same `struct` will see it,\n        // and not add another.\n        //\n        // Note, thank goodness, that `syn`'s `PartialEq` impls (provided with the `extra-traits` feature)\n        // skip any [`Span`]s contained in the items,\n        // thereby comparing for syntactic rather than structural equality. This shouldn't matter,\n        // since we expect that the `derive_table_helper` will always have the same [`Span`]s,\n        // but it's nice to know.\n        if !derive_input.attrs.contains(&derive_table_helper) {\n            derive_input.attrs.push(derive_table_helper);\n        }\n\n        let args = table::TableArgs::parse(args.into(), &derive_input.ident)?;\n        let generated = table::table_impl(args, &derive_input)?;\n        Ok(TokenStream::from_iter([quote!(#derive_input), generated]))\n    })\n}\nmod util;\nmod view;\n\n#[proc_macro_attribute]\npub fn view(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    let item_ts: TokenStream = item.into();\n    let original_function = match syn::parse2::<ItemFn>(item_ts.clone()) {\n        Ok(f) => f,\n        Err(e) => return TokenStream::from_iter([item_ts, e.into_compile_error()]).into(),\n    };\n    let args = match view::ViewArgs::parse(args.into(), &original_function.sig.ident) {\n        Ok(a) => a,\n        Err(e) => return TokenStream::from_iter([item_ts, e.into_compile_error()]).into(),\n    };\n    match view::view_impl(args, &original_function) {\n        Ok(ts) => ts.into(),\n        Err(e) => TokenStream::from_iter([item_ts, e.into_compile_error()]).into(),\n    }\n}\n\nuse proc_macro::TokenStream as StdTokenStream;\nuse proc_macro2::TokenStream;\nuse quote::quote;\nuse std::time::Duration;\nuse syn::{parse::ParseStream, Attribute};\nuse syn::{ItemConst, ItemFn};\nuse util::{cvt_attr, ok_or_compile_error};\n\nmod sym {\n    /// A symbol known at compile-time against\n    /// which identifiers and paths may be matched.\n    pub struct Symbol(&'static str);\n\n    macro_rules! symbol {\n        ($ident:ident) => {\n            symbol!($ident, $ident);\n        };\n        ($const:ident, $ident:ident) => {\n            #[allow(non_upper_case_globals)]\n            #[doc = concat!(\"Matches `\", stringify!($ident), \"`.\")]\n            pub const $const: Symbol = Symbol(stringify!($ident));\n        };\n    }\n\n    symbol!(accessor);\n    symbol!(at);\n    symbol!(auto_inc);\n    symbol!(btree);\n    symbol!(client_connected);\n    symbol!(client_disconnected);\n    symbol!(column);\n    symbol!(columns);\n    symbol!(crate_, crate);\n    symbol!(direct);\n    symbol!(hash);\n    symbol!(index);\n    symbol!(init);\n    symbol!(name);\n    symbol!(primary_key);\n    symbol!(private);\n    symbol!(public);\n    symbol!(repr);\n    symbol!(sats);\n    symbol!(scheduled);\n    symbol!(unique);\n    symbol!(update);\n    symbol!(default);\n    symbol!(event);\n\n    symbol!(u8);\n    symbol!(i8);\n    symbol!(u16);\n    symbol!(i16);\n    symbol!(u32);\n    symbol!(i32);\n    symbol!(u64);\n    symbol!(i64);\n    symbol!(u128);\n    symbol!(i128);\n    symbol!(f32);\n    symbol!(f64);\n\n    impl PartialEq<Symbol> for syn::Ident {\n        fn eq(&self, sym: &Symbol) -> bool {\n            self == sym.0\n        }\n    }\n    impl PartialEq<Symbol> for &syn::Ident {\n        fn eq(&self, sym: &Symbol) -> bool {\n            *self == sym.0\n        }\n    }\n    impl PartialEq<Symbol> for syn::Path {\n        fn eq(&self, sym: &Symbol) -> bool {\n            self.is_ident(sym)\n        }\n    }\n    impl PartialEq<Symbol> for &syn::Path {\n        fn eq(&self, sym: &Symbol) -> bool {\n            self.is_ident(sym)\n        }\n    }\n    impl std::fmt::Display for Symbol {\n        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n            f.write_str(self.0)\n        }\n    }\n    impl std::borrow::Borrow<str> for Symbol {\n        fn borrow(&self) -> &str {\n            self.0\n        }\n    }\n}\n\n/// It turns out to be shockingly difficult to construct an [`Attribute`].\n/// That type is not [`Parse`], instead having two distinct methods\n/// for parsing \"inner\" vs \"outer\" attributes.\n///\n/// We need this [`Attribute`] in [`table`] so that we can \"pushnew\" it\n/// onto the end of a list of attributes. See comments within [`table`].\nfn derive_table_helper_attr() -> Attribute {\n    let source = quote!(#[derive(spacetimedb::__TableHelper)]);\n\n    syn::parse::Parser::parse2(Attribute::parse_outer, source)\n        .unwrap()\n        .into_iter()\n        .next()\n        .unwrap()\n}\n\n/// Special alias for `derive(SpacetimeType)`, aka [`schema_type`], for use by [`table`].\n///\n/// Provides helper attributes for `#[spacetimedb::table]`, so that we don't get unknown attribute errors.\n#[doc(hidden)]\n#[proc_macro_derive(__TableHelper, attributes(sats, unique, auto_inc, primary_key, index, default))]\npub fn table_helper(input: StdTokenStream) -> StdTokenStream {\n    schema_type(input)\n}\n\n#[proc_macro]\npub fn duration(input: StdTokenStream) -> StdTokenStream {\n    let dur = syn::parse_macro_input!(input with parse_duration);\n    let (secs, nanos) = (dur.as_secs(), dur.subsec_nanos());\n    quote!({\n        const DUR: ::core::time::Duration = ::core::time::Duration::new(#secs, #nanos);\n        DUR\n    })\n    .into()\n}\n\nfn parse_duration(input: ParseStream) -> syn::Result<Duration> {\n    let lookahead = input.lookahead1();\n    let (s, span) = if lookahead.peek(syn::LitStr) {\n        let s = input.parse::<syn::LitStr>()?;\n        (s.value(), s.span())\n    } else if lookahead.peek(syn::LitInt) {\n        let i = input.parse::<syn::LitInt>()?;\n        (i.to_string(), i.span())\n    } else {\n        return Err(lookahead.error());\n    };\n    humantime::parse_duration(&s).map_err(|e| syn::Error::new(span, format_args!(\"can't parse as duration: {e}\")))\n}\n\n/// A helper for the common bits of the derive macros.\nfn sats_derive(\n    input: StdTokenStream,\n    assume_in_module: bool,\n    logic: impl FnOnce(&sats::SatsType) -> TokenStream,\n) -> StdTokenStream {\n    let input = syn::parse_macro_input!(input as syn::DeriveInput);\n    let crate_fallback = if assume_in_module {\n        quote!(spacetimedb::spacetimedb_lib)\n    } else {\n        quote!(spacetimedb_lib)\n    };\n    sats::sats_type_from_derive(&input, crate_fallback)\n        .map(|ty| logic(&ty))\n        .unwrap_or_else(syn::Error::into_compile_error)\n        .into()\n}\n\n#[proc_macro_derive(Deserialize, attributes(sats))]\npub fn deserialize(input: StdTokenStream) -> StdTokenStream {\n    sats_derive(input, false, sats::derive_deserialize)\n}\n\n#[proc_macro_derive(Serialize, attributes(sats))]\npub fn serialize(input: StdTokenStream) -> StdTokenStream {\n    sats_derive(input, false, sats::derive_serialize)\n}\n\n#[proc_macro_derive(SpacetimeType, attributes(sats))]\npub fn schema_type(input: StdTokenStream) -> StdTokenStream {\n    sats_derive(input, true, |ty| {\n        let ident = ty.ident;\n        let name = &ty.name;\n\n        let krate = &ty.krate;\n        TokenStream::from_iter([\n            sats::derive_satstype(ty),\n            sats::derive_deserialize(ty),\n            sats::derive_serialize(ty),\n            // unfortunately, generic types don't work in modules at the moment.\n            quote!(#krate::__make_register_reftype!(#ident, #name);),\n        ])\n    })\n}\n\n#[proc_macro_attribute]\npub fn client_visibility_filter(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    ok_or_compile_error(|| {\n        if !args.is_empty() {\n            return Err(syn::Error::new_spanned(\n                TokenStream::from(args),\n                \"The `client_visibility_filter` attribute does not accept arguments\",\n            ));\n        }\n\n        let item: ItemConst = syn::parse(item)?;\n        let rls_ident = item.ident.clone();\n        let register_rls_symbol = format!(\"__preinit__20_register_row_level_security_{rls_ident}\");\n\n        Ok(quote! {\n            #item\n\n            const _: () = {\n                #[unsafe(export_name = #register_rls_symbol)]\n                extern \"C\" fn __register_client_visibility_filter() {\n                    spacetimedb::rt::register_row_level_security(#rls_ident.sql_text())\n                }\n            };\n        })\n    })\n}\n\n/// Known setting names and their registration code generators.\nconst KNOWN_SETTINGS: &[&str] = &[\"CASE_CONVERSION_POLICY\"];\n\n#[proc_macro_attribute]\npub fn settings(args: StdTokenStream, item: StdTokenStream) -> StdTokenStream {\n    ok_or_compile_error(|| {\n        if !args.is_empty() {\n            return Err(syn::Error::new_spanned(\n                TokenStream::from(args),\n                \"The `settings` attribute does not accept arguments\",\n            ));\n        }\n\n        let item: ItemConst = syn::parse(item)?;\n        let ident = &item.ident;\n        let ident_str = ident.to_string();\n\n        if !KNOWN_SETTINGS.contains(&ident_str.as_str()) {\n            return Err(syn::Error::new_spanned(\n                ident,\n                format!(\n                    \"unknown setting `{ident_str}`. Known settings: {}\",\n                    KNOWN_SETTINGS.join(\", \")\n                ),\n            ));\n        }\n\n        // Use a fixed export name so that two `#[spacetimedb::settings]` consts\n        // for the same setting produce a linker error (duplicate symbol).\n        let register_symbol = format!(\"__preinit__05_setting_{ident_str}\");\n\n        // Generate the registration call based on the setting name.\n        let register_call = match ident_str.as_str() {\n            \"CASE_CONVERSION_POLICY\" => quote! {\n                spacetimedb::rt::register_case_conversion_policy(#ident)\n            },\n            _ => unreachable!(\"validated above\"),\n        };\n\n        Ok(quote! {\n            #item\n\n            const _: () = {\n                #[unsafe(export_name = #register_symbol)]\n                extern \"C\" fn __register_setting() {\n                    #register_call\n                }\n            };\n        })\n    })\n}\n"
  },
  {
    "path": "crates/bindings-macro/src/procedure.rs",
    "content": "use crate::reducer::{assert_only_lifetime_generics, extract_typed_args, generate_explicit_names_impl};\nuse crate::sym;\nuse crate::util::{check_duplicate, ident_to_litstr, match_meta};\nuse proc_macro2::TokenStream;\nuse quote::quote;\nuse syn::parse::Parser as _;\nuse syn::{ItemFn, LitStr};\n\n#[derive(Default)]\npub(crate) struct ProcedureArgs {\n    /// For consistency with reducers: allow specifying a different export name than the Rust function name.\n    name: Option<LitStr>,\n}\n\nimpl ProcedureArgs {\n    pub(crate) fn parse(input: TokenStream) -> syn::Result<Self> {\n        let mut args = Self::default();\n        syn::meta::parser(|meta| {\n            match_meta!(match meta {\n                sym::name => {\n                    check_duplicate(&args.name, &meta)?;\n                    args.name = Some(meta.value()?.parse()?);\n                }\n            });\n            Ok(())\n        })\n        .parse2(input)?;\n        Ok(args)\n    }\n}\n\npub(crate) fn procedure_impl(_args: ProcedureArgs, original_function: &ItemFn) -> syn::Result<TokenStream> {\n    let func_name = &original_function.sig.ident;\n    let vis = &original_function.vis;\n    let explicit_name = _args.name.as_ref();\n\n    let procedure_name = ident_to_litstr(func_name);\n\n    assert_only_lifetime_generics(original_function, \"procedures\")?;\n\n    let typed_args = extract_typed_args(original_function)?;\n\n    // TODO: Require that procedures be `async` functions syntactically,\n    // and use `futures_util::FutureExt::now_or_never` to poll them.\n    // if !&original_function.sig.asyncness.is_some() {\n    //     return Err(syn::Error::new_spanned(\n    //         original_function.sig.clone(),\n    //         \"procedures must be `async`\",\n    //     ));\n    // };\n\n    // Extract all function parameter names.\n    let opt_arg_names = typed_args.iter().map(|arg| {\n        if let syn::Pat::Ident(i) = &*arg.pat {\n            let name = i.ident.to_string();\n            quote!(Some(#name))\n        } else {\n            quote!(None)\n        }\n    });\n\n    let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::<Vec<_>>();\n    let first_arg_ty = arg_tys.first().into_iter();\n    let rest_arg_tys = arg_tys.iter().skip(1);\n\n    // Extract the return type.\n    let ret_ty_for_assert = match &original_function.sig.output {\n        syn::ReturnType::Default => None,\n        syn::ReturnType::Type(_, t) => Some(&**t),\n    }\n    .into_iter();\n\n    let ret_ty_for_info = match &original_function.sig.output {\n        syn::ReturnType::Default => quote!(()),\n        syn::ReturnType::Type(_, t) => quote!(#t),\n    };\n\n    let register_describer_symbol = format!(\"__preinit__20_register_describer_{}\", procedure_name.value());\n\n    let lifetime_params = &original_function.sig.generics;\n    let lifetime_where_clause = &lifetime_params.where_clause;\n\n    let generated_describe_function = quote! {\n        #[unsafe(export_name = #register_describer_symbol)]\n        pub extern \"C\" fn __register_describer() {\n            spacetimedb::rt::register_procedure::<_, _, #func_name>(#func_name)\n        }\n    };\n\n    let generate_explicit_names = generate_explicit_names_impl(&procedure_name.value(), func_name, explicit_name);\n\n    Ok(quote! {\n        const _: () = {\n            #generated_describe_function\n        };\n        #[allow(non_camel_case_types)]\n        #vis struct #func_name { _never: ::core::convert::Infallible }\n        const _: () = {\n            fn _assert_args #lifetime_params () #lifetime_where_clause {\n                #(let _ = <#first_arg_ty as spacetimedb::rt::ProcedureContextArg>::_ITEM;)*\n                #(let _ = <#rest_arg_tys as spacetimedb::rt::ProcedureArg>::_ITEM;)*\n                #(let _ = <#ret_ty_for_assert as spacetimedb::rt::IntoProcedureResult>::to_result;)*\n            }\n        };\n        impl #func_name {\n            fn invoke(__ctx: &mut spacetimedb::ProcedureContext, __args: &[u8]) -> spacetimedb::ProcedureResult {\n                spacetimedb::rt::invoke_procedure(#func_name, __ctx, __args)\n            }\n        }\n        #[automatically_derived]\n        impl spacetimedb::rt::FnInfo for #func_name {\n            /// The type of this function.\n            type Invoke = spacetimedb::rt::ProcedureFn;\n\n            /// The function kind, which will cause scheduled tables to accept procedures.\n            type FnKind = spacetimedb::rt::FnKindProcedure<#ret_ty_for_info>;\n\n            /// The name of this function\n            const NAME: &'static str = #procedure_name;\n\n            /// The parameter names of this function\n            const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*];\n\n            /// The pointer for invoking this function\n            const INVOKE: spacetimedb::rt::ProcedureFn = #func_name::invoke;\n\n            /// The return type of this function\n            fn return_type(ts: &mut impl spacetimedb::sats::typespace::TypespaceBuilder) -> Option<spacetimedb::sats::AlgebraicType> {\n                Some(<#ret_ty_for_info as spacetimedb::SpacetimeType>::make_type(ts))\n            }\n        }\n\n        #generate_explicit_names\n    })\n}\n"
  },
  {
    "path": "crates/bindings-macro/src/reducer.rs",
    "content": "use crate::sym;\nuse crate::util::{check_duplicate, check_duplicate_msg, ident_to_litstr, match_meta};\nuse proc_macro2::{Span, TokenStream};\nuse quote::{quote, quote_spanned};\nuse syn::parse::Parser as _;\nuse syn::spanned::Spanned;\nuse syn::{FnArg, Ident, ItemFn, LitStr, PatType};\n\n#[derive(Default)]\npub(crate) struct ReducerArgs {\n    name: Option<LitStr>,\n    lifecycle: Option<LifecycleReducer>,\n}\n\nenum LifecycleReducer {\n    Init(Span),\n    ClientConnected(Span),\n    ClientDisconnected(Span),\n    Update(Span),\n}\nimpl LifecycleReducer {\n    fn to_lifecycle_value(&self) -> Option<TokenStream> {\n        let (Self::Init(span) | Self::ClientConnected(span) | Self::ClientDisconnected(span) | Self::Update(span)) =\n            *self;\n        let name = match self {\n            Self::Init(_) => \"Init\",\n            Self::ClientConnected(_) => \"OnConnect\",\n            Self::ClientDisconnected(_) => \"OnDisconnect\",\n            Self::Update(_) => return None,\n        };\n        let ident = Ident::new(name, span);\n        Some(quote_spanned!(span => spacetimedb::rt::LifecycleReducer::#ident))\n    }\n}\n\nimpl ReducerArgs {\n    pub(crate) fn parse(input: TokenStream) -> syn::Result<Self> {\n        let mut args = Self::default();\n        syn::meta::parser(|meta| {\n            let mut set_lifecycle = |kind: fn(Span) -> _| -> syn::Result<()> {\n                check_duplicate_msg(&args.lifecycle, &meta, \"already specified a lifecycle reducer kind\")?;\n                args.lifecycle = Some(kind(meta.path.span()));\n                Ok(())\n            };\n            match_meta!(match meta {\n                sym::init => set_lifecycle(LifecycleReducer::Init)?,\n                sym::client_connected => set_lifecycle(LifecycleReducer::ClientConnected)?,\n                sym::client_disconnected => set_lifecycle(LifecycleReducer::ClientDisconnected)?,\n                sym::update => set_lifecycle(LifecycleReducer::Update)?,\n                sym::name => {\n                    check_duplicate(&args.name, &meta)?;\n                    args.name = Some(meta.value()?.parse()?);\n                }\n            });\n            Ok(())\n        })\n        .parse2(input)?;\n        Ok(args)\n    }\n}\n\npub(crate) fn assert_only_lifetime_generics(original_function: &ItemFn, function_kind_plural: &str) -> syn::Result<()> {\n    for param in &original_function.sig.generics.params {\n        let err = |msg| syn::Error::new_spanned(param, msg);\n        match param {\n            syn::GenericParam::Lifetime(_) => {}\n            syn::GenericParam::Type(_) => {\n                return Err(err(format!(\n                    \"type parameters are not allowed on {function_kind_plural}\"\n                )));\n            }\n            syn::GenericParam::Const(_) => {\n                return Err(err(format!(\n                    \"const parameters are not allowed on {function_kind_plural}\"\n                )));\n            }\n        }\n    }\n    Ok(())\n}\n\n/// Extract all function parameters, except for `self` ones that aren't allowed.\npub(crate) fn extract_typed_args(original_function: &ItemFn) -> syn::Result<Vec<&PatType>> {\n    original_function\n        .sig\n        .inputs\n        .iter()\n        .map(|arg| match arg {\n            FnArg::Typed(arg) => Ok(arg),\n            _ => Err(syn::Error::new_spanned(arg, \"expected typed argument\")),\n        })\n        .collect()\n}\n\npub(crate) fn reducer_impl(args: ReducerArgs, original_function: &ItemFn) -> syn::Result<TokenStream> {\n    let func_name = &original_function.sig.ident;\n    let vis = &original_function.vis;\n\n    let reducer_name = ident_to_litstr(func_name);\n\n    assert_only_lifetime_generics(original_function, \"reducers\")?;\n\n    let lifecycle = args.lifecycle.iter().filter_map(|lc| lc.to_lifecycle_value());\n\n    let typed_args = extract_typed_args(original_function)?;\n\n    let explicit_name = args.name.as_ref();\n\n    let generate_explicit_names = generate_explicit_names_impl(&reducer_name.value(), func_name, explicit_name);\n\n    // Extract all function parameter names.\n    let opt_arg_names = typed_args.iter().map(|arg| {\n        if let syn::Pat::Ident(i) = &*arg.pat {\n            let name = i.ident.to_string();\n            quote!(Some(#name))\n        } else {\n            quote!(None)\n        }\n    });\n\n    let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::<Vec<_>>();\n    let first_arg_ty = arg_tys.first().into_iter();\n    let rest_arg_tys = arg_tys.iter().skip(1);\n\n    // Extract the return type.\n    let ret_ty = match &original_function.sig.output {\n        syn::ReturnType::Default => None,\n        syn::ReturnType::Type(_, t) => Some(&**t),\n    }\n    .into_iter();\n\n    let register_describer_symbol = format!(\"__preinit__20_register_describer_{}\", reducer_name.value());\n\n    let lt_params = &original_function.sig.generics;\n    let lt_where_clause = &lt_params.where_clause;\n\n    let generated_describe_function = quote! {\n        #[unsafe(export_name = #register_describer_symbol)]\n        pub extern \"C\" fn __register_describer() {\n            spacetimedb::rt::register_reducer::<_, #func_name>(#func_name)\n        }\n    };\n\n    Ok(quote! {\n        const _: () = {\n            #generated_describe_function\n        };\n        #[allow(non_camel_case_types)]\n        #vis struct #func_name { _never: ::core::convert::Infallible }\n        const _: () = {\n            fn _assert_args #lt_params () #lt_where_clause {\n                #(let _ = <#first_arg_ty as spacetimedb::rt::ReducerContextArg>::_ITEM;)*\n                #(let _ = <#rest_arg_tys as spacetimedb::rt::ReducerArg>::_ITEM;)*\n                #(let _ = <#ret_ty as spacetimedb::rt::IntoReducerResult>::into_result;)*\n            }\n        };\n        impl #func_name {\n            fn invoke(__ctx: &spacetimedb::ReducerContext, __args: &[u8]) -> spacetimedb::ReducerResult {\n                spacetimedb::rt::invoke_reducer(#func_name, __ctx, __args)\n            }\n        }\n        #[automatically_derived]\n        impl spacetimedb::rt::FnInfo for #func_name {\n            type Invoke = spacetimedb::rt::ReducerFn;\n            /// The function kind, which will cause scheduled tables to accept reducers.\n            type FnKind = spacetimedb::rt::FnKindReducer;\n            const NAME: &'static str = #reducer_name;\n            #(const LIFECYCLE: Option<spacetimedb::rt::LifecycleReducer> = Some(#lifecycle);)*\n            const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*];\n            const INVOKE: Self::Invoke = #func_name::invoke;\n        }\n\n        #generate_explicit_names\n    })\n}\n\npub(crate) fn generate_explicit_names_impl(\n    func_name: &str,\n    func_handle: &Ident,\n    explicit_name: Option<&LitStr>,\n) -> TokenStream {\n    let mut explicit_names_body = Vec::new();\n\n    // Table name\n    if let Some(explicit_name) = explicit_name {\n        explicit_names_body.push(quote! {\n            names.insert_function(\n                #func_name,\n                #explicit_name,\n            );\n        });\n    };\n\n    quote! {\n\n        impl spacetimedb::rt::ExplicitNames for #func_handle {\n            fn explicit_names() -> spacetimedb::spacetimedb_lib::ExplicitNames {\n                let mut names = spacetimedb::spacetimedb_lib::ExplicitNames::default();\n                #(#explicit_names_body)*\n                names\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-macro/src/sats.rs",
    "content": "extern crate core;\nextern crate proc_macro;\n\nuse proc_macro2::{Span, TokenStream};\nuse quote::{quote, quote_spanned, ToTokens};\nuse syn::punctuated::Pair;\nuse syn::spanned::Spanned;\nuse syn::{LitStr, Token};\n\nuse crate::sym;\nuse crate::util::{check_duplicate, match_meta};\n\npub(crate) struct SatsType<'a> {\n    pub ident: &'a syn::Ident,\n    pub generics: &'a syn::Generics,\n    pub name: LitStr,\n    pub krate: TokenStream,\n    // may want to use in the future\n    #[allow(unused)]\n    pub original_attrs: &'a [syn::Attribute],\n    pub data: SatsTypeData<'a>,\n    /// Was the type marked as `#[repr(C)]`?\n    pub is_repr_c: bool,\n}\n\npub(crate) enum SatsTypeData<'a> {\n    Product(Vec<SatsField<'a>>),\n    Sum(Vec<SatsVariant<'a>>),\n}\n\n#[derive(Clone)]\npub(crate) struct SatsField<'a> {\n    pub ident: Option<&'a syn::Ident>,\n    pub vis: &'a syn::Visibility,\n    pub name: Option<String>,\n    pub ty: &'a syn::Type,\n    pub original_attrs: &'a [syn::Attribute],\n}\n\npub(crate) struct SatsVariant<'a> {\n    pub ident: &'a syn::Ident,\n    pub name: String,\n    pub ty: Option<&'a syn::Type>,\n    pub member: Option<syn::Member>,\n    // may want to use in the future\n    #[allow(unused)]\n    pub original_attrs: &'a [syn::Attribute],\n}\n\npub(crate) fn sats_type_from_derive(\n    input: &syn::DeriveInput,\n    crate_fallback: TokenStream,\n) -> syn::Result<SatsType<'_>> {\n    let data = match &input.data {\n        syn::Data::Struct(struc) => {\n            let fields = struc.fields.iter().map(|field| SatsField {\n                ident: field.ident.as_ref(),\n                vis: &field.vis,\n                name: field.ident.as_ref().map(syn::Ident::to_string),\n                ty: &field.ty,\n                original_attrs: &field.attrs,\n            });\n            SatsTypeData::Product(fields.collect())\n        }\n        syn::Data::Enum(enu) => {\n            let variants = enu.variants.iter().map(|var| {\n                let (member, ty) = variant_data(var)?.unzip();\n                Ok(SatsVariant {\n                    ident: &var.ident,\n                    name: var.ident.to_string(),\n                    ty,\n                    member,\n                    original_attrs: &var.attrs,\n                })\n            });\n            SatsTypeData::Sum(variants.collect::<syn::Result<Vec<_>>>()?)\n        }\n        syn::Data::Union(u) => return Err(syn::Error::new(u.union_token.span, \"unions not supported\")),\n    };\n    extract_sats_type(&input.ident, &input.generics, &input.attrs, data, crate_fallback)\n}\n\nfn is_repr_c(attrs: &[syn::Attribute]) -> bool {\n    let mut is_repr_c = false;\n    for attr in attrs.iter().filter(|a| a.path() == sym::repr) {\n        let _ = attr.parse_nested_meta(|meta| {\n            is_repr_c |= meta.path.is_ident(\"C\");\n            Ok(())\n        });\n    }\n    is_repr_c\n}\n\npub(crate) fn extract_sats_type<'a>(\n    ident: &'a syn::Ident,\n    generics: &'a syn::Generics,\n    attrs: &'a [syn::Attribute],\n    data: SatsTypeData<'a>,\n    crate_fallback: TokenStream,\n) -> syn::Result<SatsType<'a>> {\n    let mut name = None;\n    let mut krate = None;\n    for attr in attrs {\n        if attr.path() != sym::sats {\n            continue;\n        }\n        attr.parse_nested_meta(|meta| {\n            match_meta!(match meta {\n                sym::crate_ => {\n                    check_duplicate(&krate, &meta)?;\n                    let value = meta.value()?;\n                    let v = value.call(syn::Path::parse_mod_style)?;\n                    krate = Some(v.into_token_stream());\n                }\n                sym::name => {\n                    check_duplicate(&name, &meta)?;\n                    let value = meta.value()?;\n                    let v = value.parse::<LitStr>()?;\n                    name = Some(v);\n                }\n            });\n            Ok(())\n        })?;\n    }\n    let krate = krate.unwrap_or(crate_fallback);\n    let name = name.unwrap_or_else(|| crate::util::ident_to_litstr(ident));\n\n    let is_repr_c = is_repr_c(attrs);\n\n    Ok(SatsType {\n        ident,\n        generics,\n        name,\n        krate,\n        original_attrs: attrs,\n        data,\n        is_repr_c,\n    })\n}\n\npub(crate) fn derive_satstype(ty: &SatsType<'_>) -> TokenStream {\n    let ty_name = &ty.name;\n    let name = &ty.ident;\n    let krate = &ty.krate;\n\n    let mut add_impls_for_plain_enum = false;\n    let typ = match &ty.data {\n        SatsTypeData::Product(fields) => {\n            let fields = fields.iter().map(|field| {\n                let field_name = match &field.name {\n                    Some(name) => quote!(Some(#name)),\n                    None => quote!(None),\n                };\n                let ty = field.ty;\n                quote!((\n                    #field_name,\n                    <#ty as #krate::SpacetimeType>::make_type(__typespace)\n                ))\n            });\n            let len = fields.len();\n            quote!(\n                #krate::sats::AlgebraicType::product::<\n                    [(Option<&str>, #krate::sats::AlgebraicType); #len]\n                >(\n                    [#(#fields),*]\n                )\n            )\n        }\n        SatsTypeData::Sum(variants) => {\n            // To allow an enum, with all-unit variants, as an index key type,\n            // add derive `Filterable` for the enum.\n            add_impls_for_plain_enum = variants.iter().all(|var| var.ty.is_none());\n\n            let unit = syn::Type::Tuple(syn::TypeTuple {\n                paren_token: Default::default(),\n                elems: Default::default(),\n            });\n            let variants = variants.iter().map(|var| {\n                let variant_name = &var.name;\n                let ty = var.ty.unwrap_or(&unit);\n                quote!((\n                    #variant_name,\n                    <#ty as #krate::SpacetimeType>::make_type(__typespace)\n                ))\n            });\n            let len = variants.len();\n            quote!(\n                #krate::sats::AlgebraicType::sum::<\n                    [(&str, #krate::sats::AlgebraicType); #len]\n                >(\n                    [#(#variants),*]\n                )\n            )\n        }\n    };\n\n    let mut sats_generics = ty.generics.clone();\n    // the 'static here is an unfortunate restriction from TypeId :(\n    add_type_bounds(&mut sats_generics, &quote!(#krate::SpacetimeType + 'static));\n    let (impl_generics, ty_generics, where_clause) = sats_generics.split_for_impl();\n\n    // TypeId::of() requires all the lifetimes to be 'static\n    let mut typeid_generics = sats_generics.clone();\n    let static_lt = syn::Lifetime::new(\"'static\", Span::call_site());\n    for param in &mut typeid_generics.params {\n        if let syn::GenericParam::Lifetime(param) = param {\n            param.lifetime = static_lt.clone();\n        }\n    }\n    let (_, typeid_ty_generics, _) = typeid_generics.split_for_impl();\n\n    let impl_plain_enum_extras = if add_impls_for_plain_enum {\n        // These will mostly be empty as lifetime and type parameters must be constrained\n        // but const parameters don't require constraining.\n        let mut generics = ty.generics.clone();\n        add_type_bounds(&mut generics, &quote!(#krate::FilterableValue));\n        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();\n        // Assume that the type is `Copy`, as most all-unit enums will be.\n        let filterable_impl = quote! {\n            #[automatically_derived]\n            impl #impl_generics #krate::Private for #name #ty_generics #where_clause {}\n                #[automatically_derived]\n            impl #impl_generics #krate::FilterableValue for #name #ty_generics #where_clause {\n                type Column = #name #ty_generics;\n            }\n            #[automatically_derived]\n            impl #impl_generics #krate::Private for &#name #ty_generics #where_clause {}\n            #[automatically_derived]\n            impl #impl_generics #krate::FilterableValue for &#name #ty_generics #where_clause {\n                type Column = #name #ty_generics;\n            }\n        };\n\n        let mut generics = ty.generics.clone();\n        add_type_bounds(&mut generics, &quote!(#krate::DirectIndexKey));\n        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();\n        let dik_impl = quote! {\n            #[automatically_derived]\n            impl #impl_generics #krate::DirectIndexKey for #name #ty_generics #where_clause {}\n        };\n\n        Some(quote! {\n            #filterable_impl\n            #dik_impl\n        })\n    } else {\n        None\n    };\n\n    quote! {\n        #impl_plain_enum_extras\n\n        #[automatically_derived]\n        impl #impl_generics #krate::SpacetimeType for #name #ty_generics #where_clause {\n            fn make_type<S: #krate::sats::typespace::TypespaceBuilder>(__typespace: &mut S) -> #krate::sats::AlgebraicType {\n                #krate::sats::typespace::TypespaceBuilder::add(\n                    __typespace,\n                    core::any::TypeId::of::<#name #typeid_ty_generics>(),\n                    Some(#ty_name),\n                    |__typespace| #typ,\n                )\n            }\n        }\n    }\n}\n\nfn add_type_bounds(generics: &mut syn::Generics, trait_bound: &TokenStream) {\n    for param in &generics.params {\n        let syn::GenericParam::Type(param) = param else {\n            continue;\n        };\n        let param_name = &param.ident;\n        let where_clause = generics.where_clause.get_or_insert_with(|| syn::WhereClause {\n            where_token: Default::default(),\n            predicates: Default::default(),\n        });\n        where_clause\n            .predicates\n            .push(syn::parse_quote!(#param_name: #trait_bound));\n    }\n}\n\n/// Returns the list of types if syntactically we see that the `ty`\n/// is `#[repr(C)]` of only primitives.\n///\n/// We later assert semantically in generated code that the list of types\n/// actually are primitives.\n/// We'll also check that `ty` is paddingless.\nfn extract_repr_c_primitive<'a>(ty: &'a SatsType) -> Option<Vec<&'a syn::Ident>> {\n    // Ensure we have a `#[repr(C)]` struct.\n    if !ty.is_repr_c {\n        return None;\n    }\n    let SatsTypeData::Product(fields) = &ty.data else {\n        return None;\n    };\n\n    // Ensure every field is a primitive and collect the idents.\n    const PRIM_TY: &[sym::Symbol] = &[\n        sym::u8,\n        sym::i8,\n        sym::u16,\n        sym::i16,\n        sym::u32,\n        sym::i32,\n        sym::u64,\n        sym::i64,\n        sym::u128,\n        sym::i128,\n        sym::f32,\n        sym::f64,\n    ];\n    let mut field_tys = Vec::with_capacity(fields.len());\n    for field in fields {\n        if let syn::Type::Path(ty) = &field.ty {\n            let ident = ty.path.get_ident().filter(|ident| PRIM_TY.iter().any(|p| ident == p))?;\n            field_tys.push(ident);\n        } else {\n            return None;\n        }\n    }\n    Some(field_tys)\n}\n\npub(crate) fn derive_deserialize(ty: &SatsType<'_>) -> TokenStream {\n    let (name, tuple_name) = (&ty.ident, &ty.name);\n    let spacetimedb_lib = &ty.krate;\n    let (impl_generics, ty_generics, where_clause) = ty.generics.split_for_impl();\n\n    let de_lifetime = syn::Lifetime::new(\"'de\", Span::call_site());\n    let deserialize_t = quote!(#spacetimedb_lib::de::Deserialize<#de_lifetime>);\n\n    let mut de_generics = ty.generics.clone();\n\n    add_type_bounds(&mut de_generics, &deserialize_t);\n\n    for lp in de_generics.lifetimes_mut() {\n        lp.bounds.push(de_lifetime.clone());\n    }\n\n    let mut de_lt_param = syn::LifetimeParam::new(de_lifetime);\n    de_lt_param.bounds = de_generics\n        .lifetimes()\n        .map(|lp| Pair::Punctuated(lp.lifetime.clone(), Token![+](Span::call_site())))\n        .collect();\n\n    de_generics.params.insert(0, de_lt_param.into());\n    let (de_impl_generics, _, de_where_clause) = de_generics.split_for_impl();\n\n    let (iter_n, iter_n2, iter_n3, iter_n4, iter_n5, iter_n6, iter_n7) =\n        (0usize.., 0usize.., 0usize.., 0usize.., 0usize.., 0usize.., 0usize..);\n\n    match &ty.data {\n        SatsTypeData::Product(fields) => {\n            let mut fast_body = None;\n            if let Some(fields) = extract_repr_c_primitive(ty) {\n                fast_body = Some(quote! {\n                    #[inline(always)]\n                    fn deserialize_from_bsatn<R: #spacetimedb_lib::buffer::BufReader<'de>>(\n                        mut deserializer: #spacetimedb_lib::bsatn::Deserializer<'de, R>\n                    ) -> Result<Self, #spacetimedb_lib::bsatn::DecodeError> {\n                        const _: () = {\n                            #(#spacetimedb_lib::bsatn::assert_is_primitive_type::<#fields>();)*\n                        };\n                        // This guarantees that `Self` has no padding.\n                        if const { core::mem::size_of::<Self>() == #(core::mem::size_of::<#fields>())+* } {\n                            let bytes = deserializer.get_slice(core::mem::size_of::<Self>())?;\n                            let ptr = bytes as *const [u8] as *const u8 as *const Self;\n                            // SAFETY:\n                            // - `ptr` is valid for reads, `size_of::<T>()`.\n                            // - `ptr` is trivially properly aligned (alignment = 1).\n                            // - `ptr` points to a properly initialized `Foo`\n                            //   as we've guaranteed that there is no padding.\n                            Ok(unsafe { core::ptr::read(ptr) })\n                        } else {\n                            Self::deserialize(deserializer)\n                        }\n                    }\n                });\n            }\n\n            let n_fields = fields.len();\n\n            let field_names = fields.iter().map(|f| f.ident.unwrap()).collect::<Vec<_>>();\n            let field_strings = fields.iter().map(|f| f.name.as_deref().unwrap()).collect::<Vec<_>>();\n            let field_types = fields.iter().map(|f| f.ty);\n            let field_types2 = field_types.clone();\n            let field_types3 = field_types.clone();\n            let field_types4 = field_types.clone();\n            quote! {\n                #[allow(non_camel_case_types)]\n                #[allow(clippy::all)]\n                const _: () = {\n                    impl #de_impl_generics #spacetimedb_lib::de::Deserialize<'de> for #name #ty_generics #de_where_clause {\n                        #fast_body\n\n                        fn deserialize<D: #spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n                            deserializer.deserialize_product(__ProductVisitor {\n                                _marker: std::marker::PhantomData::<fn() -> #name #ty_generics>,\n                            })\n                        }\n\n                        fn validate<D: #spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> {\n                            deserializer.validate_product(__ProductVisitor {\n                                _marker: std::marker::PhantomData::<fn() -> #name #ty_generics>,\n                            })\n                        }\n                    }\n\n                    struct __ProductVisitor #impl_generics #where_clause {\n                        _marker: std::marker::PhantomData<fn() -> #name #ty_generics>,\n                    }\n\n                    impl #de_impl_generics #spacetimedb_lib::de::ProductVisitor<'de> for __ProductVisitor #ty_generics #de_where_clause {\n                        type Output = #name #ty_generics;\n\n                        fn product_name(&self) -> Option<&str> {\n                            Some(#tuple_name)\n                        }\n                        fn product_len(&self) -> usize {\n                            #n_fields\n                        }\n\n                        fn visit_seq_product<A: #spacetimedb_lib::de::SeqProductAccess<'de>>(self, mut tup: A) -> Result<Self::Output, A::Error> {\n                            Ok(#name {\n                                #(#field_names:\n                                    tup.next_element::<#field_types>()?\n                                        .ok_or_else(|| #spacetimedb_lib::de::Error::invalid_product_length(#iter_n, &self))?,)*\n                            })\n                        }\n                        fn validate_seq_product<A: #spacetimedb_lib::de::SeqProductAccess<'de>>(self, mut tup: A) -> Result<(), A::Error> {\n                            #(\n                                tup.validate_next_element::<#field_types2>()?\n                                    .ok_or_else(|| #spacetimedb_lib::de::Error::invalid_product_length(#iter_n2, &self))?;\n                            )*\n                            Ok(())\n                        }\n                        fn visit_named_product<A: #spacetimedb_lib::de::NamedProductAccess<'de>>(self, mut __prod: A) -> Result<Self::Output, A::Error> {\n                            #(let mut #field_names = None;)*\n                            while let Some(__field) = #spacetimedb_lib::de::NamedProductAccess::get_field_ident(&mut __prod, Self {\n                                _marker: std::marker::PhantomData,\n                            })? {\n                                match __field {\n                                    #(__ProductFieldIdent::#field_names => {\n                                        if #field_names.is_some() {\n                                            return Err(#spacetimedb_lib::de::Error::duplicate_field(#iter_n3, Some(#field_strings), &self))\n                                        }\n                                        #field_names = Some(#spacetimedb_lib::de::NamedProductAccess::get_field_value::<#field_types3>(&mut __prod)?)\n                                    })*\n                                }\n                            }\n                            Ok(#name {\n                                #(#field_names:\n                                    #field_names.ok_or_else(|| #spacetimedb_lib::de::Error::missing_field(#iter_n4, Some(#field_strings), &self))?,)*\n                            })\n                        }\n                        fn validate_named_product<A: #spacetimedb_lib::de::NamedProductAccess<'de>>(self, mut __prod: A) -> Result<(), A::Error> {\n                            #(let mut #field_names = false;)*\n                            while let Some(__field) = #spacetimedb_lib::de::NamedProductAccess::get_field_ident(&mut __prod, Self {\n                                _marker: std::marker::PhantomData,\n                            })? {\n                                match __field {\n                                    #(__ProductFieldIdent::#field_names => {\n                                        if #field_names {\n                                            return Err(#spacetimedb_lib::de::Error::duplicate_field(#iter_n5, Some(#field_strings), &self))\n                                        }\n                                        #spacetimedb_lib::de::NamedProductAccess::validate_field_value::<#field_types4>(&mut __prod)?;\n                                        #field_names = true;\n                                    })*\n                                }\n                            }\n                            #(\n                                if !#field_names {\n                                    return Err(#spacetimedb_lib::de::Error::missing_field(#iter_n6, Some(#field_strings), &self));\n                                }\n                            )*\n                            Ok(())\n                        }\n                    }\n\n                    impl #de_impl_generics #spacetimedb_lib::de::FieldNameVisitor<'de> for __ProductVisitor #ty_generics #de_where_clause {\n                        type Output = __ProductFieldIdent;\n\n                        fn field_names(&self) -> impl '_ + Iterator<Item = Option<&str>> {\n                            [#(#field_strings),*].into_iter().map(Some)\n                        }\n\n                        fn visit<__E: #spacetimedb_lib::de::Error>(self, name: &str) -> Result<Self::Output, __E> {\n                            match name {\n                                #(#field_strings => Ok(__ProductFieldIdent::#field_names),)*\n                                _ => Err(#spacetimedb_lib::de::Error::unknown_field_name(name, &self)),\n                            }\n                        }\n\n                        fn visit_seq(self, index: usize) -> Self::Output {\n                            match index {\n                                #(#iter_n7 => __ProductFieldIdent::#field_names,)*\n                                _ => core::unreachable!(),\n                            }\n                        }\n                    }\n\n                    #[allow(non_camel_case_types)]\n                    enum __ProductFieldIdent {\n                        #(#field_names,)*\n                    }\n                };\n            }\n        }\n        SatsTypeData::Sum(variants) => {\n            let variant_names = variants.iter().map(|var| &*var.name).collect::<Vec<_>>();\n            let variant_idents = variants.iter().map(|var| var.ident).collect::<Vec<_>>();\n            let tags = 0u8..;\n            let arms = variants.iter().map(|var| {\n                let ident = var.ident;\n                if let (Some(member), Some(ty)) = (&var.member, var.ty) {\n                    quote! {\n                        __Variant::#ident => Ok(#name::#ident { #member: #spacetimedb_lib::de::VariantAccess::deserialize::<#ty>(__access)? }),\n                    }\n                } else {\n                    quote! {\n                        __Variant::#ident => {\n                            let () = #spacetimedb_lib::de::VariantAccess::deserialize(__access)?;\n                            Ok(#name::#ident)\n                        }\n                    }\n                }\n            });\n            let arms_validate = variants.iter().map(|var| {\n                let ident = var.ident;\n                if let Some(ty) = var.ty {\n                    quote! {\n                        __Variant::#ident => #spacetimedb_lib::de::VariantAccess::validate::<#ty>(__access)?,\n                    }\n                } else {\n                    quote! {\n                        __Variant::#ident => #spacetimedb_lib::de::VariantAccess::validate::<()>(__access)?,\n                    }\n                }\n            });\n            quote! {\n                #[allow(clippy::all)]\n                const _: () = {\n                    impl #de_impl_generics #spacetimedb_lib::de::Deserialize<'de> for #name #ty_generics #de_where_clause {\n                        fn deserialize<D: #spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n                            deserializer.deserialize_sum(__SumVisitor {\n                                _marker: std::marker::PhantomData::<fn() -> #name #ty_generics>,\n                            })\n                        }\n\n                        fn validate<D: #spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> {\n                            deserializer.validate_sum(__SumVisitor {\n                                _marker: std::marker::PhantomData::<fn() -> #name #ty_generics>,\n                            })\n                        }\n                    }\n\n                    struct __SumVisitor #impl_generics #where_clause {\n                        _marker: std::marker::PhantomData<fn() -> #name #ty_generics>,\n                    }\n\n                    impl #de_impl_generics #spacetimedb_lib::de::SumVisitor<'de> for __SumVisitor #ty_generics #de_where_clause {\n                        type Output = #name #ty_generics;\n\n                        fn sum_name(&self) -> Option<&str> {\n                            Some(#tuple_name)\n                        }\n\n                        fn visit_sum<A: #spacetimedb_lib::de::SumAccess<'de>>(self, __data: A) -> Result<Self::Output, A::Error> {\n                            let (__variant, __access) = __data.variant(self)?;\n                            match __variant {\n                                #(#arms)*\n                            }\n                        }\n\n                        fn validate_sum<A: #spacetimedb_lib::de::SumAccess<'de>>(self, __data: A) -> Result<(), A::Error> {\n                            let (__variant, __access) = __data.variant(self)?;\n                            match __variant {\n                                #(#arms_validate)*\n                            }\n                            Ok(())\n                        }\n                    }\n\n                    #[allow(non_camel_case_types)]\n                    enum __Variant {\n                        #(#variant_idents,)*\n                    }\n\n                    impl #de_impl_generics #spacetimedb_lib::de::VariantVisitor<'de> for __SumVisitor #ty_generics #de_where_clause {\n                        type Output = __Variant;\n\n                        fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n                            [#(#variant_names,)*].into_iter()\n                        }\n\n                        fn visit_tag<E: #spacetimedb_lib::de::Error>(self, __tag: u8) -> Result<Self::Output, E> {\n                            match __tag {\n                                #(#tags => Ok(__Variant::#variant_idents),)*\n                                _ => Err(#spacetimedb_lib::de::Error::unknown_variant_tag(__tag, &self)),\n                            }\n                        }\n                        fn visit_name<E: #spacetimedb_lib::de::Error>(self, __name: &str) -> Result<Self::Output, E> {\n                            match __name {\n                                #(#variant_names => Ok(__Variant::#variant_idents),)*\n                                _ => Err(#spacetimedb_lib::de::Error::unknown_variant_name(__name, &self)),\n                            }\n                        }\n                    }\n                };\n            }\n        }\n    }\n}\n\npub(crate) fn derive_serialize(ty: &SatsType) -> TokenStream {\n    let spacetimedb_lib = &ty.krate;\n    let name = &ty.ident;\n\n    let mut generics = ty.generics.clone();\n\n    let serialize_t = quote!(#spacetimedb_lib::ser::Serialize);\n    add_type_bounds(&mut generics, &serialize_t);\n\n    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();\n\n    let mut fast_body = None;\n    let body = match &ty.data {\n        SatsTypeData::Product(fields) => {\n            if let Some(fields) = extract_repr_c_primitive(ty) {\n                fast_body = Some(quote! {\n                    #[inline(always)]\n                    fn serialize_into_bsatn<W: #spacetimedb_lib::buffer::BufWriter>(\n                            &self,\n                            serializer: #spacetimedb_lib::bsatn::Serializer<'_, W>\n                    ) -> Result<(), #spacetimedb_lib::bsatn::EncodeError> {\n                        const _: () = {\n                            #(#spacetimedb_lib::bsatn::assert_is_primitive_type::<#fields>();)*\n                        };\n                        // This guarantees that `Self` has no padding.\n                        if const { core::mem::size_of::<Self>() == #(core::mem::size_of::<#fields>())+* } {\n                            // SAFETY:\n                            // - We know `self` is non-null as it's a shared reference\n                            //   and we know it's valid for reads for `core::mem::size_of::<Self>()` bytes.\n                            //   Alignment of `u8` is 1, so it's trivially satisfied.\n                            //   - The slice is all within `self`, so in the same allocated object.\n                            // - `self` does point to `core::mem::size_of::<Self>()` consecutive `u8`s,\n                            //    as per `assert_is_primitive_type` above,\n                            //    we know none of the fields of `Self` have any padding.\n                            // - We're not going to mutate the memory within `bytes`.\n                            // - We know `core::mem::size_of::<Self>() < isize::MAX`.\n                            let bytes = unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, core::mem::size_of::<Self>()) };\n                            serializer.raw_write_bytes(bytes);\n                            Ok(())\n                        } else {\n                            self.serialize(serializer)\n                        }\n                    }\n                });\n            }\n\n            let fieldnames = fields.iter().map(|field| field.ident.unwrap());\n            let tys = fields.iter().map(|f| &f.ty);\n            let fieldnamestrings = fields.iter().map(|field| field.name.as_ref().unwrap());\n            let nfields = fields.len();\n            quote! {\n                let mut __prod = __serializer.serialize_named_product(#nfields)?;\n                #(#spacetimedb_lib::ser::SerializeNamedProduct::serialize_element::<#tys>(&mut __prod, Some(#fieldnamestrings), &self.#fieldnames)?;)*\n                #spacetimedb_lib::ser::SerializeNamedProduct::end(__prod)\n            }\n        }\n        SatsTypeData::Sum(variants) => {\n            let arms = variants.iter().enumerate().map(|(i, var)| {\n                let (name,name_str) = (var.ident, &var.name);\n                let tag = i as u8;\n                if let (Some(member), Some(ty)) = (&var.member, var.ty) {\n                    quote_spanned! {ty.span()=>\n                        Self::#name { #member: __variant } => __serializer.serialize_variant::<#ty>(#tag, Some(#name_str), __variant),\n                    }\n                } else {\n                    quote! {\n                        Self::#name => __serializer.serialize_variant(#tag, Some(#name_str), &()),\n                    }\n                }\n            });\n            quote!(match self {\n                #(#arms)*\n                _ => unreachable!(),\n            })\n        }\n    };\n    quote! {\n        impl #impl_generics #spacetimedb_lib::ser::Serialize for #name #ty_generics #where_clause {\n            #fast_body\n            fn serialize<S: #spacetimedb_lib::ser::Serializer>(&self, __serializer: S) -> Result<S::Ok, S::Error> {\n                #body\n            }\n        }\n    }\n}\n\nfn variant_data(variant: &syn::Variant) -> syn::Result<Option<(syn::Member, &syn::Type)>> {\n    let field = match &variant.fields {\n        syn::Fields::Named(f) if f.named.len() == 1 => &f.named[0],\n        syn::Fields::Named(_) => {\n            return Err(syn::Error::new_spanned(\n                &variant.fields,\n                \"must be a unit variant or a newtype variant\",\n            ))\n        }\n        syn::Fields::Unnamed(f) if f.unnamed.len() != 1 => {\n            return Err(syn::Error::new_spanned(\n                &variant.fields,\n                \"must be a unit variant or a newtype variant\",\n            ))\n        }\n        syn::Fields::Unnamed(f) => &f.unnamed[0],\n        syn::Fields::Unit => return Ok(None),\n    };\n    let member = field\n        .ident\n        .clone()\n        .map(Into::into)\n        .unwrap_or_else(|| syn::Member::from(0));\n    Ok(Some((member, &field.ty)))\n}\n"
  },
  {
    "path": "crates/bindings-macro/src/table.rs",
    "content": "use crate::sats;\nuse crate::sym;\nuse crate::util::{check_duplicate, check_duplicate_msg, match_meta};\nuse core::slice;\nuse heck::ToSnakeCase;\nuse proc_macro2::{Span, TokenStream};\nuse quote::{format_ident, quote, quote_spanned, ToTokens};\nuse std::borrow::Cow;\nuse syn::ext::IdentExt;\nuse syn::meta::ParseNestedMeta;\nuse syn::parse::Parse;\nuse syn::parse::Parser as _;\nuse syn::punctuated::Punctuated;\nuse syn::spanned::Spanned;\nuse syn::LitStr;\nuse syn::{parse_quote, Ident, Path, Token};\n\npub(crate) struct TableArgs {\n    access: Option<TableAccess>,\n    name: Option<LitStr>,\n    scheduled: Option<ScheduledArg>,\n    accessor: Ident,\n    indices: Vec<IndexArg>,\n    event: Option<Span>,\n}\n\nenum TableAccess {\n    Public(Span),\n    Private(Span),\n}\n\nimpl TableAccess {\n    fn to_value(&self) -> TokenStream {\n        let (TableAccess::Public(span) | TableAccess::Private(span)) = *self;\n        let name = match self {\n            TableAccess::Public(_) => \"Public\",\n            TableAccess::Private(_) => \"Private\",\n        };\n        let ident = Ident::new(name, span);\n        quote_spanned!(span => spacetimedb::table::TableAccess::#ident)\n    }\n}\n\nstruct ScheduledArg {\n    span: Span,\n    reducer_or_procedure: Path,\n    at: Option<Ident>,\n}\n\nstruct IndexArg {\n    accessor: Ident,\n    canonical_name: Option<LitStr>,\n    is_unique: bool,\n    kind: IndexType,\n}\n\nimpl IndexArg {\n    fn new(accessor: Ident, kind: IndexType, canonical_name: Option<LitStr>) -> Self {\n        // We don't know if its unique yet.\n        // We'll discover this once we have collected constraints.\n        let is_unique = false;\n        Self {\n            canonical_name,\n            accessor,\n            is_unique,\n            kind,\n        }\n    }\n}\n\nenum IndexType {\n    BTree { columns: Vec<Ident> },\n    Hash { columns: Vec<Ident> },\n    Direct { column: Ident },\n}\n\nimpl TableArgs {\n    pub(crate) fn parse(input: TokenStream, struct_ident: &Ident) -> syn::Result<Self> {\n        let mut access = None;\n        let mut scheduled = None;\n        let mut accessor = None;\n        let mut name: Option<LitStr> = None;\n        let mut indices = Vec::new();\n        let mut event = None;\n        syn::meta::parser(|meta| {\n            match_meta!(match meta {\n                sym::public => {\n                    check_duplicate_msg(&access, &meta, \"already specified access level\")?;\n                    access = Some(TableAccess::Public(meta.path.span()));\n                }\n                sym::private => {\n                    check_duplicate_msg(&access, &meta, \"already specified access level\")?;\n                    access = Some(TableAccess::Private(meta.path.span()));\n                }\n                sym::accessor => {\n                    check_duplicate(&accessor, &meta)?;\n                    let value = meta.value()?;\n                    accessor = Some(value.parse()?);\n                }\n                sym::name => {\n                    check_duplicate(&name, &meta)?;\n                    let value = meta.value()?;\n                    // `fork` as a way to do lookahead `peek`, so that below when we parse as the `LitStr` we actually want,\n                    // it works.\n                    if let Ok(sym) = value.fork().parse::<Ident>() {\n                        // The update from SpacetimeDB 1.* to 2.* changes `name =` to `accessor =`,\n                        // and uses `name =` for a different thing. Now, only `accessor =` is mandatory,\n                        // and `name =` accepts a string literal rather than an identifier.\n                        // Detect the specific case where the user specifies a 1.*-style `name = ident`,\n                        // and offer a diagnostic with a simple migration path.\n                        // Unfortunately, we can't hook in to rustc's system for providing quick fixes in compiler errors,\n                        // until [this ancient issue](https://github.com/rust-lang/rust/issues/54140) gets stabilized.\n                        return Err(\n                            if accessor.is_some() {\n                                // If we've already encountered an `accessor`,\n                                // then probably the user is actually trying to overwrite the `name`,\n                                // so tell them to use a string literal instead of an ident.\n                                // This is a best-effort check, and we won't hit it if the user specifies `name` first,\n                                // but we're prioritizing the migration UX here.\n                                meta.error(format_args!(\n                                    \"Expected a string literal for `name`, but found an identifier.\n\nTo overwrite the canonical name of the table, replace `name = {sym}` with `name = \\\"{sym}\\\"`.\"\n                                ))\n                            } else {\n                                // FIXME: Ideally, this error span would point to the full pair `name = my_table`,\n                                // but I (pgoldman 2026-02-18) can only figure out how to get it at either `name` or `my_table`.\n                                // This version points at `name`, which, :shrug:.\n                                // Note that, if the user specifies `name = {ident}` followed by `accessor = {ident}`,\n                                // we'll hit this branch, even though the diagnostic doesn't apply and we'd prefer not to.\n                                // I (pgoldman 2026-02-18) don't see a good way to distinguish this case\n                                // without making our parsing dramatically more complicated,\n                                // and it seems unlikely to occur.\n                                meta.error(format_args!(\n                                    \"Expected a string literal for `name`, but found an identifier. Did you mean to specify an `accessor`?\n\nIf you're migrating from SpacetimeDB 1.*, replace `name = {sym}` with `accessor = {sym}`.\"\n                                ))\n                            })\n                    }\n                    name = Some(value.parse()?);\n                }\n                sym::index => indices.push(IndexArg::parse_meta(meta)?),\n                sym::scheduled => {\n                    check_duplicate(&scheduled, &meta)?;\n                    scheduled = Some(ScheduledArg::parse_meta(meta)?);\n                }\n                sym::event => {\n                    check_duplicate(&event, &meta)?;\n                    event = Some(meta.path.span());\n                }\n            });\n            Ok(())\n        })\n        .parse2(input)?;\n        let accessor = accessor.ok_or_else(|| {\n            if let Some(name_str) = &name {\n                // If a user's gotten partway through migrating from 1.* to 2.* in a misguided way,\n                // they may end up with a `table` invocation that specifies `name = \"my_table_name\"` and no `accessor`.\n                // In this case, they probably intended to change `name =` to `accessor =`,\n                // but were misled into keeping `name =` and changing the name from an ident into a lit string.\n                // Detect that and offer a diagnostic with a simple fix.\n                // Unfortunately, we can't hook in to rustc's system for providing quick fixes in compiler errors,\n                // until [this ancient issue](https://github.com/rust-lang/rust/issues/54140) gets stabilized.\n                let name_str_value = name_str.value();\n                syn::Error::new_spanned(\n                    name_str,\n                    format_args!(\n                        \"Expected an `accessor` in table definition, but got only a `name`.\nDid you mean to specify `accessor` instead?\n`accessor` is required, but `name` is optional.\n\nIf you're migrating from SpacetimeDB 1.*, replace `name = {name_str_value:?}` with `accessor = {name_str_value}`\",\n                    ),\n                )\n            } else {\n                let table = struct_ident.to_string().to_snake_case();\n                syn::Error::new(\n                    Span::call_site(),\n                    format_args!(\"must specify table accessor, e.g. `#[spacetimedb::table(accessor = {table})]\"),\n                )\n            }\n        })?;\n        Ok(TableArgs {\n            access,\n            scheduled,\n            accessor,\n            indices,\n            name,\n            event,\n        })\n    }\n}\n\nimpl ScheduledArg {\n    fn parse_meta(meta: ParseNestedMeta) -> syn::Result<Self> {\n        let span = meta.path.span();\n        let mut reducer_or_procedure = None;\n        let mut at = None;\n\n        meta.parse_nested_meta(|meta| {\n            if meta.input.peek(syn::Token![=]) || meta.input.peek(syn::token::Paren) {\n                match_meta!(match meta {\n                    sym::at => {\n                        check_duplicate(&at, &meta)?;\n                        let ident = meta.value()?.parse()?;\n                        at = Some(ident);\n                    }\n                })\n            } else {\n                check_duplicate_msg(\n                    &reducer_or_procedure,\n                    &meta,\n                    \"can only specify one scheduled reducer or procedure\",\n                )?;\n                reducer_or_procedure = Some(meta.path);\n            }\n            Ok(())\n        })?;\n\n        let reducer_or_procedure = reducer_or_procedure.ok_or_else(|| {\n            meta.error(\n                \"must specify scheduled reducer or procedure associated with the table: scheduled(function_name)\",\n            )\n        })?;\n        Ok(Self {\n            span,\n            reducer_or_procedure,\n            at,\n        })\n    }\n}\n\nimpl IndexArg {\n    fn parse_meta(meta: ParseNestedMeta) -> syn::Result<Self> {\n        let mut accessor = None;\n        let mut canonical_name = None;\n        let mut algo = None;\n\n        meta.parse_nested_meta(|meta| {\n            match_meta!(match meta {\n                sym::accessor => {\n                    check_duplicate(&accessor, &meta)?;\n                    accessor = Some(meta.value()?.parse()?);\n                }\n                sym::name => {\n                    check_duplicate(&canonical_name, &meta)?;\n                    canonical_name = Some(meta.value()?.parse()?);\n                }\n\n                sym::btree => {\n                    check_duplicate_msg(&algo, &meta, \"index algorithm specified twice\")?;\n                    algo = Some(Self::parse_btree(meta)?);\n                }\n                sym::hash => {\n                    check_duplicate_msg(&algo, &meta, \"index algorithm specified twice\")?;\n                    algo = Some(Self::parse_hash(meta)?);\n                }\n                sym::direct => {\n                    check_duplicate_msg(&algo, &meta, \"index algorithm specified twice\")?;\n                    algo = Some(Self::parse_direct(meta)?);\n                }\n                sym::name => {\n                    // If the user is trying to specify a `name`, do a bit of guessing at what their goal is.\n                    // This is going to be best-effort, and we're not going to try to do lookahead or anything.\n\n                    return Err(if accessor.is_some() {\n                        // If the user's already specified an `accessor`,\n                        // then probably they're trying to specify the canonical name,\n                        // like you can for tables.\n                        // Print an error that says this is unsupported.\n                        meta.error(\n                            \"Unexpected argument `name` in index definition.\n\nOverwriting the `name` of an index is currently unsupported.\",\n                        )\n                    } else if let Ok(sym) = meta.value().and_then(|val| val.parse::<Ident>()) {\n                        // If we haven't seen an `accessor` yet, and the value is an ident,\n                        // then probably this is 1.* syntax that needs a migration.\n                        // Note that, if the user specifies `name = {ident}` followed by `accessor = {ident}`,\n                        // we'll hit this branch, even though the diagnostic doesn't apply and we'd prefer not to.\n                        // I (pgoldman 2026-02-18) don't see a good way to distinguish this case\n                        // without making our parsing dramatically more complicated,\n                        // and it seems unlikely to occur.\n                        meta.error(format_args!(\n                            \"Expected an `accessor` in index definition, but got a `name` instead.\n\nIf you're migrating from SpacetimeDB 1.*, replace `name = {sym}` with `accessor = {sym}`.\"\n                        ))\n                    } else {\n                        // If we haven't seen an `accessor` yet, but the value is not an ident,\n                        // then we're not really sure what's going wrong, so print a more generic error message.\n                        meta.error(format_args!(\n                            \"Unexpected argument `name` in index definition.\n\nOverwriting the `name` of an index is currently unsupported.\nDid you mean to specify an `accessor` instead? Do so with `accessor = my_index`, where `my_index` is an unquoted identifier.\"\n                        ))\n                    });\n                }\n            });\n            Ok(())\n        })?;\n        let accessor = accessor.ok_or_else(|| meta.error(\"missing index accessor, e.g. `accessor = my_index`\"))?;\n        let kind = algo.ok_or_else(|| {\n            meta.error(\n                \"missing index algorithm, e.g., `btree(columns = [col1, col2])`, \\\n                `hash(columns = [col1, col2])`, or `direct(column = col1)`\",\n            )\n        })?;\n\n        Ok(IndexArg::new(accessor, kind, canonical_name))\n    }\n\n    fn parse_columns(meta: &ParseNestedMeta) -> syn::Result<Option<Vec<Ident>>> {\n        let mut columns = None;\n        meta.parse_nested_meta(|meta| {\n            match_meta!(match meta {\n                sym::columns => {\n                    check_duplicate(&columns, &meta)?;\n                    let value = meta.value()?;\n                    let inner;\n                    syn::bracketed!(inner in value);\n                    columns = Some(\n                        Punctuated::<Ident, Token![,]>::parse_terminated(&inner)?\n                            .into_iter()\n                            .collect::<Vec<_>>(),\n                    );\n                }\n            });\n            Ok(())\n        })?;\n        Ok(columns)\n    }\n\n    fn parse_btree(meta: ParseNestedMeta) -> syn::Result<IndexType> {\n        let columns = Self::parse_columns(&meta)?;\n        let columns = columns\n            .ok_or_else(|| meta.error(\"must specify columns for btree index, e.g. `btree(columns = [col1, col2])`\"))?;\n        Ok(IndexType::BTree { columns })\n    }\n\n    fn parse_hash(meta: ParseNestedMeta) -> syn::Result<IndexType> {\n        let columns = Self::parse_columns(&meta)?;\n        let columns = columns\n            .ok_or_else(|| meta.error(\"must specify columns for hash index, e.g. `hash(columns = [col1, col2])`\"))?;\n        Ok(IndexType::Hash { columns })\n    }\n\n    fn parse_direct(meta: ParseNestedMeta) -> syn::Result<IndexType> {\n        let mut column = None;\n        meta.parse_nested_meta(|meta| {\n            match_meta!(match meta {\n                sym::column => {\n                    check_duplicate(&column, &meta)?;\n                    let value = meta.value()?;\n                    let inner;\n                    syn::bracketed!(inner in value);\n                    column = Some(Ident::parse(&inner)?);\n                }\n            });\n            Ok(())\n        })?;\n        let column = column\n            .ok_or_else(|| meta.error(\"must specify the column for direct index, e.g. `direct(column = col1)`\"))?;\n        Ok(IndexType::Direct { column })\n    }\n\n    /// Parses an inline `#[index(btree)]`, `#[index(hash)]`, or `#[index(direct)]` attribute on a field.\n    fn parse_index_attr(field: &Ident, attr: &syn::Attribute) -> syn::Result<Self> {\n        let mut kind = None;\n        attr.parse_nested_meta(|meta| {\n            match_meta!(match meta {\n                sym::btree => {\n                    check_duplicate_msg(&kind, &meta, \"index type specified twice\")?;\n                    kind = Some(IndexType::BTree {\n                        columns: vec![field.clone()],\n                    });\n                }\n                sym::hash => {\n                    check_duplicate_msg(&kind, &meta, \"index type specified twice\")?;\n                    kind = Some(IndexType::Hash {\n                        columns: vec![field.clone()],\n                    });\n                }\n                sym::direct => {\n                    check_duplicate_msg(&kind, &meta, \"index type specified twice\")?;\n                    kind = Some(IndexType::Direct { column: field.clone() })\n                }\n            });\n            Ok(())\n        })?;\n        let kind =\n            kind.ok_or_else(|| syn::Error::new_spanned(&attr.meta, \"must specify kind of index (`btree` , `direct`)\"))?;\n\n        // Default accessor = field name if not provided\n        let accessor = field.clone();\n        Ok(IndexArg::new(accessor, kind, None))\n    }\n\n    fn validate<'a>(&'a self, table_name: &str, cols: &'a [Column<'a>]) -> syn::Result<ValidatedIndex<'a>> {\n        let find_column = |ident| find_column(cols, ident);\n        let (kind, kind_str) = match &self.kind {\n            IndexType::BTree { columns } => {\n                let cols = columns.iter().map(find_column).collect::<syn::Result<Vec<_>>>()?;\n                (ValidatedIndexType::BTree { cols }, \"btree\")\n            }\n            IndexType::Hash { columns } => {\n                let cols = columns.iter().map(find_column).collect::<syn::Result<Vec<_>>>()?;\n                (ValidatedIndexType::Hash { cols }, \"hash\")\n            }\n            IndexType::Direct { column } => {\n                let col = find_column(column)?;\n\n                if !self.is_unique {\n                    return Err(syn::Error::new(\n                        column.span(),\n                        \"a direct index must be paired with a `#[unique] constraint\",\n                    ));\n                }\n\n                (ValidatedIndexType::Direct { col }, \"direct\")\n            }\n        };\n        let gen_index_name = || {\n            // See crates/schema/src/validate/v9.rs for the format of index names.\n            // It's slightly unnerving that we just trust that component to generate this format correctly,\n            // but what can you do.\n            let cols = kind.columns();\n            let cols = cols.iter().map(|col| col.ident.to_string()).collect::<Vec<_>>();\n            let cols = cols.join(\"_\");\n            format!(\"{table_name}_{cols}_idx_{kind_str}\")\n        };\n\n        Ok(ValidatedIndex {\n            is_unique: self.is_unique,\n            // This must be the canonical name (name used internally in database),\n            // as it is used in `index_id_from_name` abi.\n            index_name: gen_index_name(),\n            accessor_name: &self.accessor,\n            kind,\n            canonical_name: self.canonical_name.as_ref().map(|s| s.value()),\n        })\n    }\n}\n\nenum AccessorType {\n    Read,\n    ReadWrite,\n}\n\nimpl AccessorType {\n    fn unique(&self) -> proc_macro2::TokenStream {\n        match self {\n            AccessorType::Read => quote!(spacetimedb::UniqueColumnReadOnly),\n            AccessorType::ReadWrite => quote!(spacetimedb::UniqueColumn),\n        }\n    }\n\n    fn range(&self) -> proc_macro2::TokenStream {\n        match self {\n            AccessorType::Read => quote!(spacetimedb::RangedIndexReadOnly),\n            AccessorType::ReadWrite => quote!(spacetimedb::RangedIndex),\n        }\n    }\n\n    fn point(&self) -> proc_macro2::TokenStream {\n        match self {\n            AccessorType::Read => quote!(spacetimedb::PointIndexReadOnly),\n            AccessorType::ReadWrite => quote!(spacetimedb::PointIndex),\n        }\n    }\n\n    fn unique_doc_typename(&self) -> &'static str {\n        match self {\n            AccessorType::Read => \"UniqueColumnReadOnly\",\n            AccessorType::ReadWrite => \"UniqueColumn\",\n        }\n    }\n\n    fn range_doc_typename(&self) -> &'static str {\n        match self {\n            AccessorType::Read => \"RangedIndexReadOnly\",\n            AccessorType::ReadWrite => \"RangedIndex\",\n        }\n    }\n\n    fn point_doc_typename(&self) -> &'static str {\n        match self {\n            AccessorType::Read => \"PointIndexReadOnly\",\n            AccessorType::ReadWrite => \"PointIndex\",\n        }\n    }\n}\n\nstruct ValidatedIndex<'a> {\n    index_name: String,\n    accessor_name: &'a Ident,\n    is_unique: bool,\n    kind: ValidatedIndexType<'a>,\n    canonical_name: Option<String>,\n}\n\nenum ValidatedIndexType<'a> {\n    BTree { cols: Vec<&'a Column<'a>> },\n    Hash { cols: Vec<&'a Column<'a>> },\n    Direct { col: &'a Column<'a> },\n}\n\nimpl ValidatedIndexType<'_> {\n    fn columns(&self) -> &[&Column<'_>] {\n        match self {\n            Self::BTree { cols } | Self::Hash { cols } => cols,\n            Self::Direct { col } => slice::from_ref(col),\n        }\n    }\n\n    fn one_col(&self) -> Option<&Column<'_>> {\n        match self.columns() {\n            [col] => Some(col),\n            _ => None,\n        }\n    }\n}\n\nimpl ValidatedIndex<'_> {\n    fn desc(&self) -> TokenStream {\n        let algo = match &self.kind {\n            ValidatedIndexType::BTree { cols } => {\n                let col_ids = cols.iter().map(|col| col.index);\n                quote!(spacetimedb::table::IndexAlgo::BTree {\n                    columns: &[#(#col_ids),*]\n                })\n            }\n            ValidatedIndexType::Hash { cols } => {\n                let col_ids = cols.iter().map(|col| col.index);\n                quote!(spacetimedb::table::IndexAlgo::Hash {\n                    columns: &[#(#col_ids),*]\n                })\n            }\n            ValidatedIndexType::Direct { col } => {\n                let col_id = col.index;\n                quote!(spacetimedb::table::IndexAlgo::Direct {\n                    column: #col_id\n                })\n            }\n        };\n        let source_name = self.index_name.clone();\n        let accessor_name = self.accessor_name.to_string();\n        // Note: we do not pass the index_name through here.\n        // We trust the schema validation logic to reconstruct the name we've stored in `self.name`.\n        //TODO(shub): pass generated index name instead of accessor name as source_name\n        quote!(spacetimedb::table::IndexDesc {\n            source_name: #source_name,\n            accessor_name: #accessor_name,\n            algo: #algo,\n        })\n    }\n\n    fn accessor(\n        &self,\n        vis: &syn::Visibility,\n        row_type_ident: &Ident,\n        tbl_type_ident: &Ident,\n        flavor: AccessorType,\n    ) -> TokenStream {\n        if self.is_unique {\n            self.unique_accessor(row_type_ident, tbl_type_ident, flavor)\n        } else {\n            self.non_unique_accessor(vis, row_type_ident, tbl_type_ident, flavor)\n        }\n    }\n\n    fn unique_accessor(&self, row_type_ident: &Ident, tbl_type_ident: &Ident, flavor: AccessorType) -> TokenStream {\n        let col = self.kind.one_col().unwrap();\n        let index_ident = self.accessor_name;\n        let vis = col.vis;\n        let col_ty = col.ty;\n        let column_ident = col.ident;\n\n        let unique_ty = flavor.unique();\n        let tbl_token = quote!(#tbl_type_ident);\n        let doc_type = flavor.unique_doc_typename();\n\n        let doc = format!(\n            \"Gets the [`{doc_type}`][spacetimedb::{doc_type}] for the \\\n             [`{column_ident}`][{row_type_ident}::{column_ident}] column.\"\n        );\n        quote! {\n            #[doc = #doc]\n            #vis fn #column_ident(&self) -> #unique_ty<#tbl_token, #col_ty, __indices::#index_ident> {\n                #unique_ty::__NEW\n            }\n        }\n    }\n\n    fn non_unique_accessor(\n        &self,\n        vis: &syn::Visibility,\n        row_type_ident: &Ident,\n        tbl_type_ident: &Ident,\n        flavor: AccessorType,\n    ) -> TokenStream {\n        let index_ident = self.accessor_name;\n        let cols = self.kind.columns();\n        let col_tys = cols.iter().map(|c| c.ty);\n\n        let (handle_ty, doc_type, kind_doc) = match &self.kind {\n            ValidatedIndexType::BTree { .. } => (flavor.range(), flavor.range_doc_typename(), \"B-tree\"),\n            // Should be unreachable, but we might as well include this.\n            ValidatedIndexType::Direct { .. } => (flavor.range(), flavor.range_doc_typename(), \"Direct\"),\n            ValidatedIndexType::Hash { .. } => (flavor.point(), flavor.point_doc_typename(), \"Hash\"),\n        };\n        let mut doc = format!(\n            \"Gets the `{index_ident}` [`{doc_type}`][spacetimedb::{doc_type}] as defined \\\n                    on this table.\\n\\nThis {kind_doc} index is defined on the following columns, in order:\\n\"\n        );\n        for col in cols {\n            use std::fmt::Write;\n            writeln!(\n                doc,\n                \"- [`{ident}`][{row_type_ident}#structfield.{ident}]: [`{ty}`]\",\n                ident = col.ident,\n                ty = col.ty.to_token_stream()\n            )\n            .unwrap();\n        }\n\n        let tbl_token = quote!(#tbl_type_ident);\n        quote! {\n            #[doc = #doc]\n            #vis fn #index_ident(&self) -> #handle_ty<#tbl_token, (#(#col_tys,)*), __indices::#index_ident> {\n                #handle_ty::__NEW\n            }\n        }\n    }\n\n    fn marker_type(\n        &self,\n        vis: &syn::Visibility,\n        tablehandle_ident: &Ident,\n        primary_key_column: Option<&Column<'_>>,\n    ) -> TokenStream {\n        let index_ident = self.accessor_name;\n        let index_name = &self.index_name;\n\n        let (typeck_direct_index, is_ranged) = match &self.kind {\n            ValidatedIndexType::BTree { .. } => (None, true),\n            ValidatedIndexType::Direct { col } => {\n                let col_ty = col.ty;\n                let typeck = quote_spanned!(col_ty.span()=>\n                    const _: () = {\n                        spacetimedb::spacetimedb_lib::assert_column_type_valid_for_direct_index::<#col_ty>();\n                    };\n                );\n                (Some(typeck), true)\n            }\n            ValidatedIndexType::Hash { .. } => (None, false),\n        };\n        let vis = if self.is_unique {\n            self.kind.one_col().unwrap().vis\n        } else {\n            vis\n        };\n        let vis = superize_vis(vis);\n\n        let cols = self.kind.columns();\n        let num_cols = cols.len();\n        let index_kind_trait = if is_ranged {\n            quote!(IndexIsRanged)\n        } else {\n            quote!(IndexIsPointed)\n        };\n        let mut decl = quote! {\n            #typeck_direct_index\n\n            #vis struct #index_ident;\n            impl spacetimedb::table::#index_kind_trait for #index_ident {}\n            impl spacetimedb::table::Index for #index_ident {\n                const NUM_COLS_INDEXED: usize = #num_cols;\n                fn index_id() -> spacetimedb::table::IndexId {\n                    static INDEX_ID: std::sync::OnceLock<spacetimedb::table::IndexId> = std::sync::OnceLock::new();\n                    *INDEX_ID.get_or_init(|| {\n                        spacetimedb::sys::index_id_from_name(#index_name).unwrap()\n                    })\n                }\n            }\n        };\n        if self.is_unique {\n            let col = self.kind.one_col().unwrap();\n            let col_ty = col.ty;\n            let col_name = col.ident.to_string();\n            let field_ident = col.ident;\n            decl.extend(quote! {\n                impl spacetimedb::table::Column for #index_ident {\n                    type Table = #tablehandle_ident;\n                    type ColType = #col_ty;\n                    const COLUMN_NAME: &'static str = #col_name;\n                    fn get_field(row: &<Self::Table as spacetimedb::Table>::Row) -> &Self::ColType {\n                        &row.#field_ident\n                    }\n                }\n            });\n            if primary_key_column.is_some_and(|pk| col.ident == pk.ident) {\n                decl.extend(quote!(impl spacetimedb::table::PrimaryKey for #index_ident {}));\n            }\n        }\n        decl\n    }\n}\n\n/// Transform a visibility marker to one with the same effective visibility, but\n/// for use in a child module of the module of the original marker.\nfn superize_vis(vis: &syn::Visibility) -> Cow<'_, syn::Visibility> {\n    match vis {\n        syn::Visibility::Public(_) => Cow::Borrowed(vis),\n        syn::Visibility::Restricted(vis_r) => {\n            let first = &vis_r.path.segments[0];\n            if first.ident == \"crate\" || vis_r.path.leading_colon.is_some() {\n                Cow::Borrowed(vis)\n            } else {\n                let mut vis_r = vis_r.clone();\n                if first.ident == \"super\" {\n                    vis_r.path.segments.insert(0, first.clone())\n                } else if first.ident == \"self\" {\n                    vis_r.path.segments[0].ident = Ident::new(\"super\", Span::call_site())\n                }\n                Cow::Owned(syn::Visibility::Restricted(vis_r))\n            }\n        }\n        syn::Visibility::Inherited => Cow::Owned(parse_quote!(pub(super))),\n    }\n}\n\n#[derive(Clone)]\nstruct Column<'a> {\n    index: u16,\n    vis: &'a syn::Visibility,\n    ident: &'a syn::Ident,\n    ty: &'a syn::Type,\n    default_value: Option<syn::Expr>,\n}\n\nfn try_find_column<'a, 'b, T: ?Sized>(cols: &'a [Column<'b>], name: &T) -> Option<&'a Column<'b>>\nwhere\n    Ident: PartialEq<T>,\n{\n    cols.iter().find(|col| col.ident == name)\n}\n\nfn find_column<'a, 'b>(cols: &'a [Column<'b>], name: &Ident) -> syn::Result<&'a Column<'b>> {\n    try_find_column(cols, name).ok_or_else(|| syn::Error::new(name.span(), \"not a column of the table\"))\n}\n\nenum ColumnAttr {\n    Unique(Span),\n    AutoInc(Span),\n    PrimaryKey(Span),\n    Index(IndexArg),\n    Default(syn::Expr, Span),\n}\n\nimpl ColumnAttr {\n    fn parse(attr: &syn::Attribute, field_ident: &Ident) -> syn::Result<Option<Self>> {\n        let Some(ident) = attr.path().get_ident() else {\n            return Ok(None);\n        };\n        Ok(if ident == sym::index {\n            let index = IndexArg::parse_index_attr(field_ident, attr)?;\n            Some(ColumnAttr::Index(index))\n        } else if ident == sym::unique {\n            attr.meta.require_path_only()?;\n            Some(ColumnAttr::Unique(ident.span()))\n        } else if ident == sym::auto_inc {\n            attr.meta.require_path_only()?;\n            Some(ColumnAttr::AutoInc(ident.span()))\n        } else if ident == sym::primary_key {\n            attr.meta.require_path_only()?;\n            Some(ColumnAttr::PrimaryKey(ident.span()))\n        } else if ident == sym::default {\n            Some(parse_default_attr(attr, ident)?)\n        } else {\n            None\n        })\n    }\n}\n\nfn parse_default_attr(attr: &syn::Attribute, ident: &Ident) -> syn::Result<ColumnAttr> {\n    if let Ok(expr) = attr.parse_args::<syn::Expr>() {\n        return Ok(ColumnAttr::Default(expr, ident.span()));\n    }\n\n    Err(syn::Error::new_spanned(\n        &attr.meta,\n        \"expected default value in format `#[default(CONSTANT_VALUE)]`\",\n    ))\n}\n\nuse std::collections::HashSet;\nuse std::sync::Mutex;\n\n// Same struct can be annotated with `#[spacetimedb::table]` multiple times.\n// This mutex keeps track of which structs we've already generated code for.\n// This avoids duplicate definitions when the same struct is annotated multiple times.\nstatic GENERATED_STRUCTS: std::sync::LazyLock<Mutex<HashSet<String>>> =\n    std::sync::LazyLock::new(|| Mutex::new(HashSet::new()));\n\nfn is_first_appearance(struct_name: &str) -> bool {\n    let mut set = GENERATED_STRUCTS.lock().expect(\"mutex poisoned\");\n\n    set.insert(struct_name.to_string())\n}\n\npub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::Result<TokenStream> {\n    let vis = &item.vis;\n    let sats_ty = sats::sats_type_from_derive(item, quote!(spacetimedb::spacetimedb_lib))?;\n\n    let original_struct_ident = sats_ty.ident;\n    let table_ident = &args.accessor;\n    let explicit_table_name = args.name.as_ref().map(|s| s.value());\n    let view_trait_ident = format_ident!(\"{}__view\", table_ident);\n    let query_trait_ident = format_ident!(\"{}__query\", table_ident);\n    let query_cols_struct = format_ident!(\"{}Cols\", original_struct_ident);\n    let query_ix_cols_struct = format_ident!(\"{}IxCols\", original_struct_ident);\n    let table_name = table_ident.unraw().to_string();\n    let sats::SatsTypeData::Product(fields) = &sats_ty.data else {\n        return Err(syn::Error::new(Span::call_site(), \"spacetimedb table must be a struct\"));\n    };\n\n    for param in &item.generics.params {\n        let err = |msg| syn::Error::new_spanned(param, msg);\n        match param {\n            syn::GenericParam::Lifetime(_) => {}\n            syn::GenericParam::Type(_) => return Err(err(\"type parameters are not allowed on tables\")),\n            syn::GenericParam::Const(_) => return Err(err(\"const parameters are not allowed on tables\")),\n        }\n    }\n\n    let table_id_from_name_func = quote! {\n        fn table_id() -> spacetimedb::TableId {\n            static TABLE_ID: std::sync::OnceLock<spacetimedb::TableId> = std::sync::OnceLock::new();\n            *TABLE_ID.get_or_init(|| {\n                spacetimedb::table_id_from_name(<Self as spacetimedb::table::TableInternal>::TABLE_NAME)\n            })\n        }\n    };\n\n    if fields.len() > u16::MAX.into() {\n        return Err(syn::Error::new_spanned(\n            item,\n            \"too many columns; the most a table can have is 2^16\",\n        ));\n    }\n\n    let mut columns = vec![];\n    let mut unique_columns = vec![];\n    let mut sequenced_columns = vec![];\n    let mut primary_key_column = None;\n\n    for (i, field) in fields.iter().enumerate() {\n        let col_num = i as u16;\n        let field_ident = field.ident.unwrap();\n\n        let mut unique = None;\n        let mut auto_inc = None;\n        let mut primary_key = None;\n        let mut default_value = None;\n        for attr in field.original_attrs {\n            let Some(attr) = ColumnAttr::parse(attr, field_ident)? else {\n                continue;\n            };\n            match attr {\n                ColumnAttr::Unique(span) => {\n                    check_duplicate(&unique, span)?;\n                    unique = Some(span);\n                }\n                ColumnAttr::AutoInc(span) => {\n                    check_duplicate(&auto_inc, span)?;\n                    auto_inc = Some(span);\n                }\n                ColumnAttr::PrimaryKey(span) => {\n                    check_duplicate(&primary_key, span)?;\n                    primary_key = Some(span);\n                }\n                ColumnAttr::Index(index_arg) => args.indices.push(index_arg),\n                ColumnAttr::Default(expr, span) => {\n                    check_duplicate(&default_value, span)?;\n                    default_value = Some(expr);\n                }\n            }\n        }\n\n        if let Some(default_value) = &default_value\n            && (auto_inc.is_some() || primary_key.is_some() || unique.is_some())\n        {\n            return Err(syn::Error::new(\n                default_value.span(),\n                \"invalid combination: auto_inc, unique index or primary key cannot have a default value\",\n            ));\n        };\n\n        let column = Column {\n            index: col_num,\n            ident: field_ident,\n            vis: field.vis,\n            ty: field.ty,\n            default_value,\n        };\n\n        if unique.is_some() || primary_key.is_some() {\n            unique_columns.push(column.clone());\n        }\n        if auto_inc.is_some() {\n            sequenced_columns.push(column.clone());\n        }\n        if let Some(span) = primary_key {\n            check_duplicate_msg(&primary_key_column, span, \"can only have one primary key per table\")?;\n            primary_key_column = Some(column.clone());\n        }\n\n        columns.push(column.clone());\n    }\n\n    let row_type = quote!(#original_struct_ident);\n\n    // Mark all indices with a single column matching a unique constraint as unique.\n    // For all the unpaired unique columns, create a unique index.\n    for unique_col in &unique_columns {\n        if args.indices.iter_mut().any(|index| {\n            let covered_by_index = match &index.kind {\n                IndexType::BTree { columns } | IndexType::Hash { columns } => {\n                    &**columns == slice::from_ref(unique_col.ident)\n                }\n                IndexType::Direct { column } => column == unique_col.ident,\n            };\n            index.is_unique |= covered_by_index;\n            covered_by_index\n        }) {\n            continue;\n        }\n        // NOTE(centril): We pick `btree` here if the user does not specify otherwise,\n        // as it's the safest choice of index for the general case,\n        // even if isn't optimal in specific cases.\n        let accessor = unique_col.ident.clone();\n        let columns = vec![accessor.clone()];\n        args.indices.push(IndexArg {\n            accessor,\n            //name: None,\n            is_unique: true,\n            kind: IndexType::BTree { columns },\n            canonical_name: None,\n        })\n    }\n\n    let mut indices = args\n        .indices\n        .iter()\n        .map(|index| index.validate(&table_name, &columns))\n        .collect::<syn::Result<Vec<_>>>()?;\n\n    // Order unique accessors before index accessors.\n    indices.sort_by_key(|index| !index.is_unique);\n\n    let tablehandle_ident = format_ident!(\"{}__TableHandle\", table_ident);\n    let viewhandle_ident = format_ident!(\"{}__ViewHandle\", table_ident);\n\n    let index_descs = indices.iter().map(|index| index.desc());\n    let index_accessors_rw = indices\n        .iter()\n        .map(|index| index.accessor(vis, original_struct_ident, &tablehandle_ident, AccessorType::ReadWrite));\n    let index_accessors_ro = indices\n        .iter()\n        .map(|index| index.accessor(vis, original_struct_ident, &tablehandle_ident, AccessorType::Read));\n    let index_marker_types: Vec<_> = indices\n        .iter()\n        .map(|index| index.marker_type(vis, &tablehandle_ident, primary_key_column.as_ref()))\n        .collect();\n\n    // Generate `integrate_generated_columns`\n    // which will integrate all generated auto-inc col values into `_row`.\n    let integrate_gen_col = sequenced_columns.iter().map(|col| {\n        let field = col.ident;\n        quote_spanned!(field.span()=>\n            spacetimedb::table::SequenceTrigger::maybe_decode_into(&mut __row.#field, &mut __generated_cols);\n        )\n    });\n    let integrate_generated_columns = quote_spanned!(item.span() =>\n        fn integrate_generated_columns(__row: &mut #row_type, mut __generated_cols: &[u8]) {\n            #(#integrate_gen_col)*\n        }\n    );\n\n    let table_access = args.access.iter().map(|acc| acc.to_value());\n    let is_event = args.event.iter().map(|_| {\n        quote!(\n            const IS_EVENT: bool = true;\n        )\n    });\n    let can_be_lookup_impl = if args.event.is_none() {\n        quote! {\n            impl spacetimedb::query_builder::CanBeLookupTable for #original_struct_ident {}\n        }\n    } else {\n        quote! {}\n    };\n    let unique_col_ids = unique_columns.iter().map(|col| col.index);\n    let primary_col_id = primary_key_column.clone().into_iter().map(|col| col.index);\n    let sequence_col_ids = sequenced_columns.iter().map(|col| col.index);\n\n    let default_type_check: TokenStream = columns\n        .iter()\n        .filter_map(|col| {\n            if let Some(val) = &col.default_value {\n                let ty = &col.ty;\n                let ident_span = col.ident.span();\n                Some(quote_spanned! { ident_span =>\n                    // This closure enforces that `val` is of type `ty` at compile-time.\n                    let _check: #ty = #val;\n                })\n            } else {\n                None\n            }\n        })\n        .collect();\n\n    let col_defaults: Vec<TokenStream> = columns.iter().filter_map(|col| {\n        if let Some(val) = &col.default_value {\n            let col_id = col.index;\n            Some(quote! {\n                spacetimedb::table::ColumnDefault {\n                    col_id: #col_id,\n                    value: #val.serialize(spacetimedb::sats::algebraic_value::ser::ValueSerializer).expect(\"default value serialization failed\"),\n                },\n            })\n        } else {\n            None\n        }\n    }).collect();\n\n    let default_fn: TokenStream = quote! {\n        fn get_default_col_values() -> Vec<spacetimedb::table::ColumnDefault> {\n            [#(#col_defaults)*].to_vec()\n        }\n    };\n\n    let (schedule, schedule_typecheck) = args\n        .scheduled\n        .as_ref()\n        .map(|sched| {\n            let scheduled_at_column = match &sched.at {\n                Some(at) => Some(find_column(&columns, at)?),\n                None => try_find_column(&columns, \"scheduled_at\"),\n            };\n            // better error message when both are missing\n            if scheduled_at_column.is_none() && primary_key_column.is_none() {\n                return Err(syn::Error::new(\n                    sched.span,\n                    \"scheduled table missing required columns; add these to your struct:\\n\\\n                             #[primary_key]\\n\\\n                             #[auto_inc]\\n\\\n                             scheduled_id: u64,\\n\\\n                             scheduled_at: spacetimedb::ScheduleAt,\",\n                ));\n            }\n            let scheduled_at_column = scheduled_at_column.ok_or_else(|| {\n                syn::Error::new(\n                    sched.span,\n                    \"scheduled tables must have a `scheduled_at: spacetimedb::ScheduleAt` column. \\\n                             if the column has a name besides `scheduled_at`, you can specify it with \\\n                             `scheduled(my_reducer, at = custom_scheduled_at)`\",\n                )\n            })?;\n            let primary_key_column = primary_key_column.ok_or_else(|| {\n                syn::Error::new(\n                    sched.span,\n                    \"scheduled tables must have a `#[primary_key] #[auto_inc] scheduled_id: u64` column\",\n                )\n            })?;\n\n            let reducer_or_procedure = &sched.reducer_or_procedure;\n            let scheduled_at_id = scheduled_at_column.index;\n            let desc = quote!(spacetimedb::table::ScheduleDesc {\n                reducer_or_procedure_name: <#reducer_or_procedure as spacetimedb::rt::FnInfo>::NAME,\n                scheduled_at_column: #scheduled_at_id,\n            });\n\n            let primary_key_ty = primary_key_column.ty;\n            let scheduled_at_ty = scheduled_at_column.ty;\n            let typecheck = quote! {\n                spacetimedb::rt::scheduled_typecheck::<\n                    #original_struct_ident,\n                    <#reducer_or_procedure as spacetimedb::rt::FnInfo>::FnKind,\n                >(#reducer_or_procedure);\n                spacetimedb::rt::assert_scheduled_table_primary_key::<#primary_key_ty>();\n                let _ = |x: #scheduled_at_ty| { let _: spacetimedb::ScheduleAt = x; };\n            };\n\n            Ok((desc, typecheck))\n        })\n        .transpose()?\n        .unzip();\n    let schedule = schedule.into_iter();\n\n    let unique_err = if !unique_columns.is_empty() {\n        quote!(spacetimedb::UniqueConstraintViolation)\n    } else {\n        quote!(::core::convert::Infallible)\n    };\n    let autoinc_err = if !sequenced_columns.is_empty() {\n        quote!(spacetimedb::AutoIncOverflow)\n    } else {\n        quote!(::core::convert::Infallible)\n    };\n\n    let field_types = fields.iter().map(|f| f.ty).collect::<Vec<_>>();\n\n    let tabletype_impl = quote! {\n        use spacetimedb::Serialize;\n        impl spacetimedb::Table for #tablehandle_ident {\n            type Row = #row_type;\n\n            type UniqueConstraintViolation = #unique_err;\n            type AutoIncOverflow = #autoinc_err;\n\n            #integrate_generated_columns\n        }\n        impl spacetimedb::table::TableInternal for #tablehandle_ident {\n            const TABLE_NAME: &'static str = #table_name;\n            // the default value if not specified is Private\n            #(const TABLE_ACCESS: spacetimedb::table::TableAccess = #table_access;)*\n            #(#is_event)*\n            const UNIQUE_COLUMNS: &'static [u16] = &[#(#unique_col_ids),*];\n            const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#index_descs),*];\n            #(const PRIMARY_KEY: Option<u16> = Some(#primary_col_id);)*\n            const SEQUENCES: &'static [u16] = &[#(#sequence_col_ids),*];\n            #(const SCHEDULE: Option<spacetimedb::table::ScheduleDesc<'static>> = Some(#schedule);)*\n\n            #table_id_from_name_func\n            #default_fn\n        }\n    };\n\n    let explicit_names_impl =\n        generate_explicit_names_impl(&table_name, &tablehandle_ident, &explicit_table_name, &indices);\n\n    let register_describer_symbol = format!(\"__preinit__20_register_describer_{table_ident}\");\n\n    let describe_table_func = quote! {\n        #[unsafe(export_name = #register_describer_symbol)]\n        extern \"C\" fn __register_describer() {\n            spacetimedb::rt::register_table::<#tablehandle_ident>()\n        }\n    };\n\n    // Output all macro data\n    let trait_def = quote_spanned! {table_ident.span()=>\n        #[allow(non_camel_case_types, dead_code)]\n        #vis trait #table_ident {\n            #[allow(non_camel_case_types, dead_code)]\n            fn #table_ident(&self) -> &#tablehandle_ident;\n        }\n        impl #table_ident for spacetimedb::Local {\n            #[allow(non_camel_case_types, dead_code)]\n            fn #table_ident(&self) -> &#tablehandle_ident {\n                &#tablehandle_ident {}\n            }\n        }\n    };\n\n    let trait_def_view = quote_spanned! {table_ident.span()=>\n        #[allow(non_camel_case_types, dead_code)]\n        #vis trait #view_trait_ident {\n            #[allow(non_camel_case_types, dead_code)]\n            fn #table_ident(&self) -> &#viewhandle_ident;\n        }\n        impl #view_trait_ident for spacetimedb::LocalReadOnly {\n            #[inline]\n            fn #table_ident(&self) -> &#viewhandle_ident {\n                &#viewhandle_ident {}\n            }\n        }\n    };\n\n    let cols_struct_fields = fields.iter().map(|col| {\n        let ident = col.ident.unwrap();\n        let ty = &col.ty;\n\n        quote! {\n            pub #ident: spacetimedb::query_builder::Col<#original_struct_ident, #ty>,\n        }\n    });\n\n    let ix_cols_struct_fields = indices.iter().filter_map(|index| {\n        let ident = index.accessor_name.clone();\n        let ty = index.kind.one_col()?.ty;\n\n        Some(quote! {\n            pub #ident: spacetimedb::query_builder::IxCol<#original_struct_ident, #ty>,\n        })\n    });\n\n    let cols_init = fields.iter().map(|col| {\n        let ident = col.ident.as_ref().unwrap();\n\n        quote! {\n            #ident: spacetimedb::query_builder::Col::new(_table_name, stringify!(#ident)),\n        }\n    });\n\n    let ix_cols_init = indices.iter().map(|index| {\n        if index.kind.one_col().is_none() {\n            quote! {}\n        } else {\n            let ident = index.accessor_name;\n            quote! {\n                #ident: spacetimedb::query_builder::IxCol::new(_table_name, stringify!(#ident)),\n            }\n        }\n    });\n\n    let query_builder_helper_structs = quote_spanned! {table_ident.span()=>\n           #[allow(non_camel_case_types, dead_code)]\n           pub struct #query_cols_struct{\n               #(#cols_struct_fields)*\n           }\n\n           impl spacetimedb::query_builder::HasCols for #original_struct_ident  {\n               type Cols = #query_cols_struct;\n                fn cols(_table_name: &'static str) -> Self::Cols {\n                     #query_cols_struct {\n                          #(#cols_init)*\n                     }\n                }\n           }\n\n        #[allow(non_camel_case_types, dead_code)]\n        pub struct #query_ix_cols_struct{\n            #(#ix_cols_struct_fields)*\n        }\n        impl spacetimedb::query_builder::HasIxCols for #original_struct_ident {\n            type IxCols = #query_ix_cols_struct;\n            fn ix_cols(_table_name: &'static str) -> Self::IxCols {\n                #query_ix_cols_struct {\n                    #(#ix_cols_init)*\n                }\n            }\n        }\n\n        #can_be_lookup_impl\n\n    };\n\n    let table_query_handle_def = quote! {\n           #[allow(non_camel_case_types, dead_code)]\n           #vis trait #query_trait_ident {\n               fn #table_ident(&self) -> spacetimedb::query_builder::Table<#original_struct_ident> {\n                   spacetimedb::query_builder::Table::new(stringify!(#table_ident))\n               }\n           }\n           impl #query_trait_ident for spacetimedb::QueryBuilder {}\n    };\n\n    let tablehandle_def = quote! {\n        #[allow(non_camel_case_types)]\n        #[non_exhaustive]\n        #vis struct #tablehandle_ident {}\n    };\n\n    let viewhandle_def = quote! {\n        #[allow(non_camel_case_types)]\n        #[non_exhaustive]\n        #vis struct #viewhandle_ident {}\n    };\n\n    let struct_name = original_struct_ident.to_string();\n    let is_first_table = is_first_appearance(&struct_name);\n\n    let struct_level_query_impl = if is_first_table {\n        quote! { #query_builder_helper_structs }\n    } else {\n        quote! {}\n    };\n\n    let emission = quote! {\n        const _: () = {\n            #(let _ = <#field_types as spacetimedb::rt::TableColumn>::_ITEM;)*\n            #schedule_typecheck\n            #default_type_check\n        };\n\n        #trait_def\n        #trait_def_view\n\n        #tablehandle_def\n        #viewhandle_def\n        #table_query_handle_def\n        #struct_level_query_impl\n\n        const _: () = {\n            impl #tablehandle_ident {\n                #(#index_accessors_rw)*\n            }\n\n            impl #viewhandle_ident {\n                #[inline]\n                pub fn count(&self) -> u64 {\n                    spacetimedb::table::count::<#tablehandle_ident>()\n                }\n\n                #(#index_accessors_ro)*\n            }\n\n            #tabletype_impl\n\n            #explicit_names_impl\n\n            #[allow(non_camel_case_types)]\n            mod __indices {\n                #[allow(unused)]\n                use super::*;\n                #(#index_marker_types)*\n            }\n\n            #describe_table_func\n        };\n    };\n\n    if std::env::var(\"PROC_MACRO_DEBUG\").is_ok() {\n        {\n            #![allow(clippy::disallowed_macros)]\n            println!(\"{emission}\");\n        }\n    }\n\n    Ok(emission)\n}\n\nfn generate_explicit_names_impl(\n    table_name: &str,\n    tablehandle_ident: &Ident,\n    explicit_table_name: &Option<String>,\n    indexes: &[ValidatedIndex],\n) -> TokenStream {\n    let mut explicit_names_body = Vec::new();\n\n    // Table name\n    if let Some(explicit_table_name) = explicit_table_name {\n        explicit_names_body.push(quote! {\n            names.insert_table(\n                #table_name,\n                #explicit_table_name,\n            );\n        });\n    };\n\n    // Index names\n    for index in indexes {\n        if let Some(canonical_name) = &index.canonical_name {\n            let index_name = &index.index_name;\n            explicit_names_body.push(quote! {\n                names.insert_index(\n                    #index_name,\n                    #canonical_name,\n                );\n            });\n        }\n    }\n\n    quote! {\n\n        impl spacetimedb::rt::ExplicitNames for #tablehandle_ident {\n            fn explicit_names() -> spacetimedb::spacetimedb_lib::ExplicitNames {\n                let mut names = spacetimedb::spacetimedb_lib::ExplicitNames::default();\n                #(#explicit_names_body)*\n                names\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-macro/src/util.rs",
    "content": "use proc_macro::TokenStream as StdTokenStream;\nuse proc_macro2::{Span, TokenStream};\nuse syn::parse::Parse;\nuse syn::Ident;\n\n/// Parses `item`, passing it and `args` to `f`,\n/// which should return only whats newly added, excluding the `item`.\n/// Returns the full token stream `extra_attr item newly_added`.\npub(crate) fn cvt_attr<Item: Parse + quote::ToTokens>(\n    args: StdTokenStream,\n    item: StdTokenStream,\n    extra_attr: TokenStream,\n    f: impl FnOnce(TokenStream, &Item) -> syn::Result<TokenStream>,\n) -> StdTokenStream {\n    let item: TokenStream = item.into();\n    let parsed_item = match syn::parse2::<Item>(item.clone()) {\n        Ok(i) => i,\n        Err(e) => return TokenStream::from_iter([item, e.into_compile_error()]).into(),\n    };\n    let generated = f(args.into(), &parsed_item).unwrap_or_else(syn::Error::into_compile_error);\n    TokenStream::from_iter([extra_attr, item, generated]).into()\n}\n\n/// Run `f`, converting `Err` returns into a compile error.\n///\n/// This helper allows code within the closure `f` to use `?` for early return.\npub(crate) fn ok_or_compile_error<Res: Into<StdTokenStream>>(f: impl FnOnce() -> syn::Result<Res>) -> StdTokenStream {\n    match f() {\n        Ok(ok) => ok.into(),\n        Err(e) => e.into_compile_error().into(),\n    }\n}\n\npub(crate) fn ident_to_litstr(ident: &Ident) -> syn::LitStr {\n    syn::LitStr::new(&ident.to_string(), ident.span())\n}\n\npub(crate) trait ErrorSource {\n    fn error(self, msg: impl std::fmt::Display) -> syn::Error;\n}\nimpl ErrorSource for Span {\n    fn error(self, msg: impl std::fmt::Display) -> syn::Error {\n        syn::Error::new(self, msg)\n    }\n}\nimpl ErrorSource for &syn::meta::ParseNestedMeta<'_> {\n    fn error(self, msg: impl std::fmt::Display) -> syn::Error {\n        self.error(msg)\n    }\n}\n\n/// Ensures that `x` is `None` or returns an error.\npub(crate) fn check_duplicate<T>(x: &Option<T>, src: impl ErrorSource) -> syn::Result<()> {\n    check_duplicate_msg(x, src, \"duplicate attribute\")\n}\npub(crate) fn check_duplicate_msg<T>(\n    x: &Option<T>,\n    src: impl ErrorSource,\n    msg: impl std::fmt::Display,\n) -> syn::Result<()> {\n    if x.is_none() {\n        Ok(())\n    } else {\n        Err(src.error(msg))\n    }\n}\n\npub(crate) fn one_of(options: &[crate::sym::Symbol]) -> String {\n    match options {\n        [] => \"unexpected attribute\".to_owned(),\n        [a] => {\n            format!(\"expected `{a}`\")\n        }\n        [a, b] => {\n            format!(\"expected `{a}` or `{b}`\")\n        }\n        _ => {\n            let join = options.join(\"`, `\");\n            format!(\"expected one of: `{join}`\")\n        }\n    }\n}\n\nmacro_rules! match_meta {\n    (match $meta:ident { $($matches:tt)* }) => {{\n        let meta: &syn::meta::ParseNestedMeta = &$meta;\n        match_meta!(@match (), (), meta { $($matches)* })\n    }};\n\n    (@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:block $($rest:tt)* }) => {\n        match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })\n    };\n    (@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:expr, $($rest:tt)* }) => {\n        match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })\n    };\n\n    (@match ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident {}) => {\n        match () {\n            $($acc)*\n            _ => return Err($meta.error($crate::util::one_of(&[$($comparisons),*]))),\n        }\n    };\n\n    (@case ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident, $binding:tt, $sym:path, $body:expr, { $($rest:tt)* }) => {\n        match_meta!(@match (\n            $($acc)*\n            _ if $meta.path == $sym => $body,\n        ), ($($comparisons,)* $sym), $meta { $($rest)* })\n    };\n}\npub(crate) use match_meta;\n"
  },
  {
    "path": "crates/bindings-macro/src/view.rs",
    "content": "use heck::ToSnakeCase;\nuse proc_macro2::{Ident, Span, TokenStream};\nuse quote::quote;\nuse syn::ext::IdentExt;\nuse syn::parse::Parser;\nuse syn::{FnArg, ItemFn, LitStr};\n\nuse crate::reducer::generate_explicit_names_impl;\nuse crate::sym;\nuse crate::util::{check_duplicate_msg, match_meta};\n\npub(crate) struct ViewArgs {\n    name: Option<LitStr>,\n    accessor: Ident,\n    #[allow(unused)]\n    public: bool,\n}\n\nimpl ViewArgs {\n    /// Parse `#[view(accessor = ..., public)]` where both `name` and `public` are required.\n    pub(crate) fn parse(input: TokenStream, func_ident: &Ident) -> syn::Result<Self> {\n        let mut name = None;\n        let mut accessor = None;\n        let mut public = None;\n        syn::meta::parser(|meta| {\n            match_meta!(match meta {\n                sym::name => {\n                    check_duplicate_msg(&name, &meta, \"`name` already specified\")?;\n                    name = Some(meta.value()?.parse::<LitStr>()?);\n                }\n                sym::public => {\n                    check_duplicate_msg(&public, &meta, \"`public` already specified\")?;\n                    public = Some(());\n                }\n                sym::accessor => {\n                    check_duplicate_msg(&accessor, &meta, \"`accessor` already specified\")?;\n                    accessor = Some(meta.value()?.parse()?);\n                }\n            });\n            Ok(())\n        })\n        .parse2(input)?;\n        let accessor = accessor.ok_or_else(|| {\n            let view = func_ident.to_string().to_snake_case();\n            syn::Error::new(\n                Span::call_site(),\n                format_args!(\"must specify view accessor, e.g. `#[spacetimedb::view(accessor = {view})]\"),\n            )\n        })?;\n        let () = public\n            .ok_or_else(|| syn::Error::new(Span::call_site(), \"views must be `public`, e.g. `#[view(public)]`\"))?;\n        Ok(Self {\n            name,\n            public: true,\n            accessor,\n        })\n    }\n}\n\n/// If `ty` is `impl Query<T>`, returns `Some(T)`. Otherwise `None`.\nfn extract_impl_query_inner(ty: &syn::Type) -> Option<&syn::Type> {\n    if let syn::Type::ImplTrait(impl_trait) = ty {\n        for bound in &impl_trait.bounds {\n            if let syn::TypeParamBound::Trait(trait_bound) = bound\n                && let Some(seg) = trait_bound.path.segments.last()\n                && seg.ident == \"Query\"\n                && let syn::PathArguments::AngleBracketed(args) = &seg.arguments\n                && let Some(syn::GenericArgument::Type(inner)) = args.args.first()\n            {\n                return Some(inner);\n            }\n        }\n    }\n    None\n}\n\npub(crate) fn view_impl(args: ViewArgs, original_function: &ItemFn) -> syn::Result<TokenStream> {\n    let vis = &original_function.vis;\n    let func_name = &original_function.sig.ident;\n    let view_ident = args.accessor;\n    let view_name = view_ident.unraw().to_string();\n\n    for param in &original_function.sig.generics.params {\n        let err = |msg| syn::Error::new_spanned(param, msg);\n        match param {\n            syn::GenericParam::Lifetime(_) => {}\n            syn::GenericParam::Type(_) => return Err(err(\"type parameters are not allowed on views\")),\n            syn::GenericParam::Const(_) => return Err(err(\"const parameters are not allowed on views\")),\n        }\n    }\n\n    // Extract parameters\n    let typed_args = original_function\n        .sig\n        .inputs\n        .iter()\n        .map(|arg| match arg {\n            FnArg::Typed(arg) => Ok(arg),\n            FnArg::Receiver(_) => Err(syn::Error::new_spanned(\n                arg,\n                \"The `self` parameter is not allowed in views\",\n            )),\n        })\n        .collect::<syn::Result<Vec<_>>>()?;\n\n    // Extract parameter names\n    let opt_arg_names = typed_args.iter().map(|arg| {\n        if let syn::Pat::Ident(i) = &*arg.pat {\n            let name = i.ident.to_string();\n            quote!(Some(#name))\n        } else {\n            quote!(None)\n        }\n    });\n\n    let arg_tys = typed_args.iter().map(|arg| arg.ty.as_ref()).collect::<Vec<_>>();\n\n    // Extract the context type and the rest of the parameter types\n    let [ctx_ty, arg_tys @ ..] = &arg_tys[..] else {\n        return Err(syn::Error::new_spanned(\n            &original_function.sig,\n            \"Views must always have a context parameter: `&ViewContext` or `&AnonymousViewContext`\",\n        ));\n    };\n\n    // TODO: Re-enable parameterized views once we can pass args from sql\n    if !arg_tys.is_empty() {\n        return Err(syn::Error::new_spanned(\n            &original_function.sig,\n            \"Views do not take parameters other than `&ViewContext` or `&AnonymousViewContext`\",\n        ));\n    }\n\n    // Extract the context type\n    let ctx_ty = match ctx_ty {\n        syn::Type::Reference(ctx_ty) => ctx_ty.elem.as_ref(),\n        _ => {\n            return Err(syn::Error::new_spanned(\n                ctx_ty,\n                \"The first parameter of a view must be a context parameter: `&ViewContext` or `&AnonymousViewContext`; passed by reference\",\n            ));\n        }\n    };\n\n    // Views must return a result\n    let ret_ty = match &original_function.sig.output {\n        syn::ReturnType::Type(_, t) => t.as_ref(),\n        syn::ReturnType::Default => {\n            return Err(syn::Error::new_spanned(\n                &original_function.sig,\n                \"views must return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`\",\n            ));\n        }\n    };\n\n    let register_describer_symbol = format!(\"__preinit__20_register_describer_{}\", view_name);\n\n    let lt_params = &original_function.sig.generics;\n    let lt_where_clause = &lt_params.where_clause;\n\n    let generated_describe_function = quote! {\n        #[unsafe(export_name = #register_describer_symbol)]\n        pub extern \"C\" fn __register_describer() {\n            spacetimedb::rt::ViewRegistrar::<#ctx_ty>::register::<_, #func_name, _, _>(#func_name)\n        }\n    };\n\n    let explicit_name = args.name.as_ref();\n    let generate_explicit_names = generate_explicit_names_impl(&view_name, func_name, explicit_name);\n\n    let original_attrs = &original_function.attrs;\n    let original_body = &original_function.block;\n\n    // Detect `impl Query<T>` return type and extract `T`.\n    let impl_query_inner = extract_impl_query_inner(ret_ty);\n\n    // When the return type is `impl Query<T>`:\n    //   - Rewrite the function to return `RawQuery<T>`\n    //   - Wrap the body: `RawQuery::new(Query::into_sql({ body }))`\n    //   - Use `RawQuery<T>` for SpacetimeType/ViewReturn assertions\n    // When the return type is `RawQuery<T>` (concrete query struct):\n    //   - Wrap with `.into()` so builder types auto-convert\n    // Otherwise (Vec<T>, Option<T>):\n    //   - Emit unchanged to preserve type inference\n    let (emitted_fn, effective_ret_ty) = if let Some(inner_ty) = impl_query_inner {\n        let original_sig = &original_function.sig;\n        // Build a new signature with the return type replaced\n        let mut new_sig = original_sig.clone();\n        new_sig.output = syn::parse_quote!(-> spacetimedb::RawQuery<#inner_ty>);\n        let effective_ty: syn::Type = syn::parse_quote!(spacetimedb::RawQuery<#inner_ty>);\n        (\n            quote! {\n                #(#original_attrs)*\n                #vis\n                #new_sig {\n                    spacetimedb::RawQuery::new(\n                        Query::into_sql(#original_body)\n                    )\n                }\n            },\n            effective_ty,\n        )\n    } else {\n        let original_sig = &original_function.sig;\n        let returns_raw_query =\n            matches!(ret_ty, syn::Type::Path(p) if p.path.segments.last().is_some_and(|s| s.ident == \"RawQuery\"));\n        let emitted_body = if returns_raw_query {\n            quote! { { ::core::convert::Into::into(#original_body) } }\n        } else {\n            quote! { #original_body }\n        };\n        (\n            quote! {\n                #(#original_attrs)*\n                #vis\n                #original_sig\n                    #emitted_body\n            },\n            ret_ty.clone(),\n        )\n    };\n\n    let eff_ret_ty = &effective_ret_ty;\n\n    Ok(quote! {\n        #emitted_fn\n\n        const _: () = { #generated_describe_function };\n\n        #[allow(non_camel_case_types)]\n        #vis struct #func_name { _never: ::core::convert::Infallible }\n\n        const _: () = {\n            fn _assert_args #lt_params () #lt_where_clause {\n                let _ = <#ctx_ty as spacetimedb::rt::ViewContextArg>::_ITEM;\n                let _ = <#eff_ret_ty as spacetimedb::rt::ViewReturn>::_ITEM;\n            }\n        };\n\n        const _: () = {\n            fn _assert_args #lt_params () #lt_where_clause {\n                #(let _ = <#arg_tys as spacetimedb::rt::ViewArg>::_ITEM;)*\n            }\n        };\n\n        impl #func_name {\n            fn invoke(__ctx: #ctx_ty, __args: &[u8]) -> Vec<u8> {\n                spacetimedb::rt::ViewDispatcher::<#ctx_ty>::invoke::<_, _, _>(#func_name, __ctx, __args)\n            }\n        }\n\n        #[automatically_derived]\n        impl spacetimedb::rt::FnInfo for #func_name {\n            /// The type of this function\n            type Invoke = <spacetimedb::rt::ViewKind<#ctx_ty> as spacetimedb::rt::ViewKindTrait>::InvokeFn;\n\n            /// The function kind, which will cause scheduled tables to reject views.\n            type FnKind = spacetimedb::rt::FnKindView;\n\n            /// The name of this function\n            const NAME: &'static str = #view_name;\n\n            /// The parameter names of this function\n            const ARG_NAMES: &'static [Option<&'static str>] = &[#(#opt_arg_names),*];\n\n            /// The pointer for invoking this function\n            const INVOKE: Self::Invoke = #func_name::invoke;\n\n            /// The return type of this function\n            fn return_type(\n                ts: &mut impl spacetimedb::sats::typespace::TypespaceBuilder\n            ) -> Option<spacetimedb::sats::AlgebraicType> {\n                Some(<#eff_ret_ty as spacetimedb::SpacetimeType>::make_type(ts))\n            }\n        }\n\n        #generate_explicit_names\n    })\n}\n"
  },
  {
    "path": "crates/bindings-sys/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-bindings-sys\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Easy support for interacting between SpacetimeDB and Rust.\"\nrust-version.workspace = true\n\n[lib]\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n[features]\nunstable = []\n\n[dependencies]\nspacetimedb-primitives.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/bindings-sys/src/lib.rs",
    "content": "//! Defines sys calls to interact with SpacetimeDB.\n//! This forms an ABI of sorts that modules written in Rust can use.\n\nextern crate alloc;\n\nuse core::fmt;\nuse core::mem::MaybeUninit;\nuse core::num::NonZeroU16;\nuse std::ptr;\n\nuse spacetimedb_primitives::{errno, errnos, ColId, IndexId, TableId};\n\n/// Provides a raw set of sys calls which abstractions can be built atop of.\npub mod raw {\n    use spacetimedb_primitives::{ColId, IndexId, TableId};\n\n    // this module identifier determines the abi version that modules built with this crate depend\n    // on. Any non-breaking additions to the abi surface should be put in a new `extern {}` block\n    // with a module identifier with a minor version 1 above the previous highest minor version.\n    // For breaking changes, all functions should be moved into one new `spacetime_X.0` block.\n    #[link(wasm_import_module = \"spacetime_10.0\")]\n    unsafe extern \"C\" {\n        /// Queries the `table_id` associated with the given (table) `name`\n        /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.\n        ///\n        /// The table id is written into the `out` pointer.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n        /// - `name` is not valid UTF-8.\n        /// - `out` is NULL or `out[..size_of::<TableId>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `name` is not the name of a table.\n        pub fn table_id_from_name(name: *const u8, name_len: usize, out: *mut TableId) -> u16;\n\n        /// Queries the `index_id` associated with the given (index) `name`\n        /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.\n        ///\n        /// If a `name` was provided for the `RawIndexDef` for this index, that is the name of the index.\n        /// If no name was provided, a name was autogenerated like so:\n        /// ```\n        /// let table_name: String = \"my_table\".into();\n        /// let column_names: Vec<String> = vec![\"bananas\".into(), \"oranges\".into()];\n        /// let column_names = column_names.join(\"_\");\n        /// let name = format!(\"{table_name}_{column_names}_idx_btree\");\n        /// ```\n        /// (See the function `spacetimedb_schema::def::validate::v9::generate_index_name` for more\n        /// information.)\n        ///\n        /// The index id is written into the `out` pointer.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n        /// - `name` is not valid UTF-8.\n        /// - `out` is NULL or `out[..size_of::<IndexId>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_INDEX`, when `name` is not the name of an index.\n        pub fn index_id_from_name(name_ptr: *const u8, name_len: usize, out: *mut IndexId) -> u16;\n\n        /// Writes the number of rows currently in table identified by `table_id` to `out`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `out` is NULL or `out[..size_of::<u64>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n        pub fn datastore_table_row_count(table_id: TableId, out: *mut u64) -> u16;\n\n        /// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.\n        ///\n        /// On success, the iterator handle is written to the `out` pointer.\n        /// This handle can be advanced by [`row_iter_bsatn_advance`].\n        ///\n        /// # Traps\n        ///\n        /// This function does not trap.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n        pub fn datastore_table_scan_bsatn(table_id: TableId, out: *mut RowIter) -> u16;\n\n        /// Finds all rows in the index identified by `index_id`,\n        /// according to the:\n        /// - `prefix = prefix_ptr[..prefix_len]`,\n        /// - `rstart = rstart_ptr[..rstart_len]`,\n        /// - `rend = rend_ptr[..rend_len]`,\n        ///\n        /// in WASM memory.\n        ///\n        /// The index itself has a schema/type.\n        /// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s\n        /// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`\n        /// where the `AlgebraicValue`s are wrapped in `Bound`.\n        /// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.\n        ///\n        /// Matching is then defined by equating `prefix`\n        /// to the initial `prefix_elems` columns of the index\n        /// and then imposing `rstart` as the starting bound\n        /// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.\n        /// Remaining columns of the index are then unbounded.\n        /// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),\n        /// in which case this becomes a ranged index scan on a single-col index\n        /// or even a full table scan if `rstart` and `rend` are both unbounded.\n        ///\n        /// The relevant table for the index is found implicitly via the `index_id`,\n        /// which is unique for the module.\n        ///\n        /// On success, the iterator handle is written to the `out` pointer.\n        /// This handle can be advanced by [`row_iter_bsatn_advance`].\n        ///\n        /// # Non-obvious queries\n        ///\n        /// For an index on columns `[a, b, c]`:\n        ///\n        /// - `a = x, b = y` is encoded as a prefix `[x, y]`\n        ///   and a range `Range::Unbounded`,\n        ///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.\n        /// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`\n        ///   and a  range `rstart = rend = Range::Inclusive(z)`.\n        /// - A sorted full scan is encoded as an empty prefix\n        ///   and a range `Range::Unbounded`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `prefix_elems > 0`\n        ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).\n        /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.\n        /// - `rend` is NULL or `rend` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n        /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n        ///   a `prefix_elems` number of `AlgebraicValue`\n        ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n        ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n        ///   where the inner `AlgebraicValue`s are\n        ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n        pub fn datastore_index_scan_range_bsatn(\n            index_id: IndexId,\n            prefix_ptr: *const u8,\n            prefix_len: usize,\n            prefix_elems: ColId,\n            rstart_ptr: *const u8, // Bound<AlgebraicValue>\n            rstart_len: usize,\n            rend_ptr: *const u8, // Bound<AlgebraicValue>\n            rend_len: usize,\n            out: *mut RowIter,\n        ) -> u16;\n\n        /// This is the same as [`datastore_index_scan_range_bsatn`].\n        #[deprecated = \"use `datastore_index_scan_range_bsatn` instead\"]\n        #[doc(alias = \"datastore_index_scan_range_bsatn\")]\n        pub fn datastore_btree_scan_bsatn(\n            index_id: IndexId,\n            prefix_ptr: *const u8,\n            prefix_len: usize,\n            prefix_elems: ColId,\n            rstart_ptr: *const u8, // Bound<AlgebraicValue>\n            rstart_len: usize,\n            rend_ptr: *const u8, // Bound<AlgebraicValue>\n            rend_len: usize,\n            out: *mut RowIter,\n        ) -> u16;\n\n        /// Deletes all rows found in the index identified by `index_id`,\n        /// according to the:\n        /// - `prefix = prefix_ptr[..prefix_len]`,\n        /// - `rstart = rstart_ptr[..rstart_len]`,\n        /// - `rend = rend_ptr[..rend_len]`,\n        ///\n        /// in WASM memory.\n        ///\n        /// This syscall will delete all the rows found by\n        /// [`datastore_index_scan_range_bsatn`] with the same arguments passed,\n        /// including `prefix_elems`.\n        /// See `datastore_index_scan_range_bsatn` for details.\n        ///\n        /// The number of rows deleted is written to the WASM pointer `out`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `prefix_elems > 0`\n        ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).\n        /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.\n        /// - `rend` is NULL or `rend` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n        /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n        ///   a `prefix_elems` number of `AlgebraicValue`\n        ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n        ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n        ///   where the inner `AlgebraicValue`s are\n        ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n        pub fn datastore_delete_by_index_scan_range_bsatn(\n            index_id: IndexId,\n            prefix_ptr: *const u8,\n            prefix_len: usize,\n            prefix_elems: ColId,\n            rstart_ptr: *const u8, // Bound<AlgebraicValue>\n            rstart_len: usize,\n            rend_ptr: *const u8, // Bound<AlgebraicValue>\n            rend_len: usize,\n            out: *mut u32,\n        ) -> u16;\n\n        /// This is the same as [`datastore_delete_by_index_scan_range_bsatn`].\n        #[deprecated = \"use `datastore_delete_by_index_scan_range_bsatn` instead\"]\n        #[doc(alias = \"datastore_delete_by_index_scan_range_bsatn\")]\n        pub fn datastore_delete_by_btree_scan_bsatn(\n            index_id: IndexId,\n            prefix_ptr: *const u8,\n            prefix_len: usize,\n            prefix_elems: ColId,\n            rstart_ptr: *const u8, // Bound<AlgebraicValue>\n            rstart_len: usize,\n            rend_ptr: *const u8, // Bound<AlgebraicValue>\n            rend_len: usize,\n            out: *mut u32,\n        ) -> u16;\n\n        /// Deletes those rows, in the table identified by `table_id`,\n        /// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.\n        ///\n        /// Matching is defined by first BSATN-decoding\n        /// the byte string pointed to at by `relation` to a `Vec<ProductValue>`\n        /// according to the row schema of the table\n        /// and then using `Ord for AlgebraicValue`.\n        /// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n        /// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.\n        ///\n        /// The number of rows deleted is written to the WASM pointer `out`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `rel_ptr` is NULL or `rel` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n        /// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`\n        ///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.\n        pub fn datastore_delete_all_by_eq_bsatn(\n            table_id: TableId,\n            rel_ptr: *const u8,\n            rel_len: usize,\n            out: *mut u32,\n        ) -> u16;\n\n        /// Reads rows from the given iterator registered under `iter`.\n        ///\n        /// Takes rows from the iterator\n        /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`,\n        /// encoded in BSATN format.\n        ///\n        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n        /// On success (`0` or `-1` is returned),\n        /// `buffer_len` is set to the combined length of the encoded rows.\n        /// When `-1` is returned, the iterator has been exhausted\n        /// and there are no more rows to read,\n        /// leading to the iterator being immediately destroyed.\n        /// Note that the host is free to reuse allocations in a pool,\n        /// destroying the handle logically does not entail that memory is necessarily reclaimed.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.\n        /// - `BUFFER_TOO_SMALL`, when there are rows left but they cannot fit in `buffer`.\n        ///   When this occurs, `buffer_len` is set to the size of the next item in the iterator.\n        ///   To make progress, the caller should reallocate the buffer to at least that size and try again.\n        pub fn row_iter_bsatn_advance(iter: RowIter, buffer_ptr: *mut u8, buffer_len_ptr: *mut usize) -> i16;\n\n        /// Destroys the iterator registered under `iter`.\n        ///\n        /// Once `row_iter_bsatn_close` is called on `iter`, the `iter` is invalid.\n        /// That is, `row_iter_bsatn_close(iter)` the second time will yield `NO_SUCH_ITER`.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.\n        pub fn row_iter_bsatn_close(iter: RowIter) -> u16;\n\n        /// Inserts a row into the table identified by `table_id`,\n        /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory\n        /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.\n        ///\n        /// The byte string `row` must be a BSATN-encoded `ProductValue`\n        /// typed at the table's `ProductType` row-schema.\n        ///\n        /// To handle auto-incrementing columns,\n        /// when the call is successful,\n        /// the `row` is written back to with the generated sequence values.\n        /// These values are written as a BSATN-encoded `pv: ProductValue`.\n        /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n        /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n        /// When the table has no sequences,\n        /// this implies that the `pv`, and thus `row`, will be empty.\n        /// The `row_len` is set to the length of `bsatn(pv)`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.\n        /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n        /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`.\n        ///   typed at the `ProductType` the table's schema specifies.\n        /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.\n        /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.\n        pub fn datastore_insert_bsatn(table_id: TableId, row_ptr: *mut u8, row_len_ptr: *mut usize) -> u16;\n\n        /// Updates a row in the table identified by `table_id` to `row`\n        /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory\n        /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.\n        ///\n        /// The byte string `row` must be a BSATN-encoded `ProductValue`\n        /// typed at the table's `ProductType` row-schema.\n        ///\n        /// The row to update is found by projecting `row`\n        /// to the type of the *unique* index identified by `index_id`.\n        /// If no row is found, the error `NO_SUCH_ROW` is returned.\n        ///\n        /// To handle auto-incrementing columns,\n        /// when the call is successful,\n        /// the `row` is written back to with the generated sequence values.\n        /// These values are written as a BSATN-encoded `pv: ProductValue`.\n        /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n        /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n        /// When the table has no sequences,\n        /// this implies that the `pv`, and thus `row`, will be empty.\n        /// The `row_len` is set to the length of `bsatn(pv)`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.\n        /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n        /// - `INDEX_NOT_UNIQUE`, when the index was not unique.\n        /// - `NO_SUCH_ROW`, when the row was not found in the unique index.\n        /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`\n        ///   typed at the `ProductType` the table's schema specifies\n        ///   or when it cannot be projected to the index identified by `index_id`.\n        /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.\n        /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.\n        pub fn datastore_update_bsatn(\n            table_id: TableId,\n            index_id: IndexId,\n            row_ptr: *mut u8,\n            row_len_ptr: *mut usize,\n        ) -> u16;\n\n        /// Schedules a reducer to be called asynchronously, nonatomically,\n        /// and immediately on a best effort basis.\n        ///\n        /// The reducer is named as the valid UTF-8 slice `(name, name_len)`,\n        /// and is passed the slice `(args, args_len)` as its argument.\n        ///\n        /// Traps if\n        /// - `name` does not point to valid UTF-8\n        /// - `name + name_len` or `args + args_len` overflow a 64-bit integer\n        #[cfg(feature = \"unstable\")]\n        pub fn volatile_nonatomic_schedule_immediate(\n            name: *const u8,\n            name_len: usize,\n            args: *const u8,\n            args_len: usize,\n        );\n\n        /// Writes up to `buffer_len` bytes from `buffer = buffer_ptr[..buffer_len]`,\n        /// to the `sink`, registered in the host environment.\n        ///\n        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n        /// On success (`0` is returned),\n        /// `buffer_len` is set to the number of bytes written to `sink`.\n        ///\n        /// # Traps\n        ///\n        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NO_SUCH_BYTES`, when `sink` is not a valid bytes sink.\n        /// - `NO_SPACE`, when there is no room for more bytes in `sink`.\n        pub fn bytes_sink_write(sink: BytesSink, buffer_ptr: *const u8, buffer_len_ptr: *mut usize) -> u16;\n\n        /// Reads bytes from `source`, registered in the host environment,\n        /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`.\n        ///\n        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n        /// On success (`0` or `-1` is returned),\n        /// `buffer_len` is set to the number of bytes written to `buffer`.\n        /// When `-1` is returned, the resource has been exhausted\n        /// and there are no more bytes to read,\n        /// leading to the resource being immediately destroyed.\n        /// Note that the host is free to reuse allocations in a pool,\n        /// destroying the handle logically does not entail that memory is necessarily reclaimed.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.\n        ///\n        /// # Example\n        ///\n        /// The typical use case for this ABI is in `__call_reducer__`,\n        /// to read and deserialize the `args`.\n        /// An example definition, dealing with `args` might be:\n        /// ```rust,ignore\n        /// /// #[no_mangle]\n        /// extern \"C\" fn __call_reducer__(..., args: BytesSource, ...) -> i16 {\n        ///     // ...\n        ///\n        ///     let mut buf = Vec::<u8>::with_capacity(1024);\n        ///     loop {\n        ///         // Write into the spare capacity of the buffer.\n        ///         let buf_ptr = buf.spare_capacity_mut();\n        ///         let spare_len = buf_ptr.len();\n        ///         let mut buf_len = buf_ptr.len();\n        ///         let buf_ptr = buf_ptr.as_mut_ptr().cast();\n        ///         let ret = unsafe { bytes_source_read(args, buf_ptr, &mut buf_len) };\n        ///         // SAFETY: `bytes_source_read` just appended `spare_len` bytes to `buf`.\n        ///         unsafe { buf.set_len(buf.len() + spare_len) };\n        ///         match ret {\n        ///             // Host side source exhausted, we're done.\n        ///             -1 => break,\n        ///             // Wrote the entire spare capacity.\n        ///             // Need to reserve more space in the buffer.\n        ///             0 if spare_len == buf_len => buf.reserve(1024),\n        ///             // Host didn't write as much as possible.\n        ///             // Try to read some more.\n        ///             // The host will likely not trigger this branch,\n        ///             // but a module should be prepared for it.\n        ///             0 => {}\n        ///             _ => unreachable!(),\n        ///         }\n        ///     }\n        ///\n        ///     // ...\n        /// }\n        /// ```\n        pub fn bytes_source_read(source: BytesSource, buffer_ptr: *mut u8, buffer_len_ptr: *mut usize) -> i16;\n\n        /// Logs at `level` a `message` message occurring in `filename:line_number`\n        /// with `target` being the module path at the `log!` invocation site.\n        ///\n        /// These various pointers are interpreted lossily as UTF-8 strings with a corresponding `_len`.\n        ///\n        /// The `target` and `filename` pointers are ignored by passing `NULL`.\n        /// The line number is ignored if `line_number == u32::MAX`.\n        ///\n        /// No message is logged if\n        /// - `target != NULL && target + target_len > u64::MAX`\n        /// - `filename != NULL && filename + filename_len > u64::MAX`\n        /// - `message + message_len > u64::MAX`\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `target` is not NULL and `target_ptr[..target_len]` is not in bounds of WASM memory.\n        /// - `filename` is not NULL and `filename_ptr[..filename_len]` is not in bounds of WASM memory.\n        /// - `message` is not NULL and `message_ptr[..message_len]` is not in bounds of WASM memory.\n        ///\n        /// [target]: https://docs.rs/log/latest/log/struct.Record.html#method.target\n        pub fn console_log(\n            level: u8,\n            target_ptr: *const u8,\n            target_len: usize,\n            filename_ptr: *const u8,\n            filename_len: usize,\n            line_number: u32,\n            message_ptr: *const u8,\n            message_len: usize,\n        );\n\n        /// Begins a timing span with `name = name_ptr[..name_len]`.\n        ///\n        /// When the returned `ConsoleTimerId` is passed to [`console_timer_end`],\n        /// the duration between the calls will be printed to the module's logs.\n        ///\n        /// The `name` is interpreted lossily as UTF-8.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n        pub fn console_timer_start(name_ptr: *const u8, name_len: usize) -> u32;\n\n        /// End a timing span.\n        ///\n        /// The `timer_id` must be the result of a call to `console_timer_start`.\n        /// The duration between the two calls will be computed and printed to the module's logs.\n        /// Once `console_timer_end` is called on `id: ConsoleTimerId`, the `id` is invalid.\n        /// That is, `console_timer_end(id)` the second time will yield `NO_SUCH_CONSOLE_TIMER`.\n        ///\n        /// Note that the host is free to reuse allocations in a pool,\n        /// destroying the handle logically does not entail that memory is necessarily reclaimed.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        /// - `NO_SUCH_CONSOLE_TIMER`, when `timer_id` does not exist.\n        pub fn console_timer_end(timer_id: u32) -> u16;\n\n        /// Writes the identity of the module into `out = out_ptr[..32]`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `out_ptr` is NULL or `out` is not in bounds of WASM memory.\n        pub fn identity(out_ptr: *mut u8);\n    }\n\n    // See comment on previous `extern \"C\"` block re: ABI version.\n    #[link(wasm_import_module = \"spacetime_10.1\")]\n    unsafe extern \"C\" {\n        /// Read the remaining length of a [`BytesSource`] and write it to `out`.\n        ///\n        /// Note that the host automatically frees byte sources which are exhausted.\n        /// Such sources are invalid, and this method will return an error when passed one.\n        /// Callers of [`bytes_source_read`] should check for a return of -1\n        /// before invoking this function on the same `source`.\n        ///\n        /// Also note that the special [`BytesSource::INVALID`] (zero) is always invalid.\n        /// Callers should check for that value before invoking this function.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `out` is NULL or `out` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.\n        ///\n        /// If this function returns an error, `out` is not written.\n        pub fn bytes_source_remaining_length(source: BytesSource, out: *mut u32) -> i16;\n    }\n\n    // See comment on previous `extern \"C\"` block re: ABI version.\n    #[link(wasm_import_module = \"spacetime_10.2\")]\n    unsafe extern \"C\" {\n        /// Finds the JWT payload associated with `connection_id`.\n        /// A `[ByteSourceId]` for the payload will be written to `target_ptr`.\n        /// If nothing is found for the connection, `[ByteSourceId::INVALID]` (zero) is written to `target_ptr`.\n        ///\n        /// This must be called inside a transaction (because it reads from a system table).\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `connection_id` does not point to a valid little-endian `ConnectionId`.\n        /// - `target_ptr` is NULL or `target_ptr[..size_of::<u32>()]` is not in bounds of WASM memory.\n        ///  - The `ByteSourceId` to be written to `target_ptr` would overflow [`u32::MAX`].\n        pub fn get_jwt(connection_id_ptr: *const u8, bytes_source_id: *mut BytesSource) -> u16;\n    }\n\n    #[cfg(feature = \"unstable\")]\n    #[link(wasm_import_module = \"spacetime_10.3\")]\n    unsafe extern \"C\" {\n        /// Suspends execution of this WASM instance until approximately `wake_at_micros_since_unix_epoch`.\n        ///\n        /// Returns immediately if `wake_at_micros_since_unix_epoch` is in the past.\n        ///\n        /// Upon resuming, returns the current timestamp as microseconds since the Unix epoch.\n        ///\n        /// Not particularly useful, except for testing SpacetimeDB internals related to suspending procedure execution.\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - The calling WASM instance is holding open a transaction.\n        /// - The calling WASM instance is not executing a procedure.\n        // TODO(procedure-sleep-until): remove this\n        pub fn procedure_sleep_until(wake_at_micros_since_unix_epoch: i64) -> i64;\n\n        /// Starts a mutable transaction,\n        /// blocking until a mutable transaction lock is acquired.\n        ///\n        /// Returns `0` on success,\n        /// enabling further calls that require a pending transaction,\n        /// or an error code otherwise.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `out` is NULL or `out[..size_of::<i64>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.\n        pub fn procedure_start_mut_tx(out: *mut i64) -> u16;\n\n        /// Commits a mutable transaction,\n        /// blocking until the transaction has been committed\n        /// and subscription queries have been run and broadcast.\n        ///\n        /// Once complete, it returns `0` on success, or an error code otherwise.\n        ///\n        /// # Traps\n        ///\n        /// This function does not trap.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `TRANSACTION_NOT_ANONYMOUS`,\n        ///   if the transaction was not started in [`procedure_start_mut_tx`].\n        ///   This can happen if this syscall is erroneously called by a reducer.\n        ///   The code `NOT_IN_TRANSACTION` does not happen,\n        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n        ///   This currently does not happen as anonymous read transactions\n        ///   are not exposed to modules.\n        pub fn procedure_commit_mut_tx() -> u16;\n\n        /// Aborts a mutable transaction,\n        /// blocking until the transaction has been aborted.\n        ///\n        /// Returns `0` on success, or an error code otherwise.\n        ///\n        /// # Traps\n        ///\n        /// This function does not trap.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `TRANSACTION_NOT_ANONYMOUS`,\n        ///   if the transaction was not started in [`procedure_start_mut_tx`].\n        ///   This can happen if this syscall is erroneously called by a reducer.\n        ///   The code `NOT_IN_TRANSACTION` does not happen,\n        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n        ///   This currently does not happen as anonymous read transactions\n        ///   are not exposed to modules.\n        pub fn procedure_abort_mut_tx() -> u16;\n\n        /// Perform an HTTP request as specified by the buffer `request_ptr[..request_len]`,\n        /// suspending execution until the request is complete,\n        /// then return its response details via a [`BytesSource`] written to `out[0]`\n        /// and its response body via another [`BytesSource`] written to `out[1]`.\n        ///\n        /// `request_ptr[..request_len]` should store a BSATN-serialized `spacetimedb_lib::http::Request` object\n        /// containing the details of the request to be performed.\n        ///\n        /// `body_ptr[..body_len]` should store a byte array, which will be treated as the body of the request.\n        /// `body_ptr` should be non-null and within the bounds of linear memory even when `body_len` is 0.\n        ///\n        /// If the request is successful, a [`BytesSource`] is written to `out[0]`\n        /// containing a BSATN-encoded `spacetimedb_lib::http::Response` object,\n        /// another [`BytesSource`] containing the bytes of the response body are written to `out[1]`,\n        /// and this function returns 0.\n        ///\n        /// \"Successful\" in this context includes any connection which results in any HTTP status code,\n        /// regardless of the specified meaning of that code.\n        /// This includes HTTP error codes such as 404 Not Found and 500 Internal Server Error.\n        ///\n        /// If the request fails, a [`BytesSource`] is written to `out[0]`\n        /// containing a BSATN-encoded [`String`] describing the failure,\n        /// and this function returns `HTTP_ERROR`.\n        /// In this case, `out[1]` is not written.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `WOULD_BLOCK_TRANSACTION` if there is currently a transaction open.\n        ///   In this case, `out` is not written.\n        /// - `BSATN_DECODE_ERROR` if `request_ptr[..request_len]` does not contain\n        ///   a valid BSATN-serialized `spacetimedb_lib::http::Request` object.\n        ///   In this case, `out` is not written.\n        /// - `HTTP_ERROR` if an error occurs while executing the HTTP request.\n        ///   In this case, a [`BytesSource`] is written to `out`\n        ///   containing a BSATN-encoded `spacetimedb_lib::http::Error` object.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        ///\n        /// - `request_ptr` is NULL or `request_ptr[..request_len]` is not in bounds of WASM memory.\n        /// - `body_ptr` is NULL or `body_ptr[..body_len]` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n        /// - `request_ptr[..request_len]` does not contain a valid BSATN-serialized `spacetimedb_lib::http::Request` object.\n        #[cfg(feature = \"unstable\")]\n        pub fn procedure_http_request(\n            request_ptr: *const u8,\n            request_len: u32,\n            body_ptr: *const u8,\n            body_len: u32,\n            out: *mut [BytesSource; 2],\n        ) -> u16;\n    }\n\n    #[link(wasm_import_module = \"spacetime_10.4\")]\n    unsafe extern \"C\" {\n        /// Finds all rows in the index identified by `index_id`,\n        /// according to `point = point_ptr[..point_len]` in WASM memory.\n        ///\n        /// The index itself has a schema/type.\n        /// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`\n        /// and then comparing the decoded `point` to the keys in the index\n        /// using `Ord for AlgebraicValue`.\n        /// to the keys in the index.\n        /// The `point` is BSATN-decoded to that `AlgebraicType`.\n        /// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n        /// This occurs exactly when the row's BSATN-encoding\n        /// is equal to the encoding of the `AlgebraicValue`.\n        ///\n        /// This ABI is not limited to single column indices.\n        /// Multi-column indices can be queried by providing\n        /// a BSATN-encoded `ProductValue`\n        /// that is typed at the `ProductType` of the index.\n        ///\n        /// The relevant table for the index is found implicitly via the `index_id`,\n        /// which is unique for the module.\n        ///\n        /// On success, the iterator handle is written to the `out` pointer.\n        /// This handle can be advanced by [`row_iter_bsatn_advance`].\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n        /// - `WRONG_INDEX_ALGO` if the index is not a range-scan compatible index.\n        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n        ///   typed at the index's key type (`AlgebraicType`).\n        pub fn datastore_index_scan_point_bsatn(\n            index_id: IndexId,\n            point_ptr: *const u8, // AlgebraicValue\n            point_len: usize,\n            out: *mut RowIter,\n        ) -> u16;\n\n        /// Deletes all rows found in the index identified by `index_id`,\n        /// according to `point = point_ptr[..point_len]` in WASM memory.\n        ///\n        /// This syscall will delete all the rows found by\n        /// [`datastore_index_scan_point_bsatn`] with the same arguments passed.\n        /// See `datastore_index_scan_point_bsatn` for details.\n        ///\n        /// The number of rows deleted is written to the WASM pointer `out`.\n        ///\n        /// # Traps\n        ///\n        /// Traps if:\n        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.\n        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n        ///\n        /// # Errors\n        ///\n        /// Returns an error:\n        ///\n        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n        ///   typed at the index's key type (`AlgebraicType`).\n        pub fn datastore_delete_by_index_scan_point_bsatn(\n            index_id: IndexId,\n            point_ptr: *const u8, // AlgebraicValue\n            point_len: usize,\n            out: *mut u32,\n        ) -> u16;\n    }\n\n    /// What strategy does the database index use?\n    ///\n    /// See also: <https://www.postgresql.org/docs/current/sql-createindex.html>\n    #[repr(u8)]\n    #[non_exhaustive]\n    pub enum IndexType {\n        /// Indexing works by putting the index key into a b-tree.\n        BTree = 0,\n        /// Indexing works by hashing the index key.\n        Hash = 1,\n    }\n\n    /// The error log level. See [`console_log`].\n    pub const LOG_LEVEL_ERROR: u8 = 0;\n    /// The warn log level. See [`console_log`].\n    pub const LOG_LEVEL_WARN: u8 = 1;\n    /// The info log level. See [`console_log`].\n    pub const LOG_LEVEL_INFO: u8 = 2;\n    /// The debug log level. See [`console_log`].\n    pub const LOG_LEVEL_DEBUG: u8 = 3;\n    /// The trace log level. See [`console_log`].\n    pub const LOG_LEVEL_TRACE: u8 = 4;\n    /// The panic log level. See [`console_log`].\n    ///\n    /// A panic level is emitted just before a fatal error causes the WASM module to trap.\n    pub const LOG_LEVEL_PANIC: u8 = 101;\n\n    /// A handle into a buffer of bytes in the host environment that can be read from.\n    ///\n    /// Used for transporting bytes from host to WASM linear memory.\n    #[derive(PartialEq, Eq, Copy, Clone)]\n    #[repr(transparent)]\n    pub struct BytesSource(u32);\n\n    impl BytesSource {\n        /// An invalid handle, used e.g., when the reducer arguments were empty.\n        pub const INVALID: Self = Self(0);\n    }\n\n    /// A handle into a buffer of bytes in the host environment that can be written to.\n    ///\n    /// Used for transporting bytes from WASM linear memory to host.\n    #[derive(PartialEq, Eq, Copy, Clone)]\n    #[repr(transparent)]\n    pub struct BytesSink(u32);\n\n    /// Represents table iterators.\n    #[derive(PartialEq, Eq, Copy, Clone)]\n    #[repr(transparent)]\n    pub struct RowIter(u32);\n\n    impl RowIter {\n        /// An invalid handle, used e.g., when the iterator has been exhausted.\n        pub const INVALID: Self = Self(0);\n    }\n\n    #[cfg(any())]\n    mod module_exports {\n        type Encoded<T> = Buffer;\n        type Identity = Encoded<[u8; 32]>;\n        /// Microseconds since the unix epoch\n        type Timestamp = u64;\n        /// Buffer::INVALID => Ok(()); else errmsg => Err(errmsg)\n        type Result = Buffer;\n        extern \"C\" {\n            /// All functions prefixed with `__preinit__` are run first in alphabetical order.\n            /// For those it's recommended to use /etc/xxxx.d conventions of like `__preinit__20_do_thing`:\n            /// <https://man7.org/linux/man-pages/man5/sysctl.d.5.html#CONFIGURATION_DIRECTORIES_AND_PRECEDENCE>\n            fn __preinit__XX_XXXX();\n            /// Optional. Run after `__preinit__`; can return an error. Intended for dynamic languages; this\n            /// would be where you would initialize the interepreter and load the user module into it.\n            fn __setup__() -> Result;\n            /// Required. Runs after `__setup__`; returns all the exports for the module.\n            fn __describe_module__() -> Encoded<ModuleDef>;\n            /// Required. id is an index into the `ModuleDef.reducers` returned from `__describe_module__`.\n            /// args is a bsatn-encoded product value defined by the schema at `reducers[id]`.\n            fn __call_reducer__(\n                id: usize,\n                sender_0: u64,\n                sender_1: u64,\n                sender_2: u64,\n                sender_3: u64,\n                conn_id_0: u64,\n                conn_id_1: u64,\n                timestamp: u64,\n                args: Buffer,\n            ) -> Result;\n            /// Currently unused?\n            fn __migrate_database__XXXX(sender: Identity, timestamp: Timestamp, something: Buffer) -> Result;\n        }\n    }\n}\n\n/// Error values used in the safe bindings API.\n#[derive(Copy, Clone, PartialEq, Eq)]\n#[repr(transparent)]\npub struct Errno(NonZeroU16);\n\n// once Error gets exposed from core this crate can be no_std again\nimpl std::error::Error for Errno {}\n\npub type Result<T, E = Errno> = core::result::Result<T, E>;\n\nmacro_rules! def_errno {\n    ($($err_name:ident($errno:literal, $errmsg:literal),)*) => {\n        impl Errno {\n            $(#[doc = $errmsg] pub const $err_name: Errno = Errno(errno::$err_name);)*\n        }\n    };\n}\nerrnos!(def_errno);\n\nimpl Errno {\n    /// Returns a description of the errno value, if any.\n    pub const fn message(self) -> Option<&'static str> {\n        errno::strerror(self.0)\n    }\n\n    /// Converts the given `code` to an error number in `Errno`'s representation.\n    #[inline]\n    pub const fn from_code(code: u16) -> Option<Self> {\n        match NonZeroU16::new(code) {\n            Some(code) => Some(Errno(code)),\n            None => None,\n        }\n    }\n\n    /// Converts this `errno` into a primitive error code.\n    #[inline]\n    pub const fn code(self) -> u16 {\n        self.0.get()\n    }\n}\n\nimpl fmt::Debug for Errno {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let mut fmt = f.debug_struct(\"Errno\");\n        fmt.field(\"code\", &self.code());\n        if let Some(msg) = self.message() {\n            fmt.field(\"message\", &msg);\n        }\n        fmt.finish()\n    }\n}\n\nimpl fmt::Display for Errno {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let message = self.message().unwrap_or(\"Unknown error\");\n        write!(f, \"{message} (error {})\", self.code())\n    }\n}\n\n/// Convert the status value `x` into a result.\n/// When `x = 0`, we have a success status.\nfn cvt(x: u16) -> Result<()> {\n    match Errno::from_code(x) {\n        None => Ok(()),\n        Some(err) => Err(err),\n    }\n}\n\n/// Runs the given function `f` provided with an uninitialized `out` pointer.\n///\n/// Assuming the call to `f` succeeds (`Ok(_)`), the `out` pointer's value is returned.\n///\n/// # Safety\n///\n/// This function is safe to call, if and only if,\n/// - The function `f` writes a safe and valid `T` to the `out` pointer.\n///   It's not required to write to `out` when `f(out)` returns an error code.\n/// - The function `f` never reads a safe and valid `T` from the `out` pointer\n///   before writing a safe and valid `T` to it.\n#[inline]\nunsafe fn call<T: Copy>(f: impl FnOnce(*mut T) -> u16) -> Result<T> {\n    unsafe {\n        let mut out = MaybeUninit::uninit();\n        let f_code = f(out.as_mut_ptr());\n        cvt(f_code)?;\n        Ok(out.assume_init())\n    }\n}\n\n/// Runs the given function `f`.\n///\n/// Assuming the call to `f` returns 0, `Ok(())` is returned,\n/// and otherwise `Err(err)` is returned.\n#[inline]\n#[cfg(feature = \"unstable\")]\nfn call_no_ret(f: impl FnOnce() -> u16) -> Result<()> {\n    let f_code = f();\n    cvt(f_code)?;\n    Ok(())\n}\n\n/// Queries the `table_id` associated with the given (table) `name`.\n///\n/// The table id is returned.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_TABLE`, when `name` is not the name of a table.\n#[inline]\npub fn table_id_from_name(name: &str) -> Result<TableId> {\n    unsafe { call(|out| raw::table_id_from_name(name.as_ptr(), name.len(), out)) }\n}\n\n/// Queries the `index_id` associated with the given (index) `name`.\n///\n/// The index id is returned.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_INDEX`, when `name` is not the name of an index.\n#[inline]\npub fn index_id_from_name(name: &str) -> Result<IndexId> {\n    unsafe { call(|out| raw::index_id_from_name(name.as_ptr(), name.len(), out)) }\n}\n\n/// Returns the number of rows currently in table identified by `table_id`.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n#[inline]\npub fn datastore_table_row_count(table_id: TableId) -> Result<u64> {\n    unsafe { call(|out| raw::datastore_table_row_count(table_id, out)) }\n}\n\n/// Inserts a row into the table identified by `table_id`,\n/// where the row is a BSATN-encoded `ProductValue`\n/// matching the table's `ProductType` row-schema.\n///\n/// The `row` is `&mut` due to auto-incrementing columns.\n/// So `row` is written to with the inserted row re-encoded.\n///\n/// Returns an error if\n/// - a table with the provided `table_id` doesn't exist\n/// - there were unique constraint violations\n/// - `row` doesn't decode from BSATN to a `ProductValue`\n///   according to the `ProductType` that the table's schema specifies.\n#[inline]\npub fn datastore_insert_bsatn(table_id: TableId, row: &mut [u8]) -> Result<&[u8]> {\n    let row_ptr = row.as_mut_ptr();\n    let row_len = &mut row.len();\n    cvt(unsafe { raw::datastore_insert_bsatn(table_id, row_ptr, row_len) }).map(|()| &row[..*row_len])\n}\n\n/// Updates a row into the table identified by `table_id`,\n/// where the row is a BSATN-encoded `ProductValue`\n/// matching the table's `ProductType` row-schema.\n///\n/// The row to update is found by projecting `row`\n/// to the type of the *unique* index identified by `index_id`.\n/// If no row is found, `row` is inserted.\n///\n/// The `row` is `&mut` due to auto-incrementing columns.\n/// So `row` is written to with the updated row re-encoded.\n///\n/// Returns an error if\n/// - a table with the provided `table_id` doesn't exist\n/// - an index with the provided `index_id` doesn't exist or if the index was not unique.\n/// - there were unique constraint violations\n/// - `row` doesn't decode from BSATN to a `ProductValue`\n///   according to the `ProductType` that the table's schema specifies\n///   or if `row` cannot project to the index's type.\n/// - the row was not found\n#[inline]\npub fn datastore_update_bsatn(table_id: TableId, index_id: IndexId, row: &mut [u8]) -> Result<&[u8]> {\n    let row_ptr = row.as_mut_ptr();\n    let row_len = &mut row.len();\n    cvt(unsafe { raw::datastore_update_bsatn(table_id, index_id, row_ptr, row_len) }).map(|()| &row[..*row_len])\n}\n\n/// Deletes those rows, in the table identified by `table_id`,\n/// that match any row in the byte string `relation`.\n///\n/// Matching is defined by first BSATN-decoding\n/// the byte string pointed to at by `relation` to a `Vec<ProductValue>`\n/// according to the row schema of the table\n/// and then using `Ord for AlgebraicValue`.\n/// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n/// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.\n///\n/// The number of rows deleted is returned.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n/// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`\n///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.\n#[inline]\npub fn datastore_delete_all_by_eq_bsatn(table_id: TableId, relation: &[u8]) -> Result<u32> {\n    unsafe { call(|out| raw::datastore_delete_all_by_eq_bsatn(table_id, relation.as_ptr(), relation.len(), out)) }\n}\n\n/// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.\n/// Returns iterator handle is written to the `out` pointer.\n/// This handle can be advanced by [`RowIter::read`].\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\npub fn datastore_table_scan_bsatn(table_id: TableId) -> Result<RowIter> {\n    let raw = unsafe { call(|out| raw::datastore_table_scan_bsatn(table_id, out))? };\n    Ok(RowIter { raw })\n}\n\n/// Finds all rows in the index identified by `index_id`,\n/// according to the `point.\n///\n/// The index itself has a schema/type.\n/// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`\n/// and then comparing the decoded `point` to the keys in the index\n/// using `Ord for AlgebraicValue`.\n/// to the keys in the index.\n/// The `point` is BSATN-decoded to that `AlgebraicType`.\n/// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n/// This occurs exactly when the row's BSATN-encoding\n/// is equal to the encoding of the `AlgebraicValue`.\n///\n/// This ABI is not limited to single column indices.\n/// Multi-column indices can be queried by providing\n/// a BSATN-encoded `ProductValue`\n/// that is typed at the `ProductType` of the index.\n///\n/// The relevant table for the index is found implicitly via the `index_id`,\n/// which is unique for the module.\n///\n/// On success, the iterator handle is written to the `out` pointer.\n/// This handle can be advanced by [`RowIter::read`].\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n///   typed at the index's key type (`AlgebraicType`).\npub fn datastore_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<RowIter> {\n    let raw = unsafe { call(|out| raw::datastore_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out))? };\n    Ok(RowIter { raw })\n}\n\n/// Finds all rows in the index identified by `index_id`,\n/// according to the `prefix`, `rstart`, and `rend`.\n///\n/// The index itself has a schema/type.\n/// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s\n/// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`\n/// where the `AlgebraicValue`s are wrapped in `Bound`.\n/// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.\n///\n/// Matching is then defined by equating `prefix`\n/// to the initial `prefix_elems` columns of the index\n/// and then imposing `rstart` as the starting bound\n/// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.\n/// Remaining columns of the index are then unbounded.\n/// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),\n/// in which case this becomes a ranged index scan on a single-col index\n/// or even a full table scan if `rstart` and `rend` are both unbounded.\n///\n/// The relevant table for the index is found implicitly via the `index_id`,\n/// which is unique for the module.\n///\n/// On success, the iterator handle is written to the `out` pointer.\n/// This handle can be advanced by [`RowIter::read`].\n///\n/// # Non-obvious queries\n///\n/// For an index on columns `[a, b, c]`:\n///\n/// - `a = x, b = y` is encoded as a prefix `[x, y]`\n///   and a range `Range::Unbounded`,\n///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.\n/// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`\n///   and a  range `rstart = rend = Range::Inclusive(z)`.\n/// - A sorted full scan is encoded as an empty prefix\n///   and a range `Range::Unbounded`.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\npub fn datastore_index_scan_range_bsatn(\n    index_id: IndexId,\n    prefix: &[u8],\n    prefix_elems: ColId,\n    rstart: &[u8],\n    rend: &[u8],\n) -> Result<RowIter> {\n    let raw = unsafe {\n        call(|out| {\n            raw::datastore_index_scan_range_bsatn(\n                index_id,\n                prefix.as_ptr(),\n                prefix.len(),\n                prefix_elems,\n                rstart.as_ptr(),\n                rstart.len(),\n                rend.as_ptr(),\n                rend.len(),\n                out,\n            )\n        })?\n    };\n    Ok(RowIter { raw })\n}\n\n/// Deletes all rows found in the index identified by `index_id`,\n/// according to the `point.\n///\n/// This syscall will delete all the rows found by\n/// [`datastore_index_scan_point_bsatn`] with the same arguments passed.\n/// See `datastore_index_scan_point_bsatn` for details.\n///\n/// The number of rows deleted is returned on success.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n///   typed at the index's key type (`AlgebraicType`).\npub fn datastore_delete_by_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<u32> {\n    unsafe { call(|out| raw::datastore_delete_by_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out)) }\n}\n\n/// Deletes all rows found in the index identified by `index_id`,\n/// according to the `prefix`, `rstart`, and `rend`.\n///\n/// This syscall will delete all the rows found by\n/// [`datastore_index_scan_range_bsatn`] with the same arguments passed,\n/// including `prefix_elems`.\n/// See `datastore_index_scan_range_bsatn` for details.\n///\n/// The number of rows deleted is returned on success.\n///\n/// # Errors\n///\n/// Returns an error:\n///\n/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\npub fn datastore_delete_by_index_scan_range_bsatn(\n    index_id: IndexId,\n    prefix: &[u8],\n    prefix_elems: ColId,\n    rstart: &[u8],\n    rend: &[u8],\n) -> Result<u32> {\n    unsafe {\n        call(|out| {\n            raw::datastore_delete_by_index_scan_range_bsatn(\n                index_id,\n                prefix.as_ptr(),\n                prefix.len(),\n                prefix_elems,\n                rstart.as_ptr(),\n                rstart.len(),\n                rend.as_ptr(),\n                rend.len(),\n                out,\n            )\n        })\n    }\n}\n\n/// A log level that can be used in `console_log`.\n/// The variants are convertible into a raw `u8` log level.\n#[repr(u8)]\npub enum LogLevel {\n    /// The error log level. See [`console_log`].\n    Error = raw::LOG_LEVEL_ERROR,\n    /// The warn log level. See [`console_log`].\n    Warn = raw::LOG_LEVEL_WARN,\n    /// The info log level. See [`console_log`].\n    Info = raw::LOG_LEVEL_INFO,\n    /// The debug log level. See [`console_log`].\n    Debug = raw::LOG_LEVEL_DEBUG,\n    /// The trace log level. See [`console_log`].\n    Trace = raw::LOG_LEVEL_TRACE,\n    /// The panic log level. See [`console_log`].\n    ///\n    /// A panic level is emitted just before a fatal error causes the WASM module to trap.\n    Panic = raw::LOG_LEVEL_PANIC,\n}\n\n/// Log at `level` a `text` message occurring in `filename:line_number`\n/// with [`target`] being the module path at the `log!` invocation site.\n///\n/// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target\n#[inline]\npub fn console_log(\n    level: LogLevel,\n    target: Option<&str>,\n    filename: Option<&str>,\n    line_number: Option<u32>,\n    text: &str,\n) {\n    let opt_ptr = |b: Option<&str>| b.map_or(ptr::null(), |b| b.as_ptr());\n    let opt_len = |b: Option<&str>| b.map_or(0, |b| b.len());\n    unsafe {\n        raw::console_log(\n            level as u8,\n            opt_ptr(target),\n            opt_len(target),\n            opt_ptr(filename),\n            opt_len(filename),\n            line_number.unwrap_or(u32::MAX),\n            text.as_ptr(),\n            text.len(),\n        )\n    }\n}\n\n/// Schedule a reducer to be called asynchronously, nonatomically, and immediately\n/// on a best-effort basis.\n///\n/// The reducer is assigned `name` and is provided `args` as its argument.\n#[cfg(feature = \"unstable\")]\n#[inline]\npub fn volatile_nonatomic_schedule_immediate(name: &str, args: &[u8]) {\n    unsafe { raw::volatile_nonatomic_schedule_immediate(name.as_ptr(), name.len(), args.as_ptr(), args.len()) }\n}\n\n/// Read the current module's identity, as a little-endian byte array.\n///\n/// Doesn't return a proper typed `Identity` because this crate doesn't depend on `spacetimedb_lib`.\n#[inline]\npub fn identity() -> [u8; 32] {\n    let mut buf = [0u8; 32];\n    unsafe {\n        raw::identity(buf.as_mut_ptr());\n    }\n    buf\n}\n\n/// Finds the JWT payload associated with `connection_id`.\n/// If nothing is found for the connection, this returns None.\n/// If a payload is found, this will return a valid [`raw::BytesSource`].\n///\n/// This must be called inside a transaction (because it reads from a system table).\n///\n/// # Errors\n///\n/// This panics on any error. You can see details about errors in [`raw::get_jwt`].\n#[inline]\npub fn get_jwt(connection_id: [u8; 16]) -> Option<raw::BytesSource> {\n    let source = unsafe {\n        call(|out| raw::get_jwt(connection_id.as_ptr(), out))\n            .unwrap_or_else(|errno: Errno| panic!(\"Error getting jwt: {errno}\"))\n    };\n\n    if source == raw::BytesSource::INVALID {\n        None // No JWT found.\n    } else {\n        Some(source)\n    }\n}\n\npub struct RowIter {\n    raw: raw::RowIter,\n}\n\nimpl RowIter {\n    /// Read some number of BSATN-encoded rows into the provided buffer.\n    ///\n    /// Returns the number of new bytes added to the end of the buffer.\n    /// When the iterator has been exhausted,\n    /// `self.is_exhausted()` will return `true`.\n    pub fn read(&mut self, buf: &mut Vec<u8>) -> usize {\n        loop {\n            let buf_ptr = buf.spare_capacity_mut();\n            let mut buf_len = buf_ptr.len();\n            let ret = unsafe { raw::row_iter_bsatn_advance(self.raw, buf_ptr.as_mut_ptr().cast(), &mut buf_len) };\n            if let -1 | 0 = ret {\n                // SAFETY: `_row_iter_bsatn_advance` just wrote `buf_len` bytes into the end of `buf`.\n                unsafe { buf.set_len(buf.len() + buf_len) };\n            }\n\n            const TOO_SMALL: i16 = errno::BUFFER_TOO_SMALL.get() as i16;\n            match ret {\n                -1 => {\n                    self.raw = raw::RowIter::INVALID;\n                    return buf_len;\n                }\n                0 => return buf_len,\n                TOO_SMALL => buf.reserve(buf_len),\n                e => panic!(\"unexpected error from `_row_iter_bsatn_advance`: {e}\"),\n            }\n        }\n    }\n\n    /// Returns whether the iterator is exhausted or not.\n    pub fn is_exhausted(&self) -> bool {\n        self.raw == raw::RowIter::INVALID\n    }\n}\n\nimpl Drop for RowIter {\n    fn drop(&mut self) {\n        // Avoid this syscall when `_row_iter_bsatn_advance` above\n        // notifies us that the iterator is exhausted.\n        if self.is_exhausted() {\n            return;\n        }\n        unsafe {\n            raw::row_iter_bsatn_close(self.raw);\n        }\n    }\n}\n\n#[cfg(feature = \"unstable\")]\npub mod procedure {\n    //! Side-effecting or asynchronous operations which only procedures are allowed to perform.\n\n    use super::{call, call_no_ret, raw, Result};\n\n    #[inline]\n    pub fn sleep_until(wake_at_timestamp: i64) -> i64 {\n        // Safety: Just calling an `extern \"C\"` function.\n        // Nothing weird happening here.\n        unsafe { raw::procedure_sleep_until(wake_at_timestamp) }\n    }\n\n    /// Starts a mutable transaction,\n    /// blocking until a mutable transaction lock is acquired.\n    ///\n    /// Once complete, returns `Ok(timestamp)` on success,\n    /// enabling further calls that require a pending transaction,\n    /// or [`Errno`] otherwise.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.\n    #[inline]\n    pub fn procedure_start_mut_tx() -> Result<i64> {\n        unsafe { call(|out| raw::procedure_start_mut_tx(out)) }\n    }\n\n    /// Commits a mutable transaction,\n    /// blocking until the transaction has been committed\n    /// and subscription queries have been run and broadcast.\n    ///\n    /// Once complete, returns `Ok(())` on success, or an [`Errno`] otherwise.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `TRANSACTION_NOT_ANONYMOUS`,\n    ///   if the transaction was not started in [`procedure_start_mut_tx`].\n    ///   This can happen if this syscall is erroneously called by a reducer.\n    ///   The code `NOT_IN_TRANSACTION` does not happen,\n    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n    ///   This currently does not happen as anonymous read transactions\n    ///   are not exposed to modules.\n    #[inline]\n    pub fn procedure_commit_mut_tx() -> Result<()> {\n        call_no_ret(|| unsafe { raw::procedure_commit_mut_tx() })\n    }\n\n    /// Aborts a mutable transaction,\n    /// blocking until the transaction has been rolled back.\n    ///\n    /// Once complete, returns `Ok(())` on success, or an [`Errno`] otherwise.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `TRANSACTION_NOT_ANONYMOUS`,\n    ///   if the transaction was not started in [`procedure_start_mut_tx`].\n    ///   This can happen if this syscall is erroneously called by a reducer.\n    ///   The code `NOT_IN_TRANSACTION` does not happen,\n    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n    ///   This currently does not happen as anonymous read transactions\n    ///   are not exposed to modules.\n    #[inline]\n    pub fn procedure_abort_mut_tx() -> Result<()> {\n        call_no_ret(|| unsafe { raw::procedure_abort_mut_tx() })\n    }\n\n    #[inline]\n    #[cfg(feature = \"unstable\")]\n    /// Perform an HTTP request as specified by `http_request_bsatn`,\n    /// suspending execution until the request is complete,\n    /// then return its response or error.\n    ///\n    /// `http_request_bsatn` should be a BSATN-serialized `spacetimedb_lib::http::Request`.\n    ///\n    /// If the request completes successfully,\n    /// this function returns `Ok(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Response`.\n    /// All HTTP response codes are treated as successful for these purposes;\n    /// this method only returns an error if it is unable to produce any HTTP response whatsoever.\n    /// In that case, this function returns `Err(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Error`.\n    pub fn http_request(\n        http_request_bsatn: &[u8],\n        body: &[u8],\n    ) -> Result<(raw::BytesSource, raw::BytesSource), raw::BytesSource> {\n        let mut out = [raw::BytesSource::INVALID; 2];\n\n        let res = unsafe {\n            super::raw::procedure_http_request(\n                http_request_bsatn.as_ptr(),\n                http_request_bsatn.len() as u32,\n                body.as_ptr(),\n                body.len() as u32,\n                &mut out as *mut [raw::BytesSource; 2],\n            )\n        };\n\n        match super::Errno::from_code(res) {\n            // Success: `out` is a `spacetimedb_lib::http::Response`.\n            None => Ok((out[0], out[1])),\n            // HTTP_ERROR: `out` is a `spacetimedb_lib::http::Error`.\n            Some(errno) if errno == super::Errno::HTTP_ERROR => Err(out[0]),\n            Some(errno) => panic!(\"{errno}\"),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/.editorconfig",
    "content": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.ts]\nquote_type = single\nij_typescript_use_double_quotes = false\n\n[*.md]\nmax_line_length = off\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "crates/bindings-typescript/.gitattributes",
    "content": "src/sdk/client_api/*.ts linguist-generated=true\nsrc/lib/autogen/*.ts linguist-generated=true\ntest-app/src/module_bindings/*.ts linguist-generated=true\nexamples/quickstart/client/src/module_bindings/*.ts linguist-generated=true\n"
  },
  {
    "path": "crates/bindings-typescript/.gitignore",
    "content": "node_modules/\n.envrc\ndist/\n.DS_Store"
  },
  {
    "path": "crates/bindings-typescript/.npmignore",
    "content": "tests\n.envrc\nnode_modules\n"
  },
  {
    "path": "crates/bindings-typescript/DEVELOP.md",
    "content": "# Notes for maintainers\n\nThe directory `src/sdk/client_api` is generated from [the SpacetimeDB client-api-messages](https://github.com/clockworklabs/SpacetimeDB/tree/master/crates/client-api-messages).\n\nThe directory `src/lib/autogen` is generated from the SpacetimeDB `ModuleDef` definition using the `regen-typescript-moduledef` Rust program.\n\nIn order to regenerate both of these bindings, run `pnpm generate`.\n\nWhenever the `client-api-messages` crate or the `ModuleDef` changes, you'll have to manually re-generate the definitions.\n\n## Releases and publishing\n\nIn order to release and publish a new version of the package, update the version and run `npm publish`.\n"
  },
  {
    "path": "crates/bindings-typescript/README.md",
    "content": "## SpacetimeDB Module Library and SDK\n\n### Overview\n\nThis repository contains both the SpacetimeDB module library and the TypeScript SDK for SpacetimeDB. The SDK allows you to interact with the database server from a client and applies type information from your SpacetimeDB server module.\n\n### Installation\n\nThe SDK is an NPM package, thus you can use your package manager of choice like NPM or Yarn, for example:\n\n```\nnpm add spacetimedb\n```\n\nYou can use the package in the browser, using a bundler like vite/parcel/rsbuild, in server-side applications like NodeJS, Deno, Bun, NextJS, Remix, and in Cloudflare Workers.\n\n> NOTE: For usage in NodeJS 18-21, you need to install the `undici` package as a peer dependency: `npm add spacetimedb undici`. Node 22 and later are supported out of the box.\n\n### Usage\n\nIn order to connect to a database you have to generate module bindings for your database.\n\n```ts\nimport { DbConnection, tables } from './module_bindings';\n\nconst connection = DbConnection.builder()\n  .withUri('ws://localhost:3000')\n  .withDatabaseName('MODULE_NAME')\n  .onDisconnect(() => {\n    console.log('disconnected');\n  })\n  .onConnectError(() => {\n    console.log('client_error');\n  })\n  .onConnect((connection, identity, _token) => {\n    console.log(\n      'Connected to SpacetimeDB with identity:',\n      identity.toHexString()\n    );\n\n    connection.subscriptionBuilder().subscribe(tables.player);\n  })\n  .withToken('TOKEN')\n  .build();\n```\n\nIf you need to disconnect the client:\n\n```ts\nconnection.disconnect();\n```\n\nTypically, you will use the SDK with types generated from SpacetimeDB module. For example, given a table named `Player` you can subscribe to player updates like this:\n\n```ts\nconnection.db.player.onInsert((ctx, player) => {\n  console.log(player);\n});\n```\n\nGiven a reducer called `CreatePlayer` you can call it using a call method:\n\n```ts\nconnection.reducers.createPlayer();\n```\n\n#### React Usage\n\nThis module also includes React hooks to subscribe to tables under the `spacetimedb/react` subpath. The React integration is fully compatible with React StrictMode and handles the double-mount behavior correctly (only one WebSocket connection is created).\n\nIn order to use SpacetimeDB React hooks in your project, first add a `SpacetimeDBProvider` at the top of your component hierarchy:\n\n```tsx\nconst connectionBuilder = DbConnection.builder()\n  .withUri('ws://localhost:3000')\n  .withDatabaseName('MODULE_NAME')\n  .withLightMode(true)\n  .onDisconnect(() => {\n    console.log('disconnected');\n  })\n  .onConnectError(() => {\n    console.log('client_error');\n  })\n  .onConnect((conn, identity, _token) => {\n    console.log(\n      'Connected to SpacetimeDB with identity:',\n      identity.toHexString()\n    );\n\n    conn.subscriptionBuilder().subscribe(tables.player);\n  })\n  .withToken('TOKEN');\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <SpacetimeDBProvider connectionBuilder={connectionBuilder}>\n      <App />\n    </SpacetimeDBProvider>\n  </React.StrictMode>\n);\n```\n\nOne you add a `SpacetimeDBProvider` to your hierarchy, you can use SpacetimeDB React hooks in your render function:\n\n```tsx\nfunction App() {\n  const conn = useSpacetimeDB<DbConnection>();\n  const { rows: messages } = useTable<DbConnection, Message>('message');\n\n  ...\n}\n```\n\n### Developer notes\n\nTo run the tests, do:\n\n```sh\npnpm build && pnpm test\n```\n"
  },
  {
    "path": "crates/bindings-typescript/package.json",
    "content": "{\n  \"name\": \"spacetimedb\",\n  \"version\": \"2.0.4\",\n  \"description\": \"API and ABI bindings for the SpacetimeDB TypeScript module library\",\n  \"homepage\": \"https://github.com/clockworklabs/SpacetimeDB#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/clockworklabs/SpacetimeDB/issues\"\n  },\n  \"files\": [\n    \"src\",\n    \"dist\",\n    \"README.md\",\n    \"LICENSE.txt\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/clockworklabs/SpacetimeDB.git\"\n  },\n  \"license\": \"ISC\",\n  \"author\": \"Clockwork Labs\",\n  \"type\": \"module\",\n  \"sideEffects\": [\n    \"./src/server/polyfills.ts\"\n  ],\n  \"scripts\": {\n    \"build:js\": \"tsup\",\n    \"build:types\": \"tsc -p tsconfig.build.json\",\n    \"build\": \"pnpm -s build:js && pnpm -s build:types\",\n    \"format\": \"prettier . --write --ignore-path ../../.prettierignore\",\n    \"lint\": \"eslint . && prettier . --check --ignore-path ../../.prettierignore\",\n    \"test\": \"vitest run\",\n    \"test:typecheck\": \"vitest typecheck --run\",\n    \"coverage\": \"vitest run --coverage\",\n    \"brotli-size\": \"brotli-size dist/index.js\",\n    \"size\": \"pnpm -s build && size-limit\",\n    \"generate:moduledef\": \"cargo run -p spacetimedb-codegen --example regen-typescript-moduledef && prettier --write src/lib/autogen\",\n    \"generate:client-api\": \"cargo run -p generate-client-api && prettier --write src/sdk/client_api\",\n    \"generate:test-app\": \"pnpm --filter @clockworklabs/test-app generate\",\n    \"generate:examples:chat-react-ts\": \"pnpm --filter @clockworklabs/chat-react-ts generate\",\n    \"generate:examples:react-ts\": \"pnpm --filter @clockworklabs/react-ts generate\",\n    \"generate:examples:empty\": \"pnpm --filter @clockworklabs/empty-client generate\",\n    \"generate\": \"pnpm generate:moduledef && pnpm generate:client-api && pnpm generate:test-app && pnpm generate:examples:chat-react-ts && pnpm generate:examples:react-ts && pnpm generate:examples:empty\",\n    \"prepublishOnly\": \"pnpm run build && pnpm run test && pnpm run size\"\n  },\n  \"main\": \"dist/index.cjs\",\n  \"module\": \"dist/index.mjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"browser\": \"./dist/index.browser.mjs\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.cjs\",\n      \"default\": \"./dist/index.mjs\"\n    },\n    \"./sdk\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"browser\": \"./dist/sdk/index.browser.mjs\",\n      \"import\": \"./dist/sdk/index.mjs\",\n      \"require\": \"./dist/sdk/index.cjs\",\n      \"default\": \"./dist/sdk/index.mjs\"\n    },\n    \"./react\": {\n      \"types\": \"./dist/react/index.d.ts\",\n      \"import\": \"./dist/react/index.mjs\",\n      \"require\": \"./dist/react/index.cjs\",\n      \"default\": \"./dist/react/index.mjs\"\n    },\n    \"./server\": {\n      \"types\": \"./dist/server/index.d.ts\",\n      \"import\": \"./dist/server/index.mjs\",\n      \"require\": \"./dist/server/index.cjs\",\n      \"default\": \"./dist/server/index.mjs\"\n    },\n    \"./vue\": {\n      \"types\": \"./dist/vue/index.d.ts\",\n      \"import\": \"./dist/vue/index.mjs\",\n      \"require\": \"./dist/vue/index.cjs\",\n      \"default\": \"./dist/vue/index.mjs\"\n    },\n    \"./tanstack\": {\n      \"types\": \"./dist/tanstack/index.d.ts\",\n      \"import\": \"./dist/tanstack/index.mjs\",\n      \"require\": \"./dist/tanstack/index.cjs\",\n      \"default\": \"./dist/tanstack/index.mjs\"\n    },\n    \"./svelte\": {\n      \"types\": \"./dist/svelte/index.d.ts\",\n      \"import\": \"./dist/svelte/index.mjs\",\n      \"require\": \"./dist/svelte/index.cjs\",\n      \"default\": \"./dist/svelte/index.mjs\"\n    },\n    \"./angular\": {\n      \"types\": \"./dist/angular/index.d.ts\",\n      \"import\": \"./dist/angular/index.mjs\",\n      \"require\": \"./dist/angular/index.cjs\",\n      \"default\": \"./dist/angular/index.mjs\"\n    }\n  },\n  \"size-limit\": [\n    {\n      \"name\": \"cjs (brotli)\",\n      \"path\": \"dist/index.cjs\",\n      \"brotli\": true,\n      \"limit\": \"54 kB\"\n    },\n    {\n      \"name\": \"esm (brotli)\",\n      \"path\": \"dist/index.mjs\",\n      \"brotli\": true,\n      \"limit\": \"62 kB\"\n    },\n    {\n      \"name\": \"esm (gzip)\",\n      \"path\": \"dist/index.mjs\",\n      \"gzip\": true,\n      \"limit\": \"40 kB\"\n    },\n    {\n      \"name\": \"esm (uncompressed)\",\n      \"path\": \"dist/index.mjs\",\n      \"brotli\": false,\n      \"limit\": \"328 kB\"\n    },\n    {\n      \"name\": \"esm min (brotli)\",\n      \"path\": \"dist/min/index.browser.mjs\",\n      \"brotli\": true,\n      \"limit\": \"30 kB\"\n    },\n    {\n      \"name\": \"esm min (gzip)\",\n      \"path\": \"dist/min/index.browser.mjs\",\n      \"gzip\": true,\n      \"limit\": \"34 kB\"\n    },\n    {\n      \"name\": \"esm min (uncompressed)\",\n      \"path\": \"dist/min/index.browser.mjs\",\n      \"brotli\": false,\n      \"limit\": \"134 kB\"\n    },\n    {\n      \"name\": \"react esm min (brotli)\",\n      \"path\": \"dist/min/react/index.mjs\",\n      \"brotli\": true,\n      \"limit\": \"10 kB\"\n    },\n    {\n      \"name\": \"react esm min (uncompressed)\",\n      \"path\": \"dist/min/react/index.mjs\",\n      \"limit\": \"10 kB\"\n    },\n    {\n      \"name\": \"react esm min (gzip)\",\n      \"path\": \"dist/min/react/index.mjs\",\n      \"gzip\": true,\n      \"limit\": \"15 kB\"\n    },\n    {\n      \"name\": \"sdk esm min (brotli)\",\n      \"path\": \"dist/min/sdk/index.browser.mjs\",\n      \"brotli\": true,\n      \"limit\": \"30 kB\"\n    },\n    {\n      \"name\": \"sdk esm min (gzip)\",\n      \"path\": \"dist/min/sdk/index.browser.mjs\",\n      \"gzip\": true,\n      \"limit\": \"32 kB\"\n    },\n    {\n      \"name\": \"sdk esm min (uncompressed)\",\n      \"path\": \"dist/min/sdk/index.browser.mjs\",\n      \"limit\": \"30 kB\"\n    }\n  ],\n  \"dependencies\": {\n    \"base64-js\": \"^1.5.1\",\n    \"headers-polyfill\": \"^4.0.3\",\n    \"object-inspect\": \"^1.13.4\",\n    \"prettier\": \"^3.3.3\",\n    \"pure-rand\": \"^7.0.1\",\n    \"safe-stable-stringify\": \"^2.5.0\",\n    \"statuses\": \"^2.0.2\",\n    \"url-polyfill\": \"^1.1.14\"\n  },\n  \"peerDependencies\": {\n    \"@angular/core\": \">=17.0.0\",\n    \"@tanstack/react-query\": \"^5.0.0\",\n    \"react\": \"^18.0.0 || ^19.0.0-0 || ^19.0.0\",\n    \"svelte\": \"^4.0.0 || ^5.0.0\",\n    \"undici\": \"^6.19.2\",\n    \"vue\": \"^3.3.0\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@tanstack/react-query\": {\n      \"optional\": true\n    },\n    \"react\": {\n      \"optional\": true\n    },\n    \"svelte\": {\n      \"optional\": true\n    },\n    \"undici\": {\n      \"optional\": true\n    },\n    \"vue\": {\n      \"optional\": true\n    },\n    \"@angular/core\": {\n      \"optional\": true\n    }\n  },\n  \"devDependencies\": {\n    \"@angular/compiler\": \"^21.1.0\",\n    \"@angular/core\": \"^21.1.0\",\n    \"@eslint/js\": \"^9.17.0\",\n    \"@size-limit/file\": \"^11.2.0\",\n    \"@tanstack/react-query\": \"^5.90.19\",\n    \"@types/fast-text-encoding\": \"^1.0.3\",\n    \"@types/object-inspect\": \"^1.13.0\",\n    \"@types/react\": \"^19.1.13\",\n    \"@types/statuses\": \"^2.0.6\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.18.2\",\n    \"@typescript-eslint/parser\": \"^8.18.2\",\n    \"@vitest/coverage-v8\": \"^3.2.4\",\n    \"brotli-size-cli\": \"^1.0.0\",\n    \"eslint\": \"^9.33.0\",\n    \"eslint-plugin-jsdoc\": \"^61.5.0\",\n    \"globals\": \"^15.14.0\",\n    \"size-limit\": \"^11.2.0\",\n    \"svelte\": \"^5.0.0\",\n    \"ts-node\": \"^10.9.2\",\n    \"tsup\": \"^8.1.0\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.18.2\",\n    \"vite\": \"^7.1.5\",\n    \"vitest\": \"^3.2.4\"\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/connection_state.ts",
    "content": "import { InjectionToken, type WritableSignal } from '@angular/core';\nimport type { ConnectionId } from '../lib/connection_id';\nimport type { Identity } from '../lib/identity';\nimport type { DbConnectionImpl } from '../sdk/db_connection_impl';\n\nexport interface ConnectionState {\n  isActive: boolean;\n  identity?: Identity;\n  token?: string;\n  connectionId: ConnectionId;\n  connectionError?: Error;\n  getConnection<\n    DbConnection extends DbConnectionImpl<any>,\n  >(): DbConnection | null;\n}\n\nexport const SPACETIMEDB_CONNECTION = new InjectionToken<\n  WritableSignal<ConnectionState>\n>('SpacetimeDB Connection State');\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/index.ts",
    "content": "export type { ConnectionState } from './connection_state.ts';\nexport * from './injectors/index.ts';\nexport * from './providers/index.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/injectors/index.ts",
    "content": "export { injectSpacetimeDB } from './inject-spacetimedb.ts';\nexport { injectTable, type TableRows } from './inject-table.ts';\nexport { injectSpacetimeDBConnected } from './inject-spacetimedb-connected.ts';\nexport { injectReducer } from './inject-reducer.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/injectors/inject-reducer.ts",
    "content": "import { assertInInjectionContext, inject, effect } from '@angular/core';\nimport { SPACETIMEDB_CONNECTION } from '../connection_state';\nimport type { ParamsType } from '../../sdk';\nimport type { UntypedReducerDef } from '../../sdk/reducers';\n\nexport function injectReducer<ReducerDef extends UntypedReducerDef>(\n  reducerDef: ReducerDef\n): (...params: ParamsType<ReducerDef>) => void {\n  assertInInjectionContext(injectReducer);\n\n  const connState = inject(SPACETIMEDB_CONNECTION);\n  const queue: ParamsType<ReducerDef>[] = [];\n  const reducerName = reducerDef.accessorName;\n\n  // flush queued calls when connection becomes active\n  effect((onCleanup: (fn: () => void) => void) => {\n    const state = connState();\n    if (!state.isActive) {\n      return;\n    }\n\n    const connection = state.getConnection();\n    if (!connection) {\n      return;\n    }\n\n    const callReducer = (connection.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => void;\n\n    if (queue.length) {\n      const pending = queue.splice(0);\n      for (const params of pending) {\n        callReducer(...params);\n      }\n    }\n\n    onCleanup(() => {\n      queue.splice(0);\n    });\n  });\n\n  return (...params: ParamsType<ReducerDef>) => {\n    const state = connState();\n    if (!state.isActive) {\n      queue.push(params);\n      return;\n    }\n\n    const connection = state.getConnection();\n    if (!connection) {\n      queue.push(params);\n      return;\n    }\n\n    const callReducer = (connection.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => void;\n\n    return callReducer(...params);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/injectors/inject-spacetimedb-connected.ts",
    "content": "import {\n  assertInInjectionContext,\n  inject,\n  computed,\n  type Signal,\n} from '@angular/core';\nimport { SPACETIMEDB_CONNECTION } from '../connection_state';\n\nexport function injectSpacetimeDBConnected(): Signal<boolean> {\n  assertInInjectionContext(injectSpacetimeDBConnected);\n  const state = inject(SPACETIMEDB_CONNECTION);\n  return computed(() => state().isActive);\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/injectors/inject-spacetimedb.ts",
    "content": "import { assertInInjectionContext, inject, type Signal } from '@angular/core';\nimport {\n  SPACETIMEDB_CONNECTION,\n  type ConnectionState,\n} from '../connection_state';\n\nexport function injectSpacetimeDB(): Signal<ConnectionState> {\n  assertInInjectionContext(injectSpacetimeDB);\n  return inject(SPACETIMEDB_CONNECTION).asReadonly();\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/injectors/inject-table.ts",
    "content": "import {\n  assertInInjectionContext,\n  inject,\n  signal,\n  effect,\n  type Signal,\n} from '@angular/core';\nimport type { RowType, UntypedTableDef } from '../../lib/table';\nimport type { Prettify } from '../../lib/type_util';\nimport { SPACETIMEDB_CONNECTION } from '../connection_state';\nimport {\n  type Query,\n  type BooleanExpr,\n  toSql,\n  evaluateBooleanExpr,\n  getQueryAccessorName,\n  getQueryWhereClause,\n} from '../../lib/query';\nimport type { EventContextInterface } from '../../sdk';\nimport type { UntypedRemoteModule } from '../../sdk/spacetime_module';\n\nexport type RowTypeDef<TableDef extends UntypedTableDef> = Prettify<\n  RowType<TableDef>\n>;\n\nexport interface TableRows<TableDef extends UntypedTableDef> {\n  rows: readonly RowTypeDef<TableDef>[];\n  isLoading: boolean;\n}\n\nexport interface InjectTableCallbacks<RowType> {\n  onInsert?: (row: RowType) => void;\n  onDelete?: (row: RowType) => void;\n  onUpdate?: (oldRow: RowType, newRow: RowType) => void;\n}\n\ntype MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';\n\nfunction classifyMembership(\n  whereExpr: BooleanExpr<any> | undefined,\n  oldRow: Record<string, any>,\n  newRow: Record<string, any>\n): MembershipChange {\n  if (!whereExpr) return 'stayIn';\n  const oldIn = evaluateBooleanExpr(whereExpr, oldRow);\n  const newIn = evaluateBooleanExpr(whereExpr, newRow);\n  if (oldIn && !newIn) return 'leave';\n  if (!oldIn && newIn) return 'enter';\n  if (oldIn && newIn) return 'stayIn';\n  return 'stayOut';\n}\n\n/**\n * Angular injection function to subscribe to a table in SpacetimeDB and receive live updates.\n *\n * Must be called within an injection context (component field initializer or constructor).\n *\n * Accepts a query builder expression as the first argument:\n * - `tables.user` — subscribe to all rows\n * - `tables.user.where(r => r.online.eq(true))` — subscribe with a filter\n *\n * @template TableDef The table definition type.\n *\n * @param query - A query builder expression (table reference or filtered query).\n * @param callbacks - Optional callbacks for row insert, delete, and update events.\n *\n * @returns A signal containing the current rows and loading state.\n *\n * @example\n * ```typescript\n * export class UsersComponent {\n *   users = injectTable(tables.user);\n *\n *   // With a filter:\n *   onlineUsers = injectTable(\n *     tables.user.where(r => r.online.eq(true)),\n *     {\n *       onInsert: (row) => console.log('Inserted:', row),\n *       onDelete: (row) => console.log('Deleted:', row),\n *       onUpdate: (oldRow, newRow) => console.log('Updated:', oldRow, newRow),\n *     }\n *   );\n *\n *   // In template: {{ users().rows.length }} users\n *   // Loading state: {{ users().isLoading }}\n * }\n * ```\n */\nexport function injectTable<TableDef extends UntypedTableDef>(\n  query: Query<TableDef>,\n  callbacks?: InjectTableCallbacks<RowTypeDef<TableDef>>\n): Signal<TableRows<TableDef>> {\n  assertInInjectionContext(injectTable);\n\n  const connState = inject(SPACETIMEDB_CONNECTION);\n\n  const accessorName = getQueryAccessorName(query);\n  const whereExpr = getQueryWhereClause(query);\n  const querySql = toSql(query);\n\n  const tableSignal = signal<TableRows<TableDef>>({\n    isLoading: true,\n    rows: [],\n  });\n\n  let latestTransactionEvent: any = null;\n  let subscribeApplied = false;\n\n  // Note: this code is mostly derived from the React useTable implementation\n  // in order to keep behavior consistent across frameworks.\n\n  const computeSnapshot = (): readonly RowTypeDef<TableDef>[] => {\n    const state = connState();\n    if (!state.isActive) {\n      return [];\n    }\n\n    const connection = state.getConnection();\n    if (!connection) {\n      return [];\n    }\n\n    const table = connection.db[accessorName];\n\n    if (whereExpr) {\n      return Array.from(table.iter()).filter(row =>\n        evaluateBooleanExpr(whereExpr, row as Record<string, any>)\n      ) as RowTypeDef<TableDef>[];\n    }\n\n    return Array.from(table.iter()) as RowTypeDef<TableDef>[];\n  };\n\n  const updateSnapshot = () => {\n    tableSignal.set({\n      rows: computeSnapshot(),\n      isLoading: !subscribeApplied,\n    });\n  };\n\n  effect((onCleanup: (fn: () => void) => void) => {\n    const state = connState();\n    if (!state.isActive) {\n      return;\n    }\n\n    const connection = state.getConnection();\n    if (!connection) {\n      return;\n    }\n\n    const table = connection.db[accessorName];\n\n    const onInsert = (\n      ctx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) {\n        return;\n      }\n\n      callbacks?.onInsert?.(row);\n\n      if (ctx.event !== latestTransactionEvent || !latestTransactionEvent) {\n        latestTransactionEvent = ctx.event;\n        updateSnapshot();\n      }\n    };\n\n    const onDelete = (\n      ctx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) {\n        return;\n      }\n\n      callbacks?.onDelete?.(row);\n\n      if (ctx.event !== latestTransactionEvent || !latestTransactionEvent) {\n        latestTransactionEvent = ctx.event;\n        updateSnapshot();\n      }\n    };\n\n    const onUpdate = (\n      ctx: EventContextInterface<UntypedRemoteModule>,\n      oldRow: any,\n      newRow: any\n    ) => {\n      const change = classifyMembership(whereExpr, oldRow, newRow);\n\n      switch (change) {\n        case 'leave':\n          callbacks?.onDelete?.(oldRow);\n          break;\n        case 'enter':\n          callbacks?.onInsert?.(newRow);\n          break;\n        case 'stayIn':\n          callbacks?.onUpdate?.(oldRow, newRow);\n          break;\n        case 'stayOut':\n          return;\n      }\n\n      if (ctx.event !== latestTransactionEvent || !latestTransactionEvent) {\n        latestTransactionEvent = ctx.event;\n        updateSnapshot();\n      }\n    };\n\n    table.onInsert(onInsert);\n    table.onDelete(onDelete);\n    table.onUpdate?.(onUpdate);\n\n    const subscription = connection\n      .subscriptionBuilder()\n      .onApplied(() => {\n        subscribeApplied = true;\n        updateSnapshot();\n      })\n      .subscribe(querySql);\n\n    onCleanup(() => {\n      table.removeOnInsert(onInsert);\n      table.removeOnDelete(onDelete);\n      table.removeOnUpdate?.(onUpdate);\n      subscription.unsubscribe();\n    });\n  });\n\n  return tableSignal.asReadonly();\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/providers/index.ts",
    "content": "export { provideSpacetimeDB } from './provide-spacetimedb.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/angular/providers/provide-spacetimedb.ts",
    "content": "import {\n  makeEnvironmentProviders,\n  provideAppInitializer,\n  signal,\n  type EnvironmentProviders,\n} from '@angular/core';\nimport type {\n  DbConnectionBuilder,\n  DbConnectionImpl,\n  ErrorContextInterface,\n  RemoteModuleOf,\n} from '../../sdk/db_connection_impl';\nimport {\n  SPACETIMEDB_CONNECTION,\n  type ConnectionState,\n} from '../connection_state';\nimport { ConnectionId } from '../../lib/connection_id';\n\nlet connRef: DbConnectionImpl<any> | null = null;\n\nexport function provideSpacetimeDB<DbConnection extends DbConnectionImpl<any>>(\n  connectionBuilder: DbConnectionBuilder<DbConnection>\n): EnvironmentProviders {\n  const state = signal<ConnectionState>({\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n    getConnection: () => null,\n  });\n\n  return makeEnvironmentProviders([\n    { provide: SPACETIMEDB_CONNECTION, useValue: state },\n    provideAppInitializer(() => {\n      if (typeof window === 'undefined') {\n        return;\n      }\n\n      const getConnection = <T extends DbConnectionImpl<any>>() =>\n        connRef as T | null;\n\n      if (!connRef) {\n        connRef = connectionBuilder.build();\n      }\n\n      const onConnect = (conn: DbConnection) => {\n        state.set({\n          ...state(),\n          isActive: conn.isActive,\n          identity: conn.identity,\n          token: conn.token,\n          connectionId: conn.connectionId,\n          getConnection,\n        });\n      };\n\n      const onDisconnect = (\n        ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>\n      ) => {\n        state.set({\n          ...state(),\n          isActive: ctx.isActive,\n        });\n      };\n\n      const onConnectError = (\n        ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n        err: Error\n      ) => {\n        state.set({\n          ...state(),\n          isActive: ctx.isActive,\n          connectionError: err,\n        });\n      };\n\n      connectionBuilder.onConnect(onConnect);\n      connectionBuilder.onDisconnect(onDisconnect);\n      connectionBuilder.onConnectError(onConnectError);\n\n      // sync initial state if already connected\n      const conn = connRef;\n      if (conn) {\n        state.set({\n          ...state(),\n          isActive: conn.isActive,\n          identity: conn.identity,\n          token: conn.token,\n          connectionId: conn.connectionId,\n          getConnection,\n        });\n      }\n    }),\n  ]);\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/index.ts",
    "content": "export * from './lib/connection_id';\nexport * from './lib/errors';\nexport * from './lib/algebraic_type';\nexport * from './lib/algebraic_value';\nexport { default as BinaryReader } from './lib/binary_reader';\nexport { default as BinaryWriter } from './lib/binary_writer';\nexport * from './lib/schedule_at';\nexport * from './lib/time_duration';\nexport * from './lib/timestamp';\nexport * from './lib/uuid';\nexport * from './lib/util';\nexport * from './lib/identity';\nexport * from './lib/option';\nexport * from './lib/result';\nexport * from './lib/query';\nexport * from './sdk';\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/algebraic_type.ts",
    "content": "import { TimeDuration } from './time_duration';\nimport { Timestamp } from './timestamp';\nimport { Uuid } from './uuid';\nimport { ConnectionId } from './connection_id';\nimport BinaryReader from './binary_reader';\nimport BinaryWriter from './binary_writer';\nimport { Identity } from './identity';\nimport * as AlgebraicTypeVariants from './algebraic_type_variants';\nimport { hasOwn } from './util';\n\ntype TypespaceType = {\n  types: AlgebraicTypeType[];\n};\n\nexport type ProductTypeType = {\n  elements: ProductTypeElement[];\n};\n\n/**\n * A factor / element of a product type.\n *\n * An element consist of an optional name and a type.\n *\n * NOTE: Each element has an implicit element tag based on its order.\n * Uniquely identifies an element similarly to protobuf tags.\n */\nexport type ProductTypeElement = {\n  name: string | undefined;\n  algebraicType: AlgebraicTypeType;\n};\n\nexport type SumTypeType = {\n  variants: SumTypeVariant[];\n};\n\n/**\n * A variant of a sum type.\n *\n * NOTE: Each element has an implicit element tag based on its order.\n * Uniquely identifies an element similarly to protobuf tags.\n */\nexport type SumTypeVariant = {\n  name: string | undefined;\n  algebraicType: AlgebraicTypeType;\n};\n\nexport type AlgebraicTypeType =\n  | AlgebraicTypeVariants.Ref\n  | AlgebraicTypeVariants.Sum\n  | AlgebraicTypeVariants.Product\n  | AlgebraicTypeVariants.Array\n  | AlgebraicTypeVariants.String\n  | AlgebraicTypeVariants.Bool\n  | AlgebraicTypeVariants.I8\n  | AlgebraicTypeVariants.U8\n  | AlgebraicTypeVariants.I16\n  | AlgebraicTypeVariants.U16\n  | AlgebraicTypeVariants.I32\n  | AlgebraicTypeVariants.U32\n  | AlgebraicTypeVariants.I64\n  | AlgebraicTypeVariants.U64\n  | AlgebraicTypeVariants.I128\n  | AlgebraicTypeVariants.U128\n  | AlgebraicTypeVariants.I256\n  | AlgebraicTypeVariants.U256\n  | AlgebraicTypeVariants.F32\n  | AlgebraicTypeVariants.F64;\n\nexport type AlgebraicType = AlgebraicTypeType;\n\n/**\n * The variant types of the Algebraic Type tagged union.\n */\nexport { AlgebraicTypeVariants };\n\nexport type Serializer<T> = (writer: BinaryWriter, value: T) => void;\n\nexport type Deserializer<T> = (reader: BinaryReader) => T;\n\n// Caches to prevent `makeSerializer`/`makeDeserializer` from recursing\n// infinitely when called on recursive types.\n//\n// We check for recursion in `{Product,Sum}Type.make{Deser,Ser}ializer` rather\n// than in `AlgebraciType.make{Deser,Ser}ializer` because we need to store the\n// [de]serializer in the cache before recursing into its fields/variants, and\n// we wouldn't be able to do that in the `AlgebraicType` functions.\nconst SERIALIZERS = new Map<ProductType | SumType, Serializer<any>>();\nconst DESERIALIZERS = new Map<ProductType | SumType, Deserializer<any>>();\n\n// A value with helper functions to construct the type.\nexport const AlgebraicType = {\n  Ref: (value: number): AlgebraicTypeVariants.Ref => ({ tag: 'Ref', value }),\n  Sum: <T extends SumTypeType>(value: T): { tag: 'Sum'; value: T } => ({\n    tag: 'Sum',\n    value,\n  }),\n  Product: <T extends ProductTypeType>(\n    value: T\n  ): { tag: 'Product'; value: T } => ({\n    tag: 'Product',\n    value,\n  }),\n  Array: <T extends AlgebraicTypeType>(\n    value: T\n  ): { tag: 'Array'; value: T } => ({\n    tag: 'Array',\n    value,\n  }),\n  String: { tag: 'String' } as const,\n  Bool: { tag: 'Bool' } as const,\n  I8: { tag: 'I8' } as const,\n  U8: { tag: 'U8' } as const,\n  I16: { tag: 'I16' } as const,\n  U16: { tag: 'U16' } as const,\n  I32: { tag: 'I32' } as const,\n  U32: { tag: 'U32' } as const,\n  I64: { tag: 'I64' } as const,\n  U64: { tag: 'U64' } as const,\n  I128: { tag: 'I128' } as const,\n  U128: { tag: 'U128' } as const,\n  I256: { tag: 'I256' } as const,\n  U256: { tag: 'U256' } as const,\n  F32: { tag: 'F32' } as const,\n  F64: { tag: 'F64' } as const,\n  makeSerializer(\n    ty: AlgebraicTypeType,\n    typespace?: TypespaceType\n  ): Serializer<any> {\n    if (ty.tag === 'Ref') {\n      if (!typespace)\n        throw new Error('cannot serialize refs without a typespace');\n      while (ty.tag === 'Ref') ty = typespace.types[ty.value];\n    }\n    switch (ty.tag) {\n      case 'Product':\n        return ProductType.makeSerializer(ty.value, typespace);\n      case 'Sum':\n        return SumType.makeSerializer(ty.value, typespace);\n      case 'Array':\n        if (ty.value.tag === 'U8') {\n          return serializeUint8Array;\n        } else {\n          const serialize = AlgebraicType.makeSerializer(ty.value, typespace);\n          return (writer, value) => {\n            writer.writeU32(value.length);\n            for (const elem of value) {\n              serialize(writer, elem);\n            }\n          };\n        }\n      default:\n        return primitiveSerializers[ty.tag];\n    }\n  },\n  /** @deprecated Use `makeSerializer` instead. */\n  serializeValue(\n    writer: BinaryWriter,\n    ty: AlgebraicTypeType,\n    value: any,\n    typespace?: TypespaceType\n  ) {\n    AlgebraicType.makeSerializer(ty, typespace)(writer, value);\n  },\n  makeDeserializer(\n    ty: AlgebraicTypeType,\n    typespace?: TypespaceType\n  ): Deserializer<any> {\n    if (ty.tag === 'Ref') {\n      if (!typespace)\n        throw new Error('cannot deserialize refs without a typespace');\n      while (ty.tag === 'Ref') ty = typespace.types[ty.value];\n    }\n    switch (ty.tag) {\n      case 'Product':\n        return ProductType.makeDeserializer(ty.value, typespace);\n      case 'Sum':\n        return SumType.makeDeserializer(ty.value, typespace);\n      case 'Array':\n        if (ty.value.tag === 'U8') {\n          return deserializeUint8Array;\n        } else {\n          const deserialize = AlgebraicType.makeDeserializer(\n            ty.value,\n            typespace\n          );\n          return reader => {\n            const length = reader.readU32();\n            const result: any[] = Array(length);\n            for (let i = 0; i < length; i++) {\n              result[i] = deserialize(reader);\n            }\n            return result;\n          };\n        }\n      default:\n        return primitiveDeserializers[ty.tag];\n    }\n  },\n  /** @deprecated Use `makeDeserializer` instead. */\n  deserializeValue(\n    reader: BinaryReader,\n    ty: AlgebraicTypeType,\n    typespace?: TypespaceType\n  ): any {\n    return AlgebraicType.makeDeserializer(ty, typespace)(reader);\n  },\n  /**\n   * Convert a value of the algebraic type into something that can be used as a key in a map.\n   * There are no guarantees about being able to order it.\n   * This is only guaranteed to be comparable to other values of the same type.\n   * @param value A value of the algebraic type\n   * @returns Something that can be used as a key in a map.\n   */\n  intoMapKey: function (\n    ty: AlgebraicTypeType,\n    value: any\n  ): ComparablePrimitive {\n    switch (ty.tag) {\n      case 'U8':\n      case 'U16':\n      case 'U32':\n      case 'U64':\n      case 'U128':\n      case 'U256':\n      case 'I8':\n      case 'I16':\n      case 'I32':\n      case 'I64':\n      case 'I128':\n      case 'I256':\n      case 'F32':\n      case 'F64':\n      case 'String':\n      case 'Bool':\n        return value;\n      case 'Product':\n        return ProductType.intoMapKey(ty.value, value);\n      default: {\n        // The fallback is to serialize and base64 encode the bytes.\n        const writer = new BinaryWriter(10);\n        AlgebraicType.serializeValue(writer, ty, value);\n        return writer.toBase64();\n      }\n    }\n  },\n};\n\nfunction bindCall<F extends (this: any, ...args: any[]) => any>(\n  f: F\n): (recv: ThisParameterType<F>, ...args: Parameters<F>) => ReturnType<F> {\n  return Function.prototype.call.bind(f);\n}\n\ntype Primitives = Exclude<\n  AlgebraicType['tag'],\n  'Ref' | 'Sum' | 'Product' | 'Array'\n>;\n\nconst primitiveSerializers: Record<Primitives, Serializer<any>> = {\n  Bool: bindCall(BinaryWriter.prototype.writeBool),\n  I8: bindCall(BinaryWriter.prototype.writeI8),\n  U8: bindCall(BinaryWriter.prototype.writeU8),\n  I16: bindCall(BinaryWriter.prototype.writeI16),\n  U16: bindCall(BinaryWriter.prototype.writeU16),\n  I32: bindCall(BinaryWriter.prototype.writeI32),\n  U32: bindCall(BinaryWriter.prototype.writeU32),\n  I64: bindCall(BinaryWriter.prototype.writeI64),\n  U64: bindCall(BinaryWriter.prototype.writeU64),\n  I128: bindCall(BinaryWriter.prototype.writeI128),\n  U128: bindCall(BinaryWriter.prototype.writeU128),\n  I256: bindCall(BinaryWriter.prototype.writeI256),\n  U256: bindCall(BinaryWriter.prototype.writeU256),\n  F32: bindCall(BinaryWriter.prototype.writeF32),\n  F64: bindCall(BinaryWriter.prototype.writeF64),\n  String: bindCall(BinaryWriter.prototype.writeString),\n};\nObject.freeze(primitiveSerializers);\n\nconst serializeUint8Array = bindCall(BinaryWriter.prototype.writeUInt8Array);\n\nconst primitiveDeserializers: Record<Primitives, Deserializer<any>> = {\n  Bool: bindCall(BinaryReader.prototype.readBool),\n  I8: bindCall(BinaryReader.prototype.readI8),\n  U8: bindCall(BinaryReader.prototype.readU8),\n  I16: bindCall(BinaryReader.prototype.readI16),\n  U16: bindCall(BinaryReader.prototype.readU16),\n  I32: bindCall(BinaryReader.prototype.readI32),\n  U32: bindCall(BinaryReader.prototype.readU32),\n  I64: bindCall(BinaryReader.prototype.readI64),\n  U64: bindCall(BinaryReader.prototype.readU64),\n  I128: bindCall(BinaryReader.prototype.readI128),\n  U128: bindCall(BinaryReader.prototype.readU128),\n  I256: bindCall(BinaryReader.prototype.readI256),\n  U256: bindCall(BinaryReader.prototype.readU256),\n  F32: bindCall(BinaryReader.prototype.readF32),\n  F64: bindCall(BinaryReader.prototype.readF64),\n  String: bindCall(BinaryReader.prototype.readString),\n};\nObject.freeze(primitiveDeserializers);\n\nconst deserializeUint8Array = bindCall(BinaryReader.prototype.readUInt8Array);\n\ntype FixedSizePrimitives = Exclude<Primitives, 'String'>;\n\nconst primitiveSizes: Record<FixedSizePrimitives, number> = {\n  Bool: 1,\n  I8: 1,\n  U8: 1,\n  I16: 2,\n  U16: 2,\n  I32: 4,\n  U32: 4,\n  I64: 8,\n  U64: 8,\n  I128: 16,\n  U128: 16,\n  I256: 32,\n  U256: 32,\n  F32: 4,\n  F64: 8,\n};\n\nconst fixedSizePrimitives = new Set(Object.keys(primitiveSizes));\n\ntype FixedSizeProductType = {\n  elements: { name: string; algebraicType: { tag: FixedSizePrimitives } }[];\n};\n\nconst isFixedSizeProduct = (ty: ProductType): ty is FixedSizeProductType =>\n  ty.elements.every(({ algebraicType }) =>\n    fixedSizePrimitives.has(algebraicType.tag)\n  );\n\nconst productSize = (ty: FixedSizeProductType): number =>\n  ty.elements.reduce(\n    (acc, { algebraicType }) => acc + primitiveSizes[algebraicType.tag],\n    0\n  );\n\ntype JSPrimitives = Exclude<\n  FixedSizePrimitives,\n  'I128' | 'U128' | 'I256' | 'U256'\n>;\n\nconst primitiveJSName: Record<JSPrimitives, string> = {\n  Bool: 'Uint8',\n  I8: 'Int8',\n  U8: 'Uint8',\n  I16: 'Int16',\n  U16: 'Uint16',\n  I32: 'Int32',\n  U32: 'Uint32',\n  I64: 'BigInt64',\n  U64: 'BigUint64',\n  F32: 'Float32',\n  F64: 'Float64',\n};\n\ntype SpecialProducts = {\n  __time_duration_micros__: TimeDuration;\n  __timestamp_micros_since_unix_epoch__: Timestamp;\n  __identity__: Identity;\n  __connection_id__: ConnectionId;\n  __uuid__: Uuid;\n};\n\nconst specialProductDeserializers: {\n  [k in keyof SpecialProducts]: Deserializer<SpecialProducts[k]>;\n} = {\n  __time_duration_micros__: reader => new TimeDuration(reader.readI64()),\n  __timestamp_micros_since_unix_epoch__: reader =>\n    new Timestamp(reader.readI64()),\n  __identity__: reader => new Identity(reader.readU256()),\n  __connection_id__: reader => new ConnectionId(reader.readU128()),\n  __uuid__: reader => new Uuid(reader.readU128()),\n};\nObject.freeze(specialProductDeserializers);\n\nconst unitDeserializer: Deserializer<{}> = () => ({});\n\nconst getElementInitializer = (element: ProductTypeElement) => {\n  let init: string;\n  switch (element.algebraicType.tag) {\n    case 'String':\n      init = \"''\";\n      break;\n    case 'Bool':\n      init = 'false';\n      break;\n    case 'I8':\n    case 'U8':\n    case 'I16':\n    case 'U16':\n    case 'I32':\n    case 'U32':\n      init = '0';\n      break;\n    case 'I64':\n    case 'U64':\n    case 'I128':\n    case 'U128':\n    case 'I256':\n    case 'U256':\n      init = '0n';\n      break;\n    case 'F32':\n    case 'F64':\n      init = '0.0';\n      break;\n    default:\n      init = 'undefined';\n  }\n  return `${element.name!}: ${init}`;\n};\n\n/**\n * A structural product type  of the factors given by `elements`.\n *\n * This is also known as `struct` and `tuple` in many languages,\n * but note that unlike most languages, products in SATs are *[structural]* and not nominal.\n * When checking whether two nominal types are the same,\n * their names and/or declaration sites (e.g., module / namespace) are considered.\n * Meanwhile, a structural type system would only check the structure of the type itself,\n * e.g., the names of its fields and their types in the case of a record.\n * The name \"product\" comes from category theory.\n *\n * See also: https://ncatlab.org/nlab/show/product+type.\n *\n * These structures are known as product types because the number of possible values in product\n * ```ignore\n * { N_0: T_0, N_1: T_1, ..., N_n: T_n }\n * ```\n * is:\n * ```ignore\n * Π (i ∈ 0..n). values(T_i)\n * ```\n * so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.\n *\n * [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n */\nexport type ProductType = ProductTypeType;\n\nexport const ProductType = {\n  makeSerializer(\n    ty: ProductTypeType,\n    typespace?: TypespaceType\n  ): Serializer<any> {\n    let serializer = SERIALIZERS.get(ty);\n    if (serializer != null) return serializer;\n\n    if (isFixedSizeProduct(ty)) {\n      const size = productSize(ty);\n      const body = `\\\n\"use strict\";\nwriter.expandBuffer(${size});\nconst view = writer.view;\n${ty.elements\n  .map(({ name, algebraicType: { tag } }) =>\n    tag in primitiveJSName\n      ? `\\\nview.set${primitiveJSName[tag as JSPrimitives]}(writer.offset, value.${name!}, ${primitiveSizes[tag] > 1 ? 'true' : ''});\nwriter.offset += ${primitiveSizes[tag]};`\n      : `writer.write${tag}(value.${name});`\n  )\n  .join('\\n')}`;\n      serializer = Function('writer', 'value', body) as Serializer<any>;\n      SERIALIZERS.set(ty, serializer);\n      return serializer;\n    }\n\n    // Because V8 forces us to generate our code as a string, rather than a proper syntax tree,\n    // we can't have our `body` close over values.\n    // Instead, we construct an object with the values we'd otherwise \"close over\" in `serializers`,\n    // and use `Function.prototype.bind` to pass it as the `this` argument.\n    //\n    // We populate `serializers` after constructing this type's `serializer`\n    // so that it can close over itself, in the case that `ty` is recursive.\n    const serializers: Record<string, Serializer<any>> = {};\n    const body =\n      '\"use strict\";\\n' +\n      ty.elements\n        .map(\n          element => `this.${element.name!}(writer, value.${element.name!});`\n        )\n        .join('\\n');\n    serializer = Function('writer', 'value', body).bind(\n      serializers\n    ) as Serializer<any>;\n    // In case `ty` is recursive, we cache the function *before* before computing\n    // `serializers`, so that a recursive `makeSerializer` with the same `ty` has\n    // an exit condition.\n    SERIALIZERS.set(ty, serializer);\n    for (const { name, algebraicType } of ty.elements) {\n      serializers[name!] = AlgebraicType.makeSerializer(\n        algebraicType,\n        typespace\n      );\n    }\n    Object.freeze(serializers);\n    return serializer;\n  },\n  /** @deprecated Use `makeSerializer` instead. */\n  serializeValue(\n    writer: BinaryWriter,\n    ty: ProductTypeType,\n    value: any,\n    typespace?: TypespaceType\n  ): void {\n    ProductType.makeSerializer(ty, typespace)(writer, value);\n  },\n  makeDeserializer(\n    ty: ProductTypeType,\n    typespace?: TypespaceType\n  ): Deserializer<any> {\n    switch (ty.elements.length) {\n      case 0:\n        return unitDeserializer;\n      case 1: {\n        const fieldName = ty.elements[0].name!;\n        if (hasOwn(specialProductDeserializers, fieldName))\n          return specialProductDeserializers[\n            fieldName as keyof SpecialProducts\n          ];\n      }\n    }\n\n    let deserializer = DESERIALIZERS.get(ty);\n    if (deserializer != null) return deserializer;\n\n    if (isFixedSizeProduct(ty)) {\n      const body = `\\\n\"use strict\";\nconst result = { ${ty.elements.map(getElementInitializer).join(', ')} };\nconst view = reader.view;\n${ty.elements\n  .map(({ name, algebraicType: { tag } }) =>\n    tag in primitiveJSName\n      ? tag === 'Bool'\n        ? `\\\nresult.${name} = view.getUint8(reader.offset) !== 0;\nreader.offset += 1;`\n        : `\\\nresult.${name} = view.get${primitiveJSName[tag as JSPrimitives]}(reader.offset, ${primitiveSizes[tag] > 1 ? 'true' : ''});\nreader.offset += ${primitiveSizes[tag]};`\n      : `result.${name} = reader.read${tag}();`\n  )\n  .join('\\n')}\nreturn result;`;\n      deserializer = Function('reader', body) as Deserializer<any>;\n      DESERIALIZERS.set(ty, deserializer);\n      return deserializer;\n    }\n\n    // Because V8 forces us to generate our code as a string, rather than a proper syntax tree,\n    // we can't have our `body` close over values.\n    // Instead, we construct an object with the values we'd otherwise \"close over\" in `deserializers`,\n    // and use `Function.prototype.bind` to pass it as the `this` argument.\n    //\n    // We populate `deserializers` after constructing this type's `deserializer`\n    // so that it can close over itself, in the case that `ty` is recursive.\n    const deserializers: Record<string, Deserializer<any>> = {};\n    deserializer = Function(\n      'reader',\n      `\\\n\"use strict\";\nconst result = { ${ty.elements.map(getElementInitializer).join(', ')} };\n${ty.elements.map(({ name }) => `result.${name!} = this.${name!}(reader);`).join('\\n')}\nreturn result;`\n    ).bind(deserializers) as Deserializer<any>;\n    // In case `ty` is recursive, we cache the function *before* before computing\n    // `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has\n    // an exit condition.\n    DESERIALIZERS.set(ty, deserializer);\n    for (const { name, algebraicType } of ty.elements) {\n      deserializers[name!] = AlgebraicType.makeDeserializer(\n        algebraicType,\n        typespace\n      );\n    }\n    Object.freeze(deserializers);\n    return deserializer;\n  },\n  /** @deprecated Use `makeDeserializer` instead. */\n  deserializeValue(\n    reader: BinaryReader,\n    ty: ProductTypeType,\n    typespace?: TypespaceType\n  ): any {\n    return ProductType.makeDeserializer(ty, typespace)(reader);\n  },\n  intoMapKey(ty: ProductTypeType, value: any): ComparablePrimitive {\n    if (ty.elements.length === 1) {\n      const fieldName = ty.elements[0].name!;\n      if (hasOwn(specialProductDeserializers, fieldName)) {\n        return value[fieldName];\n      }\n    }\n    // The fallback is to serialize and base64 encode the bytes.\n    const writer = new BinaryWriter(10);\n    AlgebraicType.serializeValue(writer, AlgebraicType.Product(ty), value);\n    return writer.toBase64();\n  },\n};\n\nexport type SumType = SumTypeType;\n\n/**\n * Unlike most languages, sums in SATS are *[structural]* and not nominal.\n * When checking whether two nominal types are the same,\n * their names and/or declaration sites (e.g., module / namespace) are considered.\n * Meanwhile, a structural type system would only check the structure of the type itself,\n * e.g., the names of its variants and their inner data types in the case of a sum.\n *\n * This is also known as a discriminated union (implementation) or disjoint union.\n * Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).\n *\n * These structures are known as sum types because the number of possible values a sum\n * ```ignore\n * { N_0(T_0), N_1(T_1), ..., N_n(T_n) }\n * ```\n * is:\n * ```ignore\n * Σ (i ∈ 0..n). values(T_i)\n * ```\n * so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.\n *\n * See also: https://ncatlab.org/nlab/show/sum+type.\n *\n * [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n */\nexport const SumType = {\n  makeSerializer(ty: SumTypeType, typespace?: TypespaceType): Serializer<any> {\n    if (\n      ty.variants.length == 2 &&\n      ty.variants[0].name === 'some' &&\n      ty.variants[1].name === 'none'\n    ) {\n      const serialize = AlgebraicType.makeSerializer(\n        ty.variants[0].algebraicType,\n        typespace\n      );\n      return (writer, value) => {\n        if (value !== null && value !== undefined) {\n          writer.writeByte(0);\n          serialize(writer, value);\n        } else {\n          writer.writeByte(1);\n        }\n      };\n    } else if (\n      ty.variants.length == 2 &&\n      ty.variants[0].name === 'ok' &&\n      ty.variants[1].name === 'err'\n    ) {\n      const serializeOk = AlgebraicType.makeSerializer(\n        ty.variants[0].algebraicType,\n        typespace\n      );\n      const serializeErr = AlgebraicType.makeSerializer(\n        ty.variants[0].algebraicType,\n        typespace\n      );\n\n      return (writer, value) => {\n        if ('ok' in value) {\n          writer.writeU8(0);\n          serializeOk(writer, value.ok);\n        } else if ('err' in value) {\n          writer.writeU8(1);\n          serializeErr(writer, value.err);\n        } else {\n          throw new TypeError(\n            'could not serialize result: object had neither a `ok` nor an `err` field'\n          );\n        }\n      };\n    } else {\n      let serializer = SERIALIZERS.get(ty);\n      if (serializer != null) return serializer;\n\n      const serializers: Record<string, Serializer<any>> = {};\n\n      const body = `\\\nswitch (value.tag) {\n${ty.variants\n  .map(\n    ({ name }, i) => `\\\n  case ${JSON.stringify(name!)}:\n    writer.writeByte(${i});\n    return this.${name!}(writer, value.value);`\n  )\n  .join('\\n')}\n  default:\n    throw new TypeError(\n      \\`Could not serialize sum type; unknown tag \\${value.tag}\\`\n    )\n}\n`;\n\n      serializer = Function('writer', 'value', body).bind(\n        serializers\n      ) as Serializer<any>;\n\n      // In case `ty` is recursive, we cache the function *before* before computing\n      // `variants`, so that a recursive `makeSerializer` with the same `ty` has\n      // an exit condition.\n      SERIALIZERS.set(ty, serializer);\n\n      for (const { name, algebraicType } of ty.variants) {\n        serializers[name!] = AlgebraicType.makeSerializer(\n          algebraicType,\n          typespace\n        );\n      }\n      Object.freeze(serializers);\n      return serializer;\n    }\n  },\n  /** @deprecated Use `makeSerializer` instead. */\n  serializeValue(\n    writer: BinaryWriter,\n    ty: SumTypeType,\n    value: any,\n    typespace?: TypespaceType\n  ): void {\n    SumType.makeSerializer(ty, typespace)(writer, value);\n  },\n  makeDeserializer(\n    ty: SumTypeType,\n    typespace?: TypespaceType\n  ): Deserializer<any> {\n    // In TypeScript we handle Option values as a special case\n    // we don't represent the some and none variants, but instead\n    // we represent the value directly.\n    //\n    // For these special cases, we don't do dynamic codegen, since that has the\n    // most benefit in cases where the object has a different shape. Since\n    // option/result always have the same number of variants, there's not as\n    // much benefit for the amount of work.\n    if (\n      ty.variants.length == 2 &&\n      ty.variants[0].name === 'some' &&\n      ty.variants[1].name === 'none'\n    ) {\n      const deserialize = AlgebraicType.makeDeserializer(\n        ty.variants[0].algebraicType,\n        typespace\n      );\n      return reader => {\n        const tag = reader.readU8();\n        if (tag === 0) {\n          return deserialize(reader);\n        } else if (tag === 1) {\n          return undefined;\n        } else {\n          throw `Can't deserialize an option type, couldn't find ${tag} tag`;\n        }\n      };\n    } else if (\n      ty.variants.length == 2 &&\n      ty.variants[0].name === 'ok' &&\n      ty.variants[1].name === 'err'\n    ) {\n      const deserializeOk = AlgebraicType.makeDeserializer(\n        ty.variants[0].algebraicType,\n        typespace\n      );\n      const deserializeErr = AlgebraicType.makeDeserializer(\n        ty.variants[1].algebraicType,\n        typespace\n      );\n      return reader => {\n        const tag = reader.readByte();\n        if (tag === 0) {\n          return { ok: deserializeOk(reader) };\n        } else if (tag === 1) {\n          return { err: deserializeErr(reader) };\n        } else {\n          throw `Can't deserialize a result type, couldn't find ${tag} tag`;\n        }\n      };\n    } else {\n      let deserializer = DESERIALIZERS.get(ty);\n      if (deserializer != null) return deserializer;\n      const deserializers: Record<string, Deserializer<any>> = {};\n      deserializer = Function(\n        'reader',\n        `switch (reader.readU8()) {\\n${ty.variants\n          .map(\n            ({ name }, i) =>\n              `case ${i}: return { tag: ${JSON.stringify(name!)}, value: this.${name!}(reader) };`\n          )\n          .join('\\n')} }`\n      ).bind(deserializers) as Deserializer<any>;\n      // In case `ty` is recursive, we cache the function *before* before computing\n      // `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has\n      // an exit condition.\n      DESERIALIZERS.set(ty, deserializer);\n      for (const { name, algebraicType } of ty.variants) {\n        deserializers[name!] = AlgebraicType.makeDeserializer(\n          algebraicType,\n          typespace\n        );\n      }\n      Object.freeze(deserializers);\n      return deserializer;\n    }\n  },\n  /** @deprecated Use `makeDeserializer` instead. */\n  deserializeValue(\n    reader: BinaryReader,\n    ty: SumTypeType,\n    typespace?: TypespaceType\n  ): any {\n    return SumType.makeDeserializer(ty, typespace)(reader);\n  },\n};\n\nexport type ComparablePrimitive = number | string | boolean | bigint;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/algebraic_type_variants.ts",
    "content": "import type {\n  AlgebraicTypeType,\n  ProductTypeType,\n  SumTypeType,\n} from './algebraic_type';\n\nexport type Ref = { tag: 'Ref'; value: number };\nexport type Sum = { tag: 'Sum'; value: SumTypeType };\nexport type Product = { tag: 'Product'; value: ProductTypeType };\nexport type Array = { tag: 'Array'; value: AlgebraicTypeType };\nexport type String = { tag: 'String' };\nexport type Bool = { tag: 'Bool' };\nexport type I8 = { tag: 'I8' };\nexport type U8 = { tag: 'U8' };\nexport type I16 = { tag: 'I16' };\nexport type U16 = { tag: 'U16' };\nexport type I32 = { tag: 'I32' };\nexport type U32 = { tag: 'U32' };\nexport type I64 = { tag: 'I64' };\nexport type U64 = { tag: 'U64' };\nexport type I128 = { tag: 'I128' };\nexport type U128 = { tag: 'U128' };\nexport type I256 = { tag: 'I256' };\nexport type U256 = { tag: 'U256' };\nexport type F32 = { tag: 'F32' };\nexport type F64 = { tag: 'F64' };\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/algebraic_value.ts",
    "content": "import BinaryReader from './binary_reader';\n\nexport interface ParseableType<T> {\n  deserialize: (reader: BinaryReader) => T;\n}\n\nexport function parseValue<T>(ty: ParseableType<T>, src: Uint8Array): T {\n  const reader = new BinaryReader(src);\n  return ty.deserialize(reader);\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/autogen/types.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../lib/type_builders';\n\n// The tagged union or sum type for the algebraic type `AlgebraicType`.\nexport const AlgebraicType: __TypeBuilder<\n  __AlgebraicTypeType,\n  __AlgebraicTypeType\n> = __t.enum('AlgebraicType', {\n  Ref: __t.u32(),\n  get Sum() {\n    return SumType;\n  },\n  get Product() {\n    return ProductType;\n  },\n  get Array() {\n    return AlgebraicType;\n  },\n  String: __t.unit(),\n  Bool: __t.unit(),\n  I8: __t.unit(),\n  U8: __t.unit(),\n  I16: __t.unit(),\n  U16: __t.unit(),\n  I32: __t.unit(),\n  U32: __t.unit(),\n  I64: __t.unit(),\n  U64: __t.unit(),\n  I128: __t.unit(),\n  U128: __t.unit(),\n  I256: __t.unit(),\n  U256: __t.unit(),\n  F32: __t.unit(),\n  F64: __t.unit(),\n});\nexport type AlgebraicType = __Infer<typeof AlgebraicType>;\n\n// The tagged union or sum type for the algebraic type `CaseConversionPolicy`.\nexport const CaseConversionPolicy = __t.enum('CaseConversionPolicy', {\n  None: __t.unit(),\n  SnakeCase: __t.unit(),\n});\nexport type CaseConversionPolicy = __Infer<typeof CaseConversionPolicy>;\n\n// The tagged union or sum type for the algebraic type `ExplicitNameEntry`.\nexport const ExplicitNameEntry = __t.enum('ExplicitNameEntry', {\n  get Table() {\n    return NameMapping;\n  },\n  get Function() {\n    return NameMapping;\n  },\n  get Index() {\n    return NameMapping;\n  },\n});\nexport type ExplicitNameEntry = __Infer<typeof ExplicitNameEntry>;\n\nexport const ExplicitNames = __t.object('ExplicitNames', {\n  get entries() {\n    return __t.array(ExplicitNameEntry);\n  },\n});\nexport type ExplicitNames = __Infer<typeof ExplicitNames>;\n\n// The tagged union or sum type for the algebraic type `FunctionVisibility`.\nexport const FunctionVisibility = __t.enum('FunctionVisibility', {\n  Private: __t.unit(),\n  ClientCallable: __t.unit(),\n});\nexport type FunctionVisibility = __Infer<typeof FunctionVisibility>;\n\nexport const HttpHeaderPair = __t.object('HttpHeaderPair', {\n  name: __t.string(),\n  value: __t.byteArray(),\n});\nexport type HttpHeaderPair = __Infer<typeof HttpHeaderPair>;\n\nexport const HttpHeaders = __t.object('HttpHeaders', {\n  get entries() {\n    return __t.array(HttpHeaderPair);\n  },\n});\nexport type HttpHeaders = __Infer<typeof HttpHeaders>;\n\n// The tagged union or sum type for the algebraic type `HttpMethod`.\nexport const HttpMethod = __t.enum('HttpMethod', {\n  Get: __t.unit(),\n  Head: __t.unit(),\n  Post: __t.unit(),\n  Put: __t.unit(),\n  Delete: __t.unit(),\n  Connect: __t.unit(),\n  Options: __t.unit(),\n  Trace: __t.unit(),\n  Patch: __t.unit(),\n  Extension: __t.string(),\n});\nexport type HttpMethod = __Infer<typeof HttpMethod>;\n\nexport const HttpRequest = __t.object('HttpRequest', {\n  get method() {\n    return HttpMethod;\n  },\n  get headers() {\n    return HttpHeaders;\n  },\n  timeout: __t.option(__t.timeDuration()),\n  uri: __t.string(),\n  get version() {\n    return HttpVersion;\n  },\n});\nexport type HttpRequest = __Infer<typeof HttpRequest>;\n\nexport const HttpResponse = __t.object('HttpResponse', {\n  get headers() {\n    return HttpHeaders;\n  },\n  get version() {\n    return HttpVersion;\n  },\n  code: __t.u16(),\n});\nexport type HttpResponse = __Infer<typeof HttpResponse>;\n\n// The tagged union or sum type for the algebraic type `HttpVersion`.\nexport const HttpVersion = __t.enum('HttpVersion', {\n  Http09: __t.unit(),\n  Http10: __t.unit(),\n  Http11: __t.unit(),\n  Http2: __t.unit(),\n  Http3: __t.unit(),\n});\nexport type HttpVersion = __Infer<typeof HttpVersion>;\n\n// The tagged union or sum type for the algebraic type `IndexType`.\nexport const IndexType = __t.enum('IndexType', {\n  BTree: __t.unit(),\n  Hash: __t.unit(),\n});\nexport type IndexType = __Infer<typeof IndexType>;\n\n// The tagged union or sum type for the algebraic type `Lifecycle`.\nexport const Lifecycle = __t.enum('Lifecycle', {\n  Init: __t.unit(),\n  OnConnect: __t.unit(),\n  OnDisconnect: __t.unit(),\n});\nexport type Lifecycle = __Infer<typeof Lifecycle>;\n\n// The tagged union or sum type for the algebraic type `MiscModuleExport`.\nexport const MiscModuleExport = __t.enum('MiscModuleExport', {\n  get TypeAlias() {\n    return TypeAlias;\n  },\n});\nexport type MiscModuleExport = __Infer<typeof MiscModuleExport>;\n\nexport const NameMapping = __t.object('NameMapping', {\n  sourceName: __t.string(),\n  canonicalName: __t.string(),\n});\nexport type NameMapping = __Infer<typeof NameMapping>;\n\nexport const ProductType = __t.object('ProductType', {\n  get elements() {\n    return __t.array(ProductTypeElement);\n  },\n});\nexport type ProductType = __Infer<typeof ProductType>;\n\nexport const ProductTypeElement = __t.object('ProductTypeElement', {\n  name: __t.option(__t.string()),\n  get algebraicType() {\n    return AlgebraicType;\n  },\n});\nexport type ProductTypeElement = __Infer<typeof ProductTypeElement>;\n\nexport const RawColumnDefV8 = __t.object('RawColumnDefV8', {\n  colName: __t.string(),\n  get colType() {\n    return AlgebraicType;\n  },\n});\nexport type RawColumnDefV8 = __Infer<typeof RawColumnDefV8>;\n\nexport const RawColumnDefaultValueV10 = __t.object('RawColumnDefaultValueV10', {\n  colId: __t.u16(),\n  value: __t.byteArray(),\n});\nexport type RawColumnDefaultValueV10 = __Infer<typeof RawColumnDefaultValueV10>;\n\nexport const RawColumnDefaultValueV9 = __t.object('RawColumnDefaultValueV9', {\n  table: __t.string(),\n  colId: __t.u16(),\n  value: __t.byteArray(),\n});\nexport type RawColumnDefaultValueV9 = __Infer<typeof RawColumnDefaultValueV9>;\n\n// The tagged union or sum type for the algebraic type `RawConstraintDataV9`.\nexport const RawConstraintDataV9 = __t.enum('RawConstraintDataV9', {\n  get Unique() {\n    return RawUniqueConstraintDataV9;\n  },\n});\nexport type RawConstraintDataV9 = __Infer<typeof RawConstraintDataV9>;\n\nexport const RawConstraintDefV10 = __t.object('RawConstraintDefV10', {\n  sourceName: __t.option(__t.string()),\n  get data() {\n    return RawConstraintDataV9;\n  },\n});\nexport type RawConstraintDefV10 = __Infer<typeof RawConstraintDefV10>;\n\nexport const RawConstraintDefV8 = __t.object('RawConstraintDefV8', {\n  constraintName: __t.string(),\n  constraints: __t.u8(),\n  columns: __t.array(__t.u16()),\n});\nexport type RawConstraintDefV8 = __Infer<typeof RawConstraintDefV8>;\n\nexport const RawConstraintDefV9 = __t.object('RawConstraintDefV9', {\n  name: __t.option(__t.string()),\n  get data() {\n    return RawConstraintDataV9;\n  },\n});\nexport type RawConstraintDefV9 = __Infer<typeof RawConstraintDefV9>;\n\n// The tagged union or sum type for the algebraic type `RawIndexAlgorithm`.\nexport const RawIndexAlgorithm = __t.enum('RawIndexAlgorithm', {\n  BTree: __t.array(__t.u16()),\n  Hash: __t.array(__t.u16()),\n  Direct: __t.u16(),\n});\nexport type RawIndexAlgorithm = __Infer<typeof RawIndexAlgorithm>;\n\nexport const RawIndexDefV10 = __t.object('RawIndexDefV10', {\n  sourceName: __t.option(__t.string()),\n  accessorName: __t.option(__t.string()),\n  get algorithm() {\n    return RawIndexAlgorithm;\n  },\n});\nexport type RawIndexDefV10 = __Infer<typeof RawIndexDefV10>;\n\nexport const RawIndexDefV8 = __t.object('RawIndexDefV8', {\n  indexName: __t.string(),\n  isUnique: __t.bool(),\n  get indexType() {\n    return IndexType;\n  },\n  columns: __t.array(__t.u16()),\n});\nexport type RawIndexDefV8 = __Infer<typeof RawIndexDefV8>;\n\nexport const RawIndexDefV9 = __t.object('RawIndexDefV9', {\n  name: __t.option(__t.string()),\n  accessorName: __t.option(__t.string()),\n  get algorithm() {\n    return RawIndexAlgorithm;\n  },\n});\nexport type RawIndexDefV9 = __Infer<typeof RawIndexDefV9>;\n\nexport const RawLifeCycleReducerDefV10 = __t.object(\n  'RawLifeCycleReducerDefV10',\n  {\n    get lifecycleSpec() {\n      return Lifecycle;\n    },\n    functionName: __t.string(),\n  }\n);\nexport type RawLifeCycleReducerDefV10 = __Infer<\n  typeof RawLifeCycleReducerDefV10\n>;\n\n// The tagged union or sum type for the algebraic type `RawMiscModuleExportV9`.\nexport const RawMiscModuleExportV9 = __t.enum('RawMiscModuleExportV9', {\n  get ColumnDefaultValue() {\n    return RawColumnDefaultValueV9;\n  },\n  get Procedure() {\n    return RawProcedureDefV9;\n  },\n  get View() {\n    return RawViewDefV9;\n  },\n});\nexport type RawMiscModuleExportV9 = __Infer<typeof RawMiscModuleExportV9>;\n\n// The tagged union or sum type for the algebraic type `RawModuleDef`.\nexport const RawModuleDef = __t.enum('RawModuleDef', {\n  get V8BackCompat() {\n    return RawModuleDefV8;\n  },\n  get V9() {\n    return RawModuleDefV9;\n  },\n  get V10() {\n    return RawModuleDefV10;\n  },\n});\nexport type RawModuleDef = __Infer<typeof RawModuleDef>;\n\nexport const RawModuleDefV10 = __t.object('RawModuleDefV10', {\n  get sections() {\n    return __t.array(RawModuleDefV10Section);\n  },\n});\nexport type RawModuleDefV10 = __Infer<typeof RawModuleDefV10>;\n\n// The tagged union or sum type for the algebraic type `RawModuleDefV10Section`.\nexport const RawModuleDefV10Section = __t.enum('RawModuleDefV10Section', {\n  get Typespace() {\n    return Typespace;\n  },\n  get Types() {\n    return __t.array(RawTypeDefV10);\n  },\n  get Tables() {\n    return __t.array(RawTableDefV10);\n  },\n  get Reducers() {\n    return __t.array(RawReducerDefV10);\n  },\n  get Procedures() {\n    return __t.array(RawProcedureDefV10);\n  },\n  get Views() {\n    return __t.array(RawViewDefV10);\n  },\n  get Schedules() {\n    return __t.array(RawScheduleDefV10);\n  },\n  get LifeCycleReducers() {\n    return __t.array(RawLifeCycleReducerDefV10);\n  },\n  get RowLevelSecurity() {\n    return __t.array(RawRowLevelSecurityDefV9);\n  },\n  get CaseConversionPolicy() {\n    return CaseConversionPolicy;\n  },\n  get ExplicitNames() {\n    return ExplicitNames;\n  },\n});\nexport type RawModuleDefV10Section = __Infer<typeof RawModuleDefV10Section>;\n\nexport const RawModuleDefV8 = __t.object('RawModuleDefV8', {\n  get typespace() {\n    return Typespace;\n  },\n  get tables() {\n    return __t.array(TableDesc);\n  },\n  get reducers() {\n    return __t.array(ReducerDef);\n  },\n  get miscExports() {\n    return __t.array(MiscModuleExport);\n  },\n});\nexport type RawModuleDefV8 = __Infer<typeof RawModuleDefV8>;\n\nexport const RawModuleDefV9 = __t.object('RawModuleDefV9', {\n  get typespace() {\n    return Typespace;\n  },\n  get tables() {\n    return __t.array(RawTableDefV9);\n  },\n  get reducers() {\n    return __t.array(RawReducerDefV9);\n  },\n  get types() {\n    return __t.array(RawTypeDefV9);\n  },\n  get miscExports() {\n    return __t.array(RawMiscModuleExportV9);\n  },\n  get rowLevelSecurity() {\n    return __t.array(RawRowLevelSecurityDefV9);\n  },\n});\nexport type RawModuleDefV9 = __Infer<typeof RawModuleDefV9>;\n\nexport const RawProcedureDefV10 = __t.object('RawProcedureDefV10', {\n  sourceName: __t.string(),\n  get params() {\n    return ProductType;\n  },\n  get returnType() {\n    return AlgebraicType;\n  },\n  get visibility() {\n    return FunctionVisibility;\n  },\n});\nexport type RawProcedureDefV10 = __Infer<typeof RawProcedureDefV10>;\n\nexport const RawProcedureDefV9 = __t.object('RawProcedureDefV9', {\n  name: __t.string(),\n  get params() {\n    return ProductType;\n  },\n  get returnType() {\n    return AlgebraicType;\n  },\n});\nexport type RawProcedureDefV9 = __Infer<typeof RawProcedureDefV9>;\n\nexport const RawReducerDefV10 = __t.object('RawReducerDefV10', {\n  sourceName: __t.string(),\n  get params() {\n    return ProductType;\n  },\n  get visibility() {\n    return FunctionVisibility;\n  },\n  get okReturnType() {\n    return AlgebraicType;\n  },\n  get errReturnType() {\n    return AlgebraicType;\n  },\n});\nexport type RawReducerDefV10 = __Infer<typeof RawReducerDefV10>;\n\nexport const RawReducerDefV9 = __t.object('RawReducerDefV9', {\n  name: __t.string(),\n  get params() {\n    return ProductType;\n  },\n  get lifecycle() {\n    return __t.option(Lifecycle);\n  },\n});\nexport type RawReducerDefV9 = __Infer<typeof RawReducerDefV9>;\n\nexport const RawRowLevelSecurityDefV9 = __t.object('RawRowLevelSecurityDefV9', {\n  sql: __t.string(),\n});\nexport type RawRowLevelSecurityDefV9 = __Infer<typeof RawRowLevelSecurityDefV9>;\n\nexport const RawScheduleDefV10 = __t.object('RawScheduleDefV10', {\n  sourceName: __t.option(__t.string()),\n  tableName: __t.string(),\n  scheduleAtCol: __t.u16(),\n  functionName: __t.string(),\n});\nexport type RawScheduleDefV10 = __Infer<typeof RawScheduleDefV10>;\n\nexport const RawScheduleDefV9 = __t.object('RawScheduleDefV9', {\n  name: __t.option(__t.string()),\n  reducerName: __t.string(),\n  scheduledAtColumn: __t.u16(),\n});\nexport type RawScheduleDefV9 = __Infer<typeof RawScheduleDefV9>;\n\nexport const RawScopedTypeNameV10 = __t.object('RawScopedTypeNameV10', {\n  scope: __t.array(__t.string()),\n  sourceName: __t.string(),\n});\nexport type RawScopedTypeNameV10 = __Infer<typeof RawScopedTypeNameV10>;\n\nexport const RawScopedTypeNameV9 = __t.object('RawScopedTypeNameV9', {\n  scope: __t.array(__t.string()),\n  name: __t.string(),\n});\nexport type RawScopedTypeNameV9 = __Infer<typeof RawScopedTypeNameV9>;\n\nexport const RawSequenceDefV10 = __t.object('RawSequenceDefV10', {\n  sourceName: __t.option(__t.string()),\n  column: __t.u16(),\n  start: __t.option(__t.i128()),\n  minValue: __t.option(__t.i128()),\n  maxValue: __t.option(__t.i128()),\n  increment: __t.i128(),\n});\nexport type RawSequenceDefV10 = __Infer<typeof RawSequenceDefV10>;\n\nexport const RawSequenceDefV8 = __t.object('RawSequenceDefV8', {\n  sequenceName: __t.string(),\n  colPos: __t.u16(),\n  increment: __t.i128(),\n  start: __t.option(__t.i128()),\n  minValue: __t.option(__t.i128()),\n  maxValue: __t.option(__t.i128()),\n  allocated: __t.i128(),\n});\nexport type RawSequenceDefV8 = __Infer<typeof RawSequenceDefV8>;\n\nexport const RawSequenceDefV9 = __t.object('RawSequenceDefV9', {\n  name: __t.option(__t.string()),\n  column: __t.u16(),\n  start: __t.option(__t.i128()),\n  minValue: __t.option(__t.i128()),\n  maxValue: __t.option(__t.i128()),\n  increment: __t.i128(),\n});\nexport type RawSequenceDefV9 = __Infer<typeof RawSequenceDefV9>;\n\nexport const RawTableDefV10 = __t.object('RawTableDefV10', {\n  sourceName: __t.string(),\n  productTypeRef: __t.u32(),\n  primaryKey: __t.array(__t.u16()),\n  get indexes() {\n    return __t.array(RawIndexDefV10);\n  },\n  get constraints() {\n    return __t.array(RawConstraintDefV10);\n  },\n  get sequences() {\n    return __t.array(RawSequenceDefV10);\n  },\n  get tableType() {\n    return TableType;\n  },\n  get tableAccess() {\n    return TableAccess;\n  },\n  get defaultValues() {\n    return __t.array(RawColumnDefaultValueV10);\n  },\n  isEvent: __t.bool(),\n});\nexport type RawTableDefV10 = __Infer<typeof RawTableDefV10>;\n\nexport const RawTableDefV8 = __t.object('RawTableDefV8', {\n  tableName: __t.string(),\n  get columns() {\n    return __t.array(RawColumnDefV8);\n  },\n  get indexes() {\n    return __t.array(RawIndexDefV8);\n  },\n  get constraints() {\n    return __t.array(RawConstraintDefV8);\n  },\n  get sequences() {\n    return __t.array(RawSequenceDefV8);\n  },\n  tableType: __t.string(),\n  tableAccess: __t.string(),\n  scheduled: __t.option(__t.string()),\n});\nexport type RawTableDefV8 = __Infer<typeof RawTableDefV8>;\n\nexport const RawTableDefV9 = __t.object('RawTableDefV9', {\n  name: __t.string(),\n  productTypeRef: __t.u32(),\n  primaryKey: __t.array(__t.u16()),\n  get indexes() {\n    return __t.array(RawIndexDefV9);\n  },\n  get constraints() {\n    return __t.array(RawConstraintDefV9);\n  },\n  get sequences() {\n    return __t.array(RawSequenceDefV9);\n  },\n  get schedule() {\n    return __t.option(RawScheduleDefV9);\n  },\n  get tableType() {\n    return TableType;\n  },\n  get tableAccess() {\n    return TableAccess;\n  },\n});\nexport type RawTableDefV9 = __Infer<typeof RawTableDefV9>;\n\nexport const RawTypeDefV10 = __t.object('RawTypeDefV10', {\n  get sourceName() {\n    return RawScopedTypeNameV10;\n  },\n  ty: __t.u32(),\n  customOrdering: __t.bool(),\n});\nexport type RawTypeDefV10 = __Infer<typeof RawTypeDefV10>;\n\nexport const RawTypeDefV9 = __t.object('RawTypeDefV9', {\n  get name() {\n    return RawScopedTypeNameV9;\n  },\n  ty: __t.u32(),\n  customOrdering: __t.bool(),\n});\nexport type RawTypeDefV9 = __Infer<typeof RawTypeDefV9>;\n\nexport const RawUniqueConstraintDataV9 = __t.object(\n  'RawUniqueConstraintDataV9',\n  {\n    columns: __t.array(__t.u16()),\n  }\n);\nexport type RawUniqueConstraintDataV9 = __Infer<\n  typeof RawUniqueConstraintDataV9\n>;\n\nexport const RawViewDefV10 = __t.object('RawViewDefV10', {\n  sourceName: __t.string(),\n  index: __t.u32(),\n  isPublic: __t.bool(),\n  isAnonymous: __t.bool(),\n  get params() {\n    return ProductType;\n  },\n  get returnType() {\n    return AlgebraicType;\n  },\n});\nexport type RawViewDefV10 = __Infer<typeof RawViewDefV10>;\n\nexport const RawViewDefV9 = __t.object('RawViewDefV9', {\n  name: __t.string(),\n  index: __t.u32(),\n  isPublic: __t.bool(),\n  isAnonymous: __t.bool(),\n  get params() {\n    return ProductType;\n  },\n  get returnType() {\n    return AlgebraicType;\n  },\n});\nexport type RawViewDefV9 = __Infer<typeof RawViewDefV9>;\n\nexport const ReducerDef = __t.object('ReducerDef', {\n  name: __t.string(),\n  get args() {\n    return __t.array(ProductTypeElement);\n  },\n});\nexport type ReducerDef = __Infer<typeof ReducerDef>;\n\nexport const SumType = __t.object('SumType', {\n  get variants() {\n    return __t.array(SumTypeVariant);\n  },\n});\nexport type SumType = __Infer<typeof SumType>;\n\nexport const SumTypeVariant = __t.object('SumTypeVariant', {\n  name: __t.option(__t.string()),\n  get algebraicType() {\n    return AlgebraicType;\n  },\n});\nexport type SumTypeVariant = __Infer<typeof SumTypeVariant>;\n\n// The tagged union or sum type for the algebraic type `TableAccess`.\nexport const TableAccess = __t.enum('TableAccess', {\n  Public: __t.unit(),\n  Private: __t.unit(),\n});\nexport type TableAccess = __Infer<typeof TableAccess>;\n\nexport const TableDesc = __t.object('TableDesc', {\n  get schema() {\n    return RawTableDefV8;\n  },\n  data: __t.u32(),\n});\nexport type TableDesc = __Infer<typeof TableDesc>;\n\n// The tagged union or sum type for the algebraic type `TableType`.\nexport const TableType = __t.enum('TableType', {\n  System: __t.unit(),\n  User: __t.unit(),\n});\nexport type TableType = __Infer<typeof TableType>;\n\nexport const TypeAlias = __t.object('TypeAlias', {\n  name: __t.string(),\n  ty: __t.u32(),\n});\nexport type TypeAlias = __Infer<typeof TypeAlias>;\n\nexport const Typespace = __t.object('Typespace', {\n  get types() {\n    return __t.array(AlgebraicType);\n  },\n});\nexport type Typespace = __Infer<typeof Typespace>;\n\n// The tagged union or sum type for the algebraic type `ViewResultHeader`.\nexport const ViewResultHeader = __t.enum('ViewResultHeader', {\n  RowData: __t.unit(),\n  RawSql: __t.string(),\n});\nexport type ViewResultHeader = __Infer<typeof ViewResultHeader>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/binary_reader.ts",
    "content": "export default class BinaryReader {\n  /**\n   * The DataView used to read values from the binary data.\n   *\n   * Note: The DataView's `byteOffset` is relative to the beginning of the\n   * underlying ArrayBuffer, not the start of the provided Uint8Array input.\n   * This `BinaryReader`'s `#offset` field is used to track the current read position\n   * relative to the start of the provided Uint8Array input.\n   */\n  view: DataView;\n\n  /**\n   * Represents the offset (in bytes) relative to the start of the DataView\n   * and provided Uint8Array input.\n   *\n   * Note: This is *not* the absolute byte offset within the underlying ArrayBuffer.\n   */\n  offset: number = 0;\n\n  constructor(input: Uint8Array | DataView) {\n    this.view =\n      input instanceof DataView\n        ? input\n        : new DataView(input.buffer, input.byteOffset, input.byteLength);\n    this.offset = 0;\n  }\n\n  reset(view: DataView) {\n    this.view = view;\n    this.offset = 0;\n  }\n\n  get remaining(): number {\n    return this.view.byteLength - this.offset;\n  }\n\n  /** Ensure we have at least `n` bytes left to read */\n  #ensure(n: number): void {\n    if (this.offset + n > this.view.byteLength) {\n      throw new RangeError(\n        `Tried to read ${n} byte(s) at relative offset ${this.offset}, but only ${this.remaining} byte(s) remain`\n      );\n    }\n  }\n\n  readUInt8Array(): Uint8Array {\n    const length = this.readU32();\n    this.#ensure(length);\n    return this.readBytes(length);\n  }\n\n  readBool(): boolean {\n    const value = this.view.getUint8(this.offset);\n    this.offset += 1;\n    return value !== 0;\n  }\n\n  readByte(): number {\n    const value = this.view.getUint8(this.offset);\n    this.offset += 1;\n    return value;\n  }\n\n  readBytes(length: number): Uint8Array {\n    // Create a Uint8Array view over the DataView's buffer at the current offset\n    // The #view.buffer is the whole ArrayBuffer, so we need to account for the\n    // #view's starting position in that buffer (#view.byteOffset) and the current #offset\n    const array = new Uint8Array(\n      this.view.buffer,\n      this.view.byteOffset + this.offset,\n      length\n    );\n    this.offset += length;\n    return array;\n  }\n\n  readI8(): number {\n    const value = this.view.getInt8(this.offset);\n    this.offset += 1;\n    return value;\n  }\n\n  readU8(): number {\n    return this.readByte();\n  }\n\n  readI16(): number {\n    const value = this.view.getInt16(this.offset, true);\n    this.offset += 2;\n    return value;\n  }\n\n  readU16(): number {\n    const value = this.view.getUint16(this.offset, true);\n    this.offset += 2;\n    return value;\n  }\n\n  readI32(): number {\n    const value = this.view.getInt32(this.offset, true);\n    this.offset += 4;\n    return value;\n  }\n\n  readU32(): number {\n    const value = this.view.getUint32(this.offset, true);\n    this.offset += 4;\n    return value;\n  }\n\n  readI64(): bigint {\n    const value = this.view.getBigInt64(this.offset, true);\n    this.offset += 8;\n    return value;\n  }\n\n  readU64(): bigint {\n    const value = this.view.getBigUint64(this.offset, true);\n    this.offset += 8;\n    return value;\n  }\n\n  readU128(): bigint {\n    const lowerPart = this.view.getBigUint64(this.offset, true);\n    const upperPart = this.view.getBigUint64(this.offset + 8, true);\n    this.offset += 16;\n\n    return (upperPart << BigInt(64)) + lowerPart;\n  }\n\n  readI128(): bigint {\n    const lowerPart = this.view.getBigUint64(this.offset, true);\n    const upperPart = this.view.getBigInt64(this.offset + 8, true);\n    this.offset += 16;\n\n    return (upperPart << BigInt(64)) + lowerPart;\n  }\n\n  readU256(): bigint {\n    const p0 = this.view.getBigUint64(this.offset, true);\n    const p1 = this.view.getBigUint64(this.offset + 8, true);\n    const p2 = this.view.getBigUint64(this.offset + 16, true);\n    const p3 = this.view.getBigUint64(this.offset + 24, true);\n    this.offset += 32;\n\n    return (\n      (p3 << BigInt(3 * 64)) +\n      (p2 << BigInt(2 * 64)) +\n      (p1 << BigInt(1 * 64)) +\n      p0\n    );\n  }\n\n  readI256(): bigint {\n    const p0 = this.view.getBigUint64(this.offset, true);\n    const p1 = this.view.getBigUint64(this.offset + 8, true);\n    const p2 = this.view.getBigUint64(this.offset + 16, true);\n    const p3 = this.view.getBigInt64(this.offset + 24, true);\n    this.offset += 32;\n\n    return (\n      (p3 << BigInt(3 * 64)) +\n      (p2 << BigInt(2 * 64)) +\n      (p1 << BigInt(1 * 64)) +\n      p0\n    );\n  }\n\n  readF32(): number {\n    const value = this.view.getFloat32(this.offset, true);\n    this.offset += 4;\n    return value;\n  }\n\n  readF64(): number {\n    const value = this.view.getFloat64(this.offset, true);\n    this.offset += 8;\n    return value;\n  }\n\n  readString(): string {\n    const uint8Array = this.readUInt8Array();\n    return new TextDecoder('utf-8').decode(uint8Array);\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/binary_writer.ts",
    "content": "import { fromByteArray } from 'base64-js';\n\nconst ArrayBufferPrototypeTransfer =\n  ArrayBuffer.prototype.transfer ??\n  function (this: ArrayBuffer, newByteLength) {\n    if (newByteLength === undefined) {\n      return this.slice();\n    } else if (newByteLength <= this.byteLength) {\n      return this.slice(0, newByteLength);\n    } else {\n      const copy = new Uint8Array(newByteLength);\n      copy.set(new Uint8Array(this));\n      return copy.buffer;\n    }\n  };\n\nexport class ResizableBuffer {\n  buffer: ArrayBuffer;\n  view: DataView;\n\n  constructor(init: number | ArrayBuffer) {\n    this.buffer = typeof init === 'number' ? new ArrayBuffer(init) : init;\n    this.view = new DataView(this.buffer);\n  }\n\n  get capacity(): number {\n    return this.buffer.byteLength;\n  }\n\n  grow(newSize: number) {\n    if (newSize <= this.buffer.byteLength) return;\n    this.buffer = ArrayBufferPrototypeTransfer.call(this.buffer, newSize);\n    this.view = new DataView(this.buffer);\n  }\n}\n\nexport default class BinaryWriter {\n  buffer: ResizableBuffer;\n  offset: number = 0;\n\n  constructor(init: number | ResizableBuffer) {\n    this.buffer = typeof init === 'number' ? new ResizableBuffer(init) : init;\n  }\n\n  clear() {\n    this.offset = 0;\n  }\n\n  reset(buffer: ResizableBuffer) {\n    this.buffer = buffer;\n    this.offset = 0;\n  }\n\n  expandBuffer(additionalCapacity: number): void {\n    const minCapacity = this.offset + additionalCapacity + 1;\n    if (minCapacity <= this.buffer.capacity) return;\n    let newCapacity = this.buffer.capacity * 2;\n    if (newCapacity < minCapacity) newCapacity = minCapacity;\n    this.buffer.grow(newCapacity);\n  }\n\n  toBase64(): string {\n    return fromByteArray(this.getBuffer());\n  }\n\n  getBuffer(): Uint8Array {\n    return new Uint8Array(this.buffer.buffer, 0, this.offset);\n  }\n\n  get view() {\n    return this.buffer.view;\n  }\n\n  writeUInt8Array(value: Uint8Array): void {\n    const length = value.length;\n\n    this.expandBuffer(4 + length);\n\n    this.writeU32(length);\n    new Uint8Array(this.buffer.buffer, this.offset).set(value);\n    this.offset += length;\n  }\n\n  writeBool(value: boolean): void {\n    this.expandBuffer(1);\n    this.view.setUint8(this.offset, value ? 1 : 0);\n    this.offset += 1;\n  }\n\n  writeByte(value: number): void {\n    this.expandBuffer(1);\n    this.view.setUint8(this.offset, value);\n    this.offset += 1;\n  }\n\n  writeI8(value: number): void {\n    this.expandBuffer(1);\n    this.view.setInt8(this.offset, value);\n    this.offset += 1;\n  }\n\n  writeU8(value: number): void {\n    this.expandBuffer(1);\n    this.view.setUint8(this.offset, value);\n    this.offset += 1;\n  }\n\n  writeI16(value: number): void {\n    this.expandBuffer(2);\n    this.view.setInt16(this.offset, value, true);\n    this.offset += 2;\n  }\n\n  writeU16(value: number): void {\n    this.expandBuffer(2);\n    this.view.setUint16(this.offset, value, true);\n    this.offset += 2;\n  }\n\n  writeI32(value: number): void {\n    this.expandBuffer(4);\n    this.view.setInt32(this.offset, value, true);\n    this.offset += 4;\n  }\n\n  writeU32(value: number): void {\n    this.expandBuffer(4);\n    this.view.setUint32(this.offset, value, true);\n    this.offset += 4;\n  }\n\n  writeI64(value: bigint): void {\n    this.expandBuffer(8);\n    this.view.setBigInt64(this.offset, value, true);\n    this.offset += 8;\n  }\n\n  writeU64(value: bigint): void {\n    this.expandBuffer(8);\n    this.view.setBigUint64(this.offset, value, true);\n    this.offset += 8;\n  }\n\n  writeU128(value: bigint): void {\n    this.expandBuffer(16);\n    const lowerPart = value & BigInt('0xFFFFFFFFFFFFFFFF');\n    const upperPart = value >> BigInt(64);\n    this.view.setBigUint64(this.offset, lowerPart, true);\n    this.view.setBigUint64(this.offset + 8, upperPart, true);\n    this.offset += 16;\n  }\n\n  writeI128(value: bigint): void {\n    this.expandBuffer(16);\n    const lowerPart = value & BigInt('0xFFFFFFFFFFFFFFFF');\n    const upperPart = value >> BigInt(64);\n    this.view.setBigInt64(this.offset, lowerPart, true);\n    this.view.setBigInt64(this.offset + 8, upperPart, true);\n    this.offset += 16;\n  }\n\n  writeU256(value: bigint): void {\n    this.expandBuffer(32);\n    const low_64_mask = BigInt('0xFFFFFFFFFFFFFFFF');\n    const p0 = value & low_64_mask;\n    const p1 = (value >> BigInt(64 * 1)) & low_64_mask;\n    const p2 = (value >> BigInt(64 * 2)) & low_64_mask;\n    const p3 = value >> BigInt(64 * 3);\n    this.view.setBigUint64(this.offset + 8 * 0, p0, true);\n    this.view.setBigUint64(this.offset + 8 * 1, p1, true);\n    this.view.setBigUint64(this.offset + 8 * 2, p2, true);\n    this.view.setBigUint64(this.offset + 8 * 3, p3, true);\n    this.offset += 32;\n  }\n\n  writeI256(value: bigint): void {\n    this.expandBuffer(32);\n    const low_64_mask = BigInt('0xFFFFFFFFFFFFFFFF');\n    const p0 = value & low_64_mask;\n    const p1 = (value >> BigInt(64 * 1)) & low_64_mask;\n    const p2 = (value >> BigInt(64 * 2)) & low_64_mask;\n    const p3 = value >> BigInt(64 * 3);\n    this.view.setBigUint64(this.offset + 8 * 0, p0, true);\n    this.view.setBigUint64(this.offset + 8 * 1, p1, true);\n    this.view.setBigUint64(this.offset + 8 * 2, p2, true);\n    this.view.setBigInt64(this.offset + 8 * 3, p3, true);\n    this.offset += 32;\n  }\n\n  writeF32(value: number): void {\n    this.expandBuffer(4);\n    this.view.setFloat32(this.offset, value, true);\n    this.offset += 4;\n  }\n\n  writeF64(value: number): void {\n    this.expandBuffer(8);\n    this.view.setFloat64(this.offset, value, true);\n    this.offset += 8;\n  }\n\n  writeString(value: string): void {\n    const encoder = new TextEncoder();\n    const encodedString = encoder.encode(value);\n    this.writeUInt8Array(encodedString);\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/connection_id.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\nimport { hexStringToU128, u128ToHexString, u128ToUint8Array } from './util';\n\nexport type ConnectionIdAlgebraicType = {\n  tag: 'Product';\n  value: {\n    elements: [{ name: '__connection_id__'; algebraicType: { tag: 'U128' } }];\n  };\n};\n\n/**\n * A unique identifier for a client connected to a database.\n */\nexport class ConnectionId {\n  __connection_id__: bigint;\n\n  /**\n   * Creates a new `ConnectionId`.\n   */\n  constructor(data: bigint) {\n    this.__connection_id__ = data;\n  }\n\n  /**\n   * Get the algebraic type representation of the {@link ConnectionId} type.\n   * @returns The algebraic type representation of the type.\n   */\n  static getAlgebraicType(): ConnectionIdAlgebraicType {\n    return AlgebraicType.Product({\n      elements: [\n        { name: '__connection_id__', algebraicType: AlgebraicType.U128 },\n      ],\n    });\n  }\n\n  isZero(): boolean {\n    return this.__connection_id__ === BigInt(0);\n  }\n\n  static nullIfZero(addr: ConnectionId): ConnectionId | null {\n    if (addr.isZero()) {\n      return null;\n    } else {\n      return addr;\n    }\n  }\n\n  static random(): ConnectionId {\n    function randomU8(): number {\n      return Math.floor(Math.random() * 0xff);\n    }\n    let result = BigInt(0);\n    for (let i = 0; i < 16; i++) {\n      result = (result << BigInt(8)) | BigInt(randomU8());\n    }\n    return new ConnectionId(result);\n  }\n\n  /**\n   * Compare two connection IDs for equality.\n   */\n  isEqual(other: ConnectionId): boolean {\n    return this.__connection_id__ == other.__connection_id__;\n  }\n\n  /**\n   * Check if two connection IDs are equal.\n   */\n  equals(other: ConnectionId): boolean {\n    return this.isEqual(other);\n  }\n\n  /**\n   * Print the connection ID as a hexadecimal string.\n   */\n  toHexString(): string {\n    return u128ToHexString(this.__connection_id__);\n  }\n\n  /**\n   * Convert the connection ID to a Uint8Array.\n   */\n  toUint8Array(): Uint8Array {\n    return u128ToUint8Array(this.__connection_id__);\n  }\n\n  /**\n   * Parse a connection ID from a hexadecimal string.\n   */\n  static fromString(str: string): ConnectionId {\n    return new ConnectionId(hexStringToU128(str));\n  }\n\n  static fromStringOrNull(str: string): ConnectionId | null {\n    const addr = ConnectionId.fromString(str);\n    if (addr.isZero()) {\n      return null;\n    } else {\n      return addr;\n    }\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/constraints.ts",
    "content": "import type { table, UntypedTableDef } from './table';\nimport type { ColumnMetadata } from './type_builders';\n\n/**\n * A helper type to determine if all columns in an index are unique.\n */\nexport type AllUnique<\n  TableDef extends UntypedTableDef,\n  Columns extends ReadonlyArray<keyof TableDef['columns']>,\n> = Columns extends readonly [\n  infer Head extends keyof TableDef['columns'],\n  ...infer Tail extends ReadonlyArray<keyof TableDef['columns']>,\n]\n  ? ColumnIsUnique<TableDef['columns'][Head]['columnMetadata']> extends true\n    ? AllUnique<TableDef, Tail>\n    : false\n  : true;\n\n/**\n * A helper type to determine if a column is unique based on its metadata.\n * A column is considered unique if it has either `isUnique` or `isPrimaryKey` set to true in its metadata.\n * @template M - The column metadata to check.\n * @returns `true` if the column is unique, otherwise `false`.\n * @example\n * ```typescript\n * type Meta1 = { isUnique: true };\n * type Meta2 = { isPrimaryKey: true };\n * type Meta3 = { isUnique: false };\n * type Meta4 = {};\n * type Result1 = ColumnIsUnique<Meta1>; // true\n * type Result2 = ColumnIsUnique<Meta2>; // true\n * type Result3 = ColumnIsUnique<Meta3>; // false\n * type Result4 = ColumnIsUnique<Meta4>; // false\n * ```\n */\nexport type ColumnIsUnique<M extends ColumnMetadata<any>> = M extends\n  | { isUnique: true }\n  | { isPrimaryKey: true }\n  ? true\n  : false;\n\n/**\n * Constraint helper type used *inside* {@link table} to enforce the type\n * of constraint definitions.\n */\nexport type ConstraintOpts<AllowedCol extends string> = {\n  name?: string;\n} & { constraint: 'unique'; columns: [AllowedCol] };\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/errors.ts",
    "content": "/**\n * An error thrown by a reducer that indicates a problem to the sender.\n *\n * When this error is thrown by a reducer, the sender will be notified\n * that the reducer failed gracefully with the given message.\n */\nexport class SenderError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n  get name(): string {\n    return 'SenderError';\n  }\n}\n\n/**\n * An internal reducer error returned by the server runtime.\n */\nexport class InternalError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n  get name(): string {\n    return 'InternalError';\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/filter.ts",
    "content": "import type { RowType, UntypedTableDef } from './table';\nimport { Uuid } from './uuid';\n\nexport type Value = string | number | boolean | Uuid;\n\nexport type Expr<Column extends string> =\n  | { type: 'eq'; key: Column; value: Value }\n  | { type: 'and'; children: Expr<Column>[] }\n  | { type: 'or'; children: Expr<Column>[] };\n\nexport const eq = <Column extends string>(\n  key: Column,\n  value: Value\n): Expr<Column> => ({ type: 'eq', key, value });\n\nexport const and = <Column extends string>(\n  ...children: Expr<Column>[]\n): Expr<Column> => {\n  const flat: Expr<Column>[] = [];\n  for (const c of children) {\n    if (!c) continue;\n    if (c.type === 'and') flat.push(...c.children);\n    else flat.push(c);\n  }\n  const pruned = flat.filter(Boolean);\n  if (pruned.length === 0) return { type: 'and', children: [] };\n  if (pruned.length === 1) return pruned[0];\n  return { type: 'and', children: pruned };\n};\n\nexport const or = <Column extends string>(\n  ...children: Expr<Column>[]\n): Expr<Column> => {\n  const flat: Expr<Column>[] = [];\n  for (const c of children) {\n    if (!c) continue;\n    if (c.type === 'or') flat.push(...c.children);\n    else flat.push(c);\n  }\n  const pruned = flat.filter(Boolean);\n  if (pruned.length === 0) return { type: 'or', children: [] };\n  if (pruned.length === 1) return pruned[0];\n  return { type: 'or', children: pruned };\n};\n\nexport const isEq = <Column extends string>(\n  e: Expr<Column>\n): e is Extract<Expr<Column>, { type: 'eq' }> => e.type === 'eq';\nexport const isAnd = <Column extends string>(\n  e: Expr<Column>\n): e is Extract<Expr<Column>, { type: 'and' }> => e.type === 'and';\nexport const isOr = <Column extends string>(\n  e: Expr<Column>\n): e is Extract<Expr<Column>, { type: 'or' }> => e.type === 'or';\n\nexport function evaluate<Column extends string>(\n  expr: Expr<Column>,\n  row: Record<Column, any>\n): boolean {\n  switch (expr.type) {\n    case 'eq': {\n      // The actual value of the Column\n      const v = row[expr.key];\n      if (\n        typeof v === 'string' ||\n        typeof v === 'number' ||\n        typeof v === 'boolean'\n      ) {\n        return v === expr.value;\n      }\n      if (typeof v === 'object') {\n        // Value of the Column and passed Value are both a Uuid so do an integer comparison.\n        if (v instanceof Uuid && expr.value instanceof Uuid) {\n          return v.asBigInt() === expr.value.asBigInt();\n        }\n        // Value of the Column is a Uuid but passed Value is a String so compare them via string.\n        if (v instanceof Uuid && typeof expr.value === 'string') {\n          return v.toString() === expr.value;\n        }\n      }\n      return false;\n    }\n    case 'and':\n      return (\n        expr.children.length === 0 || expr.children.every(c => evaluate(c, row))\n      );\n    case 'or':\n      return (\n        expr.children.length !== 0 && expr.children.some(c => evaluate(c, row))\n      );\n  }\n}\n\nfunction formatValue(v: Value): string {\n  switch (typeof v) {\n    case 'string':\n      return `'${v.replace(/'/g, \"''\")}'`;\n    case 'number':\n      return Number.isFinite(v) ? String(v) : `'${String(v)}'`;\n    case 'boolean':\n      return v ? 'TRUE' : 'FALSE';\n    case 'object': {\n      if (v instanceof Uuid) {\n        return `'${v.toString()}'`;\n      }\n\n      return '';\n    }\n  }\n}\n\nfunction escapeIdent(id: string): string {\n  if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(id)) return id;\n  return `\"${id.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction parenthesize(s: string): string {\n  if (!s.includes(' AND ') && !s.includes(' OR ')) return s;\n  return `(${s})`;\n}\n\nexport function toString<TableDef extends UntypedTableDef>(\n  tableDef: TableDef,\n  expr: Expr<ColumnsFromRow<RowType<TableDef>>>\n): string {\n  switch (expr.type) {\n    case 'eq': {\n      const key = tableDef.columns[expr.key].columnMetadata.name ?? expr.key;\n      return `${escapeIdent(key)} = ${formatValue(expr.value)}`;\n    }\n    case 'and':\n      return parenthesize(\n        expr.children.map(expr => toString(tableDef, expr)).join(' AND ')\n      );\n    case 'or':\n      return parenthesize(\n        expr.children.map(expr => toString(tableDef, expr)).join(' OR ')\n      );\n  }\n}\n\n/**\n * This is just the identity function to make things look like SQL.\n * @param expr\n * @returns\n */\nexport function where<Column extends string>(expr: Expr<Column>): Expr<Column> {\n  return expr;\n}\n\ntype MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';\n\nexport function classifyMembership<\n  Col extends string,\n  R extends Record<string, unknown>,\n>(where: Expr<Col> | undefined, oldRow: R, newRow: R): MembershipChange {\n  // No filter: everything is in, so updates are always \"stayIn\".\n  if (!where) {\n    return 'stayIn';\n  }\n\n  const oldIn = evaluate(where, oldRow);\n  const newIn = evaluate(where, newRow);\n\n  if (oldIn && !newIn) {\n    return 'leave';\n  }\n  if (!oldIn && newIn) {\n    return 'enter';\n  }\n  if (oldIn && newIn) {\n    return 'stayIn';\n  }\n  return 'stayOut';\n}\n\n/**\n * Extracts the column names from a RowType whose values are of type Value.\n * Note that this will exclude columns that are of type object, array, etc.\n */\nexport type ColumnsFromRow<R> = {\n  [K in keyof R]-?: R[K] extends Value | undefined ? K : never;\n}[keyof R] &\n  string;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/http_types.ts",
    "content": "export {\n  HttpHeaderPair,\n  HttpHeaders,\n  HttpMethod,\n  HttpRequest,\n  HttpResponse,\n  HttpVersion,\n} from './autogen/types';\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/identity.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\nimport { hexStringToU256, u256ToHexString, u256ToUint8Array } from './util';\n\nexport type IdentityAlgebraicType = {\n  tag: 'Product';\n  value: {\n    elements: [{ name: '__identity__'; algebraicType: { tag: 'U256' } }];\n  };\n};\n\n/**\n * A unique identifier for a user connected to a database.\n */\nexport class Identity {\n  __identity__: bigint;\n\n  /**\n   * Creates a new `Identity`.\n   *\n   * `data` can be a hexadecimal string or a `bigint`.\n   */\n  constructor(data: string | bigint) {\n    // we get a JSON with __identity__ when getting a token with a JSON API\n    // and an bigint when using BSATN\n    this.__identity__ = typeof data === 'string' ? hexStringToU256(data) : data;\n  }\n\n  /**\n   * Get the algebraic type representation of the {@link Identity} type.\n   * @returns The algebraic type representation of the type.\n   */\n  static getAlgebraicType(): IdentityAlgebraicType {\n    return AlgebraicType.Product({\n      elements: [{ name: '__identity__', algebraicType: AlgebraicType.U256 }],\n    });\n  }\n\n  /**\n   * Check if two identities are equal.\n   */\n  isEqual(other: Identity): boolean {\n    return this.toHexString() === other.toHexString();\n  }\n\n  /**\n   * Check if two identities are equal.\n   */\n  equals(other: Identity): boolean {\n    return this.isEqual(other);\n  }\n\n  /**\n   * Print the identity as a hexadecimal string.\n   */\n  toHexString(): string {\n    return u256ToHexString(this.__identity__);\n  }\n\n  /**\n   * Convert the address to a Uint8Array.\n   */\n  toUint8Array(): Uint8Array {\n    return u256ToUint8Array(this.__identity__);\n  }\n\n  /**\n   * Parse an Identity from a hexadecimal string.\n   */\n  static fromString(str: string): Identity {\n    return new Identity(str);\n  }\n\n  /**\n   * Zero identity (0x0000000000000000000000000000000000000000000000000000000000000000)\n   */\n  static zero(): Identity {\n    return new Identity(0n);\n  }\n\n  toString(): string {\n    return this.toHexString();\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/indexes.ts",
    "content": "import type { RowType, table, UntypedTableDef } from './table';\nimport type { ColumnMetadata, IndexTypes } from './type_builders';\nimport type { CollapseTuple, Prettify } from './type_util';\nimport { Range } from '../server/range';\nimport type { ColumnIsUnique } from './constraints';\n\n/**\n * Index helper type used *inside* {@link table} to enforce that only\n * existing column names are referenced.\n */\nexport type IndexOpts<AllowedCol extends string> = {\n  accessor: string;\n  name?: string;\n} & (\n  | { algorithm: 'btree'; columns: readonly AllowedCol[] }\n  | { algorithm: 'hash'; columns: readonly AllowedCol[] }\n  | { algorithm: 'direct'; column: AllowedCol }\n);\n\n/**\n * An untyped representation of an index definition.\n */\nexport type UntypedIndex<AllowedCol extends string> = {\n  name: string;\n  unique?: boolean;\n  algorithm: 'btree' | 'direct' | 'hash';\n  columns: readonly AllowedCol[];\n};\n\n/**\n * A helper type to extract the column names from an index definition.\n */\nexport type IndexColumns<I extends IndexOpts<any>> = I extends {\n  columns: readonly string[];\n}\n  ? readonly [...I['columns']]\n  : I extends { column: infer Name extends string }\n    ? readonly [Name]\n    : never;\n\n/**\n * A type representing the indexes defined on a table.\n */\nexport type Indexes<\n  TableDef extends UntypedTableDef,\n  I extends Record<string, UntypedIndex<keyof TableDef['columns'] & string>>,\n> = {\n  [k in keyof I]: Index<TableDef, I[k]>;\n};\n\n/**\n * Check whether every column in an index is a primary key column.\n */\ntype AllColumnsPrimaryKey<\n  TableDef extends UntypedTableDef,\n  Columns extends readonly string[],\n> = Columns extends readonly [\n  infer Head extends keyof TableDef['columns'] & string,\n  ...infer Tail extends readonly string[],\n]\n  ? TableDef['columns'][Head]['columnMetadata'] extends { isPrimaryKey: true }\n    ? AllColumnsPrimaryKey<TableDef, Tail>\n    : false\n  : true;\n\n/**\n * A type representing a database index,\n * which can either be unique or filter for a single value or range of values.\n * Unique indexes on primary key columns additionally support `update`.\n */\nexport type Index<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> = I['unique'] extends true\n  ? AllColumnsPrimaryKey<TableDef, I['columns']> extends true\n    ? UniqueIndex<TableDef, I> & {\n        update(row: Prettify<RowType<TableDef>>): Prettify<RowType<TableDef>>;\n      }\n    : UniqueIndex<TableDef, I>\n  : I['algorithm'] extends 'hash'\n    ? PointIndex<TableDef, I>\n    : RangedIndex<TableDef, I>;\n\n/**\n * A type representing a collection of read-only indexes defined on a table.\n */\nexport type ReadonlyIndexes<\n  TableDef extends UntypedTableDef,\n  I extends Record<string, UntypedIndex<keyof TableDef['columns'] & string>>,\n> = {\n  [k in keyof I]: ReadonlyIndex<TableDef, I[k]>;\n};\n\n/**\n * A type representing a read-only database index,\n * which can be either unique, pointed, or ranged.\n * This type only exposes read-only operations.\n */\nexport type ReadonlyIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> = I['unique'] extends true\n  ? ReadonlyUniqueIndex<TableDef, I>\n  : I['algorithm'] extends 'hash'\n    ? ReadonlyPointIndex<TableDef, I>\n    : ReadonlyRangedIndex<TableDef, I>;\n\n/**\n * A type representing a read-only unique index on a database table.\n */\nexport type ReadonlyUniqueIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> = {\n  find(colVal: IndexVal<TableDef, I>): RowType<TableDef> | null;\n};\n\n/**\n * A type representing a unique index on a database table.\n * Unique indexes enforce that the indexed columns contain unique values.\n */\nexport interface UniqueIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> extends ReadonlyUniqueIndex<TableDef, I> {\n  delete(colVal: IndexVal<TableDef, I>): boolean;\n}\n\n/**\n * A type representing a read-only point index on a database table.\n */\nexport interface ReadonlyPointIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> {\n  filter(\n    point: IndexVal<TableDef, I>\n  ): IteratorObject<Prettify<RowType<TableDef>>, undefined>;\n}\n\n/**\n * A type representing a point index on a database table.\n * Point indexes allow for exact match queries on the indexed columns.\n */\nexport interface PointIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> extends ReadonlyPointIndex<TableDef, I> {\n  delete(point: IndexVal<TableDef, I>): number;\n}\n\n/**\n * A type representing a read-only ranged index on a database table.\n */\nexport interface ReadonlyRangedIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> {\n  filter(\n    range: IndexScanRangeBounds<TableDef, I>\n  ): IteratorObject<Prettify<RowType<TableDef>>, undefined>;\n}\n\n/**\n * A type representing a ranged index on a database table.\n * Ranged indexes allow for range queries on the indexed columns.\n */\nexport interface RangedIndex<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> extends ReadonlyRangedIndex<TableDef, I> {\n  delete(range: IndexScanRangeBounds<TableDef, I>): number;\n}\n\n/**\n * A helper type to extract the value type of an index based on the table definition and index definition.\n * This type constructs a tuple of the types of the columns that make up the index.\n */\nexport type IndexVal<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> = CollapseTuple<_IndexVal<TableDef, I['columns']>>;\n\n/**\n * A helper type to extract the types of the columns that make up an index.\n */\ntype _IndexVal<\n  TableDef extends UntypedTableDef,\n  Columns extends readonly string[],\n> = Columns extends readonly [\n  infer Head extends string,\n  ...infer Tail extends readonly string[],\n]\n  ? [\n      TableDef['columns'][Head]['typeBuilder']['type'],\n      ..._IndexVal<TableDef, Tail>,\n    ]\n  : [];\n\n/**\n * A helper type to define the bounds for scanning an index.\n * This type allows for specifying exact values or ranges for each column in the index.\n * It supports omitting trailing columns if the index is multi-column.\n */\nexport type IndexScanRangeBounds<\n  TableDef extends UntypedTableDef,\n  I extends UntypedIndex<keyof TableDef['columns'] & string>,\n> = _IndexScanRangeBounds<_IndexVal<TableDef, I['columns']>>;\n\n/**\n * A helper type to define the bounds for scanning an index.\n * This type allows for specifying exact values or ranges for each column in the index.\n * It supports omitting trailing columns if the index is multi-column.\n * This version only allows omitting the array if the index is single-column to avoid ambiguity.\n */\ntype _IndexScanRangeBounds<Columns extends readonly any[]> = Columns extends [\n  infer Term,\n]\n  ? Term | Range<Term>\n  : _IndexScanRangeBoundsCase<Columns>;\n\n/**\n * A helper type to define the bounds for scanning an index.\n * This type allows for specifying exact values or ranges for each column in the index.\n * It supports omitting trailing columns if the index is multi-column.\n */\ntype _IndexScanRangeBoundsCase<Columns extends readonly any[]> =\n  Columns extends [...infer Prefix, infer Term]\n    ? readonly [...Prefix, Term | Range<Term>] | _IndexScanRangeBounds<Prefix>\n    : never;\n\n/**\n * A helper type representing a column index definition.\n */\nexport type ColumnIndex<\n  Name extends string,\n  M extends ColumnMetadata<any>,\n> = Prettify<\n  {\n    name: Name;\n    unique: ColumnIsUnique<M>;\n    columns: readonly [Name];\n    algorithm: 'btree' | 'direct' | 'hash';\n  } & (M extends {\n    indexType: infer I extends NonNullable<IndexTypes>;\n  }\n    ? { algorithm: I }\n    : ColumnIsUnique<M> extends true\n      ? { algorithm: 'btree' }\n      : never)\n>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/option.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\n\nexport type OptionAlgebraicType<T extends AlgebraicType = AlgebraicType> = {\n  tag: 'Sum';\n  value: {\n    variants: [\n      { name: 'some'; algebraicType: T },\n      {\n        name: 'none';\n        algebraicType: { tag: 'Product'; value: { elements: [] } };\n      },\n    ];\n  };\n};\n\nexport const Option: {\n  getAlgebraicType<T extends AlgebraicType = AlgebraicType>(\n    innerType: T\n  ): OptionAlgebraicType<T>;\n} = {\n  getAlgebraicType<T extends AlgebraicType = AlgebraicType>(\n    innerType: T\n  ): OptionAlgebraicType<T> {\n    return AlgebraicType.Sum({\n      variants: [\n        { name: 'some', algebraicType: innerType },\n        {\n          name: 'none',\n          algebraicType: AlgebraicType.Product({ elements: [] }),\n        },\n      ],\n    });\n  },\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/query.ts",
    "content": "import { ConnectionId } from './connection_id';\nimport { Identity } from './identity';\nimport type { ColumnIndex, IndexColumns, IndexOpts } from './indexes';\nimport type { UntypedSchemaDef } from './schema';\nimport type { UntypedTableSchema } from './table_schema';\nimport { Timestamp } from './timestamp';\nimport type {\n  ColumnBuilder,\n  ColumnMetadata,\n  RowBuilder,\n  TypeBuilder,\n} from './type_builders';\nimport type { Values } from './type_util';\nimport type { Bool as SatsBool } from './algebraic_type_variants';\n\n/**\n * Helper to get the set of table names.\n */\nexport type TableNames<SchemaDef extends UntypedSchemaDef> = Values<\n  SchemaDef['tables']\n>['accessorName'] &\n  string;\n\n/** helper: pick the table def object from the schema by its name */\nexport type TableDefByName<\n  SchemaDef extends UntypedSchemaDef,\n  Name extends TableNames<SchemaDef>,\n> = Extract<Values<SchemaDef['tables']>, { accessorName: Name }>;\n\n// internal only — NOT exported.\n// This is how we make sure queries are only created with our helpers.\nconst QueryBrand = Symbol('QueryBrand');\n\nexport interface TableTypedQuery<TableDef extends TypedTableDef> {\n  readonly [QueryBrand]: true;\n  readonly __table?: TableDef;\n}\n\nexport interface RowTypedQuery<Row, ST> {\n  readonly [QueryBrand]: true;\n  // Phantom type to track the row type.\n  readonly __row?: Row;\n  readonly __algebraicType?: ST;\n}\n\nexport type Query<TableDef extends TypedTableDef> = RowTypedQuery<\n  RowType<TableDef>,\n  TableDef['rowType']\n>;\n\nexport const isRowTypedQuery = (val: unknown): val is RowTypedQuery<any, any> =>\n  !!val && typeof val === 'object' && QueryBrand in (val as object);\n\nexport const isTypedQuery = (val: unknown): val is TableTypedQuery<any> =>\n  !!val && typeof val === 'object' && QueryBrand in (val as object);\n\nexport function toSql(q: Query<any>): string {\n  return (q as unknown as { toSql(): string }).toSql();\n}\n\n// A query builder with a single table.\ntype From<TableDef extends TypedTableDef> = RowTypedQuery<\n  RowType<TableDef>,\n  TableDef['rowType']\n> &\n  Readonly<{\n    toSql(): string;\n    where(\n      predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>\n    ): From<TableDef>;\n    rightSemijoin<RightTable extends TypedTableDef>(\n      other: TableRef<RightTable>,\n      on: (\n        left: IndexedRowExpr<TableDef>,\n        right: IndexedRowExpr<RightTable>\n      ) => BooleanExpr<TableDef | RightTable>\n    ): SemijoinBuilder<RightTable>;\n    leftSemijoin<RightTable extends TypedTableDef>(\n      other: TableRef<RightTable>,\n      on: (\n        left: IndexedRowExpr<TableDef>,\n        right: IndexedRowExpr<RightTable>\n      ) => BooleanExpr<TableDef | RightTable>\n    ): SemijoinBuilder<TableDef>;\n    /** @deprecated No longer needed — builder is already a valid query. */\n    build(): Query<TableDef>;\n  }>;\n\n// A query builder with a semijoin.\ntype SemijoinBuilder<TableDef extends TypedTableDef> = RowTypedQuery<\n  RowType<TableDef>,\n  TableDef['rowType']\n> &\n  Readonly<{\n    toSql(): string;\n    where(\n      predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>\n    ): SemijoinBuilder<TableDef>;\n    /** @deprecated No longer needed — builder is already a valid query. */\n    build(): Query<TableDef>;\n  }>;\n\nclass SemijoinImpl<TableDef extends TypedTableDef>\n  implements SemijoinBuilder<TableDef>, TableTypedQuery<TableDef>\n{\n  readonly [QueryBrand] = true;\n  readonly type = 'semijoin' as const;\n  constructor(\n    readonly sourceQuery: FromBuilder<TableDef>,\n    readonly filterQuery: FromBuilder<any>,\n    readonly joinCondition: BooleanExpr<any>\n  ) {\n    if (sourceQuery.table.sourceName === filterQuery.table.sourceName) {\n      // TODO: Handle aliasing properly instead of just forbidding it.\n      throw new Error('Cannot semijoin a table to itself');\n    }\n  }\n\n  build(): Query<TableDef> {\n    return this as Query<TableDef>;\n  }\n\n  where(\n    predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>\n  ): SemijoinImpl<TableDef> {\n    const nextSourceQuery = this.sourceQuery.where(predicate);\n    return new SemijoinImpl<TableDef>(\n      nextSourceQuery,\n      this.filterQuery,\n      this.joinCondition\n    );\n  }\n\n  toSql(): string {\n    const left = this.filterQuery;\n    const right = this.sourceQuery;\n    const leftTable = quoteIdentifier(left.table.sourceName);\n    const rightTable = quoteIdentifier(right.table.sourceName);\n    let sql = `SELECT ${rightTable}.* FROM ${leftTable} JOIN ${rightTable} ON ${booleanExprToSql(this.joinCondition)}`;\n\n    const clauses: string[] = [];\n    if (left.whereClause) {\n      clauses.push(booleanExprToSql(left.whereClause));\n    }\n    if (right.whereClause) {\n      clauses.push(booleanExprToSql(right.whereClause));\n    }\n\n    if (clauses.length > 0) {\n      const whereSql =\n        clauses.length === 1\n          ? clauses[0]\n          : clauses.map(wrapInParens).join(' AND ');\n      sql += ` WHERE ${whereSql}`;\n    }\n\n    return sql;\n  }\n}\n\nclass FromBuilder<TableDef extends TypedTableDef>\n  implements From<TableDef>, TableTypedQuery<TableDef>\n{\n  readonly [QueryBrand] = true;\n  constructor(\n    readonly table: TableRef<TableDef>,\n    readonly whereClause?: BooleanExpr<TableDef>\n  ) {}\n\n  where(\n    predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>\n  ): FromBuilder<TableDef> {\n    const newCondition = normalizePredicateExpr(predicate(this.table.cols));\n    const nextWhere = this.whereClause\n      ? this.whereClause.and(newCondition)\n      : newCondition;\n    return new FromBuilder<TableDef>(this.table, nextWhere);\n  }\n\n  rightSemijoin<OtherTable extends TypedTableDef>(\n    right: TableRef<OtherTable>,\n    on: (\n      left: IndexedRowExpr<TableDef>,\n      right: IndexedRowExpr<OtherTable>\n    ) => BooleanExpr<TableDef | OtherTable>\n  ): SemijoinBuilder<OtherTable> {\n    const sourceQuery = new FromBuilder(right);\n    const joinCondition = on(\n      this.table.indexedCols,\n      right.indexedCols\n    ) as BooleanExpr<any>;\n    return new SemijoinImpl<OtherTable>(sourceQuery, this, joinCondition);\n  }\n\n  leftSemijoin<OtherTable extends TypedTableDef>(\n    right: TableRef<OtherTable>,\n    on: (\n      left: IndexedRowExpr<TableDef>,\n      right: IndexedRowExpr<OtherTable>\n    ) => BooleanExpr<TableDef | OtherTable>\n  ): SemijoinBuilder<TableDef> {\n    const filterQuery = new FromBuilder(right);\n    const joinCondition = on(\n      this.table.indexedCols,\n      right.indexedCols\n    ) as BooleanExpr<any>;\n    return new SemijoinImpl<TableDef>(this, filterQuery, joinCondition);\n  }\n\n  toSql(): string {\n    return renderSelectSqlWithJoins(this.table, this.whereClause);\n  }\n\n  build(): Query<TableDef> {\n    return this as Query<TableDef>;\n  }\n}\n\nexport type QueryBuilder<SchemaDef extends UntypedSchemaDef> = {\n  readonly [Tbl in Values<\n    SchemaDef['tables']\n  > as Tbl['accessorName']]: TableRef<Tbl> & From<Tbl>;\n} & {};\n\n/**\n * A runtime reference to a table. This materializes the RowExpr for us.\n * TODO: Maybe add the full SchemaDef to the type signature depending on how joins will work.\n */\nexport type TableRef<TableDef extends TypedTableDef> = Readonly<{\n  type: 'table';\n  sourceName: TableDef['sourceName'];\n  accessorName: string;\n  cols: RowExpr<TableDef>;\n  indexedCols: IndexedRowExpr<TableDef>;\n  tableDef: TableDef;\n  // Delegated UntypedTableDef properties for compatibility.\n  columns: TableDef['columns'];\n  indexes: TableDef['indexes'];\n  rowType: TableDef['rowType'];\n  constraints: any;\n}>;\n\nclass TableRefImpl<TableDef extends TypedTableDef>\n  implements TableRef<TableDef>, From<TableDef>\n{\n  readonly [QueryBrand] = true;\n  readonly type = 'table' as const;\n  sourceName: string;\n  accessorName: string;\n  cols: RowExpr<TableDef>;\n  indexedCols: IndexedRowExpr<TableDef>;\n  tableDef: TableDef;\n  // Delegate UntypedTableDef properties from tableDef so this can be used as a table def.\n  get columns() {\n    return this.tableDef.columns;\n  }\n  get indexes() {\n    return this.tableDef.indexes;\n  }\n  get rowType() {\n    return this.tableDef.rowType;\n  }\n  get constraints() {\n    return (this.tableDef as any).constraints;\n  }\n  constructor(tableDef: TableDef) {\n    this.sourceName = tableDef.sourceName;\n    this.accessorName = tableDef.accessorName;\n    this.cols = createRowExpr(tableDef);\n    // this.indexedCols = createIndexedRowExpr(tableDef, this.cols);\n    // TODO: we could create an indexedRowExpr to avoid having the extra columns.\n    // Right now, the objects we pass will actually have all the columns, but the\n    // type system will consider it an error.\n    this.indexedCols = this.cols;\n    this.tableDef = tableDef;\n    Object.freeze(this);\n  }\n\n  asFrom(): FromBuilder<TableDef> {\n    return new FromBuilder<TableDef>(this);\n  }\n\n  rightSemijoin<RightTable extends TypedTableDef>(\n    other: TableRef<RightTable>,\n    on: (\n      left: IndexedRowExpr<TableDef>,\n      right: IndexedRowExpr<RightTable>\n    ) => EqExpr<TableDef | RightTable>\n  ): SemijoinBuilder<RightTable> {\n    return this.asFrom().rightSemijoin(other, on);\n  }\n\n  leftSemijoin<RightTable extends TypedTableDef>(\n    other: TableRef<RightTable>,\n    on: (\n      left: IndexedRowExpr<TableDef>,\n      right: IndexedRowExpr<RightTable>\n    ) => EqExpr<TableDef | RightTable>\n  ): SemijoinBuilder<TableDef> {\n    return this.asFrom().leftSemijoin(other, on);\n  }\n\n  build(): Query<TableDef> {\n    return this.asFrom().build();\n  }\n\n  toSql(): string {\n    return this.asFrom().toSql();\n  }\n\n  where(\n    predicate: (row: RowExpr<TableDef>) => PredicateExpr<TableDef>\n  ): FromBuilder<TableDef> {\n    return this.asFrom().where(predicate);\n  }\n}\n\nexport function createTableRefFromDef<TableDef extends TypedTableDef>(\n  tableDef: TableDef\n): TableRef<TableDef> {\n  return new TableRefImpl<TableDef>(tableDef);\n}\n\nexport function makeQueryBuilder<SchemaDef extends UntypedSchemaDef>(\n  schema: SchemaDef\n): QueryBuilder<SchemaDef> {\n  const qb = Object.create(null) as QueryBuilder<SchemaDef>;\n  for (const table of Object.values(schema.tables)) {\n    const ref = createTableRefFromDef(\n      table as TableDefByName<SchemaDef, TableNames<SchemaDef>>\n    );\n    (qb as Record<string, TableRef<any>>)[table.accessorName] = ref;\n  }\n  return Object.freeze(qb) as QueryBuilder<SchemaDef>;\n}\n\nfunction createRowExpr<TableDef extends TypedTableDef>(\n  tableDef: TableDef\n): RowExpr<TableDef> {\n  const row: Record<string, ColumnExpr<TableDef, any>> = {};\n  for (const columnName of Object.keys(tableDef.columns) as Array<\n    keyof TableDef['columns'] & string\n  >) {\n    const columnBuilder = tableDef.columns[columnName];\n    const column = new ColumnExpression<TableDef, typeof columnName>(\n      tableDef.sourceName,\n      columnName,\n      columnBuilder.typeBuilder.algebraicType as InferSpacetimeTypeOfColumn<\n        TableDef,\n        typeof columnName\n      >,\n      columnBuilder.columnMetadata.name\n    );\n    row[columnName] = Object.freeze(column);\n  }\n  return Object.freeze(row) as RowExpr<TableDef>;\n}\n\nfunction renderSelectSqlWithJoins<Table extends TypedTableDef>(\n  table: TableRef<Table>,\n  where?: BooleanExpr<Table>,\n  extraClauses: readonly string[] = []\n): string {\n  const quotedTable = quoteIdentifier(table.sourceName);\n  const sql = `SELECT * FROM ${quotedTable}`;\n  const clauses: string[] = [];\n  if (where) clauses.push(booleanExprToSql(where));\n  clauses.push(...extraClauses);\n  if (clauses.length === 0) return sql;\n  const whereSql =\n    clauses.length === 1 ? clauses[0] : clauses.map(wrapInParens).join(' AND ');\n  return `${sql} WHERE ${whereSql}`;\n}\n\n// TODO: Just use UntypedTableDef if they end up being the same.\nexport type TypedTableDef<\n  Columns extends Record<\n    string,\n    ColumnBuilder<any, any, ColumnMetadata<any>>\n  > = Record<string, ColumnBuilder<any, any, ColumnMetadata<any>>>,\n> = {\n  sourceName: string;\n  accessorName: string;\n  columns: Columns;\n  indexes: readonly IndexOpts<any>[];\n  rowType: RowBuilder<Columns>['algebraicType']['value'];\n};\n\nexport type TableSchemaAsTableDef<TSchema extends UntypedTableSchema> = {\n  name: TSchema['tableName'];\n  columns: TSchema['rowType']['row'];\n  indexes: TSchema['idxs'];\n};\n\ntype RowType<TableDef extends TypedTableDef> = {\n  [K in keyof TableDef['columns']]: TableDef['columns'][K] extends ColumnBuilder<\n    infer T,\n    any,\n    any\n  >\n    ? T\n    : never;\n};\n\n// TODO: Consider making a smaller version of these types that doesn't expose the internals.\n// Restricting it later should not break anyone in practice.\nexport type ColumnExpr<\n  TableDef extends TypedTableDef,\n  ColumnName extends ColumnNames<TableDef>,\n> = ColumnExpression<TableDef, ColumnName>;\n\ntype ColumnSpacetimeType<Col extends ColumnExpr<any, any>> =\n  Col extends ColumnExpr<infer T, infer N>\n    ? InferSpacetimeTypeOfColumn<T, N>\n    : never;\n\n// TODO: This checks that they match, but we also need to make sure that they are comparable types,\n// since you can use product types at all.\ntype ColumnSameSpacetime<\n  ThisTable extends TypedTableDef,\n  ThisCol extends ColumnNames<ThisTable>,\n  OtherCol extends ColumnExpr<any, any>,\n> = [InferSpacetimeTypeOfColumn<ThisTable, ThisCol>] extends [\n  ColumnSpacetimeType<OtherCol>,\n]\n  ? [ColumnSpacetimeType<OtherCol>] extends [\n      InferSpacetimeTypeOfColumn<ThisTable, ThisCol>,\n    ]\n    ? OtherCol\n    : never\n  : never;\n\n// Helper to get the table back from a column.\ntype ExtractTable<Col extends ColumnExpr<any, any>> =\n  Col extends ColumnExpr<infer T, any> ? T : never;\n\nexport class ColumnExpression<\n  TableDef extends TypedTableDef,\n  ColumnName extends ColumnNames<TableDef>,\n> {\n  readonly type = 'column' as const;\n  // This is the column accessor\n  readonly column: ColumnName;\n  // The name of the column in the database.\n  readonly columnName: string;\n  readonly table: TableDef['sourceName'];\n  // phantom: actual runtime value is undefined\n  readonly tsValueType?: RowType<TableDef>[ColumnName];\n  readonly spacetimeType: InferSpacetimeTypeOfColumn<TableDef, ColumnName>;\n\n  constructor(\n    table: TableDef['sourceName'],\n    column: ColumnName,\n    spacetimeType: InferSpacetimeTypeOfColumn<TableDef, ColumnName>,\n    columnName?: string\n  ) {\n    this.table = table;\n    this.column = column;\n    this.columnName = columnName || column;\n    this.spacetimeType = spacetimeType;\n  }\n\n  eq(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  eq<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  eq(x: any): any {\n    return new BooleanExpr({\n      type: 'eq',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n\n  ne(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  ne<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  ne(x: any): any {\n    return new BooleanExpr({\n      type: 'ne',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n\n  lt(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  lt<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  lt(x: any): any {\n    return new BooleanExpr({\n      type: 'lt',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n\n  lte(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  lte<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  lte(x: any): any {\n    return new BooleanExpr({\n      type: 'lte',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n\n  gt(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  gt<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  gt(x: any): any {\n    return new BooleanExpr({\n      type: 'gt',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n\n  gte(\n    literal: LiteralValue & RowType<TableDef>[ColumnName]\n  ): BooleanExpr<TableDef>;\n  gte<OtherCol extends ColumnExpr<any, any>>(\n    value: ColumnSameSpacetime<TableDef, ColumnName, OtherCol>\n  ): BooleanExpr<TableDef | ExtractTable<OtherCol>>;\n\n  gte(x: any): any {\n    return new BooleanExpr({\n      type: 'gte',\n      left: this as unknown as ValueExpr<TableDef, any>,\n      right: normalizeValue(x) as ValueExpr<TableDef, any>,\n    });\n  }\n}\n\n/**\n * Helper to get the spacetime type of a column.\n */\ntype InferSpacetimeTypeOfColumn<\n  TableDef extends TypedTableDef,\n  ColumnName extends ColumnNames<TableDef>,\n> =\n  TableDef['columns'][ColumnName]['typeBuilder'] extends TypeBuilder<\n    any,\n    infer U\n  >\n    ? U\n    : never;\n\ntype ColumnNames<TableDef extends TypedTableDef> = keyof RowType<TableDef> &\n  string;\n\n// For composite indexes, we only consider it as an index over the first column in the index.\ntype FirstIndexColumn<I extends IndexOpts<any>> =\n  IndexColumns<I> extends readonly [infer Head extends string, ...infer _Rest]\n    ? Head\n    : never;\n\n// Columns that are indexed by something in the indexes: [...] part.\ntype ExplicitIndexedColumns<TableDef extends TypedTableDef> =\n  TableDef['indexes'][number] extends infer I\n    ? I extends IndexOpts<ColumnNames<TableDef>>\n      ? FirstIndexColumn<I> & ColumnNames<TableDef>\n      : never\n    : never;\n\n// Columns with an index defined on the column definition.\ntype MetadataIndexedColumns<TableDef extends TypedTableDef> = {\n  [K in ColumnNames<TableDef>]: ColumnIndex<\n    K,\n    TableDef['columns'][K]['columnMetadata']\n  > extends never\n    ? never\n    : K;\n}[ColumnNames<TableDef>];\n\nexport type IndexedColumnNames<TableDef extends TypedTableDef> =\n  | ExplicitIndexedColumns<TableDef>\n  | MetadataIndexedColumns<TableDef>;\n\nexport type IndexedRowExpr<TableDef extends TypedTableDef> = Readonly<{\n  readonly [C in IndexedColumnNames<TableDef>]: ColumnExpr<TableDef, C>;\n}>;\n\n/**\n * Acts as a row when writing filters for queries. It is a way to get column references.\n */\nexport type RowExpr<TableDef extends TypedTableDef> = Readonly<{\n  readonly [C in ColumnNames<TableDef>]: ColumnExpr<TableDef, C>;\n}>;\n\n/**\n * Union of ColumnExprs from Table whose spacetimeType is compatible with Value\n * (produces a union of ColumnExpr<Table, C> for matching columns).\n */\nexport type ColumnExprForValue<Table extends TypedTableDef, Value> = {\n  [C in ColumnNames<Table>]: InferSpacetimeTypeOfColumn<Table, C> extends Value\n    ? ColumnExpr<Table, C>\n    : never;\n}[ColumnNames<Table>];\n\ntype LiteralValue =\n  | string\n  | number\n  | bigint\n  | boolean\n  | Identity\n  | Timestamp\n  | ConnectionId;\n\ntype ValueLike = LiteralValue | ColumnExpr<any, any> | LiteralExpr<any>;\ntype ValueInput<TableDef extends TypedTableDef> =\n  | ValueLike\n  | ValueExpr<TableDef, any>;\n\nexport type ValueExpr<TableDef extends TypedTableDef, Value> =\n  | LiteralExpr<Value & LiteralValue>\n  | ColumnExprForValue<TableDef, Value>;\n\ntype PredicateExpr<TableDef extends TypedTableDef> =\n  | BooleanExpr<TableDef>\n  | ColumnExprForValue<TableDef, SatsBool>\n  | boolean;\n\ntype LiteralExpr<Value> = {\n  type: 'literal';\n  value: Value;\n};\n\nexport function literal<Value extends LiteralValue>(\n  value: Value\n): ValueExpr<never, Value> {\n  return { type: 'literal', value };\n}\n\n// This is here to take literal values and wrap them in an AST node.\nfunction normalizeValue(val: ValueInput<any>): ValueExpr<any, any> {\n  if ((val as LiteralExpr<any>).type === 'literal')\n    return val as LiteralExpr<any>;\n  if (\n    typeof val === 'object' &&\n    val != null &&\n    'type' in (val as any) &&\n    (val as any).type === 'column'\n  ) {\n    return val as ColumnExpr<any, any>;\n  }\n  return literal(val as LiteralValue);\n}\n\nfunction normalizePredicateExpr<TableDef extends TypedTableDef>(\n  value: PredicateExpr<TableDef>\n): BooleanExpr<TableDef> {\n  if (value instanceof BooleanExpr) return value;\n  if (typeof value === 'boolean') {\n    return new BooleanExpr({\n      type: 'eq',\n      left: literal(value),\n      right: literal(true),\n    });\n  }\n  return new BooleanExpr({\n    type: 'eq',\n    left: value as ValueExpr<TableDef, any>,\n    right: literal(true),\n  });\n}\n\ntype EqExpr<Table extends TypedTableDef = any> = BooleanExpr<Table>;\n\ntype BooleanExprData<Table extends TypedTableDef> = (\n  | {\n      type: 'eq' | 'ne' | 'gt' | 'lt' | 'gte' | 'lte';\n      left: ValueExpr<Table, any>;\n      right: ValueExpr<Table, any>;\n    }\n  | {\n      type: 'and';\n      clauses: readonly [\n        BooleanExprData<Table>,\n        BooleanExprData<Table>,\n        ...BooleanExprData<Table>[],\n      ];\n    }\n  | {\n      type: 'or';\n      clauses: readonly [\n        BooleanExprData<Table>,\n        BooleanExprData<Table>,\n        ...BooleanExprData<Table>[],\n      ];\n    }\n  | {\n      type: 'not';\n      clause: BooleanExprData<Table>;\n    }\n) & {\n  _tableType?: Table;\n};\n\ntype AndOrMixedTableScopeError = {\n  readonly 'Cannot combine predicates from different table scopes with and/or. In semijoin on(...), keep only the join equality and move extra predicates to .where(...).': never;\n};\n\ntype RequireSameAndOrTable<\n  Expected extends TypedTableDef,\n  Actual extends TypedTableDef,\n> = [Expected] extends [Actual]\n  ? [Actual] extends [Expected]\n    ? unknown\n    : AndOrMixedTableScopeError\n  : AndOrMixedTableScopeError;\n\nexport class BooleanExpr<Table extends TypedTableDef> {\n  constructor(readonly data: BooleanExprData<Table>) {}\n\n  and<OtherTable extends TypedTableDef>(\n    other: BooleanExpr<OtherTable> & RequireSameAndOrTable<Table, OtherTable>\n  ): BooleanExpr<Table> {\n    return new BooleanExpr({\n      type: 'and',\n      clauses: [this.data, other.data as BooleanExprData<Table>],\n    });\n  }\n\n  or<OtherTable extends TypedTableDef>(\n    other: BooleanExpr<OtherTable> & RequireSameAndOrTable<Table, OtherTable>\n  ): BooleanExpr<Table> {\n    return new BooleanExpr({\n      type: 'or',\n      clauses: [this.data, other.data as BooleanExprData<Table>],\n    });\n  }\n\n  not(): BooleanExpr<Table> {\n    return new BooleanExpr({ type: 'not', clause: this.data });\n  }\n}\n\nexport function not<T extends TypedTableDef>(\n  clause: BooleanExpr<T>\n): BooleanExpr<T> {\n  return new BooleanExpr({ type: 'not', clause: clause.data });\n}\n\nexport function and<\n  Table extends TypedTableDef,\n  OtherTable extends TypedTableDef,\n>(\n  first: BooleanExpr<Table>,\n  second: BooleanExpr<OtherTable> & RequireSameAndOrTable<Table, OtherTable>,\n  ...rest: readonly BooleanExpr<Table>[]\n): BooleanExpr<Table> {\n  const clauses = [first, second, ...rest];\n  return new BooleanExpr({\n    type: 'and',\n    clauses: clauses.map(c => c.data) as [\n      BooleanExprData<Table>,\n      BooleanExprData<Table>,\n      ...BooleanExprData<Table>[],\n    ],\n  });\n}\n\nexport function or<\n  Table extends TypedTableDef,\n  OtherTable extends TypedTableDef,\n>(\n  first: BooleanExpr<Table>,\n  second: BooleanExpr<OtherTable> & RequireSameAndOrTable<Table, OtherTable>,\n  ...rest: readonly BooleanExpr<Table>[]\n): BooleanExpr<Table> {\n  const clauses = [first, second, ...rest];\n  return new BooleanExpr({\n    type: 'or',\n    clauses: clauses.map(c => c.data) as [\n      BooleanExprData<Table>,\n      BooleanExprData<Table>,\n      ...BooleanExprData<Table>[],\n    ],\n  });\n}\n\nfunction booleanExprToSql<Table extends TypedTableDef>(\n  expr: BooleanExpr<Table> | BooleanExprData<Table>,\n  tableAlias?: string\n): string {\n  const data = expr instanceof BooleanExpr ? expr.data : expr;\n  switch (data.type) {\n    case 'eq':\n      return `${valueExprToSql(data.left, tableAlias)} = ${valueExprToSql(data.right, tableAlias)}`;\n    case 'ne':\n      return `${valueExprToSql(data.left, tableAlias)} <> ${valueExprToSql(data.right, tableAlias)}`;\n    case 'gt':\n      return `${valueExprToSql(data.left, tableAlias)} > ${valueExprToSql(data.right, tableAlias)}`;\n    case 'gte':\n      return `${valueExprToSql(data.left, tableAlias)} >= ${valueExprToSql(data.right, tableAlias)}`;\n    case 'lt':\n      return `${valueExprToSql(data.left, tableAlias)} < ${valueExprToSql(data.right, tableAlias)}`;\n    case 'lte':\n      return `${valueExprToSql(data.left, tableAlias)} <= ${valueExprToSql(data.right, tableAlias)}`;\n    case 'and':\n      return data.clauses\n        .map(c => booleanExprToSql(c, tableAlias))\n        .map(wrapInParens)\n        .join(' AND ');\n    case 'or':\n      return data.clauses\n        .map(c => booleanExprToSql(c, tableAlias))\n        .map(wrapInParens)\n        .join(' OR ');\n    case 'not':\n      return `NOT ${wrapInParens(booleanExprToSql(data.clause, tableAlias))}`;\n  }\n}\n\nfunction wrapInParens(sql: string): string {\n  return `(${sql})`;\n}\n\nfunction valueExprToSql<Table extends TypedTableDef>(\n  expr: ValueExpr<Table, any>,\n  tableAlias?: string\n): string {\n  if (isLiteralExpr(expr)) {\n    return literalValueToSql(expr.value);\n  }\n  const table = tableAlias ?? expr.table;\n  return `${quoteIdentifier(table)}.${quoteIdentifier(expr.columnName)}`;\n}\n\nfunction literalValueToSql(value: unknown): string {\n  if (value === null || value === undefined) {\n    return 'NULL';\n  }\n  if (value instanceof Identity || value instanceof ConnectionId) {\n    // We use this hex string syntax.\n    return `0x${value.toHexString()}`;\n  }\n  if (value instanceof Timestamp) {\n    return `'${value.toISOString()}'`;\n  }\n  switch (typeof value) {\n    case 'number':\n    case 'bigint':\n      return String(value);\n    case 'boolean':\n      return value ? 'TRUE' : 'FALSE';\n    case 'string':\n      return `'${value.replace(/'/g, \"''\")}'`;\n    default:\n      // It might be safer to error here?\n      return `'${JSON.stringify(value).replace(/'/g, \"''\")}'`;\n  }\n}\n\nfunction quoteIdentifier(name: string): string {\n  return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction isLiteralExpr<Value>(\n  expr: ValueExpr<any, Value>\n): expr is LiteralExpr<Value & LiteralValue> {\n  return (expr as LiteralExpr<Value>).type === 'literal';\n}\n\n/**\n * Evaluate a BooleanExpr against a row at runtime for client-side filtering.\n */\nexport function evaluateBooleanExpr(\n  expr: BooleanExpr<any>,\n  row: Record<string, any>\n): boolean {\n  return evaluateData(expr.data, row);\n}\n\nfunction evaluateData(\n  data: BooleanExprData<any>,\n  row: Record<string, any>\n): boolean {\n  switch (data.type) {\n    case 'eq':\n      return resolveValue(data.left, row) === resolveValue(data.right, row);\n    case 'ne':\n      return resolveValue(data.left, row) !== resolveValue(data.right, row);\n    case 'gt':\n      return resolveValue(data.left, row) > resolveValue(data.right, row);\n    case 'gte':\n      return resolveValue(data.left, row) >= resolveValue(data.right, row);\n    case 'lt':\n      return resolveValue(data.left, row) < resolveValue(data.right, row);\n    case 'lte':\n      return resolveValue(data.left, row) <= resolveValue(data.right, row);\n    case 'and':\n      return data.clauses.every(c => evaluateData(c, row));\n    case 'or':\n      return data.clauses.some(c => evaluateData(c, row));\n    case 'not':\n      return !evaluateData(data.clause, row);\n  }\n}\n\nfunction resolveValue(\n  expr: ValueExpr<any, any>,\n  row: Record<string, any>\n): any {\n  if (isLiteralExpr(expr)) {\n    return toComparableValue(expr.value);\n  }\n  return toComparableValue(row[expr.column]);\n}\n\ntype TimestampLike = {\n  __timestamp_micros_since_unix_epoch__: bigint;\n};\n\ntype HexSerializableLike = {\n  toHexString: () => string;\n};\n\nfunction isHexSerializableLike(value: unknown): value is HexSerializableLike {\n  return (\n    !!value &&\n    typeof value === 'object' &&\n    typeof (value as { toHexString?: unknown }).toHexString === 'function'\n  );\n}\n\n// Check if this value is a Timestamp-like object. This is here because\n// running locally can end up with different versions of the Timestamp class,\n// which breaks the simple instanceof version.\nfunction isTimestampLike(value: unknown): value is TimestampLike {\n  if (!value || typeof value !== 'object') return false;\n\n  if (value instanceof Timestamp) return true;\n\n  const micros = (value as Record<string, unknown>)[\n    '__timestamp_micros_since_unix_epoch__'\n  ];\n  return typeof micros === 'bigint';\n}\n\n// Exported for tests.\nexport function toComparableValue(value: any): any {\n  // Handle `ConnectionId` and `Identity`.\n  if (isHexSerializableLike(value)) {\n    return value.toHexString();\n  }\n  if (isTimestampLike(value)) {\n    return value.__timestamp_micros_since_unix_epoch__;\n  }\n  return value;\n}\n\n/**\n * Extract the table name from a query builder expression.\n */\nexport function getQueryTableName(query: any): string {\n  if (query.table) return query.table.name; // FromBuilder\n  if (query.name) return query.name; // TableRefImpl\n  if (query.sourceQuery) return query.sourceQuery.table.name; // SemijoinImpl (source table)\n  throw new Error('Cannot extract table name from query');\n}\n\n/**\n * Extract the accessor name from a query builder expression.\n */\nexport function getQueryAccessorName(query: any): string {\n  if (query.table) return query.table.accessorName; // FromBuilder\n  if (query.accessorName) return query.accessorName; // TableRefImpl\n  if (query.sourceQuery) return query.sourceQuery.table.accessorName; // SemijoinImpl\n  throw new Error('Cannot extract accessor name from query');\n}\n\n/**\n * Extract the BooleanExpr from a query builder, if any.\n */\nexport function getQueryWhereClause(query: any): BooleanExpr<any> | undefined {\n  if (query.whereClause) return query.whereClause; // FromBuilder\n  return undefined; // TableRefImpl has no where clause\n}\n\n// TODO: Fix this.\nfunction _createIndexedRowExpr<TableDef extends TypedTableDef>(\n  tableDef: TableDef,\n  cols: RowExpr<TableDef>\n): IndexedRowExpr<TableDef> {\n  const indexed = new Set<ColumnNames<TableDef>>();\n  for (const idx of tableDef.indexes) {\n    if ('columns' in idx) {\n      const [first] = idx.columns;\n      if (first) indexed.add(first);\n    } else if ('column' in idx) {\n      indexed.add(idx.column);\n    }\n  }\n  const pickedEntries = [...indexed].map(name => [name, cols[name]]);\n  return Object.freeze(\n    Object.fromEntries(pickedEntries)\n  ) as IndexedRowExpr<TableDef>;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/reducer_schema.ts",
    "content": "import type { ProductType } from './algebraic_type';\nimport type { RawReducerDefV9 } from './autogen/types';\nimport type { ParamsObj } from './reducers';\nimport type { RowBuilder, RowObj } from './type_builders';\nimport type { CamelCase } from './type_util';\n\n/**\n * Represents a handle to a database reducer, including its name and argument type.\n */\nexport type ReducerSchema<\n  ReducerName extends string,\n  Params extends ParamsObj | RowObj,\n> = {\n  /**\n   * The name of the reducer.\n   */\n  readonly reducerName: ReducerName;\n\n  /**\n   * The accessor name for the reducer.\n   */\n  readonly accessorName: CamelCase<ReducerName>;\n\n  /**\n   * The TypeBuilder representation of the reducer's parameter type.\n   */\n  readonly params: RowBuilder<Params>;\n\n  /**\n   * The {@link ProductType} representing the structure of the reducer's parameters.\n   */\n  readonly paramsSpacetimeType: ProductType;\n\n  /**\n   * The {@link RawReducerDefV9} of the configured reducer.\n   */\n  readonly reducerDef: RawReducerDefV9;\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/reducers.ts",
    "content": "import type { DbView } from '../server/db_view';\nimport type { Random } from '../server/rng';\nimport type { ConnectionId } from './connection_id';\nimport type { Identity } from './identity';\nimport { type UntypedSchemaDef } from './schema';\nimport { type Timestamp } from './timestamp';\nimport {\n  ColumnBuilder,\n  type InferTypeOfRow,\n  type TypeBuilder,\n} from './type_builders';\nimport { Uuid } from './uuid.ts';\n\n/**\n * Helper to extract the parameter types from an object type\n */\nexport type ParamsObj = Record<\n  string,\n  TypeBuilder<any, any> | ColumnBuilder<any, any, any>\n>;\n\n/**\n * Helper to convert a ParamsObj or RowObj into an object type\n */\nexport type ParamsAsObject<ParamDef extends ParamsObj> =\n  InferTypeOfRow<ParamDef>;\n\n/**\n * Defines a SpacetimeDB reducer function.\n * Reducers are the primary way to modify the state of your SpacetimeDB application.\n * They are atomic, meaning that either all operations within a reducer succeed,\n * or none of them do.\n * @template S - The inferred schema type of the SpacetimeDB module.\n * @template Params - The type of the parameters object expected by the reducer.\n * @param ctx - The reducer context, providing access to `sender`, `timestamp`, `connection_id`, and `db`.\n * @param payload - An object containing the arguments passed to the reducer, typed according to `params`.\n * @example\n * ```typescript\n * // Define a reducer named 'create_user' that takes 'username' (string) and 'email' (string)\n * reducer(\n *   'create_user',\n *   {\n *    username: t.string(),\n *    email: t.string(),\n *   },\n *   (ctx, { username, email }) => {\n *     // Access the 'user' table from the database view in the context\n *     ctx.db.user.insert({ username, email, created_at: ctx.timestamp });\n *     console.log(`User ${username} created by ${ctx.sender.identityId}`);\n *   }\n * );\n * ```\n */\nexport type Reducer<S extends UntypedSchemaDef, Params extends ParamsObj> = (\n  ctx: ReducerCtx<S>,\n  payload: ParamsAsObject<Params>\n) => void;\n\n/**\n * Authentication information for the caller of a reducer.\n */\nexport type AuthCtx = Readonly<{\n  /** Whether the caller is an internal system process. */\n  isInternal: boolean;\n  /** Whether the caller has authenticated with a JWT token. */\n  hasJWT: boolean;\n  /** The JWT claims associated with the caller, or null if hasJWT == false. */\n  jwt: JwtClaims | null;\n}>;\n\nexport type JsonValue =\n  | string\n  | number\n  | boolean\n  | null\n  | Array<JsonValue>\n  | JsonObject;\n\nexport interface JsonObject {\n  [key: string]: JsonValue;\n}\n\n/**\n * Auth Claims extracted from the payload of a JWT token\n */\nexport interface JwtClaims {\n  /** The full payload as a JSON string */\n  readonly rawPayload: string;\n  /** The subject of the JWT token ('sub') */\n  readonly subject: string;\n  /** The issuer of the JWT token ('iss') */\n  readonly issuer: string;\n  /** The audience of the JWT token ('aud') */\n  readonly audience: readonly string[];\n  /** The identity associated with the JWT token, which is based on the sub and iss */\n  readonly identity: Identity;\n  /** The full payload as a JsonObject */\n  readonly fullPayload: JsonObject;\n}\n\n/**\n * Reducer context parametrized by the inferred Schema\n */\nexport type ReducerCtx<SchemaDef extends UntypedSchemaDef> = Readonly<{\n  sender: Identity;\n  identity: Identity;\n  timestamp: Timestamp;\n  connectionId: ConnectionId | null;\n  db: DbView<SchemaDef>;\n  senderAuth: AuthCtx;\n  newUuidV4(): Uuid;\n  newUuidV7(): Uuid;\n  random: Random;\n}>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/result.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\n\nexport type ResultAlgebraicType<\n  T extends AlgebraicType = AlgebraicType,\n  E extends AlgebraicType = AlgebraicType,\n> = {\n  tag: 'Sum';\n  value: {\n    variants: [\n      { name: 'ok'; algebraicType: T },\n      { name: 'err'; algebraicType: E },\n    ];\n  };\n};\n\nexport const Result: {\n  getAlgebraicType<\n    T extends AlgebraicType = AlgebraicType,\n    E extends AlgebraicType = AlgebraicType,\n  >(\n    okType: T,\n    errType: E\n  ): ResultAlgebraicType<T, E>;\n} = {\n  getAlgebraicType<\n    T extends AlgebraicType = AlgebraicType,\n    E extends AlgebraicType = AlgebraicType,\n  >(okType: T, errType: E): ResultAlgebraicType<T, E> {\n    return AlgebraicType.Sum({\n      variants: [\n        { name: 'ok', algebraicType: okType },\n        { name: 'err', algebraicType: errType },\n      ],\n    });\n  },\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/schedule_at.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\nimport { TimeDuration, type TimeDurationAlgebraicType } from './time_duration';\nimport { Timestamp, type TimestampAlgebraicType } from './timestamp';\n\nexport type ScheduleAtAlgebraicType = {\n  tag: 'Sum';\n  value: {\n    variants: [\n      { name: 'Interval'; algebraicType: TimeDurationAlgebraicType },\n      { name: 'Time'; algebraicType: TimestampAlgebraicType },\n    ];\n  };\n};\n\ntype ScheduleAtType = Interval | Time;\n\nexport const ScheduleAt: {\n  interval: (micros: bigint) => ScheduleAtType;\n  time: (microsSinceUnixEpoch: bigint) => ScheduleAtType;\n  /**\n   * Get the algebraic type representation of the {@link ScheduleAt} type.\n   * @returns The algebraic type representation of the type.\n   */\n  getAlgebraicType(): ScheduleAtAlgebraicType;\n  isScheduleAt(\n    algebraicType: AlgebraicType\n  ): algebraicType is ScheduleAtAlgebraicType;\n} = {\n  interval(value: bigint): ScheduleAtType {\n    return Interval(value);\n  },\n  time(value: bigint): ScheduleAtType {\n    return Time(value);\n  },\n  getAlgebraicType(): ScheduleAtAlgebraicType {\n    return AlgebraicType.Sum({\n      variants: [\n        {\n          name: 'Interval',\n          algebraicType: TimeDuration.getAlgebraicType(),\n        },\n        { name: 'Time', algebraicType: Timestamp.getAlgebraicType() },\n      ],\n    });\n  },\n  isScheduleAt(\n    algebraicType: AlgebraicType\n  ): algebraicType is ScheduleAtAlgebraicType {\n    if (algebraicType.tag !== 'Sum') {\n      return false;\n    }\n    const variants = algebraicType.value.variants;\n    if (variants.length !== 2) {\n      return false;\n    }\n    const intervalVariant = variants.find(v => v.name === 'Interval');\n    const timeVariant = variants.find(v => v.name === 'Time');\n    if (!intervalVariant || !timeVariant) {\n      return false;\n    }\n    return (\n      TimeDuration.isTimeDuration(intervalVariant.algebraicType) &&\n      Timestamp.isTimestamp(timeVariant.algebraicType)\n    );\n  },\n};\n\nexport type Interval = {\n  tag: 'Interval';\n  value: TimeDuration;\n};\nexport const Interval = (micros: bigint): Interval => ({\n  tag: 'Interval',\n  value: new TimeDuration(micros),\n});\nexport type Time = {\n  tag: 'Time';\n  value: Timestamp;\n};\nexport const Time = (microsSinceUnixEpoch: bigint): Time => ({\n  tag: 'Time',\n  value: new Timestamp(microsSinceUnixEpoch),\n});\n\nexport default ScheduleAt;\nexport type ScheduleAt = ScheduleAtType;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/schema.ts",
    "content": "import {\n  AlgebraicType,\n  ProductType,\n  SumType,\n  type AlgebraicTypeType,\n  type AlgebraicTypeVariants,\n} from './algebraic_type';\nimport type {\n  CaseConversionPolicy,\n  RawModuleDefV10,\n  RawModuleDefV10Section,\n  RawScopedTypeNameV10,\n  RawTableDefV10,\n} from './autogen/types';\nimport type { UntypedIndex } from './indexes';\nimport type { UntypedTableDef } from './table';\nimport type { UntypedTableSchema } from './table_schema';\nimport {\n  ArrayBuilder,\n  OptionBuilder,\n  ProductBuilder,\n  RefBuilder,\n  ResultBuilder,\n  RowBuilder,\n  SumBuilder,\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  TypeBuilder,\n  type ElementsObj,\n  type Infer,\n  type InferSpacetimeTypeOfTypeBuilder,\n  type RowObj,\n  type VariantsObj,\n} from './type_builders';\nimport type { Values } from './type_util';\n\nexport type TableNamesOf<S extends UntypedSchemaDef> = Values<\n  S['tables']\n>['accessorName'];\n\n/**\n * An untyped representation of the database schema.\n */\nexport type UntypedSchemaDef = {\n  tables: Record<string, UntypedTableDef>;\n};\n\n/**\n * Helper type to convert an array of TableSchema into a schema definition\n */\nexport interface TablesToSchema<T extends Record<string, UntypedTableSchema>>\n  extends UntypedSchemaDef {\n  tables: {\n    readonly [AccName in keyof T & string]: TableToSchema<AccName, T[AccName]>;\n  };\n}\n\nexport interface TableToSchema<\n  AccName extends string,\n  T extends UntypedTableSchema,\n> extends UntypedTableDef {\n  accessorName: AccName;\n  columns: T['rowType']['row'];\n  rowType: T['rowSpacetimeType'];\n  // Declarative user-provided table-level indexes.\n  indexes: T['idxs'];\n  // Resolved runtime index metadata used by runtime consumers (e.g. TableCache).\n  resolvedIndexes: readonly UntypedIndex<keyof T['rowType']['row'] & string>[];\n  constraints: T['constraints'];\n}\n\nexport function tablesToSchema<\n  const T extends Record<string, UntypedTableSchema>,\n>(ctx: ModuleContext, tables: T): TablesToSchema<T> {\n  // `TablesToSchema<T>['tables']` is intentionally readonly in the public type,\n  // but we need a mutable builder while materializing it from entries.\n  type MutableTableDefs = {\n    -readonly [AccName in keyof TablesToSchema<T>['tables']]: TablesToSchema<T>['tables'][AccName];\n  };\n  const tableDefs = Object.create(null) as MutableTableDefs;\n  for (const [accName, schema] of Object.entries(tables) as [\n    keyof T & string,\n    T[keyof T & string],\n  ][]) {\n    tableDefs[accName] = tableToSchema(\n      accName,\n      schema,\n      schema.tableDef(ctx, accName)\n    ) as TablesToSchema<T>['tables'][typeof accName];\n  }\n\n  return {\n    tables: tableDefs as TablesToSchema<T>['tables'],\n  };\n}\n\nexport function tableToSchema<\n  AccName extends string,\n  const T extends UntypedTableSchema,\n>(\n  accName: AccName,\n  schema: T,\n  tableDef: RawTableDefV10\n): TableToSchema<AccName, T> {\n  const getColName = (i: number) =>\n    schema.rowType.algebraicType.value.elements[i].name;\n\n  type AllowedCol = keyof T['rowType']['row'] & string;\n  // Build fully-resolved runtime index metadata from the host-facing RawTableDef.\n  // This is intentionally separate from `schema.idxs`, which keeps the original\n  // user-declared `IndexOpts` shape for type-level inference.\n  const resolvedIndexes: UntypedIndex<AllowedCol>[] = tableDef.indexes.map(\n    idx => {\n      const accessorName = idx.accessorName;\n      if (typeof accessorName !== 'string' || accessorName.length === 0) {\n        throw new TypeError(\n          `Index '${idx.sourceName ?? '<unknown>'}' on table '${tableDef.sourceName}' is missing accessor name`\n        );\n      }\n\n      const columnIds =\n        idx.algorithm.tag === 'Direct'\n          ? [idx.algorithm.value]\n          : idx.algorithm.value;\n\n      const unique = tableDef.constraints.some(\n        c =>\n          c.data.tag === 'Unique' &&\n          c.data.value.columns.every(col => columnIds.includes(col))\n      );\n\n      const algorithm = (\n        {\n          BTree: 'btree',\n          Hash: 'hash',\n          Direct: 'direct',\n        } as const\n      )[idx.algorithm.tag];\n\n      return {\n        name: accessorName,\n        unique,\n        algorithm,\n        columns: columnIds.map(getColName) as AllowedCol[],\n      };\n    }\n  );\n\n  return {\n    // For client,`schama.tableName` will always be there as canonical name.\n    // For module, if explicit name is not provided via `name`, accessor name will\n    // be used, it is stored as alias in database, hence works in query builder.\n    sourceName: schema.tableName || accName,\n    accessorName: accName,\n    columns: schema.rowType.row, // typed as T[i]['rowType']['row'] under TablesToSchema<T>\n    rowType: schema.rowSpacetimeType,\n    // Keep declarative indexes in their original shape for type-level consumers.\n    indexes: schema.idxs,\n    constraints: tableDef.constraints.map(c => ({\n      name: c.sourceName,\n      constraint: 'unique',\n      columns: c.data.value.columns.map(getColName) as [string],\n    })),\n    // Expose resolved runtime indexes separately so runtime users don't have to\n    // reinterpret `indexes` with unsafe casts.\n    resolvedIndexes,\n    tableDef,\n    ...(tableDef.isEvent ? { isEvent: true } : {}),\n  };\n}\n\ntype CompoundTypeCache = Map<\n  AlgebraicTypeVariants.Product | AlgebraicTypeVariants.Sum,\n  RefBuilder<any, any>\n>;\n\nexport type ModuleDef = {\n  [S in RawModuleDefV10Section as Uncapitalize<S['tag']>]: S['value'];\n};\n\ntype Section = RawModuleDefV10Section;\n\nexport class ModuleContext {\n  #compoundTypes: CompoundTypeCache = new Map();\n\n  /**\n   * The global module definition that gets populated by calls to `reducer()` and lifecycle hooks.\n   */\n  #moduleDef: ModuleDef = {\n    typespace: { types: [] },\n    tables: [],\n    reducers: [],\n    types: [],\n    rowLevelSecurity: [],\n    schedules: [],\n    procedures: [],\n    views: [],\n    lifeCycleReducers: [],\n    caseConversionPolicy: { tag: 'SnakeCase' },\n    explicitNames: {\n      entries: [],\n    },\n  };\n\n  get moduleDef(): ModuleDef {\n    return this.#moduleDef;\n  }\n\n  rawModuleDefV10(): RawModuleDefV10 {\n    const sections: Section[] = [];\n\n    const push = <T extends Section>(s: T | undefined) => {\n      if (s) sections.push(s);\n    };\n\n    const module = this.#moduleDef;\n\n    push(module.typespace && { tag: 'Typespace', value: module.typespace });\n    push(module.types && { tag: 'Types', value: module.types });\n    push(module.tables && { tag: 'Tables', value: module.tables });\n    push(module.reducers && { tag: 'Reducers', value: module.reducers });\n    push(module.procedures && { tag: 'Procedures', value: module.procedures });\n    push(module.views && { tag: 'Views', value: module.views });\n    push(module.schedules && { tag: 'Schedules', value: module.schedules });\n    push(\n      module.lifeCycleReducers && {\n        tag: 'LifeCycleReducers',\n        value: module.lifeCycleReducers,\n      }\n    );\n    push(\n      module.rowLevelSecurity && {\n        tag: 'RowLevelSecurity',\n        value: module.rowLevelSecurity,\n      }\n    );\n    push(\n      module.explicitNames && {\n        tag: 'ExplicitNames',\n        value: module.explicitNames,\n      }\n    );\n    push(\n      module.caseConversionPolicy && {\n        tag: 'CaseConversionPolicy',\n        value: module.caseConversionPolicy,\n      }\n    );\n    return { sections };\n  }\n\n  /**\n   * Set the case conversion policy for this module.\n   * Called by the settings mechanism.\n   */\n  setCaseConversionPolicy(policy: CaseConversionPolicy) {\n    this.#moduleDef.caseConversionPolicy = policy;\n  }\n\n  get typespace() {\n    return this.#moduleDef.typespace;\n  }\n\n  /**\n   * Resolves the actual type of a TypeBuilder by following its references until it reaches a non-ref type.\n   * @param typespace The typespace to resolve types against.\n   * @param typeBuilder The TypeBuilder to resolve.\n   * @returns The resolved algebraic type.\n   */\n  public resolveType<AT extends AlgebraicTypeType>(\n    typeBuilder: RefBuilder<any, AT>\n  ): AT {\n    let ty: AlgebraicType = typeBuilder.algebraicType;\n    while (ty.tag === 'Ref') {\n      ty = this.typespace.types[ty.value];\n    }\n    return ty as AT;\n  }\n\n  /**\n   * Adds a type to the module definition's typespace as a `Ref` if it is a named compound type (Product or Sum).\n   * Otherwise, returns the type as is.\n   * @param name\n   * @param ty\n   * @returns\n   */\n  public registerTypesRecursively<T extends TypeBuilder<any, AlgebraicType>>(\n    typeBuilder: T\n  ): T extends SumBuilder<any> | ProductBuilder<any> | RowBuilder<any>\n    ? RefBuilder<Infer<T>, InferSpacetimeTypeOfTypeBuilder<T>>\n    : T {\n    if (\n      (typeBuilder instanceof ProductBuilder && !isUnit(typeBuilder)) ||\n      typeBuilder instanceof SumBuilder ||\n      typeBuilder instanceof RowBuilder\n    ) {\n      return this.#registerCompoundTypeRecursively(typeBuilder) as any;\n    } else if (typeBuilder instanceof OptionBuilder) {\n      return new OptionBuilder(\n        this.registerTypesRecursively(typeBuilder.value)\n      ) as any;\n    } else if (typeBuilder instanceof ResultBuilder) {\n      return new ResultBuilder(\n        this.registerTypesRecursively(typeBuilder.ok),\n        this.registerTypesRecursively(typeBuilder.err)\n      ) as any;\n    } else if (typeBuilder instanceof ArrayBuilder) {\n      return new ArrayBuilder(\n        this.registerTypesRecursively(typeBuilder.element)\n      ) as any;\n    } else {\n      return typeBuilder as any;\n    }\n  }\n\n  #registerCompoundTypeRecursively<\n    T extends\n      | SumBuilder<VariantsObj>\n      | ProductBuilder<ElementsObj>\n      | RowBuilder<RowObj>,\n  >(typeBuilder: T): RefBuilder<Infer<T>, InferSpacetimeTypeOfTypeBuilder<T>> {\n    const ty = typeBuilder.algebraicType;\n    // NB! You must ensure that all TypeBuilder passed into this function\n    // have a name. This function ensures that nested types always have a\n    // name by assigning them one if they are missing it.\n    const name = typeBuilder.typeName;\n    if (name === undefined) {\n      throw new Error(\n        `Missing type name for ${typeBuilder.constructor.name ?? 'TypeBuilder'} ${JSON.stringify(typeBuilder)}`\n      );\n    }\n\n    let r = this.#compoundTypes.get(ty);\n    if (r != null) {\n      // Already added to typespace\n      return r;\n    }\n\n    // Recursively register nested compound types\n    const newTy =\n      typeBuilder instanceof RowBuilder || typeBuilder instanceof ProductBuilder\n        ? ({\n            tag: 'Product',\n            value: { elements: [] },\n          } as AlgebraicTypeVariants.Product)\n        : ({\n            tag: 'Sum',\n            value: { variants: [] },\n          } as AlgebraicTypeVariants.Sum);\n\n    r = new RefBuilder(this.#moduleDef.typespace.types.length);\n    this.#moduleDef.typespace.types.push(newTy);\n\n    this.#compoundTypes.set(ty, r);\n\n    if (typeBuilder instanceof RowBuilder) {\n      for (const [name, elem] of Object.entries(typeBuilder.row)) {\n        (newTy.value as ProductType).elements.push({\n          name,\n          algebraicType: this.registerTypesRecursively(elem.typeBuilder)\n            .algebraicType,\n        });\n      }\n    } else if (typeBuilder instanceof ProductBuilder) {\n      for (const [name, elem] of Object.entries(typeBuilder.elements)) {\n        (newTy.value as ProductType).elements.push({\n          name,\n          algebraicType: this.registerTypesRecursively(elem).algebraicType,\n        });\n      }\n    } else if (typeBuilder instanceof SumBuilder) {\n      for (const [name, variant] of Object.entries(typeBuilder.variants)) {\n        (newTy.value as SumType).variants.push({\n          name,\n          algebraicType: this.registerTypesRecursively(variant).algebraicType,\n        });\n      }\n    }\n\n    this.#moduleDef.types.push({\n      sourceName: splitName(name),\n      ty: r.ref,\n      customOrdering: true,\n    });\n\n    return r;\n  }\n}\n\nfunction isUnit(typeBuilder: ProductBuilder<ElementsObj>): boolean {\n  return (\n    typeBuilder.typeName == null &&\n    typeBuilder.algebraicType.value.elements.length === 0\n  );\n}\n\nexport function splitName(name: string): RawScopedTypeNameV10 {\n  const scope = name.split('.');\n  return { sourceName: scope.pop()!, scope };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/table.ts",
    "content": "import type { ProcedureExport, ReducerExport, t } from '../server';\nimport type { errors } from '../server/errors';\nimport {\n  ExplicitNameEntry,\n  RawColumnDefaultValueV10,\n  RawConstraintDefV10,\n  RawIndexAlgorithm,\n  RawIndexDefV10,\n  RawSequenceDefV10,\n  RawTableDefV10,\n} from './autogen/types';\nimport BinaryWriter from './binary_writer';\nimport type { AllUnique, ConstraintOpts } from './constraints';\nimport type {\n  ColumnIndex,\n  IndexColumns,\n  Indexes,\n  IndexOpts,\n  ReadonlyIndexes,\n  UntypedIndex,\n} from './indexes';\nimport ScheduleAt from './schedule_at';\nimport type { TableSchema } from './table_schema';\nimport {\n  RowBuilder,\n  type ColumnBuilder,\n  type ColumnMetadata,\n  type InferTypeOfRow,\n  type RowObj,\n  type TypeBuilder,\n} from './type_builders';\nimport type {\n  InvalidColumnMetadata,\n  Prettify,\n  ValidateColumnMetadata,\n} from './type_util';\nimport { toPascalCase } from './util';\n\nexport type AlgebraicTypeRef = number;\ntype ColId = number;\ntype ColList = ColId[];\n\n/**\n * Check if any column in the row has invalid metadata.\n */\ntype HasInvalidColumn<Row extends RowObj> =\n  // this checks if Row exactly equals RowObj - if it does, we can't\n  // do type-system-level checking, so just let it pass\n  (<G>() => G extends Row ? 1 : 2) extends <G>() => G extends RowObj ? 1 : 2\n    ? false\n    : {\n          [K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>\n            ? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>\n              ? true\n              : false\n            : false;\n        }[keyof Row] extends false\n      ? false\n      : true;\n\n/**\n * Extract the names of columns that have invalid metadata.\n */\ntype InvalidColumnNames<Row extends RowObj> = {\n  [K in keyof Row]: Row[K] extends ColumnBuilder<any, any, infer M>\n    ? ValidateColumnMetadata<M> extends InvalidColumnMetadata<any>\n      ? K & string\n      : never\n    : never;\n}[keyof Row];\n\n/**\n * A descriptive error type that surfaces the validation error.\n * The type name itself contains the error message for better CLI output.\n */\ntype ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<\n  InvalidColumns extends string,\n> = {\n  _invalidColumns: InvalidColumns;\n  _fix: 'Remove either default() or the constraint (primaryKey/unique/autoInc) from these columns';\n};\n\n/**\n * A helper type to extract the row type from a TableDef\n */\nexport type RowType<TableDef extends Pick<UntypedTableDef, 'columns'>> =\n  InferTypeOfRow<TableDef['columns']>;\n\n/**\n * Coerces a column which may be a TypeBuilder or ColumnBuilder into a ColumnBuilder\n */\nexport type CoerceColumn<\n  Col extends TypeBuilder<any, any> | ColumnBuilder<any, any, any>,\n> =\n  Col extends TypeBuilder<infer T, infer U>\n    ? ColumnBuilder<T, U, ColumnMetadata<any>>\n    : Col;\n\n/**\n * Coerces a RowObj where TypeBuilders are replaced with ColumnBuilders\n */\nexport type CoerceRow<Row extends RowObj> = {\n  [k in keyof Row & string]: CoerceColumn<Row[k]>;\n};\n\n/**\n * Helper type to coerce an array of IndexOpts\n */\ntype CoerceArray<X extends IndexOpts<any>[]> = X;\n\n/**\n * An untyped representation of a table's schema.\n */\nexport type UntypedTableDef = {\n  sourceName: string;\n  accessorName: string;\n  columns: Record<string, ColumnBuilder<any, any, ColumnMetadata<any>>>;\n  // This is really just a ProductType where all the elements have names.\n  rowType: RowBuilder<RowObj>['algebraicType']['value'];\n  /**\n   * Declarative multi-column indexes supplied by user code in `table({ indexes: [...] }, ...)`.\n   *\n   * This is intentionally the *declarative* shape (`IndexOpts`) because a lot of\n   * type-level behavior is derived from these entries (for example query-builder\n   * inference over composite indexes).\n   */\n  indexes: readonly IndexOpts<any>[];\n  /**\n   * Fully-resolved runtime indexes materialized from `RawTableDefV10`.\n   *\n   * This contains both:\n   * 1) field-level indexes inferred from column metadata, and\n   * 2) explicit table-level indexes.\n   *\n   * Runtime consumers like `TableCacheImpl` should use this field instead of\n   * reinterpreting `indexes` as runtime index metadata.\n   */\n  resolvedIndexes: readonly UntypedIndex<any>[];\n  constraints: readonly ConstraintOpts<any>[];\n  tableDef: RawTableDefV10;\n  isEvent?: boolean;\n};\n\n/**\n * A type representing the indexes defined on a table.\n */\nexport type TableIndexes<TableDef extends UntypedTableDef> = {\n  [K in keyof TableDef['columns'] & string as ColumnIndex<\n    K,\n    TableDef['columns'][K]['columnMetadata']\n  > extends never\n    ? never\n    : K]: ColumnIndex<K, TableDef['columns'][K]['columnMetadata']>;\n} & {\n  [I in TableDef['indexes'][number] as I['accessor'] & {}]: TableIndexFromDef<\n    TableDef,\n    I\n  >;\n};\n\ntype TableIndexFromDef<\n  TableDef extends UntypedTableDef,\n  I extends IndexOpts<keyof TableDef['columns'] & string>,\n> =\n  NormalizeIndexColumns<TableDef, I> extends infer Cols extends ReadonlyArray<\n    keyof TableDef['columns'] & string\n  >\n    ? {\n        name: I['accessor'];\n        unique: AllUnique<TableDef, Cols>;\n        algorithm: Lowercase<I['algorithm']>;\n        columns: Cols;\n      }\n    : never;\n\ntype NormalizeIndexColumns<\n  TableDef extends UntypedTableDef,\n  I extends IndexOpts<keyof TableDef['columns'] & string>,\n> =\n  IndexColumns<I> extends ReadonlyArray<keyof TableDef['columns'] & string>\n    ? IndexColumns<I>\n    : never;\n\n/**\n * Options for configuring a database table.\n * - `name`: The name of the table.\n * - `public`: Whether the table is publicly accessible. Defaults to `false`.\n * - `indexes`: An array of index configurations for the table.\n * - `constraints`: An array of constraint configurations for the table.\n * - `scheduled`: The name of the reducer to be executed based on the scheduled rows in this table.\n */\nexport type TableOpts<Row extends RowObj> = {\n  name?: string;\n  public?: boolean;\n  indexes?: IndexOpts<keyof Row & string>[]; // declarative multi‑column indexes\n  constraints?: ConstraintOpts<keyof Row & string>[];\n  scheduled?: () =>\n    | ReducerExport<any, { [k: string]: RowBuilder<RowObj> }>\n    | ProcedureExport<\n        any,\n        { [k: string]: RowBuilder<RowObj> },\n        ReturnType<typeof t.unit>\n      >;\n  event?: boolean;\n};\n\n/**\n * Extracts the indices from TableOpts, defaulting to an empty array if none are provided.\n */\ntype OptsIndices<Opts extends TableOpts<any>> = Opts extends {\n  indexes: infer Ixs extends NonNullable<any[]>;\n}\n  ? Ixs\n  : CoerceArray<[]>;\n\n/**\n * Extracts the constraints from TableOpts, defaulting to an empty array if none are provided.\n */\ntype OptsConstraints<Opts extends TableOpts<any>> = Opts extends {\n  constraints: infer Constraints extends NonNullable<any[]>;\n}\n  ? Constraints\n  : CoerceArray<[]>;\n\n/**\n * Table<Row, UniqueConstraintViolation = never, AutoIncOverflow = never>\n *\n * - Row: row shape\n * - UCV: unique-constraint violation error type (never if none)\n * - AIO: auto-increment overflow error type (never if none)\n */\nexport type Table<TableDef extends UntypedTableDef> = Prettify<\n  TableMethods<TableDef> & Indexes<TableDef, TableIndexes<TableDef>>\n>;\n\nexport type ReadonlyTable<TableDef extends UntypedTableDef> = Prettify<\n  ReadonlyTableMethods<TableDef> &\n    ReadonlyIndexes<TableDef, TableIndexes<TableDef>>\n>;\n\nexport interface ReadonlyTableMethods<TableDef extends UntypedTableDef> {\n  /**\n   * Returns the number of rows in this table.\n   *\n   * This reads datastore metadata, so it runs in constant time.\n   * It also takes into account modifications by the current transaction.\n   */\n  count(): bigint;\n\n  /** Iterate over all rows in the TX state. Rust Iterator<Item=Row> → TS IterableIterator<Row>. */\n  iter(): IteratorObject<Prettify<RowType<TableDef>>, undefined>;\n  [Symbol.iterator](): IteratorObject<Prettify<RowType<TableDef>>, undefined>;\n}\n\n/**\n * A type representing the methods available on a table.\n */\nexport interface TableMethods<TableDef extends UntypedTableDef>\n  extends ReadonlyTableMethods<TableDef> {\n  /**\n   * Insert and return the inserted row (auto-increment fields filled).\n   *\n   * May throw on error:\n   * * If there are any unique or primary key columns in this table, may throw {@link errors.UniqueAlreadyExists}.\n   * * If there are any auto-incrementing columns in this table, may throw {@link errors.AutoIncOverflow}.\n   * */\n  insert(row: Prettify<RowType<TableDef>>): Prettify<RowType<TableDef>>;\n\n  /** Delete a row equal to `row`. Returns true if something was deleted. */\n  delete(row: Prettify<RowType<TableDef>>): boolean;\n}\n\n/**\n * Defines a database table with schema and options.\n *\n * @param opts - Table configuration including name, indexes, and access control\n * @param row - Product type defining the table's row structure\n * @returns Table handle for use in schema() function\n *\n * @example\n * ```ts\n * const playerTable = table(\n *   { name: 'player', public: true },\n *   {\n *     id: t.u32().primaryKey(),\n *     name: t.string().index('btree')\n *   }\n * );\n * ```\n *\n * ## Column Validation Error\n *\n * **If you see an error like \"Expected 3 arguments, but got 2\"**, this means\n * one of your columns has an invalid combination of attributes.\n *\n * Specifically, `default()` cannot be combined with:\n * - `primaryKey()`\n * - `unique()`\n * - `autoInc()`\n *\n * **Example of invalid code:**\n * ```ts\n * // ERROR: default() + primaryKey() is not allowed\n * const badTable = table(\n *   { name: 'bad' },\n *   { id: t.u64().default(0n).primaryKey() }  // <- This causes \"Expected 3 arguments\"\n * );\n * ```\n *\n * **How to fix:** Remove either `default()` or the constraint (`primaryKey`/`unique`/`autoInc`).\n */\nexport function table<Row extends RowObj, const Opts extends TableOpts<Row>>(\n  opts: Opts,\n  row: Row | RowBuilder<Row>,\n  // ⚠️ INTERNAL: This parameter enforces compile-time validation of column metadata.\n  // It is never passed at runtime. If you see \"Expected 3 arguments, but got 2\",\n  // it means a column has an invalid combination (e.g., default + primaryKey).\n  // See the JSDoc above for details on how to fix this error.\n  ..._: HasInvalidColumn<Row> extends true\n    ? [\n        error: ERROR_default_cannot_be_combined_with_primaryKey_unique_or_autoInc<\n          InvalidColumnNames<Row>\n        >,\n      ]\n    : []\n): TableSchema<CoerceRow<Row>, OptsIndices<Opts>> {\n  const {\n    name,\n    public: isPublic = false,\n    indexes: userIndexes = [],\n    scheduled,\n    event: isEvent = false,\n  } = opts;\n\n  // 1. column catalogue + helpers\n  const colIds = new Map<keyof Row & string, ColId>();\n  const colNameList: string[] = [];\n\n  if (!(row instanceof RowBuilder)) {\n    row = new RowBuilder(row);\n  }\n\n  row.algebraicType.value.elements.forEach((elem, i) => {\n    colIds.set(elem.name, i);\n    colNameList.push(elem.name);\n  });\n\n  // gather primary keys, per‑column indexes, uniques, sequences\n  const pk: ColList = [];\n  const indexes: (RawIndexDefV10 & { canonicalName?: string })[] = [];\n  const constraints: RawConstraintDefV10[] = [];\n  const sequences: RawSequenceDefV10[] = [];\n\n  let scheduleAtCol: ColId | undefined;\n  const defaultValues: RawColumnDefaultValueV10[] = [];\n\n  for (const [name, builder] of Object.entries(row.row)) {\n    const meta: ColumnMetadata<any> = builder.columnMetadata;\n\n    if (meta.isPrimaryKey) {\n      pk.push(colIds.get(name)!);\n    }\n\n    const isUnique = meta.isUnique || meta.isPrimaryKey;\n\n    // implicit 1‑column indexes\n    if (meta.indexType || isUnique) {\n      const algo = meta.indexType ?? 'btree';\n      const id = colIds.get(name)!;\n      let algorithm: RawIndexAlgorithm;\n      switch (algo) {\n        case 'btree':\n          algorithm = RawIndexAlgorithm.BTree([id]);\n          break;\n        case 'hash':\n          algorithm = RawIndexAlgorithm.Hash([id]);\n          break;\n        case 'direct':\n          algorithm = RawIndexAlgorithm.Direct(id);\n          break;\n      }\n      indexes.push({\n        sourceName: undefined, // Unnamed indexes will be assigned a globally unique name\n        accessorName: name,\n        algorithm,\n      });\n    }\n\n    if (isUnique) {\n      constraints.push({\n        sourceName: undefined,\n        data: { tag: 'Unique', value: { columns: [colIds.get(name)!] } },\n      });\n    }\n\n    if (meta.isAutoIncrement) {\n      sequences.push({\n        sourceName: undefined,\n        start: undefined,\n        minValue: undefined,\n        maxValue: undefined,\n        column: colIds.get(name)!,\n        increment: 1n,\n      });\n    }\n\n    if (meta.defaultValue) {\n      const writer = new BinaryWriter(16);\n      builder.serialize(writer, meta.defaultValue);\n      defaultValues.push({\n        colId: colIds.get(name)!,\n        value: writer.getBuffer(),\n      });\n    }\n\n    // If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column\n    if (scheduled) {\n      const algebraicType = builder.typeBuilder.algebraicType;\n      if (ScheduleAt.isScheduleAt(algebraicType)) {\n        scheduleAtCol = colIds.get(name)!;\n      }\n    }\n  }\n\n  // convert explicit multi‑column indexes coming from options.indexes\n  for (const indexOpts of userIndexes ?? []) {\n    const accessor = indexOpts.accessor;\n    if (typeof accessor !== 'string' || accessor.length === 0) {\n      const tableLabel = name ?? '<unnamed>';\n      const indexLabel = indexOpts.name ?? '<unnamed>';\n      throw new TypeError(\n        `Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`\n      );\n    }\n    let algorithm: RawIndexAlgorithm;\n    switch (indexOpts.algorithm) {\n      case 'btree':\n        algorithm = {\n          tag: 'BTree',\n          value: indexOpts.columns.map(c => colIds.get(c)!),\n        };\n        break;\n      case 'hash':\n        algorithm = {\n          tag: 'Hash',\n          value: indexOpts.columns.map(c => colIds.get(c)!),\n        };\n        break;\n      case 'direct':\n        algorithm = { tag: 'Direct', value: colIds.get(indexOpts.column)! };\n        break;\n    }\n\n    // Unnamed indexes are assigned a globally unique source name.\n    // `accessor` controls the TypeScript property used to access the index.\n    // `name` (if present) is preserved as the canonical schema name.\n    //\n    // IMPORTANT: we intentionally do not reject duplicate accessor names here.\n    // This preserves existing behavior for raw table definitions. Downstream\n    // runtime consumers decide how duplicates are resolved:\n    // - server runtime merges duplicate accessors onto one accessor object\n    // - client cache assignment is last-write-wins for duplicate accessors\n    indexes.push({\n      sourceName: undefined,\n      accessorName: accessor,\n      algorithm,\n      canonicalName: indexOpts.name,\n    });\n  }\n\n  // add explicit constraints from options.constraints\n  for (const constraintOpts of opts.constraints ?? []) {\n    if (constraintOpts.constraint === 'unique') {\n      const data: RawConstraintDefV10['data'] = {\n        tag: 'Unique',\n        value: { columns: constraintOpts.columns.map(c => colIds.get(c)!) },\n      };\n      constraints.push({ sourceName: constraintOpts.name, data });\n      continue;\n    }\n  }\n\n  const productType = row.algebraicType.value as RowBuilder<\n    CoerceRow<Row>\n  >['algebraicType']['value'];\n\n  const schedule =\n    scheduled && scheduleAtCol !== undefined\n      ? { scheduleAtCol, reducer: scheduled }\n      : undefined;\n\n  return {\n    rowType: row as RowBuilder<CoerceRow<Row>>,\n    tableName: name,\n    rowSpacetimeType: productType,\n    tableDef: (ctx, accName) => {\n      const tableName = name ?? accName;\n      if (row.typeName === undefined) {\n        row.typeName = toPascalCase(tableName);\n      }\n\n      // Build index source names using accName\n      for (const index of indexes) {\n        const cols =\n          index.algorithm.tag === 'Direct'\n            ? [index.algorithm.value]\n            : index.algorithm.value;\n\n        const colS = cols.map(i => colNameList[i]).join('_');\n        const sourceName =\n          (index.sourceName = `${accName}_${colS}_idx_${index.algorithm.tag.toLowerCase()}`);\n\n        const { canonicalName } = index;\n        if (canonicalName !== undefined) {\n          ctx.moduleDef.explicitNames.entries.push(\n            ExplicitNameEntry.Index({ sourceName, canonicalName })\n          );\n        }\n      }\n\n      return {\n        sourceName: accName,\n        productTypeRef: ctx.registerTypesRecursively(row).ref,\n        primaryKey: pk,\n        indexes,\n        constraints,\n        sequences,\n        tableType: { tag: 'User' },\n        tableAccess: { tag: isPublic ? 'Public' : 'Private' },\n        defaultValues,\n        isEvent,\n      };\n    },\n    // Preserve the declared index options as runtime data so `tableToSchema`\n    // can expose them without type-smuggling.\n    idxs: userIndexes as OptsIndices<Opts>,\n    constraints: constraints as OptsConstraints<Opts>,\n    schedule,\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/table_schema.ts",
    "content": "import type { ProcedureExport, ReducerExport } from '../server';\nimport type { ProductType } from './algebraic_type';\nimport type { RawScheduleDefV10, RawTableDefV10 } from './autogen/types';\nimport type { IndexOpts } from './indexes';\nimport type { ModuleContext } from './schema';\nimport type { ColumnBuilder, RowBuilder } from './type_builders';\n\n/**\n * Represents a handle to a database table, including its name, row type, and row spacetime type.\n */\nexport type TableSchema<\n  Row extends Record<string, ColumnBuilder<any, any, any>>,\n  Idx extends readonly IndexOpts<keyof Row & string>[],\n> = {\n  /**\n   * The name of the table.\n   */\n  readonly tableName?: string;\n\n  /**\n   * The TypeBuilder representation of the type of the rows in the table.\n   **/\n  readonly rowType: RowBuilder<Row>;\n\n  /**\n   * The {@link ProductType} representing the structure of a row in the table.\n   */\n  readonly rowSpacetimeType: RowBuilder<Row>['algebraicType']['value'];\n\n  /**\n   * The {@link RawTableDefV10} of the configured table\n   */\n  tableDef(\n    ctx: ModuleContext,\n    accName: string\n  ): RawTableDefV10 & { schedule?: RawScheduleDefV10 };\n\n  /**\n   * The indexes defined on the table.\n   */\n  readonly idxs: Idx;\n\n  /**\n   * The constraints defined on the table.\n   */\n  readonly constraints: readonly {\n    name: string | undefined;\n    constraint: 'unique';\n    columns: [any];\n  }[];\n\n  /**\n   * The schedule defined on the table, if any.\n   */\n  readonly schedule?: {\n    scheduleAtCol: number;\n    reducer: () => ReducerExport<any, any> | ProcedureExport<any, any, any>;\n  };\n};\n\nexport type UntypedTableSchema = TableSchema<\n  Record<string, ColumnBuilder<any, any, any>>,\n  readonly IndexOpts<string>[]\n>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/time_duration.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\n\nexport type TimeDurationAlgebraicType = {\n  tag: 'Product';\n  value: {\n    elements: [\n      { name: '__time_duration_micros__'; algebraicType: { tag: 'I64' } },\n    ];\n  };\n};\n\n/**\n * A difference between two points in time, represented as a number of microseconds.\n */\nexport class TimeDuration {\n  __time_duration_micros__: bigint;\n\n  private static MICROS_PER_MILLIS: bigint = 1000n;\n\n  /**\n   * Get the algebraic type representation of the {@link TimeDuration} type.\n   * @returns The algebraic type representation of the type.\n   */\n  static getAlgebraicType(): TimeDurationAlgebraicType {\n    return AlgebraicType.Product({\n      elements: [\n        {\n          name: '__time_duration_micros__',\n          algebraicType: AlgebraicType.I64,\n        },\n      ],\n    });\n  }\n\n  static isTimeDuration(\n    algebraicType: AlgebraicType\n  ): algebraicType is TimeDurationAlgebraicType {\n    if (algebraicType.tag !== 'Product') {\n      return false;\n    }\n    const elements = algebraicType.value.elements;\n    if (elements.length !== 1) {\n      return false;\n    }\n    const microsElement = elements[0];\n    return (\n      microsElement.name === '__time_duration_micros__' &&\n      microsElement.algebraicType.tag === 'I64'\n    );\n  }\n\n  get micros(): bigint {\n    return this.__time_duration_micros__;\n  }\n\n  get millis(): number {\n    return Number(this.micros / TimeDuration.MICROS_PER_MILLIS);\n  }\n\n  constructor(micros: bigint) {\n    this.__time_duration_micros__ = micros;\n  }\n\n  static fromMillis(millis: number): TimeDuration {\n    return new TimeDuration(BigInt(millis) * TimeDuration.MICROS_PER_MILLIS);\n  }\n\n  /** This outputs the same string format that we use in the host and in Rust modules */\n  toString(): string {\n    const micros = this.micros;\n    const sign = micros < 0 ? '-' : '+';\n    const pos = micros < 0 ? -micros : micros;\n    const secs = pos / 1_000_000n;\n    const micros_remaining = pos % 1_000_000n;\n    return `${sign}${secs}.${String(micros_remaining).padStart(6, '0')}`;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/timestamp.ts",
    "content": "import { AlgebraicType } from './algebraic_type';\nimport { TimeDuration } from './time_duration';\n\nexport type TimestampAlgebraicType = {\n  tag: 'Product';\n  value: {\n    elements: [\n      {\n        name: '__timestamp_micros_since_unix_epoch__';\n        algebraicType: { tag: 'I64' };\n      },\n    ];\n  };\n};\n\n/**\n * A point in time, represented as a number of microseconds since the Unix epoch.\n */\nexport class Timestamp {\n  __timestamp_micros_since_unix_epoch__: bigint;\n\n  private static MICROS_PER_MILLIS: bigint = 1000n;\n\n  get microsSinceUnixEpoch(): bigint {\n    return this.__timestamp_micros_since_unix_epoch__;\n  }\n\n  constructor(micros: bigint) {\n    this.__timestamp_micros_since_unix_epoch__ = micros;\n  }\n\n  /**\n   * Get the algebraic type representation of the {@link Timestamp} type.\n   * @returns The algebraic type representation of the type.\n   */\n  static getAlgebraicType(): TimestampAlgebraicType {\n    return AlgebraicType.Product({\n      elements: [\n        {\n          name: '__timestamp_micros_since_unix_epoch__',\n          algebraicType: AlgebraicType.I64,\n        },\n      ],\n    });\n  }\n\n  static isTimestamp(\n    algebraicType: AlgebraicType\n  ): algebraicType is TimestampAlgebraicType {\n    if (algebraicType.tag !== 'Product') {\n      return false;\n    }\n    const elements = algebraicType.value.elements;\n    if (elements.length !== 1) {\n      return false;\n    }\n    const microsElement = elements[0];\n    return (\n      microsElement.name === '__timestamp_micros_since_unix_epoch__' &&\n      microsElement.algebraicType.tag === 'I64'\n    );\n  }\n\n  /**\n   * The Unix epoch, the midnight at the beginning of January 1, 1970, UTC.\n   */\n  static UNIX_EPOCH: Timestamp = new Timestamp(0n);\n\n  /**\n   * Get a `Timestamp` representing the execution environment's belief of the current moment in time.\n   */\n  static now(): Timestamp {\n    return Timestamp.fromDate(new Date());\n  }\n\n  /** Convert to milliseconds since Unix epoch. */\n  toMillis(): bigint {\n    return this.microsSinceUnixEpoch / 1000n;\n  }\n\n  /**\n   * Get a `Timestamp` representing the same point in time as `date`.\n   */\n  static fromDate(date: Date): Timestamp {\n    const millis = date.getTime();\n    const micros = BigInt(millis) * Timestamp.MICROS_PER_MILLIS;\n    return new Timestamp(micros);\n  }\n\n  /**\n   * Get a `Date` representing approximately the same point in time as `this`.\n   *\n   * This method truncates to millisecond precision,\n   * and throws `RangeError` if the `Timestamp` is outside the range representable as a `Date`.\n   */\n  toDate(): Date {\n    const micros = this.__timestamp_micros_since_unix_epoch__;\n    const millis = micros / Timestamp.MICROS_PER_MILLIS;\n    if (\n      millis > BigInt(Number.MAX_SAFE_INTEGER) ||\n      millis < BigInt(Number.MIN_SAFE_INTEGER)\n    ) {\n      throw new RangeError(\n        \"Timestamp is outside of the representable range of JS's Date\"\n      );\n    }\n    return new Date(Number(millis));\n  }\n\n  /**\n   * Get an ISO 8601 / RFC 3339 formatted string representation of this timestamp with microsecond precision.\n   *\n   * This method preserves the full microsecond precision of the timestamp,\n   * and throws `RangeError` if the `Timestamp` is outside the range representable in ISO format.\n   *\n   * @returns ISO 8601 formatted string with microsecond precision (e.g., '2025-02-17T10:30:45.123456Z')\n   */\n  toISOString(): string {\n    const micros = this.__timestamp_micros_since_unix_epoch__;\n    const millis = micros / Timestamp.MICROS_PER_MILLIS;\n\n    if (\n      millis > BigInt(Number.MAX_SAFE_INTEGER) ||\n      millis < BigInt(Number.MIN_SAFE_INTEGER)\n    ) {\n      throw new RangeError(\n        'Timestamp is outside of the representable range for ISO string formatting'\n      );\n    }\n\n    const date = new Date(Number(millis));\n    const isoBase = date.toISOString(); // Format: '2025-02-17T10:30:45.123Z'\n\n    // Extract the full 6 decimal places of microseconds\n    const microsRemainder = Math.abs(Number(micros % 1000000n));\n    const fractionalPart = String(microsRemainder).padStart(6, '0');\n\n    // Replace the 3-digit millisecond part with the full 6-digit microsecond part\n    return isoBase.replace(/\\.\\d{3}Z$/, `.${fractionalPart}Z`);\n  }\n\n  since(other: Timestamp): TimeDuration {\n    return new TimeDuration(\n      this.__timestamp_micros_since_unix_epoch__ -\n        other.__timestamp_micros_since_unix_epoch__\n    );\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/type_builders.test-d.ts",
    "content": "import { t } from '../server';\nimport { type AlgebraicTypeVariants } from '..';\nimport type {\n  I32ColumnBuilder,\n  I64ColumnBuilder,\n  InferTypeOfRow,\n  InferTypeOfTypeBuilder,\n  TypeBuilder,\n  U8ColumnBuilder,\n} from './type_builders';\n\ntype MustBeNever<T> = [T] extends [never]\n  ? true\n  : ['Error: Type must be never', T];\n\n// Test type inference on a row\n// i.e. a Record<string, TypeBuilder | ColumnBuilder> type\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst row = {\n  foo: t.string(),\n  bar: t.i32().primaryKey(),\n  idx: t.i64().index('btree').unique(),\n};\ntype Row = InferTypeOfRow<typeof row>;\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _row: Row = {\n  foo: 'hello',\n  bar: 42,\n  idx: 100n,\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst rowOptionOptional = {\n  foo: t.string().optional().optional(),\n};\ntype RowOptionOptional = InferTypeOfRow<typeof rowOptionOptional>;\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _rowOptionOptionalNone: RowOptionOptional = {\n  foo: undefined,\n};\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _rowOptionOptionalSome: RowOptionOptional = {\n  foo: 'hello',\n};\n\n// Test that a row must not allow non-TypeBuilder or ColumnBuilder values\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst row2 = {\n  foo: {\n    // bar is not a TypeBuilder or ColumnBuilder, so this should fail\n    bar: t.string(),\n  },\n  bar: t.i32().primaryKey(),\n  idx: t.i64().index('btree').unique(),\n};\n// @ts-expect-error this should error\ntype Row2 = InferTypeOfRow<typeof row2>;\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype _ = MustBeNever<Row2>;\n\n// Test type inference on a type with a nested object\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst point = t.object('Point', {\n  x: t.i32(),\n  y: t.f64(),\n  z: t.object('Foo', {\n    foo: t.string(),\n  }),\n});\ntype Point = InferTypeOfTypeBuilder<typeof point>;\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _point: Point = {\n  x: 1.0,\n  y: 2.0,\n  z: {\n    foo: 'bar',\n  },\n};\n\n// Test type inference on an enum\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst e = t.enum('E', {\n  A: t.string(),\n  B: t.number(),\n});\ntype E = InferTypeOfTypeBuilder<typeof e>;\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _e: E = { tag: 'A', value: 'hello' };\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _e2: E = { tag: 'B', value: 42 };\n\n// Test that the type of a row includes the correct ColumnBuilder types\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _row3: {\n  foo: TypeBuilder<string, AlgebraicTypeVariants.String>;\n  bar: I32ColumnBuilder<{\n    isPrimaryKey: true;\n  }>;\n  idx: I64ColumnBuilder<{\n    isUnique: true;\n    indexType: 'btree';\n  }>;\n} = {\n  foo: t.string(),\n  bar: t.i32().primaryKey(),\n  idx: t.i64().index('btree').unique(),\n};\n\n// Test that you can add the index and unique constraint in any order\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst _row4: {\n  foo: TypeBuilder<string, AlgebraicTypeVariants.String>;\n  baz: U8ColumnBuilder<{\n    isAutoIncrement: true;\n  }>;\n  bar: I32ColumnBuilder<{\n    isPrimaryKey: true;\n  }>;\n  idx: I64ColumnBuilder<{\n    isUnique: true;\n    indexType: 'btree';\n  }>;\n} = {\n  foo: t.string(),\n  baz: t.u8().autoInc(),\n  bar: t.i32().primaryKey(),\n  idx: t.i64().unique().index('btree'),\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/type_builders.ts",
    "content": "import { AlgebraicType, type AlgebraicTypeVariants } from './algebraic_type';\nimport type BinaryReader from './binary_reader';\nimport type BinaryWriter from './binary_writer';\nimport { ConnectionId, type ConnectionIdAlgebraicType } from './connection_id';\nimport { Identity, type IdentityAlgebraicType } from './identity';\nimport { Option, type OptionAlgebraicType } from './option';\nimport { Result, type ResultAlgebraicType } from './result';\nimport ScheduleAt, { type ScheduleAtAlgebraicType } from './schedule_at';\nimport type { CoerceRow } from './table';\nimport { TimeDuration, type TimeDurationAlgebraicType } from './time_duration';\nimport { Timestamp, type TimestampAlgebraicType } from './timestamp';\nimport { set, type Prettify, type SetField } from './type_util';\nimport { Uuid, type UuidAlgebraicType } from './uuid';\n\n// Used in codegen files\nexport { type AlgebraicTypeType } from './algebraic_type';\n\n/**\n * Helper type to extract the TypeScript type from a TypeBuilder\n */\nexport type InferTypeOfTypeBuilder<T extends TypeBuilder<any, any>> =\n  T extends TypeBuilder<infer U, any> ? Prettify<U> : never;\n\n/**\n * Helper type to extract the Spacetime type from a TypeBuilder\n */\nexport type InferSpacetimeTypeOfTypeBuilder<T extends TypeBuilder<any, any>> =\n  T extends TypeBuilder<any, infer U> ? U : never;\n\n/**\n * Helper type to extract the TypeScript type from a TypeBuilder\n */\nexport type Infer<T> = T extends RowObj\n  ? InferTypeOfRow<T>\n  : T extends TypeBuilder<any, any>\n    ? InferTypeOfTypeBuilder<T>\n    : never;\n\n/**\n * Helper type to extract the type of a row from an object.\n */\nexport type InferTypeOfRow<T extends RowObj> = {\n  [K in keyof T & string]: InferTypeOfTypeBuilder<CollapseColumn<T[K]>>;\n};\n\n/**\n * Helper type to extract the type of a row from an object.\n */\nexport type InferSpacetimeTypeOfRow<T extends RowObj> = {\n  [K in keyof T & string]: InferSpacetimeTypeOfTypeBuilder<\n    CollapseColumn<T[K]>\n  >;\n};\n\n/**\n * Helper type to extract the Spacetime type from a row object.\n */\ntype CollapseColumn<\n  T extends TypeBuilder<any, any> | ColumnBuilder<any, any, any>,\n> = T extends ColumnBuilder<any, any, any> ? T['typeBuilder'] : T;\n\n/**\n * A type representing an object which is used to define the type of\n * a row in a table.\n */\nexport type RowObj = Record<\n  string,\n  TypeBuilder<any, any> | ColumnBuilder<any, any, ColumnMetadata<any>>\n>;\n\n/**\n * Type which converts the elements of RowObj to a ProductType elements array\n */\ntype ElementsArrayFromRowObj<Obj extends RowObj> = Array<\n  {\n    [N in keyof Obj & string]: {\n      name: N;\n      algebraicType: InferSpacetimeTypeOfTypeBuilder<CollapseColumn<Obj[N]>>;\n    };\n  }[keyof Obj & string]\n>;\n\n/**\n * A type which converts the elements of RowObj to a TypeScript object type.\n * It works by `Infer`ing the types of the column builders which are the values of\n * the keys in the object passed in.\n *\n * e.g. { a: I32TypeBuilder, b: StringBuilder } -> { a: number, b: string }\n */\ntype RowType<Row extends RowObj> = {\n  [K in keyof Row]: InferTypeOfTypeBuilder<CollapseColumn<Row[K]>>;\n};\n\n/**\n * Type which represents a valid argument to the ProductColumnBuilder\n */\nexport type ElementsObj = Record<string, TypeBuilder<any, any>>;\n\n/**\n * Type which converts the elements of ElementsObj to a ProductType elements array\n */\ntype ElementsArrayFromElementsObj<Obj extends ElementsObj> = Array<\n  {\n    [N in keyof Obj & string]: {\n      name: N;\n      algebraicType: InferSpacetimeTypeOfTypeBuilder<Obj[N]>;\n    };\n  }[keyof Obj & string]\n>;\n\n/**\n * A type which converts the elements of ElementsObj to a TypeScript object type.\n * It works by `Infer`ing the types of the column builders which are the values of\n * the keys in the object passed in.\n *\n * e.g. { a: I32TypeBuilder, b: StringBuilder } -> { a: number, b: string }\n */\ntype ObjectType<Elements extends ElementsObj> = {\n  [K in keyof Elements]: InferTypeOfTypeBuilder<Elements[K]>;\n};\n\nexport type VariantsObj = Record<string, TypeBuilder<any, any>>;\ntype SimpleVariantsObj = Record<string, UnitBuilder>;\n\ntype IsUnit<B> = B extends UnitBuilder ? true : false;\n\n/**\n * A type which converts the elements of ElementsObj to a TypeScript object type.\n * It works by `Infer`ing the types of the column builders which are the values of\n * the keys in the object passed in.\n *\n * e.g. { A: I32TypeBuilder, B: StringBuilder } -> { tag: \"A\", value: number } | { tag: \"B\", value: string }\n */\ntype EnumType<Variants extends VariantsObj> = {\n  [K in keyof Variants & string]: IsUnit<Variants[K]> extends true\n    ? { tag: K }\n    : { tag: K; value: InferTypeOfTypeBuilder<Variants[K]> };\n}[keyof Variants & string];\n\n/**\n * Type which converts the elements of VariantsObj to a SumType variants array\n */\ntype VariantsArrayFromVariantsObj<Obj extends VariantsObj> = {\n  name: keyof Obj & string;\n  algebraicType: InferSpacetimeTypeOfTypeBuilder<Obj[keyof Obj & string]>;\n}[];\n\n/**\n * A generic type builder that captures both the TypeScript type\n * and the corresponding `AlgebraicType`.\n */\nexport class TypeBuilder<Type, SpacetimeType extends AlgebraicType>\n  implements Optional<Type, SpacetimeType>\n{\n  /**\n   * The TypeScript phantom type. This is not stored at runtime,\n   * but is visible to the compiler\n   */\n  readonly type!: Type;\n\n  /**\n   * The SpacetimeDB algebraic type (run‑time value). In addition to storing\n   * the runtime representation of the `AlgebraicType`, it also captures\n   * the TypeScript type information of the `AlgebraicType`. That is to say\n   * the value is not merely an `AlgebraicType`, but is constructed to be\n   * the corresponding concrete `AlgebraicType` for the TypeScript type `Type`.\n   *\n   * e.g. `string` corresponds to `AlgebraicType.String`\n   */\n  readonly algebraicType: SpacetimeType;\n\n  constructor(algebraicType: SpacetimeType) {\n    this.algebraicType = algebraicType;\n  }\n\n  optional(): OptionBuilder<typeof this> {\n    return new OptionBuilder(this);\n  }\n\n  serialize(writer: BinaryWriter, value: Type): void {\n    const serialize = (this.serialize = AlgebraicType.makeSerializer(\n      this.algebraicType\n    ));\n    serialize(writer, value);\n  }\n\n  deserialize(reader: BinaryReader): Type {\n    const deserialize = (this.deserialize = AlgebraicType.makeDeserializer(\n      this.algebraicType\n    ));\n    return deserialize(reader);\n  }\n}\n\n/**\n * Interface for types that can be converted into a column builder with primary key metadata.\n *\n * Implementing this interface allows a type to be marked as the primary key of a table column\n * in a type-safe manner. The `primaryKey()` method returns a new `ColumnBuilder` instance\n * with the metadata updated to indicate that the column is a primary key.\n *\n * @typeParam Type - The TypeScript type of the column's value.\n * @typeParam SpacetimeType - The corresponding SpacetimeDB algebraic type.\n * @typeParam M - The metadata type for the column, defaulting to `DefaultMetadata`.\n *\n * @remarks\n * - This interface is typically implemented by type builders for primitive and complex types.\n * - The returned `ColumnBuilder` will have its metadata extended with `{ isPrimaryKey: true }`.\n * - **Cannot be combined with `default()`.**\n */\ninterface PrimaryKeyable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify this column as primary key\n   * @remarks Cannot be combined with `default()`.\n   */\n  primaryKey(): ColumnBuilder<\n    Type,\n    SpacetimeType,\n    SetField<M, 'isPrimaryKey', true>\n  >;\n}\n\n/**\n * Interface for types that can be converted into a column builder with unique metadata.\n *\n * Implementing this interface allows a type to be marked as unique in a table column\n * in a type-safe manner. The `unique()` method returns a new `ColumnBuilder` instance\n * with the metadata updated to indicate that the column is unique.\n *\n * @typeParam Type - The TypeScript type of the column's value.\n * @typeParam SpacetimeType - The corresponding SpacetimeDB algebraic type.\n * @typeParam M - The metadata type for the column, defaulting to `DefaultMetadata`.\n *\n * @remarks\n * - This interface is typically implemented by type builders for primitive and complex types.\n * - The returned `ColumnBuilder` will have its metadata extended with `{ isUnique: true }`.\n * - **Cannot be combined with `default()`.**\n */\ninterface Uniqueable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify this column as unique\n   * @remarks Cannot be combined with `default()`.\n   */\n  unique(): ColumnBuilder<Type, SpacetimeType, SetField<M, 'isUnique', true>>;\n}\n\n/**\n * Interface for types that can be converted into a column builder with index metadata.\n *\n * Implementing this interface allows a type to be indexed in a table column\n * in a type-safe manner. The `index()` method returns a new `ColumnBuilder` instance\n * with the metadata updated to indicate the index type.\n *\n * @typeParam Type - The TypeScript type of the column's value.\n * @typeParam SpacetimeType - The corresponding SpacetimeDB algebraic type.\n * @typeParam M - The metadata type for the column, defaulting to `DefaultMetadata`.\n *\n * @remarks\n * - This interface is typically implemented by type builders for primitive and complex types.\n * - The returned `ColumnBuilder` will have its metadata extended with `{ indexType: N }`.\n * - Indexing a column may have implications for performance and query optimization.\n */\ninterface Indexable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify the index type for this column\n   * @param algorithm The index algorithm to use\n   */\n  index(): ColumnBuilder<\n    Type,\n    SpacetimeType,\n    SetField<M, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): ColumnBuilder<Type, SpacetimeType, SetField<M, 'indexType', N>>;\n}\n\n/**\n * Interface for types that can be converted into a column builder with auto-increment metadata.\n *\n * Implementing this interface allows a type to be marked as auto-incrementing in a table column\n * in a type-safe manner. The `autoInc()` method returns a new `ColumnBuilder` instance\n * with the metadata updated to indicate that the column is auto-incrementing.\n *\n * @typeParam Type - The TypeScript type of the column's value.\n * @typeParam SpacetimeType - The corresponding SpacetimeDB algebraic type.\n * @typeParam M - The metadata type for the column, defaulting to `DefaultMetadata`.\n *\n * @remarks\n * - This interface is typically implemented by type builders for primitive and complex types.\n * - The returned `ColumnBuilder` will have its metadata extended with `{ isAutoIncrement: true }`.\n * - **Cannot be combined with `default()`.**\n */\ninterface AutoIncrementable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify this column as auto-incrementing\n   * @remarks Cannot be combined with `default()`.\n   */\n  autoInc(): ColumnBuilder<\n    Type,\n    SpacetimeType,\n    SetField<M, 'isAutoIncrement', true>\n  >;\n}\n\n/**\n * Interface for types that can be converted into an optional type.\n * All {@link TypeBuilder}s implement this interface, however since the `optional()` method\n * returns an {@link OptionBuilder}, {@link OptionBuilder} controls what metadata is allowed\n * to be configured for the column. This allows us to restrict whether things like indexes\n * or unique constraints can be applied to optional columns.\n *\n * For this reason {@link ColumnBuilder} does not implement this interface.\n */\ninterface Optional<Type, SpacetimeType extends AlgebraicType> {\n  /**\n   * Specify this column as optional\n   */\n  optional(this: TypeBuilder<Type, SpacetimeType>): OptionBuilder<typeof this>;\n}\n\n/**\n * Interface for types that can be converted into a column builder with default value metadata.\n * Implementing this interface allows a type to have a default value specified in a table column\n * in a type-safe manner. The `default()` method returns a new `ColumnBuilder` instance\n * with the metadata updated to include the specified default value.\n *\n * @typeParam Type - The TypeScript type of the column's value.\n * @typeParam SpacetimeType - The corresponding SpacetimeDB algebraic type.\n * @typeParam M - The metadata type for the column, defaulting to `DefaultMetadata`.\n *\n * @remarks\n * - This interface is typically implemented by type builders for primitive and complex types.\n * - The returned `ColumnBuilder` will have its metadata extended with `{ default: value }`.\n * - The default value must be of the same type as the column's TypeScript type.\n * - This method can be called multiple times; the last call takes precedence.\n * - **Cannot be combined with `primaryKey()`, `unique()`, or `autoInc()`.**\n */\ninterface Defaultable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify a default value for this column\n   * @param value The default value for the column\n   * @example\n   * ```typescript\n   * const col = t.i32().default(42);\n   * ```\n   * @remarks\n   * - This method can be called multiple times; the last call takes precedence.\n   * - The default value must be of the same type as the column's TypeScript type.\n   * - Cannot be combined with `primaryKey()`, `unique()`, or `autoInc()`.\n   */\n  default(\n    value: Type\n  ): ColumnBuilder<Type, SpacetimeType, SetField<M, 'defaultValue', Type>>;\n}\n\ninterface Nameable<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  /**\n   * Specify the in-database name for this column.\n   */\n  name<const Name extends string>(\n    name: Name\n  ): Nameable<Type, SpacetimeType, SetField<M, 'name', Name>>;\n}\n\nexport class U8Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.U8>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U8>,\n    Uniqueable<number, AlgebraicTypeVariants.U8>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U8>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U8>,\n    Defaultable<number, AlgebraicTypeVariants.U8>,\n    Nameable<number, AlgebraicTypeVariants.U8>\n{\n  constructor() {\n    super(AlgebraicType.U8);\n  }\n  index(): U8ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U8ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U8ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U8ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U8ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U8ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): U8ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U8ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U8ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U8ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): U8ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new U8ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U8ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U8ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class U16Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.U16>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U16>,\n    Uniqueable<number, AlgebraicTypeVariants.U16>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U16>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U16>,\n    Defaultable<number, AlgebraicTypeVariants.U16>,\n    Nameable<number, AlgebraicTypeVariants.U16>\n{\n  constructor() {\n    super(AlgebraicType.U16);\n  }\n  index(): U16ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U16ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U16ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U16ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U16ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U16ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): U16ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U16ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U16ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U16ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): U16ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new U16ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U16ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U16ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class U32Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.U32>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U32>,\n    Uniqueable<number, AlgebraicTypeVariants.U32>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U32>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U32>,\n    Defaultable<number, AlgebraicTypeVariants.U32>,\n    Nameable<number, AlgebraicTypeVariants.U32>\n{\n  constructor() {\n    super(AlgebraicType.U32);\n  }\n  index(): U32ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U32ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U32ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U32ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U32ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U32ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): U32ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U32ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U32ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U32ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): U32ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new U32ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U32ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U32ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class U64Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.U64>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U64>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U64>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U64>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U64>,\n    Defaultable<bigint, AlgebraicTypeVariants.U64>,\n    Nameable<bigint, AlgebraicTypeVariants.U64>\n{\n  constructor() {\n    super(AlgebraicType.U64);\n  }\n  index(): U64ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U64ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U64ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U64ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U64ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U64ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): U64ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U64ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U64ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U64ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U64ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new U64ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U64ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U64ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class U128Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.U128>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U128>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U128>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U128>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U128>,\n    Defaultable<bigint, AlgebraicTypeVariants.U128>,\n    Nameable<bigint, AlgebraicTypeVariants.U128>\n{\n  constructor() {\n    super(AlgebraicType.U128);\n  }\n  index(): U128ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U128ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U128ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U128ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U128ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U128ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U128ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U128ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new U128ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U128ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U128ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class U256Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.U256>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U256>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U256>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U256>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U256>,\n    Defaultable<bigint, AlgebraicTypeVariants.U256>,\n    Nameable<bigint, AlgebraicTypeVariants.U256>\n{\n  constructor() {\n    super(AlgebraicType.U256);\n  }\n  index(): U256ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U256ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U256ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new U256ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U256ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new U256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U256ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new U256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U256ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new U256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U256ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new U256ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U256ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new U256ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I8Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.I8>\n  implements\n    Indexable<number, AlgebraicTypeVariants.I8>,\n    Uniqueable<number, AlgebraicTypeVariants.I8>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I8>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I8>,\n    Defaultable<number, AlgebraicTypeVariants.I8>,\n    Nameable<number, AlgebraicTypeVariants.I8>\n{\n  constructor() {\n    super(AlgebraicType.I8);\n  }\n  index(): I8ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I8ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I8ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I8ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I8ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I8ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): I8ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I8ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I8ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I8ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): I8ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new I8ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I8ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I8ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I16Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.I16>\n  implements\n    Indexable<number, AlgebraicTypeVariants.I16>,\n    Uniqueable<number, AlgebraicTypeVariants.I16>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I16>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I16>,\n    Defaultable<number, AlgebraicTypeVariants.I16>,\n    Nameable<number, AlgebraicTypeVariants.I16>\n{\n  constructor() {\n    super(AlgebraicType.I16);\n  }\n  index(): I16ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I16ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I16ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I16ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I16ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I16ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): I16ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I16ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I16ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I16ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): I16ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new I16ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I16ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I16ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I32Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.I32>\n  implements\n    TypeBuilder<number, AlgebraicTypeVariants.I32>,\n    Indexable<number, AlgebraicTypeVariants.I32>,\n    Uniqueable<number, AlgebraicTypeVariants.I32>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I32>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I32>,\n    Defaultable<number, AlgebraicTypeVariants.I32>,\n    Nameable<number, AlgebraicTypeVariants.I32>\n{\n  constructor() {\n    super(AlgebraicType.I32);\n  }\n  index(): I32ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I32ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I32ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I32ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I32ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I32ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): I32ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I32ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I32ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I32ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): I32ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new I32ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I32ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I32ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I64Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.I64>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I64>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I64>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I64>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I64>,\n    Defaultable<bigint, AlgebraicTypeVariants.I64>,\n    Nameable<bigint, AlgebraicTypeVariants.I64>\n{\n  constructor() {\n    super(AlgebraicType.I64);\n  }\n  index(): I64ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I64ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I64ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I64ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I64ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I64ColumnBuilder(this, set(defaultMetadata, { isUnique: true }));\n  }\n  primaryKey(): I64ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I64ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I64ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I64ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I64ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new I64ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I64ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I64ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I128Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.I128>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I128>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I128>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I128>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I128>,\n    Defaultable<bigint, AlgebraicTypeVariants.I128>,\n    Nameable<bigint, AlgebraicTypeVariants.I128>\n{\n  constructor() {\n    super(AlgebraicType.I128);\n  }\n  index(): I128ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I128ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I128ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I128ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I128ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I128ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I128ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I128ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I128ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new I128ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I128ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I128ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class I256Builder\n  extends TypeBuilder<bigint, AlgebraicTypeVariants.I256>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I256>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I256>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I256>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I256>,\n    Defaultable<bigint, AlgebraicTypeVariants.I256>,\n    Nameable<bigint, AlgebraicTypeVariants.I256>\n{\n  constructor() {\n    super(AlgebraicType.I256);\n  }\n  index(): I256ColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I256ColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I256ColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new I256ColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I256ColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new I256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I256ColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new I256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I256ColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new I256ColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I256ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', bigint>> {\n    return new I256ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I256ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new I256ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class F32Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.F32>\n  implements\n    Defaultable<number, AlgebraicTypeVariants.F32>,\n    Nameable<number, AlgebraicTypeVariants.F32>\n{\n  constructor() {\n    super(AlgebraicType.F32);\n  }\n  default(\n    value: number\n  ): F32ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new F32ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): F32ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new F32ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class F64Builder\n  extends TypeBuilder<number, AlgebraicTypeVariants.F64>\n  implements\n    Defaultable<number, AlgebraicTypeVariants.F64>,\n    Nameable<number, AlgebraicTypeVariants.F64>\n{\n  constructor() {\n    super(AlgebraicType.F64);\n  }\n  default(\n    value: number\n  ): F64ColumnBuilder<SetField<DefaultMetadata, 'defaultValue', number>> {\n    return new F64ColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): F64ColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new F64ColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class BoolBuilder\n  extends TypeBuilder<boolean, AlgebraicTypeVariants.Bool>\n  implements\n    Indexable<boolean, AlgebraicTypeVariants.Bool>,\n    Uniqueable<boolean, AlgebraicTypeVariants.Bool>,\n    PrimaryKeyable<boolean, AlgebraicTypeVariants.Bool>,\n    Defaultable<boolean, AlgebraicTypeVariants.Bool>,\n    Nameable<boolean, AlgebraicTypeVariants.Bool>\n{\n  constructor() {\n    super(AlgebraicType.Bool);\n  }\n  index(): BoolColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): BoolColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): BoolColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new BoolColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): BoolColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new BoolColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): BoolColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new BoolColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: boolean\n  ): BoolColumnBuilder<SetField<DefaultMetadata, 'defaultValue', boolean>> {\n    return new BoolColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): BoolColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new BoolColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class StringBuilder\n  extends TypeBuilder<string, AlgebraicTypeVariants.String>\n  implements\n    Indexable<string, AlgebraicTypeVariants.String>,\n    Uniqueable<string, AlgebraicTypeVariants.String>,\n    PrimaryKeyable<string, AlgebraicTypeVariants.String>,\n    Defaultable<string, AlgebraicTypeVariants.String>,\n    Nameable<string, AlgebraicTypeVariants.String>\n{\n  constructor() {\n    super(AlgebraicType.String);\n  }\n  index(): StringColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): StringColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): StringColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new StringColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): StringColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new StringColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): StringColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new StringColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: string\n  ): StringColumnBuilder<SetField<DefaultMetadata, 'defaultValue', string>> {\n    return new StringColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): StringColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new StringColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class ArrayBuilder<Element extends TypeBuilder<any, any>>\n  extends TypeBuilder<\n    Array<InferTypeOfTypeBuilder<Element>>,\n    { tag: 'Array'; value: InferSpacetimeTypeOfTypeBuilder<Element> }\n  >\n  implements\n    Defaultable<Array<InferTypeOfTypeBuilder<Element>>, any>,\n    Nameable<Array<InferTypeOfTypeBuilder<Element>>, any>\n{\n  element: Element;\n\n  constructor(element: Element) {\n    super(AlgebraicType.Array(element.algebraicType));\n    this.element = element;\n  }\n  default(\n    value: Array<InferTypeOfTypeBuilder<Element>>\n  ): ArrayColumnBuilder<\n    Element,\n    SetField<DefaultMetadata, 'defaultValue', any>\n  > {\n    return new ArrayColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ArrayColumnBuilder<Element, SetField<DefaultMetadata, 'name', Name>> {\n    return new ArrayColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class ByteArrayBuilder\n  extends TypeBuilder<\n    Uint8Array,\n    { tag: 'Array'; value: AlgebraicTypeVariants.U8 }\n  >\n  implements Defaultable<Uint8Array, any>, Nameable<Uint8Array, any>\n{\n  constructor() {\n    super(AlgebraicType.Array(AlgebraicType.U8));\n  }\n  default(\n    value: Uint8Array\n  ): ByteArrayColumnBuilder<SetField<DefaultMetadata, 'defaultValue', any>> {\n    return new ByteArrayColumnBuilder(\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ByteArrayColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new ByteArrayColumnBuilder(set(defaultMetadata, { name }));\n  }\n}\n\nexport class OptionBuilder<Value extends TypeBuilder<any, any>>\n  extends TypeBuilder<\n    InferTypeOfTypeBuilder<Value> | undefined,\n    OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>\n  >\n  implements\n    Defaultable<\n      InferTypeOfTypeBuilder<Value> | undefined,\n      OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>\n    >,\n    Nameable<\n      InferTypeOfTypeBuilder<Value> | undefined,\n      OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>\n    >\n{\n  value: Value;\n\n  constructor(value: Value) {\n    super(Option.getAlgebraicType(value.algebraicType));\n    this.value = value;\n  }\n  default(\n    value: InferTypeOfTypeBuilder<Value> | undefined\n  ): OptionColumnBuilder<\n    Value,\n    SetField<\n      DefaultMetadata,\n      'defaultValue',\n      InferTypeOfTypeBuilder<Value> | undefined\n    >\n  > {\n    return new OptionColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): OptionColumnBuilder<Value, SetField<DefaultMetadata, 'name', Name>> {\n    return new OptionColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\ntype ElementsToProductType<Elements extends ElementsObj> = {\n  tag: 'Product';\n  value: { elements: ElementsArrayFromElementsObj<Elements> };\n};\n\nexport class ProductBuilder<Elements extends ElementsObj>\n  extends TypeBuilder<ObjectType<Elements>, ElementsToProductType<Elements>>\n  implements\n    Defaultable<ObjectType<Elements>, ElementsToProductType<Elements>>,\n    Nameable<ObjectType<Elements>, ElementsToProductType<Elements>>\n{\n  readonly typeName: string | undefined;\n  readonly elements: Elements;\n  constructor(elements: Elements, name?: string) {\n    function elementsArrayFromElementsObj<Obj extends ElementsObj>(obj: Obj) {\n      return Object.keys(obj).map(key => ({\n        name: key,\n        // Lazily resolve the underlying object's algebraicType.\n        // This will call obj[key].algebraicType only when someone\n        // actually reads this property.\n        get algebraicType() {\n          return obj[key].algebraicType;\n        },\n      }));\n    }\n    super(\n      AlgebraicType.Product({\n        elements: elementsArrayFromElementsObj(elements),\n      })\n    );\n    this.typeName = name;\n    this.elements = elements;\n  }\n  default(\n    value: ObjectType<Elements>\n  ): ProductColumnBuilder<\n    Elements,\n    SetField<DefaultMetadata, 'defaultValue', any>\n  > {\n    return new ProductColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ProductColumnBuilder<Elements, SetField<DefaultMetadata, 'name', Name>> {\n    return new ProductColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class ResultBuilder<\n    Ok extends TypeBuilder<any, any>,\n    Err extends TypeBuilder<any, any>,\n  >\n  extends TypeBuilder<\n    InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>,\n    ResultAlgebraicType<\n      InferSpacetimeTypeOfTypeBuilder<Ok>,\n      InferSpacetimeTypeOfTypeBuilder<Err>\n    >\n  >\n  implements\n    Defaultable<\n      InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>,\n      ResultAlgebraicType<\n        InferSpacetimeTypeOfTypeBuilder<Ok>,\n        InferSpacetimeTypeOfTypeBuilder<Err>\n      >\n    >\n{\n  ok: Ok;\n  err: Err;\n\n  constructor(ok: Ok, err: Err) {\n    super(Result.getAlgebraicType(ok.algebraicType, err.algebraicType));\n    this.ok = ok;\n    this.err = err;\n  }\n  default(\n    value: InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n  ): ResultColumnBuilder<\n    Ok,\n    Err,\n    SetField<\n      DefaultMetadata,\n      'defaultValue',\n      InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n    >\n  > {\n    return new ResultColumnBuilder<\n      Ok,\n      Err,\n      SetField<\n        DefaultMetadata,\n        'defaultValue',\n        InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n      >\n    >(this, set(defaultMetadata, { defaultValue: value }));\n  }\n}\n\nclass UnitBuilder extends TypeBuilder<\n  {},\n  { tag: 'Product'; value: { elements: [] } }\n> {\n  constructor() {\n    super({ tag: 'Product', value: { elements: [] } });\n  }\n}\n\nexport class RowBuilder<Row extends RowObj> extends TypeBuilder<\n  RowType<CoerceRow<Row>>,\n  {\n    tag: 'Product';\n    value: { elements: ElementsArrayFromRowObj<CoerceRow<Row>> };\n  }\n> {\n  readonly row: CoerceRow<Row>;\n  typeName: string | undefined;\n  constructor(row: Row, name?: string) {\n    const mappedRow = Object.fromEntries(\n      Object.entries(row).map(([colName, builder]) => [\n        colName,\n        builder instanceof ColumnBuilder\n          ? builder\n          : new ColumnBuilder(builder, {}),\n      ])\n    ) as CoerceRow<Row>;\n\n    const elements = Object.keys(mappedRow).map(name => ({\n      name,\n      get algebraicType() {\n        return mappedRow[name].typeBuilder.algebraicType;\n      },\n    }));\n\n    super(AlgebraicType.Product({ elements }));\n    this.row = mappedRow;\n    this.typeName = name;\n  }\n}\n\n// Value type produced for a given variant key + builder\ntype EnumValue<K extends string, B extends TypeBuilder<any, any>> =\n  IsUnit<B> extends true\n    ? { tag: K }\n    : { tag: K; value: InferTypeOfTypeBuilder<B> };\n\ntype VariantConstructor<K extends string, V extends TypeBuilder<any, any>> =\n  IsUnit<V> extends true\n    ? EnumValue<K, V>\n    : (value: InferTypeOfTypeBuilder<V>) => EnumValue<K, V>;\n\ntype SumBuilderVariantConstructors<Variants extends VariantsObj> = {\n  [K in keyof Variants & string]: VariantConstructor<K, Variants[K]>;\n};\n\nexport type SumBuilder<Variants extends VariantsObj> =\n  SumBuilderImpl<Variants> & SumBuilderVariantConstructors<Variants>;\n\ntype VariantsToSumType<Variants extends VariantsObj> = {\n  tag: 'Sum';\n  value: { variants: VariantsArrayFromVariantsObj<Variants> };\n};\n\nclass SumBuilderImpl<Variants extends VariantsObj>\n  extends TypeBuilder<EnumType<Variants>, VariantsToSumType<Variants>>\n  implements\n    Defaultable<EnumType<Variants>, VariantsToSumType<Variants>>,\n    Nameable<EnumType<Variants>, VariantsToSumType<Variants>>\n{\n  readonly variants: Variants;\n  readonly typeName: string | undefined;\n\n  constructor(variants: Variants, name?: string) {\n    function variantsArrayFromVariantsObj<Variants extends VariantsObj>(\n      variants: Variants\n    ) {\n      return (Object.keys(variants) as Array<keyof Variants>).map(key => ({\n        name: key as string,\n        // Lazily resolve the underlying object's algebraicType.\n        // This will call obj[key].algebraicType only when someone\n        // actually reads this property.\n        get algebraicType() {\n          return variants[key].algebraicType;\n        },\n      }));\n    }\n    super(\n      AlgebraicType.Sum({\n        variants: variantsArrayFromVariantsObj(variants),\n      })\n    );\n\n    this.variants = variants;\n    this.typeName = name;\n\n    for (const key of Object.keys(variants) as Array<keyof Variants & string>) {\n      const desc = Object.getOwnPropertyDescriptor(variants, key);\n\n      const isAccessor =\n        !!desc &&\n        (typeof desc.get === 'function' || typeof desc.set === 'function');\n\n      let isUnit = false;\n\n      if (!isAccessor) {\n        // Only read variants[key] if it's a *data* property\n        // otherwise assume non-unit because it's a getter\n        const variant = variants[key];\n        isUnit = variant instanceof UnitBuilder;\n      }\n\n      if (isUnit) {\n        // Unit: expose a read-only VALUE (no call)\n        const constant = this.create(key as any) as EnumValue<\n          typeof key,\n          Variants[typeof key]\n        >;\n        Object.defineProperty(this, key, {\n          value: constant,\n          writable: false,\n          enumerable: true,\n          configurable: false,\n        });\n      } else {\n        const fn = ((value: any) =>\n          this.create(key as any, value)) as VariantConstructor<\n          typeof key & string,\n          Variants[typeof key]\n        >;\n\n        Object.defineProperty(this, key, {\n          value: fn,\n          writable: false,\n          enumerable: true,\n          configurable: false,\n        });\n      }\n    }\n  }\n\n  /**\n   * Create a value of this sum type.\n   * - Unit variants: create('bar')\n   * - Payload variants: create('foo', value)\n   */\n  private create<K extends keyof Variants & string>(\n    tag: K\n  ): EnumValue<K, Variants[K]>;\n  private create<K extends keyof Variants & string>(\n    tag: K,\n    value: InferTypeOfTypeBuilder<Variants[K]>\n  ): EnumValue<K, Variants[K]>;\n  private create(tag: string, value?: unknown) {\n    return value === undefined ? { tag } : { tag, value };\n  }\n\n  default(\n    value: EnumType<Variants>\n  ): SumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'defaultValue', any>\n  > {\n    return new SumColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): SumColumnBuilder<Variants, SetField<DefaultMetadata, 'name', Name>> {\n    return new SumColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport const SumBuilder: {\n  new <Variants extends VariantsObj>(\n    variants: Variants,\n    name?: string\n  ): SumBuilder<Variants>;\n  [Symbol.hasInstance](x: any): x is SumBuilder<VariantsObj>;\n} = SumBuilderImpl as any;\n\nclass SimpleSumBuilderImpl<Variants extends SimpleVariantsObj>\n  extends SumBuilderImpl<Variants>\n  implements\n    Indexable<\n      EnumType<Variants>,\n      {\n        tag: 'Sum';\n        value: { variants: VariantsArrayFromVariantsObj<Variants> };\n      }\n    >,\n    PrimaryKeyable<\n      EnumType<Variants>,\n      {\n        tag: 'Sum';\n        value: { variants: VariantsArrayFromVariantsObj<Variants> };\n      }\n    >\n{\n  index(): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', N>\n  >;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', IndexTypes>\n  > {\n    return new SimpleSumColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  primaryKey(): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new SimpleSumColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n}\n\nexport const SimpleSumBuilder: {\n  new <Variants extends SimpleVariantsObj>(\n    variants: Variants,\n    name?: string\n  ): SimpleSumBuilderImpl<Variants> & SumBuilderVariantConstructors<Variants>;\n} = SimpleSumBuilderImpl as any;\n\nexport type SimpleSumBuilder<Variants extends SimpleVariantsObj> =\n  SimpleSumBuilderImpl<Variants> & SumBuilderVariantConstructors<Variants>;\n\nexport class ScheduleAtBuilder\n  extends TypeBuilder<ScheduleAt, ScheduleAtAlgebraicType>\n  implements\n    Defaultable<ScheduleAt, ScheduleAtAlgebraicType>,\n    Nameable<ScheduleAt, ScheduleAtAlgebraicType>\n{\n  constructor() {\n    super(ScheduleAt.getAlgebraicType());\n  }\n  default(\n    value: ScheduleAt\n  ): ScheduleAtColumnBuilder<\n    SetField<DefaultMetadata, 'defaultValue', ScheduleAt>\n  > {\n    return new ScheduleAtColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ScheduleAtColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new ScheduleAtColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class IdentityBuilder\n  extends TypeBuilder<Identity, IdentityAlgebraicType>\n  implements\n    Indexable<Identity, IdentityAlgebraicType>,\n    Uniqueable<Identity, IdentityAlgebraicType>,\n    PrimaryKeyable<Identity, IdentityAlgebraicType>,\n    Defaultable<Identity, IdentityAlgebraicType>,\n    Nameable<Identity, IdentityAlgebraicType>\n{\n  constructor() {\n    super(Identity.getAlgebraicType());\n  }\n  index(): IdentityColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): IdentityColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): IdentityColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new IdentityColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): IdentityColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new IdentityColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): IdentityColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new IdentityColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): IdentityColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new IdentityColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: Identity\n  ): IdentityColumnBuilder<\n    SetField<DefaultMetadata, 'defaultValue', Identity>\n  > {\n    return new IdentityColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): IdentityColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new IdentityColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class ConnectionIdBuilder\n  extends TypeBuilder<ConnectionId, ConnectionIdAlgebraicType>\n  implements\n    Indexable<ConnectionId, ConnectionIdAlgebraicType>,\n    Uniqueable<ConnectionId, ConnectionIdAlgebraicType>,\n    PrimaryKeyable<ConnectionId, ConnectionIdAlgebraicType>,\n    Defaultable<ConnectionId, ConnectionIdAlgebraicType>,\n    Nameable<ConnectionId, ConnectionIdAlgebraicType>\n{\n  constructor() {\n    super(ConnectionId.getAlgebraicType());\n  }\n  index(): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): ConnectionIdColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', IndexTypes>\n  > {\n    return new ConnectionIdColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'isUnique', true>\n  > {\n    return new ConnectionIdColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new ConnectionIdColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new ConnectionIdColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: ConnectionId\n  ): ConnectionIdColumnBuilder<\n    SetField<DefaultMetadata, 'defaultValue', ConnectionId>\n  > {\n    return new ConnectionIdColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ConnectionIdColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new ConnectionIdColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class TimestampBuilder\n  extends TypeBuilder<Timestamp, TimestampAlgebraicType>\n  implements\n    Indexable<Timestamp, TimestampAlgebraicType>,\n    Uniqueable<Timestamp, TimestampAlgebraicType>,\n    PrimaryKeyable<Timestamp, TimestampAlgebraicType>,\n    Defaultable<Timestamp, TimestampAlgebraicType>,\n    Nameable<Timestamp, TimestampAlgebraicType>\n{\n  constructor() {\n    super(Timestamp.getAlgebraicType());\n  }\n  index(): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): TimestampColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', IndexTypes>\n  > {\n    return new TimestampColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'isUnique', true>\n  > {\n    return new TimestampColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new TimestampColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new TimestampColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: Timestamp\n  ): TimestampColumnBuilder<\n    SetField<DefaultMetadata, 'defaultValue', Timestamp>\n  > {\n    return new TimestampColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): TimestampColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new TimestampColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class TimeDurationBuilder\n  extends TypeBuilder<TimeDuration, TimeDurationAlgebraicType>\n  implements\n    Indexable<TimeDuration, TimeDurationAlgebraicType>,\n    Uniqueable<TimeDuration, TimeDurationAlgebraicType>,\n    PrimaryKeyable<TimeDuration, TimeDurationAlgebraicType>,\n    Defaultable<TimeDuration, TimeDurationAlgebraicType>,\n    Nameable<TimeDuration, TimeDurationAlgebraicType>\n{\n  constructor() {\n    super(TimeDuration.getAlgebraicType());\n  }\n  index(): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): TimeDurationColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'indexType', IndexTypes>\n  > {\n    return new TimeDurationColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'isUnique', true>\n  > {\n    return new TimeDurationColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new TimeDurationColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new TimeDurationColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: TimeDuration\n  ): TimeDurationColumnBuilder<\n    SetField<DefaultMetadata, 'defaultValue', TimeDuration>\n  > {\n    return new TimeDurationColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): TimeDurationColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new TimeDurationColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\nexport class UuidBuilder\n  extends TypeBuilder<Uuid, UuidAlgebraicType>\n  implements\n    Indexable<Uuid, UuidAlgebraicType>,\n    Uniqueable<Uuid, UuidAlgebraicType>,\n    PrimaryKeyable<Uuid, UuidAlgebraicType>,\n    Defaultable<Uuid, UuidAlgebraicType>,\n    Nameable<Uuid, UuidAlgebraicType>\n{\n  constructor() {\n    super(Uuid.getAlgebraicType());\n  }\n  index(): UuidColumnBuilder<SetField<DefaultMetadata, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): UuidColumnBuilder<SetField<DefaultMetadata, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): UuidColumnBuilder<SetField<DefaultMetadata, 'indexType', IndexTypes>> {\n    return new UuidColumnBuilder(\n      this,\n      set(defaultMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): UuidColumnBuilder<SetField<DefaultMetadata, 'isUnique', true>> {\n    return new UuidColumnBuilder(\n      this,\n      set(defaultMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): UuidColumnBuilder<\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new UuidColumnBuilder(\n      this,\n      set(defaultMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): UuidColumnBuilder<\n    SetField<DefaultMetadata, 'isAutoIncrement', true>\n  > {\n    return new UuidColumnBuilder(\n      this,\n      set(defaultMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: Uuid\n  ): UuidColumnBuilder<SetField<DefaultMetadata, 'defaultValue', Uuid>> {\n    return new UuidColumnBuilder(\n      this,\n      set(defaultMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): UuidColumnBuilder<SetField<DefaultMetadata, 'name', Name>> {\n    return new UuidColumnBuilder(this, set(defaultMetadata, { name }));\n  }\n}\n\n/**\n * The type of index types that can be applied to a column.\n * `undefined` is the default\n */\nexport type IndexTypes = 'btree' | 'direct' | 'hash' | undefined;\n\n/**\n * Metadata describing column constraints and index type\n */\nexport type ColumnMetadata<Type = any> = {\n  isPrimaryKey?: true;\n  isUnique?: true;\n  isAutoIncrement?: true;\n  indexType?: IndexTypes;\n  defaultValue?: Type;\n  name?: string;\n};\n\n/**\n * Default metadata state type for a newly created column\n */\ntype DefaultMetadata = object;\n\n/**\n * Default metadata state value for a newly created column\n */\nconst defaultMetadata: ColumnMetadata<never> = {};\n\n/**\n * A column builder allows you to incrementally specify constraints\n * and metadata for a column in a type-safe way.\n *\n * It carries both a phantom TypeScript type (the `Type`) and\n * runtime algebraic type information.\n *\n * IMPORTANT! We have deliberately chosen to not have {@link ColumnBuilder}\n * extend {@link TypeBuilder} so that you cannot pass a {@link ColumnBuilder}\n * where a {@link TypeBuilder} is expected. i.e. We want to maintain\n * contravariance for functions that accept {@link TypeBuilder} parameters.\n */\nexport class ColumnBuilder<\n  Type,\n  SpacetimeType extends AlgebraicType,\n  M extends ColumnMetadata<Type> = DefaultMetadata,\n> {\n  typeBuilder: TypeBuilder<Type, SpacetimeType>;\n  columnMetadata: M;\n\n  constructor(typeBuilder: TypeBuilder<Type, SpacetimeType>, metadata: M) {\n    this.typeBuilder = typeBuilder;\n    this.columnMetadata = metadata;\n  }\n\n  serialize(writer: BinaryWriter, value: Type): void {\n    this.typeBuilder.serialize(writer, value);\n  }\n\n  deserialize(reader: BinaryReader): Type {\n    return this.typeBuilder.deserialize(reader);\n  }\n}\n\nexport class U8ColumnBuilder<M extends ColumnMetadata<number> = DefaultMetadata>\n  extends ColumnBuilder<number, AlgebraicTypeVariants.U8, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U8>,\n    Uniqueable<number, AlgebraicTypeVariants.U8>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U8>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U8>,\n    Defaultable<number, AlgebraicTypeVariants.U8>,\n    Nameable<number, AlgebraicTypeVariants.U8>\n{\n  index(): U8ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U8ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U8ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U8ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U8ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U8ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true as const })\n    );\n  }\n  default(value: number): U8ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U8ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class U16ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.U16, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U16>,\n    Uniqueable<number, AlgebraicTypeVariants.U16>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U16>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U16>,\n    Defaultable<number, AlgebraicTypeVariants.U16>,\n    Nameable<number, AlgebraicTypeVariants.U16>\n{\n  index(): U16ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U16ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U16ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U16ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U16ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U16ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): U16ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U16ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class U32ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.U32, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.U32>,\n    Uniqueable<number, AlgebraicTypeVariants.U32>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.U32>,\n    AutoIncrementable<number, AlgebraicTypeVariants.U32>,\n    Defaultable<number, AlgebraicTypeVariants.U32>,\n    Nameable<number, AlgebraicTypeVariants.U32>\n{\n  index(): U32ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U32ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U32ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U32ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U32ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U32ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): U32ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U32ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class U64ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.U64, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U64>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U64>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U64>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U64>,\n    Defaultable<bigint, AlgebraicTypeVariants.U64>,\n    Nameable<bigint, AlgebraicTypeVariants.U64>\n{\n  index(): U64ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U64ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U64ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U64ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U64ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U64ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U64ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U64ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class U128ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.U128, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U128>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U128>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U128>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U128>,\n    Defaultable<bigint, AlgebraicTypeVariants.U128>,\n    Nameable<bigint, AlgebraicTypeVariants.U128>\n{\n  index(): U128ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U128ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U128ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U128ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U128ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U128ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U128ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U128ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class U256ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.U256, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.U256>,\n    Uniqueable<bigint, AlgebraicTypeVariants.U256>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.U256>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.U256>,\n    Defaultable<bigint, AlgebraicTypeVariants.U256>,\n    Nameable<bigint, AlgebraicTypeVariants.U256>\n{\n  index(): U256ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): U256ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): U256ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): U256ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): U256ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): U256ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): U256ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): U256ColumnBuilder<SetField<M, 'name', Name>> {\n    return new U256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I8ColumnBuilder<M extends ColumnMetadata<number> = DefaultMetadata>\n  extends ColumnBuilder<number, AlgebraicTypeVariants.I8, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.I8>,\n    Uniqueable<number, AlgebraicTypeVariants.I8>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I8>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I8>,\n    Defaultable<number, AlgebraicTypeVariants.I8>,\n    Nameable<number, AlgebraicTypeVariants.I8>\n{\n  index(): I8ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I8ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I8ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I8ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I8ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I8ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(value: number): I8ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I8ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I8ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I16ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.I16, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.I16>,\n    Uniqueable<number, AlgebraicTypeVariants.I16>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I16>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I16>,\n    Defaultable<number, AlgebraicTypeVariants.I16>,\n    Nameable<number, AlgebraicTypeVariants.I16>\n{\n  index(): I16ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I16ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I16ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I16ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I16ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I16ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): I16ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I16ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I16ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I32ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.I32, M>\n  implements\n    Indexable<number, AlgebraicTypeVariants.I32>,\n    Uniqueable<number, AlgebraicTypeVariants.I32>,\n    PrimaryKeyable<number, AlgebraicTypeVariants.I32>,\n    AutoIncrementable<number, AlgebraicTypeVariants.I32>,\n    Defaultable<number, AlgebraicTypeVariants.I32>,\n    Nameable<number, AlgebraicTypeVariants.I32>\n{\n  index(): I32ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I32ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I32ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I32ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I32ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I32ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: number\n  ): I32ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I32ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I64ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.I64, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I64>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I64>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I64>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I64>,\n    Defaultable<bigint, AlgebraicTypeVariants.I64>,\n    Nameable<bigint, AlgebraicTypeVariants.I64>\n{\n  index(): I64ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I64ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I64ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I64ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I64ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I64ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I64ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I64ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I128ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.I128, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I128>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I128>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I128>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I128>,\n    Defaultable<bigint, AlgebraicTypeVariants.I128>,\n    Nameable<bigint, AlgebraicTypeVariants.I128>\n{\n  index(): I128ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I128ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I128ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I128ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I128ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I128ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I128ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I128ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I128ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class I256ColumnBuilder<\n    M extends ColumnMetadata<bigint> = DefaultMetadata,\n  >\n  extends ColumnBuilder<bigint, AlgebraicTypeVariants.I256, M>\n  implements\n    Indexable<bigint, AlgebraicTypeVariants.I256>,\n    Uniqueable<bigint, AlgebraicTypeVariants.I256>,\n    PrimaryKeyable<bigint, AlgebraicTypeVariants.I256>,\n    AutoIncrementable<bigint, AlgebraicTypeVariants.I256>,\n    Defaultable<bigint, AlgebraicTypeVariants.I256>,\n    Nameable<bigint, AlgebraicTypeVariants.I256>\n{\n  index(): I256ColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): I256ColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): I256ColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): I256ColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): I256ColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  autoInc(): I256ColumnBuilder<SetField<M, 'isAutoIncrement', true>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isAutoIncrement: true })\n    );\n  }\n  default(\n    value: bigint\n  ): I256ColumnBuilder<SetField<M, 'defaultValue', bigint>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): I256ColumnBuilder<SetField<M, 'name', Name>> {\n    return new I256ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class F32ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.F32, M>\n  implements\n    Defaultable<number, AlgebraicTypeVariants.F32>,\n    Nameable<number, AlgebraicTypeVariants.F32>\n{\n  default(\n    value: number\n  ): F32ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new F32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): F32ColumnBuilder<SetField<M, 'name', Name>> {\n    return new F32ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class F64ColumnBuilder<\n    M extends ColumnMetadata<number> = DefaultMetadata,\n  >\n  extends ColumnBuilder<number, AlgebraicTypeVariants.F64, M>\n  implements\n    Defaultable<number, AlgebraicTypeVariants.F64>,\n    Nameable<number, AlgebraicTypeVariants.F64>\n{\n  default(\n    value: number\n  ): F64ColumnBuilder<SetField<M, 'defaultValue', number>> {\n    return new F64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): F64ColumnBuilder<SetField<M, 'name', Name>> {\n    return new F64ColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class BoolColumnBuilder<\n    M extends ColumnMetadata<boolean> = DefaultMetadata,\n  >\n  extends ColumnBuilder<boolean, AlgebraicTypeVariants.Bool, M>\n  implements\n    Indexable<boolean, AlgebraicTypeVariants.Bool>,\n    Uniqueable<boolean, AlgebraicTypeVariants.Bool>,\n    PrimaryKeyable<boolean, AlgebraicTypeVariants.Bool>,\n    Defaultable<boolean, AlgebraicTypeVariants.Bool>,\n    Nameable<boolean, AlgebraicTypeVariants.Bool>\n{\n  index(): BoolColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): BoolColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): BoolColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new BoolColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): BoolColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new BoolColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): BoolColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new BoolColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: boolean\n  ): BoolColumnBuilder<SetField<M, 'defaultValue', boolean>> {\n    return new BoolColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): BoolColumnBuilder<SetField<M, 'name', Name>> {\n    return new BoolColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class StringColumnBuilder<\n    M extends ColumnMetadata<string> = DefaultMetadata,\n  >\n  extends ColumnBuilder<string, AlgebraicTypeVariants.String, M>\n  implements\n    Indexable<string, AlgebraicTypeVariants.String>,\n    Uniqueable<string, AlgebraicTypeVariants.String>,\n    PrimaryKeyable<string, AlgebraicTypeVariants.String>,\n    Defaultable<string, AlgebraicTypeVariants.String>,\n    Nameable<string, AlgebraicTypeVariants.String>\n{\n  index(): StringColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): StringColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): StringColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new StringColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): StringColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new StringColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): StringColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new StringColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: string\n  ): StringColumnBuilder<SetField<M, 'defaultValue', string>> {\n    return new StringColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): StringColumnBuilder<SetField<M, 'name', Name>> {\n    return new StringColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class ArrayColumnBuilder<\n    Element extends TypeBuilder<any, any>,\n    M extends ColumnMetadata<\n      Array<InferTypeOfTypeBuilder<Element>>\n    > = DefaultMetadata,\n  >\n  extends ColumnBuilder<\n    Array<InferTypeOfTypeBuilder<Element>>,\n    { tag: 'Array'; value: InferSpacetimeTypeOfTypeBuilder<Element> },\n    M\n  >\n  implements\n    Defaultable<\n      Array<InferTypeOfTypeBuilder<Element>>,\n      AlgebraicTypeVariants.Array\n    >,\n    Nameable<\n      Array<InferTypeOfTypeBuilder<Element>>,\n      AlgebraicTypeVariants.Array\n    >\n{\n  default(\n    value: Array<InferTypeOfTypeBuilder<Element>>\n  ): ArrayColumnBuilder<\n    Element,\n    SetField<M, 'defaultValue', Array<InferTypeOfTypeBuilder<Element>>>\n  > {\n    return new ArrayColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ArrayColumnBuilder<Element, SetField<M, 'name', Name>> {\n    return new ArrayColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\ntype ByteArrayType = {\n  tag: 'Array';\n  value: AlgebraicTypeVariants.U8;\n};\n\nexport class ByteArrayColumnBuilder<\n    M extends ColumnMetadata<Uint8Array> = DefaultMetadata,\n  >\n  extends ColumnBuilder<Uint8Array, ByteArrayType, M>\n  implements\n    Defaultable<Uint8Array, ByteArrayType, M>,\n    Nameable<Uint8Array, ByteArrayType, M>\n{\n  constructor(metadata: M) {\n    super(new TypeBuilder(AlgebraicType.Array(AlgebraicType.U8)), metadata);\n  }\n  default(\n    value: Uint8Array\n  ): ByteArrayColumnBuilder<SetField<M, 'defaultValue', Uint8Array>> {\n    return new ByteArrayColumnBuilder(\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ByteArrayColumnBuilder<SetField<M, 'name', Name>> {\n    return new ByteArrayColumnBuilder(set(this.columnMetadata, { name }));\n  }\n}\n\nexport class OptionColumnBuilder<\n    Value extends TypeBuilder<any, any>,\n    M extends ColumnMetadata<\n      InferTypeOfTypeBuilder<Value> | undefined\n    > = DefaultMetadata,\n  >\n  extends ColumnBuilder<\n    InferTypeOfTypeBuilder<Value> | undefined,\n    OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>,\n    M\n  >\n  implements\n    Defaultable<\n      InferTypeOfTypeBuilder<Value> | undefined,\n      OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>\n    >,\n    Nameable<\n      InferTypeOfTypeBuilder<Value> | undefined,\n      OptionAlgebraicType<InferSpacetimeTypeOfTypeBuilder<Value>>\n    >\n{\n  default(\n    value: InferTypeOfTypeBuilder<Value> | undefined\n  ): OptionColumnBuilder<\n    Value,\n    SetField<M, 'defaultValue', InferTypeOfTypeBuilder<Value> | undefined>\n  > {\n    return new OptionColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): OptionColumnBuilder<Value, SetField<M, 'name', Name>> {\n    return new OptionColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class ResultColumnBuilder<\n    Ok extends TypeBuilder<any, any>,\n    Err extends TypeBuilder<any, any>,\n    M extends ColumnMetadata<\n      InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n    > = DefaultMetadata,\n  >\n  extends ColumnBuilder<\n    InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>,\n    ResultAlgebraicType<\n      InferSpacetimeTypeOfTypeBuilder<Ok>,\n      InferSpacetimeTypeOfTypeBuilder<Err>\n    >,\n    M\n  >\n  implements\n    Defaultable<\n      InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>,\n      ResultAlgebraicType<\n        InferSpacetimeTypeOfTypeBuilder<Ok>,\n        InferSpacetimeTypeOfTypeBuilder<Err>\n      >\n    >\n{\n  constructor(typeBuilder: TypeBuilder<any, any>, metadata: M) {\n    super(typeBuilder, metadata);\n  }\n\n  default(\n    value: InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n  ): ResultColumnBuilder<\n    Ok,\n    Err,\n    SetField<\n      M,\n      'defaultValue',\n      InferTypeOfTypeBuilder<Ok> | InferTypeOfTypeBuilder<Err>\n    >\n  > {\n    return new ResultColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, {\n        defaultValue: value,\n      })\n    );\n  }\n}\n\nexport class ProductColumnBuilder<\n    Elements extends ElementsObj,\n    M extends ColumnMetadata<ObjectType<Elements>> = DefaultMetadata,\n  >\n  extends ColumnBuilder<\n    ObjectType<Elements>,\n    ElementsToProductType<Elements>,\n    M\n  >\n  implements\n    Defaultable<ObjectType<Elements>, ElementsToProductType<Elements>>,\n    Nameable<ObjectType<Elements>, ElementsToProductType<Elements>>\n{\n  default(\n    value: ObjectType<Elements>\n  ): ProductColumnBuilder<\n    Elements,\n    SetField<DefaultMetadata, 'defaultValue', any>\n  > {\n    return new ProductColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ProductColumnBuilder<Elements, SetField<DefaultMetadata, 'name', Name>> {\n    return new ProductColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class SumColumnBuilder<\n    Variants extends VariantsObj,\n    M extends ColumnMetadata<EnumType<Variants>> = DefaultMetadata,\n  >\n  extends ColumnBuilder<EnumType<Variants>, VariantsToSumType<Variants>, M>\n  implements\n    Defaultable<EnumType<Variants>, VariantsToSumType<Variants>>,\n    Nameable<EnumType<Variants>, VariantsToSumType<Variants>>\n{\n  default(\n    value: EnumType<Variants>\n  ): SumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'defaultValue', any>\n  > {\n    return new SumColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): SumColumnBuilder<Variants, SetField<DefaultMetadata, 'name', Name>> {\n    return new SumColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class SimpleSumColumnBuilder<\n    Variants extends VariantsObj,\n    M extends ColumnMetadata<EnumType<Variants>> = DefaultMetadata,\n  >\n  extends SumColumnBuilder<Variants, M>\n  implements\n    Indexable<EnumType<Variants>, AlgebraicTypeVariants.Sum>,\n    PrimaryKeyable<EnumType<Variants>, AlgebraicTypeVariants.Sum>\n{\n  index(): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', 'btree'>\n  >;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', N>\n  >;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'indexType', IndexTypes>\n  > {\n    return new SimpleSumColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  primaryKey(): SimpleSumColumnBuilder<\n    Variants,\n    SetField<DefaultMetadata, 'isPrimaryKey', true>\n  > {\n    return new SimpleSumColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n}\n\nexport class ScheduleAtColumnBuilder<\n    M extends ColumnMetadata<ScheduleAt> = DefaultMetadata,\n  >\n  extends ColumnBuilder<ScheduleAt, ScheduleAtAlgebraicType, M>\n  implements\n    Defaultable<ScheduleAt, ScheduleAtAlgebraicType>,\n    Nameable<ScheduleAt, ScheduleAtAlgebraicType>\n{\n  default(\n    value: ScheduleAt\n  ): ScheduleAtColumnBuilder<SetField<M, 'defaultValue', ScheduleAt>> {\n    return new ScheduleAtColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ScheduleAtColumnBuilder<SetField<M, 'name', Name>> {\n    return new ScheduleAtColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class IdentityColumnBuilder<\n    M extends ColumnMetadata<Identity> = DefaultMetadata,\n  >\n  extends ColumnBuilder<Identity, IdentityAlgebraicType, M>\n  implements\n    Indexable<Identity, IdentityAlgebraicType>,\n    Uniqueable<Identity, IdentityAlgebraicType>,\n    PrimaryKeyable<Identity, IdentityAlgebraicType>,\n    Defaultable<Identity, IdentityAlgebraicType>,\n    Nameable<Identity, IdentityAlgebraicType>\n{\n  index(): IdentityColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): IdentityColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): IdentityColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new IdentityColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): IdentityColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new IdentityColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): IdentityColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new IdentityColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: Identity\n  ): IdentityColumnBuilder<SetField<M, 'defaultValue', Identity>> {\n    return new IdentityColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): IdentityColumnBuilder<SetField<M, 'name', Name>> {\n    return new IdentityColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class ConnectionIdColumnBuilder<\n    M extends ColumnMetadata<ConnectionId> = DefaultMetadata,\n  >\n  extends ColumnBuilder<ConnectionId, ConnectionIdAlgebraicType, M>\n  implements\n    Indexable<ConnectionId, ConnectionIdAlgebraicType>,\n    Uniqueable<ConnectionId, ConnectionIdAlgebraicType>,\n    PrimaryKeyable<ConnectionId, ConnectionIdAlgebraicType>,\n    Defaultable<ConnectionId, ConnectionIdAlgebraicType>,\n    Nameable<ConnectionId, ConnectionIdAlgebraicType>\n{\n  index(): ConnectionIdColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): ConnectionIdColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): ConnectionIdColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new ConnectionIdColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): ConnectionIdColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new ConnectionIdColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): ConnectionIdColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new ConnectionIdColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: ConnectionId\n  ): ConnectionIdColumnBuilder<SetField<M, 'defaultValue', ConnectionId>> {\n    return new ConnectionIdColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): ConnectionIdColumnBuilder<SetField<M, 'name', Name>> {\n    return new ConnectionIdColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class TimestampColumnBuilder<\n    M extends ColumnMetadata<Timestamp> = DefaultMetadata,\n  >\n  extends ColumnBuilder<Timestamp, TimestampAlgebraicType, M>\n  implements\n    Indexable<Timestamp, TimestampAlgebraicType>,\n    Uniqueable<Timestamp, TimestampAlgebraicType>,\n    PrimaryKeyable<Timestamp, TimestampAlgebraicType>,\n    Defaultable<Timestamp, TimestampAlgebraicType>,\n    Nameable<Timestamp, TimestampAlgebraicType>\n{\n  index(): TimestampColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): TimestampColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): TimestampColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new TimestampColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): TimestampColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new TimestampColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): TimestampColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new TimestampColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: Timestamp\n  ): TimestampColumnBuilder<SetField<M, 'defaultValue', Timestamp>> {\n    return new TimestampColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): TimestampColumnBuilder<SetField<M, 'name', Name>> {\n    return new TimestampColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class TimeDurationColumnBuilder<\n    M extends ColumnMetadata<TimeDuration> = DefaultMetadata,\n  >\n  extends ColumnBuilder<TimeDuration, TimeDurationAlgebraicType, M>\n  implements\n    Indexable<TimeDuration, TimeDurationAlgebraicType>,\n    Uniqueable<TimeDuration, TimeDurationAlgebraicType>,\n    PrimaryKeyable<TimeDuration, TimeDurationAlgebraicType>,\n    Defaultable<TimeDuration, TimeDurationAlgebraicType>,\n    Nameable<TimeDuration, TimeDurationAlgebraicType>\n{\n  index(): TimeDurationColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): TimeDurationColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): TimeDurationColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new TimeDurationColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): TimeDurationColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new TimeDurationColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): TimeDurationColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new TimeDurationColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(\n    value: TimeDuration\n  ): TimeDurationColumnBuilder<SetField<M, 'defaultValue', TimeDuration>> {\n    return new TimeDurationColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): TimeDurationColumnBuilder<SetField<M, 'name', Name>> {\n    return new TimeDurationColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class UuidColumnBuilder<M extends ColumnMetadata<Uuid> = DefaultMetadata>\n  extends ColumnBuilder<Uuid, UuidAlgebraicType, M>\n  implements\n    Indexable<Uuid, UuidAlgebraicType>,\n    Uniqueable<Uuid, UuidAlgebraicType>,\n    PrimaryKeyable<Uuid, UuidAlgebraicType>,\n    Defaultable<Uuid, UuidAlgebraicType>,\n    Nameable<Uuid, UuidAlgebraicType>\n{\n  index(): UuidColumnBuilder<SetField<M, 'indexType', 'btree'>>;\n  index<N extends NonNullable<IndexTypes>>(\n    algorithm: N\n  ): UuidColumnBuilder<SetField<M, 'indexType', N>>;\n  index(\n    algorithm: IndexTypes = 'btree'\n  ): UuidColumnBuilder<SetField<M, 'indexType', IndexTypes>> {\n    return new UuidColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { indexType: algorithm })\n    );\n  }\n  unique(): UuidColumnBuilder<SetField<M, 'isUnique', true>> {\n    return new UuidColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isUnique: true })\n    );\n  }\n  primaryKey(): UuidColumnBuilder<SetField<M, 'isPrimaryKey', true>> {\n    return new UuidColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { isPrimaryKey: true })\n    );\n  }\n  default(value: Uuid): UuidColumnBuilder<SetField<M, 'defaultValue', Uuid>> {\n    return new UuidColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { defaultValue: value })\n    );\n  }\n  name<const Name extends string>(\n    name: Name\n  ): UuidColumnBuilder<SetField<M, 'name', Name>> {\n    return new UuidColumnBuilder(\n      this.typeBuilder,\n      set(this.columnMetadata, { name })\n    );\n  }\n}\n\nexport class RefBuilder<Type, SpacetimeType> extends TypeBuilder<\n  Type,\n  AlgebraicTypeVariants.Ref\n> {\n  readonly ref: number;\n  /** The phantom type of the pointee of this ref. */\n  private readonly __spacetimeType!: SpacetimeType;\n  constructor(ref: number) {\n    super(AlgebraicType.Ref(ref));\n    this.ref = ref;\n  }\n}\n\ninterface EnumFn {\n  /**\n   * Creates a simple sum type whose cases are all unit variants.\n   * Each string in the array becomes a case of the enum.\n   *\n   * Example:\n   * ```ts\n   * t.enum(\"Color\", [\"red\", \"green\", \"blue\"]);\n   * ```\n   */\n  <Case extends string>(\n    name: string,\n    cases: readonly [Case, ...Case[]]\n  ): SimpleSumBuilderImpl<Record<Case, UnitBuilder>>;\n\n  /**\n   * Creates an empty simple sum type (no cases, equivalent to `never`).\n   * This can be useful for code generation or placeholder types.\n   * Example:\n   * ```ts\n   * t.enum(\"Never\", []);\n   * ```\n   */\n  (name: string, cases: []): SimpleSumBuilderImpl<Record<never, UnitBuilder>>;\n\n  /**\n   * Creates a full sum type, where each case can have a payload.\n   * Each value in the object must be a {@link TypeBuilder}.\n   *\n   * Example:\n   * ```ts\n   * t.enum(\"Result\", { Ok: t.unit(), Err: t.string() });\n   * ```\n   */\n  <Obj extends VariantsObj>(name: string, obj: Obj): SumBuilder<Obj>;\n}\n\nconst enumImpl = ((nameOrObj: any, maybeObj?: any) => {\n  let obj: any = nameOrObj;\n  let name: string | undefined = undefined;\n\n  if (typeof nameOrObj === 'string') {\n    if (!maybeObj) {\n      throw new TypeError(\n        'When providing a name, you must also provide the variants object or array.'\n      );\n    }\n    obj = maybeObj;\n    name = nameOrObj;\n  }\n\n  // Simple sum (array form)\n  if (Array.isArray(obj)) {\n    const simpleVariantsObj: Record<string, UnitBuilder> = {};\n    for (const variant of obj) {\n      simpleVariantsObj[variant] = new UnitBuilder();\n    }\n    return new SimpleSumBuilderImpl(simpleVariantsObj, name);\n  }\n\n  // Regular sum (object form)\n  return new SumBuilder(obj, name);\n}) as EnumFn;\n\n/**\n * A collection of factory functions for creating various SpacetimeDB algebraic types\n * to be used in table definitions. Each function returns a corresponding builder\n * for a specific type, such as `BoolBuilder`, `StringBuilder`, or `F64Builder`.\n *\n * These builders are used to define the schema of tables in SpacetimeDB, and each\n * builder implements the {@link TypeBuilder} interface, allowing for type-safe\n * schema construction in TypeScript.\n *\n * @remarks\n * - Primitive types (e.g., `bool`, `string`, `number`) map to their respective TypeScript types.\n * - Integer and floating-point types (e.g., `i8`, `u64`, `f32`) are represented as `number` or `bigint` in TypeScript.\n * - Complex types such as `object`, `array`, and `enum` allow for nested and structured schemas.\n * - The `scheduleAt` builder is a special column type for scheduling.\n *\n * @see {@link TypeBuilder}\n */\nexport const t = {\n  /**\n   * Creates a new `Bool` {@link AlgebraicType} to be used in table definitions\n   * Represented as `boolean` in TypeScript.\n   * @returns A new {@link BoolBuilder} instance\n   */\n  bool: (): BoolBuilder => new BoolBuilder(),\n\n  /**\n   * Creates a new `String` {@link AlgebraicType} to be used in table definitions\n   * Represented as `string` in TypeScript.\n   * @returns A new {@link StringBuilder} instance\n   */\n  string: (): StringBuilder => new StringBuilder(),\n\n  /**\n   * Creates a new `F64` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link F64Builder} instance\n   */\n  number: (): F64Builder => new F64Builder(),\n\n  /**\n   * Creates a new `I8` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link I8Builder} instance\n   */\n  i8: (): I8Builder => new I8Builder(),\n\n  /**\n   * Creates a new `U8` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link U8Builder} instance\n   */\n  u8: (): U8Builder => new U8Builder(),\n\n  /**\n   * Creates a new `I16` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link I16Builder} instance\n   */\n  i16: (): I16Builder => new I16Builder(),\n\n  /**\n   * Creates a new `U16` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link U16Builder} instance\n   */\n  u16: (): U16Builder => new U16Builder(),\n\n  /**\n   * Creates a new `I32` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link I32Builder} instance\n   */\n  i32: (): I32Builder => new I32Builder(),\n\n  /**\n   * Creates a new `U32` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link U32Builder} instance\n   */\n  u32: (): U32Builder => new U32Builder(),\n\n  /**\n   * Creates a new `I64` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link I64Builder} instance\n   */\n  i64: (): I64Builder => new I64Builder(),\n\n  /**\n   * Creates a new `U64` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link U64Builder} instance\n   */\n  u64: (): U64Builder => new U64Builder(),\n\n  /**\n   * Creates a new `I128` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link I128Builder} instance\n   */\n  i128: (): I128Builder => new I128Builder(),\n\n  /**\n   * Creates a new `U128` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link U128Builder} instance\n   */\n  u128: (): U128Builder => new U128Builder(),\n\n  /**\n   * Creates a new `I256` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link I256Builder} instance\n   */\n  i256: (): I256Builder => new I256Builder(),\n\n  /**\n   * Creates a new `U256` {@link AlgebraicType} to be used in table definitions\n   * Represented as `bigint` in TypeScript.\n   * @returns A new {@link U256Builder} instance\n   */\n  u256: (): U256Builder => new U256Builder(),\n\n  /**\n   * Creates a new `F32` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link F32Builder} instance\n   */\n  f32: (): F32Builder => new F32Builder(),\n\n  /**\n   * Creates a new `F64` {@link AlgebraicType} to be used in table definitions\n   * Represented as `number` in TypeScript.\n   * @returns A new {@link F64Builder} instance\n   */\n  f64: (): F64Builder => new F64Builder(),\n\n  /**\n   * Creates a new `Product` {@link AlgebraicType} to be used in table definitions. Product types in SpacetimeDB\n   * are essentially the same as objects in JavaScript/TypeScript.\n   * Properties of the object must also be {@link TypeBuilder}s.\n   * Represented as an object with specific properties in TypeScript.\n   *\n   * @param name (optional) A display name for the product type. If omitted, an anonymous product type is created.\n   * @param obj The object defining the properties of the type, whose property\n   * values must be {@link TypeBuilder}s.\n   * @returns A new {@link ProductBuilder} instance.\n   */\n  object: ((nameOrObj: any, maybeObj?: any) => {\n    if (typeof nameOrObj === 'string') {\n      if (!maybeObj) {\n        throw new TypeError(\n          'When providing a name, you must also provide the object.'\n        );\n      }\n      return new ProductBuilder(maybeObj, nameOrObj);\n    }\n    return new ProductBuilder(nameOrObj, undefined);\n  }) as {\n    <Obj extends ElementsObj>(name: string, obj: Obj): ProductBuilder<Obj>;\n    // TODO: Currently names are not optional\n    // <Obj extends ElementsObj>(obj: Obj): ProductBuilder<Obj>;\n  },\n\n  /**\n   * Creates a new `Row` {@link AlgebraicType} to be used in table definitions. Row types in SpacetimeDB\n   * are similar to `Product` types, but are specifically used to define the schema of a table row.\n   * Properties of the object must also be {@link TypeBuilder} or {@link ColumnBuilder}s.\n   *\n   * You can represent a `Row` as either a {@link RowObj} or an {@link RowBuilder} type when\n   * defining a table schema.\n   *\n   * The {@link RowBuilder} type is useful when you want to create a type which can be used anywhere\n   * a {@link TypeBuilder} is accepted, such as in nested objects or arrays, or as the argument\n   * to a scheduled function.\n   *\n   * @param obj The object defining the properties of the row, whose property\n   * values must be {@link TypeBuilder}s or {@link ColumnBuilder}s.\n   * @returns A new {@link RowBuilder} instance\n   */\n  row: (<Obj extends RowObj>(\n    nameOrObj: string | Obj,\n    maybeObj?: Obj\n  ): RowBuilder<Obj> => {\n    const [obj, name] =\n      typeof nameOrObj === 'string'\n        ? [maybeObj!, nameOrObj]\n        : [nameOrObj, undefined];\n    return new RowBuilder(obj, name);\n  }) as {\n    <Obj extends RowObj>(obj: Obj): RowBuilder<Obj>;\n    <Obj extends RowObj>(name: string, obj: Obj): RowBuilder<Obj>;\n  },\n\n  /**\n   * Creates a new `Array` {@link AlgebraicType} to be used in table definitions.\n   * Represented as an array in TypeScript.\n   * @param element The element type of the array, which must be a `TypeBuilder`.\n   * @returns A new {@link ArrayBuilder} instance\n   */\n  array<Element extends TypeBuilder<any, any>>(\n    e: Element\n  ): ArrayBuilder<Element> {\n    return new ArrayBuilder(e);\n  },\n\n  enum: enumImpl,\n\n  /**\n   * This is a special helper function for conveniently creating `Product` type columns with no fields.\n   *\n   * @returns A new {@link ProductBuilder} instance with no fields.\n   */\n  unit(): UnitBuilder {\n    return new UnitBuilder();\n  },\n\n  /**\n   * Creates a lazily-evaluated {@link TypeBuilder}. This is useful for creating\n   * recursive types, such as a tree or linked list.\n   * @param thunk A function that returns a {@link TypeBuilder}.\n   * @returns A proxy {@link TypeBuilder} that evaluates the thunk on first access.\n   */\n  lazy<Build extends () => TypeBuilder<any, any>>(\n    thunk: Build\n  ): ReturnType<Build> {\n    type B = ReturnType<Build>;\n    let cached: B | null = null;\n    const get = (): B => (cached ??= thunk() as B);\n\n    const proxy = new Proxy({} as unknown as B, {\n      get(_t, prop, recv) {\n        const target = get() as any;\n        const val = Reflect.get(target, prop, recv);\n        return typeof val === 'function' ? val.bind(target) : val;\n      },\n      set(_t, prop, value, recv) {\n        return Reflect.set(get() as any, prop, value, recv);\n      },\n      has(_t, prop) {\n        return prop in (get() as any);\n      },\n      ownKeys() {\n        return Reflect.ownKeys(get() as any);\n      },\n      getOwnPropertyDescriptor(_t, prop) {\n        return Object.getOwnPropertyDescriptor(get() as any, prop);\n      },\n      getPrototypeOf() {\n        // makes `instanceof TypeBuilder` work if you care about it\n        return Object.getPrototypeOf(get() as any);\n      },\n    }) as B;\n\n    return proxy;\n  },\n\n  /**\n   * This is a special helper function for conveniently creating {@link ScheduleAt} type columns.\n   * @returns A new ColumnBuilder instance with the {@link ScheduleAt} type.\n   */\n  scheduleAt: (): ScheduleAtBuilder => {\n    return new ScheduleAtBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link Option} type.\n   * You can create a column of the same type by constructing an enum with a `some` and `none` variant.\n   * @param value The type of the value contained in the `some` variant of the `Option`.\n   * @returns A new {@link OptionBuilder} instance with the {@link Option} type.\n   */\n  option<Value extends TypeBuilder<any, any>>(\n    value: Value\n  ): OptionBuilder<Value> {\n    return new OptionBuilder(value);\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link Result} type.\n   * You can create a column of the same type by constructing an enum with an `ok` and `err` variant.\n   * @param ok The type of the value contained in the `ok` variant of the `Result`.\n   * @param err The type of the value contained in the `err` variant of the `Result`.\n   * @returns A new {@link ResultBuilder} instance with the {@link Result} type.\n   */\n  result<Ok extends TypeBuilder<any, any>, Err extends TypeBuilder<any, any>>(\n    ok: Ok,\n    err: Err\n  ): ResultBuilder<Ok, Err> {\n    return new ResultBuilder(ok, err);\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link Identity} type.\n   * You can create a column of the same type by constructing an `object` with a single `__identity__` element.\n   * @returns A new {@link TypeBuilder} instance with the {@link Identity} type.\n   */\n  identity: (): IdentityBuilder => {\n    return new IdentityBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link ConnectionId} type.\n   * You can create a column of the same type by constructing an `object` with a single `__connection_id__` element.\n   * @returns A new {@link TypeBuilder} instance with the {@link ConnectionId} type.\n   */\n  connectionId: (): ConnectionIdBuilder => {\n    return new ConnectionIdBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link Timestamp} type.\n   * You can create a column of the same type by constructing an `object` with a single `__timestamp_micros_since_unix_epoch__` element.\n   * @returns A new {@link TypeBuilder} instance with the {@link Timestamp} type.\n   */\n  timestamp: (): TimestampBuilder => {\n    return new TimestampBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link TimeDuration} type.\n   * You can create a column of the same type by constructing an `object` with a single `__time_duration_micros__` element.\n   * @returns A new {@link TypeBuilder} instance with the {@link TimeDuration} type.\n   */\n  timeDuration: (): TimeDurationBuilder => {\n    return new TimeDurationBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the {@link Uuid} type.\n   * You can create a column of the same type by constructing an `object` with a single `__uuid__` element.\n   * @returns A new {@link TypeBuilder} instance with the {@link Uuid} type.\n   */\n  uuid: (): UuidBuilder => {\n    return new UuidBuilder();\n  },\n\n  /**\n   * This is a convenience method for creating a column with the `ByteArray` type.\n   * You can create a column of the same type by constructing an `array` of `u8`.\n   * The TypeScript representation is {@link Uint8Array}.\n   * @returns A new {@link ByteArrayBuilder} instance with the `ByteArray` type.\n   */\n  byteArray: (): ByteArrayBuilder => {\n    return new ByteArrayBuilder();\n  },\n} as const;\nexport default t;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/type_util.ts",
    "content": "import type { ConnectionId } from './connection_id';\nimport type { Identity } from './identity';\nimport type { ScheduleAt } from './schedule_at';\nimport type { TimeDuration } from './time_duration';\nimport type { Timestamp } from './timestamp';\n\ntype DoNotPrettify =\n  | Identity\n  | ConnectionId\n  | Timestamp\n  | TimeDuration\n  | ScheduleAt;\n\n/**\n * Utility to make TS show cleaner types by flattening intersections.\n */\nexport type Prettify<T> = T extends DoNotPrettify\n  ? T\n  : { [K in keyof T]: T[K] } & {};\n\n/**\n * Helper function to sets a field in an object\n */\nexport type SetField<T, F extends string, V> = Prettify<\n  Omit<T, F> & { [K in F]: V }\n>;\n\n/**\n * Sets a field in an object\n * @param x The original object\n * @param t The object containing the field to set\n * @returns A new object with the field set\n */\nexport function set<T, F extends string, V>(\n  x: T,\n  t: { [k in F]: V }\n): SetField<T, F, V> {\n  return { ...x, ...t } as SetField<T, F, V>;\n}\n\n/**\n * Helper to extract the value types from an object type\n */\nexport type Values<T> = T[keyof T];\n\n/**\n * A helper type to collapse a tuple into a single type if it has only one element.\n */\nexport type CollapseTuple<A extends any[]> = A extends [infer T] ? T : A;\n\ntype CamelCaseImpl<S extends string> = S extends `${infer Head}_${infer Tail}`\n  ? `${Head}${Capitalize<CamelCaseImpl<Tail>>}`\n  : S extends `${infer Head}-${infer Tail}`\n    ? `${Head}${Capitalize<CamelCaseImpl<Tail>>}`\n    : S;\n\n/**\n * Convert \"Some_identifier-name\" -> \"someIdentifierName\"\n * - No spaces; allowed separators: \"_\" and \"-\"\n * - Normalizes the *first* character to lowercase (e.g. \"User_Name\" -> \"userName\")\n */\nexport type CamelCase<S extends string> = Uncapitalize<CamelCaseImpl<S>>;\n\n/** Type safe conversion from \"some_identifier-name\" to \"some_identifier_name\"\n * - No spaces; allowed separators: \"_\" and \"-\"\n * - Normalizes the *first* character to lowercase (e.g. \"User_Name\" -> \"user_name\")\n */\nexport type SnakeCase<S extends string> = S extends `${infer Head}${infer Tail}`\n  ? Tail extends Uncapitalize<Tail>\n    ? `${Lowercase<Head>}${SnakeCase<Tail>}`\n    : `${Lowercase<Head>}_${SnakeCase<Tail>}`\n  : Lowercase<S>;\n\ntype PascalCaseImpl<S extends string> = S extends `${infer Head}_${infer Tail}`\n  ? `${Capitalize<Head>}${PascalCaseImpl<Tail>}`\n  : S extends `${infer Head}-${infer Tail}`\n    ? `${Capitalize<Head>}${PascalCaseImpl<Tail>}`\n    : Capitalize<S>;\n\n/**\n * Convert \"some_identifier-name\" -> \"SomeIdentifierName\"\n * - No spaces; allowed separators: \"_\" and \"-\"\n * - Normalizes the *first* character to uppercase (e.g. \"user_name\" -> \"UserName\")\n */\nexport type PascalCase<S extends string> = PascalCaseImpl<S>;\n\n/**\n * Check if a metadata type has fields that are incompatible with default values.\n * Default values cannot be combined with isPrimaryKey, isUnique, or isAutoIncrement.\n */\nexport type HasDefaultIncompatibleFields<M> = M extends {\n  isPrimaryKey: true;\n}\n  ? true\n  : M extends { isUnique: true }\n    ? true\n    : M extends { isAutoIncrement: true }\n      ? true\n      : false;\n\n/**\n * Check if a metadata type has a default value set.\n */\nexport type HasDefaultValue<M> = M extends { defaultValue: any } ? true : false;\n\n/**\n * Validate that a column's metadata doesn't have invalid combinations.\n * Returns the metadata type if valid, or an error type if invalid.\n */\nexport type ValidateColumnMetadata<M> =\n  HasDefaultValue<M> extends true\n    ? HasDefaultIncompatibleFields<M> extends true\n      ? InvalidColumnMetadata<'default() cannot be combined with primaryKey(), unique(), or autoInc()'>\n      : M\n    : M;\n\n/**\n * Error type for invalid column metadata combinations.\n * This type is designed to cause a compile-time error with a descriptive message.\n */\nexport type InvalidColumnMetadata<Message extends string> = {\n  __error: Message;\n  __brand: 'InvalidColumnMetadata';\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/util.ts",
    "content": "import type { AlgebraicType } from './algebraic_type';\nimport type { Typespace } from './autogen/types';\nimport BinaryReader from './binary_reader';\nimport BinaryWriter from './binary_writer';\nimport type { ParamsObj } from './reducers';\nimport type { ColumnBuilder, TypeBuilder } from './type_builders';\nimport type { CamelCase, SnakeCase } from './type_util';\n\nexport function deepEqual(obj1: any, obj2: any): boolean {\n  // If both are strictly equal (covers primitives and reference equality), return true\n  if (obj1 === obj2) return true;\n\n  // If either is a primitive type or one is null, return false since we already checked for strict equality\n  if (\n    typeof obj1 !== 'object' ||\n    obj1 === null ||\n    typeof obj2 !== 'object' ||\n    obj2 === null\n  ) {\n    return false;\n  }\n\n  // Get keys of both objects\n  const keys1 = Object.keys(obj1);\n  const keys2 = Object.keys(obj2);\n\n  // If number of keys is different, return false\n  if (keys1.length !== keys2.length) return false;\n\n  // Check all keys and compare values recursively\n  for (const key of keys1) {\n    if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nexport function uint8ArrayToHexString(array: Uint8Array): string {\n  return Array.prototype.map\n    .call(array.reverse(), x => ('00' + x.toString(16)).slice(-2))\n    .join('');\n}\n\nexport function uint8ArrayToU128(array: Uint8Array): bigint {\n  if (array.length != 16) {\n    throw new Error(`Uint8Array is not 16 bytes long: ${array}`);\n  }\n  return new BinaryReader(array).readU128();\n}\n\nexport function uint8ArrayToU256(array: Uint8Array): bigint {\n  if (array.length != 32) {\n    throw new Error(`Uint8Array is not 32 bytes long: [${array}]`);\n  }\n  return new BinaryReader(array).readU256();\n}\n\nexport function hexStringToUint8Array(str: string): Uint8Array {\n  if (str.startsWith('0x')) {\n    str = str.slice(2);\n  }\n  const matches = str.match(/.{1,2}/g) || [];\n  const data = Uint8Array.from(\n    matches.map((byte: string) => parseInt(byte, 16))\n  );\n  return data.reverse();\n}\n\nexport function hexStringToU128(str: string): bigint {\n  return uint8ArrayToU128(hexStringToUint8Array(str));\n}\n\nexport function hexStringToU256(str: string): bigint {\n  return uint8ArrayToU256(hexStringToUint8Array(str));\n}\n\nexport function u128ToUint8Array(data: bigint): Uint8Array {\n  const writer = new BinaryWriter(16);\n  writer.writeU128(data);\n  return writer.getBuffer();\n}\n\nexport function u128ToHexString(data: bigint): string {\n  return uint8ArrayToHexString(u128ToUint8Array(data));\n}\n\nexport function u256ToUint8Array(data: bigint): Uint8Array {\n  const writer = new BinaryWriter(32);\n  writer.writeU256(data);\n  return writer.getBuffer();\n}\n\nexport function u256ToHexString(data: bigint): string {\n  return uint8ArrayToHexString(u256ToUint8Array(data));\n}\n\n/**\n * Converts a string to PascalCase (UpperCamelCase).\n * @param str The string to convert\n * @returns The converted string\n */\nexport function toPascalCase(s: string): string {\n  const str = toCamelCase(s);\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Type safe conversion from a string like \"some_identifier-name\" to \"someIdentifierName\".\n * @param str The string to convert\n * @returns The converted string\n */\nexport function toCamelCase<T extends string>(s: T): CamelCase<T> {\n  const str = s\n    .replace(/[-_]+/g, '_') // collapse runs to a single separator (no backtracking issue)\n    .replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase());\n  return (str.charAt(0).toLowerCase() + str.slice(1)) as CamelCase<T>;\n}\n\n/** Type safe conversion from a string like \"some_Identifier-name\" to \"some_identifier_name\".\n * @param str The string to convert\n * @returns The converted string\n */\nexport function toSnakeCase<T extends string>(str: T): SnakeCase<T> {\n  return str\n    .replace(/([A-Z])/g, '_$1') // insert underscores before capitals\n    .replace(/[-\\s]+/g, '_') // replace spaces and dashes with underscores\n    .toLowerCase() as SnakeCase<T>;\n}\n\nexport function bsatnBaseSize(typespace: Typespace, ty: AlgebraicType): number {\n  const assumedArrayLength = 4;\n  while (ty.tag === 'Ref') ty = typespace.types[ty.value];\n  if (ty.tag === 'Product') {\n    let sum = 0;\n    for (const { algebraicType: elem } of ty.value.elements) {\n      sum += bsatnBaseSize(typespace, elem);\n    }\n    return sum;\n  } else if (ty.tag === 'Sum') {\n    let min = Infinity;\n    for (const { algebraicType: vari } of ty.value.variants) {\n      const vSize = bsatnBaseSize(typespace, vari);\n      if (vSize < min) min = vSize;\n    }\n    if (min === Infinity) min = 0;\n    return 4 + min;\n  } else if (ty.tag == 'Array') {\n    return 4 + assumedArrayLength * bsatnBaseSize(typespace, ty.value);\n  }\n  return {\n    String: 4 + assumedArrayLength,\n    Sum: 1,\n    Bool: 1,\n    I8: 1,\n    U8: 1,\n    I16: 2,\n    U16: 2,\n    I32: 4,\n    U32: 4,\n    F32: 4,\n    I64: 8,\n    U64: 8,\n    F64: 8,\n    I128: 16,\n    U128: 16,\n    I256: 32,\n    U256: 32,\n  }[ty.tag];\n}\n\nexport type CoerceTypeBuilder<\n  Col extends TypeBuilder<any, any> | ColumnBuilder<any, any, any>,\n> = Col extends ColumnBuilder<any, any> ? Col['typeBuilder'] : Col;\n\nexport type CoerceParams<Params extends ParamsObj> = {\n  [k in keyof Params & string]: CoerceTypeBuilder<Params[k]>;\n};\n\nexport function coerceParams<Params extends ParamsObj>(\n  params: Params\n): CoerceParams<Params> {\n  return Object.fromEntries(\n    Object.entries(params).map(([n, c]) => [\n      n,\n      'typeBuilder' in c ? c.typeBuilder : c,\n    ])\n  ) as CoerceParams<Params>;\n}\n\nexport const hasOwn: <K extends PropertyKey>(\n  o: object,\n  k: K\n) => o is K extends PropertyKey ? { [k in K]: unknown } : never =\n  Object.hasOwn as any;\n"
  },
  {
    "path": "crates/bindings-typescript/src/lib/uuid.ts",
    "content": "import { Timestamp } from './timestamp';\nimport { AlgebraicType } from './algebraic_type.ts';\n\nexport type UuidAlgebraicType = {\n  tag: 'Product';\n  value: {\n    elements: [\n      {\n        name: '__uuid__';\n        algebraicType: { tag: 'U128' };\n      },\n    ];\n  };\n};\n\n/**\n * Supported UUID versions.\n *\n * - `Nil` – The \"Nil\" UUID (all zeros)\n * - `V4`  – Version 4: random\n * - `V7`  – Version 7: timestamp + counter + random\n * - `Max` – The \"Max\" UUID (all ones)\n */\ntype UuidVersion = 'Nil' | 'V4' | 'V7' | 'Max';\n\n/**\n * A universally unique identifier (UUID).\n *\n * Supports UUID `Nil`, `Max`, `V4` (random), and `V7`\n * (timestamp + counter + random).\n *\n * Internally represented as an unsigned 128-bit between 0 and `MAX_UUID_BIGINT`.\n */\nexport class Uuid {\n  __uuid__: bigint;\n\n  /**\n   * The nil UUID (all zeros).\n   *\n   * @example\n   * ```ts\n   * const uuid = Uuid.NIL;\n   * console.assert(\n   *   uuid.toString() === \"00000000-0000-0000-0000-000000000000\"\n   * );\n   * ```\n   */\n  static readonly NIL = new Uuid(0n);\n  static readonly MAX_UUID_BIGINT = 0xffffffffffffffffffffffffffffffffn;\n  /**\n   * The max UUID (all ones).\n   *\n   * @example\n   * ```ts\n   * const uuid = Uuid.MAX;\n   * console.assert(\n   *   uuid.toString() === \"ffffffff-ffff-ffff-ffff-ffffffffffff\"\n   * );\n   * ```\n   */\n  static readonly MAX = new Uuid(Uuid.MAX_UUID_BIGINT);\n\n  /**\n   * Create a UUID from a raw 128-bit value.\n   *\n   * @param u - Unsigned 128-bit integer\n   * @throws {Error} If the value is outside the valid UUID range\n   */\n  constructor(u: bigint) {\n    // Must fit in exactly 16 bytes\n    if (u < 0n || u > Uuid.MAX_UUID_BIGINT) {\n      throw new Error('Invalid UUID: must be between 0 and `MAX_UUID_BIGINT`');\n    }\n    this.__uuid__ = u;\n  }\n\n  /**\n   * Create a UUID `v4` from explicit random bytes.\n   *\n   * This method assumes the bytes are already sufficiently random.\n   * It only sets the appropriate bits for the UUID version and variant.\n   *\n   * @param bytes - Exactly 16 random bytes\n   * @returns A UUID `v4`\n   * @throws {Error} If `bytes.length !== 16`\n   *\n   * @example\n   * ```ts\n   * const randomBytes = new Uint8Array(16);\n   * const uuid = Uuid.fromRandomBytesV4(randomBytes);\n   *\n   * console.assert(\n   *   uuid.toString() === \"00000000-0000-4000-8000-000000000000\"\n   * );\n   * ```\n   */\n  static fromRandomBytesV4(bytes: Uint8Array): Uuid {\n    if (bytes.length !== 16) throw new Error('UUID v4 requires 16 bytes');\n    const arr = new Uint8Array(bytes);\n    arr[6] = (arr[6] & 0x0f) | 0x40; // version 4\n    arr[8] = (arr[8] & 0x3f) | 0x80; // variant\n    return new Uuid(Uuid.bytesToBigInt(arr));\n  }\n\n  /**\n   * Generate a UUID `v7` using a monotonic counter from `0` to `2^31 - 1`,\n   * a timestamp, and 4 random bytes.\n   *\n   * The counter wraps around on overflow.\n   *\n   * The UUID `v7` is structured as follows:\n   *\n   * ```ascii\n   * ┌───────────────────────────────────────────────┬───────────────────┐\n   * | B0  | B1  | B2  | B3  | B4  | B5              |         B6        |\n   * ├───────────────────────────────────────────────┼───────────────────┤\n   * |                 unix_ts_ms                    |      version 7    |\n   * └───────────────────────────────────────────────┴───────────────────┘\n   * ┌──────────────┬─────────┬──────────────────┬───────────────────────┐\n   * | B7           | B8      | B9  | B10 | B11  | B12 | B13 | B14 | B15 |\n   * ├──────────────┼─────────┼──────────────────┼───────────────────────┤\n   * | counter_high | variant |    counter_low   |        random         |\n   * └──────────────┴─────────┴──────────────────┴───────────────────────┘\n   * ```\n   *\n   * @param counter - Mutable monotonic counter (31-bit)\n   * @param now - Timestamp since the Unix epoch\n   * @param randomBytes - Exactly 4 random bytes\n   * @returns A UUID `v7`\n   *\n   * @throws {Error} If the `counter` is negative\n   * @throws {Error} If the `timestamp` is before the Unix epoch\n   * @throws {Error} If `randomBytes.length !== 4`\n   *\n   * @example\n   * ```ts\n   * const now = Timestamp.fromMillis(1_686_000_000_000n);\n   * const counter = { value: 1 };\n   * const randomBytes = new Uint8Array(4);\n   *\n   * const uuid = Uuid.fromCounterV7(counter, now, randomBytes);\n   *\n   * console.assert(\n   *   uuid.toString() === \"0000647e-5180-7000-8000-000200000000\"\n   * );\n   * ```\n   */\n  static fromCounterV7(\n    counter: { value: number },\n    now: Timestamp,\n    randomBytes: Uint8Array\n  ): Uuid {\n    if (randomBytes.length !== 4) {\n      throw new Error('`fromCounterV7` requires `randomBytes.length == 4`');\n    }\n\n    if (counter.value < 0) {\n      throw new Error('`fromCounterV7` uuid `counter` must be non-negative');\n    }\n\n    if (now.__timestamp_micros_since_unix_epoch__ < 0) {\n      throw new Error('`fromCounterV7` `timestamp` before unix epoch');\n    }\n\n    // 31-bit monotonic counter with wraparound\n    const counterVal = counter.value;\n    counter.value = (counterVal + 1) & 0x7fff_ffff;\n\n    // 48-bit unix timestamp (ms)\n    const tsMs = now.toMillis() & 0xffff_ffff_ffffn;\n\n    const bytes = new Uint8Array(16);\n\n    // unix_ts_ms (48 bits)\n    bytes[0] = Number((tsMs >> 40n) & 0xffn);\n    bytes[1] = Number((tsMs >> 32n) & 0xffn);\n    bytes[2] = Number((tsMs >> 24n) & 0xffn);\n    bytes[3] = Number((tsMs >> 16n) & 0xffn);\n    bytes[4] = Number((tsMs >> 8n) & 0xffn);\n    bytes[5] = Number(tsMs & 0xffn);\n\n    // Counter bits (31 bits total)\n    bytes[7] = (counterVal >>> 23) & 0xff;\n    bytes[9] = (counterVal >>> 15) & 0xff;\n    bytes[10] = (counterVal >>> 7) & 0xff;\n    bytes[11] = ((counterVal & 0x7f) << 1) & 0xff;\n\n    // Random bytes\n    bytes[12] |= randomBytes[0] & 0x7f;\n    bytes[13] = randomBytes[1];\n    bytes[14] = randomBytes[2];\n    bytes[15] = randomBytes[3];\n\n    // Version 7\n    bytes[6] = (bytes[6] & 0x0f) | 0x70;\n\n    // Variant RFC4122\n    bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n    return new Uuid(Uuid.bytesToBigInt(bytes));\n  }\n\n  /**\n   * Parse a UUID from a string representation.\n   *\n   * @param s - UUID string\n   * @returns Parsed UUID\n   * @throws {Error} If the string is not a valid UUID\n   *\n   * @example\n   * ```ts\n   * const s = \"01888d6e-5c00-7000-8000-000000000000\";\n   * const uuid = Uuid.parse(s);\n   *\n   * console.assert(uuid.toString() === s);\n   * ```\n   */\n  static parse(s: string): Uuid {\n    const hex = s.replace(/-/g, '');\n    if (hex.length !== 32) throw new Error('Invalid hex UUID');\n\n    let v = 0n;\n    for (let i = 0; i < 32; i += 2) {\n      v = (v << 8n) | BigInt(parseInt(hex.slice(i, i + 2), 16));\n    }\n    return new Uuid(v);\n  }\n\n  /** Convert to string (hyphenated form). */\n  toString(): string {\n    const bytes = Uuid.bigIntToBytes(this.__uuid__);\n    const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');\n\n    // Format as 8-4-4-4-12\n    return (\n      hex.slice(0, 8) +\n      '-' +\n      hex.slice(8, 12) +\n      '-' +\n      hex.slice(12, 16) +\n      '-' +\n      hex.slice(16, 20) +\n      '-' +\n      hex.slice(20)\n    );\n  }\n\n  /** Convert to bigint (u128). */\n  asBigInt(): bigint {\n    return this.__uuid__;\n  }\n\n  /** Return a `Uint8Array` of 16 bytes. */\n  toBytes(): Uint8Array {\n    return Uuid.bigIntToBytes(this.__uuid__);\n  }\n\n  private static bytesToBigInt(bytes: Uint8Array): bigint {\n    let result = 0n;\n    for (const b of bytes) result = (result << 8n) | BigInt(b);\n    return result;\n  }\n\n  private static bigIntToBytes(value: bigint): Uint8Array {\n    const bytes = new Uint8Array(16);\n    for (let i = 15; i >= 0; i--) {\n      bytes[i] = Number(value & 0xffn);\n      value >>= 8n;\n    }\n    return bytes;\n  }\n\n  /**\n   * Returns the version of this UUID.\n   *\n   * This represents the algorithm used to generate the value.\n   *\n   * @returns A `UuidVersion`\n   * @throws {Error} If the version field is not recognized\n   */\n  getVersion(): UuidVersion {\n    const version = (this.toBytes()[6] >> 4) & 0x0f;\n\n    switch (version) {\n      case 4:\n        return 'V4';\n      case 7:\n        return 'V7';\n      default:\n        if (this == Uuid.NIL) {\n          return 'Nil';\n        }\n        if (this == Uuid.MAX) {\n          return 'Max';\n        }\n        throw new Error(`Unsupported UUID version: ${version}`);\n    }\n  }\n\n  /**\n   * Extract the monotonic counter from a UUIDv7.\n   *\n   * Intended for testing and diagnostics.\n   * Behavior is undefined if called on a non-V7 UUID.\n   *\n   * @returns 31-bit counter value\n   */\n  getCounter(): number {\n    const bytes = this.toBytes(); // big-endian, 16 bytes\n\n    const high = bytes[7]; // bits 30..23\n    const mid1 = bytes[9]; // bits 22..15\n    const mid2 = bytes[10]; // bits 14..7\n    const low = bytes[11] >>> 1; // bits 6..0\n\n    // reconstruct 31-bit counter\n    return (high << 23) | (mid1 << 15) | (mid2 << 7) | low | 0; // force 32-bit int\n  }\n\n  compareTo(other: Uuid): number {\n    if (this.__uuid__ < other.__uuid__) return -1;\n    if (this.__uuid__ > other.__uuid__) return 1;\n\n    return 0;\n  }\n\n  static getAlgebraicType(): UuidAlgebraicType {\n    return AlgebraicType.Product({\n      elements: [\n        {\n          name: '__uuid__',\n          algebraicType: AlgebraicType.U128,\n        },\n      ],\n    });\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/SpacetimeDBProvider.ts",
    "content": "import {\n  DbConnectionBuilder,\n  type DbConnectionImpl,\n} from '../sdk/db_connection_impl';\nimport * as React from 'react';\nimport { SpacetimeDBContext } from './useSpacetimeDB';\nimport type { ConnectionState } from './connection_state';\nimport { ConnectionId } from '../lib/connection_id';\nimport {\n  ConnectionManager,\n  type ConnectionState as ManagerConnectionState,\n} from '../sdk/connection_manager';\n\nexport interface SpacetimeDBProviderProps<\n  DbConnection extends DbConnectionImpl<any>,\n> {\n  connectionBuilder: DbConnectionBuilder<DbConnection>;\n  children?: React.ReactNode;\n}\n\nexport function SpacetimeDBProvider<\n  DbConnection extends DbConnectionImpl<any>,\n>({\n  connectionBuilder,\n  children,\n}: SpacetimeDBProviderProps<DbConnection>): React.JSX.Element {\n  const uri = connectionBuilder.getUri();\n  const moduleName = connectionBuilder.getModuleName();\n  const key = React.useMemo(\n    () => ConnectionManager.getKey(uri, moduleName),\n    [uri, moduleName]\n  );\n\n  const fallbackStateRef = React.useRef<ManagerConnectionState>({\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n  });\n\n  const subscribe = React.useCallback(\n    (onStoreChange: () => void) =>\n      ConnectionManager.subscribe(key, onStoreChange),\n    [key]\n  );\n  const getSnapshot = React.useCallback(\n    () => ConnectionManager.getSnapshot(key) ?? fallbackStateRef.current,\n    [key]\n  );\n  const getServerSnapshot = React.useCallback(\n    () => fallbackStateRef.current,\n    []\n  );\n\n  const state = React.useSyncExternalStore(\n    subscribe,\n    getSnapshot,\n    getServerSnapshot\n  );\n\n  const getConnection = React.useCallback(\n    () => ConnectionManager.getConnection<DbConnection>(key),\n    [key]\n  );\n\n  const contextValue = React.useMemo<ConnectionState>(\n    () => ({ ...state, getConnection }),\n    [state, getConnection]\n  );\n\n  React.useEffect(() => {\n    ConnectionManager.retain(key, connectionBuilder);\n    return () => {\n      ConnectionManager.release(key);\n    };\n  }, [key, connectionBuilder]);\n\n  return React.createElement(\n    SpacetimeDBContext.Provider,\n    { value: contextValue },\n    children\n  );\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/connection_state.ts",
    "content": "import type { DbConnectionImpl } from '../sdk/db_connection_impl';\nimport type { ConnectionState as ManagerConnectionState } from '../sdk/connection_manager';\n\nexport type ConnectionState = ManagerConnectionState & {\n  getConnection(): DbConnectionImpl<any> | null;\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/index.ts",
    "content": "export * from './SpacetimeDBProvider.ts';\nexport { useSpacetimeDB } from './useSpacetimeDB.ts';\nexport { useTable } from './useTable.ts';\nexport { useReducer } from './useReducer.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/useReducer.ts",
    "content": "import { useCallback, useEffect, useRef } from 'react';\nimport type { UntypedReducerDef } from '../sdk/reducers';\nimport { useSpacetimeDB } from './useSpacetimeDB';\nimport type { ParamsType } from '../sdk';\n\nexport function useReducer<ReducerDef extends UntypedReducerDef>(\n  reducerDef: ReducerDef\n): (...params: ParamsType<ReducerDef>) => Promise<void> {\n  const { getConnection, isActive } = useSpacetimeDB();\n  const reducerName = reducerDef.accessorName;\n\n  // Holds calls made before the connection exists\n  const queueRef = useRef<\n    {\n      params: ParamsType<ReducerDef>;\n      resolve: () => void;\n      reject: (err: unknown) => void;\n    }[]\n  >([]);\n\n  // Flush when we finally have a connection\n  useEffect(() => {\n    const conn = getConnection();\n    if (!conn) {\n      return;\n    }\n    const fn = (conn.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => Promise<void>;\n    if (queueRef.current.length) {\n      const pending = queueRef.current.splice(0);\n      for (const item of pending) {\n        fn(...item.params).then(item.resolve, item.reject);\n      }\n    }\n  }, [getConnection, reducerName, isActive]);\n\n  return useCallback(\n    (...params: ParamsType<ReducerDef>) => {\n      const conn = getConnection();\n      if (!conn) {\n        return new Promise<void>((resolve, reject) => {\n          queueRef.current.push({ params, resolve, reject });\n        });\n      }\n      const fn = (conn.reducers as any)[reducerName] as (\n        ...p: ParamsType<ReducerDef>\n      ) => Promise<void>;\n      return fn(...params);\n    },\n    [getConnection, reducerName]\n  );\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/useSpacetimeDB.ts",
    "content": "import { createContext, useContext } from 'react';\nimport type { ConnectionState } from './connection_state';\n\nexport const SpacetimeDBContext = createContext<ConnectionState | undefined>(\n  undefined\n);\n\n// Throws an error if used outside of a SpacetimeDBProvider\n// Error is caught by other hooks like useTable so they can provide better error messages\nexport function useSpacetimeDB(): ConnectionState {\n  const context = useContext(SpacetimeDBContext) as ConnectionState | undefined;\n  if (!context) {\n    throw new Error(\n      'useSpacetimeDB must be used within a SpacetimeDBProvider component. Did you forget to add a `SpacetimeDBProvider` to your component tree?'\n    );\n  }\n  return context;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/react/useTable.ts",
    "content": "import {\n  useCallback,\n  useEffect,\n  useRef,\n  useState,\n  useSyncExternalStore,\n} from 'react';\nimport { useSpacetimeDB } from './useSpacetimeDB';\nimport { type EventContextInterface } from '../sdk/db_connection_impl';\nimport type { ConnectionState } from './connection_state';\nimport type { UntypedRemoteModule } from '../sdk/spacetime_module';\nimport type { RowType, UntypedTableDef } from '../lib/table';\nimport type { Prettify } from '../lib/type_util';\nimport {\n  type Query,\n  toSql,\n  type BooleanExpr,\n  evaluateBooleanExpr,\n  getQueryAccessorName,\n  getQueryWhereClause,\n} from '../lib/query';\n\nexport interface UseTableCallbacks<RowType> {\n  onInsert?: (row: RowType) => void;\n  onDelete?: (row: RowType) => void;\n  onUpdate?: (oldRow: RowType, newRow: RowType) => void;\n}\n\ntype MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';\n\nfunction classifyMembership(\n  whereExpr: BooleanExpr<any> | undefined,\n  oldRow: Record<string, any>,\n  newRow: Record<string, any>\n): MembershipChange {\n  if (!whereExpr) return 'stayIn';\n  const oldIn = evaluateBooleanExpr(whereExpr, oldRow);\n  const newIn = evaluateBooleanExpr(whereExpr, newRow);\n  if (oldIn && !newIn) return 'leave';\n  if (!oldIn && newIn) return 'enter';\n  if (oldIn && newIn) return 'stayIn';\n  return 'stayOut';\n}\n\n/**\n * React hook to subscribe to a table in SpacetimeDB and receive live updates.\n *\n * Accepts a query builder expression as the first argument:\n * - `tables.user` — subscribe to all rows\n * - `tables.user.where(r => r.online.eq(true))` — subscribe with a filter\n *\n * @param query - A query builder expression (table reference or filtered query).\n * @param callbacks - Optional callbacks for row insert, delete, and update events.\n * @returns A tuple of [rows, isReady].\n *\n * @example\n * ```tsx\n * const [rows, isReady] = useTable(tables.user);\n * const [onlineUsers, isReady] = useTable(\n *   tables.user.where(r => r.online.eq(true)),\n *   { onInsert: (row) => console.log('New user:', row) }\n * );\n * ```\n */\nexport function useTable<TableDef extends UntypedTableDef>(\n  query: Query<TableDef>,\n  callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>\n): [readonly Prettify<RowType<TableDef>>[], boolean] {\n  type UseTableRowType = RowType<TableDef>;\n  const accessorName = getQueryAccessorName(query);\n  const whereExpr = getQueryWhereClause(query);\n\n  const [subscribeApplied, setSubscribeApplied] = useState(false);\n  let connectionState: ConnectionState | undefined;\n  try {\n    connectionState = useSpacetimeDB();\n  } catch {\n    throw new Error(\n      'Could not find SpacetimeDB client! Did you forget to add a ' +\n        '`SpacetimeDBProvider`? `useTable` must be used in the React component tree ' +\n        'under a `SpacetimeDBProvider` component.'\n    );\n  }\n\n  const querySql = toSql(query);\n\n  const latestTransactionEventId = useRef<string | null>(null);\n  const lastSnapshotRef = useRef<\n    [readonly Prettify<UseTableRowType>[], boolean] | null\n  >(null);\n\n  const computeSnapshot = useCallback((): [\n    readonly Prettify<UseTableRowType>[],\n    boolean,\n  ] => {\n    const connection = connectionState.getConnection();\n    if (!connection) {\n      return [[], false];\n    }\n    const table = connection.db[accessorName];\n    const result: readonly Prettify<UseTableRowType>[] = whereExpr\n      ? (Array.from(table.iter()).filter(row =>\n          evaluateBooleanExpr(whereExpr, row as Record<string, any>)\n        ) as Prettify<UseTableRowType>[])\n      : (Array.from(table.iter()) as Prettify<UseTableRowType>[]);\n    return [result, subscribeApplied];\n    // TODO: investigating refactoring so that this is no longer necessary, as we have had genuine bugs with missed deps.\n    // See https://github.com/clockworklabs/SpacetimeDB/pull/4580.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [connectionState, accessorName, querySql, subscribeApplied]);\n\n  // Invalidate the cached snapshot when computeSnapshot changes (e.g. when\n  // subscribeApplied flips to true) so getSnapshot() recomputes on the next\n  // render instead of returning a stale [rows, false] tuple.\n  useEffect(() => {\n    lastSnapshotRef.current = null;\n  }, [computeSnapshot]);\n\n  useEffect(() => {\n    const connection = connectionState.getConnection()!;\n    if (connectionState.isActive && connection) {\n      const cancel = connection\n        .subscriptionBuilder()\n        .onApplied(() => {\n          setSubscribeApplied(true);\n        })\n        .subscribe(querySql);\n      return () => {\n        cancel.unsubscribe();\n      };\n    }\n  }, [querySql, connectionState.isActive, connectionState]);\n\n  const subscribe = useCallback(\n    (onStoreChange: () => void) => {\n      const onInsert = (\n        ctx: EventContextInterface<UntypedRemoteModule>,\n        row: any\n      ) => {\n        if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) {\n          return;\n        }\n        callbacks?.onInsert?.(row);\n        if (ctx.event.id !== latestTransactionEventId.current) {\n          latestTransactionEventId.current = ctx.event.id;\n          lastSnapshotRef.current = computeSnapshot();\n          onStoreChange();\n        }\n      };\n\n      const onDelete = (\n        ctx: EventContextInterface<UntypedRemoteModule>,\n        row: any\n      ) => {\n        if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) {\n          return;\n        }\n        callbacks?.onDelete?.(row);\n        if (ctx.event.id !== latestTransactionEventId.current) {\n          latestTransactionEventId.current = ctx.event.id;\n          lastSnapshotRef.current = computeSnapshot();\n          onStoreChange();\n        }\n      };\n\n      const onUpdate = (\n        ctx: EventContextInterface<UntypedRemoteModule>,\n        oldRow: any,\n        newRow: any\n      ) => {\n        const change = classifyMembership(whereExpr, oldRow, newRow);\n\n        switch (change) {\n          case 'leave':\n            callbacks?.onDelete?.(oldRow);\n            break;\n          case 'enter':\n            callbacks?.onInsert?.(newRow);\n            break;\n          case 'stayIn':\n            callbacks?.onUpdate?.(oldRow, newRow);\n            break;\n          case 'stayOut':\n            return; // no-op\n        }\n\n        if (ctx.event.id !== latestTransactionEventId.current) {\n          latestTransactionEventId.current = ctx.event.id;\n          lastSnapshotRef.current = computeSnapshot();\n          onStoreChange();\n        }\n      };\n\n      const connection = connectionState.getConnection();\n      if (!connection) {\n        return () => {};\n      }\n\n      const table = connection.db[accessorName];\n      table.onInsert(onInsert);\n      table.onDelete(onDelete);\n      table.onUpdate?.(onUpdate);\n\n      return () => {\n        table.removeOnInsert(onInsert);\n        table.removeOnDelete(onDelete);\n        table.removeOnUpdate?.(onUpdate);\n      };\n    },\n    // TODO: investigating refactoring so that this is no longer necessary, as we have had genuine bugs with missed deps.\n    // See https://github.com/clockworklabs/SpacetimeDB/pull/4580.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [\n      connectionState,\n      accessorName,\n      querySql,\n      computeSnapshot,\n      callbacks?.onDelete,\n      callbacks?.onInsert,\n      callbacks?.onUpdate,\n    ]\n  );\n\n  const getSnapshot = useCallback((): [\n    readonly Prettify<UseTableRowType>[],\n    boolean,\n  ] => {\n    if (!lastSnapshotRef.current) {\n      lastSnapshotRef.current = computeSnapshot();\n    }\n    return lastSnapshotRef.current;\n  }, [computeSnapshot]);\n\n  // SSR fallback can be the same getter\n  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_api/index.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb cli version 2.0.0 (commit 098afaf1a5ed935bce5a32c88620e829506effe7).\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  DbConnectionBuilder as __DbConnectionBuilder,\n  DbConnectionImpl as __DbConnectionImpl,\n  SubscriptionBuilderImpl as __SubscriptionBuilderImpl,\n  TypeBuilder as __TypeBuilder,\n  Uuid as __Uuid,\n  convertToAccessorMap as __convertToAccessorMap,\n  makeQueryBuilder as __makeQueryBuilder,\n  procedureSchema as __procedureSchema,\n  procedures as __procedures,\n  reducerSchema as __reducerSchema,\n  reducers as __reducers,\n  schema as __schema,\n  t as __t,\n  table as __table,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type DbConnectionConfig as __DbConnectionConfig,\n  type ErrorContextInterface as __ErrorContextInterface,\n  type Event as __Event,\n  type EventContextInterface as __EventContextInterface,\n  type Infer as __Infer,\n  type QueryBuilder as __QueryBuilder,\n  type ReducerEventContextInterface as __ReducerEventContextInterface,\n  type RemoteModule as __RemoteModule,\n  type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,\n  type SubscriptionHandleImpl as __SubscriptionHandleImpl,\n} from '../../index';\n\n// Import all reducer arg schemas\n\n// Import all procedure arg schemas\n\n// Import all table schema definitions\n\n/** Type-only namespace exports for generated type groups. */\n\n/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */\nconst tablesSchema = __schema({});\n\n/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */\nconst reducersSchema = __reducers();\n\n/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */\nconst proceduresSchema = __procedures();\n\n/** The remote SpacetimeDB module schema, both runtime and type information. */\nconst REMOTE_MODULE = {\n  versionInfo: {\n    cliVersion: '2.0.0' as const,\n  },\n  tables: tablesSchema.schemaType.tables,\n  reducers: reducersSchema.reducersType.reducers,\n  ...proceduresSchema,\n} satisfies __RemoteModule<\n  typeof tablesSchema.schemaType,\n  typeof reducersSchema.reducersType,\n  typeof proceduresSchema\n>;\n\n/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */\nexport const tables: __QueryBuilder<typeof tablesSchema.schemaType> =\n  __makeQueryBuilder(tablesSchema.schemaType);\n\n/** The reducers available in this remote SpacetimeDB module. */\nexport const reducers = __convertToAccessorMap(\n  reducersSchema.reducersType.reducers\n);\n\n/** The context type returned in callbacks for all possible events. */\nexport type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for reducer events. */\nexport type ReducerEventContext = __ReducerEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for subscription events. */\nexport type SubscriptionEventContext = __SubscriptionEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for error events. */\nexport type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;\n/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */\nexport type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;\n\n/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */\nexport class SubscriptionBuilder extends __SubscriptionBuilderImpl<\n  typeof REMOTE_MODULE\n> {}\n\n/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */\nexport class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}\n\n/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */\nexport class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {\n  /** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */\n  static builder = (): DbConnectionBuilder => {\n    return new DbConnectionBuilder(\n      REMOTE_MODULE,\n      (config: __DbConnectionConfig<typeof REMOTE_MODULE>) =>\n        new DbConnection(config)\n    );\n  };\n\n  /** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */\n  override subscriptionBuilder = (): SubscriptionBuilder => {\n    return new SubscriptionBuilder(this);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_api/types/procedures.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../lib/type_builders';\n\n// Import all procedure arg schemas\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_api/types/reducers.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../lib/type_builders';\n\n// Import all reducer arg schemas\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_api/types.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../lib/type_builders';\n\nexport const BsatnRowList = __t.object('BsatnRowList', {\n  get sizeHint() {\n    return RowSizeHint;\n  },\n  rowsData: __t.byteArray(),\n});\nexport type BsatnRowList = __Infer<typeof BsatnRowList>;\n\nexport const CallProcedure = __t.object('CallProcedure', {\n  requestId: __t.u32(),\n  flags: __t.u8(),\n  procedure: __t.string(),\n  args: __t.byteArray(),\n});\nexport type CallProcedure = __Infer<typeof CallProcedure>;\n\nexport const CallReducer = __t.object('CallReducer', {\n  requestId: __t.u32(),\n  flags: __t.u8(),\n  reducer: __t.string(),\n  args: __t.byteArray(),\n});\nexport type CallReducer = __Infer<typeof CallReducer>;\n\n// The tagged union or sum type for the algebraic type `ClientMessage`.\nexport const ClientMessage = __t.enum('ClientMessage', {\n  get Subscribe() {\n    return Subscribe;\n  },\n  get Unsubscribe() {\n    return Unsubscribe;\n  },\n  get OneOffQuery() {\n    return OneOffQuery;\n  },\n  get CallReducer() {\n    return CallReducer;\n  },\n  get CallProcedure() {\n    return CallProcedure;\n  },\n});\nexport type ClientMessage = __Infer<typeof ClientMessage>;\n\nexport const EventTableRows = __t.object('EventTableRows', {\n  get events() {\n    return BsatnRowList;\n  },\n});\nexport type EventTableRows = __Infer<typeof EventTableRows>;\n\nexport const InitialConnection = __t.object('InitialConnection', {\n  identity: __t.identity(),\n  connectionId: __t.connectionId(),\n  token: __t.string(),\n});\nexport type InitialConnection = __Infer<typeof InitialConnection>;\n\nexport const OneOffQuery = __t.object('OneOffQuery', {\n  requestId: __t.u32(),\n  queryString: __t.string(),\n});\nexport type OneOffQuery = __Infer<typeof OneOffQuery>;\n\nexport const OneOffQueryResult = __t.object('OneOffQueryResult', {\n  requestId: __t.u32(),\n  get result() {\n    return __t.result(QueryRows, __t.string());\n  },\n});\nexport type OneOffQueryResult = __Infer<typeof OneOffQueryResult>;\n\nexport const PersistentTableRows = __t.object('PersistentTableRows', {\n  get inserts() {\n    return BsatnRowList;\n  },\n  get deletes() {\n    return BsatnRowList;\n  },\n});\nexport type PersistentTableRows = __Infer<typeof PersistentTableRows>;\n\nexport const ProcedureResult = __t.object('ProcedureResult', {\n  get status() {\n    return ProcedureStatus;\n  },\n  timestamp: __t.timestamp(),\n  totalHostExecutionDuration: __t.timeDuration(),\n  requestId: __t.u32(),\n});\nexport type ProcedureResult = __Infer<typeof ProcedureResult>;\n\n// The tagged union or sum type for the algebraic type `ProcedureStatus`.\nexport const ProcedureStatus = __t.enum('ProcedureStatus', {\n  Returned: __t.byteArray(),\n  InternalError: __t.string(),\n});\nexport type ProcedureStatus = __Infer<typeof ProcedureStatus>;\n\nexport const QueryRows = __t.object('QueryRows', {\n  get tables() {\n    return __t.array(SingleTableRows);\n  },\n});\nexport type QueryRows = __Infer<typeof QueryRows>;\n\nexport const QuerySetId = __t.object('QuerySetId', {\n  id: __t.u32(),\n});\nexport type QuerySetId = __Infer<typeof QuerySetId>;\n\nexport const QuerySetUpdate = __t.object('QuerySetUpdate', {\n  get querySetId() {\n    return QuerySetId;\n  },\n  get tables() {\n    return __t.array(TableUpdate);\n  },\n});\nexport type QuerySetUpdate = __Infer<typeof QuerySetUpdate>;\n\nexport const ReducerOk = __t.object('ReducerOk', {\n  retValue: __t.byteArray(),\n  get transactionUpdate() {\n    return TransactionUpdate;\n  },\n});\nexport type ReducerOk = __Infer<typeof ReducerOk>;\n\n// The tagged union or sum type for the algebraic type `ReducerOutcome`.\nexport const ReducerOutcome = __t.enum('ReducerOutcome', {\n  get Ok() {\n    return ReducerOk;\n  },\n  OkEmpty: __t.unit(),\n  Err: __t.byteArray(),\n  InternalError: __t.string(),\n});\nexport type ReducerOutcome = __Infer<typeof ReducerOutcome>;\n\nexport const ReducerResult = __t.object('ReducerResult', {\n  requestId: __t.u32(),\n  timestamp: __t.timestamp(),\n  get result() {\n    return ReducerOutcome;\n  },\n});\nexport type ReducerResult = __Infer<typeof ReducerResult>;\n\n// The tagged union or sum type for the algebraic type `RowSizeHint`.\nexport const RowSizeHint = __t.enum('RowSizeHint', {\n  FixedSize: __t.u16(),\n  RowOffsets: __t.array(__t.u64()),\n});\nexport type RowSizeHint = __Infer<typeof RowSizeHint>;\n\n// The tagged union or sum type for the algebraic type `ServerMessage`.\nexport const ServerMessage = __t.enum('ServerMessage', {\n  get InitialConnection() {\n    return InitialConnection;\n  },\n  get SubscribeApplied() {\n    return SubscribeApplied;\n  },\n  get UnsubscribeApplied() {\n    return UnsubscribeApplied;\n  },\n  get SubscriptionError() {\n    return SubscriptionError;\n  },\n  get TransactionUpdate() {\n    return TransactionUpdate;\n  },\n  get OneOffQueryResult() {\n    return OneOffQueryResult;\n  },\n  get ReducerResult() {\n    return ReducerResult;\n  },\n  get ProcedureResult() {\n    return ProcedureResult;\n  },\n});\nexport type ServerMessage = __Infer<typeof ServerMessage>;\n\nexport const SingleTableRows = __t.object('SingleTableRows', {\n  table: __t.string(),\n  get rows() {\n    return BsatnRowList;\n  },\n});\nexport type SingleTableRows = __Infer<typeof SingleTableRows>;\n\nexport const Subscribe = __t.object('Subscribe', {\n  requestId: __t.u32(),\n  get querySetId() {\n    return QuerySetId;\n  },\n  queryStrings: __t.array(__t.string()),\n});\nexport type Subscribe = __Infer<typeof Subscribe>;\n\nexport const SubscribeApplied = __t.object('SubscribeApplied', {\n  requestId: __t.u32(),\n  get querySetId() {\n    return QuerySetId;\n  },\n  get rows() {\n    return QueryRows;\n  },\n});\nexport type SubscribeApplied = __Infer<typeof SubscribeApplied>;\n\nexport const SubscriptionError = __t.object('SubscriptionError', {\n  requestId: __t.option(__t.u32()),\n  get querySetId() {\n    return QuerySetId;\n  },\n  error: __t.string(),\n});\nexport type SubscriptionError = __Infer<typeof SubscriptionError>;\n\nexport const TableUpdate = __t.object('TableUpdate', {\n  tableName: __t.string(),\n  get rows() {\n    return __t.array(TableUpdateRows);\n  },\n});\nexport type TableUpdate = __Infer<typeof TableUpdate>;\n\n// The tagged union or sum type for the algebraic type `TableUpdateRows`.\nexport const TableUpdateRows = __t.enum('TableUpdateRows', {\n  get PersistentTable() {\n    return PersistentTableRows;\n  },\n  get EventTable() {\n    return EventTableRows;\n  },\n});\nexport type TableUpdateRows = __Infer<typeof TableUpdateRows>;\n\nexport const TransactionUpdate = __t.object('TransactionUpdate', {\n  get querySets() {\n    return __t.array(QuerySetUpdate);\n  },\n});\nexport type TransactionUpdate = __Infer<typeof TransactionUpdate>;\n\nexport const Unsubscribe = __t.object('Unsubscribe', {\n  requestId: __t.u32(),\n  get querySetId() {\n    return QuerySetId;\n  },\n  get flags() {\n    return UnsubscribeFlags;\n  },\n});\nexport type Unsubscribe = __Infer<typeof Unsubscribe>;\n\nexport const UnsubscribeApplied = __t.object('UnsubscribeApplied', {\n  requestId: __t.u32(),\n  get querySetId() {\n    return QuerySetId;\n  },\n  get rows() {\n    return __t.option(QueryRows);\n  },\n});\nexport type UnsubscribeApplied = __Infer<typeof UnsubscribeApplied>;\n\n// The tagged union or sum type for the algebraic type `UnsubscribeFlags`.\nexport const UnsubscribeFlags = __t.enum('UnsubscribeFlags', {\n  Default: __t.unit(),\n  SendDroppedRows: __t.unit(),\n});\nexport type UnsubscribeFlags = __Infer<typeof UnsubscribeFlags>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_cache.ts",
    "content": "import type { TableNamesOf, UntypedSchemaDef } from '../lib/schema.ts';\nimport type { UntypedTableDef } from '../lib/table.ts';\nimport type { Values } from '../lib/type_util.ts';\nimport type { UntypedRemoteModule } from './spacetime_module.ts';\nimport { type TableCache, TableCacheImpl } from './table_cache.ts';\n\ntype TableName<SchemaDef> = [SchemaDef] extends [UntypedSchemaDef]\n  ? TableNamesOf<SchemaDef>\n  : string;\n\nexport type TableDefForTableName<\n  SchemaDef extends UntypedSchemaDef,\n  N extends TableName<SchemaDef>,\n> = [SchemaDef] extends [UntypedSchemaDef]\n  ? Values<SchemaDef['tables']> & { accessorName: N }\n  : UntypedTableDef & { accessorName: N };\n\ntype TableCacheForTableName<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = TableCache<RemoteModule, TableName>;\n\n/**\n * This is a helper class that provides a mapping from table names to their corresponding TableCache instances\n * while preserving the correspondence between the key and value type.\n */\nclass TableMap<RemoteModule extends UntypedRemoteModule> {\n  private readonly map: Map<\n    string,\n    TableCacheForTableName<RemoteModule, TableName<RemoteModule>>\n  > = new Map();\n\n  get<K extends TableName<RemoteModule>>(\n    key: K\n  ): TableCacheForTableName<RemoteModule, K> | undefined {\n    // Cast required: a Map<string, Union> can't refine the union to the exact K-specific member on get<K>(key: K).\n    return this.map.get(key) as\n      | TableCacheForTableName<RemoteModule, K>\n      | undefined;\n  }\n\n  set<K extends TableName<RemoteModule>>(\n    key: K,\n    value: TableCacheForTableName<RemoteModule, K>\n  ): this {\n    this.map.set(key, value);\n    return this;\n  }\n\n  has(key: TableName<RemoteModule>): boolean {\n    return this.map.has(key);\n  }\n\n  delete(key: TableName<RemoteModule>): boolean {\n    return this.map.delete(key);\n  }\n\n  // optional: iteration stays broadly typed (cannot express per-key relation here)\n  keys(): IterableIterator<string> {\n    return this.map.keys();\n  }\n  values(): IterableIterator<\n    TableCacheForTableName<RemoteModule, TableName<RemoteModule>>\n  > {\n    return this.map.values();\n  }\n  entries(): IterableIterator<\n    [string, TableCacheForTableName<RemoteModule, TableName<RemoteModule>>]\n  > {\n    return this.map.entries();\n  }\n  [Symbol.iterator]() {\n    return this.entries();\n  }\n}\n\n/**\n * ClientCache maintains a cache of TableCache instances for each table in the database.\n * It provides methods to get or create TableCache instances by table name,\n * ensuring type safety based on the provided SchemaDef.\n */\nexport class ClientCache<RemoteModule extends UntypedRemoteModule> {\n  /**\n   * The tables in the database.\n   */\n  readonly tables = new TableMap<RemoteModule>();\n\n  /**\n   * Returns the table with the given name.\n   * - If SchemaDef is a concrete schema, `name` is constrained to known table names,\n   *   and the return type matches that table.\n   * - If SchemaDef is undefined, `name` is string and the return type is untyped.\n   */\n  getTable<N extends TableName<RemoteModule>>(\n    name: N\n  ): TableCacheForTableName<RemoteModule, N> {\n    const table = this.tables.get(name);\n    if (!table) {\n      console.error(\n        'The table has not been registered for this client. Please register the table before using it. If you have registered global tables using the SpacetimeDBClient.registerTables() or `registerTable()` method, please make sure that is executed first!'\n      );\n      throw new Error(`Table ${String(name)} does not exist`);\n    }\n    return table;\n  }\n\n  /**\n   * Returns the table with the given name, creating it if needed.\n   * - Typed mode: `tableTypeInfo.tableName` is constrained to known names and\n   *   the return type matches that table.\n   * - Untyped mode: accepts any string and returns an untyped TableCache.\n   */\n  getOrCreateTable<N extends TableName<RemoteModule>>(\n    tableDef: TableDefForTableName<RemoteModule, N>\n  ): TableCacheForTableName<RemoteModule, N> {\n    const name = tableDef.accessorName;\n\n    const table = this.tables.get(name);\n    if (table) {\n      return table;\n    }\n\n    const newTable = new TableCacheImpl<RemoteModule, N>(\n      tableDef\n    ) as TableCache<RemoteModule, N>;\n    this.tables.set(name, newTable);\n    return newTable;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/client_table.ts",
    "content": "import type { ReadonlyIndexes } from '../lib/indexes';\nimport type { TableNamesOf } from '../lib/schema';\nimport type {\n  ReadonlyTableMethods,\n  RowType,\n  TableIndexes,\n  UntypedTableDef,\n} from '../lib/table';\nimport type { ColumnBuilder } from '../lib/type_builders';\nimport type { Prettify } from '../lib/type_util';\nimport type { TableDefForTableName } from './client_cache';\nimport type { EventContextInterface } from './event_context';\nimport type { UntypedRemoteModule } from './spacetime_module';\n\nexport type ClientTablePrimaryKeyMethods<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = {\n  /**\n   * Registers a callback to be invoked when a row is updated in the table.\n   * Requires that the table has a primary key defined.\n   * @param cb The callback to invoke when a row is updated.\n   */\n  onUpdate(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      oldRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n      newRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n\n  /**\n   * Removes a previously registered update event listener.\n   * @param cb The callback to remove from the update event listeners.\n   */\n  removeOnUpdate(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      oldRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n      newRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n};\n\nexport type ClientTableInsertMethods<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = {\n  /**\n   * Registers a callback to be invoked when a row is inserted into the table.\n   */\n  onInsert(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n\n  /**\n   *  Removes a previously registered insert event listener.\n   * @param cb The callback to remove from the insert event listeners.\n   */\n  removeOnInsert(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n};\n\nexport type ClientTableDeleteMethods<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = {\n  /**\n   * Registers a callback to be invoked when a row is deleted from the table.\n   */\n  onDelete(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n\n  /**\n   * Removes a previously registered delete event listener.\n   * @param cb The callback to remove from the delete event listeners.\n   */\n  removeOnDelete(\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void;\n};\n\nexport type ClientTableMethods<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = ClientTableInsertMethods<RemoteModule, TableName> &\n  ClientTableDeleteMethods<RemoteModule, TableName>;\n\n/**\n * Table<Row, UniqueConstraintViolation = never, AutoIncOverflow = never>\n *\n * - Row: row shape\n * - UCV: unique-constraint violation error type (never if none)\n * - AIO: auto-increment overflow error type (never if none)\n */\nexport type ClientTable<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = Prettify<\n  ClientTableCore<RemoteModule, TableName> &\n    ReadonlyIndexes<\n      TableDefForTableName<RemoteModule, TableName>,\n      TableIndexes<TableDefForTableName<RemoteModule, TableName>>\n    >\n>;\n\ntype IsEventTable<TableDef extends UntypedTableDef> = TableDef extends {\n  isEvent: true;\n}\n  ? true\n  : false;\n\ntype HasPrimaryKey<TableDef extends UntypedTableDef> = ColumnsHavePrimaryKey<\n  TableDef['columns']\n>;\n\ntype ColumnsHavePrimaryKey<\n  Cs extends Record<string, ColumnBuilder<any, any, any>>,\n> = {\n  [K in keyof Cs]: Cs[K] extends ColumnBuilder<any, any, infer M>\n    ? M extends { isPrimaryKey: true }\n      ? true\n      : never\n    : never;\n}[keyof Cs] extends true\n  ? true\n  : false;\n\ntype MaybePKMethods<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = Partial<ClientTablePrimaryKeyMethods<RemoteModule, TableName>>;\n\n/**\n * A variant of ClientTableCore where the primary key methods are always optional,\n * allowing for classes like TableCache to implement this interface\n */\nexport type ClientTableCoreImplementable<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = ReadonlyTableMethods<TableDefForTableName<RemoteModule, TableName>> &\n  ClientTableMethods<RemoteModule, TableName> &\n  // always present but optional -> statically known member set\n  MaybePKMethods<RemoteModule, TableName>;\n\n/**\n * Core methods of ClientTable, without the indexes mixed in.\n * Includes only statically known methods.\n *\n * Event tables only expose insert callbacks (no delete or update),\n * matching the Rust SDK's `EventTable` trait.\n */\nexport type ClientTableCore<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = ReadonlyTableMethods<TableDefForTableName<RemoteModule, TableName>> &\n  ClientTableInsertMethods<RemoteModule, TableName> &\n  (IsEventTable<TableDefForTableName<RemoteModule, TableName>> extends true\n    ? {}\n    : ClientTableDeleteMethods<RemoteModule, TableName> &\n        (HasPrimaryKey<\n          TableDefForTableName<RemoteModule, TableName>\n        > extends true\n          ? ClientTablePrimaryKeyMethods<RemoteModule, TableName>\n          : {}));\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/connection_manager.ts",
    "content": "/**\n * ConnectionManager - A reference-counted connection manager for SpacetimeDB.\n *\n * This module implements a TanStack Query-style pattern for managing WebSocket\n * connections in React applications. It solves the React StrictMode double-mount\n * problem by using reference counting and deferred cleanup.\n *\n * ## How it works:\n *\n * 1. **Reference Counting**: Each `retain()` increments a counter, `release()` decrements it.\n *    The connection is only closed when the count reaches zero.\n *\n * 2. **Deferred Cleanup**: When refCount hits zero, cleanup is scheduled via `setTimeout(0)`.\n *    This allows React StrictMode's rapid unmount→remount cycle to cancel the cleanup.\n *\n * 3. **useSyncExternalStore Integration**: The `subscribe()` and `getSnapshot()` methods\n *    are designed to work with React's `useSyncExternalStore` hook for tear-free reads.\n *\n * ## StrictMode Lifecycle:\n *\n * ```\n * Mount   → retain()  → refCount: 0→1, connection created\n * Unmount → release() → refCount: 1→0, cleanup SCHEDULED (not executed)\n * Remount → retain()  → refCount: 0→1, cleanup CANCELLED\n * Result: Single WebSocket survives ✓\n * ```\n *\n * @module connection_manager\n */\nimport type {\n  DbConnectionBuilder,\n  DbConnectionImpl,\n  ErrorContextInterface,\n} from './db_connection_impl';\nimport type { Identity } from '../lib/identity';\nimport { ConnectionId } from '../lib/connection_id';\n\n/** Represents the current state of a managed connection. */\nexport type ConnectionState = {\n  isActive: boolean;\n  identity?: Identity;\n  token?: string;\n  connectionId: ConnectionId;\n  connectionError?: Error;\n};\n\ntype Listener = () => void;\n\ntype ManagedConnection = {\n  connection?: DbConnectionImpl<any>;\n  refCount: number;\n  state: ConnectionState;\n  listeners: Set<Listener>;\n  pendingRelease: ReturnType<typeof setTimeout> | null;\n  onConnect?: (conn: DbConnectionImpl<any>) => void;\n  onDisconnect?: (ctx: ErrorContextInterface<any>, error?: Error) => void;\n  onConnectError?: (ctx: ErrorContextInterface<any>, error: Error) => void;\n};\n\nfunction defaultState(): ConnectionState {\n  return {\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n  };\n}\n\n/**\n * Singleton manager for SpacetimeDB connections.\n * Use the exported `ConnectionManager` instance rather than instantiating directly.\n */\nclass ConnectionManagerImpl {\n  #connections = new Map<string, ManagedConnection>();\n\n  /** Generates a unique key for a connection based on URI and module name. */\n  static getKey(uri: string, moduleName: string): string {\n    return `${uri}::${moduleName}`;\n  }\n\n  /** Instance method wrapper for getKey. */\n  getKey(uri: string, moduleName: string): string {\n    return ConnectionManagerImpl.getKey(uri, moduleName);\n  }\n\n  #ensureEntry(key: string): ManagedConnection {\n    const existing = this.#connections.get(key);\n    if (existing) {\n      return existing;\n    }\n    const managed: ManagedConnection = {\n      connection: undefined,\n      refCount: 0,\n      state: defaultState(),\n      listeners: new Set(),\n      pendingRelease: null,\n    };\n    this.#connections.set(key, managed);\n    return managed;\n  }\n\n  #notify(managed: ManagedConnection): void {\n    for (const listener of managed.listeners) {\n      listener();\n    }\n  }\n\n  /**\n   * Retains a connection, incrementing its reference count.\n   * Creates the connection on first call; returns existing connection on subsequent calls.\n   * Cancels any pending release if the connection was about to be cleaned up.\n   *\n   * @param key - Unique identifier for the connection (use getKey to generate)\n   * @param builder - Connection builder to create the connection if needed\n   * @returns The managed connection instance\n   */\n  retain<T extends DbConnectionImpl<any>>(\n    key: string,\n    builder: DbConnectionBuilder<T>\n  ): T {\n    const managed = this.#ensureEntry(key);\n    if (managed.pendingRelease) {\n      clearTimeout(managed.pendingRelease);\n      managed.pendingRelease = null;\n    }\n    managed.refCount += 1;\n    if (managed.connection) {\n      return managed.connection as T;\n    }\n\n    const connection = builder.build();\n    managed.connection = connection;\n\n    const updateState = (updates: Partial<ConnectionState>) => {\n      managed.state = { ...managed.state, ...updates };\n      this.#notify(managed);\n    };\n\n    updateState({\n      isActive: connection.isActive,\n      identity: connection.identity,\n      token: connection.token,\n      connectionId: connection.connectionId,\n      connectionError: undefined,\n    });\n\n    managed.onConnect = conn => {\n      updateState({\n        isActive: conn.isActive,\n        identity: conn.identity,\n        token: conn.token,\n        connectionId: conn.connectionId,\n        connectionError: undefined,\n      });\n    };\n\n    managed.onDisconnect = (ctx, error) => {\n      updateState({\n        isActive: ctx.isActive,\n        connectionError: error ?? undefined,\n      });\n    };\n\n    managed.onConnectError = (ctx, error) => {\n      updateState({\n        isActive: ctx.isActive,\n        connectionError: error,\n      });\n    };\n\n    builder.onConnect(managed.onConnect);\n    builder.onDisconnect(managed.onDisconnect);\n    builder.onConnectError(managed.onConnectError);\n\n    return connection as T;\n  }\n\n  release(key: string): void {\n    const managed = this.#connections.get(key);\n    if (!managed) {\n      return;\n    }\n\n    managed.refCount -= 1;\n    if (managed.refCount > 0 || managed.pendingRelease) {\n      return;\n    }\n\n    managed.pendingRelease = setTimeout(() => {\n      managed.pendingRelease = null;\n      if (managed.refCount > 0) {\n        return;\n      }\n      if (managed.connection) {\n        if (managed.onConnect) {\n          managed.connection.removeOnConnect(managed.onConnect as any);\n        }\n        if (managed.onDisconnect) {\n          managed.connection.removeOnDisconnect(managed.onDisconnect as any);\n        }\n        if (managed.onConnectError) {\n          managed.connection.removeOnConnectError(\n            managed.onConnectError as any\n          );\n        }\n        managed.connection.disconnect();\n      }\n      this.#connections.delete(key);\n    }, 0);\n  }\n\n  subscribe(key: string, listener: Listener): () => void {\n    const managed = this.#ensureEntry(key);\n    managed.listeners.add(listener);\n    return () => {\n      managed.listeners.delete(listener);\n      if (\n        managed.refCount <= 0 &&\n        managed.listeners.size === 0 &&\n        !managed.connection\n      ) {\n        this.#connections.delete(key);\n      }\n    };\n  }\n\n  getSnapshot(key: string): ConnectionState | undefined {\n    return this.#connections.get(key)?.state;\n  }\n\n  getConnection<T extends DbConnectionImpl<any>>(key: string): T | null {\n    return (this.#connections.get(key)?.connection as T | undefined) ?? null;\n  }\n}\n\nexport const ConnectionManager = new ConnectionManagerImpl();\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/db_connection_builder.ts",
    "content": "import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl';\nimport { EventEmitter } from './event_emitter';\nimport type {\n  DbConnectionConfig,\n  ErrorContextInterface,\n  Identity,\n  RemoteModuleOf,\n} from '../';\nimport { ensureMinimumVersionOrThrow } from './version';\nimport { WebsocketDecompressAdapter } from './websocket_decompress_adapter';\n\n/**\n * The database client connection to a SpacetimeDB server.\n * NOTE: DbConnectionImpl<any> is used here because UntypedRemoteModule causes\n * variance issues with function paramters, and the end user will never be\n * constructing a DbConnectionBuilder directly since it's code generated. We will\n * always have a concrete RemoteModule type in those cases. Even if they user\n * did do this, they would just lose type safety on the RemoteModule.\n */\nexport class DbConnectionBuilder<DbConnection extends DbConnectionImpl<any>> {\n  #uri?: URL;\n  #nameOrAddress?: string;\n  #identity?: Identity;\n  #token?: string;\n  #emitter: EventEmitter<ConnectionEvent> = new EventEmitter();\n  #compression: 'gzip' | 'none' = 'gzip';\n  #lightMode: boolean = false;\n  #confirmedReads?: boolean;\n  #createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn;\n\n  /**\n   * Creates a new `DbConnectionBuilder` database client and set the initial parameters.\n   *\n   * Users are not expected to call this constructor directly. Instead, use the static method `DbConnection.builder()`.\n   *\n   * @param remoteModule The remote module to use to connect to the SpacetimeDB server.\n   * @param dbConnectionConstructor The constructor to use to create a new `DbConnection`.\n   */\n  constructor(\n    private remoteModule: RemoteModuleOf<DbConnection>,\n    private dbConnectionCtor: (\n      config: DbConnectionConfig<RemoteModuleOf<DbConnection>>\n    ) => DbConnection\n  ) {\n    this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn;\n  }\n\n  /**\n   * Set the URI of the SpacetimeDB server to connect to.\n   *\n   * @param uri The URI of the SpacetimeDB server to connect to.\n   *\n   **/\n  withUri(uri: string | URL): this {\n    this.#uri = new URL(uri);\n    return this;\n  }\n\n  /**\n   * Set the name or Identity of the remote database to connect to.\n   *\n   * @param nameOrAddress\n   *\n   * @returns The `DbConnectionBuilder` instance.\n   */\n  withDatabaseName(nameOrAddress: string): this {\n    this.#nameOrAddress = nameOrAddress;\n    return this;\n  }\n\n  /**\n   * Set the identity of the client to connect to the database.\n   *\n   * @param token The credentials to use to authenticate with SpacetimeDB. This\n   * is optional. You can store the token returned by the `onConnect` callback\n   * to use in future connections.\n   *\n   * @returns The `DbConnectionBuilder` instance.\n   */\n  withToken(token?: string): this {\n    this.#token = token;\n    return this;\n  }\n\n  withWSFn(\n    createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn\n  ): this {\n    this.#createWSFn = createWSFn;\n    return this;\n  }\n\n  /**\n   * Set the compression algorithm to use for the connection.\n   *\n   * @param compression The compression algorithm to use for the connection.\n   */\n  withCompression(compression: 'gzip' | 'none'): this {\n    this.#compression = compression;\n    return this;\n  }\n\n  /**\n   * Sets the connection to operate in light mode.\n   *\n   * Light mode is a mode that reduces the amount of data sent over the network.\n   *\n   * @param lightMode The light mode for the connection.\n   */\n  withLightMode(lightMode: boolean): this {\n    this.#lightMode = lightMode;\n    return this;\n  }\n\n  /**\n   * Sets the connection to use confirmed reads.\n   *\n   * When enabled, the server will send query results only after they are\n   * confirmed to be durable.\n   *\n   * What durable means depends on the server configuration: a single node\n   * server may consider a transaction durable once it is `fsync`'ed to disk,\n   * whereas a cluster may require that some number of replicas have\n   * acknowledge that they have stored the transactions.\n   *\n   * Note that enabling confirmed reads will increase the latency between a\n   * reducer call and the corresponding subscription update arriving at the\n   * client.\n   *\n   * If this method is not called, not preference is sent to the server, and\n   * the server will choose the default.\n   *\n   * @param confirmedReads `true` to enable confirmed reads, `false` to disable.\n   */\n  withConfirmedReads(confirmedReads: boolean): this {\n    this.#confirmedReads = confirmedReads;\n    return this;\n  }\n\n  /**\n   * Register a callback to be invoked upon authentication with the database.\n   *\n   * @param identity A unique identifier for a client connected to a database.\n   * @param token The credentials to use to authenticate with SpacetimeDB.\n   *\n   * @returns The `DbConnectionBuilder` instance.\n   *\n   * The callback will be invoked with the `Identity` and private authentication `token` provided by the database to identify this connection.\n   *\n   * If credentials were supplied to connect, those passed to the callback will be equivalent to the ones used to connect.\n   *\n   * If the initial connection was anonymous, a new set of credentials will be generated by the database to identify this user.\n   *\n   * The credentials passed to the callback can be saved and used to authenticate the same user in future connections.\n   *\n   * @example\n   *\n   * ```ts\n   * DbConnection.builder().onConnect((ctx, identity, token) => {\n   *  console.log(\"Connected to SpacetimeDB with identity:\", identity.toHexString());\n   * });\n   * ```\n   */\n  onConnect(\n    callback: (\n      connection: DbConnection,\n      identity: Identity,\n      token: string\n    ) => void\n  ): this {\n    this.#emitter.on('connect', callback);\n    return this;\n  }\n\n  /**\n   * Register a callback to be invoked upon an error.\n   *\n   * @example\n   *\n   * ```ts\n   * DbConnection.builder().onConnectError((ctx, error) => {\n   *   console.log(\"Error connecting to SpacetimeDB:\", error);\n   * });\n   * ```\n   */\n  onConnectError(\n    callback: (\n      ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n      error: Error\n    ) => void\n  ): this {\n    this.#emitter.on('connectError', callback);\n    return this;\n  }\n\n  /**\n   * Registers a callback to run when a {@link DbConnection} whose connection initially succeeded\n   * is disconnected, either after a {@link DbConnection.disconnect()} call or due to an error.\n   *\n   * If the connection ended because of an error, the error is passed to the callback.\n   *\n   * The `callback` will be installed on the `DbConnection` created by `build`\n   * before initiating the connection, ensuring there's no opportunity for the disconnect to happen\n   * before the callback is installed.\n   *\n   * Note that this does not trigger if `build` fails\n   * or in cases where {@link DbConnectionBuilder.onConnectError} would trigger.\n   * This callback only triggers if the connection closes after `build` returns successfully\n   * and {@link DbConnectionBuilder.onConnect} is invoked, i.e., after the initial connection\n   * message is received.\n   *\n   * To simplify SDK implementation, at most one such callback can be registered.\n   * Calling `onDisconnect` on the same `DbConnectionBuilder` multiple times throws an error.\n   *\n   * Unlike callbacks registered via {@link DbConnection},\n   * no mechanism is provided to unregister the provided callback.\n   * This is a concession to ergonomics; there's no clean place to return a `CallbackId` from this method\n   * or from `build`.\n   *\n   * @param {function(error?: Error): void} callback - The callback to invoke upon disconnection.\n   * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`.\n   */\n  onDisconnect(\n    callback: (\n      ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n      error?: Error | undefined\n    ) => void\n  ): this {\n    this.#emitter.on('disconnect', callback);\n    return this;\n  }\n\n  getUri(): string {\n    return this.#uri?.toString() ?? '';\n  }\n\n  getModuleName(): string {\n    return this.#nameOrAddress ?? '';\n  }\n\n  /**\n   * Builds a new `DbConnection` with the parameters set on this `DbConnectionBuilder` and attempts to connect to the SpacetimeDB server.\n   *\n   * @returns A new `DbConnection` with the parameters set on this `DbConnectionBuilder`.\n   *\n   * @example\n   *\n   * ```ts\n   * const host = \"http://localhost:3000\";\n   * const name_or_address = \"database_name\"\n   * const auth_token = undefined;\n   * DbConnection.builder().withUri(host).withDatabaseName(name_or_address).withToken(auth_token).build();\n   * ```\n   */\n  build(): DbConnection {\n    if (!this.#uri) {\n      throw new Error('URI is required to connect to SpacetimeDB');\n    }\n\n    if (!this.#nameOrAddress) {\n      throw new Error(\n        'Database name or address is required to connect to SpacetimeDB'\n      );\n    }\n    // We could consider making this an `onConnectError` instead of throwing here.\n    // Ideally, it would be a compile time error, but I'm not sure how to accomplish that.\n    ensureMinimumVersionOrThrow(this.remoteModule.versionInfo?.cliVersion);\n\n    return this.dbConnectionCtor({\n      uri: this.#uri,\n      nameOrAddress: this.#nameOrAddress,\n      identity: this.#identity,\n      token: this.#token,\n      emitter: this.#emitter,\n      compression: this.#compression,\n      lightMode: this.#lightMode,\n      confirmedReads: this.#confirmedReads,\n      createWSFn: this.#createWSFn,\n      remoteModule: this.remoteModule,\n    });\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/db_connection_impl.ts",
    "content": "import { ConnectionId, ProductBuilder, ProductType } from '../';\nimport { AlgebraicType, type ComparablePrimitive } from '../';\nimport { BinaryReader } from '../';\nimport { BinaryWriter } from '../';\nimport {\n  BsatnRowList,\n  ClientMessage,\n  QueryRows,\n  QuerySetUpdate,\n  ServerMessage,\n  TableUpdateRows,\n  UnsubscribeFlags,\n} from './client_api/types';\nimport { ClientCache } from './client_cache.ts';\nimport { DbConnectionBuilder } from './db_connection_builder.ts';\nimport { INTERNAL_REMOTE_MODULE } from './internal.ts';\nimport { type DbContext } from './db_context.ts';\nimport type { Event } from './event.ts';\nimport {\n  type ErrorContextInterface,\n  type EventContextInterface,\n  type ReducerEventContextInterface,\n  type SubscriptionEventContextInterface,\n} from './event_context.ts';\nimport { EventEmitter } from './event_emitter.ts';\nimport type { Deserializer, Identity, InferTypeOfRow, Serializer } from '../';\nimport type {\n  ProcedureResultMessage,\n  ReducerResultMessage,\n} from './message_types.ts';\nimport type { ReducerEvent } from './reducer_event.ts';\nimport { type UntypedRemoteModule } from './spacetime_module.ts';\nimport { makeQueryBuilder } from '../lib/query';\nimport {\n  type TableCache,\n  type Operation,\n  type PendingCallback,\n  type TableUpdate as CacheTableUpdate,\n} from './table_cache.ts';\nimport {\n  WebsocketDecompressAdapter,\n  type WebsocketAdapter,\n} from './websocket_decompress_adapter.ts';\nimport {\n  SubscriptionBuilderImpl,\n  SubscriptionHandleImpl,\n  SubscriptionManager,\n  type SubscribeEvent,\n} from './subscription_builder_impl.ts';\nimport { stdbLogger, stringify } from './logger.ts';\nimport { fromByteArray } from 'base64-js';\nimport type {\n  ReducerEventInfo,\n  ReducersView,\n  SubscriptionEventCallback,\n} from './reducers.ts';\nimport type { ClientDbView } from './db_view.ts';\nimport type { RowType, UntypedTableDef } from '../lib/table.ts';\nimport type { ProceduresView } from './procedures.ts';\nimport type { Values } from '../lib/type_util.ts';\nimport type { TransactionUpdate } from './client_api/types.ts';\nimport { InternalError, SenderError } from '../lib/errors.ts';\n\nexport {\n  DbConnectionBuilder,\n  SubscriptionBuilderImpl,\n  SubscriptionHandleImpl,\n  type TableCache,\n  type Event,\n};\n\nexport type RemoteModuleOf<C> =\n  C extends DbConnectionImpl<infer RM> ? RM : never;\n\nexport type {\n  DbContext,\n  EventContextInterface,\n  ReducerEventContextInterface,\n  SubscriptionEventContextInterface,\n  ErrorContextInterface,\n  ReducerEvent,\n};\n\nexport type ConnectionEvent = 'connect' | 'disconnect' | 'connectError';\n\nexport type DbConnectionConfig<RemoteModule extends UntypedRemoteModule> = {\n  uri: URL;\n  nameOrAddress: string;\n  identity?: Identity;\n  token?: string;\n  emitter: EventEmitter<ConnectionEvent>;\n  createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn;\n  compression: 'gzip' | 'none';\n  lightMode: boolean;\n  confirmedReads?: boolean;\n  remoteModule: RemoteModule;\n};\n\ntype ProcedureCallback = (result: ProcedureResultMessage['result']) => void;\n\nexport class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>\n  implements DbContext<RemoteModule>\n{\n  /**\n   * Whether or not the connection is active.\n   */\n  isActive = false;\n\n  /**\n   * This connection's public identity.\n   */\n  identity?: Identity = undefined;\n\n  /**\n   * This connection's private authentication token.\n   */\n  token?: string = undefined;\n\n  /** @internal */\n  [INTERNAL_REMOTE_MODULE](): RemoteModule {\n    return this.#remoteModule;\n  }\n\n  /**\n   * The accessor field to access the tables in the database and associated\n   * callback functions.\n   */\n  db: ClientDbView<RemoteModule>;\n\n  /**\n   * The accessor field to access the reducers in the database.\n   */\n  reducers: ReducersView<RemoteModule>;\n\n  /**\n   * The accessor field to access the procedures in the database.\n   */\n  procedures: ProceduresView<RemoteModule>;\n\n  /**\n   * The `ConnectionId` of the connection to to the database.\n   */\n  connectionId: ConnectionId = ConnectionId.random();\n\n  // These fields are meant to be strictly private.\n  #queryId = 0;\n  #requestId = 0;\n  #eventId = 0;\n  #emitter: EventEmitter<ConnectionEvent>;\n  #messageQueue = Promise.resolve();\n  #outboundQueue: Uint8Array[] = [];\n  #subscriptionManager = new SubscriptionManager<RemoteModule>();\n  #remoteModule: RemoteModule;\n  #reducerCallbacks = new Map<\n    number,\n    (result: ReducerResultMessage['result']) => void\n  >();\n  #reducerCallInfo = new Map<number, { name: string; args: object }>();\n  #procedureCallbacks = new Map<number, ProcedureCallback>();\n  #rowDeserializers: Record<string, Deserializer<any>>;\n  #reducerArgsSerializers: Record<\n    string,\n    { serialize: Serializer<any>; deserialize: Deserializer<any> }\n  >;\n  #procedureSerializers: Record<\n    string,\n    { serializeArgs: Serializer<any>; deserializeReturn: Deserializer<any> }\n  >;\n  #sourceNameToTableDef: Record<string, Values<RemoteModule['tables']>>;\n\n  // These fields are not part of the public API, but in a pinch you\n  // could use JavaScript to access them by bypassing TypeScript's\n  // private fields.\n  // We use them in testing.\n  private clientCache: ClientCache<RemoteModule>;\n  private ws?: WebsocketAdapter;\n  private wsPromise: Promise<WebsocketAdapter | undefined>;\n\n  constructor({\n    uri,\n    nameOrAddress,\n    identity,\n    token,\n    emitter,\n    remoteModule,\n    createWSFn,\n    compression,\n    lightMode,\n    confirmedReads,\n  }: DbConnectionConfig<RemoteModule>) {\n    stdbLogger('info', 'Connecting to SpacetimeDB WS...');\n\n    // We use .toString() here because some versions of React Native contain a bug where the URL constructor\n    // incorrectly treats a URL instance as a plain string.\n    // This results in an attempt to call .endsWith() on it, leading to an error.\n    const url = new URL(uri.toString());\n    if (!/^wss?:/.test(uri.protocol)) {\n      url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n    }\n\n    this.identity = identity;\n    this.token = token;\n\n    this.#remoteModule = remoteModule;\n    this.#emitter = emitter;\n\n    this.#rowDeserializers = Object.create(null);\n    this.#sourceNameToTableDef = Object.create(null);\n    for (const table of Object.values(remoteModule.tables)) {\n      this.#rowDeserializers[table.sourceName] = ProductType.makeDeserializer(\n        table.rowType\n      );\n      this.#sourceNameToTableDef[table.sourceName] = table as Values<\n        RemoteModule['tables']\n      >;\n    }\n\n    this.#reducerArgsSerializers = Object.create(null);\n    for (const reducer of remoteModule.reducers) {\n      this.#reducerArgsSerializers[reducer.name] = {\n        serialize: ProductType.makeSerializer(reducer.paramsType),\n        deserialize: ProductType.makeDeserializer(reducer.paramsType),\n      };\n    }\n\n    this.#procedureSerializers = Object.create(null);\n    for (const procedure of remoteModule.procedures) {\n      this.#procedureSerializers[procedure.name] = {\n        serializeArgs: ProductType.makeSerializer(\n          new ProductBuilder(procedure.params).algebraicType.value\n        ),\n        deserializeReturn: AlgebraicType.makeDeserializer(\n          procedure.returnType.algebraicType\n        ),\n      };\n    }\n\n    const connectionId = this.connectionId.toHexString();\n    url.searchParams.set('connection_id', connectionId);\n\n    this.clientCache = new ClientCache<RemoteModule>();\n    this.db = this.#makeDbView();\n    this.reducers = this.#makeReducers(remoteModule);\n    this.procedures = this.#makeProcedures(remoteModule);\n\n    this.wsPromise = createWSFn({\n      url,\n      nameOrAddress,\n      wsProtocol: 'v2.bsatn.spacetimedb',\n      authToken: token,\n      compression: compression,\n      lightMode: lightMode,\n      confirmedReads: confirmedReads,\n    })\n      .then(v => {\n        this.ws = v;\n\n        this.ws.onclose = () => {\n          this.#emitter.emit('disconnect', this);\n          this.isActive = false;\n        };\n        this.ws.onerror = (e: ErrorEvent) => {\n          this.#emitter.emit('connectError', this, e);\n          this.isActive = false;\n        };\n        this.ws.onopen = this.#handleOnOpen.bind(this);\n        this.ws.onmessage = this.#handleOnMessage.bind(this);\n        return v;\n      })\n      .catch(e => {\n        stdbLogger('error', 'Error connecting to SpacetimeDB WS');\n        this.#emitter.emit('connectError', this, e);\n\n        return undefined;\n      });\n  }\n\n  #getNextQueryId = () => {\n    const queryId = this.#queryId;\n    this.#queryId += 1;\n    return queryId;\n  };\n\n  #getNextRequestId = () => this.#requestId++;\n\n  #makeDbView(): ClientDbView<RemoteModule> {\n    const view = Object.create(null) as ClientDbView<RemoteModule>;\n\n    for (const tbl of Object.values(this.#sourceNameToTableDef)) {\n      // ClientDbView uses this name verbatim\n      const key = tbl.accessorName;\n      Object.defineProperty(view, key, {\n        enumerable: true,\n        configurable: false,\n        get: () => this.clientCache.getOrCreateTable(tbl),\n      });\n    }\n\n    return view;\n  }\n\n  #makeReducers(def: RemoteModule): ReducersView<RemoteModule> {\n    const out: Record<string, unknown> = {};\n\n    const writer = new BinaryWriter(1024);\n\n    for (const reducer of def.reducers) {\n      const reducerName = reducer.name;\n      const key = reducer.accessorName;\n\n      const { serialize: serializeArgs } =\n        this.#reducerArgsSerializers[reducerName];\n\n      (out as any)[key] = (params: InferTypeOfRow<typeof reducer.params>) => {\n        writer.clear();\n        serializeArgs(writer, params);\n        const argsBuffer = writer.getBuffer();\n        return this.callReducer(reducerName, argsBuffer, params);\n      };\n    }\n\n    return out as ReducersView<RemoteModule>;\n  }\n\n  #makeProcedures(def: RemoteModule): ProceduresView<RemoteModule> {\n    const out: Record<string, unknown> = {};\n\n    const writer = new BinaryWriter(1024);\n\n    for (const procedure of def.procedures) {\n      const procedureName = procedure.name;\n      const key = procedure.accessorName;\n\n      const { serializeArgs, deserializeReturn } =\n        this.#procedureSerializers[procedureName];\n\n      (out as any)[key] = (\n        params: InferTypeOfRow<typeof procedure.params>\n      ): Promise<any> => {\n        writer.clear();\n        serializeArgs(writer, params);\n        const argsBuffer = writer.getBuffer();\n        return this.callProcedure(procedureName, argsBuffer).then(returnBuf => {\n          return deserializeReturn(new BinaryReader(returnBuf));\n        });\n      };\n    }\n\n    return out as ProceduresView<RemoteModule>;\n  }\n\n  #makeEventContext(\n    event: Event<\n      ReducerEventInfo<\n        RemoteModule['reducers'][number]['name'],\n        InferTypeOfRow<RemoteModule['reducers'][number]['params']>\n      >\n    >\n  ): EventContextInterface<RemoteModule> {\n    // Bind methods to preserve `this` (#private fields safe)\n    return {\n      db: this.db,\n      reducers: this.reducers,\n      isActive: this.isActive,\n      subscriptionBuilder: this.subscriptionBuilder.bind(this),\n      disconnect: this.disconnect.bind(this),\n      event,\n    };\n  }\n\n  // NOTE: This is very important!!! This is the actual function that\n  // gets called when you call `connection.subscriptionBuilder()`.\n  // The `subscriptionBuilder` function which is generated, just shadows\n  // this function in the type system, but not the actual implementation!\n  // Do not remove this function, or shoot yourself in the foot please.\n  // It's not clear what would be a better way to do this at this exact\n  // moment.\n  subscriptionBuilder = (): SubscriptionBuilderImpl<RemoteModule> => {\n    return new SubscriptionBuilderImpl(this);\n  };\n\n  getTablesMap(): any {\n    return makeQueryBuilder({ tables: this.#remoteModule.tables } as any);\n  }\n\n  registerSubscription(\n    handle: SubscriptionHandleImpl<RemoteModule>,\n    handleEmitter: EventEmitter<\n      SubscribeEvent,\n      SubscriptionEventCallback<RemoteModule>\n    >,\n    querySql: string[]\n  ): number {\n    const querySetId = this.#getNextQueryId();\n    this.#subscriptionManager.subscriptions.set(querySetId, {\n      handle,\n      emitter: handleEmitter,\n    });\n    const requestId = this.#getNextRequestId();\n    this.#sendMessage(\n      ClientMessage.Subscribe({\n        queryStrings: querySql,\n        querySetId: { id: querySetId },\n        requestId,\n      })\n    );\n    return querySetId;\n  }\n\n  unregisterSubscription(querySetId: number): void {\n    const requestId = this.#getNextRequestId();\n    this.#sendMessage(\n      ClientMessage.Unsubscribe({\n        querySetId: { id: querySetId },\n        requestId,\n        flags: UnsubscribeFlags.SendDroppedRows,\n      })\n    );\n  }\n\n  #parseRowList(\n    type: 'insert' | 'delete',\n    tableName: string,\n    rowList: BsatnRowList\n  ): Operation[] {\n    const buffer = rowList.rowsData;\n    const reader = new BinaryReader(buffer);\n    const rows: Operation[] = [];\n\n    const deserializeRow = this.#rowDeserializers[tableName];\n    const table = this.#sourceNameToTableDef[tableName];\n    // TODO: performance\n    const columnsArray = Object.entries(table.columns);\n    const primaryKeyColumnEntry = columnsArray.find(\n      col => col[1].columnMetadata.isPrimaryKey\n    );\n    let previousOffset = 0;\n    while (reader.remaining > 0) {\n      const row = deserializeRow(reader);\n      let rowId: ComparablePrimitive | undefined = undefined;\n      if (primaryKeyColumnEntry !== undefined) {\n        const primaryKeyColName = primaryKeyColumnEntry[0];\n        const primaryKeyColType =\n          primaryKeyColumnEntry[1].typeBuilder.algebraicType;\n        rowId = AlgebraicType.intoMapKey(\n          primaryKeyColType,\n          row[primaryKeyColName]\n        );\n      } else {\n        // Get a view of the bytes for this row.\n        const rowBytes = buffer.subarray(previousOffset, reader.offset);\n        // Convert it to a base64 string, so we can use it as a map key.\n        const asBase64 = fromByteArray(rowBytes);\n        rowId = asBase64;\n      }\n      previousOffset = reader.offset;\n\n      rows.push({\n        type,\n        rowId,\n        row,\n      });\n    }\n    return rows;\n  }\n\n  // Take a bunch of table updates and ensure that there is at most one update per table.\n  #mergeTableUpdates(\n    updates: CacheTableUpdate<UntypedTableDef>[]\n  ): CacheTableUpdate<UntypedTableDef>[] {\n    const merged = new Map<string, Operation[]>();\n    for (const update of updates) {\n      const ops = merged.get(update.tableName);\n      if (ops) {\n        for (const op of update.operations) ops.push(op);\n      } else {\n        merged.set(update.tableName, update.operations.slice());\n      }\n    }\n    return Array.from(merged, ([tableName, operations]) => ({\n      tableName,\n      operations,\n    }));\n  }\n\n  #queryRowsToTableUpdates(\n    rows: QueryRows,\n    opType: 'insert' | 'delete'\n  ): CacheTableUpdate<UntypedTableDef>[] {\n    const updates: CacheTableUpdate<UntypedTableDef>[] = [];\n    for (const tableRows of rows.tables) {\n      updates.push({\n        tableName: tableRows.table,\n        operations: this.#parseRowList(opType, tableRows.table, tableRows.rows),\n      });\n    }\n    return this.#mergeTableUpdates(updates);\n  }\n\n  #tableUpdateRowsToOperations(\n    tableName: string,\n    rows: TableUpdateRows\n  ): Operation[] {\n    if (rows.tag === 'PersistentTable') {\n      const inserts = this.#parseRowList(\n        'insert',\n        tableName,\n        rows.value.inserts\n      );\n      const deletes = this.#parseRowList(\n        'delete',\n        tableName,\n        rows.value.deletes\n      );\n      return inserts.concat(deletes);\n    }\n    if (rows.tag === 'EventTable') {\n      // Event table rows are insert-only. The table cache handles skipping\n      // storage for event tables and only firing on_insert callbacks.\n      return this.#parseRowList('insert', tableName, rows.value.events);\n    }\n    return [];\n  }\n\n  #querySetUpdateToTableUpdates(\n    querySetUpdate: QuerySetUpdate\n  ): CacheTableUpdate<UntypedTableDef>[] {\n    const updates: CacheTableUpdate<UntypedTableDef>[] = [];\n    for (const tableUpdate of querySetUpdate.tables) {\n      let operations: Operation[] = [];\n      for (const rows of tableUpdate.rows) {\n        operations = operations.concat(\n          this.#tableUpdateRowsToOperations(tableUpdate.tableName, rows)\n        );\n      }\n      updates.push({\n        tableName: tableUpdate.tableName,\n        operations,\n      });\n    }\n    return this.#mergeTableUpdates(updates);\n  }\n\n  #flushOutboundQueue(wsResolved: WebsocketAdapter): void {\n    const pending = this.#outboundQueue.splice(0);\n    for (const message of pending) {\n      wsResolved.send(message);\n    }\n  }\n\n  #clientMessageEncoder = new BinaryWriter(1024);\n  #sendMessage(message: ClientMessage): void {\n    const writer = this.#clientMessageEncoder;\n    writer.clear();\n    ClientMessage.serialize(writer, message);\n    const encoded = writer.getBuffer();\n\n    if (this.ws && this.isActive) {\n      if (this.#outboundQueue.length) this.#flushOutboundQueue(this.ws);\n\n      stdbLogger(\n        'trace',\n        () => `Sending message to server: ${stringify(message)}`\n      );\n      this.ws.send(encoded);\n    } else {\n      stdbLogger(\n        'trace',\n        () => `Queuing message to server: ${stringify(message)}`\n      );\n      // use slice() to copy, in case the clientMessageEncoder's buffer gets used\n      this.#outboundQueue.push(encoded.slice());\n    }\n  }\n\n  #nextEventId(): string {\n    this.#eventId += 1;\n    return `${this.connectionId.toHexString()}:${this.#eventId}`;\n  }\n\n  /**\n   * Handles WebSocket onOpen event.\n   */\n  #handleOnOpen(): void {\n    this.isActive = true;\n    if (this.ws) {\n      this.#flushOutboundQueue(this.ws);\n    }\n  }\n\n  #applyTableUpdates(\n    tableUpdates: CacheTableUpdate<UntypedTableDef>[],\n    eventContext: EventContextInterface<RemoteModule>\n  ): PendingCallback[] {\n    const pendingCallbacks: PendingCallback[] = [];\n    for (const tableUpdate of tableUpdates) {\n      // Get table information for the table being updated\n      const tableName = tableUpdate.tableName;\n      const tableDef = this.#sourceNameToTableDef[tableName];\n      const table = this.clientCache.getOrCreateTable(tableDef);\n      const newCallbacks = table.applyOperations(\n        tableUpdate.operations as Operation<\n          RowType<Values<RemoteModule['tables']>>\n        >[],\n        eventContext\n      );\n      for (const callback of newCallbacks) {\n        pendingCallbacks.push(callback);\n      }\n    }\n    return pendingCallbacks;\n  }\n\n  #applyTransactionUpdates(\n    eventContext: EventContextInterface<RemoteModule>,\n    tu: TransactionUpdate\n  ): PendingCallback[] {\n    const allUpdates: CacheTableUpdate<UntypedTableDef>[] = [];\n    for (const querySetUpdate of tu.querySets) {\n      const tableUpdates = this.#querySetUpdateToTableUpdates(querySetUpdate);\n      for (const update of tableUpdates) {\n        allUpdates.push(update);\n      }\n      // TODO: When we have per-query storage, we will want to apply the per-query events here.\n    }\n    return this.#applyTableUpdates(\n      this.#mergeTableUpdates(allUpdates),\n      eventContext\n    );\n  }\n\n  async #processMessage(data: Uint8Array): Promise<void> {\n    const serverMessage = ServerMessage.deserialize(new BinaryReader(data));\n    stdbLogger(\n      'trace',\n      () => `Processing server message: ${stringify(serverMessage)}`\n    );\n    switch (serverMessage.tag) {\n      case 'InitialConnection': {\n        this.identity = serverMessage.value.identity;\n        if (!this.token && serverMessage.value.token) {\n          this.token = serverMessage.value.token;\n        }\n        this.connectionId = serverMessage.value.connectionId;\n        this.#emitter.emit('connect', this, this.identity, this.token);\n        break;\n      }\n      case 'SubscribeApplied': {\n        const querySetId = serverMessage.value.querySetId.id;\n        const subscription =\n          this.#subscriptionManager.subscriptions.get(querySetId);\n        if (!subscription) {\n          stdbLogger(\n            'error',\n            `Received SubscribeApplied for unknown querySetId ${querySetId}.`\n          );\n          return;\n        }\n        const event: Event<never> = {\n          id: this.#nextEventId(),\n          tag: 'SubscribeApplied',\n        };\n        const eventContext = this.#makeEventContext(event);\n        const tableUpdates = this.#queryRowsToTableUpdates(\n          serverMessage.value.rows,\n          'insert'\n        );\n        const callbacks = this.#applyTableUpdates(tableUpdates, eventContext);\n        const { event: _, ...subscriptionEventContext } = eventContext;\n        subscription.emitter.emit('applied', subscriptionEventContext);\n        stdbLogger(\n          'trace',\n          () => `Calling ${callbacks.length} triggered row callbacks`\n        );\n        for (const callback of callbacks) {\n          callback.cb();\n        }\n        break;\n      }\n      case 'UnsubscribeApplied': {\n        const querySetId = serverMessage.value.querySetId.id;\n        const subscription =\n          this.#subscriptionManager.subscriptions.get(querySetId);\n        if (!subscription) {\n          stdbLogger(\n            'error',\n            `Received UnsubscribeApplied for unknown querySetId ${querySetId}.`\n          );\n          return;\n        }\n        const event: Event<never> = {\n          id: this.#nextEventId(),\n          tag: 'UnsubscribeApplied',\n        };\n        const eventContext = this.#makeEventContext(event);\n        const tableUpdates = serverMessage.value.rows\n          ? this.#queryRowsToTableUpdates(serverMessage.value.rows, 'delete')\n          : [];\n        const callbacks = this.#applyTableUpdates(tableUpdates, eventContext);\n        const { event: _, ...subscriptionEventContext } = eventContext;\n        subscription.emitter.emit('end', subscriptionEventContext);\n        this.#subscriptionManager.subscriptions.delete(querySetId);\n        stdbLogger(\n          'trace',\n          () => `Calling ${callbacks.length} triggered row callbacks`\n        );\n        for (const callback of callbacks) {\n          callback.cb();\n        }\n        break;\n      }\n      case 'SubscriptionError': {\n        const querySetId = serverMessage.value.querySetId.id;\n        const requestId = serverMessage.value.requestId;\n        const error = Error(serverMessage.value.error);\n        const event: Event<never> = {\n          id: this.#nextEventId(),\n          tag: 'Error',\n          value: error,\n        };\n        const eventContext = this.#makeEventContext(event);\n        const errorContext = {\n          ...eventContext,\n          event: error,\n        };\n\n        // If the requestId isn't set, that means we already applied the subscription.\n        // Since we don't know how to remove the relevant rows from our table cache, we need\n        // to kill the connection. Once we have per-query storage, this won't be fatal.\n        if (requestId == null) {\n          stdbLogger(\n            'error',\n            `Disconnecting due to error for a previously applied subscription: ${serverMessage.value.error}`\n          );\n          this.disconnect();\n          break;\n        }\n\n        const subscription =\n          this.#subscriptionManager.subscriptions.get(querySetId);\n        if (subscription) {\n          subscription.emitter.emit('error', errorContext, error);\n          this.#subscriptionManager.subscriptions.delete(querySetId);\n        } else {\n          stdbLogger(\n            'error',\n            `Received SubscriptionError for unknown querySetId ${querySetId}:`,\n            error\n          );\n        }\n        break;\n      }\n      case 'TransactionUpdate': {\n        const event: Event<never> = {\n          id: this.#nextEventId(),\n          tag: 'Transaction',\n        };\n        const eventContext = this.#makeEventContext(event);\n        const callbacks = this.#applyTransactionUpdates(\n          eventContext,\n          serverMessage.value\n        );\n        stdbLogger(\n          'trace',\n          () => `Calling ${callbacks.length} triggered row callbacks`\n        );\n        for (const callback of callbacks) {\n          callback.cb();\n        }\n        break;\n      }\n      case 'ReducerResult': {\n        const { requestId, result } = serverMessage.value;\n\n        if (result.tag === 'Ok') {\n          const reducerInfo = this.#reducerCallInfo.get(requestId);\n          const eventId: string = this.#nextEventId();\n          const event: Event<any> = reducerInfo\n            ? {\n                id: eventId,\n                tag: 'Reducer',\n                value: {\n                  timestamp: serverMessage.value.timestamp,\n                  outcome: result,\n                  reducer: {\n                    name: reducerInfo.name,\n                    args: reducerInfo.args,\n                  },\n                },\n              }\n            : {\n                id: eventId,\n                tag: 'Transaction',\n              };\n          const eventContext = this.#makeEventContext(event as any);\n\n          const callbacks = this.#applyTransactionUpdates(\n            eventContext,\n            result.value.transactionUpdate\n          );\n          stdbLogger(\n            'trace',\n            () => `Calling ${callbacks.length} triggered row callbacks`\n          );\n          for (const callback of callbacks) {\n            callback.cb();\n          }\n        }\n        this.#reducerCallInfo.delete(requestId);\n        const cb = this.#reducerCallbacks.get(requestId);\n        this.#reducerCallbacks.delete(requestId);\n        cb?.(result);\n        break;\n      }\n      case 'ProcedureResult': {\n        const { status, requestId } = serverMessage.value;\n        const result: ProcedureResultMessage['result'] =\n          status.tag === 'Returned'\n            ? { tag: 'Ok', value: status.value }\n            : { tag: 'Err', value: status.value };\n        const cb = this.#procedureCallbacks.get(requestId);\n        this.#procedureCallbacks.delete(requestId);\n        cb?.(result);\n        break;\n      }\n      case 'OneOffQueryResult': {\n        stdbLogger(\n          'warn',\n          'Received OneOffQueryResult but SDK does not expose one-off query APIs yet.'\n        );\n        break;\n      }\n    }\n  }\n\n  /**\n   * Handles WebSocket onMessage event.\n   * @param wsMessage MessageEvent object.\n   */\n  #handleOnMessage(wsMessage: { data: Uint8Array }): void {\n    // Utilize promise chaining to ensure that we process messages in order\n    // even though we are processing them asyncronously. This will not begin\n    // processing the next message until we await the processing of the\n    // current message.\n    this.#messageQueue = this.#messageQueue.then(() => {\n      return this.#processMessage(wsMessage.data);\n    });\n  }\n\n  /**\n   * Call a reducer on your SpacetimeDB module.\n   *\n   * @param reducerName The name of the reducer to call\n   * @param argsSerializer The arguments to pass to the reducer\n   */\n  callReducer(\n    reducerName: string,\n    argsBuffer: Uint8Array,\n    reducerArgs?: object\n  ): Promise<void> {\n    const { promise, resolve, reject } = Promise.withResolvers<void>();\n    const requestId = this.#getNextRequestId();\n    const message = ClientMessage.CallReducer({\n      reducer: reducerName,\n      args: argsBuffer,\n      requestId,\n      flags: 0,\n    });\n    this.#sendMessage(message);\n    if (reducerArgs) {\n      this.#reducerCallInfo.set(requestId, {\n        name: reducerName,\n        args: reducerArgs,\n      });\n    }\n    this.#reducerCallbacks.set(requestId, result => {\n      if (result.tag === 'Ok' || result.tag === 'OkEmpty') {\n        resolve();\n      } else {\n        if (result.tag === 'Err') {\n          /// Interpret the user-returned error as a string.\n          const reader = new BinaryReader(result.value);\n          const errorString = reader.readString();\n          reject(new SenderError(errorString));\n        } else if (result.tag === 'InternalError') {\n          reject(new InternalError(result.value));\n        } else {\n          const unreachable: never = result;\n          reject(new Error('Unexpected reducer result'));\n          void unreachable;\n        }\n      }\n    });\n    return promise;\n  }\n\n  /**\n   * Call a reducer on your SpacetimeDB module with typed arguments.\n   * @param reducerSchema The schema of the reducer to call\n   * @param callReducerFlags The flags for the reducer call\n   * @param params The arguments to pass to the reducer\n   */\n  callReducerWithParams(\n    reducerName: string,\n    // TODO: remove\n    _paramsType: ProductType,\n    params: object\n  ): Promise<void> {\n    const writer = new BinaryWriter(1024);\n    this.#reducerArgsSerializers[reducerName].serialize(writer, params);\n    const argsBuffer = writer.getBuffer();\n    return this.callReducer(reducerName, argsBuffer, params);\n  }\n\n  /**\n   * Call a reducer on your SpacetimeDB module.\n   *\n   * @param procedureName The name of the reducer to call\n   * @param argsBuffer The arguments to pass to the reducer\n   */\n  callProcedure(\n    procedureName: string,\n    argsBuffer: Uint8Array\n  ): Promise<Uint8Array> {\n    const { promise, resolve, reject } = Promise.withResolvers<Uint8Array>();\n    const requestId = this.#getNextRequestId();\n    const message = ClientMessage.CallProcedure({\n      procedure: procedureName,\n      args: argsBuffer,\n      requestId,\n      // reserved for future use - 0 is the only valid value\n      flags: 0,\n    });\n    this.#sendMessage(message);\n    this.#procedureCallbacks.set(requestId, result => {\n      if (result.tag === 'Ok') {\n        resolve(result.value);\n      } else {\n        reject(result.value);\n      }\n    });\n    return promise;\n  }\n\n  /**\n   * Call a reducer on your SpacetimeDB module with typed arguments.\n   * @param reducerSchema The schema of the reducer to call\n   * @param callReducerFlags The flags for the reducer call\n   * @param params The arguments to pass to the reducer\n   */\n  callProcedureWithParams(\n    procedureName: string,\n    // TODO: remove\n    _paramsType: ProductType,\n    params: object,\n    // TODO: remove\n    _returnType: AlgebraicType\n  ): Promise<any> {\n    const writer = new BinaryWriter(1024);\n    const { serializeArgs, deserializeReturn } =\n      this.#procedureSerializers[procedureName];\n    serializeArgs(writer, params);\n    const argsBuffer = writer.getBuffer();\n    return this.callProcedure(procedureName, argsBuffer).then(returnBuf => {\n      return deserializeReturn(new BinaryReader(returnBuf));\n    });\n  }\n\n  /**\n   * Close the current connection.\n   *\n   * @example\n   *\n   * ```ts\n   * const connection = DbConnection.builder().build();\n   * connection.disconnect()\n   * ```\n   */\n  disconnect(): void {\n    this.wsPromise.then(ws => ws?.close());\n  }\n\n  private on(\n    eventName: ConnectionEvent,\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.on(eventName, callback);\n  }\n\n  private off(\n    eventName: ConnectionEvent,\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.off(eventName, callback);\n  }\n\n  private onConnect(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.on('connect', callback);\n  }\n\n  private onDisconnect(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.on('disconnect', callback);\n  }\n\n  private onConnectError(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.on('connectError', callback);\n  }\n\n  removeOnConnect(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.off('connect', callback);\n  }\n\n  removeOnDisconnect(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.off('disconnect', callback);\n  }\n\n  removeOnConnectError(\n    callback: (ctx: DbConnectionImpl<RemoteModule>, ...args: any[]) => void\n  ): void {\n    this.#emitter.off('connectError', callback);\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/db_context.ts",
    "content": "import type { ClientDbView } from './db_view';\nimport type { ReducersView } from './reducers';\nimport type { UntypedRemoteModule } from './spacetime_module';\nimport type { SubscriptionBuilderImpl } from './subscription_builder_impl';\n\n/**\n * Interface representing a database context.\n *\n * @template DbView - Type representing the database view.\n * @template ReducersDef - Type representing the reducers.\n */\nexport interface DbContext<RemoteModule extends UntypedRemoteModule> {\n  db: ClientDbView<RemoteModule>;\n  reducers: ReducersView<RemoteModule>;\n  isActive: boolean;\n\n  /**\n   * Creates a new subscription builder.\n   *\n   * @returns The subscription builder.\n   */\n  subscriptionBuilder(): SubscriptionBuilderImpl<RemoteModule>;\n\n  /**\n   * Disconnects from the database.\n   */\n  disconnect(): void;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/db_view.ts",
    "content": "import type { UntypedRemoteModule } from './spacetime_module';\nimport type { ClientTable } from './client_table';\nimport type { Values } from '../lib/type_util';\n\n/**\n * A type representing a client-side database view, mapping table names to their corresponding client Table handles.\n */\nexport type ClientDbView<RemoteModule extends UntypedRemoteModule> = {\n  readonly [TblName in Values<\n    RemoteModule['tables']\n  >['accessorName']]: ClientTable<RemoteModule, TblName>;\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/decompress.ts",
    "content": "export async function decompress(\n  buffer: Uint8Array,\n  // Leaving it here to expand to brotli when it lands in the browsers and NodeJS\n  type: 'gzip',\n  chunkSize: number = 128 * 1024 // 128KB\n): Promise<Uint8Array> {\n  // Create a single ReadableStream to handle chunks\n  let offset = 0;\n  const readableStream = new ReadableStream({\n    pull(controller) {\n      if (offset < buffer.length) {\n        // Slice a chunk of the buffer and enqueue it\n        const chunk = buffer.subarray(\n          offset,\n          Math.min(offset + chunkSize, buffer.length)\n        );\n        controller.enqueue(chunk);\n        offset += chunkSize;\n      } else {\n        controller.close();\n      }\n    },\n  });\n\n  // Create a single DecompressionStream\n  const decompressionStream = new DecompressionStream(type);\n\n  // Pipe the ReadableStream through the DecompressionStream\n  const decompressedStream = readableStream.pipeThrough(decompressionStream);\n\n  // Collect the decompressed chunks efficiently\n  const reader = decompressedStream.getReader();\n  const chunks: Uint8Array[] = [];\n  let totalLength = 0;\n  let result: any;\n\n  while (!(result = await reader.read()).done) {\n    chunks.push(result.value);\n    totalLength += result.value.length;\n  }\n\n  // Allocate a single Uint8Array for the decompressed data\n  const decompressedArray = new Uint8Array(totalLength);\n  let chunkOffset = 0;\n\n  for (const chunk of chunks) {\n    decompressedArray.set(chunk, chunkOffset);\n    chunkOffset += chunk.length;\n  }\n\n  return decompressedArray;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/event.ts",
    "content": "import type { ReducerEvent } from './reducer_event';\nimport type { ReducerEventInfo } from './reducers';\n\ntype WithId = {\n  /**\n   * A client-generated id to distinguish between different events.\n   */\n  id: string;\n};\n\nexport type Event<Reducer extends ReducerEventInfo> = WithId &\n  (\n    | { tag: 'Reducer'; value: ReducerEvent<Reducer> }\n    | { tag: 'SubscribeApplied' }\n    | { tag: 'UnsubscribeApplied' }\n    | { tag: 'Error'; value: Error }\n    | { tag: 'Transaction' }\n  );\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/event_context.ts",
    "content": "import type { InferTypeOfRow } from '../lib/type_builders.ts';\nimport type { DbContext } from './db_context';\nimport type { Event } from './event.ts';\nimport type { ReducerEvent } from './reducer_event.ts';\nimport type { ReducerEventInfo } from './reducers.ts';\nimport type { UntypedRemoteModule } from './spacetime_module.ts';\n\nexport type UntypedEventContext = EventContextInterface<UntypedRemoteModule>;\n\nexport interface EventContextInterface<RemoteModule extends UntypedRemoteModule>\n  extends DbContext<RemoteModule> {\n  /** Enum with variants for all possible events. */\n  event: Event<\n    ReducerEventInfo<\n      RemoteModule['reducers'][number]['name'],\n      InferTypeOfRow<RemoteModule['reducers'][number]['params']>\n    >\n  >;\n}\n\nexport interface ReducerEventContextInterface<\n  RemoteModule extends UntypedRemoteModule,\n> extends DbContext<RemoteModule> {\n  /** Enum with variants for all possible events. */\n  event: ReducerEvent<\n    ReducerEventInfo<\n      RemoteModule['reducers'][number]['name'],\n      InferTypeOfRow<RemoteModule['reducers'][number]['params']>\n    >\n  >;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ProcedureEventContextInterface<\n  RemoteModule extends UntypedRemoteModule,\n> extends DbContext<RemoteModule> {\n  /** No event is provided */\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SubscriptionEventContextInterface<\n  RemoteModule extends UntypedRemoteModule,\n> extends DbContext<RemoteModule> {\n  /** No event is provided **/\n}\n\nexport interface ErrorContextInterface<RemoteModule extends UntypedRemoteModule>\n  extends DbContext<RemoteModule> {\n  /** Enum with variants for all possible events. */\n  event?: Error;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/event_emitter.ts",
    "content": "// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport class EventEmitter<Key, Callback extends Function = Function> {\n  #events: Map<Key, Set<Callback>> = new Map();\n\n  on(event: Key, callback: Callback): void {\n    let callbacks = this.#events.get(event);\n    if (!callbacks) {\n      callbacks = new Set();\n      this.#events.set(event, callbacks);\n    }\n    callbacks.add(callback);\n  }\n\n  off(event: Key, callback: Callback): void {\n    const callbacks = this.#events.get(event);\n    if (!callbacks) {\n      return;\n    }\n    callbacks.delete(callback);\n  }\n\n  emit(event: Key, ...args: any[]): void {\n    const callbacks = this.#events.get(event);\n    if (!callbacks) {\n      return;\n    }\n\n    for (const callback of callbacks) {\n      callback(...args);\n    }\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/index.ts",
    "content": "// Should be at the top as other modules depend on it\nexport * from './db_connection_impl.ts';\nexport * from './client_cache.ts';\nexport * from './message_types.ts';\nexport * from '../lib/errors.ts';\nexport * from './logger.ts';\nexport { type ClientTable } from './client_table.ts';\nexport { type RemoteModule } from './spacetime_module.ts';\nexport * from '../lib/type_builders.ts';\nexport { schema, convertToAccessorMap } from './schema.ts';\nexport { table } from '../lib/table.ts';\nexport { reducerSchema, reducers } from './reducers.ts';\nexport { procedureSchema, procedures } from './procedures.ts';\nexport * from './type_utils.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/internal.ts",
    "content": "// Internal symbols not exported from the public SDK surface.\nexport const INTERNAL_REMOTE_MODULE = Symbol('INTERNAL_REMOTE_MODULE');\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/json_api.ts",
    "content": "export interface Message {\n  IdentityToken?: IdentityToken | undefined;\n  SubscriptionUpdate?: SubscriptionUpdate | undefined;\n  TransactionUpdate?: TransactionUpdate | undefined;\n}\n\nexport interface IdentityToken {\n  identity: string;\n  token: string;\n  address: string;\n}\n\nexport interface SubscriptionUpdate {\n  table_updates: TableUpdate[];\n}\n\nexport interface TableUpdate {\n  table_id: number;\n  table_name: string;\n  table_row_operations: TableRowOperation[];\n}\n\nexport interface TableRowOperation {\n  op: 'insert' | 'delete';\n  row: any[];\n}\n\nexport interface TransactionUpdate {\n  event: Event;\n  subscription_update: SubscriptionUpdate;\n}\n\nexport interface Event {\n  timestamp: number;\n  status: 'committed' | 'failed' | 'out_of_energy';\n  caller_identity: string;\n  caller_address: string;\n  function_call: FunctionCall;\n  energy_quanta_used: number;\n  message: string;\n}\n\nexport interface FunctionCall {\n  reducer: string;\n  args: string;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/logger.ts",
    "content": "import { stringify as ssStringify } from 'safe-stable-stringify';\nimport { u128ToHexString, u256ToHexString } from '../lib/util';\nexport type LogLevel = 'info' | 'warn' | 'error' | 'debug' | 'trace';\n\nconst LogLevelIdentifierIcon = {\n  component: '📦',\n  info: 'ℹ️',\n  warn: '⚠️',\n  error: '❌',\n  debug: '🐛',\n  trace: '🔍',\n};\n\nconst LogStyle = {\n  component:\n    'color: #fff; background-color: #8D6FDD; padding: 2px 5px; border-radius: 3px;',\n  info: 'color: #fff; background-color: #007bff; padding: 2px 5px; border-radius: 3px;',\n  warn: 'color: #fff; background-color: #ffc107; padding: 2px 5px; border-radius: 3px;',\n  error:\n    'color: #fff; background-color: #dc3545; padding: 2px 5px; border-radius: 3px;',\n  debug:\n    'color: #fff; background-color: #28a745; padding: 2px 5px; border-radius: 3px;',\n  trace:\n    'color: #fff; background-color: #17a2b8; padding: 2px 5px; border-radius: 3px;',\n};\n\nconst LogTextStyle = {\n  component: 'color: #8D6FDD;',\n  info: 'color: #007bff;',\n  warn: 'color: #ffc107;',\n  error: 'color: #dc3545;',\n  debug: 'color: #28a745;',\n  trace: 'color: #17a2b8;',\n};\n\nconst LogLevelRank: Record<LogLevel, number> = {\n  error: 0,\n  warn: 1,\n  info: 2,\n  debug: 3,\n  trace: 4,\n};\n\nlet globalLogLevel: LogLevel = 'info';\n\nexport const setGlobalLogLevel = (level: LogLevel): void => {\n  globalLogLevel = level;\n};\n\nexport const getGlobalLogLevel = (): LogLevel => globalLogLevel;\n\nconst shouldLog = (level: LogLevel): boolean =>\n  LogLevelRank[level] <= LogLevelRank[globalLogLevel];\n\n// Lazy can be a function or the actual thing, so we can make verbose logs cheap when disabled.\ntype Lazy<T> = T | (() => T);\nconst resolveLazy = <T>(v: Lazy<T>): T =>\n  typeof v === 'function' ? (v as () => T)() : v;\n\nconst toHex = (bytes: Uint8Array): string =>\n  Array.from(bytes)\n    .map(b => b.toString(16).padStart(2, '0'))\n    .join('');\nconst ARRAY_TRUNCATION_THRESHOLD = 25;\nconst ARRAY_PREVIEW_COUNT = 10;\n\nconst SENSITIVE_KEYS = new Set([\n  'token',\n  'authToken',\n  'authorization',\n  'accessToken',\n  'refreshToken',\n]);\n\nexport const stringify = (value: unknown): string | undefined =>\n  ssStringify(value, (key, current) => {\n    if (SENSITIVE_KEYS.has(key)) {\n      return '[REDACTED]';\n    }\n    if (\n      current &&\n      typeof current === 'object' &&\n      '__identity__' in current &&\n      typeof (current as { __identity__: unknown }).__identity__ === 'bigint'\n    ) {\n      return u256ToHexString(\n        (current as { __identity__: bigint }).__identity__\n      );\n    }\n    if (\n      current &&\n      typeof current === 'object' &&\n      '__connection_id__' in current &&\n      typeof (current as { __connection_id__: unknown }).__connection_id__ ===\n        'bigint'\n    ) {\n      return u128ToHexString(\n        (current as { __connection_id__: bigint }).__connection_id__\n      );\n    }\n    if (current instanceof Uint8Array) {\n      if (current.length < 25) {\n        return `0x${toHex(current)}`;\n      }\n      const head = current.subarray(0, 10);\n      return `Uint8Array(len=${current.length}, head=0x${toHex(head)})`;\n    }\n    if (\n      Array.isArray(current) &&\n      current.length >= ARRAY_TRUNCATION_THRESHOLD\n    ) {\n      const head = ssStringify(current.slice(0, ARRAY_PREVIEW_COUNT));\n      return `Array(len=${current.length}, head=${head ?? '[]'})`;\n    }\n    return current;\n  });\n\nexport const stdbLogger = (\n  level: LogLevel,\n  message: Lazy<any>,\n  ...args: Lazy<any>\n): void => {\n  if (!shouldLog(level)) {\n    return;\n  }\n  const resolvedMessage = resolveLazy(message);\n  const resolvedArgs = args.map(resolveLazy);\n  console.log(\n    `%c${LogLevelIdentifierIcon[level]} ${level.toUpperCase()}%c ${resolvedMessage}`,\n    LogStyle[level],\n    LogTextStyle[level],\n    ...resolvedArgs\n  );\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/message_types.ts",
    "content": "import type { TableUpdate } from './table_cache.ts';\nimport type { UntypedTableDef } from '../lib/table.ts';\nimport type { ReducerOutcome } from './client_api/types';\n\nexport type TransactionUpdateMessage = {\n  tag: 'TransactionUpdate';\n  tableUpdates: TableUpdate<UntypedTableDef>[];\n};\n\nexport type SubscribeAppliedMessage = {\n  tag: 'SubscribeApplied';\n  querySetId: number;\n  tableUpdates: TableUpdate<UntypedTableDef>[];\n};\n\nexport type UnsubscribeAppliedMessage = {\n  tag: 'UnsubscribeApplied';\n  querySetId: number;\n  tableUpdates: TableUpdate<UntypedTableDef>[];\n};\n\nexport type SubscriptionError = {\n  tag: 'SubscriptionError';\n  querySetId: number;\n  error: string;\n};\n\nexport type ReducerResultMessage = {\n  tag: 'ReducerResult';\n  requestId: number;\n  result: ReducerOutcome;\n};\n\nexport type ProcedureResultMessage = {\n  tag: 'ProcedureResult';\n  requestId: number;\n  result: { tag: 'Ok'; value: Uint8Array } | { tag: 'Err'; value: string };\n};\n\nexport type Message =\n  | TransactionUpdateMessage\n  | SubscribeAppliedMessage\n  | UnsubscribeAppliedMessage\n  | SubscriptionError\n  | ReducerResultMessage\n  | ProcedureResultMessage;\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/procedures.ts",
    "content": "import type { ParamsObj } from '../lib/reducers';\nimport type { Infer, InferTypeOfRow, TypeBuilder } from '../lib/type_builders';\nimport type { CamelCase } from '../lib/type_util';\nimport { coerceParams, toCamelCase, type CoerceParams } from '../lib/util';\nimport type { UntypedRemoteModule } from './spacetime_module';\n\n// Utility: detect 'any'\ntype IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;\n\n// Loose shape that allows all three families even when key names are unknown\ntype ProceduresViewLoose = {\n  // call: camelCase(name)\n  [k: string]: (params: any) => Promise<any>;\n};\n\nexport type ProceduresView<RemoteModule> = IfAny<\n  RemoteModule,\n  ProceduresViewLoose,\n  RemoteModule extends UntypedRemoteModule\n    ? // x: camelCase(name)\n      {\n        [K in RemoteModule['procedures'][number] as K['accessorName']]: (\n          params: InferTypeOfRow<K['params']>\n        ) => Promise<Infer<K['returnType']>>;\n      }\n    : never\n>;\n\nexport type UntypedProcedureDef = {\n  name: string;\n  accessorName: string;\n  params: CoerceParams<ParamsObj>;\n  returnType: TypeBuilder<any, any>;\n};\n\nexport type UntypedProceduresDef = {\n  procedures: readonly UntypedProcedureDef[];\n};\n\nexport function procedures<const H extends readonly UntypedProcedureDef[]>(\n  ...handles: H\n): { procedures: H };\n\nexport function procedures<const H extends readonly UntypedProcedureDef[]>(\n  handles: H\n): { procedures: H };\n\nexport function procedures<const H extends readonly UntypedProcedureDef[]>(\n  ...args: [H] | H\n): { procedures: H } {\n  const procedures = (\n    args.length === 1 && Array.isArray(args[0]) ? args[0] : args\n  ) as H;\n  return { procedures };\n}\n\ntype ProcedureDef<\n  Name extends string,\n  Params extends ParamsObj,\n  ReturnType extends TypeBuilder<any, any>,\n> = {\n  name: Name;\n  accessorName: CamelCase<Name>;\n  params: CoerceParams<Params>;\n  returnType: ReturnType;\n};\n\nexport function procedureSchema<\n  ProcedureName extends string,\n  Params extends ParamsObj,\n  ReturnType extends TypeBuilder<any, any>,\n>(\n  name: ProcedureName,\n  params: Params,\n  returnType: ReturnType\n): ProcedureDef<ProcedureName, Params, ReturnType> {\n  return {\n    name,\n    accessorName: toCamelCase(name),\n    params: coerceParams(params),\n    returnType,\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/reducer_event.ts",
    "content": "import { Timestamp } from '../';\nimport type { ReducerOutcome } from './client_api/types';\nimport type { ReducerEventInfo } from './reducers.ts';\n\nexport type ReducerEvent<Reducer extends ReducerEventInfo> = {\n  /**\n   * The time when the reducer started running.\n   *\n   * @internal This is a number and not Date, as JSON.stringify with date in it gives number, but JSON.parse of the same string does not give date. TO avoid\n   * confusion in typing we'll keep it a number\n   */\n  timestamp: Timestamp;\n\n  /**\n   * The reducer outcome, including optional return value and updates.\n   */\n  outcome: ReducerOutcome;\n\n  reducer: Reducer;\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/reducer_handle.ts",
    "content": "export type ReducerHandle<ReducerName extends string> = {\n  /** Phantom reducer name */\n  readonly reducerName?: ReducerName;\n};\n\nexport type ReducerNamesFromReducers<R> = R extends object\n  ? {\n      [K in keyof R]: R[K] extends ReducerHandle<infer ReducerName>\n        ? ReducerName\n        : never;\n    }[keyof R]\n  : never;\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/reducers.ts",
    "content": "import type { ProductType } from '../lib/algebraic_type';\nimport type { ReducerSchema } from '../lib/reducer_schema';\nimport type { ParamsObj } from '../lib/reducers';\nimport type { CoerceRow } from '../lib/table';\nimport { RowBuilder, type InferTypeOfRow } from '../lib/type_builders';\nimport { toCamelCase } from '../lib/util';\nimport type { SubscriptionEventContextInterface } from './event_context';\nimport type { UntypedRemoteModule } from './spacetime_module';\n\nexport type SubscriptionEventCallback<\n  RemoteModule extends UntypedRemoteModule,\n> = (ctx: SubscriptionEventContextInterface<RemoteModule>) => void;\n\n// Utility: detect 'any'\ntype IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;\n\n// Loose shape that allows all three families even when key names are unknown\ntype ReducersViewLoose = {\n  // call: camelCase(name)\n  [k: string]: (params: any) => Promise<void>;\n};\n\nexport type ReducersView<RemoteModule> = IfAny<\n  RemoteModule,\n  ReducersViewLoose,\n  RemoteModule extends UntypedRemoteModule\n    ? {\n        [K in RemoteModule['reducers'][number] as K['accessorName']]: (\n          params: InferTypeOfRow<K['params']>\n        ) => Promise<void>;\n      }\n    : never\n>;\n\nexport type ReducerEventInfo<\n  Name extends string = string,\n  Args extends object = object,\n> = {\n  name: Name;\n  args: Args;\n};\n\nexport type UntypedReducerDef = {\n  name: string;\n  accessorName: string;\n  params: CoerceRow<ParamsObj>;\n  paramsType: ProductType;\n};\n\nexport type UntypedReducersDef = {\n  reducers: readonly UntypedReducerDef[];\n};\n\nclass Reducers<ReducersDef extends UntypedReducersDef> {\n  reducersType: ReducersDef;\n\n  constructor(handles: readonly ReducerSchema<any, any>[]) {\n    this.reducersType = reducersToSchema(handles) as ReducersDef;\n  }\n}\n\n/**\n * Helper type to convert an array of TableSchema into a schema definition\n */\ntype ReducersToSchema<T extends readonly ReducerSchema<any, any>[]> = {\n  reducers: {\n    /** @type {UntypedReducerDef} */\n    readonly [i in keyof T]: {\n      name: T[i]['reducerName'];\n      accessorName: T[i]['accessorName'];\n      params: T[i]['params']['row'];\n      paramsType: T[i]['paramsSpacetimeType'];\n    };\n  };\n};\n\nexport function reducersToSchema<\n  const T extends readonly ReducerSchema<any, any>[],\n>(reducers: T): ReducersToSchema<T> {\n  const mapped = reducers.map(r => {\n    const paramsRow = r.params.row;\n\n    return {\n      name: r.reducerName,\n      // Prefer the schema's own accessorName if present at runtime; otherwise derive it.\n      accessorName: r.accessorName,\n      params: paramsRow,\n      paramsType: r.paramsSpacetimeType,\n    } as const;\n  }) as {\n    readonly [I in keyof T]: {\n      name: T[I]['reducerName'];\n      accessorName: T[I]['accessorName'];\n      params: T[I]['params']['row'];\n      paramsType: T[I]['paramsSpacetimeType'];\n    };\n  };\n\n  const result = { reducers: mapped } satisfies ReducersToSchema<T>;\n  return result;\n}\n\n/**\n * Creates a schema from table definitions\n * @param handles - Array of table handles created by table() function\n * @returns ColumnBuilder representing the complete database schema\n * @example\n * ```ts\n * const s = schema({\n *   user: table({}, userType),\n *   post: table({}, postType)\n * });\n * ```\n */\nexport function reducers<const H extends readonly ReducerSchema<any, any>[]>(\n  ...handles: H\n): Reducers<ReducersToSchema<H>>;\n\n/**\n * Creates a schema from table definitions (array overload)\n * @param handles - Array of table handles created by table() function\n * @returns ColumnBuilder representing the complete database schema\n */\nexport function reducers<const H extends readonly ReducerSchema<any, any>[]>(\n  handles: H\n): Reducers<ReducersToSchema<H>>;\n\nexport function reducers<const H extends readonly ReducerSchema<any, any>[]>(\n  ...args: [H] | H\n): Reducers<ReducersToSchema<H>> {\n  const handles = (\n    args.length === 1 && Array.isArray(args[0]) ? args[0] : args\n  ) as H;\n  return new Reducers(handles);\n}\n\nexport function reducerSchema<\n  ReducerName extends string,\n  Params extends ParamsObj,\n>(name: ReducerName, params: Params): ReducerSchema<ReducerName, Params> {\n  const paramType: ProductType = {\n    elements: Object.entries(params).map(([n, c]) => ({\n      name: n,\n      algebraicType:\n        'typeBuilder' in c ? c.typeBuilder.algebraicType : c.algebraicType,\n    })),\n  };\n  return {\n    reducerName: name,\n    accessorName: toCamelCase(name),\n    params: new RowBuilder<Params>(params),\n    paramsSpacetimeType: paramType,\n    reducerDef: {\n      name,\n      params: paramType,\n      lifecycle: undefined,\n    },\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/schema.ts",
    "content": "import {\n  ModuleContext,\n  tablesToSchema,\n  type TablesToSchema,\n  type UntypedSchemaDef,\n} from '../lib/schema';\nimport type { UntypedTableSchema } from '../lib/table_schema';\n\nclass Tables<S extends UntypedSchemaDef> {\n  constructor(readonly schemaType: S) {}\n}\n\n/**\n * Creates a schema from table definitions\n * @param handles - Array of table handles created by table() function\n * @returns ColumnBuilder representing the complete database schema\n * @example\n * ```ts\n * const spacetimedb = schema({\n *   user: table({}, userType),\n *   post: table({}, postType)\n * });\n * ```\n */\nexport function schema<const H extends Record<string, UntypedTableSchema>>(\n  tables: H\n): Tables<TablesToSchema<H>> {\n  const ctx = new ModuleContext();\n\n  return new Tables(tablesToSchema(ctx, tables));\n}\n\ntype HasAccessor = { accessorName: PropertyKey };\n\nexport type ConvertToAccessorMap<TableDefs extends readonly HasAccessor[]> = {\n  [Tbl in TableDefs[number] as Tbl['accessorName']]: Tbl;\n};\n\nexport function convertToAccessorMap<T extends readonly HasAccessor[]>(\n  arr: T\n): ConvertToAccessorMap<T> {\n  return Object.fromEntries(\n    arr.map(v => [v.accessorName, v])\n  ) as ConvertToAccessorMap<T>;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/spacetime_module.ts",
    "content": "import type { UntypedProceduresDef } from './procedures';\nimport type { UntypedSchemaDef } from '../lib/schema';\nimport type { UntypedReducersDef } from './reducers';\n\nexport type RemoteModule<\n  SchemaDef extends UntypedSchemaDef,\n  ReducersDef extends UntypedReducersDef,\n  ProceduresDef extends UntypedProceduresDef,\n  CLI extends string = string,\n> = SchemaDef &\n  ReducersDef &\n  ProceduresDef & {\n    versionInfo: {\n      cliVersion: CLI;\n    };\n  };\n\nexport type UntypedRemoteModule = RemoteModule<\n  UntypedSchemaDef,\n  UntypedReducersDef,\n  UntypedProceduresDef\n>;\n\nexport type SchemaDef<RemoteModule extends UntypedRemoteModule> =\n  RemoteModule['tables'];\n\nexport type ReducersDef<RemoteModule extends UntypedRemoteModule> =\n  RemoteModule['reducers'];\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/subscription_builder_impl.ts",
    "content": "import type { DbConnectionImpl } from './db_connection_impl';\nimport { INTERNAL_REMOTE_MODULE } from './internal';\nimport type {\n  ErrorContextInterface,\n  SubscriptionEventContextInterface,\n} from './event_context';\nimport { EventEmitter } from './event_emitter';\nimport type { UntypedRemoteModule } from './spacetime_module';\nimport { isRowTypedQuery, toSql, type RowTypedQuery } from '../lib/query';\nimport type { Values } from '../lib/type_util';\n\nexport class SubscriptionBuilderImpl<RemoteModule extends UntypedRemoteModule> {\n  #onApplied?: (ctx: SubscriptionEventContextInterface<RemoteModule>) => void =\n    undefined;\n  #onError?: (ctx: ErrorContextInterface<RemoteModule>) => void = undefined;\n  constructor(private db: DbConnectionImpl<RemoteModule>) {}\n\n  /**\n   * Registers `callback` to run when this query is successfully added to our subscribed set,\n   * I.e. when its `SubscriptionApplied` message is received.\n   *\n   * The database state exposed via the `&EventContext` argument\n   * includes all the rows added to the client cache as a result of the new subscription.\n   *\n   * The event in the `&EventContext` argument is `Event::SubscribeApplied`.\n   *\n   * Multiple `on_applied` callbacks for the same query may coexist.\n   * No mechanism for un-registering `on_applied` callbacks is exposed.\n   *\n   * @param cb - Callback to run when the subscription is applied.\n   * @returns The current `SubscriptionBuilder` instance.\n   */\n  onApplied(\n    cb: (ctx: SubscriptionEventContextInterface<RemoteModule>) => void\n  ): SubscriptionBuilderImpl<RemoteModule> {\n    this.#onApplied = cb;\n    return this;\n  }\n\n  /**\n   * Registers `callback` to run when this query either:\n   * - Fails to be added to our subscribed set.\n   * - Is unexpectedly removed from our subscribed set.\n   *\n   * If the subscription had previously started and has been unexpectedly removed,\n   * the database state exposed via the `&EventContext` argument contains no rows\n   * from any subscriptions removed within the same error event.\n   * As proposed, it must therefore contain no rows.\n   *\n   * The event in the `&EventContext` argument is `Event::SubscribeError`,\n   * containing a dynamic error object with a human-readable description of the error\n   * for diagnostic purposes.\n   *\n   * Multiple `on_error` callbacks for the same query may coexist.\n   * No mechanism for un-registering `on_error` callbacks is exposed.\n   *\n   * @param cb - Callback to run when there is an error in subscription.\n   * @returns The current `SubscriptionBuilder` instance.\n   */\n  onError(\n    cb: (ctx: ErrorContextInterface<RemoteModule>) => void\n  ): SubscriptionBuilderImpl<RemoteModule> {\n    this.#onError = cb;\n    return this;\n  }\n\n  /**\n   * Subscribe to a single query. The results of the query will be merged into the client\n   * cache and deduplicated on the client.\n   *\n   * @param query_sql A `SQL` query to subscribe to.\n   *\n   * @example\n   *\n   * ```ts\n   * const subscription = connection.subscriptionBuilder().onApplied(() => {\n   *   console.log(\"SDK client cache initialized.\");\n   * }).subscribe(\"SELECT * FROM User\");\n   *\n   * subscription.unsubscribe();\n   * ```\n   */\n  subscribe(\n    query_sql: string | RowTypedQuery<any, any>\n  ): SubscriptionHandleImpl<RemoteModule>;\n  subscribe(\n    query_sql: Array<string | RowTypedQuery<any, any>>\n  ): SubscriptionHandleImpl<RemoteModule>;\n  subscribe(\n    queryFn: (\n      tables: Values<RemoteModule['tables']>\n    ) => RowTypedQuery<any, any> | RowTypedQuery<any, any>[]\n  ): SubscriptionHandleImpl<RemoteModule>;\n  subscribe(\n    query_sql:\n      | string\n      | RowTypedQuery<any, any>\n      | Array<string | RowTypedQuery<any, any>>\n      | ((tables: any) => RowTypedQuery<any, any> | RowTypedQuery<any, any>[])\n  ): SubscriptionHandleImpl<RemoteModule> {\n    let queries: Array<string | RowTypedQuery<any, any>>;\n    if (typeof query_sql === 'function') {\n      const tablesMap = this.db.getTablesMap?.();\n      const result = query_sql(tablesMap);\n      queries = Array.isArray(result) ? result : [result];\n    } else {\n      queries = Array.isArray(query_sql) ? query_sql : [query_sql];\n    }\n    if (queries.length === 0) {\n      throw new Error('Subscriptions must have at least one query');\n    }\n    const queryStrings = queries.map(q => {\n      if (typeof q === 'string') return q;\n      if (isRowTypedQuery(q)) return toSql(q);\n      throw new Error('Subscriptions must be SQL strings or typed queries');\n    });\n    return new SubscriptionHandleImpl(\n      this.db,\n      queryStrings,\n      this.#onApplied,\n      this.#onError\n    );\n  }\n\n  /**\n   * Subscribes to all rows from all tables.\n   *\n   * This method is intended as a convenience\n   * for applications where client-side memory use and network bandwidth are not concerns.\n   * Applications where these resources are a constraint\n   * should register more precise queries via `subscribe`\n   * in order to replicate only the subset of data which the client needs to function.\n   *\n   * This method should not be combined with `subscribe` on the same `DbConnection`.\n   * A connection may either `subscribe` to particular queries,\n   * or `subscribeToAllTables`, but not both.\n   * Attempting to call `subscribe`\n   * on a `DbConnection` that has previously used `subscribeToAllTables`,\n   * or vice versa, may misbehave in any number of ways,\n   * including dropping subscriptions, corrupting the client cache, or throwing errors.\n   */\n  subscribeToAllTables(): void {\n    const remoteModule = this.db[INTERNAL_REMOTE_MODULE]();\n    const queries = Object.values(remoteModule.tables).map(\n      table => `SELECT * FROM ${table.sourceName}`\n    );\n    this.subscribe(queries);\n  }\n}\n\nexport type SubscribeEvent = 'applied' | 'error' | 'end';\n\nexport class SubscriptionManager<RemoteModule extends UntypedRemoteModule> {\n  subscriptions: Map<\n    number,\n    {\n      handle: SubscriptionHandleImpl<RemoteModule>;\n      emitter: EventEmitter<SubscribeEvent>;\n    }\n  > = new Map();\n}\n\nexport class SubscriptionHandleImpl<RemoteModule extends UntypedRemoteModule> {\n  #querySetId: number;\n  #unsubscribeCalled: boolean = false;\n  #endedState: boolean = false;\n  #activeState: boolean = false;\n  #emitter: EventEmitter<SubscribeEvent, (...args: any[]) => void> =\n    new EventEmitter();\n\n  constructor(\n    private db: DbConnectionImpl<RemoteModule>,\n    querySql: string[],\n    onApplied?: (ctx: SubscriptionEventContextInterface<RemoteModule>) => void,\n    onError?: (ctx: ErrorContextInterface<RemoteModule>, error: Error) => void\n  ) {\n    this.#emitter.on(\n      'applied',\n      (ctx: SubscriptionEventContextInterface<RemoteModule>) => {\n        this.#activeState = true;\n        if (onApplied) {\n          onApplied(ctx);\n        }\n      }\n    );\n    this.#emitter.on(\n      'error',\n      (ctx: ErrorContextInterface<RemoteModule>, error: Error) => {\n        this.#activeState = false;\n        this.#endedState = true;\n        if (onError) {\n          onError(ctx, error);\n        }\n      }\n    );\n    this.#querySetId = this.db.registerSubscription(\n      this,\n      this.#emitter,\n      querySql\n    );\n  }\n\n  /**\n   * Consumes self and issues an `Unsubscribe` message,\n   * removing this query from the client's set of subscribed queries.\n   * It is only valid to call this method if `is_active()` is `true`.\n   */\n  unsubscribe(): void {\n    if (this.#unsubscribeCalled) {\n      throw new Error('Unsubscribe has already been called');\n    }\n    this.#unsubscribeCalled = true;\n    this.db.unregisterSubscription(this.#querySetId);\n    this.#emitter.on(\n      'end',\n      (_ctx: SubscriptionEventContextInterface<RemoteModule>) => {\n        this.#endedState = true;\n        this.#activeState = false;\n      }\n    );\n  }\n\n  /**\n   * Unsubscribes and also registers a callback to run upon success.\n   * I.e. when an `UnsubscribeApplied` message is received.\n   *\n   * If `Unsubscribe` returns an error,\n   * or if the `on_error` callback(s) are invoked before this subscription would end normally,\n   * the `on_end` callback is not invoked.\n   *\n   * @param onEnd - Callback to run upon successful unsubscribe.\n   */\n  unsubscribeThen(\n    onEnd: (ctx: SubscriptionEventContextInterface<RemoteModule>) => void\n  ): void {\n    if (this.#endedState) {\n      throw new Error('Subscription has already ended');\n    }\n    if (this.#unsubscribeCalled) {\n      throw new Error('Unsubscribe has already been called');\n    }\n    this.#unsubscribeCalled = true;\n    this.db.unregisterSubscription(this.#querySetId);\n    this.#emitter.on(\n      'end',\n      (ctx: SubscriptionEventContextInterface<RemoteModule>) => {\n        this.#endedState = true;\n        this.#activeState = false;\n        onEnd(ctx);\n      }\n    );\n  }\n\n  /**\n   * True if this `SubscriptionHandle` has ended,\n   * either due to an error or a call to `unsubscribe`.\n   *\n   * This is initially false, and becomes true when either the `on_end` or `on_error` callback is invoked.\n   * A subscription which has not yet been applied is not active, but is also not ended.\n   */\n  isEnded(): boolean {\n    return this.#endedState;\n  }\n\n  /**\n   * True if this `SubscriptionHandle` is active, meaning it has been successfully applied\n   * and has not since ended, either due to an error or a complete `unsubscribe` request-response pair.\n   *\n   * This corresponds exactly to the interval bounded at the start by the `on_applied` callback\n   * and at the end by either the `on_end` or `on_error` callback.\n   */\n  isActive(): boolean {\n    return this.#activeState;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/table_cache.ts",
    "content": "import { EventEmitter } from './event_emitter.ts';\n\nimport { stdbLogger } from './logger.ts';\nimport { deepEqual, type ComparablePrimitive } from '../';\nimport type { EventContextInterface, TableDefForTableName } from './index.ts';\nimport type { RowType, TableIndexes, UntypedTableDef } from '../lib/table.ts';\nimport type { ClientTableCoreImplementable } from './client_table.ts';\nimport type { UntypedRemoteModule } from './spacetime_module.ts';\nimport type { TableNamesOf } from '../lib/schema.ts';\nimport type {\n  ReadonlyIndex,\n  ReadonlyIndexes,\n  ReadonlyRangedIndex,\n  ReadonlyUniqueIndex,\n} from '../lib/indexes.ts';\nimport type { Bound } from '../server/range.ts';\nimport type { Prettify } from '../lib/type_util.ts';\n\nexport type Operation<\n  RowType extends Record<string, any> = Record<string, any>,\n> = {\n  type: 'insert' | 'delete';\n  // For tables with a primary key, this is the primary key value, as a primitive or string.\n  // Otherwise, it is an encoding of the full row.\n  rowId: ComparablePrimitive;\n  row: RowType;\n};\n\nexport type TableUpdate<TableDef extends UntypedTableDef> = {\n  tableName: string;\n  operations: Operation<RowType<TableDef>>[];\n};\n\nexport type PendingCallback = {\n  type: 'insert' | 'delete' | 'update';\n  table: string;\n  cb: () => void;\n};\n\n// Strict scalar compare for index term values.\nconst scalarCompare = (x: any, y: any): number => {\n  if (x === y) return 0;\n  // Compare booleans/numbers/bigints/strings with JS ordering.\n  return x < y ? -1 : 1;\n};\n\nexport type TableIndexView<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = ReadonlyIndexes<\n  TableDefForTableName<RemoteModule, TableName>,\n  TableIndexes<TableDefForTableName<RemoteModule, TableName>>\n>;\n\nexport type TableCache<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> = TableCacheImpl<RemoteModule, TableName> &\n  TableIndexView<RemoteModule, TableName>;\n\n/**\n * Builder to generate calls to query a `table` in the database\n */\nexport class TableCacheImpl<\n  RemoteModule extends UntypedRemoteModule,\n  TableName extends TableNamesOf<RemoteModule>,\n> implements ClientTableCoreImplementable<RemoteModule, TableName>\n{\n  private rows: Map<\n    ComparablePrimitive,\n    [RowType<TableDefForTableName<RemoteModule, TableName>>, number]\n  >;\n  private tableDef: TableDefForTableName<RemoteModule, TableName>;\n  private emitter: EventEmitter<'insert' | 'delete' | 'update'>;\n\n  /**\n   * @param name the table name\n   * @param primaryKeyCol column index designated as `#[primarykey]`\n   * @param primaryKey column name designated as `#[primarykey]`\n   * @param entityClass the entityClass\n   */\n  constructor(tableDef: TableDefForTableName<RemoteModule, TableName>) {\n    this.tableDef = tableDef;\n    this.rows = new Map();\n    this.emitter = new EventEmitter();\n    // Build index views from the resolved runtime index metadata.\n    //\n    // We intentionally use `resolvedIndexes` rather than `indexes`:\n    // - `indexes` is declarative table-level config (`IndexOpts`) used mainly for typing.\n    // - `resolvedIndexes` is the runtime shape (`UntypedIndex`) that includes both\n    //   field-level and explicit table-level indexes.\n    for (const idxDef of this.tableDef.resolvedIndexes) {\n      const index = this.#makeReadonlyIndex(this.tableDef, idxDef);\n      // IMPORTANT: for duplicate accessor names, client cache uses assignment\n      // semantics and later entries overwrite earlier ones. This matches prior\n      // behavior and is intentionally different from server runtime merge logic.\n      (this as any)[idxDef.name] = index;\n    }\n  }\n\n  // TODO: this just scans the whole table; we should build proper index structures\n  #makeReadonlyIndex<\n    I extends TableDefForTableName<\n      RemoteModule,\n      TableName\n    >['resolvedIndexes'][number],\n  >(\n    tableDef: TableDefForTableName<RemoteModule, TableName>,\n    idx: I\n  ): ReadonlyIndex<TableDefForTableName<RemoteModule, TableName>, I> {\n    type TableDef = TableDefForTableName<RemoteModule, TableName>;\n    type Row = Prettify<RowType<TableDef>>;\n\n    // We do not yet support non-btree indexes\n    if (idx.algorithm !== 'btree') {\n      throw new Error('Only btree indexes are supported in TableCacheImpl');\n    }\n\n    const columns = idx.columns;\n\n    // Extract the tuple key for this btree index (column order preserved)\n    const getKey = (row: Row): readonly unknown[] => columns.map(c => row[c]);\n\n    // The server’s ranged scan fixes all prefix cols to equality and applies\n    // the bound only to the *last* term. We mirror that.\n    //\n    // rangeArg for multi-col index is:\n    //   [...prefixEqualValues, (lastTerm | Range<lastTerm>)]\n    //\n    // If only one element is provided, it’s the last term (scalar or Range).\n    const matchRange = (row: Row, rangeArg: any): boolean => {\n      const key = getKey(row);\n\n      // Normalize rangeArg into an array.\n      // With multi-col b-tree, IndexScanRangeBounds always yields at least one element.\n      const arr = Array.isArray(rangeArg) ? rangeArg : [rangeArg];\n\n      const prefixLen = Math.max(0, arr.length - 1);\n      // Check equality over the prefix (all but the last provided element)\n      for (let i = 0; i < prefixLen; i++) {\n        if (!deepEqual(key[i], arr[i])) return false;\n      }\n\n      const lastProvided = arr[arr.length - 1];\n      const kLast = key[prefixLen];\n\n      // If the last provided is a Range<T>, apply bounds; otherwise equality.\n      if (\n        lastProvided &&\n        typeof lastProvided === 'object' &&\n        'from' in lastProvided &&\n        'to' in lastProvided\n      ) {\n        // Range<T>\n        const from = lastProvided.from as Bound<any>;\n        const to = lastProvided.to as Bound<any>;\n\n        // Lower bound\n        if (from.tag !== 'unbounded') {\n          const c = scalarCompare(kLast, from.value);\n          if (c < 0) return false;\n          if (c === 0 && from.tag === 'excluded') return false;\n        }\n\n        // Upper bound\n        if (to.tag !== 'unbounded') {\n          const c = scalarCompare(kLast, to.value);\n          if (c > 0) return false;\n          if (c === 0 && to.tag === 'excluded') return false;\n        }\n\n        // All good on last term; any remaining columns (if any) are unconstrained,\n        // which matches server behavior for a prefix scan.\n        return true;\n      } else {\n        // Equality on the last provided element\n        if (!deepEqual(kLast, lastProvided)) return false;\n        // Any remaining columns are unconstrained (prefix equality only).\n        return true;\n      }\n    };\n\n    // An index is unique if it shares all columns with a unique constraint\n    const isUnique = tableDef.constraints.some(constraint => {\n      if (constraint.constraint !== 'unique') {\n        return false;\n      }\n      return deepEqual(constraint.columns, idx.columns);\n    });\n\n    // eslint-disable-next-line @typescript-eslint/no-this-alias\n    const self = this;\n    if (isUnique) {\n      const impl: ReadonlyUniqueIndex<TableDef, I> = {\n        find: (colVal: any): Row | null => {\n          // For unique btree, caller supplies the *full* key (tuple if multi-col).\n          const expected = Array.isArray(colVal) ? colVal : [colVal];\n          for (const row of self.iter()) {\n            if (deepEqual(getKey(row), expected)) return row;\n          }\n          return null;\n        },\n      };\n      return impl as ReadonlyIndex<TableDef, I>;\n    } else {\n      const impl: ReadonlyRangedIndex<TableDef, I> = {\n        *filter(range: any): IteratorObject<Row, undefined> {\n          for (const row of self.iter()) {\n            if (matchRange(row, range)) yield row;\n          }\n        },\n      };\n      return impl as ReadonlyIndex<TableDef, I>;\n    }\n  }\n\n  /**\n   * @returns number of rows in the table\n   */\n  count(): bigint {\n    return BigInt(this.rows.size);\n  }\n\n  /**\n   * @returns The values of the rows in the table\n   */\n  iter(): IteratorObject<\n    Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n    undefined\n  > {\n    function* generator(\n      rows: Map<\n        ComparablePrimitive,\n        [RowType<TableDefForTableName<RemoteModule, TableName>>, number]\n      >\n    ): IteratorObject<\n      Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n      undefined\n    > {\n      for (const [row] of rows.values()) {\n        yield row as Prettify<\n          RowType<TableDefForTableName<RemoteModule, TableName>>\n        >;\n      }\n    }\n    return generator(this.rows);\n  }\n\n  /**\n   * Allows iteration over the rows in the table\n   * @returns An iterator over the rows in the table\n   */\n  [Symbol.iterator](): IteratorObject<\n    Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n    undefined\n  > {\n    return this.iter();\n  }\n\n  applyOperations = (\n    operations: Operation<\n      RowType<TableDefForTableName<RemoteModule, TableName>>\n    >[],\n    ctx: EventContextInterface<RemoteModule>\n  ): PendingCallback[] => {\n    const pendingCallbacks: PendingCallback[] = [];\n\n    // Event tables: fire on_insert callbacks but don't store rows in the cache.\n    if (this.tableDef.isEvent) {\n      for (const op of operations) {\n        if (op.type === 'insert') {\n          pendingCallbacks.push({\n            type: 'insert',\n            table: this.tableDef.sourceName,\n            cb: () => {\n              this.emitter.emit('insert', ctx, op.row);\n            },\n          });\n        }\n      }\n      return pendingCallbacks;\n    }\n\n    // TODO: performance\n    const hasPrimaryKey = Object.values(this.tableDef.columns).some(\n      col => col.columnMetadata.isPrimaryKey === true\n    );\n    if (hasPrimaryKey) {\n      const insertMap = new Map<\n        ComparablePrimitive,\n        [\n          Operation<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n          number,\n        ]\n      >();\n      const deleteMap = new Map<\n        ComparablePrimitive,\n        [\n          Operation<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n          number,\n        ]\n      >();\n      for (const op of operations) {\n        if (op.type === 'insert') {\n          const [_, prevCount] = insertMap.get(op.rowId) || [op, 0];\n          insertMap.set(op.rowId, [op, prevCount + 1]);\n        } else {\n          const [_, prevCount] = deleteMap.get(op.rowId) || [op, 0];\n          deleteMap.set(op.rowId, [op, prevCount + 1]);\n        }\n      }\n      for (const [primaryKey, [insertOp, refCount]] of insertMap) {\n        const deleteEntry = deleteMap.get(primaryKey);\n        if (deleteEntry) {\n          const [_, deleteCount] = deleteEntry;\n          // In most cases the refCountDelta will be either 0 or refCount, but if\n          // an update moves a row in or out of the result set of different queries, then\n          // other deltas are possible.\n          const refCountDelta = refCount - deleteCount;\n          const maybeCb = this.update(\n            ctx,\n            primaryKey,\n            insertOp.row,\n            refCountDelta\n          );\n          if (maybeCb) {\n            pendingCallbacks.push(maybeCb);\n          }\n          deleteMap.delete(primaryKey);\n        } else {\n          const maybeCb = this.insert(ctx, insertOp, refCount);\n          if (maybeCb) {\n            pendingCallbacks.push(maybeCb);\n          }\n        }\n      }\n      for (const [deleteOp, refCount] of deleteMap.values()) {\n        const maybeCb = this.delete(ctx, deleteOp, refCount);\n        if (maybeCb) {\n          pendingCallbacks.push(maybeCb);\n        }\n      }\n    } else {\n      for (const op of operations) {\n        if (op.type === 'insert') {\n          const maybeCb = this.insert(ctx, op);\n          if (maybeCb) {\n            pendingCallbacks.push(maybeCb);\n          }\n        } else {\n          const maybeCb = this.delete(ctx, op);\n          if (maybeCb) {\n            pendingCallbacks.push(maybeCb);\n          }\n        }\n      }\n    }\n    return pendingCallbacks;\n  };\n\n  update = (\n    ctx: EventContextInterface<RemoteModule>,\n    rowId: ComparablePrimitive,\n    newRow: RowType<TableDefForTableName<RemoteModule, TableName>>,\n    refCountDelta: number = 0\n  ): PendingCallback | undefined => {\n    const existingEntry = this.rows.get(rowId);\n    if (!existingEntry) {\n      // TODO: this should throw an error and kill the connection.\n      stdbLogger(\n        'error',\n        `Updating a row that was not present in the cache. Table: ${this.tableDef.sourceName}, RowId: ${rowId}`\n      );\n      return undefined;\n    }\n    const [oldRow, previousCount] = existingEntry;\n    const refCount = Math.max(1, previousCount + refCountDelta);\n    if (previousCount + refCountDelta <= 0) {\n      stdbLogger(\n        'error',\n        `Negative reference count for in table ${this.tableDef.sourceName} row ${rowId} (${previousCount} + ${refCountDelta})`\n      );\n      return undefined;\n    }\n    this.rows.set(rowId, [newRow, refCount]);\n    // This indicates something is wrong, so we could arguably crash here.\n    if (previousCount === 0) {\n      stdbLogger(\n        'error',\n        `Updating a row id in table ${this.tableDef.sourceName} which was not present in the cache (rowId: ${rowId})`\n      );\n      return {\n        type: 'insert',\n        table: this.tableDef.sourceName,\n        cb: () => {\n          this.emitter.emit('insert', ctx, newRow);\n        },\n      };\n    }\n    return {\n      type: 'update',\n      table: this.tableDef.sourceName,\n      cb: () => {\n        this.emitter.emit('update', ctx, oldRow, newRow);\n      },\n    };\n  };\n\n  insert = (\n    ctx: EventContextInterface<RemoteModule>,\n    operation: Operation<\n      RowType<TableDefForTableName<RemoteModule, TableName>>\n    >,\n    count: number = 1\n  ): PendingCallback | undefined => {\n    const [_, previousCount] = this.rows.get(operation.rowId) || [\n      operation.row,\n      0,\n    ];\n    this.rows.set(operation.rowId, [operation.row, previousCount + count]);\n    if (previousCount === 0) {\n      return {\n        type: 'insert',\n        table: this.tableDef.sourceName,\n        cb: () => {\n          this.emitter.emit('insert', ctx, operation.row);\n        },\n      };\n    }\n    // It's possible to get a duplicate insert because rows can be returned from multiple queries.\n    return undefined;\n  };\n\n  delete = (\n    ctx: EventContextInterface<RemoteModule>,\n    operation: Operation<\n      RowType<TableDefForTableName<RemoteModule, TableName>>\n    >,\n    count: number = 1\n  ): PendingCallback | undefined => {\n    const [_, previousCount] = this.rows.get(operation.rowId) || [\n      operation.row,\n      0,\n    ];\n    // This should never happen.\n    if (previousCount === 0) {\n      stdbLogger('warn', 'Deleting a row that was not present in the cache');\n      return undefined;\n    }\n    // If this was the last reference, we are actually deleting the row.\n    if (previousCount <= count) {\n      // TODO: Log a warning/error if previousCount is less than count.\n      this.rows.delete(operation.rowId);\n      return {\n        type: 'delete',\n        table: this.tableDef.sourceName,\n        cb: () => {\n          this.emitter.emit('delete', ctx, operation.row);\n        },\n      };\n    }\n    this.rows.set(operation.rowId, [operation.row, previousCount - count]);\n    return undefined;\n  };\n\n  /**\n   * Register a callback for when a row is newly inserted into the database.\n   *\n   * ```ts\n   * ctx.db.user.onInsert((reducerEvent, user) => {\n   *   if (reducerEvent) {\n   *      console.log(\"New user on reducer\", reducerEvent, user);\n   *   } else {\n   *      console.log(\"New user received during subscription update on insert\", user);\n   *  }\n   * });\n   * ```\n   *\n   * @param cb Callback to be called when a new row is inserted\n   */\n  onInsert = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.on('insert', cb);\n  };\n\n  /**\n   * Register a callback for when a row is deleted from the database.\n   *\n   * ```ts\n   * ctx.db.user.onDelete((reducerEvent, user) => {\n   *   if (reducerEvent) {\n   *      console.log(\"Deleted user on reducer\", reducerEvent, user);\n   *   } else {\n   *      console.log(\"Deleted user received during subscription update on update\", user);\n   *  }\n   * });\n   * ```\n   *\n   * @param cb Callback to be called when a new row is inserted\n   */\n  onDelete = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.on('delete', cb);\n  };\n\n  /**\n   * Register a callback for when a row is updated into the database.\n   *\n   * ```ts\n   * ctx.db.user.onInsert((reducerEvent, oldUser, user) => {\n   *   if (reducerEvent) {\n   *      console.log(\"Updated user on reducer\", reducerEvent, user);\n   *   } else {\n   *      console.log(\"Updated user received during subscription update on delete\", user);\n   *  }\n   * });\n   * ```\n   *\n   * @param cb Callback to be called when a new row is inserted\n   */\n  onUpdate = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      oldRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.on('update', cb);\n  };\n\n  /**\n   * Remove a callback for when a row is newly inserted into the database.\n   *\n   * @param cb Callback to be removed\n   */\n  removeOnInsert = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.off('insert', cb);\n  };\n\n  /**\n   * Remove a callback for when a row is deleted from the database.\n   *\n   * @param cb Callback to be removed\n   */\n  removeOnDelete = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.off('delete', cb);\n  };\n\n  /**\n   * Remove a callback for when a row is updated into the database.\n   *\n   * @param cb Callback to be removed\n   */\n  removeOnUpdate = (\n    cb: (\n      ctx: EventContextInterface<RemoteModule>,\n      oldRow: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>,\n      row: Prettify<RowType<TableDefForTableName<RemoteModule, TableName>>>\n    ) => void\n  ): void => {\n    this.emitter.off('update', cb);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/type_utils.ts",
    "content": "import type { InferTypeOfRow } from '.';\nimport type { Prettify } from '../lib/type_util';\nimport type { UntypedReducerDef } from './reducers';\n\nexport type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;\nexport type MaybeParams<T> = IsEmptyObject<T> extends true ? [] : [params: T];\n\nexport type ParamsType<R extends UntypedReducerDef> = MaybeParams<\n  Prettify<InferTypeOfRow<R['params']>>\n>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/version.ts",
    "content": "export type PrereleaseId = string | number;\n\nexport type PreRelease = PrereleaseId[];\n\n// Compare pre-release identifiers according to the semver spec (https://semver.org/#spec-item-11).\nfunction comparePreReleases(a: PreRelease, b: PreRelease): number {\n  const len = Math.min(a.length, b.length);\n  for (let i = 0; i < len; i++) {\n    const aPart = a[i];\n    const bPart = b[i];\n    if (aPart === bPart) continue;\n    if (typeof aPart === 'number' && typeof bPart === 'number') {\n      return aPart - bPart;\n    }\n    if (typeof aPart === 'string' && typeof bPart === 'string') {\n      return aPart.localeCompare(bPart);\n    }\n    // According to item 11.4.3, numeric identifiers always have lower precedence than non-numeric identifiers.\n    // So if `a` is a string, it has higher precedence than `b`.\n    return typeof aPart === 'string' ? 1 : -1;\n  }\n  // See rule 11.4.4 in the semver spec.\n  return a.length - b.length;\n}\n\n// We don't use these, and they don't matter for version ordering, so I'm not going to parse it to spec.\nexport type BuildInfo = string;\n\n// This is exported for tests.\nexport class SemanticVersion {\n  major: number;\n  minor: number;\n  patch: number;\n  preRelease: PreRelease | null;\n  buildInfo: BuildInfo | null;\n\n  constructor(\n    major: number,\n    minor: number,\n    patch: number,\n    preRelease: PreRelease | null = null,\n    buildInfo: BuildInfo | null = null\n  ) {\n    this.major = major;\n    this.minor = minor;\n    this.patch = patch;\n    this.preRelease = preRelease;\n    this.buildInfo = buildInfo;\n  }\n\n  toString(): string {\n    let versionString = `${this.major}.${this.minor}.${this.patch}`;\n    if (this.preRelease) {\n      versionString += `-${this.preRelease.join('.')}`;\n    }\n    if (this.buildInfo) {\n      versionString += `+${this.buildInfo}`;\n    }\n    return versionString;\n  }\n\n  compare(other: SemanticVersion): number {\n    if (this.major !== other.major) {\n      return this.major - other.major;\n    }\n    if (this.minor !== other.minor) {\n      return this.minor - other.minor;\n    }\n    if (this.patch !== other.patch) {\n      return this.patch - other.patch;\n    }\n    if (this.preRelease && other.preRelease) {\n      return comparePreReleases(this.preRelease, other.preRelease);\n    }\n    if (this.preRelease) {\n      return -1; // The version without a pre-release is greater.\n    }\n    if (other.preRelease) {\n      return -1; // Since we don't have a pre-release, this version is greater.\n    }\n    return 0; // versions are equal\n  }\n\n  clone(): SemanticVersion {\n    return new SemanticVersion(\n      this.major,\n      this.minor,\n      this.patch,\n      this.preRelease ? [...this.preRelease] : null,\n      this.buildInfo\n    );\n  }\n\n  static parseVersionString(version: string): SemanticVersion {\n    const regex =\n      /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([\\da-zA-Z-]+(?:\\.[\\da-zA-Z-]+)*))?(?:\\+([\\da-zA-Z-]+(?:\\.[\\da-zA-Z-]+)*))?$/;\n    const match = version.match(regex);\n    if (!match) {\n      throw new Error(`Invalid version string: ${version}`);\n    }\n\n    const major = parseInt(match[1], 10);\n    const minor = parseInt(match[2], 10);\n    const patch = parseInt(match[3], 10);\n    const preRelease = match[4]\n      ? match[4].split('.').map(id => (isNaN(Number(id)) ? id : Number(id)))\n      : null;\n    const buildInfo = match[5] || null;\n\n    return new SemanticVersion(major, minor, patch, preRelease, buildInfo);\n  }\n}\n\n// The SDK depends on some module information that was not generated before this version.\nexport const _MINIMUM_CLI_VERSION: SemanticVersion = new SemanticVersion(\n  1,\n  4,\n  0\n);\n\nexport function ensureMinimumVersionOrThrow(versionString?: string): void {\n  if (versionString === undefined) {\n    throw new Error(versionErrorMessage(versionString));\n  }\n  const version = SemanticVersion.parseVersionString(versionString);\n  if (version.compare(_MINIMUM_CLI_VERSION) < 0) {\n    throw new Error(versionErrorMessage(versionString));\n  }\n}\n\nfunction versionErrorMessage(incompatibleVersion?: string): string {\n  return `Module code was generated with an incompatible version of the spacetimedb cli (${incompatibleVersion}). Update the cli version to at least ${_MINIMUM_CLI_VERSION.toString()} and regenerate the bindings. You can upgrade to the latest cli version by running: spacetime version upgrade`;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/websocket_decompress_adapter.ts",
    "content": "import { decompress } from './decompress';\nimport { resolveWS } from './ws';\n\nexport interface WebsocketAdapter {\n  send(msg: Uint8Array): void;\n  close(): void;\n\n  set onclose(handler: (ev: CloseEvent) => void);\n  set onopen(handler: () => void);\n  set onmessage(handler: (msg: { data: Uint8Array }) => void);\n  set onerror(handler: (msg: ErrorEvent) => void);\n}\n\nexport class WebsocketDecompressAdapter implements WebsocketAdapter {\n  set onclose(handler: (ev: CloseEvent) => void) {\n    this.#ws.onclose = handler;\n  }\n  set onopen(handler: () => void) {\n    this.#ws.onopen = handler;\n  }\n  set onmessage(handler: (msg: { data: Uint8Array }) => void) {\n    this.#ws.onmessage = async (msg: MessageEvent<ArrayBuffer>) => {\n      const data = await this.#decompress(new Uint8Array(msg.data));\n      handler({ data });\n    };\n  }\n  set onerror(handler: (msg: ErrorEvent) => void) {\n    this.#ws.onerror = handler as (msg: Event) => void;\n  }\n\n  #ws: WebSocket;\n\n  async #decompress(buffer: Uint8Array): Promise<Uint8Array> {\n    const tag = buffer[0];\n    const data = buffer.subarray(1);\n    switch (tag) {\n      case 0:\n        return data;\n      case 1:\n        throw new Error(\n          'Brotli Compression not supported. Please use gzip or none compression in withCompression method on DbConnection.'\n        );\n      case 2:\n        return await decompress(data, 'gzip');\n      default:\n        throw new Error(\n          'Unexpected Compression Algorithm. Please use `gzip` or `none`'\n        );\n    }\n  }\n\n  send(msg: Uint8Array): void {\n    this.#ws.send(msg);\n  }\n\n  close(): void {\n    this.#ws.close();\n  }\n\n  constructor(ws: WebSocket) {\n    ws.binaryType = 'arraybuffer';\n\n    this.#ws = ws;\n  }\n\n  static async createWebSocketFn({\n    url,\n    nameOrAddress,\n    wsProtocol,\n    authToken,\n    compression,\n    lightMode,\n    confirmedReads,\n  }: {\n    url: URL;\n    wsProtocol: string;\n    nameOrAddress: string;\n    authToken?: string;\n    compression: 'gzip' | 'none';\n    lightMode: boolean;\n    confirmedReads?: boolean;\n  }): Promise<WebsocketDecompressAdapter> {\n    const headers = new Headers();\n\n    const WS = await resolveWS();\n\n    // We swap our original token to a shorter-lived token\n    // to avoid sending the original via query params.\n    let temporaryAuthToken: string | undefined = undefined;\n    if (authToken) {\n      headers.set('Authorization', `Bearer ${authToken}`);\n      const tokenUrl = new URL('v1/identity/websocket-token', url);\n      tokenUrl.protocol = url.protocol === 'wss:' ? 'https:' : 'http:';\n\n      const response = await fetch(tokenUrl, { method: 'POST', headers });\n      if (response.ok) {\n        const { token } = await response.json();\n        temporaryAuthToken = token;\n      } else {\n        return Promise.reject(\n          new Error(`Failed to verify token: ${response.statusText}`)\n        );\n      }\n    }\n\n    const databaseUrl = new URL(`v1/database/${nameOrAddress}/subscribe`, url);\n    if (temporaryAuthToken) {\n      databaseUrl.searchParams.set('token', temporaryAuthToken);\n    }\n    databaseUrl.searchParams.set(\n      'compression',\n      compression === 'gzip' ? 'Gzip' : 'None'\n    );\n    if (lightMode) {\n      databaseUrl.searchParams.set('light', 'true');\n    }\n    if (confirmedReads !== undefined) {\n      databaseUrl.searchParams.set('confirmed', confirmedReads.toString());\n    }\n\n    const ws = new WS(databaseUrl.toString(), wsProtocol);\n\n    return new WebsocketDecompressAdapter(ws);\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/websocket_test_adapter.ts",
    "content": "import { BinaryReader, BinaryWriter } from '../';\nimport { ClientMessage, ServerMessage } from './client_api/types';\nimport type { WebsocketAdapter } from './websocket_decompress_adapter';\n\nclass WebsocketTestAdapter implements WebsocketAdapter {\n  onclose: any;\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n  onopen!: () => void;\n  onmessage: any;\n  onerror: any;\n\n  messageQueue: any[];\n  outgoingMessages: ClientMessage[];\n  closed: boolean;\n\n  constructor() {\n    this.messageQueue = [];\n    this.outgoingMessages = [];\n    this.closed = false;\n  }\n\n  send(message: any): void {\n    const parsedMessage = ClientMessage.deserialize(new BinaryReader(message));\n    this.outgoingMessages.push(parsedMessage);\n    // console.ClientMessageSerde.deserialize(message);\n    this.messageQueue.push(message);\n  }\n\n  close(): void {\n    this.closed = true;\n    this.onclose?.({ code: 1000, reason: 'normal closure', wasClean: true });\n  }\n\n  acceptConnection(): void {\n    this.onopen();\n  }\n\n  sendToClient(message: ServerMessage): void {\n    const writer = new BinaryWriter(1024);\n    ServerMessage.serialize(writer, message);\n    const rawBytes = writer.getBuffer();\n    // The brotli library's `compress` is somehow broken: it returns `null` for some inputs.\n    // See https://github.com/foliojs/brotli.js/issues/36, which is closed but not actually fixed.\n    // So we send the uncompressed data here, and in `spacetimedb.ts`,\n    // if compression fails, we treat the raw message as having been uncompressed all along.\n    // const data = compress(rawBytes);\n    this.onmessage({ data: rawBytes });\n  }\n\n  async createWebSocketFn(_args: {\n    url: URL;\n    wsProtocol: string;\n    nameOrAddress: string;\n    authToken?: string;\n    compression: 'gzip' | 'none';\n    lightMode: boolean;\n    confirmedReads?: boolean;\n  }): Promise<WebsocketTestAdapter> {\n    return this;\n  }\n}\n\nexport type { WebsocketTestAdapter };\nexport default WebsocketTestAdapter;\n"
  },
  {
    "path": "crates/bindings-typescript/src/sdk/ws.ts",
    "content": "import { stdbLogger } from './logger';\n\nexport async function resolveWS(): Promise<typeof WebSocket> {\n  // Browser or Node >= 22 (or any env that exposes global WebSocket)\n  if (typeof (globalThis as any).WebSocket !== 'undefined') {\n    return (globalThis as any).WebSocket as typeof WebSocket;\n  }\n\n  // Node without a global WebSocket: lazily load undici's polyfill.\n  // Use an unstatable dynamic import so bundlers don't prebundle it.\n  const dynamicImport = new Function('m', 'return import(m)') as (\n    m: string\n  ) => Promise<any>;\n\n  try {\n    const { WebSocket: UndiciWS } = await dynamicImport('undici');\n    return UndiciWS as unknown as typeof WebSocket;\n  } catch (err) {\n    stdbLogger(\n      'warn',\n      '[spacetimedb-sdk] No global WebSocket found. ' +\n        'On Node 18–21, please install `undici` (npm install undici) ' +\n        'to enable WebSocket support.'\n    );\n    throw err;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/console.ts",
    "content": "import type { u32 } from 'spacetime:sys@2.0';\nimport { sys } from './runtime';\nimport inspect from 'object-inspect';\n\nconst fmtLog = (...data: any[]) =>\n  data.map(x => (typeof x === 'string' ? x : inspect(x))).join(' ');\n\nconst console_level_error = 0;\nconst console_level_warn = 1;\nconst console_level_info = 2;\nconst console_level_debug = 3;\nconst console_level_trace = 4;\nconst _console_level_panic = 101;\n\nconst timerMap = new Map<string, u32>();\n\nexport const console: Console = {\n  // @ts-expect-error we want a blank prototype, but typescript complains\n  __proto__: {},\n  [Symbol.toStringTag]: 'console',\n  assert: (condition = false, ...data: any[]) => {\n    if (!condition) {\n      sys.console_log(console_level_error, fmtLog(...data));\n    }\n  },\n  clear: () => {},\n  debug: (...data: any[]) => {\n    sys.console_log(console_level_debug, fmtLog(...data));\n  },\n  error: (...data: any[]) => {\n    sys.console_log(console_level_error, fmtLog(...data));\n  },\n  info: (...data: any[]) => {\n    sys.console_log(console_level_info, fmtLog(...data));\n  },\n  log: (...data: any[]) => {\n    sys.console_log(console_level_info, fmtLog(...data));\n  },\n  table: (tabularData: any, _properties: any) => {\n    sys.console_log(console_level_info, fmtLog(tabularData));\n  },\n  trace: (...data: any[]) => {\n    sys.console_log(console_level_trace, fmtLog(...data));\n  },\n  warn: (...data: any[]) => {\n    sys.console_log(console_level_warn, fmtLog(...data));\n  },\n  dir: (_item: any, _options: any) => {},\n  dirxml: (..._data: any[]) => {},\n  // Counting\n  count: (_label = 'default') => {},\n  countReset: (_label = 'default') => {},\n  // Grouping\n  group: (..._data: any[]) => {},\n  groupCollapsed: (..._data: any[]) => {},\n  groupEnd: () => {},\n  // Timing\n  time: (label = 'default') => {\n    if (timerMap.has(label)) {\n      sys.console_log(console_level_warn, `Timer '${label}' already exists.`);\n      return;\n    }\n    timerMap.set(label, sys.console_timer_start(label));\n  },\n  timeLog: (label = 'default', ...data: any[]) => {\n    sys.console_log(console_level_info, fmtLog(label, ...data));\n  },\n  timeEnd: (label = 'default') => {\n    const spanId = timerMap.get(label);\n    if (spanId === undefined) {\n      sys.console_log(console_level_warn, `Timer '${label}' does not exist.`);\n      return;\n    }\n    sys.console_timer_end(spanId);\n    timerMap.delete(label);\n  },\n  // Additional console methods to satisfy the Console interface\n  timeStamp: () => {},\n  profile: () => {},\n  profileEnd: () => {},\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/db_view.ts",
    "content": "import type { UntypedSchemaDef } from '../lib/schema';\nimport type { ReadonlyTable, Table } from '../lib/table';\nimport type { Values } from '../lib/type_util';\n\n/**\n * A type representing a read-only database view, mapping table names to their corresponding read-only Table handles.\n */\nexport type ReadonlyDbView<SchemaDef extends UntypedSchemaDef> = {\n  readonly [Tbl in Values<\n    SchemaDef['tables']\n  > as Tbl['accessorName']]: ReadonlyTable<Tbl>;\n};\n\n/**\n * A type representing the database view, mapping table names to their corresponding Table handles.\n */\nexport type DbView<SchemaDef extends UntypedSchemaDef> = {\n  readonly [Tbl in Values<\n    SchemaDef['tables']\n  > as Tbl['accessorName']]: Table<Tbl>;\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/errors.ts",
    "content": "import { SenderError } from '../lib/errors';\n\n/**\n * Base class for all Spacetime host errors (i.e. errors that may be thrown\n * by database functions).\n */\nexport class SpacetimeHostError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n  get name(): string {\n    return 'SpacetimeHostError';\n  }\n}\n\nexport { SenderError };\n\nconst errorData = {\n  /**\n   * A generic error class for unknown error codes.\n   */\n  HostCallFailure: 1,\n\n  /**\n   * Error indicating that an ABI call was made outside of a transaction.\n   */\n  NotInTransaction: 2,\n\n  /**\n   * Error indicating that BSATN decoding failed.\n   * This typically means that the data could not be decoded to the expected type.\n   */\n  BsatnDecodeError: 3,\n\n  /**\n   * Error indicating that a specified table does not exist.\n   */\n  NoSuchTable: 4,\n\n  /**\n   * Error indicating that a specified index does not exist.\n   */\n  NoSuchIndex: 5,\n\n  /**\n   * Error indicating that a specified row iterator is not valid.\n   */\n  NoSuchIter: 6,\n\n  /**\n   * Error indicating that a specified console timer does not exist.\n   */\n  NoSuchConsoleTimer: 7,\n\n  /**\n   * Error indicating that a specified bytes source or sink is not valid.\n   */\n  NoSuchBytes: 8,\n\n  /**\n   * Error indicating that a provided sink has no more space left.\n   */\n  NoSpace: 9,\n\n  /**\n   * Error indicating that there is no more space in the database.\n   */\n  BufferTooSmall: 11,\n\n  /**\n   * Error indicating that a value with a given unique identifier already exists.\n   */\n  UniqueAlreadyExists: 12,\n\n  /**\n   * Error indicating that the specified delay in scheduling a row was too long.\n   */\n  ScheduleAtDelayTooLong: 13,\n\n  /**\n   * Error indicating that an index was not unique when it was expected to be.\n   */\n  IndexNotUnique: 14,\n\n  /**\n   * Error indicating that an index was not unique when it was expected to be.\n   */\n  NoSuchRow: 15,\n\n  /**\n   * Error indicating that an auto-increment sequence has overflowed.\n   */\n  AutoIncOverflow: 16,\n\n  WouldBlockTransaction: 17,\n\n  TransactionNotAnonymous: 18,\n\n  TransactionIsReadOnly: 19,\n\n  TransactionIsMut: 20,\n\n  HttpError: 21,\n};\n\nfunction mapEntries<const T extends Record<string, any>, U>(\n  x: T,\n  f: (key: keyof T, value: T[keyof T]) => U\n): { [k in keyof T]: U } {\n  return Object.fromEntries(\n    Object.entries(x).map(([k, v]) => [k, f(k, v)])\n  ) as any;\n}\n\n/**\n * Map from error codes to their corresponding SpacetimeError subclass.\n */\nconst errnoToClass = new Map<number, new (msg: string) => Error>();\n\nexport const errors = Object.freeze(\n  mapEntries(errorData, (name, code) => {\n    const cls = Object.defineProperty(\n      class extends SpacetimeHostError {\n        get name() {\n          return name;\n        }\n      },\n      'name',\n      { value: name, writable: false }\n    );\n    errnoToClass.set(code, cls);\n    return cls;\n  })\n);\n\nexport function getErrorConstructor(code: number): new (msg: string) => Error {\n  return errnoToClass.get(code) ?? SpacetimeHostError;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/http.ts",
    "content": "export { Headers, SyncResponse } from './http_internal';\nexport type { BodyInit, HeadersInit, ResponseInit } from './http_internal';\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/http_internal.ts",
    "content": "import { Headers, headersToList } from 'headers-polyfill';\nimport status from 'statuses';\nimport BinaryReader from '../lib/binary_reader';\nimport BinaryWriter from '../lib/binary_writer';\nimport {\n  HttpHeaders,\n  HttpMethod,\n  HttpRequest,\n  HttpResponse,\n} from '../lib/http_types';\nimport type { TimeDuration } from '../lib/time_duration';\nimport { bsatnBaseSize } from '../lib/util';\nimport { sys } from './runtime';\n\nexport { Headers };\n\nconst { freeze } = Object;\n\nexport type BodyInit = ArrayBuffer | ArrayBufferView | string;\nexport type HeadersInit = [string, string][] | Record<string, string> | Headers;\nexport interface ResponseInit {\n  headers?: HeadersInit;\n  status?: number;\n  statusText?: string;\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder('utf-8' /* { fatal: true } */);\n\nconst makeResponse = Symbol('makeResponse');\n\n// based on deno's type of the same name\ninterface InnerResponse {\n  type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect';\n  url: string | null;\n  status: number;\n  statusText: string;\n  headers: Headers;\n  aborted: boolean;\n}\n\nexport class SyncResponse {\n  #body: string | ArrayBuffer | null;\n  #inner: InnerResponse;\n\n  constructor(body?: BodyInit | null, init?: ResponseInit) {\n    if (body == null) {\n      this.#body = null;\n    } else if (typeof body === 'string') {\n      this.#body = body;\n    } else {\n      // this call is fine, the typings are just weird\n      this.#body = new Uint8Array<ArrayBuffer>(body as any).buffer;\n    }\n\n    // there's a type mismatch - headers-polyfill's typing doesn't expect its\n    // own `Headers` type, even though the actual code handles it correctly.\n    this.#inner = {\n      headers: new Headers(init?.headers as any),\n      status: init?.status ?? 200,\n      statusText: init?.statusText ?? '',\n      type: 'default',\n      url: null,\n      aborted: false,\n    };\n  }\n\n  static [makeResponse](body: BodyInit | null, inner: InnerResponse) {\n    const me = new SyncResponse(body);\n    me.#inner = inner;\n    return me;\n  }\n\n  get headers(): Headers {\n    return this.#inner.headers;\n  }\n  get status(): number {\n    return this.#inner.status;\n  }\n  get statusText() {\n    return this.#inner.statusText;\n  }\n  get ok(): boolean {\n    return 200 <= this.#inner.status && this.#inner.status <= 299;\n  }\n  get url(): string {\n    return this.#inner.url ?? '';\n  }\n  get type() {\n    return this.#inner.type;\n  }\n\n  arrayBuffer(): ArrayBuffer {\n    return this.bytes().buffer;\n  }\n\n  bytes(): Uint8Array<ArrayBuffer> {\n    if (this.#body == null) {\n      return new Uint8Array();\n    } else if (typeof this.#body === 'string') {\n      return textEncoder.encode(this.#body);\n    } else {\n      return new Uint8Array(this.#body);\n    }\n  }\n\n  json(): any {\n    return JSON.parse(this.text());\n  }\n\n  text(): string {\n    if (this.#body == null) {\n      return '';\n    } else if (typeof this.#body === 'string') {\n      return this.#body;\n    } else {\n      return textDecoder.decode(this.#body);\n    }\n  }\n}\n\nexport interface RequestOptions {\n  /** A BodyInit object or null to set request's body. */\n  body?: BodyInit | null;\n  /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */\n  headers?: HeadersInit;\n  /** A string to set request's method. */\n  method?: string;\n  /** A duration, after which the request will timeout */\n  timeout?: TimeDuration;\n  // /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */\n  // redirect?: RequestRedirect;\n}\n\nexport interface HttpClient {\n  fetch(url: URL | string, init?: RequestOptions): SyncResponse;\n}\n\nconst requestBaseSize = bsatnBaseSize({ types: [] }, HttpRequest.algebraicType);\n\nconst methods = new Map<string, HttpMethod>([\n  ['GET', { tag: 'Get' }],\n  ['HEAD', { tag: 'Head' }],\n  ['POST', { tag: 'Post' }],\n  ['PUT', { tag: 'Put' }],\n  ['DELETE', { tag: 'Delete' }],\n  ['CONNECT', { tag: 'Connect' }],\n  ['OPTIONS', { tag: 'Options' }],\n  ['TRACE', { tag: 'Trace' }],\n  ['PATCH', { tag: 'Patch' }],\n]);\n\nfunction fetch(url: URL | string, init: RequestOptions = {}) {\n  const method = methods.get(init.method?.toUpperCase() ?? 'GET') ?? {\n    tag: 'Extension',\n    value: init.method!,\n  };\n  const headers: HttpHeaders = {\n    // anys because the typings are wonky - see comment in SyncResponse.constructor\n    entries: headersToList(new Headers(init.headers as any) as any)\n      .flatMap(([k, v]) => (Array.isArray(v) ? v.map(v => [k, v]) : [[k, v]]))\n      .map(([name, value]) => ({ name, value: textEncoder.encode(value) })),\n  };\n  const uri = '' + url;\n  const request: HttpRequest = freeze({\n    method,\n    headers,\n    timeout: init.timeout,\n    uri,\n    version: { tag: 'Http11' } as const,\n  });\n  const requestBuf = new BinaryWriter(requestBaseSize);\n  HttpRequest.serialize(requestBuf, request);\n  const body =\n    init.body == null\n      ? new Uint8Array()\n      : typeof init.body === 'string'\n        ? init.body\n        : new Uint8Array<ArrayBuffer>(init.body as any);\n  const [responseBuf, responseBody] = sys.procedure_http_request(\n    requestBuf.getBuffer(),\n    body\n  );\n  const response = HttpResponse.deserialize(new BinaryReader(responseBuf));\n  return SyncResponse[makeResponse](responseBody, {\n    type: 'basic',\n    url: uri,\n    status: response.code,\n    statusText: status(response.code),\n    headers: new Headers(),\n    aborted: false,\n  });\n}\n\nfreeze(fetch);\n\nexport const httpClient: HttpClient = freeze({ fetch });\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/index.ts",
    "content": "export * from '../lib/type_builders';\nexport {\n  schema,\n  type InferSchema,\n  type ModuleExport,\n  type ModuleSettings,\n} from './schema';\nexport { CaseConversionPolicy } from '../lib/autogen/types';\nexport { table } from '../lib/table';\nexport { SenderError, SpacetimeHostError, errors } from './errors';\nexport type { Reducer, ReducerCtx } from '../lib/reducers';\nexport type { ReducerExport } from './reducers';\nexport { type DbView } from './db_view';\nexport * from './query';\nexport type {\n  ProcedureCtx,\n  TransactionCtx,\n  ProcedureExport,\n} from './procedures';\nexport { toCamelCase } from '../lib/util';\nexport type { Uuid } from '../lib/uuid';\nexport type { Random } from './rng';\nexport type { ViewExport, ViewCtx, AnonymousViewCtx } from './views';\nexport { Range, type Bound } from './range';\n\nimport './polyfills'; // Ensure polyfills are loaded\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/polyfills.ts",
    "content": "import 'url-polyfill';\nimport { console } from './console';\n\nglobalThis.console = console;\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/procedures.ts",
    "content": "import {\n  AlgebraicType,\n  ProductType,\n  type Deserializer,\n  type Serializer,\n} from '../lib/algebraic_type';\nimport { FunctionVisibility } from '../lib/autogen/types';\nimport BinaryReader from '../lib/binary_reader';\nimport BinaryWriter from '../lib/binary_writer';\nimport type { ConnectionId } from '../lib/connection_id';\nimport { Identity } from '../lib/identity';\nimport type { ParamsObj, ReducerCtx } from '../lib/reducers';\nimport { type UntypedSchemaDef } from '../lib/schema';\nimport { Timestamp } from '../lib/timestamp';\nimport {\n  type Infer,\n  type InferTypeOfRow,\n  type TypeBuilder,\n} from '../lib/type_builders';\nimport { bsatnBaseSize } from '../lib/util';\nimport { Uuid } from '../lib/uuid';\nimport { httpClient, type HttpClient } from './http_internal';\nimport type { DbView } from './db_view';\nimport { makeRandom, type Random } from './rng';\nimport { callUserFunction, ReducerCtxImpl, sys } from './runtime';\nimport {\n  exportContext,\n  registerExport,\n  type ModuleExport,\n  type SchemaInner,\n} from './schema';\n\nexport type ProcedureExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends TypeBuilder<any, any>,\n> = ProcedureFn<S, Params, Ret> & ModuleExport;\n\nexport function makeProcedureExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends TypeBuilder<any, any>,\n>(\n  ctx: SchemaInner,\n  opts: ProcedureOpts | undefined,\n  params: Params,\n  ret: Ret,\n  fn: ProcedureFn<S, Params, Ret>\n): ProcedureExport<S, Params, Ret> {\n  const name = opts?.name;\n\n  const procedureExport: ProcedureExport<S, Params, Ret> = (...args) =>\n    fn(...args);\n  procedureExport[exportContext] = ctx;\n  procedureExport[registerExport] = (ctx, exportName) => {\n    registerProcedure(ctx, name ?? exportName, params, ret, fn);\n    ctx.functionExports.set(\n      procedureExport as ProcedureExport<any, any, any>,\n      name ?? exportName\n    );\n  };\n\n  return procedureExport;\n}\n\nexport type ProcedureFn<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends TypeBuilder<any, any>,\n> = (ctx: ProcedureCtx<S>, args: InferTypeOfRow<Params>) => Infer<Ret>;\n\nexport interface ProcedureOpts {\n  name: string;\n}\n\nexport interface ProcedureCtx<S extends UntypedSchemaDef> {\n  readonly sender: Identity;\n  readonly identity: Identity;\n  readonly timestamp: Timestamp;\n  readonly connectionId: ConnectionId | null;\n  readonly http: HttpClient;\n  readonly random: Random;\n  withTx<T>(body: (ctx: TransactionCtx<S>) => T): T;\n  newUuidV4(): Uuid;\n  newUuidV7(): Uuid;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface TransactionCtx<S extends UntypedSchemaDef>\n  extends ReducerCtx<S> {}\n\ntype ITransactionCtx<S extends UntypedSchemaDef> = TransactionCtx<S>;\n\nconst TransactionCtxImpl = class TransactionCtx<S extends UntypedSchemaDef>\n  extends ReducerCtxImpl<S>\n  implements ITransactionCtx<S> {};\n\nfunction registerProcedure<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends TypeBuilder<any, any>,\n>(\n  ctx: SchemaInner,\n  exportName: string,\n  params: Params,\n  ret: Ret,\n  fn: ProcedureFn<S, Params, Ret>,\n  opts?: ProcedureOpts\n) {\n  ctx.defineFunction(exportName);\n  const paramsType: ProductType = {\n    elements: Object.entries(params).map(([n, c]) => ({\n      name: n,\n      algebraicType: ctx.registerTypesRecursively(\n        'typeBuilder' in c ? c.typeBuilder : c\n      ).algebraicType,\n    })),\n  };\n  const returnType = ctx.registerTypesRecursively(ret).algebraicType;\n\n  ctx.moduleDef.procedures.push({\n    sourceName: exportName,\n    params: paramsType,\n    returnType,\n    visibility: FunctionVisibility.ClientCallable,\n  });\n\n  if (opts?.name != null) {\n    ctx.moduleDef.explicitNames.entries.push({\n      tag: 'Function',\n      value: {\n        sourceName: exportName,\n        canonicalName: opts.name,\n      },\n    });\n  }\n  const { typespace } = ctx;\n\n  ctx.procedures.push({\n    fn,\n    deserializeArgs: ProductType.makeDeserializer(paramsType, typespace),\n    serializeReturn: AlgebraicType.makeSerializer(returnType, typespace),\n    returnTypeBaseSize: bsatnBaseSize(typespace, returnType),\n  });\n}\n\nexport type Procedures = Array<{\n  fn: ProcedureFn<any, any, any>;\n  deserializeArgs: Deserializer<any>;\n  serializeReturn: Serializer<any>;\n  returnTypeBaseSize: number;\n}>;\n\nexport function callProcedure(\n  moduleCtx: SchemaInner,\n  id: number,\n  sender: Identity,\n  connectionId: ConnectionId | null,\n  timestamp: Timestamp,\n  argsBuf: Uint8Array,\n  dbView: () => DbView<any>\n): Uint8Array {\n  const { fn, deserializeArgs, serializeReturn, returnTypeBaseSize } =\n    moduleCtx.procedures[id];\n  const args = deserializeArgs(new BinaryReader(argsBuf));\n\n  const ctx: ProcedureCtx<UntypedSchemaDef> = new ProcedureCtxImpl(\n    sender,\n    timestamp,\n    connectionId,\n    dbView\n  );\n\n  const ret = callUserFunction(fn, ctx, args);\n  const retBuf = new BinaryWriter(returnTypeBaseSize);\n  serializeReturn(retBuf, ret);\n  return retBuf.getBuffer();\n}\n\ntype IProcedureCtx<S extends UntypedSchemaDef> = ProcedureCtx<S>;\nconst ProcedureCtxImpl = class ProcedureCtx<S extends UntypedSchemaDef>\n  implements IProcedureCtx<S>\n{\n  #identity: Identity | undefined;\n  #uuidCounter: { value: 0 } | undefined;\n  #random: Random | undefined;\n  #dbView: () => DbView<any>;\n\n  constructor(\n    readonly sender: Identity,\n    readonly timestamp: Timestamp,\n    readonly connectionId: ConnectionId | null,\n    dbView: () => DbView<any>\n  ) {\n    this.#dbView = dbView;\n  }\n\n  get identity() {\n    return (this.#identity ??= new Identity(sys.identity()));\n  }\n\n  get random() {\n    return (this.#random ??= makeRandom(this.timestamp));\n  }\n\n  get http() {\n    return httpClient;\n  }\n\n  withTx<T>(body: (ctx: TransactionCtx<S>) => T): T {\n    const run = () => {\n      const timestamp = sys.procedure_start_mut_tx();\n\n      try {\n        const ctx: TransactionCtx<S> = new TransactionCtxImpl(\n          this.sender,\n          new Timestamp(timestamp),\n          this.connectionId,\n          this.#dbView()\n        );\n        return body(ctx);\n      } catch (e) {\n        sys.procedure_abort_mut_tx();\n        throw e;\n      }\n    };\n\n    let res = run();\n    try {\n      sys.procedure_commit_mut_tx();\n      return res;\n    } catch {\n      // ignore the commit error\n    }\n    console.warn('committing anonymous transaction failed');\n    res = run();\n    try {\n      sys.procedure_commit_mut_tx();\n      return res;\n    } catch (e) {\n      throw new Error('transaction retry failed again', { cause: e });\n    }\n  }\n\n  newUuidV4(): Uuid {\n    const bytes = this.random.fill(new Uint8Array(16));\n    return Uuid.fromRandomBytesV4(bytes);\n  }\n\n  newUuidV7(): Uuid {\n    const bytes = this.random.fill(new Uint8Array(4));\n    const counter = (this.#uuidCounter ??= { value: 0 });\n    return Uuid.fromCounterV7(counter, this.timestamp, bytes);\n  }\n};\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/query.ts",
    "content": "export * from '../lib/query';\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/range.ts",
    "content": "/**\n * A class representing a range with optional lower and upper bounds.\n * This class is used to specify ranges for index scans in SpacetimeDB.\n *\n * The range can be defined with inclusive or exclusive bounds, or can be unbounded on either side.\n * @template T - The type of the values in the range.\n * @example\n * ```typescript\n * // Create a range from 10 (inclusive) to 20 (exclusive)\n * const range = new Range(\n *   { tag: 'included', value: 10 },\n *   { tag: 'excluded', value: 20 }\n * );\n * // Create an unbounded range\n * const unboundedRange = new Range();\n * ```\n */\nexport class Range<T> {\n  #from: Bound<T>;\n  #to: Bound<T>;\n  public constructor(from?: Bound<T> | null, to?: Bound<T> | null) {\n    this.#from = from ?? { tag: 'unbounded' };\n    this.#to = to ?? { tag: 'unbounded' };\n  }\n\n  public get from(): Bound<T> {\n    return this.#from;\n  }\n  public get to(): Bound<T> {\n    return this.#to;\n  }\n}\n\n/**\n * A type representing a bound in a range, which can be inclusive, exclusive, or unbounded.\n * - `included`: The bound is inclusive, meaning the value is part of the range.\n * - `excluded`: The bound is exclusive, meaning the value is not part of the range.\n * - `unbounded`: The bound is unbounded, meaning there is no limit in that direction.\n * @template T - The type of the value for the bound.\n * @example\n * ```typescript\n * // Inclusive bound\n * const inclusiveBound: Bound<number> = { tag: 'included', value: 10 };\n * // Exclusive bound\n * const exclusiveBound: Bound<number> = { tag: 'excluded', value: 20 };\n * // Unbounded bound\n * const unbounded: Bound<number> = { tag: 'unbounded' };\n * ```\n */\nexport type Bound<T> =\n  | { tag: 'included'; value: T }\n  | { tag: 'excluded'; value: T }\n  | { tag: 'unbounded' };\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/reducers.ts",
    "content": "import { AlgebraicType } from '../lib/algebraic_type';\nimport { FunctionVisibility, type Lifecycle } from '../lib/autogen/types';\nimport type { ParamsObj, Reducer } from '../lib/reducers';\nimport { type UntypedSchemaDef } from '../lib/schema';\nimport { RowBuilder, type RowObj } from '../lib/type_builders';\nimport { toPascalCase } from '../lib/util';\nimport {\n  exportContext,\n  registerExport,\n  type ModuleExport,\n  type SchemaInner,\n} from './schema';\n\nexport interface ReducerExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n> extends Reducer<S, Params>,\n    ModuleExport {}\n\nexport interface ReducerOpts {\n  name: string;\n}\n\nexport function makeReducerExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n>(\n  ctx: SchemaInner,\n  opts: ReducerOpts | undefined,\n  params: RowObj | RowBuilder<RowObj>,\n  fn: Reducer<any, any>,\n  lifecycle?: Lifecycle\n): ReducerExport<S, Params> {\n  const reducerExport: ReducerExport<S, Params> = (...args) => fn(...args);\n  reducerExport[exportContext] = ctx;\n  reducerExport[registerExport] = (ctx, exportName) => {\n    registerReducer(ctx, exportName, params, fn, opts, lifecycle);\n    ctx.functionExports.set(\n      reducerExport as ReducerExport<any, any>,\n      exportName\n    );\n  };\n\n  return reducerExport;\n}\n\n/**\n * internal: pushReducer() helper used by reducer() and lifecycle wrappers\n *\n * @param name - The name of the reducer.\n * @param params - The parameters for the reducer.\n * @param fn - The reducer function.\n * @param lifecycle - Optional lifecycle hooks for the reducer.\n */\nexport function registerReducer(\n  ctx: SchemaInner,\n  exportName: string,\n  params: RowObj | RowBuilder<RowObj>,\n  fn: Reducer<any, any>,\n  opts?: ReducerOpts,\n  lifecycle?: Lifecycle\n): void {\n  ctx.defineFunction(exportName);\n\n  if (!(params instanceof RowBuilder)) {\n    params = new RowBuilder(params);\n  }\n\n  if (params.typeName === undefined) {\n    params.typeName = toPascalCase(exportName);\n  }\n\n  const ref = ctx.registerTypesRecursively(params);\n  const paramsType = ctx.resolveType(ref).value;\n  const isLifecycle = lifecycle != null;\n\n  ctx.moduleDef.reducers.push({\n    sourceName: exportName,\n    params: paramsType,\n    //ModuleDef validation code is responsible to mark private reducers\n    visibility: FunctionVisibility.ClientCallable,\n    //Hardcoded for now - reducers do not return values yet\n    okReturnType: AlgebraicType.Product({ elements: [] }),\n    errReturnType: AlgebraicType.String,\n  });\n\n  if (opts?.name != null) {\n    ctx.moduleDef.explicitNames.entries.push({\n      tag: 'Function',\n      value: {\n        sourceName: exportName,\n        canonicalName: opts.name,\n      },\n    });\n  }\n\n  if (isLifecycle) {\n    ctx.moduleDef.lifeCycleReducers.push({\n      lifecycleSpec: lifecycle,\n      functionName: exportName,\n    });\n  }\n\n  // If the function isn't named (e.g. `function foobar() {}`), give it the same\n  // name as the reducer so that it's clear what it is in in backtraces.\n  if (!fn.name) {\n    Object.defineProperty(fn, 'name', { value: exportName, writable: false });\n  }\n\n  ctx.reducers.push(fn);\n}\n\nexport type Reducers = Reducer<any, any>[];\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/rng.ts",
    "content": "import type { RandomGenerator } from 'pure-rand';\nimport { unsafeUniformBigIntDistribution } from 'pure-rand/distribution/UnsafeUniformBigIntDistribution';\nimport { unsafeUniformIntDistribution } from 'pure-rand/distribution/UnsafeUniformIntDistribution';\nimport { xoroshiro128plus } from 'pure-rand/generator/XoroShiro';\nimport type { Timestamp } from '../lib/timestamp';\n\ntype IntArray =\n  | Int8Array\n  | Uint8Array\n  | Uint8ClampedArray\n  | Int16Array\n  | Uint16Array\n  | Int32Array\n  | Uint32Array\n  | BigInt64Array\n  | BigUint64Array;\n\n/**\n * A collection of random-number-generating functions, seeded based on `ctx.timestamp`.\n *\n * ## Usage\n *\n * ```\n * const floatOneToTen = ctx.random() * 10;\n * const randomBytes = ctx.random.fill(new Uint8Array(16));\n * const intOneToTen = ctx.random.integerInRange(0, 10);\n * ```\n */\nexport interface Random {\n  /**\n   * Returns a random floating-point number in the range `[0.0, 1.0)`.\n   *\n   * The returned float will have 53 bits of randomness.\n   */\n  (): number;\n\n  /**\n   * Like `crypto.getRandomValues()`. Fills a `TypedArray` with random integers\n   * in a uniform distribution, mutating and returning it.\n   */\n  fill<T extends IntArray>(array: T): T;\n\n  /**\n   * Returns a random unsigned 32-bit integer in a uniform distribution in the\n   * range `[0, 2**32)`.\n   */\n  uint32(): number;\n\n  /**\n   * Returns an integer in the range `[min, max]`.\n   */\n  integerInRange(min: number, max: number): number;\n\n  /**\n   * Returns a bigint in the range `[min, max]`.\n   */\n  bigintInRange(min: bigint, max: bigint): bigint;\n}\n\nconst { asUintN } = BigInt;\n\n/** Based on the function of the same name in `rand_core::SeedableRng::seed_from_u64` */\nfunction pcg32(state: bigint): number {\n  const MUL = 6364136223846793005n;\n  const INC = 11634580027462260723n;\n\n  state = asUintN(64, state * MUL + INC);\n  const xorshifted = Number(asUintN(32, ((state >> 18n) ^ state) >> 27n));\n  const rot = Number(asUintN(32, state >> 59n));\n  // rotate `xorshifted` right by `rot` bits\n  return (xorshifted >> rot) | (xorshifted << (32 - rot));\n}\n\n/** From the `pure-rand` README */\nfunction generateFloat64(rng: RandomGenerator): number {\n  const g1 = unsafeUniformIntDistribution(0, (1 << 26) - 1, rng);\n  const g2 = unsafeUniformIntDistribution(0, (1 << 27) - 1, rng);\n  const value = (g1 * Math.pow(2, 27) + g2) * Math.pow(2, -53);\n  return value;\n}\n\nexport function makeRandom(seed: Timestamp): Random {\n  // Use PCG32 to turn a 64-bit seed into a 32-bit seed, as the Rust `rand` crate does.\n  const rng = xoroshiro128plus(pcg32(seed.microsSinceUnixEpoch));\n\n  const random: Random = () => generateFloat64(rng);\n\n  random.fill = array => {\n    const elem = array.at(0);\n    if (typeof elem === 'bigint') {\n      const upper = (1n << BigInt(array.BYTES_PER_ELEMENT * 8)) - 1n;\n      for (let i = 0; i < array.length; i++) {\n        array[i] = unsafeUniformBigIntDistribution(0n, upper, rng);\n      }\n    } else if (typeof elem === 'number') {\n      const upper = (1 << (array.BYTES_PER_ELEMENT * 8)) - 1;\n      for (let i = 0; i < array.length; i++) {\n        array[i] = unsafeUniformIntDistribution(0, upper, rng);\n      }\n    }\n    return array;\n  };\n\n  random.uint32 = () => rng.unsafeNext();\n\n  random.integerInRange = (min, max) =>\n    unsafeUniformIntDistribution(min, max, rng);\n\n  random.bigintInRange = (min, max) =>\n    unsafeUniformBigIntDistribution(min, max, rng);\n\n  return random;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/runtime.ts",
    "content": "import * as _syscalls2_0 from 'spacetime:sys@2.0';\n\nimport type { ModuleHooks, u128, u16, u256, u32 } from 'spacetime:sys@2.0';\nimport {\n  AlgebraicType,\n  ProductType,\n  type Deserializer,\n} from '../lib/algebraic_type';\nimport {\n  RawModuleDef,\n  ViewResultHeader,\n  type RawTableDefV10,\n  type Typespace,\n} from '../lib/autogen/types';\nimport { ConnectionId } from '../lib/connection_id';\nimport { Identity } from '../lib/identity';\nimport { Timestamp } from '../lib/timestamp';\nimport { Uuid } from '../lib/uuid';\nimport BinaryReader from '../lib/binary_reader';\nimport BinaryWriter, { ResizableBuffer } from '../lib/binary_writer';\nimport {\n  type Index,\n  type IndexVal,\n  type PointIndex,\n  type RangedIndex,\n  type UniqueIndex,\n} from '../lib/indexes';\nimport { callProcedure } from './procedures';\nimport {\n  type AuthCtx,\n  type JsonObject,\n  type JwtClaims,\n  type ReducerCtx as IReducerCtx,\n} from '../lib/reducers';\nimport { type UntypedSchemaDef } from '../lib/schema';\nimport { type RowType, type Table, type TableMethods } from '../lib/table';\nimport { hasOwn } from '../lib/util';\nimport { type AnonymousViewCtx, type ViewCtx } from './views';\nimport { isRowTypedQuery, makeQueryBuilder, toSql } from './query';\nimport type { DbView } from './db_view';\nimport { getErrorConstructor, SenderError } from './errors';\nimport { Range, type Bound } from './range';\nimport { makeRandom, type Random } from './rng';\nimport type { SchemaInner } from './schema';\n\nconst { freeze } = Object;\n\nexport const sys = _syscalls2_0;\n\nexport function parseJsonObject(json: string): JsonObject {\n  let value: unknown;\n\n  try {\n    value = JSON.parse(json);\n  } catch {\n    throw new Error('Invalid JSON: failed to parse string');\n  }\n\n  if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n    throw new Error('Expected a JSON object at the top level');\n  }\n\n  // The runtime check above guarantees this cast is safe\n  return value as JsonObject;\n}\n\nclass JwtClaimsImpl implements JwtClaims {\n  readonly fullPayload: JsonObject;\n  private readonly _identity: Identity;\n  /**\n   * Creates a new JwtClaims instance.\n   * @param rawPayload The JWT payload as a raw JSON string.\n   * @param identity The identity for this JWT. We are only taking this because we don't have a blake3 implementation (which we need to compute it).\n   */\n  constructor(\n    public readonly rawPayload: string,\n    identity: Identity\n  ) {\n    this.fullPayload = parseJsonObject(rawPayload);\n    this._identity = identity;\n  }\n  readonly [claim: string]: unknown;\n  get identity(): Identity {\n    return this._identity;\n  }\n  get subject() {\n    return this.fullPayload['sub'] as string;\n  }\n  get issuer() {\n    return this.fullPayload['iss'] as string;\n  }\n  get audience() {\n    const aud = this.fullPayload['aud'];\n    if (aud == null) {\n      return [];\n    }\n    return typeof aud === 'string' ? [aud] : (aud as string[]);\n  }\n}\n\nclass AuthCtxImpl implements AuthCtx {\n  public readonly isInternal: boolean;\n\n  // Source of the JWT payload string, if there is one.\n  private readonly _jwtSource: () => string | null;\n  // Whether we have initialized the JWT claims.\n  private _initializedJWT: boolean = false;\n  private _jwtClaims?: JwtClaims | null;\n  private _senderIdentity: Identity;\n\n  private constructor(opts: {\n    isInternal: boolean;\n    jwtSource: () => string | null;\n    senderIdentity: Identity;\n  }) {\n    this.isInternal = opts.isInternal;\n    this._jwtSource = opts.jwtSource;\n    this._senderIdentity = opts.senderIdentity;\n  }\n\n  private _initializeJWT() {\n    if (this._initializedJWT) return;\n    this._initializedJWT = true;\n\n    const token = this._jwtSource();\n    if (!token) {\n      this._jwtClaims = null;\n    } else {\n      this._jwtClaims = new JwtClaimsImpl(token, this._senderIdentity);\n    }\n    // At this point we can safely freeze the object.\n    Object.freeze(this);\n  }\n\n  /** Lazily compute whether a JWT exists and is parseable. */\n  get hasJWT(): boolean {\n    this._initializeJWT();\n    return this._jwtClaims !== null;\n  }\n\n  /** Lazily parse the JwtClaims only when accessed. */\n  get jwt(): JwtClaims | null {\n    this._initializeJWT();\n    return this._jwtClaims!;\n  }\n\n  /** Create a context representing internal (non-user) requests. */\n  static internal(): AuthCtx {\n    return new AuthCtxImpl({\n      isInternal: true,\n      jwtSource: () => null,\n      senderIdentity: Identity.zero(),\n    });\n  }\n\n  /** If there is a connection id, look up the JWT payload from the system tables. */\n  static fromSystemTables(\n    connectionId: ConnectionId | null,\n    sender: Identity\n  ): AuthCtx {\n    if (connectionId === null) {\n      return new AuthCtxImpl({\n        isInternal: false,\n        jwtSource: () => null,\n        senderIdentity: sender,\n      });\n    }\n    return new AuthCtxImpl({\n      isInternal: false,\n      jwtSource: () => {\n        const payloadBuf = sys.get_jwt_payload(connectionId.__connection_id__);\n        if (payloadBuf.length === 0) return null;\n        const payloadStr = new TextDecoder().decode(payloadBuf);\n        return payloadStr;\n      },\n      senderIdentity: sender,\n    });\n  }\n}\n\n// Using a class expression rather than declaration keeps the class out of the\n// type namespace, so that `ReducerCtx` still refers to the interface.\nexport const ReducerCtxImpl = class ReducerCtx<\n  SchemaDef extends UntypedSchemaDef,\n> implements IReducerCtx<SchemaDef>\n{\n  #identity: Identity | undefined;\n  #senderAuth: AuthCtx | undefined;\n  #uuidCounter: { value: number } | undefined;\n  #random: Random | undefined;\n  sender: Identity;\n  timestamp: Timestamp;\n  connectionId: ConnectionId | null;\n  db: DbView<SchemaDef>;\n\n  constructor(\n    sender: Identity,\n    timestamp: Timestamp,\n    connectionId: ConnectionId | null,\n    dbView: DbView<any>\n  ) {\n    Object.seal(this);\n    this.sender = sender;\n    this.timestamp = timestamp;\n    this.connectionId = connectionId;\n    this.db = dbView;\n  }\n\n  /** Reset the `ReducerCtx` to be used for a new transaction */\n  static reset(\n    me: InstanceType<typeof this>,\n    sender: Identity,\n    timestamp: Timestamp,\n    connectionId: ConnectionId | null\n  ) {\n    me.sender = sender;\n    me.timestamp = timestamp;\n    me.connectionId = connectionId;\n    me.#uuidCounter = undefined;\n    me.#senderAuth = undefined;\n  }\n\n  get identity() {\n    return (this.#identity ??= new Identity(sys.identity()));\n  }\n\n  get senderAuth() {\n    return (this.#senderAuth ??= AuthCtxImpl.fromSystemTables(\n      this.connectionId,\n      this.sender\n    ));\n  }\n\n  get random() {\n    return (this.#random ??= makeRandom(this.timestamp));\n  }\n\n  /**\n   * Create a new random {@link Uuid} `v4` using this `ReducerCtx`'s RNG.\n   */\n  newUuidV4(): Uuid {\n    const bytes = this.random.fill(new Uint8Array(16));\n    return Uuid.fromRandomBytesV4(bytes);\n  }\n\n  /**\n   * Create a new sortable {@link Uuid} `v7` using this `ReducerCtx`'s RNG, counter,\n   * and timestamp.\n   */\n  newUuidV7(): Uuid {\n    const bytes = this.random.fill(new Uint8Array(4));\n    const counter = (this.#uuidCounter ??= { value: 0 });\n    return Uuid.fromCounterV7(counter, this.timestamp, bytes);\n  }\n};\n\n/**\n * Call into a user function `fn` - the backtrace from an exception thrown in\n * `fn` or one of its descendants in the callgraph will be stripped by host\n * code in `crates/core/src/host/v8/error.rs` such that `fn` will be shown to\n * be the root of the call stack.\n */\nexport const callUserFunction = function __spacetimedb_end_short_backtrace<\n  Args extends any[],\n  R,\n>(fn: (...args: Args) => R, ...args: Args): R {\n  return fn(...args);\n};\n\nexport const makeHooks = (schema: SchemaInner): ModuleHooks =>\n  new ModuleHooksImpl(schema);\n\nclass ModuleHooksImpl implements ModuleHooks {\n  #schema: SchemaInner;\n  #dbView_: DbView<any> | undefined;\n  #reducerArgsDeserializers;\n  /** Cache the `ReducerCtx` object to avoid allocating anew for ever reducer call. */\n  #reducerCtx_: InstanceType<typeof ReducerCtxImpl> | undefined;\n\n  constructor(schema: SchemaInner) {\n    this.#schema = schema;\n    this.#reducerArgsDeserializers = schema.moduleDef.reducers.map(\n      ({ params }) => ProductType.makeDeserializer(params, schema.typespace)\n    );\n  }\n\n  get #dbView() {\n    return (this.#dbView_ ??= freeze(\n      Object.fromEntries(\n        Object.values(this.#schema.schemaType.tables).map(table => [\n          table.accessorName,\n          makeTableView(this.#schema.typespace, table.tableDef),\n        ])\n      )\n    ));\n  }\n\n  get #reducerCtx() {\n    return (this.#reducerCtx_ ??= new ReducerCtxImpl(\n      Identity.zero(),\n      Timestamp.UNIX_EPOCH,\n      null,\n      this.#dbView\n    ));\n  }\n\n  __describe_module__() {\n    const writer = new BinaryWriter(128);\n    RawModuleDef.serialize(\n      writer,\n      RawModuleDef.V10(this.#schema.rawModuleDefV10())\n    );\n    return writer.getBuffer();\n  }\n\n  __get_error_constructor__(code: number): new (msg: string) => Error {\n    return getErrorConstructor(code);\n  }\n\n  get __sender_error_class__() {\n    return SenderError;\n  }\n\n  __call_reducer__(\n    reducerId: u32,\n    sender: u256,\n    connId: u128,\n    timestamp: bigint,\n    argsBuf: DataView\n  ): void {\n    const moduleCtx = this.#schema;\n    const deserializeArgs = this.#reducerArgsDeserializers[reducerId];\n    BINARY_READER.reset(argsBuf);\n    const args = deserializeArgs(BINARY_READER);\n    const senderIdentity = new Identity(sender);\n    const ctx = this.#reducerCtx;\n    ReducerCtxImpl.reset(\n      ctx,\n      senderIdentity,\n      new Timestamp(timestamp),\n      ConnectionId.nullIfZero(new ConnectionId(connId))\n    );\n    callUserFunction(moduleCtx.reducers[reducerId], ctx, args);\n  }\n\n  __call_view__(\n    id: u32,\n    sender: u256,\n    argsBuf: Uint8Array\n  ): { data: Uint8Array } {\n    const moduleCtx = this.#schema;\n    const { fn, deserializeParams, serializeReturn, returnTypeBaseSize } =\n      moduleCtx.views[id];\n    const ctx: ViewCtx<any> = freeze({\n      sender: new Identity(sender),\n      // this is the non-readonly DbView, but the typing for the user will be\n      // the readonly one, and if they do call mutating functions it will fail\n      // at runtime\n      db: this.#dbView,\n      from: makeQueryBuilder(moduleCtx.schemaType),\n    });\n    const args = deserializeParams(new BinaryReader(argsBuf));\n    const ret = callUserFunction(fn, ctx, args);\n    const retBuf = new BinaryWriter(returnTypeBaseSize);\n    if (isRowTypedQuery(ret)) {\n      const query = toSql(ret);\n      ViewResultHeader.serialize(retBuf, ViewResultHeader.RawSql(query));\n    } else {\n      ViewResultHeader.serialize(retBuf, ViewResultHeader.RowData);\n      serializeReturn(retBuf, ret);\n    }\n    return { data: retBuf.getBuffer() };\n  }\n\n  __call_view_anon__(id: u32, argsBuf: Uint8Array): { data: Uint8Array } {\n    const moduleCtx = this.#schema;\n    const { fn, deserializeParams, serializeReturn, returnTypeBaseSize } =\n      moduleCtx.anonViews[id];\n    const ctx: AnonymousViewCtx<any> = freeze({\n      // this is the non-readonly DbView, but the typing for the user will be\n      // the readonly one, and if they do call mutating functions it will fail\n      // at runtime\n      db: this.#dbView,\n      from: makeQueryBuilder(moduleCtx.schemaType),\n    });\n    const args = deserializeParams(new BinaryReader(argsBuf));\n    const ret = callUserFunction(fn, ctx, args);\n    const retBuf = new BinaryWriter(returnTypeBaseSize);\n    if (isRowTypedQuery(ret)) {\n      const query = toSql(ret);\n      ViewResultHeader.serialize(retBuf, ViewResultHeader.RawSql(query));\n    } else {\n      ViewResultHeader.serialize(retBuf, ViewResultHeader.RowData);\n      serializeReturn(retBuf, ret);\n    }\n    return { data: retBuf.getBuffer() };\n  }\n\n  __call_procedure__(\n    id: u32,\n    sender: u256,\n    connection_id: u128,\n    timestamp: bigint,\n    args: Uint8Array\n  ): Uint8Array {\n    return callProcedure(\n      this.#schema,\n      id,\n      new Identity(sender),\n      ConnectionId.nullIfZero(new ConnectionId(connection_id)),\n      new Timestamp(timestamp),\n      args,\n      () => this.#dbView\n    );\n  }\n}\n\nconst BINARY_WRITER = new BinaryWriter(0);\nconst BINARY_READER = new BinaryReader(new Uint8Array());\n\nfunction makeTableView(\n  typespace: Typespace,\n  table: RawTableDefV10\n): Table<any> {\n  const table_id = sys.table_id_from_name(table.sourceName);\n  const rowType = typespace.types[table.productTypeRef];\n  if (rowType.tag !== 'Product') {\n    throw 'impossible';\n  }\n\n  const serializeRow = AlgebraicType.makeSerializer(rowType, typespace);\n  const deserializeRow = AlgebraicType.makeDeserializer(rowType, typespace);\n\n  const sequences = table.sequences.map(seq => {\n    const col = rowType.value.elements[seq.column];\n    const colType = col.algebraicType;\n\n    // Determine the sentinel value which users will pass to as a placeholder\n    // to cause the sequence to advance.\n    // For small integer SATS types which fit in V8 `number`s, this is `0: number`,\n    // and for larger integer SATS types it's `0n: BigInt`.\n    let sequenceTrigger: bigint | number;\n    switch (colType.tag) {\n      case 'U8':\n      case 'I8':\n      case 'U16':\n      case 'I16':\n      case 'U32':\n      case 'I32':\n        sequenceTrigger = 0;\n        break;\n      case 'U64':\n      case 'I64':\n      case 'U128':\n      case 'I128':\n      case 'U256':\n      case 'I256':\n        sequenceTrigger = 0n;\n        break;\n      default:\n        throw new TypeError('invalid sequence type');\n    }\n    return {\n      colName: col.name!,\n      sequenceTrigger,\n      deserialize: AlgebraicType.makeDeserializer(colType, typespace),\n    };\n  });\n  const hasAutoIncrement = sequences.length > 0;\n\n  const iter = () =>\n    tableIterator(sys.datastore_table_scan_bsatn(table_id), deserializeRow);\n\n  const integrateGeneratedColumns = hasAutoIncrement\n    ? (row: RowType<any>, ret_buf: DataView) => {\n        BINARY_READER.reset(ret_buf);\n        for (const { colName, deserialize, sequenceTrigger } of sequences) {\n          if (row[colName] === sequenceTrigger) {\n            row[colName] = deserialize(BINARY_READER);\n          }\n        }\n      }\n    : null;\n\n  const tableMethods: TableMethods<any> = {\n    count: () => sys.datastore_table_row_count(table_id),\n    iter,\n    [Symbol.iterator]: () => iter(),\n    insert: row => {\n      const buf = LEAF_BUF;\n      BINARY_WRITER.reset(buf);\n      serializeRow(BINARY_WRITER, row);\n      sys.datastore_insert_bsatn(table_id, buf.buffer, BINARY_WRITER.offset);\n      const ret = { ...row };\n      integrateGeneratedColumns?.(ret, buf.view);\n\n      return ret;\n    },\n    delete: (row: RowType<any>): boolean => {\n      const buf = LEAF_BUF;\n      BINARY_WRITER.reset(buf);\n      BINARY_WRITER.writeU32(1);\n      serializeRow(BINARY_WRITER, row);\n      const count = sys.datastore_delete_all_by_eq_bsatn(\n        table_id,\n        buf.buffer,\n        BINARY_WRITER.offset\n      );\n      return count > 0;\n    },\n  };\n\n  const tableView = Object.assign(\n    Object.create(null),\n    tableMethods\n  ) as Table<any>;\n\n  for (const indexDef of table.indexes) {\n    const accessorName = indexDef.accessorName!;\n    const index_id = sys.index_id_from_name(indexDef.sourceName!);\n\n    let column_ids: number[];\n    let isHashIndex = false;\n    switch (indexDef.algorithm.tag) {\n      case 'Hash':\n        isHashIndex = true;\n        column_ids = indexDef.algorithm.value;\n        break;\n      case 'BTree':\n        column_ids = indexDef.algorithm.value;\n        break;\n      case 'Direct':\n        column_ids = [indexDef.algorithm.value];\n        break;\n    }\n    const numColumns = column_ids.length;\n\n    const columnSet = new Set(column_ids);\n    const isUnique = table.constraints\n      .filter(x => x.data.tag === 'Unique')\n      .some(x => columnSet.isSubsetOf(new Set(x.data.value.columns)));\n\n    const isPrimaryKey =\n      isUnique &&\n      column_ids.length === table.primaryKey.length &&\n      column_ids.every((id, i) => table.primaryKey[i] === id);\n\n    const indexSerializers = column_ids.map(id =>\n      AlgebraicType.makeSerializer(\n        rowType.value.elements[id].algebraicType,\n        typespace\n      )\n    );\n\n    const serializePoint = (buffer: ResizableBuffer, colVal: any[]): number => {\n      BINARY_WRITER.reset(buffer);\n      for (let i = 0; i < numColumns; i++) {\n        indexSerializers[i](BINARY_WRITER, colVal[i]);\n      }\n      return BINARY_WRITER.offset;\n    };\n\n    const serializeSingleElement =\n      numColumns === 1 ? indexSerializers[0] : null;\n\n    const serializeSinglePoint =\n      serializeSingleElement &&\n      ((buffer: ResizableBuffer, colVal: any): number => {\n        BINARY_WRITER.reset(buffer);\n        serializeSingleElement(BINARY_WRITER, colVal);\n        return BINARY_WRITER.offset;\n      });\n\n    type IndexScanArgs = [\n      prefix_len: u32,\n      prefix_elems: u16,\n      rstart_len: u32,\n      rend_len: u32,\n    ];\n\n    let index: Index<any, any>;\n    if (isUnique && serializeSinglePoint) {\n      // numColumns == 1, unique index\n      const base = {\n        find: (colVal: IndexVal<any, any>): RowType<any> | null => {\n          const buf = LEAF_BUF;\n          const point_len = serializeSinglePoint(buf, colVal);\n          const iter_id = sys.datastore_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return tableIterateOne(iter_id, deserializeRow);\n        },\n        delete: (colVal: IndexVal<any, any>): boolean => {\n          const buf = LEAF_BUF;\n          const point_len = serializeSinglePoint(buf, colVal);\n          const num = sys.datastore_delete_by_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return num > 0;\n        },\n      };\n      if (isPrimaryKey) {\n        (base as any).update = (row: RowType<any>): RowType<any> => {\n          const buf = LEAF_BUF;\n          BINARY_WRITER.reset(buf);\n          serializeRow(BINARY_WRITER, row);\n          sys.datastore_update_bsatn(\n            table_id,\n            index_id,\n            buf.buffer,\n            BINARY_WRITER.offset\n          );\n          integrateGeneratedColumns?.(row, buf.view);\n          return row;\n        };\n      }\n      index = base as UniqueIndex<any, any>;\n    } else if (isUnique) {\n      // numColumns != 1, unique index\n      const base = {\n        find: (colVal: IndexVal<any, any>): RowType<any> | null => {\n          if (colVal.length !== numColumns) {\n            throw new TypeError('wrong number of elements');\n          }\n          const buf = LEAF_BUF;\n          const point_len = serializePoint(buf, colVal);\n          const iter_id = sys.datastore_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return tableIterateOne(iter_id, deserializeRow);\n        },\n        delete: (colVal: IndexVal<any, any>): boolean => {\n          if (colVal.length !== numColumns)\n            throw new TypeError('wrong number of elements');\n\n          const buf = LEAF_BUF;\n          const point_len = serializePoint(buf, colVal);\n          const num = sys.datastore_delete_by_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return num > 0;\n        },\n      };\n      if (isPrimaryKey) {\n        (base as any).update = (row: RowType<any>): RowType<any> => {\n          const buf = LEAF_BUF;\n          BINARY_WRITER.reset(buf);\n          serializeRow(BINARY_WRITER, row);\n          sys.datastore_update_bsatn(\n            table_id,\n            index_id,\n            buf.buffer,\n            BINARY_WRITER.offset\n          );\n          integrateGeneratedColumns?.(row, buf.view);\n          return row;\n        };\n      }\n      index = base as UniqueIndex<any, any>;\n    } else if (serializeSinglePoint) {\n      // numColumns == 1\n      const rawIndex = {\n        filter: (range: any): IteratorObject<RowType<any>> => {\n          const buf = LEAF_BUF;\n          const point_len = serializeSinglePoint(buf, range);\n          const iter_id = sys.datastore_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return tableIterator(iter_id, deserializeRow);\n        },\n        delete: (range: any): u32 => {\n          const buf = LEAF_BUF;\n          const point_len = serializeSinglePoint(buf, range);\n          return sys.datastore_delete_by_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n        },\n      };\n      if (isHashIndex) {\n        index = rawIndex as PointIndex<any, any>;\n      } else {\n        index = rawIndex as RangedIndex<any, any>;\n      }\n    } else if (isHashIndex) {\n      // numColumns != 1\n      index = {\n        filter: (range: any[]): IteratorObject<RowType<any>> => {\n          const buf = LEAF_BUF;\n          const point_len = serializePoint(buf, range);\n          const iter_id = sys.datastore_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n          return tableIterator(iter_id, deserializeRow);\n        },\n        delete: (range: any[]): u32 => {\n          const buf = LEAF_BUF;\n          const point_len = serializePoint(buf, range);\n          return sys.datastore_delete_by_index_scan_point_bsatn(\n            index_id,\n            buf.buffer,\n            point_len\n          );\n        },\n      } as PointIndex<any, any>;\n    } else {\n      // numColumns != 1\n      const serializeRange = (\n        buffer: ResizableBuffer,\n        range: any[]\n      ): IndexScanArgs => {\n        if (range.length > numColumns) throw new TypeError('too many elements');\n\n        BINARY_WRITER.reset(buffer);\n        const writer = BINARY_WRITER;\n        const prefix_elems = range.length - 1;\n        for (let i = 0; i < prefix_elems; i++) {\n          indexSerializers[i](writer, range[i]);\n        }\n        const rstartOffset = writer.offset;\n        const term = range[range.length - 1];\n        const serializeTerm = indexSerializers[range.length - 1];\n        if (term instanceof Range) {\n          const writeBound = (bound: Bound<any>) => {\n            const tags = { included: 0, excluded: 1, unbounded: 2 };\n            writer.writeU8(tags[bound.tag]);\n            if (bound.tag !== 'unbounded') serializeTerm(writer, bound.value);\n          };\n          writeBound(term.from);\n          const rstartLen = writer.offset - rstartOffset;\n          writeBound(term.to);\n          const rendLen = writer.offset - rstartLen;\n          return [rstartOffset, prefix_elems, rstartLen, rendLen];\n        } else {\n          writer.writeU8(0);\n          serializeTerm(writer, term);\n          const rstartLen = writer.offset;\n          const rendLen = 0;\n          return [rstartOffset, prefix_elems, rstartLen, rendLen];\n        }\n      };\n      index = {\n        filter: (range: any[]): IteratorObject<RowType<any>> => {\n          if (range.length === numColumns) {\n            const buf = LEAF_BUF;\n            const point_len = serializePoint(buf, range);\n            const iter_id = sys.datastore_index_scan_point_bsatn(\n              index_id,\n              buf.buffer,\n              point_len\n            );\n            return tableIterator(iter_id, deserializeRow);\n          } else {\n            const buf = LEAF_BUF;\n            const args = serializeRange(buf, range);\n            const iter_id = sys.datastore_index_scan_range_bsatn(\n              index_id,\n              buf.buffer,\n              ...args\n            );\n            return tableIterator(iter_id, deserializeRow);\n          }\n        },\n        delete: (range: any[]): u32 => {\n          if (range.length === numColumns) {\n            const buf = LEAF_BUF;\n            const point_len = serializePoint(buf, range);\n            return sys.datastore_delete_by_index_scan_point_bsatn(\n              index_id,\n              buf.buffer,\n              point_len\n            );\n          } else {\n            const buf = LEAF_BUF;\n            const args = serializeRange(buf, range);\n            return sys.datastore_delete_by_index_scan_range_bsatn(\n              index_id,\n              buf.buffer,\n              ...args\n            );\n          }\n        },\n      } as RangedIndex<any, any>;\n    }\n\n    // IMPORTANT: duplicate accessor handling.\n    // When multiple raw indexes share the same accessor name, we merge index\n    // methods onto a single accessor object instead of throwing.\n    if (Object.hasOwn(tableView, accessorName)) {\n      freeze(Object.assign((tableView as any)[accessorName], index));\n    } else {\n      (tableView as any)[accessorName] = freeze(index);\n    }\n  }\n\n  return freeze(tableView);\n}\n\nfunction* tableIterator<T>(\n  id: u32,\n  deserialize: Deserializer<T>\n): Generator<T, undefined> {\n  using iter = new IteratorHandle(id);\n\n  const iterBuf = takeBuf();\n  try {\n    let amt;\n    while ((amt = iter.advance(iterBuf))) {\n      const reader = new BinaryReader(iterBuf.view);\n      while (reader.offset < amt) {\n        yield deserialize(reader);\n      }\n    }\n  } finally {\n    returnBuf(iterBuf);\n  }\n}\n\nfunction tableIterateOne<T>(id: u32, deserialize: Deserializer<T>): T | null {\n  const buf = LEAF_BUF;\n  // we only need to check for the `<= 0` case, since this function is only used\n  // with iterators that should only have zero or one element.\n  const ret = advanceIterRaw(id, buf);\n  if (ret !== 0) {\n    BINARY_READER.reset(buf.view);\n    return deserialize(BINARY_READER);\n  }\n  return null;\n}\n\n/**\n * `ret < 0` means the iterator yielded elements but is now exhausted and has been destroyed.\n * `ret === 0` means the iterator was empty and has been destroyed.\n * `ret > 0` means the iterator yielded elements and has more to give.\n */\nfunction advanceIterRaw(id: u32, buf: ResizableBuffer): number {\n  while (true) {\n    try {\n      return 0 | sys.row_iter_bsatn_advance(id, buf.buffer);\n    } catch (e) {\n      if (e && typeof e === 'object' && hasOwn(e, '__buffer_too_small__')) {\n        buf.grow(e.__buffer_too_small__ as number);\n        continue;\n      }\n      throw e;\n    }\n  }\n}\n\n// This should guarantee in most cases that we don't have to reallocate an iterator\n// buffer, unless there's a single row that serializes to >1 MiB.\nconst DEFAULT_BUFFER_CAPACITY = 32 * 1024 * 2;\n\nconst ITER_BUFS: ResizableBuffer[] = [\n  new ResizableBuffer(DEFAULT_BUFFER_CAPACITY),\n];\nlet ITER_BUF_COUNT = 1;\n\nfunction takeBuf(): ResizableBuffer {\n  return ITER_BUF_COUNT\n    ? ITER_BUFS[--ITER_BUF_COUNT]\n    : new ResizableBuffer(DEFAULT_BUFFER_CAPACITY);\n}\n\nfunction returnBuf(buf: ResizableBuffer) {\n  ITER_BUFS[ITER_BUF_COUNT++] = buf;\n}\n\n/**\n * This should only be used from functions that don't need persistent ownership\n * over the buffer. While using this value, one should not call a function that\n * also uses this value.\n */\nconst LEAF_BUF = new ResizableBuffer(DEFAULT_BUFFER_CAPACITY);\n\n/** A class to manage the lifecycle of an iterator handle. */\nclass IteratorHandle implements Disposable {\n  #id: u32 | -1;\n\n  static #finalizationRegistry = new FinalizationRegistry<u32>(\n    sys.row_iter_bsatn_close\n  );\n\n  constructor(id: u32) {\n    this.#id = id;\n    IteratorHandle.#finalizationRegistry.register(this, id, this);\n  }\n\n  /** Unregister this object with the finalization registry and return the id */\n  #detach() {\n    const id = this.#id;\n    this.#id = -1;\n    IteratorHandle.#finalizationRegistry.unregister(this);\n    return id;\n  }\n\n  /** Call `row_iter_bsatn_advance`, returning 0 if this iterator has been exhausted. */\n  advance(buf: ResizableBuffer): number {\n    if (this.#id === -1) return 0;\n    const ret = advanceIterRaw(this.#id, buf);\n    if (ret <= 0) this.#detach();\n    return ret < 0 ? -ret : ret;\n  }\n\n  [Symbol.dispose]() {\n    if (this.#id >= 0) {\n      const id = this.#detach();\n      sys.row_iter_bsatn_close(id);\n    }\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/schema.test-d.ts",
    "content": "import { schema } from './schema';\nimport { table } from '../lib/table';\nimport t from '../lib/type_builders';\n\nconst person = table(\n  {\n    // name: 'person',\n    indexes: [\n      {\n        accessor: 'id_name_idx',\n        algorithm: 'btree',\n        columns: ['id', 'name'] as const,\n      },\n      {\n        accessor: 'id_name2_idx',\n        algorithm: 'btree',\n        columns: ['id', 'name2'] as const,\n      },\n      {\n        accessor: 'name_idx',\n        algorithm: 'btree',\n        columns: ['name'] as const,\n      },\n    ],\n  },\n  {\n    id: t.u32().primaryKey(),\n    name: t.string(),\n    name2: t.string().unique(),\n    married: t.bool(),\n    id2: t.identity(),\n    age: t.u32(),\n    age2: t.u16(),\n  }\n);\n\nconst spacetimedb = schema({ person });\n\nspacetimedb.init(ctx => {\n  ctx.db.person.id_name_idx.filter(1);\n  ctx.db.person.id_name_idx.filter([1, 'aname']);\n  // ctx.db.person.id_name2_idx.find\n\n  // @ts-expect-error id2 is not indexed, so this should not exist at all.\n  const _id2 = ctx.db.person.id2;\n\n  ctx.db.person.id.find(2);\n\n  // update() is allowed on primary key indexes\n  ctx.db.person.id.update({\n    id: 1,\n    name: '',\n    name2: '',\n    married: false,\n    id2: '' as any,\n    age: 0,\n    age2: 0,\n  });\n\n  // @ts-expect-error update() is NOT allowed on non-PK unique indexes\n  const _update = ctx.db.person.name2.update;\n});\n\n/**\n * Regression coverage for the declared-vs-resolved index split:\n * - declared table-level indexes must still produce typed accessors\n * - field-level indexes must still produce typed accessors\n * - non-indexed fields must not accidentally become index accessors\n */\nconst account = table(\n  {\n    indexes: [\n      {\n        accessor: 'byEmailAndOrg',\n        algorithm: 'btree',\n        columns: ['email', 'orgId'] as const,\n      },\n    ] as const,\n  },\n  {\n    accountId: t.u32(),\n    email: t.string().index('hash'),\n    orgId: t.u32(),\n    nickname: t.string(),\n  }\n);\n\nconst spacetimedbIndexSplit = schema({ account });\n\nspacetimedbIndexSplit.init(ctx => {\n  // Explicit table-level index accessor from `table({ indexes: [...] })`.\n  ctx.db.account.byEmailAndOrg.filter(['a@example.com', 1]);\n\n  // Field-level index accessor derived from column metadata.\n  ctx.db.account.email.filter('a@example.com');\n\n  // @ts-expect-error `nickname` is not indexed, so no index accessor should exist.\n  const _nickname = ctx.db.account.nickname;\n});\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/schema.ts",
    "content": "import { moduleHooks, type ModuleDefaultExport } from 'spacetime:sys@2.0';\nimport { CaseConversionPolicy, Lifecycle } from '../lib/autogen/types';\nimport {\n  type ParamsAsObject,\n  type ParamsObj,\n  type Reducer,\n  type ReducerCtx,\n} from '../lib/reducers';\nimport {\n  ModuleContext,\n  tableToSchema,\n  type TablesToSchema,\n  type UntypedSchemaDef,\n} from '../lib/schema';\nimport type { UntypedTableSchema } from '../lib/table_schema';\nimport { ColumnBuilder, TypeBuilder } from '../lib/type_builders';\nimport {\n  makeProcedureExport,\n  type ProcedureExport,\n  type ProcedureFn,\n  type ProcedureOpts,\n  type Procedures,\n} from './procedures';\nimport {\n  makeReducerExport,\n  type ReducerExport,\n  type ReducerOpts,\n  type Reducers,\n} from './reducers';\nimport { makeHooks } from './runtime';\n\nimport {\n  makeAnonViewExport,\n  makeViewExport,\n  type AnonViews,\n  type AnonymousViewFn,\n  type ViewExport,\n  type ViewFn,\n  type ViewOpts,\n  type ViewReturnTypeBuilder,\n  type Views,\n} from './views';\nimport type { UntypedTableDef } from '../lib/table';\n\nexport class SchemaInner<\n  S extends UntypedSchemaDef = UntypedSchemaDef,\n> extends ModuleContext {\n  schemaType: S;\n  existingFunctions = new Set<string>();\n  reducers: Reducers = [];\n  procedures: Procedures = [];\n  views: Views = [];\n  anonViews: AnonViews = [];\n  /**\n   * Maps ReducerExport objects to the name of the reducer.\n   * Used for resolving the reducers of scheduled tables.\n   */\n  functionExports: Map<\n    | ReducerExport<UntypedSchemaDef, any>\n    | ProcedureExport<UntypedSchemaDef, any, any>,\n    string\n  > = new Map();\n  pendingSchedules: PendingSchedule[] = [];\n\n  constructor(getSchemaType: (ctx: SchemaInner<S>) => S) {\n    super();\n    this.schemaType = getSchemaType(this);\n  }\n\n  defineFunction(name: string) {\n    if (this.existingFunctions.has(name)) {\n      throw new TypeError(\n        `There is already a reducer or procedure with the name '${name}'`\n      );\n    }\n    this.existingFunctions.add(name);\n  }\n\n  resolveSchedules() {\n    for (const { reducer, scheduleAtCol, tableName } of this.pendingSchedules) {\n      const functionName = this.functionExports.get(reducer());\n      if (functionName === undefined) {\n        const msg = `Table ${tableName} defines a schedule, but it seems like the associated function was not exported.`;\n        throw new TypeError(msg);\n      }\n      this.moduleDef.schedules.push({\n        sourceName: undefined,\n        tableName,\n        scheduleAtCol,\n        functionName,\n      });\n    }\n  }\n}\n\ntype PendingSchedule = UntypedTableSchema['schedule'] & { tableName: string };\n\n/**\n * The Schema class represents the database schema for a SpacetimeDB application.\n * It encapsulates the table definitions and typespace, and provides methods to define\n * reducers and lifecycle hooks.\n *\n * Schema has a generic parameter S which represents the inferred schema type. This type\n * is automatically inferred when creating a schema using the `schema()` function and is\n * used to type the database view in reducer contexts.\n *\n * The methods on this class are used to register reducers and lifecycle hooks\n * with the SpacetimeDB runtime. Theey forward to free functions that handle the actual\n * registration logic, but having them as methods on the Schema class helps with type inference.\n *\n * @template S - The inferred schema type of the SpacetimeDB module.\n *\n * @example\n * ```typescript\n * const spacetimedb = schema({\n *   user: table({}, userType),\n *   post: table({}, postType)\n * });\n * spacetimedb.reducer(\n *   'create_user',\n *   {  username: t.string(), email: t.string() },\n *   (ctx, { username, email }) => {\n *     ctx.db.user.insert({ username, email, created_at: ctx.timestamp });\n *     console.log(`User ${username} created by ${ctx.sender.identityId}`);\n *   }\n * );\n * ```\n */\n// TODO(cloutiertyler): It might be nice to have a way to access the types\n// for the tables from the schema object, e.g. `spacetimedb.user.type` would\n// be the type of the user table.\nexport class Schema<S extends UntypedSchemaDef> implements ModuleDefaultExport {\n  #ctx: SchemaInner<S>;\n\n  constructor(ctx: SchemaInner<S>) {\n    // TODO: TableSchema and TableDef should really be unified\n    this.#ctx = ctx;\n  }\n\n  [moduleHooks](exports: object) {\n    // if (!(hasOwn(exports, 'default') && exports.default instanceof Schema)) {\n    //   throw new TypeError('must export schema as default export');\n    // }\n    const registeredSchema = this.#ctx;\n    for (const [name, moduleExport] of Object.entries(exports)) {\n      if (name === 'default') continue;\n      if (!isModuleExport(moduleExport)) {\n        throw new TypeError(\n          'exporting something that is not a spacetime export'\n        );\n      }\n      checkExportContext(moduleExport, registeredSchema);\n      moduleExport[registerExport](registeredSchema, name);\n    }\n    registeredSchema.resolveSchedules();\n    return makeHooks(registeredSchema);\n  }\n\n  get schemaType(): S {\n    return this.#ctx.schemaType;\n  }\n\n  get moduleDef() {\n    return this.#ctx.moduleDef;\n  }\n\n  get typespace() {\n    return this.#ctx.typespace;\n  }\n\n  /**\n   * Defines a SpacetimeDB reducer function.\n   *\n   * Reducers are the primary way to modify the state of your SpacetimeDB application.\n   * They are atomic, meaning that either all operations within a reducer succeed,\n   * or none of them do.\n   *\n   * @template S - The inferred schema type of the SpacetimeDB module.\n   * @template Params - The type of the parameters object expected by the reducer.\n   *\n   * @param {Params} params - An object defining the parameters that the reducer accepts.\n   *                          Each key-value pair represents a parameter name and its corresponding\n   *                          {@link TypeBuilder} or {@link ColumnBuilder}.\n   * @param {(ctx: ReducerCtx<S>, payload: ParamsAsObject<Params>) => void} fn - The reducer function itself.\n   *   - `ctx`: The reducer context, providing access to `sender`, `timestamp`, `connection_id`, and `db`.\n   *   - `payload`: An object containing the arguments passed to the reducer, typed according to `params`.\n   *\n   * @example\n   * ```typescript\n   * // Define a reducer named 'create_user' that takes 'username' (string) and 'email' (string)\n   * export const create_user = spacetime.reducer(\n   *   {\n   *     username: t.string(),\n   *     email: t.string(),\n   *   },\n   *   (ctx, { username, email }) => {\n   *     // Access the 'user' table from the database view in the context\n   *     ctx.db.user.insert({ username, email, created_at: ctx.timestamp });\n   *     console.log(`User ${username} created by ${ctx.sender.identityId}`);\n   *   }\n   * );\n   * ```\n   */\n  reducer<Params extends ParamsObj>(\n    params: Params,\n    fn: Reducer<S, Params>\n  ): ReducerExport<S, Params>;\n  reducer(fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  reducer<Params extends ParamsObj>(\n    opts: ReducerOpts,\n    params: Params,\n    fn: Reducer<S, Params>\n  ): ReducerExport<S, Params>;\n  reducer(opts: ReducerOpts, fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  reducer<Params extends ParamsObj>(\n    ...args:\n      | [Params, Reducer<S, Params>]\n      | [Reducer<S, {}>]\n      | [ReducerOpts, Params, Reducer<S, Params>]\n      | [ReducerOpts, Reducer<S, {}>]\n  ): ReducerExport<S, Params> {\n    let opts: ReducerOpts | undefined,\n      params: Params = {} as Params,\n      fn: Reducer<S, Params>;\n    switch (args.length) {\n      case 1:\n        [fn] = args;\n        break;\n      case 2: {\n        let arg1;\n        [arg1, fn] = args;\n        if (typeof arg1.name === 'string') opts = arg1 as ReducerOpts;\n        else params = arg1 as Params;\n        break;\n      }\n      case 3:\n        [opts, params, fn] = args;\n        break;\n    }\n    return makeReducerExport(this.#ctx, opts, params, fn);\n  }\n\n  /**\n   * Registers an initialization reducer that runs when the SpacetimeDB module is published\n   * for the first time.\n   *\n   * This function is useful to set up any initial state of your database that is guaranteed\n   * to run only once, and before any other reducers or client connections.\n   *\n   * @template S - The inferred schema type of the SpacetimeDB module.\n   * @param {Reducer<S, {}>} fn - The initialization reducer function.\n   *  - `ctx`: The reducer context, providing access to `sender`, `timestamp`, `connection_id`, and `db`.\n   * @example\n   * ```typescript\n   * export const init = spacetime.init((ctx) => {\n   *   ctx.db.user.insert({ username: 'admin', email: 'admin@example.com' });\n   * });\n   * ```\n   */\n  init(fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  init(opts: ReducerOpts, fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  init(\n    ...args: [Reducer<S, {}>] | [ReducerOpts, Reducer<S, {}>]\n  ): ReducerExport<S, {}> {\n    let opts: ReducerOpts | undefined, fn: Reducer<S, {}>;\n    switch (args.length) {\n      case 1:\n        [fn] = args;\n        break;\n      case 2:\n        [opts, fn] = args;\n        break;\n    }\n    return makeReducerExport(this.#ctx, opts, {}, fn, Lifecycle.Init);\n  }\n\n  /**\n   * Registers a reducer to be called when a client connects to the SpacetimeDB module.\n   * This function allows you to define custom logic that should execute\n   * whenever a new client establishes a connection.\n   * @template S - The inferred schema type of the SpacetimeDB module.\n   *\n   * @param fn - The reducer function to execute on client connection.\n   *\n   * @example\n   * ```typescript\n   * export const onConnect = spacetime.clientConnected(\n   *   (ctx) => {\n   *     console.log(`Client ${ctx.connectionId} connected`);\n   *   }\n   * );\n   */\n  clientConnected(fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  clientConnected(opts: ReducerOpts, fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  clientConnected(\n    ...args: [Reducer<S, {}>] | [ReducerOpts, Reducer<S, {}>]\n  ): ReducerExport<S, {}> {\n    let opts: ReducerOpts | undefined, fn: Reducer<S, {}>;\n    switch (args.length) {\n      case 1:\n        [fn] = args;\n        break;\n      case 2:\n        [opts, fn] = args;\n        break;\n    }\n    return makeReducerExport(this.#ctx, opts, {}, fn, Lifecycle.OnConnect);\n  }\n\n  /**\n   * Registers a reducer to be called when a client disconnects from the SpacetimeDB module.\n   * This function allows you to define custom logic that should execute\n   * whenever a client disconnects.\n   * @template S - The inferred schema type of the SpacetimeDB module.\n   *\n   * @param fn - The reducer function to execute on client disconnection.\n   *\n   * @example\n   * ```typescript\n   * export const onDisconnect = spacetime.clientDisconnected(\n   *   (ctx) => {\n   *     console.log(`Client ${ctx.connectionId} disconnected`);\n   *   }\n   * );\n   * ```\n   */\n  clientDisconnected(fn: Reducer<S, {}>): ReducerExport<S, {}>;\n  clientDisconnected(\n    opts: ReducerOpts,\n    fn: Reducer<S, {}>\n  ): ReducerExport<S, {}>;\n  clientDisconnected(\n    ...args: [Reducer<S, {}>] | [ReducerOpts, Reducer<S, {}>]\n  ): ReducerExport<S, {}> {\n    let opts: ReducerOpts | undefined, fn: Reducer<S, {}>;\n    switch (args.length) {\n      case 1:\n        [fn] = args;\n        break;\n      case 2:\n        [opts, fn] = args;\n        break;\n    }\n    return makeReducerExport(this.#ctx, opts, {}, fn, Lifecycle.OnDisconnect);\n  }\n\n  view<Ret extends ViewReturnTypeBuilder, F extends ViewFn<S, {}, Ret>>(\n    opts: ViewOpts,\n    ret: Ret,\n    fn: F\n  ): ViewExport<F> {\n    return makeViewExport<S, {}, Ret, F>(this.#ctx, opts, {}, ret, fn);\n  }\n\n  // TODO: re-enable once parameterized views are supported in SQL\n  // view<Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   ret: Ret,\n  //   fn: ViewFn<S, {}, Ret>\n  // ): void;\n  // view<Params extends ParamsObj, Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   params: Params,\n  //   ret: Ret,\n  //   fn: ViewFn<S, {}, Ret>\n  // ): void;\n  // view<Params extends ParamsObj, Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   paramsOrRet: Ret | Params,\n  //   retOrFn: ViewFn<S, {}, Ret> | Ret,\n  //   maybeFn?: ViewFn<S, Params, Ret>\n  // ): void {\n  //   if (typeof retOrFn === 'function') {\n  //     defineView(name, false, {}, paramsOrRet as Ret, retOrFn);\n  //   } else {\n  //     defineView(name, false, paramsOrRet as Params, retOrFn, maybeFn!);\n  //   }\n  // }\n\n  anonymousView<\n    Ret extends ViewReturnTypeBuilder,\n    F extends AnonymousViewFn<S, {}, Ret>,\n  >(opts: ViewOpts, ret: Ret, fn: F): ViewExport<F> {\n    return makeAnonViewExport<S, {}, Ret, F>(this.#ctx, opts, {}, ret, fn);\n  }\n\n  // TODO: re-enable once parameterized views are supported in SQL\n  // anonymousView<Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   ret: Ret,\n  //   fn: AnonymousViewFn<S, {}, Ret>\n  // ): void;\n  // anonymousView<Params extends ParamsObj, Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   params: Params,\n  //   ret: Ret,\n  //   fn: AnonymousViewFn<S, {}, Ret>\n  // ): void;\n  // anonymousView<Params extends ParamsObj, Ret extends ViewReturnTypeBuilder>(\n  //   opts: ViewOpts,\n  //   paramsOrRet: Ret | Params,\n  //   retOrFn: AnonymousViewFn<S, {}, Ret> | Ret,\n  //   maybeFn?: AnonymousViewFn<S, Params, Ret>\n  // ): void {\n  //   if (typeof retOrFn === 'function') {\n  //     defineView(name, true, {}, paramsOrRet as Ret, retOrFn);\n  //   } else {\n  //     defineView(name, true, paramsOrRet as Params, retOrFn, maybeFn!);\n  //   }\n  // }\n\n  procedure<Params extends ParamsObj, Ret extends TypeBuilder<any, any>>(\n    params: Params,\n    ret: Ret,\n    fn: ProcedureFn<S, Params, Ret>\n  ): ProcedureFn<S, Params, Ret>;\n  procedure<Ret extends TypeBuilder<any, any>>(\n    ret: Ret,\n    fn: ProcedureFn<S, {}, Ret>\n  ): ProcedureFn<S, {}, Ret>;\n  procedure<Params extends ParamsObj, Ret extends TypeBuilder<any, any>>(\n    opts: ProcedureOpts,\n    params: Params,\n    ret: Ret,\n    fn: ProcedureFn<S, Params, Ret>\n  ): ProcedureFn<S, Params, Ret>;\n  procedure<Ret extends TypeBuilder<any, any>>(\n    opts: ProcedureOpts,\n    ret: Ret,\n    fn: ProcedureFn<S, {}, Ret>\n  ): ProcedureFn<S, {}, Ret>;\n  procedure<Params extends ParamsObj, Ret extends TypeBuilder<any, any>>(\n    ...args:\n      | [Params, Ret, ProcedureFn<S, Params, Ret>]\n      | [Ret, ProcedureFn<S, Params, Ret>]\n      | [ProcedureOpts, Params, Ret, ProcedureFn<S, Params, Ret>]\n      | [ProcedureOpts, Ret, ProcedureFn<S, Params, Ret>]\n  ): ProcedureExport<S, Params, Ret> {\n    let opts: ProcedureOpts | undefined,\n      params: Params = {} as Params,\n      ret: Ret,\n      fn: ProcedureFn<S, Params, Ret>;\n    switch (args.length) {\n      case 2:\n        [ret, fn] = args;\n        break;\n      case 3: {\n        let arg1;\n        [arg1, ret, fn] = args;\n        if (typeof arg1.name === 'string') opts = arg1 as ProcedureOpts;\n        else params = arg1 as Params;\n        break;\n      }\n      case 4:\n        [opts, params, ret, fn] = args;\n        break;\n    }\n    return makeProcedureExport(this.#ctx, opts, params, ret, fn);\n  }\n\n  /**\n   * Bundle multiple reducers, procedures, etc into one value to export.\n   * The name they will be exported with is their corresponding key in the `exports` argument.\n   */\n  exportGroup(exports: Record<string, ModuleExport>): ModuleExport {\n    return {\n      [exportContext]: this.#ctx,\n      [registerExport](ctx, _exportName) {\n        for (const [exportName, moduleExport] of Object.entries(exports)) {\n          checkExportContext(moduleExport, ctx);\n          moduleExport[registerExport](ctx, exportName);\n        }\n      },\n    };\n  }\n\n  clientVisibilityFilter = {\n    sql: (filter: string): ModuleExport => ({\n      [exportContext]: this.#ctx,\n      [registerExport](ctx, _exportName) {\n        ctx.moduleDef.rowLevelSecurity.push({ sql: filter });\n      },\n    }),\n  };\n}\n\nexport const registerExport = Symbol('SpacetimeDB.registerExport');\nexport const exportContext = Symbol('SpacetimeDB.exportContext');\n\nexport interface ModuleExport {\n  [registerExport](ctx: SchemaInner, exportName: string): void;\n  [exportContext]?: SchemaInner;\n}\n\nfunction isModuleExport(x: unknown): x is ModuleExport {\n  return (\n    (typeof x === 'function' || typeof x === 'object') &&\n    x !== null &&\n    registerExport in x\n  );\n}\n\n/** Verify that the ModuleContext that `exp` comes from is the same as `schema` */\nfunction checkExportContext(exp: ModuleExport, schema: SchemaInner) {\n  if (exp[exportContext] != null && exp[exportContext] !== schema) {\n    throw new TypeError('multiple schemas are not supported');\n  }\n}\n\n/**\n * Extracts the inferred schema type from a Schema instance\n */\nexport type InferSchema<SchemaDef extends Schema<any>> =\n  SchemaDef extends Schema<infer S> ? S : never;\n\n/**\n * Creates a schema from table definitions\n * @param handles - Array of table handles created by table() function\n * @returns ColumnBuilder representing the complete database schema\n * @example\n * ```ts\n * const spacetimedb = schema({\n *   user: table({}, userType),\n *   post: table({}, postType)\n * });\n * ```\n */\n/**\n * Module-level settings that can be passed to `schema()`.\n */\nexport interface ModuleSettings {\n  /**\n   * The case conversion policy for this module.\n   * Defaults to `SnakeCase` if not specified.\n   *\n   * @example\n   * ```ts\n   * export default schema({\n   *   player,\n   * }, { CASE_CONVERSION_POLICY: CaseConversionPolicy.None });\n   * ```\n   */\n  CASE_CONVERSION_POLICY?: CaseConversionPolicy;\n}\n\nexport function schema<const H extends Record<string, UntypedTableSchema>>(\n  tables: H,\n  moduleSettings?: ModuleSettings\n): Schema<TablesToSchema<H>> {\n  const ctx = new SchemaInner<TablesToSchema<H>>(ctx => {\n    // Apply module settings.\n    if (moduleSettings?.CASE_CONVERSION_POLICY != null) {\n      ctx.setCaseConversionPolicy(moduleSettings.CASE_CONVERSION_POLICY);\n    }\n\n    const tableSchemas: Record<string, UntypedTableDef> = {};\n    for (const [accName, table] of Object.entries(tables)) {\n      const tableDef = table.tableDef(ctx, accName);\n      tableSchemas[accName] = tableToSchema(accName, table, tableDef);\n      ctx.moduleDef.tables.push(tableDef);\n      if (table.schedule) {\n        ctx.pendingSchedules.push({\n          ...table.schedule,\n          tableName: tableDef.sourceName,\n        });\n      }\n      if (table.tableName) {\n        ctx.moduleDef.explicitNames.entries.push({\n          tag: 'Table',\n          value: {\n            sourceName: accName,\n            canonicalName: table.tableName,\n          },\n        });\n      }\n    }\n    return { tables: tableSchemas } as TablesToSchema<H>;\n  });\n\n  return new Schema(ctx);\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/sys.d.ts",
    "content": "declare module 'spacetime:sys@2.0' {\n  export type u8 = number;\n  export type u16 = number;\n  export type u32 = number;\n  export type u64 = bigint;\n  export type u128 = bigint;\n  export type u256 = bigint;\n\n  export const moduleHooks: unique symbol;\n\n  interface ModuleDefaultExport {\n    [moduleHooks](exports: object): ModuleHooks;\n  }\n\n  export interface ModuleHooks {\n    __describe_module__(): Uint8Array;\n\n    __get_error_constructor__(code: number): new (msg: string) => Error;\n    __sender_error_class__: new (msg: string) => Error;\n\n    __call_reducer__(\n      reducerId: u32,\n      sender: u256,\n      connId: u128,\n      timestamp: bigint,\n      argsBuf: DataView\n    ): void;\n\n    __call_view__(id: u32, sender: u256, args: Uint8Array): Uint8Array | object;\n\n    __call_view_anon__(id: u32, args: Uint8Array): Uint8Array | object;\n\n    __call_procedure__(\n      id: u32,\n      sender: u256,\n      connection_id: u128,\n      timestamp: bigint,\n      args: Uint8Array\n    ): Uint8Array;\n  }\n\n  export function register_hooks(hooks: ModuleHooks);\n\n  export function table_id_from_name(name: string): u32;\n  export function index_id_from_name(name: string): u32;\n  export function datastore_table_row_count(table_id: u32): u64;\n  export function datastore_table_scan_bsatn(table_id: u32): u32;\n  export function datastore_index_scan_range_bsatn(\n    index_id: u32,\n    buf: ArrayBuffer,\n    prefix_len: u32,\n    prefix_elems: u16,\n    rstart_len: u32,\n    rend_len: u32\n  ): u32;\n  export function row_iter_bsatn_advance(iter: u32, buffer: ArrayBuffer): u32;\n  export function row_iter_bsatn_close(iter: u32): void;\n  export function datastore_insert_bsatn(\n    table_id: u32,\n    row: ArrayBuffer,\n    row_len: u32\n  ): u32;\n  export function datastore_update_bsatn(\n    table_id: u32,\n    index_id: u32,\n    row: ArrayBuffer,\n    row_len: u32\n  ): u32;\n  export function datastore_delete_by_index_scan_range_bsatn(\n    index_id: u32,\n    buf: ArrayBuffer,\n    prefix_len: u32,\n    prefix_elems: u16,\n    rstart_len: u32,\n    rend_len: u32\n  ): u32;\n  export function datastore_delete_all_by_eq_bsatn(\n    table_id: u32,\n    relation: ArrayBuffer,\n    relation_len: u32\n  ): u32;\n  export function volatile_nonatomic_schedule_immediate(\n    reducer_name: string,\n    args: Uint8Array\n  ): void;\n  export function console_log(level: u8, message: string): void;\n  export function console_timer_start(name: string): u32;\n  export function console_timer_end(span_id: u32): void;\n  export function identity(): u256;\n  export function get_jwt_payload(connection_id: u128): Uint8Array;\n\n  export function procedure_http_request(\n    request: Uint8Array,\n    body: Uint8Array | string\n  ): [response: Uint8Array, body: Uint8Array];\n\n  export function procedure_start_mut_tx(): bigint;\n\n  export function procedure_commit_mut_tx();\n\n  export function procedure_abort_mut_tx();\n\n  export function datastore_index_scan_point_bsatn(\n    index_id: u32,\n    point: ArrayBuffer,\n    point_len: u32\n  ): u32;\n\n  export function datastore_delete_by_index_scan_point_bsatn(\n    index_id: u32,\n    point: ArrayBuffer,\n    point_len: u32\n  ): u32;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/view.test-d.ts",
    "content": "import { schema } from './schema';\nimport { table } from '../lib/table';\nimport t from '../lib/type_builders';\n\nconst person = table(\n  {\n    name: 'person',\n    indexes: [\n      {\n        accessor: 'name_id_idx',\n        name: 'name_id_idx',\n        algorithm: 'btree',\n        columns: ['name', 'id'] as const,\n      },\n    ],\n  },\n  {\n    id: t.u32().primaryKey(),\n    name: t.string(),\n  }\n);\n\nconst personWithExtra = table(\n  { name: 'personWithExtra' },\n  {\n    id: t.u32(),\n    name: t.string(),\n    extraField: t.string(),\n  }\n);\n\nconst personWithMissing = table(\n  { name: 'personWithMissing' },\n  {\n    id: t.u32(),\n  }\n);\n\nconst personReordered = table(\n  { name: 'personReordered' },\n  {\n    name: t.string(),\n    id: t.u32(),\n  }\n);\n\nconst order = table(\n  {\n    name: 'order',\n    indexes: [\n      {\n        accessor: 'id_person_id',\n        name: 'id_person_id', // We are adding this to make sure `person_id` still isn't considered indexed.\n        algorithm: 'btree',\n        columns: ['id', 'person_id'] as const,\n      },\n    ],\n  },\n  {\n    id: t.u32().primaryKey(),\n    person_name: t.string().index(),\n    person_id: t.u32(),\n  }\n);\n\nconst spacetime = schema({\n  person,\n  order,\n  personWithExtra,\n  personReordered,\n  personWithMissing,\n});\n\nconst arrayRetValue = t.array(person.rowType);\nconst optionalPerson = t.option(person.rowType);\n\nspacetime.anonymousView({ name: 'v1', public: true }, arrayRetValue, ctx => {\n  return ctx.from.person.build();\n});\n\nspacetime.anonymousView(\n  { name: 'optionalPerson', public: true },\n  optionalPerson,\n  ctx => {\n    return ctx.db.person.iter().next().value;\n  }\n);\n\nspacetime.anonymousView(\n  { name: 'optionalPersonWrong', public: true },\n  optionalPerson,\n  // @ts-expect-error returns a value of the wrong type.\n  ctx => {\n    return ctx.db.order.iter().next().value;\n  }\n);\n\n// Extra fields are only an issue for queries.\nspacetime.anonymousView(\n  { name: 'optionalPersonWithExtra', public: true },\n  optionalPerson,\n  ctx => {\n    return ctx.db.personWithExtra.iter().next().value;\n  }\n);\n\nspacetime.anonymousView(\n  { name: 'v2', public: true },\n  arrayRetValue,\n  // @ts-expect-error returns a query of the wrong type.\n  ctx => {\n    return ctx.from.order.build();\n  }\n);\n\n// For queries, we can't return rows with extra fields.\nspacetime.anonymousView(\n  { name: 'v3', public: true },\n  arrayRetValue,\n  // @ts-expect-error returns a query of the wrong type.\n  ctx => {\n    return ctx.from.personWithExtra.build();\n  }\n);\n\n// Ideally this would fail, since we depend on the field ordering for serialization.\nspacetime.anonymousView(\n  { name: 'reorderedPerson', public: true },\n  arrayRetValue,\n  // Comment this out if we can fix the types.\n  // // @ts-expect-error returns a query of the wrong type.\n  ctx => {\n    return ctx.from.personReordered.build();\n  }\n);\n\n// Fails because it is missing a field.\nspacetime.anonymousView(\n  { name: 'missingField', public: true },\n  arrayRetValue,\n  // @ts-expect-error returns a query of the wrong type.\n  ctx => {\n    return ctx.from.personWithMissing.build();\n  }\n);\n\nspacetime.anonymousView({ name: 'v4', public: true }, arrayRetValue, ctx => {\n  // @ts-expect-error returns a query of the wrong type.\n  const _invalid = ctx.from.person.where(row => row.id.eq('string')).build();\n  const _columnEqs = ctx.from.person.where(row => row.id.eq(row.id)).build();\n  return ctx.from.person.where(row => row.id.eq(5)).build();\n});\n\nspacetime.anonymousView({ name: 'v5', public: true }, arrayRetValue, ctx => {\n  const _nonIndexedSemijoin = ctx.from.person\n    .where(row => row.id.eq(5))\n    // @ts-expect-error person_id is not indexed.\n    .leftSemijoin(ctx.from.order, (p, o) => p.id.eq(o.person_id))\n    .build();\n  const _fromCompositeIndex = ctx.from.person\n    .where(row => row.id.eq(5))\n    .leftSemijoin(ctx.from.order, (p, o) => p.name.eq(o.person_name))\n    .build();\n  const _mixedScopeAndInJoinPredicate = ctx.from.person\n    // @ts-expect-error semijoin on(...) only supports one table scope for and/or clauses.\n    .leftSemijoin(ctx.from.order, (p, o) => p.id.eq(o.id).and(o.id.eq(5)))\n    .build();\n  return ctx.from.person\n    .where(row => row.id.eq(5))\n    .leftSemijoin(ctx.from.order, (p, o) => p.id.eq(o.id))\n    .build();\n});\n"
  },
  {
    "path": "crates/bindings-typescript/src/server/views.ts",
    "content": "import {\n  AlgebraicType,\n  ProductType,\n  type AlgebraicTypeVariants,\n  type Deserializer,\n  type Serializer,\n} from '../lib/algebraic_type';\nimport type { Identity } from '../lib/identity';\nimport type { OptionAlgebraicType } from '../lib/option';\nimport type { ParamsObj } from '../lib/reducers';\nimport { type UntypedSchemaDef } from '../lib/schema';\nimport {\n  RowBuilder,\n  type Infer,\n  type InferSpacetimeTypeOfTypeBuilder,\n  type InferTypeOfRow,\n  type TypeBuilder,\n} from '../lib/type_builders';\nimport { bsatnBaseSize, toPascalCase } from '../lib/util';\nimport type { ReadonlyDbView } from './db_view';\nimport { type QueryBuilder, type RowTypedQuery } from './query';\nimport {\n  exportContext,\n  registerExport,\n  type ModuleExport,\n  type SchemaInner,\n} from './schema';\n\nexport type ViewExport<ViewFn> = ViewFn & ModuleExport;\n\nexport function makeViewExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends ViewReturnTypeBuilder,\n  F extends ViewFn<S, Params, Ret>,\n>(\n  ctx: SchemaInner,\n  opts: ViewOpts,\n  params: Params,\n  ret: Ret,\n  fn: F\n): ViewExport<F> {\n  const viewExport =\n    // @ts-expect-error typescript incorrectly says Function#bind requires an argument.\n    fn.bind() as ViewExport<F>;\n  viewExport[exportContext] = ctx;\n  viewExport[registerExport] = (ctx, exportName) => {\n    registerView(ctx, opts, exportName, false, params, ret, fn);\n  };\n  return viewExport;\n}\n\nexport function makeAnonViewExport<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends ViewReturnTypeBuilder,\n  F extends AnonymousViewFn<S, Params, Ret>,\n>(\n  ctx: SchemaInner,\n  opts: ViewOpts,\n  params: Params,\n  ret: Ret,\n  fn: F\n): ViewExport<F> {\n  const viewExport =\n    // @ts-expect-error typescript incorrectly says Function#bind requires an argument.\n    fn.bind() as ViewExport<F>;\n  viewExport[exportContext] = ctx;\n  viewExport[registerExport] = (ctx, exportName) => {\n    registerView(ctx, opts, exportName, true, params, ret, fn);\n  };\n  return viewExport;\n}\n\nexport type ViewCtx<S extends UntypedSchemaDef> = Readonly<{\n  sender: Identity;\n  db: ReadonlyDbView<S>;\n  from: QueryBuilder<S>;\n}>;\n\nexport type AnonymousViewCtx<S extends UntypedSchemaDef> = Readonly<{\n  db: ReadonlyDbView<S>;\n  from: QueryBuilder<S>;\n}>;\n\nexport type ViewOpts = {\n  name?: string;\n  public: true;\n};\n\ntype FlattenedArray<T> = T extends readonly (infer E)[] ? E : never;\n\n// // If we allowed functions to return either.\n// type ViewReturn<Ret extends ViewReturnTypeBuilder> =\n//   | Infer<Ret>\n//   | RowTypedQuery<FlattenedArray<Infer<Ret>>>;\n\nexport type ViewFn<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends ViewReturnTypeBuilder,\n> =\n  | ((ctx: ViewCtx<S>, params: InferTypeOfRow<Params>) => Infer<Ret>)\n  | ((\n      ctx: ViewCtx<S>,\n      params: InferTypeOfRow<Params>\n    ) => RowTypedQuery<FlattenedArray<Infer<Ret>>, ExtractArrayProduct<Ret>>);\n\nexport type AnonymousViewFn<\n  S extends UntypedSchemaDef,\n  Params extends ParamsObj,\n  Ret extends ViewReturnTypeBuilder,\n> =\n  | ((ctx: AnonymousViewCtx<S>, params: InferTypeOfRow<Params>) => Infer<Ret>)\n  | ((\n      ctx: AnonymousViewCtx<S>,\n      params: InferTypeOfRow<Params>\n    ) => RowTypedQuery<FlattenedArray<Infer<Ret>>, ExtractArrayProduct<Ret>>);\n\nexport type ViewReturnTypeBuilder =\n  | TypeBuilder<\n      readonly object[],\n      { tag: 'Array'; value: AlgebraicTypeVariants.Product }\n    >\n  | TypeBuilder<\n      object | undefined,\n      OptionAlgebraicType<AlgebraicTypeVariants.Product>\n    >;\n\nexport function registerView<\n  S extends UntypedSchemaDef,\n  const Anonymous extends boolean,\n  Params extends ParamsObj,\n  Ret extends ViewReturnTypeBuilder,\n>(\n  ctx: SchemaInner,\n  opts: ViewOpts,\n  exportName: string,\n  anon: Anonymous,\n  params: Params,\n  ret: Ret,\n  fn: Anonymous extends true\n    ? AnonymousViewFn<S, Params, Ret>\n    : ViewFn<S, Params, Ret>\n) {\n  const paramsBuilder = new RowBuilder(params, toPascalCase(exportName));\n\n  // Register return types if they are product types\n  let returnType = ctx.registerTypesRecursively(ret).algebraicType;\n\n  const { typespace } = ctx;\n\n  const { value: paramType } = ctx.resolveType(\n    ctx.registerTypesRecursively(paramsBuilder)\n  );\n\n  ctx.moduleDef.views.push({\n    sourceName: exportName,\n    index: (anon ? ctx.anonViews : ctx.views).length,\n    isPublic: opts.public,\n    isAnonymous: anon,\n    params: paramType,\n    returnType,\n  });\n\n  if (opts.name != null) {\n    ctx.moduleDef.explicitNames.entries.push({\n      tag: 'Function',\n      value: {\n        sourceName: exportName,\n        canonicalName: opts.name,\n      },\n    });\n  }\n\n  // If it is an option, we wrap the function to make the return look like an array.\n  if (returnType.tag == 'Sum') {\n    const originalFn = fn;\n    fn = ((ctx: ViewCtx<S>, args: InferTypeOfRow<Params>) => {\n      const ret = originalFn(ctx, args);\n      return ret == null ? [] : [ret];\n    }) as any;\n    returnType = AlgebraicType.Array(\n      returnType.value.variants[0].algebraicType\n    );\n  }\n\n  (anon ? ctx.anonViews : ctx.views).push({\n    fn,\n    deserializeParams: ProductType.makeDeserializer(paramType, typespace),\n    serializeReturn: AlgebraicType.makeSerializer(returnType, typespace),\n    returnTypeBaseSize: bsatnBaseSize(typespace, returnType),\n  });\n}\n\ntype ViewInfo<F> = {\n  fn: F;\n  deserializeParams: Deserializer<any>;\n  serializeReturn: Serializer<any>;\n  returnTypeBaseSize: number;\n};\n\nexport type Views = ViewInfo<ViewFn<any, any, any>>[];\nexport type AnonViews = ViewInfo<AnonymousViewFn<any, any, any>>[];\n\n// A helper to get the product type out of a type builder.\n// This is only non-never if the type builder is an array.\ntype ExtractArrayProduct<T extends TypeBuilder<any, any>> =\n  InferSpacetimeTypeOfTypeBuilder<T> extends { tag: 'Array'; value: infer V }\n    ? V extends { tag: 'Product'; value: infer P }\n      ? P\n      : never\n    : never;\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/SpacetimeDBProvider.ts",
    "content": "import { setContext, onDestroy } from 'svelte';\nimport { writable, type Writable } from 'svelte/store';\nimport {\n  DbConnectionBuilder,\n  type DbConnectionImpl,\n  type ErrorContextInterface,\n  type RemoteModuleOf,\n} from '../sdk/db_connection_impl';\nimport { ConnectionId } from '../lib/connection_id';\nimport {\n  SPACETIMEDB_CONTEXT_KEY,\n  type ConnectionState,\n} from './connection_state';\n\nlet connRef: DbConnectionImpl<any> | null = null;\nlet cleanupTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\nexport function createSpacetimeDBProvider<\n  DbConnection extends DbConnectionImpl<any>,\n>(\n  connectionBuilder: DbConnectionBuilder<DbConnection>\n): Writable<ConnectionState> {\n  const getConnection = () => connRef as DbConnection | null;\n\n  const store = writable<ConnectionState>({\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n    getConnection: getConnection as ConnectionState['getConnection'],\n  });\n\n  if (cleanupTimeoutId) {\n    clearTimeout(cleanupTimeoutId);\n    cleanupTimeoutId = null;\n  }\n\n  if (!connRef) {\n    connRef = connectionBuilder.build();\n  }\n\n  const onConnect = (conn: DbConnection) => {\n    store.update(s => ({\n      ...s,\n      isActive: conn.isActive,\n      identity: conn.identity,\n      token: conn.token,\n      connectionId: conn.connectionId,\n    }));\n  };\n\n  const onDisconnect = (\n    ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>\n  ) => {\n    store.update(s => ({\n      ...s,\n      isActive: ctx.isActive,\n    }));\n  };\n\n  const onConnectError = (\n    ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n    err: Error\n  ) => {\n    store.update(s => ({\n      ...s,\n      isActive: ctx.isActive,\n      connectionError: err,\n    }));\n  };\n\n  connectionBuilder.onConnect(onConnect);\n  connectionBuilder.onDisconnect(onDisconnect);\n  connectionBuilder.onConnectError(onConnectError);\n\n  const conn = connRef;\n  store.update(s => ({\n    ...s,\n    isActive: conn.isActive,\n    identity: conn.identity,\n    token: conn.token,\n    connectionId: conn.connectionId,\n  }));\n\n  setContext(SPACETIMEDB_CONTEXT_KEY, store);\n\n  onDestroy(() => {\n    connRef?.removeOnConnect(onConnect as any);\n    connRef?.removeOnDisconnect(onDisconnect as any);\n    connRef?.removeOnConnectError(onConnectError as any);\n\n    cleanupTimeoutId = setTimeout(() => {\n      connRef?.disconnect();\n      connRef = null;\n      cleanupTimeoutId = null;\n    }, 0);\n  });\n\n  return store;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/connection_state.ts",
    "content": "import type { ConnectionId } from '../lib/connection_id';\nimport type { Identity } from '../lib/identity';\nimport type { DbConnectionImpl } from '../sdk/db_connection_impl';\n\nexport type ConnectionState = {\n  isActive: boolean;\n  identity?: Identity;\n  token?: string;\n  connectionId: ConnectionId;\n  connectionError?: Error;\n  getConnection<\n    DbConnection extends DbConnectionImpl<any>,\n  >(): DbConnection | null;\n};\n\nexport const SPACETIMEDB_CONTEXT_KEY = Symbol('spacetimedb');\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/index.ts",
    "content": "export * from './SpacetimeDBProvider.ts';\nexport { useSpacetimeDB } from './useSpacetimeDB.ts';\nexport { useTable } from './useTable.ts';\nexport { useReducer } from './useReducer.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/useReducer.ts",
    "content": "import { onDestroy } from 'svelte';\nimport { get } from 'svelte/store';\nimport type { InferTypeOfRow } from '../lib/type_builders';\nimport type { UntypedReducerDef } from '../sdk/reducers';\nimport { useSpacetimeDB } from './useSpacetimeDB';\nimport type { Prettify } from '../lib/type_util';\n\ntype IsEmptyObject<T> = [keyof T] extends [never] ? true : false;\ntype MaybeParams<T> = IsEmptyObject<T> extends true ? [] : [params: T];\n\ntype ParamsType<R extends UntypedReducerDef> = MaybeParams<\n  Prettify<InferTypeOfRow<R['params']>>\n>;\n\nexport function useReducer<ReducerDef extends UntypedReducerDef>(\n  reducerDef: ReducerDef\n): (...params: ParamsType<ReducerDef>) => Promise<void> {\n  const connectionStore = useSpacetimeDB();\n  const reducerName = reducerDef.accessorName;\n\n  // Holds calls made before the connection exists\n  const queueRef: {\n    params: ParamsType<ReducerDef>;\n    resolve: () => void;\n    reject: (err: unknown) => void;\n  }[] = [];\n\n  // Flush when we finally have a connection\n  const unsubscribe = connectionStore.subscribe(state => {\n    const conn = state.getConnection();\n    if (!conn) return;\n\n    const fn = (conn.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => Promise<void>;\n    if (queueRef.length) {\n      const pending = queueRef.splice(0);\n      for (const item of pending) {\n        fn(...item.params).then(item.resolve, item.reject);\n      }\n    }\n  });\n\n  onDestroy(() => {\n    unsubscribe();\n  });\n\n  return (...params: ParamsType<ReducerDef>) => {\n    const state = get(connectionStore);\n    const conn = state.getConnection();\n    if (!conn) {\n      return new Promise<void>((resolve, reject) => {\n        queueRef.push({ params, resolve, reject });\n      });\n    }\n    const fn = (conn.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => Promise<void>;\n    return fn(...params);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/useSpacetimeDB.ts",
    "content": "import { getContext } from 'svelte';\nimport type { Writable } from 'svelte/store';\nimport {\n  SPACETIMEDB_CONTEXT_KEY,\n  type ConnectionState,\n} from './connection_state';\n\n// Throws an error if used outside of a SpacetimeDBProvider\nexport function useSpacetimeDB(): Writable<ConnectionState> {\n  const context = getContext<Writable<ConnectionState> | undefined>(\n    SPACETIMEDB_CONTEXT_KEY\n  );\n\n  if (!context) {\n    throw new Error(\n      'useSpacetimeDB must be used within a component that called createSpacetimeDBProvider. ' +\n        'Did you forget to call `createSpacetimeDBProvider` in a parent component?'\n    );\n  }\n\n  return context;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/svelte/useTable.ts",
    "content": "import { onDestroy } from 'svelte';\nimport { writable, get, type Readable } from 'svelte/store';\nimport { useSpacetimeDB } from './useSpacetimeDB';\nimport type { EventContextInterface } from '../sdk/db_connection_impl';\nimport type { UntypedRemoteModule } from '../sdk/spacetime_module';\nimport type { RowType, UntypedTableDef } from '../lib/table';\nimport type { Prettify } from '../lib/type_util';\nimport {\n  type BooleanExpr,\n  evaluateBooleanExpr,\n  getQueryAccessorName,\n  getQueryWhereClause,\n  type Query,\n  toSql,\n} from '../lib/query';\n\nexport interface UseTableCallbacks<RowType> {\n  onInsert?: (row: RowType) => void;\n  onDelete?: (row: RowType) => void;\n  onUpdate?: (oldRow: RowType, newRow: RowType) => void;\n}\n\ntype MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';\n\nfunction classifyMembership(\n  whereExpr: BooleanExpr<any> | undefined,\n  oldRow: Record<string, any>,\n  newRow: Record<string, any>\n): MembershipChange {\n  if (!whereExpr) return 'stayIn';\n  const oldIn = evaluateBooleanExpr(whereExpr, oldRow);\n  const newIn = evaluateBooleanExpr(whereExpr, newRow);\n  if (oldIn && !newIn) return 'leave';\n  if (!oldIn && newIn) return 'enter';\n  if (oldIn && newIn) return 'stayIn';\n  return 'stayOut';\n}\n\n/**\n * Svelte hook to subscribe to a table in SpacetimeDB and receive live updates.\n *\n * Accepts a query builder expression as the first argument:\n * - `tables.user` — subscribe to all rows\n * - `tables.user.where(r => r.online.eq(true))` — subscribe with a filter\n *\n * @param query - A query builder expression (table reference or filtered query).\n * @param callbacks - Optional callbacks for row insert, delete, and update events.\n * @returns A tuple of [rows, isReady].\n */\nexport function useTable<TableDef extends UntypedTableDef>(\n  query: Query<TableDef>,\n  callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>\n): [Readable<readonly Prettify<RowType<TableDef>>[]>, Readable<boolean>] {\n  type Row = RowType<TableDef>;\n  const accessorName = getQueryAccessorName(query);\n  const whereExpr = getQueryWhereClause(query);\n  const querySql = toSql(query);\n\n  let connectionStore;\n  try {\n    connectionStore = useSpacetimeDB();\n  } catch {\n    throw new Error(\n      'Could not find SpacetimeDB client! Did you forget to call ' +\n        '`createSpacetimeDBProvider`? `useTable` must be used in a Svelte component tree ' +\n        'under a component that called `createSpacetimeDBProvider`.'\n    );\n  }\n\n  const rows = writable<readonly Prettify<Row>[]>([]);\n  const isReady = writable(false);\n\n  let latestTransactionEvent: any = null;\n  let unsubscribeFromTable: (() => void) | null = null;\n  let subscriptionHandle: { unsubscribe: () => void } | null = null;\n\n  const computeFilteredRows = (): readonly Prettify<Row>[] => {\n    const state = get(connectionStore);\n    const connection = state.getConnection();\n    if (!connection) return [];\n\n    const table = connection.db[accessorName];\n    if (!table) return [];\n\n    const allRows = Array.from(table.iter()) as Row[];\n    if (whereExpr) {\n      return allRows.filter(row =>\n        evaluateBooleanExpr(whereExpr, row as Record<string, any>)\n      ) as Prettify<Row>[];\n    }\n    return allRows as Prettify<Row>[];\n  };\n\n  const setupTableListeners = () => {\n    const state = get(connectionStore);\n    const connection = state.getConnection();\n    if (!connection) return;\n\n    const table = connection.db[accessorName];\n    if (!table) return;\n\n    const onInsert = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) return;\n      callbacks?.onInsert?.(row);\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.set(computeFilteredRows());\n      }\n    };\n\n    const onDelete = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) return;\n      callbacks?.onDelete?.(row);\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.set(computeFilteredRows());\n      }\n    };\n\n    const onUpdate = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      oldRow: any,\n      newRow: any\n    ) => {\n      const change = classifyMembership(whereExpr, oldRow, newRow);\n\n      switch (change) {\n        case 'leave':\n          callbacks?.onDelete?.(oldRow);\n          break;\n        case 'enter':\n          callbacks?.onInsert?.(newRow);\n          break;\n        case 'stayIn':\n          callbacks?.onUpdate?.(oldRow, newRow);\n          break;\n        case 'stayOut':\n          return;\n      }\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.set(computeFilteredRows());\n      }\n    };\n\n    table.onInsert(onInsert);\n    table.onDelete(onDelete);\n    table.onUpdate?.(onUpdate);\n\n    return () => {\n      table.removeOnInsert(onInsert);\n      table.removeOnDelete(onDelete);\n      table.removeOnUpdate?.(onUpdate);\n    };\n  };\n\n  const setupSubscription = () => {\n    const state = get(connectionStore);\n    const connection = state.getConnection();\n    if (!connection) return;\n\n    subscriptionHandle = connection\n      .subscriptionBuilder()\n      .onApplied(() => {\n        isReady.set(true);\n        rows.set(computeFilteredRows());\n      })\n      .subscribe(querySql);\n  };\n\n  const unsubscribeConnection = connectionStore.subscribe(state => {\n    // clean up existing listeners and subscriptions first\n    if (unsubscribeFromTable) {\n      unsubscribeFromTable();\n      unsubscribeFromTable = null;\n    }\n    if (subscriptionHandle) {\n      subscriptionHandle.unsubscribe();\n      subscriptionHandle = null;\n    }\n\n    if (state.isActive) {\n      unsubscribeFromTable = setupTableListeners() || null;\n      setupSubscription();\n      rows.set(computeFilteredRows());\n    } else {\n      isReady.set(false);\n      rows.set([]);\n    }\n  });\n\n  onDestroy(() => {\n    unsubscribeConnection();\n    unsubscribeFromTable?.();\n    subscriptionHandle?.unsubscribe();\n    latestTransactionEvent = null;\n  });\n\n  return [{ subscribe: rows.subscribe }, { subscribe: isReady.subscribe }];\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/tanstack/SpacetimeDBQueryClient.ts",
    "content": "import type {\n  QueryClient,\n  QueryKey,\n  QueryFunction,\n} from '@tanstack/react-query';\nimport {\n  type Query,\n  toSql,\n  type BooleanExpr,\n  evaluateBooleanExpr,\n  getQueryAccessorName,\n  getQueryWhereClause,\n} from '../lib/query';\n\ntype QueryInput = Query<any>;\n\nconst queryRegistry = new Map<\n  string,\n  { accessorName: string; whereExpr?: BooleanExpr<any> }\n>();\n\nexport interface SpacetimeDBQueryOptions {\n  queryKey: readonly ['spacetimedb', string, string];\n  staleTime: number;\n}\n\nexport interface SpacetimeDBQueryOptionsSkipped\n  extends SpacetimeDBQueryOptions {\n  enabled: false;\n}\n\n// creates query options for useQuery/useSuspenseQuery.\n// useQuery(spacetimeDBQuery(tables.person));\n// useQuery(spacetimeDBQuery(tables.user.where(r => r.online.eq(true))));\n// useQuery(spacetimeDBQuery(condition ? tables.user : 'skip'));\nexport function spacetimeDBQuery(\n  queryOrSkip: 'skip'\n): SpacetimeDBQueryOptionsSkipped;\n\nexport function spacetimeDBQuery(query: QueryInput): SpacetimeDBQueryOptions;\n\nexport function spacetimeDBQuery(\n  queryOrSkip: QueryInput | 'skip'\n): SpacetimeDBQueryOptions | SpacetimeDBQueryOptionsSkipped {\n  if (queryOrSkip === 'skip') {\n    return {\n      queryKey: ['spacetimedb', '', 'skip'] as const,\n      staleTime: Infinity,\n      enabled: false,\n    };\n  }\n\n  const query = queryOrSkip;\n  const accessorName = getQueryAccessorName(query);\n  const whereExpr = getQueryWhereClause(query);\n  const querySql = toSql(query);\n\n  queryRegistry.set(querySql, { accessorName, whereExpr });\n\n  return {\n    queryKey: ['spacetimedb', accessorName, querySql] as const,\n    staleTime: Infinity,\n  };\n}\n\ninterface SpacetimeConnection {\n  db: Record<string, any>;\n  subscriptionBuilder: () => {\n    onApplied: (cb: () => void) => any;\n    subscribe: (query: string) => { unsubscribe: () => void };\n  };\n}\n\ninterface SubscriptionState {\n  unsubscribe: () => void;\n  tableInstance: any;\n  applied: boolean;\n}\n\n// push updates to cache via setQueryData when SpacetimeDB data changes\nexport class SpacetimeDBQueryClient {\n  private connection: SpacetimeConnection | null = null;\n  private queryClient: QueryClient | null = null;\n  private subscriptions = new Map<string, SubscriptionState>();\n  private pendingQueries = new Map<\n    string,\n    Array<{\n      resolve: (data: any[]) => void;\n      querySql: string;\n      whereExpr?: BooleanExpr<any>;\n    }>\n  >();\n  private cacheUnsubscribe: (() => void) | null = null;\n\n  // set connection, called on onConnect callback\n  setConnection(connection: SpacetimeConnection): void {\n    this.connection = connection;\n    this.processPendingQueries();\n    this.hydrateSubscriptions();\n  }\n\n  connect(queryClient: QueryClient): void {\n    this.queryClient = queryClient;\n\n    this.cacheUnsubscribe = queryClient\n      .getQueryCache()\n      .subscribe((event: any) => {\n        if (\n          event.type === 'removed' &&\n          event.query.queryKey[0] === 'spacetimedb'\n        ) {\n          const keyStr = JSON.stringify(event.query.queryKey);\n          const sub = this.subscriptions.get(keyStr);\n          if (sub) {\n            sub.unsubscribe();\n            this.subscriptions.delete(keyStr);\n          }\n        }\n      });\n  }\n\n  queryFn: QueryFunction<any[], QueryKey> = async ({\n    queryKey,\n  }: {\n    queryKey: QueryKey;\n  }) => {\n    const keyStr = JSON.stringify(queryKey);\n    const [prefix, accessorName, querySql] = queryKey as [\n      string,\n      string,\n      string,\n    ];\n\n    if (prefix !== 'spacetimedb') {\n      throw new Error(\n        `SpacetimeDBQueryClient can only handle spacetimedb queries, got: ${prefix}`\n      );\n    }\n\n    const registered = queryRegistry.get(querySql);\n    const whereExpr = registered?.whereExpr;\n\n    const existingSub = this.subscriptions.get(keyStr);\n    if (existingSub?.applied) {\n      return this.getTableData(existingSub.tableInstance, whereExpr);\n    }\n\n    // queue query if connection not ready yet\n    if (!this.connection) {\n      return new Promise<any[]>(resolve => {\n        const pending = this.pendingQueries.get(keyStr) || [];\n        pending.push({ resolve, querySql, whereExpr });\n        this.pendingQueries.set(keyStr, pending);\n      });\n    }\n\n    return this.setupSubscription(queryKey, accessorName, querySql, whereExpr);\n  };\n\n  private getTableData(\n    tableInstance: any,\n    whereExpr?: BooleanExpr<any>\n  ): any[] {\n    const allRows = Array.from(tableInstance.iter());\n    if (whereExpr) {\n      return allRows.filter(row =>\n        evaluateBooleanExpr(whereExpr, row as Record<string, any>)\n      );\n    }\n    return allRows;\n  }\n\n  private setupSubscription(\n    queryKey: QueryKey,\n    accessorName: string,\n    querySql: string,\n    whereExpr?: BooleanExpr<any>\n  ): Promise<any[]> {\n    if (!this.connection) {\n      return Promise.resolve([]);\n    }\n\n    const keyStr = JSON.stringify(queryKey);\n    const db = this.connection.db;\n\n    const tableInstance = db[accessorName];\n\n    if (!tableInstance) {\n      console.warn(`SpacetimeDBQueryClient: table \"${accessorName}\" not found`);\n      return Promise.resolve([]);\n    }\n\n    // return existing data if already subscribed\n    const existingSub = this.subscriptions.get(keyStr);\n    if (existingSub) {\n      if (existingSub.applied) {\n        return Promise.resolve(\n          this.getTableData(existingSub.tableInstance, whereExpr)\n        );\n      }\n      return new Promise(resolve => {\n        const pending = this.pendingQueries.get(keyStr) || [];\n        pending.push({ resolve, querySql, whereExpr });\n        this.pendingQueries.set(keyStr, pending);\n      });\n    }\n\n    return new Promise<any[]>(resolve => {\n      const updateCache = () => {\n        if (!this.queryClient) return [];\n        const data = this.getTableData(tableInstance, whereExpr);\n        this.queryClient.setQueryData(queryKey, data);\n        return data;\n      };\n\n      const handle = this.connection!.subscriptionBuilder()\n        .onApplied(() => {\n          const sub = this.subscriptions.get(keyStr);\n          if (sub) {\n            sub.applied = true;\n          }\n\n          const data = updateCache();\n          resolve(data);\n\n          const pending = this.pendingQueries.get(keyStr);\n          if (pending) {\n            for (const p of pending) {\n              p.resolve(data);\n            }\n            this.pendingQueries.delete(keyStr);\n          }\n        })\n        .subscribe(querySql);\n\n      // push updates to cache when data changes\n      const onTableChange = () => {\n        const sub = this.subscriptions.get(keyStr);\n        if (sub?.applied) {\n          updateCache();\n        }\n      };\n\n      tableInstance.onInsert(onTableChange);\n      tableInstance.onDelete(onTableChange);\n      tableInstance.onUpdate?.(onTableChange);\n\n      this.subscriptions.set(keyStr, {\n        unsubscribe: () => {\n          handle.unsubscribe();\n          tableInstance.removeOnInsert(onTableChange);\n          tableInstance.removeOnDelete(onTableChange);\n          tableInstance.removeOnUpdate?.(onTableChange);\n        },\n        tableInstance,\n        applied: false,\n      });\n    });\n  }\n\n  private processPendingQueries(): void {\n    if (!this.connection) return;\n\n    const pendingEntries = Array.from(this.pendingQueries.entries());\n    this.pendingQueries.clear();\n\n    for (const [keyStr, pending] of pendingEntries) {\n      const queryKey = JSON.parse(keyStr) as QueryKey;\n      const [, accessorName] = queryKey as [string, string, string];\n\n      if (pending.length > 0) {\n        const first = pending[0];\n        this.setupSubscription(\n          queryKey,\n          accessorName,\n          first.querySql,\n          first.whereExpr\n        )\n          .then(data => {\n            for (const p of pending) {\n              p.resolve(data);\n            }\n          })\n          .catch(() => {\n            for (const p of pending) {\n              p.resolve([]);\n            }\n          });\n      }\n    }\n  }\n\n  // subscribe to queries with SSR cached data but no active subscription\n  private hydrateSubscriptions(): void {\n    if (!this.connection || !this.queryClient) {\n      return;\n    }\n\n    for (const [querySql, { accessorName, whereExpr }] of queryRegistry) {\n      const queryKey = ['spacetimedb', accessorName, querySql] as const;\n      const keyStr = JSON.stringify(queryKey);\n\n      if (this.subscriptions.has(keyStr)) {\n        continue;\n      }\n      if (this.queryClient.getQueryData(queryKey) === undefined) {\n        continue;\n      }\n\n      this.setupSubscription(queryKey, accessorName, querySql, whereExpr).catch(\n        () => {}\n      );\n    }\n  }\n\n  // clean up all subscriptions and disconnect\n  disconnect(): void {\n    if (this.cacheUnsubscribe) {\n      this.cacheUnsubscribe();\n      this.cacheUnsubscribe = null;\n    }\n\n    for (const sub of this.subscriptions.values()) {\n      sub.unsubscribe();\n    }\n    this.subscriptions.clear();\n    this.pendingQueries.clear();\n    this.connection = null;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/tanstack/hooks.ts",
    "content": "import { useQuery, useSuspenseQuery } from '@tanstack/react-query';\nimport type {\n  UseQueryOptions,\n  UseQueryResult,\n  UseSuspenseQueryOptions,\n  UseSuspenseQueryResult,\n} from '@tanstack/react-query';\nimport type { UntypedTableDef, RowType } from '../lib/table';\nimport type { Query } from '../lib/query';\nimport { spacetimeDBQuery } from './SpacetimeDBQueryClient';\n\nexport type UseSpacetimeDBQueryResult<T> = [\n  T[],\n  boolean,\n  UseQueryResult<T[], Error>,\n];\n\nexport type UseSpacetimeDBSuspenseQueryResult<T> = [\n  T[],\n  false,\n  UseSuspenseQueryResult<T[], Error>,\n];\n\n// Wraps TanStack Query useQuery and returns [data, loading, query]\n// pass 'skip' as the second argument to set enabled: false, disabling the query\n// until a condition is met\n//\n// Usage:\n//   useSpacetimeDBQuery(tables.person)\n//   useSpacetimeDBQuery(tables.user.where(r => r.online.eq(true)))\n//   useSpacetimeDBQuery(condition ? tables.user : 'skip')\nexport function useSpacetimeDBQuery<TableDef extends UntypedTableDef>(\n  queryOrSkip: Query<TableDef> | 'skip',\n  // any useQuery option (e.g. enabled, refetchInterval, select, placeholderData),\n  // except queryKey, queryFn, and meta (managed internally)\n  options?: Omit<\n    UseQueryOptions<\n      RowType<TableDef>[],\n      Error,\n      RowType<TableDef>[],\n      readonly ['spacetimedb', string, string]\n    >,\n    'queryKey' | 'queryFn' | 'meta'\n  >\n): UseSpacetimeDBQueryResult<RowType<TableDef>> {\n  const queryOptions =\n    queryOrSkip === 'skip'\n      ? spacetimeDBQuery('skip')\n      : spacetimeDBQuery(queryOrSkip);\n\n  const query = useQuery({\n    ...queryOptions,\n    ...options,\n  } as UseQueryOptions<RowType<TableDef>[], Error>);\n\n  return [query.data ?? [], query.isPending, query];\n}\n\n// Suspense version of useSpacetimeDBQuery, returns [data, false, query] tuple (loading = false)\n// Instead of returning a loading boolean, this hook suspends the component\n// until data is ready, a parent <Suspense fallback={…}> handles the loading UI.\n// does not support 'skip' because useSuspenseQuery must always resolve\nexport function useSpacetimeDBSuspenseQuery<TableDef extends UntypedTableDef>(\n  query: Query<TableDef>,\n  options?: Omit<\n    UseSuspenseQueryOptions<\n      RowType<TableDef>[],\n      Error,\n      RowType<TableDef>[],\n      readonly ['spacetimedb', string, string]\n    >,\n    'queryKey' | 'queryFn' | 'meta'\n  >\n): UseSpacetimeDBSuspenseQueryResult<RowType<TableDef>> {\n  const queryOptions = spacetimeDBQuery(query);\n\n  const q = useSuspenseQuery({\n    ...queryOptions,\n    ...options,\n  } as UseSuspenseQueryOptions<RowType<TableDef>[], Error>);\n\n  return [q.data, false, q];\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/tanstack/index.ts",
    "content": "export {\n  SpacetimeDBQueryClient,\n  spacetimeDBQuery,\n  type SpacetimeDBQueryOptions,\n  type SpacetimeDBQueryOptionsSkipped,\n} from './SpacetimeDBQueryClient';\nexport {\n  useSpacetimeDBQuery,\n  useSpacetimeDBSuspenseQuery,\n  type UseSpacetimeDBQueryResult,\n  type UseSpacetimeDBSuspenseQueryResult,\n} from './hooks';\nexport * from '../react/SpacetimeDBProvider';\nexport { useSpacetimeDB } from '../react/useSpacetimeDB';\nexport { useReducer } from '../react/useReducer';\n"
  },
  {
    "path": "crates/bindings-typescript/src/util-stub.ts",
    "content": "export const inspect = {};\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/SpacetimeDBProvider.ts",
    "content": "import {\n  defineComponent,\n  onMounted,\n  onUnmounted,\n  provide,\n  reactive,\n  type PropType,\n  type Slot,\n} from 'vue';\nimport {\n  DbConnectionBuilder,\n  type DbConnectionImpl,\n  type ErrorContextInterface,\n  type RemoteModuleOf,\n} from '../sdk/db_connection_impl';\nimport { ConnectionId } from '../lib/connection_id';\nimport {\n  SPACETIMEDB_INJECTION_KEY,\n  type ConnectionState,\n} from './connection_state';\n\nexport interface SpacetimeDBProviderProps<\n  DbConnection extends DbConnectionImpl<any>,\n> {\n  connectionBuilder: DbConnectionBuilder<DbConnection>;\n}\n\nlet connRef: DbConnectionImpl<any> | null = null;\nlet cleanupTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\nfunction setupConnection<DbConnection extends DbConnectionImpl<any>>(\n  connectionBuilder: DbConnectionBuilder<DbConnection>\n): {\n  state: ConnectionState;\n  cleanup: () => void;\n} {\n  const getConnection = <T extends DbConnectionImpl<any>>() =>\n    connRef as T | null;\n\n  const state = reactive<ConnectionState>({\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n    getConnection,\n  });\n\n  provide(SPACETIMEDB_INJECTION_KEY, state);\n\n  let onConnectCallback: ((conn: DbConnection) => void) | null = null;\n  let onDisconnectCallback:\n    | ((ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>) => void)\n    | null = null;\n  let onConnectErrorCallback:\n    | ((\n        ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n        err: Error\n      ) => void)\n    | null = null;\n\n  onMounted(() => {\n    if (cleanupTimeoutId) {\n      clearTimeout(cleanupTimeoutId);\n      cleanupTimeoutId = null;\n    }\n\n    if (!connRef) {\n      connRef = connectionBuilder.build();\n    }\n\n    onConnectCallback = (conn: DbConnection) => {\n      state.isActive = conn.isActive;\n      state.identity = conn.identity;\n      state.token = conn.token;\n      state.connectionId = conn.connectionId;\n      state.connectionError = undefined;\n    };\n\n    onDisconnectCallback = (\n      ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>\n    ) => {\n      state.isActive = ctx.isActive;\n    };\n\n    onConnectErrorCallback = (\n      ctx: ErrorContextInterface<RemoteModuleOf<DbConnection>>,\n      err: Error\n    ) => {\n      state.isActive = ctx.isActive;\n      state.connectionError = err;\n    };\n\n    connectionBuilder.onConnect(onConnectCallback);\n    connectionBuilder.onDisconnect(onDisconnectCallback);\n    connectionBuilder.onConnectError(onConnectErrorCallback);\n\n    const conn = connRef;\n    if (conn) {\n      state.isActive = conn.isActive;\n      state.identity = conn.identity;\n      state.token = conn.token;\n      state.connectionId = conn.connectionId;\n    }\n  });\n\n  const cleanup = () => {\n    if (connRef) {\n      if (onConnectCallback) {\n        connRef.removeOnConnect?.(onConnectCallback as any);\n      }\n      if (onDisconnectCallback) {\n        connRef.removeOnDisconnect?.(onDisconnectCallback as any);\n      }\n      if (onConnectErrorCallback) {\n        connRef.removeOnConnectError?.(onConnectErrorCallback as any);\n      }\n\n      cleanupTimeoutId = setTimeout(() => {\n        connRef?.disconnect();\n        connRef = null;\n        cleanupTimeoutId = null;\n      }, 0);\n    }\n  };\n\n  onUnmounted(cleanup);\n\n  return { state, cleanup };\n}\n\nexport const SpacetimeDBProvider = defineComponent({\n  name: 'SpacetimeDBProvider',\n\n  props: {\n    connectionBuilder: {\n      type: Object as PropType<DbConnectionBuilder<any>>,\n      required: true,\n    },\n  },\n\n  setup(props, { slots }) {\n    setupConnection(props.connectionBuilder);\n\n    return () => {\n      const defaultSlot = slots.default as Slot | undefined;\n      return defaultSlot ? defaultSlot() : null;\n    };\n  },\n});\n\nexport function useSpacetimeDBProvider<\n  DbConnection extends DbConnectionImpl<any>,\n>(connectionBuilder: DbConnectionBuilder<DbConnection>): ConnectionState {\n  const { state } = setupConnection(connectionBuilder);\n  return state;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/connection_state.ts",
    "content": "import type { InjectionKey } from 'vue';\nimport type { ConnectionId } from '../lib/connection_id';\nimport type { Identity } from '../lib/identity';\nimport type { DbConnectionImpl } from '../sdk/db_connection_impl';\n\nexport interface ConnectionState {\n  isActive: boolean;\n  identity?: Identity;\n  token?: string;\n  connectionId: ConnectionId;\n  connectionError?: Error;\n  getConnection<\n    DbConnection extends DbConnectionImpl<any>,\n  >(): DbConnection | null;\n}\n\nexport const SPACETIMEDB_INJECTION_KEY = Symbol(\n  'spacetimedb'\n) as InjectionKey<ConnectionState>;\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/index.ts",
    "content": "export * from './SpacetimeDBProvider.ts';\nexport { useSpacetimeDB } from './useSpacetimeDB.ts';\nexport { useTable } from './useTable.ts';\nexport { useReducer } from './useReducer.ts';\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/useReducer.ts",
    "content": "import { shallowRef, watch, onUnmounted } from 'vue';\nimport { useSpacetimeDB } from './useSpacetimeDB';\nimport type { UntypedReducerDef } from '../sdk/reducers';\nimport type { ParamsType } from '../sdk';\n\nexport function useReducer<ReducerDef extends UntypedReducerDef>(\n  reducerDef: ReducerDef\n): (...params: ParamsType<ReducerDef>) => Promise<void> {\n  const conn = useSpacetimeDB();\n  const reducerName = reducerDef.accessorName;\n\n  const queueRef = shallowRef<\n    {\n      params: ParamsType<ReducerDef>;\n      resolve: () => void;\n      reject: (err: unknown) => void;\n    }[]\n  >([]);\n\n  const stopWatch = watch(\n    () => conn.isActive,\n    () => {\n      const connection = conn.getConnection();\n      if (!connection) return;\n\n      const fn = (connection.reducers as any)[reducerName] as (\n        ...p: ParamsType<ReducerDef>\n      ) => Promise<void>;\n      if (queueRef.value.length) {\n        const pending = queueRef.value.splice(0);\n        for (const item of pending) {\n          fn(...item.params).then(item.resolve, item.reject);\n        }\n      }\n    },\n    { immediate: true }\n  );\n\n  onUnmounted(() => {\n    stopWatch();\n  });\n\n  return (...params: ParamsType<ReducerDef>) => {\n    const connection = conn.getConnection();\n    if (!connection) {\n      return new Promise<void>((resolve, reject) => {\n        queueRef.value.push({ params, resolve, reject });\n      });\n    }\n    const fn = (connection.reducers as any)[reducerName] as (\n      ...p: ParamsType<ReducerDef>\n    ) => Promise<void>;\n    return fn(...params);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/useSpacetimeDB.ts",
    "content": "import { inject } from 'vue';\nimport {\n  SPACETIMEDB_INJECTION_KEY,\n  type ConnectionState,\n} from './connection_state';\n\nexport function useSpacetimeDB(): ConnectionState {\n  const context = inject(SPACETIMEDB_INJECTION_KEY);\n\n  if (!context) {\n    throw new Error(\n      'useSpacetimeDB must be used within a SpacetimeDBProvider component. ' +\n        'Did you forget to add a `SpacetimeDBProvider` to your component tree?'\n    );\n  }\n\n  return context;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/src/vue/useTable.ts",
    "content": "import {\n  onUnmounted,\n  readonly,\n  ref,\n  shallowRef,\n  watch,\n  type DeepReadonly,\n  type Ref,\n} from 'vue';\nimport { useSpacetimeDB } from './useSpacetimeDB';\n\nimport type { EventContextInterface } from '../sdk/db_connection_impl';\nimport type { UntypedRemoteModule } from '../sdk/spacetime_module';\nimport type { RowType, UntypedTableDef } from '../lib/table';\nimport type { Prettify } from '../lib/type_util';\nimport {\n  type Query,\n  type BooleanExpr,\n  toSql,\n  evaluateBooleanExpr,\n  getQueryAccessorName,\n  getQueryWhereClause,\n} from '../lib/query';\n\nexport interface UseTableCallbacks<RowType> {\n  onInsert?: (row: RowType) => void;\n  onDelete?: (row: RowType) => void;\n  onUpdate?: (oldRow: RowType, newRow: RowType) => void;\n}\n\ntype MembershipChange = 'enter' | 'leave' | 'stayIn' | 'stayOut';\n\nfunction classifyMembership(\n  whereExpr: BooleanExpr<any> | undefined,\n  oldRow: Record<string, any>,\n  newRow: Record<string, any>\n): MembershipChange {\n  if (!whereExpr) return 'stayIn';\n  const oldIn = evaluateBooleanExpr(whereExpr, oldRow);\n  const newIn = evaluateBooleanExpr(whereExpr, newRow);\n  if (oldIn && !newIn) return 'leave';\n  if (!oldIn && newIn) return 'enter';\n  if (oldIn && newIn) return 'stayIn';\n  return 'stayOut';\n}\n\n/**\n * Vue composable to subscribe to a table in SpacetimeDB and receive live updates.\n *\n * Accepts a query builder expression as the first argument:\n * - `tables.user` — subscribe to all rows\n * - `tables.user.where(r => r.online.eq(true))` — subscribe with a filter\n *\n * @param query - A query builder expression (table reference or filtered query).\n * @param callbacks - Optional callbacks for row insert, delete, and update events.\n * @returns A tuple of [rows, isReady].\n */\nexport function useTable<TableDef extends UntypedTableDef>(\n  query: Query<TableDef>,\n  callbacks?: UseTableCallbacks<Prettify<RowType<TableDef>>>\n): [\n  DeepReadonly<Ref<readonly Prettify<RowType<TableDef>>[]>>,\n  DeepReadonly<Ref<boolean>>,\n] {\n  type Row = RowType<TableDef>;\n  const accessorName = getQueryAccessorName(query);\n  const whereExpr = getQueryWhereClause(query);\n  const querySql = toSql(query);\n\n  let conn;\n  try {\n    conn = useSpacetimeDB();\n  } catch {\n    throw new Error(\n      'Could not find SpacetimeDB client! Did you forget to add a ' +\n        '`SpacetimeDBProvider`? `useTable` must be used in a Vue component tree ' +\n        'under a `SpacetimeDBProvider` component.'\n    );\n  }\n\n  const rows = shallowRef<readonly Prettify<Row>[]>([]);\n  const isReady = ref(false);\n\n  let latestTransactionEvent: any = null;\n  let unsubscribeFromTable: (() => void) | null = null;\n  let subscriptionHandle: { unsubscribe: () => void } | null = null;\n\n  const computeFilteredRows = (): readonly Prettify<Row>[] => {\n    const connection = conn.getConnection();\n    if (!connection) return [];\n\n    const table = connection.db[accessorName];\n    if (!table) return [];\n\n    const allRows = Array.from(table.iter()) as Row[];\n    if (whereExpr) {\n      return allRows.filter(row =>\n        evaluateBooleanExpr(whereExpr, row as Record<string, any>)\n      ) as Prettify<Row>[];\n    }\n    return allRows as Prettify<Row>[];\n  };\n\n  const setupTableListeners = () => {\n    const connection = conn.getConnection();\n    if (!connection) return;\n\n    const table = connection.db[accessorName];\n    if (!table) return;\n\n    const onInsert = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) return;\n      callbacks?.onInsert?.(row);\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.value = computeFilteredRows();\n      }\n    };\n\n    const onDelete = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      row: any\n    ) => {\n      if (whereExpr && !evaluateBooleanExpr(whereExpr, row)) return;\n      callbacks?.onDelete?.(row);\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.value = computeFilteredRows();\n      }\n    };\n\n    const onUpdate = (\n      eventCtx: EventContextInterface<UntypedRemoteModule>,\n      oldRow: any,\n      newRow: any\n    ) => {\n      const change = classifyMembership(whereExpr, oldRow, newRow);\n\n      switch (change) {\n        case 'leave':\n          callbacks?.onDelete?.(oldRow);\n          break;\n        case 'enter':\n          callbacks?.onInsert?.(newRow);\n          break;\n        case 'stayIn':\n          callbacks?.onUpdate?.(oldRow, newRow);\n          break;\n        case 'stayOut':\n          return;\n      }\n\n      if (\n        eventCtx.event !== latestTransactionEvent ||\n        !latestTransactionEvent\n      ) {\n        latestTransactionEvent = eventCtx.event;\n        rows.value = computeFilteredRows();\n      }\n    };\n\n    table.onInsert(onInsert);\n    table.onDelete(onDelete);\n    table.onUpdate?.(onUpdate);\n\n    return () => {\n      table.removeOnInsert(onInsert);\n      table.removeOnDelete(onDelete);\n      table.removeOnUpdate?.(onUpdate);\n    };\n  };\n\n  const setupSubscription = () => {\n    const connection = conn.getConnection();\n    if (!connection) return;\n\n    subscriptionHandle = connection\n      .subscriptionBuilder()\n      .onApplied(() => {\n        isReady.value = true;\n        rows.value = computeFilteredRows();\n      })\n      .subscribe(querySql);\n  };\n\n  watch(\n    () => conn.isActive,\n    isActive => {\n      // Clean up existing listeners and subscriptions first\n      if (unsubscribeFromTable) {\n        unsubscribeFromTable();\n        unsubscribeFromTable = null;\n      }\n      if (subscriptionHandle) {\n        subscriptionHandle.unsubscribe();\n        subscriptionHandle = null;\n      }\n\n      if (isActive) {\n        unsubscribeFromTable = setupTableListeners() || null;\n        setupSubscription();\n        rows.value = computeFilteredRows();\n      } else {\n        isReady.value = false;\n        rows.value = [];\n      }\n    },\n    { immediate: true }\n  );\n\n  onUnmounted(() => {\n    unsubscribeFromTable?.();\n    subscriptionHandle?.unsubscribe();\n    latestTransactionEvent = null;\n  });\n\n  return [readonly(rows), readonly(isReady)];\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/CHANGELOG.md",
    "content": "# @clockworklabs/test-app\n\n## 0.0.1\n\n### Patch Changes\n\n- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:\n  - @clockworklabs/spacetimedb-sdk@1.2.0\n\n## 0.0.3-rc1.0\n\n### Patch Changes\n\n- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:\n  - @clockworklabs/spacetimedb-sdk@1.0.0-rc1.0\n\n## 0.0.2\n\n### Patch Changes\n\n- Updated dependencies [[`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d), [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf), [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912)]:\n  - @clockworklabs/spacetimedb-sdk@0.12.1\n\n## 0.0.1\n\n### Patch Changes\n\n- Updated dependencies [[`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8), [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1), [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213), [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a)]:\n  - @clockworklabs/spacetimedb-sdk@0.12.0\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/README.md",
    "content": "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type aware lint rules:\n\n- Configure the top-level `parserOptions` property like this:\n\n```js\nexport default {\n  // other rules...\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],\n    tsconfigRootDir: __dirname,\n  },\n};\n```\n\n- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`\n- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`\n- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/package.json",
    "content": "{\n  \"name\": \"@clockworklabs/test-app\",\n  \"private\": true,\n  \"version\": \"0.0.1\",\n  \"type\": \"module\",\n  \"files\": [\n    \"src\"\n  ],\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"format\": \"prettier . --write --ignore-path ../../../.prettierignore\",\n    \"lint\": \"eslint . && prettier . --check --ignore-path ../../../.prettierignore\",\n    \"preview\": \"vite preview\",\n    \"generate\": \"cargo run -p gen-bindings -- --replacement ../../../src/index && prettier --write src/module_bindings\",\n    \"spacetime:generate\": \"spacetime generate --lang typescript --out-dir src/module_bindings --project-path server\",\n    \"spacetime:start\": \"spacetime start\",\n    \"spacetime:publish:local\": \"spacetime publish game --project-path server --server local\",\n    \"spacetime:publish\": \"spacetime publish game --project-path server --server maincloud\"\n  },\n  \"dependencies\": {\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.2.2\",\n    \"vite\": \"^7.1.5\"\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/server/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n# Spacetime ignore\n/.spacetime"
  },
  {
    "path": "crates/bindings-typescript/test-app/server/Cargo.toml",
    "content": "[package]\nname = \"typescript-test-app\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = \"1.2.0\"\nlog = \"0.4\"\nanyhow = \"1.0\"\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/server/src/lib.rs",
    "content": "use spacetimedb::{reducer, table, Identity, ReducerContext, SpacetimeType, Table};\n\n#[table(name = player, public)]\npub struct Player {\n    #[primary_key]\n    #[auto_inc]\n    id: u32,\n    user_id: Identity,\n    name: String,\n    location: Point,\n}\n\n#[derive(SpacetimeType)]\npub struct Point {\n    pub x: u16,\n    pub y: u16,\n}\n\n#[table(name = user, public)]\npub struct User {\n    #[primary_key]\n    pub identity: Identity,\n    pub username: String,\n}\n\n#[table(name = unindexed_player, public)]\npub struct UnindexedPlayer {\n    #[primary_key]\n    #[auto_inc]\n    id: u32,\n    owner_id: Identity,\n    name: String,\n    location: Point,\n}\n\n#[reducer]\npub fn create_player(ctx: &ReducerContext, name: String, location: Point) {\n    ctx.db.user().insert(User {\n        identity: ctx.sender,\n        username: name.clone(),\n    });\n    ctx.db.player().insert(Player {\n        id: 0,\n        user_id: ctx.sender,\n        name,\n        location,\n    });\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/.gitattributes",
    "content": "/module_bindings/** linguist-generated=true\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/App.css",
    "content": "#root {\n  max-width: 1280px;\n  margin: 0 auto;\n  padding: 2rem;\n  text-align: center;\n}\n\n.logo {\n  height: 6em;\n  padding: 1.5em;\n  will-change: filter;\n  transition: filter 300ms;\n}\n.logo:hover {\n  filter: drop-shadow(0 0 2em #646cffaa);\n}\n.logo.react:hover {\n  filter: drop-shadow(0 0 2em #61dafbaa);\n}\n\n@keyframes logo-spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  a:nth-of-type(2) .logo {\n    animation: logo-spin infinite 20s linear;\n  }\n}\n\n.card {\n  padding: 2em;\n}\n\n.read-the-docs {\n  color: #888;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/App.tsx",
    "content": "import { tables, reducers } from './module_bindings';\nimport { useEffect } from 'react';\nimport './App.css';\nimport { useReducer, useSpacetimeDB, useTable } from '../../src/react';\n\nfunction getRandomInt(max: number) {\n  return Math.floor(Math.random() * max);\n}\n\nfunction App() {\n  const connection = useSpacetimeDB();\n  const [players] = useTable(\n    tables.player.where(r => r.name.eq('Hello')),\n    {\n      onInsert: row => {\n        console.log('Player inserted:', row);\n      },\n    }\n  );\n  const createPlayer = useReducer(reducers.createPlayer);\n\n  useEffect(() => {\n    setTimeout(() => {\n      console.log(players);\n    }, 5000);\n  }, [connection, players]);\n\n  return (\n    <div className=\"App\">\n      <h1>Typescript SDK Test!</h1>\n      <p>{connection.identity?.toHexString()}</p>\n\n      <button\n        onClick={() => {\n          const player = {\n            name: 'Hello',\n            location: { x: getRandomInt(100), y: getRandomInt(100) },\n          };\n          console.log('Creating player:', player);\n          createPlayer(player);\n        }}\n      >\n        Update\n      </button>\n      <div>\n        {Array.from(players).map((player, i) => (\n          <div key={i}>\n            {player.name} - ({player.location.x}, {player.location.y})\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/index.css",
    "content": ":root {\n  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n  font-weight: 400;\n\n  color-scheme: light dark;\n  color: rgba(255, 255, 255, 0.87);\n  background-color: #242424;\n\n  font-synthesis: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\nbody {\n  margin: 0;\n  display: flex;\n  place-items: center;\n  min-width: 320px;\n  min-height: 100vh;\n}\n\nh1 {\n  font-size: 3.2em;\n  line-height: 1.1;\n}\n\nbutton {\n  border-radius: 8px;\n  border: 1px solid transparent;\n  padding: 0.6em 1.2em;\n  font-size: 1em;\n  font-weight: 500;\n  font-family: inherit;\n  background-color: #1a1a1a;\n  cursor: pointer;\n  transition: border-color 0.25s;\n}\nbutton:hover {\n  border-color: #646cff;\n}\nbutton:focus,\nbutton:focus-visible {\n  outline: 4px auto -webkit-focus-ring-color;\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    color: #213547;\n    background-color: #ffffff;\n  }\n  a:hover {\n    color: #747bff;\n  }\n  button {\n    background-color: #f9f9f9;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/main.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport App from './App.tsx';\nimport './index.css';\nimport { SpacetimeDBProvider } from '../../src/react';\nimport { DbConnection, tables } from './module_bindings/index.ts';\n\nconst connectionBuilder = DbConnection.builder()\n  .withUri('ws://localhost:3000')\n  .withDatabaseName('game')\n  .withLightMode(true)\n  .onDisconnect(() => {\n    console.log('disconnected');\n  })\n  .onConnectError((ctx, err) => {\n    console.log('client_error: ', err);\n  })\n  .onConnect((conn, identity, _token) => {\n    console.log(\n      'Connected to SpacetimeDB with identity:',\n      identity.toHexString()\n    );\n\n    conn.subscriptionBuilder().subscribe(tables.player);\n  });\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <SpacetimeDBProvider connectionBuilder={connectionBuilder}>\n      <App />\n    </SpacetimeDBProvider>\n  </React.StrictMode>\n);\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/create_player_reducer.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nimport { Point } from './types';\n\nexport default {\n  name: __t.string(),\n  get location() {\n    return Point;\n  },\n};\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/index.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb cli version 2.0.0 (commit 098afaf1a5ed935bce5a32c88620e829506effe7).\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  DbConnectionBuilder as __DbConnectionBuilder,\n  DbConnectionImpl as __DbConnectionImpl,\n  SubscriptionBuilderImpl as __SubscriptionBuilderImpl,\n  TypeBuilder as __TypeBuilder,\n  Uuid as __Uuid,\n  convertToAccessorMap as __convertToAccessorMap,\n  makeQueryBuilder as __makeQueryBuilder,\n  procedureSchema as __procedureSchema,\n  procedures as __procedures,\n  reducerSchema as __reducerSchema,\n  reducers as __reducers,\n  schema as __schema,\n  t as __t,\n  table as __table,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type DbConnectionConfig as __DbConnectionConfig,\n  type ErrorContextInterface as __ErrorContextInterface,\n  type Event as __Event,\n  type EventContextInterface as __EventContextInterface,\n  type Infer as __Infer,\n  type QueryBuilder as __QueryBuilder,\n  type ReducerEventContextInterface as __ReducerEventContextInterface,\n  type RemoteModule as __RemoteModule,\n  type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,\n  type SubscriptionHandleImpl as __SubscriptionHandleImpl,\n} from '../../../src/index';\n\n// Import all reducer arg schemas\nimport CreatePlayerReducer from './create_player_reducer';\n\n// Import all procedure arg schemas\n\n// Import all table schema definitions\nimport PlayerRow from './player_table';\nimport UnindexedPlayerRow from './unindexed_player_table';\nimport UserRow from './user_table';\n\n/** Type-only namespace exports for generated type groups. */\n\n/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */\nconst tablesSchema = __schema({\n  player: __table(\n    {\n      name: 'player',\n      indexes: [\n        {\n          accessor: 'id',\n          name: 'player_id_idx_btree',\n          algorithm: 'btree',\n          columns: ['id'],\n        },\n      ],\n      constraints: [\n        { name: 'player_id_key', constraint: 'unique', columns: ['id'] },\n      ],\n    },\n    PlayerRow\n  ),\n  unindexed_player: __table(\n    {\n      name: 'unindexed_player',\n      indexes: [\n        {\n          accessor: 'id',\n          name: 'unindexed_player_id_idx_btree',\n          algorithm: 'btree',\n          columns: ['id'],\n        },\n      ],\n      constraints: [\n        {\n          name: 'unindexed_player_id_key',\n          constraint: 'unique',\n          columns: ['id'],\n        },\n      ],\n    },\n    UnindexedPlayerRow\n  ),\n  user: __table(\n    {\n      name: 'user',\n      indexes: [\n        {\n          accessor: 'identity',\n          name: 'user_identity_idx_btree',\n          algorithm: 'btree',\n          columns: ['identity'],\n        },\n      ],\n      constraints: [\n        {\n          name: 'user_identity_key',\n          constraint: 'unique',\n          columns: ['identity'],\n        },\n      ],\n    },\n    UserRow\n  ),\n});\n\n/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */\nconst reducersSchema = __reducers(\n  __reducerSchema('create_player', CreatePlayerReducer)\n);\n\n/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */\nconst proceduresSchema = __procedures();\n\n/** The remote SpacetimeDB module schema, both runtime and type information. */\nconst REMOTE_MODULE = {\n  versionInfo: {\n    cliVersion: '2.0.0' as const,\n  },\n  tables: tablesSchema.schemaType.tables,\n  reducers: reducersSchema.reducersType.reducers,\n  ...proceduresSchema,\n} satisfies __RemoteModule<\n  typeof tablesSchema.schemaType,\n  typeof reducersSchema.reducersType,\n  typeof proceduresSchema\n>;\n\n/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */\nexport const tables: __QueryBuilder<typeof tablesSchema.schemaType> =\n  __makeQueryBuilder(tablesSchema.schemaType);\n\n/** The reducers available in this remote SpacetimeDB module. */\nexport const reducers = __convertToAccessorMap(\n  reducersSchema.reducersType.reducers\n);\n\n/** The context type returned in callbacks for all possible events. */\nexport type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for reducer events. */\nexport type ReducerEventContext = __ReducerEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for subscription events. */\nexport type SubscriptionEventContext = __SubscriptionEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for error events. */\nexport type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;\n/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */\nexport type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;\n\n/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */\nexport class SubscriptionBuilder extends __SubscriptionBuilderImpl<\n  typeof REMOTE_MODULE\n> {}\n\n/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */\nexport class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}\n\n/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */\nexport class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {\n  /** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */\n  static builder = (): DbConnectionBuilder => {\n    return new DbConnectionBuilder(\n      REMOTE_MODULE,\n      (config: __DbConnectionConfig<typeof REMOTE_MODULE>) =>\n        new DbConnection(config)\n    );\n  };\n\n  /** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */\n  override subscriptionBuilder = (): SubscriptionBuilder => {\n    return new SubscriptionBuilder(this);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/player_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\nimport { Point } from './types';\n\nexport default __t.row({\n  id: __t.u32().primaryKey(),\n  userId: __t.identity().name('user_id'),\n  name: __t.string(),\n  get location() {\n    return Point;\n  },\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/types/procedures.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../../src/index';\n\n// Import all procedure arg schemas\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/types/reducers.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../../src/index';\n\n// Import all reducer arg schemas\nimport CreatePlayerReducer from '../create_player_reducer';\n\nexport type CreatePlayerParams = __Infer<typeof CreatePlayerReducer>;\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/types.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport const Player = __t.object('Player', {\n  id: __t.u32(),\n  userId: __t.identity(),\n  name: __t.string(),\n  get location() {\n    return Point;\n  },\n});\nexport type Player = __Infer<typeof Player>;\n\nexport const Point = __t.object('Point', {\n  x: __t.u16(),\n  y: __t.u16(),\n});\nexport type Point = __Infer<typeof Point>;\n\nexport const UnindexedPlayer = __t.object('UnindexedPlayer', {\n  id: __t.u32(),\n  ownerId: __t.identity(),\n  name: __t.string(),\n  get location() {\n    return Point;\n  },\n});\nexport type UnindexedPlayer = __Infer<typeof UnindexedPlayer>;\n\nexport const User = __t.object('User', {\n  identity: __t.identity(),\n  username: __t.string(),\n});\nexport type User = __Infer<typeof User>;\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/unindexed_player_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\nimport { Point } from './types';\n\nexport default __t.row({\n  id: __t.u32().primaryKey(),\n  ownerId: __t.identity().name('owner_id'),\n  name: __t.string(),\n  get location() {\n    return Point;\n  },\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/src/module_bindings/user_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.row({\n  identity: __t.identity().primaryKey(),\n  username: __t.string(),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ESNext\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src/**/*\", \"vite.config.ts\", \"../src/**/*\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.app.json\"\n    },\n    {\n      \"path\": \"./tsconfig.node.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-app/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/CHANGELOG.md",
    "content": "# @clockworklabs/test-app\n\n## 0.0.1\n\n### Patch Changes\n\n- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:\n  - @clockworklabs/spacetimedb-sdk@1.2.0\n\n## 0.0.3-rc1.0\n\n### Patch Changes\n\n- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:\n  - @clockworklabs/spacetimedb-sdk@1.0.0-rc1.0\n\n## 0.0.2\n\n### Patch Changes\n\n- Updated dependencies [[`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d), [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf), [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912)]:\n  - @clockworklabs/spacetimedb-sdk@0.12.1\n\n## 0.0.1\n\n### Patch Changes\n\n- Updated dependencies [[`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8), [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1), [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213), [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a)]:\n  - @clockworklabs/spacetimedb-sdk@0.12.0\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/README.md",
    "content": "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type aware lint rules:\n\n- Configure the top-level `parserOptions` property like this:\n\n```js\nexport default {\n  // other rules...\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],\n    tsconfigRootDir: __dirname,\n  },\n};\n```\n\n- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`\n- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`\n- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/package.json",
    "content": "{\n  \"name\": \"@clockworklabs/test-app\",\n  \"private\": true,\n  \"version\": \"0.0.1\",\n  \"type\": \"module\",\n  \"files\": [\n    \"src\"\n  ],\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc -b && vite build\",\n    \"format\": \"prettier . --write --ignore-path ../../../.prettierignore\",\n    \"lint\": \"eslint . && prettier . --check --ignore-path ../../../.prettierignore\",\n    \"preview\": \"vite preview\",\n    \"generate\": \"cargo run -p gen-bindings -- --replacement ../../../src/index && prettier --write src/module_bindings\",\n    \"spacetime:generate\": \"spacetime generate --lang typescript --out-dir src/module_bindings --module-path server\",\n    \"spacetime:start\": \"spacetime start\",\n    \"spacetime:publish:local\": \"spacetime publish game --module-path server --server local\",\n    \"spacetime:publish\": \"spacetime publish game --module-path server --server maincloud\"\n  },\n  \"dependencies\": {\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-router-dom\": \"^7.9.4\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@types/react-router-dom\": \"^5.3.3\",\n    \"@vitejs/plugin-react\": \"^4.3.1\",\n    \"typescript\": \"^5.2.2\",\n    \"vite\": \"^7.1.5\"\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/server/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n# Spacetime ignore\n/.spacetime"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/server/Cargo.toml",
    "content": "[package]\nname = \"typescript-test-react-router-app\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog = \"0.4\"\nanyhow = \"1.0\"\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/server/src/lib.rs",
    "content": "use spacetimedb::{reducer, table, Identity, ReducerContext, Table};\n\n#[table(public, accessor = counter)]\nstruct Counter {\n    #[primary_key]\n    id: u32,\n    count: u32,\n}\n\n#[table(public, accessor = user)]\n#[table(public, accessor = offline_user)]\nstruct User {\n    #[primary_key]\n    identity: Identity,\n    has_incremented_count: u32,\n}\n\n#[reducer(init)]\nfn init(ctx: &ReducerContext) {\n    ctx.db.counter().insert(Counter { id: 0, count: 0 });\n}\n\n#[reducer(client_connected)]\nfn client_connected(ctx: &ReducerContext) {\n    let existing_user = ctx.db.offline_user().identity().find(ctx.sender());\n    if let Some(user) = existing_user {\n        ctx.db.user().insert(user);\n        ctx.db.offline_user().identity().delete(ctx.sender());\n        return;\n    }\n    ctx.db.offline_user().insert(User {\n        identity: ctx.sender(),\n        has_incremented_count: 0,\n    });\n}\n\n#[reducer(client_disconnected)]\nfn client_disconnected(ctx: &ReducerContext) -> Result<(), String> {\n    let existing_user = ctx.db.user().identity().find(ctx.sender()).ok_or(\"User not found\")?;\n    ctx.db.offline_user().insert(existing_user);\n    ctx.db.user().identity().delete(ctx.sender());\n    Ok(())\n}\n\n#[reducer]\nfn increment_counter(ctx: &ReducerContext) -> Result<(), String> {\n    let mut counter = ctx.db.counter().id().find(0).ok_or(\"Counter not found\")?;\n    counter.count += 1;\n    ctx.db.counter().id().update(counter);\n\n    let mut user = ctx.db.user().identity().find(ctx.sender()).ok_or(\"User not found\")?;\n    user.has_incremented_count += 1;\n    ctx.db.user().identity().update(user);\n\n    Ok(())\n}\n\n#[reducer]\nfn clear_counter(ctx: &ReducerContext) {\n    for row in ctx.db.counter().iter() {\n        ctx.db.counter().id().delete(row.id);\n        ctx.db.counter().insert(Counter { id: 0, count: 0 });\n    }\n\n    for row in ctx.db.user().iter() {\n        let user = User {\n            identity: row.identity,\n            has_incremented_count: 0,\n        };\n        ctx.db.user().identity().update(user);\n    }\n\n    for row in ctx.db.offline_user().iter() {\n        let user = User {\n            identity: row.identity,\n            has_incremented_count: 0,\n        };\n        ctx.db.offline_user().identity().update(user);\n    }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/.gitattributes",
    "content": "/module_bindings/** linguist-generated=true\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/App.css",
    "content": "#root {\n  max-width: 1280px;\n  margin: 0 auto;\n  padding: 2rem;\n  text-align: center;\n}\n\n.logo {\n  height: 6em;\n  padding: 1.5em;\n  will-change: filter;\n  transition: filter 300ms;\n}\n.logo:hover {\n  filter: drop-shadow(0 0 2em #646cffaa);\n}\n.logo.react:hover {\n  filter: drop-shadow(0 0 2em #61dafbaa);\n}\n\n@keyframes logo-spin {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  a:nth-of-type(2) .logo {\n    animation: logo-spin infinite 20s linear;\n  }\n}\n\n.card {\n  padding: 2em;\n}\n\n.read-the-docs {\n  color: #888;\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/App.tsx",
    "content": "import './App.css';\nimport { Link, Route, Routes } from 'react-router-dom';\nimport CounterPage from './pages/CounterPage';\nimport UserPage from './pages/UserPage';\n\nfunction App() {\n  return (\n    <div>\n      <nav style={{ marginBottom: '1rem' }}>\n        <Link to=\"/\">Counter</Link> | <Link to=\"/user\">User</Link>\n      </nav>\n\n      <Routes>\n        <Route path=\"/\" element={<CounterPage />} />\n        <Route path=\"/user\" element={<UserPage />} />\n      </Routes>\n    </div>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/index.css",
    "content": ":root {\n  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n  font-weight: 400;\n\n  color-scheme: light dark;\n  color: rgba(255, 255, 255, 0.87);\n  background-color: #242424;\n\n  font-synthesis: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\na {\n  font-weight: 500;\n  color: #646cff;\n  text-decoration: inherit;\n}\na:hover {\n  color: #535bf2;\n}\n\nbody {\n  margin: 0;\n  display: flex;\n  place-items: center;\n  min-width: 320px;\n  min-height: 100vh;\n}\n\nh1 {\n  font-size: 3.2em;\n  line-height: 1.1;\n}\n\nbutton {\n  border-radius: 8px;\n  border: 1px solid transparent;\n  padding: 0.6em 1.2em;\n  font-size: 1em;\n  font-weight: 500;\n  font-family: inherit;\n  background-color: #1a1a1a;\n  cursor: pointer;\n  transition: border-color 0.25s;\n}\nbutton:hover {\n  border-color: #646cff;\n}\nbutton:focus,\nbutton:focus-visible {\n  outline: 4px auto -webkit-focus-ring-color;\n}\n\n@media (prefers-color-scheme: light) {\n  :root {\n    color: #213547;\n    background-color: #ffffff;\n  }\n  a:hover {\n    color: #747bff;\n  }\n  button {\n    background-color: #f9f9f9;\n  }\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/main.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport App from './App.tsx';\nimport './index.css';\nimport { SpacetimeDBProvider } from '../../src/react';\nimport { DbConnection, ErrorContext } from './module_bindings/index.ts';\nimport { BrowserRouter } from 'react-router-dom';\nimport { Identity } from '../../src/index.ts';\n\nconst onConnect = (_conn: DbConnection, identity: Identity, token: string) => {\n  localStorage.setItem('stdbToken', token);\n  console.log('Connected to SpacetimeDB! [' + identity.toHexString() + ']');\n};\n\nconst onDisconnect = () => {\n  console.log('Disconnected from SpacetimeDB!');\n};\n\nconst onConnectError = (_ctx: ErrorContext, err: Error) => {\n  console.log('Error connecting to SpacetimeDB! ', err);\n};\n\n// we DO NOT .build() the builder here, that's done automatically in the <SpacetimeDBProvider> component\n// set all the settings you need, be sure your Uri and Module Name are correct\nconst connBuilder = DbConnection.builder()\n  .withUri('ws://localhost:3000')\n  .withDatabaseName('simple-stdb-react-hooks-example')\n  .withToken(localStorage.getItem('stdbToken') || '')\n  .onConnect(onConnect)\n  .onConnectError(onConnectError)\n  .onDisconnect(onDisconnect);\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <SpacetimeDBProvider connectionBuilder={connBuilder}>\n      <BrowserRouter>\n        <App />\n      </BrowserRouter>\n    </SpacetimeDBProvider>\n  </React.StrictMode>\n);\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/clear_counter_reducer.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default {};\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/client_connected_reducer.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default {};\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/client_disconnected_reducer.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default {};\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.row({\n  id: __t.u32().primaryKey(),\n  count: __t.u32(),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/counter_type.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.object('Counter', {\n  id: __t.u32(),\n  count: __t.u32(),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/increment_counter_reducer.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default {};\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/index.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb cli version 2.0.0 (commit ed2408b3d1fb8b634b143fcc289b8bfda5d385ee).\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  DbConnectionBuilder as __DbConnectionBuilder,\n  DbConnectionImpl as __DbConnectionImpl,\n  SubscriptionBuilderImpl as __SubscriptionBuilderImpl,\n  TypeBuilder as __TypeBuilder,\n  Uuid as __Uuid,\n  convertToAccessorMap as __convertToAccessorMap,\n  makeQueryBuilder as __makeQueryBuilder,\n  procedureSchema as __procedureSchema,\n  procedures as __procedures,\n  reducerSchema as __reducerSchema,\n  reducers as __reducers,\n  schema as __schema,\n  t as __t,\n  table as __table,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type DbConnectionConfig as __DbConnectionConfig,\n  type ErrorContextInterface as __ErrorContextInterface,\n  type Event as __Event,\n  type EventContextInterface as __EventContextInterface,\n  type Infer as __Infer,\n  type QueryBuilder as __QueryBuilder,\n  type ReducerEventContextInterface as __ReducerEventContextInterface,\n  type RemoteModule as __RemoteModule,\n  type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,\n  type SubscriptionHandleImpl as __SubscriptionHandleImpl,\n} from '../../../src/index';\n\n// Import all reducer arg schemas\nimport ClearCounterReducer from './clear_counter_reducer';\nimport IncrementCounterReducer from './increment_counter_reducer';\n\n// Import all procedure arg schemas\n\n// Import all table schema definitions\nimport CounterRow from './counter_table';\nimport OfflineUserRow from './offline_user_table';\nimport UserRow from './user_table';\n\n/** Type-only namespace exports for generated type groups. */\n\n/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */\nconst tablesSchema = __schema({\n  counter: __table(\n    {\n      name: 'counter',\n      indexes: [\n        {\n          accessor: 'id',\n          name: 'counter_id_idx_btree',\n          algorithm: 'btree',\n          columns: ['id'],\n        },\n      ],\n      constraints: [\n        { name: 'counter_id_key', constraint: 'unique', columns: ['id'] },\n      ],\n    },\n    CounterRow\n  ),\n  offline_user: __table(\n    {\n      name: 'offline_user',\n      indexes: [\n        {\n          accessor: 'identity',\n          name: 'offline_user_identity_idx_btree',\n          algorithm: 'btree',\n          columns: ['identity'],\n        },\n      ],\n      constraints: [\n        {\n          name: 'offline_user_identity_key',\n          constraint: 'unique',\n          columns: ['identity'],\n        },\n      ],\n    },\n    OfflineUserRow\n  ),\n  user: __table(\n    {\n      name: 'user',\n      indexes: [\n        {\n          accessor: 'identity',\n          name: 'user_identity_idx_btree',\n          algorithm: 'btree',\n          columns: ['identity'],\n        },\n      ],\n      constraints: [\n        {\n          name: 'user_identity_key',\n          constraint: 'unique',\n          columns: ['identity'],\n        },\n      ],\n    },\n    UserRow\n  ),\n});\n\n/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */\nconst reducersSchema = __reducers(\n  __reducerSchema('clear_counter', ClearCounterReducer),\n  __reducerSchema('increment_counter', IncrementCounterReducer)\n);\n\n/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */\nconst proceduresSchema = __procedures();\n\n/** The remote SpacetimeDB module schema, both runtime and type information. */\nconst REMOTE_MODULE = {\n  versionInfo: {\n    cliVersion: '2.0.0' as const,\n  },\n  tables: tablesSchema.schemaType.tables,\n  reducers: reducersSchema.reducersType.reducers,\n  ...proceduresSchema,\n} satisfies __RemoteModule<\n  typeof tablesSchema.schemaType,\n  typeof reducersSchema.reducersType,\n  typeof proceduresSchema\n>;\n\n/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */\nexport const tables: __QueryBuilder<typeof tablesSchema.schemaType> =\n  __makeQueryBuilder(tablesSchema.schemaType);\n\n/** The reducers available in this remote SpacetimeDB module. */\nexport const reducers = __convertToAccessorMap(\n  reducersSchema.reducersType.reducers\n);\n\n/** The context type returned in callbacks for all possible events. */\nexport type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for reducer events. */\nexport type ReducerEventContext = __ReducerEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for subscription events. */\nexport type SubscriptionEventContext = __SubscriptionEventContextInterface<\n  typeof REMOTE_MODULE\n>;\n/** The context type returned in callbacks for error events. */\nexport type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;\n/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */\nexport type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;\n\n/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */\nexport class SubscriptionBuilder extends __SubscriptionBuilderImpl<\n  typeof REMOTE_MODULE\n> {}\n\n/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */\nexport class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}\n\n/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */\nexport class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {\n  /** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */\n  static builder = (): DbConnectionBuilder => {\n    return new DbConnectionBuilder(\n      REMOTE_MODULE,\n      (config: __DbConnectionConfig<typeof REMOTE_MODULE>) =>\n        new DbConnection(config)\n    );\n  };\n\n  /** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */\n  override subscriptionBuilder = (): SubscriptionBuilder => {\n    return new SubscriptionBuilder(this);\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/offline_user_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.row({\n  identity: __t.identity().primaryKey(),\n  hasIncrementedCount: __t.u32().name('has_incremented_count'),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/types/index.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../../src/index';\n\n// Import all non-reducer types\nimport Counter from '../counter_type';\nimport User from '../user_type';\n\nexport type Counter = __Infer<typeof Counter>;\nexport type User = __Infer<typeof User>;\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/types/procedures.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../../src/index';\n\n// Import all procedure arg schemas\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/types/reducers.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from '../../../../src/index';\n\n// Import all reducer arg schemas\nimport ClearCounterReducer from '../clear_counter_reducer';\nimport IncrementCounterReducer from '../increment_counter_reducer';\n\nexport type ClearCounterParams = __Infer<typeof ClearCounterReducer>;\nexport type IncrementCounterParams = __Infer<typeof IncrementCounterReducer>;\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/user_table.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.row({\n  identity: __t.identity().primaryKey(),\n  hasIncrementedCount: __t.u32().name('has_incremented_count'),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/module_bindings/user_type.ts",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from '../../../src/index';\n\nexport default __t.object('User', {\n  identity: __t.identity(),\n  hasIncrementedCount: __t.u32(),\n});\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/pages/CounterPage.tsx",
    "content": "import { useReducer, useTable } from '../../../src/react';\nimport { tables, reducers } from '../module_bindings';\n\nexport default function CounterPage() {\n  const [counter] = useTable(tables.counter);\n  const incrementCounter = useReducer(reducers.incrementCounter);\n  const clearCounter = useReducer(reducers.clearCounter);\n\n  console.log('Rendering CounterPage, current counter:', counter);\n\n  return (\n    <>\n      <h1>Counter</h1>\n      <div className=\"card\">\n        <button onClick={() => incrementCounter()}>\n          count is {counter[0]?.count}\n        </button>\n        <p>\n          Click above to increment the count, click below to clear the count.\n        </p>\n        <button onClick={() => clearCounter()}>clear count</button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/src/pages/UserPage.tsx",
    "content": "import { useSpacetimeDB, useTable } from '../../../src/react';\nimport { tables, User } from '../module_bindings';\nimport { Infer } from '../../../src';\n\nexport default function UserPage() {\n  const connection = useSpacetimeDB();\n  const [users] = useTable(tables.user);\n\n  const identityHex = connection.identity?.toHexString();\n  const currentUser = users.find(\n    (u: Infer<typeof User>) => u.identity.toHexString() === identityHex\n  );\n\n  return (\n    <div>\n      <h1>User Page</h1>\n      {currentUser ? (\n        <>\n          <p>\n            <strong>Identity:</strong> {identityHex}\n          </p>\n          <p>\n            <strong>Times Incremented:</strong>{' '}\n            {currentUser.hasIncrementedCount}\n          </p>\n        </>\n      ) : (\n        <p>No user record found. Are you connected?</p>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/tsconfig.app.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ESNext\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n\n    /* Bundler mode */\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"moduleDetection\": \"force\",\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n\n    /* Linting */\n    \"strict\": true,\n    \"noFallthroughCasesInSwitch\": true\n  },\n  \"include\": [\"src/**/*\", \"vite.config.ts\", \"../src/**/*\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.app.json\"\n    },\n    {\n      \"path\": \"./tsconfig.node.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/test-react-router-app/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/algebraic_type.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport { AlgebraicType } from '../src/lib/algebraic_type';\n\ndescribe('AlgebraicType', () => {\n  test('intoMapKey handles all primitive types', () => {\n    const primitiveTypes: Array<[any['tag'], any]> = [\n      ['Bool', true],\n      ['I8', -8],\n      ['U8', 8],\n      ['I16', -16],\n      ['U16', 16],\n      ['I32', -32],\n      ['U32', 32],\n      ['I64', -64n],\n      ['U64', 64n],\n      ['I128', -128n],\n      ['U128', 128n],\n      ['I256', -256n],\n      ['U256', 256n],\n      ['F32', 32.32],\n      ['F64', 64.64],\n      ['String', 'hello'],\n    ];\n\n    for (const [tag, value] of primitiveTypes) {\n      const algebraicType = { tag, value: undefined };\n      const mapKey = AlgebraicType.intoMapKey(algebraicType, value);\n      expect(mapKey).toEqual(value);\n    }\n  });\n\n  test('intoMapKey handles complex types', () => {\n    const productType = AlgebraicType.Product({\n      elements: [{ name: 'a', algebraicType: AlgebraicType.I32 }],\n    });\n    const productValue = { a: 42 };\n\n    const mapKey = AlgebraicType.intoMapKey(productType, productValue);\n    // Fallback for complex types is base64 encoding of serialized value\n    expect(typeof mapKey).toEqual('string');\n    // 42 as i32 little-endian is 2A000000, which is KgAAAA== in base64\n    expect(mapKey).toEqual('KgAAAA==');\n  });\n\n  test('intoMapKey fallback serializes array types', () => {\n    const arrayType = AlgebraicType.Array(AlgebraicType.U16);\n    const arrayValue = [1, 2, 3];\n\n    const mapKey = AlgebraicType.intoMapKey(arrayType, arrayValue);\n    expect(typeof mapKey).toEqual('string');\n    // Serialized as: [len (u32), val1 (u16), val2 (u16), val3 (u16)]\n    expect(mapKey).toEqual('AwAAAAEAAgADAA==');\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/binary_read_write.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport { bobIdentity, encodeUser, sallyIdentity } from './utils';\nimport {\n  ServerMessage,\n  RowSizeHint,\n  TableUpdateRows,\n} from '../src/sdk/client_api/types';\nimport BinaryReader from '../src/lib/binary_reader';\nimport BinaryWriter from '../src/lib/binary_writer';\n\n/*\n// Generated by the following Rust code:\n\n#[cfg(test)]\nmod tests {\n    use rand::{thread_rng, Rng};\n    use spacetimedb_sats::{bsatn, i256, u256};\n\n    #[test]\n    fn make_some_numbers() {\n        let mut rng = thread_rng();\n        let v_u8: u8 = rng.gen();\n        let v_u16: u16 = rng.gen();\n        let v_u32: u32 = rng.gen();\n        let v_u64: u64 = rng.gen();\n        let v_u128: u128 = rng.gen();\n        let v_u256: u256 = u256::from_words(rng.gen(), rng.gen());\n        let v_i8: i8 = rng.gen();\n        let v_i16: i16 = rng.gen();\n        let v_i32: i32 = rng.gen();\n        let v_i64: i64 = rng.gen();\n        let v_i128: i128 = rng.gen();\n        let v_i256: i256 = i256::from_words(rng.gen(), rng.gen());\n\n        println!(\"['I8', {}, {:?}],\", v_i8, bsatn::to_vec(&v_i8).unwrap());\n        println!(\"['I16', {}, {:?}],\", v_i16, bsatn::to_vec(&v_i16).unwrap());\n        println!(\"['I32', {}, {:?}],\", v_i32, bsatn::to_vec(&v_i32).unwrap());\n        println!(\"['I64', BigInt('{}'), {:?}],\", v_i64, bsatn::to_vec(&v_i64).unwrap());\n        println!(\"['I128', BigInt('{}'), {:?}],\", v_i128, bsatn::to_vec(&v_i128).unwrap());\n        println!(\"['I256', BigInt('{}'), {:?}],\", v_i256, bsatn::to_vec(&v_i256).unwrap());\n\n        println!(\"['U8', {}, {:?}],\", v_u8, bsatn::to_vec(&v_u8).unwrap());\n        println!(\"['U16', {}, {:?}],\", v_u16, bsatn::to_vec(&v_u16).unwrap());\n        println!(\"['U32', {}, {:?}],\", v_u32, bsatn::to_vec(&v_u32).unwrap());\n        println!(\"['U64', BigInt('{}'), {:?}],\", v_u64, bsatn::to_vec(&v_u64).unwrap());\n        println!(\"['U128', BigInt('{}'), {:?}],\", v_u128, bsatn::to_vec(&v_u128).unwrap());\n        println!(\"['U256', BigInt('{}'), {:?}],\", v_u256, bsatn::to_vec(&v_u256).unwrap());\n        panic!();\n    }\n}\n*/\n\nconst testCases: Array<[string, bigint | number, Array<number>]> = [\n  ['I8', 48, [48]],\n  ['I16', 2910, [94, 11]],\n  ['I32', -799760706, [190, 158, 84, 208]],\n  [\n    'I64',\n    BigInt('-1541553498090056195'),\n    [253, 213, 20, 208, 66, 77, 155, 234],\n  ],\n  [\n    'I128',\n    BigInt('12547586996680216771838914786222604020'),\n    [244, 254, 202, 102, 17, 36, 114, 210, 182, 88, 120, 98, 205, 147, 112, 9],\n  ],\n  [\n    'I256',\n    BigInt(\n      '35334490670013506332541201493144667192747188790291257662501378603950330458369'\n    ),\n    [\n      1, 177, 117, 147, 65, 153, 110, 71, 110, 80, 45, 231, 208, 112, 149, 150,\n      251, 157, 51, 25, 129, 124, 13, 154, 238, 225, 7, 63, 237, 156, 30, 78,\n    ],\n  ],\n  ['U8', 63, [63]],\n  ['U16', 14776, [184, 57]],\n  ['U32', 2260346643, [19, 39, 186, 134]],\n  [\n    'U64',\n    BigInt('6355943419584016569'),\n    [185, 112, 201, 104, 221, 216, 52, 88],\n  ],\n  [\n    'U128',\n    BigInt('190443100270131819986139062814080853012'),\n    [20, 100, 201, 134, 99, 82, 196, 32, 34, 79, 25, 142, 199, 1, 70, 143],\n  ],\n  [\n    'U256',\n    BigInt(\n      '58716185326733447174109779681509939791568291171619953995835894271369692835957'\n    ),\n    [\n      117, 240, 99, 239, 213, 99, 55, 201, 2, 145, 4, 24, 0, 173, 62, 27, 124,\n      53, 44, 244, 71, 1, 156, 30, 111, 187, 149, 150, 229, 46, 208, 129,\n    ],\n  ],\n];\n\ndescribe('BinaryReader/Writer', () => {\n  test('correctly reads/writes little endian values', () => {\n    for (const [name, int, buf] of testCases) {\n      const arr = new Uint8Array(buf);\n      const reader = new BinaryReader(arr);\n\n      const read = (reader as any)['read' + name]();\n      expect(read).toEqual(int);\n\n      const writer = new BinaryWriter(0);\n      (writer as any)['write' + name](int);\n\n      expect(writer.getBuffer()).toEqual(arr);\n    }\n  });\n\n  test('correctly serializes and then deserializes a complicated web socket message', () => {\n    const user1 = { identity: bobIdentity, username: 'bob' };\n    const user2 = {\n      identity: sallyIdentity,\n      username: 'sally',\n    };\n    const binary = [...encodeUser(user1)].concat([...encodeUser(user2)]);\n    const transactionUpdate = ServerMessage.TransactionUpdate({\n      querySets: [\n        {\n          querySetId: { id: 0 },\n          tables: [\n            {\n              tableName: 'user',\n              rows: [\n                TableUpdateRows.PersistentTable({\n                  inserts: {\n                    sizeHint: RowSizeHint.FixedSize(0),\n                    rowsData: new Uint8Array(binary),\n                  },\n                  deletes: {\n                    sizeHint: RowSizeHint.FixedSize(0),\n                    rowsData: new Uint8Array([]),\n                  },\n                }),\n              ],\n            },\n          ],\n        },\n      ],\n    });\n    const writer = new BinaryWriter(1024);\n    ServerMessage.serialize(writer, transactionUpdate);\n    const rawBytes = writer.getBuffer();\n\n    const deserializedTransactionUpdate = ServerMessage.deserialize(\n      new BinaryReader(rawBytes)\n    );\n    expect(deserializedTransactionUpdate).toEqual(transactionUpdate);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/client_query.test.ts",
    "content": "import { describe, expect, it, assertType } from 'vitest';\nimport { Identity } from '../src/lib/identity';\nimport { and, not, or, toSql } from '../src/lib/query';\nimport { tables } from '../test-app/src/module_bindings';\n\ndescribe('ClientQuery.toSql', () => {\n  it('renders a full-table scan when no filters are applied', () => {\n    const sql = toSql(tables.player.build());\n\n    expect(sql).toBe('SELECT * FROM \"player\"');\n  });\n\n  it('renders a WHERE clause for simple equality filters', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.eq(\"O'Brian\")).build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE \"player\".\"name\" = 'O''Brian'`\n    );\n  });\n\n  it('renders numeric literals and column references', () => {\n    const sql = toSql(tables.player.where(row => row.id.eq(42)).build());\n\n    expect(sql).toBe(`SELECT * FROM \"player\" WHERE \"player\".\"id\" = 42`);\n  });\n\n  it('renders AND clauses across multiple predicates', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => and(row.name.eq('Alice'), row.id.eq(30)))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE (\"player\".\"name\" = 'Alice') AND (\"player\".\"id\" = 30)`\n    );\n  });\n\n  it('renders NOT clauses around subpredicates', () => {\n    const sql = toSql(\n      tables.player.where(row => not(row.name.eq('Bob'))).build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE NOT (\"player\".\"name\" = 'Bob')`\n    );\n  });\n\n  it('accumulates multiple filters with AND logic', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => row.name.eq('Eve'))\n        .where(row => row.id.eq(25))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE (\"player\".\"name\" = 'Eve') AND (\"player\".\"id\" = 25)`\n    );\n  });\n\n  it('renders OR clauses across multiple predicates', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => or(row.name.eq('Carol'), row.name.eq('Dave')))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE (\"player\".\"name\" = 'Carol') OR (\"player\".\"name\" = 'Dave')`\n    );\n  });\n\n  it('renders Identity literals using their hex form', () => {\n    const identity = new Identity(\n      '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'\n    );\n    const sql = toSql(\n      tables.user.where(row => row.identity.eq(identity)).build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"user\" WHERE \"user\".\"identity\" = 0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`\n    );\n  });\n\n  it('renders semijoin queries without additional filters', () => {\n    const sql = toSql(\n      tables.player\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\"`\n    );\n  });\n\n  it('renders semijoin queries alongside existing predicates', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => row.id.eq(42))\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\" WHERE \"player\".\"id\" = 42`\n    );\n  });\n\n  it('escapes literals when rendering semijoin filters', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => row.name.eq(\"O'Brian\"))\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\" WHERE \"player\".\"name\" = 'O''Brian'`\n    );\n  });\n\n  it('renders compound AND filters for semijoin queries', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => and(row.name.eq('Alice'), row.id.eq(30)))\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\" WHERE (\"player\".\"name\" = 'Alice') AND (\"player\".\"id\" = 30)`\n    );\n  });\n\n  it('basic where', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.eq('Gadget')).build()\n    );\n    expect(sql).toBe(`SELECT * FROM \"player\" WHERE \"player\".\"name\" = 'Gadget'`);\n  });\n\n  it('basic where ne', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.ne('Gadget')).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE \"player\".\"name\" <> 'Gadget'`\n    );\n  });\n\n  it('basic where lt', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.lt('Gadget')).build()\n    );\n    expect(sql).toBe(`SELECT * FROM \"player\" WHERE \"player\".\"name\" < 'Gadget'`);\n  });\n\n  it('basic where lte', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.lte('Gadget')).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE \"player\".\"name\" <= 'Gadget'`\n    );\n  });\n\n  it('basic where gt', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.gt('Gadget')).build()\n    );\n    expect(sql).toBe(`SELECT * FROM \"player\" WHERE \"player\".\"name\" > 'Gadget'`);\n  });\n\n  it('basic where gte', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.gte('Gadget')).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE \"player\".\"name\" >= 'Gadget'`\n    );\n  });\n\n  it('basic semijoin', () => {\n    const sql = toSql(\n      tables.player\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\"`\n    );\n  });\n\n  it('basic left semijoin', () => {\n    const sql = toSql(\n      tables.player\n        .leftSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT \"player\".* FROM \"unindexed_player\" JOIN \"player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\"`\n    );\n  });\n\n  it('method-style chaining with .and()', () => {\n    const sql = toSql(\n      tables.player.where(row => row.id.gt(20).and(row.id.lt(30))).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE (\"player\".\"id\" > 20) AND (\"player\".\"id\" < 30)`\n    );\n  });\n\n  it('method-style chaining with .or()', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => row.name.eq('Carol').or(row.name.eq('Dave')))\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE (\"player\".\"name\" = 'Carol') OR (\"player\".\"name\" = 'Dave')`\n    );\n  });\n\n  it('method-style chaining with .not()', () => {\n    const sql = toSql(\n      tables.player.where(row => row.name.eq('Bob').not()).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"player\" WHERE NOT (\"player\".\"name\" = 'Bob')`\n    );\n  });\n\n  it('semijoin with filters on both sides', () => {\n    const sql = toSql(\n      tables.player\n        .where(row => row.id.eq(42))\n        .rightSemijoin(tables.unindexed_player, (player, other) =>\n          other.id.eq(player.id)\n        )\n        .where(row => row.name.eq('Gadget'))\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT \"unindexed_player\".* FROM \"player\" JOIN \"unindexed_player\" ON \"unindexed_player\".\"id\" = \"player\".\"id\" WHERE (\"player\".\"id\" = 42) AND (\"unindexed_player\".\"name\" = 'Gadget')`\n    );\n  });\n});\n\n// Type-level tests: verify query builder expressions expose toSql() and are\n// compatible with useTable's parameter type ({ toSql(): string } & Record<string, any>).\n// These use assertType which causes a *compile-time* failure if the types are wrong.\ndescribe('useTable type compatibility', () => {\n  type UseTableQuery = { toSql(): string } & Record<string, any>;\n\n  it('table ref (bare table) is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(tables.player);\n  });\n\n  it('.where() result is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(tables.player.where(r => r.name.eq('Hello')));\n  });\n\n  it('chained .where() result is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(\n      tables.player.where(r => r.name.eq('Hello')).where(r => r.id.eq(1))\n    );\n  });\n\n  it('rightSemijoin result is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(\n      tables.player.rightSemijoin(tables.unindexed_player, (p, o) =>\n        o.id.eq(p.id)\n      )\n    );\n  });\n\n  it('leftSemijoin result is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(\n      tables.player.leftSemijoin(tables.unindexed_player, (p, o) =>\n        o.id.eq(p.id)\n      )\n    );\n  });\n\n  it('semijoin with .where() is assignable to useTable query param', () => {\n    assertType<UseTableQuery>(\n      tables.player\n        .rightSemijoin(tables.unindexed_player, (p, o) => o.id.eq(p.id))\n        .where(r => r.name.eq('test'))\n    );\n  });\n\n  it('table ref exposes toSql() returning string', () => {\n    const sql: string = tables.player.toSql();\n    expect(typeof sql).toBe('string');\n  });\n\n  it('.where() result exposes toSql() returning string', () => {\n    const sql: string = tables.player.where(r => r.name.eq('x')).toSql();\n    expect(typeof sql).toBe('string');\n  });\n\n  it('semijoin result exposes toSql() returning string', () => {\n    const sql: string = tables.player\n      .rightSemijoin(tables.unindexed_player, (p, o) => o.id.eq(p.id))\n      .toSql();\n    expect(typeof sql).toBe('string');\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/column_metadata_validation.test.ts",
    "content": "/**\n * Type-level tests for column metadata validation.\n *\n * These tests verify that invalid combinations of column attributes\n * (like default + primaryKey) produce compile-time errors.\n *\n * To run these tests, simply run `pnpm tsc --noEmit` on this file.\n * The tests use @ts-expect-error to assert that certain combinations\n * should produce type errors.\n */\n\nimport { describe, it, expect } from 'vitest';\nimport { t } from '../src/lib/type_builders';\nimport { table } from '../src/lib/table';\n\n// ============================================================\n// VALID COMBINATIONS - These should compile without errors\n// ============================================================\n\n// Valid: default alone\nconst validDefault = table(\n  { name: 'valid_default' },\n  {\n    id: t.u64().primaryKey(),\n    score: t.u32().default(0),\n  }\n);\n\n// Valid: primaryKey alone\nconst validPrimaryKey = table(\n  { name: 'valid_primary_key' },\n  {\n    id: t.u64().primaryKey(),\n    name: t.string(),\n  }\n);\n\n// Valid: unique alone\nconst validUnique = table(\n  { name: 'valid_unique' },\n  {\n    id: t.u64().primaryKey(),\n    email: t.string().unique(),\n  }\n);\n\n// Valid: autoInc alone\nconst validAutoInc = table(\n  { name: 'valid_auto_inc' },\n  {\n    id: t.u64().primaryKey().autoInc(),\n    name: t.string(),\n  }\n);\n\n// Valid: index with default\nconst validIndexWithDefault = table(\n  { name: 'valid_index_default' },\n  {\n    id: t.u64().primaryKey(),\n    score: t.u32().index().default(0),\n  }\n);\n\n// ============================================================\n// INVALID COMBINATIONS - These should produce compile errors\n// ============================================================\n\n// Invalid: default + primaryKey\n// @ts-expect-error - default() cannot be combined with primaryKey()\nconst invalidDefaultPrimaryKey = table(\n  { name: 'invalid_default_pk' },\n  {\n    id: t.u64().default(0n).primaryKey(),\n    name: t.string(),\n  }\n);\n\n// Invalid: primaryKey + default\n// @ts-expect-error - primaryKey() cannot be combined with default()\nconst invalidPrimaryKeyDefault = table(\n  { name: 'invalid_pk_default' },\n  {\n    id: t.u64().primaryKey().default(0n),\n    name: t.string(),\n  }\n);\n\n// Invalid: default + unique\n// @ts-expect-error - default() cannot be combined with unique()\nconst invalidDefaultUnique = table(\n  { name: 'invalid_default_unique' },\n  {\n    id: t.u64().primaryKey(),\n    email: t.string().default('').unique(),\n  }\n);\n\n// Invalid: unique + default\n// @ts-expect-error - unique() cannot be combined with default()\nconst invalidUniqueDefault = table(\n  { name: 'invalid_unique_default' },\n  {\n    id: t.u64().primaryKey(),\n    email: t.string().unique().default(''),\n  }\n);\n\n// Invalid: default + autoInc\n// @ts-expect-error - default() cannot be combined with autoInc()\nconst invalidDefaultAutoInc = table(\n  { name: 'invalid_default_autoinc' },\n  {\n    id: t.u64().default(0n).autoInc(),\n    name: t.string(),\n  }\n);\n\n// Invalid: autoInc + default\n// @ts-expect-error - autoInc() cannot be combined with default()\nconst invalidAutoIncDefault = table(\n  { name: 'invalid_autoinc_default' },\n  {\n    id: t.u64().autoInc().default(0n),\n    name: t.string(),\n  }\n);\n\n// Suppress unused variable warnings\nvoid validDefault;\nvoid validPrimaryKey;\nvoid validUnique;\nvoid validAutoInc;\nvoid validIndexWithDefault;\nvoid invalidDefaultPrimaryKey;\nvoid invalidPrimaryKeyDefault;\nvoid invalidDefaultUnique;\nvoid invalidUniqueDefault;\nvoid invalidDefaultAutoInc;\nvoid invalidAutoIncDefault;\n\ndescribe('Column metadata validation', () => {\n  it('type-level tests compile correctly (see @ts-expect-error comments above)', () => {\n    // This test exists to satisfy vitest - the actual validation happens\n    // at compile time via @ts-expect-error annotations above.\n    // If this file compiles, the type-level tests have passed.\n    expect(true).toBe(true);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/connection_manager.test.ts",
    "content": "import { describe, test, expect, beforeEach, vi, afterEach } from 'vitest';\nimport { ConnectionId } from '../src';\nimport { Identity } from '../src/lib/identity';\n\n// Test identity helper\nconst testIdentity = Identity.fromString(\n  '0000000000000000000000000000000000000000000000000000000000000069'\n);\n\n// We need to test a fresh instance each time, so we import the class directly\n// and create new instances rather than using the singleton\n// ConnectionState type matching the implementation\ntype ConnectionState = {\n  isActive: boolean;\n  identity?: Identity;\n  token?: string;\n  connectionId: ConnectionId;\n  connectionError?: Error;\n};\n\ntype Listener = () => void;\n\ntype ErrorContextInterface = {\n  isActive: boolean;\n};\n\ntype ManagedConnection = {\n  connection?: MockConnection;\n  refCount: number;\n  state: ConnectionState;\n  listeners: Set<Listener>;\n  pendingRelease: ReturnType<typeof setTimeout> | null;\n  onConnect?: (conn: MockConnection) => void;\n  onDisconnect?: (ctx: ErrorContextInterface, error?: Error) => void;\n  onConnectError?: (ctx: ErrorContextInterface, error: Error) => void;\n};\n\nfunction defaultState(): ConnectionState {\n  return {\n    isActive: false,\n    identity: undefined,\n    token: undefined,\n    connectionId: ConnectionId.random(),\n    connectionError: undefined,\n  };\n}\n\n// Mock connection for testing\nclass MockConnection {\n  isActive = false;\n  identity?: Identity;\n  token?: string;\n  connectionId = ConnectionId.random();\n  disconnected = false;\n\n  #onConnectCallbacks: Set<(conn: MockConnection) => void> = new Set();\n  #onDisconnectCallbacks: Set<\n    (ctx: ErrorContextInterface, error?: Error) => void\n  > = new Set();\n  #onConnectErrorCallbacks: Set<\n    (ctx: ErrorContextInterface, error: Error) => void\n  > = new Set();\n\n  disconnect(): void {\n    this.disconnected = true;\n    this.isActive = false;\n  }\n\n  removeOnConnect(cb: (conn: MockConnection) => void): void {\n    this.#onConnectCallbacks.delete(cb);\n  }\n\n  removeOnDisconnect(\n    cb: (ctx: ErrorContextInterface, error?: Error) => void\n  ): void {\n    this.#onDisconnectCallbacks.delete(cb);\n  }\n\n  removeOnConnectError(\n    cb: (ctx: ErrorContextInterface, error: Error) => void\n  ): void {\n    this.#onConnectErrorCallbacks.delete(cb);\n  }\n\n  // Test helpers to simulate connection events\n  simulateConnect(identity: Identity, token: string): void {\n    this.isActive = true;\n    this.identity = identity;\n    this.token = token;\n    for (const cb of this.#onConnectCallbacks) {\n      cb(this);\n    }\n  }\n\n  simulateDisconnect(error?: Error): void {\n    this.isActive = false;\n    for (const cb of this.#onDisconnectCallbacks) {\n      cb({ isActive: false }, error);\n    }\n  }\n\n  simulateConnectError(error: Error): void {\n    this.isActive = false;\n    for (const cb of this.#onConnectErrorCallbacks) {\n      cb({ isActive: false }, error);\n    }\n  }\n\n  registerOnConnect(cb: (conn: MockConnection) => void): void {\n    this.#onConnectCallbacks.add(cb);\n  }\n\n  registerOnDisconnect(\n    cb: (ctx: ErrorContextInterface, error?: Error) => void\n  ): void {\n    this.#onDisconnectCallbacks.add(cb);\n  }\n\n  registerOnConnectError(\n    cb: (ctx: ErrorContextInterface, error: Error) => void\n  ): void {\n    this.#onConnectErrorCallbacks.add(cb);\n  }\n}\n\n// Mock builder for testing\n// The real builder pattern allows registering callbacks before OR after build()\n// ConnectionManager calls builder.onConnect() AFTER build(), so we need to handle that\nclass MockBuilder {\n  #connection: MockConnection;\n\n  constructor(connection: MockConnection) {\n    this.#connection = connection;\n  }\n\n  build(): MockConnection {\n    return this.#connection;\n  }\n\n  onConnect(cb: (conn: MockConnection) => void): MockBuilder {\n    // Register immediately on connection (works before or after build)\n    this.#connection.registerOnConnect(cb);\n    return this;\n  }\n\n  onDisconnect(\n    cb: (ctx: ErrorContextInterface, error?: Error) => void\n  ): MockBuilder {\n    this.#connection.registerOnDisconnect(cb);\n    return this;\n  }\n\n  onConnectError(\n    cb: (ctx: ErrorContextInterface, error: Error) => void\n  ): MockBuilder {\n    this.#connection.registerOnConnectError(cb);\n    return this;\n  }\n}\n\n// Re-implement ConnectionManagerImpl for testing (to avoid singleton issues)\nclass ConnectionManagerImpl {\n  #connections = new Map<string, ManagedConnection>();\n\n  static getKey(uri: string, moduleName: string): string {\n    return `${uri}::${moduleName}`;\n  }\n\n  getKey(uri: string, moduleName: string): string {\n    return ConnectionManagerImpl.getKey(uri, moduleName);\n  }\n\n  #ensureEntry(key: string): ManagedConnection {\n    const existing = this.#connections.get(key);\n    if (existing) {\n      return existing;\n    }\n    const managed: ManagedConnection = {\n      connection: undefined,\n      refCount: 0,\n      state: defaultState(),\n      listeners: new Set(),\n      pendingRelease: null,\n    };\n    this.#connections.set(key, managed);\n    return managed;\n  }\n\n  #notify(managed: ManagedConnection): void {\n    for (const listener of managed.listeners) {\n      listener();\n    }\n  }\n\n  retain(key: string, builder: MockBuilder): MockConnection {\n    const managed = this.#ensureEntry(key);\n    if (managed.pendingRelease) {\n      clearTimeout(managed.pendingRelease);\n      managed.pendingRelease = null;\n    }\n    managed.refCount += 1;\n    if (managed.connection) {\n      return managed.connection;\n    }\n\n    const connection = builder.build();\n    managed.connection = connection;\n\n    const updateState = (updates: Partial<ConnectionState>) => {\n      managed.state = { ...managed.state, ...updates };\n      this.#notify(managed);\n    };\n\n    updateState({\n      isActive: connection.isActive,\n      identity: connection.identity,\n      token: connection.token,\n      connectionId: connection.connectionId,\n      connectionError: undefined,\n    });\n\n    managed.onConnect = conn => {\n      updateState({\n        isActive: conn.isActive,\n        identity: conn.identity,\n        token: conn.token,\n        connectionId: conn.connectionId,\n        connectionError: undefined,\n      });\n    };\n\n    managed.onDisconnect = (ctx, error) => {\n      updateState({\n        isActive: ctx.isActive,\n        connectionError: error ?? undefined,\n      });\n    };\n\n    managed.onConnectError = (ctx, error) => {\n      updateState({\n        isActive: ctx.isActive,\n        connectionError: error,\n      });\n    };\n\n    builder.onConnect(managed.onConnect);\n    builder.onDisconnect(managed.onDisconnect);\n    builder.onConnectError(managed.onConnectError);\n\n    return connection;\n  }\n\n  release(key: string): void {\n    const managed = this.#connections.get(key);\n    if (!managed) {\n      return;\n    }\n\n    managed.refCount -= 1;\n    if (managed.refCount > 0 || managed.pendingRelease) {\n      return;\n    }\n\n    managed.pendingRelease = setTimeout(() => {\n      managed.pendingRelease = null;\n      if (managed.refCount > 0) {\n        return;\n      }\n      if (managed.connection) {\n        if (managed.onConnect) {\n          managed.connection.removeOnConnect(managed.onConnect);\n        }\n        if (managed.onDisconnect) {\n          managed.connection.removeOnDisconnect(managed.onDisconnect);\n        }\n        if (managed.onConnectError) {\n          managed.connection.removeOnConnectError(managed.onConnectError);\n        }\n        managed.connection.disconnect();\n      }\n      this.#connections.delete(key);\n    }, 0);\n  }\n\n  subscribe(key: string, listener: Listener): () => void {\n    const managed = this.#ensureEntry(key);\n    managed.listeners.add(listener);\n    return () => {\n      managed.listeners.delete(listener);\n      if (\n        managed.refCount <= 0 &&\n        managed.listeners.size === 0 &&\n        !managed.connection\n      ) {\n        this.#connections.delete(key);\n      }\n    };\n  }\n\n  getSnapshot(key: string): ConnectionState | undefined {\n    return this.#connections.get(key)?.state;\n  }\n\n  getConnection(key: string): MockConnection | null {\n    return this.#connections.get(key)?.connection ?? null;\n  }\n\n  // Test helper to check internal state\n  _getRefCount(key: string): number {\n    return this.#connections.get(key)?.refCount ?? 0;\n  }\n\n  _hasConnection(key: string): boolean {\n    return this.#connections.get(key)?.connection !== undefined;\n  }\n\n  _hasEntry(key: string): boolean {\n    return this.#connections.has(key);\n  }\n\n  _hasPendingRelease(key: string): boolean {\n    return this.#connections.get(key)?.pendingRelease !== null;\n  }\n}\n\ndescribe('ConnectionManager', () => {\n  let manager: ConnectionManagerImpl;\n\n  beforeEach(() => {\n    vi.useFakeTimers();\n    manager = new ConnectionManagerImpl();\n  });\n\n  afterEach(() => {\n    vi.useRealTimers();\n  });\n\n  describe('getKey', () => {\n    test('generates consistent keys from uri and moduleName', () => {\n      const key1 = manager.getKey('ws://localhost:3000', 'myModule');\n      const key2 = manager.getKey('ws://localhost:3000', 'myModule');\n      expect(key1).toBe(key2);\n      expect(key1).toBe('ws://localhost:3000::myModule');\n    });\n\n    test('generates different keys for different uris', () => {\n      const key1 = manager.getKey('ws://localhost:3000', 'myModule');\n      const key2 = manager.getKey('ws://localhost:4000', 'myModule');\n      expect(key1).not.toBe(key2);\n    });\n\n    test('generates different keys for different modules', () => {\n      const key1 = manager.getKey('ws://localhost:3000', 'moduleA');\n      const key2 = manager.getKey('ws://localhost:3000', 'moduleB');\n      expect(key1).not.toBe(key2);\n    });\n\n    test('static getKey matches instance method', () => {\n      const uri = 'ws://localhost:3000';\n      const moduleName = 'myModule';\n      expect(ConnectionManagerImpl.getKey(uri, moduleName)).toBe(\n        manager.getKey(uri, moduleName)\n      );\n    });\n  });\n\n  describe('retain', () => {\n    test('creates connection on first retain', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      const connection = manager.retain(key, builder);\n\n      expect(connection).toBe(mockConnection);\n      expect(manager._getRefCount(key)).toBe(1);\n    });\n\n    test('returns same connection on subsequent retains', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      const connection1 = manager.retain(key, builder);\n      const connection2 = manager.retain(key, builder);\n\n      expect(connection1).toBe(connection2);\n      expect(manager._getRefCount(key)).toBe(2);\n    });\n\n    test('increments refCount on each retain', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(1);\n\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(2);\n\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(3);\n    });\n\n    test('cancels pending release when retaining again', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n\n      expect(manager._hasPendingRelease(key)).toBe(true);\n\n      manager.retain(key, builder);\n\n      expect(manager._hasPendingRelease(key)).toBe(false);\n      expect(manager._getRefCount(key)).toBe(1);\n      expect(mockConnection.disconnected).toBe(false);\n    });\n  });\n\n  describe('release', () => {\n    test('decrements refCount on release', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(2);\n\n      manager.release(key);\n      expect(manager._getRefCount(key)).toBe(1);\n    });\n\n    test('schedules cleanup when refCount reaches zero', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n\n      expect(manager._hasPendingRelease(key)).toBe(true);\n      expect(mockConnection.disconnected).toBe(false);\n    });\n\n    test('disconnects after timeout when refCount is zero', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n\n      vi.runAllTimers();\n\n      expect(mockConnection.disconnected).toBe(true);\n      expect(manager._hasConnection(key)).toBe(false);\n    });\n\n    test('does not disconnect if re-retained before timeout', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n      manager.retain(key, builder);\n\n      vi.runAllTimers();\n\n      expect(mockConnection.disconnected).toBe(false);\n      expect(manager._hasConnection(key)).toBe(true);\n    });\n\n    test('does nothing when releasing unknown key', () => {\n      expect(() => manager.release('unknown-key')).not.toThrow();\n    });\n\n    test('does not schedule multiple cleanups', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.retain(key, builder);\n      manager.release(key);\n      manager.release(key);\n\n      // Should only have one pending release\n      expect(manager._hasPendingRelease(key)).toBe(true);\n\n      vi.runAllTimers();\n\n      expect(mockConnection.disconnected).toBe(true);\n    });\n  });\n\n  describe('React StrictMode simulation', () => {\n    test('survives mount → unmount → remount cycle', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      // Mount: retain\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(1);\n\n      // Unmount: release (schedules cleanup)\n      manager.release(key);\n      expect(manager._getRefCount(key)).toBe(0);\n      expect(manager._hasPendingRelease(key)).toBe(true);\n\n      // Remount: retain again (cancels cleanup)\n      manager.retain(key, builder);\n      expect(manager._getRefCount(key)).toBe(1);\n      expect(manager._hasPendingRelease(key)).toBe(false);\n\n      // Let any timers run\n      vi.runAllTimers();\n\n      // Connection should still be active\n      expect(mockConnection.disconnected).toBe(false);\n      expect(manager.getConnection(key)).toBe(mockConnection);\n    });\n\n    test('maintains single connection across multiple StrictMode cycles', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      // First cycle\n      manager.retain(key, builder);\n      manager.release(key);\n      manager.retain(key, builder);\n\n      // Second cycle (nested component)\n      manager.retain(key, builder);\n      manager.release(key);\n      manager.retain(key, builder);\n\n      vi.runAllTimers();\n\n      expect(mockConnection.disconnected).toBe(false);\n      expect(manager._getRefCount(key)).toBe(2);\n    });\n  });\n\n  describe('subscribe', () => {\n    test('adds listener and returns unsubscribe function', () => {\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      const unsubscribe = manager.subscribe(key, listener);\n\n      expect(typeof unsubscribe).toBe('function');\n    });\n\n    test('notifies listeners on state change', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      manager.subscribe(key, listener);\n      manager.retain(key, builder);\n\n      // Initial state update during retain\n      expect(listener).toHaveBeenCalled();\n    });\n\n    test('notifies listeners when connection state changes', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      manager.subscribe(key, listener);\n      manager.retain(key, builder);\n      listener.mockClear();\n\n      const identity = testIdentity;\n      mockConnection.simulateConnect(identity, 'test-token');\n\n      expect(listener).toHaveBeenCalled();\n    });\n\n    test('stops notifying after unsubscribe', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      const unsubscribe = manager.subscribe(key, listener);\n      manager.retain(key, builder);\n      listener.mockClear();\n\n      unsubscribe();\n\n      const identity = testIdentity;\n      mockConnection.simulateConnect(identity, 'test-token');\n\n      expect(listener).not.toHaveBeenCalled();\n    });\n\n    test('cleans up entry when no listeners and no connection', () => {\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      const unsubscribe = manager.subscribe(key, listener);\n      expect(manager._hasConnection(key)).toBe(false);\n\n      unsubscribe();\n\n      // Entry should be cleaned up since there's no connection and no listeners\n      expect(manager.getSnapshot(key)).toBeUndefined();\n    });\n\n    test('does not clean up entry when connection exists', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n      const listener = vi.fn();\n\n      const unsubscribe = manager.subscribe(key, listener);\n      manager.retain(key, builder);\n      unsubscribe();\n\n      // Entry should still exist because connection is active\n      expect(manager.getSnapshot(key)).toBeDefined();\n    });\n  });\n\n  describe('getSnapshot', () => {\n    test('returns undefined for unknown key', () => {\n      expect(manager.getSnapshot('unknown-key')).toBeUndefined();\n    });\n\n    test('returns state after retain', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      const snapshot = manager.getSnapshot(key);\n\n      expect(snapshot).toBeDefined();\n      expect(snapshot?.isActive).toBe(false);\n    });\n\n    test('reflects connection state changes', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n\n      const identity = testIdentity;\n      mockConnection.simulateConnect(identity, 'test-token');\n\n      const snapshot = manager.getSnapshot(key);\n      expect(snapshot?.isActive).toBe(true);\n      expect(snapshot?.identity).toBe(identity);\n      expect(snapshot?.token).toBe('test-token');\n    });\n\n    test('reflects disconnect state', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      const identity = testIdentity;\n      mockConnection.simulateConnect(identity, 'test-token');\n      mockConnection.simulateDisconnect();\n\n      const snapshot = manager.getSnapshot(key);\n      expect(snapshot?.isActive).toBe(false);\n    });\n\n    test('reflects connection error', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      const error = new Error('Connection failed');\n      mockConnection.simulateConnectError(error);\n\n      const snapshot = manager.getSnapshot(key);\n      expect(snapshot?.isActive).toBe(false);\n      expect(snapshot?.connectionError).toBe(error);\n    });\n  });\n\n  describe('getConnection', () => {\n    test('returns null for unknown key', () => {\n      expect(manager.getConnection('unknown-key')).toBeNull();\n    });\n\n    test('returns connection after retain', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n\n      expect(manager.getConnection(key)).toBe(mockConnection);\n    });\n\n    test('returns null after cleanup', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n      vi.runAllTimers();\n\n      expect(manager.getConnection(key)).toBeNull();\n    });\n  });\n\n  describe('multiple connections', () => {\n    test('manages multiple independent connections', () => {\n      const connection1 = new MockConnection();\n      const connection2 = new MockConnection();\n      const builder1 = new MockBuilder(connection1);\n      const builder2 = new MockBuilder(connection2);\n      const key1 = 'connection-1';\n      const key2 = 'connection-2';\n\n      manager.retain(key1, builder1);\n      manager.retain(key2, builder2);\n\n      expect(manager.getConnection(key1)).toBe(connection1);\n      expect(manager.getConnection(key2)).toBe(connection2);\n      expect(manager._getRefCount(key1)).toBe(1);\n      expect(manager._getRefCount(key2)).toBe(1);\n    });\n\n    test('releases connections independently', () => {\n      const connection1 = new MockConnection();\n      const connection2 = new MockConnection();\n      const builder1 = new MockBuilder(connection1);\n      const builder2 = new MockBuilder(connection2);\n      const key1 = 'connection-1';\n      const key2 = 'connection-2';\n\n      manager.retain(key1, builder1);\n      manager.retain(key2, builder2);\n      manager.release(key1);\n      vi.runAllTimers();\n\n      expect(connection1.disconnected).toBe(true);\n      expect(connection2.disconnected).toBe(false);\n      expect(manager.getConnection(key1)).toBeNull();\n      expect(manager.getConnection(key2)).toBe(connection2);\n    });\n  });\n\n  describe('callback cleanup', () => {\n    test('removes callbacks on disconnect', () => {\n      const mockConnection = new MockConnection();\n      const builder = new MockBuilder(mockConnection);\n      const key = 'test-key';\n\n      manager.retain(key, builder);\n      manager.release(key);\n      vi.runAllTimers();\n\n      // After cleanup, simulating events should not cause issues\n      // (callbacks were removed)\n      const identity = testIdentity;\n      expect(() => {\n        mockConnection.simulateConnect(identity, 'test-token');\n      }).not.toThrow();\n    });\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/db_connection.test.ts",
    "content": "import { beforeEach, describe, expect, test } from 'vitest';\nimport {\n  BinaryWriter,\n  ConnectionId,\n  Identity,\n  InternalError,\n  SenderError,\n  Timestamp,\n  type Infer,\n} from '../src';\nimport { ServerMessage } from '../src/sdk/client_api/types';\nimport WebsocketTestAdapter from '../src/sdk/websocket_test_adapter';\nimport { DbConnection } from '../test-app/src/module_bindings';\nimport User from '../test-app/src/module_bindings/user_table';\nimport {\n  anIdentity,\n  bobIdentity,\n  encodePlayer,\n  encodeUser,\n  makeQuerySetUpdate,\n  sallyIdentity,\n} from './utils';\n\nclass Deferred<T> {\n  #isResolved: boolean = false;\n  #isRejected: boolean = false;\n  #resolve: (value: T | PromiseLike<T>) => void = () => {};\n  #reject: (reason?: any) => void = () => {};\n  promise: Promise<T>;\n\n  constructor() {\n    this.promise = new Promise<T>((resolve, reject) => {\n      this.#resolve = resolve;\n      this.#reject = reject;\n    });\n  }\n\n  // Getter for isResolved\n  get isResolved(): boolean {\n    return this.#isResolved;\n  }\n\n  // Getter for isRejected\n  get isRejected(): boolean {\n    return this.#isRejected;\n  }\n\n  // Resolve method\n  resolve(value: T): void {\n    if (!this.#isResolved && !this.#isRejected) {\n      this.#isResolved = true;\n      this.#resolve(value);\n    }\n  }\n\n  // Reject method\n  reject(reason?: any): void {\n    if (!this.#isResolved && !this.#isRejected) {\n      this.#isRejected = true;\n      this.#reject(reason);\n    }\n  }\n}\n\nbeforeEach(() => {});\n\nfunction getLastCallReducerRequestId(wsAdapter: WebsocketTestAdapter): number {\n  for (let i = wsAdapter.outgoingMessages.length - 1; i >= 0; i--) {\n    const message = wsAdapter.outgoingMessages[i];\n    if (message.tag === 'CallReducer') {\n      return message.value.requestId;\n    }\n\n    console.log('Message: ', JSON.stringify(message));\n  }\n  console.log('Outgoing messages length: ', wsAdapter.outgoingMessages.length);\n  throw new Error('No CallReducer message found in messageQueue.');\n}\n\nfunction getLastSubscribeMessageInfo(wsAdapter: WebsocketTestAdapter): {\n  requestId: number;\n  querySetId: number;\n} {\n  for (let i = wsAdapter.outgoingMessages.length - 1; i >= 0; i--) {\n    const message = wsAdapter.outgoingMessages[i];\n    if (message.tag === 'Subscribe') {\n      return {\n        requestId: message.value.requestId,\n        querySetId: message.value.querySetId.id,\n      };\n    }\n  }\n  throw new Error('No Subscribe message found in messageQueue.');\n}\n\nfunction makeReducerResult(\n  requestId: number,\n  reducerQuerySetUpdate: ReturnType<typeof makeQuerySetUpdate>\n) {\n  return ServerMessage.ReducerResult({\n    requestId,\n    timestamp: new Timestamp(0n),\n    result: {\n      tag: 'Ok',\n      value: {\n        retValue: new Uint8Array(),\n        transactionUpdate: {\n          querySets: [reducerQuerySetUpdate],\n        },\n      },\n    },\n  });\n}\n\nfunction makeReducerErrorResult(requestId: number, error: string) {\n  const errorWriter = new BinaryWriter(64);\n  errorWriter.writeString(error);\n  const errorPayload = errorWriter.getBuffer();\n  return ServerMessage.ReducerResult({\n    requestId,\n    timestamp: new Timestamp(0n),\n    result: {\n      tag: 'Err',\n      value: errorPayload,\n    },\n  });\n}\n\nfunction makeReducerInternalErrorResult(requestId: number, error: string) {\n  return ServerMessage.ReducerResult({\n    requestId,\n    timestamp: new Timestamp(0n),\n    result: {\n      tag: 'InternalError',\n      value: error,\n    },\n  });\n}\n\ndescribe('DbConnection', () => {\n  test('call onConnectError callback after websocket connection failed to be established', async () => {\n    const onConnectErrorPromise = new Deferred<void>();\n\n    let errorCalled = false;\n    let connectCalled = false;\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(() => {\n        return Promise.reject(new Error('Failed to connect'));\n      })\n      .onConnect(() => {\n        connectCalled = true;\n      })\n      .onConnectError(() => {\n        errorCalled = true;\n        onConnectErrorPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    await onConnectErrorPromise.promise;\n    expect(errorCalled).toBeTruthy();\n    expect(connectCalled).toBeFalsy();\n  });\n\n  test('call onConnect callback after getting an identity', async () => {\n    const onConnectPromise = new Deferred<void>();\n\n    const wsAdapter = new WebsocketTestAdapter();\n    let called = false;\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {\n        called = true;\n        onConnectPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n\n    const tokenMessage = ServerMessage.InitialConnection({\n      identity: anIdentity,\n      token: 'a-token',\n      connectionId: ConnectionId.random(),\n    });\n    wsAdapter.sendToClient(tokenMessage);\n\n    await onConnectPromise.promise;\n\n    expect(called).toBeTruthy();\n  });\n\n  test('disconnects when SubscriptionError has no requestId', async () => {\n    const onDisconnectPromise = new Deferred<void>();\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onDisconnect(() => {\n        onDisconnectPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n    wsAdapter.sendToClient(\n      ServerMessage.SubscriptionError({\n        requestId: undefined,\n        querySetId: { id: 9999 },\n        error: 'test subscription error',\n      })\n    );\n\n    await onDisconnectPromise.promise;\n\n    expect(wsAdapter.closed).toBeTruthy();\n  });\n\n  test('handles SubscriptionError with requestId via subscription error callback', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n    wsAdapter.sendToClient(\n      ServerMessage.InitialConnection({\n        identity: anIdentity,\n        token: 'a-token',\n        connectionId: ConnectionId.random(),\n      })\n    );\n\n    const onErrorPromise = new Deferred<void>();\n    client\n      .subscriptionBuilder()\n      .onError(ctx => {\n        expect(ctx.event!.message).toEqual('test subscription error');\n        onErrorPromise.resolve();\n      })\n      .subscribe('SELECT * FROM user');\n\n    await Promise.resolve();\n    const { requestId, querySetId } = getLastSubscribeMessageInfo(wsAdapter);\n    wsAdapter.sendToClient(\n      ServerMessage.SubscriptionError({\n        requestId,\n        querySetId: { id: querySetId },\n        error: 'test subscription error',\n      })\n    );\n\n    await onErrorPromise.promise;\n    expect(wsAdapter.closed).toBeFalsy();\n  });\n\n  test('fires row callbacks after reducer resolution in ReducerResult', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const onConnectPromise = new Deferred<void>();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {\n        onConnectPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n    wsAdapter.sendToClient(\n      ServerMessage.InitialConnection({\n        identity: anIdentity,\n        token: 'a-token',\n        connectionId: ConnectionId.random(),\n      })\n    );\n    await onConnectPromise.promise;\n\n    let reducerResolved = false;\n\n    const rowCallbackPromise = new Deferred<void>();\n    client.db.player.onInsert(ctx => {\n      expect(reducerResolved).toBeFalsy();\n      expect(ctx.event.tag).toEqual('Reducer');\n      if (ctx.event.tag === 'Reducer') {\n        expect(ctx.event.value.reducer.name).toEqual('create_player');\n        expect(ctx.event.value.reducer.args).toEqual({\n          name: 'A Player',\n          location: { x: 1, y: 2 },\n        });\n      }\n      rowCallbackPromise.resolve();\n    });\n\n    const reducerPromise = client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 1, y: 2 },\n    });\n    reducerPromise.then(() => {\n      reducerResolved = true;\n    });\n    // Hack to get the request sent from the client.\n    await Promise.resolve();\n    const requestId = getLastCallReducerRequestId(wsAdapter);\n    const reducerQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'player',\n      encodePlayer({\n        id: 1,\n        userId: anIdentity,\n        name: 'A Player',\n        location: { x: 1, y: 2 },\n      })\n    );\n    wsAdapter.sendToClient(makeReducerResult(requestId, reducerQuerySetUpdate));\n\n    await rowCallbackPromise.promise;\n    await reducerPromise;\n    expect(reducerResolved).toBeTruthy();\n  });\n\n  test('reducer error rejects and does not fire row callbacks', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const onConnectPromise = new Deferred<void>();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {\n        onConnectPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n    wsAdapter.sendToClient(\n      ServerMessage.InitialConnection({\n        identity: anIdentity,\n        token: 'a-token',\n        connectionId: ConnectionId.random(),\n      })\n    );\n    await onConnectPromise.promise;\n\n    let insertCalled = false;\n    client.db.player.onInsert(() => {\n      insertCalled = true;\n    });\n\n    const reducerPromise = client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 1, y: 2 },\n    });\n\n    await Promise.resolve();\n    const requestId = getLastCallReducerRequestId(wsAdapter);\n    wsAdapter.sendToClient(makeReducerErrorResult(requestId, 'test error'));\n\n    await expect(reducerPromise).rejects.toBeInstanceOf(SenderError);\n    await expect(reducerPromise).rejects.toHaveProperty(\n      'message',\n      'test error'\n    );\n    expect(insertCalled).toBeFalsy();\n  });\n\n  test('reducer internal error rejects with InternalError', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const onConnectPromise = new Deferred<void>();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {\n        onConnectPromise.resolve();\n      })\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n    wsAdapter.sendToClient(\n      ServerMessage.InitialConnection({\n        identity: anIdentity,\n        token: 'a-token',\n        connectionId: ConnectionId.random(),\n      })\n    );\n    await onConnectPromise.promise;\n\n    const reducerPromise = client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 1, y: 2 },\n    });\n\n    await Promise.resolve();\n    const requestId = getLastCallReducerRequestId(wsAdapter);\n    wsAdapter.sendToClient(\n      makeReducerInternalErrorResult(requestId, 'internal test error')\n    );\n\n    await expect(reducerPromise).rejects.toBeInstanceOf(InternalError);\n    await expect(reducerPromise).rejects.toHaveProperty(\n      'message',\n      'internal test error'\n    );\n  });\n\n  /*\n  test('it calls onInsert callback when a record is added with a subscription update and then with a transaction update', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {})\n      .build();\n\n    await Promise.race([\n      client['wsPromise'],\n      new Promise((_, reject) =>\n        setTimeout(() => reject(new Error('Timeout')), 1000)\n      ),\n    ]);\n    wsAdapter.acceptConnection();\n\n    const tokenMessage = ServerMessage.InitialConnection({\n      identity: anIdentity,\n      token: 'a-token',\n      connectionId: ConnectionId.random(),\n    });\n    wsAdapter.sendToClient(tokenMessage);\n\n    const inserts: {\n      reducerEvent:\n        | ReducerEvent<{\n            name: 'create_player';\n            args: Infer<typeof CreatePlayerReducer>;\n          }>\n        | undefined;\n      player: Infer<typeof Player>;\n    }[] = [];\n\n    const insert1Promise = new Deferred<void>();\n    const insert2Promise = new Deferred<void>();\n\n    client.db.player.onInsert((ctx, player) => {\n      console.log('onInsert called');\n      if (ctx.event.tag === 'Reducer') {\n        inserts.push({ reducerEvent: ctx.event.value, player });\n      } else {\n        inserts.push({ reducerEvent: undefined, player });\n      }\n\n      if (!insert1Promise.isResolved) {\n        insert1Promise.resolve();\n      } else {\n        insert2Promise.resolve();\n      }\n    });\n\n    const reducerCallbackLog: {\n      reducerEvent: ReducerEvent<{\n        name: 'create_player';\n        args: Infer<typeof CreatePlayerReducer>;\n      }>;\n      reducerArgs: any[];\n    }[] = [];\n    client.reducers.onCreatePlayer(\n      (ctx, { name, location }: Infer<typeof CreatePlayerReducer>) => {\n        const reducerEvent = ctx.event;\n        reducerCallbackLog.push({\n          reducerEvent,\n          reducerArgs: [name, location],\n        });\n      }\n    );\n\n    const initialQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'player',\n      encodePlayer({\n        id: 1,\n        userId: anIdentity,\n        name: 'drogus',\n        location: { x: 0, y: 0 },\n      })\n    );\n    wsAdapter.sendToClient(\n      ServerMessage.TransactionUpdate({\n        querySets: [initialQuerySetUpdate],\n      })\n    );\n\n    await Promise.race([\n      insert1Promise.promise,\n      new Promise((_, reject) =>\n        setTimeout(() => reject(new Error('Timeout')), 1000)\n      ),\n    ]);\n\n    expect(inserts).toHaveLength(1);\n    expect(inserts[0].player.id).toEqual(1);\n    expect(inserts[0].reducerEvent).toEqual(undefined);\n\n    client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 2, y: 3 },\n    });\n    const requestId = await getLastCallReducerRequestId(wsAdapter);\n    const reducerQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'player',\n      encodePlayer({\n        id: 2,\n        userId: anIdentity,\n        name: 'drogus',\n        location: { x: 2, y: 3 },\n      })\n    );\n    wsAdapter.sendToClient(makeReducerResult(requestId, reducerQuerySetUpdate));\n\n    await Promise.race([\n      insert2Promise.promise,\n      new Promise((_, reject) =>\n        setTimeout(() => reject(new Error('Timeout')), 1000)\n      ),\n    ]);\n\n    expect(inserts).toHaveLength(2);\n    expect(inserts[1].player.id).toEqual(2);\n    expect(inserts[1].reducerEvent).toEqual(undefined);\n\n    expect(reducerCallbackLog).toHaveLength(1);\n    expect(reducerCallbackLog[0].reducerEvent.reducer.name).toEqual(\n      'create_player'\n    );\n    expect(reducerCallbackLog[0].reducerEvent.outcome.tag).toEqual('Ok');\n    expect(reducerCallbackLog[0].reducerEvent.reducer.args).toEqual({\n      name: 'A Player',\n      location: { x: 2, y: 3 },\n    });\n  });\n  */\n\n  /*\n  test('tables should be updated before the reducer callback', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {})\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n\n    const updatePromise = new Deferred<void>();\n\n    expect(client.db.player.count()).toEqual(0n);\n\n    client.reducers.onCreatePlayer(() => {\n      expect(client.db.player.count()).toEqual(1n);\n      updatePromise.resolve();\n    });\n\n    client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 2, y: 3 },\n    });\n    const requestId = await getLastCallReducerRequestId(wsAdapter);\n    const reducerQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'player',\n      new Uint8Array([\n        ...encodePlayer({\n          id: 1,\n          userId: anIdentity,\n          name: 'foo',\n          location: { x: 0, y: 0 },\n        }),\n      ])\n    );\n    wsAdapter.sendToClient(makeReducerResult(requestId, reducerQuerySetUpdate));\n\n    await Promise.all([updatePromise.promise]);\n  });\n  */\n\n  /*\n  test('a reducer callback should be called after the database callbacks', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {})\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n\n    const callbackLog: string[] = [];\n\n    const insertPromise = new Deferred<void>();\n    const updatePromise = new Deferred<void>();\n\n    client.db.player.onInsert(() => {\n      callbackLog.push('Player');\n\n      insertPromise.resolve();\n    });\n\n    client.reducers.onCreatePlayer(() => {\n      callbackLog.push('CreatePlayerReducer');\n\n      updatePromise.resolve();\n    });\n\n    client.reducers.createPlayer({\n      name: 'A Player',\n      location: { x: 2, y: 3 },\n    });\n    const requestId = await getLastCallReducerRequestId(wsAdapter);\n    const reducerQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'player',\n      new Uint8Array([\n        ...encodePlayer({\n          id: 2,\n          userId: anIdentity,\n          name: 'foo',\n          location: { x: 0, y: 0 },\n        }),\n      ])\n    );\n    wsAdapter.sendToClient(makeReducerResult(requestId, reducerQuerySetUpdate));\n\n    await Promise.all([insertPromise.promise, updatePromise.promise]);\n\n    expect(callbackLog).toEqual(['Player', 'CreatePlayerReducer']);\n  });\n  */\n\n  test('it calls onUpdate callback when a record is added with a subscription update and then with a transaction update when the PK is of type Identity', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .onConnect(() => {})\n      .build();\n\n    await client['wsPromise'];\n    wsAdapter.acceptConnection();\n\n    const tokenMessage = ServerMessage.InitialConnection({\n      identity: Identity.fromString(\n        '0000000000000000000000000000000000000000000000000000000000000069'\n      ),\n      token: 'a-token',\n      connectionId: ConnectionId.random(),\n    });\n    wsAdapter.sendToClient(tokenMessage);\n\n    const update1Promise = new Deferred<void>();\n    const initialInsertPromise = new Deferred<void>();\n    const userIdentity = Identity.fromString(\n      '41db74c20cdda916dd2637e5a11b9f31eb1672249aa7172f7e22b4043a6a9008'\n    );\n\n    const initialUser: Infer<typeof User> = {\n      identity: userIdentity,\n      username: 'originalName',\n    };\n    const updatedUser: Infer<typeof User> = {\n      identity: userIdentity,\n      username: 'newName',\n    };\n\n    const updates: {\n      oldUser: Infer<typeof User>;\n      newUser: Infer<typeof User>;\n    }[] = [];\n    client.db.user.onInsert(() => {\n      initialInsertPromise.resolve();\n      console.log('got insert');\n    });\n    client.db.user.onUpdate((_ctx, oldUser, newUser) => {\n      updates.push({\n        oldUser,\n        newUser,\n      });\n      update1Promise.resolve();\n    });\n\n    const initialQuerySetUpdate = makeQuerySetUpdate(\n      0,\n      'user',\n      new Uint8Array([...encodeUser(initialUser)])\n    );\n    wsAdapter.sendToClient(\n      ServerMessage.TransactionUpdate({\n        querySets: [initialQuerySetUpdate],\n      })\n    );\n\n    // await update1Promise.promise;\n    await initialInsertPromise.promise;\n    console.log('First insert is done');\n\n    const transactionUpdate = ServerMessage.TransactionUpdate({\n      querySets: [\n        makeQuerySetUpdate(\n          0,\n          'user',\n          new Uint8Array([...encodeUser(updatedUser)]),\n          new Uint8Array([...encodeUser(initialUser)])\n        ),\n      ],\n    });\n\n    console.log('Sending transaction update');\n    wsAdapter.sendToClient(transactionUpdate);\n\n    await update1Promise.promise;\n\n    expect(updates).toHaveLength(1);\n    expect(updates[0]['oldUser'].username).toEqual(initialUser.username);\n    expect(updates[0]['newUser'].username).toEqual(updatedUser.username);\n\n    console.log('Users: ', [...client.db.user.iter()]);\n    expect(client.db.user.count()).toEqual(1n);\n  });\n\n  test('Filtering works', async () => {\n    const wsAdapter = new WebsocketTestAdapter();\n    const client = DbConnection.builder()\n      .withUri('ws://127.0.0.1:1234')\n      .withDatabaseName('db')\n      .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter) as any)\n      .build();\n    await client['wsPromise'];\n    const user1 = { identity: bobIdentity, username: 'bob' };\n    const user2 = {\n      identity: sallyIdentity,\n      username: 'sally',\n    };\n    const binary = [...encodeUser(user1)].concat([...encodeUser(user2)]);\n    const transactionUpdate = ServerMessage.TransactionUpdate({\n      querySets: [makeQuerySetUpdate(0, 'user', new Uint8Array(binary))],\n    });\n    const gotAllInserts = new Deferred<void>();\n    let inserts = 0;\n    client.db.user.onInsert(() => {\n      inserts++;\n      if (inserts == 2) {\n        gotAllInserts.resolve();\n      }\n    });\n    wsAdapter.sendToClient(transactionUpdate);\n    await gotAllInserts.promise;\n\n    const foundUser = client.db.user.identity.find(sallyIdentity);\n    expect(foundUser).not.toBeUndefined();\n    expect(foundUser!.username).toEqual('sally');\n    expect(client.db.user.count()).toEqual(2n);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/index.test.ts",
    "content": "import { describe, it, expect } from 'vitest';\nimport {\n  AlgebraicType,\n  ConnectionId,\n  Identity,\n  ScheduleAt,\n  toCamelCase,\n  type Infer,\n} from '../src/index';\nimport * as ws from '../src/sdk/client_api';\nimport type { ColumnBuilder } from '../src/server';\nimport { t } from '../src/lib/type_builders';\n\ndescribe('TypeBuilder', () => {\n  it('builds the correct algebraic type for a point', () => {\n    const point = t.object('', {\n      x: t.f64(),\n      y: t.f64(),\n      z: t.f64(),\n    });\n    expect(point.algebraicType).toEqual({\n      tag: 'Product',\n      value: {\n        elements: [\n          { name: 'x', algebraicType: AlgebraicType.F64 },\n          { name: 'y', algebraicType: AlgebraicType.F64 },\n          { name: 'z', algebraicType: AlgebraicType.F64 },\n        ],\n      },\n    });\n  });\n\n  it('builds the correct algebraic type for a sum type', () => {\n    const sumType = t.enum('', {\n      a: t.string(),\n      b: t.number(),\n    });\n    expect(sumType.algebraicType).toEqual({\n      tag: 'Sum',\n      value: {\n        variants: [\n          { name: 'a', algebraicType: AlgebraicType.String },\n          { name: 'b', algebraicType: AlgebraicType.F64 },\n        ],\n      },\n    });\n  });\n\n  it('builds a ColumnBuilder with an index, unique constraint, and primary key', () => {\n    const col = t.i32().index('btree').unique().primaryKey() as ColumnBuilder<\n      any,\n      any,\n      any\n    >;\n    expect(col.typeBuilder.algebraicType).toEqual({\n      tag: 'I32',\n    });\n    expect(col.columnMetadata.isPrimaryKey).toEqual(true);\n    expect(col.columnMetadata.isUnique).toEqual(true);\n    expect(col.columnMetadata.indexType).toEqual('btree');\n    expect(col.columnMetadata.isAutoIncrement).toEqual(undefined);\n    expect(col.columnMetadata.isScheduleAt).toEqual(undefined);\n  });\n\n  it('builds ColumnBuilders with the correct metadata', () => {\n    const indexCol = t.i32().index('btree') as ColumnBuilder<any, any, any>;\n    const uniqueCol = t.i32().unique() as ColumnBuilder<any, any, any>;\n    const primaryKeyCol = t.i32().primaryKey() as ColumnBuilder<any, any, any>;\n    const autoIncCol = t.i32().autoInc() as ColumnBuilder<any, any, any>;\n\n    expect(indexCol.typeBuilder.algebraicType).toEqual({\n      tag: 'I32',\n    });\n    expect(indexCol.columnMetadata.isPrimaryKey).toEqual(undefined);\n    expect(indexCol.columnMetadata.isUnique).toEqual(undefined);\n    expect(indexCol.columnMetadata.indexType).toEqual('btree');\n    expect(indexCol.columnMetadata.isAutoIncrement).toEqual(undefined);\n    expect(indexCol.columnMetadata.isScheduleAt).toEqual(undefined);\n\n    expect(uniqueCol.typeBuilder.algebraicType).toEqual({\n      tag: 'I32',\n    });\n    expect(uniqueCol.columnMetadata.isPrimaryKey).toEqual(undefined);\n    expect(uniqueCol.columnMetadata.isUnique).toEqual(true);\n    expect(uniqueCol.columnMetadata.indexType).toEqual(undefined);\n    expect(uniqueCol.columnMetadata.isAutoIncrement).toEqual(undefined);\n    expect(uniqueCol.columnMetadata.isScheduleAt).toEqual(undefined);\n\n    expect(primaryKeyCol.typeBuilder.algebraicType).toEqual({\n      tag: 'I32',\n    });\n    expect(primaryKeyCol.columnMetadata.isPrimaryKey).toEqual(true);\n    expect(primaryKeyCol.columnMetadata.isUnique).toEqual(undefined);\n    expect(primaryKeyCol.columnMetadata.indexType).toEqual(undefined);\n    expect(primaryKeyCol.columnMetadata.isAutoIncrement).toEqual(undefined);\n    expect(primaryKeyCol.columnMetadata.isScheduleAt).toEqual(undefined);\n\n    expect(autoIncCol.typeBuilder.algebraicType).toEqual({\n      tag: 'I32',\n    });\n    expect(autoIncCol.columnMetadata.isPrimaryKey).toEqual(undefined);\n    expect(autoIncCol.columnMetadata.isUnique).toEqual(undefined);\n    expect(autoIncCol.columnMetadata.indexType).toEqual(undefined);\n    expect(autoIncCol.columnMetadata.isAutoIncrement).toEqual(true);\n    expect(autoIncCol.columnMetadata.isScheduleAt).toEqual(undefined);\n  });\n\n  it('builds a ScheduleAt column with the correct type and metadata', () => {\n    const col = t.scheduleAt();\n    expect(col.algebraicType).toEqual({\n      tag: 'Sum',\n      value: {\n        variants: [\n          {\n            name: 'Interval',\n            algebraicType: {\n              tag: 'Product',\n              value: {\n                elements: [\n                  {\n                    name: '__time_duration_micros__',\n                    algebraicType: AlgebraicType.I64,\n                  },\n                ],\n              },\n            },\n          },\n          {\n            name: 'Time',\n            algebraicType: {\n              tag: 'Product',\n              value: {\n                elements: [\n                  {\n                    name: '__timestamp_micros_since_unix_epoch__',\n                    algebraicType: AlgebraicType.I64,\n                  },\n                ],\n              },\n            },\n          },\n        ],\n      },\n    });\n    expect(ScheduleAt.isScheduleAt(col.algebraicType)).toEqual(true);\n  });\n});\n\ndescribe('Identity', () => {\n  it('imports something from the spacetimedb sdk', () => {\n    const _msg: Infer<typeof ws.InitialConnection> = {\n      identity: Identity.fromString(\n        '0xc200000000000000000000000000000000000000000000000000000000000000'\n      ),\n      token: 'some-token',\n      connectionId: ConnectionId.fromString(\n        '0x00000000000000000000000000000000'\n      ),\n    };\n  });\n});\n\ndescribe(toCamelCase, () => {\n  it('converts PascalCase to camelCase', () => {\n    expect(toCamelCase('FooBar')).toEqual('fooBar');\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/logger.test.ts",
    "content": "import { beforeEach, describe, expect, test, vi } from 'vitest';\nimport { ConnectionId, Identity } from '../src';\nimport {\n  getGlobalLogLevel,\n  setGlobalLogLevel,\n  stringify,\n  stdbLogger,\n} from '../src/sdk/logger';\n\ndescribe('logger', () => {\n  beforeEach(() => {\n    setGlobalLogLevel('debug');\n    vi.restoreAllMocks();\n  });\n\n  test('setGlobalLogLevel controls emitted logs', () => {\n    const spy = vi.spyOn(console, 'log').mockImplementation(() => {});\n\n    setGlobalLogLevel('warn');\n    stdbLogger('info', 'info message');\n    stdbLogger('warn', 'warn message');\n    stdbLogger('error', 'error message');\n\n    expect(spy).toHaveBeenCalledTimes(2);\n  });\n\n  test('lazy log messages are only evaluated when emitted', () => {\n    const spy = vi.spyOn(console, 'log').mockImplementation(() => {});\n    const lazyMessage = vi.fn(() => 'computed');\n\n    setGlobalLogLevel('error');\n    stdbLogger('debug', lazyMessage);\n    expect(lazyMessage).not.toHaveBeenCalled();\n\n    stdbLogger('error', lazyMessage);\n    expect(lazyMessage).toHaveBeenCalledTimes(1);\n    expect(spy).toHaveBeenCalledTimes(1);\n  });\n\n  test('trace logs are only emitted at trace level', () => {\n    const spy = vi.spyOn(console, 'log').mockImplementation(() => {});\n\n    setGlobalLogLevel('debug');\n    stdbLogger('trace', 'trace message');\n    expect(spy).not.toHaveBeenCalled();\n\n    setGlobalLogLevel('trace');\n    stdbLogger('trace', 'trace message');\n    expect(spy).toHaveBeenCalledTimes(1);\n  });\n\n  test('getGlobalLogLevel returns the current level', () => {\n    setGlobalLogLevel('info');\n    expect(getGlobalLogLevel()).toBe('info');\n  });\n\n  test('stringify renders short Uint8Array as full hex', () => {\n    const payload = new Uint8Array([0, 1, 15, 16, 255]);\n    expect(stringify({ payload })).toBe('{\"payload\":\"0x00010f10ff\"}');\n  });\n\n  test('stringify renders long Uint8Array as summary', () => {\n    const payload = Uint8Array.from(Array.from({ length: 30 }, (_, i) => i));\n    expect(stringify({ payload })).toBe(\n      '{\"payload\":\"Uint8Array(len=30, head=0x00010203040506070809)\"}'\n    );\n  });\n\n  test('stringify renders identity wrappers as hex string', () => {\n    const identity = Identity.fromString(\n      'c2005a97608f921d92a0f68cb32ecbf10829b5221993a6fba2f62058cc9b3233'\n    );\n    expect(stringify({ identity })).toBe(\n      '{\"identity\":\"c2005a97608f921d92a0f68cb32ecbf10829b5221993a6fba2f62058cc9b3233\"}'\n    );\n  });\n\n  test('stringify renders connection id wrappers as hex string', () => {\n    const connectionId = ConnectionId.fromString(\n      'e4df11f8f7ad5f05f4e90e401a3890ca'\n    );\n    expect(stringify({ connectionId })).toBe(\n      '{\"connectionId\":\"e4df11f8f7ad5f05f4e90e401a3890ca\"}'\n    );\n  });\n\n  test('stringify redacts token-like keys', () => {\n    expect(\n      stringify({\n        token: 'secret-token',\n        authToken: 'auth-secret',\n        authorization: 'Bearer abc',\n        accessToken: 'access-secret',\n        refreshToken: 'refresh-secret',\n      })\n    ).toBe(\n      '{\"accessToken\":\"[REDACTED]\",\"authToken\":\"[REDACTED]\",\"authorization\":\"[REDACTED]\",\"refreshToken\":\"[REDACTED]\",\"token\":\"[REDACTED]\"}'\n    );\n  });\n\n  test('stringify redacts nested token fields', () => {\n    expect(\n      stringify({\n        tag: 'InitialConnection',\n        value: { token: 'jwt', nested: { authToken: 'inner-secret' } },\n      })\n    ).toBe(\n      '{\"tag\":\"InitialConnection\",\"value\":{\"nested\":{\"authToken\":\"[REDACTED]\"},\"token\":\"[REDACTED]\"}}'\n    );\n  });\n\n  test('stringify truncates long normal arrays', () => {\n    const arr = Array.from({ length: 30 }, (_, i) => i);\n    expect(stringify({ arr })).toBe(\n      '{\"arr\":\"Array(len=30, head=[0,1,2,3,4,5,6,7,8,9])\"}'\n    );\n  });\n\n  test('stringify preserves short normal arrays', () => {\n    const arr = [1, 2, 3];\n    expect(stringify({ arr })).toBe('{\"arr\":[1,2,3]}');\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/query.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { Identity } from '../src/lib/identity';\nimport {\n  makeQueryBuilder,\n  and,\n  or,\n  not,\n  toSql,\n  toComparableValue,\n} from '../src/server/query';\nimport { ModuleContext, tablesToSchema } from '../src/lib/schema';\nimport { table } from '../src/lib/table';\nimport { t } from '../src/lib/type_builders';\nimport { Timestamp } from '../src';\n\nconst personTable = table(\n  {\n    name: 'person',\n\n    indexes: [\n      {\n        accessor: 'id_name_idx',\n        name: 'id_name_idx',\n        algorithm: 'btree',\n        columns: ['id', 'name'] as const,\n      },\n    ] as const,\n  },\n  {\n    id: t.identity(),\n    name: t.string(),\n    age: t.u32(),\n    active: t.bool(),\n  }\n);\n\nconst ordersTable = table(\n  {\n    name: 'orders',\n    indexes: [\n      {\n        accessor: 'orders_person_id_idx',\n        name: 'orders_person_id_idx',\n        algorithm: 'btree',\n        columns: ['person_id'],\n      },\n    ] as const,\n  },\n\n  {\n    order_id: t.identity(),\n    person_id: t.identity(),\n    item_name: t.string(),\n  }\n);\n\nconst renamedColumnsTable = table(\n  {\n    name: 'renamed_columns',\n  },\n  {\n    displayName: t.string().name('display_name'),\n    ageYears: t.u32().name('age_years'),\n  }\n);\n\nconst schemaDef = tablesToSchema(new ModuleContext(), {\n  person: personTable,\n  orders: ordersTable,\n  renamedColumns: renamedColumnsTable,\n});\n\ndescribe('Timestamp thing', () => {\n  it('Compares them', () => {\n    const d1 = new Date('2024-01-01T00:00:00Z');\n    const d2 = new Date('2024-01-02T00:00:00Z');\n    const t1 = Timestamp.fromDate(d1);\n    const t2 = Timestamp.fromDate(d2);\n\n    expect(toComparableValue(t1) <= toComparableValue(t2)).toBe(true);\n    expect(toComparableValue(t1) >= toComparableValue(t2)).toBe(false);\n  });\n});\n\ndescribe('TableScan.toSql', () => {\n  it('renders a full-table scan when no filters are applied', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.build());\n\n    expect(sql).toBe('SELECT * FROM \"person\"');\n  });\n\n  it('renders a WHERE clause for simple equality filters', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => row.name.eq(\"O'Brian\")).build());\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE \"person\".\"name\" = 'O''Brian'`\n    );\n  });\n\n  it('renders numeric literals and column references', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => row.age.eq(42)).build());\n\n    expect(sql).toBe(`SELECT * FROM \"person\" WHERE \"person\".\"age\" = 42`);\n  });\n\n  it('renders AND clauses across multiple predicates', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person.where(row => and(row.name.eq('Alice'), row.age.eq(30))).build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE (\"person\".\"name\" = 'Alice') AND (\"person\".\"age\" = 30)`\n    );\n  });\n\n  it('renders NOT clauses around subpredicates', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => not(row.name.eq('Bob'))).build());\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE NOT (\"person\".\"name\" = 'Bob')`\n    );\n  });\n\n  it('accumulates multiple filters with AND logic', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => row.name.eq('Eve'))\n        .where(row => row.age.eq(25))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE (\"person\".\"name\" = 'Eve') AND (\"person\".\"age\" = 25)`\n    );\n  });\n\n  it('renders OR clauses across multiple predicates', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => or(row.name.eq('Carol'), row.name.eq('Dave')))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE (\"person\".\"name\" = 'Carol') OR (\"person\".\"name\" = 'Dave')`\n    );\n  });\n\n  it('accepts boolean columns directly as where predicates', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => row.active).build());\n\n    expect(sql).toBe(`SELECT * FROM \"person\" WHERE \"person\".\"active\" = TRUE`);\n  });\n\n  it('renders Identity literals using their hex form', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const identity = new Identity(\n      '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'\n    );\n    const sql = toSql(qb.person.where(row => row.id.eq(identity)).build());\n\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE \"person\".\"id\" = 0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`\n    );\n  });\n\n  it('renders semijoin queries without additional filters', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .rightSemijoin(qb.orders, (person, order) =>\n          order.person_id.eq(person.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"orders\".\"person_id\" = \"person\".\"id\"`\n    );\n  });\n\n  it('renders semijoin queries alongside existing predicates', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => row.age.eq(42))\n        .rightSemijoin(qb.orders, (person, order) =>\n          order.person_id.eq(person.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"orders\".\"person_id\" = \"person\".\"id\" WHERE \"person\".\"age\" = 42`\n    );\n  });\n\n  it('escapes literals when rendering semijoin filters', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => row.name.eq(\"O'Brian\"))\n        .rightSemijoin(qb.orders, (person, order) =>\n          order.person_id.eq(person.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"orders\".\"person_id\" = \"person\".\"id\" WHERE \"person\".\"name\" = 'O''Brian'`\n    );\n  });\n\n  it('renders compound AND filters for semijoin queries', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => and(row.name.eq('Alice'), row.age.eq(30)))\n        .rightSemijoin(qb.orders, (person, order) =>\n          order.person_id.eq(person.id)\n        )\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"orders\".\"person_id\" = \"person\".\"id\" WHERE (\"person\".\"name\" = 'Alice') AND (\"person\".\"age\" = 30)`\n    );\n  });\n\n  it('basic where', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.eq('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" = 'Gadget'`\n    );\n  });\n\n  it('basic where ne', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.ne('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" <> 'Gadget'`\n    );\n  });\n\n  it('basic where lt', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.lt('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" < 'Gadget'`\n    );\n  });\n\n  it('basic where lte', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.lte('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" <= 'Gadget'`\n    );\n  });\n\n  it('basic where gt', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.gt('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" > 'Gadget'`\n    );\n  });\n\n  it('basic where gte', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.orders.where(o => o.item_name.gte('Gadget')).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"orders\" WHERE \"orders\".\"item_name\" >= 'Gadget'`\n    );\n  });\n\n  it('basic semijoin', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person.rightSemijoin(qb.orders, (p, o) => p.id.eq(o.person_id)).build()\n    );\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"person\".\"id\" = \"orders\".\"person_id\"`\n    );\n  });\n\n  it('basic left semijoin', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person.leftSemijoin(qb.orders, (p, o) => p.id.eq(o.person_id)).build()\n    );\n    expect(sql).toBe(\n      `SELECT \"person\".* FROM \"orders\" JOIN \"person\" ON \"person\".\"id\" = \"orders\".\"person_id\"`\n    );\n  });\n\n  it('method-style chaining with .and()', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person.where(row => row.age.gt(20).and(row.age.lt(30))).build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE (\"person\".\"age\" > 20) AND (\"person\".\"age\" < 30)`\n    );\n  });\n\n  it('method-style chaining with .or()', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => row.name.eq('Carol').or(row.name.eq('Dave')))\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE (\"person\".\"name\" = 'Carol') OR (\"person\".\"name\" = 'Dave')`\n    );\n  });\n\n  it('method-style chaining with .not()', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => row.name.eq('Bob').not()).build());\n    expect(sql).toBe(\n      `SELECT * FROM \"person\" WHERE NOT (\"person\".\"name\" = 'Bob')`\n    );\n  });\n\n  it('semijoin with filters on both sides', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.person\n        .where(row => row.age.eq(42))\n        .rightSemijoin(qb.orders, (p, o) => p.id.eq(o.person_id))\n        .where(row => row.item_name.eq('Gadget'))\n        .build()\n    );\n    expect(sql).toBe(\n      `SELECT \"orders\".* FROM \"person\" JOIN \"orders\" ON \"person\".\"id\" = \"orders\".\"person_id\" WHERE (\"person\".\"age\" = 42) AND (\"orders\".\"item_name\" = 'Gadget')`\n    );\n  });\n\n  it('passes builder directly to toSql() without .build()', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person.where(row => row.age.eq(42)));\n    expect(sql).toBe(`SELECT * FROM \"person\" WHERE \"person\".\"age\" = 42`);\n  });\n\n  it('passes table ref directly to toSql() without .build()', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(qb.person);\n    expect(sql).toBe('SELECT * FROM \"person\"');\n  });\n\n  it('uses DB column names for accessors with explicit DB names', () => {\n    const qb = makeQueryBuilder(schemaDef);\n    const sql = toSql(\n      qb.renamedColumns\n        .where(row => row.displayName.eq('Alice').and(row.ageYears.gt(30)))\n        .build()\n    );\n\n    expect(sql).toBe(\n      `SELECT * FROM \"renamed_columns\" WHERE (\"renamed_columns\".\"display_name\" = 'Alice') AND (\"renamed_columns\".\"age_years\" > 30)`\n    );\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/query_error_message.test.ts",
    "content": "import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as ts from 'typescript';\nimport { describe, expect, it } from 'vitest';\n\nconst bindingsRoot = path.resolve(\n  path.dirname(fileURLToPath(import.meta.url)),\n  '..'\n);\n\nfunction runTypecheck(semijoinPredicateExpr: string) {\n  const tmpDir = mkdtempSync(path.join(tmpdir(), 'stdb-query-diag-'));\n  const reproPath = path.join(tmpDir, 'repro.ts');\n\n  const imports = {\n    query: path.join(bindingsRoot, 'src/lib/query.ts'),\n    moduleBindings: path.join(\n      bindingsRoot,\n      'test-app/src/module_bindings/index.ts'\n    ),\n    sys: path.join(bindingsRoot, 'src/server/sys.d.ts'),\n  };\n\n  const source = `\nimport { and } from ${JSON.stringify(imports.query)};\nimport { tables } from ${JSON.stringify(imports.moduleBindings)};\n\ntables.player\n  .leftSemijoin(tables.unindexed_player, (l, r) => ${semijoinPredicateExpr})\n  .build();\n`;\n\n  writeFileSync(reproPath, source);\n\n  try {\n    const options: ts.CompilerOptions = {\n      target: ts.ScriptTarget.ESNext,\n      module: ts.ModuleKind.ESNext,\n      strict: true,\n      noEmit: true,\n      skipLibCheck: true,\n      forceConsistentCasingInFileNames: true,\n      allowImportingTsExtensions: true,\n      noImplicitAny: true,\n      moduleResolution: ts.ModuleResolutionKind.Bundler,\n      useDefineForClassFields: true,\n      verbatimModuleSyntax: true,\n      isolatedModules: true,\n    };\n\n    const host = ts.createCompilerHost(options);\n    const program = ts.createProgram([reproPath, imports.sys], options, host);\n    const diagnostics = ts.getPreEmitDiagnostics(program);\n    const output = diagnostics\n      .map(d => ts.flattenDiagnosticMessageText(d.messageText, '\\n'))\n      .join('\\n');\n\n    return {\n      status: diagnostics.length === 0 ? 0 : 1,\n      output,\n    };\n  } finally {\n    rmSync(tmpDir, { recursive: true, force: true });\n  }\n}\n\ndescribe('query builder diagnostics', () => {\n  const messageStart =\n    'Cannot combine predicates from different table scopes with and/or.';\n  const messageHint = 'move extra predicates to .where(...)';\n\n  it('reports a clear message for free-floating and(...) in semijoin predicates', () => {\n    const { status, output } = runTypecheck('and(l.id.eq(r.id), r.id.eq(5))');\n    expect(status).not.toBe(0);\n    expect(output).toContain(messageStart);\n    expect(output).toContain(messageHint);\n  });\n\n  it('reports a clear message for method-style .and(...) in semijoin predicates', () => {\n    const { status, output } = runTypecheck('l.id.eq(r.id).and(r.id.eq(5))');\n    expect(status).not.toBe(0);\n    expect(output).toContain(messageStart);\n    expect(output).toContain(messageHint);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/schema_index_resolution.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { ModuleContext, tablesToSchema } from '../src/lib/schema';\nimport { table } from '../src/lib/table';\nimport { t } from '../src/lib/type_builders';\n\ndescribe('schema index resolution', () => {\n  it('keeps declarative index options separate from resolved runtime indexes', () => {\n    /**\n     * Why this test exists:\n     * We intentionally model two different index representations:\n     * 1) `indexes`: declarative table-level options as authored by the user\n     * 2) `resolvedIndexes`: runtime-ready index metadata derived from RawTableDef\n     *\n     * The rewrite is correct only if:\n     * - `indexes` still preserves the original declaration shape for type inference.\n     * - `resolvedIndexes` includes every runtime index (field-level + table-level).\n     */\n    const player = table(\n      {\n        name: 'player',\n        indexes: [\n          {\n            accessor: 'byTeamAndLevel',\n            algorithm: 'btree',\n            columns: ['team', 'level'] as const,\n          },\n        ] as const,\n      },\n      {\n        // Field-level index declared on metadata.\n        displayName: t.string().index('hash'),\n        team: t.string(),\n        level: t.u32(),\n      }\n    );\n\n    const schemaDef = tablesToSchema(new ModuleContext(), { player });\n    const playerDef = schemaDef.tables.player;\n\n    /**\n     * `indexes` should remain the declarative shape from `table({ indexes: ... })`.\n     * This drives type-level behavior, so it must not be replaced with resolved\n     * runtime metadata.\n     */\n    expect(playerDef.indexes).toEqual([\n      {\n        accessor: 'byTeamAndLevel',\n        algorithm: 'btree',\n        columns: ['team', 'level'],\n      },\n    ]);\n\n    /**\n     * `resolvedIndexes` should contain:\n     * - the field-level index derived from `displayName.index('hash')`, and\n     * - the explicit table-level index `byTeamAndLevel`.\n     *\n     * Runtime consumers (e.g. TableCache) use this field because it is the\n     * normalized shape with resolved algorithms, names, and column lists.\n     */\n    expect(playerDef.resolvedIndexes).toEqual(\n      expect.arrayContaining([\n        {\n          name: 'displayName',\n          unique: false,\n          algorithm: 'hash',\n          columns: ['displayName'],\n        },\n        {\n          name: 'byTeamAndLevel',\n          unique: false,\n          algorithm: 'btree',\n          columns: ['team', 'level'],\n        },\n      ])\n    );\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/serde.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport {\n  AlgebraicType,\n  BinaryReader,\n  BinaryWriter,\n  ConnectionId,\n  Identity,\n  ScheduleAt,\n  Uuid,\n} from '../src';\n\ndescribe('it correctly serializes and deserializes algebraic values', () => {\n  test('when it serializes and deserializes with a product type', () => {\n    const value = { foo: 'foobar' };\n    const algebraic_type = AlgebraicType.Product({\n      elements: [{ name: 'foo', algebraicType: AlgebraicType.String }],\n    });\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n\n    expect(buffer).toEqual(\n      new Uint8Array([6, 0, 0, 0, 102, 111, 111, 98, 97, 114])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes with a sum type', () => {\n    const value = { tag: 'bar', value: 5 };\n    const algebraic_type = AlgebraicType.Sum({\n      variants: [\n        { name: 'bar', algebraicType: AlgebraicType.U32 },\n        { name: 'foo', algebraicType: AlgebraicType.String },\n      ],\n    });\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n\n    expect(buffer).toEqual(new Uint8Array([0, 5, 0, 0, 0]));\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes an Identity type', () => {\n    const value = {\n      __identity__: BigInt(1234567890123456789012345678901234567890n),\n    };\n\n    const algebraic_type = Identity.getAlgebraicType();\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n\n    // Little endian encoding of the number 1234567890123456789012345678901234567890n\n    expect(buffer).toEqual(\n      new Uint8Array([\n        210, 10, 63, 206, 150, 95, 188, 172, 184, 243, 219, 192, 117, 32, 201,\n        160, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n      ])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes an Interval ScheduleAt', () => {\n    const value = {\n      tag: 'Interval',\n      value: {\n        __time_duration_micros__: BigInt(1234567890123456789n),\n      },\n    };\n\n    const algebraic_type = ScheduleAt.getAlgebraicType();\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n    expect(buffer).toEqual(\n      new Uint8Array([0, 21, 129, 233, 125, 244, 16, 34, 17])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes a Time ScheduleAt', () => {\n    const value = {\n      tag: 'Time',\n      value: {\n        __timestamp_micros_since_unix_epoch__: BigInt(1234567890123456789n),\n      },\n    };\n\n    const algebraic_type = ScheduleAt.getAlgebraicType();\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n    expect(buffer).toEqual(\n      new Uint8Array([1, 21, 129, 233, 125, 244, 16, 34, 17])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes a ConnectionId ', () => {\n    const U128_MAX = (1n << 128n) - 1n;\n    const value = {\n      __connection_id__: U128_MAX,\n    };\n\n    const algebraic_type = ConnectionId.getAlgebraicType();\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n    expect(buffer).toEqual(\n      new Uint8Array([\n        255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n        255, 255,\n      ])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    console.log(deserializedValue);\n\n    expect(deserializedValue).toEqual(value);\n  });\n\n  test('when it serializes and deserializes an Uuid ', () => {\n    const value = {\n      __uuid__: BigInt('0x1234567890abcdef1234567890abcdef'),\n    };\n\n    const algebraic_type = Uuid.getAlgebraicType();\n    const binaryWriter = new BinaryWriter(1024);\n    AlgebraicType.serializeValue(binaryWriter, algebraic_type, value);\n\n    const buffer = binaryWriter.getBuffer();\n    expect(buffer).toEqual(\n      new Uint8Array([\n        239, 205, 171, 144, 120, 86, 52, 18, 239, 205, 171, 144, 120, 86, 52,\n        18,\n      ])\n    );\n\n    const deserializedValue = AlgebraicType.deserializeValue(\n      new BinaryReader(buffer),\n      algebraic_type\n    );\n\n    expect(deserializedValue).toEqual(value);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/table_cache.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport { AlgebraicType, Identity, type Infer } from '../src';\nimport { type Operation, TableCacheImpl } from '../src/sdk/table_cache';\nimport { tables } from '../test-app/src/module_bindings/index.ts';\nimport { Player } from '../test-app/src/module_bindings/types';\nimport UnindexedPlayer from '../test-app/src/module_bindings/unindexed_player_table.ts';\n\ninterface ApplyOperations {\n  ops: Operation[];\n  ctx: any;\n}\n\ninterface CallbackEvent {\n  type: 'insert' | 'delete' | 'update';\n  ctx: any;\n  row: any;\n  oldRow?: any; // Only there for updates.\n}\n\nfunction insertEvent(row: any, ctx: any = {}): CallbackEvent {\n  return {\n    type: 'insert',\n    ctx,\n    row,\n  };\n}\n\nfunction updateEvent(oldRow: any, row: any, ctx: any = {}): CallbackEvent {\n  return {\n    type: 'update',\n    ctx,\n    row,\n    oldRow,\n  };\n}\n\nfunction deleteEvent(row: any, ctx: any = {}): CallbackEvent {\n  return {\n    type: 'delete',\n    ctx,\n    row,\n  };\n}\n\ninterface AssertionInput {\n  // The state of the table cache.\n  tableCache: TableCacheImpl<any, string>;\n  // The sequence of callbacks that were fired from the last applyOperations.\n  callbackHistory: CallbackEvent[];\n}\n\ntype Assertion = (arg0: AssertionInput) => void;\n\ninterface TestStep {\n  // The operations to apply.\n  ops: ApplyOperations;\n  // The assertions to make after applying the operations.\n  assertions: Assertion[];\n}\n\nfunction runTest(\n  tableCache: TableCacheImpl<any, string>,\n  testSteps: TestStep[]\n) {\n  const callbackHistory: CallbackEvent[] = [];\n  tableCache.onInsert((ctx, row) => {\n    callbackHistory.push({\n      type: 'insert',\n      ctx,\n      row,\n    });\n  });\n  tableCache.onDelete((ctx, row) => {\n    callbackHistory.push({\n      type: 'delete',\n      ctx,\n      row,\n    });\n  });\n  tableCache.onUpdate((ctx, oldRow, row) => {\n    callbackHistory.push({\n      type: 'update',\n      ctx,\n      row,\n      oldRow,\n    });\n  });\n\n  for (const step of testSteps) {\n    const { ops: applyOperations, assertions } = step;\n    const { ops, ctx } = applyOperations;\n    const callbacks = tableCache.applyOperations(ops, ctx);\n    callbacks.forEach(cb => cb.cb());\n    for (const assertion of assertions) {\n      assertion({ tableCache, callbackHistory });\n    }\n    // Clear the callback history for the next step.\n    callbackHistory.length = 0;\n  }\n}\n\ndescribe('TableCache', () => {\n  describe('Unindexed player table', () => {\n    const newTable = () => new TableCacheImpl(tables.unindexed_player.tableDef);\n    const mkOperation = (\n      type: 'insert' | 'delete',\n      row: Infer<typeof UnindexedPlayer>\n    ) => {\n      const rowId = AlgebraicType.intoMapKey(\n        { tag: 'Product', value: tables.unindexed_player.rowType },\n        row\n      );\n      return {\n        type,\n        rowId,\n        row,\n      };\n    };\n\n    test('Insert one', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Inserting one twice only triggers one event', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player), mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert dupe is a noop', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(0);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert once and delete', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('delete');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert twice and delete', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const mkPlayer = () => ({\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      });\n      const player = {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [\n            mkOperation('insert', mkPlayer()),\n            mkOperation('insert', player),\n          ],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            // We still have one reference left, so it isn't actually deleted.\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(0);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            // Now it is actually deleted.\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('delete');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      // Now we are going to insert again, so we can delete both refs at once.\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory).toEqual([insertEvent(player)]);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory).toEqual([]);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player), mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory).toEqual([deleteEvent(mkPlayer())]);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert one', () => {\n      const tableCache = newTable();\n      const op = mkOperation('insert', {\n        id: 1,\n        ownerId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      });\n      let rowsInserted = 0;\n      const callbacks = tableCache.applyOperations([op], {} as any);\n      tableCache.onInsert((ctx, row) => {\n        rowsInserted++;\n        expect(row).toEqual(op.row);\n      });\n      expect(callbacks.length).toEqual(1);\n      expect(tableCache.count()).toEqual(1n);\n      callbacks.forEach(cb => {\n        cb.cb();\n      });\n      expect(rowsInserted).toEqual(1);\n    });\n  });\n  describe('Indexed player table', () => {\n    const newTable = () => new TableCacheImpl(tables.player.tableDef);\n    const mkOperation = (\n      type: 'insert' | 'delete',\n      row: Infer<typeof Player>\n    ) => {\n      const rowId = AlgebraicType.intoMapKey(\n        Player.elements['id'].algebraicType,\n        row['id']\n      );\n      return {\n        type,\n        rowId,\n        row,\n      };\n    };\n\n    test('Insert one', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Inserting one twice only triggers one event', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player), mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert dupe is a noop', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter()).length).toEqual(1);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(0);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert once and delete', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const player = {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('delete');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Update smoke test', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const mkPlayer = (name: string) => ({\n        id: 1,\n        userId: Identity.zero(),\n        name: name,\n        location: {\n          x: 1,\n          y: 2,\n        },\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', mkPlayer('jeff'))],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(mkPlayer('jeff'));\n            expect(callbackHistory).toEqual([insertEvent(mkPlayer('jeff'))]);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [\n            mkOperation('delete', mkPlayer('jeff')),\n            mkOperation('insert', mkPlayer('jeffv2')),\n          ],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(\n              mkPlayer('jeffv2')\n            );\n            expect(callbackHistory).toEqual([\n              updateEvent(mkPlayer('jeff'), mkPlayer('jeffv2')),\n            ]);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert twice and delete', () => {\n      const tableCache = newTable();\n      const steps: TestStep[] = [];\n      const mkPlayer = () => ({\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      });\n      const player = {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      };\n      steps.push({\n        ops: {\n          ops: [\n            mkOperation('insert', mkPlayer()),\n            mkOperation('insert', player),\n          ],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('insert');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            // We still have one reference left, so it isn't actually deleted.\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory.length).toEqual(0);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            // Now it is actually deleted.\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory.length).toEqual(1);\n            expect(callbackHistory[0].type).toEqual('delete');\n            expect(callbackHistory[0].row).toEqual(player);\n          },\n        ],\n      });\n      // Now we are going to insert again, so we can delete both refs at once.\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory).toEqual([insertEvent(player)]);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('insert', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(1n);\n            expect(Array.from(tableCache.iter())[0]).toEqual(player);\n            expect(callbackHistory).toEqual([]);\n          },\n        ],\n      });\n      steps.push({\n        ops: {\n          ops: [mkOperation('delete', player), mkOperation('delete', player)],\n          ctx: {} as any,\n        },\n        assertions: [\n          ({ tableCache, callbackHistory }) => {\n            expect(tableCache.count()).toEqual(0n);\n            expect(callbackHistory).toEqual([deleteEvent(mkPlayer())]);\n          },\n        ],\n      });\n      runTest(tableCache, steps);\n    });\n\n    test('Insert one', () => {\n      const tableCache = newTable();\n      const op = mkOperation('insert', {\n        id: 1,\n        userId: Identity.zero(),\n        name: 'Player 1',\n        location: {\n          x: 1,\n          y: 2,\n        },\n      });\n      let rowsInserted = 0;\n      const callbacks = tableCache.applyOperations([op], {} as any);\n      tableCache.onInsert((ctx, row) => {\n        rowsInserted++;\n        expect(row).toEqual(op.row);\n      });\n      expect(callbacks.length).toEqual(1);\n      expect(tableCache.count()).toEqual(1n);\n      callbacks.forEach(cb => {\n        cb.cb();\n      });\n      expect(rowsInserted).toEqual(1);\n    });\n  });\n\n  test('should be empty on creation', () => {\n    const tableCache = new TableCacheImpl(tables.player.tableDef);\n    expect(tableCache.count()).toEqual(0n);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/table_cache_resolved_indexes.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { ModuleContext, tablesToSchema } from '../src/lib/schema';\nimport { table } from '../src/lib/table';\nimport { TableCacheImpl } from '../src/sdk/table_cache';\nimport { t } from '../src/lib/type_builders';\n\ndescribe('table cache resolved indexes', () => {\n  it('builds index accessors from resolvedIndexes (field-level + table-level)', () => {\n    /**\n     * Why this test exists:\n     * `TableCacheImpl` previously consumed `table.indexes` and re-cast it into a\n     * runtime shape. After the refactor, runtime index construction must come\n     * from `table.resolvedIndexes` instead.\n     *\n     * This test validates the observable contract:\n     * - field-level indexes still materialize as cache accessors\n     * - explicit table-level indexes still materialize as cache accessors\n     * - both accessors execute correctly against cached rows\n     */\n    const player = table(\n      {\n        name: 'player',\n        indexes: [\n          {\n            accessor: 'byTeamAndLevel',\n            algorithm: 'btree',\n            columns: ['team', 'level'] as const,\n          },\n        ] as const,\n      },\n      {\n        // Field-level index.\n        email: t.string().index('btree'),\n        team: t.string(),\n        level: t.u32(),\n      }\n    );\n\n    const schemaDef = tablesToSchema(new ModuleContext(), { player });\n    const playerDef = schemaDef.tables.player;\n    const tableCache = new TableCacheImpl<any, string>(playerDef as any);\n\n    const rows = [\n      { email: 'a@example.com', team: 'red', level: 1 },\n      { email: 'b@example.com', team: 'blue', level: 2 },\n      { email: 'c@example.com', team: 'red', level: 3 },\n    ];\n\n    const callbacks = tableCache.applyOperations(\n      rows.map(row => ({\n        type: 'insert' as const,\n        // This table has no primary key, so any stable row id is acceptable for this test.\n        rowId: row.email,\n        row,\n      })),\n      {}\n    );\n    callbacks.forEach(cb => cb.cb());\n\n    const emailIndex = (tableCache as any).email;\n    const byTeamAndLevel = (tableCache as any).byTeamAndLevel;\n\n    // The field-level accessor must exist and support point lookup semantics.\n    expect(typeof emailIndex?.filter).toBe('function');\n    expect(Array.from(emailIndex.filter('a@example.com'))).toEqual([rows[0]]);\n\n    // The explicit table-level accessor must exist and support tuple filtering.\n    expect(typeof byTeamAndLevel?.filter).toBe('function');\n    expect(Array.from(byTeamAndLevel.filter(['red', 1]))).toEqual([rows[0]]);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/table_index_accessor.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { ModuleContext } from '../src/lib/schema';\nimport { table } from '../src/lib/table';\nimport { t } from '../src/lib/type_builders';\n\ndescribe('table index accessors', () => {\n  it('throws when an explicit index is missing accessor', () => {\n    expect(() =>\n      table(\n        {\n          name: 'person',\n          indexes: [\n            {\n              name: 'id_idx',\n              algorithm: 'btree',\n              columns: ['id'] as const,\n            } as any,\n          ] as const,\n        },\n        {\n          id: t.identity(),\n        }\n      )\n    ).toThrowError(\"must define a non-empty 'accessor'\");\n  });\n\n  it('allows duplicate explicit index accessors in raw table definitions', () => {\n    /**\n     * table() does not reject duplicate explicit accessor names at definition\n     * time. Runtime table view construction handles duplicate accessors by\n     * merging methods onto a single accessor object.\n     */\n    const tableSchema = table(\n      {\n        name: 'person',\n        indexes: [\n          {\n            accessor: 'dup',\n            algorithm: 'btree',\n            columns: ['id'] as const,\n          },\n          {\n            accessor: 'dup',\n            algorithm: 'hash',\n            columns: ['email'] as const,\n          },\n        ] as const,\n      },\n      {\n        id: t.identity(),\n        email: t.string(),\n      }\n    );\n\n    const rawTableDef = tableSchema.tableDef(new ModuleContext(), 'person');\n    expect(rawTableDef.indexes).toHaveLength(2);\n    expect(rawTableDef.indexes.map(index => index.accessorName)).toEqual([\n      'dup',\n      'dup',\n    ]);\n  });\n\n  it('accepts an explicit accessor for table-level indexes', () => {\n    const tableSchema = table(\n      {\n        name: 'person',\n        indexes: [\n          {\n            accessor: 'byName',\n            algorithm: 'btree',\n            columns: ['name'] as const,\n          },\n        ] as const,\n      },\n      {\n        name: t.string(),\n      }\n    );\n\n    const rawTableDef = tableSchema.tableDef(new ModuleContext(), 'person');\n    expect(rawTableDef.indexes).toHaveLength(1);\n    expect(rawTableDef.indexes[0].accessorName).toBe('byName');\n  });\n\n  it('derives accessor from the field name for field-level indexes', () => {\n    const tableSchema = table(\n      {\n        name: 'person',\n      },\n      {\n        displayName: t.string().index('btree'),\n      }\n    );\n\n    const rawTableDef = tableSchema.tableDef(new ModuleContext(), 'person');\n    expect(rawTableDef.indexes).toHaveLength(1);\n    expect(rawTableDef.indexes[0].accessorName).toBe('displayName');\n  });\n\n  it('keeps both implicit and explicit index entries when accessors match', () => {\n    /**\n     * Generated client bindings can include an explicit table-level index entry\n     * for the same logical index that is already inferred from field metadata\n     * (e.g. primaryKey implies an implicit index). This should not fail as a\n     * duplicate accessor when the definitions are equivalent.\n     */\n    const tableSchema = table(\n      {\n        name: 'player',\n        indexes: [\n          {\n            accessor: 'id',\n            name: 'player_id_idx_btree',\n            algorithm: 'btree',\n            columns: ['id'] as const,\n          },\n        ] as const,\n      },\n      {\n        id: t.u32().primaryKey(),\n      }\n    );\n\n    const ctx = new ModuleContext();\n    const rawTableDef = tableSchema.tableDef(ctx, 'player');\n\n    // Raw schema keeps both entries; runtime accessor construction merges by\n    // accessor name.\n    expect(rawTableDef.indexes).toHaveLength(2);\n    expect(rawTableDef.indexes.map(index => index.accessorName)).toEqual([\n      'id',\n      'id',\n    ]);\n\n    // The explicit canonical name should still be preserved in explicit names.\n    expect(ctx.moduleDef.explicitNames.entries).toEqual([\n      {\n        tag: 'Index',\n        value: {\n          sourceName: 'player_id_idx_btree',\n          canonicalName: 'player_id_idx_btree',\n        },\n      },\n    ]);\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/utils.ts",
    "content": "import BinaryWriter from '../src/lib/binary_writer';\nimport { Identity } from '../src/lib/identity';\nimport type { Infer } from '../src/lib/type_builders';\nimport { RowSizeHint, TableUpdateRows } from '../src/sdk/client_api/types';\nimport PlayerRow from '../test-app/src/module_bindings/player_table';\nimport { Point } from '../test-app/src/module_bindings/types';\nimport UserRow from '../test-app/src/module_bindings/user_table';\n\nexport const anIdentity = Identity.fromString(\n  '0000000000000000000000000000000000000000000000000000000000000069'\n);\nexport const bobIdentity = Identity.fromString(\n  '0000000000000000000000000000000000000000000000000000000000000b0b'\n);\nexport const sallyIdentity = Identity.fromString(\n  '000000000000000000000000000000000000000000000000000000000006a111'\n);\n\nexport function encodePlayer(value: Infer<typeof PlayerRow>): Uint8Array {\n  const writer = new BinaryWriter(1024);\n  PlayerRow.serialize(writer, value);\n  return writer.getBuffer();\n}\n\nexport function encodeUser(value: Infer<typeof UserRow>): Uint8Array {\n  const writer = new BinaryWriter(1024);\n  UserRow.serialize(writer, value);\n  return writer.getBuffer();\n}\n\nexport function encodeCreatePlayerArgs(\n  name: string,\n  location: Infer<typeof Point>\n): Uint8Array {\n  const writer = new BinaryWriter(1024);\n  writer.writeString(name);\n  Point.serialize(writer, location);\n  return writer.getBuffer();\n}\n\nexport function makeRowList(rowsData: Uint8Array) {\n  return {\n    sizeHint: RowSizeHint.FixedSize(0),\n    rowsData,\n  };\n}\n\nexport function makeQueryRows(table: string, rowsData: Uint8Array) {\n  return {\n    tables: [\n      {\n        table,\n        rows: makeRowList(rowsData),\n      },\n    ],\n  };\n}\n\nexport function makeQuerySetUpdate(\n  querySetId: number,\n  tableName: string,\n  inserts: Uint8Array,\n  deletes: Uint8Array = new Uint8Array()\n) {\n  return {\n    querySetId: { id: querySetId },\n    tables: [\n      {\n        tableName,\n        rows: [\n          TableUpdateRows.PersistentTable({\n            inserts: makeRowList(inserts),\n            deletes: makeRowList(deletes),\n          }),\n        ],\n      },\n    ],\n  };\n}\n"
  },
  {
    "path": "crates/bindings-typescript/tests/uuid.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport { Timestamp, Uuid } from '../src';\nimport * as crypto from 'crypto';\n\ndescribe('Uuid', () => {\n  test('toString UUid', () => {\n    const uuids = [\n      Uuid.NIL,\n      new Uuid(0x0102_0304_0506_0708_090a_0b0c_0d0e_0f10n),\n      Uuid.MAX,\n    ];\n\n    for (const uuid of uuids) {\n      const s = uuid.toString();\n      const uuid2 = Uuid.parse(s);\n      expect(s).toBe(uuid2.toString());\n      // Bigint structural equality\n      expect(uuid.asBigInt()).toBe(uuid2.asBigInt());\n    }\n  });\n\n  test('round_trip', () => {\n    const u1 = Uuid.NIL;\n    const s = u1.toString();\n    const u2 = Uuid.parse(s);\n\n    expect(u1.toString()).toBe(u2.toString());\n    expect(u1).toStrictEqual(u2);\n    expect(s).toBe(u2.toString());\n  });\n\n  test('version', () => {\n    let u = Uuid.NIL;\n    expect(u.getVersion()).toBe('Nil');\n    u = Uuid.MAX;\n    expect(u.getVersion()).toBe('Max');\n\n    const randomBytes = crypto.getRandomValues(new Uint8Array(16));\n    u = Uuid.fromRandomBytesV4(randomBytes);\n    expect(u.getVersion()).toBe('V4');\n\n    const counter = { value: Number(0) };\n    u = Uuid.fromCounterV7(\n      counter,\n      new Timestamp(1_686_000_000_000n),\n      randomBytes.slice(0, 4)\n    );\n    expect(u.getVersion()).toBe('V7');\n  });\n  test('wrap_around', () => {\n    // Check wraparound behavior\n    const counter = { value: 0x7fffffff }; // i32::MAX\n\n    Uuid.fromCounterV7(counter, Timestamp.now(), new Uint8Array(4));\n\n    expect(counter.value).toBe(0);\n  });\n  test('negative_timestamp_error', () => {\n    const counter = { value: 0 };\n    const ts = new Timestamp(-1n);\n\n    expect(() => {\n      Uuid.fromCounterV7(counter, ts, new Uint8Array(4));\n    }).toThrow('`fromCounterV7` `timestamp` before unix epoch');\n  });\n  test('ordered', () => {\n    // from_u128 equivalent:\n    const u1 = new Uuid(1n);\n    const u2 = new Uuid(2n);\n\n    expect(u1.compareTo(u2)).toBeLessThan(0);\n    expect(u2.compareTo(u1)).toBeGreaterThan(0);\n    expect(u1.compareTo(u1)).toBe(0);\n    expect(u1.compareTo(u2)).not.toBe(0);\n\n    // Check we start from zero\n    const counterStart = { value: 0 };\n    const tsStart = Timestamp.now();\n\n    const uStart = Uuid.fromCounterV7(counterStart, tsStart, new Uint8Array(4));\n\n    expect(uStart.getCounter()).toBe(0);\n\n    // Check ordering over many UUIDs up to the max counter value\n    const total = 10_000;\n    const counter = { value: 0x7fffffff - total };\n    const ts = Timestamp.now();\n\n    const bytes = crypto.getRandomValues(new Uint8Array(4));\n    let a = Uuid.fromCounterV7(counter, ts, bytes);\n\n    for (let i = 0; i < total; i++) {\n      const b = Uuid.fromCounterV7(counter, ts, bytes);\n\n      expect(a.getVersion()).toBe('V7');\n\n      expect(\n        a.compareTo(b),\n        `UUIDs are not ordered at ${i}: ${a.toString()} !< ${b.toString()}`\n      ).toBeLessThan(0);\n\n      expect(\n        a.getCounter(),\n        `UUID counters are not ordered at ${i}: ${a.getCounter()} !< ${b.getCounter()}`\n      ).toBeLessThan(b.getCounter());\n      a = b;\n    }\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tests/version.test.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport {\n  SemanticVersion,\n  _MINIMUM_CLI_VERSION,\n  ensureMinimumVersionOrThrow,\n} from '../src/sdk/version.ts';\n\ndescribe('SemanticVersion', () => {\n  describe('Minimum version check', () => {\n    test('older versions throw an error', () => {\n      const olderVersions: string[] = ['0.0.1', '0.1.0', '1.1.0'];\n      if (_MINIMUM_CLI_VERSION.major > 0) {\n        const olderVersion = _MINIMUM_CLI_VERSION.clone();\n        olderVersion.major -= 1;\n        olderVersions.push(olderVersion.toString());\n      }\n      if (_MINIMUM_CLI_VERSION.minor > 0) {\n        const olderVersion = _MINIMUM_CLI_VERSION.clone();\n        olderVersion.minor -= 1;\n        olderVersions.push(olderVersion.toString());\n      }\n      if (_MINIMUM_CLI_VERSION.patch > 0) {\n        const olderVersion = _MINIMUM_CLI_VERSION.clone();\n        olderVersion.patch -= 1;\n        olderVersions.push(olderVersion.toString());\n      }\n      if (!_MINIMUM_CLI_VERSION.preRelease) {\n        const olderVersion = _MINIMUM_CLI_VERSION.clone();\n        olderVersion.preRelease = ['alpha'];\n        olderVersions.push(olderVersion.toString());\n      }\n      const olderVersion = _MINIMUM_CLI_VERSION.clone();\n      if (olderVersion.preRelease != null) {\n        if (typeof olderVersion.preRelease[0] === 'number') {\n          olderVersion.preRelease[0] = olderVersion.preRelease[0] - 1;\n        } else {\n          olderVersion.preRelease[0] += 'alpha';\n        }\n      }\n      const errorRegexp = new RegExp(\n        '.*generated with an incompatible version.*'\n      );\n      for (const versionString of olderVersions) {\n        expect(\n          () => ensureMinimumVersionOrThrow(versionString),\n          `Checking ${versionString}`\n        ).toThrow(errorRegexp);\n      }\n    });\n\n    test('newer versions do not throw', () => {\n      const newerVersions: string[] = [_MINIMUM_CLI_VERSION.toString()];\n      let newVersion = _MINIMUM_CLI_VERSION.clone();\n      newVersion.major += 1;\n      newerVersions.push(newVersion.toString());\n      newVersion = _MINIMUM_CLI_VERSION.clone();\n      newVersion.minor += 1;\n      newerVersions.push(newVersion.toString());\n      newVersion = _MINIMUM_CLI_VERSION.clone();\n      newVersion.patch += 1;\n      newerVersions.push(newVersion.toString());\n      newVersion = _MINIMUM_CLI_VERSION.clone();\n      if (newVersion.preRelease != null) {\n        newVersion.preRelease = null;\n        newerVersions.push(newVersion.toString());\n      }\n      for (const versionString of newerVersions) {\n        expect(\n          () => ensureMinimumVersionOrThrow(versionString),\n          `Checking ${versionString}`\n        ).not.toThrow();\n      }\n    });\n  });\n  describe('Parsing semantic version strings', () => {\n    test('valid versions', () => {\n      // This is a dummy test to ensure that the test suite runs\n      // and that the TableCache is working as expected.\n      // You can add more tests here to cover different scenarios.\n      //parseVersionString('1.0.0');\n      const tests: [string, SemanticVersion][] = [\n        ['1.0.0', new SemanticVersion(1, 0, 0)],\n        ['1.0.0-alpha', new SemanticVersion(1, 0, 0, ['alpha'])],\n        ['1.0.0-alpha.1', new SemanticVersion(1, 0, 0, ['alpha', 1])],\n        [\n          '1.0.0-alpha.20.beta',\n          new SemanticVersion(1, 0, 0, ['alpha', 20, 'beta']),\n        ],\n        ['1.0.0-alpha.beta', new SemanticVersion(1, 0, 0, ['alpha', 'beta'])],\n        ['0.2.1', new SemanticVersion(0, 2, 1)],\n        ['0.0.0', new SemanticVersion(0, 0, 0)],\n        ['10.2.1', new SemanticVersion(10, 2, 1)],\n        [\n          '1.0.0+20130313144700',\n          new SemanticVersion(1, 0, 0, null, '20130313144700'),\n        ],\n        ['1.0.0-alpha+001', new SemanticVersion(1, 0, 0, ['alpha'], '001')],\n        [\n          '1.0.0-alpha.beta+exp.sha.5114f85',\n          new SemanticVersion(1, 0, 0, ['alpha', 'beta'], 'exp.sha.5114f85'),\n        ],\n        [\n          '1.0.0+exp.sha.5114f85',\n          new SemanticVersion(1, 0, 0, null, 'exp.sha.5114f85'),\n        ],\n      ];\n      for (const [versionString, expectedVersion] of tests) {\n        const parsedVersion = SemanticVersion.parseVersionString(versionString);\n        expect(parsedVersion, `Parsing ${versionString}`).toEqual(\n          expectedVersion\n        );\n      }\n    });\n\n    test('invalid version strings should throw an error', () => {\n      const invalidTests: string[] = [\n        '1.0', // Missing patch version\n        '1', // Missing minor and patch versions\n        '1.0.0-', // Trailing hyphen\n        '1.0.0+', // Trailing plus\n        '1.0.0-alpha..1', // Double dots in pre-release\n        '1.0.0+build..info', // Double dots in build metadata\n        '1.0.0-alpha!', // Invalid character in pre-release\n        '1.0.0+build!', // Invalid character in build metadata\n        'abc.def.ghi', // Completely invalid format\n        '', // Empty string\n      ];\n      for (const versionString of invalidTests) {\n        expect(() => SemanticVersion.parseVersionString(versionString)).toThrow(\n          `Invalid version string: ${versionString}`\n        );\n\n        // Checking the minimum version should also fail.\n        expect(() => ensureMinimumVersionOrThrow(versionString)).toThrow(\n          `Invalid version string: ${versionString}`\n        );\n      }\n    });\n  });\n  describe('Comparing SemanticVersions', () => {\n    function normalizedCompare(\n      version1: SemanticVersion,\n      version2: SemanticVersion\n    ): number {\n      const c = version1.compare(version2);\n      if (c < 0) {\n        return -1;\n      }\n      if (c > 0) {\n        return 1;\n      }\n      return 0;\n    }\n    test('test comparison order', () => {\n      const cases: [string, string, number][] = [\n        ['1.0.0', '1.0.0', 0],\n        ['1.0.0', '1.0.1', -1],\n        ['1.10.0', '1.2.1', 1],\n        ['1.2.1', '1.10.0', -1],\n        ['1.0.1', '1.0.0', 1],\n        ['1.0.0-alpha', '1.0.0-alpha', 0],\n        ['1.0.0-alpha', '1.0.0-beta', -1],\n        ['1.0.0-beta', '1.0.0-alpha', 1],\n        ['1.0.0-alpha', '1.0.0-alpha.beta', -1],\n        ['1.0.0-1', '1.0.0-alpha.beta', -1],\n        ['1.0.0-alpha.1', '1.0.0-alpha.2', -1],\n        ['1.0.0-alpha.beta', '1.0.0-alpha', 1],\n        ['2.0.0-alpha.beta', '2.0.0-alpha.beta', 0],\n        ['2.0.0-alpha.beta+001', '2.0.0-alpha.beta+001', 0],\n        ['2.0.0-alpha.beta+001', '2.0.0-alpha.beta+002', 0], // Build tags don't affect comparison\n      ];\n      for (const [left, right, expectedComparison] of cases) {\n        const parsedLeft = SemanticVersion.parseVersionString(left);\n        const parsedRight = SemanticVersion.parseVersionString(right);\n        expect(\n          normalizedCompare(parsedLeft, parsedRight),\n          'Comparing ' + left + ' and ' + right\n        ).toEqual(expectedComparison);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "crates/bindings-typescript/tsconfig.build.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": false,\n    \"declaration\": true,\n    \"emitDeclarationOnly\": true,\n    \"declarationMap\": true,\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\"\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"module\": \"ESNext\",\n\n    \"strict\": true,\n    \"declaration\": true,\n    \"emitDeclarationOnly\": false,\n    \"noEmit\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"allowImportingTsExtensions\": true,\n    \"noImplicitAny\": true,\n    \"moduleResolution\": \"Bundler\",\n    \"isolatedDeclarations\": false,\n\n    // This library is ESM-only, do not import commonjs modules\n    \"esModuleInterop\": false,\n    \"useDefineForClassFields\": true,\n\n    // Crucial when using esbuild/swc/babel instead of tsc emit:\n    \"verbatimModuleSyntax\": true,\n    \"isolatedModules\": true\n  },\n  \"include\": [\"src/**/*\", \"tests/**/*\", \"vitest.config.ts\", \"tsup.config.ts\"],\n  \"exclude\": [\"node_modules\", \"**/__tests__/*\", \"dist/**/*\"]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/tsconfig.typecheck.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\n    \"tests/**/*\",\n    \"src/lib/**/*\",\n    \"src/sdk/**/*\",\n    \"src/react/**/*\",\n    \"test-app/**/*\"\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \"dist/**/*\",\n    \"src/svelte/**/*\",\n    \"src/vue/**/*\",\n    \"src/server/**/*\"\n  ]\n}\n"
  },
  {
    "path": "crates/bindings-typescript/tsup.config.ts",
    "content": "// tsup.config.ts\nimport { defineConfig, type Options } from 'tsup';\n\nfunction commonEsbuildTweaks(): NonNullable<Options['esbuildOptions']> {\n  return options => {\n    // Prefer \"exports\".\"development\" when deps provide it; harmless otherwise.\n    options.conditions = ['development', 'import', 'default'];\n    options.mainFields = ['browser', 'module', 'main'];\n  };\n}\n\nconst outExtension = (ctx: { format: string }) => ({\n  js: ctx.format === 'cjs' ? '.cjs' : ctx.format === 'esm' ? '.mjs' : '.js',\n});\n\nexport default defineConfig([\n  // Root wrapper (SSR-friendly): dist/index.{mjs,cjs}\n  {\n    entry: { index: 'src/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Browser-flavored root wrapper: dist/index.browser.mjs\n  {\n    entry: { 'index.browser': 'src/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // React subpath (SSR-friendly): dist/react/index.{mjs,cjs}\n  {\n    entry: { index: 'src/react/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/react',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // React subpath (browser ESM): dist/browser/react/index.mjs\n  {\n    entry: { index: 'src/react/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/browser/react',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Vue subpath (SSR-friendly): dist/vue/index.{mjs,cjs}\n  {\n    entry: { index: 'src/vue/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/vue',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['vue'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Vue subpath (browser ESM): dist/browser/vue/index.mjs\n  {\n    entry: { index: 'src/vue/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/browser/vue',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['vue'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Svelte subpath (SSR-friendly): dist/svelte/index.{mjs,cjs}\n  {\n    entry: { index: 'src/svelte/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/svelte',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['svelte', 'svelte/store'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Svelte subpath (browser ESM): dist/browser/svelte/index.mjs\n  {\n    entry: { index: 'src/svelte/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/browser/svelte',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['svelte', 'svelte/store'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Angular subpath (SSR-friendly): dist/angular/index.{mjs,cjs}\n  {\n    entry: { index: 'src/angular/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/angular',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['@angular/core'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Angular subpath (browser ESM): dist/browser/angular/index.mjs\n  {\n    entry: { index: 'src/angular/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/browser/angular',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['@angular/core'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // SDK subpath (SSR-friendly): dist/sdk/index.{mjs,cjs}\n  {\n    entry: { index: 'src/sdk/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/sdk',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // SDK browser ESM: dist/sdk/index.browser.mjs\n  {\n    entry: { 'index.browser': 'src/sdk/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/sdk',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Server subpath (SSR / node-friendly): dist/server/index.mjs\n  {\n    entry: { index: 'src/server/index.ts' },\n    format: 'esm',\n    target: 'esnext',\n    outDir: 'dist/server',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral', // flip to 'node' if you actually rely on Node builtins\n    banner: {\n      js:\n        'typeof globalThis!==\"undefined\"&&(' +\n        '(globalThis.global=globalThis.global||globalThis),' +\n        '(globalThis.window=globalThis.window||globalThis));',\n    },\n    treeshake: {\n      moduleSideEffects: ['src/server/polyfills.ts'],\n    },\n    external: ['undici', /^spacetime:sys.*$/],\n    noExternal: ['object-inspect', 'base64-js', 'statuses', 'pure-rand'],\n    outExtension,\n    esbuildOptions: (options, ctx) => {\n      // object-inspect tries to import the node `util` module,\n      // which we want to disable.\n      options.alias = { util: './src/util-stub.ts' };\n      commonEsbuildTweaks()(options, ctx);\n    },\n  },\n\n  // TanStack subpath (SSR-friendly): dist/tanstack/index.{mjs,cjs}\n  {\n    entry: { index: 'src/tanstack/index.ts' },\n    format: ['esm', 'cjs'],\n    target: 'es2022',\n    outDir: 'dist/tanstack',\n    dts: false,\n    sourcemap: true,\n    clean: true,\n    platform: 'neutral',\n    treeshake: 'smallest',\n    external: ['react', '@tanstack/react-query'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // The below minified builds are not referenced in package.json and are\n  // just included in the build for measuring the size impact of minification.\n  // It is expected that consumers of the library will run their own\n  // minification as part of their app bundling process.\n\n  // Minified browser build: dist/min/index.browser.mjs\n  {\n    entry: { 'index.browser': 'src/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/min',\n    dts: false,\n    sourcemap: true,\n    minify: 'terser',\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension,\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Minified browser React build: dist/min/react/index.mjs\n  {\n    entry: { index: 'src/react/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/min/react',\n    dts: false,\n    sourcemap: true,\n    minify: 'terser',\n    platform: 'browser',\n    external: ['undici'],\n    treeshake: 'smallest',\n    outExtension: ({ format }) => ({ js: format === 'cjs' ? '.cjs' : '.mjs' }),\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n\n  // Minified browser SDK build: dist/min/sdk/index.browser.mjs\n  {\n    entry: { 'index.browser': 'src/sdk/index.ts' },\n    format: ['esm'],\n    target: 'es2022',\n    outDir: 'dist/min/sdk',\n    dts: false,\n    sourcemap: true,\n    minify: 'terser',\n    platform: 'browser',\n    treeshake: 'smallest',\n    external: ['undici'],\n    outExtension: ({ format }) => ({ js: format === 'cjs' ? '.cjs' : '.mjs' }),\n    esbuildOptions: commonEsbuildTweaks(),\n  },\n]) satisfies\n  | Options\n  | Options[]\n  | ((\n      overrideOptions: Options\n    ) => Options | Options[] | Promise<Options | Options[]>);\n"
  },
  {
    "path": "crates/bindings-typescript/vitest.config.ts",
    "content": "import type { UserConfig } from 'vite';\nimport { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    include: ['tests/**/*.test.ts'],\n    globals: true,\n    environment: 'node',\n    typecheck: {\n      include: ['tests/**/*.test.ts'],\n      tsconfig: './tsconfig.typecheck.json',\n    },\n  },\n}) satisfies UserConfig as UserConfig;\n"
  },
  {
    "path": "crates/cli/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-cli\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"A command line interface for SpacetimeDB\"\nrust-version.workspace = true\n\n[features]\nmarkdown-docs = []\n\n[lib]\nbench = false\n\n[[bin]]\nname = \"spacetimedb-cli\"\npath = \"src/main.rs\"\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nspacetimedb-auth.workspace = true\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-codegen.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-fs-utils.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-schema.workspace = true\n\nanyhow.workspace = true\nbase64.workspace = true\nbytes.workspace = true\ncargo_metadata.workspace = true\nchrono.workspace = true\nclap = { workspace = true, features = [\"derive\", \"env\", \"string\"] }\ncolored.workspace = true\nconvert_case.workspace = true\ndirs.workspace = true\nduct.workspace = true\nemail_address.workspace = true\nfutures.workspace = true\nflate2.workspace = true\nfs-err.workspace = true\nhttp.workspace = true\nis-terminal.workspace = true\nitertools.workspace = true\nignore.workspace = true\nindicatif.workspace = true\njson5.workspace = true\njsonwebtoken.workspace = true\nmimalloc.workspace = true\npercent-encoding.workspace = true\nregex.workspace = true\nreqwest.workspace = true\nrustyline.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\nserde_json = { workspace = true, features = [\"raw_value\", \"preserve_order\"] }\nserde_with = { workspace = true, features = [\"chrono_0_4\"] }\nslab.workspace = true\nsyntect.workspace = true\ntabled.workspace = true\ntar.workspace = true\ntempfile.workspace = true\ntermcolor.workspace = true\ntermtree.workspace = true\nthiserror.workspace = true\ntokio.workspace = true\ntokio-tungstenite.workspace = true\ntoml.workspace = true\ntoml_edit.workspace = true\ntracing = { workspace = true, features = [\"release_max_level_off\"] }\nwalkdir.workspace = true\nwasmbin.workspace = true\nwebbrowser.workspace = true\nclap-markdown.workspace = true\ngit2.workspace = true\nglob.workspace = true\ndialoguer = { workspace = true, features = [\"fuzzy-select\"] }\nrolldown.workspace = true\nrolldown_common.workspace = true\nrolldown_error.workspace = true\nrolldown_utils.workspace = true\nxmltree.workspace = true\nquick-xml.workspace = true\nnames.workspace = true\nnotify.workspace = true\npath-clean = \"1.0.1\"\n\n[dev-dependencies]\npretty_assertions.workspace = true\nfs_extra.workspace = true\n\n[target.'cfg(not(target_env = \"msvc\"))'.dependencies]\ntikv-jemallocator = { workspace = true }\ntikv-jemalloc-ctl = { workspace = true }\n\n[target.'cfg(windows)'.dependencies]\nwindows-sys = { workspace = true, features = [\"Win32_System_Console\"] }\n\n[build-dependencies]\nserde = { workspace = true, features = [\"derive\"] }\nserde_json.workspace = true\ntoml.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/cli/build.rs",
    "content": "use std::collections::BTreeMap;\nuse std::fs;\nuse std::io::{self, Write};\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse toml::Value;\n\nfn main() {\n    let git_hash = find_git_hash();\n    println!(\"cargo:rustc-env=GIT_HASH={git_hash}\");\n\n    generate_template_files();\n}\n\nfn nix_injected_commit_hash() -> Option<String> {\n    use std::env::VarError;\n    // Our flake.nix sets this environment variable to be our git commit hash during the build.\n    // This is important because git metadata is otherwise not available within the nix build sandbox,\n    // and we don't install the git command-line tool in our build.\n    match std::env::var(\"SPACETIMEDB_NIX_BUILD_GIT_COMMIT\") {\n        Ok(commit_sha) => {\n            // Var is set, we're building under Nix.\n            Some(commit_sha)\n        }\n\n        Err(VarError::NotPresent) => {\n            // Var is not set, we're not in Nix.\n            None\n        }\n        Err(VarError::NotUnicode(gross)) => {\n            // Var is set but is invalid unicode, something is very wrong.\n            panic!(\"Injected commit hash is not valid unicode: {gross:?}\")\n        }\n    }\n}\n\nfn is_nix_build() -> bool {\n    nix_injected_commit_hash().is_some()\n}\n\nfn find_git_hash() -> String {\n    nix_injected_commit_hash().unwrap_or_else(|| {\n        // When we're *not* building in Nix, we can assume that git metadata is still present in the filesystem,\n        // and that the git command-line tool is installed.\n        let output = Command::new(\"git\").args([\"rev-parse\", \"HEAD\"]).output().unwrap();\n        String::from_utf8(output.stdout).unwrap().trim().to_string()\n    })\n}\n\nfn get_manifest_dir() -> PathBuf {\n    PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap())\n}\n\n// This method generates functions with data used in `spacetime init`:\n//\n//   * `get_templates_json` - returns contents of the JSON file with the list of templates\n//   * `get_template_files` - returns a HashMap with templates contents based on the\n//                            templates list at templates/templates-list.json\n//   * `get_ai_rules_base` - returns base AI rules for all languages\n//   * `get_ai_rules_typescript` - returns TypeScript-specific AI rules\n//   * `get_ai_rules_rust` - returns Rust-specific AI rules\n//   * `get_ai_rules_csharp` - returns C#-specific AI rules\nfn generate_template_files() {\n    let manifest_dir = get_manifest_dir();\n    let repo_root = get_repo_root();\n    let templates_dir = repo_root.join(\"templates\");\n    let out_dir = std::env::var(\"OUT_DIR\").unwrap();\n    let dest_path = Path::new(&out_dir).join(\"embedded_templates.rs\");\n\n    println!(\"cargo:rerun-if-changed=../../templates\");\n\n    let discovered_templates = discover_templates(&templates_dir);\n\n    let mut generated_code = String::new();\n    generated_code.push_str(\"use spacetimedb_data_structures::map::HashMap;\\n\\n\");\n\n    generated_code.push_str(\"pub fn get_templates_json() -> &'static str {\\n\");\n    generated_code.push_str(\"    r#\\\"\");\n    generated_code.push_str(&generate_templates_json(&discovered_templates));\n    generated_code.push_str(\"\\\"#\\n\");\n    generated_code.push_str(\"}\\n\\n\");\n\n    generated_code\n        .push_str(\"pub fn get_template_files() -> HashMap<&'static str, HashMap<&'static str, &'static str>> {\\n\");\n    generated_code.push_str(\"    let mut templates = HashMap::new();\\n\\n\");\n\n    for template in &discovered_templates {\n        if let Some(ref server_source) = template.server_source {\n            let server_path = PathBuf::from(server_source);\n            generate_template_entry(&mut generated_code, &server_path, server_source, &manifest_dir);\n        }\n\n        if let Some(ref client_source) = template.client_source {\n            let client_path = PathBuf::from(client_source);\n            generate_template_entry(&mut generated_code, &client_path, client_source, &manifest_dir);\n        }\n    }\n\n    generated_code.push_str(\"    templates\\n\");\n    generated_code.push_str(\"}\\n\\n\");\n\n    let repo_root = get_repo_root();\n    let workspace_cargo = repo_root.join(\"Cargo.toml\");\n    println!(\"cargo:rerun-if-changed={}\", workspace_cargo.display());\n\n    let (workspace_edition, workspace_versions) =\n        extract_workspace_metadata(&workspace_cargo).expect(\"Failed to extract workspace metadata\");\n\n    let ts_bindings_package = repo_root.join(\"crates/bindings-typescript/package.json\");\n    println!(\"cargo:rerun-if-changed={}\", ts_bindings_package.display());\n    let ts_bindings_version =\n        extract_ts_bindings_version(&ts_bindings_package).expect(\"Failed to read TypeScript bindings version\");\n\n    // Embed AI rules files from docs/static/ai-rules/\n    let ai_rules_dir = repo_root.join(\"docs/static/ai-rules\");\n\n    // Base rules (all languages)\n    let base_rules_path = ai_rules_dir.join(\"spacetimedb.mdc\");\n    if base_rules_path.exists() {\n        generated_code.push_str(\"pub fn get_ai_rules_base() -> &'static str {\\n\");\n        generated_code.push_str(&format!(\n            \"    include_str!(\\\"{}\\\")\\n\",\n            base_rules_path.to_str().unwrap().replace('\\\\', \"\\\\\\\\\")\n        ));\n        generated_code.push_str(\"}\\n\\n\");\n        println!(\"cargo:rerun-if-changed={}\", base_rules_path.display());\n    } else {\n        panic!(\"Could not find \\\"docs/static/ai-rules/spacetimedb.mdc\\\" file.\");\n    }\n\n    // TypeScript-specific rules\n    let ts_rules_path = ai_rules_dir.join(\"spacetimedb-typescript.mdc\");\n    if ts_rules_path.exists() {\n        generated_code.push_str(\"pub fn get_ai_rules_typescript() -> &'static str {\\n\");\n        generated_code.push_str(&format!(\n            \"    include_str!(\\\"{}\\\")\\n\",\n            ts_rules_path.to_str().unwrap().replace('\\\\', \"\\\\\\\\\")\n        ));\n        generated_code.push_str(\"}\\n\\n\");\n        println!(\"cargo:rerun-if-changed={}\", ts_rules_path.display());\n    } else {\n        panic!(\"Could not find \\\"docs/static/ai-rules/spacetimedb-typescript.mdc\\\" file.\");\n    }\n\n    // Rust-specific rules\n    let rust_rules_path = ai_rules_dir.join(\"spacetimedb-rust.mdc\");\n    if rust_rules_path.exists() {\n        generated_code.push_str(\"pub fn get_ai_rules_rust() -> &'static str {\\n\");\n        generated_code.push_str(&format!(\n            \"    include_str!(\\\"{}\\\")\\n\",\n            rust_rules_path.to_str().unwrap().replace('\\\\', \"\\\\\\\\\")\n        ));\n        generated_code.push_str(\"}\\n\\n\");\n        println!(\"cargo:rerun-if-changed={}\", rust_rules_path.display());\n    } else {\n        panic!(\"Could not find \\\"docs/static/ai-rules/spacetimedb-rust.mdc\\\" file.\");\n    }\n\n    // C#-specific rules\n    let csharp_rules_path = ai_rules_dir.join(\"spacetimedb-csharp.mdc\");\n    if csharp_rules_path.exists() {\n        generated_code.push_str(\"pub fn get_ai_rules_csharp() -> &'static str {\\n\");\n        generated_code.push_str(&format!(\n            \"    include_str!(\\\"{}\\\")\\n\",\n            csharp_rules_path.to_str().unwrap().replace('\\\\', \"\\\\\\\\\")\n        ));\n        generated_code.push_str(\"}\\n\\n\");\n        println!(\"cargo:rerun-if-changed={}\", csharp_rules_path.display());\n    } else {\n        panic!(\"Could not find \\\"docs/static/ai-rules/spacetimedb-csharp.mdc\\\" file.\");\n    }\n\n    // Expose workspace metadata so `spacetime init` can rewrite template manifests without hardcoding versions.\n    generated_code.push_str(\"pub fn get_workspace_edition() -> &'static str {\\n\");\n    generated_code.push_str(&format!(\"    \\\"{}\\\"\\n\", workspace_edition.escape_default()));\n    generated_code.push_str(\"}\\n\\n\");\n\n    generated_code.push_str(\"pub fn get_workspace_dependency_version(name: &str) -> Option<&'static str> {\\n\");\n    generated_code.push_str(\"    match name {\\n\");\n    for (name, version) in &workspace_versions {\n        generated_code.push_str(&format!(\n            \"        \\\"{}\\\" => Some(\\\"{}\\\"),\\n\",\n            name.escape_default(),\n            version.escape_default()\n        ));\n    }\n    generated_code.push_str(\"        _ => None,\\n\");\n    generated_code.push_str(\"    }\\n\");\n    generated_code.push_str(\"}\\n\");\n\n    generated_code.push('\\n');\n    generated_code.push_str(\"pub fn get_typescript_bindings_version() -> &'static str {\\n\");\n    generated_code.push_str(&format!(\"    \\\"{}\\\"\\n\", ts_bindings_version.escape_default()));\n    generated_code.push_str(\"}\\n\");\n\n    write_if_changed(&dest_path, generated_code.as_bytes()).expect(\"Failed to write embedded_templates.rs\");\n}\n\n#[derive(Debug, serde::Serialize)]\nstruct TemplateInfo {\n    id: String,\n    description: String,\n    #[serde(serialize_with = \"serialize_option_string_as_empty\")]\n    server_source: Option<String>,\n    #[serde(serialize_with = \"serialize_option_string_as_empty\")]\n    client_source: Option<String>,\n    server_lang: Option<String>,\n    client_lang: Option<String>,\n    client_framework: Option<String>,\n}\n\n#[derive(serde::Serialize)]\nstruct TemplatesJson<'a> {\n    templates: &'a [TemplateInfo],\n}\n\n#[derive(serde::Deserialize)]\nstruct TemplateMetadata {\n    description: String,\n    client_lang: Option<String>,\n    client_framework: Option<String>,\n    server_lang: Option<String>,\n}\n\nfn discover_templates(templates_dir: &Path) -> Vec<TemplateInfo> {\n    let mut templates = Vec::new();\n\n    let entries = match fs::read_dir(templates_dir) {\n        Ok(entries) => entries,\n        Err(_) => return templates,\n    };\n\n    for entry in entries.flatten() {\n        let path = entry.path();\n        if !path.is_dir() {\n            continue;\n        }\n\n        let template_name = match path.file_name().and_then(|n| n.to_str()) {\n            Some(name) => name,\n            None => continue,\n        };\n\n        let metadata_path = path.join(\".template.json\");\n        if !metadata_path.exists() {\n            continue;\n        }\n\n        let metadata_content = match fs::read_to_string(&metadata_path) {\n            Ok(content) => content,\n            Err(_) => continue,\n        };\n\n        let metadata: TemplateMetadata = match serde_json::from_str(&metadata_content) {\n            Ok(meta) => meta,\n            Err(_) => continue,\n        };\n\n        let server_source = if metadata.server_lang.is_some() {\n            Some(format!(\"{}/spacetimedb\", template_name))\n        } else {\n            None\n        };\n\n        let client_source = if metadata.client_lang.is_some() {\n            Some(template_name.to_string())\n        } else {\n            None\n        };\n\n        if server_source.is_some() || client_source.is_some() {\n            templates.push(TemplateInfo {\n                id: template_name.to_string(),\n                description: metadata.description,\n                server_source,\n                client_source,\n                server_lang: metadata.server_lang,\n                client_lang: metadata.client_lang,\n                client_framework: metadata.client_framework,\n            });\n        }\n    }\n\n    templates.sort_by(|a, b| a.id.cmp(&b.id));\n    templates\n}\n\nfn generate_templates_json(templates: &[TemplateInfo]) -> String {\n    let payload = TemplatesJson { templates };\n    serde_json::to_string_pretty(&payload).expect(\"Failed to serialize templates JSON\")\n}\n\nfn serialize_option_string_as_empty<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>\nwhere\n    S: serde::Serializer,\n{\n    serializer.serialize_str(value.as_deref().unwrap_or(\"\"))\n}\n\nfn generate_template_entry(code: &mut String, template_path: &Path, source: &str, manifest_dir: &Path) {\n    let (git_files, resolved_base) = get_git_tracked_files(template_path, manifest_dir);\n\n    if git_files.is_empty() {\n        panic!(\"Template '{}' has no git-tracked files! Check that the directory exists and contains files tracked by git.\", source);\n    }\n\n    // Example: /Users/user/SpacetimeDB\n    let repo_root = get_repo_root();\n    let repo_root_canonical = std::fs::canonicalize(&repo_root).unwrap();\n    // Example: /Users/user/SpacetimeDB/crates/cli\n    let manifest_canonical = Path::new(manifest_dir).canonicalize().unwrap();\n    // Example: crates/cli\n    let manifest_rel = manifest_canonical.strip_prefix(&repo_root_canonical).unwrap();\n\n    // Example for inside crate: /Users/user/SpacetimeDB/crates/cli/templates/basic-rs/server\n    // Example for outside crate: /Users/user/SpacetimeDB/modules/chat-console-rs\n    let resolved_canonical = repo_root.join(&resolved_base).canonicalize().unwrap();\n\n    // If the files are outside of the cli crate we need to copy them to the crate directory,\n    // so they're included properly even when the crate is published\n    let local_copy_dir = if resolved_canonical.strip_prefix(&manifest_canonical).is_err() {\n        // Example source: \"../../modules/quickstart-chat\"\n        // Sanitized: \"parent_parent_modules_quickstart-chat\"\n        let sanitized_source = source.replace(\"/\", \"_\").replace(\"\\\\\", \"_\").replace(\"..\", \"parent\");\n        // Example: /Users/user/SpacetimeDB/crates/cli/.templates/parent_parent_modules_quickstart-chat\n        let copy_dir = Path::new(manifest_dir).join(\".templates\").join(&sanitized_source);\n        fs::create_dir_all(&copy_dir).expect(\"Failed to create .templates directory\");\n\n        Some(copy_dir)\n    } else {\n        None\n    };\n\n    code.push_str(\"    {\\n\");\n    code.push_str(\"        let mut files = HashMap::new();\\n\");\n\n    for file_path in git_files {\n        // Example file_path: modules/chat-console-rs/src/lib.rs (relative to repo root)\n        // Example resolved_base: modules/chat-console-rs\n        // Example relative_path: src/lib.rs\n        let relative_path = match file_path.strip_prefix(&resolved_base) {\n            Ok(p) => p,\n            Err(_) => {\n                eprintln!(\n                    \"Warning: Could not strip prefix '{}' from '{}' for source '{}'\",\n                    resolved_base.display(),\n                    file_path.display(),\n                    source\n                );\n                continue;\n            }\n        };\n        // Example: \"src/lib.rs\"\n        let relative_str = relative_path.to_str().unwrap().replace(\"\\\\\", \"/\");\n\n        // Example: /Users/user/SpacetimeDB/modules/quickstart-chat/src/lib.rs\n        let full_path = repo_root.join(&file_path);\n        if full_path.exists() && full_path.is_file() {\n            let include_path = if let Some(ref copy_dir) = local_copy_dir {\n                // Outside crate: copy to .templates\n                // Example dest_file: /Users/user/SpacetimeDB/crates/cli/.templates/parent_parent_modules_chat-console-rs/src/lib.rs\n                let dest_file = copy_dir.join(relative_path);\n                fs::create_dir_all(dest_file.parent().unwrap()).expect(\"Failed to create parent directory\");\n                copy_if_changed(&full_path, &dest_file)\n                    .unwrap_or_else(|_| panic!(\"Failed to copy file {:?} to {:?}\", full_path, dest_file));\n\n                // Example relative_to_manifest: .templates/parent_parent_modules_chat-console-rs/src/lib.rs\n                let relative_to_manifest = dest_file.strip_prefix(manifest_dir).unwrap();\n                let path_str = relative_to_manifest.to_str().unwrap().replace(\"\\\\\", \"/\");\n                // Watch the original file for changes\n                // Example: modules/chat-console-rs/src/lib.rs\n                println!(\"cargo:rerun-if-changed={}\", full_path.display());\n                path_str\n            } else {\n                // Inside crate: use path relative to CARGO_MANIFEST_DIR\n                // Example file_path: crates/cli/templates/basic-rs/server/src/lib.rs\n                // Example manifest_rel: crates/cli\n                // Result: templates/basic-rs/server/src/lib.rs\n                let relative_to_manifest = file_path.strip_prefix(manifest_rel).unwrap();\n                let path_str = relative_to_manifest.to_str().unwrap().replace(\"\\\\\", \"/\");\n                // Example: crates/cli/templates/basic-rs/server/src/lib.rs\n                println!(\"cargo:rerun-if-changed={}\", full_path.display());\n                path_str\n            };\n\n            // Example include_path (inside crate): \"templates/basic-rs/server/src/lib.rs\"\n            // Example include_path (outside crate): \".templates/parent_parent_modules_chat-console-rs/src/lib.rs\"\n            // Example relative_str: \"src/lib.rs\"\n            code.push_str(&format!(\n                \"        files.insert(\\\"{}\\\", include_str!(concat!(env!(\\\"CARGO_MANIFEST_DIR\\\"), \\\"/{}\\\")));\\n\",\n                relative_str, include_path\n            ));\n        }\n    }\n\n    code.push_str(&format!(\"        templates.insert(\\\"{}\\\", files);\\n\", source));\n    code.push_str(\"    }\\n\\n\");\n}\n\n/// Get a list of files tracked by git from a given directory\nfn get_git_tracked_files(path: &Path, manifest_dir: &Path) -> (Vec<PathBuf>, PathBuf) {\n    if is_nix_build() {\n        // When building in Nix, we already know that there are no untracked files in our source tree,\n        // so we just list all of the files.\n        list_all_files(path, manifest_dir)\n    } else {\n        // When building outside of Nix, we invoke `git` to list all the tracked files.\n        get_git_tracked_files_via_cli(path, manifest_dir)\n    }\n}\n\nfn list_all_files(path: &Path, manifest_dir: &Path) -> (Vec<PathBuf>, PathBuf) {\n    let manifest_dir = manifest_dir.canonicalize().unwrap_or_else(|err| {\n        panic!(\n            \"Failed to canonicalize manifest_dir path {}: {err:#?}\",\n            manifest_dir.display()\n        )\n    });\n\n    let template_root_absolute = get_full_path_within_manifest_dir(path, &manifest_dir);\n\n    let repo_root = get_repo_root();\n\n    let mut files = Vec::new();\n    ls_recursively(&template_root_absolute, &repo_root, &mut files);\n\n    (files, make_repo_root_relative(&template_root_absolute, &repo_root))\n}\n\n/// Get all the paths of files within `root_dir`,\n/// transform them into paths relative to `repo_root`,\n/// and insert them into `out`.\nfn ls_recursively(root_dir: &Path, repo_root: &Path, out: &mut Vec<PathBuf>) {\n    for dir_ent in std::fs::read_dir(root_dir).unwrap_or_else(|err| {\n        panic!(\n            \"Failed to read_dir from template directory {}: {err:#?}\",\n            root_dir.display()\n        )\n    }) {\n        let dir_ent = dir_ent.unwrap_or_else(|err| {\n            panic!(\n                \"Got error during read_dir from template directory {}: {err:#?}\",\n                root_dir.display(),\n            )\n        });\n        let file_path = dir_ent.path();\n        let file_type = dir_ent.file_type().unwrap_or_else(|err| {\n            panic!(\n                \"Failed to get file_type for template file {}: {err:#?}\",\n                file_path.display(),\n            )\n        });\n        if file_type.is_dir() {\n            ls_recursively(&file_path, repo_root, out);\n        } else {\n            out.push(make_repo_root_relative(&file_path, repo_root));\n        }\n    }\n}\n\n/// Treat `relative_path` as a relative path within the repo root's templates directory\n/// and transform it into an absolute, canonical path.\nfn get_full_path_within_manifest_dir(relative_path: &Path, _manifest_dir: &Path) -> PathBuf {\n    let repo_root = get_repo_root();\n    let full_path = repo_root.join(\"templates\").join(relative_path);\n\n    full_path.canonicalize().unwrap_or_else(|e| {\n        panic!(\"Failed to canonicalize path {}: {}\", full_path.display(), e);\n    })\n}\n\n/// Transform `full_path` into a relative path within `repo_root`.\n///\n/// `full_path` and `repo_root` should both be canonical paths, as by [`Path::canonicalize`].\nfn make_repo_root_relative(full_path: &Path, repo_root: &Path) -> PathBuf {\n    full_path\n        .strip_prefix(repo_root)\n        .map(|p| p.to_path_buf())\n        .unwrap_or_else(|_| {\n            panic!(\n                \"Path {} is outside repo root {}\",\n                full_path.display(),\n                repo_root.display()\n            )\n        })\n}\n\nfn get_git_tracked_files_via_cli(path: &Path, manifest_dir: &Path) -> (Vec<PathBuf>, PathBuf) {\n    let repo_root = get_repo_root();\n    let repo_root = repo_root.canonicalize().unwrap_or_else(|err| {\n        panic!(\n            \"Failed to canonicalize repo_root path {}: {err:#?}\",\n            repo_root.display(),\n        )\n    });\n\n    let resolved_path = make_repo_root_relative(&get_full_path_within_manifest_dir(path, manifest_dir), &repo_root);\n\n    let output = Command::new(\"git\")\n        .args([\"ls-files\", resolved_path.to_str().unwrap()])\n        .current_dir(repo_root)\n        .output()\n        .expect(\"Failed to execute git ls-files\");\n\n    if !output.status.success() {\n        return (Vec::new(), resolved_path);\n    }\n\n    let stdout = String::from_utf8(output.stdout).unwrap();\n    let files: Vec<PathBuf> = stdout\n        .lines()\n        .filter(|line| !line.is_empty())\n        .map(PathBuf::from)\n        .collect();\n\n    (files, resolved_path)\n}\n\nfn get_repo_root() -> PathBuf {\n    let manifest_dir = get_manifest_dir();\n    // Cargo doesn't expose a way to get the workspace root, AFAICT (pgoldman 2025-10-31).\n    // We don't want to query git metadata for this, as that will break in Nix builds.\n    // We happen to know our own directory structure, so we can just walk the tree to get to the root.\n    let repo_root = manifest_dir.join(\"..\").join(\"..\");\n    repo_root.canonicalize().unwrap_or_else(|err| {\n        panic!(\n            \"Failed to canonicalize repo_root path {}: {err:#?}\",\n            repo_root.display()\n        )\n    })\n}\n\nfn extract_workspace_metadata(path: &Path) -> io::Result<(String, BTreeMap<String, String>)> {\n    let content = fs::read_to_string(path)?;\n    let parsed: Value = content\n        .parse()\n        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;\n\n    let table = parsed\n        .as_table()\n        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"workspace manifest is not a table\"))?;\n\n    let workspace = table\n        .get(\"workspace\")\n        .and_then(Value::as_table)\n        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"workspace section missing\"))?;\n\n    let edition = workspace\n        .get(\"package\")\n        .and_then(Value::as_table)\n        .and_then(|pkg| pkg.get(\"edition\"))\n        .and_then(Value::as_str)\n        .unwrap_or(\"2021\")\n        .to_string();\n\n    let mut versions = BTreeMap::new();\n    if let Some(deps) = workspace.get(\"dependencies\").and_then(Value::as_table) {\n        for (name, value) in deps {\n            let version_opt = match value {\n                Value::String(s) => Some(normalize_version(s)),\n                Value::Table(table) => table.get(\"version\").and_then(Value::as_str).map(normalize_version),\n                _ => None,\n            };\n\n            if let Some(version) = version_opt {\n                versions.insert(name.clone(), version);\n            }\n        }\n    }\n\n    Ok((edition, versions))\n}\n\nfn extract_ts_bindings_version(path: &Path) -> io::Result<String> {\n    let content = fs::read_to_string(path)?;\n    let parsed: serde_json::Value =\n        serde_json::from_str(&content).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;\n    parsed\n        .get(\"version\")\n        .and_then(serde_json::Value::as_str)\n        .map(|s| s.to_string())\n        .ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"Missing \\\"version\\\" field in TypeScript bindings package.json\",\n            )\n        })\n}\n\nfn normalize_version(version: &str) -> String {\n    version.trim().trim_start_matches('=').to_string()\n}\n\nfn write_if_changed(path: &Path, contents: &[u8]) -> io::Result<()> {\n    match fs::read(path) {\n        Ok(existing) if existing == contents => Ok(()),\n        _ => {\n            if let Some(parent) = path.parent() {\n                fs::create_dir_all(parent)?;\n            }\n            let mut file = fs::File::create(path)?;\n            file.write_all(contents)\n        }\n    }\n}\n\nfn copy_if_changed(src: &Path, dst: &Path) -> io::Result<()> {\n    let src_bytes = fs::read(src)?;\n    if let Ok(existing) = fs::read(dst)\n        && existing == src_bytes\n    {\n        return Ok(());\n    }\n\n    if let Some(parent) = dst.parent() {\n        fs::create_dir_all(parent)?;\n    }\n\n    let mut file = fs::File::create(dst)?;\n    file.write_all(&src_bytes)\n}\n"
  },
  {
    "path": "crates/cli/clippy.toml",
    "content": "# Overwrite the root's disallowed-macros,\n# as the CLI is allowed to use println and friends.\ndisallowed-macros = []\n"
  },
  {
    "path": "crates/cli/src/api.rs",
    "content": "use std::iter::Sum;\nuse std::ops::Add;\n\nuse reqwest::{header, Client, RequestBuilder};\nuse serde::Deserialize;\n\nuse spacetimedb_lib::db::raw_def::v9::RawModuleDefV9;\nuse spacetimedb_lib::de::serde::DeserializeWrapper;\nuse spacetimedb_lib::Identity;\n\nuse crate::util::{AuthHeader, ResponseExt};\n\nstatic APP_USER_AGENT: &str = concat!(env!(\"CARGO_PKG_NAME\"), \"/\", env!(\"CARGO_PKG_VERSION\"),);\n\n#[derive(Debug, Clone)]\npub struct Connection {\n    pub(crate) host: String,\n    pub(crate) database_identity: Identity,\n    pub(crate) database: String,\n    pub(crate) auth_header: AuthHeader,\n}\n\nimpl Connection {\n    pub fn db_uri(&self, endpoint: &str) -> String {\n        [\n            &self.host,\n            \"/v1/database/\",\n            &self.database_identity.to_hex(),\n            \"/\",\n            endpoint,\n        ]\n        .concat()\n    }\n}\n\npub fn build_client(con: &Connection) -> Client {\n    let mut builder = Client::builder().user_agent(APP_USER_AGENT);\n\n    if let Some(auth_header) = con.auth_header.to_header() {\n        let headers = http::HeaderMap::from_iter([(header::AUTHORIZATION, auth_header)]);\n\n        builder = builder.default_headers(headers);\n    }\n\n    builder.build().unwrap()\n}\n\npub struct ClientApi {\n    pub con: Connection,\n    client: Client,\n}\n\nimpl ClientApi {\n    pub fn new(con: Connection) -> Self {\n        let client = build_client(&con);\n        Self { con, client }\n    }\n\n    pub fn sql(&self) -> RequestBuilder {\n        self.client.post(self.con.db_uri(\"sql\"))\n    }\n\n    /// Reads the `ModuleDef` from the `schema` endpoint.\n    pub async fn module_def(&self) -> anyhow::Result<RawModuleDefV9> {\n        let res = self\n            .client\n            .get(self.con.db_uri(\"schema\"))\n            .query(&[(\"version\", \"9\")])\n            .send()\n            .await?;\n        let DeserializeWrapper(module_def) = res.json_or_error().await?;\n        Ok(module_def)\n    }\n\n    pub async fn call(&self, reducer_name: &str, arg_json: String) -> anyhow::Result<reqwest::Response> {\n        Ok(self\n            .client\n            .post(self.con.db_uri(\"call\") + \"/\" + reducer_name)\n            .header(http::header::CONTENT_TYPE, \"application/json\")\n            .body(arg_json)\n            .send()\n            .await?)\n    }\n}\n\npub(crate) type SqlStmtResult<'a> =\n    spacetimedb_client_api_messages::http::SqlStmtResult<&'a serde_json::value::RawValue>;\n\n#[derive(Debug, Clone, Deserialize, Default)]\npub struct StmtStats {\n    pub total_duration_micros: u64,\n    pub rows_inserted: u64,\n    pub rows_updated: u64,\n    pub rows_deleted: u64,\n    pub total_rows: usize,\n}\n\nimpl Sum<StmtStats> for StmtStats {\n    fn sum<I: Iterator<Item = StmtStats>>(iter: I) -> Self {\n        iter.fold(StmtStats::default(), Add::add)\n    }\n}\n\nimpl Add for StmtStats {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        Self {\n            total_duration_micros: self.total_duration_micros + rhs.total_duration_micros,\n            rows_inserted: self.rows_inserted + rhs.rows_inserted,\n            rows_deleted: self.rows_deleted + rhs.rows_deleted,\n            rows_updated: self.rows_updated + rhs.rows_updated,\n            total_rows: self.total_rows + rhs.total_rows,\n        }\n    }\n}\n\nimpl From<&SqlStmtResult<'_>> for StmtStats {\n    fn from(value: &SqlStmtResult<'_>) -> Self {\n        Self {\n            total_duration_micros: value.total_duration_micros,\n            rows_inserted: value.stats.rows_inserted,\n            rows_deleted: value.stats.rows_deleted,\n            rows_updated: value.stats.rows_updated,\n            total_rows: value.rows.len(),\n        }\n    }\n}\n\npub fn from_json_seed<'de, T: serde::de::DeserializeSeed<'de>>(\n    s: &'de str,\n    seed: T,\n) -> Result<T::Value, serde_json::Error> {\n    let mut de = serde_json::Deserializer::from_str(s);\n    let out = seed.deserialize(&mut de)?;\n    de.end()?;\n    Ok(out)\n}\n"
  },
  {
    "path": "crates/cli/src/common_args.rs",
    "content": "use clap::ArgAction::SetTrue;\nuse clap::{value_parser, Arg, ValueEnum};\n\n#[derive(Copy, Clone, Debug, ValueEnum, PartialEq)]\npub enum ClearMode {\n    Always,     // parses as \"always\"\n    OnConflict, // parses as \"on-conflict\"\n    Never,      // parses as \"never\"\n}\n\npub fn server() -> Arg {\n    Arg::new(\"server\")\n        .long(\"server\")\n        .short('s')\n        .help(\"The nickname, host name or URL of the server\")\n}\n\npub fn anonymous() -> Arg {\n    Arg::new(\"anon_identity\")\n        .long(\"anonymous\")\n        .action(SetTrue)\n        .help(\"Perform this action with an anonymous identity\")\n}\n\npub fn yes() -> Arg {\n    Arg::new(\"force\")\n        .long(\"yes\")\n        .short('y')\n        .action(SetTrue)\n        .help(\"Run non-interactively wherever possible. This will answer \\\"yes\\\" to almost all prompts, but will sometimes answer \\\"no\\\" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com).\")\n}\n\npub fn confirmed() -> Arg {\n    Arg::new(\"confirmed\")\n        .required(false)\n        .long(\"confirmed\")\n        .num_args(1)\n        .value_parser(value_parser!(bool))\n        .help(\"Instruct the server to deliver only updates of confirmed transactions\")\n}\n\npub fn clear_database() -> Arg {\n    Arg::new(\"clear-database\")\n        .long(\"delete-data\")\n        .alias(\"clear-database\")\n        .short('c')\n        .num_args(0..=1)\n        .value_parser(value_parser!(ClearMode))\n        // Because we have a default value for this flag, invocations can be ambiguous between\n        //passing a value to this flag, vs using the default value and passing an anonymous arg\n        // to the rest of the command. Adding `require_equals` resolves this ambiguity.\n        .require_equals(true)\n        .default_missing_value(\"always\")\n        .help(\n            \"When publishing to an existing database identity, first DESTROY all data associated with the module. With 'on-conflict': only when breaking schema changes occur.\"\n        )\n}\n"
  },
  {
    "path": "crates/cli/src/config.rs",
    "content": "use crate::errors::CliError;\nuse crate::util::{contains_protocol, host_or_url_to_host_and_protocol};\nuse anyhow::Context;\nuse jsonwebtoken::DecodingKey;\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_fs_utils::atomic_write;\nuse spacetimedb_paths::cli::CliTomlPath;\nuse std::io;\nuse std::path::Path;\nuse toml_edit::ArrayOfTables;\n\nconst DEFAULT_SERVER_KEY: &str = \"default_server\";\nconst WEB_SESSION_TOKEN_KEY: &str = \"web_session_token\";\nconst SPACETIMEDB_TOKEN_KEY: &str = \"spacetimedb_token\";\nconst SERVER_CONFIGS_KEY: &str = \"server_configs\";\nconst NICKNAME_KEY: &str = \"nickname\";\nconst HOST_KEY: &str = \"host\";\nconst PROTOCOL_KEY: &str = \"protocol\";\nconst ECDSA_PUBLIC_KEY: &str = \"ecdsa_public_key\";\n\n#[derive(Clone, Debug)]\npub struct ServerConfig {\n    pub nickname: Option<String>,\n    pub host: String,\n    pub protocol: String,\n    pub ecdsa_public_key: Option<String>,\n}\n\nimpl ServerConfig {\n    /// Generate a new [`Table`] representing this [`ServerConfig`].\n    pub fn as_table(&self) -> toml_edit::Table {\n        let mut table = toml_edit::Table::new();\n        Self::update_table(&mut table, self);\n        table\n    }\n\n    /// Update an existing [`Table`] with the values of a [`ServerConfig`].\n    pub fn update_table(edit: &mut toml_edit::Table, from: &ServerConfig) {\n        set_table_opt_value(edit, NICKNAME_KEY, from.nickname.as_deref());\n        set_table_opt_value(edit, HOST_KEY, Some(&from.host));\n        set_table_opt_value(edit, PROTOCOL_KEY, Some(&from.protocol));\n        set_table_opt_value(edit, ECDSA_PUBLIC_KEY, from.ecdsa_public_key.as_deref());\n    }\n\n    fn nick_or_host(&self) -> &str {\n        if let Some(nick) = &self.nickname {\n            nick\n        } else {\n            &self.host\n        }\n    }\n    pub fn get_host_url(&self) -> String {\n        format!(\"{}://{}\", self.protocol, self.host)\n    }\n\n    pub fn nick_or_host_or_url_is(&self, name: &str) -> bool {\n        self.nickname.as_deref() == Some(name) || self.host == name || {\n            let (host, _) = host_or_url_to_host_and_protocol(name);\n            self.host == host\n        }\n    }\n}\n\nfn read_table<'a>(table: &'a toml_edit::Table, key: &'a str) -> Result<Option<&'a ArrayOfTables>, CliError> {\n    if let Some(value) = table.get(key) {\n        if value.is_array_of_tables() {\n            Ok(value.as_array_of_tables())\n        } else {\n            Err(CliError::ConfigType {\n                key: key.to_string(),\n                kind: \"table array\",\n                found: Box::new(value.clone()),\n            })\n        }\n    } else {\n        Ok(None)\n    }\n}\n\nfn read_opt_str(table: &toml_edit::Table, key: &str) -> Result<Option<String>, CliError> {\n    if let Some(value) = table.get(key) {\n        if value.is_str() {\n            Ok(value.as_str().map(String::from))\n        } else {\n            Err(CliError::ConfigType {\n                key: key.to_string(),\n                kind: \"string\",\n                found: Box::new(value.clone()),\n            })\n        }\n    } else {\n        Ok(None)\n    }\n}\n\nfn read_str(table: &toml_edit::Table, key: &str) -> Result<String, CliError> {\n    read_opt_str(table, key)?.ok_or_else(|| CliError::Config { key: key.to_string() })\n}\n\nimpl TryFrom<&toml_edit::Table> for ServerConfig {\n    type Error = CliError;\n\n    fn try_from(table: &toml_edit::Table) -> Result<Self, Self::Error> {\n        let nickname = read_opt_str(table, NICKNAME_KEY)?;\n        let host = read_str(table, HOST_KEY)?;\n        let protocol = read_str(table, PROTOCOL_KEY)?;\n        let ecdsa_public_key = read_opt_str(table, ECDSA_PUBLIC_KEY)?;\n        Ok(ServerConfig {\n            nickname,\n            host,\n            protocol,\n            ecdsa_public_key,\n        })\n    }\n}\n\n// Any change here in the fields definition must be coordinated with Config::doc,\n// because the deserialize and serialize methods are manually implemented.\n#[derive(Default, Debug, Clone)]\npub struct RawConfig {\n    default_server: Option<String>,\n    server_configs: Vec<ServerConfig>,\n    // TODO: Consider how these tokens should look to be backwards-compatible with the future changes (e.g. we may want to allow users to `login` to switch between multiple accounts - what will we cache and where?)\n    // TODO: Move these IDs/tokens out of config so we're no longer storing sensitive tokens in a human-edited file.\n    web_session_token: Option<String>,\n    spacetimedb_token: Option<String>,\n}\n\n#[derive(Debug, Clone)]\npub struct Config {\n    home: RawConfig,\n    home_path: CliTomlPath,\n    /// The TOML document that was parsed to create `home`.\n    ///\n    /// We need to keep it to preserve comments and formatting when saving the config.\n    doc: toml_edit::DocumentMut,\n}\n\nconst NO_DEFAULT_SERVER_ERROR_MESSAGE: &str = \"No default server configuration.\nSet an existing server as the default with:\n\\tspacetime server set-default <server>\nOr add a new server which will become the default:\n\\tspacetime server add {server} <url> --default\";\n\nfn no_such_server_error(server: &str) -> anyhow::Error {\n    anyhow::anyhow!(\n        \"No such saved server configuration: {server}\nAdd a new server configuration with:\n\\tspacetime server add {server} --url <url>\",\n    )\n}\n\nfn hanging_default_server_context(server: &str) -> String {\n    format!(\"Default server does not refer to a saved server configuration: {server}\")\n}\n\nimpl RawConfig {\n    fn new_with_localhost() -> Self {\n        let local = ServerConfig {\n            host: \"127.0.0.1:3000\".to_string(),\n            protocol: \"http\".to_string(),\n            nickname: Some(\"local\".to_string()),\n            ecdsa_public_key: None,\n        };\n        let maincloud = ServerConfig {\n            host: \"maincloud.spacetimedb.com\".to_string(),\n            protocol: \"https\".to_string(),\n            nickname: Some(\"maincloud\".to_string()),\n            ecdsa_public_key: None,\n        };\n        RawConfig {\n            default_server: maincloud.nickname.clone(),\n            server_configs: vec![maincloud, local],\n            web_session_token: None,\n            spacetimedb_token: None,\n        }\n    }\n\n    fn find_server(&self, name_or_host: &str) -> anyhow::Result<&ServerConfig> {\n        for cfg in &self.server_configs {\n            if cfg.nickname.as_deref() == Some(name_or_host) || cfg.host == name_or_host {\n                return Ok(cfg);\n            }\n        }\n        Err(no_such_server_error(name_or_host))\n    }\n\n    fn find_server_mut(&mut self, name_or_host: &str) -> anyhow::Result<&mut ServerConfig> {\n        for cfg in &mut self.server_configs {\n            if cfg.nickname.as_deref() == Some(name_or_host) || cfg.host == name_or_host {\n                return Ok(cfg);\n            }\n        }\n        Err(no_such_server_error(name_or_host))\n    }\n\n    fn default_server(&self) -> anyhow::Result<&ServerConfig> {\n        if let Some(default_server) = self.default_server.as_ref() {\n            self.find_server(default_server)\n                .with_context(|| hanging_default_server_context(default_server))\n        } else {\n            Err(anyhow::anyhow!(NO_DEFAULT_SERVER_ERROR_MESSAGE))\n        }\n    }\n\n    fn default_server_mut(&mut self) -> anyhow::Result<&mut ServerConfig> {\n        if let Some(default_server) = self.default_server.as_ref() {\n            let default = default_server.to_string();\n            self.find_server_mut(&default)\n                .with_context(|| hanging_default_server_context(&default))\n        } else {\n            Err(anyhow::anyhow!(NO_DEFAULT_SERVER_ERROR_MESSAGE))\n        }\n    }\n\n    fn add_server(\n        &mut self,\n        host: String,\n        protocol: String,\n        ecdsa_public_key: Option<String>,\n        nickname: Option<String>,\n    ) -> anyhow::Result<()> {\n        if let Some(nickname) = &nickname\n            && let Ok(cfg) = self.find_server(nickname)\n        {\n            anyhow::bail!(\n                \"Server nickname {} already in use: {}://{}\",\n                nickname,\n                cfg.protocol,\n                cfg.host,\n            );\n        }\n\n        if let Ok(cfg) = self.find_server(&host) {\n            if let Some(nick) = &cfg.nickname\n                && nick == &host\n            {\n                anyhow::bail!(\"Server host name is ambiguous with existing server nickname: {nick}\");\n            }\n            anyhow::bail!(\"Server already configured for host: {host}\");\n        }\n\n        self.server_configs.push(ServerConfig {\n            nickname,\n            host,\n            protocol,\n            ecdsa_public_key,\n        });\n        Ok(())\n    }\n\n    fn host(&self, server: &str) -> anyhow::Result<&str> {\n        self.find_server(server)\n            .map(|cfg| cfg.host.as_ref())\n            .with_context(|| format!(\"Cannot find hostname for unknown server: {server}\"))\n    }\n\n    fn default_host(&self) -> anyhow::Result<&str> {\n        self.default_server()\n            .with_context(|| \"Cannot find hostname for default server\")\n            .map(|cfg| cfg.host.as_ref())\n    }\n\n    fn protocol(&self, server: &str) -> anyhow::Result<&str> {\n        self.find_server(server).map(|cfg| cfg.protocol.as_ref())\n    }\n\n    fn default_protocol(&self) -> anyhow::Result<&str> {\n        self.default_server()\n            .with_context(|| \"Cannot find protocol for default server\")\n            .map(|cfg| cfg.protocol.as_ref())\n    }\n\n    fn set_default_server(&mut self, server: &str) -> anyhow::Result<()> {\n        // Check that such a server exists before setting the default.\n        self.find_server(server)\n            .with_context(|| format!(\"Cannot set default server to unknown server {server}\"))?;\n\n        self.default_server = Some(server.to_string());\n\n        Ok(())\n    }\n\n    /// Implements `spacetime server remove`.\n    fn remove_server(&mut self, server: &str) -> anyhow::Result<()> {\n        // Have to find the server config manually instead of doing `find_server_mut`\n        // because we need to mutably borrow multiple components of `self`.\n        if let Some(idx) = self\n            .server_configs\n            .iter()\n            .position(|cfg| cfg.nick_or_host_or_url_is(server))\n        {\n            // Actually remove the config.\n            let cfg = self.server_configs.remove(idx);\n\n            // If we're removing the default server,\n            // unset the default server.\n            if let Some(default_server) = &self.default_server\n                && cfg.nick_or_host_or_url_is(default_server)\n            {\n                self.default_server = None;\n            }\n\n            return Ok(());\n        }\n        Err(no_such_server_error(server))\n    }\n\n    /// Return the ECDSA public key in PEM format for the server named by `server`.\n    ///\n    /// Returns an `Err` if there is no such server configuration.\n    /// Returns `None` if the server configuration exists, but does not have a fingerprint saved.\n    fn server_fingerprint(&self, server: &str) -> anyhow::Result<Option<&str>> {\n        self.find_server(server)\n            .with_context(|| {\n                format!(\n                    \"No saved fingerprint for server: {server}\nFetch the server's fingerprint with:\n\\tspacetime server fingerprint -s {server}\"\n                )\n            })\n            .map(|cfg| cfg.ecdsa_public_key.as_deref())\n    }\n\n    /// Return the ECDSA public key in PEM format for the default server.\n    ///\n    /// Returns an `Err` if there is no default server configuration.\n    /// Returns `None` if the server configuration exists, but does not have a fingerprint saved.\n    fn default_server_fingerprint(&self) -> anyhow::Result<Option<&str>> {\n        if let Some(server) = &self.default_server {\n            self.server_fingerprint(server)\n        } else {\n            Err(anyhow::anyhow!(NO_DEFAULT_SERVER_ERROR_MESSAGE))\n        }\n    }\n\n    /// Store the fingerprint for the server named `server`.\n    ///\n    /// Returns an `Err` if no such server configuration exists.\n    /// On success, any existing fingerprint is dropped.\n    fn set_server_fingerprint(&mut self, server: &str, ecdsa_public_key: String) -> anyhow::Result<()> {\n        let cfg = self.find_server_mut(server)?;\n        cfg.ecdsa_public_key = Some(ecdsa_public_key);\n        Ok(())\n    }\n\n    /// Store the fingerprint for the default server.\n    ///\n    /// Returns an `Err` if no default server configuration exists.\n    /// On success, any existing fingerprint is dropped.\n    fn set_default_server_fingerprint(&mut self, ecdsa_public_key: String) -> anyhow::Result<()> {\n        let cfg = self.default_server_mut()?;\n        cfg.ecdsa_public_key = Some(ecdsa_public_key);\n        Ok(())\n    }\n\n    /// Edit a saved server configuration.\n    ///\n    /// Implements `spacetime server edit`.\n    ///\n    /// Returns `Err` if no such server exists.\n    /// On success, returns `(old_nickname, old_host, hold_protocol)`,\n    /// with `Some` for each field that was changed.\n    pub fn edit_server(\n        &mut self,\n        server: &str,\n        new_nickname: Option<&str>,\n        new_host: Option<&str>,\n        new_protocol: Option<&str>,\n    ) -> anyhow::Result<(Option<String>, Option<String>, Option<String>)> {\n        // Check if the new nickname or host name would introduce ambiguities between\n        // server configurations.\n        if let Some(new_nick) = new_nickname\n            && let Ok(other_server) = self.find_server(new_nick)\n        {\n            anyhow::bail!(\n                \"Nickname {} conflicts with saved configuration for server {}: {}://{}\",\n                new_nick,\n                other_server.nick_or_host(),\n                other_server.protocol,\n                other_server.host\n            );\n        }\n        if let Some(new_host) = new_host\n            && let Ok(other_server) = self.find_server(new_host)\n        {\n            anyhow::bail!(\n                \"Host {} conflicts with saved configuration for server {}: {}://{}\",\n                new_host,\n                other_server.nick_or_host(),\n                other_server.protocol,\n                other_server.host\n            );\n        }\n\n        let cfg = self.find_server_mut(server)?;\n        let old_nickname = if let Some(new_nickname) = new_nickname {\n            cfg.nickname.replace(new_nickname.to_string())\n        } else {\n            None\n        };\n        let old_host = if let Some(new_host) = new_host {\n            Some(std::mem::replace(&mut cfg.host, new_host.to_string()))\n        } else {\n            None\n        };\n        let old_protocol = if let Some(new_protocol) = new_protocol {\n            Some(std::mem::replace(&mut cfg.protocol, new_protocol.to_string()))\n        } else {\n            None\n        };\n\n        // If the server we edited was the default server,\n        // and we changed the identifier stored in the `default_server` field,\n        // update that field.\n        if let Some(default_server) = &mut self.default_server {\n            if let Some(old_host) = &old_host {\n                if default_server == old_host {\n                    *default_server = new_host.unwrap().to_string();\n                }\n            } else if let Some(old_nick) = &old_nickname\n                && default_server == old_nick\n            {\n                *default_server = new_nickname.unwrap().to_string();\n            }\n        }\n\n        Ok((old_nickname, old_host, old_protocol))\n    }\n\n    pub fn delete_server_fingerprint(&mut self, server: &str) -> anyhow::Result<()> {\n        let cfg = self.find_server_mut(server)?;\n        cfg.ecdsa_public_key = None;\n        Ok(())\n    }\n\n    pub fn delete_default_server_fingerprint(&mut self) -> anyhow::Result<()> {\n        let cfg = self.default_server_mut()?;\n        cfg.ecdsa_public_key = None;\n        Ok(())\n    }\n\n    pub fn set_web_session_token(&mut self, token: String) {\n        self.web_session_token = Some(token);\n    }\n\n    pub fn set_spacetimedb_token(&mut self, token: String) {\n        self.spacetimedb_token = Some(token);\n    }\n\n    pub fn clear_login_tokens(&mut self) {\n        self.web_session_token = None;\n        self.spacetimedb_token = None;\n    }\n}\n\nimpl TryFrom<&toml_edit::DocumentMut> for RawConfig {\n    type Error = CliError;\n\n    fn try_from(value: &toml_edit::DocumentMut) -> Result<Self, Self::Error> {\n        let default_server = read_opt_str(value, DEFAULT_SERVER_KEY)?;\n        let web_session_token = read_opt_str(value, WEB_SESSION_TOKEN_KEY)?;\n        let spacetimedb_token = read_opt_str(value, SPACETIMEDB_TOKEN_KEY)?;\n\n        let mut server_configs = Vec::new();\n        if let Some(arr) = read_table(value, SERVER_CONFIGS_KEY)? {\n            for table in arr {\n                server_configs.push(ServerConfig::try_from(table)?);\n            }\n        }\n\n        Ok(RawConfig {\n            default_server,\n            server_configs,\n            web_session_token,\n            spacetimedb_token,\n        })\n    }\n}\n\nimpl Config {\n    pub fn default_server_name(&self) -> Option<&str> {\n        self.home.default_server.as_deref()\n    }\n\n    /// Add a `ServerConfig` to the home configuration.\n    ///\n    /// Returns an `Err` on name conflict,\n    /// i.e. if a `ServerConfig` with the `nickname` or `host` already exists.\n    ///\n    /// Callers should call `Config::save` afterwards\n    /// to ensure modifications are persisted to disk.\n    pub fn add_server(\n        &mut self,\n        host: String,\n        protocol: String,\n        ecdsa_public_key: Option<String>,\n        nickname: Option<String>,\n    ) -> anyhow::Result<()> {\n        self.home.add_server(host, protocol, ecdsa_public_key, nickname)\n    }\n\n    /// Set the default server in the home configuration.\n    ///\n    /// Returns an `Err` if `nickname_or_host_or_url`\n    /// does not refer to an existing `ServerConfig`\n    /// in the home configuration.\n    ///\n    /// Callers should call `Config::save` afterwards\n    /// to ensure modifications are persisted to disk.\n    pub fn set_default_server(&mut self, nickname_or_host_or_url: &str) -> anyhow::Result<()> {\n        let (host, _) = host_or_url_to_host_and_protocol(nickname_or_host_or_url);\n        self.home.set_default_server(host)\n    }\n\n    /// Delete a `ServerConfig` from the home configuration.\n    ///\n    /// Returns an `Err` if `nickname_or_host_or_url`\n    /// does not refer to an existing `ServerConfig`\n    /// in the home configuration.\n    ///\n    /// Callers should call `Config::save` afterwards\n    /// to ensure modifications are persisted to disk.\n    pub fn remove_server(&mut self, nickname_or_host_or_url: &str) -> anyhow::Result<()> {\n        let (host, _) = host_or_url_to_host_and_protocol(nickname_or_host_or_url);\n        self.home.remove_server(host)\n    }\n\n    /// Get a URL for the specified `server`.\n    ///\n    /// Returns the URL of the default server if `server` is `None`.\n    ///\n    /// If `server` is `Some` and is a complete URL,\n    /// including protocol and hostname,\n    /// returns that URL without accessing the configuration.\n    ///\n    /// Returns an `Err` if:\n    /// - `server` is `Some`, but not a complete URL,\n    ///   and the supplied name does not refer to any server\n    ///   in the configuration.\n    /// - `server` is `None`, but the configuration does not have a default server.\n    pub fn get_host_url(&self, server: Option<&str>) -> anyhow::Result<String> {\n        Ok(format!(\"{}://{}\", self.protocol(server)?, self.host(server)?))\n    }\n\n    /// Get the hostname of the specified `server`.\n    ///\n    /// Returns the hostname of the default server if `server` is `None`.\n    ///\n    /// If `server` is `Some` and is a complete URL,\n    /// including protocol and hostname,\n    /// returns that hostname without accessing the configuration.\n    ///\n    /// Returns an `Err` if:\n    /// - `server` is `Some`, but not a complete URL,\n    ///   and the supplied name does not refer to any server\n    ///   in the configuration.\n    /// - `server` is `None`, but the configuration does not\n    ///   have a default server.\n    pub fn host<'a>(&'a self, server: Option<&'a str>) -> anyhow::Result<&'a str> {\n        if let Some(server) = server {\n            if contains_protocol(server) {\n                Ok(host_or_url_to_host_and_protocol(server).0)\n            } else {\n                self.home.host(server)\n            }\n        } else {\n            self.home.default_host()\n        }\n    }\n\n    /// Get the protocol of the specified `server`, either `\"http\"` or `\"https\"`.\n    ///\n    /// Returns the protocol of the default server if `server` is `None`.\n    ///\n    /// If `server` is `Some` and is a complete URL,\n    /// including protocol and hostname,\n    /// returns that protocol without accessing the configuration.\n    /// In that case, the protocol is not validated.\n    ///\n    /// Returns an `Err` if:\n    /// - `server` is `Some`, but not a complete URL,\n    ///   and the supplied name does not refer to any server\n    ///   in the configuration.\n    /// - `server` is `None`, but the configuration does not have a default server.\n    pub fn protocol<'a>(&'a self, server: Option<&'a str>) -> anyhow::Result<&'a str> {\n        if let Some(server) = server {\n            if contains_protocol(server) {\n                Ok(host_or_url_to_host_and_protocol(server).1.unwrap())\n            } else {\n                self.home.protocol(server)\n            }\n        } else {\n            self.home.default_protocol()\n        }\n    }\n\n    pub fn server_configs(&self) -> &[ServerConfig] {\n        &self.home.server_configs\n    }\n\n    /// Parse [`RawConfig`] from a TOML file at the given path, returning `None` if the file does not exist.\n    ///\n    /// **NOTE**: Comments and formatting in the file will be preserved.\n    fn parse_config(path: &Path) -> anyhow::Result<Option<(toml_edit::DocumentMut, RawConfig)>> {\n        match std::fs::read_to_string(path) {\n            Ok(contents) => {\n                let doc = contents.parse::<toml_edit::DocumentMut>()?;\n                let config = RawConfig::try_from(&doc)?;\n                Ok(Some((doc, config)))\n            }\n            Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),\n            Err(e) => Err(e.into()),\n        }\n    }\n\n    pub fn load(home_path: CliTomlPath) -> anyhow::Result<Self> {\n        let home = Self::parse_config(home_path.as_ref())\n            .with_context(|| format!(\"config file {} is invalid\", home_path.display()))?;\n        Ok(match home {\n            Some((doc, home)) => Self { home, home_path, doc },\n            None => {\n                let config = Self {\n                    home: RawConfig::new_with_localhost(),\n                    home_path,\n                    doc: Default::default(),\n                };\n                config.save();\n                config\n            }\n        })\n    }\n\n    #[doc(hidden)]\n    /// Used in tests.\n    pub fn new_with_localhost(home_path: CliTomlPath) -> Self {\n        Self {\n            home: RawConfig::new_with_localhost(),\n            home_path,\n            doc: Default::default(),\n        }\n    }\n\n    /// Returns a preserving copy of [`Config`].\n    fn doc(&self) -> toml_edit::DocumentMut {\n        let mut doc = self.doc.clone();\n\n        let mut set_value = |key: &str, value: Option<&str>| {\n            set_opt_value(&mut doc, key, value);\n        };\n        // Intentionally use a destructuring assignment in case the fields change...\n        let RawConfig {\n            default_server,\n            server_configs: old_server_configs,\n            web_session_token,\n            spacetimedb_token,\n        } = &self.home;\n\n        set_value(DEFAULT_SERVER_KEY, default_server.as_deref());\n        set_value(WEB_SESSION_TOKEN_KEY, web_session_token.as_deref());\n        set_value(SPACETIMEDB_TOKEN_KEY, spacetimedb_token.as_deref());\n\n        // Short-circuit if there are no servers.\n        if old_server_configs.is_empty() {\n            doc.remove(SERVER_CONFIGS_KEY);\n            return doc;\n        }\n        // ... or if there are no server_configs to edit.\n        let new_server_configs = if let Some(cfg) = doc\n            .get_mut(SERVER_CONFIGS_KEY)\n            .and_then(toml_edit::Item::as_array_of_tables_mut)\n        {\n            cfg\n        } else {\n            doc[SERVER_CONFIGS_KEY] =\n                toml_edit::Item::ArrayOfTables(old_server_configs.iter().map(ServerConfig::as_table).collect());\n            return doc;\n        };\n\n        let mut new_configs = self\n            .home\n            .server_configs\n            .iter()\n            .map(|cfg| (cfg.nick_or_host(), cfg))\n            .collect::<HashMap<_, _>>();\n\n        // Update the existing servers, and remove deleted servers.\n        // We'll add new servers later.\n        // We do this somewhat elaborate dance rather than just overwriting the config\n        // in order to preserve the order and formatting of pre-existing server configs in the file.\n        let mut new_vec = Vec::with_capacity(new_server_configs.len());\n        for old_config in new_server_configs.iter_mut() {\n            let nick_or_host = old_config\n                .get(NICKNAME_KEY)\n                .or_else(|| old_config.get(HOST_KEY))\n                .and_then(|v| v.as_str())\n                .unwrap();\n\n            if let Some(new_config) = new_configs.remove(nick_or_host) {\n                ServerConfig::update_table(old_config, new_config);\n                new_vec.push(old_config.clone());\n            }\n        }\n\n        // Add the new servers. This appends them to the end of the config file,\n        // after the (preserved) existing configs.\n        new_vec.extend(new_configs.values().cloned().map(ServerConfig::as_table));\n        *new_server_configs = toml_edit::ArrayOfTables::from_iter(new_vec);\n\n        doc\n    }\n\n    pub fn save(&self) {\n        let home_path = &self.home_path;\n        // If the `home_path` is in a directory, ensure it exists.\n        home_path.create_parent().unwrap();\n\n        let config = self.doc().to_string();\n\n        eprintln!(\"Saving config to {}.\", home_path.display());\n        // TODO: We currently have a race condition if multiple processes are modifying the config.\n        // If process X and process Y read the config, each make independent changes, and then save\n        // the config, the first writer will have its changes clobbered by the second writer.\n        //\n        // We used to use `Lockfile` to prevent this from happening, but we had other issues with\n        // that approach (see https://github.com/clockworklabs/SpacetimeDB/issues/1339, and the\n        // TODO in `lockfile.rs`).\n        //\n        // We should address this issue, but we currently don't expect it to arise very frequently\n        // (see https://github.com/clockworklabs/SpacetimeDB/pull/1341#issuecomment-2150857432).\n        if let Err(e) = atomic_write(&home_path.0, config) {\n            eprintln!(\"Could not save config file: {e}\")\n        }\n    }\n\n    pub fn server_decoding_key(&self, server: Option<&str>) -> anyhow::Result<DecodingKey> {\n        self.server_fingerprint(server).and_then(|fing| {\n            if let Some(fing) = fing {\n                DecodingKey::from_ec_pem(fing.as_bytes()).with_context(|| {\n                    format!(\n                        \"Unable to parse invalid saved server fingerprint as ECDSA public key.\nUpdate the server's fingerprint with:\n\\tspacetime server fingerprint {}\",\n                        server.unwrap_or(\"\")\n                    )\n                })\n            } else {\n                Err(anyhow::anyhow!(\n                    \"No fingerprint saved for server: {}\",\n                    self.server_nick_or_host(server)?,\n                ))\n            }\n        })\n    }\n\n    pub fn server_nick_or_host<'a>(&'a self, server: Option<&'a str>) -> anyhow::Result<&'a str> {\n        if let Some(server) = server {\n            let (host, _) = host_or_url_to_host_and_protocol(server);\n            Ok(host)\n        } else {\n            self.home.default_server().map(ServerConfig::nick_or_host)\n        }\n    }\n\n    pub fn server_fingerprint(&self, server: Option<&str>) -> anyhow::Result<Option<&str>> {\n        if let Some(server) = server {\n            let (host, _) = host_or_url_to_host_and_protocol(server);\n            self.home.server_fingerprint(host)\n        } else {\n            self.home.default_server_fingerprint()\n        }\n    }\n\n    pub fn set_server_fingerprint(&mut self, server: Option<&str>, new_fingerprint: String) -> anyhow::Result<()> {\n        if let Some(server) = server {\n            let (host, _) = host_or_url_to_host_and_protocol(server);\n            self.home.set_server_fingerprint(host, new_fingerprint)\n        } else {\n            self.home.set_default_server_fingerprint(new_fingerprint)\n        }\n    }\n\n    pub fn edit_server(\n        &mut self,\n        server: &str,\n        new_nickname: Option<&str>,\n        new_host: Option<&str>,\n        new_protocol: Option<&str>,\n    ) -> anyhow::Result<(Option<String>, Option<String>, Option<String>)> {\n        let (host, _) = host_or_url_to_host_and_protocol(server);\n        self.home.edit_server(host, new_nickname, new_host, new_protocol)\n    }\n\n    pub fn delete_server_fingerprint(&mut self, server: Option<&str>) -> anyhow::Result<()> {\n        if let Some(server) = server {\n            let (host, _) = host_or_url_to_host_and_protocol(server);\n            self.home.delete_server_fingerprint(host)\n        } else {\n            self.home.delete_default_server_fingerprint()\n        }\n    }\n\n    pub fn set_web_session_token(&mut self, token: String) {\n        self.home.set_web_session_token(token);\n    }\n\n    pub fn set_spacetimedb_token(&mut self, token: String) {\n        self.home.set_spacetimedb_token(token);\n    }\n\n    pub fn clear_login_tokens(&mut self) {\n        self.home.clear_login_tokens();\n    }\n\n    pub fn web_session_token(&self) -> Option<&String> {\n        self.home.web_session_token.as_ref()\n    }\n\n    pub fn spacetimedb_token(&self) -> Option<&String> {\n        self.home.spacetimedb_token.as_ref()\n    }\n}\n\n/// Update the value of a key in a `TOML` document, preserving the formatting and comments of the original value.\n///\n/// ie:\n///\n/// ```toml;no_run\n/// # Moving key = value to key = new_value\n/// old = \"value\" # Comment\n/// new = \"new_value\" # Comment\n/// ```\nfn copy_value_with_decor(old_value: Option<&toml_edit::Item>, new_value: &str) -> toml_edit::Item {\n    match old_value {\n        Some(toml_edit::Item::Value(toml_edit::Value::String(old_value))) => {\n            // Creates a new `toml_edit::Value` with the same formatting as the old value.\n            let mut new = toml_edit::Value::String(toml_edit::Formatted::new(new_value.to_string()));\n            let decor = new.decor_mut();\n            // Copy the comments and formatting from the old value.\n            *decor = old_value.decor().clone();\n            new.into()\n        }\n        _ => new_value.into(),\n    }\n}\n\n/// Set the value of a key in a `TOML` document, removing the key if the value is `None`.\n///\n/// **NOTE**: This function will preserve the formatting and comments of the original value.\npub fn set_opt_value(doc: &mut toml_edit::DocumentMut, key: &str, value: Option<&str>) {\n    let old_value = doc.get(key);\n    if let Some(new) = value {\n        doc[key] = copy_value_with_decor(old_value, new);\n    } else {\n        doc.remove(key);\n    }\n}\n\n/// Set the value of a key in a `TOML` table, removing the key if the value is `None`.\n///\n/// **NOTE**: This function will preserve the formatting and comments of the original value.\npub fn set_table_opt_value(table: &mut toml_edit::Table, key: &str, value: Option<&str>) {\n    let old_value = table.get(key);\n    if let Some(new) = value {\n        table[key] = copy_value_with_decor(old_value, new);\n    } else {\n        table.remove(key);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_lib::error::ResultTest;\n    use spacetimedb_paths::cli::CliTomlPath;\n    use spacetimedb_paths::FromPathUnchecked;\n    use std::fs;\n    use std::thread;\n\n    const CONFIG_FULL: &str = r#\"default_server = \"local\"\nweb_session_token = \"web_session\"\nspacetimedb_token = \"26ac38857c2bd6c5b60ec557ecd4f9add918fef577dc92c01ca96ff08af5b84d\"\n\n# comment on table\n[[server_configs]]\nnickname = \"local\"\nhost = \"127.0.0.1:3000\"\nprotocol = \"http\"\n\n# comment on table\n[[server_configs]]\n# comment on table\nnickname = \"testnet\" # Comment nickname\nhost = \"testnet.spacetimedb.com\" # Comment host\n# Comment protocol\nprotocol = \"https\"\n\n# Comment end\n\"#;\n    const CONFIG_FULL_NO_COMMENT: &str = r#\"default_server = \"local\"\nweb_session_token = \"web_session\"\nspacetimedb_token = \"26ac38857c2bd6c5b60ec557ecd4f9add918fef577dc92c01ca96ff08af5b84d\"\n\n[[server_configs]]\nnickname = \"local\"\nhost = \"127.0.0.1:3000\"\nprotocol = \"http\"\n\n[[server_configs]]\nnickname = \"testnet\"\nhost = \"testnet.spacetimedb.com\"\nprotocol = \"https\"\n\n# Comment end\n\"#;\n    const CONFIG_CHANGE_SERVER: &str = r#\"default_server = \"local\"\nweb_session_token = \"web_session\"\nspacetimedb_token = \"26ac38857c2bd6c5b60ec557ecd4f9add918fef577dc92c01ca96ff08af5b84d\"\n\n# comment on table\n[[server_configs]]\n# comment on table\nnickname = \"testnet\" # Comment nickname\nhost = \"prod.spacetimedb.com\" # Comment host\n# Comment protocol\nprotocol = \"https\"\n\n# Comment end\n\"#;\n    const CONFIG_EMPTY: &str = r#\"\n# Comment end\n\"#;\n    const CONFIG_INVALID_START: &str = r#\"\nthis=\"not a valid key\"\n\"#;\n    const CONFIG_INVALID_END: &str = r#\"\nthis=\"not a valid key\"\ndefault_server = \"local\"\n\"#;\n\n    fn check_invalid(contents: &str, expect: CliError) -> ResultTest<()> {\n        let doc = contents.parse::<toml_edit::DocumentMut>()?;\n        let err = RawConfig::try_from(&doc);\n        assert_eq!(err.unwrap_err().to_string(), expect.to_string());\n\n        Ok(())\n    }\n\n    fn check_config<F>(input: &str, output: &str, f: F) -> ResultTest<()>\n    where\n        F: FnOnce(&mut Config) -> ResultTest<()>,\n    {\n        let tmp = tempfile::tempdir()?;\n        let config_path = CliTomlPath::from_path_unchecked(tmp.path().join(\"config.toml\"));\n\n        fs::write(&config_path, input)?;\n\n        let mut config = Config::load(config_path.clone()).unwrap();\n        f(&mut config)?;\n        config.save();\n\n        let contents = fs::read_to_string(&config_path)?;\n\n        assert_eq!(contents, output);\n\n        Ok(())\n    }\n\n    // Test editing the config file.\n    #[test]\n    fn test_config_edits() -> ResultTest<()> {\n        check_config(CONFIG_FULL, CONFIG_EMPTY, |config| {\n            config.home.default_server = None;\n            config.home.server_configs.clear();\n            config.home.spacetimedb_token = None;\n            config.home.web_session_token = None;\n\n            Ok(())\n        })?;\n\n        check_config(CONFIG_FULL, CONFIG_CHANGE_SERVER, |config| {\n            config.home.server_configs.remove(0);\n            config.home.server_configs[0].host = \"prod.spacetimedb.com\".to_string();\n            Ok(())\n        })?;\n\n        Ok(())\n    }\n\n    // Test adding to the config file.\n    #[test]\n    fn test_config_adds() -> ResultTest<()> {\n        check_config(CONFIG_FULL, CONFIG_FULL, |_| Ok(()))?;\n        check_config(CONFIG_EMPTY, CONFIG_EMPTY, |_| Ok(()))?;\n\n        check_config(CONFIG_EMPTY, CONFIG_FULL_NO_COMMENT, |config| {\n            config.home.default_server = Some(\"local\".to_string());\n            config.home.server_configs = vec![\n                ServerConfig {\n                    nickname: Some(\"local\".to_string()),\n                    host: \"127.0.0.1:3000\".to_string(),\n                    protocol: \"http\".to_string(),\n                    ecdsa_public_key: None,\n                },\n                ServerConfig {\n                    nickname: Some(\"testnet\".to_string()),\n                    host: \"testnet.spacetimedb.com\".to_string(),\n                    protocol: \"https\".to_string(),\n                    ecdsa_public_key: None,\n                },\n            ];\n            config.home.spacetimedb_token =\n                Some(\"26ac38857c2bd6c5b60ec557ecd4f9add918fef577dc92c01ca96ff08af5b84d\".to_string());\n            config.home.web_session_token = Some(\"web_session\".to_string());\n\n            Ok(())\n        })?;\n\n        Ok(())\n    }\n\n    // Test that modify a config file with wrong extra configs is fine\n    #[test]\n    fn test_config_invalid_mut() -> ResultTest<()> {\n        check_config(CONFIG_INVALID_START, CONFIG_INVALID_END, |config| {\n            config.home.default_server = Some(\"local\".to_string());\n            Ok(())\n        })?;\n\n        Ok(())\n    }\n\n    // Test invalid types in the config file.\n    #[test]\n    fn test_config_invalid() -> ResultTest<()> {\n        check_invalid(\n            r#\"default_server =1\"#,\n            CliError::ConfigType {\n                key: \"default_server\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n        check_invalid(\n            r#\"web_session_token =1\"#,\n            CliError::ConfigType {\n                key: \"web_session_token\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n        check_invalid(\n            r#\"spacetimedb_token =1\"#,\n            CliError::ConfigType {\n                key: \"spacetimedb_token\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n        check_invalid(\n            r#\"\n[server_configs]\n\"#,\n            CliError::ConfigType {\n                key: \"server_configs\".to_string(),\n                kind: \"table array\",\n                found: Box::new(toml_edit::table()),\n            },\n        )?;\n        check_invalid(\n            r#\"\n[[server_configs]]\nnickname =1\n\"#,\n            CliError::ConfigType {\n                key: \"nickname\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n        check_invalid(\n            r#\"\n[[server_configs]]\nhost =1\n\"#,\n            CliError::ConfigType {\n                key: \"host\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n\n        check_invalid(\n            r#\"\n[[server_configs]]\nhost = \"127.0.0.1:3000\"\nprotocol =1\n\"#,\n            CliError::ConfigType {\n                key: \"protocol\".to_string(),\n                kind: \"string\",\n                found: Box::new(toml_edit::value(1)),\n            },\n        )?;\n        Ok(())\n    }\n\n    // Test editing the config file concurrently don't corrupt the file.\n    //\n    // The test only confirms that the file is not corrupted, not that the changes are deterministic.\n    #[test]\n    fn test_config_concurrent() -> ResultTest<()> {\n        let tmp = tempfile::tempdir()?;\n        let config_path = CliTomlPath::from_path_unchecked(tmp.path().join(\"config.toml\"));\n\n        let mut local = Config::load(config_path.clone()).unwrap();\n        let mut testnet = local.clone();\n        let mut maincloud = local.clone();\n\n        local.home.default_server = Some(\"local\".to_string());\n        testnet.home.default_server = Some(\"testnet\".to_string());\n        maincloud.home.default_server = Some(\"maincloud\".to_string());\n\n        let mut handles = vec![];\n        let total_threads: usize = 8;\n\n        // Writer threads\n        for i in 0..total_threads {\n            let local = local.clone();\n            let testnet = testnet.clone();\n            handles.push(thread::spawn(move || {\n                if i % 2 == 0 {\n                    local.save();\n                    local\n                } else {\n                    testnet.save();\n                    testnet\n                }\n                .doc()\n                .to_string()\n            }));\n        }\n\n        // Reader threads\n        for _ in 0..total_threads {\n            let config_path = config_path.clone();\n            handles.push(thread::spawn(move || {\n                let config = Config::load(config_path).unwrap();\n                config.doc().to_string()\n            }));\n        }\n\n        let mut results = vec![];\n        for handle in handles {\n            results.push(handle.join().unwrap());\n        }\n        let local = local.doc().to_string();\n        let testnet = testnet.doc().to_string();\n        let maincloud = maincloud.doc().to_string();\n\n        // As long the results are any valid config, we're good.\n        assert!(results\n            .iter()\n            .all(|r| r.trim() == local.trim() || r.trim() == testnet.trim() || r.trim() == maincloud.trim()));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/detect.rs",
    "content": "use duct::cmd;\nuse std::io;\nuse std::path::Path;\n\n/// Find an executable in the `PATH`.\npub(crate) fn find_executable(exe_name: impl AsRef<Path>) -> Option<std::path::PathBuf> {\n    std::env::var_os(\"PATH\").and_then(|paths| {\n        std::env::split_paths(&paths)\n            .map(|dir| dir.join(&exe_name))\n            .find(|x| x.is_file())\n    })\n}\n\n/// Check if `rustup` is installed (aka: Is in the `PATH`).\npub(crate) fn has_rust_up() -> bool {\n    match std::env::consts::OS {\n        \"linux\" | \"freebsd\" | \"netbsd\" | \"openbsd\" | \"solaris\" | \"macos\" => find_executable(\"rustup\").is_some(),\n        \"windows\" => find_executable(\"rustup.exe\").is_some(),\n        unsupported_os => {\n            eprintln!(\"This OS may be unsupported for `rustup`: {unsupported_os}\");\n            false\n        }\n    }\n}\n\n/// Check if `rustfmt` is installed (aka: Is in the `PATH`).\npub(crate) fn has_rust_fmt() -> bool {\n    match std::env::consts::OS {\n        \"linux\" | \"freebsd\" | \"netbsd\" | \"openbsd\" | \"solaris\" | \"macos\" => find_executable(\"rustfmt\").is_some(),\n        \"windows\" => find_executable(\"rustfmt.exe\").is_some(),\n        unsupported_os => {\n            eprintln!(\"This OS may be unsupported for `rustfmt`: {unsupported_os}\");\n            false\n        }\n    }\n}\n\n/// Check if the target `wasm32-unknown-unknown` is installed.\npub(crate) fn has_wasm32_target() -> bool {\n    let result = || {\n        let path = cmd!(\n            \"rustc\",\n            \"--print\",\n            \"target-libdir\",\n            \"--target\",\n            \"wasm32-unknown-unknown\"\n        )\n        .read()?;\n        Path::new(path.trim())\n            .try_exists()\n            .map_err(|err: io::Error| anyhow::anyhow!(err))\n    };\n\n    result().unwrap_or_else(|err| {\n        eprintln!(\"Error checking for wasm32 target: {err}\");\n        false\n    })\n}\n\n/// Check if a given `PackageManager` executable is available on `PATH`.\n///\n/// On Windows, npm/pnpm/yarn are `.cmd` shims while bun is a `.exe`,\n/// so we check the platform-appropriate extension.\npub(crate) fn has_package_manager(pm: crate::spacetime_config::PackageManager) -> bool {\n    let name = pm.to_string();\n    if cfg!(windows) {\n        // bun ships as bun.exe; npm, pnpm, yarn are .cmd shims\n        let ext = if name == \"bun\" { \"exe\" } else { \"cmd\" };\n        find_executable(format!(\"{name}.{ext}\")).is_some()\n    } else {\n        find_executable(&name).is_some()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_has_target() -> anyhow::Result<()> {\n        assert!(has_wasm32_target());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/edit_distance.rs",
    "content": "/*\nSome parts copyright, The Rust project developers.\nSee https://github.com/rust-lang/rust/blob/8882507bc7dbad0cc0548204eb8777e51ac92332/COPYRIGHT\nfor the parts where MIT / Apache-2.0 applies.\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n*/\n\n//! Edit distances.\n//!\n//! The [edit distance] is a metric for measuring the difference between two strings.\n//!\n//! [edit distance]: https://en.wikipedia.org/wiki/Edit_distance\n\n// The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted\n// because it does not permit modifying characters that have already been transposed. The specific\n// algorithm should not matter to the caller of the methods, which is why it is not noted in the\n// documentation.\n\nuse std::{cmp, mem};\n\n/// Finds the [edit distance] between two strings.\n///\n/// Returns `None` if the distance exceeds the limit.\n///\n/// [edit distance]: https://en.wikipedia.org/wiki/Edit_distance\npub fn edit_distance(a: &str, b: &str, limit: usize) -> Option<usize> {\n    let mut a = &a.chars().collect::<Vec<_>>()[..];\n    let mut b = &b.chars().collect::<Vec<_>>()[..];\n\n    // Ensure that `b` is the shorter string, minimizing memory use.\n    if a.len() < b.len() {\n        mem::swap(&mut a, &mut b);\n    }\n\n    let min_dist = a.len() - b.len();\n    // If we know the limit will be exceeded, we can return early.\n    if min_dist > limit {\n        return None;\n    }\n\n    // Strip common prefix.\n    while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_first().zip(a.split_first()) {\n        if a_char != b_char {\n            break;\n        }\n        a = a_rest;\n        b = b_rest;\n    }\n    // Strip common suffix.\n    while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_last().zip(a.split_last()) {\n        if a_char != b_char {\n            break;\n        }\n        a = a_rest;\n        b = b_rest;\n    }\n\n    // If either string is empty, the distance is the length of the other.\n    // We know that `b` is the shorter string, so we don't need to check `a`.\n    if b.is_empty() {\n        return Some(min_dist);\n    }\n\n    let mut prev_prev = vec![usize::MAX; b.len() + 1];\n    let mut prev = (0..=b.len()).collect::<Vec<_>>();\n    let mut current = vec![0; b.len() + 1];\n\n    // row by row\n    for i in 1..=a.len() {\n        current[0] = i;\n        let a_idx = i - 1;\n\n        // column by column\n        for j in 1..=b.len() {\n            let b_idx = j - 1;\n\n            // There is no cost to substitute a character with itself.\n            let substitution_cost = if a[a_idx] == b[b_idx] { 0 } else { 1 };\n\n            current[j] = cmp::min(\n                // deletion\n                prev[j] + 1,\n                cmp::min(\n                    // insertion\n                    current[j - 1] + 1,\n                    // substitution\n                    prev[j - 1] + substitution_cost,\n                ),\n            );\n\n            if (i > 1) && (j > 1) && (a[a_idx] == b[b_idx - 1]) && (a[a_idx - 1] == b[b_idx]) {\n                // transposition\n                current[j] = cmp::min(current[j], prev_prev[j - 2] + 1);\n            }\n        }\n\n        // Rotate the buffers, reusing the memory.\n        [prev_prev, prev, current] = [prev, current, prev_prev];\n    }\n\n    // `prev` because we already rotated the buffers.\n    let distance = prev[b.len()];\n    (distance <= limit).then_some(distance)\n}\n\n/// Finds the best match for a given word in the given iterator.\n///\n/// As a loose rule to avoid the obviously incorrect suggestions, it takes\n/// an optional limit for the maximum allowable edit distance, which defaults\n/// to one-third of the given word.\n///\n/// We use case insensitive comparison to improve accuracy on an edge case with a lower(upper)case\n/// letters mismatch.\npub fn find_best_match_for_name<'c>(candidates: &[&'c str], lookup: &str, dist: Option<usize>) -> Option<&'c str> {\n    let lookup_uppercase = lookup.to_uppercase();\n\n    // Priority of matches:\n    // 1. Exact case insensitive match\n    // 2. Edit distance match\n    // 3. Sorted word match\n    if let Some(c) = candidates.iter().find(|c| c.to_uppercase() == lookup_uppercase) {\n        return Some(*c);\n    }\n\n    let mut dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);\n    let mut best = None;\n    // store the candidates with the same distance, only for `use_substring_score` current.\n    for c in candidates {\n        match edit_distance(lookup, c, dist) {\n            Some(0) => return Some(*c),\n            Some(d) => {\n                dist = d - 1;\n                best = Some(*c);\n            }\n            None => {}\n        }\n    }\n\n    if best.is_some() {\n        return best;\n    }\n\n    find_match_by_sorted_words(candidates, lookup)\n}\n\nfn find_match_by_sorted_words<'c>(iter_names: &[&'c str], lookup: &str) -> Option<&'c str> {\n    iter_names.iter().fold(None, |result, candidate| {\n        if sort_by_words(candidate) == sort_by_words(lookup) {\n            Some(*candidate)\n        } else {\n            result\n        }\n    })\n}\n\nfn sort_by_words(name: &str) -> String {\n    let mut split_words: Vec<&str> = name.split('_').collect();\n    // We are sorting primitive &strs and can use unstable sort here.\n    split_words.sort_unstable();\n    split_words.join(\"_\")\n}\n"
  },
  {
    "path": "crates/cli/src/errors.rs",
    "content": "use thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum CliError {\n    #[error(\"Config error: The option `{key}` not found\")]\n    Config { key: String },\n    #[error(\"Config error: The option `{key}` is not a `{kind}`, found: `{type}: {value}`\",\n        type=found.type_name(),\n        value=found\n    )]\n    ConfigType {\n        key: String,\n        kind: &'static str,\n        found: Box<toml_edit::Item>,\n    },\n}\n"
  },
  {
    "path": "crates/cli/src/lib.rs",
    "content": "pub mod api;\nmod common_args;\nmod config;\npub(crate) mod detect;\nmod edit_distance;\nmod errors;\npub mod spacetime_config;\nmod subcommands;\nmod tasks;\npub mod util;\npub mod version;\n\nuse std::process::ExitCode;\n\nuse clap::{ArgMatches, Command};\n\npub use config::Config;\nuse spacetimedb_paths::{RootDir, SpacetimePaths};\npub use subcommands::*;\npub use tasks::build;\n\npub fn get_subcommands() -> Vec<Command> {\n    vec![\n        publish::cli(),\n        delete::cli(),\n        logs::cli(),\n        call::cli(),\n        describe::cli(),\n        dev::cli(),\n        sql::cli(),\n        dns::cli(),\n        generate::cli(),\n        list::cli(),\n        login::cli(),\n        logout::cli(),\n        init::cli(),\n        build::cli(),\n        server::cli(),\n        subscribe::cli(),\n        start::cli(),\n        subcommands::version::cli(),\n    ]\n}\n\npub async fn exec_subcommand(\n    config: Config,\n    paths: &SpacetimePaths,\n    root_dir: Option<&RootDir>,\n    cmd: &str,\n    args: &ArgMatches,\n) -> anyhow::Result<ExitCode> {\n    match cmd {\n        \"call\" => call::exec(config, args).await,\n        \"describe\" => describe::exec(config, args).await,\n        \"dev\" => dev::exec(config, args).await,\n        \"publish\" => publish::exec(config, args).await,\n        \"delete\" => delete::exec(config, args).await,\n        \"logs\" => logs::exec(config, args).await,\n        \"sql\" => sql::exec(config, args).await,\n        \"rename\" => dns::exec(config, args).await,\n        \"generate\" => generate::exec(config, args).await,\n        \"list\" => list::exec(config, args).await,\n        \"init\" => init::exec(config, args).await.map(|_| ()),\n        \"build\" => build::exec(config, args).await.map(drop),\n        \"server\" => server::exec(config, paths, args).await,\n        \"subscribe\" => subscribe::exec(config, args).await,\n        \"start\" => return start::exec(paths, args).await,\n        \"login\" => login::exec(config, args).await,\n        \"logout\" => logout::exec(config, args).await,\n        \"version\" => return subcommands::version::exec(paths, root_dir, args).await,\n        unknown => Err(anyhow::anyhow!(\"Invalid subcommand: {unknown}\")),\n    }\n    .map(|()| ExitCode::SUCCESS)\n}\n\n/// An error type indicating that the process should exit silently with the\n/// given `ExitCode`.\n#[derive(thiserror::Error, Debug)]\n#[error(\"exit with {0:?}\")]\npub struct ExitWithCode(pub ExitCode);\n\nimpl ExitWithCode {\n    /// Basic unsuccessful termination.\n    pub const FAILURE: Self = ExitWithCode(ExitCode::FAILURE);\n}\n"
  },
  {
    "path": "crates/cli/src/main.rs",
    "content": "use std::process::ExitCode;\n\nuse clap::{Arg, Command};\nuse spacetimedb_cli::*;\nuse spacetimedb_paths::cli::CliTomlPath;\nuse spacetimedb_paths::RootDir;\n\n// Note that the standalone server is invoked through standaline/src/main.rs, so you will\n// also want to set the allocator there.\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemallocator::Jemalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\n#[global_allocator]\nstatic GLOBAL: Jemalloc = Jemalloc;\n\n#[cfg(target_env = \"msvc\")]\nuse mimalloc::MiMalloc;\n\n#[cfg(target_env = \"msvc\")]\n#[global_allocator]\nstatic GLOBAL: MiMalloc = MiMalloc;\n\n#[cfg(not(feature = \"markdown-docs\"))]\n#[tokio::main]\nasync fn main() -> anyhow::Result<ExitCode> {\n    use spacetimedb_paths::SpacetimePaths;\n\n    // Compute matches before loading the config, because `Config` has an observable `drop` method\n    // (which deletes a lockfile),\n    // and Clap calls `exit` on parse failure rather than panicking, so destructors never run.\n    let matches = get_command().get_matches();\n    let (cmd, subcommand_args) = matches.subcommand().unwrap();\n\n    let root_dir = matches.get_one::<RootDir>(\"root_dir\");\n    let paths = match root_dir {\n        Some(dir) => SpacetimePaths::from_root_dir(dir),\n        None => SpacetimePaths::platform_defaults()?,\n    };\n    let cli_toml = matches\n        .get_one::<CliTomlPath>(\"config_path\")\n        .cloned()\n        .unwrap_or_else(|| paths.cli_config_dir.cli_toml());\n    let config = Config::load(cli_toml)?;\n\n    exec_subcommand(config, &paths, root_dir, cmd, subcommand_args)\n        .await\n        .or_else(|e| e.downcast::<ExitWithCode>().map(|e| e.0))\n}\n\n#[cfg(feature = \"markdown-docs\")]\n#[tokio::main]\nasync fn main() -> anyhow::Result<ExitCode> {\n    let markdown = clap_markdown::help_markdown_command(&get_command());\n    println!(\"{}\", markdown);\n    Ok(ExitCode::SUCCESS)\n}\n\nfn get_command() -> Command {\n    Command::new(\"spacetime\")\n        .version(version::CLI_VERSION)\n        .long_version(version::long_version())\n        .arg_required_else_help(true)\n        .subcommand_required(true)\n        .arg(\n            Arg::new(\"root_dir\")\n                .long(\"root-dir\")\n                .help(\"The root directory to store all spacetime files in.\")\n                .value_parser(clap::value_parser!(RootDir)),\n        )\n        .arg(\n            Arg::new(\"config_path\")\n                .long(\"config-path\")\n                .help(\"The path to the cli.toml config file\")\n                .value_parser(clap::value_parser!(CliTomlPath)),\n        )\n        .subcommands(get_subcommands())\n        .help_expected(true)\n        .help_template(\n            r#\"\n┌───────────────────────────────────────────────────────────────────────────────────────────────────────┐\n│                                                                                                       │\n│                                                                                                       │\n│                                                                              ⢀⠔⠁                      │\n│                                                                            ⣠⡞⠁                        │\n│                                              ⣀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣀⣀⣀⣀⣀⣀⣀⣤⣤⡴⠒    ⢀⣠⡾⠋                          │\n│                                         ⢀⣤⣶⣾88888888888888888888⠿⠋    ⢀⣴8⡟⠁                           │\n│                                      ⢀⣤⣾88888⡿⠿⠛⠛⠛⠛⠛⠛⠛⠛⠻⠿88888⠟⠁    ⣠⣾88⡟                             │\n│                                    ⢀⣴88888⠟⠋⠁ ⣀⣤⠤⠶⠶⠶⠶⠶⠤⣤⣀ ⠉⠉⠉    ⢀⣴⣾888⡟                              │\n│                                   ⣠88888⠋  ⣠⠶⠋⠉         ⠉⠙⠶⣄   ⢀⣴888888⠃                              │\n│                                  ⣰8888⡟⠁ ⣰⠟⠁               ⠈⠻⣆ ⠈⢿888888                               │\n│                                 ⢠8888⡟  ⡼⠁                   ⠈⢧ ⠈⢿8888⡿                               │\n│                                 ⣼8888⠁ ⢸⠇                     ⠸⡇ ⠘8888⣷                               │\n│                                 88888  8                       8  88888                               │\n│                                 ⢿8888⡄ ⢸⡆                     ⢰⡇ ⢀8888⡟                               │\n│                                 ⣾8888⣷⡀ ⢳⡀                   ⢀⡞  ⣼8888⠃                               │\n│                                 888888⣷⡀ ⠹⣦⡀               ⢀⣴⠏ ⢀⣼8888⠏                                │\n│                                ⢠888888⠟⠁   ⠙⠶⣄⣀         ⣀⣠⠶⠋  ⣠88888⠋                                 │\n│                                ⣼888⡿⠟⠁    ⣀⣀⣀ ⠉⠛⠒⠶⠶⠶⠶⠶⠒⠛⠉ ⢀⣠⣴88888⠟⠁                                  │\n│                               ⣼88⡿⠋    ⢀⣴88888⣶⣦⣤⣤⣤⣤⣤⣤⣤⣤⣶⣾88888⡿⠛⠁                                    │\n│                             ⢀⣼8⠟⠁    ⣠⣶88888888888888888888⡿⠿⠛⠁                                       │\n│                            ⣠⡾⠋⠁    ⠤⠞⠛⠛⠉⠉⠉⠉⠉⠉⠉⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠉⠉                                            │\n│                          ⢀⡼⠋                                                                          │\n│                        ⢀⠔⠁                                                                            │\n│                                                                                                       │\n│                                                                                                       │\n│  .d8888b.                                     888    d8b                        8888888b.  888888b.   │\n│ d88P  Y88b                                    888    Y8P                        888  \"Y88b 888  \"88b  │\n│ Y88b.                                         888                               888    888 888  .88P  │\n│  \"Y888b.   88888b.   8888b.   .d8888b .d88b.  888888 888 88888b.d88b.   .d88b.  888    888 8888888K.  │\n│     \"Y88b. 888 \"88b     \"88b d88P\"   d8P  Y8b 888    888 888 \"888 \"88b d8P  Y8b 888    888 888  \"Y88b │\n│       \"888 888  888 .d888888 888     88888888 888    888 888  888  888 88888888 888    888 888    888 │\n│ Y88b  d88P 888 d88P 888  888 Y88b.   Y8b.     Y88b.  888 888  888  888 Y8b.     888  .d88P 888   d88P │\n│  \"Y8888P\"  88888P\"  \"Y888888  \"Y8888P \"Y8888   \"Y888 888 888  888  888  \"Y8888  8888888P\"  8888888P\"  │\n│            888                                                                                        │\n│            888                                                                                        │\n│            888                                                                                        │\n│                                  \"Multiplayer at the speed of light\"                                  │\n│                                                                                                       │\n├───────────────────────────────────────────────────────────────────────────────────────────────────────┤\n│ SpacetimeDB Command Line Tool                                                                         │\n│ Easily interact with a SpacetimeDB node                                                               │\n│                                                                                                       │\n│ Give us feedback in our Discord server:                                                               │\n│    https://discord.gg/spacetimedb                                                                     │\n└───────────────────────────────────────────────────────────────────────────────────────────────────────┘\nUsage:\n{usage}\n\nOptions:\n{options}\n\nCommands:\n{subcommands}\n\"#,\n        )\n}\n"
  },
  {
    "path": "crates/cli/src/spacetime_config.rs",
    "content": "use anyhow::Context;\nuse clap::{ArgMatches, Command};\nuse path_clean::PathClean;\nuse serde::{Deserialize, Serialize};\nuse serde_json::Value;\nuse std::collections::{HashMap, HashSet};\nuse std::fmt;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse thiserror::Error;\n\n/// The filename for configuration\npub const CONFIG_FILENAME: &str = \"spacetime.json\";\n\n/// Supported package managers for JavaScript/TypeScript projects\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum PackageManager {\n    Npm,\n    Pnpm,\n    Yarn,\n    Bun,\n}\n\nimpl fmt::Display for PackageManager {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let s = match self {\n            PackageManager::Npm => \"npm\",\n            PackageManager::Pnpm => \"pnpm\",\n            PackageManager::Yarn => \"yarn\",\n            PackageManager::Bun => \"bun\",\n        };\n        write!(f, \"{s}\")\n    }\n}\n\nimpl PackageManager {\n    /// Get the command to run a dev script\n    pub fn run_dev_command(&self) -> &'static str {\n        match self {\n            PackageManager::Npm => \"npm run dev\",\n            PackageManager::Pnpm => \"pnpm run dev\",\n            PackageManager::Yarn => \"yarn dev\",\n            PackageManager::Bun => \"bun run dev\",\n        }\n    }\n}\n\n/// Errors that can occur when building or using CommandConfig\n#[derive(Debug, Error)]\npub enum CommandConfigError {\n    #[error(\"The option `--{arg_name}` is defined in Clap, but not in the config. If this is intentional and the option shouldn't be available in the config, you can exclude it with the `CommandConfigBuilder::exclude` function\")]\n    ClapArgNotDefined { arg_name: String },\n\n    #[error(\"Key '{config_name}' references clap argument '{clap_name}' which doesn't exist in the Command. If the config key should be different than the clap argument, use from_clap()\")]\n    InvalidClapReference { config_name: String, clap_name: String },\n\n    #[error(\"Key '{config_name}' has alias '{alias}' which doesn't exist in the Command\")]\n    InvalidAliasReference { config_name: String, alias: String },\n\n    #[error(\"Excluded key '{key}' doesn't exist in the clap Command\")]\n    InvalidExclusion { key: String },\n\n    #[error(\"Config key '{config_key}' is not supported in the config file. Available keys: {available_keys}\")]\n    UnsupportedConfigKey { config_key: String, available_keys: String },\n\n    #[error(\"Required key '{key}' is missing from the config file or CLI\")]\n    MissingRequiredKey { key: String },\n\n    #[error(\"Failed to convert config value for key '{key}' to type {target_type}\")]\n    ConversionError {\n        key: String,\n        target_type: String,\n        #[source]\n        source: anyhow::Error,\n    },\n}\n\n/// Project configuration loaded from spacetime.json.\n///\n/// The root object IS a database entity. `generate` is per-database\n/// (inherited by children), and `dev` is root-only.\n///\n/// Example (simple):\n/// ```json\n/// {\n///   \"database\": \"my-database\",\n///   \"server\": \"local\",\n///   \"module-path\": \"./server\",\n///   \"dev\": { \"run\": \"pnpm dev\" },\n///   \"generate\": [\n///     { \"language\": \"typescript\", \"out-dir\": \"./src/module_bindings\" }\n///   ]\n/// }\n/// ```\n///\n/// Example (multi-database):\n/// ```json\n/// {\n///   \"server\": \"local\",\n///   \"module-path\": \"./server\",\n///   \"children\": [\n///     { \"database\": \"region-1\" },\n///     { \"database\": \"region-2\", \"module-path\": \"./region-server\" }\n///   ]\n/// }\n/// ```\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\n#[serde(rename_all = \"kebab-case\")]\npub struct SpacetimeConfig {\n    /// Configuration for the dev command. Root-level only, not inherited.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub dev: Option<DevConfig>,\n\n    /// Per-database generate entries. Inherited by children unless overridden.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub generate: Option<Vec<HashMap<String, Value>>>,\n\n    /// Child database entities.\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub children: Option<Vec<SpacetimeConfig>>,\n\n    /// Name of the config file from which this target's `database` value was merged.\n    #[serde(rename = \"_source-config\", skip_serializing)]\n    pub source_config: Option<String>,\n\n    /// All other entity-level fields (database, module-path, server, etc.)\n    #[serde(flatten)]\n    pub additional_fields: HashMap<String, Value>,\n}\n\n/// Configuration for `spacetime dev` command.\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\n#[serde(rename_all = \"kebab-case\", deny_unknown_fields)]\npub struct DevConfig {\n    /// The command to run the client development server.\n    /// This is used by `spacetime dev` to start the client after publishing.\n    /// Example: \"npm run dev\", \"pnpm dev\", \"cargo run\"\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub run: Option<String>,\n}\n\n/// A fully resolved database target after inheritance.\n/// Contains all fields needed for both publish and generate operations.\n#[derive(Debug, Clone)]\npub struct FlatTarget {\n    /// All entity-level fields (database, module-path, server, etc.)\n    pub fields: HashMap<String, Value>,\n    /// Name of the config file from which this target's `database` value was merged.\n    pub source_config: Option<String>,\n    /// Generate entries for this target (inherited or overridden)\n    pub generate: Option<Vec<HashMap<String, Value>>>,\n}\n\n/// Result of loading config from one or more files.\npub struct LoadedConfig {\n    pub config: SpacetimeConfig,\n    pub config_dir: PathBuf,\n    /// Which files contributed to this config\n    pub loaded_files: Vec<PathBuf>,\n    /// Whether a dev-specific file (spacetime.dev.json or spacetime.dev.local.json) was loaded\n    pub has_dev_file: bool,\n}\n\nimpl SpacetimeConfig {\n    /// Collect all database targets with parent→child inheritance.\n    /// Children inherit unset `additional_fields` from their parent.\n    /// `dev`, `generate`, and `children` are NOT propagated to child targets.\n    /// Returns `Vec<FlatTarget>` with fully resolved fields.\n    pub fn collect_all_targets_with_inheritance(&self) -> Vec<FlatTarget> {\n        self.collect_targets_inner(None)\n    }\n\n    fn collect_targets_inner(&self, parent_fields: Option<&HashMap<String, Value>>) -> Vec<FlatTarget> {\n        // module-path, bin-path, and js-path are mutually exclusive module sources.\n        // If a child specifies any one, the other two are not inherited from the parent.\n        const MODULE_SOURCE_KEYS: &[&str] = &[\"module-path\", \"bin-path\", \"js-path\"];\n        let child_specifies_source = MODULE_SOURCE_KEYS\n            .iter()\n            .any(|k| self.additional_fields.contains_key(*k));\n\n        // Build this node's fields by inheriting from parent\n        let mut fields = self.additional_fields.clone();\n        if let Some(parent) = parent_fields {\n            for (key, value) in parent {\n                if fields.contains_key(key) {\n                    continue;\n                }\n                // If the child specifies any module source, skip inheriting the others\n                if child_specifies_source && MODULE_SOURCE_KEYS.contains(&key.as_str()) {\n                    continue;\n                }\n                fields.insert(key.clone(), value.clone());\n            }\n        }\n\n        // Generate is never inherited. It is tied to a specific module and output location:\n        // inheriting is redundant when the child shares the parent's module (deduplication\n        // handles it) and dangerous when the child uses a different module (two modules\n        // would write bindings to the same output directory).\n        let effective_generate = self.generate.clone();\n\n        let target = FlatTarget {\n            fields: fields.clone(),\n            source_config: self.source_config.clone(),\n            generate: effective_generate,\n        };\n\n        let mut result = vec![target];\n\n        if let Some(children) = &self.children {\n            for child in children {\n                let child_targets = child.collect_targets_inner(Some(&fields));\n                result.extend(child_targets);\n            }\n        }\n\n        result\n    }\n\n    /// Iterate through all database targets (self + children recursively).\n    /// Note: Does NOT apply parent→child inheritance. Use\n    /// `collect_all_targets_with_inheritance()` for that.\n    pub fn iter_all_targets(&self) -> Box<dyn Iterator<Item = &SpacetimeConfig> + '_> {\n        Box::new(\n            std::iter::once(self).chain(\n                self.children\n                    .iter()\n                    .flat_map(|children| children.iter())\n                    .flat_map(|child| child.iter_all_targets()),\n            ),\n        )\n    }\n\n    /// Count total number of targets (self + all descendants)\n    pub fn count_targets(&self) -> usize {\n        1 + self\n            .children\n            .as_ref()\n            .map(|children| children.iter().map(|child| child.count_targets()).sum())\n            .unwrap_or(0)\n    }\n}\n\n/// A unified config that merges clap arguments with config file values.\n/// Provides a `get_one::<T>(key)` interface similar to clap's ArgMatches.\n/// CLI arguments take precedence over config file values.\n#[derive(Debug)]\npub struct CommandConfig<'a> {\n    /// Schema defining the contract between CLI and config\n    schema: &'a CommandSchema,\n    /// Config file values\n    config_values: HashMap<String, Value>,\n    /// CLI arguments\n    matches: &'a ArgMatches,\n}\n\n/// Schema that defines the contract between CLI arguments and config file keys.\n/// Does not hold ArgMatches - methods take matches as a parameter instead.\n#[derive(Debug)]\npub struct CommandSchema {\n    /// Key definitions\n    keys: Vec<Key>,\n    /// Map from config name to clap arg name (for from_clap mapping)\n    config_to_clap: HashMap<String, String>,\n    /// Map from config name to alias (for alias mapping)\n    config_to_alias: HashMap<String, String>,\n}\n\n/// Builder for creating a CommandSchema with custom mappings and exclusions.\npub struct CommandSchemaBuilder {\n    /// Keys defined for this command\n    keys: Vec<Key>,\n    /// Set of keys to exclude from being read from the config file\n    excluded_keys: HashSet<String>,\n}\n\nimpl CommandSchemaBuilder {\n    pub fn new() -> Self {\n        Self {\n            keys: Vec::new(),\n            excluded_keys: HashSet::new(),\n        }\n    }\n\n    /// Add a key definition to the builder.\n    /// Example: `.key(Key::new(\"server\"))`\n    pub fn key(mut self, key: Key) -> Self {\n        self.keys.push(key);\n        self\n    }\n\n    /// Exclude a key from being read from the config file.\n    /// This is useful for keys that should only come from CLI arguments.\n    pub fn exclude(mut self, key: impl Into<String>) -> Self {\n        self.excluded_keys.insert(key.into());\n        self\n    }\n\n    /// Build a CommandSchema by validating against the clap Command.\n    ///\n    /// # Arguments\n    /// * `command` - The clap Command to validate against\n    pub fn build(self, command: &Command) -> Result<CommandSchema, CommandConfigError> {\n        // Collect all clap argument names for validation\n        let clap_arg_names: HashSet<String> = command\n            .get_arguments()\n            .map(|arg| arg.get_id().as_str().to_string())\n            .collect();\n\n        // Check that all the defined keys exist in clap\n        for key in &self.keys {\n            if !clap_arg_names.contains(key.clap_arg_name()) {\n                return Err(CommandConfigError::InvalidClapReference {\n                    config_name: key.config_name().to_string(),\n                    clap_name: key.clap_arg_name().to_string(),\n                });\n            }\n\n            // Validate alias if present\n            if let Some(alias) = &key.clap_alias\n                && !clap_arg_names.contains(alias)\n            {\n                return Err(CommandConfigError::InvalidAliasReference {\n                    config_name: key.config_name().to_string(),\n                    alias: alias.clone(),\n                });\n            }\n        }\n\n        // Validate exclusions reference valid clap arguments\n        for excluded_key in &self.excluded_keys {\n            if !clap_arg_names.contains(excluded_key) {\n                return Err(CommandConfigError::InvalidExclusion {\n                    key: excluded_key.clone(),\n                });\n            }\n        }\n\n        // A list of clap args that are referenced by the config keys\n        let mut referenced_clap_args = HashSet::new();\n        let mut config_to_clap_map = HashMap::new();\n        let mut config_to_alias_map = HashMap::new();\n\n        for key in &self.keys {\n            let config_name = key.config_name().to_string();\n            let clap_name = key.clap_arg_name().to_string();\n\n            referenced_clap_args.insert(clap_name.clone());\n\n            // Track the mapping from config name to clap arg name (if using from_clap)\n            if key.clap_name.is_some() {\n                config_to_clap_map.insert(config_name.clone(), clap_name.clone());\n            }\n\n            // Register the alias if present\n            if let Some(alias) = &key.clap_alias {\n                referenced_clap_args.insert(alias.clone());\n                config_to_alias_map.insert(config_name.clone(), alias.clone());\n            }\n        }\n\n        // Check that all clap arguments are either referenced or excluded\n        for arg in command.get_arguments() {\n            let arg_name = arg.get_id().as_str();\n\n            // Skip clap's built-in arguments\n            if arg_name == \"help\" || arg_name == \"version\" {\n                continue;\n            }\n\n            if !referenced_clap_args.contains(arg_name) && !self.excluded_keys.contains(arg_name) {\n                return Err(CommandConfigError::ClapArgNotDefined {\n                    arg_name: arg_name.to_string(),\n                });\n            }\n        }\n\n        Ok(CommandSchema {\n            keys: self.keys,\n            config_to_clap: config_to_clap_map,\n            config_to_alias: config_to_alias_map,\n        })\n    }\n}\n\nimpl Default for CommandSchemaBuilder {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl CommandSchema {\n    /// Get a value from clap arguments only (not from config).\n    /// Useful for filtering or checking if a value was provided via CLI.\n    pub fn get_clap_arg<T: Clone + Send + Sync + 'static>(\n        &self,\n        matches: &ArgMatches,\n        config_name: &str,\n    ) -> Result<Option<T>, CommandConfigError> {\n        // Check clap with mapped name (if from_clap was used, use that name, otherwise use config name)\n        let clap_name = self\n            .config_to_clap\n            .get(config_name)\n            .map(|s| s.as_str())\n            .unwrap_or(config_name);\n\n        // Only return the value if it was actually provided by the user, not from defaults\n        if let Some(source) = matches.value_source(clap_name)\n            && source == clap::parser::ValueSource::CommandLine\n            && let Some(value) = matches.get_one::<T>(clap_name)\n        {\n            return Ok(Some(value.clone()));\n        }\n\n        // Try clap with the alias if it exists\n        if let Some(alias) = self.config_to_alias.get(config_name)\n            && let Some(source) = matches.value_source(alias)\n            && source == clap::parser::ValueSource::CommandLine\n            && let Some(value) = matches.get_one::<T>(alias)\n        {\n            return Ok(Some(value.clone()));\n        }\n\n        Ok(None)\n    }\n\n    /// Check if a value was provided via CLI (not from config).\n    /// Only returns true if the user explicitly provided the value, not if it came from a default.\n    pub fn is_from_cli(&self, matches: &ArgMatches, config_name: &str) -> bool {\n        // Check clap with mapped name\n        let clap_name = self\n            .config_to_clap\n            .get(config_name)\n            .map(|s| s.as_str())\n            .unwrap_or(config_name);\n\n        // Use value_source to check if the value was actually provided by the user\n        if let Some(source) = matches.value_source(clap_name)\n            && source == clap::parser::ValueSource::CommandLine\n        {\n            return true;\n        }\n\n        // Check clap with alias\n        if let Some(alias) = self.config_to_alias.get(config_name)\n            && let Some(source) = matches.value_source(alias)\n            && source == clap::parser::ValueSource::CommandLine\n        {\n            return true;\n        }\n\n        false\n    }\n\n    /// Get all module-specific keys that were provided via CLI.\n    pub fn module_specific_cli_args(&self, matches: &ArgMatches) -> Vec<&str> {\n        self.keys\n            .iter()\n            .filter(|k| k.module_specific && self.is_from_cli(matches, k.config_name()))\n            .map(|k| k.config_name())\n            .collect()\n    }\n\n    /// Get user-facing CLI flags (e.g. `--bin-path`) for all module-specific options\n    /// that were explicitly provided via CLI.\n    pub fn module_specific_cli_flags(&self, command: &Command, matches: &ArgMatches) -> Vec<String> {\n        self.module_specific_cli_args(matches)\n            .iter()\n            .map(|arg| {\n                let clap_name = self.clap_arg_name_for(arg);\n                command\n                    .get_arguments()\n                    .find(|a| a.get_id().as_str() == clap_name)\n                    .and_then(|a| a.get_long())\n                    .map(|long| format!(\"--{long}\"))\n                    .unwrap_or_else(|| format!(\"--{}\", clap_name.replace('_', \"-\")))\n            })\n            .collect()\n    }\n\n    /// Validate that module-specific CLI flags are not used when operating on multiple targets.\n    pub fn validate_no_module_specific_cli_args_for_multiple_targets(\n        &self,\n        command: &Command,\n        matches: &ArgMatches,\n        target_count: usize,\n        operation_context: &str,\n        resolution_hint: &str,\n    ) -> anyhow::Result<()> {\n        if target_count <= 1 {\n            return Ok(());\n        }\n\n        let display_args = self.module_specific_cli_flags(command, matches);\n        if display_args.is_empty() {\n            return Ok(());\n        }\n\n        anyhow::bail!(\n            \"Cannot use module-specific arguments ({}) when {}. {}\",\n            display_args.join(\", \"),\n            operation_context,\n            resolution_hint\n        );\n    }\n\n    /// Get all generate-entry-specific keys that were provided via CLI.\n    pub fn generate_entry_specific_cli_args(&self, matches: &ArgMatches) -> Vec<&str> {\n        self.keys\n            .iter()\n            .filter(|k| k.generate_entry_specific && self.is_from_cli(matches, k.config_name()))\n            .map(|k| k.config_name())\n            .collect()\n    }\n\n    /// Get user-facing CLI flags for generate-entry-specific options provided via CLI.\n    pub fn generate_entry_specific_cli_flags(&self, command: &Command, matches: &ArgMatches) -> Vec<String> {\n        self.generate_entry_specific_cli_args(matches)\n            .iter()\n            .map(|arg| {\n                let clap_name = self.clap_arg_name_for(arg);\n                command\n                    .get_arguments()\n                    .find(|a| a.get_id().as_str() == clap_name)\n                    .and_then(|a| a.get_long())\n                    .map(|long| format!(\"--{long}\"))\n                    .unwrap_or_else(|| format!(\"--{}\", clap_name.replace('_', \"-\")))\n            })\n            .collect()\n    }\n\n    /// Validate that generate-entry-specific CLI flags are not used when operating on multiple generate entries.\n    pub fn validate_no_generate_entry_specific_cli_args(\n        &self,\n        command: &Command,\n        matches: &ArgMatches,\n        entry_count: usize,\n    ) -> anyhow::Result<()> {\n        if entry_count <= 1 {\n            return Ok(());\n        }\n\n        let display_args = self.generate_entry_specific_cli_flags(command, matches);\n        if display_args.is_empty() {\n            return Ok(());\n        }\n\n        anyhow::bail!(\n            \"Cannot use generate-entry-specific arguments ({}) when generating for multiple entries. \\\n             Specify a database name to select a single target, or remove these arguments.\",\n            display_args.join(\", \"),\n        );\n    }\n\n    /// Get the clap argument name for a config key.\n    pub fn clap_arg_name_for<'a>(&'a self, config_name: &'a str) -> &'a str {\n        self.config_to_clap\n            .get(config_name)\n            .map(|s| s.as_str())\n            .unwrap_or(config_name)\n    }\n}\n\n/// Configuration for a single key in the CommandConfig.\n#[derive(Debug, Clone)]\npub struct Key {\n    /// The key name in the config file (e.g., \"module-path\")\n    config_name: String,\n    /// The corresponding clap argument name (e.g., \"project-path\"), if different\n    clap_name: Option<String>,\n    /// Alias for a clap argument, useful for example if we have to deprecate a clap\n    /// argument and still allow to use it in the CLI args, but not in the config file\n    clap_alias: Option<String>,\n    /// Whether this key is module-specific (per-database)\n    module_specific: bool,\n    /// Whether this key is generate-entry-specific (per-generate-entry within a database)\n    generate_entry_specific: bool,\n    /// Whether this key is required in the config file\n    required: bool,\n}\n\nimpl Key {\n    /// Returns a new Key instance\n    pub fn new(name: impl Into<String>) -> Self {\n        Self {\n            config_name: name.into(),\n            clap_name: None,\n            clap_alias: None,\n            module_specific: false,\n            generate_entry_specific: false,\n            required: false,\n        }\n    }\n\n    /// Map this config key to a different clap argument name. When fetching values\n    /// the key that is defined should be used.\n    /// Example: Key::new(\"module-path\").from_clap(\"project-path\")\n    ///          - in this case the value for either project-path in clap or\n    ///            for module-path in the config file will be fetched\n    pub fn from_clap(mut self, clap_arg_name: impl Into<String>) -> Self {\n        self.clap_name = Some(clap_arg_name.into());\n        self\n    }\n\n    /// Add an alias for a clap argument name that also maps to this key.\n    /// This is useful for backwards compatibility when renaming arguments.\n    /// Example: Key::new(\"module-path\").alias(\"project-path\")\n    ///\n    /// This allows both --module-path and --project-path to map to the same config key.\n    /// The value should then be accessed by using `module-path`\n    ///\n    /// The difference between from_clap and alias is that from_clap will work by mapping\n    /// a single value from clap, whereas alias will check both of them in the CLI args\n    pub fn alias(mut self, alias_name: impl Into<String>) -> Self {\n        self.clap_alias = Some(alias_name.into());\n        self\n    }\n\n    /// Mark this key as module-specific. For example, the `js-bin` config option makes sense\n    /// only when applied to a single module. The `server` config option makes sense for\n    /// multiple publish targets\n    pub fn module_specific(mut self) -> Self {\n        self.module_specific = true;\n        self\n    }\n\n    /// Mark this key as generate-entry-specific. These keys (like `language`, `out_dir`)\n    /// only make sense when a single generate entry is targeted. If multiple generate\n    /// entries exist and this key is provided via CLI, it's an error.\n    pub fn generate_entry_specific(mut self) -> Self {\n        self.generate_entry_specific = true;\n        self\n    }\n\n    /// Mark this key as required in the config file. If a config file is provided but\n    /// this key is missing, an error will be returned.\n    pub fn required(mut self) -> Self {\n        self.required = true;\n        self\n    }\n\n    /// Get the clap argument name (either the mapped name or the config name)\n    pub fn clap_arg_name(&self) -> &str {\n        self.clap_name.as_deref().unwrap_or(&self.config_name)\n    }\n\n    /// Get the config name\n    pub fn config_name(&self) -> &str {\n        &self.config_name\n    }\n\n    /// Check if this key is required\n    pub fn is_required(&self) -> bool {\n        self.required\n    }\n}\n\nimpl<'a> CommandConfig<'a> {\n    /// Create a new CommandConfig by validating config values against a schema.\n    ///\n    /// # Arguments\n    /// * `schema` - The command schema that defines valid keys and types\n    /// * `config_values` - Values from the config file\n    /// * `matches` - CLI arguments\n    ///\n    /// # Errors\n    /// Returns an error if any config keys are not defined in the schema.\n    /// Note: Required key validation happens when get_one() is called, not during construction.\n    pub fn new(\n        schema: &'a CommandSchema,\n        config_values: HashMap<String, Value>,\n        matches: &'a ArgMatches,\n    ) -> Result<Self, CommandConfigError> {\n        // Normalize keys from kebab-case to snake_case to match clap's Arg::new() convention\n        let normalized_values: HashMap<String, Value> = config_values\n            .into_iter()\n            .map(|(k, v)| (k.replace('-', \"_\"), v))\n            .collect();\n\n        // Build set of valid config keys from schema\n        let valid_config_keys: HashSet<String> = schema.keys.iter().map(|k| k.config_name().to_string()).collect();\n\n        // Check that all keys in config file are defined in schema\n        for config_key in normalized_values.keys() {\n            if !valid_config_keys.contains(config_key) {\n                return Err(CommandConfigError::UnsupportedConfigKey {\n                    config_key: config_key.clone(),\n                    available_keys: valid_config_keys\n                        .iter()\n                        .map(|s| s.as_str())\n                        .collect::<Vec<_>>()\n                        .join(\", \"),\n                });\n            }\n        }\n\n        Ok(CommandConfig {\n            schema,\n            config_values: normalized_values,\n            matches,\n        })\n    }\n\n    /// Get a single value from the config as a specific type.\n    /// First checks clap args (via schema), then falls back to config values.\n    ///\n    /// Returns:\n    /// - Ok(Some(T)) if the value exists and can be converted\n    /// - Ok(None) if the value doesn't exist in either clap or config\n    /// - Err if conversion fails\n    pub fn get_one<T: Clone + Send + Sync + serde::de::DeserializeOwned + 'static>(\n        &self,\n        key: &str,\n    ) -> Result<Option<T>, CommandConfigError> {\n        // Try clap arguments first (CLI takes precedence) via schema\n        let from_cli = self.schema.get_clap_arg::<T>(self.matches, key)?;\n        if let Some(ref value) = from_cli {\n            return Ok(Some(value.clone()));\n        }\n\n        // Fall back to config values using the config name\n        if let Some(value) = self.config_values.get(key) {\n            serde_json::from_value::<T>(value.clone())\n                .map_err(|e| CommandConfigError::ConversionError {\n                    key: key.to_string(),\n                    target_type: std::any::type_name::<T>().to_string(),\n                    source: e.into(),\n                })\n                .map(Some)\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Get a config value (from config file only, not merged with CLI).\n    ///\n    /// This is useful for filtering scenarios where you need to compare\n    /// CLI values against config file values.\n    pub fn get_config_value(&self, key: &str) -> Option<&Value> {\n        self.config_values.get(key)\n    }\n\n    /// Get a path value and resolve it against `config_dir` if it came from config (not CLI).\n    pub fn get_resolved_path(\n        &self,\n        key: &str,\n        config_dir: Option<&Path>,\n    ) -> Result<Option<PathBuf>, CommandConfigError> {\n        let path = self.get_one::<PathBuf>(key)?;\n        let from_cli = self.is_from_cli(key);\n        Ok(path.map(|p| {\n            let resolved = if p.is_absolute() || from_cli {\n                p\n            } else if let Some(base_dir) = config_dir {\n                base_dir.join(p)\n            } else {\n                p\n            };\n            resolved.clean()\n        }))\n    }\n\n    /// Returns true when this key was explicitly provided via CLI.\n    pub fn is_from_cli(&self, key: &str) -> bool {\n        self.schema.is_from_cli(self.matches, key)\n    }\n\n    /// Validate that all required keys are present in either config or CLI.\n    pub fn validate(&self) -> Result<(), CommandConfigError> {\n        for key in &self.schema.keys {\n            if key.is_required()\n                && !self.config_values.contains_key(key.config_name())\n                && !self.schema.is_from_cli(self.matches, key.config_name())\n            {\n                return Err(CommandConfigError::MissingRequiredKey {\n                    key: key.config_name().to_string(),\n                });\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl SpacetimeConfig {\n    /// Find and load a spacetime.json file (convenience wrapper for no env).\n    ///\n    /// Searches for spacetime.json starting from the current directory\n    /// and walking up the directory tree until found or filesystem root is reached.\n    ///\n    /// Returns `Ok(Some((path, config)))` if found and successfully parsed.\n    /// Returns `Ok(None)` if not found.\n    /// Returns `Err` if found but failed to parse.\n    pub fn find_and_load() -> anyhow::Result<Option<(PathBuf, Self)>> {\n        Self::find_and_load_from(std::env::current_dir()?)\n    }\n\n    /// Find and load a spacetime.json file starting from a specific directory.\n    ///\n    /// Searches for spacetime.json starting from `start_dir`\n    /// and walking up the directory tree until found or filesystem root is reached.\n    pub fn find_and_load_from(start_dir: PathBuf) -> anyhow::Result<Option<(PathBuf, Self)>> {\n        Ok(find_and_load_with_env_from(None, start_dir)?.map(|loaded| {\n            let config_path = loaded.config_dir.join(CONFIG_FILENAME);\n            (config_path, loaded.config)\n        }))\n    }\n\n    /// Load a spacetime.json file from a specific path.\n    ///\n    /// The file must exist and be valid JSON5 format (supports comments).\n    pub fn load(path: &Path) -> anyhow::Result<Self> {\n        let content =\n            std::fs::read_to_string(path).with_context(|| format!(\"Failed to read config file: {}\", path.display()))?;\n\n        let config: Self = json5::from_str(&content)\n            .map_err(|e| anyhow::anyhow!(\"Failed to parse config file {}: {}\", path.display(), e))?;\n\n        Ok(config)\n    }\n\n    /// Save the config to a file.\n    ///\n    /// The config will be serialized as pretty-printed JSON.\n    pub fn save(&self, path: &Path) -> anyhow::Result<()> {\n        let json = serde_json::to_string_pretty(self).context(\"Failed to serialize config\")?;\n\n        std::fs::write(path, json).with_context(|| format!(\"Failed to write config file: {}\", path.display()))?;\n\n        Ok(())\n    }\n\n    /// Create a spacetime.json file in the current directory with the given config.\n    pub fn create_in_current_dir(&self) -> anyhow::Result<PathBuf> {\n        let config_path = std::env::current_dir()?.join(\"spacetime.json\");\n        self.save(&config_path)?;\n        Ok(config_path)\n    }\n\n    /// Create a configuration with a run command for dev\n    pub fn with_run_command(run_command: impl Into<String>) -> Self {\n        Self {\n            dev: Some(DevConfig {\n                run: Some(run_command.into()),\n            }),\n            ..Default::default()\n        }\n    }\n\n    /// Create a configuration for a specific client language.\n    /// Determines the appropriate run command based on the language and package manager.\n    pub fn for_client_lang(client_lang: &str, package_manager: Option<PackageManager>) -> Self {\n        let run_command = match client_lang.to_lowercase().as_str() {\n            \"typescript\" => package_manager.map(|pm| pm.run_dev_command()).unwrap_or(\"npm run dev\"),\n            \"rust\" => \"cargo run\",\n            \"csharp\" | \"c#\" => \"dotnet run\",\n            _ => \"npm run dev\", // default fallback\n        };\n        Self {\n            dev: Some(DevConfig {\n                run: Some(run_command.to_string()),\n            }),\n            ..Default::default()\n        }\n    }\n\n    /// Load configuration from a directory.\n    /// Returns `None` if no config file exists.\n    pub fn load_from_dir(dir: &Path) -> anyhow::Result<Option<Self>> {\n        let config_path = dir.join(CONFIG_FILENAME);\n        if config_path.exists() {\n            Self::load(&config_path).map(Some)\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Save configuration to `spacetime.json` in the specified directory.\n    pub fn save_to_dir(&self, dir: &Path) -> anyhow::Result<PathBuf> {\n        let path = dir.join(CONFIG_FILENAME);\n        self.save(&path)?;\n        Ok(path)\n    }\n}\n\n/// Find the config directory by walking up from start_dir looking for spacetime.json.\nfn find_config_dir(start_dir: PathBuf) -> Option<PathBuf> {\n    let mut current_dir = start_dir;\n    loop {\n        let config_path = current_dir.join(\"spacetime.json\");\n        if config_path.exists() {\n            return Some(current_dir);\n        }\n        if !current_dir.pop() {\n            break;\n        }\n    }\n    None\n}\n\n/// Load a JSON5 file as a serde_json::Value, or None if the file doesn't exist.\nfn load_json_value(path: &Path) -> anyhow::Result<Option<serde_json::Value>> {\n    if !path.exists() {\n        return Ok(None);\n    }\n    let content =\n        std::fs::read_to_string(path).with_context(|| format!(\"Failed to read config file: {}\", path.display()))?;\n\n    // In one of the releases we mistakenly save _source-config field into the JSON file\n    // Check if the field exists and remove it. We use text-based removal to preserve\n    // comments and formatting since json5 crate doesn't support serialization.\n    remove_source_config_from_text(path, &content);\n\n    let value: serde_json::Value = json5::from_str(&content)\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse config file {}: {}\", path.display(), e))?;\n    Ok(Some(value))\n}\n\nconst SOURCE_CONFIG_KEY: &str = \"_source-config\";\n\n/// Remove _source-config field from JSON text using regex\n/// This preserves comments and formatting in the file\nfn remove_source_config_from_text(path: &Path, content: &str) {\n    if !content.contains(SOURCE_CONFIG_KEY) {\n        return;\n    }\n\n    use regex::Regex;\n\n    // Match \"_source-config\": \"value\", or \"_source-config\": \"value\"\n    // Handles trailing comma and various whitespace patterns\n    let re = Regex::new(r#\"(?m)^\\s*\"_source-config\"\\s*:\\s*\"[^\"]*\"\\s*,?\\s*$\\n?\"#).unwrap();\n    let cleaned = re.replace_all(content, \"\");\n\n    // Also remove trailing commas that might be left behind before closing braces\n    let re_trailing = Regex::new(r#\"(?m),(\\s*[\\]}])\"#).unwrap();\n    let cleaned_content = re_trailing.replace_all(&cleaned, \"$1\").to_string();\n\n    // Validate that the cleaned content is still valid JSON5\n    // If validation fails, don't save\n    if json5::from_str::<serde_json::Value>(&cleaned_content).is_err() {\n        return;\n    }\n\n    // Write the cleaned content back to the file (best effort, ignore errors)\n    let _ = std::fs::write(path, &cleaned_content);\n}\n\nfn mark_source_config(value: &mut serde_json::Value, source_file_name: &str) {\n    if let Some(obj) = value.as_object_mut() {\n        if obj.contains_key(\"database\") {\n            obj.insert(\n                SOURCE_CONFIG_KEY.to_string(),\n                serde_json::Value::String(source_file_name.to_string()),\n            );\n        }\n        if let Some(serde_json::Value::Array(children)) = obj.get_mut(\"children\") {\n            for child in children {\n                mark_source_config(child, source_file_name);\n            }\n        }\n    }\n}\n\nfn overlay_children_arrays(\n    base_children: &mut [serde_json::Value],\n    overlay_children: Vec<serde_json::Value>,\n    source_file_name: &str,\n) {\n    let merge_len = std::cmp::min(base_children.len(), overlay_children.len());\n    for (idx, overlay_child) in overlay_children.into_iter().enumerate().take(merge_len) {\n        let base_child = &mut base_children[idx];\n        match overlay_child {\n            serde_json::Value::Object(_) if base_child.is_object() => {\n                // Recursively apply overlay semantics to child objects.\n                overlay_json(base_child, overlay_child, source_file_name);\n            }\n            other => {\n                // For non-object child entries, replace value directly.\n                *base_child = other;\n            }\n        }\n    }\n}\n\n/// Overlay `overlay` values onto `base`.\n/// Most keys use top-level replacement. `children` is merged recursively by index,\n/// up to the lower of base/overlay lengths.\nfn overlay_json(base: &mut serde_json::Value, mut overlay: serde_json::Value, source_file_name: &str) {\n    mark_source_config(&mut overlay, source_file_name);\n    if let (Some(base_obj), Some(overlay_obj)) = (base.as_object_mut(), overlay.as_object()) {\n        for (key, value) in overlay_obj {\n            let value_owned = value.clone();\n            if key == \"children\" {\n                match (base_obj.get_mut(\"children\"), value_owned) {\n                    (Some(serde_json::Value::Array(base_children)), serde_json::Value::Array(overlay_children)) => {\n                        overlay_children_arrays(base_children, overlay_children, source_file_name);\n                    }\n                    (_, other) => {\n                        base_obj.insert(key.clone(), other);\n                    }\n                }\n            } else {\n                base_obj.insert(key.clone(), value_owned);\n            }\n        }\n    }\n}\n\n/// Find and load config with environment layering from the current directory.\n///\n/// Loading order (each overlays the previous via top-level key replacement):\n/// 1. `spacetime.json` (required)\n/// 2. `spacetime.local.json` (if exists)\n/// 3. `spacetime.<env>.json` (if env specified and file exists)\n/// 4. `spacetime.<env>.local.json` (if env specified and file exists)\npub fn find_and_load_with_env(env: Option<&str>) -> anyhow::Result<Option<LoadedConfig>> {\n    find_and_load_with_env_from(env, std::env::current_dir()?)\n}\n\n/// Find and load config with environment layering starting from a specific directory.\npub fn find_and_load_with_env_from(env: Option<&str>, start_dir: PathBuf) -> anyhow::Result<Option<LoadedConfig>> {\n    let config_dir = match find_config_dir(start_dir) {\n        Some(dir) => dir,\n        None => return Ok(None),\n    };\n\n    let base_path = config_dir.join(\"spacetime.json\");\n    let mut merged = load_json_value(&base_path)?\n        .ok_or_else(|| anyhow::anyhow!(\"spacetime.json not found in {}\", config_dir.display()))?;\n    mark_source_config(&mut merged, \"spacetime.json\");\n\n    let mut loaded_files = vec![base_path];\n    let mut has_dev_file = false;\n\n    // Overlay local file\n    let local_path = config_dir.join(\"spacetime.local.json\");\n    if let Some(local_value) = load_json_value(&local_path)? {\n        overlay_json(&mut merged, local_value, \"spacetime.local.json\");\n        loaded_files.push(local_path);\n    }\n\n    // Overlay environment-specific file\n    if let Some(env_name) = env {\n        let env_path = config_dir.join(format!(\"spacetime.{env_name}.json\"));\n        if let Some(env_value) = load_json_value(&env_path)? {\n            overlay_json(&mut merged, env_value, &format!(\"spacetime.{env_name}.json\"));\n            loaded_files.push(env_path);\n            if env_name == \"dev\" {\n                has_dev_file = true;\n            }\n        }\n    }\n\n    // Overlay environment-specific local file\n    if let Some(env_name) = env {\n        let env_local_path = config_dir.join(format!(\"spacetime.{env_name}.local.json\"));\n        if let Some(env_local_value) = load_json_value(&env_local_path)? {\n            overlay_json(\n                &mut merged,\n                env_local_value,\n                &format!(\"spacetime.{env_name}.local.json\"),\n            );\n            loaded_files.push(env_local_path);\n            if env_name == \"dev\" {\n                has_dev_file = true;\n            }\n        }\n    }\n\n    let config: SpacetimeConfig = serde_json::from_value(merged).context(\"Failed to deserialize merged config\")?;\n\n    Ok(Some(LoadedConfig {\n        config,\n        config_dir,\n        loaded_files,\n        has_dev_file,\n    }))\n}\n\n/// Set up a spacetime.json config for a project.\n/// If `client_lang` is provided, creates a config for that language.\n/// Otherwise, attempts to auto-detect from package.json.\n/// Returns the path to the created config, or None if no config was created.\npub fn setup_for_project(\n    project_path: &Path,\n    client_lang: Option<&str>,\n    package_manager: Option<PackageManager>,\n) -> anyhow::Result<Option<PathBuf>> {\n    if project_path.join(CONFIG_FILENAME).exists() {\n        return Ok(None);\n    }\n\n    if let Some(lang) = client_lang {\n        let config = SpacetimeConfig::for_client_lang(lang, package_manager);\n        return Ok(Some(config.save_to_dir(project_path)?));\n    }\n\n    if let Some((detected_cmd, _)) = detect_client_command(project_path) {\n        return Ok(Some(\n            SpacetimeConfig::with_run_command(&detected_cmd).save_to_dir(project_path)?,\n        ));\n    }\n\n    Ok(None)\n}\n\n/// Detect the package manager from lock files in the project directory.\npub fn detect_package_manager(project_dir: &Path) -> Option<PackageManager> {\n    // Check for lock files in order of preference\n    if project_dir.join(\"pnpm-lock.yaml\").exists() {\n        return Some(PackageManager::Pnpm);\n    }\n    if project_dir.join(\"yarn.lock\").exists() {\n        return Some(PackageManager::Yarn);\n    }\n    if project_dir.join(\"bun.lockb\").exists() || project_dir.join(\"bun.lock\").exists() {\n        return Some(PackageManager::Bun);\n    }\n    if project_dir.join(\"package-lock.json\").exists() {\n        return Some(PackageManager::Npm);\n    }\n    // Default to npm if package.json exists but no lock file\n    if project_dir.join(\"package.json\").exists() {\n        return Some(PackageManager::Npm);\n    }\n    None\n}\n\n/// Simple auto-detection for projects without `spacetime.json`.\n/// Returns the client command and optionally the detected package manager.\npub fn detect_client_command(project_dir: &Path) -> Option<(String, Option<PackageManager>)> {\n    // JavaScript/TypeScript: package.json with \"dev\" script\n    let package_json = project_dir.join(\"package.json\");\n    if package_json.exists()\n        && let Ok(content) = fs::read_to_string(&package_json)\n        && let Ok(json) = serde_json::from_str::<serde_json::Value>(&content)\n    {\n        let has_dev = json.get(\"scripts\").and_then(|s| s.get(\"dev\")).is_some();\n        if has_dev {\n            let pm = detect_package_manager(project_dir);\n            let cmd = pm.map(|p| p.run_dev_command()).unwrap_or(\"npm run dev\");\n            return Some((cmd.to_string(), pm));\n        }\n    }\n\n    // Rust: Cargo.toml\n    if project_dir.join(\"Cargo.toml\").exists() {\n        return Some((\"cargo run\".to_string(), None));\n    }\n\n    // C#: .csproj file\n    if let Ok(entries) = fs::read_dir(project_dir) {\n        for entry in entries.flatten() {\n            if entry.path().extension().is_some_and(|e| e == \"csproj\") {\n                return Some((\"dotnet run\".to_string(), None));\n            }\n        }\n    }\n\n    None\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use clap::Arg;\n\n    #[test]\n    fn test_deserialize_full_config() {\n        let json = r#\"{\n            \"dev\": {\n                \"run\": \"pnpm dev\"\n            },\n            \"database\": \"bitcraft\",\n            \"module-path\": \"spacetimedb\",\n            \"server\": \"local\",\n            \"generate\": [\n                {\n                    \"out-dir\": \"./foobar\",\n                    \"module-path\": \"region-module\",\n                    \"language\": \"csharp\"\n                },\n                {\n                    \"out-dir\": \"./global\",\n                    \"module-path\": \"global-module\",\n                    \"language\": \"csharp\"\n                }\n            ],\n            \"children\": [\n                {\n                    \"database\": \"region-1\",\n                    \"module-path\": \"region-module\"\n                },\n                {\n                    \"database\": \"region-2\",\n                    \"module-path\": \"region-module\"\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n\n        assert_eq!(config.dev.as_ref().and_then(|d| d.run.as_deref()), Some(\"pnpm dev\"));\n\n        let generate = config.generate.as_ref().unwrap();\n        assert_eq!(generate.len(), 2);\n        assert_eq!(generate[0].get(\"out-dir\").and_then(|v| v.as_str()), Some(\"./foobar\"));\n        assert_eq!(generate[0].get(\"language\").and_then(|v| v.as_str()), Some(\"csharp\"));\n\n        assert_eq!(\n            config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"bitcraft\")\n        );\n        assert_eq!(\n            config.additional_fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"spacetimedb\")\n        );\n\n        let children = config.children.as_ref().unwrap();\n        assert_eq!(children.len(), 2);\n        assert_eq!(\n            children[0].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"region-1\")\n        );\n        assert_eq!(\n            children[1].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"region-2\")\n        );\n    }\n\n    #[test]\n    fn test_deserialize_with_comments() {\n        let json = r#\"{\n            // This is a comment\n            \"dev\": {\n                \"run\": \"npm start\"\n            },\n            /* Multi-line comment */\n            \"generate\": [\n                {\n                    \"out-dir\": \"./src/bindings\", // inline comment\n                    \"language\": \"typescript\"\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        assert_eq!(config.dev.as_ref().and_then(|d| d.run.as_deref()), Some(\"npm start\"));\n    }\n\n    #[test]\n    fn test_minimal_config() {\n        let json = r#\"{}\"#;\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n\n        assert!(config.dev.is_none());\n        assert!(config.generate.is_none());\n        assert!(config.children.is_none());\n        assert!(config.additional_fields.is_empty());\n    }\n\n    #[test]\n    fn test_project_config_builder() {\n        use clap::{Arg, Command};\n\n        // Create a simple clap command with some arguments\n        let cmd = Command::new(\"test\")\n            .arg(Arg::new(\"out-dir\").long(\"out-dir\").value_name(\"DIR\"))\n            .arg(Arg::new(\"lang\").long(\"lang\").value_name(\"LANG\"))\n            .arg(Arg::new(\"server\").long(\"server\").value_name(\"SERVER\"));\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--out-dir\", \"./bindings\", \"--lang\", \"typescript\"]);\n\n        // Build schema\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"language\").from_clap(\"lang\"))\n            .key(Key::new(\"out-dir\"))\n            .key(Key::new(\"server\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Simulate config file values\n        let mut config_values = HashMap::new();\n        config_values.insert(\"language\".to_string(), Value::String(\"rust\".to_string()));\n        config_values.insert(\"server\".to_string(), Value::String(\"local\".to_string()));\n\n        // Create CommandConfig with schema\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // CLI args should override config values\n        assert_eq!(\n            command_config.get_one::<String>(\"out-dir\").unwrap(),\n            Some(\"./bindings\".to_string())\n        );\n        assert_eq!(\n            command_config.get_one::<String>(\"language\").unwrap(),\n            Some(\"typescript\".to_string())\n        ); // CLI overrides (use config name, not clap name)\n        assert_eq!(\n            command_config.get_one::<String>(\"server\").unwrap(),\n            Some(\"local\".to_string())\n        ); // from config\n    }\n\n    #[test]\n    fn test_database_entity_config_extraction() {\n        use clap::{Arg, Command};\n\n        // Parse a database entity config from JSON (database-centric model)\n        let json = r#\"{\n            \"database\": \"my-database\",\n            \"server\": \"local\",\n            \"module-path\": \"./my-module\",\n            \"build-options\": \"--features extra\",\n            \"break-clients\": true,\n            \"anonymous\": false\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n\n        // Verify children field\n        assert!(config.children.is_none());\n\n        // Verify all fields are in additional_fields\n        assert_eq!(\n            config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"my-database\")\n        );\n        assert_eq!(\n            config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local\")\n        );\n        assert_eq!(\n            config.additional_fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./my-module\")\n        );\n        assert_eq!(\n            config.additional_fields.get(\"build-options\").and_then(|v| v.as_str()),\n            Some(\"--features extra\")\n        );\n        assert_eq!(\n            config.additional_fields.get(\"break-clients\").and_then(|v| v.as_bool()),\n            Some(true)\n        );\n\n        // Now test merging with clap args\n        let cmd = Command::new(\"test\")\n            .arg(Arg::new(\"database\").long(\"database\"))\n            .arg(Arg::new(\"server\").long(\"server\"))\n            .arg(Arg::new(\"module_path\").long(\"module-path\"))\n            .arg(Arg::new(\"build_options\").long(\"build-options\"))\n            .arg(Arg::new(\"break_clients\").long(\"break-clients\"))\n            .arg(Arg::new(\"anon_identity\").long(\"anonymous\"));\n\n        // CLI overrides the server\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"--server\", \"maincloud\"]);\n\n        // Build schema with snake_case keys\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\"))\n            .key(Key::new(\"server\"))\n            .key(Key::new(\"module_path\"))\n            .key(Key::new(\"build_options\"))\n            .key(Key::new(\"break_clients\"))\n            // Config uses \"anonymous\", clap uses \"anon_identity\"\n            .key(Key::new(\"anonymous\").from_clap(\"anon_identity\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Just pass the additional_fields directly - they will be normalized from kebab to snake_case\n        let command_config = CommandConfig::new(&schema, config.additional_fields, &matches).unwrap();\n\n        // database comes from config\n        assert_eq!(\n            command_config.get_one::<String>(\"database\").unwrap(),\n            Some(\"my-database\".to_string())\n        );\n        // server comes from CLI (overrides config)\n        assert_eq!(\n            command_config.get_one::<String>(\"server\").unwrap(),\n            Some(\"maincloud\".to_string())\n        );\n        // module_path comes from config (kebab-case in JSON was normalized to snake_case)\n        assert_eq!(\n            command_config.get_one::<String>(\"module_path\").unwrap(),\n            Some(\"./my-module\".to_string())\n        );\n        // build_options comes from config\n        assert_eq!(\n            command_config.get_one::<String>(\"build_options\").unwrap(),\n            Some(\"--features extra\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_schema_missing_key_definition_error() {\n        use clap::{Arg, Command};\n\n        // Define clap command with some arguments\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(Arg::new(\"yes\").long(\"yes\").action(clap::ArgAction::SetTrue));\n\n        // Try to build schema but don't define all keys (missing \"server\" key)\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"yes\"))\n            // Missing .key(Key::new(\"server\"))\n            .build(&cmd);\n\n        // This should error because \"server\" is in clap but not defined in the builder\n        // and not excluded\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::ClapArgNotDefined { arg_name } if arg_name == \"server\"\n        ));\n    }\n\n    #[test]\n    fn test_key_with_clap_name_mapping() {\n        use clap::{Arg, Command};\n\n        // Clap uses \"project-path\" but config uses \"module-path\"\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"project-path\")\n                .long(\"project-path\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--project-path\", \"./my-project\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module_path\").from_clap(\"project-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config file uses \"module-path\" (kebab-case, will be normalized to module_path)\n        let mut config_values = HashMap::new();\n        config_values.insert(\"module-path\".to_string(), Value::String(\"./config-project\".to_string()));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // CLI should override config, accessed via config name \"module_path\" (snake_case)\n        assert_eq!(\n            command_config.get_one::<String>(\"module_path\").unwrap(),\n            Some(\"./my-project\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_clap_argument_with_alias() {\n        use clap::{Arg, Command};\n\n        // Argument with both long name and alias\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"module-path\")\n                .long(\"module-path\")\n                .alias(\"project-path\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        // Use the alias\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--project-path\", \"./my-project\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        // Should be accessible via the primary name\n        assert_eq!(\n            command_config.get_one::<String>(\"module-path\").unwrap(),\n            Some(\"./my-project\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_optional_argument_not_provided() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"server\")\n                .long(\"server\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new().key(Key::new(\"server\")).build(&cmd).unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        // Should return Ok(None) when optional argument not provided\n        assert_eq!(command_config.get_one::<String>(\"server\").unwrap(), None);\n    }\n\n    #[test]\n    fn test_alias_support() {\n        use clap::{Arg, Command};\n\n        // Clap has both module-path and deprecated project-path\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"module-path\")\n                    .long(\"module-path\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"project-path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        // User uses the deprecated --project-path flag\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--project-path\", \"./deprecated\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\").alias(\"project-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        // Should be able to get the value via the canonical name\n        assert_eq!(\n            command_config.get_one::<String>(\"module-path\").unwrap(),\n            Some(\"./deprecated\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_alias_canonical_takes_precedence() {\n        use clap::{Arg, Command};\n\n        // Clap has both module-path and deprecated project-path\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"module-path\")\n                    .long(\"module-path\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"project-path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        // User provides BOTH flags (shouldn't happen but let's test precedence)\n        let matches = cmd.clone().get_matches_from(vec![\n            \"test\",\n            \"--module-path\",\n            \"./canonical\",\n            \"--project-path\",\n            \"./deprecated\",\n        ]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\").alias(\"project-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        // Canonical name should take precedence\n        assert_eq!(\n            command_config.get_one::<String>(\"module-path\").unwrap(),\n            Some(\"./canonical\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_alias_with_config_fallback() {\n        use clap::{Arg, Command};\n\n        // Clap has both module_path and deprecated project-path as alias\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"module_path\")\n                    .long(\"module-path\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"project-path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        // User doesn't provide CLI args\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module_path\").alias(\"project-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config has the value (kebab-case will be normalized)\n        let mut config_values = HashMap::new();\n        config_values.insert(\"module-path\".to_string(), Value::String(\"./from-config\".to_string()));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should fall back to config\n        assert_eq!(\n            command_config.get_one::<String>(\"module_path\").unwrap(),\n            Some(\"./from-config\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_schema_invalid_from_clap_reference() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"server\")\n                .long(\"server\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        // Try to map to a non-existent clap arg\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\").from_clap(\"non-existent\"))\n            .exclude(\"server\") // Exclude the server arg we're not using\n            .build(&cmd);\n\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::InvalidClapReference { config_name, clap_name }\n                if config_name == \"module-path\" && clap_name == \"non-existent\"\n        ));\n    }\n\n    #[test]\n    fn test_schema_invalid_alias_reference() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"module-path\")\n                .long(\"module-path\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        // Try to alias a non-existent clap arg\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\").alias(\"non-existent-alias\"))\n            .build(&cmd);\n\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::InvalidAliasReference { config_name, alias }\n                if config_name == \"module-path\" && alias == \"non-existent-alias\"\n        ));\n    }\n\n    #[test]\n    fn test_undefined_config_key_error() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"server\")\n                .long(\"server\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new().key(Key::new(\"server\")).build(&cmd).unwrap();\n\n        // Config has a key that's not defined in CommandConfig\n        let mut config_values = HashMap::new();\n        config_values.insert(\"server\".to_string(), Value::String(\"local\".to_string()));\n        config_values.insert(\"undefined-key\".to_string(), Value::String(\"value\".to_string()));\n\n        let result = CommandConfig::new(&schema, config_values, &matches);\n\n        // After normalization, \"undefined-key\" becomes \"undefined_key\"\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::UnsupportedConfigKey { config_key, .. }\n                if config_key == \"undefined_key\"\n        ));\n    }\n\n    #[test]\n    fn test_schema_from_clap_with_wrong_arg_name() {\n        use clap::{Arg, Command};\n\n        // Command has \"lang\" argument\n        let cmd = Command::new(\"test\").arg(Arg::new(\"lang\").long(\"lang\").value_parser(clap::value_parser!(String)));\n\n        // Try to create a key that references \"language\" via from_clap, but clap has \"lang\"\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"lang\").from_clap(\"language\"))\n            .build(&cmd);\n\n        // Should fail because \"language\" doesn't exist in the Command\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::InvalidClapReference { config_name, clap_name }\n                if config_name == \"lang\" && clap_name == \"language\"\n        ));\n    }\n\n    #[test]\n    fn test_excluded_key_in_config_should_error() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(Arg::new(\"yes\").long(\"yes\").action(clap::ArgAction::SetTrue))\n            .arg(Arg::new(\"server\").long(\"server\").value_name(\"SERVER\"));\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"server\"))\n            .exclude(\"yes\")\n            .build(&cmd)\n            .unwrap();\n\n        // Config has yes, which is excluded\n        let mut config_values = HashMap::new();\n        config_values.insert(\"yes\".to_string(), Value::Bool(true));\n        config_values.insert(\"server\".to_string(), Value::String(\"local\".to_string()));\n\n        let result = CommandConfig::new(&schema, config_values, &matches);\n\n        // Should error because \"yes\" is excluded and shouldn't be in config\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::UnsupportedConfigKey { config_key, .. }\n                if config_key == \"yes\"\n        ));\n    }\n\n    #[test]\n    fn test_schema_get_clap_arg() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(Arg::new(\"port\").long(\"port\").value_parser(clap::value_parser!(i64)));\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--server\", \"localhost\", \"--port\", \"8080\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"server\"))\n            .key(Key::new(\"port\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Should get values from CLI\n        assert_eq!(\n            schema.get_clap_arg::<String>(&matches, \"server\").unwrap(),\n            Some(\"localhost\".to_string())\n        );\n        assert_eq!(schema.get_clap_arg::<i64>(&matches, \"port\").unwrap(), Some(8080));\n    }\n\n    #[test]\n    fn test_schema_is_from_cli() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(Arg::new(\"port\").long(\"port\").value_parser(clap::value_parser!(i64)));\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"--server\", \"localhost\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"server\"))\n            .key(Key::new(\"port\"))\n            .build(&cmd)\n            .unwrap();\n\n        // server was provided via CLI\n        assert!(schema.is_from_cli(&matches, \"server\"));\n        // port was not provided\n        assert!(!schema.is_from_cli(&matches, \"port\"));\n    }\n\n    #[test]\n    fn test_schema_module_specific_cli_args() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"module-path\")\n                    .long(\"module-path\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"database\")\n                    .long(\"database\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--module-path\", \"./module\", \"--server\", \"local\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"server\"))\n            .key(Key::new(\"module-path\").module_specific())\n            .key(Key::new(\"database\"))\n            .build(&cmd)\n            .unwrap();\n\n        let module_specific = schema.module_specific_cli_args(&matches);\n        assert_eq!(module_specific.len(), 1);\n        assert!(module_specific.contains(&\"module-path\"));\n    }\n\n    #[test]\n    fn test_schema_get_clap_arg_with_from_clap() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(Arg::new(\"name\").long(\"name\").value_parser(clap::value_parser!(String)));\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"--name\", \"my-db\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\").from_clap(\"name\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Should get value using config name, which maps to clap arg \"name\"\n        assert_eq!(\n            schema.get_clap_arg::<String>(&matches, \"database\").unwrap(),\n            Some(\"my-db\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_schema_get_clap_arg_with_alias() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"module-path\")\n                    .long(\"module-path\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"project-path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--project-path\", \"./my-project\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"module-path\").alias(\"project-path\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Should get value from alias\n        assert_eq!(\n            schema.get_clap_arg::<String>(&matches, \"module-path\").unwrap(),\n            Some(\"./my-project\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_schema_invalid_exclusion() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"server\")\n                .long(\"server\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        // Try to exclude a non-existent arg\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"server\"))\n            .exclude(\"non-existent\")\n            .build(&cmd);\n\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::InvalidExclusion { key } if key == \"non-existent\"\n        ));\n    }\n\n    #[test]\n    fn test_config_value_type_conversion_error() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(Arg::new(\"port\").long(\"port\").value_parser(clap::value_parser!(i64)));\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new().key(Key::new(\"port\")).build(&cmd).unwrap();\n\n        // Config has a string value for port, but clap expects i64\n        let mut config_values = HashMap::new();\n        config_values.insert(\"port\".to_string(), Value::String(\"not-a-number\".to_string()));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should error when trying to convert invalid value\n        let result = command_config.get_one::<i64>(\"port\");\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::ConversionError { key, target_type, .. }\n                if key == \"port\" && target_type.contains(\"i64\")\n        ));\n    }\n\n    #[test]\n    fn test_validate_required_key_missing() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"database\")\n                    .long(\"database\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\").required())\n            .key(Key::new(\"server\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config is missing the required \"database\" key\n        let config_values = HashMap::new();\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should error on validation\n        let result = command_config.validate();\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::MissingRequiredKey { key }\n                if key == \"database\"\n        ));\n    }\n\n    #[test]\n    fn test_validate_required_key_present() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"database\")\n                    .long(\"database\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\").required())\n            .key(Key::new(\"server\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config has the required database key\n        let mut config_values = HashMap::new();\n        config_values.insert(\"database\".to_string(), Value::String(\"my-db\".to_string()));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should succeed on validation\n        assert!(command_config.validate().is_ok());\n    }\n\n    #[test]\n    fn test_validate_no_required_keys() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"server\")\n                .long(\"server\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new().key(Key::new(\"server\")).build(&cmd).unwrap();\n\n        // No required keys, empty config should be fine\n        let config_values = HashMap::new();\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should succeed on validation\n        assert!(command_config.validate().is_ok());\n    }\n\n    #[test]\n    fn test_default_values_not_treated_as_cli() {\n        use clap::{Arg, Command};\n        use std::path::PathBuf;\n\n        // Create a command with a default value\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"project_path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(PathBuf))\n                    .default_value(\".\"),\n            )\n            .arg(\n                Arg::new(\"build_options\")\n                    .long(\"build-options\")\n                    .value_parser(clap::value_parser!(String))\n                    .default_value(\"\"),\n            );\n\n        // Get matches WITHOUT providing the arguments\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"project_path\"))\n            .key(Key::new(\"build_options\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config file has values\n        let mut config_values = HashMap::new();\n        config_values.insert(\"project_path\".to_string(), Value::String(\"./my-module\".to_string()));\n        config_values.insert(\"build_options\".to_string(), Value::String(\"--release\".to_string()));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Default values should NOT override config values\n        assert_eq!(\n            command_config.get_one::<PathBuf>(\"project_path\").unwrap(),\n            Some(PathBuf::from(\"./my-module\"))\n        );\n        assert_eq!(\n            command_config.get_one::<String>(\"build_options\").unwrap(),\n            Some(\"--release\".to_string())\n        );\n\n        // is_from_cli should return false for default values\n        assert!(!schema.is_from_cli(&matches, \"project_path\"));\n        assert!(!schema.is_from_cli(&matches, \"build_options\"));\n    }\n\n    #[test]\n    fn test_module_specific_only_checks_cli() {\n        use clap::{Arg, Command};\n        use std::path::PathBuf;\n\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"project_path\")\n                    .long(\"project-path\")\n                    .value_parser(clap::value_parser!(PathBuf))\n                    .default_value(\".\"),\n            )\n            .arg(\n                Arg::new(\"build_options\")\n                    .long(\"build-options\")\n                    .value_parser(clap::value_parser!(String))\n                    .default_value(\"\"),\n            );\n\n        // Test 1: No CLI args provided (only defaults)\n        let matches_no_cli = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"project_path\").module_specific())\n            .key(Key::new(\"build_options\").module_specific())\n            .build(&cmd)\n            .unwrap();\n\n        // module_specific_cli_args should be empty when only defaults are present\n        let module_specific = schema.module_specific_cli_args(&matches_no_cli);\n        assert!(module_specific.is_empty());\n\n        // Test 2: CLI args actually provided\n        let matches_with_cli = cmd.clone().get_matches_from(vec![\n            \"test\",\n            \"--project-path\",\n            \"./custom\",\n            \"--build-options\",\n            \"release-mode\",\n        ]);\n\n        let module_specific = schema.module_specific_cli_args(&matches_with_cli);\n        assert_eq!(module_specific.len(), 2);\n        assert!(module_specific.contains(&\"project_path\"));\n        assert!(module_specific.contains(&\"build_options\"));\n    }\n\n    #[test]\n    fn test_validate_module_specific_uses_user_facing_flag_names() {\n        use clap::{Arg, Command};\n        use std::path::PathBuf;\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"wasm_file\")\n                .long(\"bin-path\")\n                .value_parser(clap::value_parser!(PathBuf)),\n        );\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"test\", \"--bin-path\", \"./module.wasm\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"wasm_file\").module_specific())\n            .build(&cmd)\n            .unwrap();\n\n        let err = schema\n            .validate_no_module_specific_cli_args_for_multiple_targets(\n                &cmd,\n                &matches,\n                2,\n                \"testing multiple targets\",\n                \"Select a single target.\",\n            )\n            .unwrap_err();\n        let err_msg = err.to_string();\n        assert!(\n            err_msg.contains(\"--bin-path\"),\n            \"Expected --bin-path in error, got: {err_msg}\"\n        );\n    }\n\n    #[test]\n    fn test_kebab_case_normalization() {\n        use clap::{Arg, Command};\n\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"build_options\")\n                .long(\"build-options\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"build_options\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config file uses kebab-case\n        let mut config_values = HashMap::new();\n        config_values.insert(\"build-options\".to_string(), Value::String(\"--release\".to_string()));\n\n        // The normalization in CommandConfig::new should convert build-options to build_options\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        // Should be able to access via snake_case key\n        assert_eq!(\n            command_config.get_one::<String>(\"build_options\").unwrap(),\n            Some(\"--release\".to_string())\n        );\n    }\n\n    // CommandSchema Tests\n\n    #[test]\n    fn test_invalid_clap_reference_caught() {\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"valid_arg\")\n                .long(\"valid-arg\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let result = CommandSchemaBuilder::new().key(Key::new(\"nonexistent_arg\")).build(&cmd);\n\n        assert!(result.is_err());\n        assert!(matches!(\n            result.unwrap_err(),\n            CommandConfigError::InvalidClapReference { .. }\n        ));\n    }\n\n    #[test]\n    fn test_invalid_alias_reference_caught() {\n        let cmd = Command::new(\"test\").arg(Arg::new(\"name\").long(\"name\").value_parser(clap::value_parser!(String)));\n\n        // Reference a valid arg (name) but add invalid alias (nonexistent) via .alias()\n        let result = CommandSchemaBuilder::new()\n            .key(Key::new(\"my_key\").from_clap(\"name\").alias(\"nonexistent\"))\n            .build(&cmd);\n\n        assert!(result.is_err());\n        let err = result.unwrap_err();\n        assert!(matches!(err, CommandConfigError::InvalidAliasReference { .. }));\n    }\n\n    // CommandConfig Tests\n\n    #[test]\n    fn test_get_one_returns_none_when_missing_from_both_sources() {\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"some_arg\")\n                .long(\"some-arg\")\n                .value_parser(clap::value_parser!(String)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"some_arg\"))\n            .build(&cmd)\n            .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        assert_eq!(command_config.get_one::<String>(\"some_arg\").unwrap(), None);\n    }\n\n    #[test]\n    fn test_get_one_with_aliased_keys() {\n        let cmd = Command::new(\"test\").arg(Arg::new(\"name|identity\").value_parser(clap::value_parser!(String)));\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"my-database\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\").from_clap(\"name|identity\"))\n            .build(&cmd)\n            .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        assert_eq!(\n            command_config.get_one::<String>(\"database\").unwrap(),\n            Some(\"my-database\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_is_from_cli_identifies_sources_correctly() {\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"cli_arg\")\n                    .long(\"cli-arg\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"default_arg\")\n                    .long(\"default-arg\")\n                    .default_value(\"default\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"config_arg\")\n                    .long(\"config-arg\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"--cli-arg\", \"from-cli\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"cli_arg\"))\n            .key(Key::new(\"default_arg\"))\n            .key(Key::new(\"config_arg\"))\n            .build(&cmd)\n            .unwrap();\n\n        // CLI arg should be detected\n        assert!(schema.is_from_cli(&matches, \"cli_arg\"));\n\n        // Default arg should NOT be detected as CLI\n        assert!(!schema.is_from_cli(&matches, \"default_arg\"));\n\n        // Config arg (not provided anywhere) should NOT be detected as CLI\n        assert!(!schema.is_from_cli(&matches, \"config_arg\"));\n    }\n\n    // SpacetimeConfig Tests\n\n    #[test]\n    fn test_find_and_load_walks_up_directory_tree() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n        let subdir1 = root.join(\"level1\");\n        let subdir2 = subdir1.join(\"level2\");\n        fs::create_dir_all(&subdir2).unwrap();\n\n        // Create config in root\n        let config = SpacetimeConfig {\n            dev: Some(DevConfig {\n                run: Some(\"test\".to_string()),\n            }),\n            ..Default::default()\n        };\n        config.save(&root.join(\"spacetime.json\")).unwrap();\n\n        // Search from subdir2 - should find config in root\n        let result = SpacetimeConfig::find_and_load_from(subdir2).unwrap();\n        assert!(result.is_some());\n        let (found_path, found_config) = result.unwrap();\n        assert_eq!(found_path, root.join(\"spacetime.json\"));\n        assert_eq!(found_config.dev.as_ref().and_then(|d| d.run.as_deref()), Some(\"test\"));\n    }\n\n    #[test]\n    fn test_malformed_json_returns_error() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let config_path = temp.path().join(\"spacetime.json\");\n\n        fs::write(&config_path, \"{ invalid json }\").unwrap();\n\n        let result = SpacetimeConfig::find_and_load_from(temp.path().to_path_buf());\n        assert!(result.is_err());\n    }\n\n    #[test]\n    fn test_missing_file_returns_none() {\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n\n        let result = SpacetimeConfig::find_and_load_from(temp.path().to_path_buf()).unwrap();\n        assert!(result.is_none());\n    }\n\n    #[test]\n    fn test_empty_config_file_handled() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let config_path = temp.path().join(\"spacetime.json\");\n\n        fs::write(&config_path, \"{}\").unwrap();\n\n        let result = SpacetimeConfig::find_and_load_from(temp.path().to_path_buf()).unwrap();\n        assert!(result.is_some());\n        let (_, config) = result.unwrap();\n        assert!(config.dev.is_none());\n        assert!(config.children.is_none());\n        assert!(config.generate.is_none());\n    }\n\n    #[test]\n    fn test_serde_deserialize_u8_from_config() {\n        // Verifies that serde_json::from_value handles u8 (num_replicas) correctly,\n        // which was broken with the old TypeId-based approach.\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"num_replicas\")\n                .long(\"num-replicas\")\n                .value_parser(clap::value_parser!(u8)),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"num_replicas\"))\n            .build(&cmd)\n            .unwrap();\n\n        let mut config_values = HashMap::new();\n        config_values.insert(\"num_replicas\".to_string(), Value::Number(serde_json::Number::from(3u8)));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        assert_eq!(command_config.get_one::<u8>(\"num_replicas\").unwrap(), Some(3u8));\n    }\n\n    #[test]\n    fn test_serde_deserialize_bool_from_config() {\n        // Verifies that bool values (like include_private) can be read from config.\n        let cmd = Command::new(\"test\").arg(\n            Arg::new(\"include_private\")\n                .long(\"include-private\")\n                .action(clap::ArgAction::SetTrue),\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"include_private\"))\n            .build(&cmd)\n            .unwrap();\n\n        let mut config_values = HashMap::new();\n        config_values.insert(\"include_private\".to_string(), Value::Bool(true));\n\n        let command_config = CommandConfig::new(&schema, config_values, &matches).unwrap();\n\n        assert_eq!(command_config.get_one::<bool>(\"include_private\").unwrap(), Some(true));\n    }\n\n    #[test]\n    fn test_validate_required_key_provided_via_cli_only() {\n        // Verifies that validate() passes when a required key is provided\n        // via CLI but not in the config file.\n        let cmd = Command::new(\"test\")\n            .arg(\n                Arg::new(\"database\")\n                    .long(\"database\")\n                    .value_parser(clap::value_parser!(String)),\n            )\n            .arg(\n                Arg::new(\"server\")\n                    .long(\"server\")\n                    .value_parser(clap::value_parser!(String)),\n            );\n\n        let matches = cmd.clone().get_matches_from(vec![\"test\", \"--database\", \"my-db\"]);\n\n        let schema = CommandSchemaBuilder::new()\n            .key(Key::new(\"database\").required())\n            .key(Key::new(\"server\"))\n            .build(&cmd)\n            .unwrap();\n\n        // Config is empty - required key \"database\" is only in CLI\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n\n        // Should pass validation because CLI provides the required key\n        assert!(command_config.validate().is_ok());\n    }\n\n    #[test]\n    fn test_parent_child_inheritance() {\n        // Verifies that children inherit unset fields from the parent.\n        let json = r#\"{\n            \"database\": \"parent-db\",\n            \"server\": \"local\",\n            \"module-path\": \"./parent-module\",\n            \"build-options\": \"--release\",\n            \"children\": [\n                {\n                    \"database\": \"child-1\",\n                    \"module-path\": \"./child-module\"\n                },\n                {\n                    \"database\": \"child-2\",\n                    \"module-path\": \"./child-module\",\n                    \"server\": \"maincloud\"\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        // Should have 3 targets: parent + 2 children\n        assert_eq!(targets.len(), 3);\n\n        // Parent target\n        assert_eq!(\n            targets[0].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"parent-db\")\n        );\n        assert_eq!(targets[0].fields.get(\"server\").and_then(|v| v.as_str()), Some(\"local\"));\n\n        // Child 1: inherits server and build-options from parent\n        assert_eq!(\n            targets[1].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"child-1\")\n        );\n        assert_eq!(\n            targets[1].fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local\") // inherited from parent\n        );\n        assert_eq!(\n            targets[1].fields.get(\"build-options\").and_then(|v| v.as_str()),\n            Some(\"--release\") // inherited from parent\n        );\n\n        // Child 2: overrides server, inherits build-options\n        assert_eq!(\n            targets[2].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"child-2\")\n        );\n        assert_eq!(\n            targets[2].fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"maincloud\") // overridden\n        );\n        assert_eq!(\n            targets[2].fields.get(\"build-options\").and_then(|v| v.as_str()),\n            Some(\"--release\") // inherited from parent\n        );\n    }\n\n    #[test]\n    fn test_parent_child_inheritance_no_children() {\n        // When there are no children, collect_all_targets_with_inheritance\n        // returns just the parent.\n        let json = r#\"{\n            \"database\": \"single-db\",\n            \"server\": \"local\",\n            \"module-path\": \"./module\"\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        assert_eq!(targets.len(), 1);\n        assert_eq!(\n            targets[0].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"single-db\")\n        );\n    }\n\n    #[test]\n    fn test_nested_inheritance_grandchildren() {\n        // Verifies that inheritance works recursively: grandchildren\n        // inherit from their parent (which already inherited from grandparent).\n        let json = r#\"{\n            \"server\": \"production\",\n            \"build-options\": \"--release\",\n            \"database\": \"root\",\n            \"children\": [\n                {\n                    \"database\": \"mid\",\n                    \"module-path\": \"./mid-module\",\n                    \"children\": [\n                        {\n                            \"database\": \"leaf\"\n                        }\n                    ]\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        // root + mid + leaf = 3\n        assert_eq!(targets.len(), 3);\n\n        // Root\n        assert_eq!(targets[0].fields.get(\"database\").and_then(|v| v.as_str()), Some(\"root\"));\n\n        // Mid: inherits server and build-options from root, has own module-path\n        assert_eq!(targets[1].fields.get(\"database\").and_then(|v| v.as_str()), Some(\"mid\"));\n        assert_eq!(\n            targets[1].fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"production\")\n        );\n        assert_eq!(\n            targets[1].fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./mid-module\")\n        );\n\n        // Leaf: inherits server and build-options (from root via mid),\n        // AND inherits module-path from mid\n        assert_eq!(targets[2].fields.get(\"database\").and_then(|v| v.as_str()), Some(\"leaf\"));\n        assert_eq!(\n            targets[2].fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"production\")\n        );\n        assert_eq!(\n            targets[2].fields.get(\"build-options\").and_then(|v| v.as_str()),\n            Some(\"--release\")\n        );\n        assert_eq!(\n            targets[2].fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./mid-module\")\n        );\n    }\n\n    #[test]\n    fn test_generate_not_inherited_from_parent() {\n        // Generate is never inherited. Children must define their own.\n        let json = r#\"{\n            \"database\": \"parent-db\",\n            \"server\": \"local\",\n            \"generate\": [\n                { \"language\": \"typescript\", \"out-dir\": \"./client/src/bindings\" }\n            ],\n            \"children\": [\n                { \"database\": \"child-1\" },\n                {\n                    \"database\": \"child-2\",\n                    \"generate\": [\n                        { \"language\": \"csharp\", \"out-dir\": \"./csharp-bindings\" }\n                    ]\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        assert_eq!(targets.len(), 3);\n\n        // Parent has its own generate\n        let parent_gen = targets[0].generate.as_ref().unwrap();\n        assert_eq!(parent_gen.len(), 1);\n        assert_eq!(\n            parent_gen[0].get(\"language\").and_then(|v| v.as_str()),\n            Some(\"typescript\")\n        );\n\n        // Child 1 does not inherit parent's generate\n        assert!(targets[1].generate.is_none());\n\n        // Child 2 has its own generate\n        let child2_gen = targets[2].generate.as_ref().unwrap();\n        assert_eq!(child2_gen.len(), 1);\n        assert_eq!(child2_gen[0].get(\"language\").and_then(|v| v.as_str()), Some(\"csharp\"));\n    }\n\n    #[test]\n    fn test_find_and_load_with_env_layering() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        // Base config\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{ \"database\": \"my-db\", \"server\": \"local\" }\"#,\n        )\n        .unwrap();\n\n        // Dev environment overlay - replaces server\n        fs::write(root.join(\"spacetime.dev.json\"), r#\"{ \"server\": \"maincloud\" }\"#).unwrap();\n\n        // Load without env\n        let result = find_and_load_with_env_from(None, root.to_path_buf()).unwrap().unwrap();\n        assert_eq!(\n            result.config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local\")\n        );\n        assert!(!result.has_dev_file);\n\n        // Load with dev env\n        let result = find_and_load_with_env_from(Some(\"dev\"), root.to_path_buf())\n            .unwrap()\n            .unwrap();\n        assert_eq!(\n            result.config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"maincloud\")\n        );\n        assert_eq!(\n            result.config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"my-db\")\n        );\n        assert!(result.has_dev_file);\n        assert_eq!(result.loaded_files.len(), 2);\n    }\n\n    #[test]\n    fn test_find_and_load_with_env_local_overlay() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        // Base config\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{ \"database\": \"my-db\", \"server\": \"local\" }\"#,\n        )\n        .unwrap();\n\n        // Local overlay\n        fs::write(root.join(\"spacetime.local.json\"), r#\"{ \"database\": \"my-local-db\" }\"#).unwrap();\n\n        let result = find_and_load_with_env_from(None, root.to_path_buf()).unwrap().unwrap();\n        // Local overlay replaces database\n        assert_eq!(\n            result.config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"my-local-db\")\n        );\n        // Server is preserved from base\n        assert_eq!(\n            result.config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local\")\n        );\n    }\n\n    #[test]\n    fn test_children_overlay_merges_by_index_with_lower_count() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{\n                \"database\": \"root\",\n                \"children\": [\n                    { \"database\": \"db-a\", \"server\": \"base-a\" },\n                    { \"database\": \"db-b\", \"server\": \"base-b\" }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        fs::write(\n            root.join(\"spacetime.local.json\"),\n            r#\"{\n                \"children\": [\n                    { \"server\": \"local-a\", \"module-path\": \"./a\" },\n                    { \"server\": \"local-b\", \"module-path\": \"./b\" },\n                    { \"database\": \"db-extra\", \"server\": \"extra\" }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        let result = find_and_load_with_env_from(None, root.to_path_buf()).unwrap().unwrap();\n        let children = result.config.children.as_ref().unwrap();\n        assert_eq!(children.len(), 2, \"child count should remain from base config\");\n\n        assert_eq!(\n            children[0].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"db-a\")\n        );\n        assert_eq!(\n            children[0].additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local-a\")\n        );\n        assert_eq!(\n            children[0]\n                .additional_fields\n                .get(\"module-path\")\n                .and_then(|v| v.as_str()),\n            Some(\"./a\")\n        );\n\n        assert_eq!(\n            children[1].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"db-b\")\n        );\n        assert_eq!(\n            children[1].additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"local-b\")\n        );\n        assert_eq!(\n            children[1]\n                .additional_fields\n                .get(\"module-path\")\n                .and_then(|v| v.as_str()),\n            Some(\"./b\")\n        );\n    }\n\n    #[test]\n    fn test_children_overlay_merges_recursively_for_nested_children() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{\n                \"database\": \"root\",\n                \"children\": [\n                    {\n                        \"database\": \"parent-a\",\n                        \"children\": [\n                            { \"database\": \"grand-a1\", \"server\": \"base-g1\" },\n                            { \"database\": \"grand-a2\", \"server\": \"base-g2\" }\n                        ]\n                    }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        fs::write(\n            root.join(\"spacetime.local.json\"),\n            r#\"{\n                \"children\": [\n                    {\n                        \"children\": [\n                            { \"server\": \"local-g1\", \"module-path\": \"./nested\" }\n                        ]\n                    }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        let result = find_and_load_with_env_from(None, root.to_path_buf()).unwrap().unwrap();\n        let children = result.config.children.as_ref().unwrap();\n        let grandchildren = children[0].children.as_ref().unwrap();\n        assert_eq!(grandchildren.len(), 2);\n\n        assert_eq!(\n            grandchildren[0]\n                .additional_fields\n                .get(\"database\")\n                .and_then(|v| v.as_str()),\n            Some(\"grand-a1\")\n        );\n        assert_eq!(\n            grandchildren[0]\n                .additional_fields\n                .get(\"server\")\n                .and_then(|v| v.as_str()),\n            Some(\"local-g1\")\n        );\n        assert_eq!(\n            grandchildren[0]\n                .additional_fields\n                .get(\"module-path\")\n                .and_then(|v| v.as_str()),\n            Some(\"./nested\")\n        );\n\n        assert_eq!(\n            grandchildren[1]\n                .additional_fields\n                .get(\"database\")\n                .and_then(|v| v.as_str()),\n            Some(\"grand-a2\")\n        );\n        assert_eq!(\n            grandchildren[1]\n                .additional_fields\n                .get(\"server\")\n                .and_then(|v| v.as_str()),\n            Some(\"base-g2\")\n        );\n    }\n\n    #[test]\n    fn test_source_config_tracks_database_origin_per_target() {\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{\n                \"database\": \"root-base\",\n                \"children\": [\n                    { \"database\": \"child-a-base\", \"server\": \"base-a\" },\n                    { \"database\": \"child-b-base\", \"server\": \"base-b\" }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        fs::write(\n            root.join(\"spacetime.local.json\"),\n            r#\"{\n                \"database\": \"root-local\",\n                \"children\": [\n                    { \"database\": \"child-a-local\" },\n                    { \"server\": \"only-server-override\" }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        fs::write(\n            root.join(\"spacetime.dev.json\"),\n            r#\"{\n                \"children\": [\n                    { \"server\": \"dev-a\" },\n                    { \"database\": \"child-b-dev\" }\n                ]\n            }\"#,\n        )\n        .unwrap();\n\n        let result = find_and_load_with_env_from(Some(\"dev\"), root.to_path_buf())\n            .unwrap()\n            .unwrap();\n\n        assert_eq!(result.config.source_config.as_deref(), Some(\"spacetime.local.json\"));\n\n        let children = result.config.children.as_ref().unwrap();\n        assert_eq!(children[0].source_config.as_deref(), Some(\"spacetime.local.json\"));\n        assert_eq!(children[1].source_config.as_deref(), Some(\"spacetime.dev.json\"));\n    }\n\n    #[test]\n    fn test_source_config_not_inherited_to_children_without_database() {\n        let json = r#\"{\n            \"database\": \"root-db\",\n            \"_source-config\": \"spacetime.local.json\",\n            \"children\": [\n                { \"server\": \"local\" }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        assert_eq!(config.source_config.as_deref(), Some(\"spacetime.local.json\"));\n        let child = config.children.as_ref().unwrap().first().unwrap();\n        assert!(\n            child.source_config.is_none(),\n            \"_source-config should not be inherited to children\"\n        );\n    }\n\n    #[test]\n    fn test_multi_level_env_layering_staging() {\n        // Full overlay order: base → local → staging → staging.local\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        // Base config\n        fs::write(\n            root.join(\"spacetime.json\"),\n            r#\"{ \"database\": \"base-db\", \"server\": \"local\", \"module-path\": \"./server\" }\"#,\n        )\n        .unwrap();\n\n        // Staging env overlay (applies after local)\n        fs::write(\n            root.join(\"spacetime.staging.json\"),\n            r#\"{ \"server\": \"staging-server\", \"database\": \"staging-db\" }\"#,\n        )\n        .unwrap();\n\n        // Local overlay (applies before env)\n        fs::write(\n            root.join(\"spacetime.local.json\"),\n            r#\"{ \"database\": \"local-override-db\" }\"#,\n        )\n        .unwrap();\n\n        // Staging local overlay (applies last)\n        fs::write(\n            root.join(\"spacetime.staging.local.json\"),\n            r#\"{ \"database\": \"staging-local-db\" }\"#,\n        )\n        .unwrap();\n\n        let result = find_and_load_with_env_from(Some(\"staging\"), root.to_path_buf())\n            .unwrap()\n            .unwrap();\n\n        // database: base-db → local-override-db → staging-db → staging-local-db\n        assert_eq!(\n            result.config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"staging-local-db\")\n        );\n        // server: local → staging-server (not overridden by local files)\n        assert_eq!(\n            result.config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"staging-server\")\n        );\n        // module-path: only in base, preserved through all overlays\n        assert_eq!(\n            result\n                .config\n                .additional_fields\n                .get(\"module-path\")\n                .and_then(|v| v.as_str()),\n            Some(\"./server\")\n        );\n        // 4 files loaded\n        assert_eq!(result.loaded_files.len(), 4);\n        assert_eq!(\n            result.loaded_files[1].file_name().and_then(|s| s.to_str()),\n            Some(\"spacetime.local.json\")\n        );\n        assert_eq!(\n            result.loaded_files[2].file_name().and_then(|s| s.to_str()),\n            Some(\"spacetime.staging.json\")\n        );\n    }\n\n    #[test]\n    fn test_has_dev_file_false_for_non_dev_env() {\n        // has_dev_file should only be true for env=\"dev\", not for other envs\n        use std::fs;\n        use tempfile::TempDir;\n\n        let temp = TempDir::new().unwrap();\n        let root = temp.path();\n\n        fs::write(root.join(\"spacetime.json\"), r#\"{ \"database\": \"my-db\" }\"#).unwrap();\n\n        fs::write(root.join(\"spacetime.staging.json\"), r#\"{ \"server\": \"staging\" }\"#).unwrap();\n\n        let result = find_and_load_with_env_from(Some(\"staging\"), root.to_path_buf())\n            .unwrap()\n            .unwrap();\n        assert!(!result.has_dev_file, \"has_dev_file should be false for staging env\");\n\n        // But dev env should set it\n        fs::write(root.join(\"spacetime.dev.json\"), r#\"{ \"server\": \"local\" }\"#).unwrap();\n\n        let result = find_and_load_with_env_from(Some(\"dev\"), root.to_path_buf())\n            .unwrap()\n            .unwrap();\n        assert!(result.has_dev_file, \"has_dev_file should be true for dev env\");\n    }\n\n    #[test]\n    fn test_dev_not_propagated_to_children() {\n        // dev is root-only and should NOT appear in child targets\n        let json = r#\"{\n            \"database\": \"parent-db\",\n            \"server\": \"local\",\n            \"dev\": { \"run\": \"npm run dev\" },\n            \"children\": [\n                { \"database\": \"child-db\" }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        assert_eq!(targets.len(), 2);\n\n        // Parent should have database and server in fields\n        assert_eq!(\n            targets[0].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"parent-db\")\n        );\n\n        // Child should inherit server but NOT have dev in fields\n        assert_eq!(\n            targets[1].fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"child-db\")\n        );\n        assert_eq!(targets[1].fields.get(\"server\").and_then(|v| v.as_str()), Some(\"local\"));\n        // dev should not be in additional_fields of FlatTarget\n        assert!(\n            !targets[1].fields.contains_key(\"dev\"),\n            \"dev should not be propagated to children via additional_fields\"\n        );\n        // Also verify parent's flat target doesn't leak dev into fields\n        assert!(\n            !targets[0].fields.contains_key(\"dev\"),\n            \"dev should not appear in FlatTarget fields (it's a typed field, not in additional_fields)\"\n        );\n    }\n\n    #[test]\n    /// Even when children share the parent's module-path, generate is not inherited.\n    ///\n    /// Deduplication in generate.rs handles the common case; inheritance would be\n    /// dangerous when a child overrides module-path.\n    fn test_generate_not_inherited_for_children_sharing_module() {\n        let json = r#\"{\n            \"module-path\": \"./server\",\n            \"generate\": [\n                { \"language\": \"typescript\", \"out-dir\": \"./client/src/bindings\" }\n            ],\n            \"children\": [\n                { \"database\": \"region-1\" },\n                { \"database\": \"region-2\" }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let targets = config.collect_all_targets_with_inheritance();\n\n        assert_eq!(targets.len(), 3);\n\n        // Parent has generate\n        assert!(targets[0].generate.is_some());\n\n        // Children do not inherit generate\n        assert!(targets[1].generate.is_none());\n        assert!(targets[2].generate.is_none());\n\n        // All share the same module-path via field inheritance\n        for target in &targets {\n            assert_eq!(\n                target.fields.get(\"module-path\").and_then(|v| v.as_str()),\n                Some(\"./server\")\n            );\n        }\n    }\n\n    #[test]\n    fn test_iter_all_targets_includes_self_and_descendants() {\n        let json = r#\"{\n            \"database\": \"root\",\n            \"children\": [\n                {\n                    \"database\": \"mid\",\n                    \"children\": [\n                        { \"database\": \"leaf\" }\n                    ]\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        let all: Vec<_> = config.iter_all_targets().collect();\n        assert_eq!(all.len(), 3);\n        assert_eq!(\n            all[0].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"root\")\n        );\n        assert_eq!(\n            all[1].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"mid\")\n        );\n        assert_eq!(\n            all[2].additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"leaf\")\n        );\n    }\n\n    #[test]\n    fn test_count_targets() {\n        let json = r#\"{\n            \"database\": \"root\",\n            \"children\": [\n                { \"database\": \"child-1\" },\n                {\n                    \"database\": \"child-2\",\n                    \"children\": [\n                        { \"database\": \"grandchild\" }\n                    ]\n                }\n            ]\n        }\"#;\n\n        let config: SpacetimeConfig = json5::from_str(json).unwrap();\n        assert_eq!(config.count_targets(), 4); // root + child-1 + child-2 + grandchild\n    }\n\n    #[test]\n    fn test_path_clean_preserves_leading_dotdot() {\n        // Regression test for #4429: leading `..` must be preserved.\n        // All config paths (--out-dir, --module-path, etc.) go through\n        // get_resolved_path which calls PathClean::clean().\n        use path_clean::PathClean;\n        use std::path::Path;\n\n        // --out-dir cases\n        assert_eq!(Path::new(\"../foo\").clean(), PathBuf::from(\"../foo\"));\n        assert_eq!(Path::new(\"../../a/b\").clean(), PathBuf::from(\"../../a/b\"));\n        assert_eq!(\n            Path::new(\"../frontend-ts-src/module-bindings\").clean(),\n            PathBuf::from(\"../frontend-ts-src/module-bindings\")\n        );\n        // Inner `..` should still resolve.\n        assert_eq!(Path::new(\"a/b/../c\").clean(), PathBuf::from(\"a/c\"));\n        // Pure `..` should stay.\n        assert_eq!(Path::new(\"..\").clean(), PathBuf::from(\"..\"));\n        // Absolute paths\n        assert_eq!(\n            Path::new(\"/home/user/project/../foo\").clean(),\n            PathBuf::from(\"/home/user/foo\")\n        );\n        // Current dir collapses.\n        assert_eq!(Path::new(\"./foo\").clean(), PathBuf::from(\"foo\"));\n        // Empty result → \".\"\n        assert_eq!(Path::new(\".\").clean(), PathBuf::from(\".\"));\n        assert_eq!(Path::new(\"a/..\").clean(), PathBuf::from(\".\"));\n\n        // --module-path cases (same bug, reported by user on #4431)\n        assert_eq!(Path::new(\"../server\").clean(), PathBuf::from(\"../server\"));\n        assert_eq!(\n            Path::new(\"../../repos/server\").clean(),\n            PathBuf::from(\"../../repos/server\")\n        );\n        assert_eq!(\n            Path::new(\"../repos/server/spacetimedb\").clean(),\n            PathBuf::from(\"../repos/server/spacetimedb\")\n        );\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/build.rs",
    "content": "use crate::util::find_module_path;\nuse crate::Config;\nuse clap::ArgAction::SetTrue;\nuse clap::{Arg, ArgMatches};\nuse std::ffi::OsString;\nuse std::path::{Path, PathBuf};\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"build\")\n        .about(\"Builds a spacetime module.\")\n        .arg(\n            Arg::new(\"module_path\")\n                .long(\"module-path\")\n                .short('p')\n                .value_parser(clap::value_parser!(PathBuf))\n                .help(\"The system path (absolute or relative) to the module project. Defaults to spacetimedb/ subdirectory, then current directory.\")\n        )\n        .arg(\n            Arg::new(\"lint_dir\")\n                .long(\"lint-dir\")\n                .value_parser(clap::value_parser!(OsString))\n                .default_value(\"src\")\n                .help(\"The directory to lint for nonfunctional print statements. If set to the empty string, skips linting.\")\n        )\n        .arg(\n            // TODO: Make this into --extra-build-args (or something similar) that will get passed along to the language's compiler.\n            Arg::new(\"features\")\n                .long(\"features\")\n                .value_parser(clap::value_parser!(OsString))\n                .required(false)\n                .help(\"Additional features to pass to the build process (e.g. `--features feature1,feature2` for Rust modules).\")\n                // We're hiding this because we think it deserves a refactor first (see the TODO above)\n                .hide(true)\n        )\n        .arg(\n            Arg::new(\"debug\")\n                .long(\"debug\")\n                .short('d')\n                .action(SetTrue)\n                .help(\"Builds the module using debug instead of release (intended to speed up local iteration, not recommended for CI)\"),\n        )\n}\n\npub async fn exec(_config: Config, args: &ArgMatches) -> Result<(PathBuf, &'static str), anyhow::Error> {\n    let module_path = match args.get_one::<PathBuf>(\"module_path\").cloned() {\n        Some(path) => path,\n        None => find_module_path(&std::env::current_dir()?).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Could not find a SpacetimeDB module in spacetimedb/ or the current directory. \\\n                 Use --module-path to specify the module location.\"\n            )\n        })?,\n    };\n    let features = args.get_one::<OsString>(\"features\");\n    let lint_dir = args.get_one::<OsString>(\"lint_dir\").unwrap();\n    let lint_dir = if lint_dir.is_empty() {\n        None\n    } else {\n        Some(PathBuf::from(lint_dir))\n    };\n    let build_debug = args.get_flag(\"debug\");\n    let features = features.cloned();\n\n    run_build(module_path, lint_dir, build_debug, features)\n}\n\npub fn run_build(\n    module_path: PathBuf,\n    lint_dir: Option<PathBuf>,\n    build_debug: bool,\n    features: Option<OsString>,\n) -> Result<(PathBuf, &'static str), anyhow::Error> {\n    // Create the project path, or make sure the target project path is empty.\n    if module_path.exists() {\n        if !module_path.is_dir() {\n            return Err(anyhow::anyhow!(\n                \"Fatal Error: path {} exists but is not a directory.\",\n                module_path.display()\n            ));\n        }\n    } else {\n        return Err(anyhow::anyhow!(\n            \"Fatal Error: path {} does not exist.\",\n            module_path.display()\n        ));\n    }\n\n    let result = crate::tasks::build(&module_path, lint_dir.as_deref(), build_debug, features.as_ref())?;\n    println!(\"Build finished successfully.\");\n\n    Ok(result)\n}\n\npub async fn exec_with_argstring(\n    project_path: &Path,\n    arg_string: &str,\n) -> Result<(PathBuf, &'static str), anyhow::Error> {\n    let argv = exec_with_argstring_argv(project_path, arg_string);\n    let arg_matches = cli().get_matches_from(argv);\n\n    let module_path = arg_matches\n        .get_one::<PathBuf>(\"module_path\")\n        .cloned()\n        .ok_or_else(|| anyhow::anyhow!(\"module_path is required\"))?;\n    let features = arg_matches.get_one::<OsString>(\"features\").cloned();\n    let lint_dir = arg_matches.get_one::<OsString>(\"lint_dir\").unwrap();\n    let lint_dir = if lint_dir.is_empty() {\n        None\n    } else {\n        Some(PathBuf::from(lint_dir))\n    };\n    let build_debug = arg_matches.get_flag(\"debug\");\n\n    run_build(module_path, lint_dir, build_debug, features)\n}\n\nfn exec_with_argstring_argv(project_path: &Path, arg_string: &str) -> Vec<OsString> {\n    // Note: \"build\" must be the first argv token because `build::cli()` is the entire build subcommand.\n    // Keep module-path as its own argv item so paths containing spaces are not split.\n    let mut argv: Vec<OsString> = vec![\"build\".into()];\n    argv.extend(arg_string.split_whitespace().map(OsString::from));\n    argv.push(\"--module-path\".into());\n    argv.push(project_path.as_os_str().to_os_string());\n    argv\n}\n\n#[cfg(test)]\nmod tests {\n    use super::exec_with_argstring_argv;\n    use std::path::Path;\n\n    #[test]\n    fn exec_with_argstring_keeps_module_path_with_spaces_as_single_argv_item() {\n        let project_path = Path::new(\"SpacetimeDB Projects/My SpacetimeDB App/spacetimedb\");\n        let argv = exec_with_argstring_argv(project_path, \"--debug --lint-dir src\");\n\n        assert_eq!(argv[0].to_string_lossy(), \"build\");\n        assert_eq!(argv[1].to_string_lossy(), \"--debug\");\n        assert_eq!(argv[2].to_string_lossy(), \"--lint-dir\");\n        assert_eq!(argv[3].to_string_lossy(), \"src\");\n        assert_eq!(argv[4].to_string_lossy(), \"--module-path\");\n        assert_eq!(\n            argv[5].to_string_lossy(),\n            \"SpacetimeDB Projects/My SpacetimeDB App/spacetimedb\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/call.rs",
    "content": "use crate::api::ClientApi;\nuse crate::common_args;\nuse crate::config::Config;\nuse crate::edit_distance::{edit_distance, find_best_match_for_name};\nuse crate::subcommands::db_arg_resolution::{load_config_db_targets, resolve_optional_database_parts};\nuse crate::util::UNSTABLE_WARNING;\nuse crate::util::{database_identity, get_auth_header};\nuse anyhow::{bail, Context, Error};\nuse clap::{Arg, ArgMatches};\nuse convert_case::{Case, Casing};\nuse core::ops::Deref;\nuse itertools::Itertools;\nuse spacetimedb_lib::sats::{self, AlgebraicType, Typespace};\nuse spacetimedb_lib::{Identity, ProductTypeElement};\nuse spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef};\nuse std::fmt::Write;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"call\")\n        .about(format!(\n            \"Invokes a function (reducer or procedure) in a database. {UNSTABLE_WARNING}\"\n        ))\n        .arg(\n            Arg::new(\"call_parts\")\n                .help(\"Call arguments: [DATABASE] <FUNCTION_NAME> [ARGUMENTS...]\")\n                .num_args(1..),\n        )\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server hosting the database\"))\n        .arg(common_args::anonymous())\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .after_help(\"Run `spacetime help call` for more detailed information.\\n\")\n}\n\nenum CallDef<'a> {\n    Reducer(&'a ReducerDef),\n    Procedure(&'a ProcedureDef),\n}\n\nimpl<'a> CallDef<'a> {\n    fn params(&self) -> &'a sats::ProductType {\n        match self {\n            CallDef::Reducer(reducer_def) => &reducer_def.params,\n            CallDef::Procedure(procedure_def) => &procedure_def.params,\n        }\n    }\n    fn name(&self) -> &str {\n        match self {\n            CallDef::Reducer(reducer_def) => &reducer_def.name,\n            CallDef::Procedure(procedure_def) => &procedure_def.name,\n        }\n    }\n    fn kind(&self) -> &str {\n        match self {\n            CallDef::Reducer(_) => \"reducer\",\n            CallDef::Procedure(_) => \"procedure\",\n        }\n    }\n}\n\npub async fn exec(config: Config, args: &ArgMatches) -> Result<(), Error> {\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n    let server = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let force = args.get_flag(\"force\");\n    let anon_identity = args.get_flag(\"anon_identity\");\n    let no_config = args.get_flag(\"no_config\");\n\n    let raw_parts: Vec<String> = args\n        .get_many::<String>(\"call_parts\")\n        .map(|vals| vals.cloned().collect())\n        .unwrap_or_default();\n\n    let config_targets = load_config_db_targets(no_config)?;\n    let resolved = resolve_optional_database_parts(\n        &raw_parts,\n        config_targets.as_deref(),\n        \"function_name\",\n        \"spacetime call [database] <function_name> <arguments>... (or --no-config for legacy behavior)\",\n    )?;\n    let reducer_procedure_name = resolved\n        .remaining_args\n        .first()\n        .ok_or_else(|| anyhow::anyhow!(\"internal error: function_name should be present after argument resolution\"))?;\n    let call_arguments = resolved.remaining_args.iter().skip(1);\n    let resolved_server = server.or(resolved.server.as_deref());\n\n    let mut config = config;\n    let conn = crate::api::Connection {\n        host: config.get_host_url(resolved_server)?,\n        auth_header: get_auth_header(&mut config, anon_identity, resolved_server, !force).await?,\n        database_identity: database_identity(&config, &resolved.database, resolved_server).await?,\n        database: resolved.database.clone(),\n    };\n    let api = ClientApi::new(conn);\n\n    let database_identity = api.con.database_identity;\n    let database = &api.con.database;\n\n    let module_def: ModuleDef = api.module_def().await?.try_into()?;\n\n    let call_def = match module_def.reducer(&**reducer_procedure_name) {\n        Some(reducer_def) => CallDef::Reducer(reducer_def),\n        None => match module_def.procedure(&**reducer_procedure_name) {\n            Some(procedure_def) => CallDef::Procedure(procedure_def),\n            None => {\n                return Err(anyhow::Error::msg(no_such_reducer_or_procedure(\n                    &database_identity,\n                    database,\n                    reducer_procedure_name,\n                    &module_def,\n                )));\n            }\n        },\n    };\n\n    // String quote any arguments that should be quoted\n    let arguments = call_arguments\n        .zip(&call_def.params().elements)\n        .map(|(argument, element)| match &element.algebraic_type {\n            AlgebraicType::String if !argument.starts_with('\\\"') || !argument.ends_with('\\\"') => {\n                format!(\"\\\"{argument}\\\"\")\n            }\n            _ => argument.to_string(),\n        });\n\n    let arg_json = format!(\"[{}]\", arguments.format(\", \"));\n    let res = api.call(reducer_procedure_name, arg_json).await?;\n\n    if let Err(e) = res.error_for_status_ref() {\n        let Ok(response_text) = res.text().await else {\n            // Cannot give a better error than this if we don't know what the problem is.\n            bail!(e);\n        };\n\n        let error = Err(e).context(format!(\"Response text: {response_text}\"));\n\n        let error_msg =\n            if response_text.starts_with(\"no such reducer\") || response_text.starts_with(\"no such procedure\") {\n                no_such_reducer_or_procedure(&database_identity, database, reducer_procedure_name, &module_def)\n            } else if response_text.starts_with(\"invalid arguments\") {\n                invalid_arguments(&database_identity, database, &response_text, &module_def, call_def)\n            } else {\n                return error;\n            };\n\n        return error.context(error_msg);\n    }\n\n    if let CallDef::Procedure(_) = call_def {\n        let body = res.text().await?;\n        println!(\"{body}\");\n    }\n\n    Ok(())\n}\n\n/// Returns an error message for when `reducer` is called with wrong arguments.\nfn invalid_arguments(identity: &Identity, db: &str, text: &str, module_def: &ModuleDef, call_def: CallDef) -> String {\n    let mut error = format!(\n        \"Invalid arguments provided for {} `{}` for database `{}` resolving to identity `{}`.\",\n        call_def.kind(),\n        call_def.name(),\n        db,\n        identity\n    );\n\n    if let Some((actual, expected)) = find_actual_expected(text).filter(|(a, e)| a != e) {\n        write!(\n            error,\n            \"\\n\\n{expected} parameters were expected, but {actual} were provided.\"\n        )\n        .unwrap();\n    }\n\n    write!(\n        error,\n        \"\\n\\nThe {} has the following signature:\\n\\t{}\",\n        call_def.kind(),\n        CallSignature(module_def.typespace().with_type(&call_def))\n    )\n    .unwrap();\n\n    error\n}\n\n/// Parse actual/expected parameter numbers from the invalid args response text.\nfn find_actual_expected(text: &str) -> Option<(usize, usize)> {\n    let (_, x) = split_at_first_substring(text, \"invalid length\")?;\n    let (x, y) = split_at_first_substring(x, \"args for test with\")?;\n    let (x, _) = split_at_first_substring(x, \",\")?;\n    let (y, _) = split_at_first_substring(y, \"elements\")?;\n    let actual: usize = x.trim().parse().ok()?;\n    let expected: usize = y.trim().parse().ok()?;\n    Some((actual, expected))\n}\n\n/// Returns a tuple with\n/// - everything after the first `substring`\n/// - and anything before it.\nfn split_at_first_substring<'t>(text: &'t str, substring: &str) -> Option<(&'t str, &'t str)> {\n    text.find(substring)\n        .map(|pos| (&text[..pos], &text[pos + substring.len()..]))\n}\n\n/// Provided the `schema_json` for the database,\n/// returns the signature for a reducer OR procedure with `name`.\nstruct CallSignature<'a>(sats::WithTypespace<'a, CallDef<'a>>);\nimpl std::fmt::Display for CallSignature<'_> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let call_def = self.0.ty();\n        let typespace = self.0.typespace();\n\n        write!(f, \"{}(\", call_def.name())?;\n\n        // Print the arguments to `args`.\n        let mut comma = false;\n        for arg in &*call_def.params().elements {\n            if comma {\n                write!(f, \", \")?;\n            }\n            comma = true;\n            if let Some(name) = arg.name() {\n                write!(f, \"{}: \", name.deref().to_case(Case::Snake))?;\n            }\n            write_type::write_type(typespace, f, &arg.algebraic_type)?;\n        }\n\n        write!(f, \")\")\n    }\n}\n\n/// Returns an error message for when `reducer` or `procedure` does not exist in `db`.\nfn no_such_reducer_or_procedure(database_identity: &Identity, db: &str, name: &str, module_def: &ModuleDef) -> String {\n    let mut error = format!(\n        \"No such reducer OR procedure `{name}` for database `{db}` resolving to identity `{database_identity}`.\"\n    );\n\n    add_reducer_procedure_ctx_to_err(&mut error, module_def, name);\n\n    error\n}\n\nconst CALL_PRINT_LIMIT: usize = 10;\n\n/// Provided the schema for the database,\n/// decorate `error` with more helpful info about reducers and procedures.\nfn add_reducer_procedure_ctx_to_err(error: &mut String, module_def: &ModuleDef, reducer_name: &str) {\n    let reducers = module_def\n        .reducers()\n        .filter(|reducer| reducer.lifecycle.is_none())\n        .map(|reducer| &*reducer.name)\n        .collect::<Vec<_>>();\n\n    let procedures = module_def\n        .procedures()\n        .map(|reducer| &*reducer.name)\n        .collect::<Vec<_>>();\n\n    if let Some(best) = find_best_match_for_name(&reducers, reducer_name, None) {\n        write!(error, \"\\n\\nA reducer with a similar name exists: `{best}`\").unwrap();\n    } else if let Some(best) = find_best_match_for_name(&procedures, reducer_name, None) {\n        write!(error, \"\\n\\nA procedure with a similar name exists: `{best}`\").unwrap();\n    } else {\n        let mut list_similar = |mut list: Vec<&str>, name: &str, kind: &str| {\n            if list.is_empty() {\n                write!(error, \"\\n\\nThe database has no {kind}s.\").unwrap();\n                return;\n            }\n            list.sort_by_key(|candidate| edit_distance(name, candidate, usize::MAX));\n\n            // Don't spam the user with too many entries.\n            let too_many_to_show = list.len() > CALL_PRINT_LIMIT;\n            let diff = list.len().abs_diff(CALL_PRINT_LIMIT);\n            list.truncate(CALL_PRINT_LIMIT);\n\n            // List them.\n            write!(error, \"\\n\\nHere are some existing {kind}s:\").unwrap();\n            for candidate in list {\n                write!(error, \"\\n- {candidate}\").unwrap();\n            }\n\n            // When somewhere not listed, note that are more.\n            if too_many_to_show {\n                let plural = if diff == 1 { \"\" } else { \"s\" };\n                write!(error, \"\\n... ({diff} {kind}{plural} not shown)\").unwrap();\n            }\n        };\n\n        list_similar(reducers, reducer_name, \"reducer\");\n        list_similar(procedures, reducer_name, \"procedure\");\n    }\n}\n\n// this is an old version of code in generate::rust that got\n// refactored, but reducer_signature() was using it\n// TODO: port reducer_signature() to use AlgebraicTypeUse et al, somehow.\nmod write_type {\n    use super::*;\n    use sats::ArrayType;\n    use spacetimedb_lib::ProductType;\n    use std::fmt;\n\n    pub fn write_type<W: fmt::Write>(typespace: &Typespace, out: &mut W, ty: &AlgebraicType) -> fmt::Result {\n        match ty {\n            p if p.is_identity() => write!(out, \"Identity\")?,\n            p if p.is_connection_id() => write!(out, \"ConnectionId\")?,\n            p if p.is_schedule_at() => write!(out, \"ScheduleAt\")?,\n            AlgebraicType::Sum(sum_type) => {\n                if let Some(inner_ty) = sum_type.as_option() {\n                    write!(out, \"Option<\")?;\n                    write_type(typespace, out, inner_ty)?;\n                    write!(out, \">\")?;\n                } else {\n                    write!(out, \"enum \")?;\n                    print_comma_sep_braced(out, &sum_type.variants, |out: &mut W, elem: &_| {\n                        if let Some(name) = &elem.name {\n                            write!(out, \"{name}: \")?;\n                        }\n                        write_type(typespace, out, &elem.algebraic_type)\n                    })?;\n                }\n            }\n            AlgebraicType::Product(ProductType { elements }) => {\n                print_comma_sep_braced(out, elements, |out: &mut W, elem: &ProductTypeElement| {\n                    if let Some(name) = &elem.name {\n                        write!(out, \"{name}: \")?;\n                    }\n                    write_type(typespace, out, &elem.algebraic_type)\n                })?;\n            }\n            AlgebraicType::Bool => write!(out, \"bool\")?,\n            AlgebraicType::I8 => write!(out, \"i8\")?,\n            AlgebraicType::U8 => write!(out, \"u8\")?,\n            AlgebraicType::I16 => write!(out, \"i16\")?,\n            AlgebraicType::U16 => write!(out, \"u16\")?,\n            AlgebraicType::I32 => write!(out, \"i32\")?,\n            AlgebraicType::U32 => write!(out, \"u32\")?,\n            AlgebraicType::I64 => write!(out, \"i64\")?,\n            AlgebraicType::U64 => write!(out, \"u64\")?,\n            AlgebraicType::I128 => write!(out, \"i128\")?,\n            AlgebraicType::U128 => write!(out, \"u128\")?,\n            AlgebraicType::I256 => write!(out, \"i256\")?,\n            AlgebraicType::U256 => write!(out, \"u256\")?,\n            AlgebraicType::F32 => write!(out, \"f32\")?,\n            AlgebraicType::F64 => write!(out, \"f64\")?,\n            AlgebraicType::String => write!(out, \"String\")?,\n            AlgebraicType::Array(ArrayType { elem_ty }) => {\n                write!(out, \"Vec<\")?;\n                write_type(typespace, out, elem_ty)?;\n                write!(out, \">\")?;\n            }\n            AlgebraicType::Ref(r) => {\n                write_type(typespace, out, &typespace[*r])?;\n            }\n        }\n        Ok(())\n    }\n\n    fn print_comma_sep_braced<W: fmt::Write, T>(\n        out: &mut W,\n        elems: &[T],\n        on: impl Fn(&mut W, &T) -> fmt::Result,\n    ) -> fmt::Result {\n        write!(out, \"{{\")?;\n\n        let mut iter = elems.iter();\n\n        // First factor.\n        if let Some(elem) = iter.next() {\n            write!(out, \" \")?;\n            on(out, elem)?;\n        }\n        // Other factors.\n        for elem in iter {\n            write!(out, \", \")?;\n            on(out, elem)?;\n        }\n\n        if !elems.is_empty() {\n            write!(out, \" \")?;\n        }\n\n        write!(out, \"}}\")?;\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    // Resolution tests live in db_arg_resolution.rs\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/db_arg_resolution.rs",
    "content": "// Database argument resolution for CLI commands.\n//\n// When a spacetime.json config file is present, CLI commands resolve database arguments\n// from the config. If the user provides a database name that doesn't match any configured\n// target, the behavior depends on whether the database argument is unambiguous:\n//\n// | Command     | Resolution function                | DB arg unambiguous? | Auto-fallthrough? |\n// |-------------|-------------------------------------|---------------------|-------------------|\n// | logs        | resolve_database_arg               | Yes (dedicated arg) | Yes               |\n// | delete      | resolve_database_arg               | Yes (dedicated arg) | Yes               |\n// | sql         | resolve_optional_database_parts    | Only with 2+ args   | Yes (at call site)|\n// | call        | resolve_optional_database_parts    | No (variable args)  | No                |\n// | subscribe   | resolve_optional_database_parts    | No (variable args)  | No                |\n// | describe    | resolve_database_with_optional_parts | No (optional args)| No                |\n//\n// \"Auto-fallthrough\" means: if the provided database name doesn't match any config target,\n// treat it as an ad-hoc database outside the project (equivalent to --no-config for that arg).\n//\n// Commands using `resolve_database_arg` always have an unambiguous `<database>` arg, so we\n// can safely fall through. For `sql`, the call site knows that exactly 1 query arg is expected,\n// so 2+ positional args means the first must be a database. For `call`/`subscribe`/`describe`,\n// the first positional could be a non-database argument, so we must error to avoid misinterpreting it.\n\nuse crate::spacetime_config::find_and_load_with_env;\nuse itertools::Itertools;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub(crate) struct ConfigDbTarget {\n    pub database: String,\n    pub server: Option<String>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub(crate) struct ResolvedDbArgs {\n    pub database: String,\n    pub server: Option<String>,\n    pub remaining_args: Vec<String>,\n}\n\n/// Build an error for when the first positional arg doesn't match any configured database target.\nfn unknown_database_error(db: &str, config_targets: &[ConfigDbTarget]) -> anyhow::Error {\n    let known: Vec<&str> = config_targets.iter().map(|t| t.database.as_str()).collect();\n    if known.len() > 1 {\n        anyhow::anyhow!(\n            \"Multiple databases found in config: {}. Please specify which database to use, \\\n             or pass --no-config to use '{}' directly.\",\n            known.join(\", \"),\n            db\n        )\n    } else {\n        anyhow::anyhow!(\n            \"Database '{}' is not in the config file. \\\n             If you want to run against a database outside of the current project, pass --no-config.\",\n            db\n        )\n    }\n}\n\npub(crate) fn load_config_db_targets(no_config: bool) -> anyhow::Result<Option<Vec<ConfigDbTarget>>> {\n    if no_config {\n        return Ok(None);\n    }\n\n    Ok(find_and_load_with_env(None)?\n        .map(|loaded| {\n            loaded\n                .config\n                .collect_all_targets_with_inheritance()\n                .iter()\n                .filter_map(|t| {\n                    let database = t.fields.get(\"database\").and_then(|v| v.as_str())?;\n                    let server = t.fields.get(\"server\").and_then(|v| v.as_str()).map(|s| s.to_string());\n                    Some(ConfigDbTarget {\n                        database: database.to_string(),\n                        server,\n                    })\n                })\n                .unique_by(|t| t.database.clone())\n                .collect::<Vec<_>>()\n        })\n        .filter(|targets| !targets.is_empty()))\n}\n\npub(crate) fn resolve_optional_database_parts(\n    raw_parts: &[String],\n    config_targets: Option<&[ConfigDbTarget]>,\n    required_arg_name: &str,\n    usage: &str,\n) -> anyhow::Result<ResolvedDbArgs> {\n    let require_arg = |name: &str| {\n        anyhow::anyhow!(\n            \"the following required arguments were not provided:\\n  <{}>\\n\\nUsage: {}\",\n            name,\n            usage\n        )\n    };\n\n    let Some(config_targets) = config_targets else {\n        if raw_parts.len() < 2 {\n            return if raw_parts.is_empty() {\n                Err(require_arg(\"database\"))\n            } else {\n                Err(require_arg(required_arg_name))\n            };\n        }\n        return Ok(ResolvedDbArgs {\n            database: raw_parts[0].clone(),\n            server: None,\n            remaining_args: raw_parts[1..].to_vec(),\n        });\n    };\n\n    if config_targets.len() == 1 {\n        let target = &config_targets[0];\n        if raw_parts.is_empty() {\n            return Err(require_arg(required_arg_name));\n        }\n        if raw_parts[0] == target.database {\n            if raw_parts.len() < 2 {\n                return Err(require_arg(required_arg_name));\n            }\n            return Ok(ResolvedDbArgs {\n                database: target.database.clone(),\n                server: target.server.clone(),\n                remaining_args: raw_parts[1..].to_vec(),\n            });\n        }\n        return Ok(ResolvedDbArgs {\n            database: target.database.clone(),\n            server: target.server.clone(),\n            remaining_args: raw_parts.to_vec(),\n        });\n    }\n\n    let db = &raw_parts[0];\n    let Some(target) = config_targets.iter().find(|t| t.database == *db) else {\n        return Err(unknown_database_error(db, config_targets));\n    };\n    if raw_parts.len() < 2 {\n        return Err(require_arg(required_arg_name));\n    }\n\n    Ok(ResolvedDbArgs {\n        database: db.clone(),\n        server: target.server.clone(),\n        remaining_args: raw_parts[1..].to_vec(),\n    })\n}\n\npub(crate) fn resolve_database_arg(\n    raw_database: Option<&str>,\n    config_targets: Option<&[ConfigDbTarget]>,\n    usage: &str,\n) -> anyhow::Result<ResolvedDbArgs> {\n    let require_database = || {\n        anyhow::anyhow!(\n            \"the following required arguments were not provided:\\n  <database>\\n\\nUsage: {}\",\n            usage\n        )\n    };\n\n    let Some(config_targets) = config_targets else {\n        let database = raw_database.ok_or_else(require_database)?;\n        return Ok(ResolvedDbArgs {\n            database: database.to_string(),\n            server: None,\n            remaining_args: vec![],\n        });\n    };\n\n    if config_targets.len() == 1 {\n        let target = &config_targets[0];\n        if let Some(db) = raw_database\n            && db != target.database\n        {\n            // The database arg is unambiguous, so treat it as an ad-hoc database\n            // outside the project config (auto-fallthrough).\n            return Ok(ResolvedDbArgs {\n                database: db.to_string(),\n                server: None,\n                remaining_args: vec![],\n            });\n        }\n        return Ok(ResolvedDbArgs {\n            database: target.database.clone(),\n            server: target.server.clone(),\n            remaining_args: vec![],\n        });\n    }\n\n    let db = raw_database.ok_or_else(require_database)?;\n    let Some(target) = config_targets.iter().find(|t| t.database == db) else {\n        // The database arg is unambiguous, so treat it as an ad-hoc database\n        // outside the project config (auto-fallthrough).\n        return Ok(ResolvedDbArgs {\n            database: db.to_string(),\n            server: None,\n            remaining_args: vec![],\n        });\n    };\n\n    Ok(ResolvedDbArgs {\n        database: target.database.clone(),\n        server: target.server.clone(),\n        remaining_args: vec![],\n    })\n}\n\npub(crate) fn resolve_database_with_optional_parts(\n    raw_parts: &[String],\n    config_targets: Option<&[ConfigDbTarget]>,\n    usage: &str,\n) -> anyhow::Result<ResolvedDbArgs> {\n    let require_database = || {\n        anyhow::anyhow!(\n            \"the following required arguments were not provided:\\n  <database>\\n\\nUsage: {}\",\n            usage\n        )\n    };\n\n    let Some(config_targets) = config_targets else {\n        let Some(database) = raw_parts.first() else {\n            return Err(require_database());\n        };\n        return Ok(ResolvedDbArgs {\n            database: database.clone(),\n            server: None,\n            remaining_args: raw_parts[1..].to_vec(),\n        });\n    };\n\n    if config_targets.len() == 1 {\n        let target = &config_targets[0];\n        if raw_parts.first().is_some_and(|db| db == &target.database) {\n            return Ok(ResolvedDbArgs {\n                database: target.database.clone(),\n                server: target.server.clone(),\n                remaining_args: raw_parts[1..].to_vec(),\n            });\n        }\n        return Ok(ResolvedDbArgs {\n            database: target.database.clone(),\n            server: target.server.clone(),\n            remaining_args: raw_parts.to_vec(),\n        });\n    }\n\n    let Some(db) = raw_parts.first() else {\n        return Err(require_database());\n    };\n    let Some(target) = config_targets.iter().find(|t| t.database == *db) else {\n        return Err(unknown_database_error(db, config_targets));\n    };\n\n    Ok(ResolvedDbArgs {\n        database: db.clone(),\n        server: target.server.clone(),\n        remaining_args: raw_parts[1..].to_vec(),\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{\n        resolve_database_arg, resolve_database_with_optional_parts, resolve_optional_database_parts, ConfigDbTarget,\n    };\n\n    #[test]\n    fn single_db_infers_database() {\n        let parts = vec![\"reducer\".to_string(), \"arg1\".to_string()];\n        let targets = vec![ConfigDbTarget {\n            database: \"foo\".to_string(),\n            server: Some(\"maincloud\".to_string()),\n        }];\n        let parsed = resolve_optional_database_parts(\n            &parts,\n            Some(&targets),\n            \"function_name\",\n            \"spacetime call [database] <function_name> <arguments>...\",\n        )\n        .unwrap();\n        assert_eq!(parsed.database, \"foo\");\n        assert_eq!(parsed.server.as_deref(), Some(\"maincloud\"));\n        assert_eq!(parsed.remaining_args, parts);\n    }\n\n    #[test]\n    fn single_db_accepts_explicit_db_prefix() {\n        let parts = vec![\"foo\".to_string(), \"SELECT 1\".to_string()];\n        let targets = vec![ConfigDbTarget {\n            database: \"foo\".to_string(),\n            server: Some(\"local\".to_string()),\n        }];\n        let parsed = resolve_optional_database_parts(\n            &parts,\n            Some(&targets),\n            \"query\",\n            \"spacetime subscribe [database] <query> [query...]\",\n        )\n        .unwrap();\n        assert_eq!(parsed.database, \"foo\");\n        assert_eq!(parsed.server.as_deref(), Some(\"local\"));\n        assert_eq!(parsed.remaining_args, vec![\"SELECT 1\".to_string()]);\n    }\n\n    #[test]\n    fn multi_db_rejects_unknown_database() {\n        let parts = vec![\"baz\".to_string(), \"SELECT 1\".to_string()];\n        let targets = vec![\n            ConfigDbTarget {\n                database: \"foo\".to_string(),\n                server: Some(\"maincloud\".to_string()),\n            },\n            ConfigDbTarget {\n                database: \"bar\".to_string(),\n                server: Some(\"local\".to_string()),\n            },\n        ];\n        let err = resolve_optional_database_parts(\n            &parts,\n            Some(&targets),\n            \"query\",\n            \"spacetime subscribe [database] <query> [query...]\",\n        )\n        .unwrap_err();\n        assert!(err.to_string().contains(\"Multiple databases found in config: foo, bar\"));\n        assert!(err.to_string().contains(\"--no-config\"));\n    }\n\n    #[test]\n    fn resolve_database_arg_single_target_uses_config_database() {\n        let targets = vec![ConfigDbTarget {\n            database: \"foo\".to_string(),\n            server: Some(\"maincloud\".to_string()),\n        }];\n        let resolved = resolve_database_arg(None, Some(&targets), \"spacetime logs [database]\").unwrap();\n        assert_eq!(resolved.database, \"foo\");\n        assert_eq!(resolved.server.as_deref(), Some(\"maincloud\"));\n    }\n\n    #[test]\n    fn resolve_database_with_optional_parts_single_target_allows_no_parts() {\n        let targets = vec![ConfigDbTarget {\n            database: \"foo\".to_string(),\n            server: Some(\"maincloud\".to_string()),\n        }];\n        let resolved =\n            resolve_database_with_optional_parts(&[], Some(&targets), \"spacetime describe [database] [entity]\")\n                .unwrap();\n        assert_eq!(resolved.database, \"foo\");\n        assert!(resolved.remaining_args.is_empty());\n    }\n\n    #[test]\n    fn resolve_database_arg_single_target_falls_through_for_unknown_db() {\n        let targets = vec![ConfigDbTarget {\n            database: \"foo\".to_string(),\n            server: Some(\"maincloud\".to_string()),\n        }];\n        let resolved = resolve_database_arg(Some(\"other-db\"), Some(&targets), \"spacetime logs [database]\").unwrap();\n        assert_eq!(resolved.database, \"other-db\");\n        assert_eq!(resolved.server, None);\n    }\n\n    #[test]\n    fn resolve_database_arg_multi_target_falls_through_for_unknown_db() {\n        let targets = vec![\n            ConfigDbTarget {\n                database: \"foo\".to_string(),\n                server: Some(\"maincloud\".to_string()),\n            },\n            ConfigDbTarget {\n                database: \"bar\".to_string(),\n                server: Some(\"local\".to_string()),\n            },\n        ];\n        let resolved = resolve_database_arg(Some(\"other-db\"), Some(&targets), \"spacetime logs [database]\").unwrap();\n        assert_eq!(resolved.database, \"other-db\");\n        assert_eq!(resolved.server, None);\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/delete.rs",
    "content": "use std::io;\n\nuse crate::common_args;\nuse crate::config::Config;\nuse crate::subcommands::db_arg_resolution::{load_config_db_targets, resolve_database_arg};\nuse crate::util::{add_auth_header_opt, database_identity, get_auth_header, y_or_n, AuthHeader};\nuse clap::{Arg, ArgMatches};\nuse http::StatusCode;\nuse itertools::Itertools as _;\nuse reqwest::Response;\nuse spacetimedb_client_api_messages::http::{DatabaseDeleteConfirmationResponse, DatabaseTree, DatabaseTreeNode};\nuse spacetimedb_lib::Hash;\nuse tokio::io::AsyncWriteExt as _;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"delete\")\n        .about(\"Deletes a SpacetimeDB database\")\n        .arg(\n            Arg::new(\"database\")\n                .required(false)\n                .help(\"The name or identity of the database to delete\"),\n        )\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server hosting the database\"))\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .after_help(\"Run `spacetime help delete` for more detailed information.\\n\")\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server_from_cli = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let no_config = args.get_flag(\"no_config\");\n    let database_arg = args.get_one::<String>(\"database\").map(|s| s.as_str());\n    let config_targets = load_config_db_targets(no_config)?;\n    let resolved = resolve_database_arg(\n        database_arg,\n        config_targets.as_deref(),\n        \"spacetime delete [database] [--no-config]\",\n    )?;\n    let server = server_from_cli.or(resolved.server.as_deref());\n    let force = args.get_flag(\"force\");\n\n    let identity = database_identity(&config, &resolved.database, server).await?;\n    let host_url = config.get_host_url(server)?;\n    let request_path = format!(\"{host_url}/v1/database/{identity}\");\n    let auth_header = get_auth_header(&mut config, false, server, !force).await?;\n    let client = reqwest::Client::new();\n\n    let response = send_request(&client, &request_path, &auth_header, None).await?;\n    match response.status() {\n        StatusCode::PRECONDITION_REQUIRED => {\n            let confirm = response.json::<DatabaseDeleteConfirmationResponse>().await?;\n            println!(\"WARNING: Deleting the database {identity} will also delete its children!\");\n            if !force {\n                print_database_tree_info(&confirm.database_tree).await?;\n            }\n            if y_or_n(force, \"Do you want to proceed deleting above databases?\")? {\n                send_request(&client, &request_path, &auth_header, Some(confirm.confirmation_token))\n                    .await?\n                    .error_for_status()?;\n            } else {\n                println!(\"Aborting\");\n            }\n\n            Ok(())\n        }\n        StatusCode::OK => Ok(()),\n        _ => response.error_for_status().map(drop).map_err(Into::into),\n    }\n}\n\nasync fn send_request(\n    client: &reqwest::Client,\n    request_path: &str,\n    auth: &AuthHeader,\n    confirmation_token: Option<Hash>,\n) -> Result<Response, reqwest::Error> {\n    let mut builder = client.delete(request_path);\n    builder = add_auth_header_opt(builder, auth);\n    if let Some(token) = confirmation_token {\n        builder = builder.query(&[(\"token\", token)]);\n    }\n    builder.send().await\n}\n\nasync fn print_database_tree_info(tree: &DatabaseTree) -> io::Result<()> {\n    let mut stdout = tokio::io::stdout();\n    stdout.write_all(as_termtree(tree).to_string().as_bytes()).await?;\n    stdout.write_u8(b'\\n').await?;\n    stdout.flush().await?;\n\n    Ok(())\n}\n\nfn as_termtree(tree: &DatabaseTree) -> termtree::Tree<String> {\n    let mut stack: Vec<(&DatabaseTree, bool)> = vec![];\n    stack.push((tree, false));\n\n    let mut built: Vec<termtree::Tree<String>> = <_>::default();\n\n    while let Some((node, visited)) = stack.pop() {\n        if visited {\n            let mut term_node = termtree::Tree::new(fmt_tree_node(&node.root));\n            term_node.leaves = built.drain(built.len() - node.children.len()..).collect();\n            term_node.leaves.reverse();\n            built.push(term_node);\n        } else {\n            stack.push((node, true));\n            stack.extend(node.children.iter().rev().map(|child| (child, false)));\n        }\n    }\n\n    built\n        .pop()\n        .expect(\"database tree contains a root and we pushed it last\")\n}\n\nfn fmt_tree_node(node: &DatabaseTreeNode) -> String {\n    format!(\n        \"{}{}\",\n        node.database_identity,\n        if node.database_names.is_empty() {\n            <_>::default()\n        } else {\n            format!(\": {}\", node.database_names.iter().join(\", \"))\n        }\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_client_api_messages::http::{DatabaseTree, DatabaseTreeNode};\n    use spacetimedb_lib::{sats::u256, Identity};\n\n    #[test]\n    fn render_termtree() {\n        let tree = DatabaseTree {\n            root: DatabaseTreeNode {\n                database_identity: Identity::ONE,\n                database_names: [\"parent\".into()].into(),\n            },\n            children: vec![\n                DatabaseTree {\n                    root: DatabaseTreeNode {\n                        database_identity: Identity::from_u256(u256::new(2)),\n                        database_names: [\"child\".into()].into(),\n                    },\n                    children: vec![\n                        DatabaseTree {\n                            root: DatabaseTreeNode {\n                                database_identity: Identity::from_u256(u256::new(3)),\n                                database_names: [\"grandchild\".into()].into(),\n                            },\n                            children: vec![],\n                        },\n                        DatabaseTree {\n                            root: DatabaseTreeNode {\n                                database_identity: Identity::from_u256(u256::new(5)),\n                                database_names: [].into(),\n                            },\n                            children: vec![],\n                        },\n                    ],\n                },\n                DatabaseTree {\n                    root: DatabaseTreeNode {\n                        database_identity: Identity::from_u256(u256::new(4)),\n                        database_names: [\"sibling\".into(), \"bro\".into()].into(),\n                    },\n                    children: vec![],\n                },\n            ],\n        };\n        pretty_assertions::assert_eq!(\n            \"\\\n0000000000000000000000000000000000000000000000000000000000000001: parent\n├── 0000000000000000000000000000000000000000000000000000000000000004: bro, sibling\n└── 0000000000000000000000000000000000000000000000000000000000000002: child\n    ├── 0000000000000000000000000000000000000000000000000000000000000005\n    └── 0000000000000000000000000000000000000000000000000000000000000003: grandchild\n\",\n            &as_termtree(&tree).to_string()\n        );\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/describe.rs",
    "content": "use crate::api::ClientApi;\nuse crate::common_args;\nuse crate::config::Config;\nuse crate::subcommands::db_arg_resolution::{load_config_db_targets, resolve_database_with_optional_parts};\nuse crate::util::UNSTABLE_WARNING;\nuse crate::util::{database_identity, get_auth_header};\nuse anyhow::Context;\nuse clap::{Arg, ArgAction, ArgMatches};\nuse spacetimedb_lib::sats;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"describe\")\n        .about(format!(\n            \"Describe the structure of a database or entities within it. {UNSTABLE_WARNING}\"\n        ))\n        .arg(\n            Arg::new(\"describe_parts\")\n                .num_args(0..)\n                .help(\"Describe arguments: [DATABASE] [ENTITY_TYPE ENTITY_NAME]\"),\n        )\n        .arg(\n            Arg::new(\"json\")\n                .long(\"json\")\n                .action(ArgAction::SetTrue)\n                // make not required() once we have a human readable output\n                .required(true)\n                .help(\n                    \"Output the schema in JSON format. Currently required; in the future, omitting this will \\\n                     give human-readable output.\",\n                ),\n        )\n        .arg(common_args::anonymous())\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server hosting the database\"))\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .after_help(\"Run `spacetime help describe` for more detailed information.\\n\")\n}\n\n#[derive(clap::ValueEnum, Clone, Copy)]\nenum EntityType {\n    Reducer,\n    Table,\n}\n\npub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n\n    let json = args.get_flag(\"json\");\n    let no_config = args.get_flag(\"no_config\");\n    let raw_parts: Vec<String> = args\n        .get_many::<String>(\"describe_parts\")\n        .map(|vals| vals.cloned().collect())\n        .unwrap_or_default();\n    let config_targets = load_config_db_targets(no_config)?;\n    let resolved = resolve_database_with_optional_parts(\n        &raw_parts,\n        config_targets.as_deref(),\n        \"spacetime describe [database] [entity_type entity_name] --json [--no-config]\",\n    )?;\n    let entity = match resolved.remaining_args.as_slice() {\n        [] => None,\n        [entity_type, entity_name] => {\n            let entity_type = match entity_type.as_str() {\n                \"reducer\" => EntityType::Reducer,\n                \"table\" => EntityType::Table,\n                _ => {\n                    anyhow::bail!(\n                        \"Invalid entity_type '{}'. Expected one of: reducer, table.\",\n                        entity_type\n                    )\n                }\n            };\n            Some((entity_type, entity_name.as_str()))\n        }\n        _ => {\n            anyhow::bail!(\n                \"Invalid describe arguments.\\nUsage: spacetime describe [database] [entity_type entity_name] --json [--no-config]\"\n            );\n        }\n    };\n\n    let mut config = config;\n    let server_from_cli = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let server = server_from_cli.or(resolved.server.as_deref());\n    let force = args.get_flag(\"force\");\n    let anon_identity = args.get_flag(\"anon_identity\");\n    let conn = crate::api::Connection {\n        host: config.get_host_url(server)?,\n        auth_header: get_auth_header(&mut config, anon_identity, server, !force).await?,\n        database_identity: database_identity(&config, &resolved.database, server).await?,\n        database: resolved.database,\n    };\n    let api = ClientApi::new(conn);\n\n    let module_def = api.module_def().await?;\n\n    if json {\n        fn sats_to_json<T: sats::Serialize>(v: &T) -> serde_json::Result<String> {\n            serde_json::to_string_pretty(sats::serde::SerdeWrapper::from_ref(v))\n        }\n        let json = match entity {\n            Some((EntityType::Reducer, reducer_name)) => {\n                let reducer = module_def\n                    .reducers\n                    .iter()\n                    .find(|r| *r.name == *reducer_name)\n                    .context(\"no such reducer\")?;\n                sats_to_json(reducer)?\n            }\n            Some((EntityType::Table, table_name)) => {\n                let table = module_def\n                    .tables\n                    .iter()\n                    .find(|t| *t.name == *table_name)\n                    .context(\"no such table\")?;\n                sats_to_json(table)?\n            }\n            None => sats_to_json(&module_def)?,\n        };\n\n        // TODO: validate the JSON output\n        println!(\"{json}\");\n    } else {\n        // TODO: human-readable API\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/dev.rs",
    "content": "use crate::common_args::ClearMode;\nuse crate::config::Config;\nuse crate::generate::Language;\nuse crate::spacetime_config::{\n    detect_client_command, find_and_load_with_env_from, CommandConfig, CommandSchema, SpacetimeConfig, CONFIG_FILENAME,\n};\nuse crate::subcommands::init;\nuse crate::util::{\n    add_auth_header_opt, database_identity, find_module_path, get_auth_header, get_login_token_or_log_in,\n    spacetime_reverse_dns, strip_verbatim_prefix, ResponseExt,\n};\nuse crate::{common_args, generate};\nuse crate::{publish, tasks};\nuse anyhow::Context;\nuse clap::parser::ValueSource;\nuse clap::{Arg, ArgMatches, Command};\nuse colored::Colorize;\nuse dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input};\nuse futures::stream::{self, StreamExt};\nuse futures::{AsyncBufReadExt, TryStreamExt};\nuse ignore::gitignore::{Gitignore, GitignoreBuilder};\nuse indicatif::{ProgressBar, ProgressStyle};\nuse notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};\nuse regex::Regex;\nuse serde::Deserialize;\nuse serde_json::json;\nuse std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::fs;\nuse std::io::IsTerminal;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc::channel;\nuse std::time::Duration;\nuse tabled::{\n    settings::{object::Columns, Alignment, Modify, Style},\n    Table, Tabled,\n};\nuse termcolor::{Color, ColorSpec, WriteColor};\nuse tokio::process::{Child, Command as TokioCommand};\nuse tokio::task::JoinHandle;\nuse tokio::time::sleep;\n\npub fn cli() -> Command {\n    Command::new(\"dev\")\n        .about(\"Start development mode with auto-regenerate client module bindings, auto-rebuild, and auto-publish on file changes.\")\n        .arg(\n            Arg::new(\"database\")\n                .help(\"The database name/identity to publish to (optional, will prompt if not provided)\"),\n        )\n        // Deprecated: --database flag for backwards compatibility\n        .arg(\n            Arg::new(\"database-flag\")\n                .long(\"database\")\n                .hide(true)\n                .help(\"DEPRECATED: Use positional argument instead\"),\n        )\n        .arg(\n            Arg::new(\"project-path\")\n                .long(\"project-path\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .default_value(\".\")\n                .help(\"The path to the project directory\"),\n        )\n        .arg(\n            Arg::new(\"module-bindings-path\")\n                .long(\"module-bindings-path\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .default_value(\"src/module_bindings\")\n                .help(\"The path to the module bindings directory relative to the project directory, defaults to `<project-path>/src/module_bindings`\"),\n        )\n        // NOTE: All server templates must have their server code in `spacetimedb/` directory\n        // This is not a requirement in general, but is a requirement for all templates\n        // i.e. `spacetime dev` is valid on non-templates.\n        .arg(\n            Arg::new(\"module-path\")\n                .long(\"module-path\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .help(\"Path to the SpacetimeDB server module, relative to current directory. Defaults to `<project-path>/spacetimedb`.\"),\n        )\n        .arg(\n            Arg::new(\"client-lang\")\n                .long(\"client-lang\")\n                .value_parser(clap::value_parser!(Language))\n                .help(\"The programming language for the generated client module bindings (e.g., typescript, csharp, python). If not specified, it will be detected from the project.\"),\n        )\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server to publish to\"))\n        .arg(common_args::yes())\n        .arg(common_args::clear_database())\n        .arg(\n            Arg::new(\"template\")\n                .short('t')\n                .long(\"template\")\n                .value_name(\"TEMPLATE\")\n                .help(\"Template ID or GitHub repository (owner/repo or URL) for project initialization\"),\n        )\n        .arg(\n            Arg::new(\"run\")\n                .long(\"run\")\n                .value_name(\"COMMAND\")\n                .help(\"Command to run the client development server (overrides spacetime.json config)\"),\n        )\n        .arg(\n            Arg::new(\"server-only\")\n                .long(\"server-only\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Only run the server (module) without starting the client\"),\n        )\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .arg(\n            Arg::new(\"env\")\n                .long(\"env\")\n                .value_name(\"ENV\")\n                .help(\"Environment name for config file layering (e.g., dev, staging). Defaults to 'dev'.\"),\n        )\n        .arg(\n            Arg::new(\"skip_publish\")\n                .long(\"skip-publish\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Skip the publish step\"),\n        )\n        .arg(\n            Arg::new(\"skip_generate\")\n                .long(\"skip-generate\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Skip the generate step\"),\n        )\n}\n\n#[derive(Deserialize)]\nstruct DatabasesResult {\n    pub identities: Vec<String>,\n}\n\n#[derive(Tabled, Clone)]\nstruct DatabaseRow {\n    pub identity: String,\n    pub name: String,\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let project_path = args.get_one::<PathBuf>(\"project-path\").unwrap();\n    let module_path_from_cli = args.get_one::<PathBuf>(\"module-path\");\n    let module_bindings_path = args.get_one::<PathBuf>(\"module-bindings-path\").unwrap();\n    let client_language = args.get_one::<Language>(\"client-lang\");\n    let clear_database = args\n        .get_one::<ClearMode>(\"clear-database\")\n        .copied()\n        .unwrap_or(ClearMode::OnConflict);\n    let force = args.get_flag(\"force\");\n\n    // If you don't specify a server, we default to your default server\n    // If you don't have one of those, we default to \"maincloud\"\n    let server_from_cli = args.get_one::<String>(\"server\").map(|s| s.as_str());\n\n    let default_server_name = config.default_server_name().map(|s| s.to_string());\n\n    let mut resolved_server = server_from_cli\n        .or(default_server_name.as_deref())\n        .ok_or_else(|| anyhow::anyhow!(\"Server not specified and no default server configured.\"))?;\n\n    let cwd = std::env::current_dir()?;\n    let mut project_dir = if project_path.is_absolute() {\n        project_path.clone()\n    } else {\n        cwd.join(project_path)\n    };\n\n    if module_bindings_path.is_absolute() {\n        anyhow::bail!(\"Module bindings path must be a relative path\");\n    }\n    let mut module_bindings_dir = project_dir.join(module_bindings_path);\n\n    let mut spacetimedb_dir = match module_path_from_cli {\n        Some(path) => {\n            if path.is_absolute() {\n                path.clone()\n            } else {\n                std::env::current_dir()?.join(path)\n            }\n        }\n        None => project_dir.join(\"spacetimedb\"),\n    };\n\n    let no_config = args.get_flag(\"no_config\");\n    let skip_publish = args.get_flag(\"skip_publish\");\n    let skip_generate = args.get_flag(\"skip_generate\");\n\n    // --env defaults to \"dev\" for spacetime dev\n    let env = args.get_one::<String>(\"env\").map(|s| s.as_str()).unwrap_or(\"dev\");\n\n    // Load spacetime.json config early so we can use it for determining project\n    // directories\n    let mut loaded_config = if no_config {\n        None\n    } else {\n        find_and_load_with_env_from(Some(env), project_dir.clone()).with_context(|| \"Failed to load spacetime.json\")?\n    };\n\n    // If config was found while starting from a subdirectory (for example from `spacetimedb/`),\n    // treat the config directory as the project root for all relative defaults.\n    if let Some(lc) = loaded_config.as_ref() {\n        project_dir = lc.config_dir.clone();\n        module_bindings_dir = project_dir.join(module_bindings_path);\n        if module_path_from_cli.is_none() {\n            spacetimedb_dir = project_dir.join(\"spacetimedb\");\n        }\n    }\n\n    let has_any_config_files = loaded_config.is_some();\n\n    // Config exists, but default module dir is missing: recover by asking for module-path\n    // and persisting it on the root config.\n    if !no_config && has_any_config_files && (!spacetimedb_dir.exists() || !spacetimedb_dir.is_dir()) {\n        let merged_has_module_path = loaded_config\n            .as_ref()\n            .and_then(|lc| lc.config.additional_fields.get(\"module-path\"))\n            .and_then(|v| v.as_str())\n            .is_some();\n\n        if !merged_has_module_path && module_path_from_cli.is_none() {\n            let files = loaded_config\n                .as_ref()\n                .map(|lc| {\n                    lc.loaded_files\n                        .iter()\n                        .map(|f| strip_verbatim_prefix(f).display().to_string())\n                        .collect::<Vec<_>>()\n                        .join(\", \")\n                })\n                .unwrap_or_else(|| \"spacetime.json\".to_string());\n            println!(\"{} {}\", \"Found config files:\".yellow().bold(), files.dimmed());\n            println!(\n                \"{}\",\n                \"Could not determine module path because no `module-path` was found and `./spacetimedb` does not exist.\"\n                    .yellow()\n            );\n            let should_provide = Confirm::new()\n                .with_prompt(\"Would you like to provide --module-path now?\")\n                .default(true)\n                .interact()?;\n            if !should_provide {\n                anyhow::bail!(\"Cannot continue without a module path.\");\n            }\n\n            let config_dir = loaded_config\n                .as_ref()\n                .map(|lc| lc.config_dir.clone())\n                .ok_or_else(|| anyhow::anyhow!(\"Missing loaded config directory\"))?;\n\n            let provided_module_path: String = Input::with_theme(&ColorfulTheme::default())\n                .with_prompt(\"Module path\")\n                .default(\"spacetimedb\".to_string())\n                .validate_with({\n                    let config_dir = config_dir.clone();\n                    move |input: &String| -> Result<(), String> {\n                        let candidate = PathBuf::from(input);\n                        let resolved = if candidate.is_absolute() {\n                            candidate\n                        } else {\n                            config_dir.join(&candidate)\n                        };\n                        if resolved.exists() {\n                            Ok(())\n                        } else {\n                            Err(format!(\n                                \"Path does not exist: {} (resolved to {})\",\n                                input,\n                                resolved.display()\n                            ))\n                        }\n                    }\n                })\n                .interact_text()?;\n\n            // Save to root `spacetime.json` (not env/local overlays), then reload merged config.\n            let saved_path = save_root_module_path_to_spacetime_json(&config_dir, &provided_module_path)?;\n            println!(\n                \"{} Updated {}\",\n                \"✓\".green(),\n                strip_verbatim_prefix(&saved_path).display()\n            );\n\n            loaded_config = find_and_load_with_env_from(Some(env), project_dir.clone())\n                .with_context(|| \"Failed to reload spacetime.json after updating module-path\")?;\n        }\n    }\n\n    // If config has a module-path and CLI didn't provide one, resolve spacetimedb_dir from it.\n    // This handles the case where spacetime.json specifies module-path but has no publish targets.\n    if module_path_from_cli.is_none()\n        && let Some(config_module_path) = loaded_config\n            .as_ref()\n            .and_then(|lc| lc.config.additional_fields.get(\"module-path\"))\n            .and_then(|v| v.as_str())\n    {\n        let p = PathBuf::from(config_module_path);\n        spacetimedb_dir = if p.is_absolute() { p } else { project_dir.join(p) };\n    }\n\n    let spacetime_config = loaded_config.as_ref().map(|lc| &lc.config);\n    // A config has publish targets if it has a \"database\" field or children\n    let has_publish_targets_in_config = spacetime_config\n        .map(|c| c.additional_fields.contains_key(\"database\") || c.children.is_some())\n        .unwrap_or(false);\n    let has_generate_targets_in_config = spacetime_config\n        .and_then(|c| c.generate.as_ref())\n        .map(|g| !g.is_empty())\n        .unwrap_or(false);\n\n    let module_path_from_cli_flag = args.value_source(\"module-path\") == Some(ValueSource::CommandLine);\n    let project_path_from_cli_flag = args.value_source(\"project-path\") == Some(ValueSource::CommandLine);\n    let module_bindings_path_from_cli_flag =\n        args.value_source(\"module-bindings-path\") == Some(ValueSource::CommandLine);\n\n    if has_publish_targets_in_config && module_path_from_cli_flag {\n        anyhow::bail!(\n            \"`--module-path` cannot be used when `spacetime.json` contains publish targets. \\\n             Remove `--module-path` or run without publish targets in config.\"\n        );\n    }\n\n    if has_generate_targets_in_config\n        && (module_path_from_cli_flag || project_path_from_cli_flag || module_bindings_path_from_cli_flag)\n    {\n        anyhow::bail!(\n            \"`--module-path`, `--project-path`, and `--module-bindings-path` cannot be used when \\\n             `spacetime.json` contains generate targets. Remove these flags or remove generate targets from config.\"\n        );\n    }\n\n    // Fetch the database name if it was passed through a CLI arg\n    let database_name_from_cli: Option<String> = args\n        .get_one::<String>(\"database\")\n        .or_else(|| args.get_one::<String>(\"database-flag\"))\n        .map(|name| {\n            if args.get_one::<String>(\"database-flag\").is_some() {\n                println!(\n                    \"{} {}\",\n                    \"Warning:\".yellow().bold(),\n                    \"--database flag is deprecated. Use positional argument instead: spacetime dev <database>\".dimmed()\n                );\n            }\n            name.clone()\n        });\n    let database_name_from_cli_for_init = database_name_from_cli.clone();\n\n    // Build publish configs. It is easier to work with one type of data,\n    // so if we don't have publish configs from the config file, we build a single\n    // publish config based on the CLI args\n    let publish_cmd = publish::cli();\n    let publish_schema = publish::build_publish_schema(&publish_cmd)?;\n\n    // Create ArgMatches for publish command\n    let mut publish_argv: Vec<String> = vec![\"publish\".to_string()];\n    if let Some(db) = &database_name_from_cli {\n        publish_argv.push(db.clone());\n    }\n    if let Some(srv) = args.get_one::<String>(\"server\") {\n        publish_argv.push(\"--server\".to_string());\n        publish_argv.push(srv.clone());\n    }\n\n    let publish_args = publish_cmd\n        .clone()\n        .try_get_matches_from(publish_argv)\n        .context(\"Failed to create publish arguments\")?;\n\n    let mut publish_configs = determine_publish_configs(\n        database_name_from_cli,\n        spacetime_config,\n        &publish_cmd,\n        &publish_schema,\n        &publish_args,\n        resolved_server,\n        &spacetimedb_dir,\n    )?;\n\n    // Check if we are in a SpacetimeDB project directory, but only if we don't have any\n    // publish_configs that would specify desired modules\n    if !has_any_config_files\n        && module_path_from_cli.is_none()\n        && (!spacetimedb_dir.exists() || !spacetimedb_dir.is_dir())\n        && let Some(found_module) = find_module_path(&std::env::current_dir()?)\n    {\n        spacetimedb_dir = found_module;\n    }\n\n    if !has_any_config_files && (!spacetimedb_dir.exists() || !spacetimedb_dir.is_dir()) {\n        println!(\"{}\", \"No SpacetimeDB project found in current directory.\".yellow());\n        let should_init = Confirm::new()\n            .with_prompt(\"Would you like to initialize a new project?\")\n            .default(true)\n            .interact()?;\n\n        if should_init {\n            let init_options = init::InitOptions {\n                local: resolved_server == \"local\",\n                template: args.get_one::<String>(\"template\").cloned(),\n                project_name_default: database_name_from_cli_for_init.clone(),\n                database_name_default: database_name_from_cli_for_init.clone(),\n                skip_next_steps: true,\n                ..Default::default()\n            };\n            let created_project_path = init::exec_with_options(&mut config, &init_options).await?;\n\n            let canonical_created_path = created_project_path\n                .canonicalize()\n                .context(\"Failed to canonicalize created project path\")?;\n            spacetimedb_dir = canonical_created_path.join(\"spacetimedb\");\n            module_bindings_dir = canonical_created_path.join(module_bindings_path);\n            project_dir = canonical_created_path.clone();\n\n            // If the project was created in a subdirectory, hint the user to cd into it\n            // and show useful CLI commands they can run from there.\n            let current_dir = std::env::current_dir().context(\"Failed to get current directory\")?;\n            let display_path = strip_verbatim_prefix(&canonical_created_path);\n            if display_path != current_dir {\n                let rel_path = display_path.strip_prefix(&current_dir).unwrap_or(display_path);\n                println!(\n                    \"\\n{} To interact with your database, open a new terminal and run:\",\n                    \"Tip:\".yellow().bold(),\n                );\n                println!(\"  cd ./{}\", rel_path.display());\n                println!(\"  spacetime call add Alice\");\n                println!(\"  spacetime sql \\\"SELECT * FROM person\\\"\");\n                println!(\"  spacetime logs\");\n                println!();\n            }\n\n            if !spacetimedb_dir.exists() {\n                anyhow::bail!(\"Project initialization did not create spacetimedb directory\");\n            }\n        } else {\n            anyhow::bail!(\"Not in a SpacetimeDB project directory\");\n        }\n    } else if args.get_one::<String>(\"template\").is_some() {\n        println!(\n            \"{}\",\n            \"Warning: --template option is ignored because a SpacetimeDB project already exists.\".yellow()\n        );\n    }\n\n    if let Some(config) = publish_configs.first() {\n        // if we have publish configs and we're past spacetimedb_dir manipulation,\n        // we should set spacetimedb_dir to the path of the first config as this will be\n        // later used for next steps\n        if let Some(path) = config\n            .get_one::<PathBuf>(\"module_path\")\n            .context(\"failed to read module_path from config\")?\n        {\n            spacetimedb_dir = if path.is_absolute() {\n                path\n            } else {\n                project_dir.join(path)\n            };\n        }\n    }\n\n    // Refresh layered config after potential init/config creation so downstream behavior\n    // uses the latest spacetime.json + local/env overlays.\n    if !no_config {\n        loaded_config = find_and_load_with_env_from(Some(env), project_dir.clone())\n            .with_context(|| \"Failed to reload spacetime.json after initialization\")?;\n    }\n\n    let spacetime_config = loaded_config.as_ref().map(|lc| &lc.config);\n    let using_spacetime_config = spacetime_config.is_some();\n    let generate_configs_from_file: Vec<HashMap<String, serde_json::Value>> =\n        spacetime_config.and_then(|c| c.generate.clone()).unwrap_or_default();\n\n    // Re-resolve publish targets now that config files may have been created by init.\n    if publish_configs.is_empty() {\n        publish_configs = determine_publish_configs(\n            database_name_from_cli_for_init.clone(),\n            spacetime_config,\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            resolved_server,\n            &spacetimedb_dir,\n        )?;\n    }\n\n    let use_local = resolved_server == \"local\";\n\n    if !no_config && let Some(path) = create_default_spacetime_config_if_missing(&project_dir)? {\n        println!(\"{} Created {}\", \"✓\".green(), strip_verbatim_prefix(&path).display());\n    }\n\n    // If we don't have any publish configs by now, we need to ask the user about the\n    // database they want to use. This should only happen if no configs are available\n    // in the config file and no database name has been passed through the CLI\n    if publish_configs.is_empty() {\n        println!(\"\\n{}\", \"Found existing SpacetimeDB project.\".green());\n        println!(\"Now we need to select a database to publish to.\\n\");\n\n        let selected = if use_local {\n            generate_database_name()\n        } else {\n            // If not logged in before, but login was successful just now, this will have the token\n            let token = get_login_token_or_log_in(&mut config, Some(resolved_server), !force).await?;\n\n            let choice = FuzzySelect::with_theme(&ColorfulTheme::default())\n                .with_prompt(\"Database selection\")\n                .items(&[\"Create new database with random name\", \"Select from existing databases\"])\n                .default(0)\n                .interact()?;\n\n            if choice == 0 {\n                generate_database_name()\n            } else {\n                select_database(&config, resolved_server, &token).await?\n            }\n        };\n\n        println!(\"\\n{} {}\", \"Selected database:\".green().bold(), selected.cyan());\n        println!(\n            \"{} {}\",\n            \"Tip:\".yellow().bold(),\n            format!(\"Use `spacetime dev {}` to skip this question next time\", selected).dimmed()\n        );\n\n        let mut config_map = HashMap::new();\n        config_map.insert(\"database\".to_string(), json!(selected));\n        config_map.insert(\"server\".to_string(), json!(resolved_server));\n\n        publish_configs = vec![CommandConfig::new(&publish_schema, config_map, &publish_args)?];\n    }\n\n    if !no_config {\n        let db_to_persist = database_name_from_cli_for_init.as_deref().or_else(|| {\n            publish_configs\n                .first()\n                .and_then(|cfg| cfg.get_config_value(\"database\"))\n                .and_then(|v| v.as_str())\n        });\n        if let Some(db_name) = db_to_persist\n            && let Some(path) = create_local_spacetime_config_if_missing(&project_dir, db_name)?\n        {\n            println!(\"{} Created {}\", \"✓\".green(), strip_verbatim_prefix(&path).display());\n        }\n    }\n\n    if !module_bindings_dir.exists() {\n        // Create the module bindings directory if it doesn't exist\n        std::fs::create_dir_all(&module_bindings_dir).with_context(|| {\n            format!(\n                \"Failed to create module bindings path {}\",\n                module_bindings_dir.display()\n            )\n        })?;\n    } else if !module_bindings_dir.is_dir() {\n        anyhow::bail!(\n            \"Module bindings path {} exists but is not a directory.\",\n            module_bindings_path.display()\n        );\n    }\n\n    // Check if we need to login to maincloud\n    // Either because --server maincloud was provided, or because any of the publish configs use maincloud\n    let needs_maincloud_login = resolved_server == \"maincloud\"\n        || spacetime_config\n            .map(|c| {\n                c.iter_all_targets().any(|target| {\n                    target\n                        .additional_fields\n                        .get(\"server\")\n                        .and_then(|v| v.as_str())\n                        .map(|s| s == \"maincloud\")\n                        .unwrap_or(false)\n                })\n            })\n            .unwrap_or(false);\n\n    if needs_maincloud_login && config.spacetimedb_token().is_none() {\n        let should_login = Confirm::new()\n            .with_prompt(\"Would you like to sign in now?\")\n            .default(true)\n            .interact()?;\n        if !should_login && server_from_cli.is_some() {\n            // The user explicitly provided --server maincloud but doesn't want to log in\n            anyhow::bail!(\"Login required to publish to maincloud server\");\n        } else if !should_login {\n            // Print warning saying that without logging in we will use local server regardless\n            // of what their default server is in their config\n            println!(\n                \"{} {}\",\n                \"Warning:\".yellow().bold(),\n                \"Without logging in, the local server will be used regardless of your default server.\".dimmed()\n            );\n            // Switch the server to local\n            resolved_server = \"local\";\n        } else {\n            // Login\n            get_login_token_or_log_in(&mut config, Some(resolved_server), !force).await?;\n        }\n    }\n\n    // Determine client command: CLI flag > config file > auto-detect (and save)\n    let server_only = args.get_flag(\"server-only\");\n\n    let client_command = if server_only {\n        None\n    } else if let Some(cmd) = args.get_one::<String>(\"run\") {\n        // Explicit CLI flag takes priority\n        Some(cmd.clone())\n    } else if no_config {\n        // --no-config means \"don't read or write spacetime config files\".\n        detect_client_command(&project_dir).map(|(cmd, _)| cmd)\n    } else if let Some(sc) = spacetime_config {\n        // Reuse already-loaded config instead of loading again\n        if let Some(ref lc) = loaded_config {\n            let files: Vec<_> = lc\n                .loaded_files\n                .iter()\n                .map(|f| strip_verbatim_prefix(f).display().to_string())\n                .collect();\n            println!(\"{} Using configuration from {}\", \"✓\".green(), files.join(\", \"));\n        }\n\n        if sc.dev.as_ref().and_then(|d| d.run.as_ref()).is_none() {\n            detect_and_save_client_command(&project_dir, Some(sc.clone()))\n        } else {\n            sc.dev.as_ref().and_then(|d| d.run.clone())\n        }\n    } else {\n        // No config file - try to detect and create new\n        detect_and_save_client_command(&project_dir, None)\n    };\n\n    // Extract database names from publish configs for log streaming\n    let db_names_for_logging: Vec<String> = publish_configs\n        .iter()\n        .map(|config| {\n            config\n                .get_config_value(\"database\")\n                .and_then(|v| v.as_str())\n                .ok_or_else(|| anyhow::anyhow!(\"database is a required field in publish config\"))\n                .map(|s| s.to_string())\n        })\n        .collect::<Result<Vec<_>, _>>()?;\n\n    // Use first database for client process\n    let db_name_for_client = &db_names_for_logging[0];\n\n    // Extract watch directories from publish configs\n    let watch_dirs = extract_watch_dirs(&publish_configs, &spacetimedb_dir, &project_dir);\n\n    println!(\"\\n{}\", \"Starting development mode...\".green().bold());\n    if db_names_for_logging.len() == 1 {\n        println!(\"Database: {}\", db_names_for_logging[0].cyan());\n    } else {\n        println!(\"Databases: {}\", db_names_for_logging.join(\", \").cyan());\n    }\n\n    // Announce watch directories\n    if watch_dirs.len() == 1 {\n        println!(\n            \"Watching for changes in: {}\",\n            strip_verbatim_prefix(watch_dirs.iter().next().unwrap())\n                .display()\n                .to_string()\n                .cyan()\n        );\n    } else {\n        let watch_dirs_vec: Vec<_> = watch_dirs.iter().collect();\n        println!(\"Watching for changes in {} directories:\", watch_dirs.len());\n        for dir in &watch_dirs_vec {\n            println!(\"  - {}\", strip_verbatim_prefix(dir).display().to_string().cyan());\n        }\n    }\n\n    // Safety prompt: warn if any selected database target is defined in spacetime.json.\n    // spacetime.local.json is gitignored and personal, so it's fine for dev use.\n    if let Some(ref lc) = loaded_config {\n        let database_sources = resolve_database_sources(&lc.config);\n        let databases_from_main_config: Vec<String> = db_names_for_logging\n            .iter()\n            .filter(|db| {\n                database_sources\n                    .get((*db).as_str())\n                    .is_some_and(|src| src.as_deref() == Some(\"spacetime.json\"))\n            })\n            .cloned()\n            .collect();\n\n        if !databases_from_main_config.is_empty() && !force {\n            eprintln!(\n                \"{} Database(s) `{}` are defined in spacetime.json (usually reserved for production databases).\",\n                \"Warning:\".yellow().bold(),\n                databases_from_main_config.join(\", \")\n            );\n            let should_continue = Confirm::new()\n                .with_prompt(\"Do you want to proceed with publishing in dev mode?\")\n                .default(true)\n                .interact()?;\n            if !should_continue {\n                anyhow::bail!(\"Aborted.\");\n            }\n        }\n    }\n\n    if let Some(ref cmd) = client_command {\n        println!(\"Client command: {}\", cmd.cyan());\n    }\n    println!(\"{}\", \"Press Ctrl+C to stop\".dimmed());\n    println!();\n    let loaded_config_dir = loaded_config.as_ref().map(|lc| lc.config_dir.clone());\n\n    generate_build_and_publish(\n        &config,\n        &project_dir,\n        loaded_config_dir.as_deref(),\n        &spacetimedb_dir,\n        &module_bindings_dir,\n        client_language,\n        clear_database,\n        &publish_configs,\n        &generate_configs_from_file,\n        using_spacetime_config,\n        server_from_cli,\n        force,\n        skip_publish,\n        skip_generate,\n    )\n    .await?;\n\n    // Sleep for a second to allow the database to be published on Maincloud\n    sleep(Duration::from_secs(1)).await;\n\n    // Start log streams for all targets\n    let use_prefix = db_names_for_logging.len() > 1;\n    let mut log_handles = Vec::new();\n    for config_entry in &publish_configs {\n        let db_name = config_entry\n            .get_config_value(\"database\")\n            .and_then(|v| v.as_str())\n            .expect(\"database is a required field\");\n\n        let server_opt = config_entry.get_one::<String>(\"server\")?;\n        let server_for_db = server_opt.as_deref().unwrap_or(resolved_server);\n\n        let db_identity = database_identity(&config, db_name, Some(server_for_db)).await?;\n        let prefix = if use_prefix { Some(db_name.to_string()) } else { None };\n        let handle = start_log_stream(\n            config.clone(),\n            db_identity.to_hex().to_string(),\n            Some(server_for_db),\n            prefix,\n        )\n        .await?;\n        log_handles.push(handle);\n    }\n\n    // Start the client development server if configured\n    let server_opt_client = publish_configs\n        .first()\n        .and_then(|c| c.get_one::<String>(\"server\").ok().flatten());\n    let server_for_client = server_opt_client.as_deref().unwrap_or(resolved_server);\n    let server_host_url = config.get_host_url(Some(server_for_client))?;\n    let mut client_handle = if let Some(ref cmd) = client_command {\n        let mut child = start_client_process(cmd, &project_dir, db_name_for_client, &server_host_url)?;\n\n        // Give the process a moment to fail fast (e.g., command not found, missing deps)\n        sleep(Duration::from_millis(200)).await;\n        match child.try_wait() {\n            Ok(Some(status)) if !status.success() => {\n                anyhow::bail!(\n                    \"Client command '{}' failed immediately with exit code: {}\",\n                    cmd,\n                    status\n                        .code()\n                        .map(|c| c.to_string())\n                        .unwrap_or_else(|| \"unknown\".to_string())\n                );\n            }\n            Err(e) => {\n                anyhow::bail!(\"Failed to check client process status: {}\", e);\n            }\n            _ => {} // Still running or exited successfully (unusual but ok)\n        }\n        Some(child)\n    } else {\n        None\n    };\n\n    let gitignore = build_gitignore_matcher(&project_dir, &spacetimedb_dir);\n\n    let (tx, rx) = channel();\n    let mut watcher: RecommendedWatcher = Watcher::new(\n        move |res: Result<Event, notify::Error>| {\n            if let Ok(event) = res\n                && matches!(\n                    event.kind,\n                    notify::EventKind::Modify(_) | notify::EventKind::Create(_) | notify::EventKind::Remove(_)\n                )\n                && event.paths.iter().any(|p| !should_ignore_path(p, &gitignore))\n            {\n                let _ = tx.send(());\n            }\n        },\n        notify::Config::default().with_poll_interval(Duration::from_millis(500)),\n    )?;\n\n    // Watch all directories\n    for watch_dir in &watch_dirs {\n        watcher.watch(watch_dir, RecursiveMode::Recursive)?;\n    }\n\n    let mut debounce_timer;\n    loop {\n        // Use recv_timeout so we can periodically check if the client process exited\n        match rx.recv_timeout(Duration::from_secs(1)) {\n            Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => break Ok(()),\n            Ok(()) => {\n                debounce_timer = std::time::Instant::now();\n                while debounce_timer.elapsed() < Duration::from_millis(300) {\n                    if rx.recv_timeout(Duration::from_millis(100)).is_ok() {\n                        debounce_timer = std::time::Instant::now();\n                    }\n                }\n\n                println!(\"\\n{}\", \"File change detected, rebuilding...\".yellow());\n                match generate_build_and_publish(\n                    &config,\n                    &project_dir,\n                    loaded_config_dir.as_deref(),\n                    &spacetimedb_dir,\n                    &module_bindings_dir,\n                    client_language,\n                    clear_database,\n                    &publish_configs,\n                    &generate_configs_from_file,\n                    using_spacetime_config,\n                    server_from_cli,\n                    force,\n                    skip_publish,\n                    skip_generate,\n                )\n                .await\n                {\n                    Ok(_) => {}\n                    Err(e) => {\n                        eprintln!(\"{} {}\", \"Error:\".red().bold(), e);\n                        println!(\"{}\", \"Waiting for next change...\".dimmed());\n                    }\n                }\n            }\n            Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {\n                // No rebuild yet. Check if the client process has exited.\n                let Some(ref mut child) = client_handle else {\n                    continue;\n                };\n                match child.try_wait() {\n                    Ok(None) => {}\n                    Ok(Some(status)) => {\n                        client_handle = None;\n                        let code = status\n                            .code()\n                            .map(|c| c.to_string())\n                            .unwrap_or_else(|| \"unknown\".to_string());\n                        println!(\n                            \"\\n{} {}. {}\",\n                            \"Client process exited with code\".yellow(),\n                            code,\n                            \"File watcher is still active.\".dimmed()\n                        );\n                    }\n                    Err(e) => {\n                        client_handle = None;\n                        eprintln!(\n                            \"\\n{} Failed to check client process status: {}\",\n                            \"Warning:\".yellow().bold(),\n                            e\n                        );\n                    }\n                }\n            }\n        };\n    }\n}\n\nfn determine_publish_configs<'a>(\n    database_name: Option<String>,\n    spacetime_config: Option<&SpacetimeConfig>,\n    publish_cmd: &Command,\n    publish_schema: &'a CommandSchema,\n    publish_args: &'a ArgMatches,\n    resolved_server: &str,\n    default_module_path: &Path,\n) -> anyhow::Result<Vec<CommandConfig<'a>>> {\n    // Build publish configs. It is easier to work with one type of data,\n    // so if we don't have publish configs from the config file, we build a single\n    // publish config based on the CLI args\n    let mut publish_configs: Vec<CommandConfig> = vec![];\n\n    if let Some(config) = spacetime_config {\n        // Get and filter publish configs if the config has database targets\n        if config.additional_fields.contains_key(\"database\") || config.children.is_some() {\n            publish_configs = publish::get_filtered_publish_configs(config, publish_cmd, publish_schema, publish_args)?;\n        }\n    }\n\n    if !publish_configs.is_empty() {\n        return Ok(publish_configs);\n    }\n\n    // If we still have no configs, it means that filtering by the database name filtered out\n    // all configs, we assume the user wants to run with a different DB\n    if let Some(ref db_name) = database_name {\n        let mut config_map = HashMap::new();\n        config_map.insert(\"database\".to_string(), json!(db_name));\n        config_map.insert(\"server\".to_string(), json!(resolved_server));\n        config_map.insert(\"module-path\".to_string(), json!(default_module_path.to_string_lossy()));\n\n        Ok(vec![CommandConfig::new(publish_schema, config_map, publish_args)?])\n    } else {\n        // If there is no provided database name nor publish configs return no\n        // configs, we will handle it by asking user for a database or auto-generate one\n        Ok(vec![])\n    }\n}\n\n/// Upserts all SPACETIMEDB_DB_NAME and SPACETIMEDB_HOST variants into `.env.local`,\n/// preserving comments/formatting and leaving unrelated keys unchanged.\nfn upsert_env_db_names_and_hosts(env_path: &Path, server_host_url: &str, database_name: &str) -> anyhow::Result<()> {\n    // Framework-agnostic variants (same list for both DB_NAME and HOST)\n    let prefixes = [\n        \"SPACETIMEDB\",             // generic / backend\n        \"VITE_SPACETIMEDB\",        // Vite\n        \"NEXT_PUBLIC_SPACETIMEDB\", // Next.js\n        \"REACT_APP_SPACETIMEDB\",   // CRA\n        \"EXPO_PUBLIC_SPACETIMEDB\", // Expo\n        \"PUBLIC_SPACETIMEDB\",      // SvelteKit\n    ];\n\n    let mut contents = if env_path.exists() {\n        fs::read_to_string(env_path)?\n    } else {\n        String::new()\n    };\n    let original_contents = contents.clone();\n\n    for prefix in prefixes {\n        for (suffix, value) in [(\"DB_NAME\", database_name), (\"HOST\", server_host_url)] {\n            let key = format!(\"{prefix}_{suffix}\");\n            let re = Regex::new(&format!(r\"(?m)^(?P<prefix>\\s*{key}\\s*=\\s*)(?P<val>.*)$\"))?;\n            if re.is_match(&contents) {\n                contents = re.replace_all(&contents, format!(\"${{prefix}}{value}\")).to_string();\n            } else {\n                if !contents.is_empty() && !contents.ends_with('\\n') {\n                    contents.push('\\n');\n                }\n                contents.push_str(&format!(\"{key}={value}\\n\"));\n            }\n        }\n    }\n\n    if !contents.ends_with('\\n') {\n        contents.push('\\n');\n    }\n\n    if contents != original_contents {\n        fs::write(env_path, contents)?;\n    }\n    Ok(())\n}\n\n#[allow(clippy::too_many_arguments)]\nasync fn generate_build_and_publish(\n    config: &Config,\n    project_dir: &Path,\n    config_dir: Option<&Path>,\n    spacetimedb_dir: &Path,\n    module_bindings_dir: &Path,\n    client_language: Option<&Language>,\n    clear_database: ClearMode,\n    publish_configs: &[CommandConfig<'_>],\n    generate_configs: &[HashMap<String, serde_json::Value>],\n    using_spacetime_config: bool,\n    server: Option<&str>,\n    yes: bool,\n    skip_publish: bool,\n    skip_generate: bool,\n) -> Result<(), anyhow::Error> {\n    println!(\"{}\", \"Building...\".cyan());\n    let (_path_to_program, _host_type) =\n        tasks::build(spacetimedb_dir, Some(Path::new(\"src\")), false, None).context(\"Failed to build project\")?;\n    println!(\"{}\", \"Build complete!\".green());\n\n    // For TypeScript client, always update .env.local with the database name\n    // from config so the client connects to the correct database.\n    if let Some(first_config) = publish_configs.first() {\n        let is_ts_client = client_language == Some(&Language::TypeScript)\n            || generate::resolve_language(spacetimedb_dir, client_language.copied())\n                .map(|l| l == Language::TypeScript)\n                .unwrap_or(false);\n\n        if is_ts_client && let Some(first_db_name) = first_config.get_config_value(\"database\").and_then(|v| v.as_str())\n        {\n            let server_for_env = server.or_else(|| first_config.get_config_value(\"server\").and_then(|v| v.as_str()));\n\n            println!(\n                \"{} {}...\",\n                \"Updating .env.local with database name\".cyan(),\n                first_db_name\n            );\n            let env_path = project_dir.join(\".env.local\");\n            let server_host_url = config.get_host_url(server_for_env)?;\n            upsert_env_db_names_and_hosts(&env_path, &server_host_url, first_db_name)?;\n        }\n    }\n\n    if skip_generate {\n        println!(\"{}\", \"Skipping generate step (--skip-generate).\".dimmed());\n    } else if using_spacetime_config {\n        if generate_configs.is_empty() {\n            println!(\n                \"{}\",\n                \"No generate targets in spacetime.json. Skipping module bindings generation.\".dimmed()\n            );\n        } else {\n            println!(\"{}\", \"Generating module bindings from spacetime.json...\".cyan());\n            generate::exec_from_entries(\n                generate_configs.to_vec(),\n                crate::generate::extract_descriptions,\n                yes,\n                config_dir,\n            )\n            .await?;\n        }\n    } else {\n        let resolved_client_language = generate::resolve_language(spacetimedb_dir, client_language.copied())?;\n\n        println!(\"{}\", \"Generating module bindings...\".cyan());\n        let generate_entry = generate::build_generate_entry(\n            Some(spacetimedb_dir),\n            Some(resolved_client_language),\n            Some(module_bindings_dir),\n        );\n        generate::exec_from_entries(\n            vec![generate_entry],\n            crate::generate::extract_descriptions,\n            yes,\n            config_dir,\n        )\n        .await?;\n    }\n\n    if skip_publish {\n        println!(\"{}\", \"Skipping publish step (--skip-publish).\".dimmed());\n        return Ok(());\n    }\n\n    println!(\"{}\", \"Publishing...\".cyan());\n\n    // Loop through all publish configs\n    for config_entry in publish_configs {\n        let db_name = config_entry\n            .get_config_value(\"database\")\n            .and_then(|v| v.as_str())\n            .ok_or_else(|| anyhow::anyhow!(\"database is a required field in publish config\"))?;\n\n        // Read module_path from each config entry, falling back to the shared spacetimedb_dir\n        let entry_module_path = config_entry\n            .get_config_value(\"module_path\")\n            .and_then(|v| v.as_str())\n            .map(|s| s.to_string());\n        let module_path_str = entry_module_path\n            .as_deref()\n            .unwrap_or_else(|| spacetimedb_dir.to_str().expect(\"spacetimedb_dir should be valid UTF-8\"));\n\n        if publish_configs.len() > 1 {\n            println!(\"{} {}...\", \"Publishing to\".cyan(), db_name.cyan().bold());\n        }\n\n        let mut publish_entry = HashMap::new();\n        publish_entry.insert(\"database\".to_string(), json!(db_name));\n        publish_entry.insert(\"module-path\".to_string(), json!(module_path_str));\n\n        // Forward per-target server from config if set, or CLI server override\n        if let Some(srv) = server {\n            publish_entry.insert(\"server\".to_string(), json!(srv));\n        } else if let Some(srv) = config_entry.get_config_value(\"server\").and_then(|v| v.as_str()) {\n            publish_entry.insert(\"server\".to_string(), json!(srv));\n        }\n\n        // Forward per-target build options if set\n        if let Some(build_opts) = config_entry.get_config_value(\"build_options\").and_then(|v| v.as_str())\n            && !build_opts.is_empty()\n        {\n            publish_entry.insert(\"build-options\".to_string(), json!(build_opts));\n        }\n\n        // Forward break-clients if set\n        if config_entry\n            .get_config_value(\"break_clients\")\n            .and_then(|v| v.as_bool())\n            .unwrap_or(false)\n        {\n            publish_entry.insert(\"break-clients\".to_string(), json!(true));\n        }\n\n        publish::exec_from_entry(config.clone(), publish_entry, config_dir, clear_database, yes).await?;\n    }\n\n    println!(\"{}\", \"Published successfully!\".green().bold());\n    println!(\"{}\", \"---\".dimmed());\n\n    Ok(())\n}\n\nasync fn select_database(config: &Config, server: &str, token: &str) -> Result<String, anyhow::Error> {\n    let identity = crate::util::decode_identity(&token.to_string())?;\n\n    let spinner = ProgressBar::new_spinner();\n    spinner.set_style(\n        ProgressStyle::default_spinner()\n            .template(\"{spinner:.cyan} {msg}\")\n            .unwrap(),\n    );\n    spinner.set_message(\"Fetching database list...\");\n    spinner.enable_steady_tick(Duration::from_millis(100));\n\n    let client = reqwest::Client::new();\n    let res = client\n        .get(format!(\n            \"{}/v1/identity/{}/databases\",\n            config.get_host_url(Some(server))?,\n            identity\n        ))\n        .bearer_auth(token)\n        .send()\n        .await?;\n\n    let result: DatabasesResult = res\n        .json_or_error()\n        .await\n        .context(\"Unable to retrieve databases for identity\")?;\n\n    if result.identities.is_empty() {\n        spinner.finish_and_clear();\n        println!(\"{}\", \"No existing databases found.\".yellow());\n        Ok(generate_database_name())\n    } else {\n        let total = result.identities.len();\n        spinner.set_message(format!(\"Fetching names for {} databases...\", total));\n\n        // Fetch database names with HTTP queries to /database/{identity}/names\n        // It's parallelyzed in case a user has a lot of databases\n        // TODO: we should introduce an endpoint that returns user's databases with names\n        let databases: Vec<DatabaseRow> = stream::iter(result.identities.into_iter())\n            .map(|identity_str| {\n                let config = config.clone();\n                async move {\n                    let names_response = spacetime_reverse_dns(&config, &identity_str, Some(server)).await?;\n                    let name = if names_response.names.is_empty() {\n                        identity_str.clone()\n                    } else {\n                        names_response.names[0].as_ref().to_string()\n                    };\n                    Ok::<DatabaseRow, anyhow::Error>(DatabaseRow {\n                        identity: identity_str,\n                        name,\n                    })\n                }\n            })\n            .buffer_unordered(30)\n            .collect::<Vec<_>>()\n            .await\n            .into_iter()\n            .collect::<Result<Vec<_>, _>>()?;\n\n        spinner.finish_and_clear();\n\n        let display_limit = 10;\n        if databases.len() <= display_limit {\n            let mut table = Table::new(&databases);\n            table\n                .with(Style::psql())\n                .with(Modify::new(Columns::first()).with(Alignment::left()));\n            println!(\"\\nYour databases:\\n\");\n            println!(\"{table}\");\n            println!();\n        } else {\n            let display_databases: Vec<_> = databases.iter().take(display_limit).cloned().collect();\n            let mut table = Table::new(&display_databases);\n            table\n                .with(Style::psql())\n                .with(Modify::new(Columns::first()).with(Alignment::left()));\n            println!(\"\\nYour databases (showing {} of {}):\\n\", display_limit, databases.len());\n            println!(\"{table}\");\n            println!();\n        }\n\n        let items: Vec<String> = databases\n            .iter()\n            .map(|db| {\n                let truncated_identity = truncate_identity(&db.identity);\n                format!(\"{} ({})\", db.name, truncated_identity)\n            })\n            .collect();\n\n        let selection = FuzzySelect::with_theme(&ColorfulTheme::default())\n            .with_prompt(\"Select database (type to filter)\")\n            .items(&items)\n            .default(0)\n            .interact()?;\n\n        Ok(databases[selection].name.clone())\n    }\n}\n\nfn truncate_identity(identity: &str) -> String {\n    if identity.len() <= 16 {\n        identity.to_string()\n    } else {\n        format!(\"{}...{}\", &identity[..8], &identity[identity.len() - 8..])\n    }\n}\n\nasync fn start_log_stream(\n    mut config: Config,\n    database_identity: String,\n    server: Option<&str>,\n    prefix: Option<String>,\n) -> Result<JoinHandle<()>, anyhow::Error> {\n    let server = server.map(|s| s.to_string());\n    let host_url = config.get_host_url(server.as_deref())?;\n    let auth_header = get_auth_header(&mut config, false, server.as_deref(), false).await?;\n\n    let handle = tokio::spawn(async move {\n        loop {\n            if let Err(e) = stream_logs(&host_url, &database_identity, &auth_header, prefix.as_deref()).await {\n                eprintln!(\"\\n{} Log streaming error: {}\", \"Error:\".red().bold(), e);\n                eprintln!(\"{}\", \"Reconnecting in 10 seconds...\".yellow());\n                tokio::time::sleep(Duration::from_secs(10)).await;\n            }\n        }\n    });\n\n    Ok(handle)\n}\n\nasync fn stream_logs(\n    host_url: &str,\n    database_identity: &str,\n    auth_header: &crate::util::AuthHeader,\n    prefix: Option<&str>,\n) -> Result<(), anyhow::Error> {\n    let client = reqwest::Client::new();\n    let builder = client.get(format!(\"{host_url}/v1/database/{database_identity}/logs\"));\n    let builder = add_auth_header_opt(builder, auth_header);\n    let res = builder.query(&[(\"num_lines\", \"10\"), (\"follow\", \"true\")]).send().await?;\n\n    let status = res.status();\n    if status.is_client_error() || status.is_server_error() {\n        let mut err = res.text().await?;\n        // The server doesn't always send an error description in the response\n        // body (maybe it should), so default to status code + canonical reason\n        // phrase (e.g. \"502 Bad Gateway\").\n        if err.is_empty() {\n            err = format!(\"{status}\");\n        }\n        anyhow::bail!(err)\n    }\n\n    let term_color = if std::io::stdout().is_terminal() {\n        termcolor::ColorChoice::Auto\n    } else {\n        termcolor::ColorChoice::Never\n    };\n\n    let mut rdr = res.bytes_stream().map_err(std::io::Error::other).into_async_read();\n    let mut line = String::new();\n    while rdr.read_line(&mut line).await? != 0 {\n        let record = serde_json::from_str::<LogRecord<'_>>(&line)?;\n        let out = termcolor::StandardStream::stdout(term_color);\n        let mut out = out.lock();\n        format_log_record(&mut out, &record, prefix)?;\n        drop(out);\n        line.clear();\n    }\n\n    Ok(())\n}\n\nconst SENTINEL: &str = \"__spacetimedb__\";\n\n#[derive(serde::Deserialize)]\nenum LogLevel {\n    Error,\n    Warn,\n    Info,\n    Debug,\n    Trace,\n    Panic,\n}\n\n#[serde_with::serde_as]\n#[derive(serde::Deserialize)]\nstruct LogRecord<'a> {\n    #[serde_as(as = \"Option<serde_with::TimestampMicroSeconds>\")]\n    ts: Option<chrono::DateTime<chrono::Utc>>,\n    level: LogLevel,\n    #[serde(borrow)]\n    #[allow(unused)]\n    target: Option<Cow<'a, str>>,\n    #[serde(borrow)]\n    filename: Option<Cow<'a, str>>,\n    line_number: Option<u32>,\n    #[serde(borrow)]\n    function: Option<Cow<'a, str>>,\n    #[serde(borrow)]\n    message: Cow<'a, str>,\n}\n\nfn format_log_record<W: WriteColor>(\n    out: &mut W,\n    record: &LogRecord<'_>,\n    prefix: Option<&str>,\n) -> Result<(), std::io::Error> {\n    // Write prefix if provided\n    if let Some(prefix) = prefix {\n        out.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))?;\n        write!(out, \"[{}] \", prefix)?;\n        out.reset()?;\n    }\n\n    if let Some(ts) = record.ts {\n        out.set_color(ColorSpec::new().set_dimmed(true))?;\n        write!(out, \"{ts:?} \")?;\n    }\n    let mut color = ColorSpec::new();\n    let level = match record.level {\n        LogLevel::Error => {\n            color.set_fg(Some(Color::Red));\n            \"ERROR\"\n        }\n        LogLevel::Warn => {\n            color.set_fg(Some(Color::Yellow));\n            \"WARN\"\n        }\n        LogLevel::Info => {\n            color.set_fg(Some(Color::Blue));\n            \"INFO\"\n        }\n        LogLevel::Debug => {\n            color.set_dimmed(true).set_bold(true);\n            \"DEBUG\"\n        }\n        LogLevel::Trace => {\n            color.set_dimmed(true);\n            \"TRACE\"\n        }\n        LogLevel::Panic => {\n            color.set_fg(Some(Color::Red)).set_bold(true).set_intense(true);\n            \"PANIC\"\n        }\n    };\n    out.set_color(&color)?;\n    write!(out, \"{level:>5}: \")?;\n    out.reset()?;\n    let mut need_space_before_filename = false;\n    let mut need_colon_sep = false;\n    let dimmed = ColorSpec::new().set_dimmed(true).clone();\n    if let Some(function) = &record.function\n        && function.as_ref() != SENTINEL\n    {\n        out.set_color(&dimmed)?;\n        write!(out, \"{function}\")?;\n        out.reset()?;\n        need_space_before_filename = true;\n        need_colon_sep = true;\n    }\n    if let Some(filename) = &record.filename\n        && filename.as_ref() != SENTINEL\n    {\n        out.set_color(&dimmed)?;\n        if need_space_before_filename {\n            write!(out, \" \")?;\n        }\n        write!(out, \"{filename}\")?;\n        if let Some(line) = record.line_number {\n            write!(out, \":{line}\")?;\n        }\n        out.reset()?;\n        need_colon_sep = true;\n    }\n    if need_colon_sep {\n        write!(out, \": \")?;\n    }\n    writeln!(out, \"{}\", record.message)?;\n    Ok(())\n}\n\n/// Directory names that should always be ignored by the file watcher,\n/// regardless of `.gitignore` rules.\nconst ALWAYS_IGNORE_DIRS: &[&str] = &[\n    \".git\",\n    \".hg\",\n    \".svn\",   // VCS\n    \"target\", // Rust\n    \"build\",  // C++\n    \"bin\",\n    \"obj\", // .NET/C#\n    \"node_modules\",\n    \"dist\",\n    \".next\", // JS/TS\n    \".nuxt\",\n    \".output\", // Nuxt\n    \"__pycache__\",\n    \".venv\",\n    \"venv\", // Python\n    \".vs\",\n    \".idea\", // IDE\n];\n\n/// Returns `true` if the given path should always trigger a rebuild,\n/// even if it would otherwise be gitignored.\nfn is_always_watched(path: &Path) -> bool {\n    let Some(name) = path.file_name().and_then(|n| n.to_str()) else {\n        return false;\n    };\n    name == \".env.local\" || (name.starts_with(\"spacetime.\") && name.ends_with(\".local.json\"))\n}\n\n/// Build a gitignore matcher that loads rules from:\n/// - the global gitignore (via `gitconfig_excludes_path`)\n/// - `project_dir/.gitignore` (if different from `spacetimedb_dir`)\n/// - `spacetimedb_dir/.gitignore`\nfn build_gitignore_matcher(project_dir: &Path, spacetimedb_dir: &Path) -> Gitignore {\n    let mut builder = GitignoreBuilder::new(spacetimedb_dir);\n\n    // Global gitignore\n    if let Some(global) = ignore::gitignore::gitconfig_excludes_path() {\n        let _ = builder.add(global);\n    }\n\n    // Project-level .gitignore (if the project root differs from the module dir)\n    let project_gitignore = project_dir.join(\".gitignore\");\n    let spacetimedb_gitignore = spacetimedb_dir.join(\".gitignore\");\n    if project_dir != spacetimedb_dir && project_gitignore.exists() {\n        let _ = builder.add(&project_gitignore);\n    }\n\n    // Module-level .gitignore\n    if spacetimedb_gitignore.exists() {\n        let _ = builder.add(&spacetimedb_gitignore);\n    }\n\n    match builder.build() {\n        Ok(gi) => gi,\n        Err(e) => {\n            eprintln!(\n                \"{} Failed to parse .gitignore rules: {}. Falling back to no gitignore filtering.\",\n                \"Warning:\".yellow().bold(),\n                e\n            );\n            Gitignore::empty()\n        }\n    }\n}\n\n/// Determines whether a path should be ignored by the file watcher.\n///\n/// Layered filtering:\n/// 1. If any path component is in `ALWAYS_IGNORE_DIRS` → ignore\n/// 2. If the filename matches always-watch patterns → don't ignore\n/// 3. Otherwise, consult the gitignore matcher\nfn should_ignore_path(path: &Path, gitignore: &Gitignore) -> bool {\n    // Layer 1: always-ignore directories\n    for component in path.components() {\n        if let std::path::Component::Normal(c) = component\n            && let Some(s) = c.to_str()\n            && ALWAYS_IGNORE_DIRS.contains(&s)\n        {\n            return true;\n        }\n    }\n\n    // Layer 2 exception: always-watch files\n    if is_always_watched(path) {\n        return false;\n    }\n\n    // Layer 3: gitignore rules\n    gitignore.matched(path, path.is_dir()).is_ignore()\n}\n\nfn generate_database_name() -> String {\n    let mut generator = names::Generator::with_naming(names::Name::Numbered);\n    generator.next().unwrap()\n}\n\nfn resolve_database_sources(config: &SpacetimeConfig) -> HashMap<String, Option<String>> {\n    let mut sources = HashMap::new();\n    for target in config.collect_all_targets_with_inheritance() {\n        if let Some(database) = target.fields.get(\"database\").and_then(|v| v.as_str()) {\n            sources.insert(database.to_string(), target.source_config.clone());\n        }\n    }\n    sources\n}\n\n/// Extract unique watch directories from publish configs\nfn extract_watch_dirs(\n    publish_configs: &[CommandConfig<'_>],\n    default_spacetimedb_dir: &Path,\n    project_dir: &Path,\n) -> std::collections::HashSet<PathBuf> {\n    use std::collections::HashSet;\n    let mut watch_dirs = HashSet::new();\n\n    for config_entry in publish_configs {\n        let module_path = config_entry\n            .get_config_value(\"module_path\")\n            .and_then(|v| v.as_str())\n            .map(|s| {\n                let p = PathBuf::from(s);\n                if p.is_absolute() {\n                    p\n                } else {\n                    project_dir.join(p)\n                }\n            })\n            .unwrap_or_else(|| default_spacetimedb_dir.to_path_buf());\n\n        // Canonicalize to normalize the path\n        let canonical_path = module_path.canonicalize().unwrap_or(module_path);\n\n        watch_dirs.insert(canonical_path);\n    }\n\n    watch_dirs\n}\n\n/// Detect client command and save to config (updating existing config if present)\nfn detect_and_save_client_command(project_dir: &Path, existing_config: Option<SpacetimeConfig>) -> Option<String> {\n    if let Some((detected_cmd, _detected_pm)) = detect_client_command(project_dir) {\n        // Update provided config, config on disk, or create new one.\n        let config_to_save = if let Some(mut config) = existing_config {\n            config.dev = Some(crate::spacetime_config::DevConfig {\n                run: Some(detected_cmd.clone()),\n            });\n            config\n        } else if project_dir.join(CONFIG_FILENAME).exists() {\n            match SpacetimeConfig::load(&project_dir.join(CONFIG_FILENAME)) {\n                Ok(mut config) => {\n                    config.dev = Some(crate::spacetime_config::DevConfig {\n                        run: Some(detected_cmd.clone()),\n                    });\n                    config\n                }\n                Err(_) => SpacetimeConfig::with_run_command(&detected_cmd),\n            }\n        } else {\n            SpacetimeConfig::with_run_command(&detected_cmd)\n        };\n\n        if let Ok(path) = config_to_save.save_to_dir(project_dir) {\n            println!(\n                \"{} Detected client command and saved to {}\",\n                \"✓\".green(),\n                strip_verbatim_prefix(&path).display()\n            );\n        }\n        Some(detected_cmd)\n    } else {\n        None\n    }\n}\n\nfn create_default_spacetime_config_if_missing(project_dir: &Path) -> anyhow::Result<Option<PathBuf>> {\n    let config_path = project_dir.join(CONFIG_FILENAME);\n    if config_path.exists() {\n        return Ok(None);\n    }\n\n    let mut config = SpacetimeConfig::default();\n    config\n        .additional_fields\n        .insert(\"server\".to_string(), json!(\"maincloud\"));\n\n    if project_dir.join(\"spacetimedb\").is_dir() {\n        config\n            .additional_fields\n            .insert(\"module-path\".to_string(), json!(\"./spacetimedb\"));\n    }\n\n    Ok(Some(config.save_to_dir(project_dir)?))\n}\n\nfn create_local_spacetime_config_if_missing(\n    project_dir: &Path,\n    database_name: &str,\n) -> anyhow::Result<Option<PathBuf>> {\n    let main_config_path = project_dir.join(CONFIG_FILENAME);\n    if !main_config_path.exists() {\n        return Ok(None);\n    }\n\n    let local_config_path = project_dir.join(\"spacetime.local.json\");\n    if local_config_path.exists() {\n        let mut local_config = SpacetimeConfig::load(&local_config_path)\n            .with_context(|| format!(\"Failed to load {}\", local_config_path.display()))?;\n        if local_config.additional_fields.contains_key(\"database\") {\n            return Ok(None);\n        }\n        local_config\n            .additional_fields\n            .insert(\"database\".to_string(), json!(database_name));\n        local_config.save(&local_config_path)?;\n        return Ok(Some(local_config_path));\n    }\n\n    let mut local_config = SpacetimeConfig::default();\n    local_config\n        .additional_fields\n        .insert(\"database\".to_string(), json!(database_name));\n    local_config.save(&local_config_path)?;\n\n    Ok(Some(local_config_path))\n}\n\n// Persist the root module-path so subsequent layered loads resolve module location\n// without interactive prompts.\nfn save_root_module_path_to_spacetime_json(config_dir: &Path, module_path: &str) -> anyhow::Result<PathBuf> {\n    let config_path = config_dir.join(CONFIG_FILENAME);\n    let mut config = SpacetimeConfig::load(&config_path).with_context(|| {\n        format!(\n            \"Failed to load root config for writing module-path: {}\",\n            config_path.display()\n        )\n    })?;\n    config\n        .additional_fields\n        .insert(\"module-path\".to_string(), json!(module_path));\n    config.save(&config_path)?;\n    Ok(config_path)\n}\n\n/// Start the client development server as a child process.\n/// The process inherits stdout/stderr so the user can see the output.\n/// Sets SPACETIMEDB_DB_NAME and SPACETIMEDB_HOST environment variables for the client.\nfn start_client_process(\n    command: &str,\n    working_dir: &Path,\n    database_name: &str,\n    host_url: &str,\n) -> Result<Child, anyhow::Error> {\n    println!(\"{} {}\", \"Starting client:\".cyan(), command.dimmed());\n\n    if command.trim().is_empty() {\n        anyhow::bail!(\"Empty client command\");\n    }\n\n    // Use shell to handle PATH resolution and .cmd/.bat scripts on Windows\n    #[cfg(windows)]\n    let child = TokioCommand::new(\"cmd\")\n        .args([\"/C\", command])\n        .current_dir(working_dir)\n        .env(\"SPACETIMEDB_DB_NAME\", database_name)\n        .env(\"SPACETIMEDB_HOST\", host_url)\n        .stdout(std::process::Stdio::inherit())\n        .stderr(std::process::Stdio::inherit())\n        .stdin(std::process::Stdio::inherit())\n        .kill_on_drop(true)\n        .spawn()\n        .with_context(|| format!(\"Failed to start client command: {}\", command))?;\n\n    #[cfg(not(windows))]\n    let child = TokioCommand::new(\"sh\")\n        .args([\"-c\", command])\n        .current_dir(working_dir)\n        .env(\"SPACETIMEDB_DB_NAME\", database_name)\n        .env(\"SPACETIMEDB_HOST\", host_url)\n        .stdout(std::process::Stdio::inherit())\n        .stderr(std::process::Stdio::inherit())\n        .stdin(std::process::Stdio::inherit())\n        .kill_on_drop(true)\n        .spawn()\n        .with_context(|| format!(\"Failed to start client command: {}\", command))?;\n\n    Ok(child)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::fs;\n    use tempfile::TempDir;\n\n    #[test]\n    fn test_detect_and_save_preserves_existing_config() {\n        let temp = TempDir::new().unwrap();\n\n        // Create a database-centric config with generate but no dev-run\n        let initial_config = r#\"{\n            \"database\": \"test-db\",\n            \"server\": \"maincloud\",\n            \"generate\": [\n                { \"out-dir\": \"./foo-client/src/module_bindings\", \"module-path\": \"foo\", \"language\": \"rust\" }\n            ]\n        }\"#;\n\n        let config_path = temp.path().join(\"spacetime.json\");\n        fs::write(&config_path, initial_config).unwrap();\n\n        // Create a package.json to enable detection\n        let package_json = r#\"{\n            \"name\": \"test\",\n            \"scripts\": {\n                \"dev\": \"vite\"\n            }\n        }\"#;\n        fs::write(temp.path().join(\"package.json\"), package_json).unwrap();\n\n        // Load the config\n        let loaded_config = SpacetimeConfig::load(&config_path).unwrap();\n        assert!(loaded_config.dev.is_none());\n        assert!(loaded_config.generate.is_some());\n        assert_eq!(\n            loaded_config.additional_fields.get(\"database\").and_then(|v| v.as_str()),\n            Some(\"test-db\")\n        );\n\n        // Call detect_and_save_client_command which should detect \"npm run dev\"\n        let detected = detect_and_save_client_command(temp.path(), Some(loaded_config));\n        assert!(detected.is_some(), \"Should detect client command from package.json\");\n\n        // Load again and verify all fields are preserved\n        let reloaded_config = SpacetimeConfig::load(&config_path).unwrap();\n        assert!(\n            reloaded_config.dev.as_ref().and_then(|d| d.run.as_ref()).is_some(),\n            \"dev.run should be set\"\n        );\n        assert!(reloaded_config.generate.is_some(), \"generate field should be preserved\");\n        assert_eq!(\n            reloaded_config\n                .additional_fields\n                .get(\"database\")\n                .and_then(|v| v.as_str()),\n            Some(\"test-db\"),\n            \"database field should be preserved\"\n        );\n        assert_eq!(\n            reloaded_config.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"maincloud\"),\n            \"server field should be preserved\"\n        );\n\n        // Verify the generate array has the expected content\n        let generate = reloaded_config.generate.unwrap();\n        assert_eq!(generate.len(), 1);\n        assert_eq!(\n            generate[0].get(\"out-dir\").unwrap().as_str().unwrap(),\n            \"./foo-client/src/module_bindings\"\n        );\n    }\n\n    #[test]\n    fn test_determine_publish_configs_no_database_no_config() {\n        // When there's no config and no CLI database name, returns empty vec\n        // (dev will later prompt the user)\n        let publish_cmd = publish::cli();\n        let publish_schema = publish::build_publish_schema(&publish_cmd).unwrap();\n        let publish_args = publish_cmd.clone().get_matches_from(vec![\"publish\"]);\n\n        let result = determine_publish_configs(\n            None,\n            None,\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            \"local\",\n            Path::new(\"spacetimedb\"),\n        )\n        .unwrap();\n\n        assert!(result.is_empty());\n    }\n\n    #[test]\n    fn test_determine_publish_configs_cli_database_no_config() {\n        // When CLI provides a database name but no config, creates a single publish config\n        let publish_cmd = publish::cli();\n        let publish_schema = publish::build_publish_schema(&publish_cmd).unwrap();\n        let publish_args = publish_cmd.clone().get_matches_from(vec![\"publish\", \"my-custom-db\"]);\n\n        let result = determine_publish_configs(\n            Some(\"my-custom-db\".to_string()),\n            None,\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            \"local\",\n            Path::new(\"spacetimedb\"),\n        )\n        .unwrap();\n\n        assert_eq!(result.len(), 1);\n        assert_eq!(\n            result[0].get_config_value(\"database\").and_then(|v| v.as_str()),\n            Some(\"my-custom-db\")\n        );\n        assert_eq!(\n            result[0].get_config_value(\"server\").and_then(|v| v.as_str()),\n            Some(\"local\")\n        );\n    }\n\n    #[test]\n    fn test_determine_publish_configs_with_config_targets() {\n        // When config has database targets, returns those targets\n        let publish_cmd = publish::cli();\n        let publish_schema = publish::build_publish_schema(&publish_cmd).unwrap();\n        let publish_args = publish_cmd.clone().get_matches_from(vec![\"publish\"]);\n\n        let config: SpacetimeConfig = serde_json::from_value(serde_json::json!({\n            \"database\": \"config-db\",\n            \"server\": \"maincloud\",\n            \"module-path\": \"./server\"\n        }))\n        .unwrap();\n\n        let result = determine_publish_configs(\n            None,\n            Some(&config),\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            \"local\",\n            Path::new(\"spacetimedb\"),\n        )\n        .unwrap();\n\n        assert_eq!(result.len(), 1);\n        assert_eq!(\n            result[0].get_one::<String>(\"database\").unwrap(),\n            Some(\"config-db\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_determine_publish_configs_config_no_database_falls_through() {\n        // Config exists but has no database field or children → falls through to CLI database\n        let publish_cmd = publish::cli();\n        let publish_schema = publish::build_publish_schema(&publish_cmd).unwrap();\n        let publish_args = publish_cmd.clone().get_matches_from(vec![\"publish\", \"cli-db\"]);\n\n        // Config with only dev and generate, no database\n        let config: SpacetimeConfig = serde_json::from_value(serde_json::json!({\n            \"dev\": { \"run\": \"npm run dev\" },\n            \"generate\": [{ \"language\": \"typescript\", \"out-dir\": \"./bindings\" }]\n        }))\n        .unwrap();\n\n        let result = determine_publish_configs(\n            Some(\"cli-db\".to_string()),\n            Some(&config),\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            \"local\",\n            Path::new(\"spacetimedb\"),\n        )\n        .unwrap();\n\n        // Should fall through to CLI database since config has no publish targets\n        assert_eq!(result.len(), 1);\n        assert_eq!(\n            result[0].get_config_value(\"database\").and_then(|v| v.as_str()),\n            Some(\"cli-db\")\n        );\n    }\n\n    #[test]\n    fn test_determine_publish_configs_fallback_uses_provided_module_path() {\n        // When falling through to CLI database, the fallback should use the provided\n        // default_module_path instead of hardcoding \"spacetimedb\"\n        let publish_cmd = publish::cli();\n        let publish_schema = publish::build_publish_schema(&publish_cmd).unwrap();\n        let publish_args = publish_cmd.clone().get_matches_from(vec![\"publish\", \"my-db\"]);\n\n        let custom_path = Path::new(\"/custom/module/path\");\n        let result = determine_publish_configs(\n            Some(\"my-db\".to_string()),\n            None,\n            &publish_cmd,\n            &publish_schema,\n            &publish_args,\n            \"local\",\n            custom_path,\n        )\n        .unwrap();\n\n        assert_eq!(result.len(), 1);\n        assert_eq!(\n            result[0].get_config_value(\"module_path\").and_then(|v| v.as_str()),\n            Some(\"/custom/module/path\")\n        );\n    }\n\n    #[test]\n    fn test_cli_env_flag_defaults_to_dev() {\n        // Verify that the dev CLI defaults --env to \"dev\"\n        let cmd = cli();\n        let matches = cmd.clone().get_matches_from(vec![\"dev\"]);\n\n        // --env is not set, so it should return None from clap\n        let env_from_cli = matches.get_one::<String>(\"env\");\n        assert!(env_from_cli.is_none(), \"env should not be set by default in clap\");\n\n        // But in exec(), we default to \"dev\":\n        let env = env_from_cli.map(|s| s.as_str()).unwrap_or(\"dev\");\n        assert_eq!(env, \"dev\");\n    }\n\n    #[test]\n    fn test_cli_skip_flags_exist() {\n        // Verify that --skip-publish and --skip-generate flags are registered\n        let cmd = cli();\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"dev\", \"--skip-publish\", \"--skip-generate\"]);\n\n        assert!(matches.get_flag(\"skip_publish\"));\n        assert!(matches.get_flag(\"skip_generate\"));\n    }\n\n    #[test]\n    fn test_cli_env_flag_accepts_value() {\n        let cmd = cli();\n        let matches = cmd.clone().get_matches_from(vec![\"dev\", \"--env\", \"staging\"]);\n\n        assert_eq!(matches.get_one::<String>(\"env\").map(|s| s.as_str()), Some(\"staging\"));\n    }\n\n    #[test]\n    fn test_create_default_spacetime_config_if_missing_creates_expected_config() {\n        let temp = TempDir::new().unwrap();\n        let project_path = temp.path();\n        std::fs::create_dir_all(project_path.join(\"spacetimedb\")).unwrap();\n\n        let created = create_default_spacetime_config_if_missing(project_path)\n            .unwrap()\n            .expect(\"expected config to be created\");\n        assert_eq!(created, project_path.join(\"spacetime.json\"));\n\n        let content = std::fs::read_to_string(&created).unwrap();\n        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();\n        assert!(parsed.get(\"database\").is_none());\n        assert_eq!(parsed.get(\"server\").and_then(|v| v.as_str()), Some(\"maincloud\"));\n        assert_eq!(\n            parsed.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./spacetimedb\")\n        );\n    }\n\n    #[test]\n    fn test_create_local_spacetime_config_if_missing_creates_database_override() {\n        let temp = TempDir::new().unwrap();\n        let project_path = temp.path();\n\n        std::fs::write(project_path.join(\"spacetime.json\"), \"{}\").unwrap();\n\n        let created = create_local_spacetime_config_if_missing(project_path, \"my-app-123456\")\n            .unwrap()\n            .expect(\"expected local config to be created\");\n        assert_eq!(created, project_path.join(\"spacetime.local.json\"));\n\n        let content = std::fs::read_to_string(&created).unwrap();\n        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();\n        let db = parsed\n            .get(\"database\")\n            .and_then(|v| v.as_str())\n            .expect(\"database should be present\");\n\n        assert_eq!(db, \"my-app-123456\");\n\n        let obj = parsed.as_object().expect(\"local config should be a JSON object\");\n        assert_eq!(obj.len(), 1, \"local config should only contain database\");\n    }\n\n    #[test]\n    fn test_create_local_spacetime_config_if_missing_upserts_missing_database() {\n        let temp = TempDir::new().unwrap();\n        let project_path = temp.path();\n\n        std::fs::write(project_path.join(\"spacetime.json\"), \"{}\").unwrap();\n        std::fs::write(project_path.join(\"spacetime.local.json\"), r#\"{ \"server\": \"local\" }\"#).unwrap();\n\n        let updated = create_local_spacetime_config_if_missing(project_path, \"my-cli-db\")\n            .unwrap()\n            .expect(\"expected local config to be updated\");\n        assert_eq!(updated, project_path.join(\"spacetime.local.json\"));\n\n        let content = std::fs::read_to_string(project_path.join(\"spacetime.local.json\")).unwrap();\n        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();\n        assert_eq!(parsed.get(\"server\").and_then(|v| v.as_str()), Some(\"local\"));\n        assert_eq!(parsed.get(\"database\").and_then(|v| v.as_str()), Some(\"my-cli-db\"));\n    }\n\n    #[test]\n    fn test_detect_and_save_merges_into_existing_file_when_no_existing_config_passed() {\n        let temp = TempDir::new().unwrap();\n        let config_path = temp.path().join(\"spacetime.json\");\n        fs::write(\n            &config_path,\n            r#\"{\n                \"server\": \"maincloud\",\n                \"module-path\": \"./spacetimedb\"\n            }\"#,\n        )\n        .unwrap();\n        fs::write(\n            temp.path().join(\"package.json\"),\n            r#\"{\n                \"name\": \"test\",\n                \"scripts\": {\n                    \"dev\": \"vite\"\n                }\n            }\"#,\n        )\n        .unwrap();\n\n        let detected = detect_and_save_client_command(temp.path(), None);\n        assert!(detected.is_some());\n\n        let reloaded = SpacetimeConfig::load(&config_path).unwrap();\n        assert_eq!(\n            reloaded.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"maincloud\")\n        );\n        assert_eq!(\n            reloaded.additional_fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./spacetimedb\")\n        );\n        assert_eq!(\n            reloaded.dev.as_ref().and_then(|d| d.run.as_deref()),\n            Some(\"npm run dev\")\n        );\n    }\n\n    #[test]\n    fn test_save_root_module_path_to_spacetime_json_updates_root_config() {\n        let temp = TempDir::new().unwrap();\n        let config_path = temp.path().join(\"spacetime.json\");\n        fs::write(\n            &config_path,\n            r#\"{\n                \"server\": \"maincloud\",\n                \"children\": [{ \"database\": \"child-db\" }]\n            }\"#,\n        )\n        .unwrap();\n\n        let saved = save_root_module_path_to_spacetime_json(temp.path(), \"./custom-module\").unwrap();\n        assert_eq!(saved, config_path);\n\n        let reloaded = SpacetimeConfig::load(&config_path).unwrap();\n        assert_eq!(\n            reloaded.additional_fields.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./custom-module\")\n        );\n        assert_eq!(\n            reloaded.additional_fields.get(\"server\").and_then(|v| v.as_str()),\n            Some(\"maincloud\")\n        );\n        assert!(reloaded.children.is_some());\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/dns.rs",
    "content": "use crate::common_args;\nuse crate::config::Config;\nuse crate::util::{add_auth_header_opt, get_auth_header, ResponseExt};\nuse clap::ArgMatches;\nuse clap::{Arg, Command};\n\nuse spacetimedb_client_api_messages::name::{DomainName, SetDomainsResult};\n\npub fn cli() -> Command {\n    Command::new(\"rename\")\n        .about(\"Rename a database\")\n        .arg(\n            Arg::new(\"new-name\")\n                .long(\"to\")\n                .required(true)\n                .help(\"The new name you would like to assign\"),\n        )\n        .arg(\n            Arg::new(\"database-identity\")\n                .required(true)\n                .help(\"The database identity to rename\"),\n        )\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server on which to set the name\"))\n        .arg(common_args::yes())\n        .after_help(\"Run `spacetime rename --help` for more detailed information.\\n\")\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let domain = args.get_one::<String>(\"new-name\").unwrap();\n    let database_identity = args.get_one::<String>(\"database-identity\").unwrap();\n    let server = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let force = args.get_flag(\"force\");\n    let auth_header = get_auth_header(&mut config, false, server, !force).await?;\n\n    let domain: DomainName = domain.parse()?;\n\n    let builder = reqwest::Client::new()\n        .put(format!(\n            \"{}/v1/database/{database_identity}/names\",\n            config.get_host_url(server)?\n        ))\n        .header(reqwest::header::CONTENT_TYPE, \"application/json\")\n        .body(serde_json::to_string(&[&domain])?);\n    let builder = add_auth_header_opt(builder, &auth_header);\n\n    let response = builder.send().await?;\n    let status = &response.status();\n    let result: SetDomainsResult = response.json_or_error().await?;\n\n    if !status.is_success() {\n        anyhow::bail!(match result {\n            SetDomainsResult::Success => \"\".to_string(),\n            SetDomainsResult::PermissionDenied { domain } => format!(\"Permission denied for domain: {domain}\"),\n            SetDomainsResult::PermissionDeniedOnAny { domains } =>\n                format!(\"Permission denied for domains: {domains:?}\"),\n            SetDomainsResult::DatabaseNotFound => format!(\"Database {database_identity} not found\"),\n            SetDomainsResult::NotYourDatabase { .. } =>\n                format!(\"You cannot rename {database_identity} because it is owned by another identity.\"),\n            SetDomainsResult::OtherError(err) => err,\n        });\n    }\n\n    println!(\"Name set to {domain} for identity {database_identity}.\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/generate.rs",
    "content": "#![warn(clippy::uninlined_format_args)]\n\nuse anyhow::Context;\nuse clap::parser::ValueSource;\nuse clap::Arg;\nuse clap::ArgAction::{Set, SetTrue};\nuse fs_err as fs;\nuse spacetimedb_codegen::{\n    generate, private_table_names, CodegenOptions, CodegenVisibility, Csharp, Lang, OutputFile, Rust, TypeScript,\n    UnrealCpp, AUTO_GENERATED_PREFIX,\n};\nuse spacetimedb_lib::de::serde::DeserializeWrapper;\nuse spacetimedb_lib::{sats, RawModuleDef};\nuse spacetimedb_schema;\nuse spacetimedb_schema::def::ModuleDef;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Stdio};\n\nuse crate::spacetime_config::{\n    find_and_load_with_env, CommandConfig, CommandSchema, CommandSchemaBuilder, Key, LoadedConfig, SpacetimeConfig,\n};\nuse crate::tasks::csharp::dotnet_format;\nuse crate::tasks::rust::rustfmt;\nuse crate::util::{resolve_sibling_binary, y_or_n};\nuse crate::Config;\nuse crate::{build, common_args};\nuse clap::builder::PossibleValue;\n\nuse std::collections::{BTreeSet, HashMap};\nuse std::io::Read;\n\n/// Build the CommandSchema for generate command configuration.\n///\n/// This schema is used to validate and merge values from both the config file\n/// and CLI arguments, with CLI arguments taking precedence over config values.\nfn build_generate_config_schema(command: &clap::Command) -> Result<CommandSchema, anyhow::Error> {\n    CommandSchemaBuilder::new()\n        .key(\n            Key::new(\"language\")\n                .from_clap(\"lang\")\n                .required()\n                .generate_entry_specific(),\n        )\n        .key(Key::new(\"out_dir\").generate_entry_specific())\n        .key(Key::new(\"uproject_dir\").generate_entry_specific())\n        .key(Key::new(\"module_path\").module_specific())\n        .key(Key::new(\"wasm_file\").module_specific())\n        .key(Key::new(\"js_file\").module_specific())\n        .key(Key::new(\"namespace\").generate_entry_specific())\n        .key(Key::new(\"unreal_module_name\").generate_entry_specific())\n        .key(Key::new(\"module_prefix\").generate_entry_specific())\n        .key(Key::new(\"build_options\").module_specific())\n        .key(Key::new(\"include_private\"))\n        .exclude(\"json_module\")\n        .exclude(\"force\")\n        .exclude(\"no_config\")\n        .exclude(\"env\")\n        .exclude(\"database\")\n        .build(command)\n        .map_err(Into::into)\n}\n\n/// Get filtered generate configs based on CLI arguments.\n///\n/// Uses the database-centric model: collects all targets with inheritance,\n/// filters by database name (glob), then collects generate entries from matched targets.\n/// Deduplicates by (canonical_module_path, serialized_generate_entry).\nfn get_filtered_generate_configs<'a>(\n    spacetime_config: &SpacetimeConfig,\n    command: &clap::Command,\n    schema: &'a CommandSchema,\n    args: &'a clap::ArgMatches,\n) -> Result<Vec<CommandConfig<'a>>, anyhow::Error> {\n    // Get all database targets from config with parent→child inheritance\n    let all_targets = spacetime_config.collect_all_targets_with_inheritance();\n\n    if all_targets.is_empty() {\n        return Ok(vec![]);\n    }\n\n    // Filter by database name pattern (glob) if provided via CLI\n    let filtered_targets = if let Some(cli_database) = args.get_one::<String>(\"database\") {\n        let pattern =\n            glob::Pattern::new(cli_database).with_context(|| format!(\"Invalid glob pattern: {cli_database}\"))?;\n\n        let matched: Vec<_> = all_targets\n            .into_iter()\n            .filter(|target| {\n                target\n                    .fields\n                    .get(\"database\")\n                    .and_then(|v| v.as_str())\n                    .is_some_and(|db| pattern.matches(db))\n            })\n            .collect();\n\n        if matched.is_empty() {\n            anyhow::bail!(\n                \"No database target matches '{}'. Available databases: {}\",\n                cli_database,\n                spacetime_config\n                    .collect_all_targets_with_inheritance()\n                    .iter()\n                    .filter_map(|t| t.fields.get(\"database\").and_then(|v| v.as_str()))\n                    .collect::<Vec<_>>()\n                    .join(\", \")\n            );\n        }\n\n        matched\n    } else {\n        all_targets\n    };\n\n    // Collect generate entries from matched targets, inheriting entity fields\n    // Deduplicate by (module_path, serialized_generate_entry)\n    let mut seen = std::collections::HashSet::new();\n    let mut generate_configs = Vec::new();\n\n    for target in &filtered_targets {\n        let generate_entries = match &target.generate {\n            Some(entries) if !entries.is_empty() => entries,\n            _ => continue,\n        };\n\n        // Get module_path from the target's entity fields for dedup\n        let module_path = target.fields.get(\"module-path\").and_then(|v| v.as_str()).unwrap_or(\"\");\n\n        for entry in generate_entries {\n            // Deduplicate: same module path + same generate entry config = generate once\n            let dedup_key = format!(\"{}:{}\", module_path, serde_json::to_string(entry).unwrap_or_default());\n            if !seen.insert(dedup_key) {\n                continue;\n            }\n\n            // Merge entity-level fields (module-path, etc.) with the generate entry\n            let mut merged = entry.clone();\n            // Inherit module-path from the target entity if not set in the generate entry\n            if let Some(mp) = target.fields.get(\"module-path\") {\n                merged.entry(\"module-path\".to_string()).or_insert_with(|| mp.clone());\n            }\n\n            let command_config = CommandConfig::new(schema, merged, args)?;\n            command_config.validate()?;\n            generate_configs.push(command_config);\n        }\n    }\n\n    if generate_configs.is_empty() {\n        return Ok(vec![]);\n    }\n\n    // Validate generate-entry-specific flags when multiple entries\n    schema.validate_no_generate_entry_specific_cli_args(command, args, generate_configs.len())?;\n\n    // Also validate module-specific flags\n    schema.validate_no_module_specific_cli_args_for_multiple_targets(\n        command,\n        args,\n        generate_configs.len(),\n        \"generating for multiple targets\",\n        \"Please specify a database name to select a single target, or remove these arguments.\",\n    )?;\n\n    Ok(generate_configs)\n}\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"generate\")\n        .about(\"Generate client files for a spacetime module.\")\n        .override_usage(\"generate [DATABASE] --lang <LANG> --out-dir <DIR> [--module-path <DIR> | --bin-path <PATH> | --unreal-module-name <MODULE_NAME> | --uproject-dir <DIR> | --include-private]\")\n        .arg(\n            Arg::new(\"database\")\n                .help(\"Database name or glob pattern to filter which databases to generate for\"),\n        )\n        .arg(\n            Arg::new(\"wasm_file\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"bin-path\")\n                .short('b')\n                .group(\"source\")\n                .conflicts_with(\"module_path\")\n                .conflicts_with(\"build_options\")\n                .help(\"The system path (absolute or relative) to the compiled wasm binary we should inspect\"),\n        )\n        .arg(\n            Arg::new(\"js_file\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"js-path\")\n                .short('j')\n                .group(\"source\")\n                .conflicts_with(\"module_path\")\n                .conflicts_with(\"build_options\")\n                .help(\"The system path (absolute or relative) to the bundled javascript file we should inspect\"),\n        )\n        .arg(\n            Arg::new(\"module_path\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"module-path\")\n                .short('p')\n                .group(\"source\")\n                .help(\"The system path (absolute or relative) to the module project. Defaults to spacetimedb/ subdirectory, then current directory.\"),\n        )\n        .arg(\n            Arg::new(\"json_module\")\n                .hide(true)\n                .num_args(0..=1)\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"module-def\")\n                .group(\"source\")\n                .help(\"Generate from a ModuleDef encoded as json\"),\n        )\n        .arg(\n            Arg::new(\"out_dir\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"out-dir\")\n                .short('o')\n                .help(\"The system path (absolute or relative) to the generate output directory\"),\n        )\n        .arg(\n            Arg::new(\"uproject_dir\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"uproject-dir\")\n                .help(\"Path to the Unreal project directory, replaces --out-dir for Unreal generation (only used with --lang unrealcpp)\")\n        )\n        .arg(\n            Arg::new(\"namespace\")\n                .default_value(\"SpacetimeDB.Types\")\n                .long(\"namespace\")\n                .help(\"The namespace that should be used\"),\n        )\n        .arg(\n            Arg::new(\"unreal_module_name\")\n                .long(\"unreal-module-name\")\n                .alias(\"module-name\")\n                .help(\"The module name that should be used for DLL export macros (required for lang unrealcpp)\")\n        )\n        .arg(\n            Arg::new(\"module_prefix\")\n                .long(\"module-prefix\")\n                .help(\"The module prefix to use for generated types (only used with --lang unrealcpp)\")\n        )\n        .arg(\n            Arg::new(\"lang\")\n                .long(\"lang\")\n                .short('l')\n                .value_parser(clap::value_parser!(Language))\n                .help(\"The language to generate\"),\n        )\n        .arg(\n            Arg::new(\"build_options\")\n                .long(\"build-options\")\n                .alias(\"build-opts\")\n                .action(Set)\n                .default_value(\"\")\n                .help(\"Options to pass to the build command, for example --build-options='--lint-dir='\"),\n        )\n        .arg(\n            Arg::new(\"include_private\")\n                .long(\"include-private\")\n                .action(SetTrue)\n                .default_value(\"false\")\n                .help(\"Include private tables and functions in generated code (types are always included).\"),\n        )\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(SetTrue)\n                .help(\"Ignore spacetime.json configuration\")\n        )\n        .arg(\n            Arg::new(\"env\")\n                .long(\"env\")\n                .value_name(\"ENV\")\n                .action(Set)\n                .help(\"Environment name for config file layering (e.g., dev, staging)\")\n        )\n        .after_help(\"Run `spacetime help generate` for more detailed information.\")\n}\n\npub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()> {\n    exec_ex(config, args, extract_descriptions, false, None).await\n}\n\n#[derive(Debug, Clone)]\npub struct GenerateRunConfig {\n    pub project_path: PathBuf,\n    pub wasm_file: Option<PathBuf>,\n    pub js_file: Option<PathBuf>,\n    pub lang: Language,\n    pub namespace: String,\n    pub module_name: Option<String>,\n    pub module_prefix: Option<String>,\n    pub build_options: String,\n    pub out_dir: PathBuf,\n    pub include_private: bool,\n}\n\nfn prepare_generate_run_configs<'a>(\n    generate_configs: Vec<CommandConfig<'a>>,\n    _using_config: bool,\n    config_dir: Option<&Path>,\n) -> anyhow::Result<Vec<GenerateRunConfig>> {\n    let mut runs = Vec::with_capacity(generate_configs.len());\n\n    for command_config in generate_configs {\n        let project_path = command_config\n            .get_resolved_path(\"module_path\", config_dir)?\n            .unwrap_or_else(|| {\n                config_dir\n                    .map(|d| d.join(\"spacetimedb\"))\n                    .unwrap_or_else(|| PathBuf::from(\"spacetimedb\"))\n            });\n\n        let wasm_file = command_config.get_one::<PathBuf>(\"wasm_file\")?;\n        let js_file = command_config.get_one::<PathBuf>(\"js_file\")?;\n        let requested_lang = command_config.get_one::<Language>(\"language\")?;\n        let namespace = command_config\n            .get_one::<String>(\"namespace\")?\n            .unwrap_or_else(|| \"SpacetimeDB.Types\".to_string());\n        let module_name = command_config.get_one::<String>(\"unreal_module_name\")?;\n        let module_prefix = command_config.get_one::<String>(\"module_prefix\")?;\n        let build_options = command_config\n            .get_one::<String>(\"build_options\")?\n            .unwrap_or_else(String::new);\n\n        // Validate Unreal-specific args first to preserve focused errors for this mode.\n        if requested_lang == Some(Language::UnrealCpp) {\n            if command_config.get_one::<PathBuf>(\"uproject_dir\")?.is_none() {\n                return Err(anyhow::anyhow!(\"--uproject-dir is required for --lang unrealcpp\"));\n            }\n            if module_name.is_none() {\n                return Err(anyhow::anyhow!(\"--unreal-module-name is required for --lang unrealcpp\"));\n            }\n        }\n\n        // Validate module source path for source-based generation.\n        if wasm_file.is_none() && js_file.is_none() && !project_path.is_dir() {\n            anyhow::bail!(\n                \"Could not find module source at '{}'. \\\n                 If this is not correct, pass --module-path or add module-path in spacetime.json. \\\n                 You can also pass --bin-path/--js-path to generate without building from source.\",\n                project_path.display()\n            );\n        }\n\n        let lang = match requested_lang {\n            Some(lang) => lang,\n            None => {\n                let client_project_dir = config_dir.unwrap_or_else(|| Path::new(\".\"));\n                let detected = detect_default_language(client_project_dir)?;\n                println!(\n                    \"Detected client language '{}' from '{}'. If this is not correct, pass --lang or add a generate target in spacetime.json.\",\n                    language_cli_name(detected),\n                    client_project_dir.display()\n                );\n                detected\n            }\n        };\n\n        let out_dir = command_config\n            .get_resolved_path(\"out_dir\", config_dir)?\n            .or_else(|| {\n                command_config\n                    .get_resolved_path(\"uproject_dir\", config_dir)\n                    .ok()\n                    .flatten()\n            })\n            .or_else(|| default_out_dir_for_language(lang).map(|p| config_dir.map(|d| d.join(&p)).unwrap_or(p)))\n            .ok_or_else(|| anyhow::anyhow!(\"Either --out-dir or --uproject-dir is required\"))?;\n\n        let include_private = command_config.get_one::<bool>(\"include_private\")?.unwrap_or(false);\n\n        runs.push(GenerateRunConfig {\n            project_path,\n            wasm_file,\n            js_file,\n            lang,\n            namespace,\n            module_name,\n            module_prefix,\n            build_options,\n            out_dir,\n            include_private,\n        });\n    }\n\n    Ok(runs)\n}\n\nfn detect_default_language(client_project_dir: &Path) -> anyhow::Result<Language> {\n    if client_project_dir.join(\"package.json\").exists() {\n        return Ok(Language::TypeScript);\n    }\n    if client_project_dir.join(\"Cargo.toml\").exists() {\n        return Ok(Language::Rust);\n    }\n    if let Ok(entries) = fs::read_dir(client_project_dir)\n        && entries\n            .flatten()\n            .any(|entry| entry.path().extension().is_some_and(|e| e == \"csproj\"))\n    {\n        return Ok(Language::Csharp);\n    }\n\n    anyhow::bail!(\n        \"Could not auto-detect client language from '{}'. \\\n         If this is not correct, pass --lang or add a generate target in spacetime.json.\",\n        client_project_dir.display()\n    );\n}\n\nfn language_cli_name(lang: Language) -> &'static str {\n    match lang {\n        Language::Rust => \"rust\",\n        Language::Csharp => \"csharp\",\n        Language::TypeScript => \"typescript\",\n        Language::UnrealCpp => \"unrealcpp\",\n    }\n}\n\npub fn default_out_dir_for_language(lang: Language) -> Option<PathBuf> {\n    match lang {\n        Language::Rust | Language::TypeScript => Some(PathBuf::from(\"src/module_bindings\")),\n        Language::Csharp => Some(PathBuf::from(\"module_bindings\")),\n        Language::UnrealCpp => None,\n    }\n}\n\npub fn resolve_language(module_path: &Path, requested: Option<Language>) -> anyhow::Result<Language> {\n    match requested {\n        Some(lang) => Ok(lang),\n        None => detect_default_language(module_path),\n    }\n}\n\npub fn build_generate_entry(\n    module_path: Option<&Path>,\n    language: Option<Language>,\n    out_dir: Option<&Path>,\n) -> HashMap<String, serde_json::Value> {\n    let mut entry = HashMap::new();\n    if let Some(lang) = language {\n        entry.insert(\"language\".to_string(), serde_json::json!(language_cli_name(lang)));\n    }\n    if let Some(path) = module_path {\n        entry.insert(\"module-path\".to_string(), serde_json::json!(path));\n    }\n    if let Some(path) = out_dir {\n        entry.insert(\"out-dir\".to_string(), serde_json::json!(path));\n    }\n    entry\n}\n\npub async fn run_prepared_generate_configs(\n    run_configs: Vec<GenerateRunConfig>,\n    extract_descriptions: ExtractDescriptions,\n    json_module: Option<Vec<PathBuf>>,\n    force: bool,\n    namespace_from_cli: bool,\n) -> anyhow::Result<()> {\n    for run in run_configs {\n        println!(\n            \"Generating {} module bindings for module {}\",\n            run.lang.display_name(),\n            run.project_path.display()\n        );\n\n        if namespace_from_cli && run.lang != Language::Csharp {\n            return Err(anyhow::anyhow!(\"--namespace is only supported with --lang csharp\"));\n        }\n\n        let module: ModuleDef = if let Some(paths) = &json_module {\n            let DeserializeWrapper::<RawModuleDef>(module) = if let Some(path) = paths.first() {\n                serde_json::from_slice(&fs::read(path)?)?\n            } else {\n                serde_json::from_reader(std::io::stdin().lock())?\n            };\n            module.try_into()?\n        } else {\n            let path = if let Some(path) = &run.wasm_file {\n                println!(\"Skipping build. Instead we are inspecting {}\", path.display());\n                path.clone()\n            } else if let Some(path) = &run.js_file {\n                println!(\"Skipping build. Instead we are inspecting {}\", path.display());\n                path.clone()\n            } else {\n                let (path, _) = build::exec_with_argstring(&run.project_path, &run.build_options).await?;\n                path\n            };\n            let spinner = indicatif::ProgressBar::new_spinner();\n            spinner.enable_steady_tick(std::time::Duration::from_millis(60));\n            spinner.set_message(format!(\"Extracting schema from {}...\", path.display()));\n            extract_descriptions(&path).context(\"could not extract schema\")?\n        };\n\n        fs::create_dir_all(&run.out_dir)?;\n\n        let mut paths = BTreeSet::new();\n        let private_tables = private_table_names(&module);\n        if !private_tables.is_empty() && !run.include_private {\n            println!(\"Skipping private tables during codegen: {}.\", private_tables.join(\", \"));\n        }\n        let mut options = CodegenOptions::default();\n        if run.include_private {\n            options.visibility = CodegenVisibility::IncludePrivate;\n        }\n\n        let csharp_lang;\n        let unreal_cpp_lang;\n        let gen_lang = match run.lang {\n            Language::Csharp => {\n                csharp_lang = Csharp {\n                    namespace: &run.namespace,\n                };\n                &csharp_lang as &dyn Lang\n            }\n            Language::UnrealCpp => {\n                unreal_cpp_lang = UnrealCpp {\n                    module_name: run.module_name.as_ref().unwrap(),\n                    uproject_dir: &run.out_dir,\n                    module_prefix: run.module_prefix.as_deref().unwrap_or(\"\"),\n                };\n                &unreal_cpp_lang as &dyn Lang\n            }\n            Language::Rust => &Rust,\n            Language::TypeScript => &TypeScript,\n        };\n\n        for OutputFile { filename, code } in generate(&module, gen_lang, &options) {\n            let fname = Path::new(&filename);\n            if let Some(parent) = fname.parent().filter(|p| !p.as_os_str().is_empty()) {\n                println!(\"Creating directory {}\", run.out_dir.join(parent).display());\n                fs::create_dir_all(run.out_dir.join(parent))?;\n            }\n            let path = run.out_dir.join(fname);\n            if !path.exists() || fs::read_to_string(&path)? != code {\n                println!(\"Writing file {}\", path.display());\n                fs::write(&path, code)?;\n            }\n            paths.insert(path);\n        }\n\n        let cleanup_root = match run.lang {\n            Language::UnrealCpp => run.out_dir.join(\"Source\").join(run.module_name.as_ref().unwrap()),\n            _ => run.out_dir.clone(),\n        };\n\n        let mut auto_generated_buf: [u8; AUTO_GENERATED_PREFIX.len()] = [0; AUTO_GENERATED_PREFIX.len()];\n        let files_to_delete = walkdir::WalkDir::new(&cleanup_root)\n            .into_iter()\n            .map(|entry_result| {\n                let entry = entry_result?;\n                if !entry.file_type().is_file() {\n                    return Ok(None);\n                }\n                let path = entry.into_path();\n                if paths.contains(&path) {\n                    return Ok(None);\n                }\n                let mut file = fs::File::open(&path)?;\n                Ok(match file.read_exact(&mut auto_generated_buf) {\n                    Ok(()) => (auto_generated_buf == AUTO_GENERATED_PREFIX.as_bytes()).then_some(path),\n                    Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => None,\n                    Err(err) => return Err(err.into()),\n                })\n            })\n            .filter_map(Result::transpose)\n            .collect::<anyhow::Result<Vec<_>>>()?;\n\n        if !files_to_delete.is_empty() {\n            println!(\"The following files were not generated by this command and will be deleted:\");\n            for path in &files_to_delete {\n                println!(\"  {}\", path.to_str().unwrap());\n            }\n\n            if y_or_n(force, \"Are you sure you want to delete these files?\")? {\n                for path in files_to_delete {\n                    fs::remove_file(path)?;\n                }\n                println!(\"Files deleted successfully.\");\n            } else {\n                println!(\"Files not deleted.\");\n            }\n        }\n\n        if let Err(err) = run.lang.format_files(&run.out_dir, paths) {\n            eprintln!(\"Could not format generated files: {err}\");\n        }\n\n        println!(\"Generate finished successfully.\");\n    }\n\n    Ok(())\n}\n\n/// Like `exec`, but lets you specify a custom a function to extract a schema from a file.\npub async fn exec_ex(\n    _config: Config,\n    args: &clap::ArgMatches,\n    extract_descriptions: ExtractDescriptions,\n    quiet_config: bool,\n    pre_loaded_config: Option<&LoadedConfig>,\n) -> anyhow::Result<()> {\n    // Build schema\n    let cmd = cli();\n    let schema = build_generate_config_schema(&cmd)?;\n\n    let no_config = args.get_flag(\"no_config\");\n    let env = args.get_one::<String>(\"env\").map(|s| s.as_str());\n\n    // Get generate configs (from spacetime.json or empty)\n    let owned_loaded;\n    let loaded_config_ref = if no_config {\n        None\n    } else if let Some(pre) = pre_loaded_config {\n        Some(pre)\n    } else {\n        owned_loaded = find_and_load_with_env(env)?;\n        owned_loaded.as_ref().inspect(|loaded| {\n            if !quiet_config {\n                for path in &loaded.loaded_files {\n                    println!(\"Using configuration from {}\", path.display());\n                }\n            }\n        })\n    };\n    let (using_config, generate_configs) = if let Some(loaded) = loaded_config_ref {\n        let filtered = get_filtered_generate_configs(&loaded.config, &cmd, &schema, args)?;\n        if filtered.is_empty() {\n            (false, vec![CommandConfig::new(&schema, HashMap::new(), args)?])\n        } else {\n            (true, filtered)\n        }\n    } else {\n        (false, vec![CommandConfig::new(&schema, HashMap::new(), args)?])\n    };\n\n    let config_dir = loaded_config_ref.map(|lc| lc.config_dir.as_path());\n    let run_configs = prepare_generate_run_configs(generate_configs, using_config, config_dir)?;\n    let json_module = args\n        .get_many::<PathBuf>(\"json_module\")\n        .map(|vals| vals.cloned().collect::<Vec<_>>());\n    let force = args.get_flag(\"force\");\n    let namespace_from_cli = args.value_source(\"namespace\") == Some(ValueSource::CommandLine);\n\n    run_prepared_generate_configs(\n        run_configs,\n        extract_descriptions,\n        json_module,\n        force,\n        namespace_from_cli,\n    )\n    .await\n}\n\n/// Execute generate from explicit entries without parsing generate CLI arguments\n/// or loading any config files from disk.\npub async fn exec_from_entries(\n    entries: Vec<HashMap<String, serde_json::Value>>,\n    extract_descriptions: ExtractDescriptions,\n    force: bool,\n    config_dir: Option<&Path>,\n) -> anyhow::Result<()> {\n    let cmd = cli();\n    let schema = build_generate_config_schema(&cmd)?;\n    let empty_matches = cmd.get_matches_from(vec![\"generate\"]);\n\n    let generate_configs: Vec<CommandConfig> = entries\n        .into_iter()\n        .map(|entry| {\n            let command_config = CommandConfig::new(&schema, entry, &empty_matches)?;\n            command_config.validate()?;\n            Ok(command_config)\n        })\n        .collect::<Result<Vec<_>, anyhow::Error>>()?;\n\n    let run_configs = prepare_generate_run_configs(generate_configs, true, config_dir)?;\n    run_prepared_generate_configs(run_configs, extract_descriptions, None, force, false).await\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, serde::Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum Language {\n    Csharp,\n    TypeScript,\n    Rust,\n    #[serde(alias = \"uecpp\", alias = \"ue5cpp\", alias = \"unreal\")]\n    UnrealCpp,\n}\n\nimpl clap::ValueEnum for Language {\n    fn value_variants<'a>() -> &'a [Self] {\n        &[Self::Csharp, Self::TypeScript, Self::Rust, Self::UnrealCpp]\n    }\n    fn to_possible_value(&self) -> Option<PossibleValue> {\n        Some(match self {\n            Self::Csharp => clap::builder::PossibleValue::new(\"csharp\").aliases([\"c#\", \"cs\"]),\n            Self::TypeScript => clap::builder::PossibleValue::new(\"typescript\").aliases([\"ts\", \"TS\"]),\n            Self::Rust => clap::builder::PossibleValue::new(\"rust\").aliases([\"rs\", \"RS\"]),\n            Self::UnrealCpp => PossibleValue::new(\"unrealcpp\").aliases([\"uecpp\", \"ue5cpp\", \"unreal\"]),\n        })\n    }\n}\n\nimpl Language {\n    /// Returns the display name for the language\n    pub fn display_name(&self) -> &'static str {\n        match self {\n            Language::Rust => \"Rust\",\n            Language::Csharp => \"C#\",\n            Language::TypeScript => \"TypeScript\",\n            Language::UnrealCpp => \"Unreal C++\",\n        }\n    }\n\n    fn format_files(&self, project_dir: &Path, generated_files: BTreeSet<PathBuf>) -> anyhow::Result<()> {\n        match self {\n            Language::Rust => rustfmt(generated_files)?,\n            Language::Csharp => dotnet_format(project_dir, generated_files)?,\n            Language::TypeScript => {\n                // TODO: implement formatting.\n            }\n            Language::UnrealCpp => {\n                // TODO: implement formatting.\n            }\n        }\n\n        Ok(())\n    }\n}\n\npub type ExtractDescriptions = fn(&Path) -> anyhow::Result<ModuleDef>;\npub fn extract_descriptions(wasm_file: &Path) -> anyhow::Result<ModuleDef> {\n    let bin_path = resolve_sibling_binary(\"spacetimedb-standalone\")?;\n    let child = Command::new(&bin_path)\n        .arg(\"extract-schema\")\n        .arg(wasm_file)\n        .stdout(Stdio::piped())\n        .spawn()\n        .with_context(|| format!(\"failed to spawn {}\", bin_path.display()))?;\n    let sats::serde::SerdeWrapper::<RawModuleDef>(module) = serde_json::from_reader(child.stdout.unwrap())?;\n    Ok(module.try_into()?)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::spacetime_config::*;\n    use std::collections::HashMap;\n\n    /// Helper to build a SpacetimeConfig with generate entries (database-centric)\n    fn make_gen_config(\n        fields: HashMap<String, serde_json::Value>,\n        generate: Vec<HashMap<String, serde_json::Value>>,\n    ) -> SpacetimeConfig {\n        SpacetimeConfig {\n            generate: Some(generate),\n            additional_fields: fields,\n            ..Default::default()\n        }\n    }\n\n    #[test]\n    fn test_filter_by_database_name() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let mut db1_fields = HashMap::new();\n        db1_fields.insert(\"database\".to_string(), serde_json::json!(\"db1\"));\n        db1_fields.insert(\"module-path\".to_string(), serde_json::json!(\"./module1\"));\n\n        let gen1 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out1\"));\n            m\n        };\n\n        let mut db2_fields = HashMap::new();\n        db2_fields.insert(\"database\".to_string(), serde_json::json!(\"db2\"));\n        db2_fields.insert(\"module-path\".to_string(), serde_json::json!(\"./module2\"));\n\n        let gen2 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"typescript\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out2\"));\n            m\n        };\n\n        let spacetime_config = SpacetimeConfig {\n            children: Some(vec![\n                make_gen_config(db1_fields, vec![gen1]),\n                make_gen_config(db2_fields, vec![gen2]),\n            ]),\n            ..Default::default()\n        };\n\n        // Filter by db1\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"db1\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 1, \"Should only match db1's generate entry\");\n    }\n\n    #[test]\n    fn test_no_filter_returns_all_generate_entries() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let mut fields = HashMap::new();\n        fields.insert(\"module-path\".to_string(), serde_json::json!(\"./module\"));\n\n        let gen1 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out1\"));\n            m\n        };\n        let gen2 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"typescript\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out2\"));\n            m\n        };\n\n        let spacetime_config = make_gen_config(fields, vec![gen1, gen2]);\n\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 2);\n    }\n\n    #[test]\n    fn test_generate_entry_inherits_module_path_from_parent() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"module-path\".to_string(), serde_json::json!(\"./server\"));\n\n        let mut child_fields = HashMap::new();\n        child_fields.insert(\"database\".to_string(), serde_json::json!(\"my-db\"));\n\n        let r#gen = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out\"));\n            m\n        };\n\n        let spacetime_config = SpacetimeConfig {\n            additional_fields: parent_fields,\n            children: Some(vec![make_gen_config(child_fields, vec![r#gen])]),\n            ..Default::default()\n        };\n\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"my-db\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 1);\n        // module_path should be inherited from parent\n        assert_eq!(\n            filtered[0].get_one::<PathBuf>(\"module_path\").unwrap(),\n            Some(PathBuf::from(\"./server\"))\n        );\n    }\n\n    #[test]\n    fn test_generate_deduplication() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let r#gen = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"typescript\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out\"));\n            m\n        };\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"module-path\".to_string(), serde_json::json!(\"./server\"));\n\n        let spacetime_config = SpacetimeConfig {\n            additional_fields: parent_fields,\n            generate: Some(vec![r#gen]),\n            children: Some(vec![\n                {\n                    let mut f = HashMap::new();\n                    f.insert(\"database\".to_string(), serde_json::json!(\"region-1\"));\n                    SpacetimeConfig {\n                        additional_fields: f,\n                        ..Default::default()\n                    }\n                },\n                {\n                    let mut f = HashMap::new();\n                    f.insert(\"database\".to_string(), serde_json::json!(\"region-2\"));\n                    SpacetimeConfig {\n                        additional_fields: f,\n                        ..Default::default()\n                    }\n                },\n            ]),\n            ..Default::default()\n        };\n\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        // Same module-path + same generate entry = deduplicated\n        assert_eq!(\n            filtered.len(),\n            1,\n            \"Expected deduplication: same module + same generate config = generate once\"\n        );\n    }\n\n    #[test]\n    fn test_generate_entry_specific_args_error_with_multiple_entries() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let mut fields = HashMap::new();\n        fields.insert(\"module-path\".to_string(), serde_json::json!(\"./module\"));\n\n        let gen1 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out1\"));\n            m\n        };\n        let gen2 = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"typescript\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out2\"));\n            m\n        };\n\n        let spacetime_config = make_gen_config(fields, vec![gen1, gen2]);\n\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"generate\", \"--out-dir\", \"/tmp/override\"]);\n        let err = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap_err();\n        let err_msg = err.to_string();\n        assert!(\n            err_msg.contains(\"--out-dir\"),\n            \"Expected error to mention --out-dir, got: {err_msg}\"\n        );\n    }\n\n    // Language-Specific Validation Tests\n\n    #[test]\n    fn test_rust_defaults_out_dir() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"--lang\", \"rust\"]);\n\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        std::fs::write(\n            module_dir.join(\"Cargo.toml\"),\n            \"[package]\\nname = \\\"m\\\"\\nversion = \\\"0.1.0\\\"\\n\",\n        )\n        .unwrap();\n\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], false, None).unwrap();\n        assert_eq!(runs.len(), 1);\n        assert_eq!(runs[0].out_dir, PathBuf::from(\"src/module_bindings\"));\n    }\n\n    #[test]\n    fn test_module_path_defaults_to_spacetimedb() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd\n            .clone()\n            .get_matches_from(vec![\"generate\", \"--lang\", \"rust\", \"--bin-path\", \"dummy.wasm\"]);\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], false, None).unwrap();\n        assert_eq!(runs[0].project_path, PathBuf::from(\"spacetimedb\"));\n    }\n\n    #[test]\n    fn test_defaults_resolve_relative_to_config_dir_when_config_exists() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"--lang\", \"rust\"]);\n\n        let config_dir = tempfile::TempDir::new().unwrap();\n        let module_dir = config_dir.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        std::fs::write(\n            module_dir.join(\"Cargo.toml\"),\n            \"[package]\\nname = \\\"m\\\"\\nversion = \\\"0.1.0\\\"\\n\",\n        )\n        .unwrap();\n\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], true, Some(config_dir.path())).unwrap();\n\n        assert_eq!(runs[0].project_path, module_dir);\n        assert_eq!(runs[0].out_dir, config_dir.path().join(\"src/module_bindings\"));\n    }\n\n    #[test]\n    fn test_cli_relative_out_dir_is_not_rebased_to_config_dir() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let config_dir = tempfile::TempDir::new().unwrap();\n        let module_dir = config_dir.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        std::fs::write(\n            module_dir.join(\"Cargo.toml\"),\n            \"[package]\\nname = \\\"m\\\"\\nversion = \\\"0.1.0\\\"\\n\",\n        )\n        .unwrap();\n\n        let matches =\n            cmd.clone()\n                .get_matches_from(vec![\"generate\", \"--lang\", \"rust\", \"--out-dir\", \"src/module_bindings\"]);\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], true, Some(config_dir.path())).unwrap();\n\n        assert_eq!(runs[0].out_dir, PathBuf::from(\"src/module_bindings\"));\n    }\n\n    #[test]\n    fn test_typescript_defaults_out_dir() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"--lang\", \"typescript\"]);\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], false, None).unwrap();\n        assert_eq!(runs[0].out_dir, PathBuf::from(\"src/module_bindings\"));\n    }\n\n    #[test]\n    fn test_csharp_defaults_out_dir() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"--lang\", \"csharp\"]);\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], false, None).unwrap();\n        assert_eq!(runs[0].out_dir, PathBuf::from(\"module_bindings\"));\n    }\n\n    #[test]\n    fn test_detect_typescript_language_from_client_project() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        std::fs::write(temp.path().join(\"package.json\"), \"{\\\"name\\\":\\\"client\\\"}\").unwrap();\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], true, Some(temp.path())).unwrap();\n        assert_eq!(runs[0].lang, Language::TypeScript);\n        assert_eq!(runs[0].out_dir, temp.path().join(\"src/module_bindings\"));\n    }\n\n    #[test]\n    fn test_detect_csharp_language_from_client_project() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        std::fs::write(temp.path().join(\"Client.csproj\"), \"<Project/>\").unwrap();\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let runs = prepare_generate_run_configs(vec![command_config], true, Some(temp.path())).unwrap();\n        assert_eq!(runs[0].lang, Language::Csharp);\n        assert_eq!(runs[0].out_dir, temp.path().join(\"module_bindings\"));\n    }\n\n    #[test]\n    fn test_error_when_default_module_path_missing_and_lang_not_set() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let command_config = CommandConfig::new(&schema, HashMap::new(), &matches).unwrap();\n        let err = prepare_generate_run_configs(vec![command_config], false, None).unwrap_err();\n        let msg = err.to_string();\n        assert!(msg.contains(\"Could not find module source at 'spacetimedb'\"));\n        assert!(msg.contains(\"--module-path\"));\n        assert!(msg.contains(\"spacetime.json\"));\n    }\n\n    #[test]\n    fn test_error_when_client_language_cannot_be_detected() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let temp = tempfile::TempDir::new().unwrap();\n        let module_dir = temp.path().join(\"spacetimedb\");\n        std::fs::create_dir_all(&module_dir).unwrap();\n        let mut cfg = HashMap::new();\n        cfg.insert(\n            \"module-path\".to_string(),\n            serde_json::Value::String(module_dir.display().to_string()),\n        );\n        let command_config = CommandConfig::new(&schema, cfg, &matches).unwrap();\n        let err = prepare_generate_run_configs(vec![command_config], true, Some(temp.path())).unwrap_err();\n        let msg = err.to_string();\n        assert!(msg.contains(\"Could not auto-detect client language\"));\n        assert!(msg.contains(\"--lang\"));\n        assert!(msg.contains(\"spacetime.json\"));\n    }\n\n    #[tokio::test]\n    async fn test_unrealcpp_requires_uproject_dir_and_unreal_module_name() {\n        use crate::config::Config;\n        use spacetimedb_paths::cli::CliTomlPath;\n        use spacetimedb_paths::FromPathUnchecked;\n\n        let cmd = cli();\n        let config = Config::new_with_localhost(CliTomlPath::from_path_unchecked(\"/tmp/test-config.toml\"));\n\n        // Test missing --uproject-dir (use alias --module-name for backwards compat)\n        let matches =\n            cmd.clone()\n                .get_matches_from(vec![\"generate\", \"--lang\", \"unrealcpp\", \"--module-name\", \"MyModule\"]);\n        let result = exec(config.clone(), &matches).await;\n\n        assert!(result.is_err());\n        let err_msg = result.unwrap_err().to_string();\n        assert!(\n            err_msg.contains(\"--uproject-dir\") || err_msg.contains(\"--out-dir\"),\n            \"Expected error about missing --uproject-dir or --out-dir, got: {err_msg}\",\n        );\n\n        // Test missing --unreal-module-name\n        let matches =\n            cmd.clone()\n                .get_matches_from(vec![\"generate\", \"--lang\", \"unrealcpp\", \"--uproject-dir\", \"/tmp/out\"]);\n        let result = exec(config, &matches).await;\n\n        assert!(result.is_err());\n        let err_msg = result.unwrap_err().to_string();\n        assert!(\n            err_msg.contains(\"--unreal-module-name is required for --lang unrealcpp\"),\n            \"Expected error about missing --unreal-module-name, got: {err_msg}\"\n        );\n    }\n\n    #[test]\n    fn test_validation_considers_both_cli_and_config() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        // Config provides uproject_dir\n        let mut config = HashMap::new();\n        config.insert(\n            \"language\".to_string(),\n            serde_json::Value::String(\"unrealcpp\".to_string()),\n        );\n        config.insert(\n            \"uproject_dir\".to_string(),\n            serde_json::Value::String(\"/config/path\".to_string()),\n        );\n\n        // CLI provides unreal_module_name (via alias --module-name)\n        let matches =\n            cmd.clone()\n                .get_matches_from(vec![\"generate\", \"--lang\", \"unrealcpp\", \"--module-name\", \"MyModule\"]);\n\n        let command_config = CommandConfig::new(&schema, config, &matches).unwrap();\n\n        let uproject_dir = command_config.get_one::<PathBuf>(\"uproject_dir\").unwrap();\n        let module_name = command_config.get_one::<String>(\"unreal_module_name\").unwrap();\n\n        assert_eq!(uproject_dir, Some(PathBuf::from(\"/config/path\")));\n        assert_eq!(module_name, Some(\"MyModule\".to_string()));\n    }\n\n    #[test]\n    fn test_generate_dedup_with_inherited_generate_from_parent() {\n        // Two sibling databases inheriting the same generate + same module-path from parent\n        // should deduplicate to a single generate entry\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let r#gen = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"typescript\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/bindings\"));\n            m\n        };\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"module-path\".to_string(), serde_json::json!(\"./server\"));\n\n        let spacetime_config = SpacetimeConfig {\n            additional_fields: parent_fields,\n            generate: Some(vec![r#gen]),\n            children: Some(vec![\n                {\n                    let mut f = HashMap::new();\n                    f.insert(\"database\".to_string(), serde_json::json!(\"region-1\"));\n                    SpacetimeConfig {\n                        additional_fields: f,\n                        ..Default::default()\n                    }\n                },\n                {\n                    let mut f = HashMap::new();\n                    f.insert(\"database\".to_string(), serde_json::json!(\"region-2\"));\n                    SpacetimeConfig {\n                        additional_fields: f,\n                        ..Default::default()\n                    }\n                },\n            ]),\n            ..Default::default()\n        };\n\n        // No filter — all 3 targets (parent + 2 children) share same module-path + same generate\n        let matches = cmd.clone().get_matches_from(vec![\"generate\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        // Should be deduplicated to 1 entry since all have same (module_path, generate_entry)\n        assert_eq!(\n            filtered.len(),\n            1,\n            \"Inherited generate entries with same module-path should be deduplicated\"\n        );\n    }\n\n    #[test]\n    fn test_generate_glob_filter_matches_pattern() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let r#gen = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out\"));\n            m\n        };\n\n        let spacetime_config = SpacetimeConfig {\n            children: Some(vec![\n                make_gen_config(\n                    {\n                        let mut m = HashMap::new();\n                        m.insert(\"database\".to_string(), serde_json::json!(\"region-1\"));\n                        m.insert(\"module-path\".to_string(), serde_json::json!(\"./m1\"));\n                        m\n                    },\n                    vec![r#gen.clone()],\n                ),\n                make_gen_config(\n                    {\n                        let mut m = HashMap::new();\n                        m.insert(\"database\".to_string(), serde_json::json!(\"region-2\"));\n                        m.insert(\"module-path\".to_string(), serde_json::json!(\"./m2\"));\n                        m\n                    },\n                    vec![r#gen.clone()],\n                ),\n                make_gen_config(\n                    {\n                        let mut m = HashMap::new();\n                        m.insert(\"database\".to_string(), serde_json::json!(\"global\"));\n                        m.insert(\"module-path\".to_string(), serde_json::json!(\"./m3\"));\n                        m\n                    },\n                    vec![r#gen],\n                ),\n            ]),\n            ..Default::default()\n        };\n\n        // Glob: region-* should match region-1 and region-2 but not global\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"region-*\"]);\n        let filtered = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        // region-1 and region-2 have different module-paths, so no dedup → 2 entries\n        assert_eq!(filtered.len(), 2);\n    }\n\n    #[test]\n    fn test_generate_error_when_glob_matches_nothing() {\n        let cmd = cli();\n        let schema = build_generate_config_schema(&cmd).unwrap();\n\n        let r#gen = {\n            let mut m = HashMap::new();\n            m.insert(\"language\".to_string(), serde_json::json!(\"rust\"));\n            m.insert(\"out_dir\".to_string(), serde_json::json!(\"/tmp/out\"));\n            m\n        };\n\n        let spacetime_config = make_gen_config(\n            {\n                let mut m = HashMap::new();\n                m.insert(\"database\".to_string(), serde_json::json!(\"my-db\"));\n                m.insert(\"module-path\".to_string(), serde_json::json!(\"./server\"));\n                m\n            },\n            vec![r#gen],\n        );\n\n        let matches = cmd.clone().get_matches_from(vec![\"generate\", \"nonexistent-*\"]);\n        let result = get_filtered_generate_configs(&spacetime_config, &cmd, &schema, &matches);\n        assert!(result.is_err());\n        let err_msg = result.unwrap_err().to_string();\n        assert!(\n            err_msg.contains(\"No database target matches\"),\n            \"Error should mention no match, got: {err_msg}\"\n        );\n    }\n\n    #[test]\n    fn test_language_serde_deserialize_all_variants() {\n        // Verify all Language variants deserialize correctly from config JSON strings.\n        // This catches drift between the serde and clap ValueEnum impls.\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"csharp\".into())).unwrap(),\n            Language::Csharp\n        );\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"typescript\".into())).unwrap(),\n            Language::TypeScript\n        );\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"rust\".into())).unwrap(),\n            Language::Rust\n        );\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"unrealcpp\".into())).unwrap(),\n            Language::UnrealCpp\n        );\n\n        // Aliases\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"uecpp\".into())).unwrap(),\n            Language::UnrealCpp\n        );\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"ue5cpp\".into())).unwrap(),\n            Language::UnrealCpp\n        );\n        assert_eq!(\n            serde_json::from_value::<Language>(serde_json::Value::String(\"unreal\".into())).unwrap(),\n            Language::UnrealCpp\n        );\n\n        // Invalid language should error\n        assert!(serde_json::from_value::<Language>(serde_json::Value::String(\"java\".into())).is_err());\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/init.rs",
    "content": "use crate::detect::{find_executable, has_package_manager};\nuse crate::Config;\nuse anyhow::anyhow;\nuse anyhow::Context;\nuse clap::{Arg, ArgMatches};\nuse colored::Colorize;\nuse convert_case::{Case, Casing};\nuse dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input, Select};\nuse reqwest::Url;\nuse serde::{Deserialize, Serialize};\nuse serde_json::json;\nuse spacetimedb_client_api_messages::name::parse_database_name;\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\nuse std::collections::BTreeMap;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::time::{SystemTime, UNIX_EPOCH};\nuse toml_edit::{value, DocumentMut, Item};\nuse xmltree::{Element, XMLNode};\n\nuse crate::spacetime_config::{PackageManager, SpacetimeConfig, CONFIG_FILENAME};\nuse crate::subcommands::login::{spacetimedb_login_and_save, DEFAULT_AUTH_HOST};\n\nmod embedded {\n    use spacetimedb_data_structures::map::HashCollectionExt as _;\n    include!(concat!(env!(\"OUT_DIR\"), \"/embedded_templates.rs\"));\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize)]\npub struct TemplateDefinition {\n    pub id: String,\n    pub description: String,\n    pub server_source: String,\n    pub client_source: String,\n    #[serde(default)]\n    pub server_lang: Option<String>,\n    #[serde(default)]\n    pub client_lang: Option<String>,\n    #[serde(default)]\n    pub client_framework: Option<String>,\n}\n\n#[derive(Debug, Deserialize)]\nstruct TemplatesList {\n    templates: Vec<TemplateDefinition>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum TemplateType {\n    Builtin,\n    GitHub,\n    Empty,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ServerLanguage {\n    Rust,\n    Csharp,\n    TypeScript,\n    Cpp,\n}\n\nimpl ServerLanguage {\n    fn as_str(&self) -> &'static str {\n        match self {\n            ServerLanguage::Rust => \"rust\",\n            ServerLanguage::Csharp => \"csharp\",\n            ServerLanguage::TypeScript => \"typescript\",\n            ServerLanguage::Cpp => \"cpp\",\n        }\n    }\n\n    fn from_str(s: &str) -> anyhow::Result<Option<Self>> {\n        match s.to_lowercase().as_str() {\n            \"rust\" => Ok(Some(ServerLanguage::Rust)),\n            \"csharp\" | \"c#\" => Ok(Some(ServerLanguage::Csharp)),\n            \"typescript\" => Ok(Some(ServerLanguage::TypeScript)),\n            \"cpp\" | \"c++\" | \"cxx\" => Ok(Some(ServerLanguage::Cpp)),\n            _ => Err(anyhow!(\"Unknown server language: {}\", s)),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ClientLanguage {\n    Rust,\n    Csharp,\n    TypeScript,\n}\n\nimpl ClientLanguage {\n    fn as_str(&self) -> &'static str {\n        match self {\n            ClientLanguage::Rust => \"rust\",\n            ClientLanguage::Csharp => \"csharp\",\n            ClientLanguage::TypeScript => \"typescript\",\n        }\n    }\n\n    fn from_str(s: &str) -> anyhow::Result<Option<Self>> {\n        match s.to_lowercase().as_str() {\n            \"rust\" => Ok(Some(ClientLanguage::Rust)),\n            \"csharp\" | \"c#\" => Ok(Some(ClientLanguage::Csharp)),\n            \"typescript\" => Ok(Some(ClientLanguage::TypeScript)),\n            _ => Err(anyhow!(\"Unknown client language: {}\", s)),\n        }\n    }\n}\n\npub struct TemplateConfig {\n    pub project_name: String,\n    pub project_path: PathBuf,\n    pub template_type: TemplateType,\n    pub server_lang: Option<ServerLanguage>,\n    pub client_lang: Option<ClientLanguage>,\n    pub github_repo: Option<String>,\n    pub template_def: Option<TemplateDefinition>,\n    pub use_local: bool,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct InitOptions {\n    pub project_path: Option<PathBuf>,\n    pub project_name: Option<String>,\n    pub project_name_default: Option<String>,\n    pub database_name_default: Option<String>,\n    pub server_only: bool,\n    pub lang: Option<String>,\n    pub template: Option<String>,\n    pub local: bool,\n    pub non_interactive: bool,\n    /// When true, suppress the \"Next steps\" message after init (e.g. when called from `spacetime dev`).\n    pub skip_next_steps: bool,\n}\n\nimpl InitOptions {\n    pub fn from_args(args: &ArgMatches) -> Self {\n        Self {\n            project_path: args.get_one::<PathBuf>(\"project-path\").cloned(),\n            project_name: args.get_one::<String>(\"project-name\").cloned(),\n            project_name_default: None,\n            database_name_default: None,\n            server_only: args.get_flag(\"server-only\"),\n            lang: args.get_one::<String>(\"lang\").cloned(),\n            template: args.get_one::<String>(\"template\").cloned(),\n            local: args.get_flag(\"local\"),\n            non_interactive: args.get_flag(\"non-interactive\"),\n            skip_next_steps: false,\n        }\n    }\n}\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"init\")\n        .about(\"Initializes a new spacetime project.\")\n        .arg(\n            Arg::new(\"project-path\")\n                .long(\"project-path\")\n                .value_name(\"PATH\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .help(\"Directory where the project will be created (defaults to `./<PROJECT_NAME>`)\"),\n        )\n        .arg(Arg::new(\"project-name\").value_name(\"PROJECT_NAME\").help(\"Project name\"))\n        .arg(\n            Arg::new(\"server-only\")\n                .long(\"server-only\")\n                .help(\"Initialize server only from the template (no client)\")\n                .action(clap::ArgAction::SetTrue),\n        )\n        .arg(Arg::new(\"lang\").long(\"lang\").value_name(\"LANG\").help(\n            \"Server language: rust, csharp, typescript, cpp (it can only be used when --template is not specified)\",\n        ))\n        .arg(\n            Arg::new(\"template\")\n                .short('t')\n                .long(\"template\")\n                .value_name(\"TEMPLATE\")\n                .help(\"Template ID or GitHub repository (owner/repo or URL)\"),\n        )\n        .arg(\n            Arg::new(\"local\")\n                .long(\"local\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Use local deployment instead of Maincloud\"),\n        )\n        .arg(\n            Arg::new(\"non-interactive\")\n                .long(\"non-interactive\")\n                .action(clap::ArgAction::SetTrue)\n                .help(\"Run in non-interactive mode\"),\n        )\n}\n\npub async fn fetch_templates_list() -> anyhow::Result<Vec<TemplateDefinition>> {\n    let content = embedded::get_templates_json();\n    let templates_list: TemplatesList = serde_json::from_str(content).context(\"Failed to parse templates list JSON\")?;\n\n    Ok(templates_list.templates)\n}\n\npub async fn check_and_prompt_login(config: &mut Config) -> anyhow::Result<bool> {\n    if config.spacetimedb_token().is_some() {\n        println!(\"{}\", \"You are logged in to SpacetimeDB.\".green());\n        return Ok(true);\n    }\n\n    println!(\"{}\", \"You are not logged in to SpacetimeDB.\".yellow());\n\n    let theme = ColorfulTheme::default();\n    let should_login = Confirm::with_theme(&theme)\n        .with_prompt(\"Would you like to log in? (required for Maincloud deployment)\")\n        .default(true)\n        .interact()?;\n\n    if should_login {\n        let host = Url::parse(DEFAULT_AUTH_HOST)?;\n        spacetimedb_login_and_save(config, &host, false, true).await?;\n        println!(\"{}\", \"Successfully logged in!\".green());\n        Ok(true)\n    } else {\n        println!(\"{}\", \"Continuing with local deployment.\".yellow());\n        Ok(false)\n    }\n}\n\nfn slugify(name: &str) -> String {\n    name.to_case(Case::Kebab)\n}\n\nasync fn get_project_name(options: &InitOptions, is_interactive: bool) -> anyhow::Result<String> {\n    if let Some(name) = &options.project_name {\n        if is_interactive {\n            println!(\"{} {}\", \"Project name:\".bold(), name);\n        }\n        return Ok(name.clone());\n    }\n\n    if !is_interactive {\n        anyhow::bail!(\"PROJECT_NAME is required in non-interactive mode\");\n    }\n\n    let default_project_name = options\n        .project_name_default\n        .clone()\n        .unwrap_or_else(|| \"my-spacetime-app\".to_string());\n\n    let theme = ColorfulTheme::default();\n    let name = Input::with_theme(&theme)\n        .with_prompt(\"Project name\")\n        .default(default_project_name)\n        .validate_with(|input: &String| -> Result<(), String> {\n            if input.trim().is_empty() {\n                return Err(\"Project name cannot be empty\".to_string());\n            }\n            Ok(())\n        })\n        .interact_text()?\n        .trim()\n        .to_string();\n\n    Ok(name)\n}\n\nasync fn get_project_path(\n    options: &InitOptions,\n    project_name: &str,\n    is_interactive: bool,\n    is_server_only: bool,\n) -> anyhow::Result<PathBuf> {\n    if let Some(path) = &options.project_path {\n        if is_interactive {\n            println!(\"{} {}\", \"Project path:\".bold(), path.display());\n        }\n        return Ok(path.clone());\n    }\n\n    if !is_interactive {\n        return Ok(PathBuf::from(slugify(project_name)));\n    }\n\n    let theme = ColorfulTheme::default();\n    let path_str = Input::with_theme(&theme)\n        .with_prompt(\"Project path\")\n        .default(format!(\"./{}\", slugify(project_name)))\n        .validate_with(|input: &String| -> Result<(), String> {\n            if input.trim().is_empty() {\n                return Err(\"Project path cannot be empty\".to_string());\n            }\n\n            let path = Path::new(input);\n            if path.exists() {\n                if !path.is_dir() {\n                    return Err(format!(\"A file exists at '{}'. Please choose a different path.\", input));\n                }\n                match std::fs::read_dir(path) {\n                    Ok(entries) => {\n                        // If server-only, allow non-empty directories (client files won't be created)\n                        // but only if the `spacetimedb` subdirectory does not already exist\n                        let entries_vec = entries.collect::<Vec<_>>();\n                        if is_server_only\n                            && !entries_vec.iter().any(|e| match e {\n                                Ok(dir_entry) => dir_entry.file_name() == \"spacetimedb\",\n                                Err(_) => false,\n                            })\n                        {\n                            return Ok(());\n                        }\n                        if entries_vec.iter().filter(|e| e.is_ok()).count() > 0 {\n                            return Err(format!(\n                                \"Directory '{}' already exists and is not empty. Please choose a different path.\",\n                                input\n                            ));\n                        }\n                    }\n                    Err(_) => {\n                        return Err(format!(\n                            \"Cannot access directory '{}'. Please choose a different path.\",\n                            input\n                        ));\n                    }\n                }\n            }\n            Ok(())\n        })\n        .interact_text()?\n        .trim()\n        .to_string();\n\n    Ok(PathBuf::from(path_str))\n}\n\nfn create_template_config_from_template_str(\n    project_name: String,\n    project_path: PathBuf,\n    template_str: &str,\n    templates: &[TemplateDefinition],\n) -> anyhow::Result<TemplateConfig> {\n    if let Some(template) = templates.iter().find(|t| t.id == template_str) {\n        // Builtin template\n        Ok(TemplateConfig {\n            project_name,\n            project_path,\n            template_type: TemplateType::Builtin,\n            server_lang: parse_server_lang(&template.server_lang)?,\n            client_lang: parse_client_lang(&template.client_lang)?,\n            github_repo: None,\n            template_def: Some(template.clone()),\n            use_local: true,\n        })\n    } else {\n        // GitHub template\n        Ok(TemplateConfig {\n            project_name,\n            project_path,\n            template_type: TemplateType::GitHub,\n            server_lang: None,\n            client_lang: None,\n            github_repo: Some(template_str.to_string()),\n            template_def: None,\n            use_local: true,\n        })\n    }\n}\n\n#[cfg(windows)]\nfn run_pm(pm: PackageManager, args: &[&str], cwd: &Path) -> std::io::Result<std::process::ExitStatus> {\n    // Use cmd to resolve .cmd/.bat/.exe shims properly on Windows\n    std::process::Command::new(\"cmd\")\n        .arg(\"/C\")\n        .arg(pm.to_string())\n        .args(args)\n        .current_dir(cwd)\n        .status()\n}\n\n#[cfg(not(windows))]\nfn run_pm(pm: PackageManager, args: &[&str], cwd: &Path) -> std::io::Result<std::process::ExitStatus> {\n    std::process::Command::new(pm.to_string())\n        .args(args)\n        .current_dir(cwd)\n        .status()\n}\n\npub fn prompt_for_typescript_package_manager() -> anyhow::Result<Option<PackageManager>> {\n    println!(\n        \"\\n{}\",\n        \"TypeScript server requires dependencies to be installed before publishing.\".yellow()\n    );\n\n    let theme = ColorfulTheme::default();\n    let choices = vec![\"npm\", \"pnpm\", \"yarn\", \"bun\", \"none\"];\n\n    loop {\n        let selection = Select::with_theme(&theme)\n            .with_prompt(\"Which package manager would you like to use?\")\n            .items(&choices)\n            .default(0)\n            .interact()?;\n\n        let pm = match selection {\n            0 => Some(PackageManager::Npm),\n            1 => Some(PackageManager::Pnpm),\n            2 => Some(PackageManager::Yarn),\n            3 => Some(PackageManager::Bun),\n            _ => None,\n        };\n\n        match pm {\n            Some(pm) if !has_package_manager(pm) => {\n                println!(\n                    \"{}\",\n                    format!(\n                        \"'{}' was not found on PATH. Please install it or choose a different package manager.\",\n                        pm\n                    )\n                    .yellow()\n                );\n            }\n            _ => return Ok(pm),\n        }\n    }\n}\n\npub fn install_typescript_dependencies(\n    package_dir: &Path,\n    package_manager: Option<PackageManager>,\n) -> anyhow::Result<()> {\n    if let Some(pm) = package_manager {\n        println!(\"Installing dependencies with {}...\", pm);\n\n        // Command arguments\n        let mut args_map: HashMap<&str, Vec<&str>> = HashMap::new();\n        args_map.insert(\"npm\", vec![\"install\", \"--no-fund\", \"--no-audit\", \"--loglevel=error\"]);\n        args_map.insert(\"yarn\", vec![\"install\", \"--no-fund\"]);\n        args_map.insert(\n            \"pnpm\",\n            vec![\"install\", \"--ignore-workspace\", \"--config.ignore-scripts=false\"],\n        );\n        args_map.insert(\"bun\", vec![\"install\"]);\n\n        let args: &[&str] = args_map\n            .get(pm.to_string().as_str())\n            .map(|v| v.as_slice())\n            .unwrap_or(&[]);\n\n        // Run and stream output cross-platform\n        let status = run_pm(pm, args, package_dir);\n\n        match status {\n            Ok(s) if s.success() => {\n                println!(\"{}\", \"Dependencies installed successfully!\".green());\n            }\n            Ok(s) => {\n                eprintln!(\n                    \"{}\",\n                    format!(\"Installation failed (exit code {}).\", s.code().unwrap_or(-1)).red()\n                );\n                println!(\n                    \"{}\",\n                    format!(\"Please run '{} install' manually in {}.\", pm, package_dir.display()).yellow()\n                );\n            }\n            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n                eprintln!(\n                    \"{}\",\n                    format!(\"Failed to find '{}'. Is it installed and on PATH?\", pm).red()\n                );\n                println!(\n                    \"{}\",\n                    format!(\"Please run '{} install' manually in {}.\", pm, package_dir.display()).yellow()\n                );\n            }\n            Err(e) => {\n                eprintln!(\"{}\", format!(\"Failed to execute {}: {}\", pm, e).red());\n                println!(\n                    \"{}\",\n                    format!(\"Please run '{} install' manually in {}.\", pm, package_dir.display()).yellow()\n                );\n            }\n        }\n    } else {\n        println!(\n            \"{}\",\n            format!(\n                \"You have chosen not to use a package manager. Please install dependencies manually in {}.\",\n                package_dir.display()\n            )\n            .yellow()\n        );\n    }\n\n    Ok(())\n}\n\npub async fn exec_with_options(config: &mut Config, options: &InitOptions) -> anyhow::Result<PathBuf> {\n    let is_interactive = !options.non_interactive;\n\n    let use_local = if options.local {\n        true\n    } else if is_interactive {\n        !check_and_prompt_login(config).await?\n    } else {\n        // In non-interactive mode, default to local deployment if not logged in\n        config.spacetimedb_token().is_none()\n    };\n\n    let is_server_only = options.server_only;\n\n    let project_name = get_project_name(options, is_interactive).await?;\n    let project_path = get_project_path(options, &project_name, is_interactive, is_server_only).await?;\n    let local_database_name = get_local_database_name(options, &project_name, is_interactive)?;\n\n    let mut template_config = if is_interactive {\n        get_template_config_interactive(options, project_name, project_path.clone()).await?\n    } else {\n        get_template_config_non_interactive(options, project_name, project_path.clone()).await?\n    };\n\n    template_config.use_local = use_local;\n\n    ensure_empty_directory(\n        &template_config.project_name,\n        &template_config.project_path,\n        is_server_only,\n    )?;\n    init_from_template(&template_config, &template_config.project_path, is_server_only).await?;\n\n    if let Some(path) = create_default_spacetime_config_if_missing(&project_path)? {\n        println!(\"{} Created {}\", \"✓\".green(), path.display());\n    }\n\n    // Determine package manager for TypeScript projects\n    let uses_typescript = template_config.server_lang == Some(ServerLanguage::TypeScript)\n        || template_config.client_lang == Some(ClientLanguage::TypeScript);\n\n    let package_manager = if uses_typescript && is_interactive {\n        prompt_for_typescript_package_manager()?\n    } else {\n        None\n    };\n\n    if template_config.server_lang == Some(ServerLanguage::TypeScript)\n        && template_config.client_lang == Some(ClientLanguage::TypeScript)\n    {\n        // If server & client are TypeScript, handle dependency installation\n        // NOTE: All server templates must have their server code in `spacetimedb/` directory\n        // This is not a requirement in general, but is a requirement for all templates\n        // i.e. `spacetime dev` is valid on non-templates.\n        let client_dir = &template_config.project_path;\n        let server_dir = client_dir.join(\"spacetimedb\");\n        install_typescript_dependencies(&server_dir, package_manager)?;\n        install_typescript_dependencies(client_dir, package_manager)?;\n    } else if template_config.client_lang == Some(ClientLanguage::TypeScript) {\n        let client_dir = &template_config.project_path;\n        install_typescript_dependencies(client_dir, package_manager)?;\n    } else if template_config.server_lang == Some(ServerLanguage::TypeScript) {\n        // NOTE: All server templates must have their server code in `spacetimedb/` directory\n        // This is not a requirement in general, but is a requirement for all templates\n        // i.e. `spacetime dev` is valid on non-templates.\n        let server_dir = template_config.project_path.join(\"spacetimedb\");\n        install_typescript_dependencies(&server_dir, package_manager)?;\n    }\n\n    // Configure client dev command if a client is present\n    if !is_server_only {\n        let client_lang_str = template_config.client_lang.as_ref().map(|l| l.as_str());\n        if let Some(path) = crate::spacetime_config::setup_for_project(&project_path, client_lang_str, package_manager)?\n        {\n            println!(\"{} Created {}\", \"✓\".green(), path.display());\n        }\n    }\n\n    if let Some(path) = create_local_spacetime_config_if_missing(&project_path, &local_database_name)? {\n        println!(\"{} Created {}\", \"✓\".green(), path.display());\n    }\n\n    if !options.skip_next_steps {\n        print_next_steps(&template_config, &project_path)?;\n    }\n\n    Ok(project_path)\n}\n\nfn get_local_database_name(options: &InitOptions, project_name: &str, is_interactive: bool) -> anyhow::Result<String> {\n    let default_database = options\n        .database_name_default\n        .clone()\n        .unwrap_or_else(|| format!(\"{project_name}-{}\", random_suffix(5)));\n    if !is_interactive {\n        return Ok(default_database);\n    }\n\n    let theme = ColorfulTheme::default();\n    let database_name = Input::with_theme(&theme)\n        .with_prompt(\"Database name\")\n        .default(default_database)\n        .validate_with(|input: &String| -> Result<(), String> {\n            parse_database_name(input.trim()).map_err(|e| e.to_string())?;\n            Ok(())\n        })\n        .interact_text()?\n        .trim()\n        .to_string();\n\n    Ok(database_name)\n}\n\nfn create_default_spacetime_config_if_missing(project_path: &Path) -> anyhow::Result<Option<PathBuf>> {\n    let config_path = project_path.join(CONFIG_FILENAME);\n    if config_path.exists() {\n        return Ok(None);\n    }\n\n    let mut config = SpacetimeConfig::default();\n    config\n        .additional_fields\n        .insert(\"server\".to_string(), json!(\"maincloud\"));\n\n    if project_path.join(\"spacetimedb\").is_dir() {\n        config\n            .additional_fields\n            .insert(\"module-path\".to_string(), json!(\"./spacetimedb\"));\n    }\n\n    Ok(Some(config.save_to_dir(project_path)?))\n}\n\nfn create_local_spacetime_config_if_missing(\n    project_path: &Path,\n    database_name: &str,\n) -> anyhow::Result<Option<PathBuf>> {\n    let main_config_path = project_path.join(CONFIG_FILENAME);\n    if !main_config_path.exists() {\n        return Ok(None);\n    }\n\n    let local_config_path = project_path.join(\"spacetime.local.json\");\n    if local_config_path.exists() {\n        return Ok(None);\n    }\n\n    let mut local_config = SpacetimeConfig::default();\n    local_config\n        .additional_fields\n        .insert(\"database\".to_string(), json!(database_name));\n    local_config.save(&local_config_path)?;\n\n    Ok(Some(local_config_path))\n}\n\nfn random_suffix(len: usize) -> String {\n    const ALNUM: &[u8] = b\"abcdefghijklmnopqrstuvwxyz0123456789\";\n    let now = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .map(|d| d.as_nanos() as u64)\n        .unwrap_or(0);\n    let pid = std::process::id() as u64;\n    let mut state = now ^ (pid << 16);\n    let mut out = String::with_capacity(len);\n    for _ in 0..len {\n        // Simple xorshift to derive pseudo-random chars without extra deps.\n        state ^= state << 13;\n        state ^= state >> 7;\n        state ^= state << 17;\n        let idx = (state % ALNUM.len() as u64) as usize;\n        out.push(ALNUM[idx] as char);\n    }\n    out\n}\n\nasync fn get_template_config_non_interactive(\n    options: &InitOptions,\n    project_name: String,\n    project_path: PathBuf,\n) -> anyhow::Result<TemplateConfig> {\n    // Check if template is provided\n    if let Some(template_str) = options.template.as_ref() {\n        // Check if it's a builtin template\n        let templates = fetch_templates_list().await?;\n        return create_template_config_from_template_str(project_name, project_path, template_str, &templates);\n    }\n\n    // No template - require at least one language option\n    let server_lang_str = options.lang.clone();\n\n    if server_lang_str.is_none() {\n        anyhow::bail!(\"Either --template or --lang must be provided in non-interactive mode\");\n    }\n\n    Ok(TemplateConfig {\n        project_name,\n        project_path,\n        template_type: TemplateType::Empty,\n        server_lang: parse_server_lang(&server_lang_str)?,\n        client_lang: None,\n        github_repo: None,\n        template_def: None,\n        use_local: true,\n    })\n}\n\npub fn ensure_empty_directory(_project_name: &str, project_path: &Path, is_server_only: bool) -> anyhow::Result<()> {\n    if project_path.exists() {\n        if !project_path.is_dir() {\n            anyhow::bail!(\n                \"Path {} exists but is not a directory. A new SpacetimeDB project must be initialized in an empty directory.\",\n                project_path.display()\n            );\n        }\n\n        if std::fs::read_dir(project_path).unwrap().count() > 0 {\n            if is_server_only {\n                let server_dir = project_path.join(\"spacetimedb\");\n                if server_dir.exists() && std::fs::read_dir(server_dir).unwrap().count() > 0 {\n                    anyhow::bail!(\n                        \"A SpacetimeDB module already exists in the target directory: {}\",\n                        project_path.display()\n                    );\n                }\n            } else {\n                anyhow::bail!(\n                    \"Cannot create new SpacetimeDB project in non-empty directory: {}\",\n                    project_path.display()\n                );\n            }\n        }\n    } else {\n        fs::create_dir_all(project_path).context(\"Failed to create directory\")?;\n    }\n    Ok(())\n}\n\nasync fn get_template_config_interactive(\n    options: &InitOptions,\n    project_name: String,\n    project_path: PathBuf,\n) -> anyhow::Result<TemplateConfig> {\n    let theme = ColorfulTheme::default();\n\n    // Check if template is provided\n    if let Some(template_str) = options.template.as_ref() {\n        println!(\"{} {}\", \"Template:\".bold(), template_str);\n\n        let templates = fetch_templates_list().await?;\n        return create_template_config_from_template_str(project_name, project_path, template_str, &templates);\n    }\n\n    let server_lang_arg = options.lang.as_ref();\n    if server_lang_arg.is_some() {\n        let server_lang = parse_server_lang(&server_lang_arg.cloned())?;\n        if let Some(lang_str) = server_lang_arg {\n            println!(\"{} {}\", \"Server language:\".bold(), lang_str);\n        }\n\n        return Ok(TemplateConfig {\n            project_name,\n            project_path,\n            template_type: TemplateType::Empty,\n            server_lang,\n            client_lang: None,\n            github_repo: None,\n            template_def: None,\n            use_local: true,\n        });\n    }\n\n    // Fully interactive mode - prompt for template/language selection\n    let templates = fetch_templates_list().await?;\n\n    // First menu: all language combinations from templates + GitHub + None.\n    let mut templates_by_lang: BTreeMap<String, Vec<usize>> = BTreeMap::new();\n    for (idx, template) in templates.iter().enumerate() {\n        let server_lang = template.server_lang.as_deref();\n        let client_lang = template.client_framework.as_deref().or(template.client_lang.as_deref());\n        let lang = if server_lang == client_lang {\n            format_language_label(server_lang)\n        } else {\n            format!(\n                \"{}/{}\",\n                format_language_label(server_lang),\n                format_language_label(client_lang),\n            )\n        };\n        templates_by_lang.entry(lang).or_default().push(idx);\n    }\n\n    let lang_keys: Vec<String> = templates_by_lang.keys().cloned().collect();\n    let mut lang_items: Vec<String> = templates_by_lang\n        .iter()\n        .map(|(lang, template_indices)| {\n            let count = template_indices.len();\n            let template_word = if count == 1 { \"template\" } else { \"templates\" };\n            format!(\"{lang} ({count} {template_word})\")\n        })\n        .collect();\n    lang_items.push(\"Clone from GitHub (owner/repo or git URL)\".to_string());\n    lang_items.push(\"None\".to_string());\n\n    let github_clone_index = lang_keys.len();\n    let none_index = lang_keys.len() + 1;\n    loop {\n        let client_selection = FuzzySelect::with_theme(&theme)\n            .with_prompt(\"Select a language (type to filter)\")\n            .items(&lang_items)\n            .default(0)\n            .interact()?;\n\n        if client_selection < lang_keys.len() {\n            let selected_lang = &lang_keys[client_selection];\n            let template_indices = templates_by_lang\n                .get(selected_lang)\n                .ok_or_else(|| anyhow::anyhow!(\"No templates found for selected language\"))?;\n\n            let template = if template_indices.len() > 1 {\n                let template_items: Vec<String> = template_indices\n                    .iter()\n                    .map(|&idx| {\n                        let template = &templates[idx];\n                        format!(\"{} - {}\", template.id, template.description)\n                    })\n                    .collect();\n\n                let pair_prompt = format!(\"Templates available for {selected_lang} (type to filter, Esc to go back)\");\n                let template_selection = FuzzySelect::with_theme(&theme)\n                    .with_prompt(&pair_prompt)\n                    .items(&template_items)\n                    .default(0)\n                    .interact_opt()?;\n\n                let Some(template_selection) = template_selection else {\n                    continue;\n                };\n\n                &templates[template_indices[template_selection]]\n            } else {\n                &templates[template_indices[0]]\n            };\n\n            return Ok(TemplateConfig {\n                project_name,\n                project_path,\n                template_type: TemplateType::Builtin,\n                server_lang: parse_server_lang(&template.server_lang)?,\n                client_lang: parse_client_lang(&template.client_lang)?,\n                github_repo: None,\n                template_def: Some(template.clone()),\n                use_local: true,\n            });\n        } else if client_selection == github_clone_index {\n            return loop {\n                let repo_input = Input::<String>::with_theme(&theme)\n                    .with_prompt(\"GitHub repository (owner/repo) or git URL\")\n                    .interact_text()?\n                    .trim()\n                    .to_string();\n                if repo_input.is_empty() {\n                    eprintln!(\"{}\", \"Please enter a GitHub repository.\".bold());\n                    continue;\n                }\n                break create_template_config_from_template_str(\n                    project_name.clone(),\n                    project_path.clone(),\n                    &repo_input,\n                    &templates,\n                );\n            };\n        } else if client_selection == none_index {\n            // Ask for server language only\n            let server_lang_choices = vec![\"Rust\", \"C#\", \"TypeScript\"];\n            let server_selection = Select::with_theme(&theme)\n                .with_prompt(\"Select server language\")\n                .items(&server_lang_choices)\n                .default(0)\n                .interact()?;\n\n            let server_lang = match server_selection {\n                0 => Some(ServerLanguage::Rust),\n                1 => Some(ServerLanguage::Csharp),\n                2 => Some(ServerLanguage::TypeScript),\n                _ => unreachable!(\"Invalid server language selection\"),\n            };\n\n            return Ok(TemplateConfig {\n                project_name,\n                project_path,\n                template_type: TemplateType::Empty,\n                server_lang,\n                client_lang: None,\n                github_repo: None,\n                template_def: None,\n                use_local: true,\n            });\n        } else {\n            unreachable!(\"Invalid selection index\");\n        }\n    }\n}\n\nfn format_language_label(lang: Option<&str>) -> String {\n    match lang {\n        Some(value) if value.eq_ignore_ascii_case(\"rust\") => \"Rust\".to_string(),\n        Some(value) if value.eq_ignore_ascii_case(\"csharp\") || value.eq_ignore_ascii_case(\"c#\") => \"C#\".to_string(),\n        Some(value) if value.eq_ignore_ascii_case(\"typescript\") => \"TypeScript\".to_string(),\n        Some(value) if value.eq_ignore_ascii_case(\"cpp\") || value.eq_ignore_ascii_case(\"c++\") => \"C++\".to_string(),\n        Some(value) if !value.trim().is_empty() => value.to_string(),\n        _ => \"None\".to_string(),\n    }\n}\n\nfn clone_github_template(repo_input: &str, target: &Path, is_server_only: bool) -> anyhow::Result<()> {\n    let is_git_url = |s: &str| {\n        s.starts_with(\"git@\") || s.starts_with(\"ssh://\") || s.starts_with(\"http://\") || s.starts_with(\"https://\")\n    };\n\n    let repo_url = if is_git_url(repo_input) {\n        repo_input.to_string()\n    } else if repo_input.contains('/') {\n        format!(\"https://github.com/{}\", repo_input)\n    } else {\n        anyhow::bail!(\"Invalid repository format. Use 'owner/repo' or full git clone URL\");\n    };\n\n    println!(\"  Cloning from {}...\", repo_url);\n\n    let temp_dir = tempfile::tempdir()?;\n\n    let mut builder = git2::build::RepoBuilder::new();\n\n    let mut fetch_options = git2::FetchOptions::new();\n    let mut callbacks = git2::RemoteCallbacks::new();\n\n    callbacks.credentials(|_url, username_from_url, allowed_types| {\n        if allowed_types.contains(git2::CredentialType::SSH_KEY)\n            && let Some(username) = username_from_url\n        {\n            return git2::Cred::ssh_key_from_agent(username);\n        }\n        if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {\n            return git2::Cred::userpass_plaintext(\"\", \"\");\n        }\n        if allowed_types.contains(git2::CredentialType::DEFAULT) {\n            return git2::Cred::default();\n        }\n        Err(git2::Error::from_str(\"no auth method available\"))\n    });\n\n    fetch_options.remote_callbacks(callbacks);\n    builder.fetch_options(fetch_options);\n\n    builder\n        .clone(&repo_url, temp_dir.path())\n        .context(\"Failed to clone repository\")?;\n\n    if is_server_only {\n        let server_subdir = temp_dir.path().join(\"spacetimedb\");\n        let server_subdir_target = target.join(\"spacetimedb\");\n        copy_dir_all(&server_subdir, &server_subdir_target)?;\n    } else {\n        copy_dir_all(temp_dir.path(), target)?;\n    }\n\n    Ok(())\n}\n\nfn copy_dir_all(src: &Path, dst: &Path) -> anyhow::Result<()> {\n    fs::create_dir_all(dst)?;\n    for entry in fs::read_dir(src)? {\n        let entry = entry?;\n        let ty = entry.file_type()?;\n        let src_path = entry.path();\n        let dst_path = dst.join(entry.file_name());\n\n        if entry.file_name() == \".git\" {\n            continue;\n        }\n\n        if ty.is_dir() {\n            copy_dir_all(&src_path, &dst_path)?;\n        } else {\n            fs::copy(&src_path, &dst_path)?;\n        }\n    }\n    Ok(())\n}\n\nfn get_spacetimedb_typescript_version() -> &'static str {\n    embedded::get_typescript_bindings_version()\n}\n\nfn update_package_json(dir: &Path, package_name: &str) -> anyhow::Result<()> {\n    let package_path = dir.join(\"package.json\");\n    if !package_path.exists() {\n        return Ok(());\n    }\n\n    let content = fs::read_to_string(&package_path)?;\n    let mut package: serde_json::Value = serde_json::from_str(&content)?;\n\n    package[\"name\"] = json!(package_name);\n\n    // Update spacetimedb version if it exists in dependencies\n    if let Some(deps) = package.get_mut(\"dependencies\")\n        && deps.get(\"spacetimedb\").is_some()\n    {\n        deps[\"spacetimedb\"] = json!(format!(\"^{}\", get_spacetimedb_typescript_version()));\n    }\n\n    let updated_content = serde_json::to_string_pretty(&package)?;\n    fs::write(package_path, updated_content)?;\n\n    Ok(())\n}\n\nfn to_patch_wildcard(ver: &str) -> String {\n    let mut parts: Vec<&str> = ver.split('.').collect();\n    if parts.len() >= 3 {\n        parts[2] = \"*\";\n    }\n    parts.join(\".\")\n}\n\nfn update_cargo_toml_name(dir: &Path, package_name: &str) -> anyhow::Result<()> {\n    let version = env!(\"CARGO_PKG_VERSION\");\n    let patch_wildcard = to_patch_wildcard(version);\n    let cargo_path = dir.join(\"Cargo.toml\");\n    if !cargo_path.exists() {\n        return Ok(());\n    }\n\n    let original = fs::read_to_string(&cargo_path)?;\n    let mut doc: DocumentMut = original.parse()?;\n\n    let safe_name = package_name\n        .chars()\n        .map(|c| if c.is_alphanumeric() || c == '_' { c } else { '_' })\n        .collect::<String>();\n\n    if let Some(package_item) = doc.get_mut(\"package\")\n        && let Some(package_table) = package_item.as_table_mut()\n    {\n        package_table[\"name\"] = value(safe_name);\n        if let Some(edition_item) = package_table.get_mut(\"edition\")\n            && edition_uses_workspace(edition_item)\n        {\n            *edition_item = value(embedded::get_workspace_edition());\n        }\n    }\n\n    if let Some(deps_item) = doc.get_mut(\"dependencies\")\n        && let Some(deps_table) = deps_item.as_table_mut()\n    {\n        let keys: Vec<String> = deps_table.iter().map(|(k, _)| k.to_string()).collect();\n        for key in keys {\n            if let Some(dep_item) = deps_table.get_mut(&key)\n                && dependency_uses_workspace(dep_item)\n            {\n                if has_path(dep_item) {\n                    if key == \"spacetimedb\" {\n                        if let Some(version) = embedded::get_workspace_dependency_version(&key) {\n                            set_dependency_version(dep_item, version, true);\n                        }\n                    } else if key == \"spacetimedb-sdk\" {\n                        set_dependency_version(dep_item, patch_wildcard.as_str(), true);\n                    }\n                    continue;\n                }\n\n                if uses_workspace(dep_item)\n                    && let Some(version) = embedded::get_workspace_dependency_version(&key)\n                {\n                    set_dependency_version(dep_item, version, key == \"spacetimedb\");\n                }\n            }\n        }\n    }\n\n    let updated = doc.to_string();\n    if updated != original {\n        fs::write(cargo_path, updated)?;\n    }\n    Ok(())\n}\n\npub fn update_csproj_server_to_nuget(dir: &Path) -> anyhow::Result<()> {\n    if let Some(csproj_path) = find_first_csproj(dir)? {\n        let original =\n            fs::read_to_string(&csproj_path).with_context(|| format!(\"reading {}\", csproj_path.display()))?;\n        let mut root: Element =\n            Element::parse(original.as_bytes()).with_context(|| format!(\"parsing xml {}\", csproj_path.display()))?;\n\n        upsert_packageref(\n            &mut root,\n            \"SpacetimeDB.Runtime\",\n            &get_spacetimedb_csharp_runtime_version(),\n        );\n        remove_all_project_references(&mut root);\n\n        write_if_changed(csproj_path, original, root)?;\n    }\n    Ok(())\n}\n\npub fn update_csproj_client_to_nuget(dir: &Path) -> anyhow::Result<()> {\n    if let Some(csproj_path) = find_first_csproj(dir)? {\n        let original =\n            fs::read_to_string(&csproj_path).with_context(|| format!(\"reading {}\", csproj_path.display()))?;\n        let mut root: Element =\n            Element::parse(original.as_bytes()).with_context(|| format!(\"parsing xml {}\", csproj_path.display()))?;\n\n        upsert_packageref(\n            &mut root,\n            \"SpacetimeDB.ClientSDK\",\n            &get_spacetimedb_csharp_clientsdk_version(),\n        );\n        remove_all_project_references(&mut root);\n\n        write_if_changed(csproj_path, original, root)?;\n    }\n    Ok(())\n}\n\n// Helpers\n\nfn write_if_changed(path: PathBuf, original: String, root: Element) -> anyhow::Result<()> {\n    let mut out = Vec::new();\n    root.write(&mut out)?;\n    let compact = String::from_utf8(out)?;\n    let updated = pretty_format_xml(&compact)?;\n    if updated != original {\n        fs::write(&path, updated).with_context(|| format!(\"writing {}\", path.display()))?;\n    }\n    Ok(())\n}\n\nfn find_first_csproj(dir: &Path) -> anyhow::Result<Option<PathBuf>> {\n    if !dir.is_dir() {\n        return Ok(None);\n    }\n    for entry in fs::read_dir(dir)? {\n        let p = entry?.path();\n        if p.extension().map(|e| e == \"csproj\").unwrap_or(false) {\n            return Ok(Some(p));\n        }\n    }\n    Ok(None)\n}\n\n/// Remove every <ProjectReference/> under any <ItemGroup>\nfn remove_all_project_references(project: &mut Element) {\n    for node in project.children.iter_mut() {\n        if let XMLNode::Element(item_group) = node\n            && item_group.name == \"ItemGroup\"\n        {\n            item_group\n                .children\n                .retain(|n| !matches!(n, XMLNode::Element(el) if el.name == \"ProjectReference\"));\n        }\n    }\n    // Optional: prune empty ItemGroups\n    project.children.retain(|n| {\n        if let XMLNode::Element(el) = n\n            && el.name == \"ItemGroup\"\n        {\n            return el.children.iter().any(|c| matches!(c, XMLNode::Element(_)));\n        }\n        true\n    });\n}\n\n/// Insert or update <PackageReference Include=\"...\" Version=\"...\"/>\nfn upsert_packageref(project: &mut Element, include: &str, version: &str) {\n    // Try to find an existing PackageReference\n    for node in project.children.iter_mut() {\n        if let XMLNode::Element(item_group) = node\n            && item_group.name == \"ItemGroup\"\n            && let Some(XMLNode::Element(existing)) = item_group.children.iter_mut().find(|n| {\n                matches!(n,\n                    XMLNode::Element(e)\n                    if e.name == \"PackageReference\"\n                       && e.attributes.get(\"Include\").map(|v| v == include).unwrap_or(false)\n                )\n            })\n        {\n            existing.attributes.insert(\"Version\".to_string(), version.to_string());\n            return;\n        }\n    }\n    // Otherwise create one in (or create) an ItemGroup\n    let item_group = get_or_create_direct_child(project, \"ItemGroup\");\n    let mut pr = Element::new(\"PackageReference\");\n    pr.attributes.insert(\"Include\".into(), include.to_string());\n    pr.attributes.insert(\"Version\".into(), version.to_string());\n    item_group.children.push(XMLNode::Element(pr));\n}\n\nfn get_or_create_direct_child<'a>(parent: &'a mut Element, name: &str) -> &'a mut Element {\n    // First, scan IMMUTABLY to find the index of an existing child.\n    if let Some(idx) = parent.children.iter().enumerate().find_map(|(i, n)| match n {\n        XMLNode::Element(e) if e.name == name => Some(i),\n        _ => None,\n    }) {\n        // Now borrow MUTABLY by index.\n        if let XMLNode::Element(el) = &mut parent.children[idx] {\n            return el;\n        }\n        unreachable!(\"Matched non-element while checking by name\");\n    }\n\n    // Not found: create, then borrow by index.\n    parent.children.push(XMLNode::Element(Element::new(name)));\n    let idx = parent.children.len() - 1;\n    match &mut parent.children[idx] {\n        XMLNode::Element(el) => el,\n        _ => unreachable!(\"just pushed an Element\"),\n    }\n}\n\n/// Pretty-print XML with indentation.\n/// Keeps UTF-8 declaration if present.\nfn pretty_format_xml(xml: &str) -> anyhow::Result<String> {\n    use quick_xml::events::Event;\n    use quick_xml::Reader;\n    use quick_xml::Writer;\n    use std::io::Cursor;\n\n    let mut reader = Reader::from_str(xml);\n    reader.trim_text(true);\n    let mut buf = Vec::new();\n    let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 2);\n\n    loop {\n        match reader.read_event_into(&mut buf)? {\n            Event::Eof => break,\n            e => writer.write_event(e)?,\n        }\n        buf.clear();\n    }\n\n    let result = writer.into_inner().into_inner();\n    Ok(String::from_utf8(result)?)\n}\n\n/// Just do 2.* for now\nfn get_spacetimedb_csharp_runtime_version() -> String {\n    \"2.*\".to_string()\n}\n\nfn get_spacetimedb_csharp_clientsdk_version() -> String {\n    \"2.*\".to_string()\n}\n\n/// Writes a `.env.local` file that includes all common\n/// frontend environment variable variants for SpacetimeDB.\nfn write_typescript_client_env_file(client_dir: &Path, module_name: &str, use_local: bool) -> anyhow::Result<()> {\n    let env_path = client_dir.join(\".env.local\");\n\n    let db_name = module_name;\n    let host = if use_local {\n        \"ws://localhost:3000\"\n    } else {\n        \"wss://maincloud.spacetimedb.com\"\n    };\n\n    // Framework-agnostic variants\n    let env_content = format!(\n        \"\\\n# Generic / backend\nSPACETIMEDB_DB_NAME={db_name}\nSPACETIMEDB_HOST={host}\n\n# Vite\nVITE_SPACETIMEDB_DB_NAME={db_name}\nVITE_SPACETIMEDB_HOST={host}\n\n# Next.js\nNEXT_PUBLIC_SPACETIMEDB_DB_NAME={db_name}\nNEXT_PUBLIC_SPACETIMEDB_HOST={host}\n\n# Create React App\nREACT_APP_SPACETIMEDB_DB_NAME={db_name}\nREACT_APP_SPACETIMEDB_HOST={host}\n\n# Expo\nEXPO_PUBLIC_SPACETIMEDB_DB_NAME={db_name}\nEXPO_PUBLIC_SPACETIMEDB_HOST={host}\n\n# SvelteKit\nPUBLIC_SPACETIMEDB_DB_NAME={db_name}\nPUBLIC_SPACETIMEDB_HOST={host}\n\"\n    );\n\n    fs::write(&env_path, env_content)?;\n\n    println!(\"✅ Wrote environment configuration to {}\", env_path.display());\n    Ok(())\n}\n\npub async fn init_from_template(\n    config: &TemplateConfig,\n    project_path: &Path,\n    is_server_only: bool,\n) -> anyhow::Result<()> {\n    println!(\"{}\", \"Initializing project from template...\".cyan());\n\n    match config.template_type {\n        TemplateType::Builtin => init_builtin(config, project_path, is_server_only)?,\n        TemplateType::GitHub => init_github_template(config, project_path, is_server_only)?,\n        TemplateType::Empty => init_empty(config, project_path)?,\n    }\n\n    // Install AI assistant rules for multiple editors/tools\n    install_ai_rules(config, project_path)?;\n\n    println!(\"{}\", \"Project initialized successfully!\".green());\n\n    Ok(())\n}\n\nfn init_builtin(config: &TemplateConfig, project_path: &Path, is_server_only: bool) -> anyhow::Result<()> {\n    let template_def = config\n        .template_def\n        .as_ref()\n        .ok_or_else(|| anyhow::anyhow!(\"Template definition missing\"))?;\n\n    let template_files = embedded::get_template_files();\n\n    if !is_server_only {\n        println!(\n            \"Setting up client ({})...\",\n            config.client_lang.map(|l| l.as_str()).unwrap_or(\"none\")\n        );\n        let client_source = &template_def.client_source;\n        if let Some(files) = template_files.get(client_source.as_str()) {\n            copy_embedded_files(files, project_path)?;\n        } else {\n            anyhow::bail!(\"Client template not found: {}\", client_source);\n        }\n\n        // Update client name\n        match config.client_lang {\n            Some(ClientLanguage::TypeScript) => {\n                update_package_json(project_path, &config.project_name)?;\n                write_typescript_client_env_file(project_path, &config.project_name, config.use_local)?;\n                println!(\n                    \"{}\",\n                    \"Note: Run 'npm install' in the project directory to install dependencies\".yellow()\n                );\n            }\n            Some(ClientLanguage::Rust) => {\n                update_cargo_toml_name(project_path, &config.project_name)?;\n            }\n            Some(ClientLanguage::Csharp) => {\n                update_csproj_client_to_nuget(project_path)?;\n            }\n            None => {}\n        }\n    }\n\n    println!(\n        \"Setting up server ({})...\",\n        config.server_lang.map(|l| l.as_str()).unwrap_or(\"none\")\n    );\n    let server_dir = project_path.join(\"spacetimedb\");\n    let server_source = &template_def.server_source;\n    if let Some(files) = template_files.get(server_source.as_str()) {\n        copy_embedded_files(files, &server_dir)?;\n    } else {\n        anyhow::bail!(\"Server template not found: {}\", server_source);\n    }\n\n    // Update server name\n    match config.server_lang {\n        Some(ServerLanguage::TypeScript) => {\n            update_package_json(&server_dir, &config.project_name)?;\n        }\n        Some(ServerLanguage::Rust) => {\n            update_cargo_toml_name(&server_dir, &config.project_name)?;\n        }\n        Some(ServerLanguage::Csharp) => {\n            update_csproj_server_to_nuget(&server_dir)?;\n        }\n        Some(ServerLanguage::Cpp) => {\n            // No name update needed for C++ at the moment\n        }\n        None => {}\n    }\n\n    Ok(())\n}\n\nfn copy_embedded_files(files: &HashMap<&str, &str>, target_dir: &Path) -> anyhow::Result<()> {\n    for (file_path, content) in files {\n        // Skip .template.json files - they're only for template metadata\n        if file_path.ends_with(\".template.json\") {\n            continue;\n        }\n\n        let full_path = target_dir.join(file_path);\n        if let Some(parent) = full_path.parent() {\n            fs::create_dir_all(parent)?;\n        }\n        fs::write(&full_path, content)?;\n    }\n    Ok(())\n}\n\nfn init_github_template(config: &TemplateConfig, project_path: &Path, is_server_only: bool) -> anyhow::Result<()> {\n    let repo = config.github_repo.as_ref().unwrap();\n    clone_github_template(repo, project_path, is_server_only)?;\n\n    let package_path = project_path.join(\"package.json\");\n    if package_path.exists() {\n        let content = fs::read_to_string(&package_path)?;\n        let mut package: serde_json::Value = serde_json::from_str(&content)?;\n        package[\"name\"] = json!(config.project_name.clone());\n        let updated_content = serde_json::to_string_pretty(&package)?;\n        fs::write(package_path, updated_content)?;\n    }\n\n    println!(\"{}\", \"Note: Custom templates require manual configuration.\".yellow());\n\n    Ok(())\n}\n\nfn init_empty(config: &TemplateConfig, project_path: &Path) -> anyhow::Result<()> {\n    match config.server_lang {\n        Some(ServerLanguage::Rust) => {\n            println!(\"Setting up Rust server...\");\n            let server_dir = project_path.join(\"spacetimedb\");\n            init_empty_rust_server(&server_dir, &config.project_name)?;\n        }\n        Some(ServerLanguage::Csharp) => {\n            println!(\"Setting up C# server...\");\n            let server_dir = project_path.join(\"spacetimedb\");\n            init_empty_csharp_server(&server_dir, &config.project_name)?;\n        }\n        Some(ServerLanguage::TypeScript) => {\n            println!(\"Setting up TypeScript server...\");\n            let server_dir = project_path.join(\"spacetimedb\");\n            init_empty_typescript_server(&server_dir, &config.project_name)?;\n        }\n        Some(ServerLanguage::Cpp) => {\n            println!(\"Setting up C++ server...\");\n            let server_dir = project_path.join(\"spacetimedb\");\n            init_empty_cpp_server(&server_dir, &config.project_name)?;\n        }\n        None => {}\n    }\n\n    Ok(())\n}\n\nfn init_empty_rust_server(server_dir: &Path, project_name: &str) -> anyhow::Result<()> {\n    init_rust_project(server_dir)?;\n    update_cargo_toml_name(server_dir, project_name)?;\n    Ok(())\n}\n\nfn init_empty_csharp_server(server_dir: &Path, _project_name: &str) -> anyhow::Result<()> {\n    init_csharp_project(server_dir)\n}\n\nfn init_empty_typescript_server(server_dir: &Path, project_name: &str) -> anyhow::Result<()> {\n    init_typescript_project(server_dir)?;\n    update_package_json(server_dir, project_name)?;\n    Ok(())\n}\n\nfn init_empty_cpp_server(server_dir: &Path, _project_name: &str) -> anyhow::Result<()> {\n    init_cpp_project(server_dir)\n}\n\nfn print_next_steps(config: &TemplateConfig, _project_path: &Path) -> anyhow::Result<()> {\n    println!();\n    println!(\"{}\", \"Next steps:\".bold());\n\n    let rel_path = config\n        .project_path\n        .strip_prefix(std::env::current_dir()?)\n        .unwrap_or(&config.project_path);\n\n    if rel_path != Path::new(\".\") && rel_path != Path::new(\"\") {\n        println!(\"  cd {}\", rel_path.display());\n    }\n\n    match (config.template_type, config.server_lang, config.client_lang) {\n        (TemplateType::Builtin, Some(ServerLanguage::Rust), Some(ClientLanguage::Rust)) => {\n            println!(\n                \"  spacetime publish --module-path spacetimedb {}{}\",\n                if config.use_local { \"--server local \" } else { \"\" },\n                config.project_name\n            );\n            println!(\"  spacetime generate --lang rust --out-dir src/module_bindings --module-path spacetimedb\");\n            println!(\"  cargo run\");\n        }\n        (TemplateType::Builtin, Some(ServerLanguage::TypeScript), Some(ClientLanguage::TypeScript)) => {\n            println!(\"  npm install\");\n            println!(\n                \"  spacetime publish --module-path spacetimedb {}{}\",\n                if config.use_local { \"--server local \" } else { \"\" },\n                config.project_name\n            );\n            println!(\"  spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb\");\n            println!(\"  npm run dev\");\n        }\n        (TemplateType::Builtin, Some(ServerLanguage::Csharp), Some(ClientLanguage::Csharp)) => {\n            println!(\n                \"  spacetime publish --module-path spacetimedb {}{}\",\n                if config.use_local { \"--server local \" } else { \"\" },\n                config.project_name\n            );\n            println!(\"  spacetime generate --lang csharp --out-dir module_bindings --module-path spacetimedb\");\n        }\n        (TemplateType::Empty, _, Some(ClientLanguage::TypeScript)) => {\n            println!(\"  npm install\");\n            if config.server_lang.is_some() {\n                println!(\n                    \"  spacetime publish --module-path spacetimedb {}{}\",\n                    if config.use_local { \"--server local \" } else { \"\" },\n                    config.project_name\n                );\n                println!(\n                    \"  spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb\"\n                );\n            }\n            println!(\"  npm run dev\");\n        }\n        (TemplateType::Empty, _, Some(ClientLanguage::Rust)) => {\n            if config.server_lang.is_some() {\n                println!(\n                    \"  spacetime publish --module-path spacetimedb {}{}\",\n                    if config.use_local { \"--server local \" } else { \"\" },\n                    config.project_name\n                );\n                println!(\"  spacetime generate --lang rust --out-dir src/module_bindings --module-path spacetimedb\");\n            }\n            println!(\"  cargo run\");\n        }\n        (_, _, _) => {\n            println!(\"  # Follow the template's README for setup instructions\");\n        }\n    }\n\n    println!();\n    println!(\"Learn more: {}\", \"https://spacetimedb.com/docs\".cyan());\n\n    Ok(())\n}\n\nfn check_for_cargo() -> bool {\n    match std::env::consts::OS {\n        \"linux\" | \"freebsd\" | \"netbsd\" | \"openbsd\" | \"solaris\" | \"macos\" => {\n            if find_executable(\"cargo\").is_some() {\n                return true;\n            }\n            println!(\"{}\", \"Warning: You have created a rust project, but you are missing cargo. You should install cargo with the following command:\\n\\n\\tcurl https://sh.rustup.rs -sSf | sh\\n\".yellow());\n        }\n        \"windows\" => {\n            if find_executable(\"cargo.exe\").is_some() {\n                return true;\n            }\n            println!(\"{}\", \"Warning: You have created a rust project, but you are missing `cargo`. Visit https://www.rust-lang.org/tools/install for installation instructions:\\n\\n\\tYou have created a rust project, but you are missing cargo.\\n\".yellow());\n        }\n        unsupported_os => {\n            println!(\"{}\", format!(\"This OS may be unsupported: {unsupported_os}\").yellow());\n        }\n    }\n    false\n}\n\nfn check_for_dotnet() -> bool {\n    use std::fmt::Write;\n\n    let subpage = match std::env::consts::OS {\n        \"windows\" => {\n            if find_executable(\"dotnet.exe\").is_some() {\n                return true;\n            }\n            Some(\"windows\")\n        }\n        os => {\n            if find_executable(\"dotnet\").is_some() {\n                return true;\n            }\n            match os {\n                \"linux\" | \"macos\" => Some(os),\n                // can't give any hint for those other OS\n                _ => None,\n            }\n        }\n    };\n    let mut msg = \"Warning: You have created a C# project, but you are missing dotnet CLI.\".to_owned();\n    if let Some(subpage) = subpage {\n        write!(\n            msg,\n            \" Check out https://docs.microsoft.com/en-us/dotnet/core/install/{subpage}/ for installation instructions.\"\n        )\n        .unwrap();\n    }\n    println!(\"{}\", msg.yellow());\n    false\n}\n\nfn check_for_git() -> bool {\n    match std::env::consts::OS {\n        \"linux\" | \"freebsd\" | \"netbsd\" | \"openbsd\" | \"solaris\" => {\n            if find_executable(\"git\").is_some() {\n                return true;\n            }\n            println!(\n                \"{}\",\n                \"Warning: Git is not installed. You should install git using your package manager.\\n\".yellow()\n            );\n        }\n        \"macos\" => {\n            if find_executable(\"git\").is_some() {\n                return true;\n            }\n            println!(\n                \"{}\",\n                \"Warning: Git is not installed. You can install git by invoking:\\n\\n\\tgit --version\\n\".yellow()\n            );\n        }\n        \"windows\" => {\n            if find_executable(\"git.exe\").is_some() {\n                return true;\n            }\n\n            println!(\"{}\", \"Warning: You are missing git. You should install git from here:\\n\\n\\thttps://git-scm.com/download/win\\n\".yellow());\n        }\n        unsupported_os => {\n            println!(\"{}\", format!(\"This OS may be unsupported: {unsupported_os}\").yellow());\n        }\n    }\n    false\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> anyhow::Result<PathBuf> {\n    let options = InitOptions::from_args(args);\n    let is_interactive = !options.non_interactive;\n    let template = options.template.as_ref();\n    let server_lang = options.lang.as_ref();\n    let project_name_arg = options.project_name.as_ref();\n\n    // Validate that template and lang options are not used together\n    if template.is_some() && server_lang.is_some() {\n        anyhow::bail!(\"Cannot specify both --template and --lang. Language is determined by the template.\");\n    }\n\n    if !is_interactive {\n        // In non-interactive mode, validate all required args are present\n        if project_name_arg.is_none() {\n            anyhow::bail!(\"PROJECT_NAME is required in non-interactive mode\");\n        }\n        if template.is_none() && server_lang.is_none() {\n            anyhow::bail!(\"Either --template or --lang must be provided in non-interactive mode\");\n        }\n    }\n\n    exec_with_options(&mut config, &options).await\n}\n\npub fn init_rust_project(project_path: &Path) -> anyhow::Result<()> {\n    let export_files = vec![\n        (\n            include_str!(\"../../../../templates/basic-rs/spacetimedb/Cargo.toml\"),\n            \"Cargo.toml\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-rs/spacetimedb/src/lib.rs\"),\n            \"src/lib.rs\",\n        ),\n    ];\n\n    for data_file in export_files {\n        let path = project_path.join(data_file.1);\n        create_directory(path.parent().unwrap())?;\n        std::fs::write(path, data_file.0)?;\n    }\n\n    check_for_cargo();\n    check_for_git();\n\n    Ok(())\n}\n\npub fn init_csharp_project(project_path: &Path) -> anyhow::Result<()> {\n    let export_files = vec![\n        (\n            include_str!(\"../../../../templates/basic-cs/spacetimedb/StdbModule.csproj\"),\n            \"StdbModule.csproj\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-cs/spacetimedb/Lib.cs\"),\n            \"Lib.cs\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-cs/spacetimedb/global.json\"),\n            \"global.json\",\n        ),\n    ];\n\n    check_for_dotnet();\n    check_for_git();\n\n    for data_file in export_files {\n        let path = project_path.join(data_file.1);\n        create_directory(path.parent().unwrap())?;\n        std::fs::write(path, data_file.0)?;\n    }\n\n    Ok(())\n}\n\npub fn init_typescript_project(project_path: &Path) -> anyhow::Result<()> {\n    let export_files = vec![\n        (\n            include_str!(\"../../../../templates/basic-ts/spacetimedb/package.json\"),\n            \"package.json\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-ts/spacetimedb/tsconfig.json\"),\n            \"tsconfig.json\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-ts/spacetimedb/src/index.ts\"),\n            \"src/index.ts\",\n        ),\n    ];\n\n    check_for_git();\n\n    for data_file in export_files {\n        let path = project_path.join(data_file.1);\n        create_directory(path.parent().unwrap())?;\n        std::fs::write(path, data_file.0)?;\n    }\n\n    Ok(())\n}\n\npub fn init_cpp_project(project_path: &Path) -> anyhow::Result<()> {\n    let export_files = vec![\n        (\n            include_str!(\"../../../../templates/basic-cpp/spacetimedb/CMakeLists.txt\"),\n            \"CMakeLists.txt\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-cpp/spacetimedb/src/lib.cpp\"),\n            \"src/lib.cpp\",\n        ),\n        (\n            include_str!(\"../../../../templates/basic-cpp/spacetimedb/.gitignore\"),\n            \".gitignore\",\n        ),\n    ];\n\n    for data_file in export_files {\n        let path = project_path.join(data_file.1);\n        create_directory(path.parent().unwrap())?;\n        std::fs::write(path, data_file.0)?;\n    }\n\n    check_for_emscripten_and_cmake();\n    check_for_git();\n\n    Ok(())\n}\n\npub async fn exec_init_rust(args: &ArgMatches) -> anyhow::Result<()> {\n    let project_path = args.get_one::<PathBuf>(\"project-path\").unwrap();\n    init_rust_project(project_path)?;\n\n    println!(\n        \"{}\",\n        format!(\"Project successfully created at path: {}\", project_path.display()).green()\n    );\n\n    Ok(())\n}\n\npub async fn exec_init_csharp(args: &ArgMatches) -> anyhow::Result<()> {\n    let project_path = args.get_one::<PathBuf>(\"project-path\").unwrap();\n    init_csharp_project(project_path)?;\n\n    println!(\n        \"{}\",\n        format!(\"Project successfully created at path: {}\", project_path.display()).green()\n    );\n\n    Ok(())\n}\n\nfn create_directory(path: &Path) -> anyhow::Result<()> {\n    std::fs::create_dir_all(path).context(\"Failed to create directory\")\n}\n\npub fn parse_server_lang(lang: &Option<String>) -> anyhow::Result<Option<ServerLanguage>> {\n    match lang.as_deref() {\n        Some(s) => Ok(ServerLanguage::from_str(s)?),\n        None => Ok(None),\n    }\n}\n\npub fn parse_client_lang(lang: &Option<String>) -> anyhow::Result<Option<ClientLanguage>> {\n    match lang.as_deref() {\n        Some(s) => Ok(ClientLanguage::from_str(s)?),\n        None => Ok(None),\n    }\n}\n\nfn edition_uses_workspace(item: &Item) -> bool {\n    match item {\n        Item::Value(val) => val\n            .as_inline_table()\n            .map(|table| table.get(\"workspace\").is_some())\n            .unwrap_or(false),\n        Item::Table(table) => table.get(\"workspace\").is_some(),\n        _ => false,\n    }\n}\n\nfn dependency_uses_workspace(item: &Item) -> bool {\n    uses_workspace(item) || has_path(item)\n}\n\nfn uses_workspace(item: &Item) -> bool {\n    match item {\n        Item::Value(val) => val\n            .as_inline_table()\n            .map(|table| table.get(\"workspace\").is_some())\n            .unwrap_or(false),\n        Item::Table(table) => table.get(\"workspace\").is_some(),\n        _ => false,\n    }\n}\n\nfn has_path(item: &Item) -> bool {\n    match item {\n        Item::Value(val) => val\n            .as_inline_table()\n            .map(|table| table.get(\"path\").is_some())\n            .unwrap_or(false),\n        Item::Table(table) => table.get(\"path\").is_some(),\n        _ => false,\n    }\n}\n\nfn set_dependency_version(item: &mut Item, version: &str, remove_path: bool) {\n    if let Item::Value(val) = item\n        && let Some(inline) = val.as_inline_table_mut()\n    {\n        inline.remove(\"workspace\");\n        if remove_path {\n            inline.remove(\"path\");\n        }\n        inline.insert(\"version\", toml_edit::Value::from(version.to_string()));\n        return;\n    }\n\n    if let Item::Table(table) = item {\n        table.remove(\"workspace\");\n        if remove_path {\n            table.remove(\"path\");\n        }\n        table[\"version\"] = value(version.to_string());\n        return;\n    }\n\n    *item = value(version.to_string());\n}\n\n/// Install AI assistant rules for multiple editors/tools.\n/// Writes rules to:\n/// - .cursor/rules/ (Cursor)\n/// - CLAUDE.md (Claude Code)\n/// - AGENTS.md (Opencode)\n/// - .windsurfrules (Windsurf)\n/// - .github/copilot-instructions.md (VS Code Copilot)\nfn install_ai_rules(config: &TemplateConfig, project_path: &Path) -> anyhow::Result<()> {\n    let base_rules = embedded::get_ai_rules_base();\n    let ts_rules = embedded::get_ai_rules_typescript();\n    let rust_rules = embedded::get_ai_rules_rust();\n    let csharp_rules = embedded::get_ai_rules_csharp();\n\n    // Check which languages are used in server or client\n    let uses_typescript = config.server_lang == Some(ServerLanguage::TypeScript)\n        || config.client_lang == Some(ClientLanguage::TypeScript);\n    let uses_rust =\n        config.server_lang == Some(ServerLanguage::Rust) || config.client_lang == Some(ClientLanguage::Rust);\n    let uses_csharp =\n        config.server_lang == Some(ServerLanguage::Csharp) || config.client_lang == Some(ClientLanguage::Csharp);\n\n    // 1. Cursor: .cursor/rules/ directory with separate files\n    let cursor_dir = project_path.join(\".cursor/rules\");\n    fs::create_dir_all(&cursor_dir)?;\n    fs::write(cursor_dir.join(\"spacetimedb.mdc\"), base_rules)?;\n    if uses_typescript {\n        fs::write(cursor_dir.join(\"spacetimedb-typescript.mdc\"), ts_rules)?;\n    }\n    if uses_rust {\n        fs::write(cursor_dir.join(\"spacetimedb-rust.mdc\"), rust_rules)?;\n    }\n    if uses_csharp {\n        fs::write(cursor_dir.join(\"spacetimedb-csharp.mdc\"), csharp_rules)?;\n    }\n\n    // Build combined content for single-file AI assistants\n    // Strip the YAML frontmatter from the .mdc files for non-Cursor tools\n    let base_content = strip_mdc_frontmatter(base_rules);\n    let mut combined_content = base_content.to_string();\n\n    if uses_typescript {\n        let ts_content = strip_mdc_frontmatter(ts_rules);\n        combined_content.push_str(\"\\n\\n\");\n        combined_content.push_str(ts_content);\n    }\n    if uses_rust {\n        let rust_content = strip_mdc_frontmatter(rust_rules);\n        combined_content.push_str(\"\\n\\n\");\n        combined_content.push_str(rust_content);\n    }\n    if uses_csharp {\n        let csharp_content = strip_mdc_frontmatter(csharp_rules);\n        combined_content.push_str(\"\\n\\n\");\n        combined_content.push_str(csharp_content);\n    }\n\n    // 2. Claude Code: CLAUDE.md\n    fs::write(project_path.join(\"CLAUDE.md\"), &combined_content)?;\n\n    // 3. Opencode: AGENTS.md\n    fs::write(project_path.join(\"AGENTS.md\"), &combined_content)?;\n\n    // 4. Windsurf: .windsurfrules\n    fs::write(project_path.join(\".windsurfrules\"), &combined_content)?;\n\n    // 5. VS Code Copilot: .github/copilot-instructions.md\n    let github_dir = project_path.join(\".github\");\n    fs::create_dir_all(&github_dir)?;\n    fs::write(github_dir.join(\"copilot-instructions.md\"), &combined_content)?;\n\n    Ok(())\n}\n\n/// Strip YAML frontmatter from .mdc files (the --- delimited section at the start)\nfn strip_mdc_frontmatter(content: &str) -> &str {\n    // Look for frontmatter: starts with --- and ends with ---\n    if let Some(after_opening) = content.strip_prefix(\"---\")\n        && let Some(end_idx) = after_opening.find(\"\\n---\")\n    {\n        // Skip past the closing --- and the newline after it\n        let remaining = &after_opening[end_idx + 4..]; // 4 for \\n---\n                                                       // Skip any leading newlines after frontmatter\n        return remaining.trim_start_matches('\\n');\n    }\n    content\n}\n\n/// Check if Emscripten and CMake tooling are available in PATH.\n///\n/// On Windows, looks for emcc.bat and cmake.exe.\n/// On Unix-like systems, looks for emcc and cmake.\n///\n/// Returns true if both tools are found, false otherwise with helpful warnings.\nfn check_for_emscripten_and_cmake() -> bool {\n    // Check for emcc with platform-specific extension\n    #[cfg(windows)]\n    let emcc_ok = find_executable(\"emcc.bat\").is_some();\n    #[cfg(not(windows))]\n    let emcc_ok = find_executable(\"emcc\").is_some();\n\n    // Check for cmake with platform-specific extension\n    #[cfg(windows)]\n    let cmake_ok = find_executable(\"cmake.exe\").is_some();\n    #[cfg(not(windows))]\n    let cmake_ok = find_executable(\"cmake\").is_some();\n\n    if emcc_ok && cmake_ok {\n        return true;\n    }\n\n    // Print helpful warnings about missing tools\n    println!(\n        \"{}\",\n        \"Warning: You have created a C++ project, but you are missing emcc (Emscripten) or cmake.\".yellow()\n    );\n\n    if !emcc_ok {\n        println!(\n            \"{}\",\n            \"Install Emscripten from: https://emscripten.org/docs/getting_started/downloads.html\".yellow()\n        );\n        println!(\n            \"{}\",\n            \"Note: After installing, activate the environment with: emsdk_env.bat (Windows) or source emsdk_env.sh (Unix)\".yellow()\n        );\n    }\n\n    if !cmake_ok {\n        println!(\"{}\", \"Install CMake from: https://cmake.org/download/\".yellow());\n    }\n\n    false\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_create_default_spacetime_config_if_missing_creates_expected_config() {\n        let temp = tempfile::TempDir::new().unwrap();\n        let project_path = temp.path();\n        std::fs::create_dir_all(project_path.join(\"spacetimedb\")).unwrap();\n\n        let created = create_default_spacetime_config_if_missing(project_path)\n            .unwrap()\n            .expect(\"expected config to be created\");\n        assert_eq!(created, project_path.join(\"spacetime.json\"));\n\n        let content = std::fs::read_to_string(&created).unwrap();\n        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();\n        assert!(parsed.get(\"database\").is_none());\n        assert_eq!(parsed.get(\"server\").and_then(|v| v.as_str()), Some(\"maincloud\"));\n        assert_eq!(\n            parsed.get(\"module-path\").and_then(|v| v.as_str()),\n            Some(\"./spacetimedb\")\n        );\n    }\n\n    #[test]\n    fn test_create_local_spacetime_config_if_missing_creates_database_override() {\n        let temp = tempfile::TempDir::new().unwrap();\n        let project_path = temp.path();\n\n        std::fs::write(project_path.join(\"spacetime.json\"), \"{}\").unwrap();\n\n        let created = create_local_spacetime_config_if_missing(project_path, \"my-app-abc12\")\n            .unwrap()\n            .expect(\"expected local config to be created\");\n        assert_eq!(created, project_path.join(\"spacetime.local.json\"));\n\n        let content = std::fs::read_to_string(&created).unwrap();\n        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();\n        let db = parsed\n            .get(\"database\")\n            .and_then(|v| v.as_str())\n            .expect(\"database should be present\");\n\n        assert_eq!(db, \"my-app-abc12\");\n\n        let obj = parsed.as_object().expect(\"local config should be a JSON object\");\n        assert_eq!(obj.len(), 1, \"local config should only contain database\");\n    }\n\n    #[test]\n    fn test_get_local_database_name_uses_explicit_default_without_suffix() {\n        let options = InitOptions {\n            database_name_default: Some(\"my-explicit-db\".to_string()),\n            ..Default::default()\n        };\n\n        let db = get_local_database_name(&options, \"my-project\", false).unwrap();\n        assert_eq!(db, \"my-explicit-db\");\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/list.rs",
    "content": "use crate::common_args;\nuse crate::util;\nuse crate::util::get_login_token_or_log_in;\nuse crate::util::ResponseExt;\nuse crate::util::UNSTABLE_WARNING;\nuse crate::Config;\nuse anyhow::Context;\nuse clap::{ArgMatches, Command};\nuse serde::Deserialize;\nuse spacetimedb_lib::Identity;\nuse tabled::{\n    settings::{object::Columns, Alignment, Modify, Style},\n    Table, Tabled,\n};\n\npub fn cli() -> Command {\n    Command::new(\"list\")\n        .about(format!(\n            \"Lists the databases attached to an identity. {UNSTABLE_WARNING}\"\n        ))\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server from which to list databases\"))\n        .arg(common_args::yes())\n}\n\n#[derive(Deserialize)]\nstruct DatabasesResult {\n    pub identities: Vec<IdentityRow>,\n}\n\n#[derive(Tabled, Deserialize)]\n#[serde(transparent)]\nstruct IdentityRow {\n    pub db_identity: Identity,\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n\n    let server = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let force = args.get_flag(\"force\");\n    let token = get_login_token_or_log_in(&mut config, server, !force).await?;\n    let identity = util::decode_identity(&token)?;\n\n    let client = reqwest::Client::new();\n    let res = client\n        .get(format!(\n            \"{}/v1/identity/{}/databases\",\n            config.get_host_url(server)?,\n            identity\n        ))\n        .bearer_auth(token)\n        .send()\n        .await?;\n\n    let result: DatabasesResult = res\n        .json_or_error()\n        .await\n        .context(\"unable to retrieve databases for identity\")?;\n\n    if !result.identities.is_empty() {\n        let mut table = Table::new(result.identities);\n        table\n            .with(Style::psql())\n            .with(Modify::new(Columns::first()).with(Alignment::left()));\n        println!(\"Associated database identities for {identity}:\\n\");\n        println!(\"{table}\");\n    } else {\n        println!(\"No databases found for {identity}.\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/login.rs",
    "content": "use crate::Config;\nuse crate::{logout::ensure_logged_out, util::decode_identity};\nuse clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command};\nuse reqwest::Url;\nuse serde::Deserialize;\nuse webbrowser;\n\npub const DEFAULT_AUTH_HOST: &str = \"https://spacetimedb.com\";\n\npub fn cli() -> Command {\n    Command::new(\"login\")\n        .args_conflicts_with_subcommands(true)\n        .subcommands(get_subcommands())\n        .group(ArgGroup::new(\"login-method\").required(false))\n        .arg(\n            Arg::new(\"auth-host\")\n                .long(\"auth-host\")\n                .default_value(DEFAULT_AUTH_HOST)\n                .group(\"login-method\")\n                .help(\"Fetch login token from a different host\"),\n        )\n        .arg(\n            Arg::new(\"server\")\n                .long(\"server-issued-login\")\n                .group(\"login-method\")\n                .help(\"Log in to a SpacetimeDB server directly, without going through a global auth server\"),\n        )\n        .arg(\n            Arg::new(\"spacetimedb-token\")\n                .long(\"token\")\n                .group(\"login-method\")\n                .help(\"Bypass the login flow and use a login token directly\"),\n        )\n        .arg(\n            Arg::new(\"no-browser\")\n                .long(\"no-browser\")\n                .action(ArgAction::SetTrue)\n                .help(\"Do not open a browser window\"),\n        )\n        .about(\"Manage your login to the SpacetimeDB CLI\")\n}\n\nfn get_subcommands() -> Vec<Command> {\n    vec![Command::new(\"show\")\n        .arg(\n            Arg::new(\"token\")\n                .long(\"token\")\n                .action(ArgAction::SetTrue)\n                .help(\"Also show the auth token\"),\n        )\n        .about(\"Show the current login info\")]\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    if let Some((cmd, subcommand_args)) = args.subcommand() {\n        return exec_subcommand(config, cmd, subcommand_args).await;\n    }\n\n    let spacetimedb_token: Option<&String> = args.get_one(\"spacetimedb-token\");\n    let host: &String = args.get_one(\"auth-host\").unwrap();\n    let host = Url::parse(host)?;\n    let server_issued_login: Option<&String> = args.get_one(\"server\");\n    let open_browser = !args.get_flag(\"no-browser\");\n\n    let _was_logged_in = ensure_logged_out(&mut config, &host).await;\n\n    if let Some(token) = spacetimedb_token {\n        config.set_spacetimedb_token(token.clone());\n        config.save();\n        match decode_identity(token) {\n            Ok(identity) => println!(\"Logged in with identity {identity}\"),\n            Err(_) => println!(\"Token saved.\"),\n        }\n        return Ok(());\n    }\n\n    if let Some(server) = server_issued_login {\n        let host = Url::parse(&config.get_host_url(Some(server))?)?;\n        spacetimedb_login_and_save(&mut config, &host, true, open_browser).await?;\n    } else {\n        spacetimedb_login_and_save(&mut config, &host, false, open_browser).await?;\n    }\n\n    Ok(())\n}\n\nasync fn exec_subcommand(config: Config, cmd: &str, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    match cmd {\n        \"show\" => exec_show(config, args).await,\n        unknown => Err(anyhow::anyhow!(\"Invalid subcommand: {unknown}\")),\n    }\n}\n\nasync fn exec_show(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let include_token = args.get_flag(\"token\");\n\n    let token = if let Some(token) = config.spacetimedb_token() {\n        token\n    } else {\n        println!(\"You are not logged in. Run `spacetime login` to log in.\");\n        return Ok(());\n    };\n\n    let identity = decode_identity(token)?;\n    println!(\"You are logged in as {identity}\");\n\n    if include_token {\n        println!(\"Your auth token (don't share this!) is {token}\");\n    }\n\n    Ok(())\n}\n\npub async fn spacetimedb_login_and_save(\n    config: &mut Config,\n    host: &Url,\n    direct_login: bool,\n    open_browser: bool,\n) -> anyhow::Result<String> {\n    let token = if direct_login {\n        let token = spacetimedb_direct_login(host).await?;\n        println!(\"We have logged in directly to your target server.\");\n        println!(\"WARNING: This login will NOT work for any other servers.\");\n        token\n    } else {\n        let session_token = web_login_or_cached(config, host, open_browser).await?;\n        spacetimedb_login(host, &session_token).await?\n    };\n    config.set_spacetimedb_token(token.clone());\n    config.save();\n\n    let identity = decode_identity(&token)?;\n    println!(\"Logged in with identity {identity}\");\n\n    Ok(token)\n}\n\nasync fn web_login_or_cached(config: &mut Config, host: &Url, open_browser: bool) -> anyhow::Result<String> {\n    if let Some(session_token) = config.web_session_token() {\n        // Currently, these session tokens do not expire. At some point in the future, we may also need to check this session token for validity.\n        Ok(session_token.clone())\n    } else {\n        let session_token = web_login(host, open_browser).await?;\n        config.set_web_session_token(session_token.clone());\n        config.save();\n        Ok(session_token)\n    }\n}\n\n#[derive(Clone, Deserialize)]\nstruct WebLoginTokenData {\n    token: String,\n}\n\n#[derive(Clone, Deserialize)]\nstruct WebLoginTokenResponse {\n    success: bool,\n    data: WebLoginTokenData,\n}\n\n#[derive(Clone, Deserialize)]\nstruct WebLoginSessionResponse {\n    success: bool,\n    error: Option<String>,\n    data: Option<WebLoginSessionData>,\n}\n\n#[derive(Clone, Deserialize)]\nstruct WebLoginSessionData {\n    approved: bool,\n\n    #[serde(rename = \"sessionToken\")]\n    session_token: Option<String>,\n}\n\n#[derive(Clone, Deserialize)]\nstruct WebLoginSessionResponseApproved {\n    session_token: String,\n}\n\nimpl WebLoginSessionResponse {\n    fn approved(self) -> anyhow::Result<Option<WebLoginSessionResponseApproved>> {\n        if !self.success {\n            return Err(anyhow::anyhow!(self\n                .error\n                .clone()\n                .unwrap_or(\"Unknown error\".to_string())));\n        }\n\n        let data = self.data.ok_or(anyhow::anyhow!(\"Response data is missing.\"))?;\n        if !data.approved {\n            // Approved is false, no session token expected\n            return Ok(None);\n        }\n\n        let session_token = data\n            .session_token\n            .ok_or(anyhow::anyhow!(\"Session token is missing in response.\".to_string()))?;\n        Ok(Some(WebLoginSessionResponseApproved {\n            session_token: session_token.clone(),\n        }))\n    }\n}\n\nasync fn web_login(remote: &Url, open_browser: bool) -> Result<String, anyhow::Error> {\n    let client = reqwest::Client::new();\n\n    let response: WebLoginTokenResponse = client\n        .post(remote.join(\"/api/auth/cli/login/request-token\")?)\n        .send()\n        .await?\n        .json()\n        .await?;\n\n    if !response.success {\n        return Err(anyhow::anyhow!(\"Failed to request token\"));\n    }\n\n    let web_login_request_token = response.data.token.as_str();\n\n    let mut browser_url = remote.join(\"login/cli\")?;\n    browser_url\n        .query_pairs_mut()\n        .append_pair(\"token\", web_login_request_token);\n    if open_browser {\n        println!(\"Opening {browser_url} in your browser.\");\n        if webbrowser::open(browser_url.as_str()).is_err() {\n            println!(\"Unable to open your browser! Please open the URL above manually.\");\n        }\n    } else {\n        println!(\"Open {browser_url} in your browser to finish logging in.\");\n    }\n\n    println!(\"Waiting to hear response from the server...\");\n    loop {\n        tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n        let mut status_url = remote.join(\"api/auth/cli/status\")?;\n        status_url\n            .query_pairs_mut()\n            .append_pair(\"token\", web_login_request_token);\n        let response: WebLoginSessionResponse = client.get(status_url).send().await?.json().await?;\n        if let Some(approved) = response.approved()? {\n            println!(\"Login successful!\");\n            return Ok(approved.session_token.clone());\n        }\n    }\n}\n\n#[derive(Deserialize, Debug)]\nstruct SpacetimeDBTokenResponse {\n    success: bool,\n    error: Option<String>,\n    data: Option<SpacetimeDBTokenData>,\n}\n\n#[derive(Deserialize, Debug)]\nstruct SpacetimeDBTokenData {\n    token: String,\n}\n\nasync fn spacetimedb_login(remote: &Url, web_session_token: &String) -> Result<String, anyhow::Error> {\n    let client = reqwest::Client::new();\n\n    let response: SpacetimeDBTokenResponse = client\n        .post(remote.join(\"api/spacetimedb-token\")?)\n        .header(\"Authorization\", format!(\"Bearer {web_session_token}\"))\n        .send()\n        .await?\n        .json()\n        .await?;\n\n    if !response.success {\n        return Err(anyhow::anyhow!(\n            \"Failed to get token: {}\",\n            response.error.unwrap_or(\"Unknown error\".to_string())\n        ));\n    }\n    Ok(response.data.unwrap().token.clone())\n}\n\n#[derive(Debug, Clone, Deserialize)]\nstruct LocalLoginResponse {\n    pub token: String,\n}\n\nasync fn spacetimedb_direct_login(host: &Url) -> Result<String, anyhow::Error> {\n    let client = reqwest::Client::new();\n    let response: LocalLoginResponse = client\n        .post(host.join(\"/v1/identity\")?)\n        .send()\n        .await?\n        .error_for_status()?\n        .json()\n        .await?;\n    Ok(response.token)\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/logout.rs",
    "content": "use crate::util::decode_identity;\nuse crate::Config;\nuse clap::{Arg, ArgMatches, Command};\nuse reqwest::Url;\nuse std::time::Duration;\n\npub fn cli() -> Command {\n    Command::new(\"logout\").arg(\n        Arg::new(\"auth-host\")\n            .long(\"auth-host\")\n            .default_value(\"https://spacetimedb.com\")\n            .help(\"Log out from a custom auth server\"),\n    )\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    // Check if already logged out.\n    if config.spacetimedb_token().is_none() && config.web_session_token().is_none() {\n        println!(\"You are not logged in.\");\n        return Ok(());\n    }\n\n    let host: &String = args.get_one(\"auth-host\").unwrap();\n    let host = Url::parse(host)?;\n\n    let _ = ensure_logged_out(&mut config, &host).await;\n\n    Ok(())\n}\n\nasync fn server_logout(config: &mut Config, host: &Url) -> Result<(), anyhow::Error> {\n    let Some(web_session_token) = config.web_session_token() else {\n        anyhow::bail!(\"No web session token\");\n    };\n    // Best-effort server-side session invalidation.\n    let client = reqwest::Client::builder().timeout(Duration::from_secs(5)).build()?;\n    client\n        .post(host.join(\"auth/cli/logout\")?)\n        .header(\"Authorization\", format!(\"Bearer {web_session_token}\"))\n        .send()\n        .await?;\n    Ok(())\n}\n\n/// Logs out the user from the specified auth server.\n/// Returns true if the user was logged out, false if they were not logged in.\npub async fn ensure_logged_out(config: &mut Config, host: &Url) -> bool {\n    let Some(token) = config.spacetimedb_token() else {\n        return false;\n    };\n    // Grab identity before clearing tokens.\n    let identity = decode_identity(token).ok();\n\n    // Best-effort server-side session invalidation.\n    if let Err(e) = server_logout(config, host).await {\n        eprintln!(\"Warning: Failed to logout from auth server: {e}\\nLocal credentials have been cleared.\");\n    }\n    config.clear_login_tokens();\n    config.save();\n\n    if let Some(id) = identity {\n        println!(\"Logged out (identity {id}).\");\n    } else {\n        println!(\"Logged out.\");\n    }\n\n    true\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/logs.rs",
    "content": "use std::borrow::Cow;\nuse std::io::{self, Write};\n\nuse crate::common_args;\nuse crate::config::Config;\nuse crate::subcommands::db_arg_resolution::{load_config_db_targets, resolve_database_arg};\nuse crate::util::{add_auth_header_opt, database_identity, get_auth_header};\nuse clap::{Arg, ArgAction, ArgMatches};\nuse futures::{AsyncBufReadExt, TryStreamExt};\nuse is_terminal::IsTerminal;\nuse termcolor::{Color, ColorSpec, WriteColor};\nuse tokio::io::AsyncWriteExt;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"logs\")\n        .about(\"Prints logs from a SpacetimeDB database\")\n        .arg(\n            Arg::new(\"database\")\n                .required(false)\n                .help(\"The name or identity of the database to print logs from\"),\n        )\n        .arg(\n            common_args::server()\n                .help(\"The nickname, host name or URL of the server hosting the database\"),\n        )\n        .arg(\n            Arg::new(\"num_lines\")\n                .long(\"num-lines\")\n                .short('n')\n                .value_parser(clap::value_parser!(u32))\n                .help(\"The number of lines to print from the start of the log of this database\")\n                .long_help(\"The number of lines to print from the start of the log of this database. If no num lines is provided, all lines will be returned.\"),\n        )\n        .arg(\n            Arg::new(\"follow\")\n                .long(\"follow\")\n                .short('f')\n                .required(false)\n                .action(ArgAction::SetTrue)\n                .help(\"A flag indicating whether or not to follow the logs\")\n                .long_help(\"A flag that causes logs to not stop when end of the log file is reached, but rather to wait for additional data to be appended to the input.\"),\n        )\n        .arg(\n            Arg::new(\"format\")\n                .long(\"format\")\n                .default_value(\"text\")\n                .required(false)\n                .value_parser(clap::value_parser!(Format))\n                .help(\"Output format for the logs\")\n        )\n        .arg(\n            Arg::new(\"level\")\n                .long(\"level\")\n                .short('l')\n                .value_parser(clap::value_parser!(LogLevel))\n                .help(\"Minimum log level to display\")\n                .long_help(\n                    \"Filter logs by severity level. Only messages at the specified level or higher \\\n                     will be shown. Levels from least to most severe: trace, debug, info, warn, error, panic.\",\n                ),\n        )\n        .arg(\n            Arg::new(\"level_exact\")\n                .long(\"level-exact\")\n                .requires(\"level\")\n                .action(ArgAction::SetTrue)\n                .help(\"Show only logs at exactly the specified level\")\n                .long_help(\n                    \"When combined with --level, show only logs at exactly the specified level \\\n                     instead of that level and above.\",\n                ),\n        )\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .after_help(\"Run `spacetime help logs` for more detailed information.\\n\")\n}\n\n#[derive(Clone, Copy, serde::Deserialize)]\npub enum LogLevel {\n    Error,\n    Warn,\n    Info,\n    Debug,\n    Trace,\n    Panic,\n}\n\nimpl LogLevel {\n    /// Returns a numeric severity value. Higher means more severe.\n    fn severity(self) -> u8 {\n        match self {\n            LogLevel::Trace => 0,\n            LogLevel::Debug => 1,\n            LogLevel::Info => 2,\n            LogLevel::Warn => 3,\n            LogLevel::Error => 4,\n            LogLevel::Panic => 5,\n        }\n    }\n}\n\nimpl clap::ValueEnum for LogLevel {\n    fn value_variants<'a>() -> &'a [Self] {\n        &[\n            Self::Trace,\n            Self::Debug,\n            Self::Info,\n            Self::Warn,\n            Self::Error,\n            Self::Panic,\n        ]\n    }\n    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {\n        match self {\n            Self::Trace => Some(clap::builder::PossibleValue::new(\"trace\")),\n            Self::Debug => Some(clap::builder::PossibleValue::new(\"debug\")),\n            Self::Info => Some(clap::builder::PossibleValue::new(\"info\")),\n            Self::Warn => Some(clap::builder::PossibleValue::new(\"warn\")),\n            Self::Error => Some(clap::builder::PossibleValue::new(\"error\")),\n            Self::Panic => Some(clap::builder::PossibleValue::new(\"panic\")),\n        }\n    }\n}\n\n/// Sentinel value used for injected system logs.\n///\n/// Keep this in sync with the constants in `spacetimedb_core::database_logger::Record`.\nconst SENTINEL: &str = \"__spacetimedb__\";\n\n/// Keep this in sync with `spacetimedb_core::database_logger::Record`.\n#[serde_with::serde_as]\n#[derive(serde::Deserialize)]\nstruct Record<'a> {\n    #[serde_as(as = \"Option<serde_with::TimestampMicroSeconds>\")]\n    ts: Option<chrono::DateTime<chrono::Utc>>, // TODO: remove Option once 0.9 has been out for a while\n    level: LogLevel,\n    #[serde(borrow)]\n    #[allow(unused)] // TODO: format this somehow\n    target: Option<Cow<'a, str>>,\n    #[serde(borrow)]\n    filename: Option<Cow<'a, str>>,\n    line_number: Option<u32>,\n    #[serde(borrow)]\n    function: Option<Cow<'a, str>>,\n    #[serde(borrow)]\n    message: Cow<'a, str>,\n    trace: Option<Vec<BacktraceFrame<'a>>>,\n}\n\n#[derive(serde::Deserialize)]\npub struct BacktraceFrame<'a> {\n    #[serde(borrow)]\n    pub module_name: Option<Cow<'a, str>>,\n    #[serde(borrow)]\n    pub func_name: Option<Cow<'a, str>>,\n}\n\n#[derive(serde::Serialize)]\nstruct LogsParams {\n    num_lines: Option<u32>,\n    follow: bool,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum Format {\n    Text,\n    Json,\n}\n\nimpl clap::ValueEnum for Format {\n    fn value_variants<'a>() -> &'a [Self] {\n        &[Self::Text, Self::Json]\n    }\n    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {\n        match self {\n            Self::Text => Some(clap::builder::PossibleValue::new(\"text\").aliases([\"default\", \"txt\"])),\n            Self::Json => Some(clap::builder::PossibleValue::new(\"json\")),\n        }\n    }\n}\n\npub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server_from_cli = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let no_config = args.get_flag(\"no_config\");\n    let database_arg = args.get_one::<String>(\"database\").map(|s| s.as_str());\n    let config_targets = load_config_db_targets(no_config)?;\n    let resolved = resolve_database_arg(\n        database_arg,\n        config_targets.as_deref(),\n        \"spacetime logs [database] [--no-config]\",\n    )?;\n    let server = server_from_cli.or(resolved.server.as_deref());\n    let force = args.get_flag(\"force\");\n    let mut num_lines = args.get_one::<u32>(\"num_lines\").copied();\n    let follow = args.get_flag(\"follow\");\n    let format = *args.get_one::<Format>(\"format\").unwrap();\n    let min_level = args.get_one::<LogLevel>(\"level\").copied();\n    let level_exact = args.get_flag(\"level_exact\");\n\n    let auth_header = get_auth_header(&mut config, false, server, !force).await?;\n\n    let database_identity = database_identity(&config, &resolved.database, server).await?;\n\n    if follow && num_lines.is_none() {\n        // We typically don't want logs from the very beginning if we're also following.\n        num_lines = Some(10);\n    }\n    let query_params = LogsParams { num_lines, follow };\n\n    let host_url = config.get_host_url(server)?;\n\n    let builder = reqwest::Client::new().get(format!(\"{host_url}/v1/database/{database_identity}/logs\"));\n    let builder = add_auth_header_opt(builder, &auth_header);\n    let mut res = builder.query(&query_params).send().await?;\n    let status = res.status();\n\n    if status.is_client_error() || status.is_server_error() {\n        let err = res.text().await?;\n        anyhow::bail!(err)\n    }\n\n    if format == Format::Json {\n        let mut stdout = tokio::io::stdout();\n        if min_level.is_none() {\n            // Fast path: no filtering, stream raw bytes.\n            while let Some(chunk) = res.chunk().await? {\n                stdout.write_all(&chunk).await?;\n            }\n        } else {\n            // Parse each line to apply level filtering, then re-emit as JSON.\n            let mut rdr = res.bytes_stream().map_err(io::Error::other).into_async_read();\n            let mut line = String::new();\n            while rdr.read_line(&mut line).await? != 0 {\n                let record = serde_json::from_str::<Record<'_>>(&line)?;\n                if should_display(record.level, min_level, level_exact) {\n                    stdout.write_all(line.as_bytes()).await?;\n                }\n                line.clear();\n            }\n        }\n        return Ok(());\n    }\n\n    let term_color = if std::io::stdout().is_terminal() {\n        termcolor::ColorChoice::Auto\n    } else {\n        termcolor::ColorChoice::Never\n    };\n    let out = termcolor::StandardStream::stdout(term_color);\n    let mut out = out.lock();\n\n    let mut rdr = res.bytes_stream().map_err(io::Error::other).into_async_read();\n    let mut line = String::new();\n    while rdr.read_line(&mut line).await? != 0 {\n        let record = serde_json::from_str::<Record<'_>>(&line)?;\n\n        // Apply log level filtering.\n        if !should_display(record.level, min_level, level_exact) {\n            line.clear();\n            continue;\n        }\n\n        if let Some(ts) = record.ts {\n            out.set_color(ColorSpec::new().set_dimmed(true))?;\n            write!(out, \"{ts:?} \")?;\n        }\n        let mut color = ColorSpec::new();\n        let level = match record.level {\n            LogLevel::Error => {\n                color.set_fg(Some(Color::Red));\n                \"ERROR\"\n            }\n            LogLevel::Warn => {\n                color.set_fg(Some(Color::Yellow));\n                \"WARN\"\n            }\n            LogLevel::Info => {\n                color.set_fg(Some(Color::Blue));\n                \"INFO\"\n            }\n            LogLevel::Debug => {\n                color.set_dimmed(true).set_bold(true);\n                \"DEBUG\"\n            }\n            LogLevel::Trace => {\n                color.set_dimmed(true);\n                \"TRACE\"\n            }\n            LogLevel::Panic => {\n                color.set_fg(Some(Color::Red)).set_bold(true).set_intense(true);\n                \"PANIC\"\n            }\n        };\n        out.set_color(&color)?;\n        write!(out, \"{level:>5}: \")?;\n        out.reset()?;\n        let mut need_space_before_filename = false;\n        let mut need_colon_sep = false;\n        let dimmed = ColorSpec::new().set_dimmed(true).clone();\n        if let Some(function) = record.function\n            && function != SENTINEL\n        {\n            out.set_color(&dimmed)?;\n            write!(out, \"{function}\")?;\n            out.reset()?;\n            need_space_before_filename = true;\n            need_colon_sep = true;\n        }\n        if let Some(filename) = record.filename\n            && filename != SENTINEL\n        {\n            out.set_color(&dimmed)?;\n            if need_space_before_filename {\n                write!(out, \" \")?;\n            }\n            write!(out, \"{filename}\")?;\n            if let Some(line) = record.line_number {\n                write!(out, \":{line}\")?;\n            }\n            out.reset()?;\n            need_colon_sep = true;\n        }\n        if need_colon_sep {\n            write!(out, \": \")?;\n        }\n        writeln!(out, \"{}\", record.message)?;\n        if let Some(trace) = &record.trace {\n            for frame in trace {\n                write!(out, \"    in \")?;\n                if let Some(module) = &frame.module_name {\n                    out.set_color(&dimmed)?;\n                    write!(out, \"{module}\")?;\n                    out.reset()?;\n                    write!(out, \" :: \")?;\n                }\n                if let Some(function) = &frame.func_name {\n                    out.set_color(&dimmed)?;\n                    writeln!(out, \"{function}\")?;\n                    out.reset()?;\n                }\n            }\n        }\n\n        line.clear();\n    }\n\n    Ok(())\n}\n\n/// Returns true if the record should be displayed given the filter settings.\nfn should_display(record_level: LogLevel, min_level: Option<LogLevel>, level_exact: bool) -> bool {\n    match min_level {\n        None => true,\n        Some(min) => {\n            if level_exact {\n                record_level.severity() == min.severity()\n            } else {\n                record_level.severity() >= min.severity()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/mod.rs",
    "content": "pub mod build;\npub mod call;\npub mod db_arg_resolution;\npub mod delete;\npub mod describe;\npub mod dev;\npub mod dns;\npub mod generate;\npub mod init;\npub mod list;\npub mod login;\npub mod logout;\npub mod logs;\npub mod publish;\npub mod repl;\npub mod server;\npub mod sql;\npub mod start;\npub mod subscribe;\npub mod version;\n"
  },
  {
    "path": "crates/cli/src/subcommands/project/typescript/_gitignore",
    "content": ""
  },
  {
    "path": "crates/cli/src/subcommands/project/typescript/index._ts",
    "content": "import { schema, table, t } from 'spacetimedb/server';\n\nconst spacetimedb = schema({\n  person: table(\n    { name: 'person' },\n    {\n      name: t.string(),\n    }\n  )\n});\nexport default spacetimedb;\n\nexport const init = spacetimedb.init((_ctx) => {\n  // Called when the module is initially published\n});\n\nexport const onConnect = spacetimedb.clientConnected((_ctx) => {\n  // Called every time a new client connects\n});\n\nexport const onDisconnect = spacetimedb.clientDisconnected((_ctx) => {\n  // Called every time a client disconnects\n});\n\nexport const add = spacetimedb.reducer({ name: t.string() }, (ctx, { name }) => {\n  ctx.db.person.insert({ name });\n});\n\nexport const sayHello = spacetimedb.reducer((ctx) => {\n  for (const person of ctx.db.person.iter()) {\n    console.info(`Hello, ${person.name}!`);\n  }\n  console.info('Hello, World!');\n});"
  },
  {
    "path": "crates/cli/src/subcommands/project/typescript/package._json",
    "content": "{\n  \"name\": \"spacetime-module\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"scripts\": {\n    \"build\": \"spacetime build\",\n    \"publish\": \"spacetime publish\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"spacetimedb\": \"2.0.*\"\n  }\n}"
  },
  {
    "path": "crates/cli/src/subcommands/project/typescript/tsconfig._json",
    "content": "\n/*\n * This tsconfig is used for TypeScript projects created with `spacetimedb init\n * --lang typescript`. You can modify it as needed for your project, although\n * some options are required by SpacetimeDB.\n */\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"moduleResolution\": \"bundler\",\n    \"jsx\": \"react-jsx\",\n\n    /* The following options are required by SpacetimeDB\n     * and should not be modified\n     */\n    \"target\": \"ESNext\",\n    \"lib\": [\"ES2021\", \"dom\"],\n    \"module\": \"ESNext\",\n    \"isolatedModules\": true,\n    \"noEmit\": true\n  },\n  \"include\": [\"./**/*\"]\n}"
  },
  {
    "path": "crates/cli/src/subcommands/publish.rs",
    "content": "use anyhow::{ensure, Context};\nuse clap::Arg;\nuse clap::ArgAction::{Set, SetTrue};\nuse clap::ArgMatches;\nuse reqwest::{StatusCode, Url};\nuse spacetimedb_client_api_messages::name::{is_identity, parse_database_name, PublishResult};\nuse spacetimedb_client_api_messages::name::{DatabaseNameError, PrePublishResult, PrettyPrintStyle, PublishOp};\nuse std::collections::HashMap;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nuse crate::common_args::ClearMode;\nuse crate::config::Config;\nuse crate::spacetime_config::{\n    find_and_load_with_env, CommandConfig, CommandSchema, CommandSchemaBuilder, FlatTarget, Key, LoadedConfig,\n    SpacetimeConfig,\n};\nuse crate::util::{add_auth_header_opt, get_auth_header, strip_verbatim_prefix, AuthHeader, ResponseExt};\nuse crate::util::{decode_identity, y_or_n};\nuse crate::{build, common_args};\n\n/// Build the CommandSchema for publish command\npub fn build_publish_schema(command: &clap::Command) -> Result<CommandSchema, anyhow::Error> {\n    CommandSchemaBuilder::new()\n        .key(Key::new(\"database\").from_clap(\"name|identity\").required())\n        .key(Key::new(\"server\"))\n        .key(Key::new(\"module_path\").module_specific())\n        .key(Key::new(\"build_options\").module_specific())\n        .key(Key::new(\"wasm_file\").module_specific())\n        .key(Key::new(\"js_file\").module_specific())\n        .key(Key::new(\"num_replicas\").module_specific())\n        .key(Key::new(\"break_clients\"))\n        .key(Key::new(\"anon_identity\"))\n        .key(Key::new(\"parent\"))\n        .key(Key::new(\"organization\"))\n        .exclude(\"clear-database\")\n        .exclude(\"force\")\n        .exclude(\"no_config\")\n        .exclude(\"env\")\n        .build(command)\n        .map_err(Into::into)\n}\n\n/// Get filtered publish configs based on CLI arguments.\n/// Uses glob matching on database names when a pattern is provided via CLI.\npub fn get_filtered_publish_configs<'a>(\n    spacetime_config: &SpacetimeConfig,\n    command: &clap::Command,\n    schema: &'a CommandSchema,\n    args: &'a ArgMatches,\n) -> Result<Vec<CommandConfig<'a>>, anyhow::Error> {\n    // Get all database targets from config with parent→child inheritance\n    let all_targets = spacetime_config.collect_all_targets_with_inheritance();\n\n    // If no targets, return empty (will use CLI args only)\n    if all_targets.is_empty() {\n        return Ok(vec![]);\n    }\n\n    // Filter by database name pattern (glob) if provided via CLI\n    let filtered_targets: Vec<FlatTarget> = if schema.is_from_cli(args, \"database\") {\n        let cli_database = schema.get_clap_arg::<String>(args, \"database\")?.unwrap_or_default();\n\n        let pattern =\n            glob::Pattern::new(&cli_database).with_context(|| format!(\"Invalid glob pattern: {cli_database}\"))?;\n\n        let matched: Vec<FlatTarget> = all_targets\n            .into_iter()\n            .filter(|target| {\n                target\n                    .fields\n                    .get(\"database\")\n                    .and_then(|v| v.as_str())\n                    .is_some_and(|db| pattern.matches(db))\n            })\n            .collect();\n\n        if matched.is_empty() {\n            anyhow::bail!(\n                \"No database target matches '{}'. Available databases: {}\",\n                cli_database,\n                spacetime_config\n                    .collect_all_targets_with_inheritance()\n                    .iter()\n                    .filter_map(|t| t.fields.get(\"database\").and_then(|v| v.as_str()))\n                    .collect::<Vec<_>>()\n                    .join(\", \")\n            );\n        }\n\n        matched\n    } else {\n        all_targets\n    };\n\n    // Build CommandConfig for each target\n    let configs: Vec<CommandConfig> = filtered_targets\n        .into_iter()\n        .map(|target| {\n            let config = CommandConfig::new(schema, target.fields, args)?;\n            config.validate()?;\n            Ok(config)\n        })\n        .collect::<Result<Vec<_>, anyhow::Error>>()?;\n\n    schema.validate_no_module_specific_cli_args_for_multiple_targets(\n        command,\n        args,\n        configs.len(),\n        \"publishing to multiple targets\",\n        \"Please specify the database name or identity to select a single target, or remove these arguments.\",\n    )?;\n\n    Ok(configs)\n}\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"publish\")\n        .about(\"Create and update a SpacetimeDB database\")\n        .arg(\n            common_args::clear_database()\n                .requires(\"name|identity\")\n        )\n        .arg(\n            Arg::new(\"build_options\")\n                .long(\"build-options\")\n                .alias(\"build-opts\")\n                .action(Set)\n                .default_value(\"\")\n                .help(\"Options to pass to the build command, for example --build-options='--lint-dir='\")\n        )\n        .arg(\n            Arg::new(\"module_path\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"module-path\")\n                .short('p')\n                .help(\"The system path (absolute or relative) to the module project. Defaults to spacetimedb/ subdirectory, then current directory.\")\n        )\n        .arg(\n            Arg::new(\"wasm_file\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"bin-path\")\n                .short('b')\n                .conflicts_with(\"module_path\")\n                .conflicts_with(\"build_options\")\n                .conflicts_with(\"js_file\")\n                .help(\"The system path (absolute or relative) to the compiled wasm binary we should publish, instead of building the project.\"),\n        )\n        .arg(\n            Arg::new(\"js_file\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .long(\"js-path\")\n                .short('j')\n                .conflicts_with(\"module_path\")\n                .conflicts_with(\"build_options\")\n                .conflicts_with(\"wasm_file\")\n                .help(\"UNSTABLE: The system path (absolute or relative) to the javascript file we should publish, instead of building the project.\"),\n        )\n        .arg(\n            Arg::new(\"num_replicas\")\n                .value_parser(clap::value_parser!(u8))\n                .long(\"num-replicas\")\n                .hide(true)\n                .help(\"UNSTABLE: The number of replicas the database should have\")\n        )\n        .arg(\n            Arg::new(\"break_clients\")\n                .long(\"break-clients\")\n                .action(SetTrue)\n                .help(\"Allow breaking changes when publishing to an existing database identity. This will force publish even if it will break existing clients, but will NOT force publish if it would cause deletion of any data in the database. See --yes and --delete-data for details.\")\n        )\n        .arg(\n            common_args::anonymous()\n        )\n        .arg(\n            Arg::new(\"parent\")\n            .help(\"Domain or identity of a parent for this database\")\n            .long(\"parent\")\n            .long_help(\n\"A valid domain or identity of an existing database that should be the parent of this database.\n\nIf a parent is given, the new database inherits the team permissions from the parent.\nA parent can only be set when a database is created, not when it is updated.\"\n            )\n        )\n        .arg(\n            Arg::new(\"organization\")\n            .help(\"Name or identity of an organization for this database\")\n            .long(\"organization\")\n            .alias(\"org\")\n            .long_help(\n\"The name or identity of an existing organization this database should be created under.\n\nIf an organization is given, the organization member's permissions apply to the new database.\nAn organization can only be set when a database is created, not when it is updated.\"\n            )\n        )\n        .arg(\n            Arg::new(\"name|identity\")\n                .help(\"A valid domain or identity for this database\")\n                .long_help(\n\"A valid domain or identity for this database.\n\nDatabase names must match the regex `/^[a-z0-9]+(-[a-z0-9]+)*$/`,\ni.e. only lowercase ASCII letters and numbers, separated by dashes.\"),\n        )\n        .arg(common_args::server()\n                .help(\"The nickname, domain name or URL of the server to host the database.\"),\n        )\n        .arg(\n            common_args::yes()\n        )\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(SetTrue)\n                .help(\"Ignore spacetime.json configuration\")\n        )\n        .arg(\n            Arg::new(\"env\")\n                .long(\"env\")\n                .value_name(\"ENV\")\n                .action(Set)\n                .help(\"Environment name for config file layering (e.g., dev, staging)\")\n        )\n        .after_help(\"Run `spacetime help publish` for more detailed information.\")\n}\n\nfn confirm_and_clear(\n    name_or_identity: &str,\n    force: bool,\n    mut builder: reqwest::RequestBuilder,\n) -> Result<reqwest::RequestBuilder, anyhow::Error> {\n    println!(\n        \"This will DESTROY the current {} module, and ALL corresponding data.\",\n        name_or_identity\n    );\n    if !y_or_n(\n        force,\n        format!(\"Are you sure you want to proceed? [deleting {}]\", name_or_identity).as_str(),\n    )? {\n        anyhow::bail!(\"Aborted.\");\n    }\n\n    builder = builder.query(&[(\"clear\", true)]);\n    Ok(builder)\n}\n\nfn confirm_major_version_upgrade(force: bool) -> Result<(), anyhow::Error> {\n    println!(\n        \"It looks like you're trying to do a major version upgrade from 1.0 to 2.0. We recommend first looking at the upgrade notes before committing to this upgrade: https://spacetimedb.com/docs/upgrade\"\n    );\n    println!();\n    println!(\"WARNING: Once you publish you cannot revert back to version 1.0.\");\n    println!();\n\n    if force {\n        return Ok(());\n    }\n\n    let mut input = String::new();\n    print!(\"Please type 'upgrade' to accept this change: \");\n    let mut stdout = std::io::stdout();\n    std::io::Write::flush(&mut stdout)?;\n    std::io::stdin().read_line(&mut input)?;\n\n    if input.trim() == \"upgrade\" {\n        return Ok(());\n    }\n\n    anyhow::bail!(\"Aborting because major version upgrade was not accepted.\");\n}\n\npub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    exec_with_options(config, args, false, None).await\n}\n\n/// This function can be used when calling publish programatically rather than straight from the\n/// CLI, like we do in `spacetime dev`. When calling from `spacetime dev` we don't want to display\n/// information about using the `spacetime.json` file as it's already announced as part of the\n/// `dev` command\npub async fn exec_with_options(\n    mut config: Config,\n    args: &ArgMatches,\n    quiet_config: bool,\n    pre_loaded_config: Option<&LoadedConfig>,\n) -> Result<(), anyhow::Error> {\n    // Build schema\n    let cmd = cli();\n    let schema = build_publish_schema(&cmd)?;\n\n    let no_config = args.get_flag(\"no_config\");\n    let env = args.get_one::<String>(\"env\").map(|s| s.as_str());\n\n    // Get publish configs (from spacetime.json or empty)\n    let owned_loaded;\n    let loaded_config_ref = if no_config {\n        None\n    } else if let Some(pre) = pre_loaded_config {\n        Some(pre)\n    } else {\n        owned_loaded = find_and_load_with_env(env)?;\n        owned_loaded.as_ref().inspect(|loaded| {\n            if !quiet_config {\n                for path in &loaded.loaded_files {\n                    println!(\"Using configuration from {}\", path.display());\n                }\n            }\n        })\n    };\n\n    let (using_config, publish_configs) = if let Some(loaded) = loaded_config_ref {\n        let filtered = get_filtered_publish_configs(&loaded.config, &cmd, &schema, args)?;\n        if filtered.is_empty() {\n            anyhow::bail!(\n                \"No matching target found in spacetime.json for the provided arguments. \\\n                 Use --no-config to ignore the config file.\"\n            );\n        }\n        (true, filtered)\n    } else {\n        (\n            false,\n            vec![CommandConfig::new(&schema, std::collections::HashMap::new(), args)?],\n        )\n    };\n\n    let clear_database = args\n        .get_one::<ClearMode>(\"clear-database\")\n        .copied()\n        .unwrap_or(ClearMode::Never);\n    let force = args.get_flag(\"force\");\n    let config_dir = loaded_config_ref.map(|lc| lc.config_dir.as_path());\n\n    execute_publish_configs(\n        &mut config,\n        publish_configs,\n        using_config,\n        config_dir,\n        clear_database,\n        force,\n    )\n    .await\n}\n\npub async fn exec_from_entry(\n    mut config: Config,\n    entry: HashMap<String, serde_json::Value>,\n    config_dir: Option<&std::path::Path>,\n    clear_database: ClearMode,\n    force: bool,\n) -> Result<(), anyhow::Error> {\n    let cmd = cli();\n    let schema = build_publish_schema(&cmd)?;\n    let matches = cmd.get_matches_from(vec![\"publish\"]);\n\n    let command_config = CommandConfig::new(&schema, entry, &matches)?;\n    command_config.validate()?;\n\n    execute_publish_configs(\n        &mut config,\n        vec![command_config],\n        true,\n        config_dir,\n        clear_database,\n        force,\n    )\n    .await\n}\n\nasync fn execute_publish_configs<'a>(\n    config: &mut Config,\n    publish_configs: Vec<CommandConfig<'a>>,\n    using_config: bool,\n    config_dir: Option<&std::path::Path>,\n    clear_database: ClearMode,\n    force: bool,\n) -> Result<(), anyhow::Error> {\n    // Execute publish for each config\n    for command_config in publish_configs {\n        // Get values using command_config.get_one() which merges CLI + config\n        let server_opt = command_config.get_one::<String>(\"server\")?;\n        let server = server_opt.as_deref();\n        let name_or_identity_opt = command_config.get_one::<String>(\"database\")?;\n        let name_or_identity = name_or_identity_opt.as_deref();\n        let anon_identity = command_config.get_one::<bool>(\"anon_identity\")?.unwrap_or(false);\n        let wasm_file = command_config.get_one::<PathBuf>(\"wasm_file\")?;\n        let js_file = command_config.get_one::<PathBuf>(\"js_file\")?;\n        let resolved_module_path = command_config.get_resolved_path(\"module_path\", config_dir)?;\n        let path_to_project = if wasm_file.is_some() || js_file.is_some() {\n            resolved_module_path\n        } else {\n            Some(match resolved_module_path {\n                Some(path) => path,\n                None => default_publish_module_path(&std::env::current_dir()?),\n            })\n        };\n\n        if using_config {\n            if let Some(path_to_project) = path_to_project.as_ref() {\n                println!(\n                    \"Publishing module {} to database '{}'\",\n                    strip_verbatim_prefix(path_to_project).display(),\n                    name_or_identity.unwrap()\n                );\n            } else {\n                println!(\n                    \"Publishing precompiled module to database '{}'\",\n                    name_or_identity.unwrap()\n                );\n            }\n        }\n        let database_host = config.get_host_url(server)?;\n        let build_options = command_config\n            .get_one::<String>(\"build_options\")?\n            .unwrap_or_else(String::new);\n        let num_replicas = command_config.get_one::<u8>(\"num_replicas\")?;\n        let force_break_clients = command_config.get_one::<bool>(\"break_clients\")?.unwrap_or(false);\n        let parent_opt = command_config.get_one::<String>(\"parent\")?;\n        let parent = parent_opt.as_deref();\n        let org_opt = command_config.get_one::<String>(\"organization\")?;\n        let org = org_opt.as_deref();\n\n        // If the user didn't specify an identity and we didn't specify an anonymous identity, then\n        // we want to use the default identity\n        // TODO(jdetter): We should maybe have some sort of user prompt here for them to be able to\n        //  easily create a new identity with an email\n        let auth_header = get_auth_header(config, anon_identity, server, !force).await?;\n\n        let (name_or_identity, parent) = validate_name_and_parent(name_or_identity, parent)?;\n\n        if let Some(path_to_project) = path_to_project.as_ref()\n            && !path_to_project.exists()\n        {\n            return Err(anyhow::anyhow!(\n                \"Project path does not exist: {}\",\n                path_to_project.display()\n            ));\n        }\n\n        // Decide program file path and read program.\n        // Optionally build the program.\n        let (path_to_program, host_type) = if let Some(path) = wasm_file {\n            println!(\"(WASM) Skipping build. Instead we are publishing {}\", path.display());\n            (path.clone(), \"Wasm\")\n        } else if let Some(path) = js_file {\n            println!(\"(JS) Skipping build. Instead we are publishing {}\", path.display());\n            (path.clone(), \"Js\")\n        } else {\n            build::exec_with_argstring(\n                path_to_project\n                    .as_ref()\n                    .expect(\"path_to_project must exist when publishing from source\"),\n                &build_options,\n            )\n            .await?\n        };\n        let program_bytes = fs::read(path_to_program)?;\n\n        let server_address = {\n            let url = Url::parse(&database_host)?;\n            url.host_str().unwrap_or(\"<default>\").to_string()\n        };\n        if server_address != \"localhost\" && server_address != \"127.0.0.1\" {\n            println!(\"You are about to publish to a non-local server: {server_address}\");\n            if !y_or_n(force, \"Are you sure you want to proceed?\")? {\n                anyhow::bail!(\"Publish aborted by user.\");\n            }\n        }\n\n        println!(\n            \"Uploading to {} => {}\",\n            server.unwrap_or(config.default_server_name().unwrap_or(\"<default>\")),\n            database_host\n        );\n\n        let client = reqwest::Client::new();\n        // If a name was given, ensure to percent-encode it.\n        // We also use PUT with a name or identity, and POST otherwise.\n        let mut builder = if let Some(name_or_identity) = name_or_identity {\n            let encode_set = const { &percent_encoding::NON_ALPHANUMERIC.remove(b'_').remove(b'-') };\n            let domain = percent_encoding::percent_encode(name_or_identity.as_bytes(), encode_set);\n            let mut builder = client.put(format!(\"{database_host}/v1/database/{domain}\"));\n\n            // note that this only happens in the case where we've passed a `name_or_identity`, but that's required if we pass `--clear-database`.\n            if clear_database == ClearMode::Always {\n                builder = confirm_and_clear(name_or_identity, force, builder)?;\n            } else {\n                builder = apply_pre_publish_if_needed(\n                    builder,\n                    &client,\n                    &database_host,\n                    name_or_identity,\n                    &domain.to_string(),\n                    host_type,\n                    &program_bytes,\n                    &auth_header,\n                    clear_database,\n                    force_break_clients,\n                    force,\n                )\n                .await?;\n            }\n\n            builder\n        } else {\n            client.post(format!(\"{database_host}/v1/database\"))\n        };\n\n        if let Some(n) = num_replicas {\n            eprintln!(\"WARNING: Use of unstable option `--num-replicas`.\\n\");\n            builder = builder.query(&[(\"num_replicas\", n)]);\n        }\n        if let Some(parent) = parent {\n            builder = builder.query(&[(\"parent\", parent)]);\n        }\n        if let Some(org) = org {\n            builder = builder.query(&[(\"org\", org)]);\n        }\n\n        println!(\"Publishing module...\");\n\n        builder = add_auth_header_opt(builder, &auth_header);\n\n        // Set the host type.\n        builder = builder.query(&[(\"host_type\", host_type)]);\n\n        let res = builder.body(program_bytes).send().await?;\n        let response: PublishResult = res.json_or_error().await?;\n        match response {\n            PublishResult::Success {\n                domain,\n                database_identity,\n                op,\n            } => {\n                let op = match op {\n                    PublishOp::Created => \"Created new\",\n                    PublishOp::Updated => \"Updated\",\n                };\n                if let Some(ref domain) = domain {\n                    println!(\"{op} database with name: {domain}, identity: {database_identity}\");\n                } else {\n                    println!(\"{op} database with identity: {database_identity}\");\n                }\n\n                if is_maincloud_host(&database_host)\n                    && let Some(domain) = domain.as_ref()\n                {\n                    println!(\"Dashboard: https://spacetimedb.com/{}\", domain.as_ref());\n                }\n            }\n            PublishResult::PermissionDenied { name } => {\n                if anon_identity {\n                    anyhow::bail!(\"You need to be logged in as the owner of {name} to publish to {name}\",);\n                }\n                // If we're not in the `anon_identity` case, then we have already forced the user to log in above (using `get_auth_header`), so this should be safe to unwrap.\n                let token = config.spacetimedb_token().unwrap();\n                let identity = decode_identity(token)?;\n                //TODO(jdetter): Have a nice name generator here, instead of using some abstract characters\n                // we should perhaps generate fun names like 'green-fire-dragon' instead\n                let suggested_tld: String = identity.chars().take(12).collect();\n                return Err(anyhow::anyhow!(\n                    \"The database {name} is not registered to the identity you provided.\\n\\\n                    We suggest you push to either a domain owned by you, or a new domain like:\\n\\\n                    \\tspacetime publish {suggested_tld}\\n\",\n                ));\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn default_publish_module_path(current_dir: &std::path::Path) -> PathBuf {\n    let spacetimedb_dir = current_dir.join(\"spacetimedb\");\n    if spacetimedb_dir.is_dir() {\n        spacetimedb_dir\n    } else {\n        current_dir.to_path_buf()\n    }\n}\n\nfn is_maincloud_host(database_host: &str) -> bool {\n    Url::parse(database_host)\n        .ok()\n        .and_then(|url| {\n            url.host_str()\n                .map(|h| h.eq_ignore_ascii_case(\"maincloud.spacetimedb.com\"))\n        })\n        .unwrap_or(false)\n}\n\nfn validate_name_or_identity(name_or_identity: &str) -> Result<(), DatabaseNameError> {\n    if is_identity(name_or_identity) {\n        Ok(())\n    } else {\n        parse_database_name(name_or_identity).map(drop)\n    }\n}\n\nfn invalid_parent_name(name: &str) -> String {\n    format!(\"invalid parent database name `{name}`\")\n}\n\nfn validate_name_and_parent<'a>(\n    name: Option<&'a str>,\n    parent: Option<&'a str>,\n) -> anyhow::Result<(Option<&'a str>, Option<&'a str>)> {\n    if let Some(parent) = parent.as_ref() {\n        validate_name_or_identity(parent).with_context(|| invalid_parent_name(parent))?;\n    }\n\n    match name {\n        Some(name) => match name.split_once('/') {\n            Some((parent_alt, child)) => {\n                ensure!(\n                    parent.is_none() || parent.is_some_and(|parent| parent == parent_alt),\n                    \"cannot specify both --parent and <parent>/<child>\"\n                );\n                validate_name_or_identity(parent_alt).with_context(|| invalid_parent_name(parent_alt))?;\n                validate_name_or_identity(child)?;\n\n                Ok((Some(child), Some(parent_alt)))\n            }\n            None => {\n                validate_name_or_identity(name)?;\n                Ok((Some(name), parent))\n            }\n        },\n        None => Ok((None, parent)),\n    }\n}\n\n/// Determine the pretty print style based on the NO_COLOR environment variable.\n///\n/// See: https://no-color.org\npub fn pretty_print_style_from_env() -> PrettyPrintStyle {\n    match env::var(\"NO_COLOR\") {\n        Ok(_) => PrettyPrintStyle::NoColor,\n        Err(_) => PrettyPrintStyle::AnsiColor,\n    }\n}\n\n/// Applies pre-publish logic: checking for migration plan, prompting user, and\n/// modifying the request builder accordingly.\n#[allow(clippy::too_many_arguments)]\nasync fn apply_pre_publish_if_needed(\n    mut builder: reqwest::RequestBuilder,\n    client: &reqwest::Client,\n    base_url: &str,\n    name_or_identity: &str,\n    domain: &String,\n    host_type: &str,\n    program_bytes: &[u8],\n    auth_header: &AuthHeader,\n    clear_database: ClearMode,\n    force_break_clients: bool,\n    force: bool,\n) -> Result<reqwest::RequestBuilder, anyhow::Error> {\n    // The caller enforces this\n    assert!(clear_database != ClearMode::Always);\n\n    if let Some(pre) = call_pre_publish(\n        client,\n        base_url,\n        &domain.to_string(),\n        host_type,\n        program_bytes,\n        auth_header,\n    )\n    .await?\n    {\n        let major_version_upgrade = match &pre {\n            PrePublishResult::AutoMigrate(auto) => auto.major_version_upgrade,\n            PrePublishResult::ManualMigrate(manual) => manual.major_version_upgrade,\n        };\n        if major_version_upgrade {\n            confirm_major_version_upgrade(force)?;\n        }\n\n        match pre {\n            PrePublishResult::ManualMigrate(manual) => {\n                if clear_database == ClearMode::Never {\n                    println!(\"{}\", manual.reason);\n                    println!(\"Aborting publish due to required manual migration.\");\n                    anyhow::bail!(\"Aborting because publishing would require manual migration or deletion of data and --delete-data was not specified.\");\n                }\n                println!(\"{}\", manual.reason);\n                println!(\"Proceeding with database clear due to --delete-data=on-conflict.\");\n\n                builder = confirm_and_clear(name_or_identity, force, builder)?;\n            }\n            PrePublishResult::AutoMigrate(auto) => {\n                println!(\"{}\", auto.migrate_plan);\n                // We only arrive here if you have not specified ClearMode::Always AND there was no\n                // conflict that required manual migration.\n                if auto.break_clients\n                    && !y_or_n(\n                        force_break_clients || force,\n                        \"The above changes will BREAK existing clients. Do you want to proceed?\",\n                    )?\n                {\n                    println!(\"Aborting\");\n                    // Early exit: return an error or a special signal. Here we bail out by returning Err.\n                    anyhow::bail!(\"Publishing aborted by user\");\n                }\n                builder = builder\n                    .query(&[(\"token\", auto.token)])\n                    .query(&[(\"policy\", \"BreakClients\")]);\n            }\n        }\n    }\n\n    Ok(builder)\n}\n\nasync fn call_pre_publish(\n    client: &reqwest::Client,\n    database_host: &str,\n    domain: &String,\n    host_type: &str,\n    program_bytes: &[u8],\n    auth_header: &AuthHeader,\n) -> Result<Option<PrePublishResult>, anyhow::Error> {\n    let mut builder = client.post(format!(\"{database_host}/v1/database/{domain}/pre_publish\"));\n    let style: PrettyPrintStyle = pretty_print_style_from_env();\n    builder = builder\n        .query(&[(\"pretty_print_style\", style)])\n        .query(&[(\"host_type\", host_type)]);\n\n    builder = add_auth_header_opt(builder, auth_header);\n\n    println!(\"Checking for breaking changes...\");\n    let res = builder.body(program_bytes.to_vec()).send().await?;\n\n    if res.status() == StatusCode::NOT_FOUND {\n        // This is a new database, so there are no breaking changes\n        return Ok(None);\n    }\n\n    if !res.status().is_success() {\n        anyhow::bail!(\n            \"Pre-publish check failed with status {}: {}\",\n            res.status(),\n            res.text().await?\n        );\n    }\n\n    let pre_publish_result: PrePublishResult = res.json_or_error().await?;\n    Ok(Some(pre_publish_result))\n}\n\n#[cfg(test)]\nmod tests {\n    use pretty_assertions::assert_matches;\n    use spacetimedb_lib::Identity;\n    use std::collections::HashMap;\n\n    use super::*;\n\n    #[test]\n    fn validate_none_arguments_returns_none_values() {\n        assert_matches!(validate_name_and_parent(None, None), Ok((None, None)));\n        assert_matches!(validate_name_and_parent(Some(\"foo\"), None), Ok((Some(_), None)));\n        assert_matches!(validate_name_and_parent(None, Some(\"foo\")), Ok((None, Some(_))));\n    }\n\n    #[test]\n    fn validate_valid_arguments_returns_arguments() {\n        let name = \"child\";\n        let parent = \"parent\";\n        let result = (Some(name), Some(parent));\n        assert_matches!(\n            validate_name_and_parent(Some(name), Some(parent)),\n            Ok(val) if val == result\n        );\n    }\n\n    #[test]\n    fn validate_parent_and_path_name_returns_error_unless_parent_equal() {\n        assert_matches!(\n            validate_name_and_parent(Some(\"parent/child\"), Some(\"parent\")),\n            Ok((Some(\"child\"), Some(\"parent\")))\n        );\n        assert_matches!(validate_name_and_parent(Some(\"parent/child\"), Some(\"cousin\")), Err(_));\n    }\n\n    #[test]\n    fn validate_more_than_two_path_segments_are_an_error() {\n        assert_matches!(validate_name_and_parent(Some(\"proc/net/tcp\"), None), Err(_));\n        assert_matches!(validate_name_and_parent(Some(\"proc//net\"), None), Err(_));\n    }\n\n    #[test]\n    fn validate_trailing_slash_is_an_error() {\n        assert_matches!(validate_name_and_parent(Some(\"foo//\"), None), Err(_));\n        assert_matches!(validate_name_and_parent(Some(\"foo/bar/\"), None), Err(_));\n    }\n\n    #[test]\n    fn validate_parent_cant_have_slash() {\n        assert_matches!(validate_name_and_parent(Some(\"child\"), Some(\"par/ent\")), Err(_));\n        assert_matches!(validate_name_and_parent(Some(\"child\"), Some(\"parent/\")), Err(_));\n    }\n\n    #[test]\n    fn validate_name_or_parent_can_be_identities() {\n        let parent = Identity::ZERO.to_string();\n        let child = Identity::ONE.to_string();\n\n        assert_matches!(\n            validate_name_and_parent(Some(&child), Some(&parent)),\n            Ok(res) if res == (Some(&child), Some(&parent))\n        );\n    }\n\n    /// Helper to build a SpacetimeConfig with additional_fields (database-centric).\n    fn make_config(fields: HashMap<String, serde_json::Value>) -> SpacetimeConfig {\n        SpacetimeConfig {\n            additional_fields: fields,\n            ..Default::default()\n        }\n    }\n\n    fn make_config_with_children(\n        fields: HashMap<String, serde_json::Value>,\n        children: Vec<SpacetimeConfig>,\n    ) -> SpacetimeConfig {\n        SpacetimeConfig {\n            additional_fields: fields,\n            children: Some(children),\n            ..Default::default()\n        }\n    }\n\n    #[test]\n    fn test_filter_by_database_from_cli() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"database\".to_string(), serde_json::json!(\"parent-db\"));\n\n        let mut child1_fields = HashMap::new();\n        child1_fields.insert(\"database\".to_string(), serde_json::json!(\"db1\"));\n\n        let mut child2_fields = HashMap::new();\n        child2_fields.insert(\"database\".to_string(), serde_json::json!(\"db2\"));\n\n        let spacetime_config = make_config_with_children(\n            parent_fields,\n            vec![make_config(child1_fields), make_config(child2_fields)],\n        );\n\n        // Filter by db1 (should only match child1, not parent or child2)\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"db1\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 1, \"Should only match db1\");\n        assert_eq!(\n            filtered[0].get_one::<String>(\"database\").unwrap(),\n            Some(\"db1\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_no_filter_when_database_not_from_cli() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"database\".to_string(), serde_json::json!(\"parent-db\"));\n\n        let mut child1_fields = HashMap::new();\n        child1_fields.insert(\"database\".to_string(), serde_json::json!(\"db1\"));\n\n        let mut child2_fields = HashMap::new();\n        child2_fields.insert(\"database\".to_string(), serde_json::json!(\"db2\"));\n\n        let spacetime_config = make_config_with_children(\n            parent_fields,\n            vec![make_config(child1_fields), make_config(child2_fields)],\n        );\n\n        // No database provided via CLI\n        let matches = cmd.clone().get_matches_from(vec![\"publish\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        // Should return all configs (parent + 2 children)\n        assert_eq!(filtered.len(), 3);\n    }\n\n    #[test]\n    fn test_error_when_filter_no_match() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"database\".to_string(), serde_json::json!(\"parent-db\"));\n\n        let mut child1_fields = HashMap::new();\n        child1_fields.insert(\"database\".to_string(), serde_json::json!(\"db1\"));\n\n        let spacetime_config = make_config_with_children(parent_fields, vec![make_config(child1_fields)]);\n\n        // Filter by non-existent database — now errors instead of returning empty\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"nonexistent\"]);\n        let result = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches);\n        assert!(result.is_err());\n        let err_msg = result.unwrap_err().to_string();\n        assert!(err_msg.contains(\"No database target matches\"));\n    }\n\n    #[test]\n    fn test_default_publish_module_path_prefers_spacetimedb_dir() {\n        let temp = tempfile::TempDir::new().unwrap();\n        let cwd = temp.path().to_path_buf();\n        let spacetimedb_dir = cwd.join(\"spacetimedb\");\n        std::fs::create_dir_all(&spacetimedb_dir).unwrap();\n\n        let resolved = default_publish_module_path(&cwd);\n        assert_eq!(resolved, spacetimedb_dir);\n    }\n\n    #[test]\n    fn test_default_publish_module_path_falls_back_to_current_dir() {\n        let temp = tempfile::TempDir::new().unwrap();\n        let cwd = temp.path().to_path_buf();\n\n        let resolved = default_publish_module_path(&cwd);\n        assert_eq!(resolved, cwd);\n    }\n\n    #[test]\n    fn test_is_maincloud_host_true_for_maincloud_url() {\n        assert!(is_maincloud_host(\"https://maincloud.spacetimedb.com\"));\n    }\n\n    #[test]\n    fn test_is_maincloud_host_false_for_non_maincloud_url() {\n        assert!(!is_maincloud_host(\"http://localhost:3000\"));\n        assert!(!is_maincloud_host(\"https://testnet.spacetimedb.com\"));\n    }\n\n    #[test]\n    fn test_glob_filter_matches_pattern() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"server\".to_string(), serde_json::json!(\"local\"));\n\n        let spacetime_config = make_config_with_children(\n            parent_fields,\n            vec![\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"region-1\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"region-2\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"global\"));\n                    m\n                }),\n            ],\n        );\n\n        // Glob: region-* should match region-1 and region-2 but not global\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"region-*\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 2);\n    }\n\n    #[test]\n    fn test_publish_filter_inherits_parent_fields() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let mut parent_fields = HashMap::new();\n        parent_fields.insert(\"database\".to_string(), serde_json::json!(\"parent-db\"));\n        parent_fields.insert(\"server\".to_string(), serde_json::json!(\"local\"));\n\n        let mut child_fields = HashMap::new();\n        child_fields.insert(\"database\".to_string(), serde_json::json!(\"child-db\"));\n        // child does NOT set \"server\" — should inherit from parent\n\n        let spacetime_config = make_config_with_children(parent_fields, vec![make_config(child_fields)]);\n\n        // Filter to the child target\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"child-db\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 1);\n        // The child should have inherited \"server\" from the parent\n        assert_eq!(\n            filtered[0].get_one::<String>(\"server\").unwrap(),\n            Some(\"local\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_glob_star_matches_all() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let spacetime_config = make_config_with_children(\n            HashMap::new(),\n            vec![\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"alpha\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"beta\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"gamma\"));\n                    m\n                }),\n            ],\n        );\n\n        // Glob: * should match all databases\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"*\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 3);\n    }\n\n    #[test]\n    fn test_glob_exact_match() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let spacetime_config = make_config_with_children(\n            HashMap::new(),\n            vec![\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"region-1\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"region-2\"));\n                    m\n                }),\n            ],\n        );\n\n        // Exact match should return only one\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"region-1\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 1);\n        assert_eq!(\n            filtered[0].get_one::<String>(\"database\").unwrap(),\n            Some(\"region-1\".to_string())\n        );\n    }\n\n    #[test]\n    fn test_glob_multiple_wildcards() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let spacetime_config = make_config_with_children(\n            HashMap::new(),\n            vec![\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"us-east-prod\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"us-west-prod\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"eu-east-staging\"));\n                    m\n                }),\n                make_config({\n                    let mut m = HashMap::new();\n                    m.insert(\"database\".to_string(), serde_json::json!(\"us-east-staging\"));\n                    m\n                }),\n            ],\n        );\n\n        // Pattern with multiple wildcards: *-east-*\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"*-east-*\"]);\n        let filtered = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches).unwrap();\n\n        assert_eq!(filtered.len(), 3); // us-east-prod, eu-east-staging, us-east-staging\n    }\n\n    #[test]\n    fn test_glob_empty_pattern_error() {\n        use std::collections::HashMap;\n\n        let cmd = cli();\n        let schema = build_publish_schema(&cmd).unwrap();\n\n        let spacetime_config = make_config_with_children(\n            HashMap::new(),\n            vec![make_config({\n                let mut m = HashMap::new();\n                m.insert(\"database\".to_string(), serde_json::json!(\"my-db\"));\n                m\n            })],\n        );\n\n        // Empty string as pattern — won't match anything\n        let matches = cmd.clone().get_matches_from(vec![\"publish\", \"\"]);\n        let result = get_filtered_publish_configs(&spacetime_config, &cmd, &schema, &matches);\n        assert!(result.is_err());\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/repl.rs",
    "content": "use crate::api::{ClientApi, Connection};\nuse crate::sql::run_sql;\nuse colored::*;\nuse dirs::home_dir;\nuse std::env::temp_dir;\n\nuse rustyline::completion::Completer;\nuse rustyline::error::ReadlineError;\nuse rustyline::highlight::Highlighter;\nuse rustyline::hint::Hinter;\nuse rustyline::history::DefaultHistory;\nuse rustyline::validate::{MatchingBracketValidator, Validator};\nuse rustyline::{Editor, Helper};\n\nuse syntect::easy::HighlightLines;\nuse syntect::highlighting::{Theme, ThemeSet};\nuse syntect::parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder};\nuse syntect::util::LinesWithEndings;\n\nstatic SQL_SYNTAX: &str = include_str!(\"../../tools/sublime/SpacetimeDBSQL.sublime-syntax\");\nstatic SYNTAX_NAME: &str = \"SQL (SpacetimeDB)\";\n\nstatic AUTO_COMPLETE: &str = \"\\\ntrue\nfalse\nselect\nfrom\ninsert\ninto\nvalues\nupdate,\ndelete,\ncreate,\nwhere\njoin\nsort by\n.exit\n.clear\n\";\n\npub async fn exec(con: Connection) -> Result<(), anyhow::Error> {\n    let database = con.database.clone();\n    let mut rl = Editor::<ReplHelper, DefaultHistory>::new().unwrap();\n    let history = home_dir().unwrap_or_else(temp_dir).join(\".stdb.history.txt\");\n    if rl.load_history(&history).is_err() {\n        eprintln!(\"No previous history.\");\n    }\n    rl.set_helper(Some(ReplHelper::new().unwrap()));\n\n    println!(\n        \"\\\n┌──────────────────────────────────────────────────────────┐\n│ .exit: Exit the REPL                                     │\n│ .clear: Clear the Screen                                 │\n│                                                          │\n│ Give us feedback in our Discord server:                  │\n│    https://discord.gg/w2DVqNZXdN                         │\n└──────────────────────────────────────────────────────────┘\",\n    );\n\n    let api = ClientApi::new(con);\n\n    loop {\n        let readline = rl.readline(&format!(\"🪐{}>\", &database).green());\n        match readline {\n            Ok(line) => match line.as_str() {\n                \".exit\" => break,\n                \".clear\" => {\n                    rl.clear_screen().ok();\n                }\n                sql => {\n                    rl.add_history_entry(sql).ok();\n\n                    if let Err(err) = run_sql(api.sql(), sql, true).await {\n                        eprintln!(\"{}\", err.to_string().red())\n                    }\n                }\n            },\n            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {\n                println!(\"\\n{}\", \"Aborted!\".red());\n                break;\n            }\n            x => {\n                eprintln!(\"\\nUnexpected: {x:?}\");\n                break;\n            }\n        }\n    }\n\n    rl.save_history(&history).ok();\n\n    Ok(())\n}\n\npub(crate) struct ReplHelper {\n    syntaxes: SyntaxSet,\n    theme: Theme,\n    brackets: MatchingBracketValidator,\n}\n\nimpl ReplHelper {\n    pub fn new() -> Result<Self, ()> {\n        let syntax_def = SyntaxDefinition::load_from_str(SQL_SYNTAX, false, Some(SYNTAX_NAME)).unwrap();\n        let mut builder = SyntaxSetBuilder::new();\n        builder.add(syntax_def);\n\n        let syntaxes = builder.build();\n\n        let _ps = SyntaxSet::load_defaults_newlines();\n        let ts = ThemeSet::load_defaults();\n        let theme = ts.themes[\"base16-ocean.dark\"].clone();\n\n        Ok(ReplHelper {\n            syntaxes,\n            theme,\n            brackets: MatchingBracketValidator::new(),\n        })\n    }\n}\n\nimpl Helper for ReplHelper {}\n\nimpl Completer for ReplHelper {\n    type Candidate = String;\n\n    fn complete(\n        &self,\n        line: &str,\n        pos: usize,\n        _: &rustyline::Context<'_>,\n    ) -> rustyline::Result<(usize, Vec<Self::Candidate>)> {\n        let mut name = String::new();\n        let mut name_pos = pos;\n        while let Some(char) = line\n            .chars()\n            .nth(name_pos.wrapping_sub(1))\n            .filter(|c| c.is_ascii_alphanumeric() || ['_', '.'].contains(c))\n        {\n            name.push(char);\n            name_pos -= 1;\n        }\n        if name.is_empty() {\n            return Ok((0, vec![]));\n        }\n        name = name.chars().rev().collect();\n\n        let completions: Vec<_> = AUTO_COMPLETE\n            .split('\\n')\n            .filter(|it| it.starts_with(&name))\n            .map(str::to_owned)\n            .collect();\n\n        Ok((name_pos, completions))\n    }\n}\n\nimpl Hinter for ReplHelper {\n    type Hint = String;\n\n    fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<Self::Hint> {\n        if line.len() > pos {\n            return None;\n        }\n        if let Ok((mut completion_pos, completions)) = self.complete(line, pos, ctx) {\n            if completions.is_empty() {\n                return None;\n            }\n            let mut hint = completions[0].clone();\n            while completion_pos < pos {\n                if hint.is_empty() {\n                    return None;\n                }\n                hint.remove(0);\n                completion_pos += 1;\n            }\n            Some(hint)\n        } else {\n            None\n        }\n    }\n}\n\nimpl Highlighter for ReplHelper {\n    fn highlight<'l>(&self, line: &'l str, _: usize) -> std::borrow::Cow<'l, str> {\n        let mut h = HighlightLines::new(self.syntaxes.find_syntax_by_name(SYNTAX_NAME).unwrap(), &self.theme);\n        let mut out = String::new();\n        for line in LinesWithEndings::from(line) {\n            let ranges = h.highlight_line(line, &self.syntaxes).unwrap();\n            let escaped = syntect::util::as_24_bit_terminal_escaped(&ranges[..], false);\n            out += &escaped;\n        }\n        std::borrow::Cow::Owned(out)\n    }\n\n    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(&'s self, prompt: &'p str, _: bool) -> std::borrow::Cow<'b, str> {\n        std::borrow::Cow::Owned(prompt.green().to_string())\n    }\n\n    fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> {\n        std::borrow::Cow::Owned(hint.bright_black().to_string())\n    }\n\n    fn highlight_candidate<'c>(&self, candidate: &'c str, _: rustyline::CompletionType) -> std::borrow::Cow<'c, str> {\n        std::borrow::Cow::Owned(candidate.bright_cyan().to_string())\n    }\n\n    fn highlight_char(&self, _: &str, _: usize) -> bool {\n        true\n    }\n}\n\nimpl Validator for ReplHelper {\n    fn validate(\n        &self,\n        ctx: &mut rustyline::validate::ValidationContext,\n    ) -> rustyline::Result<rustyline::validate::ValidationResult> {\n        self.brackets.validate(ctx)\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/server.rs",
    "content": "use crate::{\n    common_args,\n    util::{host_or_url_to_host_and_protocol, spacetime_server_fingerprint, y_or_n, UNSTABLE_WARNING, VALID_PROTOCOLS},\n    Config,\n};\nuse anyhow::Context;\nuse clap::{Arg, ArgAction, ArgMatches, Command};\nuse spacetimedb_paths::{server::ServerDataDir, SpacetimePaths};\nuse tabled::{\n    settings::{object::Columns, Alignment, Modify, Style},\n    Table, Tabled,\n};\n\npub fn cli() -> Command {\n    Command::new(\"server\")\n        .args_conflicts_with_subcommands(true)\n        .subcommand_required(true)\n        .subcommands(get_subcommands())\n        .about(format!(\n            \"Manage the connection to the SpacetimeDB server. {UNSTABLE_WARNING}\"\n        ))\n}\n\nfn get_subcommands() -> Vec<Command> {\n    vec![\n        Command::new(\"list\").about(\"List stored server configurations\"),\n        Command::new(\"set-default\")\n            .about(\"Set the default server for future operations\")\n            .arg(\n                Arg::new(\"server\")\n                    .help(\"The nickname, host name or URL of the new default server\")\n                    .required(true),\n            ),\n        Command::new(\"add\")\n            .about(\"Add a new server configuration\")\n            .arg(\n                Arg::new(\"url\")\n                    .long(\"url\")\n                    .help(\"The URL of the server to add\")\n                    .required(true),\n            )\n            .arg(Arg::new(\"name\").help(\"Nickname for this server\").required(true))\n            .arg(\n                Arg::new(\"default\")\n                    .help(\"Make the new server the default server for future operations\")\n                    .long(\"default\")\n                    .short('d')\n                    .action(ArgAction::SetTrue),\n            )\n            .arg(\n                Arg::new(\"no-fingerprint\")\n                    .help(\"Skip fingerprinting the server\")\n                    .long(\"no-fingerprint\")\n                    .action(ArgAction::SetTrue),\n            ),\n        Command::new(\"remove\")\n            .about(\"Remove a saved server configuration\")\n            .arg(\n                Arg::new(\"server\")\n                    .help(\"The nickname, host name or URL of the server to remove\")\n                    .required(true),\n            )\n            .arg(common_args::yes()),\n        Command::new(\"fingerprint\")\n            .about(\"Show or update a saved server's fingerprint\")\n            .arg(\n                Arg::new(\"server\")\n                    .required(true)\n                    .help(\"The nickname, host name or URL of the server\"),\n            )\n            .arg(common_args::yes()),\n        Command::new(\"ping\")\n            .about(\"Checks to see if a SpacetimeDB host is online\")\n            .arg(\n                Arg::new(\"server\")\n                    .required(true)\n                    .help(\"The nickname, host name or URL of the server to ping\"),\n            ),\n        Command::new(\"edit\")\n            .about(\"Update a saved server's nickname, host name or protocol\")\n            .arg(\n                Arg::new(\"server\")\n                    .required(true)\n                    .help(\"The nickname, host name or URL of the server\"),\n            )\n            .arg(\n                Arg::new(\"nickname\")\n                    .help(\"A new nickname to assign the server configuration\")\n                    .long(\"new-name\"),\n            )\n            .arg(\n                Arg::new(\"url\")\n                    .long(\"url\")\n                    .help(\"A new URL to assign the server configuration\"),\n            )\n            .arg(\n                Arg::new(\"no-fingerprint\")\n                    .help(\"Skip fingerprinting the server\")\n                    .long(\"no-fingerprint\")\n                    .action(ArgAction::SetTrue),\n            )\n            .arg(common_args::yes()),\n        Command::new(\"clear\")\n            .about(\"Deletes all data from all local databases\")\n            .arg(\n                Arg::new(\"data_dir\")\n                    .long(\"data-dir\")\n                    .help(\"The path to the server data directory to clear [default: that of the selected spacetime instance]\")\n                    .value_parser(clap::value_parser!(ServerDataDir)),\n            )\n            .arg(common_args::yes()),\n        // TODO: set-name, set-protocol, set-host, set-url\n    ]\n}\n\npub async fn exec(config: Config, paths: &SpacetimePaths, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let (cmd, subcommand_args) = args.subcommand().expect(\"Subcommand required\");\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n    exec_subcommand(config, paths, cmd, subcommand_args).await\n}\n\nasync fn exec_subcommand(\n    config: Config,\n    paths: &SpacetimePaths,\n    cmd: &str,\n    args: &ArgMatches,\n) -> Result<(), anyhow::Error> {\n    match cmd {\n        \"list\" => exec_list(config, args).await,\n        \"set-default\" => exec_set_default(config, args).await,\n        \"add\" => exec_add(config, args).await,\n        \"remove\" => exec_remove(config, args).await,\n        \"fingerprint\" => exec_fingerprint(config, args).await,\n        \"ping\" => exec_ping(config, args).await,\n        \"edit\" => exec_edit(config, args).await,\n        \"clear\" => exec_clear(config, paths, args).await,\n        unknown => Err(anyhow::anyhow!(\"Invalid subcommand: {unknown}\")),\n    }\n}\n\n#[derive(Tabled)]\n#[tabled(rename_all = \"UPPERCASE\")]\nstruct LsRow {\n    default: String,\n    hostname: String,\n    protocol: String,\n    nickname: String,\n}\n\npub async fn exec_list(config: Config, _args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let mut rows: Vec<LsRow> = Vec::new();\n    for server_config in config.server_configs() {\n        let default = if let Some(default_name) = config.default_server_name() {\n            server_config.nick_or_host_or_url_is(default_name)\n        } else {\n            false\n        };\n        rows.push(LsRow {\n            default: if default { \"***\" } else { \"\" }.to_string(),\n            hostname: server_config.host.to_string(),\n            protocol: server_config.protocol.to_string(),\n            nickname: server_config.nickname.as_deref().unwrap_or(\"\").to_string(),\n        });\n    }\n\n    let mut table = Table::new(&rows);\n    table\n        .with(Style::empty())\n        .with(Modify::new(Columns::first()).with(Alignment::right()));\n    println!(\"{table}\");\n\n    Ok(())\n}\n\npub async fn exec_set_default(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server = args.get_one::<String>(\"server\").unwrap();\n    config.set_default_server(server)?;\n    config.save();\n    Ok(())\n}\n\nfn valid_protocol_or_error(protocol: &str) -> anyhow::Result<()> {\n    if !VALID_PROTOCOLS.contains(&protocol) {\n        Err(anyhow::anyhow!(\"Invalid protocol: {protocol}\"))\n    } else {\n        Ok(())\n    }\n}\n\npub async fn exec_add(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    // Trim trailing `/`s because otherwise we end up with a double `//` in some later codepaths.\n    // See https://github.com/clockworklabs/SpacetimeDB/issues/1551.\n    let url = args.get_one::<String>(\"url\").unwrap().trim_end_matches('/');\n    let nickname = args.get_one::<String>(\"name\");\n    let default = *args.get_one::<bool>(\"default\").unwrap();\n    let no_fingerprint = *args.get_one::<bool>(\"no-fingerprint\").unwrap();\n\n    let (host, protocol) = host_or_url_to_host_and_protocol(url);\n    let protocol = protocol.ok_or_else(|| anyhow::anyhow!(\"Invalid url: {url}\"))?;\n\n    valid_protocol_or_error(protocol)?;\n\n    let fingerprint = if no_fingerprint {\n        None\n    } else {\n        let fingerprint = spacetime_server_fingerprint(url).await.with_context(|| {\n            format!(\n                \"Unable to retrieve fingerprint for server: {url}\nIs the server running?\nAdd a server without retrieving its fingerprint with:\n\\tspacetime server add --url {url} --no-fingerprint\",\n            )\n        })?;\n        println!(\"For server {url}, got fingerprint:\\n{fingerprint}\");\n        Some(fingerprint)\n    };\n\n    config.add_server(host.to_string(), protocol.to_string(), fingerprint, nickname.cloned())?;\n\n    if default {\n        config.set_default_server(host)?;\n    }\n\n    println!(\"Host: {host}\");\n    println!(\"Protocol: {protocol}\");\n\n    config.save();\n\n    Ok(())\n}\n\npub async fn exec_remove(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server = args.get_one::<String>(\"server\").unwrap();\n\n    config.remove_server(server)?;\n\n    config.save();\n\n    Ok(())\n}\n\nasync fn update_server_fingerprint(config: &mut Config, server: Option<&str>) -> Result<bool, anyhow::Error> {\n    let url = config.get_host_url(server)?;\n    let nick_or_host = config.server_nick_or_host(server)?;\n    let new_fing = spacetime_server_fingerprint(&url)\n        .await\n        .context(\"Error fetching server fingerprint\")?;\n    if let Some(saved_fing) = config.server_fingerprint(server)? {\n        if saved_fing == new_fing {\n            println!(\"Fingerprint is unchanged for server {nick_or_host}:\\n{saved_fing}\");\n\n            Ok(false)\n        } else {\n            println!(\"Fingerprint has changed for server {nick_or_host}.\\nWas:\\n{saved_fing}\\nNew:\\n{new_fing}\");\n\n            config.set_server_fingerprint(server, new_fing)?;\n\n            Ok(true)\n        }\n    } else {\n        println!(\"No saved fingerprint for server {nick_or_host}. New fingerprint:\\n{new_fing}\");\n\n        config.set_server_fingerprint(server, new_fing)?;\n\n        Ok(true)\n    }\n}\n\npub async fn exec_fingerprint(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server = args.get_one::<String>(\"server\").unwrap().as_str();\n    let force = args.get_flag(\"force\");\n\n    if update_server_fingerprint(&mut config, Some(server)).await? {\n        if !y_or_n(force, \"Continue?\")? {\n            anyhow::bail!(\"Aborted\");\n        }\n\n        config.save();\n    }\n\n    Ok(())\n}\n\npub async fn exec_ping(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server = args.get_one::<String>(\"server\").unwrap().as_str();\n    let url = config.get_host_url(Some(server))?;\n\n    let builder = reqwest::Client::new().get(format!(\"{url}/v1/ping\").as_str());\n    let response = builder.send().await?;\n\n    match response.status() {\n        reqwest::StatusCode::OK => {\n            println!(\"Server is online: {url}\");\n        }\n        reqwest::StatusCode::NOT_FOUND => {\n            println!(\"Server returned 404 (Not Found): {url}\");\n        }\n        err => {\n            println!(\"Server could not be reached ({err}): {url}\");\n        }\n    }\n    Ok(())\n}\n\npub async fn exec_edit(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let server = args.get_one::<String>(\"server\").unwrap().as_str();\n\n    let old_url = config.get_host_url(Some(server))?;\n\n    let new_nick = args.get_one::<String>(\"nickname\").map(|s| s.as_str());\n    let new_url = args.get_one::<String>(\"url\").map(|s| s.as_str());\n    let (new_host, new_proto) = match new_url {\n        None => (None, None),\n        Some(new_url) => {\n            let (new_host, new_proto) = host_or_url_to_host_and_protocol(new_url);\n            let new_proto = new_proto.ok_or_else(|| anyhow::anyhow!(\"Invalid url: {new_url}\"))?;\n            (Some(new_host), Some(new_proto))\n        }\n    };\n\n    let no_fingerprint = args.get_flag(\"no-fingerprint\");\n    let force = args.get_flag(\"force\");\n\n    if let Some(new_proto) = new_proto {\n        valid_protocol_or_error(new_proto)?;\n    }\n\n    let (old_nick, old_host, old_proto) = config.edit_server(server, new_nick, new_host, new_proto)?;\n    let server = new_nick.unwrap_or(server);\n\n    if let (Some(new_nick), Some(old_nick)) = (new_nick, old_nick) {\n        println!(\"Changing nickname from {old_nick} to {new_nick}\");\n    }\n    if let (Some(new_host), Some(old_host)) = (new_host, old_host) {\n        println!(\"Changing host from {old_host} to {new_host}\");\n    }\n    if let (Some(new_proto), Some(old_proto)) = (new_proto, old_proto) {\n        println!(\"Changing protocol from {old_proto} to {new_proto}\");\n    }\n\n    let new_url = config.get_host_url(Some(server))?;\n\n    if old_url != new_url {\n        if no_fingerprint {\n            config.delete_server_fingerprint(Some(&new_url))?;\n        } else {\n            update_server_fingerprint(&mut config, Some(&new_url)).await?;\n        }\n    }\n\n    if !y_or_n(force, \"Continue?\")? {\n        anyhow::bail!(\"Aborted\");\n    }\n\n    config.save();\n\n    Ok(())\n}\n\nasync fn exec_clear(_config: Config, paths: &SpacetimePaths, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    let force = args.get_flag(\"force\");\n    let data_dir = args.get_one::<ServerDataDir>(\"data_dir\").unwrap_or(&paths.data_dir);\n\n    if data_dir.0.exists() {\n        println!(\"Database path: {}\", data_dir.display());\n\n        if !y_or_n(\n            force,\n            \"Are you sure you want to delete all data from the local database?\",\n        )? {\n            println!(\"Aborting\");\n            return Ok(());\n        }\n\n        std::fs::remove_dir_all(data_dir)?;\n        println!(\"Deleted database: {}\", data_dir.display());\n    } else {\n        println!(\"Local database not found. Nothing has been deleted.\");\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/sql.rs",
    "content": "use std::fmt;\nuse std::fmt::Write;\nuse std::time::{Duration, Instant};\n\nuse crate::api::{from_json_seed, ClientApi, Connection, SqlStmtResult, StmtStats};\nuse crate::common_args;\nuse crate::config::Config;\nuse crate::subcommands::db_arg_resolution::{\n    load_config_db_targets, resolve_database_arg, resolve_optional_database_parts, ResolvedDbArgs,\n};\nuse crate::util::{database_identity, get_auth_header, ResponseExt, UNSTABLE_WARNING};\nuse anyhow::Context;\nuse clap::{Arg, ArgAction, ArgMatches};\nuse reqwest::RequestBuilder;\nuse spacetimedb_lib::de::serde::SeedWrapper;\nuse spacetimedb_lib::sats::satn::PsqlClient;\nuse spacetimedb_lib::sats::{satn, ProductType, ProductValue, Typespace};\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"sql\")\n        .about(format!(\"Runs a SQL query on the database. {UNSTABLE_WARNING}\"))\n        .arg(\n            Arg::new(\"sql_parts\")\n                .num_args(0..)\n                .help(\"SQL arguments: [DATABASE] <QUERY>\"),\n        )\n        .arg(\n            Arg::new(\"interactive\")\n                .long(\"interactive\")\n                .action(ArgAction::SetTrue)\n                .help(\"Instead of using a query, run an interactive command prompt for `SQL` expressions\"),\n        )\n        .arg(common_args::confirmed())\n        .arg(common_args::anonymous())\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server hosting the database\"))\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n}\n\npub(crate) async fn parse_req(\n    mut config: Config,\n    args: &ArgMatches,\n    database_name_or_identity: &str,\n    server_from_config: Option<&str>,\n) -> Result<Connection, anyhow::Error> {\n    let server_from_cli = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let force = args.get_flag(\"force\");\n    let anon_identity = args.get_flag(\"anon_identity\");\n    let server = server_from_cli.or(server_from_config);\n\n    Ok(Connection {\n        host: config.get_host_url(server)?,\n        auth_header: get_auth_header(&mut config, anon_identity, server, !force).await?,\n        database_identity: database_identity(&config, database_name_or_identity, server).await?,\n        database: database_name_or_identity.to_string(),\n    })\n}\n\nstruct StmtResult {\n    table: tabled::Table,\n    stats: Option<StmtStats>,\n}\n\nimpl fmt::Display for StmtResult {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let has_table = !self.table.is_empty();\n        if has_table {\n            write!(f, \"{}\", self.table)?;\n        }\n\n        if let Some(stats) = &self.stats {\n            if has_table {\n                writeln!(f)?;\n            }\n            let txt = if stats.total_rows == 1 { \"row\" } else { \"rows\" };\n\n            let result = format!(\"({} {txt})\", stats.total_rows);\n            let mut info = Vec::new();\n            if stats.rows_inserted != 0 {\n                info.push(format!(\"inserted: {}\", stats.rows_inserted));\n            }\n            if stats.rows_deleted != 0 {\n                info.push(format!(\"deleted: {}\", stats.rows_deleted));\n            }\n            if stats.rows_updated != 0 {\n                info.push(format!(\"updated: {}\", stats.rows_updated));\n            }\n            info.push(format!(\n                \"server: {:.2?}\",\n                std::time::Duration::from_micros(stats.total_duration_micros)\n            ));\n\n            if !info.is_empty() {\n                write!(f, \"{result} [{info}]\", info = info.join(\", \"))?;\n            } else {\n                write!(f, \"{result}\")?;\n            };\n        };\n        Ok(())\n    }\n}\n\nfn print_stmt_result(\n    stmt_results: &[SqlStmtResult],\n    with_stats: Option<Duration>,\n    f: &mut String,\n) -> anyhow::Result<()> {\n    let if_empty: Option<anyhow::Result<StmtResult>> = stmt_results.is_empty().then_some(anyhow::Ok(StmtResult {\n        stats: with_stats.is_some().then_some(StmtStats::default()),\n        table: tabled::Table::new([\"\"]),\n    }));\n    let total = stmt_results.len();\n    for (pos, result) in if_empty\n        .into_iter()\n        .chain(stmt_results.iter().map(|stmt_result| {\n            let (stats, table) = stmt_result_to_table(PsqlClient::SpacetimeDB, stmt_result)?;\n\n            anyhow::Ok(StmtResult {\n                stats: with_stats.is_some().then_some(stats),\n                table,\n            })\n        }))\n        .enumerate()\n    {\n        let result = result?;\n        f.write_str(&format!(\"{result}\"))?;\n        if pos + 1 < total {\n            f.write_char('\\n')?;\n            f.write_char('\\n')?;\n        }\n    }\n\n    if let Some(with_stats) = with_stats {\n        f.write_char('\\n')?;\n        f.write_str(&format!(\"Roundtrip time: {with_stats:.2?}\"))?;\n        f.write_char('\\n')?;\n    }\n    Ok(())\n}\n\npub(crate) async fn run_sql(builder: RequestBuilder, sql: &str, with_stats: bool) -> Result<(), anyhow::Error> {\n    let now = Instant::now();\n\n    let json = builder\n        .body(sql.to_owned())\n        .send()\n        .await?\n        .ensure_content_type(\"application/json\")\n        .await?\n        .text()\n        .await?;\n\n    let stmt_result_json: Vec<SqlStmtResult> = serde_json::from_str(&json).context(\"malformed sql response\")?;\n\n    let mut out = String::new();\n    print_stmt_result(&stmt_result_json, with_stats.then_some(now.elapsed()), &mut out)?;\n    println!(\"{out}\");\n\n    Ok(())\n}\n\nfn stmt_result_to_table(client: PsqlClient, stmt_result: &SqlStmtResult) -> anyhow::Result<(StmtStats, tabled::Table)> {\n    let stats = StmtStats::from(stmt_result);\n    let SqlStmtResult { schema, rows, .. } = stmt_result;\n    let ty = Typespace::EMPTY.with_type(schema);\n\n    let table = build_table(\n        client,\n        schema,\n        rows.iter().map(|row| from_json_seed(row.get(), SeedWrapper(ty))),\n    )?;\n\n    Ok((stats, table))\n}\n\npub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n    let interactive = args.get_one::<bool>(\"interactive\").unwrap_or(&false);\n    let no_config = args.get_flag(\"no_config\");\n    let raw_parts: Vec<String> = args\n        .get_many::<String>(\"sql_parts\")\n        .map(|vals| vals.cloned().collect())\n        .unwrap_or_default();\n    let config_targets = load_config_db_targets(no_config)?;\n\n    if *interactive {\n        if raw_parts.len() > 1 {\n            anyhow::bail!(\n                \"Too many positional arguments for interactive mode.\\nUsage: spacetime sql [database] --interactive [--no-config]\"\n            );\n        }\n        let resolved = resolve_database_arg(\n            raw_parts.first().map(|s| s.as_str()),\n            config_targets.as_deref(),\n            \"spacetime sql [database] --interactive [--no-config]\",\n        )?;\n        let con = parse_req(config, args, &resolved.database, resolved.server.as_deref()).await?;\n\n        crate::repl::exec(con).await?;\n    } else {\n        let resolved = resolve_optional_database_parts(\n            &raw_parts,\n            config_targets.as_deref(),\n            \"query\",\n            \"spacetime sql [database] <query> [--no-config]\",\n        )\n        .or_else(|e| {\n            // `sql` expects exactly 1 query arg, so if we have 2+ positional args the first\n            // must be a database name. If it didn't match any config target, treat it as an\n            // ad-hoc database outside the project (auto-fallthrough).\n            if raw_parts.len() >= 2 {\n                Ok(ResolvedDbArgs {\n                    database: raw_parts[0].clone(),\n                    server: None,\n                    remaining_args: raw_parts[1..].to_vec(),\n                })\n            } else if raw_parts.len() == 1 && raw_parts[0].contains(' ') {\n                // The single arg contains spaces, so it's almost certainly a SQL query,\n                // not a database name. Give a clearer error than \"missing <query>\".\n                let targets = config_targets.as_deref().unwrap_or_default();\n                let known: Vec<&str> = targets.iter().map(|t| t.database.as_str()).collect();\n                Err(anyhow::anyhow!(\n                    \"Multiple databases found in config: {}. Please specify which database to query:\\n  \\\n                     spacetime sql <database> \\\"{}\\\"\",\n                    known.join(\", \"),\n                    raw_parts[0]\n                ))\n            } else {\n                Err(e)\n            }\n        })?;\n        let query = resolved.remaining_args.join(\" \");\n        let confirmed = args.get_one::<bool>(\"confirmed\").copied();\n\n        let con = parse_req(config, args, &resolved.database, resolved.server.as_deref()).await?;\n        let mut api = ClientApi::new(con).sql();\n        if let Some(confirmed) = confirmed {\n            api = api.query(&[(\"confirmed\", if confirmed { \"true\" } else { \"false\" })]);\n        }\n\n        run_sql(api, &query, false).await?;\n    }\n    Ok(())\n}\n\n/// Generates a [`tabled::Table`] from a schema and rows, using the style of a psql table.\nfn build_table<E>(\n    client: PsqlClient,\n    schema: &ProductType,\n    rows: impl Iterator<Item = Result<ProductValue, E>>,\n) -> Result<tabled::Table, E> {\n    let mut builder = tabled::builder::Builder::default();\n    builder.set_header(schema.elements.iter().enumerate().map(|(i, e)| {\n        e.name\n            .as_ref()\n            .map(|n| n.to_string())\n            .unwrap_or_else(|| format!(\"column {i}\"))\n    }));\n\n    let ty = Typespace::EMPTY.with_type(schema);\n    for row in rows {\n        let row = row?;\n        builder.push_record(ty.with_values(&row).enumerate().map(|(idx, value)| {\n            let ty = satn::PsqlType {\n                client,\n                tuple: ty.ty(),\n                field: &ty.ty().elements[idx],\n                idx,\n            };\n\n            satn::PsqlWrapper { ty, value }.to_string()\n        }));\n    }\n\n    let mut table = builder.build();\n    table.with(tabled::settings::Style::psql());\n\n    Ok(table)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use itertools::Itertools;\n    use serde_json::value::RawValue;\n    use spacetimedb_client_api_messages::http::SqlStmtStats;\n    use spacetimedb_lib::error::ResultTest;\n    use spacetimedb_lib::sats::time_duration::TimeDuration;\n    use spacetimedb_lib::sats::timestamp::Timestamp;\n    use spacetimedb_lib::sats::{product, GroundSpacetimeType, ProductType};\n    use spacetimedb_lib::{AlgebraicType, AlgebraicValue, ConnectionId, Identity, Uuid};\n\n    fn make_row(row: &[AlgebraicValue]) -> Result<Box<RawValue>, serde_json::Error> {\n        let json = serde_json::json!(row);\n        RawValue::from_string(json.to_string())\n    }\n\n    fn check_outputs(\n        result: &[SqlStmtResult],\n        duration: Option<Duration>,\n        expect: &str,\n    ) -> Result<String, anyhow::Error> {\n        let mut out = String::new();\n        print_stmt_result(result, duration, &mut out)?;\n\n        // Need to trim the output to because rustfmt remove the `expect` spaces\n        let out = out.lines().map(|line| line.trim_end()).join(\"\\n\");\n        assert_eq!(out, expect,);\n\n        Ok(out)\n    }\n\n    fn check_output(\n        schema: ProductType,\n        rows: Vec<&RawValue>,\n        stats: SqlStmtStats,\n        duration: Option<Duration>,\n        expect: &str,\n    ) -> Result<String, anyhow::Error> {\n        let table = SqlStmtResult {\n            schema: schema.clone(),\n            rows,\n            total_duration_micros: 1000,\n            stats: stats.clone(),\n        };\n\n        let mut out = String::new();\n        print_stmt_result(&[table], duration, &mut out)?;\n\n        // Need to trim the output to because rustfmt remove the `expect` spaces\n        let out = out.lines().map(|line| line.trim_end()).join(\"\\n\");\n        assert_eq!(out, expect,);\n\n        Ok(out)\n    }\n\n    #[test]\n    fn test_output() -> Result<(), anyhow::Error> {\n        let duration = Duration::from_micros(1000);\n        let schema = ProductType::from([(\"a\", AlgebraicType::I32), (\"b\", AlgebraicType::I64)]);\n        let row = make_row(&[AlgebraicValue::I32(1), AlgebraicValue::I64(2)])?;\n        // Verify with and without stats\n        check_output(\n            schema.clone(),\n            vec![&row],\n            SqlStmtStats {\n                rows_inserted: 1,\n                rows_deleted: 1,\n                rows_updated: 1,\n            },\n            None,\n            r#\" a | b\n---+---\n 1 | 2\"#,\n        )?;\n\n        check_output(\n            schema.clone(),\n            vec![&row],\n            SqlStmtStats {\n                rows_inserted: 1,\n                rows_deleted: 1,\n                rows_updated: 1,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n 1 | 2\n(1 row) [inserted: 1, deleted: 1, updated: 1, server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        // Only a query result\n        check_output(\n            schema.clone(),\n            vec![&row],\n            SqlStmtStats {\n                rows_inserted: 0,\n                rows_deleted: 0,\n                rows_updated: 0,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n 1 | 2\n(1 row) [server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        // Empty table\n        check_output(\n            schema.clone(),\n            vec![],\n            SqlStmtStats {\n                rows_inserted: 0,\n                rows_deleted: 0,\n                rows_updated: 0,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n(0 rows) [server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        // DML\n        check_output(\n            schema.clone(),\n            vec![],\n            SqlStmtStats {\n                rows_inserted: 1,\n                rows_deleted: 0,\n                rows_updated: 0,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n(0 rows) [inserted: 1, server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        check_output(\n            schema.clone(),\n            vec![],\n            SqlStmtStats {\n                rows_inserted: 0,\n                rows_deleted: 1,\n                rows_updated: 0,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n(0 rows) [deleted: 1, server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        check_output(\n            schema.clone(),\n            vec![],\n            SqlStmtStats {\n                rows_inserted: 0,\n                rows_deleted: 0,\n                rows_updated: 1,\n            },\n            Some(duration),\n            r#\" a | b\n---+---\n(0 rows) [updated: 1, server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_multiple_output() -> Result<(), anyhow::Error> {\n        let duration = Duration::from_micros(1000);\n        let schema = ProductType::from([(\"a\", AlgebraicType::I32), (\"b\", AlgebraicType::I64)]);\n        let row = make_row(&[AlgebraicValue::I32(1), AlgebraicValue::I64(2)])?;\n\n        // Verify with and without stats\n        check_outputs(\n            &[\n                SqlStmtResult {\n                    schema: schema.clone(),\n                    rows: vec![&row],\n                    total_duration_micros: 1000,\n                    stats: SqlStmtStats {\n                        rows_inserted: 1,\n                        rows_deleted: 1,\n                        rows_updated: 1,\n                    },\n                },\n                SqlStmtResult {\n                    schema: schema.clone(),\n                    rows: vec![&row],\n                    total_duration_micros: 1000,\n                    stats: SqlStmtStats {\n                        rows_inserted: 1,\n                        rows_deleted: 1,\n                        rows_updated: 1,\n                    },\n                },\n            ],\n            Some(duration),\n            r#\" a | b\n---+---\n 1 | 2\n(1 row) [inserted: 1, deleted: 1, updated: 1, server: 1.00ms]\n\n a | b\n---+---\n 1 | 2\n(1 row) [inserted: 1, deleted: 1, updated: 1, server: 1.00ms]\nRoundtrip time: 1.00ms\"#,\n        )?;\n\n        Ok(())\n    }\n\n    fn expect_psql_table(client: PsqlClient, ty: &ProductType, rows: Vec<ProductValue>, expected: &str) {\n        let table = build_table(client, ty, rows.into_iter().map(Ok::<_, ()>))\n            .unwrap()\n            .to_string();\n        let mut table = table.split('\\n').map(|x| x.trim_end()).join(\"\\n\");\n        table.insert(0, '\\n');\n        assert_eq!(expected, table);\n    }\n\n    // Verify the output of `sql` matches the inputs that return true for [`AlgebraicType::is_special()`]\n    #[test]\n    fn output_special_types() -> ResultTest<()> {\n        // Check tuples\n        let kind: ProductType = [\n            AlgebraicType::String,\n            AlgebraicType::U256,\n            Identity::get_type(),\n            ConnectionId::get_type(),\n            Timestamp::get_type(),\n            TimeDuration::get_type(),\n            Uuid::get_type(),\n        ]\n        .into();\n        let value = product![\n            \"a\",\n            Identity::ZERO.to_u256(),\n            Identity::ZERO,\n            ConnectionId::ZERO,\n            Timestamp::UNIX_EPOCH,\n            TimeDuration::ZERO,\n            Uuid::NIL\n        ];\n\n        expect_psql_table(\n            PsqlClient::SpacetimeDB,\n            &kind,\n            vec![value.clone()],\n            r#\"\n column 0 | column 1 | column 2                                                           | column 3                           | column 4                  | column 5  | column 6\n----------+----------+--------------------------------------------------------------------+------------------------------------+---------------------------+-----------+----------------------------------------\n \"a\"      | 0        | 0x0000000000000000000000000000000000000000000000000000000000000000 | 0x00000000000000000000000000000000 | 1970-01-01T00:00:00+00:00 | +0.000000 | \"00000000-0000-0000-0000-000000000000\"\"#,\n        );\n\n        expect_psql_table(\n            PsqlClient::Postgres,\n            &kind,\n            vec![value],\n            r#\"\n column 0 | column 1 | column 2                                                             | column 3                             | column 4                    | column 5 | column 6\n----------+----------+----------------------------------------------------------------------+--------------------------------------+-----------------------------+----------+----------------------------------------\n \"a\"      | 0        | \"0x0000000000000000000000000000000000000000000000000000000000000000\" | \"0x00000000000000000000000000000000\" | \"1970-01-01T00:00:00+00:00\" | \"P0D\"    | \"00000000-0000-0000-0000-000000000000\"\"#,\n        );\n\n        // Check struct\n        let kind: ProductType = [\n            (\"bool\", AlgebraicType::Bool),\n            (\"str\", AlgebraicType::String),\n            (\"bytes\", AlgebraicType::bytes()),\n            (\"identity\", Identity::get_type()),\n            (\"connection_id\", ConnectionId::get_type()),\n            (\"timestamp\", Timestamp::get_type()),\n            (\"duration\", TimeDuration::get_type()),\n            (\"uuid\", Uuid::get_type()),\n        ]\n        .into();\n\n        let value = product![\n            true,\n            \"This is spacetimedb\".to_string(),\n            AlgebraicValue::Bytes([1, 2, 3, 4, 5, 6, 7].into()),\n            Identity::ZERO,\n            ConnectionId::ZERO,\n            Timestamp::UNIX_EPOCH,\n            TimeDuration::ZERO,\n            Uuid::NIL\n        ];\n\n        expect_psql_table(\n            PsqlClient::SpacetimeDB,\n            &kind,\n            vec![value.clone()],\n            r#\"\n bool | str                   | bytes            | identity                                                           | connection_id                      | timestamp                 | duration  | uuid\n------+-----------------------+------------------+--------------------------------------------------------------------+------------------------------------+---------------------------+-----------+----------------------------------------\n true | \"This is spacetimedb\" | 0x01020304050607 | 0x0000000000000000000000000000000000000000000000000000000000000000 | 0x00000000000000000000000000000000 | 1970-01-01T00:00:00+00:00 | +0.000000 | \"00000000-0000-0000-0000-000000000000\"\"#,\n        );\n\n        expect_psql_table(\n            PsqlClient::Postgres,\n            &kind,\n            vec![value.clone()],\n            r#\"\n bool | str                   | bytes              | identity                                                             | connection_id                        | timestamp                   | duration | uuid\n------+-----------------------+--------------------+----------------------------------------------------------------------+--------------------------------------+-----------------------------+----------+----------------------------------------\n true | \"This is spacetimedb\" | \"0x01020304050607\" | \"0x0000000000000000000000000000000000000000000000000000000000000000\" | \"0x00000000000000000000000000000000\" | \"1970-01-01T00:00:00+00:00\" | \"P0D\"    | \"00000000-0000-0000-0000-000000000000\"\"#,\n        );\n\n        // Check nested struct, tuple...\n        let kind: ProductType = [(None, AlgebraicType::product(kind))].into();\n\n        let value = product![value.clone()];\n\n        expect_psql_table(\n            PsqlClient::SpacetimeDB,\n            &kind,\n            vec![value.clone()],\n            r#\"\n column 0\n---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n (bool = true, str = \"This is spacetimedb\", bytes = 0x01020304050607, identity = 0x0000000000000000000000000000000000000000000000000000000000000000, connection_id = 0x00000000000000000000000000000000, timestamp = 1970-01-01T00:00:00+00:00, duration = +0.000000, uuid = \"00000000-0000-0000-0000-000000000000\")\"#,\n        );\n\n        expect_psql_table(\n            PsqlClient::Postgres,\n            &kind,\n            vec![value.clone()],\n            r#\"\n column 0\n---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n {\"bool\": true, \"str\": \"This is spacetimedb\", \"bytes\": \"0x01020304050607\", \"identity\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", \"connection_id\": \"0x00000000000000000000000000000000\", \"timestamp\": \"1970-01-01T00:00:00+00:00\", \"duration\": \"P0D\", \"uuid\": \"00000000-0000-0000-0000-000000000000\"}\"#,\n        );\n\n        let kind: ProductType = [(\"tuple\", AlgebraicType::product(kind))].into();\n\n        let value = product![value];\n\n        expect_psql_table(\n            PsqlClient::SpacetimeDB,\n            &kind,\n            vec![value.clone()],\n            r#\"\n tuple\n-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n (col_0 = (bool = true, str = \"This is spacetimedb\", bytes = 0x01020304050607, identity = 0x0000000000000000000000000000000000000000000000000000000000000000, connection_id = 0x00000000000000000000000000000000, timestamp = 1970-01-01T00:00:00+00:00, duration = +0.000000, uuid = \"00000000-0000-0000-0000-000000000000\"))\"#,\n        );\n\n        expect_psql_table(\n            PsqlClient::Postgres,\n            &kind,\n            vec![value],\n            r#\"\n tuple\n--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n {\"col_0\": {\"bool\": true, \"str\": \"This is spacetimedb\", \"bytes\": \"0x01020304050607\", \"identity\": \"0x0000000000000000000000000000000000000000000000000000000000000000\", \"connection_id\": \"0x00000000000000000000000000000000\", \"timestamp\": \"1970-01-01T00:00:00+00:00\", \"duration\": \"P0D\", \"uuid\": \"00000000-0000-0000-0000-000000000000\"}}\"#,\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/start.rs",
    "content": "use std::ffi::OsString;\nuse std::io;\nuse std::process::{Command, ExitCode};\n\nuse anyhow::Context;\nuse clap::{Arg, ArgMatches};\nuse spacetimedb_paths::SpacetimePaths;\n\nuse crate::util::resolve_sibling_binary;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"start\")\n        .about(\"Start a local SpacetimeDB instance\")\n        .long_about(\n            \"\\\nStart a local SpacetimeDB instance\n\nRun `spacetime start --help` to see all options.\",\n        )\n        .disable_help_flag(true)\n        .arg(\n            Arg::new(\"edition\")\n                .long(\"edition\")\n                .help(\"The edition of SpacetimeDB to start up\")\n                .value_parser(clap::value_parser!(Edition))\n                .default_value(\"standalone\"),\n        )\n        .arg(\n            Arg::new(\"args\")\n                .help(\"The args to pass to `spacetimedb-{edition} start`\")\n                .value_parser(clap::value_parser!(OsString))\n                .allow_hyphen_values(true)\n                .num_args(0..),\n        )\n}\n\n#[derive(clap::ValueEnum, Clone, Copy)]\nenum Edition {\n    Standalone,\n    Cloud,\n}\n\npub async fn exec(paths: &SpacetimePaths, args: &ArgMatches) -> anyhow::Result<ExitCode> {\n    let edition = args.get_one::<Edition>(\"edition\").unwrap();\n    let args = args.get_many::<OsString>(\"args\").unwrap_or_default();\n    let bin_name = match edition {\n        Edition::Standalone => \"spacetimedb-standalone\",\n        Edition::Cloud => \"spacetimedb-cloud\",\n    };\n    let bin_path = resolve_sibling_binary(bin_name)?;\n    let mut cmd = Command::new(&bin_path);\n    cmd.arg(\"start\")\n        .arg(\"--data-dir\")\n        .arg(&paths.data_dir)\n        .arg(\"--jwt-key-dir\")\n        .arg(&paths.cli_config_dir)\n        .args(args);\n\n    exec_replace(&mut cmd).with_context(|| format!(\"exec failed for {}\", bin_path.display()))\n}\n\n// implementation based on and docs taken verbatim from `cargo_util::ProcessBuilder::exec_replace`\n//\n/// Replaces the current process with the target process.\n///\n/// On Unix, this executes the process using the Unix syscall `execvp`, which will block\n/// this process, and will only return if there is an error.\n///\n/// On Windows this isn't technically possible. Instead we emulate it to the best of our\n/// ability. One aspect we fix here is that we specify a handler for the Ctrl-C handler.\n/// In doing so (and by effectively ignoring it) we should emulate proxying Ctrl-C\n/// handling to the application at hand, which will either terminate or handle it itself.\n/// According to Microsoft's documentation at\n/// <https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals>.\n/// the Ctrl-C signal is sent to all processes attached to a terminal, which should\n/// include our child process. If the child terminates then we'll reap them in Cargo\n/// pretty quickly, and if the child handles the signal then we won't terminate\n/// (and we shouldn't!) until the process itself later exits.\npub(crate) fn exec_replace(cmd: &mut Command) -> io::Result<ExitCode> {\n    #[cfg(unix)]\n    {\n        use std::os::unix::process::CommandExt;\n        // if exec() succeeds, it diverges, so the function just returns an io::Error\n        let err = cmd.exec();\n        Err(err)\n    }\n    #[cfg(windows)]\n    {\n        use windows_sys::Win32::Foundation::{BOOL, FALSE, TRUE};\n        use windows_sys::Win32::System::Console::SetConsoleCtrlHandler;\n\n        unsafe extern \"system\" fn ctrlc_handler(_: u32) -> BOOL {\n            // Do nothing. Let the child process handle it.\n            TRUE\n        }\n        unsafe {\n            if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {\n                return Err(io::Error::other(\"Unable to set console handler\"));\n            }\n        }\n\n        cmd.status()\n            .map(|status| ExitCode::from(status.code().unwrap_or(1).try_into().unwrap_or(1)))\n    }\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/subscribe.rs",
    "content": "use anyhow::Context;\nuse clap::{value_parser, Arg, ArgAction, ArgMatches};\nuse futures::{Sink, SinkExt, TryStream, TryStreamExt};\nuse http::header;\nuse reqwest::Url;\nuse serde_json::Value;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_lib::db::raw_def::v9::RawModuleDefV9;\nuse spacetimedb_lib::de::serde::{DeserializeWrapper, SeedWrapper};\nuse spacetimedb_lib::ser::serde::SerializeWrapper;\nuse std::io;\nuse std::time::Duration;\nuse thiserror::Error;\nuse tokio::io::AsyncWriteExt;\nuse tokio_tungstenite::tungstenite::client::IntoClientRequest;\nuse tokio_tungstenite::tungstenite::{Error as WsError, Message as WsMessage};\n\nuse crate::api::ClientApi;\nuse crate::common_args;\nuse crate::subcommands::db_arg_resolution::{load_config_db_targets, resolve_optional_database_parts};\nuse crate::util::UNSTABLE_WARNING;\nuse crate::util::{database_identity, get_auth_header};\nuse crate::Config;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"subscribe\")\n        .about(format!(\"Subscribe to SQL queries on the database. {UNSTABLE_WARNING}\"))\n        .arg(\n            Arg::new(\"subscribe_parts\")\n                .num_args(1..)\n                .help(\"Subscribe arguments: [DATABASE] <QUERY> [QUERY...]\"),\n        )\n        .arg(\n            Arg::new(\"num-updates\")\n                .required(false)\n                .long(\"num-updates\")\n                .short('n')\n                .action(ArgAction::Set)\n                .value_parser(value_parser!(u32))\n                .help(\"The number of subscription updates to receive before exiting\"),\n        )\n        .arg(\n            Arg::new(\"timeout\")\n                .required(false)\n                .short('t')\n                .long(\"timeout\")\n                .action(ArgAction::Set)\n                .value_parser(value_parser!(u32))\n                .help(\n                    \"The timeout, in seconds, after which to disconnect and stop receiving \\\n                     subscription messages. If `-n` is specified, it will stop after whichever\n                     one comes first.\",\n                ),\n        )\n        .arg(\n            Arg::new(\"print_initial_update\")\n                .required(false)\n                .long(\"print-initial-update\")\n                .action(ArgAction::SetTrue)\n                .help(\"Print the initial update for the queries.\"),\n        )\n        .arg(common_args::confirmed())\n        .arg(common_args::anonymous())\n        .arg(common_args::yes())\n        .arg(\n            Arg::new(\"no_config\")\n                .long(\"no-config\")\n                .action(ArgAction::SetTrue)\n                .help(\"Ignore spacetime.json configuration\"),\n        )\n        .arg(common_args::server().help(\"The nickname, host name or URL of the server hosting the database\"))\n}\n\nfn parse_msg_json(msg: &WsMessage) -> Option<ws_v1::ServerMessage<ws_v1::JsonFormat>> {\n    let WsMessage::Text(msg) = msg else { return None };\n    serde_json::from_str::<DeserializeWrapper<ws_v1::ServerMessage<ws_v1::JsonFormat>>>(msg)\n        .inspect_err(|e| eprintln!(\"couldn't parse message from server: {e}\"))\n        .map(|wrapper| wrapper.0)\n        .ok()\n}\n\nfn reformat_update<'a>(\n    msg: &'a ws_v1::DatabaseUpdate<ws_v1::JsonFormat>,\n    schema: &RawModuleDefV9,\n) -> anyhow::Result<HashMap<&'a str, SubscriptionTable>> {\n    msg.tables\n        .iter()\n        .map(|upd| {\n            let table_ty = schema.typespace.resolve(\n                schema\n                    .type_ref_for_table_like(&upd.table_name)\n                    .context(\"table not found in schema\")?,\n            );\n\n            let reformat_row = |row: &str| -> anyhow::Result<Value> {\n                // TODO: can the following two calls be merged into a single call to reduce allocations?\n                let row = serde_json::from_str::<Value>(row)?;\n                let row = serde::de::DeserializeSeed::deserialize(SeedWrapper(table_ty), row)?;\n                let row = table_ty.with_value(&row);\n                let row = serde_json::to_value(SerializeWrapper::from_ref(&row))?;\n                Ok(row)\n            };\n\n            let mut deletes = Vec::new();\n            let mut inserts = Vec::new();\n            for upd in &upd.updates {\n                for s in &upd.deletes {\n                    deletes.push(reformat_row(s)?);\n                }\n                for s in &upd.inserts {\n                    inserts.push(reformat_row(s)?);\n                }\n            }\n\n            Ok((&*upd.table_name, SubscriptionTable { deletes, inserts }))\n        })\n        .collect()\n}\n\n#[derive(serde::Serialize, Debug)]\nstruct SubscriptionTable {\n    deletes: Vec<serde_json::Value>,\n    inserts: Vec<serde_json::Value>,\n}\n\npub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {\n    eprintln!(\"{UNSTABLE_WARNING}\\n\");\n\n    let server = args.get_one::<String>(\"server\").map(|s| s.as_ref());\n    let force = args.get_flag(\"force\");\n    let anon_identity = args.get_flag(\"anon_identity\");\n    let no_config = args.get_flag(\"no_config\");\n\n    let raw_parts: Vec<String> = args\n        .get_many::<String>(\"subscribe_parts\")\n        .map(|vals| vals.cloned().collect())\n        .unwrap_or_default();\n    let config_targets = load_config_db_targets(no_config)?;\n    let resolved = resolve_optional_database_parts(\n        &raw_parts,\n        config_targets.as_deref(),\n        \"query\",\n        \"spacetime subscribe [database] <query> [query...] (or --no-config for legacy behavior)\",\n    )?;\n    let queries: Vec<String> = resolved.remaining_args;\n\n    let num = args.get_one::<u32>(\"num-updates\").copied();\n    let timeout = args.get_one::<u32>(\"timeout\").copied();\n    let print_initial_update = args.get_flag(\"print_initial_update\");\n    let confirmed = args.get_one::<bool>(\"confirmed\").copied();\n    let resolved_server = server.or(resolved.server.as_deref());\n\n    let mut config = config;\n    let conn = crate::api::Connection {\n        host: config.get_host_url(resolved_server)?,\n        auth_header: get_auth_header(&mut config, anon_identity, resolved_server, !force).await?,\n        database_identity: database_identity(&config, &resolved.database, resolved_server).await?,\n        database: resolved.database.clone(),\n    };\n    let api = ClientApi::new(conn);\n    let module_def = api.module_def().await?;\n\n    let mut url = Url::parse(&api.con.db_uri(\"subscribe\"))?;\n    // Change the URI scheme from `http(s)` to `ws(s)`.\n    url.set_scheme(match url.scheme() {\n        \"http\" => \"ws\",\n        \"https\" => \"wss\",\n        unknown => unreachable!(\"Invalid URL scheme in `Connection::db_uri`: {unknown}\"),\n    })\n    .unwrap();\n    if let Some(confirmed) = confirmed {\n        url.query_pairs_mut()\n            .append_pair(\"confirmed\", if confirmed { \"true\" } else { \"false\" });\n    }\n\n    // Create the websocket request.\n    let mut req = url.into_client_request()?;\n    req.headers_mut().insert(\n        header::SEC_WEBSOCKET_PROTOCOL,\n        http::HeaderValue::from_static(ws_v1::TEXT_PROTOCOL),\n    );\n    //  Add the authorization header, if any.\n    if let Some(auth_header) = api.con.auth_header.to_header() {\n        req.headers_mut().insert(header::AUTHORIZATION, auth_header);\n    }\n    let mut ws = tokio_tungstenite::connect_async(req).await.map(|(ws, _)| ws)?;\n\n    let task = async {\n        subscribe(&mut ws, queries.iter().cloned().map(Into::into).collect()).await?;\n        await_initial_update(&mut ws, print_initial_update.then_some(&module_def)).await?;\n        consume_transaction_updates(&mut ws, num, &module_def).await\n    };\n\n    let res = if let Some(timeout) = timeout {\n        let timeout = Duration::from_secs(timeout.into());\n        match tokio::time::timeout(timeout, task).await {\n            Ok(res) => res,\n            Err(_elapsed) => {\n                eprintln!(\"timed out after {}s\", timeout.as_secs());\n                Ok(())\n            }\n        }\n    } else {\n        task.await\n    };\n\n    // Close the connection gracefully.\n    // This will return an error if the server already closed,\n    // or the connection is in a bad state.\n    // The error (if any) relevant to the user is already stored in `res`,\n    // so we can ignore errors here -- graceful close is basically a\n    // courtesy to the server.\n    let _ = ws.close(None).await;\n    // The server closing the connection is not considered an error,\n    // but any other error is.\n    res.or_else(|e| {\n        if e.is_server_closed_connection() {\n            Ok(())\n        } else {\n            Err(e)\n        }\n    })\n    .map_err(anyhow::Error::from)\n}\n\n#[derive(Debug, Error)]\nenum Error {\n    #[error(\"error sending subscription queries\")]\n    Subscribe {\n        #[source]\n        source: WsError,\n    },\n    #[error(\"protocol error: {details}\")]\n    Protocol { details: &'static str },\n    #[error(\"websocket error: {source}\")]\n    Websocket {\n        #[source]\n        source: WsError,\n    },\n    #[error(\"encountered failed transaction: {reason}\")]\n    TransactionFailure { reason: Box<str> },\n    #[error(\"error formatting response: {source:#}\")]\n    Reformat {\n        #[source]\n        source: anyhow::Error,\n    },\n    #[error(transparent)]\n    Serde(#[from] serde_json::Error),\n    #[error(transparent)]\n    Io(#[from] io::Error),\n}\n\nimpl Error {\n    fn is_server_closed_connection(&self) -> bool {\n        matches!(\n            self,\n            Self::Websocket {\n                source: WsError::ConnectionClosed\n            }\n        )\n    }\n}\n\n/// Send the subscribe message.\nasync fn subscribe<S>(ws: &mut S, query_strings: Box<[Box<str>]>) -> Result<(), Error>\nwhere\n    S: Sink<WsMessage, Error = WsError> + Unpin,\n{\n    let msg = serde_json::to_string(&SerializeWrapper::new(ws_v1::ClientMessage::<()>::Subscribe(\n        ws_v1::Subscribe {\n            query_strings,\n            request_id: 0,\n        },\n    )))\n    .unwrap();\n    ws.send(msg.into()).await.map_err(|source| Error::Subscribe { source })\n}\n\n/// Await the initial [`ServerMessage::SubscriptionUpdate`].\n/// If `module_def` is `Some`, print a JSON representation to stdout.\nasync fn await_initial_update<S>(ws: &mut S, module_def: Option<&RawModuleDefV9>) -> Result<(), Error>\nwhere\n    S: TryStream<Ok = WsMessage, Error = WsError> + Unpin,\n{\n    const RECV_TX_UPDATE: &str = \"protocol error: received transaction update before initial subscription update\";\n\n    while let Some(msg) = ws.try_next().await.map_err(|source| Error::Websocket { source })? {\n        let Some(msg) = parse_msg_json(&msg) else { continue };\n        match msg {\n            ws_v1::ServerMessage::InitialSubscription(sub) => {\n                if let Some(module_def) = module_def {\n                    let output = format_output_json(&sub.database_update, module_def)?;\n                    tokio::io::stdout().write_all(output.as_bytes()).await?\n                }\n                break;\n            }\n            ws_v1::ServerMessage::TransactionUpdate(ws_v1::TransactionUpdate { status, .. }) => {\n                return Err(match status {\n                    ws_v1::UpdateStatus::Failed(msg) => Error::TransactionFailure { reason: msg },\n                    _ => Error::Protocol {\n                        details: RECV_TX_UPDATE,\n                    },\n                })\n            }\n            ws_v1::ServerMessage::TransactionUpdateLight(ws_v1::TransactionUpdateLight { .. }) => {\n                return Err(Error::Protocol {\n                    details: RECV_TX_UPDATE,\n                })\n            }\n            _ => continue,\n        }\n    }\n\n    Ok(())\n}\n\n/// Print `num` [`ServerMessage::TransactionUpdate`] messages as JSON.\n/// If `num` is `None`, keep going indefinitely.\nasync fn consume_transaction_updates<S>(ws: &mut S, num: Option<u32>, module_def: &RawModuleDefV9) -> Result<(), Error>\nwhere\n    S: TryStream<Ok = WsMessage, Error = WsError> + Unpin,\n{\n    let mut stdout = tokio::io::stdout();\n    let mut num_received = 0;\n    loop {\n        if num.is_some_and(|n| num_received >= n) {\n            return Ok(());\n        }\n        let Some(msg) = ws.try_next().await.map_err(|source| Error::Websocket { source })? else {\n            eprintln!(\"disconnected by server\");\n            return Err(Error::Websocket {\n                source: WsError::ConnectionClosed,\n            });\n        };\n\n        let Some(msg) = parse_msg_json(&msg) else { continue };\n        match msg {\n            ws_v1::ServerMessage::InitialSubscription(_) => {\n                return Err(Error::Protocol {\n                    details: \"received a second initial subscription update\",\n                })\n            }\n            ws_v1::ServerMessage::TransactionUpdateLight(ws_v1::TransactionUpdateLight { update, .. })\n            | ws_v1::ServerMessage::TransactionUpdate(ws_v1::TransactionUpdate {\n                status: ws_v1::UpdateStatus::Committed(update),\n                ..\n            }) => {\n                let output = format_output_json(&update, module_def)?;\n                stdout.write_all(output.as_bytes()).await?;\n                num_received += 1;\n            }\n            _ => continue,\n        }\n    }\n}\n\nfn format_output_json(\n    msg: &ws_v1::DatabaseUpdate<ws_v1::JsonFormat>,\n    schema: &RawModuleDefV9,\n) -> Result<String, Error> {\n    let formatted = reformat_update(msg, schema).map_err(|source| Error::Reformat { source })?;\n    let output = serde_json::to_string(&formatted)? + \"\\n\";\n\n    Ok(output)\n}\n"
  },
  {
    "path": "crates/cli/src/subcommands/version.rs",
    "content": "use std::ffi::OsString;\nuse std::path::PathBuf;\nuse std::process::{Command, ExitCode};\n\nuse anyhow::Context;\nuse clap::{ArgMatches, Args};\nuse spacetimedb_paths::cli::BinFile;\nuse spacetimedb_paths::{FromPathUnchecked, RootDir, SpacetimePaths};\n\npub fn cli() -> clap::Command {\n    Version::augment_args(clap::Command::new(\"version\"))\n}\n\n/// Manage installed spacetime versions\n///\n/// Run `spacetime version --help` to see all options.\n#[derive(clap::Args)]\n#[command(disable_help_flag = true)]\nstruct Version {\n    /// The args to pass to spacetimedb-update\n    #[arg(allow_hyphen_values = true, num_args = 0..)]\n    args: Vec<OsString>,\n}\n\npub async fn exec(paths: &SpacetimePaths, root_dir: Option<&RootDir>, args: &ArgMatches) -> anyhow::Result<ExitCode> {\n    let args = args.get_many::<OsString>(\"args\").unwrap_or_default();\n    let bin_path;\n    let bin_path = if let Some(artifact_dir) = running_from_target_dir() {\n        let update_path = artifact_dir\n            .join(\"spacetimedb-update\")\n            .with_extension(std::env::consts::EXE_EXTENSION);\n        anyhow::ensure!(\n            update_path.exists(),\n            \"running `spacetime version` from a target/ directory, but the spacetimedb-update\n             binary doesn't exist. try running `cargo build -p spacetimedb-update`\"\n        );\n        bin_path = BinFile::from_path_unchecked(update_path);\n        &bin_path\n    } else {\n        &paths.cli_bin_file\n    };\n    let mut cmd = Command::new(bin_path);\n    if let Some(root_dir) = root_dir {\n        cmd.arg(\"--root-dir\").arg(root_dir);\n    }\n    cmd.arg(\"version\").args(args);\n    let applet = \"spacetimedb-update\";\n    #[cfg(unix)]\n    {\n        use std::os::unix::process::CommandExt;\n        cmd.arg0(applet);\n    }\n    #[cfg(windows)]\n    cmd.env(\"SPACETIMEDB_UPDATE_MULTICALL_APPLET\", applet);\n    super::start::exec_replace(&mut cmd).with_context(|| format!(\"exec failed for {}\", bin_path.display()))\n}\n\n/// Checks to see if we're running from a subdirectory of a `target` dir that has a `Cargo.toml`\n/// as a sibling, and returns the containing directory of the current executable if so.\nfn running_from_target_dir() -> Option<PathBuf> {\n    let mut exe_path = std::env::current_exe().ok()?;\n    exe_path.pop();\n    let artifact_dir = exe_path;\n    // check for target/debug/spacetimedb-update and target/x86_64-unknown-foobar/debug/spacetimedb-update\n    let target_dir = artifact_dir\n        .ancestors()\n        .skip(1)\n        .take(2)\n        .find(|p| p.file_name() == Some(\"target\".as_ref()))?;\n    target_dir\n        .parent()?\n        .join(\"Cargo.toml\")\n        .try_exists()\n        .ok()\n        .map(|_| artifact_dir)\n}\n"
  },
  {
    "path": "crates/cli/src/tasks/cpp.rs",
    "content": "use anyhow::{anyhow, Context};\nuse std::path::{Path, PathBuf};\nuse walkdir::WalkDir;\n\nuse crate::detect::find_executable;\n\n/// Execute a command in a working directory and verify it succeeds.\n///\n/// On Windows, commands are wrapped in `cmd /C` to support `.bat` and `.cmd` files\n/// (e.g., `emcc.bat`, `emcmake.cmd`), which don't work with direct execution.\n///\n/// Returns an error if the command fails (non-zero exit code).\nfn run_command<S: AsRef<str>>(prog: &str, args: &[S], cwd: &Path) -> anyhow::Result<()> {\n    #[cfg(windows)]\n    {\n        let mut pieces: Vec<String> = Vec::with_capacity(1 + args.len());\n        pieces.push(prog.to_string());\n        pieces.extend(args.iter().map(|s| s.as_ref().to_string()));\n        duct::cmd(\"cmd\", std::iter::once(\"/C\").chain(pieces.iter().map(String::as_str)))\n            .dir(cwd)\n            .run()\n            .with_context(|| format!(\"failed running `{prog}`\"))?;\n    }\n    #[cfg(not(windows))]\n    {\n        duct::cmd(prog, args.iter().map(|s| s.as_ref()))\n            .dir(cwd)\n            .run()\n            .with_context(|| format!(\"failed running `{prog}`\"))?;\n    }\n    Ok(())\n}\n\npub(crate) fn build_cpp(project_path: &Path, build_debug: bool) -> anyhow::Result<PathBuf> {\n    // Verify required tools are in PATH\n    #[cfg(windows)]\n    let emcc_found = find_executable(\"emcc.bat\").is_some();\n    #[cfg(not(windows))]\n    let emcc_found = find_executable(\"emcc\").is_some();\n\n    if !emcc_found {\n        return Err(anyhow!(\"`emcc` not found in PATH. Activate Emscripten (emsdk_env).\"));\n    }\n\n    #[cfg(windows)]\n    let cmake_found = find_executable(\"cmake.exe\").is_some();\n    #[cfg(not(windows))]\n    let cmake_found = find_executable(\"cmake\").is_some();\n\n    if !cmake_found {\n        return Err(anyhow!(\"`cmake` not found in PATH.\"));\n    }\n\n    #[cfg(windows)]\n    let emcmake_found = find_executable(\"emcmake.bat\").is_some();\n    #[cfg(not(windows))]\n    let emcmake_found = find_executable(\"emcmake\").is_some();\n\n    if !emcmake_found {\n        return Err(anyhow!(\"`emcmake` not found in PATH. Is Emscripten env active?\"));\n    }\n\n    let build_type = if build_debug { \"Debug\" } else { \"Release\" };\n    let build_dir = project_path.join(\"build\");\n\n    // === Configure (no generator flags; let emcmake/cmake decide or reuse existing) ===\n    // This matches: emcmake cmake -B build .\n    // We keep -S/-B so `project_path` can be anywhere, and pass CMAKE_BUILD_TYPE (ignored by multi-config).\n    let cfg_args = [\n        \"cmake\",\n        \"-S\",\n        \".\",\n        \"-B\",\n        \"build\",\n        &format!(\"-DCMAKE_BUILD_TYPE={}\", build_type),\n    ];\n    run_command(\"emcmake\", &cfg_args, project_path).context(\"Failed to configure C++ project with emcmake/cmake\")?;\n\n    // === Build (matches: cmake --build build) ===\n    // Always pass --config; it's required for multi-config and ignored for single-config.\n    let build_args = [\"--build\", \"build\", \"--config\", build_type, \"--parallel\"];\n    run_command(\"cmake\", &build_args, project_path).context(\"Failed to build C++ project\")?;\n\n    // Find the most recently modified .wasm under build/ directory\n    // This ensures we get the latest build output when rebuilding, instead of potentially\n    // picking up an older cached wasm file from a previous build\n    let wasm = WalkDir::new(&build_dir)\n        .into_iter()\n        .filter_map(Result::ok)\n        .filter_map(|e| {\n            let p = e.path();\n            if p.extension().and_then(|s| s.to_str()) == Some(\"wasm\") {\n                e.metadata()\n                    .ok()\n                    .and_then(|m| m.modified().ok())\n                    .map(|mtime| (p.to_path_buf(), mtime))\n            } else {\n                None\n            }\n        })\n        .max_by_key(|(_, mtime)| *mtime)\n        .map(|(path, _)| path)\n        .ok_or_else(|| {\n            anyhow!(\n                \"Built successfully but couldn't find a .wasm under {}\",\n                build_dir.display()\n            )\n        })?;\n\n    Ok(wasm)\n}\n"
  },
  {
    "path": "crates/cli/src/tasks/csharp.rs",
    "content": "use anyhow::Context;\nuse itertools::Itertools;\nuse std::ffi::OsString;\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\nfn parse_major_version(version: &str) -> Option<u8> {\n    version.split('.').next()?.parse::<u8>().ok()\n}\n\npub(crate) fn build_csharp(project_path: &Path, build_debug: bool) -> anyhow::Result<PathBuf> {\n    // All `dotnet` commands must execute in the project directory, otherwise\n    // global.json won't have any effect and wrong .NET SDK might be picked.\n    macro_rules! dotnet {\n        ($($arg:expr),*) => {\n            duct::cmd!(\"dotnet\", $($arg),*).dir(project_path)\n        };\n    }\n\n    // Check if the `wasi-experimental` workload is installed. Unfortunately, we\n    // have to do this by inspecting the human-readable output. There is a\n    // hidden `--machine-readable` flag but it also mixes in human-readable\n    // output as well as unnecessarily updates various unrelated manifests.\n    match dotnet!(\"workload\", \"list\").read() {\n        Ok(workloads) if workloads.contains(\"wasi-experimental\") => {}\n        Ok(_) => {\n            // If wasi-experimental is not found, first check if we're running\n            // on .NET SDK 8.0. We can't even install that workload on older\n            // versions, and we don't support .NET 9.0 yet, so this helps to\n            // provide a nicer message than \"Workload ID wasi-experimental is not recognized.\".\n            let version = dotnet!(\"--version\").read().unwrap_or_default();\n            if parse_major_version(&version) != Some(8) {\n                anyhow::bail!(concat!(\n                    \".NET SDK 8.0 is required, but found {version}.\\n\",\n                    \"If you have multiple versions of .NET SDK installed, configure your project using https://learn.microsoft.com/en-us/dotnet/core/tools/global-json.\"\n                ));\n            }\n\n            // Finally, try to install the workload ourselves. On some systems\n            // this might require elevated privileges, so print a nice error\n            // message if it fails.\n            dotnet!(\n                \"workload\",\n                \"install\",\n                \"wasi-experimental\",\n                \"--skip-manifest-update\"\n            )\n            .stderr_capture()\n            .run()\n            .context(concat!(\n                \"Couldn't install the required wasi-experimental workload.\\n\",\n                \"You might need to install it manually by running `dotnet workload install wasi-experimental` with privileged rights.\"\n            ))?;\n        }\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {\n            anyhow::bail!(\"dotnet not found in PATH. Please install .NET SDK 8.0.\")\n        }\n        Err(error) => anyhow::bail!(\"{error}\"),\n    };\n\n    let config_name = if build_debug { \"Debug\" } else { \"Release\" };\n\n    // Ensure the project path exists.\n    fs::metadata(project_path).with_context(|| {\n        format!(\n            \"The provided project path '{}' does not exist.\",\n            project_path.to_str().unwrap()\n        )\n    })?;\n\n    // run dotnet publish using cmd macro\n    dotnet!(\"publish\", \"-c\", config_name, \"-v\", \"quiet\").run()?;\n\n    // check if file exists\n    let subdir = if std::env::var_os(\"EXPERIMENTAL_WASM_AOT\").is_some_and(|v| v == \"1\") {\n        \"publish\"\n    } else {\n        \"AppBundle\"\n    };\n    // TODO: This code looks for build outputs in both `bin` and `bin~` as output directories. @bfops feels like we shouldn't have to look for `bin~`, since the `~` suffix is just intended to cause Unity to ignore directories, and that shouldn't be relevant here. We do think we've seen `bin~` appear though, and it's not harmful to do the extra checks, so we're merging for now due to imminent code freeze. At some point, it would be good to figure out if we do actually see `bin~` in module directories, and where that's coming from (which could suggest a bug).\n    // check for the old .NET 7 path for projects that haven't migrated yet\n    let bad_output_paths = [\n        project_path.join(format!(\"bin/{config_name}/net7.0/StdbModule.wasm\")),\n        // for some reason there is sometimes a tilde here?\n        project_path.join(format!(\"bin~/{config_name}/net7.0/StdbModule.wasm\")),\n    ];\n    if bad_output_paths.iter().any(|p| p.exists()) {\n        anyhow::bail!(concat!(\n            \"Looks like your project is using the deprecated .NET 7.0 WebAssembly bindings.\\n\",\n            \"Please migrate your project to the new .NET 8.0 template and delete the folders: bin, bin~, obj, obj~\"\n        ));\n    }\n    let possible_output_paths = [\n        project_path.join(format!(\"bin/{config_name}/net8.0/wasi-wasm/{subdir}/StdbModule.wasm\")),\n        project_path.join(format!(\"bin~/{config_name}/net8.0/wasi-wasm/{subdir}/StdbModule.wasm\")),\n    ];\n    if possible_output_paths.iter().all(|p| p.exists()) {\n        anyhow::bail!(concat!(\n            \"For some reason, your project has both a `bin` and a `bin~` folder.\\n\",\n            \"I don't know which to use, so please delete both and rerun this command so that we can see which is up-to-date.\"\n        ));\n    }\n    for output_path in possible_output_paths {\n        if output_path.exists() {\n            return Ok(output_path);\n        }\n    }\n    anyhow::bail!(\"Built project successfully but couldn't find the output file.\");\n}\n\npub(crate) fn dotnet_format(project_dir: &Path, files: impl IntoIterator<Item = PathBuf>) -> anyhow::Result<()> {\n    let cwd = std::env::current_dir().expect(\"Failed to retrieve current directory\");\n    duct::cmd(\n        \"dotnet\",\n        itertools::chain(\n            [\n                \"format\",\n                // We can't guarantee that the output lives inside a valid project or solution,\n                // so to avoid crash we need to use the `dotnet whitespace --folder` mode instead\n                // of a full style-aware formatter. Still better than nothing though.\n                \"whitespace\",\n                \"--folder\",\n                project_dir.to_str().unwrap(),\n                // Our files are marked with <auto-generated /> and will be skipped without this option.\n                \"--include-generated\",\n                \"--include\",\n            ]\n            .into_iter()\n            .map_into::<OsString>(),\n            // Resolve absolute paths for all of the files, because we receive them as relative paths to cwd, but\n            // `dotnet format` will interpret those paths relative to `project_dir`.\n            files\n                .into_iter()\n                .map(|f| {\n                    let f = if f.is_absolute() { f } else { cwd.join(f) };\n                    f.canonicalize().expect(\"Failed to canonicalize path: {f}\")\n                })\n                .map_into(),\n        ),\n    )\n    .run()?;\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/tasks/javascript.rs",
    "content": "use anyhow::Context;\nuse regex::Regex;\nuse rolldown::{BundleOutput, Bundler, BundlerOptions, Either, SourceMapType};\nuse rolldown_error::{BuildDiagnostic, DiagnosableResolveError, DiagnosticOptions, Severity};\nuse rolldown_utils::indexmap::FxIndexMap;\nuse rolldown_utils::js_regex::HybridRegex;\nuse rolldown_utils::pattern_filter::StringOrRegex;\nuse std::fs;\nuse std::io::IsTerminal;\nuse std::path::{Path, PathBuf};\nuse std::process::ExitCode;\nuse std::sync::{Arc, OnceLock};\nuse tokio::runtime::{Builder, Handle, Runtime};\n\nuse crate::ExitWithCode;\n\nstatic RUNTIME: OnceLock<Runtime> = OnceLock::new();\n\nfn runtime() -> &'static Runtime {\n    RUNTIME.get_or_init(|| {\n        Builder::new_multi_thread()\n            .enable_all()\n            .build()\n            .expect(\"build tokio runtime\")\n    })\n}\n\n/// Synchronously run an async future from a non-async function.\n///\n/// - If we're already inside a Tokio runtime, we switch to the blocking pool\n///   and `block_on` the future (prevents scheduler starvation).\n/// - Otherwise, we use a shared, long-lived runtime.\npub fn run_blocking<F, T>(fut: F) -> T\nwhere\n    F: std::future::Future<Output = T> + Send + 'static,\n    T: Send + 'static,\n{\n    match Handle::try_current() {\n        Ok(handle) => tokio::task::block_in_place(|| handle.block_on(fut)),\n        Err(_) => runtime().block_on(fut),\n    }\n}\n\npub(crate) fn build_javascript(project_path: &Path, build_debug: bool) -> anyhow::Result<PathBuf> {\n    let cwd = fs::canonicalize(project_path)?;\n\n    let mut tsc_path = cwd.join(\"node_modules/.bin/tsc\");\n    if cfg!(windows) {\n        tsc_path.set_extension(\"cmd\");\n    }\n    if tsc_path.exists() {\n        let status = std::process::Command::new(tsc_path)\n            .arg(\"--noEmit\")\n            .current_dir(&cwd)\n            .status()\n            .context(\"Failed to execute tsc\")?;\n        if !status.success() {\n            if let Some(code) = status.code()\n                && let Ok(code) = u8::try_from(code).map(ExitCode::from)\n            {\n                anyhow::bail!(ExitWithCode(code));\n            }\n            // For an abnormal exit, show the details of the status.\n            anyhow::bail!(\"tsc exited with {status}\");\n        }\n    } else {\n        eprintln!(\n            \"tsc not found in node_modules. Make sure you have the `typescript` package \\\n             as a dev-dependency and that your dependencies are installed.\"\n        )\n    }\n\n    let mut bundler = Bundler::new(BundlerOptions {\n        input: Some(vec![\"./src/index.ts\".to_string().into()]),\n        cwd: Some(cwd.clone()),\n        sourcemap: Some(SourceMapType::Inline),\n        external: Some(rolldown::IsExternal::StringOrRegex(vec![\n            // Mark the bindings as external so we don't get a warning.\n            StringOrRegex::Regex(HybridRegex::Optimize(Regex::new(\"spacetime:sys.*\").unwrap())),\n        ])),\n        platform: Some(rolldown::Platform::Browser), // Browser is the correct choice here because it doesn't inject Node.js polyfills.\n        shim_missing_exports: Some(false),\n        name: None,\n        entry_filenames: None,\n        chunk_filenames: None, // The pattern to use for naming shared chunks created when code-splitting\n        css_entry_filenames: None,\n        css_chunk_filenames: None, // We're not doing CSS\n        asset_filenames: None,     // assets/[name]-[hash][extname]\n        sanitize_filename: Some(rolldown::SanitizeFilename::Boolean(true)), // Replace characters that are invalid in filenames with underscores\n        dir: None, // The output directory to write to. We only want a single output file, so we won't set this.\n        file: Some(\"./dist/bundle.js\".into()), // The output file to write to. We want a single output file.\n        format: Some(rolldown::OutputFormat::Esm), // We want to use ES Modules in SpacetimeDB\n        exports: Some(rolldown::OutputExports::Named), // Use named exports for ES modules.\n        globals: None, // We don't have any external dependencies except for `spacetimedb` which is a dependency and declares all its globals\n        paths: None,   // Maps external module IDs to paths\n        generated_code: Some(rolldown::GeneratedCodeOptions::es2015()),\n        es_module: Some(rolldown::EsModuleFlag::IfDefaultProp), // See https://rollupjs.org/configuration-options/#output-esmodule\n        drop_labels: None,\n        hash_characters: None,       // File name hash characters, we don't care\n        banner: None,                // String to prepend to the bundle\n        footer: None,                // String to append to the bundle\n        post_banner: None,           // String to prepend to the bundle\n        post_footer: None,           // String to append to the bundle\n        intro: None,                 // Similar to the above, but inside the wrappers\n        outro: None,                 // Similar to the above, but inside the wrappers\n        sourcemap_base_url: None,    // Absolute URLs for the source map\n        sourcemap_ignore_list: None, // See https://rollupjs.org/configuration-options/#output-sourcemapignorelist\n        // Function to transform source map paths\n        sourcemap_path_transform: Some(rolldown::SourceMapPathTransform::new(Arc::new(\n            |relative_path, _sourcemap_path| {\n                // The output file is ./dist/bundle.js, so all the paths will be relative to it,\n                // e.g. from the perspective of `./dist` the entry file is `../src/index.ts`.\n                // So, strip the leading `../`\n                let path = relative_path.strip_prefix(\"../\").unwrap_or(relative_path);\n                Box::pin(futures::future::ok(path.to_owned()))\n            },\n        ))),\n        sourcemap_debug_ids: Some(true), // Seems like a good idea. See: https://rollupjs.org/configuration-options/#output-sourcemapdebugids\n        module_types: None, // Lets you associate file extensions with module types, e.g. `.data` -> `json`. We don't need this.\n        // Wrapper around https://docs.rs/oxc_resolver/latest/oxc_resolver/struct.ResolveOptions.html, see also https://rolldown.rs/guide/features#module-resolution\n        resolve: Some(rolldown::ResolveOptions {\n            // Prefer environment-neutral exports\n            condition_names: Some(vec![\"production\".into(), \"import\".into(), \"default\".into()]),\n            main_fields: Some(vec![\"exports\".into(), \"module\".into(), \"main\".into()]),\n            extensions: Some(vec![\n                \".ts\".into(),\n                \".tsx\".into(),\n                \".mjs\".into(),\n                \".js\".into(),\n                \".cjs\".into(),\n                \".json\".into(),\n            ]),\n            symlinks: Some(true),\n            ..Default::default()\n        }),\n        treeshake: rolldown::TreeshakeOptions::Option(rolldown::InnerOptions {\n            module_side_effects: rolldown::ModuleSideEffects::Boolean(true), // TODO: SpacetimeDB currently relies on `import './runtime'` to set up the environment, so we can't tree-shake that away.\n            annotations: Some(true), // Respect the `/* @__PURE__ */` annotations that tools like Terser use to identify pure functions for tree-shaking.\n            manual_pure_functions: None, // Don't manually specify any pure functions.\n            unknown_global_side_effects: Some(true), // Default, basically if there is an unknown global, assume it has side effects.\n            commonjs: Some(true), // Enable some optimizations for CommonJS modules, even though we don't use any. This is the default.\n            property_read_side_effects: Some(rolldown::PropertyReadSideEffects::Always), // Assume that property reads can have side effects. This is safest for users who might use getters with side effects.\n            property_write_side_effects: Some(rolldown::PropertyWriteSideEffects::Always), // Assume that property writes can have side effects. This is safest for users who might use setters with side effects.\n            invalid_import_side_effects: Some(true), // Assume that invalid improts can have side effects.\n        }),\n        experimental: None, // None for now, although be aware that Rollup has an experimental `perf` option.\n        minify: Some(rolldown::RawMinifyOptions::Bool(false)), // Disable minification until we have proper support for source maps.\n        define: Some(FxIndexMap::from_iter([\n            // TODO(cloutiertyler): I actually think we should probably just always do production mode event in debug builds\n            (\n                \"process.env.NODE_ENV\".to_string(),\n                if build_debug { \"development\" } else { \"production\" }.into(),\n            ),\n        ])),\n        extend: Some(false), // Not relevant for us, this is for extending global variables in UMD/IIFE bundles and we have ESM only.\n        profiler_names: None, // Unclear what this is, choosing the default.\n        keep_names: None,    // Unclear what this is, choosing the default.\n        inject: None,        // Unclear on why we'd need this, choosing the default.\n        external_live_bindings: Some(true), // Don't assume that external bindings are going to change over time. Generates more optimized code.\n        code_splitting: None,               // Split a bundle into multiple chunks on dynamic `import()` boundaries.\n        dynamic_import_in_cjs: None,        // Emit `import()` in CommonJS\n        manual_code_splitting: None,        // Not relevant to us, this is for advanced code-splitting strategies.\n        checks: Some(rolldown::ChecksOptions {\n            circular_dependency: Some(true),           // Check circular dependencies\n            eval: Some(false),                         // We don't care about eval\n            missing_global_name: Some(true), // Warn if a global variable is missing a name in the output bundle\n            missing_name_option_for_iife_export: None, // Don't care, we don't use IIFE\n            mixed_exports: Some(false),      // Don't care about mixed exports\n            unresolved_entry: Some(true),    // If the entry point is unresolved, that's a problem\n            unresolved_import: Some(true),   // If an import is unresolved, that's a problem\n            filename_conflict: Some(true),\n            common_js_variable_in_esm: Some(true),\n            import_is_undefined: Some(true),\n            empty_import_meta: Some(true),\n            tolerated_transform: None,\n            cannot_call_namespace: Some(true),\n            configuration_field_conflict: Some(true),\n            prefer_builtin_feature: Some(true),\n            could_not_clean_directory: None,\n            plugin_timings: None,\n            duplicate_shebang: None,\n            unsupported_tsconfig_option: None,\n        }),\n        transform: Some(rolldown::BundlerTransformOptions {\n            jsx: None,                                       // Don't transform JSX\n            target: Some(Either::Left(\"esnext\".to_owned())), // Default, no transformation\n            assumptions: None, // No compiler assumptions, we don't need to minmax output size\n            decorator: None,   // Disable experimental decorators\n            typescript: Some(rolldown::TypeScriptOptions {\n                jsx_pragma: None,                                     // I am unclear on what this is\n                jsx_pragma_frag: None,                                // I am unclear on what this is\n                only_remove_type_imports: Some(true),                 // I am assuming we just want to strip these\n                allow_namespaces: Some(false),                        // No namespaces, only allow JS + types\n                allow_declare_fields: Some(true), // Allow `declare` fields in classes and strip these\n                remove_class_fields_without_initializer: Some(false), // Leave them there to not mess with behavior\n                declaration: None, // We don't need to generate any declaration files, these are not libraries, although you could imagine this in the future\n                rewrite_import_extensions: Some(Either::Left(true)), // Rewrite .ts/.tsx extensions to .js/.jsx, not really relevant for us since we only have a single entry point\n            }),\n            plugins: None,\n        }),\n        watch: None,                                         // We don't need watch mode\n        legal_comments: Some(rolldown::LegalComments::None), // We don't need any legal comments\n        polyfill_require: Some(false),                       // We don't need to polyfill require, only ESM here\n        defer_sync_scan_data: None,                          // Unclear what this is\n        make_absolute_externals_relative: None, // See https://rollupjs.org/configuration-options/#makeabsoluteexternalsrelative\n        devtools: None,                         // This is undocumented\n        invalidate_js_side_cache: None,\n        log_level: Some(rolldown::LogLevel::Debug), // Default logging\n        on_log: None,                               // Don't need it\n        preserve_modules: Some(false),              // We want a single output file\n        virtual_dirname: None,                      // Requires preserve_modules to be true\n        preserve_modules_root: None,                // Only relevant if preserve_modules is true\n        preserve_entry_signatures: None, // Default is fine, see https://rollupjs.org/configuration-options/#preserveentrysignatures\n        optimization: None,              // Defaults are fine\n        top_level_var: Some(false),      // This is the safer choice since we'll keep vars scoped to modules\n        minify_internal_exports: Some(true), // Sure\n        clean_dir: None,\n        context: None, // We don't want a top level `this` in modules\n        tsconfig: Some(rolldown::TsConfig::Manual(cwd.join(\"tsconfig.json\"))),\n        strict_execution_order: None,\n    })?;\n\n    let bundle_result = run_blocking(async move { bundler.write().await });\n\n    let (mut bundle_result, diagnostics) = match bundle_result {\n        Ok(BundleOutput { warnings, assets }) => (Some(assets), warnings),\n        Err(errors) => (None, errors.into_vec()),\n    };\n\n    let color = std::io::stderr().is_terminal();\n    let diag_options = DiagnosticOptions { cwd };\n    for mut diag in diagnostics {\n        // If an import failed to resolve, force it to be an error.\n        if let Some(err) = diag.downcast_mut::<DiagnosableResolveError>() {\n            err.reason = \"Module not found.\".into();\n            err.help = Some(\"You may have forgotten to install dependencies with your package manager.\".into());\n            // `BuildDiagnostic` doesn't let us change its severity to error. Instead, we\n            // construct a fresh `BuildDiagnostic` (which will have Severity::Error), then\n            // swap in the real `DiagnosableResolveError`.\n            let mut new_diag = BuildDiagnostic::resolve_error(\n                Default::default(),\n                Default::default(),\n                rolldown_error::DiagnosableArcstr::String(Default::default()),\n                Default::default(),\n                err.diagnostic_kind,\n                Default::default(),\n            );\n            std::mem::swap(new_diag.downcast_mut::<DiagnosableResolveError>().unwrap(), err);\n            diag = new_diag;\n        }\n        // if there are any errors, we want to bail after printing\n        if diag.severity() == Severity::Error {\n            bundle_result = None;\n        }\n        eprintln!(\"{}\", diag.to_diagnostic_with(&diag_options).convert_to_string(color));\n    }\n    let bundle_assets = bundle_result.ok_or(ExitWithCode::FAILURE)?;\n\n    let output_chunk = bundle_assets\n        .into_iter()\n        .find_map(|chunk| match chunk {\n            rolldown_common::Output::Chunk(chunk) if chunk.is_entry && chunk.filename == \"bundle.js\" => Some(chunk),\n            _ => None,\n        })\n        .expect(\"there should be an output chunk\");\n\n    let sys_imports = output_chunk.imports.iter().filter_map(|spec| {\n        let (maj, min) = spec.strip_prefix(\"spacetime:sys@\")?.split_once('.')?;\n        Option::zip(maj.parse::<u16>().ok(), min.parse::<u16>().ok())\n    });\n\n    let mut maj_sys_ver = None;\n    for (maj, _min) in sys_imports {\n        anyhow::ensure!(\n            *maj_sys_ver.get_or_insert(maj) == maj,\n            \"The module pulls in 2 different versions of the `spacetimedb/server` package\"\n        );\n    }\n\n    let maj_sys_ver = maj_sys_ver.context(\n        \"Your module doesn't import the `spacetimedb/server` package at all - \\\n         this is likely a mistake, as your module will not be able to interface \\\n         with the SpacetimeDB host.\",\n    )?;\n\n    if maj_sys_ver == 2 {\n        anyhow::ensure!(\n            output_chunk.exports.contains(&\"default\".into()),\n            \"It seems like you haven't exported your schema. You must `export default schema(...);`\"\n        );\n    }\n\n    Ok(project_path.join(\"dist\").join(\"bundle.js\"))\n}\n"
  },
  {
    "path": "crates/cli/src/tasks/mod.rs",
    "content": "use std::path::{Path, PathBuf};\n\nuse crate::util::{self, ModuleLanguage};\n\nuse self::cpp::build_cpp;\nuse self::csharp::build_csharp;\nuse self::javascript::build_javascript;\nuse self::rust::build_rust;\n\nuse duct::cmd;\n\n// TODO: Replace the returned `&'static str` with a copy of `HostType` from core.\npub fn build(\n    project_path: &Path,\n    lint_dir: Option<&Path>,\n    build_debug: bool,\n    features: Option<&std::ffi::OsString>,\n) -> anyhow::Result<(PathBuf, &'static str)> {\n    let lang = util::detect_module_language(project_path)?;\n    if features.is_some() && lang != ModuleLanguage::Rust {\n        anyhow::bail!(\"The --features option is only supported for Rust modules.\");\n    }\n    let output_path = match lang {\n        ModuleLanguage::Rust => build_rust(project_path, features, lint_dir, build_debug),\n        ModuleLanguage::Csharp => build_csharp(project_path, build_debug),\n        ModuleLanguage::Javascript => build_javascript(project_path, build_debug),\n        ModuleLanguage::Cpp => build_cpp(project_path, build_debug),\n    }?;\n\n    if lang == ModuleLanguage::Javascript {\n        Ok((output_path, \"Js\"))\n    } else if build_debug {\n        Ok((output_path, \"Wasm\"))\n    } else {\n        // for release builds, optimize wasm modules with wasm-opt\n        let mut wasm_path = output_path;\n        eprintln!(\"Optimising module with wasm-opt...\");\n        let wasm_path_opt = wasm_path.with_extension(\"opt.wasm\");\n        match cmd!(\"wasm-opt\", \"-all\", \"-g\", \"-O2\", &wasm_path, \"-o\", &wasm_path_opt).run() {\n            Ok(_) => wasm_path = wasm_path_opt,\n            // Non-critical error for backward compatibility with users who don't have wasm-opt.\n            Err(err) => {\n                if err.kind() == std::io::ErrorKind::NotFound {\n                    eprintln!(\"Could not find wasm-opt to optimise the module.\");\n                    eprintln!(\n                        \"For best performance install wasm-opt from https://github.com/WebAssembly/binaryen/releases.\"\n                    );\n                } else {\n                    // If wasm-opt exists but failed for some reason, print the error but continue with unoptimised module.\n                    // This is to reduce disruption in case we produce a module that wasm-opt can't handle like happened before.\n                    eprintln!(\"Failed to optimise module with wasm-opt: {err}\");\n                }\n                eprintln!(\"Continuing with unoptimised module.\");\n            }\n        }\n        Ok((wasm_path, \"Wasm\"))\n    }\n}\n\npub mod cpp;\npub mod csharp;\npub mod javascript;\npub mod rust;\n"
  },
  {
    "path": "crates/cli/src/tasks/rust.rs",
    "content": "use std::ffi::OsString;\nuse std::io::BufRead;\nuse std::path::{Path, PathBuf};\nuse std::{fs, io};\n\nuse crate::detect::{has_rust_fmt, has_rust_up, has_wasm32_target};\nuse anyhow::Context;\nuse cargo_metadata::Message;\nuse duct::cmd;\nuse itertools::Itertools;\n\nfn cargo_cmd(subcommand: &str, build_debug: bool, args: &[&str]) -> duct::Expression {\n    duct::cmd(\n        \"cargo\",\n        [\n            subcommand,\n            \"--config=net.git-fetch-with-cli=true\",\n            \"--target=wasm32-unknown-unknown\",\n        ]\n        .into_iter()\n        .chain((!build_debug).then_some(\"--release\"))\n        .chain(args.iter().copied()),\n    )\n}\n\npub(crate) fn build_rust(\n    project_path: &Path,\n    features: Option<&std::ffi::OsString>,\n    lint_dir: Option<&Path>,\n    build_debug: bool,\n) -> anyhow::Result<PathBuf> {\n    // Make sure that we have the wasm target installed\n    if !has_wasm32_target() {\n        if has_rust_up() {\n            cmd!(\"rustup\", \"target\", \"add\", \"wasm32-unknown-unknown\")\n                .run()\n                .context(\"Failed to install wasm32-unknown-unknown target with rustup\")?;\n        } else {\n            anyhow::bail!(\"wasm32-unknown-unknown target is not installed. Please install it.\");\n        }\n    }\n\n    if let Some(lint_dir) = lint_dir {\n        let mut err_count: u32 = 0;\n        let lint_dir = project_path.join(lint_dir);\n        for file in walkdir::WalkDir::new(lint_dir).into_iter() {\n            let file = file?;\n            let printable_path = file.path().to_str().ok_or(anyhow::anyhow!(\"path not utf-8\"))?;\n            if file.file_type().is_file() && file.path().extension().is_some_and(|ext| ext == \"rs\") {\n                let file = fs::File::open(file.path())?;\n                for (idx, line) in io::BufReader::new(file).lines().enumerate() {\n                    let line = line?;\n                    let line_number = idx + 1;\n                    for disallowed in &[\"println!\", \"print!\", \"eprintln!\", \"eprint!\", \"dbg!\"] {\n                        if line.contains(disallowed) {\n                            if err_count == 0 {\n                                eprintln!(\"\\nDetected nonfunctional print statements:\\n\");\n                            }\n                            eprintln!(\"{printable_path}:{line_number}: {line}\");\n                            err_count += 1;\n                        }\n                    }\n                }\n            }\n        }\n        if err_count > 0 {\n            eprintln!();\n            anyhow::bail!(\n                \"Found {err_count} disallowed print statement(s).\\n\\\n                These will not be printed from SpacetimeDB modules.\\n\\\n                If you need to print something, use the `log` crate\\n\\\n                and the `log::info!` macro instead.\"\n            );\n        }\n    } else {\n        println!(\n            \"Warning: Skipping checks for nonfunctional print statements.\\n\\\n            If you have used builtin macros for printing, such as println!,\\n\\\n            your logs will not show up.\"\n        );\n    }\n\n    let mut args = if let Some(features) = features {\n        vec![format!(\"--features={}\", features.to_string_lossy())]\n    } else {\n        vec![]\n    };\n    args.push(\"--message-format=json-render-diagnostics\".to_string());\n\n    // Convert Vec<String> to Vec<&str>\n    let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();\n\n    let reader = cargo_cmd(\"build\", build_debug, &args_str).dir(project_path).reader()?;\n\n    let mut artifact = None;\n    for message in Message::parse_stream(io::BufReader::new(reader)) {\n        match message {\n            Ok(Message::CompilerArtifact(art)) => artifact = Some(art),\n            Err(error) => return Err(anyhow::anyhow!(error)),\n            _ => {}\n        }\n    }\n    let artifact = artifact.context(\"no artifact found?\")?;\n    let artifact = artifact.filenames.into_iter().next().context(\"no wasm?\")?;\n\n    check_for_issues(artifact.as_ref())?;\n\n    Ok(artifact.into())\n}\n\nfn check_for_issues(artifact: &Path) -> anyhow::Result<()> {\n    // if this fails for some reason, just let it fail elsewhere\n    let Ok(file) = fs::File::open(artifact) else {\n        return Ok(());\n    };\n    let Ok(module) = wasmbin::Module::decode_from(&mut io::BufReader::new(file)) else {\n        return Ok(());\n    };\n    if has_wasm_bindgen(&module) {\n        anyhow::bail!(\n            \"wasm-bindgen detected.\\n\\\n             \\n\\\n             It seems like either you or a crate in your dependency tree is depending on\\n\\\n             wasm-bindgen. wasm-bindgen is only for webassembly modules that target the web\\n\\\n             platform, and will not work in the context of SpacetimeDB.\\n\\\n             \\n\\\n             To find the offending dependency, run `cargo tree -i wasm-bindgen`. Try checking\\n\\\n             its cargo features for 'js' or 'web' or 'wasm-bindgen' to see if there's a way\\n\\\n             to disable it.\"\n        )\n    }\n    if has_getrandom(&module) {\n        anyhow::bail!(\n            \"getrandom usage detected.\\n\\\n             \\n\\\n             It seems like either you or a crate in your dependency tree is depending on\\n\\\n             the `getrandom` crate for random number generation. getrandom is the default\\n\\\n             randomness source for the `rand` crate, and is used when you call\\n\\\n             `rand::random()` or `rand::thread_rng()`. If this is you, you should instead\\n\\\n             use `ctx.rng()` on a `ReducerContext`. If this is a crate in your\\n\\\n             tree, you should try to see if the crate provides a way to pass in a custom\\n\\\n             `Rng` type, and pass it the rng returned from `ctx.rng()`.\"\n        )\n    }\n    Ok(())\n}\n\nconst WBINDGEN_PREFIX: &str = \"__wbindgen\";\nfn has_wasm_bindgen(module: &wasmbin::Module) -> bool {\n    let check_import = |import: &wasmbin::sections::Import| {\n        import.path.module.starts_with(WBINDGEN_PREFIX) || import.path.name.starts_with(WBINDGEN_PREFIX)\n    };\n    let check_export = |export: &wasmbin::sections::Export| export.name.starts_with(WBINDGEN_PREFIX);\n\n    module\n        .find_std_section::<wasmbin::sections::payload::Import>()\n        .and_then(|imports| imports.try_contents().ok())\n        .is_some_and(|imports| imports.iter().any(check_import))\n        || module\n            .find_std_section::<wasmbin::sections::payload::Export>()\n            .and_then(|exports| exports.try_contents().ok())\n            .is_some_and(|exports| exports.iter().any(check_export))\n}\n\nfn has_getrandom(module: &wasmbin::Module) -> bool {\n    module\n        .find_std_section::<wasmbin::sections::payload::Import>()\n        .and_then(|imports| imports.try_contents().ok())\n        .is_some_and(|imports| imports.iter().any(|import| import.path.name == \"__getrandom_custom\"))\n}\n\npub(crate) fn rustfmt(files: impl IntoIterator<Item = PathBuf>) -> anyhow::Result<()> {\n    if !has_rust_fmt() {\n        if has_rust_up() {\n            cmd!(\"rustup\", \"component\", \"add\", \"rustfmt\")\n                .run()\n                .context(\"Failed to install rustfmt with Rustup\")?;\n        } else {\n            anyhow::bail!(\"rustfmt is not installed. Please install it.\");\n        }\n    }\n    cmd(\n        \"rustfmt\",\n        itertools::chain(\n            [\"--edition\", \"2021\"].into_iter().map_into::<OsString>(),\n            files.into_iter().map_into(),\n        ),\n    )\n    .run()?;\n    Ok(())\n}\n"
  },
  {
    "path": "crates/cli/src/util.rs",
    "content": "use anyhow::Context;\nuse base64::{engine::general_purpose::STANDARD_NO_PAD as BASE_64_STD_NO_PAD, Engine as _};\nuse reqwest::{RequestBuilder, Url};\nuse spacetimedb_auth::identity::{IncomingClaims, SpacetimeIdentityClaims};\nuse spacetimedb_client_api_messages::name::GetNamesResponse;\nuse spacetimedb_lib::Identity;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\n\nuse crate::config::Config;\nuse crate::login::{spacetimedb_login_and_save, DEFAULT_AUTH_HOST};\n\npub const UNSTABLE_WARNING: &str = \"WARNING: This command is UNSTABLE and subject to breaking changes.\";\n\n/// Strip the Windows extended-length path prefix (`\\\\?\\`) if present.\n/// `fs::canonicalize()` on Windows produces these prefixes, which are\n/// correct but ugly in user-facing output.\npub fn strip_verbatim_prefix(path: &Path) -> &Path {\n    path.to_str()\n        .and_then(|s| s.strip_prefix(r\"\\\\?\\\"))\n        .map(Path::new)\n        .unwrap_or(path)\n}\n\n/// Determine the identity of the `database`.\npub async fn database_identity(\n    config: &Config,\n    name_or_identity: &str,\n    server: Option<&str>,\n) -> Result<Identity, anyhow::Error> {\n    if let Ok(identity) = Identity::from_hex(name_or_identity) {\n        return Ok(identity);\n    }\n    spacetime_dns(config, name_or_identity, server)\n        .await?\n        .with_context(|| format!(\"failed to find database `{name_or_identity}`.\"))\n}\n\npub(crate) trait ResponseExt: Sized {\n    /// Ensure that this response has the given content-type, especially if it's\n    /// a success response.\n    ///\n    /// This checks the response status for you, so you shouldn't call\n    /// `error_for_status()` beforehand.\n    ///\n    /// If the response does not have the given content type, assume it's an error message\n    /// and return it as such. Success responses with the wrong content type are treated\n    /// as a bug in the API implementation, since that makes it harder to tell what's\n    /// meant to be a structured response and what's a plain-text error message.\n    async fn ensure_content_type(self, content_type: &str) -> anyhow::Result<Self>;\n\n    /// Like [`reqwest::Response::json()`], but handles non-JSON error messages gracefully.\n    async fn json_or_error<T: serde::de::DeserializeOwned>(self) -> anyhow::Result<T>;\n\n    /// Transforms a status of `NOT_FOUND` into `None`.\n    fn found(self) -> Option<Self>;\n}\n\nfn err_status_desc(status: http::StatusCode) -> Option<&'static str> {\n    if status.is_success() {\n        None\n    } else if status.is_client_error() {\n        Some(\"HTTP status client error\")\n    } else if status.is_server_error() {\n        Some(\"HTTP status server error\")\n    } else {\n        Some(\"unexpected HTTP status code\")\n    }\n}\n\nimpl ResponseExt for reqwest::Response {\n    async fn ensure_content_type(self, content_type: &str) -> anyhow::Result<Self> {\n        let status = self.status();\n        if self\n            .headers()\n            .get(http::header::CONTENT_TYPE)\n            .is_some_and(|ty| ty == content_type)\n        {\n            return Ok(self);\n        }\n        let url = self.url();\n        let Some(status_desc) = err_status_desc(status) else {\n            anyhow::bail!(\"HTTP response from url ({url}) was success but did not have content-type: {content_type}\");\n        };\n        let url = url.to_string();\n        let status_err = match self.error_for_status_ref() {\n            Err(e) => anyhow::Error::from(e),\n            Ok(_) => anyhow::anyhow!(\"{status_desc} ({status}) from url ({url})\"),\n        };\n        let err = match self.text().await {\n            Ok(text) => status_err.context(text),\n            Err(err) => anyhow::Error::from(err)\n                .context(format!(\"{status_desc} ({status})\"))\n                .context(\"failed to get response text\"),\n        };\n        Err(err)\n    }\n\n    async fn json_or_error<T: serde::de::DeserializeOwned>(self) -> anyhow::Result<T> {\n        let status = self.status();\n        self.ensure_content_type(\"application/json\")\n            .await?\n            .json()\n            .await\n            .map_err(|err| {\n                let mut err = anyhow::Error::from(err);\n                if let Some(desc) = err_status_desc(status) {\n                    err = err.context(format!(\"malformed json payload for {desc} ({status})\"))\n                }\n                err\n            })\n    }\n\n    fn found(self) -> Option<Self> {\n        (self.status() != http::StatusCode::NOT_FOUND).then_some(self)\n    }\n}\n\n/// Converts a name to a database identity.\npub async fn spacetime_dns(\n    config: &Config,\n    domain: &str,\n    server: Option<&str>,\n) -> Result<Option<Identity>, anyhow::Error> {\n    let client = reqwest::Client::new();\n    let url = format!(\"{}/v1/database/{}/identity\", config.get_host_url(server)?, domain);\n    let Some(res) = client.get(url).send().await?.found() else {\n        return Ok(None);\n    };\n    let text = res.error_for_status()?.text().await?;\n    text.parse()\n        .map(Some)\n        .context(\"identity endpoint did not return an identity\")\n}\n\npub async fn spacetime_server_fingerprint(url: &str) -> anyhow::Result<String> {\n    let builder = reqwest::Client::new().get(format!(\"{url}/v1/identity/public-key\").as_str());\n    let res = builder.send().await?.error_for_status()?;\n    let fingerprint = res.text().await?;\n    Ok(fingerprint)\n}\n\n/// Returns all known names for the given identity.\npub async fn spacetime_reverse_dns(\n    config: &Config,\n    identity: &str,\n    server: Option<&str>,\n) -> Result<GetNamesResponse, anyhow::Error> {\n    let client = reqwest::Client::new();\n    let url = format!(\"{}/v1/database/{}/names\", config.get_host_url(server)?, identity);\n    client.get(url).send().await?.json_or_error().await\n}\n\n/// Add an authorization header, if provided, to the request `builder`.\npub fn add_auth_header_opt(mut builder: RequestBuilder, auth_header: &AuthHeader) -> RequestBuilder {\n    if let Some(token) = &auth_header.token {\n        builder = builder.bearer_auth(token);\n    }\n    builder\n}\n\n/// Gets the `auth_header` for a request to the server depending on how you want\n/// to identify yourself.  If you specify `anon_identity = true` then no\n/// `auth_header` is returned. If you specify an identity this function will try\n/// to find the identity in the config file. If no identity can be found, the\n/// program will `exit(1)`. If you do not specify an identity this function will\n/// either get the default identity if one exists or create and save a new\n/// default identity returning the one that was just created.\n///\n/// # Arguments\n///  * `config` - The config file reference\n///  * `anon_identity` - Whether or not to just use an anonymous identity (no identity)\n///  * `identity_or_name` - The identity to try to lookup, which is typically provided from the command line\npub async fn get_auth_header(\n    config: &mut Config,\n    anon_identity: bool,\n    target_server: Option<&str>,\n    interactive: bool,\n) -> anyhow::Result<AuthHeader> {\n    let token = if anon_identity {\n        None\n    } else {\n        Some(get_login_token_or_log_in(config, target_server, interactive).await?)\n    };\n    Ok(AuthHeader { token })\n}\n\n#[derive(Debug, Clone)]\npub struct AuthHeader {\n    token: Option<String>,\n}\nimpl AuthHeader {\n    pub fn to_header(&self) -> Option<http::HeaderValue> {\n        self.token.as_ref().map(|token| {\n            let mut val = http::HeaderValue::try_from([\"Bearer \", token].concat()).unwrap();\n            val.set_sensitive(true);\n            val\n        })\n    }\n}\n\npub const VALID_PROTOCOLS: [&str; 2] = [\"http\", \"https\"];\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum ModuleLanguage {\n    Csharp,\n    Rust,\n    Javascript,\n    Cpp,\n}\nimpl clap::ValueEnum for ModuleLanguage {\n    fn value_variants<'a>() -> &'a [Self] {\n        &[Self::Csharp, Self::Rust, Self::Javascript, Self::Cpp]\n    }\n    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {\n        match self {\n            Self::Csharp => Some(clap::builder::PossibleValue::new(\"csharp\").aliases([\"c#\", \"cs\", \"C#\", \"CSharp\"])),\n            Self::Rust => Some(clap::builder::PossibleValue::new(\"rust\").aliases([\"rs\", \"Rust\"])),\n            Self::Javascript => Some(clap::builder::PossibleValue::new(\"typescript\").aliases([\n                \"JavaScript\",\n                \"javascript\",\n                \"js\",\n                \"TypeScript\",\n                \"ts\",\n                \"ECMAScript\",\n                \"ecmascript\",\n                \"es\",\n            ])),\n            Self::Cpp => Some(clap::builder::PossibleValue::new(\"cpp\").aliases([\"c++\", \"cxx\", \"C++\", \"Cpp\"])),\n        }\n    }\n}\n\n/// Try to find a SpacetimeDB module directory, checking in order:\n/// 1. `{project_dir}/spacetimedb/` subdirectory\n/// 2. `{project_dir}` itself\n///\n/// Returns the first path that contains a recognizable SpacetimeDB module, or `None`.\npub fn find_module_path(project_dir: &Path) -> Option<PathBuf> {\n    let spacetimedb_subdir = project_dir.join(\"spacetimedb\");\n    if spacetimedb_subdir.is_dir() && detect_module_language(&spacetimedb_subdir).is_ok() {\n        return Some(spacetimedb_subdir);\n    }\n    if project_dir.is_dir() && detect_module_language(project_dir).is_ok() {\n        return Some(project_dir.to_path_buf());\n    }\n    None\n}\n\npub fn detect_module_language(path_to_module: &Path) -> anyhow::Result<ModuleLanguage> {\n    // TODO: Possible add a config file durlng spacetime init with the language\n    if !path_to_module.exists() {\n        anyhow::bail!(\n            \"Module directory does not exist: '{}'. \\\n             Check your --module-path flag or the module-path setting in spacetime.json.\",\n            path_to_module.display()\n        );\n    }\n    // check for Cargo.toml\n    if path_to_module.join(\"Cargo.toml\").exists() {\n        Ok(ModuleLanguage::Rust)\n    } else if path_to_module.is_dir()\n        && path_to_module\n            .read_dir()\n            .map_err(|e| anyhow::anyhow!(\"Failed to read directory {}: {}\", path_to_module.display(), e))?\n            .flatten()\n            .any(|entry| entry.path().extension() == Some(\"csproj\".as_ref()))\n    {\n        Ok(ModuleLanguage::Csharp)\n    } else if path_to_module.join(\"package.json\").exists() {\n        Ok(ModuleLanguage::Javascript)\n    } else if path_to_module.join(\"CMakeLists.txt\").exists() {\n        Ok(ModuleLanguage::Cpp)\n    } else {\n        anyhow::bail!(\"Could not detect the language of the module. Are you in a SpacetimeDB project directory?\")\n    }\n}\n\npub fn url_to_host_and_protocol(url: &str) -> anyhow::Result<(&str, &str)> {\n    if contains_protocol(url) {\n        let protocol = url.split(\"://\").next().unwrap();\n        let host = url.split(\"://\").last().unwrap();\n\n        if !VALID_PROTOCOLS.contains(&protocol) {\n            Err(anyhow::anyhow!(\"Invalid protocol: {protocol}\"))\n        } else {\n            Ok((host, protocol))\n        }\n    } else {\n        Err(anyhow::anyhow!(\"Invalid url: {url}\"))\n    }\n}\n\npub fn contains_protocol(name_or_url: &str) -> bool {\n    name_or_url.contains(\"://\")\n}\n\npub fn host_or_url_to_host_and_protocol(host_or_url: &str) -> (&str, Option<&str>) {\n    if contains_protocol(host_or_url) {\n        let (host, protocol) = url_to_host_and_protocol(host_or_url).unwrap();\n        (host, Some(protocol))\n    } else {\n        (host_or_url, None)\n    }\n}\n\n/// Prompt the user for `y` or `n` from stdin.\n///\n/// Return `false` unless the input is `y`.\npub fn y_or_n(force: bool, prompt: &str) -> anyhow::Result<bool> {\n    if force {\n        println!(\"Skipping confirmation due to --yes\");\n        return Ok(true);\n    }\n    let mut input = String::new();\n    print!(\"{prompt} [y/N]\");\n    std::io::stdout().flush()?;\n    std::io::stdin().read_line(&mut input)?;\n    let input = input.trim().to_lowercase();\n    Ok(input == \"y\" || input == \"yes\")\n}\n\npub fn decode_identity(token: &String) -> anyhow::Result<String> {\n    // Here, we manually extract and decode the claims from the json web token.\n    // We do this without using the `jsonwebtoken` crate because it doesn't seem to have a way to skip signature verification.\n    // But signature verification would require getting the public key from a server, and we don't necessarily want to do that.\n    let token_parts: Vec<_> = token.split('.').collect();\n    if token_parts.len() != 3 {\n        return Err(anyhow::anyhow!(\"Token does not look like a JSON web token: {token}\"));\n    }\n    let decoded_bytes = BASE_64_STD_NO_PAD.decode(token_parts[1])?;\n    let decoded_string = String::from_utf8(decoded_bytes)?;\n\n    let claims_data: IncomingClaims = serde_json::from_str(decoded_string.as_str())?;\n    let claims_data: SpacetimeIdentityClaims = claims_data.try_into()?;\n\n    Ok(claims_data.identity.to_string())\n}\n\npub async fn get_login_token_or_log_in(\n    config: &mut Config,\n    target_server: Option<&str>,\n    interactive: bool,\n) -> anyhow::Result<String> {\n    if let Some(token) = config.spacetimedb_token() {\n        return Ok(token.clone());\n    }\n\n    // Note: We pass `force: false` to `y_or_n` because if we're running non-interactively we want to default to \"no\", not yes!\n    let full_login = interactive\n        && y_or_n(\n            false,\n            // It would be \"ideal\" if we could print the `spacetimedb.com` by deriving it from the `default_auth_host` constant,\n            // but this will change _so_ infrequently that it's not even worth the time to write that code and test it.\n            \"You are not logged in. Would you like to log in with spacetimedb.com?\",\n        )?;\n\n    if full_login {\n        let host = Url::parse(DEFAULT_AUTH_HOST)?;\n        spacetimedb_login_and_save(config, &host, false, true).await\n    } else {\n        let host = Url::parse(&config.get_host_url(target_server)?)?;\n        spacetimedb_login_and_save(config, &host, true, true).await\n    }\n}\n\npub fn resolve_sibling_binary(bin_name: &str) -> anyhow::Result<PathBuf> {\n    let resolved_exe = std::env::current_exe().context(\"could not retrieve current exe\")?;\n    let bin_path = resolved_exe\n        .parent()\n        .unwrap()\n        .join(bin_name)\n        .with_extension(std::env::consts::EXE_EXTENSION);\n    Ok(bin_path)\n}\n"
  },
  {
    "path": "crates/cli/src/version.rs",
    "content": "pub const CLI_VERSION: &str = env!(\"CARGO_PKG_VERSION\");\npub const CLI_GIT_HASH: &str = env!(\"GIT_HASH\");\n\npub fn long_version() -> String {\n    format!(\n        \"\\\nPath: {path}\nCommit: {commit}\nspacetimedb tool version {CLI_VERSION}; spacetimedb-lib version {lib_ver};\",\n        path = std::env::current_exe().unwrap_or_else(|_| \"<unknown>\".into()).display(),\n        commit = CLI_GIT_HASH,\n        lib_ver = spacetimedb_lib::version::spacetimedb_lib_version()\n    )\n}\n"
  },
  {
    "path": "crates/cli/tools/sublime/SpacetimeDBSQL.sublime-syntax",
    "content": "%YAML 1.2\n---\nname: SQL (SpacetimeDB)\nfile_extensions:\n  - sql\n  - stsql\nscope: source.sql\n\nvariables:\n  ws: '[ \\t]*'\n  wsnl: '([ \\t\\n])*'\n\n  int_literal: '[0-9](?:[0-9_])*'\n  # 00-23\n  time_hour: '[0-2][0-9]'\n  # 00-59\n  time_minute: '[0-5][0-9]'\n  # 00-58, 00-59, 00-60 (leap second rules)\n  time_second: '[0-6][0-9]'\n  # ( \"+\" / \"-\" ) time-hour \":\" time-minute\n  time_numoffset: '[+-] {{time_hour}} : {{time_minute}}'\n\n  # time-hour \":\" time-minute \":\" time-second\n  partial_time: '{{time_hour}} : {{time_minute}} : {{time_second}}'\n\n  # partial-time time-offset\n  full_time: '{{partial_time}} {{time_numoffset}}'\n\n  # 2000\n  date_fullyear: '[0-9]{4}'\n  # 01-12\n  date_month: '[0-1][0-9]'\n  # 01-28, 01-29, 01-30, 01-31 based on month/year\n  date_mday: '[0-3][0-9]'\n\n  # date-fullyear \"-\" date-month \"-\" date-mday\n  full_date: '{{date_fullyear}} - {{date_month}} - {{date_mday}}'\n\n  # full-date T|%20 full-time\n  offset_date_time: '{{full_date}} [T ] {{full_time}}'\n  # full-date T|%20 partial-time\n  local_date_time: '{{full_date}} [T ] {{partial_time}}'\n\n  date_time: '{{offset_date_time}} | {{local_date_time}} | {{full_date}} | {{partial_time}}'\n\ncontexts:\n  # The prototype context is prepended to all contexts but those setting\n  # meta_include_prototype: false.\n  prototype:\n    - include: comments\n\n  main:\n    # The main context is the initial starting point of our syntax.\n    # Include other contexts from here (or specify them directly).\n    - include: keywords\n    - include: parens\n    - include: booleans\n    - include: numbers\n    - include: date-time\n    - include: strings\n    - include: ident\n    - match: '{{ws}}$'\n      # Don't show an incomplete line as invalid to avoid frequent red\n      # highlighting while typing.\n      pop: true\n    - match: '\\w+|.'\n      scope: invalid.illegal.value.sql\n      pop: true\n\n  ident:\n    - name: variable.parameter.sql\n      match: \\b([a-zA-Z0-9_]+)\\b\n\n  keywords:\n    - name: keyword.operator.point.pgsql\n      match: \\.\n    - name: keyword.operator.comma.pgsql\n      match: \\,\n    - name: keyword.operator.semicolon.pgsql\n      match: \\;\n    - name: keyword.operator.star.pgsql\n      match: \\*\n    - match: '(?i)\\b(select|from|insert|into|join|values|update|delete|create|where|order by)\\b'\n      scope: keyword.control.sql\n\n    - match: '[!<>]?=|<>|<|>'\n      scope: keyword.operator.comparison.sql\n\n    - match: \\+|\\-|\\*|/|\\^\n      scope: keyword.operator.arithmetic.sql\n\n    - match: \\b(and|in|not|or)\\b\n      comment: keyword operators that evaluate to true or false\n      scope: keyword.operator.logical.sql\n\n  booleans:\n    - match: (?i)\\b(true|false|null)\\b\n      scope: constant.language.source.sql\n\n  numbers:\n    # Binary Float\n    - match: '\\b({{int_literal}}(?:\\.{{int_literal}})?)f\\b'\n      scope: constant.numeric.source.sql\n    - match: '\\b({{int_literal}}(?:\\.{{int_literal}})?)\\b'\n      scope: constant.numeric.source.sql\n\n  strings:\n    # Strings begin and end with quotes, and use backslashes as an escape\n    # character\n    - match: '\"'\n      scope: punctuation.definition.string.begin.source.sql\n      push: double_quoted_string\n\n    - match: \"'\"\n      scope: punctuation.definition.string.begin.source.sql\n      push: single_quoted_string\n\n  double_quoted_string:\n    - meta_scope: string.quoted.double.source.sql\n    - match: '\\\\.'\n      scope: constant.character.escape.source.sql\n    - match: '\"'\n      scope: punctuation.definition.string.end.source.sql\n      pop: true\n\n  single_quoted_string:\n    - meta_scope: string.quoted.double.source.sql\n    - match: '\\\\.'\n      scope: constant.character.escape.source.sql\n    - match: \"'\"\n      scope: punctuation.definition.string.end.source.sql\n      pop: true\n\n  date-time:\n    - match: \"(d|t|dt)'\"\n      scope: constant.other.datetime.begin.source.sql\n      push: single_quoted_date\n    - match: '(d|t|dt)\"'\n      scope: constant.other.datetime.begin.source.sql\n      push: double_quoted_date\n\n  double_quoted_date:\n    - meta_scope: string.quoted.double.source.sql\n    - match: '(?x) {{date_time}}'\n      scope: constant.character.escape.source.sql\n    - match: '\"'\n      scope: constant.other.datetime.end.source.sql\n      pop: true\n\n  single_quoted_date:\n    - meta_scope: string.quoted.double.source.sql\n    - match: '(?x) {{date_time}}'\n      scope: constant.character.escape.source.sql\n    - match: \"'\"\n      scope: constant.other.datetime.end.source.sql\n      pop: true\n\n  parens:\n    - match: \\(\n      push: brackets\n    - match: \\)\n      scope: invalid.illegal.stray-bracket-end\n\n  brackets:\n    - match: \\)\n      pop: true\n    - include: parens\n\n  comments:\n    # Comments begin with a '--' and finish at the end of the line.\n    - match: '--'\n      scope: punctuation.definition.comment.source.sql\n      push:\n        # This is an anonymous context push for brevity.\n        - meta_scope: comment.line.double-slash.source.sql\n        - match: $\\n?\n          pop: true\n"
  },
  {
    "path": "crates/client-api/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-client-api\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The HTTP API for SpacetimeDB\"\nrust-version.workspace = true\n\n[dependencies]\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-core.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-datastore.workspace = true\nspacetimedb-lib = { workspace = true, features = [\"serde\"] }\nspacetimedb-paths.workspace = true\nspacetimedb-schema.workspace = true\n\nbase64.workspace = true\ntokio = { version = \"1.2\", features = [\"full\"] }\nlazy_static = \"1.4.0\"\nlog = \"0.4.4\"\nserde = \"1.0.136\"\nserde_json = { version = \"1.0\", features = [\"raw_value\"] }\nanyhow = { version = \"1.0.57\", features = [\"backtrace\"] }\nregex = \"1\"\nprometheus.workspace = true\nemail_address = \"0.2.3\"\ntempfile.workspace = true\nasync-trait = \"0.1.60\"\nchrono = { workspace = true, features = [\"serde\"] }\nrand.workspace = true\naxum.workspace = true\naxum-extra.workspace = true\nhyper.workspace = true\nhyper-util.workspace = true\nhttp.workspace = true\nheaders.workspace = true\nmime = \"0.3.17\"\ntokio-stream = { version = \"0.1.12\", features = [\"sync\"] }\ntower-layer.workspace = true\ntower-service.workspace = true\ntower-http.workspace = true\nfutures = \"0.3\"\nbytes = \"1\"\ntracing.workspace = true\nbytestring = \"1\"\ntokio-tungstenite.workspace = true\nitoa.workspace = true\nderive_more = \"0.99.17\"\nuuid.workspace = true\njsonwebtoken.workspace = true\nscopeguard.workspace = true\nserde_with.workspace = true\nasync-stream.workspace = true\ncrossbeam-queue.workspace = true\nhumantime.workspace = true\nthiserror.workspace = true\n\n[target.'cfg(not(target_env = \"msvc\"))'.dependencies]\njemalloc_pprof.workspace = true\n\n[dev-dependencies]\njsonwebtoken.workspace = true\npretty_assertions = { workspace = true, features = [\"unstable\"] }\nproptest.workspace = true\ntokio = { workspace = true, features = [\"full\", \"test-util\"] }\ntoml.workspace = true\n\n[lints]\nworkspace = true\n\n[features]\nunstable = []\n"
  },
  {
    "path": "crates/client-api/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/client-api/src/auth.rs",
    "content": "use anyhow::anyhow;\nuse axum::extract::{Query, Request, State};\nuse axum::middleware::Next;\nuse axum::response::IntoResponse;\nuse axum_extra::typed_header::TypedHeader;\nuse headers::{authorization, HeaderMapExt};\nuse http::{request, HeaderValue, StatusCode};\nuse serde::{Deserialize, Serialize};\nuse spacetimedb::auth::identity::{ConnectionAuthCtx, SpacetimeIdentityClaims};\nuse spacetimedb::auth::identity::{JwtError, JwtErrorKind};\nuse spacetimedb::auth::token_validation::{\n    new_validator, DefaultValidator, TokenSigner, TokenValidationError, TokenValidator,\n};\nuse spacetimedb::auth::JwtKeys;\nuse spacetimedb::energy::EnergyQuanta;\nuse spacetimedb::identity::Identity;\nuse spacetimedb_data_structures::map::HashMap;\nuse std::time::{Duration, SystemTime};\nuse uuid::Uuid;\n\nuse crate::{log_and_500, ControlStateDelegate, NodeDelegate};\nuse base64::{engine::general_purpose, Engine};\n\n/// Credentials for login for a spacetime identity, represented as a JWT.\n///\n/// This can be passed as a header `Authentication: Bearer $token` or as\n/// a query param `?token=$token`, with the former taking precedence over\n/// the latter.\n#[derive(Clone, Deserialize)]\npub struct SpacetimeCreds {\n    token: String,\n}\n\npub const LOCALHOST: &str = \"localhost\";\n\nimpl SpacetimeCreds {\n    /// The JWT token representing these credentials.\n    pub fn token(&self) -> &str {\n        &self.token\n    }\n\n    pub fn from_signed_token(token: String) -> Self {\n        Self { token }\n    }\n\n    fn extract_jwt_payload_string(&self) -> Option<String> {\n        let parts: Vec<&str> = self.token.split('.').collect();\n        if parts.len() != 3 {\n            return None;\n        }\n\n        let payload_encoded = parts[1];\n        let decoded_bytes = general_purpose::URL_SAFE_NO_PAD.decode(payload_encoded).ok()?;\n        let json_str = String::from_utf8(decoded_bytes).ok()?;\n\n        Some(json_str)\n    }\n\n    pub fn to_header_value(&self) -> HeaderValue {\n        let mut val = HeaderValue::try_from([\"Bearer \", self.token()].concat()).unwrap();\n        val.set_sensitive(true);\n        val\n    }\n\n    /// Extract credentials from the headers or else query string of a request.\n    fn from_request_parts(parts: &request::Parts) -> Result<Option<Self>, headers::Error> {\n        let header = parts\n            .headers\n            .typed_try_get::<headers::Authorization<authorization::Bearer>>()?;\n        if let Some(headers::Authorization(bearer)) = header {\n            let token = bearer.token().to_owned();\n            return Ok(Some(SpacetimeCreds { token }));\n        }\n        if let Ok(Query(creds)) = Query::<Self>::try_from_uri(&parts.uri) {\n            return Ok(Some(creds));\n        }\n        Ok(None)\n    }\n}\n\n/// The auth information in a request.\n///\n/// This is inserted as an extension by [`auth_middleware`]; make sure that's applied if you're making expecting\n/// this to be present.\n#[derive(Clone)]\npub struct SpacetimeAuth {\n    pub creds: SpacetimeCreds,\n    pub claims: SpacetimeIdentityClaims,\n    /// The JWT payload as a json string (after base64 decoding).\n    pub jwt_payload: Box<str>,\n}\n\nimpl SpacetimeAuth {\n    pub fn new(creds: SpacetimeCreds, claims: SpacetimeIdentityClaims) -> Result<Self, anyhow::Error> {\n        let payload = creds\n            .extract_jwt_payload_string()\n            .ok_or_else(|| anyhow!(\"Failed to extract JWT payload\"))?\n            .into_boxed_str();\n        Ok(Self {\n            creds,\n            claims,\n            jwt_payload: payload,\n        })\n    }\n}\n\nimpl From<SpacetimeAuth> for ConnectionAuthCtx {\n    fn from(auth: SpacetimeAuth) -> Self {\n        ConnectionAuthCtx {\n            claims: auth.claims,\n            jwt_payload: auth.jwt_payload.clone(),\n        }\n    }\n}\n\nuse jsonwebtoken;\n\npub struct TokenClaims {\n    pub issuer: Box<str>,\n    pub subject: Box<str>,\n    pub audience: Box<[Box<str>]>,\n    pub extra: Option<HashMap<Box<str>, serde_json::Value>>,\n}\n\nimpl From<SpacetimeAuth> for TokenClaims {\n    fn from(auth: SpacetimeAuth) -> Self {\n        Self {\n            issuer: auth.claims.issuer,\n            subject: auth.claims.subject,\n            audience: auth.claims.audience,\n            extra: auth.claims.extra,\n        }\n    }\n}\n\nimpl TokenClaims {\n    pub fn new(issuer: Box<str>, subject: Box<str>) -> Self {\n        Self {\n            issuer,\n            subject,\n            audience: [].into(),\n            extra: None,\n        }\n    }\n\n    // Compute the id from the issuer and subject.\n    pub fn id(&self) -> Identity {\n        Identity::from_claims(&self.issuer, &self.subject)\n    }\n\n    /// Encode the claims into a JWT token and sign it with the provided signer.\n    /// This also adds claims for expiry and issued at time.\n    /// Returns an object representing the claims and the signed token.\n    pub fn encode_and_sign_with_expiry(\n        &self,\n        signer: &impl TokenSigner,\n        expiry: Option<Duration>,\n    ) -> Result<(SpacetimeIdentityClaims, String), JwtError> {\n        let iat = SystemTime::now();\n        let exp = expiry.map(|dur| iat + dur);\n        let claims = SpacetimeIdentityClaims {\n            identity: self.id(),\n            subject: self.subject.clone(),\n            issuer: self.issuer.clone(),\n            audience: self.audience.clone(),\n            extra: self.extra.clone(),\n            iat,\n            exp,\n        };\n        let token = signer.sign(&claims)?;\n        Ok((claims, token))\n    }\n\n    /// Encode the claims into a JWT token and sign it with the provided signer.\n    /// This also adds a claim for issued at time.\n    /// Returns an object representing the claims and the signed token.\n    pub fn encode_and_sign(&self, signer: &impl TokenSigner) -> Result<(SpacetimeIdentityClaims, String), JwtError> {\n        self.encode_and_sign_with_expiry(signer, None)\n    }\n}\n\nimpl SpacetimeAuth {\n    /// Allocate a new identity, and mint a new token for it.\n    pub async fn alloc(ctx: &(impl NodeDelegate + ControlStateDelegate + ?Sized)) -> axum::response::Result<Self> {\n        // Generate claims with a random subject.\n        let subject = Uuid::new_v4().to_string().into();\n        let claims = TokenClaims {\n            issuer: ctx.jwt_auth_provider().local_issuer().into(),\n            subject,\n            // Placeholder audience.\n            audience: [\"spacetimedb\".into()].into(),\n            extra: None,\n        };\n\n        let (claims, token) = claims.encode_and_sign(ctx.jwt_auth_provider()).map_err(log_and_500)?;\n        let creds = SpacetimeCreds::from_signed_token(token);\n        // Pulling out the payload should never fail, since we just made it.\n        Self::new(creds, claims).map_err(log_and_500)\n    }\n\n    /// Get the auth credentials as headers to be returned from an endpoint.\n    pub fn into_headers(self) -> (TypedHeader<SpacetimeIdentity>, TypedHeader<SpacetimeIdentityToken>) {\n        (\n            TypedHeader(SpacetimeIdentity(self.claims.identity)),\n            TypedHeader(SpacetimeIdentityToken(self.creds)),\n        )\n    }\n\n    // Sign a new token with the same claims and a new expiry.\n    // Note that this will not change the issuer, so the private_key might not match.\n    // We do this to create short-lived tokens that we will be able to verify.\n    pub fn re_sign_with_expiry(\n        &self,\n        signer: &impl TokenSigner,\n        expiry: Duration,\n    ) -> Result<(SpacetimeIdentityClaims, String), JwtError> {\n        TokenClaims::from(self.clone()).encode_and_sign_with_expiry(signer, Some(expiry))\n    }\n}\n\n// JwtAuthProvider is used for signing and verifying JWT tokens.\npub trait JwtAuthProvider: Sync + Send + TokenSigner {\n    type TV: TokenValidator + Send + Sync;\n    /// Used to validate incoming JWTs.\n    fn validator(&self) -> &Self::TV;\n\n    /// The issuer to use when signing JWTs.\n    fn local_issuer(&self) -> &str;\n\n    /// Return the public key used to verify JWTs, as the bytes of a PEM public key file.\n    ///\n    /// The `/identity/public-key` route calls this method to return the public key to callers.\n    fn public_key_bytes(&self) -> &[u8];\n}\n\npub struct JwtKeyAuthProvider<TV: TokenValidator + Send + Sync> {\n    keys: JwtKeys,\n    local_issuer: Box<str>,\n    validator: TV,\n}\n\npub type DefaultJwtAuthProvider = JwtKeyAuthProvider<DefaultValidator>;\n\n// Create a new AuthEnvironment using the default caching validator.\npub fn default_auth_environment(keys: JwtKeys, local_issuer: Box<str>) -> JwtKeyAuthProvider<DefaultValidator> {\n    let validator = new_validator(keys.public.clone(), local_issuer.clone());\n    JwtKeyAuthProvider::new(keys, local_issuer, validator)\n}\n\nimpl<TV: TokenValidator + Send + Sync> JwtKeyAuthProvider<TV> {\n    fn new(keys: JwtKeys, local_issuer: Box<str>, validator: TV) -> Self {\n        Self {\n            keys,\n            local_issuer,\n            validator,\n        }\n    }\n}\n\nimpl<TV: TokenValidator + Send + Sync> TokenSigner for JwtKeyAuthProvider<TV> {\n    fn sign<T: Serialize>(&self, claims: &T) -> Result<String, JwtError> {\n        let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::ES256);\n        jsonwebtoken::encode(&header, &claims, &self.keys.private)\n    }\n}\n\nimpl<TV: TokenValidator + Send + Sync> JwtAuthProvider for JwtKeyAuthProvider<TV> {\n    type TV = TV;\n\n    fn validator(&self) -> &Self::TV {\n        &self.validator\n    }\n\n    fn local_issuer(&self) -> &str {\n        &self.local_issuer\n    }\n\n    fn public_key_bytes(&self) -> &[u8] {\n        &self.keys.public_pem\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::auth::{SpacetimeCreds, TokenClaims};\n    use anyhow::Ok;\n\n    use spacetimedb::auth::{token_validation::TokenValidator, JwtKeys};\n    use spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap, HashSet};\n\n    // Make sure that when we encode TokenClaims, we can decode to get the expected identity.\n    #[tokio::test]\n    async fn decode_encoded_token() -> Result<(), anyhow::Error> {\n        let kp = JwtKeys::generate()?;\n\n        let claims = TokenClaims {\n            issuer: \"localhost\".into(),\n            subject: \"test-subject\".into(),\n            audience: [\"spacetimedb\".into()].into(),\n            extra: None,\n        };\n        let id = claims.id();\n        let (_, token) = claims.encode_and_sign(&kp.private)?;\n        let decoded = kp.public.validate_token(&token).await?;\n\n        assert_eq!(decoded.identity, id);\n        Ok(())\n    }\n\n    fn to_hashmap(value: serde_json::Value) -> HashMap<Box<str>, serde_json::Value> {\n        let mut map = HashMap::new();\n        value.as_object().unwrap().iter().for_each(|(k, v)| {\n            map.insert(k.clone().into(), v.clone());\n        });\n        map\n    }\n\n    // Make sure that when we encode TokenClaims, we can decode the extra claims.\n    #[tokio::test]\n    async fn decode_encoded_token_with_extra_claims() -> Result<(), anyhow::Error> {\n        let kp = JwtKeys::generate()?;\n\n        let claims = TokenClaims {\n            issuer: \"localhost\".into(),\n            subject: \"test-subject\".into(),\n            audience: [\"spacetimedb\".into()].into(),\n            extra: Some(to_hashmap(serde_json::json!({\"custom_claim\": \"value\"}))),\n        };\n        let id = claims.id();\n        let (_, token) = claims.encode_and_sign(&kp.private)?;\n        let decoded = kp.public.validate_token(&token).await?;\n\n        assert_eq!(decoded.identity, id);\n        let custom_claim_value = decoded.extra.as_ref().unwrap().get(\"custom_claim\").unwrap();\n        assert_eq!(custom_claim_value.as_str().unwrap(), \"value\");\n        Ok(())\n    }\n\n    // Test that extracting a JWT payload from a valid token gets the json representation.\n    #[tokio::test]\n    async fn extract_payload() -> Result<(), anyhow::Error> {\n        let kp = JwtKeys::generate()?;\n\n        let dummy_audience: Box<str> = \"spacetimedb\".into();\n        let claims = TokenClaims {\n            issuer: \"localhost\".into(),\n            subject: \"test-subject\".into(),\n            audience: [dummy_audience.clone()].into(),\n            extra: None,\n        };\n        let (_, token) = claims.encode_and_sign(&kp.private)?;\n        let st_creds = SpacetimeCreds::from_signed_token(token);\n        let payload = st_creds\n            .extract_jwt_payload_string()\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to extract JWT payload\"))?;\n        // Make sure it is valid json.\n        let parsed: serde_json::Value = serde_json::from_str(&payload)?;\n        assert_eq!(parsed.get(\"iss\").unwrap().as_str().unwrap(), &*claims.issuer);\n        assert_eq!(parsed.get(\"sub\").unwrap().as_str().unwrap(), &*claims.subject);\n        assert_eq!(\n            parsed.get(\"aud\").unwrap().as_array().unwrap()[0].as_str().unwrap(),\n            &*dummy_audience\n        );\n        let as_object = parsed\n            .as_object()\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to parse JWT payload as object\"))?;\n        let keys: HashSet<String> = as_object.keys().map(|s| s.to_string()).collect();\n        let expected_keys = vec![\"iss\", \"sub\", \"aud\", \"iat\", \"exp\", \"hex_identity\"]\n            .into_iter()\n            .map(|s| s.to_string())\n            .collect::<HashSet<String>>();\n        assert_eq!(keys, expected_keys);\n        Ok(())\n    }\n}\n\npub async fn validate_token<S: NodeDelegate>(\n    state: &S,\n    token: &str,\n) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n    state.jwt_auth_provider().validator().validate_token(token).await\n}\n\npub struct SpacetimeAuthHeader {\n    auth: Option<SpacetimeAuth>,\n}\n\n#[async_trait::async_trait]\nimpl<S: NodeDelegate + Send + Sync> axum::extract::FromRequestParts<S> for SpacetimeAuthHeader {\n    type Rejection = AuthorizationRejection;\n    async fn from_request_parts(parts: &mut request::Parts, state: &S) -> Result<Self, Self::Rejection> {\n        let Some(creds) = SpacetimeCreds::from_request_parts(parts)? else {\n            return Ok(Self { auth: None });\n        };\n\n        let claims = validate_token(state, &creds.token)\n            .await\n            .map_err(AuthorizationRejection::Custom)?;\n\n        let payload = creds.extract_jwt_payload_string().ok_or_else(|| {\n            AuthorizationRejection::Custom(TokenValidationError::Other(anyhow!(\"Internal error parsing token\")))\n        })?;\n        let auth = SpacetimeAuth {\n            creds,\n            claims,\n            jwt_payload: payload.into(),\n        };\n        Ok(Self { auth: Some(auth) })\n    }\n}\n\n/// A response by the API signifying that an authorization was rejected with the `reason` for this.\n#[derive(Debug, derive_more::From)]\npub enum AuthorizationRejection {\n    Jwt(JwtError),\n    Header(headers::Error),\n    Custom(TokenValidationError),\n    Required,\n}\n\nimpl IntoResponse for AuthorizationRejection {\n    fn into_response(self) -> axum::response::Response {\n        // Most likely, the server key was rotated.\n        const ROTATED: (StatusCode, &str) = (\n            StatusCode::UNAUTHORIZED,\n            \"Authorization failed: token not signed by this instance\",\n        );\n        // The JWT is malformed, see SpacetimeCreds for specifics on the format.\n        const INVALID: (StatusCode, &str) = (StatusCode::BAD_REQUEST, \"Authorization is invalid: malformed token\");\n        // Sensible fallback if no auth header is present.\n        const REQUIRED: (StatusCode, &str) = (StatusCode::UNAUTHORIZED, \"Authorization required\");\n\n        log::trace!(\"Authorization rejection: {self:?}\");\n\n        match self {\n            AuthorizationRejection::Jwt(e) if *e.kind() == JwtErrorKind::InvalidSignature => ROTATED.into_response(),\n            AuthorizationRejection::Jwt(_) | AuthorizationRejection::Header(_) => INVALID.into_response(),\n            AuthorizationRejection::Custom(msg) => (StatusCode::UNAUTHORIZED, format!(\"{msg:?}\")).into_response(),\n            AuthorizationRejection::Required => REQUIRED.into_response(),\n        }\n    }\n}\n\nimpl SpacetimeAuthHeader {\n    pub fn get(self) -> Option<SpacetimeAuth> {\n        self.auth\n    }\n\n    /// Given an authorization header we will try to get the identity and token from the auth header (as JWT).\n    /// If there is no JWT in the auth header we will create a new identity and token and return it.\n    pub async fn get_or_create(\n        self,\n        ctx: &(impl NodeDelegate + ControlStateDelegate + ?Sized),\n    ) -> axum::response::Result<SpacetimeAuth> {\n        match self.auth {\n            Some(auth) => Ok(auth),\n            None => SpacetimeAuth::alloc(ctx).await,\n        }\n    }\n}\n\npub struct SpacetimeAuthRequired(pub SpacetimeAuth);\n\n#[async_trait::async_trait]\nimpl<S: NodeDelegate + Send + Sync> axum::extract::FromRequestParts<S> for SpacetimeAuthRequired {\n    type Rejection = AuthorizationRejection;\n    async fn from_request_parts(parts: &mut request::Parts, state: &S) -> Result<Self, Self::Rejection> {\n        let auth = SpacetimeAuthHeader::from_request_parts(parts, state).await?;\n        let auth = auth.get().ok_or(AuthorizationRejection::Required)?;\n        Ok(SpacetimeAuthRequired(auth))\n    }\n}\n\npub struct SpacetimeIdentity(pub Identity);\nimpl headers::Header for SpacetimeIdentity {\n    fn name() -> &'static http::HeaderName {\n        static NAME: http::HeaderName = http::HeaderName::from_static(\"spacetime-identity\");\n        &NAME\n    }\n\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(_values: &mut I) -> Result<Self, headers::Error> {\n        unimplemented!()\n    }\n\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([self.0.to_hex().as_str().try_into().unwrap()])\n    }\n}\n\npub struct SpacetimeIdentityToken(pub SpacetimeCreds);\nimpl headers::Header for SpacetimeIdentityToken {\n    fn name() -> &'static http::HeaderName {\n        static NAME: http::HeaderName = http::HeaderName::from_static(\"spacetime-identity-token\");\n        &NAME\n    }\n\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(_values: &mut I) -> Result<Self, headers::Error> {\n        unimplemented!()\n    }\n\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([self.0.token().try_into().unwrap()])\n    }\n}\n\npub struct SpacetimeEnergyUsed(pub EnergyQuanta);\nimpl headers::Header for SpacetimeEnergyUsed {\n    fn name() -> &'static http::HeaderName {\n        static NAME: http::HeaderName = http::HeaderName::from_static(\"spacetime-energy-used\");\n        &NAME\n    }\n\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(_values: &mut I) -> Result<Self, headers::Error> {\n        unimplemented!()\n    }\n\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        let mut buf = itoa::Buffer::new();\n        let value = buf.format(self.0.get());\n        values.extend([value.try_into().unwrap()]);\n    }\n}\n\npub struct SpacetimeExecutionDurationMicros(pub Duration);\nimpl headers::Header for SpacetimeExecutionDurationMicros {\n    fn name() -> &'static http::HeaderName {\n        static NAME: http::HeaderName = http::HeaderName::from_static(\"spacetime-execution-duration-micros\");\n        &NAME\n    }\n\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(_values: &mut I) -> Result<Self, headers::Error> {\n        unimplemented!()\n    }\n\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([(self.0.as_micros() as u64).into()])\n    }\n}\n\npub async fn anon_auth_middleware<S: ControlStateDelegate + NodeDelegate>(\n    State(worker_ctx): State<S>,\n    auth: SpacetimeAuthHeader,\n    mut req: Request,\n    next: Next,\n) -> axum::response::Result<impl IntoResponse> {\n    let auth = auth.get_or_create(&worker_ctx).await?;\n    req.extensions_mut().insert(auth.clone());\n    let resp = next.run(req).await;\n    Ok((auth.into_headers(), resp))\n}\n"
  },
  {
    "path": "crates/client-api/src/lib.rs",
    "content": "use std::fmt;\nuse std::future::Future;\nuse std::num::NonZeroU8;\nuse std::sync::Arc;\n\nuse anyhow::anyhow;\nuse async_trait::async_trait;\nuse axum::response::ErrorResponse;\nuse bytes::Bytes;\nuse http::StatusCode;\n\nuse spacetimedb::client::ClientActorIndex;\nuse spacetimedb::energy::{EnergyBalance, EnergyQuanta};\nuse spacetimedb::host::{HostController, MigratePlanResult, ModuleHost, NoSuchModule, UpdateDatabaseResult};\nuse spacetimedb::identity::{AuthCtx, Identity};\nuse spacetimedb::messages::control_db::{Database, HostType, Node, Replica};\nuse spacetimedb::sql;\nuse spacetimedb_client_api_messages::http::{SqlStmtResult, SqlStmtStats};\nuse spacetimedb_client_api_messages::name::{DomainName, InsertDomainResult, RegisterTldResult, SetDomainsResult, Tld};\nuse spacetimedb_lib::{ProductTypeElement, ProductValue};\nuse spacetimedb_paths::server::ModuleLogsDir;\nuse spacetimedb_schema::auto_migrate::{MigrationPolicy, PrettyPrintStyle};\nuse thiserror::Error;\nuse tokio::sync::watch;\n\npub mod auth;\npub mod routes;\npub mod util;\n\n/// The default value for the `confirmed` reads parameter when the client does\n/// not specify it explicitly. When `true`, the server waits for durability\n/// confirmation before sending subscription updates and SQL results.\npub const DEFAULT_CONFIRMED_READS: bool = true;\n\n/// Defines the state / environment of a SpacetimeDB node from the PoV of the\n/// client API.\n///\n/// Types returned here should be considered internal state and **never** be\n/// surfaced to the API.\n#[async_trait]\npub trait NodeDelegate: Send + Sync {\n    /// Error returned by [Self::leader].\n    ///\n    /// Must satisfy [MaybeMisdirected] to indicate whether the method would\n    /// never succeed on this node due to the database not being scheduled on it.\n    ///\n    /// The [Into<axum::response::ErrorResponse] shall convert the error into an\n    /// HTTP response, providing an error message suitable for API clients.\n    /// The [fmt::Display] impl is used for logging the error, and may provide\n    /// additional context useful for debugging purposes.\n    type GetLeaderHostError: MaybeMisdirected + Into<axum::response::ErrorResponse> + fmt::Display + Send + Sync;\n\n    fn gather_metrics(&self) -> Vec<prometheus::proto::MetricFamily>;\n    fn client_actor_index(&self) -> &ClientActorIndex;\n\n    type JwtAuthProviderT: auth::JwtAuthProvider;\n    fn jwt_auth_provider(&self) -> &Self::JwtAuthProviderT;\n    /// Return the leader [`Host`] of `database_id`.\n    ///\n    /// The [`Host`] is spawned implicitly if not already running.\n    async fn leader(&self, database_id: u64) -> Result<Host, Self::GetLeaderHostError>;\n    fn module_logs_dir(&self, replica_id: u64) -> ModuleLogsDir;\n}\n\n/// Predicate on the [NodeDelegate::GetLeaderHostError].\n///\n/// Normally, the routing layer determines the cluster node hosting the current\n/// leader. In between the routing decision and actually executing the API\n/// handler on the node, the database's state can, however, change, so that the\n/// [NodeDelegate::leader] method is unable to provide the current leader [Host].\n///\n/// This trait allows to detect this case.\n//\n// Used in the logs endpoint to allow serving module logs even if\n// the database is not currently running.\npub trait MaybeMisdirected {\n    /// Return `true` if the current node is not responsible for the leader\n    /// replica of the requested database.\n    ///\n    /// This could be the case if:\n    ///\n    /// - the current or most-recently-known leader is not assigned to the node\n    /// - no leader is currently known\n    /// - the database does not exist\n    ///\n    /// Note that a database may not be running (e.g. due to being in a\n    /// suspended state). If its last leader is known and assigned to the\n    /// current node, this method shall return `true`.\n    fn is_misdirected(&self) -> bool;\n}\n\n/// Client view of a running module.\npub struct Host {\n    pub replica_id: u64,\n    host_controller: HostController,\n}\n\nimpl Host {\n    pub fn new(replica_id: u64, host_controller: HostController) -> Self {\n        Self {\n            replica_id,\n            host_controller,\n        }\n    }\n\n    pub async fn module(&self) -> Result<ModuleHost, NoSuchModule> {\n        self.host_controller.get_module_host(self.replica_id).await\n    }\n\n    /// Wait for the module host to become available, retrying with backoff.\n    ///\n    /// This is useful for routes like `/schema` that may be called while the\n    /// database is still loading. Instead of returning an immediate 500, we\n    /// poll for up to `timeout` before giving up.\n    pub async fn wait_for_module(&self, timeout: std::time::Duration) -> Result<ModuleHost, NoSuchModule> {\n        let deadline = tokio::time::Instant::now() + timeout;\n        let mut interval = tokio::time::Duration::from_millis(100);\n        loop {\n            match self.host_controller.get_module_host(self.replica_id).await {\n                Ok(module) => return Ok(module),\n                Err(NoSuchModule) => {\n                    if tokio::time::Instant::now() >= deadline {\n                        return Err(NoSuchModule);\n                    }\n                    tokio::time::sleep(interval).await;\n                    // Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1s, 1s, ...\n                    interval = (interval * 2).min(tokio::time::Duration::from_secs(1));\n                }\n            }\n        }\n    }\n\n    pub async fn module_watcher(&self) -> Result<watch::Receiver<ModuleHost>, NoSuchModule> {\n        self.host_controller.watch_module_host(self.replica_id).await\n    }\n\n    pub async fn exec_sql(\n        &self,\n        auth: AuthCtx,\n        database: Database,\n        confirmed_read: bool,\n        body: String,\n    ) -> axum::response::Result<Vec<SqlStmtResult<ProductValue>>> {\n        let module_host = self\n            .module()\n            .await\n            .map_err(|_| (StatusCode::NOT_FOUND, \"module not found\".to_string()))?;\n\n        let (tx_offset, durable_offset, json) = self\n            .host_controller\n            .using_database(database, self.replica_id, move |db| async move {\n                tracing::info!(sql = body);\n                let mut header = vec![];\n                let sql_start = std::time::Instant::now();\n                let sql_span = tracing::trace_span!(\"execute_sql\", total_duration = tracing::field::Empty,);\n                let _guard = sql_span.enter();\n\n                let result = sql::execute::run(\n                    db.clone(),\n                    body,\n                    auth,\n                    Some(module_host.info.subscriptions.clone()),\n                    Some(module_host),\n                    &mut header,\n                )\n                .await\n                .map_err(|e| {\n                    log::warn!(\"{e}\");\n                    (StatusCode::BAD_REQUEST, e.to_string())\n                })?;\n\n                let total_duration = sql_start.elapsed();\n                drop(_guard);\n                sql_span.record(\"total_duration\", tracing::field::debug(total_duration));\n\n                let schema = header\n                    .into_iter()\n                    .map(|(col_name, col_type)| ProductTypeElement::new(col_type, Some(col_name)))\n                    .collect();\n\n                Ok::<_, (StatusCode, String)>((\n                    result.tx_offset,\n                    db.durable_tx_offset(),\n                    vec![SqlStmtResult {\n                        schema,\n                        rows: result.rows,\n                        total_duration_micros: total_duration.as_micros() as u64,\n                        stats: SqlStmtStats::from_metrics(&result.metrics),\n                    }],\n                ))\n            })\n            .await\n            .map_err(log_and_500)??;\n\n        if confirmed_read && let Some(mut durable_offset) = durable_offset {\n            let tx_offset = tx_offset.await.map_err(|_| log_and_500(\"transaction aborted\"))?;\n            durable_offset.wait_for(tx_offset).await.map_err(log_and_500)?;\n        }\n\n        Ok(json)\n    }\n\n    pub async fn update(\n        &self,\n        database: Database,\n        host_type: HostType,\n        program_bytes: Box<[u8]>,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        self.host_controller\n            .update_module_host(database, host_type, self.replica_id, program_bytes, policy)\n            .await\n    }\n}\n/// Parameters for publishing a database.\n///\n/// See [`ControlStateDelegate::publish_database`].\npub struct DatabaseDef {\n    /// The [`Identity`] the database shall have.\n    pub database_identity: Identity,\n    /// The compiled program of the database module.\n    pub program_bytes: Bytes,\n    /// The desired number of replicas the database shall have.\n    ///\n    /// If `None`, the edition default is used.\n    pub num_replicas: Option<NonZeroU8>,\n    /// The host type of the supplied program.\n    pub host_type: HostType,\n    /// The optional identity of an existing database the database shall be a\n    /// child of.\n    pub parent: Option<Identity>,\n    /// The optional identity of an organization the database shall belong to.\n    pub organization: Option<Identity>,\n}\n\n/// Parameters for resetting a database via [`ControlStateDelegate::reset_database`].\npub struct DatabaseResetDef {\n    pub database_identity: Identity,\n    pub program_bytes: Option<Bytes>,\n    pub num_replicas: Option<NonZeroU8>,\n    pub host_type: Option<HostType>,\n}\n\n/// API of the SpacetimeDB control plane.\n///\n/// The trait is the composition of [`ControlStateReadAccess`] and\n/// [`ControlStateWriteAccess`] to reflect the consistency model of SpacetimeDB\n/// as of this writing:\n///\n/// The control plane state represents the _desired_ state of an ensemble of\n/// SpacetimeDB nodes. As such, this state can be read from a local (in-memory)\n/// representation, which is guaranteed to be \"prefix consistent\" across all\n/// nodes of a cluster. Prefix consistency means that the state being examined\n/// is consistent, but reads may not return the most recently written values.\n///\n/// As a consequence, implementations are not currently required to guarantee\n/// read-after-write consistency. In the future, however, write operations may\n/// be required to return the observed state after completing. As this may\n/// require them to suspend themselves while waiting for the writes to propagate,\n/// [`ControlStateWriteAccess`] methods are marked `async` today already.\n#[async_trait]\npub trait ControlStateDelegate: ControlStateReadAccess + ControlStateWriteAccess + Send + Sync {}\n\nimpl<T: ControlStateReadAccess + ControlStateWriteAccess + Send + Sync> ControlStateDelegate for T {}\n\n/// Query API of the SpacetimeDB control plane.\n#[async_trait]\npub trait ControlStateReadAccess {\n    // Nodes\n    async fn get_node_id(&self) -> Option<u64>;\n    async fn get_node_by_id(&self, node_id: u64) -> anyhow::Result<Option<Node>>;\n    async fn get_nodes(&self) -> anyhow::Result<Vec<Node>>;\n\n    // Databases\n    async fn get_database_by_id(&self, id: u64) -> anyhow::Result<Option<Database>>;\n    async fn get_database_by_identity(&self, database_identity: &Identity) -> anyhow::Result<Option<Database>>;\n    async fn get_databases(&self) -> anyhow::Result<Vec<Database>>;\n\n    // Replicas\n    async fn get_replica_by_id(&self, id: u64) -> anyhow::Result<Option<Replica>>;\n    async fn get_replicas(&self) -> anyhow::Result<Vec<Replica>>;\n    async fn get_leader_replica_by_database(&self, database_id: u64) -> Option<Replica>;\n\n    // Energy\n    async fn get_energy_balance(&self, identity: &Identity) -> anyhow::Result<Option<EnergyBalance>>;\n\n    // DNS\n    async fn lookup_database_identity(&self, domain: &str) -> anyhow::Result<Option<Identity>>;\n    async fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result<Vec<DomainName>>;\n    async fn lookup_namespace_owner(&self, name: &str) -> anyhow::Result<Option<Identity>>;\n}\n\n/// Write operations on the SpacetimeDB control plane.\n#[async_trait]\npub trait ControlStateWriteAccess: Send + Sync {\n    /// Publish a database acc. to [`DatabaseDef`].\n    ///\n    /// If the database with the given identity was successfully published before,\n    /// it is updated acc. to the module lifecycle conventions. `Some` result is\n    /// returned in that case.\n    ///\n    /// Otherwise, `None` is returned meaning that the database was freshly\n    /// initialized.\n    async fn publish_database(\n        &self,\n        publisher: &Identity,\n        spec: DatabaseDef,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<Option<UpdateDatabaseResult>>;\n\n    async fn migrate_plan(&self, spec: DatabaseDef, style: PrettyPrintStyle) -> anyhow::Result<MigratePlanResult>;\n\n    async fn delete_database(&self, caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()>;\n\n    /// Remove all data from a database, and reset it according to the\n    /// given [DatabaseResetDef].\n    async fn reset_database(&self, caller_identity: &Identity, spec: DatabaseResetDef) -> anyhow::Result<()>;\n\n    // Energy\n    async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()>;\n    async fn withdraw_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()>;\n\n    // DNS\n    async fn register_tld(&self, identity: &Identity, tld: Tld) -> anyhow::Result<RegisterTldResult>;\n    async fn create_dns_record(\n        &self,\n        owner_identity: &Identity,\n        domain: &DomainName,\n        database_identity: &Identity,\n    ) -> anyhow::Result<InsertDomainResult>;\n\n    /// Replace all dns records pointing to `database_identity` with `domain_names`.\n    ///\n    /// All existing names in the database and in `domain_names` must be\n    /// owned by `owner_identity` (i.e. their TLD must belong to `owner_identity`).\n    ///\n    /// The `owner_identity` is typically also the owner of the database.\n    ///\n    /// Note that passing an empty slice is legal, and will just remove any\n    /// existing dns records.\n    async fn replace_dns_records(\n        &self,\n        database_identity: &Identity,\n        owner_identity: &Identity,\n        domain_names: &[DomainName],\n    ) -> anyhow::Result<SetDomainsResult>;\n}\n\n#[async_trait]\nimpl<T: ControlStateReadAccess + Send + Sync + Sync + ?Sized> ControlStateReadAccess for Arc<T> {\n    // Nodes\n    async fn get_node_id(&self) -> Option<u64> {\n        (**self).get_node_id().await\n    }\n    async fn get_node_by_id(&self, node_id: u64) -> anyhow::Result<Option<Node>> {\n        (**self).get_node_by_id(node_id).await\n    }\n    async fn get_nodes(&self) -> anyhow::Result<Vec<Node>> {\n        (**self).get_nodes().await\n    }\n\n    // Databases\n    async fn get_database_by_id(&self, id: u64) -> anyhow::Result<Option<Database>> {\n        (**self).get_database_by_id(id).await\n    }\n    async fn get_database_by_identity(&self, identity: &Identity) -> anyhow::Result<Option<Database>> {\n        (**self).get_database_by_identity(identity).await\n    }\n    async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {\n        (**self).get_databases().await\n    }\n\n    // Replicas\n    async fn get_replica_by_id(&self, id: u64) -> anyhow::Result<Option<Replica>> {\n        (**self).get_replica_by_id(id).await\n    }\n    async fn get_replicas(&self) -> anyhow::Result<Vec<Replica>> {\n        (**self).get_replicas().await\n    }\n\n    async fn get_leader_replica_by_database(&self, database_id: u64) -> Option<Replica> {\n        (**self).get_leader_replica_by_database(database_id).await\n    }\n\n    // Energy\n    async fn get_energy_balance(&self, identity: &Identity) -> anyhow::Result<Option<EnergyBalance>> {\n        (**self).get_energy_balance(identity).await\n    }\n\n    // DNS\n    async fn lookup_database_identity(&self, domain: &str) -> anyhow::Result<Option<Identity>> {\n        (**self).lookup_database_identity(domain).await\n    }\n\n    async fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result<Vec<DomainName>> {\n        (**self).reverse_lookup(database_identity).await\n    }\n\n    async fn lookup_namespace_owner(&self, name: &str) -> anyhow::Result<Option<Identity>> {\n        (**self).lookup_namespace_owner(name).await\n    }\n}\n\n#[async_trait]\nimpl<T: ControlStateWriteAccess + ?Sized> ControlStateWriteAccess for Arc<T> {\n    async fn publish_database(\n        &self,\n        identity: &Identity,\n        spec: DatabaseDef,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<Option<UpdateDatabaseResult>> {\n        (**self).publish_database(identity, spec, policy).await\n    }\n\n    async fn migrate_plan(&self, spec: DatabaseDef, style: PrettyPrintStyle) -> anyhow::Result<MigratePlanResult> {\n        (**self).migrate_plan(spec, style).await\n    }\n\n    async fn delete_database(&self, caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()> {\n        (**self).delete_database(caller_identity, database_identity).await\n    }\n\n    async fn reset_database(&self, caller_identity: &Identity, spec: DatabaseResetDef) -> anyhow::Result<()> {\n        (**self).reset_database(caller_identity, spec).await\n    }\n\n    async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()> {\n        (**self).add_energy(identity, amount).await\n    }\n    async fn withdraw_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()> {\n        (**self).withdraw_energy(identity, amount).await\n    }\n\n    async fn register_tld(&self, identity: &Identity, tld: Tld) -> anyhow::Result<RegisterTldResult> {\n        (**self).register_tld(identity, tld).await\n    }\n\n    async fn create_dns_record(\n        &self,\n        identity: &Identity,\n        domain: &DomainName,\n        database_identity: &Identity,\n    ) -> anyhow::Result<InsertDomainResult> {\n        (**self).create_dns_record(identity, domain, database_identity).await\n    }\n\n    async fn replace_dns_records(\n        &self,\n        database_identity: &Identity,\n        owner_identity: &Identity,\n        domain_names: &[DomainName],\n    ) -> anyhow::Result<SetDomainsResult> {\n        (**self)\n            .replace_dns_records(database_identity, owner_identity, domain_names)\n            .await\n    }\n}\n\n#[async_trait]\nimpl<T: NodeDelegate + ?Sized> NodeDelegate for Arc<T> {\n    type JwtAuthProviderT = T::JwtAuthProviderT;\n    type GetLeaderHostError = T::GetLeaderHostError;\n\n    fn gather_metrics(&self) -> Vec<prometheus::proto::MetricFamily> {\n        (**self).gather_metrics()\n    }\n\n    fn client_actor_index(&self) -> &ClientActorIndex {\n        (**self).client_actor_index()\n    }\n\n    fn jwt_auth_provider(&self) -> &Self::JwtAuthProviderT {\n        (**self).jwt_auth_provider()\n    }\n\n    async fn leader(&self, database_id: u64) -> Result<Host, Self::GetLeaderHostError> {\n        (**self).leader(database_id).await\n    }\n\n    fn module_logs_dir(&self, replica_id: u64) -> ModuleLogsDir {\n        (**self).module_logs_dir(replica_id)\n    }\n}\n\n/// Result of an authorization check performed by an implementation of the\n/// [Authorization] trait.\n///\n/// [Unauthorized::Unauthorized] means that the subject was denied the\n/// permission to perform the requested action.\n///\n/// [Unauthorized::InternalError] indicates an error to perform the check in\n/// the first place. It may succeed when retried.\n///\n/// The [axum::response::IntoResponse] impl maps the variants to HTTP responses\n/// as follows:\n///\n/// * [Unauthorized::InternalError] is mapped to a 503 Internal Server Error\n///   response with the inner error sent as a string in the response body.\n///\n/// * [Unauthorized::Unauthorized] is mapped to a 403 Forbidden response with\n///   the [fmt::Display] form of the variant sent as the response body.\n///\n///   NOTE: [401 Unauthorized] means something different in HTTP, namely that\n///   the provided credentials are missing or invalid.\n///\n/// [401 Unauthorized]: https://datatracker.ietf.org/doc/html/rfc7235#section-3.1\n#[derive(Debug, Error)]\npub enum Unauthorized {\n    #[error(\n        \"{} is not authorized to perform action{}: {}\",\n        subject,\n        database.map(|ident| format!(\" on database {ident}\")).unwrap_or_default(),\n        action\n    )]\n    Unauthorized {\n        subject: Identity,\n        action: Action,\n        // `Option` for future, non-database-bound actions.\n        database: Option<Identity>,\n        #[source]\n        source: Option<anyhow::Error>,\n    },\n    #[error(\"authorization failed due to internal error\")]\n    InternalError(#[from] anyhow::Error),\n}\n\nimpl axum::response::IntoResponse for Unauthorized {\n    fn into_response(self) -> axum::response::Response {\n        let (status, e) = match self {\n            unauthorized @ Self::Unauthorized { .. } => (StatusCode::FORBIDDEN, anyhow!(unauthorized)),\n            Self::InternalError(e) => {\n                log::error!(\"internal error: {e:#}\");\n                (StatusCode::INTERNAL_SERVER_ERROR, e)\n            }\n        };\n\n        (status, format!(\"{e:#}\")).into_response()\n    }\n}\n\n/// Action to be authorized via [Authorization::authorize_action].\n#[derive(Clone, Copy, Debug)]\npub enum Action {\n    CreateDatabase {\n        parent: Option<Identity>,\n        organization: Option<Identity>,\n    },\n    UpdateDatabase,\n    ResetDatabase,\n    DeleteDatabase,\n    RenameDatabase,\n    ViewModuleLogs,\n}\n\nimpl fmt::Display for Action {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::CreateDatabase { parent, organization } => match (parent, organization) {\n                (Some(parent), Some(org)) => {\n                    write!(f, \"create database with parent {} and organization {}\", parent, org)\n                }\n                (Some(parent), None) => write!(f, \"create database with parent {}\", parent),\n                (None, Some(org)) => write!(f, \"create database with organization {}\", org),\n                (None, None) => f.write_str(\"create database\"),\n            },\n            Self::UpdateDatabase => f.write_str(\"update database\"),\n            Self::ResetDatabase => f.write_str(\"reset database\"),\n            Self::DeleteDatabase => f.write_str(\"delete database\"),\n            Self::RenameDatabase => f.write_str(\"rename database\"),\n            Self::ViewModuleLogs => f.write_str(\"view module logs\"),\n        }\n    }\n}\n\n/// Trait to delegate authorization of \"actions\" performed through the\n/// client API to an external, edition-specific implementation.\npub trait Authorization {\n    /// Authorize `subject` to perform [Action] `action` on `database`.\n    ///\n    /// Return `Ok(())` if permission is granted, `Err(Unauthorized)` if denied.\n    fn authorize_action(\n        &self,\n        subject: Identity,\n        database: Identity,\n        action: Action,\n    ) -> impl Future<Output = Result<(), Unauthorized>> + Send;\n\n    /// Obtain an attenuated [AuthCtx] for `subject` to evaluate SQL against\n    /// `database`.\n    ///\n    /// \"SQL\" includes the sql endpoint, pg wire connections, as well as\n    /// subscription queries.\n    ///\n    /// If any SQL should be rejected outright, or the authorization database\n    /// is not available, return `Err(Unauthorized)`.\n    fn authorize_sql(\n        &self,\n        subject: Identity,\n        database: Identity,\n    ) -> impl Future<Output = Result<AuthCtx, Unauthorized>> + Send;\n}\n\nimpl<T: Authorization> Authorization for Arc<T> {\n    fn authorize_action(\n        &self,\n        subject: Identity,\n        database: Identity,\n        action: Action,\n    ) -> impl Future<Output = Result<(), Unauthorized>> + Send {\n        (**self).authorize_action(subject, database, action)\n    }\n\n    fn authorize_sql(\n        &self,\n        subject: Identity,\n        database: Identity,\n    ) -> impl Future<Output = Result<AuthCtx, Unauthorized>> + Send {\n        (**self).authorize_sql(subject, database)\n    }\n}\n\npub fn log_and_500(e: impl std::fmt::Display) -> ErrorResponse {\n    log::error!(\"internal error: {e:#}\");\n    (StatusCode::INTERNAL_SERVER_ERROR, format!(\"{e:#}\")).into()\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/database.rs",
    "content": "use std::borrow::Cow;\nuse std::num::NonZeroU8;\nuse std::str::FromStr;\nuse std::time::Duration;\nuse std::{env, io};\n\nuse crate::auth::{\n    anon_auth_middleware, SpacetimeAuth, SpacetimeEnergyUsed, SpacetimeExecutionDurationMicros, SpacetimeIdentity,\n    SpacetimeIdentityToken,\n};\nuse crate::routes::subscribe::generate_random_connection_id;\npub use crate::util::{ByteStringBody, NameOrIdentity};\nuse crate::{\n    log_and_500, Action, Authorization, ControlStateDelegate, DatabaseDef, DatabaseResetDef, Host, MaybeMisdirected,\n    NodeDelegate, Unauthorized,\n};\nuse axum::body::{Body, Bytes};\nuse axum::extract::{Path, Query, State};\nuse axum::response::{ErrorResponse, IntoResponse};\nuse axum::routing::MethodRouter;\nuse axum::Extension;\nuse axum_extra::TypedHeader;\nuse futures::TryStreamExt;\nuse http::StatusCode;\nuse log::{info, warn};\nuse serde::Deserialize;\nuse spacetimedb::database_logger::DatabaseLogger;\nuse spacetimedb::host::module_host::ClientConnectedError;\nuse spacetimedb::host::{CallResult, UpdateDatabaseResult};\nuse spacetimedb::host::{FunctionArgs, MigratePlanResult};\nuse spacetimedb::host::{ModuleHost, ReducerOutcome};\nuse spacetimedb::host::{ProcedureCallError, ReducerCallError};\nuse spacetimedb::identity::Identity;\nuse spacetimedb::messages::control_db::{Database, HostType};\nuse spacetimedb_client_api_messages::http::SqlStmtResult;\nuse spacetimedb_client_api_messages::name::{\n    self, DatabaseName, DomainName, MigrationPolicy, PrePublishAutoMigrateResult, PrePublishManualMigrateResult,\n    PrePublishResult, PrettyPrintStyle, PublishOp, PublishResult,\n};\nuse spacetimedb_lib::db::raw_def::v10::RawModuleDefV10;\nuse spacetimedb_lib::db::raw_def::v9::RawModuleDefV9;\nuse spacetimedb_lib::{sats, AlgebraicValue, Hash, ProductValue, Timestamp};\nuse spacetimedb_schema::auto_migrate::{\n    MigrationPolicy as SchemaMigrationPolicy, MigrationToken, PrettyPrintStyle as AutoMigratePrettyPrintStyle,\n};\n\nuse super::subscribe::{handle_websocket, HasWebSocketOptions};\n\nfn require_spacetime_auth_for_creation() -> Option<String> {\n    // If the string is a non-empty value, return the string to be used as the required issuer\n    // TODO(cloutiertyler): This env var replaces TEMP_REQUIRE_SPACETIME_AUTH,\n    // we should remove that one in the future. We may eventually remove\n    // the below restriction entirely as well in Maincloud.\n    match env::var(\"TEMP_SPACETIMEAUTH_ISSUER_REQUIRED_TO_PUBLISH\") {\n        Ok(v) if !v.is_empty() => Some(v),\n        _ => None,\n    }\n}\n\n// A hacky function to let us restrict database creation on maincloud.\nfn allow_creation(auth: &SpacetimeAuth) -> Result<(), ErrorResponse> {\n    let Some(required_issuer) = require_spacetime_auth_for_creation() else {\n        return Ok(());\n    };\n    let issuer = auth.claims.issuer.trim_end_matches('/');\n    if issuer == required_issuer {\n        Ok(())\n    } else {\n        log::trace!(\n            \"Rejecting creation request because auth issuer is {} and required issuer is {}\",\n            auth.claims.issuer,\n            required_issuer\n        );\n        Err((\n            StatusCode::UNAUTHORIZED,\n            \"To create a database, you must be logged in with a SpacetimeDB account.\",\n        )\n            .into())\n    }\n}\n#[derive(Deserialize)]\npub struct CallParams {\n    name_or_identity: NameOrIdentity,\n    reducer: String,\n}\n\npub const NO_SUCH_DATABASE: (StatusCode, &str) = (StatusCode::NOT_FOUND, \"No such database.\");\nconst MISDIRECTED: (StatusCode, &str) = (StatusCode::NOT_FOUND, \"Database is not scheduled on this host\");\n\nfn map_reducer_error(e: ReducerCallError, reducer: &str) -> (StatusCode, String) {\n    let status_code = match e {\n        ReducerCallError::Args(_) => {\n            log::debug!(\"Attempt to call reducer {reducer} with invalid arguments\");\n            StatusCode::BAD_REQUEST\n        }\n        ReducerCallError::NoSuchModule(_) | ReducerCallError::ScheduleReducerNotFound => StatusCode::NOT_FOUND,\n        ReducerCallError::NoSuchReducer => {\n            log::debug!(\"Attempt to call non-existent reducer {reducer}\");\n            StatusCode::NOT_FOUND\n        }\n        ReducerCallError::LifecycleReducer(lifecycle) => {\n            log::debug!(\"Attempt to call {lifecycle:?} lifecycle reducer {reducer}\");\n            StatusCode::BAD_REQUEST\n        }\n    };\n\n    log::debug!(\"Error while invoking reducer {e:#}\");\n    (status_code, format!(\"{:#}\", anyhow::anyhow!(e)))\n}\n\nfn map_procedure_error(e: ProcedureCallError, procedure: &str) -> (StatusCode, String) {\n    let status_code = match e {\n        ProcedureCallError::Args(_) => {\n            log::debug!(\"Attempt to call procedure {procedure} with invalid arguments\");\n            StatusCode::BAD_REQUEST\n        }\n        ProcedureCallError::NoSuchModule(_) => StatusCode::NOT_FOUND,\n        ProcedureCallError::NoSuchProcedure => {\n            log::debug!(\"Attempt to call non-existent procedure OR reducer {procedure}\");\n            StatusCode::NOT_FOUND\n        }\n        ProcedureCallError::OutOfEnergy => StatusCode::PAYMENT_REQUIRED,\n        ProcedureCallError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,\n    };\n    log::error!(\"Error while invoking procedure {e:#}\");\n    (status_code, format!(\"{:#}\", anyhow::anyhow!(e)))\n}\n\n/// Call a reducer or procedure on the specified database module.\npub async fn call<S: ControlStateDelegate + NodeDelegate>(\n    State(worker_ctx): State<S>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    Path(CallParams {\n        name_or_identity,\n        reducer,\n    }): Path<CallParams>,\n    TypedHeader(content_type): TypedHeader<headers::ContentType>,\n    ByteStringBody(body): ByteStringBody,\n) -> axum::response::Result<impl IntoResponse> {\n    assert_content_type_json(content_type)?;\n\n    let caller_identity = auth.claims.identity;\n\n    let args = FunctionArgs::Json(body);\n\n    // HTTP callers always need a connection ID to provide to connect/disconnect,\n    // so generate one.\n    let connection_id = generate_random_connection_id();\n\n    let (module, Database { owner_identity, .. }) = find_module_and_database(&worker_ctx, name_or_identity).await?;\n\n    // Call the database's `client_connected` reducer, if any.\n    // If it fails or rejects the connection, bail.\n    module\n        .call_identity_connected(auth.into(), connection_id)\n        .await\n        .map_err(client_connected_error_to_response)?;\n\n    let result = match module\n        .call_reducer(\n            caller_identity,\n            Some(connection_id),\n            None,\n            None,\n            None,\n            &reducer,\n            args.clone(),\n        )\n        .await\n    {\n        Ok(rcr) => Ok(CallResult::Reducer(rcr)),\n        Err(ReducerCallError::NoSuchReducer | ReducerCallError::ScheduleReducerNotFound) => {\n            // Not a reducer — try procedure instead\n            match module\n                .call_procedure(caller_identity, Some(connection_id), None, &reducer, args)\n                .await\n                .result\n            {\n                Ok(res) => Ok(CallResult::Procedure(res)),\n                Err(e) => Err(map_procedure_error(e, &reducer)),\n            }\n        }\n        Err(e) => Err(map_reducer_error(e, &reducer)),\n    };\n\n    module\n        .call_identity_disconnected(caller_identity, connection_id)\n        .await\n        .map_err(client_disconnected_error_to_response)?;\n\n    match result {\n        Ok(CallResult::Reducer(result)) => {\n            let (status, body) = reducer_outcome_response(&owner_identity, &reducer, result.outcome);\n            Ok((\n                status,\n                TypedHeader(SpacetimeEnergyUsed(result.energy_used)),\n                TypedHeader(SpacetimeExecutionDurationMicros(result.execution_duration)),\n                body,\n            )\n                .into_response())\n        }\n        Ok(CallResult::Procedure(result)) => {\n            // Procedures don't assign a special meaning to error returns, unlike reducers,\n            // as there's no transaction for them to automatically abort.\n            // Instead, we just pass on their return value with the OK status so long as we successfully invoked the procedure.\n            let (status, body) = procedure_outcome_response(result.return_val);\n            Ok((\n                status,\n                TypedHeader(SpacetimeExecutionDurationMicros(result.execution_duration)),\n                body,\n            )\n                .into_response())\n        }\n        Err(e) => Err((e.0, e.1).into()),\n    }\n}\n\nfn assert_content_type_json(content_type: headers::ContentType) -> axum::response::Result<()> {\n    if content_type != headers::ContentType::json() {\n        Err(axum::extract::rejection::MissingJsonContentType::default().into())\n    } else {\n        Ok(())\n    }\n}\n\nfn reducer_outcome_response(\n    owner_identity: &Identity,\n    reducer: &str,\n    outcome: ReducerOutcome,\n) -> (StatusCode, Box<str>) {\n    match outcome {\n        ReducerOutcome::Committed => (StatusCode::OK, \"\".into()),\n        ReducerOutcome::Failed(errmsg) => {\n            // TODO: different status code? this is what cloudflare uses, sorta\n            (StatusCode::from_u16(530).unwrap(), *errmsg)\n        }\n        ReducerOutcome::BudgetExceeded => {\n            log::warn!(\"Node's energy budget exceeded for identity: {owner_identity} while executing {reducer}\");\n            (StatusCode::PAYMENT_REQUIRED, \"Module energy budget exhausted.\".into())\n        }\n    }\n}\n\nfn client_connected_error_to_response(err: ClientConnectedError) -> ErrorResponse {\n    match err {\n        // If `call_identity_connected` returns `Err(Rejected)`, then the `client_connected` reducer errored,\n        // meaning the connection was refused. Return 403 forbidden.\n        ClientConnectedError::Rejected(msg) => (StatusCode::FORBIDDEN, msg).into(),\n        // If `call_identity_connected` returns `Err(OutOfEnergy)`,\n        // then, well, the database is out of energy.\n        // Return 503 service unavailable.\n        ClientConnectedError::OutOfEnergy => (StatusCode::SERVICE_UNAVAILABLE, err.to_string()).into(),\n        // If `call_identity_connected` returns `Err(ReducerCall)`,\n        // something went wrong while invoking the `client_connected` reducer.\n        // I (pgoldman 2025-03-27) am not really sure how this would happen,\n        // but we returned 404 not found in this case prior to my editing this code,\n        // so I guess let's keep doing that.\n        ClientConnectedError::ReducerCall(e) => (StatusCode::NOT_FOUND, format!(\"{:#}\", anyhow::anyhow!(e))).into(),\n        // If `call_identity_connected` returns `Err(DBError)`,\n        // then the module didn't define `client_connected`,\n        // but something went wrong when we tried to insert into `st_client`.\n        // That's weird and scary, so return 500 internal error.\n        ClientConnectedError::DBError(_) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into(),\n    }\n}\n\n/// If `call_identity_disconnected` errors, something is very wrong:\n/// it means we tried to delete the `st_client` row but failed.\n///\n/// Note that `call_identity_disconnected` swallows errors from the `client_disconnected` reducer.\n/// Slap a 500 on it and pray.\nfn client_disconnected_error_to_response(err: ReducerCallError) -> ErrorResponse {\n    (StatusCode::INTERNAL_SERVER_ERROR, format!(\"{:#}\", anyhow::anyhow!(err))).into()\n}\n\nasync fn find_leader_and_database<S: ControlStateDelegate + NodeDelegate>(\n    worker_ctx: &S,\n    name_or_identity: NameOrIdentity,\n) -> axum::response::Result<(Host, Database)> {\n    let db_identity = name_or_identity.resolve(worker_ctx).await?;\n    let database = worker_ctx_find_database(worker_ctx, &db_identity)\n        .await?\n        .ok_or_else(|| {\n            log::error!(\"Could not find database: {}\", db_identity.to_hex());\n            NO_SUCH_DATABASE\n        })?;\n\n    let leader = worker_ctx.leader(database.id).await.map_err(log_and_500)?;\n\n    Ok((leader, database))\n}\n\nasync fn find_module_and_database<S: ControlStateDelegate + NodeDelegate>(\n    worker_ctx: &S,\n    name_or_identity: NameOrIdentity,\n) -> axum::response::Result<(ModuleHost, Database)> {\n    let (leader, database) = find_leader_and_database(worker_ctx, name_or_identity).await?;\n    let module = leader.module().await.map_err(log_and_500)?;\n\n    Ok((module, database))\n}\n\n#[derive(Debug, derive_more::From)]\npub enum DBCallErr {\n    HandlerError(ErrorResponse),\n    NoSuchDatabase,\n    InstanceNotScheduled,\n}\n\nfn procedure_outcome_response(return_val: AlgebraicValue) -> (StatusCode, axum::response::Response) {\n    (\n        StatusCode::OK,\n        axum::Json(sats::serde::SerdeWrapper(return_val)).into_response(),\n    )\n}\n\n#[derive(Deserialize)]\npub struct SchemaParams {\n    name_or_identity: NameOrIdentity,\n}\n#[derive(Deserialize)]\npub struct SchemaQueryParams {\n    version: SchemaVersion,\n}\n\n#[derive(Deserialize)]\nenum SchemaVersion {\n    #[serde(rename = \"9\")]\n    V9,\n    #[serde(rename = \"10\")]\n    V10,\n}\n\npub async fn schema<S>(\n    State(worker_ctx): State<S>,\n    Path(SchemaParams { name_or_identity }): Path<SchemaParams>,\n    Query(SchemaQueryParams { version }): Query<SchemaQueryParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n) -> axum::response::Result<impl IntoResponse>\nwhere\n    S: ControlStateDelegate + NodeDelegate,\n{\n    let (leader, _) = find_leader_and_database(&worker_ctx, name_or_identity).await?;\n    // Wait for the module to finish loading rather than returning an immediate\n    // 500 error. The database may still be initializing (replaying the log,\n    // running init reducers, etc.).\n    let module = leader\n        .wait_for_module(std::time::Duration::from_secs(10))\n        .await\n        .map_err(log_and_500)?;\n\n    let module_def = &module.info.module_def;\n    let response_json = match version {\n        SchemaVersion::V9 => {\n            let raw = RawModuleDefV9::from(module_def.as_ref().clone());\n            axum::Json(sats::serde::SerdeWrapper(raw)).into_response()\n        }\n        SchemaVersion::V10 => {\n            let raw = RawModuleDefV10::from(module_def.as_ref().clone());\n            axum::Json(sats::serde::SerdeWrapper(raw)).into_response()\n        }\n    };\n\n    Ok((\n        TypedHeader(SpacetimeIdentity(auth.claims.identity)),\n        TypedHeader(SpacetimeIdentityToken(auth.creds)),\n        response_json,\n    ))\n}\n\n#[derive(Deserialize)]\npub struct DatabaseParam {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(sats::Serialize)]\nstruct DatabaseResponse {\n    database_identity: Identity,\n    owner_identity: Identity,\n    host_type: HostType,\n    initial_program: spacetimedb_lib::Hash,\n}\n\nimpl From<Database> for DatabaseResponse {\n    fn from(db: Database) -> Self {\n        DatabaseResponse {\n            database_identity: db.database_identity,\n            owner_identity: db.owner_identity,\n            host_type: db.host_type,\n            initial_program: db.initial_program,\n        }\n    }\n}\n\npub async fn db_info<S: ControlStateDelegate>(\n    State(worker_ctx): State<S>,\n    Path(DatabaseParam { name_or_identity }): Path<DatabaseParam>,\n) -> axum::response::Result<impl IntoResponse> {\n    log::trace!(\"Trying to resolve database identity: {name_or_identity:?}\");\n    let database_identity = name_or_identity.resolve(&worker_ctx).await?;\n    log::trace!(\"Resolved identity to: {database_identity:?}\");\n    let database = worker_ctx_find_database(&worker_ctx, &database_identity)\n        .await?\n        .ok_or(NO_SUCH_DATABASE)?;\n    log::trace!(\"Fetched database from the worker db for database identity: {database_identity:?}\");\n\n    let response = DatabaseResponse::from(database);\n    Ok(axum::Json(sats::serde::SerdeWrapper(response)))\n}\n\n#[derive(Deserialize)]\npub struct LogsParams {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct LogsQuery {\n    num_lines: Option<u32>,\n    #[serde(default)]\n    follow: bool,\n}\n\npub async fn logs<S>(\n    State(worker_ctx): State<S>,\n    Path(LogsParams { name_or_identity }): Path<LogsParams>,\n    Query(LogsQuery { num_lines, follow }): Query<LogsQuery>,\n    Extension(auth): Extension<SpacetimeAuth>,\n) -> axum::response::Result<impl IntoResponse>\nwhere\n    S: ControlStateDelegate + NodeDelegate + Authorization,\n{\n    // You should not be able to read the logs from a database that you do not own\n    // so, unless you are the owner, this will fail.\n\n    let database_identity: Identity = name_or_identity.resolve(&worker_ctx).await?;\n    let database = worker_ctx_find_database(&worker_ctx, &database_identity)\n        .await?\n        .ok_or(NO_SUCH_DATABASE)?;\n\n    worker_ctx\n        .authorize_action(auth.claims.identity, database.database_identity, Action::ViewModuleLogs)\n        .await?;\n\n    fn log_err(database: Identity) -> impl Fn(&io::Error) {\n        move |e| warn!(\"error serving module logs for database {database}: {e:#}\")\n    }\n\n    let body = match worker_ctx.leader(database.id).await {\n        Ok(host) => {\n            let module = host.module().await.map_err(log_and_500)?;\n            let logs = module.database_logger().tail(num_lines, follow).await.map_err(|e| {\n                warn!(\"database={database_identity} unable to tail logs: {e:#}\");\n                (StatusCode::SERVICE_UNAVAILABLE, \"Logs are temporarily not available\")\n            })?;\n            Body::from_stream(logs.inspect_err(log_err(database_identity)))\n        }\n        Err(e) if e.is_misdirected() => return Err(MISDIRECTED.into()),\n        // If this is the right node for the current or last-known leader,\n        // we may still be able to serve logs from disk,\n        // even if we can't get hold of a running [ModuleHost].\n        Err(e) => {\n            warn!(\"could not obtain leader host for module logs: {e:#}\");\n            let Some(replica) = worker_ctx.get_leader_replica_by_database(database.id).await else {\n                return Err(MISDIRECTED.into());\n            };\n            let logs_dir = worker_ctx.module_logs_dir(replica.id);\n            if !logs_dir.0.try_exists().map_err(log_and_500)? {\n                // Probably an in-memory database.\n                // Logs may become available at a later time.\n                return Err((\n                    StatusCode::SERVICE_UNAVAILABLE,\n                    \"Database is not running and doesn't have persistent logs\",\n                )\n                    .into());\n            }\n            let logs = DatabaseLogger::read_latest_on_disk(logs_dir, num_lines);\n            Body::from_stream(logs.inspect_err(log_err(database_identity)))\n        }\n    };\n\n    Ok((\n        TypedHeader(headers::CacheControl::new().with_no_cache()),\n        TypedHeader(headers::ContentType::from(mime_ndjson())),\n        body,\n    ))\n}\n\nfn mime_ndjson() -> mime::Mime {\n    \"application/x-ndjson\".parse().unwrap()\n}\n\npub(crate) async fn worker_ctx_find_database(\n    worker_ctx: &(impl ControlStateDelegate + ?Sized),\n    database_identity: &Identity,\n) -> axum::response::Result<Option<Database>> {\n    worker_ctx\n        .get_database_by_identity(database_identity)\n        .await\n        .map_err(log_and_500)\n}\n\n#[derive(Deserialize)]\npub struct SqlParams {\n    pub name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct SqlQueryParams {\n    /// If `true`, return the query result only after its transaction offset\n    /// is confirmed to be durable.\n    #[serde(default)]\n    pub confirmed: Option<bool>,\n}\n\npub async fn sql_direct<S>(\n    worker_ctx: S,\n    SqlParams { name_or_identity }: SqlParams,\n    SqlQueryParams { confirmed }: SqlQueryParams,\n    caller_identity: Identity,\n    sql: String,\n) -> axum::response::Result<Vec<SqlStmtResult<ProductValue>>>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Authorization,\n{\n    // Anyone is authorized to execute SQL queries. The SQL engine will determine\n    // which queries this identity is allowed to execute against the database.\n\n    let (host, database) = find_leader_and_database(&worker_ctx, name_or_identity).await?;\n\n    let auth = worker_ctx\n        .authorize_sql(caller_identity, database.database_identity)\n        .await?;\n\n    host.exec_sql(auth, database, confirmed.unwrap_or(crate::DEFAULT_CONFIRMED_READS), sql)\n        .await\n}\n\npub async fn sql<S>(\n    State(worker_ctx): State<S>,\n    Path(name_or_identity): Path<SqlParams>,\n    Query(params): Query<SqlQueryParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    body: String,\n) -> axum::response::Result<impl IntoResponse>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Authorization,\n{\n    let json = sql_direct(worker_ctx, name_or_identity, params, auth.claims.identity, body).await?;\n\n    let total_duration = json.iter().fold(0, |acc, x| acc + x.total_duration_micros);\n\n    Ok((\n        TypedHeader(SpacetimeExecutionDurationMicros(Duration::from_micros(total_duration))),\n        axum::Json(json),\n    ))\n}\n\n#[derive(Deserialize)]\npub struct DNSParams {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct ReverseDNSParams {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct DNSQueryParams {}\n\npub async fn get_identity<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(DNSParams { name_or_identity }): Path<DNSParams>,\n    Query(DNSQueryParams {}): Query<DNSQueryParams>,\n) -> axum::response::Result<impl IntoResponse> {\n    let identity = name_or_identity.resolve(&ctx).await?;\n    Ok(identity.to_string())\n}\n\npub async fn get_names<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(ReverseDNSParams { name_or_identity }): Path<ReverseDNSParams>,\n) -> axum::response::Result<impl IntoResponse> {\n    let database_identity = name_or_identity.resolve(&ctx).await?;\n\n    let names = ctx\n        .reverse_lookup(&database_identity)\n        .await\n        .map_err(log_and_500)?\n        .into_iter()\n        .filter_map(|x| String::from(x).try_into().ok())\n        .collect();\n\n    let response = name::GetNamesResponse { names };\n    Ok(axum::Json(response))\n}\n\n#[derive(Deserialize)]\npub struct ResetDatabaseParams {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct ResetDatabaseQueryParams {\n    num_replicas: Option<usize>,\n    #[serde(default)]\n    host_type: HostType,\n}\n\npub async fn reset<S: NodeDelegate + ControlStateDelegate + Authorization>(\n    State(ctx): State<S>,\n    Path(ResetDatabaseParams { name_or_identity }): Path<ResetDatabaseParams>,\n    Query(ResetDatabaseQueryParams {\n        num_replicas,\n        host_type,\n    }): Query<ResetDatabaseQueryParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    program_bytes: Option<Bytes>,\n) -> axum::response::Result<axum::Json<PublishResult>> {\n    let database_identity = name_or_identity.resolve(&ctx).await?;\n    let database = worker_ctx_find_database(&ctx, &database_identity)\n        .await?\n        .ok_or(NO_SUCH_DATABASE)?;\n\n    ctx.authorize_action(auth.claims.identity, database.database_identity, Action::ResetDatabase)\n        .await?;\n\n    let num_replicas = num_replicas.map(validate_replication_factor).transpose()?.flatten();\n    ctx.reset_database(\n        &auth.claims.identity,\n        DatabaseResetDef {\n            database_identity,\n            program_bytes,\n            num_replicas,\n            host_type: Some(host_type),\n        },\n    )\n    .await\n    .map_err(log_and_500)?;\n\n    Ok(axum::Json(PublishResult::Success {\n        domain: name_or_identity.name().cloned(),\n        database_identity,\n        op: PublishOp::Updated,\n    }))\n}\n\n#[derive(Deserialize)]\npub struct PublishDatabaseParams {\n    name_or_identity: Option<NameOrIdentity>,\n}\n\n#[derive(Deserialize)]\npub struct PublishDatabaseQueryParams {\n    #[serde(default)]\n    clear: bool,\n    num_replicas: Option<usize>,\n    /// [`Hash`] of [`MigrationToken`]` to be checked if `MigrationPolicy::BreakClients` is set.\n    ///\n    /// Users obtain such a hash via the `/database/:name_or_identity/pre-publish POST` route.\n    /// This is a safeguard to require explicit approval for updates which will break clients.\n    token: Option<Hash>,\n    #[serde(default)]\n    policy: MigrationPolicy,\n    #[serde(default)]\n    host_type: HostType,\n    parent: Option<NameOrIdentity>,\n    #[serde(alias = \"org\")]\n    organization: Option<NameOrIdentity>,\n}\n\npub async fn publish<S: NodeDelegate + ControlStateDelegate + Authorization>(\n    State(ctx): State<S>,\n    Path(PublishDatabaseParams { name_or_identity }): Path<PublishDatabaseParams>,\n    Query(PublishDatabaseQueryParams {\n        clear,\n        num_replicas,\n        token,\n        policy,\n        host_type,\n        parent,\n        organization,\n    }): Query<PublishDatabaseQueryParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    program_bytes: Bytes,\n) -> axum::response::Result<axum::Json<PublishResult>> {\n    // If `clear`, check that the database exists and delegate to `reset`.\n    // If it doesn't exist, ignore the `clear` parameter.\n    // TODO: Replace with actual redirect at the next possible version bump.\n    if clear {\n        let name_or_identity = name_or_identity\n            .as_ref()\n            .ok_or_else(|| bad_request(\"Clear database requires database name or identity\".into()))?;\n        let database_identity = name_or_identity.try_resolve(&ctx).await.map_err(log_and_500)?;\n        if let Ok(identity) = database_identity {\n            let exists = ctx\n                .get_database_by_identity(&identity)\n                .await\n                .map_err(log_and_500)?\n                .is_some();\n            if exists {\n                if parent.is_some() {\n                    return Err(bad_request(\n                        \"Setting the parent of an existing database is not supported\".into(),\n                    ));\n                }\n\n                return self::reset(\n                    State(ctx),\n                    Path(ResetDatabaseParams {\n                        name_or_identity: name_or_identity.clone(),\n                    }),\n                    Query(ResetDatabaseQueryParams {\n                        num_replicas,\n                        host_type,\n                    }),\n                    Extension(auth),\n                    Some(program_bytes),\n                )\n                .await;\n            }\n        }\n    }\n\n    let (database_identity, db_name) = get_or_create_identity_and_name(&ctx, &auth, name_or_identity.as_ref()).await?;\n    let maybe_parent_database_identity = match parent.as_ref() {\n        None => None,\n        Some(parent) => parent.resolve(&ctx).await.map(Some)?,\n    };\n    let maybe_org_identity = match organization.as_ref() {\n        None => None,\n        Some(org) => org.resolve_namespace_owner(&ctx).await.map(Some)?,\n    };\n\n    // Check that the replication factor looks somewhat sane.\n    let num_replicas = num_replicas.map(validate_replication_factor).transpose()?.flatten();\n\n    log::trace!(\"Publishing to the identity: {}\", database_identity.to_hex());\n\n    // Check if the database already exists.\n    let existing = ctx\n        .get_database_by_identity(&database_identity)\n        .await\n        .map_err(log_and_500)?;\n    match existing.as_ref() {\n        None => {\n            allow_creation(&auth)?;\n            ctx.authorize_action(\n                auth.claims.identity,\n                database_identity,\n                Action::CreateDatabase {\n                    parent: maybe_parent_database_identity,\n                    organization: maybe_org_identity,\n                },\n            )\n            .await?;\n        }\n        Some(database) => {\n            ctx.authorize_action(auth.claims.identity, database.database_identity, Action::UpdateDatabase)\n                .await?;\n        }\n    }\n\n    // Indicate in the response whether we created or updated the database.\n    let publish_op = if existing.is_some() {\n        PublishOp::Updated\n    } else {\n        PublishOp::Created\n    };\n    // If a parent is given, resolve to an existing database.\n    let parent = if let Some(name_or_identity) = parent {\n        let identity = name_or_identity\n            .resolve(&ctx)\n            .await\n            .map_err(|_| bad_request(format!(\"Parent database {name_or_identity} not found\").into()))?;\n        Some(identity)\n    } else {\n        None\n    };\n\n    let schema_migration_policy = schema_migration_policy(policy, token)?;\n    let maybe_updated = ctx\n        .publish_database(\n            &auth.claims.identity,\n            DatabaseDef {\n                database_identity,\n                program_bytes,\n                num_replicas,\n                host_type,\n                parent,\n                organization: maybe_org_identity,\n            },\n            schema_migration_policy,\n        )\n        .await\n        .map_err(log_and_500)?;\n\n    match maybe_updated {\n        Some(UpdateDatabaseResult::AutoMigrateError(errs)) => {\n            Err(bad_request(format!(\"Database update rejected: {errs}\").into()))\n        }\n        Some(UpdateDatabaseResult::ErrorExecutingMigration(err)) => Err(bad_request(\n            format!(\"Failed to create or update the database: {err}\").into(),\n        )),\n        None\n        | Some(\n            UpdateDatabaseResult::NoUpdateNeeded\n            | UpdateDatabaseResult::UpdatePerformed\n            | UpdateDatabaseResult::UpdatePerformedWithClientDisconnect,\n        ) => Ok(axum::Json(PublishResult::Success {\n            domain: db_name.cloned(),\n            database_identity,\n            op: publish_op,\n        })),\n    }\n}\n\n/// Try to resolve `name_or_identity` to an [Identity] and [DatabaseName].\n///\n/// - If the database exists and has a name registered for it, return that.\n/// - If the database does not exist, but `name_or_identity` is a name,\n///   try to register the name and return alongside a newly allocated [Identity]\n/// - Otherwise, if the database does not exist and `name_or_identity` is `None`,\n///   allocate a fresh [Identity] and no name.\n///\nasync fn get_or_create_identity_and_name<'a>(\n    ctx: &(impl ControlStateDelegate + NodeDelegate),\n    auth: &SpacetimeAuth,\n    name_or_identity: Option<&'a NameOrIdentity>,\n) -> axum::response::Result<(Identity, Option<&'a DatabaseName>)> {\n    match name_or_identity {\n        Some(noi) => match noi.try_resolve(ctx).await.map_err(log_and_500)? {\n            Ok(resolved) => Ok((resolved, noi.name())),\n            Err(name) => {\n                // `name_or_identity` was a `NameOrIdentity::Name`, but no record\n                // exists yet. Create it now with a fresh identity.\n                allow_creation(auth)?;\n                let database_auth = SpacetimeAuth::alloc(ctx).await?;\n                let database_identity = database_auth.claims.identity;\n                create_name(ctx, auth, &database_identity, name).await?;\n                Ok((database_identity, Some(name)))\n            }\n        },\n        None => {\n            let database_auth = SpacetimeAuth::alloc(ctx).await?;\n            let database_identity = database_auth.claims.identity;\n            Ok((database_identity, None))\n        }\n    }\n}\n\n/// Try to register `name` for database `database_identity`.\nasync fn create_name(\n    ctx: &(impl NodeDelegate + ControlStateDelegate),\n    auth: &SpacetimeAuth,\n    database_identity: &Identity,\n    name: &DatabaseName,\n) -> axum::response::Result<()> {\n    let tld: name::Tld = name.clone().into();\n    let tld = match ctx\n        .register_tld(&auth.claims.identity, tld)\n        .await\n        .map_err(log_and_500)?\n    {\n        name::RegisterTldResult::Success { domain } | name::RegisterTldResult::AlreadyRegistered { domain } => domain,\n        name::RegisterTldResult::Unauthorized { .. } => {\n            return Err((\n                StatusCode::UNAUTHORIZED,\n                axum::Json(PublishResult::PermissionDenied { name: name.clone() }),\n            )\n                .into())\n        }\n    };\n    let res = ctx\n        .create_dns_record(&auth.claims.identity, &tld.into(), database_identity)\n        .await\n        .map_err(log_and_500)?;\n    match res {\n        name::InsertDomainResult::Success { .. } => Ok(()),\n        name::InsertDomainResult::TldNotRegistered { .. } | name::InsertDomainResult::PermissionDenied { .. } => {\n            Err(log_and_500(\"impossible: we just registered the tld\"))\n        }\n        name::InsertDomainResult::OtherError(e) => Err(log_and_500(e)),\n    }\n}\n\nfn schema_migration_policy(\n    policy: MigrationPolicy,\n    token: Option<Hash>,\n) -> axum::response::Result<SchemaMigrationPolicy> {\n    const MISSING_TOKEN: &str = \"Migration policy is set to `BreakClients`, but no migration token was provided.\";\n\n    match policy {\n        MigrationPolicy::BreakClients => token\n            .map(SchemaMigrationPolicy::BreakClients)\n            .ok_or_else(|| bad_request(MISSING_TOKEN.into())),\n        MigrationPolicy::Compatible => Ok(SchemaMigrationPolicy::Compatible),\n    }\n}\n\nfn validate_replication_factor(n: usize) -> Result<Option<NonZeroU8>, ErrorResponse> {\n    let n = u8::try_from(n).map_err(|_| bad_request(format!(\"Replication factor {n} out of bounds\").into()))?;\n    Ok(NonZeroU8::new(n))\n}\n\nfn bad_request(message: Cow<'static, str>) -> ErrorResponse {\n    (StatusCode::BAD_REQUEST, message).into()\n}\n\n#[derive(serde::Deserialize)]\npub struct PrePublishParams {\n    name_or_identity: NameOrIdentity,\n}\n\n#[derive(serde::Deserialize)]\npub struct PrePublishQueryParams {\n    #[serde(default)]\n    style: PrettyPrintStyle,\n    #[serde(default)]\n    host_type: HostType,\n}\n\npub async fn pre_publish<S: NodeDelegate + ControlStateDelegate + Authorization>(\n    State(ctx): State<S>,\n    Path(PrePublishParams { name_or_identity }): Path<PrePublishParams>,\n    Query(PrePublishQueryParams { style, host_type }): Query<PrePublishQueryParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    program_bytes: Bytes,\n) -> axum::response::Result<axum::Json<PrePublishResult>> {\n    // User should not be able to print migration plans for a database that they do not own\n    let database_identity = resolve_and_authenticate(&ctx, &name_or_identity, &auth).await?;\n    let style = match style {\n        PrettyPrintStyle::NoColor => AutoMigratePrettyPrintStyle::NoColor,\n        PrettyPrintStyle::AnsiColor => AutoMigratePrettyPrintStyle::AnsiColor,\n    };\n\n    info!(\"planning migration for database {database_identity}\");\n    let migrate_plan = ctx\n        .migrate_plan(\n            DatabaseDef {\n                database_identity,\n                program_bytes,\n                num_replicas: None,\n                host_type,\n                parent: None,\n                organization: None,\n            },\n            style,\n        )\n        .await\n        .map_err(log_and_500)?;\n\n    match migrate_plan {\n        MigratePlanResult::Success {\n            old_module_hash,\n            new_module_hash,\n            breaks_client,\n            plan,\n            major_version_upgrade,\n        } => {\n            info!(\n                \"planned auto-migration of database {} from {} to {}\",\n                database_identity, old_module_hash, new_module_hash\n            );\n            let token = MigrationToken {\n                database_identity,\n                old_module_hash,\n                new_module_hash,\n            }\n            .hash();\n\n            Ok(PrePublishResult::AutoMigrate(PrePublishAutoMigrateResult {\n                token,\n                migrate_plan: plan,\n                break_clients: breaks_client,\n                major_version_upgrade,\n            }))\n        }\n        MigratePlanResult::AutoMigrationError {\n            error: e,\n            major_version_upgrade,\n        } => {\n            info!(\"database {database_identity} needs manual migration\");\n            Ok(PrePublishResult::ManualMigrate(PrePublishManualMigrateResult {\n                reason: e.to_string(),\n                major_version_upgrade,\n            }))\n        }\n    }\n    .map(axum::Json)\n}\n\n/// Resolves the [`NameOrIdentity`] to a database identity and checks if the\n/// `auth` identity owns the database.\nasync fn resolve_and_authenticate<S: ControlStateDelegate + Authorization>(\n    ctx: &S,\n    name_or_identity: &NameOrIdentity,\n    auth: &SpacetimeAuth,\n) -> axum::response::Result<Identity> {\n    let database_identity = name_or_identity.resolve(ctx).await?;\n    let database = worker_ctx_find_database(ctx, &database_identity)\n        .await?\n        .ok_or(NO_SUCH_DATABASE)?;\n\n    ctx.authorize_action(auth.claims.identity, database.database_identity, Action::UpdateDatabase)\n        .await?;\n\n    Ok(database_identity)\n}\n\n#[derive(Deserialize)]\npub struct DeleteDatabaseParams {\n    pub name_or_identity: NameOrIdentity,\n}\n\npub async fn delete_database<S: ControlStateDelegate + Authorization>(\n    State(ctx): State<S>,\n    Path(DeleteDatabaseParams { name_or_identity }): Path<DeleteDatabaseParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n) -> axum::response::Result<impl IntoResponse> {\n    let database_identity = name_or_identity.resolve(&ctx).await?;\n    let Some(_database) = worker_ctx_find_database(&ctx, &database_identity).await? else {\n        return Ok(());\n    };\n\n    ctx.authorize_action(auth.claims.identity, database_identity, Action::DeleteDatabase)\n        .await?;\n    ctx.delete_database(&auth.claims.identity, &database_identity)\n        .await\n        .map_err(log_and_500)?;\n\n    Ok(())\n}\n\n#[derive(Deserialize)]\npub struct AddNameParams {\n    name_or_identity: NameOrIdentity,\n}\n\npub async fn add_name<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(AddNameParams { name_or_identity }): Path<AddNameParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    name: String,\n) -> axum::response::Result<impl IntoResponse> {\n    let name = DatabaseName::try_from(name).map_err(|err| (StatusCode::BAD_REQUEST, err.to_string()))?;\n    let database_identity = name_or_identity.resolve(&ctx).await?;\n\n    let response = ctx\n        .create_dns_record(&auth.claims.identity, &name.into(), &database_identity)\n        .await\n        // TODO: better error code handling\n        .map_err(log_and_500)?;\n\n    let code = match response {\n        name::InsertDomainResult::Success { .. } => StatusCode::OK,\n        name::InsertDomainResult::TldNotRegistered { .. } => StatusCode::BAD_REQUEST,\n        name::InsertDomainResult::PermissionDenied { .. } => StatusCode::UNAUTHORIZED,\n        name::InsertDomainResult::OtherError(_) => StatusCode::INTERNAL_SERVER_ERROR,\n    };\n\n    Ok((code, axum::Json(response)))\n}\n\n#[derive(Deserialize)]\npub struct SetNamesParams {\n    name_or_identity: NameOrIdentity,\n}\n\npub async fn set_names<S: ControlStateDelegate + Authorization>(\n    State(ctx): State<S>,\n    Path(SetNamesParams { name_or_identity }): Path<SetNamesParams>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    names: axum::Json<Vec<String>>,\n) -> axum::response::Result<impl IntoResponse> {\n    let validated_names = names\n        .0\n        .into_iter()\n        .map(|s| DatabaseName::from_str(&s).map(DomainName::from).map_err(|e| (s, e)))\n        .collect::<Result<Vec<_>, _>>()\n        .map_err(|(input, e)| (StatusCode::BAD_REQUEST, format!(\"Error parsing `{input}`: {e}\")))?;\n\n    let database_identity = name_or_identity.resolve(&ctx).await?;\n\n    let database = ctx\n        .get_database_by_identity(&database_identity)\n        .await\n        .map_err(log_and_500)?;\n    let Some(database) = database else {\n        return Ok((\n            StatusCode::NOT_FOUND,\n            axum::Json(name::SetDomainsResult::DatabaseNotFound),\n        ));\n    };\n\n    ctx.authorize_action(auth.claims.identity, database.database_identity, Action::RenameDatabase)\n        .await\n        .map_err(|e| match e {\n            Unauthorized::Unauthorized { .. } => (\n                StatusCode::UNAUTHORIZED,\n                axum::Json(name::SetDomainsResult::NotYourDatabase {\n                    database: database.database_identity,\n                }),\n            )\n                .into(),\n            Unauthorized::InternalError(e) => log_and_500(e),\n        })?;\n\n    for name in &validated_names {\n        if ctx\n            .lookup_database_identity(name.as_str())\n            .await\n            .map_err(log_and_500)?\n            .is_some()\n        {\n            return Ok((\n                StatusCode::BAD_REQUEST,\n                axum::Json(name::SetDomainsResult::OtherError(format!(\n                    \"Cannot rename to {} because it already is in use.\",\n                    name.as_str()\n                ))),\n            ));\n        }\n    }\n\n    let response = ctx\n        .replace_dns_records(&database_identity, &database.owner_identity, &validated_names)\n        .await\n        .map_err(log_and_500)?;\n    let status = match response {\n        name::SetDomainsResult::Success => StatusCode::OK,\n        name::SetDomainsResult::PermissionDenied { .. }\n        | name::SetDomainsResult::PermissionDeniedOnAny { .. }\n        | name::SetDomainsResult::NotYourDatabase { .. } => StatusCode::UNAUTHORIZED,\n        name::SetDomainsResult::DatabaseNotFound => StatusCode::NOT_FOUND,\n        name::SetDomainsResult::OtherError(_) => StatusCode::INTERNAL_SERVER_ERROR,\n    };\n\n    Ok((status, axum::Json(response)))\n}\n\n#[derive(serde::Deserialize)]\npub struct TimestampParams {\n    name_or_identity: NameOrIdentity,\n}\n\n/// Returns the database's view of the current time,\n/// as a SATS-JSON encoded [`Timestamp`].\n///\n/// Takes a particular database's [`NameOrIdentity`] as an argument\n/// because in a clusterized SpacetimeDB-cloud deployment,\n/// this request will be routed to the node running the requested database.\nasync fn get_timestamp<S: ControlStateDelegate>(\n    State(worker_ctx): State<S>,\n    Path(TimestampParams { name_or_identity }): Path<TimestampParams>,\n) -> axum::response::Result<impl IntoResponse> {\n    let db_identity = name_or_identity.resolve(&worker_ctx).await?;\n\n    let _database = worker_ctx_find_database(&worker_ctx, &db_identity)\n        .await?\n        .ok_or_else(|| {\n            log::error!(\"Could not find database: {}\", db_identity.to_hex());\n            NO_SUCH_DATABASE\n        })?;\n\n    Ok(axum::Json(sats::serde::SerdeWrapper(Timestamp::now())).into_response())\n}\n\n/// This struct allows the edition to customize `/database` routes more meticulously.\npub struct DatabaseRoutes<S> {\n    /// POST /database\n    pub root_post: MethodRouter<S>,\n    /// PUT: /database/:name_or_identity\n    pub db_put: MethodRouter<S>,\n    /// GET: /database/:name_or_identity\n    pub db_get: MethodRouter<S>,\n    /// DELETE: /database/:name_or_identity\n    pub db_delete: MethodRouter<S>,\n    /// GET: /database/:name_or_identity/names\n    pub names_get: MethodRouter<S>,\n    /// POST: /database/:name_or_identity/names\n    pub names_post: MethodRouter<S>,\n    /// PUT: /database/:name_or_identity/names\n    pub names_put: MethodRouter<S>,\n    /// GET: /database/:name_or_identity/identity\n    pub identity_get: MethodRouter<S>,\n    /// GET: /database/:name_or_identity/subscribe\n    pub subscribe_get: MethodRouter<S>,\n    /// POST: /database/:name_or_identity/call/:reducer\n    pub call_reducer_procedure_post: MethodRouter<S>,\n    /// GET: /database/:name_or_identity/schema\n    pub schema_get: MethodRouter<S>,\n    /// GET: /database/:name_or_identity/logs\n    pub logs_get: MethodRouter<S>,\n    /// POST: /database/:name_or_identity/sql\n    pub sql_post: MethodRouter<S>,\n    /// POST: /database/:name_or_identity/pre-publish\n    pub pre_publish: MethodRouter<S>,\n    /// PUT: /database/:name_or_identity/reset\n    pub db_reset: MethodRouter<S>,\n    /// GET: /database/: name_or_identity/unstable/timestamp\n    pub timestamp_get: MethodRouter<S>,\n}\n\nimpl<S> Default for DatabaseRoutes<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + HasWebSocketOptions + Authorization + Clone + 'static,\n{\n    fn default() -> Self {\n        use axum::routing::{delete, get, post, put};\n        Self {\n            root_post: post(publish::<S>),\n            db_put: put(publish::<S>),\n            db_get: get(db_info::<S>),\n            db_delete: delete(delete_database::<S>),\n            names_get: get(get_names::<S>),\n            names_post: post(add_name::<S>),\n            names_put: put(set_names::<S>),\n            identity_get: get(get_identity::<S>),\n            subscribe_get: get(handle_websocket::<S>),\n            call_reducer_procedure_post: post(call::<S>),\n            schema_get: get(schema::<S>),\n            logs_get: get(logs::<S>),\n            sql_post: post(sql::<S>),\n            pre_publish: post(pre_publish::<S>),\n            db_reset: put(reset::<S>),\n            timestamp_get: get(get_timestamp::<S>),\n        }\n    }\n}\n\nimpl<S> DatabaseRoutes<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Authorization + Clone + 'static,\n{\n    pub fn into_router(self, ctx: S) -> axum::Router<S> {\n        let db_router = axum::Router::<S>::new()\n            .route(\"/\", self.db_put)\n            .route(\"/\", self.db_get)\n            .route(\"/\", self.db_delete)\n            .route(\"/names\", self.names_get)\n            .route(\"/names\", self.names_post)\n            .route(\"/names\", self.names_put)\n            .route(\"/identity\", self.identity_get)\n            .route(\"/subscribe\", self.subscribe_get)\n            .route(\"/call/:reducer\", self.call_reducer_procedure_post)\n            .route(\"/schema\", self.schema_get)\n            .route(\"/logs\", self.logs_get)\n            .route(\"/sql\", self.sql_post)\n            .route(\"/unstable/timestamp\", self.timestamp_get)\n            .route(\"/pre_publish\", self.pre_publish)\n            .route(\"/reset\", self.db_reset);\n\n        axum::Router::new()\n            .route(\"/\", self.root_post)\n            .nest(\"/:name_or_identity\", db_router)\n            .route_layer(axum::middleware::from_fn_with_state(ctx, anon_auth_middleware::<S>))\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/energy.rs",
    "content": "use axum::extract::{Path, Query, State};\nuse axum::response::IntoResponse;\nuse http::StatusCode;\nuse serde::{Deserialize, Serialize};\n\nuse spacetimedb::energy::EnergyQuanta;\nuse spacetimedb_lib::Identity;\n\nuse crate::auth::SpacetimeAuthRequired;\nuse crate::{log_and_500, ControlStateDelegate, NodeDelegate};\n\nuse super::identity::IdentityForUrl;\n\n#[derive(Deserialize)]\npub struct IdentityParams {\n    identity: IdentityForUrl,\n}\n\n// TODO: do we want to require auth on this?\npub async fn get_energy_balance<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(IdentityParams { identity }): Path<IdentityParams>,\n) -> axum::response::Result<impl IntoResponse> {\n    let identity = Identity::from(identity);\n    get_budget_inner(ctx, identity).await\n}\n\n#[serde_with::serde_as]\n#[derive(Serialize)]\nstruct BalanceResponse {\n    // Note: balance must be returned as a string to avoid truncation.\n    #[serde_as(as = \"serde_with::DisplayFromStr\")]\n    balance: i128,\n}\n\n#[derive(Deserialize)]\npub struct AddEnergyQueryParams {\n    amount: Option<String>,\n}\npub async fn add_energy<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Query(AddEnergyQueryParams { amount }): Query<AddEnergyQueryParams>,\n    SpacetimeAuthRequired(auth): SpacetimeAuthRequired,\n) -> axum::response::Result<impl IntoResponse> {\n    // Nb.: Negative amount withdraws\n    let amount = amount.map(|s| s.parse::<u128>()).transpose().map_err(|e| {\n        log::error!(\"Failed to parse amount: {e:?}\");\n        StatusCode::BAD_REQUEST\n    })?;\n\n    if let Some(satoshi) = amount {\n        ctx.add_energy(&auth.claims.identity, EnergyQuanta::new(satoshi))\n            .await\n            .map_err(log_and_500)?;\n    }\n\n    // TODO: is this guaranteed to pull the updated balance?\n    let balance = ctx\n        .get_energy_balance(&auth.claims.identity)\n        .await\n        .map_err(log_and_500)?\n        .map_or(0, |quanta| quanta.get());\n\n    Ok(axum::Json(BalanceResponse { balance }))\n}\n\nasync fn get_budget_inner(\n    ctx: impl ControlStateDelegate,\n    identity: Identity,\n) -> axum::response::Result<impl IntoResponse> {\n    let balance = ctx\n        .get_energy_balance(&identity)\n        .await\n        .map_err(log_and_500)?\n        .map_or(0, |quanta| quanta.get());\n\n    Ok(axum::Json(BalanceResponse { balance }))\n}\n\n#[derive(Deserialize)]\npub struct SetEnergyBalanceQueryParams {\n    balance: Option<String>,\n}\npub async fn set_energy_balance<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(IdentityParams { identity }): Path<IdentityParams>,\n    Query(SetEnergyBalanceQueryParams { balance }): Query<SetEnergyBalanceQueryParams>,\n    SpacetimeAuthRequired(auth): SpacetimeAuthRequired,\n) -> axum::response::Result<impl IntoResponse> {\n    // TODO(cloutiertyler): For the Testnet no one shall be authorized to set the energy balance\n    // of an identity. Each identity will begin with a default balance and they cannot be refilled.\n    // This will be a natural rate limiter until we can begin to sell energy.\n\n    // No one is able to be the dummy identity so this always returns unauthorized.\n    if auth.claims.identity != Identity::__dummy() {\n        return Err(StatusCode::UNAUTHORIZED.into());\n    }\n\n    let identity = Identity::from(identity);\n\n    let desired_balance = balance\n        .map(|balance| balance.parse::<i128>())\n        .transpose()\n        .map_err(|err| {\n            log::error!(\"Failed to parse balance: {err:?}\");\n            StatusCode::BAD_REQUEST\n        })?\n        .unwrap_or(0);\n    let current_balance = ctx\n        .get_energy_balance(&identity)\n        .await\n        .map_err(log_and_500)?\n        .map_or(0, |quanta| quanta.get());\n\n    // TODO: this is a race condition waiting to happen. have a set_balance method on ControlStateDelegate\n    let delta = EnergyQuanta::new(desired_balance.abs_diff(current_balance));\n    if desired_balance > current_balance {\n        ctx.add_energy(&identity, delta).await.map_err(log_and_500)?;\n    } else {\n        ctx.withdraw_energy(&identity, delta).await.map_err(log_and_500)?;\n    }\n\n    Ok(axum::Json(BalanceResponse {\n        balance: desired_balance,\n    }))\n}\n\npub fn router<S>() -> axum::Router<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Clone + 'static,\n{\n    use axum::routing::get;\n    axum::Router::new().route(\n        \"/:identity\",\n        get(get_energy_balance::<S>)\n            .put(set_energy_balance::<S>)\n            .post(add_energy::<S>),\n    )\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/health.rs",
    "content": "use crate::{ControlStateDelegate, NodeDelegate};\nuse axum::extract::State;\nuse axum::response::IntoResponse;\nuse http::StatusCode;\n\nstatic VERSION: &str = env!(\"CARGO_PKG_VERSION\");\nstatic PACKAGE_NAME: &str = env!(\"CARGO_PKG_NAME\");\n\npub async fn health<S: ControlStateDelegate + NodeDelegate>(\n    State(ctx): State<S>,\n) -> axum::response::Result<impl IntoResponse> {\n    let nodes: Vec<u64> = ctx\n        .get_nodes()\n        .await\n        .map_err(|_| {\n            (\n                StatusCode::INTERNAL_SERVER_ERROR,\n                \"Couldn't connect to the control database\",\n            )\n        })?\n        .iter()\n        .map(|n| n.id)\n        .collect();\n    let schedulable = !ctx\n        .get_node_by_id(\n            ctx.get_node_id()\n                .await\n                .ok_or((StatusCode::INTERNAL_SERVER_ERROR, \"Can't get node id\"))?,\n        )\n        .await\n        .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, \"Couldn't get node info\"))?\n        .map(|n| n.unschedulable)\n        .unwrap_or(false);\n\n    Ok(axum::Json(serde_json::json!({\n        \"package_name\": PACKAGE_NAME,\n        \"version\": VERSION,\n        \"nodes\": nodes,\n        \"schedulable\": schedulable,\n    })))\n}\n\npub fn router<S>() -> axum::Router<S>\nwhere\n    S: ControlStateDelegate + NodeDelegate + Clone + 'static,\n{\n    use axum::routing::get;\n    axum::Router::new().route(\"/\", get(health::<S>))\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/identity.rs",
    "content": "use std::time::Duration;\n\nuse axum::extract::{Path, State};\nuse axum::response::IntoResponse;\nuse axum::routing::MethodRouter;\nuse http::header::CONTENT_TYPE;\nuse http::StatusCode;\nuse serde::{Deserialize, Serialize};\n\nuse spacetimedb_lib::de::serde::DeserializeWrapper;\nuse spacetimedb_lib::Identity;\n\nuse crate::auth::{JwtAuthProvider, SpacetimeAuth, SpacetimeAuthRequired};\nuse crate::{log_and_500, ControlStateDelegate, NodeDelegate};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct CreateIdentityResponse {\n    identity: Identity,\n    token: String,\n}\n\npub async fn create_identity<S: ControlStateDelegate + NodeDelegate>(\n    State(ctx): State<S>,\n) -> axum::response::Result<impl IntoResponse> {\n    let auth = SpacetimeAuth::alloc(&ctx).await?;\n\n    let identity_response = CreateIdentityResponse {\n        identity: auth.claims.identity,\n        token: auth.creds.token().to_owned(),\n    };\n    Ok(axum::Json(identity_response))\n}\n\n/// A version of `Identity` appropriate for URL de/encoding.\n///\n/// Because `Identity` is represented in SATS as a `ProductValue`,\n/// its serialized format is somewhat gnarly.\n/// When URL-encoding identities, we want to use only the hex string,\n/// without wrapping it in a `ProductValue`.\n/// This keeps our routes pretty, like `/identity/<64 hex chars>/set-email`.\n///\n/// This newtype around `Identity` implements `Deserialize`\n/// directly from the inner identity bytes,\n/// without the enclosing `ProductValue` wrapper.\n#[derive(derive_more::Into, Clone, Debug, Copy)]\npub struct IdentityForUrl(Identity);\n\nimpl From<Identity> for IdentityForUrl {\n    fn from(i: Identity) -> Self {\n        IdentityForUrl(i)\n    }\n}\n\nimpl IdentityForUrl {\n    pub fn into_inner(&self) -> Identity {\n        self.0\n    }\n}\n\nimpl<'de> serde::Deserialize<'de> for IdentityForUrl {\n    fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {\n        <_>::deserialize(de).map(|DeserializeWrapper(b)| IdentityForUrl(Identity::from_be_byte_array(b)))\n    }\n}\n\n#[derive(Deserialize)]\npub struct GetDatabasesParams {\n    pub identity: IdentityForUrl,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct GetDatabasesResponse {\n    pub identities: Vec<Identity>,\n}\n\npub async fn get_databases<S: ControlStateDelegate>(\n    State(ctx): State<S>,\n    Path(GetDatabasesParams { identity }): Path<GetDatabasesParams>,\n) -> axum::response::Result<impl IntoResponse> {\n    let identity = identity.into();\n    // Linear scan for all databases that have this owner, and return their identities\n    let all_dbs = ctx.get_databases().await.map_err(|e| {\n        log::error!(\"Failure when retrieving databases for search: {e}\");\n        StatusCode::INTERNAL_SERVER_ERROR\n    })?;\n    let identities = all_dbs\n        .iter()\n        .filter(|db| db.owner_identity == identity)\n        .map(|db| db.database_identity)\n        .collect();\n    Ok(axum::Json(GetDatabasesResponse { identities }))\n}\n\n#[derive(Debug, Serialize)]\npub struct WebsocketTokenResponse {\n    pub token: String,\n}\n\n// This endpoint takes a token from a client and sends a newly signed token with a 60s expiry.\n// Note that even if the token has a different issuer, we will sign it with our key.\n// This is ok because `FullTokenValidator` checks if we signed the token before worrying about the issuer.\npub async fn create_websocket_token<S: NodeDelegate>(\n    State(ctx): State<S>,\n    SpacetimeAuthRequired(auth): SpacetimeAuthRequired,\n) -> axum::response::Result<impl IntoResponse> {\n    let expiry = Duration::from_secs(60);\n    let (_, token) = auth\n        .re_sign_with_expiry(ctx.jwt_auth_provider(), expiry)\n        .map_err(log_and_500)?;\n    // let token = encode_token_with_expiry(ctx.private_key(), auth.identity, Some(expiry)).map_err(log_and_500)?;\n    Ok(axum::Json(WebsocketTokenResponse { token }))\n}\n\n#[derive(Deserialize)]\npub struct ValidateTokenParams {\n    identity: IdentityForUrl,\n}\n\npub async fn validate_token(\n    Path(ValidateTokenParams { identity }): Path<ValidateTokenParams>,\n    SpacetimeAuthRequired(auth): SpacetimeAuthRequired,\n) -> axum::response::Result<impl IntoResponse> {\n    let identity = Identity::from(identity);\n\n    if auth.claims.identity != identity {\n        return Err(StatusCode::BAD_REQUEST.into());\n    }\n\n    Ok(StatusCode::NO_CONTENT)\n}\n\npub async fn get_public_key<S: NodeDelegate>(State(ctx): State<S>) -> axum::response::Result<impl IntoResponse> {\n    Ok((\n        [(CONTENT_TYPE, \"application/pem-certificate-chain\")],\n        ctx.jwt_auth_provider().public_key_bytes().to_owned(),\n    ))\n}\n\n/// A struct to allow customization of the `/identity` routes.\npub struct IdentityRoutes<S> {\n    /// POST /identity\n    pub create_post: MethodRouter<S>,\n    /// GET /identity/public-key\n    pub public_key_get: MethodRouter<S>,\n    /// POST /identity/websocket-tocken\n    pub websocket_token_post: MethodRouter<S>,\n    /// GET /identity/:identity/verify\n    pub verify_get: MethodRouter<S>,\n    /// GET /identity/:identity/databases\n    pub databases_get: MethodRouter<S>,\n}\n\nimpl<S> Default for IdentityRoutes<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Clone + 'static,\n{\n    fn default() -> Self {\n        use axum::routing::{get, post};\n        Self {\n            create_post: post(create_identity::<S>),\n            public_key_get: get(get_public_key::<S>),\n            websocket_token_post: post(create_websocket_token::<S>),\n            verify_get: get(validate_token),\n            databases_get: get(get_databases::<S>),\n        }\n    }\n}\n\nimpl<S> IdentityRoutes<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Clone + 'static,\n{\n    pub fn into_router(self) -> axum::Router<S> {\n        axum::Router::new()\n            .route(\"/\", self.create_post)\n            .route(\"/public-key\", self.public_key_get)\n            .route(\"/websocket-token\", self.websocket_token_post)\n            .route(\"/:identity/verify\", self.verify_get)\n            .route(\"/:identity/databases\", self.databases_get)\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/internal.rs",
    "content": "use crate::NodeDelegate;\n\n#[cfg(not(target_env = \"msvc\"))]\nmod jemalloc_profiling {\n    use axum::body::Body;\n    use axum::extract::Query;\n    use axum::response::{IntoResponse, Response};\n    use axum::Json;\n    use http::header::CONTENT_TYPE;\n    use http::StatusCode;\n    use serde::{Deserialize, Serialize};\n\n    /// Query parameters for the unified `heap` endpoint\n    #[derive(Deserialize)]\n    struct HeapQuery {\n        format: Option<String>,\n    }\n\n    async fn handle_get_heap(Query(params): Query<HeapQuery>) -> Result<impl IntoResponse, (StatusCode, String)> {\n        let Some(ctl) = jemalloc_pprof::PROF_CTL.as_ref() else {\n            return Err((\n                StatusCode::INTERNAL_SERVER_ERROR,\n                \"jemalloc profiling is disabled and cannot be activated\".into(),\n            ));\n        };\n\n        let mut prof_ctl = ctl.lock().await;\n        require_profiling_activated(&prof_ctl)?;\n\n        match params.format.as_deref() {\n            Some(\"flame\") => {\n                let svg = prof_ctl\n                    .dump_flamegraph()\n                    .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;\n                Response::builder()\n                    .header(CONTENT_TYPE, \"image/svg+xml\")\n                    .body(Body::from(svg))\n                    .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))\n            }\n            // Default to pprof if no or an invalid format is provided\n            _ => {\n                let pprof = prof_ctl\n                    .dump_pprof()\n                    .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;\n                Response::builder()\n                    .header(CONTENT_TYPE, \"application/octet-stream\")\n                    .body(Body::from(pprof))\n                    .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))\n            }\n        }\n    }\n\n    /// Checks whether jemalloc profiling is activated an returns an error response if not.\n    fn require_profiling_activated(prof_ctl: &jemalloc_pprof::JemallocProfCtl) -> Result<(), (StatusCode, String)> {\n        if prof_ctl.activated() {\n            Ok(())\n        } else {\n            Err((\n                axum::http::StatusCode::FORBIDDEN,\n                \"heap profiling is not activate. Activate by POSTing to /heap/settings?enabled=true\".into(),\n            ))\n        }\n    }\n\n    /// Query parameters for toggling heap profiling\n    #[derive(Deserialize)]\n    struct ToggleQuery {\n        enabled: bool,\n    }\n\n    /// JSON response for the current state of heap profiling\n    #[derive(Serialize)]\n    struct CurrentState {\n        enabled: bool,\n    }\n\n    /// Handles toggling heap profiling (on or off) via a `POST` request\n    async fn handle_post_heap_enabled(\n        Query(params): Query<ToggleQuery>,\n    ) -> Result<impl IntoResponse, (StatusCode, String)> {\n        let Some(ctl) = jemalloc_pprof::PROF_CTL.as_ref() else {\n            return Err((\n                StatusCode::INTERNAL_SERVER_ERROR,\n                \"jemalloc profiling is disabled and cannot be activated\".into(),\n            ));\n        };\n\n        let mut prof_ctl = ctl.lock().await;\n\n        if params.enabled {\n            prof_ctl.activate().map_err(|e| {\n                (\n                    StatusCode::INTERNAL_SERVER_ERROR,\n                    format!(\"Failed to activate heap profiling: {e}\"),\n                )\n            })?;\n            Ok((\"Heap profiling activated\").into_response())\n        } else {\n            prof_ctl.deactivate().map_err(|e| {\n                (\n                    StatusCode::INTERNAL_SERVER_ERROR,\n                    format!(\"Failed to deactivate heap profiling: {e}\"),\n                )\n            })?;\n            Ok((\"Heap profiling deactivated\").into_response())\n        }\n    }\n\n    /// Handles retrieving the current state of heap profiling via a `GET` request\n    async fn handle_get_heap_enabled() -> Result<impl IntoResponse, (StatusCode, String)> {\n        let Some(ctl) = jemalloc_pprof::PROF_CTL.as_ref() else {\n            return Err((\n                StatusCode::INTERNAL_SERVER_ERROR,\n                \"jemalloc profiling is disabled and cannot be activated.\".into(),\n            ));\n        };\n\n        let prof_ctl = ctl.lock().await;\n\n        let current_state = CurrentState {\n            enabled: prof_ctl.activated(),\n        };\n\n        Ok(Json(current_state))\n    }\n\n    pub fn jemalloc_router<S: Clone + Send + Sync + 'static>() -> axum::Router<S> {\n        use axum::routing::get;\n        axum::Router::new()\n            .route(\"/\", get(handle_get_heap))\n            .route(\"/settings\", get(handle_get_heap_enabled).post(handle_post_heap_enabled))\n    }\n}\n\n#[cfg(target_env = \"msvc\")]\nmod jemalloc_profiling {\n    use axum::response::IntoResponse;\n    use http::StatusCode;\n\n    async fn jemalloc_unsupported() -> impl IntoResponse {\n        // Return an error for msvc environments\n        (\n            StatusCode::INTERNAL_SERVER_ERROR,\n            \"jemalloc heap profiling is not supported on this platform.\",\n        )\n    }\n\n    pub fn jemalloc_router<S: Clone + Send + Sync + 'static>() -> axum::Router<S> {\n        use axum::routing::get;\n        axum::Router::new()\n            .route(\"/\", get(jemalloc_unsupported))\n            .route(\"/settings\", get(jemalloc_unsupported).post(jemalloc_unsupported))\n    }\n}\n\n// The internal router is for things that are not meant to be exposed to the public API.\npub fn router<S>() -> axum::Router<S>\nwhere\n    S: NodeDelegate + Clone + 'static,\n{\n    axum::Router::new().nest(\"/heap\", jemalloc_profiling::jemalloc_router())\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/metrics.rs",
    "content": "use axum::extract::State;\nuse axum::response::IntoResponse;\n\nuse crate::NodeDelegate;\n\n// #[derive(Clone, NewMiddleware)]\n// pub struct MetricsAuthMiddleware;\n\n// impl Middleware for MetricsAuthMiddleware {\n//     fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>\n//     where\n//         Chain: FnOnce(State) -> Pin<Box<HandlerFuture>>,\n//     {\n//         chain(state)\n//     }\n// }\n\npub async fn metrics<S: NodeDelegate>(State(ctx): State<S>) -> axum::response::Result<impl IntoResponse> {\n    let mut buf = String::new();\n\n    let mut encode_to_buffer = |mfs: &[_]| {\n        if let Err(e) = prometheus::TextEncoder.encode_utf8(mfs, &mut buf) {\n            log::error!(\"could not encode custom metrics: {e}\");\n        }\n    };\n\n    encode_to_buffer(&ctx.gather_metrics());\n    encode_to_buffer(&prometheus::gather());\n\n    Ok(buf)\n}\n\npub fn router<S>() -> axum::Router<S>\nwhere\n    S: NodeDelegate + Clone + 'static,\n{\n    use axum::routing::get;\n    axum::Router::new().route(\"/\", get(metrics::<S>))\n    // TODO:\n    // .layer(MetricsAuthMiddleware)\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/mod.rs",
    "content": "use http::header;\nuse tower_http::cors;\n\nuse crate::{Authorization, ControlStateDelegate, NodeDelegate};\n\npub mod database;\npub mod energy;\npub mod health;\npub mod identity;\nmod internal;\npub mod metrics;\npub mod prometheus;\npub mod subscribe;\n\nuse self::{database::DatabaseRoutes, identity::IdentityRoutes};\n\n/// This API call is just designed to allow clients to determine whether or not they can\n/// establish a connection to SpacetimeDB. This API call doesn't actually do anything.\npub async fn ping(_auth: crate::auth::SpacetimeAuthHeader) {}\n\n#[allow(clippy::let_and_return)]\npub fn router<S>(\n    ctx: &S,\n    database_routes: DatabaseRoutes<S>,\n    identity_routes: IdentityRoutes<S>,\n    extra: axum::Router<S>,\n) -> axum::Router<S>\nwhere\n    S: NodeDelegate + ControlStateDelegate + Authorization + Clone + 'static,\n{\n    use axum::routing::get;\n    let router = axum::Router::new()\n        .nest(\"/database\", database_routes.into_router(ctx.clone()))\n        .nest(\"/identity\", identity_routes.into_router())\n        .nest(\"/energy\", energy::router())\n        .nest(\"/prometheus\", prometheus::router())\n        .nest(\"/metrics\", metrics::router())\n        .route(\"/ping\", get(ping))\n        .merge(extra);\n\n    let cors = cors::CorsLayer::new()\n        .allow_headers([header::AUTHORIZATION, header::ACCEPT, header::CONTENT_TYPE])\n        .allow_methods(cors::Any)\n        .allow_origin(cors::Any);\n\n    axum::Router::new()\n        .nest(\"/v1\", router.layer(cors))\n        .nest(\"/internal\", internal::router())\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/prometheus.rs",
    "content": "use crate::{log_and_500, ControlStateReadAccess};\nuse axum::extract::State;\nuse axum::response::IntoResponse;\nuse serde::{Deserialize, Serialize};\nuse spacetimedb_data_structures::map::HashMap;\n\n#[derive(Serialize, Deserialize)]\nstruct SDConfig {\n    targets: Vec<String>,\n    labels: HashMap<String, String>,\n}\n\npub async fn get_sd_config<S: ControlStateReadAccess>(\n    State(ctx): State<S>,\n) -> axum::response::Result<impl IntoResponse> {\n    // TODO(cloutiertyler): security\n    let nodes = ctx.get_nodes().await.map_err(log_and_500)?;\n\n    let mut targets = Vec::new();\n    let labels = HashMap::default();\n\n    for node in nodes {\n        if let Some(addr) = node.advertise_addr {\n            targets.push(addr);\n        }\n    }\n\n    let sd_config = SDConfig { targets, labels };\n\n    Ok(axum::Json(vec![sd_config]))\n}\n\npub fn router<S>() -> axum::Router<S>\nwhere\n    S: ControlStateReadAccess + Clone + Send + Sync + 'static,\n{\n    use axum::routing::get;\n    axum::Router::new().route(\"/sd_config\", get(get_sd_config::<S>))\n}\n"
  },
  {
    "path": "crates/client-api/src/routes/subscribe.rs",
    "content": "use std::fmt::Display;\nuse std::future::Future;\nuse std::num::NonZeroUsize;\nuse std::panic;\nuse std::pin::{pin, Pin};\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse std::time::Duration;\n\nuse async_stream::stream;\nuse axum::extract::{Path, Query, State};\nuse axum::response::IntoResponse;\nuse axum::Extension;\nuse axum_extra::TypedHeader;\nuse bytes::Bytes;\nuse bytestring::ByteString;\nuse crossbeam_queue::ArrayQueue;\nuse derive_more::From;\nuse futures::{pin_mut, Sink, SinkExt, Stream, StreamExt};\nuse http::{HeaderValue, StatusCode};\nuse prometheus::{Histogram, IntGauge};\nuse scopeguard::{defer, ScopeGuard};\nuse serde::Deserialize;\nuse spacetimedb::client::messages::{\n    serialize, serialize_v2, IdentityTokenMessage, InUseSerializeBuffer, SerializeBuffer, SwitchedServerMessage,\n    ToProtocol,\n};\nuse spacetimedb::client::{\n    ClientActorId, ClientConfig, ClientConnection, ClientConnectionReceiver, DataMessage, MessageExecutionError,\n    MessageHandleError, MeteredReceiver, MeteredSender, OutboundMessage, Protocol, WsVersion,\n};\nuse spacetimedb::host::module_host::ClientConnectedError;\nuse spacetimedb::host::NoSuchModule;\nuse spacetimedb::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse spacetimedb::util::spawn_rayon;\nuse spacetimedb::worker_metrics::WORKER_METRICS;\nuse spacetimedb::Identity;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_client_api_messages::websocket::v2 as ws_v2;\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_lib::connection_id::{ConnectionId, ConnectionIdForUrl};\nuse tokio::sync::{mpsc, watch};\nuse tokio::task::JoinHandle;\nuse tokio::time::error::Elapsed;\nuse tokio::time::{sleep_until, timeout, Instant};\nuse tokio_tungstenite::tungstenite::protocol::frame::coding::{Data, OpCode};\nuse tokio_tungstenite::tungstenite::protocol::frame::Frame;\nuse tokio_tungstenite::tungstenite::Utf8Bytes;\n\nuse crate::auth::SpacetimeAuth;\nuse crate::util::serde::humantime_duration;\nuse crate::util::websocket::{\n    CloseCode, CloseFrame, Message as WsMessage, WebSocketConfig, WebSocketStream, WebSocketUpgrade, WsError,\n};\nuse crate::util::{NameOrIdentity, XForwardedFor};\nuse crate::{log_and_500, Authorization, ControlStateDelegate, NodeDelegate};\n\n#[allow(clippy::declare_interior_mutable_const)]\npub const TEXT_PROTOCOL: HeaderValue = HeaderValue::from_static(ws_v1::TEXT_PROTOCOL);\n#[allow(clippy::declare_interior_mutable_const)]\npub const BIN_PROTOCOL: HeaderValue = HeaderValue::from_static(ws_v1::BIN_PROTOCOL);\n#[allow(clippy::declare_interior_mutable_const)]\npub const V2_BIN_PROTOCOL: HeaderValue = HeaderValue::from_static(ws_v2::BIN_PROTOCOL);\n\npub trait HasWebSocketOptions {\n    fn websocket_options(&self) -> WebSocketOptions;\n}\n\nimpl<T: HasWebSocketOptions> HasWebSocketOptions for Arc<T> {\n    fn websocket_options(&self) -> WebSocketOptions {\n        (**self).websocket_options()\n    }\n}\n\n#[derive(Deserialize)]\npub struct SubscribeParams {\n    pub name_or_identity: NameOrIdentity,\n}\n\n#[derive(Deserialize)]\npub struct SubscribeQueryParams {\n    pub connection_id: Option<ConnectionIdForUrl>,\n    #[serde(default)]\n    pub compression: ws_v1::Compression,\n    /// Whether we want \"light\" responses, tailored to network bandwidth constrained clients.\n    /// This knob works by setting other, more specific, knobs to the value.\n    #[serde(default)]\n    pub light: bool,\n    /// If `true`, send the subscription updates only after the transaction\n    /// offset they're computed from is confirmed to be durable.\n    ///\n    /// If `false`, send them immediately.\n    #[serde(default)]\n    pub confirmed: Option<bool>,\n}\n\nfn resolve_confirmed_reads_default(version: WsVersion, confirmed: Option<bool>) -> bool {\n    if let Some(confirmed) = confirmed {\n        return confirmed;\n    }\n    match version {\n        WsVersion::V1 => false,\n        WsVersion::V2 => crate::DEFAULT_CONFIRMED_READS,\n    }\n}\n\npub fn generate_random_connection_id() -> ConnectionId {\n    ConnectionId::from_le_byte_array(rand::random())\n}\n\npub async fn handle_websocket<S>(\n    State(ctx): State<S>,\n    Path(SubscribeParams { name_or_identity }): Path<SubscribeParams>,\n    Query(SubscribeQueryParams {\n        connection_id,\n        compression,\n        light,\n        confirmed,\n    }): Query<SubscribeQueryParams>,\n    forwarded_for: Option<TypedHeader<XForwardedFor>>,\n    Extension(auth): Extension<SpacetimeAuth>,\n    ws: WebSocketUpgrade,\n) -> axum::response::Result<impl IntoResponse>\nwhere\n    S: NodeDelegate + ControlStateDelegate + HasWebSocketOptions + Authorization,\n{\n    if connection_id.is_some() {\n        // TODO: Bump this up to `log::warn!` after removing the client SDKs' uses of that parameter.\n        log::debug!(\"The connection_id query parameter to the subscribe HTTP endpoint is internal and will be removed in a future version of SpacetimeDB.\");\n    }\n\n    let connection_id = connection_id\n        .map(ConnectionId::from)\n        .unwrap_or_else(generate_random_connection_id);\n\n    if connection_id == ConnectionId::ZERO {\n        Err((\n            StatusCode::BAD_REQUEST,\n            \"Invalid connection ID: the all-zeros ConnectionId is reserved.\",\n        ))?;\n    }\n\n    let db_identity = name_or_identity.resolve(&ctx).await?;\n    let sql_auth = ctx.authorize_sql(auth.claims.identity, db_identity).await?;\n\n    #[derive(Clone, Copy)]\n    struct NegotiatedProtocol {\n        protocol: Protocol,\n        version: WsVersion,\n    }\n\n    let (res, ws_upgrade, protocol) = ws.select_protocol([\n        (\n            V2_BIN_PROTOCOL,\n            NegotiatedProtocol {\n                protocol: Protocol::Binary,\n                version: WsVersion::V2,\n            },\n        ),\n        (\n            BIN_PROTOCOL,\n            NegotiatedProtocol {\n                protocol: Protocol::Binary,\n                version: WsVersion::V1,\n            },\n        ),\n        (\n            TEXT_PROTOCOL,\n            NegotiatedProtocol {\n                protocol: Protocol::Text,\n                version: WsVersion::V1,\n            },\n        ),\n    ]);\n\n    let negotiated = protocol.ok_or((StatusCode::BAD_REQUEST, \"no valid protocol selected\"))?;\n    let client_config = ClientConfig {\n        protocol: negotiated.protocol,\n        version: negotiated.version,\n        compression,\n        tx_update_full: !light,\n        confirmed_reads: resolve_confirmed_reads_default(negotiated.version, confirmed),\n    };\n\n    // TODO: Should also maybe refactor the code and the protocol to allow a single websocket\n    // to connect to multiple modules\n\n    let database = ctx\n        .get_database_by_identity(&db_identity)\n        .await\n        .unwrap()\n        .ok_or(StatusCode::NOT_FOUND)?;\n\n    let leader = ctx.leader(database.id).await.map_err(log_and_500)?;\n\n    let identity_token = auth.creds.token().into();\n\n    let mut module_rx = leader.module_watcher().await.map_err(log_and_500)?;\n\n    let client_identity = auth.claims.identity;\n    let client_id = ClientActorId {\n        identity: client_identity,\n        connection_id,\n        name: ctx.client_actor_index().next_client_name(),\n    };\n\n    let ws_config = WebSocketConfig::default()\n        .max_message_size(Some(0x2000000))\n        .max_frame_size(None)\n        .accept_unmasked_frames(false);\n    let ws_opts = ctx.websocket_options();\n\n    tokio::spawn(async move {\n        let ws = match ws_upgrade.upgrade(ws_config).await {\n            Ok(ws) => ws,\n            Err(err) => {\n                log::error!(\"websocket: WebSocket init error: {err}\");\n                return;\n            }\n        };\n\n        let identity = client_id.identity;\n        let client_log_string = match forwarded_for {\n            Some(TypedHeader(XForwardedFor(ip))) => {\n                format!(\"ip {ip} with Identity {identity} and ConnectionId {connection_id}\")\n            }\n            None => format!(\"unknown ip with Identity {identity} and ConnectionId {connection_id}\"),\n        };\n\n        log::debug!(\"websocket: New client connected from {client_log_string}\");\n\n        let connected = match ClientConnection::call_client_connected_maybe_reject(\n            &mut module_rx,\n            client_id,\n            auth.clone().into(),\n        )\n        .await\n        {\n            Ok(connected) => {\n                log::debug!(\"websocket: client_connected returned Ok for {client_log_string}\");\n                connected\n            }\n            Err(e @ (ClientConnectedError::Rejected(_) | ClientConnectedError::OutOfEnergy)) => {\n                log::info!(\n                    \"websocket: Rejecting connection for {client_log_string} due to error from client_connected reducer: {e}\"\n                );\n                return;\n            }\n            Err(e @ (ClientConnectedError::DBError(_) | ClientConnectedError::ReducerCall(_))) => {\n                log::warn!(\"websocket: ModuleHost died while {client_log_string} was connecting: {e:#}\");\n                return;\n            }\n        };\n\n        log::debug!(\n            \"websocket: Database accepted connection from {client_log_string}; spawning ws_client_actor and ClientConnection\"\n        );\n\n        let actor = |client, receiver| ws_client_actor(ws_opts, client, ws, receiver);\n        let client = ClientConnection::spawn(\n            client_id,\n            auth.into(),\n            sql_auth,\n            client_config,\n            leader.replica_id,\n            module_rx,\n            actor,\n            connected,\n        )\n        .await;\n\n        // Send the client their identity token message as the first message\n        // NOTE: We're adding this to the protocol because some client libraries are\n        // unable to access the http response headers.\n        // Clients that receive the token from the response headers should ignore this\n        // message.\n        let send_res = match client.config.version {\n            WsVersion::V1 => {\n                let message = IdentityTokenMessage {\n                    identity: client_identity,\n                    token: identity_token,\n                    connection_id,\n                };\n                client.send_message(None, OutboundMessage::V1(message.into()))\n            }\n            WsVersion::V2 => {\n                let message = ws_v2::ServerMessage::InitialConnection(ws_v2::InitialConnection {\n                    identity: client_identity,\n                    connection_id,\n                    token: identity_token,\n                });\n                client.send_message(None, OutboundMessage::V2(message))\n            }\n        };\n        if let Err(e) = send_res {\n            log::warn!(\"websocket: Error sending initial message to {client_log_string}: {e}\");\n        }\n    });\n\n    Ok(res)\n}\n\nstruct ActorState {\n    pub client_id: ClientActorId,\n    pub database: Identity,\n    config: WebSocketOptions,\n    closed: AtomicBool,\n    got_pong: AtomicBool,\n}\n\nimpl ActorState {\n    pub fn new(database: Identity, client_id: ClientActorId, config: WebSocketOptions) -> Self {\n        Self {\n            database,\n            client_id,\n            config,\n            closed: AtomicBool::new(false),\n            got_pong: AtomicBool::new(true),\n        }\n    }\n\n    pub fn closed(&self) -> bool {\n        self.closed.load(Ordering::Relaxed)\n    }\n\n    pub fn close(&self) -> bool {\n        self.closed.swap(true, Ordering::Relaxed)\n    }\n\n    pub fn set_ponged(&self) {\n        self.got_pong.store(true, Ordering::Relaxed);\n    }\n\n    pub fn reset_ponged(&self) -> bool {\n        self.got_pong.swap(false, Ordering::Relaxed)\n    }\n\n    pub fn next_idle_deadline(&self) -> Instant {\n        Instant::now() + self.config.idle_timeout\n    }\n}\n\n/// Configuration for WebSocket connections.\n#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = \"kebab-case\")]\npub struct WebSocketOptions {\n    /// Interval at which to send `Ping` frames.\n    ///\n    /// We use pings for connection keep-alive.\n    /// Value must be smaller than `idle_timeout`.\n    ///\n    /// Default: 15s\n    #[serde(with = \"humantime_duration\")]\n    #[serde(default = \"WebSocketOptions::default_ping_interval\")]\n    pub ping_interval: Duration,\n    /// Amount of time after which an idle connection is closed.\n    ///\n    /// A connection is considered idle if no data is received nor sent.\n    /// This includes `Ping`/`Pong` frames used for keep-alive.\n    ///\n    /// Value must be greater than `ping_interval`.\n    ///\n    /// Default: 30s\n    #[serde(with = \"humantime_duration\")]\n    #[serde(default = \"WebSocketOptions::default_idle_timeout\")]\n    pub idle_timeout: Duration,\n    /// For how long to keep draining the incoming messages until a client close\n    /// is received.\n    ///\n    /// Default: 250ms\n    #[serde(with = \"humantime_duration\")]\n    #[serde(default = \"WebSocketOptions::default_close_handshake_timeout\")]\n    pub close_handshake_timeout: Duration,\n    /// Maximum number of messages to queue for processing.\n    ///\n    /// If this number is exceeded, the client is disconnected.\n    ///\n    /// Default: 16384\n    #[serde(default = \"WebSocketOptions::default_incoming_queue_length\")]\n    pub incoming_queue_length: NonZeroUsize,\n}\n\nimpl Default for WebSocketOptions {\n    fn default() -> Self {\n        Self::DEFAULT\n    }\n}\n\nimpl WebSocketOptions {\n    const DEFAULT_PING_INTERVAL: Duration = Duration::from_secs(15);\n    const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(30);\n    const DEFAULT_CLOSE_HANDSHAKE_TIMEOUT: Duration = Duration::from_millis(250);\n    const DEFAULT_INCOMING_QUEUE_LENGTH: NonZeroUsize = NonZeroUsize::new(16384).expect(\"16384 > 0, qed\");\n\n    const DEFAULT: Self = Self {\n        ping_interval: Self::DEFAULT_PING_INTERVAL,\n        idle_timeout: Self::DEFAULT_IDLE_TIMEOUT,\n        close_handshake_timeout: Self::DEFAULT_CLOSE_HANDSHAKE_TIMEOUT,\n        incoming_queue_length: Self::DEFAULT_INCOMING_QUEUE_LENGTH,\n    };\n\n    const fn default_ping_interval() -> Duration {\n        Self::DEFAULT_PING_INTERVAL\n    }\n\n    const fn default_idle_timeout() -> Duration {\n        Self::DEFAULT_IDLE_TIMEOUT\n    }\n\n    const fn default_close_handshake_timeout() -> Duration {\n        Self::DEFAULT_CLOSE_HANDSHAKE_TIMEOUT\n    }\n\n    const fn default_incoming_queue_length() -> NonZeroUsize {\n        Self::DEFAULT_INCOMING_QUEUE_LENGTH\n    }\n}\n\nasync fn ws_client_actor(\n    options: WebSocketOptions,\n    client: ClientConnection,\n    ws: WebSocketStream,\n    sendrx: ClientConnectionReceiver,\n) {\n    // ensure that even if this task gets cancelled, we always cleanup the connection\n    let mut client = scopeguard::guard(client, |client| {\n        tokio::spawn(client.disconnect());\n    });\n\n    ws_client_actor_inner(&mut client, options, ws, sendrx).await;\n\n    ScopeGuard::into_inner(client).disconnect().await;\n}\n\nasync fn ws_client_actor_inner(\n    client: &mut ClientConnection,\n    config: WebSocketOptions,\n    ws: WebSocketStream,\n    sendrx: ClientConnectionReceiver,\n) {\n    let database = client.module().info().database_identity;\n    let client_id = client.id;\n    let client_closed_metric = WORKER_METRICS.ws_clients_closed_connection.with_label_values(&database);\n    let state = Arc::new(ActorState::new(database, client_id, config));\n\n    // Channel for [`UnorderedWsMessage`]s.\n    let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n    // Split websocket into send and receive halves.\n    let (ws_send, ws_recv) = ws.split();\n\n    // Set up the idle timer.\n    let (idle_tx, idle_rx) = watch::channel(state.next_idle_deadline());\n    let idle_timer = ws_idle_timer(idle_rx);\n\n    let bsatn_rlb_pool = client.module().subscriptions().bsatn_rlb_pool.clone();\n\n    // Spawn a task to send outgoing messages\n    // obtained from `sendrx` and `unordered_rx`.\n    let send_task = tokio::spawn(ws_send_loop(\n        state.clone(),\n        client.config,\n        ws_send,\n        sendrx,\n        unordered_rx,\n        bsatn_rlb_pool,\n    ));\n    // Spawn a task to handle incoming messages.\n    let recv_task = tokio::spawn(ws_recv_task(\n        state.clone(),\n        idle_tx,\n        client_closed_metric,\n        {\n            let client = client.clone();\n            move |data, timer| {\n                let client = client.clone();\n                async move { client.handle_message(data, timer.into()).await }\n            }\n        },\n        unordered_tx.clone(),\n        ws_recv,\n        client.config.version,\n    ));\n    let hotswap = {\n        let client = client.clone();\n        move || {\n            let mut client = client.clone();\n            async move { client.watch_module_host().await }\n        }\n    };\n\n    ws_main_loop(state, hotswap, idle_timer, send_task, recv_task, move |msg| {\n        let _ = unordered_tx.send(msg);\n    })\n    .await;\n    log::info!(\"Client connection ended: {client_id}\");\n}\n\n/// The main `select!` loop of the websocket client actor.\n///\n/// > This function is defined standalone with generic parameters so that its\n/// > behavior can be tested in isolation, not requiring I/O and allowing to\n/// > mock effects easily.\n///\n/// The loop's responsibilities are:\n///\n/// - Drive the tasks handling the send and receive ends of the websockets to\n///   completion, terminating when either of them completes.\n///\n/// - Terminating if the connection is idle for longer than [`ActorConfig::idle_timeout`].\n///   The connection becomes idle if nothing is received from the socket.\n///\n/// - Periodically sending `Ping` frames to prevent the connection from becoming\n///   idle (the client is supposed to respond with `Pong`, which resets the\n///   idle timer). See [`ActorConfig::ping_interval`].\n///\n/// - Watch for changes to the [`ClientConnection`]'s module reference.\n///   If it changes, the [`ClientConnection`] \"hotswaps\" the module, if it\n///   is exited, the loop schedules a `Close` frame to be sent, initiating a\n///   connection shutdown.\n///\n/// A peculiarity of handling termination is the websocket [close handshake]:\n/// whichever side wants to close the connection sends a `Close` frame and needs\n/// to wait for the other end to respond with a `Close` for the connection to\n/// end cleanly.\n///\n/// `tungstenite` handles the protocol details of the close handshake for us,\n/// but for it to work properly, we must keep polling the socket until the\n/// handshake is complete.\n///\n/// This is straightforward when the client initiates the close, as the receive\n/// stream will just become exhausted, and we'll exit the loop.\n///\n/// In the case of a server-initiated close, it's a bit more tricky, as we're\n/// not supposed to send any more data after a `Close` frame (and `tungstenite`\n/// prevents it). Yet, we need to keep polling the receive end until either\n/// the `Close` response (which could be queued behind a large number of\n/// outstanding messages) arrives, or a timeout elapses (in case the client\n/// never responds).\n///\n/// The implementations [`ws_recv_loop`] and [`ws_send_loop`] thus share the\n/// [`ActorState`], which tracks whether the connection is in the closing phase\n/// ([`ActorState::closed()`]). If closed, both the send and receive loops keep\n/// running, but drop any incoming or outgoing messages respectively until\n/// either the `Close` response arrives or [`ActorConfig::close_handshake_timeout`]\n/// elapses.\n///\n///\n/// Parameters:\n///\n/// * **state**:\n///   The shared [`ActorState`], updated here when a `Pong` message is received.\n///\n/// * **hotswap**:\n///   An abstraction for [`ClientConnection::watch_module_host`], which updates\n///   the connection's internal reference to the module if it was updated,\n///   allowing database updates without disconnecting clients.\n///\n///   It is polled here for its error return value: if the output of the future\n///   is `Err(NoSuchModule)`, the database was shut down and existing clients\n///   must be disconnected.\n///\n/// * **idle_timer**:\n///   Abstraction for [`ws_idle_timer`]: if and when the future completes, the\n///   connection is considered unresponsive, and the connection is closed.\n///\n///   The idle timer should be reset whenever data is received from the websocket.\n///\n/// * **send_task**:\n///   Task handling outgoing messages. Holds the receive end of `unordered_tx`.\n///\n///   If the task returns, the connection is considered bad, and the main loop\n///   exits. If the task panicked, the panic is resumed on the current thread.\n///\n///   Note that the send task must not terminate after it has sent a `Close`\n///   frame (via `unordered_tx`) -- the websocket protocol mandates that the\n///   initiator of the close handshake wait for the other end to respond with\n///   a `Close` frame. Thus, the loop must continue to poll `recv_task` and not\n///   exit due to `send_task` being complete.\n///\n///   See [`ws_send_loop`].\n///\n/// * **recv_task**:\n///   Task handling incoming messages.\n///\n///   If the task returns, the connection is considered closed, and the main\n///   loop exits. If the task panicked, the panic is resumed on the current\n///   thread.\n///\n///   See [`ws_recv_task`].\n///\n/// * **unordered_tx**:\n///   Channel connected to `send_task` that allows the loop to send `Ping` and\n///   `Close` frames.\n///\n///   Note that messages sent while the receiving `send_task` is already\n///   terminated are silently ignored. This is safe because the loop will exit\n///   anyway when the `send_task` is complete.\n///\n///\n/// [close handshake]: https://datatracker.ietf.org/doc/html/rfc6455#section-7\nasync fn ws_main_loop<HotswapWatcher>(\n    state: Arc<ActorState>,\n    hotswap: impl Fn() -> HotswapWatcher,\n    idle_timer: impl Future<Output = ()>,\n    mut send_task: JoinHandle<()>,\n    mut recv_task: JoinHandle<()>,\n    unordered_tx: impl Fn(UnorderedWsMessage),\n) where\n    HotswapWatcher: Future<Output = Result<(), NoSuchModule>>,\n{\n    // Ensure we terminate both tasks if either exits.\n    let abort_send = send_task.abort_handle();\n    let abort_recv = recv_task.abort_handle();\n    defer! {\n        abort_send.abort();\n        abort_recv.abort();\n    };\n    // Set up the ping interval.\n    let mut ping_interval = tokio::time::interval(state.config.ping_interval);\n    // Arm the first hotswap watcher.\n    let watch_hotswap = hotswap();\n\n    pin_mut!(watch_hotswap);\n    pin_mut!(idle_timer);\n\n    loop {\n        let closed = state.closed();\n\n        tokio::select! {\n            // Drive send and receive tasks to completion,\n            // propagating panics.\n            //\n            // If either task completes,\n            // the connection is considered closed and we break the loop.\n            //\n            // NOTE: We don't abort the tasks until this function returns,\n            // so the `Err` can't contain an `is_cancelled()` value.\n            //\n            // Even if the tasks were cancelled (e.g. if the caller retains\n            // [`tokio::task::AbortHandle`]s), the reasonable thing to do is to\n            // exit the loop as if the tasks completed normally.\n            res = &mut send_task => {\n                if let Err(e) = res\n                    && e.is_panic() {\n                        panic::resume_unwind(e.into_panic())\n                    }\n                break;\n            },\n            res = &mut recv_task => {\n                if let Err(e) = res\n                    && e.is_panic() {\n                        panic::resume_unwind(e.into_panic())\n                    }\n                break;\n            },\n\n            // Exit if we haven't heard from the client for too long.\n            _ = &mut idle_timer => {\n                log::warn!(\"Client {} timed out\", state.client_id);\n                break;\n            },\n\n            // Update the client's module host if it was hotswapped,\n            // or close the session if the module exited.\n            //\n            // Branch is disabled if we already sent a close frame.\n            res = &mut watch_hotswap, if !closed => {\n                if let Err(NoSuchModule) = res {\n                    let close = CloseFrame {\n                        code: CloseCode::Away,\n                        reason: \"module exited\".into()\n                    };\n                    unordered_tx(close.into());\n                }\n                watch_hotswap.set(hotswap());\n            },\n\n            // Send ping.\n            //\n            // If we didn't receive a response to the last ping,\n            // we don't bother sending a fresh one.\n            //\n            // Either the connection is idle (in which case the timer will kick\n            // in), or there is a massive backlog to process until the pong\n            // appears on the ordered stream. In either case, adding more pings\n            // is of no value.\n            //\n            // Branch is disabled if we already sent a close frame.\n            _ = ping_interval.tick(), if !closed => {\n                let was_ponged = state.reset_ponged();\n                if was_ponged {\n                    unordered_tx(UnorderedWsMessage::Ping(Bytes::new()));\n                }\n            }\n        }\n    }\n}\n\n/// A sleep that can be extended by sending it new deadlines.\n///\n/// Sleeps until the deadline appearing on the `activity` channel,\n/// i.e. if a new deadline appears before the sleep finishes,\n/// the sleep is reset to the new deadline.\n///\n/// The `activity` should be updated whenever a new message is received.\nasync fn ws_idle_timer(mut activity: watch::Receiver<Instant>) {\n    let mut deadline = *activity.borrow();\n    let sleep = sleep_until(deadline);\n    pin_mut!(sleep);\n\n    loop {\n        tokio::select! {\n            biased;\n\n            Ok(()) = activity.changed() => {\n                let new_deadline = *activity.borrow_and_update();\n                if new_deadline != deadline {\n                    deadline = new_deadline;\n                    sleep.as_mut().reset(deadline);\n                }\n            },\n\n            () = &mut sleep => {\n                break;\n            },\n        }\n    }\n}\n\n/// Consumes `ws` by composing [`ws_recv_queue`], [`ws_recv_loop`],\n/// [`ws_client_message_handler`] and `message_handler`.\n///\n/// `idle_tx` is the sending end of a [`ws_idle_timer`]. The [`ws_recv_loop`]\n/// sends a new, extended deadline whenever it receives a message.\n///\n/// `unordered_tx` is used to send message execution errors\n/// or to initiate a close handshake.\n///\n/// Initiates a close handshake if the `message_handler` returns any variant\n/// of [`MessageHandleError`] that is **not** [`MessageHandleError::Execution`].\n///\n/// Terminates if:\n///\n/// - the `ws` stream is exhausted\n/// - or, `unordered_tx` is already closed\n///\n/// In the latter case, we assume that the connection is in an errored state,\n/// such that we wouldn't be able to receive any more messages anyway.\nasync fn ws_recv_task<MessageHandler>(\n    state: Arc<ActorState>,\n    idle_tx: watch::Sender<Instant>,\n    client_closed_metric: IntGauge,\n    message_handler: impl Fn(DataMessage, Instant) -> MessageHandler,\n    unordered_tx: mpsc::UnboundedSender<UnorderedWsMessage>,\n    ws: impl Stream<Item = Result<WsMessage, WsError>> + Unpin + Send + 'static,\n    ws_version: WsVersion,\n) where\n    MessageHandler: Future<Output = Result<(), MessageHandleError>>,\n{\n    let recv_queue_gauge = WORKER_METRICS\n        .total_incoming_queue_length\n        .with_label_values(&state.database);\n    let recv_queue = ws_recv_queue(state.clone(), unordered_tx.clone(), recv_queue_gauge, ws);\n    let recv_loop = pin!(ws_recv_loop(state.clone(), idle_tx, recv_queue));\n    let recv_handler = ws_client_message_handler(state.clone(), client_closed_metric, recv_loop);\n    pin_mut!(recv_handler);\n\n    while let Some((data, timer)) = recv_handler.next().await {\n        let result = message_handler(data, timer).await;\n        if let Err(e) = result {\n            if ws_version == WsVersion::V1\n                && let MessageHandleError::Execution(err) = e\n            {\n                log::error!(\"{err:#}\");\n                // If the send task has exited, also exit this recv task.\n                if unordered_tx.send(err.into()).is_err() {\n                    break;\n                }\n                continue;\n            }\n            log::debug!(\"Client caused error: {e}\");\n            let close = CloseFrame {\n                code: CloseCode::Error,\n                reason: format!(\"{e:#}\").into(),\n            };\n            // If the send task has exited, also exit this recv task.\n            // No need to send the close handshake in that case; the client is already gone.\n            if unordered_tx.send(close.into()).is_err() {\n                break;\n            };\n        }\n    }\n}\n\n/// Stream that consumes a stream of [`WsMessage`]s and yields [`ClientMessage`]s.\n///\n/// Terminates if:\n///\n/// - the input stream is exhausted\n/// - the input stream yields an error\n///\n/// If `state.closed`, continues to poll the input stream in order for the\n/// websocket close handshake to complete. Any messages received while in this\n/// state are dropped.\nfn ws_recv_loop(\n    state: Arc<ActorState>,\n    idle_tx: watch::Sender<Instant>,\n    mut ws: impl Stream<Item = Result<WsMessage, WsError>> + Unpin,\n) -> impl Stream<Item = ClientMessage> {\n    // Get the next message from `ws`, or `None` if the stream is exhausted.\n    //\n    // If `state.closed`, `ws` is drained until it either yields an `Err`, is\n    // exhausted, or a timeout of 250ms has elapsed.\n    async fn next_message(\n        state: &ActorState,\n        ws: &mut (impl Stream<Item = Result<WsMessage, WsError>> + Unpin),\n    ) -> Option<Result<WsMessage, WsError>> {\n        if state.closed() {\n            log::trace!(\"drain websocket waiting for client close\");\n            let res: Result<Option<Result<WsMessage, WsError>>, Elapsed> =\n                timeout(state.config.close_handshake_timeout, async {\n                    while let Some(item) = ws.next().await {\n                        match item {\n                            Ok(message) => drop(message),\n                            Err(e) => return Some(Err(e)),\n                        }\n                    }\n                    None\n                })\n                .await;\n            match res {\n                Err(_elapsed) => {\n                    log::warn!(\"timeout waiting for client close\");\n                    None\n                }\n                Ok(item) => item, // either error or `None`\n            }\n        } else {\n            log::trace!(\"await next client message without timeout\");\n            ws.next().await\n        }\n    }\n\n    stream! {\n        loop {\n            let Some(res) = next_message(&state, &mut ws).await else {\n                log::trace!(\"recv stream exhausted\");\n                break;\n            };\n            match res {\n                Ok(m) => {\n                    idle_tx.send(state.next_idle_deadline()).ok();\n\n                    if !state.closed() {\n                        yield ClientMessage::from_message(m);\n                        continue;\n                    }\n                    // If closed, keep polling until either:\n                    //\n                    // - the client sends a close frame (`ws` returns `None)\n                    // - or `ws` yields an error\n                    log::trace!(\"message received while already closed\");\n                }\n                // None of the error cases can be meaningfully recovered from\n                // (and some can't even occur on the `ws` stream).\n                // Exit here but spell out an exhaustive match\n                // in order to bring any future library changes to our attention.\n                Err(e) => match e {\n                    e @ (WsError::ConnectionClosed\n                    | WsError::AlreadyClosed\n                    | WsError::Io(_)\n                    | WsError::Tls(_)\n                    | WsError::Capacity(_)\n                    | WsError::Protocol(_)\n                    | WsError::WriteBufferFull(_)\n                    | WsError::Utf8(_)\n                    | WsError::AttackAttempt\n                    | WsError::Url(_)\n                    | WsError::Http(_)\n                    | WsError::HttpFormat(_)) => {\n                        log::warn!(\"Websocket receive error: {e}\");\n                        break;\n                    }\n                },\n            }\n        }\n    }\n}\n\n/// Consumes `ws` and queues its items in a channel.\n///\n/// The channel is initialized with [`ActorConfig::incoming_queue_length`].\n/// If it is at capacity, a connection shutdown is initiated by sending\n/// [`UnorderedWsMessage::Close`] via `unordered_tx`.\n///\n/// Returns the channel receiver.\n///\n/// NOTE: This function is provided for backwards-compatibility, in particular\n/// SDK clients not handling backpressure gracefully, and for observability of\n/// transaction backlogging. It will probably go away in the future, see [#1851].\n///\n/// [#1851]: https://github.com/clockworklabs/SpacetimeDBPrivate/issues/1851\nfn ws_recv_queue(\n    state: Arc<ActorState>,\n    unordered_tx: mpsc::UnboundedSender<UnorderedWsMessage>,\n    recv_queue_gauge: IntGauge,\n    mut ws: impl Stream<Item = Result<WsMessage, WsError>> + Unpin + Send + 'static,\n) -> impl Stream<Item = Result<WsMessage, WsError>> {\n    const CLOSE: UnorderedWsMessage = UnorderedWsMessage::Close(CloseFrame {\n        code: CloseCode::Again,\n        reason: Utf8Bytes::from_static(\"too many requests\"),\n    });\n    let on_message_after_close = move |client_id| {\n        log::warn!(\"client {client_id} sent message after close or error\");\n    };\n\n    let max_incoming_queue_length = state.config.incoming_queue_length.get();\n\n    let (tx, rx) = mpsc::channel(max_incoming_queue_length);\n\n    let mut tx = MeteredSender::with_gauge(tx, recv_queue_gauge.clone());\n    let rx = MeteredReceiver::with_gauge(rx, recv_queue_gauge);\n    let rx = MeteredReceiverStream { inner: rx };\n\n    tokio::spawn(async move {\n        while let Some(item) = ws.next().await {\n            if let Err(e) = tx.try_send(item) {\n                match e {\n                    // If the queue is full, disconnect the client.\n                    mpsc::error::TrySendError::Full(item) => {\n                        let client_id = state.client_id;\n                        log::warn!(\"Client {client_id} exceeded incoming_queue_length limit of {max_incoming_queue_length} requests\");\n                        // If we can't send close (send task already terminated):\n                        //\n                        // - Let downstream handlers know that we're closing,\n                        //   so that remaining items in the queue are dropped.\n                        //\n                        // - Then exit the loop, as we won't be processing any\n                        //   more messages, and we don't expect a close response\n                        //   to arrive from the client.\n                        if unordered_tx.send(CLOSE).is_err() {\n                            state.close();\n                            break;\n                        }\n                        // If we successfully enqueued `CLOSE`, enqueue `item`\n                        // as well, as soon as there is space in the channel.\n                        //\n                        // This is to allow the client to complete the close\n                        // handshake, for which the downstream handler needs to\n                        // drain the queue.\n                        //\n                        // If `tx.send` fails, the pipeline is broken, so exit.\n                        // See commentary on the `TrySendError::Closed` match\n                        // arm below.\n                        if tx.send(item).await.is_err() {\n                            on_message_after_close(state.client_id);\n                            break;\n                        }\n                    }\n                    // If the downstream consumer went away,\n                    // it has consumed a `Close` frame or `Err` value\n                    // from the queue and thus has determined that it's done.\n                    //\n                    // Well-behaved clients shouldn't send anything after\n                    // closing, so issue a warning.\n                    //\n                    // We're done either way, so break.\n                    mpsc::error::TrySendError::Closed(_item) => {\n                        on_message_after_close(state.client_id);\n                        break;\n                    }\n                }\n            }\n        }\n    });\n\n    rx\n}\n\n/// Turns a [`MeteredReceiver`] into a [`Stream`],\n/// like [`tokio_stream::wrappers::ReceiverStream`] does for [`mpsc::Receiver`].\nstruct MeteredReceiverStream<T> {\n    inner: MeteredReceiver<T>,\n}\n\nimpl<T> Stream for MeteredReceiverStream<T> {\n    type Item = T;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        self.inner.poll_recv(cx)\n    }\n}\n\n/// Stream that consumes [`ClientMessage`]s and yields [`DataMessage`]s for\n/// evaluation.\n///\n/// Calls `state.set_ponged()` if and when the input yields a pong message.\n/// Calls `state.close()` if and when the input yields a close frame,\n/// i.e. the client initiated a close handshake, which we track using the\n/// `client_closed_metric`.\n///\n/// Terminates if and when the input stream terminates.\nfn ws_client_message_handler(\n    state: Arc<ActorState>,\n    client_closed_metric: IntGauge,\n    mut messages: impl Stream<Item = ClientMessage> + Unpin,\n) -> impl Stream<Item = (DataMessage, Instant)> {\n    stream! {\n        while let Some(message) = messages.next().await {\n            match message {\n                ClientMessage::Message(message) => {\n                    log::trace!(\"Received client message\");\n                    yield (message, Instant::now());\n                },\n                ClientMessage::Ping(_bytes) => {\n                    log::trace!(\"Received ping from client {}\", state.client_id);\n                    // `tungstenite` will respond with `Pong` for us,\n                    // no need to send it ourselves.\n                },\n                ClientMessage::Pong(_bytes) => {\n                    log::trace!(\"Received pong from client {}\", state.client_id);\n                    state.set_ponged();\n                },\n                ClientMessage::Close(close_frame) => {\n                    log::trace!(\"Received Close frame from client {}: {:?}\", state.client_id, close_frame);\n                    let was_closed = state.close();\n                    // This is the client telling us they want to close.\n                    if !was_closed {\n                        client_closed_metric.inc();\n                    }\n                }\n            }\n        }\n        log::trace!(\"client message handler done\");\n    }\n}\n\n/// Outgoing messages that don't need to be ordered wrt subscription updates.\n#[derive(Debug, From)]\nenum UnorderedWsMessage {\n    /// Server-initiated close.\n    Close(CloseFrame),\n    /// Server-initiated ping.\n    Ping(Bytes),\n    /// Error calling a reducer.\n    ///\n    /// The error indicates that the reducer was **not** called,\n    /// and can thus be unordered wrt subscription updates.\n    Error(MessageExecutionError),\n}\n\n/// Abstraction over [`ClientConnectionReceiver`], so tests can use a plain\n/// [`mpsc::Receiver`].\ntrait Receiver<T> {\n    fn recv(&mut self) -> impl Future<Output = Option<T>> + Send;\n    fn close(&mut self);\n}\n\nimpl Receiver<OutboundMessage> for ClientConnectionReceiver {\n    async fn recv(&mut self) -> Option<OutboundMessage> {\n        ClientConnectionReceiver::recv(self).await\n    }\n\n    fn close(&mut self) {\n        ClientConnectionReceiver::close(self);\n    }\n}\n\nimpl<T: Send> Receiver<T> for mpsc::Receiver<T> {\n    async fn recv(&mut self) -> Option<T> {\n        mpsc::Receiver::recv(self).await\n    }\n\n    fn close(&mut self) {\n        mpsc::Receiver::close(self);\n    }\n}\n\n/// Sink that sends outgoing messages to the `ws` sink.\n///\n/// Consumes `messages`, which yields subscription updates and reducer call\n/// results. Note that [`SerializableMessage`]s require serialization and\n/// potentially compression, which can be costly.\n/// Also consumes `unordered`, which yields [`UnorderedWsMessage`]s.\n///\n/// Terminates if:\n///\n/// - `unordered` is closed\n/// - an error occurs sending to the `ws` sink\n///\n/// If an [`UnorderedWsMessage::Close`] is encountered, a close frame is sent\n/// to the `ws` sink, and `state.close()` is called. When this happens,\n/// `messages` will no longer be polled (no data can be sent after a close\n/// frame anyways), so `messages.close()` will be called.\n///\n/// Keeps polling `unordered` if `state.closed()`, but discards all data.\n/// This is so `ws_client_actor_inner` keeps polling the receive end of the\n/// socket until the close handshake completes -- it would otherwise exit early\n/// when sending to `unordered` fails.\nasync fn ws_send_loop(\n    state: Arc<ActorState>,\n    config: ClientConfig,\n    ws: impl Sink<WsMessage, Error: Display> + Unpin,\n    messages: impl Receiver<OutboundMessage>,\n    unordered: mpsc::UnboundedReceiver<UnorderedWsMessage>,\n    bsatn_rlb_pool: BsatnRowListBuilderPool,\n) {\n    let metrics = SendMetrics::new(state.database);\n    ws_send_loop_inner(state, ws, messages, unordered, move |encode_rx, frames_tx| {\n        ws_encode_task(metrics, config, encode_rx, frames_tx, bsatn_rlb_pool)\n    })\n    .await\n}\n\nasync fn ws_send_loop_inner<T, U, Encoder>(\n    state: Arc<ActorState>,\n    mut ws: impl Sink<WsMessage, Error: Display> + Unpin,\n    mut messages: impl Receiver<T>,\n    mut unordered: mpsc::UnboundedReceiver<UnorderedWsMessage>,\n    encoder: impl FnOnce(mpsc::UnboundedReceiver<U>, mpsc::UnboundedSender<Frame>) -> Encoder,\n) where\n    T: Into<U>,\n    U: From<MessageExecutionError>,\n    Encoder: Future<Output = ()> + Send + 'static,\n{\n    // The number of frames we'll `feed` to the `ws` sink in one iteration\n    // of the `select!` loop.\n    //\n    // This batching is done to allow control messages appearing on `unordered`\n    // to be interleaved with the sending of large messages split across some\n    // number of frames.\n    //\n    // This allows clients with slow connections to respond to `Ping`s, and\n    // avoid timing out, while receiving large messages.\n    //\n    // The default frame size is 4KiB, hence we write in batches of 32KiB.\n    const FRAME_BATCH_SIZE: usize = 8;\n    let mut frames_batch = Vec::with_capacity(FRAME_BATCH_SIZE);\n    let (frames_tx, mut frames_rx) = mpsc::unbounded_channel();\n\n    let (encode_tx, encode_rx) = mpsc::unbounded_channel();\n    // Spawn the encode task.\n    //\n    // NOTE: It is not technically required to introduce parallelism for\n    // encoding. We spawn mainly to avoid having to manually poll the `Encoder`\n    // future in the `select!` loop below, which proved to be quite error\n    // prone in the past (looking at you, `also_poll`).\n    tokio::spawn(encoder(encode_rx, frames_tx));\n\n    'outer: loop {\n        let closed = state.closed();\n\n        tokio::select! {\n            // `biased` because we want to:\n            //\n            // - give control messages precedence\n            // - and flush outstanding messages\n            //   before taking on more encoding work\n            biased;\n\n            // Check for control messages or execution errors.\n            maybe_msg = unordered.recv() => {\n                let Some(msg) = maybe_msg else {\n                    break;\n                };\n                // We shall not send more data after a close frame,\n                // but keep polling `unordered` so that `ws_client_actor` keeps\n                // waiting for an acknowledgement from the client,\n                // even if it spuriously initiates another close itself.\n                if closed {\n                    continue;\n                }\n                match msg {\n                    UnorderedWsMessage::Close(close_frame) => {\n                        log::trace!(\"intiating close\");\n                        // Send outstanding frames until one that has the FIN\n                        // bit set. Ensures the client won't receive partial\n                        // messages before we shut down.\n                        log::trace!(\"draining outgoing frames\");\n                        while let Ok(frame) = frames_rx.try_recv() {\n                            let eof = frame.header().is_final;\n                            if let Err(e) = ws.feed(WsMessage::Frame(frame)).await {\n                                log::warn!(\"error sending frame: {e:#}\");\n                                break 'outer;\n                            }\n\n                            if eof {\n                                break;\n                            }\n                        }\n                        // Then send the close frame.\n                        log::trace!(\"sending close frame\");\n                        if let Err(e) = ws.send(WsMessage::Close(Some(close_frame))).await {\n                            log::warn!(\"error sending close frame: {e:#}\");\n                            break;\n                        }\n\n                        // Lastly, update the state.\n                        //\n                        // NOTE: It's ok to not update the state if we fail to\n                        // send the close frame, because we assume that the main\n                        // loop with exit when this future terminates.\n                        // We shouldn't set the state to closed before sending\n                        // the close frame, however, as we would start dropping\n                        // messages immediately (defeating the purpose of the\n                        // close handshake).\n                        state.close();\n                        // We won't be polling `messages` anymore,\n                        // so let senders know.\n                        messages.close();\n                    },\n                    UnorderedWsMessage::Ping(bytes) => {\n                        log::trace!(\"sending ping\");\n                        if let Err(e) = ws.feed(WsMessage::Ping(bytes)).await {\n                            log::warn!(\"error sending ping: {e:#}\");\n                            break;\n                        }\n                    },\n                    UnorderedWsMessage::Error(err) => {\n                        log::trace!(\"encoding execution error\");\n                        encode_tx\n                            .send(err.into())\n                            // `ws_encode_task` shouldn't terminate until\n                            // `encode_tx` is dropped, except by panicking.\n                            .expect(\"encode task panicked\");\n                    },\n                }\n            },\n\n            // Send a batch of frames.\n            //\n            // Branch is disabled if we already sent a close frame.\n            //\n            // TODO: If the client sent us a close frame and we're in the middle\n            // of a large message, we may not send them the whole message.\n            // If that turns out to be a problem, we'll need to keep track of\n            // which side initiated the close handshake.\n            // Unsure if `tungstenite` will support us here, i.e. allows to keep\n            // sending when the other side initiated the close.\n            n = frames_rx.recv_many(&mut frames_batch, FRAME_BATCH_SIZE), if !closed => {\n                log::trace!(\"sending batch of {n} frames\");\n                for frame in frames_batch.drain(..n) {\n                    if let Err(e) = ws.feed(WsMessage::Frame(frame)).await {\n                        log::warn!(\"error sending frame: {e:#}\");\n                        break 'outer;\n                    }\n                }\n            },\n\n            // Take on more work.\n            //\n            // Branch is disabled if we already sent a close frame.\n            Some(message) = messages.recv(), if !closed => {\n                encode_tx\n                    .send(message.into())\n                    // `ws_encode_task` shouldn't terminate until\n                    // `encode_tx` is dropped, except by panicking.\n                    .expect(\"encode task panicked\");\n            },\n\n        }\n\n        if let Err(e) = ws.flush().await {\n            log::warn!(\"error flushing websocket: {e}\");\n            break;\n        }\n    }\n}\n\n#[derive(From)]\nenum OutboundWsMessage {\n    Error(MessageExecutionError),\n    Message(OutboundMessage),\n}\n\n/// Task that reads [`OutboundWsMessage`]s from `messages`, encodes them via\n/// [`ws_encode_message`], and sends the resuling [`Frame`]s to `outgoing_frames`.\n///\n/// Meant to be [`tokio::spawn`]ed.\n///\n/// The function also takes care of reusing serialization buffers and reporting\n/// metrics via [`SendMetrics`]..\nasync fn ws_encode_task(\n    metrics: SendMetrics,\n    config: ClientConfig,\n    mut messages: mpsc::UnboundedReceiver<OutboundWsMessage>,\n    outgoing_frames: mpsc::UnboundedSender<Frame>,\n    bsatn_rlb_pool: BsatnRowListBuilderPool,\n) {\n    // Serialize buffers can be reclaimed once all frames of a message are\n    // copied to the wire. Since we don't know when that will happen, we prepare\n    // for a few messages to be in-flight, i.e. encoded but not yet sent.\n    const BUF_POOL_CAPACITY: usize = 16;\n    let buf_pool = ArrayQueue::new(BUF_POOL_CAPACITY);\n    let mut in_use_bufs: Vec<ScopeGuard<InUseSerializeBuffer, _>> = Vec::with_capacity(BUF_POOL_CAPACITY);\n\n    while let Some(message) = messages.recv().await {\n        // Drop serialize buffers with no external referent,\n        // returning them to the pool.\n        in_use_bufs.retain(|in_use| !in_use.is_unique());\n        // Get a serialize buffer from the pool,\n        // or create a fresh one.\n        let buf = buf_pool.pop().unwrap_or_else(|| SerializeBuffer::new(config));\n\n        let in_use_buf = match message {\n            OutboundWsMessage::Error(message) => {\n                if config.version == WsVersion::V2 {\n                    log::error!(\"dropping v1 error message sent to a v2 client: {:?}\", message);\n                    continue;\n                }\n                let (stats, in_use, mut frames) = ws_encode_message(config, buf, message, false, &bsatn_rlb_pool).await;\n                metrics.report(None, None, stats);\n                if frames.try_for_each(|frame| outgoing_frames.send(frame)).is_err() {\n                    break;\n                }\n\n                in_use\n            }\n            OutboundWsMessage::Message(message) => {\n                let workload = message.workload();\n                let num_rows = message.num_rows();\n                match message {\n                    OutboundMessage::V2(server_message) => {\n                        if config.version != WsVersion::V2 {\n                            log::error!(\"dropping v2 message on v1 connection\");\n                            continue;\n                        }\n\n                        let (stats, in_use, mut frames) =\n                            ws_encode_message_v2(config, buf, server_message, false, &bsatn_rlb_pool).await;\n                        metrics.report(workload, num_rows, stats);\n                        if frames.try_for_each(|frame| outgoing_frames.send(frame)).is_err() {\n                            break;\n                        }\n\n                        in_use\n                    }\n                    OutboundMessage::V1(message) => {\n                        if config.version == WsVersion::V2 {\n                            log::error!(\n                                \"dropping v1 message for v2 connection until v2 serialization is implemented: {:?}\",\n                                message\n                            );\n                            continue;\n                        }\n\n                        let is_large = num_rows.is_some_and(|n| n > 1024);\n\n                        let (stats, in_use, mut frames) =\n                            ws_encode_message(config, buf, message, is_large, &bsatn_rlb_pool).await;\n                        metrics.report(workload, num_rows, stats);\n                        if frames.try_for_each(|frame| outgoing_frames.send(frame)).is_err() {\n                            break;\n                        }\n\n                        in_use\n                    }\n                }\n            }\n        };\n\n        if in_use_bufs.len() < BUF_POOL_CAPACITY {\n            in_use_bufs.push(scopeguard::guard(in_use_buf, |in_use| {\n                let buf = in_use.try_reclaim().expect(\"buffer should be unique\");\n                let _ = buf_pool.push(buf);\n            }));\n        }\n    }\n}\n\n/// Some stats about serialization and compression.\n///\n/// Returned by [`ws_encode_message`].\nstruct EncodeMetrics {\n    /// Time it took to serialize and (potentially) compress a message.\n    /// Does not include scheduling overhead.\n    timing: Duration,\n    /// Length in bytes of the serialized and (potentially) compressed message.\n    encoded_len: usize,\n}\n\n/// Encodes `message` into zero or more WebSocket [`Frame`]s.\n///\n/// The `message` is first [`serialize`]d. Depending on the serialized size,\n/// client `config` and format (see [`SwitchedServerMessage`]), compression may\n/// be applied to the serialized bytes.\n///\n/// If `is_large_message` is true, serialization and compression is performed\n/// on a `rayon` thread. The value should be chosen s.t. the overhead of\n/// scheduling is expected to be lower than the overhead of compression itself.\n///\n/// The resulting bytes are then split into [`Frame`]s of at most 4096 bytes\n/// of payload each, according to the rules laid out in [RFC6455], Section\n/// 5.4 Fragmentation.\n///\n/// Returns [`EncodeMetrics`], the [`InUseSerializeBuffer`] that was passed in\n/// as `buf` for later reuse, and the [`Frame`]s.\n///\n/// NOTE: When sending, the frames of a single message MUST NOT be interleaved\n/// with the frames of another message, except for control frames (`Close`,\n/// `Ping`, `Pong`).\n///\n/// [RFC6455]: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4\nasync fn ws_encode_message(\n    config: ClientConfig,\n    buf: SerializeBuffer,\n    message: impl ToProtocol<Encoded = SwitchedServerMessage> + Send + 'static,\n    is_large_message: bool,\n    bsatn_rlb_pool: &BsatnRowListBuilderPool,\n) -> (EncodeMetrics, InUseSerializeBuffer, impl Iterator<Item = Frame>) {\n    const FRAGMENT_SIZE: usize = 4096;\n\n    fn serialize_and_compress(\n        bsatn_rlb_pool: &BsatnRowListBuilderPool,\n        serialize_buf: SerializeBuffer,\n        message: impl ToProtocol<Encoded = SwitchedServerMessage> + Send + 'static,\n        config: ClientConfig,\n    ) -> (Duration, InUseSerializeBuffer, DataMessage) {\n        let start = Instant::now();\n        let (msg_alloc, msg_data) = serialize(bsatn_rlb_pool, serialize_buf, message, config);\n        (start.elapsed(), msg_alloc, msg_data)\n    }\n    let (timing, msg_alloc, msg_data) = if is_large_message {\n        let bsatn_rlb_pool = bsatn_rlb_pool.clone();\n        spawn_rayon(move || serialize_and_compress(&bsatn_rlb_pool, buf, message, config)).await\n    } else {\n        serialize_and_compress(bsatn_rlb_pool, buf, message, config)\n    };\n\n    let metrics = EncodeMetrics {\n        timing,\n        encoded_len: msg_data.len(),\n    };\n\n    let (data, ty) = match msg_data {\n        DataMessage::Text(text) => (bytestring_to_utf8bytes(text).into(), Data::Text),\n        DataMessage::Binary(bin) => (bin, Data::Binary),\n    };\n    let frames = fragment(data, ty, FRAGMENT_SIZE);\n\n    (metrics, msg_alloc, frames)\n}\n\n#[allow(dead_code, unused_variables)]\nasync fn ws_encode_message_v2(\n    config: ClientConfig,\n    buf: SerializeBuffer,\n    message: ws_v2::ServerMessage,\n    is_large_message: bool,\n    bsatn_rlb_pool: &BsatnRowListBuilderPool,\n) -> (EncodeMetrics, InUseSerializeBuffer, impl Iterator<Item = Frame> + use<>) {\n    let start = Instant::now();\n\n    let (in_use, data) = if is_large_message {\n        let bsatn_rlb_pool = bsatn_rlb_pool.clone();\n        spawn_rayon(move || serialize_v2(&bsatn_rlb_pool, buf, message, config.compression)).await\n    } else {\n        serialize_v2(bsatn_rlb_pool, buf, message, config.compression)\n    };\n\n    let metrics = EncodeMetrics {\n        timing: start.elapsed(),\n        encoded_len: data.len(),\n    };\n    let frames = fragment(data, Data::Binary, 4096);\n    (metrics, in_use, frames)\n}\n\n/// Split payload `data` of type `ty` into `fragment_size`d [`Frame`]s,\n/// according to the rules laid out in [RFC6455], Section 5.4.\n///\n/// [RFC6455]: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4\nfn fragment(data: Bytes, ty: Data, fragment_size: usize) -> impl Iterator<Item = Frame> {\n    let len = data.len();\n\n    (0..len).step_by(fragment_size).enumerate().map(move |(i, start)| {\n        let end = (start + fragment_size).min(len);\n        let chunk = data.slice(start..end);\n\n        let opcode = OpCode::Data(if i == 0 { ty } else { Data::Continue });\n        let is_final = end == len;\n\n        Frame::message(chunk, opcode, is_final)\n    })\n}\n\n#[derive(Debug)]\nenum ClientMessage {\n    Message(DataMessage),\n    Ping(Bytes),\n    Pong(Bytes),\n    Close(Option<CloseFrame>),\n}\n\nimpl ClientMessage {\n    fn from_message(msg: WsMessage) -> Self {\n        match msg {\n            WsMessage::Text(s) => Self::Message(DataMessage::Text(utf8bytes_to_bytestring(s))),\n            WsMessage::Binary(b) => Self::Message(DataMessage::Binary(b)),\n            WsMessage::Ping(b) => Self::Ping(b),\n            WsMessage::Pong(b) => Self::Pong(b),\n            WsMessage::Close(frame) => Self::Close(frame),\n            // WebSocket::read_message() never returns a raw Message::Frame\n            WsMessage::Frame(_) => unreachable!(),\n        }\n    }\n}\n\nstruct SendMetrics {\n    database: Identity,\n    encode_timing: Histogram,\n}\n\nimpl SendMetrics {\n    fn new(database: Identity) -> Self {\n        Self {\n            encode_timing: WORKER_METRICS.websocket_serialize_secs.with_label_values(&database),\n            database,\n        }\n    }\n\n    fn report(&self, workload: Option<WorkloadType>, num_rows: Option<usize>, encode: EncodeMetrics) {\n        self.encode_timing.observe(encode.timing.as_secs_f64());\n\n        // These metrics should be updated together,\n        // or not at all.\n        if let (Some(workload), Some(num_rows)) = (workload, num_rows) {\n            WORKER_METRICS\n                .websocket_sent_num_rows\n                .with_label_values(&self.database, &workload)\n                .observe(num_rows as f64);\n            WORKER_METRICS\n                .websocket_sent_msg_size\n                .with_label_values(&self.database, &workload)\n                .observe(encode.encoded_len as f64);\n        }\n    }\n}\n\nfn utf8bytes_to_bytestring(s: Utf8Bytes) -> ByteString {\n    // SAFETY: `Utf8Bytes` and `ByteString` have the same invariant of UTF-8 validity\n    unsafe { ByteString::from_bytes_unchecked(Bytes::from(s)) }\n}\nfn bytestring_to_utf8bytes(s: ByteString) -> Utf8Bytes {\n    // SAFETY: `Utf8Bytes` and `ByteString` have the same invariant of UTF-8 validity\n    unsafe { Utf8Bytes::from_bytes_unchecked(s.into_bytes()) }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{\n        future::{poll_fn, Future},\n        pin::Pin,\n        sync::atomic::AtomicUsize,\n        task::{Context, Poll},\n    };\n\n    use anyhow::anyhow;\n    use bytes::BytesMut;\n    use futures::{\n        future::{self, Either, FutureExt as _},\n        sink, stream,\n    };\n    use pretty_assertions::assert_matches;\n    use proptest::prelude::*;\n    use spacetimedb::client::{messages::SerializableMessage, ClientName, OutboundMessage};\n    use tokio::time::sleep;\n\n    use super::*;\n\n    // [NOTE: start_paused]:\n    //\n    // Some of the tests below test timeouts or rely on time in some other way.\n    // Since that is prone to flakiness (depending on machine load), we use\n    // [tokio::time::pause] to run those tests with paused time.\n    //\n    // Tokio will auto-advance time when [sleep] is used, and the executor has\n    // no other work to do, so this should work as expected: the elapsed time\n    // is the sum of the sleep time in the awaited future.\n    //\n    // Crucially, all timer-backed primitives must use [tokio::time::Instant]\n    // rather than [std::time::Instant]. In case a test becomes flaky again in\n    // the future, check for use of std `Instant` first.\n\n    fn dummy_client_id() -> ClientActorId {\n        ClientActorId {\n            identity: Identity::ZERO,\n            connection_id: ConnectionId::ZERO,\n            name: ClientName(0),\n        }\n    }\n\n    fn dummy_actor_state() -> ActorState {\n        dummy_actor_state_with_config(<_>::default())\n    }\n\n    fn dummy_actor_state_with_config(config: WebSocketOptions) -> ActorState {\n        ActorState::new(Identity::ZERO, dummy_client_id(), config)\n    }\n\n    #[tokio::test(start_paused = true)] // see [NOTE: start_paused]\n    async fn idle_timer_extends_sleep() {\n        let timeout = Duration::from_millis(10);\n\n        let start = Instant::now();\n        let (tx, rx) = watch::channel(start + timeout);\n        tokio::join!(ws_idle_timer(rx), async {\n            for _ in 0..5 {\n                sleep(Duration::from_millis(1)).await;\n                tx.send(Instant::now() + timeout).unwrap();\n            }\n        });\n        let elapsed = start.elapsed();\n        let expected = timeout + Duration::from_millis(5);\n        assert!(\n            elapsed >= expected,\n            \"{}ms elapsed, expected >= {}ms\",\n            elapsed.as_millis(),\n            expected.as_millis(),\n        );\n    }\n\n    #[tokio::test]\n    async fn recv_loop_terminates_when_input_exhausted() {\n        let state = Arc::new(dummy_actor_state());\n        let (idle_tx, _idle_rx) = watch::channel(Instant::now() + state.config.idle_timeout);\n\n        let input = stream::iter(vec![Ok(WsMessage::Ping(Bytes::new()))]);\n        pin_mut!(input);\n\n        let recv_loop = ws_recv_loop(state, idle_tx, input);\n        pin_mut!(recv_loop);\n\n        assert_matches!(recv_loop.next().await, Some(ClientMessage::Ping(_)));\n        assert_matches!(recv_loop.next().await, None);\n    }\n\n    #[tokio::test]\n    async fn recv_loop_terminates_when_input_yields_err() {\n        let state = Arc::new(dummy_actor_state());\n        let (idle_tx, _idle_rx) = watch::channel(Instant::now() + state.config.idle_timeout);\n\n        let input = stream::iter(vec![\n            Ok(WsMessage::Ping(Bytes::new())),\n            Err(WsError::ConnectionClosed),\n            Ok(WsMessage::Pong(Bytes::new())),\n        ]);\n        pin_mut!(input);\n\n        let recv_loop = ws_recv_loop(state, idle_tx, input);\n        pin_mut!(recv_loop);\n\n        assert_matches!(recv_loop.next().await, Some(ClientMessage::Ping(_)));\n        assert_matches!(recv_loop.next().await, None);\n    }\n\n    #[tokio::test]\n    async fn recv_loop_drains_remaining_messages_when_closed() {\n        let state = Arc::new(dummy_actor_state());\n        let (idle_tx, _idle_rx) = watch::channel(Instant::now() + state.config.idle_timeout);\n\n        let input = stream::iter(vec![\n            Ok(WsMessage::Ping(Bytes::new())),\n            Ok(WsMessage::Pong(Bytes::new())),\n        ]);\n        pin_mut!(input);\n        {\n            let recv_loop = ws_recv_loop(state.clone(), idle_tx, &mut input);\n            pin_mut!(recv_loop);\n\n            state.close();\n            assert_matches!(recv_loop.next().await, None);\n        }\n        assert_matches!(input.next().await, None);\n    }\n\n    #[tokio::test]\n    async fn recv_loop_stops_at_error_while_draining() {\n        let state = Arc::new(dummy_actor_state());\n        let (idle_tx, _idle_rx) = watch::channel(Instant::now() + state.config.idle_timeout);\n\n        let input = stream::iter(vec![\n            Ok(WsMessage::Ping(Bytes::new())),\n            Err(WsError::ConnectionClosed),\n            Ok(WsMessage::Pong(Bytes::new())),\n        ]);\n        pin_mut!(input);\n        {\n            let recv_loop = ws_recv_loop(state.clone(), idle_tx, &mut input);\n            pin_mut!(recv_loop);\n\n            state.close();\n            assert_matches!(recv_loop.next().await, None);\n        }\n        assert_matches!(input.next().await, Some(Ok(WsMessage::Pong(_))));\n    }\n\n    #[tokio::test]\n    async fn recv_loop_updates_idle_channel() {\n        let state = Arc::new(dummy_actor_state());\n        let idle_deadline = Instant::now() + state.config.idle_timeout;\n        let (idle_tx, mut idle_rx) = watch::channel(idle_deadline);\n\n        let input = stream::iter(vec![\n            Ok(WsMessage::Ping(Bytes::new())),\n            Ok(WsMessage::Pong(Bytes::new())),\n        ]);\n        let recv_loop = ws_recv_loop(state, idle_tx, input);\n        pin_mut!(recv_loop);\n\n        let mut new_idle_deadline = *idle_rx.borrow();\n        while let Some(message) = recv_loop.next().await {\n            drop(message);\n            assert!(idle_rx.has_changed().unwrap());\n            new_idle_deadline = *idle_rx.borrow_and_update();\n        }\n        assert!(new_idle_deadline > idle_deadline);\n    }\n\n    #[tokio::test]\n    async fn client_message_handler_terminates_when_input_exhausted() {\n        let state = Arc::new(dummy_actor_state());\n        let metric = IntGauge::new(\"bleep\", \"unhelpful\").unwrap();\n\n        let input = stream::iter(vec![\n            ClientMessage::Ping(Bytes::new()),\n            ClientMessage::Message(DataMessage::from(\"hello\".to_owned())),\n        ]);\n        let handler = ws_client_message_handler(state, metric, input);\n        pin_mut!(handler);\n\n        assert_matches!(\n            handler.next().await,\n            Some((DataMessage::Text(data), _instant)) if data == \"hello\"\n        );\n        assert_matches!(handler.next().await, None);\n    }\n\n    #[tokio::test]\n    async fn client_message_handler_updates_pong_and_closed_states_and_metric() {\n        let state = Arc::new(dummy_actor_state());\n        state.reset_ponged();\n        let metric = IntGauge::new(\"bleep\", \"unhelpful\").unwrap();\n\n        let input = stream::iter(vec![ClientMessage::Pong(Bytes::new()), ClientMessage::Close(None)]);\n        let handler = ws_client_message_handler(state.clone(), metric.clone(), input);\n        handler.map(drop).for_each(future::ready).await;\n\n        assert!(state.closed());\n        assert!(state.reset_ponged());\n        assert_eq!(metric.get(), 1);\n    }\n\n    #[tokio::test]\n    async fn send_loop_terminates_when_unordered_closed() {\n        let state = Arc::new(dummy_actor_state());\n        let (messages_tx, messages_rx) = mpsc::channel(64);\n        let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n        let send_loop = ws_send_loop(\n            state,\n            ClientConfig::for_test(),\n            sink::drain(),\n            messages_rx,\n            unordered_rx,\n            BsatnRowListBuilderPool::new(),\n        );\n        pin_mut!(send_loop);\n\n        assert!(is_pending(&mut send_loop).await);\n        drop(messages_tx);\n        assert!(is_pending(&mut send_loop).await);\n\n        drop(unordered_tx);\n        send_loop.await;\n    }\n\n    #[tokio::test]\n    async fn send_loop_close_message_closes_state_and_messages() {\n        let state = Arc::new(dummy_actor_state());\n        let (messages_tx, messages_rx) = mpsc::channel(64);\n        let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n        let send_loop = ws_send_loop(\n            state.clone(),\n            ClientConfig::for_test(),\n            sink::drain(),\n            messages_rx,\n            unordered_rx,\n            BsatnRowListBuilderPool::new(),\n        );\n        pin_mut!(send_loop);\n\n        unordered_tx\n            .send(UnorderedWsMessage::Close(CloseFrame {\n                code: CloseCode::Away,\n                reason: \"done\".into(),\n            }))\n            .unwrap();\n\n        assert!(is_pending(&mut send_loop).await);\n        assert!(state.closed());\n        assert!(messages_tx.is_closed());\n    }\n\n    #[tokio::test]\n    async fn send_loop_terminates_if_sink_cant_be_fed() {\n        let input = [\n            Either::Left(UnorderedWsMessage::Close(CloseFrame {\n                code: CloseCode::Away,\n                reason: \"bah!\".into(),\n            })),\n            Either::Left(UnorderedWsMessage::Ping(Bytes::new())),\n            Either::Left(UnorderedWsMessage::Error(MessageExecutionError {\n                reducer: None,\n                reducer_id: None,\n                caller_identity: Identity::ZERO,\n                caller_connection_id: None,\n                err: anyhow!(\"it did not work\"),\n            })),\n            // TODO: This is the easiest to construct,\n            // but maybe we want other variants, too.\n            Either::Right(OutboundMessage::V1(SerializableMessage::Identity(\n                IdentityTokenMessage {\n                    identity: Identity::ZERO,\n                    token: \"macaron\".into(),\n                    connection_id: ConnectionId::ZERO,\n                },\n            ))),\n        ];\n\n        for message in input {\n            let state = Arc::new(dummy_actor_state());\n            let (messages_tx, messages_rx) = mpsc::channel(64);\n            let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n            let send_loop = ws_send_loop(\n                state.clone(),\n                ClientConfig::for_test(),\n                UnfeedableSink,\n                messages_rx,\n                unordered_rx,\n                BsatnRowListBuilderPool::new(),\n            );\n            pin_mut!(send_loop);\n\n            match message {\n                Either::Left(unordered) => unordered_tx.send(unordered).unwrap(),\n                Either::Right(message) => messages_tx.send(message).await.unwrap(),\n            }\n            send_loop.await;\n        }\n    }\n\n    #[tokio::test]\n    async fn send_loop_terminates_if_sink_cant_be_flushed() {\n        let input = [\n            Either::Left(UnorderedWsMessage::Close(CloseFrame {\n                code: CloseCode::Away,\n                reason: \"bah!\".into(),\n            })),\n            Either::Left(UnorderedWsMessage::Ping(Bytes::new())),\n            Either::Left(UnorderedWsMessage::Error(MessageExecutionError {\n                reducer: None,\n                reducer_id: None,\n                caller_identity: Identity::ZERO,\n                caller_connection_id: None,\n                err: anyhow!(\"it did not work\"),\n            })),\n            // TODO: This is the easiest to construct,\n            // but maybe we want other variants, too.\n            Either::Right(OutboundMessage::V1(SerializableMessage::Identity(\n                IdentityTokenMessage {\n                    identity: Identity::ZERO,\n                    token: \"macaron\".into(),\n                    connection_id: ConnectionId::ZERO,\n                },\n            ))),\n        ];\n\n        for message in input {\n            let state = Arc::new(dummy_actor_state());\n            let (messages_tx, messages_rx) = mpsc::channel(64);\n            let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n            let send_loop = ws_send_loop(\n                state.clone(),\n                ClientConfig::for_test(),\n                UnflushableSink,\n                messages_rx,\n                unordered_rx,\n                BsatnRowListBuilderPool::new(),\n            );\n            pin_mut!(send_loop);\n\n            match message {\n                Either::Left(unordered) => unordered_tx.send(unordered).unwrap(),\n                Either::Right(message) => messages_tx.send(message).await.unwrap(),\n            }\n            send_loop.await;\n        }\n    }\n\n    #[tokio::test]\n    async fn main_loop_terminates_if_either_send_or_recv_terminates() {\n        let state = Arc::new(dummy_actor_state());\n        ws_main_loop(\n            state.clone(),\n            future::pending,\n            future::pending(),\n            tokio::spawn(sleep(Duration::from_millis(10))),\n            tokio::spawn(future::pending()),\n            drop,\n        )\n        .await;\n        ws_main_loop(\n            state,\n            future::pending,\n            future::pending(),\n            tokio::spawn(future::pending()),\n            tokio::spawn(sleep(Duration::from_millis(10))),\n            drop,\n        )\n        .await;\n    }\n\n    #[tokio::test(start_paused = true)] // see [NOTE: start_paused]\n    async fn main_loop_terminates_on_idle_timeout() {\n        let state = Arc::new(dummy_actor_state_with_config(WebSocketOptions {\n            idle_timeout: Duration::from_millis(10),\n            ..<_>::default()\n        }));\n        let (idle_tx, idle_rx) = watch::channel(state.next_idle_deadline());\n\n        let start = Instant::now();\n        let mut t = tokio::spawn({\n            let state = state.clone();\n            async move {\n                ws_main_loop(\n                    state,\n                    future::pending,\n                    ws_idle_timer(idle_rx),\n                    tokio::spawn(future::pending()),\n                    tokio::spawn(future::pending()),\n                    drop,\n                )\n                .await\n            }\n        });\n\n        let loop_start = Instant::now();\n        for _ in 0..5 {\n            sleep(Duration::from_millis(5)).await;\n            idle_tx.send(state.next_idle_deadline()).unwrap();\n            assert!(is_pending(&mut t).await);\n        }\n        let timeout = loop_start.elapsed() + Duration::from_millis(10);\n\n        t.await.unwrap();\n        let elapsed = start.elapsed();\n        assert!(elapsed >= timeout);\n        assert!(elapsed < timeout + Duration::from_millis(10));\n    }\n\n    #[tokio::test(start_paused = true)] // see [NOTE: start_paused]\n    async fn main_loop_keepalive_keeps_alive() {\n        let state = Arc::new(dummy_actor_state_with_config(WebSocketOptions {\n            ping_interval: Duration::from_millis(5),\n            idle_timeout: Duration::from_millis(10),\n            ..<_>::default()\n        }));\n        let (idle_tx, idle_rx) = watch::channel(state.next_idle_deadline());\n        // Pretend we received a pong immediately after sending a ping,\n        // but only five times.\n        let unordered_tx = {\n            let state = state.clone();\n            let pings = AtomicUsize::new(0);\n            move |m| {\n                if let UnorderedWsMessage::Ping(_) = m {\n                    let n = pings.fetch_add(1, Ordering::Relaxed);\n                    if n < 5 {\n                        state.set_ponged();\n                        idle_tx.send(state.next_idle_deadline()).ok();\n                    }\n                }\n            }\n        };\n\n        let start = Instant::now();\n        let t = tokio::spawn({\n            let state = state.clone();\n            async move {\n                ws_main_loop(\n                    state,\n                    future::pending,\n                    ws_idle_timer(idle_rx),\n                    tokio::spawn(future::pending()),\n                    tokio::spawn(future::pending()),\n                    unordered_tx,\n                )\n                .await\n            }\n        });\n\n        let expected_timeout = (5 * state.config.ping_interval) + state.config.idle_timeout;\n        let res = timeout(expected_timeout, t).await;\n        let elapsed = start.elapsed();\n\n        // It didn't time out.\n        assert_matches!(res, Ok(Ok(())));\n        // It didn't exit early. Allow it to miss a ping.\n        let expected_timeout = expected_timeout - state.config.ping_interval;\n        assert!(\n            elapsed >= expected_timeout,\n            \"should not exit early: elapsed={} expected_timeout={}\",\n            elapsed.as_millis(),\n            expected_timeout.as_millis()\n        );\n    }\n\n    #[tokio::test(start_paused = true)] // see [NOTE: start_paused]\n    async fn main_loop_terminates_when_module_exits() {\n        let state = Arc::new(dummy_actor_state());\n\n        let (_idle_tx, idle_rx) = watch::channel(state.next_idle_deadline());\n        let unordered_tx = {\n            let state = state.clone();\n            move |m| {\n                if let UnorderedWsMessage::Close(_) = m {\n                    state.close();\n                }\n            }\n        };\n\n        let start = tokio::time::Instant::now();\n        tokio::spawn(async move {\n            let hotswap = || async {\n                sleep(Duration::from_millis(5)).await;\n                Err(NoSuchModule)\n            };\n\n            ws_main_loop(\n                state.clone(),\n                hotswap,\n                ws_idle_timer(idle_rx),\n                // Pretend we received a close immediately after sending one.\n                tokio::spawn(async move {\n                    loop {\n                        if state.closed() {\n                            break;\n                        }\n                        sleep(Duration::from_millis(1)).await\n                    }\n                }),\n                tokio::spawn(future::pending()),\n                unordered_tx,\n            )\n            .await\n        })\n        .await\n        .unwrap();\n        let elapsed = start.elapsed();\n\n        assert!(\n            elapsed >= Duration::from_millis(5),\n            \"main loop should run until module is shut down\"\n        );\n        assert!(\n            elapsed < Duration::from_millis(10),\n            \"main loop should shut down shortly after module is shut down\"\n        );\n    }\n\n    #[tokio::test]\n    async fn recv_queue_sends_close_when_at_capacity() {\n        let state = Arc::new(dummy_actor_state_with_config(WebSocketOptions {\n            incoming_queue_length: 10.try_into().unwrap(),\n            ..<_>::default()\n        }));\n\n        let (unordered_tx, mut unordered_rx) = mpsc::unbounded_channel();\n        let input = stream::iter((0..20).map(|i| Ok(WsMessage::text(format!(\"message {i}\")))));\n\n        let metric = IntGauge::new(\"bleep\", \"unhelpful\").unwrap();\n        let received = ws_recv_queue(state, unordered_tx, metric.clone(), input)\n            .collect::<Vec<_>>()\n            .await;\n\n        assert_matches!(unordered_rx.recv().await, Some(UnorderedWsMessage::Close(_)));\n        // Queue length metric should be zero\n        assert_eq!(metric.get(), 0);\n        // Should have received all of the input.\n        assert_eq!(received.len(), 20);\n    }\n\n    #[tokio::test]\n    async fn recv_queue_closes_state_if_sender_gone() {\n        let state = Arc::new(dummy_actor_state_with_config(WebSocketOptions {\n            incoming_queue_length: 10.try_into().unwrap(),\n            ..<_>::default()\n        }));\n\n        let (unordered_tx, _) = mpsc::unbounded_channel();\n        let input = stream::iter((0..20).map(|i| Ok(WsMessage::text(format!(\"message {i}\")))));\n\n        let metric = IntGauge::new(\"bleep\", \"unhelpful\").unwrap();\n        let received = ws_recv_queue(state.clone(), unordered_tx, metric.clone(), input)\n            .collect::<Vec<_>>()\n            .await;\n\n        assert!(state.closed());\n        // Queue length metric should be zero\n        assert_eq!(metric.get(), 0);\n        // Should have received up to capacity.\n        assert_eq!(received.len(), 10);\n    }\n\n    #[tokio::test]\n    async fn send_loop_interleaves_pings_with_frames() {\n        let state = Arc::new(dummy_actor_state());\n        let mut received = Vec::new();\n        let (messages_tx, messages_rx) = mpsc::channel(1);\n        let (unordered_tx, unordered_rx) = mpsc::unbounded_channel();\n\n        #[derive(From)]\n        enum OutgoingBytes {\n            #[allow(unused)]\n            Error(MessageExecutionError),\n            Bytes(Bytes),\n        }\n\n        async fn encoder(mut rx: mpsc::UnboundedReceiver<OutgoingBytes>, tx: mpsc::UnboundedSender<Frame>) {\n            while let Some(data) = rx.recv().await {\n                if let OutgoingBytes::Bytes(data) = data {\n                    let frames = fragment(data, Data::Binary, 4096);\n                    for frame in frames {\n                        tx.send(frame).unwrap();\n                    }\n                }\n            }\n        }\n\n        const MESSAGE_SIZE: usize = 10 * 1024 * 1024;\n        const FRAME_SIZE: usize = 4096;\n        const NUM_CONTROL_FRAMES: usize = 2;\n\n        let send_loop = tokio::spawn(async move {\n            ws_send_loop_inner(state, &mut received, messages_rx, unordered_rx, encoder).await;\n            received\n        });\n        messages_tx.send(Bytes::from_static(&[1; MESSAGE_SIZE])).await.unwrap();\n        // Yield task to give the send loop a chance to receive the message.\n        tokio::task::yield_now().await;\n        // Send ping, then close.\n        unordered_tx.send(UnorderedWsMessage::Ping(Bytes::new())).unwrap();\n        unordered_tx\n            .send(UnorderedWsMessage::Close(CloseFrame {\n                code: CloseCode::Away,\n                reason: \"we're done\".into(),\n            }))\n            .unwrap();\n\n        // Shut down the loop.\n        drop(messages_tx);\n        drop(unordered_tx);\n        let received = send_loop.await.unwrap();\n\n        let ping_pos = received\n            .iter()\n            .position(|message| matches!(message, WsMessage::Ping(_)))\n            .unwrap();\n        log::info!(\"received={} ping-at={}\", received.len(), ping_pos);\n        assert!(ping_pos > 0);\n        assert!(ping_pos < received.len() - NUM_CONTROL_FRAMES);\n        // All frames of the message should have been sent before the close frame.\n        assert_eq!(received.len(), (MESSAGE_SIZE / FRAME_SIZE) + NUM_CONTROL_FRAMES);\n        assert!(received\n            .last()\n            .is_some_and(|message| matches!(message, WsMessage::Close(_))));\n    }\n\n    #[test]\n    fn fragment_yields_no_frames_if_input_is_empty() {\n        assert!(fragment(Bytes::new(), Data::Binary, 4096)\n            .collect::<Vec<_>>()\n            .is_empty());\n    }\n\n    const MAX_DATA_SIZE: usize = 1024 * 1024;\n    const MAX_FRAME_SIZE: usize = 1024;\n\n    proptest! {\n        #[test]\n        fn fragment_input_can_be_reconstructed_from_output(\n            input in prop::collection::vec(any::<u8>(), 1..MAX_DATA_SIZE),\n            fragment_size in 1..MAX_FRAME_SIZE,\n        ) {\n            let data = Bytes::from(input);\n            let mut payloads = BytesMut::new();\n            for frame in fragment(data.clone(), Data::Binary, fragment_size) {\n                payloads.extend(Some(frame.into_payload()));\n            }\n            prop_assert_eq!(data, payloads.freeze());\n        }\n\n        #[test]\n        fn fragment_all_frames_except_last_do_not_have_the_fin_bit(\n            input in prop::collection::vec(any::<u8>(), 1..MAX_DATA_SIZE),\n            fragment_size in 1..MAX_FRAME_SIZE,\n        ) {\n            let mut frames = fragment(Bytes::from(input), Data::Binary, fragment_size).collect::<Vec<_>>();\n            prop_assert!(frames.pop().unwrap().header().is_final);\n            prop_assert!(frames.into_iter().all(|frame| !frame.header().is_final));\n        }\n\n        #[test]\n        fn fragment_first_frame_has_original_opcode_rest_are_continue(\n            input in prop::collection::vec(any::<u8>(), 1..MAX_DATA_SIZE),\n            fragment_size in 1..MAX_FRAME_SIZE,\n            ty in Just(Data::Text).prop_union(Just(Data::Binary)),\n        ) {\n            let mut frames = fragment(Bytes::from(input), ty, fragment_size);\n            prop_assert_eq!(frames.next().unwrap().header().opcode, OpCode::Data(ty));\n            for frame in frames {\n                prop_assert_eq!(frame.header().opcode, OpCode::Data(Data::Continue));\n            }\n        }\n\n        #[test]\n        fn fragment_produces_expected_number_of_equal_sized_frames(\n            input in prop::collection::vec(any::<u8>(), 1..MAX_DATA_SIZE),\n            fragment_size in 1..MAX_FRAME_SIZE,\n        ) {\n            let input = Bytes::from(input);\n            let mut frames = fragment(input.clone(), Data::Binary, fragment_size).collect::<Vec<_>>();\n            prop_assert_eq!(frames.len(), input.len().div_ceil(fragment_size));\n            prop_assert!(frames.pop().unwrap().payload().len() <= fragment_size);\n            prop_assert!(frames.iter().all(|frame| frame.payload().len() == fragment_size));\n        }\n    }\n\n    async fn is_pending(fut: &mut (impl Future + Unpin)) -> bool {\n        poll_fn(|cx| Poll::Ready(fut.poll_unpin(cx).is_pending())).await\n    }\n\n    struct UnfeedableSink;\n\n    impl<T> Sink<T> for UnfeedableSink {\n        type Error = &'static str;\n\n        fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n\n        fn start_send(self: Pin<&mut Self>, _: T) -> Result<(), Self::Error> {\n            Err(\"don't feed the sink\")\n        }\n\n        fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n\n        fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n    }\n\n    struct UnflushableSink;\n\n    impl<T> Sink<T> for UnflushableSink {\n        type Error = &'static str;\n\n        fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n\n        fn start_send(self: Pin<&mut Self>, _: T) -> Result<(), Self::Error> {\n            Ok(())\n        }\n\n        fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Err(\"don't flush the sink\"))\n        }\n\n        fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n            Poll::Ready(Ok(()))\n        }\n    }\n\n    #[test]\n    fn options_toml_roundtrip() {\n        let options = WebSocketOptions::default();\n        let toml = toml::to_string(&options).unwrap();\n        assert_eq!(options, toml::from_str::<WebSocketOptions>(&toml).unwrap());\n    }\n\n    #[test]\n    fn confirmed_reads_default_depends_on_ws_version() {\n        assert!(resolve_confirmed_reads_default(WsVersion::V2, None));\n        assert!(!resolve_confirmed_reads_default(WsVersion::V1, None));\n        assert!(resolve_confirmed_reads_default(WsVersion::V1, Some(true)));\n        assert!(!resolve_confirmed_reads_default(WsVersion::V2, Some(false)));\n    }\n\n    #[test]\n    fn options_from_partial_toml() {\n        let toml = r#\"\n            ping-interval = \"53s\"\n            idle-timeout = \"1m 3s\"\n\"#;\n\n        let expected = WebSocketOptions {\n            ping_interval: Duration::from_secs(53),\n            idle_timeout: Duration::from_secs(63),\n            ..<_>::default()\n        };\n\n        assert_eq!(expected, toml::from_str(toml).unwrap());\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/util/flat_csv.rs",
    "content": "//! copied from headers::util::flat_csv, as it's not public API\n\n// Copyright (c) 2014-2019 Sean McArthur\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n#![allow(clippy::all)]\n\nuse std::marker::PhantomData;\n\nuse bytes::BytesMut;\nuse http::HeaderValue;\n\n// A single `HeaderValue` that can flatten multiple values with commas.\n#[derive(Clone, PartialEq, Eq, Hash)]\npub(crate) struct FlatCsv<Sep = Comma> {\n    pub(crate) value: HeaderValue,\n    _marker: PhantomData<Sep>,\n}\n\npub(crate) trait Separator {\n    const BYTE: u8;\n    const CHAR: char;\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub(crate) enum Comma {}\n\nimpl Separator for Comma {\n    const BYTE: u8 = b',';\n    const CHAR: char = ',';\n}\n\n#[allow(unused)]\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub(crate) enum SemiColon {}\n\nimpl Separator for SemiColon {\n    const BYTE: u8 = b';';\n    const CHAR: char = ';';\n}\n\nimpl<Sep: Separator> FlatCsv<Sep> {\n    pub(crate) fn iter(&self) -> impl Iterator<Item = &str> {\n        self.value.to_str().ok().into_iter().flat_map(|value_str| {\n            let mut in_quotes = false;\n            value_str\n                .split(move |c| {\n                    if in_quotes {\n                        if c == '\"' {\n                            in_quotes = false;\n                        }\n                        false // dont split\n                    } else {\n                        if c == Sep::CHAR {\n                            true // split\n                        } else {\n                            if c == '\"' {\n                                in_quotes = true;\n                            }\n                            false // dont split\n                        }\n                    }\n                })\n                .map(|item| item.trim())\n        })\n    }\n}\n\nimpl<Sep> From<HeaderValue> for FlatCsv<Sep> {\n    fn from(value: HeaderValue) -> Self {\n        FlatCsv {\n            value,\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<'a, Sep: Separator> FromIterator<&'a HeaderValue> for FlatCsv<Sep> {\n    fn from_iter<I>(iter: I) -> Self\n    where\n        I: IntoIterator<Item = &'a HeaderValue>,\n    {\n        let mut values = iter.into_iter();\n\n        // Common case is there is only 1 value, optimize for that\n        if let (1, Some(1)) = values.size_hint() {\n            return values.next().expect(\"size_hint claimed 1 item\").clone().into();\n        }\n\n        // Otherwise, there are multiple, so this should merge them into 1.\n        let mut buf = values\n            .next()\n            .cloned()\n            .map(|val| BytesMut::from(val.as_bytes()))\n            .unwrap_or_else(|| BytesMut::new());\n\n        for val in values {\n            buf.extend_from_slice(&[Sep::BYTE, b' ']);\n            buf.extend_from_slice(val.as_bytes());\n        }\n\n        let val = HeaderValue::from_maybe_shared(buf.freeze()).expect(\"comma separated HeaderValues are valid\");\n\n        val.into()\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/util/serde.rs",
    "content": "/// Ser/De of [`std::time::Duration`] via the `humantime` crate.\n///\n/// Suitable for use with the `#[serde(with)]` annotation.\npub(crate) mod humantime_duration {\n    use std::time::Duration;\n\n    use ::serde::{Deserialize as _, Deserializer, Serialize as _, Serializer};\n\n    pub fn serialize<S: Serializer>(duration: &Duration, ser: S) -> Result<S::Ok, S::Error> {\n        humantime::format_duration(*duration).to_string().serialize(ser)\n    }\n\n    pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Duration, D::Error> {\n        // TODO: `toml` chokes if we try to derserialize to `&str` here.\n        let s = String::deserialize(de)?;\n        humantime::parse_duration(&s).map_err(serde::de::Error::custom)\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/util/websocket.rs",
    "content": "//! A more flexible version of axum::extract::ws. This could probably get pulled out into its own crate at some point.\n\nuse axum::extract::FromRequestParts;\nuse axum::response::{IntoResponse, Response};\nuse axum_extra::TypedHeader;\nuse headers::{Connection, HeaderMapExt, SecWebsocketAccept, SecWebsocketKey, SecWebsocketVersion, Upgrade};\nuse http::{HeaderName, HeaderValue, Method, StatusCode};\nuse hyper::upgrade::{OnUpgrade, Upgraded};\nuse hyper_util::rt::TokioIo;\n\nuse super::flat_csv::FlatCsv;\n\npub use tokio_tungstenite::tungstenite;\npub use tungstenite::{\n    error::Error as WsError,\n    protocol::{frame::coding::CloseCode, CloseFrame, Message, WebSocketConfig},\n};\n\npub type WebSocketStream = tokio_tungstenite::WebSocketStream<TokioIo<Upgraded>>;\n\npub struct RequestSecWebsocketProtocol(FlatCsv);\n\nimpl headers::Header for RequestSecWebsocketProtocol {\n    fn name() -> &'static HeaderName {\n        &http::header::SEC_WEBSOCKET_PROTOCOL\n    }\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, headers::Error> {\n        Ok(Self(values.collect()))\n    }\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([self.0.value.clone()])\n    }\n}\n\nimpl RequestSecWebsocketProtocol {\n    pub fn iter(&self) -> impl Iterator<Item = &str> {\n        self.0.iter()\n    }\n\n    pub fn select<S, P>(&self, protocols: impl IntoIterator<Item = (S, P)>) -> Option<(ResponseSecWebsocketProtocol, P)>\n    where\n        S: for<'a> PartialEq<&'a str> + TryInto<HeaderValue>,\n    {\n        protocols\n            .into_iter()\n            .find(|(protoname, _)| self.iter().any(|x| *protoname == x))\n            .map(|(protoname, proto)| {\n                let proto_header = protoname.try_into().unwrap_or_else(|_| unreachable!());\n                (ResponseSecWebsocketProtocol(proto_header), proto)\n            })\n    }\n}\n\npub struct ResponseSecWebsocketProtocol(pub HeaderValue);\n\nimpl headers::Header for ResponseSecWebsocketProtocol {\n    fn name() -> &'static HeaderName {\n        &http::header::SEC_WEBSOCKET_PROTOCOL\n    }\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, headers::Error> {\n        values.next().cloned().map(Self).ok_or_else(headers::Error::invalid)\n    }\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([self.0.clone()])\n    }\n}\n\npub struct WebSocketUpgrade {\n    key: SecWebsocketKey,\n    requested_protocol: Option<RequestSecWebsocketProtocol>,\n    upgrade: OnUpgrade,\n}\n\npub enum WebSocketUpgradeRejection {\n    MethodNotGet,\n    BadUpgrade,\n    BadVersion,\n    KeyMissing,\n}\n\n#[async_trait::async_trait]\nimpl<S> FromRequestParts<S> for WebSocketUpgrade {\n    type Rejection = WebSocketUpgradeRejection;\n    async fn from_request_parts(parts: &mut http::request::Parts, _state: &S) -> Result<Self, Self::Rejection> {\n        use WebSocketUpgradeRejection::*;\n\n        if parts.method != Method::GET {\n            return Err(MethodNotGet);\n        }\n\n        let upgrade = parts\n            .extensions\n            .remove::<OnUpgrade>()\n            .filter(|_| {\n                parts\n                    .headers\n                    .typed_get::<Connection>()\n                    .is_some_and(|conn| conn.contains(\"upgrade\"))\n                    && parts.headers.typed_get::<Upgrade>() == Some(Upgrade::websocket())\n            })\n            .ok_or(BadUpgrade)?;\n\n        if parts.headers.typed_get::<SecWebsocketVersion>() != Some(SecWebsocketVersion::V13) {\n            return Err(BadVersion);\n        }\n\n        let key = parts.headers.typed_get::<SecWebsocketKey>().ok_or(KeyMissing)?;\n\n        let requested_protocol = parts.headers.typed_get::<RequestSecWebsocketProtocol>();\n\n        Ok(WebSocketUpgrade {\n            key,\n            requested_protocol,\n            upgrade,\n        })\n    }\n}\n\nimpl IntoResponse for WebSocketUpgradeRejection {\n    fn into_response(self) -> Response {\n        match self {\n            Self::MethodNotGet => (StatusCode::METHOD_NOT_ALLOWED, \"Request method must be `GET`\").into_response(),\n            Self::BadUpgrade => (\n                StatusCode::UPGRADE_REQUIRED,\n                TypedHeader(Connection::upgrade()),\n                TypedHeader(Upgrade::websocket()),\n                \"This service requires use of the websocket protocol\",\n            )\n                .into_response(),\n            Self::BadVersion => (\n                StatusCode::BAD_REQUEST,\n                \"`Sec-WebSocket-Version` header did not include '13'\",\n            )\n                .into_response(),\n            Self::KeyMissing => (StatusCode::BAD_REQUEST, \"`Sec-WebSocket-Key` header missing\").into_response(),\n        }\n    }\n}\n\nimpl WebSocketUpgrade {\n    #[inline]\n    pub fn protocol(&self) -> Option<&RequestSecWebsocketProtocol> {\n        self.requested_protocol.as_ref()\n    }\n\n    /// Select a subprotocol from the ones provided, and prepare a response for the client.\n    pub fn select_protocol<S, P>(\n        self,\n        protocols: impl IntoIterator<Item = (S, P)>,\n    ) -> (WebSocketResponse, PendingWebSocket, Option<P>)\n    where\n        S: for<'a> PartialEq<&'a str> + TryInto<HeaderValue>,\n    {\n        let (proto_header, proto) = self\n            .requested_protocol\n            .as_ref()\n            .and_then(|proto| proto.select(protocols))\n            .unzip();\n        let (resp, ws) = self.into_response(proto_header);\n        (resp, ws, proto)\n    }\n\n    /// Prepare a response with no subprotocol selected.\n    #[inline]\n    pub fn ignore_protocol(self) -> (WebSocketResponse, PendingWebSocket) {\n        self.into_response(None)\n    }\n\n    /// Prepare a response with the given subprotocol.\n    #[inline]\n    pub fn into_response(\n        self,\n        protocol: Option<ResponseSecWebsocketProtocol>,\n    ) -> (WebSocketResponse, PendingWebSocket) {\n        let resp = WebSocketResponse {\n            accept: self.key.into(),\n            protocol,\n        };\n        (resp, PendingWebSocket(self.upgrade))\n    }\n}\n\npub struct PendingWebSocket(OnUpgrade);\n\nimpl PendingWebSocket {\n    #[inline]\n    pub async fn upgrade(self, config: WebSocketConfig) -> hyper::Result<WebSocketStream> {\n        let stream = TokioIo::new(self.0.await?);\n        Ok(WebSocketStream::from_raw_socket(stream, tungstenite::protocol::Role::Server, Some(config)).await)\n    }\n\n    #[inline]\n    pub fn into_inner(self) -> OnUpgrade {\n        self.0\n    }\n}\n\n/// An type representing an http response for a successful websocket upgrade. Note that this response\n/// must be returned to the client for [`PendingWebSocket::upgrade`] to succeed.\npub struct WebSocketResponse {\n    accept: SecWebsocketAccept,\n    protocol: Option<ResponseSecWebsocketProtocol>,\n}\n\nimpl IntoResponse for WebSocketResponse {\n    #[inline]\n    fn into_response(self) -> Response {\n        (\n            StatusCode::SWITCHING_PROTOCOLS,\n            TypedHeader(Connection::upgrade()),\n            TypedHeader(Upgrade::websocket()),\n            TypedHeader(self.accept),\n            self.protocol.map(TypedHeader),\n            (),\n        )\n            .into_response()\n    }\n}\n"
  },
  {
    "path": "crates/client-api/src/util.rs",
    "content": "mod flat_csv;\npub(crate) mod serde;\npub mod websocket;\n\nuse core::fmt;\nuse std::net::IpAddr;\n\nuse axum::body::Bytes;\nuse axum::extract::{FromRequest, Request};\nuse axum::response::IntoResponse;\nuse bytestring::ByteString;\nuse futures::TryStreamExt;\nuse http::{HeaderName, HeaderValue, StatusCode};\n\nuse hyper::body::Body;\nuse spacetimedb::Identity;\nuse spacetimedb_client_api_messages::name::DatabaseName;\n\nuse crate::routes::identity::IdentityForUrl;\nuse crate::{log_and_500, ControlStateReadAccess};\n\npub struct ByteStringBody(pub ByteString);\n\n#[async_trait::async_trait]\nimpl<S: Send + Sync> FromRequest<S> for ByteStringBody {\n    type Rejection = axum::response::Response;\n\n    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {\n        let bytes = Bytes::from_request(req, state)\n            .await\n            .map_err(IntoResponse::into_response)?;\n\n        let string = bytes\n            .try_into()\n            .map_err(|_| (StatusCode::BAD_REQUEST, \"Request body didn't contain valid UTF-8\").into_response())?;\n\n        Ok(ByteStringBody(string))\n    }\n}\n\npub struct XForwardedFor(pub IpAddr);\n\nimpl headers::Header for XForwardedFor {\n    fn name() -> &'static HeaderName {\n        static NAME: HeaderName = HeaderName::from_static(\"x-forwarded-for\");\n        &NAME\n    }\n\n    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, headers::Error> {\n        let val = values.next().ok_or_else(headers::Error::invalid)?;\n        let val = val.to_str().map_err(|_| headers::Error::invalid())?;\n        let (first, _) = val.split_once(',').ok_or_else(headers::Error::invalid)?;\n        let ip = first.trim().parse().map_err(|_| headers::Error::invalid())?;\n        Ok(XForwardedFor(ip))\n    }\n\n    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {\n        values.extend([self.0.to_string().try_into().unwrap()])\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum NameOrIdentity {\n    Identity(IdentityForUrl),\n    Name(DatabaseName),\n}\n\nimpl NameOrIdentity {\n    pub fn into_string(self) -> String {\n        match self {\n            NameOrIdentity::Identity(addr) => Identity::from(addr).to_hex().to_string(),\n            NameOrIdentity::Name(name) => name.into(),\n        }\n    }\n\n    pub fn name(&self) -> Option<&DatabaseName> {\n        if let Self::Name(name) = self {\n            Some(name)\n        } else {\n            None\n        }\n    }\n\n    /// Resolve this [`NameOrIdentity`].\n    ///\n    /// If `self` is a [`NameOrIdentity::Identity`], the inner [`Identity`] is\n    /// returned directly.\n    ///\n    /// Otherwise, if `self` is a [`NameOrIdentity::Name`], the [`Identity`] is\n    /// looked up by that name in the SpacetimeDB DNS and returned.\n    ///\n    /// Errors are returned if the DNS lookup fails.\n    ///\n    /// An `Ok` result is itself a [`Result`], which is `Err(DatabaseName)` if the\n    /// given [`NameOrIdentity::Name`] is not registered in the SpacetimeDB DNS,\n    /// i.e. no corresponding [`Identity`] exists.\n    pub async fn try_resolve(\n        &self,\n        ctx: &(impl ControlStateReadAccess + ?Sized),\n    ) -> anyhow::Result<Result<Identity, &DatabaseName>> {\n        Ok(match self {\n            Self::Identity(identity) => Ok(Identity::from(*identity)),\n            Self::Name(name) => ctx.lookup_database_identity(name.as_ref()).await?.ok_or(name),\n        })\n    }\n\n    /// A variant of [`Self::try_resolve()`] which maps to a 404 (Not Found)\n    /// response if `self` is a [`NameOrIdentity::Name`] for which no\n    /// corresponding [`Identity`] is found in the SpacetimeDB DNS.\n    pub async fn resolve(&self, ctx: &(impl ControlStateReadAccess + ?Sized)) -> axum::response::Result<Identity> {\n        self.try_resolve(ctx)\n            .await\n            .map_err(log_and_500)?\n            .map_err(|name| (StatusCode::NOT_FOUND, format!(\"`{name}` not found\")).into())\n    }\n\n    /// If `self` is a [`NameOrIdentity::Name`], looks up the name in the\n    /// namespace registry (also known as \"top level domain\") and returns the\n    /// owner identity if found.\n    ///\n    /// If the name is not found, returns a 404 (Not Found) error response.\n    ///\n    /// If `self` is a [`NameOrIdentity::Identity`], returns the identity.\n    //\n    // NOTE: Namespace (TLD) owner identities are also used as organization\n    // identities.\n    pub async fn resolve_namespace_owner(\n        &self,\n        ctx: &(impl ControlStateReadAccess + ?Sized),\n    ) -> axum::response::Result<Identity> {\n        match self {\n            Self::Identity(identity) => Ok(Identity::from(*identity)),\n            Self::Name(name) => ctx\n                .lookup_namespace_owner(name.as_ref())\n                .await\n                .map_err(log_and_500)?\n                .ok_or_else(|| (StatusCode::NOT_FOUND, format!(\"`{name}` not found\")).into()),\n        }\n    }\n}\n\nimpl<'de> ::serde::Deserialize<'de> for NameOrIdentity {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: ::serde::Deserializer<'de>,\n    {\n        let s = String::deserialize(deserializer)?;\n        if let Ok(addr) = Identity::from_hex(&s) {\n            Ok(NameOrIdentity::Identity(IdentityForUrl::from(addr)))\n        } else {\n            let name: DatabaseName = s.try_into().map_err(::serde::de::Error::custom)?;\n            Ok(NameOrIdentity::Name(name))\n        }\n    }\n}\n\nimpl fmt::Display for NameOrIdentity {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::Identity(addr) => f.write_str(addr.into_inner().to_hex().as_str()),\n            Self::Name(name) => f.write_str(name.as_ref()),\n        }\n    }\n}\n\npub struct EmptyBody;\n\n#[async_trait::async_trait]\nimpl<S> FromRequest<S> for EmptyBody {\n    type Rejection = axum::response::Response;\n    async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> {\n        let body = req.into_body();\n        if body.is_end_stream() {\n            return Ok(Self);\n        }\n\n        if body\n            .into_data_stream()\n            .try_any(|data| futures::future::ready(!data.is_empty()))\n            .await\n            .map_err(|_| (StatusCode::BAD_REQUEST, \"Failed to buffer the request body\").into_response())?\n        {\n            return Err((StatusCode::BAD_REQUEST, \"body must be empty\").into_response());\n        }\n        Ok(Self)\n    }\n}\n"
  },
  {
    "path": "crates/client-api-messages/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-client-api-messages\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Types for the SpacetimeDB client API messages\"\nrust-version.workspace = true\n\n[dependencies]\nspacetimedb-lib = { workspace = true, features = [\"serde\"] }\nspacetimedb-primitives.workspace = true\nspacetimedb-sats = { workspace = true, features = [\"bytestring\"] }\n\nbytes.workspace = true\nbytestring.workspace = true\nchrono = { workspace = true, features = [\"serde\"] }\nenum-as-inner.workspace = true\nserde = { workspace = true, features = [\"derive\"] }\nserde_json.workspace = true\nserde_with.workspace = true\nsmallvec.workspace = true\nstrum.workspace = true\nthiserror.workspace = true\nderive_more.workspace = true\n\n[dev-dependencies]\nhex.workspace = true\nitertools.workspace = true\nproptest.workspace = true\nserde_json.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/client-api-messages/DEVELOP.md",
    "content": "# Generate client bindings for the WebSocket message schema\n\nAfter changing the WebSocket message schema, generate client bindings for them as follows.\n\nIn this directory:\n\n```sh\ncargo run --example get_ws_schema > ws_schema.json\nspacetime generate -p spacetimedb-cli --lang <SDK lang> \\\n  --out-dir <sdk WebSocket schema bindings dir> \\\n  --module-def ws_schema.json\n```\n\nFor the v2 WebSocket protocol schema:\n\n```sh\ncargo run --example get_ws_schema_v2 > ws_schema_v2.json\nspacetime generate -p spacetimedb-cli --lang <SDK lang> \\\n  --out-dir <sdk WebSocket schema bindings dir> \\\n  --module-def ws_schema_v2.json\n```\n"
  },
  {
    "path": "crates/client-api-messages/README.md",
    "content": "> ⚠️ **Unstable Crate** ⚠️\n>\n> The interface of this crate is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/client-api-messages/examples/get_ws_schema.rs",
    "content": "use bytes::Bytes;\nuse spacetimedb_client_api_messages::websocket::v1::{BsatnFormat, ClientMessage, ServerMessage};\nuse spacetimedb_lib::ser::serde::SerializeWrapper;\nuse spacetimedb_lib::{RawModuleDef, RawModuleDefV8};\n\nfn main() -> Result<(), serde_json::Error> {\n    let module = RawModuleDefV8::with_builder(|module| {\n        module.add_type::<ClientMessage<Bytes>>();\n        module.add_type::<ServerMessage<BsatnFormat>>();\n    });\n    let module = RawModuleDef::V8BackCompat(module);\n\n    serde_json::to_writer(std::io::stdout().lock(), SerializeWrapper::from_ref(&module))\n}\n"
  },
  {
    "path": "crates/client-api-messages/examples/get_ws_schema_v2.rs",
    "content": "use spacetimedb_client_api_messages::websocket::v2::{ClientMessage, ServerMessage};\nuse spacetimedb_lib::ser::serde::SerializeWrapper;\nuse spacetimedb_lib::{RawModuleDef, RawModuleDefV8};\n\nfn main() -> Result<(), serde_json::Error> {\n    let module = RawModuleDefV8::with_builder(|module| {\n        module.add_type::<ClientMessage>();\n        module.add_type::<ServerMessage>();\n    });\n    let module = RawModuleDef::V8BackCompat(module);\n\n    serde_json::to_writer(std::io::stdout().lock(), SerializeWrapper::from_ref(&module))\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/energy.rs",
    "content": "use derive_more::{Add, AddAssign, From, Sub, SubAssign};\nuse spacetimedb_sats::SpacetimeType;\nuse std::fmt;\nuse std::time::Duration;\n\n/// [EnergyQuanta] represents an amount of energy in a canonical unit.\n/// It represents the smallest unit of energy that can be used to pay for\n/// a reducer invocation. We will likely refer to this unit as an \"eV\".\n///\n#[derive(SpacetimeType, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Add, Sub, AddAssign, SubAssign)]\n#[sats(crate = spacetimedb_sats)]\npub struct EnergyQuanta {\n    pub quanta: u128,\n}\n\nimpl EnergyQuanta {\n    pub const ZERO: Self = EnergyQuanta { quanta: 0 };\n\n    #[inline]\n    pub fn new(quanta: u128) -> Self {\n        Self { quanta }\n    }\n\n    #[inline]\n    pub fn get(&self) -> u128 {\n        self.quanta\n    }\n\n    pub fn from_disk_usage(bytes_stored: u64, storage_period: Duration) -> Self {\n        let bytes_stored = u128::from(bytes_stored);\n        let sec = u128::from(storage_period.as_secs());\n        let nsec = u128::from(storage_period.subsec_nanos());\n        // bytes_stored * storage_period, but make it complicated. floats might be lossy for large\n        // enough values, so instead we expand the multiplication to (b * trunc(dur) + b * frac(dur)),\n        // in a way that preserves integer precision despite a division\n        let energy = bytes_stored * sec + (bytes_stored * nsec) / 1_000_000_000;\n        Self::new(energy)\n    }\n\n    const ENERGY_PER_MEM_BYTE_SEC: u128 = 100;\n\n    pub fn from_memory_usage(bytes_stored: u64, storage_period: Duration) -> Self {\n        let byte_seconds = Self::from_disk_usage(bytes_stored, storage_period).get();\n        Self::new(byte_seconds * Self::ENERGY_PER_MEM_BYTE_SEC)\n    }\n}\n\nimpl fmt::Display for EnergyQuanta {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.quanta.fmt(f)?;\n        f.write_str(\"eV\")\n    }\n}\n\nimpl fmt::Debug for EnergyQuanta {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(self, f)\n    }\n}\n\n/// [`EnergyBalance`] same unit as [`EnergyQuanta`], but representing a user account's energy balance.\n///\n/// NOTE: This is represented by a signed integer, because it is possible\n/// for a user's balance to go negative. This is allowable\n/// for reasons of eventual consistency motivated by performance.\n#[derive(Copy, Clone)]\npub struct EnergyBalance(i128);\n\nimpl EnergyBalance {\n    pub const ZERO: Self = EnergyBalance(0);\n\n    #[inline]\n    pub fn new(v: i128) -> Self {\n        Self(v)\n    }\n\n    #[inline]\n    pub fn get(&self) -> i128 {\n        self.0\n    }\n\n    /// Convert to [`EnergyQuanta`].\n    ///\n    /// If this balance is negative, this method returns an `Err` holding the amount\n    /// negative that this balance is.\n    pub fn to_energy_quanta(&self) -> Result<EnergyQuanta, EnergyQuanta> {\n        if self.0.is_negative() {\n            Err(EnergyQuanta::new(self.0.unsigned_abs()))\n        } else {\n            Ok(EnergyQuanta::new(self.0 as u128))\n        }\n    }\n\n    pub fn checked_add_energy(self, energy: EnergyQuanta) -> Option<Self> {\n        self.0.checked_add_unsigned(energy.get()).map(Self)\n    }\n\n    pub fn saturating_add_energy(&self, energy: EnergyQuanta) -> Self {\n        Self(self.0.saturating_add_unsigned(energy.get()))\n    }\n\n    pub fn checked_sub_energy(self, energy: EnergyQuanta) -> Option<Self> {\n        self.0.checked_sub_unsigned(energy.get()).map(Self)\n    }\n\n    pub fn saturating_sub_energy(&self, energy: EnergyQuanta) -> Self {\n        Self(self.0.saturating_sub_unsigned(energy.get()))\n    }\n}\n\nimpl fmt::Display for EnergyBalance {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)?;\n        f.write_str(\"eV\")\n    }\n}\n\nimpl fmt::Debug for EnergyBalance {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"EnergyBalance\").field(self).finish()\n    }\n}\n\n/// A measure of energy representing the energy budget for a reducer or any callable function.\n///\n/// In contrast to [`EnergyQuanta`], this is represented by a 64-bit integer. This makes energy handling\n/// for reducers easier, while still providing a unlikely-to-ever-be-reached maximum value (e.g. for wasmtime:\n/// `(u64::MAX eV / 1000 eV/instruction) * 3 ns/instruction = 640 days`)\n#[derive(Copy, Clone, From, Add, Sub, AddAssign, SubAssign)]\npub struct FunctionBudget(u64);\n\nimpl FunctionBudget {\n    // 1 second of wasm runtime is roughly 2 TeV, so this is\n    // roughly 1 minute of wasm runtime\n    pub const DEFAULT_BUDGET: Self = FunctionBudget(120_000_000_000_000);\n\n    pub const ZERO: Self = FunctionBudget(0);\n    pub const MAX: Self = FunctionBudget(u64::MAX);\n\n    pub fn new(v: u64) -> Self {\n        Self(v)\n    }\n\n    pub fn get(&self) -> u64 {\n        self.0\n    }\n\n    /// Convert from [`EnergyQuanta`]. Returns `None` if `energy` is too large to be represented.\n    pub fn from_energy(energy: EnergyQuanta) -> Option<Self> {\n        energy.get().try_into().ok().map(Self)\n    }\n}\n\nimpl From<FunctionBudget> for EnergyQuanta {\n    fn from(value: FunctionBudget) -> Self {\n        EnergyQuanta::new(value.0.into())\n    }\n}\n\nimpl fmt::Debug for FunctionBudget {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"ReducerBudget\")\n            .field(&EnergyQuanta::from(*self))\n            .finish()\n    }\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/http.rs",
    "content": "use std::collections::BTreeSet;\nuse std::iter;\n\nuse serde::{Deserialize, Serialize};\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::{Hash, Identity, ProductType};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SqlStmtResult<Row> {\n    pub schema: ProductType,\n    pub rows: Vec<Row>,\n    pub total_duration_micros: u64,\n    #[serde(default)]\n    pub stats: SqlStmtStats,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\npub struct SqlStmtStats {\n    pub rows_inserted: u64,\n    pub rows_deleted: u64,\n    pub rows_updated: u64,\n}\n\nimpl SqlStmtStats {\n    pub fn from_metrics(metrics: &ExecutionMetrics) -> Self {\n        Self {\n            rows_inserted: metrics.rows_inserted,\n            rows_deleted: metrics.rows_deleted,\n            rows_updated: metrics.rows_updated,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub struct DatabaseTree {\n    pub root: DatabaseTreeNode,\n    pub children: Vec<DatabaseTree>,\n}\n\nimpl DatabaseTree {\n    pub fn iter(&self) -> impl Iterator<Item = &DatabaseTreeNode> + '_ {\n        let mut stack = vec![self];\n        iter::from_fn(move || {\n            let node = stack.pop()?;\n            for child in node.children.iter().rev() {\n                stack.push(child);\n            }\n            Some(&node.root)\n        })\n    }\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub struct DatabaseTreeNode {\n    pub database_identity: Identity,\n    pub database_names: BTreeSet<String>,\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct DatabaseDeleteConfirmationResponse {\n    pub database_tree: DatabaseTree,\n    pub confirmation_token: Hash,\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/lib.rs",
    "content": "//! Schemas of various messages sent over SpacetimeDB's HTTP and WebSocket APIs.\n\npub mod energy;\npub mod http;\npub mod name;\npub mod websocket;\n"
  },
  {
    "path": "crates/client-api-messages/src/name/tests.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n\nuse std::iter;\n\nuse itertools::Itertools as _;\nuse proptest::prelude::*;\n\nuse spacetimedb_sats::bsatn;\n\nuse super::*;\n\nfn gen_valid_domain_name() -> impl Strategy<Value = String> {\n    \"[\\\\S&&[^/]]{1,64}(/[\\\\S&&[^/]]{1,64}){0,255}\"\n}\n\nproptest! {\n    #[test]\n    fn prop_domain_name_parses(s in gen_valid_domain_name()) {\n        parse_domain_name(s)?;\n    }\n\n    #[test]\n    fn prop_domain_name_displays_input(s in gen_valid_domain_name()) {\n        let domain = DomainName::from_str(&s)?;\n        prop_assert_eq!(s, domain.to_string())\n    }\n\n    #[test]\n    fn prop_domain_name_into_parse(\n        tld in \"[\\\\S&&[^/]]{1,64}\",\n        sub in prop::option::of(gen_valid_domain_name())\n    ) {\n        let domain = parse_domain_name(iter::once(tld.as_str()).chain(sub.as_deref()).join(\"/\"))?;\n        prop_assert_eq!(&tld, domain.tld().as_str());\n        prop_assert_eq!(sub.as_deref(), domain.sub_domain());\n        let domain_tld = Tld::from(domain);\n        prop_assert_eq!(&tld, domain_tld.as_str());\n    }\n\n    #[test]\n    fn prop_domain_name_serde(s in gen_valid_domain_name()) {\n        let a = parse_domain_name(s)?;\n        let js = serde_json::to_string(&a)?;\n        eprintln!(\"json: `{js}`\");\n        let b: DomainName = serde_json::from_str(&js)?;\n        prop_assert_eq!(a, b)\n    }\n\n    #[test]\n    fn prop_domain_name_sats(s in gen_valid_domain_name()) {\n        let a = parse_domain_name(s)?;\n        let bsatn = bsatn::to_vec(&a)?;\n        let b: DomainName = bsatn::from_slice(&bsatn)?;\n        prop_assert_eq!(a, b)\n    }\n\n    #[test]\n    fn prop_domain_name_inequality(a in gen_valid_domain_name(), b in gen_valid_domain_name()) {\n        prop_assume!(a != b);\n        let a = parse_domain_name(a)?;\n        let b = parse_domain_name(b)?;\n        prop_assert_ne!(a, b);\n    }\n\n    #[test]\n    fn prop_domain_name_must_not_start_with_slash(s in \"/\\\\S{1,100}\") {\n        assert!(matches!(\n           parse_domain_name(s),\n           Err(DomainParsingError(ParseError::StartsSlash { .. })),\n        ))\n    }\n\n    #[test]\n    fn prop_domain_name_must_not_end_with_slash(s in \"[\\\\S&&[^/]]{1,64}/\") {\n        assert!(matches!(\n            parse_domain_name(s),\n            Err(DomainParsingError(ParseError::EndsSlash { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_domain_name_must_not_contain_slashslash(s in \"[\\\\S&&[^/]]{1,25}//[\\\\S&&[^/]]{1,25}\") {\n        assert!(matches!(\n            parse_domain_name(s),\n            Err(DomainParsingError(ParseError::SlashSlash { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_domain_name_must_not_contain_whitespace(s in \"[\\\\S&&[^/]]{0,10}\\\\s{1,10}[\\\\S&&[^/]]{0,10}\") {\n        assert!(matches!(\n            parse_domain_name(s),\n            Err(DomainParsingError(ParseError::Whitespace { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_domain_name_parts_must_not_exceed_max_chars(s in \"[\\\\S&&[^/]]{65}(/[\\\\S&&[^/]]{65})*\") {\n        assert!(matches!(\n            parse_domain_name(s),\n            Err(DomainParsingError(ParseError::TooLong { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_domain_name_cannot_have_unlimited_subdomains(s in \"[\\\\S&&[^/]]{1,64}(/[\\\\S&&[^/]]{1,64}){257}\") {\n        assert!(matches!(\n            parse_domain_name(s),\n            Err(DomainParsingError(ParseError::TooManySubdomains { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_tld_cannot_be_identity(addr_bytes in any::<[u8; 32]>()) {\n        let addr = hex::encode(addr_bytes);\n        assert!(matches!(\n            parse_domain_name(addr),\n            Err(DomainParsingError(ParseError::Identity { .. }))\n        ))\n    }\n\n    #[test]\n    fn prop_but_tld_can_be_some_other_hex_value(bytes in any::<[u8; 16]>()) {\n        let addr = hex::encode(bytes);\n        parse_domain_name(addr)?;\n    }\n}\n\n#[test]\nfn test_domain_segment_cannot_be_empty() {\n    assert!(matches!(DomainSegment::try_from(\"\"), Err(ParseError::Empty)))\n}\n\n#[test]\nfn test_domain_name_cannot_be_empty() {\n    assert!(matches!(\n        parse_domain_name(\"\"),\n        Err(DomainParsingError(ParseError::Empty))\n    ))\n}\n\n#[test]\nfn test_tld_is_domain_name() {\n    let dom = parse_domain_name(\"spacetimedb/drop\").unwrap();\n    let tld = Tld::from(dom);\n    let dom = DomainName::from(tld);\n\n    assert_eq!(\"spacetimedb\", dom.tld().as_str());\n    assert_eq!(None, dom.sub_domain());\n}\n\nmod serde {\n    use super::*;\n\n    use crate::name::serde_impls::DomainNameV1;\n\n    #[test]\n    fn test_deserialize_domain_name_v1() {\n        let js = serde_json::to_string(&DomainNameV1 {\n            tld: \"clockworklabs\",\n            sub_domain: \"bitcraft-mini\",\n        })\n        .unwrap();\n        let de: DomainName = serde_json::from_str(&js).unwrap();\n\n        assert_eq!(\"clockworklabs/bitcraft-mini\", de.as_str());\n        assert_eq!(\"clockworklabs\", de.tld().as_str());\n        assert_eq!(Some(\"bitcraft-mini\"), de.sub_domain());\n    }\n\n    #[test]\n    fn test_deserialize_domain_name_v1_validates() {\n        let invalid = serde_json::to_string(&DomainNameV1 {\n            tld: \"eve\",\n            sub_domain: \"bit//craft\",\n        })\n        .unwrap();\n        let de: Result<DomainName, serde_json::Error> = serde_json::from_str(&invalid);\n\n        assert!(matches!(de, Err(e) if e.classify() == serde_json::error::Category::Data));\n    }\n\n    #[test]\n    fn test_deserialize_domain_name_v2() {\n        let dn = parse_domain_name(\"clockworklabs/bitcraft-mini\").unwrap();\n        let js = serde_json::to_string(&dn).unwrap();\n        let de = serde_json::from_str(&js).unwrap();\n        assert_eq!(dn, de);\n    }\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/name.rs",
    "content": "use spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st};\nuse std::{borrow::Borrow, fmt, ops::Deref, str::FromStr};\n\nuse spacetimedb_lib::Identity;\n\n#[cfg(test)]\nmod tests;\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum InsertDomainResult {\n    Success {\n        domain: DomainName,\n        database_identity: Identity,\n    },\n\n    /// The top level domain for the database name is not registered. For example:\n    ///\n    ///  - `clockworklabs/bitcraft`\n    ///\n    /// if `clockworklabs` is not registered, this error is returned.\n    TldNotRegistered { domain: DomainName },\n\n    /// The top level domain for the database name is registered, but the identity that you provided does\n    /// not have permission to insert the given database name. For example:\n    ///\n    /// - `clockworklabs/bitcraft`\n    ///\n    /// If you were trying to insert this database name, but the tld `clockworklabs` is\n    /// owned by an identity other than the identity that you provided, then you will receive\n    /// this error.\n    PermissionDenied { domain: DomainName },\n\n    /// Some unspecified error occurred.\n    OtherError(String),\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum SetDomainsResult {\n    Success,\n\n    /// The top level domain for the database name is registered, but the identity that you provided does\n    /// not have permission to insert the given database name. For example:\n    ///\n    /// - `clockworklabs/bitcraft`\n    ///\n    /// If you were trying to insert this database name, but the tld `clockworklabs` is\n    /// owned by an identity other than the identity that you provided, then you will receive\n    /// this error.\n    ///\n    /// In order to set the domains for a database, you must also be the owner of that database.\n    PermissionDenied {\n        domain: DomainName,\n    },\n\n    /// Workaround for cloud, which can't extract the exact failing domain from\n    /// reducer errors.\n    PermissionDeniedOnAny {\n        domains: Box<[DomainName]>,\n    },\n\n    /// The database name or identity you provided does not exist.\n    DatabaseNotFound,\n\n    /// The caller doesn't own the database.\n    NotYourDatabase {\n        database: Identity,\n    },\n\n    /// Some unspecified error occurred.\n    OtherError(String),\n}\n\n#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum PublishOp {\n    Created,\n    Updated,\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum PublishResult {\n    Success {\n        /// `Some` if publish was given a domain name to operate on, `None`\n        /// otherwise.\n        ///\n        /// In other words, this echoes back a domain name if one was given. If\n        /// the database name given was in fact a database identity, this will be\n        /// `None`.\n        domain: Option<DatabaseName>,\n        /// The identity of the published database.\n        ///\n        /// Always set, regardless of whether publish resolved a domain name first\n        /// or not.\n        database_identity: Identity,\n        op: PublishOp,\n    },\n\n    /// The top level domain for the database name is registered, but the identity that you provided does\n    /// not have permission to insert the given database name. For example:\n    ///\n    /// - `clockworklabs/bitcraft`\n    ///\n    /// If you were trying to insert this database name, but the tld `clockworklabs` is\n    /// owned by an identity other than the identity that you provided, then you will receive\n    /// this error.\n    PermissionDenied { name: DatabaseName },\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Default)]\npub enum MigrationPolicy {\n    #[default]\n    Compatible,\n    BreakClients,\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Default)]\npub enum PrettyPrintStyle {\n    #[default]\n    AnsiColor,\n    NoColor,\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug)]\npub enum PrePublishResult {\n    AutoMigrate(PrePublishAutoMigrateResult),\n    ManualMigrate(PrePublishManualMigrateResult),\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug)]\npub struct PrePublishAutoMigrateResult {\n    pub migrate_plan: Box<str>,\n    pub break_clients: bool,\n    pub token: spacetimedb_lib::Hash,\n    pub major_version_upgrade: bool,\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug)]\npub struct PrePublishManualMigrateResult {\n    pub reason: String,\n    pub major_version_upgrade: bool,\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum DnsLookupResponse {\n    /// The lookup was successful and the domain and identity are returned.\n    Success { domain: DomainName, identity: Identity },\n\n    /// There was no domain registered with the given domain name\n    Failure { domain: DomainName },\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum RegisterTldResult {\n    Success {\n        domain: Tld,\n    },\n    /// The domain is already registered to the calling identity\n    AlreadyRegistered {\n        domain: Tld,\n    },\n    /// The domain is already registered to another identity\n    Unauthorized {\n        domain: Tld,\n    },\n    // TODO(jdetter): Insufficient funds error here\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub enum SetDefaultDomainResult {\n    Success {\n        domain: DomainName,\n    },\n    /// The identity doesn't own the domain they tried to set as their default.\n    Unauthorized {\n        domain: DomainName,\n    },\n    /// No identity owns this domain so it cannot be set as the default domain for an identity.\n    NotRegistered {\n        domain: DomainName,\n    },\n}\n\n/// A simplified version of [`DomainName`] that allows a limited set of characters.\n///\n/// Must match the regex `^[a-z0-9]+(-[a-z0-9]+)*$`\n#[derive(Clone, Debug, serde_with::DeserializeFromStr, serde_with::SerializeDisplay)]\npub struct DatabaseName(pub String);\n\nimpl AsRef<str> for DatabaseName {\n    fn as_ref(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl From<DatabaseName> for String {\n    fn from(name: DatabaseName) -> Self {\n        name.0\n    }\n}\n\nimpl fmt::Display for DatabaseName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(&self.0)\n    }\n}\n\n#[derive(thiserror::Error, Clone, Copy, Debug)]\npub enum DatabaseNameError {\n    #[error(\"database names cannot be identities\")]\n    Identity,\n    #[error(\"database names cannot be empty\")]\n    Empty,\n    #[error(\"invalid hyphen in database name\")]\n    Hyphen,\n    #[error(\"invalid characters in database name\")]\n    Invalid,\n}\n\npub fn parse_database_name(s: &str) -> Result<&str, DatabaseNameError> {\n    use DatabaseNameError::*;\n\n    if is_identity(s) {\n        return Err(Identity);\n    }\n\n    let mut chrs = s.chars();\n    let mut next = || chrs.next();\n\n    let is_az09 = |c: char| matches!(c, 'a'..='z' | '0'..='9');\n\n    let c = next().ok_or(Empty)?;\n    if c == '-' {\n        return Err(Hyphen);\n    } else if !is_az09(c) {\n        return Err(Invalid);\n    }\n\n    while let Some(c) = next() {\n        if c == '-' {\n            // can't have a hyphen at the end\n            let c = next().ok_or(Hyphen)?;\n            // can't have 2 hyphens in a row\n            if !is_az09(c) {\n                return Err(Hyphen);\n            }\n        } else if !is_az09(c) {\n            return Err(Invalid);\n        }\n    }\n\n    Ok(s)\n}\n\nimpl TryFrom<String> for DatabaseName {\n    type Error = DatabaseNameError;\n\n    fn try_from(s: String) -> Result<Self, Self::Error> {\n        parse_database_name(&s)?;\n        Ok(Self(s))\n    }\n}\n\nimpl FromStr for DatabaseName {\n    type Err = DatabaseNameError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(Self(parse_database_name(s)?.to_owned()))\n    }\n}\n\nimpl From<DatabaseName> for Tld {\n    fn from(name: DatabaseName) -> Self {\n        Tld(name.0)\n    }\n}\n\nimpl From<DatabaseName> for DomainName {\n    fn from(name: DatabaseName) -> Self {\n        Tld::from(name).into()\n    }\n}\n\n/// The top level domain part of a [`DomainName`].\n///\n/// This newtype witnesses that the TLD is well-formed as per the parsing rules\n/// of a full [`DomainName`]. A [`Tld`] is also a valid [`DomainName`], and can\n/// be converted to this type.\n///\n/// Note that the SpacetimeDB DNS registry may apply additional restrictions on\n/// what TLDs can be registered.\n#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]\npub struct Tld(String);\n\nimpl Tld {\n    pub fn as_str(&self) -> &str {\n        &self.0\n    }\n\n    pub fn to_lowercase(&self) -> String {\n        self.as_str().to_lowercase()\n    }\n}\n\nimpl AsRef<str> for Tld {\n    fn as_ref(&self) -> &str {\n        self.as_str()\n    }\n}\n\nimpl AsRef<TldRef> for Tld {\n    fn as_ref(&self) -> &TldRef {\n        TldRef::new(&self.0)\n    }\n}\n\nimpl fmt::Display for Tld {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(&self.0)\n    }\n}\n\nimpl From<DomainName> for Tld {\n    fn from(value: DomainName) -> Self {\n        let mut name = value.domain_name;\n        name.truncate(value.tld_offset);\n        Self(name)\n    }\n}\n\nimpl_st!([] Tld, spacetimedb_lib::AlgebraicType::String);\nimpl_serialize!([] Tld, (self, ser) => spacetimedb_sats::ser::Serialize::serialize(&self.0, ser));\nimpl_deserialize!([] Tld, de => {\n    let s: String = spacetimedb_sats::de::Deserialize::deserialize(de)?;\n    ensure_domain_tld(&s).map_err(spacetimedb_sats::de::Error::custom)?;\n    Ok(Self(s))\n});\n\nimpl<'de> serde::Deserialize<'de> for Tld {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let s: String = serde::Deserialize::deserialize(deserializer)?;\n        ensure_domain_tld(&s).map_err(serde::de::Error::custom)?;\n        Ok(Self(s))\n    }\n}\n\n/// A slice of a [`Tld`], akin to [`str`].\n#[derive(Debug, PartialEq, Eq)]\n#[repr(transparent)]\npub struct TldRef(str);\n\nimpl TldRef {\n    // Private to enforce parsing\n    fn new(s: &str) -> &Self {\n        // SAFETY: `TldRef` is just a wrapper around `str` with the same memory\n        // representation (`repr(transparent)`), therefore converting `&str` to\n        // `&TldRef` is safe.\n        unsafe { &*(s as *const str as *const TldRef) }\n    }\n\n    pub fn as_str(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl AsRef<TldRef> for TldRef {\n    fn as_ref(&self) -> &TldRef {\n        self\n    }\n}\n\nimpl Deref for TldRef {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl Borrow<TldRef> for Tld {\n    fn borrow(&self) -> &TldRef {\n        TldRef::new(&self.0)\n    }\n}\n\nimpl ToOwned for TldRef {\n    type Owned = Tld;\n\n    fn to_owned(&self) -> Self::Owned {\n        Tld(self.0.to_owned())\n    }\n}\n\nimpl fmt::Display for TldRef {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(&self.0)\n    }\n}\n\n/// A [`DomainName`] is the name of a database.\n///\n/// A database name is usually in one of the two following forms:\n///\n///  my_database_name\n///\n/// or\n///\n///  my_domain/mypath\n///\n/// You can also have as many path segments as you want (as long as it's less\n/// than 256):\n///\n///  my_domain/a/b/c/d\n///\n/// Database names must NOT end or start in a slash and cannot have 2 slashes in\n/// a row. These are all invalid:\n///\n///  my_domain/a//c/d\n///  /my_domain\n///  my_domain/\n///\n/// Each segment in a database name can contain any UTF-8 character, except for\n/// whitespace and '/'. The maximum segment length is 64 characters.\n///\n/// The first path segment is also referred to as the \"top-level domain\", or\n/// [`Tld`]. The concatenation of all segments after the first '/' is also\n/// referred as the \"subdomain\".\n///\n/// Note that [`PartialEq`] compares the exact string representation of a\n/// [`DomainName`], as one would expect, but the SpacetimeDB registry compares\n/// the lowercase representation of it.\n///\n/// To construct a valid [`DomainName`], use [`parse_domain_name`] or the\n/// [`FromStr`] impl.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct DomainName {\n    // Iff there is a subdomain, next char in `domain_name` is '/'.\n    tld_offset: usize,\n    domain_name: String,\n}\n\nimpl DomainName {\n    /// Returns a string slice with the domain name.\n    pub fn as_str(&self) -> &str {\n        &self.domain_name\n    }\n\n    /// Get the top-level domain, as a reference.\n    pub fn tld(&self) -> &TldRef {\n        TldRef::new(&self.domain_name[..self.tld_offset])\n    }\n\n    /// Get the top-level domain, as an owned [`Tld`].\n    pub fn to_tld(&self) -> Tld {\n        self.tld().to_owned()\n    }\n\n    /// Get the subdomain, if any.\n    pub fn sub_domain(&self) -> Option<&str> {\n        if self.tld_offset + 1 < self.domain_name.len() {\n            Some(&self.domain_name[self.tld_offset + 1..])\n        } else {\n            None\n        }\n    }\n\n    /// Render the name as a lower-case, '/'-separated string, suitable for use\n    /// as a unique constrained field in a database.\n    pub fn to_lowercase(&self) -> String {\n        self.as_str().to_lowercase()\n    }\n}\n\nimpl AsRef<str> for DomainName {\n    fn as_ref(&self) -> &str {\n        self.as_str()\n    }\n}\n\nimpl From<DomainName> for String {\n    fn from(name: DomainName) -> Self {\n        name.domain_name\n    }\n}\n\nimpl fmt::Display for DomainName {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(&self.domain_name)\n    }\n}\n\nimpl FromStr for DomainName {\n    type Err = DomainParsingError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        parse_domain_name(s)\n    }\n}\n\nimpl From<Tld> for DomainName {\n    fn from(tld: Tld) -> Self {\n        let domain_name = tld.0;\n        Self {\n            tld_offset: domain_name.len(),\n            domain_name,\n        }\n    }\n}\n\nimpl_st!([] DomainName, spacetimedb_lib::AlgebraicType::String);\nimpl_serialize!([] DomainName, (self, ser) => spacetimedb_sats::ser::Serialize::serialize(self.as_str(), ser));\nimpl_deserialize!([] DomainName, de => {\n    let s: String = spacetimedb_sats::de::Deserialize::deserialize(de)?;\n    parse_domain_name(s).map_err(spacetimedb_sats::de::Error::custom)\n});\n\nmod serde_impls {\n    use super::*;\n\n    use serde::{\n        de::{self, value::MapAccessDeserializer, MapAccess},\n        Deserialize, Deserializer, Serialize, Serializer,\n    };\n\n    impl Serialize for DomainName {\n        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n        where\n            S: Serializer,\n        {\n            Serialize::serialize(self.as_str(), serializer)\n        }\n    }\n\n    /// Version 1 of [`DomainName`] which is represented as a map in JSON.\n    #[derive(serde::Deserialize)]\n    #[cfg_attr(test, derive(serde::Serialize))]\n    pub(super) struct DomainNameV1<'a> {\n        pub(super) tld: &'a str,\n        pub(super) sub_domain: &'a str,\n    }\n\n    /// [`de::Visitor`] for deserializing [`DomainName`].\n    ///\n    /// Due to the ubiquitous use of [`DomainName`], this must ensure all past\n    /// and future `serde` representations can be deserialized.\n    struct DomainNameVisitor;\n\n    impl<'de> de::Visitor<'de> for DomainNameVisitor {\n        type Value = DomainName;\n\n        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n            formatter.write_str(\"string or map\")\n        }\n\n        fn visit_string<E>(self, v: String) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            parse_domain_name(v).map_err(de::Error::custom)\n        }\n\n        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>\n        where\n            E: de::Error,\n        {\n            parse_domain_name(v).map_err(de::Error::custom)\n        }\n\n        fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>\n        where\n            A: MapAccess<'de>,\n        {\n            let v1: DomainNameV1 = Deserialize::deserialize(MapAccessDeserializer::new(map))?;\n            parse_domain_name([v1.tld, \"/\", v1.sub_domain].concat()).map_err(de::Error::custom)\n        }\n    }\n\n    impl<'de> Deserialize<'de> for DomainName {\n        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n        where\n            D: Deserializer<'de>,\n        {\n            deserializer.deserialize_any(DomainNameVisitor)\n        }\n    }\n}\n\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub struct GetNamesResponse {\n    pub names: Vec<DatabaseName>,\n}\n\n/// Returns whether a hex string is a valid identity.\n///\n/// Any string that is a valid identity is an invalid database name.\npub fn is_identity(hex: &str) -> bool {\n    Identity::from_hex(hex).is_ok()\n}\n\n#[derive(thiserror::Error, Debug)]\n#[error(\"Error when parsing a domain, reason: {0}\")]\npub struct DomainParsingError(#[from] ParseError);\n\n// XXX(kim): not sure it is a good idea to return the full input, but keeping it\n// for now to not break any upstream expectations\n#[derive(Debug, thiserror::Error)]\nenum ParseError {\n    #[error(\"Database names cannot be empty\")]\n    Empty,\n    #[error(\"Identities cannot be database names: `{part}`\")]\n    Identity { part: String },\n    #[error(\"Database names must not start with a slash: `{input}`\")]\n    StartsSlash { input: String },\n    #[error(\"Database names must not end with a slash: `{input}`\")]\n    EndsSlash { input: String },\n    #[error(\"Database names must not have 2 consecutive slashes: `{input}`\")]\n    SlashSlash { input: String },\n    #[error(\"Domain name parts must not contain slashes: `{part}`\")]\n    ContainsSlash { part: String },\n    #[error(\"Database names must not contain whitespace: `{input}`\")]\n    Whitespace { input: String },\n    #[error(\"Domain name parts must be shorter than {MAX_CHARS_PART} characters: `{part}`\")]\n    TooLong { part: String },\n    #[error(\"Domains cannot have more the {MAX_SUBDOMAINS} subdomains: `{input}`\")]\n    TooManySubdomains { input: String },\n}\n\n/// Maximum number of unicode characters a [`DomainName`] component can have.\npub const MAX_CHARS_PART: usize = 64;\n\n/// Maximum number of subdomains a [`DomainName`] can have.\npub const MAX_SUBDOMAINS: usize = 256;\n\n/// Parses a [`DomainName`].\n///\n/// For more information, see the documentation of [`DomainName`].\npub fn parse_domain_name<S>(domain: S) -> Result<DomainName, DomainParsingError>\nwhere\n    S: AsRef<str> + Into<String>,\n{\n    let input = domain.as_ref();\n    if input.is_empty() {\n        return Err(ParseError::Empty.into());\n    }\n    let mut parts = input.split('/');\n\n    let tld = parts.next().ok_or(ParseError::Empty)?;\n    // Check len for refined error.\n    if tld.is_empty() {\n        return Err(ParseError::StartsSlash { input: domain.into() }.into());\n    }\n    ensure_domain_tld(tld)?;\n    let tld_offset = tld.len();\n\n    let mut parts = parts.peekable();\n    for (i, part) in parts.by_ref().enumerate() {\n        if i + 1 > MAX_SUBDOMAINS {\n            return Err(ParseError::TooManySubdomains { input: domain.into() }.into());\n        }\n        if part.is_empty() {\n            // no idea why borrowchk accepts this lol\n            let err = if parts.peek().is_some() {\n                ParseError::SlashSlash { input: domain.into() }\n            } else {\n                ParseError::EndsSlash { input: domain.into() }\n            };\n            return Err(err.into());\n        }\n        ensure_domain_segment(part)?;\n    }\n\n    Ok(DomainName {\n        tld_offset,\n        domain_name: domain.into(),\n    })\n}\n\nfn ensure_domain_segment(input: &str) -> Result<(), ParseError> {\n    DomainSegment::try_from(input).map(|_| ())\n}\n\nfn ensure_domain_tld(input: &str) -> Result<(), ParseError> {\n    let DomainSegment(input) = DomainSegment::try_from(input)?;\n    if input.contains('/') {\n        Err(ParseError::ContainsSlash { part: input.to_owned() })\n    } else if is_identity(input) {\n        Err(ParseError::Identity { part: input.to_owned() })\n    } else {\n        Ok(())\n    }\n}\n\n/// Parsing helper to validate (path) segments of a [`DomainName`], without\n/// consuming the input.\nstruct DomainSegment<'a>(&'a str);\n\nimpl<'a> TryFrom<&'a str> for DomainSegment<'a> {\n    type Error = ParseError;\n\n    fn try_from(value: &'a str) -> Result<Self, Self::Error> {\n        if value.is_empty() {\n            Err(ParseError::Empty)\n        } else if value.chars().count() > MAX_CHARS_PART {\n            Err(ParseError::TooLong { part: value.to_owned() })\n        } else if value.contains(|c: char| c.is_whitespace()) {\n            Err(ParseError::Whitespace {\n                input: value.to_string(),\n            })\n        } else {\n            Ok(Self(value))\n        }\n    }\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/websocket/common.rs",
    "content": "use bytes::Bytes;\nuse bytestring::ByteString;\nuse spacetimedb_sats::{de::Error, impl_deserialize, impl_serialize, impl_st, AlgebraicType, SpacetimeType};\nuse std::ops::Deref;\nuse std::ops::Range;\nuse std::sync::Arc;\n\n/// An opaque id generated by the client to refer to a subscription.\n/// This is used in Unsubscribe messages and errors.\n#[derive(SpacetimeType, Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]\n#[sats(crate = spacetimedb_lib)]\npub struct QuerySetId {\n    pub id: u32,\n}\n\nimpl QuerySetId {\n    pub fn new(id: u32) -> Self {\n        Self { id }\n    }\n}\n\n#[derive(Clone, Copy, Default, PartialEq, Eq)]\npub enum CallProcedureFlags {\n    #[default]\n    Default,\n}\n\nimpl_st!([] CallProcedureFlags, AlgebraicType::U8);\nimpl_serialize!([] CallProcedureFlags, (self, ser) => ser.serialize_u8(*self as u8));\nimpl_deserialize!([] CallProcedureFlags, de => match de.deserialize_u8()? {\n    0 => Ok(Self::Default),\n    x => Err(D::Error::custom(format_args!(\"invalid call procedure flag {x}\"))),\n});\n\n/// A specification of either a desired or decided compression algorithm.\n#[derive(serde::Deserialize, Default, PartialEq, Eq, Clone, Copy, Hash, Debug)]\npub enum Compression {\n    /// No compression ever.\n    None,\n    /// Compress using brotli if a certain size threshold was met.\n    #[default]\n    Brotli,\n    /// Compress using gzip if a certain size threshold was met.\n    Gzip,\n}\n\n/// The tag recognized by the host and SDKs to mean no compression of a `ServerMessage`.\npub const SERVER_MSG_COMPRESSION_TAG_NONE: u8 = 0;\n\n/// The tag recognized by the host and SDKs to mean brotli compression of a `ServerMessage`.\npub const SERVER_MSG_COMPRESSION_TAG_BROTLI: u8 = 1;\n\n/// The tag recognized by the host and SDKs to mean gzip compression of a `ServerMessage`.\npub const SERVER_MSG_COMPRESSION_TAG_GZIP: u8 = 2;\n\npub type RowSize = u16;\npub type RowOffset = u64;\n\n/// A packed list of BSATN-encoded rows.\n#[derive(SpacetimeType, Debug, Clone, Default)]\n#[sats(crate = spacetimedb_lib)]\npub struct BsatnRowList {\n    /// A size hint about `rows_data`\n    /// intended to facilitate parallel decode purposes on large initial updates.\n    pub(crate) size_hint: RowSizeHint,\n    /// The flattened byte array for a list of rows.\n    pub(crate) rows_data: Bytes,\n}\n\nimpl BsatnRowList {\n    /// Returns a new row list where `rows_data` is the flattened byte array\n    /// containing the BSATN of each row, without any markers for where a row begins and end.\n    ///\n    /// The `size_hint` encodes the boundaries of each row in `rows_data`.\n    /// See [`RowSizeHint`] for more details on the encoding.\n    pub fn new(size_hint: RowSizeHint, rows_data: Bytes) -> Self {\n        Self { size_hint, rows_data }\n    }\n}\n\n/// NOTE(centril, 1.0): We might want to add a `None` variant to this\n/// where the client has to decode in a loop until `rows_data` has been exhausted.\n/// The use-case for this is clients who are bandwidth limited and where every byte counts.\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub enum RowSizeHint {\n    /// Each row in `rows_data` is of the same fixed size as specified here.\n    FixedSize(RowSize),\n    /// The offsets into `rows_data` defining the boundaries of each row.\n    /// Only stores the offset to the start of each row.\n    /// The ends of each row is inferred from the start of the next row, or `rows_data.len()`.\n    /// The behavior of this is identical to that of `PackedStr`.\n    RowOffsets(Arc<[RowOffset]>),\n}\n\nimpl Default for RowSizeHint {\n    fn default() -> Self {\n        Self::RowOffsets([].into())\n    }\n}\n\nimpl RowSizeHint {\n    fn index_to_range(&self, index: usize, data_end: usize) -> Option<Range<usize>> {\n        match self {\n            Self::FixedSize(size) => {\n                let size = *size as usize;\n                let start = index * size;\n                if start >= data_end {\n                    // We've reached beyond `data_end`,\n                    // so this is a row that doesn't exist, so we are beyond the count.\n                    return None;\n                }\n                let end = (index + 1) * size;\n                Some(start..end)\n            }\n            Self::RowOffsets(offsets) => {\n                let offsets = offsets.as_ref();\n                let start = *offsets.get(index)? as usize;\n                // The end is either the start of the next element or the end.\n                let end = offsets.get(index + 1).map(|e| *e as usize).unwrap_or(data_end);\n                Some(start..end)\n            }\n        }\n    }\n}\n\nimpl RowListLen for BsatnRowList {\n    fn len(&self) -> usize {\n        match &self.size_hint {\n            // `size != 0` is always the case for `FixedSize`.\n            RowSizeHint::FixedSize(size) => self.rows_data.as_ref().len() / *size as usize,\n            RowSizeHint::RowOffsets(offsets) => offsets.as_ref().len(),\n        }\n    }\n}\n\nimpl ByteListLen for BsatnRowList {\n    /// Returns the uncompressed size of the list in bytes\n    fn num_bytes(&self) -> usize {\n        self.rows_data.as_ref().len()\n    }\n}\n\nimpl BsatnRowList {\n    /// Returns the element at `index` in the list.\n    pub fn get(&self, index: usize) -> Option<Bytes> {\n        let data_end = self.rows_data.len();\n        let data_range = self.size_hint.index_to_range(index, data_end)?;\n        Some(self.rows_data.slice(data_range))\n    }\n\n    /// Consumes the list and returns the parts.\n    pub fn into_inner(self) -> (RowSizeHint, Bytes) {\n        (self.size_hint, self.rows_data)\n    }\n}\n\n/// An iterator over all the elements in a [`BsatnRowList`].\npub struct BsatnRowListIter<'a> {\n    list: &'a BsatnRowList,\n    index: usize,\n}\n\nimpl<'a> IntoIterator for &'a BsatnRowList {\n    type IntoIter = BsatnRowListIter<'a>;\n    type Item = Bytes;\n    fn into_iter(self) -> Self::IntoIter {\n        BsatnRowListIter { list: self, index: 0 }\n    }\n}\n\nimpl Iterator for BsatnRowListIter<'_> {\n    type Item = Bytes;\n    fn next(&mut self) -> Option<Self::Item> {\n        let index = self.index;\n        self.index += 1;\n        self.list.get(index)\n    }\n}\n\npub trait RowListLen {\n    /// Returns the length, in number of rows, not bytes, of the row list.\n    fn len(&self) -> usize;\n    /// Returns whether the list is empty or not.\n    fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n}\n\nimpl<T, L: Deref<Target = [T]>> RowListLen for L {\n    fn len(&self) -> usize {\n        self.deref().len()\n    }\n    fn is_empty(&self) -> bool {\n        self.deref().is_empty()\n    }\n}\n\npub trait ByteListLen {\n    /// Returns the uncompressed size of the list in bytes\n    fn num_bytes(&self) -> usize;\n}\n\nimpl ByteListLen for Vec<ByteString> {\n    fn num_bytes(&self) -> usize {\n        self.iter().map(|str| str.len()).sum()\n    }\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/websocket/v1.rs",
    "content": "pub use super::common::{\n    BsatnRowList, CallProcedureFlags, Compression, QuerySetId as QueryId, RowOffset, RowSize, RowSizeHint,\n};\nuse crate::{\n    energy::EnergyQuanta,\n    websocket::common::{ByteListLen, RowListLen},\n};\nuse bytes::Bytes;\nuse bytestring::ByteString;\nuse core::fmt::Debug;\nuse enum_as_inner::EnumAsInner;\nuse smallvec::SmallVec;\nuse spacetimedb_lib::{ConnectionId, Identity, TimeDuration, Timestamp};\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::{\n    de::{Deserialize, Error},\n    impl_deserialize, impl_serialize, impl_st,\n    raw_identifier::RawIdentifier,\n    ser::Serialize,\n    AlgebraicType, SpacetimeType,\n};\n\npub const TEXT_PROTOCOL: &str = \"v1.json.spacetimedb\";\npub const BIN_PROTOCOL: &str = \"v1.bsatn.spacetimedb\";\n\n/// A format / codec used by the websocket API.\n///\n/// This can be e.g., BSATN, JSON.\npub trait WebsocketFormat: Sized {\n    /// The type used for the encoding of a single item.\n    type Single: SpacetimeType + for<'de> Deserialize<'de> + Serialize + Debug + Clone;\n\n    /// The type used for the encoding of a list of items.\n    type List: SpacetimeType\n        + for<'de> Deserialize<'de>\n        + Serialize\n        + RowListLen\n        + ByteListLen\n        + Debug\n        + Clone\n        + Default;\n\n    /// The type used to encode query updates.\n    /// This type exists so that some formats, e.g., BSATN, can compress an update.\n    type QueryUpdate: SpacetimeType + for<'de> Deserialize<'de> + Serialize + Debug + Clone + Send;\n}\n\n/// Messages sent from the client to the server.\n///\n/// Parametric over the reducer argument type to enable [`ClientMessage::map_args`].\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum ClientMessage<Args> {\n    /// Request a reducer run.\n    CallReducer(CallReducer<Args>),\n    /// Register SQL queries on which to receive updates.\n    Subscribe(Subscribe),\n    /// Send a one-off SQL query without establishing a subscription.\n    OneOffQuery(OneOffQuery),\n    /// Register a SQL query to to subscribe to updates. This does not affect other subscriptions.\n    SubscribeSingle(SubscribeSingle),\n    SubscribeMulti(SubscribeMulti),\n    /// Remove a subscription to a SQL query that was added with SubscribeSingle.\n    Unsubscribe(Unsubscribe),\n    UnsubscribeMulti(UnsubscribeMulti),\n    /// Request a procedure run.\n    CallProcedure(CallProcedure<Args>),\n}\n\nimpl<Args> ClientMessage<Args> {\n    pub fn map_args<Args2>(self, f: impl FnOnce(Args) -> Args2) -> ClientMessage<Args2> {\n        match self {\n            ClientMessage::CallReducer(CallReducer {\n                reducer,\n                args,\n                request_id,\n                flags,\n            }) => ClientMessage::CallReducer(CallReducer {\n                reducer,\n                args: f(args),\n                request_id,\n                flags,\n            }),\n            ClientMessage::OneOffQuery(x) => ClientMessage::OneOffQuery(x),\n            ClientMessage::SubscribeSingle(x) => ClientMessage::SubscribeSingle(x),\n            ClientMessage::Unsubscribe(x) => ClientMessage::Unsubscribe(x),\n            ClientMessage::Subscribe(x) => ClientMessage::Subscribe(x),\n            ClientMessage::SubscribeMulti(x) => ClientMessage::SubscribeMulti(x),\n            ClientMessage::UnsubscribeMulti(x) => ClientMessage::UnsubscribeMulti(x),\n            ClientMessage::CallProcedure(CallProcedure {\n                procedure,\n                args,\n                request_id,\n                flags,\n            }) => ClientMessage::CallProcedure(CallProcedure {\n                procedure,\n                args: f(args),\n                request_id,\n                flags,\n            }),\n        }\n    }\n}\n\n/// Request a reducer run.\n///\n/// Parametric over the argument type to enable [`ClientMessage::map_args`].\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct CallReducer<Args> {\n    /// The name of the reducer to call.\n    pub reducer: RawIdentifier,\n    /// The arguments to the reducer.\n    ///\n    /// In the wire format, this will be a [`Bytes`], BSATN or JSON encoded according to the reducer's argument schema\n    /// and the enclosing message format.\n    pub args: Args,\n    /// An identifier for a client request.\n    ///\n    /// The server will include the same ID in the response [`TransactionUpdate`].\n    pub request_id: u32,\n    /// Assorted flags that can be passed when calling a reducer.\n    ///\n    /// Currently accepts 0 or 1 where the latter means\n    /// that the caller does not want to be notified about the reducer\n    /// without being subscribed to any relevant queries.\n    pub flags: CallReducerFlags,\n}\n\n#[derive(Clone, Copy, Default, PartialEq, Eq)]\npub enum CallReducerFlags {\n    /// The reducer's caller does want to be notified about the reducer completing successfully\n    /// regardless of whether the caller had subscribed to a relevant query.\n    ///\n    /// Note that updates to a reducer's caller are always sent as full updates\n    /// whether subscribed to a relevant query or not.\n    /// That is, the light tx mode setting does not apply to the reducer's caller.\n    ///\n    /// This is the default flag.\n    #[default]\n    FullUpdate,\n    /// The reducer's caller does not want to be notified about the reducer completing successfully\n    /// without having subscribed to any of the relevant queries.\n    NoSuccessNotify,\n}\n\nimpl_st!([] CallReducerFlags, AlgebraicType::U8);\nimpl_serialize!([] CallReducerFlags, (self, ser) => ser.serialize_u8(*self as u8));\nimpl_deserialize!([] CallReducerFlags, de => match de.deserialize_u8()? {\n    0 => Ok(Self::FullUpdate),\n    1 => Ok(Self::NoSuccessNotify),\n    x => Err(D::Error::custom(format_args!(\"invalid call reducer flag {x}\"))),\n});\n\n/// Sent by client to database to register a set of queries, about which the client will\n/// receive `TransactionUpdate`s.\n///\n/// After issuing a `Subscribe` message, the client will receive a single\n/// `SubscriptionUpdate` message containing every current row of every table which matches\n/// the subscribed queries. Then, after each reducer run which updates one or more\n/// subscribed rows, the client will receive a `TransactionUpdate` containing the updates.\n///\n/// A `Subscribe` message sets or replaces the entire set of queries to which the client\n/// is subscribed. If the client is previously subscribed to some set of queries `A`, and\n/// then sends a `Subscribe` message to subscribe to a set `B`, afterwards, the client\n/// will be subscribed to `B` but not `A`. In this case, the client will receive a\n/// `SubscriptionUpdate` containing every existing row that matches `B`, even if some were\n/// already in `A`.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct Subscribe {\n    /// A sequence of SQL queries.\n    pub query_strings: Box<[Box<str>]>,\n    pub request_id: u32,\n}\n\n/// Sent by client to register a subscription to single query, for which the client should receive\n/// receive relevant `TransactionUpdate`s.\n///\n/// After issuing a `SubscribeSingle` message, the client will receive a single\n/// `SubscribeApplied` message containing every current row which matches the query. Then, any\n/// time a reducer updates the query's results, the client will receive a `TransactionUpdate`\n/// containing the relevant updates.\n///\n/// If a client subscribes to queries with overlapping results, the client will receive\n/// multiple copies of rows that appear in multiple queries.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeSingle {\n    /// A single SQL `SELECT` query to subscribe to.\n    pub query: Box<str>,\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// An identifier for this subscription, which should not be used for any other subscriptions on the same connection.\n    /// This is used to refer to this subscription in Unsubscribe messages from the client and errors sent from the server.\n    /// These only have meaning given a ConnectionId.\n    pub query_id: QueryId,\n}\n\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeMulti {\n    /// A single SQL `SELECT` query to subscribe to.\n    pub query_strings: Box<[Box<str>]>,\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// An identifier for this subscription, which should not be used for any other subscriptions on the same connection.\n    /// This is used to refer to this subscription in Unsubscribe messages from the client and errors sent from the server.\n    /// These only have meaning given a ConnectionId.\n    pub query_id: QueryId,\n}\n\n/// Client request for removing a query from a subscription.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct Unsubscribe {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// The ID used in the corresponding `SubscribeSingle` message.\n    pub query_id: QueryId,\n}\n\n/// Client request for removing a query from a subscription.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct UnsubscribeMulti {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// The ID used in the corresponding `SubscribeSingle` message.\n    pub query_id: QueryId,\n}\n\n/// A one-off query submission.\n///\n/// Query should be a \"SELECT * FROM Table WHERE ...\". Other types of queries will be rejected.\n/// Multiple such semicolon-delimited queries are allowed.\n///\n/// One-off queries are identified by a client-generated messageID.\n/// To avoid data leaks, the server will NOT cache responses to messages based on UUID!\n/// It also will not check for duplicate IDs. They are just a way to match responses to messages.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct OneOffQuery {\n    pub message_id: Box<[u8]>,\n    pub query_string: Box<str>,\n}\n\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\n/// Request a procedure run.\n///\n/// Parametric over the argument type to enable [`ClientMessage::map_args`].\npub struct CallProcedure<Args> {\n    /// The name of the procedure to call.\n    pub procedure: RawIdentifier,\n    /// The arguments to the procedure.\n    ///\n    /// In the wire format, this will be a [`Bytes`], BSATN or JSON encoded according to the reducer's argument schema\n    /// and the enclosing message format.\n    pub args: Args,\n    /// An identifier for a client request.\n    ///\n    /// The server will include the same ID in the response [`ProcedureResult`].\n    pub request_id: u32,\n    /// Reserved space for future extensions.\n    pub flags: CallProcedureFlags,\n}\n\n/// Messages sent from the server to the client.\n#[derive(SpacetimeType, derive_more::From)]\n#[sats(crate = spacetimedb_lib)]\npub enum ServerMessage<F: WebsocketFormat> {\n    /// Informs of changes to subscribed rows.\n    /// This will be removed when we switch to `SubscribeSingle`.\n    InitialSubscription(InitialSubscription<F>),\n    /// Upon reducer run.\n    TransactionUpdate(TransactionUpdate<F>),\n    /// Upon reducer run, but limited to just the table updates.\n    TransactionUpdateLight(TransactionUpdateLight<F>),\n    /// After connecting, to inform client of its identity.\n    IdentityToken(IdentityToken),\n    /// Return results to a one off SQL query.\n    OneOffQueryResponse(OneOffQueryResponse<F>),\n    /// Sent in response to a `SubscribeSingle` message. This contains the initial matching rows.\n    SubscribeApplied(SubscribeApplied<F>),\n    /// Sent in response to an `Unsubscribe` message. This contains the matching rows.\n    UnsubscribeApplied(UnsubscribeApplied<F>),\n    /// Communicate an error in the subscription lifecycle.\n    SubscriptionError(SubscriptionError),\n    /// Sent in response to a `SubscribeMulti` message. This contains the initial matching rows.\n    SubscribeMultiApplied(SubscribeMultiApplied<F>),\n    /// Sent in response to an `UnsubscribeMulti` message. This contains the matching rows.\n    UnsubscribeMultiApplied(UnsubscribeMultiApplied<F>),\n    /// Sent in response to a [`CallProcedure`] message. This contains the return value.\n    ProcedureResult(ProcedureResult<F>),\n}\n\n/// The matching rows of a subscription query.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeRows<F: WebsocketFormat> {\n    /// The table ID of the query.\n    pub table_id: TableId,\n    /// The table name of the query.\n    pub table_name: RawIdentifier,\n    /// The BSATN row values.\n    pub table_rows: TableUpdate<F>,\n}\n\n/// Response to [`Subscribe`] containing the initial matching rows.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeApplied<F: WebsocketFormat> {\n    /// The request_id of the corresponding `SubscribeSingle` message.\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n    /// An identifier for the subscribed query sent by the client.\n    pub query_id: QueryId,\n    /// The matching rows for this query.\n    pub rows: SubscribeRows<F>,\n}\n\n/// Server response to a client [`Unsubscribe`] request.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct UnsubscribeApplied<F: WebsocketFormat> {\n    /// Provided by the client via the `Subscribe` message.\n    /// TODO: switch to subscription id?\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n    /// The ID included in the `SubscribeApplied` and `Unsubscribe` messages.\n    pub query_id: QueryId,\n    /// The matching rows for this query.\n    /// Note, this makes unsubscribing potentially very expensive.\n    /// To remove this in the future, we would need to send query_ids with rows in transaction updates,\n    /// and we would need clients to track which rows exist in which queries.\n    pub rows: SubscribeRows<F>,\n}\n\n/// Server response to an error at any point of the subscription lifecycle.\n/// If this error doesn't have a request_id, the client should drop all subscriptions.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscriptionError {\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n    /// Provided by the client via a [`Subscribe`] or [`Unsubscribe`] message.\n    /// [`None`] if this occurred as the result of a [`TransactionUpdate`].\n    pub request_id: Option<u32>,\n    /// Provided by the client via a [`Subscribe`] or [`Unsubscribe`] message.\n    /// [`None`] if this occurred as the result of a [`TransactionUpdate`].\n    pub query_id: Option<u32>,\n    /// The return table of the query in question.\n    /// The server is not required to set this field.\n    /// It has been added to avoid a breaking change post 1.0.\n    ///\n    /// If unset, an error results in the entire subscription being dropped.\n    /// Otherwise only queries of this table type must be dropped.\n    pub table_id: Option<TableId>,\n    /// An error message describing the failure.\n    ///\n    /// This should reference specific fragments of the query where applicable,\n    /// but should not include the full text of the query,\n    /// as the client can retrieve that from the `request_id`.\n    ///\n    /// This is intended for diagnostic purposes.\n    /// It need not have a predictable/parseable format.\n    pub error: Box<str>,\n}\n\n/// Response to [`Subscribe`] containing the initial matching rows.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeMultiApplied<F: WebsocketFormat> {\n    /// The request_id of the corresponding `SubscribeSingle` message.\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n    /// An identifier for the subscribed query sent by the client.\n    pub query_id: QueryId,\n    /// The matching rows for this query.\n    pub update: DatabaseUpdate<F>,\n}\n\n/// Server response to a client [`Unsubscribe`] request.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct UnsubscribeMultiApplied<F: WebsocketFormat> {\n    /// Provided by the client via the `Subscribe` message.\n    /// TODO: switch to subscription id?\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n    /// The ID included in the `SubscribeApplied` and `Unsubscribe` messages.\n    pub query_id: QueryId,\n    /// The matching rows for this query set.\n    /// Note, this makes unsubscribing potentially very expensive.\n    /// To remove this in the future, we would need to send query_ids with rows in transaction updates,\n    /// and we would need clients to track which rows exist in which queries.\n    pub update: DatabaseUpdate<F>,\n}\n\n/// Response to [`Subscribe`] containing the initial matching rows.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscriptionUpdate<F: WebsocketFormat> {\n    /// A [`DatabaseUpdate`] containing only inserts, the rows which match the subscription queries.\n    pub database_update: DatabaseUpdate<F>,\n    /// An identifier sent by the client in requests.\n    /// The server will include the same request_id in the response.\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration_micros: u64,\n}\n\n/// Response to [`Subscribe`] containing the initial matching rows.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct InitialSubscription<F: WebsocketFormat> {\n    /// A [`DatabaseUpdate`] containing only inserts, the rows which match the subscription queries.\n    pub database_update: DatabaseUpdate<F>,\n    /// An identifier sent by the client in requests.\n    /// The server will include the same request_id in the response.\n    pub request_id: u32,\n    /// The overall time between the server receiving a request and sending the response.\n    pub total_host_execution_duration: TimeDuration,\n}\n\n/// Received by database from client to inform of user's identity, token and client connection id.\n///\n/// The database will always send an `IdentityToken` message\n/// as the first message for a new WebSocket connection.\n/// If the client is re-connecting with existing credentials,\n/// the message will include those credentials.\n/// If the client connected anonymously,\n/// the database will generate new credentials to identify it.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct IdentityToken {\n    pub identity: Identity,\n    pub token: Box<str>,\n    pub connection_id: ConnectionId,\n}\n\n/// Received by client from database upon a reducer run.\n///\n/// Clients receive `TransactionUpdate`s only for reducers\n/// which update at least one of their subscribed rows,\n/// or for their own `Failed` or `OutOfEnergy` reducer invocations.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct TransactionUpdate<F: WebsocketFormat> {\n    /// The status of the transaction. Contains the updated rows, if successful.\n    pub status: UpdateStatus<F>,\n    /// The time when the reducer started.\n    ///\n    /// Note that [`Timestamp`] serializes as `i64` nanoseconds since the Unix epoch.\n    pub timestamp: Timestamp,\n    /// The identity of the user who requested the reducer run. For event-driven and\n    /// scheduled reducers, it is the identity of the database owner.\n    pub caller_identity: Identity,\n\n    /// The 16-byte [`ConnectionId`] of the user who requested the reducer run.\n    ///\n    /// The all-zeros id is a sentinel which denotes no meaningful value.\n    /// This can occur in the following situations:\n    /// - `init` and `update` reducers will have a `caller_connection_id`\n    ///   if and only if one was provided to the `publish` HTTP endpoint.\n    /// - Scheduled reducers will never have a `caller_connection_id`.\n    /// - Reducers invoked by WebSocket or the HTTP API will always have a `caller_connection_id`.\n    pub caller_connection_id: ConnectionId,\n    /// The original CallReducer request that triggered this reducer.\n    pub reducer_call: ReducerCallInfo<F>,\n    /// The amount of energy credits consumed by running the reducer.\n    pub energy_quanta_used: EnergyQuanta,\n    /// How long the reducer took to run.\n    pub total_host_execution_duration: TimeDuration,\n}\n\n/// Received by client from database upon a reducer run.\n///\n/// Clients receive `TransactionUpdateLight`s only for reducers\n/// which update at least one of their subscribed rows.\n/// Failed reducers result in full [`TransactionUpdate`]s\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct TransactionUpdateLight<F: WebsocketFormat> {\n    /// An identifier for a client request\n    pub request_id: u32,\n\n    /// The reducer ran successfully and its changes were committed to the database.\n    /// The rows altered in the database/ are recorded in this `DatabaseUpdate`.\n    pub update: DatabaseUpdate<F>,\n}\n\n/// Contained in a [`TransactionUpdate`], metadata about a reducer invocation.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct ReducerCallInfo<F: WebsocketFormat> {\n    /// The name of the reducer that was called.\n    ///\n    /// NOTE(centril, 1.0): For bandwidth resource constrained clients\n    /// this can encourage them to have poor naming of reducers like `a`.\n    /// We should consider not sending this at all and instead\n    /// having a startup message where the name <-> id bindings\n    /// are established between the host and the client.\n    pub reducer_name: RawIdentifier,\n    /// The numerical id of the reducer that was called.\n    pub reducer_id: u32,\n    /// The arguments to the reducer, encoded as BSATN or JSON according to the reducer's argument schema\n    /// and the client's requested protocol.\n    pub args: F::Single,\n    /// An identifier for a client request\n    pub request_id: u32,\n}\n\n/// The status of a [`TransactionUpdate`].\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub enum UpdateStatus<F: WebsocketFormat> {\n    /// The reducer ran successfully and its changes were committed to the database.\n    /// The rows altered in the database/ will be recorded in the `DatabaseUpdate`.\n    Committed(DatabaseUpdate<F>),\n    /// The reducer errored, and any changes it attempted to were rolled back.\n    /// This is the error message.\n    Failed(Box<str>),\n    /// The reducer was interrupted due to insufficient energy/funds,\n    /// and any changes it attempted to make were rolled back.\n    OutOfEnergy,\n}\n\n/// A collection of inserted and deleted rows, contained in a [`TransactionUpdate`] or [`SubscriptionUpdate`].\n#[derive(SpacetimeType, Debug, Clone, Default)]\n#[sats(crate = spacetimedb_lib)]\npub struct DatabaseUpdate<F: WebsocketFormat> {\n    pub tables: Vec<TableUpdate<F>>,\n}\n\nimpl<F: WebsocketFormat> DatabaseUpdate<F> {\n    pub fn is_empty(&self) -> bool {\n        self.tables.is_empty()\n    }\n\n    pub fn num_rows(&self) -> usize {\n        self.tables.iter().map(|t| t.num_rows()).sum()\n    }\n}\n\nimpl<F: WebsocketFormat> FromIterator<TableUpdate<F>> for DatabaseUpdate<F> {\n    fn from_iter<T: IntoIterator<Item = TableUpdate<F>>>(iter: T) -> Self {\n        DatabaseUpdate {\n            tables: iter.into_iter().collect(),\n        }\n    }\n}\n\n/// Part of a [`DatabaseUpdate`] received by client from database for alterations to a single table.\n///\n/// NOTE(centril): in 0.12 we added `num_rows` and `table_name` to the struct.\n/// These inflate the size of messages, which for some customers is the wrong default.\n/// We might want to consider `v1.spacetimedb.bsatn.lightweight`\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub struct TableUpdate<F: WebsocketFormat> {\n    /// The id of the table. Clients should prefer `table_name`, as it is a stable part of a module's API,\n    /// whereas `table_id` may change between runs.\n    pub table_id: TableId,\n    /// The name of the table.\n    ///\n    /// NOTE(centril, 1.0): we might want to remove this and instead\n    /// tell clients about changes to table_name <-> table_id mappings.\n    pub table_name: RawIdentifier,\n    /// The sum total of rows in `self.updates`,\n    pub num_rows: u64,\n    /// The actual insert and delete updates for this table.\n    pub updates: SmallVec<[F::QueryUpdate; 1]>,\n}\n\n/// Computed update for a single query, annotated with the number of matching rows.\n#[derive(Debug)]\npub struct SingleQueryUpdate<F: WebsocketFormat> {\n    pub update: F::QueryUpdate,\n    pub num_rows: u64,\n}\n\nimpl<F: WebsocketFormat> TableUpdate<F> {\n    pub fn new(table_id: TableId, table_name: RawIdentifier, update: SingleQueryUpdate<F>) -> Self {\n        Self {\n            table_id,\n            table_name,\n            num_rows: update.num_rows,\n            updates: [update.update].into(),\n        }\n    }\n\n    pub fn empty(table_id: TableId, table_name: RawIdentifier) -> Self {\n        Self {\n            table_id,\n            table_name,\n            num_rows: 0,\n            updates: SmallVec::new(),\n        }\n    }\n\n    pub fn push(&mut self, update: SingleQueryUpdate<F>) {\n        self.updates.push(update.update);\n        self.num_rows += update.num_rows;\n    }\n\n    pub fn num_rows(&self) -> usize {\n        self.num_rows as usize\n    }\n}\n\n#[derive(SpacetimeType, Debug, Clone, EnumAsInner)]\n#[sats(crate = spacetimedb_lib)]\npub enum CompressableQueryUpdate<F: WebsocketFormat> {\n    Uncompressed(QueryUpdate<F>),\n    Brotli(Bytes),\n    Gzip(Bytes),\n}\n\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub struct QueryUpdate<F: WebsocketFormat> {\n    /// When in a [`TransactionUpdate`], the matching rows of this table deleted by the transaction.\n    ///\n    /// Rows are encoded as BSATN or JSON according to the table's schema\n    /// and the client's requested protocol.\n    ///\n    /// Always empty when in an [`InitialSubscription`].\n    pub deletes: F::List,\n    /// When in a [`TransactionUpdate`], the matching rows of this table inserted by the transaction.\n    /// When in an [`InitialSubscription`], the matching rows of this table in the entire committed state.\n    ///\n    /// Rows are encoded as BSATN or JSON according to the table's schema\n    /// and the client's requested protocol.\n    pub inserts: F::List,\n}\n\n/// A response to a [`OneOffQuery`].\n/// Will contain either one error or some number of response rows.\n/// At most one of these messages will be sent in reply to any query.\n///\n/// The messageId will be identical to the one sent in the original query.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct OneOffQueryResponse<F: WebsocketFormat> {\n    pub message_id: Box<[u8]>,\n    /// If query compilation or evaluation errored, an error message.\n    pub error: Option<Box<str>>,\n\n    /// If query compilation and evaluation succeeded, a set of resulting rows, grouped by table.\n    pub tables: Box<[OneOffTable<F>]>,\n\n    /// The total duration of query compilation and evaluation on the server, in microseconds.\n    pub total_host_execution_duration: TimeDuration,\n}\n\n/// A table included as part of a [`OneOffQueryResponse`].\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct OneOffTable<F: WebsocketFormat> {\n    /// The name of the table.\n    pub table_name: RawIdentifier,\n    /// The set of rows which matched the query, encoded as BSATN or JSON according to the table's schema\n    /// and the client's requested protocol.\n    ///\n    /// TODO(centril, 1.0): Evaluate whether we want to conditionally compress these.\n    pub rows: F::List,\n}\n\n/// The result of running a procedure,\n/// including the return value of the procedure on success.\n///\n/// Sent in response to a [`CallProcedure`] message.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct ProcedureResult<F: WebsocketFormat> {\n    /// The status of the procedure run.\n    ///\n    /// Contains the return value if successful, or the error message if not.\n    pub status: ProcedureStatus<F>,\n    /// The time when the reducer started.\n    ///\n    /// Note that [`Timestamp`] serializes as `i64` nanoseconds since the Unix epoch.\n    pub timestamp: Timestamp,\n    /// The time the procedure took to run.\n    pub total_host_execution_duration: TimeDuration,\n    /// The same same client-provided identifier as in the original [`ProcedureCall`] request.\n    ///\n    /// Clients use this to correlate the response with the original request.\n    pub request_id: u32,\n}\n\n/// The status of a procedure call,\n/// including the return value on success.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub enum ProcedureStatus<F: WebsocketFormat> {\n    /// The procedure ran and returned the enclosed value.\n    ///\n    /// All user error handling happens within here;\n    /// the returned value may be a `Result` or `Option`,\n    /// or any other type to which the user may ascribe arbitrary meaning.\n    Returned(F::Single),\n    /// The reducer was interrupted due to insufficient energy/funds.\n    ///\n    /// The procedure may have performed some observable side effects before being interrupted.\n    OutOfEnergy,\n    /// The call failed in the host, e.g. due to a type error or unknown procedure name.\n    InternalError(String),\n}\n\n/// Used whenever different formats need to coexist.\n#[derive(Debug, Clone)]\npub enum FormatSwitch<B, J> {\n    Bsatn(B),\n    Json(J),\n}\n\nimpl<B1, J1> FormatSwitch<B1, J1> {\n    /// Zips together two switches.\n    pub fn zip_mut<B2, J2>(&mut self, other: FormatSwitch<B2, J2>) -> FormatSwitch<(&mut B1, B2), (&mut J1, J2)> {\n        match (self, other) {\n            (FormatSwitch::Bsatn(a), FormatSwitch::Bsatn(b)) => FormatSwitch::Bsatn((a, b)),\n            (FormatSwitch::Json(a), FormatSwitch::Json(b)) => FormatSwitch::Json((a, b)),\n            _ => panic!(\"format should be the same for both sides of the zip\"),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Default, Debug, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct JsonFormat;\n\nimpl WebsocketFormat for JsonFormat {\n    type Single = ByteString;\n    type List = Vec<ByteString>;\n    type QueryUpdate = QueryUpdate<Self>;\n}\n\n#[derive(Clone, Copy, Default, Debug, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct BsatnFormat;\n\nimpl WebsocketFormat for BsatnFormat {\n    type Single = Box<[u8]>;\n    type List = BsatnRowList;\n    type QueryUpdate = CompressableQueryUpdate<Self>;\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/websocket/v2.rs",
    "content": "pub use super::common::{BsatnRowList, CallProcedureFlags, QuerySetId};\nuse bytes::Bytes;\nuse spacetimedb_lib::{ConnectionId, Identity, TimeDuration, Timestamp};\npub use spacetimedb_sats::SpacetimeType;\nuse spacetimedb_sats::{\n    de::Error, impl_deserialize, impl_serialize, impl_st, raw_identifier::RawIdentifier, AlgebraicType,\n};\n\npub const BIN_PROTOCOL: &str = \"v2.bsatn.spacetimedb\";\n\n/// Messages sent by the client to the server.\n///\n/// Each client message contains a `request_id`, a client-supplied integer ID.\n/// The server assigns no meaning to this value, but encloses the same value in its response [`ServerMessage`].\n/// Clients can use `request_id`s to correlate requests and responses.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum ClientMessage {\n    /// Add a new set of subscribed queries to construct a local materialized view of matching rows.\n    Subscribe(Subscribe),\n    /// Remove a previously-registered set of subscribed queries to stop receiving updates on its view.\n    Unsubscribe(Unsubscribe),\n    /// Run a query once and receive its results at a single point in time, without real-time updates.\n    OneOffQuery(OneOffQuery),\n    /// Invoke a reducer, a transactional non-side-effecting function which runs in the database.\n    CallReducer(CallReducer),\n    /// Invoke a procedure, a non-transactional side-effecting function which runs in the database.\n    CallProcedure(CallProcedure),\n}\n\n/// Sent by client to register a subscription to a new query set\n/// for which the client should receive [`QuerySetUpdate`]s in its [`TransactionUpdate`]s.\n///\n/// Each subscribed query set is identified by a client-supplied [`QuerySetId`],\n/// which should be unique within that client's connection.\n/// The server will include that [`QuerySetId`] in updates with the matching rows,\n/// and the client can later send that [`QuerySetId`] in an [`Unsubscribe`] message to end the subscription.\n///\n/// If the enclosed queries are valid and compute successfully,\n/// the server will respond with a [`SubscribeApplied`] message marked with the same `request_id` and [`QuerySetId`]\n/// containing the initial matching rows,\n/// and will then send matching inserts and deletes in [`QuerySetUpdate`]s enclosed in [`TransactionUpdate`] messages\n/// as the changes occur.\n///\n/// If the enclosed queries are invalid or fail to compute, the server will respond with a [`SubscriptionError`] message.\n/// If the queries become invalid after an initial successful application,\n/// the server may send a [`SubscribeApplied`], some number of [`TransactionUpdate`]s, and then a [`SubscriptionError`].\n/// After receiving a [`SubscriptionError`], the client should discard all previously-received rows for that [`QuerySetId`]\n/// and should not expect to receive updates for it in the future.\n/// That [`QuerySetId`] may then be re-used at the client's discretion.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct Subscribe {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// An identifier for this subscription,\n    /// which should not be used for any other subscriptions on the same connection.\n    ///\n    /// This is used to refer to this subscription in [`Unsubscribe`] messages from the client\n    /// and in various responses from the server.\n    /// These only have meaning given a [`ConnectionId`]; they are not global.\n    pub query_set_id: QuerySetId,\n\n    /// A set of queries to subscribe to, each a single SQL `SELECT` statement.\n    pub query_strings: Box<[Box<str>]>,\n}\n\n/// Sent by client to end a subscription which was previously added in a [`Subscribe`] message.\n///\n/// After the server processes an unsubscribe message, it will send an [`UnsubscribeApplied`] as confirmation.\n/// Following the [`UnsubscribeApplied`], the server will not reference the enclosed [`QuerySetId`] again,\n/// and so it may be reused.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct Unsubscribe {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// The ID used in the corresponding [`Subscribe`] message.\n    pub query_set_id: QuerySetId,\n\n    pub flags: UnsubscribeFlags,\n}\n\n#[derive(Clone, Copy, Default, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum UnsubscribeFlags {\n    #[default]\n    Default = 0,\n    // If set, the server will send the full set of rows to be removed from the client cache.\n    SendDroppedRows = 1,\n}\n\n/// Sent by the client to perform a query at a single point in time.\n///\n/// Unlike subscriptions registered by [`Subscribe`], this query will not receive real-time updates.\n///\n/// The server will respond with a [`OneOffQueryResponse`] message containing the same `request_id`\n/// and the status of the query, either the matching rows or an error message.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct OneOffQuery {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// A single SQL `SELECT` statement.\n    pub query_string: Box<str>,\n}\n\n/// Sent by the client to invoke a reducer, a transactional non-side-effecting database function.\n///\n/// After the reducer runs, the server will respond with a [`CallReducerResult`] message containing the same `request_id`\n/// and the status of the run, either the return value and [`TransactionUpdate`] or an error.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct CallReducer {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// Reserved 0.\n    pub flags: CallReducerFlags,\n\n    /// The name of the reducer to call.\n    pub reducer: Box<str>,\n\n    /// The arguments to the reducer.\n    ///\n    /// A BSATN-encoded [`ProductValue`] which meets the reducer's argument schema.\n    pub args: Bytes,\n}\n\n#[derive(Clone, Copy, Default, PartialEq, Eq)]\npub enum CallReducerFlags {\n    #[default]\n    Default,\n}\n\nimpl_st!([] CallReducerFlags, AlgebraicType::U8);\nimpl_serialize!([] CallReducerFlags, (self, ser) => ser.serialize_u8(*self as u8));\nimpl_deserialize!([] CallReducerFlags, de => match de.deserialize_u8()? {\n    0 => Ok(Self::Default),\n    x => Err(D::Error::custom(format_args!(\"invalid call reducer flag {x}\"))),\n});\n\n/// Sent by the client to invoke a procedure, a non-transactional side-effecting database function.\n///\n/// After the procedure runs, the server will respond with a [`CallProcedureResult`] message containing the same `request_id`\n/// and the status of the run, either the return value or an error.\n#[derive(SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct CallProcedure {\n    /// An identifier for a client request.\n    pub request_id: u32,\n\n    /// Reserved 0.\n    pub flags: CallProcedureFlags,\n\n    /// The name of the procedure to call.\n    pub procedure: Box<str>,\n\n    /// The arguments to the procedure.\n    ///\n    /// A BSATN-encoded [`ProductValue`] which meets the procedure's argument schema.\n    pub args: Bytes,\n}\n\n/// Messages sent by the server to the client in response to requests or database events.\n///\n/// Server messages which are responses to client messages will contain a `request_id`.\n/// This will take the same value as the client supplied in their request.\n/// Clients can use `request_id`s to correlate requests and responses.\n#[derive(SpacetimeType, derive_more::From, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub enum ServerMessage {\n    /// The first message sent upon a successful connection.\n    /// Contains information about the client's identity and authentication.\n    InitialConnection(InitialConnection),\n    /// In response to a [`Subscribe`] message, after a new query set has been added, containing its initial matching rows.\n    SubscribeApplied(SubscribeApplied),\n    /// In response to an [`Unsubscribe`] message, confirming that a query set has been removed.\n    UnsubscribeApplied(UnsubscribeApplied),\n    /// Notifies the client that a subscription to a query set has failed, either during initial application\n    /// or when computing a [`QuerySetUpdate`] for a [`TransactionUpdate`].\n    SubscriptionError(SubscriptionError),\n    /// Sent after the database runs a transaction, to notify the client of any changes to its subscribed query sets\n    /// in [`QuerySetUpdate`]s.\n    TransactionUpdate(TransactionUpdate),\n    /// Sent in response to a [`OneOffQuery`] message, containing the matching rows or error message.\n    OneOffQueryResult(OneOffQueryResult),\n    /// Sent in response to a [`CallReducer`] message, containing the reducer's exit status and, if it committed,\n    /// the [`TransactionUpdate`] for that reducer's transaction.\n    ReducerResult(ReducerResult),\n    /// Sent in response to a [`CallProcedure`] message, containing the procedure's exit status.\n    ProcedureResult(ProcedureResult),\n}\n\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct InitialConnection {\n    pub identity: Identity,\n    pub connection_id: ConnectionId,\n    pub token: Box<str>,\n}\n\n/// Response to [`Subscribe`] containing the initial matching rows.\n///\n/// This message's `request_id` and `query_set_id` will match those the client provided in the [`Subscribe`] message.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscribeApplied {\n    /// The request_id of the corresponding [`Subscribe`] message.\n    pub request_id: u32,\n    /// An identifier for the subscribed query set provided by the client.\n    pub query_set_id: QuerySetId,\n    /// The matching rows for this query.\n    pub rows: QueryRows,\n}\n\n/// Matching rows resident in tables at the time a query ran,\n/// used in contexts where we're not sending insert/delete deltas,\n/// like [`SubscribeApplied`].\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct QueryRows {\n    pub tables: Box<[SingleTableRows]>,\n}\n\n/// Matching rows resident in a table at the time a query ran,\n/// used in contexts where we're not sending insert/delete deltas,\n/// like the [`QueryRows`] of a [`SubscribeApplied`], and [`OneOffQueryResponse`].\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct SingleTableRows {\n    pub table: RawIdentifier,\n    pub rows: BsatnRowList,\n}\n\n/// Server response to a client [`Unsubscribe`] request.\n///\n/// This message's `request_id` and `query_set_id` will match those the client provided in the [`Unsubscribe`] message.\n///\n/// After receiving this message, the client will no longer receive any [`QuerySetUpdate`]s for the included [`QuerySetId`].\n/// That [`QuerySetId`] may then be re-used at the client's discretion.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct UnsubscribeApplied {\n    /// Provided by the client via the `Subscribe` message.\n    pub request_id: u32,\n    /// The ID included in the `SubscribeApplied` and `Unsubscribe` messages.\n    pub query_set_id: QuerySetId,\n    /// Rows to be removed from the client cache. Only populated if the Unsubscribe message requested it with the SendDroppedRows flag.\n    pub rows: Option<QueryRows>,\n}\n\n/// Server response to an error at any point of the subscription lifecycle.\n///\n/// If initial compilation or computation of a query fails, the server will send this message\n/// in lieu of a [`SubscribeApplied`].\n/// In that case, the `request_id` will be `Some` and will match the one the client supplied in the [`Subscribe`] message.\n///\n/// If a query fails after being applied, e.g. during recompilation or incremental evaluation,\n/// the server will send this message with `request_id` set to `None`.\n///\n/// In either case, this message will have its `query_set_id` set to the one provided by the client\n/// to identify the failed query set.\n/// After receiving this message, the client should consider the subscription to that query set to have ended,\n/// should discard all previously-received matching rows,\n/// and should not expect to receive any further [`QuerySetUpdate`]s for that [`QuerySetId`].\n/// That [`QuerySetId`] may then be re-used at the client's discretion.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct SubscriptionError {\n    /// Provided by the client via a [`Subscribe`] message.\n    /// [`None`] if this occurred as the result of a [`TransactionUpdate`].\n    pub request_id: Option<u32>,\n    /// Provided by the client via a [`Subscribe`] message.\n    ///\n    /// After receiving this message, the client should drop all its rows from this [`QuerySetId`],\n    /// and should not expect to receive any additional updates for that query set.\n    pub query_set_id: QuerySetId,\n    /// An error message describing the failure.\n    ///\n    /// This should reference specific fragments of the query where applicable,\n    /// but should not include the full text of the query,\n    /// as the client can retrieve that from the `request_id` or `query_set_id`.\n    ///\n    /// This is intended for diagnostic purposes.\n    /// It need not have a predictable/parseable format.\n    pub error: Box<str>,\n}\n\n/// Sent by the server to the client after a transaction runs and commits successfully in the database,\n/// containing [`QuerySetUpdate`]s for each of the client's subscribed query sets\n/// whose results were affected by the transaction.\n///\n/// If a transaction does not affect a particular query set,\n/// the transaction update will not contain a [`QuerySetUpdate`] for that set.\n///\n/// If none of a client's query sets were affected by a transaction,\n/// they will not receive an empty [`TransactionUpdate`].\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct TransactionUpdate {\n    pub query_sets: Box<[QuerySetUpdate]>,\n}\n\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct QuerySetUpdate {\n    pub query_set_id: QuerySetId,\n    // Updates are grouped by table, but the server may send multiple TableUpdates for the same table.\n    pub tables: Box<[TableUpdate]>,\n}\n\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct TableUpdate {\n    pub table_name: RawIdentifier,\n    pub rows: Box<[TableUpdateRows]>,\n}\n\n/// The rows of a [`TableUpdate`], separated based on the kind of table.\n///\n/// Regular \"persistent\" tables will include a list of inserted rows and a list of deleted rows.\n/// Event tables, whose rows are not persistent, will instead include a single list of event rows.\n///\n/// We are explicit about the row type, instead of relying on the client to know which tables\n/// are event tables, since we may want to be able to join events with persistent tables in\n/// a way that could end up with event table-ish rows being sent for a table that is normally\n/// a persistent table.\n///\n/// In the future, we may add additional variants to this enum.\n/// In particular, we may add a variant for in-place updates of rows for tables with primary keys.\n/// Note that clients will need to opt in to using this new variant,\n/// to preserve compatibility of clients which predate the new variant.\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub enum TableUpdateRows {\n    PersistentTable(PersistentTableRows),\n    EventTable(EventTableRows),\n}\n\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub struct PersistentTableRows {\n    pub inserts: BsatnRowList,\n    pub deletes: BsatnRowList,\n}\n\n#[derive(SpacetimeType, Debug, Clone)]\n#[sats(crate = spacetimedb_lib)]\npub struct EventTableRows {\n    pub events: BsatnRowList,\n}\n\n/// Response to [`OneOffQuery`] containing the matching rows or error.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct OneOffQueryResult {\n    /// The request_id of the corresponding `SubscribeSingle` message.\n    pub request_id: u32,\n    /// The matching rows for this query, or an error message if computation failed.\n    ///\n    /// This error message should follow the same format as [`SubscriptionError::error`].\n    pub result: Result<QueryRows, Box<str>>,\n}\n\n/// The result of running a reducer, including its return value and [`TransactionUpdate`] on success,\n/// or its error on failure.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct ReducerResult {\n    /// The request_id of the corresponding `SubscribeSingle` message.\n    pub request_id: u32,\n    /// The time when the reducer started.\n    ///\n    /// Note that [`Timestamp`] serializes as `i64` nanoseconds since the Unix epoch.\n    pub timestamp: Timestamp,\n    pub result: ReducerOutcome,\n}\n\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub enum ReducerOutcome {\n    /// The reducer returned successfully and its transaction committed.\n    /// The return value and [`TransactionUpdate`] are included here.\n    Ok(ReducerOk),\n    /// The reducer returned successfully and its transaction committed,\n    /// but its return value was zero bytes and its [`TransactionUpdate`] contained zero [`QuerySetUpdate`]s.\n    ///\n    /// This variant is an optimization which saves 8 bytes of wire size,\n    /// due to the BSATN format's using 4 bytes for the length of a variable-length object,\n    /// such as the `ret_value` of [`ReducerOk`] and the `query_sets` of [`TransactionUpdate`].\n    OkEmpty,\n    /// The reducer returned an expected, structured error,\n    /// and its transaction did not commit.\n    ///\n    /// The payload is a BSATN-encoded value of the reducer's error return type.\n    Err(Bytes),\n    /// The reducer panicked, returned an unexpected and unstructured error, or failed to run due to a SpacetimeDB internal error.\n    ///\n    /// The payload is an error message, which is intended for diagnostic purposes only,\n    /// and is not intended to have a stable or parseable format.\n    InternalError(Box<str>),\n}\n\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct ReducerOk {\n    /// Value returned by the reducer.\n    pub ret_value: Bytes,\n    /// Transaction update with rows being broadcast for this clients query sets.\n    pub transaction_update: TransactionUpdate,\n}\n\n/// The result of running a procedure,\n/// including the return value of the procedure on success.\n///\n/// Sent in response to a [`CallProcedure`] message.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub struct ProcedureResult {\n    /// The status of the procedure run.\n    ///\n    /// Contains the return value if successful, or the error message if not.\n    pub status: ProcedureStatus,\n    /// The time when the reducer started.\n    ///\n    /// Note that [`Timestamp`] serializes as `i64` nanoseconds since the Unix epoch.\n    pub timestamp: Timestamp,\n    /// The time the procedure took to run.\n    pub total_host_execution_duration: TimeDuration,\n    /// The same same client-provided identifier as in the original [`ProcedureCall`] request.\n    ///\n    /// Clients use this to correlate the response with the original request.\n    pub request_id: u32,\n}\n\n/// The status of a procedure call,\n/// including the return value on success.\n#[derive(SpacetimeType, Debug)]\n#[sats(crate = spacetimedb_lib)]\npub enum ProcedureStatus {\n    /// The procedure ran and returned the enclosed value.\n    ///\n    /// All user error handling happens within here;\n    /// the returned value may be a `Result` or `Option`,\n    /// or any other type to which the user may ascribe arbitrary meaning.\n    Returned(Bytes),\n    /// The call failed in the host, e.g. due to a type error or unknown procedure name.\n    InternalError(Box<str>),\n}\n"
  },
  {
    "path": "crates/client-api-messages/src/websocket.rs",
    "content": "//! Messages sent over the SpacetimeDB WebSocket protocol.\n//!\n//! Client -> Server messages are encoded as [`ClientMessage`].\n//! Server -> Client messages are encoded as [`ServerMessage`].\n//!\n//! Any changes to this file must be paired with a change to the WebSocket protocol identifiers\n//! defined in `crates/client-api/src/routes/subscribe.rs`,\n//! and be paired with changes to all of:\n//!\n//! - The C# SDK.\n//! - The TypeScript SDK.\n//! - The SpacetimeDB website.\n//!\n//! Changes to the Rust SDK are not necessarily required, as it depends on this crate\n//! rather than using an external mirror of this schema.\n\npub mod common;\npub mod v1;\npub mod v2;\n"
  },
  {
    "path": "crates/client-api-messages/ws_schema-2.json",
    "content": "{\"V8BackCompat\":{\"typespace\":{\"types\":[{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"CallReducer\"},\"algebraic_type\":{\"Ref\":1}},{\"name\":{\"some\":\"Subscribe\"},\"algebraic_type\":{\"Ref\":2}},{\"name\":{\"some\":\"OneOffQuery\"},\"algebraic_type\":{\"Ref\":3}},{\"name\":{\"some\":\"SubscribeSingle\"},\"algebraic_type\":{\"Ref\":4}},{\"name\":{\"some\":\"SubscribeMulti\"},\"algebraic_type\":{\"Ref\":6}},{\"name\":{\"some\":\"Unsubscribe\"},\"algebraic_type\":{\"Ref\":7}},{\"name\":{\"some\":\"UnsubscribeMulti\"},\"algebraic_type\":{\"Ref\":8}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"reducer\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"args\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"flags\"},\"algebraic_type\":{\"U8\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"query_strings\"},\"algebraic_type\":{\"Array\":{\"String\":[]}}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"message_id\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}},{\"name\":{\"some\":\"query_string\"},\"algebraic_type\":{\"String\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"query\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"id\"},\"algebraic_type\":{\"U32\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"query_strings\"},\"algebraic_type\":{\"Array\":{\"String\":[]}}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}}]}},{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"InitialSubscription\"},\"algebraic_type\":{\"Ref\":10}},{\"name\":{\"some\":\"TransactionUpdate\"},\"algebraic_type\":{\"Ref\":17}},{\"name\":{\"some\":\"TransactionUpdateLight\"},\"algebraic_type\":{\"Ref\":21}},{\"name\":{\"some\":\"IdentityToken\"},\"algebraic_type\":{\"Ref\":22}},{\"name\":{\"some\":\"OneOffQueryResponse\"},\"algebraic_type\":{\"Ref\":23}},{\"name\":{\"some\":\"SubscribeApplied\"},\"algebraic_type\":{\"Ref\":25}},{\"name\":{\"some\":\"UnsubscribeApplied\"},\"algebraic_type\":{\"Ref\":27}},{\"name\":{\"some\":\"SubscriptionError\"},\"algebraic_type\":{\"Ref\":28}},{\"name\":{\"some\":\"SubscribeMultiApplied\"},\"algebraic_type\":{\"Ref\":29}},{\"name\":{\"some\":\"UnsubscribeMultiApplied\"},\"algebraic_type\":{\"Ref\":30}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"database_update\"},\"algebraic_type\":{\"Ref\":11}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"total_host_execution_duration\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__time_duration_micros__\"},\"algebraic_type\":{\"I64\":[]}}]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"tables\"},\"algebraic_type\":{\"Array\":{\"Ref\":12}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"table_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"table_name\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"num_rows\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"updates\"},\"algebraic_type\":{\"Array\":{\"Ref\":13}}}]}},{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"Uncompressed\"},\"algebraic_type\":{\"Ref\":14}},{\"name\":{\"some\":\"Brotli\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}},{\"name\":{\"some\":\"Gzip\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"deletes\"},\"algebraic_type\":{\"Ref\":15}},{\"name\":{\"some\":\"inserts\"},\"algebraic_type\":{\"Ref\":15}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"size_hint\"},\"algebraic_type\":{\"Ref\":16}},{\"name\":{\"some\":\"rows_data\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}}]}},{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"FixedSize\"},\"algebraic_type\":{\"U16\":[]}},{\"name\":{\"some\":\"RowOffsets\"},\"algebraic_type\":{\"Array\":{\"U64\":[]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"status\"},\"algebraic_type\":{\"Ref\":18}},{\"name\":{\"some\":\"timestamp\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__timestamp_micros_since_unix_epoch__\"},\"algebraic_type\":{\"I64\":[]}}]}}},{\"name\":{\"some\":\"caller_identity\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__identity__\"},\"algebraic_type\":{\"U256\":[]}}]}}},{\"name\":{\"some\":\"caller_connection_id\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__connection_id__\"},\"algebraic_type\":{\"U128\":[]}}]}}},{\"name\":{\"some\":\"reducer_call\"},\"algebraic_type\":{\"Ref\":19}},{\"name\":{\"some\":\"energy_quanta_used\"},\"algebraic_type\":{\"Ref\":20}},{\"name\":{\"some\":\"total_host_execution_duration\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__time_duration_micros__\"},\"algebraic_type\":{\"I64\":[]}}]}}}]}},{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"Committed\"},\"algebraic_type\":{\"Ref\":11}},{\"name\":{\"some\":\"Failed\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"OutOfEnergy\"},\"algebraic_type\":{\"Product\":{\"elements\":[]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"reducer_name\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"reducer_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"args\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"quanta\"},\"algebraic_type\":{\"U128\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"update\"},\"algebraic_type\":{\"Ref\":11}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"identity\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__identity__\"},\"algebraic_type\":{\"U256\":[]}}]}}},{\"name\":{\"some\":\"token\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"connection_id\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__connection_id__\"},\"algebraic_type\":{\"U128\":[]}}]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"message_id\"},\"algebraic_type\":{\"Array\":{\"U8\":[]}}},{\"name\":{\"some\":\"error\"},\"algebraic_type\":{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"some\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"none\"},\"algebraic_type\":{\"Product\":{\"elements\":[]}}}]}}},{\"name\":{\"some\":\"tables\"},\"algebraic_type\":{\"Array\":{\"Ref\":24}}},{\"name\":{\"some\":\"total_host_execution_duration\"},\"algebraic_type\":{\"Product\":{\"elements\":[{\"name\":{\"some\":\"__time_duration_micros__\"},\"algebraic_type\":{\"I64\":[]}}]}}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"table_name\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"rows\"},\"algebraic_type\":{\"Ref\":15}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"total_host_execution_duration_micros\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}},{\"name\":{\"some\":\"rows\"},\"algebraic_type\":{\"Ref\":26}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"table_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"table_name\"},\"algebraic_type\":{\"String\":[]}},{\"name\":{\"some\":\"table_rows\"},\"algebraic_type\":{\"Ref\":12}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"total_host_execution_duration_micros\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}},{\"name\":{\"some\":\"rows\"},\"algebraic_type\":{\"Ref\":26}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"total_host_execution_duration_micros\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"some\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"none\"},\"algebraic_type\":{\"Product\":{\"elements\":[]}}}]}}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"some\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"none\"},\"algebraic_type\":{\"Product\":{\"elements\":[]}}}]}}},{\"name\":{\"some\":\"table_id\"},\"algebraic_type\":{\"Sum\":{\"variants\":[{\"name\":{\"some\":\"some\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"none\"},\"algebraic_type\":{\"Product\":{\"elements\":[]}}}]}}},{\"name\":{\"some\":\"error\"},\"algebraic_type\":{\"String\":[]}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"total_host_execution_duration_micros\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}},{\"name\":{\"some\":\"update\"},\"algebraic_type\":{\"Ref\":11}}]}},{\"Product\":{\"elements\":[{\"name\":{\"some\":\"request_id\"},\"algebraic_type\":{\"U32\":[]}},{\"name\":{\"some\":\"total_host_execution_duration_micros\"},\"algebraic_type\":{\"U64\":[]}},{\"name\":{\"some\":\"query_id\"},\"algebraic_type\":{\"Ref\":5}},{\"name\":{\"some\":\"update\"},\"algebraic_type\":{\"Ref\":11}}]}}]},\"tables\":[],\"reducers\":[],\"misc_exports\":[{\"TypeAlias\":{\"name\":\"ClientMessage\",\"ty\":0}},{\"TypeAlias\":{\"name\":\"CallReducer\",\"ty\":1}},{\"TypeAlias\":{\"name\":\"Subscribe\",\"ty\":2}},{\"TypeAlias\":{\"name\":\"OneOffQuery\",\"ty\":3}},{\"TypeAlias\":{\"name\":\"SubscribeSingle\",\"ty\":4}},{\"TypeAlias\":{\"name\":\"QueryId\",\"ty\":5}},{\"TypeAlias\":{\"name\":\"SubscribeMulti\",\"ty\":6}},{\"TypeAlias\":{\"name\":\"Unsubscribe\",\"ty\":7}},{\"TypeAlias\":{\"name\":\"UnsubscribeMulti\",\"ty\":8}},{\"TypeAlias\":{\"name\":\"ServerMessage\",\"ty\":9}},{\"TypeAlias\":{\"name\":\"InitialSubscription\",\"ty\":10}},{\"TypeAlias\":{\"name\":\"DatabaseUpdate\",\"ty\":11}},{\"TypeAlias\":{\"name\":\"TableUpdate\",\"ty\":12}},{\"TypeAlias\":{\"name\":\"CompressableQueryUpdate\",\"ty\":13}},{\"TypeAlias\":{\"name\":\"QueryUpdate\",\"ty\":14}},{\"TypeAlias\":{\"name\":\"BsatnRowList\",\"ty\":15}},{\"TypeAlias\":{\"name\":\"RowSizeHint\",\"ty\":16}},{\"TypeAlias\":{\"name\":\"TransactionUpdate\",\"ty\":17}},{\"TypeAlias\":{\"name\":\"UpdateStatus\",\"ty\":18}},{\"TypeAlias\":{\"name\":\"ReducerCallInfo\",\"ty\":19}},{\"TypeAlias\":{\"name\":\"EnergyQuanta\",\"ty\":20}},{\"TypeAlias\":{\"name\":\"TransactionUpdateLight\",\"ty\":21}},{\"TypeAlias\":{\"name\":\"IdentityToken\",\"ty\":22}},{\"TypeAlias\":{\"name\":\"OneOffQueryResponse\",\"ty\":23}},{\"TypeAlias\":{\"name\":\"OneOffTable\",\"ty\":24}},{\"TypeAlias\":{\"name\":\"SubscribeApplied\",\"ty\":25}},{\"TypeAlias\":{\"name\":\"SubscribeRows\",\"ty\":26}},{\"TypeAlias\":{\"name\":\"UnsubscribeApplied\",\"ty\":27}},{\"TypeAlias\":{\"name\":\"SubscriptionError\",\"ty\":28}},{\"TypeAlias\":{\"name\":\"SubscribeMultiApplied\",\"ty\":29}},{\"TypeAlias\":{\"name\":\"UnsubscribeMultiApplied\",\"ty\":30}}]}}"
  },
  {
    "path": "crates/codegen/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-codegen\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Client codegen for SpacetimeDB\"\nrust-version.workspace = true\n\n[dependencies]\nspacetimedb-data-structures = { workspace = true, features = [\"serde\"] }\nspacetimedb-lib.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-schema.workspace = true\n\nanyhow.workspace = true\nconvert_case.workspace = true\nitertools.workspace = true\nserde_json.workspace = true\n\n[dev-dependencies]\nfs-err.workspace = true\ninsta.workspace = true\nregex.workspace = true\nspacetimedb-testing = { path = \"../testing\" }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/codegen/examples/regen-cpp-moduledef.rs",
    "content": "//! This script is used to generate the C++ bindings for the `RawModuleDef` type.\n//! Run `cargo run --example regen-cpp-moduledef` to update C++ bindings whenever the module definition changes.\n#![allow(clippy::disallowed_macros)]\n\nuse fs_err as fs;\nuse spacetimedb_codegen::{cpp, generate, CodegenOptions, OutputFile};\nuse spacetimedb_lib::db::raw_def::v10::{RawModuleDefV10, RawModuleDefV10Builder};\nuse spacetimedb_lib::RawModuleDef;\nuse spacetimedb_schema::def::ModuleDef;\nuse std::path::Path;\n\nfn main() -> anyhow::Result<()> {\n    let mut builder = RawModuleDefV10Builder::new();\n    builder.add_type::<RawModuleDef>();\n    builder.add_type::<RawModuleDefV10>();\n    let module = builder.finish();\n\n    // Build relative path from the codegen crate to the C++ Module Library autogen directory\n    let manifest_dir = env!(\"CARGO_MANIFEST_DIR\");\n    let dir = Path::new(manifest_dir)\n        .parent()\n        .unwrap()\n        .join(\"bindings-cpp/include/spacetimedb/internal/autogen\");\n\n    println!(\"Target directory path: {}\", dir.display());\n\n    // Create the autogen directory if it doesn't exist\n    if dir.exists() {\n        fs::remove_dir_all(&dir)?;\n    }\n    fs::create_dir_all(&dir)?;\n\n    let module: ModuleDef = module.try_into()?;\n    generate(\n        &module,\n        &cpp::Cpp {\n            namespace: \"SpacetimeDB::Internal\",\n        },\n        &CodegenOptions::default(),\n    )\n    .into_iter()\n    .try_for_each(|OutputFile { filename, code }| {\n        // Remove any prefix and just use the filename\n        let filename = if let Some(name) = filename.strip_prefix(\"Types/\") {\n            name\n        } else {\n            &filename\n        };\n\n        println!(\"Generating {}\", filename);\n        fs::write(dir.join(filename), code)\n    })?;\n\n    println!(\"C++ autogen files written to: {}\", dir.display());\n    Ok(())\n}\n"
  },
  {
    "path": "crates/codegen/examples/regen-csharp-moduledef.rs",
    "content": "//! This script is used to generate the C# bindings for the `RawModuleDef` type.\n//! Run `cargo run --example regen-csharp-moduledef` to update C# bindings whenever the module definition changes.\n\nuse fs_err as fs;\nuse regex::Regex;\nuse spacetimedb_codegen::{csharp, generate, CodegenOptions, OutputFile};\nuse spacetimedb_lib::{RawModuleDef, RawModuleDefV8};\nuse spacetimedb_schema::def::ModuleDef;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\nmacro_rules! regex_replace {\n    ($value:expr, $re:expr, $replace:expr) => {{\n        static RE: OnceLock<Regex> = OnceLock::new();\n        RE.get_or_init(|| Regex::new($re).unwrap())\n            .replace_all($value, $replace)\n    }};\n}\n\nfn main() -> anyhow::Result<()> {\n    let module = RawModuleDefV8::with_builder(|module| {\n        module.add_type::<RawModuleDef>();\n    });\n\n    let dir = &Path::new(concat!(\n        env!(\"CARGO_MANIFEST_DIR\"),\n        \"/../bindings-csharp/Runtime/Internal/Autogen\"\n    ))\n    .canonicalize()?;\n\n    fs::remove_dir_all(dir)?;\n    fs::create_dir(dir)?;\n\n    let module: ModuleDef = module.try_into()?;\n    generate(\n        &module,\n        &csharp::Csharp {\n            namespace: \"SpacetimeDB.Internal\",\n        },\n        &CodegenOptions::default(),\n    )\n    .into_iter()\n    .try_for_each(|OutputFile { filename, code }| {\n        // Skip anything but raw types (in particular, this will skip top-level SpacetimeDBClient.g.cs which we don't need).\n        let Some(filename) = filename.strip_prefix(\"Types/\") else {\n            return Ok(());\n        };\n\n        // Someday we might replace custom BSATN types with autogenerated ones as well,\n        // but for now they're not very large and our copies are somewhat more optimised.\n        //\n        // Ignore those types and replace their references with our own with plain old regexes.\n        if filename == \"AlgebraicType.g.cs\" || filename.starts_with(\"SumType\") || filename.starts_with(\"ProductType\") {\n            return Ok(());\n        }\n\n        // CaseConversionPolicy is part of the public API — move it to\n        // the SpacetimeDB namespace so users don't have to write\n        // SpacetimeDB.Internal.CaseConversionPolicy.\n        let code = if filename == \"CaseConversionPolicy.g.cs\" {\n            regex_replace!(&code, r\"namespace SpacetimeDB\\.Internal\", \"namespace SpacetimeDB\")\n        } else {\n            // In other autogen files, qualify the type name in type position\n            // (before a space+identifier) so it resolves after the namespace move.\n            // The pattern \"CaseConversionPolicy CaseConversionPolicy\" becomes\n            // \"SpacetimeDB.CaseConversionPolicy CaseConversionPolicy\".\n            regex_replace!(\n                &code,\n                r\"\\bCaseConversionPolicy(\\s+)CaseConversionPolicy\\b\",\n                \"SpacetimeDB.CaseConversionPolicy${1}CaseConversionPolicy\"\n            )\n        };\n\n        let code = regex_replace!(&code, r\"\\bAlgebraicType\\b\", \"SpacetimeDB.BSATN.$0\");\n        let code = regex_replace!(\n            &code,\n            r\"\\b(ProductTypeElement|SumTypeVariant)\\b\",\n            \"SpacetimeDB.BSATN.AggregateElement\"\n        );\n        let code = regex_replace!(\n            &code,\n            r\"\\b(Product|Sum)Type\\b\",\n            \"List<SpacetimeDB.BSATN.AggregateElement>\"\n        );\n\n        fs::write(dir.join(filename), code.as_ref())\n    })?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/codegen/examples/regen-typescript-moduledef.rs",
    "content": "//! This script is used to generate the Typescript bindings for the `RawModuleDef` type.\n//! Run `cargo run --example regen-typescript-moduledef` to update TS bindings whenever the module definition changes.\n\n// TODO: consider renaming this file, since it doesn't just generate `RawModuleDef` anymore.\n\nuse fs_err as fs;\nuse regex::Regex;\nuse spacetimedb_codegen::{generate, typescript, CodegenOptions, OutputFile};\nuse spacetimedb_lib::db::raw_def::v9::ViewResultHeader;\nuse spacetimedb_lib::{RawModuleDef, RawModuleDefV8};\nuse spacetimedb_schema::def::ModuleDef;\nuse std::path::Path;\nuse std::sync::OnceLock;\n\nmacro_rules! regex_replace {\n    ($value:expr, $re:expr, $replace:expr) => {{\n        static RE: OnceLock<Regex> = OnceLock::new();\n        RE.get_or_init(|| Regex::new($re).unwrap())\n            .replace_all($value, $replace)\n    }};\n}\n\nfn main() -> anyhow::Result<()> {\n    let module = RawModuleDefV8::with_builder(|module| {\n        module.add_type::<RawModuleDef>();\n        module.add_type::<ViewResultHeader>();\n        module.add_type::<spacetimedb_lib::http::Request>();\n        module.add_type::<spacetimedb_lib::http::Response>();\n    });\n\n    let dir = &Path::new(concat!(\n        env!(\"CARGO_MANIFEST_DIR\"),\n        \"/../bindings-typescript/src/lib/autogen\"\n    ))\n    .canonicalize()?;\n\n    fs::remove_dir_all(dir)?;\n    fs::create_dir(dir)?;\n\n    let module: ModuleDef = module.try_into()?;\n    generate(&module, &typescript::TypeScript, &CodegenOptions::default())\n        .into_iter()\n        .try_for_each(|OutputFile { filename, code }| {\n            // Skip the index.ts since we don't need it.\n            if filename == \"index.ts\" {\n                return Ok(());\n            }\n            // We don't need the convenience types.\n            if filename.starts_with(\"types/\") {\n                return Ok(());\n            }\n            let code = regex_replace!(&code, r#\"from \"spacetimedb\";\"#, r#\"from \"../../lib/type_builders\";\"#);\n\n            // Elide types which are related to client-side only things\n            let code = regex_replace!(&code, r\"type CallReducerFlags as __CallReducerFlags,\", r\"\");\n            let code = regex_replace!(&code, r\"type ErrorContextInterface as __ErrorContextInterface,\", r\"\");\n            let code = regex_replace!(&code, r\"type Event as __Event,\", r\"\");\n            let code = regex_replace!(&code, r\"type EventContextInterface as __EventContextInterface,\", r\"\");\n            let code = regex_replace!(\n                &code,\n                r\"type ReducerEventContextInterface as __ReducerEventContextInterface,\",\n                r\"\"\n            );\n            let code = regex_replace!(\n                &code,\n                r\"type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,\",\n                r\"\"\n            );\n            let code = regex_replace!(&code, r\"DbConnectionBuilder as __DbConnectionBuilder,\", r\"\");\n            let code = regex_replace!(&code, r\"DbConnectionImpl as __DbConnectionImpl,\", r\"\");\n            let code = regex_replace!(&code, r\"type DbConnectionConfig as __DbConnectionConfig,\", r\"\");\n            let code = regex_replace!(&code, r\"SubscriptionBuilderImpl as __SubscriptionBuilderImpl,\", r\"\");\n            let code = regex_replace!(&code, r\"TableCache as __TableCache,\", r\"\");\n            let code = regex_replace!(&code, r\"ClientCache as __ClientCache,\", r\"\");\n            fs::write(dir.join(filename), code.as_bytes())\n        })?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/codegen/src/UnrealCPP-README.md",
    "content": "# SpacetimeDB UnrealCPP Code Generator\n\nThis document provides information about the UnrealCPP code generator (`unrealcpp.rs`) and its Blueprint compatibility handling.\n\n## Overview\n\nThe UnrealCPP code generator creates Unreal Engine-compatible C++ bindings for SpacetimeDB modules, including:\n\n- **Table classes** with indexing and event handling\n- **Reducer classes** with argument structures\n- **Type definitions** for module data structures\n- **Client connection** management classes\n- **Event delegates** for real-time updates\n\n## Blueprint Compatibility\n\nUnreal Engine's Blueprint system has limitations on which C++ types can be exposed. The code generator automatically handles this by detecting incompatible types and adjusting the generated code accordingly.\n\n### Blueprint-Unsupported Types\n\nThe following types **cannot** be used in Unreal Engine Blueprints:\n- `int8`   (signed   8-bit  integer)\n- `int16`  (signed   16-bit integer)\n- `uint16` (unsigned 16-bit integer)\n- `uint32` (unsigned 32-bit integer) \n- `uint64` (unsigned 64-bit integer)\n\n### Blueprint-Compatible Types\n\nThese types **can** be used in Blueprints:\n\n- `bool`\n- `int8`, `uint8`, `int16`, `int32`, `int64`\n- `float`, `double`\n- `FString`\n- `All SpacetimeDB SDK types`\n- Custom structs and enums\n- `TArray<T>` (if T is Blueprint-compatible)\n\n## Code Generation Behavior\n\n### Table Find Functions\n\nWhen a table has a primary key or unique index with an unsupported type like uint32. In generated code you'll see:\n\n**Generated Code:**\n```cpp\n// NOTE: Not exposed to Blueprint because uint32 types are not Blueprint-compatible\nFMessageType Find(uint32 Key)\n{\n    return IdIndexHelper.FindUniqueIndex(Key);\n}\n```\n\n**Behavior:**\n- Function is generated without `UFUNCTION(BlueprintCallable)`\n- Still fully functional in C++\n- Comment explains why Blueprint exposure was omitted\n\n### Reducer Functions\n\nWhen a reducer has parameters with unsupported types like uint32 and uint64:\n\n**Generated Code:**\n```cpp\n// NOTE: Not exposed to Blueprint because uint32, uint64 types are not Blueprint-compatible\nvoid SendMessage(const FString& Text, const uint32& Priority, const uint64& Timestamp);\n```\n\n**Behavior:**\n- Function is generated without `UFUNCTION(BlueprintCallable)`\n- Fully functional in C++\n- Multiple unsupported types are listed in the comment\n\n### Reducer Event Delegates\n\nWhen a reducer's event delegate has unsupported parameter types:\n\n**Generated Code:**\n```cpp\nDECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(\n    FSendMessageHandler,\n    const FReducerEventContext&, Context,\n    const FString&, Text,\n    const uint32&, Priority,\n    const uint64&, Timestamp\n);\n// NOTE: Not exposed to Blueprint because uint32, uint64 types are not Blueprint-compatible\nFSendMessageHandler OnSendMessage;\n```\n\n**Behavior:**\n- Delegate is generated without `UPROPERTY(BlueprintAssignable)`\n- Still bindable from C++\n- Blueprint cannot access the event\n\n### Struct Fields\n\nWhen struct fields have unsupported types:\n\n**Generated Code:**\n```cpp\nUSTRUCT(BlueprintType)\nstruct MYMODULE_API FSendMessageArgs\n{\n    GENERATED_BODY()\n\n    UPROPERTY(BlueprintReadWrite, Category=\"SpacetimeDB\")\n    FString Text;\n\n    // NOTE: uint32 types can't be used in blueprints\n    uint32 Priority;\n    \n    // NOTE: uint64 types can't be used in blueprints\n    uint64 Timestamp;\n};\n```\n\n**Behavior:**\n- Blueprint-compatible fields get `UPROPERTY(BlueprintReadWrite)`\n- Unsupported fields are plain C++ members with explanatory comments\n- Struct is still `USTRUCT(BlueprintType)` for the supported fields\n\n### Optional Types\n\nSpacetimeDB optional types (`Option<T>`) are generated as custom Unreal structs with special handling:\n\n**Generated Structure:**\n```cpp\nUSTRUCT(BlueprintType)\nstruct MYMODULE_API FMyModuleOptionalString\n{\n    GENERATED_BODY()\n\n    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \"SpacetimeDB\", meta = (EditCondition = \"bHasValue\"))\n    bool bHasValue = false;\n\n    // Only gets UPROPERTY if the inner type is Blueprint-compatible\n    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \"SpacetimeDB\", meta = (EditCondition = \"bHasValue\"))\n    FString Value;\n    \n    // Constructors and helper methods...\n};\n```\n\n**Behavior:**\n- Generated as separate structs in `Public/ModuleBindings/Optionals/` directory\n- The `bHasValue` field indicates whether a value is present\n- The `Value` field is only editable when `bHasValue` is true (using `EditCondition`)\n- If the inner type is not Blueprint-compatible (e.g., `Option<u32>`), the `Value` field won't have `UPROPERTY`\n- Custom `GetTypeHash` implementation for proper map/set support\n- BSATN serialization support via `UE_SPACETIMEDB_OPTIONAL` macro\n\n### Sum Types (Tagged Enums)\n\nSpacetimeDB sum types (Rust enums with variants) are generated as UStructs + TVarint + BlueprintFunctionLibrary for Blueprint compatibility:\n\n**Generated Structure:**\n```cpp\n// Tag enum for variant identification\nUENUM(BlueprintType)\nenum class ECompressableQueryUpdateTag : uint8\n{\n    Uncompressed,\n    Brotli,\n    Gzip\n};\n\n// Main struct\nUSTRUCT(BlueprintType)\nstruct SPACETIMEDBSDK_API FCompressableQueryUpdateType\n{\n    GENERATED_BODY()\n\npublic:\n    FCompressableQueryUpdateType() = default;\n\n    TVariant<FQueryUpdateType, TArray<uint8>> MessageData;\n\n    UPROPERTY(BlueprintReadOnly)\n    ECompressableQueryUpdateTag Tag;\n\n    static FCompressableQueryUpdateType Uncompressed(const FQueryUpdateType& Value)\n    {\n        FCompressableQueryUpdateType Obj;\n        Obj.Tag = ECompressableQueryUpdateTag::Uncompressed;\n        Obj.MessageData.Set<FQueryUpdateType>(Value);\n        return Obj;\n    }\n\n    static FCompressableQueryUpdateType Brotli(const TArray<uint8>& Value)\n    {\n        FCompressableQueryUpdateType Obj;\n        Obj.Tag = ECompressableQueryUpdateTag::Brotli;\n        Obj.MessageData.Set<TArray<uint8>>(Value);\n        return Obj;\n    }\n\n    static FCompressableQueryUpdateType Gzip(const TArray<uint8>& Value)\n    {\n        FCompressableQueryUpdateType Obj;\n        Obj.Tag = ECompressableQueryUpdateTag::Gzip;\n        Obj.MessageData.Set<TArray<uint8>>(Value);\n        return Obj;\n    }\n\n    // Is* functions\n    bool IsUncompressed() const { return Tag == ECompressableQueryUpdateTag::Uncompressed; }\n\n    // GetAs* functions\n    FQueryUpdateType GetAsUncompressed() const\n    {\n        ensureMsgf(IsUncompressed(), TEXT(\"MessageData does not hold Uncompressed!\"));\n        return MessageData.Get<FQueryUpdateType>();\n    }\n};\n\n// Corresponding blueprint function library for using the sum types\nUCLASS()\nclass SPACETIMEDBSDK_API UCompressableQueryUpdateBpLib : public UBlueprintFunctionLibrary\n{\n    GENERATED_BODY()\n\nprivate:\n    UFUNCTION(BlueprintCallable, Category = \"SpacetimeDB|CompressableQueryUpdate\")\n    static FCompressableQueryUpdateType Uncompressed(const FQueryUpdateType& InValue)\n    {\n        return FCompressableQueryUpdateType::Uncompressed(InValue);\n    }\n\n    UFUNCTION(BlueprintPure, Category = \"SpacetimeDB|CompressableQueryUpdate\")\n    static bool IsUncompressed(const FCompressableQueryUpdateType& InValue) { return InValue.IsUncompressed(); }\n\n    UFUNCTION(BlueprintPure, Category = \"SpacetimeDB|CompressableQueryUpdate\")\n    static FQueryUpdateType GetAsUncompressed(const FCompressableQueryUpdateType& InValue)\n    {\n        return InValue.GetAsUncompressed();\n    }\n\n    // Rest is the same for other variants...\n}\n```\n\n**Key Behaviors:**\n\n- **UStruct + TVarint - based**: Generated as UStruct + TVarint for saving memory\n- **TVarint**: Uses Unreal's TVarint to store variant payload\n- **Blueprint Function Library**: contains a bunch of private static BlueprintCallable functions. To instantiate and use varints in BPs.\n- **Type Safety**: Each variant gets its own factory function and getter\n- **Memory Overhead**: C++ unions based\n- **Unit Variants**: Variants without payload use `FSpacetimeDBUnit` type\n- **Blueprint Compatible**: All functions are BlueprintCallable or BlueprintPure for full Blueprint access\n- **BSATN Support**: Generated with proper serialization macros\n\n### Plain Enums (Simple Enums)\n\nSpacetimeDB plain enums (Rust enums with only unit variants, no payloads) are generated as simple Unreal Engine enums:\n\n**Rust Definition:**\n```rust\n#[derive(...)]\npub enum Status {\n    Pending,\n    Active,\n    Inactive,\n    Suspended,\n}\n```\n\n**Generated Code:**\n```cpp\nUENUM(BlueprintType)\nenum class EStatusType : uint8\n{\n    Pending,\n    Active,\n    Inactive,\n    Suspended,\n};\n```\n\n**Key Behaviors:**\n\n- **Simple UENUM**: Generated as standard Unreal Engine enum class with `uint8` backing type\n- **Blueprint Compatible**: `UENUM(BlueprintType)` makes it fully accessible in Blueprints\n- **Naming Convention**: `E[Name]Type` format (e.g., `EStatusType`)\n- **PascalCase Variants**: All variants converted to PascalCase for Unreal conventions\n- **Lightweight**: No memory overhead - just a simple enum value\n- **Type Safety**: Strongly typed enum class prevents implicit conversions\n\n**Usage in Generated Code:**\n- Used directly as `EStatusType` in struct fields, function parameters, etc.\n- Can be used in Blueprint dropdown selections, switch statements, etc.\n- Fully compatible with Unreal's reflection and serialization systems\n\n**Comparison with Sum Types:**\n- **Plain Enums**: Simple `UENUM` with no payload → lightweight, direct Blueprint usage\n- **Sum Types**: Complex `UStructs` with `TVarint` payload → union based, indirect Blueprint usage through BPLib\n\n## Generating Module Bindings\n\nTo generate UnrealCPP bindings for your SpacetimeDB module, use the SpacetimeDB CLI:\n\n### Basic Command\n\n```bash\ncargo run --bin spacetimedb-cli -- generate --lang unrealcpp --uproject-dir <uproject_directory> --module-path <module_path> --unreal-module-name <ModuleName>\n```\n\n### Example\n\n```bash\ncargo run --bin spacetimedb-cli -- generate --lang unrealcpp --uproject-dir crates/sdk-unreal/examples/QuickstartChat --module-path modules/quickstart-chat --unreal-module-name QuickstartChat\n```\n\n### Parameters\n\n- `--lang unrealcpp`: Specifies the UnrealCPP code generator\n- `--uproject-dir`: Directory containing your Unreal project's `.uproject` file\n- `--module-path`: Path to your SpacetimeDB module source code\n- `--unreal-module-name`: **Required** - Name used for generated classes, API prefix and putting generated module bindings in the correct Module's Source\n\n### Why Module Name is Required\n\nThe `--unreal-module-name` parameter is **mandatory** for UnrealCPP generation because:\n\n1. **Unreal Engine API Macro**: Generated classes use `MODULENAME_API` macros (e.g., `QUICKSTARTCHAT_API`) for proper DLL export/import in Unreal Engine\n2. **Class Prefixing**: All the optional generated classes are prefixed with the module name to avoid naming conflicts (e.g., `FQuickstartChatOptionalString`)\n3. **Build System Integration**: Unreal Engine's build system requires proper API macros for linking across modules\n4. **Generated Module Bindings**: Put generated bindings in correct module's source\n\n**⚠️ IMPORTANT:** Without the module name, the generated code would not compile in Unreal Engine due to missing API macros and naming conflicts.\n\n### Project Setup Behavior\n\nWhen generating into an Unreal project, the code generator also:\n\n1. Ensures the named module exists in the project's `Modules` array in the `.uproject` file.\n2. Creates missing `Source/<Module>/<Module>.Build.cs`, `Source/<Module>/<Module>.cpp`, and `Source/<Module>/<Module>.h` files.\n\nIf the `.uproject` file is missing, unreadable, malformed, or has an invalid `Modules` field, generation fails immediately.\n\n## Implementation Details\n\nThe Blueprint compatibility checking is implemented in the `is_blueprintable()` function, which recursively checks:\n\nThe code generator collects incompatible types during the first parameter iteration to avoid duplicate loops and provides specific error messages listing exactly which types are causing Blueprint incompatibility.\n\n## Error Messages\n\nAll error messages follow a consistent format:\n\n- **Single type:** `\"uint32 types are not Blueprint-compatible\"`\n- **Multiple types:** `\"uint32, uint64 types are not Blueprint-compatible\"`\n\nThis makes it clear to developers exactly which types need to be changed for Blueprint compatibility.\n"
  },
  {
    "path": "crates/codegen/src/code_indenter.rs",
    "content": "use std::fmt;\nuse std::ops::{Deref, DerefMut};\n\npub(super) type Indenter = CodeIndenter<String>;\n\n#[macro_export]\nmacro_rules! indent_scope {\n    ($x:ident) => {\n        let mut $x = $x.indented(1);\n    };\n}\n\npub struct CodeIndenter<W: fmt::Write> {\n    writer: W,\n    level: u32,\n    needs_indenting: bool,\n    indent: &'static str,\n}\nimpl<W: fmt::Write> CodeIndenter<W> {\n    pub fn new(writer: W, indent: &'static str) -> Self {\n        CodeIndenter {\n            writer,\n            level: 0,\n            needs_indenting: true,\n            indent,\n        }\n    }\n    // pub fn get_ref(&self) -> &W {\n    //     &self.writer\n    // }\n    // pub fn get_mut(&mut self) -> &mut W {\n    //     &mut self.writer\n    // }\n    pub fn into_inner(self) -> W {\n        self.writer\n    }\n    pub fn indent(&mut self, n: u32) {\n        self.level = self.level.saturating_add(n);\n    }\n    pub fn dedent(&mut self, n: u32) {\n        self.level = self.level.saturating_sub(n);\n    }\n    pub fn indented(&mut self, n: u32) -> IndentScope<'_, W> {\n        self.indent(n);\n        IndentScope { fmt: self }\n    }\n    fn write_indent(&mut self) -> fmt::Result {\n        for _ in 0..self.level {\n            self.writer.write_str(self.indent)?;\n        }\n        Ok(())\n    }\n\n    /// Invoke `f` while indenting one level greater than `self` currently does.\n    pub fn with_indent<Res>(&mut self, f: impl FnOnce(&mut Self) -> Res) -> Res {\n        let mut indenter = self.indented(1);\n        f(&mut indenter)\n    }\n\n    // Writes a newline without setting the `needs_indenting` flag.\n    // TODO(cloutiertyler): I think it should set the flag, but I don't know\n    // if anyone is relying on the current behavior.\n    pub fn newline(&mut self) {\n        self.writer.write_char('\\n').unwrap();\n    }\n\n    /// Print an indented block delimited by `before` and `after`, with body written by `f`.\n    pub fn delimited_block<Res>(&mut self, before: &str, f: impl FnOnce(&mut Self) -> Res, after: &str) -> Res {\n        self.writer.write_str(before).unwrap();\n        let res = self.with_indent(|out| {\n            out.newline();\n            // Need an explicit `write_indent` call here because calling `out.newline`\n            // will not cause the subsequent line to be indented, as `write_str` thinks\n            // it's an empty line.\n            out.write_indent().unwrap();\n            f(out)\n        });\n        self.writer.write_str(after).unwrap();\n        res\n    }\n}\nimpl<W: fmt::Write> fmt::Write for CodeIndenter<W> {\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        for (i, line) in s.split_inclusive('\\n').enumerate() {\n            let write_indent = i != 0 || std::mem::take(&mut self.needs_indenting);\n            // skip the indent if it's an empty line\n            if write_indent && line != \"\\n\" {\n                self.write_indent()?;\n            }\n            self.writer.write_str(line)?;\n        }\n        self.needs_indenting = s.ends_with('\\n');\n        Ok(())\n    }\n}\nimpl CodeIndenter<String> {\n    // This method allows `write!` and `writeln!` to be used directly on `CodeIndenter`.\n    // It does the same thing as using it via the `fmt::Write` trait but in a non-fallible manner.\n    // This is only allowed on `String` because it's the only type that can't fail to write.\n    pub fn write_fmt(&mut self, args: fmt::Arguments) {\n        fmt::Write::write_fmt(self, args).unwrap()\n    }\n}\npub struct IndentScope<'a, W: fmt::Write> {\n    fmt: &'a mut CodeIndenter<W>,\n}\nimpl<W: fmt::Write> Drop for IndentScope<'_, W> {\n    fn drop(&mut self) {\n        self.fmt.dedent(1);\n    }\n}\nimpl<T: fmt::Write> Deref for IndentScope<'_, T> {\n    type Target = CodeIndenter<T>;\n    fn deref(&self) -> &Self::Target {\n        self.fmt\n    }\n}\nimpl<T: fmt::Write> DerefMut for IndentScope<'_, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.fmt\n    }\n}\n"
  },
  {
    "path": "crates/codegen/src/cpp.rs",
    "content": "//! Minimal C++ code generation for SpacetimeDB module definitions.\n//! Generates only schema definitions - framework provides all functionality.\n\nuse crate::CodegenOptions;\nuse crate::Lang;\nuse crate::OutputFile;\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_schema::def::{ModuleDef, ReducerDef, TableDef, TypeDef};\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::type_for_generate::{\n    AlgebraicTypeDef, AlgebraicTypeUse, PlainEnumTypeDef, ProductTypeDef, SumTypeDef,\n};\n// Removed unused import\nuse std::collections::HashSet;\nuse std::fmt::{self, Write};\n\npub struct Cpp<'opts> {\n    pub namespace: &'opts str,\n}\n\nimpl<'opts> Cpp<'opts> {\n    fn write_header_comment(&self, output: &mut String) {\n        writeln!(\n            output,\n            \"// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\"\n        )\n        .unwrap();\n        writeln!(\n            output,\n            \"// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\"\n        )\n        .unwrap();\n        writeln!(output).unwrap();\n        writeln!(output, \"// This was generated using spacetimedb codegen.\").unwrap();\n        writeln!(output).unwrap();\n    }\n\n    fn write_standard_includes(&self, output: &mut String) {\n        writeln!(output, \"#pragma once\").unwrap();\n        writeln!(output).unwrap();\n        writeln!(output, \"#include <cstdint>\").unwrap();\n        writeln!(output, \"#include <string>\").unwrap();\n        writeln!(output, \"#include <vector>\").unwrap();\n        writeln!(output, \"#include <optional>\").unwrap();\n        writeln!(output, \"#include <memory>\").unwrap();\n        writeln!(output, \"#include \\\"../autogen_base.h\\\"\").unwrap();\n        writeln!(output, \"#include \\\"spacetimedb/bsatn/bsatn.h\\\"\").unwrap();\n    }\n\n    fn cpp_primitive_type(&self, p: &PrimitiveType) -> &'static str {\n        match p {\n            PrimitiveType::Bool => \"bool\",\n            PrimitiveType::I8 => \"int8_t\",\n            PrimitiveType::U8 => \"uint8_t\",\n            PrimitiveType::I16 => \"int16_t\",\n            PrimitiveType::U16 => \"uint16_t\",\n            PrimitiveType::I32 => \"int32_t\",\n            PrimitiveType::U32 => \"uint32_t\",\n            PrimitiveType::I64 => \"int64_t\",\n            PrimitiveType::U64 => \"uint64_t\",\n            PrimitiveType::I128 => \"SpacetimeDB::I128\",\n            PrimitiveType::U128 => \"SpacetimeDB::U128\",\n            PrimitiveType::I256 => \"SpacetimeDB::I256\",\n            PrimitiveType::U256 => \"SpacetimeDB::U256\",\n            PrimitiveType::F32 => \"float\",\n            PrimitiveType::F64 => \"double\",\n        }\n    }\n\n    fn write_algebraic_type(&self, output: &mut String, module: &ModuleDef, typ: &AlgebraicTypeUse) -> fmt::Result {\n        match typ {\n            AlgebraicTypeUse::Primitive(p) => write!(output, \"{}\", self.cpp_primitive_type(p)),\n            AlgebraicTypeUse::Array(elem_type) => {\n                write!(output, \"std::vector<\")?;\n                self.write_algebraic_type(output, module, elem_type)?;\n                write!(output, \">\")\n            }\n            AlgebraicTypeUse::Option(inner_type) => {\n                write!(output, \"std::optional<\")?;\n                self.write_algebraic_type(output, module, inner_type)?;\n                write!(output, \">\")\n            }\n            AlgebraicTypeUse::Result { .. } => {\n                write!(output, \"__sdk::Result\")\n            }\n            AlgebraicTypeUse::String => write!(output, \"std::string\"),\n            AlgebraicTypeUse::Identity => write!(output, \"__sdk::Identity\"),\n            AlgebraicTypeUse::ConnectionId => write!(output, \"__sdk::ConnectionId\"),\n            AlgebraicTypeUse::Timestamp => write!(output, \"__sdk::Timestamp\"),\n            AlgebraicTypeUse::TimeDuration => write!(output, \"__sdk::TimeDuration\"),\n            AlgebraicTypeUse::ScheduleAt => write!(output, \"__sdk::ScheduleAt\"),\n            AlgebraicTypeUse::Uuid => write!(output, \"__sdk::Uuid\"),\n            AlgebraicTypeUse::Unit => write!(output, \"std::monostate\"),\n            AlgebraicTypeUse::Never => write!(output, \"std::monostate\"),\n            AlgebraicTypeUse::Ref(type_ref) => {\n                if let Some((type_name, _type_def)) = module.type_def_from_ref(*type_ref) {\n                    let name = type_name.name().to_string();\n                    write!(output, \"{}::{}\", self.namespace, name)\n                } else {\n                    write!(output, \"UnknownType /* ref {} */\", type_ref.idx())\n                }\n            }\n        }\n    }\n\n    fn collect_dependencies(module: &ModuleDef, typ: &AlgebraicTypeUse) -> HashSet<String> {\n        let mut deps = HashSet::new();\n        match typ {\n            AlgebraicTypeUse::Array(elem_type) | AlgebraicTypeUse::Option(elem_type) => {\n                deps.extend(Self::collect_dependencies(module, elem_type));\n            }\n            AlgebraicTypeUse::Ref(type_ref) => {\n                if let Some((type_name, _)) = module.type_def_from_ref(*type_ref) {\n                    deps.insert(type_name.name().to_string());\n                }\n            }\n            _ => {}\n        }\n        deps\n    }\n\n    fn collect_product_dependencies(&self, module: &ModuleDef, product: &ProductTypeDef) -> HashSet<String> {\n        let mut deps = HashSet::new();\n        for (_, field_type) in &product.elements {\n            deps.extend(Self::collect_dependencies(module, field_type));\n        }\n        deps\n    }\n\n    fn collect_sum_dependencies(&self, module: &ModuleDef, sum: &SumTypeDef) -> HashSet<String> {\n        let mut deps = HashSet::new();\n        for (_, variant_type) in &sum.variants {\n            deps.extend(Self::collect_dependencies(module, variant_type));\n        }\n        deps\n    }\n\n    // Generate minimal product type (struct with fields only)\n    fn write_product_type(&self, output: &mut String, module: &ModuleDef, type_name: &str, product: &ProductTypeDef) {\n        // Use INTERNAL macro for Internal namespace\n        let macro_name = if self.namespace == \"SpacetimeDB::Internal\" {\n            \"SPACETIMEDB_INTERNAL_PRODUCT_TYPE\"\n        } else {\n            \"SPACETIMEDB_PRODUCT_TYPE\"\n        };\n        writeln!(output, \"{}({}) {{\", macro_name, type_name).unwrap();\n\n        // Write fields only\n        for (field_name, field_type) in &product.elements {\n            write!(output, \"    \").unwrap();\n            self.write_algebraic_type(output, module, field_type).unwrap();\n            writeln!(output, \" {};\", field_name).unwrap();\n        }\n\n        writeln!(output).unwrap();\n\n        // Only generate serialization for Internal types (no deserialization needed)\n        writeln!(\n            output,\n            \"    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {{\"\n        )\n        .unwrap();\n        for (field_name, _) in &product.elements {\n            writeln!(\n                output,\n                \"        ::SpacetimeDB::bsatn::serialize(writer, {});\",\n                field_name\n            )\n            .unwrap();\n        }\n        writeln!(output, \"    }}\").unwrap();\n\n        // Generate equality method\n        if !product.elements.is_empty() {\n            write!(output, \"    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(\").unwrap();\n            for (i, (field_name, _)) in product.elements.iter().enumerate() {\n                if i > 0 {\n                    write!(output, \", \").unwrap();\n                }\n                write!(output, \"{}\", field_name).unwrap();\n            }\n            writeln!(output, \")\").unwrap();\n        }\n\n        writeln!(output, \"}};\").unwrap();\n    }\n\n    // Generate minimal sum type (TaggedEnum only)\n    fn write_sum_type(&self, output: &mut String, module: &ModuleDef, type_name: &str, sum: &SumTypeDef) {\n        // Special case: Generate proper tagged enum for RawIndexAlgorithm with data variants\n        if type_name == \"RawIndexAlgorithm\" {\n            // Generate data structures for each algorithm variant\n            writeln!(output, \"// BTree algorithm data\").unwrap();\n            writeln!(\n                output,\n                \"SPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmBTreeData) {{\"\n            )\n            .unwrap();\n            writeln!(output, \"    std::vector<uint16_t> columns;\").unwrap();\n            writeln!(\n                output,\n                \"    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {{\"\n            )\n            .unwrap();\n            writeln!(output, \"        ::SpacetimeDB::bsatn::serialize(writer, columns);\").unwrap();\n            writeln!(output, \"    }}\").unwrap();\n            writeln!(output, \"    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(columns)\").unwrap();\n            writeln!(output, \"}};\").unwrap();\n            writeln!(output).unwrap();\n\n            // Generate Hash data structure (for future use)\n            writeln!(output, \"// Hash algorithm data (not currently used)\").unwrap();\n            writeln!(\n                output,\n                \"SPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmHashData) {{\"\n            )\n            .unwrap();\n            writeln!(output, \"    std::vector<uint16_t> columns;\").unwrap();\n            writeln!(\n                output,\n                \"    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {{\"\n            )\n            .unwrap();\n            writeln!(output, \"        ::SpacetimeDB::bsatn::serialize(writer, columns);\").unwrap();\n            writeln!(output, \"    }}\").unwrap();\n            writeln!(output, \"    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(columns)\").unwrap();\n            writeln!(output, \"}};\").unwrap();\n            writeln!(output).unwrap();\n\n            // Generate Direct data structure (not currently used)\n            writeln!(output, \"// Direct algorithm data (not currently used)\").unwrap();\n            writeln!(\n                output,\n                \"SPACETIMEDB_INTERNAL_PRODUCT_TYPE(RawIndexAlgorithmDirectData) {{\"\n            )\n            .unwrap();\n            writeln!(output, \"    uint16_t column;\").unwrap();\n            writeln!(\n                output,\n                \"    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {{\"\n            )\n            .unwrap();\n            writeln!(output, \"        ::SpacetimeDB::bsatn::serialize(writer, column);\").unwrap();\n            writeln!(output, \"    }}\").unwrap();\n            writeln!(output, \"    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(column)\").unwrap();\n            writeln!(output, \"}};\").unwrap();\n            writeln!(output).unwrap();\n\n            // Generate the tagged enum\n            writeln!(output, \"// RawIndexAlgorithm tagged enum with data variants\").unwrap();\n            writeln!(output, \"SPACETIMEDB_INTERNAL_TAGGED_ENUM({}, \", type_name).unwrap();\n            writeln!(output, \"    SpacetimeDB::Internal::RawIndexAlgorithmBTreeData,\").unwrap();\n            writeln!(output, \"    SpacetimeDB::Internal::RawIndexAlgorithmHashData,\").unwrap();\n            writeln!(output, \"    SpacetimeDB::Internal::RawIndexAlgorithmDirectData\").unwrap();\n            writeln!(output, \")\").unwrap();\n            return;\n        }\n\n        // Use INTERNAL macro for Internal namespace\n        let macro_name = if self.namespace == \"SpacetimeDB::Internal\" {\n            \"SPACETIMEDB_INTERNAL_TAGGED_ENUM\"\n        } else {\n            \"SPACETIMEDB_TAGGED_ENUM\"\n        };\n\n        // Collect variant types and check for duplicates\n        let mut variant_types = Vec::new();\n        let mut type_strings = Vec::new();\n\n        for (variant_name, variant_type) in &sum.variants {\n            let mut type_str = String::new();\n            self.write_algebraic_type(&mut type_str, module, variant_type).unwrap();\n\n            // Check if this type already exists\n            let duplicate_index = type_strings.iter().position(|s| s == &type_str);\n\n            if let Some(_idx) = duplicate_index {\n                // Found duplicate - create a wrapper struct to make it unique\n                let wrapper_name = format!(\"{}_{}_Wrapper\", type_name, variant_name);\n                let wrapper_macro = if self.namespace == \"SpacetimeDB::Internal\" {\n                    \"SPACETIMEDB_INTERNAL_PRODUCT_TYPE\"\n                } else {\n                    \"SPACETIMEDB_PRODUCT_TYPE\"\n                };\n                writeln!(output, \"{}({}) {{\", wrapper_macro, wrapper_name).unwrap();\n                writeln!(output, \"    {} value;\", type_str).unwrap();\n                writeln!(\n                    output,\n                    \"    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const {{\"\n                )\n                .unwrap();\n                writeln!(output, \"        ::SpacetimeDB::bsatn::serialize(writer, value);\").unwrap();\n                writeln!(output, \"    }}\").unwrap();\n                writeln!(output, \"    SPACETIMEDB_PRODUCT_TYPE_EQUALITY(value)\").unwrap();\n                writeln!(output, \"}};\").unwrap();\n                variant_types.push(wrapper_name);\n            } else {\n                // No duplicate, use the type directly\n                type_strings.push(type_str.clone());\n                variant_types.push(type_str);\n            }\n        }\n\n        // Generate the tagged enum with (potentially wrapped) variant types\n        write!(output, \"{}({}\", macro_name, type_name).unwrap();\n\n        if !variant_types.is_empty() {\n            write!(output, \", \").unwrap();\n            for (i, variant_type) in variant_types.iter().enumerate() {\n                if i > 0 {\n                    write!(output, \", \").unwrap();\n                }\n                write!(output, \"{}\", variant_type).unwrap();\n            }\n        }\n\n        writeln!(output, \")\").unwrap();\n    }\n\n    // Generate minimal plain enum\n    fn write_plain_enum(&self, output: &mut String, type_name: &str, plain_enum: &PlainEnumTypeDef) {\n        writeln!(output, \"enum class {} : uint8_t {{\", type_name).unwrap();\n\n        for (i, variant) in plain_enum.variants.iter().enumerate() {\n            writeln!(output, \"    {} = {},\", variant, i).unwrap();\n        }\n\n        writeln!(output, \"}};\").unwrap();\n    }\n\n    // Special generation for AlgebraicType to handle circular dependencies\n    fn generate_algebraic_type_special(&self) -> String {\n        r#\"// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb codegen.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <optional>\n#include <memory>\n#include <variant>\n#include \"spacetimedb/bsatn/bsatn.h\"\n#include \"../forward_declarations.h\"\n\nnamespace SpacetimeDB::Internal {\n\n// AlgebraicType is special - it uses pointers to break circular dependencies\n// This is a custom implementation due to circular references between:\n// AlgebraicType -> SumType -> SumTypeVariant -> AlgebraicType\n// AlgebraicType -> ProductType -> ProductTypeElement -> AlgebraicType\nclass AlgebraicType {\npublic:\n    enum class Tag : uint8_t {\n        Ref = 0,\n        Sum = 1, \n        Product = 2,\n        Array = 3,\n        String = 4,\n        Bool = 5,\n        I8 = 6,\n        U8 = 7,\n        I16 = 8,\n        U16 = 9,\n        I32 = 10,\n        U32 = 11,\n        I64 = 12,\n        U64 = 13,\n        I128 = 14,\n        U128 = 15,\n        I256 = 16,\n        U256 = 17,\n        F32 = 18,\n        F64 = 19\n    };\n\n    using DataType = std::variant<\n        uint32_t,                                          // Ref\n        std::unique_ptr<SpacetimeDB::Internal::SumType>,   // Sum\n        std::unique_ptr<SpacetimeDB::Internal::ProductType>, // Product \n        std::unique_ptr<SpacetimeDB::Internal::AlgebraicType>, // Array element type\n        std::monostate  // All primitive types (String, Bool, I8, U8, etc.)\n    >;\n\nprivate:\n    Tag tag_;\n    DataType data_;\n\npublic:\n    AlgebraicType();\n    AlgebraicType(Tag primitive_tag);\n    AlgebraicType(const AlgebraicType& other);\n    AlgebraicType& operator=(const AlgebraicType& other);\n    \n    Tag get_tag() const { return tag_; }\n    \n    template<size_t Index>\n    const auto& get() const { return std::get<Index>(data_); }\n\n    template<size_t Index>\n    auto& get() { return std::get<Index>(data_); }\n\n    template<size_t Index, typename T>\n    void set(T&& value);\n\n    template<size_t Index>\n    bool is() const { return tag_ == static_cast<Tag>(Index); }\n\n    template<typename Visitor>\n    auto visit(Visitor&& visitor) const -> decltype(auto) {\n        return std::visit(std::forward<Visitor>(visitor), data_);\n    }\n\n    template<typename Visitor>  \n    auto visit(Visitor&& visitor) -> decltype(auto) {\n        return std::visit(std::forward<Visitor>(visitor), data_);\n    }\n\n    void bsatn_serialize(::SpacetimeDB::bsatn::Writer& writer) const;\n\n    bool operator==(const AlgebraicType& other) const;\n    bool operator!=(const AlgebraicType& other) const { return !(*this == other); }\n};\n\n} // namespace SpacetimeDB::Internal\n\"#\n        .to_string()\n    }\n}\n\nimpl Lang for Cpp<'_> {\n    fn generate_table_file_from_schema(\n        &self,\n        module: &ModuleDef,\n        table: &TableDef,\n        _schema: TableSchema,\n    ) -> OutputFile {\n        let mut output = String::new();\n        self.write_header_comment(&mut output);\n        self.write_standard_includes(&mut output);\n\n        // Add includes for dependencies\n        if let Some(AlgebraicTypeDef::Product(product)) = module.typespace_for_generate().get(table.product_type_ref) {\n            let deps = self.collect_product_dependencies(module, product);\n            for dep in deps {\n                if dep != table.name.to_string() {\n                    writeln!(output, \"#include \\\"{}.g.h\\\"\", dep).unwrap();\n                }\n            }\n        }\n\n        writeln!(output).unwrap();\n        writeln!(output, \"namespace {} {{\", self.namespace).unwrap();\n        writeln!(output).unwrap();\n\n        // Generate table as a product type\n        if let Some(AlgebraicTypeDef::Product(product)) = module.typespace_for_generate().get(table.product_type_ref) {\n            self.write_product_type(&mut output, module, &table.name, product);\n        }\n\n        writeln!(output, \"}} // namespace {}\", self.namespace).unwrap();\n\n        OutputFile {\n            filename: format!(\"{}.g.h\", table.name),\n            code: output,\n        }\n    }\n\n    fn generate_type_files(&self, module: &ModuleDef, type_def: &TypeDef) -> Vec<OutputFile> {\n        let mut output = String::new();\n        self.write_header_comment(&mut output);\n\n        let name = type_def.accessor_name.name();\n\n        // Special handling for AlgebraicType due to circular dependencies\n        if name.to_string() == \"AlgebraicType\" {\n            return vec![OutputFile {\n                filename: format!(\"{name}.g.h\"),\n                code: self.generate_algebraic_type_special(),\n            }];\n        }\n\n        self.write_standard_includes(&mut output);\n\n        // Add includes for dependencies\n        let deps = match module.typespace_for_generate().get(type_def.ty) {\n            Some(AlgebraicTypeDef::Product(product)) => self.collect_product_dependencies(module, product),\n            Some(AlgebraicTypeDef::Sum(sum)) => self.collect_sum_dependencies(module, sum),\n            Some(AlgebraicTypeDef::PlainEnum(_)) => HashSet::new(),\n            None => HashSet::new(),\n        };\n\n        for dep in deps {\n            if dep != name.to_string() {\n                writeln!(output, \"#include \\\"{}.g.h\\\"\", dep).unwrap();\n            }\n        }\n\n        writeln!(output).unwrap();\n        writeln!(output, \"namespace {} {{\", self.namespace).unwrap();\n        writeln!(output).unwrap();\n\n        match module.typespace_for_generate().get(type_def.ty) {\n            Some(AlgebraicTypeDef::Product(product)) => {\n                self.write_product_type(&mut output, module, name, product);\n            }\n            Some(AlgebraicTypeDef::Sum(sum)) => {\n                self.write_sum_type(&mut output, module, name, sum);\n            }\n            Some(AlgebraicTypeDef::PlainEnum(plain_enum)) => {\n                self.write_plain_enum(&mut output, name, plain_enum);\n            }\n            None => {\n                writeln!(output, \"// ERROR: Could not resolve type definition for {}\", name).unwrap();\n            }\n        }\n\n        writeln!(output, \"}} // namespace {}\", self.namespace).unwrap();\n        vec![OutputFile {\n            filename: format!(\"{name}.g.h\"),\n            code: output,\n        }]\n    }\n\n    fn generate_reducer_file(&self, _module: &ModuleDef, reducer: &ReducerDef) -> OutputFile {\n        let mut output = String::new();\n        self.write_header_comment(&mut output);\n        self.write_standard_includes(&mut output);\n\n        writeln!(output).unwrap();\n        writeln!(output, \"namespace {} {{\", self.namespace).unwrap();\n        writeln!(output).unwrap();\n\n        writeln!(output, \"// Reducer: {}\", reducer.name).unwrap();\n        writeln!(output, \"// This file is intentionally minimal - reducer implementation\").unwrap();\n        writeln!(output, \"// is handled by the SpacetimeDB framework.\").unwrap();\n\n        writeln!(output, \"}} // namespace {}\", self.namespace).unwrap();\n        OutputFile {\n            filename: format!(\"{}.g.h\", reducer.name),\n            code: output,\n        }\n    }\n\n    fn generate_procedure_file(\n        &self,\n        _module: &ModuleDef,\n        procedure: &spacetimedb_schema::def::ProcedureDef,\n    ) -> OutputFile {\n        let mut output = String::new();\n        self.write_header_comment(&mut output);\n        self.write_standard_includes(&mut output);\n\n        writeln!(output).unwrap();\n        writeln!(output, \"namespace {} {{\", self.namespace).unwrap();\n        writeln!(output).unwrap();\n\n        writeln!(output, \"// Procedure: {}\", procedure.name).unwrap();\n        writeln!(\n            output,\n            \"// This file is intentionally minimal - procedure implementation\"\n        )\n        .unwrap();\n        writeln!(output, \"// is handled by the SpacetimeDB framework.\").unwrap();\n\n        writeln!(output, \"}} // namespace {}\", self.namespace).unwrap();\n        OutputFile {\n            filename: format!(\"{}.g.h\", procedure.name),\n            code: output,\n        }\n    }\n\n    fn generate_global_files(&self, _module: &ModuleDef, _options: &CodegenOptions) -> Vec<OutputFile> {\n        vec![]\n    }\n}\n"
  },
  {
    "path": "crates/codegen/src/csharp.rs",
    "content": "// Note: the generated code depends on APIs and interfaces from crates/bindings-csharp/BSATN.Runtime.\nuse super::util::fmt_fn;\n\nuse std::collections::BTreeSet;\nuse std::fmt::{self, Write};\nuse std::ops::Deref;\n\nuse super::code_indenter::CodeIndenter;\nuse super::Lang;\nuse crate::util::{\n    collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_table_names_and_types,\n    print_auto_generated_file_comment, print_auto_generated_version_comment, type_ref_name,\n};\nuse crate::{indent_scope, CodegenOptions, OutputFile};\nuse convert_case::{Case, Casing};\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm, ModuleDef, TableDef, TypeDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::type_for_generate::{\n    AlgebraicTypeDef, AlgebraicTypeUse, PlainEnumTypeDef, ProductTypeDef, SumTypeDef, TypespaceForGenerate,\n};\n\nconst INDENT: &str = \"    \";\n\nconst REDUCER_EVENTS: &str = r#\"\n    public interface IRemoteDbContext : IDbContext<RemoteTables, RemoteReducers, SubscriptionBuilder, RemoteProcedures> {\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError;\n    }\n\n    public sealed class EventContext : IEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n\n        /// <summary>\n        /// The event that caused this callback to run.\n        /// </summary>\n        public readonly Event<Reducer> Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect() {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal EventContext(DbConnection conn, Event<Reducer> Event)\n        {\n            this.conn = conn;\n            this.Event = Event;\n        }\n    }\n\n    public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The reducer event that caused this callback to run.\n        /// </summary>\n        public readonly ReducerEvent<Reducer> Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect() {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ReducerEventContext(DbConnection conn, ReducerEvent<Reducer> reducerEvent)\n        {\n            this.conn = conn;\n            Event = reducerEvent;\n        }\n    }\n\n    public sealed class ErrorContext : IErrorContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The <c>Exception</c> that caused this error callback to be run.\n        /// </summary>\n        public readonly Exception Event;\n        Exception IErrorContext.Event {\n            get {\n                return Event;\n            }\n        }\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect() {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ErrorContext(DbConnection conn, Exception error)\n        {\n            this.conn = conn;\n            Event = error;\n        }\n    }\n\n    public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect() {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal SubscriptionEventContext(DbConnection conn)\n        {\n            this.conn = conn;\n        }\n    }\n\n    public sealed class ProcedureEventContext : IProcedureEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The procedure event that caused this callback to run.\n        /// </summary>\n        public readonly ProcedureEvent Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect() {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ProcedureEventContext(DbConnection conn, ProcedureEvent Event)\n        {\n            this.conn = conn;\n            this.Event = Event;\n        }\n    }\n\n    /// <summary>\n    /// Builder-pattern constructor for subscription queries.\n    /// </summary>\n    public sealed class SubscriptionBuilder\n    {\n        private readonly IDbConnection conn;\n\n        private event Action<SubscriptionEventContext>? Applied;\n        private event Action<ErrorContext, Exception>? Error;\n\n        /// <summary>\n        /// Private API, use <c>conn.SubscriptionBuilder()</c> instead.\n        /// </summary>\n        public SubscriptionBuilder(IDbConnection conn)\n        {\n            this.conn = conn;\n        }\n\n        /// <summary>\n        /// Register a callback to run when the subscription is applied.\n        /// </summary>\n        public SubscriptionBuilder OnApplied(\n            Action<SubscriptionEventContext> callback\n        )\n        {\n            Applied += callback;\n            return this;\n        }\n\n        /// <summary>\n        /// Register a callback to run when the subscription fails.\n        ///\n        /// Note that this callback may run either when attempting to apply the subscription,\n        /// in which case <c>Self::on_applied</c> will never run,\n        /// or later during the subscription's lifetime if the module's interface changes,\n        /// in which case <c>Self::on_applied</c> may have already run.\n        /// </summary>\n        public SubscriptionBuilder OnError(\n            Action<ErrorContext, Exception> callback\n        )\n        {\n            Error += callback;\n            return this;\n        }\n    \n        /// <summary>\n        /// Add a typed query to this subscription.\n        ///\n        /// This is the entry point for building subscriptions without writing SQL by hand.\n        /// Once a typed query is added, only typed queries may follow (SQL and typed queries cannot be mixed).\n        /// </summary>\n        public TypedSubscriptionBuilder AddQuery<TRow>(\n            Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build\n        )\n        {\n            var typed = new TypedSubscriptionBuilder(conn, Applied, Error);\n            return typed.AddQuery(build);\n        }\n\n        /// <summary>\n        /// Subscribe to the following SQL queries.\n        ///\n        /// This method returns immediately, with the data not yet added to the DbConnection.\n        /// The provided callbacks will be invoked once the data is returned from the remote server.\n        /// Data from all the provided queries will be returned at the same time.\n        ///\n        /// See the SpacetimeDB SQL docs for more information on SQL syntax:\n        /// <a href=\"https://spacetimedb.com/docs/sql\">https://spacetimedb.com/docs/sql</a>\n        /// </summary>\n        public SubscriptionHandle Subscribe(\n            string[] querySqls\n        ) => new(conn, Applied, Error, querySqls);\n\n        /// <summary>\n        /// Subscribe to all rows from all tables.\n        ///\n        /// This method is intended as a convenience\n        /// for applications where client-side memory use and network bandwidth are not concerns.\n        /// Applications where these resources are a constraint\n        /// should register more precise queries via <c>Self.Subscribe</c>\n        /// in order to replicate only the subset of data which the client needs to function.\n        ///\n        /// This method should not be combined with <c>Self.Subscribe</c> on the same <c>DbConnection</c>.\n        /// A connection may either <c>Self.Subscribe</c> to particular queries,\n        /// or <c>Self.SubscribeToAllTables</c>, but not both.\n        /// Attempting to call <c>Self.Subscribe</c>\n        /// on a <c>DbConnection</c> that has previously used <c>Self.SubscribeToAllTables</c>,\n        /// or vice versa, may misbehave in any number of ways,\n        /// including dropping subscriptions, corrupting the client cache, or panicking.\n        /// </summary>\n        public SubscriptionHandle SubscribeToAllTables() =>\n            new(conn, Applied, Error, QueryBuilder.AllTablesSqlQueries());\n    }\n\n    public sealed class SubscriptionHandle : SubscriptionHandleBase<SubscriptionEventContext, ErrorContext> {\n        /// <summary>\n        /// Internal API. Construct <c>SubscriptionHandle</c>s using <c>conn.SubscriptionBuilder</c>.\n        /// </summary>\n        public SubscriptionHandle(\n            IDbConnection conn,\n            Action<SubscriptionEventContext>? onApplied,\n            Action<ErrorContext, Exception>? onError,\n            string[] querySqls\n        ) : base(conn, onApplied, onError, querySqls)\n        { }\n    }\n\"#;\n\npub struct Csharp<'opts> {\n    pub namespace: &'opts str,\n}\n\nimpl Lang for Csharp<'_> {\n    fn generate_table_file_from_schema(&self, module: &ModuleDef, table: &TableDef, schema: TableSchema) -> OutputFile {\n        let mut output = CsharpAutogen::new(\n            self.namespace,\n            &[\n                \"SpacetimeDB.BSATN\",\n                \"SpacetimeDB.ClientApi\",\n                \"System.Collections.Generic\",\n                \"System.Runtime.Serialization\",\n            ],\n            false,\n        );\n\n        writeln!(output, \"public sealed partial class RemoteTables\");\n        indented_block(&mut output, |output| {\n            let csharp_table_name = table.accessor_name.deref().to_case(Case::Pascal);\n            let csharp_table_class_name = csharp_table_name.clone() + \"Handle\";\n            let table_type = type_ref_name(module, table.product_type_ref);\n\n            let base_class = if table.is_event {\n                \"RemoteEventTableHandle\"\n            } else {\n                \"RemoteTableHandle\"\n            };\n            writeln!(\n                output,\n                \"public sealed class {csharp_table_class_name} : {base_class}<EventContext, {table_type}>\"\n            );\n            indented_block(output, |output| {\n                writeln!(\n                    output,\n                    \"protected override string RemoteTableName => \\\"{}\\\";\",\n                    table.name\n                );\n                writeln!(output);\n\n                // If this is a table, we want to generate event accessor and indexes\n                let product_type: &ProductTypeDef = module.typespace_for_generate()[table.product_type_ref]\n                    .as_product()\n                    .unwrap();\n\n                let mut index_names = Vec::new();\n\n                for idx in iter_indexes(table) {\n                    let Some(accessor_name) = idx.accessor_name.as_ref() else {\n                        // If there is no accessor name, we shouldn't generate a client-side index accessor.\n                        continue;\n                    };\n\n                    // Whatever the index algorithm on the host,\n                    // the client can still use btrees.\n                    let columns = idx.algorithm.columns();\n                    let get_csharp_field_name_and_type = |col_pos: ColId| {\n                        let (field_name, field_type) = &product_type.elements[col_pos.idx()];\n                        let csharp_field_name_pascal = field_name.deref().to_case(Case::Pascal);\n                        let csharp_field_type = ty_fmt(module, field_type);\n                        (csharp_field_name_pascal, csharp_field_type)\n                    };\n\n                    let (row_to_key, key_type) = match columns.as_singleton() {\n                        Some(col_pos) => {\n                            let (field_name, field_type) = get_csharp_field_name_and_type(col_pos);\n                            (format!(\"row.{field_name}\"), field_type.to_string())\n                        }\n                        None => {\n                            let mut key_accessors = Vec::new();\n                            let mut key_type_elems = Vec::new();\n                            for (field_name, field_type) in columns.iter().map(get_csharp_field_name_and_type) {\n                                key_accessors.push(format!(\"row.{field_name}\"));\n                                key_type_elems.push(format!(\"{field_type} {field_name}\"));\n                            }\n                            (\n                                format!(\"({})\", key_accessors.join(\", \")),\n                                format!(\"({})\", key_type_elems.join(\", \")),\n                            )\n                        }\n                    };\n\n                    let csharp_index_name = accessor_name.deref().to_case(Case::Pascal);\n\n                    let mut csharp_index_class_name = csharp_index_name.clone();\n                    let csharp_index_base_class_name = if schema.is_unique(&columns) {\n                        csharp_index_class_name += \"UniqueIndex\";\n                        \"UniqueIndexBase\"\n                    } else {\n                        csharp_index_class_name += \"Index\";\n                        \"BTreeIndexBase\"\n                    };\n\n                    writeln!(\n                        output,\n                        \"public sealed class {csharp_index_class_name} : {csharp_index_base_class_name}<{key_type}>\"\n                    );\n                    indented_block(output, |output| {\n                        writeln!(\n                            output,\n                            \"protected override {key_type} GetKey({table_type} row) => {row_to_key};\"\n                        );\n                        writeln!(output);\n                        writeln!(\n                            output,\n                            \"public {csharp_index_class_name}({csharp_table_class_name} table) : base(table) {{ }}\"\n                        );\n                    });\n                    writeln!(output);\n                    writeln!(output, \"public readonly {csharp_index_class_name} {csharp_index_name};\");\n                    writeln!(output);\n\n                    index_names.push(csharp_index_name);\n                }\n\n                writeln!(\n                    output,\n                    \"internal {csharp_table_class_name}(DbConnection conn) : base(conn)\"\n                );\n                indented_block(output, |output| {\n                    for csharp_index_name in &index_names {\n                        writeln!(output, \"{csharp_index_name} = new(this);\");\n                    }\n                });\n\n                if let Some(primary_col_index) = schema.pk() {\n                    writeln!(output);\n                    writeln!(\n                        output,\n                        \"protected override object GetPrimaryKey({table_type} row) => row.{col_name_pascal_case};\",\n                        col_name_pascal_case = primary_col_index.col_name.deref().to_case(Case::Pascal)\n                    );\n                }\n            });\n            writeln!(output);\n            writeln!(output, \"public readonly {csharp_table_class_name} {csharp_table_name};\");\n        });\n\n        // Emit top-level Cols/IxCols helpers for the typed query builder.\n        writeln!(output);\n\n        let cols_owner_name = table.accessor_name.deref().to_case(Case::Pascal);\n        let row_type = type_ref_name(module, table.product_type_ref);\n        let product_type = module.typespace_for_generate()[table.product_type_ref]\n            .as_product()\n            .unwrap();\n\n        let mut ix_col_positions: BTreeSet<usize> = BTreeSet::new();\n        for idx in iter_indexes(table) {\n            if let IndexAlgorithm::BTree(BTreeAlgorithm { columns }) = &idx.algorithm {\n                for col_pos in columns.iter() {\n                    ix_col_positions.insert(col_pos.idx());\n                }\n            }\n        }\n        for (columns, constraints) in schema.backcompat_column_constraints() {\n            if constraints.has_indexed() || constraints.has_unique() || constraints.has_primary_key() {\n                for col_pos in columns.iter() {\n                    ix_col_positions.insert(col_pos.idx());\n                }\n            }\n        }\n\n        writeln!(output, \"public sealed class {cols_owner_name}Cols\");\n        indented_block(&mut output, |output| {\n            for (field_name, field_type) in &product_type.elements {\n                let prop = field_name.deref().to_case(Case::Pascal);\n                let (col_ty, ty) = match field_type {\n                    AlgebraicTypeUse::Option(inner) => (\"Col\", ty_fmt(module, inner).to_string()),\n                    _ => (\"Col\", ty_fmt(module, field_type).to_string()),\n                };\n                writeln!(\n                    output,\n                    \"public global::SpacetimeDB.{col_ty}<{row_type}, {ty}> {prop} {{ get; }}\"\n                );\n            }\n            writeln!(output);\n            writeln!(output, \"public {cols_owner_name}Cols(string tableName)\");\n            indented_block(output, |output| {\n                for (field_name, field_type) in &product_type.elements {\n                    let prop = field_name.deref().to_case(Case::Pascal);\n                    let (col_ty, ty) = match field_type {\n                        AlgebraicTypeUse::Option(inner) => (\"Col\", ty_fmt(module, inner).to_string()),\n                        _ => (\"Col\", ty_fmt(module, field_type).to_string()),\n                    };\n                    let col_name = field_name.deref();\n                    writeln!(\n                        output,\n                        \"{prop} = new global::SpacetimeDB.{col_ty}<{row_type}, {ty}>(tableName, \\\"{col_name}\\\");\"\n                    );\n                }\n            });\n        });\n        writeln!(output);\n\n        writeln!(output, \"public sealed class {cols_owner_name}IxCols\");\n        indented_block(&mut output, |output| {\n            for (i, (field_name, field_type)) in product_type.elements.iter().enumerate() {\n                if !ix_col_positions.contains(&i) {\n                    continue;\n                }\n                let prop = field_name.deref().to_case(Case::Pascal);\n                let (col_ty, ty) = match field_type {\n                    AlgebraicTypeUse::Option(inner) => (\"IxCol\", ty_fmt(module, inner).to_string()),\n                    _ => (\"IxCol\", ty_fmt(module, field_type).to_string()),\n                };\n                writeln!(\n                    output,\n                    \"public global::SpacetimeDB.{col_ty}<{row_type}, {ty}> {prop} {{ get; }}\"\n                );\n            }\n            writeln!(output);\n            writeln!(output, \"public {cols_owner_name}IxCols(string tableName)\");\n            indented_block(output, |output| {\n                for (i, (field_name, field_type)) in product_type.elements.iter().enumerate() {\n                    if !ix_col_positions.contains(&i) {\n                        continue;\n                    }\n                    let prop = field_name.deref().to_case(Case::Pascal);\n                    let (col_ty, ty) = match field_type {\n                        AlgebraicTypeUse::Option(inner) => (\"IxCol\", ty_fmt(module, inner).to_string()),\n                        _ => (\"IxCol\", ty_fmt(module, field_type).to_string()),\n                    };\n                    let col_name = field_name.deref();\n                    writeln!(\n                        output,\n                        \"{prop} = new global::SpacetimeDB.{col_ty}<{row_type}, {ty}>(tableName, \\\"{col_name}\\\");\"\n                    );\n                }\n            });\n        });\n\n        OutputFile {\n            filename: format!(\"Tables/{}.g.cs\", table.accessor_name.deref().to_case(Case::Pascal)),\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_type_files(&self, module: &ModuleDef, typ: &TypeDef) -> Vec<OutputFile> {\n        let name = collect_case(Case::Pascal, typ.accessor_name.name_segments());\n        let filename = format!(\"Types/{name}.g.cs\");\n        let code = match &module.typespace_for_generate()[typ.ty] {\n            AlgebraicTypeDef::Sum(sum) => autogen_csharp_sum(module, name.clone(), sum, self.namespace),\n            AlgebraicTypeDef::Product(prod) => autogen_csharp_tuple(module, name.clone(), prod, self.namespace),\n            AlgebraicTypeDef::PlainEnum(plain_enum) => {\n                autogen_csharp_plain_enum(name.clone(), plain_enum, self.namespace)\n            }\n        };\n\n        vec![OutputFile { filename, code }]\n    }\n\n    fn generate_reducer_file(&self, module: &ModuleDef, reducer: &spacetimedb_schema::def::ReducerDef) -> OutputFile {\n        let mut output = CsharpAutogen::new(\n            self.namespace,\n            &[\n                \"SpacetimeDB.ClientApi\",\n                \"System.Collections.Generic\",\n                \"System.Runtime.Serialization\",\n            ],\n            false,\n        );\n\n        writeln!(output, \"public sealed partial class RemoteReducers : RemoteBase\");\n        indented_block(&mut output, |output| {\n            let func_name_pascal_case = reducer.accessor_name.deref().to_case(Case::Pascal);\n            let delegate_separator = if reducer.params_for_generate.elements.is_empty() {\n                \"\"\n            } else {\n                \", \"\n            };\n\n            let (func_params, func_args) =\n                build_func_params_and_args(module, reducer.params_for_generate.into_iter(), self.namespace);\n\n            writeln!(\n                output,\n                \"public delegate void {func_name_pascal_case}Handler(ReducerEventContext ctx{delegate_separator}{func_params});\"\n            );\n            writeln!(\n                output,\n                \"public event {func_name_pascal_case}Handler? On{func_name_pascal_case};\"\n            );\n            writeln!(output);\n\n            if is_reducer_invokable(reducer) {\n                writeln!(output, \"public void {func_name_pascal_case}({func_params})\");\n                indented_block(output, |output| {\n                    writeln!(\n                        output,\n                        \"conn.InternalCallReducer(new Reducer.{func_name_pascal_case}({func_args}));\"\n                    );\n                });\n                writeln!(output);\n            }\n\n            writeln!(\n                output,\n                \"public bool Invoke{func_name_pascal_case}(ReducerEventContext ctx, Reducer.{func_name_pascal_case} args)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"if (On{func_name_pascal_case} == null)\");\n                indented_block(output, |output| {\n                    writeln!(output, \"if (InternalOnUnhandledReducerError != null)\");\n                    indented_block(output, |output| {\n                        writeln!(output, \"switch(ctx.Event.Status)\");\n                        indented_block(output, |output| {\n                            writeln!(output, \"case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\");\n                            writeln!(output, \"case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\\\"out of energy\\\")); break;\");\n                        });\n                    });\n                    writeln!(output, \"return false;\");\n                });\n\n                writeln!(output, \"On{func_name_pascal_case}(\");\n                // Write out arguments one per line\n                {\n                    indent_scope!(output);\n                    write!(output, \"ctx\");\n                    for (arg_name, _) in &reducer.params_for_generate {\n                        writeln!(output, \",\");\n                        let arg_name = arg_name.deref().to_case(Case::Pascal);\n                        write!(output, \"args.{arg_name}\");\n                    }\n                    writeln!(output);\n                }\n                writeln!(output, \");\");\n                writeln!(output, \"return true;\");\n            });\n        });\n\n        writeln!(output);\n\n        writeln!(output, \"public abstract partial class Reducer\");\n        indented_block(&mut output, |output| {\n            autogen_csharp_product_common(\n                module,\n                output,\n                reducer.accessor_name.deref().to_case(Case::Pascal),\n                &reducer.params_for_generate,\n                \"Reducer, IReducerArgs\",\n                |output| {\n                    if !reducer.params_for_generate.elements.is_empty() {\n                        writeln!(output);\n                    }\n                    writeln!(output, \"string IReducerArgs.ReducerName => \\\"{}\\\";\", reducer.name);\n                },\n            );\n        });\n\n        OutputFile {\n            filename: format!(\"Reducers/{}.g.cs\", reducer.accessor_name.deref().to_case(Case::Pascal)),\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_procedure_file(\n        &self,\n        module: &ModuleDef,\n        procedure: &spacetimedb_schema::def::ProcedureDef,\n    ) -> OutputFile {\n        let mut output = CsharpAutogen::new(\n            self.namespace,\n            &[\n                \"SpacetimeDB.ClientApi\",\n                \"System.Collections.Generic\",\n                \"System.Runtime.Serialization\",\n            ],\n            false,\n        );\n\n        writeln!(output, \"public sealed partial class RemoteProcedures : RemoteBase\");\n        indented_block(&mut output, |output| {\n            let func_name_pascal_case = procedure.accessor_name.deref().to_case(Case::Pascal);\n            let delegate_separator = if procedure.params_for_generate.elements.is_empty() {\n                \"\"\n            } else {\n                \", \"\n            };\n\n            let (func_params, func_args) =\n                build_func_params_and_args(module, procedure.params_for_generate.into_iter(), self.namespace);\n            let return_type_str = ty_fmt_with_ns(module, &procedure.return_type_for_generate, self.namespace);\n            // Generate the clean public API that users call to allow us of BSATN.Decode<> then reflect to the proper return type\n            writeln!(\n                output,\n                \"public void {func_name_pascal_case}({func_params}{delegate_separator}ProcedureCallback<{return_type_str}> callback)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"// Convert the clean callback to the wrapper callback\");\n                writeln!(\n                    output,\n                    \"Internal{func_name_pascal_case}({func_args}{delegate_separator}(ctx, result) => {{\"\n                );\n\n                writeln!(output, \"if (result.IsSuccess && result.Value != null)\");\n                indented_block(output, |output| {\n                    writeln!(\n                        output,\n                        \"callback(ctx, ProcedureCallbackResult<{return_type_str}>.Success(result.Value.Value));\"\n                    );\n                });\n                writeln!(output, \"else\");\n                indented_block(output, |output| {\n                    writeln!(\n                        output,\n                        \"callback(ctx, ProcedureCallbackResult<{return_type_str}>.Failure(result.Error!));\"\n                    );\n                });\n                writeln!(output, \"}});\");\n            });\n            writeln!(output);\n\n            // Generate the private wrapper method that handles BSATN\n            writeln!(\n                output,\n                \"private void Internal{func_name_pascal_case}({func_params}{delegate_separator}ProcedureCallback<Procedure.{func_name_pascal_case}> callback)\"\n            );\n            indented_block(output, |output| {\n                writeln!(\n                    output,\n                    \"conn.InternalCallProcedure(new Procedure.{func_name_pascal_case}Args({func_args}), callback);\"\n                );\n            });\n            writeln!(output);\n        });\n\n        writeln!(output);\n\n        writeln!(output, \"public abstract partial class Procedure\");\n        indented_block(&mut output, |output| {\n            autogen_csharp_proc_return(\n                module,\n                output,\n                procedure.accessor_name.deref().to_case(Case::Pascal).to_string(),\n                &procedure.return_type_for_generate,\n                self.namespace,\n            );\n            autogen_csharp_product_common(\n                module,\n                output,\n                format!(\"{}Args\", procedure.accessor_name.deref().to_case(Case::Pascal)),\n                &procedure.params_for_generate,\n                \"Procedure, IProcedureArgs\",\n                |output| {\n                    if !procedure.params_for_generate.elements.is_empty() {\n                        writeln!(output);\n                    }\n                    writeln!(output, \"string IProcedureArgs.ProcedureName => \\\"{}\\\";\", procedure.name);\n                },\n            );\n            writeln!(output);\n        });\n\n        OutputFile {\n            filename: format!(\n                \"Procedures/{}.g.cs\",\n                procedure.accessor_name.deref().to_case(Case::Pascal)\n            ),\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_global_files(&self, module: &ModuleDef, options: &CodegenOptions) -> Vec<OutputFile> {\n        let mut output = CsharpAutogen::new(\n            self.namespace,\n            &[\n                \"SpacetimeDB.ClientApi\",\n                \"System.Collections.Generic\",\n                \"System.Runtime.Serialization\",\n            ],\n            true, // print the version in the globals file\n        );\n\n        writeln!(output, \"public sealed partial class RemoteReducers : RemoteBase\");\n        indented_block(&mut output, |output| {\n            writeln!(output, \"internal RemoteReducers(DbConnection conn) : base(conn) {{ }}\");\n            writeln!(\n                output,\n                \"internal event Action<ReducerEventContext, Exception>? InternalOnUnhandledReducerError;\"\n            )\n        });\n        writeln!(output);\n\n        writeln!(output, \"public sealed partial class RemoteProcedures : RemoteBase\");\n        indented_block(&mut output, |output| {\n            writeln!(\n                output,\n                \"internal RemoteProcedures(DbConnection conn) : base(conn) {{ }}\"\n            );\n        });\n        writeln!(output);\n\n        writeln!(output, \"public sealed partial class RemoteTables : RemoteTablesBase\");\n        indented_block(&mut output, |output| {\n            writeln!(output, \"public RemoteTables(DbConnection conn)\");\n            indented_block(output, |output| {\n                for (_, accessor_name, _) in iter_table_names_and_types(module, options.visibility) {\n                    writeln!(\n                        output,\n                        \"AddTable({} = new(conn));\",\n                        accessor_name.deref().to_case(Case::Pascal)\n                    );\n                }\n            });\n        });\n        writeln!(output);\n\n        writeln!(output, \"{REDUCER_EVENTS}\");\n\n        writeln!(output, \"public sealed class QueryBuilder\");\n        indented_block(&mut output, |output| {\n            writeln!(output, \"public From From {{ get; }} = new();\");\n            writeln!(output);\n            writeln!(output, \"internal static string[] AllTablesSqlQueries() => new string[]\");\n            indented_block(output, |output| {\n                for (_, accessor_name, _) in iter_table_names_and_types(module, options.visibility) {\n                    let method_name = accessor_name.deref().to_case(Case::Pascal);\n                    writeln!(output, \"new QueryBuilder().From.{method_name}().ToSql(),\");\n                }\n            });\n            writeln!(output, \";\");\n        });\n        writeln!(output);\n\n        writeln!(output, \"public sealed class From\");\n        indented_block(&mut output, |output| {\n            for (name, accessor_name, product_type_ref) in iter_table_names_and_types(module, options.visibility) {\n                let method_name = accessor_name.deref().to_case(Case::Pascal);\n                let row_type = type_ref_name(module, product_type_ref);\n                let table_name_lit = format!(\"{:?}\", name.deref());\n                writeln!(\n                    output,\n                    \"public global::SpacetimeDB.Table<{row_type}, {method_name}Cols, {method_name}IxCols> {method_name}() => new({table_name_lit}, new {method_name}Cols({table_name_lit}), new {method_name}IxCols({table_name_lit}));\"\n                );\n            }\n        });\n        writeln!(output);\n\n        writeln!(output, \"public sealed class TypedSubscriptionBuilder\");\n        indented_block(&mut output, |output| {\n            writeln!(output, \"private readonly IDbConnection conn;\");\n            writeln!(output, \"private Action<SubscriptionEventContext>? Applied;\");\n            writeln!(output, \"private Action<ErrorContext, Exception>? Error;\");\n            writeln!(output, \"private readonly List<string> querySqls = new();\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"internal TypedSubscriptionBuilder(IDbConnection conn, Action<SubscriptionEventContext>? applied, Action<ErrorContext, Exception>? error)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"this.conn = conn;\");\n                writeln!(output, \"Applied = applied;\");\n                writeln!(output, \"Error = error;\");\n            });\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"public TypedSubscriptionBuilder OnApplied(Action<SubscriptionEventContext> callback)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"Applied += callback;\");\n                writeln!(output, \"return this;\");\n            });\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"public TypedSubscriptionBuilder OnError(Action<ErrorContext, Exception> callback)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"Error += callback;\");\n                writeln!(output, \"return this;\");\n            });\n            writeln!(output);\n\n            writeln!(output, \"public TypedSubscriptionBuilder AddQuery<TRow>(Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build)\");\n            indented_block(output, |output| {\n                writeln!(output, \"var qb = new QueryBuilder();\");\n                writeln!(output, \"querySqls.Add(build(qb).ToSql());\");\n                writeln!(output, \"return this;\");\n            });\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"public SubscriptionHandle Subscribe() => new(conn, Applied, Error, querySqls.ToArray());\"\n            );\n        });\n        writeln!(output);\n\n        writeln!(output, \"public abstract partial class Reducer\");\n        indented_block(&mut output, |output| {\n            // Prevent instantiation of this class from outside.\n            writeln!(output, \"private Reducer() {{ }}\");\n        });\n        writeln!(output);\n\n        writeln!(output, \"public abstract partial class Procedure\");\n        indented_block(&mut output, |output| {\n            // Prevent instantiation of this class from outside.\n            writeln!(output, \"private Procedure() {{ }}\");\n        });\n        writeln!(output);\n\n        writeln!(\n            output,\n            \"public sealed class DbConnection : DbConnectionBase<DbConnection, RemoteTables, Reducer>\"\n        );\n        indented_block(&mut output, |output: &mut CodeIndenter<String>| {\n            writeln!(output, \"public override RemoteTables Db {{ get; }}\");\n            writeln!(output, \"public readonly RemoteReducers Reducers;\");\n            writeln!(output, \"public readonly RemoteProcedures Procedures;\");\n            writeln!(output);\n\n            writeln!(output, \"public DbConnection()\");\n            indented_block(output, |output| {\n                writeln!(output, \"Db = new(this);\");\n                writeln!(output, \"Reducers = new(this);\");\n                writeln!(output, \"Procedures = new(this);\");\n            });\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override IEventContext ToEventContext(Event<Reducer> Event) =>\"\n            );\n            writeln!(output, \"new EventContext(this, Event);\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override IReducerEventContext ToReducerEventContext(ReducerEvent<Reducer> reducerEvent) =>\"\n            );\n            writeln!(output, \"new ReducerEventContext(this, reducerEvent);\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override ISubscriptionEventContext MakeSubscriptionEventContext() =>\"\n            );\n            writeln!(output, \"new SubscriptionEventContext(this);\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override IErrorContext ToErrorContext(Exception exception) =>\"\n            );\n            writeln!(output, \"new ErrorContext(this, exception);\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override IProcedureEventContext ToProcedureEventContext(ProcedureEvent procedureEvent) =>\"\n            );\n            writeln!(output, \"new ProcedureEventContext(this, procedureEvent);\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"protected override bool Dispatch(IReducerEventContext context, Reducer reducer)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"var eventContext = (ReducerEventContext)context;\");\n                writeln!(output, \"return reducer switch {{\");\n                {\n                    indent_scope!(output);\n                    for reducer_name in\n                        iter_reducers(module, options.visibility).map(|r| r.accessor_name.deref().to_case(Case::Pascal))\n                    {\n                        writeln!(\n                            output,\n                            \"Reducer.{reducer_name} args => Reducers.Invoke{reducer_name}(eventContext, args),\"\n                        );\n                    }\n                    writeln!(\n                        output,\n                        r#\"_ => throw new ArgumentOutOfRangeException(\"Reducer\", $\"Unknown reducer {{reducer}}\")\"#\n                    );\n                }\n                writeln!(output, \"}};\");\n            });\n            writeln!(output);\n\n            writeln!(output, \"public SubscriptionBuilder SubscriptionBuilder() => new(this);\");\n            writeln!(\n                output,\n                \"public event Action<ReducerEventContext, Exception> OnUnhandledReducerError\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"add => Reducers.InternalOnUnhandledReducerError += value;\");\n                writeln!(output, \"remove => Reducers.InternalOnUnhandledReducerError -= value;\");\n            });\n        });\n\n        vec![OutputFile {\n            filename: \"SpacetimeDBClient.g.cs\".to_owned(),\n            code: output.into_inner(),\n        }]\n    }\n}\n\nfn ty_fmt<'a>(module: &'a ModuleDef, ty: &'a AlgebraicTypeUse) -> impl fmt::Display + 'a {\n    fmt_fn(move |f| match ty {\n        AlgebraicTypeUse::Identity => f.write_str(\"SpacetimeDB.Identity\"),\n        AlgebraicTypeUse::ConnectionId => f.write_str(\"SpacetimeDB.ConnectionId\"),\n        AlgebraicTypeUse::ScheduleAt => f.write_str(\"SpacetimeDB.ScheduleAt\"),\n        AlgebraicTypeUse::Timestamp => f.write_str(\"SpacetimeDB.Timestamp\"),\n        AlgebraicTypeUse::TimeDuration => f.write_str(\"SpacetimeDB.TimeDuration\"),\n        AlgebraicTypeUse::Uuid => f.write_str(\"SpacetimeDB.Uuid\"),\n        AlgebraicTypeUse::Unit => f.write_str(\"SpacetimeDB.Unit\"),\n        AlgebraicTypeUse::Option(inner_ty) => write!(f, \"{}?\", ty_fmt(module, inner_ty)),\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => write!(\n            f,\n            \"SpacetimeDB.Result<{}, {}>\",\n            ty_fmt(module, ok_ty),\n            ty_fmt(module, err_ty)\n        ),\n        AlgebraicTypeUse::Array(elem_ty) => write!(f, \"System.Collections.Generic.List<{}>\", ty_fmt(module, elem_ty)),\n        AlgebraicTypeUse::String => f.write_str(\"string\"),\n        AlgebraicTypeUse::Ref(r) => f.write_str(&type_ref_name(module, *r)),\n        AlgebraicTypeUse::Primitive(prim) => f.write_str(match prim {\n            PrimitiveType::Bool => \"bool\",\n            PrimitiveType::I8 => \"sbyte\",\n            PrimitiveType::U8 => \"byte\",\n            PrimitiveType::I16 => \"short\",\n            PrimitiveType::U16 => \"ushort\",\n            PrimitiveType::I32 => \"int\",\n            PrimitiveType::U32 => \"uint\",\n            PrimitiveType::I64 => \"long\",\n            PrimitiveType::U64 => \"ulong\",\n            PrimitiveType::I128 => \"I128\",\n            PrimitiveType::U128 => \"U128\",\n            PrimitiveType::I256 => \"I256\",\n            PrimitiveType::U256 => \"U256\",\n            PrimitiveType::F32 => \"float\",\n            PrimitiveType::F64 => \"double\",\n        }),\n        AlgebraicTypeUse::Never => unimplemented!(),\n    })\n}\n\n/// Like `ty_fmt`, but prefixes type references with the provided namespace.\nfn ty_fmt_with_ns<'a>(module: &'a ModuleDef, ty: &'a AlgebraicTypeUse, namespace: &'a str) -> impl fmt::Display + 'a {\n    fmt_fn(move |f| match ty {\n        AlgebraicTypeUse::Identity => f.write_str(\"SpacetimeDB.Identity\"),\n        AlgebraicTypeUse::ConnectionId => f.write_str(\"SpacetimeDB.ConnectionId\"),\n        AlgebraicTypeUse::ScheduleAt => f.write_str(\"SpacetimeDB.ScheduleAt\"),\n        AlgebraicTypeUse::Timestamp => f.write_str(\"SpacetimeDB.Timestamp\"),\n        AlgebraicTypeUse::TimeDuration => f.write_str(\"SpacetimeDB.TimeDuration\"),\n        AlgebraicTypeUse::Uuid => f.write_str(\"SpacetimeDB.Uuid\"),\n        AlgebraicTypeUse::Unit => f.write_str(\"SpacetimeDB.Unit\"),\n        AlgebraicTypeUse::Option(inner_ty) => write!(f, \"{}?\", ty_fmt_with_ns(module, inner_ty, namespace)),\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => write!(\n            f,\n            \"SpacetimeDB.Result<{}, {}>\",\n            ty_fmt_with_ns(module, ok_ty, namespace),\n            ty_fmt_with_ns(module, err_ty, namespace)\n        ),\n        AlgebraicTypeUse::Array(elem_ty) => write!(\n            f,\n            \"System.Collections.Generic.List<{}>\",\n            ty_fmt_with_ns(module, elem_ty, namespace)\n        ),\n        AlgebraicTypeUse::String => f.write_str(\"string\"),\n        AlgebraicTypeUse::Ref(r) => write!(f, \"{}.{}\", namespace, type_ref_name(module, *r)),\n        AlgebraicTypeUse::Primitive(prim) => f.write_str(match prim {\n            PrimitiveType::Bool => \"bool\",\n            PrimitiveType::I8 => \"sbyte\",\n            PrimitiveType::U8 => \"byte\",\n            PrimitiveType::I16 => \"short\",\n            PrimitiveType::U16 => \"ushort\",\n            PrimitiveType::I32 => \"int\",\n            PrimitiveType::U32 => \"uint\",\n            PrimitiveType::I64 => \"long\",\n            PrimitiveType::U64 => \"ulong\",\n            PrimitiveType::I128 => \"I128\",\n            PrimitiveType::U128 => \"U128\",\n            PrimitiveType::I256 => \"I256\",\n            PrimitiveType::U256 => \"U256\",\n            PrimitiveType::F32 => \"float\",\n            PrimitiveType::F64 => \"double\",\n        }),\n        AlgebraicTypeUse::Never => unimplemented!(),\n    })\n}\n\nfn default_init(ctx: &TypespaceForGenerate, ty: &AlgebraicTypeUse) -> Option<&'static str> {\n    match ty {\n        // Options (`T?`) have a default value of null which is fine for us.\n        AlgebraicTypeUse::Option(_) => None,\n        AlgebraicTypeUse::Ref(r) => match &ctx[*r] {\n            // TODO: generate some proper default here (what would it be for tagged enums?).\n            AlgebraicTypeDef::Sum(_) => Some(\"null!\"),\n            // Simple enums have their own default (variant with value of zero).\n            AlgebraicTypeDef::PlainEnum(_) => None,\n            AlgebraicTypeDef::Product(_) => Some(\"new()\"),\n        },\n        // See Sum(_) handling above.\n        AlgebraicTypeUse::ScheduleAt => Some(\"null!\"),\n        AlgebraicTypeUse::Array(_) => Some(\"new()\"),\n        // Strings must have explicit default value of \"\".\n        AlgebraicTypeUse::String => Some(r#\"\"\"\"#),\n        // Primitives are initialized to zero automatically.\n        AlgebraicTypeUse::Primitive(_) => None,\n        // Result<,> must be explicitly initialized.\n        AlgebraicTypeUse::Result { .. } => Some(\"default!\"),\n        // these are structs, they are initialized to zero-filled automatically\n        AlgebraicTypeUse::Unit\n        | AlgebraicTypeUse::Identity\n        | AlgebraicTypeUse::ConnectionId\n        | AlgebraicTypeUse::Timestamp\n        | AlgebraicTypeUse::TimeDuration\n        | AlgebraicTypeUse::Uuid => None,\n        AlgebraicTypeUse::Never => unimplemented!(\"never types are not yet supported in C# output\"),\n    }\n}\n\nstruct CsharpAutogen {\n    output: CodeIndenter<String>,\n}\n\nimpl Deref for CsharpAutogen {\n    type Target = CodeIndenter<String>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.output\n    }\n}\n\nimpl std::ops::DerefMut for CsharpAutogen {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.output\n    }\n}\n\nimpl CsharpAutogen {\n    pub fn new(namespace: &str, extra_usings: &[&str], include_version: bool) -> Self {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n\n        print_auto_generated_file_comment(&mut output);\n        if include_version {\n            print_auto_generated_version_comment(&mut output);\n        }\n\n        writeln!(output, \"#nullable enable\");\n        writeln!(output);\n\n        writeln!(output, \"using System;\");\n        // Don't emit `using SpacetimeDB;` if we are going to be nested in the SpacetimeDB namespace.\n        if namespace\n            .split('.')\n            .next()\n            .expect(\"split always returns at least one string\")\n            != \"SpacetimeDB\"\n        {\n            writeln!(output, \"using SpacetimeDB;\");\n        }\n        for extra_using in extra_usings {\n            writeln!(output, \"using {extra_using};\");\n        }\n        writeln!(output);\n\n        writeln!(output, \"namespace {namespace}\");\n        writeln!(output, \"{{\");\n        output.indent(1);\n\n        Self { output }\n    }\n\n    pub fn into_inner(mut self) -> String {\n        self.dedent(1);\n        writeln!(self, \"}}\");\n\n        self.output.into_inner()\n    }\n}\n\nfn autogen_csharp_sum(module: &ModuleDef, sum_type_name: String, sum_type: &SumTypeDef, namespace: &str) -> String {\n    let mut output = CsharpAutogen::new(namespace, &[], false);\n\n    writeln!(output, \"[SpacetimeDB.Type]\");\n    write!(\n        output,\n        \"public partial record {sum_type_name} : SpacetimeDB.TaggedEnum<(\"\n    );\n    {\n        indent_scope!(output);\n        for (i, (variant_name, variant_ty)) in sum_type.variants.iter().enumerate() {\n            if i != 0 {\n                write!(output, \",\");\n            }\n            writeln!(output);\n            let variant_name = variant_name.deref().to_case(Case::Pascal);\n            write!(output, \"{} {variant_name}\", ty_fmt(module, variant_ty));\n        }\n        // If we have fewer than 2 variants, we need to add some dummy variants to make the tuple work.\n        match sum_type.variants.len() {\n            0 => {\n                writeln!(output);\n                writeln!(output, \"SpacetimeDB.Unit _Reserved1,\");\n                write!(output, \"SpacetimeDB.Unit _Reserved2\");\n            }\n            1 => {\n                writeln!(output, \",\");\n                write!(output, \"SpacetimeDB.Unit _Reserved\");\n            }\n            _ => {}\n        }\n    }\n    writeln!(output);\n    writeln!(output, \")>;\");\n\n    output.into_inner()\n}\n\nfn autogen_csharp_plain_enum(enum_type_name: String, enum_type: &PlainEnumTypeDef, namespace: &str) -> String {\n    let mut output = CsharpAutogen::new(namespace, &[], false);\n\n    writeln!(output, \"[SpacetimeDB.Type]\");\n    writeln!(output, \"public enum {enum_type_name}\");\n    indented_block(&mut output, |output| {\n        for variant in &*enum_type.variants {\n            let variant = variant.deref().to_case(Case::Pascal);\n            writeln!(output, \"{variant},\");\n        }\n    });\n\n    output.into_inner()\n}\n\nfn autogen_csharp_tuple(module: &ModuleDef, name: String, tuple: &ProductTypeDef, namespace: &str) -> String {\n    let mut output = CsharpAutogen::new(\n        namespace,\n        &[\"System.Collections.Generic\", \"System.Runtime.Serialization\"],\n        false,\n    );\n\n    autogen_csharp_product_common(module, &mut output, name, tuple, \"\", |_| {});\n\n    output.into_inner()\n}\n\nfn autogen_csharp_product_common(\n    module: &ModuleDef,\n    output: &mut CodeIndenter<String>,\n    name: String,\n    product_type: &ProductTypeDef,\n    base: &str,\n    extra_body: impl FnOnce(&mut CodeIndenter<String>),\n) {\n    writeln!(output, \"[SpacetimeDB.Type]\");\n    writeln!(output, \"[DataContract]\");\n    write!(output, \"public sealed partial class {name}\");\n    if !base.is_empty() {\n        write!(output, \" : {base}\");\n    }\n    writeln!(output);\n    indented_block(output, |output| {\n        let fields = product_type\n            .into_iter()\n            .map(|(orig_name, ty)| {\n                writeln!(output, \"[DataMember(Name = \\\"{orig_name}\\\")]\");\n\n                let field_name = orig_name.deref().to_case(Case::Pascal);\n                let ty = ty_fmt(module, ty).to_string();\n\n                writeln!(output, \"public {ty} {field_name};\");\n\n                (field_name, ty)\n            })\n            .collect::<Vec<_>>();\n\n        // If we don't have any fields, the default constructor is fine, otherwise we need to generate our own.\n        if !fields.is_empty() {\n            writeln!(output);\n\n            // Generate fully-parameterized constructor.\n            write!(output, \"public {name}(\");\n            if fields.len() > 1 {\n                writeln!(output);\n            }\n            {\n                indent_scope!(output);\n                for (i, (field_name, ty)) in fields.iter().enumerate() {\n                    if i != 0 {\n                        writeln!(output, \",\");\n                    }\n                    write!(output, \"{ty} {field_name}\");\n                }\n            }\n            if fields.len() > 1 {\n                writeln!(output);\n            }\n            writeln!(output, \")\");\n            indented_block(output, |output| {\n                for (field_name, _ty) in fields.iter() {\n                    writeln!(output, \"this.{field_name} = {field_name};\");\n                }\n            });\n            writeln!(output);\n\n            // Generate default constructor.\n            writeln!(output, \"public {name}()\");\n            indented_block(output, |output| {\n                for ((field_name, _ty), (_field, field_ty)) in fields.iter().zip(product_type) {\n                    if let Some(default) = default_init(module.typespace_for_generate(), field_ty) {\n                        writeln!(output, \"this.{field_name} = {default};\");\n                    }\n                }\n            });\n        }\n\n        extra_body(output);\n    });\n}\n\nfn autogen_csharp_proc_return(\n    module: &ModuleDef,\n    output: &mut CodeIndenter<String>,\n    name: String,\n    return_type: &AlgebraicTypeUse,\n    namespace: &str,\n) {\n    writeln!(output, \"[SpacetimeDB.Type]\");\n    writeln!(output, \"[DataContract]\");\n    write!(output, \"public sealed partial class {name}\");\n    writeln!(output);\n    indented_block(output, |output| {\n        // Generate the single field for the return value\n        writeln!(output, \"[DataMember(Name = \\\"Value\\\")]\");\n        let field_name = \"Value\".to_string();\n        let ty = ty_fmt_with_ns(module, return_type, namespace).to_string();\n        writeln!(output, \"public {ty} {field_name};\");\n\n        writeln!(output);\n\n        // Generate fully-parameterized constructor.\n        writeln!(output, \"public {name}({ty} {field_name})\");\n        indented_block(output, |output| {\n            writeln!(output, \"this.{field_name} = {field_name};\");\n        });\n        writeln!(output);\n\n        // Generate default constructor.\n        writeln!(output, \"public {name}()\");\n        indented_block(output, |output| {\n            if let Some(default) = default_init(module.typespace_for_generate(), return_type) {\n                writeln!(output, \"this.{field_name} = {default};\");\n            }\n        });\n    });\n}\n\nfn indented_block<R>(output: &mut CodeIndenter<String>, f: impl FnOnce(&mut CodeIndenter<String>) -> R) -> R {\n    writeln!(output, \"{{\");\n    let res = f(&mut output.indented(1));\n    writeln!(output, \"}}\");\n    res\n}\n\n/// Builds C# function parameter and argument lists from an iterator of parameter names and types.\nfn build_func_params_and_args<'a, I>(module: &ModuleDef, params_iter: I, namespace: &str) -> (String, String)\nwhere\n    I: Iterator<Item = &'a (Identifier, AlgebraicTypeUse)>,\n{\n    let mut func_params = String::new();\n    let mut func_args = String::new();\n\n    for (arg_i, (arg_name, arg_ty)) in params_iter.enumerate() {\n        if arg_i != 0 {\n            func_params.push_str(\", \");\n            func_args.push_str(\", \");\n        }\n\n        let arg_type_str = ty_fmt_with_ns(module, arg_ty, namespace);\n        let arg_name = arg_name.deref().to_case(Case::Camel);\n\n        write!(func_params, \"{arg_type_str} {arg_name}\").unwrap();\n        write!(func_args, \"{arg_name}\").unwrap();\n    }\n\n    (func_params, func_args)\n}\n"
  },
  {
    "path": "crates/codegen/src/lib.rs",
    "content": "use spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, TableDef, TypeDef, ViewDef};\nuse spacetimedb_schema::schema::{Schema, TableSchema};\nmod code_indenter;\npub mod cpp;\npub mod csharp;\npub mod rust;\npub mod typescript;\npub mod unrealcpp;\nmod util;\n\npub use self::csharp::Csharp;\npub use self::rust::Rust;\npub use self::typescript::TypeScript;\npub use self::unrealcpp::UnrealCpp;\npub use util::private_table_names;\npub use util::CodegenVisibility;\npub use util::AUTO_GENERATED_PREFIX;\n\n#[derive(Clone, Copy, Debug)]\npub struct CodegenOptions {\n    pub visibility: CodegenVisibility,\n}\n\nimpl Default for CodegenOptions {\n    fn default() -> Self {\n        Self {\n            visibility: CodegenVisibility::OnlyPublic,\n        }\n    }\n}\n\npub fn generate(module: &ModuleDef, lang: &dyn Lang, options: &CodegenOptions) -> Vec<OutputFile> {\n    itertools::chain!(\n        util::iter_tables(module, options.visibility).map(|tbl| lang.generate_table_file(module, tbl)),\n        module.views().map(|view| lang.generate_view_file(module, view)),\n        module.types().flat_map(|typ| lang.generate_type_files(module, typ)),\n        util::iter_reducers(module, options.visibility).map(|reducer| lang.generate_reducer_file(module, reducer)),\n        util::iter_procedures(module, options.visibility)\n            .map(|procedure| lang.generate_procedure_file(module, procedure)),\n        lang.generate_global_files(module, options),\n    )\n    .collect()\n}\n\npub struct OutputFile {\n    pub filename: String,\n    pub code: String,\n}\n\npub trait Lang {\n    fn generate_table_file_from_schema(&self, module: &ModuleDef, tbl: &TableDef, schema: TableSchema) -> OutputFile;\n    fn generate_type_files(&self, module: &ModuleDef, typ: &TypeDef) -> Vec<OutputFile>;\n    fn generate_reducer_file(&self, module: &ModuleDef, reducer: &ReducerDef) -> OutputFile;\n    fn generate_procedure_file(&self, module: &ModuleDef, procedure: &ProcedureDef) -> OutputFile;\n    fn generate_global_files(&self, module: &ModuleDef, options: &CodegenOptions) -> Vec<OutputFile>;\n\n    fn generate_table_file(&self, module: &ModuleDef, tbl: &TableDef) -> OutputFile {\n        let schema = TableSchema::from_module_def(module, tbl, (), 0.into())\n            .validated()\n            .expect(\"Failed to generate table due to validation errors\");\n        self.generate_table_file_from_schema(module, tbl, schema)\n    }\n\n    fn generate_view_file(&self, module: &ModuleDef, view: &ViewDef) -> OutputFile {\n        let tbl = TableDef::from(view.clone());\n        let schema = TableSchema::from_view_def_for_codegen(module, view)\n            .validated()\n            .expect(\"Failed to generate table due to validation errors\");\n        self.generate_table_file_from_schema(module, &tbl, schema)\n    }\n}\n"
  },
  {
    "path": "crates/codegen/src/rust.rs",
    "content": "use super::code_indenter::{CodeIndenter, Indenter};\nuse super::util::{collect_case, iter_reducers, print_lines, type_ref_name};\nuse super::Lang;\nuse crate::util::{\n    iter_indexes, iter_procedures, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, iter_views,\n    print_auto_generated_file_comment, print_auto_generated_version_comment, CodegenVisibility,\n};\nuse crate::CodegenOptions;\nuse crate::OutputFile;\nuse convert_case::{Case, Casing};\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_lib::sats::AlgebraicTypeRef;\nuse spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, ScopedTypeName, TableDef, TypeDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse};\nuse std::collections::BTreeSet;\nuse std::fmt::{self, Write};\nuse std::ops::Deref;\n\n/// Pairs of (module_name, TypeName).\ntype Imports = BTreeSet<AlgebraicTypeRef>;\n\nconst INDENT: &str = \"    \";\n\npub struct Rust;\n\nimpl Lang for Rust {\n    fn generate_type_files(&self, module: &ModuleDef, typ: &TypeDef) -> Vec<OutputFile> {\n        let type_name = collect_case(Case::Pascal, typ.accessor_name.name_segments());\n\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false);\n        out.newline();\n\n        match &module.typespace_for_generate()[typ.ty] {\n            AlgebraicTypeDef::Product(product) => {\n                gen_and_print_imports(module, out, &product.elements, &[typ.ty]);\n                out.newline();\n                define_struct_for_product(module, out, &type_name, &product.elements, \"pub\");\n            }\n            AlgebraicTypeDef::Sum(sum) => {\n                gen_and_print_imports(module, out, &sum.variants, &[typ.ty]);\n                out.newline();\n                define_enum_for_sum(module, out, &type_name, &sum.variants, false);\n            }\n            AlgebraicTypeDef::PlainEnum(plain_enum) => {\n                let variants = plain_enum\n                    .variants\n                    .iter()\n                    .cloned()\n                    .map(|var| (var, AlgebraicTypeUse::Unit))\n                    .collect::<Vec<_>>();\n                define_enum_for_sum(module, out, &type_name, &variants, true);\n            }\n        }\n        out.newline();\n\n        writeln!(\n            out,\n            \"\nimpl __sdk::InModule for {type_name} {{\n    type Module = super::RemoteModule;\n}}\n\",\n        );\n\n        // Do not implement query col types for nested types.\n        // as querying is only supported on top-level table row types.\n        let name = type_ref_name(module, typ.ty);\n        let implemented = match module\n            .tables()\n            .find(|t| type_ref_name(module, t.product_type_ref) == name)\n        {\n            Some(table) => {\n                implement_query_col_types_for_table_struct(module, out, table)\n                    .expect(\"failed to implement query col types\");\n                out.newline();\n                true\n            }\n            _ => false,\n        };\n\n        if !implemented\n            && let Some(type_ref) = module\n                .views()\n                .map(|v| v.product_type_ref)\n                .find(|type_ref| type_ref_name(module, *type_ref) == name)\n        {\n            implement_query_col_types_for_struct(module, out, type_ref).expect(\"failed to implement query col types\");\n            out.newline();\n        }\n\n        vec![OutputFile {\n            filename: type_module_name(&typ.accessor_name) + \".rs\",\n            code: output.into_inner(),\n        }]\n    }\n    fn generate_table_file_from_schema(&self, module: &ModuleDef, table: &TableDef, schema: TableSchema) -> OutputFile {\n        let type_ref = table.product_type_ref;\n\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false);\n\n        let row_type = type_ref_name(module, type_ref);\n        let row_type_module = type_ref_module_name(module, type_ref);\n\n        writeln!(out, \"use super::{row_type_module}::{row_type};\");\n\n        let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap();\n\n        // Import the types of all fields.\n        // We only need to import fields which have indices or unique constraints,\n        // but it's easier to just import all of 'em, since we have `#![allow(unused)]` anyway.\n        gen_and_print_imports(\n            module,\n            out,\n            &product_def.elements,\n            &[], // No need to skip any imports; we're not defining a type, so there's no chance of circular imports.\n        );\n\n        let table_name = table.name.deref();\n        let table_name_pascalcase = table.accessor_name.deref().to_case(Case::Pascal);\n        let table_handle = table_name_pascalcase.clone() + \"TableHandle\";\n        let insert_callback_id = table_name_pascalcase.clone() + \"InsertCallbackId\";\n        let delete_callback_id = table_name_pascalcase.clone() + \"DeleteCallbackId\";\n        let accessor_trait = table_access_trait_name(&table.accessor_name);\n        let accessor_method = table_method_name(&table.accessor_name);\n\n        write!(\n            out,\n            \"\n/// Table handle for the table `{table_name}`.\n///\n/// Obtain a handle from the [`{accessor_trait}::{accessor_method}`] method on [`super::RemoteTables`],\n/// like `ctx.db.{accessor_method}()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.{accessor_method}().on_insert(...)`.\npub struct {table_handle}<'ctx> {{\n    imp: __sdk::TableHandle<{row_type}>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `{table_name}`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait {accessor_trait} {{\n    #[allow(non_snake_case)]\n    /// Obtain a [`{table_handle}`], which mediates access to the table `{table_name}`.\n    fn {accessor_method}(&self) -> {table_handle}<'_>;\n}}\n\nimpl {accessor_trait} for super::RemoteTables {{\n    fn {accessor_method}(&self) -> {table_handle}<'_> {{\n        {table_handle} {{\n            imp: self.imp.get_table::<{row_type}>({table_name:?}),\n            ctx: std::marker::PhantomData,\n        }}\n    }}\n}}\n\npub struct {insert_callback_id}(__sdk::CallbackId);\n\"\n        );\n\n        if table.is_event {\n            // Event tables: implement the `EventTable` trait, which exposes only on-insert callbacks,\n            // not on-delete or on-update.\n            // on-update callbacks aren't meaningful for event tables,\n            // as they never have resident rows, so they can never update an existing row.\n            // on-delete callbacks are meaningful, but exactly equivalent to the on-insert callbacks,\n            // so not particularly useful.\n            // Also, don't emit unique index accessors: no resident rows means these would always be empty,\n            // so no reason to have them.\n            write!(\n                out,\n                \"\nimpl<'ctx> __sdk::EventTable for {table_handle}<'ctx> {{\n    type Row = {row_type};\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 {{ self.imp.count() }}\n    fn iter(&self) -> impl Iterator<Item = {row_type}> + '_ {{ self.imp.iter() }}\n\n    type InsertCallbackId = {insert_callback_id};\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> {insert_callback_id} {{\n        {insert_callback_id}(self.imp.on_insert(Box::new(callback)))\n    }}\n\n    fn remove_on_insert(&self, callback: {insert_callback_id}) {{\n        self.imp.remove_on_insert(callback.0)\n    }}\n}}\n\"\n            );\n        } else {\n            // Non-event tables: implement the `Table` trait, which exposes on-insert and on-delete callbacks.\n            // Also possibly implement `TableWithPrimrayKey`, which exposes on-update callbacks,\n            // and emit accessors for unique columns.\n            write!(\n                out,\n                \"pub struct {delete_callback_id}(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for {table_handle}<'ctx> {{\n    type Row = {row_type};\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 {{ self.imp.count() }}\n    fn iter(&self) -> impl Iterator<Item = {row_type}> + '_ {{ self.imp.iter() }}\n\n    type InsertCallbackId = {insert_callback_id};\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> {insert_callback_id} {{\n        {insert_callback_id}(self.imp.on_insert(Box::new(callback)))\n    }}\n\n    fn remove_on_insert(&self, callback: {insert_callback_id}) {{\n        self.imp.remove_on_insert(callback.0)\n    }}\n\n    type DeleteCallbackId = {delete_callback_id};\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> {delete_callback_id} {{\n        {delete_callback_id}(self.imp.on_delete(Box::new(callback)))\n    }}\n\n    fn remove_on_delete(&self, callback: {delete_callback_id}) {{\n        self.imp.remove_on_delete(callback.0)\n    }}\n}}\n\"\n            );\n\n            if table.primary_key.is_some() {\n                // If the table has a primary key, implement the `TableWithPrimaryKey` trait\n                // to expose on-update callbacks.\n                let update_callback_id = table_name_pascalcase.clone() + \"UpdateCallbackId\";\n                write!(\n                    out,\n                    \"\npub struct {update_callback_id}(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::TableWithPrimaryKey for {table_handle}<'ctx> {{\n    type UpdateCallbackId = {update_callback_id};\n\n    fn on_update(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,\n    ) -> {update_callback_id} {{\n        {update_callback_id}(self.imp.on_update(Box::new(callback)))\n    }}\n\n    fn remove_on_update(&self, callback: {update_callback_id}) {{\n        self.imp.remove_on_update(callback.0)\n    }}\n}}\n\"\n                );\n            }\n\n            // Emit unique index accessors for all of the table's unique fields.\n            for (unique_field_ident, unique_field_type_use) in\n                iter_unique_cols(module.typespace_for_generate(), &schema, product_def)\n            {\n                let unique_field_name = unique_field_ident.deref().to_case(Case::Snake);\n                let unique_field_name_pascalcase = unique_field_name.to_case(Case::Pascal);\n\n                let unique_constraint = table_name_pascalcase.clone() + &unique_field_name_pascalcase + \"Unique\";\n                let unique_field_type = type_name(module, unique_field_type_use);\n\n                write!(\n                    out,\n                    \"\n        /// Access to the `{unique_field_name}` unique index on the table `{table_name}`,\n        /// which allows point queries on the field of the same name\n        /// via the [`{unique_constraint}::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.{accessor_method}().{unique_field_name}().find(...)`.\n        pub struct {unique_constraint}<'ctx> {{\n            imp: __sdk::UniqueConstraintHandle<{row_type}, {unique_field_type}>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }}\n\n        impl<'ctx> {table_handle}<'ctx> {{\n            /// Get a handle on the `{unique_field_name}` unique index on the table `{table_name}`.\n            pub fn {unique_field_name}(&self) -> {unique_constraint}<'ctx> {{\n                {unique_constraint} {{\n                    imp: self.imp.get_unique_constraint::<{unique_field_type}>({unique_field_name:?}),\n                    phantom: std::marker::PhantomData,\n                }}\n            }}\n        }}\n\n        impl<'ctx> {unique_constraint}<'ctx> {{\n            /// Find the subscribed row whose `{unique_field_name}` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &{unique_field_type}) -> Option<{row_type}> {{\n                self.imp.find(col_val)\n            }}\n        }}\n        \"\n                );\n            }\n\n            // TODO: expose non-unique indices.\n        }\n\n        // Regardless of event-ness, emit `register_table` and `parse_table_update`.\n        out.delimited_block(\n            \"\n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\",\n            |out| {\n                writeln!(out, \"let _table = client_cache.get_or_make_table::<{row_type}>({table_name:?});\");\n                for (unique_field_ident, unique_field_type_use) in iter_unique_cols(module.typespace_for_generate(), &schema, product_def) {\n                    let unique_field_name = unique_field_ident.deref().to_case(Case::Snake);\n                    let unique_field_type = type_name(module, unique_field_type_use);\n                    writeln!(\n                        out,\n                        \"_table.add_unique_constraint::<{unique_field_type}>({unique_field_name:?}, |row| &row.{unique_field_name});\",\n                    );\n                }\n            },\n            \"}\",\n        );\n\n        out.newline();\n\n        write!(\n            out,\n            \"\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<{row_type}>> {{\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {{\n        __sdk::InternalError::failed_parse(\n            \\\"TableUpdate<{row_type}>\\\",\n            \\\"TableUpdate\\\",\n        ).with_cause(e).into()\n    }})\n}}\n\"\n        );\n\n        implement_query_table_accessor(table, out, &row_type).expect(\"failed to implement query table accessor\");\n\n        OutputFile {\n            filename: table_module_name(&table.accessor_name) + \".rs\",\n            code: output.into_inner(),\n        }\n    }\n    fn generate_reducer_file(&self, module: &ModuleDef, reducer: &ReducerDef) -> OutputFile {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false);\n\n        out.newline();\n\n        gen_and_print_imports(\n            module,\n            out,\n            &reducer.params_for_generate.elements,\n            // No need to skip any imports; we're not emitting a type that other modules can import.\n            &[],\n        );\n\n        out.newline();\n\n        let reducer_name = reducer.name.deref();\n        let func_name = reducer_function_name(reducer);\n        let args_type = function_args_type_name(&reducer.accessor_name);\n        let enum_variant_name = reducer_variant_name(&reducer.accessor_name);\n\n        // Define an \"args struct\" for the reducer.\n        // This is not user-facing (note the `pub(super)` visibility);\n        // it is an internal helper for serialization and deserialization.\n        // We actually want to ser/de instances of `enum Reducer`, but:\n        // - `Reducer` will have struct-like variants, which SATS ser/de does not support.\n        // - The WS format does not contain a BSATN-serialized `Reducer` instance;\n        //   it holds the reducer name or ID separately from the argument bytes.\n        //   We could work up some magic with `DeserializeSeed`\n        //   and/or custom `Serializer` and `Deserializer` types\n        //   to account for this, but it's much easier to just use an intermediate struct per reducer.\n        define_struct_for_product(\n            module,\n            out,\n            &args_type,\n            &reducer.params_for_generate.elements,\n            \"pub(super)\",\n        );\n\n        out.newline();\n\n        let FormattedArglist {\n            arglist_no_delimiters,\n            arg_names,\n        } = FormattedArglist::for_arguments(module, &reducer.params_for_generate.elements);\n\n        write!(out, \"impl From<{args_type}> for super::Reducer \");\n        out.delimited_block(\n            \"{\",\n            |out| {\n                write!(out, \"fn from(args: {args_type}) -> Self \");\n                out.delimited_block(\n                    \"{\",\n                    |out| {\n                        write!(out, \"Self::{enum_variant_name}\");\n                        if !reducer.params_for_generate.elements.is_empty() {\n                            // We generate \"struct variants\" for reducers with arguments,\n                            // but \"unit variants\" for reducers of no arguments.\n                            // These use different constructor syntax.\n                            out.delimited_block(\n                                \" {\",\n                                |out| {\n                                    for (arg_ident, _ty) in &reducer.params_for_generate.elements[..] {\n                                        let arg_name = arg_ident.deref().to_case(Case::Snake);\n                                        writeln!(out, \"{arg_name}: args.{arg_name},\");\n                                    }\n                                },\n                                \"}\",\n                            );\n                        }\n                        out.newline();\n                    },\n                    \"}\\n\",\n                );\n            },\n            \"}\\n\",\n        );\n\n        // TODO: check for lifecycle reducers and do not generate the invoke method.\n\n        writeln!(\n            out,\n            \"\nimpl __sdk::InModule for {args_type} {{\n    type Module = super::RemoteModule;\n}}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `{reducer_name}`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait {func_name} {{\n    /// Request that the remote module invoke the reducer `{reducer_name}` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`{func_name}:{func_name}_then`] to run a callback after the reducer completes.\n    fn {func_name}(&self, {arglist_no_delimiters}) -> __sdk::Result<()> {{\n        self.{func_name}_then({arg_names} |_, _| {{}})\n    }}\n\n    /// Request that the remote module invoke the reducer `{reducer_name}` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn {func_name}_then(\n        &self,\n        {arglist_no_delimiters}\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}}\n\nimpl {func_name} for super::RemoteReducers {{\n    fn {func_name}_then(\n        &self,\n        {arglist_no_delimiters}\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {{\n        self.imp.invoke_reducer_with_callback({args_type} {{ {arg_names} }}, callback)\n    }}\n}}\n\"\n        );\n\n        OutputFile {\n            filename: reducer_module_name(&reducer.accessor_name) + \".rs\",\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_procedure_file(&self, module: &ModuleDef, procedure: &ProcedureDef) -> OutputFile {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false);\n\n        out.newline();\n\n        let mut imports = Imports::new();\n        gen_imports(&mut imports, &procedure.params_for_generate.elements);\n        add_one_import(&mut imports, &procedure.return_type_for_generate);\n        print_imports(module, out, imports);\n\n        out.newline();\n\n        let procedure_name = procedure.name.deref();\n        let func_name = procedure_function_name(procedure);\n        let func_name_with_callback = procedure_function_with_callback_name(procedure);\n        let args_type = function_args_type_name(&procedure.accessor_name);\n        let res_ty_name = type_name(module, &procedure.return_type_for_generate);\n\n        // Define an \"args struct\" as a serialization helper.\n        // This is not user-facing, it's not used outside this file.\n        // Unlike with reducers, we don't have to deserialize procedure args to build events,\n        // as we don't broadcast procedure args.\n        define_struct_for_product(\n            module,\n            out,\n            &args_type,\n            &procedure.params_for_generate.elements,\n            // non-pub visibility.\n            \"\",\n        );\n\n        out.newline();\n\n        let FormattedArglist {\n            arglist_no_delimiters,\n            arg_names,\n            ..\n        } = FormattedArglist::for_arguments(module, &procedure.params_for_generate.elements);\n\n        writeln!(\n            out,\n            \"\nimpl __sdk::InModule for {args_type} {{\n    type Module = super::RemoteModule;\n}}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the procedure `{procedure_name}`.\n///\n/// Implemented for [`super::RemoteProcedures`].\npub trait {func_name} {{\n    fn {func_name}(&self, {arglist_no_delimiters}) {{\n        self.{func_name_with_callback}({arg_names} |_, _| {{}});\n    }}\n\n    fn {func_name_with_callback}(\n        &self,\n        {arglist_no_delimiters}\n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<{res_ty_name}, __sdk::InternalError>) + Send + 'static,\n    );\n}}\n\nimpl {func_name} for super::RemoteProcedures {{\n    fn {func_name_with_callback}(\n        &self,\n        {arglist_no_delimiters}\n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<{res_ty_name}, __sdk::InternalError>) + Send + 'static,\n    ) {{\n        self.imp.invoke_procedure_with_callback::<_, {res_ty_name}>(\n            {procedure_name:?},\n            {args_type} {{ {arg_names} }},\n            __callback,\n        );\n    }}\n}}\n\"\n        );\n\n        OutputFile {\n            filename: procedure_module_name(&procedure.accessor_name) + \".rs\",\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_global_files(&self, module: &ModuleDef, options: &CodegenOptions) -> Vec<OutputFile> {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, true);\n\n        out.newline();\n\n        // Declare `pub mod` for each of the files generated.\n        print_module_decls(module, options.visibility, out);\n\n        out.newline();\n\n        // Re-export all the modules for the generated files.\n        print_module_reexports(module, options.visibility, out);\n\n        out.newline();\n\n        // Define `enum Reducer`.\n        print_reducer_enum_defn(module, options.visibility, out);\n\n        out.newline();\n\n        // Define `DbUpdate`.\n        print_db_update_defn(module, options.visibility, out);\n\n        out.newline();\n\n        // Define `AppliedDiff`.\n        print_applied_diff_defn(module, options.visibility, out);\n\n        out.newline();\n\n        // Define `RemoteModule`, `DbConnection`, `EventContext`, `RemoteTables`,\n        // `RemoteReducers`, `RemoteProcedures` and `SubscriptionHandle`.\n        // Note that these do not change based on the module.\n        print_const_db_context_types(out);\n\n        out.newline();\n\n        // Implement `SpacetimeModule` for `RemoteModule`.\n        // This includes a method for initializing the tables in the client cache.\n        print_impl_spacetime_module(module, options.visibility, out);\n\n        vec![OutputFile {\n            filename: \"mod.rs\".to_string(),\n            code: output.into_inner(),\n        }]\n    }\n}\n\n/// Implements `HasCols` for the given `AlgebraicTypeRef` struct type.\nfn implement_query_col_types_for_struct(\n    module: &ModuleDef,\n    out: &mut impl Write,\n    type_ref: AlgebraicTypeRef,\n) -> fmt::Result {\n    let struct_name = type_ref_name(module, type_ref);\n    let cols_struct = struct_name.clone() + \"Cols\";\n    let product_def = module.typespace_for_generate()[type_ref]\n        .as_product()\n        .expect(\"expected product type\");\n\n    writeln!(\n        out,\n        \"\n/// Column accessor struct for the table `{struct_name}`.\n///\n/// Provides typed access to columns for query building.\npub struct {cols_struct} {{\"\n    )?;\n\n    for element in &product_def.elements {\n        let field_name = &element.0.deref().to_case(Case::Snake);\n        let field_type = type_name(module, &element.1);\n        writeln!(\n            out,\n            \"    pub {field_name}: __sdk::__query_builder::Col<{struct_name}, {field_type}>,\"\n        )?;\n    }\n\n    writeln!(out, \"}}\")?;\n\n    writeln!(\n        out,\n        \"\nimpl __sdk::__query_builder::HasCols for {struct_name} {{\n    type Cols = {cols_struct};\n    fn cols(table_name: &'static str) -> Self::Cols {{\n        {cols_struct} {{\"\n    )?;\n    for element in &product_def.elements {\n        let field_name = &element.0.deref().to_case(Case::Snake);\n        writeln!(\n            out,\n            \"            {field_name}: __sdk::__query_builder::Col::new(table_name, {field_name:?}),\"\n        )?;\n    }\n\n    writeln!(\n        out,\n        r#\"\n        }}\n    }}\n}}\"#\n    )\n}\n\n/// Implements `HasCols` and `HasIxCols` for the given table's row struct type.\nfn implement_query_col_types_for_table_struct(\n    module: &ModuleDef,\n    out: &mut impl Write,\n    table: &TableDef,\n) -> fmt::Result {\n    let type_ref = table.product_type_ref;\n    let struct_name = type_ref_name(module, type_ref);\n\n    implement_query_col_types_for_struct(module, out, type_ref)?;\n    let cols_ix = struct_name.clone() + \"IxCols\";\n    writeln!(\n        out,\n        \"\n/// Indexed column accessor struct for the table `{struct_name}`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct {cols_ix} {{\"\n    )?;\n    for index in iter_indexes(table) {\n        let cols = index.algorithm.columns();\n        if cols.len() != 1 {\n            continue;\n        }\n        let column = table\n            .columns\n            .iter()\n            .find(|col| col.col_id == cols.as_singleton().expect(\"singleton column\"))\n            .unwrap();\n        let field_name = column.accessor_name.deref().to_case(Case::Snake);\n        let field_type = type_name(module, &column.ty_for_generate);\n\n        writeln!(\n            out,\n            \"    pub {field_name}: __sdk::__query_builder::IxCol<{struct_name}, {field_type}>,\",\n        )?;\n    }\n    writeln!(out, \"}}\")?;\n\n    writeln!(\n        out,\n        \"\nimpl __sdk::__query_builder::HasIxCols for {struct_name} {{\n    type IxCols = {cols_ix};\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {{\n        {cols_ix} {{\"\n    )?;\n    for index in iter_indexes(table) {\n        let cols = index.algorithm.columns();\n        if cols.len() != 1 {\n            continue;\n        }\n        let column = table\n            .columns\n            .iter()\n            .find(|col| col.col_id == cols.as_singleton().expect(\"singleton column\"))\n            .expect(\"singleton column\");\n        let field_name = column.accessor_name.deref().to_case(Case::Snake);\n        let col_name = column.name.deref();\n\n        writeln!(\n            out,\n            \"            {field_name}: __sdk::__query_builder::IxCol::new(table_name, {col_name:?}),\",\n        )?;\n    }\n    writeln!(\n        out,\n        r#\"\n        }}\n    }}\n}}\"#\n    )?;\n\n    // Event tables cannot be used as lookup tables in semijoins.\n    if !table.is_event {\n        writeln!(\n            out,\n            \"\\nimpl __sdk::__query_builder::CanBeLookupTable for {struct_name} {{}}\"\n        )?;\n    }\n\n    Ok(())\n}\n\npub fn implement_query_table_accessor(table: &TableDef, out: &mut impl Write, struct_name: &String) -> fmt::Result {\n    // NEW: Generate query table accessor trait and implementation\n    let accessor_method = table_method_name(&table.accessor_name);\n    let table_name = table.name.deref();\n    let query_accessor_trait = accessor_method.to_string() + \"QueryTableAccess\";\n\n    writeln!(\n        out,\n        \"\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `{struct_name}`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait {query_accessor_trait} {{\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `{struct_name}`.\n            fn {accessor_method}(&self) -> __sdk::__query_builder::Table<{struct_name}>;\n        }}\n\n        impl {query_accessor_trait} for __sdk::QueryTableAccessor {{\n            fn {accessor_method}(&self) -> __sdk::__query_builder::Table<{struct_name}> {{\n                __sdk::__query_builder::Table::new({table_name:?})\n            }}\n        }}\n\"\n    )\n}\n\npub fn write_type<W: Write>(module: &ModuleDef, out: &mut W, ty: &AlgebraicTypeUse) -> fmt::Result {\n    match ty {\n        AlgebraicTypeUse::Unit => write!(out, \"()\")?,\n        AlgebraicTypeUse::Never => write!(out, \"std::convert::Infallible\")?,\n        AlgebraicTypeUse::Identity => write!(out, \"__sdk::Identity\")?,\n        AlgebraicTypeUse::ConnectionId => write!(out, \"__sdk::ConnectionId\")?,\n        AlgebraicTypeUse::Timestamp => write!(out, \"__sdk::Timestamp\")?,\n        AlgebraicTypeUse::TimeDuration => write!(out, \"__sdk::TimeDuration\")?,\n        AlgebraicTypeUse::Uuid => write!(out, \"__sdk::Uuid\")?,\n        AlgebraicTypeUse::ScheduleAt => write!(out, \"__sdk::ScheduleAt\")?,\n        AlgebraicTypeUse::Option(inner_ty) => {\n            write!(out, \"Option::<\")?;\n            write_type(module, out, inner_ty)?;\n            write!(out, \">\")?;\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            write!(out, \"Result::<\")?;\n            write_type(module, out, ok_ty)?;\n            write!(out, \", \")?;\n            write_type(module, out, err_ty)?;\n            write!(out, \">\")?;\n        }\n        AlgebraicTypeUse::Primitive(prim) => match prim {\n            PrimitiveType::Bool => write!(out, \"bool\")?,\n            PrimitiveType::I8 => write!(out, \"i8\")?,\n            PrimitiveType::U8 => write!(out, \"u8\")?,\n            PrimitiveType::I16 => write!(out, \"i16\")?,\n            PrimitiveType::U16 => write!(out, \"u16\")?,\n            PrimitiveType::I32 => write!(out, \"i32\")?,\n            PrimitiveType::U32 => write!(out, \"u32\")?,\n            PrimitiveType::I64 => write!(out, \"i64\")?,\n            PrimitiveType::U64 => write!(out, \"u64\")?,\n            PrimitiveType::I128 => write!(out, \"i128\")?,\n            PrimitiveType::U128 => write!(out, \"u128\")?,\n            PrimitiveType::I256 => write!(out, \"__sats::i256\")?,\n            PrimitiveType::U256 => write!(out, \"__sats::u256\")?,\n            PrimitiveType::F32 => write!(out, \"f32\")?,\n            PrimitiveType::F64 => write!(out, \"f64\")?,\n        },\n        AlgebraicTypeUse::String => write!(out, \"String\")?,\n        AlgebraicTypeUse::Array(elem_ty) => {\n            write!(out, \"Vec::<\")?;\n            write_type(module, out, elem_ty)?;\n            write!(out, \">\")?;\n        }\n        AlgebraicTypeUse::Ref(r) => {\n            write!(out, \"{}\", type_ref_name(module, *r))?;\n        }\n    }\n    Ok(())\n}\n\npub fn type_name(module: &ModuleDef, ty: &AlgebraicTypeUse) -> String {\n    let mut s = String::new();\n    write_type(module, &mut s, ty).unwrap();\n    s\n}\n\n/// Arguments to a reducer or procedure pretty-printed in various ways that are convenient to compute together.\nstruct FormattedArglist {\n    /// The arguments as `ident: ty, ident: ty, ident: ty,`,\n    /// like an argument list.\n    ///\n    /// Always carries a trailing comma, unless it's zero elements.\n    arglist_no_delimiters: String,\n    /// The argument names as `ident, ident, ident,`,\n    /// for passing to function call and struct literal expressions.\n    ///\n    /// Always carries a trailing comma, unless it's zero elements.\n    arg_names: String,\n}\n\nimpl FormattedArglist {\n    fn for_arguments(module: &ModuleDef, params: &[(Identifier, AlgebraicTypeUse)]) -> Self {\n        let mut arglist_no_delimiters = String::new();\n        write_arglist_no_delimiters(module, &mut arglist_no_delimiters, params, None)\n            .expect(\"Writing to a String failed... huh?\");\n\n        let mut arg_names = String::new();\n        for (arg_ident, _) in params {\n            let arg_name = arg_ident.deref().to_case(Case::Snake);\n            arg_names += &arg_name;\n            arg_names += \", \";\n        }\n\n        Self {\n            arglist_no_delimiters,\n            arg_names,\n        }\n    }\n}\n\nconst ALLOW_LINTS: &str = \"#![allow(unused, clippy::all)]\";\n\nconst SPACETIMEDB_IMPORTS: &[&str] = &[\n    \"use spacetimedb_sdk::__codegen::{\",\n    \"\\tself as __sdk,\",\n    \"\\t__lib,\",\n    \"\\t__sats,\",\n    \"\\t__ws,\",\n    \"};\",\n];\n\nfn print_spacetimedb_imports(output: &mut Indenter) {\n    print_lines(output, SPACETIMEDB_IMPORTS);\n}\n\nfn print_file_header(output: &mut Indenter, include_version: bool) {\n    print_auto_generated_file_comment(output);\n    if include_version {\n        print_auto_generated_version_comment(output);\n    }\n    writeln!(output, \"{ALLOW_LINTS}\");\n    print_spacetimedb_imports(output);\n}\n\n// TODO: figure out if/when sum types should derive:\n// - Clone\n// - Debug\n// - Copy\n// - PartialEq, Eq\n// - Hash\n//    - Complicated because `HashMap` is not `Hash`.\n// - others?\n\nconst ENUM_DERIVES: &[&str] = &[\n    \"#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\",\n    \"#[sats(crate = __lib)]\",\n];\n\nfn print_enum_derives(output: &mut Indenter) {\n    print_lines(output, ENUM_DERIVES);\n}\n\nconst PLAIN_ENUM_EXTRA_DERIVES: &[&str] = &[\"#[derive(Copy, Eq, Hash)]\"];\n\nfn print_plain_enum_extra_derives(output: &mut Indenter) {\n    print_lines(output, PLAIN_ENUM_EXTRA_DERIVES);\n}\n\n/// Generate a file which defines an `enum` corresponding to the `sum_type`.\npub fn define_enum_for_sum(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    name: &str,\n    variants: &[(Identifier, AlgebraicTypeUse)],\n    is_plain: bool,\n) {\n    print_enum_derives(out);\n    if is_plain {\n        print_plain_enum_extra_derives(out);\n    }\n    write!(out, \"pub enum {name} \");\n\n    out.delimited_block(\n        \"{\",\n        |out| {\n            for (ident, ty) in variants {\n                write_enum_variant(module, out, ident, ty);\n                out.newline();\n            }\n        },\n        \"}\\n\",\n    );\n\n    out.newline()\n}\n\nfn write_enum_variant(module: &ModuleDef, out: &mut Indenter, ident: &Identifier, ty: &AlgebraicTypeUse) {\n    let name = ident.deref().to_case(Case::Pascal);\n    write!(out, \"{name}\");\n\n    // If the contained type is the unit type, i.e. this variant has no members,\n    // write it without parens or braces, like\n    // ```\n    // Foo,\n    // ```\n    if !matches!(ty, AlgebraicTypeUse::Unit) {\n        // If the contained type is not a product, i.e. this variant has a single\n        // member, write it tuple-style, with parens.\n        write!(out, \"(\");\n        write_type(module, out, ty).unwrap();\n        write!(out, \")\");\n    }\n    writeln!(out, \",\");\n}\n\nfn write_struct_type_fields_in_braces(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    elements: &[(Identifier, AlgebraicTypeUse)],\n\n    // Whether to print a `pub` qualifier on the fields. Necessary for `struct` defns,\n    // disallowed for `enum` defns.\n    pub_qualifier: bool,\n) {\n    out.delimited_block(\n        \"{\",\n        |out| write_arglist_no_delimiters(module, out, elements, pub_qualifier.then_some(\"pub\")).unwrap(),\n        \"}\",\n    );\n}\n\nfn write_arglist_no_delimiters(\n    module: &ModuleDef,\n    out: &mut impl Write,\n    elements: &[(Identifier, AlgebraicTypeUse)],\n\n    // Written before each line. Useful for `pub`.\n    prefix: Option<&str>,\n) -> anyhow::Result<()> {\n    for (ident, ty) in elements {\n        if let Some(prefix) = prefix {\n            write!(out, \"{prefix} \")?;\n        }\n\n        let name = ident.deref().to_case(Case::Snake);\n\n        write!(out, \"{name}: \")?;\n        write_type(module, out, ty)?;\n        writeln!(out, \",\")?;\n    }\n\n    Ok(())\n}\n\n// TODO: figure out if/when product types should derive:\n// - Clone\n// - Debug\n// - Copy\n// - PartialEq, Eq\n// - Hash\n//    - Complicated because `HashMap` is not `Hash`.\n// - others?\n\nconst STRUCT_DERIVES: &[&str] = &[\n    \"#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\",\n    \"#[sats(crate = __lib)]\",\n];\n\nfn print_struct_derives(output: &mut Indenter) {\n    print_lines(output, STRUCT_DERIVES);\n}\n\nfn define_struct_for_product(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    name: &str,\n    elements: &[(Identifier, AlgebraicTypeUse)],\n    vis: &str,\n) {\n    print_struct_derives(out);\n\n    write!(out, \"{vis} struct {name} \");\n\n    // TODO: if elements is empty, define a unit struct with no brace-delimited list of fields.\n    write_struct_type_fields_in_braces(\n        module, out, elements, true, // `pub`-qualify fields.\n    );\n\n    out.newline();\n}\n\nfn type_ref_module_name(module: &ModuleDef, type_ref: AlgebraicTypeRef) -> String {\n    let (name, _) = module.type_def_from_ref(type_ref).unwrap();\n    type_module_name(name)\n}\n\nfn type_module_name(type_name: &ScopedTypeName) -> String {\n    collect_case(Case::Snake, type_name.name_segments()) + \"_type\"\n}\n\nfn table_module_name(table_name: &Identifier) -> String {\n    table_name.deref().to_case(Case::Snake) + \"_table\"\n}\n\nfn table_method_name(table_name: &Identifier) -> String {\n    table_name.deref().to_case(Case::Snake)\n}\n\nfn table_access_trait_name(table_name: &Identifier) -> String {\n    table_name.deref().to_case(Case::Pascal) + \"TableAccess\"\n}\n\nfn function_args_type_name(function_name: &str) -> String {\n    function_name.to_case(Case::Pascal) + \"Args\"\n}\n\nfn reducer_variant_name(reducer_name: &ReducerName) -> String {\n    reducer_name.deref().to_case(Case::Pascal)\n}\n\nfn reducer_module_name(reducer_name: &ReducerName) -> String {\n    reducer_name.deref().to_case(Case::Snake) + \"_reducer\"\n}\n\nfn reducer_function_name(reducer: &ReducerDef) -> String {\n    reducer.accessor_name.deref().to_case(Case::Snake)\n}\n\nfn procedure_module_name(procedure_name: &Identifier) -> String {\n    procedure_name.deref().to_case(Case::Snake) + \"_procedure\"\n}\n\nfn procedure_function_name(procedure: &ProcedureDef) -> String {\n    procedure.accessor_name.deref().to_case(Case::Snake)\n}\n\nfn procedure_function_with_callback_name(procedure: &ProcedureDef) -> String {\n    procedure_function_name(procedure) + \"_then\"\n}\n\n/// Iterate over all of the Rust `mod`s for types, reducers, views, and tables in the `module`.\nfn iter_module_names(module: &ModuleDef, visibility: CodegenVisibility) -> impl Iterator<Item = String> + '_ {\n    itertools::chain!(\n        iter_types(module).map(|ty| type_module_name(&ty.accessor_name)),\n        iter_reducers(module, visibility).map(|r| reducer_module_name(&r.accessor_name)),\n        iter_tables(module, visibility).map(|tbl| table_module_name(&tbl.accessor_name)),\n        iter_views(module).map(|view| table_module_name(&view.accessor_name)),\n        iter_procedures(module, visibility).map(|proc| procedure_module_name(&proc.accessor_name)),\n    )\n}\n\n/// Print `pub mod` declarations for all the files that will be generated for `items`.\nfn print_module_decls(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    for module_name in iter_module_names(module, visibility) {\n        writeln!(out, \"pub mod {module_name};\");\n    }\n}\n\n/// Print appropriate reexports for all the files that will be generated for `items`.\nfn print_module_reexports(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    for ty in iter_types(module) {\n        let mod_name = type_module_name(&ty.accessor_name);\n        let type_name = collect_case(Case::Pascal, ty.accessor_name.name_segments());\n        writeln!(out, \"pub use {mod_name}::{type_name};\")\n    }\n    for (_, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n        let mod_name = table_module_name(accessor_name);\n        // TODO: More precise reexport: we want:\n        // - The trait name.\n        // - The insert, delete and possibly update callback ids.\n        // We do not want:\n        // - The table handle.\n        writeln!(out, \"pub use {mod_name}::*;\");\n    }\n    for reducer in iter_reducers(module, visibility) {\n        let mod_name = reducer_module_name(&reducer.accessor_name);\n        let reducer_trait_name = reducer_function_name(reducer);\n        writeln!(out, \"pub use {mod_name}::{reducer_trait_name};\");\n    }\n    for procedure in iter_procedures(module, visibility) {\n        let mod_name = procedure_module_name(&procedure.accessor_name);\n        let trait_name = procedure_function_name(procedure);\n        writeln!(out, \"pub use {mod_name}::{trait_name};\");\n    }\n}\n\nfn print_reducer_enum_defn(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    // Don't derive ser/de on this enum;\n    // it's not a proper SATS enum and the derive will fail.\n    writeln!(out, \"#[derive(Clone, PartialEq, Debug)]\");\n    writeln!(\n        out,\n        \"\n/// One of the reducers defined by this module.\n///\n/// Contained within a [`__sdk::ReducerEvent`] in [`EventContext`]s for reducer events\n/// to indicate which reducer caused the event.\n\",\n    );\n    out.delimited_block(\n        \"pub enum Reducer {\",\n        |out| {\n            for reducer in iter_reducers(module, visibility) {\n                write!(out, \"{} \", reducer_variant_name(&reducer.accessor_name));\n                if !reducer.params_for_generate.elements.is_empty() {\n                    // If the reducer has any arguments, generate a \"struct variant,\"\n                    // like `Foo { bar: Baz, }`.\n                    // If it doesn't, generate a \"unit variant\" instead,\n                    // like `Foo,`.\n                    write_struct_type_fields_in_braces(module, out, &reducer.params_for_generate.elements, false);\n                }\n                writeln!(out, \",\");\n            }\n        },\n        \"}\\n\",\n    );\n    out.newline();\n    writeln!(\n        out,\n        \"\nimpl __sdk::InModule for Reducer {{\n    type Module = RemoteModule;\n}}\n\",\n    );\n\n    out.delimited_block(\n        \"impl __sdk::Reducer for Reducer {\",\n        |out| {\n            out.delimited_block(\n                \"fn reducer_name(&self) -> &'static str {\",\n                |out| {\n                    out.delimited_block(\n                        \"match self {\",\n                        |out| {\n                            for reducer in iter_reducers(module, visibility) {\n                                write!(out, \"Reducer::{}\", reducer_variant_name(&reducer.accessor_name));\n                                if !reducer.params_for_generate.elements.is_empty() {\n                                    // Because we're emitting unit variants when the payload is empty,\n                                    // we will emit different patterns for empty vs non-empty variants.\n                                    // This is not strictly required;\n                                    // Rust allows matching a struct-like pattern\n                                    // against a unit-like enum variant,\n                                    // but we prefer the clarity of not including the braces for unit variants.\n                                    write!(out, \" {{ .. }}\");\n                                }\n                                writeln!(out, \" => {:?},\", reducer.name.deref());\n                            }\n                            // Write a catch-all pattern to handle the case where the module defines zero reducers,\n                            // 'cause references are always considered inhabited,\n                            // even references to uninhabited types.\n                            writeln!(out, \"_ => unreachable!(),\");\n                        },\n                        \"}\\n\",\n                    );\n                },\n                \"}\\n\",\n            );\n            writeln!(out, \"#[allow(clippy::clone_on_copy)]\");\n            out.delimited_block(\n                \"fn args_bsatn(&self) -> Result<Vec<u8>, __sats::bsatn::EncodeError> {\",\n                |out| {\n                    out.delimited_block(\n                        \"match self {\",\n                        |out| {\n                            for reducer in iter_reducers(module, visibility) {\n                                write!(out, \"Reducer::{}\", reducer_variant_name(&reducer.accessor_name));\n                                if !reducer.params_for_generate.elements.is_empty() {\n                                    // Because we're emitting unit variants when the payload is empty,\n                                    // we will emit different patterns for empty vs non-empty variants.\n                                    // This is not strictly required;\n                                    // Rust allows matching a struct-like pattern\n                                    // against a unit-like enum variant,\n                                    // but we prefer the clarity of not including the braces for unit variants.\n\n                                    out.delimited_block(\n                                        \"{\",\n                                        |out| {\n                                            for (ident, _) in &reducer.params_for_generate.elements {\n                                                writeln!(out, \"{},\", ident.deref().to_case(Case::Snake));\n                                            }\n                                        },\n                                        \"}\",\n                                    );\n                                }\n\n                                write!(\n                                    out,\n                                    \" => __sats::bsatn::to_vec(&{}::{}\",\n                                    reducer_module_name(&reducer.accessor_name),\n                                    function_args_type_name(&reducer.accessor_name)\n                                );\n                                out.delimited_block(\n                                    \" {\",\n                                    |out| {\n                                        for (ident, _) in &reducer.params_for_generate.elements {\n                                            let field = ident.deref().to_case(Case::Snake);\n                                            writeln!(out, \"{field}: {field}.clone(),\");\n                                        }\n                                    },\n                                    \"}),\\n\",\n                                );\n                            }\n                            // Write a catch-all pattern to handle the case where the module defines zero reducers,\n                            // 'cause references are always considered inhabited,\n                            // even references to uninhabited types.\n                            writeln!(out, \"_ => unreachable!(),\");\n                        },\n                        \"}\\n\",\n                    );\n                },\n                \"}\\n\",\n            );\n        },\n        \"}\\n\",\n    );\n}\n\nfn print_db_update_defn(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    writeln!(out, \"#[derive(Default, Debug)]\");\n    writeln!(out, \"#[allow(non_snake_case)]\");\n    writeln!(out, \"#[doc(hidden)]\");\n    out.delimited_block(\n        \"pub struct DbUpdate {\",\n        |out| {\n            for (_, accessor_name, product_type_ref) in iter_table_names_and_types(module, visibility) {\n                writeln!(\n                    out,\n                    \"{}: __sdk::TableUpdate<{}>,\",\n                    table_method_name(accessor_name),\n                    type_ref_name(module, product_type_ref),\n                );\n            }\n        },\n        \"}\\n\",\n    );\n\n    out.newline();\n\n    out.delimited_block(\n        \"\nimpl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {\n    type Error = __sdk::Error;\n    fn try_from(raw: __ws::v2::TransactionUpdate) -> Result<Self, Self::Error> {\n        let mut db_update = DbUpdate::default();\n        for table_update in __sdk::transaction_update_iter_table_updates(raw) {\n            match &table_update.table_name[..] {\n\",\n        |out| {\n            for (name, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n                writeln!(\n                    out,\n                    \"{:?} => db_update.{}.append({}::parse_table_update(table_update)?),\",\n                    name.deref(),\n                    table_method_name(accessor_name),\n                    table_module_name(accessor_name),\n                );\n            }\n        },\n        \"\n                unknown => {\n                    return Err(__sdk::InternalError::unknown_name(\n                        \\\"table\\\",\n                        unknown,\n                        \\\"DatabaseUpdate\\\",\n                    ).into());\n                }\n            }\n        }\n        Ok(db_update)\n    }\n}\",\n    );\n\n    out.newline();\n\n    writeln!(\n        out,\n        \"\nimpl __sdk::InModule for DbUpdate {{\n    type Module = RemoteModule;\n}}\n\",\n    );\n\n    out.delimited_block(\n        \"impl __sdk::DbUpdate for DbUpdate {\",\n        |out| {\n            out.delimited_block(\n                \"fn apply_to_client_cache(&self, cache: &mut __sdk::ClientCache<RemoteModule>) -> AppliedDiff<'_> {\n                    let mut diff = AppliedDiff::default();\n                \",\n                |out| {\n                    for table in iter_tables(module, visibility) {\n                        let field_name = table_method_name(&table.accessor_name);\n                        if table.is_event {\n                            // Event tables bypass the client cache entirely.\n                            // We construct an applied diff directly from the inserts,\n                            // which will fire on_insert callbacks without storing rows.\n                            writeln!(\n                                out,\n                                \"diff.{field_name} = self.{field_name}.into_event_diff();\",\n                            );\n                        } else {\n                            let with_updates = table\n                                .primary_key\n                                .map(|col| {\n                                    let pk_field = table.get_column(col).unwrap().accessor_name.deref().to_case(Case::Snake);\n                                    format!(\".with_updates_by_pk(|row| &row.{pk_field})\")\n                                })\n                                .unwrap_or_default();\n\n                            writeln!(\n                                out,\n                                \"diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};\",\n                                type_ref_name(module, table.product_type_ref),\n                                table.name.deref(),\n                            );\n                        }\n                    }\n                    for view in iter_views(module) {\n                        let field_name = table_method_name(&view.accessor_name);\n                        let with_updates = view\n                            .primary_key\n                            .map(|col| {\n                                let pk_field = view.return_columns[col.idx()]\n                                    .accessor_name\n                                    .deref()\n                                    .to_case(Case::Snake);\n                                format!(\".with_updates_by_pk(|row| &row.{pk_field})\")\n                            })\n                            .unwrap_or_default();\n                        writeln!(\n                            out,\n                            \"diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};\",\n                            type_ref_name(module, view.product_type_ref),\n                            view.name.deref(),\n                        );\n                    }\n                },\n                \"\n                    diff\n                }\\n\",\n            );\n\n            out.delimited_block(\n                \"fn parse_initial_rows(raw: __ws::v2::QueryRows) -> __sdk::Result<Self> {\",\n                |out| {\n                    writeln!(out, \"let mut db_update = DbUpdate::default();\");\n                    out.delimited_block(\n                        \"for table_rows in raw.tables {\",\n                        |out| {\n                            out.delimited_block(\n                                \"match &table_rows.table[..] {\",\n                                |out| {\n                                    for (name, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n                                        writeln!(\n                                            out,\n                                            \"{:?} => db_update.{}.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\",\n                                            name.deref(),\n                                            table_method_name(accessor_name),\n                                        );\n                                    }\n                                    writeln!(\n                                        out,\n                                        \"unknown => {{ return Err(__sdk::InternalError::unknown_name(\\\"table\\\", unknown, \\\"QueryRows\\\").into()); }}\"\n                                    );\n                                },\n                                \"}\",\n                            );\n                        },\n                        \"}\",\n                    );\n                    writeln!(out, \"Ok(db_update)\");\n                },\n                \"}\\n\",\n            );\n\n            out.delimited_block(\n                \"fn parse_unsubscribe_rows(raw: __ws::v2::QueryRows) -> __sdk::Result<Self> {\",\n                |out| {\n                    writeln!(out, \"let mut db_update = DbUpdate::default();\");\n                    out.delimited_block(\n                        \"for table_rows in raw.tables {\",\n                        |out| {\n                            out.delimited_block(\n                                \"match &table_rows.table[..] {\",\n                                |out| {\n                                    for (name, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n                                        writeln!(\n                                            out,\n                                            \"{:?} => db_update.{}.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\",\n                                            name.deref(),\n                                            table_method_name(accessor_name),\n                                        );\n                                    }\n                                    writeln!(\n                                        out,\n                                        \"unknown => {{ return Err(__sdk::InternalError::unknown_name(\\\"table\\\", unknown, \\\"QueryRows\\\").into()); }}\"\n                                    );\n                                },\n                                \"}\",\n                            );\n                        },\n                        \"}\",\n                    );\n                    writeln!(out, \"Ok(db_update)\");\n                },\n                \"}\\n\",\n            );\n        },\n        \"}\\n\",\n    );\n}\n\nfn print_applied_diff_defn(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    writeln!(out, \"#[derive(Default)]\");\n    writeln!(out, \"#[allow(non_snake_case)]\");\n    writeln!(out, \"#[doc(hidden)]\");\n    out.delimited_block(\n        \"pub struct AppliedDiff<'r> {\",\n        |out| {\n            for (_, accessor_name, product_type_ref) in iter_table_names_and_types(module, visibility) {\n                writeln!(\n                    out,\n                    \"{}: __sdk::TableAppliedDiff<'r, {}>,\",\n                    table_method_name(accessor_name),\n                    type_ref_name(module, product_type_ref),\n                );\n            }\n            // Also write a `PhantomData` field which uses the lifetime `r`,\n            // in case the module defines zero tables,\n            // as unused lifetime params are an error.\n            writeln!(out, \"__unused: std::marker::PhantomData<&'r ()>,\",);\n        },\n        \"}\\n\",\n    );\n\n    out.newline();\n\n    writeln!(\n        out,\n        \"\nimpl __sdk::InModule for AppliedDiff<'_> {{\n    type Module = RemoteModule;\n}}\n\",\n    );\n\n    out.delimited_block(\n        \"impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {\",\n        |out| {\n            out.delimited_block(\n                \"fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks<RemoteModule>) {\",\n                |out| {\n                    for (name, accessor_name, product_type_ref) in iter_table_names_and_types(module, visibility) {\n                        writeln!(\n                            out,\n                            \"callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);\",\n                            type_ref_name(module, product_type_ref),\n                            name.deref(),\n                            table_method_name(accessor_name),\n                        );\n                    }\n                },\n                \"}\\n\",\n            );\n        },\n        \"}\\n\",\n    );\n}\n\nfn print_impl_spacetime_module(module: &ModuleDef, visibility: CodegenVisibility, out: &mut Indenter) {\n    out.delimited_block(\n        \"impl __sdk::SpacetimeModule for RemoteModule {\",\n        |out| {\n            writeln!(\n                out,\n                \"\ntype DbConnection = DbConnection;\ntype EventContext = EventContext;\ntype ReducerEventContext = ReducerEventContext;\ntype ProcedureEventContext = ProcedureEventContext;\ntype SubscriptionEventContext = SubscriptionEventContext;\ntype ErrorContext = ErrorContext;\ntype Reducer = Reducer;\ntype DbView = RemoteTables;\ntype Reducers = RemoteReducers;\ntype Procedures = RemoteProcedures;\ntype DbUpdate = DbUpdate;\ntype AppliedDiff<'r> = AppliedDiff<'r>;\ntype SubscriptionHandle = SubscriptionHandle;\ntype QueryBuilder = __sdk::QueryBuilder;\n\"\n            );\n            out.delimited_block(\n                \"fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {\",\n                |out| {\n                    for (_, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n                        writeln!(\n                            out,\n                            \"{}::register_table(client_cache);\",\n                            table_module_name(accessor_name)\n                        );\n                    }\n                },\n                \"}\\n\",\n            );\n            out.delimited_block(\n                \"const ALL_TABLE_NAMES: &'static [&'static str] = &[\",\n                |out| {\n                    for (name, _, _) in iter_table_names_and_types(module, visibility) {\n                        writeln!(out, \"\\\"{name}\\\",\");\n                    }\n                },\n                \"];\\n\",\n            );\n        },\n        \"}\\n\",\n    );\n}\n\nfn print_const_db_context_types(out: &mut Indenter) {\n    writeln!(\n        out,\n        \"\n#[doc(hidden)]\n#[derive(Debug)]\npub struct RemoteModule;\n\nimpl __sdk::InModule for RemoteModule {{\n    type Module = Self;\n}}\n\n/// The `reducers` field of [`EventContext`] and [`DbConnection`],\n/// with methods provided by extension traits for each reducer defined by the module.\npub struct RemoteReducers {{\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::InModule for RemoteReducers {{\n    type Module = RemoteModule;\n}}\n\n/// The `procedures` field of [`DbConnection`] and other [`DbContext`] types,\n/// with methods provided by extension traits for each procedure defined by the module.\npub struct RemoteProcedures {{\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::InModule for RemoteProcedures {{\n    type Module = RemoteModule;\n}}\n\n/// The `db` field of [`EventContext`] and [`DbConnection`],\n/// with methods provided by extension traits for each table defined by the module.\npub struct RemoteTables {{\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::InModule for RemoteTables {{\n    type Module = RemoteModule;\n}}\n\n/// A connection to a remote module, including a materialized view of a subset of the database.\n///\n/// Connect to a remote module by calling [`DbConnection::builder`]\n/// and using the [`__sdk::DbConnectionBuilder`] builder-pattern constructor.\n///\n/// You must explicitly advance the connection by calling any one of:\n///\n/// - [`DbConnection::frame_tick`].\n/// - [`DbConnection::run_threaded`].\n/// - [`DbConnection::run_async`].\n/// - [`DbConnection::advance_one_message`].\n/// - [`DbConnection::advance_one_message_blocking`].\n/// - [`DbConnection::advance_one_message_async`].\n///\n/// Which of these methods you should call depends on the specific needs of your application,\n/// but you must call one of them, or else the connection will never progress.\npub struct DbConnection {{\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    #[doc(hidden)]\n\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::InModule for DbConnection {{\n    type Module = RemoteModule;\n}}\n\nimpl __sdk::DbContext for DbConnection {{\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {{\n        &self.db\n    }}\n    fn reducers(&self) -> &Self::Reducers {{\n        &self.reducers\n    }}\n    fn procedures(&self) -> &Self::Procedures {{\n        &self.procedures\n    }}\n\n    fn is_active(&self) -> bool {{\n        self.imp.is_active()\n    }}\n\n    fn disconnect(&self) -> __sdk::Result<()> {{\n        self.imp.disconnect()\n    }}\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {{\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }}\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {{\n        self.imp.try_identity()\n    }}\n    fn connection_id(&self) -> __sdk::ConnectionId {{\n        self.imp.connection_id()\n    }}\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {{\n        self.imp.try_connection_id()\n    }}\n}}\n\nimpl DbConnection {{\n    /// Builder-pattern constructor for a connection to a remote module.\n    ///\n    /// See [`__sdk::DbConnectionBuilder`] for required and optional configuration for the new connection.\n    pub fn builder() -> __sdk::DbConnectionBuilder<RemoteModule> {{\n        __sdk::DbConnectionBuilder::new()\n    }}\n\n    /// If any WebSocket messages are waiting, process one of them.\n    ///\n    /// Returns `true` if a message was processed, or `false` if the queue is empty.\n    /// Callers should invoke this message in a loop until it returns `false`\n    /// or for as much time is available to process messages.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::frame_tick`] each frame\n    /// to fully exhaust the queue whenever time is available.\n    pub fn advance_one_message(&self) -> __sdk::Result<bool> {{\n        self.imp.advance_one_message()\n    }}\n\n    /// Process one WebSocket message, potentially blocking the current thread until one is received.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::run_threaded`] to spawn a thread\n    /// which advances the connection automatically.\n    pub fn advance_one_message_blocking(&self) -> __sdk::Result<()> {{\n        self.imp.advance_one_message_blocking()\n    }}\n\n    /// Process one WebSocket message, `await`ing until one is received.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::run_async`] to run an `async` loop\n    /// which advances the connection when polled.\n    pub async fn advance_one_message_async(&self) -> __sdk::Result<()> {{\n        self.imp.advance_one_message_async().await\n    }}\n\n    /// Process all WebSocket messages waiting in the queue,\n    /// then return without `await`ing or blocking the current thread.\n    pub fn frame_tick(&self) -> __sdk::Result<()> {{\n        self.imp.frame_tick()\n    }}\n\n    /// Spawn a thread which processes WebSocket messages as they are received.\n    pub fn run_threaded(&self) -> std::thread::JoinHandle<()> {{\n        self.imp.run_threaded()\n    }}\n\n    /// Run an `async` loop which processes WebSocket messages when polled.\n    pub async fn run_async(&self) -> __sdk::Result<()> {{\n        self.imp.run_async().await\n    }}\n}}\n\nimpl __sdk::DbConnection for DbConnection {{\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>) -> Self {{\n        Self {{\n            db: RemoteTables {{ imp: imp.clone() }},\n            reducers: RemoteReducers {{ imp: imp.clone() }},\n            procedures: RemoteProcedures {{ imp: imp.clone() }},\n            imp,\n        }}\n    }}\n}}\n\n/// A handle on a subscribed query.\n// TODO: Document this better after implementing the new subscription API.\n#[derive(Clone)]\npub struct SubscriptionHandle {{\n    imp: __sdk::SubscriptionHandleImpl<RemoteModule>,\n}}\n\nimpl __sdk::InModule for SubscriptionHandle {{\n    type Module = RemoteModule;\n}}\n\nimpl __sdk::SubscriptionHandle for SubscriptionHandle {{\n    fn new(imp: __sdk::SubscriptionHandleImpl<RemoteModule>) -> Self {{\n        Self {{ imp }}\n    }}\n\n    /// Returns true if this subscription has been terminated due to an unsubscribe call or an error.\n    fn is_ended(&self) -> bool {{\n        self.imp.is_ended()\n    }}\n\n    /// Returns true if this subscription has been applied and has not yet been unsubscribed.\n    fn is_active(&self) -> bool {{\n        self.imp.is_active()\n    }}\n\n    /// Unsubscribe from the query controlled by this `SubscriptionHandle`,\n    /// then run `on_end` when its rows are removed from the client cache.\n    fn unsubscribe_then(self, on_end: __sdk::OnEndedCallback<RemoteModule>) -> __sdk::Result<()> {{\n        self.imp.unsubscribe_then(Some(on_end))\n    }}\n\n    fn unsubscribe(self) -> __sdk::Result<()> {{\n        self.imp.unsubscribe_then(None)\n    }}\n\n}}\n\n/// Alias trait for a [`__sdk::DbContext`] connected to this module,\n/// with that trait's associated types bounded to this module's concrete types.\n///\n/// Users can use this trait as a boundary on definitions which should accept\n/// either a [`DbConnection`] or an [`EventContext`] and operate on either.\npub trait RemoteDbContext: __sdk::DbContext<\n    DbView = RemoteTables,\n    Reducers = RemoteReducers,\n    SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,\n> {{}}\nimpl<Ctx: __sdk::DbContext<\n    DbView = RemoteTables,\n    Reducers = RemoteReducers,\n    SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,\n>> RemoteDbContext for Ctx {{}}\n\",\n    );\n\n    define_event_context(\n        out,\n        \"EventContext\",\n        Some(\"__sdk::Event<Reducer>\"),\n        \"[`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks\",\n        Some(\"[`__sdk::Event`]\"),\n    );\n\n    define_event_context(\n        out,\n        \"ReducerEventContext\",\n        Some(\"__sdk::ReducerEvent<Reducer>\"),\n        \"on-reducer callbacks\", // There's no single trait or method for reducer callbacks, so we can't usefully link to them.\n        Some(\"[`__sdk::ReducerEvent`]\"),\n    );\n\n    define_event_context(\n        out,\n        \"ProcedureEventContext\",\n        None, // ProcedureEventContexts  have no additional `event` info, so they don't even get that field.\n        \"procedure callbacks\", // There's no single trait or method for procedure callbacks, so we can't usefully link to them.\n        None,\n    );\n\n    define_event_context(\n        out,\n        \"SubscriptionEventContext\",\n        None, // SubscriptionEventContexts have no additional `event` info, so they don't even get that field.\n        \"[`__sdk::SubscriptionBuilder::on_applied`] and [`SubscriptionHandle::unsubscribe_then`] callbacks\",\n        None,\n    );\n\n    define_event_context(\n        out,\n        \"ErrorContext\",\n        Some(\"Option<__sdk::Error>\"),\n        \"[`__sdk::DbConnectionBuilder::on_disconnect`], [`__sdk::DbConnectionBuilder::on_connect_error`] and [`__sdk::SubscriptionBuilder::on_error`] callbacks\",\n        Some(\"[`__sdk::Error`]\"),\n    );\n}\n\n/// Define a type that implements `AbstractEventContext` and one of its concrete subtraits.\n///\n/// `struct_and_trait_name` should be the name of an event context trait,\n/// and will also be used as the new struct's name.\n///\n/// `event_type`, if `Some`, should be a Rust type which will be the type of the new struct's `event` field.\n/// If `None`, the new struct will not have such a field.\n/// The `SubscriptionEventContext` will pass `None`, since there is no useful information to add.\n///\n/// `passed_to_callbacks_doc_link` should be a rustdoc-formatted phrase\n/// which links to the callback-registering functions for the callbacks which accept this event context type.\n/// It should be of the form \"foo callbacks\" or \"foo, bar and baz callbacks\",\n/// with link formatting where appropriate, and no trailing punctuation.\n///\n/// If `event_type` is `Some`, `event_type_doc_link` should be as well.\n/// It should be a rustdoc-formatted link (including square brackets and all) to the `event_type`.\n/// This may differ (in the `strcmp` sense) from `event_type` because it should not include generic parameters.\nfn define_event_context(\n    out: &mut Indenter,\n    struct_and_trait_name: &str,\n    event_type: Option<&str>,\n    passed_to_callbacks_doc_link: &str,\n    event_type_doc_link: Option<&str>,\n) {\n    if let (Some(event_type), Some(event_type_doc_link)) = (event_type, event_type_doc_link) {\n        write!(\n            out,\n            \"\n/// An [`__sdk::DbContext`] augmented with a {event_type_doc_link},\n/// passed to {passed_to_callbacks_doc_link}.\npub struct {struct_and_trait_name} {{\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    /// The event which caused these callbacks to run.\n    pub event: {event_type},\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::AbstractEventContext for {struct_and_trait_name} {{\n    type Event = {event_type};\n    fn event(&self) -> &Self::Event {{\n        &self.event\n    }}\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {{\n        Self {{\n            db: RemoteTables {{ imp: imp.clone() }},\n            reducers: RemoteReducers {{ imp: imp.clone() }},\n            procedures: RemoteProcedures {{ imp: imp.clone() }},\n            event,\n            imp,\n        }}\n    }}\n}}\n\",\n        );\n    } else {\n        debug_assert!(event_type.is_none() && event_type_doc_link.is_none());\n        write!(\n            out,\n            \"\n/// An [`__sdk::DbContext`] passed to {passed_to_callbacks_doc_link}.\npub struct {struct_and_trait_name} {{\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}}\n\nimpl __sdk::AbstractEventContext for {struct_and_trait_name} {{\n    type Event = ();\n    fn event(&self) -> &Self::Event {{\n        &()\n    }}\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {{\n        Self {{\n            db: RemoteTables {{ imp: imp.clone() }},\n            reducers: RemoteReducers {{ imp: imp.clone() }},\n            procedures: RemoteProcedures {{ imp: imp.clone() }},\n            imp,\n        }}\n    }}\n}}\n\",\n        );\n    }\n\n    write!(\n        out,\n        \"\nimpl __sdk::InModule for {struct_and_trait_name} {{\n    type Module = RemoteModule;\n}}\n\nimpl __sdk::DbContext for {struct_and_trait_name} {{\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {{\n        &self.db\n    }}\n    fn reducers(&self) -> &Self::Reducers {{\n        &self.reducers\n    }}\n    fn procedures(&self) -> &Self::Procedures {{\n        &self.procedures\n    }}\n\n    fn is_active(&self) -> bool {{\n        self.imp.is_active()\n    }}\n\n    fn disconnect(&self) -> __sdk::Result<()> {{\n        self.imp.disconnect()\n    }}\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {{\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }}\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {{\n        self.imp.try_identity()\n    }}\n    fn connection_id(&self) -> __sdk::ConnectionId {{\n        self.imp.connection_id()\n    }}\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {{\n        self.imp.try_connection_id()\n    }}\n}}\n\nimpl __sdk::{struct_and_trait_name} for {struct_and_trait_name} {{}}\n\"\n    );\n}\n\n/// Print `use super::` imports for each of the `imports`.\nfn print_imports(module: &ModuleDef, out: &mut Indenter, imports: Imports) {\n    for typeref in imports {\n        let module_name = type_ref_module_name(module, typeref);\n        let type_name = type_ref_name(module, typeref);\n        writeln!(out, \"use super::{module_name}::{type_name};\");\n    }\n}\n\nfn add_one_import(imports: &mut Imports, import: &AlgebraicTypeUse) {\n    import.for_each_ref(|r| {\n        imports.insert(r);\n    })\n}\n\nfn gen_imports(imports: &mut Imports, roots: &[(Identifier, AlgebraicTypeUse)]) {\n    for (_, ty) in roots {\n        add_one_import(imports, ty);\n    }\n}\n\nfn remove_skipped_imports(imports: &mut Imports, dont_import: &[AlgebraicTypeRef]) {\n    for skip in dont_import {\n        imports.remove(skip);\n    }\n}\n\n/// Use `search_function` on `roots` to detect required imports, then print them with `print_imports`.\n///\n/// `this_file` is passed and excluded for the case of recursive types:\n/// without it, the definition for a type like `struct Foo { foos: Vec<Foo> }`\n/// would attempt to include `import super::foo::Foo`, which fails to compile.\nfn gen_and_print_imports(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    roots: &[(Identifier, AlgebraicTypeUse)],\n    dont_import: &[AlgebraicTypeRef],\n) {\n    let mut imports = BTreeSet::new();\n\n    gen_imports(&mut imports, roots);\n    remove_skipped_imports(&mut imports, dont_import);\n\n    print_imports(module, out, imports);\n}\n"
  },
  {
    "path": "crates/codegen/src/typescript.rs",
    "content": "use crate::util::{\n    is_reducer_invokable, iter_constraints, iter_indexes, iter_procedures, iter_reducers, iter_table_names_and_types,\n    iter_tables, iter_types, iter_views, print_auto_generated_version_comment,\n};\nuse crate::{CodegenOptions, OutputFile};\n\nuse super::util::{collect_case, print_auto_generated_file_comment, type_ref_name};\n\nuse std::collections::BTreeSet;\nuse std::fmt::{self, Write};\nuse std::iter;\nuse std::ops::Deref;\n\nuse convert_case::{Case, Casing};\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_lib::sats::AlgebraicTypeRef;\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_schema::def::{ConstraintDef, IndexDef, ModuleDef, ReducerDef, TableDef, TypeDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse};\n\nuse super::code_indenter::{CodeIndenter, Indenter};\nuse super::Lang;\nuse spacetimedb_lib::version::spacetimedb_lib_version;\n\nconst INDENT: &str = \"  \";\n\npub struct TypeScript;\n\nimpl Lang for TypeScript {\n    fn generate_type_files(&self, _module: &ModuleDef, _typ: &TypeDef) -> Vec<OutputFile> {\n        vec![]\n    }\n\n    /// e.g.\n    /// ```ts\n    /// table({\n    ///   name: 'player',\n    ///   indexes: [\n    ///     {\n    ///       accessor: 'this_is_an_index',\n    ///       name: 'this_is_an_index',\n    ///       algorithm: \"btree\",\n    ///       columns: [ \"ownerId\" ],\n    ///     }\n    ///   ],\n    /// }, t.row({\n    ///   id: t.u32().primaryKey(),\n    ///   ownerId: t.string(),\n    ///   name: t.string().unique(),\n    ///   location: pointType,\n    /// }))\n    /// ```\n    fn generate_table_file_from_schema(\n        &self,\n        module: &ModuleDef,\n        table: &TableDef,\n        _schema: TableSchema,\n    ) -> OutputFile {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false, true);\n\n        let type_ref = table.product_type_ref;\n        let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap();\n\n        // Import the types of all fields.\n        // We only need to import fields which have indices or unique constraints,\n        // but it's easier to just import all of 'em, since we have `// @ts-nocheck` anyway.\n        gen_and_print_imports(\n            module,\n            out,\n            product_def.element_types(),\n            &[], // No need to skip any imports; we're not defining a type, so there's no chance of circular imports.\n        );\n\n        writeln!(out);\n\n        writeln!(out, \"export default __t.row({{\");\n        out.indent(1);\n        write_object_type_builder_fields(module, out, &product_def.elements, table.primary_key, true, true).unwrap();\n        out.dedent(1);\n        writeln!(out, \"}});\");\n        OutputFile {\n            filename: table_module_name(&table.accessor_name) + \".ts\",\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_reducer_file(&self, module: &ModuleDef, reducer: &ReducerDef) -> OutputFile {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false, true);\n\n        out.newline();\n\n        gen_and_print_imports(\n            module,\n            out,\n            reducer.params_for_generate.element_types(),\n            // No need to skip any imports; we're not emitting a type that other modules can import.\n            &[],\n        );\n\n        define_body_for_reducer(module, out, &reducer.params_for_generate.elements);\n\n        OutputFile {\n            filename: reducer_module_name(&reducer.accessor_name) + \".ts\",\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_procedure_file(\n        &self,\n        module: &ModuleDef,\n        procedure: &spacetimedb_schema::def::ProcedureDef,\n    ) -> OutputFile {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, false, true);\n\n        out.newline();\n\n        gen_and_print_imports(\n            module,\n            out,\n            procedure\n                .params_for_generate\n                .element_types()\n                .chain([&procedure.return_type_for_generate]),\n            // No need to skip any imports; we're not emitting a type that other modules can import.\n            &[],\n        );\n\n        writeln!(out, \"export const params = {{\");\n        out.with_indent(|out| {\n            write_object_type_builder_fields(module, out, &procedure.params_for_generate.elements, None, true, false)\n                .unwrap()\n        });\n        writeln!(out, \"}};\");\n\n        write!(out, \"export const returnType = \");\n        write_type_builder(module, out, &procedure.return_type_for_generate).unwrap();\n\n        OutputFile {\n            filename: procedure_module_name(&procedure.accessor_name) + \".ts\",\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_global_files(&self, module: &ModuleDef, options: &CodegenOptions) -> Vec<OutputFile> {\n        let mut output = CodeIndenter::new(String::new(), INDENT);\n        let out = &mut output;\n\n        print_file_header(out, true, false);\n\n        writeln!(out);\n        writeln!(out, \"// Import all reducer arg schemas\");\n        for reducer in iter_reducers(module, options.visibility) {\n            if !is_reducer_invokable(reducer) {\n                // Skip system-defined reducers\n                continue;\n            }\n            let reducer_module_name = reducer_module_name(&reducer.accessor_name);\n            let args_type = reducer_args_type_name(&reducer.accessor_name);\n            writeln!(out, \"import {args_type} from \\\"./{reducer_module_name}\\\";\");\n        }\n\n        writeln!(out);\n        writeln!(out, \"// Import all procedure arg schemas\");\n        for procedure in iter_procedures(module, options.visibility) {\n            let procedure_module_name = procedure_module_name(&procedure.accessor_name);\n            let args_type = procedure_args_type_name(&procedure.accessor_name);\n            writeln!(out, \"import * as {args_type} from \\\"./{procedure_module_name}\\\";\");\n        }\n\n        writeln!(out);\n        writeln!(out, \"// Import all table schema definitions\");\n        for (_, accessor_name, _) in iter_table_names_and_types(module, options.visibility) {\n            let table_module_name = table_module_name(accessor_name);\n            let table_name_pascalcase = accessor_name.deref().to_case(Case::Pascal);\n            // TODO: This really shouldn't be necessary. We could also have `table()` accept\n            // `__t.object(...)`s.\n            writeln!(out, \"import {table_name_pascalcase}Row from \\\"./{table_module_name}\\\";\");\n        }\n\n        writeln!(out);\n        writeln!(out, \"/** Type-only namespace exports for generated type groups. */\");\n\n        writeln!(out);\n        writeln!(out, \"/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */\");\n        writeln!(out, \"const tablesSchema = __schema({{\");\n        out.indent(1);\n        for table in iter_tables(module, options.visibility) {\n            let type_ref = table.product_type_ref;\n            let table_name_pascalcase = table.accessor_name.deref().to_case(Case::Pascal);\n            writeln!(out, \"{}: __table({{\", table.accessor_name);\n            out.indent(1);\n            write_table_opts(\n                module,\n                out,\n                type_ref,\n                &table.name,\n                iter_indexes(table),\n                iter_constraints(table),\n                table.is_event,\n            );\n            out.dedent(1);\n            writeln!(out, \"}}, {}Row),\", table_name_pascalcase);\n        }\n        for view in iter_views(module) {\n            let type_ref = view.product_type_ref;\n            let view_name_pascalcase = view.accessor_name.deref().to_case(Case::Pascal);\n            writeln!(out, \"{}: __table({{\", view.accessor_name);\n            out.indent(1);\n            write_table_opts(module, out, type_ref, &view.name, iter::empty(), iter::empty(), false);\n            out.dedent(1);\n            writeln!(out, \"}}, {}Row),\", view_name_pascalcase);\n        }\n        out.dedent(1);\n        writeln!(out, \"}});\");\n\n        writeln!(out);\n        writeln!(out, \"/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */\");\n        writeln!(out, \"const reducersSchema = __reducers(\");\n        out.indent(1);\n        for reducer in iter_reducers(module, options.visibility) {\n            if !is_reducer_invokable(reducer) {\n                // Skip system-defined reducers\n                continue;\n            }\n            let args_type = reducer_args_type_name(&reducer.accessor_name);\n            writeln!(out, \"__reducerSchema(\\\"{}\\\", {}),\", reducer.name, args_type);\n        }\n        out.dedent(1);\n        writeln!(out, \");\");\n\n        writeln!(out);\n        writeln!(\n            out,\n            \"/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */\"\n        );\n        writeln!(out, \"const proceduresSchema = __procedures(\");\n        out.indent(1);\n        for procedure in iter_procedures(module, options.visibility) {\n            let args_type = procedure_args_type_name(&procedure.accessor_name);\n            writeln!(\n                out,\n                \"__procedureSchema(\\\"{}\\\", {args_type}.params, {args_type}.returnType),\",\n                procedure.name,\n            );\n        }\n        out.dedent(1);\n        writeln!(out, \");\");\n\n        writeln!(out);\n        writeln!(\n            out,\n            \"/** The remote SpacetimeDB module schema, both runtime and type information. */\"\n        );\n        writeln!(out, \"const REMOTE_MODULE = {{\");\n        out.indent(1);\n        writeln!(out, \"versionInfo: {{\");\n        out.indent(1);\n        writeln!(out, \"cliVersion: \\\"{}\\\" as const,\", spacetimedb_lib_version());\n        out.dedent(1);\n        writeln!(out, \"}},\");\n        writeln!(out, \"tables: tablesSchema.schemaType.tables,\");\n        writeln!(out, \"reducers: reducersSchema.reducersType.reducers,\");\n        writeln!(out, \"...proceduresSchema,\");\n        out.dedent(1);\n        writeln!(out, \"}} satisfies __RemoteModule<\");\n        out.indent(1);\n        writeln!(out, \"typeof tablesSchema.schemaType,\");\n        writeln!(out, \"typeof reducersSchema.reducersType,\");\n        writeln!(out, \"typeof proceduresSchema\");\n        out.dedent(1);\n        writeln!(out, \">;\");\n        out.dedent(1);\n\n        writeln!(out);\n        writeln!(out, \"/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */\");\n        writeln!(\n            out,\n            \"export const tables: __QueryBuilder<typeof tablesSchema.schemaType> = __makeQueryBuilder(tablesSchema.schemaType);\"\n        );\n        writeln!(out);\n        writeln!(out, \"/** The reducers available in this remote SpacetimeDB module. */\");\n        writeln!(\n            out,\n            \"export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);\"\n        );\n\n        // Write type aliases for EventContext, ReducerEventContext, SubscriptionEventContext, ErrorContext\n        writeln!(out);\n        writeln!(\n            out,\n            \"/** The context type returned in callbacks for all possible events. */\"\n        );\n        writeln!(\n            out,\n            \"export type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;\"\n        );\n\n        writeln!(out, \"/** The context type returned in callbacks for reducer events. */\");\n        writeln!(\n            out,\n            \"export type ReducerEventContext = __ReducerEventContextInterface<typeof REMOTE_MODULE>;\"\n        );\n\n        writeln!(\n            out,\n            \"/** The context type returned in callbacks for subscription events. */\"\n        );\n        writeln!(\n            out,\n            \"export type SubscriptionEventContext = __SubscriptionEventContextInterface<typeof REMOTE_MODULE>;\"\n        );\n\n        writeln!(out, \"/** The context type returned in callbacks for error events. */\");\n        writeln!(\n            out,\n            \"export type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;\"\n        );\n\n        writeln!(out, \"/** The subscription handle type to manage active subscriptions created from a {{@link SubscriptionBuilder}}. */\");\n        writeln!(\n            out,\n            \"export type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;\"\n        );\n\n        writeln!(out);\n        writeln!(\n            out,\n            \"/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */\"\n        );\n        writeln!(\n            out,\n            \"export class SubscriptionBuilder extends __SubscriptionBuilderImpl<typeof REMOTE_MODULE> {{}}\"\n        );\n\n        writeln!(out);\n        writeln!(\n            out,\n            \"/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */\"\n        );\n        writeln!(\n            out,\n            \"export class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {{}}\"\n        );\n\n        writeln!(out);\n        writeln!(out, \"/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */\");\n        writeln!(\n            out,\n            \"export class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {{\"\n        );\n        out.indent(1);\n        writeln!(out, \"/** Creates a new {{@link DbConnectionBuilder}} to configure and connect to the remote SpacetimeDB instance. */\");\n        writeln!(out, \"static builder = (): DbConnectionBuilder => {{\");\n        out.indent(1);\n        writeln!(\n            out,\n            \"return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig<typeof REMOTE_MODULE>) => new DbConnection(config));\"\n        );\n        out.dedent(1);\n        writeln!(out, \"}};\");\n\n        writeln!(out);\n        writeln!(out, \"/** Creates a new {{@link SubscriptionBuilder}} to configure a subscription to the remote SpacetimeDB instance. */\");\n        writeln!(out, \"override subscriptionBuilder = (): SubscriptionBuilder => {{\");\n        out.indent(1);\n        writeln!(out, \"return new SubscriptionBuilder(this);\");\n\n        out.dedent(1);\n        writeln!(out, \"}};\");\n        out.dedent(1);\n        writeln!(out, \"}}\");\n        out.newline();\n\n        let index_file = OutputFile {\n            filename: \"index.ts\".to_string(),\n            code: output.into_inner(),\n        };\n\n        let reducers_file = generate_reducers_file(module, options);\n        let procedures_file = generate_procedures_file(module, options);\n        let types_file = generate_types_file(module);\n\n        vec![index_file, reducers_file, procedures_file, types_file]\n    }\n}\n\nfn generate_reducers_file(module: &ModuleDef, options: &CodegenOptions) -> OutputFile {\n    let mut output = CodeIndenter::new(String::new(), INDENT);\n    let out = &mut output;\n\n    print_auto_generated_file_comment(out);\n    print_lint_suppression(out);\n    writeln!(out, \"import {{ type Infer as __Infer }} from \\\"spacetimedb\\\";\");\n\n    writeln!(out);\n    writeln!(out, \"// Import all reducer arg schemas\");\n    for reducer in iter_reducers(module, options.visibility) {\n        let reducer_module_name = reducer_module_name(&reducer.accessor_name);\n        let args_type = reducer_args_type_name(&reducer.accessor_name);\n        writeln!(out, \"import {args_type} from \\\"../{reducer_module_name}\\\";\");\n    }\n\n    writeln!(out);\n    for reducer in iter_reducers(module, options.visibility) {\n        let reducer_name_pascalcase = reducer.accessor_name.deref().to_case(Case::Pascal);\n        let args_type = reducer_args_type_name(&reducer.accessor_name);\n        writeln!(\n            out,\n            \"export type {reducer_name_pascalcase}Params = __Infer<typeof {args_type}>;\"\n        );\n    }\n    out.newline();\n\n    OutputFile {\n        filename: \"types/reducers.ts\".to_string(),\n        code: output.into_inner(),\n    }\n}\n\nfn generate_procedures_file(module: &ModuleDef, options: &CodegenOptions) -> OutputFile {\n    let mut output = CodeIndenter::new(String::new(), INDENT);\n    let out = &mut output;\n\n    print_auto_generated_file_comment(out);\n    print_lint_suppression(out);\n    writeln!(out, \"import {{ type Infer as __Infer }} from \\\"spacetimedb\\\";\");\n\n    writeln!(out);\n    writeln!(out, \"// Import all procedure arg schemas\");\n    for procedure in iter_procedures(module, options.visibility) {\n        let procedure_module_name = procedure_module_name(&procedure.accessor_name);\n        let args_type = procedure_args_type_name(&procedure.accessor_name);\n        writeln!(out, \"import * as {args_type} from \\\"../{procedure_module_name}\\\";\");\n    }\n\n    writeln!(out);\n    for procedure in iter_procedures(module, options.visibility) {\n        let procedure_name_pascalcase = procedure.accessor_name.deref().to_case(Case::Pascal);\n        let args_type = procedure_args_type_name(&procedure.accessor_name);\n        writeln!(\n            out,\n            \"export type {procedure_name_pascalcase}Args = __Infer<typeof {args_type}.params>;\"\n        );\n        writeln!(\n            out,\n            \"export type {procedure_name_pascalcase}Result = __Infer<typeof {args_type}.returnType>;\"\n        );\n    }\n    out.newline();\n\n    OutputFile {\n        filename: \"types/procedures.ts\".to_string(),\n        code: output.into_inner(),\n    }\n}\n\nfn generate_types_file(module: &ModuleDef) -> OutputFile {\n    let mut output = CodeIndenter::new(String::new(), INDENT);\n    let out = &mut output;\n\n    print_file_header(out, false, true);\n    out.newline();\n\n    let reducer_type_names = module\n        .reducers()\n        .map(|reducer| reducer.accessor_name.deref().to_case(Case::Pascal))\n        .collect::<BTreeSet<_>>();\n\n    for ty in iter_types(module) {\n        let type_name = collect_case(Case::Pascal, ty.accessor_name.name_segments());\n        if reducer_type_names.contains(&type_name) {\n            continue;\n        }\n\n        match &module.typespace_for_generate()[ty.ty] {\n            AlgebraicTypeDef::Product(product) => define_body_for_product(module, out, &type_name, &product.elements),\n            AlgebraicTypeDef::Sum(sum) => define_body_for_sum(module, out, &type_name, &sum.variants),\n            AlgebraicTypeDef::PlainEnum(plain_enum) => {\n                let variants = plain_enum\n                    .variants\n                    .iter()\n                    .cloned()\n                    .map(|var| (var, AlgebraicTypeUse::Unit))\n                    .collect::<Vec<_>>();\n                define_body_for_sum(module, out, &type_name, &variants)\n            }\n        }\n    }\n\n    OutputFile {\n        filename: \"types.ts\".to_string(),\n        code: output.into_inner(),\n    }\n}\n\nfn print_index_imports(out: &mut Indenter) {\n    // All library imports are prefixed with `__` to avoid\n    // clashing with the names of user generated types.\n    let mut types = [\n        \"TypeBuilder as __TypeBuilder\",\n        \"type AlgebraicTypeType as __AlgebraicTypeType\",\n        \"Uuid as __Uuid\",\n        \"DbConnectionBuilder as __DbConnectionBuilder\",\n        \"convertToAccessorMap as __convertToAccessorMap\",\n        \"makeQueryBuilder as __makeQueryBuilder\",\n        \"type QueryBuilder as __QueryBuilder\",\n        \"type EventContextInterface as __EventContextInterface\",\n        \"type ReducerEventContextInterface as __ReducerEventContextInterface\",\n        \"type SubscriptionEventContextInterface as __SubscriptionEventContextInterface\",\n        \"type SubscriptionHandleImpl as __SubscriptionHandleImpl\",\n        \"type ErrorContextInterface as __ErrorContextInterface\",\n        \"type RemoteModule as __RemoteModule\",\n        \"SubscriptionBuilderImpl as __SubscriptionBuilderImpl\",\n        \"DbConnectionImpl as __DbConnectionImpl\",\n        \"type Event as __Event\",\n        \"schema as __schema\",\n        \"table as __table\",\n        \"type Infer as __Infer\",\n        \"reducers as __reducers\",\n        \"reducerSchema as __reducerSchema\",\n        \"procedures as __procedures\",\n        \"procedureSchema as __procedureSchema\",\n        \"type DbConnectionConfig as __DbConnectionConfig\",\n        \"t as __t\",\n    ];\n    types.sort();\n    writeln!(out, \"import {{\");\n    out.indent(1);\n    for ty in types {\n        writeln!(out, \"{ty},\");\n    }\n    out.dedent(1);\n    writeln!(out, \"}} from \\\"spacetimedb\\\";\");\n}\n\nfn print_type_builder_imports(out: &mut Indenter) {\n    // All library imports are prefixed with `__` to avoid\n    // clashing with the names of user generated types.\n    let mut types = [\n        \"TypeBuilder as __TypeBuilder\",\n        \"type AlgebraicTypeType as __AlgebraicTypeType\",\n        \"type Infer as __Infer\",\n        \"t as __t\",\n    ];\n    types.sort();\n    writeln!(out, \"import {{\");\n    out.indent(1);\n    for ty in types {\n        writeln!(out, \"{ty},\");\n    }\n    out.dedent(1);\n    writeln!(out, \"}} from \\\"spacetimedb\\\";\");\n}\n\nfn print_file_header(output: &mut Indenter, include_version: bool, type_builder_only: bool) {\n    print_auto_generated_file_comment(output);\n    if include_version {\n        print_auto_generated_version_comment(output);\n    }\n    print_lint_suppression(output);\n    if type_builder_only {\n        print_type_builder_imports(output);\n    } else {\n        print_index_imports(output);\n    }\n}\n\nfn print_lint_suppression(output: &mut Indenter) {\n    writeln!(output, \"/* eslint-disable */\");\n    writeln!(output, \"/* tslint:disable */\");\n}\n\n/// e.g.\n/// ```ts\n/// export default {\n///   x: __t.f32(),\n///   y: __t.f32(),\n///   fooBar: __t.string(),\n/// };\n/// ```\nfn define_body_for_reducer(module: &ModuleDef, out: &mut Indenter, params: &[(Identifier, AlgebraicTypeUse)]) {\n    write!(out, \"export default {{\");\n    if params.is_empty() {\n        writeln!(out, \"}};\");\n    } else {\n        writeln!(out);\n        out.with_indent(|out| write_object_type_builder_fields(module, out, params, None, true, false).unwrap());\n        writeln!(out, \"}};\");\n    }\n}\n\n/// e.g.\n/// ```ts\n/// export const Point = __t.object('Point', {\n///   x: __t.f32(),\n///   y: __t.f32(),\n///   fooBar: __t.string(),\n/// });\n/// export type Point = __Infer<typeof Point>;\n/// ```\nfn define_body_for_product(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    name: &str,\n    elements: &[(Identifier, AlgebraicTypeUse)],\n) {\n    write!(out, \"export const {name} = __t.object(\\\"{name}\\\", {{\");\n    if elements.is_empty() {\n        writeln!(out, \"}});\");\n    } else {\n        writeln!(out);\n        out.with_indent(|out| write_object_type_builder_fields(module, out, elements, None, true, false).unwrap());\n        writeln!(out, \"}});\");\n    }\n    writeln!(out, \"export type {name} = __Infer<typeof {name}>;\");\n    out.newline();\n}\n\nfn write_table_opts<'a>(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    type_ref: AlgebraicTypeRef,\n    name: &Identifier,\n    indexes: impl Iterator<Item = &'a IndexDef>,\n    constraints: impl Iterator<Item = &'a ConstraintDef>,\n    is_event: bool,\n) {\n    let product_def = module.typespace_for_generate()[type_ref].as_product().unwrap();\n    writeln!(out, \"name: '{}',\", name.deref());\n    writeln!(out, \"indexes: [\");\n    out.indent(1);\n    for index_def in indexes {\n        if index_def.generated() {\n            // Skip system-defined indexes\n            continue;\n        }\n\n        // We're generating code for the client,\n        // and it does not care what the algorithm on the server is,\n        // as it an use a btree in all cases.\n        let columns = index_def.algorithm.columns();\n        let get_name_and_type = |col_pos: ColId| {\n            let (field_name, field_type) = &product_def.elements[col_pos.idx()];\n            let name_camel = field_name.deref().to_case(Case::Camel);\n            (name_camel, field_type)\n        };\n        let accessor_name = index_def.accessor_name.as_deref().unwrap_or(&index_def.name);\n        writeln!(\n            out,\n            \"{{ accessor: '{}', name: '{}', algorithm: 'btree', columns: [\",\n            accessor_name, index_def.name\n        );\n        out.indent(1);\n        for col_id in columns.iter() {\n            writeln!(out, \"'{}',\", get_name_and_type(col_id).0);\n        }\n        out.dedent(1);\n        writeln!(out, \"] }},\");\n    }\n    out.dedent(1);\n    writeln!(out, \"],\");\n    writeln!(out, \"constraints: [\");\n    out.indent(1);\n    // Unique constraints sorted by name for determinism\n    for constraint in constraints {\n        let columns: Vec<_> = constraint\n            .data\n            .unique_columns() // Option<&ColSet>\n            .into_iter() // Iterator over 0 or 1 item (&ColSet)\n            .flat_map(|cs| cs.iter()) // Iterator over the ColIds inside the set\n            .map(|col_id| {\n                let (field_name, _field_type) = &product_def.elements[col_id.idx()];\n                let field_name = field_name.deref().to_case(Case::Camel);\n                format!(\"'{}'\", field_name)\n            })\n            .collect();\n\n        writeln!(\n            out,\n            \"{{ name: '{}', constraint: 'unique', columns: [{}] }},\",\n            constraint.name,\n            columns.join(\", \")\n        );\n    }\n    out.dedent(1);\n    writeln!(out, \"],\");\n    if is_event {\n        writeln!(out, \"event: true,\");\n    }\n}\n\n/// e.g.\n/// ```ts\n///   x: __t.f32().primaryKey(),\n///   y: __t.f32(),\n///   fooBar: __t.string(),\n/// ```\nfn write_object_type_builder_fields(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    elements: &[(Identifier, AlgebraicTypeUse)],\n    primary_key: Option<ColId>,\n    convert_case: bool,\n    write_original_name: bool,\n) -> anyhow::Result<()> {\n    for (i, (ident, ty)) in elements.iter().enumerate() {\n        let name = if convert_case {\n            ident.deref().to_case(Case::Camel)\n        } else {\n            ident.deref().into()\n        };\n\n        let is_primary_key = match primary_key {\n            Some(pk) => pk.idx() == i,\n            None => false,\n        };\n        let original_name = (write_original_name && convert_case && *name != **ident).then_some(&**ident);\n        write_type_builder_field(module, out, &name, original_name, ty, is_primary_key)?;\n    }\n\n    Ok(())\n}\n\nfn write_type_builder_field(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    name: &str,\n    original_name: Option<&str>,\n    ty: &AlgebraicTypeUse,\n    is_primary_key: bool,\n) -> fmt::Result {\n    // Do we need a getter? (Option/Array only if their inner is a Ref)\n    let needs_getter = match ty {\n        AlgebraicTypeUse::Ref(_) => true,\n        AlgebraicTypeUse::Option(inner) | AlgebraicTypeUse::Array(inner) => {\n            matches!(inner.as_ref(), AlgebraicTypeUse::Ref(_))\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            matches!(ok_ty.as_ref(), AlgebraicTypeUse::Ref(_)) || matches!(err_ty.as_ref(), AlgebraicTypeUse::Ref(_))\n        }\n        _ => false,\n    };\n\n    if needs_getter {\n        writeln!(out, \"get {name}() {{\");\n        out.indent(1);\n        write!(out, \"return \");\n    } else {\n        write!(out, \"{name}: \");\n    }\n    write_type_builder(module, out, ty)?;\n    if is_primary_key {\n        write!(out, \".primaryKey()\");\n    }\n    if let Some(original_name) = original_name {\n        write!(out, \".name(\\\"{original_name}\\\")\");\n    }\n    if needs_getter {\n        writeln!(out, \";\");\n        out.dedent(1);\n        writeln!(out, \"}},\");\n    } else {\n        writeln!(out, \",\");\n    }\n\n    Ok(())\n}\n\n/// e.g. `__t.option(__t.i32())`, `__t.string()`\nfn write_type_builder<W: Write>(module: &ModuleDef, out: &mut W, ty: &AlgebraicTypeUse) -> fmt::Result {\n    match ty {\n        AlgebraicTypeUse::Unit => write!(out, \"__t.unit()\")?,\n        AlgebraicTypeUse::Never => write!(out, \"__t.never()\")?,\n        AlgebraicTypeUse::Identity => write!(out, \"__t.identity()\")?,\n        AlgebraicTypeUse::ConnectionId => write!(out, \"__t.connectionId()\")?,\n        AlgebraicTypeUse::Timestamp => write!(out, \"__t.timestamp()\")?,\n        AlgebraicTypeUse::TimeDuration => write!(out, \"__t.timeDuration()\")?,\n        AlgebraicTypeUse::ScheduleAt => write!(out, \"__t.scheduleAt()\")?,\n        AlgebraicTypeUse::Uuid => write!(out, \"__t.uuid()\")?,\n        AlgebraicTypeUse::Option(inner_ty) => {\n            write!(out, \"__t.option(\")?;\n            write_type_builder(module, out, inner_ty)?;\n            write!(out, \")\")?;\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            write!(out, \"__t.result(\")?;\n            write_type_builder(module, out, ok_ty)?;\n            write!(out, \", \")?;\n            write_type_builder(module, out, err_ty)?;\n            write!(out, \")\")?;\n        }\n        AlgebraicTypeUse::Primitive(prim) => match prim {\n            PrimitiveType::Bool => write!(out, \"__t.bool()\")?,\n            PrimitiveType::I8 => write!(out, \"__t.i8()\")?,\n            PrimitiveType::U8 => write!(out, \"__t.u8()\")?,\n            PrimitiveType::I16 => write!(out, \"__t.i16()\")?,\n            PrimitiveType::U16 => write!(out, \"__t.u16()\")?,\n            PrimitiveType::I32 => write!(out, \"__t.i32()\")?,\n            PrimitiveType::U32 => write!(out, \"__t.u32()\")?,\n            PrimitiveType::I64 => write!(out, \"__t.i64()\")?,\n            PrimitiveType::U64 => write!(out, \"__t.u64()\")?,\n            PrimitiveType::I128 => write!(out, \"__t.i128()\")?,\n            PrimitiveType::U128 => write!(out, \"__t.u128()\")?,\n            PrimitiveType::I256 => write!(out, \"__t.i256()\")?,\n            PrimitiveType::U256 => write!(out, \"__t.u256()\")?,\n            PrimitiveType::F32 => write!(out, \"__t.f32()\")?,\n            PrimitiveType::F64 => write!(out, \"__t.f64()\")?,\n        },\n        AlgebraicTypeUse::String => write!(out, \"__t.string()\")?,\n        AlgebraicTypeUse::Array(elem_ty) => {\n            if matches!(&**elem_ty, AlgebraicTypeUse::Primitive(PrimitiveType::U8)) {\n                return write!(out, \"__t.byteArray()\");\n            }\n            write!(out, \"__t.array(\")?;\n            write_type_builder(module, out, elem_ty)?;\n            write!(out, \")\")?;\n        }\n        AlgebraicTypeUse::Ref(r) => {\n            write!(out, \"{}\", type_ref_name(module, *r))?;\n        }\n    }\n    Ok(())\n}\n\n/// e.g.\n/// ```ts\n/// // The tagged union or sum type for the algebraic type `Option`.\n/// export const Option = __t.enum(\"Option\", {\n///   none: __t.unit(),\n///   some: { value: __t.i32() },\n/// });\n/// export type Option = __Infer<typeof Option>;\n/// ```\nfn define_body_for_sum(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    name: &str,\n    variants: &[(Identifier, AlgebraicTypeUse)],\n) {\n    writeln!(out, \"// The tagged union or sum type for the algebraic type `{name}`.\");\n    write!(out, \"export const {name}\");\n    if name == \"AlgebraicType\" {\n        write!(out, \": __TypeBuilder<__AlgebraicTypeType, __AlgebraicTypeType>\");\n    }\n    writeln!(out, \" = __t.enum(\\\"{name}\\\", {{\");\n    // Convert variant names to PascalCase\n    let pascal_variants: Vec<(Identifier, AlgebraicTypeUse)> = variants\n        .iter()\n        .map(|(ident, ty)| {\n            let pascal = ident.deref().to_case(Case::Pascal);\n            (Identifier::for_test(pascal), ty.clone())\n        })\n        .collect();\n    out.with_indent(|out| write_object_type_builder_fields(module, out, &pascal_variants, None, false, false).unwrap());\n    writeln!(out, \"}});\");\n    writeln!(out, \"export type {name} = __Infer<typeof {name}>;\");\n    out.newline();\n}\n\nfn table_module_name(table_name: &Identifier) -> String {\n    table_name.deref().to_case(Case::Snake) + \"_table\"\n}\n\nfn reducer_args_type_name(reducer_name: &ReducerName) -> String {\n    reducer_name.deref().to_case(Case::Pascal) + \"Reducer\"\n}\n\nfn procedure_args_type_name(reducer_name: &Identifier) -> String {\n    reducer_name.deref().to_case(Case::Pascal) + \"Procedure\"\n}\n\nfn reducer_module_name(reducer_name: &ReducerName) -> String {\n    reducer_name.deref().to_case(Case::Snake) + \"_reducer\"\n}\n\nfn procedure_module_name(procedure_name: &Identifier) -> String {\n    procedure_name.deref().to_case(Case::Snake) + \"_procedure\"\n}\n\npub fn type_name(module: &ModuleDef, ty: &AlgebraicTypeUse) -> String {\n    let mut s = String::new();\n    write_type(module, &mut s, ty, None, None).unwrap();\n    s\n}\n\n// This should return true if we should wrap the type in parentheses when it is the element type of\n// an array. This is needed if the type has a `|` in it, e.g. `Option<T>` or `Foo | Bar`, since\n// without parens, `Foo | Bar[]` would be parsed as `Foo | (Bar[])`.\nfn needs_parens_within_array(ty: &AlgebraicTypeUse) -> bool {\n    match ty {\n        AlgebraicTypeUse::Unit\n        | AlgebraicTypeUse::Never\n        | AlgebraicTypeUse::Identity\n        | AlgebraicTypeUse::ConnectionId\n        | AlgebraicTypeUse::Timestamp\n        | AlgebraicTypeUse::TimeDuration\n        | AlgebraicTypeUse::Uuid\n        | AlgebraicTypeUse::Primitive(_)\n        | AlgebraicTypeUse::Array(_)\n        | AlgebraicTypeUse::Ref(_) // We use the type name for these.\n        | AlgebraicTypeUse::String => {\n            false\n        }\n        AlgebraicTypeUse::ScheduleAt | AlgebraicTypeUse::Option(_) | AlgebraicTypeUse::Result { .. } => {\n            true\n        }\n    }\n}\n\npub fn write_type<W: Write>(\n    module: &ModuleDef,\n    out: &mut W,\n    ty: &AlgebraicTypeUse,\n    ref_prefix: Option<&str>,\n    ref_suffix: Option<&str>,\n) -> fmt::Result {\n    match ty {\n        AlgebraicTypeUse::Unit => write!(out, \"void\")?,\n        AlgebraicTypeUse::Never => write!(out, \"never\")?,\n        AlgebraicTypeUse::Identity => write!(out, \"__Infer<typeof __t.identity()>\")?,\n        AlgebraicTypeUse::ConnectionId => write!(out, \"__Infer<typeof __t.connectionId()>\")?,\n        AlgebraicTypeUse::Timestamp => write!(out, \"__Infer<typeof __t.timestamp()>\")?,\n        AlgebraicTypeUse::TimeDuration => write!(out, \"__Infer<typeof __t.timeDuration()>\")?,\n        AlgebraicTypeUse::Uuid => write!(out, \"__Uuid\")?,\n        AlgebraicTypeUse::ScheduleAt => write!(\n            out,\n            \"{{ tag: \\\"Interval\\\", value: __Infer<typeof __t.timeDuration()> }} | {{ tag: \\\"Time\\\", value: __Infer<typeof __t.timestamp()> }}\"\n        )?,\n        AlgebraicTypeUse::Option(inner_ty) => {\n            write_type(module, out, inner_ty, ref_prefix, ref_suffix)?;\n            write!(out, \" | undefined\")?;\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            write_type(module, out, ok_ty, ref_prefix, ref_suffix)?;\n            write!(out, \" | \")?;\n            write_type(module, out, err_ty, ref_prefix, ref_suffix)?;\n        }\n        AlgebraicTypeUse::Primitive(prim) => match prim {\n            PrimitiveType::Bool => write!(out, \"boolean\")?,\n            PrimitiveType::I8 => write!(out, \"number\")?,\n            PrimitiveType::U8 => write!(out, \"number\")?,\n            PrimitiveType::I16 => write!(out, \"number\")?,\n            PrimitiveType::U16 => write!(out, \"number\")?,\n            PrimitiveType::I32 => write!(out, \"number\")?,\n            PrimitiveType::U32 => write!(out, \"number\")?,\n            PrimitiveType::I64 => write!(out, \"bigint\")?,\n            PrimitiveType::U64 => write!(out, \"bigint\")?,\n            PrimitiveType::I128 => write!(out, \"bigint\")?,\n            PrimitiveType::U128 => write!(out, \"bigint\")?,\n            PrimitiveType::I256 => write!(out, \"bigint\")?,\n            PrimitiveType::U256 => write!(out, \"bigint\")?,\n            PrimitiveType::F32 => write!(out, \"number\")?,\n            PrimitiveType::F64 => write!(out, \"number\")?,\n        },\n        AlgebraicTypeUse::String => write!(out, \"string\")?,\n        AlgebraicTypeUse::Array(elem_ty) => {\n            if matches!(&**elem_ty, AlgebraicTypeUse::Primitive(PrimitiveType::U8)) {\n                return write!(out, \"Uint8Array\");\n            }\n            let needs_parens = needs_parens_within_array(elem_ty);\n            // We wrap the inner type in parentheses to avoid ambiguity with the [] binding.\n            if needs_parens {\n                write!(out, \"(\")?;\n            }\n            write_type(module, out, elem_ty, ref_prefix, ref_suffix)?;\n            if needs_parens {\n                write!(out, \")\")?;\n            }\n            write!(out, \"[]\")?;\n        }\n        AlgebraicTypeUse::Ref(r) => {\n            write!(out, \"__Infer<typeof \")?;\n            if let Some(prefix) = ref_prefix {\n                write!(out, \"{prefix}\")?;\n            }\n            write!(out, \"{}\", type_ref_name(module, *r))?;\n            if let Some(suffix) = ref_suffix {\n                write!(out, \"{suffix}\")?;\n            }\n            write!(out, \">\")?;\n        }\n    }\n    Ok(())\n}\n\n/// Use `search_function` on `roots` to detect required imports, then print them with `print_imports`.\n///\n/// `this_file` is passed and excluded for the case of recursive types:\n/// without it, the definition for a type like `struct Foo { foos: Vec<Foo> }`\n/// would attempt to include `import { Foo } from \"./foo\"`.\nfn gen_and_print_imports<'a>(\n    module: &ModuleDef,\n    out: &mut Indenter,\n    roots: impl Iterator<Item = &'a AlgebraicTypeUse>,\n    dont_import: &[AlgebraicTypeRef],\n) {\n    let mut imports = BTreeSet::new();\n\n    for ty in roots {\n        ty.for_each_ref(|r| {\n            imports.insert(r);\n        });\n    }\n    for skip in dont_import {\n        imports.remove(skip);\n    }\n\n    if !imports.is_empty() {\n        writeln!(out, \"import {{\");\n        out.indent(1);\n        for typeref in imports {\n            let type_name = type_ref_name(module, typeref);\n            writeln!(out, \"{type_name},\");\n        }\n        out.dedent(1);\n        writeln!(out, \"}} from \\\"./types\\\";\");\n        out.newline()\n    }\n}\n\n// const RESERVED_KEYWORDS: [&str; 36] = [\n//     \"break\",\n//     \"case\",\n//     \"catch\",\n//     \"class\",\n//     \"const\",\n//     \"continue\",\n//     \"debugger\",\n//     \"default\",\n//     \"delete\",\n//     \"do\",\n//     \"else\",\n//     \"enum\",\n//     \"export\",\n//     \"extends\",\n//     \"false\",\n//     \"finally\",\n//     \"for\",\n//     \"function\",\n//     \"if\",\n//     \"import\",\n//     \"in\",\n//     \"instanceof\",\n//     \"new\",\n//     \"null\",\n//     \"return\",\n//     \"super\",\n//     \"switch\",\n//     \"this\",\n//     \"throw\",\n//     \"true\",\n//     \"try\",\n//     \"typeof\",\n//     \"var\",\n//     \"void\",\n//     \"while\",\n//     \"with\",\n// ];\n\n// fn typescript_field_name(field_name: String) -> String {\n//     if RESERVED_KEYWORDS\n//         .into_iter()\n//         .map(String::from)\n//         .collect::<Vec<String>>()\n//         .contains(&field_name)\n//     {\n//         return format!(\"_{field_name}\");\n//     }\n\n//     field_name\n// }\n"
  },
  {
    "path": "crates/codegen/src/unrealcpp.rs",
    "content": "//! Autogenerated Unreal‑C++ code‑gen backend for SpacetimeDB CLI\nuse crate::code_indenter::CodeIndenter;\nuse crate::util::{\n    fmt_fn, iter_table_names_and_types, iter_tables, print_auto_generated_file_comment,\n    print_auto_generated_version_comment, CodegenVisibility,\n};\nuse crate::util::{iter_indexes, iter_procedures, iter_reducers};\nuse crate::Lang;\nuse crate::{CodegenOptions, OutputFile};\nuse convert_case::{Case, Casing};\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashSet};\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_schema::def::{ModuleDef, ReducerDef, TableDef, TypeDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::schema::{Schema, TableSchema};\nuse spacetimedb_schema::type_for_generate::{\n    AlgebraicTypeDef, AlgebraicTypeUse, PlainEnumTypeDef, ProductTypeDef, SumTypeDef,\n};\nuse std::fmt::{self};\nuse std::ops::Deref;\nuse std::path::Path;\n\npub struct UnrealCpp<'opts> {\n    pub module_name: &'opts str,\n    pub uproject_dir: &'opts Path,\n    pub module_prefix: &'opts str,\n}\n\n// ---------------------------------------------------------------------------\n//  Lang impl\n// ---------------------------------------------------------------------------\n\nimpl UnrealCpp<'_> {\n    fn get_api_macro(&self) -> String {\n        format!(\"{}_API\", self.module_name.to_uppercase())\n    }\n}\n\nimpl Lang for UnrealCpp<'_> {\n    fn generate_table_file_from_schema(&self, module: &ModuleDef, table: &TableDef, schema: TableSchema) -> OutputFile {\n        let module_prefix = self.module_prefix;\n        let struct_name = type_ref_name(self.module_prefix, module, table.product_type_ref);\n        let table_pascal = format!(\"{module_prefix}{}\", table.name.deref().to_case(Case::Pascal));\n        let self_header = table_pascal.clone() + \"Table\";\n\n        let mut output = UnrealCppAutogen::new(\n            &[\n                \"Types/Builtins.h\",\n                &format!(\"ModuleBindings/Types/{struct_name}Type.g.h\"),\n                \"Tables/RemoteTable.h\",\n                \"DBCache/WithBsatn.h\",\n                \"DBCache/TableHandle.h\",\n                \"DBCache/TableCache.h\",\n            ],\n            &self_header,\n            false,\n        );\n\n        let row_struct = format!(\"F{struct_name}Type\"); // e.g. \"FUserType\", \"FMessageType\"\n        let handle_cls = format!(\"U{table_pascal}Table\"); // \"UMessageTable\"\n        let table_name = table.name.deref().to_string();\n\n        // Generate unique index classes first\n        let product_type = module.typespace_for_generate()[table.product_type_ref].as_product();\n\n        let mut unique_indexes = Vec::new();\n        let mut multi_key_indexes = Vec::new();\n\n        for idx in iter_indexes(table) {\n            let Some(accessor_name) = idx.accessor_name.as_ref() else {\n                continue;\n            };\n\n            // Whatever the index algorithm on the host,\n            // the client can still use btrees.\n            let columns = idx.algorithm.columns();\n            if schema.is_unique(&columns) {\n                if let Some(col) = columns.as_singleton() {\n                    let (f_name, f_ty) = &product_type.unwrap().elements[col.idx()];\n                    let field_name = f_name.deref().to_case(Case::Pascal);\n                    let field_type =\n                        cpp_ty_fmt_with_module(self.module_prefix, module, f_ty, self.module_name).to_string();\n                    let index_name = accessor_name.deref().to_case(Case::Pascal);\n                    let index_class_name = format!(\"U{table_pascal}{index_name}UniqueIndex\");\n                    let key_type = field_type.clone();\n                    let field_name_lowercase = field_name.to_lowercase();\n\n                    writeln!(output, \"UCLASS(Blueprintable)\");\n                    writeln!(\n                        output,\n                        \"class {} {index_class_name} : public UObject\",\n                        self.get_api_macro()\n                    );\n                    writeln!(output, \"{{\");\n                    writeln!(output, \"    GENERATED_BODY()\");\n                    writeln!(output);\n                    writeln!(output, \"private:\");\n                    writeln!(output, \"    // Declare an instance of your templated helper.\");\n                    writeln!(\n                        output,\n                        \"    // It's private because the UObject wrapper will expose its functionality.\"\n                    );\n                    writeln!(\n                            output,\n                            \"    FUniqueIndexHelper<{row_struct}, {key_type}, FTableCache<{row_struct}>> {index_name}IndexHelper;\"\n                        );\n                    writeln!(output);\n                    writeln!(output, \"public:\");\n                    writeln!(output, \"    {index_class_name}()\");\n                    writeln!(\n                        output,\n                        \"        // Initialize the helper with the specific unique index name\"\n                    );\n                    writeln!(output, \"        : {index_name}IndexHelper(\\\"{}\\\") {{\", f_name.deref());\n                    writeln!(output, \"    }}\");\n                    writeln!(output);\n                    writeln!(output, \"    /**\");\n                    writeln!(\n                        output,\n                        \"     * Finds a {table_pascal} by their unique {field_name_lowercase}.\"\n                    );\n                    writeln!(output, \"     * @param Key The {field_name_lowercase} to search for.\");\n                    writeln!(\n                        output,\n                        \"     * @return The found {row_struct}, or a default-constructed {row_struct} if not found.\"\n                    );\n                    writeln!(output, \"     */\");\n\n                    // Only mark as BlueprintCallable if the key type is Blueprint-compatible\n                    if is_blueprintable(module, f_ty) {\n                        writeln!(\n                            output,\n                            \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{table_pascal}Index\\\")\"\n                        );\n                    } else {\n                        writeln!(\n                                output,\n                                \"    // NOTE: Not exposed to Blueprint because {key_type} types are not Blueprint-compatible\"\n                            );\n                    }\n\n                    writeln!(output, \"    {row_struct} Find({key_type} Key)\");\n                    writeln!(output, \"    {{\");\n                    writeln!(output, \"        // Simply delegate the call to the internal helper\");\n                    writeln!(output, \"        return {index_name}IndexHelper.FindUniqueIndex(Key);\");\n                    writeln!(output, \"    }}\");\n                    writeln!(output);\n                    writeln!(\n                        output,\n                        \"    // A public setter to provide the cache to the helper after construction\"\n                    );\n                    writeln!(output, \"    // This is a common pattern when the cache might be created or provided by another system.\");\n                    writeln!(\n                        output,\n                        \"    void SetCache(TSharedPtr<const FTableCache<{row_struct}>> In{table_pascal}Cache)\"\n                    );\n                    writeln!(output, \"    {{\");\n                    writeln!(output, \"        {index_name}IndexHelper.Cache = In{table_pascal}Cache;\");\n                    writeln!(output, \"    }}\");\n                    writeln!(output, \"}};\");\n                    writeln!(output, \"/***/\");\n                    writeln!(output);\n\n                    unique_indexes.push((index_name, index_class_name, field_type, f_name.deref().to_string()));\n                }\n            }\n            // Handle non-unique BTree indexes\n            else {\n                // Generate non-unique BTree index class\n                let _index_name = accessor_name.deref().to_case(Case::Pascal);\n                let index_class_name = format!(\"U{table_pascal}{_index_name}Index\");\n\n                // Get column information\n                let column_info: Vec<_> = columns\n                    .iter()\n                    .map(|col| {\n                        let (f_name, f_ty) = &product_type.unwrap().elements[col.idx()];\n                        let field_name = f_name.deref().to_case(Case::Pascal);\n                        let field_type =\n                            cpp_ty_fmt_with_module(self.module_prefix, module, f_ty, self.module_name).to_string();\n                        let param_type = format!(\"const {field_type}&\");\n\n                        (field_name, field_type, param_type, f_ty, f_name.deref().to_string())\n                    })\n                    .collect();\n\n                // Create filter method name by concatenating column names\n                let filter_method_name = format!(\n                    \"Filter{}\",\n                    column_info\n                        .iter()\n                        .map(|(name, _, _, _, _)| name.as_str())\n                        .collect::<Vec<_>>()\n                        .join(\"\")\n                );\n\n                // Create parameter list for methods\n                let method_params = column_info\n                    .iter()\n                    .map(|(field_name, _, param_type, _, _)| format!(\"{param_type} {field_name}\"))\n                    .collect::<Vec<_>>()\n                    .join(\", \");\n\n                // Create parameter names for internal call\n                let param_names = column_info\n                    .iter()\n                    .map(|(field_name, _, _, _, _)| field_name.clone())\n                    .collect::<Vec<_>>()\n                    .join(\", \");\n\n                // Create TTuple type for FindByMultiKeyBTreeIndex\n                let tuple_types = column_info\n                    .iter()\n                    .map(|(_, field_type, _, _, _)| field_type.clone())\n                    .collect::<Vec<_>>()\n                    .join(\", \");\n\n                // This is a potential bug in the original code, but keeping it as is for now\n                // Originally Arvikasoft had if column_info.len() == 1 { format!(\"TTuple<{tuple_types}>\"); } else { format!(\"TTuple<{tuple_types}>\"); }\n                // This makes no sense since both branches are the same\n                let tuple_type = format!(\"TTuple<{tuple_types}>\");\n\n                writeln!(output, \"UCLASS(Blueprintable)\");\n                writeln!(output, \"class {index_class_name} : public UObject\");\n                writeln!(output, \"{{\");\n                writeln!(output, \"    GENERATED_BODY()\");\n                writeln!(output);\n                writeln!(output, \"public:\");\n\n                writeln!(output, \"    TArray<{row_struct}> Filter({method_params}) const\");\n                writeln!(output, \"    {{\");\n                writeln!(output, \"        TArray<{row_struct}> OutResults;\");\n                writeln!(output);\n                writeln!(output, \"        LocalCache->FindByMultiKeyBTreeIndex<{tuple_type}>(\");\n                writeln!(output, \"            OutResults,\");\n                writeln!(output, \"            TEXT(\\\"{}\\\"),\", accessor_name.deref());\n                writeln!(output, \"            MakeTuple({param_names})\");\n                writeln!(output, \"        );\");\n                writeln!(output);\n                writeln!(output, \"        return OutResults;\");\n                writeln!(output, \"    }}\");\n                writeln!(output);\n\n                writeln!(\n                    output,\n                    \"    void SetCache(TSharedPtr<FTableCache<{row_struct}>> InCache)\"\n                );\n                writeln!(output, \"    {{\");\n                writeln!(output, \"        LocalCache = InCache;\");\n                writeln!(output, \"    }}\");\n                writeln!(output);\n                writeln!(output, \"private:\");\n\n                // Check if all parameter types are Blueprint-compatible\n                let all_blueprintable = column_info\n                    .iter()\n                    .all(|(_, _, _, f_ty, _)| is_blueprintable(module, f_ty));\n\n                if all_blueprintable {\n                    writeln!(output, \"    UFUNCTION(BlueprintCallable)\");\n                } else {\n                    writeln!(output, \"    // NOTE: Not exposed to Blueprint because some parameter types are not Blueprint-compatible\");\n                }\n\n                writeln!(\n                    output,\n                    \"    void {filter_method_name}(TArray<{row_struct}>& OutResults, {method_params})\"\n                );\n                writeln!(output, \"    {{\");\n                writeln!(output, \"        OutResults = Filter({param_names});\");\n                writeln!(output, \"    }}\");\n                writeln!(output);\n\n                writeln!(output, \"    TSharedPtr<FTableCache<{row_struct}>> LocalCache;\");\n                writeln!(output, \"}};\");\n                writeln!(output);\n\n                // Store information for PostInitialize generation\n                let property_name = accessor_name.deref().to_case(Case::Pascal);\n                multi_key_indexes.push((property_name, index_class_name));\n            }\n        }\n\n        writeln!(output, \"UCLASS(BlueprintType)\");\n        writeln!(\n            output,\n            \"class {} {handle_cls} : public URemoteTable\",\n            self.get_api_macro()\n        );\n        writeln!(output, \"{{\");\n        writeln!(output, \"    GENERATED_BODY()\");\n        writeln!(output);\n        writeln!(output, \"public:\");\n\n        // Generate unique index properties\n        for (index_name, index_class_name, _, _) in &unique_indexes {\n            writeln!(output, \"    UPROPERTY(BlueprintReadOnly)\");\n            writeln!(output, \"    {index_class_name}* {index_name};\");\n            writeln!(output);\n        }\n\n        // Generate non-unique BTree index properties\n        for (index_name, index_class_name) in &multi_key_indexes {\n            writeln!(output, \"    UPROPERTY(BlueprintReadOnly)\");\n            writeln!(output, \"    {index_class_name}* {};\", index_name.to_case(Case::Pascal));\n            writeln!(output);\n        }\n\n        writeln!(output, \"    void PostInitialize();\");\n        writeln!(output);\n\n        writeln!(output, \"    /** Update function for {table_name} table*/\");\n        writeln!(\n            output,\n            \"    FTableAppliedDiff<{row_struct}> Update(TArray<FWithBsatn<{row_struct}>> InsertsRef, TArray<FWithBsatn<{row_struct}>> DeletesRef);\"\n        );\n        writeln!(output);\n\n        writeln!(output, \"    /** Number of subscribed rows currently in the cache */\");\n        writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n        writeln!(output, \"    int32 Count() const;\");\n        writeln!(output);\n\n        writeln!(output, \"    /** Return all subscribed rows in the cache */\");\n        writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n        writeln!(output, \"    TArray<{row_struct}> Iter() const;\");\n        writeln!(output);\n\n        // Generate table events in public section\n        writeln!(output, \"    // Table Events\");\n        writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( \");\n        writeln!(output, \"        F{module_prefix}On{table_pascal}Insert,\");\n        writeln!(output, \"        const F{module_prefix}EventContext&, Context,\");\n        writeln!(output, \"        const {row_struct}&, NewRow);\");\n        writeln!(output);\n\n        if !table.is_event {\n            writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams( \");\n            writeln!(output, \"        F{module_prefix}On{table_pascal}Update,\");\n            writeln!(output, \"        const F{module_prefix}EventContext&, Context,\");\n            writeln!(output, \"        const {row_struct}&, OldRow,\");\n            writeln!(output, \"        const {row_struct}&, NewRow);\");\n            writeln!(output);\n\n            writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( \");\n            writeln!(output, \"        F{module_prefix}On{table_pascal}Delete,\");\n            writeln!(output, \"        const F{module_prefix}EventContext&, Context,\");\n            writeln!(output, \"        const {row_struct}&, DeletedRow);\");\n            writeln!(output);\n        }\n\n        writeln!(\n            output,\n            \"    UPROPERTY(BlueprintAssignable, Category = \\\"SpacetimeDB Events\\\")\"\n        );\n        writeln!(output, \"    F{module_prefix}On{table_pascal}Insert OnInsert;\");\n        writeln!(output);\n\n        if !table.is_event {\n            writeln!(\n                output,\n                \"    UPROPERTY(BlueprintAssignable, Category = \\\"SpacetimeDB Events\\\")\"\n            );\n            writeln!(output, \"    F{module_prefix}On{table_pascal}Update OnUpdate;\");\n            writeln!(output);\n\n            writeln!(\n                output,\n                \"    UPROPERTY(BlueprintAssignable, Category = \\\"SpacetimeDB Events\\\")\"\n            );\n            writeln!(output, \"    F{module_prefix}On{table_pascal}Delete OnDelete;\");\n            writeln!(output);\n        }\n\n        writeln!(output, \"private:\");\n        writeln!(output, \"    const FString TableName = TEXT(\\\"{table_name}\\\");\");\n        writeln!(output);\n        writeln!(output, \"    TSharedPtr<UClientCache<{row_struct}>> Data;\");\n\n        writeln!(output, \"}};\"); // end UCLASS\n\n        OutputFile {\n            filename: format!(\n                \"Source/{}/Public/ModuleBindings/Tables/{}Table.g.h\",\n                self.module_name,\n                format_args!(\"{module_prefix}{}\", table.name.deref().to_case(Case::Pascal))\n            ),\n            code: output.into_inner(),\n        }\n    }\n\n    fn generate_type_files(&self, module: &ModuleDef, typ: &TypeDef) -> Vec<OutputFile> {\n        let name = type_ref_name(self.module_prefix, module, typ.ty);\n        let filename = format!(\n            \"Source/{}/Public/ModuleBindings/Types/{}Type.g.h\",\n            self.module_name, &name\n        );\n        let code: String = match &module.typespace_for_generate()[typ.ty] {\n            AlgebraicTypeDef::PlainEnum(plain_enum) => autogen_cpp_enum(&name, plain_enum),\n            AlgebraicTypeDef::Product(product_type_def) => autogen_cpp_struct(\n                self.module_prefix,\n                module,\n                &name,\n                product_type_def,\n                &self.get_api_macro(),\n                self.module_name,\n            ),\n            AlgebraicTypeDef::Sum(sum_type_def) => autogen_cpp_sum(\n                self.module_prefix,\n                module,\n                &name,\n                sum_type_def,\n                &self.get_api_macro(),\n                self.module_name,\n            ),\n        };\n\n        vec![OutputFile { filename, code }]\n    }\n\n    fn generate_reducer_file(&self, module: &ModuleDef, reducer: &ReducerDef) -> OutputFile {\n        let module_prefix = self.module_prefix;\n        let reducer_pascal = reducer.name.deref().to_case(Case::Pascal);\n        let pascal = format!(\"{module_prefix}{reducer_pascal}\");\n\n        // Collect includes for parameter types\n        let mut includes = HashSet::<String>::new();\n        for (_param_name, param_type) in &reducer.params_for_generate.elements {\n            collect_includes_for_type(self.module_prefix, module, param_type, &mut includes, self.module_name);\n        }\n\n        // Add {module_prefix}ReducerBase.g.h for U{module_prefix}ReducerBase definition\n        includes.insert(format!(\"ModuleBindings/{module_prefix}ReducerBase.g.h\"));\n\n        // Convert to sorted vector\n        let mut include_vec: Vec<String> = includes.into_iter().collect();\n        include_vec.sort();\n\n        // Convert to string references\n        let include_refs: Vec<&str> = include_vec.iter().map(|s| s.as_str()).collect();\n\n        let mut header = UnrealCppAutogen::new(&include_refs, &pascal, false);\n\n        let args_struct = format!(\"F{pascal}Args\");\n\n        // Generate reducer arguments struct\n        writeln!(header, \"// Reducer arguments struct for {pascal}\");\n        writeln!(header, \"USTRUCT(BlueprintType)\");\n        writeln!(header, \"struct {} {args_struct}\", self.get_api_macro());\n        writeln!(header, \"{{\");\n        writeln!(header, \"    GENERATED_BODY()\");\n        writeln!(header);\n\n        // Generate properties for each parameter\n        for (param_name, param_type) in &reducer.params_for_generate.elements {\n            let param_pascal = param_name.deref().to_case(Case::Pascal);\n            let type_str = cpp_ty_fmt_with_module(self.module_prefix, module, param_type, self.module_name).to_string();\n            let init_str = cpp_ty_init_fmt_impl(self.module_prefix, module, param_type);\n            let field_decl = format!(\"{type_str} {param_pascal}{init_str}\");\n\n            // Check if the type is blueprintable\n            if is_blueprintable(module, param_type) {\n                writeln!(header, \"    UPROPERTY(BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\");\n            } else {\n                // Add comment explaining why the field isn't exposed as UPROPERTY\n                writeln!(\n                    header,\n                    \"    // NOTE: {type_str} field not exposed to Blueprint due to non-blueprintable elements\"\n                );\n            }\n            writeln!(header, \"    {field_decl};\");\n            writeln!(header);\n        }\n\n        // Generate default constructor\n        writeln!(header, \"    {args_struct}() = default;\");\n        writeln!(header);\n\n        // Generate parameterized constructor (for BSATN/internal use)\n        if !reducer.params_for_generate.elements.is_empty() {\n            write!(header, \"    {args_struct}(\");\n            let mut first = true;\n            for (param_name, param_type) in &reducer.params_for_generate.elements {\n                if !first {\n                    write!(header, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                let type_str =\n                    cpp_ty_fmt_with_module(self.module_prefix, module, param_type, self.module_name).to_string();\n\n                write!(header, \"const {type_str}& In{param_pascal}\");\n            }\n            writeln!(header, \")\");\n\n            write!(header, \"        : \");\n            let mut first = true;\n            for (param_name, _) in &reducer.params_for_generate.elements {\n                if !first {\n                    write!(header, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(header, \"{param_pascal}(In{param_pascal})\");\n            }\n            writeln!(header);\n            writeln!(header, \"    {{}}\");\n            writeln!(header);\n        }\n\n        // Add operator== and operator!=\n        writeln!(header);\n        writeln!(\n            header,\n            \"    FORCEINLINE bool operator==(const {args_struct}& Other) const\"\n        );\n        writeln!(header, \"    {{\");\n\n        // Generate comparison for each field\n        let mut comparisons = Vec::new();\n        for (param_name, _) in &reducer.params_for_generate.elements {\n            let param_pascal = param_name.deref().to_case(Case::Pascal);\n\n            // For value types, direct comparison\n            comparisons.push(format!(\"{param_pascal} == Other.{param_pascal}\"));\n        }\n\n        if comparisons.is_empty() {\n            writeln!(header, \"        return true;\");\n        } else {\n            writeln!(header, \"        return {};\", comparisons.join(\" && \"));\n        }\n\n        writeln!(header, \"    }}\");\n        writeln!(\n            header,\n            \"    FORCEINLINE bool operator!=(const {args_struct}& Other) const\"\n        );\n        writeln!(header, \"    {{\");\n        writeln!(header, \"        return !(*this == Other);\");\n        writeln!(header, \"    }}\");\n\n        writeln!(header, \"}};\");\n        writeln!(header);\n\n        // Add BSATN serialization support for reducer args struct\n        writeln!(header, \"namespace UE::SpacetimeDB\");\n        writeln!(header, \"{{\");\n\n        // Generate the field list for the UE_SPACETIMEDB_STRUCT macro\n        let field_names: Vec<String> = reducer\n            .params_for_generate\n            .elements\n            .iter()\n            .map(|(name, _)| name.deref().to_case(Case::Pascal))\n            .collect();\n\n        if field_names.is_empty() {\n            writeln!(header, \"    UE_SPACETIMEDB_STRUCT_EMPTY({args_struct});\");\n        } else {\n            writeln!(\n                header,\n                \"    UE_SPACETIMEDB_STRUCT({args_struct}, {});\",\n                field_names.join(\", \")\n            );\n        }\n\n        writeln!(header, \"}}\");\n        writeln!(header);\n\n        // Generate the reducer class\n        writeln!(header, \"// Reducer class for internal dispatching\");\n        writeln!(header, \"UCLASS(BlueprintType)\");\n        writeln!(\n            header,\n            \"class {} U{pascal}Reducer : public U{module_prefix}ReducerBase\",\n            self.get_api_macro()\n        );\n        writeln!(header, \"{{\");\n        writeln!(header, \"    GENERATED_BODY()\");\n        writeln!(header);\n        writeln!(header, \"public:\");\n\n        // Generate properties for each parameter (for dispatching)\n        for (param_name, param_type) in &reducer.params_for_generate.elements {\n            let param_pascal = param_name.deref().to_case(Case::Pascal);\n            let type_str = cpp_ty_fmt_with_module(self.module_prefix, module, param_type, self.module_name).to_string();\n            let init_str = cpp_ty_init_fmt_impl(self.module_prefix, module, param_type);\n            let field_decl = format!(\"{type_str} {param_pascal}{init_str}\");\n\n            // Check if the type is blueprintable\n            if is_blueprintable(module, param_type) {\n                writeln!(header, \"    UPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n            } else {\n                // Add comment explaining why the field isn't exposed as UPROPERTY\n                writeln!(\n                    header,\n                    \"    // NOTE: {type_str} field not exposed to Blueprint due to non-blueprintable elements\"\n                );\n            }\n            writeln!(header, \"    {field_decl};\");\n        }\n        if !reducer.params_for_generate.elements.is_empty() {\n            writeln!(header);\n        }\n\n        writeln!(header, \"}};\");\n        writeln!(header);\n\n        writeln!(header);\n\n        let module_name = &self.module_name;\n        OutputFile {\n            filename: format!(\"Source/{module_name}/Public/ModuleBindings/Reducers/{pascal}.g.h\"),\n            code: header.into_inner(),\n        }\n    }\n\n    fn generate_procedure_file(\n        &self,\n        module: &ModuleDef,\n        procedure: &spacetimedb_schema::def::ProcedureDef,\n    ) -> OutputFile {\n        let procedure_snake = procedure.name.deref();\n        let procedure_pascal = procedure_snake.to_case(Case::Pascal);\n        let pascal = format!(\"{}{}\", self.module_prefix, procedure_pascal);\n\n        // Collect includes for parameter types\n        let mut includes = HashSet::<String>::new();\n        for (_param_name, param_type) in &procedure.params_for_generate.elements {\n            collect_includes_for_type(self.module_prefix, module, param_type, &mut includes, self.module_name);\n        }\n        collect_includes_for_type(\n            self.module_prefix,\n            module,\n            &procedure.return_type_for_generate,\n            &mut includes,\n            self.module_name,\n        );\n\n        // Convert to sorted vector\n        let mut include_vec: Vec<String> = includes.into_iter().collect();\n        include_vec.sort();\n\n        // Convert to string references\n        let include_refs: Vec<&str> = include_vec.iter().map(|s| s.as_str()).collect();\n\n        let mut header = UnrealCppAutogen::new(&include_refs, &pascal, false);\n\n        let args_struct = format!(\"F{pascal}Args\");\n\n        // Generate procedure arguments struct\n        writeln!(header, \"// Procedure arguments struct for {pascal}\");\n        writeln!(header, \"USTRUCT(BlueprintType)\");\n        writeln!(header, \"struct {} {args_struct}\", self.get_api_macro());\n        writeln!(header, \"{{\");\n        writeln!(header, \"    GENERATED_BODY()\");\n        writeln!(header);\n\n        // Generate properties for each parameter\n        for (param_name, param_type) in &procedure.params_for_generate.elements {\n            let param_pascal = param_name.deref().to_case(Case::Pascal);\n            let type_str = cpp_ty_fmt_with_module(self.module_prefix, module, param_type, self.module_name).to_string();\n            let init_str = cpp_ty_init_fmt_impl(self.module_prefix, module, param_type);\n            let field_decl = format!(\"{type_str} {param_pascal}{init_str}\");\n\n            // Check if the type is blueprintable\n            if is_blueprintable(module, param_type) {\n                writeln!(header, \"    UPROPERTY(BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\");\n            } else {\n                // Add comment explaining why the field isn't exposed as UPROPERTY\n                writeln!(\n                    header,\n                    \"    // NOTE: {type_str} field not exposed to Blueprint due to non-blueprintable elements\"\n                );\n            }\n            writeln!(header, \"    {field_decl};\");\n            writeln!(header);\n        }\n\n        // Generate default constructor\n        writeln!(header, \"    {args_struct}() = default;\");\n        writeln!(header);\n\n        // Generate parameterized constructor (for BSATN/internal use)\n        if !procedure.params_for_generate.elements.is_empty() {\n            write!(header, \"    {args_struct}(\");\n            let mut first = true;\n            for (param_name, param_type) in &procedure.params_for_generate.elements {\n                if !first {\n                    write!(header, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                let type_str =\n                    cpp_ty_fmt_with_module(self.module_prefix, module, param_type, self.module_name).to_string();\n\n                write!(header, \"const {type_str}& In{param_pascal}\");\n            }\n            writeln!(header, \")\");\n\n            write!(header, \"        : \");\n            let mut first = true;\n            for (param_name, _) in &procedure.params_for_generate.elements {\n                if !first {\n                    write!(header, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(header, \"{param_pascal}(In{param_pascal})\");\n            }\n            writeln!(header);\n            writeln!(header, \"    {{}}\");\n            writeln!(header);\n        }\n\n        // Add operator== and operator!=\n        writeln!(header);\n        writeln!(\n            header,\n            \"    FORCEINLINE bool operator==(const {args_struct}& Other) const\"\n        );\n        writeln!(header, \"    {{\");\n\n        // Generate comparison for each field\n        let mut comparisons = Vec::new();\n        for (param_name, _) in &procedure.params_for_generate.elements {\n            let param_pascal = param_name.deref().to_case(Case::Pascal);\n\n            // For value types, direct comparison\n            comparisons.push(format!(\"{param_pascal} == Other.{param_pascal}\"));\n        }\n\n        if comparisons.is_empty() {\n            writeln!(header, \"        return true;\");\n        } else {\n            writeln!(header, \"        return {};\", comparisons.join(\" && \"));\n        }\n\n        writeln!(header, \"    }}\");\n        writeln!(\n            header,\n            \"    FORCEINLINE bool operator!=(const {args_struct}& Other) const\"\n        );\n        writeln!(header, \"    {{\");\n        writeln!(header, \"        return !(*this == Other);\");\n        writeln!(header, \"    }}\");\n\n        writeln!(header, \"}};\");\n        writeln!(header);\n\n        // Add BSATN serialization support for procedure args struct\n        writeln!(header, \"namespace UE::SpacetimeDB\");\n        writeln!(header, \"{{\");\n\n        // Generate the field list for the UE_SPACETIMEDB_STRUCT macro\n        let field_names: Vec<String> = procedure\n            .params_for_generate\n            .elements\n            .iter()\n            .map(|(name, _)| name.deref().to_case(Case::Pascal))\n            .collect();\n\n        if field_names.is_empty() {\n            writeln!(header, \"    UE_SPACETIMEDB_STRUCT_EMPTY({args_struct});\");\n        } else {\n            writeln!(\n                header,\n                \"    UE_SPACETIMEDB_STRUCT({args_struct}, {});\",\n                field_names.join(\", \")\n            );\n        }\n\n        writeln!(header, \"}}\");\n\n        OutputFile {\n            filename: format!(\n                \"Source/{}/Public/ModuleBindings/Procedures/{}.g.h\",\n                self.module_name, pascal\n            ),\n            code: header.into_inner(),\n        }\n    }\n\n    fn generate_global_files(&self, module: &ModuleDef, options: &CodegenOptions) -> Vec<OutputFile> {\n        let module_prefix = self.module_prefix;\n        let mut files: Vec<OutputFile> = vec![];\n\n        let source_dir = self.uproject_dir.join(\"Source\").join(self.module_name);\n        let required_files = [\n            (\n                format!(\"{}.Build.cs\", self.module_name),\n                generate_build_cs_content(self.module_name),\n            ),\n            (\n                format!(\"{}.cpp\", self.module_name),\n                generate_module_cpp_content(self.module_name),\n            ),\n            (\n                format!(\"{}.h\", self.module_name),\n                generate_module_h_content(self.module_name),\n            ),\n        ];\n\n        for (filename, content) in required_files {\n            let file_path = source_dir.join(&filename);\n            if !file_path.exists() {\n                files.push(OutputFile {\n                    filename: format!(\"Source/{}/{}\", self.module_name, filename),\n                    code: content,\n                });\n            }\n        }\n\n        ensure_module_in_uproject(self.uproject_dir, self.module_name).unwrap_or_else(|err| panic!(\"{err}\"));\n\n        // First, collect and generate all optional types\n        let (optional_types, result_types) = collect_wrapper_types(self.module_prefix, module, options.visibility);\n        for optional_name in optional_types {\n            let module_name = &self.module_name;\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            let filename =\n                format!(\"Source/{module_name}/Public/ModuleBindings/Optionals/{module_name_pascal}{optional_name}.g.h\");\n\n            let content = generate_optional_type(\n                &optional_name,\n                self.module_prefix,\n                module,\n                &self.get_api_macro(),\n                self.module_name,\n            );\n            files.push(OutputFile {\n                filename,\n                code: content,\n            });\n        }\n\n        for (ok_name, error_name) in result_types {\n            let module_name = &self.module_name;\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            let filename = format!(\n                \"Source/{module_name}/Public/ModuleBindings/Results/{module_name_pascal}Result{ok_name}{error_name}.g.h\"\n            );\n\n            let content = generate_result_type(\n                self.module_prefix,\n                &ok_name,\n                &error_name,\n                module,\n                &self.get_api_macro(),\n                self.module_name,\n            );\n            files.push(OutputFile {\n                filename,\n                code: content,\n            });\n        }\n\n        // Generate the main SpacetimeDBClient file with proper manual includes\n        let mut includes = HashSet::<String>::new();\n\n        // Add base includes\n        includes.insert(\"Connection/DbConnectionBase.h\".to_string());\n        includes.insert(\"Connection/DbConnectionBuilder.h\".to_string());\n        includes.insert(\"Connection/Subscription.h\".to_string());\n        includes.insert(\"Connection/Callback.h\".to_string());\n        includes.insert(format!(\"ModuleBindings/{module_prefix}ReducerBase.g.h\"));\n        includes.insert(\"Kismet/BlueprintFunctionLibrary.h\".to_string());\n\n        // Include reducers\n        for reducer in iter_reducers(module, options.visibility) {\n            let reducer_pascal = format!(\"{module_prefix}{}\", reducer.name.deref().to_case(Case::Pascal));\n            includes.insert(format!(\"ModuleBindings/Reducers/{reducer_pascal}.g.h\"));\n        }\n        // Include procedures\n        for procedure in iter_procedures(module, options.visibility) {\n            let procedure_pascal = format!(\"{module_prefix}{}\", procedure.name.deref().to_case(Case::Pascal));\n            includes.insert(format!(\"ModuleBindings/Procedures/{procedure_pascal}.g.h\"));\n        }\n        // Collect includes for types used in delegates and contexts\n        // FSpacetimeDBIdentity is used in F{module_prefix}OnConnectDelegate and context methods\n        collect_includes_for_type(\n            self.module_prefix,\n            module,\n            &AlgebraicTypeUse::Identity,\n            &mut includes,\n            self.module_name,\n        );\n        // FSpacetimeDBConnectionId is used in context methods\n        collect_includes_for_type(\n            self.module_prefix,\n            module,\n            &AlgebraicTypeUse::ConnectionId,\n            &mut includes,\n            self.module_name,\n        );\n\n        // Collect includes for all reducer parameter types\n        for reducer in iter_reducers(module, options.visibility) {\n            for (_param_name, param_type) in &reducer.params_for_generate.elements {\n                collect_includes_for_type(self.module_prefix, module, param_type, &mut includes, self.module_name);\n            }\n        }\n\n        // Collect includes for all procedure parameter types\n        for procedure in iter_procedures(module, options.visibility) {\n            for (_param_name, param_type) in &procedure.params_for_generate.elements {\n                collect_includes_for_type(self.module_prefix, module, param_type, &mut includes, self.module_name);\n            }\n            collect_includes_for_type(\n                self.module_prefix,\n                module,\n                &procedure.return_type_for_generate,\n                &mut includes,\n                self.module_name,\n            );\n        }\n\n        // Convert to sorted vector\n        let mut include_vec: Vec<String> = includes.into_iter().collect();\n        include_vec.sort();\n\n        // Convert to string references\n        let include_refs: Vec<&str> = include_vec.iter().map(|s| s.as_str()).collect();\n\n        let self_header = format!(\"{module_prefix}SpacetimeDBClient\");\n        let mut client_h = UnrealCppAutogen::new(&include_refs, &self_header, true);\n\n        // Forward declarations\n        writeln!(client_h, \"// Forward declarations\");\n        writeln!(client_h, \"class U{module_prefix}DbConnection;\");\n        writeln!(client_h, \"class U{module_prefix}RemoteTables;\");\n        writeln!(client_h, \"class U{module_prefix}RemoteReducers;\");\n        writeln!(client_h, \"class U{module_prefix}RemoteProcedures;\");\n        writeln!(client_h, \"class U{module_prefix}SubscriptionBuilder;\");\n        writeln!(client_h, \"class U{module_prefix}SubscriptionHandle;\");\n        writeln!(client_h);\n\n        writeln!(client_h, \"/** Forward declaration for tables */\");\n        for (_, accessor_name, _) in iter_table_names_and_types(module, options.visibility) {\n            writeln!(\n                client_h,\n                \"class U{module_prefix}{}Table;\",\n                accessor_name.deref().to_case(Case::Pascal)\n            );\n        }\n        writeln!(client_h, \"/***/\");\n        writeln!(client_h);\n\n        // Delegates first (as in manual)\n        generate_delegates(&mut client_h, self.module_prefix);\n\n        // Context structs\n        generate_context_structs(\n            &mut client_h,\n            module,\n            options.visibility,\n            self.module_prefix,\n            &self.get_api_macro(),\n            &self.module_name.to_case(Case::Pascal),\n        );\n\n        // RemoteTables class\n        generate_remote_tables_class(\n            &mut client_h,\n            self.module_prefix,\n            module,\n            options.visibility,\n            &self.get_api_macro(),\n        );\n\n        // RemoteReducers class\n        generate_remote_reducers_class(\n            &mut client_h,\n            self.module_prefix,\n            module,\n            options.visibility,\n            &self.get_api_macro(),\n            self.module_name,\n        );\n\n        // RemoteProcedures class\n        generate_remote_procedures_class(\n            &mut client_h,\n            self.module_prefix,\n            module,\n            options.visibility,\n            &self.get_api_macro(),\n            self.module_name,\n        );\n\n        // SubscriptionBuilder class\n        generate_subscription_builder_class(&mut client_h, self.module_prefix, &self.get_api_macro());\n\n        // SubscriptionHandle class\n        generate_subscription_handle_class(&mut client_h, self.module_prefix, &self.get_api_macro());\n\n        // DbConnectionBuilder class\n        generate_db_connection_builder_class(&mut client_h, self.module_prefix, &self.get_api_macro());\n\n        // Main DbConnection class\n        generate_db_connection_class(\n            &mut client_h,\n            self.module_prefix,\n            self.module_name,\n            module,\n            &self.get_api_macro(),\n        );\n\n        // Generate the separate ReducerBase file\n        let reducer_base_header_name = format!(\"{module_prefix}ReducerBase\");\n        let mut reducer_base_header = UnrealCppAutogen::new(&[], &reducer_base_header_name, false);\n\n        // Generate the U{module_prefix}ReducerBase class\n        writeln!(reducer_base_header, \"// Abstract Reducer base class\");\n        writeln!(reducer_base_header, \"UCLASS(Abstract, BlueprintType)\");\n        writeln!(\n            reducer_base_header,\n            \"class {} U{module_prefix}ReducerBase : public UObject\",\n            self.get_api_macro()\n        );\n        writeln!(reducer_base_header, \"{{\");\n        writeln!(reducer_base_header, \"    GENERATED_BODY()\");\n        writeln!(reducer_base_header);\n        writeln!(reducer_base_header, \"public:\");\n        writeln!(\n            reducer_base_header,\n            \"    virtual ~U{module_prefix}ReducerBase() = default;\"\n        );\n        writeln!(reducer_base_header, \"}};\");\n        writeln!(reducer_base_header);\n\n        files.push(OutputFile {\n            filename: format!(\n                \"Source/{}/Public/ModuleBindings/{module_prefix}ReducerBase.g.h\",\n                self.module_name\n            ),\n            code: reducer_base_header.into_inner(),\n        });\n\n        files.push(OutputFile {\n            filename: format!(\n                \"Source/{}/Public/ModuleBindings/{module_prefix}SpacetimeDBClient.g.h\",\n                self.module_name,\n            ),\n            code: client_h.into_inner(),\n        });\n\n        // Build table includes\n        let table_includes: Vec<String> = iter_table_names_and_types(module, options.visibility)\n            .map(|(_, accessor_name, _)| {\n                format!(\n                    \"ModuleBindings/Tables/{}Table.g.h\",\n                    format_args!(\"{module_prefix}{}\", accessor_name.deref().to_case(Case::Pascal))\n                )\n            })\n            .collect();\n        let table_includes_str: Vec<&str> = table_includes.iter().map(|s| s.as_str()).collect();\n\n        let spacetime_include = format!(\"ModuleBindings/{module_prefix}SpacetimeDBClient.g.h\");\n        let mut cpp_includes = vec![spacetime_include.as_str()];\n\n        // Add additional includes from manual reference\n        cpp_includes.extend_from_slice(&[\"DBCache/WithBsatn.h\", \"BSATN/UEBSATNHelpers.h\"]);\n\n        // Add table includes\n        cpp_includes.extend(table_includes_str);\n\n        let mut client_cpp = UnrealCppAutogen::new_cpp(&cpp_includes);\n        generate_client_implementation(\n            &mut client_cpp,\n            module,\n            options.visibility,\n            self.module_prefix,\n            self.module_name,\n        );\n        files.push(OutputFile {\n            filename: format!(\n                \"Source/{}/Private/ModuleBindings/{module_prefix}SpacetimeDBClient.g.cpp\",\n                self.module_name,\n            ),\n            code: client_cpp.into_inner(),\n        });\n\n        // Generate .cpp implementation files for each table\n        for table in iter_tables(module, options.visibility) {\n            let schema = TableSchema::from_module_def(module, table, (), 0.into())\n                .validated()\n                .expect(\"table schema should validate\");\n            let table_cpp_content = generate_table_cpp(self.module_prefix, module, table, self.module_name, &schema);\n            let table_cpp_filename = format!(\n                \"Source/{}/Private/ModuleBindings/Tables/{}Table.g.cpp\",\n                self.module_name,\n                format_args!(\"{module_prefix}{}\", table.name.deref().to_case(Case::Pascal))\n            );\n            files.push(OutputFile {\n                filename: table_cpp_filename,\n                code: table_cpp_content,\n            });\n        }\n        for view in module.views() {\n            let tbl = TableDef::from(view.clone());\n            let schema = TableSchema::from_view_def_for_codegen(module, view)\n                .validated()\n                .expect(\"Failed to generate table due to validation errors\");\n            let view_cpp_content = generate_table_cpp(self.module_prefix, module, &tbl, self.module_name, &schema);\n            let view_cpp_filename = format!(\n                \"Source/{}/Private/ModuleBindings/Tables/{}Table.g.cpp\",\n                self.module_name,\n                format_args!(\"{module_prefix}{}\", view.name.deref().to_case(Case::Pascal))\n            );\n            files.push(OutputFile {\n                filename: view_cpp_filename,\n                code: view_cpp_content,\n            });\n        }\n\n        files\n    }\n}\n\n// Helper function to generate table .cpp implementation files\nfn generate_table_cpp(\n    module_prefix: &str,\n    module: &ModuleDef,\n    table: &TableDef,\n    module_name: &str,\n    schema: &TableSchema,\n) -> String {\n    let table_pascal = format!(\"{module_prefix}{}\", table.name.deref().to_case(Case::Pascal));\n    let struct_name = type_ref_name(module_prefix, module, table.product_type_ref);\n    let row_struct = format!(\"F{struct_name}Type\");\n\n    // Include the table header and other necessary headers\n    let table_header = format!(\"ModuleBindings/Tables/{table_pascal}Table.g.h\");\n    let includes = vec![\n        table_header.as_str(),\n        \"DBCache/UniqueIndex.h\",\n        \"DBCache/BTreeUniqueIndex.h\",\n        \"DBCache/ClientCache.h\",\n        \"DBCache/TableCache.h\",\n    ];\n\n    let mut output = UnrealCppAutogen::new_cpp(&includes);\n\n    // Get unique indexes and non-unique BTree indexes\n    let product_type = module.typespace_for_generate()[table.product_type_ref].as_product();\n\n    let mut unique_indexes = Vec::new();\n    let mut multi_key_indexes = Vec::new();\n\n    for idx in iter_indexes(table) {\n        let Some(accessor_name) = idx.accessor_name.as_ref() else {\n            continue;\n        };\n        // Whatever the index algorithm on the host,\n        // the client can still use btrees.\n        let columns = idx.algorithm.columns();\n        if schema.is_unique(&columns) {\n            if let Some(col) = columns.as_singleton() {\n                let (f_name, f_ty) = &product_type.unwrap().elements[col.idx()];\n                let _field_name = f_name.deref().to_case(Case::Pascal);\n                let field_type = cpp_ty_fmt_with_module(module_prefix, module, f_ty, module_name).to_string();\n                let index_name = accessor_name.deref().to_case(Case::Pascal);\n                unique_indexes.push((index_name, field_type, f_name.deref().to_string()));\n            }\n        } else {\n            // Non-unique BTree index\n            let index_name = accessor_name.deref().to_case(Case::Pascal);\n            let index_class_name = format!(\"U{table_pascal}{index_name}Index\");\n\n            // Collect column information for AddMultiKeyBTreeIndex call\n            let column_info: Vec<_> = columns\n                .iter()\n                .map(|col| {\n                    let (f_name, f_ty) = &product_type.unwrap().elements[col.idx()];\n                    let field_name = f_name.deref().to_case(Case::Pascal);\n                    let field_type = cpp_ty_fmt_with_module(module_prefix, module, f_ty, module_name).to_string();\n                    (field_name, field_type)\n                })\n                .collect();\n\n            multi_key_indexes.push((\n                index_name,\n                index_class_name,\n                accessor_name.deref().to_string(),\n                column_info,\n            ));\n        }\n    }\n\n    // Generate PostInitialize implementation\n    writeln!(output, \"void U{table_pascal}Table::PostInitialize()\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"    /** Client cache init and setting up indexes*/\");\n    writeln!(output, \"    Data = MakeShared<UClientCache<{row_struct}>>();\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    TSharedPtr<FTableCache<{row_struct}>> {table_pascal}Table = Data->GetOrAdd(TableName);\"\n    );\n\n    // Add unique constraints for each unique index\n    for (_index_name, field_type, field_name) in &unique_indexes {\n        writeln!(\n            output,\n            \"    {table_pascal}Table->AddUniqueConstraint<{field_type}>(\\\"{}\\\", [](const {row_struct}& Row) -> const {field_type}& {{\",\n            field_name.to_lowercase()\n        );\n        writeln!(output, \"        return Row.{}; }});\", field_name.to_case(Case::Pascal));\n    }\n\n    writeln!(output);\n    for (index_name, _, _) in &unique_indexes {\n        let index_class_name = format!(\"U{table_pascal}{index_name}UniqueIndex\");\n        writeln!(output, \"    {index_name} = NewObject<{index_class_name}>(this);\");\n        writeln!(output, \"    {index_name}->SetCache({table_pascal}Table);\");\n        writeln!(output);\n    }\n\n    // Add multi-key BTree indexes\n    for (index_name, index_class_name, accessor_name, column_info) in &multi_key_indexes {\n        // Generate TTuple type for AddMultiKeyBTreeIndex\n        let tuple_types: Vec<String> = column_info.iter().map(|(_, field_type)| field_type.clone()).collect();\n        let tuple_type = format!(\"TTuple<{}>\", tuple_types.join(\", \"));\n\n        // Generate field access expressions\n        let field_accesses: Vec<String> = column_info\n            .iter()\n            .map(|(field_name, _)| format!(\"Row.{field_name}\"))\n            .collect();\n\n        writeln!(\n            output,\n            \"    // Register a new multi-key B-Tree index named \\\"{accessor_name}\\\" on the {table_pascal}Table.\"\n        );\n        writeln!(output, \"    {table_pascal}Table->AddMultiKeyBTreeIndex<{tuple_type}>(\");\n        writeln!(output, \"        TEXT(\\\"{accessor_name}\\\"),\");\n        writeln!(output, \"        [](const {row_struct}& Row)\");\n        writeln!(output, \"        {{\");\n        writeln!(\n            output,\n            \"            // This tuple is stored in the B-Tree index for fast composite key lookups.\"\n        );\n        writeln!(output, \"            return MakeTuple({});\", field_accesses.join(\", \"));\n        writeln!(output, \"        }}\");\n        writeln!(output, \"    );\");\n        writeln!(output);\n        writeln!(output, \"    {index_name} = NewObject<{index_class_name}>(this);\");\n        writeln!(output, \"    {index_name}->SetCache({table_pascal}Table);\");\n        writeln!(output);\n    }\n\n    writeln!(output, \"    /***/\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Generate Update implementation\n    writeln!(\n        output,\n        \"FTableAppliedDiff<{row_struct}> U{table_pascal}Table::Update(TArray<FWithBsatn<{row_struct}>> InsertsRef, TArray<FWithBsatn<{row_struct}>> DeletesRef)\"\n    );\n    writeln!(output, \"{{\");\n    if table.is_event {\n        writeln!(\n            output,\n            \"    // Event tables are callback-only: do not persist rows in the local cache.\"\n        );\n        writeln!(output, \"    FTableAppliedDiff<{row_struct}> Diff;\");\n        writeln!(output, \"    for (const FWithBsatn<{row_struct}>& Insert : InsertsRef)\");\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        Diff.Inserts.Add(Insert.Bsatn, Insert.Row);\");\n        writeln!(output, \"    }}\");\n    } else {\n        writeln!(\n            output,\n            \"    FTableAppliedDiff<{row_struct}> Diff = BaseUpdate<{row_struct}>(InsertsRef, DeletesRef, Data, TableName);\"\n        );\n    }\n    writeln!(output);\n\n    // Add DeriveUpdatesByPrimaryKey for persistent tables with a primary key.\n    if let (false, Some(pk)) = (table.is_event, schema.pk()) {\n        let pk_field_name = pk.col_name.deref().to_case(Case::Pascal);\n        let pk_type = &product_type.unwrap().elements[pk.col_pos.idx()].1;\n        let pk_type_str = cpp_ty_fmt_with_module(module_prefix, module, pk_type, module_name).to_string();\n        writeln!(output, \"    Diff.DeriveUpdatesByPrimaryKey<{pk_type_str}>(\");\n        writeln!(output, \"        [](const {row_struct}& Row) \");\n        writeln!(output, \"        {{\");\n        writeln!(output, \"            return Row.{pk_field_name}; \");\n        writeln!(output, \"        }}\");\n        writeln!(output, \"    );\");\n        writeln!(output);\n    }\n\n    // Reset cache for indexes\n    // for (index_name, _, _) in &unique_indexes {\n    //     writeln!(output, \"    {}->SetCache(Data->GetOrAdd(TableName));\", index_name);\n    // }\n\n    // for (index_name, _, _, _) in &multi_key_indexes {\n    //     writeln!(output, \"    {}->SetCache(Data->GetOrAdd(TableName));\", index_name);\n    // }\n\n    writeln!(output, \"    return Diff;\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Generate Count implementation\n    writeln!(output, \"int32 U{table_pascal}Table::Count() const\");\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"    return GetRowCountFromTable<{row_struct}>(Data, TableName);\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Generate Iter implementation\n    writeln!(output, \"TArray<{row_struct}> U{table_pascal}Table::Iter() const\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"    return GetAllRowsFromTable<{row_struct}>(Data, TableName);\");\n    writeln!(output, \"}}\");\n\n    output.into_inner()\n}\n\n// Helper functions for generating the consolidated SpacetimeDBClient file\n\nfn generate_delegates(output: &mut UnrealCppAutogen, module_prefix: &str) {\n    writeln!(\n        output,\n        \"// Delegates using the generated connection type. These wrap the base\"\n    );\n    writeln!(\n        output,\n        \"// delegates defined in the SDK so that projects can work directly with\"\n    );\n    writeln!(\n        output,\n        \"// U{module_prefix}DbConnection without manual casting in user code.\"\n    );\n    writeln!(output, \"DECLARE_DYNAMIC_DELEGATE_ThreeParams(\");\n    writeln!(output, \"\\tF{module_prefix}OnConnectDelegate,\");\n    writeln!(output, \"\\tU{module_prefix}DbConnection*, Connection,\");\n    writeln!(output, \"\\tFSpacetimeDBIdentity, Identity,\");\n    writeln!(output, \"\\tconst FString&, Token);\");\n    writeln!(output);\n    writeln!(output, \"DECLARE_DYNAMIC_DELEGATE_TwoParams(\");\n    writeln!(output, \"\\tF{module_prefix}OnDisconnectDelegate,\");\n    writeln!(output, \"\\tU{module_prefix}DbConnection*, Connection,\");\n    writeln!(output, \"\\tconst FString&, Error);\");\n    writeln!(output);\n    writeln!(output);\n}\n\nfn generate_context_structs(\n    output: &mut UnrealCppAutogen,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    module_prefix: &str,\n    api_macro: &str,\n    module_name: &str,\n) {\n    writeln!(output, \"// Context classes for event handling\");\n    writeln!(output);\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{module_prefix}ContextBase\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tF{module_prefix}ContextBase() : Db(nullptr), Reducers(nullptr), Procedures(nullptr), Conn(nullptr) {{}};\"\n    );\n    writeln!(\n        output,\n        \"\\tF{module_prefix}ContextBase(U{module_prefix}DbConnection* InConn);\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"\\tU{module_prefix}RemoteTables* Db;\");\n    writeln!(output);\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"\\tU{module_prefix}RemoteReducers* Reducers;\");\n    writeln!(output);\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"\\tU{module_prefix}RemoteProcedures* Procedures;\");\n    writeln!(output);\n    writeln!(output, \"\\tbool IsActive() const;\");\n    writeln!(output, \"\\tvoid Disconnect();\");\n    writeln!(\n        output,\n        \"\\tbool TryGetIdentity(FSpacetimeDBIdentity& OutIdentity) const;\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBConnectionId GetConnectionId() const;\");\n    writeln!(output, \"\\tU{module_prefix}SubscriptionBuilder* SubscriptionBuilder();\");\n    writeln!(output);\n    writeln!(output, \"protected:\");\n    writeln!(output, \"\\tUPROPERTY()\");\n    writeln!(output, \"\\tU{module_prefix}DbConnection* Conn;\");\n    writeln!(output);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // BPLib for F{module_prefix}ContextBase - Needed to allow inheritance in Blueprint\n    writeln!(output, \"UCLASS()\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}ContextBaseBpLib : public UBlueprintFunctionLibrary\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"private:\");\n\n    writeln!(output, \"\\tUFUNCTION(BlueprintPure, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"\\tstatic U{module_prefix}RemoteTables* GetDb(const F{module_prefix}ContextBase& Ctx) {{ return Ctx.Db; }}\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\tUFUNCTION(BlueprintPure, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"\\tstatic U{module_prefix}RemoteReducers* GetReducers(const F{module_prefix}ContextBase& Ctx) {{ return Ctx.Reducers; }}\"\n    );\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tstatic U{module_prefix}RemoteProcedures* GetProcedures(const F{module_prefix}ContextBase& Ctx) {{ return Ctx.Procedures; }}\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\tUFUNCTION(BlueprintPure, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"\\tstatic bool IsActive(const F{module_prefix}ContextBase& Ctx) {{ return Ctx.IsActive(); }}\"\n    );\n\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    generate_reducer_bindings(output, module_prefix, module, visibility, api_macro, module_name);\n    generate_procedure_bindings(output, module_prefix, module, visibility, api_macro, module_name);\n    // {}Event: union-like struct representing SpacetimeDB event messages\n    writeln!(output, \"/** Represents event with variant message data. */\");\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{module_name}Event\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\t/** Tagged union holding reducer call, unit events, or error string */\"\n    );\n    writeln!(\n        output,\n        \"\\tTVariant<F{module_prefix}Reducer, FSpacetimeDBUnit, FString> MessageData;\"\n    );\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Type tag indicating what this event represents */\");\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"\\tESpacetimeDBEventTag Tag = ESpacetimeDBEventTag::UnknownTransaction;\"\n    );\n    writeln!(output);\n\n    // === Static factory methods ===\n    writeln!(output, \"\\t/** === Static factory methods ===*/\");\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event Reducer(const F{module_prefix}Reducer& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::Reducer;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<F{module_prefix}Reducer>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event SubscribeApplied(const FSpacetimeDBUnit& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::SubscribeApplied;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FSpacetimeDBUnit>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event UnsubscribeApplied(const FSpacetimeDBUnit& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::UnsubscribeApplied;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FSpacetimeDBUnit>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event Disconnected(const FSpacetimeDBUnit& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::Disconnected;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FSpacetimeDBUnit>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event Transaction(const FSpacetimeDBUnit& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::Transaction;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FSpacetimeDBUnit>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event SubscribeError(const FString& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::SubscribeError;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FString>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tstatic F{module_name}Event UnknownTransaction(const FSpacetimeDBUnit& Value)\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_name}Event Obj;\");\n    writeln!(output, \"\\t\\tObj.Tag = ESpacetimeDBEventTag::UnknownTransaction;\");\n    writeln!(output, \"\\t\\tObj.MessageData.Set<FSpacetimeDBUnit>(Value);\");\n    writeln!(output, \"\\t\\treturn Obj;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    // === Tag checks and getters ===\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsReducer() const {{ return Tag == ESpacetimeDBEventTag::Reducer; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE F{module_prefix}Reducer GetAsReducer() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsReducer(), TEXT(\\\"MessageData does not hold Reducer!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<F{module_prefix}Reducer>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsSubscribeApplied() const {{ return Tag == ESpacetimeDBEventTag::SubscribeApplied; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FSpacetimeDBUnit GetAsSubscribeApplied() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsSubscribeApplied(), TEXT(\\\"MessageData does not hold SubscribeApplied!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FSpacetimeDBUnit>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsUnsubscribeApplied() const {{ return Tag == ESpacetimeDBEventTag::UnsubscribeApplied; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FSpacetimeDBUnit GetAsUnsubscribeApplied() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsUnsubscribeApplied(), TEXT(\\\"MessageData does not hold UnsubscribeApplied!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FSpacetimeDBUnit>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsDisconnected() const {{ return Tag == ESpacetimeDBEventTag::Disconnected; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FSpacetimeDBUnit GetAsDisconnected() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsDisconnected(), TEXT(\\\"MessageData does not hold Disconnected!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FSpacetimeDBUnit>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsTransaction() const {{ return Tag == ESpacetimeDBEventTag::Transaction; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FSpacetimeDBUnit GetAsTransaction() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsTransaction(), TEXT(\\\"MessageData does not hold Transaction!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FSpacetimeDBUnit>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsSubscribeError() const {{ return Tag == ESpacetimeDBEventTag::SubscribeError; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FString GetAsSubscribeError() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsSubscribeError(), TEXT(\\\"MessageData does not hold SubscribeError!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FString>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool IsUnknownTransaction() const {{ return Tag == ESpacetimeDBEventTag::UnknownTransaction; }}\"\n    );\n    writeln!(output, \"\\tFORCEINLINE FSpacetimeDBUnit GetAsUnknownTransaction() const\");\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tensureMsgf(IsUnknownTransaction(), TEXT(\\\"MessageData does not hold UnknownTransaction!\\\"));\"\n    );\n    writeln!(output, \"\\t\\treturn MessageData.Get<FSpacetimeDBUnit>();\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    // === Equality operators ===\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator==(const F{module_name}Event& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tif (Tag != Other.Tag) return false;\");\n    writeln!(output, \"\\t\\tswitch (Tag)\");\n    writeln!(output, \"\\t\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\tcase ESpacetimeDBEventTag::Reducer: return GetAsReducer() == Other.GetAsReducer();\"\n    );\n    writeln!(output, \"\\t\\tcase ESpacetimeDBEventTag::SubscribeApplied: return GetAsSubscribeApplied() == Other.GetAsSubscribeApplied();\");\n    writeln!(output, \"\\t\\tcase ESpacetimeDBEventTag::UnsubscribeApplied: return GetAsUnsubscribeApplied() == Other.GetAsUnsubscribeApplied();\");\n    writeln!(\n        output,\n        \"\\t\\tcase ESpacetimeDBEventTag::Disconnected: return GetAsDisconnected() == Other.GetAsDisconnected();\"\n    );\n    writeln!(\n        output,\n        \"\\t\\tcase ESpacetimeDBEventTag::Transaction: return GetAsTransaction() == Other.GetAsTransaction();\"\n    );\n    writeln!(\n        output,\n        \"\\t\\tcase ESpacetimeDBEventTag::SubscribeError: return GetAsSubscribeError() == Other.GetAsSubscribeError();\"\n    );\n    writeln!(output, \"\\t\\tcase ESpacetimeDBEventTag::UnknownTransaction: return GetAsUnknownTransaction() == Other.GetAsUnknownTransaction();\");\n    writeln!(output, \"\\t\\tdefault: return false;\");\n    writeln!(output, \"\\t\\t}}\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator!=(const F{module_name}Event& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\treturn !(*this == Other);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // BPLib\n    writeln!(output, \"UCLASS()\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_name}EventBpLib : public UBlueprintFunctionLibrary\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"private:\");\n\n    // Reducer\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event Reducer(const F{module_prefix}Reducer& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return F{module_name}Event::Reducer(InValue);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // SubscribeApplied\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event SubscribeApplied(const FSpacetimeDBUnit& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return F{module_name}Event::SubscribeApplied(InValue);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // UnsubscribeApplied\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event UnsubscribeApplied(const FSpacetimeDBUnit& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(\n        output,\n        \"        return F{module_name}Event::UnsubscribeApplied(InValue);\"\n    );\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // Disconnected\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event Disconnected(const FSpacetimeDBUnit& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return F{module_name}Event::Disconnected(InValue);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // Transaction\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event Transaction(const FSpacetimeDBUnit& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return F{module_name}Event::Transaction(InValue);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // SubscribeError\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event SubscribeError(const FString& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return F{module_name}Event::SubscribeError(InValue);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // UnknownTransaction\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_name}Event UnknownTransaction(const FSpacetimeDBUnit& InValue)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(\n        output,\n        \"        return F{module_name}Event::UnknownTransaction(InValue);\"\n    );\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // Is* helpers\n    for case in [\n        \"Reducer\",\n        \"SubscribeApplied\",\n        \"UnsubscribeApplied\",\n        \"Disconnected\",\n        \"Transaction\",\n        \"SubscribeError\",\n        \"UnknownTransaction\",\n    ] {\n        writeln!(\n            output,\n            \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n        );\n        writeln!(\n            output,\n            \"    static bool Is{case}(const F{module_name}Event& Event) {{ return Event.Is{case}(); }}\"\n        );\n        writeln!(output);\n    }\n\n    // GetAs* helpers\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static F{module_prefix}Reducer GetAsReducer(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsReducer();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FSpacetimeDBUnit GetAsSubscribeApplied(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsSubscribeApplied();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FSpacetimeDBUnit GetAsUnsubscribeApplied(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsUnsubscribeApplied();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FSpacetimeDBUnit GetAsDisconnected(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsDisconnected();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FSpacetimeDBUnit GetAsTransaction(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsTransaction();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FString GetAsSubscribeError(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsSubscribeError();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{module_name}Event\\\")\"\n    );\n    writeln!(\n        output,\n        \"    static FSpacetimeDBUnit GetAsUnknownTransaction(const F{module_name}Event& Event)\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return Event.GetAsUnknownTransaction();\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}EventContext, F{module_prefix}ReducerEventContext, F{module_prefix}ErrorContext, F{module_prefix}SubscriptionEventContext\n    writeln!(output);\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(\n        output,\n        \"struct {api_macro} F{module_prefix}EventContext : public F{module_prefix}ContextBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"\\tF{module_prefix}EventContext() = default;\");\n    writeln!(\n        output,\n        \"\\tF{module_prefix}EventContext(U{module_prefix}DbConnection* InConn, const F{module_name}Event& InEvent) : F{module_prefix}ContextBase(InConn), Event(InEvent) {{}}\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(output, \"\\tF{module_name}Event Event;\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}ReducerEventContext\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(\n        output,\n        \"struct {api_macro} F{module_prefix}ReducerEventContext : public F{module_prefix}ContextBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"\\tF{module_prefix}ReducerEventContext() = default;\");\n    writeln!(output, \"\\tF{module_prefix}ReducerEventContext(U{module_prefix}DbConnection* InConn, F{module_name}ReducerEvent InEvent) : F{module_prefix}ContextBase(InConn), Event(InEvent) {{}}\");\n    writeln!(output, \"\\t\");\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\") \");\n    writeln!(output, \"\\tF{module_name}ReducerEvent Event;\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}ProcedureEventContext\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(\n        output,\n        \"struct {api_macro} F{module_prefix}ProcedureEventContext : public F{module_prefix}ContextBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"\\tF{module_prefix}ProcedureEventContext() = default;\");\n    writeln!(output, \"\\tF{module_prefix}ProcedureEventContext(U{module_prefix}DbConnection* InConn, F{module_name}ProcedureEvent InEvent) : F{module_prefix}ContextBase(InConn), Event(InEvent) {{}}\");\n    writeln!(output, \"\\t\");\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\") \");\n    writeln!(output, \"\\tF{module_name}ProcedureEvent Event;\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}ErrorContext\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(\n        output,\n        \"struct {api_macro} F{module_prefix}ErrorContext : public F{module_prefix}ContextBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"\\tF{module_prefix}ErrorContext() = default;\");\n    writeln!(\n        output,\n        \"\\tF{module_prefix}ErrorContext(U{module_prefix}DbConnection* InConn, const FString& InError) : F{module_prefix}ContextBase(InConn), Error(InError) {{}}\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\tUPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(output, \"\\tFString Error;\");\n    writeln!(output);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}SubscriptionEventContext\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(\n        output,\n        \"struct {api_macro} F{module_prefix}SubscriptionEventContext : public F{module_prefix}ContextBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"\\tF{module_prefix}SubscriptionEventContext() = default;\");\n    writeln!(\n        output,\n        \"\\tF{module_prefix}SubscriptionEventContext(U{module_prefix}DbConnection* InConn) : F{module_prefix}ContextBase(InConn) {{}}\"\n    );\n    writeln!(output);\n    writeln!(output, \"}};\");\n    writeln!(output);\n    writeln!(output, \"DECLARE_DYNAMIC_DELEGATE_OneParam(\");\n    writeln!(output, \"\\tF{module_prefix}OnSubscriptionApplied,\");\n    writeln!(output, \"\\tF{module_prefix}SubscriptionEventContext, Context);\");\n    writeln!(output);\n    writeln!(output, \"DECLARE_DYNAMIC_DELEGATE_OneParam(\");\n    writeln!(output, \"\\tF{module_prefix}OnSubscriptionError,\");\n    writeln!(output, \"\\tF{module_prefix}ErrorContext, Context);\");\n    writeln!(output);\n}\n\nfn generate_reducer_bindings(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    api_macro: &str,\n    module_name: &str,\n) {\n    let reducer_count: usize = iter_reducers(module, visibility).count();\n    // ---------------------------------------------------------------------\n    // Per-module typed Reducer tagged union + typed Event\n    // ---------------------------------------------------------------------\n    writeln!(output, \"UENUM(BlueprintType, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"enum class E{module_prefix}ReducerTag : uint8\");\n    writeln!(output, \"{{\");\n    {\n        let mut first = true;\n        for reducer in iter_reducers(module, visibility) {\n            let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n            if !first {\n                writeln!(output, \",\");\n            } else {\n                first = false;\n            }\n            write!(output, \"    {reducer_name}\");\n        }\n        writeln!(output);\n    }\n    if reducer_count == 0 {\n        writeln!(output, \"    // (No reducers defined in module)\");\n        writeln!(output, \"    None\");\n    }\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // F{module_prefix}Reducer: tagged union over reducer args, with optional metadata\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{module_prefix}Reducer\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    E{module_prefix}ReducerTag Tag = static_cast<E{module_prefix}ReducerTag>(0);\"\n    );\n    writeln!(output);\n    write!(output, \"    TVariant<\");\n    {\n        let mut first = true;\n        for reducer in iter_reducers(module, visibility) {\n            let reducer_pascal = format!(\"{module_prefix}{}\", reducer.name.deref().to_case(Case::Pascal));\n            if !first {\n                write!(output, \", \");\n            } else {\n                first = false;\n            }\n            write!(output, \"F{reducer_pascal}Args\");\n        }\n    }\n    if reducer_count == 0 {\n        write!(output, \"FSpacetimeDBUnit\");\n    }\n    writeln!(output, \"> Data;\");\n    writeln!(output);\n    writeln!(output, \"    // Optional metadata\");\n    writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"    FString ReducerName;\");\n    writeln!(output, \"    uint32 ReducerId = 0;\");\n    writeln!(output, \"    uint32 RequestId = 0;\");\n    writeln!(output);\n\n    // Static constructors, Is*, GetAs*\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n        let reducer_pascal = format!(\"{module_prefix}{reducer_name}\");\n        writeln!(\n            output,\n            \"    static F{module_prefix}Reducer {reducer_name}(const F{reducer_pascal}Args& Value)\"\n        );\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        F{module_prefix}Reducer Out;\");\n        writeln!(output, \"        Out.Tag = E{module_prefix}ReducerTag::{reducer_name};\");\n        writeln!(output, \"        Out.Data.Set<F{reducer_pascal}Args>(Value);\");\n        writeln!(output, \"        Out.ReducerName = TEXT(\\\"{}\\\");\", reducer.name.deref());\n        writeln!(output, \"        return Out;\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n        writeln!(\n            output,\n            \"    FORCEINLINE bool Is{reducer_name}() const {{ return Tag == E{module_prefix}ReducerTag::{reducer_name}; }}\"\n        );\n        writeln!(\n            output,\n            \"    FORCEINLINE F{reducer_pascal}Args GetAs{reducer_name}() const\"\n        );\n        writeln!(output, \"    {{\");\n        writeln!(\n            output,\n            \"        ensureMsgf(Is{reducer_name}(), TEXT(\\\"Reducer does not hold {reducer_name}!\\\"));\"\n        );\n        writeln!(output, \"        return Data.Get<F{reducer_pascal}Args>();\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n    }\n    writeln!(\n        output,\n        \"    FORCEINLINE bool operator==(const F{module_prefix}Reducer& Other) const\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        if (Tag != Other.Tag || ReducerId != Other.ReducerId || RequestId != Other.RequestId || ReducerName != Other.ReducerName) return false;\");\n    writeln!(output, \"        switch (Tag)\");\n    writeln!(output, \"        {{\");\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n        writeln!(output, \"        case E{module_prefix}ReducerTag::{reducer_name}:\");\n        writeln!(\n            output,\n            \"            return GetAs{reducer_name}() == Other.GetAs{reducer_name}();\"\n        );\n    }\n    if reducer_count == 0 {\n        writeln!(output, \"        case E{module_prefix}ReducerTag::None:\");\n        writeln!(output, \"            return true;\");\n    }\n\n    writeln!(output, \"        default: return false;\");\n    writeln!(output, \"        }}\");\n    writeln!(output, \"    }}\");\n    writeln!(\n        output,\n        \"    FORCEINLINE bool operator!=(const F{module_prefix}Reducer& Other) const {{ return !(*this == Other); }}\"\n    );\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // BPLib for F{module_prefix}Reducer\n    writeln!(output, \"UCLASS()\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}ReducerBpLib : public UBlueprintFunctionLibrary\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"private:\");\n\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n        let reducer_pascal = format!(\"{module_prefix}{reducer_name}\");\n        // ---- Static constructors ----\n        writeln!(output);\n        writeln!(\n            output,\n            \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|Reducer\\\")\"\n        );\n        writeln!(\n            output,\n            \"    static F{module_prefix}Reducer {reducer_name}(const F{reducer_pascal}Args& Value) {{\"\n        );\n        writeln!(output, \"        return F{module_prefix}Reducer::{reducer_name}(Value);\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n\n        // Is*\n        writeln!(\n            output,\n            \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|Reducer\\\")\"\n        );\n        writeln!(\n            output,\n            \"    static bool Is{reducer_name}(const F{module_prefix}Reducer& Reducer) {{ return Reducer.Is{reducer_name}(); }}\"\n        );\n        writeln!(output);\n\n        // GetAs*\n        writeln!(\n            output,\n            \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|Reducer\\\")\"\n        );\n        writeln!(\n            output,\n            \"    static F{reducer_pascal}Args GetAs{reducer_name}(const F{module_prefix}Reducer& Reducer) {{\"\n        );\n        writeln!(output, \"        return Reducer.GetAs{reducer_name}();\");\n        writeln!(output, \"    }}\");\n    }\n\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // FReducerEvent: metadata about a reducer invocation, with typed args\n    writeln!(output, \"/** Metadata describing a reducer run. */\");\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{module_name}ReducerEvent\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Timestamp for when the reducer executed */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBTimestamp Timestamp;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Result status of the reducer */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBStatus Status;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Identity that initiated the call */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBIdentity CallerIdentity;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Connection ID for the caller */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBConnectionId CallerConnectionId;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Energy consumed while executing */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFEnergyQuantaType EnergyConsumed;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Detailed call information */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tF{module_prefix}Reducer Reducer;\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator==(const F{module_name}ReducerEvent& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\treturn Status == Other.Status && Timestamp == Other.Timestamp && CallerIdentity == Other.CallerIdentity &&\");\n    writeln!(\n        output,\n        \"\\t\\t\\tCallerConnectionId == Other.CallerConnectionId && EnergyConsumed == Other.EnergyConsumed &&\"\n    );\n    writeln!(output, \"\\t\\t\\tReducer == Other.Reducer;\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator!=(const F{module_name}ReducerEvent& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\treturn !(*this == Other);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_procedure_bindings(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    api_macro: &str,\n    module_name: &str,\n) {\n    let procedure_count: usize = iter_procedures(module, visibility).count();\n    if procedure_count > 0 {\n        // ---------------------------------------------------------------------\n        // Per-module typed Procedure tagged union + typed Event\n        // ---------------------------------------------------------------------\n        writeln!(output, \"UENUM(BlueprintType, Category = \\\"SpacetimeDB\\\")\");\n        writeln!(output, \"enum class E{module_prefix}ProcedureTag : uint8\");\n        writeln!(output, \"{{\");\n        {\n            let mut first = true;\n            for procedure in iter_procedures(module, visibility) {\n                let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n                if !first {\n                    writeln!(output, \",\");\n                } else {\n                    first = false;\n                }\n                write!(output, \"    {procedure_name}\");\n            }\n            writeln!(output);\n        }\n        writeln!(output, \"}};\");\n        writeln!(output);\n\n        // F{module_prefix}Procedure: tagged union over procedure args, with optional metadata\n        writeln!(output, \"USTRUCT(BlueprintType)\");\n        writeln!(output, \"struct {api_macro} F{module_prefix}Procedure\");\n        writeln!(output, \"{{\");\n        writeln!(output, \"    GENERATED_BODY()\");\n        writeln!(output);\n        writeln!(output, \"public:\");\n        writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n        writeln!(\n            output,\n            \"    E{module_prefix}ProcedureTag Tag = static_cast<E{module_prefix}ProcedureTag>(0);\"\n        );\n        writeln!(output);\n        write!(output, \"    TVariant<\");\n        {\n            let mut first = true;\n            for procedure in iter_procedures(module, visibility) {\n                let procedure_pascal = format!(\"{module_prefix}{}\", procedure.name.deref().to_case(Case::Pascal));\n                if !first {\n                    write!(output, \", \");\n                } else {\n                    first = false;\n                }\n                write!(output, \"F{procedure_pascal}Args\");\n            }\n        }\n        writeln!(output, \"> Data;\");\n        writeln!(output);\n        writeln!(output, \"    // Optional metadata\");\n        writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category = \\\"SpacetimeDB\\\")\");\n        writeln!(output, \"    FString ProcedureName;\");\n        writeln!(output, \"    uint32 ProcedureId = 0;\");\n        writeln!(output, \"    uint32 RequestId = 0;\");\n        writeln!(output);\n\n        // Static constructors, Is*, GetAs*\n        for procedure in iter_procedures(module, visibility) {\n            let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n            let procedure_pascal = format!(\"{module_prefix}{procedure_name}\");\n            writeln!(\n                output,\n                \"    static F{module_prefix}Procedure {procedure_name}(const F{procedure_pascal}Args& Value)\"\n            );\n            writeln!(output, \"    {{\");\n            writeln!(output, \"        F{module_prefix}Procedure Out;\");\n            writeln!(\n                output,\n                \"        Out.Tag = E{module_prefix}ProcedureTag::{procedure_name};\"\n            );\n            writeln!(output, \"        Out.Data.Set<F{procedure_pascal}Args>(Value);\");\n            writeln!(\n                output,\n                \"        Out.ProcedureName = TEXT(\\\"{}\\\");\",\n                procedure.name.deref()\n            );\n            writeln!(output, \"        return Out;\");\n            writeln!(output, \"    }}\");\n            writeln!(output);\n            writeln!(\n                output,\n                \"    FORCEINLINE bool Is{procedure_name}() const {{ return Tag == E{module_prefix}ProcedureTag::{procedure_name}; }}\"\n            );\n            writeln!(\n                output,\n                \"    FORCEINLINE F{procedure_pascal}Args GetAs{procedure_name}() const\"\n            );\n            writeln!(output, \"    {{\");\n            writeln!(\n                output,\n                \"        ensureMsgf(Is{procedure_name}(), TEXT(\\\"Procedure does not hold {procedure_name}!\\\"));\"\n            );\n            writeln!(output, \"        return Data.Get<F{procedure_pascal}Args>();\");\n            writeln!(output, \"    }}\");\n            writeln!(output);\n        }\n        writeln!(\n            output,\n            \"    FORCEINLINE bool operator==(const F{module_prefix}Procedure& Other) const\"\n        );\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        if (Tag != Other.Tag || ProcedureId != Other.ProcedureId || RequestId != Other.RequestId || ProcedureName != Other.ProcedureName) return false;\");\n        writeln!(output, \"        switch (Tag)\");\n        writeln!(output, \"        {{\");\n        for procedure in iter_procedures(module, visibility) {\n            let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n            writeln!(output, \"        case E{module_prefix}ProcedureTag::{procedure_name}:\");\n            writeln!(\n                output,\n                \"            return GetAs{procedure_name}() == Other.GetAs{procedure_name}();\"\n            );\n        }\n        writeln!(output, \"        default: return false;\");\n        writeln!(output, \"        }}\");\n        writeln!(output, \"    }}\");\n        writeln!(\n            output,\n            \"    FORCEINLINE bool operator!=(const F{module_prefix}Procedure& Other) const {{ return !(*this == Other); }}\"\n        );\n        writeln!(output, \"}};\");\n        writeln!(output);\n\n        // BPLib for F{module_prefix}Procedure\n        writeln!(output, \"UCLASS()\");\n        writeln!(\n            output,\n            \"class {api_macro} U{module_prefix}ProcedureBpLib : public UBlueprintFunctionLibrary\"\n        );\n        writeln!(output, \"{{\");\n        writeln!(output, \"    GENERATED_BODY()\");\n        writeln!(output);\n        writeln!(output, \"private:\");\n\n        for procedure in iter_procedures(module, visibility) {\n            let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n            let procedure_pascal = format!(\"{module_prefix}{procedure_name}\");\n            // ---- Static constructors ----\n            writeln!(output);\n            writeln!(\n                output,\n                \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|Procedure\\\")\"\n            );\n            writeln!(\n                output,\n                \"    static F{module_prefix}Procedure {procedure_name}(const F{procedure_pascal}Args& Value) {{\"\n            );\n            writeln!(\n                output,\n                \"        return F{module_prefix}Procedure::{procedure_name}(Value);\"\n            );\n            writeln!(output, \"    }}\");\n            writeln!(output);\n\n            // Is*\n            writeln!(\n                output,\n                \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|Procedure\\\")\"\n            );\n            writeln!(\n                output,\n                \"    static bool Is{procedure_name}(const F{module_prefix}Procedure& Procedure) {{ return Procedure.Is{procedure_name}(); }}\"\n            );\n            writeln!(output);\n\n            // GetAs*\n            writeln!(\n                output,\n                \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|Procedure\\\")\"\n            );\n            writeln!(\n                output,\n                \"    static F{procedure_pascal}Args GetAs{procedure_name}(const F{module_prefix}Procedure& Procedure) {{\"\n            );\n            writeln!(output, \"        return Procedure.GetAs{procedure_name}();\");\n            writeln!(output, \"    }}\");\n        }\n\n        writeln!(output, \"}};\");\n        writeln!(output);\n    } else {\n        writeln!(output, \"// No procedures defined in this module.\");\n    }\n    // FProcedureEvent: metadata about a procedure invocation, with typed args\n    writeln!(output, \"/** Metadata describing a procedure run. */\");\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{module_name}ProcedureEvent\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tGENERATED_BODY()\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Timestamp for when the procedure executed */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBTimestamp Timestamp;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Result status of the procedure */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBProcedureStatus Status;\");\n    writeln!(output);\n\n    writeln!(output, \"\\t/** Identity that initiated the call */\");\n    writeln!(\n        output,\n        \"\\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"\\tFSpacetimeDBTimeDuration TotalHostExecutionDuration;\");\n    writeln!(output);\n\n    writeln!(output, \"\\tF{module_name}ProcedureEvent() {{\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"\\tF{module_name}ProcedureEvent(FProcedureEvent Event) {{\");\n    writeln!(output, \"\\t\\tTimestamp = Event.Timestamp;\");\n    writeln!(\n        output,\n        \"\\t\\tStatus = FSpacetimeDBProcedureStatus::FromStatus(Event.Status);\"\n    );\n    writeln!(\n        output,\n        \"\\t\\tTotalHostExecutionDuration = Event.TotalHostExecutionDuration;\"\n    );\n    writeln!(output, \"\\t}}\");\n\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator==(const F{module_name}ProcedureEvent& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(\n        output,\n        \"\\t\\treturn Status == Other.Status && Timestamp == Other.Timestamp &&\"\n    );\n    writeln!(\n        output,\n        \"\\t\\t\\tTotalHostExecutionDuration == Other.TotalHostExecutionDuration;\"\n    );\n    writeln!(output, \"\\t}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"\\tFORCEINLINE bool operator!=(const F{module_name}ProcedureEvent& Other) const\"\n    );\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\treturn !(*this == Other);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_remote_tables_class(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    api_macro: &str,\n) {\n    writeln!(output, \"// RemoteTables class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}RemoteTables : public UObject\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output, \"    void Initialize();\");\n    writeln!(output);\n\n    // Generate table handle properties\n    for (_, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n        writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n        writeln!(\n            output,\n            \"    U{module_prefix}{}Table* {};\",\n            accessor_name.deref().to_case(Case::Pascal),\n            accessor_name.deref().to_case(Case::Pascal)\n        );\n        writeln!(output);\n    }\n\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_remote_reducers_class(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    api_macro: &str,\n    module_name: &str,\n) {\n    writeln!(output, \"// RemoteReducers class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}RemoteReducers : public UObject\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output);\n\n    // Generate reducer events and call methods\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n        let reducer_pascal = format!(\"{module_prefix}{reducer_name}\");\n\n        // Generate delegate for reducer event\n        let param_count = reducer.params_for_generate.elements.len() + 1; // +1 for context\n        let use_args_struct = param_count > 9; // Unreal only supports up to 9 params in delegates\n\n        let mut non_blueprintable_types_for_delegate = Vec::new();\n        let mut non_blueprintable_types_for_function = Vec::new();\n\n        if use_args_struct {\n            // For more than 9 params, use a struct to wrap the arguments\n            writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(\");\n            writeln!(output, \"        F{reducer_pascal}Handler,\");\n            writeln!(output, \"        const F{module_prefix}ReducerEventContext&, Context,\");\n            writeln!(output, \"        const F{reducer_pascal}Args&, Args\");\n            writeln!(output, \"    );\");\n\n            // For delegates using args struct, check the actual delegate parameters:\n            // 1. F{module_prefix}ReducerEventContext (always blueprintable)\n            // 2. F{Reducer}Args struct (always blueprintable as a USTRUCT)\n            // So delegates with args struct are always blueprintable\n\n            // But functions still need to check individual parameters\n            for (_, param_type) in &reducer.params_for_generate.elements {\n                if !is_type_blueprintable_for_delegates(module, param_type) {\n                    let type_str = cpp_ty_fmt_with_module(module_prefix, module, param_type, module_name).to_string();\n                    non_blueprintable_types_for_function.push(type_str);\n                }\n            }\n        } else {\n            // Use the original approach for 9 or fewer params\n            let delegate_macro = match param_count {\n                1 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam\",\n                2 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams\",\n                3 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams\",\n                4 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams\",\n                5 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams\",\n                6 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams\",\n                7 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_SevenParams\",\n                8 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_EightParams\",\n                9 => \"DECLARE_DYNAMIC_MULTICAST_DELEGATE_NineParams\",\n                _ => unreachable!(\"Should use args struct for >9 params\"),\n            };\n\n            write!(\n                output,\n                \"    {delegate_macro}(\\n        F{reducer_pascal}Handler,\\n        const F{module_prefix}ReducerEventContext&, Context\"\n            );\n\n            for (param_name, param_type) in &reducer.params_for_generate.elements {\n                // Use Blueprint-compatible types for delegates\n                let type_str =\n                    cpp_ty_fmt_blueprint_compatible(module_prefix, module, param_type, module_name).to_string();\n\n                // Collect non-blueprintable types for both delegate and function\n                if !is_type_blueprintable_for_delegates(module, param_type) {\n                    non_blueprintable_types_for_delegate.push(type_str.clone());\n                    non_blueprintable_types_for_function.push(type_str.clone());\n                }\n\n                if should_pass_by_value_in_delegate(module, param_type) {\n                    // Only true primitives are passed by value in delegates\n                    write!(\n                        output,\n                        \",\\n        {type_str}, {}\",\n                        param_name.deref().to_case(Case::Pascal)\n                    );\n                } else {\n                    // Complex types (including FSpacetimeDB structs) use const references\n                    write!(\n                        output,\n                        \",\\n        const {type_str}&, {}\",\n                        param_name.deref().to_case(Case::Pascal)\n                    );\n                }\n            }\n            writeln!(output, \"\\n    );\");\n        }\n\n        // Generate delegate property\n        if non_blueprintable_types_for_delegate.is_empty() {\n            writeln!(output, \"    UPROPERTY(BlueprintAssignable, Category=\\\"SpacetimeDB\\\")\");\n        } else {\n            // Generate specific message about which types are not Blueprint-compatible\n            let types_str = non_blueprintable_types_for_delegate.join(\", \");\n            writeln!(\n                output,\n                \"    // NOTE: Not exposed to Blueprint because {types_str} types are not Blueprint-compatible\"\n            );\n        }\n        writeln!(output, \"    F{reducer_pascal}Handler On{reducer_name};\");\n        writeln!(output);\n\n        // Generate call method\n        if non_blueprintable_types_for_function.is_empty() {\n            write!(\n                output,\n                \"    UFUNCTION(BlueprintCallable, Category=\\\"SpacetimeDB\\\")\\n    void {reducer_name}(\"\n            );\n        } else {\n            // Generate specific message about which types are not Blueprint-compatible\n            let types_str = non_blueprintable_types_for_function.join(\", \");\n            write!(\n                output,\n                \"    // NOTE: Not exposed to Blueprint because {types_str} types are not Blueprint-compatible\\n    void {reducer_name}(\"\n            );\n        }\n\n        let mut first = true;\n        for (param_name, param_type) in &reducer.params_for_generate.elements {\n            if !first {\n                write!(output, \", \");\n            }\n            first = false;\n\n            // For UFUNCTION parameters, use Blueprint-compatible types\n            let type_str = if non_blueprintable_types_for_function.is_empty() {\n                cpp_ty_fmt_blueprint_compatible(module_prefix, module, param_type, module_name).to_string()\n            } else {\n                cpp_ty_fmt_with_module(module_prefix, module, param_type, module_name).to_string()\n            };\n\n            if should_pass_by_value_in_delegate(module, param_type) {\n                // Primitives use const by-value in functions\n                write!(\n                    output,\n                    \"const {} {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            } else {\n                // Complex types (including FSpacetimeDB structs) use const references\n                write!(\n                    output,\n                    \"const {}& {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            }\n        }\n        writeln!(output, \");\");\n        writeln!(output);\n\n        // Generate invoke method (UObject version - kept for backwards compatibility)\n        write!(\n            output,\n            \"    bool Invoke{reducer_name}(const F{module_prefix}ReducerEventContext& Context, const U{reducer_pascal}Reducer* Args);\"\n        );\n        writeln!(output);\n\n        // Generate invoke method (FArgs version - zero allocation, used internally)\n        write!(\n            output,\n            \"    bool Invoke{reducer_name}WithArgs(const F{module_prefix}ReducerEventContext& Context, const F{reducer_pascal}Args& Args);\"\n        );\n        writeln!(output);\n        writeln!(output);\n    }\n\n    // Internal error handling\n    writeln!(output, \"    // Internal error handling\");\n    writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(F{module_prefix}InternalOnUnhandledReducerError, const F{module_prefix}ReducerEventContext&, Context, const FString&, Error);\");\n    writeln!(\n        output,\n        \"    F{module_prefix}InternalOnUnhandledReducerError InternalOnUnhandledReducerError;\"\n    );\n    writeln!(output);\n\n    writeln!(output, \"private:\");\n    writeln!(output);\n    writeln!(output, \"    friend U{module_prefix}DbConnection;\");\n    writeln!(output);\n    writeln!(output, \"    UPROPERTY()\");\n    writeln!(output, \"    class U{module_prefix}DbConnection* Conn;\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_remote_procedures_class(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    api_macro: &str,\n    module_name: &str,\n) {\n    for procedure in iter_procedures(module, visibility) {\n        let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n        let blueprintable_type_for_return =\n            is_type_blueprintable_for_delegates(module, &procedure.return_type_for_generate);\n\n        // In generate_remote_procedures_class, before the existing event delegate generation:\n        let return_type_str =\n            cpp_ty_fmt_with_module(module_prefix, module, &procedure.return_type_for_generate, module_name).to_string();\n        let return_type_ref = if return_type_str.starts_with('F') && return_type_str != \"FSpacetimeDBUnit\" {\n            format!(\"const {}&\", return_type_str)\n        } else {\n            return_type_str.clone()\n        };\n\n        if !blueprintable_type_for_return {\n            writeln!(\n                output,\n                \"// NOTE: Procedure {procedure_name} has non-Blueprint-compatible return type: {return_type_str}\"\n            );\n            writeln!(\n                output,\n                \"DECLARE_DELEGATE_ThreeParams(F{module_prefix}On{procedure_name}Complete,\"\n            );\n            writeln!(output, \"    const F{module_prefix}ProcedureEventContext&, /*Context*/\");\n            writeln!(output, \"    {return_type_ref}, /*Result,*/\");\n            writeln!(output, \"    bool /*bSuccess*/);\");\n            writeln!(output);\n        } else {\n            writeln!(\n                output,\n                \"DECLARE_DYNAMIC_DELEGATE_ThreeParams(F{module_prefix}On{procedure_name}Complete,\"\n            );\n            writeln!(output, \"    const F{module_prefix}ProcedureEventContext&, Context,\");\n            writeln!(output, \"    {}, Result,\", return_type_ref);\n            writeln!(output, \"    bool, bSuccess);\");\n            writeln!(output);\n        }\n    }\n\n    writeln!(output, \"// RemoteProcedures class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}RemoteProcedures : public UObject\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output);\n\n    // Generate procedure events and call methods\n    for procedure in iter_procedures(module, visibility) {\n        let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n\n        let mut non_blueprintable_types_for_function = Vec::new();\n\n        for (_, param_type) in &procedure.params_for_generate.elements {\n            if !is_type_blueprintable_for_delegates(module, param_type) {\n                let type_str = cpp_ty_fmt_with_module(module_prefix, module, param_type, module_name).to_string();\n                non_blueprintable_types_for_function.push(type_str);\n            }\n        }\n        let blueprintable_type_for_return =\n            is_type_blueprintable_for_delegates(module, &procedure.return_type_for_generate);\n\n        // Generate call method\n        if non_blueprintable_types_for_function.is_empty() && blueprintable_type_for_return {\n            write!(\n                output,\n                \"    UFUNCTION(BlueprintCallable, Category=\\\"SpacetimeDB\\\")\\n    void {procedure_name}(\"\n            );\n        } else {\n            // Generate specific message about which types are not Blueprint-compatible\n            let types_str = non_blueprintable_types_for_function.join(\", \");\n            write!(\n                output,\n                \"    // NOTE: Not exposed to Blueprint because return type or {types_str} types are not Blueprint-compatible\\n    void {procedure_name}(\"\n            );\n        }\n\n        let mut first = true;\n        for (param_name, param_type) in &procedure.params_for_generate.elements {\n            if !first {\n                write!(output, \", \");\n            }\n            first = false;\n\n            // For UFUNCTION parameters, use Blueprint-compatible types\n            let type_str = if non_blueprintable_types_for_function.is_empty() {\n                cpp_ty_fmt_blueprint_compatible(module_prefix, module, param_type, module_name).to_string()\n            } else {\n                cpp_ty_fmt_with_module(module_prefix, module, param_type, module_name).to_string()\n            };\n\n            if should_pass_by_value_in_delegate(module, param_type) {\n                // Primitives use const by-value in functions\n                write!(\n                    output,\n                    \"const {} {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            } else {\n                // Complex types (including FSpacetimeDB structs) use const references\n                write!(\n                    output,\n                    \"const {}& {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            }\n        }\n        if !first {\n            write!(output, \", \");\n        }\n        writeln!(output, \"F{module_prefix}On{}Complete Callback);\", procedure_name);\n        writeln!(output);\n\n        writeln!(output);\n    }\n\n    // Internal error handling\n    writeln!(output, \"    // Internal error handling\");\n    writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FInternalOnUnhandledProcedureError, const F{module_prefix}ProcedureEventContext&, Context, const FString&, Error);\");\n    writeln!(\n        output,\n        \"    FInternalOnUnhandledProcedureError InternalOnUnhandledProcedureError;\"\n    );\n    writeln!(output);\n\n    writeln!(output, \"private:\");\n    writeln!(output);\n    writeln!(output, \"    friend U{module_prefix}DbConnection;\");\n    writeln!(output);\n    writeln!(output, \"    UPROPERTY()\");\n    writeln!(output, \"    class U{module_prefix}DbConnection* Conn;\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_subscription_builder_class(output: &mut UnrealCppAutogen, module_prefix: &str, api_macro: &str) {\n    writeln!(output, \"// SubscriptionBuilder class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}SubscriptionBuilder : public USubscriptionBuilderBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}SubscriptionBuilder* OnApplied(F{module_prefix}OnSubscriptionApplied Callback);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}SubscriptionBuilder* OnError(F{module_prefix}OnSubscriptionError Callback);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}SubscriptionHandle* Subscribe(const TArray<FString>& SQL);\"\n    );\n    writeln!(output);\n    writeln!(\n        output,\n        \"    /** Convenience for subscribing to all rows from all tables */\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}SubscriptionHandle* SubscribeToAllTables();\"\n    );\n    writeln!(output);\n    writeln!(output);\n    writeln!(output, \"    friend class U{module_prefix}DbConnection;\");\n    writeln!(output, \"    friend class UDbConnectionBase;\");\n    writeln!(output);\n    writeln!(output, \"protected:\");\n    writeln!(output, \"    UPROPERTY()\");\n    writeln!(output, \"    class U{module_prefix}DbConnection* Conn;\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Delegates stored so Subscribe() can bind forwarding callbacks\"\n    );\n    writeln!(\n        output,\n        \"    F{module_prefix}OnSubscriptionApplied OnAppliedDelegateInternal;\"\n    );\n    writeln!(\n        output,\n        \"    F{module_prefix}OnSubscriptionError OnErrorDelegateInternal;\"\n    );\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_subscription_handle_class(output: &mut UnrealCppAutogen, module_prefix: &str, api_macro: &str) {\n    writeln!(output, \"// SubscriptionHandle class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}SubscriptionHandle : public USubscriptionHandleBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(output);\n    writeln!(output, \"    U{module_prefix}SubscriptionHandle() {{}};\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    explicit U{module_prefix}SubscriptionHandle(U{module_prefix}DbConnection* InConn);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    friend class U{module_prefix}SubscriptionBuilder;\");\n    writeln!(output);\n    writeln!(output, \"private:\");\n    writeln!(output, \"    UPROPERTY()\");\n    writeln!(output, \"    class U{module_prefix}DbConnection* Conn;\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Delegates that expose subscription events with connection aware contexts\"\n    );\n    writeln!(output, \"    F{module_prefix}OnSubscriptionApplied OnAppliedDelegate;\");\n    writeln!(output, \"    F{module_prefix}OnSubscriptionError OnErrorDelegate;\");\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(\n        output,\n        \"    void ForwardOnApplied(const FSubscriptionEventContextBase& BaseCtx);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(output, \"    void ForwardOnError(const FErrorContextBase& BaseCtx);\");\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_db_connection_builder_class(output: &mut UnrealCppAutogen, module_prefix: &str, api_macro: &str) {\n    writeln!(output, \"/*\");\n    writeln!(output, \"    @Note: Child class of UDbConnectionBuilderBase.\");\n    writeln!(output, \"*/\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}DbConnectionBuilder : public UDbConnectionBuilderBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output, \"public:\");\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* WithUri(const FString& InUri);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* WithDatabaseName(const FString& InName);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* WithToken(const FString& InToken);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* WithCompression(const ESpacetimeDBCompression& InCompression);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* OnConnect(F{module_prefix}OnConnectDelegate Callback);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* OnConnectError(FOnConnectErrorDelegate Callback);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}DbConnectionBuilder* OnDisconnect(F{module_prefix}OnDisconnectDelegate Callback);\"\n    );\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\")\");\n    writeln!(output, \"    U{module_prefix}DbConnection* Build();\");\n    writeln!(output);\n    writeln!(output, \"private:\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Stored delegates which will be forwarded when the connection events occur.\"\n    );\n    writeln!(\n        output,\n        \"    F{module_prefix}OnConnectDelegate OnConnectDelegateInternal;\"\n    );\n    writeln!(\n        output,\n        \"    F{module_prefix}OnDisconnectDelegate OnDisconnectDelegateInternal;\"\n    );\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_db_connection_class(\n    output: &mut UnrealCppAutogen,\n    module_prefix: &str,\n    module_name: &str,\n    _module: &ModuleDef,\n    api_macro: &str,\n) {\n    writeln!(output, \"// Main DbConnection class\");\n    writeln!(output, \"UCLASS(BlueprintType)\");\n    writeln!(\n        output,\n        \"class {api_macro} U{module_prefix}DbConnection : public UDbConnectionBase\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    GENERATED_BODY()\");\n    writeln!(output);\n    writeln!(output, \"public:\");\n    writeln!(\n        output,\n        \"    explicit U{module_prefix}DbConnection(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());\"\n    );\n    writeln!(output);\n    writeln!(output);\n    writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(output, \"    U{module_prefix}RemoteTables* Db;\");\n    writeln!(output);\n    writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(output, \"    U{module_prefix}RemoteReducers* Reducers;\");\n    writeln!(output);\n    writeln!(output, \"    UPROPERTY(BlueprintReadOnly, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(output, \"    U{module_prefix}RemoteProcedures* Procedures;\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Delegates that allow users to bind with the concrete connection type.\"\n    );\n    writeln!(output, \"    F{module_prefix}OnConnectDelegate OnConnectDelegate;\");\n    writeln!(output, \"    F{module_prefix}OnDisconnectDelegate OnDisconnectDelegate;\");\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION(BlueprintCallable, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    U{module_prefix}SubscriptionBuilder* SubscriptionBuilder();\"\n    );\n    writeln!(output);\n    writeln!(output, \"    /** Static entry point for constructing a connection. */\");\n    writeln!(\n        output,\n        \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB\\\", DisplayName = \\\"SpacetimeDB {module_name} Builder\\\")\"\n    );\n    writeln!(output, \"    static U{module_prefix}DbConnectionBuilder* Builder();\");\n    writeln!(output);\n    writeln!(output, \"    // Error handling\");\n    writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(F{module_prefix}OnUnhandledReducerError, const F{module_prefix}ReducerEventContext&, Context, const FString&, Error);\");\n    writeln!(output, \"    UPROPERTY(BlueprintAssignable, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    F{module_prefix}OnUnhandledReducerError OnUnhandledReducerError;\"\n    );\n    writeln!(output);\n    writeln!(output, \"    // Error handling\");\n    writeln!(output, \"    DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(F{module_prefix}OnUnhandledProcedureError, const F{module_prefix}ProcedureEventContext&, Context, const FString&, Error);\");\n    writeln!(output, \"    UPROPERTY(BlueprintAssignable, Category=\\\"SpacetimeDB\\\")\");\n    writeln!(\n        output,\n        \"    F{module_prefix}OnUnhandledProcedureError OnUnhandledProcedureError;\"\n    );\n    writeln!(output);\n    writeln!(output);\n    writeln!(output, \"protected:\");\n    writeln!(output);\n    writeln!(output, \"    // Hook up error handling to reducers and procedures\");\n    writeln!(output, \"    virtual void PostInitProperties() override;\");\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(output, \"    void ForwardOnConnect(UDbConnectionBase* BaseConnection, FSpacetimeDBIdentity InIdentity, const FString& InToken);\");\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(\n        output,\n        \"    void ForwardOnDisconnect(UDbConnectionBase* BaseConnection, const FString& Error);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(\n        output,\n        \"    void OnUnhandledReducerErrorHandler(const F{module_prefix}ReducerEventContext& Context, const FString& Error);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    UFUNCTION()\");\n    writeln!(\n        output,\n        \"    void OnUnhandledProcedureErrorHandler(const F{module_prefix}ProcedureEventContext& Context, const FString& Error);\"\n    );\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Override the DbConnectionBase methods to handle updates and events\"\n    );\n    writeln!(\n        output,\n        \"    virtual void DbUpdate(const FDatabaseUpdateType& Update, const FSpacetimeDBEvent& Event) override;\"\n    );\n    writeln!(output, \"    \");\n    writeln!(\n        output,\n        \"    // Override the reducer event handler to dispatch events to the appropriate reducers\"\n    );\n    writeln!(\n        output,\n        \"    virtual void ReducerEvent(const FReducerEvent& Event) override;\"\n    );\n    writeln!(output, \"    \");\n    writeln!(output, \"    // Override the reducer event failed handler\");\n    writeln!(\n        output,\n        \"    virtual void ReducerEventFailed(const FReducerEvent& Event, const FString ErrorMessage) override;\"\n    );\n    writeln!(output, \"    // Override the procedure event failed handler\");\n    writeln!(\n        output,\n        \"    virtual void ProcedureEventFailed(const FProcedureEvent& Event, const FString ErrorMessage) override;\"\n    );\n    writeln!(output);\n    writeln!(output, \"    friend class U{module_prefix}SubscriptionBuilder;\");\n    writeln!(output, \"    friend class U{module_prefix}DbConnectionBuilder;\");\n    writeln!(output, \"    friend class U{module_prefix}RemoteReducers;\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Internal reducer correlation helpers (request_id -> typed reducer)\"\n    );\n    writeln!(\n        output,\n        \"    void RegisterPendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer Reducer);\"\n    );\n    writeln!(\n        output,\n        \"    bool TryGetPendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer& OutReducer) const;\"\n    );\n    writeln!(\n        output,\n        \"    bool TryTakePendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer& OutReducer);\"\n    );\n    writeln!(output);\n    writeln!(output, \"private:\");\n    writeln!(\n        output,\n        \"    TMap<uint32, F{module_prefix}Reducer> PendingTypedReducers;\"\n    );\n    writeln!(output, \"}};\");\n    writeln!(output);\n}\n\nfn generate_client_implementation(\n    output: &mut UnrealCppAutogen,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    module_prefix: &str,\n    module_name: &str,\n) {\n    // U{module_prefix}DbConnection constructor\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnection::U{module_prefix}DbConnection(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\tDb = ObjectInitializer.CreateDefaultSubobject<U{module_prefix}RemoteTables>(this, TEXT(\\\"RemoteTables\\\"));\"\n    );\n    writeln!(output, \"\\tDb->Initialize();\");\n    writeln!(output, \"\\t\");\n    writeln!(\n        output,\n        \"\\tReducers = ObjectInitializer.CreateDefaultSubobject<U{module_prefix}RemoteReducers>(this, TEXT(\\\"RemoteReducers\\\"));\"\n    );\n    writeln!(output, \"\\tReducers->Conn = this;\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\tProcedures = ObjectInitializer.CreateDefaultSubobject<U{module_prefix}RemoteProcedures>(this, TEXT(\\\"RemoteProcedures\\\"));\"\n    );\n    writeln!(output, \"\\tProcedures->Conn = this;\");\n    writeln!(output);\n\n    for (name, accessor_name, product_type_ref) in iter_table_names_and_types(module, visibility) {\n        let struct_name = type_ref_name(module_prefix, module, product_type_ref);\n        let accessor = accessor_name.deref();\n        writeln!(\n            output,\n            \"\\tRegisterTable<F{}Type, U{module_prefix}{}Table, F{module_prefix}EventContext>(TEXT(\\\"{}\\\"), Db->{});\",\n            struct_name,\n            accessor.to_case(Case::Pascal),\n            name.deref(),\n            accessor.to_case(Case::Pascal)\n        );\n    }\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // F{module_prefix}ContextBase constructor\n    writeln!(\n        output,\n        \"F{module_prefix}ContextBase::F{module_prefix}ContextBase(U{module_prefix}DbConnection* InConn)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tDb = InConn->Db;\");\n    writeln!(output, \"\\tReducers = InConn->Reducers;\");\n    writeln!(output, \"\\tProcedures = InConn->Procedures;\");\n    writeln!(output, \"\\tConn = InConn;\");\n    writeln!(output, \"}}\");\n\n    // F{module_prefix}ContextBase methods\n    writeln!(output, \"bool F{module_prefix}ContextBase::IsActive() const\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn Conn->IsActive();\");\n    writeln!(output, \"}}\");\n    writeln!(output, \"void F{module_prefix}ContextBase::Disconnect()\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tConn->Disconnect();\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionBuilder* F{module_prefix}ContextBase::SubscriptionBuilder()\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn Conn->SubscriptionBuilder();\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"bool F{module_prefix}ContextBase::TryGetIdentity(FSpacetimeDBIdentity& OutIdentity) const\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn Conn->TryGetIdentity(OutIdentity);\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"FSpacetimeDBConnectionId F{module_prefix}ContextBase::GetConnectionId() const\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn Conn->GetConnectionId();\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // U{module_prefix}RemoteTables Initialize method\n    writeln!(output, \"void U{module_prefix}RemoteTables::Initialize()\");\n    writeln!(output, \"{{\");\n    writeln!(output);\n    writeln!(output, \"\\t/** Creating tables */\");\n    for (_, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n        writeln!(\n            output,\n            \"\\t{} = NewObject<U{module_prefix}{}Table>(this);\",\n            accessor_name.deref().to_case(Case::Pascal),\n            accessor_name.deref().to_case(Case::Pascal)\n        );\n    }\n    writeln!(output, \"\\t/**/\");\n    writeln!(output);\n    writeln!(output, \"\\t/** Initialization */\");\n    for (_, accessor_name, _) in iter_table_names_and_types(module, visibility) {\n        writeln!(\n            output,\n            \"\\t{}->PostInitialize();\",\n            accessor_name.deref().to_case(Case::Pascal)\n        );\n    }\n    writeln!(output, \"\\t/**/\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    generate_remote_reducer_calls(output, module, visibility, module_prefix, module_name);\n    generate_remote_procedure_calls(output, module, visibility, module_prefix, module_name);\n\n    // Hook up error handling\n    writeln!(output, \"void U{module_prefix}DbConnection::PostInitProperties()\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"    Super::PostInitProperties();\");\n    writeln!(output, \"    \");\n    writeln!(\n        output,\n        \"    // Connect OnUnhandledReducerError to Reducers.InternalOnUnhandledReducerError\"\n    );\n    writeln!(output, \"    if (Reducers)\");\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        Reducers->InternalOnUnhandledReducerError.AddDynamic(this, &U{module_prefix}DbConnection::OnUnhandledReducerErrorHandler);\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    // Connect OnUnhandledProcedureError to Procedures.InternalOnUnhandledProcedureError\"\n    );\n    writeln!(output, \"    if (Procedures)\");\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        Procedures->InternalOnUnhandledProcedureError.AddDynamic(this, &U{module_prefix}DbConnection::OnUnhandledProcedureErrorHandler);\");\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Error handler method for reducers\n    writeln!(output, \"UFUNCTION()\");\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::OnUnhandledReducerErrorHandler(const F{module_prefix}ReducerEventContext& Context, const FString& Error)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    if (OnUnhandledReducerError.IsBound())\");\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        OnUnhandledReducerError.Broadcast(Context, Error);\");\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Error handler method for procedures\n    writeln!(output, \"UFUNCTION()\");\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::OnUnhandledProcedureErrorHandler(const F{module_prefix}ProcedureEventContext& Context, const FString& Error)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    if (OnUnhandledProcedureError.IsBound())\");\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        OnUnhandledProcedureError.Broadcast(Context, Error);\");\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::RegisterPendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer Reducer)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    Reducer.RequestId = RequestId;\");\n    writeln!(output, \"    PendingTypedReducers.Add(RequestId, MoveTemp(Reducer));\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"bool U{module_prefix}DbConnection::TryGetPendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer& OutReducer) const\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"    if (const F{module_prefix}Reducer* Found = PendingTypedReducers.Find(RequestId))\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        OutReducer = *Found;\");\n    writeln!(output, \"        return true;\");\n    writeln!(output, \"    }}\");\n    writeln!(output, \"    return false;\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"bool U{module_prefix}DbConnection::TryTakePendingTypedReducer(uint32 RequestId, F{module_prefix}Reducer& OutReducer)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"    if (F{module_prefix}Reducer* Found = PendingTypedReducers.Find(RequestId))\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        OutReducer = *Found;\");\n    writeln!(output, \"        PendingTypedReducers.Remove(RequestId);\");\n    writeln!(output, \"        return true;\");\n    writeln!(output, \"    }}\");\n    writeln!(output, \"    return false;\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // ReducerEvent method implementation\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::ReducerEvent(const FReducerEvent& Event)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    if (!Reducers) {{ return; }}\");\n    writeln!(output);\n\n    writeln!(output, \"    F{module_prefix}Reducer DecodedReducer;\");\n    writeln!(\n        output,\n        \"    if (!TryTakePendingTypedReducer(Event.RequestId, DecodedReducer))\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(\n        output,\n        \"        const FString ErrorMessage = FString::Printf(TEXT(\\\"Reducer result for unknown request_id %u\\\"), Event.RequestId);\"\n    );\n    writeln!(output, \"        HandleProtocolViolation(ErrorMessage);\");\n    writeln!(output, \"        return;\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    let module_name_pascal = module_name.to_case(Case::Pascal);\n    // Build the {}ReducerEvent object\n    writeln!(output, \"    F{module_name_pascal}ReducerEvent ReducerEvent;\");\n    writeln!(\n        output,\n        \"    ReducerEvent.CallerConnectionId = Event.CallerConnectionId;\"\n    );\n    writeln!(output, \"    ReducerEvent.CallerIdentity     = Event.CallerIdentity;\");\n    writeln!(output, \"    ReducerEvent.EnergyConsumed     = Event.EnergyConsumed;\");\n    writeln!(output, \"    ReducerEvent.Status             = Event.Status;\");\n    writeln!(output, \"    ReducerEvent.Timestamp          = Event.Timestamp;\");\n    writeln!(output, \"    ReducerEvent.Reducer            = DecodedReducer;\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    F{module_prefix}ReducerEventContext Context(this, ReducerEvent);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    // Dispatch by typed reducer metadata\");\n    writeln!(\n        output,\n        \"    const FString& ReducerName = ReducerEvent.Reducer.ReducerName;\"\n    );\n    writeln!(output);\n\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref();\n        let reducer_method = reducer_name.to_case(Case::Pascal);\n        let reducer_pascal = format!(\"{module_prefix}{reducer_method}\");\n        writeln!(output, \"    if (ReducerName == TEXT(\\\"{reducer_name}\\\"))\");\n        writeln!(output, \"    {{\");\n        writeln!(\n            output,\n            \"        F{reducer_pascal}Args Args = ReducerEvent.Reducer.GetAs{reducer_method}();\"\n        );\n        // FIX: Pass FArgs directly to InvokeWithArgs instead of creating UReducer UObject.\n        // UObject creation/destruction cannot keep up at 30Hz tick rate, causing memory leak.\n        // Stack-allocated FArgs struct has zero allocation overhead.\n        writeln!(\n            output,\n            \"        Reducers->Invoke{reducer_method}WithArgs(Context, Args);\"\n        );\n        writeln!(output, \"        return;\");\n        writeln!(output, \"    }}\");\n    }\n    writeln!(output);\n    writeln!(\n        output,\n        \"    UE_LOG(LogTemp, Warning, TEXT(\\\"Unknown reducer: %s\\\"), *ReducerName);\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // ReducerEventFailed method implementation\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::ReducerEventFailed(const FReducerEvent& Event, const FString ErrorMessage)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    if (!Reducers) {{ return; }}\");\n    writeln!(output);\n\n    // Build the {}ReducerEvent object\n    writeln!(output, \"    F{module_name_pascal}ReducerEvent ReducerEvent;\");\n    writeln!(\n        output,\n        \"    ReducerEvent.CallerConnectionId = Event.CallerConnectionId;\"\n    );\n    writeln!(output, \"    ReducerEvent.CallerIdentity     = Event.CallerIdentity;\");\n    writeln!(output, \"    ReducerEvent.EnergyConsumed     = Event.EnergyConsumed;\");\n    writeln!(output, \"    ReducerEvent.Status             = Event.Status;\");\n    writeln!(output, \"    ReducerEvent.Timestamp          = Event.Timestamp;\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    F{module_prefix}ReducerEventContext Context(this, ReducerEvent);\"\n    );\n    writeln!(output);\n    writeln!(output, \"    if (Reducers->InternalOnUnhandledReducerError.IsBound())\");\n    writeln!(output, \"    {{\");\n    writeln!(\n        output,\n        \"        Reducers->InternalOnUnhandledReducerError.Broadcast(Context, ErrorMessage);\"\n    );\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // ProcedureEventFailed method implementation\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::ProcedureEventFailed(const FProcedureEvent& Event, const FString ErrorMessage)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"    if (!Procedures) {{ return; }}\");\n    writeln!(output);\n\n    // Build the {}ProcedureEvent object\n    writeln!(output, \"    F{module_name_pascal}ProcedureEvent ProcedureEvent;\");\n    writeln!(\n        output,\n        \"    ProcedureEvent.Status             = FSpacetimeDBProcedureStatus::FromStatus(Event.Status);\"\n    );\n    writeln!(output, \"    ProcedureEvent.Timestamp          = Event.Timestamp;\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    F{module_prefix}ProcedureEventContext Context(this, ProcedureEvent);\"\n    );\n    writeln!(output);\n    writeln!(\n        output,\n        \"    if (Procedures->InternalOnUnhandledProcedureError.IsBound())\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(\n        output,\n        \"        Procedures->InternalOnUnhandledProcedureError.Broadcast(Context, ErrorMessage);\"\n    );\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Additional methods from manual reference\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnection::Builder()\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn NewObject<U{module_prefix}DbConnectionBuilder>();\");\n    writeln!(output, \"}}\");\n    writeln!(output, \"// Added for creating subscriptions\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionBuilder* U{module_prefix}DbConnection::SubscriptionBuilder()\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\tU{module_prefix}SubscriptionBuilder* Builder = NewObject<U{module_prefix}SubscriptionBuilder>(this);\"\n    );\n    writeln!(output, \"\\tBuilder->Conn = this;\");\n    writeln!(output, \"\\treturn Builder;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionBuilder* U{module_prefix}SubscriptionBuilder::OnApplied(F{module_prefix}OnSubscriptionApplied Callback)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tOnAppliedDelegateInternal = Callback;\");\n    writeln!(output, \"\\treturn this;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionBuilder* U{module_prefix}SubscriptionBuilder::OnError(F{module_prefix}OnSubscriptionError Callback)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tOnErrorDelegateInternal = Callback;\");\n    writeln!(output, \"\\treturn this;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionHandle* U{module_prefix}SubscriptionBuilder::Subscribe(const TArray<FString>& SQL)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\tU{module_prefix}SubscriptionHandle* Handle = NewObject<U{module_prefix}SubscriptionHandle>();\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\t// Store user callbacks on the handle\");\n    writeln!(output, \"\\tHandle->Conn = Conn;\");\n    writeln!(output, \"\\tHandle->OnAppliedDelegate = OnAppliedDelegateInternal;\");\n    writeln!(output, \"\\tHandle->OnErrorDelegate = OnErrorDelegateInternal;\");\n    writeln!(output);\n    writeln!(output, \"\\t// Bind forwarding functions that will convert base contexts\");\n    writeln!(output, \"\\tFSubscriptionEventDelegate BaseApplied;\");\n    writeln!(\n        output,\n        \"\\tBaseApplied.BindUFunction(Handle, TEXT(\\\"ForwardOnApplied\\\"));\"\n    );\n    writeln!(output, \"\\tOnAppliedBase(BaseApplied);\");\n    writeln!(output);\n    writeln!(output, \"\\tFSubscriptionErrorDelegate BaseError;\");\n    writeln!(output, \"\\tBaseError.BindUFunction(Handle, TEXT(\\\"ForwardOnError\\\"));\");\n    writeln!(output, \"\\tOnErrorBase(BaseError);\");\n    writeln!(output);\n    writeln!(output, \"\\tSubscribeBase(SQL, Handle);\");\n    writeln!(output, \"\\tif (Conn)\");\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tConn->StartSubscription(Handle);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"\\treturn Handle;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionHandle* U{module_prefix}SubscriptionBuilder::SubscribeToAllTables()\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\treturn Subscribe({{ \\\"SELECT * FROM * \\\" }});\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"U{module_prefix}SubscriptionHandle::U{module_prefix}SubscriptionHandle(U{module_prefix}DbConnection* InConn)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tConn = InConn;\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"void U{module_prefix}SubscriptionHandle::ForwardOnApplied(const FSubscriptionEventContextBase& BaseCtx)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tif (OnAppliedDelegate.IsBound())\");\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_prefix}SubscriptionEventContext Ctx(Conn);\");\n    writeln!(output, \"\\t\\tOnAppliedDelegate.Execute(Ctx);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"void U{module_prefix}SubscriptionHandle::ForwardOnError(const FErrorContextBase& BaseCtx)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tif (OnErrorDelegate.IsBound())\");\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tF{module_prefix}ErrorContext Ctx(Conn, BaseCtx.Error);\");\n    writeln!(output, \"\\t\\tOnErrorDelegate.Execute(Ctx);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(output);\n    writeln!(output, \"// Cast from parent to child class\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::WithUri(const FString& InUri)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnectionBuilder>(WithUriBase(InUri));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::WithDatabaseName(const FString& InName)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnectionBuilder>(WithDatabaseNameBase(InName));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::WithToken(const FString& InToken)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnectionBuilder>(WithTokenBase(InToken));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::WithCompression(const ESpacetimeDBCompression& InCompression)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnectionBuilder>(WithCompressionBase(InCompression));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::OnConnect(F{module_prefix}OnConnectDelegate Callback)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tOnConnectDelegateInternal = Callback;\");\n    writeln!(output, \"\\treturn this;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::OnConnectError(FOnConnectErrorDelegate Callback)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnectionBuilder>(OnConnectErrorBase(Callback));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnectionBuilder* U{module_prefix}DbConnectionBuilder::OnDisconnect(F{module_prefix}OnDisconnectDelegate Callback)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tOnDisconnectDelegateInternal = Callback;\");\n    writeln!(output, \"\\treturn this;\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"U{module_prefix}DbConnection* U{module_prefix}DbConnectionBuilder::Build()\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"\\tU{module_prefix}DbConnection* Connection = NewObject<U{module_prefix}DbConnection>();\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\t// Store delegates on the connection for later use\");\n    writeln!(output, \"\\tConnection->OnConnectDelegate = OnConnectDelegateInternal;\");\n    writeln!(\n        output,\n        \"\\tConnection->OnDisconnectDelegate = OnDisconnectDelegateInternal;\"\n    );\n    writeln!(output);\n    writeln!(output, \"\\t// Wrap delegates so the base builder can bind them\");\n    writeln!(output, \"\\tFOnConnectBaseDelegate BaseConnect;\");\n    writeln!(\n        output,\n        \"\\tBaseConnect.BindUFunction(Connection, TEXT(\\\"ForwardOnConnect\\\"));\"\n    );\n    writeln!(output, \"\\tConnection->SetOnConnectDelegate(BaseConnect);\");\n    writeln!(output, \"\\tOnConnectBase(BaseConnect);\");\n    writeln!(output);\n    writeln!(output, \"\\tFOnDisconnectBaseDelegate BaseDisconnect;\");\n    writeln!(\n        output,\n        \"\\tBaseDisconnect.BindUFunction(Connection, TEXT(\\\"ForwardOnDisconnect\\\"));\"\n    );\n    writeln!(output, \"\\tConnection->SetOnDisconnectDelegate(BaseDisconnect);\");\n    writeln!(output, \"\\tOnDisconnectBase(BaseDisconnect);\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"\\treturn Cast<U{module_prefix}DbConnection>(BuildConnection(Connection));\"\n    );\n    writeln!(output, \"}}\");\n    writeln!(output, \"void U{module_prefix}DbConnection::ForwardOnConnect(UDbConnectionBase* BaseConnection, FSpacetimeDBIdentity InIdentity, const FString& InToken)\");\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tif (OnConnectDelegate.IsBound())\");\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tOnConnectDelegate.Execute(this, Identity, Token);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}}\");\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::ForwardOnDisconnect(UDbConnectionBase* BaseConnection, const FString& Error)\"\n    );\n    writeln!(output, \"{{\");\n    writeln!(output, \"\\tPendingTypedReducers.Empty();\");\n    writeln!(output, \"\\tif (OnDisconnectDelegate.IsBound())\");\n    writeln!(output, \"\\t{{\");\n    writeln!(output, \"\\t\\tOnDisconnectDelegate.Execute(this, Error);\");\n    writeln!(output, \"\\t}}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"void U{module_prefix}DbConnection::DbUpdate(const FDatabaseUpdateType& Update, const FSpacetimeDBEvent& Event)\"\n    );\n    writeln!(output, \"{{\");\n\n    // Create {}Event\n    writeln!(output, \"    F{module_name_pascal}Event BaseEvent;\");\n    writeln!(output, \"    BaseEvent.Tag = Event.Tag;\");\n    writeln!(output);\n\n    // Switch on Event.Tag\n    writeln!(output, \"    switch (Event.Tag)\");\n    writeln!(output, \"    {{\");\n\n    // Reducer case\n    writeln!(output, \"    case ESpacetimeDBEventTag::Reducer:\");\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        FReducerEvent ReducerEvent = Event.GetAsReducer();\");\n    writeln!(output, \"        F{module_prefix}Reducer Reducer;\");\n    writeln!(\n        output,\n        \"        if (!TryGetPendingTypedReducer(ReducerEvent.RequestId, Reducer))\"\n    );\n    writeln!(output, \"        {{\");\n    writeln!(\n        output,\n        \"            const FString ErrorMessage = FString::Printf(TEXT(\\\"Reducer result for unknown request_id %u\\\"), ReducerEvent.RequestId);\"\n    );\n    writeln!(output, \"            HandleProtocolViolation(ErrorMessage);\");\n    writeln!(output, \"            return;\");\n    writeln!(output, \"        }}\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::Reducer(Reducer);\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // Other cases\n    writeln!(output, \"    case ESpacetimeDBEventTag::SubscribeApplied:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::SubscribeApplied(Event.GetAsSubscribeApplied());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    case ESpacetimeDBEventTag::UnsubscribeApplied:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::UnsubscribeApplied(Event.GetAsUnsubscribeApplied());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    case ESpacetimeDBEventTag::Disconnected:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::Disconnected(Event.GetAsDisconnected());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    case ESpacetimeDBEventTag::Transaction:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::Transaction(Event.GetAsTransaction());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    case ESpacetimeDBEventTag::SubscribeError:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::SubscribeError(Event.GetAsSubscribeError());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    case ESpacetimeDBEventTag::UnknownTransaction:\");\n    writeln!(\n        output,\n        \"        BaseEvent = F{module_name_pascal}Event::UnknownTransaction(Event.GetAsUnknownTransaction());\"\n    );\n    writeln!(output, \"        break;\");\n    writeln!(output);\n\n    writeln!(output, \"    default:\");\n    writeln!(output, \"        break;\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    // Wrap in EventContext\n    writeln!(output, \"    F{module_prefix}EventContext Context(this, BaseEvent);\");\n    writeln!(\n        output,\n        \"    // Populate typed reducer args for convenience in table handlers\"\n    );\n    writeln!(output);\n\n    // Apply updates\n    writeln!(output, \"    ApplyRegisteredTableUpdates(Update, &Context);\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n}\n\nfn generate_remote_reducer_calls(\n    output: &mut UnrealCppAutogen,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    module_prefix: &str,\n    module_name: &str,\n) {\n    // Reducer implementations\n    for reducer in iter_reducers(module, visibility) {\n        let reducer_name = reducer.name.deref().to_case(Case::Pascal);\n        let reducer_pascal = format!(\"{module_prefix}{reducer_name}\");\n        let reducer_snake = reducer.name.deref();\n\n        // Call method implementation\n        write!(output, \"void U{module_prefix}RemoteReducers::{reducer_name}(\");\n        let mut first = true;\n        for (param_name, param_type) in &reducer.params_for_generate.elements {\n            if !first {\n                write!(output, \", \");\n            }\n            first = false;\n            // Use Blueprint-compatible types (same as UFUNCTION and delegates)\n            let type_str = cpp_ty_fmt_blueprint_compatible(module_prefix, module, param_type, module_name).to_string();\n            if should_pass_by_value_in_delegate(module, param_type) {\n                // Primitives use const by-value in U{module_prefix}RemoteReducers methods (same as UFUNCTION)\n                write!(\n                    output,\n                    \"const {} {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            } else {\n                // Complex types (including FSpacetimeDB structs) use const references\n                write!(\n                    output,\n                    \"const {}& {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            }\n        }\n        writeln!(output, \")\");\n        writeln!(output, \"{{\");\n        writeln!(output, \"    if (!Conn)\");\n        writeln!(output, \"    {{\");\n        writeln!(\n            output,\n            \"        UE_LOG(LogTemp, Error, TEXT(\\\"SpacetimeDB connection is null\\\"));\"\n        );\n        writeln!(output, \"        return;\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n        // Call reducer using typed helper to hide serialization\n        if reducer.params_for_generate.elements.is_empty() {\n            writeln!(output, \"\\tF{reducer_pascal}Args ReducerArgs;\");\n            writeln!(\n                output,\n                \"\\tconst uint32 RequestId = Conn->CallReducerTyped(TEXT(\\\"{reducer_snake}\\\"), ReducerArgs);\"\n            );\n            writeln!(\n                output,\n                \"\\tif (RequestId != 0) {{ Conn->RegisterPendingTypedReducer(RequestId, F{module_prefix}Reducer::{reducer_name}(ReducerArgs)); }}\"\n            );\n        } else {\n            write!(output, \"\\tF{reducer_pascal}Args ReducerArgs(\");\n            let mut first = true;\n            for (param_name, _) in &reducer.params_for_generate.elements {\n                if !first {\n                    write!(output, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(output, \"{param_pascal}\");\n            }\n            writeln!(output, \");\");\n            writeln!(\n                output,\n                \"\\tconst uint32 RequestId = Conn->CallReducerTyped(TEXT(\\\"{reducer_snake}\\\"), ReducerArgs);\"\n            );\n            writeln!(\n                output,\n                \"\\tif (RequestId != 0) {{ Conn->RegisterPendingTypedReducer(RequestId, F{module_prefix}Reducer::{reducer_name}(ReducerArgs)); }}\"\n            );\n        }\n        writeln!(output, \"}}\");\n        writeln!(output);\n\n        // Invoke method implementation\n        write!(\n            output,\n            \"bool U{module_prefix}RemoteReducers::Invoke{reducer_name}(const F{module_prefix}ReducerEventContext& Context, const U{reducer_pascal}Reducer* Args)\"\n        );\n        writeln!(output);\n        writeln!(output, \"{{\");\n        writeln!(output, \"    if (!On{reducer_name}.IsBound())\");\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        // Handle unhandled reducer error\");\n        writeln!(output, \"        if (InternalOnUnhandledReducerError.IsBound())\");\n        writeln!(output, \"        {{\");\n        writeln!(\n            output,\n            \"            // TODO: Check Context.Event.Status for Failed/OutOfEnergy cases\"\n        );\n        writeln!(output, \"            // For now, just broadcast any error\");\n        writeln!(\n            output,\n            \"            InternalOnUnhandledReducerError.Broadcast(Context, TEXT(\\\"No handler registered for {reducer_name}\\\"));\"\n        );\n        writeln!(output, \"        }}\");\n        writeln!(output, \"        return false;\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n\n        // Check if we're using args struct (more than 9 params including context)\n        let param_count = reducer.params_for_generate.elements.len() + 1; // +1 for context\n        let use_args_struct = param_count > 9;\n\n        if use_args_struct {\n            // Create args struct from individual fields\n            writeln!(output, \"    F{reducer_pascal}Args ArgsStruct;\");\n            for (param_name, _) in &reducer.params_for_generate.elements {\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                writeln!(output, \"    ArgsStruct.{param_pascal} = Args->{param_pascal};\");\n            }\n            writeln!(output, \"    On{reducer_name}.Broadcast(Context, ArgsStruct);\");\n        } else {\n            // Use individual parameters\n            write!(output, \"    On{reducer_name}.Broadcast(Context\");\n            for (param_name, _) in &reducer.params_for_generate.elements {\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(output, \", Args->{param_pascal}\");\n            }\n            writeln!(output, \");\");\n        }\n\n        writeln!(output, \"    return true;\");\n        writeln!(output, \"}}\");\n        writeln!(output);\n\n        // InvokeWithArgs method implementation (zero allocation version)\n        write!(\n            output,\n            \"bool U{module_prefix}RemoteReducers::Invoke{reducer_name}WithArgs(const F{module_prefix}ReducerEventContext& Context, const F{reducer_pascal}Args& Args)\"\n        );\n        writeln!(output);\n        writeln!(output, \"{{\");\n        writeln!(output, \"    if (!On{reducer_name}.IsBound())\");\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        if (InternalOnUnhandledReducerError.IsBound())\");\n        writeln!(output, \"        {{\");\n        writeln!(\n            output,\n            \"            InternalOnUnhandledReducerError.Broadcast(Context, TEXT(\\\"No handler registered for {reducer_name}\\\"));\"\n        );\n        writeln!(output, \"        }}\");\n        writeln!(output, \"        return false;\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n\n        // Check if we're using args struct (more than 9 params including context)\n        if use_args_struct {\n            // Pass args struct directly\n            writeln!(output, \"    On{reducer_name}.Broadcast(Context, Args);\");\n        } else {\n            // Use individual parameters from Args struct\n            write!(output, \"    On{reducer_name}.Broadcast(Context\");\n            for (param_name, _) in &reducer.params_for_generate.elements {\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(output, \", Args.{param_pascal}\");\n            }\n            writeln!(output, \");\");\n        }\n\n        writeln!(output, \"    return true;\");\n        writeln!(output, \"}}\");\n        writeln!(output);\n    }\n}\n\nfn generate_remote_procedure_calls(\n    output: &mut UnrealCppAutogen,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n    module_prefix: &str,\n    module_name: &str,\n) {\n    // Procedure implementations\n    for procedure in iter_procedures(module, visibility) {\n        let procedure_name = procedure.name.deref().to_case(Case::Pascal);\n        let procedure_pascal = format!(\"{module_prefix}{procedure_name}\");\n        let procedure_snake = procedure.name.deref();\n\n        // Call method implementation\n        write!(output, \"void U{module_prefix}RemoteProcedures::{procedure_name}(\");\n        let mut first = true;\n        for (param_name, param_type) in &procedure.params_for_generate.elements {\n            if !first {\n                write!(output, \", \");\n            }\n            first = false;\n            // Use Blueprint-compatible types (same as UFUNCTION and delegates)\n            let type_str = cpp_ty_fmt_blueprint_compatible(module_prefix, module, param_type, module_name).to_string();\n            if should_pass_by_value_in_delegate(module, param_type) {\n                // Primitives use const by-value in U{module_prefix}RemoteProcedures methods (same as UFUNCTION)\n                write!(\n                    output,\n                    \"const {} {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            } else {\n                // Complex types (including FSpacetimeDB structs) use const references\n                write!(\n                    output,\n                    \"const {}& {}\",\n                    type_str,\n                    param_name.deref().to_case(Case::Pascal)\n                );\n            }\n        }\n        if !first {\n            write!(output, \", \");\n        }\n        writeln!(output, \"F{module_prefix}On{}Complete Callback)\", procedure_name);\n        writeln!(output, \"{{\");\n        writeln!(output, \"    if (!Conn)\");\n        writeln!(output, \"    {{\");\n        writeln!(\n            output,\n            \"        UE_LOG(LogTemp, Error, TEXT(\\\"SpacetimeDB connection is null\\\"));\"\n        );\n        writeln!(output, \"        return;\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n\n        // Get the actual return type for this procedure\n        let return_type_str =\n            cpp_ty_fmt_with_module(module_prefix, module, &procedure.return_type_for_generate, module_name).to_string();\n\n        writeln!(output, \"    FOnProcedureCompleteDelegate Wrapper;\");\n        writeln!(output, \"    Wrapper.BindLambda(\");\n        writeln!(output, \"        [Callback = MoveTemp(Callback), Conn = this->Conn]\");\n        writeln!(output, \"        (const FSpacetimeDBEvent& Event,\");\n        writeln!(output, \"            const TArray<uint8>& ResultData,\");\n        writeln!(output, \"            bool bSuccess) mutable\");\n        writeln!(output, \"        {{\");\n        writeln!(output, \"            {} ResultValue{{}};\", return_type_str);\n        writeln!(output);\n        writeln!(output, \"            if (bSuccess) {{\");\n        writeln!(\n            output,\n            \"                ResultValue = UE::SpacetimeDB::Deserialize<{}>(ResultData);\",\n            return_type_str\n        );\n        writeln!(output, \"            }}\");\n        writeln!(output);\n        writeln!(output, \"            F{module_name}ProcedureEvent ProcedureEvent = F{module_name}ProcedureEvent(Event.GetAsProcedure());\");\n        writeln!(\n            output,\n            \"            F{module_prefix}ProcedureEventContext Context = F{module_prefix}ProcedureEventContext(Conn, ProcedureEvent);\"\n        );\n        writeln!(output, \"            // Fire the user's typed delegate\");\n        writeln!(\n            output,\n            \"            Callback.ExecuteIfBound(Context, ResultValue, bSuccess);\"\n        );\n        writeln!(output, \"        }});\");\n\n        // Call procedure using typed helper to hide serialization\n        if procedure.params_for_generate.elements.is_empty() {\n            writeln!(\n                output,\n                \"\\tConn->CallProcedureTyped(TEXT(\\\"{procedure_snake}\\\"), F{procedure_pascal}Args(), Wrapper);\"\n            );\n        } else {\n            write!(\n                output,\n                \"\\tConn->CallProcedureTyped(TEXT(\\\"{procedure_snake}\\\"), F{procedure_pascal}Args(\"\n            );\n            let mut first = true;\n            for (param_name, _) in &procedure.params_for_generate.elements {\n                if !first {\n                    write!(output, \", \");\n                }\n                first = false;\n                let param_pascal = param_name.deref().to_case(Case::Pascal);\n                write!(output, \"{param_pascal}\");\n            }\n            writeln!(output, \"), Wrapper);\");\n        }\n        writeln!(output, \"}}\");\n        writeln!(output);\n    }\n}\n\nstruct UnrealCppAutogen {\n    output: CodeIndenter<String>,\n}\n\nimpl Deref for UnrealCppAutogen {\n    type Target = CodeIndenter<String>;\n    fn deref(&self) -> &Self::Target {\n        &self.output\n    }\n}\nimpl std::ops::DerefMut for UnrealCppAutogen {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.output\n    }\n}\n\nimpl UnrealCppAutogen {\n    fn new(extra_includes: &[&str], self_header: &str, include_version: bool) -> Self {\n        let mut output = CodeIndenter::new(String::new(), \"    \");\n        print_auto_generated_file_comment(&mut output);\n        if include_version {\n            print_auto_generated_version_comment(&mut output);\n        }\n        writeln!(output, \"#pragma once\");\n        writeln!(output, \"#include \\\"CoreMinimal.h\\\"\");\n        writeln!(output, \"#include \\\"BSATN/UESpacetimeDB.h\\\"\");\n\n        // Add specific includes based on what's needed\n        for include in extra_includes {\n            writeln!(output, \"#include \\\"{include}\\\"\");\n        }\n        writeln!(output, \"#include \\\"{self_header}.g.generated.h\\\"\"); // need to include the generated header for Unreal's reflection\n        writeln!(output);\n        Self { output }\n    }\n\n    fn new_cpp(extra_includes: &[&str]) -> Self {\n        let mut output = CodeIndenter::new(String::new(), \"    \");\n        print_auto_generated_file_comment(&mut output);\n        for include in extra_includes {\n            writeln!(output, \"#include \\\"{include}\\\"\");\n        }\n        writeln!(output);\n        Self { output }\n    }\n\n    fn into_inner(self) -> String {\n        self.output.into_inner()\n    }\n}\n\n// ---------------------------------------------------------------------------\n//  Helpers\n// ---------------------------------------------------------------------------\n\n// Unified helper function to collect special wrapper types (optionals and results)\nfn collect_wrapper_types(\n    module_prefix: &str,\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n) -> (HashSet<String>, HashSet<(String, String)>) {\n    let mut optional_types = HashSet::new();\n    let mut result_types = HashSet::new();\n\n    // Helper function to recursively collect from a type\n    fn collect_from_type(\n        module_prefix: &str,\n        module: &ModuleDef,\n        ty: &AlgebraicTypeUse,\n        optional_types: &mut HashSet<String>,\n        result_types: &mut HashSet<(String, String)>,\n    ) {\n        match ty {\n            AlgebraicTypeUse::Option(inner) => {\n                // Generate the optional type name\n                let optional_name = get_optional_type_name(module_prefix, module, inner);\n                optional_types.insert(optional_name);\n                // Recursively collect from inner type\n                collect_from_type(module_prefix, module, inner, optional_types, result_types);\n            }\n            AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n                // Generate the result type name components\n                let ok_name = get_type_name_for_result(module_prefix, module, ok_ty);\n                let err_name = get_type_name_for_result(module_prefix, module, err_ty);\n                result_types.insert((ok_name, err_name));\n                // Recursively collect from both inner types\n                collect_from_type(module_prefix, module, ok_ty, optional_types, result_types);\n                collect_from_type(module_prefix, module, err_ty, optional_types, result_types);\n            }\n            AlgebraicTypeUse::Array(elem) => {\n                collect_from_type(module_prefix, module, elem, optional_types, result_types);\n            }\n            AlgebraicTypeUse::Ref(r) => {\n                // Check if the referenced type contains optionals or results\n                match &module.typespace_for_generate()[*r] {\n                    AlgebraicTypeDef::Product(product) => {\n                        for (_, field_ty) in &product.elements {\n                            collect_from_type(module_prefix, module, field_ty, optional_types, result_types);\n                        }\n                    }\n                    AlgebraicTypeDef::Sum(sum) => {\n                        for (_, variant_ty) in &sum.variants {\n                            collect_from_type(module_prefix, module, variant_ty, optional_types, result_types);\n                        }\n                    }\n                    _ => {}\n                }\n            }\n            _ => {}\n        }\n    }\n\n    // Collect from all tables\n    for (_, _, product_type_ref) in iter_table_names_and_types(module, visibility) {\n        let product_type = module.typespace_for_generate()[product_type_ref].as_product().unwrap();\n        for (_, field_ty) in &product_type.elements {\n            collect_from_type(module_prefix, module, field_ty, &mut optional_types, &mut result_types);\n        }\n    }\n\n    // Collect from all types\n    for typ in module.types() {\n        match &module.typespace_for_generate()[typ.ty] {\n            AlgebraicTypeDef::Product(product) => {\n                for (_, field_ty) in &product.elements {\n                    collect_from_type(module_prefix, module, field_ty, &mut optional_types, &mut result_types);\n                }\n            }\n            AlgebraicTypeDef::Sum(sum) => {\n                for (_, variant_ty) in &sum.variants {\n                    collect_from_type(\n                        module_prefix,\n                        module,\n                        variant_ty,\n                        &mut optional_types,\n                        &mut result_types,\n                    );\n                }\n            }\n            _ => {}\n        }\n    }\n\n    // Collect from reducer parameters\n    for reducer in iter_reducers(module, visibility) {\n        for (_, param_ty) in &reducer.params_for_generate.elements {\n            collect_from_type(module_prefix, module, param_ty, &mut optional_types, &mut result_types);\n        }\n    }\n\n    // Collect from procedure parameters and return types\n    for procedure in iter_procedures(module, visibility) {\n        for (_, param_ty) in &procedure.params_for_generate.elements {\n            collect_from_type(module_prefix, module, param_ty, &mut optional_types, &mut result_types);\n        }\n        collect_from_type(\n            module_prefix,\n            module,\n            &procedure.return_type_for_generate,\n            &mut optional_types,\n            &mut result_types,\n        );\n    }\n\n    (optional_types, result_types)\n}\n\n// Helper function to get C++ type for array elements in optional arrays\nfn get_cpp_type_for_array_element(\n    module_prefix: &str,\n    elem_type_str: &str,\n    module: &ModuleDef,\n    module_name: &str,\n) -> String {\n    match elem_type_str {\n        \"Bool\" => \"bool\".to_string(),\n        \"I8\" | \"Int8\" => \"int8\".to_string(),\n        \"U8\" | \"UInt8\" => \"uint8\".to_string(),\n        \"I16\" | \"Int16\" => \"int16\".to_string(),\n        \"U16\" | \"UInt16\" => \"uint16\".to_string(),\n        \"I32\" | \"Int32\" => \"int32\".to_string(),\n        \"U32\" | \"UInt32\" => \"uint32\".to_string(),\n        \"I64\" | \"Int64\" => \"int64\".to_string(),\n        \"U64\" | \"UInt64\" => \"uint64\".to_string(),\n        \"F32\" | \"Float\" => \"float\".to_string(),\n        \"F64\" | \"Double\" => \"double\".to_string(),\n        \"I128\" | \"Int128\" => \"FSpacetimeDBInt128\".to_string(),\n        \"U128\" | \"UInt128\" => \"FSpacetimeDBUInt128\".to_string(),\n        \"I256\" | \"Int256\" => \"FSpacetimeDBInt256\".to_string(),\n        \"U256\" | \"UInt256\" => \"FSpacetimeDBUInt256\".to_string(),\n        \"String\" => \"FString\".to_string(),\n        \"Identity\" => \"FSpacetimeDBIdentity\".to_string(),\n        \"ConnectionId\" => \"FSpacetimeDBConnectionId\".to_string(),\n        \"Timestamp\" => \"FSpacetimeDBTimestamp\".to_string(),\n        \"TimeDuration\" => \"FSpacetimeDBTimeDuration\".to_string(),\n        \"Uuid\" => \"FSpacetimeDBUuid\".to_string(),\n        \"ScheduleAt\" => \"FSpacetimeDBScheduleAt\".to_string(),\n        _ if elem_type_str.starts_with(\"Int32\") => {\n            // Handle nested optionals like Int32 from OptionalInt32\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            format!(\"F{module_name_pascal}OptionalInt32\")\n        }\n        _ => {\n            let is_enum = module.types().any(|type_def| {\n                let type_name = type_def\n                    .accessor_name\n                    .name_segments()\n                    .last()\n                    .map(|id| id.deref().to_string())\n                    .unwrap_or_else(|| \"Unnamed\".to_string());\n                let prefixed_type_name = format!(\"{module_prefix}{type_name}\");\n                prefixed_type_name == elem_type_str\n                    && matches!(\n                        module.typespace_for_generate()[type_def.ty],\n                        AlgebraicTypeDef::PlainEnum(_)\n                    )\n            });\n\n            if is_enum {\n                format!(\"E{elem_type_str}Type\")\n            } else {\n                format!(\"F{elem_type_str}Type\")\n            }\n        }\n    }\n}\n\n// Helper function to get array element type name for optional array types\nfn get_array_element_type_name(module_prefix: &str, module: &ModuleDef, elem: &AlgebraicTypeUse) -> String {\n    match elem {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            PrimitiveType::Bool => \"Bool\".to_string(),\n            PrimitiveType::I8 => \"Int8\".to_string(),\n            PrimitiveType::U8 => \"UInt8\".to_string(),\n            PrimitiveType::I16 => \"Int16\".to_string(),\n            PrimitiveType::U16 => \"UInt16\".to_string(),\n            PrimitiveType::I32 => \"Int32\".to_string(),\n            PrimitiveType::U32 => \"UInt32\".to_string(),\n            PrimitiveType::I64 => \"Int64\".to_string(),\n            PrimitiveType::U64 => \"UInt64\".to_string(),\n            PrimitiveType::F32 => \"Float\".to_string(),\n            PrimitiveType::F64 => \"Double\".to_string(),\n            PrimitiveType::I128 => \"Int128\".to_string(),\n            PrimitiveType::U128 => \"UInt128\".to_string(),\n            PrimitiveType::I256 => \"Int256\".to_string(),\n            PrimitiveType::U256 => \"UInt256\".to_string(),\n        },\n        AlgebraicTypeUse::String => \"String\".to_string(),\n        AlgebraicTypeUse::Identity => \"Identity\".to_string(),\n        AlgebraicTypeUse::ConnectionId => \"ConnectionId\".to_string(),\n        AlgebraicTypeUse::Timestamp => \"Timestamp\".to_string(),\n        AlgebraicTypeUse::TimeDuration => \"TimeDuration\".to_string(),\n        AlgebraicTypeUse::Uuid => \"Uuid\".to_string(),\n        AlgebraicTypeUse::ScheduleAt => \"ScheduleAt\".to_string(),\n        AlgebraicTypeUse::Ref(r) => type_ref_name(module_prefix, module, *r),\n        AlgebraicTypeUse::Option(nested_inner) => {\n            // Handle optional elements in arrays like Vec<Option<i32>>\n            get_optional_type_name(module_prefix, module, nested_inner)\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            // Handle result elements in arrays like Vec<Result<i32, String>>\n            get_result_type_name(module_prefix, module, ok_ty, err_ty)\n        }\n        _ => \"Unknown\".to_string(),\n    }\n}\n\n// Get the name for an optional type (e.g., \"OptionalString\", \"OptionalInt32\")\nfn get_optional_type_name(module_prefix: &str, module: &ModuleDef, inner: &AlgebraicTypeUse) -> String {\n    match inner {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            PrimitiveType::Bool => \"OptionalBool\".to_string(),\n            PrimitiveType::I8 => \"OptionalInt8\".to_string(),\n            PrimitiveType::U8 => \"OptionalUInt8\".to_string(),\n            PrimitiveType::I16 => \"OptionalInt16\".to_string(),\n            PrimitiveType::U16 => \"OptionalUInt16\".to_string(),\n            PrimitiveType::I32 => \"OptionalInt32\".to_string(),\n            PrimitiveType::U32 => \"OptionalUInt32\".to_string(),\n            PrimitiveType::I64 => \"OptionalInt64\".to_string(),\n            PrimitiveType::U64 => \"OptionalUInt64\".to_string(),\n            PrimitiveType::F32 => \"OptionalFloat\".to_string(),\n            PrimitiveType::F64 => \"OptionalDouble\".to_string(),\n            PrimitiveType::I128 => \"OptionalInt128\".to_string(),\n            PrimitiveType::U128 => \"OptionalUInt128\".to_string(),\n            PrimitiveType::I256 => \"OptionalInt256\".to_string(),\n            PrimitiveType::U256 => \"OptionalUInt256\".to_string(),\n        },\n        AlgebraicTypeUse::String => \"OptionalString\".to_string(),\n        AlgebraicTypeUse::Identity => \"OptionalIdentity\".to_string(),\n        AlgebraicTypeUse::ConnectionId => \"OptionalConnectionId\".to_string(),\n        AlgebraicTypeUse::Timestamp => \"OptionalTimestamp\".to_string(),\n        AlgebraicTypeUse::TimeDuration => \"OptionalTimeDuration\".to_string(),\n        AlgebraicTypeUse::Uuid => \"OptionalUuid\".to_string(),\n        AlgebraicTypeUse::ScheduleAt => \"OptionalScheduleAt\".to_string(),\n        AlgebraicTypeUse::Array(elem) => {\n            // Generate specific optional array types based on element type\n            let elem_name = get_array_element_type_name(module_prefix, module, elem);\n            format!(\"OptionalVec{elem_name}\")\n        }\n        AlgebraicTypeUse::Ref(r) => {\n            let type_name = type_ref_name(module_prefix, module, *r);\n            format!(\"Optional{type_name}\")\n        }\n        AlgebraicTypeUse::Option(nested_inner) => {\n            // Handle nested optionals like Option<Option<String>>\n            let inner_optional_name = get_optional_type_name(module_prefix, module, nested_inner);\n            format!(\"Optional{inner_optional_name}\")\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            let result_name = get_result_type_name(module_prefix, module, ok_ty, err_ty);\n            format!(\"Optional{result_name}\")\n        }\n        _ => \"OptionalUnknown\".to_string(),\n    }\n}\n\n// Helper function to determine C++ type from type name string for result types\nfn determine_cpp_type_for_result(\n    module_prefix: &str,\n    type_name: &str,\n    module: &ModuleDef,\n    module_name: &str,\n) -> String {\n    match type_name {\n        \"Bool\" => \"bool\".to_string(),\n        \"Int8\" => \"int8\".to_string(),\n        \"UInt8\" => \"uint8\".to_string(),\n        \"Int16\" => \"int16\".to_string(),\n        \"UInt16\" => \"uint16\".to_string(),\n        \"Int32\" => \"int32\".to_string(),\n        \"UInt32\" => \"uint32\".to_string(),\n        \"Int64\" => \"int64\".to_string(),\n        \"UInt64\" => \"uint64\".to_string(),\n        \"Float\" => \"float\".to_string(),\n        \"Double\" => \"double\".to_string(),\n        \"Int128\" => \"FSpacetimeDBInt128\".to_string(),\n        \"UInt128\" => \"FSpacetimeDBUInt128\".to_string(),\n        \"Int256\" => \"FSpacetimeDBInt256\".to_string(),\n        \"UInt256\" => \"FSpacetimeDBUInt256\".to_string(),\n        \"String\" => \"FString\".to_string(),\n        \"Identity\" => \"FSpacetimeDBIdentity\".to_string(),\n        \"ConnectionId\" => \"FSpacetimeDBConnectionId\".to_string(),\n        \"Timestamp\" => \"FSpacetimeDBTimestamp\".to_string(),\n        \"TimeDuration\" => \"FSpacetimeDBTimeDuration\".to_string(),\n        \"Uuid\" => \"FSpacetimeDBUuid\".to_string(),\n        \"ScheduleAt\" => \"FSpacetimeDBScheduleAt\".to_string(),\n        \"Unit\" => \"FSpacetimeDBUnit\".to_string(),\n        _ if type_name.starts_with(\"Optional\") => {\n            // Handle optional types like OptionalString\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            format!(\"F{module_name_pascal}{type_name}\")\n        }\n        _ if type_name.starts_with(\"VecOptional\") => {\n            // Handle specific optional array types like OptionalVecOptionalI32, OptionalVecOptionalString, etc.\n            let elem_type_str = &type_name[11..]; // Remove \"VecOptional\" prefix\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            format!(\"TArray<F{module_name_pascal}Optional{elem_type_str}>\")\n        }\n        _ if type_name.starts_with(\"Vec\") => {\n            // Handle array types like VecInt32\n            let elem_type_str = &type_name[3..]; // Remove \"Vec\" prefix\n            let cpp_elem_type = get_cpp_type_for_array_element(module_prefix, elem_type_str, module, module_name);\n            format!(\"TArray<{cpp_elem_type}>\")\n        }\n        _ if type_name.starts_with(\"Result\") => {\n            // Handle nested result types\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            format!(\"F{module_name_pascal}{type_name}\")\n        }\n        _ => {\n            // For custom types, check if it's an enum or struct\n            let is_enum = module.types().any(|type_def| {\n                let type_name_from_def = type_def\n                    .accessor_name\n                    .name_segments()\n                    .last()\n                    .map(|id| id.deref().to_string())\n                    .unwrap_or_else(|| \"Unnamed\".to_string());\n                let prefixed_type_name = format!(\"{module_prefix}{type_name_from_def}\");\n                prefixed_type_name == type_name\n                    && matches!(\n                        module.typespace_for_generate()[type_def.ty],\n                        AlgebraicTypeDef::PlainEnum(_)\n                    )\n            });\n\n            if is_enum {\n                format!(\"E{type_name}Type\")\n            } else {\n                format!(\"F{type_name}Type\")\n            }\n        }\n    }\n}\n\n// Helper function to check if a type name is blueprintable\nfn is_type_name_blueprintable(type_name: &str) -> bool {\n    match type_name {\n        // Non-blueprintable primitive types\n        \"Int8\" | \"Int16\" | \"UInt16\" | \"UInt32\" | \"UInt64\" => false,\n\n        // Blueprintable types\n        \"Bool\" | \"Int32\" | \"Int64\" | \"Float\" | \"Double\" | \"String\" | \"Identity\" | \"ConnectionId\" | \"Timestamp\"\n        | \"TimeDuration\" | \"ScheduleAt\" | \"Int128\" | \"UInt128\" | \"Int256\" | \"UInt256\" | \"Unit\" => true,\n\n        // Optional types - check the inner type\n        _ if type_name.starts_with(\"Optional\") => {\n            let inner_type = &type_name[8..]; // Remove \"Optional\" prefix\n            is_type_name_blueprintable(inner_type)\n        }\n\n        // Result types - both Ok and Err types must be blueprintable\n        // Parse ResultXXXYYY to extract both type names\n        _ if type_name.starts_with(\"Result\") => {\n            // This is complex - we'd need to parse \"ResultInt32String\" into \"Int32\" and \"String\"\n            // For now, be conservative and return true since Result wrapper itself is a USTRUCT\n            // The individual fields will have their own blueprintable checks\n            true\n        }\n\n        // Vec types - arrays are blueprintable if element is blueprintable\n        _ if type_name.starts_with(\"Vec\") => {\n            let elem_type = &type_name[3..]; // Remove \"Vec\" prefix\n            is_type_name_blueprintable(elem_type)\n        }\n\n        // Custom types (structs/enums) are always blueprintable\n        _ => true,\n    }\n}\n\n// Helper function to add includes for a type name\nfn add_includes_for_type_name(type_name: &str, includes: &mut Vec<String>, module_name: &str) {\n    match type_name {\n        \"Identity\" | \"ConnectionId\" | \"Timestamp\" | \"TimeDuration\" | \"ScheduleAt\" | \"Uuid\" => {\n            includes.push(\"Types/Builtins.h\".to_string());\n        }\n        \"Int128\" | \"UInt128\" | \"Int256\" | \"UInt256\" => {\n            includes.push(\"Types/LargeIntegers.h\".to_string());\n        }\n        \"Unit\" => {\n            includes.push(\"Types/UnitType.h\".to_string());\n        }\n        \"String\" | \"Bool\" | \"Int8\" | \"UInt8\" | \"Int16\" | \"UInt16\" | \"Int32\" | \"UInt32\" | \"Int64\" | \"UInt64\"\n        | \"Float\" | \"Double\" => {\n            // Basic types, no extra includes needed\n        }\n        _ if type_name.starts_with(\"Vec\") => {\n            // Array types - no extra include needed for TArray itself\n        }\n        _ if type_name.starts_with(\"Optional\") => {\n            // Nested optional, need its header\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            includes.push(format!(\"ModuleBindings/Optionals/{module_name_pascal}{type_name}.g.h\"));\n        }\n        _ if type_name.starts_with(\"Result\") => {\n            // Nested result, need its header\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            includes.push(format!(\"ModuleBindings/Results/{module_name_pascal}{type_name}.g.h\"));\n        }\n        _ if !type_name.starts_with(\"Int\") && !type_name.starts_with(\"UInt\") => {\n            // Custom type, need its header\n            includes.push(format!(\"ModuleBindings/Types/{type_name}Type.g.h\"));\n        }\n        _ => {}\n    }\n}\n\n// Generate the content for an optional type file\nfn generate_optional_type(\n    optional_name: &str,\n    module_prefix: &str,\n    module: &ModuleDef,\n    api_macro: &str,\n    module_name: &str,\n) -> String {\n    // Extract the inner type from the optional name\n    let inner_type_str = optional_name.strip_prefix(\"Optional\").unwrap_or(optional_name);\n\n    // Determine the C++ type for the inner value\n    let cpp_inner_type = determine_cpp_type_for_result(module_prefix, inner_type_str, module, module_name);\n\n    // Determine if we need extra includes\n    let mut extra_includes = vec![];\n    add_includes_for_type_name(inner_type_str, &mut extra_includes, module_name);\n\n    let extra_includes_refs: Vec<&str> = extra_includes.iter().map(|s| s.as_str()).collect();\n    let module_name_pascal = module_name.to_case(Case::Pascal);\n    let struct_name = format!(\"F{module_name_pascal}{optional_name}\");\n    let header_name = format!(\"{module_name_pascal}{optional_name}\");\n    let mut output = UnrealCppAutogen::new(&extra_includes_refs, &header_name, false);\n\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} {struct_name}\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"GENERATED_BODY()\");\n    writeln!(output);\n\n    // Has value flag\n    writeln!(\n        output,\n        \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"bool bHasValue = false;\");\n    writeln!(output);\n\n    // The actual value\n    // Check if the type is blueprintable\n    let is_blueprintable = is_type_name_blueprintable(inner_type_str);\n\n    if is_blueprintable {\n        writeln!(output, \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\", meta = (EditCondition = \\\"bHasValue\\\"))\");\n    } else {\n        writeln!(\n            output,\n            \"// NOTE: {cpp_inner_type} field not exposed to Blueprint due to non-blueprintable elements\"\n        );\n    }\n    writeln!(output, \"{cpp_inner_type} Value = {{}};\");\n    writeln!(output);\n\n    // Constructors\n    writeln!(output, \"{struct_name}() = default;\");\n    writeln!(output);\n    writeln!(output, \"explicit {struct_name}(const {cpp_inner_type}& InValue)\");\n    writeln!(output, \"    : bHasValue(true), Value(InValue) {{}}\");\n    writeln!(output);\n\n    // Helper methods\n    writeln!(output, \"bool IsSet() const {{ return bHasValue; }}\");\n    writeln!(output, \"void Reset() {{ bHasValue = false; }}\");\n    writeln!(output);\n\n    // Operators\n    writeln!(output, \"FORCEINLINE bool operator==(const {struct_name}& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"if (bHasValue != Other.bHasValue) return false;\");\n    writeln!(output, \"return !bHasValue || Value == Other.Value;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    writeln!(output, \"FORCEINLINE bool operator!=(const {struct_name}& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"return !(*this == Other);\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n\n    output.dedent(1);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // Add GetTypeHash implementation for optional types\n    writeln!(output, \"/**\");\n    writeln!(output, \" * Custom hash function for {struct_name}.\");\n    writeln!(output, \" * Hashes the HasValue flag and the Value if present.\");\n    writeln!(output, \" * @param Optional The {struct_name} instance to hash.\");\n    writeln!(output, \" * @return The combined hash value.\");\n    writeln!(output, \" */\");\n    writeln!(output, \"FORCEINLINE uint32 GetTypeHash(const {struct_name}& Optional)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"uint32 Hash = GetTypeHash(Optional.bHasValue);\");\n    writeln!(output, \"if (Optional.bHasValue)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"Hash = HashCombine(Hash, GetTypeHash(Optional.Value));\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output, \"return Hash;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // BSATN serialization support\n    writeln!(output, \"namespace UE::SpacetimeDB\");\n    writeln!(output, \"{{\");\n\n    writeln!(output, \"    UE_SPACETIMEDB_ENABLE_TARRAY({struct_name});\");\n\n    writeln!(output);\n\n    writeln!(output, \"    UE_SPACETIMEDB_OPTIONAL({struct_name}, bHasValue, Value);\");\n    writeln!(output, \"}}\");\n\n    output.into_inner()\n}\n\n// Get the name for a result type (e.g., \"ResultStringString\", \"ResultInt32String\")\nfn get_result_type_name(\n    module_prefix: &str,\n    module: &ModuleDef,\n    ok_ty: &AlgebraicTypeUse,\n    err_ty: &AlgebraicTypeUse,\n) -> String {\n    let ok_name = get_type_name_for_result(module_prefix, module, ok_ty);\n    let err_name = get_type_name_for_result(module_prefix, module, err_ty);\n    format!(\"Result{ok_name}{err_name}\")\n}\n\n// Helper function to get the type name component for result types\nfn get_type_name_for_result(module_prefix: &str, module: &ModuleDef, ty: &AlgebraicTypeUse) -> String {\n    match ty {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            PrimitiveType::Bool => \"Bool\".to_string(),\n            PrimitiveType::I8 => \"Int8\".to_string(),\n            PrimitiveType::U8 => \"UInt8\".to_string(),\n            PrimitiveType::I16 => \"Int16\".to_string(),\n            PrimitiveType::U16 => \"UInt16\".to_string(),\n            PrimitiveType::I32 => \"Int32\".to_string(),\n            PrimitiveType::U32 => \"UInt32\".to_string(),\n            PrimitiveType::I64 => \"Int64\".to_string(),\n            PrimitiveType::U64 => \"UInt64\".to_string(),\n            PrimitiveType::F32 => \"Float\".to_string(),\n            PrimitiveType::F64 => \"Double\".to_string(),\n            PrimitiveType::I128 => \"Int128\".to_string(),\n            PrimitiveType::U128 => \"UInt128\".to_string(),\n            PrimitiveType::I256 => \"Int256\".to_string(),\n            PrimitiveType::U256 => \"UInt256\".to_string(),\n        },\n        AlgebraicTypeUse::String => \"String\".to_string(),\n        AlgebraicTypeUse::Identity => \"Identity\".to_string(),\n        AlgebraicTypeUse::ConnectionId => \"ConnectionId\".to_string(),\n        AlgebraicTypeUse::Timestamp => \"Timestamp\".to_string(),\n        AlgebraicTypeUse::TimeDuration => \"TimeDuration\".to_string(),\n        AlgebraicTypeUse::ScheduleAt => \"ScheduleAt\".to_string(),\n        AlgebraicTypeUse::Uuid => \"Uuid\".to_string(),\n        AlgebraicTypeUse::Unit => \"Unit\".to_string(),\n        AlgebraicTypeUse::Array(elem) => {\n            // Generate specific array types based on element type\n            let elem_name = get_array_element_type_name(module_prefix, module, elem);\n            format!(\"Vec{elem_name}\")\n        }\n        AlgebraicTypeUse::Ref(r) => type_ref_name(module_prefix, module, *r),\n        AlgebraicTypeUse::Option(inner) => {\n            // Handle optional types like Option<String>\n            let inner_name = get_type_name_for_result(module_prefix, module, inner);\n            format!(\"Optional{inner_name}\")\n        }\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            // Handle nested results like Result<Result<i32, String>, bool>\n            get_result_type_name(module_prefix, module, ok_ty, err_ty)\n        }\n        AlgebraicTypeUse::Never => \"Never\".to_string(),\n    }\n}\n\n// Generate the content for a result type file\nfn generate_result_type(\n    module_prefix: &str,\n    ok_type_name: &str,\n    err_type_name: &str,\n    module: &ModuleDef,\n    api_macro: &str,\n    module_name: &str,\n) -> String {\n    // Determine the C++ type for the ok value\n    let cpp_ok_type = determine_cpp_type_for_result(module_prefix, ok_type_name, module, module_name);\n\n    // Determine the C++ type for the err value\n    let cpp_err_type = determine_cpp_type_for_result(module_prefix, err_type_name, module, module_name);\n\n    // Determine if we need extra includes\n    let mut extra_includes = vec![];\n    add_includes_for_type_name(ok_type_name, &mut extra_includes, module_name);\n    add_includes_for_type_name(err_type_name, &mut extra_includes, module_name);\n\n    let extra_includes_refs: Vec<&str> = extra_includes.iter().map(|s| s.as_str()).collect();\n    let module_name_pascal = module_name.to_case(Case::Pascal);\n    let result_name = format!(\"Result{ok_type_name}{err_type_name}\");\n    let struct_name = format!(\"F{module_name_pascal}{result_name}\");\n    let header_name = format!(\"{module_name_pascal}{result_name}\");\n    let mut output = UnrealCppAutogen::new(&extra_includes_refs, &header_name, false);\n\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} {struct_name}\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"GENERATED_BODY()\");\n    writeln!(output);\n\n    // Is ok flag\n    writeln!(\n        output,\n        \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\")\"\n    );\n    writeln!(output, \"bool bIsOk = false;\");\n    writeln!(output);\n\n    // The ok value\n    let is_ok_blueprintable = is_type_name_blueprintable(ok_type_name);\n    if is_ok_blueprintable {\n        writeln!(output, \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\", meta = (EditCondition = \\\"bIsOk\\\"))\");\n    } else {\n        writeln!(\n            output,\n            \"// NOTE: {cpp_ok_type} field not exposed to Blueprint due to non-blueprintable type\"\n        );\n    }\n    writeln!(output, \"{cpp_ok_type} OkValue = {{}};\");\n    writeln!(output);\n\n    // The err value\n    let is_err_blueprintable = is_type_name_blueprintable(err_type_name);\n    if is_err_blueprintable {\n        writeln!(output, \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\", meta = (EditCondition = \\\"!bIsOk\\\"))\");\n    } else {\n        writeln!(\n            output,\n            \"// NOTE: {cpp_err_type} field not exposed to Blueprint due to non-blueprintable type\"\n        );\n    }\n    writeln!(output, \"{cpp_err_type} ErrValue = {{}};\");\n    writeln!(output);\n\n    // Constructors\n    writeln!(output, \"{struct_name}() = default;\");\n    writeln!(output);\n\n    // Ok constructor\n    writeln!(output, \"static {struct_name} Ok(const {cpp_ok_type}& InValue)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"{struct_name} Result;\");\n    writeln!(output, \"Result.bIsOk = true;\");\n    writeln!(output, \"Result.OkValue = InValue;\");\n    writeln!(output, \"return Result;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Err constructor\n    writeln!(output, \"static {struct_name} Err(const {cpp_err_type}& InValue)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"{struct_name} Result;\");\n    writeln!(output, \"Result.bIsOk = false;\");\n    writeln!(output, \"Result.ErrValue = InValue;\");\n    writeln!(output, \"return Result;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Helper methods\n    writeln!(output, \"bool IsOk() const {{ return bIsOk; }}\");\n    writeln!(output, \"bool IsErr() const {{ return !bIsOk; }}\");\n    writeln!(output);\n\n    writeln!(output, \"const {cpp_ok_type}& GetOk() const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"check(bIsOk);\");\n    writeln!(output, \"return OkValue;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    writeln!(output, \"const {cpp_err_type}& GetErr() const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"check(!bIsOk);\");\n    writeln!(output, \"return ErrValue;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Operators\n    writeln!(output, \"FORCEINLINE bool operator==(const {struct_name}& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"if (bIsOk != Other.bIsOk) return false;\");\n    writeln!(output, \"if (bIsOk)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"return OkValue == Other.OkValue;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output, \"else\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"return ErrValue == Other.ErrValue;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    writeln!(output, \"FORCEINLINE bool operator!=(const {struct_name}& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"return !(*this == Other);\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n\n    output.dedent(1);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // Add GetTypeHash implementation\n    writeln!(output, \"/**\");\n    writeln!(output, \" * Custom hash function for {struct_name}.\");\n    writeln!(output, \" * Hashes the bIsOk flag and the appropriate value.\");\n    writeln!(output, \" * @param Result The {struct_name} instance to hash.\");\n    writeln!(output, \" * @return The combined hash value.\");\n    writeln!(output, \" */\");\n    writeln!(output, \"FORCEINLINE uint32 GetTypeHash(const {struct_name}& Result)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"uint32 Hash = GetTypeHash(Result.bIsOk);\");\n    writeln!(output, \"if (Result.bIsOk)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"Hash = HashCombine(Hash, GetTypeHash(Result.OkValue));\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output, \"else\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"Hash = HashCombine(Hash, GetTypeHash(Result.ErrValue));\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output, \"return Hash;\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // BSATN serialization support\n    writeln!(output, \"namespace UE::SpacetimeDB\");\n    writeln!(output, \"{{\");\n\n    writeln!(output, \"    UE_SPACETIMEDB_ENABLE_TARRAY({struct_name});\");\n    writeln!(output);\n    writeln!(\n        output,\n        \"    UE_SPACETIMEDB_RESULT({struct_name}, bIsOk, OkValue, ErrValue);\"\n    );\n    writeln!(output, \"}}\");\n\n    output.into_inner()\n}\n\nfn autogen_cpp_struct(\n    module_prefix: &str,\n    module: &ModuleDef,\n    name: &str, // Changed to &str\n    product_type: &ProductTypeDef,\n    api_macro: &str,\n    module_name: &str,\n) -> String {\n    let mut headers = HashSet::<String>::new();\n\n    for (_, field_ty) in product_type {\n        collect_includes_for_type(module_prefix, module, field_ty, &mut headers, module_name);\n    }\n\n    // Convert to `Vec<&str>` so UnrealCppAutogen::new works\n    let mut slices: Vec<&str> = headers.iter().map(|s| s.as_str()).collect();\n    slices.sort(); // make output deterministic\n\n    // --------- start writing file ---------\n    let type_name = format!(\"{name}Type\");\n    let mut output = UnrealCppAutogen::new(&slices, &type_name, false);\n\n    writeln!(output, \"USTRUCT(BlueprintType)\");\n    writeln!(output, \"struct {api_macro} F{name}Type\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"GENERATED_BODY()\");\n    writeln!(output);\n\n    for (orig_name, ty) in product_type.into_iter() {\n        let field_name = orig_name.deref().to_case(Case::Pascal);\n        let ty_str = cpp_ty_fmt_with_module(module_prefix, module, ty, module_name).to_string();\n        let init_str = cpp_ty_init_fmt_impl(module_prefix, module, ty);\n        let field_decl = format!(\"{ty_str} {field_name}{init_str}\");\n\n        // Check if the type is blueprintable\n        if is_blueprintable(module, ty) {\n            writeln!(\n                output,\n                \"UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = \\\"SpacetimeDB\\\")\"\n            );\n        } else {\n            // Add comment explaining why the field isn't exposed as UPROPERTY\n            writeln!(\n                output,\n                \"// NOTE: {ty_str} field not exposed to Blueprint due to non-blueprintable elements\"\n            );\n        }\n        writeln!(output, \"{field_decl};\");\n        writeln!(output);\n    }\n\n    // Generate operator== and operator!=\n    writeln!(output, \"FORCEINLINE bool operator==(const F{name}Type& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n\n    // Generate comparison for each field\n    let mut comparisons = Vec::new();\n    for (orig_name, _) in product_type.into_iter() {\n        let field_name = orig_name.deref().to_case(Case::Pascal);\n\n        // For value types, direct comparison\n        comparisons.push(format!(\"{field_name} == Other.{field_name}\"));\n    }\n\n    if comparisons.is_empty() {\n        writeln!(output, \"return true;\");\n    } else {\n        writeln!(output, \"return {};\", comparisons.join(\" && \"));\n    }\n\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    writeln!(output, \"FORCEINLINE bool operator!=(const F{name}Type& Other) const\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n    writeln!(output, \"return !(*this == Other);\");\n    output.dedent(1);\n    writeln!(output, \"}}\");\n\n    output.dedent(1);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // Add GetTypeHash implementation\n    writeln!(output, \"/**\");\n    writeln!(output, \" * Custom hash function for F{name}Type.\");\n    writeln!(\n        output,\n        \" * Combines the hashes of all fields that are compared in operator==.\"\n    );\n    writeln!(output, \" * @param {name}Type The F{name}Type instance to hash.\");\n    writeln!(output, \" * @return The combined hash value.\");\n    writeln!(output, \" */\");\n    writeln!(output, \"FORCEINLINE uint32 GetTypeHash(const F{name}Type& {name}Type)\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n\n    // Generate hash combining for each field\n    let field_names: Vec<String> = product_type\n        .into_iter()\n        .map(|(name, _)| name.deref().to_case(Case::Pascal))\n        .collect();\n\n    if field_names.is_empty() {\n        writeln!(output, \"return 0; // Empty struct\");\n    } else {\n        // First field\n        writeln!(output, \"uint32 Hash = GetTypeHash({}Type.{});\", name, field_names[0]);\n\n        // Combine with remaining fields\n        for field_name in field_names.iter().skip(1) {\n            writeln!(\n                output,\n                \"Hash = HashCombine(Hash, GetTypeHash({name}Type.{field_name}));\"\n            );\n        }\n\n        writeln!(output, \"return Hash;\");\n    }\n\n    output.dedent(1);\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Add BSATN serialization support\n    writeln!(output, \"namespace UE::SpacetimeDB\");\n    writeln!(output, \"{{\");\n\n    writeln!(output, \"    UE_SPACETIMEDB_ENABLE_TARRAY(F{name}Type);\");\n\n    writeln!(output);\n\n    if field_names.is_empty() {\n        writeln!(output, \"    UE_SPACETIMEDB_STRUCT_EMPTY(F{name}Type);\");\n    } else {\n        writeln!(\n            output,\n            \"    UE_SPACETIMEDB_STRUCT(F{}Type, {});\",\n            name,\n            field_names.join(\", \")\n        );\n    }\n\n    writeln!(output, \"}}\");\n\n    output.into_inner()\n}\n\nfn is_primitive_or_fstring(ty: &AlgebraicTypeUse) -> bool {\n    matches!(ty, AlgebraicTypeUse::Primitive(_) | AlgebraicTypeUse::String)\n}\n\n// Helper function to check if a type should be passed by value (only true primitives)\nfn should_pass_by_value_in_delegate(_module: &ModuleDef, ty: &AlgebraicTypeUse) -> bool {\n    match ty {\n        // Only basic C++ primitives should be passed by value in delegates\n        AlgebraicTypeUse::Primitive(p) => match p {\n            PrimitiveType::Bool\n            | PrimitiveType::I8\n            | PrimitiveType::U8\n            | PrimitiveType::I16\n            | PrimitiveType::U16\n            | PrimitiveType::I32\n            | PrimitiveType::U32\n            | PrimitiveType::I64\n            | PrimitiveType::U64\n            | PrimitiveType::F32\n            | PrimitiveType::F64 => true,\n            // These are mapped to FSpacetimeDB types which are USTRUCTs\n            PrimitiveType::I128 | PrimitiveType::U128 | PrimitiveType::I256 | PrimitiveType::U256 => false,\n        },\n        AlgebraicTypeUse::Unit => true,\n        // ALL built-in SpacetimeDB types are USTRUCTs, use const references\n        AlgebraicTypeUse::String => false,\n        AlgebraicTypeUse::Identity => false,     // FSpacetimeDBIdentity is a USTRUCT\n        AlgebraicTypeUse::ConnectionId => false, // FSpacetimeDBConnectionId is a USTRUCT\n        AlgebraicTypeUse::Timestamp => false,    // FSpacetimeDBTimestamp is a USTRUCT\n        AlgebraicTypeUse::TimeDuration => false, // FSpacetimeDBTimeDuration is a USTRUCT\n        AlgebraicTypeUse::Uuid => false,         // FSpacetimeDBUuid is a USTRUCT\n        // Custom structs/enums use const references\n        AlgebraicTypeUse::Ref(_) => false,\n        AlgebraicTypeUse::Array(_) => false, // Arrays use const references\n        AlgebraicTypeUse::Option(inner) => should_pass_by_value_in_delegate(_module, inner),\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            should_pass_by_value_in_delegate(_module, ok_ty) && should_pass_by_value_in_delegate(_module, err_ty)\n        }\n        AlgebraicTypeUse::ScheduleAt => false,\n        AlgebraicTypeUse::Never => false,\n    }\n}\n\n// Helper function to check if a type is blueprintable as a field (UPROPERTY)\nfn is_blueprintable(module: &ModuleDef, ty: &AlgebraicTypeUse) -> bool {\n    match ty {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            // These types are not blueprintable in Unreal Engine\n            PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::U64 => {\n                false\n            }\n            // All other primitive types are blueprintable\n            _ => true,\n        },\n        // Arrays are blueprintable if their element type is a struct/enum/primitive that can be used in Blueprint\n        // TArray<USTRUCT> is always blueprintable, even if the USTRUCT has non-blueprintable fields\n        AlgebraicTypeUse::Array(inner) => match inner.as_ref() {\n            AlgebraicTypeUse::Ref(r) => {\n                // TArray<CustomType> is blueprintable if CustomType is a UStruct/UEnum\n                match &module.typespace_for_generate()[*r] {\n                    AlgebraicTypeDef::Product(_) => true,   // TArray<USTRUCT> is always blueprintable\n                    AlgebraicTypeDef::Sum(_) => true,       // TArray<UENUM> is always blueprintable\n                    AlgebraicTypeDef::PlainEnum(_) => true, // TArray<PlainEnum> is always blueprintable\n                }\n            }\n            // For primitive arrays, check if the primitive is blueprintable\n            _ => is_blueprintable(module, inner),\n        },\n        AlgebraicTypeUse::String => true,\n        AlgebraicTypeUse::Identity => true,\n        AlgebraicTypeUse::ConnectionId => true,\n        AlgebraicTypeUse::Timestamp => true,\n        AlgebraicTypeUse::TimeDuration => true,\n        AlgebraicTypeUse::Uuid => true,\n        AlgebraicTypeUse::ScheduleAt => true, // ScheduleAt is blueprintable as a property (TObjectPtr)\n        AlgebraicTypeUse::Unit => true,\n        AlgebraicTypeUse::Ref(r) => {\n            // For struct/class fields, USTRUCTs and UENUMs are always blueprintable as field types\n            // The individual fields within the struct may not be accessible, but the struct itself can be used\n            match &module.typespace_for_generate()[*r] {\n                AlgebraicTypeDef::Product(_) => true, // USTRUCTs are always blueprintable as field types\n                AlgebraicTypeDef::Sum(_) => true,     // Sum types are blueprintable\n                AlgebraicTypeDef::PlainEnum(_) => true, // Enums are blueprintable\n            }\n        }\n        AlgebraicTypeUse::Option(inner) => is_blueprintable(module, inner),\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            is_blueprintable(module, ok_ty) && is_blueprintable(module, err_ty)\n        }\n        AlgebraicTypeUse::Never => false,\n    }\n}\n\n// Helper function to check if a type can be used as a delegate parameter\n// For delegates, structs are blueprintable even if they contain non-blueprintable fields\nfn is_type_blueprintable_for_delegates(module: &ModuleDef, ty: &AlgebraicTypeUse) -> bool {\n    match ty {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            // These types are not blueprintable in Unreal Engine\n            PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::U64 => {\n                false\n            }\n            // All other primitive types are blueprintable\n            _ => true,\n        },\n        // Arrays are blueprintable only if their element type is blueprintable for delegates\n        AlgebraicTypeUse::Array(inner) => is_type_blueprintable_for_delegates(module, inner),\n        AlgebraicTypeUse::String => true,\n        AlgebraicTypeUse::Identity => true,\n        AlgebraicTypeUse::ConnectionId => true,\n        AlgebraicTypeUse::Timestamp => true,\n        AlgebraicTypeUse::TimeDuration => true,\n        AlgebraicTypeUse::Uuid => true,\n        AlgebraicTypeUse::ScheduleAt => true,\n        AlgebraicTypeUse::Unit => true,\n        AlgebraicTypeUse::Ref(r) => {\n            // For delegate parameters, any USTRUCT is blueprintable regardless of its fields\n            match &module.typespace_for_generate()[*r] {\n                AlgebraicTypeDef::Product(_) => true, // All structs are blueprintable as delegate params\n                _ => true,                            // Enums and sum types are generally blueprintable\n            }\n        }\n        AlgebraicTypeUse::Option(inner) => is_type_blueprintable_for_delegates(module, inner),\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            is_type_blueprintable_for_delegates(module, ok_ty) && is_type_blueprintable_for_delegates(module, err_ty)\n        }\n        AlgebraicTypeUse::Never => false,\n    }\n}\n\n// Helper function to generate a C++ enum definition\nfn autogen_cpp_enum(name: &str, enum_type: &PlainEnumTypeDef) -> String {\n    // Changed name to &str\n\n    let type_name = format!(\"{name}Type\");\n    let mut output = UnrealCppAutogen::new(&[], &type_name, false);\n    writeln!(output, \"UENUM(BlueprintType)\");\n    writeln!(output, \"enum class E{name}Type : uint8\");\n    writeln!(output, \"{{\");\n    output.indent(1);\n\n    for variant in &*enum_type.variants {\n        writeln!(output, \"{},\", variant.deref().to_case(Case::Pascal)); // Ensure variants are PascalCase, common for UE enums\n    }\n\n    output.dedent(1);\n    writeln!(output, \"}};\");\n    writeln!(output);\n\n    // Add BSATN serialization support\n    writeln!(output, \"namespace UE::SpacetimeDB\");\n    writeln!(output, \"{{\");\n\n    writeln!(output, \"    UE_SPACETIMEDB_ENABLE_TARRAY(E{name}Type);\");\n\n    writeln!(output, \"}}\");\n\n    output.into_inner()\n}\n\nfn autogen_cpp_sum(\n    module_prefix: &str,\n    module: &ModuleDef,\n    name: &str,\n    sum_type: &SumTypeDef,\n    api_macro: &str,\n    module_name: &str,\n) -> String {\n    use convert_case::Case;\n\n    /* ------------------------------------------------------------------ */\n    /* 1. gather #includes for every variant payload -------------------- */\n    /* ------------------------------------------------------------------ */\n    let mut includes = HashSet::<String>::new();\n    for (_, alg_ty) in &sum_type.variants {\n        collect_includes_for_type(module_prefix, module, alg_ty, &mut includes, module_name);\n    }\n\n    includes.insert(\"Kismet/BlueprintFunctionLibrary.h\".to_string());\n\n    let include_slices: Vec<&str> = includes.iter().map(|s| s.as_str()).collect();\n\n    // /* ------------------------------------------------------------------ */\n    // /* 2. emit boiler-plate + forward decls ----------------------------- */\n    // /* ------------------------------------------------------------------ */\n    let type_name = format!(\"{name}Type\");\n    let mut output = UnrealCppAutogen::new(&include_slices, &type_name, false);\n\n    /* ------------------------------------------------------------------ */\n    /* 3. generate the tag enum ----------------------------------------- */\n    /* ------------------------------------------------------------------ */\n    writeln!(output, \"UENUM(BlueprintType)\\nenum class E{name}Tag : uint8\\n{{\");\n\n    let mut variant_type = HashSet::<String>::new();\n\n    for (ix, (variant, _variant_type)) in sum_type.variants.iter().enumerate() {\n        let comma = if ix + 1 == sum_type.variants.len() { \"\" } else { \",\" };\n        writeln!(output, \"    {}{}\", variant.to_case(Case::Pascal), comma);\n\n        let variant_cpp_type = cpp_ty_fmt_with_module(module_prefix, module, _variant_type, module_name).to_string();\n        variant_type.insert(variant_cpp_type);\n    }\n    writeln!(output, \"}};\\n\");\n\n    /* ------------------------------------------------------------------ */\n    /* 4. generate the UStruct ----------------------------------- */\n    /* ------------------------------------------------------------------ */\n\n    writeln!(output, \"USTRUCT(BlueprintType)\\nstruct {api_macro} F{name}Type\\n{{\");\n    writeln!(output, \"    GENERATED_BODY()\\n\");\n\n    // default ctor\n    writeln!(output, \"public:\\n    F{name}Type() = default;\\n\");\n\n    // data + tag\n    writeln!(\n        output,\n        \"    TVariant<{}> MessageData;\",\n        variant_type\n            .iter()\n            .map(|s| s.as_str())\n            .collect::<Vec<&str>>()\n            .join(\", \")\n    );\n\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    UPROPERTY(BlueprintReadOnly)\\n    E{name}Tag Tag = static_cast<E{name}Tag>(0);\\n\"\n    );\n\n    /* 4a. Static factories per variant -------------------------------- */\n    for (variant_name, variant_type) in &sum_type.variants {\n        let pas = variant_name.to_case(Case::Pascal);\n        let variant_cpp_type = cpp_ty_fmt_with_module(module_prefix, module, variant_type, module_name).to_string();\n        let param_type = format!(\"const {variant_cpp_type}& \");\n\n        writeln!(output, \"    static F{name}Type {pas}({param_type}Value)\\n    {{\");\n\n        writeln!(output, \"        F{name}Type Obj;\");\n        writeln!(output, \"        Obj.Tag = E{name}Tag::{pas};\");\n\n        writeln!(\n            output,\n            \"        Obj.MessageData.Set<{variant_cpp_type}>(Value);\\n        return Obj;\\n    }}\\n\"\n        );\n    }\n\n    /* 4b. Get/Is helpers ---------------------------------------------- */\n    for (variant_name, variant_type) in &sum_type.variants {\n        let pas = variant_name.to_case(Case::Pascal);\n        let variant_cpp_type = cpp_ty_fmt_with_module(module_prefix, module, variant_type, module_name).to_string();\n\n        // Is*\n        writeln!(\n            output,\n            \"    FORCEINLINE bool Is{pas}() const {{ return Tag == E{name}Tag::{pas}; }}\"\n        );\n\n        // GetAs*\n        writeln!(\n            output,\n            \"\\n    FORCEINLINE {variant_cpp_type} GetAs{pas}() const\\n    {{\"\n        );\n\n        writeln!(\n            output,\n            \"        ensureMsgf(Is{pas}(), TEXT(\\\"MessageData does not hold {pas}!\\\"));\\n        return MessageData.Get<{variant_cpp_type}>();\\n    }}\\n\"\n        );\n    }\n\n    // Add inline equality operators\n    writeln!(output, \"    // Inline equality operators\");\n    writeln!(\n        output,\n        \"    FORCEINLINE bool operator==(const F{name}Type& Other) const\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        if (Tag != Other.Tag) return false;\");\n    writeln!(output);\n    writeln!(output, \"        switch (Tag)\");\n    writeln!(output, \"        {{\");\n\n    // Generate cases for each variant\n    for (variant_name, _variant_type) in &sum_type.variants {\n        let pas = variant_name.to_case(Case::Pascal);\n\n        writeln!(output, \"            case E{name}Tag::{pas}:\");\n        writeln!(\n            output,\n            \"                return GetAs{variant_name}() == Other.GetAs{variant_name}();\"\n        );\n    }\n\n    writeln!(output, \"            default:\");\n    writeln!(output, \"                return false;\");\n    writeln!(output, \"        }}\");\n    writeln!(output, \"    }}\");\n    writeln!(output);\n\n    writeln!(\n        output,\n        \"    FORCEINLINE bool operator!=(const F{name}Type& Other) const\"\n    );\n    writeln!(output, \"    {{\");\n    writeln!(output, \"        return !(*this == Other);\");\n    writeln!(output, \"    }}\");\n\n    writeln!(output, \"}};\\n\");\n\n    // Add GetTypeHash implementation\n    writeln!(output, \"/**\");\n    writeln!(output, \" * Custom hash function for F{name}Type.\");\n    writeln!(\n        output,\n        \" * Combines the hashes of all fields that are compared in operator==.\"\n    );\n    writeln!(output, \" * @param {name}Type The F{name}Type instance to hash.\");\n    writeln!(output, \" * @return The combined hash value.\");\n    writeln!(output, \" */\");\n    writeln!(output, \"FORCEINLINE uint32 GetTypeHash(const F{name}Type& {name})\");\n    writeln!(output, \"{{\");\n    writeln!(\n        output,\n        \"    const uint32 TagHash = GetTypeHash(static_cast<uint8>({name}.Tag));\"\n    );\n\n    writeln!(output, \"    switch ({name}.Tag)\");\n    writeln!(output, \"    {{\");\n\n    output.indent(1);\n\n    for (variant_name, variant_type) in &sum_type.variants {\n        let pascal_variant = variant_name.deref().to_case(Case::Pascal);\n\n        let hash_fn_prefix = if is_primitive_or_fstring(variant_type) {\n            \"GetTypeHash\"\n        } else {\n            \"::GetTypeHash\"\n        };\n\n        writeln!(\n            output,\n            \"    case E{name}Tag::{pascal_variant}: return HashCombine(TagHash, {hash_fn_prefix}({name}.GetAs{pascal_variant}()));\"\n        );\n    }\n\n    writeln!(output, \"    default: return TagHash;\");\n\n    output.dedent(1);\n    writeln!(output, \"    }}\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Add BSATN serialization support for tagged variant\n    writeln!(output, \"namespace UE::SpacetimeDB\");\n    writeln!(output, \"{{\");\n\n    writeln!(output, \"    UE_SPACETIMEDB_ENABLE_TARRAY(F{name}Type);\");\n\n    writeln!(output);\n\n    writeln!(output, \"    UE_SPACETIMEDB_TAGGED_ENUM(\");\n    writeln!(output, \"        F{name}Type,\");\n    writeln!(output, \"        E{name}Tag,\");\n    writeln!(output, \"        MessageData,\");\n\n    // Generate the variant list\n    let variant_count = sum_type.variants.len();\n    for (idx, (variant_name, variant_type)) in sum_type.variants.iter().enumerate() {\n        let variant_pascal = variant_name.to_case(Case::Pascal);\n        let variant_cpp_type = cpp_ty_fmt_with_module(module_prefix, module, variant_type, module_name).to_string();\n\n        if idx < variant_count - 1 {\n            writeln!(output, \"        {variant_pascal}, {variant_cpp_type},\");\n        } else {\n            writeln!(output, \"        {variant_pascal}, {variant_cpp_type}\");\n        }\n    }\n\n    writeln!(output, \"    );\");\n    writeln!(output, \"}}\");\n    writeln!(output);\n\n    // Blueprint Function Library support for BPs\n    writeln!(\n        output,\n        \"UCLASS()\\nclass {api_macro} U{name}BpLib : public UBlueprintFunctionLibrary\\n{{\"\n    );\n    writeln!(output, \"    GENERATED_BODY()\\n\");\n\n    writeln!(output, \"private:\");\n\n    for (variant_name, variant_type) in &sum_type.variants {\n        let pas = variant_name.to_case(Case::Pascal);\n        let variant_cpp_type = cpp_ty_fmt_with_module(module_prefix, module, variant_type, module_name).to_string();\n\n        // ctor functions\n        if is_blueprintable(module, variant_type) {\n            writeln!(\n                output,\n                \"    UFUNCTION(BlueprintCallable, Category = \\\"SpacetimeDB|{name}\\\")\"\n            );\n        } else {\n            writeln!(\n                output,\n                \"    // NOTE: Not exposed to Blueprint because {variant_cpp_type} types are not Blueprint-compatible\"\n            );\n        }\n\n        writeln!(\n            output,\n            \"    static F{name}Type {pas}(const {variant_cpp_type}& InValue)\"\n        );\n        writeln!(output, \"    {{\");\n        writeln!(output, \"        return F{name}Type::{pas}(InValue);\");\n        writeln!(output, \"    }}\");\n        writeln!(output);\n\n        // Is*\n        writeln!(\n            output,\n            \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{name}\\\")\"\n        );\n        writeln!(\n            output,\n            \"    static bool Is{pas}(const F{name}Type& InValue) {{ return InValue.Is{pas}(); }}\"\n        );\n\n        writeln!(output);\n\n        // GetAs*\n        if is_blueprintable(module, variant_type) {\n            writeln!(\n                output,\n                \"    UFUNCTION(BlueprintPure, Category = \\\"SpacetimeDB|{name}\\\")\"\n            );\n        } else {\n            writeln!(\n                output,\n                \"    // NOTE: Not exposed to Blueprint because {variant_cpp_type} types are not Blueprint-compatible\"\n            );\n        }\n        writeln!(\n            output,\n            \"    static {variant_cpp_type} GetAs{pas}(const F{name}Type& InValue)\\n    {{\"\n        );\n\n        writeln!(output, \"        return InValue.GetAs{pas}();\\n    }}\\n\");\n    }\n\n    writeln!(output, \"}};\");\n\n    output.into_inner()\n}\n\n// Helper trait for Identifier case conversion\ntrait IdentifierCasing {\n    fn to_case(&self, case: Case) -> String;\n}\n\nimpl IdentifierCasing for Identifier {\n    fn to_case(&self, case: Case) -> String {\n        self.deref().to_case(case)\n    }\n}\n\nfn cpp_ty_fmt_with_module<'a>(\n    module_prefix: &'a str,\n    module: &'a ModuleDef,\n    ty: &'a AlgebraicTypeUse,\n    module_name: &'a str,\n) -> impl fmt::Display + 'a {\n    cpp_ty_fmt_impl(module_prefix, module, ty, module_name)\n}\n\nfn cpp_ty_fmt_blueprint_compatible<'a>(\n    module_prefix: &'a str,\n    module: &'a ModuleDef,\n    ty: &'a AlgebraicTypeUse,\n    module_name: &'a str,\n) -> impl fmt::Display + 'a {\n    cpp_ty_fmt_blueprint_impl(module_prefix, module, ty, module_name)\n}\n\nfn cpp_ty_fmt_blueprint_impl<'a>(\n    module_prefix: &'a str,\n    module: &'a ModuleDef,\n    ty: &'a AlgebraicTypeUse,\n    module_name: &'a str,\n) -> impl fmt::Display + 'a {\n    fmt_fn(move |f| match ty {\n        AlgebraicTypeUse::Array(elem) => {\n            let elem_type = cpp_ty_fmt_with_module(module_prefix, module, elem, module_name).to_string();\n            write!(f, \"TArray<{elem_type}>\")\n        }\n        // For all other types, use the regular implementation\n        _ => {\n            let display_obj = cpp_ty_fmt_with_module(module_prefix, module, ty, module_name);\n            write!(f, \"{display_obj}\")\n        }\n    })\n}\n\nfn cpp_ty_fmt_impl<'a>(\n    module_prefix: &'a str,\n    module: &'a ModuleDef,\n    ty: &'a AlgebraicTypeUse,\n    module_name: &'a str,\n) -> impl fmt::Display + 'a {\n    fmt_fn(move |f| match ty {\n        // --------- primitives etc ---------\n        AlgebraicTypeUse::Primitive(p) => f.write_str(match p {\n            PrimitiveType::Bool => \"bool\",\n            PrimitiveType::I8 => \"int8\",\n            PrimitiveType::U8 => \"uint8\",\n            PrimitiveType::I16 => \"int16\",\n            PrimitiveType::U16 => \"uint16\",\n            PrimitiveType::I32 => \"int32\",\n            PrimitiveType::U32 => \"uint32\",\n            PrimitiveType::I64 => \"int64\",\n            PrimitiveType::U64 => \"uint64\",\n            PrimitiveType::F32 => \"float\",\n            PrimitiveType::F64 => \"double\",\n            PrimitiveType::I128 => \"FSpacetimeDBInt128\",\n            PrimitiveType::U128 => \"FSpacetimeDBUInt128\",\n            PrimitiveType::I256 => \"FSpacetimeDBInt256\",\n            PrimitiveType::U256 => \"FSpacetimeDBUInt256\",\n        }),\n\n        AlgebraicTypeUse::Array(elem) if matches!(elem.as_ref(), AlgebraicTypeUse::Primitive(PrimitiveType::U8)) => {\n            f.write_str(\"TArray<uint8>\")\n        }\n\n        AlgebraicTypeUse::Array(elem) => {\n            let elem_type = cpp_ty_fmt_impl(module_prefix, module, elem, module_name).to_string();\n            write!(f, \"TArray<{elem_type}>\")\n        }\n\n        AlgebraicTypeUse::String => f.write_str(\"FString\"),\n        AlgebraicTypeUse::Identity => f.write_str(\"FSpacetimeDBIdentity\"),\n        AlgebraicTypeUse::ConnectionId => f.write_str(\"FSpacetimeDBConnectionId\"),\n        AlgebraicTypeUse::Timestamp => f.write_str(\"FSpacetimeDBTimestamp\"),\n        AlgebraicTypeUse::TimeDuration => f.write_str(\"FSpacetimeDBTimeDuration\"),\n        AlgebraicTypeUse::Uuid => f.write_str(\"FSpacetimeDBUuid\"),\n        AlgebraicTypeUse::ScheduleAt => f.write_str(\"FSpacetimeDBScheduleAt\"),\n        AlgebraicTypeUse::Unit => f.write_str(\"FSpacetimeDBUnit\"),\n\n        // --------- references to user-defined types ---------\n        AlgebraicTypeUse::Ref(r) => {\n            let scoped = type_ref_name(module_prefix, module, *r); // PascalCase\n            match &module.typespace_for_generate()[*r] {\n                AlgebraicTypeDef::PlainEnum(_) => write!(f, \"E{scoped}Type\"), // enum → EFooType\n                AlgebraicTypeDef::Product(_) => write!(f, \"F{scoped}Type\"),   // struct/record → FFooType\n                AlgebraicTypeDef::Sum(_) => write!(f, \"F{scoped}Type\"),       // sum type → FFooType (UStruct)\n            }\n        }\n\n        // Options use the generated optional types\n        AlgebraicTypeUse::Option(inner) => {\n            let optional_name = get_optional_type_name(module_prefix, module, inner);\n            if module_name.is_empty() {\n                write!(f, \"F{optional_name}\")\n            } else {\n                let module_name_pascal = module_name.to_case(Case::Pascal);\n                write!(f, \"F{module_name_pascal}{optional_name}\")\n            }\n        }\n\n        // Result use the generated result types\n        AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n            let ok_name = get_type_name_for_result(module_prefix, module, ok_ty);\n            let err_name = get_type_name_for_result(module_prefix, module, err_ty);\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n\n            write!(f, \"F{module_name_pascal}Result{ok_name}{err_name}\")\n        }\n\n        AlgebraicTypeUse::Never => unreachable!(\"never type\"),\n    })\n}\n\n// For UPROPERTY() Unreal expects initialization values for certain types.\n// UE5 strict mode requires all UPROPERTY fields to be explicitly initialized,\n// otherwise the engine logs \"property not initialized properly\" errors.\n// This includes primitives (bool defaults to true if not initialized to false),\n// and enum types (must be initialized to a valid enum value).\nfn cpp_ty_init_fmt_impl(module_prefix: &str, module: &ModuleDef, ty: &AlgebraicTypeUse) -> String {\n    match ty {\n        AlgebraicTypeUse::Primitive(p) => match p {\n            PrimitiveType::Bool => \" = false\".to_string(),\n            PrimitiveType::I8 => \" = 0\".to_string(),\n            PrimitiveType::U8 => \" = 0\".to_string(),\n            PrimitiveType::I16 => \" = 0\".to_string(),\n            PrimitiveType::U16 => \" = 0\".to_string(),\n            PrimitiveType::I32 => \" = 0\".to_string(),\n            PrimitiveType::U32 => \" = 0\".to_string(),\n            PrimitiveType::I64 => \" = 0\".to_string(),\n            PrimitiveType::U64 => \" = 0\".to_string(),\n            PrimitiveType::F32 => \" = 0.0f\".to_string(),\n            PrimitiveType::F64 => \" = 0.0\".to_string(),\n            PrimitiveType::I128 => String::new(),\n            PrimitiveType::U128 => String::new(),\n            PrimitiveType::I256 => String::new(),\n            PrimitiveType::U256 => String::new(),\n        },\n        AlgebraicTypeUse::Array(_elem) => String::new(),\n        AlgebraicTypeUse::String => String::new(),\n        AlgebraicTypeUse::Identity => String::new(),\n        AlgebraicTypeUse::ConnectionId => String::new(),\n        AlgebraicTypeUse::Timestamp => String::new(),\n        AlgebraicTypeUse::TimeDuration => String::new(),\n        AlgebraicTypeUse::ScheduleAt => String::new(),\n        AlgebraicTypeUse::Uuid => String::new(),\n        AlgebraicTypeUse::Unit => String::new(),\n        // --------- references to user-defined types ---------\n        AlgebraicTypeUse::Ref(r) => {\n            // Enum types must be initialized to a valid enum value in UE5.\n            // Use the first variant as the default value.\n            match &module.typespace_for_generate()[*r] {\n                AlgebraicTypeDef::PlainEnum(plain_enum) => {\n                    let type_name = type_ref_name(module_prefix, module, *r);\n                    if let Some(first_variant) = plain_enum.variants.first() {\n                        let variant_name = first_variant.deref().to_case(Case::Pascal);\n                        format!(\" = E{type_name}Type::{variant_name}\")\n                    } else {\n                        String::new()\n                    }\n                }\n                // Product and Sum types have proper default constructors\n                AlgebraicTypeDef::Product(_) => String::new(),\n                AlgebraicTypeDef::Sum(_) => String::new(),\n            }\n        }\n        // Options use the generated optional types\n        AlgebraicTypeUse::Option(_inner) => String::new(),\n        // Result use the generated result types\n        AlgebraicTypeUse::Result { ok_ty: _, err_ty: _ } => String::new(),\n        AlgebraicTypeUse::Never => unreachable!(\"never type\"),\n    }\n}\n\n// Given an `AlgebraicTypeUse`, add every referenced type’s generated\n// header name (`\"<FTypeName>.g.h\"`) into `out` (a `HashSet` avoids dups).\nfn collect_includes_for_type(\n    module_prefix: &str,\n    module: &ModuleDef,\n    ty: &AlgebraicTypeUse,\n    out: &mut HashSet<String>,\n    module_name: &str,\n) {\n    use AlgebraicTypeUse::*;\n    match ty {\n        Ref(r) => {\n            let header = format!(\n                \"ModuleBindings/Types/{}Type.g.h\",\n                type_ref_name(module_prefix, module, *r)\n            );\n            out.insert(header);\n        }\n        Option(inner) => {\n            // Add the optional type header\n            let optional_name = get_optional_type_name(module_prefix, module, inner);\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            let header = format!(\"ModuleBindings/Optionals/{module_name_pascal}{optional_name}.g.h\");\n            out.insert(header);\n            // Also collect includes for the inner type\n            collect_includes_for_type(module_prefix, module, inner, out, module_name);\n        }\n        Result { ok_ty, err_ty } => {\n            // Add the result type header\n            let result_name = get_result_type_name(module_prefix, module, ok_ty, err_ty);\n            let module_name_pascal = module_name.to_case(Case::Pascal);\n            let header = format!(\"ModuleBindings/Results/{module_name_pascal}{result_name}.g.h\");\n            out.insert(header);\n            // Also collect includes for the ok and err types\n            collect_includes_for_type(module_prefix, module, ok_ty, out, module_name);\n            collect_includes_for_type(module_prefix, module, err_ty, out, module_name);\n        }\n        Array(inner) => {\n            collect_includes_for_type(module_prefix, module, inner, out, module_name);\n        }\n        // Builtin types that require Builtins.h (also includes LargeIntegers.h)\n        Identity | ConnectionId | Timestamp | TimeDuration | ScheduleAt | Uuid => {\n            out.insert(\"Types/Builtins.h\".to_string());\n        }\n        // Large integer primitives also need Builtins.h (for LargeIntegers.h)\n        Primitive(PrimitiveType::I128)\n        | Primitive(PrimitiveType::U128)\n        | Primitive(PrimitiveType::I256)\n        | Primitive(PrimitiveType::U256) => {\n            out.insert(\"Types/Builtins.h\".to_string());\n        }\n        // String type - we use FString directly, no special includes needed\n        String => {\n            // FString is a native Unreal type, no additional includes required\n        }\n        // Other primitive types - we use native C++ types directly, no special includes needed\n        Primitive(_) => {\n            // Native types like uint8, int32, float, etc. don't need special includes\n        }\n        // Unit type needs UnitType.h header\n        Unit => {\n            out.insert(\"Types/UnitType.h\".to_string());\n        }\n        // Never type needs no special headers\n        Never => {}\n    }\n}\n\n// UnrealCPP-specific type reference name function that preserves original case\nfn type_ref_name(module_prefix: &str, module: &ModuleDef, typeref: spacetimedb_lib::sats::AlgebraicTypeRef) -> String {\n    let (name, _def) = module.type_def_from_ref(typeref).unwrap();\n    // Preserve original case instead of applying Pascal case conversion\n    let base_name = name\n        .name_segments()\n        .last()\n        .map(|id| id.deref().to_string())\n        .unwrap_or_else(|| \"Unnamed\".to_string());\n    format!(\"{module_prefix}{base_name}\")\n}\n\nfn ensure_module_in_uproject(uproject_dir: &Path, module_name: &str) -> Result<(), String> {\n    let uproject_file = std::fs::read_dir(uproject_dir)\n        .map_err(|e| format!(\"Failed to read Unreal project directory '{}': {e}\", uproject_dir.display()))?\n        .filter_map(|entry| entry.ok())\n        .find(|entry| {\n            entry\n                .path()\n                .extension()\n                .and_then(|ext| ext.to_str())\n                .map(|ext| ext == \"uproject\")\n                .unwrap_or(false)\n        })\n        .ok_or_else(|| {\n            format!(\n                \"No .uproject file found in '{}'. Unreal code generation requires a project directory containing a .uproject file.\",\n                uproject_dir.display()\n            )\n        })?;\n\n    let uproject_path = uproject_file.path();\n    let content = std::fs::read_to_string(&uproject_path)\n        .map_err(|e| format!(\"Failed to read .uproject file '{}': {e}\", uproject_path.display()))?;\n\n    let mut json: serde_json::Value = serde_json::from_str(&content)\n        .map_err(|e| format!(\"Invalid JSON in .uproject file '{}': {e}\", uproject_path.display()))?;\n\n    if !json.is_object() {\n        return Err(format!(\n            \"Invalid .uproject file '{}': top-level JSON must be an object.\",\n            uproject_path.display()\n        ));\n    }\n\n    let json_obj = json\n        .as_object_mut()\n        .expect(\"validated object .uproject JSON should have an object map\");\n\n    if !json_obj.contains_key(\"Modules\") {\n        json_obj.insert(\"Modules\".to_string(), serde_json::Value::Array(vec![]));\n    }\n\n    let modules = json_obj\n        .get_mut(\"Modules\")\n        .and_then(|m| m.as_array_mut())\n        .ok_or_else(|| {\n            format!(\n                \"Invalid .uproject file '{}': 'Modules' must be an array.\",\n                uproject_path.display()\n            )\n        })?;\n\n    let module_exists = modules.iter().any(|module| {\n        module\n            .get(\"Name\")\n            .and_then(|name| name.as_str())\n            .map(|name| name == module_name)\n            .unwrap_or(false)\n    });\n\n    if !module_exists {\n        modules.push(serde_json::json!({\n            \"Name\": module_name,\n            \"Type\": \"Runtime\",\n            \"LoadingPhase\": \"Default\"\n        }));\n\n        let formatted_json = serde_json::to_string_pretty(&json)\n            .map_err(|e| format!(\"Failed to serialize .uproject file '{}': {e}\", uproject_path.display()))?;\n\n        std::fs::write(&uproject_path, formatted_json)\n            .map_err(|e| format!(\"Failed to write .uproject file '{}': {e}\", uproject_path.display()))?;\n    }\n\n    Ok(())\n}\n\nfn generate_build_cs_content(module_name: &str) -> String {\n    format!(\n        r#\"using UnrealBuildTool;\n\npublic class {module_name} : ModuleRules\n{{\n    public {module_name}(ReadOnlyTargetRules Target) : base(Target)\n    {{\n        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;\n\n        PublicDependencyModuleNames.AddRange(new string[] {{\n            \"Core\",\n            \"CoreUObject\",\n            \"Engine\",\n            \"SpacetimeDbSdk\"\n        }});\n\n        PrivateDependencyModuleNames.AddRange(new string[] {{\n        }});\n    }}\n}}\n\"#,\n        module_name = module_name\n    )\n}\n\nfn generate_module_cpp_content(module_name: &str) -> String {\n    format!(\n        r#\"#include \"{module_name}.h\"\n#include \"Modules/ModuleManager.h\"\n\nIMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, {module_name}, \"{module_name}\");\n\"#,\n        module_name = module_name\n    )\n}\n\nfn generate_module_h_content(_module_name: &str) -> String {\n    r#\"#pragma once\n\n#include \"CoreMinimal.h\"\n\"#\n    .to_string()\n}\n"
  },
  {
    "path": "crates/codegen/src/util.rs",
    "content": "//! Various utility functions that the generate modules have in common.\n\nuse std::{\n    fmt::{Display, Formatter, Result},\n    ops::Deref,\n};\n\nuse super::code_indenter::Indenter;\nuse convert_case::{Case, Casing};\nuse itertools::Itertools;\nuse spacetimedb_lib::db::raw_def::v9::TableAccess;\nuse spacetimedb_lib::sats::layout::PrimitiveType;\nuse spacetimedb_lib::version;\nuse spacetimedb_lib::{db::raw_def::v9::Lifecycle, sats::AlgebraicTypeRef};\nuse spacetimedb_primitives::ColList;\nuse spacetimedb_schema::{def::ViewDef, type_for_generate::ProductTypeDef};\nuse spacetimedb_schema::{\n    def::{ConstraintDef, ProcedureDef},\n    schema::TableSchema,\n};\nuse spacetimedb_schema::{\n    def::{IndexDef, TableDef, TypeDef},\n    type_for_generate::TypespaceForGenerate,\n};\nuse spacetimedb_schema::{\n    def::{ModuleDef, ReducerDef},\n    identifier::Identifier,\n    type_for_generate::AlgebraicTypeUse,\n};\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum CodegenVisibility {\n    IncludePrivate,\n    OnlyPublic,\n}\n\n/// Turns a closure `f: Fn(&mut Formatter) -> Result` into `fmt::Display`.\npub(super) fn fmt_fn(f: impl Fn(&mut Formatter) -> Result) -> impl Display {\n    struct FDisplay<F>(F);\n    impl<F: Fn(&mut Formatter) -> Result> Display for FDisplay<F> {\n        fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n            (self.0)(f)\n        }\n    }\n    FDisplay(f)\n}\n\npub(super) fn collect_case<'a>(case: Case, segs: impl Iterator<Item = &'a Identifier>) -> String {\n    segs.map(|s| s.deref().to_case(case)).join(case.delim())\n}\n\npub(super) fn print_lines(output: &mut Indenter, lines: &[&str]) {\n    for line in lines {\n        writeln!(output, \"{line}\");\n    }\n}\n\npub const AUTO_GENERATED_PREFIX: &str = \"// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB\";\n\nconst AUTO_GENERATED_FILE_COMMENT: &[&str] = &[\n    \"// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\",\n    \"// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\",\n    \"\",\n];\n\npub(super) fn print_auto_generated_file_comment(output: &mut Indenter) {\n    print_lines(output, AUTO_GENERATED_FILE_COMMENT);\n}\n\npub(super) fn print_auto_generated_version_comment(output: &mut Indenter) {\n    writeln!(\n        output,\n        \"// This was generated using spacetimedb cli version {} (commit {}).\",\n        version::spacetimedb_lib_version(),\n        version::GIT_HASH\n    );\n    writeln!(output);\n}\n\npub(super) fn type_ref_name(module: &ModuleDef, typeref: AlgebraicTypeRef) -> String {\n    let (name, _def) = module.type_def_from_ref(typeref).unwrap();\n    collect_case(Case::Pascal, name.name_segments())\n}\n\npub(super) fn is_type_filterable(typespace: &TypespaceForGenerate, ty: &AlgebraicTypeUse) -> bool {\n    match ty {\n        AlgebraicTypeUse::Primitive(prim) => !matches!(prim, PrimitiveType::F32 | PrimitiveType::F64),\n        AlgebraicTypeUse::String | AlgebraicTypeUse::Identity | AlgebraicTypeUse::ConnectionId => true,\n        // Sum types with all unit variants:\n        AlgebraicTypeUse::Never => true,\n        AlgebraicTypeUse::Option(inner) => matches!(&**inner, AlgebraicTypeUse::Unit),\n        AlgebraicTypeUse::Ref(r) => typespace[r].is_plain_enum(),\n        _ => false,\n    }\n}\n\npub(super) fn is_reducer_invokable(reducer: &ReducerDef) -> bool {\n    reducer.lifecycle.is_none()\n}\n\n/// Iterate over all the [`ReducerDef`]s defined by the module, in alphabetical order by name.\n///\n/// Skipping the `init` reducer and internal [`FunctionVisibiity::Internal`] reducers because\n/// they should not be directly invokable.\n/// Sorting is not necessary for reducers because they are already stored in an IndexMap.\npub(super) fn iter_reducers(module: &ModuleDef, visibility: CodegenVisibility) -> impl Iterator<Item = &ReducerDef> {\n    module\n        .reducers()\n        // `RawModuleDefV10` already marks all lifecycle reducers as private, but we keep\n        // this filter for backward compatibility with older versions where `init`\n        // reducers were not private.\n        .filter(|reducer| reducer.lifecycle != Some(Lifecycle::Init))\n        // Prior to `RawModuleDefV10`, all reducers were public by default. Filtering out\n        // internal reducers here does not break SDKs built against older versions.\n        .filter(move |reducer| match visibility {\n            CodegenVisibility::IncludePrivate => true,\n            CodegenVisibility::OnlyPublic => !reducer.visibility.is_private(),\n        })\n}\n\n/// Iterate over all the [`ProcedureDef`]s defined by the module, in alphabetical order by name.\n///\n/// Skipping internal [`FunctionVisibiity::Internal`] procedures because they should not be\n/// directly invokable.\n/// Sorting is necessary to have deterministic reproducible codegen.\npub(super) fn iter_procedures(\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n) -> impl Iterator<Item = &ProcedureDef> {\n    module\n        .procedures()\n        .sorted_by_key(|procedure| &procedure.name)\n        .filter(move |procedure| match visibility {\n            CodegenVisibility::IncludePrivate => true,\n            CodegenVisibility::OnlyPublic => !procedure.visibility.is_private(),\n        })\n}\n\n/// Iterate over all the [`TableDef`]s defined by the module, in alphabetical order by name.\n///\n/// Sorting is necessary to have deterministic reproducible codegen.\npub(super) fn iter_tables(module: &ModuleDef, visibility: CodegenVisibility) -> impl Iterator<Item = &TableDef> {\n    module\n        .tables()\n        .filter(move |table| match visibility {\n            CodegenVisibility::IncludePrivate => true,\n            CodegenVisibility::OnlyPublic => table.table_access == TableAccess::Public,\n        })\n        .sorted_by_key(|table| &table.name)\n}\n\n/// Return the names of all private tables, in alphabetical order.\npub fn private_table_names(module: &ModuleDef) -> Vec<String> {\n    module\n        .tables()\n        .filter(|table| table.table_access == TableAccess::Private)\n        .sorted_by_key(|table| &table.name)\n        .map(|table| table.name.to_string())\n        .collect()\n}\n\n/// Iterate over all the [`ViewDef`]s defined by the module, in alphabetical order by name.\n///\n/// Sorting is necessary to have deterministic reproducible codegen.\npub(super) fn iter_views(module: &ModuleDef) -> impl Iterator<Item = &ViewDef> {\n    module.views().sorted_by_key(|view| &view.name)\n}\n\n/// Iterate over the names of all the tables and views defined by the module, in alphabetical order.\n///\n/// Returns `(name, accessor_name, product_type_ref)` for each table/view.\n/// Use `name` for protocol/wire communication; use `accessor_name` for generated identifiers.\n///\n/// Sorting is necessary to have deterministic reproducible codegen.\npub(super) fn iter_table_names_and_types(\n    module: &ModuleDef,\n    visibility: CodegenVisibility,\n) -> impl Iterator<Item = (&Identifier, &Identifier, AlgebraicTypeRef)> {\n    module\n        .tables()\n        .filter(move |table| match visibility {\n            CodegenVisibility::IncludePrivate => true,\n            CodegenVisibility::OnlyPublic => table.table_access == TableAccess::Public,\n        })\n        .map(|def| (&def.name, &def.accessor_name, def.product_type_ref))\n        .chain(\n            module\n                .views()\n                .map(|def| (&def.name, &def.accessor_name, def.product_type_ref)),\n        )\n        .sorted_by_key(|(_, accessor_name, _)| *accessor_name)\n}\n\npub(super) fn iter_unique_cols<'a>(\n    typespace: &'a TypespaceForGenerate,\n    schema: &'a TableSchema,\n    product_def: &'a ProductTypeDef,\n) -> impl Iterator<Item = &'a (Identifier, AlgebraicTypeUse)> + 'a {\n    let constraints = schema.backcompat_column_constraints();\n    schema.columns().iter().filter_map(move |field| {\n        constraints[&ColList::from(field.col_pos)]\n            .has_unique()\n            .then(|| {\n                let res @ (_, ty) = &product_def.elements[field.col_pos.idx()];\n                is_type_filterable(typespace, ty).then_some(res)\n            })\n            .flatten()\n    })\n}\n\npub(super) fn iter_indexes(table: &TableDef) -> impl Iterator<Item = &IndexDef> {\n    table.indexes.values().sorted_by_key(|index| &index.name)\n}\n\npub(super) fn iter_constraints(table: &TableDef) -> impl Iterator<Item = &ConstraintDef> {\n    table.constraints.values().sorted_by_key(|constraint| &constraint.name)\n}\n\n/// Iterate over all the [`TypeDef`]s defined by the module, in alphabetical order by name.\n///\n/// Sorting is necessary to have deterministic reproducible codegen.\npub fn iter_types(module: &ModuleDef) -> impl Iterator<Item = &TypeDef> {\n    module.types().sorted_by_key(|table| &table.accessor_name)\n}\n"
  },
  {
    "path": "crates/codegen/tests/codegen.rs",
    "content": "use spacetimedb_codegen::{generate, CodegenOptions, Csharp, Rust, TypeScript};\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_testing::modules::{CompilationMode, CompiledModule};\nuse std::sync::OnceLock;\n\nfn compiled_module() -> &'static ModuleDef {\n    static COMPILED_MODULE: OnceLock<ModuleDef> = OnceLock::new();\n    COMPILED_MODULE\n        .get_or_init(|| CompiledModule::compile(\"module-test\", CompilationMode::Debug).extract_schema_blocking())\n}\n\nmacro_rules! declare_tests {\n    ($($name:ident => $lang:expr,)*) => ($(\n        #[test]\n        fn $name() {\n            let module = compiled_module();\n            let outfiles = generate(&module, &$lang, &CodegenOptions::default())\n                .into_iter()\n                .map(|f| (f.filename, f.code))\n                .collect::<HashMap<_, _>>();\n            let mut settings = insta::Settings::clone_current();\n            settings.set_sort_maps(true);\n            // Ignore the autogenerated comments with version info, since it changes with every\n            // build.\n            settings.add_filter(r\"// This was generated using spacetimedb cli version \\d+\\.\\d+\\.\\d+ .*\", \"VERSION_COMMENT\");\n            // Ignore the place where the CLI version is put in the typescript REMOTE_MODULE info,\n            // so it isn't constantly changing.\n            settings.add_filter(r#\"cliVersion: \"\\d+\\.\\d+\\.\\d+\",\"#, r#\"cliVersion: \"X.Y.Z\",\"#);\n            settings.bind(|| {\n                insta::assert_toml_snapshot!(outfiles);\n            });\n        }\n    )*);\n}\n\ndeclare_tests! {\n    test_codegen_csharp => Csharp { namespace: \"SpacetimeDB\" },\n    test_codegen_typescript => TypeScript,\n    test_codegen_rust => Rust,\n}\n"
  },
  {
    "path": "crates/codegen/tests/snapshots/codegen__codegen_csharp.snap",
    "content": "---\nsource: crates/codegen/tests/codegen.rs\nexpression: outfiles\n---\n\"Procedures/GetMySchemaViaHttp.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        public void GetMySchemaViaHttp(ProcedureCallback<string> callback)\n        {\n            // Convert the clean callback to the wrapper callback\n            InternalGetMySchemaViaHttp((ctx, result) => {\n            if (result.IsSuccess && result.Value != null)\n            {\n                callback(ctx, ProcedureCallbackResult<string>.Success(result.Value.Value));\n            }\n            else\n            {\n                callback(ctx, ProcedureCallbackResult<string>.Failure(result.Error!));\n            }\n            });\n        }\n\n        private void InternalGetMySchemaViaHttp(ProcedureCallback<Procedure.GetMySchemaViaHttp> callback)\n        {\n            conn.InternalCallProcedure(new Procedure.GetMySchemaViaHttpArgs(), callback);\n        }\n\n    }\n\n    public abstract partial class Procedure\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class GetMySchemaViaHttp\n        {\n            [DataMember(Name = \"Value\")]\n            public string Value;\n\n            public GetMySchemaViaHttp(string Value)\n            {\n                this.Value = Value;\n            }\n\n            public GetMySchemaViaHttp()\n            {\n                this.Value = \"\";\n            }\n        }\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class GetMySchemaViaHttpArgs : Procedure, IProcedureArgs\n        {\n            string IProcedureArgs.ProcedureName => \"get_my_schema_via_http\";\n        }\n\n    }\n}\n'''\n\"Procedures/ReturnValue.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        public void ReturnValue(ulong foo, ProcedureCallback<SpacetimeDB.Baz> callback)\n        {\n            // Convert the clean callback to the wrapper callback\n            InternalReturnValue(foo, (ctx, result) => {\n            if (result.IsSuccess && result.Value != null)\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Baz>.Success(result.Value.Value));\n            }\n            else\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Baz>.Failure(result.Error!));\n            }\n            });\n        }\n\n        private void InternalReturnValue(ulong foo, ProcedureCallback<Procedure.ReturnValue> callback)\n        {\n            conn.InternalCallProcedure(new Procedure.ReturnValueArgs(foo), callback);\n        }\n\n    }\n\n    public abstract partial class Procedure\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class ReturnValue\n        {\n            [DataMember(Name = \"Value\")]\n            public SpacetimeDB.Baz Value;\n\n            public ReturnValue(SpacetimeDB.Baz Value)\n            {\n                this.Value = Value;\n            }\n\n            public ReturnValue()\n            {\n                this.Value = new();\n            }\n        }\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class ReturnValueArgs : Procedure, IProcedureArgs\n        {\n            [DataMember(Name = \"foo\")]\n            public ulong Foo;\n\n            public ReturnValueArgs(ulong Foo)\n            {\n                this.Foo = Foo;\n            }\n\n            public ReturnValueArgs()\n            {\n            }\n\n            string IProcedureArgs.ProcedureName => \"return_value\";\n        }\n\n    }\n}\n'''\n\"Procedures/SleepOneSecond.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        public void SleepOneSecond(ProcedureCallback<SpacetimeDB.Unit> callback)\n        {\n            // Convert the clean callback to the wrapper callback\n            InternalSleepOneSecond((ctx, result) => {\n            if (result.IsSuccess && result.Value != null)\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Unit>.Success(result.Value.Value));\n            }\n            else\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Unit>.Failure(result.Error!));\n            }\n            });\n        }\n\n        private void InternalSleepOneSecond(ProcedureCallback<Procedure.SleepOneSecond> callback)\n        {\n            conn.InternalCallProcedure(new Procedure.SleepOneSecondArgs(), callback);\n        }\n\n    }\n\n    public abstract partial class Procedure\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class SleepOneSecond\n        {\n            [DataMember(Name = \"Value\")]\n            public SpacetimeDB.Unit Value;\n\n            public SleepOneSecond(SpacetimeDB.Unit Value)\n            {\n                this.Value = Value;\n            }\n\n            public SleepOneSecond()\n            {\n            }\n        }\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class SleepOneSecondArgs : Procedure, IProcedureArgs\n        {\n            string IProcedureArgs.ProcedureName => \"sleep_one_second\";\n        }\n\n    }\n}\n'''\n\"Procedures/WithTx.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        public void WithTx(ProcedureCallback<SpacetimeDB.Unit> callback)\n        {\n            // Convert the clean callback to the wrapper callback\n            InternalWithTx((ctx, result) => {\n            if (result.IsSuccess && result.Value != null)\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Unit>.Success(result.Value.Value));\n            }\n            else\n            {\n                callback(ctx, ProcedureCallbackResult<SpacetimeDB.Unit>.Failure(result.Error!));\n            }\n            });\n        }\n\n        private void InternalWithTx(ProcedureCallback<Procedure.WithTx> callback)\n        {\n            conn.InternalCallProcedure(new Procedure.WithTxArgs(), callback);\n        }\n\n    }\n\n    public abstract partial class Procedure\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class WithTx\n        {\n            [DataMember(Name = \"Value\")]\n            public SpacetimeDB.Unit Value;\n\n            public WithTx(SpacetimeDB.Unit Value)\n            {\n                this.Value = Value;\n            }\n\n            public WithTx()\n            {\n            }\n        }\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class WithTxArgs : Procedure, IProcedureArgs\n        {\n            string IProcedureArgs.ProcedureName => \"with_tx\";\n        }\n\n    }\n}\n'''\n\"Reducers/Add.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void AddHandler(ReducerEventContext ctx, string name, byte age);\n        public event AddHandler? OnAdd;\n\n        public void Add(string name, byte age)\n        {\n            conn.InternalCallReducer(new Reducer.Add(name, age));\n        }\n\n        public bool InvokeAdd(ReducerEventContext ctx, Reducer.Add args)\n        {\n            if (OnAdd == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnAdd(\n                ctx,\n                args.Name,\n                args.Age\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class Add : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"name\")]\n            public string Name;\n            [DataMember(Name = \"age\")]\n            public byte Age;\n\n            public Add(\n                string Name,\n                byte Age\n            )\n            {\n                this.Name = Name;\n                this.Age = Age;\n            }\n\n            public Add()\n            {\n                this.Name = \"\";\n            }\n\n            string IReducerArgs.ReducerName => \"add\";\n        }\n    }\n}\n'''\n\"Reducers/AddPlayer.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void AddPlayerHandler(ReducerEventContext ctx, string name);\n        public event AddPlayerHandler? OnAddPlayer;\n\n        public void AddPlayer(string name)\n        {\n            conn.InternalCallReducer(new Reducer.AddPlayer(name));\n        }\n\n        public bool InvokeAddPlayer(ReducerEventContext ctx, Reducer.AddPlayer args)\n        {\n            if (OnAddPlayer == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnAddPlayer(\n                ctx,\n                args.Name\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class AddPlayer : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"name\")]\n            public string Name;\n\n            public AddPlayer(string Name)\n            {\n                this.Name = Name;\n            }\n\n            public AddPlayer()\n            {\n                this.Name = \"\";\n            }\n\n            string IReducerArgs.ReducerName => \"add_player\";\n        }\n    }\n}\n'''\n\"Reducers/AddPrivate.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void AddPrivateHandler(ReducerEventContext ctx, string name);\n        public event AddPrivateHandler? OnAddPrivate;\n\n        public void AddPrivate(string name)\n        {\n            conn.InternalCallReducer(new Reducer.AddPrivate(name));\n        }\n\n        public bool InvokeAddPrivate(ReducerEventContext ctx, Reducer.AddPrivate args)\n        {\n            if (OnAddPrivate == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnAddPrivate(\n                ctx,\n                args.Name\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class AddPrivate : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"name\")]\n            public string Name;\n\n            public AddPrivate(string Name)\n            {\n                this.Name = Name;\n            }\n\n            public AddPrivate()\n            {\n                this.Name = \"\";\n            }\n\n            string IReducerArgs.ReducerName => \"add_private\";\n        }\n    }\n}\n'''\n\"Reducers/AssertCallerIdentityIsModuleIdentity.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void AssertCallerIdentityIsModuleIdentityHandler(ReducerEventContext ctx);\n        public event AssertCallerIdentityIsModuleIdentityHandler? OnAssertCallerIdentityIsModuleIdentity;\n\n        public void AssertCallerIdentityIsModuleIdentity()\n        {\n            conn.InternalCallReducer(new Reducer.AssertCallerIdentityIsModuleIdentity());\n        }\n\n        public bool InvokeAssertCallerIdentityIsModuleIdentity(ReducerEventContext ctx, Reducer.AssertCallerIdentityIsModuleIdentity args)\n        {\n            if (OnAssertCallerIdentityIsModuleIdentity == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnAssertCallerIdentityIsModuleIdentity(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class AssertCallerIdentityIsModuleIdentity : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"assert_caller_identity_is_module_identity\";\n        }\n    }\n}\n'''\n\"Reducers/DeletePlayer.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void DeletePlayerHandler(ReducerEventContext ctx, ulong id);\n        public event DeletePlayerHandler? OnDeletePlayer;\n\n        public void DeletePlayer(ulong id)\n        {\n            conn.InternalCallReducer(new Reducer.DeletePlayer(id));\n        }\n\n        public bool InvokeDeletePlayer(ReducerEventContext ctx, Reducer.DeletePlayer args)\n        {\n            if (OnDeletePlayer == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnDeletePlayer(\n                ctx,\n                args.Id\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class DeletePlayer : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"id\")]\n            public ulong Id;\n\n            public DeletePlayer(ulong Id)\n            {\n                this.Id = Id;\n            }\n\n            public DeletePlayer()\n            {\n            }\n\n            string IReducerArgs.ReducerName => \"delete_player\";\n        }\n    }\n}\n'''\n\"Reducers/DeletePlayersByName.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void DeletePlayersByNameHandler(ReducerEventContext ctx, string name);\n        public event DeletePlayersByNameHandler? OnDeletePlayersByName;\n\n        public void DeletePlayersByName(string name)\n        {\n            conn.InternalCallReducer(new Reducer.DeletePlayersByName(name));\n        }\n\n        public bool InvokeDeletePlayersByName(ReducerEventContext ctx, Reducer.DeletePlayersByName args)\n        {\n            if (OnDeletePlayersByName == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnDeletePlayersByName(\n                ctx,\n                args.Name\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class DeletePlayersByName : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"name\")]\n            public string Name;\n\n            public DeletePlayersByName(string Name)\n            {\n                this.Name = Name;\n            }\n\n            public DeletePlayersByName()\n            {\n                this.Name = \"\";\n            }\n\n            string IReducerArgs.ReducerName => \"delete_players_by_name\";\n        }\n    }\n}\n'''\n\"Reducers/ListOverAge.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void ListOverAgeHandler(ReducerEventContext ctx, byte age);\n        public event ListOverAgeHandler? OnListOverAge;\n\n        public void ListOverAge(byte age)\n        {\n            conn.InternalCallReducer(new Reducer.ListOverAge(age));\n        }\n\n        public bool InvokeListOverAge(ReducerEventContext ctx, Reducer.ListOverAge args)\n        {\n            if (OnListOverAge == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnListOverAge(\n                ctx,\n                args.Age\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class ListOverAge : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"age\")]\n            public byte Age;\n\n            public ListOverAge(byte Age)\n            {\n                this.Age = Age;\n            }\n\n            public ListOverAge()\n            {\n            }\n\n            string IReducerArgs.ReducerName => \"list_over_age\";\n        }\n    }\n}\n'''\n\"Reducers/LogModuleIdentity.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void LogModuleIdentityHandler(ReducerEventContext ctx);\n        public event LogModuleIdentityHandler? OnLogModuleIdentity;\n\n        public void LogModuleIdentity()\n        {\n            conn.InternalCallReducer(new Reducer.LogModuleIdentity());\n        }\n\n        public bool InvokeLogModuleIdentity(ReducerEventContext ctx, Reducer.LogModuleIdentity args)\n        {\n            if (OnLogModuleIdentity == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnLogModuleIdentity(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class LogModuleIdentity : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"log_module_identity\";\n        }\n    }\n}\n'''\n\"Reducers/QueryPrivate.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void QueryPrivateHandler(ReducerEventContext ctx);\n        public event QueryPrivateHandler? OnQueryPrivate;\n\n        public void QueryPrivate()\n        {\n            conn.InternalCallReducer(new Reducer.QueryPrivate());\n        }\n\n        public bool InvokeQueryPrivate(ReducerEventContext ctx, Reducer.QueryPrivate args)\n        {\n            if (OnQueryPrivate == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnQueryPrivate(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class QueryPrivate : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"query_private\";\n        }\n    }\n}\n'''\n\"Reducers/SayHello.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void SayHelloHandler(ReducerEventContext ctx);\n        public event SayHelloHandler? OnSayHello;\n\n        public void SayHello()\n        {\n            conn.InternalCallReducer(new Reducer.SayHello());\n        }\n\n        public bool InvokeSayHello(ReducerEventContext ctx, Reducer.SayHello args)\n        {\n            if (OnSayHello == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnSayHello(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class SayHello : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"say_hello\";\n        }\n    }\n}\n'''\n\"Reducers/Test.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void TestHandler(ReducerEventContext ctx, SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.NamespaceTestC arg3, SpacetimeDB.NamespaceTestF arg4);\n        public event TestHandler? OnTest;\n\n        public void Test(SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.NamespaceTestC arg3, SpacetimeDB.NamespaceTestF arg4)\n        {\n            conn.InternalCallReducer(new Reducer.Test(arg, arg2, arg3, arg4));\n        }\n\n        public bool InvokeTest(ReducerEventContext ctx, Reducer.Test args)\n        {\n            if (OnTest == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnTest(\n                ctx,\n                args.Arg,\n                args.Arg2,\n                args.Arg3,\n                args.Arg4\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class Test : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"arg\")]\n            public TestA Arg;\n            [DataMember(Name = \"arg_2\")]\n            public TestB Arg2;\n            [DataMember(Name = \"arg_3\")]\n            public NamespaceTestC Arg3;\n            [DataMember(Name = \"arg_4\")]\n            public NamespaceTestF Arg4;\n\n            public Test(\n                TestA Arg,\n                TestB Arg2,\n                NamespaceTestC Arg3,\n                NamespaceTestF Arg4\n            )\n            {\n                this.Arg = Arg;\n                this.Arg2 = Arg2;\n                this.Arg3 = Arg3;\n                this.Arg4 = Arg4;\n            }\n\n            public Test()\n            {\n                this.Arg = new();\n                this.Arg2 = new();\n                this.Arg4 = null!;\n            }\n\n            string IReducerArgs.ReducerName => \"test\";\n        }\n    }\n}\n'''\n\"Reducers/TestBtreeIndexArgs.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void TestBtreeIndexArgsHandler(ReducerEventContext ctx);\n        public event TestBtreeIndexArgsHandler? OnTestBtreeIndexArgs;\n\n        public void TestBtreeIndexArgs()\n        {\n            conn.InternalCallReducer(new Reducer.TestBtreeIndexArgs());\n        }\n\n        public bool InvokeTestBtreeIndexArgs(ReducerEventContext ctx, Reducer.TestBtreeIndexArgs args)\n        {\n            if (OnTestBtreeIndexArgs == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch(ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnTestBtreeIndexArgs(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class TestBtreeIndexArgs : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"test_btree_index_args\";\n        }\n    }\n}\n'''\n\"SpacetimeDBClient.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\nVERSION_COMMENT\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        internal RemoteReducers(DbConnection conn) : base(conn) { }\n        internal event Action<ReducerEventContext, Exception>? InternalOnUnhandledReducerError;\n    }\n\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        internal RemoteProcedures(DbConnection conn) : base(conn) { }\n    }\n\n    public sealed partial class RemoteTables : RemoteTablesBase\n    {\n        public RemoteTables(DbConnection conn)\n        {\n            AddTable(LoggedOutPlayer = new(conn));\n            AddTable(MyPlayer = new(conn));\n            AddTable(Person = new(conn));\n            AddTable(Player = new(conn));\n            AddTable(TestD = new(conn));\n            AddTable(TestF = new(conn));\n        }\n    }\n\n\n        public interface IRemoteDbContext : IDbContext<RemoteTables, RemoteReducers, SubscriptionBuilder, RemoteProcedures> {\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError;\n        }\n\n        public sealed class EventContext : IEventContext, IRemoteDbContext\n        {\n            private readonly DbConnection conn;\n\n            /// <summary>\n            /// The event that caused this callback to run.\n            /// </summary>\n            public readonly Event<Reducer> Event;\n\n            /// <summary>\n            /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n            ///\n            /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n            /// </summary>\n            public RemoteTables Db => conn.Db;\n            /// <summary>\n            /// Access to reducers defined by the module.\n            ///\n            /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n            /// plus methods for adding and removing callbacks on each of those reducers.\n            /// </summary>\n            public RemoteReducers Reducers => conn.Reducers;\n            /// <summary>\n            /// Access to procedures defined by the module.\n            ///\n            /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n            /// with a callback for when the procedure completes and returns a value.\n            /// </summary>\n            public RemoteProcedures Procedures => conn.Procedures;\n            /// <summary>\n            /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n            /// </summary>\n            public bool IsActive => conn.IsActive;\n            /// <summary>\n            /// Close the connection.\n            ///\n            /// Throws an error if the connection is already closed.\n            /// </summary>\n            public void Disconnect() {\n                conn.Disconnect();\n            }\n            /// <summary>\n            /// Start building a subscription.\n            /// </summary>\n            /// <returns>A builder-pattern constructor for subscribing to queries,\n            /// causing matching rows to be replicated into the client cache.</returns>\n            public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n            /// <summary>\n            /// Get the <c>Identity</c> of this connection.\n            ///\n            /// This method returns null if the connection was constructed anonymously\n            /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n            /// </summary>\n            public Identity? Identity => conn.Identity;\n            /// <summary>\n            /// Get this connection's <c>ConnectionId</c>.\n            /// </summary>\n            public ConnectionId ConnectionId => conn.ConnectionId;\n            /// <summary>\n            /// Register a callback to be called when a reducer with no handler returns an error.\n            /// </summary>\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n                add => Reducers.InternalOnUnhandledReducerError += value;\n                remove => Reducers.InternalOnUnhandledReducerError -= value;\n            }\n\n            internal EventContext(DbConnection conn, Event<Reducer> Event)\n            {\n                this.conn = conn;\n                this.Event = Event;\n            }\n        }\n\n        public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext\n        {\n            private readonly DbConnection conn;\n            /// <summary>\n            /// The reducer event that caused this callback to run.\n            /// </summary>\n            public readonly ReducerEvent<Reducer> Event;\n\n            /// <summary>\n            /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n            ///\n            /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n            /// </summary>\n            public RemoteTables Db => conn.Db;\n            /// <summary>\n            /// Access to reducers defined by the module.\n            ///\n            /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n            /// plus methods for adding and removing callbacks on each of those reducers.\n            /// </summary>\n            public RemoteReducers Reducers => conn.Reducers;\n            /// <summary>\n            /// Access to procedures defined by the module.\n            ///\n            /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n            /// with a callback for when the procedure completes and returns a value.\n            /// </summary>\n            public RemoteProcedures Procedures => conn.Procedures;\n            /// <summary>\n            /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n            /// </summary>\n            public bool IsActive => conn.IsActive;\n            /// <summary>\n            /// Close the connection.\n            ///\n            /// Throws an error if the connection is already closed.\n            /// </summary>\n            public void Disconnect() {\n                conn.Disconnect();\n            }\n            /// <summary>\n            /// Start building a subscription.\n            /// </summary>\n            /// <returns>A builder-pattern constructor for subscribing to queries,\n            /// causing matching rows to be replicated into the client cache.</returns>\n            public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n            /// <summary>\n            /// Get the <c>Identity</c> of this connection.\n            ///\n            /// This method returns null if the connection was constructed anonymously\n            /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n            /// </summary>\n            public Identity? Identity => conn.Identity;\n            /// <summary>\n            /// Get this connection's <c>ConnectionId</c>.\n            /// </summary>\n            public ConnectionId ConnectionId => conn.ConnectionId;\n            /// <summary>\n            /// Register a callback to be called when a reducer with no handler returns an error.\n            /// </summary>\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n                add => Reducers.InternalOnUnhandledReducerError += value;\n                remove => Reducers.InternalOnUnhandledReducerError -= value;\n            }\n\n            internal ReducerEventContext(DbConnection conn, ReducerEvent<Reducer> reducerEvent)\n            {\n                this.conn = conn;\n                Event = reducerEvent;\n            }\n        }\n\n        public sealed class ErrorContext : IErrorContext, IRemoteDbContext\n        {\n            private readonly DbConnection conn;\n            /// <summary>\n            /// The <c>Exception</c> that caused this error callback to be run.\n            /// </summary>\n            public readonly Exception Event;\n            Exception IErrorContext.Event {\n                get {\n                    return Event;\n                }\n            }\n\n            /// <summary>\n            /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n            ///\n            /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n            /// </summary>\n            public RemoteTables Db => conn.Db;\n            /// <summary>\n            /// Access to reducers defined by the module.\n            ///\n            /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n            /// plus methods for adding and removing callbacks on each of those reducers.\n            /// </summary>\n            public RemoteReducers Reducers => conn.Reducers;\n            /// <summary>\n            /// Access to procedures defined by the module.\n            ///\n            /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n            /// with a callback for when the procedure completes and returns a value.\n            /// </summary>\n            public RemoteProcedures Procedures => conn.Procedures;\n            /// <summary>\n            /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n            /// </summary>\n            public bool IsActive => conn.IsActive;\n            /// <summary>\n            /// Close the connection.\n            ///\n            /// Throws an error if the connection is already closed.\n            /// </summary>\n            public void Disconnect() {\n                conn.Disconnect();\n            }\n            /// <summary>\n            /// Start building a subscription.\n            /// </summary>\n            /// <returns>A builder-pattern constructor for subscribing to queries,\n            /// causing matching rows to be replicated into the client cache.</returns>\n            public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n            /// <summary>\n            /// Get the <c>Identity</c> of this connection.\n            ///\n            /// This method returns null if the connection was constructed anonymously\n            /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n            /// </summary>\n            public Identity? Identity => conn.Identity;\n            /// <summary>\n            /// Get this connection's <c>ConnectionId</c>.\n            /// </summary>\n            public ConnectionId ConnectionId => conn.ConnectionId;\n            /// <summary>\n            /// Register a callback to be called when a reducer with no handler returns an error.\n            /// </summary>\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n                add => Reducers.InternalOnUnhandledReducerError += value;\n                remove => Reducers.InternalOnUnhandledReducerError -= value;\n            }\n\n            internal ErrorContext(DbConnection conn, Exception error)\n            {\n                this.conn = conn;\n                Event = error;\n            }\n        }\n\n        public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext\n        {\n            private readonly DbConnection conn;\n\n            /// <summary>\n            /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n            ///\n            /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n            /// </summary>\n            public RemoteTables Db => conn.Db;\n            /// <summary>\n            /// Access to reducers defined by the module.\n            ///\n            /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n            /// plus methods for adding and removing callbacks on each of those reducers.\n            /// </summary>\n            public RemoteReducers Reducers => conn.Reducers;\n            /// <summary>\n            /// Access to procedures defined by the module.\n            ///\n            /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n            /// with a callback for when the procedure completes and returns a value.\n            /// </summary>\n            public RemoteProcedures Procedures => conn.Procedures;\n            /// <summary>\n            /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n            /// </summary>\n            public bool IsActive => conn.IsActive;\n            /// <summary>\n            /// Close the connection.\n            ///\n            /// Throws an error if the connection is already closed.\n            /// </summary>\n            public void Disconnect() {\n                conn.Disconnect();\n            }\n            /// <summary>\n            /// Start building a subscription.\n            /// </summary>\n            /// <returns>A builder-pattern constructor for subscribing to queries,\n            /// causing matching rows to be replicated into the client cache.</returns>\n            public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n            /// <summary>\n            /// Get the <c>Identity</c> of this connection.\n            ///\n            /// This method returns null if the connection was constructed anonymously\n            /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n            /// </summary>\n            public Identity? Identity => conn.Identity;\n            /// <summary>\n            /// Get this connection's <c>ConnectionId</c>.\n            /// </summary>\n            public ConnectionId ConnectionId => conn.ConnectionId;\n            /// <summary>\n            /// Register a callback to be called when a reducer with no handler returns an error.\n            /// </summary>\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n                add => Reducers.InternalOnUnhandledReducerError += value;\n                remove => Reducers.InternalOnUnhandledReducerError -= value;\n            }\n\n            internal SubscriptionEventContext(DbConnection conn)\n            {\n                this.conn = conn;\n            }\n        }\n\n        public sealed class ProcedureEventContext : IProcedureEventContext, IRemoteDbContext\n        {\n            private readonly DbConnection conn;\n            /// <summary>\n            /// The procedure event that caused this callback to run.\n            /// </summary>\n            public readonly ProcedureEvent Event;\n\n            /// <summary>\n            /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n            ///\n            /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n            /// </summary>\n            public RemoteTables Db => conn.Db;\n            /// <summary>\n            /// Access to reducers defined by the module.\n            ///\n            /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n            /// plus methods for adding and removing callbacks on each of those reducers.\n            /// </summary>\n            public RemoteReducers Reducers => conn.Reducers;\n            /// <summary>\n            /// Access to procedures defined by the module.\n            ///\n            /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n            /// with a callback for when the procedure completes and returns a value.\n            /// </summary>\n            public RemoteProcedures Procedures => conn.Procedures;\n            /// <summary>\n            /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n            /// </summary>\n            public bool IsActive => conn.IsActive;\n            /// <summary>\n            /// Close the connection.\n            ///\n            /// Throws an error if the connection is already closed.\n            /// </summary>\n            public void Disconnect() {\n                conn.Disconnect();\n            }\n            /// <summary>\n            /// Start building a subscription.\n            /// </summary>\n            /// <returns>A builder-pattern constructor for subscribing to queries,\n            /// causing matching rows to be replicated into the client cache.</returns>\n            public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n            /// <summary>\n            /// Get the <c>Identity</c> of this connection.\n            ///\n            /// This method returns null if the connection was constructed anonymously\n            /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n            /// </summary>\n            public Identity? Identity => conn.Identity;\n            /// <summary>\n            /// Get this connection's <c>ConnectionId</c>.\n            /// </summary>\n            public ConnectionId ConnectionId => conn.ConnectionId;\n            /// <summary>\n            /// Register a callback to be called when a reducer with no handler returns an error.\n            /// </summary>\n            public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError {\n                add => Reducers.InternalOnUnhandledReducerError += value;\n                remove => Reducers.InternalOnUnhandledReducerError -= value;\n            }\n\n            internal ProcedureEventContext(DbConnection conn, ProcedureEvent Event)\n            {\n                this.conn = conn;\n                this.Event = Event;\n            }\n        }\n\n        /// <summary>\n        /// Builder-pattern constructor for subscription queries.\n        /// </summary>\n        public sealed class SubscriptionBuilder\n        {\n            private readonly IDbConnection conn;\n\n            private event Action<SubscriptionEventContext>? Applied;\n            private event Action<ErrorContext, Exception>? Error;\n\n            /// <summary>\n            /// Private API, use <c>conn.SubscriptionBuilder()</c> instead.\n            /// </summary>\n            public SubscriptionBuilder(IDbConnection conn)\n            {\n                this.conn = conn;\n            }\n\n            /// <summary>\n            /// Register a callback to run when the subscription is applied.\n            /// </summary>\n            public SubscriptionBuilder OnApplied(\n                Action<SubscriptionEventContext> callback\n            )\n            {\n                Applied += callback;\n                return this;\n            }\n\n            /// <summary>\n            /// Register a callback to run when the subscription fails.\n            ///\n            /// Note that this callback may run either when attempting to apply the subscription,\n            /// in which case <c>Self::on_applied</c> will never run,\n            /// or later during the subscription's lifetime if the module's interface changes,\n            /// in which case <c>Self::on_applied</c> may have already run.\n            /// </summary>\n            public SubscriptionBuilder OnError(\n                Action<ErrorContext, Exception> callback\n            )\n            {\n                Error += callback;\n                return this;\n            }\n        \n            /// <summary>\n            /// Add a typed query to this subscription.\n            ///\n            /// This is the entry point for building subscriptions without writing SQL by hand.\n            /// Once a typed query is added, only typed queries may follow (SQL and typed queries cannot be mixed).\n            /// </summary>\n            public TypedSubscriptionBuilder AddQuery<TRow>(\n                Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build\n            )\n            {\n                var typed = new TypedSubscriptionBuilder(conn, Applied, Error);\n                return typed.AddQuery(build);\n            }\n\n            /// <summary>\n            /// Subscribe to the following SQL queries.\n            ///\n            /// This method returns immediately, with the data not yet added to the DbConnection.\n            /// The provided callbacks will be invoked once the data is returned from the remote server.\n            /// Data from all the provided queries will be returned at the same time.\n            ///\n            /// See the SpacetimeDB SQL docs for more information on SQL syntax:\n            /// <a href=\"https://spacetimedb.com/docs/sql\">https://spacetimedb.com/docs/sql</a>\n            /// </summary>\n            public SubscriptionHandle Subscribe(\n                string[] querySqls\n            ) => new(conn, Applied, Error, querySqls);\n\n            /// <summary>\n            /// Subscribe to all rows from all tables.\n            ///\n            /// This method is intended as a convenience\n            /// for applications where client-side memory use and network bandwidth are not concerns.\n            /// Applications where these resources are a constraint\n            /// should register more precise queries via <c>Self.Subscribe</c>\n            /// in order to replicate only the subset of data which the client needs to function.\n            ///\n            /// This method should not be combined with <c>Self.Subscribe</c> on the same <c>DbConnection</c>.\n            /// A connection may either <c>Self.Subscribe</c> to particular queries,\n            /// or <c>Self.SubscribeToAllTables</c>, but not both.\n            /// Attempting to call <c>Self.Subscribe</c>\n            /// on a <c>DbConnection</c> that has previously used <c>Self.SubscribeToAllTables</c>,\n            /// or vice versa, may misbehave in any number of ways,\n            /// including dropping subscriptions, corrupting the client cache, or panicking.\n            /// </summary>\n            public SubscriptionHandle SubscribeToAllTables() =>\n                new(conn, Applied, Error, QueryBuilder.AllTablesSqlQueries());\n        }\n\n        public sealed class SubscriptionHandle : SubscriptionHandleBase<SubscriptionEventContext, ErrorContext> {\n            /// <summary>\n            /// Internal API. Construct <c>SubscriptionHandle</c>s using <c>conn.SubscriptionBuilder</c>.\n            /// </summary>\n            public SubscriptionHandle(\n                IDbConnection conn,\n                Action<SubscriptionEventContext>? onApplied,\n                Action<ErrorContext, Exception>? onError,\n                string[] querySqls\n            ) : base(conn, onApplied, onError, querySqls)\n            { }\n        }\n\n    public sealed class QueryBuilder\n    {\n        public From From { get; } = new();\n\n        internal static string[] AllTablesSqlQueries() => new string[]\n        {\n            new QueryBuilder().From.LoggedOutPlayer().ToSql(),\n            new QueryBuilder().From.MyPlayer().ToSql(),\n            new QueryBuilder().From.Person().ToSql(),\n            new QueryBuilder().From.Player().ToSql(),\n            new QueryBuilder().From.TestD().ToSql(),\n            new QueryBuilder().From.TestF().ToSql(),\n        }\n        ;\n    }\n\n    public sealed class From\n    {\n        public global::SpacetimeDB.Table<Player, LoggedOutPlayerCols, LoggedOutPlayerIxCols> LoggedOutPlayer() => new(\"logged_out_player\", new LoggedOutPlayerCols(\"logged_out_player\"), new LoggedOutPlayerIxCols(\"logged_out_player\"));\n        public global::SpacetimeDB.Table<Player, MyPlayerCols, MyPlayerIxCols> MyPlayer() => new(\"my_player\", new MyPlayerCols(\"my_player\"), new MyPlayerIxCols(\"my_player\"));\n        public global::SpacetimeDB.Table<Person, PersonCols, PersonIxCols> Person() => new(\"person\", new PersonCols(\"person\"), new PersonIxCols(\"person\"));\n        public global::SpacetimeDB.Table<Player, PlayerCols, PlayerIxCols> Player() => new(\"player\", new PlayerCols(\"player\"), new PlayerIxCols(\"player\"));\n        public global::SpacetimeDB.Table<TestD, TestDCols, TestDIxCols> TestD() => new(\"test_d\", new TestDCols(\"test_d\"), new TestDIxCols(\"test_d\"));\n        public global::SpacetimeDB.Table<TestFoobar, TestFCols, TestFIxCols> TestF() => new(\"test_f\", new TestFCols(\"test_f\"), new TestFIxCols(\"test_f\"));\n    }\n\n    public sealed class TypedSubscriptionBuilder\n    {\n        private readonly IDbConnection conn;\n        private Action<SubscriptionEventContext>? Applied;\n        private Action<ErrorContext, Exception>? Error;\n        private readonly List<string> querySqls = new();\n\n        internal TypedSubscriptionBuilder(IDbConnection conn, Action<SubscriptionEventContext>? applied, Action<ErrorContext, Exception>? error)\n        {\n            this.conn = conn;\n            Applied = applied;\n            Error = error;\n        }\n\n        public TypedSubscriptionBuilder OnApplied(Action<SubscriptionEventContext> callback)\n        {\n            Applied += callback;\n            return this;\n        }\n\n        public TypedSubscriptionBuilder OnError(Action<ErrorContext, Exception> callback)\n        {\n            Error += callback;\n            return this;\n        }\n\n        public TypedSubscriptionBuilder AddQuery<TRow>(Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build)\n        {\n            var qb = new QueryBuilder();\n            querySqls.Add(build(qb).ToSql());\n            return this;\n        }\n\n        public SubscriptionHandle Subscribe() => new(conn, Applied, Error, querySqls.ToArray());\n    }\n\n    public abstract partial class Reducer\n    {\n        private Reducer() { }\n    }\n\n    public abstract partial class Procedure\n    {\n        private Procedure() { }\n    }\n\n    public sealed class DbConnection : DbConnectionBase<DbConnection, RemoteTables, Reducer>\n    {\n        public override RemoteTables Db { get; }\n        public readonly RemoteReducers Reducers;\n        public readonly RemoteProcedures Procedures;\n\n        public DbConnection()\n        {\n            Db = new(this);\n            Reducers = new(this);\n            Procedures = new(this);\n        }\n\n        protected override IEventContext ToEventContext(Event<Reducer> Event) =>\n        new EventContext(this, Event);\n\n        protected override IReducerEventContext ToReducerEventContext(ReducerEvent<Reducer> reducerEvent) =>\n        new ReducerEventContext(this, reducerEvent);\n\n        protected override ISubscriptionEventContext MakeSubscriptionEventContext() =>\n        new SubscriptionEventContext(this);\n\n        protected override IErrorContext ToErrorContext(Exception exception) =>\n        new ErrorContext(this, exception);\n\n        protected override IProcedureEventContext ToProcedureEventContext(ProcedureEvent procedureEvent) =>\n        new ProcedureEventContext(this, procedureEvent);\n\n        protected override bool Dispatch(IReducerEventContext context, Reducer reducer)\n        {\n            var eventContext = (ReducerEventContext)context;\n            return reducer switch {\n                Reducer.Add args => Reducers.InvokeAdd(eventContext, args),\n                Reducer.AddPlayer args => Reducers.InvokeAddPlayer(eventContext, args),\n                Reducer.AddPrivate args => Reducers.InvokeAddPrivate(eventContext, args),\n                Reducer.AssertCallerIdentityIsModuleIdentity args => Reducers.InvokeAssertCallerIdentityIsModuleIdentity(eventContext, args),\n                Reducer.DeletePlayer args => Reducers.InvokeDeletePlayer(eventContext, args),\n                Reducer.DeletePlayersByName args => Reducers.InvokeDeletePlayersByName(eventContext, args),\n                Reducer.ListOverAge args => Reducers.InvokeListOverAge(eventContext, args),\n                Reducer.LogModuleIdentity args => Reducers.InvokeLogModuleIdentity(eventContext, args),\n                Reducer.QueryPrivate args => Reducers.InvokeQueryPrivate(eventContext, args),\n                Reducer.SayHello args => Reducers.InvokeSayHello(eventContext, args),\n                Reducer.Test args => Reducers.InvokeTest(eventContext, args),\n                Reducer.TestBtreeIndexArgs args => Reducers.InvokeTestBtreeIndexArgs(eventContext, args),\n                _ => throw new ArgumentOutOfRangeException(\"Reducer\", $\"Unknown reducer {reducer}\")\n            };\n        }\n\n        public SubscriptionBuilder SubscriptionBuilder() => new(this);\n        public event Action<ReducerEventContext, Exception> OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n    }\n}\n'''\n\"Tables/LoggedOutPlayer.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class LoggedOutPlayerHandle : RemoteTableHandle<EventContext, Player>\n        {\n            protected override string RemoteTableName => \"logged_out_player\";\n\n            public sealed class IdentityUniqueIndex : UniqueIndexBase<SpacetimeDB.Identity>\n            {\n                protected override SpacetimeDB.Identity GetKey(Player row) => row.Identity;\n\n                public IdentityUniqueIndex(LoggedOutPlayerHandle table) : base(table) { }\n            }\n\n            public readonly IdentityUniqueIndex Identity;\n\n            public sealed class NameUniqueIndex : UniqueIndexBase<string>\n            {\n                protected override string GetKey(Player row) => row.Name;\n\n                public NameUniqueIndex(LoggedOutPlayerHandle table) : base(table) { }\n            }\n\n            public readonly NameUniqueIndex Name;\n\n            public sealed class PlayerIdUniqueIndex : UniqueIndexBase<ulong>\n            {\n                protected override ulong GetKey(Player row) => row.PlayerId;\n\n                public PlayerIdUniqueIndex(LoggedOutPlayerHandle table) : base(table) { }\n            }\n\n            public readonly PlayerIdUniqueIndex PlayerId;\n\n            internal LoggedOutPlayerHandle(DbConnection conn) : base(conn)\n            {\n                Identity = new(this);\n                Name = new(this);\n                PlayerId = new(this);\n            }\n\n            protected override object GetPrimaryKey(Player row) => row.Identity;\n        }\n\n        public readonly LoggedOutPlayerHandle LoggedOutPlayer;\n    }\n\n    public sealed class LoggedOutPlayerCols\n    {\n        public global::SpacetimeDB.Col<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.Col<Player, ulong> PlayerId { get; }\n        public global::SpacetimeDB.Col<Player, string> Name { get; }\n\n        public LoggedOutPlayerCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.Col<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.Col<Player, ulong>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.Col<Player, string>(tableName, \"name\");\n        }\n    }\n\n    public sealed class LoggedOutPlayerIxCols\n    {\n        public global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.IxCol<Player, ulong> PlayerId { get; }\n        public global::SpacetimeDB.IxCol<Player, string> Name { get; }\n\n        public LoggedOutPlayerIxCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.IxCol<Player, ulong>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.IxCol<Player, string>(tableName, \"name\");\n        }\n    }\n}\n'''\n\"Tables/MyPlayer.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class MyPlayerHandle : RemoteTableHandle<EventContext, Player>\n        {\n            protected override string RemoteTableName => \"my_player\";\n\n            internal MyPlayerHandle(DbConnection conn) : base(conn)\n            {\n            }\n        }\n\n        public readonly MyPlayerHandle MyPlayer;\n    }\n\n    public sealed class MyPlayerCols\n    {\n        public global::SpacetimeDB.Col<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.Col<Player, ulong> PlayerId { get; }\n        public global::SpacetimeDB.Col<Player, string> Name { get; }\n\n        public MyPlayerCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.Col<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.Col<Player, ulong>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.Col<Player, string>(tableName, \"name\");\n        }\n    }\n\n    public sealed class MyPlayerIxCols\n    {\n\n        public MyPlayerIxCols(string tableName)\n        {\n        }\n    }\n}\n'''\n\"Tables/Person.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class PersonHandle : RemoteTableHandle<EventContext, Person>\n        {\n            protected override string RemoteTableName => \"person\";\n\n            public sealed class AgeIndex : BTreeIndexBase<byte>\n            {\n                protected override byte GetKey(Person row) => row.Age;\n\n                public AgeIndex(PersonHandle table) : base(table) { }\n            }\n\n            public readonly AgeIndex Age;\n\n            public sealed class IdUniqueIndex : UniqueIndexBase<uint>\n            {\n                protected override uint GetKey(Person row) => row.Id;\n\n                public IdUniqueIndex(PersonHandle table) : base(table) { }\n            }\n\n            public readonly IdUniqueIndex Id;\n\n            internal PersonHandle(DbConnection conn) : base(conn)\n            {\n                Age = new(this);\n                Id = new(this);\n            }\n\n            protected override object GetPrimaryKey(Person row) => row.Id;\n        }\n\n        public readonly PersonHandle Person;\n    }\n\n    public sealed class PersonCols\n    {\n        public global::SpacetimeDB.Col<Person, uint> Id { get; }\n        public global::SpacetimeDB.Col<Person, string> Name { get; }\n        public global::SpacetimeDB.Col<Person, byte> Age { get; }\n\n        public PersonCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.Col<Person, uint>(tableName, \"id\");\n            Name = new global::SpacetimeDB.Col<Person, string>(tableName, \"name\");\n            Age = new global::SpacetimeDB.Col<Person, byte>(tableName, \"age\");\n        }\n    }\n\n    public sealed class PersonIxCols\n    {\n        public global::SpacetimeDB.IxCol<Person, uint> Id { get; }\n        public global::SpacetimeDB.IxCol<Person, byte> Age { get; }\n\n        public PersonIxCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.IxCol<Person, uint>(tableName, \"id\");\n            Age = new global::SpacetimeDB.IxCol<Person, byte>(tableName, \"age\");\n        }\n    }\n}\n'''\n\"Tables/Player.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class PlayerHandle : RemoteTableHandle<EventContext, Player>\n        {\n            protected override string RemoteTableName => \"player\";\n\n            public sealed class IdentityUniqueIndex : UniqueIndexBase<SpacetimeDB.Identity>\n            {\n                protected override SpacetimeDB.Identity GetKey(Player row) => row.Identity;\n\n                public IdentityUniqueIndex(PlayerHandle table) : base(table) { }\n            }\n\n            public readonly IdentityUniqueIndex Identity;\n\n            public sealed class NameUniqueIndex : UniqueIndexBase<string>\n            {\n                protected override string GetKey(Player row) => row.Name;\n\n                public NameUniqueIndex(PlayerHandle table) : base(table) { }\n            }\n\n            public readonly NameUniqueIndex Name;\n\n            public sealed class PlayerIdUniqueIndex : UniqueIndexBase<ulong>\n            {\n                protected override ulong GetKey(Player row) => row.PlayerId;\n\n                public PlayerIdUniqueIndex(PlayerHandle table) : base(table) { }\n            }\n\n            public readonly PlayerIdUniqueIndex PlayerId;\n\n            internal PlayerHandle(DbConnection conn) : base(conn)\n            {\n                Identity = new(this);\n                Name = new(this);\n                PlayerId = new(this);\n            }\n\n            protected override object GetPrimaryKey(Player row) => row.Identity;\n        }\n\n        public readonly PlayerHandle Player;\n    }\n\n    public sealed class PlayerCols\n    {\n        public global::SpacetimeDB.Col<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.Col<Player, ulong> PlayerId { get; }\n        public global::SpacetimeDB.Col<Player, string> Name { get; }\n\n        public PlayerCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.Col<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.Col<Player, ulong>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.Col<Player, string>(tableName, \"name\");\n        }\n    }\n\n    public sealed class PlayerIxCols\n    {\n        public global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.IxCol<Player, ulong> PlayerId { get; }\n        public global::SpacetimeDB.IxCol<Player, string> Name { get; }\n\n        public PlayerIxCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.IxCol<Player, ulong>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.IxCol<Player, string>(tableName, \"name\");\n        }\n    }\n}\n'''\n\"Tables/TestD.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class TestDHandle : RemoteTableHandle<EventContext, TestD>\n        {\n            protected override string RemoteTableName => \"test_d\";\n\n            internal TestDHandle(DbConnection conn) : base(conn)\n            {\n            }\n        }\n\n        public readonly TestDHandle TestD;\n    }\n\n    public sealed class TestDCols\n    {\n        public global::SpacetimeDB.Col<TestD, NamespaceTestC> TestC { get; }\n\n        public TestDCols(string tableName)\n        {\n            TestC = new global::SpacetimeDB.Col<TestD, NamespaceTestC>(tableName, \"test_c\");\n        }\n    }\n\n    public sealed class TestDIxCols\n    {\n\n        public TestDIxCols(string tableName)\n        {\n        }\n    }\n}\n'''\n\"Tables/TestF.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class TestFHandle : RemoteTableHandle<EventContext, TestFoobar>\n        {\n            protected override string RemoteTableName => \"test_f\";\n\n            internal TestFHandle(DbConnection conn) : base(conn)\n            {\n            }\n        }\n\n        public readonly TestFHandle TestF;\n    }\n\n    public sealed class TestFCols\n    {\n        public global::SpacetimeDB.Col<TestFoobar, Foobar> Field { get; }\n\n        public TestFCols(string tableName)\n        {\n            Field = new global::SpacetimeDB.Col<TestFoobar, Foobar>(tableName, \"field\");\n        }\n    }\n\n    public sealed class TestFIxCols\n    {\n\n        public TestFIxCols(string tableName)\n        {\n        }\n    }\n}\n'''\n\"Types/Baz.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Baz\n    {\n        [DataMember(Name = \"field\")]\n        public string Field;\n\n        public Baz(string Field)\n        {\n            this.Field = Field;\n        }\n\n        public Baz()\n        {\n            this.Field = \"\";\n        }\n    }\n}\n'''\n\"Types/Foobar.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    public partial record Foobar : SpacetimeDB.TaggedEnum<(\n        Baz Baz,\n        SpacetimeDB.Unit Bar,\n        uint Har\n    )>;\n}\n'''\n\"Types/HasSpecialStuff.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class HasSpecialStuff\n    {\n        [DataMember(Name = \"identity\")]\n        public SpacetimeDB.Identity Identity;\n        [DataMember(Name = \"connection_id\")]\n        public SpacetimeDB.ConnectionId ConnectionId;\n\n        public HasSpecialStuff(\n            SpacetimeDB.Identity Identity,\n            SpacetimeDB.ConnectionId ConnectionId\n        )\n        {\n            this.Identity = Identity;\n            this.ConnectionId = ConnectionId;\n        }\n\n        public HasSpecialStuff()\n        {\n        }\n    }\n}\n'''\n\"Types/NamespaceTestC.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    public enum NamespaceTestC\n    {\n        Foo,\n        Bar,\n    }\n}\n'''\n\"Types/NamespaceTestF.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    public partial record NamespaceTestF : SpacetimeDB.TaggedEnum<(\n        SpacetimeDB.Unit Foo,\n        SpacetimeDB.Unit Bar,\n        string Baz\n    )>;\n}\n'''\n\"Types/Person.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Person\n    {\n        [DataMember(Name = \"id\")]\n        public uint Id;\n        [DataMember(Name = \"name\")]\n        public string Name;\n        [DataMember(Name = \"age\")]\n        public byte Age;\n\n        public Person(\n            uint Id,\n            string Name,\n            byte Age\n        )\n        {\n            this.Id = Id;\n            this.Name = Name;\n            this.Age = Age;\n        }\n\n        public Person()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n'''\n\"Types/PkMultiIdentity.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class PkMultiIdentity\n    {\n        [DataMember(Name = \"id\")]\n        public uint Id;\n        [DataMember(Name = \"other\")]\n        public uint Other;\n\n        public PkMultiIdentity(\n            uint Id,\n            uint Other\n        )\n        {\n            this.Id = Id;\n            this.Other = Other;\n        }\n\n        public PkMultiIdentity()\n        {\n        }\n    }\n}\n'''\n\"Types/Player.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Player\n    {\n        [DataMember(Name = \"identity\")]\n        public SpacetimeDB.Identity Identity;\n        [DataMember(Name = \"player_id\")]\n        public ulong PlayerId;\n        [DataMember(Name = \"name\")]\n        public string Name;\n\n        public Player(\n            SpacetimeDB.Identity Identity,\n            ulong PlayerId,\n            string Name\n        )\n        {\n            this.Identity = Identity;\n            this.PlayerId = PlayerId;\n            this.Name = Name;\n        }\n\n        public Player()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n'''\n\"Types/Point.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Point\n    {\n        [DataMember(Name = \"x\")]\n        public long X;\n        [DataMember(Name = \"y\")]\n        public long Y;\n\n        public Point(\n            long X,\n            long Y\n        )\n        {\n            this.X = X;\n            this.Y = Y;\n        }\n\n        public Point()\n        {\n        }\n    }\n}\n'''\n\"Types/PrivateTable.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class PrivateTable\n    {\n        [DataMember(Name = \"name\")]\n        public string Name;\n\n        public PrivateTable(string Name)\n        {\n            this.Name = Name;\n        }\n\n        public PrivateTable()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n'''\n\"Types/RemoveTable.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RemoveTable\n    {\n        [DataMember(Name = \"id\")]\n        public uint Id;\n\n        public RemoveTable(uint Id)\n        {\n            this.Id = Id;\n        }\n\n        public RemoveTable()\n        {\n        }\n    }\n}\n'''\n\"Types/RepeatingTestArg.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class RepeatingTestArg\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n        [DataMember(Name = \"prev_time\")]\n        public SpacetimeDB.Timestamp PrevTime;\n\n        public RepeatingTestArg(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt,\n            SpacetimeDB.Timestamp PrevTime\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n            this.PrevTime = PrevTime;\n        }\n\n        public RepeatingTestArg()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n'''\n\"Types/TestA.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TestA\n    {\n        [DataMember(Name = \"x\")]\n        public uint X;\n        [DataMember(Name = \"y\")]\n        public uint Y;\n        [DataMember(Name = \"z\")]\n        public string Z;\n\n        public TestA(\n            uint X,\n            uint Y,\n            string Z\n        )\n        {\n            this.X = X;\n            this.Y = Y;\n            this.Z = Z;\n        }\n\n        public TestA()\n        {\n            this.Z = \"\";\n        }\n    }\n}\n'''\n\"Types/TestB.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TestB\n    {\n        [DataMember(Name = \"foo\")]\n        public string Foo;\n\n        public TestB(string Foo)\n        {\n            this.Foo = Foo;\n        }\n\n        public TestB()\n        {\n            this.Foo = \"\";\n        }\n    }\n}\n'''\n\"Types/TestD.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TestD\n    {\n        [DataMember(Name = \"test_c\")]\n        public NamespaceTestC? TestC;\n\n        public TestD(NamespaceTestC? TestC)\n        {\n            this.TestC = TestC;\n        }\n\n        public TestD()\n        {\n        }\n    }\n}\n'''\n\"Types/TestE.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TestE\n    {\n        [DataMember(Name = \"id\")]\n        public ulong Id;\n        [DataMember(Name = \"name\")]\n        public string Name;\n\n        public TestE(\n            ulong Id,\n            string Name\n        )\n        {\n            this.Id = Id;\n            this.Name = Name;\n        }\n\n        public TestE()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n'''\n\"Types/TestFoobar.g.cs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class TestFoobar\n    {\n        [DataMember(Name = \"field\")]\n        public Foobar Field;\n\n        public TestFoobar(Foobar Field)\n        {\n            this.Field = Field;\n        }\n\n        public TestFoobar()\n        {\n            this.Field = null!;\n        }\n    }\n}\n'''\n"
  },
  {
    "path": "crates/codegen/tests/snapshots/codegen__codegen_rust.snap",
    "content": "---\nsource: crates/codegen/tests/codegen.rs\nexpression: outfiles\n---\n\"add_player_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct AddPlayerArgs {\n    pub name: String,\n}\n\nimpl From<AddPlayerArgs> for super::Reducer {\n    fn from(args: AddPlayerArgs) -> Self {\n        Self::AddPlayer {\n            name: args.name,\n}\n}\n}\n\nimpl __sdk::InModule for AddPlayerArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `add_player`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait add_player {\n    /// Request that the remote module invoke the reducer `add_player` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`add_player:add_player_then`] to run a callback after the reducer completes.\n    fn add_player(&self, name: String,\n) -> __sdk::Result<()> {\n        self.add_player_then(name,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `add_player` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn add_player_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl add_player for super::RemoteReducers {\n    fn add_player_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(AddPlayerArgs { name,  }, callback)\n    }\n}\n\n'''\n\"add_private_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct AddPrivateArgs {\n    pub name: String,\n}\n\nimpl From<AddPrivateArgs> for super::Reducer {\n    fn from(args: AddPrivateArgs) -> Self {\n        Self::AddPrivate {\n            name: args.name,\n}\n}\n}\n\nimpl __sdk::InModule for AddPrivateArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `add_private`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait add_private {\n    /// Request that the remote module invoke the reducer `add_private` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`add_private:add_private_then`] to run a callback after the reducer completes.\n    fn add_private(&self, name: String,\n) -> __sdk::Result<()> {\n        self.add_private_then(name,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `add_private` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn add_private_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl add_private for super::RemoteReducers {\n    fn add_private_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(AddPrivateArgs { name,  }, callback)\n    }\n}\n\n'''\n\"add_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct AddArgs {\n    pub name: String,\n    pub age: u8,\n}\n\nimpl From<AddArgs> for super::Reducer {\n    fn from(args: AddArgs) -> Self {\n        Self::Add {\n            name: args.name,\n            age: args.age,\n}\n}\n}\n\nimpl __sdk::InModule for AddArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `add`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait add {\n    /// Request that the remote module invoke the reducer `add` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`add:add_then`] to run a callback after the reducer completes.\n    fn add(&self, name: String,\nage: u8,\n) -> __sdk::Result<()> {\n        self.add_then(name, age,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `add` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn add_then(\n        &self,\n        name: String,\nage: u8,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl add for super::RemoteReducers {\n    fn add_then(\n        &self,\n        name: String,\nage: u8,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(AddArgs { name, age,  }, callback)\n    }\n}\n\n'''\n\"assert_caller_identity_is_module_identity_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct AssertCallerIdentityIsModuleIdentityArgs {\n    }\n\nimpl From<AssertCallerIdentityIsModuleIdentityArgs> for super::Reducer {\n    fn from(args: AssertCallerIdentityIsModuleIdentityArgs) -> Self {\n        Self::AssertCallerIdentityIsModuleIdentity\n}\n}\n\nimpl __sdk::InModule for AssertCallerIdentityIsModuleIdentityArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `assert_caller_identity_is_module_identity`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait assert_caller_identity_is_module_identity {\n    /// Request that the remote module invoke the reducer `assert_caller_identity_is_module_identity` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`assert_caller_identity_is_module_identity:assert_caller_identity_is_module_identity_then`] to run a callback after the reducer completes.\n    fn assert_caller_identity_is_module_identity(&self, ) -> __sdk::Result<()> {\n        self.assert_caller_identity_is_module_identity_then( |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `assert_caller_identity_is_module_identity` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn assert_caller_identity_is_module_identity_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl assert_caller_identity_is_module_identity for super::RemoteReducers {\n    fn assert_caller_identity_is_module_identity_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(AssertCallerIdentityIsModuleIdentityArgs {  }, callback)\n    }\n}\n\n'''\n\"baz_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct Baz {\n    pub field: String,\n}\n\n\nimpl __sdk::InModule for Baz {\n    type Module = super::RemoteModule;\n}\n\n'''\n\"delete_player_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct DeletePlayerArgs {\n    pub id: u64,\n}\n\nimpl From<DeletePlayerArgs> for super::Reducer {\n    fn from(args: DeletePlayerArgs) -> Self {\n        Self::DeletePlayer {\n            id: args.id,\n}\n}\n}\n\nimpl __sdk::InModule for DeletePlayerArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `delete_player`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait delete_player {\n    /// Request that the remote module invoke the reducer `delete_player` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`delete_player:delete_player_then`] to run a callback after the reducer completes.\n    fn delete_player(&self, id: u64,\n) -> __sdk::Result<()> {\n        self.delete_player_then(id,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `delete_player` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn delete_player_then(\n        &self,\n        id: u64,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl delete_player for super::RemoteReducers {\n    fn delete_player_then(\n        &self,\n        id: u64,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(DeletePlayerArgs { id,  }, callback)\n    }\n}\n\n'''\n\"delete_players_by_name_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct DeletePlayersByNameArgs {\n    pub name: String,\n}\n\nimpl From<DeletePlayersByNameArgs> for super::Reducer {\n    fn from(args: DeletePlayersByNameArgs) -> Self {\n        Self::DeletePlayersByName {\n            name: args.name,\n}\n}\n}\n\nimpl __sdk::InModule for DeletePlayersByNameArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `delete_players_by_name`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait delete_players_by_name {\n    /// Request that the remote module invoke the reducer `delete_players_by_name` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`delete_players_by_name:delete_players_by_name_then`] to run a callback after the reducer completes.\n    fn delete_players_by_name(&self, name: String,\n) -> __sdk::Result<()> {\n        self.delete_players_by_name_then(name,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `delete_players_by_name` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn delete_players_by_name_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl delete_players_by_name for super::RemoteReducers {\n    fn delete_players_by_name_then(\n        &self,\n        name: String,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(DeletePlayersByNameArgs { name,  }, callback)\n    }\n}\n\n'''\n\"foobar_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\nuse super::baz_type::Baz;\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub enum Foobar {\n    Baz(Baz),\n\n    Bar,\n\n    Har(u32),\n\n}\n\n\n\nimpl __sdk::InModule for Foobar {\n    type Module = super::RemoteModule;\n}\n\n'''\n\"get_my_schema_via_http_procedure.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\n struct GetMySchemaViaHttpArgs {\n    }\n\n\nimpl __sdk::InModule for GetMySchemaViaHttpArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the procedure `get_my_schema_via_http`.\n///\n/// Implemented for [`super::RemoteProcedures`].\npub trait get_my_schema_via_http {\n    fn get_my_schema_via_http(&self, ) {\n        self.get_my_schema_via_http_then( |_, _| {});\n    }\n\n    fn get_my_schema_via_http_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>) + Send + 'static,\n    );\n}\n\nimpl get_my_schema_via_http for super::RemoteProcedures {\n    fn get_my_schema_via_http_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>) + Send + 'static,\n    ) {\n        self.imp.invoke_procedure_with_callback::<_, String>(\n            \"get_my_schema_via_http\",\n            GetMySchemaViaHttpArgs {  },\n            __callback,\n        );\n    }\n}\n\n'''\n\"has_special_stuff_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct HasSpecialStuff {\n    pub identity: __sdk::Identity,\n    pub connection_id: __sdk::ConnectionId,\n}\n\n\nimpl __sdk::InModule for HasSpecialStuff {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `HasSpecialStuff`.\n///\n/// Provides typed access to columns for query building.\npub struct HasSpecialStuffCols {\n    pub identity: __sdk::__query_builder::Col<HasSpecialStuff, __sdk::Identity>,\n    pub connection_id: __sdk::__query_builder::Col<HasSpecialStuff, __sdk::ConnectionId>,\n}\n\nimpl __sdk::__query_builder::HasCols for HasSpecialStuff {\n    type Cols = HasSpecialStuffCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        HasSpecialStuffCols {\n            identity: __sdk::__query_builder::Col::new(table_name, \"identity\"),\n            connection_id: __sdk::__query_builder::Col::new(table_name, \"connection_id\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `HasSpecialStuff`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct HasSpecialStuffIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for HasSpecialStuff {\n    type IxCols = HasSpecialStuffIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        HasSpecialStuffIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for HasSpecialStuff {}\n\n'''\n\"list_over_age_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct ListOverAgeArgs {\n    pub age: u8,\n}\n\nimpl From<ListOverAgeArgs> for super::Reducer {\n    fn from(args: ListOverAgeArgs) -> Self {\n        Self::ListOverAge {\n            age: args.age,\n}\n}\n}\n\nimpl __sdk::InModule for ListOverAgeArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `list_over_age`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait list_over_age {\n    /// Request that the remote module invoke the reducer `list_over_age` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`list_over_age:list_over_age_then`] to run a callback after the reducer completes.\n    fn list_over_age(&self, age: u8,\n) -> __sdk::Result<()> {\n        self.list_over_age_then(age,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `list_over_age` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn list_over_age_then(\n        &self,\n        age: u8,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl list_over_age for super::RemoteReducers {\n    fn list_over_age_then(\n        &self,\n        age: u8,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(ListOverAgeArgs { age,  }, callback)\n    }\n}\n\n'''\n\"log_module_identity_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct LogModuleIdentityArgs {\n    }\n\nimpl From<LogModuleIdentityArgs> for super::Reducer {\n    fn from(args: LogModuleIdentityArgs) -> Self {\n        Self::LogModuleIdentity\n}\n}\n\nimpl __sdk::InModule for LogModuleIdentityArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `log_module_identity`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait log_module_identity {\n    /// Request that the remote module invoke the reducer `log_module_identity` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`log_module_identity:log_module_identity_then`] to run a callback after the reducer completes.\n    fn log_module_identity(&self, ) -> __sdk::Result<()> {\n        self.log_module_identity_then( |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `log_module_identity` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn log_module_identity_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl log_module_identity for super::RemoteReducers {\n    fn log_module_identity_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(LogModuleIdentityArgs {  }, callback)\n    }\n}\n\n'''\n\"logged_out_player_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::player_type::Player;\n\n/// Table handle for the table `logged_out_player`.\n///\n/// Obtain a handle from the [`LoggedOutPlayerTableAccess::logged_out_player`] method on [`super::RemoteTables`],\n/// like `ctx.db.logged_out_player()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.logged_out_player().on_insert(...)`.\npub struct LoggedOutPlayerTableHandle<'ctx> {\n    imp: __sdk::TableHandle<Player>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `logged_out_player`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait LoggedOutPlayerTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`LoggedOutPlayerTableHandle`], which mediates access to the table `logged_out_player`.\n    fn logged_out_player(&self) -> LoggedOutPlayerTableHandle<'_>;\n}\n\nimpl LoggedOutPlayerTableAccess for super::RemoteTables {\n    fn logged_out_player(&self) -> LoggedOutPlayerTableHandle<'_> {\n        LoggedOutPlayerTableHandle {\n            imp: self.imp.get_table::<Player>(\"logged_out_player\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct LoggedOutPlayerInsertCallbackId(__sdk::CallbackId);\npub struct LoggedOutPlayerDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for LoggedOutPlayerTableHandle<'ctx> {\n    type Row = Player;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = Player> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = LoggedOutPlayerInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> LoggedOutPlayerInsertCallbackId {\n        LoggedOutPlayerInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: LoggedOutPlayerInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = LoggedOutPlayerDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> LoggedOutPlayerDeleteCallbackId {\n        LoggedOutPlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: LoggedOutPlayerDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\npub struct LoggedOutPlayerUpdateCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::TableWithPrimaryKey for LoggedOutPlayerTableHandle<'ctx> {\n    type UpdateCallbackId = LoggedOutPlayerUpdateCallbackId;\n\n    fn on_update(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,\n    ) -> LoggedOutPlayerUpdateCallbackId {\n        LoggedOutPlayerUpdateCallbackId(self.imp.on_update(Box::new(callback)))\n    }\n\n    fn remove_on_update(&self, callback: LoggedOutPlayerUpdateCallbackId) {\n        self.imp.remove_on_update(callback.0)\n    }\n}\n\n        /// Access to the `identity` unique index on the table `logged_out_player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`LoggedOutPlayerIdentityUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.logged_out_player().identity().find(...)`.\n        pub struct LoggedOutPlayerIdentityUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, __sdk::Identity>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> LoggedOutPlayerTableHandle<'ctx> {\n            /// Get a handle on the `identity` unique index on the table `logged_out_player`.\n            pub fn identity(&self) -> LoggedOutPlayerIdentityUnique<'ctx> {\n                LoggedOutPlayerIdentityUnique {\n                    imp: self.imp.get_unique_constraint::<__sdk::Identity>(\"identity\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> LoggedOutPlayerIdentityUnique<'ctx> {\n            /// Find the subscribed row whose `identity` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &__sdk::Identity) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n        /// Access to the `player_id` unique index on the table `logged_out_player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`LoggedOutPlayerPlayerIdUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.logged_out_player().player_id().find(...)`.\n        pub struct LoggedOutPlayerPlayerIdUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, u64>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> LoggedOutPlayerTableHandle<'ctx> {\n            /// Get a handle on the `player_id` unique index on the table `logged_out_player`.\n            pub fn player_id(&self) -> LoggedOutPlayerPlayerIdUnique<'ctx> {\n                LoggedOutPlayerPlayerIdUnique {\n                    imp: self.imp.get_unique_constraint::<u64>(\"player_id\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> LoggedOutPlayerPlayerIdUnique<'ctx> {\n            /// Find the subscribed row whose `player_id` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &u64) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n        /// Access to the `name` unique index on the table `logged_out_player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`LoggedOutPlayerNameUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.logged_out_player().name().find(...)`.\n        pub struct LoggedOutPlayerNameUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, String>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> LoggedOutPlayerTableHandle<'ctx> {\n            /// Get a handle on the `name` unique index on the table `logged_out_player`.\n            pub fn name(&self) -> LoggedOutPlayerNameUnique<'ctx> {\n                LoggedOutPlayerNameUnique {\n                    imp: self.imp.get_unique_constraint::<String>(\"name\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> LoggedOutPlayerNameUnique<'ctx> {\n            /// Find the subscribed row whose `name` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &String) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n    let _table = client_cache.get_or_make_table::<Player>(\"logged_out_player\");\n    _table.add_unique_constraint::<__sdk::Identity>(\"identity\", |row| &row.identity);\n    _table.add_unique_constraint::<u64>(\"player_id\", |row| &row.player_id);\n    _table.add_unique_constraint::<String>(\"name\", |row| &row.name);\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<Player>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<Player>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `Player`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait logged_out_playerQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `Player`.\n            fn logged_out_player(&self) -> __sdk::__query_builder::Table<Player>;\n        }\n\n        impl logged_out_playerQueryTableAccess for __sdk::QueryTableAccessor {\n            fn logged_out_player(&self) -> __sdk::__query_builder::Table<Player> {\n                __sdk::__query_builder::Table::new(\"logged_out_player\")\n            }\n        }\n\n'''\n\"mod.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\nVERSION_COMMENT\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\npub mod baz_type;\npub mod foobar_type;\npub mod has_special_stuff_type;\npub mod person_type;\npub mod pk_multi_identity_type;\npub mod player_type;\npub mod point_type;\npub mod private_table_type;\npub mod remove_table_type;\npub mod repeating_test_arg_type;\npub mod test_a_type;\npub mod test_b_type;\npub mod test_d_type;\npub mod test_e_type;\npub mod test_foobar_type;\npub mod namespace_test_c_type;\npub mod namespace_test_f_type;\npub mod add_reducer;\npub mod add_player_reducer;\npub mod add_private_reducer;\npub mod assert_caller_identity_is_module_identity_reducer;\npub mod delete_player_reducer;\npub mod delete_players_by_name_reducer;\npub mod list_over_age_reducer;\npub mod log_module_identity_reducer;\npub mod query_private_reducer;\npub mod say_hello_reducer;\npub mod test_reducer;\npub mod test_btree_index_args_reducer;\npub mod logged_out_player_table;\npub mod person_table;\npub mod player_table;\npub mod test_d_table;\npub mod test_f_table;\npub mod my_player_table;\npub mod get_my_schema_via_http_procedure;\npub mod return_value_procedure;\npub mod sleep_one_second_procedure;\npub mod with_tx_procedure;\n\npub use baz_type::Baz;\npub use foobar_type::Foobar;\npub use has_special_stuff_type::HasSpecialStuff;\npub use person_type::Person;\npub use pk_multi_identity_type::PkMultiIdentity;\npub use player_type::Player;\npub use point_type::Point;\npub use private_table_type::PrivateTable;\npub use remove_table_type::RemoveTable;\npub use repeating_test_arg_type::RepeatingTestArg;\npub use test_a_type::TestA;\npub use test_b_type::TestB;\npub use test_d_type::TestD;\npub use test_e_type::TestE;\npub use test_foobar_type::TestFoobar;\npub use namespace_test_c_type::NamespaceTestC;\npub use namespace_test_f_type::NamespaceTestF;\npub use logged_out_player_table::*;\npub use my_player_table::*;\npub use person_table::*;\npub use player_table::*;\npub use test_d_table::*;\npub use test_f_table::*;\npub use add_reducer::add;\npub use add_player_reducer::add_player;\npub use add_private_reducer::add_private;\npub use assert_caller_identity_is_module_identity_reducer::assert_caller_identity_is_module_identity;\npub use delete_player_reducer::delete_player;\npub use delete_players_by_name_reducer::delete_players_by_name;\npub use list_over_age_reducer::list_over_age;\npub use log_module_identity_reducer::log_module_identity;\npub use query_private_reducer::query_private;\npub use say_hello_reducer::say_hello;\npub use test_reducer::test;\npub use test_btree_index_args_reducer::test_btree_index_args;\npub use get_my_schema_via_http_procedure::get_my_schema_via_http;\npub use return_value_procedure::return_value;\npub use sleep_one_second_procedure::sleep_one_second;\npub use with_tx_procedure::with_tx;\n\n#[derive(Clone, PartialEq, Debug)]\n\n/// One of the reducers defined by this module.\n///\n/// Contained within a [`__sdk::ReducerEvent`] in [`EventContext`]s for reducer events\n/// to indicate which reducer caused the event.\n\npub enum Reducer {\n        Add {\n        name: String,\n        age: u8,\n}    ,\n    AddPlayer {\n        name: String,\n}    ,\n    AddPrivate {\n        name: String,\n}    ,\n    AssertCallerIdentityIsModuleIdentity ,\n    DeletePlayer {\n        id: u64,\n}    ,\n    DeletePlayersByName {\n        name: String,\n}    ,\n    ListOverAge {\n        age: u8,\n}    ,\n    LogModuleIdentity ,\n    QueryPrivate ,\n    SayHello ,\n    Test {\n        arg: TestA,\n        arg_2: TestB,\n        arg_3: NamespaceTestC,\n        arg_4: NamespaceTestF,\n}    ,\n    TestBtreeIndexArgs ,\n}\n\n\nimpl __sdk::InModule for Reducer {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::Reducer for Reducer {\n    fn reducer_name(&self) -> &'static str {\n        match self {\n                        Reducer::Add { .. } => \"add\",\n            Reducer::AddPlayer { .. } => \"add_player\",\n            Reducer::AddPrivate { .. } => \"add_private\",\n            Reducer::AssertCallerIdentityIsModuleIdentity => \"assert_caller_identity_is_module_identity\",\n            Reducer::DeletePlayer { .. } => \"delete_player\",\n            Reducer::DeletePlayersByName { .. } => \"delete_players_by_name\",\n            Reducer::ListOverAge { .. } => \"list_over_age\",\n            Reducer::LogModuleIdentity => \"log_module_identity\",\n            Reducer::QueryPrivate => \"query_private\",\n            Reducer::SayHello => \"say_hello\",\n            Reducer::Test { .. } => \"test\",\n            Reducer::TestBtreeIndexArgs => \"test_btree_index_args\",\n            _ => unreachable!(),\n}\n}\n    #[allow(clippy::clone_on_copy)]\nfn args_bsatn(&self) -> Result<Vec<u8>, __sats::bsatn::EncodeError> {\n        match self {\n                        Reducer::Add{\n                name,\n                age,\n}             => __sats::bsatn::to_vec(&add_reducer::AddArgs {\n                name: name.clone(),\n                age: age.clone(),\n}),\n            Reducer::AddPlayer{\n                name,\n}             => __sats::bsatn::to_vec(&add_player_reducer::AddPlayerArgs {\n                name: name.clone(),\n}),\n            Reducer::AddPrivate{\n                name,\n}             => __sats::bsatn::to_vec(&add_private_reducer::AddPrivateArgs {\n                name: name.clone(),\n}),\n            Reducer::AssertCallerIdentityIsModuleIdentity => __sats::bsatn::to_vec(&assert_caller_identity_is_module_identity_reducer::AssertCallerIdentityIsModuleIdentityArgs {\n                }),\nReducer::DeletePlayer{\n                id,\n}             => __sats::bsatn::to_vec(&delete_player_reducer::DeletePlayerArgs {\n                id: id.clone(),\n}),\n            Reducer::DeletePlayersByName{\n                name,\n}             => __sats::bsatn::to_vec(&delete_players_by_name_reducer::DeletePlayersByNameArgs {\n                name: name.clone(),\n}),\n            Reducer::ListOverAge{\n                age,\n}             => __sats::bsatn::to_vec(&list_over_age_reducer::ListOverAgeArgs {\n                age: age.clone(),\n}),\n            Reducer::LogModuleIdentity => __sats::bsatn::to_vec(&log_module_identity_reducer::LogModuleIdentityArgs {\n                }),\nReducer::QueryPrivate => __sats::bsatn::to_vec(&query_private_reducer::QueryPrivateArgs {\n                }),\nReducer::SayHello => __sats::bsatn::to_vec(&say_hello_reducer::SayHelloArgs {\n                }),\nReducer::Test{\n                arg,\n                arg_2,\n                arg_3,\n                arg_4,\n}             => __sats::bsatn::to_vec(&test_reducer::TestArgs {\n                arg: arg.clone(),\n                arg_2: arg_2.clone(),\n                arg_3: arg_3.clone(),\n                arg_4: arg_4.clone(),\n}),\n            Reducer::TestBtreeIndexArgs => __sats::bsatn::to_vec(&test_btree_index_args_reducer::TestBtreeIndexArgsArgs {\n                }),\n_ => unreachable!(),\n}\n}\n}\n\n#[derive(Default, Debug)]\n#[allow(non_snake_case)]\n#[doc(hidden)]\npub struct DbUpdate {\n        logged_out_player: __sdk::TableUpdate<Player>,\n    my_player: __sdk::TableUpdate<Player>,\n    person: __sdk::TableUpdate<Person>,\n    player: __sdk::TableUpdate<Player>,\n    test_d: __sdk::TableUpdate<TestD>,\n    test_f: __sdk::TableUpdate<TestFoobar>,\n}\n\n\nimpl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {\n    type Error = __sdk::Error;\n    fn try_from(raw: __ws::v2::TransactionUpdate) -> Result<Self, Self::Error> {\n        let mut db_update = DbUpdate::default();\n        for table_update in __sdk::transaction_update_iter_table_updates(raw) {\n            match &table_update.table_name[..] {\n\n        \"logged_out_player\" => db_update.logged_out_player.append(logged_out_player_table::parse_table_update(table_update)?),\n    \"my_player\" => db_update.my_player.append(my_player_table::parse_table_update(table_update)?),\n    \"person\" => db_update.person.append(person_table::parse_table_update(table_update)?),\n    \"player\" => db_update.player.append(player_table::parse_table_update(table_update)?),\n    \"test_d\" => db_update.test_d.append(test_d_table::parse_table_update(table_update)?),\n    \"test_f\" => db_update.test_f.append(test_f_table::parse_table_update(table_update)?),\n\n                unknown => {\n                    return Err(__sdk::InternalError::unknown_name(\n                        \"table\",\n                        unknown,\n                        \"DatabaseUpdate\",\n                    ).into());\n                }\n            }\n        }\n        Ok(db_update)\n    }\n}\n\nimpl __sdk::InModule for DbUpdate {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbUpdate for DbUpdate {\n    fn apply_to_client_cache(&self, cache: &mut __sdk::ClientCache<RemoteModule>) -> AppliedDiff<'_> {\n                    let mut diff = AppliedDiff::default();\n                \n                diff.logged_out_player = cache.apply_diff_to_table::<Player>(\"logged_out_player\", &self.logged_out_player).with_updates_by_pk(|row| &row.identity);\n        diff.person = cache.apply_diff_to_table::<Person>(\"person\", &self.person).with_updates_by_pk(|row| &row.id);\n        diff.player = cache.apply_diff_to_table::<Player>(\"player\", &self.player).with_updates_by_pk(|row| &row.identity);\n        diff.test_d = cache.apply_diff_to_table::<TestD>(\"test_d\", &self.test_d);\n        diff.test_f = cache.apply_diff_to_table::<TestFoobar>(\"test_f\", &self.test_f);\n        diff.my_player = cache.apply_diff_to_table::<Player>(\"my_player\", &self.my_player);\n\n                    diff\n                }\nfn parse_initial_rows(raw: __ws::v2::QueryRows) -> __sdk::Result<Self> {\n                let mut db_update = DbUpdate::default();\nfor table_rows in raw.tables {\n            match &table_rows.table[..] {\n                                \"logged_out_player\" => db_update.logged_out_player.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                \"my_player\" => db_update.my_player.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                \"person\" => db_update.person.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                \"player\" => db_update.player.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                \"test_d\" => db_update.test_d.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                \"test_f\" => db_update.test_f.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),\n                unknown => { return Err(__sdk::InternalError::unknown_name(\"table\", unknown, \"QueryRows\").into()); }\n}}        Ok(db_update)\n}\nfn parse_unsubscribe_rows(raw: __ws::v2::QueryRows) -> __sdk::Result<Self> {\n                let mut db_update = DbUpdate::default();\nfor table_rows in raw.tables {\n            match &table_rows.table[..] {\n                                \"logged_out_player\" => db_update.logged_out_player.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                \"my_player\" => db_update.my_player.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                \"person\" => db_update.person.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                \"player\" => db_update.player.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                \"test_d\" => db_update.test_d.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                \"test_f\" => db_update.test_f.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),\n                unknown => { return Err(__sdk::InternalError::unknown_name(\"table\", unknown, \"QueryRows\").into()); }\n}}        Ok(db_update)\n}\n}\n\n#[derive(Default)]\n#[allow(non_snake_case)]\n#[doc(hidden)]\npub struct AppliedDiff<'r> {\n        logged_out_player: __sdk::TableAppliedDiff<'r, Player>,\n    my_player: __sdk::TableAppliedDiff<'r, Player>,\n    person: __sdk::TableAppliedDiff<'r, Person>,\n    player: __sdk::TableAppliedDiff<'r, Player>,\n    test_d: __sdk::TableAppliedDiff<'r, TestD>,\n    test_f: __sdk::TableAppliedDiff<'r, TestFoobar>,\n    __unused: std::marker::PhantomData<&'r ()>,\n}\n\n\nimpl __sdk::InModule for AppliedDiff<'_> {\n    type Module = RemoteModule;\n}\n\nimpl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {\n    fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks<RemoteModule>) {\n                callbacks.invoke_table_row_callbacks::<Player>(\"logged_out_player\", &self.logged_out_player, event);\n        callbacks.invoke_table_row_callbacks::<Player>(\"my_player\", &self.my_player, event);\n        callbacks.invoke_table_row_callbacks::<Person>(\"person\", &self.person, event);\n        callbacks.invoke_table_row_callbacks::<Player>(\"player\", &self.player, event);\n        callbacks.invoke_table_row_callbacks::<TestD>(\"test_d\", &self.test_d, event);\n        callbacks.invoke_table_row_callbacks::<TestFoobar>(\"test_f\", &self.test_f, event);\n}\n}\n\n\n#[doc(hidden)]\n#[derive(Debug)]\npub struct RemoteModule;\n\nimpl __sdk::InModule for RemoteModule {\n    type Module = Self;\n}\n\n/// The `reducers` field of [`EventContext`] and [`DbConnection`],\n/// with methods provided by extension traits for each reducer defined by the module.\npub struct RemoteReducers {\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::InModule for RemoteReducers {\n    type Module = RemoteModule;\n}\n\n/// The `procedures` field of [`DbConnection`] and other [`DbContext`] types,\n/// with methods provided by extension traits for each procedure defined by the module.\npub struct RemoteProcedures {\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::InModule for RemoteProcedures {\n    type Module = RemoteModule;\n}\n\n/// The `db` field of [`EventContext`] and [`DbConnection`],\n/// with methods provided by extension traits for each table defined by the module.\npub struct RemoteTables {\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::InModule for RemoteTables {\n    type Module = RemoteModule;\n}\n\n/// A connection to a remote module, including a materialized view of a subset of the database.\n///\n/// Connect to a remote module by calling [`DbConnection::builder`]\n/// and using the [`__sdk::DbConnectionBuilder`] builder-pattern constructor.\n///\n/// You must explicitly advance the connection by calling any one of:\n///\n/// - [`DbConnection::frame_tick`].\n/// - [`DbConnection::run_threaded`].\n/// - [`DbConnection::run_async`].\n/// - [`DbConnection::advance_one_message`].\n/// - [`DbConnection::advance_one_message_blocking`].\n/// - [`DbConnection::advance_one_message_async`].\n///\n/// Which of these methods you should call depends on the specific needs of your application,\n/// but you must call one of them, or else the connection will never progress.\npub struct DbConnection {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    #[doc(hidden)]\n\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::InModule for DbConnection {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for DbConnection {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl DbConnection {\n    /// Builder-pattern constructor for a connection to a remote module.\n    ///\n    /// See [`__sdk::DbConnectionBuilder`] for required and optional configuration for the new connection.\n    pub fn builder() -> __sdk::DbConnectionBuilder<RemoteModule> {\n        __sdk::DbConnectionBuilder::new()\n    }\n\n    /// If any WebSocket messages are waiting, process one of them.\n    ///\n    /// Returns `true` if a message was processed, or `false` if the queue is empty.\n    /// Callers should invoke this message in a loop until it returns `false`\n    /// or for as much time is available to process messages.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::frame_tick`] each frame\n    /// to fully exhaust the queue whenever time is available.\n    pub fn advance_one_message(&self) -> __sdk::Result<bool> {\n        self.imp.advance_one_message()\n    }\n\n    /// Process one WebSocket message, potentially blocking the current thread until one is received.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::run_threaded`] to spawn a thread\n    /// which advances the connection automatically.\n    pub fn advance_one_message_blocking(&self) -> __sdk::Result<()> {\n        self.imp.advance_one_message_blocking()\n    }\n\n    /// Process one WebSocket message, `await`ing until one is received.\n    ///\n    /// Returns an error if the connection is disconnected.\n    /// If the disconnection in question was normal,\n    ///  i.e. the result of a call to [`__sdk::DbContext::disconnect`],\n    /// the returned error will be downcastable to [`__sdk::DisconnectedError`].\n    ///\n    /// This is a low-level primitive exposed for power users who need significant control over scheduling.\n    /// Most applications should call [`Self::run_async`] to run an `async` loop\n    /// which advances the connection when polled.\n    pub async fn advance_one_message_async(&self) -> __sdk::Result<()> {\n        self.imp.advance_one_message_async().await\n    }\n\n    /// Process all WebSocket messages waiting in the queue,\n    /// then return without `await`ing or blocking the current thread.\n    pub fn frame_tick(&self) -> __sdk::Result<()> {\n        self.imp.frame_tick()\n    }\n\n    /// Spawn a thread which processes WebSocket messages as they are received.\n    pub fn run_threaded(&self) -> std::thread::JoinHandle<()> {\n        self.imp.run_threaded()\n    }\n\n    /// Run an `async` loop which processes WebSocket messages when polled.\n    pub async fn run_async(&self) -> __sdk::Result<()> {\n        self.imp.run_async().await\n    }\n}\n\nimpl __sdk::DbConnection for DbConnection {\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            imp,\n        }\n    }\n}\n\n/// A handle on a subscribed query.\n// TODO: Document this better after implementing the new subscription API.\n#[derive(Clone)]\npub struct SubscriptionHandle {\n    imp: __sdk::SubscriptionHandleImpl<RemoteModule>,\n}\n\nimpl __sdk::InModule for SubscriptionHandle {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::SubscriptionHandle for SubscriptionHandle {\n    fn new(imp: __sdk::SubscriptionHandleImpl<RemoteModule>) -> Self {\n        Self { imp }\n    }\n\n    /// Returns true if this subscription has been terminated due to an unsubscribe call or an error.\n    fn is_ended(&self) -> bool {\n        self.imp.is_ended()\n    }\n\n    /// Returns true if this subscription has been applied and has not yet been unsubscribed.\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    /// Unsubscribe from the query controlled by this `SubscriptionHandle`,\n    /// then run `on_end` when its rows are removed from the client cache.\n    fn unsubscribe_then(self, on_end: __sdk::OnEndedCallback<RemoteModule>) -> __sdk::Result<()> {\n        self.imp.unsubscribe_then(Some(on_end))\n    }\n\n    fn unsubscribe(self) -> __sdk::Result<()> {\n        self.imp.unsubscribe_then(None)\n    }\n\n}\n\n/// Alias trait for a [`__sdk::DbContext`] connected to this module,\n/// with that trait's associated types bounded to this module's concrete types.\n///\n/// Users can use this trait as a boundary on definitions which should accept\n/// either a [`DbConnection`] or an [`EventContext`] and operate on either.\npub trait RemoteDbContext: __sdk::DbContext<\n    DbView = RemoteTables,\n    Reducers = RemoteReducers,\n    SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,\n> {}\nimpl<Ctx: __sdk::DbContext<\n    DbView = RemoteTables,\n    Reducers = RemoteReducers,\n    SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,\n>> RemoteDbContext for Ctx {}\n\n\n/// An [`__sdk::DbContext`] augmented with a [`__sdk::Event`],\n/// passed to [`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks.\npub struct EventContext {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    /// The event which caused these callbacks to run.\n    pub event: __sdk::Event<Reducer>,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::AbstractEventContext for EventContext {\n    type Event = __sdk::Event<Reducer>;\n    fn event(&self) -> &Self::Event {\n        &self.event\n    }\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            event,\n            imp,\n        }\n    }\n}\n\nimpl __sdk::InModule for EventContext {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for EventContext {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl __sdk::EventContext for EventContext {}\n\n/// An [`__sdk::DbContext`] augmented with a [`__sdk::ReducerEvent`],\n/// passed to on-reducer callbacks.\npub struct ReducerEventContext {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    /// The event which caused these callbacks to run.\n    pub event: __sdk::ReducerEvent<Reducer>,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::AbstractEventContext for ReducerEventContext {\n    type Event = __sdk::ReducerEvent<Reducer>;\n    fn event(&self) -> &Self::Event {\n        &self.event\n    }\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            event,\n            imp,\n        }\n    }\n}\n\nimpl __sdk::InModule for ReducerEventContext {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for ReducerEventContext {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl __sdk::ReducerEventContext for ReducerEventContext {}\n\n/// An [`__sdk::DbContext`] passed to procedure callbacks.\npub struct ProcedureEventContext {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::AbstractEventContext for ProcedureEventContext {\n    type Event = ();\n    fn event(&self) -> &Self::Event {\n        &()\n    }\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            imp,\n        }\n    }\n}\n\nimpl __sdk::InModule for ProcedureEventContext {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for ProcedureEventContext {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl __sdk::ProcedureEventContext for ProcedureEventContext {}\n\n/// An [`__sdk::DbContext`] passed to [`__sdk::SubscriptionBuilder::on_applied`] and [`SubscriptionHandle::unsubscribe_then`] callbacks.\npub struct SubscriptionEventContext {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::AbstractEventContext for SubscriptionEventContext {\n    type Event = ();\n    fn event(&self) -> &Self::Event {\n        &()\n    }\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, _event: Self::Event) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            imp,\n        }\n    }\n}\n\nimpl __sdk::InModule for SubscriptionEventContext {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for SubscriptionEventContext {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl __sdk::SubscriptionEventContext for SubscriptionEventContext {}\n\n/// An [`__sdk::DbContext`] augmented with a [`__sdk::Error`],\n/// passed to [`__sdk::DbConnectionBuilder::on_disconnect`], [`__sdk::DbConnectionBuilder::on_connect_error`] and [`__sdk::SubscriptionBuilder::on_error`] callbacks.\npub struct ErrorContext {\n    /// Access to tables defined by the module via extension traits implemented for [`RemoteTables`].\n    pub db: RemoteTables,\n    /// Access to reducers defined by the module via extension traits implemented for [`RemoteReducers`].\n    pub reducers: RemoteReducers,\n    /// Access to procedures defined by the module via extension traits implemented for [`RemoteProcedures`].\n    pub procedures: RemoteProcedures,\n    /// The event which caused these callbacks to run.\n    pub event: Option<__sdk::Error>,\n    imp: __sdk::DbContextImpl<RemoteModule>,\n}\n\nimpl __sdk::AbstractEventContext for ErrorContext {\n    type Event = Option<__sdk::Error>;\n    fn event(&self) -> &Self::Event {\n        &self.event\n    }\n    fn new(imp: __sdk::DbContextImpl<RemoteModule>, event: Self::Event) -> Self {\n        Self {\n            db: RemoteTables { imp: imp.clone() },\n            reducers: RemoteReducers { imp: imp.clone() },\n            procedures: RemoteProcedures { imp: imp.clone() },\n            event,\n            imp,\n        }\n    }\n}\n\nimpl __sdk::InModule for ErrorContext {\n    type Module = RemoteModule;\n}\n\nimpl __sdk::DbContext for ErrorContext {\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n\n    fn db(&self) -> &Self::DbView {\n        &self.db\n    }\n    fn reducers(&self) -> &Self::Reducers {\n        &self.reducers\n    }\n    fn procedures(&self) -> &Self::Procedures {\n        &self.procedures\n    }\n\n    fn is_active(&self) -> bool {\n        self.imp.is_active()\n    }\n\n    fn disconnect(&self) -> __sdk::Result<()> {\n        self.imp.disconnect()\n    }\n\n    type SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>;\n\n    fn subscription_builder(&self) -> Self::SubscriptionBuilder {\n        __sdk::SubscriptionBuilder::new(&self.imp)\n    }\n\n    fn try_identity(&self) -> Option<__sdk::Identity> {\n        self.imp.try_identity()\n    }\n    fn connection_id(&self) -> __sdk::ConnectionId {\n        self.imp.connection_id()\n    }\n    fn try_connection_id(&self) -> Option<__sdk::ConnectionId> {\n        self.imp.try_connection_id()\n    }\n}\n\nimpl __sdk::ErrorContext for ErrorContext {}\n\nimpl __sdk::SpacetimeModule for RemoteModule {\n    \n    type DbConnection = DbConnection;\n    type EventContext = EventContext;\n    type ReducerEventContext = ReducerEventContext;\n    type ProcedureEventContext = ProcedureEventContext;\n    type SubscriptionEventContext = SubscriptionEventContext;\n    type ErrorContext = ErrorContext;\n    type Reducer = Reducer;\n    type DbView = RemoteTables;\n    type Reducers = RemoteReducers;\n    type Procedures = RemoteProcedures;\n    type DbUpdate = DbUpdate;\n    type AppliedDiff<'r> = AppliedDiff<'r>;\n    type SubscriptionHandle = SubscriptionHandle;\n    type QueryBuilder = __sdk::QueryBuilder;\n\nfn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {\n                logged_out_player_table::register_table(client_cache);\n        my_player_table::register_table(client_cache);\n        person_table::register_table(client_cache);\n        player_table::register_table(client_cache);\n        test_d_table::register_table(client_cache);\n        test_f_table::register_table(client_cache);\n}\nconst ALL_TABLE_NAMES: &'static [&'static str] = &[\n                \"logged_out_player\",\n        \"my_player\",\n        \"person\",\n        \"player\",\n        \"test_d\",\n        \"test_f\",\n];\n}\n'''\n\"my_player_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::player_type::Player;\n\n/// Table handle for the table `my_player`.\n///\n/// Obtain a handle from the [`MyPlayerTableAccess::my_player`] method on [`super::RemoteTables`],\n/// like `ctx.db.my_player()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.my_player().on_insert(...)`.\npub struct MyPlayerTableHandle<'ctx> {\n    imp: __sdk::TableHandle<Player>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `my_player`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait MyPlayerTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`MyPlayerTableHandle`], which mediates access to the table `my_player`.\n    fn my_player(&self) -> MyPlayerTableHandle<'_>;\n}\n\nimpl MyPlayerTableAccess for super::RemoteTables {\n    fn my_player(&self) -> MyPlayerTableHandle<'_> {\n        MyPlayerTableHandle {\n            imp: self.imp.get_table::<Player>(\"my_player\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct MyPlayerInsertCallbackId(__sdk::CallbackId);\npub struct MyPlayerDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for MyPlayerTableHandle<'ctx> {\n    type Row = Player;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = Player> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = MyPlayerInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> MyPlayerInsertCallbackId {\n        MyPlayerInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: MyPlayerInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = MyPlayerDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> MyPlayerDeleteCallbackId {\n        MyPlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: MyPlayerDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n        let _table = client_cache.get_or_make_table::<Player>(\"my_player\");\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<Player>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<Player>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `Player`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait my_playerQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `Player`.\n            fn my_player(&self) -> __sdk::__query_builder::Table<Player>;\n        }\n\n        impl my_playerQueryTableAccess for __sdk::QueryTableAccessor {\n            fn my_player(&self) -> __sdk::__query_builder::Table<Player> {\n                __sdk::__query_builder::Table::new(\"my_player\")\n            }\n        }\n\n'''\n\"namespace_test_c_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\n#[derive(Copy, Eq, Hash)]\npub enum NamespaceTestC {\n    Foo,\n\n    Bar,\n\n}\n\n\n\nimpl __sdk::InModule for NamespaceTestC {\n    type Module = super::RemoteModule;\n}\n\n'''\n\"namespace_test_f_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub enum NamespaceTestF {\n    Foo,\n\n    Bar,\n\n    Baz(String),\n\n}\n\n\n\nimpl __sdk::InModule for NamespaceTestF {\n    type Module = super::RemoteModule;\n}\n\n'''\n\"person_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::person_type::Person;\n\n/// Table handle for the table `person`.\n///\n/// Obtain a handle from the [`PersonTableAccess::person`] method on [`super::RemoteTables`],\n/// like `ctx.db.person()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.person().on_insert(...)`.\npub struct PersonTableHandle<'ctx> {\n    imp: __sdk::TableHandle<Person>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `person`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait PersonTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`PersonTableHandle`], which mediates access to the table `person`.\n    fn person(&self) -> PersonTableHandle<'_>;\n}\n\nimpl PersonTableAccess for super::RemoteTables {\n    fn person(&self) -> PersonTableHandle<'_> {\n        PersonTableHandle {\n            imp: self.imp.get_table::<Person>(\"person\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct PersonInsertCallbackId(__sdk::CallbackId);\npub struct PersonDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for PersonTableHandle<'ctx> {\n    type Row = Person;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = Person> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = PersonInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> PersonInsertCallbackId {\n        PersonInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: PersonInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = PersonDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> PersonDeleteCallbackId {\n        PersonDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: PersonDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\npub struct PersonUpdateCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::TableWithPrimaryKey for PersonTableHandle<'ctx> {\n    type UpdateCallbackId = PersonUpdateCallbackId;\n\n    fn on_update(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,\n    ) -> PersonUpdateCallbackId {\n        PersonUpdateCallbackId(self.imp.on_update(Box::new(callback)))\n    }\n\n    fn remove_on_update(&self, callback: PersonUpdateCallbackId) {\n        self.imp.remove_on_update(callback.0)\n    }\n}\n\n        /// Access to the `id` unique index on the table `person`,\n        /// which allows point queries on the field of the same name\n        /// via the [`PersonIdUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.person().id().find(...)`.\n        pub struct PersonIdUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Person, u32>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> PersonTableHandle<'ctx> {\n            /// Get a handle on the `id` unique index on the table `person`.\n            pub fn id(&self) -> PersonIdUnique<'ctx> {\n                PersonIdUnique {\n                    imp: self.imp.get_unique_constraint::<u32>(\"id\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> PersonIdUnique<'ctx> {\n            /// Find the subscribed row whose `id` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &u32) -> Option<Person> {\n                self.imp.find(col_val)\n            }\n        }\n        \n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n    let _table = client_cache.get_or_make_table::<Person>(\"person\");\n    _table.add_unique_constraint::<u32>(\"id\", |row| &row.id);\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<Person>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<Person>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `Person`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait personQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `Person`.\n            fn person(&self) -> __sdk::__query_builder::Table<Person>;\n        }\n\n        impl personQueryTableAccess for __sdk::QueryTableAccessor {\n            fn person(&self) -> __sdk::__query_builder::Table<Person> {\n                __sdk::__query_builder::Table::new(\"person\")\n            }\n        }\n\n'''\n\"person_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct Person {\n    pub id: u32,\n    pub name: String,\n    pub age: u8,\n}\n\n\nimpl __sdk::InModule for Person {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `Person`.\n///\n/// Provides typed access to columns for query building.\npub struct PersonCols {\n    pub id: __sdk::__query_builder::Col<Person, u32>,\n    pub name: __sdk::__query_builder::Col<Person, String>,\n    pub age: __sdk::__query_builder::Col<Person, u8>,\n}\n\nimpl __sdk::__query_builder::HasCols for Person {\n    type Cols = PersonCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        PersonCols {\n            id: __sdk::__query_builder::Col::new(table_name, \"id\"),\n            name: __sdk::__query_builder::Col::new(table_name, \"name\"),\n            age: __sdk::__query_builder::Col::new(table_name, \"age\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `Person`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct PersonIxCols {\n    pub age: __sdk::__query_builder::IxCol<Person, u8>,\n    pub id: __sdk::__query_builder::IxCol<Person, u32>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for Person {\n    type IxCols = PersonIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        PersonIxCols {\n            age: __sdk::__query_builder::IxCol::new(table_name, \"age\"),\n            id: __sdk::__query_builder::IxCol::new(table_name, \"id\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for Person {}\n\n'''\n\"pk_multi_identity_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct PkMultiIdentity {\n    pub id: u32,\n    pub other: u32,\n}\n\n\nimpl __sdk::InModule for PkMultiIdentity {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `PkMultiIdentity`.\n///\n/// Provides typed access to columns for query building.\npub struct PkMultiIdentityCols {\n    pub id: __sdk::__query_builder::Col<PkMultiIdentity, u32>,\n    pub other: __sdk::__query_builder::Col<PkMultiIdentity, u32>,\n}\n\nimpl __sdk::__query_builder::HasCols for PkMultiIdentity {\n    type Cols = PkMultiIdentityCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        PkMultiIdentityCols {\n            id: __sdk::__query_builder::Col::new(table_name, \"id\"),\n            other: __sdk::__query_builder::Col::new(table_name, \"other\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `PkMultiIdentity`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct PkMultiIdentityIxCols {\n    pub id: __sdk::__query_builder::IxCol<PkMultiIdentity, u32>,\n    pub other: __sdk::__query_builder::IxCol<PkMultiIdentity, u32>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for PkMultiIdentity {\n    type IxCols = PkMultiIdentityIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        PkMultiIdentityIxCols {\n            id: __sdk::__query_builder::IxCol::new(table_name, \"id\"),\n            other: __sdk::__query_builder::IxCol::new(table_name, \"other\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for PkMultiIdentity {}\n\n'''\n\"player_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::player_type::Player;\n\n/// Table handle for the table `player`.\n///\n/// Obtain a handle from the [`PlayerTableAccess::player`] method on [`super::RemoteTables`],\n/// like `ctx.db.player()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.player().on_insert(...)`.\npub struct PlayerTableHandle<'ctx> {\n    imp: __sdk::TableHandle<Player>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `player`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait PlayerTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`PlayerTableHandle`], which mediates access to the table `player`.\n    fn player(&self) -> PlayerTableHandle<'_>;\n}\n\nimpl PlayerTableAccess for super::RemoteTables {\n    fn player(&self) -> PlayerTableHandle<'_> {\n        PlayerTableHandle {\n            imp: self.imp.get_table::<Player>(\"player\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct PlayerInsertCallbackId(__sdk::CallbackId);\npub struct PlayerDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for PlayerTableHandle<'ctx> {\n    type Row = Player;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = Player> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = PlayerInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> PlayerInsertCallbackId {\n        PlayerInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: PlayerInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = PlayerDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> PlayerDeleteCallbackId {\n        PlayerDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: PlayerDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\npub struct PlayerUpdateCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::TableWithPrimaryKey for PlayerTableHandle<'ctx> {\n    type UpdateCallbackId = PlayerUpdateCallbackId;\n\n    fn on_update(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,\n    ) -> PlayerUpdateCallbackId {\n        PlayerUpdateCallbackId(self.imp.on_update(Box::new(callback)))\n    }\n\n    fn remove_on_update(&self, callback: PlayerUpdateCallbackId) {\n        self.imp.remove_on_update(callback.0)\n    }\n}\n\n        /// Access to the `identity` unique index on the table `player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`PlayerIdentityUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.player().identity().find(...)`.\n        pub struct PlayerIdentityUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, __sdk::Identity>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> PlayerTableHandle<'ctx> {\n            /// Get a handle on the `identity` unique index on the table `player`.\n            pub fn identity(&self) -> PlayerIdentityUnique<'ctx> {\n                PlayerIdentityUnique {\n                    imp: self.imp.get_unique_constraint::<__sdk::Identity>(\"identity\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> PlayerIdentityUnique<'ctx> {\n            /// Find the subscribed row whose `identity` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &__sdk::Identity) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n        /// Access to the `player_id` unique index on the table `player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`PlayerPlayerIdUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.player().player_id().find(...)`.\n        pub struct PlayerPlayerIdUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, u64>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> PlayerTableHandle<'ctx> {\n            /// Get a handle on the `player_id` unique index on the table `player`.\n            pub fn player_id(&self) -> PlayerPlayerIdUnique<'ctx> {\n                PlayerPlayerIdUnique {\n                    imp: self.imp.get_unique_constraint::<u64>(\"player_id\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> PlayerPlayerIdUnique<'ctx> {\n            /// Find the subscribed row whose `player_id` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &u64) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n        /// Access to the `name` unique index on the table `player`,\n        /// which allows point queries on the field of the same name\n        /// via the [`PlayerNameUnique::find`] method.\n        ///\n        /// Users are encouraged not to explicitly reference this type,\n        /// but to directly chain method calls,\n        /// like `ctx.db.player().name().find(...)`.\n        pub struct PlayerNameUnique<'ctx> {\n            imp: __sdk::UniqueConstraintHandle<Player, String>,\n            phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,\n        }\n\n        impl<'ctx> PlayerTableHandle<'ctx> {\n            /// Get a handle on the `name` unique index on the table `player`.\n            pub fn name(&self) -> PlayerNameUnique<'ctx> {\n                PlayerNameUnique {\n                    imp: self.imp.get_unique_constraint::<String>(\"name\"),\n                    phantom: std::marker::PhantomData,\n                }\n            }\n        }\n\n        impl<'ctx> PlayerNameUnique<'ctx> {\n            /// Find the subscribed row whose `name` column value is equal to `col_val`,\n            /// if such a row is present in the client cache.\n            pub fn find(&self, col_val: &String) -> Option<Player> {\n                self.imp.find(col_val)\n            }\n        }\n        \n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n    let _table = client_cache.get_or_make_table::<Player>(\"player\");\n    _table.add_unique_constraint::<__sdk::Identity>(\"identity\", |row| &row.identity);\n    _table.add_unique_constraint::<u64>(\"player_id\", |row| &row.player_id);\n    _table.add_unique_constraint::<String>(\"name\", |row| &row.name);\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<Player>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<Player>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `Player`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait playerQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `Player`.\n            fn player(&self) -> __sdk::__query_builder::Table<Player>;\n        }\n\n        impl playerQueryTableAccess for __sdk::QueryTableAccessor {\n            fn player(&self) -> __sdk::__query_builder::Table<Player> {\n                __sdk::__query_builder::Table::new(\"player\")\n            }\n        }\n\n'''\n\"player_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct Player {\n    pub identity: __sdk::Identity,\n    pub player_id: u64,\n    pub name: String,\n}\n\n\nimpl __sdk::InModule for Player {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `Player`.\n///\n/// Provides typed access to columns for query building.\npub struct PlayerCols {\n    pub identity: __sdk::__query_builder::Col<Player, __sdk::Identity>,\n    pub player_id: __sdk::__query_builder::Col<Player, u64>,\n    pub name: __sdk::__query_builder::Col<Player, String>,\n}\n\nimpl __sdk::__query_builder::HasCols for Player {\n    type Cols = PlayerCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        PlayerCols {\n            identity: __sdk::__query_builder::Col::new(table_name, \"identity\"),\n            player_id: __sdk::__query_builder::Col::new(table_name, \"player_id\"),\n            name: __sdk::__query_builder::Col::new(table_name, \"name\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `Player`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct PlayerIxCols {\n    pub identity: __sdk::__query_builder::IxCol<Player, __sdk::Identity>,\n    pub name: __sdk::__query_builder::IxCol<Player, String>,\n    pub player_id: __sdk::__query_builder::IxCol<Player, u64>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for Player {\n    type IxCols = PlayerIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        PlayerIxCols {\n            identity: __sdk::__query_builder::IxCol::new(table_name, \"identity\"),\n            name: __sdk::__query_builder::IxCol::new(table_name, \"name\"),\n            player_id: __sdk::__query_builder::IxCol::new(table_name, \"player_id\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for Player {}\n\n'''\n\"point_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct Point {\n    pub x: i64,\n    pub y: i64,\n}\n\n\nimpl __sdk::InModule for Point {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `Point`.\n///\n/// Provides typed access to columns for query building.\npub struct PointCols {\n    pub x: __sdk::__query_builder::Col<Point, i64>,\n    pub y: __sdk::__query_builder::Col<Point, i64>,\n}\n\nimpl __sdk::__query_builder::HasCols for Point {\n    type Cols = PointCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        PointCols {\n            x: __sdk::__query_builder::Col::new(table_name, \"x\"),\n            y: __sdk::__query_builder::Col::new(table_name, \"y\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `Point`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct PointIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for Point {\n    type IxCols = PointIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        PointIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for Point {}\n\n'''\n\"private_table_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct PrivateTable {\n    pub name: String,\n}\n\n\nimpl __sdk::InModule for PrivateTable {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `PrivateTable`.\n///\n/// Provides typed access to columns for query building.\npub struct PrivateTableCols {\n    pub name: __sdk::__query_builder::Col<PrivateTable, String>,\n}\n\nimpl __sdk::__query_builder::HasCols for PrivateTable {\n    type Cols = PrivateTableCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        PrivateTableCols {\n            name: __sdk::__query_builder::Col::new(table_name, \"name\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `PrivateTable`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct PrivateTableIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for PrivateTable {\n    type IxCols = PrivateTableIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        PrivateTableIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for PrivateTable {}\n\n'''\n\"query_private_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct QueryPrivateArgs {\n    }\n\nimpl From<QueryPrivateArgs> for super::Reducer {\n    fn from(args: QueryPrivateArgs) -> Self {\n        Self::QueryPrivate\n}\n}\n\nimpl __sdk::InModule for QueryPrivateArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `query_private`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait query_private {\n    /// Request that the remote module invoke the reducer `query_private` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`query_private:query_private_then`] to run a callback after the reducer completes.\n    fn query_private(&self, ) -> __sdk::Result<()> {\n        self.query_private_then( |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `query_private` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn query_private_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl query_private for super::RemoteReducers {\n    fn query_private_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(QueryPrivateArgs {  }, callback)\n    }\n}\n\n'''\n\"remove_table_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct RemoveTable {\n    pub id: u32,\n}\n\n\nimpl __sdk::InModule for RemoveTable {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `RemoveTable`.\n///\n/// Provides typed access to columns for query building.\npub struct RemoveTableCols {\n    pub id: __sdk::__query_builder::Col<RemoveTable, u32>,\n}\n\nimpl __sdk::__query_builder::HasCols for RemoveTable {\n    type Cols = RemoveTableCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        RemoveTableCols {\n            id: __sdk::__query_builder::Col::new(table_name, \"id\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `RemoveTable`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct RemoveTableIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for RemoveTable {\n    type IxCols = RemoveTableIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        RemoveTableIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for RemoveTable {}\n\n'''\n\"repeating_test_arg_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct RepeatingTestArg {\n    pub scheduled_id: u64,\n    pub scheduled_at: __sdk::ScheduleAt,\n    pub prev_time: __sdk::Timestamp,\n}\n\n\nimpl __sdk::InModule for RepeatingTestArg {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `RepeatingTestArg`.\n///\n/// Provides typed access to columns for query building.\npub struct RepeatingTestArgCols {\n    pub scheduled_id: __sdk::__query_builder::Col<RepeatingTestArg, u64>,\n    pub scheduled_at: __sdk::__query_builder::Col<RepeatingTestArg, __sdk::ScheduleAt>,\n    pub prev_time: __sdk::__query_builder::Col<RepeatingTestArg, __sdk::Timestamp>,\n}\n\nimpl __sdk::__query_builder::HasCols for RepeatingTestArg {\n    type Cols = RepeatingTestArgCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        RepeatingTestArgCols {\n            scheduled_id: __sdk::__query_builder::Col::new(table_name, \"scheduled_id\"),\n            scheduled_at: __sdk::__query_builder::Col::new(table_name, \"scheduled_at\"),\n            prev_time: __sdk::__query_builder::Col::new(table_name, \"prev_time\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `RepeatingTestArg`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct RepeatingTestArgIxCols {\n    pub scheduled_id: __sdk::__query_builder::IxCol<RepeatingTestArg, u64>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for RepeatingTestArg {\n    type IxCols = RepeatingTestArgIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        RepeatingTestArgIxCols {\n            scheduled_id: __sdk::__query_builder::IxCol::new(table_name, \"scheduled_id\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for RepeatingTestArg {}\n\n'''\n\"return_value_procedure.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\nuse super::baz_type::Baz;\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\n struct ReturnValueArgs {\n    pub foo: u64,\n}\n\n\nimpl __sdk::InModule for ReturnValueArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the procedure `return_value`.\n///\n/// Implemented for [`super::RemoteProcedures`].\npub trait return_value {\n    fn return_value(&self, foo: u64,\n) {\n        self.return_value_then(foo,  |_, _| {});\n    }\n\n    fn return_value_then(\n        &self,\n        foo: u64,\n\n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<Baz, __sdk::InternalError>) + Send + 'static,\n    );\n}\n\nimpl return_value for super::RemoteProcedures {\n    fn return_value_then(\n        &self,\n        foo: u64,\n\n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<Baz, __sdk::InternalError>) + Send + 'static,\n    ) {\n        self.imp.invoke_procedure_with_callback::<_, Baz>(\n            \"return_value\",\n            ReturnValueArgs { foo,  },\n            __callback,\n        );\n    }\n}\n\n'''\n\"say_hello_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct SayHelloArgs {\n    }\n\nimpl From<SayHelloArgs> for super::Reducer {\n    fn from(args: SayHelloArgs) -> Self {\n        Self::SayHello\n}\n}\n\nimpl __sdk::InModule for SayHelloArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `say_hello`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait say_hello {\n    /// Request that the remote module invoke the reducer `say_hello` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`say_hello:say_hello_then`] to run a callback after the reducer completes.\n    fn say_hello(&self, ) -> __sdk::Result<()> {\n        self.say_hello_then( |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `say_hello` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn say_hello_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl say_hello for super::RemoteReducers {\n    fn say_hello_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(SayHelloArgs {  }, callback)\n    }\n}\n\n'''\n\"sleep_one_second_procedure.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\n struct SleepOneSecondArgs {\n    }\n\n\nimpl __sdk::InModule for SleepOneSecondArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the procedure `sleep_one_second`.\n///\n/// Implemented for [`super::RemoteProcedures`].\npub trait sleep_one_second {\n    fn sleep_one_second(&self, ) {\n        self.sleep_one_second_then( |_, _| {});\n    }\n\n    fn sleep_one_second_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<(), __sdk::InternalError>) + Send + 'static,\n    );\n}\n\nimpl sleep_one_second for super::RemoteProcedures {\n    fn sleep_one_second_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<(), __sdk::InternalError>) + Send + 'static,\n    ) {\n        self.imp.invoke_procedure_with_callback::<_, ()>(\n            \"sleep_one_second\",\n            SleepOneSecondArgs {  },\n            __callback,\n        );\n    }\n}\n\n'''\n\"test_a_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct TestA {\n    pub x: u32,\n    pub y: u32,\n    pub z: String,\n}\n\n\nimpl __sdk::InModule for TestA {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `TestA`.\n///\n/// Provides typed access to columns for query building.\npub struct TestACols {\n    pub x: __sdk::__query_builder::Col<TestA, u32>,\n    pub y: __sdk::__query_builder::Col<TestA, u32>,\n    pub z: __sdk::__query_builder::Col<TestA, String>,\n}\n\nimpl __sdk::__query_builder::HasCols for TestA {\n    type Cols = TestACols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        TestACols {\n            x: __sdk::__query_builder::Col::new(table_name, \"x\"),\n            y: __sdk::__query_builder::Col::new(table_name, \"y\"),\n            z: __sdk::__query_builder::Col::new(table_name, \"z\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `TestA`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct TestAIxCols {\n    pub x: __sdk::__query_builder::IxCol<TestA, u32>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for TestA {\n    type IxCols = TestAIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        TestAIxCols {\n            x: __sdk::__query_builder::IxCol::new(table_name, \"x\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for TestA {}\n\n'''\n\"test_b_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct TestB {\n    pub foo: String,\n}\n\n\nimpl __sdk::InModule for TestB {\n    type Module = super::RemoteModule;\n}\n\n'''\n\"test_btree_index_args_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct TestBtreeIndexArgsArgs {\n    }\n\nimpl From<TestBtreeIndexArgsArgs> for super::Reducer {\n    fn from(args: TestBtreeIndexArgsArgs) -> Self {\n        Self::TestBtreeIndexArgs\n}\n}\n\nimpl __sdk::InModule for TestBtreeIndexArgsArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `test_btree_index_args`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait test_btree_index_args {\n    /// Request that the remote module invoke the reducer `test_btree_index_args` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`test_btree_index_args:test_btree_index_args_then`] to run a callback after the reducer completes.\n    fn test_btree_index_args(&self, ) -> __sdk::Result<()> {\n        self.test_btree_index_args_then( |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `test_btree_index_args` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn test_btree_index_args_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl test_btree_index_args for super::RemoteReducers {\n    fn test_btree_index_args_then(\n        &self,\n        \n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(TestBtreeIndexArgsArgs {  }, callback)\n    }\n}\n\n'''\n\"test_d_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::test_d_type::TestD;\nuse super::namespace_test_c_type::NamespaceTestC;\n\n/// Table handle for the table `test_d`.\n///\n/// Obtain a handle from the [`TestDTableAccess::test_d`] method on [`super::RemoteTables`],\n/// like `ctx.db.test_d()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.test_d().on_insert(...)`.\npub struct TestDTableHandle<'ctx> {\n    imp: __sdk::TableHandle<TestD>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `test_d`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait TestDTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`TestDTableHandle`], which mediates access to the table `test_d`.\n    fn test_d(&self) -> TestDTableHandle<'_>;\n}\n\nimpl TestDTableAccess for super::RemoteTables {\n    fn test_d(&self) -> TestDTableHandle<'_> {\n        TestDTableHandle {\n            imp: self.imp.get_table::<TestD>(\"test_d\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct TestDInsertCallbackId(__sdk::CallbackId);\npub struct TestDDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for TestDTableHandle<'ctx> {\n    type Row = TestD;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = TestD> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = TestDInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> TestDInsertCallbackId {\n        TestDInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: TestDInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = TestDDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> TestDDeleteCallbackId {\n        TestDDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: TestDDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n        let _table = client_cache.get_or_make_table::<TestD>(\"test_d\");\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<TestD>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<TestD>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `TestD`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait test_dQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `TestD`.\n            fn test_d(&self) -> __sdk::__query_builder::Table<TestD>;\n        }\n\n        impl test_dQueryTableAccess for __sdk::QueryTableAccessor {\n            fn test_d(&self) -> __sdk::__query_builder::Table<TestD> {\n                __sdk::__query_builder::Table::new(\"test_d\")\n            }\n        }\n\n'''\n\"test_d_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\nuse super::namespace_test_c_type::NamespaceTestC;\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct TestD {\n    pub test_c: Option::<NamespaceTestC>,\n}\n\n\nimpl __sdk::InModule for TestD {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `TestD`.\n///\n/// Provides typed access to columns for query building.\npub struct TestDCols {\n    pub test_c: __sdk::__query_builder::Col<TestD, Option::<NamespaceTestC>>,\n}\n\nimpl __sdk::__query_builder::HasCols for TestD {\n    type Cols = TestDCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        TestDCols {\n            test_c: __sdk::__query_builder::Col::new(table_name, \"test_c\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `TestD`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct TestDIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for TestD {\n    type IxCols = TestDIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        TestDIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for TestD {}\n\n'''\n\"test_e_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct TestE {\n    pub id: u64,\n    pub name: String,\n}\n\n\nimpl __sdk::InModule for TestE {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `TestE`.\n///\n/// Provides typed access to columns for query building.\npub struct TestECols {\n    pub id: __sdk::__query_builder::Col<TestE, u64>,\n    pub name: __sdk::__query_builder::Col<TestE, String>,\n}\n\nimpl __sdk::__query_builder::HasCols for TestE {\n    type Cols = TestECols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        TestECols {\n            id: __sdk::__query_builder::Col::new(table_name, \"id\"),\n            name: __sdk::__query_builder::Col::new(table_name, \"name\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `TestE`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct TestEIxCols {\n    pub id: __sdk::__query_builder::IxCol<TestE, u64>,\n    pub name: __sdk::__query_builder::IxCol<TestE, String>,\n}\n\nimpl __sdk::__query_builder::HasIxCols for TestE {\n    type IxCols = TestEIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        TestEIxCols {\n            id: __sdk::__query_builder::IxCol::new(table_name, \"id\"),\n            name: __sdk::__query_builder::IxCol::new(table_name, \"name\"),\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for TestE {}\n\n'''\n\"test_f_table.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\nuse super::test_foobar_type::TestFoobar;\nuse super::foobar_type::Foobar;\n\n/// Table handle for the table `test_f`.\n///\n/// Obtain a handle from the [`TestFTableAccess::test_f`] method on [`super::RemoteTables`],\n/// like `ctx.db.test_f()`.\n///\n/// Users are encouraged not to explicitly reference this type,\n/// but to directly chain method calls,\n/// like `ctx.db.test_f().on_insert(...)`.\npub struct TestFTableHandle<'ctx> {\n    imp: __sdk::TableHandle<TestFoobar>,\n    ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the table `test_f`.\n///\n/// Implemented for [`super::RemoteTables`].\npub trait TestFTableAccess {\n    #[allow(non_snake_case)]\n    /// Obtain a [`TestFTableHandle`], which mediates access to the table `test_f`.\n    fn test_f(&self) -> TestFTableHandle<'_>;\n}\n\nimpl TestFTableAccess for super::RemoteTables {\n    fn test_f(&self) -> TestFTableHandle<'_> {\n        TestFTableHandle {\n            imp: self.imp.get_table::<TestFoobar>(\"test_f\"),\n            ctx: std::marker::PhantomData,\n        }\n    }\n}\n\npub struct TestFInsertCallbackId(__sdk::CallbackId);\npub struct TestFDeleteCallbackId(__sdk::CallbackId);\n\nimpl<'ctx> __sdk::Table for TestFTableHandle<'ctx> {\n    type Row = TestFoobar;\n    type EventContext = super::EventContext;\n\n    fn count(&self) -> u64 { self.imp.count() }\n    fn iter(&self) -> impl Iterator<Item = TestFoobar> + '_ { self.imp.iter() }\n\n    type InsertCallbackId = TestFInsertCallbackId;\n\n    fn on_insert(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> TestFInsertCallbackId {\n        TestFInsertCallbackId(self.imp.on_insert(Box::new(callback)))\n    }\n\n    fn remove_on_insert(&self, callback: TestFInsertCallbackId) {\n        self.imp.remove_on_insert(callback.0)\n    }\n\n    type DeleteCallbackId = TestFDeleteCallbackId;\n\n    fn on_delete(\n        &self,\n        callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,\n    ) -> TestFDeleteCallbackId {\n        TestFDeleteCallbackId(self.imp.on_delete(Box::new(callback)))\n    }\n\n    fn remove_on_delete(&self, callback: TestFDeleteCallbackId) {\n        self.imp.remove_on_delete(callback.0)\n    }\n}\n\n#[doc(hidden)]\npub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {\n\n        let _table = client_cache.get_or_make_table::<TestFoobar>(\"test_f\");\n}\n\n#[doc(hidden)]\npub(super) fn parse_table_update(\n    raw_updates: __ws::v2::TableUpdate,\n) -> __sdk::Result<__sdk::TableUpdate<TestFoobar>> {\n    __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {\n        __sdk::InternalError::failed_parse(\n            \"TableUpdate<TestFoobar>\",\n            \"TableUpdate\",\n        ).with_cause(e).into()\n    })\n}\n\n        #[allow(non_camel_case_types)]\n        /// Extension trait for query builder access to the table `TestFoobar`.\n        ///\n        /// Implemented for [`__sdk::QueryTableAccessor`].\n        pub trait test_fQueryTableAccess {\n            #[allow(non_snake_case)]\n            /// Get a query builder for the table `TestFoobar`.\n            fn test_f(&self) -> __sdk::__query_builder::Table<TestFoobar>;\n        }\n\n        impl test_fQueryTableAccess for __sdk::QueryTableAccessor {\n            fn test_f(&self) -> __sdk::__query_builder::Table<TestFoobar> {\n                __sdk::__query_builder::Table::new(\"test_f\")\n            }\n        }\n\n'''\n\"test_foobar_type.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\nuse super::foobar_type::Foobar;\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub struct TestFoobar {\n    pub field: Foobar,\n}\n\n\nimpl __sdk::InModule for TestFoobar {\n    type Module = super::RemoteModule;\n}\n\n\n/// Column accessor struct for the table `TestFoobar`.\n///\n/// Provides typed access to columns for query building.\npub struct TestFoobarCols {\n    pub field: __sdk::__query_builder::Col<TestFoobar, Foobar>,\n}\n\nimpl __sdk::__query_builder::HasCols for TestFoobar {\n    type Cols = TestFoobarCols;\n    fn cols(table_name: &'static str) -> Self::Cols {\n        TestFoobarCols {\n            field: __sdk::__query_builder::Col::new(table_name, \"field\"),\n\n        }\n    }\n}\n\n/// Indexed column accessor struct for the table `TestFoobar`.\n///\n/// Provides typed access to indexed columns for query building.\npub struct TestFoobarIxCols {\n}\n\nimpl __sdk::__query_builder::HasIxCols for TestFoobar {\n    type IxCols = TestFoobarIxCols;\n    fn ix_cols(table_name: &'static str) -> Self::IxCols {\n        TestFoobarIxCols {\n\n        }\n    }\n}\n\nimpl __sdk::__query_builder::CanBeLookupTable for TestFoobar {}\n\n'''\n\"test_reducer.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\nuse super::test_a_type::TestA;\nuse super::test_b_type::TestB;\nuse super::namespace_test_c_type::NamespaceTestC;\nuse super::namespace_test_f_type::NamespaceTestF;\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\npub(super) struct TestArgs {\n    pub arg: TestA,\n    pub arg_2: TestB,\n    pub arg_3: NamespaceTestC,\n    pub arg_4: NamespaceTestF,\n}\n\nimpl From<TestArgs> for super::Reducer {\n    fn from(args: TestArgs) -> Self {\n        Self::Test {\n            arg: args.arg,\n            arg_2: args.arg_2,\n            arg_3: args.arg_3,\n            arg_4: args.arg_4,\n}\n}\n}\n\nimpl __sdk::InModule for TestArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the reducer `test`.\n///\n/// Implemented for [`super::RemoteReducers`].\npub trait test {\n    /// Request that the remote module invoke the reducer `test` to run as soon as possible.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and this method provides no way to listen for its completion status.\n    /// /// Use [`test:test_then`] to run a callback after the reducer completes.\n    fn test(&self, arg: TestA,\narg_2: TestB,\narg_3: NamespaceTestC,\narg_4: NamespaceTestF,\n) -> __sdk::Result<()> {\n        self.test_then(arg, arg_2, arg_3, arg_4,  |_, _| {})\n    }\n\n    /// Request that the remote module invoke the reducer `test` to run as soon as possible,\n    /// registering `callback` to run when we are notified that the reducer completed.\n    ///\n    /// This method returns immediately, and errors only if we are unable to send the request.\n    /// The reducer will run asynchronously in the future,\n    ///  and its status can be observed with the `callback`.\n    fn test_then(\n        &self,\n        arg: TestA,\narg_2: TestB,\narg_3: NamespaceTestC,\narg_4: NamespaceTestF,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()>;\n}\n\nimpl test for super::RemoteReducers {\n    fn test_then(\n        &self,\n        arg: TestA,\narg_2: TestB,\narg_3: NamespaceTestC,\narg_4: NamespaceTestF,\n\n        callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)\n            + Send\n            + 'static,\n    ) -> __sdk::Result<()> {\n        self.imp.invoke_reducer_with_callback(TestArgs { arg, arg_2, arg_3, arg_4,  }, callback)\n    }\n}\n\n'''\n\"with_tx_procedure.rs\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#![allow(unused, clippy::all)]\nuse spacetimedb_sdk::__codegen::{\n\tself as __sdk,\n\t__lib,\n\t__sats,\n\t__ws,\n};\n\n\n#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]\n#[sats(crate = __lib)]\n struct WithTxArgs {\n    }\n\n\nimpl __sdk::InModule for WithTxArgs {\n    type Module = super::RemoteModule;\n}\n\n#[allow(non_camel_case_types)]\n/// Extension trait for access to the procedure `with_tx`.\n///\n/// Implemented for [`super::RemoteProcedures`].\npub trait with_tx {\n    fn with_tx(&self, ) {\n        self.with_tx_then( |_, _| {});\n    }\n\n    fn with_tx_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<(), __sdk::InternalError>) + Send + 'static,\n    );\n}\n\nimpl with_tx for super::RemoteProcedures {\n    fn with_tx_then(\n        &self,\n        \n        __callback: impl FnOnce(&super::ProcedureEventContext, Result<(), __sdk::InternalError>) + Send + 'static,\n    ) {\n        self.imp.invoke_procedure_with_callback::<_, ()>(\n            \"with_tx\",\n            WithTxArgs {  },\n            __callback,\n        );\n    }\n}\n\n'''\n"
  },
  {
    "path": "crates/codegen/tests/snapshots/codegen__codegen_typescript.snap",
    "content": "---\nsource: crates/codegen/tests/codegen.rs\nexpression: outfiles\n---\n\"add_player_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  name: __t.string(),\n};\n'''\n\"add_private_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  name: __t.string(),\n};\n'''\n\"add_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  name: __t.string(),\n  age: __t.u8(),\n};\n'''\n\"assert_caller_identity_is_module_identity_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {};\n'''\n\"delete_player_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  id: __t.u64(),\n};\n'''\n\"delete_players_by_name_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  name: __t.string(),\n};\n'''\n\"get_my_schema_via_http_procedure.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport const params = {\n};\nexport const returnType = __t.string()'''\n\"index.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\nVERSION_COMMENT\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  DbConnectionBuilder as __DbConnectionBuilder,\n  DbConnectionImpl as __DbConnectionImpl,\n  SubscriptionBuilderImpl as __SubscriptionBuilderImpl,\n  TypeBuilder as __TypeBuilder,\n  Uuid as __Uuid,\n  convertToAccessorMap as __convertToAccessorMap,\n  makeQueryBuilder as __makeQueryBuilder,\n  procedureSchema as __procedureSchema,\n  procedures as __procedures,\n  reducerSchema as __reducerSchema,\n  reducers as __reducers,\n  schema as __schema,\n  t as __t,\n  table as __table,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type DbConnectionConfig as __DbConnectionConfig,\n  type ErrorContextInterface as __ErrorContextInterface,\n  type Event as __Event,\n  type EventContextInterface as __EventContextInterface,\n  type Infer as __Infer,\n  type QueryBuilder as __QueryBuilder,\n  type ReducerEventContextInterface as __ReducerEventContextInterface,\n  type RemoteModule as __RemoteModule,\n  type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,\n  type SubscriptionHandleImpl as __SubscriptionHandleImpl,\n} from \"spacetimedb\";\n\n// Import all reducer arg schemas\nimport AddReducer from \"./add_reducer\";\nimport AddPlayerReducer from \"./add_player_reducer\";\nimport AddPrivateReducer from \"./add_private_reducer\";\nimport AssertCallerIdentityIsModuleIdentityReducer from \"./assert_caller_identity_is_module_identity_reducer\";\nimport DeletePlayerReducer from \"./delete_player_reducer\";\nimport DeletePlayersByNameReducer from \"./delete_players_by_name_reducer\";\nimport ListOverAgeReducer from \"./list_over_age_reducer\";\nimport LogModuleIdentityReducer from \"./log_module_identity_reducer\";\nimport QueryPrivateReducer from \"./query_private_reducer\";\nimport SayHelloReducer from \"./say_hello_reducer\";\nimport TestReducer from \"./test_reducer\";\nimport TestBtreeIndexArgsReducer from \"./test_btree_index_args_reducer\";\n\n// Import all procedure arg schemas\nimport * as GetMySchemaViaHttpProcedure from \"./get_my_schema_via_http_procedure\";\nimport * as ReturnValueProcedure from \"./return_value_procedure\";\nimport * as SleepOneSecondProcedure from \"./sleep_one_second_procedure\";\nimport * as WithTxProcedure from \"./with_tx_procedure\";\n\n// Import all table schema definitions\nimport LoggedOutPlayerRow from \"./logged_out_player_table\";\nimport MyPlayerRow from \"./my_player_table\";\nimport PersonRow from \"./person_table\";\nimport PlayerRow from \"./player_table\";\nimport TestDRow from \"./test_d_table\";\nimport TestFRow from \"./test_f_table\";\n\n/** Type-only namespace exports for generated type groups. */\n\n/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */\nconst tablesSchema = __schema({\n  logged_out_player: __table({\n    name: 'logged_out_player',\n    indexes: [\n      { accessor: 'identity', name: 'logged_out_player_identity_idx_btree', algorithm: 'btree', columns: [\n        'identity',\n      ] },\n      { accessor: 'name', name: 'logged_out_player_name_idx_btree', algorithm: 'btree', columns: [\n        'name',\n      ] },\n      { accessor: 'player_id', name: 'logged_out_player_player_id_idx_btree', algorithm: 'btree', columns: [\n        'playerId',\n      ] },\n    ],\n    constraints: [\n      { name: 'logged_out_player_identity_key', constraint: 'unique', columns: ['identity'] },\n      { name: 'logged_out_player_name_key', constraint: 'unique', columns: ['name'] },\n      { name: 'logged_out_player_player_id_key', constraint: 'unique', columns: ['playerId'] },\n    ],\n  }, LoggedOutPlayerRow),\n  person: __table({\n    name: 'person',\n    indexes: [\n      { accessor: 'age', name: 'person_age_idx_btree', algorithm: 'btree', columns: [\n        'age',\n      ] },\n      { accessor: 'id', name: 'person_id_idx_btree', algorithm: 'btree', columns: [\n        'id',\n      ] },\n    ],\n    constraints: [\n      { name: 'person_id_key', constraint: 'unique', columns: ['id'] },\n    ],\n  }, PersonRow),\n  player: __table({\n    name: 'player',\n    indexes: [\n      { accessor: 'identity', name: 'player_identity_idx_btree', algorithm: 'btree', columns: [\n        'identity',\n      ] },\n      { accessor: 'name', name: 'player_name_idx_btree', algorithm: 'btree', columns: [\n        'name',\n      ] },\n      { accessor: 'player_id', name: 'player_player_id_idx_btree', algorithm: 'btree', columns: [\n        'playerId',\n      ] },\n    ],\n    constraints: [\n      { name: 'player_identity_key', constraint: 'unique', columns: ['identity'] },\n      { name: 'player_name_key', constraint: 'unique', columns: ['name'] },\n      { name: 'player_player_id_key', constraint: 'unique', columns: ['playerId'] },\n    ],\n  }, PlayerRow),\n  test_d: __table({\n    name: 'test_d',\n    indexes: [\n    ],\n    constraints: [\n    ],\n  }, TestDRow),\n  test_f: __table({\n    name: 'test_f',\n    indexes: [\n    ],\n    constraints: [\n    ],\n  }, TestFRow),\n  my_player: __table({\n    name: 'my_player',\n    indexes: [\n    ],\n    constraints: [\n    ],\n  }, MyPlayerRow),\n});\n\n/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */\nconst reducersSchema = __reducers(\n  __reducerSchema(\"add\", AddReducer),\n  __reducerSchema(\"add_player\", AddPlayerReducer),\n  __reducerSchema(\"add_private\", AddPrivateReducer),\n  __reducerSchema(\"assert_caller_identity_is_module_identity\", AssertCallerIdentityIsModuleIdentityReducer),\n  __reducerSchema(\"delete_player\", DeletePlayerReducer),\n  __reducerSchema(\"delete_players_by_name\", DeletePlayersByNameReducer),\n  __reducerSchema(\"list_over_age\", ListOverAgeReducer),\n  __reducerSchema(\"log_module_identity\", LogModuleIdentityReducer),\n  __reducerSchema(\"query_private\", QueryPrivateReducer),\n  __reducerSchema(\"say_hello\", SayHelloReducer),\n  __reducerSchema(\"test\", TestReducer),\n  __reducerSchema(\"test_btree_index_args\", TestBtreeIndexArgsReducer),\n);\n\n/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */\nconst proceduresSchema = __procedures(\n  __procedureSchema(\"get_my_schema_via_http\", GetMySchemaViaHttpProcedure.params, GetMySchemaViaHttpProcedure.returnType),\n  __procedureSchema(\"return_value\", ReturnValueProcedure.params, ReturnValueProcedure.returnType),\n  __procedureSchema(\"sleep_one_second\", SleepOneSecondProcedure.params, SleepOneSecondProcedure.returnType),\n  __procedureSchema(\"with_tx\", WithTxProcedure.params, WithTxProcedure.returnType),\n);\n\n/** The remote SpacetimeDB module schema, both runtime and type information. */\nconst REMOTE_MODULE = {\n  versionInfo: {\n    cliVersion: \"2.0.5\" as const,\n  },\n  tables: tablesSchema.schemaType.tables,\n  reducers: reducersSchema.reducersType.reducers,\n  ...proceduresSchema,\n} satisfies __RemoteModule<\n  typeof tablesSchema.schemaType,\n  typeof reducersSchema.reducersType,\n  typeof proceduresSchema\n>;\n\n/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */\nexport const tables: __QueryBuilder<typeof tablesSchema.schemaType> = __makeQueryBuilder(tablesSchema.schemaType);\n\n/** The reducers available in this remote SpacetimeDB module. */\nexport const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);\n\n/** The context type returned in callbacks for all possible events. */\nexport type EventContext = __EventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for reducer events. */\nexport type ReducerEventContext = __ReducerEventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for subscription events. */\nexport type SubscriptionEventContext = __SubscriptionEventContextInterface<typeof REMOTE_MODULE>;\n/** The context type returned in callbacks for error events. */\nexport type ErrorContext = __ErrorContextInterface<typeof REMOTE_MODULE>;\n/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */\nexport type SubscriptionHandle = __SubscriptionHandleImpl<typeof REMOTE_MODULE>;\n\n/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */\nexport class SubscriptionBuilder extends __SubscriptionBuilderImpl<typeof REMOTE_MODULE> {}\n\n/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */\nexport class DbConnectionBuilder extends __DbConnectionBuilder<DbConnection> {}\n\n/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */\nexport class DbConnection extends __DbConnectionImpl<typeof REMOTE_MODULE> {\n  /** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */\n  static builder = (): DbConnectionBuilder => {\n    return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig<typeof REMOTE_MODULE>) => new DbConnection(config));\n  };\n\n  /** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */\n  override subscriptionBuilder = (): SubscriptionBuilder => {\n    return new SubscriptionBuilder(this);\n  };\n}\n\n'''\n\"list_over_age_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {\n  age: __t.u8(),\n};\n'''\n\"log_module_identity_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {};\n'''\n\"logged_out_player_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default __t.row({\n  identity: __t.identity().primaryKey(),\n  playerId: __t.u64().name(\"player_id\"),\n  name: __t.string(),\n});\n'''\n\"my_player_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default __t.row({\n  identity: __t.identity(),\n  playerId: __t.u64().name(\"player_id\"),\n  name: __t.string(),\n});\n'''\n\"person_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default __t.row({\n  id: __t.u32().primaryKey(),\n  name: __t.string(),\n  age: __t.u8(),\n});\n'''\n\"player_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default __t.row({\n  identity: __t.identity().primaryKey(),\n  playerId: __t.u64().name(\"player_id\"),\n  name: __t.string(),\n});\n'''\n\"query_private_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {};\n'''\n\"return_value_procedure.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nimport {\n  Baz,\n} from \"./types\";\n\nexport const params = {\n  foo: __t.u64(),\n};\nexport const returnType = Baz'''\n\"say_hello_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {};\n'''\n\"sleep_one_second_procedure.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport const params = {\n};\nexport const returnType = __t.unit()'''\n\"test_btree_index_args_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport default {};\n'''\n\"test_d_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\nimport {\n  NamespaceTestC,\n} from \"./types\";\n\n\nexport default __t.row({\n  get testC() {\n    return __t.option(NamespaceTestC).name(\"test_c\");\n  },\n});\n'''\n\"test_f_table.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\nimport {\n  Foobar,\n} from \"./types\";\n\n\nexport default __t.row({\n  get field() {\n    return Foobar;\n  },\n});\n'''\n\"test_reducer.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nimport {\n  TestA,\n  TestB,\n  NamespaceTestC,\n  NamespaceTestF,\n} from \"./types\";\n\nexport default {\n  get arg() {\n    return TestA;\n  },\n  get arg2() {\n    return TestB;\n  },\n  get arg3() {\n    return NamespaceTestC;\n  },\n  get arg4() {\n    return NamespaceTestF;\n  },\n};\n'''\n\"types.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport const Baz = __t.object(\"Baz\", {\n  field: __t.string(),\n});\nexport type Baz = __Infer<typeof Baz>;\n\n// The tagged union or sum type for the algebraic type `Foobar`.\nexport const Foobar = __t.enum(\"Foobar\", {\n  get Baz() {\n    return Baz;\n  },\n  Bar: __t.unit(),\n  Har: __t.u32(),\n});\nexport type Foobar = __Infer<typeof Foobar>;\n\nexport const HasSpecialStuff = __t.object(\"HasSpecialStuff\", {\n  identity: __t.identity(),\n  connectionId: __t.connectionId(),\n});\nexport type HasSpecialStuff = __Infer<typeof HasSpecialStuff>;\n\nexport const Person = __t.object(\"Person\", {\n  id: __t.u32(),\n  name: __t.string(),\n  age: __t.u8(),\n});\nexport type Person = __Infer<typeof Person>;\n\nexport const PkMultiIdentity = __t.object(\"PkMultiIdentity\", {\n  id: __t.u32(),\n  other: __t.u32(),\n});\nexport type PkMultiIdentity = __Infer<typeof PkMultiIdentity>;\n\nexport const Player = __t.object(\"Player\", {\n  identity: __t.identity(),\n  playerId: __t.u64(),\n  name: __t.string(),\n});\nexport type Player = __Infer<typeof Player>;\n\nexport const Point = __t.object(\"Point\", {\n  x: __t.i64(),\n  y: __t.i64(),\n});\nexport type Point = __Infer<typeof Point>;\n\nexport const PrivateTable = __t.object(\"PrivateTable\", {\n  name: __t.string(),\n});\nexport type PrivateTable = __Infer<typeof PrivateTable>;\n\nexport const RemoveTable = __t.object(\"RemoveTable\", {\n  id: __t.u32(),\n});\nexport type RemoveTable = __Infer<typeof RemoveTable>;\n\nexport const RepeatingTestArg = __t.object(\"RepeatingTestArg\", {\n  scheduledId: __t.u64(),\n  scheduledAt: __t.scheduleAt(),\n  prevTime: __t.timestamp(),\n});\nexport type RepeatingTestArg = __Infer<typeof RepeatingTestArg>;\n\nexport const TestA = __t.object(\"TestA\", {\n  x: __t.u32(),\n  y: __t.u32(),\n  z: __t.string(),\n});\nexport type TestA = __Infer<typeof TestA>;\n\nexport const TestB = __t.object(\"TestB\", {\n  foo: __t.string(),\n});\nexport type TestB = __Infer<typeof TestB>;\n\nexport const TestD = __t.object(\"TestD\", {\n  get testC() {\n    return __t.option(NamespaceTestC);\n  },\n});\nexport type TestD = __Infer<typeof TestD>;\n\nexport const TestE = __t.object(\"TestE\", {\n  id: __t.u64(),\n  name: __t.string(),\n});\nexport type TestE = __Infer<typeof TestE>;\n\nexport const TestFoobar = __t.object(\"TestFoobar\", {\n  get field() {\n    return Foobar;\n  },\n});\nexport type TestFoobar = __Infer<typeof TestFoobar>;\n\n// The tagged union or sum type for the algebraic type `NamespaceTestC`.\nexport const NamespaceTestC = __t.enum(\"NamespaceTestC\", {\n  Foo: __t.unit(),\n  Bar: __t.unit(),\n});\nexport type NamespaceTestC = __Infer<typeof NamespaceTestC>;\n\n// The tagged union or sum type for the algebraic type `NamespaceTestF`.\nexport const NamespaceTestF = __t.enum(\"NamespaceTestF\", {\n  Foo: __t.unit(),\n  Bar: __t.unit(),\n  Baz: __t.string(),\n});\nexport type NamespaceTestF = __Infer<typeof NamespaceTestF>;\n\n'''\n\"types/procedures.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from \"spacetimedb\";\n\n// Import all procedure arg schemas\nimport * as GetMySchemaViaHttpProcedure from \"../get_my_schema_via_http_procedure\";\nimport * as ReturnValueProcedure from \"../return_value_procedure\";\nimport * as SleepOneSecondProcedure from \"../sleep_one_second_procedure\";\nimport * as WithTxProcedure from \"../with_tx_procedure\";\n\nexport type GetMySchemaViaHttpArgs = __Infer<typeof GetMySchemaViaHttpProcedure.params>;\nexport type GetMySchemaViaHttpResult = __Infer<typeof GetMySchemaViaHttpProcedure.returnType>;\nexport type ReturnValueArgs = __Infer<typeof ReturnValueProcedure.params>;\nexport type ReturnValueResult = __Infer<typeof ReturnValueProcedure.returnType>;\nexport type SleepOneSecondArgs = __Infer<typeof SleepOneSecondProcedure.params>;\nexport type SleepOneSecondResult = __Infer<typeof SleepOneSecondProcedure.returnType>;\nexport type WithTxArgs = __Infer<typeof WithTxProcedure.params>;\nexport type WithTxResult = __Infer<typeof WithTxProcedure.returnType>;\n\n'''\n\"types/reducers.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport { type Infer as __Infer } from \"spacetimedb\";\n\n// Import all reducer arg schemas\nimport AddReducer from \"../add_reducer\";\nimport AddPlayerReducer from \"../add_player_reducer\";\nimport AddPrivateReducer from \"../add_private_reducer\";\nimport AssertCallerIdentityIsModuleIdentityReducer from \"../assert_caller_identity_is_module_identity_reducer\";\nimport DeletePlayerReducer from \"../delete_player_reducer\";\nimport DeletePlayersByNameReducer from \"../delete_players_by_name_reducer\";\nimport ListOverAgeReducer from \"../list_over_age_reducer\";\nimport LogModuleIdentityReducer from \"../log_module_identity_reducer\";\nimport QueryPrivateReducer from \"../query_private_reducer\";\nimport SayHelloReducer from \"../say_hello_reducer\";\nimport TestReducer from \"../test_reducer\";\nimport TestBtreeIndexArgsReducer from \"../test_btree_index_args_reducer\";\n\nexport type AddParams = __Infer<typeof AddReducer>;\nexport type AddPlayerParams = __Infer<typeof AddPlayerReducer>;\nexport type AddPrivateParams = __Infer<typeof AddPrivateReducer>;\nexport type AssertCallerIdentityIsModuleIdentityParams = __Infer<typeof AssertCallerIdentityIsModuleIdentityReducer>;\nexport type DeletePlayerParams = __Infer<typeof DeletePlayerReducer>;\nexport type DeletePlayersByNameParams = __Infer<typeof DeletePlayersByNameReducer>;\nexport type ListOverAgeParams = __Infer<typeof ListOverAgeReducer>;\nexport type LogModuleIdentityParams = __Infer<typeof LogModuleIdentityReducer>;\nexport type QueryPrivateParams = __Infer<typeof QueryPrivateReducer>;\nexport type SayHelloParams = __Infer<typeof SayHelloReducer>;\nexport type TestParams = __Infer<typeof TestReducer>;\nexport type TestBtreeIndexArgsParams = __Infer<typeof TestBtreeIndexArgsReducer>;\n\n'''\n\"with_tx_procedure.ts\" = '''\n// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n/* eslint-disable */\n/* tslint:disable */\nimport {\n  TypeBuilder as __TypeBuilder,\n  t as __t,\n  type AlgebraicTypeType as __AlgebraicTypeType,\n  type Infer as __Infer,\n} from \"spacetimedb\";\n\nexport const params = {\n};\nexport const returnType = __t.unit()'''\n"
  },
  {
    "path": "crates/commitlog/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-commitlog\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\n\ndescription = \"Implementation of the SpacetimeDB commitlog.\"\n\n[features]\ndefault = [\"serde\"]\n# Enable streaming reads + writes\nstreaming = [\"dep:async-stream\", \"dep:bytes\", \"dep:futures\", \"dep:tokio\", \"dep:tokio-util\"]\n# Enable types + impls useful for testing\ntest = [\"dep:env_logger\"]\n# Enable `fallocate` of segments\nfallocate = [\"dep:nix\"]\n\n[dependencies]\nasync-stream = { workspace = true, optional = true }\nbitflags.workspace = true\nbytes= { workspace = true, optional = true }\ncrc32c.workspace = true\nfutures = { workspace = true, optional = true }\nitertools.workspace = true\nlog.workspace = true\nmemmap2 = \"0.9.4\"\nnix = { workspace = true, optional = true, features = [\"fs\"] }\nserde = { workspace = true, optional = true }\nspacetimedb-fs-utils.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-sats.workspace = true\ntempfile.workspace = true\nthiserror.workspace = true\ntokio = { workspace = true, optional = true }\ntokio-util = { workspace = true, optional = true, features = [\"io-util\"] }\nzstd-framed.workspace = true\n\n# For the 'test' feature\nenv_logger = { workspace = true, optional = true }\npretty_assertions.workspace = true\n\n[dev-dependencies]\n# Enable streaming in tests\n# Also enable 'test' feature, so integration tests can use the helpers.\nspacetimedb-commitlog = { path = \".\", features = [\"test\", \"streaming\"] }\n\nenv_logger.workspace = true\nonce_cell.workspace = true\npretty_assertions = { workspace = true, features = [\"unstable\"] }\nproptest-derive.workspace = true\nproptest.workspace = true\nrand.workspace = true\ntempfile.workspace = true\ntokio-stream = { version = \"0.1.17\", features = [\"fs\"] }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/commitlog/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/commitlog/proptest-regressions/commit.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 99379dfa7ddc8e9547aef6261c7ed937bd6d100950ceab6dfb3b2c819d5e5eb2 # shrinks to mask = 147\ncc d7996990b19364fdd507c75b6eab42c966200a9a87c1cf20b0d525e943707028 # shrinks to mask = 0\ncc 90f9c1e55a9e1a0b6239a493778d94a99c483a96b142214ff068d08a59587474 # shrinks to mask = 234\ncc d98894bc4f25542121d91f3022916c5e140fea25834dde209a001232a7a3a122 # shrinks to mask = 54\ncc 893c2bb61cc7c758ac3b6f36485ee8789e6ed6f92ecc531ca9622e9b0e23a5d0 # shrinks to mask = 183\ncc ca54db4fcdc0af1fe3c039f58b43b6f51c065120851261bdd495c8e0afed60bd # shrinks to mask = 10\ncc 3dc841d9145fa0f6abec7fbb37fec26d47c223b00d59cf2f67ad9ee79fcab728 # shrinks to mask = 166\n"
  },
  {
    "path": "crates/commitlog/proptest-regressions/tests/bitflip.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc a224c9559a4f825676852b58397b59027a14561c8bd9439b52691234fab848de # shrinks to inputs = Inputs { byte_pos: 354, bit_mask: 205, segment_offset: 30 }\ncc a62542123f6c7a5c747cdf8d64246d93b1ba55e53f207dd0827d3bc65442cb35 # shrinks to inputs = Inputs { byte_pos: 25, bit_mask: 1, segment_offset: 0 }\n"
  },
  {
    "path": "crates/commitlog/src/commit.rs",
    "content": "use std::{\n    io::{self, Read, Write},\n    ops::Range,\n};\n\nuse crc32c::{Crc32cReader, Crc32cWriter};\nuse spacetimedb_sats::buffer::{BufReader, Cursor, DecodeError};\n\nuse crate::{\n    error::ChecksumMismatch,\n    payload::Decoder,\n    segment::{CHECKSUM_ALGORITHM_CRC32C, CHECKSUM_CRC32C_LEN},\n    Transaction, DEFAULT_LOG_FORMAT_VERSION,\n};\n\n#[derive(Default)]\nenum Version {\n    V0,\n    #[default]\n    V1,\n}\n\npub struct Header {\n    pub min_tx_offset: u64,\n    pub epoch: u64,\n    pub n: u16,\n    pub len: u32,\n}\n\nimpl Header {\n    pub const LEN: usize = /* offset */ 8 + /* epoch */ 8 + /* n */ 2 + /* len */  4;\n\n    /// Read [`Self::LEN`] bytes from `reader` and interpret them as the\n    /// \"header\" of a [`Commit`].\n    ///\n    /// Returns `None` if:\n    ///\n    /// - The reader cannot provide exactly [`Self::LEN`] bytes\n    ///\n    ///   I.e. it is at EOF\n    ///\n    /// - Or, the read bytes are all zeroes\n    ///\n    ///   This is to allow preallocation of segments.\n    ///\n    pub fn decode<R: Read>(reader: R) -> io::Result<Option<Self>> {\n        Self::decode_v1(reader)\n    }\n\n    fn decode_internal<R: Read>(reader: R, v: Version) -> io::Result<Option<Self>> {\n        use Version::*;\n        match v {\n            V0 => Self::decode_v0(reader),\n            V1 => Self::decode_v1(reader),\n        }\n    }\n\n    fn decode_v0<R: Read>(mut reader: R) -> io::Result<Option<Self>> {\n        let mut hdr = [0; Self::LEN - 8];\n        if let Err(e) = reader.read_exact(&mut hdr) {\n            if e.kind() == io::ErrorKind::UnexpectedEof {\n                return Ok(None);\n            }\n\n            return Err(e);\n        }\n        match &mut hdr.as_slice() {\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] => Ok(None),\n            buf => {\n                let min_tx_offset = buf.get_u64().map_err(decode_error)?;\n                let n = buf.get_u16().map_err(decode_error)?;\n                let len = buf.get_u32().map_err(decode_error)?;\n\n                Ok(Some(Self {\n                    min_tx_offset,\n                    epoch: Commit::DEFAULT_EPOCH,\n                    n,\n                    len,\n                }))\n            }\n        }\n    }\n\n    fn decode_v1<R: Read>(mut reader: R) -> io::Result<Option<Self>> {\n        let mut hdr = [0; Self::LEN];\n        if let Err(e) = reader.read_exact(&mut hdr) {\n            if e.kind() == io::ErrorKind::UnexpectedEof {\n                return Ok(None);\n            }\n\n            return Err(e);\n        }\n        match &mut hdr.as_slice() {\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] => Ok(None),\n            buf => {\n                let min_tx_offset = buf.get_u64().map_err(decode_error)?;\n                let epoch = buf.get_u64().map_err(decode_error)?;\n                let n = buf.get_u16().map_err(decode_error)?;\n                let len = buf.get_u32().map_err(decode_error)?;\n\n                Ok(Some(Self {\n                    min_tx_offset,\n                    epoch,\n                    n,\n                    len,\n                }))\n            }\n        }\n    }\n}\n\n/// Entry type of a [`crate::Commitlog`].\n#[derive(Clone, Debug, Default, PartialEq)]\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\npub struct Commit {\n    /// The offset of the first record in this commit.\n    ///\n    /// The offset starts from zero and is counted from the beginning of the\n    /// entire log.\n    pub min_tx_offset: u64,\n    /// The epoch within which the commit was created.\n    ///\n    /// Indicates the monotonically increasing term number of the leader when\n    /// the commitlog is being written to in a distributed deployment.\n    ///\n    /// The default epoch is 0 (zero). It should be used when the log is written\n    /// to by a single process.\n    ///\n    /// Note, however, that an existing log may have a non-zero epoch.\n    /// It is currently unspecified how a commitlog is transitioned between\n    /// distributed and single-node deployment, wrt the epoch.\n    pub epoch: u64,\n    /// The number of records in the commit.\n    pub n: u16,\n    /// A buffer of all records in the commit in serialized form.\n    ///\n    /// Readers must bring their own [`crate::Decoder`] to interpret this buffer.\n    /// `n` indicates how many records the buffer contains.\n    pub records: Vec<u8>,\n}\n\nimpl Commit {\n    pub const DEFAULT_EPOCH: u64 = 0;\n\n    pub const FRAMING_LEN: usize = Header::LEN + Self::CHECKSUM_LEN;\n    pub const CHECKSUM_ALGORITHM: u8 = CHECKSUM_ALGORITHM_CRC32C;\n    pub const CHECKSUM_LEN: usize = CHECKSUM_CRC32C_LEN;\n\n    /// The range of transaction offsets contained in this commit.\n    pub fn tx_range(&self) -> Range<u64> {\n        self.min_tx_offset..self.min_tx_offset + self.n as u64\n    }\n\n    /// Length in bytes of this commit when written to the log via [`Self::write`].\n    pub fn encoded_len(&self) -> usize {\n        Self::FRAMING_LEN + self.records.len()\n    }\n\n    /// Serialize and write `self` to `out`.\n    ///\n    /// Returns the crc32 checksum of the commit on success.\n    pub fn write<W: Write>(&self, out: W) -> io::Result<u32> {\n        let mut out = Crc32cWriter::new(out);\n\n        let min_tx_offset = self.min_tx_offset.to_le_bytes();\n        let epoch = self.epoch.to_le_bytes();\n        let n = self.n.to_le_bytes();\n        let len = (self.records.len() as u32).to_le_bytes();\n\n        out.write_all(&min_tx_offset)?;\n        out.write_all(&epoch)?;\n        out.write_all(&n)?;\n        out.write_all(&len)?;\n        out.write_all(&self.records)?;\n\n        let crc = out.crc32c();\n        let mut out = out.into_inner();\n        out.write_all(&crc.to_le_bytes())?;\n\n        Ok(crc)\n    }\n\n    /// Attempt to read one [`Commit`] from the given [`Read`]er.\n    ///\n    /// Returns `None` if the reader is already at EOF.\n    ///\n    /// Verifies the checksum of the commit. If it doesn't match, an error of\n    /// kind [`io::ErrorKind::InvalidData`] with an inner error downcastable to\n    /// [`ChecksumMismatch`] is returned.\n    ///\n    /// To retain access to the checksum, use [`StoredCommit::decode`].\n    pub fn decode<R: Read>(reader: R) -> io::Result<Option<Self>> {\n        let commit = StoredCommit::decode(reader)?;\n        Ok(commit.map(Into::into))\n    }\n\n    /// Convert `self` into an iterator yielding [`Transaction`]s.\n    ///\n    /// The supplied [`Decoder`] is responsible for extracting individual\n    /// transactions from the `records` buffer.\n    ///\n    /// `version` is the log format version of the current segment, and gets\n    /// passed to [`Decoder::decode_record`].\n    ///\n    /// `from_offset` is the transaction offset within the current commit from\n    /// which to start decoding. That is:\n    ///\n    /// * if the tx offset within the commit is smaller than `from_offset`,\n    ///   [`Decoder::skip_record`] is called.\n    ///\n    ///   The iterator does not yield a value, unless `skip_record` returns an\n    ///   error.\n    ///\n    /// * if the tx offset within the commit is greater of equal to `from_offset`,\n    ///   [`Decoder::decode_record`] is called.\n    ///\n    ///   The iterator yields the result of this call.\n    ///\n    /// * if `from_offset` doesn't fall into the current commit, the iterator\n    ///   yields nothing.\n    ///\n    pub fn into_transactions<D: Decoder>(\n        self,\n        version: u8,\n        from_offset: u64,\n        de: &D,\n    ) -> impl Iterator<Item = Result<Transaction<D::Record>, D::Error>> + '_ {\n        let records = Cursor::new(self.records);\n        (self.min_tx_offset..(self.min_tx_offset + self.n as u64))\n            .scan(records, move |recs, offset| {\n                let mut cursor = &*recs;\n                let ret = if offset < from_offset {\n                    de.skip_record(version, offset, &mut cursor).err().map(Err)\n                } else {\n                    let tx = de\n                        .decode_record(version, offset, &mut cursor)\n                        .map(|txdata| Transaction { offset, txdata });\n                    Some(tx)\n                };\n\n                Some(ret)\n            })\n            .flatten()\n    }\n}\n\nimpl From<StoredCommit> for Commit {\n    fn from(\n        StoredCommit {\n            min_tx_offset,\n            epoch,\n            n,\n            records,\n            checksum: _,\n        }: StoredCommit,\n    ) -> Self {\n        Self {\n            min_tx_offset,\n            epoch,\n            n,\n            records,\n        }\n    }\n}\n\n/// A [`Commit`] as stored on disk.\n///\n/// Differs from [`Commit`] only in the presence of a `checksum` field, which\n/// is computed when encoding a commit for storage.\n#[derive(Debug, PartialEq)]\npub struct StoredCommit {\n    /// See [`Commit::min_tx_offset`].\n    pub min_tx_offset: u64,\n    /// See [`Commit::epoch`].\n    pub epoch: u64,\n    /// See [`Commit::n`].\n    pub n: u16,\n    /// See [`Commit::records`].\n    pub records: Vec<u8>,\n    /// The checksum computed when encoding a [`Commit`] for storage.\n    pub checksum: u32,\n}\n\nimpl StoredCommit {\n    /// The range of transaction offsets contained in this commit.\n    pub fn tx_range(&self) -> Range<u64> {\n        self.min_tx_offset..self.min_tx_offset + self.n as u64\n    }\n\n    /// Attempt to read one [`StoredCommit`] from the given [`Read`]er.\n    ///\n    /// Returns `None` if the reader is already at EOF.\n    ///\n    /// Verifies the checksum of the commit. If it doesn't match, an error of\n    /// kind [`io::ErrorKind::InvalidData`] with an inner error downcastable to\n    /// [`ChecksumMismatch`] is returned.\n    pub fn decode<R: Read>(reader: R) -> io::Result<Option<Self>> {\n        Self::decode_internal(reader, DEFAULT_LOG_FORMAT_VERSION)\n    }\n\n    pub(crate) fn decode_internal<R: Read>(reader: R, log_format_version: u8) -> io::Result<Option<Self>> {\n        let mut reader = Crc32cReader::new(reader);\n\n        let v = if log_format_version == 0 {\n            Version::V0\n        } else {\n            Version::V1\n        };\n        let Some(hdr) = Header::decode_internal(&mut reader, v)? else {\n            return Ok(None);\n        };\n        let mut records = vec![0; hdr.len as usize];\n        reader.read_exact(&mut records)?;\n\n        let chk = reader.crc32c();\n        let crc = decode_u32(reader.into_inner())?;\n\n        if chk != crc {\n            return Err(invalid_data(ChecksumMismatch));\n        }\n\n        Ok(Some(Self {\n            min_tx_offset: hdr.min_tx_offset,\n            epoch: hdr.epoch,\n            n: hdr.n,\n            records,\n            checksum: crc,\n        }))\n    }\n\n    /// Convert `self` into an iterator yielding [`Transaction`]s.\n    ///\n    /// The supplied [`Decoder`] is responsible for extracting individual\n    /// transactions from the `records` buffer.\n    pub fn into_transactions<D: Decoder>(\n        self,\n        version: u8,\n        from_offset: u64,\n        de: &D,\n    ) -> impl Iterator<Item = Result<Transaction<D::Record>, D::Error>> + '_ {\n        Commit::from(self).into_transactions(version, from_offset, de)\n    }\n}\n\n/// A [`StoredCommit`] sans the records payload.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Metadata {\n    pub tx_range: Range<u64>,\n    pub size_in_bytes: u64,\n    pub epoch: u64,\n    pub checksum: u32,\n}\n\nimpl Metadata {\n    /// Extract the [`Metadata`] of a single [`StoredCommit`] from the given\n    /// reader.\n    ///\n    /// Note that this decodes the commit due to checksum verification.\n    /// Like [`StoredCommit::decode`], this method returns `None` if the reader\n    /// is at EOF already.\n    pub fn extract<R: io::Read>(reader: R) -> io::Result<Option<Self>> {\n        StoredCommit::decode(reader).map(|maybe_commit| maybe_commit.map(Self::from))\n    }\n}\n\nimpl From<StoredCommit> for Metadata {\n    fn from(commit: StoredCommit) -> Self {\n        let tx_range = commit.tx_range();\n        let epoch = commit.epoch;\n        let checksum = commit.checksum;\n        let size_in_bytes = Commit::from(commit).encoded_len() as u64;\n\n        Self {\n            tx_range,\n            size_in_bytes,\n            epoch,\n            checksum,\n        }\n    }\n}\n\nfn decode_u32<R: Read>(mut read: R) -> io::Result<u32> {\n    let mut buf = [0; 4];\n    read.read_exact(&mut buf)?;\n    Ok(u32::from_le_bytes(buf))\n}\n\nfn decode_error(e: DecodeError) -> io::Error {\n    invalid_data(e)\n}\n\nfn invalid_data<E>(e: E) -> io::Error\nwhere\n    E: Into<Box<dyn std::error::Error + Send + Sync>>,\n{\n    io::Error::new(io::ErrorKind::InvalidData, e)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::num::NonZeroU8;\n\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::{payload::ArrayDecoder, tests::helpers::enable_logging, DEFAULT_LOG_FORMAT_VERSION};\n\n    #[test]\n    fn commit_roundtrip() {\n        let records = vec![0; 128];\n        let commit = Commit {\n            min_tx_offset: 0,\n            n: 3,\n            records,\n            epoch: Commit::DEFAULT_EPOCH,\n        };\n\n        let mut buf = Vec::with_capacity(commit.encoded_len());\n        commit.write(&mut buf).unwrap();\n        let commit2 = Commit::decode(&mut buf.as_slice()).unwrap().unwrap();\n\n        assert_eq!(commit, commit2);\n    }\n\n    #[test]\n    fn into_transactions_can_skip_txs() {\n        enable_logging();\n\n        let commit = Commit {\n            min_tx_offset: 0,\n            n: 4,\n            records: vec![0; 128],\n            epoch: Commit::DEFAULT_EPOCH,\n        };\n\n        let txs = commit\n            .into_transactions(DEFAULT_LOG_FORMAT_VERSION, 2, &ArrayDecoder::<32>)\n            .collect::<Result<Vec<_>, _>>()\n            .unwrap();\n\n        assert_eq!(\n            txs,\n            vec![\n                Transaction {\n                    offset: 2,\n                    txdata: [0u8; 32]\n                },\n                Transaction {\n                    offset: 3,\n                    txdata: [0; 32]\n                }\n            ]\n        )\n    }\n\n    proptest! {\n        #[test]\n        fn bitflip(pos in Header::LEN..512, mask in any::<NonZeroU8>()) {\n            let commit = Commit {\n                min_tx_offset: 42,\n                n: 10,\n                records: vec![1; 512],\n                epoch: Commit::DEFAULT_EPOCH,\n            };\n\n            let mut buf = Vec::with_capacity(commit.encoded_len());\n            commit.write(&mut buf).unwrap();\n\n            // Flip bit in the `records` section,\n            // so we get `ChecksumMismatch` not any other error.\n            buf[pos] ^= mask.get();\n\n            match Commit::decode(&mut buf.as_slice()) {\n                Err(e) => {\n                    assert_eq!(e.kind(), io::ErrorKind::InvalidData);\n                    e.into_inner()\n                        .unwrap()\n                        .downcast::<ChecksumMismatch>()\n                        .expect(\"IO inner should be checksum mismatch\");\n                }\n                Ok(commit) => panic!(\"expected checksum mismatch, got valid commit: {commit:?}\"),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/commitlog.rs",
    "content": "use std::{\n    fmt::Debug,\n    io,\n    marker::PhantomData,\n    mem,\n    ops::{Range, RangeBounds},\n    vec,\n};\n\nuse itertools::Itertools;\nuse log::{debug, error, info, trace, warn};\n\nuse crate::{\n    commit::StoredCommit,\n    error::{self, source_chain},\n    index::IndexError,\n    payload::Decoder,\n    repo::{self, Repo, SegmentLen as _, TxOffsetIndex},\n    segment::{self, FileLike, Transaction, Writer},\n    Commit, Encode, Options, DEFAULT_LOG_FORMAT_VERSION,\n};\n\npub use crate::segment::Committed;\n\n/// A commitlog generic over the storage backend as well as the type of records\n/// its [`Commit`]s contain.\n#[derive(Debug)]\npub struct Generic<R: Repo, T> {\n    /// The storage backend.\n    pub(crate) repo: R,\n    /// The segment currently being written to.\n    ///\n    /// If we squint, all segments in a log are a non-empty linked list, the\n    /// head of which is the segment open for writing.\n    pub(crate) head: Writer<R::SegmentWriter>,\n    /// The tail of the non-empty list of segments.\n    ///\n    /// We only retain the min transaction offset of each, from which the\n    /// segments can be opened for reading when needed.\n    ///\n    /// This is a `Vec`, not a linked list, so the last element is the newest\n    /// segment (after `head`).\n    tail: Vec<u64>,\n    /// Configuration options.\n    opts: Options,\n    /// Type of a single record in this log's [`Commit::records`].\n    _record: PhantomData<T>,\n    /// Tracks panics/errors to control what happens on drop.\n    ///\n    /// Set to `true` before any I/O operation, and back to `false` after it\n    /// succeeded. This way, we won't try to perform I/O on drop when it is\n    /// unlikely to succeed, or even has a chance to panic.\n    panicked: bool,\n}\n\nimpl<R: Repo, T> Generic<R, T> {\n    pub fn open(repo: R, opts: Options) -> io::Result<Self> {\n        let mut tail = repo.existing_offsets()?;\n        if !tail.is_empty() {\n            debug!(\"segments: {tail:?}\");\n        }\n        let head = if let Some(last) = tail.pop() {\n            debug!(\"resuming last segment: {last}\");\n            // Resume the last segment for writing, or create a new segment\n            // starting from the last good commit + 1.\n            repo::resume_segment_writer(&repo, opts, last)?.or_else(|meta| {\n                // The first commit in the last segment being corrupt is an\n                // edge case: we'd try to start a new segment with an offset\n                // equal to the already existing one, which would fail.\n                //\n                // We cannot just skip it either, as we don't know the reason\n                // for the corruption (there could be more, potentially\n                // recoverable commits in the segment).\n                //\n                // Thus, provide some context about what is wrong and refuse to\n                // start.\n                if meta.tx_range.is_empty() {\n                    return Err(io::Error::new(\n                        io::ErrorKind::InvalidData,\n                        format!(\"repo {}: first commit in resumed segment {} is corrupt\", repo, last),\n                    ));\n                }\n                tail.push(meta.tx_range.start);\n                repo::create_segment_writer(&repo, opts, meta.max_epoch, meta.tx_range.end)\n            })?\n        } else {\n            debug!(\"starting fresh log\");\n            repo::create_segment_writer(&repo, opts, Commit::DEFAULT_EPOCH, 0)?\n        };\n\n        Ok(Self {\n            repo,\n            head,\n            tail,\n            opts,\n            _record: PhantomData,\n            panicked: false,\n        })\n    }\n\n    /// Get the current epoch.\n    ///\n    /// See also: [`Commit::epoch`].\n    pub fn epoch(&self) -> u64 {\n        self.head.commit.epoch\n    }\n\n    /// Update the current epoch.\n    ///\n    /// Does nothing if the given `epoch` is equal to the current epoch.\n    ///\n    /// # Errors\n    ///\n    /// If `epoch` is smaller than the current epoch, an error of kind\n    /// [`io::ErrorKind::InvalidInput`] is returned.\n    pub fn set_epoch(&mut self, epoch: u64) -> io::Result<()> {\n        if epoch < self.head.epoch() {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"new epoch is smaller than current epoch\",\n            ));\n        }\n        self.head.set_epoch(epoch);\n        Ok(())\n    }\n\n    /// Force the currently active segment to be flushed to storage.\n    ///\n    /// Using a filesystem backend, this means to call `fsync(2)`.\n    ///\n    /// **Note** that this does not flush the buffered data from calls to\n    /// [Self::commit], it only instructs the underlying storage to flush its\n    /// buffers. Call [Self::flush] prior to this method to ensure data from\n    /// all previous [Self::commit] calls is flushed to the underlying storage.\n    ///\n    /// # Panics\n    ///\n    /// As an `fsync` failure leaves a file in a more of less undefined state,\n    /// this method panics in this case, thereby preventing any further writes\n    /// to the log and forcing the user to re-read the state from disk.\n    pub fn sync(&mut self) {\n        self.panicked = true;\n        if let Err(e) = self.head.fsync() {\n            panic!(\"Failed to fsync segment: {e}\");\n        }\n        self.panicked = false;\n    }\n\n    /// Flush the buffered data from previous calls to [Self::commit] to the\n    /// underlying storage.\n    ///\n    /// Call [Self::sync] to instruct the underlying storage to flush its\n    /// buffers as well.\n    pub fn flush(&mut self) -> io::Result<()> {\n        self.head.flush()\n    }\n\n    /// Calls [Self::flush] and then [Self::sync].\n    fn flush_and_sync(&mut self) -> io::Result<()> {\n        self.flush()?;\n        self.sync();\n        Ok(())\n    }\n\n    /// The last transaction offset written to disk, or `None` if nothing has\n    /// been written yet.\n    ///\n    /// Note that this does not imply durability: [`Self::sync`] may not have\n    /// been called at this offset.\n    pub fn max_committed_offset(&self) -> Option<u64> {\n        // Naming is hard: the segment's `next_tx_offset` indicates how many\n        // txs are already in the log (it's the next commit's min-tx-offset).\n        // If the value is zero, however, the initial commit hasn't been\n        // committed yet.\n        self.head.next_tx_offset().checked_sub(1)\n    }\n\n    /// The first transaction offset written to disk, or `None` if nothing has\n    /// been written yet.\n    pub fn min_committed_offset(&self) -> Option<u64> {\n        self.tail\n            .first()\n            .copied()\n            .or_else(|| (!self.head.is_empty()).then(|| self.head.min_tx_offset()))\n    }\n\n    // Helper to obtain a list of the segment offsets which include transaction\n    // offset `offset`.\n    //\n    // The returned `Vec` is sorted in **ascending** order, such that the first\n    // element is the segment which contains `offset`.\n    //\n    // The offset of `self.head` is always included, regardless of how many\n    // entries it actually contains.\n    fn segment_offsets_from(&self, offset: u64) -> Vec<u64> {\n        if offset >= self.head.min_tx_offset {\n            vec![self.head.min_tx_offset]\n        } else {\n            let mut offs = Vec::with_capacity(self.tail.len() + 1);\n            if let Some(pos) = self.tail.iter().rposition(|off| off <= &offset) {\n                offs.extend_from_slice(&self.tail[pos..]);\n                offs.push(self.head.min_tx_offset);\n            }\n\n            offs\n        }\n    }\n\n    pub fn commits_from(&self, offset: u64) -> Commits<R> {\n        let offsets = self.segment_offsets_from(offset);\n        let segments = Segments {\n            offs: offsets.into_iter(),\n            repo: self.repo.clone(),\n            max_log_format_version: self.opts.log_format_version,\n        };\n        Commits {\n            inner: None,\n            segments,\n            last_commit: CommitInfo::Initial { next_offset: offset },\n            last_error: None,\n        }\n    }\n\n    pub fn reset(mut self) -> io::Result<Self> {\n        info!(\"hard reset\");\n\n        self.panicked = true;\n        self.tail.reserve(1);\n        self.tail.push(self.head.min_tx_offset);\n        for segment in self.tail.iter().rev() {\n            debug!(\"removing segment {segment}\");\n            self.repo.remove_segment(*segment)?;\n        }\n        // Prevent finalizer from running by not updating self.panicked.\n\n        Self::open(self.repo.clone(), self.opts)\n    }\n\n    pub fn reset_to(mut self, offset: u64) -> io::Result<Self> {\n        info!(\"reset to {offset}\");\n\n        self.panicked = true;\n        self.tail.reserve(1);\n        self.tail.push(self.head.min_tx_offset);\n        reset_to_internal(&self.repo, &self.tail, offset)?;\n        // Prevent finalizer from running by not updating self.panicked.\n\n        Self::open(self.repo.clone(), self.opts)\n    }\n\n    /// Start a new segment, preserving the current head's `Commit`.\n    ///\n    /// The caller must ensure that the current head is synced to disk as\n    /// appropriate. It is not appropriate to sync after a write error, as that\n    /// is likely to return an error as well: the `Commit` will be written to\n    /// the new segment anyway.\n    fn start_new_segment(&mut self) -> io::Result<&mut Writer<R::SegmentWriter>> {\n        debug!(\n            \"starting new segment offset={} prev-offset={}\",\n            self.head.next_tx_offset(),\n            self.head.min_tx_offset()\n        );\n        let new = repo::create_segment_writer(&self.repo, self.opts, self.head.epoch(), self.head.next_tx_offset())?;\n        let old = mem::replace(&mut self.head, new);\n        self.tail.push(old.min_tx_offset());\n        self.head.commit = old.commit;\n\n        Ok(&mut self.head)\n    }\n}\n\nimpl<R: Repo, T: Encode> Generic<R, T> {\n    /// Write `transactions` to the log.\n    ///\n    /// This will store all `transactions` as a single [Commit]\n    /// (note that `transactions` must not yield more than [u16::MAX] elements).\n    ///\n    /// Data is buffered by the underlying segment [Writer].\n    /// Call [Self::flush] to force flushing to the underlying storage.\n    ///\n    /// If, after writing the transactions, the writer's total written bytes\n    /// exceed [Options::max_segment_size], the current segment is flushed,\n    /// `fsync`ed and closed, and a new segment is created.\n    ///\n    /// Returns `Ok(None)` if `transactions` was empty, otherwise [Committed],\n    /// which contains the offset range and checksum of the commit.\n    ///\n    /// Note that supplying empty `transactions` may cause the current segment\n    /// to be rotated.\n    ///\n    /// # Errors\n    ///\n    /// An `Err` value is returned in the following cases:\n    ///\n    /// - if the transaction sequence is invalid, e.g. because the transaction\n    ///   offsets are not contiguous.\n    ///\n    ///   In this case, **none** of the `transactions` will be written.\n    ///\n    /// - if creating the new segment fails due to an I/O error.\n    ///\n    /// # Panics\n    ///\n    /// The method panics if:\n    ///\n    /// - `transactions` exceeds [u16::MAX] elements\n    ///\n    /// - [Self::flush] or writing to the underlying [Writer] fails\n    ///\n    ///   This is likely caused by some storage issue. As we cannot tell with\n    ///   certainty how much data (if any) has been written, the internal state\n    ///   becomes invalid and thus a panic is raised.\n    ///\n    /// - [Self::sync] panics (called when rotating segments)\n    pub fn commit<U: Into<Transaction<T>>>(\n        &mut self,\n        transactions: impl IntoIterator<Item = U>,\n    ) -> io::Result<Option<Committed>> {\n        self.panicked = true;\n        let writer = &mut self.head;\n        let committed = writer.commit(transactions)?;\n        if writer.len() >= self.opts.max_segment_size {\n            self.flush().expect(\"failed to flush segment upon rotation\");\n            self.sync();\n            self.start_new_segment()?;\n        }\n        self.panicked = false;\n\n        Ok(committed)\n    }\n\n    pub fn transactions_from<'a, D>(\n        &self,\n        offset: u64,\n        decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a + use<'a, D, R, T>\n    where\n        D: Decoder<Record = T>,\n        D::Error: From<error::Traversal>,\n        R: 'a,\n        T: 'a,\n    {\n        transactions_from_internal(self.commits_from(offset).with_log_format_version(), offset, decoder)\n    }\n\n    pub fn fold_transactions_from<D>(&self, offset: u64, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        fold_transactions_internal(self.commits_from(offset).with_log_format_version(), decoder, offset..)\n    }\n\n    pub fn fold_transaction_range<D>(&self, range: impl RangeBounds<u64>, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        use std::ops::Bound::*;\n\n        let start = match range.start_bound() {\n            Included(x) => *x,\n            Excluded(x) => x + 1,\n            Unbounded => 0,\n        };\n        fold_transactions_internal(self.commits_from(start).with_log_format_version(), decoder, range)\n    }\n}\n\nimpl<R: Repo, T> Drop for Generic<R, T> {\n    fn drop(&mut self) {\n        if !self.panicked\n            && let Err(e) = self.flush_and_sync()\n        {\n            error!(\"failed to commit on drop: {e}\");\n        }\n    }\n}\n\n/// The most recent non empty segment in repo `R`.\n///\n/// Created by [open_newest_non_empty_segment].\nstruct MostRecentNonEmptySegment<R> {\n    /// Number of empty segments that were ignored.\n    empty_segments: usize,\n    /// Offset of the non-empty segment.\n    segment_offset: u64,\n    /// [Repo::SegmentReader] for the non-empty segment.\n    segment_reader: R,\n}\n\n/// Open the most recent segment in `repo` that is larger than\n/// [segment::Header::LEN].\n///\n/// Note that there should be at most one empty segment in the log. We may,\n/// however, want to be lenient on this read-only path, so the number of\n/// empty segments is tracked in the returned type rather than returning an\n/// error.\nfn open_newest_non_empty_segment<R: Repo>(repo: R) -> io::Result<Option<MostRecentNonEmptySegment<R::SegmentReader>>> {\n    let mut segments = repo.existing_offsets()?;\n\n    let mut empty_segments = 0;\n    let mut segment_offset;\n    let mut segment_reader;\n    loop {\n        let Some(last) = segments.pop() else {\n            return Ok(None);\n        };\n        segment_offset = last;\n        segment_reader = repo.open_segment_reader(segment_offset)?;\n        if segment_reader.segment_len()? > segment::Header::LEN as u64 {\n            break;\n        } else {\n            empty_segments += 1;\n        }\n    }\n\n    Ok(Some(MostRecentNonEmptySegment {\n        empty_segments,\n        segment_offset,\n        segment_reader,\n    }))\n}\n\n/// The most recently written [segment::Metadata] for a given [Repo].\n///\n/// The type preserves the error information in case the most recent segment\n/// contains corrupted data at the end (typically due to a torn write).\n///\n/// Created by [committed_meta].\npub enum CommittedMeta {\n    /// The most recent segment could not be traversed successfully until the\n    /// end, i.e. there is trailing garbage in the segment.\n    ///\n    /// This variant is also returned in case [open_newest_non_empty_segment]\n    /// finds more than a single empty segment at the end of the log.\n    Prefix {\n        /// The metadata of the prefix that could be traversed successfully.\n        ///\n        /// It is guaranteed that the metadata spans at least one commit.\n        metadata: segment::Metadata,\n        /// The error encountered.\n        error: io::Error,\n    },\n    /// The most recent segment could be traversed successfully until the end.\n    Complete {\n        /// The segment metadata.\n        ///\n        /// It is guaranteed that the metadata spans at least one commit.\n        metadata: segment::Metadata,\n    },\n}\n\nimpl CommittedMeta {\n    pub fn metadata(&self) -> &segment::Metadata {\n        let (Self::Prefix { metadata, .. } | Self::Complete { metadata }) = self;\n        metadata\n    }\n\n    fn extract(repo: impl Repo) -> io::Result<Option<Self>> {\n        let Some(MostRecentNonEmptySegment {\n            empty_segments,\n            segment_offset,\n            mut segment_reader,\n        }) = open_newest_non_empty_segment(&repo)?\n        else {\n            return Ok(None);\n        };\n        let offset_index = repo.get_offset_index(segment_offset).ok();\n        match segment::Metadata::extract(segment_offset, &mut segment_reader, offset_index.as_ref()) {\n            // Segment is intact.\n            Ok(metadata) if empty_segments <= 1 => {\n                assert!(\n                    !metadata.tx_range.is_empty(),\n                    \"segment was promised to be non-empty but contains zero transactions\"\n                );\n                Ok(Some(CommittedMeta::Complete { metadata }))\n            }\n            // Segment is good, but there are too many empty segments.\n            Ok(metadata) => Ok(Some(CommittedMeta::Prefix {\n                metadata,\n                error: io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"repo {}: too many empty segments: {}\", repo, empty_segments),\n                ),\n            })),\n            // Segment is non-empty, but first commit is corrupt.\n            Err(error::SegmentMetadata::InvalidCommit { sofar, source }) if sofar.tx_range.is_empty() => {\n                Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"repo {}: first commit in the most recent segment is corrupt: {}\",\n                        repo, source\n                    ),\n                ))\n            }\n            // Some prefix of the segment is good.\n            Err(error::SegmentMetadata::InvalidCommit { sofar, source }) => Ok(Some(CommittedMeta::Prefix {\n                metadata: sofar,\n                error: source,\n            })),\n            // Something went wrong, including out-of-order errors and such.\n            Err(error::SegmentMetadata::Io(e)) => Err(e),\n        }\n    }\n}\n\nimpl From<CommittedMeta> for segment::Metadata {\n    fn from(meta: CommittedMeta) -> Self {\n        let (CommittedMeta::Prefix { metadata, .. } | CommittedMeta::Complete { metadata }) = meta;\n        metadata\n    }\n}\n\n/// Extract the most recently written [CommittedMeta] from the commitlog\n/// in `repo`.\n///\n/// Returns `None` if the commitlog is empty.\n///\n/// Note that this function validates the most recent segment, which entails\n/// traversing it from the start.\n///\n/// The function can be used instead of the pattern:\n///\n/// ```ignore\n/// let log = Commitlog::open(..)?;\n/// let max_offset = log.max_committed_offset();\n/// ```\n///\n/// like so:\n///\n/// ```ignore\n/// let max_offset = committed_meta(..)?.map(|meta| meta.metadata().tx_range.end);\n/// ```\n///\n/// Unlike `open`, no segment will be created in an empty `repo`.\npub fn committed_meta(repo: impl Repo) -> io::Result<Option<CommittedMeta>> {\n    CommittedMeta::extract(repo)\n}\n\npub fn commits_from<R: Repo>(repo: R, max_log_format_version: u8, offset: u64) -> io::Result<Commits<R>> {\n    let mut offsets = repo.existing_offsets()?;\n    if let Some(pos) = offsets.iter().rposition(|&off| off <= offset) {\n        offsets = offsets.split_off(pos);\n    }\n    let segments = Segments {\n        offs: offsets.into_iter(),\n        repo,\n        max_log_format_version,\n    };\n    Ok(Commits {\n        inner: None,\n        segments,\n        last_commit: CommitInfo::Initial { next_offset: offset },\n        last_error: None,\n    })\n}\n\npub fn transactions_from<'a, R, D, T>(\n    repo: R,\n    max_log_format_version: u8,\n    offset: u64,\n    de: &'a D,\n) -> io::Result<impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a>\nwhere\n    R: Repo + 'a,\n    D: Decoder<Record = T>,\n    D::Error: From<error::Traversal>,\n    T: 'a,\n{\n    commits_from(repo, max_log_format_version, offset)\n        .map(|commits| transactions_from_internal(commits.with_log_format_version(), offset, de))\n}\n\npub fn fold_transactions_from<R, D>(repo: R, max_log_format_version: u8, offset: u64, de: D) -> Result<(), D::Error>\nwhere\n    R: Repo,\n    D: Decoder,\n    D::Error: From<error::Traversal> + From<io::Error>,\n{\n    fold_transaction_range(repo, max_log_format_version, offset.., de)\n}\n\npub fn fold_transaction_range<R, D>(\n    repo: R,\n    max_log_format_version: u8,\n    range: impl RangeBounds<u64>,\n    de: D,\n) -> Result<(), D::Error>\nwhere\n    R: Repo,\n    D: Decoder,\n    D::Error: From<error::Traversal> + From<io::Error>,\n{\n    use std::ops::Bound::*;\n\n    let start = match range.start_bound() {\n        Included(x) => *x,\n        Excluded(x) => x + 1,\n        Unbounded => 0,\n    };\n    let commits = commits_from(repo, max_log_format_version, start)?;\n    fold_transactions_internal(commits.with_log_format_version(), de, range)\n}\n\nfn transactions_from_internal<'a, R, D, T>(\n    commits: CommitsWithVersion<R>,\n    offset: u64,\n    de: &'a D,\n) -> impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a\nwhere\n    R: Repo + 'a,\n    D: Decoder<Record = T>,\n    D::Error: From<error::Traversal>,\n    T: 'a,\n{\n    commits\n        .map(|x| x.map_err(D::Error::from))\n        .map_ok(move |(version, commit)| commit.into_transactions(version, offset, de))\n        .flatten_ok()\n        .map(|x| x.and_then(|y| y))\n}\n\nfn fold_transactions_internal<R, D>(\n    mut commits: CommitsWithVersion<R>,\n    de: D,\n    range: impl RangeBounds<u64>,\n) -> Result<(), D::Error>\nwhere\n    R: Repo,\n    D: Decoder,\n    D::Error: From<error::Traversal>,\n{\n    use std::ops::Bound::*;\n\n    // Avoid reading the first commit if it wouldn't be in the range anyway.\n    if range_is_empty(&range) {\n        return Ok(());\n    }\n\n    // `true` if `offset` is outside `range`, s.t. it is smaller than the start\n    // bound.\n    let before_start = |offset: &u64| match range.start_bound() {\n        Included(x) => offset < x,\n        Excluded(x) => offset <= x,\n        Unbounded => false,\n    };\n    // `true` if `offset` is outside `range`, s.t. it is greater than the end\n    // bound.\n    let past_end = |offset: &u64| match range.end_bound() {\n        Included(x) => offset > x,\n        Excluded(x) => offset >= x,\n        Unbounded => false,\n    };\n\n    while let Some(commit) = commits.next() {\n        let (version, commit) = match commit {\n            Ok(version_and_commit) => version_and_commit,\n            Err(e) => {\n                // Ignore it if the very last commit in the log is broken.\n                // The next `append` will fix the log, but the `decoder`\n                // has no way to tell whether we're at the end or not.\n                // This is unlike the consumer of an iterator, which can\n                // perform below check itself.\n                if commits.next().is_none() {\n                    return Ok(());\n                }\n\n                return Err(e.into());\n            }\n        };\n        trace!(\"commit {} n={} version={}\", commit.min_tx_offset, commit.n, version);\n\n        let max_tx_offset = commit.min_tx_offset + commit.n as u64;\n        // Skip if no transaction in the commit is in range.\n        if before_start(&max_tx_offset) {\n            continue;\n        }\n\n        let records = &mut commit.records.as_slice();\n        for n in 0..commit.n {\n            let tx_offset = commit.min_tx_offset + n as u64;\n            if before_start(&tx_offset) {\n                de.skip_record(version, tx_offset, records)?;\n            } else if past_end(&tx_offset) {\n                return Ok(());\n            } else {\n                de.consume_record(version, tx_offset, records)?;\n            }\n        }\n    }\n\n    Ok(())\n}\n\n/// Remove all data past the given transaction `offset`.\n///\n/// The function deletes log segments starting from the newest. As multiple\n/// segments cannot be deleted atomically, the log may be left longer than\n/// `offset` if the function does not return successfully.\n///\n/// If the function returns successfully, the most recent [`Commit`] in the\n/// log will contain the transaction at `offset`.\n///\n/// The log must be re-opened if it is to be used after calling this function.\npub fn reset_to(repo: &impl Repo, offset: u64) -> io::Result<()> {\n    let segments = repo.existing_offsets()?;\n    reset_to_internal(repo, &segments, offset)\n}\n\nfn reset_to_internal(repo: &impl Repo, segments: &[u64], offset: u64) -> io::Result<()> {\n    for segment in segments.iter().copied().rev() {\n        if segment > offset {\n            // Segment is outside the offset, so remove it wholesale.\n            debug!(\"removing segment {segment}\");\n            repo.remove_segment(segment)?;\n        } else {\n            // Read commit-wise until we find the byte offset.\n            let mut reader = repo::open_segment_reader(repo, DEFAULT_LOG_FORMAT_VERSION, segment)?;\n\n            let (index_file, mut byte_offset) = try_seek_using_offset_index(repo, &mut reader, offset)\n                .map(|(index_file, byte_offset)| (Some(index_file), byte_offset))\n                .unwrap_or((None, segment::Header::LEN as u64));\n\n            let commits = reader.commits();\n\n            for commit in commits {\n                let commit = commit?;\n                if commit.min_tx_offset > offset {\n                    break;\n                }\n                byte_offset += Commit::from(commit).encoded_len() as u64;\n            }\n\n            if byte_offset == segment::Header::LEN as u64 {\n                // Segment is empty, just remove it.\n                repo.remove_segment(segment)?;\n            } else {\n                debug!(\"truncating segment {segment} to {offset} at {byte_offset}\");\n                let mut file = repo.open_segment_writer(segment)?;\n\n                if let Some(mut index_file) = index_file {\n                    let index_file = index_file.as_mut();\n                    // Note: The offset index truncates equal or greater,\n                    // inclusive. We'd like to retain `offset` in the index, as\n                    // the commit is also retained in the log.\n                    index_file.ftruncate(offset + 1, byte_offset).map_err(|e| {\n                        io::Error::new(\n                            io::ErrorKind::InvalidData,\n                            format!(\"Failed to truncate offset index: {e}\"),\n                        )\n                    })?;\n                    index_file.async_flush()?;\n                }\n\n                file.ftruncate(offset, byte_offset)?;\n                // Some filesystems require fsync after ftruncate.\n                file.fsync()?;\n                break;\n            }\n        }\n    }\n\n    Ok(())\n}\n\npub struct Segments<R> {\n    repo: R,\n    offs: vec::IntoIter<u64>,\n    max_log_format_version: u8,\n}\n\nimpl<R: Repo> Iterator for Segments<R> {\n    type Item = io::Result<segment::Reader<R::SegmentReader>>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let off = self.offs.next()?;\n        debug!(\"iter segment {off}\");\n        Some(repo::open_segment_reader(&self.repo, self.max_log_format_version, off))\n    }\n}\n\n/// Helper for the [`Commits`] iterator.\nenum CommitInfo {\n    /// Constructed in [`Generic::commits_from`], specifying the offset the next\n    /// commit should have.\n    Initial { next_offset: u64 },\n    /// The last commit seen by the iterator.\n    ///\n    /// Stores the range of transaction offsets, where `tx_range.end` is the\n    /// offset the next commit is expected to have. Also retains the checksum\n    /// needed to detect duplicate commits.\n    LastSeen { tx_range: Range<u64>, checksum: u32 },\n}\n\nimpl CommitInfo {\n    /// `true` if the last seen commit in self and the provided one have the\n    /// same `min_tx_offset`.\n    fn same_offset_as(&self, commit: &StoredCommit) -> bool {\n        let Self::LastSeen { tx_range, .. } = self else {\n            return false;\n        };\n        tx_range.start == commit.min_tx_offset\n    }\n\n    /// `true` if the last seen commit in self and the provided one have the\n    /// same `checksum`.\n    fn same_checksum_as(&self, commit: &StoredCommit) -> bool {\n        let Some(checksum) = self.checksum() else { return false };\n        checksum == &commit.checksum\n    }\n\n    fn checksum(&self) -> Option<&u32> {\n        match self {\n            Self::Initial { .. } => None,\n            Self::LastSeen { checksum, .. } => Some(checksum),\n        }\n    }\n\n    fn expected_offset(&self) -> &u64 {\n        match self {\n            Self::Initial { next_offset } => next_offset,\n            Self::LastSeen { tx_range, .. } => &tx_range.end,\n        }\n    }\n\n    // If initial offset falls within a commit, adjust it to the commit boundary.\n    //\n    // Returns `true` if the initial offset is past `commit`.\n    // Returns `false` if `self` isn't `Self::Initial`,\n    // or the initial offset has been adjusted to the starting offset of `commit`.\n    //\n    // For iteration, `true` means to skip the commit, `false` to yield it.\n    fn adjust_initial_offset(&mut self, commit: &StoredCommit) -> bool {\n        if let Self::Initial { next_offset } = self {\n            let last_tx_offset = commit.min_tx_offset + commit.n as u64 - 1;\n            if *next_offset > last_tx_offset {\n                return true;\n            } else {\n                *next_offset = commit.min_tx_offset;\n            }\n        }\n\n        false\n    }\n}\n\npub struct Commits<R: Repo> {\n    inner: Option<segment::Commits<R::SegmentReader>>,\n    segments: Segments<R>,\n    last_commit: CommitInfo,\n    last_error: Option<error::Traversal>,\n}\n\nimpl<R: Repo> Commits<R> {\n    fn current_segment_header(&self) -> Option<&segment::Header> {\n        self.inner.as_ref().map(|segment| &segment.header)\n    }\n\n    /// Turn `self` into an iterator which pairs the log format version of the\n    /// current segment with the [`Commit`].\n    pub fn with_log_format_version(self) -> CommitsWithVersion<R> {\n        CommitsWithVersion { inner: self }\n    }\n\n    /// Advance the current-segment iterator to yield the next commit.\n    ///\n    /// Checks that the offset sequence is contiguous, and may skip commits\n    /// until the requested offset.\n    ///\n    /// Returns `None` if the segment iterator is exhausted or returns an error.\n    fn next_commit(&mut self) -> Option<Result<StoredCommit, error::Traversal>> {\n        loop {\n            match self.inner.as_mut()?.next()? {\n                Ok(commit) => {\n                    // Pop the last error. Either we'll return it below, or it's no longer\n                    // interesting.\n                    let prev_error = self.last_error.take();\n\n                    // Skip entries before the initial commit.\n                    if self.last_commit.adjust_initial_offset(&commit) {\n                        trace!(\"adjust initial offset\");\n                        continue;\n                    // Same offset: ignore if duplicate (same crc), else report a \"fork\".\n                    } else if self.last_commit.same_offset_as(&commit) {\n                        if !self.last_commit.same_checksum_as(&commit) {\n                            warn!(\n                                \"forked: commit={:?} last-error={:?} last-crc={:?}\",\n                                commit,\n                                prev_error,\n                                self.last_commit.checksum()\n                            );\n                            return Some(Err(error::Traversal::Forked {\n                                offset: commit.min_tx_offset,\n                            }));\n                        } else {\n                            trace!(\"ignore duplicate\");\n                            continue;\n                        }\n                    // Not the expected offset: report out-of-order.\n                    } else if self.last_commit.expected_offset() != &commit.min_tx_offset {\n                        warn!(\"out-of-order: commit={commit:?} last-error={prev_error:?}\");\n                        return Some(Err(error::Traversal::OutOfOrder {\n                            expected_offset: *self.last_commit.expected_offset(),\n                            actual_offset: commit.min_tx_offset,\n                            prev_error: prev_error.map(Box::new),\n                        }));\n                    // Seems legit, record info.\n                    } else {\n                        self.last_commit = CommitInfo::LastSeen {\n                            tx_range: commit.tx_range(),\n                            checksum: commit.checksum,\n                        };\n\n                        return Some(Ok(commit));\n                    }\n                }\n\n                Err(e) => {\n                    warn!(\"error reading next commit: {e}\");\n                    // Stop traversing this segment here.\n                    //\n                    // If this is just a partial write at the end of the segment,\n                    // we may be able to obtain a commit with right offset from\n                    // the next segment.\n                    //\n                    // If we don't, the error here is likely more helpful, but\n                    // would be clobbered by `OutOfOrder`. Therefore we store it\n                    // here.\n                    self.set_last_error(e);\n\n                    return None;\n                }\n            }\n        }\n    }\n\n    /// Store `e` has the last error for delayed reporting.\n    fn set_last_error(&mut self, e: io::Error) {\n        // Recover a checksum mismatch.\n        let last_error = if e.kind() == io::ErrorKind::InvalidData && e.get_ref().is_some() {\n            e.into_inner()\n                .unwrap()\n                .downcast::<error::ChecksumMismatch>()\n                .map(|source| error::Traversal::Checksum {\n                    offset: *self.last_commit.expected_offset(),\n                    source: *source,\n                })\n                .unwrap_or_else(|e| io::Error::new(io::ErrorKind::InvalidData, e).into())\n        } else {\n            error::Traversal::from(e)\n        };\n        self.last_error = Some(last_error);\n    }\n\n    /// If we're still looking for the initial commit, try to use the offset\n    /// index to advance the segment reader.\n    fn try_seek_to_initial_offset(&self, segment: &mut segment::Reader<R::SegmentReader>) {\n        if let CommitInfo::Initial { next_offset } = &self.last_commit {\n            try_seek_using_offset_index(&self.segments.repo, segment, *next_offset);\n        }\n    }\n}\n\nimpl<R: Repo> Iterator for Commits<R> {\n    type Item = Result<StoredCommit, error::Traversal>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if let Some(item) = self.next_commit() {\n            return Some(item);\n        }\n\n        match self.segments.next() {\n            // When there is no more data, the last commit being bad is an error\n            None => self.last_error.take().map(Err),\n            Some(segment) => segment.map_or_else(\n                |e| Some(Err(e.into())),\n                |mut segment| {\n                    self.try_seek_to_initial_offset(&mut segment);\n                    self.inner = Some(segment.commits());\n                    self.next()\n                },\n            ),\n        }\n    }\n}\n\npub struct CommitsWithVersion<R: Repo> {\n    inner: Commits<R>,\n}\n\nimpl<R: Repo> Iterator for CommitsWithVersion<R> {\n    type Item = Result<(u8, Commit), error::Traversal>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let next = self.inner.next()?;\n        match next {\n            Ok(commit) => {\n                let version = self\n                    .inner\n                    .current_segment_header()\n                    .map(|hdr| hdr.log_format_version)\n                    .expect(\"segment header none even though segment yielded a commit\");\n                Some(Ok((version, commit.into())))\n            }\n            Err(e) => Some(Err(e)),\n        }\n    }\n}\n\n/// Try to advance `reader` to `offset` using the offset index.\n///\n/// If successful, returns the offset index and the byte position of `reader`.\n/// `None` if the position of `reader` is unchanged.\nfn try_seek_using_offset_index<R: Repo>(\n    repo: &R,\n    reader: &mut segment::Reader<R::SegmentReader>,\n    offset: u64,\n) -> Option<(TxOffsetIndex, u64)> {\n    let segment_offset = reader.min_tx_offset;\n    let index = repo\n        .get_offset_index(segment_offset)\n        .inspect_err(|e| {\n            if e.kind() == io::ErrorKind::NotFound {\n                debug!(\"offset index does not exist segment={segment_offset}\");\n            } else {\n                warn!(\n                    \"error opening offset index segment={segment_offset}: {e} {}\",\n                    source_chain(&e)\n                );\n            }\n        })\n        .ok()?;\n\n    reader\n        .seek_to_offset(&index, offset)\n        .inspect_err(|e| match e {\n            // Can happen if the segment is empty or small, so don't spam the logs.\n            IndexError::KeyNotFound => {\n                debug!(\"offset not found segment={segment_offset} offset={offset}\");\n            }\n            e => {\n                warn!(\n                    \"error reading index segment={segment_offset} offset={offset}: {e} {}\",\n                    source_chain(&e)\n                );\n            }\n        })\n        .ok()\n        .map(|pos| (index, pos))\n}\n\n// `range_bounds_is_empty` https://github.com/rust-lang/rust/issues/137300\n//\n// This is correct for integers, but unsound for arbitrary `T`, so unlikely to\n// be stabilized.\nfn range_is_empty(range: &impl RangeBounds<u64>) -> bool {\n    use std::ops::Bound::*;\n\n    #[rustfmt::skip]\n    let not_empty = match (range.start_bound(), range.end_bound()) {\n        (Unbounded, _) | (_, Unbounded) => true,\n        (Included(start), Excluded(end))\n        | (Excluded(start), Included(end))\n        | (Excluded(start), Excluded(end)) => start < end,\n        (Included(start), Included(end)) => start <= end,\n    };\n\n    !not_empty\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{cell::Cell, iter::repeat};\n\n    use pretty_assertions::assert_matches;\n\n    use super::*;\n    use crate::{\n        payload::{ArrayDecodeError, ArrayDecoder},\n        tests::helpers::{enable_logging, fill_log, mem_log},\n    };\n\n    #[test]\n    fn rotate_segments_simple() {\n        let mut log = mem_log::<[u8; 32]>(128);\n        for i in 0..4 {\n            log.commit([(i, [0; 32])]).unwrap();\n        }\n        log.flush_and_sync().unwrap();\n\n        let offsets = log.repo.existing_offsets().unwrap();\n        assert_eq!(&offsets[..offsets.len() - 1], &log.tail);\n        // TODO: We overshoot the max segment size.\n        assert_eq!(&offsets, &[0, 3]);\n    }\n\n    #[test]\n    fn huge_commit() {\n        let mut log = mem_log::<[u8; 32]>(32);\n\n        log.commit([(0, [0; 32]), (1, [1; 32])]).unwrap();\n        log.flush_and_sync().unwrap();\n        // First segment got rotated out.\n        assert_eq!(&log.tail, &[0]);\n\n        log.commit([(2, [2; 32])]).unwrap();\n        log.flush_and_sync().unwrap();\n\n        // Second segment got rotated out and segment 3 is created.\n        assert_eq!(&log.repo.existing_offsets().unwrap(), &[0, 2, 3]);\n    }\n\n    #[test]\n    fn traverse_commits() {\n        let mut log = mem_log::<[u8; 32]>(32);\n        fill_log(&mut log, 10, repeat(1));\n\n        for (i, commit) in (0..10).zip(log.commits_from(0)) {\n            assert_eq!(i, commit.unwrap().min_tx_offset);\n        }\n    }\n\n    #[test]\n    fn traverse_commits_with_offset() {\n        let mut log = mem_log::<[u8; 32]>(32);\n        fill_log(&mut log, 10, repeat(1));\n\n        for offset in 0..10 {\n            for commit in log.commits_from(offset) {\n                let commit = commit.unwrap();\n                assert!(commit.min_tx_offset >= offset);\n            }\n        }\n        assert_eq!(0, log.commits_from(10).count());\n    }\n\n    #[test]\n    fn fold_transactions_with_offset() {\n        let mut log = mem_log::<[u8; 32]>(32);\n        fill_log(&mut log, 10, repeat(1));\n\n        /// A [`Decoder`] which counts the number of records decoded,\n        /// and asserts that the `tx_offset` is as expected.\n        struct CountDecoder {\n            count: Cell<u64>,\n            next_tx_offset: Cell<u64>,\n        }\n\n        impl Decoder for &CountDecoder {\n            type Record = [u8; 32];\n            type Error = ArrayDecodeError;\n\n            fn decode_record<'a, R: spacetimedb_sats::buffer::BufReader<'a>>(\n                &self,\n                _version: u8,\n                _tx_offset: u64,\n                _reader: &mut R,\n            ) -> Result<Self::Record, Self::Error> {\n                unreachable!(\"Folding never calls `decode_record`\")\n            }\n\n            fn consume_record<'a, R: spacetimedb_sats::buffer::BufReader<'a>>(\n                &self,\n                version: u8,\n                tx_offset: u64,\n                reader: &mut R,\n            ) -> Result<(), Self::Error> {\n                let decoder = ArrayDecoder::<32>;\n                decoder.consume_record(version, tx_offset, reader)?;\n                self.count.set(self.count.get() + 1);\n                let expected_tx_offset = self.next_tx_offset.get();\n                assert_eq!(expected_tx_offset, tx_offset);\n                self.next_tx_offset.set(expected_tx_offset + 1);\n                Ok(())\n            }\n\n            fn skip_record<'a, R: spacetimedb_sats::buffer::BufReader<'a>>(\n                &self,\n                version: u8,\n                tx_offset: u64,\n                reader: &mut R,\n            ) -> Result<(), Self::Error> {\n                let decoder = ArrayDecoder::<32>;\n                decoder.consume_record(version, tx_offset, reader)?;\n                Ok(())\n            }\n        }\n\n        for offset in 0..10 {\n            let decoder = CountDecoder {\n                count: Cell::new(0),\n                next_tx_offset: Cell::new(offset),\n            };\n\n            log.fold_transactions_from(offset, &decoder).unwrap();\n\n            assert_eq!(decoder.count.get(), 10 - offset);\n            assert_eq!(decoder.next_tx_offset.get(), 10);\n        }\n    }\n\n    #[test]\n    fn traverse_commits_ignores_duplicates() {\n        let mut log = mem_log::<[u8; 32]>(1024);\n\n        let tx1 = [42u8; 32];\n        let tx2 = [43u8; 32];\n\n        log.commit([(0, tx1)]).unwrap();\n        let commit1 = Commit {\n            min_tx_offset: 0,\n            n: 1,\n            records: tx1.to_vec(),\n            ..log.head.commit.clone()\n        };\n\n        // Reset the commit offset, so we can write the same commit twice.\n        log.head.commit.min_tx_offset = 0;\n        log.commit([(0, tx1)]).unwrap();\n\n        // Write another one.\n        log.commit([(1, tx2)]).unwrap();\n        let commit2 = Commit {\n            min_tx_offset: 1,\n            n: 1,\n            records: tx2.to_vec(),\n            ..log.head.commit.clone()\n        };\n\n        log.flush_and_sync().unwrap();\n\n        assert_eq!(\n            [commit1, commit2].as_slice(),\n            &log.commits_from(0)\n                .map_ok(Commit::from)\n                .collect::<Result<Vec<_>, _>>()\n                .unwrap()\n        );\n    }\n\n    #[test]\n    fn traverse_commits_errors_when_forked() {\n        let mut log = mem_log::<[u8; 32]>(1024);\n\n        log.commit([(0, [42; 32])]).unwrap();\n        // Reset the commit offset,\n        // and write a different commit at the same offset.\n        // This is considered a fork.\n        log.head.commit.min_tx_offset = 0;\n        log.commit([(0, [43; 32])]).unwrap();\n\n        log.flush_and_sync().unwrap();\n\n        let res = log.commits_from(0).collect::<Result<Vec<_>, _>>();\n        assert!(\n            matches!(res, Err(error::Traversal::Forked { offset: 0 })),\n            \"expected fork error: {res:?}\"\n        )\n    }\n\n    #[test]\n    fn traverse_commits_errors_when_offset_not_contiguous() {\n        let mut log = mem_log::<[u8; 32]>(1024);\n\n        log.commit([(0, [42; 32])]).unwrap();\n        log.head.commit.min_tx_offset = 18;\n        log.commit([(18, [42; 32])]).unwrap();\n\n        log.flush_and_sync().unwrap();\n\n        let res = log.commits_from(0).collect::<Result<Vec<_>, _>>();\n        assert!(\n            matches!(\n                res,\n                Err(error::Traversal::OutOfOrder {\n                    expected_offset: 1,\n                    actual_offset: 18,\n                    prev_error: None\n                })\n            ),\n            \"expected out-of-order error: {res:?}\"\n        )\n    }\n\n    #[test]\n    fn traverse_transactions() {\n        let mut log = mem_log::<[u8; 32]>(32);\n        let total_txs = fill_log(&mut log, 10, (1..=3).cycle()) as u64;\n\n        for (i, tx) in (0..total_txs).zip(log.transactions_from(0, &ArrayDecoder)) {\n            assert_eq!(i, tx.unwrap().offset);\n        }\n    }\n\n    #[test]\n    fn traverse_transactions_with_offset() {\n        let mut log = mem_log::<[u8; 32]>(32);\n        let total_txs = fill_log(&mut log, 10, (1..=3).cycle()) as u64;\n\n        for offset in 0..total_txs {\n            let mut iter = log.transactions_from(offset, &ArrayDecoder);\n            assert_eq!(offset, iter.next().expect(\"at least one tx expected\").unwrap().offset);\n            for tx in iter {\n                assert!(tx.unwrap().offset >= offset);\n            }\n        }\n        assert_eq!(0, log.transactions_from(total_txs, &ArrayDecoder).count());\n    }\n\n    #[test]\n    fn traverse_empty() {\n        let log = mem_log::<[u8; 32]>(32);\n\n        assert_eq!(0, log.commits_from(0).count());\n        assert_eq!(0, log.commits_from(42).count());\n        assert_eq!(0, log.transactions_from(0, &ArrayDecoder).count());\n        assert_eq!(0, log.transactions_from(42, &ArrayDecoder).count());\n    }\n\n    #[test]\n    fn reset_hard() {\n        let mut log = mem_log::<[u8; 32]>(128);\n        fill_log(&mut log, 50, (1..=10).cycle());\n\n        log = log.reset().unwrap();\n        assert_eq!(0, log.transactions_from(0, &ArrayDecoder).count());\n    }\n\n    #[test]\n    fn reset_to_offset() {\n        enable_logging();\n\n        let mut log = mem_log::<[u8; 32]>(128);\n        let total_txs = fill_log(&mut log, 50, repeat(1)) as u64;\n\n        for offset in (0..total_txs).rev() {\n            log = log.reset_to(offset).unwrap();\n            assert_eq!(\n                offset,\n                log.transactions_from(0, &ArrayDecoder)\n                    .map(Result::unwrap)\n                    .last()\n                    .unwrap()\n                    .offset\n            );\n            // We're counting from zero, so offset + 1 is the # of txs.\n            assert_eq!(\n                offset + 1,\n                log.transactions_from(0, &ArrayDecoder).map(Result::unwrap).count() as u64\n            );\n        }\n    }\n\n    #[test]\n    fn reset_to_offset_many_txs_per_commit() {\n        let mut log = mem_log::<[u8; 32]>(128);\n        let total_txs = fill_log(&mut log, 50, (1..=10).cycle()) as u64;\n\n        // No op.\n        log = log.reset_to(total_txs).unwrap();\n        assert_eq!(total_txs, log.transactions_from(0, &ArrayDecoder).count() as u64);\n\n        let middle_commit = log.commits_from(0).nth(25).unwrap().unwrap();\n\n        // Both fall into the middle commit, which should be retained.\n        log = log.reset_to(middle_commit.min_tx_offset + 1).unwrap();\n        assert_eq!(\n            middle_commit.tx_range().end,\n            log.transactions_from(0, &ArrayDecoder).count() as u64\n        );\n        log = log.reset_to(middle_commit.min_tx_offset).unwrap();\n        assert_eq!(\n            middle_commit.tx_range().end,\n            log.transactions_from(0, &ArrayDecoder).count() as u64\n        );\n\n        // Offset falls into 2nd commit.\n        // 1st commit (1 tx) + 2nd commit (2 txs) = 3\n        log = log.reset_to(1).unwrap();\n        assert_eq!(3, log.transactions_from(0, &ArrayDecoder).count() as u64);\n\n        // Offset falls into 1st commit.\n        // 1st commit (1 tx) = 1\n        log = log.reset_to(0).unwrap();\n        assert_eq!(1, log.transactions_from(0, &ArrayDecoder).count() as u64);\n    }\n\n    #[test]\n    fn reopen() {\n        let mut log = mem_log::<[u8; 32]>(1024);\n        let total_txs = fill_log(&mut log, 100, (1..=10).cycle());\n        assert_eq!(\n            total_txs,\n            log.transactions_from(0, &ArrayDecoder).map(Result::unwrap).count()\n        );\n\n        let mut log = Generic::<_, [u8; 32]>::open(\n            log.repo.clone(),\n            Options {\n                max_segment_size: 1024,\n                ..Options::default()\n            },\n        )\n        .unwrap();\n        let total_txs = fill_log(&mut log, 100, (1..=10).cycle());\n\n        assert_eq!(\n            total_txs,\n            log.transactions_from(0, &ArrayDecoder).map(Result::unwrap).count()\n        );\n    }\n\n    #[test]\n    fn set_new_epoch() {\n        let mut log = Generic::<_, [u8; 32]>::open(repo::Memory::unlimited(), <_>::default()).unwrap();\n        assert_eq!(log.epoch(), Commit::DEFAULT_EPOCH);\n        log.commit([(0, [12; 32])]).unwrap();\n        log.set_epoch(42).unwrap();\n        assert_eq!(log.epoch(), 42);\n        log.commit([(1, [13; 32])]).unwrap();\n\n        log.flush_and_sync().unwrap();\n\n        let epochs = log\n            .commits_from(0)\n            .map(Result::unwrap)\n            .map(|commit| commit.epoch)\n            .collect::<Vec<_>>();\n        assert_eq!(&[Commit::DEFAULT_EPOCH, 42], epochs.as_slice());\n    }\n\n    #[test]\n    fn set_lower_epoch_returns_error() {\n        let mut log = Generic::<_, [u8; 32]>::open(repo::Memory::unlimited(), <_>::default()).unwrap();\n        log.set_epoch(42).unwrap();\n        assert_eq!(log.epoch(), 42);\n        assert_matches!(log.set_epoch(7), Err(e) if e.kind() == io::ErrorKind::InvalidInput)\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/error.rs",
    "content": "use std::io;\n\nuse spacetimedb_sats::buffer::DecodeError;\nuse thiserror::Error;\n\nuse crate::segment;\n\n/// Error yielded by public commitlog iterators.\n#[derive(Debug, Error)]\npub enum Traversal {\n    #[error(\"out-of-order commit: expected-offset={expected_offset} actual-offset={actual_offset}\")]\n    OutOfOrder {\n        expected_offset: u64,\n        actual_offset: u64,\n        /// If the next segment starts with a commit with matching offset, a\n        /// previous bad commit will be ignored. If, however, the offset does\n        /// **not** match, `prev_error` contains the error encountered when\n        /// trying to read the previous commit (which was skipped).\n        #[source]\n        prev_error: Option<Box<Self>>,\n    },\n    /// The log is considered forked iff a commit with the same `min_tx_offset`\n    /// but a different crc32 than the previous commit is encountered.\n    ///\n    /// This may happen in rare circumstances where a write was considered\n    /// failed (e.g. due to a failed `fsync(2)`), when it was actually successful.\n    #[error(\"forked history: offset={offset}\")]\n    Forked { offset: u64 },\n    #[error(\"failed to decode tx record at offset={offset}\")]\n    Decode {\n        offset: u64,\n        #[source]\n        source: DecodeError,\n    },\n    #[error(\"checksum mismatch at offset={offset}\")]\n    Checksum {\n        offset: u64,\n        #[source]\n        source: ChecksumMismatch,\n    },\n    #[error(transparent)]\n    Io(#[from] io::Error),\n}\n\n/// Error returned by [`crate::Commitlog::append`].\n#[derive(Debug, Error)]\n#[error(\"failed to commit during append\")]\npub struct Append<T> {\n    /// The payload which was passed to [`crate::Commitlog::append`], but was\n    /// not retained because flushing the data to the underlying storage failed.\n    pub txdata: T,\n    /// Why flushing to persistent storage failed.\n    #[source]\n    pub source: io::Error,\n}\n\n/// A checksum mismatch was detected.\n///\n/// Usually wrapped in another error, such as [`io::Error`].\n#[derive(Debug, Error)]\n#[error(\"checksum mismatch\")]\npub struct ChecksumMismatch;\n\n#[derive(Debug, Error)]\npub enum SegmentMetadata {\n    #[error(\"invalid commit encountered\")]\n    InvalidCommit {\n        sofar: segment::Metadata,\n        #[source]\n        source: io::Error,\n    },\n    #[error(transparent)]\n    Io(#[from] io::Error),\n}\n\n/// Recursively concatenate `e.source()`, separated by \": \".\npub(crate) fn source_chain(e: &impl std::error::Error) -> String {\n    let mut s = String::new();\n    let mut source = e.source();\n    while let Some(cause) = source {\n        s.push(':');\n        s.push(' ');\n        s.push_str(&cause.to_string());\n        source = cause.source()\n    }\n\n    s\n}\n"
  },
  {
    "path": "crates/commitlog/src/index/indexfile.rs",
    "content": "use std::{\n    fs::{self, File},\n    io,\n    marker::PhantomData,\n    mem,\n};\n\nuse log::{debug, trace};\nuse memmap2::MmapMut;\nuse spacetimedb_paths::server::OffsetIndexFile;\n\nuse super::IndexError;\nconst KEY_SIZE: usize = mem::size_of::<u64>();\nconst ENTRY_SIZE: usize = KEY_SIZE + mem::size_of::<u64>();\n\n/// A mutable representation of an index file using memory-mapped I/O.\n///\n/// `IndexFileMut` provides efficient read and write access to an index file, which stores\n/// key-value pairs\n/// Successive key written should be sorted in ascending order, 0 is invalid-key value\n#[derive(Debug)]\npub struct IndexFileMut<Key> {\n    // A mutable memory-mapped buffer that represents the file contents.\n    inner: MmapMut,\n    /// The number of entries currently stored in the index file.\n    num_entries: usize,\n\n    _marker: PhantomData<Key>,\n}\n\nimpl<Key: Into<u64> + From<u64>> IndexFileMut<Key> {\n    pub fn create_index_file(path: &OffsetIndexFile, cap: u64) -> io::Result<Self> {\n        path.open_file(File::options().write(true).read(true).create_new(true))\n            .and_then(|file| {\n                file.set_len(cap * ENTRY_SIZE as u64)?;\n                let mmap = unsafe { MmapMut::map_mut(&file) }?;\n\n                Ok(IndexFileMut {\n                    inner: mmap,\n                    num_entries: 0,\n                    _marker: PhantomData,\n                })\n            })\n            .or_else(|e| {\n                if e.kind() == io::ErrorKind::AlreadyExists {\n                    debug!(\"Index file {} already exists\", path.display());\n                    Self::open_index_file(path, cap)\n                } else {\n                    Err(e)\n                }\n            })\n    }\n\n    pub fn open_index_file(path: &OffsetIndexFile, cap: u64) -> io::Result<Self> {\n        let file = path.open_file(File::options().read(true).write(true))?;\n        file.set_len(cap * ENTRY_SIZE as u64)?;\n        let mmap = unsafe { MmapMut::map_mut(&file)? };\n\n        let mut me = IndexFileMut {\n            inner: mmap,\n            num_entries: 0,\n            _marker: PhantomData,\n        };\n        me.num_entries = me.num_entries().map_err(io::Error::other)?;\n\n        Ok(me)\n    }\n\n    pub fn delete_index_file(path: &OffsetIndexFile) -> io::Result<()> {\n        fs::remove_file(path)\n    }\n\n    // Searches for first 0-key, to count number of entries\n    fn num_entries(&self) -> Result<usize, IndexError> {\n        for index in 0.. {\n            match self.index_lookup(index) {\n                Ok((entry, _)) => {\n                    if entry.into() == 0 {\n                        return Ok(index);\n                    }\n                }\n                Err(IndexError::OutOfRange) => return Ok(index),\n                Err(e) => return Err(e),\n            }\n        }\n        Ok(0)\n    }\n\n    /// Finds the 0 based index of the first key encountered that is just smaller than or equal to the given key.\n    ///\n    /// # Error\n    ///\n    /// - `IndexError::KeyNotFound`: If the key is smaller than the first entry key\n    pub fn find_index(&self, key: Key) -> Result<(Key, u64), IndexError> {\n        let key = key.into();\n\n        let mut low = 0;\n        let mut high = self.num_entries;\n\n        while low < high {\n            let mid = low + (high - low) / 2;\n            let (mid_key, _) = self.index_lookup(mid)?;\n            if mid_key.into() > key {\n                high = mid;\n            } else {\n                low = mid;\n            }\n\n            if high - low == 1 {\n                break;\n            }\n        }\n\n        let low_key = self.index_lookup(low).map(|(k, _)| k.into())?;\n        if low == 0 && key < low_key {\n            return Err(IndexError::KeyNotFound);\n        }\n        // If found key is 0, return `KeyNotFound`\n        if low_key == 0 {\n            return Err(IndexError::KeyNotFound);\n        }\n\n        Ok((Key::from(low_key), low as u64))\n    }\n\n    /// Looks up the key-value pair at the specified index in the index file.\n    /// # Errors\n    ///\n    /// - `IndexError::OutOfMemory`: If the index is out of memory range.\n    fn index_lookup(&self, index: usize) -> Result<(Key, u64), IndexError> {\n        let start = index * ENTRY_SIZE;\n        if start + ENTRY_SIZE > self.inner.len() {\n            return Err(IndexError::OutOfRange);\n        }\n\n        entry(&self.inner, start)\n    }\n\n    /// Returns the last key in the index file.\n    /// Or 0 if no key is present\n    fn last_key(&self) -> Result<u64, IndexError> {\n        if self.num_entries == 0 {\n            return Ok(0);\n        }\n        let start = (self.num_entries - 1) * ENTRY_SIZE;\n        u64_from_le_bytes(&self.inner[start..start + KEY_SIZE])\n    }\n\n    // Return (key, value) pair of key just smaller or equal to given key\n    ///\n    /// # Error\n    /// - `IndexError::KeyNotFound`: If the key is smaller than the first entry key\n    pub fn key_lookup(&self, key: Key) -> Result<(Key, u64), IndexError> {\n        let (_, idx) = self.find_index(key)?;\n        self.index_lookup(idx as usize)\n    }\n\n    /// Appends a key-value pair to the index file.\n    /// Successive calls to `append` must supply key in ascending order\n    ///\n    /// Errors\n    /// - `IndexError::InvalidInput`: Either Key or Value is 0\n    /// - `IndexError::OutOfMemory`: Append after index file is already full.\n    pub fn append(&mut self, key: Key, value: u64) -> Result<(), IndexError> {\n        let key = key.into();\n        let last_key = self.last_key()?;\n        if last_key >= key {\n            return Err(IndexError::InvalidInput(last_key, key));\n        }\n\n        let start = self.num_entries * ENTRY_SIZE;\n        if start + ENTRY_SIZE > self.inner.len() {\n            return Err(IndexError::OutOfRange);\n        }\n\n        let key_bytes = key.to_le_bytes();\n        let value_bytes = value.to_le_bytes();\n\n        self.inner[start..start + KEY_SIZE].copy_from_slice(&key_bytes);\n        self.inner[start + KEY_SIZE..start + ENTRY_SIZE].copy_from_slice(&value_bytes);\n        self.num_entries += 1;\n        Ok(())\n    }\n\n    /// Asynchronously flushes any pending changes to the index file\n    ///\n    /// Due to Async nature, `Ok(())` does not guarantee that the changes are flushed.\n    /// an `Err` value indicates it definitely did not succeed\n    pub fn async_flush(&self) -> io::Result<()> {\n        self.inner.flush_async()\n    }\n\n    /// Truncates the index file starting from the entry with a key greater than\n    /// or equal to the given key.\n    ///\n    /// If successful, `key` will no longer be in the index.\n    pub(crate) fn truncate(&mut self, key: Key) -> Result<(), IndexError> {\n        let key = key.into();\n        let (found_key, index) = self\n            .find_index(Key::from(key))\n            .map(|(found, index)| (found.into(), index))\n            .or_else(|e| {\n                match e {\n                    // If key is smaller than first entry, truncate all entries\n                    IndexError::KeyNotFound => Ok((key, 0)),\n                    _ => Err(e),\n                }\n            })?;\n\n        // If returned key is smaller than asked key, truncate from next entry\n        self.num_entries = if found_key == key {\n            index as usize\n        } else {\n            index as usize + 1\n        };\n\n        let start = self.num_entries * ENTRY_SIZE;\n        trace!(\n            \"truncate key={} found={} index={} num-entries={} start={}\",\n            key,\n            found_key,\n            index,\n            self.num_entries,\n            start\n        );\n\n        if start < self.inner.len() {\n            self.inner[start..].fill(0);\n        }\n\n        self.inner.flush()?;\n\n        Ok(())\n    }\n\n    /// Obtain an iterator over the entries of the index.\n    pub fn entries(&self) -> Entries<'_, Key> {\n        Entries {\n            mmap: &self.inner,\n            pos: 0,\n            max: self.num_entries * ENTRY_SIZE,\n            _key: PhantomData,\n        }\n    }\n}\n\nimpl<'a, K: Into<u64> + From<u64>> IntoIterator for &'a IndexFileMut<K> {\n    type Item = Result<(K, u64), IndexError>;\n    type IntoIter = Entries<'a, K>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.entries()\n    }\n}\n\nimpl<Key: Into<u64> + From<u64>> From<IndexFile<Key>> for IndexFileMut<Key> {\n    fn from(IndexFile { inner }: IndexFile<Key>) -> Self {\n        inner\n    }\n}\n\n/// A wrapper over [`IndexFileMut`] to provide read-only access to the index file.\npub struct IndexFile<Key> {\n    inner: IndexFileMut<Key>,\n}\n\nimpl<Key: Into<u64> + From<u64>> IndexFile<Key> {\n    pub fn open_index_file(path: &OffsetIndexFile) -> io::Result<Self> {\n        let file = path.open_file(File::options().read(true).write(true))?;\n        let mmap = unsafe { MmapMut::map_mut(&file)? };\n\n        let mut inner = IndexFileMut {\n            inner: mmap,\n            num_entries: 0,\n            _marker: PhantomData,\n        };\n        inner.num_entries = inner.num_entries().map_err(io::Error::other)?;\n\n        Ok(Self { inner })\n    }\n\n    pub fn key_lookup(&self, key: Key) -> Result<(Key, u64), IndexError> {\n        self.inner.key_lookup(key)\n    }\n\n    /// Obtain an iterator over the entries of the index.\n    pub fn entries(&self) -> Entries<'_, Key> {\n        self.inner.entries()\n    }\n}\n\nimpl<K> AsMut<IndexFileMut<K>> for IndexFile<K> {\n    fn as_mut(&mut self) -> &mut IndexFileMut<K> {\n        &mut self.inner\n    }\n}\n\nimpl<'a, Key: Into<u64> + From<u64>> IntoIterator for &'a IndexFile<Key> {\n    type Item = Result<(Key, u64), IndexError>;\n    type IntoIter = Entries<'a, Key>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.entries()\n    }\n}\n\nimpl<Key: Into<u64> + From<u64>> From<IndexFileMut<Key>> for IndexFile<Key> {\n    fn from(inner: IndexFileMut<Key>) -> Self {\n        Self { inner }\n    }\n}\n\n/// Iterator over the entries of an [`IndexFileMut`] or [`IndexFile`].\n///\n/// Yields pairs of `(K, u64)` or an error if an entry could not be decoded.\npub struct Entries<'a, K> {\n    mmap: &'a [u8],\n    pos: usize,\n    max: usize,\n    _key: PhantomData<K>,\n}\n\nimpl<K: From<u64>> Iterator for Entries<'_, K> {\n    type Item = Result<(K, u64), IndexError>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.pos >= self.max {\n            return None;\n        }\n\n        let item = entry(self.mmap, self.pos);\n        if item.is_ok() {\n            self.pos += ENTRY_SIZE;\n        }\n        Some(item)\n    }\n}\n\nfn entry<K: From<u64>>(mmap: &[u8], start: usize) -> Result<(K, u64), IndexError> {\n    let entry = &mmap[start..start + ENTRY_SIZE];\n    let sz = mem::size_of::<u64>();\n    let key = u64_from_le_bytes(&entry[..sz])?;\n    let val = u64_from_le_bytes(&entry[sz..])?;\n\n    Ok((key.into(), val))\n}\n\nfn u64_from_le_bytes(x: &[u8]) -> Result<u64, IndexError> {\n    x.try_into()\n        .map_err(|_| IndexError::InvalidFormat)\n        .map(u64::from_le_bytes)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::path::Path;\n\n    use super::*;\n    use pretty_assertions::assert_matches;\n    use spacetimedb_paths::server::CommitLogDir;\n    use spacetimedb_paths::FromPathUnchecked;\n    use tempfile::TempDir;\n\n    /// Create and fill index file with key as first `fill_till - 1` even numbers\n    fn create_and_fill_index(cap: u64, fill_till: u64) -> Result<IndexFileMut<u64>, IndexError> {\n        // Create a temporary directory for testing.\n        // Dropping this at the end of the function is fine, as we're memory-\n        // mapping the index file.\n        let temp_dir = TempDir::new()?;\n        create_and_fill_index_in(temp_dir.path(), cap, fill_till)\n    }\n\n    fn create_and_fill_index_in(dir: &Path, cap: u64, fill_till: u64) -> Result<IndexFileMut<u64>, IndexError> {\n        // Create an index file\n        let mut index_file = create_index_in(dir, cap)?;\n\n        // Enter even number keys from 2\n        for i in 1..fill_till {\n            index_file.append(i * 2, i * 2 * 100)?;\n        }\n\n        Ok(index_file)\n    }\n\n    /// Create an index file in `dir`.\n    ///\n    /// Useful if `dir` is a temporary directory and should not be dropped.\n    fn create_index_in(dir: &Path, cap: u64) -> io::Result<IndexFileMut<u64>> {\n        let index_path = index_path(dir);\n        IndexFileMut::create_index_file(&index_path, cap)\n    }\n\n    fn index_path(dir: &Path) -> OffsetIndexFile {\n        CommitLogDir::from_path_unchecked(dir).index(0)\n    }\n\n    trait KeyLookup {\n        type Key;\n        fn key_lookup(&self, key: Self::Key) -> Result<(Self::Key, u64), IndexError>;\n    }\n\n    impl<K: Into<u64> + From<u64>> KeyLookup for IndexFileMut<K> {\n        type Key = K;\n        fn key_lookup(&self, key: Self::Key) -> Result<(Self::Key, u64), IndexError> {\n            IndexFileMut::key_lookup(self, key)\n        }\n    }\n\n    impl<K: Into<u64> + From<u64>> KeyLookup for IndexFile<K> {\n        type Key = K;\n        fn key_lookup(&self, key: Self::Key) -> Result<(Self::Key, u64), IndexError> {\n            IndexFile::key_lookup(self, key)\n        }\n    }\n\n    fn assert_key_lookup(index: &impl KeyLookup<Key = u64>) -> Result<(), IndexError> {\n        // looking for exact match key\n        assert_eq!(index.key_lookup(2)?, (2, 200));\n\n        // Should fetch smaller key\n        assert_eq!(index.key_lookup(5)?, (4, 400));\n\n        // Key bigger than last entry should return last entry\n        assert_eq!(index.key_lookup(100)?, (8, 800));\n\n        // key smaller than 1st entry should return error\n        assert!(index.key_lookup(1).is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_empty_index_lookup_should_fail() -> Result<(), IndexError> {\n        let index = create_index_in(TempDir::new().unwrap().path(), 100)?;\n        assert_matches!(index.key_lookup(0), Err(IndexError::KeyNotFound));\n        assert_matches!(index.key_lookup(10), Err(IndexError::KeyNotFound));\n        Ok(())\n    }\n\n    #[test]\n    fn test_key_lookup() -> Result<(), IndexError> {\n        let index = create_and_fill_index(10, 5)?;\n        assert_key_lookup(&index)\n    }\n\n    #[test]\n    fn test_key_lookup_reopen() -> Result<(), IndexError> {\n        let tmp = TempDir::new()?;\n        create_and_fill_index_in(tmp.path(), 10, 5)?;\n\n        // Re-open as mutable index.\n        let index: IndexFileMut<_> = IndexFileMut::open_index_file(&index_path(tmp.path()), 10)?;\n        assert_key_lookup(&index)\n    }\n\n    #[test]\n    fn test_key_lookup_readonly() -> Result<(), IndexError> {\n        let tmp = TempDir::new()?;\n        create_and_fill_index_in(tmp.path(), 10, 5)?;\n\n        // Re-open as read-only index.\n        let index: IndexFile<u64> = IndexFile::open_index_file(&index_path(tmp.path()))?;\n        assert_key_lookup(&index)\n    }\n\n    #[test]\n    fn test_append() -> Result<(), IndexError> {\n        // fill till one below capacity\n        let mut index = create_and_fill_index(10, 10)?;\n\n        assert_eq!(index.num_entries, 9);\n\n        // append smaller than already appended key\n        assert!(index.append(17, 300).is_err());\n\n        // append duplicate key\n        assert!(index.append(18, 500).is_err());\n\n        // append to fill the capacty\n        assert!(index.append(22, 500).is_ok());\n\n        // Append after capacity should give error\n        assert!(index.append(224, 600).is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_truncate() -> Result<(), IndexError> {\n        let mut index = create_and_fill_index(10, 9)?;\n\n        assert_eq!(index.num_entries, 8);\n\n        // Truncate last present entry\n        index.truncate(16)?;\n        assert_eq!(index.num_entries, 7);\n\n        // Truncate from middle key entry\n        // as key is not present, key with bigger entries should truncate\n        index.truncate(9)?;\n        assert_eq!(index.num_entries, 4);\n\n        // Truncate from middle key entry\n        // as key is not present, key with bigger entries should truncate\n        index.truncate(9)?;\n        assert_eq!(index.num_entries, 4);\n\n        // Truncating from bigger key than already present must be no-op\n        index.truncate(9)?;\n        assert_eq!(index.num_entries, 4);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_truncate_eddge_cases() -> Result<(), IndexError> {\n        // index file with no entries\n        let mut index = create_and_fill_index(10, 0)?;\n\n        // Truncate from key smaller than first entry should truncate all entries\n        index.truncate(1)?;\n        assert_eq!(index.num_entries, 0);\n\n        // first entry will be with key 2\n        let mut index = create_and_fill_index(10, 5)?;\n        assert_eq!(index.num_entries, 4);\n        index.truncate(1)?;\n        assert_eq!(index.num_entries, 0);\n\n        index.truncate(0)?;\n        assert_eq!(index.num_entries, 0);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_close_open_index() -> Result<(), IndexError> {\n        // Create a temporary directory for testing\n        let temp_dir = TempDir::new()?;\n        let path = CommitLogDir::from_path_unchecked(temp_dir.path());\n        let index_path = path.index(0);\n\n        // Create an index file\n        let mut index_file: IndexFileMut<u64> = IndexFileMut::create_index_file(&index_path, 100)?;\n\n        for i in 1..10 {\n            index_file.append(i * 2, i * 2 * 100)?;\n        }\n\n        assert_eq!(index_file.num_entries, 9);\n        drop(index_file);\n\n        let open_index_file: IndexFileMut<u64> = IndexFileMut::open_index_file(&index_path, 100)?;\n        assert_eq!(open_index_file.num_entries, 9);\n        assert_eq!(open_index_file.key_lookup(6)?, (6, 600));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_iterator_iterates() -> Result<(), IndexError> {\n        let index = create_and_fill_index(100, 100)?;\n\n        let expected = (1..100).map(|key| (key * 2, key * 2 * 100)).collect::<Vec<_>>();\n        let entries = index.entries().collect::<Result<Vec<_>, _>>()?;\n        assert_eq!(&entries, &expected);\n\n        // `IndexFile` should yield the same result\n        let index: IndexFile<u64> = index.into();\n        let entries = index.entries().collect::<Result<Vec<_>, _>>()?;\n        assert_eq!(&entries, &expected);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_iterator_yields_nothing_for_empty_index() -> Result<(), IndexError> {\n        let index = create_and_fill_index(100, 0)?;\n        let entries = index.entries().collect::<Result<Vec<_>, _>>()?;\n        assert!(entries.is_empty());\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/index/mod.rs",
    "content": "use std::io;\n\nuse thiserror::Error;\nmod indexfile;\n\npub use indexfile::{IndexFile, IndexFileMut};\n\n#[derive(Error, Debug)]\npub enum IndexError {\n    #[error(\"I/O error: {0}\")]\n    Io(#[from] io::Error),\n\n    #[error(\"Index file out of range\")]\n    OutOfRange,\n\n    #[error(\"Asked key is smaller than the first entry in the index\")]\n    KeyNotFound,\n\n    #[error(\"Key should be monotonically increasing: input: {1}, last: {0}\")]\n    InvalidInput(u64, u64),\n\n    #[error(\"index file is not readable\")]\n    InvalidFormat,\n}\n"
  },
  {
    "path": "crates/commitlog/src/lib.rs",
    "content": "use std::{\n    io,\n    num::NonZeroU64,\n    ops::RangeBounds,\n    sync::{Arc, RwLock},\n};\n\nuse log::trace;\nuse repo::{fs::OnNewSegmentFn, Repo};\nuse spacetimedb_paths::server::CommitLogDir;\n\npub mod commit;\npub mod commitlog;\nmod index;\npub mod repo;\npub mod segment;\nmod varchar;\nmod varint;\n\nuse crate::segment::Committed;\npub use crate::{\n    commit::{Commit, StoredCommit},\n    commitlog::CommittedMeta,\n    payload::{Decoder, Encode},\n    repo::{fs::SizeOnDisk, TxOffset},\n    segment::{Transaction, DEFAULT_LOG_FORMAT_VERSION},\n    varchar::Varchar,\n};\npub mod error;\npub mod payload;\n\n#[cfg(feature = \"streaming\")]\npub mod stream;\n\n#[cfg(any(test, feature = \"test\"))]\npub mod tests;\n\n/// [`Commitlog`] options.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(\n    feature = \"serde\",\n    derive(serde::Serialize, serde::Deserialize),\n    serde(rename_all = \"kebab-case\")\n)]\npub struct Options {\n    /// Set the log format version to write, and the maximum supported version.\n    ///\n    /// Choosing a payload format `T` of [`Commitlog`] should usually result in\n    /// updating the [`DEFAULT_LOG_FORMAT_VERSION`] of this crate. Sometimes it\n    /// may however be useful to set the version at runtime, e.g. to experiment\n    /// with new or very old versions.\n    ///\n    /// Default: [`DEFAULT_LOG_FORMAT_VERSION`]\n    #[cfg_attr(feature = \"serde\", serde(default = \"Options::default_log_format_version\"))]\n    pub log_format_version: u8,\n    /// The maximum size in bytes to which log segments should be allowed to\n    /// grow.\n    ///\n    /// Default: 1GiB\n    #[cfg_attr(feature = \"serde\", serde(default = \"Options::default_max_segment_size\"))]\n    pub max_segment_size: u64,\n    /// Whenever at least this many bytes have been written to the currently\n    /// active segment, an entry is added to its offset index.\n    ///\n    /// Default: 4096\n    #[cfg_attr(feature = \"serde\", serde(default = \"Options::default_offset_index_interval_bytes\"))]\n    pub offset_index_interval_bytes: NonZeroU64,\n    /// If `true`, require that the segment must be synced to disk before an\n    /// index entry is added.\n    ///\n    /// Setting this to `false` (the default) will update the index every\n    /// `offset_index_interval_bytes`, even if the commitlog wasn't synced.\n    /// This means that the index could contain non-existent entries in the\n    /// event of a crash.\n    ///\n    /// Setting it to `true` will update the index when the commitlog is synced,\n    /// and `offset_index_interval_bytes` have been written.\n    /// This means that the index could contain fewer index entries than\n    /// strictly every `offset_index_interval_bytes`.\n    ///\n    /// Default: false\n    #[cfg_attr(\n        feature = \"serde\",\n        serde(default = \"Options::default_offset_index_require_segment_fsync\")\n    )]\n    pub offset_index_require_segment_fsync: bool,\n    /// If `true`, preallocate the disk space for commitlog segments, up to the\n    /// `max_segment_size`.\n    ///\n    /// Has no effect if the `fallocate` feature is not enabled.\n    #[cfg_attr(feature = \"serde\", serde(default = \"Options::default_preallocate_segments\"))]\n    pub preallocate_segments: bool,\n    /// Size in bytes of the memory buffer holding commit data before flushing\n    /// to storage.\n    ///\n    /// Default: 8KiB\n    #[cfg_attr(feature = \"serde\", serde(default = \"Options::default_write_buffer_size\"))]\n    pub write_buffer_size: usize,\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Self::DEFAULT\n    }\n}\n\nimpl Options {\n    pub const DEFAULT_MAX_SEGMENT_SIZE: u64 = 1024 * 1024 * 1024;\n    pub const DEFAULT_OFFSET_INDEX_INTERVAL_BYTES: NonZeroU64 = NonZeroU64::new(4096).expect(\"4096 > 0, qed\");\n    pub const DEFAULT_OFFSET_INDEX_REQUIRE_SEGMENT_FSYNC: bool = false;\n    pub const DEFAULT_PREALLOCATE_SEGMENTS: bool = false;\n    pub const DEFAULT_WRITE_BUFFER_SIZE: usize = 8 * 1024;\n\n    pub const DEFAULT: Self = Self {\n        log_format_version: DEFAULT_LOG_FORMAT_VERSION,\n        max_segment_size: Self::default_max_segment_size(),\n        offset_index_interval_bytes: Self::default_offset_index_interval_bytes(),\n        offset_index_require_segment_fsync: Self::default_offset_index_require_segment_fsync(),\n        preallocate_segments: Self::default_preallocate_segments(),\n        write_buffer_size: Self::default_write_buffer_size(),\n    };\n\n    pub const fn default_log_format_version() -> u8 {\n        DEFAULT_LOG_FORMAT_VERSION\n    }\n\n    pub const fn default_max_segment_size() -> u64 {\n        Self::DEFAULT_MAX_SEGMENT_SIZE\n    }\n\n    pub const fn default_offset_index_interval_bytes() -> NonZeroU64 {\n        Self::DEFAULT_OFFSET_INDEX_INTERVAL_BYTES\n    }\n\n    pub const fn default_offset_index_require_segment_fsync() -> bool {\n        Self::DEFAULT_OFFSET_INDEX_REQUIRE_SEGMENT_FSYNC\n    }\n\n    pub const fn default_preallocate_segments() -> bool {\n        Self::DEFAULT_PREALLOCATE_SEGMENTS\n    }\n\n    pub const fn default_write_buffer_size() -> usize {\n        Self::DEFAULT_WRITE_BUFFER_SIZE\n    }\n\n    /// Compute the length in bytes of an offset index based on the settings in\n    /// `self`.\n    pub fn offset_index_len(&self) -> u64 {\n        self.max_segment_size / self.offset_index_interval_bytes\n    }\n}\n\n/// The canonical commitlog, backed by on-disk log files.\n///\n/// Records in the log are of type `T`, which canonically is instantiated to\n/// [`payload::Txdata`].\npub struct Commitlog<T> {\n    inner: RwLock<commitlog::Generic<repo::Fs, T>>,\n}\n\nimpl<T> Commitlog<T> {\n    /// Open the log at root directory `root` with [`Options`].\n    ///\n    /// The root directory must already exist.\n    ///\n    /// Note that opening a commitlog involves I/O: some consistency checks are\n    /// performed, and the next writing position is determined.\n    ///\n    /// This is only necessary when opening the commitlog for writing. See the\n    /// free-standing functions in this module for how to traverse a read-only\n    /// commitlog.\n    pub fn open(root: CommitLogDir, opts: Options, on_new_segment: Option<Arc<OnNewSegmentFn>>) -> io::Result<Self> {\n        #[cfg(not(feature = \"fallocate\"))]\n        if opts.preallocate_segments {\n            log::warn!(\n                \"`preallocate_segments` enabled but not supported by this build. commitlog-dir={}\",\n                root.display()\n            );\n        }\n        let inner = commitlog::Generic::open(repo::Fs::new(root, on_new_segment)?, opts)?;\n\n        Ok(Self {\n            inner: RwLock::new(inner),\n        })\n    }\n\n    /// Determine the maximum transaction offset considered durable.\n    ///\n    /// The offset is `None` if the log hasn't been flushed to disk yet.\n    pub fn max_committed_offset(&self) -> Option<u64> {\n        self.inner.read().unwrap().max_committed_offset()\n    }\n\n    /// Determine the minimum transaction offset in the log.\n    ///\n    /// The offset is `None` if the log hasn't been flushed to disk yet.\n    pub fn min_committed_offset(&self) -> Option<u64> {\n        self.inner.read().unwrap().min_committed_offset()\n    }\n\n    /// Get the current epoch.\n    ///\n    /// See also: [`Commit::epoch`].\n    pub fn epoch(&self) -> u64 {\n        self.inner.read().unwrap().epoch()\n    }\n\n    /// Update the current epoch.\n    ///\n    /// Does nothing if the given `epoch` is equal to the current epoch.\n    /// Otherwise flushes outstanding transactions to disk (equivalent to\n    /// [`Self::flush`]) before updating the epoch.\n    ///\n    /// Returns the maximum transaction offset written to disk. The offset is\n    /// `None` if the log is empty and no data was pending to be flushed.\n    ///\n    /// # Errors\n    ///\n    /// If `epoch` is smaller than the current epoch, an error of kind\n    /// [`io::ErrorKind::InvalidInput`] is returned.\n    ///\n    /// Errors from the implicit flush are propagated.\n    pub fn set_epoch(&self, epoch: u64) -> io::Result<Option<u64>> {\n        let mut inner = self.inner.write().unwrap();\n        inner.set_epoch(epoch)?;\n\n        Ok(inner.max_committed_offset())\n    }\n\n    /// Sync all OS-buffered writes to disk.\n    ///\n    /// Note that this does **not** write outstanding records to disk.\n    /// Use [`Self::flush_and_sync`] or call [`Self::flush`] prior to this\n    /// method to ensure all data is on disk.\n    ///\n    /// Returns the maximum transaction offset which is considered durable after\n    /// this method returns successfully. The offset is `None` if the log hasn't\n    /// been flushed to disk yet.\n    ///\n    /// # Panics\n    ///\n    /// This method panics if syncing fails irrecoverably.\n    pub fn sync(&self) -> Option<u64> {\n        let mut inner = self.inner.write().unwrap();\n        trace!(\"sync commitlog\");\n        inner.sync();\n\n        inner.max_committed_offset()\n    }\n\n    /// Write all outstanding transaction records to disk.\n    ///\n    /// Note that this does **not** force the OS to sync the data to disk.\n    /// Use [`Self::flush_and_sync`] or call [`Self::sync`] after this method\n    /// to ensure all data is on disk.\n    ///\n    /// Returns the maximum transaction offset written to disk. The offset is\n    /// `None` if the log is empty and no data was pending to be flushed.\n    ///\n    /// Repeatedly calling this method may return the same value.\n    pub fn flush(&self) -> io::Result<Option<u64>> {\n        let mut inner = self.inner.write().unwrap();\n        trace!(\"flush commitlog\");\n        inner.flush()?;\n\n        Ok(inner.max_committed_offset())\n    }\n\n    /// Write all outstanding transaction records to disk and flush OS buffers.\n    ///\n    /// Equivalent to calling [`Self::flush`] followed by [`Self::sync`], but\n    /// without releasing the write lock in between.\n    ///\n    /// # Errors\n    ///\n    /// An error is returned if writing to disk fails due to an I/O error.\n    ///\n    /// # Panics\n    ///\n    /// This method panics if syncing fails irrecoverably.\n    pub fn flush_and_sync(&self) -> io::Result<Option<u64>> {\n        let mut inner = self.inner.write().unwrap();\n        trace!(\"flush and sync commitlog\");\n        inner.flush()?;\n        inner.sync();\n\n        Ok(inner.max_committed_offset())\n    }\n\n    /// Obtain an iterator which traverses the log from the start, yielding\n    /// [`StoredCommit`]s.\n    ///\n    /// The returned iterator is not aware of segment rotation. That is, if a\n    /// new segment is created after this method returns, the iterator will not\n    /// traverse it.\n    ///\n    /// Commits appended to the log while it is being traversed are generally\n    /// visible to the iterator. Upon encountering [`io::ErrorKind::UnexpectedEof`],\n    /// however, a new iterator should be created using [`Self::commits_from`]\n    /// with the last transaction offset yielded.\n    ///\n    /// Note that the very last [`StoredCommit`] in a commitlog may be corrupt\n    /// (e.g. due to a partial write to disk), but a subsequent `append` will\n    /// bring the log into a consistent state.\n    ///\n    /// This means that, when this iterator yields an `Err` value, the consumer\n    /// may want to check if the iterator is exhausted (by calling `next()`)\n    /// before treating the `Err` value as an application error.\n    pub fn commits(&self) -> impl Iterator<Item = Result<StoredCommit, error::Traversal>> + use<T> {\n        self.commits_from(0)\n    }\n\n    /// Obtain an iterator starting from transaction offset `offset`, yielding\n    /// [`StoredCommit`]s.\n    ///\n    /// Similar to [`Self::commits`] but will skip until the offset is contained\n    /// in the next [`StoredCommit`] to yield.\n    ///\n    /// Note that the first [`StoredCommit`] yielded is the first commit\n    /// containing the given transaction offset, i.e. its `min_tx_offset` may be\n    /// smaller than `offset`.\n    pub fn commits_from(&self, offset: u64) -> impl Iterator<Item = Result<StoredCommit, error::Traversal>> + use<T> {\n        self.inner.read().unwrap().commits_from(offset)\n    }\n\n    /// Get a list of segment offsets, sorted in ascending order.\n    pub fn existing_segment_offsets(&self) -> io::Result<Vec<u64>> {\n        self.inner.read().unwrap().repo.existing_offsets()\n    }\n\n    /// Compress the segments at the offsets provided, marking them as immutable.\n    pub fn compress_segments(&self, offsets: &[u64]) -> io::Result<()> {\n        // even though `compress_segment` takes &self, we take an\n        // exclusive lock to avoid any weirdness happening.\n        #[allow(clippy::readonly_write_lock)]\n        let inner = self.inner.write().unwrap();\n        assert!(!offsets.contains(&inner.head.min_tx_offset()));\n        // TODO: parallelize, maybe\n        offsets\n            .iter()\n            .try_for_each(|&offset| inner.repo.compress_segment(offset))\n    }\n\n    /// Remove all data from the log and reopen it.\n    ///\n    /// Log segments are deleted starting from the newest. As multiple segments\n    /// cannot be deleted atomically, the log may not be completely empty if\n    /// the method returns an error.\n    ///\n    /// Note that the method consumes `self` to ensure the log is not modified\n    /// while resetting.\n    pub fn reset(self) -> io::Result<Self> {\n        let inner = self.inner.into_inner().unwrap().reset()?;\n        Ok(Self {\n            inner: RwLock::new(inner),\n        })\n    }\n\n    /// Remove all data past the given transaction `offset` from the log and\n    /// reopen it.\n    ///\n    /// Like with [`Self::reset`], it may happen that not all segments newer\n    /// than `offset` can be deleted.\n    ///\n    /// If the method returns successfully, the most recent [`Commit`] in the\n    /// log will contain the transaction at `offset`.\n    ///\n    /// Note that the method consumes `self` to ensure the log is not modified\n    /// while resetting.\n    pub fn reset_to(self, offset: u64) -> io::Result<Self> {\n        let inner = self.inner.into_inner().unwrap().reset_to(offset)?;\n        Ok(Self {\n            inner: RwLock::new(inner),\n        })\n    }\n\n    /// Determine the size on disk of this commitlog.\n    pub fn size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        let inner = self.inner.read().unwrap();\n        inner.repo.size_on_disk()\n    }\n}\n\nimpl<T: Encode> Commitlog<T> {\n    /// Write `transactions` to the log.\n    ///\n    /// This will store all `transactions` as a single [Commit]\n    /// (note that `transactions` must not yield more than [u16::MAX] elements).\n    ///\n    /// Data is buffered internally, call [Self::flush] to force flushing to\n    /// the underlying storage.\n    ///\n    /// Returns `Ok(None)` if `transactions` was empty, otherwise [Committed],\n    /// which contains the offset range and checksum of the commit.\n    ///\n    /// # Errors\n    ///\n    /// An `Err` value is returned in the following cases:\n    ///\n    /// - if the transaction sequence is invalid, e.g. because the transaction\n    ///   offsets are not contiguous.\n    ///\n    ///   In this case, **none** of the `transactions` will be written.\n    ///\n    /// - if creating the new segment fails due to an I/O error.\n    ///\n    /// # Panics\n    ///\n    /// The method panics if:\n    ///\n    /// - `transactions` exceeds [u16::MAX] elements\n    ///\n    /// - [Self::flush] or writing to the underlying buffered writer fails\n    ///\n    ///   This is likely caused by some storage issue. As we cannot tell with\n    ///   certainty how much data (if any) has been written, the internal state\n    ///   becomes invalid and thus a panic is raised.\n    ///\n    /// - [Self::sync] panics (called when rotating segments)\n    pub fn commit<U: Into<Transaction<T>>>(\n        &self,\n        transactions: impl IntoIterator<Item = U>,\n    ) -> io::Result<Option<Committed>> {\n        let mut inner = self.inner.write().unwrap();\n        inner.commit(transactions)\n    }\n\n    /// Obtain an iterator which traverses the log from the start, yielding\n    /// [`Transaction`]s.\n    ///\n    /// The provided `decoder`'s [`Decoder::decode_record`] method will be\n    /// called [`Commit::n`] times per [`Commit`] to obtain the individual\n    /// transaction payloads.\n    ///\n    /// Like [`Self::commits`], the iterator is not aware of segment rotation.\n    /// That is, if a new segment is created after this method returns, the\n    /// iterator will not traverse it.\n    ///\n    /// Transactions appended to the log while it is being traversed are\n    /// generally visible to the iterator. Upon encountering [`io::ErrorKind::UnexpectedEof`],\n    /// however, a new iterator should be created using [`Self::transactions_from`]\n    /// with the last transaction offset yielded.\n    ///\n    /// Note that the very last [`Commit`] in a commitlog may be corrupt (e.g.\n    /// due to a partial write to disk), but a subsequent `append` will bring\n    /// the log into a consistent state.\n    ///\n    /// This means that, when this iterator yields an `Err` value, the consumer\n    /// may want to check if the iterator is exhausted (by calling `next()`)\n    /// before treating the `Err` value as an application error.\n    pub fn transactions<'a, D>(\n        &self,\n        de: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a + use<'a, D, T>\n    where\n        D: Decoder<Record = T>,\n        D::Error: From<error::Traversal>,\n        T: 'a,\n    {\n        self.transactions_from(0, de)\n    }\n\n    /// Obtain an iterator starting from transaction offset `offset`, yielding\n    /// [`Transaction`]s.\n    ///\n    /// Similar to [`Self::transactions`] but will skip until the provided\n    /// `offset`, i.e. the first [`Transaction`] yielded will be the transaction\n    /// with offset `offset`.\n    pub fn transactions_from<'a, D>(\n        &self,\n        offset: u64,\n        de: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a + use<'a, D, T>\n    where\n        D: Decoder<Record = T>,\n        D::Error: From<error::Traversal>,\n        T: 'a,\n    {\n        self.inner.read().unwrap().transactions_from(offset, de)\n    }\n\n    /// Traverse the log from the start and \"fold\" its transactions into the\n    /// provided [`Decoder`].\n    ///\n    /// A [`Decoder`] is a stateful object due to the requirement to store\n    /// schema information in the log itself. That is, a [`Decoder`] may need to\n    /// be able to resolve transaction schema information dynamically while\n    /// traversing the log.\n    ///\n    /// This is equivalent to \"replaying\" a log into a database state. In this\n    /// scenario, it is not interesting to consume the [`Transaction`] payload\n    /// as an iterator.\n    ///\n    /// This method allows the use of a [`Decoder`] which returns zero-sized\n    /// data (e.g. `Decoder<Record = ()>`), as it will not allocate the commit\n    /// payload into a struct.\n    ///\n    /// Note that, unlike [`Self::transactions`], this method will ignore a\n    /// corrupt commit at the very end of the traversed log.\n    pub fn fold_transactions<D>(&self, de: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        self.fold_transactions_from(0, de)\n    }\n\n    /// Traverse the log from the given transaction offset and \"fold\" its\n    /// transactions into the provided [`Decoder`].\n    ///\n    /// Similar to [`Self::fold_transactions`] but will skip until the provided\n    /// `offset`, i.e. the first `tx_offset` passed to [`Decoder::decode_record`]\n    /// will be equal to `offset`.\n    pub fn fold_transactions_from<D>(&self, offset: u64, de: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        self.inner.read().unwrap().fold_transactions_from(offset, de)\n    }\n\n    pub fn fold_transactions_range<D>(&self, range: impl RangeBounds<u64>, de: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        self.inner.read().unwrap().fold_transaction_range(range, de)\n    }\n}\n\n/// Extract the most recently written [`segment::Metadata`] from the commitlog\n/// in `repo`.\n///\n/// Returns `None` if the commitlog is empty.\n///\n/// Note that this function validates the most recent segment, which entails\n/// traversing it from the start.\n///\n/// The function can be used instead of the pattern:\n///\n/// ```ignore\n/// let log = Commitlog::open(..)?;\n/// let max_offset = log.max_committed_offset();\n/// ```\n///\n/// like so:\n///\n/// ```ignore\n/// let max_offset = committed_meta(..)?.map(|meta| meta.tx_range.end);\n/// ```\n///\n/// Unlike `open`, no segment will be created in an empty `repo`.\npub fn committed_meta(root: CommitLogDir) -> io::Result<Option<CommittedMeta>> {\n    commitlog::committed_meta(repo::Fs::new(root, None)?)\n}\n\n/// Obtain an iterator which traverses the commitlog located at the `root`\n/// directory from the start, yielding [`StoredCommit`]s.\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::commits`] for more information.\npub fn commits(root: CommitLogDir) -> io::Result<impl Iterator<Item = Result<StoredCommit, error::Traversal>>> {\n    commits_from(root, 0)\n}\n\n/// Obtain an iterator which traverses the commitlog located at the `root`\n/// directory starting from `offset` and yielding [`StoredCommit`]s.\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::commits_from`] for more information.\npub fn commits_from(\n    root: CommitLogDir,\n    offset: u64,\n) -> io::Result<impl Iterator<Item = Result<StoredCommit, error::Traversal>>> {\n    commitlog::commits_from(repo::Fs::new(root, None)?, DEFAULT_LOG_FORMAT_VERSION, offset)\n}\n\n/// Obtain an iterator which traverses the commitlog located at the `root`\n/// directory from the start, yielding [`Transaction`]s.\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::transactions`] for more information.\npub fn transactions<'a, D, T>(\n    root: CommitLogDir,\n    de: &'a D,\n) -> io::Result<impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a>\nwhere\n    D: Decoder<Record = T>,\n    D::Error: From<error::Traversal>,\n    T: 'a,\n{\n    transactions_from(root, 0, de)\n}\n\n/// Obtain an iterator which traverses the commitlog located at the `root`\n/// directory starting from `offset` and yielding [`Transaction`]s.\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::transactions_from`] for more information.\npub fn transactions_from<'a, D, T>(\n    root: CommitLogDir,\n    offset: u64,\n    de: &'a D,\n) -> io::Result<impl Iterator<Item = Result<Transaction<T>, D::Error>> + 'a>\nwhere\n    D: Decoder<Record = T>,\n    D::Error: From<error::Traversal>,\n    T: 'a,\n{\n    commitlog::transactions_from(repo::Fs::new(root, None)?, DEFAULT_LOG_FORMAT_VERSION, offset, de)\n}\n\n/// Traverse the commitlog located at the `root` directory from the start and\n/// \"fold\" its transactions into the provided [`Decoder`].\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::fold_transactions`] for more information.\npub fn fold_transactions<D>(root: CommitLogDir, de: D) -> Result<(), D::Error>\nwhere\n    D: Decoder,\n    D::Error: From<error::Traversal> + From<io::Error>,\n{\n    fold_transactions_from(root, 0, de)\n}\n\n/// Traverse the commitlog located at the `root` directory starting from `offset`\n/// and \"fold\" its transactions into the provided [`Decoder`].\n///\n/// Starts the traversal without the upfront I/O imposed by [`Commitlog::open`].\n/// See [`Commitlog::fold_transactions_from`] for more information.\npub fn fold_transactions_from<D>(root: CommitLogDir, offset: u64, de: D) -> Result<(), D::Error>\nwhere\n    D: Decoder,\n    D::Error: From<error::Traversal> + From<io::Error>,\n{\n    commitlog::fold_transactions_from(repo::Fs::new(root, None)?, DEFAULT_LOG_FORMAT_VERSION, offset, de)\n}\n\npub fn fold_transaction_range<D>(root: CommitLogDir, range: impl RangeBounds<u64>, de: D) -> Result<(), D::Error>\nwhere\n    D: Decoder,\n    D::Error: From<error::Traversal> + From<io::Error>,\n{\n    commitlog::fold_transaction_range(repo::Fs::new(root, None)?, DEFAULT_LOG_FORMAT_VERSION, range, de)\n}\n"
  },
  {
    "path": "crates/commitlog/src/payload/txdata.rs",
    "content": "use std::sync::Arc;\n\nuse bitflags::bitflags;\nuse spacetimedb_sats::buffer::{BufReader, BufWriter, DecodeError};\nuse thiserror::Error;\n\nuse crate::{\n    error,\n    varint::{decode_varint, encode_varint},\n    Encode, Varchar, DEFAULT_LOG_FORMAT_VERSION,\n};\n\n// Re-export so we get a hyperlink in rustdocs by default\npub use spacetimedb_primitives::TableId;\n\n/// A visitor useful to implement stateful [`super::Decoder`]s of [`Txdata`] payloads.\npub trait Visitor {\n    type Error: From<DecodeError>;\n    /// The type corresponding to one element in [`Ops::rowdata`].\n    type Row;\n\n    /// Called for each row in each [`Ops`] of a [`Mutations`]' `inserts`.\n    ///\n    /// The implementation is expected to determine the appropriate schema based\n    /// on the supplied [`TableId`], and to decode the row data from the\n    /// supplied [`BufReader`].\n    ///\n    /// The reader's position is assumed to be at the start of the next row\n    /// after the method returns.\n    fn visit_insert<'a, R: BufReader<'a>>(\n        &mut self,\n        table_id: TableId,\n        reader: &mut R,\n    ) -> Result<Self::Row, Self::Error>;\n\n    /// Called for each row in each [`Ops`] of a [`Mutations`]' `deletes`.\n    ///\n    /// Similar to [`Self::visit_insert`], but allows the visitor to determine\n    /// the start of the next section in a [`Mutations`] payload.\n    fn visit_delete<'a, R: BufReader<'a>>(\n        &mut self,\n        table_id: TableId,\n        reader: &mut R,\n    ) -> Result<Self::Row, Self::Error>;\n\n    /// Called to skip over rows from `reader` within a [`Mutations`] of a TX that should not be folded.\n    ///\n    /// Takes `&mut self` because schema lookups may need mutable access to the visitor\n    /// in order to memoize the computed schema.\n    /// This method should not store or use the row in any way.\n    fn skip_row<'a, R: BufReader<'a>>(&mut self, table_id: TableId, reader: &mut R) -> Result<(), Self::Error>;\n\n    /// Called for each [`TableId`] encountered in the `truncates` section of\n    /// a [`Mutations`].\n    ///\n    /// The default implementation does nothing.\n    fn visit_truncate(&mut self, _table_id: TableId) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    /// Called for each [`Txdata`] record in a [`crate::Commit`].\n    ///\n    /// The default implementation does nothing.\n    fn visit_tx_start(&mut self, _offset: u64) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    /// Called after each successful decode of a [`Txdata`] payload.\n    ///\n    /// The default implementation does nothing.\n    fn visit_tx_end(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    /// Called for each [`Inputs`] encountered in a [`Txdata`] payload.\n    ///\n    /// The default implementation does nothing.\n    fn visit_inputs(&mut self, _inputs: &Inputs) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    /// Called for each [`Outputs`] encountered in a [`Txdata`] payload.\n    ///\n    /// The default implementation does nothing.\n    fn visit_outputs(&mut self, _outputs: &Outputs) -> Result<(), Self::Error> {\n        Ok(())\n    }\n}\n\nbitflags! {\n    #[derive(Clone, Copy)]\n    pub struct Flags: u8 {\n        const HAVE_INPUTS    = 0b10000000;\n        const HAVE_OUTPUTS   = 0b01000000;\n        const HAVE_MUTATIONS = 0b00100000;\n    }\n}\n\n/// The canonical payload format of a [`crate::Commitlog`].\n///\n/// This type may eventually be defined in the core datastore crate.\n#[derive(Clone, Debug, PartialEq)]\npub struct Txdata<T> {\n    pub inputs: Option<Inputs>,\n    pub outputs: Option<Outputs>,\n    pub mutations: Option<Mutations<T>>,\n}\n\nimpl<T> Txdata<T> {\n    /// `true` if `self` contains neither inputs, outputs nor mutations.\n    pub fn is_empty(&self) -> bool {\n        self.inputs.is_none()\n            && self.outputs.is_none()\n            && self.mutations.as_ref().map(Mutations::is_empty).unwrap_or(true)\n    }\n}\n\nimpl<T: Encode> Txdata<T> {\n    pub const VERSION: u8 = DEFAULT_LOG_FORMAT_VERSION;\n\n    pub fn encode(&self, buf: &mut impl BufWriter) {\n        let mut flags = Flags::empty();\n        flags.set(Flags::HAVE_INPUTS, self.inputs.is_some());\n        flags.set(Flags::HAVE_OUTPUTS, self.outputs.is_some());\n        flags.set(Flags::HAVE_MUTATIONS, self.mutations.is_some());\n\n        buf.put_u8(flags.bits());\n        if let Some(inputs) = &self.inputs {\n            inputs.encode(buf);\n        }\n        if let Some(outputs) = &self.outputs {\n            outputs.encode(buf);\n        }\n        if let Some(mutations) = &self.mutations {\n            mutations.encode(buf)\n        }\n    }\n\n    /// Decode `Self` from the given buffer.\n    pub fn decode<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<Self, V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        let flags = Flags::from_bits_retain(reader.get_u8()?);\n\n        // If the flags indicate that the payload contains `Inputs`,\n        // try to decode and visit them.\n        let inputs = flags\n            .contains(Flags::HAVE_INPUTS)\n            .then(|| Inputs::decode(reader))\n            .transpose()?;\n        if let Some(inputs) = &inputs {\n            visitor.visit_inputs(inputs)?;\n        }\n\n        // If the flags indicate that the payload contains `Outputs`,\n        // try to decode and visit them.\n        let outputs = flags\n            .contains(Flags::HAVE_OUTPUTS)\n            .then(|| Outputs::decode(reader))\n            .transpose()?;\n        if let Some(outputs) = &outputs {\n            visitor.visit_outputs(outputs)?;\n        }\n\n        // If the flags indicate that the payload contains `Mutations`,\n        // try to decode them.\n        let mutations = flags\n            .contains(Flags::HAVE_MUTATIONS)\n            .then(|| Mutations::decode(visitor, reader))\n            .transpose()?;\n\n        Ok(Self {\n            inputs,\n            outputs,\n            mutations,\n        })\n    }\n\n    /// Variant of [`Self::decode`] which doesn't allocate `Self`.\n    ///\n    /// Useful for folding traversals where the visitor state suffices.\n    /// Note that both [`Inputs`] and [`Outputs`] are still allocated to satisfy\n    /// the [`Visitor`] trait, but [`Mutations`] aren't.\n    pub fn consume<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<(), V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        let flags = Flags::from_bits_retain(reader.get_u8()?);\n\n        // If the flags indicate that the payload contains `Inputs`,\n        // try to decode and visit them.\n        if flags.contains(Flags::HAVE_INPUTS) {\n            let inputs = Inputs::decode(reader)?;\n            visitor.visit_inputs(&inputs)?;\n        }\n\n        // If the flags indicate that the payload contains `Outputs`,\n        // try to decode and visit them.\n        if flags.contains(Flags::HAVE_OUTPUTS) {\n            let outputs = Outputs::decode(reader)?;\n            visitor.visit_outputs(&outputs)?;\n        }\n\n        // If the flags indicate that the payload contains `Mutations`,\n        // try to consume them (i.e. decode but don't allocate).\n        if flags.contains(Flags::HAVE_MUTATIONS) {\n            Mutations::consume(visitor, reader)?;\n        }\n\n        Ok(())\n    }\n\n    pub fn skip<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<(), V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        let flags = Flags::from_bits_retain(reader.get_u8()?);\n\n        // If the flags indicate that the payload contains `Inputs`,\n        // try to decode them.\n        if flags.contains(Flags::HAVE_INPUTS) {\n            Inputs::decode(reader)?;\n        }\n\n        // If the flags indicate that the payload contains `Outputs`,\n        // try to decode them.\n        if flags.contains(Flags::HAVE_OUTPUTS) {\n            Outputs::decode(reader)?;\n        }\n\n        // If the flags indicate that the payload contains `Mutations`,\n        // try to consume them (i.e. decode but don't allocate).\n        if flags.contains(Flags::HAVE_MUTATIONS) {\n            Mutations::skip(visitor, reader)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// The inputs of a transaction, i.e. the name and arguments of a reducer call.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\npub struct Inputs {\n    pub reducer_name: Arc<Varchar<255>>,\n    pub reducer_args: Arc<[u8]>,\n    // TODO: `reducer_args` should be a `ProductValue`, which\n    // requires `Decoder` to be able to resolve the argument\n    // type by `reducer_name`.\n    //\n    // We can do that once modules are stored in the database\n    // itself, or the database is otherwise able to load the\n    // module info.\n    //\n    // At that point, we can also remove the length prefix of\n    // `Inputs`.\n}\n\nimpl Inputs {\n    pub fn encode(&self, buf: &mut impl BufWriter) {\n        let slen = self.reducer_name.len() as u8;\n        let len = /* slen */ 1 + slen as usize + self.reducer_args.len();\n        buf.put_u32(len as u32);\n        buf.put_u8(slen);\n        buf.put_slice(self.reducer_name.as_bytes());\n        buf.put_slice(&self.reducer_args);\n    }\n\n    pub fn decode<'a, R: BufReader<'a>>(reader: &mut R) -> Result<Self, DecodeError> {\n        let len = reader.get_u32()?;\n        let slen = reader.get_u8()?;\n        let reducer_name = {\n            let bytes = reader.get_slice(slen as usize)?;\n            Varchar::from_str(std::str::from_utf8(bytes)?)\n                .expect(\"slice len cannot be > 255\")\n                .into()\n        };\n        let reducer_args = reader.get_slice(len as usize - /* slen */ 1 - slen as usize)?.into();\n\n        Ok(Self {\n            reducer_name,\n            reducer_args,\n        })\n    }\n}\n\n/// The outputs of a transaction.\n///\n/// The only currently possible output of a transaction is a string\n/// representation of an `Err` return value of a reducer.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\npub struct Outputs {\n    // TODO: We may want a `Cow`-backed variant of `Varchar` for this.\n    pub reducer_output: Arc<Varchar<255>>,\n}\n\nimpl Outputs {\n    pub fn encode(&self, buf: &mut impl BufWriter) {\n        let slen = self.reducer_output.len() as u8;\n        buf.put_u8(slen);\n        buf.put_slice(self.reducer_output.as_bytes());\n    }\n\n    pub fn decode<'a, R: BufReader<'a>>(reader: &mut R) -> Result<Self, DecodeError> {\n        let slen = reader.get_u8()?;\n        let reducer_output = {\n            let bytes = reader.get_slice(slen as usize)?;\n            Varchar::from_str(std::str::from_utf8(bytes)?).unwrap().into()\n        };\n\n        Ok(Self { reducer_output })\n    }\n}\n\n/// Mutations of the data store performed by a transaction.\n///\n/// All operations are supposed to be **mutually exclusive**.\n///\n/// Even though not enforced, each kind of mutation should not contain duplicate\n/// [`TableId`]s.\n#[derive(Clone, Debug, PartialEq)]\npub struct Mutations<T> {\n    /// Rows inserted.\n    pub inserts: Box<[Ops<T>]>,\n    /// Rows deleted.\n    pub deletes: Box<[Ops<T>]>,\n    /// Truncated tables.\n    pub truncates: Box<[TableId]>,\n}\n\nimpl<T> Mutations<T> {\n    /// `true` if `self` contains no mutations at all.\n    pub fn is_empty(&self) -> bool {\n        self.inserts.is_empty() && self.deletes.is_empty() && self.truncates.is_empty()\n    }\n}\n\nimpl<T: Encode> Mutations<T> {\n    /// Encode `self` in the canonical format to the given buffer.\n    pub fn encode(&self, buf: &mut impl BufWriter) {\n        encode_varint(self.inserts.len(), buf);\n        for ops in self.inserts.iter() {\n            ops.encode(buf);\n        }\n        encode_varint(self.deletes.len(), buf);\n        for ops in self.deletes.iter() {\n            ops.encode(buf);\n        }\n        encode_varint(self.truncates.len(), buf);\n        for TableId(table_id) in self.truncates.iter() {\n            buf.put_u32(*table_id);\n        }\n    }\n\n    pub fn skip<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<(), V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        // Skip the 'insert' operations.\n        // The row data of a single 'insert' operation is decoded by the visitor.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            for _ in 0..m {\n                visitor.skip_row(table_id, reader)?;\n            }\n        }\n\n        // Do the same for the `delete` operations.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            for _ in 0..m {\n                visitor.skip_row(table_id, reader)?;\n            }\n        }\n\n        // Skip the truncates. This does not require involvement from the visitor.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            reader.get_u32()?;\n        }\n\n        Ok(())\n    }\n\n    /// Decode `Self` from the given buffer.\n    pub fn decode<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<Self, V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        // Extract, visit and collect the 'insert' operations.\n        // The row data of a single 'insert' operation is extracted by the visitor.\n        let n = decode_varint(reader)?;\n        let mut inserts = Vec::with_capacity(n);\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            let mut rowdata = Vec::with_capacity(m);\n            for _ in 0..m {\n                let row = visitor.visit_insert(table_id, reader)?;\n                rowdata.push(row);\n            }\n            inserts.push(Ops {\n                table_id,\n                rowdata: rowdata.into(),\n            });\n        }\n\n        // Extract, visit and collect the 'delete' operations.\n        // The row data of a single 'delete' operation is extracted by the visitor.\n        let n = decode_varint(reader)?;\n        let mut deletes = Vec::with_capacity(n);\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            let mut rowdata = Vec::with_capacity(m);\n            for _ in 0..m {\n                let row = visitor.visit_delete(table_id, reader)?;\n                rowdata.push(row);\n            }\n            deletes.push(Ops {\n                table_id,\n                rowdata: rowdata.into(),\n            });\n        }\n\n        // Extract, visit and collect the 'truncate' operations.\n        // 'truncate' operations don't have row data.\n        let n = decode_varint(reader)?;\n        let mut truncates = Vec::with_capacity(n);\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            visitor.visit_truncate(table_id)?;\n            truncates.push(table_id);\n        }\n\n        Ok(Self {\n            inserts: inserts.into(),\n            deletes: deletes.into(),\n            truncates: truncates.into(),\n        })\n    }\n\n    /// Variant of [`Self::decode`] which does not allocate `Self`.\n    ///\n    /// Useful for folding traversals where the visitor state suffices.\n    pub fn consume<'a, V, R>(visitor: &mut V, reader: &mut R) -> Result<(), V::Error>\n    where\n        V: Visitor<Row = T>,\n        R: BufReader<'a>,\n    {\n        // Extract and visit the 'insert' operations.\n        // Any row data returned by the visitor is discarded.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            for _ in 0..m {\n                visitor.visit_insert(table_id, reader)?;\n            }\n        }\n\n        // Extract and visit the 'delete' operations.\n        // Any row data returned by the visitor is discarded.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            let m = decode_varint(reader)?;\n            for _ in 0..m {\n                visitor.visit_delete(table_id, reader)?;\n            }\n        }\n\n        // Extract and visit the 'truncate' operations.\n        let n = decode_varint(reader)?;\n        for _ in 0..n {\n            let table_id = reader.get_u32().map(TableId)?;\n            visitor.visit_truncate(table_id)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<T: Encode> Encode for Txdata<T> {\n    fn encode_record<W: BufWriter>(&self, writer: &mut W) {\n        self.encode(writer)\n    }\n}\n\n/// An operation (insert or delete) on a given table.\n#[derive(Clone, Debug, PartialEq)]\npub struct Ops<T> {\n    /// The table this operation applies to.\n    pub table_id: TableId,\n    /// The list of full rows inserted or deleted.\n    pub rowdata: Arc<[T]>,\n}\n\nimpl<T: Encode> Ops<T> {\n    /// Encode `self` in the canonical format to the given buffer.\n    pub fn encode(&self, buf: &mut impl BufWriter) {\n        buf.put_u32(self.table_id.0);\n        encode_varint(self.rowdata.len(), buf);\n        for row in self.rowdata.iter() {\n            Encode::encode_record(row, buf);\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum DecoderError<V> {\n    #[error(\"unsupported version: {given} supported={supported}\")]\n    UnsupportedVersion { supported: u8, given: u8 },\n    #[error(transparent)]\n    Decode(#[from] DecodeError),\n    #[error(transparent)]\n    Visitor(V),\n    #[error(transparent)]\n    Traverse(#[from] error::Traversal),\n}\n\n/// A free standing implementation of [`crate::Decoder::skip_record`]\n/// specifically for `Txdata<ProductValue>`.\npub fn skip_record_fn<'a, V, R>(visitor: &mut V, version: u8, reader: &mut R) -> Result<(), DecoderError<V::Error>>\nwhere\n    V: Visitor,\n    V::Row: Encode,\n    R: BufReader<'a>,\n{\n    if version > Txdata::<V::Row>::VERSION {\n        return Err(DecoderError::UnsupportedVersion {\n            supported: Txdata::<V::Row>::VERSION,\n            given: version,\n        });\n    }\n    Txdata::skip(visitor, reader).map_err(DecoderError::Visitor)?;\n\n    Ok(())\n}\n\n/// A free standing implementation of [`crate::Decoder::decode_record`], which\n/// drives the supplied [`Visitor`].\n///\n/// Simplifies decoder implementations operating on [`Txdata`], as the\n/// implementation only needs to care about managing the [`Visitor`] (which may\n/// be behind a lock).\npub fn decode_record_fn<'a, V, R>(\n    visitor: &mut V,\n    version: u8,\n    tx_offset: u64,\n    reader: &mut R,\n) -> Result<Txdata<V::Row>, DecoderError<V::Error>>\nwhere\n    V: Visitor,\n    V::Row: Encode,\n    R: BufReader<'a>,\n{\n    process_record(visitor, version, tx_offset, reader, Txdata::decode)\n}\n\n/// Variant of [`decode_record_fn`] which expects, but doesn't allocate\n/// [`Txdata`] records.\npub fn consume_record_fn<'a, V, R>(\n    visitor: &mut V,\n    version: u8,\n    tx_offset: u64,\n    reader: &mut R,\n) -> Result<(), DecoderError<V::Error>>\nwhere\n    V: Visitor,\n    V::Row: Encode,\n    R: BufReader<'a>,\n{\n    process_record(visitor, version, tx_offset, reader, Txdata::consume)\n}\n\nfn process_record<'a, V, R, F, T>(\n    visitor: &mut V,\n    version: u8,\n    tx_offset: u64,\n    reader: &mut R,\n    decode_txdata: F,\n) -> Result<T, DecoderError<V::Error>>\nwhere\n    V: Visitor,\n    V::Row: Encode,\n    R: BufReader<'a>,\n    F: FnOnce(&mut V, &mut R) -> Result<T, V::Error>,\n{\n    if version > Txdata::<V::Row>::VERSION {\n        return Err(DecoderError::UnsupportedVersion {\n            supported: Txdata::<V::Row>::VERSION,\n            given: version,\n        });\n    }\n    visitor.visit_tx_start(tx_offset).map_err(DecoderError::Visitor)?;\n    let record = decode_txdata(visitor, reader).map_err(DecoderError::Visitor)?;\n    visitor.visit_tx_end().map_err(DecoderError::Visitor)?;\n\n    Ok(record)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use once_cell::sync::Lazy;\n    use proptest::prelude::*;\n    use spacetimedb_sats::{product, AlgebraicType, ProductType, ProductValue};\n\n    fn gen_table_id() -> impl Strategy<Value = TableId> {\n        any::<u32>().prop_map(TableId)\n    }\n\n    fn gen_ops(pv: ProductValue) -> impl Strategy<Value = Ops<ProductValue>> {\n        (gen_table_id(), prop::collection::vec(Just(pv), 1..10)).prop_map(|(table_id, rowdata)| Ops {\n            table_id,\n            rowdata: rowdata.into(),\n        })\n    }\n\n    fn gen_mutations(pv: ProductValue) -> impl Strategy<Value = Mutations<ProductValue>> {\n        (\n            prop::collection::vec(gen_ops(pv.clone()), 0..10),\n            prop::collection::vec(gen_ops(pv.clone()), 0..10),\n            prop::collection::vec(gen_table_id(), 0..10),\n        )\n            .prop_map(|(inserts, deletes, truncates)| Mutations {\n                inserts: inserts.into(),\n                deletes: deletes.into(),\n                truncates: truncates.into(),\n            })\n    }\n\n    fn gen_txdata(pv: ProductValue) -> impl Strategy<Value = Txdata<ProductValue>> {\n        (\n            prop::option::of(any::<Inputs>()),\n            prop::option::of(any::<Outputs>()),\n            prop::option::of(gen_mutations(pv)),\n        )\n            .prop_map(|(inputs, outputs, mutations)| Txdata {\n                inputs,\n                outputs,\n                mutations,\n            })\n    }\n\n    static SOME_PV: Lazy<ProductValue> = Lazy::new(|| product![42u64, \"kermit\", 4u32, 2u32, 18u32]);\n    static SOME_PV_TY: Lazy<ProductType> = Lazy::new(|| {\n        ProductType::from([\n            (\"id\", AlgebraicType::U64),\n            (\"name\", AlgebraicType::String),\n            (\"x\", AlgebraicType::U32),\n            (\"y\", AlgebraicType::U32),\n            (\"z\", AlgebraicType::U32),\n        ])\n    });\n\n    struct MockVisitor;\n\n    impl Visitor for MockVisitor {\n        type Error = DecodeError;\n        type Row = ProductValue;\n\n        fn visit_insert<'a, R: BufReader<'a>>(\n            &mut self,\n            _table_id: TableId,\n            reader: &mut R,\n        ) -> Result<Self::Row, Self::Error> {\n            ProductValue::decode(&SOME_PV_TY, reader)\n        }\n\n        fn visit_delete<'a, R: BufReader<'a>>(\n            &mut self,\n            _table_id: TableId,\n            reader: &mut R,\n        ) -> Result<Self::Row, Self::Error> {\n            ProductValue::decode(&SOME_PV_TY, reader)\n        }\n\n        fn skip_row<'a, R: BufReader<'a>>(&mut self, _table_id: TableId, reader: &mut R) -> Result<(), Self::Error> {\n            ProductValue::decode(&SOME_PV_TY, reader)?;\n            Ok(())\n        }\n    }\n\n    proptest! {\n        #[test]\n        fn prop_inputs_roundtrip(inputs in any::<Inputs>()) {\n            let mut buf = Vec::new();\n            inputs.encode(&mut buf);\n            assert_eq!(inputs, Inputs::decode(&mut buf.as_slice()).unwrap());\n        }\n\n        #[test]\n        fn prop_outputs_roundtrip(outputs in any::<Outputs>()) {\n            let mut buf = Vec::new();\n            outputs.encode(&mut buf);\n            assert_eq!(outputs, Outputs::decode(&mut buf.as_slice()).unwrap());\n        }\n\n        #[test]\n        fn prop_mutations_roundtrip(muts in gen_mutations(SOME_PV.clone())) {\n            let mut buf = Vec::new();\n            muts.encode(&mut buf);\n            assert_eq!(muts, Mutations::decode(&mut MockVisitor, &mut buf.as_slice()).unwrap());\n        }\n\n        #[test]\n        fn prop_txdata_roundtrip(txdata in gen_txdata(SOME_PV.clone())) {\n            let mut buf = Vec::new();\n            txdata.encode(&mut buf);\n            assert_eq!(txdata, Txdata::decode(&mut MockVisitor, &mut buf.as_slice()).unwrap());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/payload.rs",
    "content": "use std::sync::Arc;\n\nuse spacetimedb_sats::{\n    bsatn,\n    buffer::{BufReader, BufWriter, DecodeError},\n    ser::Serialize,\n    ProductValue,\n};\nuse thiserror::Error;\n\npub mod txdata;\npub use txdata::Txdata;\n\n/// A **datatype** which can be encoded.\n///\n/// The transaction payload of the commitlog (i.e. individual records in the log)\n/// must satisfy this trait.\npub trait Encode {\n    /// Encode `self` to the given buffer.\n    fn encode_record<W: BufWriter>(&self, writer: &mut W);\n}\n\nimpl<T: Encode> Encode for Arc<T> {\n    fn encode_record<W: BufWriter>(&self, writer: &mut W) {\n        (**self).encode_record(writer)\n    }\n}\n\nimpl Encode for ProductValue {\n    fn encode_record<W: BufWriter>(&self, writer: &mut W) {\n        self.serialize(bsatn::Serializer::new(writer))\n            .expect(\"bsatn serialize should never fail\");\n    }\n}\n\nimpl Encode for () {\n    fn encode_record<W: BufWriter>(&self, _writer: &mut W) {}\n}\n\n/// A decoder which can decode the transaction (aka record) format of the log.\n///\n/// Unlike [`Encode`], this is not a datatype: the canonical commitlog format\n/// requires to look up row types during log traversal in order to be able to\n/// decode.\npub trait Decoder {\n    /// The type of records this decoder can decode.\n    /// This is also the type which can be appended to a commitlog, and so must\n    /// satisfy [`Encode`].\n    type Record: Encode;\n    /// The type of decode errors, which must subsume [`DecodeError`].\n    type Error: From<DecodeError>;\n\n    /// Decode one [`Self::Record`] from the given buffer.\n    ///\n    /// The `version` argument corresponds to the log format version of the\n    /// current segment (see [`crate::segment::Header::log_format_version`]).\n    ///\n    /// The `tx_argument` is the transaction offset of the current record\n    /// relative to the start of the log.\n    fn decode_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        tx_offset: u64,\n        reader: &mut R,\n    ) -> Result<Self::Record, Self::Error>;\n\n    /// Variant of [`Self::decode_record`] which discards the decoded\n    /// [`Self::Record`].\n    ///\n    /// Useful for folds which don't need to yield or collect record values.\n    ///\n    /// The default implementation just drops the record returned from\n    /// [`Self::decode_record`]. Implementations may want to override this, such\n    /// that the record is not allocated in the first place.\n    fn consume_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        tx_offset: u64,\n        reader: &mut R,\n    ) -> Result<(), Self::Error> {\n        self.decode_record(version, tx_offset, reader).map(drop)\n    }\n\n    /// Advance `reader` past the next [`Self::Record`], without returning it\n    /// or including it in a fold.\n    fn skip_record<'a, R: BufReader<'a>>(&self, version: u8, tx_offset: u64, reader: &mut R)\n        -> Result<(), Self::Error>;\n}\n\nimpl<const N: usize> Encode for [u8; N] {\n    fn encode_record<W: BufWriter>(&self, writer: &mut W) {\n        writer.put_slice(&self[..])\n    }\n}\n\n#[derive(Debug, Error)]\npub enum ArrayDecodeError {\n    #[error(transparent)]\n    Decode(#[from] DecodeError),\n    #[error(transparent)]\n    Traversal(#[from] crate::error::Traversal),\n    #[error(transparent)]\n    Io(#[from] std::io::Error),\n}\n\npub struct ArrayDecoder<const N: usize>;\n\nimpl<const N: usize> Decoder for ArrayDecoder<N> {\n    type Record = [u8; N];\n    type Error = ArrayDecodeError;\n\n    fn decode_record<'a, R: BufReader<'a>>(\n        &self,\n        _version: u8,\n        _tx_offset: u64,\n        reader: &mut R,\n    ) -> Result<Self::Record, Self::Error> {\n        Ok(*reader.get_array()?)\n    }\n\n    fn skip_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        tx_offset: u64,\n        reader: &mut R,\n    ) -> Result<(), Self::Error> {\n        self.decode_record(version, tx_offset, reader).map(drop)\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/repo/fs.rs",
    "content": "use std::fmt;\nuse std::fs::{self, File};\nuse std::io;\nuse std::sync::Arc;\n\nuse log::{debug, warn};\nuse spacetimedb_fs_utils::compression::{compress_with_zstd, CompressReader};\nuse spacetimedb_paths::server::{CommitLogDir, SegmentFile};\nuse tempfile::NamedTempFile;\n\nuse crate::segment::FileLike;\n\nuse super::{Repo, SegmentLen, SegmentReader, TxOffset, TxOffsetIndex, TxOffsetIndexMut};\n\nconst SEGMENT_FILE_EXT: &str = \".stdb.log\";\n\n// TODO\n//\n// - should use advisory locks?\n//\n// Experiment:\n//\n// - O_DIRECT | O_DSYNC\n// - io_uring\n//\n\npub type OnNewSegmentFn = dyn Fn() + Send + Sync + 'static;\n\n/// Size on disk of a [Fs] repo.\n///\n/// Created by [Fs::size_on_disk].\n#[derive(Clone, Copy, Default)]\npub struct SizeOnDisk {\n    /// The total size in bytes of all segments and offset indexes in the repo.\n    pub total_bytes: u64,\n    /// The total number of 512-bytes blocks allocated by all segments and\n    /// offset indexes in the repo.\n    ///\n    /// Only available on unix platforms.\n    ///\n    /// For other platforms, the number computed from the number of 4096-bytes\n    /// pages that would be needed to store `total_bytes`. This may or may not\n    /// reflect that actual storage allocation.\n    ///\n    /// The number of allocated blocks is typically larger than the number of\n    /// actually written bytes.\n    ///\n    /// When the `fallocate` feature is enabled, the number can diverge\n    /// substantially. Use `total_blocks` in this case to monitor disk space.\n    pub total_blocks: u64,\n}\n\nimpl SizeOnDisk {\n    #[cfg(unix)]\n    fn add(&mut self, stat: std::fs::Metadata) {\n        self.total_bytes += stat.len();\n        self.total_blocks += std::os::unix::fs::MetadataExt::blocks(&stat);\n    }\n\n    #[cfg(not(unix))]\n    fn add(&mut self, _stat: std::fs::Metadata) {\n        let imaginary_blocks = if self.total_bytes > 0 {\n            8 * self.total_bytes.div_ceil(4096)\n        } else {\n            0\n        };\n        self.total_blocks = imaginary_blocks;\n    }\n}\n\n/// A commitlog repository [`Repo`] which stores commits in ordinary files on\n/// disk.\n#[derive(Clone)]\npub struct Fs {\n    /// The base directory within which segment files will be stored.\n    root: CommitLogDir,\n\n    /// Channel through which to send a message whenever we create a new segment.\n    ///\n    /// The other end of this channel will be a `SnapshotWorker`,\n    /// which will capture a snapshot each time we rotate segments.\n    on_new_segment: Option<Arc<OnNewSegmentFn>>,\n}\n\nimpl std::fmt::Debug for Fs {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Fs\").field(\"root\", &self.root).finish_non_exhaustive()\n    }\n}\n\nimpl Fs {\n    /// Create a commitlog repository which stores segments in the directory `root`.\n    ///\n    /// `root` must name an extant, accessible, writeable directory.\n    pub fn new(root: CommitLogDir, on_new_segment: Option<Arc<OnNewSegmentFn>>) -> io::Result<Self> {\n        root.create()?;\n        Ok(Self { root, on_new_segment })\n    }\n\n    /// Get the filename for a segment starting with `offset` within this\n    /// repository.\n    pub fn segment_path(&self, offset: u64) -> SegmentFile {\n        self.root.segment(offset)\n    }\n\n    /// Determine the size on disk as the sum of the sizes of all segments, as\n    /// well as offset indexes.\n    ///\n    /// Note that the actively written-to segment (if any) is included.\n    pub fn size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        let mut size = SizeOnDisk::default();\n\n        for offset in self.existing_offsets()? {\n            let segment = self.segment_path(offset);\n            let stat = segment.metadata()?;\n            size.add(stat);\n\n            // Add the size of the offset index file if present.\n            let index = self.root.index(offset);\n            let Some(stat) = index.metadata().map(Some).or_else(|e| match e.kind() {\n                io::ErrorKind::NotFound => Ok(None),\n                _ => Err(e),\n            })?\n            else {\n                continue;\n            };\n            size.add(stat);\n        }\n\n        Ok(size)\n    }\n}\n\nimpl fmt::Display for Fs {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.root.display())\n    }\n}\n\nimpl SegmentLen for File {}\n\nimpl FileLike for NamedTempFile {\n    fn fsync(&mut self) -> io::Result<()> {\n        self.as_file_mut().fsync()\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, size: u64) -> io::Result<()> {\n        self.as_file_mut().ftruncate(tx_offset, size)\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        self.as_file_mut().fallocate(size)\n    }\n}\n\n/// A file-backed, read-only segment.\n///\n/// Transparently handles reading compressed segments.\n/// [Self::sealed] returns `true` if the segment is compressed.\npub struct ReadOnlySegment {\n    inner: CompressReader,\n}\n\nimpl SegmentReader for ReadOnlySegment {\n    #[inline]\n    fn sealed(&self) -> bool {\n        self.inner.is_compressed()\n    }\n}\n\nimpl io::Read for ReadOnlySegment {\n    #[inline]\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        self.inner.read(buf)\n    }\n}\n\nimpl io::BufRead for ReadOnlySegment {\n    #[inline]\n    fn fill_buf(&mut self) -> io::Result<&[u8]> {\n        self.inner.fill_buf()\n    }\n\n    #[inline]\n    fn consume(&mut self, amount: usize) {\n        self.inner.consume(amount);\n    }\n}\n\nimpl io::Seek for ReadOnlySegment {\n    #[inline]\n    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {\n        self.inner.seek(pos)\n    }\n}\n\nimpl SegmentLen for ReadOnlySegment {}\n\nimpl Repo for Fs {\n    type SegmentWriter = File;\n    type SegmentReader = ReadOnlySegment;\n\n    fn create_segment(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        File::options()\n            .read(true)\n            .append(true)\n            .create_new(true)\n            .open(self.segment_path(offset))\n            .or_else(|e| {\n                if e.kind() == io::ErrorKind::AlreadyExists {\n                    debug!(\"segment {offset} already exists\");\n                    // If the segment is completely empty, we can resume writing.\n                    let file = self.open_segment_writer(offset)?;\n                    if file.metadata()?.len() == 0 {\n                        debug!(\"segment {offset} is empty\");\n                        return Ok(file);\n                    }\n\n                    // Otherwise, provide some context.\n                    return Err(io::Error::new(\n                        io::ErrorKind::AlreadyExists,\n                        format!(\"repo {}: segment {} already exists and is non-empty\", self, offset),\n                    ));\n                }\n\n                Err(e)\n            })\n            .inspect(|_| {\n                // We're rotating commitlog segments, so we should also take a snapshot at the earliest opportunity.\n                if let Some(on_new_segment) = self.on_new_segment.as_ref() {\n                    // No need to handle the error here: if the snapshot worker is closed we'll eventually close too,\n                    // and we don't want to die prematurely if there are still TXes to write.\n                    on_new_segment();\n                }\n            })\n    }\n\n    fn open_segment_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        File::options().read(true).append(true).open(self.segment_path(offset))\n    }\n\n    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader> {\n        debug!(\"fs: open segment at {}\", self.segment_path(offset).display());\n        let file = File::open(self.segment_path(offset))?;\n        CompressReader::new(file).map(|inner| ReadOnlySegment { inner })\n    }\n\n    fn remove_segment(&self, offset: u64) -> io::Result<()> {\n        let _ = self.remove_offset_index(offset).map_err(|e| {\n            warn!(\n                \"repo {}: failed to remove offset index for segment {}: {}\",\n                self, offset, e\n            );\n        });\n        fs::remove_file(self.segment_path(offset))\n    }\n\n    fn compress_segment(&self, offset: u64) -> io::Result<()> {\n        let src = self.open_segment_reader(offset)?;\n        // if it's already compressed, leave it be\n        let CompressReader::None(mut src) = src.inner else {\n            return Ok(());\n        };\n\n        let mut dst = NamedTempFile::new_in(&self.root)?;\n        // bytes per frame. in the future, it might be worth looking into putting\n        // every commit into its own frame, to make seeking more efficient.\n        let max_frame_size = 0x1000;\n        compress_with_zstd(&mut src, &mut dst, Some(max_frame_size))?;\n        dst.persist(self.segment_path(offset))?;\n\n        Ok(())\n    }\n\n    fn existing_offsets(&self) -> io::Result<Vec<u64>> {\n        let mut segments = Vec::new();\n\n        for entry in fs::read_dir(&self.root)? {\n            let entry = entry?;\n            if entry.file_type()?.is_file() {\n                let path = entry.path();\n                let name = path.file_name().unwrap_or_default().to_string_lossy();\n                let Some(file_name) = name.strip_suffix(SEGMENT_FILE_EXT) else {\n                    continue;\n                };\n                let Ok(offset) = file_name.parse::<u64>() else {\n                    continue;\n                };\n\n                segments.push(offset);\n            }\n        }\n\n        segments.sort_unstable();\n\n        Ok(segments)\n    }\n\n    fn create_offset_index(&self, offset: TxOffset, cap: u64) -> io::Result<TxOffsetIndexMut> {\n        TxOffsetIndexMut::create_index_file(&self.root.index(offset), cap)\n    }\n\n    fn remove_offset_index(&self, offset: TxOffset) -> io::Result<()> {\n        TxOffsetIndexMut::delete_index_file(&self.root.index(offset))\n    }\n\n    fn get_offset_index(&self, offset: TxOffset) -> io::Result<TxOffsetIndex> {\n        TxOffsetIndex::open_index_file(&self.root.index(offset))\n    }\n}\n\nimpl SegmentLen for CompressReader {}\n\n#[cfg(feature = \"streaming\")]\nimpl crate::stream::AsyncRepo for Fs {\n    type AsyncSegmentWriter = tokio::io::BufWriter<tokio::fs::File>;\n    type AsyncSegmentReader = spacetimedb_fs_utils::compression::AsyncCompressReader<tokio::fs::File>;\n\n    async fn open_segment_reader_async(&self, offset: u64) -> io::Result<Self::AsyncSegmentReader> {\n        let file = tokio::fs::File::open(self.segment_path(offset)).await?;\n        spacetimedb_fs_utils::compression::AsyncCompressReader::new(file).await\n    }\n}\n\n#[cfg(feature = \"streaming\")]\nimpl<T> crate::stream::AsyncLen for spacetimedb_fs_utils::compression::AsyncCompressReader<T> where\n    T: tokio::io::AsyncSeek + tokio::io::AsyncRead + Unpin + Send\n{\n}\n"
  },
  {
    "path": "crates/commitlog/src/repo/mem/segment.rs",
    "content": "use std::{\n    io,\n    slice::SliceIndex,\n    sync::{Arc, Mutex, RwLock},\n};\n\nuse crate::{\n    repo::{\n        mem::{SpaceOnDevice, PAGE_SIZE},\n        SegmentLen, SegmentReader,\n    },\n    segment::FileLike,\n};\n\npub type SharedLock<T> = Arc<RwLock<T>>;\n\n/// Backing storage for a [Segment].\n///\n/// Morally, this consists of [PAGE_SIZE] chunks. Actually allocating the\n/// memory is, however, prohibitively expensive (in particular in property\n/// test). Thus, the underlying [Vec<u8>] buffer allocates as necessary, but\n/// [Storage] tracks the logical amount of allocated space (in [PAGE_SIZE]\n/// increments).\n///\n/// The data of a [Storage] is fully managed by its frontend [Segment].\n/// The type is exported to allow sharing the storage between different\n/// segments, each tracking a different read/write position.\n#[derive(Debug)]\npub(super) struct Storage {\n    alloc: u64,\n    buf: Vec<u8>,\n}\n\nimpl Storage {\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        Self {\n            alloc: 0,\n            buf: Vec::with_capacity(PAGE_SIZE),\n        }\n    }\n\n    pub const fn len(&self) -> usize {\n        self.buf.len()\n    }\n\n    pub const fn is_empty(&self) -> bool {\n        self.buf.is_empty()\n    }\n}\n\n/// A log segment backed by a [Vec<u8>].\n///\n/// Writing to the segment behaves like a file opened with `O_APPEND`:\n/// [`io::Write::write`] always appends to the segment, regardless of the\n/// current position, and updates the position to the new length of the segment.\n/// The initial position is zero.\n///\n/// Note that this is not a faithful model of a file, as safe Rust requires to\n/// protect the buffer with a lock. This means that pathological situations\n/// arising from concurrent read/write access of a file are impossible to occur.\n#[derive(Clone, Debug)]\npub struct Segment {\n    pos: u64,\n    storage: SharedLock<Storage>,\n    space: SpaceOnDevice,\n}\n\nimpl Segment {\n    pub fn new(space: u64) -> Self {\n        Self::from_shared(Arc::new(Mutex::new(space)), Arc::new(RwLock::new(Storage::new())))\n    }\n\n    pub(super) fn from_shared(space: SpaceOnDevice, storage: SharedLock<Storage>) -> Self {\n        Self { pos: 0, space, storage }\n    }\n\n    pub fn len(&self) -> usize {\n        self.storage.read().unwrap().len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.storage.read().unwrap().is_empty()\n    }\n\n    pub fn modify_byte_at(&mut self, pos: usize, f: impl FnOnce(u8) -> u8) {\n        let mut storage = self.storage.write().unwrap();\n        storage.buf[pos] = f(storage.buf[pos])\n    }\n\n    pub fn modify_bytes_at(&mut self, range: impl SliceIndex<[u8], Output = [u8]>, f: impl FnOnce(&mut [u8])) {\n        let mut storage = self.storage.write().unwrap();\n        f(&mut storage.buf[range])\n    }\n\n    pub fn allocated_space(&self) -> u64 {\n        self.storage.read().unwrap().alloc\n    }\n}\n\nimpl io::Write for Segment {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        let mut storage = self.storage.write().unwrap();\n\n        let mut remaining = (storage.alloc - self.pos) as usize;\n        // If we don't have enough space, allocate some.\n        // If not enough space to write all of `buf` can be allocated,\n        // just write as much as we can. The next `write` call will return\n        // ENOSPC then.\n        if remaining == 0 {\n            let mut avail = self.space.lock().unwrap();\n            if *avail == 0 {\n                return Err(enospc());\n            }\n\n            let want = (buf.len() - remaining).next_multiple_of(PAGE_SIZE);\n            let have = want.min(*avail as usize);\n\n            storage.alloc += have as u64;\n            *avail -= have as u64;\n            remaining = (storage.alloc - self.pos) as usize;\n        }\n\n        let read = buf.len().min(remaining);\n        storage.buf.extend(&buf[..read]);\n        self.pos += read as u64;\n\n        Ok(read)\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl io::Read for Segment {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        let storage = self.storage.read().unwrap();\n\n        let Some(remaining) = storage.len().checked_sub(self.pos as usize) else {\n            return Ok(0);\n        };\n        let want = remaining.min(buf.len());\n        let pos = self.pos as usize;\n        buf[..want].copy_from_slice(&storage.buf[pos..pos + want]);\n        self.pos += want as u64;\n\n        Ok(want)\n    }\n}\n\nimpl io::Seek for Segment {\n    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {\n        let (base_pos, offset) = match pos {\n            io::SeekFrom::Start(n) => {\n                self.pos = n;\n                return Ok(n);\n            }\n            io::SeekFrom::End(n) => (self.len() as u64, n),\n            io::SeekFrom::Current(n) => (self.pos, n),\n        };\n        match base_pos.checked_add_signed(offset) {\n            Some(n) => {\n                self.pos = n;\n                Ok(n)\n            }\n            None => Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"invalid seek to a negative or overflowing position\",\n            )),\n        }\n    }\n}\n\nimpl SegmentLen for Segment {\n    fn segment_len(&mut self) -> io::Result<u64> {\n        Ok(self.len() as _)\n    }\n}\n\nimpl FileLike for Segment {\n    fn fsync(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n\n    fn ftruncate(&mut self, _tx_offset: u64, size: u64) -> io::Result<()> {\n        let mut storage = self.storage.write().unwrap();\n        let mut avail = self.space.lock().unwrap();\n\n        // NOTE: We don't modify `self.pos`, which is how `ftruncate(2)` behaves.\n        // This means the position can be invalid after calling this.\n        if size > storage.alloc {\n            if *avail == 0 {\n                return Err(enospc());\n            }\n\n            let want = size.next_multiple_of(PAGE_SIZE as u64) - storage.alloc;\n            let have = want.min(*avail);\n\n            storage.alloc += have;\n            *avail -= have;\n            storage.buf.resize(size as usize, 0);\n\n            // NOTE: `ftruncate(2)` is a bit ambiguous as to what should happen\n            // if the requested size exceeds the available space.\n            //\n            // [std::fs::File::set_len] will succeed, but all subsequent\n            // operations return EBADF.\n            //\n            // That's not super helpful, so instead we zero out as much space as\n            // possible, and return ENOSPC if more than that was requested.\n            if want > have {\n                return Err(enospc());\n            }\n        } else {\n            let alloc = size.next_multiple_of(PAGE_SIZE as u64);\n            *avail += storage.alloc - alloc;\n            storage.alloc = alloc;\n            storage.buf.resize(size as usize, 0);\n        }\n\n        Ok(())\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        let mut storage = self.storage.write().unwrap();\n\n        if size <= storage.alloc {\n            return Ok(());\n        }\n\n        let mut avail = self.space.lock().unwrap();\n        if *avail == 0 {\n            return Err(enospc());\n        }\n\n        let want = size.next_multiple_of(PAGE_SIZE as u64) - storage.alloc;\n        let have = want.min(*avail);\n        storage.alloc += have;\n        *avail -= have;\n\n        if want > have {\n            return Err(enospc());\n        }\n\n        Ok(())\n    }\n}\n\nfn enospc() -> io::Error {\n    io::Error::new(io::ErrorKind::StorageFull, \"no space left on device\")\n}\n\n#[cfg(feature = \"streaming\")]\nmod async_impls {\n    use super::*;\n\n    use std::{\n        io::{Read as _, Seek as _, Write as _},\n        pin::Pin,\n        task::{Context, Poll},\n    };\n\n    use tokio::io::{self, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};\n\n    use crate::stream::{AsyncFsync, AsyncLen, IntoAsyncWriter};\n\n    impl IntoAsyncWriter for Segment {\n        type AsyncWriter = tokio::io::BufWriter<Self>;\n\n        fn into_async_writer(self) -> Self::AsyncWriter {\n            tokio::io::BufWriter::new(self)\n        }\n    }\n\n    impl AsyncRead for Segment {\n        fn poll_read(self: Pin<&mut Self>, _: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<io::Result<()>> {\n            let res = self.get_mut().read(buf.initialize_unfilled());\n            if let Ok(read) = &res {\n                buf.advance(*read);\n            }\n            Poll::Ready(res.map(drop))\n        }\n    }\n\n    impl AsyncWrite for Segment {\n        fn poll_write(self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {\n            Poll::Ready(self.get_mut().write(buf))\n        }\n\n        fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n            Poll::Ready(Ok(()))\n        }\n\n        fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n            Poll::Ready(Ok(()))\n        }\n    }\n\n    impl AsyncSeek for Segment {\n        fn start_seek(self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {\n            self.get_mut().seek(position).map(drop)\n        }\n\n        fn poll_complete(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<u64>> {\n            Poll::Ready(self.get_mut().stream_position())\n        }\n    }\n\n    impl AsyncFsync for Segment {\n        async fn fsync(&self) {}\n    }\n\n    impl AsyncLen for Segment {\n        async fn segment_len(&mut self) -> io::Result<u64> {\n            Ok(self.len() as u64)\n        }\n    }\n}\n\npub struct ReadOnlySegment {\n    inner: io::BufReader<Segment>,\n}\n\nimpl From<Segment> for ReadOnlySegment {\n    fn from(inner: Segment) -> Self {\n        Self {\n            inner: io::BufReader::new(inner),\n        }\n    }\n}\n\nimpl SegmentReader for ReadOnlySegment {\n    /// Memory segments dont' support compression, so are never sealed.\n    fn sealed(&self) -> bool {\n        false\n    }\n}\n\nimpl io::Read for ReadOnlySegment {\n    #[inline]\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        self.inner.read(buf)\n    }\n}\n\nimpl io::BufRead for ReadOnlySegment {\n    #[inline]\n    fn fill_buf(&mut self) -> io::Result<&[u8]> {\n        self.inner.fill_buf()\n    }\n\n    #[inline]\n    fn consume(&mut self, amount: usize) {\n        self.inner.consume(amount);\n    }\n}\n\nimpl io::Seek for ReadOnlySegment {\n    #[inline]\n    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {\n        self.inner.seek(pos)\n    }\n}\n\nimpl SegmentLen for ReadOnlySegment {}\n"
  },
  {
    "path": "crates/commitlog/src/repo/mem.rs",
    "content": "use std::{\n    collections::{btree_map, BTreeMap},\n    fmt, io,\n    sync::{Arc, Mutex, RwLock},\n};\n\nuse crate::repo::{\n    mem::segment::{SharedLock, Storage},\n    Repo,\n};\n\nmod segment;\npub use segment::{ReadOnlySegment, Segment};\n\npub const PAGE_SIZE: usize = 4096;\n\n/// The total capacity of the imaginary storage device.\n///\n/// [Segment]s are allocated from [Memory], which tracks the total space it\n/// has available. [SpaceOnDevice] is shared by each [Segment].\n///\n/// [Segment]s allocate space in [PAGE_SIZE] increments. When space is allocated,\n/// it is deducted from [SpaceOnDevice]. When there is not enough [SpaceOnDevice],\n/// allocating operations will return [io::ErrorKind::StorageFull].\npub type SpaceOnDevice = Arc<Mutex<u64>>;\n\n/// In-memory implementation of [`Repo`].\n#[derive(Clone, Debug)]\npub struct Memory {\n    space: SpaceOnDevice,\n    segments: SharedLock<BTreeMap<u64, SharedLock<Storage>>>,\n}\n\nimpl Memory {\n    pub fn new(total_space: u64) -> Self {\n        Self {\n            space: Arc::new(Mutex::new(total_space)),\n            segments: <_>::default(),\n        }\n    }\n\n    pub fn unlimited() -> Self {\n        Self::new(u64::MAX)\n    }\n}\n\nimpl fmt::Display for Memory {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"<memory>\")\n    }\n}\n\nimpl Repo for Memory {\n    type SegmentWriter = Segment;\n    type SegmentReader = ReadOnlySegment;\n\n    fn create_segment(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        let mut inner = self.segments.write().unwrap();\n        match inner.entry(offset) {\n            btree_map::Entry::Occupied(entry) => {\n                let entry = entry.get();\n                let read_guard = entry.read().unwrap();\n                if read_guard.is_empty() {\n                    Ok(Segment::from_shared(self.space.clone(), entry.clone()))\n                } else {\n                    Err(io::Error::new(\n                        io::ErrorKind::AlreadyExists,\n                        format!(\"segment {offset} already exists\"),\n                    ))\n                }\n            }\n            btree_map::Entry::Vacant(entry) => {\n                let segment = entry.insert(Arc::new(RwLock::new(Storage::new())));\n                Ok(Segment::from_shared(self.space.clone(), segment.clone()))\n            }\n        }\n    }\n\n    fn open_segment_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        let inner = self.segments.read().unwrap();\n        let Some(buf) = inner.get(&offset) else {\n            return Err(io::Error::new(\n                io::ErrorKind::NotFound,\n                format!(\"segment {offset} does not exist\"),\n            ));\n        };\n        Ok(Segment::from_shared(self.space.clone(), buf.clone()))\n    }\n\n    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader> {\n        self.open_segment_writer(offset).map(Into::into)\n    }\n\n    fn remove_segment(&self, offset: u64) -> io::Result<()> {\n        let mut inner = self.segments.write().unwrap();\n        if inner.remove(&offset).is_none() {\n            return Err(io::Error::new(\n                io::ErrorKind::NotFound,\n                format!(\"segment {offset} does not exist\"),\n            ));\n        }\n\n        Ok(())\n    }\n\n    fn compress_segment(&self, _offset: u64) -> io::Result<()> {\n        Ok(())\n    }\n\n    fn existing_offsets(&self) -> io::Result<Vec<u64>> {\n        Ok(self.segments.read().unwrap().keys().copied().collect())\n    }\n}\n\n#[cfg(feature = \"streaming\")]\nmod async_impls {\n    use std::io;\n\n    use crate::{\n        repo::{\n            mem::{Memory, Segment},\n            Repo as _,\n        },\n        stream::AsyncRepo,\n    };\n\n    impl AsyncRepo for Memory {\n        type AsyncSegmentWriter = tokio::io::BufWriter<Segment>;\n        type AsyncSegmentReader = tokio::io::BufReader<Segment>;\n\n        async fn open_segment_reader_async(&self, offset: u64) -> io::Result<Self::AsyncSegmentReader> {\n            self.open_segment_writer(offset).map(tokio::io::BufReader::new)\n        }\n    }\n\n    #[cfg(test)]\n    mod tests {\n        use std::io;\n\n        use pretty_assertions::assert_matches;\n        use tempfile::tempfile;\n        use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncSeek, AsyncSeekExt as _, AsyncWrite, AsyncWriteExt as _};\n\n        use crate::{repo::mem::Segment, tests::helpers::enable_logging};\n\n        async fn read_write_seek(f: &mut (impl AsyncRead + AsyncSeek + AsyncWrite + Unpin)) {\n            enable_logging();\n\n            f.write_all(b\"alonso\").await.unwrap();\n\n            f.seek(io::SeekFrom::Start(0)).await.unwrap();\n            let mut buf = [0; 6];\n            f.read_exact(&mut buf).await.unwrap();\n            assert_eq!(&buf, b\"alonso\");\n\n            f.seek(io::SeekFrom::Start(2)).await.unwrap();\n            let n = f.read(&mut buf).await.unwrap();\n            assert_eq!(n, 4);\n            assert_eq!(&buf[..4], b\"onso\");\n\n            f.seek(io::SeekFrom::Current(-4)).await.unwrap();\n            let n = f.read(&mut buf).await.unwrap();\n            assert_eq!(n, 4);\n            assert_eq!(&buf[..4], b\"onso\");\n\n            f.seek(io::SeekFrom::End(-3)).await.unwrap();\n            let n = f.read(&mut buf).await.unwrap();\n            assert_eq!(n, 3);\n            assert_eq!(&buf[0..3], b\"nso\");\n\n            f.seek(io::SeekFrom::End(4096)).await.unwrap();\n            let n = f.read(&mut buf).await.unwrap();\n            assert_eq!(n, 0);\n        }\n\n        #[tokio::test]\n        async fn std_file_read_write_seek() {\n            let tmp = tempfile().unwrap();\n            read_write_seek(&mut tokio::fs::File::from_std(tmp)).await\n        }\n\n        #[tokio::test]\n        async fn segment_read_write_seek() {\n            read_write_seek(&mut Segment::new(4096)).await\n        }\n\n        #[tokio::test]\n        async fn write_many_pages() {\n            use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _};\n\n            enable_logging();\n\n            let mut segment = Segment::new(4 * 4096);\n\n            let data = [b'y'; 4096];\n            for _ in 0..4 {\n                segment.write_all(&data[..2048]).await.unwrap();\n                segment.write_all(&data[2048..]).await.unwrap();\n            }\n            assert_matches!(\n                segment.write_all(&data[..2048]).await,\n                Err(e) if e.kind() == io::ErrorKind::StorageFull\n            );\n            segment.rewind().await.unwrap();\n\n            let mut buf = [0; 4096];\n            for _ in 0..4 {\n                segment.read_exact(&mut buf).await.unwrap();\n                assert!(buf.iter().all(|&x| x == b'y'));\n            }\n            assert_matches!(\n                segment.read_exact(&mut buf).await,\n                Err(e) if e.kind() == io::ErrorKind::UnexpectedEof\n            );\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io::{Read, Seek, Write};\n\n    use pretty_assertions::assert_matches;\n    use tempfile::tempfile;\n\n    use super::*;\n    use crate::{segment::FileLike as _, tests::helpers::enable_logging};\n\n    fn read_write_seek(f: &mut (impl Read + Seek + Write)) {\n        f.write_all(b\"alonso\").unwrap();\n\n        f.seek(io::SeekFrom::Start(0)).unwrap();\n        let mut buf = [0; 6];\n        f.read_exact(&mut buf).unwrap();\n        assert_eq!(&buf, b\"alonso\");\n\n        f.seek(io::SeekFrom::Start(2)).unwrap();\n        let n = f.read(&mut buf).unwrap();\n        assert_eq!(n, 4);\n        assert_eq!(&buf[..4], b\"onso\");\n\n        f.seek(io::SeekFrom::Current(-4)).unwrap();\n        let n = f.read(&mut buf).unwrap();\n        assert_eq!(n, 4);\n        assert_eq!(&buf[..4], b\"onso\");\n\n        f.seek(io::SeekFrom::End(-3)).unwrap();\n        let n = f.read(&mut buf).unwrap();\n        assert_eq!(n, 3);\n        assert_eq!(&buf[0..3], b\"nso\");\n\n        f.seek(io::SeekFrom::End(4096)).unwrap();\n        let n = f.read(&mut buf).unwrap();\n        assert_eq!(n, 0);\n    }\n\n    #[test]\n    fn segment_read_write_seek() {\n        read_write_seek(&mut Segment::new(4096));\n    }\n\n    #[test]\n    fn std_file_read_write_seek() {\n        read_write_seek(&mut tempfile().unwrap());\n    }\n\n    #[test]\n    fn ftruncate() {\n        enable_logging();\n\n        let mut segment = Segment::new(2 * 4096);\n\n        let data = [b'z'; 512];\n        let mut buf = Vec::with_capacity(4096);\n\n        segment.write_all(&data).unwrap();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(&buf, &data);\n\n        // Extend adds zeroes.\n        segment.ftruncate(42, 1024).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(&buf[..512], &data);\n        assert_eq!(&buf[512..], &[0; 512]);\n\n        // Extend beyond existing page allocates zeroed page.\n        segment.ftruncate(42, 5120).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(&buf[..512], &data);\n        let rest = &buf[512..];\n        assert_eq!(rest.len(), 5120 - 512);\n        assert!(rest.iter().all(|&b| b == 0));\n        assert_eq!(segment.allocated_space(), 8192);\n\n        // Extend beyond available space returns `StorageFull`.\n        assert_matches!(\n            segment.ftruncate(42, 9216),\n            Err(e) if e.kind() == io::ErrorKind::StorageFull\n        );\n\n        // Shrink deallocates pages.\n        segment.ftruncate(42, 512).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, data);\n        assert_eq!(segment.allocated_space(), 4096);\n\n        segment.ftruncate(42, 256).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, &data[..256]);\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    #[test]\n    fn fallocate() {\n        enable_logging();\n\n        let mut segment = Segment::new(8192);\n\n        let data = [b'z'; 512];\n        let mut buf = Vec::with_capacity(4096);\n\n        segment.write_all(&data).unwrap();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, data);\n\n        // Extend within existing page doesn't allocate.\n        segment.fallocate(1024).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, data);\n        assert_eq!(segment.allocated_space(), 4096);\n\n        // Extend beyond page allocates new page.\n        segment.fallocate(5120).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, data);\n        assert_eq!(segment.allocated_space(), 2 * 4096);\n\n        // Extend beyond available space returns `StorageFull`.\n        assert_matches!(\n            segment.fallocate(9216),\n            Err(e) if e.kind() == io::ErrorKind::StorageFull\n        );\n\n        // Shrink does nothing.\n        segment.fallocate(256).unwrap();\n        buf.clear();\n        read_from_start_to_end(&mut segment, &mut buf).unwrap();\n        assert_eq!(buf, data);\n        assert_eq!(segment.allocated_space(), 2 * 4096);\n    }\n\n    #[test]\n    fn write_many_pages() {\n        enable_logging();\n\n        let mut segment = Segment::new(4 * 4096);\n\n        let data = [b'y'; 4096];\n        for _ in 0..4 {\n            segment.write_all(&data[..2048]).unwrap();\n            segment.write_all(&data[2048..]).unwrap();\n        }\n        assert_matches!(\n            segment.write_all(&data[..2048]),\n            Err(e) if e.kind() == io::ErrorKind::StorageFull\n        );\n        segment.rewind().unwrap();\n\n        let mut buf = [0; 4096];\n        for _ in 0..4 {\n            segment.read_exact(&mut buf).unwrap();\n            assert!(buf.iter().all(|&x| x == b'y'));\n        }\n        assert_matches!(\n            segment.read_exact(&mut buf),\n            Err(e) if e.kind() == io::ErrorKind::UnexpectedEof\n        );\n    }\n\n    fn read_from_start_to_end(f: &mut (impl Read + Seek), buf: &mut Vec<u8>) -> io::Result<usize> {\n        f.rewind()?;\n        f.read_to_end(buf)\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/repo/mod.rs",
    "content": "use std::{\n    fmt,\n    io::{self, Seek},\n};\n\nuse log::{debug, warn};\n\nuse crate::{\n    commit::Commit,\n    error,\n    index::{IndexFile, IndexFileMut},\n    segment::{FileLike, Header, Metadata, OffsetIndexWriter, Reader, Writer},\n    Options,\n};\n\npub(crate) mod fs;\n#[cfg(any(test, feature = \"test\"))]\npub mod mem;\n\npub use fs::{Fs, OnNewSegmentFn, SizeOnDisk};\n#[cfg(any(test, feature = \"test\"))]\npub use mem::Memory;\n\npub type TxOffset = u64;\npub type TxOffsetIndexMut = IndexFileMut<TxOffset>;\npub type TxOffsetIndex = IndexFile<TxOffset>;\n\npub trait SegmentLen: io::Seek {\n    /// Determine the length in bytes of the segment.\n    ///\n    /// This method does not rely on metadata `fsync`, and may use up to three\n    /// `seek` operations.\n    ///\n    /// If the method returns successfully, the seek position before the call is\n    /// restored. However, if it returns an error, the seek position is\n    /// unspecified.\n    ///\n    /// The returned length will be the bytes actually written to the segment,\n    /// not the allocated size of the segment (if the `fallocate` feature is\n    /// enabled).\n    //\n    // TODO: Remove trait and replace with `Seek::stream_len` if / when stabilized:\n    // https://github.com/rust-lang/rust/issues/59359\n    fn segment_len(&mut self) -> io::Result<u64> {\n        let old_pos = self.stream_position()?;\n        let len = self.seek(io::SeekFrom::End(0))?;\n\n        // Avoid seeking a third time when we were already at the end of the\n        // stream. The branch is usually way cheaper than a seek operation.\n        if old_pos != len {\n            self.seek(io::SeekFrom::Start(old_pos))?;\n        }\n\n        Ok(len)\n    }\n}\n\npub trait SegmentReader: io::BufRead + SegmentLen + Send + Sync {\n    /// Whether the segment is considered immutable.\n    ///\n    /// Currently, this is true when the segment is compressed.\n    /// [resume_segment_writer] uses this method to indicate that a new segment\n    /// should be created when opening a commitlog.\n    fn sealed(&self) -> bool;\n}\n\npub trait SegmentWriter: FileLike + io::Read + io::Write + SegmentLen + Send + Sync {}\nimpl<T: FileLike + io::Read + io::Write + SegmentLen + Send + Sync> SegmentWriter for T {}\n\n/// A repository of log segments.\n///\n/// This is mainly an internal trait to allow testing against an in-memory\n/// representation.\n///\n/// The [fmt::Display] should provide context about the location of the repo,\n/// e.g. the root directory for a filesystem-based implementation.\npub trait Repo: Clone + fmt::Display {\n    /// The type of log segments managed by this repo, which must behave like a file.\n    type SegmentWriter: SegmentWriter + 'static;\n    type SegmentReader: SegmentReader + 'static;\n\n    /// Create a new segment with the minimum transaction offset `offset`.\n    ///\n    /// This **must** create the segment atomically, and return\n    /// [`io::ErrorKind::AlreadyExists`] if the segment already exists.\n    ///\n    /// It is permissible, however, to successfully return the new segment if\n    /// it is completely empty (i.e. [`create_segment_writer`] did not previously\n    /// succeed in writing the segment header).\n    fn create_segment(&self, offset: u64) -> io::Result<Self::SegmentWriter>;\n\n    /// Open an existing segment at the minimum transaction offset `offset`.\n    ///\n    /// Must return [`io::ErrorKind::NotFound`] if a segment with the given\n    /// `offset` does not exist.\n    ///\n    /// The method does not guarantee that the segment is non-empty -- this case\n    /// will be caught by [`open_segment_reader`].\n    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader>;\n\n    /// Open an existing segment at the minimum transaction offset `offset`.\n    ///\n    /// Must return [`io::ErrorKind::NotFound`] if a segment with the given\n    /// `offset` does not exist.\n    ///\n    /// The method does not guarantee that the segment is non-empty -- this case\n    /// will be caught by [`resume_segment_writer`].\n    fn open_segment_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter>;\n\n    /// Remove the segment at the minimum transaction offset `offset`.\n    ///\n    /// Return [`io::ErrorKind::NotFound`] if no such segment exists.\n    fn remove_segment(&self, offset: u64) -> io::Result<()>;\n\n    /// Compress a segment in storage, marking it as immutable.\n    fn compress_segment(&self, offset: u64) -> io::Result<()>;\n\n    /// Traverse all segments in this repository and return list of their\n    /// offsets, sorted in ascending order.\n    fn existing_offsets(&self) -> io::Result<Vec<u64>>;\n\n    /// Create [`TxOffsetIndexMut`] for the given `offset` or open it if already exist.\n    /// The `cap` parameter is the maximum number of entries in the index.\n    fn create_offset_index(&self, _offset: TxOffset, _cap: u64) -> io::Result<TxOffsetIndexMut> {\n        Err(io::Error::other(\"not implemented\"))\n    }\n\n    /// Remove [`TxOffsetIndexMut`] named with `offset`.\n    fn remove_offset_index(&self, _offset: TxOffset) -> io::Result<()> {\n        Err(io::Error::other(\"not implemented\"))\n    }\n\n    /// Get [`TxOffsetIndex`] for the given `offset`.\n    fn get_offset_index(&self, _offset: TxOffset) -> io::Result<TxOffsetIndex> {\n        Err(io::Error::other(\"not implemented\"))\n    }\n}\n\nimpl<T: Repo> Repo for &T {\n    type SegmentWriter = T::SegmentWriter;\n    type SegmentReader = T::SegmentReader;\n\n    fn create_segment(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        T::create_segment(self, offset)\n    }\n\n    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader> {\n        T::open_segment_reader(self, offset)\n    }\n\n    fn open_segment_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        T::open_segment_writer(self, offset)\n    }\n\n    fn remove_segment(&self, offset: u64) -> io::Result<()> {\n        T::remove_segment(self, offset)\n    }\n\n    fn compress_segment(&self, offset: u64) -> io::Result<()> {\n        T::compress_segment(self, offset)\n    }\n\n    fn existing_offsets(&self) -> io::Result<Vec<u64>> {\n        T::existing_offsets(self)\n    }\n\n    fn create_offset_index(&self, offset: TxOffset, cap: u64) -> io::Result<TxOffsetIndexMut> {\n        T::create_offset_index(self, offset, cap)\n    }\n\n    /// Remove [`TxOffsetIndexMut`] named with `offset`.\n    fn remove_offset_index(&self, offset: TxOffset) -> io::Result<()> {\n        T::remove_offset_index(self, offset)\n    }\n\n    /// Get [`TxOffsetIndex`] for the given `offset`.\n    fn get_offset_index(&self, offset: TxOffset) -> io::Result<TxOffsetIndex> {\n        T::get_offset_index(self, offset)\n    }\n}\n\nimpl<T: SegmentLen> SegmentLen for io::BufReader<T> {\n    fn segment_len(&mut self) -> io::Result<u64> {\n        SegmentLen::segment_len(self.get_mut())\n    }\n}\n\npub(crate) fn create_offset_index_writer<R: Repo>(repo: &R, offset: u64, opts: Options) -> Option<OffsetIndexWriter> {\n    repo.create_offset_index(offset, opts.offset_index_len())\n        .map(|index| OffsetIndexWriter::new(index, opts))\n        .map_err(|e| {\n            warn!(\"failed to get offset index for segment {offset}: {e}\");\n        })\n        .ok()\n}\n\n/// Create a new segment [`Writer`] with `offset`.\n///\n/// Immediately attempts to write the segment header with the supplied\n/// `log_format_version`.\n///\n/// If the segment already exists, [`io::ErrorKind::AlreadyExists`] is returned.\npub fn create_segment_writer<R: Repo>(\n    repo: &R,\n    opts: Options,\n    epoch: u64,\n    offset: u64,\n) -> io::Result<Writer<R::SegmentWriter>> {\n    let mut storage = repo.create_segment(offset)?;\n    // Ensure we have enough space for this segment.\n    fallocate(&mut storage, &opts)?;\n    Header {\n        log_format_version: opts.log_format_version,\n        checksum_algorithm: Commit::CHECKSUM_ALGORITHM,\n    }\n    .write(&mut storage)?;\n    storage.fsync()?;\n\n    Ok(Writer {\n        commit: Commit {\n            min_tx_offset: offset,\n            n: 0,\n            records: Vec::new(),\n            epoch,\n        },\n        inner: io::BufWriter::with_capacity(opts.write_buffer_size, storage),\n\n        min_tx_offset: offset,\n        bytes_written: Header::LEN as u64,\n\n        offset_index_head: create_offset_index_writer(repo, offset, opts),\n    })\n}\n\n/// Open the existing segment at `offset` for writing.\n///\n/// This will traverse the segment in order to find the offset of the next\n/// commit to write to it, which may fail for various reasons.\n///\n/// If the traversal is successful, the segment header is checked against the\n/// `max_log_format_version`, and [`io::ErrorKind::InvalidData`] is returned if\n/// the segment's log format version is greater than the given value. Likewise\n/// if the checksum algorithm stored in the segment header cannot be handled\n/// by this crate.\n///\n/// If only a (non-empty) prefix of the segment could be read due to a failure\n/// to decode a [`Commit`], the segment [`Metadata`] read up to the faulty\n/// commit is returned in an `Err`. In this case, a new segment should be\n/// created for writing.\npub fn resume_segment_writer<R: Repo>(\n    repo: &R,\n    opts: Options,\n    offset: u64,\n) -> io::Result<Result<Writer<R::SegmentWriter>, Metadata>> {\n    let mut reader = repo.open_segment_reader(offset)?;\n    let offset_index = repo.get_offset_index(offset).ok();\n    let meta = match Metadata::extract(offset, &mut reader, offset_index.as_ref()) {\n        Err(error::SegmentMetadata::InvalidCommit { sofar, source }) => {\n            warn!(\"invalid commit in segment {offset}: {source}\");\n            debug!(\"sofar={sofar:?}\");\n            return Ok(Err(sofar));\n        }\n        Err(error::SegmentMetadata::Io(e)) => return Err(e),\n        Ok(meta) => meta,\n    };\n    meta.header\n        .ensure_compatible(opts.log_format_version, Commit::CHECKSUM_ALGORITHM)\n        .map_err(|msg| io::Error::new(io::ErrorKind::InvalidData, msg))?;\n    // When resuming, the log format version must be equal.\n    if meta.header.log_format_version != opts.log_format_version {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"log format version mismatch: current={} segment={}\",\n                opts.log_format_version, meta.header.log_format_version\n            ),\n        ));\n    }\n\n    if reader.sealed() {\n        Ok(Err(meta))\n    } else {\n        let Metadata {\n            header: _,\n            tx_range,\n            size_in_bytes,\n            max_epoch,\n            max_commit_offset: _,\n            max_commit: _,\n        } = meta;\n        let mut writer = repo.open_segment_writer(offset)?;\n        // Ensure we have enough space for this segment.\n        // The segment could have been created without the `fallocate` feature\n        // enabled, so we call this here again to ensure writes can't fail due\n        // to ENOSPC.\n        fallocate(&mut writer, &opts)?;\n        // We use `O_APPEND`, but make the file offset consistent regardless.\n        writer.seek(io::SeekFrom::End(0))?;\n\n        Ok(Ok(Writer {\n            commit: Commit {\n                min_tx_offset: tx_range.end,\n                n: 0,\n                records: Vec::new(),\n                epoch: max_epoch,\n            },\n            inner: io::BufWriter::new(writer),\n\n            min_tx_offset: tx_range.start,\n            bytes_written: size_in_bytes,\n\n            offset_index_head: create_offset_index_writer(repo, offset, opts),\n        }))\n    }\n}\n\n/// Open the existing segment at `offset` for reading.\n///\n/// Unlike [`resume_segment_writer`], this does not traverse the segment. It\n/// does, however, attempt to read the segment header and checks that the log\n/// format version and checksum algorithm are compatible.\npub fn open_segment_reader<R: Repo>(\n    repo: &R,\n    max_log_format_version: u8,\n    offset: u64,\n) -> io::Result<Reader<R::SegmentReader>> {\n    debug!(\"open segment reader at {offset}\");\n    let storage = repo.open_segment_reader(offset)?;\n    Reader::new(max_log_format_version, offset, storage)\n}\n\n/// Allocate [Options::max_segment_size] of space for [FileLike]\n/// if the `fallocate` feature is enabled,\n/// and [Options::preallocate_segments] is `true`.\n///\n/// No-op otherwise.\n#[inline]\npub(crate) fn fallocate(_f: &mut impl FileLike, _opts: &Options) -> io::Result<()> {\n    #[cfg(feature = \"fallocate\")]\n    if _opts.preallocate_segments {\n        _f.fallocate(_opts.max_segment_size)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/commitlog/src/segment.rs",
    "content": "use std::{\n    fs::File,\n    io::{self, BufWriter, ErrorKind, SeekFrom, Write as _},\n    num::NonZeroU64,\n    ops::Range,\n};\n\nuse log::{debug, warn};\n\nuse crate::{\n    commit::{self, Commit, StoredCommit},\n    error,\n    index::{IndexError, IndexFileMut},\n    payload::Encode,\n    repo::{TxOffset, TxOffsetIndex, TxOffsetIndexMut},\n    Options,\n};\n\npub const MAGIC: [u8; 6] = [b'(', b'd', b's', b')', b'^', b'2'];\n\npub const DEFAULT_LOG_FORMAT_VERSION: u8 = 1;\npub const DEFAULT_CHECKSUM_ALGORITHM: u8 = CHECKSUM_ALGORITHM_CRC32C;\n\npub const CHECKSUM_ALGORITHM_CRC32C: u8 = 0;\npub const CHECKSUM_CRC32C_LEN: usize = 4;\n\n/// Lookup table for checksum length, index is [`Header::checksum_algorithm`].\n// Supported algorithms must be numbered consecutively!\npub const CHECKSUM_LEN: [usize; 1] = [CHECKSUM_CRC32C_LEN];\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct Header {\n    pub log_format_version: u8,\n    pub checksum_algorithm: u8,\n}\n\nimpl Header {\n    pub const LEN: usize = MAGIC.len() + /* log_format_version + checksum_algorithm + reserved + reserved */ 4;\n\n    pub fn write<W: io::Write>(&self, mut out: W) -> io::Result<()> {\n        out.write_all(&MAGIC)?;\n        out.write_all(&[self.log_format_version, self.checksum_algorithm, 0, 0])?;\n\n        Ok(())\n    }\n\n    pub fn decode<R: io::Read>(mut read: R) -> io::Result<Self> {\n        let mut buf = [0; Self::LEN];\n        read.read_exact(&mut buf)?;\n\n        if !buf.starts_with(&MAGIC) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"segment header does not start with magic\",\n            ));\n        }\n\n        Ok(Self {\n            log_format_version: buf[MAGIC.len()],\n            checksum_algorithm: buf[MAGIC.len() + 1],\n        })\n    }\n\n    pub fn ensure_compatible(&self, max_log_format_version: u8, checksum_algorithm: u8) -> Result<(), String> {\n        if self.log_format_version > max_log_format_version {\n            return Err(format!(\"unsupported log format version: {}\", self.log_format_version));\n        }\n        if self.checksum_algorithm != checksum_algorithm {\n            return Err(format!(\"unsupported checksum algorithm: {}\", self.checksum_algorithm));\n        }\n\n        Ok(())\n    }\n}\n\nimpl Default for Header {\n    fn default() -> Self {\n        Self {\n            log_format_version: DEFAULT_LOG_FORMAT_VERSION,\n            checksum_algorithm: DEFAULT_CHECKSUM_ALGORITHM,\n        }\n    }\n}\n\n/// Metadata about a [`Commit`] which was successfully written via [`Writer::commit`].\n#[derive(Debug, PartialEq)]\npub struct Committed {\n    /// The range of transaction offsets included in the commit.\n    pub tx_range: Range<u64>,\n    /// The crc32 checksum of the commit's serialized form,\n    /// as written to the commitlog.\n    pub checksum: u32,\n}\n\n#[derive(Debug)]\npub struct Writer<W: io::Write> {\n    pub(crate) commit: Commit,\n    pub(crate) inner: BufWriter<W>,\n\n    pub(crate) min_tx_offset: u64,\n    pub(crate) bytes_written: u64,\n\n    pub(crate) offset_index_head: Option<OffsetIndexWriter>,\n}\n\nimpl<W: io::Write> Writer<W> {\n    pub fn commit<T: Into<Transaction<U>>, U: Encode>(\n        &mut self,\n        transactions: impl IntoIterator<Item = T>,\n    ) -> io::Result<Option<Committed>> {\n        for tx in transactions {\n            let tx = tx.into();\n            let expected_offset = self.commit.min_tx_offset + self.commit.n as u64;\n            if tx.offset != expected_offset {\n                self.commit.n = 0;\n                self.commit.records.clear();\n\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"invalid transaction offset {}, expected {}\", tx.offset, expected_offset),\n                ));\n            }\n            assert!(\n                self.commit.n < u16::MAX,\n                \"maximum number of transactions in a single commit exceeded\"\n            );\n            self.commit.n += 1;\n            tx.txdata.encode_record(&mut self.commit.records);\n        }\n\n        if self.commit.n == 0 {\n            return Ok(None);\n        }\n\n        let checksum = self\n            .commit\n            .write(&mut self.inner)\n            // Panic here as we don't know how much of the commit has been\n            // written (if anything). Further commits would leave corrupted data\n            // in the log.\n            .unwrap_or_else(|e| panic!(\"failed to write commit {}: {:#}\", self.commit.min_tx_offset, e));\n        let commit_len = self.commit.encoded_len() as u64;\n\n        if let Some(index) = self.offset_index_head.as_mut() {\n            let _ = index\n                .append_after_commit(self.commit.min_tx_offset, self.bytes_written, commit_len)\n                .inspect_err(|e| debug!(\"failed to append to offset index: {e}\"));\n        }\n\n        let tx_range_start = self.commit.min_tx_offset;\n\n        self.bytes_written += commit_len;\n        self.commit.min_tx_offset += self.commit.n as u64;\n        self.commit.n = 0;\n        self.commit.records.clear();\n\n        Ok(Some(Committed {\n            tx_range: tx_range_start..self.commit.min_tx_offset,\n            checksum,\n        }))\n    }\n\n    pub fn flush(&mut self) -> io::Result<()> {\n        self.inner.flush()\n    }\n\n    /// Get the current epoch.\n    pub fn epoch(&self) -> u64 {\n        self.commit.epoch\n    }\n\n    /// Update the epoch.\n    ///\n    /// The caller must ensure that:\n    ///\n    /// - The new epoch is greater than the current epoch.\n    /// - [`Self::commit`] has been called as appropriate.\n    ///\n    pub fn set_epoch(&mut self, epoch: u64) {\n        self.commit.epoch = epoch;\n    }\n\n    /// The smallest transaction offset in this segment.\n    pub fn min_tx_offset(&self) -> u64 {\n        self.min_tx_offset\n    }\n\n    /// The next transaction offset to be written if [`Self::commit`] was called.\n    pub fn next_tx_offset(&self) -> u64 {\n        self.commit.min_tx_offset\n    }\n\n    /// `true` if the segment contains no commits.\n    ///\n    /// The segment will, however, contain a header. This thus violates the\n    /// convention that `is_empty == (len == 0)`.\n    pub fn is_empty(&self) -> bool {\n        self.bytes_written <= Header::LEN as u64\n    }\n\n    /// Number of bytes written to this segment, including the header.\n    pub fn len(&self) -> u64 {\n        self.bytes_written\n    }\n}\n\npub trait FileLike {\n    fn fsync(&mut self) -> io::Result<()>;\n    fn ftruncate(&mut self, tx_offset: u64, size: u64) -> io::Result<()>;\n    /// Allocate space for at least `size` bytes in the [FileLike].\n    ///\n    /// The allocated space shall not contain zero bytes, and shall not modify\n    /// the apparent size of the file (as reported by `stat`).\n    ///\n    /// No-op if `size` is smaller than the already allocated space.\n    ///\n    /// In other words, the method shall behave like:\n    ///\n    /// ```ignore\n    /// fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, size)\n    /// ```\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()>;\n}\n\nimpl FileLike for File {\n    fn fsync(&mut self) -> io::Result<()> {\n        self.sync_data()\n    }\n\n    fn ftruncate(&mut self, _tx_offset: u64, size: u64) -> io::Result<()> {\n        self.set_len(size)\n    }\n\n    #[cfg(all(feature = \"fallocate\", target_os = \"linux\"))]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        use nix::fcntl::{fallocate, FallocateFlags};\n\n        fallocate(self, FallocateFlags::FALLOC_FL_KEEP_SIZE, 0, size as _)?;\n        Ok(())\n    }\n\n    // Fail compilation if `fallocate` is enabled but not supported.\n    #[cfg(all(feature = \"fallocate\", not(target_os = \"linux\"), not(any(test, feature = \"test\"))))]\n    compile_error!(\"`fallocate(2)` is not available on this platform\");\n\n    // No-op if `fallocate` is enabled, unsupported, but this is a test build.\n    //\n    // If it's a test build, we may want to run `fallocate` semantics against\n    // an in-memory backend (on any platform). Hence, we need the method to be\n    // present.\n    #[cfg(all(feature = \"fallocate\", not(target_os = \"linux\"), any(test, feature = \"test\")))]\n    fn fallocate(&mut self, _: u64) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl<W: io::Write + FileLike> FileLike for BufWriter<W> {\n    fn fsync(&mut self) -> io::Result<()> {\n        self.get_mut().fsync()\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, size: u64) -> io::Result<()> {\n        self.get_mut().ftruncate(tx_offset, size)\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        self.get_mut().fallocate(size)\n    }\n}\n\nimpl<W: io::Write + FileLike> FileLike for Writer<W> {\n    fn fsync(&mut self) -> io::Result<()> {\n        self.inner.fsync()?;\n        self.offset_index_head.as_mut().map(|index| index.fsync());\n        Ok(())\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, size: u64) -> io::Result<()> {\n        self.inner.ftruncate(tx_offset, size)?;\n        self.offset_index_head\n            .as_mut()\n            .map(|index| index.ftruncate(tx_offset, size));\n        Ok(())\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        self.inner.fallocate(size)\n    }\n}\n\n#[derive(Debug)]\npub struct OffsetIndexWriter {\n    pub(crate) head: TxOffsetIndexMut,\n\n    require_segment_fsync: bool,\n    min_write_interval: NonZeroU64,\n\n    pub(crate) candidate_min_tx_offset: TxOffset,\n    pub(crate) candidate_byte_offset: u64,\n    pub(crate) bytes_since_last_index: u64,\n}\n\nimpl OffsetIndexWriter {\n    pub fn new(head: TxOffsetIndexMut, opts: Options) -> Self {\n        OffsetIndexWriter {\n            head,\n            require_segment_fsync: opts.offset_index_require_segment_fsync,\n            min_write_interval: opts.offset_index_interval_bytes,\n            candidate_min_tx_offset: TxOffset::default(),\n            candidate_byte_offset: 0,\n            bytes_since_last_index: 0,\n        }\n    }\n\n    fn reset(&mut self) {\n        self.candidate_byte_offset = 0;\n        self.candidate_min_tx_offset = TxOffset::default();\n        self.bytes_since_last_index = 0;\n    }\n\n    /// Either append to index or save offsets to append at future fsync\n    pub fn append_after_commit(\n        &mut self,\n        min_tx_offset: TxOffset,\n        byte_offset: u64,\n        commit_len: u64,\n    ) -> Result<(), IndexError> {\n        self.bytes_since_last_index += commit_len;\n\n        if self.candidate_min_tx_offset == 0 {\n            self.candidate_byte_offset = byte_offset;\n            self.candidate_min_tx_offset = min_tx_offset;\n        }\n\n        if !self.require_segment_fsync {\n            self.append_internal()?;\n        }\n\n        Ok(())\n    }\n\n    fn append_internal(&mut self) -> Result<(), IndexError> {\n        // If the candidate offset is zero, there has not been a commit since the last offset entry\n        if self.candidate_min_tx_offset == 0 {\n            return Ok(());\n        }\n\n        if self.bytes_since_last_index < self.min_write_interval.get() {\n            return Ok(());\n        }\n\n        self.head\n            .append(self.candidate_min_tx_offset, self.candidate_byte_offset)?;\n        self.head.async_flush()?;\n        self.reset();\n\n        Ok(())\n    }\n}\n\nimpl FileLike for OffsetIndexWriter {\n    /// Must be called via SegmentWriter::fsync\n    fn fsync(&mut self) -> io::Result<()> {\n        let _ = self.append_internal().map_err(|e| {\n            warn!(\"failed to append to offset index: {e:?}\");\n        });\n        let _ = self\n            .head\n            .async_flush()\n            .map_err(|e| warn!(\"failed to flush offset index: {e:?}\"));\n        Ok(())\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, _size: u64) -> io::Result<()> {\n        self.reset();\n        self.head\n            .truncate(tx_offset)\n            .inspect_err(|e| {\n                warn!(\"failed to truncate offset index at {tx_offset}: {e:?}\");\n            })\n            .ok();\n        Ok(())\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, _: u64) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nimpl FileLike for IndexFileMut<TxOffset> {\n    fn fsync(&mut self) -> io::Result<()> {\n        self.async_flush()\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, _size: u64) -> io::Result<()> {\n        self.truncate(tx_offset)\n            .map_err(|e| io::Error::other(format!(\"failed to truncate offset index at {tx_offset}: {e:?}\")))\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, _: u64) -> io::Result<()> {\n        Ok(())\n    }\n}\n\n#[derive(Debug)]\npub struct Reader<R> {\n    pub header: Header,\n    pub min_tx_offset: u64,\n    inner: R,\n}\n\nimpl<R: io::Read + io::Seek> Reader<R> {\n    pub fn new(max_log_format_version: u8, min_tx_offset: u64, mut inner: R) -> io::Result<Self> {\n        let header = Header::decode(&mut inner)?;\n        header\n            .ensure_compatible(max_log_format_version, Commit::CHECKSUM_ALGORITHM)\n            .map_err(|msg| io::Error::new(io::ErrorKind::InvalidData, msg))?;\n\n        Ok(Self {\n            header,\n            min_tx_offset,\n            inner,\n        })\n    }\n}\n\nimpl<R: io::BufRead + io::Seek> Reader<R> {\n    pub fn commits(self) -> Commits<R> {\n        Commits {\n            header: self.header,\n            reader: self.inner,\n        }\n    }\n\n    pub fn seek_to_offset(&mut self, index_file: &TxOffsetIndex, start_tx_offset: u64) -> Result<u64, IndexError> {\n        seek_to_offset(&mut self.inner, index_file, start_tx_offset)\n    }\n\n    #[cfg(test)]\n    pub fn transactions<'a, D>(self, de: &'a D) -> impl Iterator<Item = Result<Transaction<D::Record>, D::Error>> + 'a\n    where\n        D: crate::Decoder,\n        D::Error: From<io::Error>,\n        R: 'a,\n    {\n        use itertools::Itertools as _;\n\n        self.commits()\n            .with_log_format_version()\n            .map(|x| x.map_err(Into::into))\n            .map_ok(move |(version, commit)| {\n                let start = commit.min_tx_offset;\n                commit.into_transactions(version, start, de)\n            })\n            .flatten_ok()\n            .map(|x| x.and_then(|y| y))\n    }\n\n    #[cfg(test)]\n    pub(crate) fn metadata(self) -> Result<Metadata, error::SegmentMetadata> {\n        Metadata::with_header(self.min_tx_offset, self.header, self.inner, None)\n    }\n}\n\n/// Advances the `segment` reader to the position corresponding to the `start_tx_offset`\n/// using the `index_file` for efficient seeking.\n///\n/// Input:\n/// - `segment` - segment reader\n/// - `min_tx_offset` - minimum transaction offset in the segment\n/// - `start_tx_offset` - transaction offset to advance to\n///\n/// Returns the byte position `segment` is at after seeking.\npub fn seek_to_offset<R: io::Read + io::Seek>(\n    mut segment: &mut R,\n    index_file: &TxOffsetIndex,\n    start_tx_offset: u64,\n) -> Result<u64, IndexError> {\n    let (index_key, byte_offset) = index_file.key_lookup(start_tx_offset)?;\n\n    // If the index_key is 0, it means the index file is empty, return error without seeking\n    if index_key == 0 {\n        return Err(IndexError::KeyNotFound);\n    }\n    debug!(\"index lookup for key={start_tx_offset}: found key={index_key} at byte-offset={byte_offset}\");\n    // returned `index_key` should never be greater than `start_tx_offset`\n    debug_assert!(index_key <= start_tx_offset);\n\n    // Check if the offset index is pointing to the right commit.\n    let hdr = validate_commit_header(&mut segment, byte_offset)?;\n    if hdr.min_tx_offset == index_key {\n        // Advance the segment Seek if expected commit is found.\n        segment.seek(SeekFrom::Start(byte_offset))\n    } else {\n        Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"mismatched key in offset index file\",\n        ))\n    }\n    .map_err(Into::into)\n}\n\n/// Try to extract the commit header from the asked position without advancing seek.\n/// `IndexFileMut` fsync asynchoronously, which makes it important for reader to verify its entry\npub fn validate_commit_header<Reader: io::Read + io::Seek>(\n    mut reader: &mut Reader,\n    byte_offset: u64,\n) -> io::Result<commit::Header> {\n    let pos = reader.stream_position()?;\n    reader.seek(SeekFrom::Start(byte_offset))?;\n\n    let hdr = commit::Header::decode(&mut reader)\n        .and_then(|hdr| hdr.ok_or_else(|| io::Error::new(ErrorKind::UnexpectedEof, \"unexpected EOF\")));\n\n    // Restore the original position\n    reader.seek(SeekFrom::Start(pos))?;\n\n    hdr\n}\n\n/// Pair of transaction offset and payload.\n///\n/// Created by iterators which \"flatten\" commits into individual transaction\n/// records.\n#[derive(Debug, PartialEq)]\npub struct Transaction<T> {\n    /// The offset of this transaction relative to the start of the log.\n    pub offset: u64,\n    /// The transaction payload.\n    pub txdata: T,\n}\n\nimpl<T> From<(u64, T)> for Transaction<T> {\n    fn from((offset, txdata): (u64, T)) -> Self {\n        Self { offset, txdata }\n    }\n}\n\npub struct Commits<R> {\n    pub header: Header,\n    reader: R,\n}\n\nimpl<R: io::BufRead> Iterator for Commits<R> {\n    type Item = io::Result<StoredCommit>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        StoredCommit::decode_internal(&mut self.reader, self.header.log_format_version).transpose()\n    }\n}\n\n#[cfg(test)]\nimpl<R: io::BufRead> Commits<R> {\n    pub fn with_log_format_version(self) -> impl Iterator<Item = io::Result<(u8, StoredCommit)>> {\n        CommitsWithVersion { inner: self }\n    }\n}\n\n#[cfg(test)]\nstruct CommitsWithVersion<R> {\n    inner: Commits<R>,\n}\n\n#[cfg(test)]\nimpl<R: io::BufRead> Iterator for CommitsWithVersion<R> {\n    type Item = io::Result<(u8, StoredCommit)>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let next = self.inner.next()?;\n        match next {\n            Ok(commit) => {\n                let version = self.inner.header.log_format_version;\n                Some(Ok((version, commit)))\n            }\n            Err(e) => Some(Err(e)),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Metadata {\n    /// The segment header.\n    pub header: Header,\n    /// The range of transactions contained in the segment.\n    pub tx_range: Range<u64>,\n    /// The size of the segment.\n    pub size_in_bytes: u64,\n    /// The largest epoch found in the segment.\n    pub max_epoch: u64,\n    /// The latest commit found in the segment.\n    ///\n    /// The value is the `min_tx_offset` of the commit, i.e.\n    /// `max_commit_offset..tx_range.end` is the range of\n    /// transactions contained in it.\n    pub max_commit_offset: u64,\n    pub max_commit: Option<commit::Metadata>,\n}\n\nimpl Metadata {\n    /// Reads and validates metadata from a segment.\n    /// It will look for last commit index offset and then traverse the segment\n    ///\n    /// Determines `max_tx_offset`, `size_in_bytes`, and `max_epoch` from the segment.\n    pub(crate) fn extract<R: io::Read + io::Seek>(\n        min_tx_offset: TxOffset,\n        mut reader: R,\n        offset_index: Option<&TxOffsetIndex>,\n    ) -> Result<Self, error::SegmentMetadata> {\n        let header = Header::decode(&mut reader)?;\n        Self::with_header(min_tx_offset, header, reader, offset_index)\n    }\n\n    fn with_header<R: io::Read + io::Seek>(\n        min_tx_offset: u64,\n        header: Header,\n        mut reader: R,\n        offset_index: Option<&TxOffsetIndex>,\n    ) -> Result<Self, error::SegmentMetadata> {\n        let mut sofar = offset_index\n            .and_then(|index| Self::find_valid_indexed_commit(min_tx_offset, header, &mut reader, index).ok())\n            .unwrap_or_else(|| Self {\n                header,\n                tx_range: Range {\n                    start: min_tx_offset,\n                    end: min_tx_offset,\n                },\n                size_in_bytes: Header::LEN as u64,\n                max_epoch: u64::default(),\n                max_commit_offset: min_tx_offset,\n                max_commit: None,\n            });\n\n        reader.seek(SeekFrom::Start(sofar.size_in_bytes))?;\n\n        fn commit_meta<R: io::Read>(\n            reader: &mut R,\n            sofar: &Metadata,\n        ) -> Result<Option<commit::Metadata>, error::SegmentMetadata> {\n            commit::Metadata::extract(reader).map_err(|e| {\n                if matches!(e.kind(), io::ErrorKind::InvalidData | io::ErrorKind::UnexpectedEof) {\n                    error::SegmentMetadata::InvalidCommit {\n                        sofar: sofar.clone(),\n                        source: e,\n                    }\n                } else {\n                    e.into()\n                }\n            })\n        }\n        while let Some(commit) = commit_meta(&mut reader, &sofar)? {\n            debug!(\"commit::{commit:?}\");\n            if commit.tx_range.start != sofar.tx_range.end {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"out-of-order offset: expected={} actual={}\",\n                        sofar.tx_range.end, commit.tx_range.start,\n                    ),\n                )\n                .into());\n            }\n            sofar.tx_range.end = commit.tx_range.end;\n            sofar.size_in_bytes += commit.size_in_bytes;\n            // TODO: Should it be an error to encounter an epoch going backwards?\n            sofar.max_epoch = commit.epoch.max(sofar.max_epoch);\n            sofar.max_commit_offset = commit.tx_range.start;\n            sofar.max_commit = Some(commit);\n        }\n\n        Ok(sofar)\n    }\n\n    /// Finds the last valid commit in the segment using the offset index.\n    /// It traverses the index in reverse order, starting from the last key.\n    ///\n    /// Returns\n    /// * `Ok((Metadata)` - If a valid commit is found containing the commit, It adds a default\n    ///   header, which should be replaced with the actual header.\n    /// * `Err` - If no valid commit is found or if the index is empty\n    fn find_valid_indexed_commit<R: io::Read + io::Seek>(\n        min_tx_offset: u64,\n        header: Header,\n        reader: &mut R,\n        offset_index: &TxOffsetIndex,\n    ) -> io::Result<Metadata> {\n        let mut candidate_last_key = TxOffset::MAX;\n\n        while let Ok((key, byte_offset)) = offset_index.key_lookup(candidate_last_key) {\n            match Self::validate_commit_at_offset(reader, key, byte_offset) {\n                Ok(commit) => {\n                    return Ok(Metadata {\n                        header,\n                        tx_range: Range {\n                            start: min_tx_offset,\n                            end: commit.tx_range.end,\n                        },\n                        size_in_bytes: byte_offset + commit.size_in_bytes,\n                        max_epoch: commit.epoch,\n                        max_commit_offset: commit.tx_range.start,\n                        max_commit: Some(commit),\n                    });\n                }\n\n                // `TxOffset` at `byte_offset` is not valid, so try with previous entry\n                Err(_) => {\n                    candidate_last_key = key.saturating_sub(1);\n                    if candidate_last_key == 0 {\n                        break;\n                    }\n                }\n            }\n        }\n\n        Err(io::Error::new(\n            ErrorKind::InvalidData,\n            format!(\"No valid commit found in index up to key: {candidate_last_key}\"),\n        ))\n    }\n\n    /// Validates and decodes a commit at `byte_offset` in the segment.\n    ///\n    /// # Returns\n    /// * `Ok(commit::Metadata)` - If a valid commit is found with matching transaction offset\n    /// * `Err` - If commit can't be decoded or has mismatched transaction offset\n    fn validate_commit_at_offset<R: io::Read + io::Seek>(\n        reader: &mut R,\n        tx_offset: TxOffset,\n        byte_offset: u64,\n    ) -> io::Result<commit::Metadata> {\n        reader.seek(SeekFrom::Start(byte_offset))?;\n        let commit = commit::Metadata::extract(reader)?\n            .ok_or_else(|| io::Error::new(ErrorKind::InvalidData, \"failed to decode commit\"))?;\n\n        if commit.tx_range.start != tx_offset {\n            return Err(io::Error::new(\n                ErrorKind::InvalidData,\n                format!(\n                    \"mismatch key in index offset file: expected={} actual={}\",\n                    tx_offset, commit.tx_range.start\n                ),\n            ));\n        }\n\n        Ok(commit)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use itertools::Itertools;\n    use pretty_assertions::assert_matches;\n    use spacetimedb_paths::server::CommitLogDir;\n    use tempfile::tempdir;\n\n    use super::*;\n    use crate::{payload::ArrayDecoder, repo, Options};\n\n    #[test]\n    fn header_roundtrip() {\n        let hdr = Header {\n            log_format_version: 42,\n            checksum_algorithm: 7,\n        };\n\n        let mut buf = [0u8; Header::LEN];\n        hdr.write(&mut &mut buf[..]).unwrap();\n        let h2 = Header::decode(&buf[..]).unwrap();\n\n        assert_eq!(hdr, h2);\n    }\n\n    #[test]\n    fn write_read_roundtrip() {\n        let repo = repo::Memory::unlimited();\n\n        let mut writer = repo::create_segment_writer(&repo, <_>::default(), Commit::DEFAULT_EPOCH, 0).unwrap();\n        writer.commit([(0, [0; 32]), (1, [1; 32]), (2, [2; 32])]).unwrap();\n        writer.flush().unwrap();\n\n        let reader = repo::open_segment_reader(&repo, DEFAULT_LOG_FORMAT_VERSION, 0).unwrap();\n        let header = reader.header;\n        let commit = reader\n            .commits()\n            .next()\n            .expect(\"expected one commit\")\n            .expect(\"unexpected IO\");\n\n        assert_eq!(\n            header,\n            Header {\n                log_format_version: DEFAULT_LOG_FORMAT_VERSION,\n                checksum_algorithm: DEFAULT_CHECKSUM_ALGORITHM\n            }\n        );\n        assert_eq!(commit.min_tx_offset, 0);\n        assert_eq!(commit.records, [[0; 32], [1; 32], [2; 32]].concat());\n    }\n\n    #[test]\n    fn metadata() {\n        let repo = repo::Memory::unlimited();\n\n        let mut writer = repo::create_segment_writer(&repo, <_>::default(), Commit::DEFAULT_EPOCH, 0).unwrap();\n        // Commit 0..2\n        writer.commit([(0, [0; 32]), (1, [0; 32])]).unwrap();\n        // Commit 2..3\n        writer.commit([(2, [1; 32])]).unwrap();\n        // Commit 3..5\n        writer.commit([(3, [2; 32]), (4, [2; 32])]).unwrap();\n\n        writer.flush().unwrap();\n\n        let reader = repo::open_segment_reader(&repo, DEFAULT_LOG_FORMAT_VERSION, 0).unwrap();\n        let Metadata {\n            header,\n            tx_range,\n            size_in_bytes,\n            max_epoch,\n            max_commit_offset,\n            max_commit,\n        } = reader.metadata().unwrap();\n\n        assert_eq!(\n            (\n                header,\n                tx_range,\n                size_in_bytes,\n                max_epoch,\n                max_commit_offset,\n                max_commit.is_some_and(|meta| meta.tx_range == (3..5))\n            ),\n            (\n                Header::default(),\n                0..5,\n                // header + 5 txs + 3 commits\n                (Header::LEN + (5 * 32) + (3 * Commit::FRAMING_LEN)) as u64,\n                Commit::DEFAULT_EPOCH,\n                3,\n                true\n            )\n        );\n    }\n\n    #[test]\n    fn commits() {\n        let repo = repo::Memory::unlimited();\n        let commits = vec![\n            vec![(0, [1; 32]), (1, [2; 32])],\n            vec![(2, [3; 32])],\n            vec![(3, [4; 32]), (4, [5; 32])],\n        ];\n\n        let mut writer = repo::create_segment_writer(&repo, <_>::default(), Commit::DEFAULT_EPOCH, 0).unwrap();\n\n        for commit in &commits {\n            writer.commit(commit.clone()).unwrap();\n        }\n\n        writer.flush().unwrap();\n\n        let reader = repo::open_segment_reader(&repo, DEFAULT_LOG_FORMAT_VERSION, 0).unwrap();\n        let mut commits1 = Vec::with_capacity(commits.len());\n        let mut min_tx_offset = 0;\n        for txs in commits {\n            let n = txs.len();\n            commits1.push(Commit {\n                min_tx_offset,\n                n: n as u16,\n                records: itertools::concat(txs.into_iter().map(|(_, payload)| payload.to_vec())),\n                epoch: 0,\n            });\n            min_tx_offset += n as u64;\n        }\n        let commits2 = reader\n            .commits()\n            .map_ok(Into::into)\n            .collect::<Result<Vec<Commit>, _>>()\n            .unwrap();\n        assert_eq!(commits1, commits2);\n    }\n\n    #[test]\n    fn transactions() {\n        let repo = repo::Memory::unlimited();\n        let commits = vec![\n            vec![(0, [1; 32]), (1, [2; 32])],\n            vec![(2, [3; 32])],\n            vec![(3, [4; 32]), (4, [5; 32])],\n        ];\n\n        let mut writer = repo::create_segment_writer(&repo, <_>::default(), Commit::DEFAULT_EPOCH, 0).unwrap();\n        for commit in &commits {\n            writer.commit(commit.clone()).unwrap();\n        }\n\n        writer.flush().unwrap();\n\n        let reader = repo::open_segment_reader(&repo, DEFAULT_LOG_FORMAT_VERSION, 0).unwrap();\n        let txs = reader\n            .transactions(&ArrayDecoder)\n            .collect::<Result<Vec<_>, _>>()\n            .unwrap();\n        assert_eq!(txs, commits.into_iter().flatten().map(Into::into).collect::<Vec<_>>());\n    }\n\n    #[test]\n    fn next_tx_offset() {\n        let mut writer = Writer {\n            commit: Commit::default(),\n            inner: BufWriter::new(Vec::new()),\n\n            min_tx_offset: 0,\n            bytes_written: 0,\n\n            offset_index_head: None,\n        };\n\n        assert_eq!(0, writer.next_tx_offset());\n        writer.commit([(0, [0; 16])]).unwrap();\n        assert_eq!(1, writer.next_tx_offset());\n        writer.commit([(1, [1; 16])]).unwrap();\n        writer.commit([(2, [1; 16])]).unwrap();\n        assert_eq!(3, writer.next_tx_offset());\n    }\n\n    #[test]\n    fn offset_index_writer_truncates_to_offset() {\n        use spacetimedb_paths::FromPathUnchecked as _;\n\n        let tmp = tempdir().unwrap();\n        let commitlog_dir = CommitLogDir::from_path_unchecked(tmp.path());\n        let index_path = commitlog_dir.index(0);\n        let mut writer = OffsetIndexWriter::new(\n            TxOffsetIndexMut::create_index_file(&index_path, 100).unwrap(),\n            Options {\n                // Ensure we're writing every index entry.\n                offset_index_interval_bytes: 127.try_into().unwrap(),\n                offset_index_require_segment_fsync: false,\n                ..Default::default()\n            },\n        );\n\n        for i in 1..=10 {\n            writer.append_after_commit(i, i * 128, 128).unwrap();\n        }\n        // Ensure all entries have been written.\n        for i in 1..=10 {\n            assert_eq!(writer.head.key_lookup(i).unwrap(), (i, i * 128));\n        }\n\n        // Truncating to any offset in the written range or larger\n        // retains that offset - 1, or the max offset written.\n        for truncate_to in (2..=10u64).rev() {\n            let retained_key = truncate_to.saturating_sub(1).min(10);\n            let retained_val = retained_key * 128;\n            let retained = (retained_key, retained_val);\n\n            writer.ftruncate(truncate_to, rand::random()).unwrap();\n            assert_matches!(\n                writer.head.key_lookup(truncate_to),\n                Ok(x) if x == retained,\n                \"truncate to {truncate_to} should retain {retained:?}\"\n            );\n            // Make sure this also holds after reopen.\n            let index = TxOffsetIndex::open_index_file(&index_path).unwrap();\n            assert_matches!(\n                index.key_lookup(truncate_to),\n                Ok(x) if x == retained,\n                \"truncate to {truncate_to} should retain {retained:?} after reopen\"\n            );\n        }\n\n        // Truncating to 1 leaves no entries in the index\n        writer.ftruncate(1, rand::random()).unwrap();\n        assert_matches!(writer.head.key_lookup(1), Err(IndexError::KeyNotFound));\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/stream/common.rs",
    "content": "use std::{\n    future::Future,\n    io,\n    ops::{Bound, RangeBounds},\n};\n\nuse tokio::io::{AsyncBufRead, AsyncBufReadExt as _, AsyncRead, AsyncReadExt as _, AsyncSeek, AsyncWrite};\n\nuse crate::{commit, repo::Repo};\n\n/// How to convert [`crate::repo::SegmentWriter`]s into async I/O types.\npub trait IntoAsyncWriter {\n    type AsyncWriter: AsyncWrite + AsyncFsync + AsyncLen + Unpin + Send;\n\n    fn into_async_writer(self) -> Self::AsyncWriter;\n}\n\nimpl IntoAsyncWriter for std::fs::File {\n    type AsyncWriter = tokio::io::BufWriter<tokio::fs::File>;\n\n    fn into_async_writer(self) -> Self::AsyncWriter {\n        tokio::io::BufWriter::new(tokio::fs::File::from_std(self))\n    }\n}\n\npub trait AsyncRepo: Repo<SegmentWriter: IntoAsyncWriter<AsyncWriter = Self::AsyncSegmentWriter>> {\n    type AsyncSegmentWriter: AsyncWrite + AsyncLen + AsyncFsync + AsyncLen + Unpin + Send;\n    type AsyncSegmentReader: AsyncBufRead + AsyncLen + Unpin + Send;\n\n    fn open_segment_reader_async(\n        &self,\n        offset: u64,\n    ) -> impl Future<Output = io::Result<Self::AsyncSegmentReader>> + Send;\n}\n\npub trait AsyncFsync {\n    fn fsync(&self) -> impl Future<Output = ()> + Send;\n}\n\nimpl<T: AsyncWrite + AsyncFsync + Send + Sync> AsyncFsync for tokio::io::BufWriter<T> {\n    async fn fsync(&self) {\n        self.get_ref().fsync().await\n    }\n}\n\nimpl AsyncFsync for tokio::fs::File {\n    async fn fsync(&self) {\n        self.sync_data().await.expect(\"fsync failed\")\n    }\n}\n\npub trait AsyncLen: AsyncSeek + Unpin + Send {\n    fn segment_len(&mut self) -> impl Future<Output = io::Result<u64>> + Send\n    where\n        Self: Sized,\n    {\n        async { spacetimedb_fs_utils::compression::segment_len(self).await }\n    }\n}\n\nimpl<T: AsyncWrite + AsyncLen + Send> AsyncLen for tokio::io::BufWriter<T> {\n    async fn segment_len(&mut self) -> io::Result<u64> {\n        self.get_mut().segment_len().await\n    }\n}\n\nimpl<T: AsyncRead + AsyncLen + Send> AsyncLen for tokio::io::BufReader<T> {\n    async fn segment_len(&mut self) -> io::Result<u64> {\n        self.get_mut().segment_len().await\n    }\n}\n\nimpl AsyncLen for tokio::fs::File {}\n\n/// An optionally half-open range.\n///\n/// Can express both `start..=end` and `start..`.\n#[derive(Clone, Copy, Debug)]\npub struct RangeFromMaybeToInclusive {\n    /// The start of the range, inclusive.\n    pub start: u64,\n    /// The end of the range, inclusive, or unbounded if `None`.\n    pub end: Option<u64>,\n}\n\nimpl RangeFromMaybeToInclusive {\n    pub fn from_range_bounds(b: impl RangeBounds<u64>) -> Self {\n        let start = match b.start_bound() {\n            Bound::Unbounded => 0,\n            Bound::Included(start) => *start,\n            Bound::Excluded(start) => start + 1,\n        };\n        let end = match b.end_bound() {\n            Bound::Unbounded => None,\n            Bound::Included(end) => Some(*end),\n            Bound::Excluded(end) => Some(end.saturating_sub(1)),\n        };\n\n        Self { start, end }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.end.is_some_and(|end| end < self.start)\n    }\n\n    pub fn contains(&self, item: &u64) -> bool {\n        item >= &self.start\n            && match &self.end {\n                None => true,\n                Some(end) => item <= end,\n            }\n    }\n}\n\nimpl RangeBounds<u64> for RangeFromMaybeToInclusive {\n    fn start_bound(&self) -> Bound<&u64> {\n        Bound::Included(&self.start)\n    }\n\n    fn end_bound(&self) -> Bound<&u64> {\n        self.end.as_ref().map(Bound::Included).unwrap_or(Bound::Unbounded)\n    }\n}\n\n#[derive(Default)]\npub(super) struct CommitBuf {\n    pub header: [u8; commit::Header::LEN],\n    pub body: Vec<u8>,\n}\n\nimpl CommitBuf {\n    pub fn as_buf(&self) -> impl bytes::Buf + '_ {\n        bytes::Buf::chain(&self.header[..], &self.body[..])\n    }\n\n    pub fn as_reader(&self) -> impl io::Read + '_ {\n        io::Read::chain(&self.header[..], &self.body[..])\n    }\n\n    pub fn filled_len(&self) -> usize {\n        self.header.len() + self.body.len()\n    }\n}\n\npub(super) enum DidReadExact {\n    All,\n    Eof,\n}\n\nimpl DidReadExact {\n    pub fn is_eof(&self) -> bool {\n        matches!(self, Self::Eof)\n    }\n}\n\npub(super) async fn read_exact(src: &mut (impl AsyncRead + Unpin), buf: &mut [u8]) -> io::Result<DidReadExact> {\n    src.read_exact(buf).await.map(|_| DidReadExact::All).or_else(|e| {\n        if e.kind() == io::ErrorKind::UnexpectedEof {\n            Ok(DidReadExact::Eof)\n        } else {\n            Err(e)\n        }\n    })\n}\n\n/// Get a reference to the [`AsyncBufRead`]'s buffer, filling it if necessary.\npub(super) async fn peek_buf(src: &mut (impl AsyncBufRead + Unpin)) -> io::Result<Option<&[u8]>> {\n    let buf = src.fill_buf().await?;\n    Ok(if buf.is_empty() { None } else { Some(buf) })\n}\n"
  },
  {
    "path": "crates/commitlog/src/stream/reader.rs",
    "content": "use std::{io::SeekFrom, ops::RangeBounds};\n\nuse async_stream::try_stream;\nuse bytes::{Buf as _, Bytes};\nuse futures::Stream;\nuse log::{trace, warn};\nuse tokio::{\n    io::{self, AsyncBufRead, AsyncReadExt as _, AsyncSeek, AsyncSeekExt as _},\n    task::spawn_blocking,\n};\nuse tokio_util::io::SyncIoBridge;\n\nuse crate::{\n    commit,\n    error::source_chain,\n    index::IndexError,\n    repo::Repo,\n    segment::{self, seek_to_offset, CHECKSUM_LEN},\n};\n\nuse super::{\n    common::{read_exact, AsyncRepo, CommitBuf},\n    RangeFromMaybeToInclusive,\n};\n\n/// Stream the `range` of transaction offsets from the commitlog in `repo` as\n/// raw commitlog data.\n///\n/// The stream contains segment headers as they are encountered scanning the\n/// `range`.\n///\n/// Only whole [`commit::StoredCommit`]s are yielded, so a `range` that doesn't\n/// fall on commit boundaries may yield extra data.\n///\n/// Only the headers of the source commitlog are inspected (in order to be able\n/// to satisfy the `range` predicate), so no guarantees are made about the\n/// integrity of the log.\n///\n/// If the commitlog is empty, that is does not contain any commits, the\n/// returned stream yields nothing.\npub fn commits<R>(repo: R, range: impl RangeBounds<u64>) -> impl Stream<Item = io::Result<Bytes>>\nwhere\n    R: AsyncRepo + Send + 'static,\n{\n    let mut range = RangeFromMaybeToInclusive::from_range_bounds(range);\n    let retain = move |segments: Vec<_>| retain_range(&segments, range);\n    try_stream! {\n        let segments = repo.existing_offsets().map(retain)?;\n        for segment_offset in segments {\n            if range.start < segment_offset {\n                range.start = segment_offset;\n            }\n            trace!(\"segment: segment={} start={}\", segment_offset, range.start);\n\n            let segment = repo.open_segment_reader_async(segment_offset).await?;\n\n            for await chunk in read_segment(repo.clone(), segment, segment_offset, range) {\n                yield chunk.inspect_err(|e| warn!(\"error reading segment {segment_offset}: {e}\"))?;\n            }\n        }\n    }\n}\n\nfn read_segment(\n    repo: impl Repo + Send + 'static,\n    mut segment: impl AsyncBufRead + AsyncSeek + Unpin + Send + 'static,\n    segment_start: u64,\n    range: RangeFromMaybeToInclusive,\n) -> impl Stream<Item = io::Result<Bytes>> {\n    try_stream! {\n        trace!(\"reading segment {segment_start}\");\n        let (segment_header, segment_header_bytes) = {\n            let mut buf = [0u8; segment::Header::LEN];\n            segment.read_exact(&mut buf).await?;\n            let header = segment::Header::decode(&buf[..])?;\n            (header, Bytes::from_owner(buf))\n        };\n        let mut send_segment_header = Some(segment_header_bytes);\n\n        // Try to seek to the starting offset\n        // if it doesn't fall on the segment boundary.\n        if range.start > segment_start {\n            // Don't send a segment header if we're not reading from the start.\n            send_segment_header = None;\n            segment = spawn_blocking(move || {\n                let mut segment = SyncIoBridge::new(segment);\n                if let Ok(offset_index) = repo.get_offset_index(segment_start) {\n                    trace!(\"seek_to_offset segment={} start={}\", segment_start, range.start);\n                    seek_to_offset(&mut segment, &offset_index, range.start)\n                        .inspect_err(|e| match e {\n                            IndexError::KeyNotFound =>\n                                trace!(\n                                    \"offset not found segment={} offset={}\",\n                                    segment_start, range.start\n                                ),\n                            e => {\n                                warn!(\n                                    \"error reading index segment={} offset={}: {} {}\",\n                                    segment_start,\n                                    range.start,\n                                    e,\n                                    source_chain(&e)\n                                )\n                            }\n                        })\n                        .ok();\n                }\n                segment.into_inner()\n            })\n            .await\n            .unwrap();\n        }\n\n        let checksum_len = CHECKSUM_LEN[segment_header.checksum_algorithm as usize];\n        let mut commit_buf = CommitBuf::default();\n        loop {\n            if read_exact(&mut segment, &mut commit_buf.header).await?.is_eof() {\n                trace!(\"eof reading commit header\");\n                break;\n            }\n            let Some(hdr) = commit::Header::decode(&commit_buf.header[..])? else {\n                warn!(\"all-zeroes commit header\");\n                break;\n            };\n            // Skip the commit if we're not at `range.start`.\n            if hdr.min_tx_offset < range.start {\n                segment.seek(SeekFrom::Current(hdr.len as i64 + checksum_len as i64)).await?;\n            // Stop if we're past the range end.\n            } else if range.end.is_some_and(|end| hdr.min_tx_offset > end) {\n                break\n            } else {\n                commit_buf.body.resize(hdr.len as usize + checksum_len, 0);\n                segment.read_exact(&mut commit_buf.body).await?;\n\n                // Send segment header if not sent already.\n                if let Some(header_bytes) = send_segment_header.take() {\n                    trace!(\"sending segment header\");\n                    yield header_bytes;\n                }\n\n                trace!(\"sending commit {}\", hdr.min_tx_offset);\n                yield commit_buf.as_buf().copy_to_bytes(commit_buf.filled_len());\n            }\n        }\n    }\n}\n\n/// Given a list of (segment) offsets, retain those which fall into the `range`.\npub fn retain_range(offsets: &[u64], range: RangeFromMaybeToInclusive) -> Vec<u64> {\n    if range.is_empty() {\n        return vec![];\n    }\n    offsets\n        .iter()\n        .zip(offsets.iter().skip(1).chain([&u64::MAX]))\n        .filter_map(|(&start, &end)| {\n            let in_start = range.start >= start && range.start < end;\n            (in_start || range.contains(&start)).then_some(start)\n        })\n        .collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n\n    fn retain_range(offsets: &[u64], range: impl RangeBounds<u64>) -> Vec<u64> {\n        super::retain_range(offsets, RangeFromMaybeToInclusive::from_range_bounds(range))\n    }\n\n    #[test]\n    fn test_slice_segments_on_single_commit() {\n        let offsets = vec![0, 10];\n\n        let retained = retain_range(&offsets, 19..=19);\n        assert_eq!(&retained, &[10]);\n    }\n\n    #[test]\n    fn test_slice_segments_on_boundary() {\n        let offsets = vec![0, 10, 20, 30];\n\n        for (i, start) in offsets.iter().enumerate() {\n            let retained = retain_range(&offsets, start..);\n            assert_eq!(&retained, &offsets[i..]);\n        }\n    }\n\n    #[test]\n    fn test_slice_segments_between_boundary() {\n        let offsets = vec![0, 10, 20, 30];\n\n        let ranges = vec![5, 11, 29];\n        for (i, start) in ranges.into_iter().enumerate() {\n            let retained = retain_range(&offsets, start..);\n            assert_eq!(&retained, &offsets[i..]);\n        }\n    }\n\n    #[test]\n    fn test_slice_segments_with_upper_bound() {\n        let offsets = vec![0, 10, 20, 30];\n        let retained = retain_range(&offsets, 11..29);\n        assert_eq!(&retained, &[10, 20]);\n    }\n\n    proptest! {\n        #[test]\n        fn prop_offset_at_or_after_last_segment_yields_last(start in 30u64..) {\n            let offsets = vec![0, 10, 20, 30];\n            let retained = retain_range(&offsets, start..);\n            prop_assert_eq!(&retained, &[30]);\n        }\n\n        #[test]\n        fn prop_empty_input_gives_empty_output(start in any::<u64>()) {\n            let retained = retain_range(&[], start..);\n            prop_assert_eq!(&retained, &[] as &[u64]);\n        }\n\n        #[test]\n        fn prop_empty_range_retains_nothing(start in any::<u64>()) {\n            let offsets = vec![0, 10, 20, 30];\n            let range = start..start;\n            prop_assert!(range.is_empty(), \"expected range to be empty: {range:?}\");\n            let retained = retain_range(&offsets, range);\n            prop_assert_eq!(&retained, &[] as &[u64]);\n        }\n\n        #[test]\n        fn prop_offset_at_or_after_last_with_upper_bound_yields_last(start in 30u64..) {\n            let offsets = vec![0, 10, 20, 30];\n            let retained = retain_range(&offsets, start..start + 16);\n            prop_assert_eq!(&retained, &[30]);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/stream/writer.rs",
    "content": "use std::{\n    io::{self, Seek as _},\n    ops::Range,\n};\n\nuse futures::TryFutureExt;\nuse log::{debug, error, info, trace, warn};\nuse tokio::{\n    io::{AsyncBufRead, AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt},\n    task::spawn_blocking,\n};\n\nuse crate::{\n    commit, error,\n    index::IndexFile,\n    repo::{fallocate, Repo, SegmentLen as _},\n    segment::{self, FileLike, OffsetIndexWriter, CHECKSUM_LEN, DEFAULT_CHECKSUM_ALGORITHM},\n    stream::common::{read_exact, AsyncFsync},\n    Options, StoredCommit, DEFAULT_LOG_FORMAT_VERSION,\n};\n\nuse super::{\n    common::{peek_buf, AsyncLen as _, AsyncRepo, CommitBuf},\n    IntoAsyncWriter,\n};\n\n/// Progress reporting for [`StreamWriter::write_all`].\npub trait Progress {\n    /// Report that the transaction range `tx_range` was written to the\n    /// destination commitlog.\n    ///\n    /// The method is called after each commit written, so should be cheap to\n    /// call and never block. A call does not imply that the commit is already\n    /// flushed to disk.\n    fn range_written(&mut self, tx_range: Range<u64>);\n}\n\nimpl<T: FnMut(Range<u64>)> Progress for T {\n    fn range_written(&mut self, tx_range: Range<u64>) {\n        (self)(tx_range)\n    }\n}\n\n/// Write a raw byte stream of commitlog data to a local commitlog.\n///\n/// Intended for mirroring commitlogs over the network.\n///\n/// The source stream is expected to contain the raw commitlog data, including\n/// segment headers.\n///\n/// Whenever a segment header is encountered in the stream, a new segment is\n/// created locally. The stream data is decoded as a series of [commits],\n/// without inspecting their payload. The checksum of each commit is verified,\n/// and it is checked that the commit offsets are contiguous.\n///\n/// Apart from this **no further validation is performed**, it is assumed that\n/// the source is trusted.\n///\n/// [commits]: crate::commit::StoredCommit\npub struct StreamWriter<R>\nwhere\n    R: AsyncRepo + Send + 'static,\n{\n    repo: R,\n    commitlog_options: Options,\n\n    last_written_tx_range: Option<Range<u64>>,\n    current_segment: Option<CurrentSegment<R::AsyncSegmentWriter>>,\n    commit_buf: CommitBuf,\n}\n\nimpl<R> StreamWriter<R>\nwhere\n    R: AsyncRepo + Send + 'static,\n{\n    /// Create a new [`StreamWriter`] from the commitlog in `repo`.\n    ///\n    /// Opens the latest segment of the commitlog for writing.\n    /// If the commitlog is empty, no segment is created and [`Self::append_all`]\n    /// expects that the source stream starts with a segment header.\n    ///\n    /// The method traverses the most recent segment to ensure it contains valid\n    /// data, and to ensure [`Self::append_all`] can only write consecutive\n    /// commits. The `on_trailing` parameter an be used to trim the segment if\n    /// it contains trailing invalid data (i.e. due to a partial write).\n    pub fn create(repo: R, commitlog_options: Options, on_trailing: OnTrailingData) -> io::Result<Self> {\n        Self::create_and_metadata(repo, commitlog_options, on_trailing).map(|(this, _)| this)\n    }\n\n    /// Like [`Self::create`], create a new [`StreamWriter`]. Additionally\n    /// return the [`segment::Metadata`] of the most recent segment.\n    ///\n    /// The metadata is `None` if the commitlog is empty.\n    pub fn create_and_metadata(\n        repo: R,\n        commitlog_options: Options,\n        on_trailing: OnTrailingData,\n    ) -> io::Result<(Self, Option<segment::Metadata>)> {\n        let Some(last) = repo.existing_offsets()?.pop() else {\n            let this = Self {\n                repo,\n                commitlog_options,\n                last_written_tx_range: None,\n                current_segment: None,\n                commit_buf: <_>::default(),\n            };\n            return Ok((this, None));\n        };\n\n        let mut segment = repo.open_segment_writer(last)?;\n        fallocate(&mut segment, &commitlog_options)?;\n\n        let mut offset_index = repo\n            .get_offset_index(last)\n            .inspect_err(|e| {\n                warn!(\"unable to open offset index for segment {last}: {e}\");\n            })\n            .ok();\n\n        let meta = match segment::Metadata::extract(last, &mut segment, offset_index.as_ref()) {\n            Ok(sofar) => sofar,\n            Err(error::SegmentMetadata::InvalidCommit { sofar, source }) => match on_trailing {\n                OnTrailingData::Error => {\n                    return Err(io::Error::new(io::ErrorKind::InvalidData, source));\n                }\n                OnTrailingData::Trim => {\n                    info!(\"trimming segment {last} after invalid commit: {sofar:?}\");\n                    if let Some(idx) = offset_index.as_mut().map(IndexFile::as_mut) {\n                        idx.ftruncate(sofar.tx_range.end, sofar.size_in_bytes)\n                            .inspect_err(|e| {\n                                error!(\n                                    \"failed to truncate offset index for segment {last} containing trailing data: {e}\"\n                                )\n                            })?;\n                        segment.ftruncate(sofar.tx_range.end, sofar.size_in_bytes)?;\n                        segment.seek(io::SeekFrom::End(0))?;\n                    }\n                    sofar\n                }\n            },\n            Err(error::SegmentMetadata::Io(e)) => Err(e)?,\n        };\n\n        meta.header\n            .ensure_compatible(DEFAULT_LOG_FORMAT_VERSION, DEFAULT_CHECKSUM_ALGORITHM)\n            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;\n\n        let current_segment = CurrentSegment {\n            header: meta.header,\n            segment: segment.into_async_writer(),\n            offset_index: offset_index.map(|index| OffsetIndexWriter::new(index.into(), commitlog_options)),\n        };\n\n        let this = Self {\n            repo,\n            commitlog_options,\n            last_written_tx_range: Some(meta.tx_range.clone()),\n            current_segment: Some(current_segment),\n            commit_buf: <_>::default(),\n        };\n\n        Ok((this, Some(meta)))\n    }\n\n    /// Consume `stream` and append it to the local commitog.\n    ///\n    /// The `stream` should be the suffix after the commitlog already present\n    /// in the local [`Repo`]. The method checks that commit offsets are\n    /// contiguous.\n    ///\n    /// Segments are created whenever the stream yields a segment header.\n    /// If the stream doesn't start with a segment header, the data is appended\n    /// to the latest segment found when the writer was created.\n    ///\n    /// An offset index is maintained locally per segment according to the\n    /// [`Options`] used for [`Self::create`]ing the writer.\n    ///\n    /// Writing data to the commitlog incrementally by calling `append_all`\n    /// repeatedly is supported. However, I/O errors may leave the local\n    /// commitlog in an inconsistent state. To prevent further appends, this\n    /// method consumes `self`, and returns it back if the input `stream` was\n    /// consumed successfully. In case of errors, the caller must re-open the\n    /// writer via [`Self::create`] in order to perform consistency checks.\n    ///\n    /// Segments and their offset indexes are synced to disk when a new\n    /// segment is created while processing the input stream.\n    ///\n    /// The caller should use [`Self::sync_all`] to ensure that if a segment\n    /// remains open after `append_all`, it is synced to disk.\n    pub async fn append_all(\n        mut self,\n        mut stream: impl AsyncBufRead + Unpin,\n        mut progress: impl Progress,\n    ) -> io::Result<Self> {\n        loop {\n            let Some(buf) = peek_buf(&mut stream).await? else {\n                break;\n            };\n\n            let mut current_segment = if buf.starts_with(&segment::MAGIC) {\n                // Ensure the previous segment, if any, is fsync'ed.\n                self.close_current_segment().await?;\n                // Ensure we actually have a valid segment header.\n                let header =\n                    segment::Header::decode(buf).inspect_err(|e| warn!(\"failed to decode segment header: {e}\"))?;\n                trace!(\n                    \"create segment at {}\",\n                    self.last_written_tx_range\n                        .as_ref()\n                        .map(|range| range.end)\n                        .unwrap_or_default()\n                );\n                let (mut segment, index) = spawn_blocking({\n                    let repo = self.repo.clone();\n                    let last_written_tx_range = self.last_written_tx_range.clone();\n                    let commitlog_options = self.commitlog_options;\n                    move || create_segment(repo, last_written_tx_range, commitlog_options)\n                })\n                .await\n                .unwrap()\n                .map(|(segment, index)| (segment.into_async_writer(), index))?;\n\n                segment.write_all(&buf[..segment::Header::LEN]).await?;\n                stream.consume(segment::Header::LEN as _);\n\n                CurrentSegment {\n                    header,\n                    segment,\n                    offset_index: index,\n                }\n            } else {\n                match self.current_segment.take() {\n                    Some(current_segment) => current_segment,\n                    _ => {\n                        return Err(io::Error::new(\n                            io::ErrorKind::InvalidData,\n                            \"no current segment, expected segment header\",\n                        ));\n                    }\n                }\n            };\n\n            // What follows is commits to be written to `current_segment`,\n            // until we encounter EOF or a segment marker.\n            let res = self\n                .append_all_inner(&mut stream, &mut current_segment, &mut progress)\n                .await;\n            // Ensure we flush application buffers (BufWriter).\n            current_segment.segment.flush().await?;\n            let maybe_eof = res?;\n            // Put back segment, so it is available for syncing or closing.\n            self.current_segment = Some(current_segment);\n            match maybe_eof {\n                AppendInnerResult::StreamExhausted => break,\n                AppendInnerResult::SegmentMarker => continue,\n            }\n        }\n\n        Ok(self)\n    }\n\n    /// Flush and sync the currently written-to segment (if any) to disk.\n    ///\n    /// Dropping a [`StreamWriter`] will attempt to invoke this, but any errors\n    /// will not be visible. Also, if the async runtime is already shutting down,\n    /// the task spawned by the [`Drop`] impl may not get a chance to run.\n    pub async fn sync_all(&mut self) -> io::Result<()> {\n        let Some(current_segment) = self.current_segment.as_mut() else {\n            return Ok(());\n        };\n        current_segment.flush_and_sync().await\n    }\n\n    async fn append_all_inner(\n        &mut self,\n        stream: &mut (impl AsyncBufRead + Unpin),\n        current_segment: &mut CurrentSegment<R::AsyncSegmentWriter>,\n        progress: &mut impl Progress,\n    ) -> io::Result<AppendInnerResult> {\n        let mut bytes_written = current_segment\n            .segment\n            .segment_len()\n            .await?\n            // We may not have flushed the segment header yet,\n            // but the offset index needs to be offset by the header length.\n            .max(segment::Header::LEN as _);\n\n        loop {\n            let Some(buf) = peek_buf(stream).await? else {\n                // The stream is exhausted, break the outer loop.\n                trace!(\"eof\");\n                return Ok(AppendInnerResult::StreamExhausted);\n            };\n            if buf.starts_with(&segment::MAGIC) {\n                // New segment, break inner loop.\n                trace!(\"segment marker\");\n                return Ok(AppendInnerResult::SegmentMarker);\n            }\n\n            // Read the header, so we can determine the size of the commit.\n            if read_exact(stream, &mut self.commit_buf.header).await?.is_eof() {\n                return Ok(AppendInnerResult::StreamExhausted);\n            }\n            let Some(commit_header) = commit::Header::decode(&self.commit_buf.header[..])\n                .inspect_err(|e| warn!(\"failed to decode commit header: {e}\"))?\n            else {\n                // Nb. eof handled above.\n                return Err(io::Error::new(io::ErrorKind::InvalidData, \"all-zeroes commit header\"));\n            };\n\n            // Read the rest of the commit.\n            self.commit_buf.body.resize(\n                commit_header.len as usize + CHECKSUM_LEN[current_segment.header.checksum_algorithm as usize],\n                0,\n            );\n            stream.read_exact(&mut self.commit_buf.body).await?;\n            // Decode the commit and verify its checksum.\n            let commit = StoredCommit::decode(self.commit_buf.as_reader())\n                .inspect_err(|e| warn!(\"failed to decode commit: {e}\"))?\n                .expect(\"commit decode cannot return `None` because we already decoded the header\");\n\n            // Check that the commit offset is what we expect.\n            let expected_offset = self\n                .last_written_tx_range\n                .as_ref()\n                .map(|range| range.end)\n                .unwrap_or_default();\n            if commit.min_tx_offset != expected_offset {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"expected commit offset {} but encountered {}\",\n                        expected_offset, commit.min_tx_offset\n                    ),\n                ));\n            }\n            trace!(\"received commit {commit:?}\");\n\n            // Write the commit and report progress.\n            current_segment\n                .segment\n                .write_all_buf(&mut self.commit_buf.as_buf())\n                .await?;\n            let written_range = commit.min_tx_offset..(commit.min_tx_offset + commit.n as u64);\n            self.last_written_tx_range = Some(written_range.clone());\n            progress.range_written(written_range);\n\n            let commit_len = (self.commit_buf.header.len() + self.commit_buf.body.len()) as u64;\n\n            // Update to offset index if we have one.\n            if let Some(offset_index) = current_segment.offset_index.as_mut() {\n                debug!(\n                    \"append_after_commit min_tx_offset={} bytes_written={} commit_len={}\",\n                    commit.min_tx_offset, bytes_written, commit_len\n                );\n                offset_index\n                    .append_after_commit(commit.min_tx_offset, bytes_written, commit_len)\n                    .inspect_err(|e| warn!(\"failed to append to offset index: {e}\"))\n                    .ok();\n            }\n\n            bytes_written += commit_len;\n        }\n    }\n\n    async fn close_current_segment(&mut self) -> io::Result<()> {\n        if let Some(current_segment) = self.current_segment.take() {\n            trace!(\"closing current segment\");\n            current_segment.close().await?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<R> Drop for StreamWriter<R>\nwhere\n    R: AsyncRepo + Send + 'static,\n{\n    fn drop(&mut self) {\n        if let Some(current_segment) = self.current_segment.take() {\n            trace!(\"closing current segment on writer drop\");\n            tokio::spawn(\n                current_segment\n                    .close()\n                    .inspect_err(|e| warn!(\"error closing segment on drop: {e}\")),\n            );\n        }\n    }\n}\n\n/// What to do when [`StreamWriter::create`] detects trailing (invalid) data\n/// in the commitlog.\n#[derive(Default)]\npub enum OnTrailingData {\n    /// Return an error. This is the default.\n    #[default]\n    Error,\n    /// Remove the suffix of the log after the last valid commit.\n    Trim,\n}\n\nenum AppendInnerResult {\n    StreamExhausted,\n    SegmentMarker,\n}\n\nstruct CurrentSegment<W> {\n    header: segment::Header,\n    segment: W,\n    offset_index: Option<OffsetIndexWriter>,\n}\n\nimpl<W: AsyncWriteExt + AsyncFsync + Unpin> CurrentSegment<W> {\n    async fn close(mut self) -> io::Result<()> {\n        self.flush_and_sync().await\n    }\n\n    async fn flush_and_sync(&mut self) -> io::Result<()> {\n        self.segment.flush().await?;\n        self.segment.fsync().await;\n        if let Some(mut index) = self.offset_index.take() {\n            let index = spawn_blocking(move || {\n                index\n                    .fsync()\n                    .inspect_err(|e| warn!(\"offset index fsync failed: {e}\"))\n                    .ok();\n                index\n            })\n            .await\n            .unwrap();\n            self.offset_index = Some(index);\n        }\n\n        Ok(())\n    }\n}\n\n/// Create a new segment at offset `last_written_tx_range.end`.\n///\n/// If the segment file already exists but has a size equal to or smaller than\n/// a segment header, the file is truncated. Otherwise, an already existing\n/// segment is an error.\nfn create_segment<R: Repo>(\n    repo: R,\n    last_written_tx_range: Option<Range<u64>>,\n    commitlog_options: Options,\n) -> io::Result<(R::SegmentWriter, Option<OffsetIndexWriter>)> {\n    let segment_offset = last_written_tx_range\n        .as_ref()\n        .map(|range| range.end)\n        .unwrap_or_default();\n    let mut segment = repo.create_segment(segment_offset).or_else(|e| {\n        if e.kind() == io::ErrorKind::AlreadyExists {\n            trace!(\"segment already exists\");\n            let mut s = repo.open_segment_writer(segment_offset)?;\n            let len = s.segment_len()?;\n            trace!(\"segment len: {len}\");\n            if len <= segment::Header::LEN as _ {\n                trace!(\"overwriting existing segment\");\n                s.ftruncate(0, 0)?;\n                return Ok(s);\n            }\n        }\n\n        Err(e)\n    })?;\n    fallocate(&mut segment, &commitlog_options)?;\n\n    let index_writer = repo\n        .create_offset_index(segment_offset, commitlog_options.offset_index_len())\n        .inspect_err(|e| warn!(\"unable to create offset index segment={segment_offset} err={e:?}\"))\n        .map(|index| OffsetIndexWriter::new(index, commitlog_options))\n        .ok();\n\n    Ok((segment, index_writer))\n}\n"
  },
  {
    "path": "crates/commitlog/src/stream.rs",
    "content": "mod writer;\npub use writer::{OnTrailingData, StreamWriter};\n\nmod reader;\npub use reader::{commits, retain_range};\n\nmod common;\npub use common::{AsyncFsync, AsyncLen, AsyncRepo, IntoAsyncWriter, RangeFromMaybeToInclusive};\n"
  },
  {
    "path": "crates/commitlog/src/tests/bitflip.rs",
    "content": "use std::{\n    fmt,\n    iter::{repeat, successors},\n    num::NonZeroU8,\n    rc::Rc,\n};\n\nuse log::debug;\nuse proptest::{prelude::*, sample::select};\n\nuse crate::{\n    commit, commitlog, error, payload,\n    repo::{self, mem::Segment, Repo},\n    segment,\n    tests::helpers::{enable_logging, fill_log, mem_log},\n    Commit,\n};\n\n/// The serialized length of a commit's crc.\nconst CRC_SIZE: usize = 4;\n/// Max size in bytes of a segment.\nconst MAX_SEGMENT_SIZE: usize = 1024;\n/// Number of commits to generate.\nconst NUM_COMMITS: usize = 100;\n/// Size in bytes of the (dummy) transactions to generate.\nconst TX_SIZE: usize = 32;\n/// Number of transactions to generate per commit.\nconst TXS_PER_COMMIT: usize = 10;\n\n/// The size in bytes of one commit according to above parameters.\nconst COMMIT_SIZE: usize = Commit::FRAMING_LEN + (TX_SIZE * TXS_PER_COMMIT) + CRC_SIZE;\n\n/// Iterator yielding the start offsets of the commits in a segment.\nfn commit_boundaries() -> impl Iterator<Item = usize> {\n    successors(Some(segment::Header::LEN), |n| Some(n + COMMIT_SIZE)).take_while(|&x| x <= MAX_SEGMENT_SIZE)\n}\n\ntype Log = Rc<commitlog::Generic<repo::Memory, [u8; TX_SIZE]>>;\n\nfn mk_log() -> Log {\n    let mut log = mem_log::<[u8; TX_SIZE]>(MAX_SEGMENT_SIZE as _);\n    fill_log(&mut log, NUM_COMMITS, repeat(TXS_PER_COMMIT));\n    Rc::new(log)\n}\n\nstruct Inputs {\n    log: Log,\n    segment: Segment,\n    byte_pos: usize,\n    bit_mask: u8,\n\n    // For debugging.\n    #[allow(unused)]\n    segment_offset: u64,\n}\n\nimpl fmt::Debug for Inputs {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"Inputs\")\n            .field(\"byte_pos\", &self.byte_pos)\n            .field(\"bit_mask\", &self.bit_mask)\n            .field(\"segment_offset\", &self.segment_offset)\n            .finish()\n    }\n}\n\nimpl Inputs {\n    fn generate() -> impl Strategy<Value = Inputs> {\n        // Open + fill log.\n        let log = mk_log();\n        // Obtain segment offsets.\n        let segment_offsets = log.repo.existing_offsets().unwrap();\n        (\n            // Select a random segment.\n            select(segment_offsets)\n                // Open the segment at that offset,\n                // and generate a byte position where we want a bit to be\n                // flipped.\n                .prop_flat_map(move |segment_offset| {\n                    let segment = log.repo.open_segment_writer(segment_offset).unwrap();\n                    let byte_pos = byte_position(segment.len());\n                    (Just(log.clone()), Just(segment), Just(segment_offset), byte_pos)\n                }),\n            // A byte to XOR with the byte at `byte_pos`\n            any::<NonZeroU8>(),\n        )\n            .prop_map(|((log, segment, segment_offset, byte_pos), bit_mask)| Self {\n                log,\n                segment,\n                byte_pos,\n                bit_mask: bit_mask.get(),\n\n                segment_offset,\n            })\n    }\n}\n\n/// Select a random position of a byte within a segment.\n///\n/// The position shall not fall on any headers (segment or commit), so as to\n/// reliably provoke checksum errors (and not any other errors).\nfn byte_position(segment_len: usize) -> impl Strategy<Value = usize> {\n    (segment::Header::LEN + commit::Header::LEN + 1..segment_len).prop_map(|mut byte_pos| {\n        for x in commit_boundaries() {\n            if byte_pos >= x && byte_pos < x + COMMIT_SIZE {\n                byte_pos = byte_pos.max(x + commit::Header::LEN + 1);\n            }\n        }\n        byte_pos\n    })\n}\n\nproptest! {\n    #[test]\n    fn detect_bitflip_during_traversal(inputs in Inputs::generate()) {\n        enable_logging();\n        debug!(\"TEST RUN: {inputs:?}\");\n\n        let Inputs {\n            log,\n            mut segment,\n            byte_pos,\n            bit_mask,\n\n            segment_offset:_ ,\n        } = inputs;\n\n        segment.modify_byte_at(byte_pos, |b| b ^ bit_mask);\n\n        let first_err = log\n            .transactions_from(0, &payload::ArrayDecoder)\n            .find_map(Result::err)\n            .expect(\"unexpected success\");\n        let unexpected = match first_err {\n            payload::ArrayDecodeError::Traversal(error::Traversal::OutOfOrder {\n                prev_error: Some(prev_error),\n                ..\n            }) if matches!(*prev_error, error::Traversal::Checksum { .. }) => None,\n            payload::ArrayDecodeError::Traversal(error::Traversal::Checksum { .. }) => None,\n            e => Some(e),\n        };\n        assert!(unexpected.is_none(), \"unexpected error: {unexpected:?}\");\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/tests/helpers.rs",
    "content": "use std::fmt::Debug;\n\nuse env_logger::Env;\n\nuse crate::{\n    commitlog,\n    repo::{self, Repo},\n    Encode, Options,\n};\n\npub fn mem_log<T: Encode>(max_segment_size: u64) -> commitlog::Generic<repo::Memory, T> {\n    commitlog::Generic::open(\n        repo::Memory::unlimited(),\n        Options {\n            max_segment_size,\n            ..Options::default()\n        },\n    )\n    .unwrap()\n}\n\npub fn fill_log<R, T>(\n    log: &mut commitlog::Generic<R, T>,\n    num_commits: usize,\n    txs_per_commit: impl Iterator<Item = usize>,\n) -> usize\nwhere\n    R: Repo,\n    T: Debug + Default + Encode,\n{\n    let mut offset = log.max_committed_offset().map(|x| x + 1).unwrap_or_default();\n    for (_, n) in (0..num_commits).zip(txs_per_commit) {\n        log.commit((0..n).map(|i| (offset + i as u64, T::default())))\n            .unwrap_or_else(|e| panic!(\"failed to commit offset {offset}: {e:#}\"));\n        offset += n as u64;\n        log.flush().expect(\"failed to flush commitlog\");\n        log.sync();\n    }\n\n    offset as usize\n}\n\n/// Put the `txes` into `log`.\n///\n/// Each TX from `txes` will be placed in its own commit within `log`.\npub fn fill_log_with<R, T>(log: &mut commitlog::Generic<R, T>, txes: impl IntoIterator<Item = T>)\nwhere\n    R: Repo,\n    T: Debug + Encode,\n{\n    for (i, tx) in txes.into_iter().enumerate() {\n        log.commit([(i as u64, tx)])\n            .unwrap_or_else(|e| panic!(\"failed to commit offset {i}: {e:#}\"));\n    }\n    log.flush().expect(\"failed to flush commitlog\");\n    log.sync();\n}\n\npub fn enable_logging() {\n    let _ = env_logger::Builder::from_env(Env::default().default_filter_or(\"trace\"))\n        .format_timestamp(None)\n        .is_test(true)\n        .try_init();\n}\n"
  },
  {
    "path": "crates/commitlog/src/tests/partial.rs",
    "content": "use std::{\n    cmp,\n    fmt::{self, Debug},\n    io::{self, Seek as _, SeekFrom},\n    iter,\n    ops::Range,\n};\n\nuse log::{debug, info};\nuse pretty_assertions::assert_matches;\n\nuse crate::{\n    commitlog, payload,\n    repo::{self, Repo, SegmentLen},\n    segment::{self, FileLike},\n    tests::helpers::{enable_logging, fill_log_with},\n    Commit, Options, TxOffset, DEFAULT_LOG_FORMAT_VERSION,\n};\n\n#[test]\n#[should_panic(expected = \"failed to flush segment\")]\nfn panics_on_partial_write() {\n    enable_logging();\n\n    let mut log = open_log::<[u8; 32]>(ShortMem::new(800));\n    for i in 0..20 {\n        info!(\"commit {i}\");\n        log.commit([(i, [b'z'; 32])]).expect(\"unexpected `Err` result\");\n    }\n}\n\nfn fill_log(mut log: commitlog::Generic<ShortMem, [u8; 32]>, range: Range<TxOffset>) {\n    debug!(\"writing range {range:?}\");\n\n    let end = range.end;\n    for i in range {\n        info!(\"commit {i}\");\n        log.commit([(i, [b'z'; 32])]).unwrap();\n    }\n    log.flush().unwrap();\n\n    // Try to write one more, which should fail.\n    log.commit([(end, [b'x'; 32])]).unwrap();\n    assert_matches!(\n        log.flush(),\n        Err(e) if e.kind() == io::ErrorKind::StorageFull\n    );\n}\n\n/// Tests that, when a partial write occurs, we can read all flushed commits\n/// up until the faulty one.\n#[test]\nfn read_log_up_to_partial_write() {\n    enable_logging();\n\n    const MAX_SEGMENT_SIZE: usize = 800;\n    const TXDATA_SIZE: usize = 32;\n    const COMMIT_SIZE: usize = Commit::FRAMING_LEN + TXDATA_SIZE;\n    const TOTAL_TXS: usize = MAX_SEGMENT_SIZE / COMMIT_SIZE;\n\n    let repo = ShortMem::new(MAX_SEGMENT_SIZE as u64);\n    fill_log(open_log::<[u8; TXDATA_SIZE]>(repo.clone()), 0..(TOTAL_TXS as u64));\n\n    let txs = commitlog::transactions_from(\n        repo,\n        DEFAULT_LOG_FORMAT_VERSION,\n        0,\n        &payload::ArrayDecoder::<TXDATA_SIZE>,\n    )\n    .unwrap()\n    .map(Result::unwrap)\n    .count();\n\n    assert_eq!(txs, TOTAL_TXS,);\n}\n\n/// Tests:\n///\n/// - fill log until a partial write occurs\n/// - corrupt the last successfully written commit\n/// - fill log until a partial write occurs\n///\n/// The log should detect the corrupt commit, create a fresh segment, and write\n/// the second batch until ENOSPC. Traversal should work.\n#[test]\nfn reopen_with_corrupt_last_commit() {\n    enable_logging();\n\n    const MAX_SEGMENT_SIZE: usize = 800;\n    const TXDATA_SIZE: usize = 32;\n    const COMMIT_SIZE: usize = Commit::FRAMING_LEN + TXDATA_SIZE;\n    const TXS_PER_SEGMENT: u64 = (MAX_SEGMENT_SIZE / COMMIT_SIZE) as u64;\n    const TOTAL_TXS: u64 = (TXS_PER_SEGMENT * 2) - 1;\n\n    let repo = ShortMem::new(MAX_SEGMENT_SIZE as u64);\n\n    // Fill with as many txs as possible until ENOSPC.\n    fill_log(open_log::<[u8; TXDATA_SIZE]>(repo.clone()), 0..TXS_PER_SEGMENT);\n\n    // Invalidate the checksum of the last commit.\n    let last_segment_offset = repo.existing_offsets().unwrap().last().copied().unwrap();\n    let last_commit: Commit = repo::open_segment_reader(&repo, DEFAULT_LOG_FORMAT_VERSION, last_segment_offset)\n        .unwrap()\n        .commits()\n        .map(Result::unwrap)\n        .last()\n        .unwrap()\n        .into();\n    {\n        let mut last_segment = repo.open_segment_writer(last_segment_offset).unwrap();\n        let pos = last_segment.len() - last_commit.encoded_len() + 1;\n        last_segment.modify_byte_at(pos, |_| 255);\n    }\n\n    // Write a second batch, starting with the offset of the corrupt commit.\n    fill_log(\n        open_log::<[u8; TXDATA_SIZE]>(repo.clone()),\n        last_commit.min_tx_offset..TOTAL_TXS,\n    );\n\n    let txs = commitlog::transactions_from(\n        repo,\n        DEFAULT_LOG_FORMAT_VERSION,\n        0,\n        &payload::ArrayDecoder::<TXDATA_SIZE>,\n    )\n    .unwrap()\n    .map(Result::unwrap)\n    .count();\n\n    assert_eq!(txs as u64, TOTAL_TXS);\n}\n\n/// Edge case surfaced in production:\n///\n/// If the first commit in the last segment is corrupt, creating a new segment\n/// would fail because the `tx_range` is the same as the corrupt segment.\n///\n/// We don't automatically recover from that, but test that `open` returns an\n/// error providing some context.\n#[test]\nfn first_commit_in_last_segment_corrupt() {\n    enable_logging();\n\n    let repo = repo::Memory::unlimited();\n    let options = Options {\n        max_segment_size: 512,\n        ..<_>::default()\n    };\n    {\n        let mut log = commitlog::Generic::open(repo.clone(), options).unwrap();\n        fill_log_with(&mut log, iter::once([b'x'; 64]).cycle().take(9));\n    }\n    let segments = repo.existing_offsets().unwrap();\n    assert_eq!(2, segments.len(), \"repo should contain 2 segments\");\n\n    {\n        let mut last_segment = repo.open_segment_writer(*segments.last().unwrap()).unwrap();\n        last_segment.modify_bytes_at(segment::Header::LEN + 1.., |data| data.fill(0));\n    }\n\n    assert_matches!(\n        commitlog::Generic::<_, [u8; 64]>::open(repo, options),\n        Err(e) if e.kind() == io::ErrorKind::InvalidData,\n    );\n}\n\nfn open_log<T>(repo: ShortMem) -> commitlog::Generic<ShortMem, T> {\n    commitlog::Generic::open(\n        repo,\n        Options {\n            max_segment_size: 1024,\n            ..Options::default()\n        },\n    )\n    .unwrap()\n}\n\nconst ENOSPC: i32 = 28;\n\n/// Wrapper around [`mem::Segment`] which causes a partial [`io::Write::write`]\n/// if and when the size of the underlying buffer exceeds a max length.\n#[derive(Debug)]\nstruct ShortSegment {\n    inner: repo::mem::Segment,\n    max_len: u64,\n}\n\nimpl ShortSegment {\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn modify_byte_at(&mut self, pos: usize, f: impl FnOnce(u8) -> u8) {\n        self.inner.modify_byte_at(pos, f);\n    }\n}\n\nimpl SegmentLen for ShortSegment {\n    fn segment_len(&mut self) -> io::Result<u64> {\n        self.inner.segment_len()\n    }\n}\n\nimpl FileLike for ShortSegment {\n    fn fsync(&mut self) -> std::io::Result<()> {\n        self.inner.fsync()\n    }\n\n    fn ftruncate(&mut self, tx_offset: u64, size: u64) -> std::io::Result<()> {\n        self.inner.ftruncate(tx_offset, size)\n    }\n\n    #[cfg(feature = \"fallocate\")]\n    fn fallocate(&mut self, size: u64) -> io::Result<()> {\n        self.inner.fallocate(size)\n    }\n}\n\nimpl io::Write for ShortSegment {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        let pos = self.inner.stream_position()?;\n        debug!(\"pos={} max_len={} buf-len={}\", pos, self.max_len, buf.len());\n        if pos + buf.len() as u64 > self.max_len {\n            let max = cmp::min(1, (self.max_len - pos) as usize);\n            let n = self.inner.write(&buf[..max])?;\n            debug!(\"partial write {}/{}\", n, buf.len());\n            return Err(io::Error::from_raw_os_error(ENOSPC));\n        }\n        self.inner.write(buf)\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        self.inner.flush()\n    }\n}\n\nimpl io::Read for ShortSegment {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        self.inner.read(buf)\n    }\n}\n\nimpl io::Seek for ShortSegment {\n    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {\n        self.inner.seek(pos)\n    }\n}\n\n/// Wrapper around [`repo::Memory`] which causes partial (or: short) writes.\n#[derive(Debug, Clone)]\nstruct ShortMem {\n    inner: repo::Memory,\n    max_len: u64,\n}\n\nimpl ShortMem {\n    pub fn new(max_len: u64) -> Self {\n        Self {\n            inner: repo::Memory::new(max_len * 4096),\n            max_len,\n        }\n    }\n}\n\nimpl fmt::Display for ShortMem {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.inner, f)\n    }\n}\n\nimpl Repo for ShortMem {\n    type SegmentWriter = ShortSegment;\n    type SegmentReader = repo::mem::ReadOnlySegment;\n\n    fn create_segment(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        self.inner.create_segment(offset).map(|inner| ShortSegment {\n            inner,\n            max_len: self.max_len,\n        })\n    }\n\n    fn open_segment_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter> {\n        self.inner.open_segment_writer(offset).map(|inner| ShortSegment {\n            inner,\n            max_len: self.max_len,\n        })\n    }\n\n    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader> {\n        self.inner.open_segment_reader(offset)\n    }\n\n    fn remove_segment(&self, offset: u64) -> io::Result<()> {\n        self.inner.remove_segment(offset)\n    }\n\n    fn compress_segment(&self, offset: u64) -> io::Result<()> {\n        self.inner.compress_segment(offset)\n    }\n\n    fn existing_offsets(&self) -> io::Result<Vec<u64>> {\n        self.inner.existing_offsets()\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/tests.rs",
    "content": "#[cfg(test)]\nmod bitflip;\n#[cfg(test)]\nmod partial;\n\npub mod helpers;\n"
  },
  {
    "path": "crates/commitlog/src/varchar.rs",
    "content": "use std::ops::Deref;\n\n/// An owned string with a max length of `N`, like the venerable VARCHAR.\n///\n/// The length is in bytes, not characters.\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\n#[repr(transparent)]\npub struct Varchar<const N: usize> {\n    // TODO: Depending on `core` usage, we may want a SSO string type, a pointer\n    // type, and / or a `Cow`-like type here.\n    inner: String,\n}\n\nimpl<const N: usize> Varchar<N> {\n    /// Construct `Some(Self)` from a string slice,\n    /// or `None` if the argument is longer than `N` bytes.\n    #[allow(clippy::should_implement_trait)]\n    pub fn from_str(s: &str) -> Option<Self> {\n        (s.len() <= N).then(|| Self { inner: s.into() })\n    }\n\n    /// Construct [`Self`] from a string slice,\n    /// or allocate a new string containing the first `N` bytes of the slice\n    /// if it is longer than `N` bytes.\n    ///\n    /// In case of truncation, the resulting string may be shorter than `N` if\n    /// `N` falls on a character boundary.\n    pub fn from_str_truncate(s: &str) -> Self {\n        Self::from_str(s).unwrap_or_else(|| {\n            let mut s = s.to_owned();\n            while s.len() > N {\n                s.pop().unwrap();\n            }\n            Self { inner: s }\n        })\n    }\n\n    /// Construct [`Self`] from a string,\n    /// or `None` if the string is longer than `N`.\n    pub fn from_string(s: String) -> Option<Self> {\n        (s.len() <= N).then_some(Self { inner: s })\n    }\n\n    /// Move the given string into `Self` if its length does not exceed `N`,\n    /// or truncate it to the appropriate length.\n    ///\n    /// In case of truncation, the resulting string may be shorter than `N` if\n    /// `N` falls on a character boundary.\n    pub fn from_string_truncate(s: String) -> Self {\n        if s.len() <= N {\n            Self { inner: s }\n        } else {\n            let mut s = s;\n            while s.len() > N {\n                s.pop().unwrap();\n            }\n            Self { inner: s }\n        }\n    }\n\n    /// Discard the `Varchar` wrapper.\n    pub fn into_inner(self) -> String {\n        self.into()\n    }\n\n    /// Extract a string slice containing the entire `Varchar`.\n    pub fn as_str(&self) -> &str {\n        self\n    }\n}\n\nimpl<const N: usize> Deref for Varchar<N> {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<const N: usize> From<Varchar<N>> for String {\n    fn from(value: Varchar<N>) -> Self {\n        value.inner\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<const N: usize> serde::Serialize for Varchar<N> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_str(self)\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<'de, const N: usize> serde::Deserialize<'de> for Varchar<N> {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let s = String::deserialize(deserializer)?;\n        let len = s.len();\n        Self::from_string(s)\n            .ok_or_else(|| serde::de::Error::custom(format!(\"input string too long: {len} max-len={N}\")))\n    }\n}\n\n#[cfg(test)]\npub(crate) mod tests {\n    use super::*;\n    use proptest::prelude::*;\n\n    impl<const N: usize> Arbitrary for Varchar<N> {\n        type Strategy = BoxedStrategy<Varchar<N>>;\n        type Parameters = ();\n\n        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {\n            use proptest::char;\n            use proptest::collection::vec;\n\n            vec(char::ranges(char::DEFAULT_PREFERRED_RANGES.into()), 0..N)\n                .prop_map(|chars| {\n                    let inner = chars.into_iter().fold(String::with_capacity(N), |mut s, c| {\n                        if s.len() + c.len_utf8() <= N {\n                            s.push(c);\n                        }\n                        s\n                    });\n                    Varchar { inner }\n                })\n                .boxed()\n        }\n    }\n\n    proptest! {\n        #[test]\n        fn prop_varchar_generator_does_not_break_invariant(varchar in any::<Varchar<255>>()) {\n            assert!(varchar.len() <= 255);\n        }\n\n        #[test]\n        fn prop_rejects_long(s in \"\\\\w{33,}\") {\n            assert!(Varchar::<32>::from_string(s).is_none());\n        }\n\n        #[test]\n        fn prop_accepts_short(s in \"[[:ascii:]]{0,32}\") {\n            assert_eq!(s.as_str(), Varchar::<32>::from_str(&s).unwrap().as_str())\n        }\n\n        #[test]\n        fn prop_truncate(s in \"[[:ascii:]]{33,}\") {\n            let vc = Varchar::<32>::from_string_truncate(s);\n            assert_eq!(32, vc.len());\n        }\n\n        #[test]\n        fn prop_truncate_n_on_char_boundary(s in \"[[:ascii:]]{31}\") {\n            let mut t = s.clone();\n            t.push('ß');\n            let vc = Varchar::<32>::from_string_truncate(t);\n            assert_eq!(*vc, s);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/src/varint.rs",
    "content": "//! Varint encoding and decoding functions.\n//!\n//! We use Protobuf's [Base-128 varint] encoding.\n//!\n//! Unsigned integers are split into 7-bit chunks, with the least significant\n//! chunk first.\n//! Each chunk is placed in the low 7 bits of a byte.\n//! Non-terminal bytes have the high bit set.\n//! The final byte in an integer has the high bit zeroed.\n//!\n//! NOTE: In the current commitlog format, varints are expected to fit into one\n//! byte most of the time. Hence, the implementation below is not particularly\n//! optimized for larger integers.\n//!   Should this change in the future, various crates are available for\n//! consideration, including [varint-simd] which provides hardware acceleration\n//! on certain architectures.\n//!\n//! [Base-128 varint]: https://protobuf.dev/programming-guides/encoding/#varints\n//! [varint-simd]: https://crates.io/crates/varint-simd\n\nuse spacetimedb_sats::buffer::{BufReader, BufWriter, DecodeError};\n\n#[inline]\npub fn encode_varint(mut value: usize, out: &mut impl BufWriter) {\n    loop {\n        if value < 0x80 {\n            out.put_u8(value as u8);\n            break;\n        } else {\n            out.put_u8(((value & 0x7f) | 0x80) as u8);\n            value >>= 7;\n        }\n    }\n}\n\n#[inline]\npub fn decode_varint<'a>(reader: &mut impl BufReader<'a>) -> Result<usize, DecodeError> {\n    let mut result = 0;\n    let mut shift = 0;\n    loop {\n        let byte = reader.get_u8()?;\n        if (byte & 0x80) == 0 {\n            result |= (byte as usize) << shift;\n            return Ok(result);\n        } else {\n            result |= ((byte & 0x7F) as usize) << shift;\n        }\n        shift += 7;\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n\n    proptest! {\n        #[test]\n        fn varint_roundtrip(val in any::<usize>()) {\n            let mut buf = Vec::new();\n            encode_varint(val, &mut buf);\n            assert_eq!(val, decode_varint(&mut buf.as_slice()).unwrap());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/commitlog/tests/io.rs",
    "content": "mod random_payload;\n\n#[cfg(feature = \"streaming\")]\nmod streaming;\n"
  },
  {
    "path": "crates/commitlog/tests/random_payload/mod.rs",
    "content": "use log::info;\nuse spacetimedb_commitlog::repo::Repo;\nuse spacetimedb_commitlog::tests::helpers::enable_logging;\nuse spacetimedb_commitlog::{commitlog, payload, repo, Commitlog, Options};\nuse spacetimedb_paths::server::CommitLogDir;\nuse spacetimedb_paths::FromPathUnchecked;\nuse tempfile::tempdir;\n\npub fn gen_payload() -> [u8; 256] {\n    rand::random()\n}\n\n#[test]\nfn smoke() {\n    let root = tempdir().unwrap();\n    let clog = Commitlog::open(\n        CommitLogDir::from_path_unchecked(root.path()),\n        Options {\n            max_segment_size: 8 * 1024,\n            ..Options::default()\n        },\n        None,\n    )\n    .unwrap();\n\n    let n_txs = 500;\n    let payload = gen_payload();\n    for i in 0..n_txs {\n        clog.commit([(i, payload)]).unwrap();\n    }\n    let committed_offset = clog.flush_and_sync().unwrap();\n\n    assert_eq!(n_txs - 1, committed_offset.unwrap());\n    assert_eq!(\n        n_txs as usize,\n        clog.transactions(&payload::ArrayDecoder).map(Result::unwrap).count()\n    );\n    // We set max_records_in_commit to 1, so n_commits == n_txs\n    assert_eq!(n_txs as usize, clog.commits().map(Result::unwrap).count());\n}\n\n#[test]\nfn resets() {\n    let root = tempdir().unwrap();\n    let mut clog = Commitlog::open(\n        CommitLogDir::from_path_unchecked(root.path()),\n        Options {\n            max_segment_size: 512,\n            ..Options::default()\n        },\n        None,\n    )\n    .unwrap();\n\n    let payload = gen_payload();\n    for i in 0..50 {\n        clog.commit([(i, payload)]).unwrap();\n    }\n    clog.flush_and_sync().unwrap();\n\n    for offset in (0..50).rev() {\n        clog = clog.reset_to(offset).unwrap();\n        assert_eq!(\n            offset,\n            clog.transactions(&payload::ArrayDecoder)\n                .map(Result::unwrap)\n                .last()\n                .unwrap()\n                .offset\n        );\n        // We're counting from zero, so offset + 1 is the # of txs.\n        assert_eq!(\n            offset + 1,\n            clog.transactions(&payload::ArrayDecoder).map(Result::unwrap).count() as u64\n        );\n    }\n}\n\n/// Try to generate commitlogs that will be amenable to compression -\n/// random data doesn't compress well, so try and have there be repetition\nfn compressible_payloads() -> impl Iterator<Item = [u8; 256]> {\n    (0..4).map(|_| gen_payload()).cycle()\n}\n\n#[test]\nfn compression() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let clog = Commitlog::open(\n        CommitLogDir::from_path_unchecked(root.path()),\n        Options {\n            max_segment_size: 8 * 1024,\n            ..Options::default()\n        },\n        None,\n    )\n    .unwrap();\n\n    let payloads = compressible_payloads().take(1024).collect::<Vec<_>>();\n    for (i, payload) in payloads.iter().enumerate() {\n        clog.commit([(i as u64, *payload)]).unwrap();\n    }\n    clog.flush_and_sync().unwrap();\n\n    let uncompressed_size = clog.size_on_disk().unwrap();\n\n    let segments = clog.existing_segment_offsets().unwrap();\n    let segments_to_compress = &segments[..segments.len() / 2];\n    info!(\"segments: {segments:?} compressing: {segments_to_compress:?}\");\n    clog.compress_segments(segments_to_compress).unwrap();\n\n    let compressed_size = clog.size_on_disk().unwrap();\n    assert!(compressed_size.total_bytes < uncompressed_size.total_bytes);\n\n    assert!(clog\n        .transactions(&payload::ArrayDecoder)\n        .map(Result::unwrap)\n        .enumerate()\n        .all(|(i, x)| x.offset == i as u64 && x.txdata == payloads[i]));\n}\n\n/// When restoring an archived commitlog, all segments are compressed and should\n/// remain immutable.\n///\n/// Tests that this is upheld, i.e. a fresh segment is created when resuming\n/// writes.\n#[test]\nfn all_segments_sealed() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let path = CommitLogDir::from_path_unchecked(root.path());\n    let opts = Options {\n        max_segment_size: 64 * 1024,\n        ..<_>::default()\n    };\n    let num_commits = 1024;\n    let repo = repo::Fs::new(path, None).unwrap();\n    {\n        let mut clog = commitlog::Generic::open(&repo, opts).unwrap();\n        for (i, payload) in compressible_payloads().take(num_commits).enumerate() {\n            clog.commit([(i as u64, payload)]).unwrap();\n        }\n        clog.flush().unwrap();\n        clog.sync();\n    }\n\n    let segments = repo.existing_offsets().unwrap();\n    let num_segments = segments.len();\n\n    // Compress all segments via the `repo`,\n    // to not trigger the assert that the head segment cannot be compressed.\n    for segment in segments {\n        repo.compress_segment(segment).unwrap();\n    }\n\n    // Re-opening the commitlog should create a fresh segment at offset `num_commits`.\n    let _ = commitlog::Generic::<_, [u8; 256]>::open(&repo, opts).unwrap();\n    let segments = repo.existing_offsets().unwrap();\n    assert_eq!(num_segments + 1, segments.len());\n    assert_eq!(segments.last().copied(), Some(num_commits as u64));\n}\n"
  },
  {
    "path": "crates/commitlog/tests/streaming/mod.rs",
    "content": "use std::{\n    io,\n    num::NonZeroU64,\n    ops::RangeBounds,\n    path::{Path, PathBuf},\n};\n\nuse futures::StreamExt as _;\nuse log::info;\nuse spacetimedb_commitlog::{\n    repo::{self, Repo, SegmentLen},\n    stream::{self, OnTrailingData, StreamWriter},\n    tests::helpers::enable_logging,\n    Commitlog, Options,\n};\nuse spacetimedb_paths::{server::CommitLogDir, FromPathUnchecked as _};\nuse tempfile::tempdir;\nuse tokio::{\n    fs,\n    io::{AsyncBufRead, AsyncReadExt, BufReader},\n    pin,\n    task::spawn_blocking,\n};\nuse tokio_stream::wrappers::ReadDirStream;\nuse tokio_util::io::StreamReader;\n\nuse super::random_payload;\n\n#[tokio::test]\nasync fn copy_all() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let (src, dst) = create_dirs(root.path()).await;\n    fill_log(src.clone()).await;\n\n    let writer = create_writer(dst.clone())\n        .await\n        .expect(\"failed to create stream writer\");\n    let reader = create_reader(&src, ..);\n    pin!(reader);\n    writer\n        .append_all(reader, |_| ())\n        .await\n        .unwrap()\n        .sync_all()\n        .await\n        .unwrap();\n\n    assert_equal_dirs(&src, &dst).await\n}\n\n#[tokio::test]\nasync fn copy_ranges() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let (src, dst) = create_dirs(root.path()).await;\n    fill_log(src.clone()).await;\n\n    let mut writer = create_writer(dst.clone())\n        .await\n        .expect(\"failed to create stream writer\");\n\n    for (start, end) in [(0, 25), (25, 50), (50, 75), (75, 101)] {\n        info!(\"appending range {start}..{end}\");\n        let reader = create_reader(&src, start..end);\n        pin!(reader);\n        writer = writer.append_all(reader, |_| ()).await.unwrap();\n    }\n    writer.sync_all().await.unwrap();\n\n    assert_equal_dirs(&src, &dst).await\n}\n\n#[tokio::test]\nasync fn copy_invalid_range() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let (src, dst) = create_dirs(root.path()).await;\n    fill_log(src.clone()).await;\n\n    let mut writer = create_writer(dst.clone()).await.expect(\"failed to create writer\");\n\n    {\n        info!(\"appending `..50`\");\n        let reader = create_reader(&src, ..50);\n        pin!(reader);\n        writer = writer.append_all(reader, |_| ()).await.unwrap();\n        writer.sync_all().await.unwrap();\n    }\n    {\n        info!(\"appending `75..`\");\n        let reader = create_reader(&src, 75..);\n        pin!(reader);\n        pretty_assertions::assert_matches!(\n            writer.append_all(reader, |_| ()).await.map(drop),\n            Err(e) if e.kind() == io::ErrorKind::InvalidData\n        );\n    }\n}\n\n#[tokio::test]\nasync fn trim_garbage() {\n    enable_logging();\n\n    let root = tempdir().unwrap();\n    let (src, dst) = create_dirs(root.path()).await;\n    fill_log(src.clone()).await;\n\n    {\n        let writer = create_writer(dst.clone())\n            .await\n            .expect(\"failed to create stream writer\");\n        let reader = create_reader(&src, ..);\n        pin!(reader);\n        writer.append_all(reader, |_| ()).await.unwrap();\n        assert_equal_dirs(&src, &dst).await\n    }\n\n    // Truncate the destination log so the last commit is broken.\n    spawn_blocking({\n        let repo = repo(&dst);\n        move || {\n            let last_segment_offset = repo.existing_offsets().unwrap().pop().unwrap();\n            let mut segment = repo.open_segment_writer(last_segment_offset).unwrap();\n            let len = segment.segment_len().unwrap();\n            segment.set_len(len - 128).unwrap();\n        }\n    })\n    .await\n    .unwrap();\n    // The default is to return an error.\n    pretty_assertions::assert_matches!(\n        create_writer(dst.clone()).await.map(drop),\n        Err(e) if e.kind() == io::ErrorKind::InvalidData\n    );\n\n    // With `Trim`, we can retry from commit 99.\n    let writer = spawn_blocking({\n        let path = dst.clone();\n        move || StreamWriter::create(repo(&path), default_options(), OnTrailingData::Trim)\n    })\n    .await\n    .unwrap()\n    .expect(\"failed to create stream writer\");\n    let reader = create_reader(&src, 99..);\n    pin!(reader);\n    writer\n        .append_all(reader, |_| ())\n        .await\n        .unwrap()\n        .sync_all()\n        .await\n        .unwrap();\n\n    assert_equal_dirs(&src, &dst).await\n}\n\nasync fn assert_equal_dirs(src: &Path, dst: &Path) {\n    let mut src_dir = fs::read_dir(src).await.map(ReadDirStream::new).unwrap();\n    let mut buf_a = vec![];\n    let mut buf_b = vec![];\n    while let Some(entry) = src_dir.next().await.map(Result::unwrap) {\n        if entry.file_type().await.unwrap().is_file() {\n            let src_path = entry.path();\n            let dst_path = dst.join(src_path.file_name().unwrap());\n\n            let mut src_file = fs::File::open(&src_path).await.unwrap();\n            let mut dst_file = fs::File::open(&dst_path).await.unwrap();\n\n            src_file.read_to_end(&mut buf_a).await.unwrap();\n            dst_file.read_to_end(&mut buf_b).await.unwrap();\n\n            assert_eq!(buf_a, buf_b, \"{} and {} differ\", src_path.display(), dst_path.display());\n        }\n        buf_a.clear();\n        buf_b.clear();\n    }\n}\n\nfn default_options() -> Options {\n    Options {\n        max_segment_size: 8 * 1024,\n        // Write an index entry for every commit.\n        offset_index_interval_bytes: NonZeroU64::new(256).unwrap(),\n        offset_index_require_segment_fsync: false,\n        ..Options::default()\n    }\n}\n\nasync fn fill_log(path: PathBuf) {\n    spawn_blocking(move || {\n        let clog = Commitlog::open(CommitLogDir::from_path_unchecked(path), default_options(), None).unwrap();\n        let payload = random_payload::gen_payload();\n        for i in 0..100 {\n            clog.commit([(i, payload)]).unwrap();\n        }\n        clog.flush_and_sync().unwrap();\n    })\n    .await\n    .unwrap();\n}\n\nasync fn create_writer(path: PathBuf) -> io::Result<StreamWriter<repo::Fs>> {\n    spawn_blocking(move || StreamWriter::create(repo(&path), default_options(), OnTrailingData::Error))\n        .await\n        .unwrap()\n}\n\nfn repo(at: &Path) -> repo::Fs {\n    repo::Fs::new(CommitLogDir::from_path_unchecked(at), None).unwrap()\n}\n\nfn create_reader<R: RangeBounds<u64>>(path: &Path, range: R) -> impl AsyncBufRead + use<R> {\n    BufReader::new(StreamReader::new(stream::commits(\n        repo::Fs::new(CommitLogDir::from_path_unchecked(path), None).unwrap(),\n        range,\n    )))\n}\n\nasync fn create_dirs(root: &Path) -> (PathBuf, PathBuf) {\n    let src = root.join(\"a\");\n    let dst = root.join(\"b\");\n    fs::create_dir(&src).await.unwrap();\n    fs::create_dir(&dst).await.unwrap();\n\n    (src, dst)\n}\n"
  },
  {
    "path": "crates/core/.gitignore",
    "content": "flamegraphs\n"
  },
  {
    "path": "crates/core/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-core\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The core library for SpacetimeDB\"\nrust-version.workspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n[lib]\nname = \"spacetimedb\" # The name of the target.\npath = \"src/lib.rs\"  # The source file of the target.\n\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n[dependencies]\nspacetimedb-auth.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib = { workspace = true, features = [\"serde\", \"metrics_impls\"] }\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-commitlog.workspace = true\nspacetimedb-datastore.workspace = true\nspacetimedb-durability.workspace = true\nspacetimedb-memory-usage.workspace = true\nspacetimedb-metrics.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-physical-plan.workspace = true\nspacetimedb-query.workspace = true\nspacetimedb-sats = { workspace = true, features = [\"serde\"] }\nspacetimedb-schema.workspace = true\nspacetimedb-table.workspace = true\nspacetimedb-snapshot.workspace = true\nspacetimedb-subscription.workspace = true\nspacetimedb-expr.workspace = true\nspacetimedb-execution.workspace = true\nspacetimedb-fs-utils.workspace = true\n\nanyhow = { workspace = true, features = [\"backtrace\"] }\narrayvec.workspace = true\nasync-trait.workspace = true\nbacktrace.workspace = true\nbase64.workspace = true\nblake3.workspace = true\nbrotli.workspace = true\nbytemuck.workspace = true\nbytes.workspace = true\nbytestring.workspace = true\nchrono.workspace = true\ncrossbeam-channel.workspace = true\ncrossbeam-queue.workspace = true\ndeno_core_icudata.workspace = true\nderive_more.workspace = true\ndirs.workspace = true\nenum-as-inner.workspace = true\nenum-map.workspace = true\nflate2.workspace = true\nflume.workspace = true\nfs2.workspace = true\nfutures.workspace = true\nfutures-util.workspace = true\nhex.workspace = true\nhostname.workspace = true\nhttp.workspace = true\nhttp-body-util.workspace = true\nhyper.workspace = true\nimara-diff.workspace = true\nindexmap.workspace = true\nitertools.workspace = true\njsonwebtoken.workspace = true\nlazy_static.workspace = true\nlog.workspace = true\nmemchr.workspace = true\nnohash-hasher.workspace = true\nonce_cell.workspace = true\nopenssl.workspace = true\nparking_lot.workspace = true\npaste.workspace = true\npin-project-lite.workspace = true\nprometheus.workspace = true\nrayon.workspace = true\nrayon-core.workspace = true\nregex.workspace = true\nreqwest.workspace = true\nrustc-demangle.workspace = true\nrustc-hash.workspace = true\nscopeguard.workspace = true\nsemver = { workspace = true, features = [\"serde\"] }\nserde.workspace = true\nserde_json.workspace = true\nserde_path_to_error.workspace = true\nserde_with = { workspace = true, features = [\"chrono_0_4\"] }\nsha1.workspace = true\nsimilar.workspace = true\nslab.workspace = true\nsled.workspace = true\nsmallvec.workspace = true\nsourcemap.workspace = true\nsqlparser.workspace = true\nstrum.workspace = true\ntabled.workspace = true\ntempfile.workspace = true\nthiserror.workspace = true\nthin-vec.workspace = true\ntokio-util.workspace = true\ntokio.workspace = true\ntokio-stream = { workspace = true, features = [\"sync\"] }\ntokio-metrics = { version = \"0.4.0\", features = [\"rt\"] }\ntoml.workspace = true\ntracing-appender.workspace = true\ntracing-core.workspace = true\ntracing-flame.workspace = true\ntracing-log.workspace = true\ntracing-subscriber.workspace = true\ntracing-tracy.workspace = true\ntracing.workspace = true\nurl.workspace = true\nurlencoding.workspace = true\nuuid.workspace = true\nv8.workspace = true\nwasmtime.workspace = true\nwasmtime-internal-fiber.workspace = true\njwks.workspace = true\nasync_cache = \"0.3.1\"\nfaststr = \"0.2.23\"\ncore_affinity = \"0.8\"\n\n[target.'cfg(not(target_env = \"msvc\"))'.dependencies]\ntikv-jemallocator = {workspace = true}\ntikv-jemalloc-ctl = {workspace = true}\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\nnix = { workspace = true, features = [\"sched\"] }\n\n[features]\n# Print a warning when doing an unindexed `iter_by_col_range` on a large table.\nunindexed_iter_by_col_range_warn = []\ndefault = [\"unindexed_iter_by_col_range_warn\"]\n# Test-only escape hatch used by SDK procedure tests that intentionally call `localhost`.\n# Keep this off in production builds.\nallow_loopback_http_for_tests = []\n# Enable timing for wasm ABI calls\nspacetimedb-wasm-instance-env-times = []\n# Enable test helpers and utils\ntest = [\"spacetimedb-commitlog/test\", \"spacetimedb-datastore/test\"]\n# Perfmaps for profiling modules\nperfmap = []\n# Disables core pinning\nno-core-pinning = []\nno-job-core-pinning = []\n\n[dev-dependencies]\nspacetimedb-lib = { path = \"../lib\", features = [\"proptest\", \"test\"] }\nspacetimedb-sats = { path = \"../sats\", features = [\"proptest\"] }\nspacetimedb-commitlog = { path = \"../commitlog\", features = [\"test\"] }\nspacetimedb-datastore = { path = \"../datastore/\", features = [\"test\"] }\n\ncriterion.workspace = true\n# Also as dev-dependencies for use in _this_ crate's tests.\nproptest.workspace = true\nproptest-derive.workspace = true\nrand.workspace = true\nenv_logger.workspace = true\npretty_assertions.workspace = true\njsonwebtoken.workspace = true\naxum.workspace = true\nfs_extra.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/core/README.md",
    "content": "> ⚠️ **Unstable Crate** ⚠️\n>\n> The interface of this crate is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/core/proptest-regressions/db/datastore/locking_tx_datastore/delete_table.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 9d74a36309d35ba5b09bd33cbc24f72f95a498152b469caeeeb8c2adf712974c # shrinks to (size, [ptr_a, ptr_b]) = (Size(2), [RowPointer(r: 0, pi: 0, po: 0, so: 1), RowPointer(r: 0, pi: 0, po: 0, so: 1)])\ncc daf4d4880192b4f6d95f928ed49d80465b48ff4fde4ae31aea5cfea3996cdbc6 # shrinks to minimal failing input: (size, [ptr_a, ptr_b]) = (Size(23), [RowPointer(r: 0, pi: 75, po: 828, so: 1), RowPointer(r: 0, pi: 75, po: 828, so: 1)])\n"
  },
  {
    "path": "crates/core/proptest-regressions/host/v8/ser.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc b4eaf1389ae0810b57684a9ca8315fb70e1bd59227a979fb90ac2885d9887684 # shrinks to (ty, val) = (Product(ProductType {\"field_0\": Bool}), Product(ProductValue { elements: [Bool(false)] }))\ncc 191c44593d03c0c9d47fed4ccc34d87b824f3964cf11ba87cc324cf67fea475c # shrinks to (ty, val) = (Sum(SumType {\"variant_0\": Bool}), Sum(SumValue { tag: 0, value: Bool(false) }))\n"
  },
  {
    "path": "crates/core/proptest-regressions/host/v8/to_value.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc b9cebcbfdb32c25a2093f9e860502a771b4fbd22f92cc7a6a72032aed46dd476 # shrinks to x = -9223372036854775809\ncc 2480a43bc9d76592946409dbe626d75ddaa94c6f1506e6508e9e4c688ef36cec # shrinks to x = -340282366920938463463374607431768211456\n"
  },
  {
    "path": "crates/core/src/auth/mod.rs",
    "content": "use jsonwebtoken::{DecodingKey, EncodingKey};\nuse openssl::ec::{EcGroup, EcKey};\nuse openssl::nid::Nid;\nuse openssl::pkey::PKey;\nuse spacetimedb_paths::cli::{PrivKeyPath, PubKeyPath};\n\nuse crate::config::CertificateAuthority;\n\npub use spacetimedb_auth::identity;\npub mod token_validation;\n\n/// JWT verification and signing keys.\n#[derive(Clone)]\npub struct JwtKeys {\n    pub public: DecodingKey,\n    pub public_pem: Box<[u8]>,\n    pub private: EncodingKey,\n    pub private_pem: Box<[u8]>,\n    pub kid: Option<String>,\n}\n\nimpl JwtKeys {\n    /// Create a new [`JwtKeys`] from paths to the public and private key files\n    /// respectively.\n    ///\n    /// The key files must be PEM encoded ECDSA P256 keys.\n    pub fn new(public_pem: impl Into<Box<[u8]>>, private_pem: impl Into<Box<[u8]>>) -> anyhow::Result<Self> {\n        let public_pem = public_pem.into();\n        let private_pem = private_pem.into();\n        let public = DecodingKey::from_ec_pem(&public_pem)?;\n        let private = EncodingKey::from_ec_pem(&private_pem)?;\n\n        Ok(Self {\n            public,\n            private,\n            public_pem,\n            private_pem,\n            kid: None,\n        })\n    }\n\n    pub fn generate() -> anyhow::Result<Self> {\n        let keypair = EcKeyPair::generate()?;\n        keypair.try_into()\n    }\n}\n\n// Get the key pair if the given files exist. If they don't, create them.\n// If only one of the files exists, return an error.\npub fn get_or_create_keys(certs: &CertificateAuthority) -> anyhow::Result<JwtKeys> {\n    let public_key_path = &certs.jwt_pub_key_path;\n    let private_key_path = &certs.jwt_priv_key_path;\n\n    let public_key_bytes = public_key_path.read().ok();\n    let private_key_bytes = private_key_path.read().ok();\n\n    // If both keys are unspecified, create them\n    let key_pair = match (public_key_bytes, private_key_bytes) {\n        (Some(pub_), Some(priv_)) => EcKeyPair::new(pub_, priv_),\n        (None, None) => {\n            let keys = EcKeyPair::generate()?;\n            keys.write_to_files(public_key_path, private_key_path)?;\n            keys\n        }\n        (None, Some(_)) => anyhow::bail!(\"Unable to read public key for JWT token verification\"),\n        (Some(_), None) => anyhow::bail!(\"Unable to read private key for JWT token signing\"),\n    };\n\n    key_pair.try_into()\n}\n\n// An Ec key pair in pem format.\npub struct EcKeyPair {\n    pub public_key_bytes: Vec<u8>,\n    pub private_key_bytes: Vec<u8>,\n}\n\nimpl TryFrom<EcKeyPair> for JwtKeys {\n    type Error = anyhow::Error;\n    fn try_from(pair: EcKeyPair) -> anyhow::Result<Self> {\n        JwtKeys::new(pair.public_key_bytes, pair.private_key_bytes)\n    }\n}\n\nimpl EcKeyPair {\n    pub fn new(public_key_bytes: Vec<u8>, private_key_bytes: Vec<u8>) -> Self {\n        Self {\n            public_key_bytes,\n            private_key_bytes,\n        }\n    }\n\n    pub fn generate() -> anyhow::Result<Self> {\n        // Create a new EC group from a named curve.\n        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;\n\n        // Create a new EC key with the specified group.\n        let eckey = EcKey::generate(&group)?;\n\n        // Create a new PKey from the EC key.\n        let pkey = PKey::from_ec_key(eckey.clone())?;\n\n        // Get the private key in PKCS#8 PEM format & write it.\n        let private_key_bytes = pkey.private_key_to_pem_pkcs8()?;\n\n        // Get the public key in PEM format & write it.\n        let public_key_bytes = eckey.public_key_to_pem()?;\n\n        Ok(Self {\n            public_key_bytes,\n            private_key_bytes,\n        })\n    }\n\n    pub fn write_to_files(&self, public_key_path: &PubKeyPath, private_key_path: &PrivKeyPath) -> anyhow::Result<()> {\n        public_key_path.write(&self.public_key_bytes)?;\n        private_key_path.write(&self.private_key_bytes)?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/auth/token_validation.rs",
    "content": "use anyhow;\nuse async_cache;\nuse async_trait::async_trait;\nuse core::ops::Deref;\nuse faststr::FastStr;\nuse jsonwebtoken::decode_header;\npub use jsonwebtoken::errors::Error as JwtError;\npub use jsonwebtoken::errors::ErrorKind as JwtErrorKind;\nuse jsonwebtoken::{decode, Validation};\npub use jsonwebtoken::{DecodingKey, EncodingKey};\nuse jwks::Jwks;\nuse lazy_static::lazy_static;\nuse serde::Serialize;\nuse std::sync::Arc;\nuse std::time::Duration;\nuse thiserror;\n\nuse super::identity::{IncomingClaims, SpacetimeIdentityClaims};\nuse super::JwtKeys;\n\n#[derive(thiserror::Error, Debug)]\npub enum TokenValidationError {\n    // TODO: Add real error types.\n\n    // TODO: If we had our own errors defined we wouldn't be locked into this lib.\n    #[error(\"Invalid token: {0}\")]\n    TokenError(#[from] JwtError),\n\n    #[error(\"Specified key ID not found in JWKs\")]\n    KeyIDNotFound,\n\n    #[error(transparent)]\n    JwkError(#[from] jwks::JwkError),\n    #[error(transparent)]\n    JwksError(#[from] jwks::JwksError),\n    // The other case is a catch-all for unexpected errors.\n    #[error(transparent)]\n    Other(#[from] anyhow::Error),\n}\n\n// A token signer is responsible for signing tokens without doing any validation.\npub trait TokenSigner: Sync + Send {\n    // Serialize the given claims and sign a JWT token with them as the payload.\n    fn sign<T: Serialize>(&self, claims: &T) -> Result<String, JwtError>;\n}\n\nimpl TokenSigner for EncodingKey {\n    fn sign<Token: Serialize>(&self, claims: &Token) -> Result<String, JwtError> {\n        let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::ES256);\n        jsonwebtoken::encode(&header, claims, self)\n    }\n}\n\nimpl TokenSigner for JwtKeys {\n    fn sign<Token: Serialize>(&self, claims: &Token) -> Result<String, JwtError> {\n        self.private.sign(claims)\n    }\n}\n\n// A TokenValidator is responsible for validating a token and returning the claims.\n// This includes looking up the public key for the issuer and verifying the signature.\n// It is also responsible for enforcing the rules around the claims.\n// For example, this must ensure that the issuer and sub are no longer than 128 bytes.\n#[async_trait]\npub trait TokenValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError>;\n}\n\n#[async_trait]\nimpl<T: TokenValidator + Send + Sync> TokenValidator for Arc<T> {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        (**self).validate_token(token).await\n    }\n}\n\npub struct UnimplementedTokenValidator;\n\n#[async_trait]\nimpl TokenValidator for UnimplementedTokenValidator {\n    async fn validate_token(&self, _token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        Err(TokenValidationError::Other(anyhow::anyhow!(\"Unimplemented\")))\n    }\n}\n\n// This validator accepts any tokens signed with the local key (regardless of issuer).\n// If it is not signed with the local key, we will try to validate it with the OIDC validator.\n// We do this because we sign short lived tokens with different issuers.\npub struct FullTokenValidator<T: TokenValidator + Send + Sync> {\n    pub local_key: DecodingKey,\n    pub local_issuer: Box<str>,\n    pub oidc_validator: T,\n}\n\n#[async_trait]\nimpl<T> TokenValidator for FullTokenValidator<T>\nwhere\n    T: TokenValidator + Send + Sync,\n{\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        let local_key_error = {\n            let first_validator = BasicTokenValidator {\n                public_key: self.local_key.clone(),\n                issuer: None,\n            };\n            match first_validator.validate_token(token).await {\n                Ok(claims) => return Ok(claims),\n                Err(e) => e,\n            }\n        };\n\n        // If that fails, we try the OIDC validator.\n        let issuer = get_raw_issuer(token)?;\n        // If we are the issuer, then we should have already validated the token.\n        // TODO: \"localhost\" should not be hard-coded.\n        if issuer == self.local_issuer {\n            return Err(local_key_error);\n        }\n        self.oidc_validator.validate_token(token).await\n    }\n}\n\npub type DefaultValidator = FullTokenValidator<CachingOidcTokenValidator>;\n\npub fn new_validator(local_key: DecodingKey, local_issuer: Box<str>) -> FullTokenValidator<CachingOidcTokenValidator> {\n    FullTokenValidator {\n        local_key,\n        local_issuer,\n        oidc_validator: CachingOidcTokenValidator::get_default(),\n    }\n}\n\n// This verifies against a given public key and expected issuer.\n// The issuer should only be None if we are checking with a local key.\n// We do that because we signed short-lived keys with different issuers.\nstruct BasicTokenValidator {\n    pub public_key: DecodingKey,\n    pub issuer: Option<Box<str>>,\n}\n\nlazy_static! {\n    // Eventually we will want to add more required claims.\n    static ref REQUIRED_CLAIMS: Vec<&'static str> = vec![\"sub\", \"iss\"];\n}\n\n#[async_trait]\nimpl TokenValidator for DecodingKey {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        let mut validation = Validation::new(jsonwebtoken::Algorithm::ES256);\n        validation.algorithms = vec![\n            jsonwebtoken::Algorithm::ES256,\n            jsonwebtoken::Algorithm::RS256,\n            jsonwebtoken::Algorithm::HS256,\n        ];\n        validation.set_required_spec_claims(&REQUIRED_CLAIMS);\n\n        // TODO: We should require a specific audience at some point.\n        validation.validate_aud = false;\n\n        let data = decode::<IncomingClaims>(token, self, &validation)?;\n        let claims = data.claims;\n        claims.try_into().map_err(TokenValidationError::Other)\n    }\n}\n\n#[async_trait]\nimpl TokenValidator for BasicTokenValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        // This validates everything but the issuer.\n        let claims = self.public_key.validate_token(token).await?;\n        if let Some(expected_issuer) = &self.issuer\n            && *claims.issuer != **expected_issuer\n        {\n            return Err(TokenValidationError::Other(anyhow::anyhow!(\n                \"Issuer mismatch: got {:?}, expected {:?}\",\n                claims.issuer,\n                expected_issuer\n            )));\n        }\n        Ok(claims)\n    }\n}\n\n// Validates tokens by looking up public keys and caching them.\npub struct CachingOidcTokenValidator {\n    cache: async_cache::AsyncCache<Arc<JwksValidator>, KeyFetcher>,\n}\n\nimpl CachingOidcTokenValidator {\n    pub fn new(refresh_duration: Duration, expiry: Option<Duration>) -> Self {\n        let cache = async_cache::Options::new(refresh_duration, KeyFetcher)\n            .with_expire(expiry)\n            .build();\n        CachingOidcTokenValidator { cache }\n    }\n\n    pub fn get_default() -> Self {\n        Self::new(Duration::from_secs(300), Some(Duration::from_secs(7200)))\n    }\n}\n\n// Jwks fetcher for the async cache.\nstruct KeyFetcher;\n\nimpl async_cache::Fetcher<Arc<JwksValidator>> for KeyFetcher {\n    type Error = TokenValidationError;\n\n    async fn fetch(&self, key: FastStr) -> Result<Arc<JwksValidator>, Self::Error> {\n        // TODO: Make this stored in the struct so we don't need to keep creating it.\n        let raw_issuer = key.deref();\n        log::info!(\"Fetching key for issuer {}\", raw_issuer);\n        let oidc_url = format!(\"{}/.well-known/openid-configuration\", raw_issuer.trim_end_matches('/'));\n        let key_or_error = Jwks::from_oidc_url(oidc_url).await;\n        // TODO: We should probably add debouncing to avoid spamming the logs.\n        // Alternatively we could add a backoff before retrying.\n        if let Err(e) = &key_or_error {\n            log::warn!(\"Error fetching public key for issuer {raw_issuer}: {e:?}\");\n        }\n        let keys = key_or_error?;\n        let validator = JwksValidator {\n            issuer: raw_issuer.into(),\n            keyset: keys,\n        };\n        Ok(Arc::new(validator))\n    }\n}\n\n#[async_trait]\nimpl TokenValidator for CachingOidcTokenValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        let raw_issuer = get_raw_issuer(token)?;\n        log::debug!(\"Getting validator for issuer {}\", raw_issuer.clone());\n        let validator = self\n            .cache\n            .get(String::from(raw_issuer.clone()).into())\n            .await\n            .ok_or_else(|| anyhow::anyhow!(\"Error fetching public key for issuer {raw_issuer}\"))?;\n        validator.validate_token(token).await\n    }\n}\n\n// This is a token validator that uses OIDC to validate tokens.\n// This will look up the public key for the issuer and validate against that key.\n// This currently has no caching.\npub struct OidcTokenValidator;\n\n// Get the issuer out of a token without validating the signature.\nfn get_raw_issuer(token: &str) -> Result<Box<str>, TokenValidationError> {\n    let mut validation = Validation::new(jsonwebtoken::Algorithm::ES256);\n    validation.set_required_spec_claims(&REQUIRED_CLAIMS);\n    validation.validate_aud = false;\n    // We are disabling signature validation, because we need to get the issuer before we can validate.\n    validation.insecure_disable_signature_validation();\n    let data = decode::<IncomingClaims>(token, &DecodingKey::from_secret(b\"fake\"), &validation)?;\n    Ok(data.claims.issuer)\n}\n\n#[async_trait]\nimpl TokenValidator for OidcTokenValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        // TODO: Make this stored in the struct so we don't need to keep creating it.\n        let raw_issuer = get_raw_issuer(token)?;\n        let oidc_url = format!(\"{}/.well-known/openid-configuration\", raw_issuer.trim_end_matches('/'));\n        log::debug!(\"Fetching key for issuer {}\", raw_issuer.clone());\n        let key_or_error = Jwks::from_oidc_url(oidc_url).await;\n        // TODO: We should probably add debouncing to avoid spamming the logs.\n        // Alternatively we could add a backoff before retrying.\n        if let Err(e) = &key_or_error {\n            log::warn!(\"Error fetching public key for issuer {raw_issuer}: {e:?}\");\n        }\n        let keys = key_or_error?;\n        let validator = JwksValidator {\n            issuer: raw_issuer,\n            keyset: keys,\n        };\n        validator.validate_token(token).await\n    }\n}\n\nstruct JwksValidator {\n    pub issuer: Box<str>,\n    pub keyset: Jwks,\n}\n\n#[async_trait]\nimpl TokenValidator for JwksValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        let header = decode_header(token)?;\n        if let Some(kid) = header.kid {\n            let key = self\n                .keyset\n                .keys\n                .get(&kid)\n                .ok_or_else(|| TokenValidationError::KeyIDNotFound)?;\n            let validator = BasicTokenValidator {\n                public_key: key.decoding_key.clone(),\n                issuer: Some(self.issuer.clone()),\n            };\n            return validator.validate_token(token).await;\n        }\n        log::debug!(\"No key id in header. Trying all keys.\");\n        // TODO: Consider returning an error if no kid is given?\n        // For now, lets just try all the keys.\n        let mut last_error = TokenValidationError::Other(anyhow::anyhow!(\"No kid found\"));\n        for (kid, key) in &self.keyset.keys {\n            log::debug!(\"Trying key {kid}\");\n            let validator = BasicTokenValidator {\n                public_key: key.decoding_key.clone(),\n                issuer: Some(self.issuer.clone()),\n            };\n            match validator.validate_token(token).await {\n                Ok(claims) => return Ok(claims),\n                Err(e) => {\n                    last_error = e;\n                    log::debug!(\"Validating with key {kid} failed\");\n                    continue;\n                }\n            }\n        }\n        // None of the keys worked.\n        Err(last_error)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use crate::auth::identity::{IncomingClaims, SpacetimeIdentityClaims};\n    use crate::auth::token_validation::{\n        BasicTokenValidator, CachingOidcTokenValidator, FullTokenValidator, OidcTokenValidator, TokenSigner,\n        TokenValidator,\n    };\n    use crate::auth::JwtKeys;\n    use base64::Engine;\n    use openssl::ec::{EcGroup, EcKey};\n    use serde_json;\n    use spacetimedb_lib::Identity;\n\n    #[tokio::test]\n    async fn test_local_validator_checks_issuer() -> anyhow::Result<()> {\n        // Test that the issuer must match the expected issuer for LocalTokenValidator.\n        let kp = JwtKeys::generate()?;\n        let issuer = \"test1\";\n        let subject = \"test_subject\";\n\n        let orig_claims = IncomingClaims {\n            identity: None,\n            subject: subject.into(),\n            issuer: issuer.into(),\n            audience: [].into(),\n            iat: std::time::SystemTime::now(),\n            exp: None,\n            extra: None,\n        };\n        let token = kp.private.sign(&orig_claims)?;\n\n        {\n            // Test that we can validate it.\n            let validator = BasicTokenValidator {\n                public_key: kp.public.clone(),\n                issuer: Some(issuer.into()),\n            };\n\n            let parsed_claims: SpacetimeIdentityClaims = validator.validate_token(&token).await?;\n            assert_eq!(&*parsed_claims.issuer, issuer);\n            assert_eq!(&*parsed_claims.subject, subject);\n            assert_eq!(parsed_claims.identity, Identity::from_claims(issuer, subject));\n        }\n        {\n            // Now try with the wrong expected issuer.\n            let validator = BasicTokenValidator {\n                public_key: kp.public.clone(),\n                issuer: Some(\"otherissuer\".into()),\n            };\n\n            assert!(validator.validate_token(&token).await.is_err());\n        }\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_local_validator_checks_key() -> anyhow::Result<()> {\n        // Test that the decoding key must work for LocalTokenValidator.\n        let kp = JwtKeys::generate()?;\n        let issuer = \"test1\";\n        let subject = \"test_subject\";\n\n        let orig_claims = IncomingClaims {\n            identity: None,\n            subject: subject.into(),\n            issuer: issuer.into(),\n            audience: [].into(),\n            iat: std::time::SystemTime::now(),\n            exp: None,\n            extra: None,\n        };\n        let token = kp.private.sign(&orig_claims)?;\n\n        {\n            // Test that we can validate it.\n            let validator = BasicTokenValidator {\n                public_key: kp.public.clone(),\n                issuer: Some(issuer.into()),\n            };\n\n            let parsed_claims: SpacetimeIdentityClaims = validator.validate_token(&token).await?;\n            assert_eq!(&*parsed_claims.issuer, issuer);\n            assert_eq!(&*parsed_claims.subject, subject);\n            assert_eq!(parsed_claims.identity, Identity::from_claims(issuer, subject));\n        }\n        {\n            // We generate a new keypair and try to decode with that key.\n            let other_kp = JwtKeys::generate()?;\n            // Now try with the wrong expected issuer.\n            let validator = BasicTokenValidator {\n                public_key: other_kp.public.clone(),\n                issuer: Some(\"otherissuer\".into()),\n            };\n\n            assert!(validator.validate_token(&token).await.is_err());\n        }\n\n        Ok(())\n    }\n\n    async fn assert_validation_fails<T: TokenValidator>(validator: &T, token: &str) -> anyhow::Result<()> {\n        let result = validator.validate_token(token).await;\n        if let Ok(claims) = result {\n            anyhow::bail!(\"Validation succeeded when it should have failed: {:?}\", claims);\n        }\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn resigned_token_ignores_issuer() -> anyhow::Result<()> {\n        // Test that the decoding key must work for LocalTokenValidator.\n        let kp = JwtKeys::generate()?;\n        let local_issuer = \"test1\";\n        let external_issuer = \"other_issuer\";\n        let subject = \"test_subject\";\n\n        let orig_claims = IncomingClaims {\n            identity: None,\n            subject: subject.into(),\n            issuer: external_issuer.into(),\n            audience: [].into(),\n            iat: std::time::SystemTime::now(),\n            exp: None,\n            extra: None,\n        };\n        let token = kp.private.sign(&orig_claims)?;\n\n        // First, try the successful case with the FullTokenValidator.\n        {\n            let validator = FullTokenValidator {\n                local_key: kp.public.clone(),\n                local_issuer: local_issuer.into(),\n                oidc_validator: OidcTokenValidator,\n            };\n\n            let parsed_claims: SpacetimeIdentityClaims = validator.validate_token(&token).await?;\n            assert_eq!(&*parsed_claims.issuer, external_issuer);\n            assert_eq!(&*parsed_claims.subject, subject);\n            assert_eq!(parsed_claims.identity, Identity::from_claims(external_issuer, subject));\n        }\n        // Double check that this token would fail with an OidcTokenValidator.\n        assert_validation_fails(&OidcTokenValidator, &token).await?;\n        // Double check that validation fails if we check the issuer.\n        assert_validation_fails(\n            &BasicTokenValidator {\n                public_key: kp.public.clone(),\n                issuer: Some(local_issuer.into()),\n            },\n            &token,\n        )\n        .await?;\n        Ok(())\n    }\n\n    use axum::routing::get;\n    use axum::Json;\n    use axum::Router;\n    use tokio::net::TcpListener;\n    use tokio::sync::oneshot;\n\n    use serde::{Deserialize, Serialize};\n    #[derive(Deserialize, Serialize, Clone)]\n    struct OIDCConfig {\n        jwks_uri: String,\n    }\n\n    async fn oidc_config_handler(config: OIDCConfig) -> Json<OIDCConfig> {\n        Json(config)\n    }\n\n    // You can drop this to shut down the server.\n    // This will host an oidc config at `{base_url}/.well-known/openid-configuration`\n    // It will also host jwks at `{base_url}/jwks.json`\n    struct OIDCServerHandle {\n        pub base_url: String,\n        #[allow(dead_code)]\n        pub shutdown_tx: oneshot::Sender<()>,\n        #[allow(dead_code)]\n        join_handle: tokio::task::JoinHandle<()>,\n    }\n\n    impl OIDCServerHandle {\n        pub async fn start_new(jwks_json: String) -> anyhow::Result<Self> {\n            let listener = TcpListener::bind(\"0.0.0.0:0\").await.unwrap();\n            let addr = listener.local_addr()?;\n            let port = addr.port();\n            let base_url = format!(\"http://localhost:{port}\");\n            let config = OIDCConfig {\n                jwks_uri: format!(\"{base_url}/jwks.json\"),\n            };\n            let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();\n\n            let app = Router::new()\n                .route(\n                    \"/.well-known/openid-configuration\",\n                    get({\n                        let config = config.clone();\n                        move || oidc_config_handler(config.clone())\n                    }),\n                )\n                .route(\n                    \"/jwks.json\",\n                    get({\n                        let jwks = jwks_json.clone();\n                        move || async move { jwks }\n                    }),\n                )\n                .route(\"/ok\", get(|| async move { \"OK\" }));\n\n            // Spawn the server in a background task\n            let join_handle = tokio::spawn(async move {\n                axum::serve(listener, app)\n                    .with_graceful_shutdown(async {\n                        shutdown_rx.await.ok();\n                    })\n                    .await\n                    .unwrap();\n            });\n\n            // Wait for server to be ready\n            let client = reqwest::Client::new();\n            let health_check_url = format!(\"{base_url}/ok\");\n\n            let mut attempts = 0;\n            const MAX_ATTEMPTS: u32 = 10;\n            const DELAY_MS: u64 = 50;\n\n            while attempts < MAX_ATTEMPTS {\n                match client.get(&health_check_url).send().await {\n                    Ok(response) if response.status().is_success() => break,\n                    _ => {\n                        log::debug!(\"Server not ready. Waiting...\");\n                        tokio::time::sleep(Duration::from_millis(DELAY_MS)).await;\n                        attempts += 1;\n                    }\n                }\n            }\n\n            if attempts == MAX_ATTEMPTS {\n                return Err(anyhow::anyhow!(\"Server failed to start after maximum attempts\"));\n            }\n\n            Ok(OIDCServerHandle {\n                base_url,\n                shutdown_tx,\n                join_handle,\n            })\n        }\n    }\n\n    #[derive(Debug, Default, Copy, Clone)]\n    struct TestOptions {\n        pub issuer_trailing_slash: bool,\n    }\n\n    async fn run_oidc_test<T: TokenValidator>(validator: T, opts: &TestOptions) -> anyhow::Result<()> {\n        // We will put 2 keys in the keyset.\n        let mut kp1 = JwtKeys::generate()?;\n        let mut kp2 = JwtKeys::generate()?;\n\n        // Note: our fetcher library requires these, even though they are optional in the spec.\n        // We should replace the jwks fetcher at some point, but most OIDC providers will have these.\n        kp1.kid = Some(\"key1\".to_string());\n        kp2.kid = Some(\"key2\".to_string());\n\n        // We won't put this in the keyset.\n        let invalid_kp = JwtKeys::generate()?;\n\n        let valid_keys: Vec<JwtKeys> = vec![kp1.clone(), kp2.clone()];\n        // let jwks = keyset_to_json(vec![&jk, &kp1])?;\n        let jwks = keyset_to_json(valid_keys)?;\n\n        let handle = OIDCServerHandle::start_new(jwks).await?;\n\n        let issuer = handle.base_url.clone();\n        let issuer = if opts.issuer_trailing_slash {\n            format!(\"{issuer}/\")\n        } else {\n            issuer\n        };\n        let subject = \"test_subject\";\n\n        let orig_claims = IncomingClaims {\n            identity: None,\n            subject: subject.into(),\n            issuer: issuer.clone().into(),\n            audience: [].into(),\n            iat: std::time::SystemTime::now(),\n            exp: None,\n            extra: None,\n        };\n        for kp in [kp1, kp2] {\n            log::debug!(\"Testing with key {:?}\", kp.kid);\n            // TODO: This test should also try using key ids in the token headers.\n            let token = kp.private.sign(&orig_claims)?;\n\n            let validated_claims = validator.validate_token(&token).await?;\n            assert_eq!(&*validated_claims.issuer, &*issuer);\n            assert_eq!(&*validated_claims.subject, subject);\n            assert_eq!(validated_claims.identity, Identity::from_claims(&issuer, subject));\n        }\n\n        let invalid_token = invalid_kp.private.sign(&orig_claims)?;\n        assert!(validator.validate_token(&invalid_token).await.is_err());\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_oidc_flow() -> anyhow::Result<()> {\n        for _ in 0..10 {\n            run_oidc_test(OidcTokenValidator, &Default::default()).await?\n        }\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_issuer_slash() -> anyhow::Result<()> {\n        let opts = TestOptions {\n            issuer_trailing_slash: true,\n        };\n\n        run_oidc_test(OidcTokenValidator, &opts).await?;\n        run_oidc_test(CachingOidcTokenValidator::get_default(), &opts).await?;\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_caching_oidc_flow() -> anyhow::Result<()> {\n        for _ in 0..10 {\n            let v = CachingOidcTokenValidator::get_default();\n            run_oidc_test(v, &Default::default()).await?;\n        }\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_full_validator_fallback() -> anyhow::Result<()> {\n        let kp = JwtKeys::generate()?;\n        let v = FullTokenValidator {\n            local_key: kp.public,\n            local_issuer: \"local_issuer\".into(),\n            oidc_validator: OidcTokenValidator,\n        };\n        run_oidc_test(v, &Default::default()).await\n    }\n\n    /// Convert a set of keys to a JWKS JSON string.\n    fn keyset_to_json<I>(jks: I) -> anyhow::Result<String>\n    where\n        I: IntoIterator<Item = JwtKeys>,\n    {\n        let jks = jks\n            .into_iter()\n            .map(|key| to_jwk_json(&key).unwrap())\n            .collect::<Vec<serde_json::Value>>();\n\n        let j = serde_json::json!({\n            \"keys\": jks,\n        });\n        Ok(j.to_string())\n    }\n\n    // Extract the x and y coordinates from a public key and return a JWK for a single key.\n    fn to_jwk_json(jk: &JwtKeys) -> anyhow::Result<serde_json::Value> {\n        let eck = EcKey::public_key_from_pem(&jk.public_pem)?;\n\n        let group = EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1)?;\n        let mut ctx = openssl::bn::BigNumContext::new()?;\n\n        // Get the x and y coordinates.\n        let mut x = openssl::bn::BigNum::new()?;\n        // let mut x = openssl::bn::BigNumRef\n        let mut y = openssl::bn::BigNum::new()?;\n        eck.public_key().affine_coordinates(&group, &mut x, &mut y, &mut ctx)?;\n\n        let x_bytes = x.to_vec();\n        let y_bytes = y.to_vec();\n\n        let x_padded = if x_bytes.len() < 32 {\n            let mut padded = vec![0u8; 32];\n            padded[32 - x_bytes.len()..].copy_from_slice(&x_bytes);\n            padded\n        } else {\n            x_bytes\n        };\n\n        let y_padded = if y_bytes.len() < 32 {\n            let mut padded = vec![0u8; 32];\n            padded[32 - y_bytes.len()..].copy_from_slice(&y_bytes);\n            padded\n        } else {\n            y_bytes\n        };\n        let x_b64 = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(x_padded);\n        let y_b64 = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(y_padded);\n\n        let mut jwks = serde_json::json!(\n            {\n                \"kty\": \"EC\",\n                \"crv\": \"P-256\",\n                \"use\": \"sig\",\n                \"alg\": \"ES256\",\n                \"x\": x_b64,\n                \"y\": y_b64\n            }\n        );\n        if let Some(kid) = &jk.kid {\n            jwks[\"kid\"] = kid.to_string().into();\n        }\n        Ok(jwks)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/callgrind_flag.rs",
    "content": "use std::hint::black_box;\nuse std::sync::atomic::{AtomicU32, Ordering};\n\n/// This module allows temporarily enabling callgrind in async SpacetimeDB code, guarded by a global enable flag.\n///\n/// Our basic problem is that callgrind loses track of control flow when work is sent through a channel across threads.\n/// Callgrind has a \"toggle-collect\" option that allows toggling event recording at a function boundary.\n/// So, as long as we wrap code we're interested in with a marker function, we can record across threads.\n///\n/// However, there's a problems with using this: it will track ANY time that function is called, including during warmup!\n/// We want to run our functions twice: once to warm up, and once to measure.\n///\n/// Our solution is to wrap the code of interest in a function that is only called when the global flag is set.\n///\n/// See: documentation on valgrind/callgrind/iai-callgrind's `toggle-collect` option (ctrl-f on these pages):\n/// - https://github.com/iai-callgrind/iai-callgrind/\n/// - https://valgrind.org/docs/manual/cl-manual.html\n///\n/// We do NOT use the valgrind macros (or the crate https://github.com/2dav/crabgrind) because they are a pain to build.\n/// (Hours wasted here: 9.)\n/// Instead, we have a wrapper function which is only called when a global flag is set.\n///\n/// To use this module, you need to do several things:\n/// 1. Wrap your target code of interest with `invoke_allowing_callgrind`.\n/// 2. In code that invokes that (possibly from another thread), wrap the code of interest with `enable_callgrind_globally`.\n/// 3. Invoke callgrind on your executable with the flags:\n///    `--collect-atstart=no --toggle-collect=`spacetimedb::callgrind_flag::flag*`\n///    Or, if using our fork of iai callgrind (https://github.com/clockworklabs/iai-callgrind), use:\n///    `LibraryBenchmarkConfig::default().with_custom_entry_point(\"spacetimedb::callgrind_flag::flag\")`;\nstatic CALLGRIND_ENABLED: AtomicU32 = AtomicU32::new(0);\n\n/// Invoke a function, enabling callgrind on all threads.\n/// If not running under valgrind, this simply invokes the function.\npub fn enable_callgrind_globally<T, F: FnOnce() -> T>(f: F) -> T {\n    CALLGRIND_ENABLED.fetch_add(1, Ordering::Release);\n    let result = black_box(flag(black_box(f)));\n    CALLGRIND_ENABLED.fetch_sub(1, Ordering::Release);\n    result\n}\n\n/// Invoke a function, allowing callgrind instrumentation.\n/// For instrumentation to be active, callgrind must be enabled globally, and\n/// the executable must be running under callgrind.\n/// If not running under callgrind, this just invokes the passed function.\npub fn invoke_allowing_callgrind<T, F: FnOnce() -> T>(f: F) -> T {\n    if CALLGRIND_ENABLED.load(Ordering::Acquire) > 0 {\n        black_box(flag(f))\n    } else {\n        black_box(f())\n    }\n}\n\n/// The magic juice.\n/// This is what you tell callgrind to look for.\n/// Don't change the name of this function (or the name of this module!) without updating\n/// the benchmarks crate.\n#[inline(never)]\nfn flag<T, F: FnOnce() -> T>(f: F) -> T {\n    black_box(f())\n}\n"
  },
  {
    "path": "crates/core/src/client/client_connection.rs",
    "content": "use std::collections::VecDeque;\nuse std::future::poll_fn;\nuse std::ops::Deref;\nuse std::sync::atomic::Ordering;\nuse std::sync::atomic::{AtomicBool, Ordering::Relaxed};\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse std::time::{Instant, SystemTime};\n\nuse super::messages::{OneOffQueryResponseMessage, ProcedureResultMessage};\nuse super::{message_handlers, ClientActorId, MessageHandleError, OutboundMessage};\nuse crate::db::relational_db::RelationalDB;\nuse crate::error::DBError;\nuse crate::host::module_host::ClientConnectedError;\nuse crate::host::{\n    CallProcedureReturn, FunctionArgs, ModuleHost, NoSuchModule, ProcedureCallResult, ReducerCallError,\n    ReducerCallResult,\n};\nuse crate::subscription::module_subscription_manager::BroadcastError;\nuse crate::subscription::row_list_builder_pool::JsonRowListBuilderFakePool;\nuse crate::util::asyncify;\nuse crate::util::prometheus_handle::IntGaugeExt;\nuse crate::worker_metrics::WORKER_METRICS;\nuse bytes::Bytes;\nuse bytestring::ByteString;\nuse derive_more::From;\nuse futures::prelude::*;\nuse prometheus::{Histogram, IntCounter, IntGauge};\nuse spacetimedb_auth::identity::{ConnectionAuthCtx, SpacetimeIdentityClaims};\nuse spacetimedb_client_api_messages::websocket::{common as ws_common, v1 as ws_v1, v2 as ws_v2};\nuse spacetimedb_durability::{DurableOffset, TxOffset};\nuse spacetimedb_lib::identity::{AuthCtx, RequestId};\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::{bsatn, Identity, TimeDuration, Timestamp};\nuse tokio::sync::mpsc::error::{SendError, TrySendError};\nuse tokio::sync::{mpsc, oneshot, watch};\nuse tokio::task::AbortHandle;\nuse tracing::{trace, warn};\n\n#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]\npub enum Protocol {\n    Text,\n    Binary,\n}\n\n#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]\npub enum WsVersion {\n    V1,\n    V2,\n}\n\nimpl Protocol {\n    pub fn as_str(self) -> &'static str {\n        match self {\n            Protocol::Text => \"text\",\n            Protocol::Binary => \"binary\",\n        }\n    }\n\n    pub(crate) fn assert_matches_format_switch<B, J>(self, fs: &ws_v1::FormatSwitch<B, J>) {\n        match (self, fs) {\n            (Protocol::Text, ws_v1::FormatSwitch::Json(_)) | (Protocol::Binary, ws_v1::FormatSwitch::Bsatn(_)) => {}\n            _ => unreachable!(\"requested protocol does not match output format\"),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct ClientConfig {\n    /// The client's desired protocol (format) when the host replies.\n    pub protocol: Protocol,\n    /// The websocket protocol version negotiated during the handshake.\n    pub version: WsVersion,\n    /// The client's desired (conditional) compression algorithm, if any.\n    pub compression: ws_common::Compression,\n    /// Whether the client prefers full [`TransactionUpdate`]s\n    /// rather than  [`TransactionUpdateLight`]s on a successful update.\n    // TODO(centril): As more knobs are added, make this into a bitfield (when there's time).\n    pub tx_update_full: bool,\n    /// If `true`, the client requests to receive updates for transactions\n    /// confirmed to be durable. If `false`, updates will be delivered\n    /// immediately.\n    pub confirmed_reads: bool,\n}\n\nimpl ClientConfig {\n    pub fn for_test() -> ClientConfig {\n        Self {\n            protocol: Protocol::Binary,\n            version: WsVersion::V1,\n            compression: <_>::default(),\n            tx_update_full: true,\n            confirmed_reads: false,\n        }\n    }\n}\n\n/// A message to be sent to the client, along with the transaction offset it\n/// was computed at, if available.\n///\n// TODO: Consider a different name, \"ClientUpdate\" is used elsewhere already.\n#[derive(Debug)]\nstruct ClientUpdate {\n    /// Transaction offset at which `message` was computed.\n    ///\n    /// This is only `Some` if `message` is a query result.\n    ///\n    /// If `Some` and [`ClientConfig::confirmed_reads`] is `true`,\n    /// [`ClientConnectionReceiver`] will delay delivery until the durable\n    /// offset of the database is equal to or greater than `tx_offset`.\n    pub tx_offset: Option<TxOffset>,\n    /// Type-erased outgoing message.\n    pub message: OutboundMessage,\n}\n\n/// Types with access to the [`DurableOffset`] of a database.\n///\n/// Provided implementors are [`watch::Receiver<ModuleHost>`] and [`RelationalDB`].\n///\n/// The latter is mostly useful for tests, where no managed [`ModuleHost`] is\n/// available, while the former supports module hotswapping.\npub trait DurableOffsetSupply: Send {\n    /// Obtain the current [`DurableOffset`] handle.\n    ///\n    /// Returns:\n    ///\n    /// - `Err(NoSuchModule)` if the database was shut down\n    /// - `Ok(None)` if the database is configured without durability\n    /// - `Ok(Some(DurableOffset))` otherwise\n    ///\n    fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule>;\n}\n\nimpl DurableOffsetSupply for watch::Receiver<ModuleHost> {\n    fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule> {\n        let module = if self.has_changed().map_err(|_| NoSuchModule)? {\n            self.borrow_and_update()\n        } else {\n            self.borrow()\n        };\n\n        Ok(module.replica_ctx().relational_db.durable_tx_offset())\n    }\n}\n\nimpl DurableOffsetSupply for Arc<RelationalDB> {\n    fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule> {\n        Ok(self.durable_tx_offset())\n    }\n}\n\n/// Receiving end of [`ClientConnectionSender`].\n///\n/// The [`ClientConnection`] actor reads messages from this channel and sends\n/// them to the client over its websocket connection.\n///\n/// The [`ClientConnectionReceiver`] takes care of confirmed reads semantics,\n/// if requested by the client.\npub struct ClientConnectionReceiver {\n    confirmed_reads: bool,\n    channel: MeteredReceiver<ClientUpdate>,\n    current: Option<ClientUpdate>,\n    offset_supply: Box<dyn DurableOffsetSupply>,\n}\n\nimpl ClientConnectionReceiver {\n    fn new(\n        confirmed_reads: bool,\n        channel: MeteredReceiver<ClientUpdate>,\n        offset_supply: impl DurableOffsetSupply + 'static,\n    ) -> Self {\n        Self {\n            confirmed_reads,\n            channel,\n            current: None,\n            offset_supply: Box::new(offset_supply),\n        }\n    }\n\n    /// Receive the next message from this channel.\n    ///\n    /// If this method returns `None`, the channel is closed and no more messages\n    /// are in the internal buffers. No more messages can ever be received from\n    /// the channel.\n    ///\n    /// Messages are returned immediately if:\n    ///\n    ///   - The (internal) [`ClientUpdate`] does not have a `tx_offset`\n    ///     (such as for error messages).\n    ///   - The client hasn't requested confirmed reads (i.e.\n    ///     [`ClientConfig::confirmed_reads`] is `false`).\n    ///   - The database is configured to not persist transactions.\n    ///\n    /// Otherwise, the update's `tx_offset` is compared against the module's\n    /// durable offset. If the durable offset is behind the `tx_offset`, the\n    /// method waits until it catches up before returning the message.\n    ///\n    /// If the database is shut down while waiting for the durable offset,\n    /// `None` is returned. In this case, no more messages can ever be received\n    /// from the channel.\n    ///\n    /// # Cancel safety\n    ///\n    /// This method is cancel safe, as long as `self` is not dropped.\n    ///\n    /// If `recv` is used in a [`tokio::select!`] statement, it may get\n    /// cancelled while waiting for the durable offset to catch up. At this\n    /// point, it has already received a value from the underlying channel.\n    /// This value is stored internally, so calling `recv` again will not lose\n    /// data.\n    //\n    // TODO: Can we make a cancel-safe `recv_many` with confirmed reads semantics?\n    pub async fn recv(&mut self) -> Option<OutboundMessage> {\n        let ClientUpdate { tx_offset, message } = match self.current.take() {\n            None => self.channel.recv().await?,\n            Some(update) => update,\n        };\n        if !self.confirmed_reads {\n            return Some(message);\n        }\n\n        if let Some(tx_offset) = tx_offset {\n            match self.offset_supply.durable_offset() {\n                Ok(Some(mut durable)) => {\n                    // Store the current update in case we get cancelled while\n                    // waiting for the durable offset.\n                    self.current = Some(ClientUpdate {\n                        tx_offset: Some(tx_offset),\n                        message,\n                    });\n                    trace!(\"waiting for offset {tx_offset} to become durable\");\n                    durable\n                        .wait_for(tx_offset)\n                        .await\n                        .inspect_err(|_| {\n                            warn!(\"database went away while waiting for durable offset\");\n                        })\n                        .ok()?;\n                    self.current.take().map(|update| update.message)\n                }\n                // Database shut down or crashed.\n                Err(NoSuchModule) => None,\n                // In-memory database.\n                Ok(None) => Some(message),\n            }\n        } else {\n            Some(message)\n        }\n    }\n\n    /// Close the receiver without dropping it.\n    ///\n    /// This is used to notify the [`ClientConnectionSender`] that the receiver\n    /// will not consume any more messages from the channel, usually because the\n    /// connection has been closed or is about to be closed.\n    ///\n    /// After calling this method, the sender will not be able to send more\n    /// messages, preventing the internal buffer from filling up.\n    pub fn close(&mut self) {\n        self.channel.close();\n    }\n}\n\n#[derive(Debug)]\npub struct ClientConnectionSender {\n    pub id: ClientActorId,\n    pub auth: ConnectionAuthCtx,\n    pub config: ClientConfig,\n    sendtx: mpsc::Sender<ClientUpdate>,\n    abort_handle: AbortHandle,\n    cancelled: AtomicBool,\n\n    /// Handles on Prometheus metrics related to connections to this database.\n    ///\n    /// Will be `None` when constructed by [`ClientConnectionSender::dummy_with_channel`]\n    /// or [`ClientConnectionSender::dummy`], which are used in tests.\n    /// Will be `Some` whenever this `ClientConnectionSender` is wired up to an actual client connection.\n    metrics: Option<ClientConnectionMetrics>,\n}\n\n#[derive(Debug)]\npub struct ClientConnectionMetrics {\n    pub websocket_request_msg_size: Histogram,\n    pub websocket_requests: IntCounter,\n\n    /// The `total_outgoing_queue_length` metric labeled with this database's `Identity`,\n    /// which we'll increment whenever sending a message.\n    ///\n    /// This metric will be decremented, and cleaned up,\n    /// by `ws_client_actor_inner` in client-api/src/routes/subscribe.rs.\n    /// Care must be taken not to increment it after the client has disconnected\n    /// and performed its clean-up.\n    pub sendtx_queue_size: IntGauge,\n}\n\nimpl ClientConnectionMetrics {\n    fn new(database_identity: Identity, protocol: Protocol) -> Self {\n        let message_kind = protocol.as_str();\n        let websocket_request_msg_size = WORKER_METRICS\n            .websocket_request_msg_size\n            .with_label_values(&database_identity, message_kind);\n        let websocket_requests = WORKER_METRICS\n            .websocket_requests\n            .with_label_values(&database_identity, message_kind);\n        let sendtx_queue_size = WORKER_METRICS\n            .total_outgoing_queue_length\n            .with_label_values(&database_identity);\n\n        Self {\n            websocket_request_msg_size,\n            websocket_requests,\n            sendtx_queue_size,\n        }\n    }\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum ClientSendError {\n    #[error(\"client disconnected\")]\n    Disconnected,\n    #[error(\"client was not responding and has been disconnected\")]\n    Cancelled,\n}\n\nimpl ClientConnectionSender {\n    pub fn dummy_with_channel(\n        id: ClientActorId,\n        config: ClientConfig,\n        offset_supply: impl DurableOffsetSupply + 'static,\n    ) -> (Self, ClientConnectionReceiver) {\n        let (sendtx, rx) = mpsc::channel(CLIENT_CHANNEL_CAPACITY_TEST);\n        // just make something up, it doesn't need to be attached to a real task\n        let abort_handle = match tokio::runtime::Handle::try_current() {\n            Ok(h) => h.spawn(async {}).abort_handle(),\n            Err(_) => tokio::runtime::Runtime::new().unwrap().spawn(async {}).abort_handle(),\n        };\n\n        let receiver = ClientConnectionReceiver::new(config.confirmed_reads, MeteredReceiver::new(rx), offset_supply);\n        let cancelled = AtomicBool::new(false);\n        let dummy_claims = SpacetimeIdentityClaims {\n            identity: id.identity,\n            subject: \"\".into(),\n            issuer: \"\".into(),\n            audience: [].into(),\n            iat: SystemTime::now(),\n            exp: None,\n            extra: None,\n        };\n        let sender = Self {\n            id,\n            auth: ConnectionAuthCtx::try_from(dummy_claims).expect(\"dummy claims should always be valid\"),\n            config,\n            sendtx,\n            abort_handle,\n            cancelled,\n            metrics: None,\n        };\n        (sender, receiver)\n    }\n\n    pub fn dummy(id: ClientActorId, config: ClientConfig, offset_supply: impl DurableOffsetSupply + 'static) -> Self {\n        Self::dummy_with_channel(id, config, offset_supply).0\n    }\n\n    pub fn is_cancelled(&self) -> bool {\n        self.cancelled.load(Ordering::Relaxed)\n    }\n\n    /// Send a message to the client. For data-related messages, you should probably use\n    /// `BroadcastQueue::send` to ensure that the client sees data messages in a consistent order.\n    ///\n    /// If `message` is the result of evaluating a query, then `tx_offset` should be\n    /// the TX offset of the database state against which the query was evaluated.\n    /// If `message` is not the result of evaluating a query (e.g. it reports an error),\n    /// `tx_offset` should be `None`.\n    /// For clients which have requested only confirmed durable reads,\n    /// the sender will delay sending `message` until the `tx_offset` is confirmed.\n    pub fn send_message(\n        &self,\n        tx_offset: Option<TxOffset>,\n        message: impl Into<OutboundMessage>,\n    ) -> Result<(), ClientSendError> {\n        let message = message.into();\n        debug_assert!(\n            matches!(\n                (&self.config.version, &message),\n                (WsVersion::V1, OutboundMessage::V1(_)) | (WsVersion::V2, OutboundMessage::V2(_))\n            ),\n            \"attempted to send message variant that does not match client websocket version\"\n        );\n        self.send(ClientUpdate { tx_offset, message })\n    }\n\n    fn send(&self, message: ClientUpdate) -> Result<(), ClientSendError> {\n        if self.cancelled.load(Relaxed) {\n            return Err(ClientSendError::Cancelled);\n        }\n\n        match self.sendtx.try_send(message) {\n            Err(mpsc::error::TrySendError::Full(_)) => {\n                // we've hit CLIENT_CHANNEL_CAPACITY messages backed up in\n                // the channel, so forcibly kick the client\n                tracing::warn!(\n                    identity = %self.id.identity,\n                    connection_id = %self.id.connection_id,\n                    confirmed_reads = self.config.confirmed_reads,\n                    \"client channel capacity exceeded\"\n                );\n                log::warn!(\n                    \"Client {:?} exceeded channel capacity of {}, kicking\",\n                    self.id,\n                    self.sendtx.capacity(),\n                );\n                self.abort_handle.abort();\n                self.cancelled.store(true, Ordering::Relaxed);\n                return Err(ClientSendError::Cancelled);\n            }\n            Err(mpsc::error::TrySendError::Closed(_)) => return Err(ClientSendError::Disconnected),\n            Ok(()) => {\n                // If we successfully pushed a message into the queue, increment the queue size metric.\n                // Don't do this before pushing because, if the client has disconnected,\n                // it will already have performed its clean-up,\n                // and so would never perform the corresponding `dec` to this `inc`.\n                if let Some(metrics) = &self.metrics {\n                    metrics.sendtx_queue_size.inc();\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(crate) fn observe_websocket_request_message(&self, message: &DataMessage) {\n        if let Some(metrics) = &self.metrics {\n            metrics.websocket_request_msg_size.observe(message.len() as f64);\n            metrics.websocket_requests.inc();\n        }\n    }\n}\n\n#[derive(Clone)]\n#[non_exhaustive]\npub struct ClientConnection {\n    sender: Arc<ClientConnectionSender>,\n    pub replica_id: u64,\n    module_rx: watch::Receiver<ModuleHost>,\n    auth: AuthCtx,\n}\n\nimpl Deref for ClientConnection {\n    type Target = ClientConnectionSender;\n    fn deref(&self) -> &Self::Target {\n        &self.sender\n    }\n}\n\n#[derive(Debug, From)]\npub enum DataMessage {\n    Text(ByteString),\n    Binary(Bytes),\n}\n\nimpl From<String> for DataMessage {\n    fn from(value: String) -> Self {\n        ByteString::from(value).into()\n    }\n}\n\nimpl From<Vec<u8>> for DataMessage {\n    fn from(value: Vec<u8>) -> Self {\n        Bytes::from(value).into()\n    }\n}\n\nimpl DataMessage {\n    /// Returns the number of bytes this message consists of.\n    pub fn len(&self) -> usize {\n        match self {\n            Self::Text(s) => s.len(),\n            Self::Binary(b) => b.len(),\n        }\n    }\n\n    /// Is the message empty?\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns a handle to the underlying allocation of the message without consuming it.\n    pub fn allocation(&self) -> Bytes {\n        match self {\n            DataMessage::Text(alloc) => alloc.as_bytes().clone(),\n            DataMessage::Binary(alloc) => alloc.clone(),\n        }\n    }\n}\n\n/// Wraps a [VecDeque] with a gauge for tracking its size.\n/// We subtract its size from the gauge on drop to avoid leaking the metric.\npub struct MeteredDeque<T> {\n    inner: VecDeque<T>,\n    gauge: IntGauge,\n}\n\nimpl<T> MeteredDeque<T> {\n    pub fn new(gauge: IntGauge) -> Self {\n        Self {\n            inner: VecDeque::new(),\n            gauge,\n        }\n    }\n\n    pub fn pop_front(&mut self) -> Option<T> {\n        self.inner.pop_front().inspect(|_| {\n            self.gauge.dec();\n        })\n    }\n\n    pub fn pop_back(&mut self) -> Option<T> {\n        self.inner.pop_back().inspect(|_| {\n            self.gauge.dec();\n        })\n    }\n\n    pub fn push_front(&mut self, value: T) {\n        self.gauge.inc();\n        self.inner.push_front(value);\n    }\n\n    pub fn push_back(&mut self, value: T) {\n        self.gauge.inc();\n        self.inner.push_back(value);\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.inner.is_empty()\n    }\n}\n\nimpl<T> Drop for MeteredDeque<T> {\n    fn drop(&mut self) {\n        // Record the number of elements still in the deque on drop\n        self.gauge.sub(self.inner.len() as _);\n    }\n}\n\n/// Wraps the receiving end of a channel with a gauge for tracking the size of the channel.\n/// We subtract the size of the channel from the gauge on drop to avoid leaking the metric.\npub struct MeteredReceiver<T> {\n    inner: mpsc::Receiver<T>,\n    gauge: Option<IntGauge>,\n}\n\nimpl<T> MeteredReceiver<T> {\n    pub fn new(inner: mpsc::Receiver<T>) -> Self {\n        Self { inner, gauge: None }\n    }\n\n    pub fn with_gauge(inner: mpsc::Receiver<T>, gauge: IntGauge) -> Self {\n        Self {\n            inner,\n            gauge: Some(gauge),\n        }\n    }\n\n    pub async fn recv(&mut self) -> Option<T> {\n        poll_fn(|cx| self.poll_recv(cx)).await\n    }\n\n    pub async fn recv_many(&mut self, buf: &mut Vec<T>, max: usize) -> usize {\n        poll_fn(|cx| self.poll_recv_many(cx, buf, max)).await\n    }\n\n    pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {\n        self.inner.poll_recv(cx).map(|maybe_item| {\n            maybe_item.inspect(|_| {\n                if let Some(gauge) = &self.gauge {\n                    gauge.dec()\n                }\n            })\n        })\n    }\n\n    pub fn poll_recv_many(&mut self, cx: &mut Context<'_>, buf: &mut Vec<T>, max: usize) -> Poll<usize> {\n        self.inner.poll_recv_many(cx, buf, max).map(|n| {\n            if let Some(gauge) = &self.gauge {\n                gauge.sub(n as _);\n            }\n            n\n        })\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.inner.is_empty()\n    }\n\n    pub fn close(&mut self) {\n        self.inner.close();\n    }\n}\n\nimpl<T> Drop for MeteredReceiver<T> {\n    fn drop(&mut self) {\n        // Record the number of elements still in the channel on drop\n        if let Some(gauge) = &self.gauge {\n            gauge.sub(self.inner.len() as _);\n        }\n    }\n}\n\n/// Wraps the transmitting end of a channel with a gauge for tracking the size of the channel.\npub struct MeteredSender<T> {\n    inner: mpsc::Sender<T>,\n    gauge: Option<IntGauge>,\n}\n\nimpl<T> MeteredSender<T> {\n    pub fn new(inner: mpsc::Sender<T>) -> Self {\n        Self { inner, gauge: None }\n    }\n\n    pub fn with_gauge(inner: mpsc::Sender<T>, gauge: IntGauge) -> Self {\n        Self {\n            inner,\n            gauge: Some(gauge),\n        }\n    }\n\n    pub async fn send(&mut self, value: T) -> Result<(), SendError<T>> {\n        self.inner.send(value).await?;\n        if let Some(gauge) = &self.gauge {\n            gauge.inc();\n        }\n        Ok(())\n    }\n\n    pub fn try_send(&mut self, value: T) -> Result<(), TrySendError<T>> {\n        self.inner.try_send(value)?;\n        if let Some(gauge) = &self.gauge {\n            gauge.inc();\n        }\n        Ok(())\n    }\n}\n\n// if a client racks up this many messages in the queue without ACK'ing\n// anything, we boot 'em.\nconst CLIENT_CHANNEL_CAPACITY: usize = 16 * KB;\n// use a smaller value for tests\nconst CLIENT_CHANNEL_CAPACITY_TEST: usize = 8;\n\nconst KB: usize = 1024;\n\n/// Value returned by [`ClientConnection::call_client_connected_maybe_reject`]\n/// and consumed by [`ClientConnection::spawn`] which acts as a proof that the client is authorized.\n///\n/// Because this struct does not capture the module or database info or the client connection info,\n/// a malicious caller could [`ClientConnected::call_client_connected_maybe_reject`] for one client\n/// and then use the resulting `Connected` token to [`ClientConnection::spawn`] for a different client.\n/// We're not particularly worried about that.\n/// This token exists as a sanity check that non-malicious callers don't accidentally [`ClientConnection::spawn`]\n/// for an unauthorized client.\n#[non_exhaustive]\npub struct Connected {\n    _private: (),\n}\n\nimpl ClientConnection {\n    /// Call the database at `module_rx`'s `client_connection` reducer, if any,\n    /// and return `Err` if it signals rejecting this client's connection.\n    ///\n    /// Call this method before [`Self::spawn`]\n    /// and pass the returned [`Connected`] to [`Self::spawn`] as proof that the client is authorized.\n    pub async fn call_client_connected_maybe_reject(\n        module_rx: &mut watch::Receiver<ModuleHost>,\n        id: ClientActorId,\n        auth: ConnectionAuthCtx,\n    ) -> Result<Connected, ClientConnectedError> {\n        let module = module_rx.borrow_and_update().clone();\n        module.call_identity_connected(auth, id.connection_id).await?;\n        Ok(Connected { _private: () })\n    }\n\n    /// Spawn a new [`ClientConnection`] for a WebSocket subscriber.\n    ///\n    /// Callers should first call [`Self::call_client_connected_maybe_reject`]\n    /// to verify that the database at `module_rx` approves of this connection,\n    /// and should not invoke this method if that call returns an error,\n    /// and pass the returned [`Connected`] as `_proof_of_client_connected_call`.\n    #[allow(clippy::too_many_arguments)]\n    pub async fn spawn<Fut>(\n        id: ClientActorId,\n        auth: ConnectionAuthCtx,\n        sql_auth: AuthCtx,\n        config: ClientConfig,\n        replica_id: u64,\n        mut module_rx: watch::Receiver<ModuleHost>,\n        actor: impl FnOnce(ClientConnection, ClientConnectionReceiver) -> Fut,\n        _proof_of_client_connected_call: Connected,\n    ) -> ClientConnection\n    where\n        Fut: Future<Output = ()> + Send + 'static,\n    {\n        // Add this client as a subscriber\n        // TODO: Right now this is connecting clients directly to a replica, but their requests should be\n        // logically subscribed to the database, not any particular replica. We should handle failover for\n        // them and stuff. Not right now though.\n        let module = module_rx.borrow_and_update().clone();\n\n        let (sendtx, sendrx) = mpsc::channel::<ClientUpdate>(CLIENT_CHANNEL_CAPACITY);\n\n        let (fut_tx, fut_rx) = oneshot::channel::<Fut>();\n        // weird dance so that we can get an abort_handle into ClientConnection\n        let module_info = module.info.clone();\n        let database_identity = module_info.database_identity;\n        let client_identity = id.identity;\n        let abort_handle = tokio::spawn(async move {\n            let Ok(fut) = fut_rx.await else { return };\n\n            let _gauge_guard = module_info.metrics.connected_clients.inc_scope();\n            module_info.metrics.ws_clients_spawned.inc();\n            scopeguard::defer! {\n                let database_identity = module_info.database_identity;\n                log::warn!(\"websocket connection aborted for client identity `{client_identity}` and database identity `{database_identity}`\");\n                module_info.metrics.ws_clients_aborted.inc();\n            };\n\n            fut.await\n        })\n        .abort_handle();\n\n        let metrics = ClientConnectionMetrics::new(database_identity, config.protocol);\n        let receiver = ClientConnectionReceiver::new(\n            config.confirmed_reads,\n            MeteredReceiver::with_gauge(sendrx, metrics.sendtx_queue_size.clone()),\n            module_rx.clone(),\n        );\n\n        let sender = Arc::new(ClientConnectionSender {\n            id,\n            auth,\n            config,\n            sendtx,\n            abort_handle,\n            cancelled: AtomicBool::new(false),\n            metrics: Some(metrics),\n        });\n        let this = Self {\n            sender,\n            replica_id,\n            module_rx,\n            auth: sql_auth,\n        };\n\n        let actor_fut = actor(this.clone(), receiver);\n        // if this fails, the actor() function called .abort(), which like... okay, I guess?\n        let _ = fut_tx.send(actor_fut);\n\n        this\n    }\n\n    pub fn dummy(\n        id: ClientActorId,\n        config: ClientConfig,\n        replica_id: u64,\n        module_rx: watch::Receiver<ModuleHost>,\n    ) -> Self {\n        let auth = AuthCtx::new(module_rx.borrow().database_info().database_identity, id.identity);\n        Self {\n            sender: Arc::new(ClientConnectionSender::dummy(id, config, module_rx.clone())),\n            replica_id,\n            module_rx,\n            auth,\n        }\n    }\n\n    pub fn sender(&self) -> Arc<ClientConnectionSender> {\n        self.sender.clone()\n    }\n\n    /// Get the [`ModuleHost`] for this connection.\n    ///\n    /// Note that modules can be hotswapped, in which case the returned handle\n    /// becomes invalid (i.e. all calls on it will result in an error).\n    /// Callers should thus drop the value as soon as they are done, and obtain\n    /// a fresh one when needed.\n    ///\n    /// While this [`ClientConnection`] is active, [`Self::watch_module_host`]\n    /// should be polled in the background, and the connection closed if and\n    /// when it returns an error.\n    pub fn module(&self) -> ModuleHost {\n        self.module_rx.borrow().clone()\n    }\n\n    #[inline]\n    pub fn handle_message(\n        &self,\n        message: impl Into<DataMessage>,\n        timer: Instant,\n    ) -> impl Future<Output = Result<(), MessageHandleError>> + '_ {\n        message_handlers::handle(self, message.into(), timer)\n    }\n\n    /// Waits until the [`ModuleHost`] of this [`ClientConnection`] instance\n    /// exits, in which case `Err` containing [`NoSuchModule`] is returned.\n    ///\n    /// Should be polled while this [`ClientConnection`] is active, so as to be\n    /// able to shut down the connection gracefully if and when the module\n    /// exits.\n    ///\n    /// Note that this borrows `self` mutably, so may require cloning the\n    /// [`ClientConnection`] instance. The module is shared, however, so all\n    /// clones will observe a swapped module.\n    pub async fn watch_module_host(&mut self) -> Result<(), NoSuchModule> {\n        loop {\n            // First check if the module exited between creating the client\n            // connection and calling `watch_module_host`...\n            if self.module_rx.changed().await.is_err() {\n                return Err(NoSuchModule);\n            }\n            // ...then mark the current module as seen, so the next iteration\n            // of the loop waits until the module changes or exits.\n            self.module_rx.mark_unchanged();\n        }\n    }\n\n    pub async fn call_reducer(\n        &self,\n        reducer: &str,\n        args: FunctionArgs,\n        request_id: RequestId,\n        timer: Instant,\n        flags: ws_v1::CallReducerFlags,\n    ) -> Result<ReducerCallResult, ReducerCallError> {\n        let caller = match flags {\n            ws_v1::CallReducerFlags::FullUpdate => Some(self.sender()),\n            // Setting `sender = None` causes `eval_updates` to skip sending to the caller\n            // as it has no access to the caller other than by id/connection id.\n            ws_v1::CallReducerFlags::NoSuccessNotify => None,\n        };\n\n        self.module()\n            .call_reducer(\n                self.id.identity,\n                Some(self.id.connection_id),\n                caller,\n                Some(request_id),\n                Some(timer),\n                reducer,\n                args,\n            )\n            .await\n    }\n\n    pub async fn call_reducer_v2(\n        &self,\n        reducer: &str,\n        args: Bytes,\n        request_id: RequestId,\n        timer: Instant,\n        _flags: ws_v2::CallReducerFlags,\n    ) -> Result<ReducerCallResult, ReducerCallError> {\n        self.module()\n            .call_reducer(\n                self.id.identity,\n                Some(self.id.connection_id),\n                Some(self.sender()),\n                Some(request_id),\n                Some(timer),\n                reducer,\n                FunctionArgs::Bsatn(args),\n            )\n            .await\n    }\n\n    pub async fn call_procedure(\n        &self,\n        procedure: &str,\n        args: FunctionArgs,\n        request_id: RequestId,\n        timer: Instant,\n    ) -> Result<(), BroadcastError> {\n        let CallProcedureReturn { result, tx_offset } = self\n            .module()\n            .call_procedure(\n                self.id.identity,\n                Some(self.id.connection_id),\n                Some(timer),\n                procedure,\n                args,\n            )\n            .await;\n\n        let message = ProcedureResultMessage::from_result(&result, request_id);\n\n        self.module()\n            .subscriptions()\n            .send_procedure_message(self.sender(), message, tx_offset)\n    }\n\n    pub async fn call_procedure_v2(\n        &self,\n        procedure: &str,\n        args: Bytes,\n        request_id: RequestId,\n        timer: Instant,\n        _flags: ws_v2::CallProcedureFlags,\n    ) -> Result<(), BroadcastError> {\n        let CallProcedureReturn { result, tx_offset } = self\n            .module()\n            .call_procedure(\n                self.id.identity,\n                Some(self.id.connection_id),\n                Some(timer),\n                procedure,\n                FunctionArgs::Bsatn(args),\n            )\n            .await;\n\n        let (status, timestamp, execution_duration) = match result {\n            Ok(ProcedureCallResult {\n                return_val,\n                execution_duration,\n                start_timestamp,\n            }) => (\n                ws_v2::ProcedureStatus::Returned(\n                    bsatn::to_vec(&return_val)\n                        .expect(\"Procedure return value failed to serialize to BSATN\")\n                        .into(),\n                ),\n                start_timestamp,\n                TimeDuration::from(execution_duration),\n            ),\n            Err(err) => (\n                ws_v2::ProcedureStatus::InternalError(err.to_string().into()),\n                Timestamp::UNIX_EPOCH,\n                TimeDuration::ZERO,\n            ),\n        };\n\n        let message = ws_v2::ProcedureResult {\n            status,\n            timestamp,\n            total_host_execution_duration: execution_duration,\n            request_id,\n        };\n\n        self.module()\n            .subscriptions()\n            .send_procedure_message_v2(self.sender(), message, tx_offset)\n    }\n\n    pub async fn subscribe_single(\n        &self,\n        subscription: ws_v1::SubscribeSingle,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread_async(\"subscribe_single\", async move || {\n                let host = me.module();\n                host.subscriptions()\n                    .add_single_subscription(Some(&host), me.sender, me.auth.clone(), subscription, timer, None)\n                    .await\n            })\n            .await?\n    }\n\n    pub async fn unsubscribe(\n        &self,\n        request: ws_v1::Unsubscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        asyncify(move || {\n            me.module()\n                .subscriptions()\n                .remove_single_subscription(me.sender, me.auth.clone(), request, timer)\n        })\n        .await\n    }\n\n    pub async fn subscribe_v2(\n        &self,\n        request: ws_v2::Subscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread_async(\"subscribe_v2\", async move || {\n                let host = me.module();\n                host.subscriptions()\n                    .add_v2_subscription(Some(&host), me.sender, me.auth.clone(), request, timer, None)\n                    .await\n            })\n            .await?\n    }\n    pub async fn subscribe_multi(\n        &self,\n        request: ws_v1::SubscribeMulti,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread_async(\"subscribe_multi\", async move || {\n                let host = me.module();\n                host.subscriptions()\n                    .add_multi_subscription(Some(&host), me.sender, me.auth.clone(), request, timer, None)\n                    .await\n            })\n            .await?\n    }\n\n    pub async fn unsubscribe_multi(\n        &self,\n        request: ws_v1::UnsubscribeMulti,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread(\"unsubscribe_multi\", move || {\n                me.module()\n                    .subscriptions()\n                    .remove_multi_subscription(me.sender, me.auth.clone(), request, timer)\n            })\n            .await?\n    }\n\n    pub async fn unsubscribe_v2(\n        &self,\n        request: ws_v2::Unsubscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread_async(\"unsubscribe_v2\", async move || {\n                let host = me.module();\n                host.subscriptions()\n                    .remove_v2_subscription(Some(&host), me.sender, me.auth.clone(), request, timer)\n                    .await\n            })\n            .await?\n    }\n\n    pub async fn subscribe(&self, subscription: ws_v1::Subscribe, timer: Instant) -> Result<ExecutionMetrics, DBError> {\n        let me = self.clone();\n        self.module()\n            .on_module_thread_async(\"subscribe\", async move || {\n                let host = me.module();\n                host.subscriptions()\n                    .add_legacy_subscriber(Some(&host), me.sender, me.auth.clone(), subscription, timer, None)\n                    .await\n            })\n            .await?\n    }\n\n    pub async fn one_off_query_json(\n        &self,\n        query: &str,\n        message_id: &[u8],\n        timer: Instant,\n    ) -> Result<(), anyhow::Error> {\n        self.module()\n            .one_off_query::<ws_v1::JsonFormat>(\n                self.auth.clone(),\n                query.to_owned(),\n                self.sender.clone(),\n                message_id.to_owned(),\n                timer,\n                JsonRowListBuilderFakePool,\n                |msg: OneOffQueryResponseMessage<ws_v1::JsonFormat>| msg.into(),\n            )\n            .await\n    }\n\n    pub async fn one_off_query_bsatn(\n        &self,\n        query: &str,\n        message_id: &[u8],\n        timer: Instant,\n    ) -> Result<(), anyhow::Error> {\n        let bsatn_rlb_pool = self.module().replica_ctx().subscriptions.bsatn_rlb_pool.clone();\n        self.module()\n            .one_off_query::<ws_v1::BsatnFormat>(\n                self.auth.clone(),\n                query.to_owned(),\n                self.sender.clone(),\n                message_id.to_owned(),\n                timer,\n                bsatn_rlb_pool,\n                |msg: OneOffQueryResponseMessage<ws_v1::BsatnFormat>| msg.into(),\n            )\n            .await\n    }\n\n    pub async fn one_off_query_v2(&self, query: &str, request_id: u32, timer: Instant) -> Result<(), anyhow::Error> {\n        let bsatn_rlb_pool = self.module().replica_ctx().subscriptions.bsatn_rlb_pool.clone();\n        self.module()\n            .one_off_query_v2(\n                self.auth.clone(),\n                query.to_owned(),\n                self.sender.clone(),\n                request_id,\n                timer,\n                bsatn_rlb_pool,\n            )\n            .await\n    }\n\n    pub async fn disconnect(self) {\n        self.module().disconnect_client(self.id).await\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::fmt;\n    use std::pin::pin;\n\n    use pretty_assertions::assert_matches;\n\n    use super::*;\n    use crate::client::messages::{SerializableMessage, SubscriptionUpdateMessage, TransactionUpdateMessage};\n\n    #[derive(Clone)]\n    struct FakeDurableOffset {\n        channel: watch::Sender<Option<TxOffset>>,\n        closed: Arc<AtomicBool>,\n    }\n\n    impl DurableOffsetSupply for FakeDurableOffset {\n        fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule> {\n            if self.closed.load(Ordering::Acquire) {\n                Err(NoSuchModule)\n            } else {\n                Ok(Some(self.channel.subscribe().into()))\n            }\n        }\n    }\n\n    impl FakeDurableOffset {\n        fn new() -> Self {\n            let (tx, _) = watch::channel(None);\n            Self {\n                channel: tx,\n                closed: <_>::default(),\n            }\n        }\n\n        fn mark_durable_at(&self, offset: TxOffset) {\n            self.channel.send_modify(|val| {\n                val.replace(offset);\n            })\n        }\n\n        fn close(&self) {\n            self.closed.store(true, Ordering::Release);\n        }\n    }\n\n    /// [DurableOffsetSupply] that only stores the receiver side of a watch\n    /// channel initialized to some value.\n    ///\n    /// Calling `wait_for` will succeed while the provided value is smaller than\n    /// or equal to the stored value, but report the channel as closed once it\n    /// attempts to wait for a new value.\n    struct DisconnectedDurableOffset {\n        receiver: watch::Receiver<Option<TxOffset>>,\n    }\n\n    impl DisconnectedDurableOffset {\n        fn new(offset: TxOffset) -> Self {\n            let (_, rx) = watch::channel(Some(offset));\n            Self { receiver: rx }\n        }\n    }\n\n    impl DurableOffsetSupply for DisconnectedDurableOffset {\n        fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule> {\n            Ok(Some(self.receiver.clone().into()))\n        }\n    }\n\n    /// [DurableOffsetSupply] that always returns `Ok(None)`.\n    struct NoneDurableOffset;\n\n    impl DurableOffsetSupply for NoneDurableOffset {\n        fn durable_offset(&mut self) -> Result<Option<DurableOffset>, NoSuchModule> {\n            Ok(None)\n        }\n    }\n\n    fn empty_tx_update() -> SerializableMessage {\n        let msg = TransactionUpdateMessage {\n            event: None,\n            database_update: SubscriptionUpdateMessage::default_for_protocol(Protocol::Binary, None),\n        };\n        SerializableMessage::TxUpdate(msg)\n    }\n\n    async fn assert_received_update(f: impl Future<Output = Option<OutboundMessage>>) {\n        assert_matches!(f.await, Some(OutboundMessage::V1(SerializableMessage::TxUpdate(_))));\n    }\n\n    async fn assert_receiver_closed(f: impl Future<Output = Option<OutboundMessage>>) {\n        assert_matches!(f.await, None);\n    }\n\n    async fn assert_pending(f: &mut (impl Future<Output: fmt::Debug> + Unpin)) {\n        assert_matches!(futures::poll!(f), Poll::Pending);\n    }\n\n    fn default_client(\n        offset_supply: impl DurableOffsetSupply + 'static,\n    ) -> (ClientConnectionSender, ClientConnectionReceiver) {\n        ClientConnectionSender::dummy_with_channel(\n            ClientActorId::for_test(Identity::ZERO),\n            ClientConfig {\n                confirmed_reads: false,\n                ..ClientConfig::for_test()\n            },\n            offset_supply,\n        )\n    }\n\n    fn client_with_confirmed_reads(\n        offset_supply: impl DurableOffsetSupply + 'static,\n    ) -> (ClientConnectionSender, ClientConnectionReceiver) {\n        ClientConnectionSender::dummy_with_channel(\n            ClientActorId::for_test(Identity::ZERO),\n            ClientConfig {\n                confirmed_reads: true,\n                ..ClientConfig::for_test()\n            },\n            offset_supply,\n        )\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_waits_for_durable_offset() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = client_with_confirmed_reads(offset.clone());\n\n        for tx_offset in 0..10 {\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            let mut recv = pin!(receiver.recv());\n            assert_pending(&mut recv).await;\n            offset.mark_durable_at(tx_offset);\n            assert_received_update(recv).await;\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_immediately_yields_message_if_already_durable() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = client_with_confirmed_reads(offset.clone());\n\n        for tx_offset in 0..10 {\n            offset.mark_durable_at(tx_offset);\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_ends_if_durable_offset_closed() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = client_with_confirmed_reads(offset.clone());\n\n        offset.close();\n        sender.send_message(Some(42), empty_tx_update()).unwrap();\n        assert_receiver_closed(receiver.recv()).await;\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_ends_if_durable_offset_dropped() {\n        const INITIAL_OFFSET: TxOffset = 1;\n        let offset = DisconnectedDurableOffset::new(INITIAL_OFFSET);\n        let (sender, mut receiver) = client_with_confirmed_reads(offset);\n\n        for tx_offset in 0..=(INITIAL_OFFSET + 1) {\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            if tx_offset <= INITIAL_OFFSET {\n                assert_received_update(receiver.recv()).await;\n            } else {\n                assert_receiver_closed(receiver.recv()).await;\n            }\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_immediately_yields_message_if_sent_without_offset() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = client_with_confirmed_reads(offset.clone());\n\n        for _ in 0..10 {\n            sender.send_message(None, empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n\n        offset.mark_durable_at(5);\n\n        for _ in 0..10 {\n            sender.send_message(None, empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_immediately_yields_message_for_client_without_confirmed_reads() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = default_client(offset.clone());\n\n        for tx_offset in 0..10 {\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n\n        offset.mark_durable_at(10);\n\n        for tx_offset in 0..10 {\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_immediately_yields_message_without_durability() {\n        let (sender, mut receiver) = client_with_confirmed_reads(NoneDurableOffset);\n\n        for tx_offset in 0..10 {\n            sender.send_message(Some(tx_offset), empty_tx_update()).unwrap();\n            assert_received_update(receiver.recv()).await;\n        }\n    }\n\n    #[tokio::test]\n    async fn client_connection_receiver_cancel_safety() {\n        let offset = FakeDurableOffset::new();\n        let (sender, mut receiver) = client_with_confirmed_reads(offset.clone());\n\n        sender.send_message(Some(3), empty_tx_update()).unwrap();\n        assert_pending(&mut pin!(receiver.recv())).await;\n        offset.mark_durable_at(3);\n        assert_received_update(receiver.recv()).await;\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client/client_connection_index.rs",
    "content": "use std::sync::atomic::{AtomicU64, Ordering::Relaxed};\n\nuse super::ClientName;\n\n#[derive(Default)]\npub struct ClientActorIndex {\n    client_name_auto_increment_state: AtomicU64,\n}\n\nimpl ClientActorIndex {\n    pub fn new() -> Self {\n        Self::default()\n    }\n    pub fn next_client_name(&self) -> ClientName {\n        ClientName(self.client_name_auto_increment_state.fetch_add(1, Relaxed))\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client/consume_each_list.rs",
    "content": "use bytes::Bytes;\nuse spacetimedb_client_api_messages::websocket::{v1 as ws_v1, v2 as ws_v2};\n\n/// Moves each buffer in `self` into a closure.\npub trait ConsumeEachBuffer {\n    /// Consumes `self`, moving each `Bytes` buffer in `self` into the closure `each`.\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes));\n}\n\nimpl ConsumeEachBuffer for ws_v2::QueryRows {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        self.tables\n            .into_vec()\n            .into_iter()\n            .for_each(|x| x.rows.consume_each_list(each));\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v2::TableUpdateRows {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        match self {\n            ws_v2::TableUpdateRows::EventTable(x) => x.events.consume_each_list(each),\n            ws_v2::TableUpdateRows::PersistentTable(x) => {\n                x.inserts.consume_each_list(each);\n                x.deletes.consume_each_list(each);\n            }\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v2::TransactionUpdate {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        self.query_sets\n            .into_vec()\n            .into_iter()\n            .flat_map(|x| x.tables.into_vec())\n            .flat_map(|x| x.rows.into_vec())\n            .for_each(|x| x.consume_each_list(each));\n    }\n}\n\nimpl<T: ConsumeEachBuffer> ConsumeEachBuffer for Option<T> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        if let Some(v) = self {\n            v.consume_each_list(each);\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v2::ServerMessage {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        use ws_v2::ServerMessage::*;\n        match self {\n            SubscribeApplied(x) => x.rows.consume_each_list(each),\n            OneOffQueryResult(x) => x.result.ok().consume_each_list(each),\n            UnsubscribeApplied(x) => x.rows.consume_each_list(each),\n            SubscriptionError(_) | InitialConnection(_) | ProcedureResult(_) => {}\n            TransactionUpdate(x) => x.consume_each_list(each),\n            ReducerResult(x) => {\n                if let ws_v2::ReducerOutcome::Ok(ro) = x.result {\n                    ro.transaction_update.consume_each_list(each);\n                }\n            }\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::ServerMessage<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        use ws_v1::ServerMessage::*;\n        match self {\n            InitialSubscription(x) => x.database_update.consume_each_list(each),\n            TransactionUpdate(x) => x.status.consume_each_list(each),\n            TransactionUpdateLight(x) => x.update.consume_each_list(each),\n            IdentityToken(_) | ProcedureResult(_) | SubscriptionError(_) => {}\n            OneOffQueryResponse(x) => x.consume_each_list(each),\n            SubscribeApplied(x) => x.rows.table_rows.consume_each_list(each),\n            UnsubscribeApplied(x) => x.rows.table_rows.consume_each_list(each),\n            SubscribeMultiApplied(x) => x.update.consume_each_list(each),\n            UnsubscribeMultiApplied(x) => x.update.consume_each_list(each),\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::OneOffQueryResponse<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        Vec::from(self.tables)\n            .into_iter()\n            .for_each(|x| x.rows.consume_each_list(each));\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::UpdateStatus<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        match self {\n            Self::Committed(x) => x.consume_each_list(each),\n            Self::Failed(_) | ws_v1::UpdateStatus::OutOfEnergy => {}\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::DatabaseUpdate<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        self.tables.into_iter().for_each(|x| x.consume_each_list(each));\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::TableUpdate<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        self.updates.into_iter().for_each(|x| x.consume_each_list(each));\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::CompressableQueryUpdate<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        match self {\n            Self::Uncompressed(x) => x.consume_each_list(each),\n            Self::Brotli(bytes) | Self::Gzip(bytes) => each(bytes),\n        }\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::QueryUpdate<ws_v1::BsatnFormat> {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        self.deletes.consume_each_list(each);\n        self.inserts.consume_each_list(each);\n    }\n}\n\nimpl ConsumeEachBuffer for ws_v1::BsatnRowList {\n    fn consume_each_list(self, each: &mut impl FnMut(Bytes)) {\n        let (_, buffer) = self.into_inner();\n        each(buffer);\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client/message_handlers.rs",
    "content": "use super::{ClientConnection, DataMessage, WsVersion};\nuse crate::client::message_handlers_v1::MessageExecutionError;\nuse spacetimedb_lib::bsatn;\nuse std::time::Instant;\n\n#[derive(thiserror::Error, Debug)]\npub enum MessageHandleError {\n    #[error(transparent)]\n    BinaryDecode(#[from] bsatn::DecodeError),\n    #[error(transparent)]\n    TextDecode(#[from] serde_json::Error),\n    #[error(transparent)]\n    Base64Decode(#[from] base64::DecodeError),\n\n    #[error(transparent)]\n    Execution(#[from] MessageExecutionError),\n\n    #[error(\"unsupported websocket version: {0}\")]\n    UnsupportedVersion(&'static str),\n}\n\npub async fn handle(client: &ClientConnection, message: DataMessage, timer: Instant) -> Result<(), MessageHandleError> {\n    match client.config.version {\n        WsVersion::V1 => super::message_handlers_v1::handle(client, message, timer).await,\n        WsVersion::V2 => super::message_handlers_v2::handle(client, message, timer).await,\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client/message_handlers_v1.rs",
    "content": "use super::messages::{SubscriptionUpdateMessage, SwitchedServerMessage, ToProtocol, TransactionUpdateMessage};\nuse super::{ClientConnection, DataMessage, MessageHandleError, Protocol};\nuse crate::energy::EnergyQuanta;\nuse crate::host::module_host::{EventStatus, ModuleEvent, ModuleFunctionCall};\nuse crate::host::{FunctionArgs, ReducerId};\nuse crate::identity::Identity;\nuse crate::worker_metrics::WORKER_METRICS;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_lib::de::serde::DeserializeWrapper;\nuse spacetimedb_lib::identity::RequestId;\nuse spacetimedb_lib::{bsatn, ConnectionId, Timestamp};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse std::borrow::Cow;\nuse std::sync::Arc;\nuse std::time::{Duration, Instant};\n\npub async fn handle(client: &ClientConnection, message: DataMessage, timer: Instant) -> Result<(), MessageHandleError> {\n    client.observe_websocket_request_message(&message);\n\n    let message = match message {\n        DataMessage::Text(text) => {\n            // TODO(breaking): this should ideally be &serde_json::RawValue, not json-nested-in-string\n            let DeserializeWrapper(message) =\n                serde_json::from_str::<DeserializeWrapper<ws_v1::ClientMessage<Cow<str>>>>(&text)?;\n            message.map_args(|s| {\n                FunctionArgs::Json(match s {\n                    Cow::Borrowed(s) => text.slice_ref(s),\n                    Cow::Owned(string) => string.into(),\n                })\n            })\n        }\n        DataMessage::Binary(message_buf) => bsatn::from_slice::<ws_v1::ClientMessage<&[u8]>>(&message_buf)?\n            .map_args(|b| FunctionArgs::Bsatn(message_buf.slice_ref(b))),\n    };\n\n    let module = client.module();\n    let mod_info = module.info();\n    let mod_metrics = &mod_info.metrics;\n    let database_identity = mod_info.database_identity;\n    let db = &module.replica_ctx().relational_db;\n    let record_metrics = |wl| {\n        move |metrics| {\n            if let Some(metrics) = metrics {\n                db.exec_counters_for(wl).record(&metrics);\n            }\n        }\n    };\n    let sub_metrics = record_metrics(WorkloadType::Subscribe);\n    let unsub_metrics = record_metrics(WorkloadType::Unsubscribe);\n\n    type HandleResult<'a> = Result<(), (Option<&'a RawIdentifier>, Option<ReducerId>, anyhow::Error)>;\n    let res: HandleResult<'_> = match message {\n        ws_v1::ClientMessage::CallReducer(ws_v1::CallReducer {\n            ref reducer,\n            args,\n            request_id,\n            flags,\n        }) => {\n            let res = client.call_reducer(reducer, args, request_id, timer, flags).await;\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Reducer, &database_identity, reducer)\n                .observe(timer.elapsed().as_secs_f64());\n            res.map(drop).map_err(|e| {\n                (\n                    Some(reducer),\n                    mod_info.module_def.reducer_full(&**reducer).map(|(id, _)| id),\n                    e.into(),\n                )\n            })\n        }\n        ws_v1::ClientMessage::SubscribeMulti(subscription) => {\n            let res = client.subscribe_multi(subscription, timer).await.map(sub_metrics);\n            mod_metrics\n                .request_round_trip_subscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v1::ClientMessage::UnsubscribeMulti(request) => {\n            let res = client.unsubscribe_multi(request, timer).await.map(unsub_metrics);\n            mod_metrics\n                .request_round_trip_unsubscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v1::ClientMessage::SubscribeSingle(subscription) => {\n            let res = client.subscribe_single(subscription, timer).await.map(sub_metrics);\n            mod_metrics\n                .request_round_trip_subscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v1::ClientMessage::Unsubscribe(request) => {\n            let res = client.unsubscribe(request, timer).await.map(unsub_metrics);\n            mod_metrics\n                .request_round_trip_unsubscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v1::ClientMessage::Subscribe(subscription) => {\n            let res = client.subscribe(subscription, timer).await.map(Some).map(sub_metrics);\n            mod_metrics\n                .request_round_trip_subscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v1::ClientMessage::OneOffQuery(ws_v1::OneOffQuery {\n            query_string: query,\n            message_id,\n        }) => {\n            let res = match client.config.protocol {\n                Protocol::Binary => client.one_off_query_bsatn(&query, &message_id, timer).await,\n                Protocol::Text => client.one_off_query_json(&query, &message_id, timer).await,\n            };\n            mod_metrics\n                .request_round_trip_sql\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|err| (None, None, err))\n        }\n        ws_v1::ClientMessage::CallProcedure(ws_v1::CallProcedure {\n            ref procedure,\n            args,\n            request_id,\n            flags: _,\n        }) => {\n            let res = client.call_procedure(procedure, args, request_id, timer).await;\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Procedure, &database_identity, procedure)\n                .observe(timer.elapsed().as_secs_f64());\n            if let Err(e) = res {\n                log::warn!(\"Procedure call failed: {e:#}\");\n            }\n            // `ClientConnection::call_procedure` handles sending the error message to the client if the call fails,\n            // so we don't need to return an `Err` here.\n            Ok(())\n        }\n    };\n    res.map_err(|(reducer_name, reducer_id, err)| MessageExecutionError {\n        reducer: reducer_name.cloned(),\n        reducer_id,\n        caller_identity: client.id.identity,\n        caller_connection_id: Some(client.id.connection_id),\n        err,\n    })?;\n\n    Ok(())\n}\n\n#[derive(thiserror::Error, Debug)]\n#[error(\"error executing message (reducer: {reducer:?}) (err: {err:#})\")]\npub struct MessageExecutionError {\n    pub reducer: Option<RawIdentifier>,\n    pub reducer_id: Option<ReducerId>,\n    pub caller_identity: Identity,\n    pub caller_connection_id: Option<ConnectionId>,\n    #[source]\n    pub err: anyhow::Error,\n}\n\nimpl MessageExecutionError {\n    fn into_event(self) -> ModuleEvent {\n        let reducer = self.reducer.as_deref().and_then(parse_reducer_name);\n        let reducer_id = self.reducer_id;\n        let caller_identity = self.caller_identity;\n        let caller_connection_id = self.caller_connection_id;\n        let err = self.err;\n        ModuleEvent {\n            timestamp: Timestamp::now(),\n            caller_identity,\n            caller_connection_id,\n            function_call: ModuleFunctionCall {\n                reducer,\n                reducer_id: reducer_id.unwrap_or(u32::MAX.into()),\n                args: Default::default(),\n            },\n            status: EventStatus::FailedInternal(format!(\"{:#}\", err)),\n            reducer_return_value: None,\n            energy_quanta_used: EnergyQuanta::ZERO,\n            host_execution_duration: Duration::ZERO,\n            request_id: Some(RequestId::default()),\n            timer: None,\n        }\n    }\n}\n\nfn parse_reducer_name(name: &str) -> Option<ReducerName> {\n    let raw = RawIdentifier::new(name);\n    Identifier::new(raw).ok().map(ReducerName::new)\n}\n\nimpl ToProtocol for MessageExecutionError {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: super::Protocol) -> Self::Encoded {\n        TransactionUpdateMessage {\n            event: Some(Arc::new(self.into_event())),\n            database_update: SubscriptionUpdateMessage::default_for_protocol(protocol, None),\n        }\n        .to_protocol(protocol)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client/message_handlers_v2.rs",
    "content": "use crate::client::MessageExecutionError;\n\nuse super::{ClientConnection, DataMessage, MessageHandleError};\nuse crate::worker_metrics::WORKER_METRICS;\nuse serde::de::Error as _;\nuse spacetimedb_client_api_messages::websocket::v2 as ws_v2;\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_lib::{bsatn, Timestamp};\nuse spacetimedb_primitives::ReducerId;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse std::time::Instant;\n\npub async fn handle(client: &ClientConnection, message: DataMessage, timer: Instant) -> Result<(), MessageHandleError> {\n    client.observe_websocket_request_message(&message);\n    let message = match message {\n        DataMessage::Binary(message_buf) => bsatn::from_slice::<ws_v2::ClientMessage>(&message_buf)?,\n        DataMessage::Text(_) => {\n            return Err(MessageHandleError::TextDecode(serde_json::Error::custom(\n                \"v2 websocket does not support text messages\",\n            )))\n        }\n    };\n    let module = client.module();\n    let mod_info = module.info();\n    let mod_metrics = &mod_info.metrics;\n    let database_identity = mod_info.database_identity;\n    let db = &module.replica_ctx().relational_db;\n    let record_metrics = |wl| {\n        move |metrics| {\n            if let Some(metrics) = metrics {\n                db.exec_counters_for(wl).record(&metrics);\n            }\n        }\n    };\n    let sub_metrics = record_metrics(WorkloadType::Subscribe);\n    let unsub_metrics = record_metrics(WorkloadType::Unsubscribe);\n    type HandleResult<'a> = Result<(), (Option<&'a RawIdentifier>, Option<ReducerId>, anyhow::Error)>;\n    let res: HandleResult<'_> = match message {\n        ws_v2::ClientMessage::Subscribe(subscribe) => {\n            let res = client.subscribe_v2(subscribe, timer).await.map(sub_metrics);\n            mod_metrics\n                .request_round_trip_subscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v2::ClientMessage::Unsubscribe(unsubscribe) => {\n            let res = client.unsubscribe_v2(unsubscribe, timer).await.map(unsub_metrics);\n            mod_metrics\n                .request_round_trip_unsubscribe\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|e| (None, None, e.into()))\n        }\n        ws_v2::ClientMessage::OneOffQuery(ws_v2::OneOffQuery {\n            request_id,\n            query_string,\n        }) => {\n            let res = client.one_off_query_v2(&query_string, request_id, timer).await;\n            mod_metrics\n                .request_round_trip_sql\n                .observe(timer.elapsed().as_secs_f64());\n            res.map_err(|err| (None, None, err))\n        }\n        ws_v2::ClientMessage::CallReducer(ws_v2::CallReducer {\n            ref reducer,\n            args,\n            request_id,\n            flags,\n        }) => {\n            let res = client.call_reducer_v2(reducer, args, request_id, timer, flags).await;\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Reducer, &database_identity, reducer)\n                .observe(timer.elapsed().as_secs_f64());\n            match res {\n                Ok(_) => {\n                    // If this was not a success, we would have already sent an error message.\n                    Ok(())\n                }\n                Err(e) => {\n                    let err_msg = format!(\"{e:#}\");\n                    let server_message = ws_v2::ServerMessage::ReducerResult(ws_v2::ReducerResult {\n                        request_id,\n                        // Maybe we should use the same timestamp that was used for the reducer context, but this is probably fine for now.\n                        timestamp: Timestamp::now(),\n                        result: ws_v2::ReducerOutcome::InternalError(err_msg.into()),\n                    });\n                    // TODO: Should we kill the client here, or does it mean the client is already dead.\n                    if let Err(send_err) = client.send_message(None, server_message) {\n                        log::warn!(\"Failed to send reducer error to client: {send_err}\");\n                    }\n                    Ok(())\n                }\n            }\n        }\n        ws_v2::ClientMessage::CallProcedure(ws_v2::CallProcedure {\n            ref procedure,\n            args,\n            request_id,\n            flags,\n        }) => {\n            let res = client\n                .call_procedure_v2(procedure, args, request_id, timer, flags)\n                .await;\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Procedure, &database_identity, procedure)\n                .observe(timer.elapsed().as_secs_f64());\n            if let Err(e) = res {\n                log::warn!(\"Procedure call failed: {e:#}\");\n            }\n            Ok(())\n        }\n    };\n    res.map_err(|(reducer_name, reducer_id, err)| MessageExecutionError {\n        reducer: reducer_name.cloned(),\n        reducer_id,\n        caller_identity: client.id.identity,\n        caller_connection_id: Some(client.id.connection_id),\n        err,\n    })?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/core/src/client/messages.rs",
    "content": "use super::{ClientConfig, DataMessage, Protocol};\nuse crate::client::consume_each_list::ConsumeEachBuffer;\nuse crate::host::module_host::{EventStatus, ModuleEvent, ProcedureCallError};\nuse crate::host::{ArgsTuple, ProcedureCallResult};\nuse crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse crate::subscription::websocket_building::{brotli_compress, decide_compression, gzip_compress};\nuse bytes::{BufMut, Bytes, BytesMut};\nuse bytestring::ByteString;\nuse derive_more::From;\nuse spacetimedb_client_api_messages::websocket::common::{self as ws_common, RowListLen as _};\nuse spacetimedb_client_api_messages::websocket::v1::{self as ws_v1};\nuse spacetimedb_client_api_messages::websocket::v2 as ws_v2;\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_lib::identity::RequestId;\nuse spacetimedb_lib::ser::serde::SerializeWrapper;\nuse spacetimedb_lib::{AlgebraicValue, ConnectionId, TimeDuration, Timestamp};\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::bsatn;\nuse spacetimedb_schema::table_name::TableName;\nuse std::sync::Arc;\nuse std::time::Instant;\n\n/// A server-to-client message which can be encoded according to a [`Protocol`],\n/// resulting in a [`ToProtocol::Encoded`] message.\npub trait ToProtocol {\n    type Encoded;\n    /// Convert `self` into a [`Self::Encoded`] where rows and arguments are encoded with `protocol`.\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded;\n}\n\npub type SwitchedServerMessage =\n    ws_v1::FormatSwitch<ws_v1::ServerMessage<ws_v1::BsatnFormat>, ws_v1::ServerMessage<ws_v1::JsonFormat>>;\npub(super) type SwitchedDbUpdate =\n    ws_v1::FormatSwitch<ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>, ws_v1::DatabaseUpdate<ws_v1::JsonFormat>>;\n\n/// The initial size of a `serialize` buffer.\n/// Currently 4k to align with the linux page size\n/// and this should be more than enough in the common case.\nconst SERIALIZE_BUFFER_INIT_CAP: usize = 4096;\n\n/// A buffer used by [`serialize`]\npub struct SerializeBuffer {\n    uncompressed: BytesMut,\n    compressed: BytesMut,\n}\n\nimpl SerializeBuffer {\n    pub fn new(config: ClientConfig) -> Self {\n        let uncompressed_capacity = SERIALIZE_BUFFER_INIT_CAP;\n        let compressed_capacity = if config.compression == ws_v1::Compression::None || config.protocol == Protocol::Text\n        {\n            0\n        } else {\n            SERIALIZE_BUFFER_INIT_CAP\n        };\n        Self {\n            uncompressed: BytesMut::with_capacity(uncompressed_capacity),\n            compressed: BytesMut::with_capacity(compressed_capacity),\n        }\n    }\n\n    /// Take the uncompressed message as the one to use.\n    fn uncompressed(self) -> (InUseSerializeBuffer, Bytes) {\n        let uncompressed = self.uncompressed.freeze();\n        let in_use = InUseSerializeBuffer::Uncompressed {\n            uncompressed: uncompressed.clone(),\n            compressed: self.compressed,\n        };\n        (in_use, uncompressed)\n    }\n\n    /// Write uncompressed data with a leading tag.\n    fn write_with_tag<F>(&mut self, tag: u8, write: F) -> &[u8]\n    where\n        F: FnOnce(bytes::buf::Writer<&mut BytesMut>),\n    {\n        self.uncompressed.put_u8(tag);\n        write((&mut self.uncompressed).writer());\n        &self.uncompressed[1..]\n    }\n\n    /// Compress the data from a `write_with_tag` call, and change the tag.\n    fn compress_with_tag(\n        self,\n        tag: u8,\n        write: impl FnOnce(&[u8], &mut bytes::buf::Writer<BytesMut>),\n    ) -> (InUseSerializeBuffer, Bytes) {\n        let mut writer = self.compressed.writer();\n        writer.get_mut().put_u8(tag);\n        write(&self.uncompressed[1..], &mut writer);\n        let compressed = writer.into_inner().freeze();\n        let in_use = InUseSerializeBuffer::Compressed {\n            uncompressed: self.uncompressed,\n            compressed: compressed.clone(),\n        };\n        (in_use, compressed)\n    }\n}\n\ntype BytesMutWriter<'a> = bytes::buf::Writer<&'a mut BytesMut>;\n\npub enum InUseSerializeBuffer {\n    Uncompressed { uncompressed: Bytes, compressed: BytesMut },\n    Compressed { uncompressed: BytesMut, compressed: Bytes },\n}\n\nimpl InUseSerializeBuffer {\n    pub fn try_reclaim(self) -> Option<SerializeBuffer> {\n        let (mut uncompressed, mut compressed) = match self {\n            Self::Uncompressed {\n                uncompressed,\n                compressed,\n            } => (uncompressed.try_into_mut().ok()?, compressed),\n            Self::Compressed {\n                uncompressed,\n                compressed,\n            } => (uncompressed, compressed.try_into_mut().ok()?),\n        };\n        uncompressed.clear();\n        compressed.clear();\n        Some(SerializeBuffer {\n            uncompressed,\n            compressed,\n        })\n    }\n\n    pub fn is_unique(&self) -> bool {\n        match self {\n            InUseSerializeBuffer::Uncompressed { uncompressed, .. } => uncompressed.is_unique(),\n            InUseSerializeBuffer::Compressed { compressed, .. } => compressed.is_unique(),\n        }\n    }\n}\n\n/// Serialize `msg` into a [`DataMessage`] containing a [`ws_v1::ServerMessage`].\n///\n/// If `protocol` is [`Protocol::Binary`],\n/// the message will be conditionally compressed by this method according to `compression`.\npub fn serialize(\n    bsatn_rlb_pool: &BsatnRowListBuilderPool,\n    mut buffer: SerializeBuffer,\n    msg: impl ToProtocol<Encoded = SwitchedServerMessage>,\n    config: ClientConfig,\n) -> (InUseSerializeBuffer, DataMessage) {\n    match msg.to_protocol(config.protocol) {\n        ws_v1::FormatSwitch::Json(msg) => {\n            let out: BytesMutWriter<'_> = (&mut buffer.uncompressed).writer();\n            serde_json::to_writer(out, &SerializeWrapper::new(msg))\n                .expect(\"should be able to json encode a `ServerMessage`\");\n\n            let (in_use, out) = buffer.uncompressed();\n            // SAFETY: `serde_json::to_writer` states that:\n            // > \"Serialization guarantees it only feeds valid UTF-8 sequences to the writer.\"\n            let msg_json = unsafe { ByteString::from_bytes_unchecked(out) };\n            (in_use, msg_json.into())\n        }\n        ws_v1::FormatSwitch::Bsatn(msg) => {\n            // First write the tag so that we avoid shifting the entire message at the end.\n            let srv_msg = buffer.write_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_NONE, |w| {\n                bsatn::to_writer(w.into_inner(), &msg).unwrap()\n            });\n\n            // At this point, we no longer have a use for `msg`,\n            // so try to reclaim its buffers.\n            msg.consume_each_list(&mut |buffer| bsatn_rlb_pool.try_put(buffer));\n\n            // Conditionally compress the message.\n            let (in_use, msg_bytes) = match decide_compression(srv_msg.len(), config.compression) {\n                ws_v1::Compression::None => buffer.uncompressed(),\n                ws_v1::Compression::Brotli => {\n                    buffer.compress_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_BROTLI, brotli_compress)\n                }\n                ws_v1::Compression::Gzip => {\n                    buffer.compress_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_GZIP, gzip_compress)\n                }\n            };\n            (in_use, msg_bytes.into())\n        }\n    }\n}\n\n/// Serialize `msg` into a [`DataMessage`] containing a [`ws_v2::ServerMessage`].\n///\n/// This mirrors the v1 framing by prepending the compression tag and applying\n/// conditional compression when configured.\npub fn serialize_v2(\n    bsatn_rlb_pool: &BsatnRowListBuilderPool,\n    mut buffer: SerializeBuffer,\n    msg: ws_v2::ServerMessage,\n    compression: ws_v1::Compression,\n) -> (InUseSerializeBuffer, Bytes) {\n    let srv_msg = buffer.write_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_NONE, |w| {\n        bsatn::to_writer(w.into_inner(), &msg).expect(\"should be able to bsatn encode v2 message\");\n    });\n\n    // At this point, we no longer have a use for `msg`,\n    // so try to reclaim its buffers.\n    msg.consume_each_list(&mut |buffer| bsatn_rlb_pool.try_put(buffer));\n\n    match decide_compression(srv_msg.len(), compression) {\n        ws_v1::Compression::None => buffer.uncompressed(),\n        ws_v1::Compression::Brotli => {\n            buffer.compress_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_BROTLI, brotli_compress)\n        }\n        ws_v1::Compression::Gzip => buffer.compress_with_tag(ws_common::SERVER_MSG_COMPRESSION_TAG_GZIP, gzip_compress),\n    }\n}\n\n#[derive(Debug, From)]\npub enum SerializableMessage {\n    QueryBinary(OneOffQueryResponseMessage<ws_v1::BsatnFormat>),\n    QueryText(OneOffQueryResponseMessage<ws_v1::JsonFormat>),\n    Identity(IdentityTokenMessage),\n    Subscribe(SubscriptionUpdateMessage),\n    Subscription(SubscriptionMessage),\n    TxUpdate(TransactionUpdateMessage),\n    ProcedureResult(ProcedureResultMessage),\n}\n\nimpl SerializableMessage {\n    pub fn num_rows(&self) -> Option<usize> {\n        match self {\n            Self::QueryBinary(msg) => Some(msg.num_rows()),\n            Self::QueryText(msg) => Some(msg.num_rows()),\n            Self::Subscribe(msg) => Some(msg.num_rows()),\n            Self::Subscription(msg) => Some(msg.num_rows()),\n            Self::TxUpdate(msg) => Some(msg.num_rows()),\n            Self::Identity(_) | Self::ProcedureResult(_) => None,\n        }\n    }\n\n    pub fn workload(&self) -> Option<WorkloadType> {\n        match self {\n            Self::QueryBinary(_) | Self::QueryText(_) => Some(WorkloadType::Sql),\n            Self::Subscribe(_) => Some(WorkloadType::Subscribe),\n            Self::Subscription(msg) => match &msg.result {\n                SubscriptionResult::Subscribe(_) => Some(WorkloadType::Subscribe),\n                SubscriptionResult::Unsubscribe(_) => Some(WorkloadType::Unsubscribe),\n                SubscriptionResult::Error(_) => None,\n                SubscriptionResult::SubscribeMulti(_) => Some(WorkloadType::Subscribe),\n                SubscriptionResult::UnsubscribeMulti(_) => Some(WorkloadType::Unsubscribe),\n            },\n            Self::TxUpdate(_) => Some(WorkloadType::Update),\n            Self::Identity(_) => None,\n            Self::ProcedureResult(_) => Some(WorkloadType::Procedure),\n        }\n    }\n}\n\nimpl ToProtocol for SerializableMessage {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        match self {\n            SerializableMessage::QueryBinary(msg) => msg.to_protocol(protocol),\n            SerializableMessage::QueryText(msg) => msg.to_protocol(protocol),\n            SerializableMessage::Identity(msg) => msg.to_protocol(protocol),\n            SerializableMessage::Subscribe(msg) => msg.to_protocol(protocol),\n            SerializableMessage::TxUpdate(msg) => msg.to_protocol(protocol),\n            SerializableMessage::Subscription(msg) => msg.to_protocol(protocol),\n            SerializableMessage::ProcedureResult(msg) => msg.to_protocol(protocol),\n        }\n    }\n}\n\n#[derive(Debug, derive_more::From)]\npub enum OutboundMessage {\n    V1(SerializableMessage),\n    V2(ws_v2::ServerMessage),\n}\n\nimpl OutboundMessage {\n    pub fn num_rows(&self) -> Option<usize> {\n        match self {\n            Self::V1(message) => message.num_rows(),\n            Self::V2(message) => v2_message_num_rows(message),\n        }\n    }\n\n    pub fn workload(&self) -> Option<WorkloadType> {\n        match self {\n            Self::V1(message) => message.workload(),\n            Self::V2(message) => match message {\n                ws_v2::ServerMessage::InitialConnection(_) => None,\n                ws_v2::ServerMessage::SubscribeApplied(_) => Some(WorkloadType::Subscribe),\n                ws_v2::ServerMessage::UnsubscribeApplied(_) => Some(WorkloadType::Unsubscribe),\n                ws_v2::ServerMessage::SubscriptionError(_) => None,\n                ws_v2::ServerMessage::TransactionUpdate(_) => Some(WorkloadType::Update),\n                ws_v2::ServerMessage::OneOffQueryResult(_) => Some(WorkloadType::Sql),\n                ws_v2::ServerMessage::ReducerResult(_) => Some(WorkloadType::Reducer),\n                ws_v2::ServerMessage::ProcedureResult(_) => Some(WorkloadType::Procedure),\n            },\n        }\n    }\n}\n\nfn v2_message_num_rows(message: &ws_v2::ServerMessage) -> Option<usize> {\n    match message {\n        ws_v2::ServerMessage::InitialConnection(_) => None,\n        ws_v2::ServerMessage::SubscribeApplied(message) => Some(count_query_rows(&message.rows)),\n        ws_v2::ServerMessage::UnsubscribeApplied(message) => {\n            Some(message.rows.as_ref().map(count_query_rows).unwrap_or_default())\n        }\n        ws_v2::ServerMessage::SubscriptionError(_) => None,\n        ws_v2::ServerMessage::TransactionUpdate(message) => Some(count_transaction_update(message)),\n        ws_v2::ServerMessage::OneOffQueryResult(message) => match &message.result {\n            Ok(rows) => Some(count_query_rows(rows)),\n            Err(_) => None,\n        },\n        ws_v2::ServerMessage::ReducerResult(message) => match &message.result {\n            ws_v2::ReducerOutcome::Ok(ok) => Some(count_transaction_update(&ok.transaction_update)),\n            ws_v2::ReducerOutcome::OkEmpty => Some(0),\n            ws_v2::ReducerOutcome::Err(_) | ws_v2::ReducerOutcome::InternalError(_) => None,\n        },\n        ws_v2::ServerMessage::ProcedureResult(_) => None,\n    }\n}\n\nfn count_query_rows(rows: &ws_v2::QueryRows) -> usize {\n    rows.tables.iter().map(|table| table.rows.len()).sum()\n}\n\nfn count_transaction_update(update: &ws_v2::TransactionUpdate) -> usize {\n    update\n        .query_sets\n        .iter()\n        .flat_map(|qs| qs.tables.iter())\n        .map(|table| table.rows.iter().map(count_table_update_rows).sum::<usize>())\n        .sum()\n}\n\nfn count_table_update_rows(rows: &ws_v2::TableUpdateRows) -> usize {\n    match rows {\n        ws_v2::TableUpdateRows::PersistentTable(rows) => rows.inserts.len() + rows.deletes.len(),\n        ws_v2::TableUpdateRows::EventTable(rows) => rows.events.len(),\n    }\n}\n\npub type IdentityTokenMessage = ws_v1::IdentityToken;\n\nimpl ToProtocol for IdentityTokenMessage {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        match protocol {\n            Protocol::Text => ws_v1::FormatSwitch::Json(ws_v1::ServerMessage::IdentityToken(self)),\n            Protocol::Binary => ws_v1::FormatSwitch::Bsatn(ws_v1::ServerMessage::IdentityToken(self)),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct TransactionUpdateMessage {\n    /// The event that caused this update.\n    /// When `None`, this is a light update.\n    pub event: Option<Arc<ModuleEvent>>,\n    pub database_update: SubscriptionUpdateMessage,\n}\n\nimpl TransactionUpdateMessage {\n    fn num_rows(&self) -> usize {\n        self.database_update.num_rows()\n    }\n}\n\nimpl ToProtocol for TransactionUpdateMessage {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        fn convert<F: ws_v1::WebsocketFormat>(\n            event: Option<Arc<ModuleEvent>>,\n            request_id: u32,\n            update: ws_v1::DatabaseUpdate<F>,\n            conv_args: impl FnOnce(&ArgsTuple) -> F::Single,\n        ) -> ws_v1::ServerMessage<F> {\n            let Some(event) = event else {\n                return ws_v1::ServerMessage::TransactionUpdateLight(ws_v1::TransactionUpdateLight {\n                    request_id,\n                    update,\n                });\n            };\n\n            let status = match &event.status {\n                EventStatus::Committed(_) => ws_v1::UpdateStatus::Committed(update),\n                EventStatus::FailedUser(errmsg) | EventStatus::FailedInternal(errmsg) => {\n                    ws_v1::UpdateStatus::Failed(errmsg.clone().into())\n                }\n                EventStatus::OutOfEnergy => ws_v1::UpdateStatus::OutOfEnergy,\n            };\n\n            let args = conv_args(&event.function_call.args);\n\n            let tx_update = ws_v1::TransactionUpdate {\n                timestamp: event.timestamp,\n                status,\n                caller_identity: event.caller_identity,\n                reducer_call: ws_v1::ReducerCallInfo {\n                    reducer_name: event\n                        .function_call\n                        .reducer\n                        .clone()\n                        .map(Into::into)\n                        .unwrap_or_else(|| \"\".into()),\n                    reducer_id: event.function_call.reducer_id.into(),\n                    args,\n                    request_id,\n                },\n                energy_quanta_used: event.energy_quanta_used,\n                total_host_execution_duration: event.host_execution_duration.into(),\n                caller_connection_id: event.caller_connection_id.unwrap_or(ConnectionId::ZERO),\n            };\n\n            ws_v1::ServerMessage::TransactionUpdate(tx_update)\n        }\n\n        let TransactionUpdateMessage { event, database_update } = self;\n        let update = database_update.database_update;\n        protocol.assert_matches_format_switch(&update);\n        let request_id = database_update.request_id.unwrap_or(0);\n        match update {\n            ws_v1::FormatSwitch::Bsatn(update) => {\n                ws_v1::FormatSwitch::Bsatn(convert(event, request_id, update, |args| {\n                    Vec::from(args.get_bsatn().clone()).into()\n                }))\n            }\n            ws_v1::FormatSwitch::Json(update) => {\n                ws_v1::FormatSwitch::Json(convert(event, request_id, update, |args| args.get_json().clone()))\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct SubscriptionUpdateMessage {\n    pub database_update: SwitchedDbUpdate,\n    pub request_id: Option<RequestId>,\n    pub timer: Option<Instant>,\n}\n\nimpl SubscriptionUpdateMessage {\n    pub(crate) fn default_for_protocol(protocol: Protocol, request_id: Option<RequestId>) -> Self {\n        Self {\n            database_update: match protocol {\n                Protocol::Text => ws_v1::FormatSwitch::Json(<_>::default()),\n                Protocol::Binary => ws_v1::FormatSwitch::Bsatn(<_>::default()),\n            },\n            request_id,\n            timer: None,\n        }\n    }\n\n    pub(crate) fn from_event_and_update(event: &ModuleEvent, update: SwitchedDbUpdate) -> Self {\n        Self {\n            database_update: update,\n            request_id: event.request_id,\n            timer: event.timer,\n        }\n    }\n\n    fn num_rows(&self) -> usize {\n        match &self.database_update {\n            ws_v1::FormatSwitch::Bsatn(x) => x.num_rows(),\n            ws_v1::FormatSwitch::Json(x) => x.num_rows(),\n        }\n    }\n}\n\nimpl ToProtocol for SubscriptionUpdateMessage {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        let request_id = self.request_id.unwrap_or(0);\n        let total_host_execution_duration = self.timer.map_or(TimeDuration::ZERO, |t| t.elapsed().into());\n\n        protocol.assert_matches_format_switch(&self.database_update);\n        match self.database_update {\n            ws_v1::FormatSwitch::Bsatn(database_update) => {\n                ws_v1::FormatSwitch::Bsatn(ws_v1::ServerMessage::InitialSubscription(ws_v1::InitialSubscription {\n                    database_update,\n                    request_id,\n                    total_host_execution_duration,\n                }))\n            }\n            ws_v1::FormatSwitch::Json(database_update) => {\n                ws_v1::FormatSwitch::Json(ws_v1::ServerMessage::InitialSubscription(ws_v1::InitialSubscription {\n                    database_update,\n                    request_id,\n                    total_host_execution_duration,\n                }))\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct SubscriptionData {\n    pub data: ws_v1::FormatSwitch<ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>, ws_v1::DatabaseUpdate<ws_v1::JsonFormat>>,\n}\n\n#[derive(Debug, Clone)]\npub struct SubscriptionRows {\n    pub table_id: TableId,\n    pub table_name: TableName,\n    pub table_rows: ws_v1::FormatSwitch<ws_v1::TableUpdate<ws_v1::BsatnFormat>, ws_v1::TableUpdate<ws_v1::JsonFormat>>,\n}\n\n#[derive(Debug, Clone)]\npub struct SubscriptionError {\n    pub table_id: Option<TableId>,\n    pub message: Box<str>,\n}\n\n#[derive(Debug, Clone)]\npub enum SubscriptionResult {\n    Subscribe(SubscriptionRows),\n    Unsubscribe(SubscriptionRows),\n    Error(SubscriptionError),\n    SubscribeMulti(SubscriptionData),\n    UnsubscribeMulti(SubscriptionData),\n}\n\n#[derive(Debug, Clone)]\npub struct SubscriptionMessage {\n    pub timer: Option<Instant>,\n    pub request_id: Option<RequestId>,\n    pub query_id: Option<ws_v1::QueryId>,\n    pub result: SubscriptionResult,\n}\n\nfn num_rows_in(rows: &SubscriptionRows) -> usize {\n    match &rows.table_rows {\n        ws_v1::FormatSwitch::Bsatn(x) => x.num_rows(),\n        ws_v1::FormatSwitch::Json(x) => x.num_rows(),\n    }\n}\n\nfn subscription_data_rows(rows: &SubscriptionData) -> usize {\n    match &rows.data {\n        ws_v1::FormatSwitch::Bsatn(x) => x.num_rows(),\n        ws_v1::FormatSwitch::Json(x) => x.num_rows(),\n    }\n}\n\nimpl SubscriptionMessage {\n    fn num_rows(&self) -> usize {\n        match &self.result {\n            SubscriptionResult::Subscribe(x) => num_rows_in(x),\n            SubscriptionResult::SubscribeMulti(x) => subscription_data_rows(x),\n            SubscriptionResult::UnsubscribeMulti(x) => subscription_data_rows(x),\n            SubscriptionResult::Unsubscribe(x) => num_rows_in(x),\n            _ => 0,\n        }\n    }\n}\n\nimpl ToProtocol for SubscriptionMessage {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        let request_id = self.request_id.unwrap_or(0);\n        let query_id = self.query_id.unwrap_or(ws_v1::QueryId::new(0));\n        let total_host_execution_duration_micros = self.timer.map_or(0, |t| t.elapsed().as_micros() as u64);\n\n        match self.result {\n            SubscriptionResult::Subscribe(result) => {\n                protocol.assert_matches_format_switch(&result.table_rows);\n                match result.table_rows {\n                    ws_v1::FormatSwitch::Bsatn(table_rows) => ws_v1::FormatSwitch::Bsatn(\n                        ws_v1::SubscribeApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            rows: ws_v1::SubscribeRows {\n                                table_id: result.table_id,\n                                table_name: result.table_name.into(),\n                                table_rows,\n                            },\n                        }\n                        .into(),\n                    ),\n                    ws_v1::FormatSwitch::Json(table_rows) => ws_v1::FormatSwitch::Json(\n                        ws_v1::SubscribeApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            rows: ws_v1::SubscribeRows {\n                                table_id: result.table_id,\n                                table_name: result.table_name.into(),\n                                table_rows,\n                            },\n                        }\n                        .into(),\n                    ),\n                }\n            }\n            SubscriptionResult::Unsubscribe(result) => {\n                protocol.assert_matches_format_switch(&result.table_rows);\n                match result.table_rows {\n                    ws_v1::FormatSwitch::Bsatn(table_rows) => ws_v1::FormatSwitch::Bsatn(\n                        ws_v1::UnsubscribeApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            rows: ws_v1::SubscribeRows {\n                                table_id: result.table_id,\n                                table_name: result.table_name.into(),\n                                table_rows,\n                            },\n                        }\n                        .into(),\n                    ),\n                    ws_v1::FormatSwitch::Json(table_rows) => ws_v1::FormatSwitch::Json(\n                        ws_v1::UnsubscribeApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            rows: ws_v1::SubscribeRows {\n                                table_id: result.table_id,\n                                table_name: result.table_name.into(),\n                                table_rows,\n                            },\n                        }\n                        .into(),\n                    ),\n                }\n            }\n            SubscriptionResult::Error(error) => {\n                let msg = ws_v1::SubscriptionError {\n                    total_host_execution_duration_micros,\n                    request_id: self.request_id,           // Pass Option through\n                    query_id: self.query_id.map(|x| x.id), // Pass Option through\n                    table_id: error.table_id,\n                    error: error.message,\n                };\n                match protocol {\n                    Protocol::Binary => ws_v1::FormatSwitch::Bsatn(msg.into()),\n                    Protocol::Text => ws_v1::FormatSwitch::Json(msg.into()),\n                }\n            }\n            SubscriptionResult::SubscribeMulti(result) => {\n                protocol.assert_matches_format_switch(&result.data);\n                match result.data {\n                    ws_v1::FormatSwitch::Bsatn(data) => ws_v1::FormatSwitch::Bsatn(\n                        ws_v1::SubscribeMultiApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            update: data,\n                        }\n                        .into(),\n                    ),\n                    ws_v1::FormatSwitch::Json(data) => ws_v1::FormatSwitch::Json(\n                        ws_v1::SubscribeMultiApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            update: data,\n                        }\n                        .into(),\n                    ),\n                }\n            }\n            SubscriptionResult::UnsubscribeMulti(result) => {\n                protocol.assert_matches_format_switch(&result.data);\n                match result.data {\n                    ws_v1::FormatSwitch::Bsatn(data) => ws_v1::FormatSwitch::Bsatn(\n                        ws_v1::UnsubscribeMultiApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            update: data,\n                        }\n                        .into(),\n                    ),\n                    ws_v1::FormatSwitch::Json(data) => ws_v1::FormatSwitch::Json(\n                        ws_v1::UnsubscribeMultiApplied {\n                            total_host_execution_duration_micros,\n                            request_id,\n                            query_id,\n                            update: data,\n                        }\n                        .into(),\n                    ),\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct OneOffQueryResponseMessage<F: ws_v1::WebsocketFormat> {\n    pub message_id: Vec<u8>,\n    pub error: Option<String>,\n    pub results: Vec<ws_v1::OneOffTable<F>>,\n    pub total_host_execution_duration: TimeDuration,\n}\n\nimpl<F: ws_v1::WebsocketFormat> OneOffQueryResponseMessage<F> {\n    fn num_rows(&self) -> usize {\n        self.results.iter().map(|table| table.rows.len()).sum()\n    }\n}\n\nimpl ToProtocol for OneOffQueryResponseMessage<ws_v1::BsatnFormat> {\n    type Encoded = SwitchedServerMessage;\n\n    fn to_protocol(self, _: Protocol) -> Self::Encoded {\n        ws_v1::FormatSwitch::Bsatn(convert(self))\n    }\n}\n\nimpl ToProtocol for OneOffQueryResponseMessage<ws_v1::JsonFormat> {\n    type Encoded = SwitchedServerMessage;\n    fn to_protocol(self, _: Protocol) -> Self::Encoded {\n        ws_v1::FormatSwitch::Json(convert(self))\n    }\n}\n\nfn convert<F: ws_v1::WebsocketFormat>(msg: OneOffQueryResponseMessage<F>) -> ws_v1::ServerMessage<F> {\n    ws_v1::ServerMessage::OneOffQueryResponse(ws_v1::OneOffQueryResponse {\n        message_id: msg.message_id.into(),\n        error: msg.error.map(Into::into),\n        tables: msg.results.into_boxed_slice(),\n        total_host_execution_duration: msg.total_host_execution_duration,\n    })\n}\n\n/// Result of a procedure run.\n#[derive(Debug)]\npub enum ProcedureStatus {\n    /// The procedure ran to completion and returned this value.\n    Returned(AlgebraicValue),\n    /// The procedure was terminated due to running out of energy.\n    OutOfEnergy,\n    /// The procedure failed to run to completion. This string describes the failure.\n    InternalError(String),\n}\n\n/// Will be sent to the caller of a procedure after that procedure finishes running.\n#[derive(Debug)]\npub struct ProcedureResultMessage {\n    status: ProcedureStatus,\n    timestamp: Timestamp,\n    total_host_execution_duration: TimeDuration,\n    request_id: u32,\n}\n\nimpl ProcedureResultMessage {\n    pub fn from_result(res: &Result<ProcedureCallResult, ProcedureCallError>, request_id: RequestId) -> Self {\n        let (status, timestamp, execution_duration) = match res {\n            Ok(ProcedureCallResult {\n                return_val,\n                execution_duration,\n                start_timestamp,\n            }) => (\n                ProcedureStatus::Returned(return_val.clone()),\n                *start_timestamp,\n                TimeDuration::from(*execution_duration),\n            ),\n            Err(err) => (\n                match err {\n                    ProcedureCallError::OutOfEnergy => ProcedureStatus::OutOfEnergy,\n                    _ => ProcedureStatus::InternalError(format!(\"{err}\")),\n                },\n                Timestamp::UNIX_EPOCH,\n                TimeDuration::ZERO,\n            ),\n        };\n\n        ProcedureResultMessage {\n            status,\n            timestamp,\n            total_host_execution_duration: execution_duration,\n            request_id,\n        }\n    }\n}\n\nimpl ToProtocol for ProcedureResultMessage {\n    type Encoded = SwitchedServerMessage;\n\n    fn to_protocol(self, protocol: Protocol) -> Self::Encoded {\n        fn convert<F: ws_v1::WebsocketFormat>(\n            msg: ProcedureResultMessage,\n            serialize_value: impl Fn(AlgebraicValue) -> F::Single,\n        ) -> ws_v1::ServerMessage<F> {\n            let ProcedureResultMessage {\n                status,\n                timestamp,\n                total_host_execution_duration,\n                request_id,\n            } = msg;\n            let status = match status {\n                ProcedureStatus::InternalError(msg) => ws_v1::ProcedureStatus::InternalError(msg),\n                ProcedureStatus::OutOfEnergy => ws_v1::ProcedureStatus::OutOfEnergy,\n                ProcedureStatus::Returned(val) => ws_v1::ProcedureStatus::Returned(serialize_value(val)),\n            };\n            ws_v1::ServerMessage::ProcedureResult(ws_v1::ProcedureResult {\n                status,\n                timestamp,\n                total_host_execution_duration,\n                request_id,\n            })\n        }\n\n        // Note that procedure returns are sent only to the caller, not broadcast to all subscribers,\n        // so we don't have to bother with memoizing the serialization the way we do for reducer args.\n        match protocol {\n            Protocol::Binary => ws_v1::FormatSwitch::Bsatn(convert(self, |val| {\n                bsatn::to_vec(&val)\n                    .expect(\"Procedure return value failed to serialize to BSATN\")\n                    .into()\n            })),\n            Protocol::Text => ws_v1::FormatSwitch::Json(convert(self, |val| {\n                serde_json::to_string(&SerializeWrapper(val))\n                    .expect(\"Procedure return value failed to serialize to JSON\")\n                    .into()\n            })),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/client.rs",
    "content": "use crate::identity::Identity;\nuse std::fmt;\n\nmod client_connection;\nmod client_connection_index;\npub mod consume_each_list;\nmod message_handlers;\nmod message_handlers_v1;\nmod message_handlers_v2;\npub mod messages;\n\npub use client_connection::{\n    ClientConfig, ClientConnection, ClientConnectionReceiver, ClientConnectionSender, ClientSendError, DataMessage,\n    MeteredDeque, MeteredReceiver, MeteredSender, Protocol, WsVersion,\n};\npub use client_connection_index::ClientActorIndex;\npub use message_handlers::MessageHandleError;\npub use message_handlers_v1::MessageExecutionError;\npub use messages::OutboundMessage;\nuse spacetimedb_lib::ConnectionId;\n\n// #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]\n#[derive(Clone, Debug, Copy)]\npub struct ClientActorId {\n    pub identity: Identity,\n    pub connection_id: ConnectionId,\n    pub name: ClientName,\n}\n\nimpl ClientActorId {\n    #[cfg(test)]\n    pub fn for_test(identity: Identity) -> Self {\n        ClientActorId {\n            identity,\n            connection_id: ConnectionId::ZERO,\n            name: ClientName(0),\n        }\n    }\n}\n\n#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]\npub struct ClientName(pub u64);\n\nimpl fmt::Display for ClientActorId {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(\n            f,\n            \"ClientActorId({}@{}/{})\",\n            self.identity.to_hex(),\n            self.connection_id.to_hex(),\n            self.name.0\n        )\n    }\n}\n"
  },
  {
    "path": "crates/core/src/config.rs",
    "content": "use std::path::Path;\nuse std::{fmt, io};\n\nuse spacetimedb_lib::ConnectionId;\nuse spacetimedb_paths::cli::{ConfigDir, PrivKeyPath, PubKeyPath};\nuse spacetimedb_paths::server::{ConfigToml, MetadataTomlPath};\n\n/// Parse a TOML file at the given path, returning `None` if the file does not exist.\n///\n/// **WARNING**: Comments and formatting in the file will be lost.\npub fn parse_config<T: serde::de::DeserializeOwned>(path: &Path) -> anyhow::Result<Option<T>> {\n    match std::fs::read_to_string(path) {\n        Ok(contents) => Ok(Some(toml::from_str(&contents)?)),\n        Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),\n        Err(e) => Err(e.into()),\n    }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug)]\npub struct MetadataFile {\n    pub version: semver::Version,\n    pub edition: String,\n\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    /// Unused and always `None` in SpacetimeDB-standalone,\n    /// but used by SpacetimeDB-cloud.\n    pub client_connection_id: Option<ConnectionId>,\n}\n\nimpl MetadataFile {\n    pub fn new(edition: &str) -> Self {\n        let mut current_version: semver::Version = env!(\"CARGO_PKG_VERSION\").parse().unwrap();\n        // set the patch version of newly-created metadata files to 0 -- v1.0.0\n        // set `cmp.patch = Some(file_version.patch)` when checking version\n        // compatibility, meaning it won't be forwards-compatible with a\n        // database claiming to be created on v1.0.1, even though that should\n        // work. This can be changed once we release v1.1.0, since we don't\n        // care about its DBs being backwards-compatible with v1.0.0 anyway.\n        if let semver::Version { major: 1, minor: 0, .. } = current_version {\n            current_version.patch = 0;\n        }\n        Self {\n            version: current_version,\n            edition: edition.to_owned(),\n            client_connection_id: None,\n        }\n    }\n\n    pub fn read(path: &MetadataTomlPath) -> anyhow::Result<Option<Self>> {\n        parse_config(path.as_ref())\n    }\n\n    pub fn write(&self, path: &MetadataTomlPath) -> io::Result<()> {\n        path.write(self.to_string())\n    }\n\n    fn check_compatibility(previous: &Self, current: &Self) -> anyhow::Result<()> {\n        anyhow::ensure!(\n            previous.edition == current.edition,\n            \"metadata.toml indicates that this database is from a different \\\n            edition of SpacetimeDB (running {:?}, but this database is {:?})\",\n            current.edition,\n            previous.edition,\n        );\n\n        // This is mostly redundant with the caret comparison below, but\n        // pre-releases make it annoying.\n        if previous.version == current.version {\n            return Ok(());\n        }\n\n        // Special-case: SpacetimeDB 2.x can run 1.x databases.\n        if previous.version.major == 1 && current.version.major == 2 {\n            return Ok(());\n        }\n\n        let cmp = semver::Comparator {\n            op: semver::Op::Caret,\n            major: previous.version.major,\n            minor: Some(previous.version.minor),\n            patch: None,\n            // We deal with pre-releases separately above.\n            pre: semver::Prerelease::new(\"\").unwrap(),\n        };\n\n        if cmp.matches(&current.version) {\n            return Ok(());\n        }\n\n        let relation = if previous.version > current.version {\n            \"a newer, incompatible\"\n        } else if previous.version < current.version {\n            \"an older, incompatible\"\n        } else {\n            \"an incompatible\"\n        };\n        anyhow::bail!(\n            \"metadata.toml indicates that you are running {relation} database. Your running version is {:?}, but the database on disk is from {:?}.\",\n            current.version,\n            previous.version,\n        );\n    }\n\n    /// Check if this meta file is compatible with the default meta\n    /// file of a just-started database, and if so return the metadata\n    /// to write back to the file.\n    ///\n    /// `self` is the metadata file read from a database, and current is\n    /// the default metadata file that the active database version would\n    /// right to a new database.\n    pub fn check_compatibility_and_update(mut self, current: Self) -> anyhow::Result<Self> {\n        Self::check_compatibility(&self, &current)?;\n        // bump the version in the file only if it's being run in a newer database.\n        self.version = std::cmp::max(self.version, current.version);\n        Ok(self)\n    }\n}\n\nimpl fmt::Display for MetadataFile {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(f, \"# THIS FILE IS GENERATED BY SPACETIMEDB, DO NOT MODIFY!\")?;\n        writeln!(f)?;\n        f.write_str(&toml::to_string(self).unwrap())\n    }\n}\n\n#[derive(serde::Deserialize, Default)]\n#[serde(rename_all = \"kebab-case\")]\npub struct ConfigFile {\n    #[serde(default)]\n    pub certificate_authority: Option<CertificateAuthority>,\n    #[serde(default)]\n    pub logs: LogConfig,\n}\n\nimpl ConfigFile {\n    pub fn read(path: &ConfigToml) -> anyhow::Result<Option<Self>> {\n        parse_config(path.as_ref())\n    }\n}\n\n#[derive(serde::Deserialize)]\n#[serde(rename_all = \"kebab-case\")]\npub struct CertificateAuthority {\n    pub jwt_priv_key_path: PrivKeyPath,\n    pub jwt_pub_key_path: PubKeyPath,\n}\n\nimpl CertificateAuthority {\n    pub fn in_cli_config_dir(dir: &ConfigDir) -> Self {\n        Self {\n            jwt_priv_key_path: dir.jwt_priv_key(),\n            jwt_pub_key_path: dir.jwt_pub_key(),\n        }\n    }\n\n    pub fn get_or_create_keys(&self) -> anyhow::Result<crate::auth::JwtKeys> {\n        crate::auth::get_or_create_keys(self)\n    }\n}\n\n#[serde_with::serde_as]\n#[derive(Clone, serde::Deserialize, Default)]\n#[serde(rename_all = \"kebab-case\")]\npub struct LogConfig {\n    #[serde_as(as = \"Option<serde_with::DisplayFromStr>\")]\n    pub level: Option<tracing_core::LevelFilter>,\n    #[serde(default)]\n    pub directives: Vec<String>,\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn mkver(major: u64, minor: u64, patch: u64) -> semver::Version {\n        semver::Version::new(major, minor, patch)\n    }\n\n    fn mkver_pre(major: u64, minor: u64, patch: u64, pre: &str) -> semver::Version {\n        semver::Version {\n            major,\n            minor,\n            patch,\n            pre: semver::Prerelease::new(pre).unwrap(),\n            build: semver::BuildMetadata::EMPTY,\n        }\n    }\n\n    fn mkmeta(major: u64, minor: u64, patch: u64) -> MetadataFile {\n        MetadataFile {\n            version: mkver(major, minor, patch),\n            edition: \"standalone\".to_owned(),\n            client_connection_id: None,\n        }\n    }\n\n    fn mkmeta_pre(major: u64, minor: u64, patch: u64, pre: &str) -> MetadataFile {\n        MetadataFile {\n            version: mkver_pre(major, minor, patch, pre),\n            edition: \"standalone\".to_owned(),\n            client_connection_id: None,\n        }\n    }\n\n    #[test]\n    fn check_metadata_compatibility_checking() {\n        assert_eq!(\n            mkmeta(1, 0, 0)\n                .check_compatibility_and_update(mkmeta(1, 0, 1))\n                .unwrap()\n                .version,\n            mkver(1, 0, 1)\n        );\n        assert_eq!(\n            mkmeta(1, 0, 1)\n                .check_compatibility_and_update(mkmeta(1, 0, 0))\n                .unwrap()\n                .version,\n            mkver(1, 0, 1)\n        );\n\n        mkmeta(1, 1, 0)\n            .check_compatibility_and_update(mkmeta(1, 0, 5))\n            .unwrap_err();\n        mkmeta(2, 0, 0)\n            .check_compatibility_and_update(mkmeta(1, 3, 5))\n            .unwrap_err();\n        assert_eq!(\n            mkmeta(1, 12, 0)\n                .check_compatibility_and_update(mkmeta(2, 0, 0))\n                .unwrap()\n                .version,\n            mkver(2, 0, 0)\n        );\n        mkmeta(2, 0, 0)\n            .check_compatibility_and_update(mkmeta(3, 0, 0))\n            .unwrap_err();\n    }\n\n    #[test]\n    fn check_metadata_compatibility_prerelease() {\n        mkmeta(1, 9, 0)\n            .check_compatibility_and_update(mkmeta_pre(2, 0, 0, \"rc1\"))\n            .unwrap();\n\n        mkmeta_pre(2, 0, 0, \"rc1\")\n            .check_compatibility_and_update(mkmeta_pre(2, 0, 0, \"rc1\"))\n            .unwrap();\n\n        mkmeta_pre(2, 0, 0, \"rc1\")\n            .check_compatibility_and_update(mkmeta(2, 0, 1))\n            .unwrap();\n\n        mkmeta_pre(2, 0, 0, \"rc1\")\n            .check_compatibility_and_update(mkmeta(2, 0, 0))\n            .unwrap();\n\n        // Now check some failures..\n\n        mkmeta_pre(2, 0, 0, \"rc1\")\n            .check_compatibility_and_update(mkmeta_pre(2, 0, 0, \"rc2\"))\n            .unwrap_err();\n\n        mkmeta_pre(2, 0, 0, \"rc2\")\n            .check_compatibility_and_update(mkmeta_pre(2, 0, 0, \"rc1\"))\n            .unwrap_err();\n\n        mkmeta(2, 0, 0)\n            .check_compatibility_and_update(mkmeta_pre(2, 1, 0, \"rc1\"))\n            .unwrap_err();\n    }\n}\n"
  },
  {
    "path": "crates/core/src/database_logger.rs",
    "content": "use bytes::Bytes;\nuse chrono::{NaiveDate, Utc};\nuse futures::stream::{self, BoxStream};\nuse futures::{Stream, StreamExt as _, TryStreamExt};\nuse pin_project_lite::pin_project;\nuse std::collections::VecDeque;\nuse std::fs::File;\nuse std::future;\nuse std::io::{self, Read, Seek, Write};\nuse std::path::Path;\nuse std::pin::Pin;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\nuse tokio::io::{AsyncRead, BufReader};\nuse tokio::sync::{broadcast, mpsc, oneshot};\nuse tokio_stream::wrappers::errors::BroadcastStreamRecvError;\nuse tokio_stream::wrappers::BroadcastStream;\nuse tokio_util::io::ReaderStream;\n\nuse spacetimedb_paths::server::{ModuleLogPath, ModuleLogsDir};\n\nuse crate::util::asyncify;\n\npub struct DatabaseLogger {\n    cmd: mpsc::UnboundedSender<Cmd>,\n}\n\n#[derive(Debug, thiserror::Error)]\n#[error(\"database logger panicked\")]\npub struct LoggerPanicked;\n\nimpl<T> From<mpsc::error::SendError<T>> for LoggerPanicked {\n    fn from(_: mpsc::error::SendError<T>) -> Self {\n        Self\n    }\n}\n\nimpl From<oneshot::error::RecvError> for LoggerPanicked {\n    fn from(_: oneshot::error::RecvError) -> Self {\n        Self\n    }\n}\n\npub type LogStream = BoxStream<'static, io::Result<Bytes>>;\n\n/// Storage backend of a [DatabaseLogger].\ntrait Logger {\n    /// Append the serialized log `record` at timestamp `ts` to this logger.\n    fn append(&mut self, ts: chrono::DateTime<Utc>, record: Bytes);\n    /// Calculate the size of this logger in bytes.\n    fn size(&self) -> io::Result<u64>;\n    /// Read up to `n` lines from the tail of this logger into memory.\n    fn tail(&self, n: u32) -> io::Result<Bytes>;\n    /// Stream up to `n` lines from the tail of this logger.\n    /// If `n` is `None`, stream all the contained lines.\n    fn tail_stream(&self, n: Option<u32>) -> LogStream;\n    /// Sync data to disk (or alternative backing storage).\n    fn sync_data(&self) -> io::Result<()>;\n}\n\n/// [Logger] that stores log records in a file.\n///\n/// The file is rotated daily upon calling [Logger::append].\nstruct FileLogger {\n    file: File,\n    date: NaiveDate,\n    path: ModuleLogPath,\n}\n\nimpl FileLogger {\n    pub fn open(path: ModuleLogPath) -> io::Result<Self> {\n        let date = path.date();\n        let file = path.open_file(File::options().create(true).append(true))?;\n        Ok(Self { file, date, path })\n    }\n\n    fn maybe_rotate(&mut self, record_date: NaiveDate) {\n        if record_date > self.date {\n            let new_path = self.path.with_date(record_date);\n            *self = Self::open(new_path).unwrap();\n        }\n    }\n}\n\nimpl Logger for FileLogger {\n    fn append(&mut self, ts: chrono::DateTime<Utc>, record: Bytes) {\n        self.maybe_rotate(ts.date_naive());\n        self.file.write_all(&record).unwrap();\n    }\n\n    fn size(&self) -> io::Result<u64> {\n        self.file.metadata().map(|stat| stat.len())\n    }\n\n    fn tail(&self, n: u32) -> io::Result<Bytes> {\n        let mut file = File::open(&self.path)?;\n        read_lines(&mut file, n).map(Into::into)\n    }\n\n    fn tail_stream(&self, n: Option<u32>) -> LogStream {\n        stream::once(asyncify({\n            let path = self.path.clone();\n            move || {\n                let mut file = File::open(path)?;\n                if let Some(n) = n {\n                    let mut buf = seek_buffer(n);\n                    seek_to(&mut file, &mut buf, n)?;\n                }\n\n                Ok::<_, io::Error>(tokio::fs::File::from_std(file))\n            }\n        }))\n        .map_ok(ReaderStream::new)\n        .try_flatten()\n        .boxed()\n    }\n\n    fn sync_data(&self) -> io::Result<()> {\n        self.file.sync_data()\n    }\n}\n\n/// [Logger] that stores log records in memory.\nstruct MemoryLogger {\n    log: VecDeque<Bytes>,\n    size: u64,\n    max_size: u64,\n}\n\nimpl MemoryLogger {\n    pub fn new(max_size: u64) -> Self {\n        Self {\n            log: <_>::default(),\n            size: 0,\n            max_size,\n        }\n    }\n\n    fn compact(&mut self) {\n        while self.size > self.max_size {\n            let Some(evicted) = self.log.pop_front() else {\n                break;\n            };\n            self.size -= evicted.len() as u64;\n        }\n    }\n}\n\nimpl Logger for MemoryLogger {\n    fn append(&mut self, _: chrono::DateTime<Utc>, record: Bytes) {\n        self.size += record.len() as u64;\n        self.log.push_back(record);\n        self.compact();\n    }\n\n    fn size(&self) -> io::Result<u64> {\n        Ok(self.size)\n    }\n\n    fn tail(&self, n: u32) -> io::Result<Bytes> {\n        let total = self.log.len();\n        let start = total.saturating_sub(n as _);\n        Ok(self.log.range(start..).flatten().copied().collect())\n    }\n\n    fn tail_stream(&self, n: Option<u32>) -> LogStream {\n        let total = self.log.len();\n        let start = total.saturating_sub(n.map(|x| x as usize).unwrap_or(total));\n        stream::iter(self.log.range(start..).cloned().collect::<Vec<_>>())\n            .map(Ok)\n            .boxed()\n    }\n\n    fn sync_data(&self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Deserialize)]\npub enum LogLevel {\n    Error,\n    Warn,\n    Info,\n    Debug,\n    Trace,\n    Panic,\n}\n\nimpl From<u8> for LogLevel {\n    fn from(level: u8) -> Self {\n        match level {\n            0 => LogLevel::Error,\n            1 => LogLevel::Warn,\n            2 => LogLevel::Info,\n            3 => LogLevel::Debug,\n            4 => LogLevel::Trace,\n            101 => LogLevel::Panic,\n            _ => LogLevel::Debug,\n        }\n    }\n}\n\n#[serde_with::skip_serializing_none]\n#[serde_with::serde_as]\n#[derive(serde::Serialize, Copy, Clone)]\n#[cfg_attr(test, derive(serde::Deserialize, Debug))]\npub struct Record<'a> {\n    #[serde_as(as = \"serde_with::TimestampMicroSeconds\")]\n    pub ts: chrono::DateTime<Utc>,\n    /// Target of the log call (usually source namespace or `mod`).\n    ///\n    /// Provided by the WASM guest as an argument to the `console_log` host function.\n    ///\n    /// The special sentinel value [`Record::SENTINEL_INJECTED_TARGET`]` denotes logs injected by the [`SystemLogger`].\n    pub target: Option<&'a str>,\n    /// Filename of the source location of the log call.\n    ///\n    /// Provided by the WASM guest as an argument to the `console_log` host function.\n    ///\n    /// The special sentinel value [`Record::SENTINEL_INJECTED_FILENAME`]` denotes logs injected by the [`SystemLogger`].\n    pub filename: Option<&'a str>,\n    pub line_number: Option<u32>,\n    /// Which exported function (i.e. reducer) was being called when this message was produced.\n    ///\n    /// Unlike `target`, `filename` and `line_number`, this is not provided by the WASM guest.\n    /// Instead, the `WasmInstanceEnv` remembers what function call is in progress and adds it to the record.\n    ///\n    /// The special sentinel value [`Record::SENTINEL_INJECTED_FUNCTION`] denotes logs injected by the [`SystemLogger`].\n    pub function: Option<&'a str>,\n    pub message: &'a str,\n}\n\nimpl<'a> Record<'a> {\n    pub const SENTINEL_INJECTED_FUNCTION: Option<&'static str> = Some(\"__spacetimedb__\");\n    pub const SENTINEL_INJECTED_TARGET: Option<&'static str> = Some(\"__spacetimedb__\");\n    pub const SENTINEL_INJECTED_FILENAME: Option<&'static str> = Some(\"__spacetimedb__\");\n\n    /// Create a log `Record` for a system message, not attributed to any reducer or user filename.\n    ///\n    /// The resulting `Record` will draw from [`chrono::Utc::now`] for its timestamp,\n    /// have `line_number: None`,\n    /// and will use [`Self::SENTINEL_INJECTED_FILENAME`], [`Self::SENTINEL_INJECTED_FUNCTION`]\n    /// and [`Self::SENTINEL_INJECTED_TARGET`].\n    pub fn injected(message: &'a str) -> Self {\n        Record {\n            ts: chrono::Utc::now(),\n            target: Self::SENTINEL_INJECTED_TARGET,\n            filename: Self::SENTINEL_INJECTED_FILENAME,\n            line_number: None,\n            function: Self::SENTINEL_INJECTED_FUNCTION,\n            message,\n        }\n    }\n}\n\npub trait BacktraceProvider {\n    fn capture(&self) -> Box<dyn ModuleBacktrace>;\n}\n\nimpl BacktraceProvider for () {\n    fn capture(&self) -> Box<dyn ModuleBacktrace> {\n        Box::new(())\n    }\n}\n\npub trait ModuleBacktrace {\n    fn frames(&self) -> Vec<BacktraceFrame<'_>>;\n}\n\nimpl ModuleBacktrace for () {\n    fn frames(&self) -> Vec<BacktraceFrame<'_>> {\n        vec![]\n    }\n}\n\n#[serde_with::skip_serializing_none]\n#[serde_with::serde_as]\n#[derive(serde::Serialize)]\npub struct BacktraceFrame<'a> {\n    #[serde_as(as = \"Option<DemangleSymbol>\")]\n    pub module_name: Option<&'a str>,\n    #[serde_as(as = \"Option<DemangleSymbol>\")]\n    pub func_name: Option<&'a str>,\n}\n\nstruct DemangleSymbol;\nimpl serde_with::SerializeAs<&str> for DemangleSymbol {\n    fn serialize_as<S>(source: &&str, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        if let Ok(sym) = rustc_demangle::try_demangle(source) {\n            serializer.serialize_str(&sym.to_string())\n        } else {\n            serializer.serialize_str(source)\n        }\n    }\n}\n\n#[serde_with::skip_serializing_none]\n#[derive(serde::Serialize)]\n#[serde(tag = \"level\")]\nenum LogEvent<'a> {\n    Error(Record<'a>),\n    Warn(Record<'a>),\n    Info(Record<'a>),\n    Debug(Record<'a>),\n    Trace(Record<'a>),\n    Panic {\n        #[serde(flatten)]\n        record: Record<'a>,\n        trace: &'a [BacktraceFrame<'a>],\n    },\n}\n\nimpl DatabaseLogger {\n    pub fn in_memory(max_size: u64) -> Self {\n        let logger = MemoryLogger::new(max_size);\n        Self::with_logger(logger)\n    }\n\n    pub fn open_today(logs_dir: ModuleLogsDir) -> Self {\n        Self::open_file(logs_dir.today())\n    }\n\n    fn open_file(path: ModuleLogPath) -> Self {\n        let logger = FileLogger::open(path).unwrap();\n        Self::with_logger(logger)\n    }\n\n    fn with_logger(logger: impl Logger + Send + 'static) -> Self {\n        let (broadcast, _) = broadcast::channel(64);\n        let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();\n        let worker = DatabaseLoggerWorker::new(logger, broadcast);\n        tokio::spawn(worker.run(cmd_rx));\n\n        Self { cmd: cmd_tx }\n    }\n\n    /// Determine the storage size of this logger.\n    ///\n    /// If the logger is [Self::in_memory], returns the resident size\n    /// (of the serialized log records excl. overhead).\n    ///\n    /// If the logger is backed by disk storage, returns the size of the most\n    /// recent log file.\n    #[tracing::instrument(level = \"trace\", name = \"DatabaseLogger::size\", skip(self), err)]\n    pub fn size(&self) -> io::Result<u64> {\n        let (tx, rx) = oneshot::channel();\n        fn panicked(_: impl std::error::Error) -> io::Error {\n            io::Error::other(\"log worker panicked\")\n        }\n        self.cmd.send(Cmd::GetSize { reply: tx }).map_err(panicked)?;\n        rx.blocking_recv().map_err(panicked)?\n    }\n\n    pub fn write(&self, level: LogLevel, &record: &Record<'_>, bt: &dyn BacktraceProvider) {\n        let (trace, frames);\n        let event = match level {\n            LogLevel::Error => LogEvent::Error(record),\n            LogLevel::Warn => LogEvent::Warn(record),\n            LogLevel::Info => LogEvent::Info(record),\n            LogLevel::Debug => LogEvent::Debug(record),\n            LogLevel::Trace => LogEvent::Trace(record),\n            LogLevel::Panic => {\n                trace = bt.capture();\n                frames = trace.frames();\n                LogEvent::Panic { record, trace: &frames }\n            }\n        };\n        // TODO(perf): Reuse serialization buffer.\n        let mut buf = serde_json::to_string(&event).unwrap();\n        buf.push('\\n');\n        let buf = Bytes::from(buf);\n        self.cmd\n            .send(Cmd::Append {\n                ts: record.ts,\n                record: buf,\n            })\n            .expect(\"log worker panicked\");\n    }\n\n    /// Stream the contents of this logger.\n    ///\n    /// If `n` is `Some`, only yield up to the last `n` lines in the log.\n    /// If `follow` is `true`, the stream waits for new records to be appended\n    /// to the log (via [Self::write]) and yields them as they become available.\n    pub async fn tail(&self, n: Option<u32>, follow: bool) -> Result<LogStream, LoggerPanicked> {\n        let (tx, rx) = oneshot::channel();\n        self.cmd.send(Cmd::Tail { n, follow, reply: tx })?;\n        Ok(rx.await?)\n    }\n\n    /// Read the most recent logs in `logs_dir`, up to `num_lines`.\n    ///\n    /// Note that this only reads from the most recent log file, even if it\n    /// contains less than `num_lines` lines.\n    ///\n    /// If no log file exists on disk, the stream will be empty.\n    pub fn read_latest_on_disk(logs_dir: ModuleLogsDir, num_lines: Option<u32>) -> LogStream {\n        stream::once(asyncify(move || {\n            let Some(mut file) = Self::open_most_recent(logs_dir)? else {\n                return Ok(None);\n            };\n            if let Some(n) = num_lines {\n                let mut buf = seek_buffer(n);\n                seek_to(&mut file, &mut buf, n)?;\n            }\n\n            Ok::<_, io::Error>(Some(file))\n        }))\n        .map_ok(into_file_stream)\n        .try_flatten()\n        .boxed()\n    }\n\n    /// Open the most recent log file found in `logs_dir`, or `None` if none exists.\n    fn open_most_recent(logs_dir: ModuleLogsDir) -> io::Result<Option<File>> {\n        let path = logs_dir.today();\n        match open_file(&path)? {\n            Some(file) => Ok(Some(file)),\n            None => {\n                let logs_dir = path.popped();\n                // `most_recent` errors if the directory doesn't exist.\n                if !logs_dir.0.try_exists()? {\n                    return Ok(None);\n                }\n                let Some(path) = logs_dir.most_recent()? else {\n                    return Ok(None);\n                };\n                open_file(&path)\n            }\n        }\n    }\n\n    pub fn system_logger(&self) -> &SystemLogger {\n        // SAFETY: SystemLogger is repr(transparent) over DatabaseLogger\n        unsafe { &*(self as *const DatabaseLogger as *const SystemLogger) }\n    }\n}\n\nenum Cmd {\n    Append {\n        ts: chrono::DateTime<Utc>,\n        record: Bytes,\n    },\n    GetSize {\n        reply: oneshot::Sender<io::Result<u64>>,\n    },\n    Tail {\n        n: Option<u32>,\n        follow: bool,\n        reply: oneshot::Sender<LogStream>,\n    },\n}\n\nstruct DatabaseLoggerWorker<T> {\n    logger: Arc<tokio::sync::Mutex<T>>,\n    broadcast: broadcast::Sender<Bytes>,\n}\n\nimpl<T: Logger + Send + 'static> DatabaseLoggerWorker<T> {\n    fn new(logger: T, broadcast: broadcast::Sender<Bytes>) -> Self {\n        let logger = Arc::new(tokio::sync::Mutex::new(logger));\n        Self { logger, broadcast }\n    }\n\n    async fn run(self, mut cmd: mpsc::UnboundedReceiver<Cmd>) {\n        while let Some(cmd) = cmd.recv().await {\n            match cmd {\n                Cmd::Append { ts, record } => self.append(ts, record).await,\n                Cmd::GetSize { reply } => {\n                    let size = self.size().await;\n                    let _ = reply.send(size);\n                }\n                Cmd::Tail { n, follow, reply } => {\n                    let logs = self.tail(n, follow).await;\n                    let _ = reply.send(logs);\n                }\n            }\n        }\n    }\n\n    async fn append(&self, ts: chrono::DateTime<Utc>, record: Bytes) {\n        asyncify({\n            let logger = self.logger.clone();\n            let record = record.clone();\n            move || logger.blocking_lock().append(ts, record)\n        })\n        .await;\n        let _ = self.broadcast.send(record);\n    }\n\n    async fn size(&self) -> io::Result<u64> {\n        let logger = self.logger.clone();\n        asyncify(move || logger.blocking_lock().size()).await\n    }\n\n    async fn tail(&self, n: Option<u32>, follow: bool) -> LogStream {\n        // If following isn't requested, we can stream the data.\n        if !follow {\n            return self.logger.lock().await.tail_stream(n);\n        }\n        match n {\n            // If we don't need to access the disk,\n            // locking and spawning can be avoided.\n            None | Some(0) => self.subscribe().map(Ok).boxed(),\n\n            // Otherwise, we need to hold the lock to prevent writes\n            // while we gather a snapshot of the persistent tail.\n            Some(n) => {\n                // Cap reading the tail into memory at a few hundred KiB.\n                let n = n.min(2500);\n                let (tail, more) = {\n                    let inner = self.logger.clone().lock_owned().await;\n                    let more = self.subscribe();\n                    asyncify(move || {\n                        inner.sync_data().expect(\"error syncing data to disk\");\n                        (inner.tail(n), more)\n                    })\n                }\n                .await;\n\n                stream::once(future::ready(tail)).chain(more.map(Ok)).boxed()\n            }\n        }\n    }\n\n    fn subscribe(&self) -> impl Stream<Item = Bytes> + use<T> {\n        BroadcastStream::new(self.broadcast.subscribe()).filter_map(move |x| {\n            future::ready(match x {\n                Ok(chunk) => Some(chunk),\n                Err(BroadcastStreamRecvError::Lagged(skipped)) => {\n                    log::trace!(\"skipped {skipped} lines in module log\");\n                    None\n                }\n            })\n        })\n    }\n}\n\nfn read_lines(file: &mut File, num_lines: u32) -> io::Result<Vec<u8>> {\n    let mut buf = seek_buffer(num_lines);\n    seek_to(file, &mut buf, num_lines)?;\n    buf.clear();\n\n    file.read_to_end(&mut buf)?;\n\n    Ok(buf)\n}\n\n/// Allocate a buffer to use with [seek_to].\n///\n/// We assume a log line is typically around 150 bytes long, and allocate space\n/// to fit `num_lines` in one read. The max size of the buffer is 64KiB.\nfn seek_buffer(num_lines: u32) -> Vec<u8> {\n    let chunk_size = std::cmp::min((num_lines as u64 * 150).next_power_of_two(), 0x10_000);\n    vec![0; chunk_size as usize]\n}\n\n/// Set `file`'s position such that reading to the end will yield `num_lines`.\n///\n/// If `file` contains less than `num_lines`, the position is set to the start.\n///\n/// The function repeatedly fills `buf` from the end of the file, and counts the\n/// number of LF characters in the buffer at each step until `num_lines` is\n/// satisfied.\n///\n/// `buf` should be created via [seek_buffer] and is supplied by the caller in\n/// order to allow reuse of the allocation.\nfn seek_to(file: &mut File, buf: &mut [u8], num_lines: u32) -> io::Result<()> {\n    let mut lines_read: u32 = 0;\n    // the file should end in a newline, so we skip that one character\n    let mut pos = file.seek(io::SeekFrom::End(0))?.saturating_sub(1) as usize;\n    'outer: while pos > 0 {\n        let (new_pos, buf) = match pos.checked_sub(buf.len()) {\n            Some(pos) => (pos, &mut buf[..]),\n            None => (0, &mut buf[..pos]),\n        };\n        pos = new_pos;\n        read_exact_at(file, buf, pos as u64)?;\n        for lf_pos in memchr::Memchr::new(b'\\n', buf).rev() {\n            lines_read += 1;\n            if lines_read >= num_lines {\n                pos += lf_pos + 1;\n                break 'outer;\n            }\n        }\n    }\n    file.seek(io::SeekFrom::Start(pos as u64))?;\n\n    Ok(())\n}\n\nfn read_exact_at(file: &std::fs::File, buf: &mut [u8], offset: u64) -> io::Result<()> {\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::FileExt;\n        file.read_exact_at(buf, offset)\n    }\n    #[cfg(not(unix))]\n    {\n        (&*file).seek(io::SeekFrom::Start(offset))?;\n        (&*file).read_exact(buf)\n    }\n}\n\n/// Open the [File] at `path` for reading, or `None` if the file doesn't exist.\nfn open_file(path: impl AsRef<Path>) -> io::Result<Option<File>> {\n    File::open(path).map(Some).or_else(|e| {\n        if e.kind() == io::ErrorKind::NotFound {\n            Ok(None)\n        } else {\n            Err(e)\n        }\n    })\n}\n\n/// Create a buffered [Stream] from a file.\n///\n/// If `file` is `None`, the stream is empty.\nfn into_file_stream(file: impl Into<Option<File>>) -> impl Stream<Item = io::Result<Bytes>> {\n    ReaderStream::new(BufReader::new(MaybeFile::new(file.into())))\n}\n\npin_project! {\n    #[project = MaybeFileProj]\n    enum MaybeFile {\n        File { #[pin] inner: tokio::fs::File },\n        Empty,\n    }\n}\n\nimpl MaybeFile {\n    pub fn new(file: Option<File>) -> Self {\n        match file.map(tokio::fs::File::from_std) {\n            Some(inner) => Self::File { inner },\n            None => Self::Empty,\n        }\n    }\n}\n\nimpl AsyncRead for MaybeFile {\n    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut tokio::io::ReadBuf<'_>) -> Poll<io::Result<()>> {\n        match self.project() {\n            MaybeFileProj::File { inner } => inner.poll_read(cx, buf),\n            MaybeFileProj::Empty => Poll::Ready(Ok(())),\n        }\n    }\n}\n\n/// Somewhat ad-hoc wrapper around [`DatabaseLogger`] which allows to inject\n/// \"system messages\" into the user-retrievable database / module log\n#[repr(transparent)]\npub struct SystemLogger {\n    inner: DatabaseLogger,\n}\n\nimpl SystemLogger {\n    pub fn info(&self, msg: &str) {\n        self.inner.write(LogLevel::Info, &Self::record(msg), &())\n    }\n\n    pub fn warn(&self, msg: &str) {\n        self.inner.write(LogLevel::Warn, &Self::record(msg), &())\n    }\n\n    pub fn error(&self, msg: &str) {\n        self.inner.write(LogLevel::Error, &Self::record(msg), &())\n    }\n\n    fn record(message: &str) -> Record<'_> {\n        Record::injected(message)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{ops::Range, sync::Arc};\n\n    use bytes::BytesMut;\n    use futures::TryStreamExt as _;\n\n    use crate::util::asyncify;\n\n    use super::{DatabaseLogger, LogLevel, Record};\n\n    async fn write_logs(logger: Arc<DatabaseLogger>, r: Range<usize>) {\n        asyncify(move || {\n            for i in r {\n                logger.write(\n                    LogLevel::Info,\n                    &Record {\n                        ts: chrono::Utc::now(),\n                        target: None,\n                        filename: None,\n                        line_number: None,\n                        function: None,\n                        message: &format!(\"log line {i}\"),\n                    },\n                    &(),\n                );\n            }\n        })\n        .await\n    }\n\n    fn deserialize_logs<'a>(raw: &'a [u8]) -> Result<Vec<Record<'a>>, serde_json::Error> {\n        serde_json::StreamDeserializer::new(serde_json::de::SliceRead::new(raw)).collect()\n    }\n\n    fn drop_logger(logger: Arc<DatabaseLogger>) {\n        Arc::try_unwrap(logger)\n            .map(drop)\n            .map_err(drop)\n            .expect(\"logger should be unique\");\n    }\n\n    /// Test calling [DatabaseLogger::tail] with `Some(n)`.\n    ///\n    /// Like `tail -n`.\n    async fn tail_n(logger: DatabaseLogger) {\n        let logger = Arc::new(logger);\n\n        write_logs(logger.clone(), 0..10).await;\n\n        let a = logger\n            .tail(Some(10), false)\n            .await\n            .unwrap()\n            .try_collect::<BytesMut>()\n            .await\n            .unwrap();\n        let b = logger\n            .tail(None, false)\n            .await\n            .unwrap()\n            .try_collect::<BytesMut>()\n            .await\n            .unwrap();\n        assert_eq!(a, b);\n\n        let c = logger\n            .tail(Some(5), false)\n            .await\n            .unwrap()\n            .try_collect::<BytesMut>()\n            .await\n            .unwrap();\n        let json_logs = deserialize_logs(&c).unwrap();\n\n        assert_eq!(json_logs.len(), 5);\n        assert_eq!(json_logs[0].message, \"log line 5\");\n        assert_eq!(json_logs[4].message, \"log line 9\");\n    }\n\n    /// Test calling [DatabaseLogger::tail] with\n    /// `follow = true`.\n    ///\n    /// Like `tail -f`.\n    async fn tail_f(logger: DatabaseLogger) {\n        let logger = Arc::new(logger);\n\n        let stream = logger.tail(None, true).await.unwrap().try_collect::<BytesMut>();\n        write_logs(logger.clone(), 0..10).await;\n        // Drop logger so stream terminates.\n        drop_logger(logger);\n\n        let raw_logs = stream.await.unwrap();\n        let json_logs = deserialize_logs(&raw_logs).unwrap();\n\n        assert_eq!(json_logs.len(), 10);\n        assert_eq!(json_logs[0].message, \"log line 0\");\n        assert_eq!(json_logs[9].message, \"log line 9\");\n    }\n\n    /// Test calling [DatabaseLogger::tail] with\n    /// both `Some(n)` and `follow = true`.\n    ///\n    /// Like `tail -n N -f`.\n    async fn tail_nf(logger: DatabaseLogger) {\n        let logger = Arc::new(logger);\n\n        write_logs(logger.clone(), 0..10).await;\n        let stream = logger.tail(Some(5), true).await.unwrap().try_collect::<BytesMut>();\n        write_logs(logger.clone(), 10..20).await;\n        // Drop logger so stream terminates.\n        drop_logger(logger);\n\n        let raw_logs = stream.await.unwrap();\n        let json_logs = deserialize_logs(&raw_logs).unwrap();\n\n        assert_eq!(json_logs.len(), 15);\n        assert_eq!(json_logs[0].message, \"log line 5\");\n        assert_eq!(json_logs[14].message, \"log line 19\");\n    }\n\n    mod memory {\n        use super::DatabaseLogger;\n\n        #[tokio::test]\n        async fn tail_n() {\n            super::tail_n(DatabaseLogger::in_memory(1024)).await\n        }\n\n        #[tokio::test]\n        async fn tail_f() {\n            super::tail_f(DatabaseLogger::in_memory(1024)).await\n        }\n\n        #[tokio::test]\n        async fn tail_nf() {\n            super::tail_nf(DatabaseLogger::in_memory(1024)).await\n        }\n    }\n\n    mod file {\n        use std::future::Future;\n\n        use spacetimedb_paths::{server::ModuleLogsDir, FromPathUnchecked};\n        use tempfile::tempdir;\n\n        use super::DatabaseLogger;\n\n        #[tokio::test]\n        async fn tail_n() {\n            with_file_logger(super::tail_n).await\n        }\n\n        #[tokio::test]\n        async fn tail_f() {\n            with_file_logger(super::tail_f).await\n        }\n\n        #[tokio::test]\n        async fn tail_nf() {\n            with_file_logger(super::tail_nf).await\n        }\n\n        async fn with_file_logger<F, Fut>(f: F)\n        where\n            F: FnOnce(DatabaseLogger) -> Fut,\n            Fut: Future<Output = ()>,\n        {\n            let tmp = tempdir().unwrap();\n            let logs_dir = ModuleLogsDir::from_path_unchecked(tmp.path());\n            let logger = DatabaseLogger::open_today(logs_dir);\n\n            f(logger).await\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/db/durability.rs",
    "content": "use std::{sync::Arc, time::Duration};\n\nuse futures::TryFutureExt as _;\nuse log::{error, info};\nuse spacetimedb_commitlog::payload::{\n    txdata::{Mutations, Ops},\n    Txdata,\n};\nuse spacetimedb_datastore::{execution_context::ReducerContext, traits::TxData};\nuse spacetimedb_durability::{DurableOffset, Transaction, TxOffset};\nuse spacetimedb_lib::Identity;\nuse tokio::{\n    runtime,\n    sync::{\n        futures::OwnedNotified,\n        mpsc::{channel, unbounded_channel, Receiver, Sender, UnboundedReceiver, UnboundedSender},\n        oneshot, Notify,\n    },\n    time::timeout,\n};\n\nuse crate::db::persistence::Durability;\n\n/// A request to persist a transaction or to terminate the actor.\npub struct DurabilityRequest {\n    reducer_context: Option<ReducerContext>,\n    tx_data: Arc<TxData>,\n}\n\ntype ShutdownReply = oneshot::Sender<OwnedNotified>;\n\n/// Represents a handle to a background task that persists transactions\n/// according to the [`Durability`] policy provided.\n///\n/// This exists to avoid holding a transaction lock while\n/// preparing the [TxData] for processing by the [Durability] layer.\npub struct DurabilityWorker {\n    database: Identity,\n    request_tx: UnboundedSender<DurabilityRequest>,\n    shutdown: Sender<ShutdownReply>,\n    durability: Arc<Durability>,\n    runtime: runtime::Handle,\n}\n\nimpl DurabilityWorker {\n    /// Create a new [`DurabilityWorker`] using the given `durability` policy.\n    ///\n    /// Background tasks will be spawned onto to provided tokio `runtime`.\n    pub fn new(database: Identity, durability: Arc<Durability>, runtime: runtime::Handle) -> Self {\n        let (request_tx, request_rx) = unbounded_channel();\n        let (shutdown_tx, shutdown_rx) = channel(1);\n\n        let actor = DurabilityWorkerActor {\n            request_rx,\n            shutdown: shutdown_rx,\n            durability: durability.clone(),\n        };\n        let _enter = runtime.enter();\n        tokio::spawn(actor.run());\n\n        Self {\n            database,\n            request_tx,\n            shutdown: shutdown_tx,\n            durability,\n            runtime,\n        }\n    }\n\n    /// Request that a transaction be made durable.\n    /// That is, if `(tx_data, ctx)` should be appended to the commitlog, do so.\n    ///\n    /// Note that by this stage\n    /// [`spacetimedb_datastore::locking_tx_datastore::committed_state::tx_consumes_offset`]\n    /// has already decided based on the reducer and operations whether the transaction should be appended;\n    /// this method is responsible only for reading its decision out of the `tx_data`\n    /// and calling `durability.append_tx`.\n    ///\n    /// This method does not block,\n    /// and sends the work to an actor that collects data and calls `durability.append_tx`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the durability worker has already closed the receive end of\n    /// its queue. This may happen if\n    ///\n    /// - the backing [Durability] has panicked, or\n    /// - [Self::shutdown] was called\n    ///\n    pub fn request_durability(&self, reducer_context: Option<ReducerContext>, tx_data: &Arc<TxData>) {\n        self.request_tx\n            .send(DurabilityRequest {\n                reducer_context,\n                tx_data: tx_data.clone(),\n            })\n            .unwrap_or_else(|_| panic!(\"durability actor vanished database={}\", self.database));\n    }\n\n    /// Get the [`DurableOffset`] of this database.\n    pub fn durable_tx_offset(&self) -> DurableOffset {\n        self.durability.durable_tx_offset()\n    }\n\n    /// Shut down the worker without dropping it,\n    /// flushing outstanding transaction.\n    ///\n    /// Closes the internal channel, then waits for the [DurableOffset] to\n    /// report the offset of the most recently enqueued transaction as durable.\n    ///\n    /// # Panics\n    ///\n    /// After this method was called, calling [Self::request_durability]\n    /// will panic.\n    pub async fn close(&self) -> Option<TxOffset> {\n        let (done_tx, done_rx) = oneshot::channel();\n        // Channel errors can be ignored.\n        // It just means that the actor already exited.\n        let _ = self\n            .shutdown\n            .send(done_tx)\n            .map_err(drop)\n            .and_then(|()| done_rx.map_err(drop))\n            .and_then(|done| async move {\n                done.await;\n                Ok(())\n            })\n            .await;\n        self.durability.close().await\n    }\n\n    /// Consume `self` and run [Self::close].\n    ///\n    /// The `lock_file` is not dropped until the shutdown is complete (either\n    /// successfully or unsuccessfully). This is to prevent the database to be\n    /// re-opened for writing while there is still an active background task\n    /// writing to the commitlog.\n    ///\n    /// The shutdown task will be spawned onto the tokio runtime provided to\n    /// [Self::new]. This means that the task may still be running when this\n    /// method returns.\n    ///\n    /// `database_identity` is used to associate log records with the database\n    /// owning this durability worker.\n    ///\n    /// This method is used in the `Drop` impl for [crate::db::relational_db::RelationalDB].\n    pub(super) fn spawn_close(self, database_identity: Identity) {\n        let rt = self.runtime.clone();\n        rt.spawn(async move {\n            let label = format!(\"[{database_identity}]\");\n            // Apply a timeout, in case `Durability::close` doesn't terminate\n            // as advertised. This is a bug, but panicking here would not\n            // unwind at the call site.\n            match timeout(Duration::from_secs(10), self.close()).await {\n                Err(_elapsed) => {\n                    error!(\"{label} timeout waiting for durability worker shutdown\");\n                }\n                Ok(offset) => {\n                    info!(\"{label} durability worker shut down at tx offset: {offset:?}\");\n                }\n            }\n        });\n    }\n}\n\npub struct DurabilityWorkerActor {\n    request_rx: UnboundedReceiver<DurabilityRequest>,\n    shutdown: Receiver<ShutdownReply>,\n    durability: Arc<Durability>,\n}\n\nimpl DurabilityWorkerActor {\n    /// Processes requests to do durability.\n    async fn run(mut self) {\n        let done = scopeguard::guard(Arc::new(Notify::new()), |done| done.notify_waiters());\n        loop {\n            tokio::select! {\n                // Biased towards the shutdown channel,\n                // so that adding new requests is prevented promptly.\n                biased;\n\n                Some(reply) = self.shutdown.recv() => {\n                    self.request_rx.close();\n                    let _ = reply.send(done.clone().notified_owned());\n                },\n\n                req = self.request_rx.recv() => {\n                    let Some(DurabilityRequest { reducer_context, tx_data }) = req else {\n                        break;\n                    };\n                    Self::do_durability(&*self.durability, reducer_context, &tx_data);\n                }\n            }\n        }\n\n        info!(\"durability worker actor done\");\n    }\n\n    pub fn do_durability(durability: &Durability, reducer_context: Option<ReducerContext>, tx_data: &TxData) {\n        let Some(tx_offset) = tx_data.tx_offset() else {\n            let name = reducer_context.as_ref().map(|rcx| &rcx.name);\n            debug_assert!(\n                !tx_data.has_rows_or_connect_disconnect(name),\n                \"tx_data has no rows but has connect/disconnect: `{name:?}`\"\n            );\n            return;\n        };\n\n        let mut inserts: Box<_> = tx_data\n            .persistent_inserts()\n            .map(|(table_id, rowdata)| Ops { table_id, rowdata })\n            .collect();\n        // What we get from `tx_data` is not necessarily sorted,\n        // but the durability layer expects by-table_id sorted data.\n        // Unstable sorts are valid, there will only ever be one entry per table_id.\n        inserts.sort_unstable_by_key(|ops| ops.table_id);\n\n        let mut deletes: Box<_> = tx_data\n            .persistent_deletes()\n            .map(|(table_id, rowdata)| Ops { table_id, rowdata })\n            .collect();\n        deletes.sort_unstable_by_key(|ops| ops.table_id);\n\n        let mut truncates: Box<[_]> = tx_data.persistent_truncates().collect();\n        truncates.sort_unstable_by_key(|table_id| *table_id);\n\n        let inputs = reducer_context.map(|rcx| rcx.into());\n\n        debug_assert!(\n            !(inserts.is_empty() && truncates.is_empty() && deletes.is_empty() && inputs.is_none()),\n            \"empty transaction\"\n        );\n\n        let txdata = Txdata {\n            inputs,\n            outputs: None,\n            mutations: Some(Mutations {\n                inserts,\n                deletes,\n                truncates,\n            }),\n        };\n\n        // This does not block, as per trait docs.\n        durability.append_tx(Transaction {\n            offset: tx_offset,\n            txdata,\n        });\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{pin::pin, task::Poll};\n\n    use futures::FutureExt as _;\n    use pretty_assertions::assert_matches;\n    use spacetimedb_sats::product;\n    use spacetimedb_schema::table_name::TableName;\n    use tokio::sync::watch;\n\n    use super::*;\n    use crate::db::relational_db::Txdata;\n\n    #[derive(Default)]\n    struct CountingDurability {\n        appended: watch::Sender<Option<TxOffset>>,\n        durable: watch::Sender<Option<TxOffset>>,\n    }\n\n    impl CountingDurability {\n        async fn mark_durable(&self, offset: TxOffset) {\n            self.appended\n                .subscribe()\n                .wait_for(|x| x.is_some_and(|appended_offset| appended_offset >= offset))\n                .await\n                .unwrap();\n            self.durable.send_modify(|durable_offset| {\n                durable_offset.replace(offset);\n            });\n        }\n    }\n\n    impl spacetimedb_durability::Durability for CountingDurability {\n        type TxData = Txdata;\n\n        fn append_tx(&self, tx: Transaction<Self::TxData>) {\n            self.appended.send_modify(|offset| {\n                offset.replace(tx.offset);\n            });\n        }\n\n        fn durable_tx_offset(&self) -> DurableOffset {\n            self.durable.subscribe().into()\n        }\n\n        fn close(&self) -> spacetimedb_durability::Close {\n            let mut durable = self.durable.subscribe();\n            let appended = self.appended.subscribe();\n            async move {\n                let durable_offset = durable\n                    .wait_for(|durable| match (*durable).zip(*appended.borrow()) {\n                        Some((durable_offset, appended_offset)) => durable_offset >= appended_offset,\n                        None => false,\n                    })\n                    .await\n                    .unwrap();\n                *durable_offset\n            }\n            .boxed()\n        }\n    }\n\n    #[tokio::test]\n    async fn shutdown_waits_until_durable() {\n        let durability = Arc::new(CountingDurability::default());\n        let worker = DurabilityWorker::new(Identity::ONE, durability.clone(), runtime::Handle::current());\n\n        for i in 0..=10 {\n            let mut txdata = TxData::default();\n            txdata.set_tx_offset(i);\n            // Ensure the transaction is non-empty.\n            txdata.set_inserts_for_table(4000.into(), &TableName::for_test(\"foo\"), [product![42u8]].into());\n\n            worker.request_durability(None, &Arc::new(txdata));\n        }\n\n        let shutdown = worker.close();\n        let mut shutdown_fut = pin!(shutdown);\n        assert_matches!(\n            futures::poll!(&mut shutdown_fut),\n            Poll::Pending,\n            \"shutdown should be pending because requested > durable\"\n        );\n\n        durability.mark_durable(5).await;\n        assert_matches!(\n            futures::poll!(&mut shutdown_fut),\n            Poll::Pending,\n            \"shutdown should be pending because requested > durable\"\n        );\n\n        durability.mark_durable(10).await;\n        assert_matches!(\n            futures::poll!(&mut shutdown_fut),\n            Poll::Ready(Some(10)),\n            \"shutdown returns, reporting durable offset at 10\"\n        );\n        assert_eq!(\n            Some(10),\n            *durability.appended.borrow(),\n            \"durability should have appended up to tx offset 10\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/core/src/db/mod.rs",
    "content": "use std::sync::Arc;\n\nuse enum_map::EnumMap;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse tokio::sync::mpsc;\n\nuse crate::subscription::ExecutionCounters;\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_datastore::{locking_tx_datastore::datastore::TxMetrics, traits::TxData};\n\nmod durability;\npub mod persistence;\npub mod relational_db;\npub mod snapshot;\npub mod update;\n\n/// Whether SpacetimeDB is run in memory, or persists objects and\n/// a message log to disk.\n#[derive(Clone, Copy)]\npub enum Storage {\n    /// The object store is in memory, and no message log is kept.\n    Memory,\n\n    /// The object store is persisted to disk, and a message log is kept.\n    Disk,\n}\n\n/// Internal database config parameters\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Specifies the object storage model.\n    pub storage: Storage,\n    /// Specifies the page pool max size in bytes.\n    pub page_pool_max_size: Option<usize>,\n}\n\n/// A message that is processed by the [`spawn_metrics_recorder`] actor.\n/// We use a separate task to record metrics to avoid blocking transactions.\npub struct MetricsMessage {\n    /// The reducer the produced these metrics.\n    reducer: Option<ReducerName>,\n    /// Metrics from a mutable transaction.\n    metrics_for_writer: Option<TxMetrics>,\n    /// Metrics from a read-only transaction.\n    /// A message may have metrics for both types of transactions,\n    /// because metrics for a reducer and its subscription updates are recorded together.\n    metrics_for_reader: Option<TxMetrics>,\n    /// The row updates for an immutable transaction.\n    /// Needed for insert and delete counters.\n    tx_data: Option<Arc<TxData>>,\n    /// Cached metrics counters for each workload type.\n    counters: Arc<EnumMap<WorkloadType, ExecutionCounters>>,\n}\n\n/// The handle used to send work to the tx metrics recorder.\n#[derive(Clone)]\npub struct MetricsRecorderQueue {\n    tx: mpsc::UnboundedSender<MetricsMessage>,\n}\n\nimpl MetricsRecorderQueue {\n    pub fn send_metrics(\n        &self,\n        reducer: Option<ReducerName>,\n        metrics_for_writer: Option<TxMetrics>,\n        metrics_for_reader: Option<TxMetrics>,\n        tx_data: Option<Arc<TxData>>,\n        counters: Arc<EnumMap<WorkloadType, ExecutionCounters>>,\n    ) {\n        if let Err(err) = self.tx.send(MetricsMessage {\n            reducer,\n            metrics_for_writer,\n            metrics_for_reader,\n            tx_data,\n            counters,\n        }) {\n            log::warn!(\"failed to send metrics: {err}\");\n        }\n    }\n}\n\n/// Spawns a task for recording transaction metrics.\n/// Returns the handle for pushing metrics to the recorder.\npub fn spawn_tx_metrics_recorder() -> (MetricsRecorderQueue, tokio::task::AbortHandle) {\n    let (tx, mut rx) = mpsc::unbounded_channel();\n    let abort_handle = tokio::spawn(async move {\n        while let Some(MetricsMessage {\n            reducer,\n            metrics_for_writer,\n            metrics_for_reader,\n            tx_data,\n            counters,\n        }) = rx.recv().await\n        {\n            if let Some(tx_metrics) = metrics_for_writer {\n                tx_metrics.report(\n                    // If row updates are present,\n                    // they will always belong to the writer transaction.\n                    tx_data.as_deref(),\n                    reducer.as_ref(),\n                    |wl| &counters[wl],\n                );\n            }\n            if let Some(tx_metrics) = metrics_for_reader {\n                tx_metrics.report(\n                    // If row updates are present,\n                    // they will never belong to the reader transaction.\n                    // Passing row updates here will most likely panic.\n                    None,\n                    reducer.as_ref(),\n                    |wl| &counters[wl],\n                );\n            }\n        }\n    })\n    .abort_handle();\n    (MetricsRecorderQueue { tx }, abort_handle)\n}\n"
  },
  {
    "path": "crates/core/src/db/persistence.rs",
    "content": "use std::{io, sync::Arc};\n\nuse async_trait::async_trait;\nuse spacetimedb_commitlog::SizeOnDisk;\nuse spacetimedb_durability::{DurabilityExited, TxOffset};\nuse spacetimedb_paths::server::ServerDataDir;\nuse spacetimedb_snapshot::SnapshotRepository;\n\nuse crate::{messages::control_db::Database, util::asyncify};\n\nuse super::{\n    relational_db::{self, Txdata},\n    snapshot::{self, SnapshotDatabaseState, SnapshotWorker},\n};\n\n/// [spacetimedb_durability::Durability] impls with a [`Txdata`] transaction\n/// payload, suitable for use in the [`relational_db::RelationalDB`].\npub type Durability = dyn spacetimedb_durability::Durability<TxData = Txdata>;\n\n/// A function to determine the size on disk of the durable state of the\n/// local database instance. This is used for metrics and energy accounting\n/// purposes.\n///\n/// It is not part of the [`Durability`] trait because it must report disk\n/// usage of the local instance only, even if exclusively remote durability is\n/// configured or the database is in follower state.\npub type DiskSizeFn = Arc<dyn Fn() -> io::Result<SizeOnDisk> + Send + Sync>;\n\n/// Persistence services for a database.\npub struct Persistence {\n    /// The [Durability] to use, for persisting transactions.\n    pub durability: Arc<Durability>,\n    /// The [DiskSizeFn].\n    ///\n    /// Currently the expectation is that the reported size is the commitlog\n    /// size only.\n    pub disk_size: DiskSizeFn,\n    /// An optional [SnapshotWorker].\n    ///\n    /// The current expectation is that snapshots are only enabled for\n    /// persistent (as opposed to in-memory) databases. This is enforced by\n    /// this type.\n    pub snapshots: Option<SnapshotWorker>,\n    /// The tokio runtime onto which durability-related tasks shall be spawned.\n    pub runtime: tokio::runtime::Handle,\n}\n\nimpl Persistence {\n    /// Convenience constructor of a [Persistence] that handles boxing.\n    pub fn new(\n        durability: impl spacetimedb_durability::Durability<TxData = Txdata> + 'static,\n        disk_size: impl Fn() -> io::Result<SizeOnDisk> + Send + Sync + 'static,\n        snapshots: Option<SnapshotWorker>,\n        runtime: tokio::runtime::Handle,\n    ) -> Self {\n        Self {\n            durability: Arc::new(durability),\n            disk_size: Arc::new(disk_size),\n            snapshots,\n            runtime,\n        }\n    }\n\n    /// If snapshots are enabled, get the [SnapshotRepository] they are stored in.\n    pub fn snapshot_repo(&self) -> Option<&SnapshotRepository> {\n        self.snapshots.as_ref().map(|worker| worker.repo())\n    }\n\n    /// Get the [TxOffset] reported as durable by the [Durability] impl.\n    ///\n    /// Returns `Ok(None)` if no offset is durable yet, and `Err(DurabilityExited)`\n    /// if the [Durability] has shut down already.\n    pub fn durable_tx_offset(&self) -> Result<Option<TxOffset>, DurabilityExited> {\n        self.durability.durable_tx_offset().get()\n    }\n\n    /// Initialize the [SnapshotWorker], no-op if snapshots are not enabled.\n    pub(super) fn set_snapshot_state(&self, state: SnapshotDatabaseState) {\n        if let Some(worker) = &self.snapshots {\n            worker.set_state(state)\n        }\n    }\n\n    /// Convenience to deconstruct an [Option<Self>] into parts.\n    ///\n    /// Returns `(Some(durability), Some(disk_size), Option<SnapshotWorker>, Some(runtime))`\n    /// if `this` is `Some`, and `(None, None, None, None)` if `this` is `None`.\n    pub(super) fn unzip(\n        this: Option<Self>,\n    ) -> (\n        Option<Arc<Durability>>,\n        Option<DiskSizeFn>,\n        Option<SnapshotWorker>,\n        Option<tokio::runtime::Handle>,\n    ) {\n        this.map(\n            |Self {\n                 durability,\n                 disk_size,\n                 snapshots,\n                 runtime,\n             }| (Some(durability), Some(disk_size), snapshots, Some(runtime)),\n        )\n        .unwrap_or_default()\n    }\n}\n\n/// A persistence provider is a \"factory\" of sorts that can produce [Persistence]\n/// services for a given replica.\n///\n/// The [crate::host::HostController] uses this to obtain [Persistence]s from\n/// an external source, and construct [relational_db::RelationalDB]s with it.\n///\n/// This is an `async_trait` to allow it to be used as a trait object.\n#[async_trait]\npub trait PersistenceProvider: Send + Sync {\n    async fn persistence(&self, database: &Database, replica_id: u64) -> anyhow::Result<Persistence>;\n}\n\n/// The standard [PersistenceProvider] for non-replicated databases.\n///\n/// [Persistence] services are provided for the local [ServerDataDir].\n///\n/// Note that its [PersistenceProvider::persistence] impl will spawn a\n/// background task that [compresses] older commitlog segments whenever a\n/// snapshot is taken.\n///\n/// [compresses]: relational_db::snapshot_watching_commitlog_compressor\npub struct LocalPersistenceProvider {\n    data_dir: Arc<ServerDataDir>,\n}\n\nimpl LocalPersistenceProvider {\n    pub fn new(data_dir: impl Into<Arc<ServerDataDir>>) -> Self {\n        Self {\n            data_dir: data_dir.into(),\n        }\n    }\n}\n\n#[async_trait]\nimpl PersistenceProvider for LocalPersistenceProvider {\n    async fn persistence(&self, database: &Database, replica_id: u64) -> anyhow::Result<Persistence> {\n        let replica_dir = self.data_dir.replica(replica_id);\n        let snapshot_dir = replica_dir.snapshots();\n\n        let database_identity = database.database_identity;\n        let snapshot_worker =\n            asyncify(move || relational_db::open_snapshot_repo(snapshot_dir, database_identity, replica_id))\n                .await\n                .map(|repo| SnapshotWorker::new(repo, snapshot::Compression::Enabled))?;\n        let (durability, disk_size) = relational_db::local_durability(replica_dir, Some(&snapshot_worker)).await?;\n\n        tokio::spawn(relational_db::snapshot_watching_commitlog_compressor(\n            snapshot_worker.subscribe(),\n            None,\n            None,\n            durability.clone(),\n        ));\n\n        Ok(Persistence {\n            durability,\n            disk_size,\n            snapshots: Some(snapshot_worker),\n            runtime: tokio::runtime::Handle::current(),\n        })\n    }\n}\n"
  },
  {
    "path": "crates/core/src/db/relational_db.rs",
    "content": "use crate::db::durability::DurabilityWorker;\nuse crate::db::MetricsRecorderQueue;\nuse crate::error::{DBError, RestoreSnapshotError};\nuse crate::subscription::ExecutionCounters;\nuse crate::util::asyncify;\nuse crate::worker_metrics::WORKER_METRICS;\nuse anyhow::{anyhow, Context};\nuse enum_map::EnumMap;\nuse log::info;\nuse spacetimedb_commitlog::repo::OnNewSegmentFn;\nuse spacetimedb_commitlog::{self as commitlog, Commitlog, SizeOnDisk};\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::error::{DatastoreError, TableError, ViewError};\nuse spacetimedb_datastore::execution_context::{Workload, WorkloadType};\nuse spacetimedb_datastore::locking_tx_datastore::datastore::TxMetrics;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::{\n    IterByColEqMutTx, IterByColRangeMutTx, IterMutTx, StateView,\n};\nuse spacetimedb_datastore::locking_tx_datastore::{MutTxId, TxId};\nuse spacetimedb_datastore::system_tables::{\n    system_tables, StModuleRow, ST_CLIENT_ID, ST_CONNECTION_CREDENTIALS_ID, ST_VIEW_SUB_ID,\n};\nuse spacetimedb_datastore::system_tables::{StFields, StVarFields, StVarName, StVarRow, ST_MODULE_ID, ST_VAR_ID};\nuse spacetimedb_datastore::traits::{\n    InsertFlags, IsolationLevel, Metadata, MutTx as _, MutTxDatastore, Program, RowTypeForTable, Tx as _, TxDatastore,\n    UpdateFlags,\n};\nuse spacetimedb_datastore::{\n    locking_tx_datastore::{\n        datastore::Locking,\n        state_view::{IterByColEqTx, IterByColRangeTx},\n    },\n    traits::TxData,\n};\nuse spacetimedb_durability::{self as durability, History};\nuse spacetimedb_lib::bsatn::ToBsatn;\nuse spacetimedb_lib::db::auth::StAccess;\nuse spacetimedb_lib::db::raw_def::v9::{btree, RawModuleDefV9Builder, RawSql};\nuse spacetimedb_lib::st_var::StVarValue;\nuse spacetimedb_lib::ConnectionId;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_paths::server::{ReplicaDir, SnapshotsPath};\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductValue};\nuse spacetimedb_schema::def::{ModuleDef, TableDef, ViewDef};\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_schema::schema::{\n    ColumnSchema, IndexSchema, RowLevelSecuritySchema, Schema, SequenceSchema, TableSchema,\n};\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_snapshot::{ReconstructedSnapshot, SnapshotError, SnapshotRepository};\nuse spacetimedb_table::indexes::RowPointer;\nuse spacetimedb_table::page_pool::PagePool;\nuse spacetimedb_table::table::{RowRef, TableScanIter};\nuse std::borrow::Cow;\nuse std::io;\nuse std::ops::{Bound, RangeBounds};\nuse std::sync::Arc;\nuse tokio::sync::watch;\n\npub use super::persistence::{DiskSizeFn, Durability, Persistence};\npub use super::snapshot::SnapshotWorker;\npub use durability::{DurableOffset, TxOffset};\n\n// NOTE(cloutiertyler): We should be using the associated types, but there is\n// a bug in the Rust compiler that prevents us from doing so.\npub type MutTx = MutTxId; //<Locking as spacetimedb_datastore::traits::MutTx>::MutTx;\npub type Tx = TxId; //<Locking as spacetimedb_datastore::traits::Tx>::Tx;\n\ntype RowCountFn = Arc<dyn Fn(TableId, &str) -> i64 + Send + Sync>;\n\n/// The type of transactions committed by [RelationalDB].\npub type Txdata = commitlog::payload::Txdata<ProductValue>;\n\n/// We've added a module version field to the system tables, but we don't yet\n/// have the infrastructure to support multiple versions.\n/// All modules are currently locked to this version, but this will be\n/// relaxed post 1.0.\npub const ONLY_MODULE_VERSION: &str = \"0.0.1\";\n\n/// The set of clients considered connected to the database.\n///\n/// A client is considered connected if there exists a corresponding row in the\n/// `st_clients` system table.\n///\n/// If rows exist in `st_clients` upon [`RelationalDB::open`], the database was\n/// not shut down gracefully. Such \"dangling\" clients should be removed by\n/// calling [`crate::host::ModuleHost::call_identity_connected_disconnected`]\n/// for each entry in [`ConnectedClients`].\npub type ConnectedClients = HashSet<(Identity, ConnectionId)>;\n\npub struct RelationalDB {\n    database_identity: Identity,\n    owner_identity: Identity,\n\n    inner: Locking,\n    durability: Option<DurabilityWorker>,\n    snapshot_worker: Option<SnapshotWorker>,\n\n    row_count_fn: RowCountFn,\n    /// Function to determine the durable size on disk.\n    /// `Some` if `durability` is `Some`, `None` otherwise.\n    disk_size_fn: Option<DiskSizeFn>,\n\n    /// A map from workload types to their cached prometheus counters.\n    workload_type_to_exec_counters: Arc<EnumMap<WorkloadType, ExecutionCounters>>,\n\n    /// An async queue for recording transaction metrics off the main thread\n    metrics_recorder_queue: Option<MetricsRecorderQueue>,\n}\n\n/// Perform a snapshot every `SNAPSHOT_FREQUENCY` transactions.\n// TODO(config): Allow DBs to specify how frequently to snapshot.\n// TODO(bikeshedding): Snapshot based on number of bytes written to commitlog, not tx offsets.\n//\n// NOTE: Replicas must agree on the snapshot frequency. By making them consult\n// this value, later introduction of dynamic configuration will allow the\n// compiler to find external dependencies.\npub const SNAPSHOT_FREQUENCY: u64 = 1_000_000;\n\nimpl std::fmt::Debug for RelationalDB {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RelationalDB\")\n            .field(\"identity\", &self.database_identity)\n            .finish()\n    }\n}\n\nimpl Drop for RelationalDB {\n    fn drop(&mut self) {\n        // Attempt to flush the outstanding transactions.\n        if let Some(worker) = self.durability.take() {\n            worker.spawn_close(self.database_identity);\n        }\n    }\n}\n\nimpl RelationalDB {\n    fn new(\n        database_identity: Identity,\n        owner_identity: Identity,\n        inner: Locking,\n        persistence: Option<Persistence>,\n        metrics_recorder_queue: Option<MetricsRecorderQueue>,\n    ) -> Self {\n        let workload_type_to_exec_counters =\n            Arc::new(EnumMap::from_fn(|ty| ExecutionCounters::new(&ty, &database_identity)));\n\n        let (durability, disk_size_fn, snapshot_worker, rt) = Persistence::unzip(persistence);\n        let durability = durability\n            .zip(rt)\n            .map(|(durability, rt)| DurabilityWorker::new(database_identity, durability, rt));\n\n        Self {\n            inner,\n            durability,\n            snapshot_worker,\n\n            database_identity,\n            owner_identity,\n\n            row_count_fn: default_row_count_fn(database_identity),\n            disk_size_fn,\n\n            workload_type_to_exec_counters,\n            metrics_recorder_queue,\n        }\n    }\n\n    /// Open a database, which may or may not already exist.\n    ///\n    /// # Initialization\n    ///\n    /// When this method returns, the internal state of the database has been\n    /// initialized with nothing written to disk (regardless of the `durability`\n    /// setting).\n    ///\n    /// This allows to hand over a pointer to the database to a [`ModuleHost`][ModuleHost]\n    /// for initialization, which will call [`Self::set_initialized`],\n    /// initializing the database's [`Metadata`] transactionally.\n    ///\n    /// If, however, a non-empty `history` was supplied, [`Metadata`] will\n    /// already be be set. In this case, i.e. if either [`Self::metadata`] or\n    /// [`Self::program_bytes`] return a `Some` value, [`Self::set_initialized`]\n    /// should _not_ be called.\n    ///\n    /// Sometimes, one may want to obtain a database without a module (e.g. for\n    /// testing). In this case, **always** call [`Self::set_initialized`],\n    /// supplying a zero `program_hash` and empty `program_bytes`.\n    ///\n    /// # Parameters\n    ///\n    /// - `root`\n    ///\n    ///   The database directory. Does not need to exist.\n    ///\n    ///   Note that, even if no `durability` is supplied, the directory will be\n    ///   created and equipped with an advisory lock file.\n    ///\n    /// - `database_identity`\n    ///\n    ///   The [`Identity`] of the database.\n    ///\n    ///   An error is returned if the database already exists, but has a\n    ///   different identity.\n    ///   If it is a new database, the identity is stored in the database's\n    ///   system tables upon calling [`Self::set_initialized`].\n    ///\n    /// - `owner_identity`\n    ///\n    ///   The [`Identity`] of the database's owner.\n    ///\n    ///   An error is returned if the database already exists, but has a\n    ///   different owner.\n    ///   If it is a new database, the identity is stored in the database's\n    ///   system tables upon calling [`Self::set_initialized`].\n    ///\n    /// - `history`\n    ///\n    ///   The [`durability::History`] to restore the database from.\n    ///\n    ///   If using local durability, this must be a pointer to the same object.\n    ///   [`durability::EmptyHistory`] can be used to start from an empty history.\n    ///\n    /// - `durability`\n    ///\n    ///   The [`Durability`] implementation to use, along with a [`DiskSizeFn`]\n    ///   reporting its size on disk. The [`DiskSizeFn`] must report zero if\n    ///   this database is a follower instance.\n    ///\n    ///   `None` may be passed to obtain an in-memory only database.\n    ///\n    /// - `snapshot_repo`\n    ///\n    ///   The [`SnapshotRepository`] which stores snapshots of this database.\n    ///   This is only meaningful if `history` and `durability` are also supplied.\n    ///   If restoring from an existing database, the `snapshot_repo` must\n    ///   store views of the same sequence of TXes as the `history`.\n    ///\n    /// - `metrics_recorder_queue`\n    ///\n    ///   The send side of a queue for recording transaction metrics.\n    ///\n    /// # Return values\n    ///\n    /// Alongside `Self`, [`ConnectedClients`] is returned, which is the set of\n    /// clients considered connected at the given snapshot and `history`.\n    ///\n    /// If [`ConnectedClients`] is non-empty, the database did not shut down\n    /// gracefully. The caller is responsible for disconnecting the clients.\n    ///\n    /// [ModuleHost]: crate::host::module_host::ModuleHost\n    #[allow(clippy::too_many_arguments)]\n    pub fn open(\n        database_identity: Identity,\n        owner_identity: Identity,\n        history: impl durability::History<TxData = Txdata>,\n        mut persistence: Option<Persistence>,\n        metrics_recorder_queue: Option<MetricsRecorderQueue>,\n        page_pool: PagePool,\n    ) -> Result<(Self, ConnectedClients), DBError> {\n        log::trace!(\"[{database_identity}] DATABASE: OPEN\");\n\n        // Check the latest durable TX and restore from a snapshot no newer than it,\n        // so that you drop TXes which were committed but not durable before the restart.\n        // TODO: delete or mark as invalid snapshots newer than this.\n        let durable_tx_offset = persistence\n            .as_ref()\n            .map(|p| p.durable_tx_offset())\n            .transpose()?\n            .flatten();\n        let (min_commitlog_offset, _) = history.tx_range_hint();\n\n        log::info!(\"[{database_identity}] DATABASE: durable_tx_offset is {durable_tx_offset:?}\");\n\n        let start_time = std::time::Instant::now();\n\n        let inner = Self::restore_from_snapshot_or_bootstrap(\n            database_identity,\n            persistence.as_ref().and_then(|p| p.snapshot_repo()),\n            durable_tx_offset,\n            min_commitlog_offset,\n            page_pool,\n        )?;\n        if let Some(persistence) = &mut persistence {\n            // Sanity check because the snapshot worker could've been used before.\n            debug_assert!(\n                persistence\n                    .snapshot_repo()\n                    .map(|repo| repo.database_identity() == database_identity)\n                    .unwrap_or(true),\n                \"snapshot repository does not match database identity\",\n            );\n            persistence.set_snapshot_state(inner.committed_state.clone());\n        }\n\n        apply_history(&inner, database_identity, history)?;\n\n        let elapsed_time = start_time.elapsed();\n        WORKER_METRICS\n            .replay_total_time_seconds\n            .with_label_values(&database_identity)\n            .set(elapsed_time.as_secs_f64());\n\n        let db = Self::new(\n            database_identity,\n            owner_identity,\n            inner,\n            persistence,\n            metrics_recorder_queue,\n        );\n        db.migrate_system_tables()?;\n\n        if let Some(meta) = db.metadata()? {\n            if meta.database_identity != database_identity {\n                return Err(anyhow!(\n                    \"mismatched database identity: {} != {}\",\n                    meta.database_identity,\n                    database_identity\n                )\n                .into());\n            }\n            if meta.owner_identity != owner_identity {\n                return Err(anyhow!(\n                    \"mismatched owner identity: {} != {}\",\n                    meta.owner_identity,\n                    owner_identity\n                )\n                .into());\n            }\n        };\n        let connected_clients = db.connected_clients()?;\n\n        Ok((db, connected_clients))\n    }\n\n    /// Shut down the database, without dropping it.\n    ///\n    /// If the database is in-memory only, this does nothing.\n    /// Otherwise, it instructs the durability layer to shut down\n    /// and waits until all outstanding transactions are reported as durable.\n    ///\n    /// After calling this method, calling [Self::commit_tx_downgrade] or\n    /// [Self::commit_tx] will panic.\n    ///\n    /// Returns `None` if the database is in-memory only,\n    /// or nothing has been durably persisted yet.\n    ///\n    /// Returns the durable [TxOffset] in a `Some` otherwise.\n    pub async fn shutdown(&self) -> Option<TxOffset> {\n        if let Some(durability) = &self.durability {\n            return durability.close().await;\n        }\n\n        None\n    }\n\n    /// Create any system tables that are missing from the datastore.\n    ///\n    /// This runs during database startup, *after* `restore_from_snapshot` and `apply_history`\n    /// have reconstructed the committed state from persisted data. Older snapshots may predate\n    /// the introduction of newer system tables (e.g. `st_event_table`, `st_view`), so those\n    /// tables won't exist in the restored state. This method fills them in.\n    ///\n    /// Because of this ordering, code that runs during snapshot restoration\n    /// (such as `schema_for_table_raw`) must tolerate missing system tables gracefully\n    /// — they will be created here shortly after.\n    fn migrate_system_tables(&self) -> Result<(), DBError> {\n        let mut tx = self.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);\n        for schema in system_tables() {\n            if !self.table_id_exists_mut(&tx, &schema.table_id) {\n                log::info!(\n                    \"[{}] DATABASE: adding missing system table {}\",\n                    self.database_identity,\n                    schema.table_name\n                );\n                // FIXME: `create_table` of tables with sequences\n                // gives a different initial allocation than is given by `CommittedState::bootstrap_system_tables`.\n                // See comment in that method.\n                // This results in requiring `CommittedState::fixup_delete_duplicate_system_sequence_rows`.\n                // Fix `migrate_system_tables` to create new system sequences\n                // with the same initial allocation as `bootstrap_system_tables`.\n                let _ = self.create_table(&mut tx, schema.clone())?;\n            }\n        }\n        let _ = self.commit_tx(tx)?;\n        self.inner.assert_system_tables_match()?;\n        Ok(())\n    }\n\n    /// Mark the database as initialized with the given module parameters.\n    ///\n    /// Records the database's identity, owner and module parameters in the\n    /// system tables. The transactional context is supplied by the caller.\n    ///\n    /// It is an error to call this method on an already-initialized database.\n    ///\n    /// See [`Self::open`] for further information.\n    pub fn set_initialized(&self, tx: &mut MutTx, program: Program) -> Result<(), DBError> {\n        log::trace!(\n            \"[{}] DATABASE: set initialized owner={} program_hash={}\",\n            self.database_identity,\n            self.owner_identity,\n            program.hash\n        );\n\n        // Probably a bug: the database is already initialized.\n        // Ignore if it would be a no-op.\n        if let Some(meta) = self.inner.metadata_mut_tx(tx)? {\n            if program.hash == meta.program_hash\n                && self.database_identity == meta.database_identity\n                && self.owner_identity == meta.owner_identity\n            {\n                return Ok(());\n            }\n            return Err(anyhow!(\"database {} already initialized\", self.database_identity).into());\n        }\n        let row = StModuleRow {\n            database_identity: self.database_identity.into(),\n            owner_identity: self.owner_identity.into(),\n\n            program_kind: program.kind,\n            program_hash: program.hash,\n            program_bytes: program.bytes,\n            module_version: ONLY_MODULE_VERSION.into(),\n        };\n        Ok(tx.insert_via_serialize_bsatn(ST_MODULE_ID, &row).map(drop)?)\n    }\n\n    /// Obtain the [`Metadata`] of this database.\n    ///\n    /// `None` if the database is not yet fully initialized.\n    pub fn metadata(&self) -> Result<Option<Metadata>, DBError> {\n        Ok(self.with_read_only(Workload::Internal, |tx| self.inner.metadata(tx))?)\n    }\n\n    /// Obtain the module associated with this database.\n    ///\n    /// `None` if the database is not yet fully initialized.\n    /// Note that a `Some` result may yield an empty slice.\n    pub fn program(&self) -> Result<Option<Program>, DBError> {\n        Ok(self.with_read_only(Workload::Internal, |tx| self.inner.program(tx))?)\n    }\n\n    /// Read the set of clients currently connected to the database.\n    pub fn connected_clients(&self) -> Result<ConnectedClients, DBError> {\n        self.with_read_only(Workload::Internal, |tx| {\n            self.inner\n                .connected_clients(tx)?\n                .collect::<Result<ConnectedClients, _>>()\n        })\n        .map_err(DBError::from)\n    }\n\n    /// Update the module associated with this database.\n    ///\n    /// The caller must ensure that:\n    ///\n    /// - `program.hash` is the [`Hash`] over `program.bytes`.\n    /// - `program.bytes` is a valid module acc. to `program.host_type`.\n    /// - the schema updates contained in the module have been applied within\n    ///   the transactional context `tx`.\n    /// - the `__init__` reducer contained in the module has been executed\n    ///   within the transactional context `tx`.\n    pub fn update_program(&self, tx: &mut MutTx, program: Program) -> Result<(), DBError> {\n        Ok(self.inner.update_program(tx, program)?)\n    }\n\n    fn restore_from_snapshot_or_bootstrap(\n        database_identity: Identity,\n        snapshot_repo: Option<&SnapshotRepository>,\n        durable_tx_offset: Option<TxOffset>,\n        min_commitlog_offset: TxOffset,\n        page_pool: PagePool,\n    ) -> Result<Locking, RestoreSnapshotError> {\n        // Try to load the `ReconstructedSnapshot` at `snapshot_offset`.\n        fn try_load_snapshot(\n            database_identity: &Identity,\n            snapshot_repo: &SnapshotRepository,\n            snapshot_offset: TxOffset,\n            page_pool: &PagePool,\n        ) -> Result<ReconstructedSnapshot, Box<SnapshotError>> {\n            log::info!(\"[{database_identity}] DATABASE: restoring snapshot of tx_offset {snapshot_offset}\");\n            let start = std::time::Instant::now();\n\n            let snapshot = snapshot_repo\n                .read_snapshot(snapshot_offset, page_pool)\n                .map_err(Box::new)?;\n\n            let elapsed_time = start.elapsed();\n\n            WORKER_METRICS\n                .replay_snapshot_read_time_seconds\n                .with_label_values(database_identity)\n                .set(elapsed_time.as_secs_f64());\n\n            log::info!(\n                \"[{database_identity}] DATABASE: read snapshot of tx_offset {snapshot_offset} in {elapsed_time:?}\",\n            );\n\n            Ok(snapshot)\n        }\n\n        // Do restore a `Locking` from the `ReconstructedSnapshot`.\n        fn restore_from_snapshot(\n            database_identity: &Identity,\n            snapshot: ReconstructedSnapshot,\n            page_pool: PagePool,\n        ) -> Result<Locking, Box<DBError>> {\n            let start = std::time::Instant::now();\n            let snapshot_offset = snapshot.tx_offset;\n            Locking::restore_from_snapshot(snapshot, page_pool)\n                .inspect(|_| {\n                    let elapsed_time = start.elapsed();\n\n                    WORKER_METRICS.replay_snapshot_restore_time_seconds.with_label_values(database_identity).set(elapsed_time.as_secs_f64());\n\n                    log::info!(\n                        \"[{database_identity}] DATABASE: restored from snapshot of tx_offset {snapshot_offset} in {elapsed_time:?}\",\n                    )\n                })\n                .inspect_err(|e| {\n                    log::warn!(\n                        \"[{database_identity}] DATABASE: failed to restore snapshot of tx_offset {snapshot_offset}: {e}\"\n                    )\n                })\n                .map_err(DBError::from)\n                .map_err(Box::new)\n        }\n\n        // `true` if the `SnapshotError` can be considered transient.\n        // It is not transient if it has to do with hash verification,\n        // deserialization or the snapshot format itself.\n        fn is_transient_error(e: &SnapshotError) -> bool {\n            match e {\n                SnapshotError::Open(_)\n                | SnapshotError::WriteObject { .. }\n                | SnapshotError::ReadObject { .. }\n                | SnapshotError::Serialize { .. }\n                | SnapshotError::Incomplete { .. }\n                | SnapshotError::NotDirectory { .. }\n                | SnapshotError::Lockfile(_)\n                | SnapshotError::Io(_) => true,\n\n                SnapshotError::HashMismatch { .. }\n                | SnapshotError::Deserialize { .. }\n                | SnapshotError::BadMagic { .. }\n                | SnapshotError::BadVersion { .. } => false,\n            }\n        }\n\n        if let Some((snapshot_repo, durable_tx_offset)) = snapshot_repo.zip(durable_tx_offset) {\n            // Mark any newer snapshots as invalid, as the history past\n            // `durable_tx_offset` may have been reset and thus diverge from\n            // any snapshots taken earlier.\n            snapshot_repo\n                .invalidate_newer_snapshots(durable_tx_offset)\n                .map_err(|e| RestoreSnapshotError::Invalidate {\n                    offset: durable_tx_offset,\n                    source: Box::new(e),\n                })?;\n\n            // Try to restore from any snapshot that was taken within the\n            // range `(min_commitlog_offset + 1)..=durable_tx_offset`.\n            let mut upper_bound = durable_tx_offset;\n            loop {\n                let Some(snapshot_offset) = snapshot_repo\n                    .latest_snapshot_older_than(upper_bound)\n                    .map_err(Box::new)?\n                else {\n                    break;\n                };\n                if min_commitlog_offset > 0 && min_commitlog_offset > snapshot_offset + 1 {\n                    log::debug!(\"snapshot_offset={snapshot_offset} min_commitlog_offset={min_commitlog_offset}\");\n                    break;\n                }\n                match try_load_snapshot(&database_identity, snapshot_repo, snapshot_offset, &page_pool) {\n                    Ok(snapshot) if snapshot.database_identity != database_identity => {\n                        return Err(RestoreSnapshotError::IdentityMismatch {\n                            expected: database_identity,\n                            actual: snapshot.database_identity,\n                        });\n                    }\n                    Ok(snapshot) => {\n                        return restore_from_snapshot(&database_identity, snapshot, page_pool)\n                            .map_err(RestoreSnapshotError::Datastore);\n                    }\n                    Err(e) => {\n                        // Invalidate the snapshot if the error is permanent.\n                        // Newly created snapshots should not depend on it.\n                        if !is_transient_error(&e) {\n                            let path = snapshot_repo.snapshot_dir_path(snapshot_offset);\n                            log::info!(\"invalidating bad snapshot at {}\", path.display());\n                            path.rename_invalid().map_err(|e| RestoreSnapshotError::Invalidate {\n                                offset: snapshot_offset,\n                                source: Box::new(e.into()),\n                            })?;\n                        }\n                        // Try the next older one if the error was transient.\n                        //\n                        // `latest_snapshot_older_than` is inclusive of the\n                        // upper bound, so subtract one and give up if there\n                        // are no more offsets to try.\n                        match snapshot_offset.checked_sub(1) {\n                            None => break,\n                            Some(older_than) => upper_bound = older_than,\n                        }\n                    }\n                }\n            }\n        }\n        log::info!(\"[{database_identity}] DATABASE: no usable snapshot on disk\");\n\n        // If we didn't find a snapshot and the commitlog doesn't start at the\n        // zero-th commit (e.g. due to archiving), there is no way to restore\n        // the database.\n        if min_commitlog_offset > 0 {\n            return Err(RestoreSnapshotError::NoConnectedSnapshot { min_commitlog_offset });\n        }\n\n        Locking::bootstrap(database_identity, page_pool)\n            .map_err(DBError::from)\n            .map_err(Box::new)\n            .map_err(RestoreSnapshotError::Bootstrap)\n    }\n\n    /// Apply the provided [`spacetimedb_durability::History`] onto the database\n    /// state.\n    ///\n    /// Consumes `self` in order to ensure exclusive access, and to prevent use\n    /// of the database in case of an incomplete replay.\n    /// This restriction may be lifted in the future to allow for \"live\" followers.\n    pub fn apply<T>(self, history: T) -> Result<Self, DBError>\n    where\n        T: durability::History<TxData = Txdata>,\n    {\n        apply_history(&self.inner, self.database_identity, history)?;\n        Ok(self)\n    }\n\n    /// Returns an approximate row count for a particular table.\n    /// TODO: Unify this with `Relation::row_count` when more statistics are added.\n    pub fn row_count(&self, table_id: TableId, table_name: &str) -> i64 {\n        (self.row_count_fn)(table_id, table_name)\n    }\n\n    /// Update this `RelationalDB` with an approximate row count function.\n    pub fn with_row_count(mut self, row_count: RowCountFn) -> Self {\n        self.row_count_fn = row_count;\n        self\n    }\n\n    /// Returns the identity for this database\n    pub fn database_identity(&self) -> Identity {\n        self.database_identity\n    }\n\n    pub fn owner_identity(&self) -> Identity {\n        self.owner_identity\n    }\n\n    /// The number of bytes on disk occupied by the durability layer.\n    ///\n    /// If this is an in-memory instance, `Ok(0)` is returned.\n    pub fn size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        self.disk_size_fn.as_ref().map_or(Ok(<_>::default()), |f| f())\n    }\n\n    /// The size in bytes of all of the in-memory data in this database.\n    pub fn size_in_memory(&self) -> usize {\n        self.inner.heap_usage()\n    }\n\n    /// Update data size metrics.\n    pub fn update_data_size_metrics(&self) {\n        let cs = self.inner.committed_state.read();\n\n        cs.report_data_size(self.database_identity)\n    }\n\n    pub fn encode_row(row: &ProductValue, bytes: &mut Vec<u8>) {\n        // TODO: large file storage of the row elements\n        row.encode(bytes);\n    }\n\n    pub fn schema_for_table_mut(&self, tx: &MutTx, table_id: TableId) -> Result<Arc<TableSchema>, DBError> {\n        Ok(self.inner.schema_for_table_mut_tx(tx, table_id)?)\n    }\n\n    pub fn schema_for_table(&self, tx: &Tx, table_id: TableId) -> Result<Arc<TableSchema>, DBError> {\n        Ok(self.inner.schema_for_table_tx(tx, table_id)?)\n    }\n\n    pub fn row_schema_for_table<'tx>(\n        &self,\n        tx: &'tx MutTx,\n        table_id: TableId,\n    ) -> Result<RowTypeForTable<'tx>, DBError> {\n        Ok(self.inner.row_type_for_table_mut_tx(tx, table_id)?)\n    }\n\n    pub fn get_all_tables_mut(&self, tx: &MutTx) -> Result<Vec<Arc<TableSchema>>, DBError> {\n        Ok(self.inner.get_all_tables_mut_tx(tx)?)\n    }\n\n    pub fn get_all_tables(&self, tx: &Tx) -> Result<Vec<Arc<TableSchema>>, DBError> {\n        Ok(self.inner.get_all_tables_tx(tx)?)\n    }\n\n    pub fn table_scheduled_id_and_at(\n        &self,\n        tx: &impl StateView,\n        table_id: TableId,\n    ) -> Result<Option<(ColId, ColId)>, DBError> {\n        let schema = tx.schema_for_table(table_id)?;\n        let Some(sched) = &schema.schedule else { return Ok(None) };\n        let primary_key = schema\n            .primary_key\n            .context(\"scheduled table doesn't have a primary key?\")?;\n        Ok(Some((primary_key, sched.at_column)))\n    }\n\n    pub fn decode_column(\n        &self,\n        tx: &MutTx,\n        table_id: TableId,\n        col_id: ColId,\n        bytes: &[u8],\n    ) -> Result<AlgebraicValue, DBError> {\n        // We need to do a manual bounds check here\n        // since we want to do `swap_remove` to get an owned value\n        // in the case of `Cow::Owned` and avoid a `clone`.\n        let check_bounds = |schema: &ProductType| -> Result<_, DBError> {\n            let col_idx = col_id.idx();\n            if col_idx >= schema.elements.len() {\n                return Err(DatastoreError::Table(TableError::ColumnNotFound(col_id)).into());\n            }\n            Ok(col_idx)\n        };\n        let row_ty = &*self.row_schema_for_table(tx, table_id)?;\n        let col_idx = check_bounds(row_ty)?;\n        let col_ty = &row_ty.elements[col_idx].algebraic_type;\n        Ok(AlgebraicValue::decode(col_ty, &mut &*bytes)?)\n    }\n\n    /// Returns the execution counters for this database.\n    pub fn exec_counter_map(&self) -> Arc<EnumMap<WorkloadType, ExecutionCounters>> {\n        self.workload_type_to_exec_counters.clone()\n    }\n\n    /// Returns the execution counters for `workload_type` for this database.\n    pub fn exec_counters_for(&self, workload_type: WorkloadType) -> &ExecutionCounters {\n        &self.workload_type_to_exec_counters[workload_type]\n    }\n\n    /// Begin a transaction.\n    ///\n    /// **Note**: this call **must** be paired with [`Self::rollback_mut_tx`] or\n    /// [`Self::commit_tx`], otherwise the database will be left in an invalid\n    /// state. See also [`Self::with_auto_commit`].\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn begin_mut_tx(&self, isolation_level: IsolationLevel, workload: Workload) -> MutTx {\n        log::trace!(\"BEGIN MUT TX\");\n        let r = self.inner.begin_mut_tx(isolation_level, workload);\n        log::trace!(\"ACQUIRED MUT TX\");\n        r\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn begin_tx(&self, workload: Workload) -> Tx {\n        log::trace!(\"BEGIN TX\");\n        let r = self.inner.begin_tx(workload);\n        log::trace!(\"ACQUIRED TX\");\n        r\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn rollback_mut_tx(&self, tx: MutTx) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        log::trace!(\"ROLLBACK MUT TX\");\n        self.inner.rollback_mut_tx(tx)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn rollback_mut_tx_downgrade(&self, tx: MutTx, workload: Workload) -> (TxMetrics, Tx) {\n        log::trace!(\"ROLLBACK MUT TX\");\n        self.inner.rollback_mut_tx_downgrade(tx, workload)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn release_tx(&self, tx: Tx) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        log::trace!(\"RELEASE TX\");\n        self.inner.release_tx(tx)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    #[allow(clippy::type_complexity)]\n    pub fn commit_tx(\n        &self,\n        tx: MutTx,\n    ) -> Result<Option<(TxOffset, Arc<TxData>, TxMetrics, Option<ReducerName>)>, DBError> {\n        log::trace!(\"COMMIT MUT TX\");\n\n        let reducer_context = tx.ctx.reducer_context().cloned();\n        // TODO: Never returns `None` -- should it?\n        let Some((tx_offset, tx_data, tx_metrics, reducer)) = self.inner.commit_mut_tx(tx)? else {\n            return Ok(None);\n        };\n\n        self.maybe_do_snapshot(&tx_data);\n\n        let tx_data = Arc::new(tx_data);\n        if let Some(durability) = &self.durability {\n            durability.request_durability(reducer_context, &tx_data);\n        }\n\n        Ok(Some((tx_offset, tx_data, tx_metrics, reducer)))\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn commit_tx_downgrade(&self, tx: MutTx, workload: Workload) -> (Arc<TxData>, TxMetrics, Tx) {\n        log::trace!(\"COMMIT MUT TX\");\n\n        let (tx_data, tx_metrics, tx) = self.inner.commit_mut_tx_downgrade(tx, workload);\n\n        self.maybe_do_snapshot(&tx_data);\n\n        let tx_data = Arc::new(tx_data);\n        if let Some(durability) = &self.durability {\n            durability.request_durability(tx.ctx.reducer_context().cloned(), &tx_data);\n        }\n\n        (tx_data, tx_metrics, tx)\n    }\n\n    /// Get the [`DurableOffset`] of this database, or `None` if this is an\n    /// in-memory instance.\n    pub fn durable_tx_offset(&self) -> Option<DurableOffset> {\n        self.durability\n            .as_ref()\n            .map(|durability| durability.durable_tx_offset())\n    }\n\n    /// Decide based on the `committed_state.next_tx_offset`\n    /// whether to request that the [`SnapshotWorker`] in `self` capture a snapshot of the database.\n    ///\n    /// Actual snapshotting happens asynchronously in a Tokio worker.\n    ///\n    /// Snapshotting must happen independent of the durable TX offset known by the [`Durability`]\n    /// because capturing a snapshot requires access to the committed state,\n    /// which in the general case may advance beyond the durable TX offset,\n    /// as our durability is an asynchronous write-behind log.\n    /// An alternate implementation might keep a second materialized [`CommittedState`]\n    /// which followed the durable TX offset rather than the committed-not-yet-durable state,\n    /// in which case we would be able to snapshot only TXes known to be durable.\n    /// In this implementation, we snapshot the existing [`CommittedState`]\n    /// which stores the committed-not-yet-durable state.\n    /// This requires a small amount of additional logic when restoring from a snapshot\n    /// to ensure we don't restore a snapshot more recent than the durable TX offset.\n    fn maybe_do_snapshot(&self, tx_data: &TxData) {\n        if let Some(snapshot_worker) = &self.snapshot_worker\n            && let Some(tx_offset) = tx_data.tx_offset()\n            && tx_offset % SNAPSHOT_FREQUENCY == 0\n        {\n            snapshot_worker.request_snapshot();\n        }\n    }\n\n    /// Subscribe to a channel of snapshot offsets.\n    ///\n    /// If a `snapshot_repo` was provided when this database was opened, this method\n    /// returns a `watch::Receiver` that updates with the latest [`TxOffset`] a snapshot\n    /// was taken at.\n    pub fn subscribe_to_snapshots(&self) -> Option<watch::Receiver<TxOffset>> {\n        self.snapshot_worker.as_ref().map(|snap| snap.subscribe())\n    }\n\n    /// Run a fallible function in a transaction.\n    ///\n    /// If the supplied function returns `Ok`, the transaction is automatically\n    /// committed. Otherwise, the transaction is rolled back.\n    ///\n    /// This method is provided for convenience, as it allows to safely use the\n    /// `?` operator in code running within a transaction context. Recall that a\n    /// [`MutTx`] does not follow the RAII pattern, so the following code is\n    /// wrong:\n    ///\n    /// ```ignore\n    /// let tx = db.begin_mut_tx(IsolationLevel::Serializable);\n    /// let _ = db.schema_for_table(tx, 42)?;\n    /// // ...\n    /// let _ = db.commit_tx(tx)?;\n    /// ```\n    ///\n    /// If `schema_for_table` returns an error, the transaction is not properly\n    /// cleaned up, as the `?` short-circuits. To avoid this, but still be able\n    /// to use `?`, you can write:\n    ///\n    /// ```ignore\n    /// db.with_auto_commit(|tx| {\n    ///     let _ = db.schema_for_table(tx, 42)?;\n    ///     // ...\n    ///     Ok(())\n    /// })?;\n    /// ```\n    pub fn with_auto_commit<F, A, E>(&self, workload: Workload, f: F) -> Result<A, E>\n    where\n        F: FnOnce(&mut MutTx) -> Result<A, E>,\n        E: From<DBError>,\n    {\n        let mut tx = self.begin_mut_tx(IsolationLevel::Serializable, workload);\n        let res = f(&mut tx);\n        self.finish_tx(tx, res)\n    }\n\n    /// Run a fallible function in a transaction, rolling it back if the\n    /// function returns `Err`.\n    ///\n    /// Similar in purpose to [`Self::with_auto_commit`], but returns the\n    /// [`MutTx`] alongside the `Ok` result of the function `F` without\n    /// committing the transaction.\n    pub fn with_auto_rollback<F, A, E>(&self, mut tx: MutTx, f: F) -> Result<(MutTx, A), E>\n    where\n        F: FnOnce(&mut MutTx) -> Result<A, E>,\n    {\n        let res = f(&mut tx);\n        self.rollback_on_err(tx, res)\n    }\n\n    /// Run a fallible function in a transaction.\n    ///\n    /// This is similar to `with_auto_commit`, but regardless of the return value of\n    /// the fallible function, the transaction will ALWAYS be rolled back. This can be used to\n    /// emulate a read-only transaction.\n    ///\n    /// TODO(jgilles): when we support actual read-only transactions, use those here instead.\n    /// TODO(jgilles, kim): get this merged with the above function (two people had similar ideas\n    /// at the same time)\n    pub fn with_read_only<F, T>(&self, workload: Workload, f: F) -> T\n    where\n        F: FnOnce(&mut Tx) -> T,\n    {\n        let mut tx = self.begin_tx(workload);\n        let res = f(&mut tx);\n        let (_tx_offset, tx_metrics, reducer) = self.release_tx(tx);\n        self.report_read_tx_metrics(reducer, tx_metrics);\n        res\n    }\n\n    /// Perform the transactional logic for the `tx` according to the `res`\n    pub fn finish_tx<A, E>(&self, tx: MutTx, res: Result<A, E>) -> Result<A, E>\n    where\n        E: From<DBError>,\n    {\n        if res.is_err() {\n            let (_, tx_metrics, reducer) = self.rollback_mut_tx(tx);\n            self.report_mut_tx_metrics(reducer, tx_metrics, None);\n        } else {\n            match self.commit_tx(tx).map_err(E::from)? {\n                Some((_tx_offset, tx_data, tx_metrics, reducer)) => {\n                    self.report_mut_tx_metrics(reducer, tx_metrics, Some(tx_data));\n                }\n                None => panic!(\"TODO: retry?\"),\n            }\n        }\n\n        res\n    }\n\n    /// Roll back transaction `tx` if `res` is `Err`, otherwise return it\n    /// alongside the `Ok` value.\n    pub fn rollback_on_err<A, E>(&self, tx: MutTx, res: Result<A, E>) -> Result<(MutTx, A), E> {\n        match res {\n            Err(e) => {\n                let (_, tx_metrics, reducer) = self.rollback_mut_tx(tx);\n                self.report_mut_tx_metrics(reducer, tx_metrics, None);\n\n                Err(e)\n            }\n            Ok(a) => Ok((tx, a)),\n        }\n    }\n\n    pub(crate) fn alter_table_access(&self, tx: &mut MutTx, name: &str, access: StAccess) -> Result<(), DBError> {\n        Ok(self.inner.alter_table_access_mut_tx(tx, name, access)?)\n    }\n\n    pub(crate) fn alter_table_row_type(\n        &self,\n        tx: &mut MutTx,\n        table_id: TableId,\n        column_schemas: Vec<ColumnSchema>,\n    ) -> Result<(), DBError> {\n        Ok(self.inner.alter_table_row_type_mut_tx(tx, table_id, column_schemas)?)\n    }\n\n    pub(crate) fn add_columns_to_table(\n        &self,\n        tx: &mut MutTx,\n        table_id: TableId,\n        column_schemas: Vec<ColumnSchema>,\n        default_values: Vec<AlgebraicValue>,\n    ) -> Result<TableId, DBError> {\n        Ok(self\n            .inner\n            .add_columns_to_table_mut_tx(tx, table_id, column_schemas, default_values)?)\n    }\n\n    /// Reports the `TxMetrics`s passed.\n    ///\n    /// Should only be called after the tx lock has been fully released.\n    pub(crate) fn report_tx_metrics(\n        &self,\n        reducer: Option<ReducerName>,\n        tx_data: Option<Arc<TxData>>,\n        metrics_for_writer: Option<TxMetrics>,\n        metrics_for_reader: Option<TxMetrics>,\n    ) {\n        if let Some(recorder) = &self.metrics_recorder_queue {\n            recorder.send_metrics(\n                reducer,\n                metrics_for_writer,\n                metrics_for_reader,\n                tx_data,\n                self.exec_counter_map(),\n            );\n        }\n    }\n}\n\n/// Duration after which expired unused views are cleaned up.\n/// Value is chosen arbitrarily; can be tuned later if needed.\nconst VIEWS_EXPIRATION: std::time::Duration = std::time::Duration::from_secs(10 * 60);\n\n/// Duration to budget for each view cleanup job,\n/// so that it doesn't hold transaction lock for to long.\n//TODO: Make this value configurable\nconst VIEW_CLEANUP_BUDGET: std::time::Duration = std::time::Duration::from_millis(10);\n\n/// Spawn a background task that periodically cleans up expired views\npub fn spawn_view_cleanup_loop(db: Arc<RelationalDB>) -> tokio::task::AbortHandle {\n    fn run_view_cleanup(db: &RelationalDB) {\n        match db.with_auto_commit(Workload::Internal, |tx| {\n            tx.clear_expired_views(VIEWS_EXPIRATION, VIEW_CLEANUP_BUDGET)\n                .map_err(DBError::from)\n        }) {\n            Ok((cleared, total_expired)) => {\n                if cleared != total_expired {\n                    // TODO: metrics\n                    log::info!(\n                        \"[{}] DATABASE: cleared {} expired views ({} remaining)\",\n                        db.database_identity(),\n                        cleared,\n                        total_expired - cleared\n                    );\n                }\n            }\n            Err(e) => {\n                log::error!(\n                    \"[{}] DATABASE: failed to clear expired views: {}\",\n                    db.database_identity(),\n                    e\n                );\n            }\n        }\n    }\n\n    tokio::spawn(async move {\n        loop {\n            // Offload actual cleanup to blocking thread pool, as `VIEW_CLEANUP_BUDGET` is defined\n            // in milliseconds, which may be too long for async tasks.\n            let db = db.clone();\n            let db_identity = db.database_identity();\n            info!(\"running view cleanup for database {db_identity}\");\n            tokio::task::spawn_blocking(move || run_view_cleanup(&db))\n                .await\n                .inspect_err(|e| {\n                    log::error!(\"[{}] DATABASE: failed to run view cleanup task: {}\", db_identity, e);\n                })\n                .ok();\n\n            info!(\"pausing view cleanup for database {db_identity}\");\n            tokio::time::sleep(VIEWS_EXPIRATION).await;\n        }\n    })\n    .abort_handle()\n}\nimpl RelationalDB {\n    pub fn create_table(&self, tx: &mut MutTx, schema: TableSchema) -> Result<TableId, DBError> {\n        Ok(self.inner.create_table_mut_tx(tx, schema)?)\n    }\n\n    pub fn drop_table(&self, tx: &mut MutTx, table_id: TableId) -> Result<(), DBError> {\n        let table_name = self\n            .table_name_from_id_mut(tx, table_id)?\n            .map(|name| name.to_string())\n            .unwrap_or_default();\n        Ok(self.inner.drop_table_mut_tx(tx, table_id).map(|_| {\n            DB_METRICS\n                .rdb_num_table_rows\n                .with_label_values(&self.database_identity, &table_id.into(), &table_name)\n                .set(0)\n        })?)\n    }\n\n    pub fn create_view(\n        &self,\n        tx: &mut MutTx,\n        module_def: &ModuleDef,\n        view_def: &ViewDef,\n    ) -> Result<(ViewId, TableId), DBError> {\n        Ok(tx.create_view(module_def, view_def)?)\n    }\n\n    pub fn drop_view(&self, tx: &mut MutTx, view_id: ViewId) -> Result<(), DBError> {\n        Ok(tx.drop_view(view_id)?)\n    }\n\n    pub fn create_table_for_test_with_the_works(\n        &self,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        indexes: &[ColList],\n        unique_constraints: &[ColList],\n        access: StAccess,\n    ) -> Result<TableId, DBError> {\n        let mut module_def_builder = RawModuleDefV9Builder::new();\n\n        let mut table_builder = module_def_builder\n            .build_table_with_new_type_for_tests(\n                RawIdentifier::new(name),\n                ProductType::from_iter(schema.iter().cloned()),\n                true,\n            )\n            .with_access(access.into());\n\n        for columns in indexes {\n            table_builder = table_builder.with_index(btree(columns.clone()), \"accessor_name_doesnt_matter\");\n        }\n        for columns in unique_constraints {\n            table_builder = table_builder.with_unique_constraint(columns.clone());\n        }\n        table_builder.finish();\n        let module_def: ModuleDef = module_def_builder.finish().try_into()?;\n\n        let table: &TableDef = module_def.table(name).expect(\"table not found\");\n\n        // Recursively sets all IDs to `SENTINEL`.\n        let schema = TableSchema::from_module_def(&module_def, table, (), TableId::SENTINEL);\n\n        //TODO: Change this to `Workload::ForTest` once `#[cfg(bench)]` is stabilized.\n        self.with_auto_commit(Workload::Internal, |tx| self.create_table(tx, schema))\n    }\n\n    pub fn create_table_for_test_with_access(\n        &self,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        indexes: &[ColId],\n        access: StAccess,\n    ) -> Result<TableId, DBError> {\n        let indexes: Vec<ColList> = indexes.iter().map(|col_id| (*col_id).into()).collect();\n        self.create_table_for_test_with_the_works(name, schema, &indexes[..], &[], access)\n    }\n\n    pub fn create_table_for_test(\n        &self,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        indexes: &[ColId],\n    ) -> Result<TableId, DBError> {\n        self.create_table_for_test_with_access(name, schema, indexes, StAccess::Public)\n    }\n\n    pub fn create_table_for_test_multi_column(\n        &self,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        idx_cols: ColList,\n    ) -> Result<TableId, DBError> {\n        self.create_table_for_test_with_the_works(name, schema, &[idx_cols], &[], StAccess::Public)\n    }\n\n    pub fn create_table_for_test_mix_indexes(\n        &self,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        idx_cols_single: &[ColId],\n        idx_cols_multi: ColList,\n    ) -> Result<TableId, DBError> {\n        let indexes: Vec<ColList> = idx_cols_single\n            .iter()\n            .map(|col_id| (*col_id).into())\n            .chain(std::iter::once(idx_cols_multi))\n            .collect();\n\n        self.create_table_for_test_with_the_works(name, schema, &indexes[..], &[], StAccess::Public)\n    }\n\n    /// Rename a table.\n    ///\n    /// Sets the name of the table to `new_name` regardless of the previous value. This is a\n    /// relatively cheap operation which only modifies the system tables.\n    ///\n    /// If the table is not found or is a system table, an error is returned.\n    pub fn rename_table(&self, tx: &mut MutTx, table_id: TableId, new_name: TableName) -> Result<(), DBError> {\n        Ok(self.inner.rename_table_mut_tx(tx, table_id, new_name)?)\n    }\n\n    pub fn view_id_from_name_mut(&self, tx: &MutTx, view_name: &str) -> Result<Option<ViewId>, DBError> {\n        Ok(self.inner.view_id_from_name_mut_tx(tx, view_name)?)\n    }\n\n    pub fn table_id_from_name_mut(&self, tx: &MutTx, table_name: &str) -> Result<Option<TableId>, DBError> {\n        Ok(self.inner.table_id_from_name_mut_tx(tx, table_name)?)\n    }\n\n    pub fn table_id_from_name(&self, tx: &Tx, table_name: &str) -> Result<Option<TableId>, DBError> {\n        Ok(self.inner.table_id_from_name_tx(tx, table_name)?)\n    }\n\n    pub fn table_id_exists(&self, tx: &Tx, table_id: &TableId) -> bool {\n        self.inner.table_id_exists_tx(tx, table_id)\n    }\n\n    pub fn table_id_exists_mut(&self, tx: &MutTx, table_id: &TableId) -> bool {\n        self.inner.table_id_exists_mut_tx(tx, table_id)\n    }\n\n    pub fn table_name_from_id<'a>(&'a self, tx: &'a Tx, table_id: TableId) -> Result<Option<Cow<'a, str>>, DBError> {\n        Ok(self.inner.table_name_from_id_tx(tx, table_id)?)\n    }\n\n    pub fn table_name_from_id_mut<'a>(\n        &'a self,\n        tx: &'a MutTx,\n        table_id: TableId,\n    ) -> Result<Option<Cow<'a, str>>, DBError> {\n        Ok(self.inner.table_name_from_id_mut_tx(tx, table_id)?)\n    }\n\n    pub fn index_id_from_name_mut(&self, tx: &MutTx, index_name: &str) -> Result<Option<IndexId>, DBError> {\n        Ok(self.inner.index_id_from_name_mut_tx(tx, index_name)?)\n    }\n\n    pub fn table_row_count_mut(&self, tx: &MutTx, table_id: TableId) -> Option<u64> {\n        // TODO(Centril): Go via MutTxDatastore trait instead.\n        // Doing this for now to ship this quicker.\n        tx.table_row_count(table_id)\n    }\n\n    /// Returns the constraints on the input `ColList`.\n    /// Note that this is ORDER-SENSITIVE: the order of the columns in the input `ColList` matters.\n    pub fn column_constraints(\n        &self,\n        tx: &mut MutTx,\n        table_id: TableId,\n        cols: &ColList,\n    ) -> Result<Constraints, DBError> {\n        let table = self.inner.schema_for_table_mut_tx(tx, table_id)?;\n\n        let index = table.indexes.iter().find(|i| i.index_algorithm.columns() == *cols);\n        let cols_set = ColSet::from(cols);\n        let unique_constraint = table\n            .constraints\n            .iter()\n            .find(|c| c.data.unique_columns() == Some(&cols_set));\n\n        if index.is_some() {\n            Ok(Constraints::from_is_unique(unique_constraint.is_some()))\n        } else if unique_constraint.is_some() {\n            Ok(Constraints::unique())\n        } else {\n            Ok(Constraints::unset())\n        }\n    }\n\n    pub fn index_id_from_name(&self, tx: &MutTx, index_name: &str) -> Result<Option<IndexId>, DBError> {\n        Ok(self.inner.index_id_from_name_mut_tx(tx, index_name)?)\n    }\n\n    pub fn sequence_id_from_name(&self, tx: &MutTx, sequence_name: &str) -> Result<Option<SequenceId>, DBError> {\n        Ok(self.inner.sequence_id_from_name_mut_tx(tx, sequence_name)?)\n    }\n\n    pub fn constraint_id_from_name(&self, tx: &MutTx, constraint_name: &str) -> Result<Option<ConstraintId>, DBError> {\n        Ok(self.inner.constraint_id_from_name(tx, constraint_name)?)\n    }\n\n    /// Adds the index into the [ST_INDEXES_NAME] table\n    ///\n    /// NOTE: It loads the data from the table into it before returning\n    pub fn create_index(&self, tx: &mut MutTx, schema: IndexSchema, is_unique: bool) -> Result<IndexId, DBError> {\n        Ok(self.inner.create_index_mut_tx(tx, schema, is_unique)?)\n    }\n\n    /// Removes the [`TableIndex`] from the database by their `index_id`\n    pub fn drop_index(&self, tx: &mut MutTx, index_id: IndexId) -> Result<(), DBError> {\n        Ok(self.inner.drop_index_mut_tx(tx, index_id)?)\n    }\n\n    pub fn create_row_level_security(\n        &self,\n        tx: &mut MutTx,\n        row_level_security_schema: RowLevelSecuritySchema,\n    ) -> Result<RawSql, DBError> {\n        Ok(tx.create_row_level_security(row_level_security_schema)?)\n    }\n\n    pub fn drop_row_level_security(&self, tx: &mut MutTx, sql: RawSql) -> Result<(), DBError> {\n        Ok(tx.drop_row_level_security(sql)?)\n    }\n\n    pub fn row_level_security_for_table_id_mut_tx(\n        &self,\n        tx: &mut MutTx,\n        table_id: TableId,\n    ) -> Result<Vec<RowLevelSecuritySchema>, DBError> {\n        Ok(tx.row_level_security_for_table_id(table_id)?)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`.\n    pub fn iter_mut<'a>(&'a self, tx: &'a MutTx, table_id: TableId) -> Result<IterMutTx<'a>, DBError> {\n        Ok(self.inner.iter_mut_tx(tx, table_id)?)\n    }\n\n    pub fn iter<'a>(&'a self, tx: &'a Tx, table_id: TableId) -> Result<TableScanIter<'a>, DBError> {\n        Ok(self.inner.iter_tx(tx, table_id)?)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the column data identified by `cols` matches `value`.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    pub fn iter_by_col_eq_mut<'a, 'r>(\n        &'a self,\n        tx: &'a MutTx,\n        table_id: impl Into<TableId>,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<IterByColEqMutTx<'a, 'r>, DBError> {\n        Ok(self.inner.iter_by_col_eq_mut_tx(tx, table_id.into(), cols, value)?)\n    }\n\n    pub fn iter_by_col_eq<'a, 'r>(\n        &'a self,\n        tx: &'a Tx,\n        table_id: impl Into<TableId>,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<IterByColEqTx<'a, 'r>, DBError> {\n        Ok(self.inner.iter_by_col_eq_tx(tx, table_id.into(), cols, value)?)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the column data identified by `cols` matches what is within `range`.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    pub fn iter_by_col_range_mut<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a MutTx,\n        table_id: impl Into<TableId>,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<IterByColRangeMutTx<'a, R>, DBError> {\n        Ok(self.inner.iter_by_col_range_mut_tx(tx, table_id.into(), cols, range)?)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the column data identified by `cols` matches what is within `range`.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    pub fn iter_by_col_range<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a Tx,\n        table_id: impl Into<TableId>,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<IterByColRangeTx<'a, R>, DBError> {\n        Ok(self.inner.iter_by_col_range_tx(tx, table_id.into(), cols, range)?)\n    }\n\n    pub fn index_scan_range<'a>(\n        &'a self,\n        tx: &'a MutTx,\n        index_id: IndexId,\n        prefix: &[u8],\n        prefix_elems: ColId,\n        rstart: &[u8],\n        rend: &[u8],\n    ) -> Result<\n        (\n            TableId,\n            Bound<AlgebraicValue>,\n            Bound<AlgebraicValue>,\n            impl Iterator<Item = RowRef<'a>> + use<'a>,\n        ),\n        DBError,\n    > {\n        Ok(tx.index_scan_range(index_id, prefix, prefix_elems, rstart, rend)?)\n    }\n\n    pub fn index_scan_point<'a>(\n        &'a self,\n        tx: &'a MutTx,\n        index_id: IndexId,\n        point: &[u8],\n    ) -> Result<(TableId, AlgebraicValue, impl Iterator<Item = RowRef<'a>> + use<'a>), DBError> {\n        Ok(tx.index_scan_point(index_id, point)?)\n    }\n\n    pub fn insert<'a>(\n        &'a self,\n        tx: &'a mut MutTx,\n        table_id: TableId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, InsertFlags), DBError> {\n        Ok(self.inner.insert_mut_tx(tx, table_id, row)?)\n    }\n\n    pub fn update<'a>(\n        &'a self,\n        tx: &'a mut MutTx,\n        table_id: TableId,\n        index_id: IndexId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, UpdateFlags), DBError> {\n        Ok(self.inner.update_mut_tx(tx, table_id, index_id, row)?)\n    }\n\n    pub fn delete(&self, tx: &mut MutTx, table_id: TableId, row_ids: impl IntoIterator<Item = RowPointer>) -> u32 {\n        self.inner.delete_mut_tx(tx, table_id, row_ids)\n    }\n\n    pub fn delete_by_rel<R: IntoIterator<Item = ProductValue>>(\n        &self,\n        tx: &mut MutTx,\n        table_id: TableId,\n        relation: R,\n    ) -> u32 {\n        self.inner.delete_by_rel_mut_tx(tx, table_id, relation)\n    }\n\n    /// Clears all rows from a table without dropping it.\n    pub fn clear_table(&self, tx: &mut MutTx, table_id: TableId) -> Result<usize, DBError> {\n        let rows_deleted = tx.clear_table(table_id)?;\n        Ok(rows_deleted)\n    }\n\n    /// Clear all rows from all view tables without dropping them.\n    pub fn clear_all_views(&self, tx: &mut MutTx) -> Result<(), DBError> {\n        Ok(tx.clear_all_views()?)\n    }\n\n    /// Empties the system tables tracking clients.\n    pub fn clear_all_clients(&self) -> Result<(), DBError> {\n        self.with_auto_commit(Workload::Internal, |mut_tx| {\n            self.clear_all_views(mut_tx)?;\n            self.clear_table(mut_tx, ST_CONNECTION_CREDENTIALS_ID)?;\n            self.clear_table(mut_tx, ST_CLIENT_ID)?;\n            self.clear_table(mut_tx, ST_VIEW_SUB_ID)?;\n            Ok(())\n        })\n    }\n\n    pub fn create_sequence(&self, tx: &mut MutTx, sequence_schema: SequenceSchema) -> Result<SequenceId, DBError> {\n        Ok(self.inner.create_sequence_mut_tx(tx, sequence_schema)?)\n    }\n\n    ///Removes the [Sequence] from database instance\n    pub fn drop_sequence(&self, tx: &mut MutTx, seq_id: SequenceId) -> Result<(), DBError> {\n        Ok(self.inner.drop_sequence_mut_tx(tx, seq_id)?)\n    }\n\n    ///Removes the [Constraints] from database instance\n    pub fn drop_constraint(&self, tx: &mut MutTx, constraint_id: ConstraintId) -> Result<(), DBError> {\n        Ok(self.inner.drop_constraint_mut_tx(tx, constraint_id)?)\n    }\n\n    /// Reports the metrics for `reducer`, using counters provided by `db`.\n    pub fn report_mut_tx_metrics(\n        &self,\n        reducer: Option<ReducerName>,\n        metrics: TxMetrics,\n        tx_data: Option<Arc<TxData>>,\n    ) {\n        self.report_tx_metrics(reducer, tx_data, Some(metrics), None);\n    }\n\n    /// Reports subscription metrics for `reducer`, using counters provided by `db`.\n    pub fn report_read_tx_metrics(&self, reducer: Option<ReducerName>, metrics: TxMetrics) {\n        self.report_tx_metrics(reducer, None, None, Some(metrics));\n    }\n\n    /// Read the value of [ST_VARNAME_ROW_LIMIT] from `st_var`\n    pub(crate) fn row_limit(&self, tx: &Tx) -> Result<Option<u64>, DBError> {\n        let data = self.read_var(tx, StVarName::RowLimit);\n\n        if let Some(StVarValue::U64(limit)) = data? {\n            return Ok(Some(limit));\n        }\n        Ok(None)\n    }\n\n    /// Read the value of a system variable from `st_var`\n    pub(crate) fn read_var(&self, tx: &Tx, name: StVarName) -> Result<Option<StVarValue>, DBError> {\n        if let Some(row_ref) = self\n            .iter_by_col_eq(tx, ST_VAR_ID, StVarFields::Name.col_id(), &name.into())?\n            .next()\n        {\n            return Ok(Some(StVarRow::try_from(row_ref)?.value));\n        }\n        Ok(None)\n    }\n\n    /// Write `rows` into a (sender) view's backing table.\n    ///\n    /// # Process\n    /// 1. Delete all rows for `sender` from the view's backing table\n    /// 2. Insert the new rows into the backing table\n    ///\n    /// # Arguments\n    /// * `tx` - Mutable transaction context\n    /// * `table_id` - The id of the view's backing table\n    /// * `sender` - The calling identity of the view being updated\n    /// * `rows` - Product values to insert\n    #[allow(clippy::too_many_arguments)]\n    pub fn materialize_view(\n        &self,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        sender: Identity,\n        rows: Vec<ProductValue>,\n    ) -> Result<(), DBError> {\n        // Delete rows for `sender` from the backing table\n        let rows_to_delete = self\n            .iter_by_col_eq_mut(tx, table_id, ColId(0), &sender.into())?\n            .map(|res| res.pointer())\n            .collect::<Vec<_>>();\n        self.delete(tx, table_id, rows_to_delete);\n\n        self.write_view_rows(tx, table_id, rows, Some(sender))?;\n\n        Ok(())\n    }\n\n    /// Write `rows` into an anonymous view's backing table.\n    ///\n    /// # Process\n    /// 1. Clear the view's backing table\n    /// 2. Insert the new rows into the backing table\n    ///\n    /// # Arguments\n    /// * `tx` - Mutable transaction context\n    /// * `table_id` - The id of the view's backing table\n    /// * `rows` - Product values to insert\n    #[allow(clippy::too_many_arguments)]\n    pub fn materialize_anonymous_view(\n        &self,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        rows: Vec<ProductValue>,\n    ) -> Result<(), DBError> {\n        // Clear entire backing table\n        self.clear_table(tx, table_id)?;\n\n        self.write_view_rows(tx, table_id, rows, None)?;\n\n        Ok(())\n    }\n\n    fn write_view_rows(\n        &self,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        rows: Vec<ProductValue>,\n        sender: Option<Identity>,\n    ) -> Result<(), DBError> {\n        match sender {\n            Some(sender) => {\n                for product in rows {\n                    let value = ProductValue::from_iter(std::iter::once(sender.into()).chain(product.elements));\n                    self.insert(\n                        tx,\n                        table_id,\n                        &value\n                            .to_bsatn_vec()\n                            .map_err(|_| ViewError::SerializeRow)\n                            .map_err(DatastoreError::from)?,\n                    )?;\n                }\n            }\n            None => {\n                for product in rows {\n                    self.insert(\n                        tx,\n                        table_id,\n                        &product\n                            .to_bsatn_vec()\n                            .map_err(|_| ViewError::SerializeRow)\n                            .map_err(DatastoreError::from)?,\n                    )?;\n                }\n            }\n        }\n\n        Ok(())\n    }\n}\n\nfn apply_history<H>(datastore: &Locking, database_identity: Identity, history: H) -> Result<(), DBError>\nwhere\n    H: durability::History<TxData = Txdata>,\n{\n    log::info!(\"[{database_identity}] DATABASE: applying transaction history...\");\n\n    // TODO: Revisit once we actually replay history suffixes, ie. starting\n    // from an offset larger than the history's min offset.\n    // TODO: We may want to require that a `tokio::runtime::Handle` is\n    // always supplied when constructing a `RelationalDB`. This would allow\n    // to spawn a timer task here which just prints the progress periodically\n    // in case the history is finite but very long.\n    let (_, max_tx_offset) = history.tx_range_hint();\n    let mut last_logged_percentage = 0;\n    let progress = |tx_offset: u64| {\n        if let Some(max_tx_offset) = max_tx_offset {\n            let percentage = f64::floor((tx_offset as f64 / max_tx_offset as f64) * 100.0) as i32;\n            if percentage > last_logged_percentage && percentage % 10 == 0 {\n                log::info!(\"[{database_identity}] Loaded {percentage}% ({tx_offset}/{max_tx_offset})\");\n                last_logged_percentage = percentage;\n            }\n        // Print _something_ even if we don't know what's still ahead.\n        } else if tx_offset.is_multiple_of(10_000) {\n            log::info!(\"[{database_identity}] Loading transaction {tx_offset}\");\n        }\n    };\n\n    let time_before = std::time::Instant::now();\n\n    let mut replay = datastore.replay(\n        progress,\n        // We don't want to instantiate an incorrect state;\n        // if the commitlog contains an inconsistency we'd rather get a hard error than showing customers incorrect data.\n        spacetimedb_datastore::locking_tx_datastore::datastore::ErrorBehavior::FailFast,\n    );\n    let start_tx_offset = replay.next_tx_offset();\n    history\n        .fold_transactions_from(start_tx_offset, &mut replay)\n        .map_err(anyhow::Error::from)?;\n\n    let time_elapsed = time_before.elapsed();\n    WORKER_METRICS\n        .replay_commitlog_time_seconds\n        .with_label_values(&database_identity)\n        .set(time_elapsed.as_secs_f64());\n\n    let end_tx_offset = replay.next_tx_offset();\n    WORKER_METRICS\n        .replay_commitlog_num_commits\n        .with_label_values(&database_identity)\n        .set((end_tx_offset - start_tx_offset) as _);\n\n    log::info!(\"[{database_identity}] DATABASE: applied transaction history\");\n    datastore.rebuild_state_after_replay()?;\n    log::info!(\"[{database_identity}] DATABASE: rebuilt state after replay\");\n\n    Ok(())\n}\n\npub type LocalDurability = Arc<durability::Local<ProductValue>>;\n/// Initialize local durability with the default parameters.\n///\n/// Also returned is a [`DiskSizeFn`] as required by [`RelationalDB::open`].\n///\n/// Note that this operation can be expensive, as it needs to traverse a suffix\n/// of the commitlog.\npub async fn local_durability(\n    replica_dir: ReplicaDir,\n    snapshot_worker: Option<&SnapshotWorker>,\n) -> Result<(LocalDurability, DiskSizeFn), DBError> {\n    let rt = tokio::runtime::Handle::current();\n    let on_new_segment = snapshot_worker.map(|snapshot_worker| {\n        let snapshot_worker = snapshot_worker.clone();\n        Arc::new(move || {\n            // Ignore errors: we don't want our durability to die and start throwing away queued TXes\n            // just because the snapshot worker shut down.\n            snapshot_worker.request_snapshot_ignore_closed();\n        }) as Arc<OnNewSegmentFn>\n    });\n    let local = asyncify(move || {\n        durability::Local::open(\n            replica_dir.clone(),\n            rt,\n            <_>::default(),\n            // Give the durability a handle to request a new snapshot run,\n            // which it will send down whenever we rotate commitlog segments.\n            on_new_segment,\n        )\n    })\n    .await\n    .map(Arc::new)?;\n    let disk_size_fn = Arc::new({\n        let durability = local.clone();\n        move || durability.size_on_disk()\n    });\n\n    Ok((local, disk_size_fn))\n}\n\n/// Open a [History] for replay from the local durable state.\n///\n/// Currently, this is simply a read-only copy of the commitlog.\npub async fn local_history(replica_dir: &ReplicaDir) -> io::Result<impl History<TxData = Txdata> + use<>> {\n    let commitlog_dir = replica_dir.commit_log();\n    asyncify(move || Commitlog::open(commitlog_dir, <_>::default(), None)).await\n}\n\n/// Watches snapshot creation events and compresses all commitlog segments older\n/// than the snapshot.\n///\n/// Suitable **only** for non-replicated databases.\npub async fn snapshot_watching_commitlog_compressor(\n    mut snapshot_rx: watch::Receiver<u64>,\n    mut clog_tx: Option<tokio::sync::mpsc::Sender<u64>>,\n    mut snap_tx: Option<tokio::sync::mpsc::Sender<u64>>,\n    durability: LocalDurability,\n) {\n    let mut prev_snapshot_offset = *snapshot_rx.borrow_and_update();\n    while snapshot_rx.changed().await.is_ok() {\n        let snapshot_offset = *snapshot_rx.borrow_and_update();\n        let durability = durability.clone();\n\n        if let Some(snap_tx) = &mut snap_tx\n            && let Err(err) = snap_tx.try_send(snapshot_offset)\n        {\n            tracing::warn!(\"failed to send offset {snapshot_offset} after snapshot creation: {err}\");\n        }\n\n        let res: io::Result<_> = asyncify(move || {\n            let segment_offsets = durability.existing_segment_offsets()?;\n            let start_idx = segment_offsets\n                .binary_search(&prev_snapshot_offset)\n                // if the snapshot is in the middle of a segment, we want to round down.\n                // [0, 2].binary_search(1) will return Err(1), so we subtract 1.\n                .unwrap_or_else(|i| i.saturating_sub(1));\n            let segment_offsets = &segment_offsets[start_idx..];\n            let end_idx = segment_offsets\n                .binary_search(&snapshot_offset)\n                .unwrap_or_else(|i| i.saturating_sub(1));\n            // in this case, segment_offsets[end_idx] is the segment that contains the snapshot,\n            // which we don't want to compress, so an exclusive range is correct.\n            let segment_offsets = &segment_offsets[..end_idx];\n            durability.compress_segments(segment_offsets)?;\n            let n = segment_offsets.len();\n            let last_compressed_segment = if n > 0 { Some(segment_offsets[n - 1]) } else { None };\n            Ok(last_compressed_segment)\n        })\n        .await;\n\n        let last_compressed_segment = match res {\n            Ok(opt_offset) => opt_offset,\n            Err(err) => {\n                tracing::warn!(\"failed to compress segments: {err}\");\n                continue;\n            }\n        };\n        prev_snapshot_offset = snapshot_offset;\n\n        if let Some((clog_tx, last_compressed_segment)) = clog_tx.as_mut().zip(last_compressed_segment)\n            && let Err(err) = clog_tx.try_send(last_compressed_segment)\n        {\n            tracing::warn!(\"failed to send offset {last_compressed_segment} after compression: {err}\");\n        }\n    }\n}\n\n/// Open a [`SnapshotRepository`] at `db_path/snapshots`,\n/// configured to store snapshots of the database `database_identity`/`replica_id`.\npub fn open_snapshot_repo(\n    path: SnapshotsPath,\n    database_identity: Identity,\n    replica_id: u64,\n) -> Result<Arc<SnapshotRepository>, Box<SnapshotError>> {\n    path.create().map_err(SnapshotError::from)?;\n    SnapshotRepository::open(path, database_identity, replica_id)\n        .map(Arc::new)\n        .map_err(Box::new)\n}\n\nfn default_row_count_fn(db: Identity) -> RowCountFn {\n    Arc::new(move |table_id, table_name| {\n        DB_METRICS\n            .rdb_num_table_rows\n            .with_label_values(&db, &table_id.into(), table_name)\n            .get()\n    })\n}\n\n#[cfg(any(test, feature = \"test\"))]\npub mod tests_utils {\n    use crate::db::snapshot;\n    use crate::db::snapshot::SnapshotWorker;\n    use crate::messages::control_db::HostType;\n\n    use super::*;\n    use core::ops::Deref;\n    use durability::EmptyHistory;\n    use spacetimedb_datastore::locking_tx_datastore::MutTxId;\n    use spacetimedb_datastore::locking_tx_datastore::TxId;\n    use spacetimedb_fs_utils::compression::CompressType;\n    use spacetimedb_lib::{bsatn::to_vec, ser::Serialize};\n    use spacetimedb_paths::server::ReplicaDir;\n    use spacetimedb_paths::server::SnapshotDirPath;\n    use spacetimedb_paths::FromPathUnchecked;\n    use tempfile::TempDir;\n\n    pub enum TestDBDir {\n        /// A directory that deletes itself when dropped.\n        Temp(TempReplicaDir),\n        /// A directory that will not delete itself when dropped.\n        Persistent(ReplicaDir),\n    }\n    impl Deref for TestDBDir {\n        type Target = ReplicaDir;\n        fn deref(&self) -> &Self::Target {\n            match self {\n                TestDBDir::Temp(dir) => &dir.0,\n                TestDBDir::Persistent(dir) => dir,\n            }\n        }\n    }\n\n    impl From<TempReplicaDir> for TestDBDir {\n        fn from(dir: TempReplicaDir) -> Self {\n            Self::Temp(dir)\n        }\n    }\n\n    /// Constituents of a [TestDB], see [TestDB::into_parts].\n    pub type TestDBParts = (\n        Arc<RelationalDB>,\n        Option<Arc<durability::Local<ProductValue>>>,\n        Option<tokio::runtime::Runtime>,\n        Option<TestDBDir>,\n    );\n\n    /// A [`RelationalDB`] in a temporary directory.\n    ///\n    /// When dropped, any resources including the temporary directory will be\n    /// removed.\n    ///\n    /// To ensure all data is flushed to disk when using the durable variant\n    /// constructed via [`Self::durable`], [`Self::close`] or [`Self::reopen`]\n    /// must be used.\n    ///\n    /// To keep the temporary directory, use [`Self::reopen`] or [`Self::into_parts`].\n    ///\n    /// [`TestDB`] is deref-coercible into [`RelationalDB`], which is dubious\n    /// but convenient.\n    pub struct TestDB {\n        pub db: Arc<RelationalDB>,\n\n        durable: Option<DurableState>,\n        want_snapshot_repo: bool,\n    }\n\n    pub struct TempReplicaDir(ReplicaDir);\n    impl TempReplicaDir {\n        pub fn new() -> io::Result<Self> {\n            let dir = TempDir::with_prefix(\"stdb_test\")?;\n            Ok(Self(ReplicaDir::from_path_unchecked(dir.keep())))\n        }\n    }\n    impl Deref for TempReplicaDir {\n        type Target = ReplicaDir;\n        fn deref(&self) -> &Self::Target {\n            &self.0\n        }\n    }\n    impl Drop for TempReplicaDir {\n        fn drop(&mut self) {\n            let _ = std::fs::remove_dir_all(&self.0);\n        }\n    }\n\n    struct DurableState {\n        durability: Arc<durability::Local<ProductValue>>,\n        rt: tokio::runtime::Runtime,\n        replica_dir: TestDBDir,\n    }\n\n    impl TestDB {\n        pub const DATABASE_IDENTITY: Identity = Identity::ZERO;\n        // pub const DATABASE_IDENTITY: Identity = Identity::ZERO;\n        pub const OWNER: Identity = Identity::ZERO;\n\n        /// Create a [`TestDB`] which does not store data on disk.\n        pub fn in_memory() -> Result<Self, DBError> {\n            let db = Self::in_memory_internal().map(Arc::new)?;\n            Ok(Self {\n                db,\n                durable: None,\n                want_snapshot_repo: false,\n            })\n        }\n\n        /// Create a [`TestDB`] which stores data in a local commitlog.\n        ///\n        /// Note that flushing the log is an asynchronous process. [`Self::reopen`]\n        /// ensures all data has been flushed to disk before re-opening the\n        /// database.\n        pub fn durable() -> Result<Self, DBError> {\n            let dir = TempReplicaDir::new()?;\n            let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;\n            // Enter the runtime so that `Self::durable_internal` can spawn a `SnapshotWorker`.\n            let _rt = rt.enter();\n            let (db, durability) = Self::durable_internal(&dir, rt.handle().clone(), true)?;\n            let durable = DurableState {\n                durability,\n                rt,\n                replica_dir: dir.into(),\n            };\n\n            Ok(Self {\n                db: Arc::new(db),\n                durable: Some(durable),\n                want_snapshot_repo: true,\n            })\n        }\n\n        pub fn durable_without_snapshot_repo() -> Result<Self, DBError> {\n            let dir = TempReplicaDir::new()?;\n            let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;\n            // Enter the runtime so that `Self::durable_internal` can spawn a `SnapshotWorker`.\n            let _rt = rt.enter();\n            let (db, durability) = Self::durable_internal(&dir, rt.handle().clone(), false)?;\n            let durable = DurableState {\n                durability,\n                rt,\n                replica_dir: dir.into(),\n            };\n\n            Ok(Self {\n                db: Arc::new(db),\n                durable: Some(durable),\n                want_snapshot_repo: false,\n            })\n        }\n\n        pub fn open_existing_durable(\n            root: &ReplicaDir,\n            rt: tokio::runtime::Handle,\n            replica_id: u64,\n            db_identity: Identity,\n            owner_identity: Identity,\n            want_snapshot_repo: bool,\n        ) -> Result<(RelationalDB, Arc<durability::Local<ProductValue>>), DBError> {\n            let snapshots = want_snapshot_repo\n                .then(|| {\n                    open_snapshot_repo(root.snapshots(), db_identity, replica_id)\n                        .map(|repo| SnapshotWorker::new(repo, snapshot::Compression::Disabled))\n                })\n                .transpose()?;\n\n            let (local, disk_size_fn) = rt.block_on(local_durability(root.clone(), snapshots.as_ref()))?;\n            let history = local.as_history();\n\n            let persistence = Persistence {\n                durability: local.clone(),\n                disk_size: disk_size_fn,\n                snapshots,\n                runtime: rt,\n            };\n\n            let (db, _) = RelationalDB::open(\n                db_identity,\n                owner_identity,\n                history,\n                Some(persistence),\n                None,\n                PagePool::new_for_test(),\n            )?;\n            let db = db.with_row_count(Self::row_count_fn());\n            Ok((db, local))\n        }\n        /// Create a [`TestDB`] which stores data in a local commitlog,\n        /// initialized with pre-existing data from `history`.\n        ///\n        /// [`TestHistory::from_txes`] is an easy-ish way to construct a non-empty [`History`].\n        ///\n        /// `expected_num_clients` is the expected size of the `connected_clients` return\n        /// from [`RelationalDB::open`] after replaying `history`.\n        /// Opening with an empty history, or one that does not insert into `st_client`,\n        /// should result in this number being 0.\n        pub fn in_memory_with_history(\n            history: impl durability::History<TxData = Txdata>,\n            expected_num_clients: usize,\n        ) -> Result<Self, DBError> {\n            let db = Self::open_db(history, None, None, expected_num_clients)?;\n            Ok(Self {\n                db: Arc::new(db),\n                durable: None,\n                want_snapshot_repo: false,\n            })\n        }\n\n        /// Re-open the database, after ensuring that all data has been flushed\n        /// to disk (if the database was created via [`Self::durable`]).\n        pub fn reopen(self) -> Result<Self, DBError> {\n            if let Some(rt) = self.runtime() {\n                rt.block_on(self.db.shutdown());\n            }\n            drop(self.db);\n\n            if let Some(DurableState {\n                durability: _,\n                rt,\n                replica_dir,\n            }) = self.durable\n            {\n                // Enter the runtime so that `Self::durable_internal` can spawn a `SnapshotWorker`.\n                let _rt = rt.enter();\n                let (db, handle) = Self::durable_internal(&replica_dir, rt.handle().clone(), self.want_snapshot_repo)?;\n                let durable = DurableState {\n                    durability: handle,\n                    rt,\n                    replica_dir,\n                };\n\n                Ok(Self {\n                    db: Arc::new(db),\n                    durable: Some(durable),\n                    ..self\n                })\n            } else {\n                let db = Self::in_memory_internal().map(Arc::new)?;\n                Ok(Self { db, ..self })\n            }\n        }\n\n        pub fn with_row_count(self, row_count: RowCountFn) -> Self {\n            let db = Arc::into_inner(self.db).expect(\"TestDB::db should be unique\");\n            let db = Arc::new(db.with_row_count(row_count));\n            Self { db, ..self }\n        }\n\n        /// The root path of the (temporary) database directory.\n        pub fn path(&self) -> Option<&TestDBDir> {\n            self.durable.as_ref().map(|dstate| &dstate.replica_dir)\n        }\n\n        /// Handle to the tokio runtime, available if [`Self::durable`] was used\n        /// to create the [`TestDB`].\n        pub fn runtime(&self) -> Option<&tokio::runtime::Handle> {\n            self.durable.as_ref().map(|ds| ds.rt.handle())\n        }\n\n        /// Deconstruct `self` into its constituents.\n        #[allow(unused)]\n        pub fn into_parts(self) -> TestDBParts {\n            let Self { db, durable, .. } = self;\n            match durable {\n                Some(DurableState {\n                    durability: handle,\n                    rt,\n                    replica_dir,\n                }) => (db, Some(handle), Some(rt), Some(replica_dir)),\n                None => (db, None, None, None),\n            }\n        }\n\n        fn in_memory_internal() -> Result<RelationalDB, DBError> {\n            Self::open_db(EmptyHistory::new(), None, None, 0)\n        }\n\n        fn durable_internal(\n            root: &ReplicaDir,\n            rt: tokio::runtime::Handle,\n            want_snapshot_repo: bool,\n        ) -> Result<(RelationalDB, Arc<durability::Local<ProductValue>>), DBError> {\n            let snapshots = want_snapshot_repo\n                .then(|| {\n                    open_snapshot_repo(root.snapshots(), Identity::ZERO, 0)\n                        .map(|repo| SnapshotWorker::new(repo, snapshot::Compression::Disabled))\n                })\n                .transpose()?;\n            let (local, disk_size_fn) = rt.block_on(local_durability(root.clone(), snapshots.as_ref()))?;\n            let history = local.as_history();\n            let persistence = Persistence {\n                durability: local.clone(),\n                disk_size: disk_size_fn,\n                snapshots,\n                runtime: rt,\n            };\n            let db = Self::open_db(history, Some(persistence), None, 0)?;\n\n            Ok((db, local))\n        }\n\n        pub fn open_db(\n            history: impl durability::History<TxData = Txdata>,\n            persistence: Option<Persistence>,\n            metrics_recorder_queue: Option<MetricsRecorderQueue>,\n            expected_num_clients: usize,\n        ) -> Result<RelationalDB, DBError> {\n            let (db, connected_clients) = RelationalDB::open(\n                Self::DATABASE_IDENTITY,\n                Self::OWNER,\n                history,\n                persistence,\n                metrics_recorder_queue,\n                PagePool::new_for_test(),\n            )?;\n            assert_eq!(connected_clients.len(), expected_num_clients);\n            let db = db.with_row_count(Self::row_count_fn());\n            db.with_auto_commit(Workload::Internal, |tx| {\n                db.set_initialized(tx, Program::empty(HostType::Wasm.into()))\n            })?;\n            Ok(db)\n        }\n\n        // NOTE: This is important to make compiler tests work.\n        fn row_count_fn() -> RowCountFn {\n            Arc::new(|_, _| i64::MAX)\n        }\n\n        pub fn take_snapshot(&self, repo: &SnapshotRepository) -> Result<Option<SnapshotDirPath>, DBError> {\n            Ok(self.inner.take_snapshot(repo)?)\n        }\n    }\n\n    impl Deref for TestDB {\n        type Target = Arc<RelationalDB>;\n\n        fn deref(&self) -> &Self::Target {\n            &self.db\n        }\n    }\n\n    pub fn with_read_only<T>(db: &RelationalDB, f: impl FnOnce(&mut Tx) -> T) -> T {\n        db.with_read_only(Workload::ForTests, f)\n    }\n\n    pub fn with_auto_commit<A, E: From<DBError>>(\n        db: &RelationalDB,\n        f: impl FnOnce(&mut MutTx) -> Result<A, E>,\n    ) -> Result<A, E> {\n        db.with_auto_commit(Workload::ForTests, f)\n    }\n\n    pub fn begin_tx(db: &RelationalDB) -> TxId {\n        db.begin_tx(Workload::ForTests)\n    }\n\n    pub fn begin_mut_tx(db: &RelationalDB) -> MutTxId {\n        db.begin_mut_tx(IsolationLevel::Serializable, Workload::ForTests)\n    }\n\n    pub fn insert<'a, T: Serialize>(\n        db: &'a RelationalDB,\n        tx: &'a mut MutTx,\n        table_id: TableId,\n        row: &T,\n    ) -> Result<(AlgebraicValue, RowRef<'a>), DBError> {\n        let (gen_cols, row_ref, _) = db.insert(tx, table_id, &to_vec(row).unwrap())?;\n        let gen_cols = row_ref.project(&gen_cols).unwrap();\n        Ok((gen_cols, row_ref))\n    }\n\n    /// Allocate a backing table in the datastore for a view\n    pub fn create_view_for_test(\n        db: &RelationalDB,\n        name: &str,\n        schema: &[(&'static str, AlgebraicType)],\n        is_anonymous: bool,\n    ) -> Result<(ViewId, TableId), DBError> {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        // Add the view's product type to the typespace\n        let name = RawIdentifier::new(name);\n        let type_ref = builder.add_algebraic_type(\n            [],\n            name.clone(),\n            AlgebraicType::Product(ProductType::from_iter(schema.iter().cloned())),\n            true,\n        );\n\n        builder.add_view(\n            name.clone(),\n            0,\n            true,\n            is_anonymous,\n            ProductType::unit(),\n            AlgebraicType::array(AlgebraicType::Ref(type_ref)),\n        );\n\n        let module_def: ModuleDef = builder.finish().try_into()?;\n        let view_def: &ViewDef = module_def.view(&*name).expect(\"view not found\");\n\n        // Allocate a backing table and return its table id\n        db.with_auto_commit(Workload::Internal, |tx| db.create_view(tx, &module_def, view_def))\n    }\n\n    /// Insert a row into a view's backing table\n    pub fn insert_into_view<'a>(\n        db: &'a RelationalDB,\n        tx: &'a mut MutTx,\n        table_id: TableId,\n        sender: Option<Identity>,\n        row: ProductValue,\n    ) -> Result<RowRef<'a>, DBError> {\n        let meta_cols = match sender {\n            Some(identity) => vec![identity.into()],\n            None => vec![],\n        };\n        let cols = meta_cols.into_iter().chain(row.elements);\n        let row = ProductValue::from_iter(cols);\n        db.insert(tx, table_id, &to_vec(&row).unwrap())\n            .map(|(_, row_ref, _)| row_ref)\n    }\n\n    /// An in-memory commitlog used for tests that want to replay a known history.\n    pub struct TestHistory(commitlog::commitlog::Generic<commitlog::repo::Memory, Txdata>);\n\n    impl durability::History for TestHistory {\n        type TxData = Txdata;\n\n        fn fold_transactions_from<D>(&self, offset: TxOffset, decoder: D) -> Result<(), D::Error>\n        where\n            D: commitlog::Decoder,\n            D::Error: From<commitlog::error::Traversal>,\n        {\n            self.0.fold_transactions_from(offset, decoder)\n        }\n\n        fn transactions_from<'a, D>(\n            &self,\n            offset: TxOffset,\n            decoder: &'a D,\n        ) -> impl Iterator<Item = Result<commitlog::Transaction<Self::TxData>, D::Error>>\n        where\n            D: commitlog::Decoder<Record = Self::TxData>,\n            D::Error: From<commitlog::error::Traversal>,\n            Self::TxData: 'a,\n        {\n            self.0.transactions_from(offset, decoder)\n        }\n\n        fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n            let min = self.0.min_committed_offset().unwrap_or_default();\n            let max = self.0.max_committed_offset();\n\n            (min, max)\n        }\n    }\n\n    impl TestHistory {\n        pub fn from_txes(txes: impl IntoIterator<Item = Txdata>) -> Self {\n            let mut log = commitlog::tests::helpers::mem_log::<Txdata>(32);\n            commitlog::tests::helpers::fill_log_with(&mut log, txes);\n            Self(log)\n        }\n    }\n\n    pub fn make_snapshot(\n        dir: SnapshotsPath,\n        identity: Identity,\n        replica: u64,\n        compress: CompressType,\n        delete_if_exists: bool,\n    ) -> (SnapshotsPath, SnapshotRepository) {\n        let path = dir.0.join(format!(\"{replica}_{compress:?}\"));\n        if delete_if_exists && path.exists() {\n            std::fs::remove_dir_all(&path).unwrap();\n        }\n        let dir = SnapshotsPath::from_path_unchecked(path);\n        dir.create().unwrap();\n        let snapshot = SnapshotRepository::open(dir.clone(), identity, replica).unwrap();\n\n        (dir, snapshot)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    #![allow(clippy::disallowed_macros)]\n\n    use std::cell::RefCell;\n    use std::fs::OpenOptions;\n    use std::path::PathBuf;\n    use std::rc::Rc;\n    use std::time::{Duration, Instant};\n\n    use super::tests_utils::begin_mut_tx;\n    use super::*;\n    use crate::db::relational_db::tests_utils::{begin_tx, create_view_for_test, insert, make_snapshot, TestDB};\n    use anyhow::bail;\n    use bytes::Bytes;\n    use commitlog::payload::txdata;\n    use commitlog::Commitlog;\n    use pretty_assertions::{assert_eq, assert_matches};\n    use spacetimedb_data_structures::map::IntMap;\n    use spacetimedb_datastore::error::{DatastoreError, IndexError};\n    use spacetimedb_datastore::execution_context::ReducerContext;\n    use spacetimedb_datastore::system_tables::{\n        system_tables, StConstraintRow, StIndexRow, StSequenceRow, StTableRow, ST_CONSTRAINT_ID, ST_INDEX_ID,\n        ST_SEQUENCE_ID, ST_TABLE_ID,\n    };\n    use spacetimedb_fs_utils::compression::CompressType;\n    use spacetimedb_lib::db::raw_def::v9::{btree, RawTableDefBuilder};\n    use spacetimedb_lib::error::ResultTest;\n    use spacetimedb_lib::Identity;\n    use spacetimedb_lib::Timestamp;\n    use spacetimedb_paths::server::ReplicaDir;\n    use spacetimedb_paths::FromPathUnchecked;\n    use spacetimedb_sats::buffer::BufReader;\n    use spacetimedb_sats::product;\n    use spacetimedb_schema::schema::RowLevelSecuritySchema;\n    use spacetimedb_snapshot::CompressionStats;\n    #[cfg(unix)]\n    use spacetimedb_snapshot::Snapshot;\n    use spacetimedb_table::read_column::ReadColumn;\n    use spacetimedb_table::table::RowRef;\n    use tempfile::TempDir;\n    use tests::tests_utils::TestHistory;\n\n    fn my_table(col_type: AlgebraicType) -> TableSchema {\n        table(\"MyTable\", ProductType::from([(\"my_col\", col_type)]), |builder| builder)\n    }\n\n    fn table(\n        name: &str,\n        columns: ProductType,\n        f: impl FnOnce(RawTableDefBuilder<'_>) -> RawTableDefBuilder,\n    ) -> TableSchema {\n        let mut builder = RawModuleDefV9Builder::new();\n        f(builder.build_table_with_new_type(RawIdentifier::new(name), columns, true));\n        let raw = builder.finish();\n        let def: ModuleDef = raw.try_into().expect(\"table validation failed\");\n        let table = def.table(name).expect(\"table not found\");\n        TableSchema::from_module_def(&def, table, (), TableId::SENTINEL)\n    }\n\n    fn view_module_def() -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        let return_type_ref = builder.add_algebraic_type(\n            [],\n            \"my_view_return_type\",\n            AlgebraicType::product([(\"b\", AlgebraicType::U8)]),\n            true,\n        );\n        builder.add_view(\n            \"my_view\",\n            0,\n            true,\n            false,\n            ProductType::unit(),\n            AlgebraicType::array(AlgebraicType::Ref(return_type_ref)),\n        );\n        let raw = builder.finish();\n        raw.try_into().expect(\"table validation failed\")\n    }\n\n    fn table_auto_inc() -> TableSchema {\n        table(\n            \"MyTable\",\n            ProductType::from([(\"my_col\", AlgebraicType::I64)]),\n            |builder| {\n                builder\n                    .with_primary_key(0)\n                    .with_column_sequence(0)\n                    .with_unique_constraint(0)\n                    .with_index_no_accessor_name(btree(0))\n            },\n        )\n    }\n\n    fn table_indexed(is_unique: bool) -> TableSchema {\n        table(\n            \"MyTable\",\n            ProductType::from([(\"my_col\", AlgebraicType::I64), (\"other_col\", AlgebraicType::I64)]),\n            |builder| {\n                let builder = builder.with_index_no_accessor_name(btree(0));\n\n                if is_unique {\n                    builder.with_unique_constraint(col_list![0])\n                } else {\n                    builder\n                }\n            },\n        )\n    }\n\n    #[test]\n    fn test() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        stdb.commit_tx(tx)?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_open_twice() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        stdb.commit_tx(tx)?;\n\n        match TestDB::open_existing_durable(\n            stdb.path().unwrap(),\n            stdb.runtime().unwrap().clone(),\n            0,\n            stdb.database_identity(),\n            stdb.owner_identity(),\n            false,\n        ) {\n            Ok(_) => {\n                panic!(\"Allowed to open database twice\")\n            }\n            Err(e) => match e {\n                DBError::Database(DatabaseError::DatabasedOpened(_, _)) => {}\n                err => {\n                    panic!(\"Failed with error {err:?}\")\n                }\n            },\n        }\n\n        Ok(())\n    }\n\n    fn setup_view(stdb: &TestDB) -> ResultTest<(ViewId, TableId, ModuleDef, ViewDef)> {\n        let module_def = view_module_def();\n        let view_def = module_def.view(\"my_view\").unwrap();\n\n        let mut tx = begin_mut_tx(stdb);\n        let (view_id, table_id) = stdb.create_view(&mut tx, &module_def, view_def)?;\n        stdb.commit_tx(tx)?;\n\n        Ok((view_id, table_id, module_def.clone(), view_def.clone()))\n    }\n\n    fn setup_anonymous_view(stdb: &TestDB) -> ResultTest<(ViewId, TableId)> {\n        Ok(create_view_for_test(\n            stdb,\n            \"my_anonymous_view\",\n            &[(\"b\", AlgebraicType::U8)],\n            true,\n        )?)\n    }\n\n    fn insert_view_row(stdb: &TestDB, view_id: ViewId, table_id: TableId, sender: Identity, v: u8) -> ResultTest<()> {\n        let row_pv = |v: u8| product![v];\n\n        let mut tx = begin_mut_tx(stdb);\n        tx.subscribe_view(view_id, ArgId::SENTINEL, sender)?;\n        stdb.materialize_view(&mut tx, table_id, sender, vec![row_pv(v)])?;\n        stdb.commit_tx(tx)?;\n\n        Ok(())\n    }\n\n    fn project_views(stdb: &TestDB, table_id: TableId, sender: Identity) -> Vec<ProductValue> {\n        let tx = begin_tx(stdb);\n\n        stdb.iter_by_col_eq(&tx, table_id, 0, &sender.into())\n            .unwrap()\n            .map(|row| {\n                let pv = row.to_product_value();\n                ProductValue {\n                    elements: pv.elements.iter().skip(1).cloned().collect(),\n                }\n            })\n            .collect()\n    }\n\n    fn project_anonymous_views(stdb: &TestDB, table_id: TableId) -> Vec<ProductValue> {\n        let tx = begin_tx(stdb);\n\n        stdb.iter(&tx, table_id)\n            .unwrap()\n            .map(|row| row.to_product_value())\n            .collect()\n    }\n\n    fn update_last_called(stdb: &TestDB, view_id: ViewId, sender: Identity, last_called: Timestamp) -> ResultTest<()> {\n        let mut tx = begin_mut_tx(stdb);\n        tx.update_view_timestamp_at(view_id, ArgId::SENTINEL, sender, last_called)?;\n        stdb.commit_tx(tx)?;\n        Ok(())\n    }\n\n    #[test]\n    fn test_view_tables_are_ephemeral_in_commitlog() -> ResultTest<()> {\n        let stdb = TestDB::durable_without_snapshot_repo()?;\n\n        let (view_id, table_id, _, _) = setup_view(&stdb)?;\n\n        // Write some rows (reusing the same helper)\n        insert_view_row(&stdb, view_id, table_id, Identity::ONE, 10)?;\n        insert_view_row(&stdb, view_id, table_id, Identity::ZERO, 20)?;\n\n        assert!(\n            !project_views(&stdb, table_id, Identity::ZERO).is_empty(),\n            \"View table should NOT be empty after insert\"\n        );\n\n        // Reopen the database — view tables must not persist\n        let stdb = stdb.reopen()?;\n\n        // Validate that the view's backing table has been removed\n        assert!(\n            project_views(&stdb, table_id, Identity::ZERO).is_empty(),\n            \"View table should be empty after reopening the database\"\n        );\n\n        let tx = begin_mut_tx(&stdb);\n        let subs_rows = tx.lookup_st_view_subs(view_id)?;\n        assert!(\n            subs_rows.is_empty(),\n            \"st_view_subs should be empty after reopening the database\"\n        );\n        Ok(())\n    }\n\n    #[test]\n    fn test_view_materialization_does_not_consume_tx_offset() -> ResultTest<()> {\n        let stdb = TestDB::durable_without_snapshot_repo()?;\n\n        let (tx_offset_1, view_id, table_id) = {\n            let module_def = view_module_def();\n            let view_def = module_def.view(\"my_view\").unwrap();\n\n            let mut tx = begin_mut_tx(&stdb);\n            let (view_id, table_id) = stdb.create_view(&mut tx, &module_def, view_def)?;\n            let (tx_offset, tx_data, ..) = stdb.commit_tx(tx)?.unwrap();\n            assert_eq!(Some(tx_offset), tx_data.tx_offset());\n\n            (tx_offset, view_id, table_id)\n        };\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.subscribe_view(view_id, ArgId::SENTINEL, Identity::ONE)?;\n        stdb.materialize_view(&mut tx, table_id, Identity::ONE, vec![product![10u8]])?;\n        let (tx_offset_2, tx_data, ..) = stdb.commit_tx(tx)?.unwrap();\n\n        // `tx_data.tx_offset()` should return `None`,\n        // so that it is not considered for durability.\n        // The tx offset reported for confirmed reads should stay the same.\n        assert!(tx_data.tx_offset().is_none());\n        assert_eq!(tx_offset_1, tx_offset_2);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_view_tables_are_ephemeral_with_snapshot() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let (view_id, table_id, _, _) = setup_view(&stdb)?;\n\n        // Write some rows (reusing the same helper)\n        insert_view_row(&stdb, view_id, table_id, Identity::ONE, 10)?;\n        insert_view_row(&stdb, view_id, table_id, Identity::ZERO, 20)?;\n\n        assert!(\n            !project_views(&stdb, table_id, Identity::ZERO).is_empty(),\n            \"View table should NOT be empty after insert\"\n        );\n\n        let root = stdb.path().unwrap().snapshots();\n        let (_, repo) = make_snapshot(root.clone(), Identity::ZERO, 0, CompressType::None, false);\n        stdb.take_snapshot(&repo)?.expect(\"snapshot should succeed\");\n\n        // Reopen the database — view tables must not persist\n        let stdb = stdb.reopen()?;\n\n        // Validate that the view's backing table has been removed\n        assert!(\n            project_views(&stdb, table_id, Identity::ZERO).is_empty(),\n            \"View table should be empty after reopening the database\"\n        );\n\n        let tx = begin_mut_tx(&stdb);\n        let subs_rows = tx.lookup_st_view_subs(view_id)?;\n        assert!(\n            subs_rows.is_empty(),\n            \"st_view_subs should be empty after reopening the database\"\n        );\n        Ok(())\n    }\n\n    #[test]\n    fn test_views() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let (view_id, table_id, _, _) = setup_view(&stdb)?;\n\n        let sender1 = Identity::ONE;\n\n        // Sender 1 insert\n        insert_view_row(&stdb, view_id, table_id, sender1, 42)?;\n\n        assert_eq!(\n            project_views(&stdb, table_id, sender1)[0],\n            product![42u8],\n            \"View row not inserted correctly\"\n        );\n\n        // Sender 2 insert\n        let sender2 = Identity::ZERO;\n        let before_sender2 = Instant::now();\n        insert_view_row(&stdb, view_id, table_id, sender2, 84)?;\n\n        assert_eq!(\n            project_views(&stdb, table_id, sender2)[0],\n            product![84u8],\n            \"Sender 2 view row not inserted correctly\"\n        );\n\n        // Restart database (view rows should NOT persist)\n        let stdb = stdb.reopen()?;\n\n        assert!(\n            project_views(&stdb, table_id, sender1).is_empty(),\n            \"Sender 1 rows should NOT persist after reopen\"\n        );\n        assert!(\n            project_views(&stdb, table_id, sender2).is_empty(),\n            \"Sender 2 rows should NOT persist after reopen\"\n        );\n\n        let tx = begin_mut_tx(&stdb);\n        let st = tx.lookup_st_view_subs(view_id)?;\n        assert!(st.is_empty(), \"st_view_subs should also be cleared after restart\");\n        stdb.commit_tx(tx)?;\n\n        // Reinsert after restart\n        insert_view_row(&stdb, view_id, table_id, sender2, 91)?;\n        assert_eq!(\n            project_views(&stdb, table_id, sender2)[0],\n            product![91u8],\n            \"Post-restart inserted rows must appear\"\n        );\n\n        // Clean expired rows\n        let mut tx = begin_mut_tx(&stdb);\n        tx.clear_expired_views(\n            Instant::now().saturating_duration_since(before_sender2),\n            VIEW_CLEANUP_BUDGET,\n        )?;\n        stdb.commit_tx(tx)?;\n\n        // Only sender2 exists after reinsertion\n        assert!(\n            project_views(&stdb, table_id, sender1).is_empty(),\n            \"Sender 1 should remain empty\"\n        );\n        assert_eq!(\n            project_views(&stdb, table_id, sender2)[0],\n            product![91u8],\n            \"Sender 2 row should remain\"\n        );\n\n        // And st_view_subs must reflect only sender2\n        let tx = begin_mut_tx(&stdb);\n        let st_after = tx.lookup_st_view_subs(view_id)?;\n        assert_eq!(st_after.len(), 1);\n        assert_eq!(st_after[0].identity.0, sender2);\n\n        Ok(())\n    }\n\n    /// Regression test for anonymous-view cleanup.\n    ///\n    /// If one subscriber row has expired but another subscriber for the same anonymous\n    /// view is still live, cleanup must delete only the stale bookkeeping row and keep\n    /// the shared materialized backing table intact.\n    #[test]\n    fn test_anonymous_view_cleanup_keeps_rows_for_live_subscribers() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n        let (view_id, table_id) = setup_anonymous_view(&stdb)?;\n\n        let stale_sender = Identity::ONE;\n        let live_sender = Identity::ZERO;\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.subscribe_view(view_id, ArgId::SENTINEL, stale_sender)?;\n        tx.subscribe_view(view_id, ArgId::SENTINEL, live_sender)?;\n        stdb.materialize_anonymous_view(&mut tx, table_id, vec![product![42u8]])?;\n        stdb.commit_tx(tx)?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.unsubscribe_view(view_id, ArgId::SENTINEL, stale_sender)?;\n        stdb.commit_tx(tx)?;\n\n        // Make one row definitely expired without relying on wall-clock sleeps.\n        update_last_called(&stdb, view_id, stale_sender, Timestamp::UNIX_EPOCH)?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.update_view_timestamp(view_id, ArgId::SENTINEL, live_sender)?;\n        stdb.commit_tx(tx)?;\n\n        // Cleanup should remove only the stale subscriber row and keep the shared\n        // anonymous materialization because another subscriber is still live.\n        let mut tx = begin_mut_tx(&stdb);\n        let (_cleaned, _total_expired) = tx.clear_expired_views(Duration::from_secs(1), VIEW_CLEANUP_BUDGET)?;\n        stdb.commit_tx(tx)?;\n\n        assert_eq!(\n            project_anonymous_views(&stdb, table_id),\n            vec![product![42u8]],\n            \"anonymous view rows should survive cleanup while another identity is still subscribed\"\n        );\n\n        let tx = begin_mut_tx(&stdb);\n        let st_after = tx.lookup_st_view_subs(view_id)?;\n        assert_eq!(st_after.len(), 1);\n        assert_eq!(st_after[0].identity.0, live_sender);\n        assert!(st_after[0].has_subscribers);\n        assert_eq!(st_after[0].num_subscribers, 1);\n\n        Ok(())\n    }\n\n    /// Regression test for anonymous-view cleanup.\n    ///\n    /// Once the final subscriber row for an anonymous view has expired, cleanup must\n    /// remove both the stale bookkeeping row and the shared materialized backing table.\n    #[test]\n    fn test_anonymous_view_cleanup_clears_rows_when_unused() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n        let (view_id, table_id) = setup_anonymous_view(&stdb)?;\n\n        let sender = Identity::ONE;\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.subscribe_view(view_id, ArgId::SENTINEL, sender)?;\n        stdb.materialize_anonymous_view(&mut tx, table_id, vec![product![42u8]])?;\n        stdb.commit_tx(tx)?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        tx.unsubscribe_view(view_id, ArgId::SENTINEL, sender)?;\n        stdb.commit_tx(tx)?;\n\n        // Mark the unsubscribed row as expired so cleanup can process it immediately.\n        update_last_called(&stdb, view_id, sender, Timestamp::UNIX_EPOCH)?;\n\n        // With no remaining subscriber rows, cleanup should drop the shared\n        // anonymous materialization and remove the bookkeeping row.\n        let mut tx = begin_mut_tx(&stdb);\n        let (_cleaned, _total_expired) = tx.clear_expired_views(Duration::from_secs(1), VIEW_CLEANUP_BUDGET)?;\n        stdb.commit_tx(tx)?;\n\n        assert!(\n            project_anonymous_views(&stdb, table_id).is_empty(),\n            \"anonymous view rows should be cleared once no entries remain\"\n        );\n\n        let tx = begin_mut_tx(&stdb);\n        let st_after = tx.lookup_st_view_subs(view_id)?;\n        assert!(st_after.is_empty());\n\n        Ok(())\n    }\n    #[test]\n    fn test_table_name() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        let t_id = stdb.table_id_from_name_mut(&tx, \"MyTable\")?;\n        assert_eq!(t_id, Some(table_id));\n        Ok(())\n    }\n\n    #[test]\n    fn test_column_name() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        let table_id = stdb.table_id_from_name_mut(&tx, \"MyTable\")?.unwrap();\n        let schema = stdb.schema_for_table_mut(&tx, table_id)?;\n        let col = schema.columns().iter().find(|x| &*x.col_name == \"my_col\").unwrap();\n        assert_eq!(col.col_pos, 0.into());\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_pre_commit() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = my_table(AlgebraicType::I32);\n        stdb.create_table(&mut tx, schema.clone())?;\n        let result = stdb.create_table(&mut tx, schema);\n        result.expect_err(\"create_table should error when called twice\");\n        Ok(())\n    }\n\n    fn read_first_col<T: ReadColumn>(row: RowRef<'_>) -> T {\n        row.read_col(0).unwrap()\n    }\n\n    fn collect_sorted<T: ReadColumn + Ord>(stdb: &RelationalDB, tx: &MutTx, table_id: TableId) -> ResultTest<Vec<T>> {\n        let mut rows = stdb.iter_mut(tx, table_id)?.map(read_first_col).collect::<Vec<T>>();\n        rows.sort();\n        Ok(rows)\n    }\n\n    fn collect_from_sorted<T: ReadColumn + Into<AlgebraicValue> + Ord>(\n        stdb: &RelationalDB,\n        tx: &MutTx,\n        table_id: TableId,\n        from: T,\n    ) -> ResultTest<Vec<T>> {\n        let from: AlgebraicValue = from.into();\n        let mut rows = stdb\n            .iter_by_col_range_mut(tx, table_id, 0, from..)?\n            .map(read_first_col)\n            .collect::<Vec<T>>();\n        rows.sort();\n        Ok(rows)\n    }\n\n    fn insert_three_i32s(stdb: &RelationalDB, tx: &mut MutTx, table_id: TableId) -> ResultTest<()> {\n        for v in [-1, 0, 1] {\n            insert(stdb, tx, table_id, &product![v])?;\n        }\n        Ok(())\n    }\n\n    #[test]\n    fn test_pre_commit() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n\n        insert_three_i32s(&stdb, &mut tx, table_id)?;\n        assert_eq!(collect_sorted::<i32>(&stdb, &tx, table_id)?, vec![-1, 0, 1]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_post_commit() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n\n        insert_three_i32s(&stdb, &mut tx, table_id)?;\n        stdb.commit_tx(tx)?;\n\n        let tx = begin_mut_tx(&stdb);\n        assert_eq!(collect_sorted::<i32>(&stdb, &tx, table_id)?, vec![-1, 0, 1]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_filter_range_pre_commit() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        insert_three_i32s(&stdb, &mut tx, table_id)?;\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i32)?, vec![0, 1]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_filter_range_post_commit() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n\n        insert_three_i32s(&stdb, &mut tx, table_id)?;\n        stdb.commit_tx(tx)?;\n\n        let tx = begin_mut_tx(&stdb);\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i32)?, vec![0, 1]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_rollback() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        let _ = stdb.rollback_mut_tx(tx);\n\n        let tx = begin_mut_tx(&stdb);\n        let result = stdb.table_id_from_name_mut(&tx, \"MyTable\")?;\n        assert!(\n            result.is_none(),\n            \"Table should not exist, so table_id_from_name should return none\"\n        );\n\n        let result = stdb.table_name_from_id_mut(&tx, table_id)?;\n        assert!(\n            result.is_none(),\n            \"Table should not exist, so table_name_from_id_mut should return none\",\n        );\n        Ok(())\n    }\n\n    #[test]\n    fn test_rollback() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        stdb.commit_tx(tx)?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        insert_three_i32s(&stdb, &mut tx, table_id)?;\n        let _ = stdb.rollback_mut_tx(tx);\n\n        let tx = begin_mut_tx(&stdb);\n        assert_eq!(collect_sorted::<i32>(&stdb, &tx, table_id)?, Vec::<i32>::new());\n        Ok(())\n    }\n\n    #[test]\n    fn test_auto_inc() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = table_auto_inc();\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        let sequence = stdb.sequence_id_from_name(&tx, \"MyTable_my_col_seq\")?;\n        assert!(sequence.is_some(), \"Sequence not created\");\n\n        insert(&stdb, &mut tx, table_id, &product![0i64])?;\n        insert(&stdb, &mut tx, table_id, &product![0i64])?;\n\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![1, 2]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_auto_inc_disable() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = table_auto_inc();\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        let sequence = stdb.sequence_id_from_name(&tx, \"MyTable_my_col_seq\")?;\n        assert!(sequence.is_some(), \"Sequence not created\");\n\n        insert(&stdb, &mut tx, table_id, &product![5i64])?;\n        insert(&stdb, &mut tx, table_id, &product![6i64])?;\n\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![5, 6]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_auto_inc_reload() -> ResultTest<()> {\n        let _ = env_logger::builder()\n            .filter_level(log::LevelFilter::Trace)\n            .format_timestamp(None)\n            .is_test(true)\n            .try_init();\n\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = table_auto_inc();\n\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        let sequence = stdb.sequence_id_from_name(&tx, \"MyTable_my_col_seq\")?;\n        assert!(sequence.is_some(), \"Sequence not created\");\n\n        insert(&stdb, &mut tx, table_id, &product![0i64])?;\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![1]);\n\n        stdb.commit_tx(tx)?;\n\n        let stdb = stdb.reopen()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        insert(&stdb, &mut tx, table_id, &product![0i64]).unwrap();\n\n        // Check the second row start after `SEQUENCE_PREALLOCATION_AMOUNT`\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![1, 4097]);\n        stdb.commit_tx(tx)?;\n\n        let stdb = stdb.reopen()?;\n        let mut tx = begin_mut_tx(&stdb);\n        insert(&stdb, &mut tx, table_id, &product![0i64]).unwrap();\n        // The next value will have a gap because we preallocate, but asserting the specific number\n        // seems brittle.\n        assert!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?[2] > 4097);\n        Ok(())\n    }\n\n    #[test]\n    fn test_indexed() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = table_indexed(false);\n\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        assert!(\n            stdb.index_id_from_name(&tx, \"MyTable_my_col_idx_btree\")?.is_some(),\n            \"Index not created\"\n        );\n\n        insert(&stdb, &mut tx, table_id, &product![1i64, 1i64])?;\n        insert(&stdb, &mut tx, table_id, &product![1i64, 1i64])?;\n\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![1]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_row_count() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = my_table(AlgebraicType::I64);\n        let table_id = stdb.create_table(&mut tx, schema)?;\n        insert(&stdb, &mut tx, table_id, &product![1i64])?;\n        insert(&stdb, &mut tx, table_id, &product![2i64])?;\n        stdb.commit_tx(tx)?;\n\n        let stdb = stdb.reopen()?;\n        let tx = begin_tx(&stdb);\n        assert_eq!(tx.table_row_count(table_id).unwrap(), 2);\n        Ok(())\n    }\n\n    // Because we don't create `rls` when first creating the database, check we pass the bootstrap\n    #[test]\n    fn test_row_level_reopen() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n        let mut tx = begin_mut_tx(&stdb);\n\n        let schema = my_table(AlgebraicType::I64);\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        let rls = RowLevelSecuritySchema {\n            sql: \"SELECT * FROM bar\".into(),\n            table_id,\n        };\n\n        tx.create_row_level_security(rls)?;\n        stdb.commit_tx(tx)?;\n\n        let stdb = stdb.reopen()?;\n        let tx = begin_mut_tx(&stdb);\n\n        assert_eq!(\n            tx.row_level_security_for_table_id(table_id)?,\n            vec![RowLevelSecuritySchema {\n                sql: \"SELECT * FROM bar\".into(),\n                table_id,\n            }]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unique() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let schema = table_indexed(true);\n        let table_id = stdb.create_table(&mut tx, schema).expect(\"stdb.create_table failed\");\n\n        assert!(\n            stdb.index_id_from_name(&tx, \"MyTable_my_col_idx_btree\")\n                .expect(\"index_id_from_name failed\")\n                .is_some(),\n            \"Index not created\"\n        );\n\n        insert(&stdb, &mut tx, table_id, &product![1i64, 0i64]).expect(\"stdb.insert failed\");\n        match insert(&stdb, &mut tx, table_id, &product![1i64, 1i64]) {\n            Ok(_) => panic!(\"Allow to insert duplicate row\"),\n            Err(DBError::Datastore(DatastoreError::Index(IndexError::UniqueConstraintViolation { .. }))) => {}\n            Err(err) => panic!(\"Expected error `UniqueConstraintViolation`, got {err}\"),\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_identity() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = table(\n            \"MyTable\",\n            ProductType::from([(\"my_col\", AlgebraicType::I64)]),\n            |builder| {\n                builder\n                    .with_column_sequence(0)\n                    .with_unique_constraint(0)\n                    .with_index_no_accessor_name(btree(0))\n            },\n        );\n\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        assert!(\n            stdb.index_id_from_name(&tx, \"MyTable_my_col_idx_btree\")?.is_some(),\n            \"Index not created\"\n        );\n\n        let sequence = stdb.sequence_id_from_name(&tx, \"MyTable_my_col_seq\")?;\n        assert!(sequence.is_some(), \"Sequence not created\");\n\n        insert(&stdb, &mut tx, table_id, &product![0i64])?;\n        insert(&stdb, &mut tx, table_id, &product![0i64])?;\n\n        assert_eq!(collect_from_sorted(&stdb, &tx, table_id, 0i64)?, vec![1, 2]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_cascade_drop_table() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let schema = table(\n            \"MyTable\",\n            ProductType::from([\n                (\"col1\", AlgebraicType::I64),\n                (\"col2\", AlgebraicType::I64),\n                (\"col3\", AlgebraicType::I64),\n                (\"col4\", AlgebraicType::I64),\n            ]),\n            |builder| {\n                builder\n                    .with_index_no_accessor_name(btree(0))\n                    .with_index_no_accessor_name(btree(1))\n                    .with_index_no_accessor_name(btree(2))\n                    .with_index_no_accessor_name(btree(3))\n                    .with_unique_constraint(0)\n                    .with_unique_constraint(1)\n                    .with_unique_constraint(3)\n                    .with_column_sequence(0)\n            },\n        );\n\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        let indexes = stdb\n            .iter_mut(&tx, ST_INDEX_ID)?\n            .map(|x| StIndexRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(indexes.len(), 4, \"Wrong number of indexes: {:#?}\", indexes);\n\n        let sequences = stdb\n            .iter_mut(&tx, ST_SEQUENCE_ID)?\n            .map(|x| StSequenceRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(sequences.len(), 1, \"Wrong number of sequences\");\n\n        let constraints = stdb\n            .iter_mut(&tx, ST_CONSTRAINT_ID)?\n            .map(|x| StConstraintRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(constraints.len(), 3, \"Wrong number of constraints\");\n\n        stdb.drop_table(&mut tx, table_id)?;\n\n        let indexes = stdb\n            .iter_mut(&tx, ST_INDEX_ID)?\n            .map(|x| StIndexRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(indexes.len(), 0, \"Wrong number of indexes DROP\");\n\n        let sequences = stdb\n            .iter_mut(&tx, ST_SEQUENCE_ID)?\n            .map(|x| StSequenceRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(sequences.len(), 0, \"Wrong number of sequences DROP\");\n\n        let constraints = stdb\n            .iter_mut(&tx, ST_CONSTRAINT_ID)?\n            .map(|x| StConstraintRow::try_from(x).unwrap())\n            .filter(|x| x.table_id == table_id)\n            .collect::<Vec<_>>();\n        assert_eq!(constraints.len(), 0, \"Wrong number of constraints DROP\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_rename_table() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n\n        let table_id = stdb.create_table(&mut tx, table_indexed(true))?;\n        stdb.rename_table(&mut tx, table_id, TableName::for_test(\"YourTable\"))?;\n        let table_name = stdb.table_name_from_id_mut(&tx, table_id)?;\n\n        assert_eq!(Some(\"YourTable\"), table_name.as_ref().map(Cow::as_ref));\n        // Also make sure we've removed the old ST_TABLES_ID row\n        let mut n = 0;\n        for row in stdb.iter_mut(&tx, ST_TABLE_ID)? {\n            let table = StTableRow::try_from(row)?;\n            if table.table_id == table_id {\n                n += 1;\n            }\n        }\n        assert_eq!(1, n);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_multi_column_index() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let columns = ProductType::from([\n            (\"a\", AlgebraicType::U64),\n            (\"b\", AlgebraicType::U64),\n            (\"c\", AlgebraicType::U64),\n        ]);\n\n        let schema = table(\"t\", columns, |builder| {\n            builder.with_index(btree([0, 1]), \"accessor_name_doesnt_matter\")\n        });\n\n        let mut tx = begin_mut_tx(&stdb);\n        let table_id = stdb.create_table(&mut tx, schema)?;\n\n        insert(&stdb, &mut tx, table_id, &product![0u64, 0u64, 1u64])?;\n        insert(&stdb, &mut tx, table_id, &product![0u64, 1u64, 2u64])?;\n        insert(&stdb, &mut tx, table_id, &product![1u64, 2u64, 2u64])?;\n\n        let cols = col_list![0, 1];\n        let value = product![0u64, 1u64].into();\n\n        let IterByColEqMutTx::Index(mut iter) = stdb.iter_by_col_eq_mut(&tx, table_id, cols, &value)? else {\n            panic!(\"expected index iterator\");\n        };\n\n        let Some(row) = iter.next() else {\n            panic!(\"expected non-empty iterator\");\n        };\n\n        assert_eq!(row.to_product_value(), product![0u64, 1u64, 2u64]);\n\n        // iter should only return a single row, so this count should now be 0.\n        assert_eq!(iter.count(), 0);\n        Ok(())\n    }\n\n    #[test]\n    /// Test that iteration yields each row only once\n    /// in the edge case where a row is committed and has been deleted and re-inserted within the iterating TX.\n    fn test_insert_delete_insert_iter() {\n        let stdb = TestDB::durable().expect(\"failed to create TestDB\");\n\n        let mut initial_tx = begin_mut_tx(&stdb);\n        let schema = my_table(AlgebraicType::I32);\n\n        let table_id = stdb.create_table(&mut initial_tx, schema).expect(\"create_table failed\");\n\n        stdb.commit_tx(initial_tx).expect(\"Commit initial_tx failed\");\n\n        // Insert a row and commit it, so the row is in the committed_state.\n        let mut insert_tx = begin_mut_tx(&stdb);\n        insert(&stdb, &mut insert_tx, table_id, &product!(AlgebraicValue::I32(0))).expect(\"Insert insert_tx failed\");\n        stdb.commit_tx(insert_tx).expect(\"Commit insert_tx failed\");\n\n        let mut delete_insert_tx = begin_mut_tx(&stdb);\n        // Delete the row, so it's in the `delete_tables` of `delete_insert_tx`.\n        assert_eq!(\n            stdb.delete_by_rel(&mut delete_insert_tx, table_id, [product!(AlgebraicValue::I32(0))]),\n            1\n        );\n\n        // Insert the row again, so that depending on the datastore internals,\n        // it may now be only in the committed_state,\n        // or in all three of the committed_state, delete_tables and insert_tables.\n        insert(\n            &stdb,\n            &mut delete_insert_tx,\n            table_id,\n            &product!(AlgebraicValue::I32(0)),\n        )\n        .expect(\"Insert delete_insert_tx failed\");\n\n        // Iterate over the table and assert that we see the committed-deleted-inserted row only once.\n        assert_eq!(\n            &stdb\n                .iter_mut(&delete_insert_tx, table_id)\n                .expect(\"iter delete_insert_tx failed\")\n                .map(|row_ref| row_ref.to_product_value())\n                .collect::<Vec<_>>(),\n            &[product!(AlgebraicValue::I32(0))],\n        );\n\n        let _ = stdb.rollback_mut_tx(delete_insert_tx);\n    }\n\n    #[test]\n    fn test_tx_inputs_are_in_the_commitlog() {\n        let _ = env_logger::builder()\n            .filter_level(log::LevelFilter::Trace)\n            .format_timestamp(None)\n            .is_test(true)\n            .try_init();\n\n        let stdb = TestDB::durable().expect(\"failed to create TestDB\");\n\n        let timestamp = Timestamp::now();\n        let workload = |name: &str| {\n            Workload::Reducer(ReducerContext {\n                name: ReducerName::for_test(name),\n                caller_identity: Identity::__dummy(),\n                caller_connection_id: ConnectionId::ZERO,\n                timestamp,\n                arg_bsatn: Bytes::new(),\n            })\n        };\n        let workload1 = workload(\"abstract_concrete_proxy_factory_impl\");\n\n        let row_ty = ProductType::from([(\"le_boeuf\", AlgebraicType::I32)]);\n        let schema = table(\"test_table\", row_ty.clone(), |builder| builder);\n\n        // Create an empty transaction\n        {\n            let tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload1.clone());\n            stdb.commit_tx(tx).expect(\"failed to commit empty transaction\");\n        }\n\n        // Create an empty transaction pretending to be an\n        // `__identity_connected__` call.\n        {\n            let tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload(\"__identity_connected__\"));\n            stdb.commit_tx(tx)\n                .expect(\"failed to commit empty __identity_connected__ transaction\");\n        }\n\n        // Create a non-empty transaction including reducer info\n        let table_id = {\n            let mut tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload1);\n            let table_id = stdb.create_table(&mut tx, schema).expect(\"failed to create table\");\n            insert(&stdb, &mut tx, table_id, &product!(AlgebraicValue::I32(0))).expect(\"failed to insert row\");\n            stdb.commit_tx(tx).expect(\"failed to commit tx\");\n\n            table_id\n        };\n\n        // Create a non-empty transaction without reducer info, as it would be\n        // created by a mutable SQL transaction\n        {\n            let mut tx = stdb.begin_mut_tx(IsolationLevel::Serializable, Workload::Sql);\n            insert(&stdb, &mut tx, table_id, &product!(AlgebraicValue::I32(-42))).expect(\"failed to insert row\");\n            stdb.commit_tx(tx).expect(\"failed to commit tx\");\n        }\n\n        // `txdata::Visitor` which only collects `txdata::Inputs`.\n        struct Inputs {\n            // The inputs collected during traversal of the log.\n            inputs: Vec<txdata::Inputs>,\n            // The number of transactions seen during traversal of the log.\n            num_txs: usize,\n            // System tables, needed to be able to consume transaction records.\n            sys: IntMap<TableId, ProductType>,\n            // The table created above, needed to be able to consume transaction\n            // records.\n            row_ty: ProductType,\n        }\n\n        impl txdata::Visitor for Inputs {\n            type Row = ();\n            type Error = anyhow::Error;\n\n            fn visit_insert<'a, R: BufReader<'a>>(\n                &mut self,\n                table_id: TableId,\n                reader: &mut R,\n            ) -> Result<Self::Row, Self::Error> {\n                let ty = self.sys.get(&table_id).unwrap_or(&self.row_ty);\n                let row = ProductValue::decode(ty, reader)?;\n                log::debug!(\"insert: {table_id} {row:?}\");\n                Ok(())\n            }\n\n            fn visit_delete<'a, R: BufReader<'a>>(\n                &mut self,\n                table_id: TableId,\n                reader: &mut R,\n            ) -> Result<Self::Row, Self::Error> {\n                // Allow specifically deletes from `st_sequence`,\n                // since the transactions in this test will allocate sequence values.\n                if table_id != ST_SEQUENCE_ID {\n                    bail!(\"unexpected delete for table: {table_id}\")\n                }\n                let ty = self.sys.get(&table_id).unwrap();\n                let row = ProductValue::decode(ty, reader)?;\n                log::debug!(\"delete: {table_id} {row:?}\");\n                Ok(())\n            }\n\n            fn skip_row<'a, R: BufReader<'a>>(\n                &mut self,\n                table_id: TableId,\n                _reader: &mut R,\n            ) -> Result<(), Self::Error> {\n                bail!(\"unexpected skip for table: {table_id}\")\n            }\n\n            fn visit_inputs(&mut self, inputs: &txdata::Inputs) -> Result<(), Self::Error> {\n                log::debug!(\"visit_inputs: {inputs:?}\");\n                self.inputs.push(inputs.clone());\n                Ok(())\n            }\n\n            fn visit_tx_start(&mut self, offset: u64) -> Result<(), Self::Error> {\n                log::debug!(\"tx start: {offset}\");\n                self.num_txs += 1;\n                Ok(())\n            }\n\n            fn visit_tx_end(&mut self) -> Result<(), Self::Error> {\n                log::debug!(\"tx end\");\n                Ok(())\n            }\n        }\n\n        struct Decoder(Rc<RefCell<Inputs>>);\n\n        impl spacetimedb_commitlog::Decoder for Decoder {\n            type Record = txdata::Txdata<()>;\n            type Error = txdata::DecoderError<anyhow::Error>;\n\n            #[inline]\n            fn decode_record<'a, R: BufReader<'a>>(\n                &self,\n                version: u8,\n                tx_offset: u64,\n                reader: &mut R,\n            ) -> Result<Self::Record, Self::Error> {\n                txdata::decode_record_fn(&mut *self.0.borrow_mut(), version, tx_offset, reader)\n            }\n\n            fn skip_record<'a, R: BufReader<'a>>(\n                &self,\n                version: u8,\n                _tx_offset: u64,\n                reader: &mut R,\n            ) -> Result<(), Self::Error> {\n                txdata::skip_record_fn(&mut *self.0.borrow_mut(), version, reader)\n            }\n        }\n\n        let replica_dir = {\n            // Ensure all resources are released and data is flushed to disk.\n            let (db, _, rt, dir) = stdb.into_parts();\n            let rt = rt.expect(\"Durable TestDB must have a runtime\");\n            rt.block_on(db.shutdown()).expect(\"should have durable offset\");\n            drop(db);\n            dir.expect(\"Durable TestDB must have a database directory\")\n        };\n\n        // Re-open commitlog and collect inputs.\n        let inputs = Rc::new(RefCell::new(Inputs {\n            inputs: Vec::new(),\n            num_txs: 0,\n            sys: system_tables()\n                .into_iter()\n                .map(|schema| (schema.table_id, schema.into_row_type()))\n                .collect(),\n            row_ty,\n        }));\n        {\n            let clog = Commitlog::<()>::open(replica_dir.commit_log(), Default::default(), None)\n                .expect(\"failed to open commitlog\");\n            let decoder = Decoder(Rc::clone(&inputs));\n            clog.fold_transactions(decoder).unwrap();\n        }\n        // Just a safeguard so we don't drop the temp dir before this point.\n        drop(replica_dir);\n\n        let inputs = Rc::into_inner(inputs).unwrap().into_inner();\n        log::debug!(\"collected inputs: {:?}\", inputs.inputs);\n\n        // We should've seen four transactions:\n        //\n        // - the internal tx which initializes `st_module`\n        // - three non-empty transactions here\n        //\n        // The empty transaction should've been ignored.\n        assert_eq!(inputs.num_txs, 4);\n        // Two of the transactions should yield inputs.\n        assert_eq!(inputs.inputs.len(), 2);\n\n        // Also assert that we got what we put in.\n        for (i, input) in inputs.inputs.into_iter().enumerate() {\n            let ReducerContext {\n                name: reducer_name,\n                caller_identity,\n                caller_connection_id,\n                timestamp: reducer_timestamp,\n                arg_bsatn,\n            } = ReducerContext::try_from(&input).unwrap();\n            if i == 0 {\n                assert_eq!(&*reducer_name, \"__identity_connected__\");\n            } else {\n                assert_eq!(&*reducer_name, \"abstract_concrete_proxy_factory_impl\");\n            }\n            assert!(\n                arg_bsatn.is_empty(),\n                \"expected args to be exhausted because nullary args were given\"\n            );\n            assert_eq!(caller_identity, Identity::ZERO);\n            assert_eq!(caller_connection_id, ConnectionId::ZERO);\n            assert_eq!(reducer_timestamp, timestamp);\n        }\n    }\n\n    /// This tests that we are able to correctly replay mutations to system tables,\n    /// in this case specifically `st_client`.\n    ///\n    /// [SpacetimeDB PR #2161](https://github.com/clockworklabs/SpacetimeDB/pull/2161)\n    /// fixed a bug where replaying deletes to `st_client` would fail due to an unpopulated index.\n    #[test]\n    fn replay_delete_from_st_client() {\n        use spacetimedb_datastore::system_tables::{StClientRow, ST_CLIENT_ID};\n\n        let row_0 = StClientRow {\n            identity: Identity::ZERO.into(),\n            connection_id: ConnectionId::ZERO.into(),\n        };\n        let row_1 = StClientRow {\n            identity: Identity::ZERO.into(),\n            connection_id: ConnectionId::from_u128(1).into(),\n        };\n\n        let history = TestHistory::from_txes([\n            // TX 0: insert row 0\n            Txdata {\n                inputs: None,\n                outputs: None,\n                mutations: Some(txdata::Mutations {\n                    inserts: Box::new([txdata::Ops {\n                        table_id: ST_CLIENT_ID,\n                        rowdata: Arc::new([row_0.into()]),\n                    }]),\n                    deletes: Box::new([]),\n                    truncates: Box::new([]),\n                }),\n            },\n            // TX 1: delete row 0\n            Txdata {\n                inputs: None,\n                outputs: None,\n                mutations: Some(txdata::Mutations {\n                    inserts: Box::new([]),\n                    deletes: Box::new([txdata::Ops {\n                        table_id: ST_CLIENT_ID,\n                        rowdata: Arc::new([row_0.into()]),\n                    }]),\n                    truncates: Box::new([]),\n                }),\n            },\n            // TX 2: insert row 1\n            Txdata {\n                inputs: None,\n                outputs: None,\n                mutations: Some(txdata::Mutations {\n                    inserts: Box::new([txdata::Ops {\n                        table_id: ST_CLIENT_ID,\n                        rowdata: Arc::new([row_1.into()]),\n                    }]),\n                    deletes: Box::new([]),\n                    truncates: Box::new([]),\n                }),\n            },\n        ]);\n\n        // We expect 1 client, since we left `row_1` in there.\n        let stdb = TestDB::in_memory_with_history(history, /* expected_num_clients: */ 1).unwrap();\n\n        let read_tx = begin_tx(&stdb);\n\n        // Read all of st_client, assert that there's only one row, and that said row is `row_1`.\n        let present_rows: Vec<StClientRow> = stdb\n            .iter(&read_tx, ST_CLIENT_ID)\n            .unwrap()\n            .map(|row_ref| row_ref.try_into().unwrap())\n            .collect();\n        assert_eq!(present_rows.len(), 1);\n        assert_eq!(present_rows[0], row_1);\n\n        let _ = stdb.release_tx(read_tx);\n    }\n\n    // Verify that we can compress snapshots and hardlink them,\n    // except for the last one, which should be uncompressed.\n    // Then, verify that we can read the compressed snapshot.\n    //\n    // NOTE: `snapshot_watching_compressor` is what filter out the last snapshot\n    #[test]\n    fn compress_snapshot_test() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n\n        let mut tx = begin_mut_tx(&stdb);\n        let schema = my_table(AlgebraicType::I32);\n        let table_id = stdb.create_table(&mut tx, schema)?;\n        for v in 0..3 {\n            insert(&stdb, &mut tx, table_id, &product![v])?;\n        }\n        stdb.commit_tx(tx)?;\n\n        let root = stdb.path().unwrap().snapshots();\n        let (dir, repo) = make_snapshot(root.clone(), Identity::ZERO, 0, CompressType::None, true);\n        stdb.take_snapshot(&repo)?;\n\n        #[cfg(unix)]\n        let total_objects = repo.size_on_disk()?.object_count;\n        // Another snapshots that will hardlink part of the first one\n        for i in 0..2 {\n            let mut tx = begin_mut_tx(&stdb);\n            for v in 0..(10 + i) {\n                insert(&stdb, &mut tx, table_id, &product![v])?;\n            }\n            stdb.commit_tx(tx)?;\n            stdb.take_snapshot(&repo)?;\n        }\n\n        let size_compress_off = repo.size_on_disk()?;\n        assert!(\n            size_compress_off.total_size > 0,\n            \"Snapshot size should be greater than 0\"\n        );\n        let mut offsets = repo.all_snapshots()?.collect::<Vec<_>>();\n        offsets.sort();\n        assert_eq!(&offsets, &[1, 2, 3]);\n        // Simulate we take except the last snapshot\n        let last_compress = 2;\n        let mut stats = CompressionStats::default();\n        repo.compress_snapshots(&mut stats, ..3)?;\n        assert_eq!(stats.compressed(), 2);\n        let size_compress_on = repo.size_on_disk()?;\n        assert!(size_compress_on.total_size < size_compress_off.total_size);\n        // Verify we hard-linked the second snapshot\n        #[cfg(unix)]\n        {\n            use std::os::unix::fs::MetadataExt;\n            let snapshot_dir = dir.snapshot_dir(last_compress);\n            let mut hard_linked_on = 0;\n            let mut hard_linked_off = 0;\n\n            let (snapshot, compress) = Snapshot::read_from_file(&snapshot_dir.snapshot_file(last_compress))?;\n            assert!(compress.is_compressed());\n            let repo = SnapshotRepository::object_repo(&snapshot_dir)?;\n            for (_, path) in snapshot.files(&repo) {\n                match path.metadata()?.nlink() {\n                    0 => hard_linked_off += 1,\n                    _ => hard_linked_on += 1,\n                }\n            }\n            assert_eq!(hard_linked_on, total_objects);\n            assert_eq!(hard_linked_off, 0);\n        }\n\n        // Sanity check that we can read the snapshot after compression\n        let repo = open_snapshot_repo(dir, Identity::ZERO, 0)?;\n        RelationalDB::restore_from_snapshot_or_bootstrap(\n            Identity::ZERO,\n            Some(&repo),\n            Some(last_compress),\n            0,\n            PagePool::new_for_test(),\n        )?;\n\n        Ok(())\n    }\n\n    // For test compression into an existing database.\n    // Must supply the path to the database and the identity of the replica using the `ENV`:\n    // - `SNAPSHOT` the path to the database, like `/tmp/db/replicas/.../8/database`\n    // - `IDENTITY` the identity in hex format\n    #[tokio::test]\n    #[ignore]\n    async fn read_existing() -> ResultTest<()> {\n        let path_db = PathBuf::from(std::env::var(\"SNAPSHOT\").expect(\"SNAPSHOT must be set to a valid path\"));\n        let identity =\n            Identity::from_hex(std::env::var(\"IDENTITY\").expect(\"IDENTITY must be set to a valid hex identity\"))?;\n        let path = ReplicaDir::from_path_unchecked(path_db);\n\n        let repo = open_snapshot_repo(path.snapshots(), Identity::ZERO, 0)?;\n        assert!(\n            repo.size_on_disk()?.total_size > 0,\n            \"Snapshot size should be greater than 0\"\n        );\n\n        let last = repo.latest_snapshot()?;\n        let stdb =\n            RelationalDB::restore_from_snapshot_or_bootstrap(identity, Some(&repo), last, 0, PagePool::new_for_test())?;\n\n        let out = TempDir::with_prefix(\"snapshot_test\")?;\n        let dir = SnapshotsPath::from_path_unchecked(out.path());\n\n        let (_, repo) = make_snapshot(dir.clone(), Identity::ZERO, 0, CompressType::zstd(), false);\n\n        stdb.take_snapshot(&repo)?;\n        let size = repo.size_on_disk()?;\n        assert!(size.total_size > 0, \"Snapshot size should be greater than 0\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn tries_older_snapshots() -> ResultTest<()> {\n        let stdb = TestDB::durable()?;\n        let snapshots_path = stdb.path().unwrap().snapshots();\n        snapshots_path.create()?;\n        let repo = SnapshotRepository::open(snapshots_path, stdb.database_identity(), 85)?;\n\n        // Ensure that an initial snapshot exists,\n        // but ignore if the snapshot worker won the race.\n        match stdb.take_snapshot(&repo) {\n            Err(e) => {\n                if !matches!(&e, DBError::Datastore(DatastoreError::Snapshot(e)) if e.is_already_exists()) {\n                    panic!(\"failed to create snapshot: {e:#}\");\n                }\n            }\n            Ok(out) => out.map(drop).expect(\"snapshot not created: empty committed state\"),\n        };\n        // Insert some data and take another snapshot.\n        {\n            let mut tx = stdb.begin_mut_tx(IsolationLevel::Serializable, Workload::ForTests);\n            let schema = my_table(AlgebraicType::I32);\n            let table_id = stdb.create_table(&mut tx, schema)?;\n            for v in 0..3 {\n                insert(&stdb, &mut tx, table_id, &product![v])?;\n            }\n            stdb.commit_tx(tx)?;\n        }\n        stdb.take_snapshot(&repo)?.expect(\"failed to take snapshot\");\n\n        let try_restore = |durable_tx_offset, min_commitlog_offset| {\n            RelationalDB::restore_from_snapshot_or_bootstrap(\n                stdb.database_identity(),\n                Some(&repo),\n                Some(durable_tx_offset),\n                min_commitlog_offset,\n                PagePool::new_for_test(),\n            )\n        };\n\n        try_restore(1, 0)?;\n        // We can restore from the previous snapshot\n        // if the snapshot file is corrupted\n        repo.snapshot_dir_path(1)\n            .snapshot_file(1)\n            .open_file(OpenOptions::new().write(true))?\n            .set_len(1)?;\n        try_restore(1, 0)?;\n        // Also if it's gone\n        std::fs::remove_file(repo.snapshot_dir_path(1).snapshot_file(1))?;\n        try_restore(1, 0)?;\n        // But not if the commitlog starts after the previous snapshot\n        assert_matches!(\n            try_restore(1, 2).map(drop),\n            Err(RestoreSnapshotError::NoConnectedSnapshot { .. })\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    /// Test that we can create a table after replaying a durable database\n    /// without a snapshot.\n    ///\n    /// Regression test for\n    /// [SpacetimeDB issue #2758](https://github.com/clockworklabs/SpacetimeDB/issues/2758).\n    /// Before [the fix](https://github.com/clockworklabs/SpacetimeDB/pull/2760),\n    /// this would fail because the sequence allocations for system table sequences,\n    /// including the one on `st_table.table_id`,\n    /// were not correctly reinitialized in memory after replaying a commitlog\n    /// into a database that had been [`Locking::bootstrap`]ped,\n    /// as opposed to restored from a snapshot.\n    fn repro_2758_create_table_after_replay_without_snapshot() {\n        // Create a new database which has an on-disk commitlog but no snapshots.\n        let stdb = TestDB::durable_without_snapshot_repo().expect(\"failed to create TestDB\");\n\n        // Begin a transaction, create a table, then commit.\n        let mut tx = begin_mut_tx(&stdb);\n        let product_type = ProductType::from([(\"col_0\", AlgebraicType::I32)]);\n        let schema = table(\"table_0\", product_type.clone(), |builder| builder);\n        let table_0_id = stdb.create_table(&mut tx, schema).unwrap();\n        stdb.commit_tx(tx).unwrap();\n\n        // At this point, the sequence on `st_table.table_id` will have allocated once and advanced once,\n        // i.e. have `allocated = 8193`, `value = 4198`.\n\n        // Reopen the database. This will replay all the way from the commitlog,\n        // since we have no snapshot repository.\n        let stdb = stdb.reopen().unwrap();\n\n        // Begin a transaction, create another table, then commit.\n        let mut tx = begin_mut_tx(&stdb);\n        let other_schema = table(\"table_1\", product_type.clone(), |builder| builder);\n        // Before the fix to issue #2758,\n        // this next call to `create_table` would fail with:\n        // ```\n        // called `Result::unwrap()` on an `Err` value: Index(UniqueConstraintViolation(UniqueConstraintViolation { constraint_name: \"st_table_table_id_idx_btree\", table_name: \"st_table\", cols: [\"table_id\"], value: U32(4097) }))\n        // ```\n        let table_1_id = stdb.create_table(&mut tx, other_schema).unwrap();\n        stdb.commit_tx(tx).unwrap();\n\n        // Quick sanity check: we actually got different table IDs,\n        // we didn't just fail to detect a unique constraint violation.\n        assert!(table_1_id > table_0_id);\n    }\n\n    use crate::error::{DBError, DatabaseError};\n    use fs_extra::dir::{copy, CopyOptions};\n    use itertools::Itertools;\n\n    /// Make a temp dir with the contents of the given directory.\n    fn copy_fixture_dir(src: &PathBuf) -> TempDir {\n        // Create a temporary directory\n        let tempdir = TempDir::new().expect(\"create temp dir\");\n\n        // Copy everything from src into tempdir\n        let options = CopyOptions {\n            overwrite: true,\n            copy_inside: true, // copy the contents, not the directory itself\n            ..Default::default()\n        };\n        copy(src, tempdir.path(), &options).expect(\"copy fixtures\");\n\n        tempdir\n    }\n\n    /// Load a v1.2 database, verify that the expected tables are present, and we can add a table.\n    fn load_1_2_data(use_snapshot: bool) -> ResultTest<()> {\n        let data_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\")).join(\"testdata/v1.2/replicas\");\n        let tempdir = copy_fixture_dir(&data_dir);\n\n        let dir = ReplicaDir::from_path_unchecked(tempdir.path().join(\"replicas/22000001\"));\n\n        let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;\n        // Enter the runtime so that `Self::durable_internal` can spawn a `SnapshotWorker`.\n        let _rt = rt.enter();\n\n        let db_identity = Identity::from_hex(\"c2006c1c2ede44b44e9835dfe00e3e1edc3bc67024b504cb6a3b8491db5d98a3\")?;\n        let owner_identity = Identity::from_hex(\"c2001c551217385bdeba5f1e1b5b19a28910852a94ce43428508e7a098760cea\")?;\n        let (db, _durability_handle) = TestDB::open_existing_durable(\n            &dir,\n            rt.handle().clone(),\n            22000001,\n            db_identity,\n            owner_identity,\n            use_snapshot,\n        )?;\n        let mut tx = db.begin_mut_tx(IsolationLevel::Serializable, Workload::ForTests);\n        let schemas = db.get_all_tables_mut(&tx)?;\n        let user_table_names: Vec<String> = schemas\n            .iter()\n            .filter(|s| s.table_id.0 > 4000)\n            .sorted_by_key(|s| s.table_id.0)\n            .map(|s| s.table_name.clone().to_string())\n            .collect();\n        let expected_table_names = vec![\"message\", \"user\"];\n        assert_eq!(user_table_names, expected_table_names);\n\n        let table_id = db.create_table(&mut tx, my_table(AlgebraicType::I32))?;\n        db.commit_tx(tx)?;\n\n        let tx = db.begin_mut_tx(IsolationLevel::Serializable, Workload::ForTests);\n        let t_id = db.table_id_from_name_mut(&tx, \"MyTable\")?.unwrap();\n        assert_eq!(table_id, t_id);\n        assert_eq!(\"MyTable\", db.table_name_from_id_mut(&tx, t_id)?.unwrap());\n        db.commit_tx(tx)?;\n        let tx = db.begin_tx(Workload::ForTests);\n        let user_table_names: Vec<String> = db\n            .get_all_tables(&tx)?\n            .iter()\n            .filter(|s| s.table_id.0 > 4000)\n            .sorted_by_key(|s| s.table_id.0)\n            .map(|s| s.table_name.clone().to_string())\n            .collect();\n        let expected_table_names = vec![\"message\", \"user\", \"MyTable\"];\n        assert_eq!(user_table_names, expected_table_names);\n\n        Ok(())\n    }\n\n    #[test]\n    fn load_1_2_quickstart_from_snapshot_test() -> ResultTest<()> {\n        load_1_2_data(true)\n    }\n\n    #[test]\n    fn load_1_2_quickstart_without_snapshot_test() -> ResultTest<()> {\n        load_1_2_data(false)\n    }\n\n    /// This tests adding a new system table st_connection_credentials, which was not in 1.2.\n    /// We ensure that we can open data from a version without it, write to the table,\n    /// and then reopen the database and read from the table.\n    fn load_1_2_data_and_migrate(use_snapshot: bool) -> ResultTest<()> {\n        let data_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\")).join(\"testdata/v1.2/replicas\");\n        let tempdir = copy_fixture_dir(&data_dir);\n\n        let dir = ReplicaDir::from_path_unchecked(tempdir.path().join(\"replicas/22000001\"));\n\n        let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;\n        // Enter the runtime so that `Self::durable_internal` can spawn a `SnapshotWorker`.\n        let _rt = rt.enter();\n\n        let db_identity = Identity::from_hex(\"c2006c1c2ede44b44e9835dfe00e3e1edc3bc67024b504cb6a3b8491db5d98a3\")?;\n        let owner_identity = Identity::from_hex(\"c2001c551217385bdeba5f1e1b5b19a28910852a94ce43428508e7a098760cea\")?;\n        let (db, _) = TestDB::open_existing_durable(\n            &dir,\n            rt.handle().clone(),\n            22000001,\n            db_identity,\n            owner_identity,\n            use_snapshot,\n        )?;\n        let schemas = db.with_read_only(Workload::ForTests, |tx| db.get_all_tables(tx))?;\n        let user_table_names: Vec<String> = schemas\n            .iter()\n            .filter(|s| s.table_id.0 > 4000)\n            .sorted_by_key(|s| s.table_id.0)\n            .map(|s| s.table_name.clone().to_string())\n            .collect();\n        let expected_table_names = vec![\"message\", \"user\"];\n        // We have this assertion just to double check that we aren't accidentally opening an empty\n        // directory.\n        assert_eq!(user_table_names, expected_table_names);\n\n        db.with_auto_commit(Workload::ForTests, |tx| {\n            tx.insert_st_client(Identity::ZERO, ConnectionId::ZERO, \"invalid_jwt\")\n                .unwrap();\n            Ok::<(), DBError>(())\n        })?;\n        // Now we are going to shut it down and reopen it, to ensure that the new table can be\n        // read from disk.\n        rt.block_on(db.shutdown());\n        drop(db);\n\n        let (db, _) = TestDB::open_existing_durable(\n            &dir,\n            rt.handle().clone(),\n            22000001,\n            db_identity,\n            owner_identity,\n            use_snapshot,\n        )?;\n\n        db.with_read_only(Workload::ForTests, |tx| {\n            let jwt = tx.get_jwt_payload(ConnectionId::ZERO).unwrap().unwrap();\n            assert_eq!(jwt, \"invalid_jwt\");\n            Ok::<(), DBError>(())\n        })?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn load_1_2_data_and_migrate_with_snapshot() -> ResultTest<()> {\n        load_1_2_data_and_migrate(true)\n    }\n\n    #[test]\n    fn load_1_2_data_and_migrate_without_snapshot() -> ResultTest<()> {\n        load_1_2_data_and_migrate(false)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/db/snapshot.rs",
    "content": "// Creates very long lines that are not very readable.\n#![allow(clippy::uninlined_format_args)]\n\nuse std::{\n    sync::{Arc, Weak},\n    time::Duration,\n};\n\nuse anyhow::Context as _;\nuse futures::{channel::mpsc, StreamExt as _};\nuse log::{info, warn};\nuse parking_lot::RwLock;\nuse prometheus::{Histogram, IntGauge};\nuse spacetimedb_datastore::locking_tx_datastore::{committed_state::CommittedState, datastore::Locking};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_snapshot::{CompressionStats, SnapshotRepository};\nuse tokio::sync::watch;\n\nuse crate::{util::asyncify, worker_metrics::WORKER_METRICS};\n\npub type SnapshotDatabaseState = Arc<RwLock<CommittedState>>;\n\n/// Whether the [SnapshotWorker] should compress historical snapshots.\n#[derive(Clone, Copy, Debug)]\npub enum Compression {\n    Enabled,\n    Disabled,\n}\n\nimpl Compression {\n    pub fn is_enabled(&self) -> bool {\n        matches!(self, Self::Enabled)\n    }\n}\n\n/// Represents a handle to a background task that takes snapshots of a\n/// [SnapshotDatabaseState] and stores them on disk.\n///\n/// A snapshot can be [requested][Self::request_snapshot] and will be taken when\n/// the background task gets scheduled and can acquire a read lock on the\n/// database state, i.e. it happens at some point in the future.\n///\n/// If the worker was created with [Compression::Enabled], it will compress\n/// snapshots older than the latest one. Compression errors are logged, but do\n/// not prevent the creation of new snapshots.\n///\n/// Whenever a snapshot is complete, its [TxOffset] is published to a channel,\n/// to which one can [subscribe][Self::subscribe].\n///\n/// The [SnapshotWorker] handle is freely cloneable, so ownership can be shared\n/// between the database and control code.\n///\n/// It is possible to re-use a [SnapshotWorker] to create a new database\n/// instance: when passed to [super::relational_db::RelationalDB::open], the\n/// worker's [SnapshotDatabaseState] will be replaced with the database's.\n/// We use this for replicated databases when transitioning between the leader and follower states,\n/// to preserve event subscriptions on the `SnapshotWorker`'s `snapshot_created` channel.\n#[derive(Clone)]\npub struct SnapshotWorker {\n    snapshot_created: watch::Sender<TxOffset>,\n    request_snapshot: mpsc::UnboundedSender<Request>,\n    snapshot_repository: Arc<SnapshotRepository>,\n}\n\nimpl SnapshotWorker {\n    /// Create a new [SnapshotWorker].\n    ///\n    /// The handle is only partially initialized, as it is lacking the\n    /// [SnapshotDatabaseState]. This allows control code to [Self::subscribe]\n    /// to future snapshots before handing off the worker to the database.\n    pub fn new(snapshot_repository: Arc<SnapshotRepository>, compression: Compression) -> Self {\n        let database = snapshot_repository.database_identity();\n        let latest_snapshot = snapshot_repository.latest_snapshot().ok().flatten().unwrap_or(0);\n        let (snapshot_created, _) = watch::channel(latest_snapshot);\n        let (request_tx, request_rx) = mpsc::unbounded();\n\n        let actor = SnapshotWorkerActor {\n            snapshot_requests: request_rx,\n            snapshot_repo: snapshot_repository.clone(),\n            snapshot_created: snapshot_created.clone(),\n            metrics: SnapshotMetrics::new(database),\n            compression: compression.is_enabled().then(|| Compressor {\n                snapshot_repo: snapshot_repository.clone(),\n                metrics: CompressionMetrics::new(database),\n                stats: <_>::default(),\n            }),\n        };\n        tokio::spawn(actor.run());\n\n        Self {\n            snapshot_created,\n            request_snapshot: request_tx,\n            snapshot_repository,\n        }\n    }\n\n    /// Finish the initialization of [Self] by passing a [SnapshotDatabaseState],\n    /// or replace the current [SnapshotDatabaseState] with a new one.\n    ///\n    /// This is called during construction of a [super::relational_db::RelationalDB].\n    pub(crate) fn set_state(&self, state: SnapshotDatabaseState) {\n        self.request_snapshot\n            .unbounded_send(Request::ReplaceState(state))\n            .expect(\"snapshot worker panicked\");\n    }\n\n    /// Get the [SnapshotRepository] this worker is operating on.\n    pub fn repo(&self) -> &SnapshotRepository {\n        &self.snapshot_repository\n    }\n\n    /// Request a snapshot to be taken.\n    ///\n    /// The snapshot will be taken at some point in the future.\n    /// The request is dropped if the handle is not yet fully initialized.\n    ///\n    /// Panics if the snapshot worker has closed the receive end of its queue(s),\n    /// which is likely due to it having panicked.\n    pub fn request_snapshot(&self) {\n        self.request_snapshot\n            .unbounded_send(Request::TakeSnapshot)\n            .expect(\"snapshot worker panicked\");\n    }\n\n    /// Like [`Self::request_snapshot`], but doesn't propogate panics from the worker.\n    ///\n    /// Used by the durability to request snapshots on commitlog segment rotation,\n    /// since the durability should continue writing queued TXes even if the snapshot worker panics.\n    pub fn request_snapshot_ignore_closed(&self) {\n        let _ = self.request_snapshot.unbounded_send(Request::TakeSnapshot);\n    }\n\n    /// Subscribe to the [TxOffset]s of snapshots created by this worker.\n    ///\n    /// Note that the returned [`watch::Receiver`] only stores the most recent\n    /// snapshot offset, but can be turned into a [`futures::Stream`] using the\n    /// `WatchStream` from the `tokio-stream` crate.\n    pub fn subscribe(&self) -> watch::Receiver<TxOffset> {\n        self.snapshot_created.subscribe()\n    }\n}\n\nstruct SnapshotMetrics {\n    snapshot_timing_total: Histogram,\n    snapshot_timing_inner: Histogram,\n}\n\nimpl SnapshotMetrics {\n    fn new(db: Identity) -> Self {\n        Self {\n            snapshot_timing_total: WORKER_METRICS.snapshot_creation_time_total.with_label_values(&db),\n            snapshot_timing_inner: WORKER_METRICS.snapshot_creation_time_inner.with_label_values(&db),\n        }\n    }\n}\n\ntype WeakDatabaseState = Weak<RwLock<CommittedState>>;\n\nenum Request {\n    TakeSnapshot,\n    ReplaceState(SnapshotDatabaseState),\n}\n\nstruct SnapshotWorkerActor {\n    snapshot_requests: mpsc::UnboundedReceiver<Request>,\n    snapshot_repo: Arc<SnapshotRepository>,\n    snapshot_created: watch::Sender<TxOffset>,\n    metrics: SnapshotMetrics,\n    compression: Option<Compressor>,\n}\n\nimpl SnapshotWorkerActor {\n    /// Read messages from `snapshot_requests` indefinitely.\n    ///\n    /// For each [Request::TakeSnapshot] message, a snapshot of `database_state`\n    /// is taken. The offset of each successfully created snapshot is sent to\n    /// the `snapshot_created` channel.\n    ///\n    /// If compression is enabled, it is run after successful creation of a\n    /// snapshot.\n    ///\n    /// The `snapshot_created` message is sent _after_ the compression pass\n    /// finished (yet regardless of its success). Downstream tasks can thus\n    /// expect that any locks on (valid) snapshots have been released when the\n    /// message is received, unless a new snapshot request is already being\n    /// processed.\n    async fn run(mut self) {\n        let database_identity = self.snapshot_repo.database_identity();\n        let mut database_state: Option<WeakDatabaseState> = None;\n        while let Some(req) = self.snapshot_requests.next().await {\n            match req {\n                Request::TakeSnapshot => {\n                    let res = self\n                        .maybe_take_snapshot(database_state.as_ref())\n                        .await\n                        .inspect_err(|e| warn!(\"database={database_identity} SnapshotWorker: {e:#}\"));\n                    if let Ok(snapshot_offset) = res {\n                        self.maybe_compress_snapshots(snapshot_offset).await;\n                        self.snapshot_created.send_replace(snapshot_offset);\n                    }\n                }\n                Request::ReplaceState(new_state) => {\n                    database_state = Some(Arc::downgrade(&new_state));\n                }\n            }\n        }\n    }\n\n    async fn maybe_take_snapshot(&self, state: Option<&WeakDatabaseState>) -> anyhow::Result<TxOffset> {\n        let state = state.context(\"database state not set, call `SnapshotWorker::set_state`\")?;\n        let state = Weak::upgrade(state).context(\"database state is already dropped\")?;\n        self.take_snapshot(state).await\n    }\n\n    async fn take_snapshot(&self, state: SnapshotDatabaseState) -> anyhow::Result<TxOffset> {\n        let timer = self.metrics.snapshot_timing_total.start_timer();\n        let inner_timer = self.metrics.snapshot_timing_inner.clone();\n\n        let snapshot_repo = self.snapshot_repo.clone();\n\n        let database_identity = self.snapshot_repo.database_identity();\n\n        let maybe_offset = asyncify(move || {\n            let _timer = inner_timer.start_timer();\n            Locking::take_snapshot_internal(&state, &snapshot_repo)\n        })\n        .await\n        .with_context(|| format!(\"error capturing snapshot of database {}\", database_identity))?;\n        maybe_offset\n            .map(|(offset, _path)| offset)\n            .inspect(|snapshot_offset| {\n                let elapsed = Duration::from_secs_f64(timer.stop_and_record());\n                info!(\n                    \"Captured snapshot of database {} at TX offset {} in {:?}\",\n                    database_identity, snapshot_offset, elapsed,\n                );\n            })\n            .with_context(|| {\n                format!(\n                    \"refusing to take snapshot of database {} at TX offset -1\",\n                    database_identity\n                )\n            })\n    }\n\n    async fn maybe_compress_snapshots(&mut self, latest_snapshot: TxOffset) {\n        if let Some(compressor) = self.compression.as_mut() {\n            compressor.compress_snapshots(latest_snapshot).await\n        }\n    }\n}\n\nstruct CompressionMetrics {\n    timing_total: Histogram,\n    timing_inner: Histogram,\n    timing_single: Histogram,\n    skipped: IntGauge,\n    compressed: IntGauge,\n    objects_compressed: IntGauge,\n    objects_hardlinked: IntGauge,\n}\n\nimpl CompressionMetrics {\n    fn new(db: Identity) -> Self {\n        Self {\n            timing_total: WORKER_METRICS.snapshot_compression_time_total.with_label_values(&db),\n            timing_inner: WORKER_METRICS.snapshot_compression_time_inner.with_label_values(&db),\n            timing_single: WORKER_METRICS.snapshot_compression_time_single.with_label_values(&db),\n            skipped: WORKER_METRICS.snapshot_compression_skipped.with_label_values(&db),\n            compressed: WORKER_METRICS.snapshot_compression_compressed.with_label_values(&db),\n            objects_compressed: WORKER_METRICS\n                .snapshot_compression_objects_compressed\n                .with_label_values(&db),\n            objects_hardlinked: WORKER_METRICS\n                .snapshot_compression_objects_hardlinked\n                .with_label_values(&db),\n        }\n    }\n\n    fn report_and_reset(\n        &self,\n        CompressionStats {\n            skipped,\n            compression_timings,\n            objects,\n            // Don't reset `last_compressed`, we need it for the next run.\n            last_compressed: _,\n        }: &mut CompressionStats,\n    ) {\n        self.skipped.set(*skipped as _);\n        *skipped = 0;\n\n        self.compressed.set(compression_timings.len() as _);\n        for duration in compression_timings.drain(..) {\n            self.timing_single.observe(duration.as_secs_f64());\n        }\n\n        self.objects_compressed.set(objects.compressed as _);\n        self.objects_hardlinked.set(objects.hardlinked as _);\n        objects.reset();\n    }\n}\n\nstruct Compressor {\n    snapshot_repo: Arc<SnapshotRepository>,\n    metrics: CompressionMetrics,\n    stats: Option<CompressionStats>,\n}\n\nimpl Compressor {\n    /// Traverse the snapshots in `self.snapshot_repository` up to and excluding\n    /// `latest_snapshot` and compress all snapshots that are not yet compressed.\n    ///\n    /// Processes the snapshots in ascending order and stops when an error\n    /// occurs.\n    ///\n    /// The first invocation on this [Compressor] instance will traverse all\n    /// snapshots, i.e. the range `..latest_snapshot`.\n    /// The latest compressed snapshot is stored internally, so subsequent\n    /// invocations will visit `(last_compressed + 1)..latest_snapshot`.\n    async fn compress_snapshots(&mut self, latest_snapshot: TxOffset) {\n        let timer = self.metrics.timing_total.start_timer();\n        let inner_timer = self.metrics.timing_inner.clone();\n\n        let snapshot_repo = self.snapshot_repo.clone();\n        let database_identity = snapshot_repo.database_identity();\n\n        let start = self\n            .stats\n            .as_ref()\n            .and_then(|stats| stats.last_compressed)\n            // If last compressed is `Some`, exclude it from the range.\n            .map(|last_compressed| last_compressed + 1)\n            // Otherwise, start at zero.\n            .unwrap_or_default();\n        let range = start..latest_snapshot;\n        let mut stats = self.stats.take().unwrap_or_default();\n\n        let (mut stats, res) = asyncify({\n            let range = range.clone();\n            move || {\n                let _timer = inner_timer.start_timer();\n                let res = snapshot_repo.compress_snapshots(&mut stats, range);\n                (stats, res)\n            }\n        })\n        .await;\n        let elapsed = Duration::from_secs_f64(timer.stop_and_record());\n        self.metrics.report_and_reset(&mut stats);\n        // Store stats for reuse.\n        // `stats.last_compressed` is unchanged,\n        // we'll use it as the range start in the next invocation.\n        self.stats = Some(stats);\n\n        if let Err(e) = res {\n            warn!(\n                \"Error compressing snapshot range {:?} of database {}: {:#}\",\n                range, database_identity, e\n            );\n        } else {\n            info!(\n                \"Compressed snapshot range {:?} of database {} in {:?}\",\n                range, database_identity, elapsed\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/db/update.rs",
    "content": "use super::relational_db::RelationalDB;\nuse crate::database_logger::SystemLogger;\nuse crate::sql::parser::RowLevelExpr;\nuse spacetimedb_datastore::locking_tx_datastore::MutTxId;\nuse spacetimedb_lib::db::auth::StTableType;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_lib::AlgebraicValue;\nuse spacetimedb_primitives::{ColSet, TableId};\nuse spacetimedb_schema::auto_migrate::{AutoMigratePlan, ManualMigratePlan, MigratePlan};\nuse spacetimedb_schema::def::{TableDef, ViewDef};\nuse spacetimedb_schema::schema::{column_schemas_from_defs, IndexSchema, Schema, SequenceSchema, TableSchema};\n\n/// The logger used for by [`update_database`] and friends.\npub trait UpdateLogger {\n    fn info(&self, msg: &str);\n}\n\nimpl UpdateLogger for SystemLogger {\n    fn info(&self, msg: &str) {\n        self.info(msg);\n    }\n}\n\n/// The result of a database update.\n/// Indicates whether clients should be disconnected when the update is complete.\n#[must_use]\npub enum UpdateResult {\n    Success,\n    RequiresClientDisconnect,\n    EvaluateSubscribedViews,\n}\n\n/// Update the database according to the migration plan.\n///\n/// The update is performed within the transactional context `tx`.\n// NOTE: Manual migration support is predicated on the transactionality of\n// dropping database objects (tables, indexes, etc.).\n// Currently, none of the drop_* methods are transactional.\n// This is safe because the __update__ reducer is no longer supported,\n// and the auto plan guarantees that the migration can't fail.\n// But when implementing manual migrations, we need to make sure that\n// drop_* become transactional.\npub fn update_database(\n    stdb: &RelationalDB,\n    tx: &mut MutTxId,\n    auth_ctx: AuthCtx,\n    plan: MigratePlan,\n    logger: &dyn UpdateLogger,\n) -> anyhow::Result<UpdateResult> {\n    let existing_tables = stdb.get_all_tables_mut(tx)?;\n\n    // TODO: consider using `ErrorStream` here.\n    let old_module_def = plan.old_def();\n    for table in existing_tables\n        .iter()\n        .filter(|table| table.table_type != StTableType::System && !table.is_view())\n    {\n        let old_def = old_module_def\n            .table(&table.table_name[..])\n            .ok_or_else(|| anyhow::anyhow!(\"table {} not found in old_module_def\", table.table_name))?;\n\n        table.check_compatible(old_module_def, old_def)?;\n    }\n\n    match plan {\n        MigratePlan::Manual(plan) => manual_migrate_database(stdb, tx, plan, logger),\n        MigratePlan::Auto(plan) => auto_migrate_database(stdb, tx, auth_ctx, plan, logger),\n    }\n}\n\n/// Manually migrate a database.\nfn manual_migrate_database(\n    _stdb: &RelationalDB,\n    _tx: &mut MutTxId,\n    _plan: ManualMigratePlan,\n    _logger: &dyn UpdateLogger,\n) -> anyhow::Result<UpdateResult> {\n    unimplemented!(\"Manual database migrations are not yet implemented\")\n}\n\n/// Logs with `info` level to `$logger` as well as via the `log` crate.\nmacro_rules! log {\n    ($logger:expr, $($tokens:tt)*) => {\n        $logger.info(&format!($($tokens)*));\n        log::info!($($tokens)*);\n    };\n}\n\n/// Automatically migrate a database.\nfn auto_migrate_database(\n    stdb: &RelationalDB,\n    tx: &mut MutTxId,\n    auth_ctx: AuthCtx,\n    plan: AutoMigratePlan,\n    logger: &dyn UpdateLogger,\n) -> anyhow::Result<UpdateResult> {\n    log::info!(\"Running database update prechecks: {}\", stdb.database_identity());\n    // We used to memoize all table schemas upfront, which cause issue #3441.\n    // Schema should be queries only when needed to ensure that any schema changes made during earlier migration steps are visible\n    // to later steps.\n\n    for precheck in plan.prechecks {\n        match precheck {\n            spacetimedb_schema::auto_migrate::AutoMigratePrecheck::CheckAddSequenceRangeValid(sequence_name) => {\n                let table_def = plan.new.stored_in_table_def(sequence_name).unwrap();\n                let sequence_def = &table_def.sequences[sequence_name];\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n\n                let ty = table_def\n                    .get_column(sequence_def.column)\n                    .ok_or_else(|| {\n                        anyhow::anyhow!(\"Precheck failed: added sequence {sequence_name} refers to unknown column\")\n                    })?\n                    .ty\n                    .clone();\n\n                // Convert `SequenceDef` min/max to `AlgebraicValue`s of the correct type.\n                let min = AlgebraicValue::from_i128(&ty, sequence_def.min_value.unwrap_or(1)).ok_or_else(|| {\n                    anyhow::anyhow!(\"Precheck failed: added sequence {sequence_name} has invalid min value\")\n                })?;\n\n                let max =\n                    AlgebraicValue::from_i128(&ty, sequence_def.max_value.unwrap_or(i128::MAX)).ok_or_else(|| {\n                        anyhow::anyhow!(\"Precheck failed: added sequence {sequence_name} has invalid max value\")\n                    })?;\n\n                let range = min..max;\n                if stdb\n                    .iter_by_col_range_mut(tx, table_id, sequence_def.column, range)?\n                    .next()\n                    .is_some()\n                {\n                    anyhow::bail!(\"Precheck failed: added sequence {sequence_name} already has values in range\",);\n                }\n            }\n        }\n    }\n\n    log::info!(\"Running database update steps: {}\", stdb.database_identity());\n    let mut res = UpdateResult::Success;\n\n    for step in plan.steps {\n        match step {\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddTable(table_name) => {\n                let table_def: &TableDef = plan.new.expect_lookup(table_name);\n\n                // Recursively sets IDs to 0.\n                // They will be initialized by the database when the table is created.\n                let table_schema = TableSchema::from_module_def(plan.new, table_def, (), TableId::SENTINEL);\n\n                log!(logger, \"Creating table `{table_name}`\");\n\n                stdb.create_table(tx, table_schema)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddView(view_name) => {\n                let view_def: &ViewDef = plan.new.expect_lookup(view_name);\n                stdb.create_view(tx, plan.new, view_def)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveView(view_name) => {\n                let view_id = stdb.view_id_from_name_mut(tx, view_name)?.unwrap();\n                stdb.drop_view(tx, view_id)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::UpdateView(_) => {\n                // if we already have to disconnect clients, no need to set\n                // `EvaluateSubscribedViews` as clients will be disconnected anyway\n                if !matches!(res, UpdateResult::RequiresClientDisconnect) {\n                    res = UpdateResult::EvaluateSubscribedViews;\n                }\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddIndex(index_name) => {\n                let table_def = plan.new.stored_in_table_def(index_name).unwrap();\n                let index_def = table_def.indexes.get(index_name).unwrap();\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n\n                let index_cols = ColSet::from(index_def.algorithm.columns());\n\n                let is_unique = table_def\n                    .constraints\n                    .iter()\n                    .filter_map(|(_, c)| c.data.unique_columns())\n                    .any(|unique_cols| unique_cols == &index_cols);\n\n                log!(logger, \"Creating index `{}` on table `{}`\", index_name, table_def.name);\n\n                let index_schema = IndexSchema::from_module_def(plan.new, index_def, table_id, 0.into());\n\n                stdb.create_index(tx, index_schema, is_unique)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveIndex(index_name) => {\n                let table_def = plan.old.stored_in_table_def(index_name).unwrap();\n\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n                let table_schema = stdb.schema_for_table_mut(tx, table_id)?;\n\n                let index_schema = table_schema\n                    .indexes\n                    .iter()\n                    .find(|index| index.index_name[..] == index_name[..])\n                    .unwrap();\n\n                log!(logger, \"Dropping index `{}` on table `{}`\", index_name, table_def.name);\n                stdb.drop_index(tx, index_schema.index_id)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveConstraint(constraint_name) => {\n                let table_def = plan.old.stored_in_table_def(constraint_name).unwrap();\n\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n                let table_schema = stdb.schema_for_table_mut(tx, table_id)?;\n                let constraint_schema = table_schema\n                    .constraints\n                    .iter()\n                    .find(|constraint| constraint.constraint_name[..] == constraint_name[..])\n                    .unwrap();\n\n                log!(\n                    logger,\n                    \"Dropping constraint `{}` on table `{}`\",\n                    constraint_name,\n                    table_def.name\n                );\n                stdb.drop_constraint(tx, constraint_schema.constraint_id)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddSequence(sequence_name) => {\n                let table_def = plan.new.stored_in_table_def(sequence_name).unwrap();\n                let sequence_def = table_def.sequences.get(sequence_name).unwrap();\n\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n                let table_schema = stdb.schema_for_table_mut(tx, table_id)?;\n\n                log!(\n                    logger,\n                    \"Adding sequence `{}` to table `{}`\",\n                    sequence_name,\n                    table_def.name\n                );\n                let sequence_schema =\n                    SequenceSchema::from_module_def(plan.new, sequence_def, table_schema.table_id, 0.into());\n                stdb.create_sequence(tx, sequence_schema)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveSequence(sequence_name) => {\n                let table_def = plan.old.stored_in_table_def(sequence_name).unwrap();\n\n                let table_id = stdb.table_id_from_name_mut(tx, &table_def.name)?.unwrap();\n                let table_schema = stdb.schema_for_table_mut(tx, table_id)?;\n                let sequence_schema = table_schema\n                    .sequences\n                    .iter()\n                    .find(|sequence| sequence.sequence_name[..] == sequence_name[..])\n                    .unwrap();\n\n                log!(\n                    logger,\n                    \"Dropping sequence `{}` from table `{}`\",\n                    sequence_name,\n                    table_def.name\n                );\n                stdb.drop_sequence(tx, sequence_schema.sequence_id)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeColumns(table_name) => {\n                let table_def = plan.new.stored_in_table_def(&table_name.clone().into()).unwrap();\n                let table_id = stdb.table_id_from_name_mut(tx, table_name).unwrap().unwrap();\n                let column_schemas = column_schemas_from_defs(plan.new, &table_def.columns, table_id);\n\n                log!(logger, \"Changing columns of table `{}`\", table_name);\n\n                stdb.alter_table_row_type(tx, table_id, column_schemas)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeAccess(table_name) => {\n                let table_def = plan.new.stored_in_table_def(&table_name.clone().into()).unwrap();\n                stdb.alter_table_access(tx, table_name, table_def.table_access.into())?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddSchedule(_) => {\n                anyhow::bail!(\"Adding schedules is not yet implemented\");\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveSchedule(_) => {\n                anyhow::bail!(\"Removing schedules is not yet implemented\");\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddRowLevelSecurity(sql_rls) => {\n                log!(logger, \"Adding row-level security `{sql_rls}`\");\n                let rls = plan.new.lookup_expect(sql_rls);\n                let rls = RowLevelExpr::build_row_level_expr(tx, &auth_ctx, rls)?;\n\n                stdb.create_row_level_security(tx, rls.def)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveRowLevelSecurity(sql_rls) => {\n                log!(logger, \"Removing-row level security `{sql_rls}`\");\n                stdb.drop_row_level_security(tx, sql_rls.clone())?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddColumns(table_name) => {\n                let table_def = plan\n                    .new\n                    .stored_in_table_def(&table_name.clone().into())\n                    .expect(\"table must exist\");\n                let table_id = stdb.table_id_from_name_mut(tx, table_name).unwrap().unwrap();\n                let column_schemas = column_schemas_from_defs(plan.new, &table_def.columns, table_id);\n\n                let default_values: Vec<AlgebraicValue> = table_def\n                    .columns\n                    .iter()\n                    .filter_map(|col_def| col_def.default_value.clone())\n                    .collect();\n                stdb.add_columns_to_table(tx, table_id, column_schemas, default_values)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::DisconnectAllUsers => {\n                log!(logger, \"Disconnecting all users\");\n                // It does not disconnect clients right away,\n                // but send response indicated that caller should drop clients\n                res = UpdateResult::RequiresClientDisconnect;\n            }\n        }\n    }\n\n    log::info!(\"Database update complete\");\n    Ok(res)\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::{\n        db::relational_db::tests_utils::{begin_mut_tx, insert, TestDB},\n        host::module_host::create_table_from_def,\n    };\n    use spacetimedb_datastore::locking_tx_datastore::PendingSchemaChange;\n    use spacetimedb_lib::db::raw_def::v9::{btree, RawModuleDefV9Builder, TableAccess};\n    use spacetimedb_sats::{product, AlgebraicType::U64};\n    use spacetimedb_schema::{auto_migrate::ponder_migrate, def::ModuleDef};\n\n    struct TestLogger;\n    impl UpdateLogger for TestLogger {\n        fn info(&self, _: &str) {}\n    }\n\n    #[test]\n    fn update_db_repro_2761() -> anyhow::Result<()> {\n        let auth_ctx = AuthCtx::for_testing();\n        let stdb = TestDB::durable()?;\n\n        // Define the old and new modules, the latter with the index on `b`.\n        let define_p = |builder: &mut RawModuleDefV9Builder| {\n            builder\n                .build_table_with_new_type(\"p\", [(\"x\", U64), (\"y\", U64)], true)\n                .with_unique_constraint(0)\n                .with_unique_constraint(1)\n                .with_index(btree(0), \"idx_x\")\n                .with_index(btree(1), \"idx_y\")\n                .with_access(TableAccess::Public)\n                .finish()\n        };\n        let define_t = |builder: &mut RawModuleDefV9Builder, with_index| {\n            let builder = builder\n                .build_table_with_new_type(\"t\", [(\"a\", U64), (\"b\", U64)], true)\n                .with_access(TableAccess::Public);\n\n            let builder = if with_index {\n                builder.with_index(btree(1), \"idx_b\")\n            } else {\n                builder\n            };\n\n            builder.finish()\n        };\n        let module_def = |with_index| -> ModuleDef {\n            let mut builder = RawModuleDefV9Builder::new();\n            define_p(&mut builder);\n            define_t(&mut builder, with_index);\n            builder\n                .finish()\n                .try_into()\n                .expect(\"builder should create a valid database definition\")\n        };\n\n        let old = module_def(false);\n        let new = module_def(true);\n\n        // Create tables for `old`.\n        let mut tx = begin_mut_tx(&stdb);\n        for def in old.tables() {\n            create_table_from_def(&stdb, &mut tx, &old, def)?;\n        }\n\n        // Write two rows to `t`\n        // that would cause a unique constraint violation if `idx_b` was unique.\n        let t_id = stdb\n            .table_id_from_name_mut(&tx, \"t\")?\n            .expect(\"there should be a table with name `t`\");\n        insert(&stdb, &mut tx, t_id, &product![0u64, 42u64])?;\n        insert(&stdb, &mut tx, t_id, &product![1u64, 42u64])?;\n        stdb.commit_tx(tx)?;\n\n        // Try to update the db.\n        let mut tx = begin_mut_tx(&stdb);\n        let plan = ponder_migrate(&old, &new)?;\n        let res = update_database(&stdb, &mut tx, auth_ctx, plan, &TestLogger)?;\n        matches!(res, UpdateResult::Success);\n\n        // Expect the schema change.\n        let idx_b_id = stdb\n            .index_id_from_name(&tx, \"t_b_idx_btree\")?\n            .expect(\"there should be an index named `idx_b`\");\n        assert_eq!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::IndexAdded(t_id, idx_b_id, None)]\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/energy.rs",
    "content": "use std::time::Duration;\n\nuse spacetimedb_lib::{Hash, Identity};\n\nuse crate::messages::control_db::Database;\n\npub use spacetimedb_client_api_messages::energy::*;\npub struct FunctionFingerprint<'a> {\n    pub module_hash: Hash,\n    pub module_identity: Identity,\n    pub caller_identity: Identity,\n    pub function_name: &'a str,\n}\n\npub trait EnergyMonitor: Send + Sync + 'static {\n    fn reducer_budget(&self, fingerprint: &FunctionFingerprint<'_>) -> FunctionBudget;\n    fn record_reducer(\n        &self,\n        fingerprint: &FunctionFingerprint<'_>,\n        energy_used: EnergyQuanta,\n        execution_duration: Duration,\n    );\n    fn record_disk_usage(&self, database: &Database, replica_id: u64, disk_usage: u64, period: Duration);\n    fn record_memory_usage(&self, database: &Database, replica_id: u64, mem_usage: u64, period: Duration);\n}\n\n// The null energy monitor records nothing and always returns the default budget.\n#[derive(Default)]\npub struct NullEnergyMonitor;\n\nimpl EnergyMonitor for NullEnergyMonitor {\n    fn reducer_budget(&self, _fingerprint: &FunctionFingerprint<'_>) -> FunctionBudget {\n        FunctionBudget::DEFAULT_BUDGET\n    }\n\n    fn record_reducer(\n        &self,\n        _fingerprint: &FunctionFingerprint<'_>,\n        _energy_used: EnergyQuanta,\n        _execution_duration: Duration,\n    ) {\n    }\n\n    fn record_disk_usage(&self, _database: &Database, _replica_id: u64, _disk_usage: u64, _period: Duration) {}\n\n    fn record_memory_usage(&self, _database: &Database, _replica_id: u64, _mem_usage: u64, _period: Duration) {}\n}\n"
  },
  {
    "path": "crates/core/src/error.rs",
    "content": "use std::io;\nuse std::num::ParseIntError;\nuse std::path::PathBuf;\nuse std::sync::{MutexGuard, PoisonError};\n\nuse hex::FromHexError;\nuse spacetimedb_commitlog::repo::TxOffset;\nuse spacetimedb_durability::DurabilityExited;\nuse spacetimedb_expr::errors::TypingError;\nuse spacetimedb_fs_utils::lockfile::advisory::LockError;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_schema::error::ValidationErrors;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_snapshot::SnapshotError;\nuse spacetimedb_table::table::ReadViaBsatnError;\nuse thiserror::Error;\n\nuse crate::client::ClientActorId;\nuse crate::host::module_host::ViewCallError;\nuse crate::host::scheduler::ScheduleError;\nuse crate::host::AbiCall;\nuse spacetimedb_lib::buffer::DecodeError;\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::hash::Hash;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_schema::def::error::{LibError, RelationError, SchemaErrors};\nuse spacetimedb_schema::relation::FieldName;\n\npub use spacetimedb_datastore::error::{DatastoreError, IndexError, SequenceError, TableError};\n\n#[derive(Error, Debug)]\npub enum ClientError {\n    #[error(\"Client not found: {0}\")]\n    NotFound(ClientActorId),\n}\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum SubscriptionError {\n    #[error(\"Index not found: {0:?}\")]\n    NotFound(IndexId),\n    #[error(\"Empty string\")]\n    Empty,\n    #[error(\"Unsupported query on subscription: {0:?}\")]\n    Unsupported(String),\n    #[error(\"Subscribing to queries in one call is not supported\")]\n    Multiple,\n}\n\n#[derive(Error, Debug)]\npub enum PlanError {\n    #[error(\"Unsupported feature: `{feature}`\")]\n    Unsupported { feature: String },\n    #[error(\"Unknown table: `{table}`\")]\n    UnknownTable { table: Box<str> },\n    #[error(\"Qualified Table `{expect}` not found\")]\n    TableNotFoundQualified { expect: String },\n    #[error(\"Unknown field: `{field}` not found in the table(s): `{tables:?}`\")]\n    UnknownField { field: String, tables: Vec<TableName> },\n    #[error(\"Unknown field name: `{field}` not found in the table(s): `{tables:?}`\")]\n    UnknownFieldName { field: FieldName, tables: Vec<TableName> },\n    #[error(\"Field(s): `{fields:?}` not found in the table(s): `{tables:?}`\")]\n    UnknownFields {\n        fields: Vec<String>,\n        tables: Vec<TableName>,\n    },\n    #[error(\"Ambiguous field: `{field}`. Also found in {found:?}\")]\n    AmbiguousField { field: String, found: Vec<String> },\n    #[error(\"Plan error: `{0}`\")]\n    Unstructured(String),\n    #[error(\"Internal DBError: `{0}`\")]\n    DatabaseInternal(Box<DBError>),\n    #[error(\"Relation Error: `{0}`\")]\n    Relation(#[from] RelationError),\n}\n\n#[derive(Error, Debug)]\npub enum DatabaseError {\n    #[error(\"Replica not found: {0}\")]\n    NotFound(u64),\n    #[error(\"Database is already opened. Path: `{0}`. Error: {1}\")]\n    DatabasedOpened(PathBuf, anyhow::Error),\n}\n\nimpl From<LockError> for DatabaseError {\n    fn from(LockError { path, source }: LockError) -> Self {\n        Self::DatabasedOpened(path, source.into())\n    }\n}\n\n#[derive(Error, Debug)]\npub enum DBError {\n    #[error(\"LibError: {0}\")]\n    Lib(#[from] LibError),\n    #[error(\"BufferError: {0}\")]\n    Buffer(#[from] DecodeError),\n    #[error(\"DatastoreError: {0}\")]\n    Datastore(#[from] DatastoreError),\n    #[error(\"SequenceError: {0}\")]\n    Sequence2(#[from] SequenceError),\n    #[error(\"SchemaError: {0}\")]\n    Schema(SchemaErrors),\n    #[error(\"IOError: {0}.\")]\n    IoError(#[from] std::io::Error),\n    #[error(\"ParseIntError: {0}.\")]\n    ParseInt(#[from] ParseIntError),\n    #[error(\"Hex representation of hash decoded to incorrect number of bytes: {0}.\")]\n    DecodeHexHash(usize),\n    #[error(\"DecodeHexError: {0}.\")]\n    DecodeHex(#[from] FromHexError),\n    #[error(\"DatabaseError: {0}.\")]\n    Database(#[from] DatabaseError),\n    #[error(\"SledError: {0}.\")]\n    SledDbError(#[from] sled::Error),\n    #[error(\"Mutex was poisoned acquiring lock on MessageLog: {0}\")]\n    MessageLogPoisoned(String),\n    #[error(\"SubscriptionError: {0}\")]\n    Subscription(#[from] SubscriptionError),\n    #[error(\"ClientError: {0}\")]\n    Client(#[from] ClientError),\n    #[error(\"SqlParserError: {error}, executing: `{sql}`\")]\n    SqlParser {\n        sql: String,\n        error: sqlparser::parser::ParserError,\n    },\n    #[error(\"SqlError: {error}, executing: `{sql}`\")]\n    Plan { sql: String, error: PlanError },\n    #[error(\"Error replaying the commit log: {0}\")]\n    LogReplay(#[from] LogReplayError),\n    #[error(transparent)]\n    // Box the inner [`SnapshotError`] to keep Clippy quiet about large `Err` variants.\n    Snapshot(#[from] Box<SnapshotError>),\n    #[error(\"Error reading a value from a table through BSATN: {0}\")]\n    ReadViaBsatnError(#[from] ReadViaBsatnError),\n    #[error(\"Module validation errors: {0}\")]\n    ModuleValidationErrors(#[from] ValidationErrors),\n    #[error(transparent)]\n    Other(#[from] anyhow::Error),\n    #[error(transparent)]\n    TypeError(#[from] TypingError),\n    #[error(\"{error}, executing: `{sql}`\")]\n    WithSql {\n        #[source]\n        error: Box<DBError>,\n        sql: Box<str>,\n    },\n    #[error(transparent)]\n    RestoreSnapshot(#[from] RestoreSnapshotError),\n    #[error(transparent)]\n    DurabilityGone(#[from] DurabilityExited),\n    #[error(transparent)]\n    View(#[from] ViewCallError),\n}\n\nimpl From<InvalidFieldError> for DBError {\n    fn from(value: InvalidFieldError) -> Self {\n        LibError::from(value).into()\n    }\n}\n\nimpl From<spacetimedb_table::read_column::TypeError> for DBError {\n    fn from(err: spacetimedb_table::read_column::TypeError) -> Self {\n        DatastoreError::Table(TableError::from(err)).into()\n    }\n}\n\nimpl From<DBError> for PlanError {\n    fn from(err: DBError) -> Self {\n        PlanError::DatabaseInternal(Box::new(err))\n    }\n}\n\nimpl<'a, T: ?Sized + 'a> From<PoisonError<std::sync::MutexGuard<'a, T>>> for DBError {\n    fn from(err: PoisonError<MutexGuard<'_, T>>) -> Self {\n        DBError::MessageLogPoisoned(err.to_string())\n    }\n}\n\nimpl From<spacetimedb_durability::local::OpenError> for DBError {\n    fn from(e: spacetimedb_durability::local::OpenError) -> Self {\n        use spacetimedb_durability::local::OpenError::*;\n\n        match e {\n            Lock(e) => Self::from(DatabaseError::from(e)),\n            Commitlog(e) => Self::Other(e.into()),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum LogReplayError {\n    #[error(\n        \"Out-of-order commit detected: {} in segment {} after offset {}\",\n        .commit_offset,\n        .segment_offset,\n        .last_commit_offset\n    )]\n    OutOfOrderCommit {\n        commit_offset: u64,\n        segment_offset: usize,\n        last_commit_offset: u64,\n    },\n    #[error(\n        \"Error reading segment {}/{} at commit {}: {}\",\n        .segment_offset,\n        .total_segments,\n        .commit_offset,\n        .source\n    )]\n    TrailingSegments {\n        segment_offset: usize,\n        total_segments: usize,\n        commit_offset: u64,\n        #[source]\n        source: io::Error,\n    },\n    #[error(\"Could not reset log to offset {}: {}\", .offset, .source)]\n    Reset {\n        offset: u64,\n        #[source]\n        source: io::Error,\n    },\n    #[error(\"Missing object {} referenced from commit {}\", .hash, .commit_offset)]\n    MissingObject { hash: Hash, commit_offset: u64 },\n    #[error(\n        \"Unexpected I/O error reading commit {} from segment {}: {}\",\n        .commit_offset,\n        .segment_offset,\n        .source\n    )]\n    Io {\n        segment_offset: usize,\n        commit_offset: u64,\n        #[source]\n        source: io::Error,\n    },\n}\n\n#[derive(Error, Debug)]\npub enum NodesError {\n    #[error(\"Failed to decode row: {0}\")]\n    DecodeRow(#[source] DecodeError),\n    #[error(\"Failed to decode value: {0}\")]\n    DecodeValue(#[source] DecodeError),\n    #[error(\"Failed to decode primary key: {0}\")]\n    DecodePrimaryKey(#[source] DecodeError),\n    #[error(\"Failed to decode schema: {0}\")]\n    DecodeSchema(#[source] DecodeError),\n    #[error(\"Failed to decode filter: {0}\")]\n    DecodeFilter(#[source] DecodeError),\n    #[error(\"table with provided name or id doesn't exist\")]\n    TableNotFound,\n    #[error(\"index with provided name or id doesn't exist\")]\n    IndexNotFound,\n    #[error(\"index was not unique\")]\n    IndexNotUnique,\n    #[error(\"row was not found in index\")]\n    IndexRowNotFound,\n    #[error(\"index does not support range scans\")]\n    IndexCannotSeekRange,\n    #[error(\"column is out of bounds\")]\n    BadColumn,\n    #[error(\"can't perform operation; not inside transaction\")]\n    NotInTransaction,\n    #[error(\"can't perform operation; not inside anonymous transaction\")]\n    NotInAnonTransaction,\n    #[error(\"ABI call not allowed while holding open a transaction: {0}\")]\n    WouldBlockTransaction(AbiCall),\n    #[error(\"table with name `{0}` start with 'st_' and that is reserved for internal system tables.\")]\n    SystemName(TableName),\n    #[error(\"internal db error: {0}\")]\n    Internal(#[source] Box<DBError>),\n    #[error(transparent)]\n    BadQuery(#[from] RelationError),\n    #[error(\"invalid index type: {0}\")]\n    BadIndexType(u8),\n    #[error(\"Failed to scheduled timer: {0}\")]\n    ScheduleError(#[source] ScheduleError),\n    #[error(\"HTTP request failed: {0}\")]\n    HttpError(String),\n}\n\nimpl From<DBError> for NodesError {\n    fn from(e: DBError) -> Self {\n        match e {\n            DBError::Datastore(\n                DatastoreError::Table(TableError::IdNotFound(_, _)) | DatastoreError::Table(TableError::NotFound(_)),\n            ) => Self::TableNotFound,\n            DBError::Datastore(DatastoreError::Table(TableError::ColumnNotFound(_))) => Self::BadColumn,\n            DBError::Datastore(DatastoreError::Index(IndexError::NotFound(_))) => Self::IndexNotFound,\n            DBError::Datastore(DatastoreError::Index(IndexError::Decode(e))) => Self::DecodeRow(e),\n            DBError::Datastore(DatastoreError::Index(IndexError::NotUnique(_))) => Self::IndexNotUnique,\n            DBError::Datastore(DatastoreError::Index(IndexError::KeyNotFound(..))) => Self::IndexRowNotFound,\n            _ => Self::Internal(Box::new(e)),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum RestoreSnapshotError {\n    #[error(\"Snapshot has incorrect database_identity: expected {expected} but found {actual}\")]\n    IdentityMismatch { expected: Identity, actual: Identity },\n    #[error(\"Failed to restore datastore from snapshot\")]\n    Datastore(#[source] Box<DBError>),\n    #[error(\"Failed to read snapshot\")]\n    Snapshot(#[from] Box<SnapshotError>),\n    #[error(\"Failed to bootstrap datastore without snapshot\")]\n    Bootstrap(#[source] Box<DBError>),\n    #[error(\"No connected snapshot found, commitlog starts at {min_commitlog_offset}\")]\n    NoConnectedSnapshot { min_commitlog_offset: TxOffset },\n    #[error(\"Failed to invalidate snapshots at or newer than {offset}\")]\n    Invalidate {\n        offset: TxOffset,\n        #[source]\n        source: Box<SnapshotError>,\n    },\n}\n"
  },
  {
    "path": "crates/core/src/estimation.rs",
    "content": "use crate::{\n    db::relational_db::{RelationalDB, Tx},\n    error::DBError,\n};\nuse spacetimedb_datastore::locking_tx_datastore::{state_view::StateView as _, NumDistinctValues};\nuse spacetimedb_lib::{identity::AuthCtx, query::Delta};\nuse spacetimedb_physical_plan::plan::{HashJoin, IxJoin, IxScan, PhysicalPlan, Sarg, TableScan};\nuse spacetimedb_primitives::{ColList, TableId};\n\n/// If the caller is not allowed to exceed the row limit,\n/// reject the request if the estimated cardinality exceeds the limit.\npub fn check_row_limit<Query>(\n    queries: &[Query],\n    db: &RelationalDB,\n    tx: &Tx,\n    row_est: impl Fn(&Query, &Tx) -> u64,\n    auth: &AuthCtx,\n) -> Result<(), DBError> {\n    if !auth.exceed_row_limit()\n        && let Some(limit) = db.row_limit(tx)?\n    {\n        let mut estimate: u64 = 0;\n        for query in queries {\n            estimate = estimate.saturating_add(row_est(query, tx));\n        }\n        if estimate > limit {\n            return Err(DBError::Other(anyhow::anyhow!(\n                \"Estimated cardinality ({estimate} rows) exceeds limit ({limit} rows)\"\n            )));\n        }\n    }\n    Ok(())\n}\n\n/// Use cardinality estimates to predict the total number of rows scanned by a query.\npub fn estimate_rows_scanned(tx: &Tx, plan: &PhysicalPlan) -> u64 {\n    match plan {\n        PhysicalPlan::TableScan(..) | PhysicalPlan::IxScan(..) => row_estimate(tx, plan),\n        PhysicalPlan::Filter(input, _) => estimate_rows_scanned(tx, input).saturating_add(row_estimate(tx, input)),\n        PhysicalPlan::NLJoin(lhs, rhs) => estimate_rows_scanned(tx, lhs)\n            .saturating_add(estimate_rows_scanned(tx, rhs))\n            .saturating_add(row_estimate(tx, lhs).saturating_mul(row_estimate(tx, rhs))),\n        PhysicalPlan::IxJoin(IxJoin { lhs, unique: true, .. }, _) => {\n            estimate_rows_scanned(tx, lhs).saturating_add(row_estimate(tx, lhs))\n        }\n        PhysicalPlan::IxJoin(\n            IxJoin {\n                lhs, rhs, rhs_field, ..\n            },\n            _,\n        ) => estimate_rows_scanned(tx, lhs).saturating_add(row_estimate(tx, lhs).saturating_mul(index_row_est(\n            tx,\n            rhs.table_id,\n            &ColList::from(*rhs_field),\n        ))),\n        PhysicalPlan::HashJoin(\n            HashJoin {\n                lhs, rhs, unique: true, ..\n            },\n            _,\n        ) => estimate_rows_scanned(tx, lhs)\n            .saturating_add(estimate_rows_scanned(tx, rhs))\n            .saturating_add(row_estimate(tx, lhs)),\n        PhysicalPlan::HashJoin(HashJoin { lhs, rhs, .. }, _) => estimate_rows_scanned(tx, lhs)\n            .saturating_add(estimate_rows_scanned(tx, rhs))\n            .saturating_add(row_estimate(tx, lhs).saturating_mul(row_estimate(tx, rhs))),\n    }\n}\n\n/// Estimate the cardinality of a physical plan.\npub fn row_estimate(tx: &Tx, plan: &PhysicalPlan) -> u64 {\n    match plan {\n        PhysicalPlan::TableScan(TableScan { limit: Some(n), .. }, _)\n        | PhysicalPlan::IxScan(IxScan { limit: Some(n), .. }, _) => *n,\n        PhysicalPlan::TableScan(\n            TableScan {\n                schema,\n                limit: None,\n                delta: None,\n            },\n            _,\n        ) => tx.table_row_count(schema.table_id).unwrap_or_default(),\n        PhysicalPlan::TableScan(\n            TableScan {\n                limit: None,\n                delta: Some(Delta::Inserts | Delta::Deletes),\n                ..\n            },\n            _,\n        ) => 0,\n        PhysicalPlan::IxScan(\n            ix @ IxScan {\n                arg: Sarg::Eq(last_col, _),\n                ..\n            },\n            _,\n        ) => {\n            let mut cols: ColList = ix.prefix.iter().map(|(c, _)| *c).collect();\n            cols.push(*last_col);\n            index_row_est(tx, ix.schema.table_id, &cols)\n        }\n        PhysicalPlan::IxScan(IxScan { schema, .. }, _) => tx.table_row_count(schema.table_id).unwrap_or_default(),\n        PhysicalPlan::Filter(input, _) => row_estimate(tx, input),\n        PhysicalPlan::NLJoin(lhs, rhs) => row_estimate(tx, lhs).saturating_mul(row_estimate(tx, rhs)),\n        PhysicalPlan::IxJoin(IxJoin { lhs, unique: true, .. }, _)\n        | PhysicalPlan::HashJoin(HashJoin { lhs, unique: true, .. }, _) => row_estimate(tx, lhs),\n        PhysicalPlan::IxJoin(\n            IxJoin {\n                lhs, rhs, rhs_field, ..\n            },\n            _,\n        ) => row_estimate(tx, lhs).saturating_mul(index_row_est(tx, rhs.table_id, &ColList::from(*rhs_field))),\n        PhysicalPlan::HashJoin(HashJoin { lhs, rhs, .. }, _) => {\n            row_estimate(tx, lhs).saturating_mul(row_estimate(tx, rhs))\n        }\n    }\n}\n\n/// The estimated number of rows that an index probe will return.\nfn index_row_est(tx: &Tx, table_id: TableId, cols: &ColList) -> u64 {\n    let table_rc = || tx.table_row_count(table_id).unwrap_or_default();\n    match tx.num_distinct_values(table_id, cols) {\n        NumDistinctValues::NonZero(ndv) => table_rc() / ndv,\n        NumDistinctValues::Zero => 0,\n        NumDistinctValues::Error => table_rc(),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{estimate_rows_scanned, row_estimate};\n    use crate::db::relational_db::tests_utils::{begin_tx, insert, with_auto_commit};\n    use crate::db::relational_db::{tests_utils::TestDB, RelationalDB};\n    use crate::error::DBError;\n    use crate::sql::ast::SchemaViewer;\n    use spacetimedb_lib::{identity::AuthCtx, AlgebraicType};\n    use spacetimedb_query::compile_subscription;\n    use spacetimedb_sats::product;\n\n    fn in_mem_db() -> TestDB {\n        TestDB::in_memory().expect(\"failed to make test db\")\n    }\n\n    fn estimate_for(db: &RelationalDB, sql: &str) -> u64 {\n        let auth = AuthCtx::for_testing();\n        let tx = begin_tx(db);\n        let tx = SchemaViewer::new(&tx, &auth);\n\n        compile_subscription(sql, &tx, &auth)\n            .map(|(plans, ..)| plans)\n            .expect(\"failed to compile sql query\")\n            .into_iter()\n            .map(|plan| plan.optimize(&auth).expect(\"failed to optimize sql query\"))\n            .map(|plan| row_estimate(&tx, &plan))\n            .sum()\n    }\n\n    fn scanned_for(db: &RelationalDB, sql: &str) -> u64 {\n        let auth = AuthCtx::for_testing();\n        let tx = begin_tx(db);\n        let tx = SchemaViewer::new(&tx, &auth);\n\n        compile_subscription(sql, &tx, &auth)\n            .map(|(plans, ..)| plans)\n            .expect(\"failed to compile sql query\")\n            .into_iter()\n            .map(|plan| plan.optimize(&auth).expect(\"failed to optimize sql query\"))\n            .map(|plan| estimate_rows_scanned(&tx, plan.physical_plan()))\n            .sum()\n    }\n\n    fn create_table_t(db: &RelationalDB, indexed: bool) {\n        let indexes = &[0.into()];\n        let indexes = if indexed { indexes } else { &[] as &[_] };\n        let table_id = db\n            .create_table_for_test(\"T\", &[\"a\", \"b\"].map(|n| (n, AlgebraicType::U64)), indexes)\n            .expect(\"Failed to create table\");\n\n        with_auto_commit(db, |tx| -> Result<(), DBError> {\n            for i in 0u64..10u64 {\n                insert(db, tx, table_id, &product![i % 5, i]).expect(\"failed to insert into table\");\n            }\n            Ok(())\n        })\n        .expect(\"failed to insert into table\");\n    }\n\n    #[test]\n    fn cardinality_estimation_index_lookup() {\n        let db = in_mem_db();\n        create_table_t(&db, true);\n        assert_eq!(2, estimate_for(&db, \"select * from T where a = 0\"));\n    }\n\n    #[test]\n    fn scanned_rows_respect_filters() {\n        let db = in_mem_db();\n        create_table_t(&db, true);\n        assert!(scanned_for(&db, \"select * from T where a = 0\") <= scanned_for(&db, \"select * from T\"));\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/disk_storage.rs",
    "content": "use async_trait::async_trait;\nuse spacetimedb_lib::{hash_bytes, Hash};\nuse std::io;\nuse std::path::PathBuf;\nuse tokio::fs;\nuse tokio::io::AsyncWriteExt;\n\nuse super::ExternalStorage;\n\n/// A simple [`ExternalStorage`] that stores programs in the filesystem.\n#[derive(Clone, Debug)]\npub struct DiskStorage {\n    base: PathBuf,\n}\n\nimpl DiskStorage {\n    pub async fn new(base: PathBuf) -> io::Result<Self> {\n        fs::create_dir_all(&base).await?;\n        Ok(Self { base })\n    }\n\n    fn object_path(&self, h: &Hash) -> PathBuf {\n        let hex = h.to_hex();\n        let (pre, suf) = hex.split_at(2);\n        let mut path = self.base.clone();\n        path.extend([pre, suf]);\n        path\n    }\n\n    #[tracing::instrument(level = \"trace\", skip(self))]\n    pub async fn get(&self, key: &Hash) -> io::Result<Option<Box<[u8]>>> {\n        let path = self.object_path(key);\n        match fs::read(path).await {\n            Ok(bytes) => {\n                let actual_hash = hash_bytes(&bytes);\n                if actual_hash == *key {\n                    Ok(Some(bytes.into()))\n                } else {\n                    log::warn!(\"hash mismatch: {actual_hash} stored at {key}\");\n                    if let Err(e) = self.prune(key).await {\n                        log::warn!(\"prune error: {e}\");\n                    }\n                    Ok(None)\n                }\n            }\n            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),\n            Err(e) => Err(e),\n        }\n    }\n\n    #[tracing::instrument(level = \"trace\", skip(self, value))]\n    pub async fn put(&self, value: &[u8]) -> io::Result<Hash> {\n        let h = hash_bytes(value);\n        let path = self.object_path(&h);\n        fs::create_dir_all(path.parent().expect(\"object path must have a parent\")).await?;\n\n        // to ensure it doesn't conflict with a concurrent call to put() - suffix with nanosecond timestamp\n        let ts = std::time::UNIX_EPOCH.elapsed().unwrap().as_nanos();\n        let tmp = path.with_extension(format!(\"tmp{ts}\"));\n        {\n            let mut f = fs::File::options().write(true).create_new(true).open(&tmp).await?;\n            f.write_all(value).await?;\n            f.sync_data().await?;\n        }\n\n        fs::rename(tmp, path).await?;\n\n        Ok(h)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip(self))]\n    pub async fn prune(&self, key: &Hash) -> anyhow::Result<()> {\n        Ok(fs::remove_file(self.object_path(key)).await?)\n    }\n}\n\n#[async_trait]\nimpl ExternalStorage for DiskStorage {\n    async fn lookup(&self, program_hash: Hash) -> anyhow::Result<Option<Box<[u8]>>> {\n        self.get(&program_hash).await.map_err(Into::into)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/host_controller.rs",
    "content": "use super::module_host::{EventStatus, ModuleHost, ModuleInfo, NoSuchModule};\nuse super::scheduler::SchedulerStarter;\nuse super::wasmtime::WasmtimeRuntime;\nuse super::{Scheduler, UpdateDatabaseResult};\nuse crate::client::{ClientActorId, ClientName};\nuse crate::database_logger::DatabaseLogger;\nuse crate::db::persistence::PersistenceProvider;\nuse crate::db::relational_db::{self, spawn_view_cleanup_loop, DiskSizeFn, RelationalDB, Txdata};\nuse crate::db::{self, spawn_tx_metrics_recorder};\nuse crate::energy::{EnergyMonitor, EnergyQuanta, NullEnergyMonitor};\nuse crate::host::v8::V8Runtime;\nuse crate::host::ProcedureCallError;\nuse crate::messages::control_db::{Database, HostType};\nuse crate::module_host_context::ModuleCreationContext;\nuse crate::replica_context::ReplicaContext;\nuse crate::subscription::module_subscription_actor::ModuleSubscriptions;\nuse crate::subscription::module_subscription_manager::{spawn_send_worker, SubscriptionManager, TransactionOffset};\nuse crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse crate::util::asyncify;\nuse crate::util::jobs::{AllocatedJobCore, JobCores};\nuse crate::worker_metrics::WORKER_METRICS;\nuse anyhow::{anyhow, bail, Context};\nuse async_trait::async_trait;\nuse durability::{Durability, EmptyHistory};\nuse log::{info, trace, warn};\nuse parking_lot::Mutex;\nuse scopeguard::defer;\nuse spacetimedb_commitlog::SizeOnDisk;\nuse spacetimedb_data_structures::error_stream::ErrorStream;\nuse spacetimedb_data_structures::map::{IntMap, IntSet};\nuse spacetimedb_datastore::db_metrics::data_size::DATA_SIZE_METRICS;\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_datastore::system_tables::ModuleKind;\nuse spacetimedb_datastore::traits::Program;\nuse spacetimedb_durability::{self as durability};\nuse spacetimedb_lib::{AlgebraicValue, Identity, Timestamp};\nuse spacetimedb_paths::server::{ModuleLogsDir, ServerDataDir};\nuse spacetimedb_sats::hash::Hash;\nuse spacetimedb_schema::auto_migrate::{ponder_migrate, AutoMigrateError, MigrationPolicy, PrettyPrintStyle};\nuse spacetimedb_schema::def::{ModuleDef, RawModuleDefVersion};\nuse spacetimedb_table::page_pool::PagePool;\nuse std::future::Future;\nuse std::ops::Deref;\nuse std::sync::Arc;\nuse std::time::Duration;\nuse tokio::sync::{watch, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock as AsyncRwLock};\nuse tokio::task::AbortHandle;\nuse tokio::time::error::Elapsed;\nuse tokio::time::{interval_at, timeout, Instant};\n\n// TODO:\n//\n// - [db::Config] should be per-[Database]\n\n/// The maximum size of in-memory database loggers.\n///\n/// Currently 16KiB, or about 111k log records of 150 bytes.\n//\n// TODO(config): We may want to allow overriding this via [db::Config], if and\n// when the config applies to individual databases (as opposed to globally).\nconst IN_MEMORY_DATABASE_LOGGER_MAX_SIZE: u64 = 0x1_000_000;\n\n/// A shared mutable cell containing a module host and associated database.\ntype HostCell = Arc<AsyncRwLock<Option<Host>>>;\n\n/// The registry of all running hosts.\ntype Hosts = Arc<Mutex<IntMap<u64, HostCell>>>;\n\npub type ExternalDurability = (Arc<dyn Durability<TxData = Txdata>>, DiskSizeFn);\n\n#[async_trait]\npub trait ExternalStorage: Send + Sync + 'static {\n    async fn lookup(&self, program_hash: Hash) -> anyhow::Result<Option<Box<[u8]>>>;\n}\n#[async_trait]\nimpl<F, Fut> ExternalStorage for F\nwhere\n    F: Fn(Hash) -> Fut + Send + Sync + 'static,\n    Fut: Future<Output = anyhow::Result<Option<Box<[u8]>>>> + Send,\n{\n    async fn lookup(&self, program_hash: Hash) -> anyhow::Result<Option<Box<[u8]>>> {\n        self(program_hash).await\n    }\n}\n\npub type ProgramStorage = Arc<dyn ExternalStorage>;\n\n/// A host controller manages the lifecycle of spacetime databases and their\n/// associated modules.\n///\n/// This type is, and must remain, cheap to clone.\n/// All of its fields should either be [`Copy`], enclosed in an [`Arc`],\n/// or have some other fast [`Clone`] implementation.\n#[derive(Clone)]\npub struct HostController {\n    /// Map of all hosts managed by this controller,\n    /// keyed by replica id.\n    hosts: Hosts,\n    /// The root directory for database data.\n    pub data_dir: Arc<ServerDataDir>,\n    /// The default configuration to use for databases created by this\n    /// controller.\n    default_config: db::Config,\n    /// The [`ProgramStorage`] to query when instantiating a module.\n    program_storage: ProgramStorage,\n    /// The [`EnergyMonitor`] used by this controller.\n    energy_monitor: Arc<dyn EnergyMonitor>,\n    /// Provides persistence services for each replica.\n    persistence: Arc<dyn PersistenceProvider>,\n    /// The page pool all databases will use by cloning the ref counted pool.\n    pub page_pool: PagePool,\n    /// The runtimes for running our modules.\n    runtimes: Arc<HostRuntimes>,\n    /// The CPU cores that are reserved for ModuleHost operations to run on.\n    db_cores: JobCores,\n    /// The pool of buffers used to build `BsatnRowList`s in subscriptions.\n    pub bsatn_rlb_pool: BsatnRowListBuilderPool,\n}\n\npub(crate) struct HostRuntimes {\n    wasmtime: WasmtimeRuntime,\n    v8: V8Runtime,\n}\n\nimpl HostRuntimes {\n    fn new(data_dir: Option<&ServerDataDir>) -> Arc<Self> {\n        let wasmtime = WasmtimeRuntime::new(data_dir);\n        let v8 = V8Runtime::default();\n        Arc::new(Self { wasmtime, v8 })\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct ReducerCallResult {\n    pub outcome: ReducerOutcome,\n    pub energy_used: EnergyQuanta,\n    pub execution_duration: Duration,\n}\n\nimpl ReducerCallResult {\n    pub fn is_err(&self) -> bool {\n        self.outcome.is_err()\n    }\n\n    pub fn is_ok(&self) -> bool {\n        !self.is_err()\n    }\n}\n\nimpl From<ReducerCallResult> for Result<(), anyhow::Error> {\n    fn from(value: ReducerCallResult) -> Self {\n        value.outcome.into_result()\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum ReducerOutcome {\n    Committed,\n    Failed(Box<Box<str>>),\n    BudgetExceeded,\n}\n\nimpl ReducerOutcome {\n    pub fn into_result(self) -> anyhow::Result<()> {\n        match self {\n            Self::Committed => Ok(()),\n            Self::Failed(e) => Err(anyhow::anyhow!(e)),\n            Self::BudgetExceeded => Err(anyhow::anyhow!(\"reducer ran out of energy\")),\n        }\n    }\n\n    pub fn is_err(&self) -> bool {\n        !matches!(self, Self::Committed)\n    }\n}\n\nimpl From<&EventStatus> for ReducerOutcome {\n    fn from(status: &EventStatus) -> Self {\n        match &status {\n            EventStatus::Committed(_) => ReducerOutcome::Committed,\n            EventStatus::FailedUser(e) | EventStatus::FailedInternal(e) => {\n                ReducerOutcome::Failed(Box::new((&**e).into()))\n            }\n            EventStatus::OutOfEnergy => ReducerOutcome::BudgetExceeded,\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct ProcedureCallResult {\n    pub return_val: AlgebraicValue,\n    pub execution_duration: Duration,\n    pub start_timestamp: Timestamp,\n}\n\n#[derive(Debug)]\npub enum CallResult {\n    Reducer(ReducerCallResult),\n    Procedure(ProcedureCallResult),\n}\n\n#[derive(Debug)]\npub struct CallProcedureReturn {\n    pub result: Result<ProcedureCallResult, ProcedureCallError>,\n    pub tx_offset: Option<TransactionOffset>,\n}\n\nimpl HostController {\n    pub fn new(\n        data_dir: Arc<ServerDataDir>,\n        default_config: db::Config,\n        program_storage: ProgramStorage,\n        energy_monitor: Arc<impl EnergyMonitor>,\n        persistence: Arc<dyn PersistenceProvider>,\n        db_cores: JobCores,\n    ) -> Self {\n        Self {\n            hosts: <_>::default(),\n            default_config,\n            program_storage,\n            energy_monitor,\n            persistence,\n            runtimes: HostRuntimes::new(Some(&data_dir)),\n            data_dir,\n            page_pool: PagePool::new(default_config.page_pool_max_size),\n            bsatn_rlb_pool: BsatnRowListBuilderPool::new(),\n            db_cores,\n        }\n    }\n\n    /// Replace the [`ProgramStorage`] used by this controller.\n    pub fn set_program_storage(&mut self, ps: ProgramStorage) {\n        self.program_storage = ps;\n    }\n\n    /// Get a [`ModuleHost`] managed by this controller, or launch it from\n    /// persistent state.\n    ///\n    /// If the host is not running, it is started according to the default\n    /// [`db::Config`] set for this controller.\n    ///   The underlying database is restored from existing data at its\n    /// canonical filesystem location _iff_ the default config mandates disk\n    /// storage.\n    ///\n    /// The module will be instantiated from the program bytes stored in an\n    /// existing database.\n    ///   If the database is empty, the `program_bytes_address` of the given\n    /// [`Database`] will be used to load the program from the controller's\n    /// [`ProgramStorage`]. The initialization procedure (schema creation,\n    /// `__init__` reducer) will be invoked on the found module, and the\n    /// database will be marked as initialized.\n    ///\n    /// See also: [`Self::get_module_host`]\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn get_or_launch_module_host(&self, database: Database, replica_id: u64) -> anyhow::Result<ModuleHost> {\n        let mut rx = self.watch_maybe_launch_module_host(database, replica_id).await?;\n        let module = rx.borrow_and_update();\n        Ok(module.clone())\n    }\n\n    /// Like [`Self::get_or_launch_module_host`], use a [`ModuleHost`] managed\n    /// by this controller, or launch it if it is not running.\n    ///\n    /// Instead of a [`ModuleHost`], this returns a [`watch::Receiver`] which\n    /// gets notified each time the module is updated.\n    ///\n    /// See also: [`Self::watch_module_host`]\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn watch_maybe_launch_module_host(\n        &self,\n        database: Database,\n        replica_id: u64,\n    ) -> anyhow::Result<watch::Receiver<ModuleHost>> {\n        // Try a read lock first.\n        {\n            if let Ok(guard) = self.acquire_read_lock(replica_id).await\n                && let Some(host) = &*guard\n            {\n                trace!(\"cached host {}/{}\", database.database_identity, replica_id);\n                return Ok(host.module.subscribe());\n            }\n        }\n\n        // We didn't find a running module, so take a write lock.\n        // Since [`tokio::sync::RwLock`] doesn't support upgrading of read locks,\n        // we'll need to check again if a module was added meanwhile.\n        let Ok(mut guard) = self.acquire_write_lock(replica_id).await else {\n            bail!(\n                \"unable to lock database {} for initialization\",\n                database.database_identity\n            );\n        };\n        if let Some(host) = &*guard {\n            trace!(\n                \"cached host {}/{} (lock upgrade)\",\n                database.database_identity,\n                replica_id\n            );\n            return Ok(host.module.subscribe());\n        }\n\n        trace!(\"launch host {}/{}\", database.database_identity, replica_id);\n\n        // `HostController::clone` is fast,\n        // as all of its fields are either `Copy` or wrapped in `Arc`.\n        let this = self.clone();\n\n        // `try_init_host` is not cancel safe, as it will spawn other async tasks\n        // which hold a filesystem lock past when `try_init_host` returns or is cancelled.\n        // This means that, if `try_init_host` is cancelled, subsequent calls will fail.\n        //\n        // This is problematic because Axum will cancel its handler tasks if the client disconnects,\n        // and this method is called from Axum handlers, e.g. for the subscribe route.\n        // `tokio::spawn` a task to build the `Host` and install it in the `guard`,\n        // so that it will run to completion even if the caller goes away.\n        //\n        // Note that `tokio::spawn` only cancels its tasks when the runtime shuts down,\n        // at which point we won't be calling `try_init_host` again anyways.\n        let rx = tokio::spawn(async move {\n            let host = this.try_init_host(database, replica_id).await?;\n\n            let rx = host.module.subscribe();\n            *guard = Some(host);\n\n            Ok::<_, anyhow::Error>(rx)\n        })\n        .await??;\n\n        Ok(rx)\n    }\n\n    /// Construct an in-memory instance of `database` running `program`,\n    /// initialize it, then immediately destroy it.\n    ///\n    /// This is used during an initial, fresh publish operation\n    /// in order to check the `program`'s validity as a module,\n    /// since some validity checks we'd like to do (e.g. typechecking RLS filters)\n    /// require a fully instantiated database.\n    ///\n    /// This is not necessary during hotswap publishes,\n    /// as the automigration planner and executor accomplish the same validity checks.\n    pub async fn check_module_validity(&self, database: Database, program: Program) -> anyhow::Result<Arc<ModuleInfo>> {\n        let (program, launched) = Host::try_init_in_memory_to_check(\n            &self.runtimes,\n            self.page_pool.clone(),\n            database,\n            program,\n            // This takes a db core to check validity, and we will later take\n            // another core to actually run the module. Due to the round-robin\n            // algorithm that JobCores uses, that will likely just be the same\n            // core - there's not a concern that we'll only end up using 1/2\n            // of the actual cores.\n            self.db_cores.take(),\n            self.bsatn_rlb_pool.clone(),\n        )\n        .await?;\n\n        let call_result = launched.module_host.init_database(program).await?;\n        if let Some(call_result) = call_result {\n            Result::from(call_result)?;\n        }\n\n        Ok(launched.module_host.info)\n    }\n\n    /// Run a computation on the [`RelationalDB`] of a [`ModuleHost`] managed by\n    /// this controller, launching the host if necessary.\n    ///\n    /// If the computation `F` panics, the host is removed from this controller,\n    /// releasing its resources.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn using_database<F, Fut, T>(&self, database: Database, replica_id: u64, f: F) -> anyhow::Result<T>\n    where\n        F: FnOnce(Arc<RelationalDB>) -> Fut + Send + 'static,\n        Fut: std::future::Future<Output = T> + Send + 'static,\n        T: Send + 'static,\n    {\n        trace!(\"using database {}/{}\", database.database_identity, replica_id);\n        let module = self.get_or_launch_module_host(database, replica_id).await?;\n        let on_panic = self.unregister_fn(replica_id);\n        scopeguard::defer_on_unwind!({\n            warn!(\"database operation panicked\");\n            on_panic();\n        });\n\n        let db = module.replica_ctx().relational_db.clone();\n        let result = module.on_module_thread_async(\"using_database\", move || f(db)).await?;\n        Ok(result)\n    }\n    /// Update the [`ModuleHost`] identified by `replica_id` to the given\n    /// program.\n    ///\n    /// The host may not be running, in which case it is spawned (see\n    /// [`Self::get_or_launch_module_host`] for details on what this entails).\n    ///\n    /// If the host was running, and the update fails, the previous version of\n    /// the host keeps running.\n    #[tracing::instrument(level = \"trace\", skip_all, err)]\n    pub async fn update_module_host(\n        &self,\n        database: Database,\n        host_type: HostType,\n        replica_id: u64,\n        program_bytes: Box<[u8]>,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        let program = Program::from_bytes(host_type.into(), program_bytes);\n        trace!(\n            \"update module host {}/{}: genesis={} update-to={}\",\n            database.database_identity,\n            replica_id,\n            database.initial_program,\n            program.hash\n        );\n\n        let Ok(mut guard) = self.acquire_write_lock(replica_id).await else {\n            bail!(\"unable to lock database {} for update\", database.database_identity);\n        };\n\n        // `HostController::clone` is fast,\n        // as all of its fields are either `Copy` or wrapped in `Arc`.\n        let this = self.clone();\n\n        // `try_init_host` is not cancel safe, as it will spawn other async tasks\n        // which hold a filesystem lock past when `try_init_host` returns or is cancelled.\n        // This means that, if `try_init_host` is cancelled, subsequent calls will fail.\n        //\n        // The rest of this future is also not cancel safe, as it will `Option::take` out of the guard\n        // at the start of the block and then store back into it at the end.\n        //\n        // This is problematic because Axum will cancel its handler tasks if the client disconnects,\n        // and this method is called from Axum handlers, e.g. for the publish route.\n        // `tokio::spawn` a task to update the contents of `guard`,\n        // so that it will run to completion even if the caller goes away.\n        //\n        // Note that `tokio::spawn` only cancels its tasks when the runtime shuts down,\n        // at which point we won't be calling `try_init_host` again anyways.\n        let update_result = tokio::spawn(async move {\n            let mut host = match guard.take() {\n                None => {\n                    trace!(\"host not running, try_init\");\n                    this.try_init_host(database, replica_id).await?\n                }\n                Some(host) => {\n                    trace!(\"host found, updating\");\n                    host\n                }\n            };\n            let update_result = host\n                .update_module(\n                    this.runtimes.clone(),\n                    program,\n                    policy,\n                    this.energy_monitor.clone(),\n                    this.unregister_fn(replica_id),\n                    this.db_cores.take(),\n                )\n                .await?;\n\n            *guard = Some(host);\n\n            Ok::<_, anyhow::Error>(update_result)\n        })\n        .await??;\n\n        Ok(update_result)\n    }\n\n    pub async fn migrate_plan(\n        &self,\n        database: Database,\n        host_type: HostType,\n        replica_id: u64,\n        program_bytes: Box<[u8]>,\n        style: PrettyPrintStyle,\n    ) -> anyhow::Result<MigratePlanResult> {\n        let program = Program::from_bytes(host_type.into(), program_bytes);\n        trace!(\n            \"migrate plan {}/{}: genesis={} update-to={}\",\n            database.database_identity,\n            replica_id,\n            database.initial_program,\n            program.hash\n        );\n\n        let Ok(guard) = self.acquire_read_lock(replica_id).await else {\n            bail!(\n                \"unable to lock database {} for migration planning\",\n                database.database_identity\n            );\n        };\n        let host = guard.as_ref().ok_or(NoSuchModule)?;\n\n        host.migrate_plan(\n            self.page_pool.clone(),\n            self.bsatn_rlb_pool.clone(),\n            &self.runtimes,\n            host_type,\n            program,\n            style,\n        )\n        .await\n    }\n\n    /// Release all resources of the [`ModuleHost`] identified by `replica_id`,\n    /// and deregister it from the controller.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn exit_module_host(&self, replica_id: u64, timeout: Duration) -> Result<(), anyhow::Error> {\n        let Some(lock) = self.hosts.lock().remove(&replica_id) else {\n            return Ok(());\n        };\n        // To debug the potential deadlock issue reported in\n        // https://github.com/clockworklabs/SpacetimeDBPrivate/issues/2337\n        // we'll log a warning every 5s if we can't acquire an exclusive lock.\n        let start = Instant::now();\n        let mut t = interval_at(start + Duration::from_secs(5), Duration::from_secs(5));\n        let warn_blocked = tokio::spawn(async move {\n            loop {\n                t.tick().await;\n                warn!(\n                    \"blocked waiting to exit module for replica {} since {}s\",\n                    replica_id,\n                    start.elapsed().as_secs_f32()\n                );\n            }\n        });\n        defer!(warn_blocked.abort());\n\n        let shutdown = tokio::time::timeout(timeout, async {\n            let mut guard = lock.write_owned().await;\n            let Some(host) = guard.take() else {\n                return;\n            };\n            let module = host.module.borrow().clone();\n            let info = module.info();\n\n            let database_identity = info.database_identity;\n            let table_names = info.module_def.tables().map(|t| t.name.deref());\n\n            // Ensure we clear the metrics even if the future is cancelled.\n            defer!(remove_database_gauges(&database_identity, table_names));\n\n            info!(\"replica={replica_id} database={database_identity} exiting module\");\n            module.exit().await;\n            let db = &module.replica_ctx().relational_db;\n            info!(\"replica={replica_id} database={database_identity} exiting database\");\n            db.shutdown().await;\n            info!(\"replica={replica_id} database={database_identity} module host exited\");\n        })\n        .await;\n\n        if shutdown.is_err() {\n            warn!(\n                \"replica={replica_id} shutdown timed out after {}s\",\n                start.elapsed().as_secs_f32()\n            );\n        }\n\n        Ok(())\n    }\n\n    /// Get the [`ModuleHost`] identified by `replica_id` or return an error\n    /// if it is not registered with the controller.\n    ///\n    /// See [`Self::get_or_launch_module_host`] for a variant which launches\n    /// the host if it is not running.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn get_module_host(&self, replica_id: u64) -> Result<ModuleHost, NoSuchModule> {\n        trace!(\"get module host {replica_id}\");\n        let guard = self.acquire_read_lock(replica_id).await.map_err(|_| {\n            warn!(\"timeout waiting for read lock on replica {replica_id} in `get_module_host`\");\n            NoSuchModule\n        })?;\n        guard\n            .as_ref()\n            .map(|Host { module, .. }| module.borrow().clone())\n            .ok_or(NoSuchModule)\n    }\n\n    /// Subscribe to updates of the [`ModuleHost`] identified by `replica_id`,\n    /// or return an error if it is not registered with the controller.\n    ///\n    /// See [`Self::watch_maybe_launch_module_host`] for a variant which\n    /// launches the host if it is not running.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn watch_module_host(&self, replica_id: u64) -> Result<watch::Receiver<ModuleHost>, NoSuchModule> {\n        trace!(\"watch module host {replica_id}\");\n        let guard = self.acquire_read_lock(replica_id).await.map_err(|_| {\n            warn!(\"timeout waiting for read lock on {replica_id} in `watch_module_host`\");\n            NoSuchModule\n        })?;\n        guard\n            .as_ref()\n            .map(|Host { module, .. }| module.subscribe())\n            .ok_or(NoSuchModule)\n    }\n\n    /// `true` if the module host `replica_id` is currently registered with\n    /// the controller.\n    pub async fn has_module_host(&self, replica_id: u64) -> bool {\n        let Ok(maybe_host) = self.acquire_read_lock(replica_id).await else {\n            warn!(\"timeout waiting for read lock on replica {replica_id} in `has_module_host`\");\n            // Technically, we have it.\n            return true;\n        };\n\n        maybe_host.is_some()\n    }\n\n    /// Obtain a snapshot of the replica ids of all hosts currently registered\n    /// with the controller.\n    pub fn managed_replicas(&self) -> IntSet<u64> {\n        self.hosts.lock().keys().copied().collect()\n    }\n\n    /// On-panic callback passed to [`ModuleHost`]s created by this controller.\n    ///\n    /// Removes the module with the given `replica_id` from this controller.\n    fn unregister_fn(&self, replica_id: u64) -> impl Fn() + Send + Sync + 'static + use<> {\n        let hosts = Arc::downgrade(&self.hosts);\n        move || {\n            if let Some(hosts) = hosts.upgrade() {\n                hosts.lock().remove(&replica_id);\n            }\n        }\n    }\n\n    /// Acquire a write lock on the [HostCell] for `replica_id`.\n    ///\n    /// This will time out after 5s to aid debugging of\n    /// https://github.com/clockworklabs/SpacetimeDBPrivate/issues/2337\n    async fn acquire_write_lock(&self, replica_id: u64) -> Result<OwnedRwLockWriteGuard<Option<Host>>, Elapsed> {\n        let lock = self.hosts.lock().entry(replica_id).or_default().clone();\n        timeout(Duration::from_secs(5), lock.write_owned()).await\n    }\n\n    /// Acquire a read lock on the [HostCell] for `replica_id`.\n    ///\n    /// This will time out after 5s to aid debugging of\n    /// https://github.com/clockworklabs/SpacetimeDBPrivate/issues/2337\n    async fn acquire_read_lock(&self, replica_id: u64) -> Result<OwnedRwLockReadGuard<Option<Host>>, Elapsed> {\n        let lock = self.hosts.lock().entry(replica_id).or_default().clone();\n        timeout(Duration::from_secs(5), lock.read_owned()).await\n    }\n\n    async fn try_init_host(&self, database: Database, replica_id: u64) -> anyhow::Result<Host> {\n        let database_identity = database.database_identity;\n        Host::try_init(self, database, replica_id)\n            .await\n            .with_context(|| format!(\"failed to init replica {} for {}\", replica_id, database_identity))\n    }\n}\n\nfn stored_program_hash(db: &RelationalDB) -> anyhow::Result<Option<Hash>> {\n    let meta = db.metadata()?;\n    Ok(meta.map(|meta| meta.program_hash))\n}\n\nasync fn make_replica_ctx(\n    module_logs: Option<ModuleLogsDir>,\n    database: Database,\n    replica_id: u64,\n    relational_db: Arc<RelationalDB>,\n    bsatn_rlb_pool: BsatnRowListBuilderPool,\n) -> anyhow::Result<ReplicaContext> {\n    let logger = match module_logs {\n        Some(path) => asyncify(move || Arc::new(DatabaseLogger::open_today(path))).await,\n        None => Arc::new(DatabaseLogger::in_memory(IN_MEMORY_DATABASE_LOGGER_MAX_SIZE)),\n    };\n\n    let send_worker_queue = spawn_send_worker(Some(database.database_identity));\n    let subscriptions = Arc::new(parking_lot::RwLock::new(SubscriptionManager::new(\n        send_worker_queue.clone(),\n    )));\n    let downgraded = Arc::downgrade(&subscriptions);\n    let subscriptions =\n        ModuleSubscriptions::new(relational_db.clone(), subscriptions, send_worker_queue, bsatn_rlb_pool);\n\n    // If an error occurs when evaluating a subscription,\n    // we mark each client that was affected,\n    // and we remove those clients from the manager async.\n    tokio::spawn(async move {\n        loop {\n            tokio::time::sleep(Duration::from_secs(10)).await;\n            let Some(subscriptions) = downgraded.upgrade() else {\n                break;\n            };\n            // This should happen on the module thread, but we haven't created the module yet.\n            asyncify(move || subscriptions.write().remove_dropped_clients()).await\n        }\n    });\n\n    Ok(ReplicaContext {\n        database,\n        replica_id,\n        logger,\n        subscriptions,\n        relational_db,\n    })\n}\n\n/// Initialize a module host for the given program.\n/// The passed replica_ctx may not be configured for this version of the program's database schema yet.\n#[allow(clippy::too_many_arguments)]\nasync fn make_module_host(\n    runtimes: Arc<HostRuntimes>,\n    replica_ctx: Arc<ReplicaContext>,\n    scheduler: Scheduler,\n    program: Program,\n    energy_monitor: Arc<dyn EnergyMonitor>,\n    unregister: impl Fn() + Send + Sync + 'static,\n    core: AllocatedJobCore,\n) -> anyhow::Result<(Program, ModuleHost)> {\n    // `make_actor` is blocking, as it needs to compile the wasm to native code,\n    // which may be computationally expensive - sometimes up to 1s for a large module.\n    // TODO: change back to using `spawn_rayon` here - asyncify runs on tokio blocking\n    //       threads, but those aren't for computation. Also, wasmtime uses rayon\n    //       to run compilation in parallel, so it'll need to run stuff in rayon anyway.\n    let database_identity = replica_ctx.database_identity;\n\n    let mcc = ModuleCreationContext {\n        replica_ctx,\n        scheduler,\n        program_hash: program.hash,\n        energy_monitor,\n    };\n\n    match HostType::from(program.kind) {\n        HostType::Wasm => {\n            asyncify(move || {\n                let start = Instant::now();\n                let module = runtimes.wasmtime.make_actor(mcc, &program.bytes, core)?;\n                trace!(\"wasmtime::make_actor blocked for {:?}\", start.elapsed());\n                let module_host = ModuleHost::new(module, unregister, database_identity);\n                Ok((program, module_host))\n            })\n            .await\n        }\n        HostType::Js => {\n            let start = Instant::now();\n            let module = runtimes.v8.make_actor(mcc, &program.bytes, core).await?;\n            trace!(\"v8::make_actor blocked for {:?}\", start.elapsed());\n            let module_host = ModuleHost::new(module, unregister, database_identity);\n            Ok((program, module_host))\n        }\n    }\n}\n\nasync fn load_program(storage: &ProgramStorage, hash: Hash) -> anyhow::Result<Box<[u8]>> {\n    storage\n        .lookup(hash)\n        .await?\n        .with_context(|| format!(\"program {hash} not found\"))\n}\n\nstruct LaunchedModule {\n    replica_ctx: Arc<ReplicaContext>,\n    module_host: ModuleHost,\n    scheduler: Scheduler,\n    scheduler_starter: SchedulerStarter,\n}\n\nstruct ModuleLauncher<F> {\n    database: Database,\n    replica_id: u64,\n    program: Program,\n    on_panic: F,\n    relational_db: Arc<RelationalDB>,\n    energy_monitor: Arc<dyn EnergyMonitor>,\n    module_logs: Option<ModuleLogsDir>,\n    runtimes: Arc<HostRuntimes>,\n    core: AllocatedJobCore,\n    bsatn_rlb_pool: BsatnRowListBuilderPool,\n}\n\nimpl<F: Fn() + Send + Sync + 'static> ModuleLauncher<F> {\n    async fn launch_module(self) -> anyhow::Result<(Program, LaunchedModule)> {\n        let db_identity = self.database.database_identity;\n        info!(\n            \"launching module db={} replica={} program={} host_type={}\",\n            db_identity,\n            self.replica_id,\n            self.program.hash,\n            HostType::from(self.program.kind)\n        );\n\n        let replica_ctx = make_replica_ctx(\n            self.module_logs,\n            self.database,\n            self.replica_id,\n            self.relational_db,\n            self.bsatn_rlb_pool,\n        )\n        .await\n        .map(Arc::new)?;\n        let (scheduler, scheduler_starter) = Scheduler::open(replica_ctx.relational_db.clone());\n        let (program, module_host) = make_module_host(\n            self.runtimes.clone(),\n            replica_ctx.clone(),\n            scheduler.clone(),\n            self.program,\n            self.energy_monitor,\n            self.on_panic,\n            self.core,\n        )\n        .await?;\n\n        trace!(\"launched database {} with program {}\", db_identity, program.hash);\n\n        Ok((\n            program,\n            LaunchedModule {\n                replica_ctx,\n                module_host,\n                scheduler,\n                scheduler_starter,\n            },\n        ))\n    }\n}\n\n/// Update a module.\n///\n/// If the `db` is not initialized yet (i.e. its program hash is `None`),\n/// return an error.\n///\n/// Otherwise, if `db.program_hash` matches the given `program_hash`, do\n/// nothing and return an empty `UpdateDatabaseResult`.\n///\n/// Otherwise, invoke `module.update_database` and return the result.\nasync fn update_module(\n    db: &RelationalDB,\n    module: &ModuleHost,\n    program: Program,\n    old_module_info: Arc<ModuleInfo>,\n    policy: MigrationPolicy,\n) -> anyhow::Result<UpdateDatabaseResult> {\n    let addr = db.database_identity();\n    match stored_program_hash(db)? {\n        None => Err(anyhow!(\"database `{addr}` not yet initialized\")),\n        Some(stored) => {\n            let res = if stored == program.hash {\n                info!(\"database `{}` up to date with program `{}`\", addr, program.hash);\n                UpdateDatabaseResult::NoUpdateNeeded\n            } else {\n                info!(\"updating `{}` from {} to {}\", addr, stored, program.hash);\n                module.update_database(program, old_module_info, policy).await?\n            };\n\n            Ok(res)\n        }\n    }\n}\n\n/// Encapsulates a database, associated module, and auxiliary state.\nstruct Host {\n    /// The [`ModuleHost`], providing the callable reducer API.\n    ///\n    /// Modules may be updated via [`Host::update_module`].\n    /// The module is wrapped in a [`watch::Sender`] to allow for \"hot swapping\":\n    /// clients may subscribe to the channel, so they get the most recent\n    /// [`ModuleHost`] version or an error if the [`Host`] was dropped.\n    module: watch::Sender<ModuleHost>,\n    /// Pointer to the `module`'s [`ReplicaContext`].\n    ///\n    /// The database stays the same if and when the module is updated via\n    /// [`Host::update_module`].\n    replica_ctx: Arc<ReplicaContext>,\n    /// Scheduler for repeating reducers, operating on the current `module`.\n    scheduler: Scheduler,\n    /// Handle to the metrics collection task started via [`disk_monitor`].\n    ///\n    /// The task collects metrics from the `replica_ctx`, and so stays alive as long\n    /// as the `replica_ctx` is live. The task is aborted when [`Host`] is dropped.\n    disk_metrics_recorder_task: AbortHandle,\n    /// Handle to the task responsible for recording metrics for each transaction.\n    /// The task is aborted when [`Host`] is dropped.\n    tx_metrics_recorder_task: AbortHandle,\n    /// Handle to the task responsible for cleaning up old views.\n    /// The task is aborted when [`Host`] is dropped.\n    view_cleanup_task: AbortHandle,\n}\n\nimpl Host {\n    /// Attempt to instantiate a [`Host`] from persistent storage.\n    ///\n    /// Note that this does **not** run module initialization routines, but may\n    /// create on-disk artifacts if the host / database did not exist.\n    #[tracing::instrument(level = \"debug\", skip_all)]\n    async fn try_init(host_controller: &HostController, database: Database, replica_id: u64) -> anyhow::Result<Self> {\n        let HostController {\n            data_dir,\n            default_config: config,\n            program_storage,\n            energy_monitor,\n            runtimes,\n            persistence,\n            page_pool,\n            bsatn_rlb_pool,\n            ..\n        } = host_controller;\n        let replica_dir = data_dir.replica(replica_id);\n        let (tx_metrics_queue, tx_metrics_recorder_task) = spawn_tx_metrics_recorder();\n\n        let (db, connected_clients) = match config.storage {\n            db::Storage::Memory => RelationalDB::open(\n                database.database_identity,\n                database.owner_identity,\n                EmptyHistory::new(),\n                None,\n                Some(tx_metrics_queue),\n                page_pool.clone(),\n            )?,\n            db::Storage::Disk => {\n                // Replay from the local state.\n                let history = relational_db::local_history(&replica_dir).await?;\n                let persistence = persistence.persistence(&database, replica_id).await?;\n                // Loading a database from persistent storage involves heavy\n                // blocking I/O. `asyncify` to avoid blocking the async worker.\n                let (db, clients) = asyncify({\n                    let database_identity = database.database_identity;\n                    let owner_identity = database.owner_identity;\n                    let page_pool = page_pool.clone();\n                    move || {\n                        RelationalDB::open(\n                            database_identity,\n                            owner_identity,\n                            history,\n                            Some(persistence),\n                            Some(tx_metrics_queue),\n                            page_pool,\n                        )\n                    }\n                })\n                .await\n                // Make sure we log the source chain of the error\n                // as a single line, with the help of `anyhow`.\n                .map_err(anyhow::Error::from)\n                .inspect_err(|e| {\n                    tracing::error!(\n                        database = %database.database_identity,\n                        replica = replica_id,\n                        \"Failed to open database: {e:#}\"\n                    );\n                })?;\n\n                (db, clients)\n            }\n        };\n        let (mut program, program_needs_init) = match db.program()? {\n            // Launch module with program from existing database.\n            Some(program) => {\n                info!(\n                    \"loaded program {} from the database host-type={}\",\n                    program.hash,\n                    HostType::from(program.kind)\n                );\n                (program, false)\n            }\n            // Database is empty, load program from external storage and run\n            // initialization.\n            None => {\n                info!(\n                    \"loading program {} from external storage host-type={}\",\n                    database.initial_program, database.host_type\n                );\n                let program_bytes = load_program(program_storage, database.initial_program).await?;\n                let program = Program {\n                    hash: database.initial_program,\n                    bytes: program_bytes,\n                    kind: database.host_type.into(),\n                };\n                (program, true)\n            }\n        };\n\n        let relational_db = Arc::new(db);\n        let (program, launched) = match HostType::from(program.kind) {\n            HostType::Js => {\n                ModuleLauncher {\n                    database,\n                    replica_id,\n                    program,\n                    on_panic: host_controller.unregister_fn(replica_id),\n                    relational_db,\n                    energy_monitor: energy_monitor.clone(),\n                    module_logs: match config.storage {\n                        db::Storage::Memory => None,\n                        db::Storage::Disk => Some(replica_dir.module_logs()),\n                    },\n                    runtimes: runtimes.clone(),\n                    core: host_controller.db_cores.take(),\n                    bsatn_rlb_pool: bsatn_rlb_pool.clone(),\n                }\n                .launch_module()\n                .await?\n            }\n            HostType::Wasm => {\n                // Prior to https://github.com/clockworklabs/SpacetimeDB/pull/4549\n                // the host type in `st_module` was always set to wasm.\n                // We now correctly use the host type from the database, but the\n                // module may in fact be a JS module.\n                // So if launching it as a wasm module fails, try JS instead.\n                // If this succeeds, the module is definitely a JS module, so\n                // attempt to repair `st_module` in this case.\n                //\n                // TODO: This code should eventually be removed once all\n                // databases have been repaired.\n                let launch_wasm_result = ModuleLauncher {\n                    database: database.clone(),\n                    replica_id,\n                    program: program.clone(),\n                    on_panic: host_controller.unregister_fn(replica_id),\n                    relational_db: relational_db.clone(),\n                    energy_monitor: energy_monitor.clone(),\n                    module_logs: match config.storage {\n                        db::Storage::Memory => None,\n                        db::Storage::Disk => Some(replica_dir.clone().module_logs()),\n                    },\n                    runtimes: runtimes.clone(),\n                    core: host_controller.db_cores.take(),\n                    bsatn_rlb_pool: bsatn_rlb_pool.clone(),\n                }\n                .launch_module()\n                .await;\n                match launch_wasm_result {\n                    Ok(program_and_module_host) => program_and_module_host,\n                    Err(e) => {\n                        warn!(\"failed to launch wasm module, trying js: {e:#}\");\n\n                        program.kind = ModuleKind::JS;\n                        let res = ModuleLauncher {\n                            database,\n                            replica_id,\n                            program: program.clone(),\n                            on_panic: host_controller.unregister_fn(replica_id),\n                            relational_db: relational_db.clone(),\n                            energy_monitor: energy_monitor.clone(),\n                            module_logs: match config.storage {\n                                db::Storage::Memory => None,\n                                db::Storage::Disk => Some(replica_dir.module_logs()),\n                            },\n                            runtimes: runtimes.clone(),\n                            core: host_controller.db_cores.take(),\n                            bsatn_rlb_pool: bsatn_rlb_pool.clone(),\n                        }\n                        .launch_module()\n                        .await;\n\n                        if res.is_ok() {\n                            let _ = relational_db\n                                .with_auto_commit(Workload::Internal, |tx| relational_db.update_program(tx, program));\n                        }\n\n                        res?\n                    }\n                }\n            }\n        };\n\n        if program_needs_init {\n            let call_result = launched.module_host.init_database(program).await?;\n            if let Some(call_result) = call_result {\n                Result::from(call_result)?;\n            }\n        } else {\n            drop(program)\n        }\n\n        let LaunchedModule {\n            replica_ctx,\n            module_host,\n            scheduler,\n            scheduler_starter,\n        } = launched;\n\n        // Disconnect dangling clients.\n        // No need to clear view tables here since we do it in `clear_all_clients`.\n        for (identity, connection_id) in connected_clients {\n            module_host\n                .call_identity_disconnected(identity, connection_id)\n                .await\n                .with_context(|| {\n                    format!(\n                        \"Error calling disconnect for {} {} on {}\",\n                        identity, connection_id, replica_ctx.database_identity\n                    )\n                })?;\n        }\n        // We should have no clients left, but we do this just in case.\n        // This should only matter if we crashed with something in st_client_credentials,\n        // then restarted with an older version of the code that doesn't use st_client_credentials.\n        // That case would cause some permanently dangling st_client_credentials.\n        // Since we have no clients on startup, this should be safe to do regardless.\n        module_host.clear_all_clients().await?;\n\n        scheduler_starter.start(&module_host)?;\n        let disk_metrics_recorder_task = tokio::spawn(metric_reporter(replica_ctx.clone())).abort_handle();\n        let view_cleanup_task = spawn_view_cleanup_loop(replica_ctx.relational_db.clone());\n\n        let module = watch::Sender::new(module_host);\n\n        Ok(Host {\n            module,\n            replica_ctx,\n            scheduler,\n            disk_metrics_recorder_task,\n            tx_metrics_recorder_task,\n            view_cleanup_task,\n        })\n    }\n\n    /// Construct an in-memory instance of `database` running `program`.\n    ///\n    /// This is used during an initial, fresh publish operation\n    /// in order to check the `program`'s validity as a module,\n    /// since some validity checks we'd like to do (e.g. typechecking RLS filters)\n    /// require a fully instantiated database.\n    ///\n    /// This is not necessary during hotswap publishes,\n    /// as the automigration planner and executor accomplish the same validity checks.\n    async fn try_init_in_memory_to_check(\n        runtimes: &Arc<HostRuntimes>,\n        page_pool: PagePool,\n        database: Database,\n        program: Program,\n        core: AllocatedJobCore,\n        bsatn_rlb_pool: BsatnRowListBuilderPool,\n    ) -> anyhow::Result<(Program, LaunchedModule)> {\n        let (db, _connected_clients) = RelationalDB::open(\n            database.database_identity,\n            database.owner_identity,\n            EmptyHistory::new(),\n            None,\n            None,\n            page_pool,\n        )?;\n\n        ModuleLauncher {\n            database,\n            replica_id: 0,\n            program,\n            // No need to register a callback here:\n            // proper publishes use it to unregister a panicked module,\n            // but this module is not registered in the first place.\n            on_panic: || log::error!(\"launch_module on_panic called for temporary publish in-memory instance\"),\n            relational_db: Arc::new(db),\n            energy_monitor: Arc::new(NullEnergyMonitor),\n            module_logs: None,\n            runtimes: runtimes.clone(),\n            core,\n            bsatn_rlb_pool,\n        }\n        .launch_module()\n        .await\n    }\n\n    /// Attempt to replace this [`Host`]'s [`ModuleHost`] with a new one running\n    /// the program `program_hash`.\n    ///\n    /// The associated [`ReplicaContext`] stays the same.\n    ///\n    /// Executes [`ModuleHost::update_database`] on the newly instantiated\n    /// module, updating the database schema and invoking the `__update__`\n    /// reducer if it is defined.\n    /// If this succeeds, the current module is replaced with the new one,\n    /// otherwise it stays the same.\n    ///\n    /// Either way, the [`UpdateDatabaseResult`] is returned.\n    #[allow(clippy::too_many_arguments)]\n    async fn update_module(\n        &mut self,\n        runtimes: Arc<HostRuntimes>,\n        program: Program,\n        policy: MigrationPolicy,\n        energy_monitor: Arc<dyn EnergyMonitor>,\n        on_panic: impl Fn() + Send + Sync + 'static,\n        core: AllocatedJobCore,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        let replica_ctx = &self.replica_ctx;\n        let (scheduler, scheduler_starter) = Scheduler::open(self.replica_ctx.relational_db.clone());\n\n        let (program, module) = make_module_host(\n            runtimes,\n            replica_ctx.clone(),\n            scheduler.clone(),\n            program,\n            energy_monitor,\n            on_panic,\n            core,\n        )\n        .await?;\n\n        // Get the old module info to diff against when building a migration plan.\n        let old_module_info = self.module.borrow().info.clone();\n\n        let update_result =\n            update_module(&replica_ctx.relational_db, &module, program, old_module_info, policy).await?;\n\n        // Only replace the module + scheduler if the update succeeded.\n        // Otherwise, we want the database to continue running with the old state.\n        match update_result {\n            UpdateDatabaseResult::NoUpdateNeeded | UpdateDatabaseResult::UpdatePerformed => {\n                self.scheduler = scheduler;\n                scheduler_starter.start(&module)?;\n                let old_module = self.module.send_replace(module);\n                old_module.exit().await;\n            }\n\n            // In this case, we need to disconnect all clients connected to the old module\n            UpdateDatabaseResult::UpdatePerformedWithClientDisconnect => {\n                // Replace the module first, so that new clients get the new module.\n                let old_watcher = std::mem::replace(&mut self.module, watch::Sender::new(module.clone()));\n\n                // Disconnect all clients connected to the old module.\n                let connected_clients = replica_ctx.relational_db.connected_clients()?;\n                for (identity, connection_id) in connected_clients {\n                    let client_actor_id = ClientActorId {\n                        identity,\n                        connection_id,\n                        name: ClientName(0),\n                    };\n                    //NOTE: This will call disconnect reducer of the new module, not the old one.\n                    //It makes sense, as relationaldb is already updated to the new module.\n                    module.disconnect_client(client_actor_id).await;\n                }\n\n                self.scheduler = scheduler;\n                scheduler_starter.start(&module)?;\n                // exit the old module, drop the `old_watcher` afterwards,\n                // which will signal websocket clients that the module is gone.\n                let old_module = old_watcher.borrow().clone();\n                old_module.exit().await;\n            }\n            _ => {}\n        }\n\n        Ok(update_result)\n    }\n\n    /// Generate a migration plan for the given `program`.\n    async fn migrate_plan(\n        &self,\n        page_pool: PagePool,\n        bsatn_rlb_pool: BsatnRowListBuilderPool,\n        host_runtimes: &Arc<HostRuntimes>,\n        host_type: HostType,\n        program: Program,\n        style: PrettyPrintStyle,\n    ) -> anyhow::Result<MigratePlanResult> {\n        let old_module = self.module.borrow().info.clone();\n\n        let module_def =\n            extract_schema_with_pools(page_pool, bsatn_rlb_pool, host_runtimes, program.bytes, host_type).await?;\n        let major_version_upgrade = matches!(\n            (\n                old_module.module_def.raw_module_def_version(),\n                module_def.raw_module_def_version()\n            ),\n            (RawModuleDefVersion::V9OrEarlier, RawModuleDefVersion::V10)\n        );\n\n        let res = match ponder_migrate(&old_module.module_def, &module_def) {\n            Ok(plan) => MigratePlanResult::Success {\n                old_module_hash: old_module.module_hash,\n                new_module_hash: program.hash,\n                breaks_client: plan.breaks_client(),\n                plan: plan.pretty_print(style)?.into(),\n                major_version_upgrade,\n            },\n            Err(e) => MigratePlanResult::AutoMigrationError {\n                error: e,\n                major_version_upgrade,\n            },\n        };\n\n        Ok(res)\n    }\n}\n\nimpl Drop for Host {\n    fn drop(&mut self) {\n        info!(\n            \"dropping host {}/{}\",\n            self.replica_ctx.database.database_identity, self.replica_ctx.replica_id\n        );\n        self.disk_metrics_recorder_task.abort();\n        self.tx_metrics_recorder_task.abort();\n        self.view_cleanup_task.abort();\n    }\n}\n\npub enum MigratePlanResult {\n    Success {\n        old_module_hash: Hash,\n        new_module_hash: Hash,\n        plan: Box<str>,\n        breaks_client: bool,\n        major_version_upgrade: bool,\n    },\n    AutoMigrationError {\n        error: ErrorStream<AutoMigrateError>,\n        major_version_upgrade: bool,\n    },\n}\n\nconst STORAGE_METERING_INTERVAL: Duration = Duration::from_secs(15);\n\n/// Periodically collect gauge stats and update prometheus metrics.\nasync fn metric_reporter(replica_ctx: Arc<ReplicaContext>) {\n    // TODO: Consider adding a metric for heap usage.\n    let message_log_size = DB_METRICS\n        .message_log_size\n        .with_label_values(&replica_ctx.database_identity);\n    let message_log_blocks = DB_METRICS\n        .message_log_blocks\n        .with_label_values(&replica_ctx.database_identity);\n    let module_log_file_size = DB_METRICS\n        .module_log_file_size\n        .with_label_values(&replica_ctx.database_identity);\n\n    loop {\n        let ctx = replica_ctx.clone();\n        // We spawn a blocking task here because this grabs blocking locks.\n        let disk_usage_future = tokio::task::spawn_blocking(move || {\n            ctx.update_gauges();\n            ctx.total_disk_usage()\n        });\n        if let Ok(disk_usage) = disk_usage_future.await {\n            if let Some(SizeOnDisk {\n                total_bytes,\n                total_blocks,\n            }) = disk_usage.durability\n            {\n                message_log_size.set(total_bytes as i64);\n                message_log_blocks.set(total_blocks as i64);\n            }\n\n            if let Some(num_bytes) = disk_usage.logs {\n                module_log_file_size.set(num_bytes as i64);\n            }\n        }\n        tokio::time::sleep(STORAGE_METERING_INTERVAL).await;\n    }\n}\n\n/// Extracts the schema from a given module.\n///\n/// Spins up a dummy host and returns the `ModuleDef` that it extracts.\npub(crate) async fn extract_schema_with_pools(\n    page_pool: PagePool,\n    bsatn_rlb_pool: BsatnRowListBuilderPool,\n    runtimes: &Arc<HostRuntimes>,\n    program_bytes: Box<[u8]>,\n    host_type: HostType,\n) -> anyhow::Result<ModuleDef> {\n    let owner_identity = Identity::from_u256(0xdcba_u32.into());\n    let database_identity = Identity::from_u256(0xabcd_u32.into());\n    let program = Program::from_bytes(host_type.into(), program_bytes);\n\n    let database = Database {\n        id: 0,\n        database_identity,\n        owner_identity,\n        host_type,\n        initial_program: program.hash,\n    };\n\n    let core = AllocatedJobCore::default();\n    // Limit the scope of the launched module just to this block.\n    let module_info = {\n        let (_, launched) =\n            Host::try_init_in_memory_to_check(runtimes, page_pool, database, program, core, bsatn_rlb_pool).await?;\n        launched.module_host.info\n    };\n    // this should always succeed, but sometimes it doesn't\n    let module_def = match Arc::try_unwrap(module_info) {\n        Ok(info) => Arc::try_unwrap(info.module_def).unwrap_or_else(|module_def| (*module_def).clone()),\n        Err(info) => (*info.module_def).clone(),\n    };\n\n    Ok(module_def)\n}\n\n/// Extracts the schema from a given module.\n///\n/// Spins up a dummy host and returns the `ModuleDef` that it extracts.\npub async fn extract_schema(program_bytes: Box<[u8]>, host_type: HostType) -> anyhow::Result<ModuleDef> {\n    extract_schema_with_pools(\n        PagePool::new(None),\n        BsatnRowListBuilderPool::new(),\n        &HostRuntimes::new(None),\n        program_bytes,\n        host_type,\n    )\n    .await\n}\n\n// Remove all gauges associated with a database.\n// This is useful if a database is being deleted.\npub fn remove_database_gauges<'a, I>(db: &Identity, table_names: I)\nwhere\n    I: IntoIterator<Item = &'a str>,\n{\n    // Remove the per-table gauges.\n    for table_name in table_names {\n        let _ = DATA_SIZE_METRICS\n            .data_size_table_num_rows\n            .remove_label_values(db, table_name);\n        let _ = DATA_SIZE_METRICS\n            .data_size_table_bytes_used_by_rows\n            .remove_label_values(db, table_name);\n        let _ = DATA_SIZE_METRICS\n            .data_size_table_num_rows_in_indexes\n            .remove_label_values(db, table_name);\n        let _ = DATA_SIZE_METRICS\n            .data_size_table_bytes_used_by_index_keys\n            .remove_label_values(db, table_name);\n    }\n    // Remove the per-db gauges.\n    let _ = DATA_SIZE_METRICS.data_size_blob_store_num_blobs.remove_label_values(db);\n    let _ = DATA_SIZE_METRICS\n        .data_size_blob_store_bytes_used_by_blobs\n        .remove_label_values(db);\n    let _ = WORKER_METRICS.wasm_memory_bytes.remove_label_values(db);\n}\n"
  },
  {
    "path": "crates/core/src/host/instance_env.rs",
    "content": "use super::scheduler::{get_schedule_from_row, ScheduleError, Scheduler};\nuse crate::database_logger::{BacktraceFrame, BacktraceProvider, LogLevel, ModuleBacktrace, Record};\nuse crate::db::relational_db::{MutTx, RelationalDB};\nuse crate::error::{DBError, DatastoreError, IndexError, NodesError};\nuse crate::host::module_host::{DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall};\nuse crate::host::wasm_common::TimingSpan;\nuse crate::replica_context::ReplicaContext;\nuse crate::subscription::module_subscription_actor::{commit_and_broadcast_event, ModuleSubscriptions};\nuse crate::subscription::module_subscription_manager::{from_tx_offset, TransactionOffset};\nuse crate::util::prometheus_handle::IntGaugeExt;\nuse chrono::{DateTime, Utc};\nuse core::mem;\nuse futures::TryFutureExt;\nuse parking_lot::{Mutex, MutexGuard};\nuse smallvec::SmallVec;\nuse spacetimedb_client_api_messages::energy::EnergyQuanta;\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId};\nuse spacetimedb_datastore::traits::IsolationLevel;\nuse spacetimedb_lib::{http as st_http, ConnectionId, Identity, Timestamp};\nuse spacetimedb_primitives::{ColId, ColList, IndexId, TableId};\nuse spacetimedb_sats::{\n    bsatn::{self, ToBsatn},\n    buffer::{CountWriter, TeeWriter},\n    AlgebraicValue, ProductValue,\n};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_table::indexes::RowPointer;\nuse spacetimedb_table::table::RowRef;\nuse std::fmt::Display;\nuse std::future::Future;\nuse std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};\nuse std::ops::DerefMut;\nuse std::sync::Arc;\nuse std::time::{Duration, Instant};\nuse std::vec::IntoIter;\n\npub struct InstanceEnv {\n    pub replica_ctx: Arc<ReplicaContext>,\n    pub scheduler: Scheduler,\n    pub tx: TxSlot,\n    /// The timestamp the current function began running.\n    pub start_time: Timestamp,\n    /// The instant the current function began running.\n    pub start_instant: Instant,\n    /// The type of the last, including current, function to be executed by this environment.\n    pub func_type: FuncCallType,\n    /// The name of the last, including current, function to be executed by this environment.\n    pub func_name: Option<Identifier>,\n    /// Are we in an anonymous tx context?\n    in_anon_tx: bool,\n    /// A procedure's last known transaction offset.\n    procedure_last_tx_offset: Option<TransactionOffset>,\n}\n\n/// `InstanceEnv` needs to be `Send` because it is created on the host thread\n/// and moved to module threads for execution (see [`ModuleHost::with_instance`]).\n///\n/// `TxSlot` must be `None` whenever `InstanceEnv` is moved across threads, which is\n/// not enforced at compile time but seems to be upheld in practice.\n///\n/// In the future, we may push to use `InstanceEnv` only within a module thread,\n/// but this still helps prevent a set of bugs that occurred due to `MutTxId` being `Send`,\n/// such as:\n/// https://github.com/clockworklabs/SpacetimeDB/pull/3938 and\n/// https://github.com/clockworklabs/SpacetimeDB/pull/3968.\n/// `InstanceEnv` needs to be `Send` because it is created on the host thread\n/// and moved to module threads for execution (see [`ModuleHost::with_instance`]).\n///\n/// `TxSlot` must be `None` whenever `InstanceEnv` is moved across threads, which is\n/// not enforced at compile time but seems to be upheld in practice.\n///\n/// In the future, we may push to use `InstanceEnv` only within a module thread,\n/// but this still helps prevent a set of bugs that occurred due to `MutTxId` being `Send`,\n/// such as:\n/// https://github.com/clockworklabs/SpacetimeDB/pull/3938 and\n/// https://github.com/clockworklabs/SpacetimeDB/pull/3968.\n///\n/// # Safety\n///\n/// `InstanceEnv` doesn't auto-derive `Send` because it may hold a `MutTxId`,\n/// which we've manually made `!Send` to preserve logical invariants.\n/// As described above, sending an `InstanceEnv` while it holds a `MutTxId` will violate logical invariants,\n/// but this is not a safety concern.\n/// Transferring a `MutTxId` between threads will never cause Undefined Behavior,\n/// though it is likely to lead to deadlocks.\nunsafe impl Send for InstanceEnv {}\n\n#[derive(Clone, Default)]\npub struct TxSlot {\n    // Wrapped in Mutex for interior mutability.\n    inner: Arc<Mutex<Option<MutTxId>>>,\n}\n\n/// The maximum number of chunks stored in a single [`ChunkPool`].\n///\n/// When returning a chunk to the pool via [`ChunkPool::put`],\n/// if the pool contains more than [`MAX_CHUNKS_IN_POOL`] chunks,\n/// the returned chunk will be freed rather than added to the pool.\n///\n/// This, together with [`MAX_CHUNK_SIZE_IN_BYTES`],\n/// prevents the heap usage of a [`ChunkPool`] from growing without bound.\n///\n/// This number chosen completely arbitrarily by pgoldman 2025-04-10.\nconst MAX_CHUNKS_IN_POOL: usize = 32;\n\n/// The maximum size of chunks which can be saved in a [`ChunkPool`].\n///\n/// When returning a chunk to the pool via [`ChunkPool::put`],\n/// if the returned chunk is larger than [`MAX_CHUNK_SIZE_IN_BYTES`],\n/// the returned chunk will be freed rather than added to the pool.\n///\n/// This, together with [`MAX_CHUNKS_IN_POOL`],\n/// prevents the heap usage of a [`ChunkPool`] from growing without bound.\n///\n/// We switch to a new chunk when we pass ROW_ITER_CHUNK_SIZE, so this adds a buffer of 4x.\nconst MAX_CHUNK_SIZE_IN_BYTES: usize = spacetimedb_primitives::ROW_ITER_CHUNK_SIZE * 4;\n\n/// A pool of available unused chunks.\n///\n/// The number of chunks stored in a `ChunkPool` is limited by [`MAX_CHUNKS_IN_POOL`],\n/// and the size of each individual saved chunk is limited by [`MAX_CHUNK_SIZE_IN_BYTES`].\n#[derive(Default)]\npub struct ChunkPool {\n    free_chunks: Vec<Vec<u8>>,\n}\n\nimpl ChunkPool {\n    /// Takes an unused chunk from this pool\n    /// or creates a new chunk if none are available.\n    /// New chunks are not actually allocated,\n    /// but will be, on first use.\n    fn take(&mut self) -> Vec<u8> {\n        self.free_chunks.pop().unwrap_or_default()\n    }\n\n    /// Return a chunk back to the pool, or frees it, as appropriate.\n    ///\n    /// `chunk` will be freed if either:\n    ///\n    /// - `self` already contains at least [`MAX_CHUNKS_IN_POOL`] chunks, or\n    /// - `chunk.capacity()` is greater than [`MAX_CHUNK_SIZE_IN_BYTES`].\n    ///\n    /// These limits place an upper bound on the memory usage of a single [`ChunkPool`].\n    pub fn put(&mut self, mut chunk: Vec<u8>) {\n        if chunk.capacity() > MAX_CHUNK_SIZE_IN_BYTES {\n            return;\n        }\n        if self.free_chunks.len() > MAX_CHUNKS_IN_POOL {\n            return;\n        }\n        chunk.clear();\n        self.free_chunks.push(chunk);\n    }\n}\n\n/// Construct a new `ChunkedWriter` using [`Self::new`].\n/// Do not impl `Default` for this struct or construct it manually;\n/// it is important that all allocated chunks are taken from the [`ChunkPool`],\n/// rather than directly from the global allocator.\nstruct ChunkedWriter {\n    /// Chunks collected thus far.\n    chunks: Vec<Vec<u8>>,\n    /// Current in progress chunk that will be added to `chunks`.\n    curr: Vec<u8>,\n}\n\nimpl ChunkedWriter {\n    /// Flushes the data collected in the current chunk\n    /// if it's larger than our chunking threshold.\n    fn flush(&mut self, pool: &mut ChunkPool) {\n        if self.curr.len() > spacetimedb_primitives::ROW_ITER_CHUNK_SIZE {\n            let curr = mem::replace(&mut self.curr, pool.take());\n            self.chunks.push(curr);\n        }\n    }\n\n    /// Creates a new `ChunkedWriter` with an empty chunk allocated from the pool.\n    fn new(pool: &mut ChunkPool) -> Self {\n        Self {\n            chunks: Vec::new(),\n            curr: pool.take(),\n        }\n    }\n\n    /// Finalises the writer and returns all the chunks.\n    fn into_chunks(mut self) -> Vec<Vec<u8>> {\n        if !self.curr.is_empty() {\n            self.chunks.push(self.curr);\n        }\n        self.chunks\n    }\n\n    pub fn collect_iter(\n        pool: &mut ChunkPool,\n        iter: impl Iterator<Item = impl ToBsatn>,\n    ) -> (Vec<Vec<u8>>, usize, usize) {\n        // Track the number of rows and the number of bytes scanned by the iterator.\n        let mut rows_scanned = 0;\n        let mut bytes_scanned = 0;\n\n        let mut chunked_writer = Self::new(pool);\n        // Consume the iterator, serializing each `item`,\n        // while allowing a chunk to be created at boundaries.\n        for item in iter {\n            // Write the item directly to the BSATN `chunked_writer` buffer.\n            item.to_bsatn_extend(&mut chunked_writer.curr).unwrap();\n            // Flush at item boundaries.\n            chunked_writer.flush(pool);\n            // Update rows scanned.\n            rows_scanned += 1;\n        }\n\n        let chunks = chunked_writer.into_chunks();\n\n        // Update (BSATN) bytes scanned\n        bytes_scanned += chunks.iter().map(|chunk| chunk.len()).sum::<usize>();\n\n        (chunks, rows_scanned, bytes_scanned)\n    }\n}\n\n// Generic 'instance environment' delegated to from various host types.\nimpl InstanceEnv {\n    pub fn new(replica_ctx: Arc<ReplicaContext>, scheduler: Scheduler) -> Self {\n        Self {\n            replica_ctx,\n            scheduler,\n            tx: TxSlot::default(),\n            start_time: Timestamp::now(),\n            start_instant: Instant::now(),\n            // arbitrary - change if we need to recognize that an `InstanceEnv` has never\n            // run a function\n            func_type: FuncCallType::Reducer,\n            func_name: None,\n            in_anon_tx: false,\n            procedure_last_tx_offset: None,\n        }\n    }\n\n    /// Returns the database's identity.\n    pub fn database_identity(&self) -> &Identity {\n        &self.replica_ctx.database.database_identity\n    }\n\n    /// Signal to this `InstanceEnv` that a function call is beginning.\n    pub fn start_funcall(&mut self, name: Identifier, ts: Timestamp, func_type: FuncCallType) {\n        self.start_time = ts;\n        self.start_instant = Instant::now();\n        self.func_type = func_type;\n        self.func_name = Some(name);\n    }\n\n    /// Returns the name of the most recent reducer to be run in this environment,\n    /// or `None` if no reducer is actively being invoked.\n    pub fn log_record_function(&self) -> Option<&str> {\n        self.func_name.as_deref()\n    }\n\n    /// Swap in a temporary function type, returning the previous one.\n    pub fn swap_func_type(&mut self, func_type: FuncCallType) -> FuncCallType {\n        mem::replace(&mut self.func_type, func_type)\n    }\n\n    fn get_tx(&self) -> Result<impl DerefMut<Target = MutTxId> + '_, GetTxError> {\n        self.tx.get()\n    }\n\n    /// True if `self` is holding an open transaction, or false if it is not.\n    pub fn in_tx(&self) -> bool {\n        self.get_tx().is_ok()\n    }\n\n    pub(crate) fn take_tx(&self) -> Result<MutTxId, GetTxError> {\n        self.tx.take()\n    }\n\n    pub(crate) fn relational_db(&self) -> &Arc<RelationalDB> {\n        &self.replica_ctx.relational_db\n    }\n\n    pub(crate) fn get_jwt_payload(&self, connection_id: ConnectionId) -> Result<Option<String>, NodesError> {\n        let tx = &mut *self.get_tx()?;\n        Ok(tx.get_jwt_payload(connection_id).map_err(DBError::from)?)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub(crate) fn console_log(&self, level: LogLevel, record: &Record, bt: &dyn BacktraceProvider) {\n        self.replica_ctx.logger.write(level, record, bt);\n        log::trace!(\n            \"MOD({}): {}\",\n            self.replica_ctx.database_identity.to_abbreviated_hex(),\n            record.message\n        );\n    }\n\n    /// Logs a simple `message` at `level`.\n    pub(crate) fn console_log_simple_message(&self, level: LogLevel, function: Option<&str>, message: &str) {\n        /// A backtrace provider that provides nothing.\n        struct Noop;\n        impl BacktraceProvider for Noop {\n            fn capture(&self) -> Box<dyn ModuleBacktrace> {\n                Box::new(Noop)\n            }\n        }\n        impl ModuleBacktrace for Noop {\n            fn frames(&self) -> Vec<BacktraceFrame<'_>> {\n                Vec::new()\n            }\n        }\n\n        let record = Record {\n            ts: Self::now_for_logging(),\n            target: None,\n            filename: None,\n            line_number: None,\n            function,\n            message,\n        };\n        self.console_log(level, &record, &Noop);\n    }\n\n    /// End a console timer by logging the span at INFO level.\n    pub(crate) fn console_timer_end(&self, span: &TimingSpan, function: Option<&str>) {\n        let elapsed = span.start.elapsed();\n        let message = format!(\"Timing span {:?}: {:?}\", &span.name, elapsed);\n\n        self.console_log_simple_message(LogLevel::Info, function, &message);\n    }\n\n    /// Returns the current time suitable for logging.\n    pub fn now_for_logging() -> DateTime<Utc> {\n        // TODO: figure out whether to use walltime now or logical reducer now (env.reducer_start).\n        chrono::Utc::now()\n    }\n\n    /// Project `cols` in `row_ref` encoded in BSATN to `buffer`\n    /// and return the full length of the BSATN.\n    ///\n    /// Assumes that the full encoding of `cols` will fit in `buffer`.\n    fn project_cols_bsatn(buffer: &mut [u8], cols: ColList, row_ref: RowRef<'_>) -> usize {\n        // We get back a col-list with the columns with generated values.\n        // Write those back to `buffer` and then the encoded length to `row_len`.\n        let counter = CountWriter::default();\n        let mut writer = TeeWriter::new(counter, buffer);\n        for col in cols.iter() {\n            // Read the column value to AV and then serialize.\n            let val = row_ref\n                .read_col::<AlgebraicValue>(col)\n                .expect(\"reading col as AV never panics\");\n            bsatn::to_writer(&mut writer, &val).unwrap();\n        }\n        writer.w1.finish()\n    }\n\n    pub fn insert(&self, table_id: TableId, buffer: &mut [u8]) -> Result<usize, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        let (row_len, row_ptr, insert_flags) = stdb\n            .insert(tx, table_id, buffer)\n            .map(|(gen_cols, row_ref, insert_flags)| {\n                let row_len = Self::project_cols_bsatn(buffer, gen_cols, row_ref);\n                (row_len, row_ref.pointer(), insert_flags)\n            })\n            .inspect_err(\n                #[cold]\n                #[inline(never)]\n                |e| match e {\n                    DBError::Datastore(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => {}\n                    _ => {\n                        let res = stdb.table_name_from_id_mut(tx, table_id);\n                        if let Ok(Some(table_name)) = res {\n                            log::debug!(\"insert(table: {table_name}, table_id: {table_id}): {e}\")\n                        } else {\n                            log::debug!(\"insert(table_id: {table_id}): {e}\")\n                        }\n                    }\n                },\n            )?;\n\n        if insert_flags.is_scheduler_table {\n            self.schedule_row(stdb, tx, table_id, row_ptr)?;\n        }\n\n        // Note, we update the metric for bytes written after the insert.\n        // This is to capture auto-inc columns.\n        tx.metrics.bytes_written += buffer.len();\n\n        Ok(row_len)\n    }\n\n    #[cold]\n    #[inline(never)]\n    fn schedule_row(\n        &self,\n        stdb: &RelationalDB,\n        tx: &mut MutTx,\n        table_id: TableId,\n        row_ptr: RowPointer,\n    ) -> Result<(), NodesError> {\n        let (id_column, at_column) = stdb\n            .table_scheduled_id_and_at(tx, table_id)?\n            .expect(\"schedule_row should only be called when we know its a scheduler table\");\n\n        let row_ref = tx.get(table_id, row_ptr).map_err(DBError::from)?.unwrap();\n        let (schedule_id, schedule_at) = get_schedule_from_row(&row_ref, id_column, at_column)\n            // NOTE(centril): Should never happen,\n            // as we successfully inserted and thus `ret` is verified against the table schema.\n            .map_err(|e| NodesError::ScheduleError(ScheduleError::DecodingError(e)))?;\n        self.scheduler\n            .schedule(\n                table_id,\n                schedule_id,\n                schedule_at,\n                id_column,\n                at_column,\n                self.start_time,\n            )\n            .map_err(NodesError::ScheduleError)?;\n\n        Ok(())\n    }\n\n    pub fn update(&self, table_id: TableId, index_id: IndexId, buffer: &mut [u8]) -> Result<usize, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        let (row_len, row_ptr, update_flags) = stdb\n            .update(tx, table_id, index_id, buffer)\n            .map(|(gen_cols, row_ref, update_flags)| {\n                let row_len = Self::project_cols_bsatn(buffer, gen_cols, row_ref);\n                (row_len, row_ref.pointer(), update_flags)\n            })\n            .inspect_err(\n                #[cold]\n                #[inline(never)]\n                |e| match e {\n                    DBError::Datastore(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => {}\n                    _ => {\n                        let res = stdb.table_name_from_id_mut(tx, table_id);\n                        if let Ok(Some(table_name)) = res {\n                            log::debug!(\"update(table: {table_name}, table_id: {table_id}, index_id: {index_id}): {e}\")\n                        } else {\n                            log::debug!(\"update(table_id: {table_id}, index_id: {index_id}): {e}\")\n                        }\n                    }\n                },\n            )?;\n\n        if update_flags.is_scheduler_table {\n            self.schedule_row(stdb, tx, table_id, row_ptr)?;\n        }\n        tx.metrics.bytes_written += buffer.len();\n        tx.metrics.rows_updated += 1;\n\n        Ok(row_len)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_delete_by_index_scan_point_bsatn(\n        &self,\n        index_id: IndexId,\n        point: &[u8],\n    ) -> Result<u32, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Find all rows in the table to delete.\n        let (table_id, _, iter) = stdb.index_scan_point(tx, index_id, point)?;\n        // Re. `SmallVec`, `delete_by_field` only cares about 1 element, so optimize for that.\n        let rows_to_delete = iter.map(|row_ref| row_ref.pointer()).collect::<SmallVec<[_; 1]>>();\n\n        Ok(Self::datastore_delete_by_index_scan(stdb, tx, table_id, rows_to_delete))\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_delete_by_index_scan_range_bsatn(\n        &self,\n        index_id: IndexId,\n        prefix: &[u8],\n        prefix_elems: ColId,\n        rstart: &[u8],\n        rend: &[u8],\n    ) -> Result<u32, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Find all rows in the table to delete.\n        let (table_id, _, _, iter) = stdb.index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?;\n        // Re. `SmallVec`, `delete_by_field` only cares about 1 element, so optimize for that.\n        let rows_to_delete = iter.map(|row_ref| row_ref.pointer()).collect::<SmallVec<[_; 1]>>();\n\n        Ok(Self::datastore_delete_by_index_scan(stdb, tx, table_id, rows_to_delete))\n    }\n\n    /// Deletes `rows_to_delete` in `tx`\n    /// and assumes `rows_to_delete` came from an index scan.\n    fn datastore_delete_by_index_scan(\n        stdb: &RelationalDB,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        rows_to_delete: SmallVec<[RowPointer; 1]>,\n    ) -> u32 {\n        // Note, we're deleting rows based on the result of an index scan.\n        // Hence we must update our `index_seeks` and `rows_scanned` metrics.\n        //\n        // Note that we're not updating `bytes_scanned` at all,\n        // because we never dereference any of the returned `RowPointer`s.\n        tx.metrics.index_seeks += 1;\n        tx.metrics.rows_scanned += rows_to_delete.len();\n\n        // Delete them and count how many we deleted.\n        stdb.delete(tx, table_id, rows_to_delete)\n    }\n\n    /// Deletes all rows in the table identified by `table_id`\n    /// where the rows match one in `relation`\n    /// which is a bsatn encoding of `Vec<ProductValue>`.\n    ///\n    /// Returns an error if\n    /// - not in a transaction.\n    /// - the table didn't exist.\n    /// - a row couldn't be decoded to the table schema type.\n    #[tracing::instrument(level = \"trace\", skip(self, relation))]\n    pub fn datastore_delete_all_by_eq_bsatn(&self, table_id: TableId, relation: &[u8]) -> Result<u32, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Track the number of bytes coming from the caller\n        tx.metrics.bytes_scanned += relation.len();\n\n        // Find the row schema using it to decode a vector of product values.\n        let row_ty = stdb.row_schema_for_table(tx, table_id)?;\n        // `TableType::delete` cares about a single element\n        // so in that case we can avoid the allocation by using `smallvec`.\n        let relation = ProductValue::decode_smallvec(&row_ty, &mut &*relation).map_err(NodesError::DecodeRow)?;\n\n        // Note, we track the number of rows coming from the caller,\n        // regardless of whether or not we actually delete them,\n        // since we have to derive row ids for each one of them.\n        tx.metrics.rows_scanned += relation.len();\n\n        // Delete them and return how many we deleted.\n        Ok(stdb.delete_by_rel(tx, table_id, relation))\n    }\n\n    /// Returns the `table_id` associated with the given `table_name`.\n    ///\n    /// Errors with `GetTxError` if not in a transaction\n    /// and `TableNotFound` if the table does not exist.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn table_id_from_name(&self, table_name: &str) -> Result<TableId, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Query the table id from the name.\n        stdb.table_id_from_name_mut(tx, table_name)?\n            .ok_or(NodesError::TableNotFound)\n    }\n\n    /// Returns the `index_id` associated with the given `index_name`.\n    ///\n    /// Errors with `GetTxError` if not in a transaction\n    /// and `IndexNotFound` if the index does not exist.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn index_id_from_name(&self, index_name: &str) -> Result<IndexId, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Query the index id from the name.\n        stdb.index_id_from_name_mut(tx, index_name)?\n            .ok_or(NodesError::IndexNotFound)\n    }\n\n    /// Returns the number of rows in the table identified by `table_id`.\n    ///\n    /// Errors with `GetTxError` if not in a transaction\n    /// and `TableNotFound` if the table does not exist.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_table_row_count(&self, table_id: TableId) -> Result<u64, NodesError> {\n        let stdb = self.relational_db();\n        let tx = &mut *self.get_tx()?;\n\n        // Query the row count for id.\n        stdb.table_row_count_mut(tx, table_id)\n            .ok_or(NodesError::TableNotFound)\n            .inspect(|_| {\n                tx.record_table_scan(&self.func_type, table_id);\n            })\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_table_scan_bsatn_chunks(\n        &self,\n        pool: &mut ChunkPool,\n        table_id: TableId,\n    ) -> Result<Vec<Vec<u8>>, NodesError> {\n        let tx = &mut *self.get_tx()?;\n\n        // Open the iterator.\n        let iter = self.relational_db().iter_mut(tx, table_id)?;\n\n        // Scan the index and serialize rows to BSATN.\n        let (chunks, rows_scanned, bytes_scanned) = ChunkedWriter::collect_iter(pool, iter);\n\n        // Record the number of rows and the number of bytes scanned by the iterator.\n        tx.metrics.bytes_scanned += bytes_scanned;\n        tx.metrics.rows_scanned += rows_scanned;\n\n        tx.record_table_scan(&self.func_type, table_id);\n\n        Ok(chunks)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_index_scan_point_bsatn_chunks(\n        &self,\n        pool: &mut ChunkPool,\n        index_id: IndexId,\n        point: &[u8],\n    ) -> Result<Vec<Vec<u8>>, NodesError> {\n        let tx = &mut *self.get_tx()?;\n\n        // Open index iterator\n        let (table_id, point, iter) = self.relational_db().index_scan_point(tx, index_id, point)?;\n\n        // Scan the index and serialize rows to BSATN.\n        let (chunks, rows_scanned, bytes_scanned) = ChunkedWriter::collect_iter(pool, iter);\n\n        // Record the number of rows and the number of bytes scanned by the iterator.\n        tx.metrics.index_seeks += 1;\n        tx.metrics.bytes_scanned += bytes_scanned;\n        tx.metrics.rows_scanned += rows_scanned;\n\n        tx.record_index_scan_point(&self.func_type, table_id, index_id, point);\n\n        Ok(chunks)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_index_scan_range_bsatn_chunks(\n        &self,\n        pool: &mut ChunkPool,\n        index_id: IndexId,\n        prefix: &[u8],\n        prefix_elems: ColId,\n        rstart: &[u8],\n        rend: &[u8],\n    ) -> Result<Vec<Vec<u8>>, NodesError> {\n        let tx = &mut *self.get_tx()?;\n\n        // Open index iterator\n        let (table_id, lower, upper, iter) =\n            self.relational_db()\n                .index_scan_range(tx, index_id, prefix, prefix_elems, rstart, rend)?;\n\n        // Scan the index and serialize rows to BSATN.\n        let (chunks, rows_scanned, bytes_scanned) = ChunkedWriter::collect_iter(pool, iter);\n\n        // Record the number of rows and the number of bytes scanned by the iterator.\n        tx.metrics.index_seeks += 1;\n        tx.metrics.bytes_scanned += bytes_scanned;\n        tx.metrics.rows_scanned += rows_scanned;\n\n        tx.record_index_scan_range(&self.func_type, table_id, index_id, lower, upper);\n\n        Ok(chunks)\n    }\n\n    pub fn fill_buffer_from_iter(\n        iter: &mut IntoIter<Vec<u8>>,\n        mut buffer: &mut [u8],\n        chunk_pool: &mut ChunkPool,\n    ) -> usize {\n        let mut written = 0;\n        // Fill the buffer as much as possible.\n        while let Some(chunk) = iter.as_slice().first() {\n            let Some((buf_chunk, rest)) = buffer.split_at_mut_checked(chunk.len()) else {\n                // Cannot fit chunk into the buffer,\n                // either because we already filled it too much,\n                // or because it is too small.\n                break;\n            };\n            buf_chunk.copy_from_slice(chunk);\n            written += chunk.len();\n            buffer = rest;\n\n            // Advance the iterator, as we used a chunk.\n            // SAFETY: We peeked one `chunk`, so there must be one at least.\n            let chunk = unsafe { iter.next().unwrap_unchecked() };\n            chunk_pool.put(chunk);\n        }\n\n        written\n    }\n\n    // Async procedure syscalls return a `Result<impl Future>`, so that we can check `get_tx()`\n    // *before* requiring an async runtime. Otherwise, the v8 module host would have to call\n    // on `tokio::runtime::Handle::try_current()` before being able to run the `get_tx()` check.\n\n    pub fn start_mutable_tx(&mut self) -> Result<(), NodesError> {\n        if self.get_tx().is_ok() {\n            return Err(NodesError::WouldBlockTransaction(\n                super::AbiCall::ProcedureStartMutTransaction,\n            ));\n        }\n\n        let stdb = self.replica_ctx.relational_db.clone();\n        // TODO(procedure-tx): should we add a new workload, e.g., `AnonTx`?\n        let tx = stdb.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);\n        self.tx.set_raw(tx);\n        self.in_anon_tx = true;\n\n        Ok(())\n    }\n\n    /// Finishes an anonymous transaction,\n    /// returning `Some(_)` if there was no ongoing one,\n    /// in which case the caller should return early.\n    fn finish_anon_tx(&mut self) -> Result<(), NodesError> {\n        if self.in_anon_tx {\n            self.in_anon_tx = false;\n            Ok(())\n        } else {\n            // Not in an anon tx context.\n            // This can happen if a reducer calls this ABI\n            // and tries to commit its own transaction early.\n            // We refuse to do this, as it would cause a later panic in the host.\n            Err(NodesError::NotInAnonTransaction)\n        }\n    }\n\n    // Async procedure syscalls return a `Result<impl Future>`, so that we can check `get_tx()`\n    // *before* requiring an async runtime. Otherwise, the v8 module host would have to call\n    // on `tokio::runtime::Handle::try_current()` before being able to run the `get_tx()` check.\n\n    pub fn commit_mutable_tx(&mut self) -> Result<(), NodesError> {\n        let tx = self.take_mutable_tx_for_commit()?;\n        self.commit_procedure_tx(tx)\n    }\n\n    /// Extract an anonymous mutable tx so callers can perform extra work before commit.\n    pub fn take_mutable_tx_for_commit(&mut self) -> Result<MutTxId, NodesError> {\n        self.finish_anon_tx()?;\n        Ok(self.take_tx()?)\n    }\n\n    /// Commit an anonymous procedure tx and broadcast resulting updates.\n    pub fn commit_procedure_tx(&mut self, tx: MutTxId) -> Result<(), NodesError> {\n        let stdb = self.relational_db().clone();\n        let subs = self.replica_ctx.subscriptions.clone();\n\n        let event = ModuleEvent {\n            timestamp: Timestamp::now(),\n            caller_identity: stdb.database_identity(),\n            caller_connection_id: None,\n            function_call: ModuleFunctionCall::default(),\n            status: EventStatus::Committed(DatabaseUpdate::default()),\n            reducer_return_value: None,\n            request_id: None,\n            timer: None,\n            // The procedure will pick up the tab for the energy.\n            energy_quanta_used: EnergyQuanta { quanta: 0 },\n            host_execution_duration: Duration::from_millis(0),\n        };\n        // Commit the tx and broadcast it.\n        let event = commit_and_broadcast_event(&subs, None, event, tx);\n        self.procedure_last_tx_offset = Some(event.tx_offset);\n\n        Ok(())\n    }\n\n    pub fn abort_mutable_tx(&mut self) -> Result<(), NodesError> {\n        self.finish_anon_tx()?;\n        let tx = self.take_tx()?;\n        self.rollback_procedure_tx(tx);\n        Ok(())\n    }\n\n    /// Roll back an anonymous procedure tx and record the resulting offset.\n    pub fn rollback_procedure_tx(&mut self, tx: MutTxId) {\n        let stdb = self.relational_db().clone();\n        let offset = ModuleSubscriptions::rollback_mut_tx(&stdb, tx);\n        self.procedure_last_tx_offset = Some(from_tx_offset(offset));\n    }\n\n    /// In-case there is a anonymous tx at the end of a procedure,\n    /// it must be terminated.\n    ///\n    /// This represents a misuse by the module author of the module ABI.\n    pub fn terminate_dangling_anon_tx(&mut self) {\n        // Try to abort the anon tx.\n        match self.abort_mutable_tx() {\n            // There was no dangling anon tx. Yay!\n            Err(NodesError::NotInAnonTransaction) => {}\n            // There was one, which has been aborted.\n            // The module is using the ABI wrong! 😭\n            Ok(()) => {\n                let message = format!(\n                    \"aborting dangling anonymous transaction in procedure {}\",\n                    self.func_name.as_deref().unwrap_or(\"<unknown>\")\n                );\n                self.console_log_simple_message(LogLevel::Error, None, &message);\n            }\n            res => unreachable!(\"should've had a tx to close; {res:?}\"),\n        }\n    }\n\n    /// After a procedure has finished, take its known last tx offset, if any.\n    pub fn take_procedure_tx_offset(&mut self) -> Option<TransactionOffset> {\n        self.procedure_last_tx_offset.take()\n    }\n\n    /// Perform an HTTP request.\n    /// Exposed to modules via the `ProcedureContext`.\n    ///\n    /// It's very important that the error returned from this function\n    /// not contain any potentially sensitive data from `request`,\n    /// such as the query parameters or header values.\n    /// This way, it's safe to log the errors (either for us to do so, or for module code to do so),\n    /// and less dangerous to send them to the calling client of a procedure.\n    pub fn http_request(\n        &mut self,\n        request: st_http::Request,\n        body: bytes::Bytes,\n    ) -> Result<impl Future<Output = Result<(st_http::Response, bytes::Bytes), NodesError>> + use<>, NodesError> {\n        if self.in_tx() {\n            // If we're holding a transaction open, refuse to perform this blocking operation.\n            return Err(NodesError::WouldBlockTransaction(super::AbiCall::ProcedureHttpRequest));\n        }\n\n        // Record in metrics that we're starting an HTTP request.\n        DB_METRICS\n            .procedure_num_http_requests\n            .with_label_values(self.database_identity())\n            .inc();\n        DB_METRICS\n            .procedure_http_request_size_bytes\n            .with_label_values(self.database_identity())\n            .inc_by((request.size_in_bytes() + body.len()) as _);\n        // Make a guard for the `in_progress` metric that will be decremented on exit.\n        let _in_progress_metric = DB_METRICS\n            .procedure_num_in_progress_http_requests\n            .with_label_values(self.database_identity())\n            .inc_scope();\n\n        /// Strip the query part out of the URL in `err`, as query parameters may be sensitive\n        /// and we'd like it to be safe to directly log errors from this method.\n        fn strip_query_params_from_reqwest_error(mut err: reqwest::Error) -> reqwest::Error {\n            if let Some(url) = err.url_mut() {\n                // `set_query` of `None` clears the query part.\n                url.set_query(None);\n            }\n            err\n        }\n\n        fn http_error<E: std::error::Error>(err: E) -> NodesError {\n            // Include the full error chain, not just the top-level message.\n            // `reqwest::Error` wraps underlying causes (DNS failure, connection refused,\n            // timeout, TLS errors, etc.) which are essential for debugging.\n            use std::fmt::Write;\n            let mut message = err.to_string();\n            let mut source = err.source();\n            while let Some(cause) = source {\n                write!(message, \": {cause}\").unwrap();\n                source = cause.source();\n            }\n            NodesError::HttpError(message)\n        }\n\n        // Then convert the request into an `http::Request`, a semi-standard \"lingua franca\" type in the Rust ecosystem,\n        // and map its body into a type `reqwest` will like.\n        //\n        // See comments on and in `convert_http_request` for justification that there's no sensitive info in this error.\n        let (request, timeout) = convert_http_request(request).map_err(http_error)?;\n\n        let request = http::Request::from_parts(request, body);\n\n        let mut reqwest: reqwest::Request = request\n            .try_into()\n            // `reqwest::Error` may contain sensitive info, namely the full URL with query params.\n            // Strip those out before returning the error.\n            .map_err(strip_query_params_from_reqwest_error)\n            .map_err(http_error)?;\n\n        // If the user requested a timeout using our extension, slot it in to reqwest's timeout.\n        // Clamp to the range `0..HTTP_DEFAULT_TIMEOUT`.\n        let timeout = timeout.unwrap_or(HTTP_DEFAULT_TIMEOUT).min(HTTP_MAX_TIMEOUT);\n\n        // reqwest's timeout covers from the start of the request to the end of reading the body,\n        // so there's no need to do our own timeout operation.\n        *reqwest.timeout_mut() = Some(timeout);\n\n        let reqwest = reqwest;\n\n        // Check if we have a blocked IP address, since IP literals bypass DNS resolution.\n        if is_blocked_ip_literal(reqwest.url()) {\n            return Err(NodesError::HttpError(BLOCKED_HTTP_ADDRESS_ERROR.to_string()));\n        }\n\n        let redirect_policy = reqwest::redirect::Policy::custom(|attempt| {\n            if is_blocked_ip_literal(attempt.url()) {\n                attempt.error(BLOCKED_HTTP_ADDRESS_ERROR)\n            } else {\n                reqwest::redirect::Policy::default().redirect(attempt)\n            }\n        });\n\n        // TODO(procedure-metrics): record size in bytes of response, time spent awaiting response.\n\n        // Actually execute the HTTP request!\n        // TODO(perf): Stash a long-lived `Client` in the env somewhere, rather than building a new one for each call.\n        let execute_fut = reqwest::Client::builder()\n            .dns_resolver(Arc::new(FilteredDnsResolver))\n            .redirect(redirect_policy)\n            .build()\n            .map_err(http_error)?\n            .execute(reqwest);\n\n        // Run the future that does IO work on a tokio worker thread, where it's more efficent.\n        let response_fut = tokio::spawn(async {\n            // `reqwest::Error` may contain sensitive info, namely the full URL with query params.\n            // We'll strip those with `strip_query_params_from_eqwest_error`\n            // after `await`ing `response_fut` below.\n            let response = execute_fut.await?;\n\n            // Download the response body, which in all likelihood will be a stream,\n            // as reqwest seems to prefer that.\n            let (response, body) = http::Response::from(response).into_parts();\n\n            // This error may also contain the full URL with query params.\n            // Again, we'll strip them after `await`ing `response_fut` below.\n            let body = http_body_util::BodyExt::collect(body).await?.to_bytes();\n\n            Ok((response, body))\n        })\n        .unwrap_or_else(|e| std::panic::resume_unwind(e.into_panic()));\n\n        let database_identity = *self.database_identity();\n\n        Ok(async move {\n            let (response, body) = response_fut\n                .await\n                .inspect_err(|err: &reqwest::Error| {\n                    // Report the request's failure in our metrics as either a timeout or a misc. failure, as appropriate.\n                    if err.is_timeout() {\n                        DB_METRICS\n                            .procedure_num_timeout_http_requests\n                            .with_label_values(&database_identity)\n                            .inc();\n                    } else {\n                        DB_METRICS\n                            .procedure_num_failed_http_requests\n                            .with_label_values(&database_identity)\n                            .inc();\n                    }\n                })\n                // `response_fut` returns a `reqwest::Error`, which may contain the full URL including query params.\n                // Strip them out to clean the error of potentially sensitive info.\n                .map_err(strip_query_params_from_reqwest_error)\n                .map_err(http_error)?;\n\n            // Transform the `http::Response` into our `spacetimedb_lib::http::Response` type,\n            // which has a stable BSATN encoding to pass across the WASM boundary.\n            let response = convert_http_response(response);\n\n            // Record the response size in bytes.\n            DB_METRICS\n                .procedure_http_response_size_bytes\n                .with_label_values(&database_identity)\n                .inc_by((response.size_in_bytes() + body.len()) as _);\n\n            Ok((response, body))\n        })\n    }\n}\n\n/// Default timeout for HTTP requests performed by [`InstanceEnv::http_request`].\n///\n/// Applied when the module does not specify a timeout.\n/// 30 seconds is generous enough for most external API calls (including LLM APIs)\n/// without silently hanging forever on a broken endpoint.\nconst HTTP_DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);\n/// Maximum timeout for HTTP requests performed by [`InstanceEnv::http_request`].\n///\n/// If the user requests a timeout longer than this, we will clamp to this value.\n/// 180 seconds accommodates long-running LLM and AI API calls,\n/// which routinely take 30-120 seconds for complex requests.\nconst HTTP_MAX_TIMEOUT: Duration = Duration::from_secs(180);\nconst BLOCKED_HTTP_ADDRESS_ERROR: &str = \"refusing to connect to private or special-purpose addresses\";\n\nstruct FilteredDnsResolver;\n\nimpl reqwest::dns::Resolve for FilteredDnsResolver {\n    fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving {\n        let host = name.as_str().to_owned();\n        Box::pin(async move {\n            let addrs = tokio::net::lookup_host((host.as_str(), 0)).await?;\n            let filtered_addrs: Vec<SocketAddr> = addrs.filter(|addr| !is_blocked_ip(addr.ip())).collect();\n\n            if filtered_addrs.is_empty() {\n                return Err(\n                    std::io::Error::new(std::io::ErrorKind::PermissionDenied, BLOCKED_HTTP_ADDRESS_ERROR).into(),\n                );\n            }\n\n            Ok(Box::new(filtered_addrs.into_iter()) as reqwest::dns::Addrs)\n        })\n    }\n}\n\nfn is_blocked_ip_literal(url: &reqwest::Url) -> bool {\n    match url.host() {\n        Some(url::Host::Ipv4(ip)) => is_blocked_ip(IpAddr::V4(ip)),\n        Some(url::Host::Ipv6(ip)) => is_blocked_ip(IpAddr::V6(ip)),\n        Some(url::Host::Domain(_)) | None => false,\n    }\n}\n\nfn is_blocked_ip(ip: IpAddr) -> bool {\n    match ip {\n        IpAddr::V4(ip) => is_blocked_ipv4(ip),\n        IpAddr::V6(ip) => is_blocked_ipv6(ip),\n    }\n}\n\nfn is_blocked_ipv4(ip: Ipv4Addr) -> bool {\n    let [a, b, c, d] = ip.octets();\n    let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n    // RFC 6890 Section 2.2.2, Table 1: \"This host on this network\" (0.0.0.0/8).\n    let is_this_host_on_this_network = a == 0;\n    // RFC 6890 Section 2.2.2, Table 2: \"Private-Use\" (10.0.0.0/8).\n    let is_private_use_10 = a == 10;\n    // RFC 6890 Section 2.2.2, Table 3: \"Shared Address Space\" (100.64.0.0/10).\n    let is_shared_address_space = a == 100 && (b & 0b1100_0000) == 0b0100_0000;\n    // RFC 6890 Section 2.2.2, Table 4: \"Loopback\" (127.0.0.0/8).\n    let is_loopback = block_loopback && a == 127;\n    // RFC 6890 Section 2.2.2, Table 5: \"Link Local\" (169.254.0.0/16).\n    let is_link_local = a == 169 && b == 254;\n    // RFC 6890 Section 2.2.2, Table 6: \"Private-Use\" (172.16.0.0/12).\n    let is_private_use_172 = a == 172 && (b & 0b1111_0000) == 16;\n    // RFC 6890 Section 2.2.2, Table 7: \"IETF Protocol Assignments\" (192.0.0.0/24).\n    let is_ietf_protocol_assignments = a == 192 && b == 0 && c == 0;\n    // RFC 6890 Section 2.2.2, Table 8: \"Documentation (TEST-NET-1)\" (192.0.2.0/24).\n    let is_test_net_1 = a == 192 && b == 0 && c == 2;\n    // RFC 6890 Section 2.2.2, Table 9: \"AS112-v4\" (192.31.196.0/24).\n    let is_as112_v4 = a == 192 && b == 31 && c == 196;\n    // RFC 6890 Section 2.2.2, Table 10: \"AMT\" (192.52.193.0/24).\n    let is_amt = a == 192 && b == 52 && c == 193;\n    // RFC 6890 Section 2.2.2, Table 11: \"6to4 Relay Anycast\" (192.88.99.0/24).\n    let is_6to4_relay_anycast = a == 192 && b == 88 && c == 99;\n    // RFC 6890 Section 2.2.2, Table 12: \"Private-Use\" (192.168.0.0/16).\n    let is_private_use_192 = a == 192 && b == 168;\n    // RFC 6890 Section 2.2.2, Table 13: \"Direct Delegation AS112 Service\" (192.175.48.0/24).\n    let is_direct_delegation_as112_service = a == 192 && b == 175 && c == 48;\n    // RFC 6890 Section 2.2.2, Table 14: \"Benchmarking\" (198.18.0.0/15).\n    let is_benchmarking = a == 198 && (b & 0b1111_1110) == 18;\n    // RFC 6890 Section 2.2.2, Table 15: \"Documentation (TEST-NET-2)\" (198.51.100.0/24).\n    let is_test_net_2 = a == 198 && b == 51 && c == 100;\n    // RFC 6890 Section 2.2.2, Table 16: \"Documentation (TEST-NET-3)\" (203.0.113.0/24).\n    let is_test_net_3 = a == 203 && b == 0 && c == 113;\n    // RFC 6890 Section 2.2.2, Table 17: \"Reserved\" (240.0.0.0/4).\n    let is_reserved = (a & 0b1111_0000) == 0b1111_0000;\n    // RFC 6890 Section 2.2.2, Table 18: \"Limited Broadcast\" (255.255.255.255/32).\n    let is_limited_broadcast = a == 255 && b == 255 && c == 255 && d == 255;\n\n    is_this_host_on_this_network\n        || is_private_use_10\n        || is_shared_address_space\n        || is_loopback\n        || is_link_local\n        || is_private_use_172\n        || is_ietf_protocol_assignments\n        || is_test_net_1\n        || is_as112_v4\n        || is_amt\n        || is_6to4_relay_anycast\n        || is_private_use_192\n        || is_direct_delegation_as112_service\n        || is_benchmarking\n        || is_test_net_2\n        || is_test_net_3\n        || is_reserved\n        || is_limited_broadcast\n}\n\nfn is_blocked_ipv6(ip: Ipv6Addr) -> bool {\n    let segments = ip.segments();\n    let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n    // RFC 6890 Section 2.2.3, Table 17: \"Loopback Address\" (::1/128).\n    let is_loopback_address = block_loopback && ip == Ipv6Addr::LOCALHOST;\n    // RFC 6890 Section 2.2.3, Table 18: \"Unspecified Address\" (::/128).\n    let is_unspecified_address = ip.is_unspecified();\n    // RFC 6890 Section 2.2.3, Table 19: \"IPv4-IPv6 Translat.\" (64:ff9b::/96).\n    let is_ipv4_ipv6_translation = segments[0] == 0x0064\n        && segments[1] == 0xff9b\n        && segments[2] == 0\n        && segments[3] == 0\n        && segments[4] == 0\n        && segments[5] == 0;\n    // IANA IPv6 Special-Purpose Address Space: \"IPv4-IPv6 Translat.\" (64:ff9b:1::/48), RFC 8215.\n    let is_ipv4_ipv6_translation_local_use = segments[0] == 0x0064 && segments[1] == 0xff9b && segments[2] == 0x0001;\n    // RFC 6890 Section 2.2.3, Table 20: \"IPv4-mapped Address\" (::ffff:0:0/96).\n    let is_ipv4_mapped = segments[0] == 0\n        && segments[1] == 0\n        && segments[2] == 0\n        && segments[3] == 0\n        && segments[4] == 0\n        && segments[5] == 0xffff;\n    // RFC 6890 Section 2.2.3, Table 21: \"Discard-Only Address Block\" (100::/64).\n    let is_discard_only_prefix = segments[0] == 0x0100 && segments[1] == 0 && segments[2] == 0 && segments[3] == 0;\n    // IANA IPv6 Special-Purpose Address Space: \"Dummy IPv6 Prefix\" (100:0:0:1::/64), RFC 9780.\n    let is_dummy_ipv6_prefix = segments[0] == 0x0100 && segments[1] == 0 && segments[2] == 0 && segments[3] == 1;\n    // RFC 6890 Section 2.2.3, Table 22: \"IETF Protocol Assignments\" (2001::/23).\n    // This broader prefix also covers newer IANA entries including:\n    // `2001:1::1/128`, `2001:1::2/128`, `2001:1::3/128`, `2001:3::/32`,\n    // `2001:4:112::/48`, `2001:20::/28`, and `2001:30::/28`.\n    let is_ietf_protocol_assignments = segments[0] == 0x2001 && (segments[1] & 0xfe00) == 0;\n    // RFC 6890 Section 2.2.3, Table 23: \"TEREDO\" (2001::/32).\n    let is_teredo = segments[0] == 0x2001 && segments[1] == 0;\n    // RFC 6890 Section 2.2.3, Table 24: \"Benchmarking\" (2001:2::/48).\n    let is_benchmarking = segments[0] == 0x2001 && segments[1] == 0x0002 && segments[2] == 0;\n    // RFC 6890 Section 2.2.3, Table 25: \"Documentation\" (2001:db8::/32).\n    let is_documentation = segments[0] == 0x2001 && segments[1] == 0x0db8;\n    // RFC 6890 Section 2.2.3, Table 26: \"ORCHID\" (2001:10::/28).\n    let is_orchid = segments[0] == 0x2001 && (segments[1] & 0xfff0) == 0x0010;\n    // RFC 6890 Section 2.2.3, Table 27: \"6to4\" (2002::/16).\n    let is_6to4 = segments[0] == 0x2002;\n    // IANA IPv6 Special-Purpose Address Space: \"Direct Delegation AS112 Service\" (2620:4f:8000::/48), RFC 7534.\n    let is_direct_delegation_as112_service = segments[0] == 0x2620 && segments[1] == 0x004f && segments[2] == 0x8000;\n    // IANA IPv6 Special-Purpose Address Space: \"Documentation\" (3fff::/20), RFC 9637.\n    let is_documentation_3fff = segments[0] == 0x3fff && (segments[1] & 0xf000) == 0;\n    // IANA IPv6 Special-Purpose Address Space: \"Segment Routing (SRv6) SIDs\" (5f00::/16), RFC 9602.\n    let is_segment_routing_srv6_sids = segments[0] == 0x5f00;\n    // RFC 6890 Section 2.2.3, Table 28: \"Unique-Local\" (fc00::/7).\n    let is_unique_local = (segments[0] & 0xfe00) == 0xfc00;\n    // RFC 6890 Section 2.2.3, Table 29: \"Linked-Scoped Unicast\" (fe80::/10).\n    let is_link_scoped_unicast = (segments[0] & 0xffc0) == 0xfe80;\n\n    is_loopback_address\n        || is_unspecified_address\n        || is_ipv4_ipv6_translation\n        || is_ipv4_ipv6_translation_local_use\n        || is_ipv4_mapped\n        || is_discard_only_prefix\n        || is_dummy_ipv6_prefix\n        || is_ietf_protocol_assignments\n        || is_teredo\n        || is_benchmarking\n        || is_documentation\n        || is_orchid\n        || is_6to4\n        || is_direct_delegation_as112_service\n        || is_documentation_3fff\n        || is_segment_routing_srv6_sids\n        || is_unique_local\n        || is_link_scoped_unicast\n}\n\n/// Unpack `request` and convert it into an [`http::request::Parts`],\n/// and a [`Duration`] from its `timeout` if supplied.\n///\n/// It's very important that the error return from this function\n/// not contain any potentially sensitive data from `request`,\n/// such as the query parameters or header values.\n/// See comment on [`InstanceEnv::http_request`].\nfn convert_http_request(request: st_http::Request) -> http::Result<(http::request::Parts, Option<Duration>)> {\n    let st_http::Request {\n        method,\n        headers,\n        timeout,\n        uri,\n        version,\n    } = request;\n\n    let (mut request, ()) = http::Request::new(()).into_parts();\n    request.method = match method {\n        st_http::Method::Get => http::Method::GET,\n        st_http::Method::Head => http::Method::HEAD,\n        st_http::Method::Post => http::Method::POST,\n        st_http::Method::Put => http::Method::PUT,\n        st_http::Method::Delete => http::Method::DELETE,\n        st_http::Method::Connect => http::Method::CONNECT,\n        st_http::Method::Options => http::Method::OPTIONS,\n        st_http::Method::Trace => http::Method::TRACE,\n        st_http::Method::Patch => http::Method::PATCH,\n        st_http::Method::Extension(method) => http::Method::from_bytes(method.as_bytes()).expect(\"Invalid HTTP method\"),\n    };\n    // The error type here, `http::uri::InvalidUri`, doesn't contain the URI itself,\n    // so it's safe to return and to log.\n    // See https://docs.rs/http/1.3.1/src/http/uri/mod.rs.html#120-141 .\n    request.uri = uri.try_into()?;\n    request.version = match version {\n        st_http::Version::Http09 => http::Version::HTTP_09,\n        st_http::Version::Http10 => http::Version::HTTP_10,\n        st_http::Version::Http11 => http::Version::HTTP_11,\n        st_http::Version::Http2 => http::Version::HTTP_2,\n        st_http::Version::Http3 => http::Version::HTTP_3,\n    };\n    request.headers = headers\n        .into_iter()\n        .map(|(k, v)| {\n            Ok((\n                // The error type here, `http::header::InvalidHeaderName`, doesn't contain the header name itself,\n                // so it's safe to return and to log.\n                // See https://docs.rs/http/1.3.1/src/http/header/name.rs.html#60-63 .\n                k.into_string().try_into()?,\n                // The error type here, `http::header::InvalidHeaderValue`, doesn't contain the header value itself,\n                // so it's safe to return and to log.\n                // See https://docs.rs/http/1.3.1/src/http/header/value.rs.html#27-31 .\n                v.into_vec().try_into()?,\n            ))\n        })\n        // Collecting into a `HeaderMap` doesn't add any new possible errors,\n        // the `?` here is just to propogate the errors from converting the individual header names and values.\n        // We know those are free from sensitive info, so this result is clean.\n        .collect::<http::Result<_>>()?;\n\n    let timeout = timeout.map(|d| d.to_duration_saturating());\n\n    Ok((request, timeout))\n}\n\nfn convert_http_response(response: http::response::Parts) -> st_http::Response {\n    let http::response::Parts {\n        extensions,\n        headers,\n        status,\n        version,\n        ..\n    } = response;\n\n    // there's a good chance that reqwest inserted some extensions into this request,\n    // but we can't control that and don't care much about it.\n    let _ = extensions;\n\n    st_http::Response {\n        headers: headers\n            .into_iter()\n            .map(|(k, v)| (k.map(|k| k.as_str().into()), v.as_bytes().into()))\n            .collect(),\n        version: match version {\n            http::Version::HTTP_09 => st_http::Version::Http09,\n            http::Version::HTTP_10 => st_http::Version::Http10,\n            http::Version::HTTP_11 => st_http::Version::Http11,\n            http::Version::HTTP_2 => st_http::Version::Http2,\n            http::Version::HTTP_3 => st_http::Version::Http3,\n            _ => unreachable!(\"Unknown HTTP version: {version:?}\"),\n        },\n        code: status.as_u16(),\n    }\n}\n\nimpl TxSlot {\n    /// Sets the slot to `tx`, ensuring that there was no tx before.\n    pub fn set_raw(&mut self, tx: MutTxId) {\n        let prev = self.inner.lock().replace(tx);\n        assert!(prev.is_none(), \"reentrant TxSlot::set\");\n    }\n\n    /// Sets the slot to `tx` runs `work`, and returns back `tx`.\n    pub fn set<T>(&mut self, tx: MutTxId, work: impl FnOnce() -> T) -> (MutTxId, T) {\n        self.set_raw(tx);\n\n        let remove_tx = || self.take().expect(\"tx was removed during transaction\");\n\n        let res = {\n            scopeguard::defer_on_unwind! { remove_tx(); }\n            work()\n        };\n\n        let tx = remove_tx();\n        (tx, res)\n    }\n\n    /// Returns the tx in the slot.\n    pub fn get(&self) -> Result<impl DerefMut<Target = MutTxId> + '_, GetTxError> {\n        MutexGuard::try_map(self.inner.lock(), |map| map.as_mut()).map_err(|_| GetTxError)\n    }\n\n    /// Steals the tx from the slot.\n    pub fn take(&self) -> Result<MutTxId, GetTxError> {\n        self.inner.lock().take().ok_or(GetTxError)\n    }\n}\n\n#[derive(Debug)]\npub struct GetTxError;\nimpl From<GetTxError> for NodesError {\n    fn from(_: GetTxError) -> Self {\n        NodesError::NotInTransaction\n    }\n}\n\nimpl Display for GetTxError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"not in a transaction\")\n    }\n}\nimpl std::error::Error for GetTxError {}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    use std::{ops::Bound, sync::Arc};\n\n    use crate::{\n        database_logger::DatabaseLogger,\n        db::relational_db::{\n            tests_utils::{begin_mut_tx, with_auto_commit, with_read_only, TestDB},\n            RelationalDB,\n        },\n        host::Scheduler,\n        messages::control_db::{Database, HostType},\n        replica_context::ReplicaContext,\n        subscription::module_subscription_actor::ModuleSubscriptions,\n    };\n    use anyhow::{anyhow, Result};\n    use spacetimedb_lib::db::auth::StAccess;\n    use spacetimedb_lib::{bsatn::to_vec, AlgebraicType, AlgebraicValue, Hash, Identity, ProductValue};\n    use spacetimedb_primitives::{IndexId, TableId};\n    use spacetimedb_sats::product;\n\n    /// An `InstanceEnv` requires a `DatabaseLogger`\n    fn temp_logger() -> DatabaseLogger {\n        DatabaseLogger::in_memory(64 * 1024)\n    }\n\n    /// An `InstanceEnv` requires a `ReplicaContext`.\n    /// For our purposes this is just a wrapper for `RelationalDB`.\n    fn replica_ctx(relational_db: Arc<RelationalDB>) -> Result<(ReplicaContext, tokio::runtime::Runtime)> {\n        let (subs, runtime) = ModuleSubscriptions::for_test_new_runtime(relational_db.clone());\n        let logger = {\n            let _rt = runtime.enter();\n            Arc::new(temp_logger())\n        };\n        Ok((\n            ReplicaContext {\n                database: Database {\n                    id: 0,\n                    database_identity: Identity::ZERO,\n                    owner_identity: Identity::ZERO,\n                    host_type: HostType::Wasm,\n                    initial_program: Hash::ZERO,\n                },\n                replica_id: 0,\n                logger,\n                subscriptions: subs,\n                relational_db,\n            },\n            runtime,\n        ))\n    }\n\n    /// An `InstanceEnv` used for testing the database syscalls.\n    fn instance_env(db: Arc<RelationalDB>) -> Result<(InstanceEnv, tokio::runtime::Runtime)> {\n        let (scheduler, _) = Scheduler::open(db.clone());\n        let (replica_context, runtime) = replica_ctx(db)?;\n        Ok((InstanceEnv::new(Arc::new(replica_context), scheduler), runtime))\n    }\n\n    /// An in-memory `RelationalDB` for testing.\n    /// It does not persist data to disk.\n    fn relational_db() -> Result<Arc<RelationalDB>> {\n        let TestDB { db, .. } = TestDB::in_memory()?;\n        Ok(db)\n    }\n\n    /// Generate a `ProductValue` for use in [create_table_with_index]\n    fn product_row(i: usize) -> ProductValue {\n        let str = i.to_string();\n        let str = str.repeat(i);\n        let id = i as u64;\n        product!(id, str)\n    }\n\n    /// Generate a BSATN encoded row for use in [create_table_with_index]\n    fn bsatn_row(i: usize) -> Result<Vec<u8>> {\n        Ok(to_vec(&product_row(i))?)\n    }\n\n    /// Instantiate the following table:\n    ///\n    /// ```text\n    /// id | str\n    /// -- | ---\n    /// 1  | \"1\"\n    /// 2  | \"22\"\n    /// 3  | \"333\"\n    /// 4  | \"4444\"\n    /// 5  | \"55555\"\n    /// ```\n    ///\n    /// with an index on `id`.\n    fn create_table_with_index(db: &RelationalDB) -> Result<(TableId, IndexId)> {\n        let table_id = db.create_table_for_test(\n            \"t\",\n            &[(\"id\", AlgebraicType::U64), (\"str\", AlgebraicType::String)],\n            &[0.into()],\n        )?;\n        let index_id = with_read_only(db, |tx| {\n            db.schema_for_table(tx, table_id)?\n                .indexes\n                .iter()\n                .find(|schema| {\n                    schema\n                        .index_algorithm\n                        .columns()\n                        .as_singleton()\n                        .is_some_and(|col_id| col_id.idx() == 0)\n                })\n                .map(|schema| schema.index_id)\n                .ok_or_else(|| anyhow!(\"Index not found for ColId `{}`\", 0))\n        })?;\n        with_auto_commit(db, |tx| -> Result<_> {\n            for i in 1..=5 {\n                db.insert(tx, table_id, &bsatn_row(i)?)?;\n            }\n            Ok(())\n        })?;\n        Ok((table_id, index_id))\n    }\n\n    fn create_table_with_unique_index(db: &RelationalDB) -> Result<(TableId, IndexId)> {\n        let table_id = db.create_table_for_test_with_the_works(\n            \"t\",\n            &[(\"id\", AlgebraicType::U64), (\"str\", AlgebraicType::String)],\n            &[0.into()],\n            &[0.into()],\n            StAccess::Public,\n        )?;\n        let index_id = with_read_only(db, |tx| {\n            db.schema_for_table(tx, table_id)?\n                .indexes\n                .iter()\n                .find(|schema| {\n                    schema\n                        .index_algorithm\n                        .columns()\n                        .as_singleton()\n                        .is_some_and(|col_id| col_id.idx() == 0)\n                })\n                .map(|schema| schema.index_id)\n                .ok_or_else(|| anyhow!(\"Index not found for ColId `{}`\", 0))\n        })?;\n        with_auto_commit(db, |tx| -> Result<_> {\n            for i in 1..=5 {\n                db.insert(tx, table_id, &bsatn_row(i)?)?;\n            }\n            Ok(())\n        })?;\n        Ok((table_id, index_id))\n    }\n\n    #[test]\n    fn blocks_each_rfc6890_ipv4_range() {\n        // RFC 6890 §2.2.2 tables 1-18.\n        let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n        let cases = [\n            // Table 1: This host on this network (0.0.0.0/8).\n            (Ipv4Addr::new(0, 0, 0, 1), true),\n            // Table 2: Private-Use (10.0.0.0/8).\n            (Ipv4Addr::new(10, 255, 255, 255), true),\n            // Table 3: Shared Address Space (100.64.0.0/10).\n            (Ipv4Addr::new(100, 127, 255, 255), true),\n            // Table 4: Loopback (127.0.0.0/8).\n            (Ipv4Addr::new(127, 0, 0, 1), block_loopback),\n            // Table 5: Link Local (169.254.0.0/16).\n            (Ipv4Addr::new(169, 254, 255, 255), true),\n            // Table 6: Private-Use (172.16.0.0/12).\n            (Ipv4Addr::new(172, 31, 255, 255), true),\n            // Table 7: IETF Protocol Assignments (192.0.0.0/24).\n            (Ipv4Addr::new(192, 0, 0, 255), true),\n            // Table 8: Documentation (TEST-NET-1) (192.0.2.0/24).\n            (Ipv4Addr::new(192, 0, 2, 1), true),\n            // Table 9: AS112-v4 (192.31.196.0/24).\n            (Ipv4Addr::new(192, 31, 196, 1), true),\n            // Table 10: AMT (192.52.193.0/24).\n            (Ipv4Addr::new(192, 52, 193, 1), true),\n            // Table 11: 6to4 Relay Anycast (192.88.99.0/24).\n            (Ipv4Addr::new(192, 88, 99, 1), true),\n            // Table 12: Private-Use (192.168.0.0/16).\n            (Ipv4Addr::new(192, 168, 255, 255), true),\n            // Table 13: Direct Delegation AS112 Service (192.175.48.0/24).\n            (Ipv4Addr::new(192, 175, 48, 1), true),\n            // Table 14: Benchmarking (198.18.0.0/15).\n            (Ipv4Addr::new(198, 19, 255, 255), true),\n            // Table 15: Documentation (TEST-NET-2) (198.51.100.0/24).\n            (Ipv4Addr::new(198, 51, 100, 1), true),\n            // Table 16: Documentation (TEST-NET-3) (203.0.113.0/24).\n            (Ipv4Addr::new(203, 0, 113, 1), true),\n            // Table 17: Reserved (240.0.0.0/4).\n            (Ipv4Addr::new(240, 0, 0, 1), true),\n            // Table 18: Limited Broadcast (255.255.255.255/32).\n            (Ipv4Addr::new(255, 255, 255, 255), true),\n        ];\n\n        for (addr, expected_blocked) in cases {\n            assert_eq!(\n                is_blocked_ip(IpAddr::V4(addr)),\n                expected_blocked,\n                \"unexpected block decision for {addr}\"\n            );\n        }\n    }\n\n    #[test]\n    fn blocks_ip_literal_hosts_in_urls() {\n        let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n        assert_eq!(\n            is_blocked_ip_literal(&reqwest::Url::parse(\"http://127.0.0.1:80/\").unwrap()),\n            block_loopback\n        );\n        assert_eq!(\n            is_blocked_ip_literal(&reqwest::Url::parse(\"http://[::1]:80/\").unwrap()),\n            block_loopback\n        );\n        assert!(is_blocked_ip_literal(\n            &reqwest::Url::parse(\"http://10.0.0.1:80/\").unwrap()\n        ));\n        assert!(is_blocked_ip_literal(\n            &reqwest::Url::parse(\"http://[fc00::1]:80/\").unwrap()\n        ));\n        assert!(!is_blocked_ip_literal(\n            &reqwest::Url::parse(\"http://8.8.8.8:80/\").unwrap()\n        ));\n        assert!(!is_blocked_ip_literal(\n            &reqwest::Url::parse(\"http://example.com:80/\").unwrap()\n        ));\n    }\n\n    #[test]\n    fn blocks_rfc6890_ipv4_range_endpoints() {\n        // RFC 6890 §2.2.2 tables 1-18, checked at each range's low/high endpoints.\n        let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n        let ranges = [\n            // Table 1: This host on this network (0.0.0.0/8).\n            (\n                \"this-host-on-this-network\",\n                Ipv4Addr::new(0, 0, 0, 0),\n                Ipv4Addr::new(0, 255, 255, 255),\n                true,\n            ),\n            // Table 2: Private-Use (10.0.0.0/8).\n            (\n                \"private-use-10\",\n                Ipv4Addr::new(10, 0, 0, 0),\n                Ipv4Addr::new(10, 255, 255, 255),\n                true,\n            ),\n            // Table 3: Shared Address Space (100.64.0.0/10).\n            (\n                \"shared-address-space\",\n                Ipv4Addr::new(100, 64, 0, 0),\n                Ipv4Addr::new(100, 127, 255, 255),\n                true,\n            ),\n            // Table 4: Loopback (127.0.0.0/8).\n            (\n                \"loopback\",\n                Ipv4Addr::new(127, 0, 0, 0),\n                Ipv4Addr::new(127, 255, 255, 255),\n                block_loopback,\n            ),\n            // Table 5: Link Local (169.254.0.0/16).\n            (\n                \"link-local\",\n                Ipv4Addr::new(169, 254, 0, 0),\n                Ipv4Addr::new(169, 254, 255, 255),\n                true,\n            ),\n            // Table 6: Private-Use (172.16.0.0/12).\n            (\n                \"private-use-172\",\n                Ipv4Addr::new(172, 16, 0, 0),\n                Ipv4Addr::new(172, 31, 255, 255),\n                true,\n            ),\n            // Table 7: IETF Protocol Assignments (192.0.0.0/24).\n            (\n                \"ietf-protocol-assignments\",\n                Ipv4Addr::new(192, 0, 0, 0),\n                Ipv4Addr::new(192, 0, 0, 255),\n                true,\n            ),\n            // Table 8: Documentation (TEST-NET-1) (192.0.2.0/24).\n            (\n                \"test-net-1\",\n                Ipv4Addr::new(192, 0, 2, 0),\n                Ipv4Addr::new(192, 0, 2, 255),\n                true,\n            ),\n            // Table 9: AS112-v4 (192.31.196.0/24).\n            (\n                \"as112-v4\",\n                Ipv4Addr::new(192, 31, 196, 0),\n                Ipv4Addr::new(192, 31, 196, 255),\n                true,\n            ),\n            // Table 10: AMT (192.52.193.0/24).\n            (\n                \"amt\",\n                Ipv4Addr::new(192, 52, 193, 0),\n                Ipv4Addr::new(192, 52, 193, 255),\n                true,\n            ),\n            // Table 11: 6to4 Relay Anycast (192.88.99.0/24).\n            (\n                \"6to4-relay-anycast\",\n                Ipv4Addr::new(192, 88, 99, 0),\n                Ipv4Addr::new(192, 88, 99, 255),\n                true,\n            ),\n            // Table 12: Private-Use (192.168.0.0/16).\n            (\n                \"private-use-192\",\n                Ipv4Addr::new(192, 168, 0, 0),\n                Ipv4Addr::new(192, 168, 255, 255),\n                true,\n            ),\n            // Table 13: Direct Delegation AS112 Service (192.175.48.0/24).\n            (\n                \"direct-delegation-as112-service\",\n                Ipv4Addr::new(192, 175, 48, 0),\n                Ipv4Addr::new(192, 175, 48, 255),\n                true,\n            ),\n            // Table 14: Benchmarking (198.18.0.0/15).\n            (\n                \"benchmarking\",\n                Ipv4Addr::new(198, 18, 0, 0),\n                Ipv4Addr::new(198, 19, 255, 255),\n                true,\n            ),\n            // Table 15: Documentation (TEST-NET-2) (198.51.100.0/24).\n            (\n                \"test-net-2\",\n                Ipv4Addr::new(198, 51, 100, 0),\n                Ipv4Addr::new(198, 51, 100, 255),\n                true,\n            ),\n            // Table 16: Documentation (TEST-NET-3) (203.0.113.0/24).\n            (\n                \"test-net-3\",\n                Ipv4Addr::new(203, 0, 113, 0),\n                Ipv4Addr::new(203, 0, 113, 255),\n                true,\n            ),\n            // Table 17: Reserved (240.0.0.0/4).\n            (\n                \"reserved\",\n                Ipv4Addr::new(240, 0, 0, 0),\n                Ipv4Addr::new(255, 255, 255, 255),\n                true,\n            ),\n            // Table 18: Limited Broadcast (255.255.255.255/32).\n            (\n                \"limited-broadcast\",\n                Ipv4Addr::new(255, 255, 255, 255),\n                Ipv4Addr::new(255, 255, 255, 255),\n                true,\n            ),\n        ];\n\n        for (name, low, high, expected_blocked) in ranges {\n            assert_eq!(\n                is_blocked_ip(IpAddr::V4(low)),\n                expected_blocked,\n                \"{name}: unexpected decision for low endpoint {low}\"\n            );\n            assert_eq!(\n                is_blocked_ip(IpAddr::V4(high)),\n                expected_blocked,\n                \"{name}: unexpected decision for high endpoint {high}\"\n            );\n        }\n    }\n\n    #[test]\n    fn blocks_each_rfc6890_ipv6_range() {\n        // RFC 6890 §2.2.3 tables 17-29.\n        let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n        let cases = [\n            // Table 17: Loopback Address (::1/128).\n            (Ipv6Addr::LOCALHOST, block_loopback),\n            // Table 18: Unspecified Address (::/128).\n            (Ipv6Addr::UNSPECIFIED, true),\n            // Table 19: IPv4-IPv6 Translat. (64:ff9b::/96).\n            (Ipv6Addr::new(0x0064, 0xff9b, 0, 0, 0, 0, 0xc000, 0x0201), true),\n            // Table 20: IPv4-mapped Address (::ffff:0:0/96).\n            (Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x0808, 0x0808), true),\n            // Table 21: Discard-Only Address Block (100::/64).\n            (Ipv6Addr::new(0x0100, 0, 0, 0, 0, 0, 0, 1), true),\n            // Table 22: IETF Protocol Assignments (2001::/23).\n            (Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 1), true),\n            // Table 23: TEREDO (2001::/32).\n            (Ipv6Addr::new(0x2001, 0x0000, 0, 0, 0, 0, 0, 1), true),\n            // Table 24: Benchmarking (2001:2::/48).\n            (Ipv6Addr::new(0x2001, 0x0002, 0x0000, 0, 0, 0, 0, 1), true),\n            // Table 25: Documentation (2001:db8::/32).\n            (Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1), true),\n            // Table 26: ORCHID (2001:10::/28).\n            (Ipv6Addr::new(0x2001, 0x0010, 0, 0, 0, 0, 0, 1), true),\n            // Table 27: 6to4 (2002::/16).\n            (Ipv6Addr::new(0x2002, 0, 0, 0, 0, 0, 0, 1), true),\n            // Table 28: Unique-Local (fc00::/7).\n            (Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1), true),\n            // Table 29: Linked-Scoped Unicast (fe80::/10).\n            (Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), true),\n        ];\n\n        for (addr, expected_blocked) in cases {\n            assert_eq!(\n                is_blocked_ip(IpAddr::V6(addr)),\n                expected_blocked,\n                \"unexpected block decision for {addr}\"\n            );\n        }\n        // A normal global IPv6 address should remain allowed.\n        assert!(!is_blocked_ip(IpAddr::V6(Ipv6Addr::new(\n            0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111\n        ))));\n    }\n\n    #[test]\n    fn blocks_rfc6890_ipv6_range_endpoints() {\n        // RFC 6890 §2.2.3 tables 17-29, checked at each range's low/high endpoints.\n        let block_loopback = !cfg!(feature = \"allow_loopback_http_for_tests\");\n        let ranges = [\n            // Table 17: Loopback Address (::1/128).\n            (\n                \"loopback-address\",\n                Ipv6Addr::LOCALHOST,\n                Ipv6Addr::LOCALHOST,\n                block_loopback,\n            ),\n            // Table 18: Unspecified Address (::/128).\n            (\n                \"unspecified-address\",\n                Ipv6Addr::UNSPECIFIED,\n                Ipv6Addr::UNSPECIFIED,\n                true,\n            ),\n            // Table 19: IPv4-IPv6 Translat. (64:ff9b::/96).\n            (\n                \"ipv4-ipv6-translation\",\n                Ipv6Addr::new(0x0064, 0xff9b, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x0064, 0xff9b, 0, 0, 0, 0, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 20: IPv4-mapped Address (::ffff:0:0/96).\n            (\n                \"ipv4-mapped-address\",\n                Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0, 0),\n                Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 21: Discard-Only Address Block (100::/64).\n            (\n                \"discard-only-address-block\",\n                Ipv6Addr::new(0x0100, 0, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x0100, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 22: IETF Protocol Assignments (2001::/23).\n            (\n                \"ietf-protocol-assignments\",\n                Ipv6Addr::new(0x2001, 0x0000, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x01ff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 23: TEREDO (2001::/32).\n            (\n                \"teredo\",\n                Ipv6Addr::new(0x2001, 0x0000, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 24: Benchmarking (2001:2::/48).\n            (\n                \"benchmarking\",\n                Ipv6Addr::new(0x2001, 0x0002, 0x0000, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x0002, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 25: Documentation (2001:db8::/32).\n            (\n                \"documentation\",\n                Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x0db8, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 26: ORCHID (2001:10::/28).\n            (\n                \"orchid\",\n                Ipv6Addr::new(0x2001, 0x0010, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x001f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 27: 6to4 (2002::/16).\n            (\n                \"6to4\",\n                Ipv6Addr::new(0x2002, 0, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2002, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 28: Unique-Local (fc00::/7).\n            (\n                \"unique-local\",\n                Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0xfdff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n            // Table 29: Linked-Scoped Unicast (fe80::/10).\n            (\n                \"linked-scoped-unicast\",\n                Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0xfebf, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n                true,\n            ),\n        ];\n\n        for (name, low, high, expected_blocked) in ranges {\n            assert_eq!(\n                is_blocked_ip(IpAddr::V6(low)),\n                expected_blocked,\n                \"{name}: unexpected decision for low endpoint {low}\"\n            );\n            assert_eq!(\n                is_blocked_ip(IpAddr::V6(high)),\n                expected_blocked,\n                \"{name}: unexpected decision for high endpoint {high}\"\n            );\n        }\n    }\n\n    #[test]\n    fn blocks_additional_iana_ipv6_range() {\n        // Additional ranges listed in the IANA IPv6 Special-Purpose Address Space registry.\n        // Some are transitively covered by the broader `2001::/23` block, and are kept\n        // here intentionally as explicit regression checks.\n        let cases = [\n            // IPv4-IPv6 Translat. local-use prefix (`64:ff9b:1::/48`).\n            Ipv6Addr::new(0x0064, 0xff9b, 0x0001, 0, 0, 0, 0, 1),\n            // Dummy IPv6 Prefix (`100:0:0:1::/64`).\n            Ipv6Addr::new(0x0100, 0, 0, 1, 0, 0, 0, 1),\n            // Port Control Protocol Anycast (`2001:1::1/128`).\n            Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 1),\n            // Traversal Using Relays around NAT Anycast (`2001:1::2/128`).\n            Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 2),\n            // DNS-SD Service Registration Protocol Anycast (`2001:1::3/128`).\n            Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 3),\n            // AMT (`2001:3::/32`).\n            Ipv6Addr::new(0x2001, 0x0003, 0, 0, 0, 0, 0, 1),\n            // AS112-v6 (`2001:4:112::/48`).\n            Ipv6Addr::new(0x2001, 0x0004, 0x0112, 0, 0, 0, 0, 1),\n            // ORCHIDv2 (`2001:20::/28`).\n            Ipv6Addr::new(0x2001, 0x0020, 0, 0, 0, 0, 0, 1),\n            // Drone Remote ID Protocol Entity Tags (DETs) Prefix (`2001:30::/28`).\n            Ipv6Addr::new(0x2001, 0x0030, 0, 0, 0, 0, 0, 1),\n            // Direct Delegation AS112 Service (`2620:4f:8000::/48`).\n            Ipv6Addr::new(0x2620, 0x004f, 0x8000, 0, 0, 0, 0, 1),\n            // Documentation (`3fff::/20`).\n            Ipv6Addr::new(0x3fff, 0x0001, 0, 0, 0, 0, 0, 1),\n            // Segment Routing (SRv6) SIDs (`5f00::/16`).\n            Ipv6Addr::new(0x5f00, 0, 0, 0, 0, 0, 0, 1),\n        ];\n\n        for addr in cases {\n            assert!(\n                is_blocked_ip(IpAddr::V6(addr)),\n                \"expected additional IANA IPv6 range to be blocked: {addr}\"\n            );\n        }\n    }\n\n    #[test]\n    fn blocks_additional_iana_ipv6_range_endpoints() {\n        // Additional ranges listed in the IANA IPv6 Special-Purpose Address Space registry.\n        // Some are transitively covered by the broader `2001::/23` block, and are kept\n        // here intentionally as explicit regression checks.\n        let ranges = [\n            (\n                \"ipv4-ipv6-translation-local-use\",\n                Ipv6Addr::new(0x0064, 0xff9b, 0x0001, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x0064, 0xff9b, 0x0001, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"dummy-ipv6-prefix\",\n                Ipv6Addr::new(0x0100, 0, 0, 1, 0, 0, 0, 0),\n                Ipv6Addr::new(0x0100, 0, 0, 1, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"pcp-anycast\",\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 1),\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 1),\n            ),\n            (\n                \"turn-anycast\",\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 2),\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 2),\n            ),\n            (\n                \"dns-sd-srp-anycast\",\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 3),\n                Ipv6Addr::new(0x2001, 0x0001, 0, 0, 0, 0, 0, 3),\n            ),\n            (\n                \"amt\",\n                Ipv6Addr::new(0x2001, 0x0003, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x0003, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"as112-v6\",\n                Ipv6Addr::new(0x2001, 0x0004, 0x0112, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x0004, 0x0112, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"orchidv2\",\n                Ipv6Addr::new(0x2001, 0x0020, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x002f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"drone-remote-id-dets-prefix\",\n                Ipv6Addr::new(0x2001, 0x0030, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2001, 0x003f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"direct-delegation-as112-service\",\n                Ipv6Addr::new(0x2620, 0x004f, 0x8000, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x2620, 0x004f, 0x8000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"documentation-3fff\",\n                Ipv6Addr::new(0x3fff, 0x0000, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x3fff, 0x0fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n            (\n                \"segment-routing-srv6-sids\",\n                Ipv6Addr::new(0x5f00, 0x0000, 0, 0, 0, 0, 0, 0),\n                Ipv6Addr::new(0x5f00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),\n            ),\n        ];\n\n        for (name, low, high) in ranges {\n            assert!(\n                is_blocked_ip(IpAddr::V6(low)),\n                \"{name}: unexpected decision for low endpoint {low}\"\n            );\n            assert!(\n                is_blocked_ip(IpAddr::V6(high)),\n                \"{name}: unexpected decision for high endpoint {high}\"\n            );\n        }\n    }\n\n    #[test]\n    fn blocks_ipv6_encodings_of_private_ipv4() {\n        // IPv4-mapped form of `10.0.0.1`: `::ffff:10.0.0.1`.\n        assert!(is_blocked_ip(IpAddr::V6(Ipv6Addr::new(\n            0, 0, 0, 0, 0, 0xffff, 0x0a00, 0x0001\n        ))));\n        // 6to4 form carrying `10.0.0.1`: `2002:0a00:0001::1`.\n        assert!(is_blocked_ip(IpAddr::V6(Ipv6Addr::new(\n            0x2002, 0x0a00, 0x0001, 0, 0, 0, 0, 1\n        ))));\n        // IPv4/IPv6 translation prefix carrying `10.0.0.1`: `64:ff9b::a00:1`.\n        assert!(is_blocked_ip(IpAddr::V6(Ipv6Addr::new(\n            0x0064, 0xff9b, 0, 0, 0, 0, 0x0a00, 0x0001\n        ))));\n    }\n\n    #[test]\n    fn table_scan_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (table_id, _) = create_table_with_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        let f = || env.datastore_table_scan_bsatn_chunks(&mut ChunkPool::default(), table_id);\n        let tx = begin_mut_tx(&db);\n        let (tx, scan_result) = tx_slot.set(tx, f);\n\n        scan_result?;\n\n        let bytes_scanned = (1..=5)\n            .map(bsatn_row)\n            .filter_map(|bsatn_result| bsatn_result.ok())\n            .map(|bsatn| bsatn.len())\n            .sum::<usize>();\n\n        // The only non-zero metrics should be rows and bytes scanned.\n        // The table has 5 rows, so we should have 5 rows scanned.\n        // We should also have scanned the same number of bytes that we inserted.\n        assert_eq!(0, tx.metrics.index_seeks);\n        assert_eq!(5, tx.metrics.rows_scanned);\n        assert_eq!(bytes_scanned, tx.metrics.bytes_scanned);\n        assert_eq!(0, tx.metrics.bytes_written);\n        assert_eq!(0, tx.metrics.bytes_sent_to_clients);\n        Ok(())\n    }\n\n    #[test]\n    fn index_scan_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (_, index_id) = create_table_with_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        // Perform two index scans\n        let f = || -> Result<_> {\n            let index_key_3 = to_vec(&Bound::Included(AlgebraicValue::U64(3)))?;\n            let index_key_5 = to_vec(&Bound::Included(AlgebraicValue::U64(5)))?;\n            env.datastore_index_scan_range_bsatn_chunks(\n                &mut ChunkPool::default(),\n                index_id,\n                &[],\n                0.into(),\n                &index_key_3,\n                &index_key_3,\n            )?;\n            env.datastore_index_scan_range_bsatn_chunks(\n                &mut ChunkPool::default(),\n                index_id,\n                &[],\n                0.into(),\n                &index_key_5,\n                &index_key_5,\n            )?;\n            Ok(())\n        };\n        let tx = begin_mut_tx(&db);\n        let (tx, scan_result) = tx_slot.set(tx, f);\n\n        scan_result?;\n\n        let bytes_scanned = [3, 5]\n            .into_iter()\n            .map(bsatn_row)\n            .filter_map(|bsatn_result| bsatn_result.ok())\n            .map(|bsatn| bsatn.len())\n            .sum::<usize>();\n\n        // We performed two index scans to fetch rows 3 and 5\n        assert_eq!(2, tx.metrics.index_seeks);\n        assert_eq!(2, tx.metrics.rows_scanned);\n        assert_eq!(bytes_scanned, tx.metrics.bytes_scanned);\n        assert_eq!(0, tx.metrics.bytes_written);\n        assert_eq!(0, tx.metrics.bytes_sent_to_clients);\n        Ok(())\n    }\n\n    #[test]\n    fn insert_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (table_id, _) = create_table_with_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        // Insert 4 new rows into `t`\n        let f = || -> Result<_> {\n            for i in 6..=9 {\n                let mut buffer = bsatn_row(i)?;\n                env.insert(table_id, &mut buffer)?;\n            }\n            Ok(())\n        };\n        let tx = begin_mut_tx(&db);\n        let (tx, insert_result) = tx_slot.set(tx, f);\n\n        insert_result?;\n\n        let bytes_written = (6..=9)\n            .map(bsatn_row)\n            .filter_map(|bsatn_result| bsatn_result.ok())\n            .map(|bsatn| bsatn.len())\n            .sum::<usize>();\n\n        // The only metric affected by inserts is bytes written\n        assert_eq!(0, tx.metrics.index_seeks);\n        assert_eq!(0, tx.metrics.rows_scanned);\n        assert_eq!(0, tx.metrics.bytes_scanned);\n        assert_eq!(bytes_written, tx.metrics.bytes_written);\n        assert_eq!(0, tx.metrics.bytes_sent_to_clients);\n        Ok(())\n    }\n\n    #[test]\n    fn update_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (table_id, index_id) = create_table_with_unique_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        let row_id: u64 = 1;\n        let row_val: String = \"string\".to_string();\n        let mut new_row_bytes = to_vec(&product!(row_id, row_val))?;\n        let new_row_len = new_row_bytes.len();\n        // Delete a single row via the index\n        let f = || -> Result<_> {\n            env.update(table_id, index_id, new_row_bytes.as_mut_slice())?;\n            Ok(())\n        };\n        let tx = begin_mut_tx(&db);\n        let (tx, res) = tx_slot.set(tx, f);\n\n        res?;\n\n        assert_eq!(new_row_len, tx.metrics.bytes_written);\n        Ok(())\n    }\n\n    #[test]\n    fn delete_by_index_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (_, index_id) = create_table_with_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        // Delete a single row via the index\n        let f = || -> Result<_> {\n            let index_key = to_vec(&Bound::Included(AlgebraicValue::U64(3)))?;\n            env.datastore_delete_by_index_scan_range_bsatn(index_id, &[], 0.into(), &index_key, &index_key)?;\n            Ok(())\n        };\n        let tx = begin_mut_tx(&db);\n        let (tx, delete_result) = tx_slot.set(tx, f);\n\n        delete_result?;\n\n        assert_eq!(1, tx.metrics.index_seeks);\n        assert_eq!(1, tx.metrics.rows_scanned);\n        assert_eq!(0, tx.metrics.bytes_scanned);\n        assert_eq!(0, tx.metrics.bytes_written);\n        assert_eq!(0, tx.metrics.bytes_sent_to_clients);\n        Ok(())\n    }\n\n    #[test]\n    fn delete_by_value_metrics() -> Result<()> {\n        let db = relational_db()?;\n        let (env, _runtime) = instance_env(db.clone())?;\n\n        let (table_id, _) = create_table_with_index(&db)?;\n\n        let mut tx_slot = env.tx.clone();\n\n        let bsatn_rows = to_vec(&(3..=5).map(product_row).collect::<Vec<_>>())?;\n\n        // Delete 3 rows by value\n        let f = || -> Result<_> {\n            env.datastore_delete_all_by_eq_bsatn(table_id, &bsatn_rows)?;\n            Ok(())\n        };\n        let tx = begin_mut_tx(&db);\n        let (tx, delete_result) = tx_slot.set(tx, f);\n\n        delete_result?;\n\n        let bytes_scanned = bsatn_rows.len();\n\n        assert_eq!(0, tx.metrics.index_seeks);\n        assert_eq!(3, tx.metrics.rows_scanned);\n        assert_eq!(bytes_scanned, tx.metrics.bytes_scanned);\n        assert_eq!(0, tx.metrics.bytes_written);\n        assert_eq!(0, tx.metrics.bytes_sent_to_clients);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/mod.rs",
    "content": "use anyhow::Context;\nuse bytes::Bytes;\nuse bytestring::ByteString;\nuse derive_more::Display;\nuse enum_map::Enum;\nuse once_cell::sync::OnceCell;\nuse spacetimedb_lib::bsatn;\nuse spacetimedb_lib::de::{serde::SeedWrapper, DeserializeSeed};\nuse spacetimedb_lib::ProductValue;\nuse spacetimedb_schema::def::deserialize::{ArgsSeed, FunctionDef};\nuse spacetimedb_schema::def::ModuleDef;\n\nmod disk_storage;\nmod host_controller;\nmod module_common;\n#[allow(clippy::too_many_arguments)]\npub mod module_host;\npub mod scheduler;\npub mod wasmtime;\n\n// Visible for integration testing.\npub mod instance_env;\npub mod v8; // only pub for testing\nmod wasm_common;\n\npub use disk_storage::DiskStorage;\npub use host_controller::{\n    extract_schema, CallProcedureReturn, CallResult, ExternalDurability, ExternalStorage, HostController,\n    MigratePlanResult, ProcedureCallResult, ProgramStorage, ReducerCallResult, ReducerOutcome,\n};\npub use module_host::{ModuleHost, NoSuchModule, ProcedureCallError, ReducerCallError, UpdateDatabaseResult};\npub use scheduler::Scheduler;\n\n/// Encoded arguments to a database function.\n///\n/// A database function is either a reducer or a procedure.\n#[derive(Debug, Clone)]\npub enum FunctionArgs {\n    Json(ByteString),\n    Bsatn(Bytes),\n    Nullary,\n}\n\nimpl FunctionArgs {\n    fn into_tuple_for_def<Def: FunctionDef>(\n        self,\n        module: &ModuleDef,\n        def: &Def,\n    ) -> Result<ArgsTuple, InvalidFunctionArguments> {\n        self.into_tuple(module.arg_seed_for(def))\n    }\n\n    fn into_tuple<Def: FunctionDef>(self, seed: ArgsSeed<'_, Def>) -> Result<ArgsTuple, InvalidFunctionArguments> {\n        self._into_tuple(seed).map_err(|err| InvalidFunctionArguments {\n            err,\n            function_name: seed.name().clone(),\n        })\n    }\n    fn _into_tuple<Def: FunctionDef>(self, seed: ArgsSeed<'_, Def>) -> anyhow::Result<ArgsTuple> {\n        Ok(match self {\n            FunctionArgs::Json(json) => ArgsTuple {\n                tuple: from_json_seed(&json, SeedWrapper(seed))?,\n                bsatn: OnceCell::new(),\n                json: OnceCell::with_value(json),\n            },\n            FunctionArgs::Bsatn(bytes) => ArgsTuple {\n                tuple: seed.deserialize(bsatn::Deserializer::new(&mut &bytes[..]))?,\n                bsatn: OnceCell::with_value(bytes),\n                json: OnceCell::new(),\n            },\n            FunctionArgs::Nullary => {\n                anyhow::ensure!(seed.params().elements.is_empty(), \"failed to typecheck args\");\n                ArgsTuple::nullary()\n            }\n        })\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct ArgsTuple {\n    tuple: ProductValue,\n    bsatn: OnceCell<Bytes>,\n    json: OnceCell<ByteString>,\n}\n\nimpl ArgsTuple {\n    pub fn nullary() -> Self {\n        ArgsTuple {\n            tuple: spacetimedb_sats::product![],\n            bsatn: OnceCell::with_value(Bytes::new()),\n            json: OnceCell::with_value(ByteString::from_static(\"[]\")),\n        }\n    }\n\n    pub fn get_bsatn(&self) -> &Bytes {\n        self.bsatn.get_or_init(|| bsatn::to_vec(&self.tuple).unwrap().into())\n    }\n    pub fn get_json(&self) -> &ByteString {\n        use spacetimedb_sats::ser::serde::SerializeWrapper;\n        self.json.get_or_init(|| {\n            serde_json::to_string(SerializeWrapper::from_ref(&self.tuple))\n                .unwrap()\n                .into()\n        })\n    }\n}\n\nimpl Default for ArgsTuple {\n    fn default() -> Self {\n        Self::nullary()\n    }\n}\n\n// TODO(noa): replace imports from this module with imports straight from primitives.\npub use spacetimedb_primitives::ReducerId;\nuse spacetimedb_schema::identifier::Identifier;\n\n/// Inner error type for [`InvalidReducerArguments`] and [`InvalidProcedureArguments`].\n#[derive(thiserror::Error, Debug)]\n#[error(\"invalid arguments for function {function_name}: {err}\")]\npub struct InvalidFunctionArguments {\n    #[source]\n    err: anyhow::Error,\n    function_name: Identifier,\n}\n\n/// Newtype over [`InvalidFunctionArguments`] which renders with the word \"reducer\".\n#[derive(thiserror::Error, Debug)]\n#[error(\"invalid arguments for reducer {}: {}\", .0.function_name, .0.err)]\npub struct InvalidReducerArguments(\n    #[from]\n    #[source]\n    InvalidFunctionArguments,\n);\n\n/// Newtype over [`InvalidFunctionArguments`] which renders with the word \"procedure\".\n#[derive(thiserror::Error, Debug)]\n#[error(\"invalid arguments for procedure {}: {}\", .0.function_name, .0.err)]\npub struct InvalidProcedureArguments(\n    #[from]\n    #[source]\n    InvalidFunctionArguments,\n);\n\n/// Newtype over [`InvalidFunctionArguments`] which renders with the word \"view\".\n#[derive(thiserror::Error, Debug)]\n#[error(\"invalid arguments for view {}: {}\", .0.function_name, .0.err)]\npub struct InvalidViewArguments(\n    #[from]\n    #[source]\n    InvalidFunctionArguments,\n);\n\nfn from_json_seed<'de, T: serde::de::DeserializeSeed<'de>>(s: &'de str, seed: T) -> anyhow::Result<T::Value> {\n    let mut de = serde_json::Deserializer::from_str(s);\n    let mut track = serde_path_to_error::Track::new();\n    let out = seed\n        .deserialize(serde_path_to_error::Deserializer::new(&mut de, &mut track))\n        .context(track.path())?;\n    de.end()?;\n    Ok(out)\n}\n\n/// Tags for each call that a `WasmInstanceEnv` can make.\n#[derive(Debug, Display, Enum, Clone, Copy, strum::AsRefStr)]\npub enum AbiCall {\n    TableIdFromName,\n    IndexIdFromName,\n    DatastoreTableRowCount,\n    DatastoreTableScanBsatn,\n    DatastoreIndexScanPointBsatn,\n    DatastoreIndexScanRangeBsatn,\n    RowIterBsatnAdvance,\n    RowIterBsatnClose,\n    DatastoreInsertBsatn,\n    DatastoreUpdateBsatn,\n    DatastoreDeleteByIndexScanPointBsatn,\n    DatastoreDeleteByIndexScanRangeBsatn,\n    DatastoreDeleteAllByEqBsatn,\n    BytesSourceRead,\n    BytesSourceRemainingLength,\n    BytesSinkWrite,\n    ConsoleLog,\n    ConsoleTimerStart,\n    ConsoleTimerEnd,\n    Identity,\n    JwtLength,\n    GetJwt,\n\n    VolatileNonatomicScheduleImmediate,\n\n    ProcedureSleepUntil,\n    ProcedureStartMutTransaction,\n    ProcedureCommitMutTransaction,\n    ProcedureAbortMutTransaction,\n    ProcedureHttpRequest,\n}\n"
  },
  {
    "path": "crates/core/src/host/module_common.rs",
    "content": "//! Module backend infrastructure, shared between different runtimes,\n//! like WASM and V8.\n\nuse crate::{\n    energy::EnergyMonitor,\n    host::{\n        module_host::ModuleInfo,\n        wasm_common::{module_host_actor::DescribeError, DESCRIBE_MODULE_DUNDER},\n        Scheduler,\n    },\n    module_host_context::ModuleCreationContext,\n    replica_context::ReplicaContext,\n};\nuse spacetimedb_lib::{Identity, RawModuleDef};\nuse spacetimedb_schema::{def::ModuleDef, error::ValidationErrors};\nuse std::sync::Arc;\n\n/// Builds a [`ModuleCommon`] from a [`RawModuleDef`].\npub fn build_common_module_from_raw(\n    mcc: ModuleCreationContext,\n    raw_def: RawModuleDef,\n) -> Result<ModuleCommon, ValidationErrors> {\n    // Perform a bunch of validation on the raw definition.\n    let def: ModuleDef = raw_def.try_into()?;\n\n    let replica_ctx = mcc.replica_ctx;\n    replica_ctx\n        .subscriptions\n        .set_module_def_version(def.raw_module_def_version());\n\n    // Note: assigns Reducer IDs based on the alphabetical order of reducer names.\n    let info = ModuleInfo::new(\n        def,\n        replica_ctx.owner_identity,\n        replica_ctx.database_identity,\n        mcc.program_hash,\n        replica_ctx.subscriptions.clone(),\n    );\n\n    Ok(ModuleCommon::new(replica_ctx, mcc.scheduler, info, mcc.energy_monitor))\n}\n\n/// Non-runtime-specific parts of a module.\n#[derive(Clone)]\npub(crate) struct ModuleCommon {\n    replica_context: Arc<ReplicaContext>,\n    scheduler: Scheduler,\n    info: Arc<ModuleInfo>,\n    energy_monitor: Arc<dyn EnergyMonitor>,\n}\n\nimpl ModuleCommon {\n    /// Returns a new common module.\n    fn new(\n        replica_context: Arc<ReplicaContext>,\n        scheduler: Scheduler,\n        info: Arc<ModuleInfo>,\n        energy_monitor: Arc<dyn EnergyMonitor>,\n    ) -> Self {\n        Self {\n            replica_context,\n            scheduler,\n            info,\n            energy_monitor,\n        }\n    }\n\n    /// Returns the module info.\n    pub fn info(&self) -> Arc<ModuleInfo> {\n        self.info.clone()\n    }\n\n    /// Returns the identity of the database.\n    pub fn database_identity(&self) -> &Identity {\n        &self.info.database_identity\n    }\n\n    /// Returns the energy monitor.\n    pub fn energy_monitor(&self) -> Arc<dyn EnergyMonitor> {\n        self.energy_monitor.clone()\n    }\n}\n\nimpl ModuleCommon {\n    pub fn replica_ctx(&self) -> &Arc<ReplicaContext> {\n        &self.replica_context\n    }\n\n    pub fn scheduler(&self) -> &Scheduler {\n        &self.scheduler\n    }\n}\n\n/// Runs the describer of modules in `run` and does some logging around it.\npub(crate) fn run_describer<T>(\n    log_traceback: impl Copy + FnOnce(&str, &str, &anyhow::Error),\n    run: impl FnOnce() -> anyhow::Result<T>,\n) -> Result<T, DescribeError> {\n    let describer_func_name = DESCRIBE_MODULE_DUNDER;\n\n    let start = std::time::Instant::now();\n    log::trace!(\"Start describer \\\"{describer_func_name}\\\"...\");\n\n    let result = run();\n\n    let duration = start.elapsed();\n    log::trace!(\"Describer \\\"{}\\\" ran: {} us\", describer_func_name, duration.as_micros());\n\n    result\n        .inspect_err(|err| log_traceback(\"describer\", describer_func_name, err))\n        .map_err(DescribeError::RuntimeError)\n}\n"
  },
  {
    "path": "crates/core/src/host/module_host.rs",
    "content": "use super::{\n    ArgsTuple, FunctionArgs, InvalidProcedureArguments, InvalidReducerArguments, ReducerCallResult, ReducerId,\n    ReducerOutcome, Scheduler,\n};\nuse crate::client::messages::{OneOffQueryResponseMessage, SerializableMessage};\nuse crate::client::{ClientActorId, ClientConnectionSender};\nuse crate::database_logger::{DatabaseLogger, LogLevel, Record};\nuse crate::db::relational_db::RelationalDB;\nuse crate::energy::EnergyQuanta;\nuse crate::error::DBError;\nuse crate::estimation::{check_row_limit, estimate_rows_scanned};\nuse crate::hash::Hash;\nuse crate::host::host_controller::CallProcedureReturn;\nuse crate::host::scheduler::{CallScheduledFunctionResult, ScheduledFunctionParams};\nuse crate::host::v8::JsInstance;\npub use crate::host::wasm_common::module_host_actor::{InstanceCommon, WasmInstance};\nuse crate::host::wasmtime::ModuleInstance;\nuse crate::host::{InvalidFunctionArguments, InvalidViewArguments};\nuse crate::identity::Identity;\nuse crate::messages::control_db::{Database, HostType};\nuse crate::replica_context::ReplicaContext;\nuse crate::sql::ast::SchemaViewer;\nuse crate::sql::execute::SqlResult;\nuse crate::sql::parser::RowLevelExpr;\nuse crate::subscription::module_subscription_actor::ModuleSubscriptions;\nuse crate::subscription::tx::DeltaTx;\nuse crate::subscription::websocket_building::{BuildableWebsocketFormat, RowListBuilderSource};\nuse crate::subscription::{execute_plan, execute_plan_for_view};\nuse crate::util::jobs::SingleCoreExecutor;\nuse crate::worker_metrics::WORKER_METRICS;\nuse anyhow::Context;\nuse bytes::Bytes;\nuse derive_more::From;\nuse futures::lock::Mutex;\nuse indexmap::IndexSet;\nuse itertools::Itertools;\nuse prometheus::{Histogram, IntGauge};\nuse scopeguard::ScopeGuard;\nuse smallvec::SmallVec;\nuse spacetimedb_auth::identity::ConnectionAuthCtx;\nuse spacetimedb_client_api_messages::energy::FunctionBudget;\nuse spacetimedb_client_api_messages::websocket::common::{ByteListLen as _, RowListLen as _};\nuse spacetimedb_client_api_messages::websocket::v1::{self as ws_v1};\nuse spacetimedb_client_api_messages::websocket::v2::{self as ws_v2};\nuse spacetimedb_data_structures::error_stream::ErrorStream;\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashSet};\nuse spacetimedb_datastore::error::DatastoreError;\nuse spacetimedb_datastore::execution_context::{Workload, WorkloadType};\nuse spacetimedb_datastore::locking_tx_datastore::{MutTxId, ViewCallInfo};\nuse spacetimedb_datastore::traits::{IsolationLevel, Program, TxData};\nuse spacetimedb_durability::DurableOffset;\nuse spacetimedb_execution::pipelined::{PipelinedProject, ViewProject};\nuse spacetimedb_execution::RelValue;\nuse spacetimedb_expr::expr::CollectViews;\nuse spacetimedb_lib::db::raw_def::v9::Lifecycle;\nuse spacetimedb_lib::identity::{AuthCtx, RequestId};\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::{ConnectionId, Timestamp};\nuse spacetimedb_primitives::{ArgId, ProcedureId, TableId, ViewFnPtr, ViewId};\nuse spacetimedb_query::compile_subscription;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, ProductValue};\nuse spacetimedb_schema::auto_migrate::{AutoMigrateError, MigrationPolicy};\nuse spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, TableDef, ViewDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_schema::schema::{Schema, TableSchema};\nuse spacetimedb_schema::table_name::TableName;\nuse std::collections::VecDeque;\nuse std::fmt;\nuse std::sync::atomic::AtomicBool;\nuse std::sync::{Arc, Weak};\nuse std::time::{Duration, Instant};\nuse tokio::sync::oneshot;\n\n#[derive(Debug, Default, Clone, From)]\npub struct DatabaseUpdate {\n    pub tables: SmallVec<[DatabaseTableUpdate; 1]>,\n}\n\nimpl FromIterator<DatabaseTableUpdate> for DatabaseUpdate {\n    fn from_iter<T: IntoIterator<Item = DatabaseTableUpdate>>(iter: T) -> Self {\n        DatabaseUpdate {\n            tables: iter.into_iter().collect(),\n        }\n    }\n}\n\nimpl DatabaseUpdate {\n    pub fn is_empty(&self) -> bool {\n        if self.tables.len() == 0 {\n            return true;\n        }\n        false\n    }\n\n    pub fn from_writes(tx_data: &TxData) -> Self {\n        let entries = tx_data.iter_table_entries();\n        let mut tables = SmallVec::with_capacity(entries.len());\n        tables.extend(entries.map(|(table_id, e)| DatabaseTableUpdate {\n            table_id,\n            table_name: e.table_name.clone(),\n            inserts: e.inserts.clone(),\n            deletes: e.deletes.clone(),\n        }));\n        DatabaseUpdate { tables }\n    }\n\n    /// The number of rows in the payload\n    pub fn num_rows(&self) -> usize {\n        self.tables.iter().map(|t| t.inserts.len() + t.deletes.len()).sum()\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct DatabaseTableUpdate {\n    pub table_id: TableId,\n    pub table_name: TableName,\n    // Note: `Arc<[ProductValue]>` allows to cheaply\n    // use the values from `TxData` without cloning the\n    // contained `ProductValue`s.\n    pub inserts: Arc<[ProductValue]>,\n    pub deletes: Arc<[ProductValue]>,\n}\n\n#[derive(Debug)]\npub struct DatabaseUpdateRelValue<'a> {\n    pub tables: Vec<DatabaseTableUpdateRelValue<'a>>,\n}\n\n#[derive(PartialEq, Debug)]\npub struct DatabaseTableUpdateRelValue<'a> {\n    pub table_id: TableId,\n    pub table_name: TableName,\n    pub updates: UpdatesRelValue<'a>,\n}\n\n#[derive(Default, PartialEq, Debug)]\npub struct UpdatesRelValue<'a> {\n    pub deletes: Vec<RelValue<'a>>,\n    pub inserts: Vec<RelValue<'a>>,\n}\n\nimpl UpdatesRelValue<'_> {\n    /// Returns whether there are any updates.\n    pub fn has_updates(&self) -> bool {\n        !(self.deletes.is_empty() && self.inserts.is_empty())\n    }\n\n    pub fn encode<F: BuildableWebsocketFormat>(\n        &self,\n        rlb_pool: &impl RowListBuilderSource<F>,\n    ) -> (F::QueryUpdate, u64, usize) {\n        let (deletes, nr_del) = F::encode_list(rlb_pool.take_row_list_builder(), self.deletes.iter());\n        let (inserts, nr_ins) = F::encode_list(rlb_pool.take_row_list_builder(), self.inserts.iter());\n        let num_rows = nr_del + nr_ins;\n        let num_bytes = deletes.num_bytes() + inserts.num_bytes();\n        let qu = ws_v1::QueryUpdate { deletes, inserts };\n        // We don't compress individual table updates.\n        // Previously we were, but the benefits, if any, were unclear.\n        // Note, each message is still compressed before being sent to clients,\n        // but we no longer have to hold a tx lock when doing so.\n        let cqu = F::into_query_update(qu, ws_v1::Compression::None);\n        (cqu, num_rows, num_bytes)\n    }\n}\n\n#[derive(Debug, Clone)]\npub enum EventStatus {\n    Committed(DatabaseUpdate),\n    FailedUser(String),\n    FailedInternal(String),\n    OutOfEnergy,\n}\n\nimpl EventStatus {\n    pub fn database_update(&self) -> Option<&DatabaseUpdate> {\n        match self {\n            EventStatus::Committed(upd) => Some(upd),\n            _ => None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Default)]\npub struct ModuleFunctionCall {\n    pub reducer: Option<ReducerName>,\n    pub reducer_id: ReducerId,\n    pub args: ArgsTuple,\n}\n\nimpl ModuleFunctionCall {\n    pub fn update() -> Self {\n        Self {\n            reducer: None,\n            reducer_id: u32::MAX.into(),\n            args: ArgsTuple::nullary(),\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct ModuleEvent {\n    pub timestamp: Timestamp,\n    pub caller_identity: Identity,\n    pub caller_connection_id: Option<ConnectionId>,\n    pub function_call: ModuleFunctionCall,\n    pub status: EventStatus,\n    pub reducer_return_value: Option<Bytes>,\n    pub energy_quanta_used: EnergyQuanta,\n    pub host_execution_duration: Duration,\n    pub request_id: Option<RequestId>,\n    pub timer: Option<Instant>,\n}\n\n/// Information about a running module.\npub struct ModuleInfo {\n    /// The definition of the module.\n    /// Loaded by loading the module's program from the system tables, extracting its definition,\n    /// and validating.\n    pub module_def: Arc<ModuleDef>,\n    /// The identity of the module.\n    pub owner_identity: Identity,\n    /// The identity of the database.\n    pub database_identity: Identity,\n    /// The hash of the module.\n    pub module_hash: Hash,\n    /// Subscriptions to this module.\n    pub subscriptions: ModuleSubscriptions,\n    /// Metrics handles for this module.\n    pub metrics: ModuleMetrics,\n}\n\nimpl fmt::Debug for ModuleInfo {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ModuleInfo\")\n            .field(\"module_def\", &self.module_def)\n            .field(\"owner_identity\", &self.owner_identity)\n            .field(\"database_identity\", &self.database_identity)\n            .field(\"module_hash\", &self.module_hash)\n            .finish()\n    }\n}\n\n#[derive(Debug)]\npub struct ModuleMetrics {\n    pub connected_clients: IntGauge,\n    pub ws_clients_spawned: IntGauge,\n    pub ws_clients_aborted: IntGauge,\n    pub request_round_trip_subscribe: Histogram,\n    pub request_round_trip_unsubscribe: Histogram,\n    pub request_round_trip_sql: Histogram,\n}\n\nimpl ModuleMetrics {\n    fn new(db: &Identity) -> Self {\n        let connected_clients = WORKER_METRICS.connected_clients.with_label_values(db);\n        let ws_clients_spawned = WORKER_METRICS.ws_clients_spawned.with_label_values(db);\n        let ws_clients_aborted = WORKER_METRICS.ws_clients_aborted.with_label_values(db);\n        let request_round_trip_subscribe =\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Subscribe, db, \"\");\n        let request_round_trip_unsubscribe =\n            WORKER_METRICS\n                .request_round_trip\n                .with_label_values(&WorkloadType::Unsubscribe, db, \"\");\n        let request_round_trip_sql = WORKER_METRICS\n            .request_round_trip\n            .with_label_values(&WorkloadType::Sql, db, \"\");\n        Self {\n            connected_clients,\n            ws_clients_spawned,\n            ws_clients_aborted,\n            request_round_trip_subscribe,\n            request_round_trip_unsubscribe,\n            request_round_trip_sql,\n        }\n    }\n}\n\nimpl ModuleInfo {\n    /// Create a new `ModuleInfo`.\n    /// Reducers are sorted alphabetically by name and assigned IDs.\n    pub fn new(\n        module_def: ModuleDef,\n        owner_identity: Identity,\n        database_identity: Identity,\n        module_hash: Hash,\n        subscriptions: ModuleSubscriptions,\n    ) -> Arc<Self> {\n        let metrics = ModuleMetrics::new(&database_identity);\n        Arc::new(ModuleInfo {\n            module_def: Arc::new(module_def),\n            owner_identity,\n            database_identity,\n            module_hash,\n            subscriptions,\n            metrics,\n        })\n    }\n\n    pub fn relational_db(&self) -> &Arc<RelationalDB> {\n        self.subscriptions.relational_db()\n    }\n}\n\n/// A bidirectional map between `Identifiers` (reducer names) and `ReducerId`s.\n/// Invariant: the reducer names are in the same order as they were declared in the `ModuleDef`.\npub struct ReducersMap(IndexSet<Box<str>>);\n\nimpl<'a> FromIterator<&'a str> for ReducersMap {\n    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {\n        Self(iter.into_iter().map_into().collect())\n    }\n}\n\nimpl fmt::Debug for ReducersMap {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nimpl ReducersMap {\n    /// Lookup the ID for a reducer name.\n    pub fn lookup_id(&self, reducer_name: &str) -> Option<ReducerId> {\n        self.0.get_index_of(reducer_name).map(ReducerId::from)\n    }\n\n    /// Lookup the name for a reducer ID.\n    pub fn lookup_name(&self, reducer_id: ReducerId) -> Option<&str> {\n        let result = self.0.get_index(reducer_id.0 as _)?;\n        Some(&**result)\n    }\n}\n\npub enum ModuleWithInstance {\n    Wasm {\n        module: super::wasmtime::Module,\n        executor: SingleCoreExecutor,\n        init_inst: Box<super::wasmtime::ModuleInstance>,\n    },\n    Js {\n        module: super::v8::JsModule,\n        init_inst: super::v8::JsInstance,\n    },\n}\n\nenum ModuleHostInner {\n    Wasm(WasmtimeModuleHost),\n    Js(V8ModuleHost),\n}\n\nstruct WasmtimeModuleHost {\n    executor: SingleCoreExecutor,\n    instance_manager: ModuleInstanceManager<super::wasmtime::Module>,\n}\n\nstruct V8ModuleHost {\n    instance_manager: ModuleInstanceManager<super::v8::JsModule>,\n}\n\n/// A module; used as a bound on `InstanceManager`.\ntrait GenericModule {\n    type Instance: GenericModuleInstance;\n    async fn create_instance(&self) -> Self::Instance;\n    fn host_type(&self) -> HostType;\n}\n\ntrait GenericModuleInstance {\n    fn trapped(&self) -> bool;\n}\n\nimpl<T: WasmInstance> GenericModuleInstance for super::wasm_common::module_host_actor::WasmModuleInstance<T> {\n    fn trapped(&self) -> bool {\n        self.trapped()\n    }\n}\n\nimpl<T: GenericModuleInstance + ?Sized> GenericModuleInstance for Box<T> {\n    fn trapped(&self) -> bool {\n        (**self).trapped()\n    }\n}\n\nimpl GenericModule for super::wasmtime::Module {\n    type Instance = Box<super::wasmtime::ModuleInstance>;\n    async fn create_instance(&self) -> Self::Instance {\n        Box::new(self.create_instance())\n    }\n    fn host_type(&self) -> HostType {\n        HostType::Wasm\n    }\n}\n\nimpl GenericModule for super::v8::JsModule {\n    type Instance = super::v8::JsInstance;\n    async fn create_instance(&self) -> Self::Instance {\n        self.create_instance().await\n    }\n    fn host_type(&self) -> HostType {\n        HostType::Js\n    }\n}\n\nimpl GenericModuleInstance for super::v8::JsInstance {\n    fn trapped(&self) -> bool {\n        self.trapped()\n    }\n}\n\n/// Creates the table for `table_def` in `stdb`.\npub fn create_table_from_def(\n    stdb: &RelationalDB,\n    tx: &mut MutTxId,\n    module_def: &ModuleDef,\n    table_def: &TableDef,\n) -> anyhow::Result<()> {\n    let schema = TableSchema::from_module_def(module_def, table_def, (), TableId::SENTINEL);\n    stdb.create_table(tx, schema)\n        .with_context(|| format!(\"failed to create table {}\", &table_def.name))?;\n    Ok(())\n}\n\n/// Creates the table for `view_def` in `stdb`.\npub fn create_table_from_view_def(\n    stdb: &RelationalDB,\n    tx: &mut MutTxId,\n    module_def: &ModuleDef,\n    view_def: &ViewDef,\n) -> anyhow::Result<()> {\n    stdb.create_view(tx, module_def, view_def)\n        .with_context(|| format!(\"failed to create table for view {}\", &view_def.name))?;\n    Ok(())\n}\n\n/// Moves out the `trapped: bool` from `res`.\nfn extract_trapped<T, E>(res: Result<(T, bool), E>) -> (Result<T, E>, bool) {\n    match res {\n        Ok((x, t)) => (Ok(x), t),\n        Err(x) => (Err(x), false),\n    }\n}\n\n/// If the module instance's `replica_ctx` is uninitialized, initialize it.\npub(crate) fn init_database(\n    replica_ctx: &ReplicaContext,\n    module_def: &ModuleDef,\n    program: Program,\n    call_reducer: impl FnOnce(Option<MutTxId>, CallReducerParams) -> (ReducerCallResult, bool),\n) -> (anyhow::Result<Option<ReducerCallResult>>, bool) {\n    extract_trapped(init_database_inner(replica_ctx, module_def, program, call_reducer))\n}\n\nfn init_database_inner(\n    replica_ctx: &ReplicaContext,\n    module_def: &ModuleDef,\n    program: Program,\n    call_reducer: impl FnOnce(Option<MutTxId>, CallReducerParams) -> (ReducerCallResult, bool),\n) -> anyhow::Result<(Option<ReducerCallResult>, bool)> {\n    log::debug!(\"init database\");\n    let timestamp = Timestamp::now();\n    let stdb = &*replica_ctx.relational_db;\n    let logger = replica_ctx.logger.system_logger();\n    let owner_identity = replica_ctx.database.owner_identity;\n\n    let tx = stdb.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);\n    let auth_ctx = AuthCtx::for_current(owner_identity);\n    let (tx, ()) = stdb\n        .with_auto_rollback(tx, |tx| {\n            // Create all in-memory tables defined by the module,\n            // with IDs ordered lexicographically by the table names.\n            let mut table_defs: Vec<_> = module_def.tables().collect();\n            table_defs.sort_by_key(|x| &x.name);\n            for def in table_defs {\n                logger.info(&format!(\"Creating table `{}`\", &def.name));\n                create_table_from_def(stdb, tx, module_def, def)?;\n            }\n\n            // Create all in-memory views defined by the module.\n            let mut view_defs: Vec<_> = module_def.views().collect();\n            view_defs.sort_by_key(|x| &x.name);\n            for def in view_defs {\n                logger.info(&format!(\"Creating table for view `{}`\", &def.name));\n                create_table_from_view_def(stdb, tx, module_def, def)?;\n            }\n\n            // Insert the late-bound row-level security expressions.\n            for rls in module_def.row_level_security() {\n                logger.info(&format!(\"Creating row level security `{}`\", rls.sql));\n\n                let rls = RowLevelExpr::build_row_level_expr(tx, &auth_ctx, rls)\n                    .with_context(|| format!(\"failed to create row-level security: `{}`\", rls.sql))?;\n                let table_id = rls.def.table_id;\n                let sql = rls.def.sql.clone();\n                stdb.create_row_level_security(tx, rls.def)\n                    .with_context(|| format!(\"failed to create row-level security for table `{table_id}`: `{sql}`\",))?;\n            }\n\n            stdb.set_initialized(tx, program)?;\n\n            anyhow::Ok(())\n        })\n        .inspect_err(|e| log::error!(\"{e:?}\"))?;\n\n    let rcr = match module_def.lifecycle_reducer(Lifecycle::Init) {\n        None => {\n            if let Some((_tx_offset, tx_data, tx_metrics, reducer)) = stdb.commit_tx(tx)? {\n                stdb.report_mut_tx_metrics(reducer, tx_metrics, Some(tx_data));\n            }\n            (None, false)\n        }\n\n        Some((reducer_id, _)) => {\n            logger.info(\"Invoking `init` reducer\");\n            let params = CallReducerParams::from_system(timestamp, owner_identity, reducer_id, ArgsTuple::nullary());\n            let (res, trapped) = call_reducer(Some(tx), params);\n            (Some(res), trapped)\n        }\n    };\n\n    logger.info(\"Database initialized\");\n    Ok(rcr)\n}\n\npub fn call_identity_connected(\n    caller_auth: ConnectionAuthCtx,\n    caller_connection_id: ConnectionId,\n    module: &ModuleInfo,\n    call_reducer: impl FnOnce(Option<MutTxId>, CallReducerParams) -> (ReducerCallResult, bool),\n    trapped_slot: &mut bool,\n) -> Result<(), ClientConnectedError> {\n    let reducer_lookup = module.module_def.lifecycle_reducer(Lifecycle::OnConnect);\n    let stdb = module.relational_db();\n    let workload = Workload::reducer_no_args(\n        ReducerName::new(Identifier::new_assume_valid(\"call_identity_connected\".into())),\n        caller_auth.claims.identity,\n        caller_connection_id,\n    );\n    let mut_tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload);\n    let mut mut_tx = scopeguard::guard(mut_tx, |mut_tx| {\n        // If we crash before committing, we need to ensure that the transaction is rolled back.\n        // This is necessary to avoid leaving the database in an inconsistent state.\n        log::debug!(\"call_identity_connected: rolling back transaction\");\n        let (_, metrics, reducer_name) = mut_tx.rollback();\n        stdb.report_mut_tx_metrics(reducer_name, metrics, None);\n    });\n\n    mut_tx\n        .insert_st_client(\n            caller_auth.claims.identity,\n            caller_connection_id,\n            &caller_auth.jwt_payload,\n        )\n        .map_err(DBError::from)\n        .map_err(Box::new)?;\n\n    if let Some((reducer_id, reducer_def)) = reducer_lookup {\n        // The module defined a lifecycle reducer to handle new connections.\n        // Call this reducer.\n        // If the call fails (as in, something unexpectedly goes wrong with guest execution),\n        // abort the connection: we can't really recover.\n        let tx = Some(ScopeGuard::into_inner(mut_tx));\n        let params = ModuleHost::call_reducer_params(\n            module,\n            caller_auth.claims.identity,\n            Some(caller_connection_id),\n            None,\n            None,\n            None,\n            reducer_id,\n            reducer_def,\n            FunctionArgs::Nullary,\n        )\n        .map_err(ReducerCallError::from)?;\n        let (reducer_outcome, trapped) = call_reducer(tx, params);\n        *trapped_slot = trapped;\n\n        match reducer_outcome.outcome {\n            // If the reducer committed successfully, we're done.\n            // `WasmModuleInstance::call_reducer_with_tx` has already ensured\n            // that `st_client` is updated appropriately.\n            //\n            // It's necessary to spread out the responsibility for updating `st_client` in this way\n            // because it's important that `call_identity_connected` commit at most one transaction.\n            // A naive implementation of this method would just run the reducer first,\n            // then insert into `st_client`,\n            // but if we crashed in between, we'd be left in an inconsistent state\n            // where the reducer had run but `st_client` was not yet updated.\n            ReducerOutcome::Committed => Ok(()),\n\n            // If the reducer returned an error or couldn't run due to insufficient energy,\n            // abort the connection: the module code has decided it doesn't want this client.\n            ReducerOutcome::Failed(message) => Err(ClientConnectedError::Rejected(*message)),\n            ReducerOutcome::BudgetExceeded => Err(ClientConnectedError::OutOfEnergy),\n        }\n    } else {\n        // The module doesn't define a client_connected reducer.\n        // We need to commit the transaction to update st_clients and st_connection_credentials.\n        //\n        // This is necessary to be able to disconnect clients after a server crash.\n\n        // TODO: Is this being broadcast? Does it need to be, or are st_client table subscriptions\n        // not allowed?\n        // I (jsdt) don't think it was being broadcast previously. See:\n        // https://github.com/clockworklabs/SpacetimeDB/issues/3130\n        stdb.finish_tx(ScopeGuard::into_inner(mut_tx), Ok(()))\n            .map_err(|e: DBError| {\n                log::error!(\"`call_identity_connected`: finish transaction failed: {e:#?}\");\n                ClientConnectedError::DBError(e.into())\n            })?;\n        Ok(())\n    }\n}\n\npub struct CallReducerParams {\n    pub timestamp: Timestamp,\n    pub caller_identity: Identity,\n    pub caller_connection_id: ConnectionId,\n    pub client: Option<Arc<ClientConnectionSender>>,\n    pub request_id: Option<RequestId>,\n    pub timer: Option<Instant>,\n    pub reducer_id: ReducerId,\n    pub args: ArgsTuple,\n}\n\nimpl CallReducerParams {\n    /// Returns a set of parameters for an internal call\n    /// without a client/caller/request_id.\n    pub fn from_system(\n        timestamp: Timestamp,\n        caller_identity: Identity,\n        reducer_id: ReducerId,\n        args: ArgsTuple,\n    ) -> Self {\n        Self {\n            timestamp,\n            caller_identity,\n            caller_connection_id: ConnectionId::ZERO,\n            client: None,\n            request_id: None,\n            timer: None,\n            reducer_id,\n            args,\n        }\n    }\n}\n\npub enum ViewCommand {\n    AddSingleSubscription {\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeSingle,\n        _timer: Instant,\n    },\n    AddMultiSubscription {\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeMulti,\n        _timer: Instant,\n    },\n    AddLegacySubscription {\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        subscribe: ws_v1::Subscribe,\n        _timer: Instant,\n    },\n    AddSubscriptionV2 {\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Subscribe,\n        _timer: Instant,\n    },\n    RemoveSubscriptionV2 {\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Unsubscribe,\n        timer: Instant,\n    },\n    Sql {\n        db: Arc<RelationalDB>,\n        sql_text: String,\n        auth: AuthCtx,\n        subs: Option<ModuleSubscriptions>,\n    },\n}\n\n#[derive(Debug)]\npub enum ViewCommandResult {\n    Subscription {\n        result: Result<Option<ExecutionMetrics>, DBError>,\n    },\n\n    Sql {\n        result: Result<SqlResult, DBError>,\n        head: Vec<(RawIdentifier, AlgebraicType)>,\n    },\n}\npub struct CallViewParams {\n    pub view_name: Identifier,\n    pub view_id: ViewId,\n    pub table_id: TableId,\n    pub fn_ptr: ViewFnPtr,\n    /// This is not always the same identity as `sender`.\n    /// For subscribe and sql calls it will be.\n    /// However for atomic view update after a reducer call,\n    /// this will be the caller of the reducer.\n    pub caller: Identity,\n    pub sender: Option<Identity>,\n    pub args: ArgsTuple,\n    pub row_type: AlgebraicTypeRef,\n    pub timestamp: Timestamp,\n}\n\npub struct CallProcedureParams {\n    pub timestamp: Timestamp,\n    pub caller_identity: Identity,\n    pub caller_connection_id: ConnectionId,\n    pub timer: Option<Instant>,\n    pub procedure_id: ProcedureId,\n    pub args: ArgsTuple,\n}\n\nimpl CallProcedureParams {\n    /// Returns a set of parameters for an internal call\n    /// without a client/caller/request_id.\n    pub fn from_system(\n        timestamp: Timestamp,\n        caller_identity: Identity,\n        procedure_id: ProcedureId,\n        args: ArgsTuple,\n    ) -> Self {\n        Self {\n            timestamp,\n            caller_identity,\n            caller_connection_id: ConnectionId::ZERO,\n            timer: None,\n            procedure_id,\n            args,\n        }\n    }\n}\n\n/// Holds a [`Module`] and a set of [`Instance`]s from it,\n/// and allocates the [`Instance`]s to be used for function calls.\n///\n/// Capable of managing and allocating multiple instances of the same module,\n/// but this functionality is currently unused, as only one reducer runs at a time.\n/// When we introduce procedures, it will be necessary to have multiple instances,\n/// as each procedure invocation will have its own sandboxed instance,\n/// and multiple procedures can run concurrently with up to one reducer.\nstruct ModuleInstanceManager<M: GenericModule> {\n    instances: Mutex<VecDeque<M::Instance>>,\n    module: M,\n    create_instance_time_metric: CreateInstanceTimeMetric,\n}\n\n/// Handle on the `spacetime_module_create_instance_time_seconds` label for a particular database\n/// which calls `remove_label_values` to clean up on drop.\nstruct CreateInstanceTimeMetric {\n    metric: Histogram,\n    host_type: HostType,\n    database_identity: Identity,\n}\n\nimpl Drop for CreateInstanceTimeMetric {\n    fn drop(&mut self) {\n        let _ = WORKER_METRICS\n            .module_create_instance_time_seconds\n            .remove_label_values(&self.database_identity, &self.host_type);\n    }\n}\n\nimpl CreateInstanceTimeMetric {\n    fn observe(&self, duration: std::time::Duration) {\n        self.metric.observe(duration.as_secs_f64());\n    }\n}\n\nimpl<M: GenericModule> ModuleInstanceManager<M> {\n    fn new(module: M, init_inst: M::Instance, database_identity: Identity) -> Self {\n        let host_type = module.host_type();\n        let create_instance_time_metric = CreateInstanceTimeMetric {\n            metric: WORKER_METRICS\n                .module_create_instance_time_seconds\n                .with_label_values(&database_identity, &host_type),\n            host_type,\n            database_identity,\n        };\n\n        // Add the first instance.\n        let mut instances = VecDeque::new();\n        instances.push_front(init_inst);\n\n        Self {\n            instances: Mutex::new(instances),\n            module,\n            create_instance_time_metric,\n        }\n    }\n\n    async fn with_instance<R>(&self, f: impl AsyncFnOnce(M::Instance) -> (R, M::Instance)) -> R {\n        let inst = self.get_instance().await;\n        let (res, inst) = f(inst).await;\n        self.return_instance(inst).await;\n        res\n    }\n\n    async fn get_instance(&self) -> M::Instance {\n        let inst = self.instances.lock().await.pop_back();\n        if let Some(inst) = inst {\n            inst\n        } else {\n            let start_time = std::time::Instant::now();\n            let res = self.module.create_instance().await;\n            let elapsed_time = start_time.elapsed();\n            self.create_instance_time_metric.observe(elapsed_time);\n            res\n        }\n    }\n\n    async fn return_instance(&self, inst: M::Instance) {\n        if inst.trapped() {\n            // Don't return trapped instances;\n            // they may have left internal data structures in the guest `Instance`\n            // (WASM linear memory, V8 global scope) in a bad state.\n            return;\n        }\n\n        self.instances.lock().await.push_front(inst);\n    }\n}\n\n#[derive(Clone)]\npub struct ModuleHost {\n    pub info: Arc<ModuleInfo>,\n    inner: Arc<ModuleHostInner>,\n    /// Called whenever a reducer call on this host panics.\n    on_panic: Arc<dyn Fn() + Send + Sync + 'static>,\n\n    /// Marks whether this module has been closed by [`Self::exit`].\n    ///\n    /// When this is true, most operations will fail with [`NoSuchModule`].\n    closed: Arc<AtomicBool>,\n}\n\nimpl fmt::Debug for ModuleHost {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ModuleHost\")\n            .field(\"info\", &self.info)\n            .field(\"inner\", &Arc::as_ptr(&self.inner))\n            .finish()\n    }\n}\n\npub struct WeakModuleHost {\n    info: Arc<ModuleInfo>,\n    inner: Weak<ModuleHostInner>,\n    on_panic: Weak<dyn Fn() + Send + Sync + 'static>,\n    closed: Weak<AtomicBool>,\n}\n\n#[derive(Debug)]\npub enum UpdateDatabaseResult {\n    NoUpdateNeeded,\n    UpdatePerformed,\n    UpdatePerformedWithClientDisconnect,\n    AutoMigrateError(Box<ErrorStream<AutoMigrateError>>),\n    ErrorExecutingMigration(anyhow::Error),\n}\nimpl UpdateDatabaseResult {\n    /// Check if a database update was successful.\n    pub fn was_successful(&self) -> bool {\n        matches!(\n            self,\n            UpdateDatabaseResult::UpdatePerformed\n                | UpdateDatabaseResult::NoUpdateNeeded\n                | UpdateDatabaseResult::UpdatePerformedWithClientDisconnect\n        )\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\n#[error(\"no such module\")]\npub struct NoSuchModule;\n\n#[derive(thiserror::Error, Debug)]\npub enum ReducerCallError {\n    #[error(transparent)]\n    Args(#[from] InvalidReducerArguments),\n    #[error(transparent)]\n    NoSuchModule(#[from] NoSuchModule),\n    #[error(\"no such reducer\")]\n    NoSuchReducer,\n    #[error(\"no such scheduled reducer\")]\n    ScheduleReducerNotFound,\n    #[error(\"can't directly call special {0:?} lifecycle reducer\")]\n    LifecycleReducer(Lifecycle),\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum ViewOutcome {\n    Success,\n    Failed(String),\n    BudgetExceeded,\n}\n\nimpl From<EventStatus> for ViewOutcome {\n    fn from(status: EventStatus) -> Self {\n        match status {\n            EventStatus::Committed(_) => ViewOutcome::Success,\n            EventStatus::FailedUser(e) | EventStatus::FailedInternal(e) => ViewOutcome::Failed(e),\n            EventStatus::OutOfEnergy => ViewOutcome::BudgetExceeded,\n        }\n    }\n}\n\npub struct ViewCallResult {\n    pub outcome: ViewOutcome,\n    pub tx: MutTxId,\n    pub energy_used: FunctionBudget,\n    pub total_duration: Duration,\n    pub abi_duration: Duration,\n}\n\nimpl fmt::Debug for ViewCallResult {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ViewCallResult\")\n            .field(\"outcome\", &self.outcome)\n            .field(\"energy_used\", &self.energy_used)\n            .field(\"total_duration\", &self.total_duration)\n            .field(\"abi_duration\", &self.abi_duration)\n            .finish()\n    }\n}\n\nimpl ViewCallResult {\n    pub fn default(tx: MutTxId) -> Self {\n        Self {\n            outcome: ViewOutcome::Success,\n            energy_used: FunctionBudget::ZERO,\n            total_duration: Duration::ZERO,\n            abi_duration: Duration::ZERO,\n            tx,\n        }\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum ViewCallError {\n    #[error(transparent)]\n    Args(#[from] InvalidViewArguments),\n    #[error(transparent)]\n    NoSuchModule(#[from] NoSuchModule),\n    #[error(\"no such view\")]\n    NoSuchView,\n    #[error(\"Table does not exist for view `{0}`\")]\n    TableDoesNotExist(ViewId),\n    #[error(\"missing client connection for view call trigged by subscription\")]\n    MissingClientConnection,\n    #[error(\"DB error during view call: {0}\")]\n    DatastoreError(#[from] DatastoreError),\n    #[error(\"The module instance encountered a fatal error: {0}\")]\n    InternalError(String),\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum ProcedureCallError {\n    #[error(transparent)]\n    Args(#[from] InvalidProcedureArguments),\n    #[error(transparent)]\n    NoSuchModule(#[from] NoSuchModule),\n    #[error(\"No such procedure\")]\n    NoSuchProcedure,\n    #[error(\"Procedure terminated due to insufficient budget\")]\n    OutOfEnergy,\n    #[error(\"The module instance encountered a fatal error: {0}\")]\n    InternalError(String),\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum InitDatabaseError {\n    #[error(transparent)]\n    Args(#[from] InvalidReducerArguments),\n    #[error(transparent)]\n    NoSuchModule(#[from] NoSuchModule),\n    #[error(transparent)]\n    Other(anyhow::Error),\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum ClientConnectedError {\n    #[error(transparent)]\n    ReducerCall(#[from] ReducerCallError),\n    #[error(\"Failed to insert `st_client` row for module without client_connected reducer: {0}\")]\n    DBError(#[from] Box<DBError>),\n    #[error(\"Connection rejected by `client_connected` reducer: {0}\")]\n    Rejected(Box<str>),\n    #[error(\"Insufficient energy balance to run `client_connected` reducer\")]\n    OutOfEnergy,\n}\n\npub struct RefInstance<'a, I: WasmInstance> {\n    pub common: &'a mut InstanceCommon,\n    pub instance: &'a mut I,\n}\n\nimpl ModuleHost {\n    pub(super) fn new(\n        module: ModuleWithInstance,\n        on_panic: impl Fn() + Send + Sync + 'static,\n        database_identity: Identity,\n    ) -> Self {\n        let info;\n        let inner = match module {\n            ModuleWithInstance::Wasm {\n                module,\n                executor,\n                init_inst,\n            } => {\n                info = module.info();\n                let instance_manager = ModuleInstanceManager::new(module, init_inst, database_identity);\n                Arc::new(ModuleHostInner::Wasm(WasmtimeModuleHost {\n                    executor,\n                    instance_manager,\n                }))\n            }\n            ModuleWithInstance::Js { module, init_inst } => {\n                info = module.info();\n                let instance_manager = ModuleInstanceManager::new(module, init_inst, database_identity);\n                Arc::new(ModuleHostInner::Js(V8ModuleHost { instance_manager }))\n            }\n        };\n        let on_panic = Arc::new(on_panic);\n\n        ModuleHost {\n            info,\n            inner,\n            on_panic,\n            closed: Arc::new(AtomicBool::new(false)),\n        }\n    }\n\n    #[inline]\n    pub fn info(&self) -> &ModuleInfo {\n        &self.info\n    }\n\n    #[inline]\n    pub fn subscriptions(&self) -> &ModuleSubscriptions {\n        &self.info.subscriptions\n    }\n\n    fn is_marked_closed(&self) -> bool {\n        // `self.closed` isn't used for any synchronization, it's just a shared flag,\n        // so `Ordering::Relaxed` is sufficient.\n        self.closed.load(std::sync::atomic::Ordering::Relaxed)\n    }\n\n    fn guard_closed(&self) -> Result<(), NoSuchModule> {\n        if self.is_marked_closed() {\n            Err(NoSuchModule)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Run a function on the JobThread for this module.\n    /// This would deadlock if it is called within another call to `on_module_thread`.\n    /// Since this is async, and `f` is sync, deadlocking shouldn't be a problem.\n    pub async fn on_module_thread<F, R>(&self, label: &str, f: F) -> Result<R, anyhow::Error>\n    where\n        F: FnOnce() -> R + Send + 'static,\n        R: Send + 'static,\n    {\n        self.on_module_thread_async(label, async move || f()).await\n    }\n\n    /// Run an async function on the JobThread for this module.\n    /// Similar to `on_module_thread`, but for async functions.\n    pub async fn on_module_thread_async<F, R>(&self, label: &str, f: F) -> Result<R, anyhow::Error>\n    where\n        F: AsyncFnOnce() -> R + Send + 'static,\n        R: Send + 'static,\n    {\n        self.guard_closed()?;\n\n        let timer_guard = self.start_call_timer(label);\n\n        Ok(match &*self.inner {\n            ModuleHostInner::Wasm(WasmtimeModuleHost { executor, .. }) => {\n                executor\n                    .run_job(async move || {\n                        drop(timer_guard);\n                        f().await\n                    })\n                    .await\n            }\n            ModuleHostInner::Js(V8ModuleHost { instance_manager }) => {\n                instance_manager\n                    .with_instance(async |mut inst| {\n                        let res = inst\n                            .run_on_thread(async move || {\n                                drop(timer_guard);\n                                f().await\n                            })\n                            .await;\n                        (res, inst)\n                    })\n                    .await\n            }\n        })\n    }\n\n    fn start_call_timer(&self, label: &str) -> ScopeGuard<(), impl FnOnce(()) + use<>> {\n        // Record the time until our function starts running.\n        let queue_timer = WORKER_METRICS\n            .reducer_wait_time\n            .with_label_values(&self.info.database_identity, label)\n            .start_timer();\n        let queue_length_gauge = WORKER_METRICS\n            .instance_queue_length\n            .with_label_values(&self.info.database_identity);\n        queue_length_gauge.inc();\n        {\n            let queue_length = queue_length_gauge.get();\n            WORKER_METRICS\n                .instance_queue_length_histogram\n                .with_label_values(&self.info.database_identity)\n                .observe(queue_length as f64);\n        }\n        // Ensure that we always decrement the gauge.\n        scopeguard::guard((), move |_| {\n            // Decrement the queue length gauge when we're done.\n            // This is done in a defer so that it happens even if the reducer call panics.\n            queue_length_gauge.dec();\n            queue_timer.stop_and_record();\n        })\n    }\n\n    /// Run a function for this module which has access to the module instance.\n    async fn with_instance<Guard, A, R>(\n        &self,\n        kind: &str,\n        label: &str,\n        arg: A,\n        timer: impl FnOnce(&str) -> Guard,\n        work_wasm: impl AsyncFnOnce(Guard, &SingleCoreExecutor, Box<ModuleInstance>, A) -> (R, Box<ModuleInstance>),\n        work_js: impl AsyncFnOnce(Guard, &mut JsInstance, A) -> R,\n    ) -> Result<R, NoSuchModule> {\n        self.guard_closed()?;\n        let timer_guard = timer(label);\n\n        // Operations on module instances (e.g. calling reducers) is blocking,\n        // partially because the computation can potentially take a long time\n        // and partially because interacting with the database requires taking\n        // a blocking lock. So, we run `f` on a dedicated thread with `self.executor`.\n        // This will bubble up any panic that may occur.\n\n        // If a function call panics, we **must** ensure to call `self.on_panic`\n        // so that the module is discarded by the host controller.\n        scopeguard::defer_on_unwind!({\n            log::warn!(\"{kind} {label} panicked\");\n            (self.on_panic)();\n        });\n\n        Ok(match &*self.inner {\n            ModuleHostInner::Wasm(WasmtimeModuleHost {\n                executor,\n                instance_manager,\n            }) => {\n                instance_manager\n                    .with_instance(async |inst| work_wasm(timer_guard, executor, inst, arg).await)\n                    .await\n            }\n            ModuleHostInner::Js(V8ModuleHost { instance_manager }) => {\n                instance_manager\n                    .with_instance(async |mut inst| (work_js(timer_guard, &mut inst, arg).await, inst))\n                    .await\n            }\n        })\n    }\n\n    /// Run a function for this module which has access to the module instance.\n    ///\n    /// For WASM, the function is run on the module's JobThread.\n    /// For V8/JS, the function is run in the current task.\n    async fn call<A, R>(\n        &self,\n        label: &str,\n        arg: A,\n        wasm: impl AsyncFnOnce(A, &mut ModuleInstance) -> R + Send + 'static,\n        js: impl AsyncFnOnce(A, &mut JsInstance) -> R,\n    ) -> Result<R, NoSuchModule>\n    where\n        R: Send + 'static,\n        A: Send + 'static,\n    {\n        self.with_instance(\n            \"reducer\",\n            label,\n            arg,\n            |l| self.start_call_timer(l),\n            // Operations on module instances (e.g. calling reducers) is blocking,\n            // partially because the computation can potentially take a long time\n            // and partially because interacting with the database requires taking a blocking lock.\n            // So, we run `work` on a dedicated thread with `self.executor`.\n            // This will bubble up any panic that may occur.\n            async move |timer_guard, executor, mut inst, arg| {\n                executor\n                    .run_job(async move || {\n                        drop(timer_guard);\n                        (wasm(arg, &mut inst).await, inst)\n                    })\n                    .await\n            },\n            async move |timer_guard, inst, arg| {\n                drop(timer_guard);\n                js(arg, inst).await\n            },\n        )\n        .await\n    }\n\n    pub async fn disconnect_client(&self, client_id: ClientActorId) {\n        log::trace!(\"disconnecting client {client_id}\");\n        if let Err(e) = self\n            .call(\n                \"disconnect_client\",\n                client_id,\n                async |client_id, inst| inst.disconnect_client(client_id),\n                async |client_id, inst| inst.disconnect_client(client_id).await,\n            )\n            .await\n        {\n            log::error!(\"Error from client_disconnected transaction: {e}\");\n        }\n    }\n\n    pub fn disconnect_client_inner(\n        client_id: ClientActorId,\n        info: &ModuleInfo,\n        call_reducer: impl FnOnce(Option<MutTxId>, CallReducerParams) -> (ReducerCallResult, bool),\n        trapped_slot: &mut bool,\n    ) -> Result<(), ReducerCallError> {\n        // Call the `client_disconnected` reducer, if it exists.\n        // This is a no-op if the module doesn't define such a reducer.\n        info.subscriptions.remove_subscriber(client_id);\n        Self::call_identity_disconnected_inner(\n            client_id.identity,\n            client_id.connection_id,\n            info,\n            call_reducer,\n            trapped_slot,\n        )\n    }\n\n    /// Invoke the module's `client_connected` reducer, if it has one,\n    /// and insert a new row into `st_client` for `(caller_identity, caller_connection_id)`.\n    ///\n    /// The host inspects `st_client` when restarting in order to run `client_disconnected` reducers\n    /// for clients that were connected at the time when the host went down.\n    /// This ensures that every client connection eventually has `client_disconnected` invoked.\n    ///\n    /// If this method returns `Ok`, then the client connection has been approved,\n    /// and the new row has been inserted into `st_client`.\n    ///\n    /// If this method returns `Err`, then the client connection has either failed or been rejected,\n    /// and `st_client` has not been modified.\n    /// In this case, the caller should terminate the connection.\n    pub async fn call_identity_connected(\n        &self,\n        caller_auth: ConnectionAuthCtx,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ClientConnectedError> {\n        self.call(\n            \"call_identity_connected\",\n            (caller_auth, caller_connection_id),\n            async |(a, b), inst| inst.call_identity_connected(a, b),\n            async |(a, b), inst| inst.call_identity_connected(a, b).await,\n        )\n        .await\n        .map_err(ReducerCallError::from)?\n    }\n\n    /// Invokes the `client_disconnected` reducer, if present,\n    /// then deletes the client’s rows from `st_client` and `st_connection_credentials`.\n    /// If the reducer fails, the rows are still deleted.\n    /// Calling this on an already-disconnected client is a no-op.\n    pub fn call_identity_disconnected_inner(\n        caller_identity: Identity,\n        caller_connection_id: ConnectionId,\n        info: &ModuleInfo,\n        call_reducer: impl FnOnce(Option<MutTxId>, CallReducerParams) -> (ReducerCallResult, bool),\n        trapped_slot: &mut bool,\n    ) -> Result<(), ReducerCallError> {\n        let stdb = info.relational_db();\n\n        let reducer_lookup = info.module_def.lifecycle_reducer(Lifecycle::OnDisconnect);\n        let reducer_name = reducer_lookup\n            .as_ref()\n            .map(|(_, def)| def.name.clone())\n            .unwrap_or_else(|| ReducerName::new(Identifier::new_assume_valid(\"__identity_disconnected__\".into())));\n\n        let is_client_exist = |mut_tx: &MutTxId| mut_tx.st_client_row(caller_identity, caller_connection_id).is_some();\n\n        let workload = || Workload::reducer_no_args(reducer_name.clone(), caller_identity, caller_connection_id);\n\n        // A fallback transaction that deletes the client from `st_client`.\n        let database_identity = stdb.database_identity();\n        let fallback = || {\n            stdb.with_auto_commit(workload(), |mut_tx| {\n                if !is_client_exist(mut_tx) {\n                    // The client is already gone. Nothing to do.\n                    log::debug!(\n                        \"`call_identity_disconnected`: no row in `st_client` for ({caller_identity}, {caller_connection_id}), nothing to do\",\n                    );\n                    return Ok(());\n                }\n\n                mut_tx\n                    .delete_st_client(caller_identity, caller_connection_id, database_identity)\n                    .map_err(DBError::from)\n            })\n            .map_err(|err| {\n                log::error!(\n                    \"`call_identity_disconnected`: fallback transaction to delete from `st_client` failed: {err}\"\n                );\n                InvalidReducerArguments(InvalidFunctionArguments {\n                    err: err.into(),\n                    function_name: reducer_name.clone().into(),\n                })\n                .into()\n            })\n        };\n\n        if let Some((reducer_id, reducer_def)) = reducer_lookup {\n            let mut_tx = stdb.begin_mut_tx(IsolationLevel::Serializable, workload());\n\n            if !is_client_exist(&mut_tx) {\n                // The client is already gone. Nothing to do.\n                log::debug!(\n                    \"`call_identity_disconnected`: no row in `st_client` for ({caller_identity}, {caller_connection_id}), nothing to do\",\n                );\n                return Ok(());\n            }\n\n            // The module defined a lifecycle reducer to handle disconnects. Call it.\n            // If it succeeds, `WasmModuleInstance::call_reducer_with_tx` has already ensured\n            // that `st_client` is updated appropriately.\n            let tx = Some(mut_tx);\n            let result = Self::call_reducer_params(\n                info,\n                caller_identity,\n                Some(caller_connection_id),\n                None,\n                None,\n                None,\n                reducer_id,\n                reducer_def,\n                FunctionArgs::Nullary,\n            )\n            .map(|params| {\n                let (res, trapped) = call_reducer(tx, params);\n                *trapped_slot = trapped;\n                res\n            });\n\n            // If it failed, we still need to update `st_client`: the client's not coming back.\n            // Commit a separate transaction that just updates `st_client`.\n            //\n            // It's OK for this to not be atomic with the previous transaction,\n            // since that transaction didn't commit. If we crash before committing this one,\n            // we'll run the `client_disconnected` reducer again unnecessarily,\n            // but the commitlog won't contain two invocations of it, which is what we care about.\n            match result {\n                Err(e) => {\n                    log::error!(\"call_reducer_inner of client_disconnected failed: {e:#?}\");\n                    fallback()\n                }\n                Ok(ReducerCallResult {\n                    outcome: ReducerOutcome::Failed(_) | ReducerOutcome::BudgetExceeded,\n                    ..\n                }) => fallback(),\n\n                // If it succeeded, as mentioned above, `st_client` is already updated.\n                Ok(ReducerCallResult {\n                    outcome: ReducerOutcome::Committed,\n                    ..\n                }) => Ok(()),\n            }\n        } else {\n            // The module doesn't define a `client_disconnected` reducer.\n            // Commit a transaction to update `st_clients`.\n            fallback()\n        }\n    }\n\n    /// Invoke the module's `client_disconnected` reducer, if it has one,\n    /// and delete the client's row from `st_client`, if any.\n    ///\n    /// The host inspects `st_client` when restarting in order to run `client_disconnected` reducers\n    /// for clients that were connected at the time when the host went down.\n    /// This ensures that every client connection eventually has `client_disconnected` invoked.\n    ///\n    /// Unlike [`Self::call_identity_connected`],\n    /// this method swallows errors returned by the `client_disconnected` reducer.\n    /// The database can't reject a disconnection - the client's gone, whether the database likes it or not.\n    ///\n    /// If this method returns an error, the database is likely to wind up in a bad state,\n    /// as that means we've somehow failed to delete from `st_client`.\n    /// We cannot meaningfully handle this.\n    /// Sometimes it just means that the database no longer exists, though, which is fine.\n    pub async fn call_identity_disconnected(\n        &self,\n        caller_identity: Identity,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ReducerCallError> {\n        self.call(\n            \"call_identity_disconnected\",\n            (caller_identity, caller_connection_id),\n            async |(a, b), inst| inst.call_identity_disconnected(a, b),\n            async |(a, b), inst| inst.call_identity_disconnected(a, b).await,\n        )\n        .await?\n    }\n\n    /// Empty the system tables tracking clients without running any lifecycle reducers.\n    pub async fn clear_all_clients(&self) -> anyhow::Result<()> {\n        self.call(\n            \"clear_all_clients\",\n            (),\n            async |_, inst| inst.clear_all_clients(),\n            async |_, inst| inst.clear_all_clients().await,\n        )\n        .await?\n    }\n\n    fn call_reducer_params(\n        module: &ModuleInfo,\n        caller_identity: Identity,\n        caller_connection_id: Option<ConnectionId>,\n        client: Option<Arc<ClientConnectionSender>>,\n        request_id: Option<RequestId>,\n        timer: Option<Instant>,\n        reducer_id: ReducerId,\n        reducer_def: &ReducerDef,\n        args: FunctionArgs,\n    ) -> Result<CallReducerParams, InvalidReducerArguments> {\n        let args = args\n            .into_tuple_for_def(&module.module_def, reducer_def)\n            .map_err(InvalidReducerArguments)?;\n        let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO);\n        Ok(CallReducerParams {\n            timestamp: Timestamp::now(),\n            caller_identity,\n            caller_connection_id,\n            client,\n            request_id,\n            timer,\n            reducer_id,\n            args,\n        })\n    }\n\n    async fn call_reducer_inner(\n        &self,\n        caller_identity: Identity,\n        caller_connection_id: Option<ConnectionId>,\n        client: Option<Arc<ClientConnectionSender>>,\n        request_id: Option<RequestId>,\n        timer: Option<Instant>,\n        reducer_id: ReducerId,\n        reducer_def: &ReducerDef,\n        args: FunctionArgs,\n    ) -> Result<ReducerCallResult, ReducerCallError> {\n        let args = args\n            .into_tuple_for_def(&self.info.module_def, reducer_def)\n            .map_err(InvalidReducerArguments)?;\n        let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO);\n        let call_reducer_params = CallReducerParams {\n            timestamp: Timestamp::now(),\n            caller_identity,\n            caller_connection_id,\n            client,\n            request_id,\n            timer,\n            reducer_id,\n            args,\n        };\n\n        Ok(self\n            .call(\n                &reducer_def.name,\n                call_reducer_params,\n                async |p, inst| inst.call_reducer(p),\n                async |p, inst| inst.call_reducer(p).await,\n            )\n            .await?)\n    }\n\n    pub async fn call_reducer(\n        &self,\n        caller_identity: Identity,\n        caller_connection_id: Option<ConnectionId>,\n        client: Option<Arc<ClientConnectionSender>>,\n        request_id: Option<RequestId>,\n        timer: Option<Instant>,\n        reducer_name: &str,\n        args: FunctionArgs,\n    ) -> Result<ReducerCallResult, ReducerCallError> {\n        let res = async {\n            let (reducer_id, reducer_def) = self\n                .info\n                .module_def\n                .reducer_full(reducer_name)\n                .ok_or(ReducerCallError::NoSuchReducer)?;\n            if let Some(lifecycle) = reducer_def.lifecycle {\n                return Err(ReducerCallError::LifecycleReducer(lifecycle));\n            }\n\n            if reducer_def.visibility.is_private() && !self.is_database_owner(caller_identity) {\n                return Err(ReducerCallError::NoSuchReducer);\n            }\n\n            self.call_reducer_inner(\n                caller_identity,\n                caller_connection_id,\n                client,\n                request_id,\n                timer,\n                reducer_id,\n                reducer_def,\n                args,\n            )\n            .await\n        }\n        .await;\n\n        let log_message = match &res {\n            Err(ReducerCallError::NoSuchReducer) => Some(no_such_function_log_message(\"reducer\", reducer_name)),\n            Err(ReducerCallError::Args(_)) => Some(args_error_log_message(\"reducer\", reducer_name)),\n            _ => None,\n        };\n        if let Some(log_message) = log_message {\n            self.inject_logs(LogLevel::Error, reducer_name, &log_message)\n        }\n\n        res\n    }\n\n    pub async fn call_view_add_single_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeSingle,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let cmd = ViewCommand::AddSingleSubscription {\n            sender,\n            auth,\n            request,\n            _timer: timer,\n        };\n\n        let res = self\n            .call(\n                \"call_view_add_single_subscription\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            //TODO: handle error better\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Subscription { result } => result,\n            ViewCommandResult::Sql { .. } => {\n                unreachable!(\"unexpected SQL result in call_view_add_single_subscription\")\n            }\n        }\n    }\n\n    pub async fn call_view_add_v2_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Subscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let cmd = ViewCommand::AddSubscriptionV2 {\n            sender,\n            auth,\n            request,\n            _timer: timer,\n        };\n\n        let res = self\n            .call(\n                \"call_view_add_multi_subscription\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            //TODO: handle error better\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Subscription { result } => result,\n            ViewCommandResult::Sql { .. } => {\n                unreachable!(\"unexpected SQL result in call_view_add_single_subscription\")\n            }\n        }\n    }\n\n    pub async fn call_view_remove_v2_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Unsubscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let cmd = ViewCommand::RemoveSubscriptionV2 {\n            sender,\n            auth,\n            request,\n            timer,\n        };\n\n        let res = self\n            .call(\n                \"call_view_remove_v2_subscription\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Subscription { result } => result,\n            ViewCommandResult::Sql { .. } => {\n                unreachable!(\"unexpected SQL result in call_view_remove_v2_subscription\")\n            }\n        }\n    }\n\n    pub async fn call_view_add_multi_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeMulti,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let cmd = ViewCommand::AddMultiSubscription {\n            sender,\n            auth,\n            request,\n            _timer: timer,\n        };\n\n        let res = self\n            .call(\n                \"call_view_add_multi_subscription\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            //TODO: handle error better\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Subscription { result } => result,\n            ViewCommandResult::Sql { .. } => {\n                unreachable!(\"unexpected SQL result in call_view_add_single_subscription\")\n            }\n        }\n    }\n\n    pub async fn call_view_add_legacy_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        subscribe: ws_v1::Subscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        let cmd = ViewCommand::AddLegacySubscription {\n            sender,\n            auth,\n            subscribe,\n            _timer: timer,\n        };\n\n        let res = self\n            .call(\n                \"call_view_add_legacy_subscription\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            //TODO: handle error better\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Subscription { result } => result,\n            ViewCommandResult::Sql { .. } => {\n                unreachable!(\"unexpected SQL result in call_view_add_single_subscription\")\n            }\n        }\n    }\n\n    pub async fn call_view_sql(\n        &self,\n        db: Arc<RelationalDB>,\n        sql_text: String,\n        auth: AuthCtx,\n        subs: Option<ModuleSubscriptions>,\n        head: &mut Vec<(RawIdentifier, AlgebraicType)>,\n    ) -> Result<SqlResult, DBError> {\n        let cmd = ViewCommand::Sql {\n            db,\n            sql_text,\n            auth,\n            subs,\n        };\n\n        let res = self\n            .call(\n                \"call_view_sql\",\n                cmd,\n                async |cmd, inst| inst.call_view(cmd),\n                async |cmd, inst| inst.call_view(cmd).await,\n            )\n            .await\n            //TODO: handle error better\n            .map_err(|e| DBError::Other(anyhow::anyhow!(e)))?;\n\n        match res {\n            ViewCommandResult::Sql { result, head: new_head } => {\n                *head = new_head;\n                result\n            }\n            ViewCommandResult::Subscription { .. } => {\n                unreachable!(\"unexpected subscription result in call_view_sql\")\n            }\n        }\n    }\n\n    pub async fn call_procedure(\n        &self,\n        caller_identity: Identity,\n        caller_connection_id: Option<ConnectionId>,\n        timer: Option<Instant>,\n        procedure_name: &str,\n        args: FunctionArgs,\n    ) -> CallProcedureReturn {\n        let res = async {\n            let (procedure_id, procedure_def) = self\n                .info\n                .module_def\n                .procedure_full(procedure_name)\n                .ok_or(ProcedureCallError::NoSuchProcedure)?;\n\n            if procedure_def.visibility.is_private() && !self.is_database_owner(caller_identity) {\n                return Err(ProcedureCallError::NoSuchProcedure);\n            }\n\n            self.call_procedure_inner(\n                caller_identity,\n                caller_connection_id,\n                timer,\n                procedure_id,\n                procedure_def,\n                args,\n            )\n            .await\n        }\n        .await;\n\n        let ret = match res {\n            Ok(ret) => ret,\n            Err(err) => CallProcedureReturn {\n                result: Err(err),\n                tx_offset: None,\n            },\n        };\n\n        let log_message = match &ret.result {\n            Err(ProcedureCallError::NoSuchProcedure) => Some(no_such_function_log_message(\"procedure\", procedure_name)),\n            Err(ProcedureCallError::Args(_)) => Some(args_error_log_message(\"procedure\", procedure_name)),\n            _ => None,\n        };\n\n        if let Some(log_message) = log_message {\n            self.inject_logs(LogLevel::Error, procedure_name, &log_message)\n        }\n\n        ret\n    }\n\n    async fn call_procedure_inner(\n        &self,\n        caller_identity: Identity,\n        caller_connection_id: Option<ConnectionId>,\n        timer: Option<Instant>,\n        procedure_id: ProcedureId,\n        procedure_def: &ProcedureDef,\n        args: FunctionArgs,\n    ) -> Result<CallProcedureReturn, ProcedureCallError> {\n        let args = args\n            .into_tuple_for_def(&self.info.module_def, procedure_def)\n            .map_err(InvalidProcedureArguments)?;\n        let caller_connection_id = caller_connection_id.unwrap_or(ConnectionId::ZERO);\n\n        let params = CallProcedureParams {\n            timestamp: Timestamp::now(),\n            caller_identity,\n            caller_connection_id,\n            timer,\n            procedure_id,\n            args,\n        };\n\n        Ok(self.call_procedure_with_params(&procedure_def.name, params).await?)\n    }\n\n    //TODO(shub) #4195: Also allow for collaborators along with owner\n    fn is_database_owner(&self, caller_identity: Identity) -> bool {\n        self.info.owner_identity == caller_identity\n    }\n\n    pub async fn call_procedure_with_params(\n        &self,\n        name: &str,\n        params: CallProcedureParams,\n    ) -> Result<CallProcedureReturn, NoSuchModule> {\n        self.call(\n            name,\n            params,\n            async move |params, inst| inst.call_procedure(params).await,\n            async move |params, inst| inst.call_procedure(params).await,\n        )\n        .await\n    }\n\n    pub(super) async fn call_scheduled_function(\n        &self,\n        params: ScheduledFunctionParams,\n    ) -> Result<CallScheduledFunctionResult, NoSuchModule> {\n        self.call(\n            \"unknown scheduled function\",\n            params,\n            async move |params, inst| inst.call_scheduled_function(params).await,\n            async move |params, inst| inst.call_scheduled_function(params).await,\n        )\n        .await\n    }\n\n    /// Materializes the views return by the `view_collector`, if not already materialized,\n    /// and updates `st_view_sub` accordingly.\n    ///\n    /// Passing [`Workload::Sql`] will update `st_view_sub.last_called`.\n    /// Passing [`Workload::Subscribe`] will also increment `st_view_sub.num_subscribers`,\n    /// in addition to updating `st_view_sub.last_called`.\n    pub fn materialize_views<I: WasmInstance>(\n        mut tx: MutTxId,\n        instance: &mut RefInstance<'_, I>,\n        view_collector: &impl CollectViews,\n        caller: Identity,\n        workload: Workload,\n    ) -> Result<(MutTxId, bool), ViewCallError> {\n        use FunctionArgs::*;\n        let mut view_ids = HashSet::new();\n        view_collector.collect_views(&mut view_ids);\n        for view_id in view_ids {\n            let st_view_row = tx.lookup_st_view(view_id)?;\n            let view_name = st_view_row.view_name.into();\n            let view_id = st_view_row.view_id;\n            let table_id = st_view_row.table_id.ok_or(ViewCallError::TableDoesNotExist(view_id))?;\n            let is_anonymous = st_view_row.is_anonymous;\n            let sender = if is_anonymous { None } else { Some(caller) };\n            let is_materialized = if is_anonymous {\n                tx.is_anonymous_view_materialized(view_id)?\n            } else {\n                tx.is_view_materialized(view_id, ArgId::SENTINEL, caller)?\n            };\n            if !is_materialized {\n                let (res, trapped) =\n                    Self::call_view(instance, tx, &view_name, view_id, table_id, Nullary, caller, sender)?;\n                tx = res.tx;\n                if trapped {\n                    return Ok((tx, true));\n                }\n            }\n            // If this is a sql call, we only update this view's \"last called\" timestamp\n            if let Workload::Sql = workload {\n                tx.update_view_timestamp(view_id, ArgId::SENTINEL, caller)?;\n            }\n            // If this is a subscribe call, we also increment this view's subscriber count\n            if let Workload::Subscribe = workload {\n                tx.subscribe_view(view_id, ArgId::SENTINEL, caller)?;\n            }\n        }\n        Ok((tx, false))\n    }\n\n    /// Refreshes every view made stale by `tx`.\n    ///\n    /// The returned transaction contains both the original writes and any backing-table\n    /// updates produced by re-materializing the affected views.\n    pub fn call_views_with_tx<I: WasmInstance>(\n        tx: MutTxId,\n        instance: &mut RefInstance<'_, I>,\n        caller: Identity,\n    ) -> (ViewCallResult, bool) {\n        Self::call_views_with_tx_at(tx, instance, caller, Timestamp::now())\n    }\n\n    /// Refreshes every view made stale by `tx`.\n    ///\n    /// This is the shared host-side path for finishing a transaction after writes have\n    /// invalidated one or more materialized views.\n    pub fn call_views_with_tx_at<I: WasmInstance>(\n        tx: MutTxId,\n        instance: &mut RefInstance<'_, I>,\n        caller: Identity,\n        timestamp: Timestamp,\n    ) -> (ViewCallResult, bool) {\n        let mut tx = tx;\n        let module_def = &instance.common.info().module_def;\n        let mut outcome = ViewOutcome::Success;\n        let mut energy_used = FunctionBudget::ZERO;\n        let mut total_duration = Duration::ZERO;\n        let mut abi_duration = Duration::ZERO;\n        let mut trapped = false;\n        for ViewCallInfo {\n            view_id,\n            table_id,\n            fn_ptr,\n            sender,\n        } in tx.views_for_refresh().cloned().collect::<Vec<_>>()\n        {\n            let Some(view_def) = module_def.get_view_by_id(fn_ptr, sender.is_none()) else {\n                outcome = ViewOutcome::Failed(format!(\"view with fn_ptr `{fn_ptr}` not found\"));\n                break;\n            };\n            let args = match FunctionArgs::Nullary.into_tuple_for_def(module_def, view_def) {\n                Ok(args) => args,\n                Err(err) => {\n                    outcome = ViewOutcome::Failed(format!(\"failed to build view args: {err}\"));\n                    break;\n                }\n            };\n\n            let (result, trap) = Self::call_view_inner(\n                instance,\n                tx,\n                &view_def.name,\n                view_id,\n                table_id,\n                view_def.fn_ptr,\n                caller,\n                sender,\n                args,\n                view_def.product_type_ref,\n                timestamp,\n            );\n\n            // Increment execution stats\n            tx = result.tx;\n            outcome = result.outcome;\n            energy_used += result.energy_used;\n            total_duration += result.total_duration;\n            abi_duration += result.abi_duration;\n            trapped |= trap;\n\n            // Terminate early if execution failed\n            if !matches!(outcome, ViewOutcome::Success) || trapped {\n                break;\n            }\n        }\n        (\n            ViewCallResult {\n                outcome,\n                tx,\n                energy_used,\n                total_duration,\n                abi_duration,\n            },\n            trapped,\n        )\n    }\n\n    fn call_view<I: WasmInstance>(\n        instance: &mut RefInstance<'_, I>,\n        tx: MutTxId,\n        view_name: &Identifier,\n        view_id: ViewId,\n        table_id: TableId,\n        args: FunctionArgs,\n        caller: Identity,\n        sender: Option<Identity>,\n    ) -> Result<(ViewCallResult, bool), ViewCallError> {\n        Self::call_view_at(\n            instance,\n            tx,\n            view_name,\n            view_id,\n            table_id,\n            args,\n            caller,\n            sender,\n            Timestamp::now(),\n        )\n    }\n\n    fn call_view_at<I: WasmInstance>(\n        instance: &mut RefInstance<'_, I>,\n        tx: MutTxId,\n        view_name: &Identifier,\n        view_id: ViewId,\n        table_id: TableId,\n        args: FunctionArgs,\n        caller: Identity,\n        sender: Option<Identity>,\n        timestamp: Timestamp,\n    ) -> Result<(ViewCallResult, bool), ViewCallError> {\n        let module_def = &instance.common.info().module_def;\n        let view_def = module_def.view(view_name).ok_or(ViewCallError::NoSuchView)?;\n        let fn_ptr = view_def.fn_ptr;\n        let row_type = view_def.product_type_ref;\n        let args = args\n            .into_tuple_for_def(module_def, view_def)\n            .map_err(InvalidViewArguments)?;\n\n        Ok(Self::call_view_inner(\n            instance, tx, view_name, view_id, table_id, fn_ptr, caller, sender, args, row_type, timestamp,\n        ))\n    }\n\n    fn call_view_inner<I: WasmInstance>(\n        instance: &mut RefInstance<'_, I>,\n        tx: MutTxId,\n        name: &Identifier,\n        view_id: ViewId,\n        table_id: TableId,\n        fn_ptr: ViewFnPtr,\n        caller: Identity,\n        sender: Option<Identity>,\n        args: ArgsTuple,\n        row_type: AlgebraicTypeRef,\n        timestamp: Timestamp,\n    ) -> (ViewCallResult, bool) {\n        let view_name = name.clone();\n        let params = CallViewParams {\n            timestamp,\n            view_name,\n            view_id,\n            table_id,\n            fn_ptr,\n            caller,\n            sender,\n            args,\n            row_type,\n        };\n\n        instance.common.call_view_with_tx(tx, params, instance.instance)\n    }\n\n    pub async fn init_database(&self, program: Program) -> Result<Option<ReducerCallResult>, InitDatabaseError> {\n        self.call(\n            \"<init_database>\",\n            program,\n            async |p, inst| inst.init_database(p),\n            async |p, inst| inst.init_database(p).await,\n        )\n        .await?\n        .map_err(InitDatabaseError::Other)\n    }\n\n    pub async fn update_database(\n        &self,\n        program: Program,\n        old_module_info: Arc<ModuleInfo>,\n        policy: MigrationPolicy,\n    ) -> Result<UpdateDatabaseResult, anyhow::Error> {\n        self.call(\n            \"<update_database>\",\n            (program, old_module_info, policy),\n            async |(a, b, c), inst| inst.update_database(a, b, c),\n            async |(a, b, c), inst| inst.update_database(a, b, c).await,\n        )\n        .await?\n    }\n\n    pub async fn exit(&self) {\n        // As in `Self::marked_closed`, `Relaxed` is sufficient because we're not synchronizing any external state.\n        self.closed.store(true, std::sync::atomic::Ordering::Relaxed);\n        self.scheduler().close();\n        self.exited().await;\n    }\n\n    pub async fn exited(&self) {\n        self.scheduler().closed().await;\n    }\n\n    pub fn inject_logs(&self, log_level: LogLevel, function_name: &str, message: &str) {\n        self.replica_ctx().logger.write(\n            log_level,\n            &Record {\n                function: Some(function_name),\n                ..Record::injected(message)\n            },\n            &(),\n        )\n    }\n\n    /// Execute a one-off query and send the results to the given client.\n    /// This only returns an error if there is a db-level problem.\n    /// An error with the query itself will be sent to the client.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn one_off_query<F: BuildableWebsocketFormat>(\n        &self,\n        auth: AuthCtx,\n        query: String,\n        client: Arc<ClientConnectionSender>,\n        message_id: Vec<u8>,\n        timer: Instant,\n        rlb_pool: impl 'static + Send + RowListBuilderSource<F>,\n        // We take this because we only have a way to convert with the concrete types (Bsatn and Json)\n        into_message: impl FnOnce(OneOffQueryResponseMessage<F>) -> SerializableMessage + Send + 'static,\n    ) -> Result<(), anyhow::Error> {\n        let replica_ctx = self.replica_ctx();\n        let db = replica_ctx.relational_db.clone();\n        let subscriptions = replica_ctx.subscriptions.clone();\n        log::debug!(\"One-off query: {query}\");\n        let metrics = self\n            .on_module_thread(\"one_off_query\", move || {\n                let (tx_offset_sender, tx_offset_receiver) = oneshot::channel();\n                let tx = scopeguard::guard(db.begin_tx(Workload::Sql), |tx| {\n                    let (tx_offset, tx_metrics, reducer) = db.release_tx(tx);\n                    let _ = tx_offset_sender.send(tx_offset);\n                    db.report_read_tx_metrics(reducer, tx_metrics);\n                });\n\n                // We wrap the actual query in a closure so we can use ? to handle errors without making\n                // the entire transaction abort with an error.\n                let result: Result<(ws_v1::OneOffTable<F>, ExecutionMetrics), anyhow::Error> = (|| {\n                    let tx = SchemaViewer::new(&*tx, &auth);\n\n                    let (\n                        // A query may compile down to several plans.\n                        // This happens when there are multiple RLS rules per table.\n                        // The original query is the union of these plans.\n                        plans,\n                        _,\n                        table_name,\n                        _,\n                    ) = compile_subscription(&query, &tx, &auth)?;\n\n                    // Optimize each fragment\n                    let optimized = plans\n                        .into_iter()\n                        .map(|plan| plan.optimize(&auth))\n                        .collect::<Result<Vec<_>, _>>()?;\n\n                    check_row_limit(\n                        &optimized,\n                        &db,\n                        &tx,\n                        // Estimate the number of rows this query will scan\n                        |plan, tx| estimate_rows_scanned(tx, plan),\n                        &auth,\n                    )?;\n\n                    let return_table = || optimized.first().and_then(|plan| plan.return_table());\n\n                    let returns_view_table = optimized.first().is_some_and(|plan| plan.returns_view_table());\n                    let num_cols = return_table().map(|schema| schema.num_cols()).unwrap_or_default();\n                    let num_private_cols = return_table()\n                        .map(|schema| schema.num_private_cols())\n                        .unwrap_or_default();\n\n                    let optimized = optimized\n                        .into_iter()\n                        // Convert into something we can execute\n                        .map(PipelinedProject::from)\n                        .collect::<Vec<_>>();\n\n                    let table_name = table_name.into();\n\n                    if returns_view_table && num_private_cols > 0 {\n                        let optimized = optimized\n                            .into_iter()\n                            .map(|plan| ViewProject::new(plan, num_cols, num_private_cols))\n                            .collect::<Vec<_>>();\n                        // Execute the union and return the results\n                        return execute_plan_for_view::<F>(&optimized, &DeltaTx::from(&*tx), &rlb_pool)\n                            .map(|(rows, _, metrics)| (ws_v1::OneOffTable { table_name, rows }, metrics))\n                            .context(\"One-off queries are not allowed to modify the database\");\n                    }\n\n                    // Execute the union and return the results\n                    execute_plan::<F>(&optimized, &DeltaTx::from(&*tx), &rlb_pool)\n                        .map(|(rows, _, metrics)| (ws_v1::OneOffTable { table_name, rows }, metrics))\n                        .context(\"One-off queries are not allowed to modify the database\")\n                })();\n\n                let total_host_execution_duration = timer.elapsed().into();\n                let (message, metrics): (SerializableMessage, Option<ExecutionMetrics>) = match result {\n                    Ok((rows, metrics)) => (\n                        into_message(OneOffQueryResponseMessage {\n                            message_id,\n                            error: None,\n                            results: vec![rows],\n                            total_host_execution_duration,\n                        }),\n                        Some(metrics),\n                    ),\n                    Err(err) => (\n                        into_message(OneOffQueryResponseMessage {\n                            message_id,\n                            error: Some(format!(\"{err}\")),\n                            results: vec![],\n                            total_host_execution_duration,\n                        }),\n                        None,\n                    ),\n                };\n\n                subscriptions.send_client_message(client, message, (&*tx, tx_offset_receiver))?;\n                Ok::<Option<ExecutionMetrics>, anyhow::Error>(metrics)\n            })\n            .await??;\n\n        if let Some(metrics) = metrics {\n            // Record the metrics for the one-off query\n            replica_ctx\n                .relational_db\n                .exec_counters_for(WorkloadType::Sql)\n                .record(&metrics);\n        }\n\n        Ok(())\n    }\n\n    /// Execute a one-off query for v2 clients and send the results to the given client.\n    ///\n    /// This only returns an error if there is a db-level problem.\n    /// An error with the query itself will be sent to the client.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn one_off_query_v2(\n        &self,\n        auth: AuthCtx,\n        query: String,\n        client: Arc<ClientConnectionSender>,\n        request_id: u32,\n        _timer: Instant,\n        rlb_pool: impl 'static + Send + RowListBuilderSource<ws_v1::BsatnFormat>,\n    ) -> Result<(), anyhow::Error> {\n        let replica_ctx = self.replica_ctx();\n        let db = replica_ctx.relational_db.clone();\n        let subscriptions = replica_ctx.subscriptions.clone();\n        log::debug!(\"One-off query: {query}\");\n        let metrics = self\n            .on_module_thread(\"one_off_query_v2\", move || {\n                Self::one_off_query_v2_inner(db, subscriptions, auth, query, client, request_id, rlb_pool)\n            })\n            .await??;\n\n        if let Some(metrics) = metrics {\n            replica_ctx\n                .relational_db\n                .exec_counters_for(WorkloadType::Sql)\n                .record(&metrics);\n        }\n\n        Ok(())\n    }\n\n    fn one_off_query_v2_inner(\n        db: Arc<RelationalDB>,\n        subscriptions: ModuleSubscriptions,\n        auth: AuthCtx,\n        query: String,\n        client: Arc<ClientConnectionSender>,\n        request_id: u32,\n        rlb_pool: impl 'static + Send + RowListBuilderSource<ws_v1::BsatnFormat>,\n    ) -> Result<Option<ExecutionMetrics>, anyhow::Error> {\n        let (tx_offset_sender, tx_offset_receiver) = oneshot::channel();\n        let tx = scopeguard::guard(db.begin_tx(Workload::Sql), |tx| {\n            let (tx_offset, tx_metrics, reducer) = db.release_tx(tx);\n            let _ = tx_offset_sender.send(tx_offset);\n            db.report_read_tx_metrics(reducer, tx_metrics);\n        });\n\n        let result: Result<(ws_v2::SingleTableRows, ExecutionMetrics), anyhow::Error> = (|| {\n            let tx = SchemaViewer::new(&*tx, &auth);\n\n            let (plans, _, table_name, _) = compile_subscription(&query, &tx, &auth)?;\n\n            let optimized = plans\n                .into_iter()\n                .map(|plan| plan.optimize(&auth))\n                .collect::<Result<Vec<_>, _>>()?;\n\n            check_row_limit(&optimized, &db, &tx, |plan, tx| estimate_rows_scanned(tx, plan), &auth)?;\n\n            let return_table = || optimized.first().and_then(|plan| plan.return_table());\n\n            let returns_view_table = optimized.first().is_some_and(|plan| plan.returns_view_table());\n            let num_cols = return_table().map(|schema| schema.num_cols()).unwrap_or_default();\n            let num_private_cols = return_table()\n                .map(|schema| schema.num_private_cols())\n                .unwrap_or_default();\n\n            let optimized = optimized.into_iter().map(PipelinedProject::from).collect::<Vec<_>>();\n\n            let table_name = table_name.into();\n\n            if returns_view_table && num_private_cols > 0 {\n                let optimized = optimized\n                    .into_iter()\n                    .map(|plan| ViewProject::new(plan, num_cols, num_private_cols))\n                    .collect::<Vec<_>>();\n                return execute_plan_for_view::<ws_v1::BsatnFormat>(&optimized, &DeltaTx::from(&*tx), &rlb_pool)\n                    .map(|(rows, _, metrics)| {\n                        (\n                            ws_v2::SingleTableRows {\n                                table: table_name,\n                                rows,\n                            },\n                            metrics,\n                        )\n                    })\n                    .context(\"One-off queries are not allowed to modify the database\");\n            }\n\n            execute_plan::<ws_v1::BsatnFormat>(&optimized, &DeltaTx::from(&*tx), &rlb_pool)\n                .map(|(rows, _, metrics)| {\n                    (\n                        ws_v2::SingleTableRows {\n                            table: table_name,\n                            rows,\n                        },\n                        metrics,\n                    )\n                })\n                .context(\"One-off queries are not allowed to modify the database\")\n        })();\n\n        let (message, metrics) = match result {\n            Ok((rows, metrics)) => (\n                ws_v2::OneOffQueryResult {\n                    request_id,\n                    result: Ok(ws_v2::QueryRows {\n                        tables: vec![rows].into_boxed_slice(),\n                    }),\n                },\n                Some(metrics),\n            ),\n            Err(err) => (\n                ws_v2::OneOffQueryResult {\n                    request_id,\n                    result: Err(err.to_string().into()),\n                },\n                None,\n            ),\n        };\n\n        subscriptions.send_one_off_query_message_v2(client, message, tx_offset_receiver)?;\n        Ok(metrics)\n    }\n\n    /// FIXME(jgilles): this is a temporary workaround for deleting not currently being supported\n    /// for tables without primary keys. It is only used in the benchmarks.\n    /// Note: this doesn't drop the table, it just clears it!\n    pub fn clear_table(&self, table_name: &str) -> Result<(), anyhow::Error> {\n        let db = &*self.replica_ctx().relational_db;\n\n        db.with_auto_commit(Workload::Internal, |tx| {\n            let tables = db.get_all_tables_mut(tx)?;\n            // We currently have unique table names,\n            // so we can assume there's only one table to clear.\n            if let Some(table_id) = tables\n                .iter()\n                .find_map(|t| (&*t.table_name == table_name).then_some(t.table_id))\n            {\n                db.clear_table(tx, table_id)?;\n            }\n            Ok(())\n        })\n    }\n\n    pub fn downgrade(&self) -> WeakModuleHost {\n        WeakModuleHost {\n            info: self.info.clone(),\n            inner: Arc::downgrade(&self.inner),\n            on_panic: Arc::downgrade(&self.on_panic),\n            closed: Arc::downgrade(&self.closed),\n        }\n    }\n\n    pub fn database_info(&self) -> &Database {\n        &self.replica_ctx().database\n    }\n\n    pub fn durable_tx_offset(&self) -> Option<DurableOffset> {\n        self.replica_ctx().relational_db.durable_tx_offset()\n    }\n\n    pub fn database_logger(&self) -> &Arc<DatabaseLogger> {\n        &self.replica_ctx().logger\n    }\n\n    pub(crate) fn replica_ctx(&self) -> &ReplicaContext {\n        match &*self.inner {\n            ModuleHostInner::Wasm(wasm) => wasm.instance_manager.module.replica_ctx(),\n            ModuleHostInner::Js(js) => js.instance_manager.module.replica_ctx(),\n        }\n    }\n\n    fn scheduler(&self) -> &Scheduler {\n        match &*self.inner {\n            ModuleHostInner::Wasm(wasm) => wasm.instance_manager.module.scheduler(),\n            ModuleHostInner::Js(js) => js.instance_manager.module.scheduler(),\n        }\n    }\n}\n\nimpl WeakModuleHost {\n    pub fn upgrade(&self) -> Option<ModuleHost> {\n        let inner = self.inner.upgrade()?;\n        let on_panic = self.on_panic.upgrade()?;\n        let closed = self.closed.upgrade()?;\n        Some(ModuleHost {\n            info: self.info.clone(),\n            inner,\n            on_panic,\n            closed,\n        })\n    }\n}\n\nfn no_such_function_log_message(function_kind: &str, function_name: &str) -> String {\n    format!(\"External attempt to call nonexistent {function_kind} \\\"{function_name}\\\" failed. Have you run `spacetime generate` recently?\")\n}\n\nfn args_error_log_message(function_kind: &str, function_name: &str) -> String {\n    format!(\n        \"External attempt to call {function_kind} \\\"{function_name}\\\" failed, invalid arguments.\\n\\\n         This is likely due to a mismatched client schema, have you run `spacetime generate` recently?\"\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use super::ModuleHost;\n    use crate::client::{\n        ClientActorId, ClientConfig, ClientConnectionReceiver, ClientConnectionSender, OutboundMessage, Protocol,\n        WsVersion,\n    };\n    use crate::db::relational_db::tests_utils::{insert, with_auto_commit, TestDB};\n    use crate::subscription::module_subscription_actor::ModuleSubscriptions;\n    use spacetimedb_client_api_messages::websocket::{common::RowListLen as _, v1 as ws_v1, v2 as ws_v2};\n    use spacetimedb_lib::identity::AuthCtx;\n    use spacetimedb_lib::{AlgebraicType, Identity};\n    use spacetimedb_sats::product;\n    use std::sync::Arc;\n\n    fn v2_client_config() -> ClientConfig {\n        ClientConfig {\n            protocol: Protocol::Binary,\n            version: WsVersion::V2,\n            compression: ws_v1::Compression::None,\n            tx_update_full: true,\n            confirmed_reads: false,\n        }\n    }\n\n    fn setup_client(\n        db: &Arc<crate::db::relational_db::RelationalDB>,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        let client_id = ClientActorId::for_test(Identity::ZERO);\n        let (sender, receiver) = ClientConnectionSender::dummy_with_channel(client_id, v2_client_config(), db.clone());\n        (Arc::new(sender), receiver)\n    }\n\n    #[test]\n    fn one_off_query_v2_returns_rows() -> anyhow::Result<()> {\n        let runtime = tokio::runtime::Runtime::new()?;\n        let _rt = runtime.enter();\n\n        let TestDB { db, .. } = TestDB::in_memory()?;\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let schema = [(\"id\", AlgebraicType::U8)];\n        let table_id = db.create_table_for_test(\"t\", &schema, &[])?;\n        with_auto_commit(&db, |tx| {\n            insert(&db, tx, table_id, &product![1_u8])?;\n            Ok::<(), crate::error::DBError>(())\n        })?;\n\n        let (sender, mut receiver) = setup_client(&db);\n        let auth = AuthCtx::new(db.owner_identity(), sender.id.identity);\n        ModuleHost::one_off_query_v2_inner(\n            db.clone(),\n            subs.clone(),\n            auth,\n            \"select * from t\".to_string(),\n            sender,\n            42,\n            subs.bsatn_rlb_pool.clone(),\n        )?;\n\n        runtime.block_on(async {\n            match receiver.recv().await {\n                Some(OutboundMessage::V2(ws_v2::ServerMessage::OneOffQueryResult(result))) => {\n                    assert_eq!(result.request_id, 42);\n                    let rows = result.result.expect(\"expected rows\");\n                    assert_eq!(rows.tables.len(), 1);\n                    assert!(rows.tables[0].rows.len() > 0);\n                }\n                other => panic!(\"Expected v2 OneOffQueryResult, got: {other:?}\"),\n            }\n        });\n\n        Ok(())\n    }\n\n    #[test]\n    fn one_off_query_v2_returns_error() -> anyhow::Result<()> {\n        let runtime = tokio::runtime::Runtime::new()?;\n        let _rt = runtime.enter();\n\n        let TestDB { db, .. } = TestDB::in_memory()?;\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let (sender, mut receiver) = setup_client(&db);\n        let auth = AuthCtx::new(db.owner_identity(), sender.id.identity);\n        ModuleHost::one_off_query_v2_inner(\n            db.clone(),\n            subs.clone(),\n            auth,\n            \"select * from missing_table\".to_string(),\n            sender,\n            7,\n            subs.bsatn_rlb_pool.clone(),\n        )?;\n\n        runtime.block_on(async {\n            match receiver.recv().await {\n                Some(OutboundMessage::V2(ws_v2::ServerMessage::OneOffQueryResult(result))) => {\n                    assert_eq!(result.request_id, 7);\n                    let err = result.result.expect_err(\"expected error\");\n                    assert!(!err.is_empty());\n                }\n                other => panic!(\"Expected v2 OneOffQueryResult, got: {other:?}\"),\n            }\n        });\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/scheduler.rs",
    "content": "use super::module_host::{\n    CallReducerParams, DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall, WeakModuleHost,\n};\nuse super::{FunctionArgs, ModuleHost};\nuse crate::db::relational_db::RelationalDB;\nuse crate::host::module_host::{CallProcedureParams, ModuleInfo};\nuse crate::host::wasm_common::module_host_actor::{InstanceCommon, WasmInstance};\nuse crate::host::{InvalidProcedureArguments, InvalidReducerArguments, NoSuchModule};\nuse anyhow::anyhow;\nuse core::time::Duration;\nuse futures::{FutureExt, StreamExt};\nuse rustc_hash::FxHashMap;\nuse spacetimedb_client_api_messages::energy::EnergyQuanta;\nuse spacetimedb_datastore::execution_context::{ExecutionContext, ReducerContext, Workload};\nuse spacetimedb_datastore::locking_tx_datastore::MutTxId;\nuse spacetimedb_datastore::system_tables::{StFields, StScheduledFields, ST_SCHEDULED_ID};\nuse spacetimedb_datastore::traits::IsolationLevel;\nuse spacetimedb_lib::scheduler::ScheduleAt;\nuse spacetimedb_lib::Timestamp;\nuse spacetimedb_primitives::{ColId, FunctionId, TableId};\nuse spacetimedb_sats::bsatn::ToBsatn as _;\nuse spacetimedb_sats::AlgebraicValue;\nuse spacetimedb_table::table::RowRef;\nuse std::panic;\nuse std::sync::Arc;\nuse tokio::sync::mpsc;\nuse tokio::time::Instant;\nuse tokio_util::time::delay_queue::{self, DelayQueue, Expired};\n\n#[derive(Copy, Clone, Eq, PartialEq, Hash)]\npub struct ScheduledFunctionId {\n    /// The ID of the table whose rows hold the scheduled reducers or procedures.\n    /// This table should have an entry in `ST_SCHEDULED`.\n    table_id: TableId,\n    /// The particular schedule row in the scheduling table referred to by `self.table_id`.\n    schedule_id: u64,\n    // These may seem redundant, but they're actually free - they fit in the struct padding.\n    // `scheduled_id: u64, table_id: u32, id_column: u16, at_column: u16` == 16 bytes, same as\n    // (`scheduled_id: u64, table_id: u32` == 12 bytes).pad_to_align() == 16 bytes\n    /// The column that the primary key (`scheduled_id`) is in.\n    id_column: ColId,\n    /// The column that the `ScheduleAt` value is in.\n    at_column: ColId,\n}\n\nspacetimedb_table::static_assert_size!(ScheduledFunctionId, 16);\n\nenum MsgOrExit<T> {\n    Msg(T),\n    Exit,\n}\n\nenum SchedulerMessage {\n    Schedule {\n        id: ScheduledFunctionId,\n        /// The timestamp we'll tell the reducer it is.\n        effective_at: Timestamp,\n        /// The actual instant we're scheduling for.\n        real_at: Instant,\n    },\n    ScheduleImmediate {\n        function_name: String,\n        args: FunctionArgs,\n    },\n}\n\npub struct ScheduledFunction {\n    function: Box<str>,\n    bsatn_args: Vec<u8>,\n}\n\n#[derive(Clone)]\npub struct Scheduler {\n    tx: mpsc::UnboundedSender<MsgOrExit<SchedulerMessage>>,\n}\n\npub struct SchedulerStarter {\n    rx: mpsc::UnboundedReceiver<MsgOrExit<SchedulerMessage>>,\n    db: Arc<RelationalDB>,\n}\n\nimpl Scheduler {\n    pub fn open(db: Arc<RelationalDB>) -> (Self, SchedulerStarter) {\n        let (tx, rx) = mpsc::unbounded_channel();\n        (Scheduler { tx }, SchedulerStarter { rx, db })\n    }\n}\n\nimpl SchedulerStarter {\n    // TODO(cloutiertyler): This whole start dance is scuffed, but I don't have\n    // time to make it better right now.\n    pub fn start(mut self, module_host: &ModuleHost) -> anyhow::Result<()> {\n        let mut queue: DelayQueue<QueueItem> = DelayQueue::new();\n        let mut key_map = FxHashMap::default();\n\n        let tx = self.db.begin_tx(Workload::Internal);\n\n        // Draining rx before processing schedules from the DB to ensure there are no in-flight messages,\n        // as this can result in duplication.\n        //\n        // Explanation: By this time, if the `Scheduler::schedule` method has been called (the `init` reducer can do that),\n        // there will be an in-flight message in tx that has already been inserted into the DB.\n        // We are building the `queue` below with the DB and then spawning `SchedulerActor`, which will processes\n        // the in-flight message, resulting in a duplicate entry in the queue.\n        while self.rx.try_recv().is_ok() {}\n\n        // Find all Scheduled tables\n        for st_scheduled_row in self.db.iter(&tx, ST_SCHEDULED_ID)? {\n            let table_id = st_scheduled_row.read_col(StScheduledFields::TableId)?;\n            let (id_column, at_column) = self\n                .db\n                .table_scheduled_id_and_at(&tx, table_id)?\n                .ok_or_else(|| anyhow!(\"scheduled table {table_id} doesn't have valid columns\"))?;\n\n            let now_ts = Timestamp::now();\n            let now_instant = Instant::now();\n\n            // Insert each entry (row) in the scheduled table into `queue`.\n            for scheduled_row in self.db.iter(&tx, table_id)? {\n                let (schedule_id, schedule_at) = get_schedule_from_row(&scheduled_row, id_column, at_column)?;\n                // calculate duration left to call the scheduled reducer\n                let duration = schedule_at.to_duration_from(now_ts);\n                let at = schedule_at.to_timestamp_from(now_ts);\n                let id = ScheduledFunctionId {\n                    table_id,\n                    schedule_id,\n                    id_column,\n                    at_column,\n                };\n                let key = queue.insert_at(QueueItem::Id { id, at }, now_instant + duration);\n\n                // This should never happen as duplicate entries should be gated by unique\n                // constraint violation in scheduled tables.\n                if key_map.insert(id, key).is_some() {\n                    return Err(anyhow!(\n                        \"Duplicate key found in scheduler queue: table_id {}, schedule_id {}\",\n                        id.table_id,\n                        id.schedule_id\n                    ));\n                }\n            }\n        }\n\n        tokio::spawn(\n            SchedulerActor {\n                rx: self.rx,\n                queue,\n                key_map,\n                module_host: module_host.downgrade(),\n            }\n            .run(),\n        );\n\n        Ok(())\n    }\n}\n\n/// The maximum `Duration` into the future that we can schedule a reducer.\n///\n/// `tokio_utils::time::DelayQueue`, as of version 0.7.8,\n/// limits its scheduling to at most approx. 2 years into the future.\n/// More specifically, they define:\n/// ```ignore\n/// const NUM_LEVELS: usize = 6;\n/// const MAX_DURATION: u64 = (1 << (6 * NUM_LEVELS)) - 1;\n/// ```\n/// These specific incantations have to do with the internal representation\n/// of `DelayQueue`.\n///\n/// Unfortunately, rather than returning an `Err`\n/// if the requested duration is longer than `MAX_DURATION`,\n/// `DelayQueue` will panic.\n/// We can't allow users to crash SpacetimeDB\n/// by scheduling a reducer in the distant future,\n/// so we have to re-derive their maximum delay\n/// and check against it ourselves.\n///\n/// The exact range of delays supported by `DelayQueue` may change in the future,\n/// but (hopefully) it won't ever shrink, as that would be a user-visible regression.\n/// If `DelayQueue` extends to support a larger range,\n/// we may reject some long-delayed schedule calls which could succeed,\n/// but we will never permit a schedule attempt which will panic.\nconst MAX_SCHEDULE_DELAY: Duration = Duration::from_millis(\n    // Equal to 64^6 - 1 milliseconds, which is 2.177589 years.\n    (1 << (6 * 6)) - 1,\n);\n\n#[derive(thiserror::Error, Debug)]\npub enum ScheduleError {\n    #[error(\"Unable to schedule with long delay at {0:?}\")]\n    DelayTooLong(Duration),\n\n    #[error(\"Unable to read scheduled row: {0:?}\")]\n    DecodingError(anyhow::Error),\n}\n\nimpl Scheduler {\n    /// Schedule a reducer/procedure to run from a scheduled table.\n    ///\n    /// `fn_start` is the timestamp of the start of the current reducer/procedure.\n    pub(super) fn schedule(\n        &self,\n        table_id: TableId,\n        schedule_id: u64,\n        schedule_at: ScheduleAt,\n        id_column: ColId,\n        at_column: ColId,\n        fn_start: Timestamp,\n    ) -> Result<(), ScheduleError> {\n        // if `Timestamp::now()` is properly monotonic, use it; otherwise, use\n        // the start of the reducer run as \"now\" for purposes of scheduling\n        // TODO(procedure-tx): when we do `with_tx` in a procedure,\n        // it inherits the timestamp of the procedure,\n        // which could become a problem here for long running procedures.\n        let now = fn_start.max(Timestamp::now());\n\n        // Check that `at` is within `tokio_utils::time::DelayQueue`'s\n        // accepted time-range.\n        //\n        // `DelayQueue` uses a sliding window, and there may be some non-zero\n        // delay between this check and the actual call to `DelayQueue::insert_at`.\n        //\n        // Assuming a monotonic clock, this means we may reject some otherwise\n        // acceptable schedule calls.\n        let delay = schedule_at.to_duration_from(now);\n        if delay >= MAX_SCHEDULE_DELAY {\n            return Err(ScheduleError::DelayTooLong(delay));\n        }\n        let effective_at = schedule_at.to_timestamp_from(now);\n        let real_at = Instant::now() + delay;\n\n        // if the actor has exited, it's fine to ignore; it means that the host actor calling\n        // schedule will exit soon as well, and it'll be scheduled to run when the module host restarts\n        let _ = self.tx.send(MsgOrExit::Msg(SchedulerMessage::Schedule {\n            id: ScheduledFunctionId {\n                table_id,\n                schedule_id,\n                id_column,\n                at_column,\n            },\n            effective_at,\n            real_at,\n        }));\n\n        Ok(())\n    }\n\n    pub fn volatile_nonatomic_schedule_immediate(&self, function_name: String, args: FunctionArgs) {\n        let _ = self.tx.send(MsgOrExit::Msg(SchedulerMessage::ScheduleImmediate {\n            function_name,\n            args,\n        }));\n    }\n\n    pub fn close(&self) {\n        let _ = self.tx.send(MsgOrExit::Exit);\n    }\n\n    pub async fn closed(&self) {\n        self.tx.closed().await\n    }\n}\n\nstruct SchedulerActor {\n    rx: mpsc::UnboundedReceiver<MsgOrExit<SchedulerMessage>>,\n    queue: DelayQueue<QueueItem>,\n    key_map: FxHashMap<ScheduledFunctionId, delay_queue::Key>,\n    module_host: WeakModuleHost,\n}\n\nenum QueueItem {\n    Id { id: ScheduledFunctionId, at: Timestamp },\n    VolatileNonatomicImmediate { function_name: String, args: FunctionArgs },\n}\n\npub(crate) struct ScheduledFunctionParams(QueueItem);\n\n#[cfg(target_pointer_width = \"64\")]\nspacetimedb_table::static_assert_size!(QueueItem, 64);\n\nimpl SchedulerActor {\n    async fn run(mut self) {\n        loop {\n            tokio::select! {\n                msg = self.rx.recv() => match msg {\n                    Some(MsgOrExit::Msg(msg)) => self.handle_message(msg),\n                    // it's fine to just drop any messages in the queue because they've\n                    // already been stored in the database\n                    Some(MsgOrExit::Exit) | None => break,\n                },\n                Some(scheduled) = self.queue.next() => {\n                    self.handle_queued(scheduled).await;\n                }\n            }\n        }\n    }\n\n    fn handle_message(&mut self, msg: SchedulerMessage) {\n        match msg {\n            SchedulerMessage::Schedule {\n                id,\n                effective_at,\n                real_at,\n            } => {\n                // Incase of row update, remove the existing entry from queue first\n                if let Some(key) = self.key_map.get(&id) {\n                    self.queue.remove(key);\n                }\n                let key = self.queue.insert_at(QueueItem::Id { id, at: effective_at }, real_at);\n                self.key_map.insert(id, key);\n            }\n            SchedulerMessage::ScheduleImmediate { function_name, args } => {\n                self.queue.insert(\n                    QueueItem::VolatileNonatomicImmediate { function_name, args },\n                    Duration::ZERO,\n                );\n            }\n        }\n    }\n\n    async fn handle_queued(&mut self, id: Expired<QueueItem>) {\n        let item = id.into_inner();\n        let id: Option<ScheduledFunctionId> = match item {\n            QueueItem::Id { id, .. } => Some(id),\n            QueueItem::VolatileNonatomicImmediate { .. } => None,\n        };\n        if let Some(id) = id {\n            self.key_map.remove(&id);\n        }\n\n        let Some(module_host) = self.module_host.upgrade() else {\n            return;\n        };\n\n        let result = module_host.call_scheduled_function(ScheduledFunctionParams(item)).await;\n\n        match result {\n            // If the module already exited, leave the `ScheduledFunction` in\n            // the database for when the module restarts.\n            Err(NoSuchModule) => {}\n            Ok(CallScheduledFunctionResult { reschedule: None }) => {\n                // nothing to do\n            }\n            Ok(CallScheduledFunctionResult {\n                reschedule: Some(Reschedule { at_ts, at_real }),\n            }) => {\n                if let Some(id) = id {\n                    // If this was repeated, we need to add it back to the queue.\n                    let key = self.queue.insert_at(QueueItem::Id { id, at: at_ts }, at_real);\n                    self.key_map.insert(id, key);\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct CallScheduledFunctionResult {\n    reschedule: Option<Reschedule>,\n}\n\n#[derive(Debug)]\nstruct Reschedule {\n    at_ts: Timestamp,\n    at_real: Instant,\n}\n\npub(super) async fn call_scheduled_function(\n    module_info: &ModuleInfo,\n    params: ScheduledFunctionParams,\n    inst_common: &mut InstanceCommon,\n    inst: &mut impl WasmInstance,\n) -> (CallScheduledFunctionResult, bool) {\n    let ScheduledFunctionParams(item) = params;\n\n    let id: Option<ScheduledFunctionId> = match item {\n        QueueItem::Id { id, .. } => Some(id),\n        QueueItem::VolatileNonatomicImmediate { .. } => None,\n    };\n    let db = &**module_info.relational_db();\n\n    enum Function {\n        Reducer(CallScheduledFunctionResult, bool),\n        Procedure {\n            params: CallProcedureParams,\n            reschedule: Option<Reschedule>,\n        },\n    }\n\n    let next_step = {\n        let mut tx = db.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);\n\n        // Determine the call params.\n        // This also lets us know whether to call a reducer or procedure.\n        let params = call_params_for_queued_item(module_info, db, &tx, item);\n        let (timestamp, instant, params) = match params {\n            // If the function was already deleted, leave the `ScheduledFunction`\n            // in the database for when the module restarts.\n            Ok(None) => return (CallScheduledFunctionResult { reschedule: None }, false),\n            Ok(Some(params)) => params,\n            Err(err) => {\n                // All we can do here is log an error.\n                log::error!(\"could not determine scheduled function or its parameters: {err:#}\");\n                let reschedule = delete_scheduled_function_row(module_info, db, id, Some(tx), None, inst_common, inst);\n                return (CallScheduledFunctionResult { reschedule }, false);\n            }\n        };\n\n        // We've determined whether we have a reducer or procedure.\n        // The logic between them will now split,\n        // as for scheduled procedures, it's incorrect to retry them if execution aborts midway,\n        // so we must remove the schedule row before executing.\n        match params {\n            CallParams::Reducer(params) => {\n                // Patch the transaction context with ReducerContext so the commitlog\n                // records the reducer's name, caller, timestamp, and arguments.\n                //\n                // Background: Scheduled reducers start with Workload::Internal, but\n                // call_reducer_with_tx only sets ReducerContext when tx is None.\n                // Since we pass Some(tx), we must set it here.\n                let reducer_name = &module_info.module_def.reducer_by_id(params.reducer_id).name;\n                tx.ctx = ExecutionContext::with_workload(\n                    tx.ctx.database_identity(),\n                    Workload::Reducer(ReducerContext {\n                        name: reducer_name.clone(),\n                        caller_identity: params.caller_identity,\n                        caller_connection_id: params.caller_connection_id,\n                        timestamp: params.timestamp,\n                        arg_bsatn: params.args.get_bsatn().clone(),\n                    }),\n                );\n\n                // We don't want a panic in the module host to affect the scheduler, as unlikely\n                // as it might be, so catch it so we can handle it \"gracefully\". Panics will\n                // print their message and backtrace when they occur, so we don't need to do\n                // anything with the error payload.\n                let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {\n                    inst_common.call_reducer_with_tx(Some(tx), params, inst)\n                }));\n                let reschedule = delete_scheduled_function_row(module_info, db, id, None, None, inst_common, inst);\n                // Currently, we drop the return value from the function call. In the future,\n                // we might want to handle it somehow.\n                let trapped = match result {\n                    Ok((_res, trapped)) => trapped,\n                    Err(_err) => true,\n                };\n                Function::Reducer(CallScheduledFunctionResult { reschedule }, trapped)\n            }\n            CallParams::Procedure(params) => {\n                // Delete scheduled row.\n                let reschedule = delete_scheduled_function_row(\n                    module_info,\n                    db,\n                    id,\n                    Some(tx),\n                    Some((timestamp, instant)),\n                    inst_common,\n                    inst,\n                );\n                Function::Procedure { params, reschedule }\n            }\n        }\n    };\n\n    // Below code is outside of the DB transaction scope because the\n    // compiler complains about holding mutable borrow across await point while calling a procedure,\n    // even though it has been already moved during `delete_scheduled_function_row` call.\n    match next_step {\n        Function::Reducer(result, trapped) => (result, trapped),\n        Function::Procedure { params, reschedule } => {\n            // Execute the procedure. See above for commentary on `catch_unwind()`.\n            let result = panic::AssertUnwindSafe(inst_common.call_procedure(params, inst))\n                .catch_unwind()\n                .await;\n\n            // Currently, we drop the return value from the function call. In the future,\n            // we might want to handle it somehow.\n            let trapped = match result {\n                Ok((_res, trapped)) => trapped,\n                Err(_err) => true,\n            };\n\n            (CallScheduledFunctionResult { reschedule }, trapped)\n        }\n    }\n}\n\n/// Deletes a scheduled-row entry after its function runs, reusing `tx` when one is already\n/// open and otherwise creating an internal transaction for the cleanup.\n///\n/// One-shot schedules are deleted and committed immediately. Interval schedules are not\n/// deleted here; instead their `ScheduleAt` is returned to the caller as a `Reschedule`.\nfn delete_scheduled_function_row(\n    module_info: &ModuleInfo,\n    db: &RelationalDB,\n    id: Option<ScheduledFunctionId>,\n    tx: Option<MutTxId>,\n    timestamp: Option<(Timestamp, Instant)>,\n    inst_common: &mut InstanceCommon,\n    inst: &mut impl WasmInstance,\n) -> Option<Reschedule> {\n    id.and_then(|id| {\n        let (timestamp, instant) = timestamp.unwrap_or_else(|| (Timestamp::now(), Instant::now()));\n        let tx = tx.unwrap_or_else(|| db.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal));\n        let schedule_at = delete_scheduled_function_row_with_tx(module_info, db, tx, id, inst_common, inst)?;\n        let ScheduleAt::Interval(dur) = schedule_at else {\n            return None;\n        };\n        Some(Reschedule {\n            at_ts: schedule_at.to_timestamp_from(timestamp),\n            at_real: instant + dur.to_duration_abs(),\n        })\n    })\n}\n\n/// Deletes a scheduled-row entry inside an existing mutable transaction.\n///\n/// If the row describes a one-shot schedule, this also refreshes any stale views and\n/// broadcasts the resulting delete in the same transaction.\n///\n/// Interval schedules are left in place and returned to the caller for rescheduling.\nfn delete_scheduled_function_row_with_tx(\n    module_info: &ModuleInfo,\n    db: &RelationalDB,\n    mut tx: MutTxId,\n    id: ScheduledFunctionId,\n    inst_common: &mut InstanceCommon,\n    inst: &mut impl WasmInstance,\n) -> Option<ScheduleAt> {\n    if let Ok(Some(schedule_row)) = get_schedule_row_mut(&tx, db, id) {\n        match read_schedule_at(&schedule_row, id.at_column) {\n            Ok(schedule_at) => {\n                // If the schedule is an interval, we handle it as a repeated schedule\n                if let ScheduleAt::Interval(_) = schedule_at {\n                    return Some(schedule_at);\n                }\n                let row_ptr = schedule_row.pointer();\n                db.delete(&mut tx, id.table_id, [row_ptr]);\n\n                refresh_views_then_commit_and_broadcast(tx, module_info, inst_common, inst);\n            }\n            _ => {\n                log::debug!(\n                    \"Failed to read 'scheduled_at' from row: table_id {}, schedule_id {}\",\n                    id.table_id,\n                    id.schedule_id\n                );\n            }\n        }\n    }\n    None\n}\n\n/// Refreshes stale views, commits transaction, and broadcasts subscription updates.\nfn refresh_views_then_commit_and_broadcast(\n    tx: MutTxId,\n    module_info: &ModuleInfo,\n    inst_common: &mut InstanceCommon,\n    inst: &mut impl WasmInstance,\n) {\n    let timestamp = Timestamp::now();\n    let (view_result, trapped) = inst_common.call_views_with_tx(tx, module_info.database_identity, inst, timestamp);\n    let mut status = match view_result.outcome {\n        crate::host::module_host::ViewOutcome::Success => EventStatus::Committed(DatabaseUpdate::default()),\n        crate::host::module_host::ViewOutcome::Failed(err) => EventStatus::FailedInternal(err),\n        crate::host::module_host::ViewOutcome::BudgetExceeded => EventStatus::OutOfEnergy,\n    };\n    if trapped && matches!(status, EventStatus::Committed(_)) {\n        status = EventStatus::FailedInternal(\"The instance encountered a fatal error.\".into());\n    }\n\n    let event = ModuleEvent {\n        timestamp,\n        caller_identity: module_info.database_identity,\n        caller_connection_id: None,\n        function_call: ModuleFunctionCall::default(),\n        status,\n        reducer_return_value: None,\n        //Keeping them 0 as it is internal transaction, not by reducer\n        energy_quanta_used: EnergyQuanta { quanta: 0 },\n        host_execution_duration: Duration::from_millis(0),\n        request_id: None,\n        timer: None,\n    };\n\n    if let Err(e) = module_info\n        .subscriptions\n        .commit_and_broadcast_event(None, event, view_result.tx)\n    {\n        log::error!(\"Failed to broadcast deletion event: {e:#}\");\n    }\n}\n\nfn call_params_for_queued_item(\n    module: &ModuleInfo,\n    db: &RelationalDB,\n    tx: &MutTxId,\n    item: QueueItem,\n) -> anyhow::Result<Option<(Timestamp, Instant, CallParams)>> {\n    Ok(Some(match item {\n        QueueItem::Id { id, at } => {\n            let Some(schedule_row) = get_schedule_row_mut(tx, db, id)? else {\n                // If the row is not found, it means the schedule is cancelled by the user.\n                return Ok(None);\n            };\n            let ScheduledFunction { function, bsatn_args } = process_schedule(tx, db, id.table_id, &schedule_row)?;\n\n            let fun_args = FunctionArgs::Bsatn(bsatn_args.into());\n            function_to_call_params(module, &function, fun_args, Some(at))?\n        }\n        QueueItem::VolatileNonatomicImmediate { function_name, args } => {\n            function_to_call_params(module, &function_name, args, None)?\n        }\n    }))\n}\n\nenum CallParams {\n    Reducer(CallReducerParams),\n    Procedure(CallProcedureParams),\n}\n\n/// Finds the function for `name`\n/// and returns the appropriate call parameters\n/// to call the function with `args`.\nfn function_to_call_params(\n    module: &ModuleInfo,\n    name: &str,\n    args: FunctionArgs,\n    at: Option<Timestamp>,\n) -> anyhow::Result<(Timestamp, Instant, CallParams)> {\n    let identity = module.database_identity;\n\n    // Find the function and deserialize the arguments.\n    let module = &module.module_def;\n    let (id, args) = if let Some((id, def)) = module.reducer_full(name) {\n        let args = args.into_tuple_for_def(module, def).map_err(InvalidReducerArguments)?;\n        (FunctionId::Reducer(id), args)\n    } else if let Some((id, def)) = module.procedure_full(name) {\n        let args = args\n            .into_tuple_for_def(module, def)\n            .map_err(InvalidProcedureArguments)?;\n        (FunctionId::Procedure(id), args)\n    } else {\n        // This should be impossible, but let's still return an error to log.\n        return Err(anyhow!(\"Reducer or procedure `{name}` not found\"));\n    };\n\n    // The timestamp we tell the function it's running at will be\n    // at least the timestamp it was scheduled to run at.\n    let now = Timestamp::now();\n    let ts = at.unwrap_or(now).max(now);\n    let instant = Instant::now() + ts.duration_since(now).unwrap_or(Duration::ZERO);\n\n    let params = match id {\n        FunctionId::Reducer(id) => CallParams::Reducer(CallReducerParams::from_system(ts, identity, id, args)),\n        FunctionId::Procedure(id) => CallParams::Procedure(CallProcedureParams::from_system(ts, identity, id, args)),\n    };\n\n    Ok((ts, instant, params))\n}\n\n/// Generate [`ScheduledFunction`] for given [`ScheduledFunctionId`].\nfn process_schedule(\n    tx: &MutTxId,\n    db: &RelationalDB,\n    table_id: TableId,\n    schedule_row: &RowRef<'_>,\n) -> Result<ScheduledFunction, anyhow::Error> {\n    // Get reducer name from `ST_SCHEDULED` table.\n    let table_id_col = StScheduledFields::TableId.col_id();\n    let function_name_col = StScheduledFields::ReducerName.col_id();\n    let st_scheduled_row = db\n        .iter_by_col_eq_mut(tx, ST_SCHEDULED_ID, table_id_col, &table_id.into())?\n        .next()\n        .ok_or_else(|| anyhow!(\"Scheduled table with id {table_id} entry does not exist in `st_scheduled`\"))?;\n    let function = st_scheduled_row.read_col::<Box<str>>(function_name_col)?;\n\n    Ok(ScheduledFunction {\n        function,\n        bsatn_args: schedule_row.to_bsatn_vec()?,\n    })\n}\n\n/// Returns the `schedule_row` for `id`.\nfn get_schedule_row_mut<'a>(\n    tx: &'a MutTxId,\n    db: &'a RelationalDB,\n    id: ScheduledFunctionId,\n) -> anyhow::Result<Option<RowRef<'a>>> {\n    Ok(db\n        .iter_by_col_eq_mut(tx, id.table_id, id.id_column, &id.schedule_id.into())?\n        .next())\n}\n\n/// Helper to get `schedule_id` and `schedule_at`\n/// from `schedule_row` product value.\npub fn get_schedule_from_row(\n    row: &RowRef<'_>,\n    id_column: ColId,\n    at_column: ColId,\n) -> anyhow::Result<(u64, ScheduleAt)> {\n    let schedule_id: u64 = row.read_col(id_column)?;\n    let schedule_at = read_schedule_at(row, at_column)?;\n\n    Ok((schedule_id, schedule_at))\n}\n\nfn read_schedule_at(row: &RowRef<'_>, at_column: ColId) -> anyhow::Result<ScheduleAt> {\n    let schedule_at_av: AlgebraicValue = row.read_col(at_column)?;\n    ScheduleAt::try_from(schedule_at_av).map_err(|e| anyhow!(\"Failed to convert 'scheduled_at' to ScheduleAt: {e:?}\"))\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/budget.rs",
    "content": "#![allow(dead_code)]\n\n//! Provides budget, energy, timeout, and long-running logging facilities.\n//!\n//! These are all driven by [`with_timeout_and_cb_every`] for V8 modules\n//! as V8 has no native notion of gas/fuel,\n//! so we have to invent one using time and timeouts.\n\nuse super::env_on_isolate;\nuse crate::host::wasm_common::module_host_actor::EnergyStats;\nuse crate::host::wasmtime::{epoch_ticker, ticks_in_duration};\nuse core::ptr;\nuse core::sync::atomic::Ordering;\nuse core::time::Duration;\nuse core::{ffi::c_void, sync::atomic::AtomicBool};\nuse spacetimedb_client_api_messages::energy::FunctionBudget;\nuse std::sync::Arc;\nuse v8::{Isolate, IsolateHandle};\n\n/// Runs `logic` concurrently wth a thread that will terminate JS execution\n/// when `budget` has been used up.\n///\n/// Every `callback_every` ticks, `callback` is called.\npub(super) fn with_timeout_and_cb_every<R>(\n    _handle: IsolateHandle,\n    _callback_every: u64,\n    _callback: InterruptCallback,\n    _budget: FunctionBudget,\n    logic: impl FnOnce() -> R,\n) -> R {\n    // Start the concurrent thread.\n    // TODO(v8): This currently leads to UB as there are bugs in th v8 crate.\n    //let timeout_thread_cancel_flag = run_timeout_and_cb_every(handle, callback_every, callback, budget);\n\n    #[allow(clippy::let_and_return)]\n    let ret = logic();\n\n    // Cancel the execution timeout in `run_timeout_and_cb_every`.\n    //timeout_thread_cancel_flag.store(true, Ordering::Relaxed);\n\n    ret\n}\n\n/// A callback passed to [`IsolateHandle::request_interrupt`].\npub(super) type InterruptCallback = extern \"C\" fn(v8::UnsafeRawIsolatePtr, *mut c_void);\n\n/// An [`InterruptCallback`] used by `call_reducer`,\n/// and called by a thread separate to V8 execution\n/// every [`EPOCH_TICKS_PER_SECOND`] ticks (~every 1 second)\n/// to log that the reducer is still running.\npub(super) extern \"C\" fn cb_log_long_running(mut isolate: v8::UnsafeRawIsolatePtr, _: *mut c_void) {\n    let isolate = unsafe { Isolate::ref_from_raw_isolate_ptr_mut_unchecked(&mut isolate) };\n    let Some(env) = env_on_isolate(isolate) else {\n        // All we can do is log something.\n        tracing::error!(\"`JsInstanceEnv` not set\");\n        return;\n    };\n    let database = env.instance_env.replica_ctx.database_identity;\n    let reducer = env.instance_env.log_record_function().unwrap_or_default();\n    let dur = env.reducer_start().elapsed();\n    tracing::warn!(reducer, ?database, \"JavaScript has been running for {dur:?}\");\n}\n\n/// An [`InterruptCallback`] that does nothing.\npub(super) extern \"C\" fn cb_noop(_: v8::UnsafeRawIsolatePtr, _: *mut c_void) {}\n\n/// Spawns a thread that will terminate execution\n/// when `budget` has been used up.\n///\n/// Every `callback_every` ticks, `callback` is called.\nfn run_timeout_and_cb_every(\n    handle: IsolateHandle,\n    callback_every: u64,\n    callback: InterruptCallback,\n    budget: FunctionBudget,\n) -> Arc<AtomicBool> {\n    // When `execution_done_flag` is set, the ticker thread will stop.\n    let execution_done_flag = Arc::new(AtomicBool::new(false));\n    let execution_done_flag2 = execution_done_flag.clone();\n\n    let timeout = budget_to_duration(budget);\n    let max_ticks = ticks_in_duration(timeout);\n\n    let mut num_ticks = 0;\n    epoch_ticker(move || {\n        // Check if execution completed.\n        if execution_done_flag2.load(Ordering::Relaxed) {\n            return None;\n        }\n\n        // We've reached the number of ticks to call `callback`.\n        if num_ticks % callback_every == 0 && handle.request_interrupt(callback, ptr::null_mut()) {\n            return None;\n        }\n\n        if num_ticks == max_ticks {\n            // Execution still ongoing while budget has been exhausted.\n            // Terminate V8 execution.\n            // This implements \"gas\" for v8.\n            handle.terminate_execution();\n        }\n\n        num_ticks += 1;\n        Some(())\n    });\n\n    execution_done_flag\n}\n\n/// Converts a [`ReducerBudget`] to a [`Duration`].\nfn budget_to_duration(_budget: FunctionBudget) -> Duration {\n    // TODO(v8): This is fake logic that allows a maximum timeout.\n    // Replace with sensible math.\n    Duration::MAX\n}\n\n/// Returns [`EnergyStats`] for a reducer given its `budget`\n/// and the `duration` it took to execute.\npub(super) fn energy_from_elapsed(budget: FunctionBudget, duration: Duration) -> EnergyStats {\n    let used = duration_to_budget(duration);\n    let remaining = budget - used;\n    EnergyStats { budget, remaining }\n}\n\n/// Converts a [`Duration`] to a [`ReducerBudget`].\nfn duration_to_budget(_duration: Duration) -> FunctionBudget {\n    // TODO(v8): This is fake logic that allows minimum energy usage.\n    // Replace with sensible math.\n    FunctionBudget::ZERO\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/builtins/delete_math_random.js",
    "content": "delete Math.random;\nObject.defineProperty(Math, 'random', {\n  enumerable: false,\n  configurable: true,\n  get() {\n    throw new TypeError(\n      'Math.random is not available in SpacetimeDB modules. Use ctx.random instead.'\n    );\n  },\n});\n"
  },
  {
    "path": "crates/core/src/host/v8/builtins/mod.rs",
    "content": "use v8::{FunctionCallbackArguments, Local, PinScope};\n\nuse super::de::scratch_buf;\nuse super::error::{exception_already_thrown, ExcResult, StringTooLongError, Throwable, TypeError};\nuse super::string::{str_from_ident, StringConst};\nuse super::{FnRet, IntoJsString};\n\npub(super) fn evaluate_builtins(scope: &mut PinScope<'_, '_>) -> ExcResult<()> {\n    macro_rules! eval_builtin {\n        ($file:literal) => {\n            eval_builtin(\n                scope,\n                const { &StringConst::new(concat!(\"internal:\", $file)) },\n                const { &StringConst::new(include_str!(concat!(\"./\", $file))) },\n            )\n        };\n    }\n    eval_builtin!(\"text_encoding.js\")?;\n    eval_builtin!(\"delete_math_random.js\")?;\n    Ok(())\n}\n\nfn eval_builtin(\n    scope: &mut PinScope<'_, '_>,\n    resource_name: &'static StringConst,\n    code: &'static StringConst,\n) -> ExcResult<()> {\n    let resource_name = resource_name.string(scope);\n    let code = code.string(scope);\n    super::eval_module(scope, resource_name.into(), code, resolve_builtins_module)?;\n    Ok(())\n}\n\nmacro_rules! create_synthetic_module {\n    ($scope:expr, $module_name:expr $(,  $fun:ident)* $(,)?) => {{\n        let export_names = &[$(str_from_ident!($fun).string($scope)),*];\n        let eval_steps = |context, module| {\n            v8::callback_scope!(unsafe scope, context);\n            $(\n                register_module_fun(scope, &module, str_from_ident!($fun), $fun);\n            )*\n\n            Some(v8::undefined(scope).into())\n        };\n\n        v8::Module::create_synthetic_module(\n            $scope,\n            const { StringConst::new($module_name) }.string($scope),\n            export_names,\n            eval_steps,\n        )\n    }}\n}\n\n/// Adapts `fun`, which returns a [`Value`] to one that works on [`v8::ReturnValue`].\nfn adapt_fun(\n    fun: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> FnRet<'scope>,\n) -> impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>, v8::ReturnValue) {\n    move |scope, args, mut rv| {\n        // Set the result `value` on success.\n        if let Ok(value) = fun(scope, args) {\n            rv.set(value);\n        }\n    }\n}\n\n/// Registers a function in `module`\n/// where the function has `name` and does `body`.\nfn register_module_fun(\n    scope: &mut v8::PinCallbackScope<'_, '_>,\n    module: &Local<'_, v8::Module>,\n    name: &'static StringConst,\n    body: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> FnRet<'scope>,\n) -> Option<bool> {\n    // Convert the name.\n    let name = name.string(scope);\n\n    // Convert the function.\n    let fun = v8::Function::builder(adapt_fun(body)).constructor_behavior(v8::ConstructorBehavior::Throw);\n    let fun = fun.build(scope)?.into();\n\n    // Set the export on the module.\n    module.set_synthetic_module_export(scope, name, fun)\n}\n\nfn resolve_builtins_module<'scope>(\n    context: Local<'scope, v8::Context>,\n    spec: Local<'scope, v8::String>,\n    _attrs: Local<'scope, v8::FixedArray>,\n    _referrer: Local<'scope, v8::Module>,\n) -> Option<Local<'scope, v8::Module>> {\n    v8::callback_scope!(unsafe scope, context);\n    // resolve_sys_module_inner(scope, spec).ok()\n    let buf = &mut scratch_buf::<32>();\n    if spec.to_rust_cow_lossy(scope, buf) != \"spacetime:internal_builtins\" {\n        TypeError(\"Unknown module\").throw(scope);\n        return None;\n    }\n    Some(internal_builtins_module(scope))\n}\n\n/// An internal module providing native functions for certain JS builtins.\n///\n/// This is not public API, since it's not accessible to user modules - only to\n/// the js builtins in this directory.\nfn internal_builtins_module<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, v8::Module> {\n    create_synthetic_module!(scope, \"spacetime:internal_builtins\", utf8_encode, utf8_decode)\n}\n\n/// Encode a JS string into UTF-8.\n///\n/// Implementing this as a host call is much faster than implementing it as userspace JS.\n///\n/// Signature from ./types.d.ts:\n/// ```ts\n/// export function utf8_encode(s: string): Uint8Array<ArrayBuffer>;\n/// ```\nfn utf8_encode<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackArguments<'scope>) -> FnRet<'scope> {\n    let string_val = args.get(0);\n    let string = string_val\n        .to_string(scope)\n        .ok_or_else(exception_already_thrown)?\n        .to_rust_string_lossy(scope);\n    let byte_length = string.len();\n    let buf = v8::ArrayBuffer::new_backing_store_from_bytes(string.into_bytes()).make_shared();\n    let buf = v8::ArrayBuffer::with_backing_store(scope, &buf);\n    v8::Uint8Array::new(scope, buf, 0, byte_length)\n        .map(Into::into)\n        .ok_or_else(exception_already_thrown)\n}\n\n/// Decode a UTF-8 string from an `ArrayBuffer` into a JS string.\n///\n/// If `fatal` is true, throw an error if the data is not valid UTF-8.\n///\n/// Signature fom ./types.d.ts:\n/// ```ts\n/// export function utf8_decode(s: ArrayBufferView, fatal: boolean): string;\n/// ```\nfn utf8_decode<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackArguments<'scope>) -> FnRet<'scope> {\n    let buf = args.get(0);\n    let fatal = args.get(1).boolean_value(scope);\n    if let Ok(buf) = buf.try_cast::<v8::ArrayBufferView>() {\n        let buffer = buf.get_contents(&mut []);\n        let res = if fatal {\n            let s = std::str::from_utf8(buffer).map_err(|e| TypeError(e.to_string()).throw(scope))?;\n            s.into_string(scope)\n        } else {\n            v8::String::new_from_utf8(scope, buffer, v8::NewStringType::Normal)\n                .ok_or_else(|| StringTooLongError::new(&String::from_utf8_lossy(buffer)))\n        };\n        res.map(Into::into).map_err(|e| e.into_range_error().throw(scope))\n    } else {\n        Err(TypeError(\"argument is not an `ArrayBuffer` or a view on one\").throw(scope))\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/builtins/text_encoding.js",
    "content": "// @ts-check\n\n/// <reference path=\"types.d.ts\" />\n\nimport { utf8_decode, utf8_encode } from 'spacetime:internal_builtins';\n\nglobalThis.TextEncoder = class TextEncoder {\n  constructor() {}\n\n  get encoding() {\n    return 'utf-8';\n  }\n\n  encode(input = '') {\n    return utf8_encode(input);\n  }\n};\n\nglobalThis.TextDecoder = class TextDecoder {\n  /** @type {string} */\n  #encoding;\n\n  /** @type {boolean} */\n  #fatal;\n\n  /**\n   * @argument {string} label\n   * @argument {any} options\n   */\n  constructor(label = 'utf-8', options = {}) {\n    if (label !== 'utf-8') {\n      throw new RangeError('The encoding label provided is invalid');\n    }\n    this.#encoding = label;\n    this.#fatal = !!options.fatal;\n    if (options.ignoreBOM) {\n      throw new TypeError(\"Option 'ignoreBOM' not supported\");\n    }\n  }\n\n  get encoding() {\n    return this.#encoding;\n  }\n  get fatal() {\n    return this.#fatal;\n  }\n  get ignoreBOM() {\n    return false;\n  }\n\n  /**\n   * @argument {any} input\n   * @argument {any} options\n   */\n  decode(input, options = {}) {\n    if (options.stream) {\n      throw new TypeError(\"Option 'stream' not supported\");\n    }\n    if (input instanceof ArrayBuffer || input instanceof SharedArrayBuffer) {\n      input = new Uint8Array(input);\n    }\n    return utf8_decode(input, this.#fatal);\n  }\n};\n"
  },
  {
    "path": "crates/core/src/host/v8/builtins/types.d.ts",
    "content": "declare module 'spacetime:internal_builtins' {\n  export function utf8_encode(s: string): Uint8Array<ArrayBuffer>;\n  export function utf8_decode(s: ArrayBufferView, fatal: boolean): string;\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/de.rs",
    "content": "use super::error::{exception_already_thrown, ExcResult, ExceptionThrown, ExceptionValue, Throwable, TypeError};\nuse super::from_value::{cast, FromValue};\nuse super::string::{TAG, VALUE};\nuse super::FnRet;\nuse core::fmt;\nuse core::iter::{repeat_n, RepeatN};\nuse core::marker::PhantomData;\nuse core::mem::MaybeUninit;\nuse derive_more::From;\nuse spacetimedb_sats::de::{self, ArrayVisitor, DeserializeSeed, ProductVisitor, SliceVisitor, SumVisitor};\nuse spacetimedb_sats::{i256, u256};\nuse std::borrow::{Borrow, Cow};\nuse v8::{Array, Local, Name, Object, PinScope, Uint8Array, Value};\n\n/// Deserializes a `T` from `val` in `scope`, using `seed` for any context needed.\npub(super) fn deserialize_js_seed<'de, T: DeserializeSeed<'de>>(\n    scope: &mut PinScope<'de, '_>,\n    val: Local<'_, Value>,\n    seed: T,\n) -> ExcResult<T::Output> {\n    let de = Deserializer::new(scope, val);\n    seed.deserialize(de).map_err(|e| e.throw(scope))\n}\n\n/// Deserializes a `T` from `val` in `scope`.\npub(super) fn deserialize_js<'de, T: de::Deserialize<'de>>(\n    scope: &mut PinScope<'de, '_>,\n    val: Local<'_, Value>,\n) -> ExcResult<T> {\n    deserialize_js_seed(scope, val, PhantomData)\n}\n\n/// Deserializes from V8 values.\nstruct Deserializer<'this, 'scope, 'isolate> {\n    common: DeserializerCommon<'this, 'scope, 'isolate>,\n    input: Local<'scope, Value>,\n}\n\nimpl<'this, 'scope, 'isolate> Deserializer<'this, 'scope, 'isolate> {\n    /// Creates a new deserializer from `input` in `scope`.\n    fn new(scope: &'this mut PinScope<'scope, 'isolate>, input: Local<'_, Value>) -> Self {\n        let input = Local::new(scope, input);\n        let common = DeserializerCommon { scope };\n        Deserializer { input, common }\n    }\n}\n\n/// Things shared between various [`Deserializer`]s.\n///\n/// The lifetime `'scope` is that of the scope of values deserialized.\nstruct DeserializerCommon<'this, 'scope, 'isolate> {\n    /// The scope of values to deserialize.\n    scope: &'this mut PinScope<'scope, 'isolate>,\n}\n\nimpl<'scope, 'isolate> DeserializerCommon<'_, 'scope, 'isolate> {\n    fn reborrow(&mut self) -> DeserializerCommon<'_, 'scope, 'isolate> {\n        DeserializerCommon { scope: self.scope }\n    }\n}\n\n/// The possible errors that [`Deserializer`] can produce.\n#[derive(Debug, From)]\nenum Error<'scope> {\n    Unthrown(ExceptionValue<'scope>),\n    Thrown(ExceptionThrown),\n    Custom(String),\n}\n\nimpl<'scope> Throwable<'scope> for Error<'scope> {\n    fn throw(self, scope: &PinScope<'scope, '_>) -> ExceptionThrown {\n        match self {\n            Self::Unthrown(exception) => exception.throw(scope),\n            Self::Thrown(thrown) => thrown,\n            Self::Custom(msg) => TypeError(msg).throw(scope),\n        }\n    }\n}\n\nimpl de::Error for Error<'_> {\n    fn custom(msg: impl fmt::Display) -> Self {\n        Self::Custom(msg.to_string())\n    }\n}\n\n/// Returns a scratch buffer to fill when deserializing strings.\npub(crate) fn scratch_buf<const N: usize>() -> [MaybeUninit<u8>; N] {\n    [const { MaybeUninit::uninit() }; N]\n}\n\n/// Extracts a reference `&'scope T` from an owned V8 [`Local<'scope, T>`].\n///\n/// The lifetime `'scope` is that of the [`HandleScope<'scope>`].\n/// This ensures that the reference to `T` won't outlive the `HandleScope`.\nfn deref_local<'scope, T>(local: Local<'scope, T>) -> &'scope T {\n    let reference = local.borrow();\n    // SAFETY: Lifetime extend `'0` to `'scope`.\n    // This is safe as the returned reference `&'scope T`\n    // will not outlive its `HandleScope<'scope, _>`,\n    // as both are tied to the lifetime `'scope`.\n    unsafe { core::mem::transmute::<&T, &'scope T>(reference) }\n}\n\n/// Deserializes a primitive via [`FromValue`].\nmacro_rules! deserialize_primitive {\n    ($dmethod:ident, $t:ty) => {\n        fn $dmethod(self) -> Result<$t, Self::Error> {\n            FromValue::from_value(self.input, self.common.scope).map_err(Error::Unthrown)\n        }\n    };\n}\n\nimpl<'de, 'this, 'scope: 'de> de::Deserializer<'de> for Deserializer<'this, 'scope, '_> {\n    type Error = Error<'scope>;\n\n    // Deserialization of primitive types defers to `FromValue`.\n    deserialize_primitive!(deserialize_bool, bool);\n    deserialize_primitive!(deserialize_u8, u8);\n    deserialize_primitive!(deserialize_u16, u16);\n    deserialize_primitive!(deserialize_u32, u32);\n    deserialize_primitive!(deserialize_u64, u64);\n    deserialize_primitive!(deserialize_u128, u128);\n    deserialize_primitive!(deserialize_u256, u256);\n    deserialize_primitive!(deserialize_i8, i8);\n    deserialize_primitive!(deserialize_i16, i16);\n    deserialize_primitive!(deserialize_i32, i32);\n    deserialize_primitive!(deserialize_i64, i64);\n    deserialize_primitive!(deserialize_i128, i128);\n    deserialize_primitive!(deserialize_i256, i256);\n    deserialize_primitive!(deserialize_f64, f64);\n    deserialize_primitive!(deserialize_f32, f32);\n\n    fn deserialize_product<V: ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        // In `ProductType.serializeValue()` in the TS SDK, null/undefined is accepted for the unit type.\n        if visitor.product_len() == 0 && self.input.is_null_or_undefined() {\n            return visitor.visit_seq_product(de::UnitAccess::new());\n        }\n\n        let object = cast!(\n            self.common.scope,\n            self.input,\n            Object,\n            \"object for product type `{}`\",\n            visitor.product_name().unwrap_or(\"<unknown>\")\n        )?;\n\n        visitor.visit_named_product(ProductAccess {\n            common: self.common,\n            object,\n            next_value: None,\n            index: 0,\n        })\n    }\n\n    fn validate_product<V: ProductVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        // In `ProductType.serializeValue()` in the TS SDK, null/undefined is accepted for the unit type.\n        if visitor.product_len() == 0 && self.input.is_null_or_undefined() {\n            return visitor.validate_seq_product(de::UnitAccess::new());\n        }\n\n        let object = cast!(\n            self.common.scope,\n            self.input,\n            Object,\n            \"object for product type `{}`\",\n            visitor.product_name().unwrap_or(\"<unknown>\")\n        )?;\n\n        visitor.validate_named_product(ProductAccess {\n            common: self.common,\n            object,\n            next_value: None,\n            index: 0,\n        })\n    }\n\n    fn deserialize_sum<V: SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let scope = &*self.common.scope;\n\n        // In `SumType.serializeValue()` in the TS SDK, option is treated specially -\n        // null/undefined marks none, any other value `x` is `some(x)`.\n        if visitor.is_option() {\n            return if self.input.is_null_or_undefined() {\n                visitor.visit_sum(de::NoneAccess::new())\n            } else {\n                visitor.visit_sum(de::SomeAccess::new(self))\n            };\n        }\n\n        let sum_name = visitor.sum_name().unwrap_or(\"<unknown>\");\n\n        // We expect a canonical representation of a sum value in JS to be\n        // `{ tag: \"foo\", value: a_value_for_foo }`.\n        let tag_field = TAG.string(scope);\n        let object = cast!(scope, self.input, Object, \"object for sum type `{}`\", sum_name)?;\n\n        // Extract the `tag` field. It needs to contain a string.\n        let tag = property(scope, object, tag_field)?;\n        let tag = cast!(scope, tag, v8::String, \"string for sum tag of `{}`\", sum_name)?;\n\n        // Extract the `value` field.\n        let value_field = VALUE.string(scope);\n        let value = property(scope, object, value_field)?;\n\n        // Stitch it all together.\n        visitor.visit_sum(SumAccess {\n            common: self.common,\n            tag,\n            value,\n        })\n    }\n\n    fn deserialize_str<V: SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let val = cast!(self.common.scope, self.input, v8::String, \"`string`\")?;\n        let mut buf = scratch_buf::<64>();\n        match val.to_rust_cow_lossy(&mut *self.common.scope, &mut buf) {\n            Cow::Borrowed(s) => visitor.visit(s),\n            Cow::Owned(string) => visitor.visit_owned(string),\n        }\n    }\n\n    fn deserialize_bytes<V: SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let arr = cast!(self.common.scope, self.input, Uint8Array, \"`Uint8Array` for bytes\")?;\n        let storage: &'static mut [u8] = &mut [0; v8::TYPED_ARRAY_MAX_SIZE_IN_HEAP];\n        let bytes: &'scope [u8] = deref_local(arr).get_contents(storage);\n        visitor.visit_borrowed(bytes)\n    }\n\n    fn deserialize_array_seed<V: ArrayVisitor<'de, T::Output>, T: DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        let arr = cast!(self.common.scope, self.input, v8::Array, \"`Array`\")?;\n        visitor.visit(ArrayAccess::new(arr, self.common, seed))\n    }\n}\n\n/// Provides access to the field names and values in a JS object\n/// under the assumption that it's a product.\nstruct ProductAccess<'this, 'scope, 'isolate> {\n    common: DeserializerCommon<'this, 'scope, 'isolate>,\n    /// The input object being deserialized.\n    object: Local<'scope, Object>,\n    /// A field's value, to deserialize next in [`NamedProductAccess::get_field_value_seed`].\n    next_value: Option<Local<'scope, Value>>,\n    /// The index in the product to\n    index: usize,\n}\n\n// Creates an interned [`v8::String`].\npub(super) fn v8_interned_string<'scope>(scope: &PinScope<'scope, '_>, field: &str) -> Local<'scope, v8::String> {\n    // Internalized v8 strings are significantly faster than \"normal\" v8 strings\n    // since v8 deduplicates re-used strings minimizing new allocations\n    // see: https://github.com/v8/v8/blob/14ac92e02cc3db38131a57e75e2392529f405f2f/include/v8.h#L3165-L3171\n    v8::String::new_from_utf8(scope, field.as_ref(), v8::NewStringType::Internalized).unwrap()\n}\n\n/// Normalizes `field` into an interned `v8::String`.\npub(super) fn intern_field_name<'scope>(\n    scope: &PinScope<'scope, '_>,\n    field: Option<&str>,\n    index: usize,\n) -> Local<'scope, Name> {\n    let field = match field {\n        Some(field) => Cow::Borrowed(field),\n        None => Cow::Owned(format!(\"{index}\")),\n    };\n    v8_interned_string(scope, &field).into()\n}\n\n/// Returns the property for `key` on `object`.\npub(super) fn property<'scope>(\n    scope: &PinScope<'scope, '_>,\n    object: Local<'_, Object>,\n    key: impl Into<Local<'scope, Value>>,\n) -> FnRet<'scope> {\n    object.get(scope, key.into()).ok_or_else(exception_already_thrown)\n}\n\nimpl<'de, 'scope: 'de> de::NamedProductAccess<'de> for ProductAccess<'_, 'scope, '_> {\n    type Error = Error<'scope>;\n\n    fn get_field_ident<V: de::FieldNameVisitor<'de>>(&mut self, visitor: V) -> Result<Option<V::Output>, Self::Error> {\n        let scope = &*self.common.scope;\n        let mut field_names = visitor.field_names();\n        while let Some(field) = field_names.nth(self.index) {\n            // Get and advance the current index.\n            let index = self.index;\n            self.index += 1;\n\n            // Normalize the field name.\n            // Integer keys are converted to strings,\n            // as that is supported on JS objects.\n            let key = intern_field_name(scope, field, index);\n\n            // Check that such a field/key exists.\n            if !self\n                .object\n                .has_own_property(scope, key)\n                .ok_or_else(exception_already_thrown)?\n            {\n                continue;\n            }\n\n            // Extract the next value, to be processed in `get_field_value_seed`.\n            let val = property(scope, self.object, key)?;\n            self.next_value = Some(val);\n\n            drop(field_names);\n            return Ok(Some(visitor.visit_seq(index)));\n        }\n\n        Ok(None)\n    }\n\n    fn get_field_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<T::Output, Self::Error> {\n        let common = self.common.reborrow();\n        // Extract the field's value.\n        let input = self\n            .next_value\n            .take()\n            .expect(\"Call next_key_seed before next_value_seed\");\n        // Deserialize the field's value.\n        seed.deserialize(Deserializer { common, input })\n    }\n\n    fn validate_field_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<(), Self::Error> {\n        let common = self.common.reborrow();\n        // Extract the field's value.\n        let input = self\n            .next_value\n            .take()\n            .expect(\"Call next_key_seed before next_value_seed\");\n        // Deserialize the field's value.\n        seed.validate(Deserializer { common, input })\n    }\n}\n\n/// Used in `Deserializer::deserialize_sum` to translate a `tag` property of a JS object\n/// to a variant and to provide a deserializer for its value/payload.\nstruct SumAccess<'this, 'scope, 'isolate> {\n    common: DeserializerCommon<'this, 'scope, 'isolate>,\n    /// The tag of the sum value.\n    tag: Local<'scope, v8::String>,\n    /// The value of the sum value.\n    value: Local<'scope, Value>,\n}\n\nimpl<'de, 'this, 'scope: 'de, 'isolate> de::SumAccess<'de> for SumAccess<'this, 'scope, 'isolate> {\n    type Error = Error<'scope>;\n    type Variant = Deserializer<'this, 'scope, 'isolate>;\n\n    fn variant<V: de::VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        // Read the `tag` property in JS.\n        // We generously provide 32 bytes for inline reading of the property\n        // before resorting to the heap.\n        let mut buf = scratch_buf::<32>();\n        let name = self.tag.to_rust_cow_lossy(self.common.scope, &mut buf);\n\n        // Select the variant to deserialize.\n        let variant = visitor.visit_name::<Self::Error>(&name)?;\n\n        // Prepare the deserialization of the payload.\n        let dpayload = Deserializer {\n            common: self.common,\n            input: self.value,\n        };\n\n        Ok((variant, dpayload))\n    }\n}\n\nimpl<'de, 'this, 'scope: 'de> de::VariantAccess<'de> for Deserializer<'this, 'scope, '_> {\n    type Error = Error<'scope>;\n\n    fn deserialize_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(self)\n    }\n}\n\n/// Used by an `ArrayVisitor` to deserialize every element of a JS array\n/// to a SATS array.\nstruct ArrayAccess<'this, 'scope, 'isolate, T> {\n    common: DeserializerCommon<'this, 'scope, 'isolate>,\n    arr: Local<'scope, Array>,\n    seeds: RepeatN<T>,\n    index: u32,\n}\n\nimpl<'de, 'this, 'scope, 'isolate, T> ArrayAccess<'this, 'scope, 'isolate, T>\nwhere\n    T: DeserializeSeed<'de> + Clone,\n{\n    fn new(arr: Local<'scope, Array>, common: DeserializerCommon<'this, 'scope, 'isolate>, seed: T) -> Self {\n        Self {\n            arr,\n            common,\n            seeds: repeat_n(seed, arr.length() as usize),\n            index: 0,\n        }\n    }\n\n    fn next_elem<'a>(&'a mut self) -> Option<Result<(T, Deserializer<'a, 'scope, 'isolate>), Error<'scope>>> {\n        self.seeds.next().map(move |seed| {\n            // Extract the array element.\n            let input = self\n                .arr\n                .get_index(self.common.scope, self.index)\n                .ok_or_else(exception_already_thrown)?;\n\n            // Make the deserializer.\n            let common = self.common.reborrow();\n            let de = Deserializer { common, input };\n\n            self.index += 1;\n            Ok((seed, de))\n        })\n    }\n}\n\nimpl<'de, 'scope: 'de, T: DeserializeSeed<'de> + Clone> de::ArrayAccess<'de> for ArrayAccess<'_, 'scope, '_, T> {\n    type Element = T::Output;\n    type Error = Error<'scope>;\n\n    fn next_element(&mut self) -> Result<Option<Self::Element>, Self::Error> {\n        self.next_elem()\n            .map(|res| res.and_then(|(seed, de)| seed.deserialize(de)))\n            .transpose()\n    }\n\n    fn validate_next_element(&mut self) -> Result<Option<()>, Self::Error> {\n        self.next_elem()\n            .map(|res| res.and_then(|(seed, de)| seed.validate(de)))\n            .transpose()\n    }\n\n    fn size_hint(&self) -> Option<usize> {\n        Some(self.seeds.len())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/error.rs",
    "content": "//! Utilities for error handling when dealing with V8.\n\nuse super::serialize_to_js;\nuse super::string::IntoJsString;\nuse crate::error::NodesError;\nuse crate::{\n    database_logger::{BacktraceFrame, BacktraceProvider, LogLevel, ModuleBacktrace, Record},\n    host::instance_env::InstanceEnv,\n    replica_context::ReplicaContext,\n};\nuse core::fmt;\nuse spacetimedb_data_structures::map::IntMap;\nuse spacetimedb_primitives::errno;\nuse spacetimedb_sats::Serialize;\nuse std::num::NonZero;\nuse v8::{tc_scope, Exception, HandleScope, Local, PinScope, PinnedRef, StackFrame, StackTrace, TryCatch, Value};\n\n/// The result of trying to convert a [`Value`] in scope `'scope` to some type `T`.\npub(super) type ValueResult<'scope, T> = Result<T, ExceptionValue<'scope>>;\n\n/// A JS exception value.\n///\n/// Newtyped for additional type safety and to track JS exceptions in the type system.\n#[derive(Debug)]\npub(super) struct ExceptionValue<'scope>(pub(super) Local<'scope, Value>);\n\n/// Error types that can convert into JS exception values.\npub(super) trait IntoException<'scope> {\n    /// Converts `self` into a JS exception value.\n    fn into_exception(self, scope: &PinScope<'scope, '_>) -> ExceptionValue<'scope>;\n}\n\nimpl<'scope> IntoException<'scope> for ExceptionValue<'scope> {\n    fn into_exception(self, _: &PinScope<'scope, '_>) -> ExceptionValue<'scope> {\n        self\n    }\n}\n\n/// A type converting into a JS `TypeError` exception.\n#[derive(Copy, Clone)]\npub struct TypeError<M>(pub M);\n\nimpl<'scope, M: IntoJsString> IntoException<'scope> for TypeError<M> {\n    fn into_exception(self, scope: &PinScope<'scope, '_>) -> ExceptionValue<'scope> {\n        match self.0.into_string(scope) {\n            Ok(msg) => ExceptionValue(Exception::type_error(scope, msg)),\n            Err(err) => err.into_range_error().into_exception(scope),\n        }\n    }\n}\n\n/// A type converting into a JS `RangeError` exception.\n#[derive(Copy, Clone)]\npub struct RangeError<M>(pub M);\n\nimpl<'scope, M: IntoJsString> IntoException<'scope> for RangeError<M> {\n    fn into_exception(self, scope: &PinScope<'scope, '_>) -> ExceptionValue<'scope> {\n        match self.0.into_string(scope) {\n            Ok(msg) => ExceptionValue(Exception::range_error(scope, msg)),\n            // This is not an infinite recursion.\n            // The `r: RangeError<String>` that `StringTooLongError` produces\n            // will always enter the branch above, as `r.0` is shorter than the maximum allowed.\n            Err(err) => err.into_range_error().into_exception(scope),\n        }\n    }\n}\n\n/// A non-JS string couldn't convert to JS as it was to long.\n#[derive(Debug)]\npub(super) struct StringTooLongError {\n    /// The length of the string that was too long (`len >` [`v8::String::MAX_LENGTH`]).\n    pub(super) len: usize,\n    /// A prefix of the string for the purpose of rendering an exception that aids the module dev.\n    pub(super) prefix: String,\n}\n\nimpl StringTooLongError {\n    /// Returns a new error that keeps a prefix of `string` and records its length.\n    pub(super) fn new(string: &str) -> Self {\n        let len = string.len();\n        let prefix = string[0..16.max(len)].to_owned();\n        Self { len, prefix }\n    }\n\n    /// Converts the error to a [`RangeError<String>`].\n    pub(super) fn into_range_error(self) -> RangeError<String> {\n        let Self { len, prefix } = self;\n        RangeError(format!(\n            r#\"The string \"`{prefix}..`\" of `{len}` bytes is too long for JS\"#\n        ))\n    }\n}\n\n/// A non-JS array couldn't convert to JS as it was to long.\n#[derive(Debug)]\npub(super) struct ArrayTooLongError {\n    /// The length of the array that was too long (`len >` [`i32::MAX`]).\n    pub(super) len: usize,\n}\n\nimpl ArrayTooLongError {\n    /// Converts the error to a [`RangeError<String>`].\n    pub(super) fn into_range_error(self) -> RangeError<String> {\n        let Self { len } = self;\n        RangeError(format!(\"`{len}` elements are too many for a JS array\"))\n    }\n}\n\n/// A catchable termination error thrown in callbacks to indicate a host error.\n#[derive(Serialize)]\npub(super) struct TerminationError {\n    __terminated__: String,\n}\n\nimpl TerminationError {\n    /// Converts [`anyhow::Error`] to a termination error.\n    pub(super) fn from_error<'scope>(\n        scope: &PinScope<'scope, '_>,\n        error: &anyhow::Error,\n    ) -> ExcResult<ExceptionValue<'scope>> {\n        let __terminated__ = format!(\"{error}\");\n        let error = Self { __terminated__ };\n        serialize_to_js(scope, &error).map(ExceptionValue)\n    }\n}\n\n/// Collapses `res` where the `Ok(x)` where `x` is throwable.\npub(super) fn collapse_exc_thrown<'scope>(\n    scope: &PinScope<'scope, '_>,\n    res: ExcResult<impl Throwable<'scope>>,\n) -> ExceptionThrown {\n    let (Ok(thrown) | Err(thrown)) = res.map(|ev| ev.throw(scope));\n    thrown\n}\n\n/// Either an exception, already thrown, or [`NodesError`] arising from [`InstanceEnv`].\n#[derive(derive_more::From)]\npub(super) enum SysCallError {\n    NoEnv,\n    Errno(NonZero<u16>),\n    /// Only occurs in the v2 ABI.\n    OutOfBounds,\n    Error(NodesError),\n    Exception(ExceptionThrown),\n}\n\nimpl SysCallError {\n    pub const NO_SUCH_ITER: Self = Self::Errno(errno::NO_SUCH_ITER);\n    pub const NO_SUCH_CONSOLE_TIMER: Self = Self::Errno(errno::NO_SUCH_CONSOLE_TIMER);\n}\n\n/// An out-of-bounds syscall error.\npub const OOB: SysCallError = SysCallError::OutOfBounds;\n\n/// A result where the error is a [`SysCallError`].\npub type SysCallResult<T> = Result<T, SysCallError>;\n\n/// A flag set in [`throw_nodes_error`].\n/// The flag should be checked in every module -> host ABI.\n/// If the flag is set, the call is prevented.\nstruct TerminationFlag;\n\n/// Terminate execution immediately due to the given host error.\npub(super) fn terminate_execution<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    err: &anyhow::Error,\n) -> ExcResult<ExceptionValue<'scope>> {\n    // Terminate execution ASAP and throw a catchable exception (`TerminationError`).\n    // Unfortunately, JS execution won't be terminated once the callback returns,\n    // so we set a slot that all callbacks immediately check\n    // to ensure that the module won't be able to do anything to the host\n    // while it's being terminated (eventually).\n    scope.terminate_execution();\n    scope.set_slot(TerminationFlag);\n    TerminationError::from_error(scope, err)\n}\n\n/// Checks the termination flag and throws a `TerminationError` if set.\n///\n/// Returns whether the flag was set.\npub(super) fn throw_if_terminated(scope: &PinScope<'_, '_>) -> bool {\n    // If the flag was set in `throw_nodes_error`,\n    // we need to block all module -> host ABI calls.\n    let set = scope.get_slot::<TerminationFlag>().is_some();\n    if set {\n        let err = anyhow::anyhow!(\"execution is being terminated\");\n        if let Ok(exception) = TerminationError::from_error(scope, &err) {\n            exception.throw(scope);\n        }\n    }\n\n    set\n}\n\n/// A catchable error code thrown in callbacks\n/// to indicate that a buffer was too small and the minimum size required.\n#[derive(Serialize)]\npub(super) struct BufferTooSmall {\n    __buffer_too_small__: u32,\n}\n\nimpl BufferTooSmall {\n    /// Create a code error from a code.\n    pub(super) fn from_requirement<'scope>(\n        scope: &PinScope<'scope, '_>,\n        __buffer_too_small__: u32,\n    ) -> ExcResult<ExceptionValue<'scope>> {\n        let error = Self { __buffer_too_small__ };\n        serialize_to_js(scope, &error).map(ExceptionValue)\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ExceptionThrown {\n    _priv: (),\n}\n\nimpl ExceptionThrown {\n    /// Turns a caught JS exception in `scope` into a [`JSError`].\n    pub(crate) fn into_error(self, scope: &mut PinTryCatch) -> JsError {\n        JsError::from_caught(scope)\n    }\n}\n\n/// A result where the error indicates that an exception has already been thrown in V8.\npub(crate) type ExcResult<T> = Result<T, ExceptionThrown>;\n\n/// Indicates that the JS side had thrown an exception.\npub(super) fn exception_already_thrown() -> ExceptionThrown {\n    ExceptionThrown { _priv: () }\n}\n\n/// Types that can be thrown as a V8 exception.\npub(super) trait Throwable<'scope> {\n    /// Throw `self` into the V8 engine as an exception.\n    ///\n    /// If an exception has already been thrown,\n    /// [`ExceptionThrown`] can be returned directly.\n    fn throw(self, scope: &PinScope<'scope, '_>) -> ExceptionThrown;\n}\n\nimpl<'scope, T: IntoException<'scope>> Throwable<'scope> for T {\n    fn throw(self, scope: &PinScope<'scope, '_>) -> ExceptionThrown {\n        let ExceptionValue(exception) = self.into_exception(scope);\n        scope.throw_exception(exception);\n        exception_already_thrown()\n    }\n}\n\n/// Either an error outside V8 JS execution, or an exception inside.\n#[derive(Debug)]\npub(super) enum ErrorOrException<Exc> {\n    Err(anyhow::Error),\n    Exception(Exc),\n}\n\nimpl<Exc> ErrorOrException<Exc> {\n    pub(super) fn map_exception<Exc2>(self, f: impl FnOnce(Exc) -> Exc2) -> ErrorOrException<Exc2> {\n        match self {\n            ErrorOrException::Err(e) => ErrorOrException::Err(e),\n            ErrorOrException::Exception(exc) => ErrorOrException::Exception(f(exc)),\n        }\n    }\n}\n\nimpl<E> From<anyhow::Error> for ErrorOrException<E> {\n    fn from(e: anyhow::Error) -> Self {\n        Self::Err(e)\n    }\n}\n\nimpl From<ExceptionThrown> for ErrorOrException<ExceptionThrown> {\n    fn from(e: ExceptionThrown) -> Self {\n        Self::Exception(e)\n    }\n}\n\nimpl From<ErrorOrException<JsError>> for anyhow::Error {\n    fn from(err: ErrorOrException<JsError>) -> Self {\n        match err {\n            ErrorOrException::Err(e) => e,\n            ErrorOrException::Exception(e) => e.into(),\n        }\n    }\n}\n\n/// A JS exception turned into an error.\n#[derive(thiserror::Error, Debug)]\npub(super) struct JsError {\n    msg: String,\n    trace: JsStackTrace,\n}\n\nimpl fmt::Display for JsError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let Self { msg, trace } = self;\n        write!(f, \"{msg}\")?;\n        if !trace.frames.is_empty() {\n            write!(f, \"\\n{trace}\")?;\n        }\n        Ok(())\n    }\n}\n\n/// A V8 stack trace that is independent of a `'scope`.\n#[derive(Debug, Default, Clone)]\npub(super) struct JsStackTrace {\n    frames: Box<[JsStackTraceFrame]>,\n}\n\nimpl JsStackTrace {\n    /// Converts a V8 [`StackTrace`] into one independent of `'scope`.\n    pub(super) fn from_trace<'scope>(scope: &mut PinScope<'scope, '_>, trace: Local<'scope, StackTrace>) -> Self {\n        let frames = (0..trace.get_frame_count())\n            .map(|index| {\n                let frame = trace.get_frame(scope, index).unwrap();\n                JsStackTraceFrame::from_frame(scope, frame)\n            })\n            // A call frame with this name is the dividing line between user stack frames\n            // and module-bindings frames (e.g. `__call_reducer__`). See `callUserFunction`\n            // in `src/server/runtime.ts` in the Typescript SDK.\n            .take_while(|frame| frame.fn_name() != \"__spacetimedb_end_short_backtrace\")\n            .collect::<Box<[_]>>();\n        Self { frames }\n    }\n\n    /// Construct a backtrace from `scope`.\n    pub(super) fn from_current_stack_trace(scope: &mut PinScope<'_, '_>) -> ExcResult<Self> {\n        let trace = StackTrace::current_stack_trace(scope, 1024).ok_or_else(exception_already_thrown)?;\n        Ok(Self::from_trace(scope, trace))\n    }\n}\n\nimpl fmt::Display for JsStackTrace {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        for (i, frame) in self.frames.iter().enumerate() {\n            if i > 0 {\n                writeln!(f)?\n            }\n            write!(f, \"\\t{frame}\")?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl BacktraceProvider for JsStackTrace {\n    fn capture(&self) -> Box<dyn ModuleBacktrace> {\n        let trace = self\n            .frames\n            .iter()\n            .map(|f| {\n                (\n                    format!(\"{}:{}:{}\", f.script_name(), f.line, f.column),\n                    f.fn_name().to_owned(),\n                )\n            })\n            .collect();\n        Box::new(JsBacktrace { trace })\n    }\n}\n\n/// A rendered backtrace for a JS exception.\nstruct JsBacktrace {\n    trace: Vec<(String, String)>,\n}\n\nimpl ModuleBacktrace for JsBacktrace {\n    fn frames(&self) -> Vec<BacktraceFrame<'_>> {\n        self.trace\n            .iter()\n            .map(|(module_name, func_name)| BacktraceFrame {\n                module_name: Some(module_name),\n                func_name: Some(func_name),\n            })\n            .collect()\n    }\n}\n\n/// A V8 stack trace frame that is independent of a `'scope`.\n#[derive(Debug, Clone)]\npub(super) struct JsStackTraceFrame {\n    line: usize,\n    column: usize,\n    #[allow(dead_code)]\n    script_id: usize,\n    script_name: Option<String>,\n    fn_name: Option<String>,\n    is_eval: bool,\n    is_ctor: bool,\n    is_wasm: bool,\n    is_user_js: bool,\n}\n\nimpl JsStackTraceFrame {\n    /// Converts a V8 [`StackFrame`] into one independent of `'scope`.\n    fn from_frame<'scope>(scope: &mut PinScope<'scope, '_>, frame: Local<'scope, StackFrame>) -> Self {\n        let script_id = frame.get_script_id();\n        let mut line = frame.get_line_number();\n        let mut column = frame.get_column();\n        let mut script_name = None;\n        let fn_name = frame.get_function_name(scope).map(|s| s.to_rust_string_lossy(scope));\n\n        let sourcemap = scope.get_slot().and_then(|SourceMaps(maps)| maps.get(&script_id));\n\n        let sourcemap = if let Some(sm) = sourcemap {\n            sm.as_ref()\n        } else {\n            SourceMaps::parse_and_insert(scope, frame)\n        };\n\n        // sourcemap uses 0-based line/column numbers, while v8 uses 1-based\n        if let Some(token) = sourcemap.and_then(|sm| sm.lookup_token(line as u32 - 1, column as u32 - 1)) {\n            line = token.get_src_line() as usize + 1;\n            column = token.get_src_col() as usize + 1;\n            if let Some(file) = token.get_source() {\n                script_name = Some(file.to_owned())\n            }\n\n            // If we ever want to support de-minifying function names, uncomment this.\n            // The process of obtaining the original name of a function given a token\n            // in that function is imperfect and could return an incorrect name for an\n            // unminified identifier. So until we need it, turn it off.\n            //\n            // if let Some((sv, fn_name)) = Option::zip(token.get_source_view(), fn_name.as_mut()) {\n            //     if let Some(new_name) = sv.get_original_function_name(token, fn_name) {\n            //         new_name.clone_into(fn_name)\n            //     }\n            // }\n        }\n\n        let script_name = script_name.or_else(|| {\n            frame\n                .get_script_name_or_source_url(scope)\n                .map(|s| s.to_rust_string_lossy(scope))\n        });\n\n        Self {\n            line,\n            column,\n            script_id,\n            script_name,\n            fn_name,\n            is_eval: frame.is_eval(),\n            is_ctor: frame.is_constructor(),\n            is_wasm: frame.is_wasm(),\n            is_user_js: frame.is_user_javascript(),\n        }\n    }\n\n    /// Returns the name of the function that was called.\n    fn fn_name(&self) -> &str {\n        self.fn_name.as_deref().unwrap_or(\"<anonymous>\")\n    }\n\n    /// Returns the name of the script where the function resides.\n    fn script_name(&self) -> &str {\n        self.script_name.as_deref().unwrap_or(\"<unknown location>\")\n    }\n}\n\nimpl fmt::Display for JsStackTraceFrame {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let fn_name = self.fn_name();\n        let script_name = self.script_name();\n\n        // This isn't exactly the same format as chrome uses,\n        // but it's close enough for now.\n        // TODO(v8): make it more like chrome in the future.\n        f.write_fmt(format_args!(\n            \"at {} ({}:{}:{})\",\n            fn_name, script_name, &self.line, &self.column\n        ))?;\n\n        if self.is_ctor {\n            f.write_str(\" (constructor)\")?;\n        }\n\n        if self.is_eval {\n            f.write_str(\" (eval)\")?;\n        }\n\n        if self.is_wasm {\n            f.write_str(\" (wasm)\")?;\n        }\n\n        if !self.is_user_js {\n            f.write_str(\" (native)\")?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Mappings from a script id to its source map.\n///\n/// An entry with `None` indicates that there is no sourcemap for that script.\n#[derive(Default)]\nstruct SourceMaps(IntMap<usize, Option<sourcemap::SourceMap>>);\n\nimpl SourceMaps {\n    /// Extract the sourcemap from the given frame, if it exists, and insert\n    /// it into the `SourceMaps` in the isolate.\n    fn parse_and_insert<'a>(\n        scope: &'a mut PinScope<'_, '_>,\n        frame: Local<'_, StackFrame>,\n    ) -> Option<&'a sourcemap::SourceMap> {\n        let sourcemap = frame.get_script_source_mapping_url(scope).and_then(|source_map_url| {\n            let source_map_url = source_map_url.to_rust_string_lossy(scope);\n            if let Ok(sourcemap::DecodedMap::Regular(sourcemap)) = sourcemap::decode_data_url(&source_map_url) {\n                Some(sourcemap)\n            } else {\n                None\n            }\n        });\n        let SourceMaps(maps) = get_or_insert_slot(scope, SourceMaps::default);\n        maps.entry(frame.get_script_id()).insert(sourcemap).into_mut().as_ref()\n    }\n}\n\n/// Get the slot `T` from `isolate`, or create it with the value `default()` if it doesn't exist.\nfn get_or_insert_slot<T: 'static>(isolate: &mut v8::Isolate, default: impl FnOnce() -> T) -> &mut T {\n    if isolate.get_slot::<T>().is_none() {\n        isolate.set_slot(default());\n    }\n    isolate.get_slot_mut().unwrap()\n}\n\nimpl JsError {\n    /// Turns a caught JS exception in `scope` into a [`JSError`].\n    fn from_caught(scope: &mut PinTryCatch<'_, '_, '_, '_>) -> Self {\n        match scope.message() {\n            Some(message) => Self {\n                trace: message\n                    .get_stack_trace(scope)\n                    .map(|trace| JsStackTrace::from_trace(scope, trace))\n                    .unwrap_or_default(),\n                msg: message.get(scope).to_rust_string_lossy(scope),\n            },\n            None => Self {\n                trace: JsStackTrace::default(),\n                msg: \"unknown error\".to_owned(),\n            },\n        }\n    }\n}\n\npub(super) fn log_traceback(replica_ctx: &ReplicaContext, func_type: &str, func: &str, e: &anyhow::Error) {\n    log::info!(\"{func_type} \\\"{func}\\\" runtime error: {e:}\");\n    if let Some(js_err) = e.downcast_ref::<JsError>() {\n        log::info!(\"JS error: {js_err}\",);\n\n        // Also log to module logs.\n        let first_frame = js_err.trace.frames.first();\n        let filename = first_frame.map(|f| f.script_name());\n        let line_number = first_frame.map(|f| f.line as u32);\n        let message = &js_err.msg;\n        let record = Record {\n            ts: InstanceEnv::now_for_logging(),\n            target: None,\n            filename,\n            line_number,\n            function: Some(func),\n            message,\n        };\n        replica_ctx.logger.write(LogLevel::Panic, &record, &js_err.trace);\n    }\n}\n\n/// Run `body` within a try-catch context and capture any JS exception thrown as a [`JsError`].\npub(super) fn catch_exception<'scope, T>(\n    scope: &mut PinScope<'scope, '_>,\n    body: impl FnOnce(&mut PinTryCatch<'scope, '_, '_, '_>) -> Result<T, ErrorOrException<ExceptionThrown>>,\n) -> Result<T, ErrorOrException<JsError>> {\n    tc_scope!(scope, scope);\n    body(scope).map_err(|e| e.map_exception(|exc| exc.into_error(scope)))\n}\n\npub(super) type PinTryCatch<'scope, 'iso, 'x, 's> = PinnedRef<'x, TryCatch<'s, 'scope, HandleScope<'iso>>>;\n"
  },
  {
    "path": "crates/core/src/host/v8/from_value.rs",
    "content": "use super::error::{ExceptionValue, IntoException as _, TypeError, ValueResult};\nuse bytemuck::{AnyBitPattern, NoUninit};\nuse spacetimedb_sats::{i256, u256};\nuse v8::{BigInt, Boolean, Int32, Local, Number, PinScope, Uint32, Value};\n\n/// Types that a v8 [`Value`] can be converted into.\npub(super) trait FromValue: Sized {\n    /// Converts `val` in `scope` to `Self` if possible.\n    fn from_value<'scope>(val: Local<'_, Value>, scope: &PinScope<'scope, '_>) -> ValueResult<'scope, Self>;\n}\n\n/// Provides a [`FromValue`] implementation.\nmacro_rules! impl_from_value {\n    ($ty:ty, ($val:ident, $scope:ident) => $logic:expr) => {\n        impl FromValue for $ty {\n            fn from_value<'scope>($val: Local<'_, Value>, $scope: &PinScope<'scope, '_>) -> ValueResult<'scope, Self> {\n                $logic\n            }\n        }\n    };\n}\n\n/// Tries to cast `Value` into `T` or raises a JS exception as a returned `Err` value.\npub(super) fn try_cast<'scope_a, 'scope_b, T>(\n    scope: &PinScope<'scope_a, '_>,\n    val: Local<'scope_b, Value>,\n    on_err: impl FnOnce(&str) -> String,\n) -> ValueResult<'scope_a, Local<'scope_b, T>>\nwhere\n    Local<'scope_b, T>: TryFrom<Local<'scope_b, Value>>,\n{\n    val.try_cast::<T>()\n        .map_err(|_| TypeError(on_err(val.type_repr())).into_exception(scope))\n}\n\n/// Tries to cast `Value` into `T` or raises a JS exception as a returned `Err` value.\nmacro_rules! cast {\n    ($scope:expr, $val:expr, $js_ty:ty, $expected:literal $(, $args:expr)* $(,)?) => {{\n        $crate::host::v8::from_value::try_cast::<$js_ty>($scope, $val, |got| format!(concat!(\"Expected \", $expected, \", got {__got}\"), $($args,)* __got = got))\n    }};\n}\npub(super) use cast;\n\n/// Returns a JS exception value indicating that a value overflowed\n/// when converting to the type `rust_ty`.\nfn value_overflowed<'scope>(rust_ty: &str, scope: &PinScope<'scope, '_>) -> ExceptionValue<'scope> {\n    TypeError(format!(\"Value overflowed `{rust_ty}`\")).into_exception(scope)\n}\n\n/// Returns a JS exception value indicating that a value underflowed\n/// when converting to the type `rust_ty`.\nfn value_underflowed<'scope>(rust_ty: &str, scope: &PinScope<'scope, '_>) -> ExceptionValue<'scope> {\n    TypeError(format!(\"Value underflowed `{rust_ty}`\")).into_exception(scope)\n}\n\n// `FromValue for bool`.\nimpl_from_value!(bool, (val, scope) => cast!(scope, val, Boolean, \"boolean\").map(|b| b.is_true()));\n\n// `FromValue for u8, u16, u32, i8, i16, i32`.\nmacro_rules! int32_from_value {\n    ($js_ty:ty, $rust_ty:ty) => {\n        impl_from_value!($rust_ty, (val, scope) => {\n            let num = cast!(scope, val, $js_ty, \"number for `{}`\", stringify!($rust_ty))?;\n            num.value().try_into().map_err(|_| value_overflowed(stringify!($rust_ty), scope))\n        });\n    }\n}\nint32_from_value!(Uint32, u8);\nint32_from_value!(Uint32, u16);\nint32_from_value!(Uint32, u32);\nint32_from_value!(Int32, i8);\nint32_from_value!(Int32, i16);\nint32_from_value!(Int32, i32);\n\n// `FromValue for f32, f64`.\n//\n// Note that, as per the rust-reference,\n// - \"Casting from an f64 to an f32 will produce the closest possible f32\"\n// https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.as.numeric.float-narrowing\nmacro_rules! float_from_value {\n    ($rust_ty:ty) => {\n        impl_from_value!($rust_ty, (val, scope) => {\n            cast!(scope, val, Number, \"number for `{}`\", stringify!($rust_ty)).map(|n| n.value() as _)\n        });\n    }\n}\nfloat_from_value!(f32);\nfloat_from_value!(f64);\n\n// `FromValue for u64, i64`.\nmacro_rules! int64_from_value {\n    ($rust_ty:ty, $conv_method: ident) => {\n        impl_from_value!($rust_ty, (val, scope) => {\n            let rust_ty = stringify!($rust_ty);\n            let bigint = cast!(scope, val, BigInt, \"bigint for `{}`\", rust_ty)?;\n            let (val, ok) = bigint.$conv_method();\n            ok.then_some(val).ok_or_else(|| value_overflowed(rust_ty, scope))\n        });\n    }\n}\nint64_from_value!(u64, u64_value);\nint64_from_value!(i64, i64_value);\n\n/// Converts `bigint` into its signnedness and its list of bytes in little-endian,\n/// or errors on overflow or unwanted signedness.\n///\n/// Parameters:\n/// - `N` are the number of bytes to accept at most.\n/// - `W = N / 8` are the number of words to accept at most.\n/// - `UNSIGNED` is `true` if only unsigned integers are accepted.\n/// - `rust_ty` is the target type as a string, for errors.\n/// - `scope` for any JS exceptions that need to be raised.\n/// - `bigint` is the integer to convert.\nfn bigint_to_bytes<'scope, const N: usize, const W: usize, const UNSIGNED: bool>(\n    rust_ty: &str,\n    scope: &PinScope<'scope, '_>,\n    bigint: &BigInt,\n) -> ValueResult<'scope, (bool, [u8; N])>\nwhere\n    [[u8; 8]; W]: NoUninit,\n    [u8; N]: AnyBitPattern,\n{\n    // Read the words.\n    let mut words = [0u64; W];\n    let (sign, _) = bigint.to_words_array(&mut words);\n\n    if bigint.word_count() > W {\n        // There's an under-/over-flow if the caller cannot handle that many words.\n        return Err(if sign {\n            value_underflowed(rust_ty, scope)\n        } else {\n            value_overflowed(rust_ty, scope)\n        });\n    }\n\n    if sign && UNSIGNED {\n        // There's an overflow if the caller cannot accept negative numbers.\n        return Err(value_overflowed(rust_ty, scope));\n    }\n\n    // convert the words to little-endian bytes.\n    let bytes = bytemuck::must_cast(words.map(|w| w.to_le_bytes()));\n    Ok((sign, bytes))\n}\n\n// `FromValue for u128, u256`.\nmacro_rules! unsigned_bigint_from_value {\n    ($rust_ty:ty, $bytes:literal, $words:literal) => {\n        impl_from_value!($rust_ty, (val, scope) => {\n            let rust_ty = stringify!($rust_ty);\n            let bigint = cast!(scope, val, v8::BigInt, \"bigint for `{}`\", rust_ty)?;\n            if let (val, true) = bigint.u64_value() {\n                // Fast path.\n                return Ok(val.into());\n            }\n            let (_, bytes) = bigint_to_bytes::<$bytes, $words, true>(rust_ty, scope, &bigint)?;\n            Ok(Self::from_le_bytes(bytes))\n        });\n    };\n}\nunsigned_bigint_from_value!(u128, 16, 2);\nunsigned_bigint_from_value!(u256, 32, 4);\n\n// `FromValue for i128, i256`.\nmacro_rules! signed_bigint_from_value {\n    ($rust_ty:ty, $bytes:literal, $words:literal) => {\n        impl_from_value!($rust_ty, (val, scope) => {\n            let rust_ty = stringify!($rust_ty);\n            let bigint = cast!(scope, val, v8::BigInt, \"bigint for `{}`\", rust_ty)?;\n            if let (val, true) = bigint.i64_value() {\n                // Fast path.\n                return Ok(val.into());\n            }\n            let (sign, bytes) = bigint_to_bytes::<$bytes, $words, false>(rust_ty, scope, &bigint)?;\n            let x = Self::from_le_bytes(bytes);\n            Ok(if sign {\n                // A negative number, but we have a positive number `x`, so we want `-x`.\n                // If that's not possible, and as we know there's no underflow, we have `MIN`.\n                x.checked_neg().unwrap_or(Self::MIN)\n            } else {\n                x\n            })\n        });\n    };\n}\nsigned_bigint_from_value!(i128, 16, 2);\nsigned_bigint_from_value!(i256, 32, 4);\n"
  },
  {
    "path": "crates/core/src/host/v8/mod.rs",
    "content": "use self::budget::energy_from_elapsed;\nuse self::error::{\n    catch_exception, exception_already_thrown, log_traceback, ErrorOrException, ExcResult, ExceptionThrown,\n    PinTryCatch, Throwable,\n};\nuse self::ser::serialize_to_js;\nuse self::string::{str_from_ident, IntoJsString};\nuse self::syscall::{\n    call_call_procedure, call_call_reducer, call_call_view, call_call_view_anon, call_describe_module, get_hooks,\n    process_thrown_exception, resolve_sys_module, FnRet, HookFunctions,\n};\nuse super::module_common::{build_common_module_from_raw, run_describer, ModuleCommon};\nuse super::module_host::{CallProcedureParams, CallReducerParams, ModuleInfo, ModuleWithInstance};\nuse super::UpdateDatabaseResult;\nuse crate::client::ClientActorId;\nuse crate::host::host_controller::CallProcedureReturn;\nuse crate::host::instance_env::{ChunkPool, InstanceEnv, TxSlot};\nuse crate::host::module_host::{\n    call_identity_connected, init_database, ClientConnectedError, ViewCommand, ViewCommandResult,\n};\nuse crate::host::scheduler::{CallScheduledFunctionResult, ScheduledFunctionParams};\nuse crate::host::wasm_common::instrumentation::CallTimes;\nuse crate::host::wasm_common::module_host_actor::{\n    AnonymousViewOp, DescribeError, ExecutionError, ExecutionResult, ExecutionStats, ExecutionTimings, InstanceCommon,\n    InstanceOp, ProcedureExecuteResult, ProcedureOp, ReducerExecuteResult, ReducerOp, ViewExecuteResult, ViewOp,\n    WasmInstance,\n};\nuse crate::host::wasm_common::{RowIters, TimingSpanSet};\nuse crate::host::{ModuleHost, ReducerCallError, ReducerCallResult, Scheduler};\nuse crate::module_host_context::ModuleCreationContext;\nuse crate::replica_context::ReplicaContext;\nuse crate::subscription::module_subscription_manager::TransactionOffset;\nuse crate::util::jobs::{AllocatedJobCore, CorePinner, LoadBalanceOnDropGuard};\nuse core::any::type_name;\nuse core::str;\nuse enum_as_inner::EnumAsInner;\nuse futures::future::LocalBoxFuture;\nuse futures::FutureExt;\nuse itertools::Either;\nuse spacetimedb_auth::identity::ConnectionAuthCtx;\nuse spacetimedb_client_api_messages::energy::FunctionBudget;\nuse spacetimedb_datastore::locking_tx_datastore::FuncCallType;\nuse spacetimedb_datastore::traits::Program;\nuse spacetimedb_lib::{ConnectionId, Identity, RawModuleDef, Timestamp};\nuse spacetimedb_schema::auto_migrate::MigrationPolicy;\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_table::static_assert_size;\nuse std::panic::AssertUnwindSafe;\nuse std::sync::{Arc, LazyLock};\nuse std::time::Instant;\nuse tokio::sync::oneshot;\nuse tracing::Instrument;\nuse v8::script_compiler::{compile_module, Source};\nuse v8::{\n    scope_with_context, ArrayBuffer, Context, Function, Isolate, Local, MapFnTo, OwnedIsolate, PinScope,\n    ResolveModuleCallback, ScriptOrigin, Value,\n};\n\nmod budget;\nmod builtins;\nmod de;\nmod error;\nmod from_value;\nmod ser;\nmod string;\nmod syscall;\nmod to_value;\nmod util;\n\n/// The V8 runtime, for modules written in e.g., JS or TypeScript.\n#[derive(Default)]\npub struct V8Runtime {\n    _priv: (),\n}\n\nimpl V8Runtime {\n    pub async fn make_actor(\n        &self,\n        mcc: ModuleCreationContext,\n        program_bytes: &[u8],\n        core: AllocatedJobCore,\n    ) -> anyhow::Result<ModuleWithInstance> {\n        V8_RUNTIME_GLOBAL.make_actor(mcc, program_bytes, core).await\n    }\n}\n\n#[cfg(test)]\nimpl V8Runtime {\n    fn init_for_test() {\n        LazyLock::force(&V8_RUNTIME_GLOBAL);\n    }\n}\n\nstatic V8_RUNTIME_GLOBAL: LazyLock<V8RuntimeInner> = LazyLock::new(V8RuntimeInner::init);\n\n/// The actual V8 runtime, with initialization of V8.\nstruct V8RuntimeInner {\n    _priv: (),\n}\n\nimpl V8RuntimeInner {\n    /// Initializes the V8 platform and engine.\n    ///\n    /// Should only be called once but it isn't unsound to call it more times.\n    fn init() -> Self {\n        // If the number in the name of this function is changed, update the version\n        // of the `deno_core_icudata` dep to match the number in the function name.\n        v8::icu::set_common_data_77(deno_core_icudata::ICU_DATA).ok();\n        // Set a default locale for functions like `toLocaleString()`.\n        // en-001 is \"International English\". <https://www.ctrl.blog/entry/en-001.html>\n        v8::icu::set_default_locale(\"en-001\");\n\n        // We don't want idle tasks nor background worker tasks,\n        // as we intend to run on a single core.\n        // Per the docs, `new_single_threaded_default_platform` requires\n        // that we pass `--single-threaded`.\n        let mut flags = \"--single-threaded\".to_owned();\n        if let Ok(env_flags) = std::env::var(\"STDB_V8_FLAGS\") {\n            flags.extend([\" \", &env_flags]);\n        }\n        v8::V8::set_flags_from_string(&flags);\n        let platform = v8::new_single_threaded_default_platform(false).make_shared();\n        // Initialize V8. Internally, this uses a global lock so it's safe that we don't.\n        v8::V8::initialize_platform(platform);\n        v8::V8::initialize();\n\n        Self { _priv: () }\n    }\n\n    async fn make_actor(\n        &self,\n        mcc: ModuleCreationContext,\n        program_bytes: &[u8],\n        core: AllocatedJobCore,\n    ) -> anyhow::Result<ModuleWithInstance> {\n        #![allow(unreachable_code, unused_variables)]\n\n        log::trace!(\n            \"Making new V8 module host actor for database {} with module {}\",\n            mcc.replica_ctx.database_identity,\n            mcc.program_hash,\n        );\n\n        // Convert program to a string.\n        let program: Arc<str> = str::from_utf8(program_bytes)?.into();\n\n        // Validate/create the module and spawn the first instance.\n        let mcc = Either::Right(mcc);\n        let load_balance_guard = Arc::new(core.guard);\n        let core_pinner = core.pinner;\n        let (common, init_inst) =\n            spawn_instance_worker(program.clone(), mcc, load_balance_guard.clone(), core_pinner.clone()).await?;\n        let module = JsModule {\n            common,\n            program,\n            load_balance_guard,\n            core_pinner,\n        };\n\n        Ok(ModuleWithInstance::Js { module, init_inst })\n    }\n}\n\n#[derive(Clone)]\npub struct JsModule {\n    common: ModuleCommon,\n    program: Arc<str>,\n    load_balance_guard: Arc<LoadBalanceOnDropGuard>,\n    core_pinner: CorePinner,\n}\n\nimpl JsModule {\n    pub fn replica_ctx(&self) -> &Arc<ReplicaContext> {\n        self.common.replica_ctx()\n    }\n\n    pub fn scheduler(&self) -> &Scheduler {\n        self.common.scheduler()\n    }\n\n    pub fn info(&self) -> Arc<ModuleInfo> {\n        self.common.info().clone()\n    }\n\n    pub async fn create_instance(&self) -> JsInstance {\n        let program = self.program.clone();\n        let common = self.common.clone();\n        let load_balance_guard = self.load_balance_guard.clone();\n        let core_pinner = self.core_pinner.clone();\n\n        // This has to be done in a blocking context because of `blocking_recv`.\n        let (_, instance) = spawn_instance_worker(program, Either::Left(common), load_balance_guard, core_pinner)\n            .await\n            .expect(\"`spawn_instance_worker` should succeed when passed `ModuleCommon`\");\n        instance\n    }\n}\n\n/// Returns the `JsInstanceEnv` bound to an [`Isolate`], fallibly.\nfn env_on_isolate(isolate: &mut Isolate) -> Option<&mut JsInstanceEnv> {\n    isolate.get_slot_mut()\n}\n\n/// Returns the `JsInstanceEnv` bound to an [`Isolate`], or panic if not set.\nfn env_on_isolate_unwrap(isolate: &mut Isolate) -> &mut JsInstanceEnv {\n    env_on_isolate(isolate).expect(\"there should be a `JsInstanceEnv`\")\n}\n\n/// The environment of a [`JsInstance`].\nstruct JsInstanceEnv {\n    instance_env: InstanceEnv,\n    module_def: Option<Arc<ModuleDef>>,\n\n    /// The slab of `BufferIters` created for this instance.\n    iters: RowIters,\n\n    /// Track time spent in module-defined spans.\n    timing_spans: TimingSpanSet,\n\n    /// Track time spent in all wasm instance env calls (aka syscall time).\n    ///\n    /// Each function, like `insert`, will add the `Duration` spent in it\n    /// to this tracker.\n    call_times: CallTimes,\n\n    /// A pool of unused allocated chunks that can be reused.\n    // TODO(Centril): consider using this pool for `console_timer_start` and `bytes_sink_write`.\n    chunk_pool: ChunkPool,\n}\n\nimpl JsInstanceEnv {\n    /// Returns a new [`JsInstanceEnv`] wrapping `instance_env` with some defaults.\n    fn new(instance_env: InstanceEnv) -> Self {\n        Self {\n            instance_env,\n            module_def: None,\n            call_times: CallTimes::new(),\n            iters: <_>::default(),\n            chunk_pool: <_>::default(),\n            timing_spans: <_>::default(),\n        }\n    }\n\n    /// Signal to this `WasmInstanceEnv` that a reducer call is beginning.\n    ///\n    /// Returns the handle used by reducers to read from `args`\n    /// as well as the handle used to write the error message, if any.\n    fn start_funcall(&mut self, name: Identifier, ts: Timestamp, func_type: FuncCallType) {\n        self.instance_env.start_funcall(name, ts, func_type);\n    }\n\n    /// Returns the name of the most recent reducer to be run in this environment,\n    /// or `None` if no reducer is actively being invoked.\n    fn log_record_function(&self) -> Option<&str> {\n        self.instance_env.log_record_function()\n    }\n\n    /// Returns the name of the most recent reducer to be run in this environment.\n    fn reducer_start(&self) -> Instant {\n        self.instance_env.start_instant\n    }\n\n    /// Signal to this `WasmInstanceEnv` that a reducer call is over.\n    /// This resets all of the state associated to a single reducer call,\n    /// and returns instrumentation records.\n    fn finish_reducer(&mut self) -> ExecutionTimings {\n        let total_duration = self.reducer_start().elapsed();\n\n        // Taking the call times record also resets timings to 0s for the next call.\n        let wasm_instance_env_call_times = self.call_times.take();\n\n        ExecutionTimings {\n            total_duration,\n            wasm_instance_env_call_times,\n        }\n    }\n\n    fn set_module_def(&mut self, module_def: Arc<ModuleDef>) {\n        self.module_def = Some(module_def);\n    }\n\n    fn module_def(&self) -> Option<Arc<ModuleDef>> {\n        self.module_def.clone()\n    }\n}\n\n/// An instance for a [`JsModule`].\n///\n/// The actual work happens in a worker thread,\n/// which the instance communicates with through channels.\n///\n/// When the instance is dropped, the channels will hang up,\n/// which will cause the worker's loop to terminate\n/// and cleanup the isolate and friends.\npub struct JsInstance {\n    request_tx: flume::Sender<JsWorkerRequest>,\n    reply_rx: flume::Receiver<(JsWorkerReply, bool)>,\n    trapped: bool,\n}\n\nimpl JsInstance {\n    pub fn trapped(&self) -> bool {\n        self.trapped\n    }\n\n    /// Send a request to the worker and wait for a reply.\n    async fn send_recv<T>(\n        &mut self,\n        extract: impl FnOnce(JsWorkerReply) -> Result<T, JsWorkerReply>,\n        request: JsWorkerRequest,\n    ) -> T {\n        // Send the request.\n        self.request_tx\n            .send_async(request)\n            .await\n            .expect(\"worker's `request_rx` should be live as `JsInstance::drop` hasn't happened\");\n\n        // Wait for the response.\n        let (reply, trapped) = self\n            .reply_rx\n            .recv_async()\n            .await\n            .expect(\"worker's `reply_tx` should be live as `JsInstance::drop` hasn't happened\");\n\n        self.trapped = trapped;\n\n        match extract(reply) {\n            Err(err) => unreachable!(\"should have received {} but got {err:?}\", type_name::<T>()),\n            Ok(reply) => reply,\n        }\n    }\n\n    /// Run the given function on the worker thread.\n    ///\n    /// This method, unlike the others, does not expect a response on the\n    /// `reply_rx` channel, since the return value `R` could be of any type.\n    pub async fn run_on_thread<F, R>(&mut self, f: F) -> R\n    where\n        F: AsyncFnOnce() -> R + Send + 'static,\n        R: Send + 'static,\n    {\n        let span = tracing::Span::current();\n        let (tx, rx) = oneshot::channel();\n\n        let request = JsWorkerRequest::RunFunction(Box::new(move || {\n            async move {\n                let result = AssertUnwindSafe(f().instrument(span)).catch_unwind().await;\n                if let Err(Err(_panic)) = tx.send(result) {\n                    tracing::warn!(\"uncaught panic on `SingleCoreExecutor`\")\n                }\n            }\n            .boxed_local()\n        }));\n\n        self.request_tx\n            .send_async(request)\n            .await\n            .expect(\"worker's `request_rx` should be live as `JsInstance::drop` hasn't happened\");\n\n        match rx.await.unwrap() {\n            Ok(r) => r,\n            Err(e) => std::panic::resume_unwind(e),\n        }\n    }\n\n    pub async fn update_database(\n        &mut self,\n        program: Program,\n        old_module_info: Arc<ModuleInfo>,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        self.send_recv(\n            JsWorkerReply::into_update_database,\n            JsWorkerRequest::UpdateDatabase {\n                program,\n                old_module_info,\n                policy,\n            },\n        )\n        .await\n    }\n\n    pub async fn call_reducer(&mut self, params: CallReducerParams) -> ReducerCallResult {\n        self.send_recv(\n            JsWorkerReply::into_call_reducer,\n            JsWorkerRequest::CallReducer { params },\n        )\n        .await\n    }\n\n    pub async fn clear_all_clients(&mut self) -> anyhow::Result<()> {\n        self.send_recv(JsWorkerReply::into_clear_all_clients, JsWorkerRequest::ClearAllClients)\n            .await\n    }\n\n    pub async fn call_identity_connected(\n        &mut self,\n        caller_auth: ConnectionAuthCtx,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ClientConnectedError> {\n        self.send_recv(\n            JsWorkerReply::into_call_identity_connected,\n            JsWorkerRequest::CallIdentityConnected(caller_auth, caller_connection_id),\n        )\n        .await\n    }\n\n    pub async fn call_identity_disconnected(\n        &mut self,\n        caller_identity: Identity,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ReducerCallError> {\n        self.send_recv(\n            JsWorkerReply::into_call_identity_disconnected,\n            JsWorkerRequest::CallIdentityDisconnected(caller_identity, caller_connection_id),\n        )\n        .await\n    }\n\n    pub async fn disconnect_client(&mut self, client_id: ClientActorId) -> Result<(), ReducerCallError> {\n        self.send_recv(\n            JsWorkerReply::into_disconnect_client,\n            JsWorkerRequest::DisconnectClient(client_id),\n        )\n        .await\n    }\n\n    pub async fn init_database(&mut self, program: Program) -> anyhow::Result<Option<ReducerCallResult>> {\n        *self\n            .send_recv(\n                JsWorkerReply::into_init_database,\n                JsWorkerRequest::InitDatabase(program),\n            )\n            .await\n    }\n\n    pub async fn call_procedure(&mut self, params: CallProcedureParams) -> CallProcedureReturn {\n        *self\n            .send_recv(\n                JsWorkerReply::into_call_procedure,\n                JsWorkerRequest::CallProcedure { params },\n            )\n            .await\n    }\n\n    pub async fn call_view(&mut self, cmd: ViewCommand) -> ViewCommandResult {\n        *self\n            .send_recv(JsWorkerReply::into_call_view, JsWorkerRequest::CallView { cmd })\n            .await\n    }\n\n    pub(in crate::host) async fn call_scheduled_function(\n        &mut self,\n        params: ScheduledFunctionParams,\n    ) -> CallScheduledFunctionResult {\n        self.send_recv(\n            JsWorkerReply::into_call_scheduled_function,\n            JsWorkerRequest::CallScheduledFunction(params),\n        )\n        .await\n    }\n}\n\n/// A reply from the worker in [`spawn_instance_worker`].\n#[derive(EnumAsInner, Debug)]\nenum JsWorkerReply {\n    UpdateDatabase(anyhow::Result<UpdateDatabaseResult>),\n    CallReducer(ReducerCallResult),\n    CallView(Box<ViewCommandResult>),\n    CallProcedure(Box<CallProcedureReturn>),\n    ClearAllClients(anyhow::Result<()>),\n    CallIdentityConnected(Result<(), ClientConnectedError>),\n    CallIdentityDisconnected(Result<(), ReducerCallError>),\n    DisconnectClient(Result<(), ReducerCallError>),\n    InitDatabase(Box<anyhow::Result<Option<ReducerCallResult>>>),\n    CallScheduledFunction(CallScheduledFunctionResult),\n}\n\nstatic_assert_size!(JsWorkerReply, 48);\n\n/// A request for the worker in [`spawn_instance_worker`].\n// We care about optimizing for `CallReducer` as it happens frequently,\n// so we don't want to box anything in it.\nenum JsWorkerRequest {\n    /// See [`JsInstance::run_on_thread`].\n    ///\n    /// This variant does not expect a [`JsWorkerReply`].\n    RunFunction(Box<dyn FnOnce() -> LocalBoxFuture<'static, ()> + Send>),\n    /// See [`JsInstance::update_database`].\n    UpdateDatabase {\n        program: Program,\n        old_module_info: Arc<ModuleInfo>,\n        policy: MigrationPolicy,\n    },\n    /// See [`JsInstance::call_reducer`].\n    CallReducer { params: CallReducerParams },\n    /// See [`JsInstance::call_view`].\n    CallView { cmd: ViewCommand },\n    /// See [`JsInstance::call_procedure`].\n    CallProcedure { params: CallProcedureParams },\n    /// See [`JsInstance::clear_all_clients`].\n    ClearAllClients,\n    /// See [`JsInstance::call_identity_connected`].\n    CallIdentityConnected(ConnectionAuthCtx, ConnectionId),\n    /// See [`JsInstance::call_identity_disconnected`].\n    CallIdentityDisconnected(Identity, ConnectionId),\n    /// See [`JsInstance::disconnect_client`].\n    DisconnectClient(ClientActorId),\n    /// See [`JsInstance::init_database`].\n    InitDatabase(Program),\n    /// See [`JsInstance::call_scheduled_function`].\n    CallScheduledFunction(ScheduledFunctionParams),\n}\n\n// These two should be the same size (once core pinning PR lands).\nstatic_assert_size!(JsWorkerRequest, 192);\nstatic_assert_size!(CallReducerParams, 192);\n\n/// Performs some of the startup work of [`spawn_instance_worker`].\n///\n/// NOTE(centril): in its own function due to lack of `try` blocks.\nfn startup_instance_worker<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    program: Arc<str>,\n    module_or_mcc: Either<ModuleCommon, ModuleCreationContext>,\n) -> anyhow::Result<(HookFunctions<'scope>, ModuleCommon)> {\n    let hook_functions = catch_exception(scope, |scope| {\n        // Start-up the user's module.\n        let exports_obj = eval_user_module(scope, &program)?;\n\n        // Find the hook functions.\n        let hooks =\n            get_hooks(scope, exports_obj)?.ok_or_else(|| anyhow::anyhow!(\"must export schema as default export\"))?;\n        Ok(hooks)\n    })?;\n\n    // If we don't have a module, make one.\n    let module_common = match module_or_mcc {\n        Either::Left(module_common) => module_common,\n        Either::Right(mcc) => {\n            let def = extract_description(scope, &hook_functions, &mcc.replica_ctx)?;\n\n            // Validate and create a common module from the raw definition.\n            build_common_module_from_raw(mcc, def)?\n        }\n    };\n\n    Ok((hook_functions, module_common))\n}\n\n/// Returns a new isolate.\nfn new_isolate() -> OwnedIsolate {\n    let mut isolate = Isolate::new(<_>::default());\n    isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 1024);\n    isolate\n}\n\n/// Spawns an instance worker for `program`\n/// and returns on success the corresponding [`JsInstance`]\n/// that talks to the worker.\n///\n/// When [`ModuleCommon`] is passed, it's assumed that `spawn_instance_worker`\n/// has already happened once for this `program` and that it has been\n/// validated. In that case, `Ok(_)` should be returned.\n///\n/// Otherwise, when [`ModuleCreationContext`] is passed,\n/// this is the first time both the module and instance are created.\n///\n/// `load_balance_guard` and `core_pinner` should both be from the same\n/// [`AllocatedJobCore`], and are used to manage the core pinning of this thread.\nasync fn spawn_instance_worker(\n    program: Arc<str>,\n    module_or_mcc: Either<ModuleCommon, ModuleCreationContext>,\n    load_balance_guard: Arc<LoadBalanceOnDropGuard>,\n    mut core_pinner: CorePinner,\n) -> anyhow::Result<(ModuleCommon, JsInstance)> {\n    // Spawn channels for bidirectional communication between worker and instance.\n    // The use-case is SPSC and all channels are rendezvous channels\n    // where each `.send` blocks until it's received.\n    // The Instance --Request-> Worker channel:\n    let (request_tx, request_rx) = flume::bounded(0);\n    // The Worker --Reply-> Instance channel:\n    let (reply_tx, reply_rx) = flume::bounded(0);\n\n    // This one-shot channel is used for initial startup error handling within the thread.\n    let (result_tx, result_rx) = oneshot::channel();\n\n    let rt = tokio::runtime::Handle::current();\n\n    std::thread::spawn(move || {\n        let _guard = load_balance_guard;\n        core_pinner.pin_now();\n\n        let _entered = rt.enter();\n\n        // Create the isolate and scope.\n        let mut isolate = new_isolate();\n        scope_with_context!(let scope, &mut isolate, Context::new(scope, Default::default()));\n\n        // Setup the instance environment.\n        let (replica_ctx, scheduler) = match &module_or_mcc {\n            Either::Left(module) => (module.replica_ctx(), module.scheduler()),\n            Either::Right(mcc) => (&mcc.replica_ctx, &mcc.scheduler),\n        };\n        let instance_env = InstanceEnv::new(replica_ctx.clone(), scheduler.clone());\n        scope.set_slot(JsInstanceEnv::new(instance_env));\n\n        catch_exception(scope, |scope| Ok(builtins::evaluate_builtins(scope)?))\n            .expect(\"our builtin code shouldn't error\");\n\n        // Setup the JS module, find call_reducer, and maybe build the module.\n        let send_result = |res| {\n            result_tx.send(res).inspect_err(|_| {\n                // This should never happen as we immediately `.recv` on the\n                // other end of the channel, but sometimes it gets cancelled.\n                log::error!(\"startup result receiver disconnected\");\n            })\n        };\n        let (hooks, module_common) = match startup_instance_worker(scope, program, module_or_mcc) {\n            Err(err) => {\n                // There was some error in module setup.\n                // Return the error and terminate the worker.\n                let _ = send_result(Err(err));\n                return;\n            }\n            Ok((crf, module_common)) => {\n                env_on_isolate_unwrap(scope).set_module_def(module_common.info().module_def.clone());\n                // Success! Send `module_common` to the spawner.\n                if send_result(Ok(module_common.clone())).is_err() {\n                    return;\n                }\n                (crf, module_common)\n            }\n        };\n\n        if let Some(get_error_constructor) = hooks.get_error_constructor {\n            scope\n                .get_current_context()\n                .set_embedder_data(GET_ERROR_CONSTRUCTOR_SLOT, get_error_constructor.into());\n        }\n\n        // Setup the instance common.\n        let info = &module_common.info();\n        let mut instance_common = InstanceCommon::new(&module_common);\n        let replica_ctx: &Arc<ReplicaContext> = module_common.replica_ctx();\n\n        // Create a zero-initialized buffer for holding reducer args.\n        // Arguments needing more space will not use this.\n        const REDUCER_ARGS_BUFFER_SIZE: usize = 4_096; // 1 page.\n        let reducer_args_buf = ArrayBuffer::new(scope, REDUCER_ARGS_BUFFER_SIZE);\n\n        let mut inst = V8Instance {\n            scope,\n            replica_ctx,\n            hooks: &hooks,\n            reducer_args_buf,\n        };\n\n        // Process requests to the worker.\n        //\n        // The loop is terminated when a `JsInstance` is dropped.\n        // This will cause channels, scopes, and the isolate to be cleaned up.\n        let reply = |ctx: &str, reply: JsWorkerReply, trapped| {\n            if let Err(e) = reply_tx.send((reply, trapped)) {\n                // This should never happen as `JsInstance::$function` immediately\n                // does `.recv` on the other end of the channel, though sometimes\n                // it gets cancelled.\n                log::error!(\"should have receiver for `{ctx}` response, {e}\");\n            }\n        };\n        for request in request_rx.iter() {\n            let mut call_reducer = |tx, params| instance_common.call_reducer_with_tx(tx, params, &mut inst);\n\n            core_pinner.pin_if_changed();\n\n            use JsWorkerReply::*;\n            match request {\n                JsWorkerRequest::RunFunction(f) => rt.block_on(f()),\n                JsWorkerRequest::UpdateDatabase {\n                    program,\n                    old_module_info,\n                    policy,\n                } => {\n                    // Update the database and reply to `JsInstance::update_database`.\n                    let res = instance_common.update_database(program, old_module_info, policy, &mut inst);\n                    reply(\"update_database\", UpdateDatabase(res), false);\n                }\n                JsWorkerRequest::CallReducer { params } => {\n                    // Call the reducer.\n                    // If execution trapped, we don't end the loop here,\n                    // but rather let this happen by `return_instance` using `JsInstance::trapped`\n                    // which will cause `JsInstance` to be dropped,\n                    // which in turn results in the loop being terminated.\n                    let (res, trapped) = call_reducer(None, params);\n                    reply(\"call_reducer\", CallReducer(res), trapped);\n                }\n                JsWorkerRequest::CallView { cmd } => {\n                    let (res, trapped) = instance_common.handle_cmd(cmd, &mut inst);\n                    reply(\"call_view\", JsWorkerReply::CallView(res.into()), trapped);\n                }\n                JsWorkerRequest::CallProcedure { params } => {\n                    let (res, trapped) = instance_common\n                        .call_procedure(params, &mut inst)\n                        .now_or_never()\n                        .expect(\"our call_procedure implementation is not actually async\");\n\n                    reply(\"call_procedure\", JsWorkerReply::CallProcedure(res.into()), trapped);\n                }\n                JsWorkerRequest::ClearAllClients => {\n                    let res = instance_common.clear_all_clients();\n                    reply(\"clear_all_clients\", ClearAllClients(res), false);\n                }\n                JsWorkerRequest::CallIdentityConnected(caller_auth, caller_connection_id) => {\n                    let mut trapped = false;\n                    let res =\n                        call_identity_connected(caller_auth, caller_connection_id, info, call_reducer, &mut trapped);\n                    reply(\"call_identity_connected\", CallIdentityConnected(res), trapped);\n                }\n                JsWorkerRequest::CallIdentityDisconnected(caller_identity, caller_connection_id) => {\n                    let mut trapped = false;\n                    let res = ModuleHost::call_identity_disconnected_inner(\n                        caller_identity,\n                        caller_connection_id,\n                        info,\n                        call_reducer,\n                        &mut trapped,\n                    );\n                    reply(\"call_identity_disconnected\", CallIdentityDisconnected(res), trapped);\n                }\n                JsWorkerRequest::DisconnectClient(client_id) => {\n                    let mut trapped = false;\n                    let res = ModuleHost::disconnect_client_inner(client_id, info, call_reducer, &mut trapped);\n                    reply(\"disconnect_client\", DisconnectClient(res), trapped);\n                }\n                JsWorkerRequest::InitDatabase(program) => {\n                    let (res, trapped): (Result<Option<ReducerCallResult>, anyhow::Error>, bool) =\n                        init_database(replica_ctx, &module_common.info().module_def, program, call_reducer);\n                    reply(\"init_database\", InitDatabase(Box::new(res)), trapped);\n                }\n                JsWorkerRequest::CallScheduledFunction(params) => {\n                    let (res, trapped) = instance_common\n                        .call_scheduled_function(params, &mut inst)\n                        .now_or_never()\n                        .expect(\"our call_procedure implementation is not actually async\");\n                    reply(\"call_scheduled_function\", CallScheduledFunction(res), trapped);\n                }\n            }\n        }\n    });\n\n    // Get the module, if any, and get any setup errors from the worker.\n    let res: Result<ModuleCommon, anyhow::Error> = result_rx.await.expect(\"should have a sender\");\n    res.map(|opt_mc| {\n        let inst = JsInstance {\n            request_tx,\n            reply_rx,\n            trapped: false,\n        };\n        (opt_mc, inst)\n    })\n}\n\n/// The embedder data slot for the `__get_error_constructor__` function.\nconst GET_ERROR_CONSTRUCTOR_SLOT: i32 = syscall::ModuleHookKey::GetErrorConstructor as i32;\n\n/// Compiles, instantiate, and evaluate `code` as a module.\nfn eval_module<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    resource_name: Local<'scope, Value>,\n    code: Local<'_, v8::String>,\n    resolve_deps: impl MapFnTo<ResolveModuleCallback<'scope>>,\n) -> ExcResult<Local<'scope, v8::Module>> {\n    // Assemble the source. v8 figures out things like the `script_id` and\n    // `source_map_url` itself, so we don't actually have to provide them.\n    let origin = ScriptOrigin::new(scope, resource_name, 0, 0, false, 0, None, false, false, true, None);\n    let source = &mut Source::new(code, Some(&origin));\n\n    // Compile the module.\n    let module = compile_module(scope, source).ok_or_else(exception_already_thrown)?;\n\n    // Instantiate the module.\n    module\n        .instantiate_module(scope, resolve_deps)\n        .filter(|x| *x)\n        .ok_or_else(exception_already_thrown)?;\n\n    // Evaluate the module.\n    let value = module.evaluate(scope).ok_or_else(exception_already_thrown)?;\n\n    if module.get_status() == v8::ModuleStatus::Errored {\n        // If there's an exception while evaluating the code of the module, `evaluate()` won't\n        // throw, but instead the status will be `Errored` and the exception can be obtained from\n        // `get_exception()`.\n        return Err(error::ExceptionValue(module.get_exception()).throw(scope));\n    }\n\n    let value = value.cast::<v8::Promise>();\n    match value.state() {\n        v8::PromiseState::Pending => {\n            // If the user were to put top-level `await new Promise((resolve) => { /* do nothing */ })`\n            // the module value would never actually resolve. For now, reject this entirely.\n            Err(error::TypeError(\"module has top-level await and is pending\").throw(scope))\n        }\n        v8::PromiseState::Rejected => Err(error::ExceptionValue(value.result(scope)).throw(scope)),\n        v8::PromiseState::Fulfilled => Ok(module),\n    }\n}\n\n/// Compiles, instantiate, and evaluate the user module with `code`.\n/// Returns the exports of the module.\nfn eval_user_module<'scope>(scope: &mut PinScope<'scope, '_>, code: &str) -> ExcResult<Local<'scope, v8::Object>> {\n    // Convert the code to a string.\n    let code = code.into_string(scope).map_err(|e| e.into_range_error().throw(scope))?;\n\n    let name = str_from_ident!(spacetimedb_module).string(scope).into();\n    let module = eval_module(scope, name, code, resolve_sys_module)?;\n    Ok(module.get_module_namespace().cast())\n}\n\n/// Calls free function `fun` with `args`.\nfn call_free_fun<'scope>(\n    scope: &PinScope<'scope, '_>,\n    fun: Local<'scope, Function>,\n    args: &[Local<'scope, Value>],\n) -> FnRet<'scope> {\n    let receiver = v8::undefined(scope).into();\n    call_recv_fun(scope, fun, receiver, args)\n}\n\n/// Calls function `fun` with `recv` and `args`.\nfn call_recv_fun<'scope>(\n    scope: &PinScope<'scope, '_>,\n    fun: Local<'_, Function>,\n    recv: Local<'_, Value>,\n    args: &[Local<'_, Value>],\n) -> FnRet<'scope> {\n    fun.call(scope, recv, args).ok_or_else(exception_already_thrown)\n}\n\nstruct V8Instance<'a, 'scope, 'isolate> {\n    scope: &'a mut PinScope<'scope, 'isolate>,\n    replica_ctx: &'a Arc<ReplicaContext>,\n    hooks: &'a HookFunctions<'scope>,\n    reducer_args_buf: Local<'scope, ArrayBuffer>,\n}\n\nimpl WasmInstance for V8Instance<'_, '_, '_> {\n    fn extract_descriptions(&mut self) -> Result<RawModuleDef, DescribeError> {\n        extract_description(self.scope, self.hooks, self.replica_ctx)\n    }\n\n    fn replica_ctx(&self) -> &Arc<ReplicaContext> {\n        self.replica_ctx\n    }\n\n    fn tx_slot(&self) -> TxSlot {\n        self.scope.get_slot::<JsInstanceEnv>().unwrap().instance_env.tx.clone()\n    }\n\n    fn set_module_def(&mut self, module_def: Arc<ModuleDef>) {\n        env_on_isolate_unwrap(self.scope).set_module_def(module_def);\n    }\n\n    fn call_reducer(&mut self, op: ReducerOp<'_>, budget: FunctionBudget) -> ReducerExecuteResult {\n        common_call(self.scope, self.hooks, budget, op, |scope, op| {\n            Ok(call_call_reducer(scope, self.hooks, op, self.reducer_args_buf)?)\n        })\n        .map_result(|call_result| call_result.and_then(|res| res.map_err(ExecutionError::User)))\n    }\n\n    fn call_view(&mut self, op: ViewOp<'_>, budget: FunctionBudget) -> ViewExecuteResult {\n        common_call(self.scope, self.hooks, budget, op, |scope, op| {\n            call_call_view(scope, self.hooks, op)\n        })\n    }\n\n    fn call_view_anon(&mut self, op: AnonymousViewOp<'_>, budget: FunctionBudget) -> ViewExecuteResult {\n        common_call(self.scope, self.hooks, budget, op, |scope, op| {\n            call_call_view_anon(scope, self.hooks, op)\n        })\n    }\n\n    fn log_traceback(&self, func_type: &str, func: &str, trap: &anyhow::Error) {\n        log_traceback(self.replica_ctx, func_type, func, trap)\n    }\n\n    async fn call_procedure(\n        &mut self,\n        op: ProcedureOp,\n        budget: FunctionBudget,\n    ) -> (ProcedureExecuteResult, Option<TransactionOffset>) {\n        let result = common_call(self.scope, self.hooks, budget, op, |scope, op| {\n            call_call_procedure(scope, self.hooks, op)\n        })\n        .map_result(|call_result| {\n            call_result.map_err(|e| match e {\n                ExecutionError::User(e) => anyhow::Error::msg(e),\n                ExecutionError::Recoverable(e) | ExecutionError::Trap(e) => e,\n            })\n        });\n        let tx_offset = env_on_isolate_unwrap(self.scope)\n            .instance_env\n            .take_procedure_tx_offset();\n        (result, tx_offset)\n    }\n}\n\nfn common_call<'scope, R, O, F>(\n    scope: &mut PinScope<'scope, '_>,\n    hooks: &HookFunctions<'_>,\n    budget: FunctionBudget,\n    op: O,\n    call: F,\n) -> ExecutionResult<R, ExecutionError>\nwhere\n    O: InstanceOp,\n    F: FnOnce(&mut PinTryCatch<'scope, '_, '_, '_>, O) -> Result<R, ErrorOrException<ExceptionThrown>>,\n{\n    // TODO(v8): Start the budget timeout and long-running logger.\n    let env = env_on_isolate_unwrap(scope);\n\n    // Start the timer.\n    // We'd like this tightly around `call`.\n    env.start_funcall(op.name().clone(), op.timestamp(), op.call_type());\n\n    v8::tc_scope!(scope, scope);\n    let call_result = call(scope, op).map_err(|mut e| {\n        if let ErrorOrException::Exception(_) = e {\n            // If we're terminating execution, don't try to check `instanceof`.\n            if scope.can_continue()\n                && let Some(exc) = scope.exception()\n            {\n                match process_thrown_exception(scope, hooks, exc) {\n                    Ok(Some(err)) => return err,\n                    Ok(None) => {}\n                    Err(exc) => e = ErrorOrException::Exception(exc),\n                }\n            }\n        }\n        let e = e.map_exception(|exc| exc.into_error(scope)).into();\n        if scope.can_continue() {\n            // We can continue.\n            ExecutionError::Recoverable(e)\n        } else if scope.has_terminated() {\n            // We can continue if we do `Isolate::cancel_terminate_execution`.\n            scope.cancel_terminate_execution();\n            ExecutionError::Recoverable(e)\n        } else {\n            // We cannot continue.\n            ExecutionError::Trap(e)\n        }\n    });\n\n    // Finish timings.\n    let timings = env_on_isolate_unwrap(scope).finish_reducer();\n\n    // Derive energy stats.\n    let energy = energy_from_elapsed(budget, timings.total_duration);\n\n    // Fetch the currently used heap size in V8.\n    // The used size is ostensibly fairer than the total size.\n    let memory_allocation = scope.get_heap_statistics().used_heap_size();\n\n    let stats = ExecutionStats {\n        energy,\n        timings,\n        memory_allocation,\n    };\n    ExecutionResult { stats, call_result }\n}\n\n/// Extracts the raw module def by running the registered `__describe_module__` hook.\nfn extract_description<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    hooks: &HookFunctions<'_>,\n    replica_ctx: &ReplicaContext,\n) -> Result<RawModuleDef, DescribeError> {\n    run_describer(\n        |a, b, c| log_traceback(replica_ctx, a, b, c),\n        || {\n            Ok(catch_exception(scope, |scope| {\n                let def = call_describe_module(scope, hooks)?;\n                Ok(def)\n            })?)\n        },\n    )\n}\n\n#[cfg(test)]\nmod test {\n    use super::to_value::test::with_scope;\n    use super::*;\n    use crate::host::v8::error::{ErrorOrException, ExceptionThrown};\n    use crate::host::wasm_common::module_host_actor::ReducerOp;\n    use crate::host::ArgsTuple;\n    use spacetimedb_lib::{ConnectionId, Identity};\n    use spacetimedb_primitives::ReducerId;\n    use spacetimedb_schema::reducer_name::ReducerName;\n\n    fn with_module_catch<T>(\n        code: &str,\n        logic: impl for<'scope> FnOnce(\n            &mut PinTryCatch<'scope, '_, '_, '_>,\n            Local<v8::Object>,\n        ) -> Result<T, ErrorOrException<ExceptionThrown>>,\n    ) -> anyhow::Result<T> {\n        with_scope(|scope| {\n            Ok(catch_exception(scope, |scope| {\n                let exports = eval_user_module(scope, code)?;\n                let ret = logic(scope, exports)?;\n                Ok(ret)\n            })?)\n        })\n    }\n\n    #[test]\n    fn call_call_reducer_works() {\n        let call = |code| {\n            with_module_catch(code, |scope, exports| {\n                let hooks = get_hooks(scope, exports)?.unwrap();\n                let op = ReducerOp {\n                    id: ReducerId(42),\n                    name: &ReducerName::for_test(\"foobar\"),\n                    caller_identity: &Identity::ONE,\n                    caller_connection_id: &ConnectionId::ZERO,\n                    timestamp: Timestamp::from_micros_since_unix_epoch(24),\n                    args: &ArgsTuple::nullary(),\n                };\n                let buffer = v8::ArrayBuffer::new(scope, 4096);\n                Ok(call_call_reducer(scope, &hooks, op, buffer)?)\n            })\n        };\n\n        // Test the trap case.\n        let ret = call(\n            r#\"\n            import { register_hooks } from \"spacetime:sys@1.0\";\n            register_hooks({\n                __describe_module__: function() {},\n                __call_reducer__: function(reducer_id, sender, conn_id, timestamp, args) {\n                    throw new Error(\"foobar\");\n                },\n            })\n        \"#,\n        );\n        let actual = ret.expect_err(\"should trap\").to_string().replace(\"\\t\", \"    \");\n        let expected = r#\"\nUncaught Error: foobar\n    at __call_reducer__ (spacetimedb_module:6:27)\n        \"#;\n        assert_eq!(actual.trim(), expected.trim());\n\n        // Test the error case.\n        let ret = call(\n            r#\"\n            import { register_hooks } from \"spacetime:sys@1.0\";\n            register_hooks({\n                __describe_module__: function() {},\n                __call_reducer__: function(reducer_id, sender, conn_id, timestamp, args) {\n                    return {\n                        \"tag\": \"err\",\n                        \"value\": \"foobar\",\n                    };\n                },\n            })\n        \"#,\n        );\n        assert_eq!(&*ret.expect(\"should not trap\").expect_err(\"should error\"), \"foobar\");\n\n        // Test the error case.\n        let ret = call(\n            r#\"\n            import { register_hooks } from \"spacetime:sys@1.0\";\n            register_hooks({\n                __describe_module__: function() {},\n                __call_reducer__: function(reducer_id, sender, conn_id, timestamp, args) {\n                    return {\n                        \"tag\": \"ok\",\n                        \"value\": {},\n                    };\n                },\n            })\n        \"#,\n        );\n        ret.expect(\"should not trap\").expect(\"should not error\");\n    }\n\n    #[test]\n    fn call_describe_module_works() {\n        let code = r#\"\n            import { register_hooks } from \"spacetime:sys@1.0\";\n            register_hooks({\n                __call_reducer__: function() {},\n                __describe_module__: function() {\n                    return new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);\n                },\n            })\n        \"#;\n        let raw_mod = with_module_catch(code, |scope, exports| {\n            let hooks = get_hooks(scope, exports)?.unwrap();\n            call_describe_module(scope, &hooks)\n        })\n        .map_err(|e| e.to_string());\n        assert_eq!(raw_mod, Ok(RawModuleDef::V9(<_>::default())));\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/ser.rs",
    "content": "use super::de::intern_field_name;\nuse super::error::{\n    exception_already_thrown, ArrayTooLongError, ExcResult, ExceptionThrown, StringTooLongError, Throwable, TypeError,\n};\nuse super::string::{IntoJsString, TAG, VALUE};\nuse super::syscall::FnRet;\nuse super::to_value::ToValue;\nuse derive_more::From;\nuse spacetimedb_sats::{\n    i256,\n    ser::{self, Serialize},\n    u256,\n};\nuse v8::{Array, IntegrityLevel, Local, Object, PinScope, Value};\n\n/// Serializes `value` into a V8 into `scope`.\npub(super) fn serialize_to_js<'scope>(scope: &PinScope<'scope, '_>, value: &impl Serialize) -> FnRet<'scope> {\n    value.serialize(Serializer::new(scope)).map_err(|e| e.throw(scope))\n}\n\n/// Deserializes to V8 values.\n#[derive(Copy, Clone)]\nstruct Serializer<'this, 'scope, 'isolate> {\n    /// The scope to serialize values into.\n    scope: &'this PinScope<'scope, 'isolate>,\n}\n\nimpl<'this, 'scope, 'isolate> Serializer<'this, 'scope, 'isolate> {\n    /// Creates a new serializer into `scope`.\n    pub fn new(scope: &'this PinScope<'scope, 'isolate>) -> Self {\n        Self { scope }\n    }\n}\n\n/// The possible errors that [`Serializer`] can produce.\n#[derive(Debug, From)]\nenum Error {\n    Custom(String),\n    Thrown(ExceptionThrown),\n    StringTooLong(StringTooLongError),\n    ArrayLengthTooLong(ArrayTooLongError),\n}\n\nimpl<'scope> Throwable<'scope> for Error {\n    fn throw(self, scope: &PinScope<'scope, '_>) -> ExceptionThrown {\n        match self {\n            Self::StringTooLong(err) => err.into_range_error().throw(scope),\n            Self::ArrayLengthTooLong(err) => err.into_range_error().throw(scope),\n            Self::Thrown(thrown) => thrown,\n            Self::Custom(msg) => TypeError(msg).throw(scope),\n        }\n    }\n}\n\nimpl ser::Error for Error {\n    fn custom<T: core::fmt::Display>(msg: T) -> Self {\n        Self::Custom(msg.to_string())\n    }\n}\n\n/// Serializes a primitive via [`ToValue`].\nmacro_rules! serialize_primitive {\n    ($method:ident, $ty:ty) => {\n        fn $method(self, val: $ty) -> Result<Self::Ok, Self::Error> {\n            Ok(ToValue::to_value(&val, self.scope))\n        }\n    };\n}\n\n/// Seal the object, so that e.g., new properties cannot be added.\n///\n/// However, the values of existing properties may be modified,\n/// which can be useful if the module wants to modify a property\n/// and then send the object back.\nfn seal_object(scope: &PinScope<'_, '_>, object: &Object) -> ExcResult<()> {\n    let _ = object\n        .set_integrity_level(scope, IntegrityLevel::Sealed)\n        .ok_or_else(exception_already_thrown)?;\n    Ok(())\n}\n\nimpl<'this, 'scope, 'isolate> ser::Serializer for Serializer<'this, 'scope, 'isolate> {\n    type Ok = Local<'scope, Value>;\n    type Error = Error;\n\n    type SerializeArray = SerializeArray<'this, 'scope, 'isolate>;\n    type SerializeSeqProduct = Self::SerializeNamedProduct;\n    type SerializeNamedProduct = SerializeNamedProduct<'this, 'scope, 'isolate>;\n\n    // Serialization of primitive types defers to `ToValue`.\n    serialize_primitive!(serialize_bool, bool);\n    serialize_primitive!(serialize_u8, u8);\n    serialize_primitive!(serialize_u16, u16);\n    serialize_primitive!(serialize_u32, u32);\n    serialize_primitive!(serialize_u64, u64);\n    serialize_primitive!(serialize_u128, u128);\n    serialize_primitive!(serialize_u256, u256);\n    serialize_primitive!(serialize_i8, i8);\n    serialize_primitive!(serialize_i16, i16);\n    serialize_primitive!(serialize_i32, i32);\n    serialize_primitive!(serialize_i64, i64);\n    serialize_primitive!(serialize_i128, i128);\n    serialize_primitive!(serialize_i256, i256);\n    serialize_primitive!(serialize_f64, f64);\n    serialize_primitive!(serialize_f32, f32);\n\n    fn serialize_str(self, string: &str) -> Result<Self::Ok, Self::Error> {\n        string\n            .into_string(self.scope)\n            .map(Into::into)\n            .map_err(Error::StringTooLong)\n    }\n\n    fn serialize_bytes(self, bytes: &[u8]) -> Result<Self::Ok, Self::Error> {\n        Ok(super::util::make_uint8array(self.scope, bytes.to_vec()).into())\n    }\n\n    fn serialize_array(self, len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        let len = len.try_into().map_err(|_| ArrayTooLongError { len })?;\n        Ok(SerializeArray {\n            array: Array::new(self.scope, len),\n            inner: self,\n            next_index: 0,\n        })\n    }\n\n    fn serialize_seq_product(self, len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        self.serialize_named_product(len)\n    }\n\n    fn serialize_named_product(self, _len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        // TODO(v8, noa): this can be more efficient if we tell it the names ahead of time\n        let object = Object::new(self.scope);\n        Ok(SerializeNamedProduct {\n            inner: self,\n            object,\n            next_index: 0,\n        })\n    }\n\n    fn serialize_variant<T: Serialize + ?Sized>(\n        self,\n        tag: u8,\n        var_name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        // Serialize the payload.\n        let value_value: Local<'scope, Value> = value.serialize(self)?;\n        // Figure out the tag.\n        let tag_value: Local<'scope, Value> = intern_field_name(self.scope, var_name, tag as usize).into();\n        let values = [tag_value, value_value];\n\n        // The property keys are always `\"tag\"` an `\"value\"`.\n        let names = [TAG.string(self.scope).into(), VALUE.string(self.scope).into()];\n\n        // Stitch together the object.\n        let prototype = v8::null(self.scope).into();\n        let object = Object::with_prototype_and_properties(self.scope, prototype, &names, &values);\n        seal_object(self.scope, &object)?;\n        Ok(object.into())\n    }\n}\n\n/// Serializes array elements and finalizes the JS array.\nstruct SerializeArray<'this, 'scope, 'isolate> {\n    inner: Serializer<'this, 'scope, 'isolate>,\n    array: Local<'scope, Array>,\n    next_index: u32,\n}\n\nimpl<'scope> ser::SerializeArray for SerializeArray<'_, 'scope, '_> {\n    type Ok = Local<'scope, Value>;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        // Serialize the current `elem`ent.\n        let value = elem.serialize(self.inner)?;\n\n        // Set the value to the array slot at `index`.\n        let index = self.next_index;\n        self.next_index += 1;\n        self.array\n            .set_index(self.inner.scope, index, value)\n            .ok_or_else(exception_already_thrown)?;\n\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(self.array.into())\n    }\n}\n\n/// Serializes into JS objects where field names are turned into property names.\nstruct SerializeNamedProduct<'this, 'scope, 'isolate> {\n    inner: Serializer<'this, 'scope, 'isolate>,\n    object: Local<'scope, Object>,\n    next_index: usize,\n}\n\nimpl<'scope> ser::SerializeSeqProduct for SerializeNamedProduct<'_, 'scope, '_> {\n    type Ok = Local<'scope, Value>;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        ser::SerializeNamedProduct::serialize_element(self, None, elem)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        ser::SerializeNamedProduct::end(self)\n    }\n}\n\nimpl<'scope> ser::SerializeNamedProduct for SerializeNamedProduct<'_, 'scope, '_> {\n    type Ok = Local<'scope, Value>;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(\n        &mut self,\n        field_name: Option<&str>,\n        elem: &T,\n    ) -> Result<(), Self::Error> {\n        // Serialize the field value.\n        let value = elem.serialize(self.inner)?;\n\n        // Figure out the object property to use.\n        let scope = self.inner.scope;\n        let index = self.next_index;\n        self.next_index += 1;\n        let key = intern_field_name(scope, field_name, index).into();\n\n        // Set the value to the property.\n        self.object\n            .set(scope, key, value)\n            .ok_or_else(exception_already_thrown)?;\n\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        seal_object(self.inner.scope, &self.object)?;\n        Ok(self.object.into())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use crate::host::v8::de::deserialize_js_seed;\n\n    use super::super::to_value::test::with_scope;\n    use super::*;\n    use core::fmt::Debug;\n    use proptest::prelude::*;\n    use spacetimedb_lib::{AlgebraicType, AlgebraicValue};\n    use spacetimedb_sats::de::DeserializeSeed;\n    use spacetimedb_sats::proptest::generate_typed_value;\n    use spacetimedb_sats::{product, SumValue, ValueWithType, WithTypespace};\n    use AlgebraicType::Bool;\n\n    /// Roundtrips `rust_val` via [`Serialize`] to the V8 representation\n    /// and then back via [`DeserializeSeed`],\n    /// asserting that it's the same as the passed value.\n    fn assert_roundtrips<B: Debug>(\n        rust_val: impl Serialize + PartialEq<B> + Debug,\n        seed: impl for<'de> DeserializeSeed<'de, Output = B>,\n    ) {\n        with_scope(|scope| {\n            // Convert to JS...\n            let js_val = serialize_to_js(scope, &rust_val).unwrap();\n\n            // ...and then back to Rust.\n            let rust_val_prime = deserialize_js_seed(scope, js_val, seed).unwrap();\n\n            // We should end up where we started.\n            assert_eq!(rust_val, rust_val_prime);\n        })\n    }\n\n    fn assert_roundtrips_with_ty(ty: AlgebraicType, val: AlgebraicValue) {\n        let ctx = WithTypespace::empty(&ty);\n        let value = ValueWithType::new(ctx, &val);\n        let seed = value.ty_s();\n        assert_roundtrips(value, seed);\n    }\n\n    proptest! {\n        #[test]\n        fn test_random_typed_value_roundtrips((ty, val) in generate_typed_value()) {\n            assert_roundtrips_with_ty(ty, val);\n        }\n    }\n\n    #[test]\n    fn anonymized_product_works() {\n        let ty = AlgebraicType::product([Bool]);\n        let val = product![false].into();\n        assert_roundtrips_with_ty(ty, val);\n    }\n\n    /// This test demonstrates that serialization misbehaves without using [`ValueWithType`].\n    #[test]\n    fn regression_test_product_serialization_needs_value_with_type() {\n        let ty = AlgebraicType::product([(\"field_0\", Bool)]);\n        let val = product![false].into();\n        assert_roundtrips_with_ty(ty, val);\n    }\n\n    #[test]\n    fn regression_test_variant() {\n        let ty = AlgebraicType::sum([(\"variant_0\", Bool)]);\n        let val = SumValue::new(0, false).into();\n        assert_roundtrips_with_ty(ty, val);\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/string.rs",
    "content": "use super::error::StringTooLongError;\nuse v8::{Local, OneByteConst, PinScope, Symbol};\n\ntype LString<'scope> = Local<'scope, v8::String>;\ntype StringResult<'scope> = Result<LString<'scope>, StringTooLongError>;\n\n/// Types that can convert into a JS string type.\npub(super) trait IntoJsString {\n    /// Converts `self` into a JS string.\n    fn into_string<'scope>(self, scope: &PinScope<'scope, '_>) -> StringResult<'scope>;\n}\n\nimpl IntoJsString for &str {\n    fn into_string<'scope>(self, scope: &PinScope<'scope, '_>) -> StringResult<'scope> {\n        v8::String::new(scope, self).ok_or_else(|| StringTooLongError::new(self))\n    }\n}\n\nimpl IntoJsString for String {\n    fn into_string<'scope>(self, scope: &PinScope<'scope, '_>) -> StringResult<'scope> {\n        (&*self).into_string(scope)\n    }\n}\n\nimpl IntoJsString for &'static StringConst {\n    fn into_string<'scope>(self, scope: &PinScope<'scope, '_>) -> StringResult<'scope> {\n        Ok(self.string(scope))\n    }\n}\n\n/// A string known at compile time to be ASCII.\npub(super) struct StringConst(OneByteConst);\n\nimpl StringConst {\n    /// Returns a new string that is known to be ASCII and static.\n    pub(super) const fn new(string: &'static str) -> Self {\n        Self(v8::String::create_external_onebyte_const(string.as_bytes()))\n    }\n\n    /// Converts the string to a V8 string.\n    pub(super) fn string<'scope>(&'static self, scope: &PinScope<'scope, '_>) -> LString<'scope> {\n        v8::String::new_from_onebyte_const(scope, &self.0)\n            .expect(\"`create_external_onebyte_const` should've asserted `.len() < kMaxLength`\")\n    }\n\n    /// Converts the string to a V8 symbol using [`Symbol::for_api`].\n    pub(super) fn symbol<'scope>(&'static self, scope: &PinScope<'scope, '_>) -> Local<'scope, Symbol> {\n        let desc = self.string(scope);\n        Symbol::for_api(scope, desc)\n    }\n\n    /// Returns the backing string slice.\n    pub(super) fn as_str(&'static self) -> &'static str {\n        self.0.as_str()\n    }\n}\n\n/// Converts an identifier to a compile-time ASCII string.\n#[macro_export]\nmacro_rules! str_from_ident {\n    ($ident:ident) => {{\n        const STR: &$crate::host::v8::string::StringConst =\n            &$crate::host::v8::string::StringConst::new(stringify!($ident));\n        STR\n    }};\n}\npub(super) use str_from_ident;\n\n/// The `tag` property of a sum object in JS.\npub(super) const TAG: &StringConst = str_from_ident!(tag);\n/// The `value` property of a sum object in JS.\npub(super) const VALUE: &StringConst = str_from_ident!(value);\n"
  },
  {
    "path": "crates/core/src/host/v8/syscall/common.rs",
    "content": "use super::super::{\n    call_recv_fun,\n    de::deserialize_js,\n    de::scratch_buf,\n    env_on_isolate,\n    error::{\n        exception_already_thrown, ErrorOrException, ExceptionThrown, JsStackTrace, SysCallError, SysCallResult,\n        Throwable, TypeError,\n    },\n    from_value::cast,\n    ser::serialize_to_js,\n    util::make_uint8array,\n    JsInstanceEnv,\n};\nuse super::{call_call_view, call_call_view_anon, get_registered_hooks, HookFunctions};\nuse crate::database_logger::{LogLevel, Record};\nuse crate::error::NodesError;\nuse crate::host::instance_env::InstanceEnv;\nuse crate::host::wasm_common::module_host_actor::{\n    deserialize_view_rows, run_query_for_view, AnonymousViewOp, ProcedureOp, ViewOp, ViewResult, ViewReturnData,\n};\nuse crate::host::wasm_common::{RowIterIdx, TimingSpan, TimingSpanIdx};\nuse anyhow::Context;\nuse bytes::Bytes;\nuse spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCallInfo};\nuse spacetimedb_lib::{ConnectionId, Identity, RawModuleDef, Timestamp};\nuse spacetimedb_primitives::{ColId, IndexId, ProcedureId, TableId};\nuse spacetimedb_sats::bsatn;\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_schema::identifier::Identifier;\nuse v8::{FunctionCallbackArguments, Isolate, Local, PinScope, Value};\n\n/// Calls the `__call_procedure__` function `fun`.\npub fn call_call_procedure(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: ProcedureOp,\n) -> Result<Bytes, ErrorOrException<ExceptionThrown>> {\n    let fun = hooks.call_procedure.context(\"`__call_procedure__` was never defined\")?;\n\n    let ProcedureOp {\n        id: ProcedureId(procedure_id),\n        name: _,\n        caller_identity: sender,\n        caller_connection_id: connection_id,\n        timestamp,\n        arg_bytes: procedure_args,\n    } = op;\n    // Serialize the arguments.\n    let procedure_id = serialize_to_js(scope, &procedure_id)?;\n    let sender = serialize_to_js(scope, &sender.to_u256())?;\n    let connection_id = serialize_to_js(scope, &connection_id.to_u128())?;\n    let timestamp = serialize_to_js(scope, &timestamp.to_micros_since_unix_epoch())?;\n    let procedure_args = serialize_to_js(scope, &procedure_args)?;\n    let args = &[procedure_id, sender, connection_id, timestamp, procedure_args];\n\n    // Call the function.\n    let ret = call_recv_fun(scope, fun, hooks.recv, args)?;\n\n    // Deserialize the user result.\n    let ret =\n        cast!(scope, ret, v8::Uint8Array, \"bytes return from `__call_procedure__`\").map_err(|e| e.throw(scope))?;\n    let bytes = ret.get_contents(&mut []);\n\n    Ok(Bytes::copy_from_slice(bytes))\n}\n\n/// Calls the registered `__describe_module__` function hook.\npub fn call_describe_module(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n) -> Result<RawModuleDef, ErrorOrException<ExceptionThrown>> {\n    // Call the function.\n    let raw_mod_js = call_recv_fun(scope, hooks.describe_module, hooks.recv, &[])?;\n\n    // Deserialize the raw module.\n    let raw_mod = cast!(\n        scope,\n        raw_mod_js,\n        v8::Uint8Array,\n        \"bytes return from `__describe_module__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n\n    let bytes = raw_mod.get_contents(&mut []);\n    let module = bsatn::from_slice::<RawModuleDef>(bytes).context(\"invalid bsatn module def\")?;\n    Ok(module)\n}\n\n/// Returns the environment or errors.\npub fn get_env(isolate: &mut Isolate) -> SysCallResult<&mut JsInstanceEnv> {\n    env_on_isolate(isolate).ok_or(SysCallError::NoEnv)\n}\n\n/// Module ABI that finds the `TableId` for a table name.\n///\n/// # Signature\n///\n/// ```ignore\n/// table_id_from_name(name: string) -> u32 throws {\n///     __code_error__: NOT_IN_TRANSACTION | NO_SUCH_TABLE\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns an `u32` containing the id of the table.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_TABLE`]\n///   when `name` is not the name of a table.\n///\n/// Throws a `TypeError` if:\n/// - `name` is not `string`.\npub fn table_id_from_name(scope: &mut PinScope<'_, '_>, args: FunctionCallbackArguments<'_>) -> SysCallResult<TableId> {\n    let name: String = deserialize_js(scope, args.get(0))?;\n    Ok(get_env(scope)?.instance_env.table_id_from_name(&name)?)\n}\n\n/// Module ABI that finds the `IndexId` for an index name.\n///\n/// # Signature\n///\n/// ```ignore\n/// index_id_from_name(name: string) -> u32 throws {\n///     __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns an `u32` containing the id of the index.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `name` is not the name of an index.\n///\n/// Throws a `TypeError`:\n/// - if `name` is not `string`.\npub fn index_id_from_name(scope: &mut PinScope<'_, '_>, args: FunctionCallbackArguments<'_>) -> SysCallResult<IndexId> {\n    let name: String = deserialize_js(scope, args.get(0))?;\n    Ok(get_env(scope)?.instance_env.index_id_from_name(&name)?)\n}\n\n/// Module ABI that returns the number of rows currently in table identified by `table_id`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_table_row_count(table_id: u32) -> u64 throws {\n///     __code_error__: NOT_IN_TRANSACTION | NO_SUCH_TABLE\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n/// - `u64` is `bigint` in JS restricted to unsigned 64-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u64` containing the number of rows in the table.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\npub fn datastore_table_row_count(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u64> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    Ok(get_env(scope)?.instance_env.datastore_table_row_count(table_id)?)\n}\n\n/// Module ABI that starts iteration on each row, as BSATN-encoded,\n/// of a table identified by `table_id`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_table_scan_bsatn(table_id: u32) -> u32 throws {\n///     __code_error__: NOT_IN_TRANSACTION | NO_SUCH_TABLE\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n/// - `u64` is `bigint` in JS restricted to unsigned 64-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the iterator handle.\n/// This handle can be advanced by [`row_iter_bsatn_advance`].\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n///\n/// Throws a `TypeError`:\n/// - if `table_id` is not a `u32`.\npub fn datastore_table_scan_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n\n    let env = get_env(scope)?;\n    // Collect the iterator chunks.\n    let chunks = env\n        .instance_env\n        .datastore_table_scan_bsatn_chunks(&mut env.chunk_pool, table_id)?;\n\n    // Register the iterator and get back the index to write to `out`.\n    // Calls to the iterator are done through dynamic dispatch.\n    Ok(env.iters.insert(chunks.into_iter()).0)\n}\n\n/// This is a helper function that is used by\n/// `v1::datastore_index_scan_range_bsatn`\n/// and `v2::datastore_index_scan_range_bsatn`.\n/// See those for additional details.\npub fn datastore_index_scan_range_bsatn_inner(\n    scope: &mut PinScope<'_, '_>,\n    index_id: IndexId,\n    mut prefix: &[u8],\n    prefix_elems: ColId,\n    rstart: &[u8],\n    rend: &[u8],\n) -> SysCallResult<u32> {\n    if prefix_elems.idx() == 0 {\n        prefix = &[];\n    }\n\n    let env = get_env(scope)?;\n\n    // Find the relevant rows.\n    let chunks = env.instance_env.datastore_index_scan_range_bsatn_chunks(\n        &mut env.chunk_pool,\n        index_id,\n        prefix,\n        prefix_elems,\n        rstart,\n        rend,\n    )?;\n\n    // Insert the encoded + concatenated rows into a new buffer and return its id.\n    Ok(env.iters.insert(chunks.into_iter()).0)\n}\n\npub fn deserialize_row_iter_idx(scope: &mut PinScope<'_, '_>, value: Local<'_, Value>) -> SysCallResult<RowIterIdx> {\n    deserialize_js(scope, value).map(RowIterIdx).map_err(Into::into)\n}\n\n/// Module ABI that destroys the iterator registered under `iter`.\n///\n/// Once `row_iter_bsatn_close` is called on `iter`, the `iter` is invalid.\n/// That is, `row_iter_bsatn_close(iter)` the second time will yield `NO_SUCH_ITER`.\n///\n/// # Signature\n///\n/// ```ignore\n/// row_iter_bsatn_close(iter: u32) -> undefined throws {\n///     __code_error__: NO_SUCH_ITER\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_ITER`]\n///   when `iter` is not a valid iterator.\n///\n/// Throws a `TypeError` if:\n/// - `iter` is not a `u32`.\npub fn row_iter_bsatn_close<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<()> {\n    let row_iter_idx: u32 = deserialize_js(scope, args.get(0))?;\n    let row_iter_idx = RowIterIdx(row_iter_idx);\n\n    // Retrieve the iterator by `row_iter_idx`, or error.\n    let env = get_env(scope)?;\n\n    // Retrieve the iterator by `row_iter_idx`, or error.\n    if env.iters.take(row_iter_idx).is_none() {\n        return Err(SysCallError::NO_SUCH_ITER);\n    } else {\n        // TODO(Centril): consider putting these into a pool for reuse.\n    }\n\n    Ok(())\n}\n\n/// # Signature\n///\n/// ```ignore\n/// volatile_nonatomic_schedule_immediate(reducer_name: string, args: u8[]) -> undefined\n/// ```\npub fn volatile_nonatomic_schedule_immediate<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<()> {\n    let name: String = deserialize_js(scope, args.get(0))?;\n    let args: Vec<u8> = deserialize_js(scope, args.get(1))?;\n\n    get_env(scope)?\n        .instance_env\n        .scheduler\n        .volatile_nonatomic_schedule_immediate(name, crate::host::FunctionArgs::Bsatn(args.into()));\n\n    Ok(())\n}\n\n/// Module ABI that logs at `level` a `message` message occurring\n/// at the parent stack frame.\n///\n/// The `message` is interpreted lossily as a UTF-8 string.\n///\n/// # Signature\n///\n/// ```ignore\n/// console_log(level: u8, message: string) -> u32\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\npub fn console_log<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<()> {\n    let level: u32 = deserialize_js(scope, args.get(0))?;\n\n    let msg = args.get(1).cast::<v8::String>();\n    let mut buf = scratch_buf::<128>();\n    let msg = msg.to_rust_cow_lossy(scope, &mut buf);\n\n    let frame: Local<'_, v8::StackFrame> = v8::StackTrace::current_stack_trace(scope, 2)\n        .ok_or_else(exception_already_thrown)?\n        .get_frame(scope, 1)\n        .ok_or_else(exception_already_thrown)?;\n    let mut buf = scratch_buf::<32>();\n    let filename = frame\n        .get_script_name(scope)\n        .map(|s| s.to_rust_cow_lossy(scope, &mut buf));\n\n    let level = (level as u8).into();\n    let trace = if level == LogLevel::Panic {\n        JsStackTrace::from_current_stack_trace(scope)?\n    } else {\n        <_>::default()\n    };\n\n    let env = get_env(scope).inspect_err(|_| {\n        tracing::warn!(\n            \"{}:{} {msg}\",\n            filename.as_deref().unwrap_or(\"unknown\"),\n            frame.get_line_number()\n        );\n    })?;\n\n    let function = env.log_record_function();\n    let record = Record {\n        // TODO: figure out whether to use walltime now or logical reducer now (env.reducer_start)\n        ts: InstanceEnv::now_for_logging(),\n        target: None,\n        filename: filename.as_deref(),\n        line_number: Some(frame.get_line_number() as u32),\n        function,\n        message: &msg,\n    };\n\n    env.instance_env.console_log(level, &record, &trace);\n\n    Ok(())\n}\n\n/// Module ABI that begins a timing span with `name`.\n///\n/// When the returned `ConsoleTimerId` is passed to [`console_timer_end`],\n/// the duration between the calls will be printed to the module's logs.\n///\n/// The `name` is interpreted lossily as a UTF-8 string.\n///\n/// # Signature\n///\n/// ```ignore\n/// console_timer_start(name: string) -> u32\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the `ConsoleTimerId`.\n///\n/// # Throws\n///\n/// Throws a `TypeError` if:\n/// - `name` is not a `string`.\npub fn console_timer_start<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<u32> {\n    let name = args.get(0).cast::<v8::String>();\n    let mut buf = scratch_buf::<128>();\n    let name = name.to_rust_cow_lossy(scope, &mut buf).into_owned();\n\n    let span_id = get_env(scope)?.timing_spans.insert(TimingSpan::new(name)).0;\n    Ok(span_id)\n}\n\n/// Module ABI that ends a timing span with `span_id`.\n///\n/// # Signature\n///\n/// ```ignore\n/// console_timer_end(span_id: u32) -> undefined throws {\n///     __code_error__: NO_SUCH_CONSOLE_TIMER\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_CONSOLE_TIMER`]\n///   when `span_id` doesn't refer to an active timing span.\n///\n/// Throws a `TypeError` if:\n/// - `span_id` is not a `u32`.\npub fn console_timer_end<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<()> {\n    let span_id: u32 = deserialize_js(scope, args.get(0))?;\n\n    let env = get_env(scope)?;\n    let span = env\n        .timing_spans\n        .take(TimingSpanIdx(span_id))\n        .ok_or(SysCallError::NO_SUCH_CONSOLE_TIMER)?;\n    let function = env.log_record_function();\n    env.instance_env.console_timer_end(&span, function);\n\n    Ok(())\n}\n\n/// Module ABI to read a JWT payload associated with a connection ID from the system tables.\n///\n/// # Signature\n///\n/// ```ignore\n/// get_jwt_payload(connection_id: u128) -> u8[] throws {\n///     __code_error__:\n///         NOT_IN_TRANSACTION\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u128` is `bigint` in JS restricted to unsigned 128-bit integers.\n///\n/// # Returns\n///\n/// Returns a byte array encoding the JWT payload if one is found. If one is not found, an\n/// empty byte array is returned.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\npub fn get_jwt_payload(scope: &mut PinScope<'_, '_>, args: FunctionCallbackArguments<'_>) -> SysCallResult<Vec<u8>> {\n    let connection_id: u128 = deserialize_js(scope, args.get(0))?;\n    let connection_id = ConnectionId::from_u128(connection_id);\n    let payload = get_env(scope)?\n        .instance_env\n        .get_jwt_payload(connection_id)?\n        .map(String::into_bytes)\n        .unwrap_or_default();\n    Ok(payload)\n}\n\n/// Module ABI that returns the module identity.\n///\n/// # Signature\n///\n/// ```ignore\n/// identity() -> { __identity__: u256 }\n/// ```\n///\n/// # Types\n///\n/// - `u256` is `bigint` in JS restricted to unsigned 256-bit integers.\n///\n/// # Returns\n///\n/// Returns the module identity.\npub fn identity<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    _: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<Identity> {\n    Ok(*get_env(scope)?.instance_env.database_identity())\n}\n\n/// Execute an HTTP request in the context of a procedure.\n///\n/// # Signature\n///\n/// ```ignore\n/// function procedure_http_request(\n///     request: Uint8Array,\n///     body: Uint8Array | string\n/// ): [response: Uint8Array, body: Uint8Array];\n/// ```\n///\n/// Accepts a BSATN-encoded [`spacetimedb_lib::http::Request`] and a request body, and\n/// returns a BSATN-encoded [`spacetimedb_lib::http::Response`] and the response body.\npub fn procedure_http_request<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<Local<'scope, v8::Array>> {\n    use spacetimedb_lib::http as st_http;\n\n    let request =\n        cast!(scope, args.get(0), v8::Uint8Array, \"Uint8Array for procedure request\").map_err(|e| e.throw(scope))?;\n\n    let request = bsatn::from_slice::<st_http::Request>(request.get_contents(&mut []))\n        .map_err(|e| TypeError(format!(\"failed to decode http request: {e}\")).throw(scope))?;\n\n    let request_body = args.get(1);\n    let request_body = if let Ok(s) = request_body.try_cast::<v8::String>() {\n        Bytes::from(s.to_rust_string_lossy(scope))\n    } else {\n        let bytes = cast!(\n            scope,\n            request_body,\n            v8::Uint8Array,\n            \"Uint8Array or string for request body\"\n        )\n        .map_err(|e| e.throw(scope))?;\n        Bytes::copy_from_slice(bytes.get_contents(&mut []))\n    };\n\n    let env = get_env(scope)?;\n\n    let fut = env.instance_env.http_request(request, request_body)?;\n\n    let rt = tokio::runtime::Handle::current();\n    let (response, response_body) = rt.block_on(fut)?;\n\n    let response = bsatn::to_vec(&response).expect(\"failed to serialize `HttpResponse`\");\n    let response = make_uint8array(scope, response);\n\n    let response_body = match response_body.try_into_mut() {\n        Ok(bytes_mut) => make_uint8array(scope, Box::new(bytes_mut)),\n        Err(bytes) => make_uint8array(scope, Vec::from(bytes)),\n    };\n\n    Ok(v8::Array::new_with_elements(\n        scope,\n        &[response.into(), response_body.into()],\n    ))\n}\n\npub fn procedure_start_mut_tx(\n    scope: &mut PinScope<'_, '_>,\n    _args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u64> {\n    let env = get_env(scope)?;\n\n    env.instance_env.start_mutable_tx()?;\n\n    let timestamp = Timestamp::now().to_micros_since_unix_epoch() as u64;\n\n    Ok(timestamp)\n}\n\npub fn procedure_abort_mut_tx(scope: &mut PinScope<'_, '_>, _args: FunctionCallbackArguments<'_>) -> SysCallResult<()> {\n    let env = get_env(scope)?;\n\n    env.instance_env.abort_mutable_tx()?;\n    Ok(())\n}\n\npub fn procedure_commit_mut_tx(\n    scope: &mut PinScope<'_, '_>,\n    _args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<()> {\n    let tx = {\n        let env = get_env(scope)?;\n        env.instance_env.take_mutable_tx_for_commit()?\n    };\n\n    let hooks = get_registered_hooks(scope).ok_or_else(|| {\n        TypeError(\"module hooks are unavailable while committing a procedure transaction\").throw(scope)\n    })?;\n    let module_def = get_env(scope)?.module_def().ok_or_else(|| {\n        TypeError(\"module definition is unavailable while committing a procedure transaction\").throw(scope)\n    })?;\n    let tx = refresh_views(scope, tx, &hooks, &module_def)?;\n    get_env(scope)?.instance_env.commit_procedure_tx(tx)?;\n\n    Ok(())\n}\n\n/// Refresh all views made stale by a procedure `tx`.\n///\n/// This runs each pending view call in the same mutable transaction and writes the refreshed rows\n/// into the corresponding backing view tables. If any step fails (missing metadata, view execution,\n/// row decoding, SQL execution, or materialization), this method rolls back `tx` and returns an error.\n///\n/// On success, it returns the same transaction handle so the caller can commit it.\nfn refresh_views(\n    scope: &mut PinScope<'_, '_>,\n    tx: MutTxId,\n    hooks: &HookFunctions<'_>,\n    module_def: &ModuleDef,\n) -> SysCallResult<MutTxId> {\n    let views_for_refresh = tx.views_for_refresh().cloned().collect::<Vec<_>>();\n    let stdb = get_env(scope)?.instance_env.relational_db().clone();\n    let database_identity = *get_env(scope)?.instance_env.database_identity();\n    let mut tx_slot = get_env(scope)?.instance_env.tx.clone();\n    let mut tx = Some(tx);\n\n    for view_call in views_for_refresh {\n        let res: SysCallResult<()> = (|| {\n            let view_def = module_def\n                .get_view_by_id(view_call.fn_ptr, view_call.sender.is_none())\n                .ok_or_else(|| {\n                    TypeError(format!(\n                        \"view with fn_ptr `{}` not found while refreshing procedure transaction\",\n                        view_call.fn_ptr\n                    ))\n                    .throw(scope)\n                })?;\n\n            let current_tx = tx.take().expect(\"procedure tx missing during view refresh\");\n            let (next_tx, call_result) =\n                tx_slot.set(current_tx, || call_view(scope, hooks, &view_call, &view_def.name));\n            tx = Some(next_tx);\n            let return_data = call_result?;\n\n            let typespace = module_def.typespace();\n            let row_product_type = typespace\n                .resolve(view_def.product_type_ref)\n                .resolve_refs()\n                .map_err(|err| {\n                    TypeError(format!(\n                        \"failed resolving row type for refreshed view `{}`: {err}\",\n                        view_def.name\n                    ))\n                    .throw(scope)\n                })?\n                .into_product()\n                .map_err(|_| {\n                    TypeError(format!(\n                        \"failed resolving row product type for refreshed view `{}`\",\n                        view_def.name\n                    ))\n                    .throw(scope)\n                })?;\n\n            let rows = match ViewResult::from_return_data(return_data).map_err(|err| {\n                TypeError(format!(\n                    \"failed parsing result for refreshed view `{}`: {err}\",\n                    view_def.name\n                ))\n                .throw(scope)\n            })? {\n                ViewResult::Rows(bytes) => {\n                    deserialize_view_rows(view_def.product_type_ref, bytes, typespace).map_err(NodesError::from)?\n                }\n                ViewResult::RawSql(query) => run_query_for_view(\n                    tx.as_mut().expect(\"procedure tx missing while running view query\"),\n                    &query,\n                    &row_product_type,\n                    &view_call,\n                    database_identity,\n                )\n                .map_err(|err| {\n                    TypeError(format!(\n                        \"failed running query for refreshed view `{}`: {err}\",\n                        view_def.name\n                    ))\n                    .throw(scope)\n                })?,\n            };\n\n            match view_call.sender {\n                Some(sender) => stdb\n                    .materialize_view(\n                        tx.as_mut()\n                            .expect(\"procedure tx missing while materializing authenticated view\"),\n                        view_call.table_id,\n                        sender,\n                        rows,\n                    )\n                    .map_err(NodesError::from)?,\n                None => stdb\n                    .materialize_anonymous_view(\n                        tx.as_mut()\n                            .expect(\"procedure tx missing while materializing anonymous view\"),\n                        view_call.table_id,\n                        rows,\n                    )\n                    .map_err(NodesError::from)?,\n            }\n\n            Ok(())\n        })();\n\n        if let Err(err) = res {\n            let tx = tx.expect(\"procedure tx missing while rolling back failed view refresh\");\n            get_env(scope)?.instance_env.rollback_procedure_tx(tx);\n            return Err(err);\n        }\n    }\n\n    Ok(tx.expect(\"procedure tx missing after refreshing views\"))\n}\n\n/// Execute a view and return its payload.\n///\n/// This helper is used by [`refresh_views`] while a procedure transaction is being committed.\n/// It temporarily sets the active function type to the target view for dependency tracking,\n/// invokes the applicable JS hook, restores the previous function type, and returns [`ViewReturnData`].\nfn call_view(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    view_call: &ViewCallInfo,\n    view_name: &Identifier,\n) -> SysCallResult<ViewReturnData> {\n    let prev_func_type = get_env(scope)?\n        .instance_env\n        .swap_func_type(FuncCallType::View(view_call.clone()));\n\n    let result = {\n        let args = crate::host::ArgsTuple::nullary();\n        match view_call.sender {\n            Some(sender) => call_call_view(\n                scope,\n                hooks,\n                ViewOp {\n                    name: view_name,\n                    view_id: view_call.view_id,\n                    table_id: view_call.table_id,\n                    fn_ptr: view_call.fn_ptr,\n                    args: &args,\n                    sender: &sender,\n                    timestamp: Timestamp::now(),\n                },\n            ),\n            None => call_call_view_anon(\n                scope,\n                hooks,\n                AnonymousViewOp {\n                    name: view_name,\n                    view_id: view_call.view_id,\n                    table_id: view_call.table_id,\n                    fn_ptr: view_call.fn_ptr,\n                    args: &args,\n                    timestamp: Timestamp::now(),\n                },\n            ),\n        }\n    };\n\n    get_env(scope)?.instance_env.swap_func_type(prev_func_type);\n\n    result.map_err(|err| match err {\n        ErrorOrException::Err(err) => TypeError(format!(\n            \"failed executing refreshed view `{}` during procedure commit: {err}\",\n            view_name\n        ))\n        .throw(scope)\n        .into(),\n        ErrorOrException::Exception(exc) => exc.into(),\n    })\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/syscall/hooks.rs",
    "content": "use std::cell::OnceCell;\nuse std::rc::Rc;\n\nuse enum_map::EnumMap;\nuse v8::{Context, Function, Local, Object, PinScope};\n\nuse super::AbiVersion;\nuse crate::host::v8::de::property;\nuse crate::host::v8::error::ExcResult;\nuse crate::host::v8::error::Throwable;\nuse crate::host::v8::error::TypeError;\nuse crate::host::v8::from_value::cast;\nuse crate::host::v8::string::StringConst;\n\n/// Returns the hook function `name` on `hooks_obj`.\npub(super) fn get_hook_function<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    hooks_obj: Local<'_, Object>,\n    name: &'static StringConst,\n) -> ExcResult<Local<'scope, Function>> {\n    let key = name.string(scope);\n    let object = property(scope, hooks_obj, key)?;\n    cast!(scope, object, Function, \"module function hook `{}`\", name.as_str()).map_err(|e| e.throw(scope))\n}\n\n/// Registers all the module function `hooks`\n/// and sets the given `AbiVersion` to `abi`.\npub(super) fn set_hook_slots(\n    scope: &mut PinScope<'_, '_>,\n    abi: AbiVersion,\n    hooks: &[(ModuleHookKey, Local<'_, Function>)],\n) -> ExcResult<()> {\n    let ctx = scope.get_current_context();\n    let hooks_info = HooksInfo::get_or_create(&ctx, abi)\n        .map_err(|_| TypeError(\"cannot call `register_hooks` from different versions\").throw(scope))?;\n    for &(hook, func) in hooks {\n        hooks_info\n            .register(hook)\n            .map_err(|_| TypeError(\"cannot call `register_hooks` multiple times\").throw(scope))?;\n        ctx.set_embedder_data(hook.to_slot_index(), func.into());\n    }\n    Ok(())\n}\n\n/// Registers the hooks for the current module instance so they can be reconstructed later\n/// from procedure syscalls that only have access to the current V8 context.\npub(in super::super) fn set_registered_hooks(scope: &mut PinScope<'_, '_>, hooks: &HookFunctions<'_>) -> ExcResult<()> {\n    let mut to_register = vec![\n        (ModuleHookKey::DescribeModule, hooks.describe_module),\n        (ModuleHookKey::CallReducer, hooks.call_reducer),\n    ];\n    if let Some(call_view) = hooks.call_view {\n        to_register.push((ModuleHookKey::CallView, call_view));\n    }\n    if let Some(call_view_anon) = hooks.call_view_anon {\n        to_register.push((ModuleHookKey::CallAnonymousView, call_view_anon));\n    }\n    if let Some(call_procedure) = hooks.call_procedure {\n        to_register.push((ModuleHookKey::CallProcedure, call_procedure));\n    }\n    if let Some(get_error_constructor) = hooks.get_error_constructor {\n        to_register.push((ModuleHookKey::GetErrorConstructor, get_error_constructor));\n    }\n    if let Some(sender_error_class) = hooks.sender_error_class {\n        to_register.push((ModuleHookKey::SenderErrorClass, sender_error_class));\n    }\n\n    set_hook_slots(scope, hooks.abi, &to_register)?;\n\n    let ctx = scope.get_current_context();\n    ctx.set_embedder_data(RECV_SLOT_INDEX, hooks.recv);\n\n    Ok(())\n}\n\n#[derive(enum_map::Enum, Copy, Clone)]\npub(in super::super) enum ModuleHookKey {\n    DescribeModule,\n    CallReducer,\n    CallView,\n    CallAnonymousView,\n    CallProcedure,\n    GetErrorConstructor,\n    SenderErrorClass,\n}\n\nimpl ModuleHookKey {\n    /// Returns the index for the slot that holds the module function hook.\n    /// The index is passed to `v8::Context::{get,set}_embedder_data`.\n    fn to_slot_index(self) -> i32 {\n        self as i32\n    }\n}\n\n/// Context embedder slot holding the receiver (`this`) value used for hook calls.\nconst RECV_SLOT_INDEX: i32 = ModuleHookKey::SenderErrorClass as i32 + 1;\n\n/// Holds the `AbiVersion` used by the module\n/// and the module hooks registered by the module\n/// for that version.\nstruct HooksInfo {\n    abi: AbiVersion,\n    registered: EnumMap<ModuleHookKey, OnceCell<()>>,\n}\n\nimpl HooksInfo {\n    /// Returns, and possibly creates, the [`HooksInfo`] stored in `ctx`.\n    ///\n    /// Returns an error if `abi` doesn't match the abi version in the\n    /// already existing `HooksInfo`.\n    fn get_or_create(ctx: &Context, abi: AbiVersion) -> Result<Rc<Self>, ()> {\n        match ctx.get_slot::<Self>() {\n            Some(this) if this.abi == abi => Ok(this),\n            Some(_) => Err(()),\n            None => {\n                let this = Rc::new(Self {\n                    abi,\n                    registered: EnumMap::default(),\n                });\n                ctx.set_slot(this.clone());\n                Ok(this)\n            }\n        }\n    }\n\n    /// Mark down the given `hook` as registered, returning an error if it already was.\n    fn register(&self, hook: ModuleHookKey) -> Result<(), ()> {\n        self.registered[hook].set(())\n    }\n}\n\n#[derive(Copy, Clone)]\n/// The actual callable module hook functions and their abi version.\npub(in super::super) struct HookFunctions<'scope> {\n    pub abi: AbiVersion,\n    /// The `this` variable to pass to the hook functions.\n    pub recv: Local<'scope, v8::Value>,\n    /// describe_module and call_reducer existed in v1.0, but everything else is `Option`al\n    pub describe_module: Local<'scope, Function>,\n    pub get_error_constructor: Option<Local<'scope, Function>>,\n    pub sender_error_class: Option<Local<'scope, Function>>,\n    pub call_reducer: Local<'scope, Function>,\n    pub call_view: Option<Local<'scope, Function>>,\n    pub call_view_anon: Option<Local<'scope, Function>>,\n    pub call_procedure: Option<Local<'scope, Function>>,\n}\n\n/// Returns the hook function previously registered in [`register_hooks`].\npub(in super::super) fn get_registered_hooks<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n) -> Option<HookFunctions<'scope>> {\n    let ctx = scope.get_current_context();\n    let hooks = ctx.get_slot::<HooksInfo>()?;\n\n    let get = |hook: ModuleHookKey| {\n        hooks.registered[hook].get().map(|()| {\n            ctx.get_embedder_data(scope, hook.to_slot_index())\n                .expect(\"if the hook is registered it must have been set\")\n                .cast()\n        })\n    };\n\n    Some(HookFunctions {\n        abi: hooks.abi,\n        recv: ctx\n            .get_embedder_data(scope, RECV_SLOT_INDEX)\n            .unwrap_or_else(|| v8::undefined(scope).into()),\n        describe_module: get(ModuleHookKey::DescribeModule)?,\n        get_error_constructor: get(ModuleHookKey::GetErrorConstructor),\n        sender_error_class: get(ModuleHookKey::SenderErrorClass),\n        call_reducer: get(ModuleHookKey::CallReducer)?,\n        call_view: get(ModuleHookKey::CallView),\n        call_view_anon: get(ModuleHookKey::CallAnonymousView),\n        call_procedure: get(ModuleHookKey::CallProcedure),\n    })\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/syscall/mod.rs",
    "content": "use super::de::scratch_buf;\nuse super::error::{ErrorOrException, ExcResult, ExceptionThrown, PinTryCatch, Throwable, TypeError};\nuse super::exception_already_thrown;\nuse crate::host::wasm_common::abi::parse_abi_version;\nuse crate::host::wasm_common::module_host_actor::{\n    AnonymousViewOp, ExecutionError, ReducerOp, ReducerResult, ViewOp, ViewReturnData,\n};\nuse spacetimedb_lib::VersionTuple;\nuse v8::{callback_scope, ArrayBuffer, Context, FixedArray, Local, Module, PinScope};\n\nmod common;\nmod hooks;\nmod v1;\nmod v2;\n\npub(super) use self::hooks::{get_registered_hooks, set_registered_hooks, HookFunctions, ModuleHookKey};\n\n/// The return type of a module -> host syscall.\npub(super) type FnRet<'scope> = ExcResult<Local<'scope, v8::Value>>;\n\n/// The version of the ABI that is exposed to V8.\n#[derive(Copy, Clone, PartialEq, Eq)]\npub enum AbiVersion {\n    V1,\n    V2,\n}\n\n/// A dependency resolver for the user's module\n/// that will resolve `spacetimedb_sys` to a module that exposes the ABI.\npub(super) fn resolve_sys_module<'scope>(\n    context: Local<'scope, Context>,\n    spec: Local<'scope, v8::String>,\n    _attrs: Local<'scope, FixedArray>,\n    _referrer: Local<'scope, Module>,\n) -> Option<Local<'scope, Module>> {\n    callback_scope!(unsafe scope, context);\n    resolve_sys_module_inner(scope, spec).ok()\n}\n\nfn resolve_sys_module_inner<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    spec: Local<'scope, v8::String>,\n) -> ExcResult<Local<'scope, Module>> {\n    let scratch = &mut scratch_buf::<32>();\n    let spec = spec.to_rust_cow_lossy(scope, scratch);\n\n    let generic_error = || TypeError(format!(\"Could not find module {spec:?}\"));\n\n    let (module, ver) = spec\n        .strip_prefix(\"spacetime:\")\n        .and_then(|spec| spec.split_once('@'))\n        .ok_or_else(|| generic_error().throw(scope))?;\n\n    let VersionTuple { major, minor } = parse_abi_version(ver)\n        .ok_or_else(|| TypeError(format!(\"Invalid version in module spec {spec:?}\")).throw(scope))?;\n\n    match module {\n        \"sys\" => match (major, minor) {\n            (1, 0) => Ok(v1::sys_v1_0(scope)),\n            (1, 1) => Ok(v1::sys_v1_1(scope)),\n            (1, 2) => Ok(v1::sys_v1_2(scope)),\n            (1, 3) => Ok(v1::sys_v1_3(scope)),\n            (2, 0) => Ok(v2::sys_v2_0(scope)),\n            _ => Err(TypeError(format!(\n                \"Could not import {spec:?}, likely because this module was built for a newer version of SpacetimeDB.\\n\\\n            It requires sys module v{major}.{minor}, but that version is not supported by the database.\"\n            ))\n            .throw(scope)),\n        },\n        _ => Err(generic_error().throw(scope)),\n    }\n}\n\n/// Calls the registered `__call_reducer__` function hook.\n///\n/// This handles any (future) ABI version differences.\npub(super) fn call_call_reducer<'scope>(\n    scope: &mut PinTryCatch<'scope, '_, '_, '_>,\n    hooks: &HookFunctions<'scope>,\n    op: ReducerOp<'_>,\n    reducer_args_buf: Local<'scope, ArrayBuffer>,\n) -> ExcResult<ReducerResult> {\n    match hooks.abi {\n        AbiVersion::V1 => v1::call_call_reducer(scope, hooks, op),\n        AbiVersion::V2 => v2::call_call_reducer(scope, hooks, op, reducer_args_buf),\n    }\n}\n\n/// Calls the registered `__call_view__` function hook.\n///\n/// This handles any (future) ABI version differences.\npub(super) fn call_call_view(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: ViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    match hooks.abi {\n        AbiVersion::V1 => v1::call_call_view(scope, hooks, op),\n        AbiVersion::V2 => v2::call_call_view(scope, hooks, op),\n    }\n}\n\n/// Calls the registered `__call_view_anon__` function hook.\n///\n/// This handles any (future) ABI version differences.\npub(super) fn call_call_view_anon(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: AnonymousViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    match hooks.abi {\n        AbiVersion::V1 => v1::call_call_view_anon(scope, hooks, op),\n        AbiVersion::V2 => v2::call_call_view_anon(scope, hooks, op),\n    }\n}\n\npub use self::common::{call_call_procedure, call_describe_module};\n\n/// Get the hooks for the module.\n///\n/// May use the module's exports if it's a v2+ module, or the registered global\n/// hooks if it's v1 module.\npub(super) fn get_hooks<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    exports_obj: Local<'_, v8::Object>,\n) -> Result<Option<HookFunctions<'scope>>, ErrorOrException<ExceptionThrown>> {\n    if let Some(hooks) = get_registered_hooks(scope) {\n        return Ok(Some(hooks));\n    }\n\n    let default = super::str_from_ident!(default).string(scope);\n    let default_export = exports_obj\n        .get(scope, default.into())\n        .ok_or_else(exception_already_thrown)?;\n    if default_export.is_null_or_undefined() {\n        return Ok(None);\n    }\n    let hooks = v2::get_hooks_from_default_export(scope, default_export, exports_obj)?\n        .ok_or_else(|| anyhow::anyhow!(\"default export is not a Schema object\"))?;\n    Ok(Some(hooks))\n}\n\n/// Process the thrown exception value into an `ExecutionError`.\npub(super) fn process_thrown_exception(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    exc: Local<'_, v8::Value>,\n) -> ExcResult<Option<ExecutionError>> {\n    match hooks.abi {\n        AbiVersion::V1 => Ok(None),\n        AbiVersion::V2 => v2::process_thrown_exception(scope, hooks, exc),\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/syscall/v1.rs",
    "content": "use super::super::de::deserialize_js;\nuse super::super::error::{\n    collapse_exc_thrown, terminate_execution, throw_if_terminated, BufferTooSmall, ErrorOrException, ExcResult,\n    ExceptionThrown, ExceptionValue, RangeError, SysCallError, SysCallResult,\n};\nuse super::super::from_value::cast;\nuse super::super::ser::serialize_to_js;\nuse super::super::string::{str_from_ident, StringConst};\nuse super::super::{call_free_fun, env_on_isolate, Throwable};\nuse super::common::{\n    console_log, console_timer_end, console_timer_start, datastore_index_scan_range_bsatn_inner,\n    datastore_table_row_count, datastore_table_scan_bsatn, deserialize_row_iter_idx, get_env, get_jwt_payload,\n    identity, index_id_from_name, procedure_abort_mut_tx, procedure_commit_mut_tx, procedure_http_request,\n    procedure_start_mut_tx, row_iter_bsatn_close, table_id_from_name, volatile_nonatomic_schedule_immediate,\n};\nuse super::hooks::HookFunctions;\nuse super::hooks::{get_hook_function, set_hook_slots};\nuse super::{AbiVersion, FnRet, ModuleHookKey};\nuse crate::error::NodesError;\nuse crate::host::instance_env::InstanceEnv;\nuse crate::host::wasm_common::err_to_errno_and_log;\nuse crate::host::wasm_common::instrumentation::span;\nuse crate::host::wasm_common::module_host_actor::{AnonymousViewOp, ReducerOp, ReducerResult, ViewOp, ViewReturnData};\nuse crate::host::AbiCall;\nuse anyhow::Context;\nuse bytes::Bytes;\nuse spacetimedb_primitives::{ColId, IndexId, ReducerId, TableId, ViewFnPtr};\nuse spacetimedb_sats::Serialize;\nuse v8::{\n    callback_scope, ConstructorBehavior, Function, FunctionCallbackArguments, Local, Module, Object, PinCallbackScope,\n    PinScope,\n};\n\nmacro_rules! create_synthetic_module {\n    ($scope:expr, $module_name:expr $(, ($wrapper:ident, $abi_call:expr, $fun:ident))* $(,)?) => {{\n        let export_names = &[$(str_from_ident!($fun).string($scope)),*];\n        let eval_steps = |context, module| {\n            callback_scope!(unsafe scope, context);\n            $(\n                register_module_fun(scope, &module, str_from_ident!($fun), |s, a| {\n                    $wrapper($abi_call, s, a, $fun)\n                })?;\n            )*\n\n            Some(v8::undefined(scope).into())\n        };\n\n        Module::create_synthetic_module(\n            $scope,\n            const { StringConst::new($module_name) }.string($scope),\n            export_names,\n            eval_steps,\n        )\n    }}\n}\n\n/// Registers all module -> host syscalls in the JS module `spacetimedb_sys`.\npub(super) fn sys_v1_0<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {\n    use register_hooks_v1_0 as register_hooks;\n    create_synthetic_module!(\n        scope,\n        \"spacetime:sys@1.0\",\n        (with_nothing, (), register_hooks),\n        (with_sys_result_ret, AbiCall::TableIdFromName, table_id_from_name),\n        (with_sys_result_ret, AbiCall::IndexIdFromName, index_id_from_name),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreTableRowCount,\n            datastore_table_row_count\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreTableScanBsatn,\n            datastore_table_scan_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreIndexScanRangeBsatn,\n            datastore_index_scan_range_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::RowIterBsatnAdvance,\n            row_iter_bsatn_advance\n        ),\n        (with_sys_result_noret, AbiCall::RowIterBsatnClose, row_iter_bsatn_close),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreInsertBsatn,\n            datastore_insert_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreUpdateBsatn,\n            datastore_update_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreDeleteByIndexScanRangeBsatn,\n            datastore_delete_by_index_scan_range_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreDeleteAllByEqBsatn,\n            datastore_delete_all_by_eq_bsatn\n        ),\n        (\n            with_sys_result_noret,\n            AbiCall::VolatileNonatomicScheduleImmediate,\n            volatile_nonatomic_schedule_immediate\n        ),\n        (with_sys_result_noret, AbiCall::ConsoleLog, console_log),\n        (with_sys_result_ret, AbiCall::ConsoleTimerStart, console_timer_start),\n        (with_sys_result_noret, AbiCall::ConsoleTimerEnd, console_timer_end),\n        (with_sys_result_ret, AbiCall::Identity, identity),\n        (with_sys_result_ret, AbiCall::GetJwt, get_jwt_payload),\n    )\n}\n\npub(super) fn sys_v1_1<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {\n    use register_hooks_v1_1 as register_hooks;\n    create_synthetic_module!(scope, \"spacetime:sys@1.1\", (with_nothing, (), register_hooks))\n}\n\npub(super) fn sys_v1_2<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {\n    use register_hooks_v1_2 as register_hooks;\n    create_synthetic_module!(\n        scope,\n        \"spacetime:sys@1.2\",\n        (with_nothing, (), register_hooks),\n        (\n            with_sys_result_value,\n            AbiCall::ProcedureHttpRequest,\n            procedure_http_request\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::ProcedureStartMutTransaction,\n            procedure_start_mut_tx\n        ),\n        (\n            with_sys_result_noret,\n            AbiCall::ProcedureAbortMutTransaction,\n            procedure_abort_mut_tx\n        ),\n        (\n            with_sys_result_noret,\n            AbiCall::ProcedureCommitMutTransaction,\n            procedure_commit_mut_tx\n        ),\n    )\n}\n\npub(super) fn sys_v1_3<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {\n    create_synthetic_module!(\n        scope,\n        \"spacetime:sys@1.2\",\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreIndexScanPointBsatn,\n            datastore_index_scan_point_bsatn\n        ),\n        (\n            with_sys_result_ret,\n            AbiCall::DatastoreDeleteByIndexScanPointBsatn,\n            datastore_delete_by_index_scan_point_bsatn\n        ),\n    )\n}\n\n/// Registers a function in `module`\n/// where the function has `name` and does `body`.\nfn register_module_fun(\n    scope: &mut PinCallbackScope<'_, '_>,\n    module: &Local<'_, Module>,\n    name: &'static StringConst,\n    body: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> FnRet<'scope>,\n) -> Option<bool> {\n    // Convert the name.\n    let name = name.string(scope);\n\n    // Convert the function.\n    let fun = Function::builder(adapt_fun(body)).constructor_behavior(ConstructorBehavior::Throw);\n    let fun = fun.build(scope)?.into();\n\n    // Set the export on the module.\n    module.set_synthetic_module_export(scope, name, fun)\n}\n\n/// Adapts `fun`, which returns a [`Value`] to one that works on [`v8::ReturnValue`].\nfn adapt_fun(\n    fun: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> FnRet<'scope>,\n) -> impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>, v8::ReturnValue) {\n    move |scope, args, mut rv| {\n        if throw_if_terminated(scope) {\n            return;\n        }\n\n        // Set the result `value` on success.\n        if let Ok(value) = fun(scope, args) {\n            rv.set(value);\n        }\n    }\n}\n\n/// Wraps `run` in [`with_span`] and returns the return value of `run` to JS.\n/// Handles [`SysCallError`] if it occurs by throwing exceptions into JS.\nfn with_sys_result_ret<'scope, O: Serialize>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    run: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> SysCallResult<O>,\n) -> FnRet<'scope> {\n    match with_span(abi_call, scope, args, run) {\n        Ok(ret) => serialize_to_js(scope, &ret),\n        Err(err) => Err(handle_sys_call_error(abi_call, scope, err)),\n    }\n}\n\n/// Wraps `run` in [`with_span`] and returns undefined to JS.\n/// Handles [`SysCallError`] if it occurs by throwing exceptions into JS.\nfn with_sys_result_noret<'scope>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    run: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> SysCallResult<()>,\n) -> FnRet<'scope> {\n    match with_span(abi_call, scope, args, run) {\n        Ok(()) => Ok(v8::undefined(scope).into()),\n        Err(err) => Err(handle_sys_call_error(abi_call, scope, err)),\n    }\n}\n\n/// Wraps `run` in [`with_span`] and returns undefined to JS.\n/// Handles [`SysCallError`] if it occurs by throwing exceptions into JS.\nfn with_sys_result_value<'scope, O>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    run: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> SysCallResult<Local<'scope, O>>,\n) -> FnRet<'scope>\nwhere\n    Local<'scope, O>: Into<Local<'scope, v8::Value>>,\n{\n    match with_span(abi_call, scope, args, run) {\n        Ok(v) => Ok(v.into()),\n        Err(err) => Err(handle_sys_call_error(abi_call, scope, err)),\n    }\n}\n\n/// A higher order function conforming to the interface of [`with_sys_result`] and [`with_span`].\nfn with_nothing<'scope>(\n    (): (),\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    run: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> FnRet<'scope>,\n) -> FnRet<'scope> {\n    run(scope, args)\n}\n\n/// Tracks the span of `body` under the label `abi_call`.\nfn with_span<'scope, T, E: From<ExceptionThrown>>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    body: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> Result<T, E>,\n) -> Result<T, E> {\n    // Start the span.\n    let span_start = span::CallSpanStart::new(abi_call);\n\n    // Call `fun` with `args` in `scope`.\n    let result = body(scope, args);\n\n    // Track the span of this call.\n    let span = span_start.end();\n    if let Some(env) = env_on_isolate(scope) {\n        span::record_span(&mut env.call_times, span);\n    }\n\n    result\n}\n\n/// Converts a `SysCallError` into a `ExceptionThrown`.\npub(super) fn handle_sys_call_error<'scope>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    err: SysCallError,\n) -> ExceptionThrown {\n    const ENV_NOT_SET: u16 = 1;\n    match err {\n        SysCallError::NoEnv => code_error(scope, ENV_NOT_SET),\n        SysCallError::Errno(errno) => code_error(scope, errno.get()),\n        SysCallError::OutOfBounds => RangeError(\"length argument was out of bounds for `ArrayBuffer`\").throw(scope),\n        SysCallError::Exception(exc) => exc,\n        SysCallError::Error(error) => throw_nodes_error(abi_call, scope, error),\n    }\n}\n\n/// A catchable error code thrown in callbacks\n/// to indicate bad arguments to a syscall.\n#[derive(Serialize)]\nstruct CodeError {\n    __code_error__: u16,\n}\n\nimpl CodeError {\n    /// Create a code error from a code.\n    fn from_code<'scope>(scope: &PinScope<'scope, '_>, __code_error__: u16) -> ExcResult<ExceptionValue<'scope>> {\n        let error = Self { __code_error__ };\n        serialize_to_js(scope, &error).map(ExceptionValue)\n    }\n}\n\n/// A catchable error code thrown in callbacks\n/// to indicate bad arguments to a syscall.\n#[derive(Serialize)]\nstruct CodeMessageError {\n    __code_error__: u16,\n    __error_message__: String,\n}\n\nimpl CodeMessageError {\n    /// Create a code error from a code.\n    fn from_code<'scope>(\n        scope: &PinScope<'scope, '_>,\n        __code_error__: u16,\n        __error_message__: String,\n    ) -> ExcResult<ExceptionValue<'scope>> {\n        let error = Self {\n            __code_error__,\n            __error_message__,\n        };\n        serialize_to_js(scope, &error).map(ExceptionValue)\n    }\n}\n\n/// Throws `{ __code_error__: code }`.\nfn code_error(scope: &PinScope<'_, '_>, code: u16) -> ExceptionThrown {\n    let res = CodeError::from_code(scope, code);\n    collapse_exc_thrown(scope, res)\n}\n\n/// Turns a [`NodesError`] into a thrown exception.\nfn throw_nodes_error(abi_call: AbiCall, scope: &mut PinScope<'_, '_>, error: NodesError) -> ExceptionThrown {\n    let res = match err_to_errno_and_log::<u16>(abi_call, error) {\n        Ok((code, None)) => CodeError::from_code(scope, code),\n        Ok((code, Some(message))) => CodeMessageError::from_code(scope, code, message),\n        Err(err) => terminate_execution(scope, &err),\n    };\n    collapse_exc_thrown(scope, res)\n}\n\n/// Module ABI that registers the functions called by the host.\n///\n/// # Signature\n///\n/// ```ignore\n/// register_hooks(hooks: {\n///     __describe_module__: () => u8[];\n///     __call_reducer__: (\n///         reducer_id: u32,\n///         sender: u256,\n///         conn_id: u128,\n///         timestamp: i64,\n///         args_buf: u8[]\n///     ) => { tag: 'ok' } | { tag: 'err'; value: string };\n/// }): void\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `i64` is `bigint` in JS restricted to signed 64-bit integers.\n/// - `u128` is `bigint` in JS restricted to unsigned 128-bit integers.\n/// - `u256` is `bigint` in JS restricted to unsigned 256-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws a `TypeError` if:\n/// - `hooks` is not an object that has functions `__describe_module__` and `__call_reducer__`.\nfn register_hooks_v1_0<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackArguments<'_>) -> FnRet<'scope> {\n    // Convert `hooks` to an object.\n    let hooks = cast!(scope, args.get(0), Object, \"hooks object\").map_err(|e| e.throw(scope))?;\n\n    let describe_module = get_hook_function(scope, hooks, str_from_ident!(__describe_module__))?;\n    let call_reducer = get_hook_function(scope, hooks, str_from_ident!(__call_reducer__))?;\n\n    // Set the hooks.\n    set_hook_slots(\n        scope,\n        AbiVersion::V1,\n        &[\n            (ModuleHookKey::DescribeModule, describe_module),\n            (ModuleHookKey::CallReducer, call_reducer),\n        ],\n    )?;\n\n    Ok(v8::undefined(scope).into())\n}\n\n/// Module ABI that registers the functions called by the host.\n///\n/// # Signature\n///\n/// ```ignore\n/// register_hooks(hooks: {\n///     __call_view__(view_id: u32, sender: u256, args: u8[]): u8[];\n///     __call_view_anon__(view_id: u32, args: u8[]): u8[];\n/// }): void\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u32` is `bigint` in JS restricted to unsigned 32-bit integers.\n/// - `u256` is `bigint` in JS restricted to unsigned 256-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws a `TypeError` if:\n/// - `hooks` is not an object that has the correct functions.\nfn register_hooks_v1_1<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackArguments<'_>) -> FnRet<'scope> {\n    // Convert `hooks` to an object.\n    let hooks = cast!(scope, args.get(0), Object, \"hooks object\").map_err(|e| e.throw(scope))?;\n\n    let call_view = get_hook_function(scope, hooks, str_from_ident!(__call_view__))?;\n    let call_view_anon = get_hook_function(scope, hooks, str_from_ident!(__call_view_anon__))?;\n\n    // Set the hooks.\n    set_hook_slots(\n        scope,\n        AbiVersion::V1,\n        &[\n            (ModuleHookKey::CallView, call_view),\n            (ModuleHookKey::CallAnonymousView, call_view_anon),\n        ],\n    )?;\n\n    Ok(v8::undefined(scope).into())\n}\n\n/// Module ABI that registers the functions called by the host.\n///\n/// # Signature\n///\n/// ```ignore\n/// export function register_hooks(hooks: {\n///     __call_procedure__(\n///         id: u32,\n///         sender: u256,\n///         connection_id: u128,\n///         timestamp: u64,\n///         args: Uint8Array\n///     ): Uint8Array;\n/// }): void;\n/// ```\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws a `TypeError` if:\n/// - `hooks` is not an object that has the correct functions.\nfn register_hooks_v1_2<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackArguments<'_>) -> FnRet<'scope> {\n    // Convert `hooks` to an object.\n    let hooks = cast!(scope, args.get(0), Object, \"hooks object\").map_err(|e| e.throw(scope))?;\n\n    let call_procedure = get_hook_function(scope, hooks, str_from_ident!(__call_procedure__))?;\n\n    // Set the hooks.\n    set_hook_slots(scope, AbiVersion::V1, &[(ModuleHookKey::CallProcedure, call_procedure)])?;\n\n    Ok(v8::undefined(scope).into())\n}\n\n/// Calls the `__call_reducer__` function `fun`.\npub(super) fn call_call_reducer(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: ReducerOp<'_>,\n) -> ExcResult<ReducerResult> {\n    let ReducerOp {\n        id: ReducerId(reducer_id),\n        name: _,\n        caller_identity: sender,\n        caller_connection_id: conn_id,\n        timestamp,\n        args: reducer_args,\n    } = op;\n    // Serialize the arguments.\n    let reducer_id = serialize_to_js(scope, &reducer_id)?;\n    let sender = serialize_to_js(scope, &sender.to_u256())?;\n    let conn_id: v8::Local<'_, v8::Value> = serialize_to_js(scope, &conn_id.to_u128())?;\n    let timestamp = serialize_to_js(scope, &timestamp.to_micros_since_unix_epoch())?;\n    let reducer_args = serialize_to_js(scope, reducer_args.get_bsatn())?;\n    let args = &[reducer_id, sender, conn_id, timestamp, reducer_args];\n\n    // Call the function.\n    let ret = call_free_fun(scope, hooks.call_reducer, args)?;\n\n    // Deserialize the user result, discarding any OK payload for now.\n    let res: Result<(), Box<str>> = deserialize_js(scope, ret)?;\n    let user_res = res.map(|_| None);\n\n    Ok(user_res)\n}\n\n/// Calls the `__call_view__` function `fun`.\npub(super) fn call_call_view(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: ViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    let fun = hooks.call_view.context(\"`__call_view__` was never defined\")?;\n\n    let ViewOp {\n        fn_ptr: ViewFnPtr(view_id),\n        view_id: _,\n        table_id: _,\n        name: _,\n        sender,\n        timestamp: _,\n        args: view_args,\n    } = op;\n    // Serialize the arguments.\n    let view_id = serialize_to_js(scope, &view_id)?;\n    let sender = serialize_to_js(scope, &sender.to_u256())?;\n    let view_args = serialize_to_js(scope, view_args.get_bsatn())?;\n    let args = &[view_id, sender, view_args];\n\n    // Call the function.\n    let ret = call_free_fun(scope, fun, args)?;\n\n    // The original version returned a byte array with the encoded rows.\n    if ret.is_typed_array() && ret.is_uint8_array() {\n        // This is the original format, which just returns the raw bytes.\n        let ret =\n            cast!(scope, ret, v8::Uint8Array, \"bytes return from `__call_view_anon__`\").map_err(|e| e.throw(scope))?;\n        let bytes = ret.get_contents(&mut []);\n\n        return Ok(ViewReturnData::Rows(Bytes::copy_from_slice(bytes)));\n    };\n\n    // The newer version returns an object with a `data` field containing the bytes.\n    let ret = cast!(scope, ret, v8::Object, \"object return from `__call_view_anon__`\").map_err(|e| e.throw(scope))?;\n\n    let Some(data_key) = v8::String::new(scope, \"data\") else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\"error creating a v8 string\")));\n    };\n    let Some(data_val) = ret.get(scope, data_key.into()) else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\n            \"data key not found in return object\"\n        )));\n    };\n\n    let ret = cast!(\n        scope,\n        data_val,\n        v8::Uint8Array,\n        \"bytes in the `data` field returned from `__call_view_anon__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n    let bytes = ret.get_contents(&mut []);\n\n    Ok(ViewReturnData::HeaderFirst(Bytes::copy_from_slice(bytes)))\n}\n\n/// Calls the `__call_view_anon__` function `fun`.\npub(super) fn call_call_view_anon(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: AnonymousViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    let fun = hooks.call_view_anon.context(\"`__call_view_anon__` was never defined\")?;\n\n    let AnonymousViewOp {\n        fn_ptr: ViewFnPtr(view_id),\n        view_id: _,\n        table_id: _,\n        name: _,\n        timestamp: _,\n        args: view_args,\n    } = op;\n    // Serialize the arguments.\n    let view_id = serialize_to_js(scope, &view_id)?;\n    let view_args = serialize_to_js(scope, view_args.get_bsatn())?;\n    let args = &[view_id, view_args];\n\n    // Call the function.\n    let ret = call_free_fun(scope, fun, args)?;\n\n    if ret.is_typed_array() && ret.is_uint8_array() {\n        // This is the original format, which just returns the raw bytes.\n        let ret =\n            cast!(scope, ret, v8::Uint8Array, \"bytes return from `__call_view_anon__`\").map_err(|e| e.throw(scope))?;\n        let bytes = ret.get_contents(&mut []);\n\n        // We are pretending this was sent with the new format.\n        return Ok(ViewReturnData::Rows(Bytes::copy_from_slice(bytes)));\n    };\n\n    let ret = cast!(\n        scope,\n        ret,\n        v8::Object,\n        \"bytes or object return from `__call_view_anon__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n\n    let Some(data_key) = v8::String::new(scope, \"data\") else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\"error creating a v8 string\")));\n    };\n    let Some(data_val) = ret.get(scope, data_key.into()) else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\n            \"data key not found in return object\"\n        )));\n    };\n\n    let ret = cast!(\n        scope,\n        data_val,\n        v8::Uint8Array,\n        \"bytes in the `data` field returned from `__call_view_anon__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n    let bytes = ret.get_contents(&mut []);\n\n    Ok(ViewReturnData::HeaderFirst(Bytes::copy_from_slice(bytes)))\n}\n\n/// Module ABI that finds all rows in the index identified by `index_id`,\n/// according to `prefix`, `rstart`, and `rend`.\n///\n/// The index itself has a schema/type.\n/// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s\n/// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`\n/// where the `AlgebraicValue`s are wrapped in `Bound`.\n/// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.\n///\n/// Matching is then defined by equating `prefix`\n/// to the initial `prefix_elems` columns of the index\n/// and then imposing `rstart` as the starting bound\n/// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.\n/// Remaining columns of the index are then unbounded.\n/// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),\n/// in which case this becomes a ranged index scan on a single-col index\n/// or even a full table scan if `rstart` and `rend` are both unbounded.\n///\n/// The relevant table for the index is found implicitly via the `index_id`,\n/// which is unique for the module.\n///\n/// On success, the iterator handle is returned.\n/// This handle can be advanced by [`row_iter_bsatn_advance`].\n///\n/// # Non-obvious queries\n///\n/// For an index on columns `[a, b, c]`:\n///\n/// - `a = x, b = y` is encoded as a prefix `[x, y]`\n///   and a range `Range::Unbounded`,\n///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.\n/// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`\n///   and a  range `rstart = rend = Range::Inclusive(z)`.\n/// - A sorted full scan is encoded as an empty prefix\n///   and a range `Range::Unbounded`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_index_scan_range_bsatn(\n///     index_id: u32,\n///     prefix: u8[],\n///     prefix_elems: u16,\n///     rstart: u8[],\n///     rend: u8[],\n/// ) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n/// - `u64` is `bigint` in JS restricted to unsigned 64-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the iterator handle.\n/// This handle can be advanced by [`row_iter_bsatn_advance`].\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n///\n/// Throws a `TypeError` if:\n/// - `index_id` is not a `u32`.\n/// - `prefix`, `rstart`, and `rend` are not arrays of `u8`s.\n/// - `prefix_elems` is not a `u16`.\nfn datastore_index_scan_range_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let prefix: &[u8] = deserialize_js(scope, args.get(1))?;\n    let prefix_elems: ColId = deserialize_js(scope, args.get(2))?;\n    let rstart: &[u8] = deserialize_js(scope, args.get(3))?;\n    let rend: &[u8] = deserialize_js(scope, args.get(4))?;\n\n    datastore_index_scan_range_bsatn_inner(scope, index_id, prefix, prefix_elems, rstart, rend)\n}\n\n/// Module ABI that reads rows from the given iterator registered under `iter`.\n///\n/// Takes rows from the iterator with id `iter`\n/// and returns them encoded in the BSATN format.\n///\n/// The rows returned take up at most `buffer_max_len` bytes.\n/// A row is never broken up between calls.\n///\n/// Aside from the BSATN,\n/// the function also returns `true` when the iterator been exhausted\n/// and there are no more rows to read.\n/// This leads to the iterator being immediately destroyed.\n/// Conversely, `false` is returned if there are more rows to read.\n/// Note that the host is free to reuse allocations in a pool,\n/// destroying the handle logically does not entail that memory is necessarily reclaimed.\n///\n/// # Signature\n///\n/// ```ignore\n/// row_iter_bsatn_advance(iter: u32, buffer_max_len: u32) -> (boolean, u8[]) throws\n///     { __code_error__: NO_SUCH_ITER } | { __buffer_too_small__: number }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns `(exhausted: boolean, rows_bsatn: u8[])` where:\n/// - `exhausted` is `true` if there are no more rows to read,\n/// - `rows_bsatn` are the BSATN-encoded row bytes, concatenated.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_ITER`]\n///   when `iter` is not a valid iterator.\n///\n/// Throws `{ __buffer_too_small__: number }`\n/// when there are rows left but they cannot fit in `buffer`.\n/// When this occurs, `__buffer_too_small__` contains the size of the next item in the iterator.\n/// To make progress, the caller should call `row_iter_bsatn_advance`\n/// with `buffer_max_len >= __buffer_too_small__` and try again.\n///\n/// Throws a `TypeError` if:\n/// - `iter` and `buffer_max_len` are not `u32`s.\nfn row_iter_bsatn_advance<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<(bool, Vec<u8>)> {\n    let row_iter_idx = deserialize_row_iter_idx(scope, args.get(0))?;\n    let buffer_max_len: u32 = deserialize_js(scope, args.get(1))?;\n\n    // Retrieve the iterator by `row_iter_idx`, or error.\n    let env = get_env(scope)?;\n    let iter = env.iters.get_mut(row_iter_idx).ok_or(SysCallError::NO_SUCH_ITER)?;\n\n    // Allocate a buffer with `buffer_max_len` capacity.\n    let mut buffer = vec![0; buffer_max_len as usize];\n    // Fill the buffer as much as possible.\n    let written = InstanceEnv::fill_buffer_from_iter(iter, &mut buffer, &mut env.chunk_pool);\n    buffer.truncate(written);\n\n    let next_buf_len = iter.as_slice().first().map(|v| v.len());\n    let done = match (written, next_buf_len) {\n        // Nothing was written and the iterator is not exhausted.\n        (0, Some(min_len)) => {\n            let min_len = min_len.try_into().unwrap();\n            let exc = BufferTooSmall::from_requirement(scope, min_len)?;\n            return Err(exc.throw(scope).into());\n        }\n        // The iterator is exhausted, destroy it, and tell the caller.\n        (_, None) => {\n            env.iters.take(row_iter_idx);\n            true\n        }\n        // Something was written, but the iterator is not exhausted.\n        (_, Some(_)) => false,\n    };\n    Ok((done, buffer))\n}\n\n/// Module ABI that inserts a row into the table identified by `table_id`,\n/// where the `row` is an array of bytes.\n///\n/// The byte array `row` must be a BSATN-encoded `ProductValue`\n/// typed at the table's `ProductType` row-schema.\n///\n/// To handle auto-incrementing columns,\n/// when the call is successful,\n/// the an array of bytes is returned, containing the generated sequence values.\n/// These values are written as a BSATN-encoded `pv: ProductValue`.\n/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n/// When the table has no sequences,\n/// this implies that the `pv`, and thus `row`, will be empty.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_insert_bsatn(table_id: u32, row: u8[]) -> u8[] throws {\n///     __code_error__:\n///           NOT_IN_TRANSACTION\n///         | NOT_SUCH_TABLE\n///         | BSATN_DECODE_ERROR\n///         | UNIQUE_ALREADY_EXISTS\n///         | SCHEDULE_AT_DELAY_TOO_LONG\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns the generated sequence values encoded in BSATN (see above).\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n/// - [`spacetimedb_primitives::errno::NOT_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n/// - [`spacetimedb_primitives::errno::`BSATN_DECODE_ERROR`]\n///   when `row` cannot be decoded to a `ProductValue`.\n///   typed at the `ProductType` the table's schema specifies.\n/// - [`spacetimedb_primitives::errno::`UNIQUE_ALREADY_EXISTS`]\n///   when inserting `row` would violate a unique constraint.\n/// - [`spacetimedb_primitives::errno::`SCHEDULE_AT_DELAY_TOO_LONG`]\n///   when the delay specified in the row was too long.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `row` is not an array of `u8`s.\nfn datastore_insert_bsatn(scope: &mut PinScope<'_, '_>, args: FunctionCallbackArguments<'_>) -> SysCallResult<Vec<u8>> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let mut row: Vec<u8> = deserialize_js(scope, args.get(1))?;\n\n    // Insert the row into the DB and write back the generated column values.\n    let row_len = get_env(scope)?.instance_env.insert(table_id, &mut row)?;\n    row.truncate(row_len);\n\n    Ok(row)\n}\n\n/// Module ABI that updates a row into the table identified by `table_id`,\n/// where the `row` is an array of bytes.\n///\n/// The byte array `row` must be a BSATN-encoded `ProductValue`\n/// typed at the table's `ProductType` row-schema.\n///\n/// The row to update is found by projecting `row`\n/// to the type of the *unique* index identified by `index_id`.\n/// If no row is found, the error `NO_SUCH_ROW` is returned.\n///\n/// To handle auto-incrementing columns,\n/// when the call is successful,\n/// the `row` is written back to with the generated sequence values.\n/// These values are written as a BSATN-encoded `pv: ProductValue`.\n/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n/// When the table has no sequences,\n/// this implies that the `pv`, and thus `row`, will be empty.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_update_bsatn(table_id: u32, index_id: u32, row: u8[]) -> u8[] throws {\n///     __code_error__:\n///         NOT_IN_TRANSACTION\n///       | NOT_SUCH_TABLE\n///       | NO_SUCH_INDEX\n///       | INDEX_NOT_UNIQUE\n///       | BSATN_DECODE_ERROR\n///       | NO_SUCH_ROW\n///       | UNIQUE_ALREADY_EXISTS\n///       | SCHEDULE_AT_DELAY_TOO_LONG\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns the generated sequence values encoded in BSATN (see above).\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n/// - [`spacetimedb_primitives::errno::NOT_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n/// - [`spacetimedb_primitives::errno::INDEX_NOT_UNIQUE`]\n///   when the index was not unique.\n/// - [`spacetimedb_primitives::errno::`BSATN_DECODE_ERROR`]\n///   when `row` cannot be decoded to a `ProductValue`.\n///   typed at the `ProductType` the table's schema specifies\n///   or when it cannot be projected to the index identified by `index_id`.\n/// - [`spacetimedb_primitives::errno::`NO_SUCH_ROW`]\n///   when the row was not found in the unique index.\n/// - [`spacetimedb_primitives::errno::`UNIQUE_ALREADY_EXISTS`]\n///   when inserting `row` would violate a unique constraint.\n/// - [`spacetimedb_primitives::errno::`SCHEDULE_AT_DELAY_TOO_LONG`]\n///   when the delay specified in the row was too long.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `row` is not an array of `u8`s.\nfn datastore_update_bsatn(scope: &mut PinScope<'_, '_>, args: FunctionCallbackArguments<'_>) -> SysCallResult<Vec<u8>> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let index_id: IndexId = deserialize_js(scope, args.get(1))?;\n    let mut row: Vec<u8> = deserialize_js(scope, args.get(2))?;\n\n    // Insert the row into the DB and write back the generated column values.\n    let row_len = get_env(scope)?.instance_env.update(table_id, index_id, &mut row)?;\n    row.truncate(row_len);\n\n    Ok(row)\n}\n\n/// Module ABI that deletes all rows found in the index identified by `index_id`,\n/// according to `prefix`, `rstart`, and `rend`.\n///\n/// This syscall will delete all the rows found by\n/// [`datastore_index_scan_range_bsatn`] with the same arguments passed,\n/// including `prefix_elems`.\n/// See `datastore_index_scan_range_bsatn` for details.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_index_scan_range_bsatn(\n///     index_id: u32,\n///     prefix: u8[],\n///     prefix_elems: u16,\n///     rstart: u8[],\n///     rend: u8[],\n/// ) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the number of rows deleted.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `prefix`, `rstart`, and `rend` are not arrays of `u8`s.\n/// - `prefix_elems` is not a `u16`.\nfn datastore_delete_by_index_scan_range_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let mut prefix: &[u8] = deserialize_js(scope, args.get(1))?;\n    let prefix_elems: ColId = deserialize_js(scope, args.get(2))?;\n    let rstart: &[u8] = deserialize_js(scope, args.get(3))?;\n    let rend: &[u8] = deserialize_js(scope, args.get(4))?;\n\n    if prefix_elems.idx() == 0 {\n        prefix = &[];\n    }\n\n    // Delete the relevant rows.\n    let count = get_env(scope)?\n        .instance_env\n        .datastore_delete_by_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)?;\n    Ok(count)\n}\n\n/// Module ABI that deletes those rows, in the table identified by `table_id`,\n/// that match any row in `relation`.\n///\n/// Matching is defined by first BSATN-decoding\n/// the array of bytes `relation` to a `Vec<ProductValue>`\n/// according to the row schema of the table\n/// and then using `Ord for AlgebraicValue`.\n/// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n/// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_delete_all_by_eq_bsatn(table_id: u32, relation: u8[]) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the number of rows deleted.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `relation` cannot be decoded to `Vec<ProductValue>`\n///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `relation` is not an array of `u8`s.\nfn datastore_delete_all_by_eq_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let relation: &[u8] = deserialize_js(scope, args.get(1))?;\n\n    let count = get_env(scope)?\n        .instance_env\n        .datastore_delete_all_by_eq_bsatn(table_id, relation)?;\n    Ok(count)\n}\n\nfn datastore_index_scan_point_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let point: &[u8] = deserialize_js(scope, args.get(1))?;\n\n    let env = get_env(scope)?;\n\n    // Find the relevant rows.\n    let chunks = env\n        .instance_env\n        .datastore_index_scan_point_bsatn_chunks(&mut env.chunk_pool, index_id, point)?;\n\n    // Insert the encoded + concatenated rows into a new buffer and return its id.\n    Ok(env.iters.insert(chunks.into_iter()).0)\n}\n\nfn datastore_delete_by_index_scan_point_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let point: &[u8] = deserialize_js(scope, args.get(1))?;\n\n    // Delete the relevant rows.\n    let count = get_env(scope)?\n        .instance_env\n        .datastore_delete_by_index_scan_point_bsatn(index_id, point)?;\n    Ok(count)\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/syscall/v2.rs",
    "content": "use super::super::de::deserialize_js;\nuse super::super::error::{\n    collapse_exc_thrown, exception_already_thrown, terminate_execution, ExceptionValue, PinTryCatch, RangeError,\n    SysCallError,\n};\nuse super::super::error::{\n    throw_if_terminated, BufferTooSmall, ErrorOrException, ExcResult, ExceptionThrown, SysCallResult, TypeError, OOB,\n};\nuse super::super::from_value::cast;\nuse super::super::ser::serialize_to_js;\nuse super::super::string::IntoJsString;\nuse super::super::string::{str_from_ident, StringConst};\nuse super::super::to_value::ToValue;\nuse super::super::util::{make_dataview, make_uint8array};\nuse super::super::{call_free_fun, call_recv_fun, env_on_isolate, Throwable};\nuse super::common::{\n    console_log, console_timer_end, console_timer_start, datastore_index_scan_range_bsatn_inner,\n    datastore_table_row_count, datastore_table_scan_bsatn, deserialize_row_iter_idx, get_env, identity,\n    index_id_from_name, procedure_abort_mut_tx, procedure_commit_mut_tx, procedure_http_request,\n    procedure_start_mut_tx, row_iter_bsatn_close, table_id_from_name, volatile_nonatomic_schedule_immediate,\n};\nuse super::hooks::get_hook_function;\nuse super::hooks::HookFunctions;\nuse super::{set_registered_hooks, AbiVersion};\nuse crate::error::NodesError;\nuse crate::host::instance_env::InstanceEnv;\nuse crate::host::wasm_common::instrumentation::span;\nuse crate::host::wasm_common::module_host_actor::{\n    AnonymousViewOp, ExecutionError, ReducerOp, ReducerResult, ViewOp, ViewReturnData,\n};\nuse crate::host::wasm_common::{err_to_errno_and_log, RowIterIdx};\nuse crate::host::{AbiCall, ArgsTuple};\nuse anyhow::Context;\nuse bytes::Bytes;\nuse core::slice;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_primitives::{errno, ColId, IndexId, ReducerId, TableId, ViewFnPtr};\nuse spacetimedb_sats::u256;\nuse v8::{\n    callback_scope, ArrayBuffer, ConstructorBehavior, DataView, Function, FunctionCallbackArguments, Local, Module,\n    Object, PinScope, Value,\n};\n\nmacro_rules! create_synthetic_module {\n    ($scope:expr, $module_name:expr $(, ($($fun:tt)*))* $(,)?) => {{\n        let export_names = &[$(synthetic_module_export_name!($($fun)*).string($scope)),*];\n        let eval_steps = |context: Local<v8::Context>, module: Local<Module>| {\n            callback_scope!(unsafe scope, context);\n            $(register_synthetic_module_export!(scope, &module, ($($fun)*));)*\n\n            Some(v8::undefined(scope).into())\n        };\n\n        Module::create_synthetic_module(\n            $scope,\n            const { StringConst::new($module_name) }.string($scope),\n            export_names,\n            eval_steps,\n        )\n    }};\n}\nmacro_rules! synthetic_module_export_name {\n    // function exports\n    ($wrapper:ident, $abi_call:expr, $fun:ident) => {\n        str_from_ident!($fun)\n    };\n    // value exports\n    ($name:ident = $value:expr) => {\n        str_from_ident!($name)\n    };\n}\nmacro_rules! register_synthetic_module_export {\n    // function exports\n    ($scope:expr, $module:expr, ($wrapper:ident, $abi_call:expr, $fun:ident)) => {\n        register_module_fun($scope, $module, str_from_ident!($fun), |s, a, rv| {\n            $wrapper($abi_call, s, a, rv, $fun)\n        })?;\n    };\n    // value exports\n    ($scope:expr, $module:expr, ($name:ident = $value:expr)) => {\n        let name = str_from_ident!($name).string($scope);\n        let value = $value($scope);\n        $module.set_synthetic_module_export($scope, name, value.into())?;\n    };\n}\n\n/// Registers all module -> host syscalls in the JS module `spacetimedb_sys`.\npub(super) fn sys_v2_0<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {\n    create_synthetic_module!(\n        scope,\n        \"spacetime:sys@2.0\",\n        (moduleHooks = hooks_symbol),\n        (with_sys_result, AbiCall::TableIdFromName, table_id_from_name),\n        (with_sys_result, AbiCall::IndexIdFromName, index_id_from_name),\n        (\n            with_sys_result,\n            AbiCall::DatastoreTableRowCount,\n            datastore_table_row_count\n        ),\n        (\n            with_sys_result,\n            AbiCall::DatastoreTableScanBsatn,\n            datastore_table_scan_bsatn\n        ),\n        (\n            with_sys_result,\n            AbiCall::DatastoreIndexScanRangeBsatn,\n            datastore_index_scan_range_bsatn\n        ),\n        (with_sys_result, AbiCall::RowIterBsatnAdvance, row_iter_bsatn_advance),\n        (with_sys_result, AbiCall::RowIterBsatnClose, row_iter_bsatn_close),\n        (with_sys_result, AbiCall::DatastoreInsertBsatn, datastore_insert_bsatn),\n        (with_sys_result, AbiCall::DatastoreUpdateBsatn, datastore_update_bsatn),\n        (\n            with_sys_result,\n            AbiCall::DatastoreDeleteByIndexScanRangeBsatn,\n            datastore_delete_by_index_scan_range_bsatn\n        ),\n        (\n            with_sys_result,\n            AbiCall::DatastoreDeleteAllByEqBsatn,\n            datastore_delete_all_by_eq_bsatn\n        ),\n        (\n            with_sys_result,\n            AbiCall::VolatileNonatomicScheduleImmediate,\n            volatile_nonatomic_schedule_immediate\n        ),\n        (with_sys_result, AbiCall::ConsoleLog, console_log),\n        (with_sys_result, AbiCall::ConsoleTimerStart, console_timer_start),\n        (with_sys_result, AbiCall::ConsoleTimerEnd, console_timer_end),\n        (with_sys_result, AbiCall::Identity, identity),\n        (with_sys_result, AbiCall::GetJwt, get_jwt_payload),\n        (with_sys_result, AbiCall::ProcedureHttpRequest, procedure_http_request),\n        (\n            with_sys_result,\n            AbiCall::ProcedureStartMutTransaction,\n            procedure_start_mut_tx\n        ),\n        (\n            with_sys_result,\n            AbiCall::ProcedureAbortMutTransaction,\n            procedure_abort_mut_tx\n        ),\n        (\n            with_sys_result,\n            AbiCall::ProcedureCommitMutTransaction,\n            procedure_commit_mut_tx\n        ),\n        (\n            with_sys_result,\n            AbiCall::DatastoreIndexScanPointBsatn,\n            datastore_index_scan_point_bsatn\n        ),\n        (\n            with_sys_result,\n            AbiCall::DatastoreDeleteByIndexScanPointBsatn,\n            datastore_delete_by_index_scan_point_bsatn\n        ),\n    )\n}\n\n/// Registers a function in `module`\n/// where the function has `name` and does `body`.\nfn register_module_fun(\n    scope: &mut PinScope<'_, '_>,\n    module: &Local<'_, Module>,\n    name: &'static StringConst,\n    body: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>, v8::ReturnValue<'_>),\n) -> Option<bool> {\n    // Convert the name.\n    let name = name.string(scope);\n\n    // Convert the function.\n    let fun = Function::builder(adapt_fun(body)).constructor_behavior(ConstructorBehavior::Throw);\n    let fun = fun.build(scope)?.into();\n\n    // Set the export on the module.\n    module.set_synthetic_module_export(scope, name, fun)\n}\n\n/// Adapts `fun` to check for termination\nfn adapt_fun(\n    fun: impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>, v8::ReturnValue<'_>),\n) -> impl Copy + for<'scope> Fn(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>, v8::ReturnValue<'_>) {\n    move |scope, args, rv| {\n        if throw_if_terminated(scope) {\n            return;\n        }\n\n        fun(scope, args, rv)\n    }\n}\n\ntrait JsReturnValue {\n    fn set_return(self, scope: &mut PinScope<'_, '_>, rv: v8::ReturnValue<'_>);\n}\n\nmacro_rules! impl_returnvalue {\n    ($t:ty, $set:ident) => {\n        impl_returnvalue!($t, (me, _, rv) => rv.$set(me));\n    };\n    ($t:ty, self$($field:tt)*) => {\n        impl_returnvalue!($t, (me, scope, rv) => me$($field)*.set_return(scope, rv));\n    };\n    ($t:ty, ($me:pat, $scope:pat, $rv:ident) => $body:expr) => {\n        impl JsReturnValue for $t {\n            fn set_return(self, $scope: &mut PinScope<'_, '_>, #[allow(unused_mut)] mut $rv: v8::ReturnValue<'_>) {\n                let $me = self;\n                $body\n            }\n        }\n    };\n}\n\nimpl_returnvalue!((), ((), _, rv) => rv.set_undefined());\nimpl_returnvalue!(u32, set_uint32);\nimpl_returnvalue!(i32, set_int32);\nimpl_returnvalue!(u64, (me, scope, rv) => rv.set(me.to_value(scope)));\nimpl_returnvalue!(u128, (me, scope, rv) => rv.set(me.to_value(scope)));\nimpl_returnvalue!(u256, (me, scope, rv) => rv.set(me.to_value(scope)));\n\nimpl_returnvalue!(TableId, self.0);\nimpl_returnvalue!(IndexId, self.0);\nimpl_returnvalue!(RowIterIdx, self.0);\nimpl_returnvalue!(Identity, self.to_u256());\n\nimpl<'s, T> JsReturnValue for Local<'s, T>\nwhere\n    Self: Into<Local<'s, v8::Value>>,\n{\n    fn set_return(self, _scope: &mut PinScope<'_, '_>, mut rv: v8::ReturnValue<'_>) {\n        rv.set(self.into())\n    }\n}\n\n/// Wraps `run` in [`with_span`] and returns the return value of `run` to JS.\n/// Handles [`SysCallError`] if it occurs by throwing exceptions into JS.\nfn with_sys_result<'scope, O: JsReturnValue>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n    rv: v8::ReturnValue<'_>,\n    run: impl FnOnce(&mut PinScope<'scope, '_>, FunctionCallbackArguments<'scope>) -> SysCallResult<O>,\n) {\n    // Start the span.\n    let span_start = span::CallSpanStart::new(abi_call);\n\n    // Call `fun` with `args` in `scope`.\n    let result = run(scope, args).map_err(|err| handle_sys_call_error(abi_call, scope, err));\n\n    // Track the span of this call.\n    let span = span_start.end();\n    if let Some(env) = env_on_isolate(scope) {\n        span::record_span(&mut env.call_times, span);\n    }\n\n    if let Ok(ret) = result {\n        ret.set_return(scope, rv)\n    }\n}\n\n/// Converts a `SysCallError` into a `ExceptionThrown`.\npub(super) fn handle_sys_call_error<'scope>(\n    abi_call: AbiCall,\n    scope: &mut PinScope<'scope, '_>,\n    err: SysCallError,\n) -> ExceptionThrown {\n    const ENV_NOT_SET: u16 = 1;\n    match err {\n        SysCallError::NoEnv => {\n            let msg = \"cannot call this function during module initialization\";\n            let exc = code_error(scope, ENV_NOT_SET, Some(msg));\n            collapse_exc_thrown(scope, exc)\n        }\n        SysCallError::Errno(errno) => {\n            let exc = code_error(scope, errno.get(), None);\n            collapse_exc_thrown(scope, exc)\n        }\n        SysCallError::OutOfBounds => RangeError(\"length argument was out of bounds for `ArrayBuffer`\").throw(scope),\n        SysCallError::Exception(exc) => exc,\n        SysCallError::Error(error) => throw_nodes_error(abi_call, scope, error),\n    }\n}\n\nmacro_rules! def_errnos {\n    ($($err_name:ident($errno:literal, $errmsg:literal),)*) => {\n        /// Get the error message for an error number, if it exists.\n        const fn strerror(num: u16) -> Option<&'static StringConst> {\n            match num {\n                $($errno => Some(const { &StringConst::new($errmsg) }),)*\n                _ => None,\n            }\n        }\n    };\n}\nerrno::errnos!(def_errnos);\n\n/// Construct a `SpacetimeHostError` value given an errno and message.\nfn code_error<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    code: u16,\n    message: Option<&str>,\n) -> ExcResult<ExceptionValue<'scope>> {\n    const DEFAULT_MESSAGE: &StringConst = &StringConst::new(\"Unknown error\");\n    let message = match message {\n        Some(msg) => msg.into_string(scope).map_err(|e| e.into_range_error().throw(scope))?,\n        None => strerror(code).unwrap_or(DEFAULT_MESSAGE).string(scope),\n    };\n    let exc = match scope\n        .get_current_context()\n        .get_embedder_data(scope, super::super::GET_ERROR_CONSTRUCTOR_SLOT)\n    {\n        // get_error_constructor: (code: number) => new (message: string) => Error\n        Some(get_error_constructor) => {\n            let errno_value = code.to_value(scope);\n            let cls = call_free_fun(scope, get_error_constructor.cast(), &[errno_value])?;\n            let cls = cast!(scope, cls, v8::Function, \"function\").map_err(|e| e.throw(scope))?;\n            cls.new_instance(scope, &[message.into()])\n                .ok_or_else(exception_already_thrown)?\n                .into()\n        }\n        None => v8::Exception::error(scope, message),\n    };\n    Ok(ExceptionValue(exc))\n}\n\n/// Turns a [`NodesError`] into a thrown exception.\nfn throw_nodes_error(abi_call: AbiCall, scope: &mut PinScope<'_, '_>, error: NodesError) -> ExceptionThrown {\n    let res = match err_to_errno_and_log::<u16>(abi_call, error) {\n        Ok((code, message)) => code_error(scope, code, message.as_deref()),\n        Err(err) => terminate_execution(scope, &err),\n    };\n    collapse_exc_thrown(scope, res)\n}\n\n/// Module ABI that registers the functions called by the host.\n///\n/// # Signature\n///\n/// ```ignore\n/// register_hooks(hooks: {\n///     __describe_module__: () => u8[];\n///     __call_reducer__: (\n///         reducer_id: u32,\n///         sender: u256,\n///         conn_id: u128,\n///         timestamp: i64,\n///         args_buf: u8[]\n///     ) => { tag: 'ok' } | { tag: 'err'; value: string };\n/// }): void\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `i64` is `bigint` in JS restricted to signed 64-bit integers.\n/// - `u128` is `bigint` in JS restricted to unsigned 128-bit integers.\n/// - `u256` is `bigint` in JS restricted to unsigned 256-bit integers.\n///\n/// # Returns\n///\n/// Returns nothing.\n///\n/// # Throws\n///\n/// Throws a `TypeError` if:\n/// - `hooks` is not an object that has functions `__describe_module__` and `__call_reducer__`.\npub fn get_hooks_from_default_export<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    default_export: Local<'_, Value>,\n    exports_obj: Local<'_, Object>,\n) -> ExcResult<Option<HookFunctions<'scope>>> {\n    // Convert `hooks` to an object.\n    let hooks_fn = default_export\n        .try_cast::<Object>()\n        .ok()\n        .map(|obj| {\n            let symbol = hooks_symbol(scope);\n            obj.get(scope, symbol.into()).ok_or_else(exception_already_thrown)\n        })\n        .transpose()?;\n    let Some(hooks_fn) = hooks_fn else { return Ok(None) };\n    let hooks_fn = cast!(scope, hooks_fn, Function, \"hooks function\").map_err(|e| e.throw(scope))?;\n    let hooks = call_recv_fun(scope, hooks_fn, default_export, &[exports_obj.into()])?;\n    let hooks = cast!(scope, hooks, Object, \"hooks object\").map_err(|e| e.throw(scope))?;\n\n    let describe_module = get_hook_function(scope, hooks, str_from_ident!(__describe_module__))?;\n    let get_error_constructor = get_hook_function(scope, hooks, str_from_ident!(__get_error_constructor__))?;\n    let sender_error_class = get_hook_function(scope, hooks, str_from_ident!(__sender_error_class__))?;\n    let call_reducer = get_hook_function(scope, hooks, str_from_ident!(__call_reducer__))?;\n    let call_view = get_hook_function(scope, hooks, str_from_ident!(__call_view__))?;\n    let call_view_anon = get_hook_function(scope, hooks, str_from_ident!(__call_view_anon__))?;\n    let call_procedure = get_hook_function(scope, hooks, str_from_ident!(__call_procedure__))?;\n\n    // Cache hooks in context slots so syscall-time code can reconstruct them.\n    let hooks = HookFunctions {\n        abi: AbiVersion::V2,\n        recv: hooks.into(),\n        describe_module,\n        get_error_constructor: Some(get_error_constructor),\n        sender_error_class: Some(sender_error_class),\n        call_reducer,\n        call_view: Some(call_view),\n        call_view_anon: Some(call_view_anon),\n        call_procedure: Some(call_procedure),\n    };\n    set_registered_hooks(scope, &hooks)?;\n    Ok(Some(hooks))\n}\n\nfn hooks_symbol<'scope>(scope: &PinScope<'scope, '_>) -> Local<'scope, v8::Symbol> {\n    const { StringConst::new(\"SpacetimeDB.moduleHooks.v2\") }.symbol(scope)\n}\n\n/// Calls the `__call_reducer__` function `fun`.\npub(super) fn call_call_reducer<'scope>(\n    scope: &mut PinTryCatch<'scope, '_, '_, '_>,\n    hooks: &HookFunctions<'scope>,\n    op: ReducerOp<'_>,\n    reducer_args_buf: Local<'scope, ArrayBuffer>,\n) -> ExcResult<ReducerResult> {\n    let ReducerOp {\n        id: ReducerId(reducer_id),\n        name: _,\n        caller_identity: sender,\n        caller_connection_id: conn_id,\n        timestamp,\n        args: reducer_args,\n    } = op;\n    // Serialize the arguments.\n    let reducer_id = serialize_to_js(scope, &reducer_id)?;\n    let sender = serialize_to_js(scope, &sender.to_u256())?;\n    let conn_id: v8::Local<'_, v8::Value> = serialize_to_js(scope, &conn_id.to_u128())?;\n    let timestamp = serialize_to_js(scope, &timestamp.to_micros_since_unix_epoch())?;\n    let reducer_args = reducer_args_to_value(scope, reducer_args, reducer_args_buf);\n\n    let args = &[reducer_id, sender, conn_id, timestamp, reducer_args];\n\n    match call_recv_fun(scope, hooks.call_reducer, hooks.recv, args) {\n        Ok(val) if val.is_undefined() => Ok(Ok(None)),\n        // TODO(reducer-return-values): replace error with deserialization\n        Ok(_) => Err(TypeError(\"Reducer returned a value other than `undefined`\").throw(scope)),\n        Err(e) => Err(e),\n    }\n}\n\n/// Process the thrown exception value into an `ExecutionError`.\npub(super) fn process_thrown_exception(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    exc: Local<'_, Value>,\n) -> ExcResult<Option<ExecutionError>> {\n    // if (typeof exc === \"object\" && exc instanceof SenderError)\n    if let Ok(exc) = exc.try_cast::<Object>()\n        && exc\n            .instance_of(scope, hooks.sender_error_class.unwrap().into())\n            .ok_or_else(exception_already_thrown)?\n    {\n        // let message = String(exc.message)\n        let key = str_from_ident!(message).string(scope);\n        let message = exc.get(scope, key.into()).ok_or_else(exception_already_thrown)?;\n        let message = message.to_string(scope).ok_or_else(exception_already_thrown)?;\n        return Ok(Some(ExecutionError::User(message.to_rust_string_lossy(scope).into())));\n    }\n    Ok(None)\n}\n\n/// Converts `args` into a `Value`.\nfn reducer_args_to_value<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: &ArgsTuple,\n    buffer: Local<'scope, ArrayBuffer>,\n) -> Local<'scope, Value> {\n    let reducer_args = &**args.get_bsatn();\n\n    let len = reducer_args.len();\n    let wrote = with_arraybuffer_mut(buffer, |buf| {\n        if len > buf.len() {\n            // Buffer is too small.\n            return false;\n        }\n        let dst = &mut buf[..len];\n        dst.copy_from_slice(reducer_args);\n        true\n    });\n\n    let dv = if wrote {\n        // Fall back to allocating new buffers.\n        DataView::new(scope, buffer, 0, len)\n    } else {\n        // Fall back to allocating new buffers.\n        make_dataview(scope, <Box<[u8]>>::from(reducer_args))\n    };\n\n    dv.into()\n}\n\n/// Calls the `__call_view__` function `fun`.\npub(super) fn call_call_view(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: ViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    let fun = hooks.call_view.context(\"`__call_view__` was never defined\")?;\n\n    let ViewOp {\n        fn_ptr: ViewFnPtr(view_id),\n        view_id: _,\n        table_id: _,\n        name: _,\n        sender,\n        timestamp: _,\n        args: view_args,\n    } = op;\n    // Serialize the arguments.\n    let view_id = serialize_to_js(scope, &view_id)?;\n    let sender = serialize_to_js(scope, &sender.to_u256())?;\n    let view_args = serialize_to_js(scope, view_args.get_bsatn())?;\n    let args = &[view_id, sender, view_args];\n\n    // Call the function.\n    let ret = call_recv_fun(scope, fun, hooks.recv, args)?;\n\n    // Returns an object with a `data` field containing the bytes.\n    let ret = cast!(scope, ret, v8::Object, \"object return from `__call_view_anon__`\").map_err(|e| e.throw(scope))?;\n\n    let Some(data_key) = v8::String::new(scope, \"data\") else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\"error creating a v8 string\")));\n    };\n    let Some(data_val) = ret.get(scope, data_key.into()) else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\n            \"data key not found in return object\"\n        )));\n    };\n\n    let ret = cast!(\n        scope,\n        data_val,\n        v8::Uint8Array,\n        \"bytes in the `data` field returned from `__call_view_anon__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n    let bytes = ret.get_contents(&mut []);\n\n    Ok(ViewReturnData::HeaderFirst(Bytes::copy_from_slice(bytes)))\n}\n\n/// Calls the `__call_view_anon__` function `fun`.\npub(super) fn call_call_view_anon(\n    scope: &mut PinScope<'_, '_>,\n    hooks: &HookFunctions<'_>,\n    op: AnonymousViewOp<'_>,\n) -> Result<ViewReturnData, ErrorOrException<ExceptionThrown>> {\n    let fun = hooks.call_view_anon.context(\"`__call_view_anon__` was never defined\")?;\n\n    let AnonymousViewOp {\n        fn_ptr: ViewFnPtr(view_id),\n        view_id: _,\n        table_id: _,\n        name: _,\n        timestamp: _,\n        args: view_args,\n    } = op;\n    // Serialize the arguments.\n    let view_id = serialize_to_js(scope, &view_id)?;\n    let view_args = serialize_to_js(scope, view_args.get_bsatn())?;\n    let args = &[view_id, view_args];\n\n    // Call the function.\n    let ret = call_recv_fun(scope, fun, hooks.recv, args)?;\n\n    let ret = cast!(scope, ret, v8::Object, \"object return from `__call_view_anon__`\").map_err(|e| e.throw(scope))?;\n\n    let Some(data_key) = v8::String::new(scope, \"data\") else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\"error creating a v8 string\")));\n    };\n    let Some(data_val) = ret.get(scope, data_key.into()) else {\n        return Err(ErrorOrException::Err(anyhow::anyhow!(\n            \"data key not found in return object\"\n        )));\n    };\n\n    let ret = cast!(\n        scope,\n        data_val,\n        v8::Uint8Array,\n        \"bytes in the `data` field returned from `__call_view_anon__`\"\n    )\n    .map_err(|e| e.throw(scope))?;\n    let bytes = ret.get_contents(&mut []);\n\n    Ok(ViewReturnData::HeaderFirst(Bytes::copy_from_slice(bytes)))\n}\n\n/// Module ABI that finds all rows in the index identified by `index_id`,\n/// according to `prefix`, `rstart`, and `rend` where:\n/// - `prefix = buffer[...prefix_len]`\n/// - `rstart = buffer[prefix_len..prefix_len + rstart_len]`\n/// - `rend = buffer[prefix_len + rstart_len..prefix_len + rstart_len + rend_len]`\n///   if `rend_len > 0`\n/// - `rend = rstart` if `rend_len == 0`\n///\n/// The index itself has a schema/type.\n/// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s\n/// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`\n/// where the `AlgebraicValue`s are wrapped in `Bound`.\n/// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.\n///\n/// Matching is then defined by equating `prefix`\n/// to the initial `prefix_elems` columns of the index\n/// and then imposing `rstart` as the starting bound\n/// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.\n/// Remaining columns of the index are then unbounded.\n/// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),\n/// in which case this becomes a ranged index scan on a single-col index\n/// or even a full table scan if `rstart` and `rend` are both unbounded.\n///\n/// The relevant table for the index is found implicitly via the `index_id`,\n/// which is unique for the module.\n///\n/// On success, the iterator handle is written to the `out` pointer.\n/// This handle can be advanced by [`row_iter_bsatn_advance`].\n///\n/// # Non-obvious queries\n///\n/// For an index on columns `[a, b, c]`:\n///\n/// - `a = x, b = y` is encoded as a prefix `[x, y]`\n///   and a range `Range::Unbounded`,\n///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.\n/// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`\n///   and a  range `rstart = rend = Range::Inclusive(z)`.\n/// - A sorted full scan is encoded as an empty prefix\n///   and a range `Range::Unbounded`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_index_scan_range_bsatn(\n///     index_id: u32,\n///     buffer: ArrayBuffer,\n///     prefix_len: u32,\n///     prefix_elems: u16,\n///     rstart_len: u32,\n///     rend_len: u32,\n/// ) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n/// - `u64` is `bigint` in JS restricted to unsigned 64-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the iterator handle.\n/// This handle can be advanced by [`row_iter_bsatn_advance`].\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n///\n/// Throws a `TypeError` if:\n/// - `index_id` is not a `u32`.\n/// - `prefix_len`, `rstart_len`, and `rend_len` are not `u32`s.\n/// - `prefix_elems` is not a `u16`.\n///\n/// Throws a `RangeError` if any of these are out of bounds of `buffer`:\n/// - `prefix_len`,\n/// - `prefix_len + rstart_len`,\n/// - or `prefix_len + rstart_len + rend_len`\nfn datastore_index_scan_range_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let buf = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n    let prefix_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n    let prefix_elems: ColId = deserialize_js(scope, args.get(3))?;\n    let rstart_len = deserialize_js::<u32>(scope, args.get(4))? as usize;\n    let rend_len = deserialize_js::<u32>(scope, args.get(5))? as usize;\n\n    with_arraybuffer(buf, |mut buf| {\n        let prefix = buf.split_off(..prefix_len).ok_or(OOB)?;\n        let rstart = buf.split_off(..rstart_len).ok_or(OOB)?;\n        let rend = if rend_len == 0 {\n            rstart\n        } else {\n            buf.split_off(..rend_len).ok_or(OOB)?\n        };\n\n        datastore_index_scan_range_bsatn_inner(scope, index_id, prefix, prefix_elems, rstart, rend)\n    })\n}\n\n/// Module ABI that reads rows from the given iterator registered under `iter`.\n///\n/// Takes rows from the iterator with id `iter`\n/// and returns them encoded in the BSATN format.\n///\n/// The rows returned take up at most `buffer_max_len` bytes.\n/// A row is never broken up between calls.\n///\n/// Aside from the BSATN,\n/// the function also returns `true` when the iterator been exhausted\n/// and there are no more rows to read.\n/// This leads to the iterator being immediately destroyed.\n/// Conversely, `false` is returned if there are more rows to read.\n/// Note that the host is free to reuse allocations in a pool,\n/// destroying the handle logically does not entail that memory is necessarily reclaimed.\n///\n/// # Signature\n///\n/// ```ignore\n/// row_iter_bsatn_advance(iter: u32, buffer_max_len: u32) -> (boolean, u8[]) throws\n///     { __code_error__: NO_SUCH_ITER } | { __buffer_too_small__: number }\n/// ```\n///\n/// # Types\n///\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns `(exhausted: boolean, rows_bsatn: u8[])` where:\n/// - `exhausted` is `true` if there are no more rows to read,\n/// - `rows_bsatn` are the BSATN-encoded row bytes, concatenated.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_ITER`]\n///   when `iter` is not a valid iterator.\n///\n/// Throws `{ __buffer_too_small__: number }`\n/// when there are rows left but they cannot fit in `buffer`.\n/// When this occurs, `__buffer_too_small__` contains the size of the next item in the iterator.\n/// To make progress, the caller should call `row_iter_bsatn_advance`\n/// with `buffer_max_len >= __buffer_too_small__` and try again.\n///\n/// Throws a `TypeError` if:\n/// - `iter` and `buffer_max_len` are not `u32`s.\nfn row_iter_bsatn_advance<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'scope>,\n) -> SysCallResult<i32> {\n    let row_iter_idx = deserialize_row_iter_idx(scope, args.get(0))?;\n    let array_buffer = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n\n    // Retrieve the iterator by `row_iter_idx`, or error.\n    let env = get_env(scope)?;\n    let iter = env.iters.get_mut(row_iter_idx).ok_or(SysCallError::NO_SUCH_ITER)?;\n\n    // Fill the buffer as much as possible.\n    let written = with_arraybuffer_mut(array_buffer, |buf| {\n        InstanceEnv::fill_buffer_from_iter(iter, buf, &mut env.chunk_pool)\n    });\n\n    let next_buf_len = iter.as_slice().first().map(|v| v.len());\n    let done = match (written, next_buf_len) {\n        // Nothing was written and the iterator is not exhausted.\n        (0, Some(min_len)) => {\n            let min_len = min_len.try_into().unwrap();\n            let exc = BufferTooSmall::from_requirement(scope, min_len)?;\n            return Err(exc.throw(scope).into());\n        }\n        // The iterator is exhausted, destroy it, and tell the caller.\n        (_, None) => {\n            env.iters.take(row_iter_idx);\n            true\n        }\n        // Something was written, but the iterator is not exhausted.\n        (_, Some(_)) => false,\n    };\n\n    let written: i32 = written.try_into().unwrap();\n    let out = if done { -written } else { written };\n    Ok(out)\n}\n\n/// Module ABI that inserts a row into the table identified by `table_id`,\n/// where the `row` is an array of bytes.\n///\n/// The byte array `row` must be a BSATN-encoded `ProductValue`\n/// typed at the table's `ProductType` row-schema.\n///\n/// To handle auto-incrementing columns,\n/// when the call is successful,\n/// the an array of bytes is returned, containing the generated sequence values.\n/// These values are written as a BSATN-encoded `pv: ProductValue`.\n/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n/// When the table has no sequences,\n/// this implies that the `pv`, and thus `row`, will be empty.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_insert_bsatn(table_id: u32, row: u8[]) -> u8[] throws {\n///     __code_error__:\n///           NOT_IN_TRANSACTION\n///         | NOT_SUCH_TABLE\n///         | BSATN_DECODE_ERROR\n///         | UNIQUE_ALREADY_EXISTS\n///         | SCHEDULE_AT_DELAY_TOO_LONG\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns the generated sequence values encoded in BSATN (see above).\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n/// - [`spacetimedb_primitives::errno::NOT_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n/// - [`spacetimedb_primitives::errno::`BSATN_DECODE_ERROR`]\n///   when `row` cannot be decoded to a `ProductValue`.\n///   typed at the `ProductType` the table's schema specifies.\n/// - [`spacetimedb_primitives::errno::`UNIQUE_ALREADY_EXISTS`]\n///   when inserting `row` would violate a unique constraint.\n/// - [`spacetimedb_primitives::errno::`SCHEDULE_AT_DELAY_TOO_LONG`]\n///   when the delay specified in the row was too long.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `row` is not an array of `u8`s.\nfn datastore_insert_bsatn<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let row_arr = cast!(scope, args.get(1), v8::ArrayBuffer, \"buffer\").map_err(|e| e.throw(scope))?;\n    let row_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n\n    // let row_offset = row_arr.byte_offset();\n    let row_len = with_arraybuffer_mut(row_arr, |buf| -> SysCallResult<_> {\n        let buf = buf.get_mut(..row_len).ok_or(OOB)?;\n        // Insert the row into the DB and write back the generated column values.\n        Ok(get_env(scope)?.instance_env.insert(table_id, buf)?)\n    })?;\n\n    Ok(row_len as u32)\n}\n\nfn with_arraybuffer<R>(buf: Local<'_, v8::ArrayBuffer>, f: impl FnOnce(&[u8]) -> R) -> R {\n    let buf: &[u8] = match buf.data().map(|p| p.cast::<u8>()) {\n        Some(data) => {\n            let ptr = data.as_ptr();\n            let len = buf.byte_length();\n\n            // SAFETY: We know `ptr` to be:\n            // - trivially properly aligned due to `u8`s alignment being 1.\n            // - non-null as it was derived from `NonNull`.\n            // - the range is within the allocation of `buffer`\n            //   as `len = buffer.byte_length()`,\n            //   so `buffer` is dereferenceable.\n            // - `ptr` will point to a valid `[u8]` as it was zero-initialized.\n            // - nothing is aliasing the pointer.\n            unsafe { slice::from_raw_parts(ptr, len) }\n        }\n        None => &[],\n    };\n    f(buf)\n}\n\nfn with_arraybuffer_mut<R>(buf: Local<'_, v8::ArrayBuffer>, f: impl FnOnce(&mut [u8]) -> R) -> R {\n    let buf: &mut [u8] = match buf.data().map(|p| p.cast::<u8>()) {\n        // SAFETY: see comment in `with_uint8array_mut`\n        Some(data) => {\n            let ptr = data.as_ptr();\n            let len = buf.byte_length();\n\n            // SAFETY: We know `ptr` to be:\n            // - trivially properly aligned due to `u8`s alignment being 1.\n            // - non-null as it was derived from `NonNull`.\n            // - the range is within the allocation of `buffer`\n            //   as `len = buffer.byte_length()`,\n            //   so `buffer` is dereferenceable.\n            // - `ptr` will point to a valid `[u8]` as it was zero-initialized.\n            // - nothing is aliasing the pointer.\n            unsafe { slice::from_raw_parts_mut(ptr, len) }\n        }\n        None => &mut [],\n    };\n    f(buf)\n}\n\n/// Module ABI that updates a row into the table identified by `table_id`,\n/// where the `row` is an array of bytes.\n///\n/// The byte array `row` must be a BSATN-encoded `ProductValue`\n/// typed at the table's `ProductType` row-schema.\n///\n/// The row to update is found by projecting `row`\n/// to the type of the *unique* index identified by `index_id`.\n/// If no row is found, the error `NO_SUCH_ROW` is returned.\n///\n/// To handle auto-incrementing columns,\n/// when the call is successful,\n/// the `row` is written back to with the generated sequence values.\n/// These values are written as a BSATN-encoded `pv: ProductValue`.\n/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n/// When the table has no sequences,\n/// this implies that the `pv`, and thus `row`, will be empty.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_update_bsatn(table_id: u32, index_id: u32, row: u8[]) -> u8[] throws {\n///     __code_error__:\n///         NOT_IN_TRANSACTION\n///       | NOT_SUCH_TABLE\n///       | NO_SUCH_INDEX\n///       | INDEX_NOT_UNIQUE\n///       | BSATN_DECODE_ERROR\n///       | NO_SUCH_ROW\n///       | UNIQUE_ALREADY_EXISTS\n///       | SCHEDULE_AT_DELAY_TOO_LONG\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns the generated sequence values encoded in BSATN (see above).\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n/// - [`spacetimedb_primitives::errno::NOT_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n/// - [`spacetimedb_primitives::errno::INDEX_NOT_UNIQUE`]\n///   when the index was not unique.\n/// - [`spacetimedb_primitives::errno::`BSATN_DECODE_ERROR`]\n///   when `row` cannot be decoded to a `ProductValue`.\n///   typed at the `ProductType` the table's schema specifies\n///   or when it cannot be projected to the index identified by `index_id`.\n/// - [`spacetimedb_primitives::errno::`NO_SUCH_ROW`]\n///   when the row was not found in the unique index.\n/// - [`spacetimedb_primitives::errno::`UNIQUE_ALREADY_EXISTS`]\n///   when inserting `row` would violate a unique constraint.\n/// - [`spacetimedb_primitives::errno::`SCHEDULE_AT_DELAY_TOO_LONG`]\n///   when the delay specified in the row was too long.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `row` is not an array of `u8`s.\nfn datastore_update_bsatn<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let index_id: IndexId = deserialize_js(scope, args.get(1))?;\n    let row_arr = cast!(scope, args.get(2), v8::ArrayBuffer, \"buffer\").map_err(|e| e.throw(scope))?;\n    let row_len = deserialize_js::<u32>(scope, args.get(3))? as usize;\n\n    // Insert the row into the DB and write back the generated column values.\n    let row_len = with_arraybuffer_mut(row_arr, |buf| -> SysCallResult<_> {\n        let buf = buf.get_mut(..row_len).ok_or(OOB)?;\n        // Insert the row into the DB and write back the generated column values.\n        Ok(get_env(scope)?.instance_env.update(table_id, index_id, buf)?)\n    })?;\n\n    Ok(row_len as u32)\n}\n\n/// Module ABI that deletes all rows found in the index identified by `index_id`,\n/// according to `prefix`, `rstart`, and `rend` where:\n/// - `prefix = buffer[...prefix_len]`\n/// - `rstart = buffer[prefix_len..prefix_len + rstart_len]`\n/// - `rend = buffer[prefix_len + rstart_len..prefix_len + rstart_len + rend_len]`\n///   if `rend_len > 0`\n/// - `rend = rstart` if `rend_len == 0`\n///\n/// This syscall will delete all the rows found by\n/// [`datastore_index_scan_range_bsatn`] with the same arguments passed,\n/// including `prefix_elems`.\n/// See `datastore_index_scan_range_bsatn` for details.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_index_scan_range_bsatn(\n///     index_id: u32,\n///     prefix: u8[],\n///     prefix_elems: u16,\n///     rstart: u8[],\n///     rend: u8[],\n/// ) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the number of rows deleted.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_INDEX`]\n///   when `index_id` is not a known ID of an index.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `prefix` cannot be decoded to\n///   a `prefix_elems` number of `AlgebraicValue`\n///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n///   where the inner `AlgebraicValue`s are\n///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n///\n/// Throws a `TypeError` if:\n/// - `index_id` is not a `u32`.\n/// - `prefix_len`, `rstart_len`, and `rend_len` are not `u32`s.\n/// - `prefix_elems` is not a `u16`.\n///\n/// Throws a `RangeError` if any of these are out of bounds of `buffer`:\n/// - `prefix_len`,\n/// - `prefix_len + rstart_len`,\n/// - or `prefix_len + rstart_len + rend_len`\nfn datastore_delete_by_index_scan_range_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let buf = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n    let prefix_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n    let prefix_elems: ColId = deserialize_js(scope, args.get(3))?;\n    let rstart_len = deserialize_js::<u32>(scope, args.get(4))? as usize;\n    let rend_len = deserialize_js::<u32>(scope, args.get(5))? as usize;\n\n    with_arraybuffer(buf, |mut buf| {\n        let oob = || OOB;\n\n        let prefix = buf.split_off(..prefix_len).ok_or_else(oob)?;\n        let rstart = buf.split_off(..rstart_len).ok_or_else(oob)?;\n        let rend = if rend_len == 0 {\n            rstart\n        } else {\n            buf.split_off(..rend_len).ok_or_else(oob)?\n        };\n\n        // Delete the relevant rows.\n        let count = get_env(scope)?\n            .instance_env\n            .datastore_delete_by_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)?;\n        Ok(count)\n    })\n}\n\n/// Module ABI that deletes those rows, in the table identified by `table_id`,\n/// that match any row in `relation`.\n///\n/// Matching is defined by first BSATN-decoding\n/// the array of bytes `relation` to a `Vec<ProductValue>`\n/// according to the row schema of the table\n/// and then using `Ord for AlgebraicValue`.\n/// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n/// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.\n///\n/// # Signature\n///\n/// ```ignore\n/// datastore_delete_all_by_eq_bsatn(table_id: u32, relation: u8[]) -> u32 throws {\n///    __code_error__: NOT_IN_TRANSACTION | NO_SUCH_INDEX | BSATN_DECODE_ERROR\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u8` is `number` in JS restricted to unsigned 8-bit integers.\n/// - `u16` is `number` in JS restricted to unsigned 16-bit integers.\n/// - `u32` is `number` in JS restricted to unsigned 32-bit integers.\n///\n/// # Returns\n///\n/// Returns a `u32` that is the number of rows deleted.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\n///\n/// - [`spacetimedb_primitives::errno::NO_SUCH_TABLE`]\n///   when `table_id` is not a known ID of a table.\n///\n/// - [`spacetimedb_primitives::errno::BSATN_DECODE_ERROR`]\n///   when `relation` cannot be decoded to `Vec<ProductValue>`\n///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.\n///\n/// Throws a `TypeError` if:\n/// - `table_id` is not a `u32`.\n/// - `relation` is not an array of `u8`s.\nfn datastore_delete_all_by_eq_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let table_id: TableId = deserialize_js(scope, args.get(0))?;\n    let buf = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n    let relation_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n\n    with_arraybuffer(buf, |buf| {\n        let relation = buf.get(..relation_len).ok_or(OOB)?;\n        let count = get_env(scope)?\n            .instance_env\n            .datastore_delete_all_by_eq_bsatn(table_id, relation)?;\n        Ok(count)\n    })\n}\n\n/// Module ABI to read a JWT payload associated with a connection ID from the system tables.\n///\n/// # Signature\n///\n/// ```ignore\n/// get_jwt_payload(connection_id: u128) -> u8[] throws {\n///     __code_error__:\n///         NOT_IN_TRANSACTION\n/// }\n/// ```\n///\n/// # Types\n///\n/// - `u128` is `bigint` in JS restricted to unsigned 128-bit integers.\n///\n/// # Returns\n///\n/// Returns a byte array encoding the JWT payload if one is found. If one is not found, an\n/// empty byte array is returned.\n///\n/// # Throws\n///\n/// Throws `{ __code_error__: u16 }` where `__code_error__` is:\n///\n/// - [`spacetimedb_primitives::errno::NOT_IN_TRANSACTION`]\n///   when called outside of a transaction.\nfn get_jwt_payload<'scope>(\n    scope: &mut PinScope<'scope, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<Local<'scope, v8::Uint8Array>> {\n    let payload = super::common::get_jwt_payload(scope, args)?;\n    Ok(make_uint8array(scope, payload))\n}\n\nfn datastore_index_scan_point_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let buf = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n    let point_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n\n    with_arraybuffer(buf, |buf| {\n        let point = buf.get(..point_len).ok_or(OOB)?;\n\n        let env = get_env(scope)?;\n\n        // Find the relevant rows.\n        let chunks = env\n            .instance_env\n            .datastore_index_scan_point_bsatn_chunks(&mut env.chunk_pool, index_id, point)?;\n\n        // Insert the encoded + concatenated rows into a new buffer and return its id.\n        Ok(env.iters.insert(chunks.into_iter()).0)\n    })\n}\n\nfn datastore_delete_by_index_scan_point_bsatn(\n    scope: &mut PinScope<'_, '_>,\n    args: FunctionCallbackArguments<'_>,\n) -> SysCallResult<u32> {\n    let index_id: IndexId = deserialize_js(scope, args.get(0))?;\n    let buf = cast!(scope, args.get(1), v8::ArrayBuffer, \"`ArrayBuffer`\").map_err(|e| e.throw(scope))?;\n    let point_len = deserialize_js::<u32>(scope, args.get(2))? as usize;\n\n    with_arraybuffer(buf, |buf| {\n        let point = buf.get(..point_len).ok_or(OOB)?;\n\n        // Delete the relevant rows.\n        let count = get_env(scope)?\n            .instance_env\n            .datastore_delete_by_index_scan_point_bsatn(index_id, point)?;\n        Ok(count)\n    })\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/to_value.rs",
    "content": "use bytemuck::{NoUninit, Pod};\nuse spacetimedb_sats::{i256, u256};\nuse v8::{BigInt, Boolean, Integer, Local, Number, PinScope, Value};\n\n/// Types that can be converted to a v8-stack-allocated [`Value`].\n/// The conversion can be done without the possibility for error.\npub(super) trait ToValue {\n    /// Converts `self` within `scope` (a sort of stack management in V8) to a [`Value`].\n    fn to_value<'scope>(&self, scope: &PinScope<'scope, '_>) -> Local<'scope, Value>;\n}\n\n/// Provides a [`ToValue`] implementation.\nmacro_rules! impl_to_value {\n    ($ty:ty, ($val:ident, $scope:ident) => $logic:expr) => {\n        impl ToValue for $ty {\n            fn to_value<'scope>(&self, $scope: &PinScope<'scope, '_>) -> Local<'scope, Value> {\n                let $val = *self;\n                $logic.into()\n            }\n        }\n    };\n}\n\n// Floats are the most direct conversion.\nimpl_to_value!(f32, (val, scope) => (val as f64).to_value(scope));\nimpl_to_value!(f64, (val, scope) => Number::new(scope, val));\n\n// Booleans have dedicated conversions.\nimpl_to_value!(bool, (val, scope) => Boolean::new(scope, val));\n\n// Sub-32-bit integers get widened to 32-bit first.\nimpl_to_value!(i8, (val, scope) => (val as i32).to_value(scope));\nimpl_to_value!(u8, (val, scope) => (val as u32).to_value(scope));\nimpl_to_value!(i16, (val, scope) => (val as i32).to_value(scope));\nimpl_to_value!(u16, (val, scope) => (val as u32).to_value(scope));\n\n// 32-bit integers have dedicated conversions.\nimpl_to_value!(i32, (val, scope) => Integer::new(scope, val));\nimpl_to_value!(u32, (val, scope) => Integer::new_from_unsigned(scope, val));\n\n// 64-bit integers have dedicated conversions.\nimpl_to_value!(i64, (val, scope) => BigInt::new_from_i64(scope, val));\nimpl_to_value!(u64, (val, scope) => BigInt::new_from_u64(scope, val));\n\n/// Converts the little-endian bytes of a number to a V8 [`BigInt`].\n///\n/// The `sign` is passed along to the `BigInt`.\nfn le_bytes_to_bigint<'scope, const N: usize, const W: usize>(\n    scope: &PinScope<'scope, '_>,\n    sign: bool,\n    le_bytes: [u8; N],\n) -> Local<'scope, BigInt>\nwhere\n    [u8; N]: NoUninit,\n    [u64; W]: Pod,\n{\n    let words = bytemuck::must_cast::<_, [u64; W]>(le_bytes).map(u64::from_le);\n    BigInt::new_from_words(scope, sign, &words).unwrap()\n}\n\n// Unsigned 128-bit and 256-bit integers have dedicated conversions.\n// They are convered to a list of words before becoming `BigInt`s.\nimpl_to_value!(u128, (val, scope) => le_bytes_to_bigint::<16, 2>(scope, false, val.to_le_bytes()));\nimpl_to_value!(u256, (val, scope) => le_bytes_to_bigint::<32, 4>(scope, false, val.to_le_bytes()));\n\npub(super) const WORD_MIN: u64 = i64::MIN as u64;\n\n/// Returns `iN::MIN` for `N = 8 * WORDS` as a V8 [`BigInt`].\n///\n/// Examples:\n/// `i64::MIN` becomes `-1 * WORD_MIN * (2^64)^0 = -1 * WORD_MIN`\n/// `i128::MIN` becomes `-1 * (0 * (2^64)^0 + WORD_MIN * (2^64)^1) = -1 * WORD_MIN * 2^64`\n/// `i256::MIN` becomes `-1 * (0 * (2^64)^0 + 0 * (2^64)^1 + WORD_MIN * (2^64)^2) = -1 * WORD_MIN * (2^128)`\nfn signed_min_bigint<'scope, const WORDS: usize>(scope: &PinScope<'scope, '_>) -> Local<'scope, BigInt> {\n    let words = &mut [0u64; WORDS];\n    if let [.., last] = words.as_mut_slice() {\n        *last = WORD_MIN;\n    }\n    v8::BigInt::new_from_words(scope, true, words).unwrap()\n}\n\n// Signed 128-bit and 256-bit integers have dedicated conversions.\n//\n// For the negative number case, the magnitude is computed and the sign is passed along.\n// A special case is the minimum number.\nimpl_to_value!(i128, (val, scope) => {\n    let sign = val.is_negative();\n    let magnitude = if sign { val.checked_neg() } else { Some(val) };\n    match magnitude {\n        Some(magnitude) => le_bytes_to_bigint::<16, 2>(scope, sign, magnitude.to_le_bytes()),\n        None => signed_min_bigint::<2>(scope),\n    }\n});\nimpl_to_value!(i256, (val, scope) => {\n    let sign = val.is_negative();\n    let magnitude = if sign { val.checked_neg() } else { Some(val) };\n    match magnitude {\n        Some(magnitude) => le_bytes_to_bigint::<32, 4>(scope, sign, magnitude.to_le_bytes()),\n        None => signed_min_bigint::<4>(scope),\n    }\n});\n\n#[cfg(test)]\npub(in super::super) mod test {\n    use super::super::{from_value::FromValue, new_isolate, V8Runtime};\n    use super::*;\n    use core::fmt::Debug;\n    use proptest::prelude::*;\n    use spacetimedb_sats::proptest::{any_i256, any_u256};\n    use v8::{scope_with_context, Context};\n\n    /// Sets up V8 and runs `logic` with a [`PinScope`].\n    pub(in super::super) fn with_scope<R>(logic: impl FnOnce(&mut PinScope<'_, '_>) -> R) -> R {\n        V8Runtime::init_for_test();\n\n        let mut isolate = new_isolate();\n        scope_with_context!(let scope, &mut isolate, Context::new(scope, Default::default()));\n\n        logic(scope)\n    }\n\n    /// Roundtrips `rust_val` via `ToValue` to the V8 representation\n    /// and then back via `FromValue`,\n    /// asserting that it's the same as the passed value.\n    fn assert_roundtrips<T: ToValue + FromValue + PartialEq + Debug>(rust_val: T) {\n        with_scope(|scope| {\n            // Convert to JS and then back.\n            let js_val = rust_val.to_value(scope);\n            let rust_val_prime = T::from_value(js_val, scope).unwrap();\n\n            // We should end up where we started.\n            assert_eq!(rust_val, rust_val_prime);\n        })\n    }\n\n    proptest! {\n        #[test] fn test_bool(x: bool) { assert_roundtrips(x); }\n\n        #[test] fn test_f32(x: f32) { assert_roundtrips(x); }\n        #[test] fn test_f64(x: f64) { assert_roundtrips(x); }\n\n        #[test] fn test_u8(x: u8) { assert_roundtrips(x); }\n        #[test] fn test_u16(x: u16) { assert_roundtrips(x); }\n        #[test] fn test_u32(x: u32) { assert_roundtrips(x); }\n        #[test] fn test_u64(x: u64) { assert_roundtrips(x); }\n        #[test] fn test_u128(x: u128) { assert_roundtrips(x); }\n        #[test] fn test_u256(x in any_u256()) { assert_roundtrips(x); }\n\n        #[test] fn test_i8(x: i8) { assert_roundtrips(x); }\n        #[test] fn test_i16(x: i16) { assert_roundtrips(x); }\n        #[test] fn test_i32(x: i32) { assert_roundtrips(x); }\n        #[test] fn test_i64(x: i64) { assert_roundtrips(x); }\n        #[test] fn test_i128(x: i128) { assert_roundtrips(x); }\n        #[test] fn test_i256(x in any_i256()) { assert_roundtrips(x); }\n    }\n\n    #[test]\n    fn test_signed_mins() {\n        assert_roundtrips(i8::MIN);\n        assert_roundtrips(i16::MIN);\n        assert_roundtrips(i32::MIN);\n        assert_roundtrips(i64::MIN);\n        assert_roundtrips(i128::MIN);\n        assert_roundtrips(i256::MIN);\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/v8/util.rs",
    "content": "/// The trait used as bound on `v8::ArrayBuffer::new_backing_store_from_bytes`\n/// isn't public, so we need to emulate it.\npub(super) trait IntoArrayBufferBackingStore {\n    fn into_backing_store(self) -> v8::UniqueRef<v8::BackingStore>;\n}\nmacro_rules! impl_into_backing_store {\n    ([$($bounds:tt)*] $t:ty) => {\n        impl<$($bounds)*> IntoArrayBufferBackingStore for $t {\n            fn into_backing_store(self) -> v8::UniqueRef<v8::BackingStore> {\n                v8::ArrayBuffer::new_backing_store_from_bytes(self)\n            }\n        }\n    };\n    ($($primitive:ty),*) => {$(\n        impl_into_backing_store!([] Box<[$primitive]>);\n        impl_into_backing_store!([] Vec<$primitive>);\n    )*};\n}\n\nimpl_into_backing_store!([T: AsMut<[u8]>] Box<T>);\nimpl_into_backing_store!(u8, u16, u32, u64, i8, i16, i32, i64);\n\n/// Taking a scope and a buffer, return a `v8::Local<'scope, v8::Uint8Array>`.\npub(super) fn make_uint8array<'scope>(\n    scope: &v8::PinScope<'scope, '_>,\n    buf: impl IntoArrayBufferBackingStore,\n) -> v8::Local<'scope, v8::Uint8Array> {\n    let store = buf.into_backing_store();\n    let len = store.byte_length();\n    let buf = v8::ArrayBuffer::with_backing_store(scope, &store.make_shared());\n    v8::Uint8Array::new(scope, buf, 0, len).expect(\"len > 8 pebibytes\")\n}\n\n/// Taking a scope and a buffer, return a `v8::Local<'scope, v8::DataView>`.\npub(super) fn make_dataview<'scope>(\n    scope: &v8::PinScope<'scope, '_>,\n    buf: impl IntoArrayBufferBackingStore,\n) -> v8::Local<'scope, v8::DataView> {\n    let store = buf.into_backing_store();\n    let len = store.byte_length();\n    let buf = v8::ArrayBuffer::with_backing_store(scope, &store.make_shared());\n    v8::DataView::new(scope, buf, 0, len)\n}\n"
  },
  {
    "path": "crates/core/src/host/wasm_common/abi.rs",
    "content": "pub use spacetimedb_lib::VersionTuple;\n\nconst MODULE_PREFIX: &str = \"spacetime_\";\n\npub fn determine_spacetime_abi<I>(\n    imports: impl IntoIterator<Item = I>,\n    get_module: impl Fn(&I) -> &str,\n) -> Result<VersionTuple, AbiVersionError> {\n    let it = imports.into_iter().filter_map(|imp| {\n        let s = get_module(&imp);\n        s.strip_prefix(MODULE_PREFIX)\n            .map(|ver| parse_abi_version(ver).ok_or_else(|| AbiVersionError::Parse { module: s.to_owned() }))\n    });\n    itertools::process_results(it, |mut it| {\n        let first = it.next().ok_or(AbiVersionError::NotDetected)?;\n        it.try_fold(first, refine_ver_req)\n    })?\n}\n\npub fn parse_abi_version(ver: &str) -> Option<VersionTuple> {\n    let (major, minor) = ver.split_once('.')?;\n    let (major, minor) = Option::zip(major.parse().ok(), minor.parse().ok())?;\n    Some(VersionTuple { major, minor })\n}\n\nfn refine_ver_req(ver: VersionTuple, new: VersionTuple) -> Result<VersionTuple, AbiVersionError> {\n    if ver.major != new.major {\n        Err(AbiVersionError::MultipleMajor(ver.major, new.major))\n    } else {\n        Ok(Ord::max(ver, new))\n    }\n}\n\npub fn verify_supported(implements: VersionTuple, got: VersionTuple) -> Result<(), AbiVersionError> {\n    if implements.supports(got) {\n        Ok(())\n    } else {\n        Err(AbiVersionError::UnsupportedVersion { implements, got })\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum AbiVersionError {\n    #[error(\"import module {module:?} has malformed version string\")]\n    Parse { module: String },\n    #[error(\"module cannot depend on both major version {0} and major version {1}\")]\n    MultipleMajor(u16, u16),\n    #[error(\"abi version {got} is not supported (host implements {implements})\")]\n    UnsupportedVersion {\n        got: VersionTuple,\n        implements: VersionTuple,\n    },\n    // TODO: (by 1.0, maybe) remove the parenthetical from this error message\n    #[error(\"unable to determine ABI of module (may be on an spacetime version < 0.8)\")]\n    NotDetected,\n}\n"
  },
  {
    "path": "crates/core/src/host/wasm_common/instrumentation.rs",
    "content": "//! This file defines `CallSpan` and `CallSpanStart`,\n//! which describe the time that a module spends executing \"host calls,\"\n//! the WASM analog to syscalls.\n//!\n//! We can collect `CallSpan` timing data for various parts of host-call execution:\n//! - `WasmInstanceEnv`, responsible for direct communication between WASM and the host.\n//! - `InstanceEnv`, responsible for calls into the relational DB.\n//! - Others?\n//!\n//! The instrumentation has non-negligible overhead,\n//! so we enable it on a per-component basis with `cfg` attributes.\n//! This is accomplished by defining two interchangeable modules,\n//! `noop` and `op`, both of which implement the call-span interface.\n//! `noop` does nothing.\n//! `op` uses `std::time::Instant` and `std::time::Duration` to capture timings.\n//! Components which use the time-span interface will conditionally import one of the two modules, like:\n//! ```no-run\n//! #[cfg(feature = \"spacetimedb-wasm-instance-times)]\n//! use instrumentation::op as span;\n//! #[cfg(not(feature = \"spacetimedb-wasm-instance-times)]\n//! use instrumentation::noop as span;\n//! ```\n\nuse std::time::{Duration, Instant};\n\nuse enum_map::{enum_map, EnumMap};\n\nuse crate::host::AbiCall;\n\n#[allow(unused)]\npub mod noop {\n    use crate::host::AbiCall;\n\n    use super::*;\n\n    pub struct CallSpanStart;\n\n    impl CallSpanStart {\n        pub fn new(_call: AbiCall) -> Self {\n            Self\n        }\n\n        pub fn end(self) -> CallSpan {\n            CallSpan\n        }\n    }\n\n    pub struct CallSpan;\n\n    pub fn record_span(_call_times: &mut CallTimes, _span: CallSpan) {}\n}\n\n#[allow(unused)]\npub mod op {\n    use crate::host::AbiCall;\n\n    use super::*;\n\n    pub struct CallSpanStart {\n        call: AbiCall,\n        start: Instant,\n    }\n\n    impl CallSpanStart {\n        pub fn new(call: AbiCall) -> Self {\n            let start = Instant::now();\n            Self { call, start }\n        }\n\n        pub fn end(self) -> CallSpan {\n            let call = self.call;\n            let duration = self.start.elapsed();\n            CallSpan { call, duration }\n        }\n    }\n\n    #[derive(Debug)]\n    pub struct CallSpan {\n        pub(super) call: AbiCall,\n        pub(super) duration: Duration,\n    }\n\n    pub fn record_span(times: &mut CallTimes, span: CallSpan) {\n        times.span(span)\n    }\n}\n\n#[derive(Debug)]\n/// Associates each `AbiCall` tag with a cumulative total `Duration` spent within that call.\npub struct CallTimes {\n    times: EnumMap<AbiCall, Duration>,\n}\n\nimpl Default for CallTimes {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl CallTimes {\n    /// Create a new timing structure, with times for all calls set to zero.\n    pub fn new() -> Self {\n        let times = enum_map! { _ => Duration::ZERO };\n        Self { times }\n    }\n\n    /// Track a particular `CallSpan` by adding its duration to the\n    /// associated `AbiCall`'s timing information.\n    pub fn span(&mut self, span: op::CallSpan) {\n        self.times[span.call] += span.duration;\n    }\n\n    pub fn sum(&self) -> Duration {\n        self.times.values().sum()\n    }\n\n    /// Taking the record of call times gives a copy of the\n    /// current values and resets the values to zero.\n    ///\n    /// WasmInstanceEnv::finish_reducer (and other future per-reducer-call metrics)\n    /// will `take`` the CallTimes after running a reducer and report the taken times,\n    /// leaving a fresh zeroed CallTimes for the next reducer invocation.\n    pub fn take(&mut self) -> CallTimes {\n        std::mem::take(self)\n    }\n}\n\n#[cfg(not(feature = \"spacetimedb-wasm-instance-env-times\"))]\npub use noop as span;\n#[cfg(feature = \"spacetimedb-wasm-instance-env-times\")]\npub use op as span;\n"
  },
  {
    "path": "crates/core/src/host/wasm_common/module_host_actor.rs",
    "content": "use super::instrumentation::CallTimes;\nuse super::*;\nuse crate::client::ClientActorId;\nuse crate::database_logger;\nuse crate::energy::{EnergyMonitor, FunctionBudget, FunctionFingerprint};\nuse crate::error::DBError;\nuse crate::host::host_controller::CallProcedureReturn;\nuse crate::host::instance_env::{InstanceEnv, TxSlot};\nuse crate::host::module_common::{build_common_module_from_raw, ModuleCommon};\nuse crate::host::module_host::{\n    call_identity_connected, init_database, CallProcedureParams, CallReducerParams, CallViewParams,\n    ClientConnectedError, DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall, ModuleInfo, RefInstance,\n    ViewCallResult, ViewCommand, ViewCommandResult, ViewOutcome,\n};\nuse crate::host::scheduler::{CallScheduledFunctionResult, ScheduledFunctionParams};\nuse crate::host::{\n    ArgsTuple, ModuleHost, ProcedureCallError, ProcedureCallResult, ReducerCallError, ReducerCallResult, ReducerId,\n    ReducerOutcome, Scheduler, UpdateDatabaseResult,\n};\nuse crate::identity::Identity;\nuse crate::messages::control_db::HostType;\nuse crate::module_host_context::ModuleCreationContext;\nuse crate::replica_context::ReplicaContext;\nuse crate::sql::ast::SchemaViewer;\nuse crate::sql::execute::run_with_instance;\nuse crate::subscription::module_subscription_actor::commit_and_broadcast_event;\nuse crate::subscription::module_subscription_manager::TransactionOffset;\nuse crate::util::prometheus_handle::{HistogramExt, TimerGuard};\nuse crate::worker_metrics::WORKER_METRICS;\nuse anyhow::{anyhow, bail, ensure, Context};\nuse bytes::{Buf, Bytes};\nuse core::future::Future;\nuse core::time::Duration;\nuse prometheus::{Histogram, IntCounter, IntGauge};\nuse spacetimedb_auth::identity::ConnectionAuthCtx;\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::error::{DatastoreError, ViewError};\nuse spacetimedb_datastore::execution_context::{self, ReducerContext, Workload};\nuse spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCallInfo};\nuse spacetimedb_datastore::traits::{IsolationLevel, Program};\nuse spacetimedb_execution::pipelined::PipelinedProject;\nuse spacetimedb_lib::buffer::DecodeError;\nuse spacetimedb_lib::db::raw_def::v9::{Lifecycle, ViewResultHeader};\nuse spacetimedb_lib::de::DeserializeSeed;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::{bsatn, ConnectionId, Hash, ProductType, RawModuleDef, Timestamp};\nuse spacetimedb_primitives::{ProcedureId, TableId, ViewFnPtr, ViewId};\nuse spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, Deserialize, ProductValue, Typespace, WithTypespace};\nuse spacetimedb_schema::auto_migrate::{MigratePlan, MigrationPolicy, MigrationPolicyError};\nuse spacetimedb_schema::def::deserialize::FunctionDef;\nuse spacetimedb_schema::def::{ModuleDef, ViewDef};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_subscription::SubscriptionPlan;\nuse std::sync::Arc;\nuse tracing::span::EnteredSpan;\n\npub trait WasmModule: Send + 'static {\n    type Instance: WasmInstance;\n    type InstancePre: WasmInstancePre<Instance = Self::Instance>;\n\n    type ExternType: FuncSigLike;\n    fn get_export(&self, s: &str) -> Option<Self::ExternType>;\n    fn for_each_export<E>(&self, f: impl FnMut(&str, &Self::ExternType) -> Result<(), E>) -> Result<(), E>;\n\n    fn instantiate_pre(&self) -> Result<Self::InstancePre, InitializationError>;\n}\n\npub trait WasmInstancePre: Send + Sync + 'static {\n    type Instance: WasmInstance;\n    fn instantiate(&self, env: InstanceEnv, func_names: &FuncNames) -> Result<Self::Instance, InitializationError>;\n}\n\n// TODO: Technically this trait is also used for V8.\n// We should rename and move to some place more appropriate.\npub trait WasmInstance {\n    fn extract_descriptions(&mut self) -> Result<RawModuleDef, DescribeError>;\n\n    fn replica_ctx(&self) -> &Arc<ReplicaContext>;\n\n    fn tx_slot(&self) -> TxSlot;\n\n    fn set_module_def(&mut self, module_def: Arc<ModuleDef>);\n\n    fn call_reducer(&mut self, op: ReducerOp<'_>, budget: FunctionBudget) -> ReducerExecuteResult;\n\n    fn call_view(&mut self, op: ViewOp<'_>, budget: FunctionBudget) -> ViewExecuteResult;\n\n    fn call_view_anon(&mut self, op: AnonymousViewOp<'_>, budget: FunctionBudget) -> ViewExecuteResult;\n\n    fn log_traceback(&self, func_type: &str, func: &str, trap: &anyhow::Error);\n\n    fn call_procedure(\n        &mut self,\n        op: ProcedureOp,\n        budget: FunctionBudget,\n    ) -> impl Future<Output = (ProcedureExecuteResult, Option<TransactionOffset>)>;\n}\n\npub struct EnergyStats {\n    pub budget: FunctionBudget,\n    pub remaining: FunctionBudget,\n}\n\nimpl EnergyStats {\n    pub const ZERO: Self = Self {\n        budget: FunctionBudget::ZERO,\n        remaining: FunctionBudget::ZERO,\n    };\n\n    /// Returns the used energy amount.\n    fn used(&self) -> FunctionBudget {\n        (self.budget.get() - self.remaining.get()).into()\n    }\n}\n\npub(crate) fn deserialize_view_rows(\n    row_type: AlgebraicTypeRef,\n    bytes: Bytes,\n    typespace: &Typespace,\n) -> Result<Vec<ProductValue>, DBError> {\n    // The return type is expected to be an array of products.\n    let row_type = typespace.resolve(row_type);\n    let ret_type = AlgebraicType::array(row_type.ty().clone());\n    let seed = WithTypespace::new(typespace, &ret_type);\n    let rows = seed\n        .deserialize(bsatn::Deserializer::new(&mut &bytes[..]))\n        .map_err(|e| DatastoreError::from(ViewError::DeserializeReturn(e.to_string())))\n        .map_err(DBError::from)?;\n\n    rows.into_array()\n        .map_err(|_| ViewError::SerializeRow)\n        .map_err(DatastoreError::from)\n        .map_err(DBError::from)?\n        .into_iter()\n        .map(|product| {\n            product\n                .into_product()\n                .map_err(|_| ViewError::SerializeRow)\n                .map_err(DatastoreError::from)\n                .map_err(DBError::from)\n        })\n        .collect()\n}\n\npub(crate) fn run_query_for_view(\n    tx: &mut MutTxId,\n    the_query: &str,\n    expected_row_type: &ProductType,\n    call_info: &ViewCallInfo,\n    database_identity: Identity,\n) -> anyhow::Result<Vec<ProductValue>> {\n    if the_query.trim().is_empty() {\n        return Ok(Vec::new());\n    }\n\n    // Views bypass RLS, since views should enforce their own access control procedurally.\n    let auth = AuthCtx::for_current(database_identity);\n    let schema_view = SchemaViewer::new(&*tx, &auth);\n\n    // Compile to subscription plans.\n    let (plans, has_params) = SubscriptionPlan::compile(the_query, &schema_view, &auth)?;\n    ensure!(\n        !has_params,\n        \"parameterized SQL is not supported for view materialization yet\"\n    );\n\n    // Validate shape and disallow views-on-views.\n    for plan in &plans {\n        let phys = plan.optimized_physical_plan();\n        let Some(source_schema) = phys.return_table() else {\n            bail!(\"query does not return plain table rows\");\n        };\n        if phys.reads_from_view(true) || phys.reads_from_view(false) {\n            bail!(\"view definition cannot read from other views\");\n        }\n        if source_schema.row_type != *expected_row_type {\n            bail!(\n                \"query returns `{}` but view expects `{}`\",\n                fmt_algebraic_type(&AlgebraicType::Product(source_schema.row_type.clone())),\n                fmt_algebraic_type(&AlgebraicType::Product(expected_row_type.clone())),\n            );\n        }\n    }\n\n    let op = FuncCallType::View(call_info.clone());\n    let mut metrics = ExecutionMetrics::default();\n    let mut rows = Vec::new();\n\n    for plan in plans {\n        // Track read sets for all tables involved in this plan.\n        // TODO(jsdt): This means we will rerun the view and query for any change to these tables, so we should optimize this asap.\n        for table_id in plan.table_ids() {\n            tx.record_table_scan(&op, table_id);\n        }\n\n        let pipelined = PipelinedProject::from(plan.optimized_physical_plan().clone());\n        pipelined.execute(&*tx, &mut metrics, &mut |row| {\n            rows.push(row.to_product_value());\n            Ok(())\n        })?;\n    }\n\n    Ok(rows)\n}\n\npub struct ExecutionTimings {\n    pub total_duration: Duration,\n    pub wasm_instance_env_call_times: CallTimes,\n}\n\nimpl ExecutionTimings {\n    /// Not a `const` because there doesn't seem to be any way to `const` construct an `enum_map::EnumMap`,\n    /// which `CallTimes` uses.\n    pub fn zero() -> Self {\n        Self {\n            total_duration: Duration::ZERO,\n            wasm_instance_env_call_times: CallTimes::new(),\n        }\n    }\n}\n\n/// The result that `__call_reducer__` produces during normal non-trap execution.\npub type ReducerResult = Result<Option<Bytes>, Box<str>>;\n\npub struct ExecutionStats {\n    pub energy: EnergyStats,\n    pub timings: ExecutionTimings,\n    pub memory_allocation: usize,\n}\n\nimpl ExecutionStats {\n    fn energy_used(&self) -> FunctionBudget {\n        self.energy.used()\n    }\n\n    fn abi_duration(&self) -> Duration {\n        self.timings.wasm_instance_env_call_times.sum()\n    }\n\n    fn total_duration(&self) -> Duration {\n        self.timings.total_duration\n    }\n}\n\npub enum ExecutionError {\n    User(Box<str>),\n    Recoverable(anyhow::Error),\n    Trap(anyhow::Error),\n}\n\n#[derive(derive_more::AsRef)]\npub struct ExecutionResult<T, E> {\n    #[as_ref]\n    pub stats: ExecutionStats,\n    pub call_result: Result<T, E>,\n}\n\npub type ReducerExecuteResult = ExecutionResult<Option<Bytes>, ExecutionError>;\n\nimpl<T, E> ExecutionResult<T, E> {\n    pub fn map_result<X, Y>(self, f: impl FnOnce(Result<T, E>) -> Result<X, Y>) -> ExecutionResult<X, Y> {\n        let Self { stats, call_result } = self;\n        let call_result = f(call_result);\n        ExecutionResult { stats, call_result }\n    }\n}\n\n// The original version of views used a different return format (it returned the rows directly).\n// The newer version uses ViewReturnData to represent the different formats.\npub enum ViewReturnData {\n    // This view returns a Vec of rows (bsatn encoded).\n    Rows(Bytes),\n    // This view returns a ViewResultHeader, potentially followed by more data.\n    HeaderFirst(Bytes),\n}\n\n// A view result after processing the return header.\npub enum ViewResult {\n    // The rows are encoded as a bsatn array of products.\n    Rows(Bytes),\n    RawSql(String),\n}\n\nimpl ViewResult {\n    pub fn from_return_data(data: ViewReturnData) -> Result<Self, anyhow::Error> {\n        match data {\n            ViewReturnData::Rows(bytes) => Ok(ViewResult::Rows(bytes)),\n            ViewReturnData::HeaderFirst(bytes) => {\n                let mut reader = &bytes[..];\n                let header = {\n                    let deserializer = bsatn::Deserializer::new(&mut reader);\n                    ViewResultHeader::deserialize(deserializer)\n                        .context(\"failed to deserialize ViewResultHeader from view return data\")?\n                };\n                match header {\n                    ViewResultHeader::RawSql(query) => Ok(ViewResult::RawSql(query)),\n                    ViewResultHeader::RowData => {\n                        let at = bytes.len() - reader.remaining();\n                        let remaining_bytes = bytes.slice(at..);\n                        Ok(ViewResult::Rows(remaining_bytes))\n                    }\n                }\n            }\n        }\n    }\n}\n\npub type ViewExecuteResult = ExecutionResult<ViewReturnData, ExecutionError>;\n\npub type ProcedureExecuteResult = ExecutionResult<Bytes, anyhow::Error>;\n\npub struct WasmModuleHostActor<T: WasmModule> {\n    module: T::InstancePre,\n    common: ModuleCommon,\n    func_names: Arc<FuncNames>,\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum InitializationError {\n    #[error(transparent)]\n    Validation(#[from] ValidationError),\n    #[error(transparent)]\n    ModuleValidation(#[from] spacetimedb_schema::error::ValidationErrors),\n    #[error(\"setup function returned an error: {0}\")]\n    Setup(Box<str>),\n    #[error(\"wasm trap while calling {func:?}\")]\n    RuntimeError {\n        #[source]\n        err: anyhow::Error,\n        func: String,\n    },\n    #[error(transparent)]\n    Instantiation(anyhow::Error),\n    #[error(\"error getting module description: {0}\")]\n    Describe(#[from] DescribeError),\n}\n\nimpl From<TypeRefError> for InitializationError {\n    fn from(err: TypeRefError) -> Self {\n        ValidationError::from(err).into()\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum DescribeError {\n    #[error(\"bad signature for descriptor function: {0}\")]\n    Signature(anyhow::Error),\n    #[error(\"error when preparing descriptor function: {0}\")]\n    Setup(anyhow::Error),\n    #[error(\"error decoding module description: {0}\")]\n    Decode(#[from] DecodeError),\n    #[error(transparent)]\n    RuntimeError(anyhow::Error),\n}\n\nimpl<T: WasmModule> WasmModuleHostActor<T> {\n    pub fn new(\n        mcc: ModuleCreationContext,\n        module: T,\n    ) -> Result<(Self, WasmModuleInstance<T::Instance>), InitializationError> {\n        log::trace!(\n            \"Making new WASM module host actor for database {} with module {}\",\n            mcc.replica_ctx.database_identity,\n            mcc.program_hash,\n        );\n\n        let func_names = {\n            FuncNames::check_required(|name| module.get_export(name))?;\n            let mut func_names = FuncNames::default();\n            module.for_each_export(|sym, ty| func_names.update_from_general(sym, ty))?;\n            func_names.preinits.sort_unstable();\n            func_names\n        };\n        let uninit_instance = module.instantiate_pre()?;\n        let instance_env = InstanceEnv::new(mcc.replica_ctx.clone(), mcc.scheduler.clone());\n        let mut instance = uninit_instance.instantiate(instance_env, &func_names)?;\n\n        let desc = instance.extract_descriptions()?;\n\n        // Validate and create a common module rom the raw definition.\n        let common = build_common_module_from_raw(mcc, desc)?;\n\n        let func_names = Arc::new(func_names);\n        let module = WasmModuleHostActor {\n            module: uninit_instance,\n            func_names,\n            common,\n        };\n        let initial_instance = module.make_from_instance(instance);\n\n        Ok((module, initial_instance))\n    }\n}\n\nimpl<T: WasmModule> WasmModuleHostActor<T> {\n    fn make_from_instance(&self, mut instance: T::Instance) -> WasmModuleInstance<T::Instance> {\n        let common = InstanceCommon::new(&self.common);\n        instance.set_module_def(common.info().module_def.clone());\n        WasmModuleInstance {\n            instance,\n            common,\n            trapped: false,\n        }\n    }\n}\n\nimpl<T: WasmModule> WasmModuleHostActor<T> {\n    pub fn replica_ctx(&self) -> &Arc<ReplicaContext> {\n        self.common.replica_ctx()\n    }\n\n    pub fn scheduler(&self) -> &Scheduler {\n        self.common.scheduler()\n    }\n\n    pub fn info(&self) -> Arc<ModuleInfo> {\n        self.common.info()\n    }\n\n    pub fn create_instance(&self) -> WasmModuleInstance<T::Instance> {\n        let common = &self.common;\n        let env = InstanceEnv::new(common.replica_ctx().clone(), common.scheduler().clone());\n        // this shouldn't fail, since we already called module.create_instance()\n        // before and it didn't error, and ideally they should be deterministic\n        let mut instance = self\n            .module\n            .instantiate(env, &self.func_names)\n            .expect(\"failed to initialize instance\");\n        let _ = instance.extract_descriptions();\n        self.make_from_instance(instance)\n    }\n}\n\npub struct WasmModuleInstance<T: WasmInstance> {\n    instance: T,\n    common: InstanceCommon,\n    trapped: bool,\n}\n\nimpl<T: WasmInstance> std::fmt::Debug for WasmModuleInstance<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"WasmInstanceActor\").finish()\n    }\n}\n\nimpl<T: WasmInstance> WasmModuleInstance<T> {\n    pub fn trapped(&self) -> bool {\n        self.trapped\n    }\n\n    pub fn update_database(\n        &mut self,\n        program: Program,\n        old_module_info: Arc<ModuleInfo>,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        self.common\n            .update_database(program, old_module_info, policy, &mut self.instance)\n    }\n\n    pub fn call_reducer(&mut self, params: CallReducerParams) -> ReducerCallResult {\n        let (res, trapped) = self.call_reducer_with_tx(None, params);\n        self.trapped = trapped;\n        res\n    }\n\n    pub fn clear_all_clients(&self) -> anyhow::Result<()> {\n        self.common.clear_all_clients()\n    }\n\n    pub fn call_identity_connected(\n        &mut self,\n        caller_auth: ConnectionAuthCtx,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ClientConnectedError> {\n        let module = &self.common.info.clone();\n        let call_reducer = |tx, params| self.call_reducer_with_tx(tx, params);\n        let mut trapped = false;\n        let res = call_identity_connected(caller_auth, caller_connection_id, module, call_reducer, &mut trapped);\n        self.trapped = trapped;\n        res\n    }\n\n    pub fn call_identity_disconnected(\n        &mut self,\n        caller_identity: Identity,\n        caller_connection_id: ConnectionId,\n    ) -> Result<(), ReducerCallError> {\n        let module = &self.common.info.clone();\n        let call_reducer = |tx, params| self.call_reducer_with_tx(tx, params);\n        let mut trapped = false;\n        let res = ModuleHost::call_identity_disconnected_inner(\n            caller_identity,\n            caller_connection_id,\n            module,\n            call_reducer,\n            &mut trapped,\n        );\n        self.trapped = trapped;\n        res\n    }\n\n    pub fn disconnect_client(&mut self, client_id: ClientActorId) -> Result<(), ReducerCallError> {\n        let module = &self.common.info.clone();\n        let call_reducer = |tx, params| self.call_reducer_with_tx(tx, params);\n        let mut trapped = false;\n        let res = ModuleHost::disconnect_client_inner(client_id, module, call_reducer, &mut trapped);\n        self.trapped = trapped;\n        res\n    }\n\n    pub fn init_database(&mut self, program: Program) -> anyhow::Result<Option<ReducerCallResult>> {\n        let module_def = &self.common.info.clone().module_def;\n        let replica_ctx = &self.instance.replica_ctx().clone();\n        let call_reducer = |tx, params| self.call_reducer_with_tx(tx, params);\n        let (res, trapped) = init_database(replica_ctx, module_def, program, call_reducer);\n        self.trapped = trapped;\n        res\n    }\n\n    pub async fn call_procedure(&mut self, params: CallProcedureParams) -> CallProcedureReturn {\n        let (res, trapped) = self.common.call_procedure(params, &mut self.instance).await;\n        self.trapped = trapped;\n        res\n    }\n\n    pub(in crate::host) async fn call_scheduled_function(\n        &mut self,\n        params: ScheduledFunctionParams,\n    ) -> CallScheduledFunctionResult {\n        let (res, trapped) = self.common.call_scheduled_function(params, &mut self.instance).await;\n        self.trapped = trapped;\n        res\n    }\n}\n\nimpl<T: WasmInstance> WasmModuleInstance<T> {\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    fn call_reducer_with_tx(&mut self, tx: Option<MutTxId>, params: CallReducerParams) -> (ReducerCallResult, bool) {\n        crate::callgrind_flag::invoke_allowing_callgrind(|| {\n            self.common.call_reducer_with_tx(tx, params, &mut self.instance)\n        })\n    }\n\n    pub fn call_view(&mut self, cmd: ViewCommand) -> ViewCommandResult {\n        let (res, trapped) = self.common.handle_cmd(cmd, &mut self.instance);\n        self.trapped = trapped;\n        res\n    }\n}\n\npub struct InstanceCommon {\n    info: Arc<ModuleInfo>,\n    energy_monitor: Arc<dyn EnergyMonitor>,\n    allocated_memory: usize,\n    metric_wasm_memory_bytes: IntGauge,\n    vm_metrics: AllVmMetrics,\n}\n\nimpl InstanceCommon {\n    pub(crate) fn new(module: &ModuleCommon) -> Self {\n        let info = module.info();\n        let vm_metrics = AllVmMetrics::new(&info);\n\n        Self {\n            info: module.info(),\n            vm_metrics,\n            energy_monitor: module.energy_monitor(),\n            // Will be updated on the first reducer call.\n            allocated_memory: 0,\n            metric_wasm_memory_bytes: WORKER_METRICS\n                .wasm_memory_bytes\n                .with_label_values(module.database_identity()),\n        }\n    }\n\n    pub(crate) fn info(&self) -> Arc<ModuleInfo> {\n        self.info.clone()\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub(crate) fn update_database<I: WasmInstance>(\n        &mut self,\n        program: Program,\n        old_module_info: Arc<ModuleInfo>,\n        policy: MigrationPolicy,\n        inst: &mut I,\n    ) -> Result<UpdateDatabaseResult, anyhow::Error> {\n        let replica_ctx = inst.replica_ctx().clone();\n        let system_logger = replica_ctx.logger.system_logger();\n        let stdb = &replica_ctx.relational_db;\n\n        let plan: MigratePlan = match policy.try_migrate(\n            self.info.database_identity,\n            old_module_info.module_hash,\n            &old_module_info.module_def,\n            self.info.module_hash,\n            &self.info.module_def,\n        ) {\n            Ok(plan) => plan,\n            Err(e) => {\n                return match e {\n                    MigrationPolicyError::AutoMigrateFailure(e) => Ok(UpdateDatabaseResult::AutoMigrateError(e.into())),\n                    _ => Ok(UpdateDatabaseResult::ErrorExecutingMigration(e.into())),\n                }\n            }\n        };\n\n        let program_hash = program.hash;\n        let host_type = HostType::from(program.kind);\n        let tx = stdb.begin_mut_tx(IsolationLevel::Serializable, Workload::Internal);\n        let (mut tx, _) = stdb.with_auto_rollback(tx, |tx| stdb.update_program(tx, program))?;\n        system_logger.info(&format!(\"Updated program to {program_hash}\"));\n\n        let auth_ctx = AuthCtx::for_current(replica_ctx.database.owner_identity);\n        let res = crate::db::update::update_database(stdb, &mut tx, auth_ctx, plan, system_logger);\n        let mut energy_quanta_used = FunctionBudget::ZERO;\n        let mut host_execution_duration = Duration::ZERO;\n\n        match res {\n            Err(e) => {\n                log::warn!(\"Database update failed: {} @ {}\", e, stdb.database_identity());\n                system_logger.warn(&format!(\"Database update failed: {e}\"));\n                let (_, tx_metrics, reducer) = stdb.rollback_mut_tx(tx);\n                stdb.report_mut_tx_metrics(reducer, tx_metrics, None);\n                Ok(UpdateDatabaseResult::ErrorExecutingMigration(e))\n            }\n            Ok(res) => {\n                system_logger.info(\"Database updated\");\n                log::info!(\"Database updated, {} host-type={}\", stdb.database_identity(), host_type);\n                let res: UpdateDatabaseResult = match res {\n                    crate::db::update::UpdateResult::Success => UpdateDatabaseResult::UpdatePerformed,\n                    crate::db::update::UpdateResult::EvaluateSubscribedViews => {\n                        let (out, trapped) = self.evaluate_subscribed_views(tx, inst)?;\n                        tx = out.tx;\n                        energy_quanta_used = out.energy_used;\n                        host_execution_duration = out.total_duration;\n\n                        if trapped || out.outcome != ViewOutcome::Success {\n                            let msg = match trapped {\n                                true => \"Trapped while evaluating views during database update\".to_string(),\n                                false => format!(\n                                    \"Views evaluation did not complete successfully during database update: {:?}\",\n                                    out.outcome\n                                ),\n                            };\n\n                            UpdateDatabaseResult::ErrorExecutingMigration(anyhow::anyhow!(msg))\n                        } else {\n                            UpdateDatabaseResult::UpdatePerformed\n                        }\n                    }\n                    crate::db::update::UpdateResult::RequiresClientDisconnect => {\n                        UpdateDatabaseResult::UpdatePerformedWithClientDisconnect\n                    }\n                };\n\n                if res.was_successful() {\n                    let event = ModuleEvent {\n                        timestamp: Timestamp::now(),\n                        caller_identity: self.info.owner_identity,\n                        caller_connection_id: None,\n                        function_call: ModuleFunctionCall::update(),\n                        status: EventStatus::Committed(DatabaseUpdate::default()),\n                        reducer_return_value: None,\n                        energy_quanta_used: energy_quanta_used.into(),\n                        host_execution_duration,\n                        request_id: None,\n                        timer: None,\n                    };\n                    //TODO: Return back event in `UpdateDatabaseResult`?\n                    let _ = commit_and_broadcast_event(&self.info.subscriptions, None, event, tx);\n                } else {\n                    let (_, tx_metrics, reducer) = stdb.rollback_mut_tx(tx);\n                    stdb.report_mut_tx_metrics(reducer, tx_metrics, None);\n                }\n                Ok(res)\n            }\n        }\n    }\n\n    /// Re-evaluates all views which have entries in `st_view_subs`.\n    fn evaluate_subscribed_views<I: WasmInstance>(\n        &mut self,\n        tx: MutTxId,\n        inst: &mut I,\n    ) -> Result<(ViewCallResult, bool), anyhow::Error> {\n        let view_calls = collect_subscribed_view_calls(&tx, &self.info.module_def, self.info.owner_identity)?;\n        Ok(self.execute_view_calls(tx, view_calls, inst))\n    }\n\n    pub(crate) async fn call_procedure<I: WasmInstance>(\n        &mut self,\n        params: CallProcedureParams,\n        inst: &mut I,\n    ) -> (CallProcedureReturn, bool) {\n        let CallProcedureParams {\n            timestamp,\n            caller_identity,\n            caller_connection_id,\n            timer,\n            procedure_id,\n            args,\n        } = params;\n\n        // We've already validated by this point that the procedure exists,\n        // so it's fine to use the panicking `procedure_by_id`.\n        let procedure_def = self.info.module_def.procedure_by_id(procedure_id);\n        let procedure_name = &procedure_def.name;\n\n        // TODO(observability): Add tracing spans, energy, metrics?\n        // These will require further thinking once we implement procedure suspend/resume,\n        // and so are not worth doing yet.\n\n        let op = ProcedureOp {\n            id: procedure_id,\n            name: procedure_name.clone(),\n            caller_identity,\n            caller_connection_id,\n            timestamp,\n            arg_bytes: args.get_bsatn().clone(),\n        };\n        let energy_fingerprint = FunctionFingerprint {\n            module_hash: self.info.module_hash,\n            module_identity: self.info.owner_identity,\n            caller_identity,\n            function_name: &procedure_def.name,\n        };\n\n        // TODO(procedure-energy): replace with call to separate function `procedure_budget`.\n        let budget = self.energy_monitor.reducer_budget(&energy_fingerprint);\n\n        let (result, tx_offset) = inst.call_procedure(op, budget).await;\n\n        let ProcedureExecuteResult {\n            stats:\n                ExecutionStats {\n                    memory_allocation,\n                    // TODO(procedure-energy): Do something with timing and energy.\n                    ..\n                },\n            call_result,\n        } = result;\n\n        // TODO(shub): deduplicate with reducer and view logic.\n        if self.allocated_memory != memory_allocation {\n            self.metric_wasm_memory_bytes.set(memory_allocation as i64);\n            self.allocated_memory = memory_allocation;\n        }\n\n        let trapped = call_result.is_err();\n\n        let result = match call_result {\n            Err(err) => {\n                inst.log_traceback(\"procedure\", &procedure_def.name, &err);\n\n                WORKER_METRICS\n                    .wasm_instance_errors\n                    .with_label_values(&self.info.database_identity, &self.info.module_hash, procedure_name)\n                    .inc();\n\n                // TODO(procedure-energy):\n                // if energy.remaining.get() == 0 {\n                //     return Err(ProcedureCallError::OutOfEnergy);\n                // } else\n                {\n                    Err(ProcedureCallError::InternalError(format!(\"{err}\")))\n                }\n            }\n            Ok(return_val) => {\n                let return_type = &procedure_def.return_type;\n                let seed = spacetimedb_sats::WithTypespace::new(self.info.module_def.typespace(), return_type);\n                seed.deserialize(bsatn::Deserializer::new(&mut &return_val[..]))\n                    .map_err(|err| ProcedureCallError::InternalError(format!(\"{err}\")))\n                    .map(|return_val| ProcedureCallResult {\n                        return_val,\n                        execution_duration: timer.map(|timer| timer.elapsed()).unwrap_or_default(),\n                        start_timestamp: timestamp,\n                    })\n            }\n        };\n\n        (CallProcedureReturn { result, tx_offset }, trapped)\n    }\n\n    /// Execute a reducer.\n    ///\n    /// If `Some` [`MutTxId`] is supplied, the reducer is called within the\n    /// context of this transaction. Otherwise, a fresh transaction is started.\n    ///\n    /// **Note** that the transaction is committed or rolled back by this method\n    /// depending on the outcome of the reducer call.\n    //\n    // TODO(kim): This should probably change in the future. The reason it is\n    // not straightforward is that the returned [`UpdateStatus`] is constructed\n    // from transaction data in the [`UpdateStatus::Committed`] (i.e. success)\n    // case.\n    //\n    /// The method also performs various measurements and records energy usage,\n    /// as well as broadcasting a [`ModuleEvent`] containing information about\n    /// the outcome of the call.\n    ///\n    /// The `bool` in the return type signifies whether there was an \"outer error\".\n    /// For WASM, this should be interpreted as a trap occurring.\n    pub(crate) fn call_reducer_with_tx<I: WasmInstance>(\n        &mut self,\n        tx: Option<MutTxId>,\n        params: CallReducerParams,\n        inst: &mut I,\n    ) -> (ReducerCallResult, bool) {\n        let CallReducerParams {\n            timestamp,\n            caller_identity,\n            caller_connection_id,\n            client,\n            request_id,\n            reducer_id,\n            args,\n            timer,\n        } = params;\n        let caller_connection_id_opt = (caller_connection_id != ConnectionId::ZERO).then_some(caller_connection_id);\n\n        let replica_ctx = inst.replica_ctx();\n        let stdb = &*replica_ctx.relational_db.clone();\n        let info = self.info.clone();\n        let reducer_def = info.module_def.reducer_by_id(reducer_id);\n        let reducer_name = &reducer_def.name;\n\n        // Do some `with_label_values`.\n        // TODO(perf, centril): consider caching this.\n        let _outer_span = start_call_function_span(reducer_name, &caller_identity, caller_connection_id_opt);\n\n        let op = ReducerOp {\n            id: reducer_id,\n            name: reducer_name,\n            caller_identity: &caller_identity,\n            caller_connection_id: &caller_connection_id,\n            timestamp,\n            args: &args,\n        };\n\n        let workload = Workload::Reducer(ReducerContext::from(op.clone()));\n        let tx = tx.unwrap_or_else(|| stdb.begin_mut_tx(IsolationLevel::Serializable, workload));\n        let mut tx_slot = inst.tx_slot();\n\n        let vm_metrics = self.vm_metrics.get_for_reducer_id(reducer_id);\n        let _guard = vm_metrics.timer_guard_for_reducer_plus_query(tx.timer);\n\n        let (mut tx, result) = tx_slot.set(tx, || {\n            self.call_function(caller_identity, reducer_name, |budget| inst.call_reducer(op, budget))\n        });\n\n        // Report execution metrics on each reducer call.\n        vm_metrics.report(&result.stats);\n\n        // An outer error occurred.\n        // This signifies a logic error in the module rather than a properly\n        // handled bad argument from the caller of a reducer.\n        // For WASM, this will be interpreted as a trap\n        // and that the instance must be discarded.\n        // However, that does not necessarily apply to e.g., V8.\n        let trapped = matches!(result.call_result, Err(ExecutionError::Trap(_)));\n\n        let (status, mut reducer_return_value) = match result.call_result {\n            Err(ExecutionError::Recoverable(err) | ExecutionError::Trap(err)) => {\n                inst.log_traceback(\"reducer\", reducer_name, &err);\n\n                (self.handle_outer_error(&result.stats.energy, reducer_name), None)\n            }\n            Err(ExecutionError::User(err)) => {\n                log_reducer_error(\n                    inst.replica_ctx(),\n                    timestamp,\n                    reducer_name,\n                    &err,\n                    &self.info.module_hash,\n                );\n                (EventStatus::FailedUser(err.into()), None)\n            }\n            // We haven't actually committed yet - `commit_and_broadcast_event` will commit\n            // for us and replace this with the actual database update.\n            Ok(return_value) => {\n                // If this is an OnDisconnect lifecycle event, remove the client from st_clients.\n                // We handle OnConnect events before running the reducer.\n                let res = match reducer_def.lifecycle {\n                    Some(Lifecycle::OnDisconnect) => {\n                        tx.delete_st_client(caller_identity, caller_connection_id, info.database_identity)\n                    }\n                    _ => Ok(()),\n                };\n                match res {\n                    Ok(()) => (EventStatus::Committed(DatabaseUpdate::default()), return_value),\n                    Err(err) => {\n                        let err = err.to_string();\n                        log_reducer_error(\n                            inst.replica_ctx(),\n                            timestamp,\n                            reducer_name,\n                            &err,\n                            &self.info.module_hash,\n                        );\n                        (EventStatus::FailedInternal(err), None)\n                    }\n                }\n            }\n        };\n\n        // Only re-evaluate and update views if the reducer's execution was successful\n        let (out, trapped) = if !trapped && matches!(status, EventStatus::Committed(_)) {\n            self.call_views_with_tx(tx, caller_identity, inst, timestamp)\n        } else {\n            (ViewCallResult::default(tx), trapped)\n        };\n\n        // Account for view execution in reducer reporting metrics\n        vm_metrics.report_energy_used(out.energy_used);\n        vm_metrics.report_total_duration(out.total_duration);\n        vm_metrics.report_abi_duration(out.abi_duration);\n\n        let status = match out.outcome {\n            ViewOutcome::BudgetExceeded => EventStatus::OutOfEnergy,\n            ViewOutcome::Failed(err) => EventStatus::FailedInternal(err),\n            ViewOutcome::Success => status,\n        };\n        if !matches!(status, EventStatus::Committed(_)) {\n            reducer_return_value = None;\n        }\n\n        let energy_quanta_used = result.stats.energy_used().into();\n        let total_duration = result.stats.total_duration();\n\n        let event = ModuleEvent {\n            timestamp,\n            caller_identity,\n            caller_connection_id: caller_connection_id_opt,\n            function_call: ModuleFunctionCall {\n                reducer: Some(reducer_name.clone()),\n                reducer_id,\n                args,\n            },\n            status,\n            reducer_return_value,\n            energy_quanta_used,\n            host_execution_duration: total_duration,\n            request_id,\n            timer,\n        };\n        let event = commit_and_broadcast_event(&info.subscriptions, client, event, out.tx).event;\n\n        let res = ReducerCallResult {\n            outcome: ReducerOutcome::from(&event.status),\n            energy_used: energy_quanta_used,\n            execution_duration: total_duration,\n        };\n\n        (res, trapped)\n    }\n\n    fn handle_outer_error(&mut self, energy: &EnergyStats, reducer_name: &str) -> EventStatus {\n        WORKER_METRICS\n            .wasm_instance_errors\n            .with_label_values(&self.info.database_identity, &self.info.module_hash, reducer_name)\n            .inc();\n\n        if energy.remaining.get() == 0 {\n            EventStatus::OutOfEnergy\n        } else {\n            EventStatus::FailedInternal(\"The instance encountered a fatal error.\".into())\n        }\n    }\n\n    /// Calls a function (reducer, view) and performs energy monitoring.\n    fn call_function<F, R: AsRef<ExecutionStats>>(\n        &mut self,\n        caller_identity: Identity,\n        function_name: &str,\n        vm_call_function: F,\n    ) -> R\n    where\n        F: FnOnce(FunctionBudget) -> R,\n    {\n        let energy_fingerprint = FunctionFingerprint {\n            module_hash: self.info.module_hash,\n            module_identity: self.info.owner_identity,\n            caller_identity,\n            function_name,\n        };\n        let budget = self.energy_monitor.reducer_budget(&energy_fingerprint);\n\n        let function_span = start_run_function_span(budget);\n\n        let result = vm_call_function(budget);\n\n        let stats: &ExecutionStats = result.as_ref();\n        let energy_used = stats.energy.used();\n        let energy_quanta_used = energy_used.into();\n        let timings = &stats.timings;\n        let memory_allocation = stats.memory_allocation;\n\n        self.energy_monitor\n            .record_reducer(&energy_fingerprint, energy_quanta_used, timings.total_duration);\n        if self.allocated_memory != memory_allocation {\n            self.metric_wasm_memory_bytes.set(memory_allocation as i64);\n            self.allocated_memory = memory_allocation;\n        }\n\n        maybe_log_long_running_function(function_name, timings.total_duration);\n\n        function_span\n            .record(\"timings.total_duration\", tracing::field::debug(timings.total_duration))\n            .record(\"energy.used\", tracing::field::debug(energy_used));\n\n        result\n    }\n\n    pub(crate) fn handle_cmd<I: WasmInstance>(&mut self, cmds: ViewCommand, inst: &mut I) -> (ViewCommandResult, bool) {\n        let info = self.info.clone();\n        let mut inst = RefInstance {\n            instance: inst,\n            common: self,\n        };\n        match cmds {\n            ViewCommand::AddSingleSubscription {\n                sender,\n                auth,\n                request,\n                _timer: timer,\n            } => {\n                let res = info\n                    .subscriptions\n                    .add_single_subscription_with_instance(&mut inst, sender, auth, request, timer, None);\n\n                match res {\n                    Ok((metrics, trapped)) => (ViewCommandResult::Subscription { result: Ok(metrics) }, trapped),\n                    Err(err) => (ViewCommandResult::Subscription { result: Err(err) }, false),\n                }\n            }\n            ViewCommand::AddLegacySubscription {\n                sender,\n                auth,\n                subscribe,\n                _timer: timer,\n            } => {\n                let res = info\n                    .subscriptions\n                    .add_legacy_subscriber_with_instance(&mut inst, sender, auth, subscribe, timer, None);\n\n                match res {\n                    Ok((metrics, trapped)) => (\n                        ViewCommandResult::Subscription {\n                            result: Ok(Some(metrics)),\n                        },\n                        trapped,\n                    ),\n                    Err(err) => (ViewCommandResult::Subscription { result: Err(err) }, false),\n                }\n            }\n            ViewCommand::AddSubscriptionV2 {\n                sender,\n                auth,\n                request,\n                _timer: timer,\n            } => {\n                let res = info\n                    .subscriptions\n                    .add_v2_subscription_with_instance(&mut inst, sender, auth, request, timer, None);\n\n                match res {\n                    Ok((metrics, trapped)) => (ViewCommandResult::Subscription { result: Ok(metrics) }, trapped),\n                    Err(err) => (ViewCommandResult::Subscription { result: Err(err) }, false),\n                }\n            }\n            ViewCommand::RemoveSubscriptionV2 {\n                sender,\n                auth,\n                request,\n                timer,\n            } => {\n                let res = info\n                    .subscriptions\n                    .remove_v2_subscription_with_instance(&mut inst, sender, auth, request, timer, None);\n\n                match res {\n                    Ok((metrics, trapped)) => (ViewCommandResult::Subscription { result: Ok(metrics) }, trapped),\n                    Err(err) => (ViewCommandResult::Subscription { result: Err(err) }, false),\n                }\n            }\n            ViewCommand::AddMultiSubscription {\n                sender,\n                auth,\n                request,\n                _timer: timer,\n            } => {\n                let res = info\n                    .subscriptions\n                    .add_multi_subscription_with_instance(&mut inst, sender, auth, request, timer, None);\n\n                match res {\n                    Ok((metrics, trapped)) => (ViewCommandResult::Subscription { result: Ok(metrics) }, trapped),\n                    Err(err) => (ViewCommandResult::Subscription { result: Err(err) }, false),\n                }\n            }\n            ViewCommand::Sql {\n                db,\n                sql_text,\n                auth,\n                subs,\n            } => {\n                let mut head = vec![];\n                let res = run_with_instance(&mut inst, db, sql_text, auth, subs, &mut head);\n\n                match res {\n                    Ok((result, trapped)) => (\n                        ViewCommandResult::Sql {\n                            result: Ok(result),\n                            head,\n                        },\n                        trapped,\n                    ),\n                    Err(err) => (ViewCommandResult::Sql { result: Err(err), head }, false),\n                }\n            }\n        }\n    }\n\n    /// Executes a view and materializes its result,\n    /// deleting any previously materialized rows.\n    ///\n    /// Similar to [`Self::call_reducer_with_tx`], but for views.\n    /// However, unlike [`Self::call_reducer_with_tx`],\n    /// it mutates a previously allocated [`MutTxId`] and returns it.\n    /// It does not commit the transaction.\n    pub(crate) fn call_view_with_tx<I: WasmInstance>(\n        &mut self,\n        tx: MutTxId,\n        params: CallViewParams,\n        inst: &mut I,\n    ) -> (ViewCallResult, bool) {\n        let CallViewParams {\n            view_name,\n            view_id,\n            table_id,\n            fn_ptr,\n            caller,\n            sender,\n            args,\n            row_type,\n            timestamp,\n        } = params;\n\n        let _outer_span = start_call_function_span(&view_name, &caller, None);\n\n        let mut tx_slot = inst.tx_slot();\n        let (mut tx, result) = tx_slot.set(tx, || {\n            self.call_function(caller, &view_name, |budget| match sender {\n                Some(sender) => inst.call_view(\n                    ViewOp {\n                        name: &view_name,\n                        view_id,\n                        table_id,\n                        fn_ptr,\n                        sender: &sender,\n                        args: &args,\n                        timestamp,\n                    },\n                    budget,\n                ),\n                None => inst.call_view_anon(\n                    AnonymousViewOp {\n                        name: &view_name,\n                        view_id,\n                        table_id,\n                        fn_ptr,\n                        args: &args,\n                        timestamp,\n                    },\n                    budget,\n                ),\n            })\n        });\n\n        // Report execution metrics on each view call.\n        self.vm_metrics\n            .get_for_view_id(view_id, &self.info.database_identity, &view_name)\n            .report(&result.stats);\n\n        let trapped = matches!(result.call_result, Err(ExecutionError::Trap(_)));\n\n        let outcome: ViewOutcome = match (result.call_result, sender) {\n            (Err(ExecutionError::Recoverable(err) | ExecutionError::Trap(err)), _) => {\n                inst.log_traceback(\"view\", &view_name, &err);\n                self.handle_outer_error(&result.stats.energy, &view_name).into()\n            }\n            // TODO: maybe do something else with user errors?\n            (Err(ExecutionError::User(err)), _) => {\n                inst.log_traceback(\"view\", &view_name, &anyhow::anyhow!(err));\n                self.handle_outer_error(&result.stats.energy, &view_name).into()\n            }\n            (Ok(raw), sender) => {\n                // This is wrapped in a closure to simplify error handling.\n                let outcome: Result<ViewOutcome, anyhow::Error> = (|| {\n                    let result = ViewResult::from_return_data(raw).context(\"Error parsing view result\")?;\n                    let typespace = self.info.module_def.typespace();\n                    let row_product_type = typespace\n                        .resolve(row_type)\n                        .resolve_refs()?\n                        .into_product()\n                        .map_err(|_| anyhow!(\"Error resolving row type for view\"))?;\n\n                    let rows = match result {\n                        ViewResult::Rows(bytes) => deserialize_view_rows(row_type, bytes, typespace)\n                            .context(\"Error deserializing rows returned by view\".to_string())?,\n                        ViewResult::RawSql(query) => self\n                            .run_query_for_view(\n                                &mut tx,\n                                &query,\n                                &row_product_type,\n                                &ViewCallInfo {\n                                    view_id,\n                                    table_id,\n                                    fn_ptr,\n                                    sender,\n                                },\n                            )\n                            .context(\"Error executing raw SQL returned by view\".to_string())?,\n                    };\n\n                    let replica_ctx = inst.replica_ctx();\n                    let stdb = &*replica_ctx.relational_db.clone();\n                    let res = match sender {\n                        Some(sender) => stdb.materialize_view(&mut tx, table_id, sender, rows),\n                        None => stdb.materialize_anonymous_view(&mut tx, table_id, rows),\n                    };\n\n                    res.context(\"Error materializing view\")?;\n\n                    Ok(ViewOutcome::Success)\n                })();\n                match outcome {\n                    Ok(outcome) => outcome,\n                    Err(err) => {\n                        log::error!(\"Error materializing view `{view_name}`: {err:?}\");\n                        ViewOutcome::Failed(format!(\"Error materializing view `{view_name}`: {err}\"))\n                    }\n                }\n            }\n        };\n\n        let res = ViewCallResult {\n            outcome,\n            tx,\n            energy_used: result.stats.energy_used(),\n            total_duration: result.stats.total_duration(),\n            abi_duration: result.stats.abi_duration(),\n        };\n\n        (res, trapped)\n    }\n\n    /// Compiles and runs a query that was returned from a view.\n    /// This tracks read dependencies for the view.\n    /// Note that this doesn't modify the resulting rows in any way.\n    fn run_query_for_view(\n        &self,\n        tx: &mut MutTxId,\n        the_query: &str,\n        expected_row_type: &ProductType,\n        call_info: &ViewCallInfo,\n    ) -> anyhow::Result<Vec<ProductValue>> {\n        run_query_for_view(tx, the_query, expected_row_type, call_info, self.info.database_identity)\n    }\n    /// A [`MutTxId`] knows which views must be updated (re-evaluated).\n    /// This method re-evaluates them and updates their backing tables.\n    pub(crate) fn call_views_with_tx<I: WasmInstance>(\n        &mut self,\n        tx: MutTxId,\n        caller: Identity,\n        inst: &mut I,\n        timestamp: Timestamp,\n    ) -> (ViewCallResult, bool) {\n        let mut instance = RefInstance {\n            common: self,\n            instance: inst,\n        };\n        ModuleHost::call_views_with_tx_at(tx, &mut instance, caller, timestamp)\n    }\n\n    /// Executes view calls and accumulate results.\n    /// Returns early if any call traps or fails.\n    fn execute_view_calls<I: WasmInstance>(\n        &mut self,\n        tx: MutTxId,\n        view_calls: Vec<CallViewParams>,\n        inst: &mut I,\n    ) -> (ViewCallResult, bool) {\n        let mut out = ViewCallResult::default(tx);\n        let mut trapped = false;\n\n        for params in view_calls {\n            let (result, call_trapped) = self.call_view_with_tx(out.tx, params, inst);\n\n            out.tx = result.tx;\n            out.outcome = result.outcome;\n            out.energy_used += result.energy_used;\n            out.total_duration += result.total_duration;\n            out.abi_duration += result.abi_duration;\n\n            trapped = trapped || call_trapped;\n\n            // Terminate early if execution failed\n            if trapped || !matches!(out.outcome, ViewOutcome::Success) {\n                break;\n            }\n        }\n\n        (out, trapped)\n    }\n\n    /// Empty the system tables tracking clients without running any lifecycle reducers.\n    pub(crate) fn clear_all_clients(&self) -> anyhow::Result<()> {\n        self.info.relational_db().clear_all_clients().map_err(Into::into)\n    }\n\n    pub(crate) async fn call_scheduled_function<I: WasmInstance>(\n        &mut self,\n        params: ScheduledFunctionParams,\n        inst: &mut I,\n    ) -> (CallScheduledFunctionResult, bool) {\n        crate::host::scheduler::call_scheduled_function(&self.info.clone(), params, self, inst).await\n    }\n}\n\nfn collect_subscribed_view_calls(\n    tx: &MutTxId,\n    module_def: &ModuleDef,\n    owner_identity: Identity,\n) -> Result<Vec<CallViewParams>, anyhow::Error> {\n    let mut view_calls = Vec::new();\n\n    for view in module_def.views() {\n        let ViewDef {\n            name: view_name,\n            is_anonymous,\n            fn_ptr,\n            product_type_ref,\n            ..\n        } = view;\n\n        let st_view = tx\n            .view_from_name(view_name)?\n            .ok_or_else(|| anyhow::anyhow!(\"view {} not found in database\", &view_name))?;\n\n        let view_id = st_view.view_id;\n        let table_id = st_view\n            .table_id\n            .ok_or_else(|| anyhow::anyhow!(\"view {} does not have a backing table in database\", &view_name))?;\n        let subs = tx.lookup_st_view_subs(view_id)?;\n\n        if *is_anonymous {\n            if subs.is_empty() {\n                continue;\n            }\n            view_calls.push(CallViewParams {\n                view_name: view_name.clone(),\n                view_id,\n                table_id,\n                fn_ptr: *fn_ptr,\n                caller: owner_identity,\n                sender: None,\n                args: ArgsTuple::nullary(),\n                row_type: *product_type_ref,\n                timestamp: Timestamp::now(),\n            });\n            continue;\n        }\n\n        for sub in subs {\n            view_calls.push(CallViewParams {\n                view_name: view_name.clone(),\n                view_id,\n                table_id,\n                fn_ptr: *fn_ptr,\n                caller: owner_identity,\n                sender: Some(sub.identity.into()),\n                args: ArgsTuple::nullary(),\n                row_type: *product_type_ref,\n                timestamp: Timestamp::now(),\n            });\n        }\n    }\n\n    Ok(view_calls)\n}\n\n/// Pre-fetched VM metrics counters for all reducers and views in a module.\n/// Anonymous views have lazily fetched metrics counters.\nstruct AllVmMetrics {\n    // We use a `Vec` here as the number of reducers + views\n    // will likely be lower than e.g., 128, which would take up a page (4096 / 32).\n    // TODO(perf, centril): Define a `VecMapWithFallback<N>`\n    // that falls back to `HashMap` when exceeding `N` entries.\n    // This could be useful elsewhere for e.g., TableId => X maps and similar.\n    counters: Vec<VmMetrics>,\n    num_reducers: u32,\n}\n\nimpl AllVmMetrics {\n    /// Pre-fetch all vm metrics counters for the module in `info`.\n    fn new(info: &ModuleInfo) -> Self {\n        // These are the reducers:\n        let def = &info.module_def;\n        let reducers = def.reducer_ids_and_defs();\n        let num_reducers = reducers.len() as u32;\n        let reducers = reducers.map(|(_, def)| def.name());\n\n        // These are the views:\n        let views = def.views().map(|def| def.name());\n\n        // Pre-fetch the metrics for both:\n        let counters = reducers\n            .chain(views)\n            .map(|name| VmMetrics::new(&info.database_identity, name))\n            .collect();\n\n        Self { counters, num_reducers }\n    }\n\n    #[inline]\n    fn get_for_index(&self, index: u32) -> Option<VmMetrics> {\n        self.counters.get(index as usize).cloned()\n    }\n\n    /// Returns the vm metrics counters for `id`,\n    /// or panics if `id` was not pre-fetched in [`AllVmMetrics::new`].\n    #[inline]\n    fn get_for_reducer_id(&self, id: ReducerId) -> VmMetrics {\n        self.get_for_index(id.0)\n            .expect(\"all counters for reducers should've been pre-fetched\")\n    }\n\n    /// Returns the vm metrics counters for `id`,\n    /// or panics if `id` was not pre-fetched in [`AllVmMetrics::new`].\n    #[inline]\n    fn get_for_view_id(&self, id: ViewId, identity: &Identity, name: &str) -> VmMetrics {\n        // Cosunters for the first view starts after counters for the last reducer.\n        self.get_for_index(self.num_reducers + id.0)\n            // For anonymous views, the `id` doesn't have pre-fetched counters available.\n            // Reducers shouldn't have to pay for adding an `Option` layer in the map,\n            // which would be necessary due to `id`s being random here,\n            // so just create `VmMetrics` on the fly instead.\n            // TODO(perf, centril): We could ostensibly add another map for anonymous views,\n            // but this doesn't seem to be a pressing performance concern at the moment.\n            .unwrap_or_else(|| VmMetrics::new(identity, name))\n    }\n}\n\n/// VM-related metrics for reducer execution.\n#[derive(Clone)]\nstruct VmMetrics {\n    /// The time spent executing a reducer + plus evaluating its subscription queries.\n    reducer_plus_query_duration: Histogram,\n    /// The total VM fuel used.\n    reducer_fuel_used: IntCounter,\n    /// The total runtime of reducer calls.\n    reducer_duration_usec: IntCounter,\n    /// The total time spent in reducer ABI calls.\n    reducer_abi_time_usec: IntCounter,\n}\n\nimpl VmMetrics {\n    /// Returns new metrics counters for `database_identity` and `reducer_name`.\n    fn new(database_identity: &Identity, reducer_name: &str) -> Self {\n        let reducer_plus_query_duration = WORKER_METRICS\n            .reducer_plus_query_duration\n            .with_label_values(database_identity, reducer_name);\n        let reducer_fuel_used = DB_METRICS\n            .reducer_wasmtime_fuel_used\n            .with_label_values(database_identity, reducer_name);\n        let reducer_duration_usec = DB_METRICS\n            .reducer_duration_usec\n            .with_label_values(database_identity, reducer_name);\n        let reducer_abi_time_usec = DB_METRICS\n            .reducer_abi_time_usec\n            .with_label_values(database_identity, reducer_name);\n\n        Self {\n            reducer_plus_query_duration,\n            reducer_fuel_used,\n            reducer_duration_usec,\n            reducer_abi_time_usec,\n        }\n    }\n\n    /// Returns a timer guard for `reducer_plus_query_duration`.\n    fn timer_guard_for_reducer_plus_query(&self, start: Instant) -> TimerGuard {\n        self.reducer_plus_query_duration.clone().with_timer(start)\n    }\n\n    fn report_energy_used(&self, energy_used: FunctionBudget) {\n        self.reducer_fuel_used.inc_by(energy_used.get());\n    }\n\n    fn report_total_duration(&self, duration: Duration) {\n        self.reducer_duration_usec.inc_by(duration.as_micros() as u64);\n    }\n\n    fn report_abi_duration(&self, duration: Duration) {\n        self.reducer_abi_time_usec.inc_by(duration.as_micros() as u64);\n    }\n\n    /// Reports some VM metrics.\n    fn report(&self, stats: &ExecutionStats) {\n        let energy_used = stats.energy.used();\n        let reducer_duration = stats.timings.total_duration;\n        let abi_time = stats.timings.wasm_instance_env_call_times.sum();\n        self.report_energy_used(energy_used);\n        self.report_total_duration(reducer_duration);\n        self.report_abi_duration(abi_time);\n    }\n}\n\n/// Starts the `call_function` span.\nfn start_call_function_span(\n    function_name: &str,\n    caller_identity: &Identity,\n    caller_connection_id_opt: Option<ConnectionId>,\n) -> EnteredSpan {\n    tracing::trace_span!(\"call_function\",\n        function_name,\n        %caller_identity,\n        caller_connection_id = caller_connection_id_opt.map(tracing::field::debug),\n    )\n    .entered()\n}\n\n/// Starts the `run_function` span.\nfn start_run_function_span(budget: FunctionBudget) -> EnteredSpan {\n    tracing::trace_span!(\n        \"run_function\",\n        timings.total_duration = tracing::field::Empty,\n        energy.budget = budget.get(),\n        energy.used = tracing::field::Empty,\n    )\n    .entered()\n}\n\n/// Logs a tracing message if a reducer doesn't finish in a single frame at 60 FPS.\nfn maybe_log_long_running_function(reducer_name: &str, total_duration: Duration) {\n    const FRAME_LEN_60FPS: Duration = Duration::from_secs(1).checked_div(60).unwrap();\n    if total_duration > FRAME_LEN_60FPS {\n        tracing::debug!(\n            message = \"Long running reducer finished executing\",\n            reducer_name,\n            ?total_duration,\n        );\n    }\n}\n\n/// Logs an error `message` for `reducer` at `timestamp` into `replica_ctx`.\nfn log_reducer_error(\n    replica_ctx: &ReplicaContext,\n    timestamp: Timestamp,\n    reducer: &str,\n    message: &str,\n    module_hash: &Hash,\n) {\n    use database_logger::Record;\n\n    WORKER_METRICS\n        .sender_errors\n        .with_label_values(&replica_ctx.database_identity, module_hash, reducer)\n        .inc();\n\n    log::info!(\"reducer returned error: {message}\");\n\n    let record = Record {\n        ts: chrono::DateTime::from_timestamp_micros(timestamp.to_micros_since_unix_epoch()).unwrap(),\n        function: Some(reducer),\n        ..Record::injected(message)\n    };\n    replica_ctx.logger.write(database_logger::LogLevel::Error, &record, &());\n}\n\n/*\n/// Detects lifecycle events for connecting/disconnecting a new client\n/// and inserts/removes into `st_clients` depending on which.\nfn lifecyle_modifications_to_tx(\n    lifecycle: Option<Lifecycle>,\n    caller_id: Identity,\n    caller_conn_id: ConnectionId,\n    db_id: Identity,\n    tx: &mut MutTxId,\n) -> Result<(), Box<str>> {\n    match lifecycle {\n        Some(Lifecycle::OnConnect) => tx.insert_st_client(caller_id, caller_conn_id),\n        Some(Lifecycle::OnDisconnect) => tx.delete_st_client(caller_id, caller_conn_id, db_id),\n        _ => Ok(()),\n    }\n    .map_err(|e| e.to_string().into())\n}\n*/\n\npub trait InstanceOp {\n    fn name(&self) -> &Identifier;\n    fn timestamp(&self) -> Timestamp;\n    fn call_type(&self) -> FuncCallType;\n}\n\n/// Describes a view call in a cheaply shareable way.\n#[derive(Clone, Debug)]\npub struct ViewOp<'a> {\n    pub name: &'a Identifier,\n    pub view_id: ViewId,\n    pub table_id: TableId,\n    pub fn_ptr: ViewFnPtr,\n    pub args: &'a ArgsTuple,\n    pub sender: &'a Identity,\n    pub timestamp: Timestamp,\n}\n\nimpl InstanceOp for ViewOp<'_> {\n    fn name(&self) -> &Identifier {\n        self.name\n    }\n\n    fn timestamp(&self) -> Timestamp {\n        self.timestamp\n    }\n\n    fn call_type(&self) -> FuncCallType {\n        FuncCallType::View(ViewCallInfo {\n            view_id: self.view_id,\n            table_id: self.table_id,\n            fn_ptr: self.fn_ptr,\n            sender: Some(*self.sender),\n        })\n    }\n}\n\n/// Describes an anonymous view call in a cheaply shareable way.\n#[derive(Clone, Debug)]\npub struct AnonymousViewOp<'a> {\n    pub name: &'a Identifier,\n    pub view_id: ViewId,\n    pub table_id: TableId,\n    pub fn_ptr: ViewFnPtr,\n    pub args: &'a ArgsTuple,\n    pub timestamp: Timestamp,\n}\n\nimpl InstanceOp for AnonymousViewOp<'_> {\n    fn name(&self) -> &Identifier {\n        self.name\n    }\n\n    fn timestamp(&self) -> Timestamp {\n        self.timestamp\n    }\n\n    fn call_type(&self) -> FuncCallType {\n        FuncCallType::View(ViewCallInfo {\n            view_id: self.view_id,\n            table_id: self.table_id,\n            fn_ptr: self.fn_ptr,\n            sender: None,\n        })\n    }\n}\n\n/// Describes a reducer call in a cheaply shareable way.\n#[derive(Clone, Debug)]\npub struct ReducerOp<'a> {\n    pub id: ReducerId,\n    pub name: &'a ReducerName,\n    pub caller_identity: &'a Identity,\n    pub caller_connection_id: &'a ConnectionId,\n    pub timestamp: Timestamp,\n    /// The arguments passed to the reducer.\n    pub args: &'a ArgsTuple,\n}\n\nimpl InstanceOp for ReducerOp<'_> {\n    fn name(&self) -> &Identifier {\n        self.name.as_identifier()\n    }\n    fn timestamp(&self) -> Timestamp {\n        self.timestamp\n    }\n    fn call_type(&self) -> FuncCallType {\n        FuncCallType::Reducer\n    }\n}\n\nimpl From<ReducerOp<'_>> for execution_context::ReducerContext {\n    fn from(\n        ReducerOp {\n            id: _,\n            name,\n            caller_identity,\n            caller_connection_id,\n            timestamp,\n            args,\n        }: ReducerOp<'_>,\n    ) -> Self {\n        Self {\n            name: name.clone(),\n            caller_identity: *caller_identity,\n            caller_connection_id: *caller_connection_id,\n            timestamp,\n            arg_bsatn: args.get_bsatn().clone(),\n        }\n    }\n}\n\n/// Describes a procedure call in a cheaply shareable way.\n#[derive(Clone, Debug)]\npub struct ProcedureOp {\n    pub id: ProcedureId,\n    pub name: Identifier,\n    pub caller_identity: Identity,\n    pub caller_connection_id: ConnectionId,\n    pub timestamp: Timestamp,\n    pub arg_bytes: Bytes,\n}\n\nimpl InstanceOp for ProcedureOp {\n    fn name(&self) -> &Identifier {\n        &self.name\n    }\n    fn timestamp(&self) -> Timestamp {\n        self.timestamp\n    }\n    fn call_type(&self) -> FuncCallType {\n        FuncCallType::Procedure\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::collect_subscribed_view_calls;\n    use crate::db::relational_db::tests_utils::{begin_mut_tx, TestDB};\n    use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9Builder;\n    use spacetimedb_lib::{AlgebraicType, Identity, ProductType};\n    use spacetimedb_primitives::ArgId;\n    use spacetimedb_sats::raw_identifier::RawIdentifier;\n    use spacetimedb_schema::def::ModuleDef;\n\n    fn module_def_for_view(name: &str, is_anonymous: bool) -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n        let name = RawIdentifier::new(name);\n        let type_ref = builder.add_algebraic_type(\n            [],\n            name.clone(),\n            AlgebraicType::Product(ProductType::from_iter([(\"x\", AlgebraicType::U8)])),\n            true,\n        );\n\n        builder.add_view(\n            name.clone(),\n            0,\n            true,\n            is_anonymous,\n            ProductType::unit(),\n            AlgebraicType::array(AlgebraicType::Ref(type_ref)),\n        );\n\n        builder.finish().try_into().expect(\"test module def should be valid\")\n    }\n\n    /// Regression test for evaluating anonymous views.\n    ///\n    /// Anonymous views have one shared materialization,\n    /// so we should only re-evaluate once even if there are multiple subscribers.\n    #[test]\n    fn test_dedup_anonymous_view_calls() -> anyhow::Result<()> {\n        let stdb = TestDB::in_memory()?;\n        let module_def = module_def_for_view(\"anonymous_view\", true);\n        let view_def = module_def.view(\"anonymous_view\").expect(\"view should exist\");\n\n        let mut tx = begin_mut_tx(&stdb);\n        let (view_id, _table_id) = stdb.create_view(&mut tx, &module_def, view_def)?;\n        tx.subscribe_view(view_id, ArgId::SENTINEL, Identity::ZERO)?;\n        tx.subscribe_view(view_id, ArgId::SENTINEL, Identity::ONE)?;\n\n        // Two subscriber rows exist, but anonymous views should still be reevaluated once\n        // because they share a single materialization.\n        let calls = collect_subscribed_view_calls(&tx, &module_def, Identity::ZERO)?;\n\n        assert_eq!(\n            calls.len(),\n            1,\n            \"anonymous views should only be reevaluated once even with multiple subscriber rows\"\n        );\n        assert_eq!(calls[0].view_id, view_id);\n        assert_eq!(calls[0].sender, None);\n        Ok(())\n    }\n\n    /// Regression test for evaluating sender-scoped views.\n    ///\n    /// These views have separate materializations per sender,\n    /// so reevaluation must emit one call per subscribed sender.\n    #[test]\n    fn test_distinct_sender_scoped_view_calls() -> anyhow::Result<()> {\n        let stdb = TestDB::in_memory()?;\n        let module_def = module_def_for_view(\"sender_view\", false);\n        let view_def = module_def.view(\"sender_view\").expect(\"view should exist\");\n\n        let mut tx = begin_mut_tx(&stdb);\n        let (view_id, _table_id) = stdb.create_view(&mut tx, &module_def, view_def)?;\n        tx.subscribe_view(view_id, ArgId::SENTINEL, Identity::ZERO)?;\n        tx.subscribe_view(view_id, ArgId::SENTINEL, Identity::ONE)?;\n\n        // Sender-backed views keep one materialization per sender, so reevaluation must\n        // preserve both callers.\n        let calls = collect_subscribed_view_calls(&tx, &module_def, Identity::ZERO)?;\n        let senders: Vec<_> = calls.iter().filter_map(|call| call.sender).collect();\n\n        assert_eq!(calls.len(), 2, \"sender views should still reevaluate once per sender\");\n        assert!(senders.contains(&Identity::ZERO));\n        assert!(senders.contains(&Identity::ONE));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/wasm_common.rs",
    "content": "pub mod abi;\npub mod instrumentation;\npub mod module_host_actor;\n\nuse std::fmt;\nuse std::num::NonZeroU16;\nuse std::time::Instant;\n\nuse super::{scheduler::ScheduleError, AbiCall};\nuse crate::error::{DBError, DatastoreError, IndexError, NodesError};\nuse spacetimedb_primitives::errno;\nuse spacetimedb_sats::typespace::TypeRefError;\nuse spacetimedb_table::table::UniqueConstraintViolation;\n\npub const CALL_REDUCER_DUNDER: &str = \"__call_reducer__\";\n\npub const CALL_PROCEDURE_DUNDER: &str = \"__call_procedure__\";\n\npub const CALL_VIEW_DUNDER: &str = \"__call_view__\";\n\npub const CALL_VIEW_ANON_DUNDER: &str = \"__call_view_anon__\";\n\npub const DESCRIBE_MODULE_DUNDER: &str = \"__describe_module__\";\n\n/// functions with this prefix run prior to __setup__, initializing global variables and the like\npub const PREINIT_DUNDER: &str = \"__preinit__\";\n/// initializes the user code in the module. fallible\npub const SETUP_DUNDER: &str = \"__setup__\";\n\n#[derive(Debug, Clone)]\n#[allow(unused)]\npub enum WasmType {\n    I32,\n    I64,\n    F32,\n    F64,\n    V128,\n    #[allow(clippy::box_collection)]\n    Ref(Box<String>),\n}\n\nimpl fmt::Display for WasmType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(match self {\n            WasmType::I32 => \"i32\",\n            WasmType::I64 => \"i64\",\n            WasmType::F32 => \"f32\",\n            WasmType::F64 => \"f64\",\n            WasmType::V128 => \"v128\",\n            WasmType::Ref(r) => r,\n        })\n    }\n}\n\nimpl PartialEq<WasmType> for wasmtime::ValType {\n    fn eq(&self, other: &WasmType) -> bool {\n        matches!(\n            (self, other),\n            (Self::I32, WasmType::I32)\n                | (Self::I64, WasmType::I64)\n                | (Self::F32, WasmType::F32)\n                | (Self::F64, WasmType::F64)\n                | (Self::V128, WasmType::V128)\n        )\n    }\n}\nimpl PartialEq<&WasmType> for wasmtime::ValType {\n    fn eq(&self, other: &&WasmType) -> bool {\n        self.eq(*other)\n    }\n}\nimpl From<wasmtime::ValType> for WasmType {\n    fn from(ty: wasmtime::ValType) -> WasmType {\n        match ty {\n            wasmtime::ValType::I32 => WasmType::I32,\n            wasmtime::ValType::I64 => WasmType::I64,\n            wasmtime::ValType::F32 => WasmType::F32,\n            wasmtime::ValType::F64 => WasmType::F64,\n            wasmtime::ValType::V128 => WasmType::V128,\n            wasmtime::ValType::Ref(ty) => WasmType::Ref(Box::new(ty.to_string())),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct FuncSig<T: AsRef<[WasmType]>> {\n    params: T,\n    results: T,\n}\nimpl<T: AsRef<[WasmType]>> fmt::Display for FuncSig<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"(func\")?;\n        let (params, results) = (self.params.as_ref(), self.results.as_ref());\n        if !params.is_empty() {\n            write!(f, \" (param\")?;\n            for p in params {\n                write!(f, \" {p}\")?;\n            }\n            write!(f, \")\")?;\n        }\n        if !results.is_empty() {\n            write!(f, \" (result\")?;\n            for r in results {\n                write!(f, \" {r}\")?;\n            }\n            write!(f, \")\")?;\n        }\n        write!(f, \")\")\n    }\n}\ntype StaticFuncSig = FuncSig<&'static [WasmType]>;\ntype BoxFuncSig = FuncSig<Box<[WasmType]>>;\nimpl StaticFuncSig {\n    const fn new(params: &'static [WasmType], results: &'static [WasmType]) -> Self {\n        Self { params, results }\n    }\n}\nimpl<T: AsRef<[WasmType]>> PartialEq<FuncSig<T>> for wasmtime::ExternType {\n    fn eq(&self, other: &FuncSig<T>) -> bool {\n        self.func()\n            .is_some_and(|f| f.params().eq(other.params.as_ref()) && f.results().eq(other.results.as_ref()))\n    }\n}\nimpl FuncSigLike for wasmtime::ExternType {\n    fn to_func_sig(&self) -> Option<BoxFuncSig> {\n        self.func().map(|f| FuncSig {\n            params: f.params().map(Into::into).collect(),\n            results: f.results().map(Into::into).collect(),\n        })\n    }\n    fn is_memory(&self) -> bool {\n        matches!(self, wasmtime::ExternType::Memory(_))\n    }\n}\n\npub trait FuncSigLike: PartialEq<StaticFuncSig> {\n    fn to_func_sig(&self) -> Option<BoxFuncSig>;\n    fn is_memory(&self) -> bool;\n}\n\nconst PREINIT_SIG: StaticFuncSig = FuncSig::new(&[], &[]);\nconst INIT_SIG: StaticFuncSig = FuncSig::new(&[WasmType::I32], &[WasmType::I32]);\nconst DESCRIBE_MODULE_SIG: StaticFuncSig = FuncSig::new(&[WasmType::I32], &[]);\nconst CALL_REDUCER_SIG: StaticFuncSig = FuncSig::new(\n    &[\n        WasmType::I32, // Reducer ID\n        // Sender's `Identity` broken into 4 u64s.\n        // ----------------------------------------------------\n        WasmType::I64, // `sender_0` contains bytes `[0 ..8 ]`.\n        WasmType::I64, // `sender_1` contains bytes `[8 ..16]`.\n        WasmType::I64, // `sender_1` contains bytes `[16..24]`.\n        WasmType::I64, // `sender_1` contains bytes `[24..32]`.\n        // ----------------------------------------------------\n        // Caller's `ConnectionId` broken into 2 u64s.\n        // ----------------------------------------------------\n        WasmType::I64, // `conn_id_0` contains bytes `[0..8 ]`.\n        WasmType::I64, // `conn_id_1` contains bytes `[8..16]`.\n        // ----------------------------------------------------\n        WasmType::I64, // Timestamp\n        WasmType::I32, // Args source buffer\n        WasmType::I32, // Errors sink buffer\n    ],\n    &[\n        WasmType::I32, // Result code\n    ],\n);\n\n#[derive(thiserror::Error, Debug)]\npub enum ValidationError {\n    #[error(\"bad {kind} signature for {name:?}; expected {expected} got {actual}\")]\n    MismatchedSignature {\n        kind: &'static str,\n        name: Box<str>,\n        expected: StaticFuncSig,\n        actual: BoxFuncSig,\n    },\n    #[error(\"expected {name:?} export to be a {kind} with signature {expected}, but it wasn't a function at all\")]\n    NotAFunction {\n        kind: &'static str,\n        name: Box<str>,\n        expected: StaticFuncSig,\n    },\n    #[error(\"there should be a memory export called \\\"memory\\\" but it does not exist\")]\n    NoMemory,\n    #[error(\"there should be a function called {name:?} but it does not exist\")]\n    NoFunction { name: &'static str },\n    #[error(transparent)]\n    TypeRef(#[from] TypeRefError),\n}\n\n#[derive(Default)]\npub struct FuncNames {\n    pub preinits: Vec<String>,\n}\nimpl FuncNames {\n    fn validate_signature<T>(\n        kind: &'static str,\n        ty: &T,\n        name: &str,\n        expected: StaticFuncSig,\n    ) -> Result<(), ValidationError>\n    where\n        T: FuncSigLike,\n    {\n        if *ty == expected {\n            Ok(())\n        } else {\n            let name = name.into();\n            Err(match ty.to_func_sig() {\n                Some(actual) => ValidationError::MismatchedSignature {\n                    kind,\n                    name,\n                    expected,\n                    actual,\n                },\n                None => ValidationError::NotAFunction { kind, name, expected },\n            })\n        }\n    }\n    pub fn update_from_general<T>(&mut self, sym: &str, ty: &T) -> Result<(), ValidationError>\n    where\n        T: FuncSigLike,\n    {\n        if sym == SETUP_DUNDER {\n            Self::validate_signature(\"setup\", ty, sym, INIT_SIG)?;\n        } else if let Some(name) = sym.strip_prefix(PREINIT_DUNDER) {\n            Self::validate_signature(\"preinit\", ty, name, PREINIT_SIG)?;\n            self.preinits.push(sym.to_owned());\n        }\n        Ok(())\n    }\n    pub fn check_required<F, T>(get_export: F) -> Result<(), ValidationError>\n    where\n        F: Fn(&str) -> Option<T>,\n        T: FuncSigLike,\n    {\n        get_export(\"memory\")\n            .filter(|t| t.is_memory())\n            .ok_or(ValidationError::NoMemory)?;\n\n        let get_func = |name| get_export(name).ok_or(ValidationError::NoFunction { name });\n\n        let sig = get_func(CALL_REDUCER_DUNDER)?;\n        Self::validate_signature(\"call_reducer\", &sig, CALL_REDUCER_DUNDER, CALL_REDUCER_SIG)?;\n\n        let sig = get_func(DESCRIBE_MODULE_DUNDER)?;\n        Self::validate_signature(\"describe_module\", &sig, DESCRIBE_MODULE_DUNDER, DESCRIBE_MODULE_SIG)?;\n\n        Ok(())\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\n#[error(transparent)]\npub enum ModuleCreationError {\n    WasmCompileError(anyhow::Error),\n    Init(#[from] module_host_actor::InitializationError),\n    Abi(#[from] abi::AbiVersionError),\n}\n\npub trait ResourceIndex {\n    type Resource;\n    fn from_u32(i: u32) -> Self;\n    fn to_u32(&self) -> u32;\n}\n\nmacro_rules! decl_index {\n    ($name:ident => $resource:ty) => {\n        #[derive(Copy, Clone)]\n        #[repr(transparent)]\n        pub(super) struct $name(pub u32);\n\n        impl ResourceIndex for $name {\n            type Resource = $resource;\n            fn from_u32(i: u32) -> Self {\n                Self(i + 1)\n            }\n            fn to_u32(&self) -> u32 {\n                self.0 - 1\n            }\n        }\n\n        impl $name {\n            // for WasmPointee to work in crate::host::wasmtime\n            #[allow(unused)]\n            #[doc(hidden)]\n            pub(super) fn to_le_bytes(self) -> [u8; 4] {\n                self.0.to_le_bytes()\n            }\n            #[allow(unused)]\n            #[doc(hidden)]\n            pub(super) fn from_le_bytes(b: [u8; 4]) -> Self {\n                Self(u32::from_le_bytes(b))\n            }\n        }\n    };\n}\n\npub struct ResourceSlab<I: ResourceIndex> {\n    slab: slab::Slab<I::Resource>,\n}\n\nimpl<I: ResourceIndex> Default for ResourceSlab<I> {\n    fn default() -> Self {\n        Self {\n            slab: slab::Slab::default(),\n        }\n    }\n}\n\nimpl<I: ResourceIndex> ResourceSlab<I> {\n    pub fn insert(&mut self, data: I::Resource) -> I {\n        let idx = self.slab.insert(data) as u32;\n        I::from_u32(idx)\n    }\n\n    pub fn get_mut(&mut self, handle: I) -> Option<&mut I::Resource> {\n        self.slab.get_mut(handle.to_u32() as usize)\n    }\n\n    pub fn take(&mut self, handle: I) -> Option<I::Resource> {\n        self.slab.try_remove(handle.to_u32() as usize)\n    }\n}\n\ndecl_index!(RowIterIdx => std::vec::IntoIter<Vec<u8>>);\npub(super) type RowIters = ResourceSlab<RowIterIdx>;\n\npub(crate) struct TimingSpan {\n    pub start: Instant,\n    pub name: String,\n}\n\nimpl TimingSpan {\n    pub fn new(name: String) -> Self {\n        Self {\n            start: Instant::now(),\n            name,\n        }\n    }\n}\n\ndecl_index!(TimingSpanIdx => TimingSpan);\npub(super) type TimingSpanSet = ResourceSlab<TimingSpanIdx>;\n\n/// Converts a [`NodesError`] to an error code, if possible.\npub fn err_to_errno(err: NodesError) -> Result<(NonZeroU16, Option<String>), NodesError> {\n    let errno = match err {\n        NodesError::NotInTransaction => errno::NOT_IN_TRANSACTION,\n        NodesError::NotInAnonTransaction => errno::TRANSACTION_NOT_ANONYMOUS,\n        NodesError::WouldBlockTransaction(_) => errno::WOULD_BLOCK_TRANSACTION,\n        NodesError::DecodeRow(_) => errno::BSATN_DECODE_ERROR,\n        NodesError::DecodeValue(_) => errno::BSATN_DECODE_ERROR,\n        NodesError::TableNotFound => errno::NO_SUCH_TABLE,\n        NodesError::IndexNotFound => errno::NO_SUCH_INDEX,\n        NodesError::IndexNotUnique => errno::INDEX_NOT_UNIQUE,\n        NodesError::IndexRowNotFound => errno::NO_SUCH_ROW,\n        NodesError::IndexCannotSeekRange => errno::WRONG_INDEX_ALGO,\n        NodesError::ScheduleError(ScheduleError::DelayTooLong(_)) => errno::SCHEDULE_AT_DELAY_TOO_LONG,\n        NodesError::HttpError(message) => return Ok((errno::HTTP_ERROR, Some(message))),\n        NodesError::Internal(ref internal) => match **internal {\n            DBError::Datastore(DatastoreError::Index(IndexError::UniqueConstraintViolation(\n                UniqueConstraintViolation {\n                    constraint_name: _,\n                    table_name: _,\n                    cols: _,\n                    value: _,\n                },\n            ))) => errno::UNIQUE_ALREADY_EXISTS,\n            _ => return Err(err),\n        },\n        _ => return Err(err),\n    };\n    Ok((errno, None))\n}\n\n/// Converts a [`NodesError`] to an error code and logs, if possible.\npub fn err_to_errno_and_log<C: From<u16>>(func: AbiCall, err: NodesError) -> anyhow::Result<(C, Option<String>)> {\n    let (errno, message) = err_to_errno(err).map_err(|err| AbiRuntimeError { func, err })?;\n    log::debug!(\n        \"abi call to {func} returned an errno: {errno} ({})\",\n        errno::strerror(errno).unwrap_or(\"<unknown>\")\n    );\n    Ok((errno.get().into(), message))\n}\n\n#[derive(Debug, thiserror::Error)]\n#[error(\"runtime error calling {func}: {err}\")]\npub struct AbiRuntimeError {\n    pub func: AbiCall,\n    #[source]\n    pub err: NodesError,\n}\n\nmacro_rules! abi_funcs {\n    ($link_sync:ident,  $link_async:ident) => {\n        $link_sync! {\n            \"spacetime_10.0\"::table_id_from_name,\n            \"spacetime_10.0\"::datastore_table_row_count,\n            \"spacetime_10.0\"::datastore_table_scan_bsatn,\n            \"spacetime_10.0\"::row_iter_bsatn_advance,\n            \"spacetime_10.0\"::row_iter_bsatn_close,\n            \"spacetime_10.0\"::datastore_insert_bsatn,\n            \"spacetime_10.0\"::datastore_update_bsatn,\n            \"spacetime_10.0\"::datastore_delete_all_by_eq_bsatn,\n            \"spacetime_10.0\"::bytes_source_read,\n            \"spacetime_10.0\"::bytes_sink_write,\n            \"spacetime_10.0\"::console_log,\n            \"spacetime_10.0\"::console_timer_start,\n            \"spacetime_10.0\"::console_timer_end,\n            \"spacetime_10.0\"::index_id_from_name,\n            \"spacetime_10.0\"::datastore_index_scan_range_bsatn,\n            \"spacetime_10.0\"::datastore_delete_by_index_scan_range_bsatn,\n            \"spacetime_10.0\"::datastore_btree_scan_bsatn,\n            \"spacetime_10.0\"::datastore_delete_by_btree_scan_bsatn,\n            \"spacetime_10.0\"::identity,\n\n            // unstable:\n            \"spacetime_10.0\"::volatile_nonatomic_schedule_immediate,\n\n            \"spacetime_10.1\"::bytes_source_remaining_length,\n\n            \"spacetime_10.2\"::get_jwt,\n\n            // The procedure must not be suspended while holding the transaction lock,\n            // as this can result in a deadlock; therefore, these ABIs are synchronous.\n            \"spacetime_10.3\"::procedure_start_mut_tx,\n            \"spacetime_10.3\"::procedure_commit_mut_tx,\n            \"spacetime_10.3\"::procedure_abort_mut_tx,\n\n            \"spacetime_10.4\"::datastore_index_scan_point_bsatn,\n            \"spacetime_10.4\"::datastore_delete_by_index_scan_point_bsatn,\n\n        }\n\n        $link_async! {\n            \"spacetime_10.3\"::procedure_sleep_until,\n            \"spacetime_10.3\"::procedure_http_request,\n        }\n    };\n}\npub(crate) use abi_funcs;\n"
  },
  {
    "path": "crates/core/src/host/wasmtime/mod.rs",
    "content": "use self::wasm_instance_env::WasmInstanceEnv;\nuse super::wasm_common::module_host_actor::{InitializationError, WasmModuleHostActor, WasmModuleInstance};\nuse super::wasm_common::{abi, ModuleCreationError};\nuse crate::energy::{EnergyQuanta, FunctionBudget};\nuse crate::error::NodesError;\nuse crate::module_host_context::ModuleCreationContext;\nuse crate::util::jobs::AllocatedJobCore;\nuse anyhow::Context;\nuse spacetimedb_paths::server::ServerDataDir;\nuse std::borrow::Cow;\nuse std::time::Duration;\nuse wasmtime::{self, Engine, Linker, StoreContext, StoreContextMut};\npub use wasmtime_module::{WasmtimeInstance, WasmtimeModule};\n\n#[cfg(unix)]\nmod pooling_stack_creator;\nmod wasm_instance_env;\nmod wasmtime_module;\n\npub struct WasmtimeRuntime {\n    engine: Engine,\n    linker: Box<Linker<WasmInstanceEnv>>,\n}\n\nconst EPOCH_TICK_LENGTH: Duration = Duration::from_millis(10);\n\npub(crate) const EPOCH_TICKS_PER_SECOND: u64 = ticks_in_duration(Duration::from_secs(1));\n\npub(crate) const fn ticks_in_duration(duration: Duration) -> u64 {\n    duration.div_duration_f64(EPOCH_TICK_LENGTH) as u64\n}\n\npub(crate) fn epoch_ticker(mut on_tick: impl 'static + Send + FnMut() -> Option<()>) {\n    tokio::spawn(async move {\n        let mut interval = tokio::time::interval(EPOCH_TICK_LENGTH);\n        loop {\n            interval.tick().await;\n            let Some(()) = on_tick() else {\n                return;\n            };\n        }\n    });\n}\n\nimpl WasmtimeRuntime {\n    pub fn new(data_dir: Option<&ServerDataDir>) -> Self {\n        let mut config = wasmtime::Config::new();\n        config\n            .cranelift_opt_level(wasmtime::OptLevel::Speed)\n            .consume_fuel(true)\n            .epoch_interruption(true)\n            .wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable)\n            // We need async support to enable suspending execution of procedures\n            // when waiting for e.g. HTTP responses or the transaction lock.\n            // We don't enable either fuel-based or epoch-based yielding\n            // (see https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.epoch_deadline_async_yield_and_update\n            // and https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.fuel_async_yield_interval)\n            // so reducers will always execute to completion during the first `Future::poll` call,\n            // and procedures will only yield when performing an asynchronous operation.\n            // These futures are executed on a separate single-threaded executor not related to the \"global\" Tokio runtime,\n            // which is responsible only for executing WASM. See `crate::util::jobs` for this infrastructure.\n            .async_support(true);\n\n        #[cfg(unix)]\n        config\n            .async_stack_size(self::pooling_stack_creator::ASYNC_STACK_SIZE)\n            .with_host_stack(self::pooling_stack_creator::PoolingStackCreator::new());\n\n        // Offer a compile-time flag for enabling perfmap generation,\n        // so `perf` can display JITted symbol names.\n        // Ideally we would be able to configure this at runtime via a flag to `spacetime start`,\n        // but this is good enough for now.\n        #[cfg(feature = \"perfmap\")]\n        config.profiler(wasmtime::ProfilingStrategy::PerfMap);\n\n        if let Some(data_dir) = data_dir {\n            let mut cache_config = wasmtime::CacheConfig::new();\n            cache_config.with_directory(data_dir.wasmtime_cache().0);\n            match wasmtime::Cache::new(cache_config) {\n                Ok(cache) => {\n                    config.cache(Some(cache));\n                }\n                Err(e) => {\n                    // caching is just an optimization, so if it fails, just log and continue\n                    tracing::warn!(\"failed to set up wasmtime cache: {e:#}\")\n                }\n            }\n        }\n\n        let engine = Engine::new(&config).unwrap();\n\n        let weak_engine = engine.weak();\n        epoch_ticker(move || {\n            let engine = weak_engine.upgrade()?;\n            engine.increment_epoch();\n            Some(())\n        });\n\n        let mut linker = Box::new(Linker::new(&engine));\n        WasmtimeModule::link_imports(&mut linker).unwrap();\n\n        WasmtimeRuntime { engine, linker }\n    }\n}\n\npub type Module = WasmModuleHostActor<WasmtimeModule>;\npub type ModuleInstance = WasmModuleInstance<WasmtimeInstance>;\n\nimpl WasmtimeRuntime {\n    pub fn make_actor(\n        &self,\n        mcc: ModuleCreationContext,\n        program_bytes: &[u8],\n        core: AllocatedJobCore,\n    ) -> anyhow::Result<super::module_host::ModuleWithInstance> {\n        let module =\n            wasmtime::Module::new(&self.engine, program_bytes).map_err(ModuleCreationError::WasmCompileError)?;\n\n        let func_imports = module\n            .imports()\n            .filter(|imp| matches!(imp.ty(), wasmtime::ExternType::Func(_)));\n        let abi = abi::determine_spacetime_abi(func_imports, |imp| imp.module())?;\n\n        abi::verify_supported(WasmtimeModule::IMPLEMENTED_ABI, abi)?;\n\n        let module = self\n            .linker\n            .instantiate_pre(&module)\n            .map_err(InitializationError::Instantiation)?;\n\n        let module = WasmtimeModule::new(module);\n\n        let (module, init_inst) = WasmModuleHostActor::new(mcc, module)?;\n        Ok(super::module_host::ModuleWithInstance::Wasm {\n            module,\n            executor: core.spawn_async_executor(),\n            init_inst: Box::new(init_inst),\n        })\n    }\n}\n\n#[derive(Debug, derive_more::From)]\npub enum WasmError {\n    Db(NodesError),\n    BufferTooSmall,\n    Wasm(anyhow::Error),\n}\n\n#[derive(Copy, Clone)]\nstruct WasmtimeFuel(u64);\n\nimpl WasmtimeFuel {}\n\nimpl From<FunctionBudget> for WasmtimeFuel {\n    fn from(v: FunctionBudget) -> Self {\n        // ReducerBudget being u64 is load-bearing here - if it was u128 and v was ReducerBudget::MAX,\n        // truncating this result would mean that with set_store_fuel(budget.into()), get_store_fuel()\n        // would be wildly different than the original `budget`, and the energy usage for the reducer\n        // would be u64::MAX even if it did nothing. ask how I know.\n        WasmtimeFuel(v.get())\n    }\n}\n\nimpl From<WasmtimeFuel> for FunctionBudget {\n    fn from(v: WasmtimeFuel) -> Self {\n        FunctionBudget::new(v.0)\n    }\n}\n\nimpl From<WasmtimeFuel> for EnergyQuanta {\n    fn from(fuel: WasmtimeFuel) -> Self {\n        EnergyQuanta::new(u128::from(fuel.0))\n    }\n}\n\npub trait WasmPointee {\n    type Pointer;\n    fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), MemError>;\n    fn read_from(mem: &mut MemView, ptr: Self::Pointer) -> Result<Self, MemError>\n    where\n        Self: Sized;\n}\nmacro_rules! impl_pointee {\n    ($($t:ty),*) => {\n        $(impl WasmPointee for $t {\n            type Pointer = u32;\n            fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), MemError> {\n                let bytes = self.to_le_bytes();\n                mem.deref_slice_mut(ptr, bytes.len() as u32)?.copy_from_slice(&bytes);\n                Ok(())\n            }\n            fn read_from(mem: &mut MemView, ptr: Self::Pointer) -> Result<Self, MemError> {\n                Ok(Self::from_le_bytes(*mem.deref_array(ptr)?))\n            }\n        })*\n    };\n}\nimpl_pointee!(u8, u16, u32, u64);\nimpl_pointee!(super::wasm_common::RowIterIdx);\n\nimpl WasmPointee for spacetimedb_lib::Identity {\n    type Pointer = u32;\n    fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), MemError> {\n        let bytes = self.to_byte_array();\n        mem.deref_slice_mut(ptr, bytes.len() as u32)?.copy_from_slice(&bytes);\n        Ok(())\n    }\n    fn read_from(mem: &mut MemView, ptr: Self::Pointer) -> Result<Self, MemError> {\n        Ok(Self::from_byte_array(*mem.deref_array(ptr)?))\n    }\n}\n\nimpl WasmPointee for spacetimedb_lib::ConnectionId {\n    type Pointer = u32;\n    fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), MemError> {\n        let bytes = self.as_le_byte_array();\n        mem.deref_slice_mut(ptr, bytes.len() as u32)?.copy_from_slice(&bytes);\n        Ok(())\n    }\n    fn read_from(mem: &mut MemView, ptr: Self::Pointer) -> Result<Self, MemError> {\n        Ok(Self::from_le_byte_array(*mem.deref_array(ptr)?))\n    }\n}\n\ntype WasmPtr<T> = <T as WasmPointee>::Pointer;\n\n/// Wraps access to WASM linear memory with some additional functionality.\n#[derive(Clone, Copy)]\npub struct Mem {\n    /// The underlying WASM `memory` instance.\n    pub memory: wasmtime::Memory,\n}\n\nimpl Mem {\n    /// Constructs an instance of `Mem` from an exports map.\n    pub fn extract(exports: &wasmtime::Instance, store: impl wasmtime::AsContextMut) -> anyhow::Result<Self> {\n        Ok(Self {\n            memory: exports.get_memory(store, \"memory\").context(\"no memory export\")?,\n        })\n    }\n\n    /// Creates and returns a view into the actual memory `store`.\n    /// This view allows for reads and writes.\n    pub fn view_and_store_mut<'a, T: 'static>(\n        &self,\n        store: impl Into<StoreContextMut<'a, T>>,\n    ) -> (&'a mut MemView, &'a mut T) {\n        let (mem, store_data) = self.memory.data_and_store_mut(store);\n        (MemView::from_slice_mut(mem), store_data)\n    }\n\n    fn view<'a, T: 'static>(&self, store: impl Into<StoreContext<'a, T>>) -> &'a MemView {\n        MemView::from_slice(self.memory.data(store))\n    }\n}\n\n#[repr(transparent)]\npub struct MemView([u8]);\n\nimpl MemView {\n    fn from_slice_mut(v: &mut [u8]) -> &mut Self {\n        // SAFETY: MemView is repr(transparent) over [u8]\n        unsafe { &mut *(v as *mut [u8] as *mut MemView) }\n    }\n    fn from_slice(v: &[u8]) -> &Self {\n        // SAFETY: MemView is repr(transparent) over [u8]\n        unsafe { &*(v as *const [u8] as *const MemView) }\n    }\n\n    /// Get a byte slice of wasm memory given a pointer and a length.\n    pub fn deref_slice(&self, offset: WasmPtr<u8>, len: u32) -> Result<&[u8], MemError> {\n        if offset == 0 {\n            return Err(MemError::Null);\n        }\n        self.0\n            .get(offset as usize..)\n            .and_then(|s| s.get(..len as usize))\n            .ok_or(MemError::OutOfBounds)\n    }\n\n    /// Get a utf8 slice of wasm memory given a pointer and a length.\n    fn deref_str(&self, offset: WasmPtr<u8>, len: u32) -> Result<&str, MemError> {\n        let b = self.deref_slice(offset, len)?;\n        std::str::from_utf8(b).map_err(MemError::Utf8)\n    }\n\n    /// Lossily get a utf8 slice of wasm memory given a pointer and a length, converting any\n    /// non-utf8 bytes to `U+FFFD REPLACEMENT CHARACTER`.\n    fn deref_str_lossy(&self, offset: WasmPtr<u8>, len: u32) -> Result<Cow<'_, str>, MemError> {\n        self.deref_slice(offset, len).map(String::from_utf8_lossy)\n    }\n\n    /// Get a mutable byte slice of wasm memory given a pointer and a length;\n    fn deref_slice_mut(&mut self, offset: WasmPtr<u8>, len: u32) -> Result<&mut [u8], MemError> {\n        if offset == 0 {\n            return Err(MemError::Null);\n        }\n        self.0\n            .get_mut(offset as usize..)\n            .and_then(|s| s.get_mut(..len as usize))\n            .ok_or(MemError::OutOfBounds)\n    }\n\n    /// Get a byte array of wasm memory the size of `N`.\n    fn deref_array<const N: usize>(&self, offset: WasmPtr<u8>) -> Result<&[u8; N], MemError> {\n        Ok(self.deref_slice(offset, N as u32)?.try_into().unwrap())\n    }\n}\n\n/// An error that can result from operations on [`MemView`].\n#[derive(thiserror::Error, Debug)]\npub enum MemError {\n    #[error(\"out of bounds pointer passed to a spacetime function\")]\n    OutOfBounds,\n    #[error(\"null pointer passed to a spacetime function\")]\n    Null,\n    #[error(\"invalid utf8 passed to a spacetime function\")]\n    Utf8(#[from] std::str::Utf8Error),\n}\n\nimpl From<MemError> for WasmError {\n    fn from(err: MemError) -> Self {\n        WasmError::Wasm(err.into())\n    }\n}\n\n/// Extension trait to gracefully handle null `WasmPtr`s, e.g.\n/// `mem.deref_slice(ptr, len).check_nullptr()? == Option<&[u8]>`.\ntrait NullableMemOp<T> {\n    fn check_nullptr(self) -> Result<Option<T>, MemError>;\n}\nimpl<T> NullableMemOp<T> for Result<T, MemError> {\n    fn check_nullptr(self) -> Result<Option<T>, MemError> {\n        match self {\n            Ok(x) => Ok(Some(x)),\n            Err(MemError::Null) => Ok(None),\n            Err(e) => Err(e),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/wasmtime/pooling_stack_creator.rs",
    "content": "use core::mem::ManuallyDrop;\nuse crossbeam_queue::ArrayQueue;\nuse std::sync::{Arc, Weak};\nuse wasmtime::{StackCreator, StackMemory};\nuse wasmtime_internal_fiber::FiberStack;\n\n/// The stack size for async stacks.\npub const ASYNC_STACK_SIZE: usize = 2 << 20;\n\npub struct PoolingStackCreator {\n    /// The actual pool of stacks.\n    pool: ArrayQueue<FiberStack>,\n    /// A weak reference to `self` that can be cloned\n    /// and put into a `PooledFiberStack` which uses `weak` on drop.\n    /// We do it self-referentially so that we can avoid another indirection.\n    weak: Weak<PoolingStackCreator>,\n}\n\nstruct PooledFiberStack {\n    stack: ManuallyDrop<FiberStack>,\n    /// A weak reference to the pool so that `self.stack`\n    /// can be returned to the pool on drop.\n    weak: Weak<PoolingStackCreator>,\n}\n\nimpl Drop for PooledFiberStack {\n    fn drop(&mut self) {\n        // SAFETY: `self.stack` is never used again.\n        let stack = unsafe { ManuallyDrop::take(&mut self.stack) };\n\n        let Some(pool) = self.weak.upgrade() else {\n            return;\n        };\n\n        let _ = pool.pool.push(stack);\n    }\n}\n\nconst UNIX_SOME: &str = \"FiberStack on unix always returns `Some(_)`\";\n\n/// SAFETY: The implementation forwards to `FiberStack as StackMemory`,\n/// which wasmtime promises to be sound.\nunsafe impl StackMemory for PooledFiberStack {\n    fn top(&self) -> *mut u8 {\n        self.stack.top().expect(UNIX_SOME)\n    }\n\n    fn range(&self) -> std::ops::Range<usize> {\n        self.stack.range().expect(UNIX_SOME)\n    }\n\n    fn guard_range(&self) -> std::ops::Range<*mut u8> {\n        self.stack.guard_range().expect(UNIX_SOME)\n    }\n}\n\n// SAFETY: Stacks created in `new_stack`\n// are never used outside of a wasmtime instance\n// and are not modified elsewhere.\nunsafe impl StackCreator for PoolingStackCreator {\n    fn new_stack(&self, size: usize, zeroed: bool) -> anyhow::Result<Box<dyn wasmtime::StackMemory>, anyhow::Error> {\n        assert_eq!(size, ASYNC_STACK_SIZE);\n\n        // SAFETY: `self.weak` is fully initialized whenever `new_stack` is called.\n        let weak = self.weak.clone();\n\n        // Either take the stack from the pool\n        // or fall back to creating a new one.\n        let stack = weak\n            .upgrade()\n            .and_then(|pool| pool.pool.pop().map(Ok))\n            .unwrap_or_else(|| FiberStack::new(ASYNC_STACK_SIZE, zeroed))?;\n\n        // Ship it.\n        let stack = ManuallyDrop::new(stack);\n        Ok(Box::new(PooledFiberStack { stack, weak }))\n    }\n}\n\nimpl PoolingStackCreator {\n    pub fn new() -> Arc<Self> {\n        Arc::new_cyclic(|weak| {\n            let pool = ArrayQueue::new(100);\n            let weak = weak.clone();\n            Self { pool, weak }\n        })\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/wasmtime/wasm_instance_env.rs",
    "content": "#![allow(clippy::too_many_arguments)]\n\nuse super::wasmtime_module::{\n    call_view_export, decode_view_result_sink_code, CallViewAnonType, CallViewType, ViewResultSinkError,\n};\nuse super::{Mem, MemView, NullableMemOp, WasmError, WasmPointee, WasmPtr};\nuse crate::database_logger::{BacktraceFrame, BacktraceProvider, ModuleBacktrace, Record};\nuse crate::error::NodesError;\nuse crate::host::instance_env::{ChunkPool, InstanceEnv};\nuse crate::host::wasm_common::instrumentation::{span, CallTimes};\nuse crate::host::wasm_common::module_host_actor::{\n    deserialize_view_rows, run_query_for_view, ExecutionTimings, ViewResult, ViewReturnData,\n};\nuse crate::host::wasm_common::{err_to_errno_and_log, RowIterIdx, RowIters, TimingSpan, TimingSpanIdx, TimingSpanSet};\nuse crate::host::AbiCall;\nuse crate::subscription::module_subscription_manager::TransactionOffset;\nuse anyhow::{anyhow, Context as _};\nuse spacetimedb_data_structures::map::IntMap;\nuse spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCallInfo};\nuse spacetimedb_lib::{bsatn, ConnectionId, Timestamp};\nuse spacetimedb_primitives::errno::HOST_CALL_FAILURE;\nuse spacetimedb_primitives::{errno, ColId};\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_schema::identifier::Identifier;\nuse std::future::Future;\nuse std::num::NonZeroU32;\nuse std::sync::Arc;\nuse std::time::Instant;\nuse wasmtime::{AsContext, Caller, StoreContextMut};\n\n/// A stream of bytes which the WASM module can read from\n/// using [`WasmInstanceEnv::bytes_source_read`].\n///\n/// These are managed in the `bytes_sources` of [`WasmInstanceEnv`],\n/// where each one is paired with an integer ID.\n/// This is basically a massively-simplified version of Unix read files and file descriptors.\n///\n/// Unlike Unix read files, we implicitly close `BytesSource`s once they are read to the end.\n/// This is sensible because we don't provide a seek operation,\n/// so the `BytesSource` becomes useless once read to the end.\nstruct BytesSource {\n    /// The actual bytes which will be returned by calls to `byte_source_read`.\n    ///\n    /// When this becomes empty, this `ByteSource` is expended and should be discarded.\n    bytes: bytes::Bytes,\n}\n\n/// Identifier for a [`BytesSource`] stored in the `bytes_sources` of a [`WasmInstanceEnv`].\n///\n/// The special sentinel [`Self::INVALID`] (zero) is used for a never-readable [`BytesSource`].\n/// We pass this to guests for a [`BytesSource`] with a length of zero\n/// so that they can avoid host calls.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub(super) struct BytesSourceId(pub(super) u32);\n\n// `nohash_hasher` recommends impling `Hash` explicitly rather than using the derive macro,\n// as the derive macro is not technically guaranteed to only call `hasher.write_{int}` for an integer newtype,\n// even though any other behavior would be deranged.\nimpl std::hash::Hash for BytesSourceId {\n    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {\n        hasher.write_u32(self.0)\n    }\n}\n\nimpl nohash_hasher::IsEnabled for BytesSourceId {}\n\nimpl BytesSourceId {\n    const INVALID: Self = Self(0);\n}\n\n/// A `WasmInstanceEnv` provides the connection between a module\n/// and the database.\n///\n/// A `WasmInstanceEnv` associates an `InstanceEnv` (responsible for\n/// the database instance and its associated state) with a wasm\n/// `Mem`. It also contains the resources (`Buffers` and\n/// `BufferIters`) needed to manage the ABI contract between modules\n/// and the host.\n///\n/// Once created, a `WasmInstanceEnv` must be instantiated with a `Mem`\n/// exactly once.\n///\n/// Some of the state associated to a `WasmInstanceEnv` is per reducer invocation.\n/// For instance, module-defined timing spans are per reducer.\npub(super) struct WasmInstanceEnv {\n    /// The database `InstanceEnv` associated to this instance.\n    instance_env: InstanceEnv,\n\n    /// A validated `ModuleDef` for this instance used by procedures to refresh views.\n    module_def: Option<Arc<ModuleDef>>,\n\n    /// A cached `__call_view__` export used by procedures to refresh views.\n    call_view: Option<CallViewType>,\n\n    /// A cached `__call_view_anon__` export used by procedures to refresh views.\n    call_view_anon: Option<CallViewAnonType>,\n\n    /// The `Mem` associated to this instance. At construction time,\n    /// this is always `None`. The `Mem` instance is extracted from the\n    /// instance exports, and after instantiation is complete, this will\n    /// always be `Some`.\n    mem: Option<Mem>,\n\n    /// `File`-like [`BytesSource`]s which guest code can read via [`Self::bytes_source_read`].\n    ///\n    /// These are essentially simplified versions of Unix read files,\n    /// with [`BytesSourceId`] being file descriptors.\n    ///\n    /// Unlike Unix files, we implicitly close a [`BytesSource`] when it is read to the end.\n    /// This is because we don't provide a seek operation and a [`BytesSource`] never grows after initialization.\n    bytes_sources: IntMap<BytesSourceId, BytesSource>,\n\n    /// Counter as a source of [`BytesSourceId`] values.\n    ///\n    /// Recall that zero is [`BytesSourceId::INVALID`], so we have to start at 1.\n    next_bytes_source_id: NonZeroU32,\n\n    /// The standard sink used for [`Self::bytes_sink_write`].\n    standard_bytes_sink: Option<Vec<u8>>,\n\n    /// The slab of `BufferIters` created for this instance.\n    iters: RowIters,\n\n    /// Track time spent in module-defined spans.\n    timing_spans: TimingSpanSet,\n\n    /// Track time spent in all wasm instance env calls (aka syscall time).\n    ///\n    /// Each function, like `insert`, will add the `Duration` spent in it\n    /// to this tracker.\n    call_times: CallTimes,\n\n    /// A pool of unused allocated chunks that can be reused.\n    // TODO(Centril): consider using this pool for `console_timer_start` and `bytes_sink_write`.\n    chunk_pool: ChunkPool,\n}\n\nconst STANDARD_BYTES_SINK: u32 = 1;\n\ntype WasmResult<T> = Result<T, WasmError>;\ntype RtResult<T> = anyhow::Result<T>;\n\n/// Wraps an `InstanceEnv` with the magic necessary to push\n/// and pull bytes from webassembly memory.\nimpl WasmInstanceEnv {\n    /// Create a new `WasmEnstanceEnv` from the given `InstanceEnv`.\n    pub fn new(instance_env: InstanceEnv) -> Self {\n        Self {\n            instance_env,\n            module_def: None,\n            call_view: None,\n            call_view_anon: None,\n            mem: None,\n            bytes_sources: IntMap::default(),\n            next_bytes_source_id: NonZeroU32::new(1).unwrap(),\n            standard_bytes_sink: None,\n            iters: Default::default(),\n            timing_spans: Default::default(),\n            call_times: CallTimes::new(),\n            chunk_pool: <_>::default(),\n        }\n    }\n\n    fn alloc_bytes_source_id(&mut self) -> RtResult<BytesSourceId> {\n        let id = self.next_bytes_source_id;\n        self.next_bytes_source_id = id\n            .checked_add(1)\n            .context(\"Allocating next `BytesSourceId` overflowed `u32`\")?;\n        Ok(BytesSourceId(id.into()))\n    }\n\n    /// Binds `bytes` to the environment and assigns it an ID.\n    ///\n    /// If `bytes` is empty, `BytesSourceId::INVALID` is returned.\n    fn create_bytes_source(&mut self, bytes: bytes::Bytes) -> RtResult<BytesSourceId> {\n        // Pass an invalid source when the bytes were empty.\n        // This allows the module to avoid allocating and make a system call in those cases.\n        if bytes.is_empty() {\n            Ok(BytesSourceId::INVALID)\n        } else if bytes.len() > u32::MAX as usize {\n            // There's no inherent reason we need to error here,\n            // other than that it makes it impossible to report the length in `bytes_source_remaining_length`\n            // and that all of our usage of `BytesSource`s as of writing (pgoldman 2025-09-26)\n            // are to immediately slurp the whole thing into a buffer in guest memory,\n            // which can't hold buffers this big because it's WASM32.\n            Err(anyhow::anyhow!(\n                \"`create_bytes_source`: `Bytes` has length {}, which is greater than `u32::MAX` {}\",\n                bytes.len(),\n                u32::MAX,\n            ))\n        } else {\n            let id = self.alloc_bytes_source_id()?;\n            self.bytes_sources.insert(id, BytesSource { bytes });\n            Ok(id)\n        }\n    }\n\n    fn free_bytes_source(&mut self, id: BytesSourceId) {\n        if self.bytes_sources.remove(&id).is_none() {\n            log::warn!(\"`free_bytes_source` on non-existent source {id:?}\");\n        }\n    }\n\n    /// Finish the instantiation of this instance with the provided `Mem`.\n    pub fn instantiate(&mut self, mem: Mem) {\n        assert!(self.mem.is_none());\n        self.mem = Some(mem);\n    }\n\n    pub fn set_module_def(&mut self, module_def: Arc<ModuleDef>) {\n        self.module_def = Some(module_def)\n    }\n\n    pub fn set_call_view_exports(&mut self, call_view: Option<CallViewType>, call_view_anon: Option<CallViewAnonType>) {\n        self.call_view = call_view;\n        self.call_view_anon = call_view_anon;\n    }\n\n    /// Returns a reference to the memory, assumed to be initialized.\n    pub fn get_mem(&self) -> Mem {\n        self.mem.expect(\"Initialized memory\")\n    }\n    fn mem_env<'a>(ctx: impl Into<StoreContextMut<'a, Self>>) -> (&'a mut MemView, &'a mut Self) {\n        let ctx = ctx.into();\n        let mem = ctx.data().get_mem();\n        mem.view_and_store_mut(ctx)\n    }\n\n    /// Return a reference to the `InstanceEnv`,\n    /// which is responsible for DB instance and associated state.\n    pub fn instance_env(&self) -> &InstanceEnv {\n        &self.instance_env\n    }\n\n    /// Setup the standard bytes sink and return a handle to it for writing.\n    pub fn setup_standard_bytes_sink(&mut self) -> u32 {\n        self.standard_bytes_sink = Some(Vec::new());\n        STANDARD_BYTES_SINK\n    }\n\n    /// Extract all the bytes written to the standard bytes sink\n    /// and prevent further writes to it.\n    pub fn take_standard_bytes_sink(&mut self) -> Vec<u8> {\n        self.standard_bytes_sink.take().unwrap_or_default()\n    }\n\n    /// Signal to this `WasmInstanceEnv` that a reducer or procedure call is beginning.\n    ///\n    /// Returns the handle used by reducers and procedures to read from `args`\n    /// as well as the handle used to write the reducer error message or procedure return value.\n    pub fn start_funcall(\n        &mut self,\n        name: Identifier,\n        args: bytes::Bytes,\n        ts: Timestamp,\n        func_type: FuncCallType,\n    ) -> (BytesSourceId, u32) {\n        // Create the output sink.\n        // Reducers which fail will write their error message here.\n        // Procedures will write their result here.\n        let errors = self.setup_standard_bytes_sink();\n\n        let args = self.create_bytes_source(args).unwrap();\n\n        self.instance_env.start_funcall(name, ts, func_type);\n\n        (args, errors)\n    }\n\n    /// Returns the name of the most recent reducer or procedure to be run in this environment,\n    /// or `None` if no reducer or procedure is actively being invoked.\n    pub fn log_record_function(&self) -> Option<&str> {\n        self.instance_env.log_record_function()\n    }\n\n    /// Returns the start time of the most recent reducer or procedure to be run in this environment.\n    pub fn funcall_start(&self) -> Instant {\n        self.instance_env.start_instant\n    }\n\n    /// Signal to this `WasmInstanceEnv` that a reducer or procedure call is over.\n    ///\n    /// Returns time measurements which can be recorded as metrics,\n    /// and the errors written by the WASM code to the standard error sink.\n    ///\n    /// This resets the call times and clears the arguments source and error sink.\n    pub fn finish_funcall(&mut self) -> (ExecutionTimings, Vec<u8>) {\n        // For the moment,\n        // we only explicitly clear the source/sink buffers and the \"syscall\" times.\n        // TODO: should we be clearing `iters` and/or `timing_spans`?\n\n        let total_duration = self.instance_env.start_instant.elapsed();\n\n        // Taking the call times record also resets timings to 0s for the next call.\n        let wasm_instance_env_call_times = self.call_times.take();\n\n        let timings = ExecutionTimings {\n            total_duration,\n            wasm_instance_env_call_times,\n        };\n\n        // Drop any outstanding bytes sources and reset the ID counter,\n        // so that we don't leak either the IDs or the buffers themselves.\n        self.bytes_sources = IntMap::default();\n        self.next_bytes_source_id = NonZeroU32::new(1).unwrap();\n\n        (timings, self.take_standard_bytes_sink())\n    }\n\n    /// After a procedure has finished, take its known last tx offset, if any.\n    pub fn take_procedure_tx_offset(&mut self) -> Option<TransactionOffset> {\n        self.instance_env.take_procedure_tx_offset()\n    }\n\n    /// Record a span with `start`.\n    fn end_span(mut caller: Caller<'_, Self>, start: span::CallSpanStart) {\n        let span = start.end();\n        span::record_span(&mut caller.data_mut().call_times, span);\n    }\n\n    fn with_span<R>(mut caller: Caller<'_, Self>, func: AbiCall, run: impl FnOnce(&mut Caller<'_, Self>) -> R) -> R {\n        let span_start = span::CallSpanStart::new(func);\n\n        // Call `run` with the caller and a handle to the memory.\n        let result = run(&mut caller);\n\n        Self::end_span(caller, span_start);\n\n        result\n    }\n\n    fn async_with_span<'caller, R, F: Send + 'caller + Future<Output = (Caller<'caller, Self>, R)>>(\n        caller: Caller<'caller, Self>,\n        func: AbiCall,\n        run: impl Send + 'caller + FnOnce(Caller<'caller, Self>) -> F,\n    ) -> Fut<'caller, R> {\n        Box::new(async move {\n            let span_start = span::CallSpanStart::new(func);\n\n            // Call `run` with the caller and a handle to the memory.\n            let (caller, result) = run(caller).await;\n\n            Self::end_span(caller, span_start);\n            result\n        })\n    }\n\n    fn convert_wasm_result<T: From<u16>>(func: AbiCall, err: WasmError) -> RtResult<T> {\n        match err {\n            WasmError::Db(err) => err_to_errno_and_log(func, err).map(|(code, _)| code),\n            WasmError::BufferTooSmall => Ok(errno::BUFFER_TOO_SMALL.get().into()),\n            WasmError::Wasm(err) => Err(err),\n        }\n    }\n\n    /// Call the function `run` with the name `func`.\n    /// The function `run` is provided with the callers environment and the host's memory.\n    ///\n    /// One of `cvt_custom`, `cvt`, `cvt_ret`, or `cvt_noret` should be used in the implementation of any\n    /// host call, to provide consistent error handling and instrumentation.\n    ///\n    /// Some database errors are logged but are otherwise regarded as `Ok(_)`.\n    /// See `err_to_errno` for a list.\n    ///\n    /// This variant should be used when more control is needed over the success value.\n    fn cvt_custom<T: From<u16>>(\n        caller: Caller<'_, Self>,\n        func: AbiCall,\n        run: impl FnOnce(&mut Caller<'_, Self>) -> WasmResult<T>,\n    ) -> RtResult<T> {\n        Self::with_span(caller, func, run).or_else(|err| Self::convert_wasm_result(func, err))\n    }\n\n    /// Call the function `run` with the name `func`.\n    /// The function `run` is provided with the callers environment and the host's memory.\n    ///\n    /// One of `cvt`, `cvt_ret`, or `cvt_noret` should be used in the implementation of any\n    /// host call, to provide consistent error handling and instrumentation.\n    ///\n    /// Some database errors are logged but are otherwise regarded as `Ok(_)`.\n    /// See `err_to_errno` for a list.\n    fn cvt<T: From<u16>>(\n        caller: Caller<'_, Self>,\n        func: AbiCall,\n        run: impl FnOnce(&mut Caller<'_, Self>) -> WasmResult<()>,\n    ) -> RtResult<T> {\n        Self::cvt_custom(caller, func, |c| run(c).map(|()| 0u16.into()))\n    }\n\n    /// Call the function `f` with any return value being written to the pointer `out`.\n    ///\n    /// Otherwise, `cvt_ret` (this function) behaves as `cvt`.\n    ///\n    /// One of `cvt`, `cvt_ret`, or `cvt_noret` should be used in the implementation of any\n    /// host call, to provide consistent error handling and instrumentation.\n    ///\n    /// This method should be used as opposed to a manual implementation,\n    /// as it helps with upholding the safety invariants of [`bindings_sys::call`].\n    ///\n    /// Returns an error if writing `T` to `out` errors.\n    fn cvt_ret<O: WasmPointee>(\n        caller: Caller<'_, Self>,\n        call: AbiCall,\n        out: WasmPtr<O>,\n        f: impl FnOnce(&mut Caller<'_, Self>) -> WasmResult<O>,\n    ) -> RtResult<u32> {\n        Self::cvt(caller, call, |caller| {\n            f(caller).and_then(|ret| {\n                let (mem, _) = Self::mem_env(caller);\n                ret.write_to(mem, out).map_err(|e| e.into())\n            })\n        })\n    }\n\n    /// Call the function `f`.\n    ///\n    /// This is the version of `cvt` or `cvt_ret` for functions with no return value.\n    /// One of `cvt`, `cvt_ret`, or `cvt_noret` should be used in the implementation of any\n    /// host call, to provide consistent error handling and instrumentation.\n    fn cvt_noret(caller: Caller<'_, Self>, call: AbiCall, f: impl FnOnce(&mut Caller<'_, Self>)) {\n        Self::with_span(caller, call, f)\n    }\n\n    fn convert_u32_to_col_id(col_id: u32) -> WasmResult<ColId> {\n        let col_id: u16 = col_id\n            .try_into()\n            .context(\"ABI violation, a `ColId` must be a `u16`\")\n            .map_err(WasmError::Wasm)?;\n        Ok(col_id.into())\n    }\n\n    /// Queries the `table_id` associated with the given (table) `name`\n    /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.\n    ///\n    /// The table id is written into the `out` pointer.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n    /// - `name` is not valid UTF-8.\n    /// - `out` is NULL or `out[..size_of::<TableId>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `name` is not the name of a table.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn table_id_from_name(\n        caller: Caller<'_, Self>,\n        name: WasmPtr<u8>,\n        name_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret::<u32>(caller, AbiCall::TableIdFromName, out, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            // Read the table name from WASM memory.\n            let name = mem.deref_str(name, name_len)?;\n\n            // Query the table id.\n            Ok(env.instance_env.table_id_from_name(name)?.into())\n        })\n    }\n\n    /// Queries the `index_id` associated with the given (index) `name`\n    /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.\n    ///\n    /// The index id is written into the `out` pointer.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n    /// - `name` is not valid UTF-8.\n    /// - `out` is NULL or `out[..size_of::<IndexId>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_INDEX`, when `name` is not the name of an index.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn index_id_from_name(\n        caller: Caller<'_, Self>,\n        name: WasmPtr<u8>,\n        name_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret::<u32>(caller, AbiCall::IndexIdFromName, out, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            // Read the index name from WASM memory.\n            let name = mem.deref_str(name, name_len)?;\n\n            // Query the index id.\n            Ok(env.instance_env.index_id_from_name(name)?.into())\n        })\n    }\n\n    /// Writes the number of rows currently in table identified by `table_id` to `out`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `out` is NULL or `out[..size_of::<u64>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_table_row_count(caller: Caller<'_, Self>, table_id: u32, out: WasmPtr<u64>) -> RtResult<u32> {\n        Self::cvt_ret::<u64>(caller, AbiCall::DatastoreTableRowCount, out, |caller| {\n            let (_, env) = Self::mem_env(caller);\n            Ok(env.instance_env.datastore_table_row_count(table_id.into())?)\n        })\n    }\n\n    /// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.\n    ///\n    /// On success, the iterator handle is written to the `out` pointer.\n    /// This handle can be advanced by [`row_iter_bsatn_advance`].\n    ///\n    /// # Traps\n    ///\n    /// This function does not trap.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n    // #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_table_scan_bsatn(\n        caller: Caller<'_, Self>,\n        table_id: u32,\n        out: WasmPtr<RowIterIdx>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreTableScanBsatn, out, |caller| {\n            let env = caller.data_mut();\n            // Collect the iterator chunks.\n            let chunks = env\n                .instance_env\n                .datastore_table_scan_bsatn_chunks(&mut env.chunk_pool, table_id.into())?;\n            // Register the iterator and get back the index to write to `out`.\n            // Calls to the iterator are done through dynamic dispatch.\n            Ok(env.iters.insert(chunks.into_iter()))\n        })\n    }\n\n    /// Finds all rows in the index identified by `index_id`,\n    /// according to `point = point_ptr[..point_len]` in WASM memory.\n    ///\n    /// The index itself has a schema/type.\n    /// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`\n    /// and then comparing the decoded `point` to the keys in the index\n    /// using `Ord for AlgebraicValue`.\n    /// to the keys in the index.\n    /// The `point` is BSATN-decoded to that `AlgebraicType`.\n    /// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n    /// This occurs exactly when the row's BSATN-encoding\n    /// is equal to the encoding of the `AlgebraicValue`.\n    ///\n    /// This ABI is not limited to single column indices.\n    /// Multi-column indices can be queried by providing\n    /// a BSATN-encoded `ProductValue`\n    /// that is typed at the `ProductType` of the index.\n    ///\n    /// The relevant table for the index is found implicitly via the `index_id`,\n    /// which is unique for the module.\n    ///\n    /// On success, the iterator handle is written to the `out` pointer.\n    /// This handle can be advanced by [`row_iter_bsatn_advance`].\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n    /// - `WRONG_INDEX_ALGO` if the index is not a range-scan compatible index.\n    /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n    ///   typed at the index's key type (`AlgebraicType`).\n    pub fn datastore_index_scan_point_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        point_ptr: WasmPtr<u8>, // AlgebraicValue\n        point_len: u32,\n        out: WasmPtr<RowIterIdx>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreIndexScanPointBsatn, out, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            // Read the `point` from WASM memory.\n            let point = mem.deref_slice(point_ptr, point_len)?;\n\n            // Find the relevant rows.\n            let chunks = env.instance_env.datastore_index_scan_point_bsatn_chunks(\n                &mut env.chunk_pool,\n                index_id.into(),\n                point,\n            )?;\n\n            // Insert the encoded + concatenated rows into a new buffer and return its id.\n            Ok(env.iters.insert(chunks.into_iter()))\n        })\n    }\n\n    /// Finds all rows in the index identified by `index_id`,\n    /// according to the:\n    /// - `prefix = prefix_ptr[..prefix_len]`,\n    /// - `rstart = rstart_ptr[..rstart_len]`,\n    /// - `rend = rend_ptr[..rend_len]`,\n    ///\n    /// in WASM memory.\n    ///\n    /// The index itself has a schema/type.\n    /// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s\n    /// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`\n    /// where the `AlgebraicValue`s are wrapped in `Bound`.\n    /// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.\n    ///\n    /// Matching is then defined by equating `prefix`\n    /// to the initial `prefix_elems` columns of the index\n    /// and then imposing `rstart` as the starting bound\n    /// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.\n    /// Remaining columns of the index are then unbounded.\n    /// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),\n    /// in which case this becomes a ranged index scan on a single-col index\n    /// or even a full table scan if `rstart` and `rend` are both unbounded.\n    ///\n    /// The relevant table for the index is found implicitly via the `index_id`,\n    /// which is unique for the module.\n    ///\n    /// On success, the iterator handle is written to the `out` pointer.\n    /// This handle can be advanced by [`row_iter_bsatn_advance`].\n    ///\n    /// # Non-obvious queries\n    ///\n    /// For an index on columns `[a, b, c]`:\n    ///\n    /// - `a = x, b = y` is encoded as a prefix `[x, y]`\n    ///   and a range `Range::Unbounded`,\n    ///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.\n    /// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`\n    ///   and a  range `rstart = rend = Range::Inclusive(z)`.\n    /// - A sorted full scan is encoded as an empty prefix\n    ///   and a range `Range::Unbounded`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `prefix_elems > 0`\n    ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).\n    /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.\n    /// - `rend` is NULL or `rend` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n    /// - `WRONG_INDEX_ALGO` if the index is not a range-scan compatible index.\n    /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n    ///   a `prefix_elems` number of `AlgebraicValue`\n    ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n    ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n    ///   where the inner `AlgebraicValue`s are\n    ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n    pub fn datastore_index_scan_range_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        prefix_ptr: WasmPtr<u8>,\n        prefix_len: u32,\n        prefix_elems: u32,\n        rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rstart_len: u32,\n        rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rend_len: u32,\n        out: WasmPtr<RowIterIdx>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreIndexScanRangeBsatn, out, |caller| {\n            let prefix_elems = Self::convert_u32_to_col_id(prefix_elems)?;\n\n            let (mem, env) = Self::mem_env(caller);\n            // Read the prefix and range start & end from WASM memory.\n            let prefix = if prefix_elems.idx() == 0 {\n                &[]\n            } else {\n                mem.deref_slice(prefix_ptr, prefix_len)?\n            };\n            let rstart = mem.deref_slice(rstart_ptr, rstart_len)?;\n            let rend = mem.deref_slice(rend_ptr, rend_len)?;\n\n            // Find the relevant rows.\n            let chunks = env.instance_env.datastore_index_scan_range_bsatn_chunks(\n                &mut env.chunk_pool,\n                index_id.into(),\n                prefix,\n                prefix_elems,\n                rstart,\n                rend,\n            )?;\n\n            // Insert the encoded + concatenated rows into a new buffer and return its id.\n            Ok(env.iters.insert(chunks.into_iter()))\n        })\n    }\n\n    /// Deprecated name for [`Self::datastore_index_scan_range_bsatn`].\n    #[deprecated = \"use `datastore_index_scan_range_bsatn` instead\"]\n    pub fn datastore_btree_scan_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        prefix_ptr: WasmPtr<u8>,\n        prefix_len: u32,\n        prefix_elems: u32,\n        rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rstart_len: u32,\n        rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rend_len: u32,\n        out: WasmPtr<RowIterIdx>,\n    ) -> RtResult<u32> {\n        Self::datastore_index_scan_range_bsatn(\n            caller,\n            index_id,\n            prefix_ptr,\n            prefix_len,\n            prefix_elems,\n            rstart_ptr,\n            rstart_len,\n            rend_ptr,\n            rend_len,\n            out,\n        )\n    }\n\n    /// Reads rows from the given iterator registered under `iter`.\n    ///\n    /// Takes rows from the iterator\n    /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`,\n    /// encoded in BSATN format.\n    ///\n    /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n    /// On success (`0` or `-1` is returned),\n    /// `buffer_len` is set to the combined length of the encoded rows.\n    /// When `-1` is returned, the iterator has been exhausted\n    /// and there are no more rows to read,\n    /// leading to the iterator being immediately destroyed.\n    /// Note that the host is free to reuse allocations in a pool,\n    /// destroying the handle logically does not entail that memory is necessarily reclaimed.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n    /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.\n    /// - `BUFFER_TOO_SMALL`, when there are rows left but they cannot fit in `buffer`.\n    ///   When this occurs, `buffer_len` is set to the size of the next item in the iterator.\n    ///   To make progress, the caller should reallocate the buffer to at least that size and try again.\n    // #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn row_iter_bsatn_advance(\n        caller: Caller<'_, Self>,\n        iter: u32,\n        buffer_ptr: WasmPtr<u8>,\n        buffer_len_ptr: WasmPtr<u32>,\n    ) -> RtResult<i32> {\n        let row_iter_idx = RowIterIdx(iter);\n        Self::cvt_custom(caller, AbiCall::RowIterBsatnAdvance, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            // Retrieve the iterator by `row_iter_idx`, or error.\n            let Some(iter) = env.iters.get_mut(row_iter_idx) else {\n                return Ok(errno::NO_SUCH_ITER.get().into());\n            };\n\n            // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`.\n            let buffer_len = u32::read_from(mem, buffer_len_ptr)?;\n            let write_buffer_len = |mem, len| u32::try_from(len).unwrap().write_to(mem, buffer_len_ptr);\n\n            // Get a mutable view to the `buffer`.\n            let buffer = mem.deref_slice_mut(buffer_ptr, buffer_len)?;\n\n            // Fill the buffer as much as possible.\n            let written = InstanceEnv::fill_buffer_from_iter(iter, buffer, &mut env.chunk_pool);\n\n            let ret = match (written, iter.as_slice().first()) {\n                // Nothing was written and the iterator is not exhausted.\n                (0, Some(chunk)) => {\n                    write_buffer_len(mem, chunk.len())?;\n                    return Ok(errno::BUFFER_TOO_SMALL.get().into());\n                }\n                // The iterator is exhausted, destroy it, and tell the caller.\n                (_, None) => {\n                    env.iters.take(row_iter_idx);\n                    -1\n                }\n                // Something was written, but the iterator is not exhausted.\n                (_, Some(_)) => 0,\n            };\n            write_buffer_len(mem, written)?;\n            Ok(ret)\n        })\n    }\n\n    /// Destroys the iterator registered under `iter`.\n    ///\n    /// Once `row_iter_bsatn_close` is called on `iter`, the `iter` is invalid.\n    /// That is, `row_iter_bsatn_close(iter)` the second time will yield `NO_SUCH_ITER`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.\n    // #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn row_iter_bsatn_close(caller: Caller<'_, Self>, iter: u32) -> RtResult<u32> {\n        let row_iter_idx = RowIterIdx(iter);\n        Self::cvt_custom(caller, AbiCall::RowIterBsatnClose, |caller| {\n            let (_, env) = Self::mem_env(caller);\n\n            // Retrieve the iterator by `row_iter_idx`, or error.\n            Ok(match env.iters.take(row_iter_idx) {\n                None => errno::NO_SUCH_ITER.get().into(),\n                // TODO(Centril): consider putting these into a pool for reuse.\n                Some(_) => 0,\n            })\n        })\n    }\n\n    /// Inserts a row into the table identified by `table_id`,\n    /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory\n    /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.\n    ///\n    /// The byte string `row` must be a BSATN-encoded `ProductValue`\n    /// typed at the table's `ProductType` row-schema.\n    ///\n    /// To handle auto-incrementing columns,\n    /// when the call is successful,\n    /// the `row` is written back to with the generated sequence values.\n    /// These values are written as a BSATN-encoded `pv: ProductValue`.\n    /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n    /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n    /// When the table has no sequences,\n    /// this implies that the `pv`, and thus `row`, will be empty.\n    /// The `row_len` is set to the length of `bsatn(pv)`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.\n    /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n    /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`.\n    ///   typed at the `ProductType` the table's schema specifies.\n    /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.\n    /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_insert_bsatn(\n        caller: Caller<'_, Self>,\n        table_id: u32,\n        row_ptr: WasmPtr<u8>,\n        row_len_ptr: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt(caller, AbiCall::DatastoreInsertBsatn, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            // Read `row-len`, i.e., the capacity of `row` pointed to by `row_ptr`.\n            let row_len = u32::read_from(mem, row_len_ptr)?;\n            // Get a mutable view to the `row`.\n            let row = mem.deref_slice_mut(row_ptr, row_len)?;\n\n            // Insert the row into the DB and write back the generated column values.\n            let row_len = env.instance_env.insert(table_id.into(), row)?;\n            u32::try_from(row_len).unwrap().write_to(mem, row_len_ptr)?;\n            Ok(())\n        })\n    }\n\n    /// Updates a row in the table identified by `table_id` to `row`\n    /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory\n    /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.\n    ///\n    /// The byte string `row` must be a BSATN-encoded `ProductValue`\n    /// typed at the table's `ProductType` row-schema.\n    ///\n    /// The row to update is found by projecting `row`\n    /// to the type of the *unique* index identified by `index_id`.\n    /// If no row is found, the error `NO_SUCH_ROW` is returned.\n    ///\n    /// To handle auto-incrementing columns,\n    /// when the call is successful,\n    /// the `row` is written back to with the generated sequence values.\n    /// These values are written as a BSATN-encoded `pv: ProductValue`.\n    /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.\n    /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.\n    /// When the table has no sequences,\n    /// this implies that the `pv`, and thus `row`, will be empty.\n    /// The `row_len` is set to the length of `bsatn(pv)`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.\n    /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n    /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n    /// - `INDEX_NOT_UNIQUE`, when the index was not unique.\n    /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`\n    ///    typed at the `ProductType` the table's schema specifies\n    ///    or when it cannot be projected to the index identified by `index_id`.\n    /// - `NO_SUCH_ROW`, when the row was not found in the unique index.\n    /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.\n    /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_update_bsatn(\n        caller: Caller<'_, Self>,\n        table_id: u32,\n        index_id: u32,\n        row_ptr: WasmPtr<u8>,\n        row_len_ptr: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt(caller, AbiCall::DatastoreUpdateBsatn, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            // Read `row-len`, i.e., the capacity of `row` pointed to by `row_ptr`.\n            let row_len = u32::read_from(mem, row_len_ptr)?;\n            // Get a mutable view to the `row`.\n            let row = mem.deref_slice_mut(row_ptr, row_len)?;\n\n            // Update the row in the DB and write back the generated column values.\n            let row_len = env.instance_env.update(table_id.into(), index_id.into(), row)?;\n            u32::try_from(row_len).unwrap().write_to(mem, row_len_ptr)?;\n            Ok(())\n        })\n    }\n\n    /// Deletes all rows found in the index identified by `index_id`,\n    /// according to `point = point_ptr[..point_len]` in WASM memory.\n    ///\n    /// This syscall will delete all the rows found by\n    /// [`datastore_index_scan_point_bsatn`] with the same arguments passed.\n    /// See `datastore_index_scan_point_bsatn` for details.\n    ///\n    /// The number of rows deleted is written to the WASM pointer `out`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n    /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n    /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`\n    ///   typed at the index's key type (`AlgebraicType`).\n    pub fn datastore_delete_by_index_scan_point_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        point_ptr: WasmPtr<u8>, // AlgebraicValue\n        point_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreDeleteByIndexScanPointBsatn, out, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            // Read the `point` from WASM memory.\n            let point = mem.deref_slice(point_ptr, point_len)?;\n\n            // Delete the relevant rows.\n            Ok(env\n                .instance_env\n                .datastore_delete_by_index_scan_point_bsatn(index_id.into(), point)?)\n        })\n    }\n\n    /// Deletes all rows found in the index identified by `index_id`,\n    /// according to the:\n    /// - `prefix = prefix_ptr[..prefix_len]`,\n    /// - `rstart = rstart_ptr[..rstart_len]`,\n    /// - `rend = rend_ptr[..rend_len]`,\n    ///\n    /// in WASM memory.\n    ///\n    /// This syscall will delete all the rows found by\n    /// [`datastore_index_scan_range_bsatn`] with the same arguments passed,\n    /// including `prefix_elems`.\n    /// See `datastore_index_scan_range_bsatn` for details.\n    ///\n    /// The number of rows deleted is written to the WASM pointer `out`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `prefix_elems > 0`\n    ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).\n    /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.\n    /// - `rend` is NULL or `rend` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.\n    /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.\n    /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to\n    ///   a `prefix_elems` number of `AlgebraicValue`\n    ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.\n    ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`\n    ///   where the inner `AlgebraicValue`s are\n    ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.\n    pub fn datastore_delete_by_index_scan_range_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        prefix_ptr: WasmPtr<u8>,\n        prefix_len: u32,\n        prefix_elems: u32,\n        rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rstart_len: u32,\n        rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rend_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreDeleteByIndexScanRangeBsatn, out, |caller| {\n            let prefix_elems = Self::convert_u32_to_col_id(prefix_elems)?;\n\n            let (mem, env) = Self::mem_env(caller);\n            // Read the prefix and range start & end from WASM memory.\n            let prefix = if prefix_elems.idx() == 0 {\n                &[]\n            } else {\n                mem.deref_slice(prefix_ptr, prefix_len)?\n            };\n            let rstart = mem.deref_slice(rstart_ptr, rstart_len)?;\n            let rend = mem.deref_slice(rend_ptr, rend_len)?;\n\n            // Delete the relevant rows.\n            Ok(env.instance_env.datastore_delete_by_index_scan_range_bsatn(\n                index_id.into(),\n                prefix,\n                prefix_elems,\n                rstart,\n                rend,\n            )?)\n        })\n    }\n\n    /// Deprecated name for [`Self::datastore_delete_by_index_scan_range_bsatn`].\n    #[deprecated = \"use `datastore_delete_by_index_scan_range_bsatn` instead\"]\n    pub fn datastore_delete_by_btree_scan_bsatn(\n        caller: Caller<'_, Self>,\n        index_id: u32,\n        prefix_ptr: WasmPtr<u8>,\n        prefix_len: u32,\n        prefix_elems: u32,\n        rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rstart_len: u32,\n        rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>\n        rend_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::datastore_delete_by_index_scan_range_bsatn(\n            caller,\n            index_id,\n            prefix_ptr,\n            prefix_len,\n            prefix_elems,\n            rstart_ptr,\n            rstart_len,\n            rend_ptr,\n            rend_len,\n            out,\n        )\n    }\n\n    /// Deletes those rows, in the table identified by `table_id`,\n    /// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.\n    ///\n    /// Matching is defined by first BSATN-decoding\n    /// the byte string pointed to at by `relation` to a `Vec<ProductValue>`\n    /// according to the row schema of the table\n    /// and then using `Ord for AlgebraicValue`.\n    /// A match happens when `Ordering::Equal` is returned from `fn cmp`.\n    /// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.\n    ///\n    /// The number of rows deleted is written to the WASM pointer `out`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `rel_ptr` is NULL or `rel` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.\n    /// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`\n    ///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn datastore_delete_all_by_eq_bsatn(\n        caller: Caller<'_, Self>,\n        table_id: u32,\n        rel_ptr: WasmPtr<u8>,\n        rel_len: u32,\n        out: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::DatastoreDeleteAllByEqBsatn, out, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            let relation = mem.deref_slice(rel_ptr, rel_len)?;\n            Ok(env\n                .instance_env\n                .datastore_delete_all_by_eq_bsatn(table_id.into(), relation)?)\n        })\n    }\n\n    pub fn volatile_nonatomic_schedule_immediate(\n        caller: Caller<'_, Self>,\n        name: WasmPtr<u8>,\n        name_len: u32,\n        args: WasmPtr<u8>,\n        args_len: u32,\n    ) -> RtResult<()> {\n        Self::with_span(caller, AbiCall::VolatileNonatomicScheduleImmediate, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            let name = mem.deref_str(name, name_len)?;\n            let args = mem.deref_slice(args, args_len)?;\n            env.instance_env.scheduler.volatile_nonatomic_schedule_immediate(\n                name.to_owned(),\n                crate::host::FunctionArgs::Bsatn(args.to_vec().into()),\n            );\n\n            Ok(())\n        })\n    }\n\n    /// Reads bytes from `source`, registered in the host environment,\n    /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`.\n    ///\n    /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n    /// On success (`0` or `-1` is returned),\n    /// `buffer_len` is set to the number of bytes written to `buffer`.\n    /// When `-1` is returned, the resource has been exhausted\n    /// and there are no more bytes to read,\n    /// leading to the resource being immediately destroyed.\n    /// Note that the host is free to reuse allocations in a pool,\n    /// destroying the handle logically does not entail that memory is necessarily reclaimed.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n    /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.\n    ///\n    /// # Example\n    ///\n    /// The typical use case for this ABI is in `__call_reducer__`,\n    /// to read and deserialize the `args`.\n    /// An example definition, dealing with `args` might be:\n    /// ```rust,ignore\n    /// /// #[no_mangle]\n    /// extern \"C\" fn __call_reducer__(..., args: BytesSource, ...) -> i16 {\n    ///     // ...\n    ///\n    ///     let mut buf = Vec::<u8>::with_capacity(1024);\n    ///     loop {\n    ///         // Write into the spare capacity of the buffer.\n    ///         let buf_ptr = buf.spare_capacity_mut();\n    ///         let spare_len = buf_ptr.len();\n    ///         let mut buf_len = buf_ptr.len();\n    ///         let buf_ptr = buf_ptr.as_mut_ptr().cast();\n    ///         let ret = unsafe { bytes_source_read(args, buf_ptr, &mut buf_len) };\n    ///         // SAFETY: `bytes_source_read` just appended `spare_len` bytes to `buf`.\n    ///         unsafe { buf.set_len(buf.len() + spare_len) };\n    ///         match ret {\n    ///             // Host side source exhausted, we're done.\n    ///             -1 => break,\n    ///             // Wrote the entire spare capacity.\n    ///             // Need to reserve more space in the buffer.\n    ///             0 if spare_len == buf_len => buf.reserve(1024),\n    ///             // Host didn't write as much as possible.\n    ///             // Try to read some more.\n    ///             // The host will likely not trigger this branch,\n    ///             // but a module should be prepared for it.\n    ///             0 => {}\n    ///             _ => unreachable!(),\n    ///         }\n    ///     }\n    ///\n    ///     // ...\n    /// }\n    /// ```\n    pub fn bytes_source_read(\n        caller: Caller<'_, Self>,\n        source: u32,\n        buffer_ptr: WasmPtr<u8>,\n        buffer_len_ptr: WasmPtr<u32>,\n    ) -> RtResult<i32> {\n        Self::cvt_custom(caller, AbiCall::BytesSourceRead, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            let source = BytesSourceId(source);\n\n            // Retrieve the reducer args if available and requested, or error.\n            let Some(bytes_source) = env.bytes_sources.get_mut(&source) else {\n                return Ok(errno::NO_SUCH_BYTES.get().into());\n            };\n\n            // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`.\n            let buffer_len = u32::read_from(mem, buffer_len_ptr)?;\n            // Get a mutable view to the `buffer`.\n            let buffer = mem.deref_slice_mut(buffer_ptr, buffer_len)?;\n            let buffer_len = buffer_len as usize;\n\n            // Derive the portion that we can read and what remains,\n            // based on what is left to read and the capacity.\n            let can_read_len = buffer_len.min(bytes_source.bytes.len());\n            let can_read = bytes_source.bytes.split_to(can_read_len);\n            // Copy to the `buffer` and write written bytes count to `buffer_len`.\n            buffer[..can_read_len].copy_from_slice(&can_read);\n            (can_read_len as u32).write_to(mem, buffer_len_ptr)?;\n\n            // Destroy the source if exhausted, or advance `cursor`.\n            if bytes_source.bytes.is_empty() {\n                env.free_bytes_source(source);\n                Ok(-1i32)\n            } else {\n                Ok(0)\n            }\n        })\n    }\n\n    /// Read the remaining length of a [`BytesSource`] and write it to `out`.\n    ///\n    /// Note that the host automatically frees byte sources which are exhausted.\n    /// Such sources are invalid, and this method will return an error when passed one.\n    /// Callers of [`Self::bytes_source_read`] should check for a return of -1\n    /// before invoking this function on the same `source`.\n    ///\n    /// Also note that the special [`BytesSourceId::INVALID`] (zero) is always invalid.\n    /// Callers should check for that value before invoking this function.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `out` is NULL or `out` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.\n    ///\n    /// If this function returns an error, `out` is not written.\n    pub fn bytes_source_remaining_length(caller: Caller<'_, Self>, source: u32, out: WasmPtr<u32>) -> RtResult<i32> {\n        Self::cvt_custom(caller, AbiCall::BytesSourceRemainingLength, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            let Some(bytes_source) = env.bytes_sources.get(&BytesSourceId(source)) else {\n                return Ok(errno::NO_SUCH_BYTES.get().into());\n            };\n\n            let remaining: u32 = bytes_source\n                .bytes\n                .len()\n                .try_into()\n                // TODO: Change this into an `errno::BYTES_SOURCE_LENGTH_UNKNOWN` rather than a trap,\n                // so that we can support very large `BytesSource`s, streams, and other file-like things that aren't just `Bytes`.\n                // This is not currently (pgoldman 2025-09-26) a useful thing to do,\n                // as all of our uses of `BytesSource` are to slurp the whole source into a single buffer in guest memory,\n                // `File::read_to_end`-style, and we don't have any use for large or streaming `BytesSource`s.\n                .context(\"Bytes object in `BytesSource` had length greater than range of u32\")?;\n\n            u32::write_to(remaining, mem, out)\n                .context(\"Failed to write output from `bytes_source_remaining_length`\")?;\n\n            Ok(0)\n        })\n    }\n\n    /// Writes up to `buffer_len` bytes from `buffer = buffer_ptr[..buffer_len]`,\n    /// to the `sink`, registered in the host environment.\n    ///\n    /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.\n    /// On success (`0` is returned),\n    /// `buffer_len` is set to the number of bytes written to `sink`.\n    ///\n    /// # Traps\n    ///\n    /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.\n    /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NO_SUCH_BYTES`, when `sink` is not a valid bytes sink.\n    /// - `NO_SPACE`, when there is no room for more bytes in `sink`.\n    ///   (Doesn't currently happen.)\n    pub fn bytes_sink_write(\n        caller: Caller<'_, Self>,\n        sink: u32,\n        buffer_ptr: WasmPtr<u8>,\n        buffer_len_ptr: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_custom(caller, AbiCall::BytesSinkWrite, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n\n            // Retrieve the reducer args if available and requested, or error.\n            let Some(sink) = env.standard_bytes_sink.as_mut().filter(|_| sink == STANDARD_BYTES_SINK) else {\n                return Ok(errno::NO_SUCH_BYTES.get().into());\n            };\n\n            // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`.\n            let buffer_len = u32::read_from(mem, buffer_len_ptr)?;\n            // Write `buffer` to `sink`.\n            let buffer = mem.deref_slice(buffer_ptr, buffer_len)?;\n            sink.extend(buffer);\n\n            Ok(0)\n        })\n    }\n\n    /// Logs at `level` a `message` message occurring in `filename:line_number`\n    /// with [`target`](target) being the module path at the `log!` invocation site.\n    ///\n    /// These various pointers are interpreted lossily as UTF-8 strings with a corresponding `_len`.\n    ///\n    /// The `target` and `filename` pointers are ignored by passing `NULL`.\n    /// The line number is ignored if `line_number == u32::MAX`.\n    ///\n    /// No message is logged if\n    /// - `target != NULL && target + target_len > u64::MAX`\n    /// - `filename != NULL && filename + filename_len > u64::MAX`\n    /// - `message + message_len > u64::MAX`\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `target` is not NULL and `target_ptr[..target_len]` is not in bounds of WASM memory.\n    /// - `filename` is not NULL and `filename_ptr[..filename_len]` is not in bounds of WASM memory.\n    /// - `message` is not NULL and `message_ptr[..message_len]` is not in bounds of WASM memory.\n    ///\n    /// [target]: https://docs.rs/log/latest/log/struct.Record.html#method.target\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn console_log(\n        caller: Caller<'_, Self>,\n        level: u32,\n        target_ptr: WasmPtr<u8>,\n        target_len: u32,\n        filename_ptr: WasmPtr<u8>,\n        filename_len: u32,\n        line_number: u32,\n        message_ptr: WasmPtr<u8>,\n        message_len: u32,\n    ) {\n        let do_console_log = |caller: &mut Caller<'_, Self>| -> WasmResult<()> {\n            let env = caller.data();\n            let mem = env.get_mem().view(&caller);\n\n            // Read the `target`, `filename`, and `message` strings from WASM memory.\n            let target = mem.deref_str_lossy(target_ptr, target_len).check_nullptr()?;\n            let filename = mem.deref_str_lossy(filename_ptr, filename_len).check_nullptr()?;\n            let message = mem.deref_str_lossy(message_ptr, message_len)?;\n\n            // The line number cannot be `u32::MAX` as this represents `Option::None`.\n            let line_number = (line_number != u32::MAX).then_some(line_number);\n\n            let function = env.log_record_function();\n\n            let record = Record {\n                ts: InstanceEnv::now_for_logging(),\n                target: target.as_deref(),\n                filename: filename.as_deref(),\n                line_number,\n                function,\n                message: &message,\n            };\n\n            // Write the log record to the `DatabaseLogger` in the database instance context (replica_ctx).\n            env.instance_env\n                .console_log((level as u8).into(), &record, &caller.as_context());\n            Ok(())\n        };\n        Self::cvt_noret(caller, AbiCall::ConsoleLog, |caller| {\n            let _ = do_console_log(caller);\n        })\n    }\n\n    /// Begins a timing span with `name = name_ptr[..name_len]`.\n    ///\n    /// When the returned `ConsoleTimerId` is passed to [`console_timer_end`],\n    /// the duration between the calls will be printed to the module's logs.\n    ///\n    /// The `name` is interpreted lossily as a UTF-8 string.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.\n    pub fn console_timer_start(caller: Caller<'_, Self>, name_ptr: WasmPtr<u8>, name_len: u32) -> RtResult<u32> {\n        Self::with_span(caller, AbiCall::ConsoleTimerStart, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            let name = mem.deref_str_lossy(name_ptr, name_len)?.into_owned();\n            Ok(env.timing_spans.insert(TimingSpan::new(name)).0)\n        })\n    }\n\n    pub fn console_timer_end(caller: Caller<'_, Self>, span_id: u32) -> RtResult<u32> {\n        Self::cvt_custom(caller, AbiCall::ConsoleTimerEnd, |caller| {\n            let Some(span) = caller.data_mut().timing_spans.take(TimingSpanIdx(span_id)) else {\n                return Ok(errno::NO_SUCH_CONSOLE_TIMER.get().into());\n            };\n            let function = caller.data().log_record_function();\n            caller.data().instance_env.console_timer_end(&span, function);\n            Ok(0)\n        })\n    }\n\n    /// Finds the JWT payload associated with `connection_id`.\n    /// A `[ByteSourceId]` for the payload will be written to `target_ptr`.\n    /// If nothing is found for the connection, `[ByteSourceId::INVALID]` (zero) is written to `target_ptr`.\n    ///\n    /// This must be called inside a transaction (because it reads from a system table).\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `connection_id` does not point to a valid little-endian `ConnectionId`.\n    /// - `target_ptr` is NULL or `target_ptr[..size_of::<u32>()]` is not in bounds of WASM memory.\n    ///  - The `ByteSourceId` to be written to `target_ptr` would overflow [`u32::MAX`].\n    pub fn get_jwt(\n        caller: Caller<'_, Self>,\n        connection_id: WasmPtr<ConnectionId>,\n        target_ptr: WasmPtr<u32>,\n    ) -> RtResult<u32> {\n        Self::cvt_ret(caller, AbiCall::GetJwt, target_ptr, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            let cid = ConnectionId::read_from(mem, connection_id)?;\n            let jwt = env.instance_env.get_jwt_payload(cid)?;\n            let jwt = match jwt {\n                None => {\n                    // We should consider logging a warning here, since we don't expect any\n                    // connection ids to not have a JWT after we migrate.\n                    return Ok(0u32);\n                }\n                Some(jwt) => jwt,\n            };\n            let b = bytes::Bytes::from(jwt);\n            let source_id = env.create_bytes_source(b)?;\n            Ok(source_id.0)\n        })\n    }\n\n    /// Writes the identity of the module into `out = out_ptr[..32]`.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `out_ptr` is NULL or `out` is not in bounds of WASM memory.\n    pub fn identity(caller: Caller<'_, Self>, out_ptr: WasmPtr<u8>) -> RtResult<()> {\n        // Use `with_span` rather than one of the `cvt_*` functions,\n        // as we want to possibly trap, but not to return an error code.\n        Self::with_span(caller, AbiCall::Identity, |caller| {\n            let (mem, env) = Self::mem_env(caller);\n            let identity = env.instance_env.database_identity();\n            // We're implicitly casting `out_ptr` to `WasmPtr<Identity>` here.\n            // (Both types are actually `u32`.)\n            // This works because `Identity::write_to` does not require an aligned pointer,\n            // as it gets a `&mut [u8]` from WASM memory and does `copy_from_slice` with it.\n            identity.write_to(mem, out_ptr)?;\n            Ok(())\n        })\n    }\n\n    /// Suspends execution of this WASM instance until approximately `wake_at_micros_since_unix_epoch`.\n    ///\n    /// Returns immediately if `wake_at_micros_since_unix_epoch` is in the past.\n    ///\n    /// Upon resuming, returns the current timestamp as microseconds since the Unix epoch.\n    ///\n    /// Not particularly useful, except for testing SpacetimeDB internals related to suspending procedure execution.\n    ///\n    /// In our public module-facing interfaces, this function is marked as unstable.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - The calling WASM instance is holding open a transaction.\n    /// - The calling WASM instance is not executing a procedure.\n    // TODO(procedure-sleep-until): remove this\n    pub fn procedure_sleep_until<'caller>(\n        caller: Caller<'caller, Self>,\n        (wake_at_micros_since_unix_epoch,): (i64,),\n    ) -> Fut<'caller, i64> {\n        Self::async_with_span(caller, AbiCall::ProcedureSleepUntil, move |caller| async move {\n            use std::time::SystemTime;\n            let get_current_time = || (caller, Timestamp::now().to_micros_since_unix_epoch());\n\n            if wake_at_micros_since_unix_epoch < 0 {\n                return get_current_time();\n            }\n\n            let wake_at = Timestamp::from_micros_since_unix_epoch(wake_at_micros_since_unix_epoch);\n            let Ok(duration) = SystemTime::from(wake_at).duration_since(SystemTime::now()) else {\n                return get_current_time();\n            };\n\n            tokio::time::sleep(duration).await;\n\n            get_current_time()\n        })\n    }\n\n    /// Starts a mutable transaction,\n    /// blocking until a mutable transaction lock is acquired.\n    ///\n    /// Returns `0` on success,\n    /// enabling further calls that require a pending transaction,\n    /// or an error code otherwise.\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    /// - `out` is NULL or `out[..size_of::<i64>()]` is not in bounds of WASM memory.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.\n    pub fn procedure_start_mut_tx<'caller>(caller: Caller<'caller, Self>, out: WasmPtr<u64>) -> RtResult<u32> {\n        Self::with_span(caller, AbiCall::ProcedureStartMutTransaction, |mut caller| {\n            let (mem, env) = Self::mem_env(&mut caller);\n            let res = env.instance_env.start_mutable_tx().map_err(WasmError::from);\n            let timestamp = Timestamp::now().to_micros_since_unix_epoch() as u64;\n            let res = res.and_then(|()| Ok(timestamp.write_to(mem, out)?));\n\n            res.map(|()| 0u16.into())\n                .or_else(|err| Self::convert_wasm_result(AbiCall::ProcedureStartMutTransaction, err))\n        })\n    }\n\n    /// Commits a mutable transaction,\n    /// blocking until the transaction has been committed\n    /// and subscription queries have been run and broadcast.\n    ///\n    /// Once complete, it returns `0` on success, or an error code otherwise.\n    ///\n    /// # Traps\n    ///\n    /// This function does not trap.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `TRANSACTION_NOT_ANONYMOUS`,\n    ///   if the transaction was not started in [`procedure_start_mut_tx`].\n    ///   This can happen if this syscall is erroneously called by a reducer.\n    ///   The code `NOT_IN_TRANSACTION` does not happen,\n    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n    ///   This currently does not happen as anonymous read transactions\n    ///   are not exposed to modules.\n    pub fn procedure_commit_mut_tx<'caller>(caller: Caller<'caller, Self>) -> RtResult<u32> {\n        Self::with_span(caller, AbiCall::ProcedureCommitMutTransaction, |caller| {\n            let res: Result<u32, WasmError> = (|| {\n                let tx = {\n                    let env = caller.data_mut();\n                    env.instance_env.take_mutable_tx_for_commit().map_err(WasmError::from)?\n                };\n                let tx = Self::refresh_views(caller, tx)?;\n                caller\n                    .data_mut()\n                    .instance_env\n                    .commit_procedure_tx(tx)\n                    .map_err(WasmError::from)?;\n                Ok(0u16.into())\n            })();\n            res.or_else(|err| Self::convert_wasm_result(AbiCall::ProcedureCommitMutTransaction, err))\n        })\n    }\n\n    /// Refresh all views made stale by a procedure `tx`.\n    ///\n    /// This runs each pending view call in the same mutable transaction and writes the refreshed rows\n    /// into the corresponding backing view tables. If any step fails (missing metadata, view execution,\n    /// row decoding, SQL execution, or materialization), this method rolls back `tx` and returns an error.\n    ///\n    /// On success, it returns the same transaction handle so the caller can commit it.\n    fn refresh_views<'a>(caller: &mut Caller<'a, Self>, tx: MutTxId) -> Result<MutTxId, WasmError> {\n        let Some(module_def) = caller.data().module_def.clone() else {\n            caller.data_mut().instance_env.rollback_procedure_tx(tx);\n            return Err(WasmError::Wasm(anyhow!(\n                \"module definition is unavailable while committing a procedure transaction\"\n            )));\n        };\n\n        let views_for_refresh = tx.views_for_refresh().cloned().collect::<Vec<_>>();\n        let mut tx = Some(tx);\n        let mut tx_slot = caller.data().instance_env.tx.clone();\n\n        for view_call in views_for_refresh {\n            let res: anyhow::Result<()> = (|| {\n                let view_def = module_def\n                    .get_view_by_id(view_call.fn_ptr, view_call.sender.is_none())\n                    .ok_or_else(|| anyhow!(\"view with fn_ptr `{}` not found\", view_call.fn_ptr))?;\n\n                let current_tx = tx.take().expect(\"procedure tx missing during view refresh\");\n                let (next_tx, call_result) =\n                    tx_slot.set(current_tx, || Self::call_view(caller, &view_call, &view_def.name));\n                tx = Some(next_tx);\n                let return_data = call_result?;\n\n                let typespace = module_def.typespace();\n                let row_product_type = typespace\n                    .resolve(view_def.product_type_ref)\n                    .resolve_refs()?\n                    .into_product()\n                    .map_err(|_| anyhow!(\"Error resolving row type for view\"))?;\n\n                let rows = match ViewResult::from_return_data(return_data)? {\n                    ViewResult::Rows(bytes) => deserialize_view_rows(view_def.product_type_ref, bytes, typespace)\n                        .map_err(|err| anyhow!(err.to_string()))?,\n                    ViewResult::RawSql(query) => run_query_for_view(\n                        tx.as_mut().expect(\"procedure tx missing while running view query\"),\n                        &query,\n                        &row_product_type,\n                        &view_call,\n                        *caller.data().instance_env.database_identity(),\n                    )?,\n                };\n\n                let stdb = caller.data().instance_env.relational_db().clone();\n                match view_call.sender {\n                    Some(sender) => stdb.materialize_view(\n                        tx.as_mut()\n                            .expect(\"procedure tx missing while materializing authenticated view\"),\n                        view_call.table_id,\n                        sender,\n                        rows,\n                    )?,\n                    None => stdb.materialize_anonymous_view(\n                        tx.as_mut()\n                            .expect(\"procedure tx missing while materializing anonymous view\"),\n                        view_call.table_id,\n                        rows,\n                    )?,\n                }\n\n                Ok(())\n            })();\n\n            if let Err(err) = res {\n                let tx = tx.expect(\"procedure tx missing while rolling back failed view refresh\");\n                caller.data_mut().instance_env.rollback_procedure_tx(tx);\n                return Err(WasmError::Wasm(err));\n            }\n        }\n\n        Ok(tx.expect(\"procedure tx missing after view refresh\"))\n    }\n\n    /// Execute a view and return its payload.\n    ///\n    /// This helper is used by [`Self::refresh_views`] while a procedure transaction is being committed.\n    /// It temporarily sets the active function type to the target view for dependency tracking,\n    /// invokes the cached typed view export, restores the previous function type, and decodes the\n    /// result sink into [`ViewReturnData`].\n    fn call_view<'a>(\n        caller: &mut Caller<'a, Self>,\n        view_call: &ViewCallInfo,\n        view_name: &Identifier,\n    ) -> anyhow::Result<ViewReturnData> {\n        // Preserve the procedure's result/error sink so this view does not overwrite it.\n        let previous_standard_sink = {\n            let env = caller.data_mut();\n            env.standard_bytes_sink.take()\n        };\n\n        let prev_func_type = caller\n            .data_mut()\n            .instance_env\n            .swap_func_type(FuncCallType::View(view_call.clone()));\n\n        let call_result = (|| -> anyhow::Result<i32> {\n            let (args_source, result_sink) = {\n                let env = caller.data_mut();\n                let args_source = env.create_bytes_source(bytes::Bytes::new())?;\n                let result_sink = env.setup_standard_bytes_sink();\n                (args_source, result_sink)\n            };\n\n            let (call_view, call_view_anon) = {\n                let env = caller.data();\n                (env.call_view.clone(), env.call_view_anon.clone())\n            };\n\n            let code = call_view_export(\n                &mut *caller,\n                call_view,\n                call_view_anon,\n                view_name,\n                view_call.fn_ptr.0,\n                view_call.sender,\n                args_source.0,\n                result_sink,\n            )?;\n\n            Ok(code)\n        })();\n\n        caller.data_mut().instance_env.swap_func_type(prev_func_type);\n\n        let result_bytes = {\n            let env = caller.data_mut();\n            // Restore the outer sink of the procedure before propagating any trap/user error from the call.\n            let result = env.take_standard_bytes_sink();\n            env.standard_bytes_sink = previous_standard_sink;\n            result\n        };\n        let code = call_result?;\n\n        decode_view_result_sink_code(code, result_bytes).map_err(|err| match err {\n            ViewResultSinkError::User(err) => anyhow!(\"view call failed: {err}\"),\n            ViewResultSinkError::UnexpectedCode(code) => anyhow!(\n                \"unexpected return code {code} from view call, expected 0, 2, or {failure}\",\n                failure = HOST_CALL_FAILURE.get()\n            ),\n        })\n    }\n\n    /// Aborts a mutable transaction,\n    /// blocking until the transaction has been aborted.\n    ///\n    /// Returns `0` on success, or an error code otherwise.\n    ///\n    /// # Traps\n    ///\n    /// This function does not trap.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `TRANSACTION_NOT_ANONYMOUS`,\n    ///   if the transaction was not started in [`procedure_start_mut_tx`].\n    ///   This can happen if this syscall is erroneously called by a reducer.\n    ///   The code `NOT_IN_TRANSACTION` does not happen,\n    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.\n    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.\n    ///   This currently does not happen as anonymous read transactions\n    ///   are not exposed to modules.\n    pub fn procedure_abort_mut_tx<'caller>(caller: Caller<'caller, Self>) -> RtResult<u32> {\n        Self::with_span(caller, AbiCall::ProcedureAbortMutTransaction, |mut caller| {\n            let (_, env) = Self::mem_env(&mut caller);\n            env.procedure_abort_mut_tx_inner()\n        })\n    }\n\n    /// See [`WasmInstanceEnv::procedure_abort_mut_tx`] for details.\n    pub fn procedure_abort_mut_tx_inner(&mut self) -> RtResult<u32> {\n        self.instance_env\n            .abort_mutable_tx()\n            .map(|()| 0u16.into())\n            .map_err(WasmError::from)\n            .or_else(|err| Self::convert_wasm_result(AbiCall::ProcedureAbortMutTransaction, err))\n    }\n\n    /// In-case there is a anonymous tx at the end of a procedure,\n    /// it must be terminated.\n    ///\n    /// This represents a misuse by the module author of the module ABI.\n    pub fn terminate_dangling_anon_tx(&mut self) {\n        self.instance_env.terminate_dangling_anon_tx();\n    }\n\n    /// Perform an HTTP request as specified by the buffer `request_ptr[..request_len]`,\n    /// suspending execution until the request is complete,\n    /// then return its response details via a [`BytesSource`] written to `out[0]`\n    /// and its response body via another [`BytesSource`] written to `out[1]`.\n    ///\n    /// `request_ptr[..request_len]` should store a BSATN-serialized [`spacetimedb_lib::http::Request`] object\n    /// containing the details of the request to be performed.\n    ///\n    /// `body_ptr[..body_len]` should store a byte array, which will be treated as the body of the request.\n    /// `body_ptr` should be non-null and within the bounds of linear memory even when `body_len` is 0.\n    ///\n    /// If the request is successful, a [`BytesSource`] is written to `out[0]`\n    /// containing a BSATN-encoded [`spacetimedb_lib::http::Response`] object,\n    /// another [`BytesSource`] containing the bytes of the response body are written to `out[1]`,\n    /// and this function returns 0.\n    ///\n    /// \"Successful\" in this context includes any connection which results in any HTTP status code,\n    /// regardless of the specified meaning of that code.\n    /// This includes HTTP error codes such as 404 Not Found and 500 Internal Server Error.\n    ///\n    /// If the request fails, a [`BytesSource`] is written to `out[0]`\n    /// containing a BSATN-encoded `String` describing the failure,\n    /// and this function returns `HTTP_ERROR`.\n    /// In this case, `out[1]` is not written.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error:\n    ///\n    /// - `WOULD_BLOCK_TRANSACTION` if there is currently a transaction open.\n    ///   In this case, `out` is not written.\n    /// - `BSATN_DECODE_ERROR` if `request_ptr[..request_len]` does not contain\n    ///   a valid BSATN-serialized [`spacetimedb_lib::http::Request`] object.\n    ///   In this case, `out` is not written.\n    /// - `HTTP_ERROR` if an error occurs while executing the HTTP request.\n    ///   In this case, a [`BytesSource`] is written to `out`\n    ///   containing a BSATN-encoded [`String`].\n    ///\n    /// # Traps\n    ///\n    /// Traps if:\n    ///\n    /// - `request_ptr` is NULL or `request_ptr[..request_len]` is not in bounds of WASM memory.\n    /// - `body_ptr` is NULL or `body_ptr[..body_len]` is not in bounds of WASM memory.\n    /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.\n    /// - `request_ptr[..request_len]` does not contain a valid BSATN-serialized `spacetimedb_lib::http::Request` object.\n    pub fn procedure_http_request<'caller>(\n        caller: Caller<'caller, Self>,\n        (request_ptr, request_len, body_ptr, body_len, out): (WasmPtr<u8>, u32, WasmPtr<u8>, u32, WasmPtr<u32>),\n    ) -> Fut<'caller, RtResult<u32>> {\n        use spacetimedb_lib::http as st_http;\n\n        Self::async_with_span(caller, AbiCall::ProcedureHttpRequest, move |mut caller| async move {\n            let (mem, env) = Self::mem_env(&mut caller);\n\n            // Yes clippy, I'm calling a closure at its definition site *on purpose*,\n            // as a hacky-but-stable `try` block.\n            #[allow(clippy::redundant_closure_call)]\n            let res = (async move || {\n                // TODO(procedure-metrics): record size in bytes of request.\n\n                // Read the request from memory as a `spacetimedb_lib::http::Request`,\n                // our bespoke type with a stable layout and BSATN encoding.\n                let request_buf = mem.deref_slice(request_ptr, request_len)?;\n                let request = bsatn::from_slice::<st_http::Request>(request_buf).map_err(|err| {\n                    // This goes to `errno::BSATN_DECODE_ERROR` in `Self::convert_wasm_result`.\n                    NodesError::DecodeValue(err)\n                })?;\n\n                let body_buf = mem.deref_slice(body_ptr, body_len)?;\n                let body = bytes::Bytes::copy_from_slice(body_buf);\n\n                let result = async { env.instance_env.http_request(request, body)?.await }\n                    // TODO(perf): Evaluate whether it's better to run this future on the \"global\" I/O Tokio executor,\n                    // rather than the thread-local database executors.\n                    .await;\n\n                match result {\n                    Ok((response, body)) => {\n                        let result = bsatn::to_vec(&response)\n                            .with_context(|| \"Failed to BSATN serialize `st_http::Response` object\".to_string())?;\n\n                        let bytes_source = WasmInstanceEnv::create_bytes_source(env, result.into())?;\n                        bytes_source.0.write_to(mem, out)?;\n\n                        let bytes_source = WasmInstanceEnv::create_bytes_source(env, body)?;\n                        bytes_source\n                            .0\n                            .write_to(mem, out.saturating_add(size_of::<u32>() as u32))?;\n\n                        Ok(0u32)\n                    }\n                    Err(NodesError::HttpError(err)) => {\n                        let result = bsatn::to_vec(&err).with_context(|| {\n                            format!(\"Failed to BSATN serialize `spacetimedb_lib::http::Error` object {err:#?}\")\n                        })?;\n                        let bytes_source = WasmInstanceEnv::create_bytes_source(env, result.into())?;\n                        bytes_source.0.write_to(mem, out)?;\n                        Ok(errno::HTTP_ERROR.get() as u32)\n                    }\n                    Err(e) => Err(WasmError::Db(e)),\n                }\n            })()\n            .await;\n\n            (\n                caller,\n                res.or_else(|err| Self::convert_wasm_result(AbiCall::ProcedureHttpRequest, err)),\n            )\n        })\n    }\n}\n\ntype Fut<'caller, T> = Box<dyn Send + 'caller + Future<Output = T>>;\n\nimpl<T> BacktraceProvider for wasmtime::StoreContext<'_, T> {\n    fn capture(&self) -> Box<dyn ModuleBacktrace> {\n        Box::new(wasmtime::WasmBacktrace::capture(self))\n    }\n}\n\nimpl ModuleBacktrace for wasmtime::WasmBacktrace {\n    fn frames(&self) -> Vec<BacktraceFrame<'_>> {\n        self.frames()\n            .iter()\n            .map(|f| BacktraceFrame {\n                module_name: None,\n                func_name: f.func_name(),\n            })\n            .collect()\n    }\n}\n"
  },
  {
    "path": "crates/core/src/host/wasmtime/wasmtime_module.rs",
    "content": "use std::sync::Arc;\n\nuse self::module_host_actor::ReducerOp;\n\nuse super::wasm_instance_env::WasmInstanceEnv;\nuse super::{Mem, WasmtimeFuel, EPOCH_TICKS_PER_SECOND};\nuse crate::energy::FunctionBudget;\nuse crate::host::instance_env::{InstanceEnv, TxSlot};\nuse crate::host::module_common::run_describer;\nuse crate::host::wasm_common::module_host_actor::{\n    AnonymousViewOp, DescribeError, ExecutionError, ExecutionStats, InitializationError, InstanceOp, ViewOp,\n    ViewReturnData,\n};\nuse crate::host::wasm_common::*;\nuse crate::replica_context::ReplicaContext;\nuse crate::subscription::module_subscription_manager::TransactionOffset;\nuse crate::util::string_from_utf8_lossy_owned;\nuse futures_util::FutureExt;\nuse spacetimedb_datastore::locking_tx_datastore::FuncCallType;\nuse spacetimedb_lib::{bsatn, ConnectionId, Identity, RawModuleDef};\nuse spacetimedb_primitives::errno::HOST_CALL_FAILURE;\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_schema::identifier::Identifier;\nuse wasmtime::{\n    AsContext, AsContextMut, ExternType, Instance, InstancePre, Linker, Store, TypedFunc, WasmBacktrace, WasmParams,\n    WasmResults,\n};\n\nfn log_traceback(func_type: &str, func: &str, e: &wasmtime::Error) {\n    log::info!(\"{func_type} \\\"{func}\\\" runtime error: {e}\");\n    if let Some(bt) = e.downcast_ref::<WasmBacktrace>() {\n        let frames_len = bt.frames().len();\n        for (i, frame) in bt.frames().iter().enumerate() {\n            log::info!(\n                \"  Frame #{}: {}\",\n                frames_len - i,\n                rustc_demangle::demangle(frame.func_name().unwrap_or(\"<unknown>\"))\n            );\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct WasmtimeModule {\n    module: InstancePre<WasmInstanceEnv>,\n}\n\nimpl WasmtimeModule {\n    pub(super) fn new(module: InstancePre<WasmInstanceEnv>) -> Self {\n        WasmtimeModule { module }\n    }\n\n    pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(10, 4);\n\n    pub(super) fn link_imports(linker: &mut Linker<WasmInstanceEnv>) -> anyhow::Result<()> {\n        const { assert!(WasmtimeModule::IMPLEMENTED_ABI.major == spacetimedb_lib::MODULE_ABI_MAJOR_VERSION) };\n        macro_rules! link_functions {\n            ($($module:literal :: $func:ident,)*) => {\n                #[allow(deprecated)]\n                linker$(.func_wrap($module, stringify!($func), WasmInstanceEnv::$func)?)*;\n            }\n        }\n        macro_rules! link_async_functions {\n            ($($module:literal :: $func:ident,)*) => {\n                #[allow(deprecated)]\n                linker$(.func_wrap_async($module, stringify!($func), WasmInstanceEnv::$func)?)*;\n            }\n        }\n        abi_funcs!(link_functions, link_async_functions);\n        Ok(())\n    }\n}\n\nimpl module_host_actor::WasmModule for WasmtimeModule {\n    type Instance = WasmtimeInstance;\n    type InstancePre = Self;\n\n    type ExternType = ExternType;\n\n    fn get_export(&self, s: &str) -> Option<Self::ExternType> {\n        self.module\n            .module()\n            .exports()\n            .find(|exp| exp.name() == s)\n            .map(|exp| exp.ty())\n    }\n\n    fn for_each_export<E>(&self, mut f: impl FnMut(&str, &Self::ExternType) -> Result<(), E>) -> Result<(), E> {\n        self.module\n            .module()\n            .exports()\n            .try_for_each(|exp| f(exp.name(), &exp.ty()))\n    }\n\n    fn instantiate_pre(&self) -> Result<Self::InstancePre, InitializationError> {\n        Ok(self.clone())\n    }\n}\n\nfn handle_error_sink_code(code: i32, error: Vec<u8>) -> Result<(), ExecutionError> {\n    handle_result_sink_code(code, error).map(drop)\n}\n\npub(super) enum ViewResultSinkError {\n    User(String),\n    UnexpectedCode(i32),\n}\n\n/// Handle the return code from a function using a result sink.\n///\n/// On success, returns the result bytes.\n/// On failure, returns the error message.\nfn handle_result_sink_code(code: i32, result: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {\n    match code {\n        0 => Ok(result),\n        CALL_FAILURE => Err(ExecutionError::User(string_from_utf8_lossy_owned(result).into())),\n        _ => Err(ExecutionError::Recoverable(anyhow::anyhow!(\"unknown return code\"))),\n    }\n}\n\n/// Handle the return code from a view function using a result sink.\n/// For views, we treat the return code 2 as a successful return using the header format.\nfn handle_view_result_sink_code(code: i32, result: Vec<u8>) -> Result<ViewReturnData, ExecutionError> {\n    decode_view_result_sink_code(code, result).map_err(|err| match err {\n        ViewResultSinkError::User(err) => ExecutionError::User(err.into()),\n        ViewResultSinkError::UnexpectedCode(_) => ExecutionError::Recoverable(anyhow::anyhow!(\"unknown return code\")),\n    })\n}\n\npub(super) fn decode_view_result_sink_code(code: i32, result: Vec<u8>) -> Result<ViewReturnData, ViewResultSinkError> {\n    match code {\n        0 => Ok(ViewReturnData::Rows(result.into())),\n        2 => Ok(ViewReturnData::HeaderFirst(result.into())),\n        CALL_FAILURE => Err(ViewResultSinkError::User(string_from_utf8_lossy_owned(result))),\n        _ => Err(ViewResultSinkError::UnexpectedCode(code)),\n    }\n}\n\nconst CALL_FAILURE: i32 = HOST_CALL_FAILURE.get() as i32;\n\n/// Invoke `typed_func` and assert that it doesn't yield.\n///\n/// Our Wasmtime is configured for `async` execution, and will panic if we use the non-async [`TypedFunc::call`].\n/// The `async` config is necessary to allow procedures to suspend, e.g. when making HTTP calls or acquiring transactions.\n/// However, most of the WASM we execute, incl. reducers and startup functions, should never block/yield.\n/// Rather than crossing our fingers and trusting, we run [`TypedFunc::call_async`] in [`FutureExt::now_or_never`],\n/// an \"async executor\" which invokes [`std::task::Future::poll`] exactly once.\npub(super) fn call_sync_typed_func<Args, Ret>(\n    typed_func: &TypedFunc<Args, Ret>,\n    mut ctx: impl AsContextMut<Data = WasmInstanceEnv>,\n    args: Args,\n) -> anyhow::Result<Ret>\nwhere\n    Args: WasmParams + Sync,\n    Ret: WasmResults + Sync,\n{\n    let fut = typed_func.call_async(ctx.as_context_mut(), args);\n    fut.now_or_never()\n        .expect(\"`call_async` of supposedly synchronous WASM function returned `Poll::Pending`\")\n}\n\n#[allow(clippy::too_many_arguments)]\npub(super) fn call_view_export(\n    mut ctx: impl AsContextMut<Data = WasmInstanceEnv>,\n    call_view: Option<CallViewType>,\n    call_view_anon: Option<CallViewAnonType>,\n    view_name: &Identifier,\n    fn_ptr: u32,\n    sender: Option<Identity>,\n    args_source: u32,\n    result_sink: u32,\n) -> anyhow::Result<i32> {\n    if let Some(sender) = sender {\n        let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(sender);\n        let call_view = call_view.ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Module defines view {} but does not export `{}`\",\n                view_name,\n                CALL_VIEW_DUNDER\n            )\n        })?;\n\n        call_sync_typed_func(\n            &call_view,\n            ctx.as_context_mut(),\n            (fn_ptr, sender_0, sender_1, sender_2, sender_3, args_source, result_sink),\n        )\n    } else {\n        let call_view_anon = call_view_anon.ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Module defines anonymous view {} but does not export `{}`\",\n                view_name,\n                CALL_VIEW_ANON_DUNDER,\n            )\n        })?;\n\n        call_sync_typed_func(\n            &call_view_anon,\n            ctx.as_context_mut(),\n            (fn_ptr, args_source, result_sink),\n        )\n    }\n}\n\nimpl module_host_actor::WasmInstancePre for WasmtimeModule {\n    type Instance = WasmtimeInstance;\n\n    fn instantiate(&self, env: InstanceEnv, func_names: &FuncNames) -> Result<Self::Instance, InitializationError> {\n        let env = WasmInstanceEnv::new(env);\n        let mut store = Store::new(self.module.module().engine(), env);\n        let instance_fut = self.module.instantiate_async(&mut store);\n\n        let instance = instance_fut\n            .now_or_never()\n            .expect(\"`instantiate_async` did not immediately return `Ready`\")\n            .map_err(InitializationError::Instantiation)?;\n\n        let mem = Mem::extract(&instance, &mut store).unwrap();\n        store.data_mut().instantiate(mem);\n\n        store.epoch_deadline_callback(|store| {\n            let env = store.data();\n            let database = env.instance_env().replica_ctx.database_identity;\n            let funcall = env.log_record_function().unwrap_or_default();\n            let dur = env.funcall_start().elapsed();\n            // TODO(procedure-timing): This measurement is not super meaningful for procedures,\n            // which may (will) suspend execution and therefore may not have been continuously running since `env.funcall_start`.\n            tracing::warn!(funcall, ?database, \"Wasm has been running for {dur:?}\");\n            Ok(wasmtime::UpdateDeadline::Continue(EPOCH_TICKS_PER_SECOND))\n        });\n\n        // Note: this budget is just for initializers\n        set_store_fuel(&mut store, FunctionBudget::DEFAULT_BUDGET.into());\n        store.set_epoch_deadline(EPOCH_TICKS_PER_SECOND);\n\n        for preinit in &func_names.preinits {\n            let func = instance.get_typed_func::<(), ()>(&mut store, preinit).unwrap();\n            call_sync_typed_func(&func, &mut store, ()).map_err(|err| InitializationError::RuntimeError {\n                err,\n                func: preinit.clone(),\n            })?;\n        }\n\n        if let Ok(init) = instance.get_typed_func::<u32, i32>(&mut store, SETUP_DUNDER) {\n            let setup_error = store.data_mut().setup_standard_bytes_sink();\n            let res = call_sync_typed_func(&init, &mut store, setup_error);\n            let error = store.data_mut().take_standard_bytes_sink();\n\n            let res = res\n                .map_err(ExecutionError::Trap)\n                .and_then(|code| handle_error_sink_code(code, error));\n\n            res.map_err(|e| match e {\n                ExecutionError::User(err) => InitializationError::Setup(err),\n                ExecutionError::Recoverable(err) | ExecutionError::Trap(err) => {\n                    let func = SETUP_DUNDER.to_owned();\n                    InitializationError::RuntimeError { err, func }\n                }\n            })?\n        }\n\n        let call_reducer = instance\n            .get_typed_func(&mut store, CALL_REDUCER_DUNDER)\n            .expect(\"no call_reducer\");\n\n        let call_procedure = get_call_procedure(&mut store, &instance);\n        let call_view = get_call_view(&mut store, &instance);\n        let call_view_anon = get_call_view_anon(&mut store, &instance);\n        store\n            .data_mut()\n            .set_call_view_exports(call_view.clone(), call_view_anon.clone());\n\n        Ok(WasmtimeInstance {\n            store,\n            instance,\n            call_reducer,\n            call_procedure,\n            call_view,\n            call_view_anon,\n        })\n    }\n}\n\n/// Look up the `instance`'s export named by [`CALL_PROCEDURE_DUNDER`].\n///\n/// Return `None` if the `instance` has no such export.\n/// Modules from before the introduction of procedures will not have a `__call_procedure__` export,\n/// which is fine because they also won't define any procedures.\n///\n/// Panics if the `instance` has an export at the expected name,\n/// but it is not a function or is a function of an inappropriate type.\n/// For new modules, this will be caught during publish.\n/// Old modules from before the introduction of procedures might have an export at that name,\n/// but it follows the double-underscore pattern of reserved names,\n/// so we're fine to break those modules.\nfn get_call_procedure(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallProcedureType> {\n    // Wasmtime uses `anyhow` for error reporting, vexing library users the world over.\n    // This means we can't distinguish between the failure modes of `Instance::get_typed_func`.\n    // Instead, we type out the body of that method ourselves,\n    // but with error handling appropriate to our needs.\n    let export = instance.get_export(store.as_context_mut(), CALL_PROCEDURE_DUNDER)?;\n\n    Some(\n        export\n            .into_func()\n            .unwrap_or_else(|| panic!(\"{CALL_PROCEDURE_DUNDER} export is not a function\"))\n            .typed(store)\n            .unwrap_or_else(|err| panic!(\"{CALL_PROCEDURE_DUNDER} export is a function with incorrect type: {err}\")),\n    )\n}\n\n/// Look up the `instance`'s export named by [`CALL_VIEW_DUNDER`].\n///\n/// Similar to [`get_call_procedure`], but for views.\nfn get_call_view(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallViewType> {\n    let export = instance.get_export(store.as_context_mut(), CALL_VIEW_DUNDER)?;\n    Some(\n        export\n            .into_func()\n            .unwrap_or_else(|| panic!(\"{CALL_VIEW_DUNDER} export is not a function\"))\n            .typed(store)\n            .unwrap_or_else(|err| panic!(\"{CALL_VIEW_DUNDER} export is a function with incorrect type: {err}\")),\n    )\n}\n\n/// Look up the `instance`'s export named by [`CALL_VIEW_ANON_DUNDER`].\n///\n/// Similar to [`get_call_procedure`], but for anonymous views.\nfn get_call_view_anon(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallViewAnonType> {\n    let export = instance.get_export(store.as_context_mut(), CALL_VIEW_ANON_DUNDER)?;\n    Some(\n        export\n            .into_func()\n            .unwrap_or_else(|| panic!(\"{CALL_VIEW_ANON_DUNDER} export is not a function\"))\n            .typed(store)\n            .unwrap_or_else(|err| panic!(\"{CALL_VIEW_ANON_DUNDER} export is a function with incorrect type: {err}\")),\n    )\n}\n\n// `__call_procedure__` takes the same arguments as `__call_reducer__`.\ntype CallProcedureType = CallReducerType;\n\n/// The function signature of `__call_reducer__`\ntype CallReducerType = TypedFunc<\n    (\n        // ReducerId\n        u32,\n        // sender_0\n        u64,\n        // sender_1\n        u64,\n        // sender_2\n        u64,\n        // sender_3\n        u64,\n        // connection_id_0\n        u64,\n        // connection_id_1\n        u64,\n        // timestamp\n        u64,\n        // byte source id for args\n        u32,\n        // byte sink id for return\n        u32,\n    ),\n    i32,\n>;\n\n/// The function signature of `__call_view__`\npub(super) type CallViewType = TypedFunc<\n    (\n        // ViewId\n        u32,\n        // sender_0\n        u64,\n        // sender_1\n        u64,\n        // sender_2\n        u64,\n        // sender_3\n        u64,\n        // byte source id for args\n        u32,\n        // byte sink id for return\n        u32,\n    ),\n    i32,\n>;\n\n/// The function signature of `__call_view_anon__`\npub(super) type CallViewAnonType = TypedFunc<\n    (\n        // ViewId\n        u32,\n        // byte source id for args\n        u32,\n        // byte sink id for return\n        u32,\n    ),\n    i32,\n>;\n\npub struct WasmtimeInstance {\n    store: Store<WasmInstanceEnv>,\n    instance: Instance,\n    call_reducer: CallReducerType,\n    call_procedure: Option<CallProcedureType>,\n    call_view: Option<CallViewType>,\n    call_view_anon: Option<CallViewAnonType>,\n}\n\nimpl module_host_actor::WasmInstance for WasmtimeInstance {\n    fn extract_descriptions(&mut self) -> Result<RawModuleDef, DescribeError> {\n        let describer_func_name = DESCRIBE_MODULE_DUNDER;\n\n        let describer = self\n            .instance\n            .get_typed_func::<u32, ()>(&mut self.store, describer_func_name)\n            .map_err(DescribeError::Signature)?;\n\n        let sink = self.store.data_mut().setup_standard_bytes_sink();\n\n        run_describer(log_traceback, || {\n            call_sync_typed_func(&describer, &mut self.store, sink)\n        })?;\n\n        // Fetch the bsatn returned by the describer call.\n        let bytes = self.store.data_mut().take_standard_bytes_sink();\n\n        let desc: RawModuleDef = bsatn::from_slice(&bytes).map_err(DescribeError::Decode)?;\n\n        Ok(desc)\n    }\n\n    fn replica_ctx(&self) -> &Arc<ReplicaContext> {\n        &self.store.data().instance_env().replica_ctx\n    }\n\n    fn tx_slot(&self) -> TxSlot {\n        self.store.data().instance_env().tx.clone()\n    }\n\n    fn set_module_def(&mut self, module_def: Arc<ModuleDef>) {\n        self.store.data_mut().set_module_def(module_def);\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    fn call_reducer(&mut self, op: ReducerOp<'_>, budget: FunctionBudget) -> module_host_actor::ReducerExecuteResult {\n        let store = &mut self.store;\n\n        prepare_store_for_call(store, budget);\n\n        // Prepare sender identity and connection ID, as LITTLE-ENDIAN byte arrays.\n        let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(*op.caller_identity);\n        let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(*op.caller_connection_id);\n\n        // Prepare arguments to the reducer + the error sink & start timings.\n        let args_bytes = op.args.get_bsatn().clone();\n\n        let reducer_name = op.name.clone().into();\n        let (args_source, errors_sink) =\n            store\n                .data_mut()\n                .start_funcall(reducer_name, args_bytes, op.timestamp, op.call_type());\n\n        let call_result = call_sync_typed_func(\n            &self.call_reducer,\n            &mut *store,\n            (\n                op.id.0,\n                sender_0,\n                sender_1,\n                sender_2,\n                sender_3,\n                conn_id_0,\n                conn_id_1,\n                op.timestamp.to_micros_since_unix_epoch() as u64,\n                args_source.0,\n                errors_sink,\n            ),\n        );\n\n        let (stats, error) = finish_opcall(store, budget);\n\n        let call_result = call_result\n            .map_err(ExecutionError::Trap)\n            .and_then(|code| handle_error_sink_code(code, error))\n            .map(|_| None);\n\n        module_host_actor::ReducerExecuteResult { stats, call_result }\n    }\n\n    fn call_view(&mut self, op: ViewOp<'_>, budget: FunctionBudget) -> module_host_actor::ViewExecuteResult {\n        let store = &mut self.store;\n        prepare_store_for_call(store, budget);\n\n        // Prepare arguments to the reducer + the error sink & start timings.\n        let args_bytes = op.args.get_bsatn().clone();\n\n        let (args_source, errors_sink) =\n            store\n                .data_mut()\n                .start_funcall(op.name.clone(), args_bytes, op.timestamp, op.call_type());\n\n        let call_result = call_view_export(\n            &mut *store,\n            self.call_view.clone(),\n            self.call_view_anon.clone(),\n            op.name,\n            op.fn_ptr.0,\n            Some(*op.sender),\n            args_source.0,\n            errors_sink,\n        );\n\n        let (stats, result_bytes) = finish_opcall(store, budget);\n\n        let call_result = call_result\n            .map_err(ExecutionError::Trap)\n            .and_then(|code| handle_view_result_sink_code(code, result_bytes));\n\n        module_host_actor::ViewExecuteResult { stats, call_result }\n    }\n\n    fn call_view_anon(\n        &mut self,\n        op: AnonymousViewOp<'_>,\n        budget: FunctionBudget,\n    ) -> module_host_actor::ViewExecuteResult {\n        let store = &mut self.store;\n        prepare_store_for_call(store, budget);\n\n        // Prepare arguments to the reducer + the error sink & start timings.\n        let args_bytes = op.args.get_bsatn().clone();\n\n        let (args_source, errors_sink) =\n            store\n                .data_mut()\n                .start_funcall(op.name.clone(), args_bytes, op.timestamp, op.call_type());\n\n        let call_result = call_view_export(\n            &mut *store,\n            self.call_view.clone(),\n            self.call_view_anon.clone(),\n            op.name,\n            op.fn_ptr.0,\n            None,\n            args_source.0,\n            errors_sink,\n        );\n\n        let (stats, result_bytes) = finish_opcall(store, budget);\n\n        let call_result = call_result\n            .map_err(ExecutionError::Trap)\n            .and_then(|code| handle_view_result_sink_code(code, result_bytes));\n\n        module_host_actor::ViewExecuteResult { stats, call_result }\n    }\n\n    fn log_traceback(&self, func_type: &str, func: &str, trap: &anyhow::Error) {\n        log_traceback(func_type, func, trap)\n    }\n\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    async fn call_procedure(\n        &mut self,\n        op: module_host_actor::ProcedureOp,\n        budget: FunctionBudget,\n    ) -> (module_host_actor::ProcedureExecuteResult, Option<TransactionOffset>) {\n        let store = &mut self.store;\n        prepare_store_for_call(store, budget);\n\n        // Prepare sender identity and connection ID, as LITTLE-ENDIAN byte arrays.\n        let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(op.caller_identity);\n        let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(op.caller_connection_id);\n\n        // Prepare arguments to the reducer + the error sink & start timings.\n        let (args_source, result_sink) =\n            store\n                .data_mut()\n                .start_funcall(op.name.clone(), op.arg_bytes, op.timestamp, FuncCallType::Procedure);\n\n        let Some(call_procedure) = self.call_procedure.as_ref() else {\n            let res = module_host_actor::ProcedureExecuteResult {\n                stats: zero_execution_stats(store),\n                call_result: Err(anyhow::anyhow!(\n                    \"Module defines procedure {} but does not export `{}`\",\n                    op.name,\n                    CALL_PROCEDURE_DUNDER,\n                )),\n            };\n            return (res, None);\n        };\n        let call_result = call_procedure\n            .call_async(\n                &mut *store,\n                (\n                    op.id.0,\n                    sender_0,\n                    sender_1,\n                    sender_2,\n                    sender_3,\n                    conn_id_0,\n                    conn_id_1,\n                    op.timestamp.to_micros_since_unix_epoch() as u64,\n                    args_source.0,\n                    result_sink,\n                ),\n            )\n            .await;\n\n        // Close any ongoing anonymous transactions by aborting them,\n        // in case of improper ABI use (start but no commit/abort).\n        store.data_mut().terminate_dangling_anon_tx();\n\n        // Close the timing span for this procedure and get the BSATN bytes of its result.\n        let (stats, result_bytes) = finish_opcall(store, budget);\n\n        let call_result = call_result.and_then(|code| {\n            (code == 0).then_some(result_bytes.into()).ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"{CALL_PROCEDURE_DUNDER} returned unexpected code {code}. Procedures should return code 0 or trap.\"\n                )\n            })\n        });\n\n        let res = module_host_actor::ProcedureExecuteResult { stats, call_result };\n\n        // Take the last tx offset.\n        // Only commits for anonymous transactions currently cause this to advance.\n        let tx_offset = store.data_mut().take_procedure_tx_offset();\n\n        (res, tx_offset)\n    }\n}\n\nfn set_store_fuel(store: &mut impl AsContextMut, fuel: WasmtimeFuel) {\n    store.as_context_mut().set_fuel(fuel.0).unwrap();\n}\n\nfn get_store_fuel(store: &impl AsContext) -> WasmtimeFuel {\n    WasmtimeFuel(store.as_context().get_fuel().unwrap())\n}\n\nfn prepare_store_for_call(store: &mut Store<WasmInstanceEnv>, budget: FunctionBudget) {\n    // note that FunctionBudget being a u64 is load-bearing here - although we convert budget right back into\n    // EnergyQuanta at the end of this function, from_energy_quanta clamps it to a u64 range.\n    // otherwise, we'd return something like `used: i128::MAX - u64::MAX`, which is inaccurate.\n    set_store_fuel(store, budget.into());\n\n    // We enable epoch interruption only to log on long-running WASM functions.\n    // Our epoch interrupt callback logs and then immediately resumes execution.\n    store.set_epoch_deadline(EPOCH_TICKS_PER_SECOND);\n}\n\n/// Convert `caller_identity` to the format used by `__call_reducer__` and `__call_procedure__`,\n/// i.e. an array of 4 `u64`s.\n///\n/// Callers should destructure this like:\n/// ```ignore\n/// # let identity = Identity::ZERO;\n/// let [sender_0, sender_1, sender_2, sender_3] = prepare_identity_for_call(identity);\n/// ```\nfn prepare_identity_for_call(caller_identity: Identity) -> [u64; 4] {\n    // Encode this as a LITTLE-ENDIAN byte array\n    bytemuck::must_cast(caller_identity.to_byte_array())\n}\n\n/// Convert `caller_connection_id` to the format used by `__call_reducer` and `__call_procedure__`,\n/// i.e. an array of 2 `u64`s.\n///\n/// Callers should destructure this like:\n/// ```ignore\n/// # let connection_id = ConnectionId::ZERO;\n/// let [conn_id_0, conn_id_1] = prepare_connection_id_for_call(connection_id);\n/// ```\n///\nfn prepare_connection_id_for_call(caller_connection_id: ConnectionId) -> [u64; 2] {\n    // Encode this as a LITTLE-ENDIAN byte array\n    bytemuck::must_cast(caller_connection_id.as_le_byte_array())\n}\n\n/// Finish the op call and calculate its [`ExecutionStats`].\nfn finish_opcall(store: &mut Store<WasmInstanceEnv>, initial_budget: FunctionBudget) -> (ExecutionStats, Vec<u8>) {\n    // Signal that this call is finished. This gets us the timings\n    // associated with it, and clears all of the instance state\n    // related to it.\n    let (timings, ret_bytes) = store.data_mut().finish_funcall();\n\n    let remaining_fuel = get_store_fuel(store);\n    let remaining: FunctionBudget = remaining_fuel.into();\n    let energy = module_host_actor::EnergyStats {\n        budget: initial_budget,\n        remaining,\n    };\n\n    let stats = ExecutionStats {\n        energy,\n        timings,\n        memory_allocation: get_memory_size(store),\n    };\n    (stats, ret_bytes)\n}\n\nfn zero_execution_stats(store: &Store<WasmInstanceEnv>) -> ExecutionStats {\n    ExecutionStats {\n        energy: module_host_actor::EnergyStats::ZERO,\n        timings: module_host_actor::ExecutionTimings::zero(),\n        memory_allocation: get_memory_size(store),\n    }\n}\n\nfn get_memory_size(store: &Store<WasmInstanceEnv>) -> usize {\n    store.data().get_mem().memory.data_size(store)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::energy::EnergyQuanta;\n\n    #[test]\n    fn test_fuel() {\n        let mut store = wasmtime::Store::new(\n            &wasmtime::Engine::new(wasmtime::Config::new().consume_fuel(true)).unwrap(),\n            (),\n        );\n        let budget = FunctionBudget::DEFAULT_BUDGET;\n        set_store_fuel(&mut store, budget.into());\n        store.set_fuel(store.get_fuel().unwrap() - 10).unwrap();\n        let remaining: EnergyQuanta = get_store_fuel(&store).into();\n        let used = EnergyQuanta::from(budget) - remaining;\n        assert_eq!(used, EnergyQuanta::new(10));\n    }\n}\n"
  },
  {
    "path": "crates/core/src/lib.rs",
    "content": "extern crate core;\n\npub mod energy;\npub mod sql;\n\npub mod auth;\npub mod db;\npub mod messages;\npub use spacetimedb_lib::Identity;\npub mod error;\npub use spacetimedb_lib::identity;\npub use spacetimedb_sats::hash;\npub mod callgrind_flag;\npub mod client;\npub mod config;\npub mod database_logger;\npub mod estimation;\npub mod host;\npub mod module_host_context;\npub mod replica_context;\npub mod startup;\npub mod subscription;\npub mod util;\npub mod worker_metrics;\n"
  },
  {
    "path": "crates/core/src/messages/control_db.rs",
    "content": "use spacetimedb_datastore::system_tables::ModuleKind;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_sats::de::Deserialize;\nuse spacetimedb_sats::hash::Hash;\nuse spacetimedb_sats::ser::Serialize;\n\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct IdentityEmail {\n    pub identity: Identity,\n    pub email: String,\n}\n/// An energy balance (per identity).\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct EnergyBalance {\n    pub identity: Identity,\n    /// The balance for this identity this identity.\n    /// NOTE: This is a signed integer, because it is possible\n    /// for a user's balance to go negative. This is allowable\n    /// for reasons of eventual consistency motivated by performance.\n    pub balance: i128,\n}\n\n/// Description of a database.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct Database {\n    /// Internal id of the database, assigned by the control database.\n    pub id: u64,\n    /// Public identity (i.e. [`Identity`]) of the database.\n    pub database_identity: Identity,\n    /// [`Identity`] of the database's owner.\n    pub owner_identity: Identity,\n    /// [`HostType`] of the module associated with the database.\n    ///\n    /// Valid only for as long as `initial_program` is valid.\n    pub host_type: HostType,\n    /// [`Hash`] of the compiled module to initialize the database with.\n    ///\n    /// Updating the database's module will **not** change this value.\n    pub initial_program: Hash,\n}\n\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct DatabaseStatus {\n    pub state: String,\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct Replica {\n    pub id: u64,\n    pub database_id: u64,\n    pub node_id: u64,\n    pub leader: bool,\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct ReplicaStatus {\n    pub state: String,\n}\n#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]\npub struct Node {\n    pub id: u64,\n    /// If `true`, no new user databases will be scheduled on this node.\n    pub unschedulable: bool,\n    /// The hostname this node is reachable at.\n    ///\n    /// If `None`, the node is not currently live.\n    pub advertise_addr: Option<String>,\n    /// The address this node is running its postgres API at.\n    ///\n    /// If `None`, the node is not currently live.\n    pub pg_addr: Option<String>,\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct NodeStatus {\n    /// TODO: node memory, CPU, and storage capacity\n    /// TODO: node memory, CPU, and storage allocatable capacity\n    /// SEE: <https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/node-v1/#NodeStatus>\n    pub state: String,\n}\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    PartialEq,\n    Eq,\n    Hash,\n    PartialOrd,\n    Ord,\n    Default,\n    Serialize,\n    Deserialize,\n    serde::Deserialize,\n    strum::AsRefStr,\n    strum::Display,\n    strum::EnumString,\n)]\n#[repr(i32)]\npub enum HostType {\n    #[default]\n    Wasm = 0,\n    Js = 1,\n}\n\nimpl From<HostType> for ModuleKind {\n    fn from(host_type: HostType) -> Self {\n        match host_type {\n            HostType::Wasm => Self::WASM,\n            HostType::Js => Self::JS,\n        }\n    }\n}\n\nimpl From<ModuleKind> for HostType {\n    fn from(kind: ModuleKind) -> Self {\n        match kind {\n            ModuleKind::WASM => Self::Wasm,\n            ModuleKind::JS => Self::Js,\n            x => unreachable!(\"missing mapping from module kind {x:?} to host type\"),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/messages/control_worker_api.rs",
    "content": "use spacetimedb_lib::Identity;\nuse spacetimedb_sats::de::Deserialize;\nuse spacetimedb_sats::ser::Serialize;\n\nuse super::control_db::*;\n\n/// Messages from control node to worker node.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum WorkerBoundMessage {\n    ScheduleState(ScheduleState),\n    ScheduleUpdate(ScheduleUpdate),\n    EnergyBalanceState(EnergyBalanceState),\n    EnergyBalanceUpdate(EnergyBalanceUpdate),\n}\n/// Messages from worker node to control node.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum ControlBoundMessage {\n    EnergyWithdrawals(EnergyWithdrawals),\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct ScheduleState {\n    pub replicas: Vec<Replica>,\n    pub databases: Vec<Database>,\n    pub nodes: Vec<Node>,\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum ScheduleUpdate {\n    Insert(InsertOperation),\n    Update(UpdateOperation),\n    Delete(DeleteOperation),\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum InsertOperation {\n    Replica(Replica),\n    Database(Database),\n    Node(Node),\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum UpdateOperation {\n    Replica(Replica),\n    Database(Database),\n    Node(Node),\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub enum DeleteOperation {\n    ReplicaId(u64),\n    DatabaseId(u64),\n    NodeId(u64),\n}\n/// An energy balance update from control node to worker node.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct EnergyBalanceUpdate {\n    pub identity: Identity,\n    pub energy_balance: i128,\n}\n// A message to synchronize energy balances from control node to worker node.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct EnergyBalanceState {\n    pub balances: Vec<EnergyBalance>,\n}\n/// Budget spend update from worker up to control node.\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct EnergyWithdrawals {\n    pub withdrawals: Vec<EnergyWithdrawal>,\n}\n#[derive(Clone, PartialEq, Serialize, Deserialize)]\npub struct EnergyWithdrawal {\n    pub identity: Identity,\n    pub amount: i128,\n}\n"
  },
  {
    "path": "crates/core/src/messages/instance_db_trace_log.rs",
    "content": "use spacetimedb_lib::Timestamp;\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::de::Deserialize;\nuse spacetimedb_sats::ser::Serialize;\n\n#[derive(Clone, Serialize, Deserialize)]\npub struct Insert {\n    pub table_id: TableId,\n    pub buffer: Vec<u8>,\n}\n/*\n#[derive(Clone, Serialize, Deserialize)]\npub struct DeletePk {\n    pub table_id: TableId,\n    pub buffer: Vec<u8>,\n    pub result_success: bool,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub struct DeleteValue {\n    pub table_id: TableId,\n    pub buffer: Vec<u8>,\n    pub result_success: bool,\n}\n*/\n#[derive(Clone, Serialize, Deserialize)]\npub struct DeleteByColEq {\n    pub table_id: TableId,\n    pub col_id: u32,\n    pub buffer: Vec<u8>,\n    pub result_deleted_count: u32,\n}\n/*\n#[derive(Clone, Serialize, Deserialize)]\npub struct DeleteRange {\n    pub table_id: TableId,\n    pub cols: u32,\n    pub start_buffer: Vec<u8>,\n    pub end_buffer: Vec<u8>,\n    pub result_deleted_count: u32,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub struct CreateTable {\n    pub table_name: String,\n    pub schema_buffer: Vec<u8>,\n    pub result_table_id: u32,\n}\n*/\n#[derive(Clone, Serialize, Deserialize)]\npub struct GetTableId {\n    pub table_name: String,\n    pub result_table_id: u32,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub struct Iter {\n    pub table_id: TableId,\n    pub result_bytes: Vec<u8>,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub struct CreateIndex {\n    pub index_name: String,\n    pub table_id: TableId,\n    pub index_type: u32,\n    pub col_ids: Vec<u32>,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub struct InstanceEvent {\n    pub event_start: Timestamp,\n    pub duration_micros: u64,\n    pub r#type: InstanceEventType,\n}\n#[derive(Clone, Serialize, Deserialize)]\npub enum InstanceEventType {\n    Insert(Insert),\n    DeleteByColEq(DeleteByColEq),\n    /*\n    DeletePk(DeletePk),\n    DeleteValue(DeleteValue),\n    DeleteRange(DeleteRange),\n    CreateTable(CreateTable),\n    */\n    GetTableId(GetTableId),\n    Iter(Iter),\n    CreateIndex(CreateIndex),\n}\n"
  },
  {
    "path": "crates/core/src/messages/mod.rs",
    "content": "pub mod control_db;\npub mod control_worker_api;\npub mod instance_db_trace_log;\npub mod worker_db;\n"
  },
  {
    "path": "crates/core/src/messages/worker_db.rs",
    "content": "use spacetimedb_sats::de::Deserialize;\nuse spacetimedb_sats::ser::Serialize;\n\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub struct ReplicaState {\n    pub replica_id: u64,\n    pub initialized: bool,\n}\n"
  },
  {
    "path": "crates/core/src/module_host_context.rs",
    "content": "use crate::energy::EnergyMonitor;\nuse crate::host::scheduler::Scheduler;\nuse crate::replica_context::ReplicaContext;\nuse spacetimedb_sats::hash::Hash;\nuse std::sync::Arc;\n\npub struct ModuleCreationContext {\n    pub replica_ctx: Arc<ReplicaContext>,\n    pub scheduler: Scheduler,\n    pub program_hash: Hash,\n    pub energy_monitor: Arc<dyn EnergyMonitor>,\n}\n"
  },
  {
    "path": "crates/core/src/replica_context.rs",
    "content": "use spacetimedb_commitlog::SizeOnDisk;\n\nuse super::database_logger::DatabaseLogger;\nuse crate::db::relational_db::RelationalDB;\nuse crate::error::DBError;\nuse crate::messages::control_db::Database;\nuse crate::subscription::module_subscription_actor::ModuleSubscriptions;\nuse std::io;\nuse std::ops::Deref;\nuse std::sync::Arc;\n\npub type Result<T> = anyhow::Result<T>;\n\n/// A \"live\" database.\n#[derive(Clone)]\npub struct ReplicaContext {\n    pub database: Database,\n    pub replica_id: u64,\n    pub logger: Arc<DatabaseLogger>,\n    pub subscriptions: ModuleSubscriptions,\n    pub relational_db: Arc<RelationalDB>,\n}\n\nimpl ReplicaContext {\n    /// The number of bytes on disk occupied by the database's durability layer.\n    ///\n    /// An in-memory database will return `Ok(0)`.\n    pub fn durability_size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        self.relational_db.size_on_disk()\n    }\n\n    /// The size of the log file.\n    pub fn log_file_size(&self) -> std::result::Result<u64, DBError> {\n        Ok(self.logger.size()?)\n    }\n\n    /// Obtain an array which can be summed to obtain the total disk usage.\n    ///\n    /// Some sources of size-on-disk may error, in which case the corresponding array element will be None.\n    pub fn total_disk_usage(&self) -> TotalDiskUsage {\n        TotalDiskUsage {\n            durability: self\n                .durability_size_on_disk()\n                .inspect_err(|e| {\n                    log::error!(\n                        \"database={} replica={}: failed to obtain durability size on disk: {:#}\",\n                        self.database.database_identity,\n                        self.replica_id,\n                        e\n                    );\n                })\n                .ok(),\n            logs: self\n                .log_file_size()\n                .inspect_err(|e| {\n                    log::error!(\n                        \"database={} replica={}: failed to obtain log file size: {:#}\",\n                        self.database.database_identity,\n                        self.replica_id,\n                        e\n                    );\n                })\n                .ok(),\n        }\n    }\n\n    /// The size in bytes of all of the in-memory data of the database.\n    pub fn mem_usage(&self) -> usize {\n        self.relational_db.size_in_memory()\n    }\n\n    /// Update data size stats.\n    pub fn update_gauges(&self) {\n        self.relational_db.update_data_size_metrics();\n        self.subscriptions.update_gauges();\n    }\n}\n\nimpl Deref for ReplicaContext {\n    type Target = Database;\n\n    fn deref(&self) -> &Self::Target {\n        &self.database\n    }\n}\n\n#[derive(Copy, Clone, Default)]\npub struct TotalDiskUsage {\n    pub durability: Option<SizeOnDisk>,\n    pub logs: Option<u64>,\n}\n\nimpl TotalDiskUsage {\n    /// Returns self, but if any of the sources are None then we take it from fallback\n    pub fn or(self, fallback: TotalDiskUsage) -> Self {\n        Self {\n            durability: self.durability.or(fallback.durability),\n            logs: self.logs.or(fallback.logs),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/sql/ast.rs",
    "content": "use anyhow::Context;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_datastore::system_tables::{StRowLevelSecurityFields, ST_ROW_LEVEL_SECURITY_ID};\nuse spacetimedb_expr::check::SchemaView;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::AlgebraicValue;\nuse spacetimedb_schema::schema::TableOrViewSchema;\nuse std::ops::Deref;\nuse std::sync::Arc;\n\npub struct SchemaViewer<'a, T> {\n    tx: &'a T,\n    auth: &'a AuthCtx,\n}\n\nimpl<T> Deref for SchemaViewer<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.tx\n    }\n}\n\nimpl<T: StateView> SchemaView for SchemaViewer<'_, T> {\n    fn table_id(&self, name: &str) -> Option<TableId> {\n        self.tx\n            .table_id_from_name_or_alias(name)\n            .ok()\n            .flatten()\n            .and_then(|table_id| self.schema_for_table(table_id))\n            .filter(|schema| self.auth.has_read_access(schema.table_access))\n            .map(|schema| schema.table_id)\n    }\n\n    fn schema_for_table(&self, table_id: TableId) -> Option<Arc<TableOrViewSchema>> {\n        self.tx\n            .get_schema(table_id)\n            .filter(|schema| self.auth.has_read_access(schema.table_access))\n            .map(Arc::clone)\n            .map(TableOrViewSchema::from)\n            .map(Arc::new)\n    }\n\n    fn rls_rules_for_table(&self, table_id: TableId) -> anyhow::Result<Vec<Box<str>>> {\n        self.tx\n            .iter_by_col_eq(\n                ST_ROW_LEVEL_SECURITY_ID,\n                StRowLevelSecurityFields::TableId,\n                &AlgebraicValue::from(table_id),\n            )?\n            .map(|row| {\n                row.read_col::<AlgebraicValue>(StRowLevelSecurityFields::Sql)\n                    .with_context(|| {\n                        format!(\n                            \"Failed to read value from the `{}` column of `{}` for table_id `{}`\",\n                            \"sql\", \"st_row_level_security\", table_id\n                        )\n                    })\n                    .and_then(|sql| {\n                        sql.into_string().map_err(|_| {\n                            anyhow::anyhow!(format!(\n                                \"Failed to read value from the `{}` column of `{}` for table_id `{}`\",\n                                \"sql\", \"st_row_level_security\", table_id\n                            ))\n                        })\n                    })\n            })\n            .collect::<anyhow::Result<_>>()\n    }\n}\n\nimpl<'a, T> SchemaViewer<'a, T> {\n    pub fn new(tx: &'a T, auth: &'a AuthCtx) -> Self {\n        Self { tx, auth }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/sql/execute.rs",
    "content": "use std::sync::Arc;\nuse std::time::Duration;\n\nuse super::ast::SchemaViewer;\nuse crate::db::relational_db::RelationalDB;\nuse crate::energy::EnergyQuanta;\nuse crate::error::DBError;\nuse crate::estimation::{check_row_limit, estimate_rows_scanned};\nuse crate::host::module_host::{\n    DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall, RefInstance, ViewCallError, ViewCallResult,\n    ViewOutcome, WasmInstance,\n};\nuse crate::host::{ArgsTuple, ModuleHost};\nuse crate::subscription::module_subscription_actor::{commit_and_broadcast_event, ModuleSubscriptions};\nuse crate::subscription::module_subscription_manager::TransactionOffset;\nuse crate::subscription::tx::DeltaTx;\nuse anyhow::anyhow;\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_datastore::traits::IsolationLevel;\nuse spacetimedb_expr::statement::Statement;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::Timestamp;\nuse spacetimedb_lib::{AlgebraicType, ProductType, ProductValue};\nuse spacetimedb_query::{compile_sql_stmt, execute_dml_stmt, execute_select_stmt};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse tokio::sync::oneshot;\n\npub struct StmtResult {\n    pub schema: ProductType,\n    pub rows: Vec<ProductValue>,\n}\n\n#[derive(Debug)]\npub struct SqlResult {\n    /// The offset of the SQL operation's transaction.\n    ///\n    /// Used to determine visibility of the transaction wrt the durability\n    /// requirements requested by the caller.\n    pub tx_offset: TransactionOffset,\n    pub rows: Vec<ProductValue>,\n    /// These metrics will be reported via `report_tx_metrics`.\n    /// They should not be reported separately to avoid double counting.\n    pub metrics: ExecutionMetrics,\n}\n\n/// Run the `SQL` string using the `auth` credentials\n///\n/// If a `ModuleHost` is provided, the SQL query is executed via the module host,\n/// meaning the module’s core is used to run the statement.\n/// If no module host is provided, the SQL query is executed on the current thread.\npub async fn run(\n    db: Arc<RelationalDB>,\n    sql_text: String,\n    auth: AuthCtx,\n    subs: Option<ModuleSubscriptions>,\n    module: Option<ModuleHost>,\n    head: &mut Vec<(RawIdentifier, AlgebraicType)>,\n) -> Result<SqlResult, DBError> {\n    match module {\n        Some(module) => module.call_view_sql(db, sql_text, auth, subs, head).await,\n        None => run_inner::<crate::host::wasmtime::WasmtimeInstance>(None, db, sql_text, auth, subs, head).map(|x| x.0),\n    }\n}\n\n/// Run the `SQL` string using the provided `WasmInstance` and `ModuleDef`\n///\n/// The query will always be executed on the module's thread.\npub(crate) fn run_with_instance<I: WasmInstance>(\n    instance: &mut RefInstance<I>,\n    db: Arc<RelationalDB>,\n    sql_text: String,\n    auth: AuthCtx,\n    subs: Option<ModuleSubscriptions>,\n    head: &mut Vec<(RawIdentifier, AlgebraicType)>,\n) -> Result<(SqlResult, bool), DBError> {\n    run_inner::<I>(Some(instance), db, sql_text, auth, subs, head)\n}\n\nfn run_inner<I: WasmInstance>(\n    instance: Option<&mut RefInstance<I>>,\n    db: Arc<RelationalDB>,\n    sql_text: String,\n    auth: AuthCtx,\n    subs: Option<ModuleSubscriptions>,\n    head: &mut Vec<(RawIdentifier, AlgebraicType)>,\n) -> Result<(SqlResult, bool), DBError> {\n    // We parse the sql statement in a mutable transaction.\n    // If it turns out to be a query, we downgrade the tx.\n    let (tx, stmt) = db.with_auto_rollback(db.begin_mut_tx(IsolationLevel::Serializable, Workload::Sql), |tx| {\n        compile_sql_stmt(&sql_text, &SchemaViewer::new(tx, &auth), &auth)\n    })?;\n\n    let mut metrics = ExecutionMetrics::default();\n\n    match stmt {\n        Statement::Select(stmt) => {\n            // Materialize views and downgrade to a read-only transaction\n            let (tx, trapped) = match instance {\n                Some(instance) => ModuleHost::materialize_views(tx, instance, &stmt, auth.caller(), Workload::Sql)?,\n                None => (tx, false),\n            };\n\n            let (tx_data, tx_metrics_mut, tx) = db.commit_tx_downgrade(tx, Workload::Sql);\n\n            let (tx_offset_send, tx_offset) = oneshot::channel();\n            // Release the tx on drop, so that we record metrics\n            // and set the transaction offset.\n            let mut tx = scopeguard::guard(tx, |tx| {\n                let (offset, tx_metrics_downgrade, reducer) = db.release_tx(tx);\n                let _ = tx_offset_send.send(offset);\n                db.report_tx_metrics(reducer, Some(tx_data), Some(tx_metrics_mut), Some(tx_metrics_downgrade));\n            });\n\n            // Compute the header for the result set\n            stmt.for_each_return_field(|col_name, col_type| {\n                head.push((col_name.clone(), col_type.clone()));\n            });\n\n            // Evaluate the query\n            let rows = execute_select_stmt(&auth, stmt, &DeltaTx::from(&*tx), &mut metrics, |plan| {\n                check_row_limit(\n                    &[&plan],\n                    &db,\n                    &tx,\n                    |plan, tx| plan.plan_iter().map(|plan| estimate_rows_scanned(tx, plan)).sum(),\n                    &auth,\n                )?;\n                Ok(plan)\n            })?;\n\n            // Update transaction metrics\n            tx.metrics.merge(metrics);\n\n            Ok((\n                SqlResult {\n                    tx_offset,\n                    rows,\n                    metrics: tx.metrics,\n                },\n                trapped,\n            ))\n        }\n        Statement::DML(stmt) => {\n            // An extra layer of auth is required for DML\n            if !auth.has_write_access() {\n                return Err(anyhow!(\"Caller {} is not authorized to run SQL DML statements\", auth.caller()).into());\n            }\n\n            // Evaluate the mutation\n            let (mut tx, _) = db.with_auto_rollback(tx, |tx| execute_dml_stmt(&auth, stmt, tx, &mut metrics))?;\n\n            // Update transaction metrics\n            tx.metrics.merge(metrics);\n\n            // Update views\n            let (result, trapped) = match instance {\n                Some(instance) => ModuleHost::call_views_with_tx(tx, instance, auth.caller()),\n                None => (ViewCallResult::default(tx), false),\n            };\n\n            // Rollback transaction and report metrics if view execution failed\n            if let ViewOutcome::Failed(err) = result.outcome {\n                let (_, metrics, reducer) = db.rollback_mut_tx(result.tx);\n                db.report_mut_tx_metrics(reducer, metrics, None);\n                return Err(DBError::View(ViewCallError::InternalError(err)));\n            }\n\n            let tx = result.tx;\n\n            // Commit the tx if there are no deltas to process\n            if subs.is_none() {\n                let metrics = tx.metrics;\n                return db.commit_tx(tx).map(|tx_opt| {\n                    let (tx_offset, tx_data, tx_metrics, reducer) = tx_opt.unwrap();\n\n                    let (tx_offset_sender, tx_offset_receiver) = oneshot::channel();\n                    let _ = tx_offset_sender.send(tx_offset);\n\n                    db.report_mut_tx_metrics(reducer, tx_metrics, Some(tx_data));\n                    (\n                        SqlResult {\n                            tx_offset: tx_offset_receiver,\n                            rows: vec![],\n                            metrics,\n                        },\n                        trapped,\n                    )\n                });\n            }\n\n            // Otherwise downgrade the tx and process the deltas.\n            // Note, we get the delta by downgrading the tx.\n            // Hence we just pass a default `DatabaseUpdate` here.\n            // It will ultimately be replaced with the correct one.\n            let event = ModuleEvent {\n                timestamp: Timestamp::now(),\n                caller_identity: auth.caller(),\n                caller_connection_id: None,\n                function_call: ModuleFunctionCall {\n                    reducer: <_>::default(),\n                    reducer_id: u32::MAX.into(),\n                    args: ArgsTuple::default(),\n                },\n                status: EventStatus::Committed(DatabaseUpdate::default()),\n                reducer_return_value: None,\n                energy_quanta_used: EnergyQuanta::ZERO,\n                host_execution_duration: Duration::ZERO,\n                request_id: None,\n                timer: None,\n            };\n            let res = commit_and_broadcast_event(&subs.unwrap(), None, event, tx);\n            Ok((\n                SqlResult {\n                    tx_offset: res.tx_offset,\n                    rows: vec![],\n                    metrics,\n                },\n                trapped,\n            ))\n        }\n    }\n}\n\n#[cfg(test)]\npub(crate) mod tests {\n    use std::sync::Arc;\n\n    use super::*;\n    use crate::db::relational_db::tests_utils::{self, begin_tx, insert, with_auto_commit, TestDB};\n    use itertools::Itertools;\n    use pretty_assertions::assert_eq;\n    use spacetimedb_datastore::system_tables::{\n        StRowLevelSecurityRow, StTableFields, ST_ROW_LEVEL_SECURITY_ID, ST_TABLE_ID, ST_TABLE_NAME,\n    };\n    use spacetimedb_lib::bsatn::ToBsatn;\n    use spacetimedb_lib::db::auth::{StAccess, StTableType};\n    use spacetimedb_lib::error::{ResultTest, TestError};\n    use spacetimedb_lib::{AlgebraicValue, Identity};\n    use spacetimedb_primitives::{col_list, ColId, TableId};\n    use spacetimedb_sats::{product, AlgebraicType, ArrayValue, ProductType};\n    use spacetimedb_schema::identifier::Identifier;\n    use spacetimedb_schema::schema::{ColumnSchema, TableSchema};\n    use spacetimedb_schema::table_name::TableName;\n\n    /// Short-cut for simplify test execution\n    pub(crate) fn run_for_testing(db: &Arc<RelationalDB>, sql_text: &str) -> Result<Vec<ProductValue>, DBError> {\n        let (subs, runtime) = ModuleSubscriptions::for_test_new_runtime(db.clone());\n        runtime\n            .block_on(run(\n                db.clone(),\n                sql_text.to_string(),\n                AuthCtx::for_testing(),\n                Some(subs),\n                None,\n                &mut vec![],\n            ))\n            .map(|x| x.rows)\n    }\n\n    #[derive(Clone, Debug, Default, Eq, PartialEq)]\n    struct TestRows {\n        data: Vec<ProductValue>,\n    }\n\n    struct GameData {\n        location: TestRows,\n        inv: TestRows,\n        player: TestRows,\n        location_ty: ProductType,\n        inv_ty: ProductType,\n        player_ty: ProductType,\n    }\n\n    fn create_game_data() -> GameData {\n        let inv_ty = ProductType::from([(\"inventory_id\", AlgebraicType::U64), (\"name\", AlgebraicType::String)]);\n        let inv = TestRows {\n            data: vec![product!(1u64, \"health\")],\n        };\n\n        let player_ty = ProductType::from([(\"entity_id\", AlgebraicType::U64), (\"inventory_id\", AlgebraicType::U64)]);\n        let player = TestRows {\n            data: vec![product!(100u64, 1u64), product!(200u64, 1u64), product!(300u64, 1u64)],\n        };\n\n        let location_ty = ProductType::from([\n            (\"entity_id\", AlgebraicType::U64),\n            (\"x\", AlgebraicType::F32),\n            (\"z\", AlgebraicType::F32),\n        ]);\n        let location = TestRows {\n            data: vec![product!(100u64, 0.0f32, 32.0f32), product!(100u64, 1.0f32, 31.0f32)],\n        };\n\n        GameData {\n            location,\n            inv,\n            player,\n            location_ty,\n            inv_ty,\n            player_ty,\n        }\n    }\n\n    fn create_table_with_rows(\n        db: &RelationalDB,\n        tx: &mut crate::db::relational_db::MutTx,\n        table_name: &str,\n        schema: ProductType,\n        rows: &[ProductValue],\n        access: StAccess,\n    ) -> ResultTest<Arc<TableSchema>> {\n        let columns = schema\n            .elements\n            .iter()\n            .cloned()\n            .enumerate()\n            .map(|(i, element)| ColumnSchema {\n                table_id: TableId::SENTINEL,\n                col_name: Identifier::new(element.name.unwrap()).unwrap(),\n                col_type: element.algebraic_type,\n                col_pos: ColId(i as _),\n                alias: None,\n            })\n            .collect();\n\n        let table_id = db.create_table(\n            tx,\n            TableSchema::new(\n                TableId::SENTINEL,\n                TableName::for_test(table_name),\n                None,\n                columns,\n                vec![],\n                vec![],\n                vec![],\n                StTableType::User,\n                access,\n                None,\n                None,\n                false,\n                None,\n            ),\n        )?;\n        let schema = db.schema_for_table_mut(tx, table_id)?;\n\n        for row in rows {\n            insert(db, tx, table_id, row)?;\n        }\n\n        Ok(schema)\n    }\n\n    fn create_data(total_rows: u64) -> ResultTest<(TestDB, TestRows)> {\n        let stdb = TestDB::durable()?;\n\n        let rows: Vec<_> = (1..=total_rows)\n            .map(|i| product!(i, format!(\"health{i}\").into_boxed_str()))\n            .collect();\n        let head = ProductType::from([(\"inventory_id\", AlgebraicType::U64), (\"name\", AlgebraicType::String)]);\n\n        with_auto_commit(&stdb, |tx| {\n            create_table_with_rows(&stdb, tx, \"inventory\", head.clone(), &rows, StAccess::Public)\n        })?;\n        Ok((stdb, TestRows { data: rows }))\n    }\n\n    fn create_identity_table(table_name: &str) -> ResultTest<(TestDB, TestRows)> {\n        let stdb = TestDB::durable()?;\n        let head = ProductType::from([(\"identity\", AlgebraicType::identity())]);\n        let rows = vec![product!(Identity::ZERO), product!(Identity::ONE)];\n\n        with_auto_commit(&stdb, |tx| {\n            create_table_with_rows(&stdb, tx, table_name, head.clone(), &rows, StAccess::Public)\n        })?;\n\n        Ok((stdb, TestRows { data: rows }))\n    }\n\n    #[test]\n    fn test_select_star() -> ResultTest<()> {\n        let (db, input) = create_data(1)?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n\n        assert_eq!(result, input.data, \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_limit() -> ResultTest<()> {\n        let (db, _) = create_data(5)?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory limit 2\")?;\n\n        let (_, input) = create_data(2)?;\n\n        assert_eq!(result, input.data, \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_count() -> ResultTest<()> {\n        let (db, _) = create_data(5)?;\n\n        let sql = \"SELECT count(*) as n FROM inventory\";\n        let result = run_for_testing(&db, sql)?;\n        assert_eq!(result, vec![product![5u64]], \"Inventory\");\n\n        let sql = \"SELECT count(*) as n FROM inventory limit 2\";\n        let result = run_for_testing(&db, sql)?;\n        assert_eq!(result, vec![product![5u64]], \"Inventory\");\n\n        let sql = \"SELECT count(*) as n FROM inventory WHERE inventory_id = 4 or inventory_id = 5\";\n        let result = run_for_testing(&db, sql)?;\n        assert_eq!(result, vec![product![2u64]], \"Inventory\");\n        Ok(())\n    }\n\n    /// Test the evaluation of SELECT, UPDATE, and DELETE parameterized with `:sender`\n    #[test]\n    fn test_sender_param() -> ResultTest<()> {\n        let (db, _) = create_identity_table(\"user\")?;\n\n        const SELECT_ALL: &str = \"SELECT * FROM user\";\n\n        let sql = \"SELECT * FROM user WHERE identity = :sender\";\n        let result = run_for_testing(&db, sql)?;\n        assert_eq!(result, vec![product![Identity::ZERO]]);\n\n        let sql = \"DELETE FROM user WHERE identity = :sender\";\n        run_for_testing(&db, sql)?;\n        let result = run_for_testing(&db, SELECT_ALL)?;\n        assert_eq!(result, vec![product![Identity::ONE]]);\n\n        let zero = \"0\".repeat(64);\n        let one = \"0\".repeat(63) + \"1\";\n\n        let sql = format!(\"UPDATE user SET identity = 0x{zero}\");\n        run_for_testing(&db, &sql)?;\n        let sql = format!(\"UPDATE user SET identity = 0x{one} WHERE identity = :sender\");\n        run_for_testing(&db, &sql)?;\n        let result = run_for_testing(&db, SELECT_ALL)?;\n        assert_eq!(result, vec![product![Identity::ONE]]);\n\n        Ok(())\n    }\n\n    /// Create an [Identity] from a [u8]\n    fn identity_from_u8(v: u8) -> Identity {\n        Identity::from_byte_array([v; 32])\n    }\n\n    /// Insert rules into the RLS system table\n    fn insert_rls_rules(\n        db: &RelationalDB,\n        table_ids: impl IntoIterator<Item = TableId>,\n        rules: impl IntoIterator<Item = &'static str>,\n    ) -> anyhow::Result<()> {\n        with_auto_commit(db, |tx| {\n            for (table_id, sql) in table_ids.into_iter().zip(rules) {\n                db.insert(\n                    tx,\n                    ST_ROW_LEVEL_SECURITY_ID,\n                    &ProductValue::from(StRowLevelSecurityRow {\n                        table_id,\n                        sql: sql.into(),\n                    })\n                    .to_bsatn_vec()?,\n                )?;\n            }\n            Ok(())\n        })\n    }\n\n    /// Insert product values into a table\n    fn insert_rows(\n        db: &RelationalDB,\n        table_id: TableId,\n        rows: impl IntoIterator<Item = ProductValue>,\n    ) -> anyhow::Result<()> {\n        with_auto_commit(db, |tx| {\n            for row in rows.into_iter() {\n                db.insert(tx, table_id, &row.to_bsatn_vec()?)?;\n            }\n            Ok(())\n        })\n    }\n\n    /// Assert this query returns the expected rows for this user\n    async fn assert_query_results(\n        db: Arc<RelationalDB>,\n        sql: &str,\n        auth: AuthCtx,\n        expected: impl IntoIterator<Item = ProductValue>,\n    ) {\n        assert_eq!(\n            run(db, sql.to_string(), auth.clone(), None, None, &mut vec![])\n                .await\n                .unwrap()\n                .rows\n                .into_iter()\n                .sorted()\n                .dedup()\n                .collect::<Vec<_>>(),\n            expected.into_iter().sorted().dedup().collect::<Vec<_>>()\n        );\n    }\n\n    /// Test a query that uses a multi-column index\n    #[tokio::test]\n    async fn test_multi_column_index() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let schema = [\n            (\"a\", AlgebraicType::U64),\n            (\"b\", AlgebraicType::U64),\n            (\"c\", AlgebraicType::U64),\n        ];\n\n        let table_id = db.create_table_for_test_multi_column(\"t\", &schema, [1, 2].into())?;\n\n        insert_rows(\n            &db,\n            table_id,\n            vec![\n                product![0_u64, 1_u64, 2_u64],\n                product![1_u64, 2_u64, 1_u64],\n                product![2_u64, 2_u64, 2_u64],\n            ],\n        )?;\n\n        assert_query_results(\n            db.clone(),\n            \"select * from t where c = 1 and b = 2\",\n            AuthCtx::for_testing(),\n            [product![1_u64, 2_u64, 1_u64]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Test querying a table with RLS rules\n    #[tokio::test]\n    async fn test_rls_rules() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let id_for_a = identity_from_u8(1);\n        let id_for_b = identity_from_u8(2);\n\n        let users_schema = [(\"identity\", AlgebraicType::identity())];\n        let sales_schema = [\n            (\"order_id\", AlgebraicType::U64),\n            (\"customer\", AlgebraicType::identity()),\n        ];\n\n        let users_table_id = db.create_table_for_test(\"users\", &users_schema, &[])?;\n        let sales_table_id = db.create_table_for_test(\"sales\", &sales_schema, &[])?;\n\n        insert_rows(&db, users_table_id, vec![product![id_for_a], product![id_for_b]])?;\n        insert_rows(\n            &db,\n            sales_table_id,\n            vec![\n                product![1u64, id_for_a],\n                product![2u64, id_for_b],\n                product![3u64, id_for_a],\n                product![4u64, id_for_b],\n            ],\n        )?;\n\n        insert_rls_rules(\n            &db,\n            [users_table_id, sales_table_id],\n            [\n                \"select * from users where identity = :sender\",\n                \"select s.* from users u join sales s on u.identity = s.customer\",\n            ],\n        )?;\n\n        let auth_for_a = AuthCtx::new(Identity::ZERO, id_for_a);\n        let auth_for_b = AuthCtx::new(Identity::ZERO, id_for_b);\n\n        assert_query_results(\n            db.clone(),\n            // Should only return the identity for sender \"a\"\n            \"select * from users\",\n            auth_for_a.clone(),\n            [product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the identity for sender \"b\"\n            \"select * from users\",\n            auth_for_b.clone(),\n            [product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            \"select * from users where identity = :sender\",\n            auth_for_a.clone(),\n            [product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            \"select * from users where identity = :sender\",\n            auth_for_b.clone(),\n            [product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            &format!(\"select * from users where identity = 0x{}\", id_for_a.to_hex()),\n            auth_for_a.clone(),\n            [product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            &format!(\"select * from users where identity = 0x{}\", id_for_b.to_hex()),\n            auth_for_b.clone(),\n            [product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            &format!(\n                \"select * from users where identity = :sender and identity = 0x{}\",\n                id_for_a.to_hex()\n            ),\n            auth_for_a.clone(),\n            [product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            &format!(\n                \"select * from users where identity = :sender and identity = 0x{}\",\n                id_for_b.to_hex()\n            ),\n            auth_for_b.clone(),\n            [product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            &format!(\n                \"select * from users where identity = :sender or identity = 0x{}\",\n                id_for_b.to_hex()\n            ),\n            auth_for_a.clone(),\n            [product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            &format!(\n                \"select * from users where identity = :sender or identity = 0x{}\",\n                id_for_a.to_hex()\n            ),\n            auth_for_b.clone(),\n            [product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should not return any rows.\n            // Querying as sender \"a\", but filtering on sender \"b\".\n            &format!(\"select * from users where identity = 0x{}\", id_for_b.to_hex()),\n            auth_for_a.clone(),\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should not return any rows.\n            // Querying as sender \"b\", but filtering on sender \"a\".\n            &format!(\"select * from users where identity = 0x{}\", id_for_a.to_hex()),\n            auth_for_b.clone(),\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should not return any rows.\n            // Querying as sender \"a\", but filtering on sender \"b\".\n            &format!(\n                \"select * from users where identity = :sender and identity = 0x{}\",\n                id_for_b.to_hex()\n            ),\n            auth_for_a.clone(),\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should not return any rows.\n            // Querying as sender \"b\", but filtering on sender \"a\".\n            &format!(\n                \"select * from users where identity = :sender and identity = 0x{}\",\n                id_for_a.to_hex()\n            ),\n            auth_for_b.clone(),\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            \"select * from sales\",\n            auth_for_a.clone(),\n            [product![1u64, id_for_a], product![3u64, id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            \"select * from sales\",\n            auth_for_b.clone(),\n            [product![2u64, id_for_b], product![4u64, id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            \"select s.* from users u join sales s on u.identity = s.customer\",\n            auth_for_a.clone(),\n            [product![1u64, id_for_a], product![3u64, id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            \"select s.* from users u join sales s on u.identity = s.customer\",\n            auth_for_b.clone(),\n            [product![2u64, id_for_b], product![4u64, id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"a\"\n            \"select s.* from users u join sales s on u.identity = s.customer where u.identity = :sender\",\n            auth_for_a.clone(),\n            [product![1u64, id_for_a], product![3u64, id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            // Should only return the orders for sender \"b\"\n            \"select s.* from users u join sales s on u.identity = s.customer where u.identity = :sender\",\n            auth_for_b.clone(),\n            [product![2u64, id_for_b], product![4u64, id_for_b]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Test querying tables with multiple levels of RLS rules\n    #[tokio::test]\n    async fn test_nested_rls_rules() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let id_for_a = identity_from_u8(1);\n        let id_for_b = identity_from_u8(2);\n        let id_for_c = identity_from_u8(3);\n\n        let users_schema = [(\"identity\", AlgebraicType::identity())];\n        let sales_schema = [\n            (\"order_id\", AlgebraicType::U64),\n            (\"product_id\", AlgebraicType::U64),\n            (\"customer\", AlgebraicType::identity()),\n        ];\n\n        let users_table_id = db.create_table_for_test(\"users\", &users_schema, &[0.into()])?;\n        let admin_table_id = db.create_table_for_test(\"admins\", &users_schema, &[0.into()])?;\n        let sales_table_id = db.create_table_for_test(\"sales\", &sales_schema, &[0.into()])?;\n\n        insert_rows(&db, admin_table_id, [product![id_for_c]])?;\n        insert_rows(\n            &db,\n            users_table_id,\n            [product![id_for_a], product![id_for_b], product![id_for_c]],\n        )?;\n        insert_rows(\n            &db,\n            sales_table_id,\n            [product![1u64, 1u64, id_for_a], product![2u64, 2u64, id_for_b]],\n        )?;\n\n        insert_rls_rules(\n            &db,\n            [admin_table_id, users_table_id, users_table_id, sales_table_id],\n            [\n                \"select * from admins where identity = :sender\",\n                \"select * from users where identity = :sender\",\n                \"select users.* from admins join users\",\n                \"select s.* from users u join sales s on u.identity = s.customer\",\n            ],\n        )?;\n\n        let auth_for_a = AuthCtx::new(Identity::ZERO, id_for_a);\n        let auth_for_b = AuthCtx::new(Identity::ZERO, id_for_b);\n        let auth_for_c = AuthCtx::new(Identity::ZERO, id_for_c);\n\n        assert_query_results(\n            db.clone(),\n            \"select * from admins\",\n            auth_for_a.clone(),\n            // Identity \"a\" is not an admin\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from admins\",\n            auth_for_b.clone(),\n            // Identity \"b\" is not an admin\n            [],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from admins\",\n            auth_for_c.clone(),\n            // Identity \"c\" is an admin\n            [product![id_for_c]],\n        )\n        .await;\n\n        assert_query_results(\n            db.clone(),\n            \"select * from users\",\n            auth_for_a.clone(),\n            // Identity \"a\" can only see its own user\n            vec![product![id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from users\",\n            auth_for_b.clone(),\n            // Identity \"b\" can only see its own user\n            vec![product![id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from users\",\n            auth_for_c.clone(),\n            // Identity \"c\" is an admin so it can see everyone's users\n            [product![id_for_a], product![id_for_b], product![id_for_c]],\n        )\n        .await;\n\n        assert_query_results(\n            db.clone(),\n            \"select * from sales\",\n            auth_for_a.clone(),\n            // Identity \"a\" can only see its own orders\n            [product![1u64, 1u64, id_for_a]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from sales\",\n            auth_for_b.clone(),\n            // Identity \"b\" can only see its own orders\n            [product![2u64, 2u64, id_for_b]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select * from sales\",\n            auth_for_c.clone(),\n            // Identity \"c\" is an admin so it can see everyone's orders\n            [product![1u64, 1u64, id_for_a], product![2u64, 2u64, id_for_b]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Test projecting columns from both tables in join\n    #[tokio::test]\n    async fn test_project_join() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let t_schema = [(\"id\", AlgebraicType::U8), (\"x\", AlgebraicType::U8)];\n        let s_schema = [(\"id\", AlgebraicType::U8), (\"y\", AlgebraicType::U8)];\n\n        let t_id = db.create_table_for_test(\"t\", &t_schema, &[0.into()])?;\n        let s_id = db.create_table_for_test(\"s\", &s_schema, &[0.into()])?;\n\n        insert_rows(&db, t_id, [product![1_u8, 2_u8]])?;\n        insert_rows(&db, s_id, [product![1_u8, 3_u8]])?;\n\n        let id = identity_from_u8(1);\n        let auth = AuthCtx::new(Identity::ZERO, id);\n\n        assert_query_results(\n            db.clone(),\n            \"select t.x, s.y from t join s on t.id = s.id\",\n            auth,\n            [product![2_u8, 3_u8]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_view() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let schema = [(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U8)];\n        let (_, table_id) = tests_utils::create_view_for_test(&db, \"my_view\", &schema, false)?;\n\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            tests_utils::insert_into_view(&db, tx, table_id, Some(identity_from_u8(1)), product![0u8, 1u8])?;\n            tests_utils::insert_into_view(&db, tx, table_id, Some(identity_from_u8(2)), product![0u8, 2u8])?;\n            Ok(())\n        })?;\n\n        let id = identity_from_u8(2);\n        let auth = AuthCtx::new(Identity::ZERO, id);\n\n        assert_query_results(db.clone(), \"select * from my_view\", auth, [product![0u8, 2u8]]).await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_anonymous_view() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let schema = [(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U8)];\n        let (_, table_id) = tests_utils::create_view_for_test(&db, \"my_view\", &schema, true)?;\n\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            tests_utils::insert_into_view(&db, tx, table_id, None, product![0u8, 1u8])?;\n            tests_utils::insert_into_view(&db, tx, table_id, None, product![0u8, 2u8])?;\n            Ok(())\n        })?;\n\n        let id = identity_from_u8(1);\n        let auth = AuthCtx::new(Identity::ZERO, id);\n\n        assert_query_results(\n            db.clone(),\n            \"select b from my_view\",\n            auth,\n            [product![1u8], product![2u8]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_view_join_table() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let schema = [(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U8)];\n        let (_, v_id) = tests_utils::create_view_for_test(&db, \"v\", &schema, false)?;\n\n        let schema = [(\"c\", AlgebraicType::U8), (\"d\", AlgebraicType::U8)];\n        let t_id = db.create_table_for_test(\"t\", &schema, &[0.into()])?;\n\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            db.insert(tx, t_id, &product![0u8, 3u8].to_bsatn_vec().unwrap())?;\n            db.insert(tx, t_id, &product![1u8, 4u8].to_bsatn_vec().unwrap())?;\n            tests_utils::insert_into_view(&db, tx, v_id, Some(identity_from_u8(1)), product![0u8, 1u8])?;\n            tests_utils::insert_into_view(&db, tx, v_id, Some(identity_from_u8(2)), product![1u8, 2u8])?;\n            Ok(())\n        })?;\n\n        let id = identity_from_u8(2);\n        let auth = AuthCtx::new(Identity::ZERO, id);\n\n        assert_query_results(\n            db.clone(),\n            \"select t.* from v join t on v.a = t.c\",\n            auth.clone(),\n            [product![1u8, 4u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.* from v join t on v.a = t.c\",\n            auth.clone(),\n            [product![1u8, 2u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.* from v join t where v.a = t.c\",\n            auth.clone(),\n            [product![1u8, 2u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.b as b, t.d as d from v join t on v.a = t.c\",\n            auth.clone(),\n            [product![2u8, 4u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.b as b, t.d as d from v join t where v.a = t.c\",\n            auth.clone(),\n            [product![2u8, 4u8]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_view_join_view() -> anyhow::Result<()> {\n        let db = TestDB::in_memory()?;\n\n        let schema = [(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U8)];\n        let (_, u_id) = tests_utils::create_view_for_test(&db, \"u\", &schema, false)?;\n\n        let schema = [(\"c\", AlgebraicType::U8), (\"d\", AlgebraicType::U8)];\n        let (_, v_id) = tests_utils::create_view_for_test(&db, \"v\", &schema, false)?;\n\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            tests_utils::insert_into_view(&db, tx, u_id, Some(identity_from_u8(1)), product![0u8, 1u8])?;\n            tests_utils::insert_into_view(&db, tx, u_id, Some(identity_from_u8(2)), product![1u8, 2u8])?;\n            tests_utils::insert_into_view(&db, tx, v_id, Some(identity_from_u8(1)), product![0u8, 3u8])?;\n            tests_utils::insert_into_view(&db, tx, v_id, Some(identity_from_u8(2)), product![1u8, 4u8])?;\n            Ok(())\n        })?;\n\n        let id = identity_from_u8(2);\n        let auth = AuthCtx::new(Identity::ZERO, id);\n\n        assert_query_results(\n            db.clone(),\n            \"select u.* from u join v on u.a = v.c\",\n            auth.clone(),\n            [product![1u8, 2u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.* from u join v on u.a = v.c\",\n            auth.clone(),\n            [product![1u8, 4u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select v.* from u join v where u.a = v.c\",\n            auth.clone(),\n            [product![1u8, 4u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select u.b as b, v.d as d from u join v on u.a = v.c\",\n            auth.clone(),\n            [product![2u8, 4u8]],\n        )\n        .await;\n        assert_query_results(\n            db.clone(),\n            \"select u.b as b, v.d as d from u join v where u.a = v.c\",\n            auth,\n            [product![2u8, 4u8]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_select_star_table() -> ResultTest<()> {\n        let (db, input) = create_data(1)?;\n\n        let result = run_for_testing(&db, \"SELECT inventory.* FROM inventory\")?;\n\n        assert_eq!(result, input.data, \"Inventory\");\n\n        let result = run_for_testing(\n            &db,\n            \"SELECT inventory.inventory_id FROM inventory WHERE inventory.inventory_id = 1\",\n        )?;\n\n        assert_eq!(result, vec![product!(1u64)], \"Inventory\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_select_catalog() -> ResultTest<()> {\n        let (db, _) = create_data(1)?;\n\n        let tx = begin_tx(&db);\n        let _ = db.release_tx(tx);\n\n        let result = run_for_testing(\n            &db,\n            &format!(\"SELECT * FROM {ST_TABLE_NAME} WHERE table_id = {ST_TABLE_ID}\"),\n        )?;\n\n        let pk_col_id: ColId = StTableFields::TableId.into();\n        let row = product![\n            ST_TABLE_ID,\n            ST_TABLE_NAME,\n            StTableType::System.as_str(),\n            StAccess::Public.as_str(),\n            Some(AlgebraicValue::Array(ArrayValue::U16(vec![pk_col_id.0].into()))),\n        ];\n\n        assert_eq!(result, vec![row], \"st_table\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_select_column() -> ResultTest<()> {\n        let (db, _) = create_data(1)?;\n\n        let result = run_for_testing(&db, \"SELECT inventory_id FROM inventory\")?;\n\n        let row = product![1u64];\n\n        assert_eq!(result, vec![row], \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_where() -> ResultTest<()> {\n        let (db, _) = create_data(1)?;\n\n        let result = run_for_testing(&db, \"SELECT inventory_id FROM inventory WHERE inventory_id = 1\")?;\n\n        let row = product![1u64];\n\n        assert_eq!(result, vec![row], \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_or() -> ResultTest<()> {\n        let (db, _) = create_data(2)?;\n\n        let mut result = run_for_testing(\n            &db,\n            \"SELECT inventory_id FROM inventory WHERE inventory_id = 1 OR inventory_id = 2\",\n        )?;\n\n        result.sort();\n\n        assert_eq!(result, vec![product![1u64], product![2u64]], \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_nested() -> ResultTest<()> {\n        let (db, _) = create_data(2)?;\n\n        let mut result = run_for_testing(\n            &db,\n            \"SELECT inventory_id FROM inventory WHERE (inventory_id = 1 OR inventory_id = 2 AND (true))\",\n        )?;\n\n        result.sort();\n\n        assert_eq!(result, vec![product![1u64], product![2u64]], \"Inventory\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_inner_join() -> ResultTest<()> {\n        let data = create_game_data();\n\n        let db = TestDB::durable()?;\n\n        with_auto_commit::<_, TestError>(&db, |tx| {\n            let i = create_table_with_rows(&db, tx, \"Inventory\", data.inv_ty, &data.inv.data, StAccess::Public)?;\n            let p = create_table_with_rows(&db, tx, \"Player\", data.player_ty, &data.player.data, StAccess::Public)?;\n            create_table_with_rows(\n                &db,\n                tx,\n                \"Location\",\n                data.location_ty,\n                &data.location.data,\n                StAccess::Public,\n            )?;\n            Ok((p, i))\n        })?;\n\n        let result = run_for_testing(\n            &db,\n            \"SELECT\n        Player.*\n            FROM\n        Player\n        JOIN Location\n        ON Location.entity_id = Player.entity_id\n        WHERE Location.x > 0 AND Location.x <= 32 AND Location.z > 0 AND Location.z <= 32\",\n        )?;\n\n        let row1 = product!(100u64, 1u64);\n\n        assert_eq!(result, vec![row1], \"Player JOIN Location\");\n\n        let result = run_for_testing(\n            &db,\n            \"SELECT\n        Inventory.*\n            FROM\n        Inventory\n        JOIN Player\n        ON Inventory.inventory_id = Player.inventory_id\n        JOIN Location\n        ON Player.entity_id = Location.entity_id\n        WHERE Location.x > 0 AND Location.x <= 32 AND Location.z > 0 AND Location.z <= 32\",\n        )?;\n\n        let row1 = product!(1u64, \"health\");\n\n        assert_eq!(result, vec![row1], \"Inventory JOIN Player JOIN Location\");\n        Ok(())\n    }\n\n    #[test]\n    fn test_multi_way_join_with_bridge_tables() -> anyhow::Result<()> {\n        let db = TestDB::durable()?;\n\n        let orders = db.create_table_for_test(\n            \"orders\",\n            &[\n                (\"o_orderkey\", AlgebraicType::U64),\n                (\"o_custkey\", AlgebraicType::U64),\n                (\"o_orderstatus\", AlgebraicType::U64),\n            ],\n            &[0.into(), 1.into(), 2.into()],\n        )?;\n\n        let customer = db.create_table_for_test(\n            \"customer\",\n            &[(\"c_custkey\", AlgebraicType::U64), (\"c_nationkey\", AlgebraicType::U64)],\n            &[0.into(), 1.into()],\n        )?;\n\n        let nation = db.create_table_for_test(\n            \"nation\",\n            &[\n                (\"n_nationkey\", AlgebraicType::U64),\n                (\"n_name\", AlgebraicType::String),\n                (\"n_regionkey\", AlgebraicType::U64),\n            ],\n            &[0.into(), 2.into()],\n        )?;\n\n        let region = db.create_table_for_test(\n            \"region\",\n            &[(\"r_regionkey\", AlgebraicType::U64), (\"r_name\", AlgebraicType::String)],\n            &[0.into()],\n        )?;\n\n        insert_rows(&db, orders, [product![1u64, 10u64, 0u64], product![2u64, 20u64, 1u64]])?;\n        insert_rows(&db, customer, [product![10u64, 100u64], product![20u64, 200u64]])?;\n        insert_rows(\n            &db,\n            nation,\n            [\n                product![100u64, \"NATION_A\", 1000u64],\n                product![200u64, \"NATION_B\", 2000u64],\n            ],\n        )?;\n        insert_rows(\n            &db,\n            region,\n            [product![1000u64, \"REGION_A\"], product![2000u64, \"REGION_B\"]],\n        )?;\n\n        let result_three_way = run_for_testing(\n            &db,\n            \"\n            SELECT customer.c_custkey, nation.n_name\n            FROM orders\n            JOIN customer ON customer.c_custkey = orders.o_custkey\n            JOIN nation ON nation.n_nationkey = customer.c_nationkey\n            WHERE orders.o_orderstatus = 0\",\n        )?;\n\n        assert_eq!(result_three_way, vec![product![10u64, \"NATION_A\"]]);\n\n        let result_four_way = run_for_testing(\n            &db,\n            \"\n            SELECT customer.c_custkey, region.r_name\n            FROM orders\n            JOIN customer ON customer.c_custkey = orders.o_custkey\n            JOIN nation ON nation.n_nationkey = customer.c_nationkey\n            JOIN region ON region.r_regionkey = nation.n_regionkey\n            WHERE orders.o_orderstatus = 0\",\n        )?;\n\n        assert_eq!(result_four_way, vec![product![10u64, \"REGION_A\"]]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert() -> ResultTest<()> {\n        let (db, mut input) = create_data(1)?;\n\n        let result = run_for_testing(&db, \"INSERT INTO inventory (inventory_id, name) VALUES (2, 'test')\")?;\n\n        assert_eq!(result.len(), 0, \"Return results\");\n\n        let mut result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n\n        input.data.push(product![2u64, \"test\"]);\n        input.data.sort();\n        result.sort();\n\n        assert_eq!(result, input.data, \"Inventory\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_delete() -> ResultTest<()> {\n        let (db, _input) = create_data(1)?;\n\n        run_for_testing(&db, \"INSERT INTO inventory (inventory_id, name) VALUES (2, 't2')\")?;\n        run_for_testing(&db, \"INSERT INTO inventory (inventory_id, name) VALUES (3, 't3')\")?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n        assert_eq!(result.len(), 3, \"Not return results\");\n\n        run_for_testing(&db, \"DELETE FROM inventory WHERE inventory.inventory_id = 3\")?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n        assert_eq!(result.len(), 2, \"Not delete correct row?\");\n\n        run_for_testing(&db, \"DELETE FROM inventory\")?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n        assert_eq!(result.len(), 0, \"Not delete all rows\");\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_update() -> ResultTest<()> {\n        let (db, input) = create_data(1)?;\n\n        run_for_testing(&db, \"INSERT INTO inventory (inventory_id, name) VALUES (2, 't2')\")?;\n        run_for_testing(&db, \"INSERT INTO inventory (inventory_id, name) VALUES (3, 't3')\")?;\n\n        run_for_testing(&db, \"UPDATE inventory SET name = 'c2' WHERE inventory_id = 2\")?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory WHERE inventory_id = 2\")?;\n\n        let mut change = input;\n        change.data.clear();\n        change.data.push(product![2u64, \"c2\"]);\n\n        assert_eq!(result, change.data, \"Update Inventory 2\");\n\n        run_for_testing(&db, \"UPDATE inventory SET name = 'c3'\")?;\n\n        let result = run_for_testing(&db, \"SELECT * FROM inventory\")?;\n\n        let updated: Vec<_> = result\n            .into_iter()\n            .map(|x| x.field_as_str(1, None).unwrap().to_string())\n            .collect();\n        assert_eq!(vec![\"c3\"; 3], updated);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_multi_column() -> ResultTest<()> {\n        let (db, _input) = create_data(1)?;\n\n        // Create table [test] with index on [a, b]\n        let schema = &[\n            (\"a\", AlgebraicType::I32),\n            (\"b\", AlgebraicType::I32),\n            (\"c\", AlgebraicType::I32),\n            (\"d\", AlgebraicType::I32),\n        ];\n        let table_id = db.create_table_for_test_multi_column(\"test\", schema, col_list![0, 1])?;\n        with_auto_commit(&db, |tx| insert(&db, tx, table_id, &product![1, 1, 1, 1]).map(drop))?;\n\n        let result = run_for_testing(&db, \"select * from test where b = 1 and a = 1\")?;\n\n        assert_eq!(result, vec![product![1, 1, 1, 1]]);\n\n        Ok(())\n    }\n\n    /// Test we are protected against stack overflows when:\n    /// 1. The query is too large (too many characters)\n    /// 2. The AST is too deep\n    ///\n    /// Exercise the limit [`recursion::MAX_RECURSION_EXPR`]\n    #[test]\n    fn test_large_query_no_panic() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let _table_id = db\n            .create_table_for_test_multi_column(\n                \"test\",\n                &[(\"x\", AlgebraicType::I32), (\"y\", AlgebraicType::I32)],\n                col_list![0, 1],\n            )\n            .unwrap();\n\n        let build_query = |total| {\n            let mut sql = \"select * from test where \".to_string();\n            for x in 1..total {\n                let fragment = format!(\"x = {x} or \");\n                sql.push_str(&fragment.repeat((total - 1) as usize));\n            }\n            sql.push_str(\"(y = 0)\");\n            sql\n        };\n        let run = |db: &Arc<RelationalDB>, sep: char, sql_text: &str| {\n            run_for_testing(db, sql_text).map_err(|e| e.to_string().split(sep).next().unwrap_or_default().to_string())\n        };\n        let sql = build_query(1_000);\n        assert_eq!(\n            run(&db, ':', &sql),\n            Err(\"SQL query exceeds maximum allowed length\".to_string())\n        );\n\n        let sql = build_query(41); // This causes stack overflow without the limit\n        assert_eq!(run(&db, ',', &sql), Err(\"Recursion limit exceeded\".to_string()));\n\n        let sql = build_query(40); // The max we can with the current limit\n        assert!(run(&db, ',', &sql).is_ok(), \"Expected query to run without panic\");\n\n        // Check no overflow with lot of joins\n        let mut sql = \"SELECT test.* FROM test \".to_string();\n        // We could push up to 700 joins without overflow as long we don't have any conditions,\n        // but here execution become too slow.\n        // TODO: Move this test to the `Plan`\n        for i in 0..200 {\n            sql.push_str(&format!(\"JOIN test AS m{i} ON test.x = m{i}.y \"));\n        }\n\n        assert!(\n            run(&db, ',', &sql).is_ok(),\n            \"Query with many joins and conditions should not overflow\"\n        );\n        Ok(())\n    }\n\n    #[test]\n    fn test_impossible_bounds_no_panic() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = db\n            .create_table_for_test(\"test\", &[(\"x\", AlgebraicType::I32)], &[ColId(0)])\n            .unwrap();\n\n        with_auto_commit(&db, |tx| {\n            for i in 0..1000i32 {\n                insert(&db, tx, table_id, &product!(i)).unwrap();\n            }\n            Ok::<(), DBError>(())\n        })\n        .unwrap();\n\n        let result = run_for_testing(&db, \"select * from test where x > 5 and x < 5\").unwrap();\n        assert!(result.is_empty());\n\n        let result = run_for_testing(&db, \"select * from test where x >= 5 and x < 4\").unwrap();\n        assert!(result.is_empty(), \"Expected no rows but found {result:#?}\");\n\n        let result = run_for_testing(&db, \"select * from test where x > 5 and x <= 4\").unwrap();\n        assert!(result.is_empty());\n        Ok(())\n    }\n\n    #[test]\n    fn test_multi_column_two_ranges() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        // Create table [test] with index on [a, b]\n        let schema = &[(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U8)];\n        let table_id = db.create_table_for_test_multi_column(\"test\", schema, col_list![0, 1])?;\n        let row = product![4u8, 8u8];\n        with_auto_commit(&db, |tx| insert(&db, tx, table_id, &row.clone()).map(drop))?;\n\n        let result = run_for_testing(&db, \"select * from test where a >= 3 and a <= 5 and b >= 3 and b <= 5\")?;\n\n        assert!(result.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_row_limit() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = db.create_table_for_test(\"T\", &[(\"a\", AlgebraicType::U8)], &[])?;\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            for i in 0..5u8 {\n                insert(&db, tx, table_id, &product!(i))?;\n            }\n            Ok(())\n        })?;\n\n        let server = Identity::from_claims(\"issuer\", \"server\");\n        let client = Identity::from_claims(\"issuer\", \"client\");\n\n        let internal_auth = AuthCtx::new(server, server);\n        let external_auth = AuthCtx::new(server, client);\n\n        let tmp_vec = Vec::new();\n\n        let rt = db.runtime().expect(\"runtime should be there\");\n\n        let run = |db, sql, auth, subs, mut tmp_vec| rt.block_on(run(db, sql, auth, subs, None, &mut tmp_vec));\n        // No row limit, both queries pass.\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            internal_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            external_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n\n        // Set row limit.\n        assert!(run(\n            db.clone(),\n            \"SET row_limit = 4\".to_string(),\n            internal_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n\n        // External query fails.\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            internal_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            external_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_err());\n\n        // Increase row limit.\n        assert!(run(\n            db.clone(),\n            \"DELETE FROM st_var WHERE name = 'row_limit'\".to_string(),\n            internal_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n        assert!(run(\n            db.clone(),\n            \"SET row_limit = 5\".to_string(),\n            internal_auth.clone(),\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n\n        // Both queries pass.\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            internal_auth,\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n        assert!(run(\n            db.clone(),\n            \"SELECT * FROM T\".to_string(),\n            external_auth,\n            None,\n            tmp_vec.clone()\n        )\n        .is_ok());\n\n        Ok(())\n    }\n\n    // Verify we don't return rows on DML\n    #[test]\n    fn test_row_dml() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = db.create_table_for_test(\"T\", &[(\"a\", AlgebraicType::U8)], &[])?;\n        with_auto_commit(&db, |tx| -> Result<_, DBError> {\n            for i in 0..4u8 {\n                insert(&db, tx, table_id, &product!(i))?;\n            }\n            Ok(())\n        })?;\n\n        let rt = db.runtime().expect(\"runtime should be there\");\n\n        let server = Identity::from_claims(\"issuer\", \"server\");\n\n        let internal_auth = AuthCtx::new(server, server);\n\n        let tmp_vec = Vec::new();\n        let run = |db, sql: &str, auth, subs, mut tmp_vec| {\n            let sql = sql.to_string();\n            async move { run(db, sql, auth, subs, None, &mut tmp_vec).await }\n        };\n\n        let check = |db, sql, auth, metrics: ExecutionMetrics| {\n            let result = rt.block_on(run(db, sql, auth, None, tmp_vec.clone()))?;\n            assert_eq!(result.rows, vec![]);\n            assert_eq!(result.metrics.rows_inserted, metrics.rows_inserted);\n            assert_eq!(result.metrics.rows_deleted, metrics.rows_deleted);\n            assert_eq!(result.metrics.rows_updated, metrics.rows_updated);\n\n            Ok::<(), DBError>(())\n        };\n\n        let ins = ExecutionMetrics {\n            rows_inserted: 1,\n            ..ExecutionMetrics::default()\n        };\n        let upd = ExecutionMetrics {\n            rows_updated: 5,\n            ..ExecutionMetrics::default()\n        };\n        let del = ExecutionMetrics {\n            rows_deleted: 1,\n            ..ExecutionMetrics::default()\n        };\n\n        check(db.clone(), \"INSERT INTO T (a) VALUES (5)\", internal_auth.clone(), ins)?;\n        check(db.clone(), \"UPDATE T SET a = 2\", internal_auth.clone(), upd)?;\n        assert_eq!(\n            rt.block_on(run(\n                db.clone(),\n                \"SELECT * FROM T\",\n                internal_auth.clone(),\n                None,\n                tmp_vec.clone()\n            ))?\n            .rows,\n            vec![product!(2u8)]\n        );\n        check(db.clone(), \"DELETE FROM T\", internal_auth, del)?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/sql/mod.rs",
    "content": "pub mod ast;\npub mod execute;\npub mod parser;\n"
  },
  {
    "path": "crates/core/src/sql/parser.rs",
    "content": "use crate::sql::ast::SchemaViewer;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_datastore::locking_tx_datastore::MutTxId;\nuse spacetimedb_expr::check::parse_and_type_sub;\nuse spacetimedb_expr::expr::ProjectName;\nuse spacetimedb_lib::db::auth::StAccess;\nuse spacetimedb_lib::db::raw_def::v9::RawRowLevelSecurityDefV9;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_schema::schema::RowLevelSecuritySchema;\n\npub struct RowLevelExpr {\n    pub sql: ProjectName,\n    pub def: RowLevelSecuritySchema,\n}\n\nimpl RowLevelExpr {\n    pub fn build_row_level_expr(\n        tx: &mut MutTxId,\n        auth_ctx: &AuthCtx,\n        rls: &RawRowLevelSecurityDefV9,\n    ) -> anyhow::Result<Self> {\n        let (sql, _) = parse_and_type_sub(&rls.sql, &SchemaViewer::new(tx, auth_ctx), auth_ctx)?;\n        let table_id = sql.return_table_id().unwrap();\n        let schema = tx.schema_for_table(table_id)?;\n\n        match schema.table_access {\n            StAccess::Private => {\n                anyhow::bail!(\n                    \"Cannot define RLS rule on private table: {}. \\\n                        Please make table public if you wish to restrict access using RLS.\",\n                    schema.table_name\n                )\n            }\n            StAccess::Public => Ok(Self {\n                def: RowLevelSecuritySchema {\n                    table_id,\n                    sql: rls.sql.clone(),\n                },\n                sql,\n            }),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/startup.rs",
    "content": "use crossbeam_queue::ArrayQueue;\nuse itertools::Itertools;\nuse spacetimedb_paths::server::{ConfigToml, LogsDir};\nuse std::path::PathBuf;\nuse std::time::Duration;\nuse tracing_appender::rolling;\nuse tracing_core::LevelFilter;\nuse tracing_flame::FlameLayer;\nuse tracing_subscriber::fmt::writer::BoxMakeWriter;\nuse tracing_subscriber::fmt::writer::MakeWriterExt;\nuse tracing_subscriber::layer::SubscriberExt;\nuse tracing_subscriber::prelude::*;\nuse tracing_subscriber::{reload, EnvFilter};\n\nuse crate::config::{ConfigFile, LogConfig};\nuse crate::util::jobs::JobCores;\n\npub use core_affinity::CoreId;\n\npub struct TracingOptions {\n    pub config: LogConfig,\n    /// Whether or not to periodically reload the log config in the background.\n    pub reload_config: Option<ConfigToml>,\n    /// Whether or not to write logs to disk.\n    pub disk_logging: Option<LogsDir>,\n    /// The edition of this spacetime server.\n    pub edition: String,\n    /// Enables tracy profiling.\n    pub tracy: bool,\n    pub flamegraph: Option<PathBuf>,\n}\n\nimpl Default for TracingOptions {\n    fn default() -> Self {\n        Self {\n            config: LogConfig::default(),\n            reload_config: None,\n            disk_logging: None,\n            edition: \"standalone\".to_owned(),\n            tracy: false,\n            flamegraph: None,\n        }\n    }\n}\n\npub fn configure_tracing(opts: TracingOptions) {\n    // Use this to change log levels at runtime.\n    // This means you can change the default log level to trace\n    // if you are trying to debug an issue and need more logs on then turn it off\n    // once you are done.\n\n    let timer = tracing_subscriber::fmt::time();\n    let format = tracing_subscriber::fmt::format::Format::default()\n        .with_timer(timer)\n        .with_line_number(true)\n        .with_file(true)\n        .with_target(false)\n        .compact();\n\n    let write_to = if let Some(logs_dir) = opts.disk_logging {\n        let roller = rolling::Builder::new()\n            .filename_prefix(LogsDir::filename_prefix(&opts.edition))\n            .filename_suffix(LogsDir::filename_extension())\n            .build(logs_dir)\n            .unwrap();\n        // TODO: syslog?\n        BoxMakeWriter::new(std::io::stdout.and(roller))\n    } else {\n        BoxMakeWriter::new(std::io::stdout)\n    };\n\n    let fmt_layer = tracing_subscriber::fmt::Layer::default()\n        .with_writer(write_to)\n        .event_format(format);\n\n    let env_filter_layer = conf_to_filter(opts.config);\n\n    let tracy_layer = if opts.tracy {\n        Some(tracing_tracy::TracyLayer::new())\n    } else {\n        None\n    };\n\n    let (flame_guard, flame_layer) = if let Some(flamegraph_path) = opts.flamegraph {\n        let (flame_layer, guard) = FlameLayer::with_file(flamegraph_path).unwrap();\n        let flame_layer = flame_layer.with_file_and_line(false).with_empty_samples(false);\n        (Some(guard), Some(flame_layer))\n    } else {\n        (None, None)\n    };\n\n    // Is important for `tracy_layer` to be before `fmt_layer` to not print ascii codes...\n    let subscriber = tracing_subscriber::Registry::default()\n        .with(tracy_layer)\n        .with(fmt_layer)\n        .with(flame_layer);\n\n    if let Some(conf_file) = opts.reload_config {\n        let (reload_layer, reload_handle) = tracing_subscriber::reload::Layer::new(env_filter_layer);\n        std::thread::spawn(move || reload_config(&conf_file, &reload_handle));\n        subscriber.with(reload_layer).init();\n    } else {\n        subscriber.with(env_filter_layer).init();\n    };\n\n    if let Some(guard) = flame_guard {\n        tokio::spawn(async move {\n            loop {\n                tokio::time::sleep(Duration::from_secs(5)).await;\n                guard.flush().unwrap();\n            }\n        });\n    }\n}\n\nfn conf_to_filter(conf: LogConfig) -> EnvFilter {\n    EnvFilter::builder()\n        .with_default_directive(conf.level.unwrap_or(LevelFilter::ERROR).into())\n        .parse_lossy(conf.directives.join(\",\"))\n}\n\nfn parse_from_file(path: &ConfigToml) -> EnvFilter {\n    let conf = match ConfigFile::read(path) {\n        Ok(Some(conf)) => conf.logs,\n        Ok(None) => LogConfig::default(),\n        #[allow(clippy::disallowed_macros)]\n        Err(e) => {\n            eprintln!(\"error reading config.toml for logconf reloading: {e}\");\n            LogConfig::default()\n        }\n    };\n    conf_to_filter(conf)\n}\n\nconst RELOAD_INTERVAL: Duration = Duration::from_secs(5);\nfn reload_config<S>(conf_file: &ConfigToml, reload_handle: &reload::Handle<EnvFilter, S>) {\n    let mut prev_time = conf_file.metadata().and_then(|m| m.modified()).ok();\n    loop {\n        std::thread::sleep(RELOAD_INTERVAL);\n        if let Ok(modified) = conf_file.metadata().and_then(|m| m.modified())\n            && prev_time.is_none_or(|prev| modified > prev)\n        {\n            log::info!(\"reloading log config...\");\n            prev_time = Some(modified);\n            if reload_handle.reload(parse_from_file(conf_file)).is_err() {\n                break;\n            }\n        }\n    }\n}\n\n/// Divide up the available CPU cores into pools for different purposes.\n///\n/// Use the fields of the returned [`Cores`] value to actually configure\n/// cores to be pinned.\n///\n/// Pinning different subsystems to different threads reduces overhead from\n/// unnecessary context switching.\n///\n/// * Database instances are critical to overall performance, and keeping each\n///   one on only one thread was shown to significantly increase transaction throughput.\n/// * Tokio and Rayon have their own userspace task schedulers, so if the OS\n///   scheduler is trying to schedule threads as well, it's likely to just\n///   cause interference.\n///\n/// Call only once per process. If obtaining the number of cores fails, or if\n/// there are too few cores, this function may return `Cores::default()`, which\n/// performs no thread pinning.\n// TODO: pinning threads might not be desirable on a machine with other\n//       processes running - this should probably be some sort of flag.\n#[must_use]\npub fn pin_threads() -> Cores {\n    pin_threads_with_reservations(CoreReservations::default())\n}\n\n/// Like [`pin_threads`], but with a custom [`CoreReservations`].\n#[must_use]\npub fn pin_threads_with_reservations(reservations: CoreReservations) -> Cores {\n    Cores::get(reservations).unwrap_or_default()\n}\n\n/// The desired distribution of available cores to purposes.\n///\n/// Note that, in addition to `reserved`, [`Cores`] reserves two additional\n/// cores for the operating system. That is, the denominator for fractions\n/// given below is `num_cpus - reserved - 2`.\npub struct CoreReservations {\n    /// Cores to run database instances on.\n    ///\n    /// Default: 1/8\n    pub databases: f64,\n    /// Cores to run tokio worker threads on.\n    ///\n    /// Default: 4/8\n    pub tokio_workers: f64,\n    /// Cores to run rayon threads on.\n    ///\n    /// Default: 1/8\n    pub rayon: f64,\n    /// Cores to reserve for IRQ handling.\n    ///\n    /// This will be the first `n` [`CoreId`]s in the list.\n    /// Only make use of this if you're configuring the machine for IRQ pinning!\n    ///\n    /// Default: 2\n    pub irq: usize,\n    /// Extra reserved cores.\n    ///\n    /// If greater than zero, this many cores will be reserved _before_\n    /// any of the other reservations are made (but after reserving the OS cores).\n    ///\n    /// Default: 0\n    pub reserved: usize,\n}\n\nimpl Default for CoreReservations {\n    fn default() -> Self {\n        Self {\n            databases: 1.0 / 8.0,\n            tokio_workers: 4.0 / 8.0,\n            rayon: 1.0 / 8.0,\n            irq: 2,\n            reserved: 0,\n        }\n    }\n}\n\nimpl CoreReservations {\n    /// Apply this reservation to an arbitrary list of core ids.\n    ///\n    /// Returns the allocated cores in the order:\n    ///\n    /// - irq\n    /// - reserved\n    /// - databases\n    /// - tokio_workers\n    /// - rayon\n    ///\n    /// Left public for testing and debugging purposes.\n    pub fn apply(&self, cores: &mut Vec<CoreId>) -> [Vec<CoreId>; 5] {\n        let irq = cores.drain(..self.irq).collect_vec();\n        let reserved = cores.drain(..self.reserved).collect_vec();\n\n        let total = cores.len() as f64;\n        let frac = |frac: f64| (total * frac).ceil() as usize;\n        fn claim(cores: &mut Vec<CoreId>, n: usize) -> impl Iterator<Item = CoreId> + '_ {\n            cores.drain(..n.min(cores.len()))\n        }\n\n        let databases = claim(cores, frac(self.databases)).collect_vec();\n        let tokio_workers = claim(cores, frac(self.tokio_workers)).collect_vec();\n        let rayon = claim(cores, frac(self.rayon)).collect_vec();\n\n        [irq, reserved, databases, tokio_workers, rayon]\n    }\n}\n\n/// A type holding cores divvied up into different sets.\n///\n/// Obtained from [`pin_threads()`].\n#[derive(Default)]\npub struct Cores {\n    /// The cores to run database instances on.\n    pub databases: DatabaseCores,\n    /// The cores to run tokio worker threads on.\n    pub tokio: TokioCores,\n    /// The cores to run rayon threads on.\n    pub rayon: RayonCores,\n    /// Extra cores if a [`CoreReservations`] with `reserved > 0` was used.\n    ///\n    /// If `Some`, the boxed array is non-empty.\n    pub reserved: Option<Box<[CoreId]>>,\n    /// Cores shared between tokio runtimes to schedule blocking tasks on.\n    ///\n    /// All remaining cores after [`CoreReservations`] have been made become\n    /// blocking cores.\n    ///\n    /// See `Tokio.blocking` for more context.\n    #[cfg(target_os = \"linux\")]\n    pub blocking: Option<nix::sched::CpuSet>,\n}\n\nimpl Cores {\n    fn get(reservations: CoreReservations) -> Option<Self> {\n        let mut cores = Self::get_core_ids()?;\n\n        let [_irq, reserved, databases, tokio_workers, rayon] = reservations.apply(&mut cores);\n\n        let databases = DatabaseCores(databases);\n        let reserved = (!reserved.is_empty()).then(|| reserved.into());\n        let rayon = RayonCores((!rayon.is_empty()).then_some(rayon));\n\n        // see comment on `TokioCores.blocking`\n        #[cfg(target_os = \"linux\")]\n        let remaining = cores\n            .into_iter()\n            .try_fold(nix::sched::CpuSet::new(), |mut cpuset, core| {\n                cpuset.set(core.id).ok()?;\n                Some(cpuset)\n            });\n\n        let tokio = TokioCores {\n            workers: Some(tokio_workers),\n            #[cfg(target_os = \"linux\")]\n            blocking: remaining,\n        };\n\n        Some(Self {\n            databases,\n            tokio,\n            rayon,\n            reserved,\n            #[cfg(target_os = \"linux\")]\n            blocking: remaining,\n        })\n    }\n\n    /// Get the cores of the local host, as reported by the operating system.\n    ///\n    /// Returns `None` if `num_cpus` is less than 8\n    /// or if core pinning is disabled.\n    /// If `Some` is returned, the `Vec` is non-empty.\n    pub fn get_core_ids() -> Option<Vec<CoreId>> {\n        if cfg!(feature = \"no-core-pinning\") {\n            return None;\n        }\n\n        let cores = core_affinity::get_core_ids()\n            .filter(|cores| cores.len() >= 10)?\n            .into_iter()\n            .collect_vec();\n\n        (!cores.is_empty()).then_some(cores)\n    }\n}\n\n#[derive(Default)]\npub struct TokioCores {\n    pub workers: Option<Vec<CoreId>>,\n    // For blocking threads, we don't want to limit them to a specific number\n    // and pin them to their own cores - they're supposed to run concurrently\n    // with each other. However, `core_affinity` doesn't support affinity masks,\n    // so we just use the Linux-specific API, since this is only a slight boost\n    // and we don't care enough about performance on other platforms.\n    #[cfg(target_os = \"linux\")]\n    pub blocking: Option<nix::sched::CpuSet>,\n}\n\nimpl TokioCores {\n    /// Configures `builder` to pin its worker threads to specific cores.\n    pub fn configure(self, builder: &mut tokio::runtime::Builder) {\n        if let Some(cores) = self.workers {\n            builder.worker_threads(cores.len());\n\n            let cores_queue = Box::new(ArrayQueue::new(cores.len()));\n            for core in cores {\n                cores_queue.push(core).unwrap();\n            }\n\n            // `on_thread_start` gets called for both async worker threads and blocking threads,\n            // but the first `worker_threads` threads that tokio spawns are worker threads,\n            // so this ends up working fine\n            builder.on_thread_start(move || {\n                if let Some(core) = cores_queue.pop() {\n                    core_affinity::set_for_current(core);\n                } else {\n                    #[cfg(target_os = \"linux\")]\n                    if let Some(cpuset) = &self.blocking {\n                        let this = nix::unistd::Pid::from_raw(0);\n                        let _ = nix::sched::sched_setaffinity(this, cpuset);\n                    }\n                }\n            });\n        }\n    }\n}\n\n#[derive(Default)]\npub struct RayonCores(Option<Vec<CoreId>>);\n\nimpl RayonCores {\n    /// Configures a global rayon threadpool, pinning its threads to specific cores.\n    ///\n    /// All rayon threads will be run with `tokio_handle` enetered into.\n    pub fn configure(self, tokio_handle: &tokio::runtime::Handle) {\n        rayon_core::ThreadPoolBuilder::new()\n            .thread_name(|_idx| \"rayon-worker\".to_string())\n            .spawn_handler(thread_spawn_handler(tokio_handle))\n            .num_threads(self.0.as_ref().map_or(0, |cores| cores.len()))\n            .start_handler(move |i| {\n                if let Some(cores) = &self.0 {\n                    core_affinity::set_for_current(cores[i]);\n                }\n            })\n            .build_global()\n            .unwrap()\n    }\n}\n\n/// A Rayon [spawn_handler](https://docs.rs/rustc-rayon-core/latest/rayon_core/struct.ThreadPoolBuilder.html#method.spawn_handler)\n/// which enters the given Tokio runtime at thread startup,\n/// so that the Rayon workers can send along async channels.\n///\n/// Other than entering the `rt`, this spawn handler behaves identitically to the default Rayon spawn handler,\n/// as documented in\n/// https://docs.rs/rustc-rayon-core/0.5.0/rayon_core/struct.ThreadPoolBuilder.html#method.spawn_handler\n///\n/// Having Rayon threads block on async operations is a code smell.\n/// We need to be careful that the Rayon threads never actually block,\n/// i.e. that every async operation they invoke immediately completes.\n/// I (pgoldman 2024-02-22) believe that our Rayon threads only ever send to unbounded channels,\n/// and therefore never wait.\nfn thread_spawn_handler(\n    rt: &tokio::runtime::Handle,\n) -> impl FnMut(rayon::ThreadBuilder) -> Result<(), std::io::Error> + '_ {\n    move |thread| {\n        let rt = rt.clone();\n        let mut builder = std::thread::Builder::new();\n        if let Some(name) = thread.name() {\n            builder = builder.name(name.to_owned());\n        }\n        if let Some(stack_size) = thread.stack_size() {\n            builder = builder.stack_size(stack_size);\n        }\n        builder.spawn(move || {\n            let _rt_guard = rt.enter();\n            thread.run()\n        })?;\n        Ok(())\n    }\n}\n\n#[derive(Default)]\npub struct DatabaseCores(Vec<CoreId>);\n\nimpl DatabaseCores {\n    /// Construct a [`JobCores`] manager suitable for running database WASM code on.\n    ///\n    /// ```rust\n    /// # use spacetimedb::startup::pin_threads;\n    /// let cores = pin_threads();\n    /// let database_cores = cores.databases.make_database_runners();\n    /// ```\n    pub fn make_database_runners(self) -> JobCores {\n        JobCores::from_pinned_cores(self.0)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/delta.rs",
    "content": "use crate::host::module_host::UpdatesRelValue;\nuse anyhow::Result;\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\nuse spacetimedb_execution::{Datastore, DeltaStore, RelValue, Row};\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_primitives::ColList;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_subscription::SubscriptionPlan;\n/// Evaluate a subscription over a delta update.\n/// Returns `None` for empty updates.\n///\n/// IMPORTANT: This does and must implement bag semantics.\n/// That is, we must not remove duplicate rows.\n/// Any deviation from this is a bug, as clients will lose information.\n///\n/// Take for example the semijoin R ⋉ S.\n/// A client needs to know for each row in R,\n/// how many rows it joins with in S.\npub fn eval_delta<'a, Tx: Datastore + DeltaStore>(\n    tx: &'a Tx,\n    metrics: &mut ExecutionMetrics,\n    plan: &SubscriptionPlan,\n) -> Result<Option<UpdatesRelValue<'a>>> {\n    metrics.delta_queries_evaluated += 1;\n\n    let mut inserts = vec![];\n    let mut deletes = vec![];\n\n    let mut duplicate_rows_evaluated = 0;\n    let mut duplicate_rows_sent = 0;\n\n    let col_list = ColList::from_iter(plan.num_private_cols()..plan.num_cols());\n\n    let maybe_project = |row: Row<'a>| -> Result<RelValue<'a>, InvalidFieldError> {\n        if plan.is_view() {\n            Ok(row.project_product(&col_list)?.into())\n        } else {\n            Ok(row.into())\n        }\n    };\n\n    if !plan.is_join() {\n        // Single table plans will never return redundant rows,\n        // so there's no need to track row counts.\n        plan.for_each_insert(tx, metrics, &mut |row| {\n            inserts.push(maybe_project(row)?);\n            Ok(())\n        })?;\n\n        plan.for_each_delete(tx, metrics, &mut |row| {\n            deletes.push(maybe_project(row)?);\n            Ok(())\n        })?;\n    } else {\n        // Query plans for joins may return redundant rows.\n        // We track row counts to avoid sending them to clients.\n        let mut insert_counts = HashMap::new();\n        let mut delete_counts = HashMap::new();\n\n        plan.for_each_insert(tx, metrics, &mut |row| {\n            let row = maybe_project(row)?;\n            let n = insert_counts.entry(row).or_default();\n            if *n > 0 {\n                duplicate_rows_evaluated += 1;\n            }\n            *n += 1;\n            Ok(())\n        })?;\n\n        plan.for_each_delete(tx, metrics, &mut |row| {\n            let row = maybe_project(row)?;\n            match insert_counts.get_mut(&row) {\n                // We have not seen an insert for this row.\n                // If we have seen a delete, increment the metric.\n                // Always increment the delete_count.\n                None => {\n                    let n = delete_counts.entry(row).or_default();\n                    if *n > 0 {\n                        duplicate_rows_evaluated += 1;\n                    }\n                    *n += 1;\n                }\n                // We have already seen an insert for this row.\n                // This is a duplicate, so increment the metric.\n                //\n                // There are no more inserts for this row,\n                // so increment the delete_count as well.\n                Some(0) => {\n                    duplicate_rows_evaluated += 1;\n                    *delete_counts.entry(row).or_default() += 1;\n                }\n                // We have already seen an insert for this row.\n                // This is a duplicate, so increment the metric.\n                //\n                // There are still more inserts for this row,\n                // so don't increment the delete_count.\n                Some(n) => {\n                    duplicate_rows_evaluated += 1;\n                    *n -= 1;\n                }\n            }\n            Ok(())\n        })?;\n\n        for (row, n) in insert_counts.into_iter().filter(|(_, n)| *n > 0) {\n            duplicate_rows_sent += n as u64 - 1;\n            inserts.extend(std::iter::repeat_n(row, n));\n        }\n        for (row, n) in delete_counts.into_iter().filter(|(_, n)| *n > 0) {\n            duplicate_rows_sent += n as u64 - 1;\n            deletes.extend(std::iter::repeat_n(row, n));\n        }\n    }\n\n    // Return `None` for empty updates\n    if inserts.is_empty() && deletes.is_empty() {\n        return Ok(None);\n    }\n\n    metrics.delta_queries_matched += 1;\n    metrics.duplicate_rows_evaluated += duplicate_rows_evaluated;\n    metrics.duplicate_rows_sent += duplicate_rows_sent;\n\n    Ok(Some(UpdatesRelValue { inserts, deletes }))\n}\n"
  },
  {
    "path": "crates/core/src/subscription/execution_unit.rs",
    "content": "use spacetimedb_lib::Identity;\nuse spacetimedb_sats::u256;\n\n/// A hash for uniquely identifying subscription plans.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub struct QueryHash {\n    data: [u8; 32],\n}\n\nimpl From<QueryHash> for u256 {\n    fn from(hash: QueryHash) -> Self {\n        u256::from_le_bytes(hash.data)\n    }\n}\n\nimpl QueryHash {\n    /// The zero value of a QueryHash.\n    pub const NONE: Self = Self { data: [0; 32] };\n\n    /// The min value of a QueryHash.\n    pub const MIN: Self = Self::NONE;\n\n    /// The max value of a QueryHash.\n    pub const MAX: Self = Self { data: [0xFFu8; 32] };\n\n    pub fn from_bytes(bytes: &[u8]) -> Self {\n        Self {\n            data: blake3::hash(bytes).into(),\n        }\n    }\n\n    /// Generate a hash from a query string.\n    pub fn from_string(sql: &str, identity: Identity, has_param: bool) -> Self {\n        if has_param {\n            return Self::from_string_and_identity(sql, identity);\n        }\n        Self::from_bytes(sql.as_bytes())\n    }\n\n    /// Parameterized queries must include the caller identity in their hash.\n    pub fn from_string_and_identity(sql: &str, identity: Identity) -> Self {\n        let mut hasher = blake3::Hasher::new();\n        hasher.update(sql.as_bytes());\n        hasher.update(&identity.to_byte_array());\n        Self {\n            data: hasher.finalize().into(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/metrics.rs",
    "content": "use spacetimedb_physical_plan::plan::PhysicalPlan;\nuse spacetimedb_schema::{schema::TableSchema, table_name::TableName};\nuse std::sync::Arc;\n\n/// Scan strategy types for subscription queries\n#[derive(Debug, Clone, Copy)]\nenum ScanStrategy {\n    /// Full table scan - no indexes used\n    Sequential,\n    /// Uses index but requires post-filtering on non-indexed columns\n    IndexedWithFilter,\n    /// Fully indexed - no post-filtering needed\n    FullyIndexed,\n    /// Mixed strategy (combination of index and table scans)\n    Mixed,\n    /// Unknown/other strategy\n    Unknown,\n}\n\n/// Metrics data for a single subscription query execution\n#[derive(Debug)]\npub struct QueryMetrics {\n    pub scan_type: String,\n    pub table_name: TableName,\n    pub unindexed_columns: String,\n    pub rows_scanned: u64,\n    pub execution_time_micros: u64,\n}\n\nimpl std::fmt::Display for ScanStrategy {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Sequential => write!(f, \"sequential\"),\n            Self::IndexedWithFilter => write!(f, \"indexed_with_filter\"),\n            Self::FullyIndexed => write!(f, \"fully_indexed\"),\n            Self::Mixed => write!(f, \"mixed\"),\n            Self::Unknown => write!(f, \"unknown\"),\n        }\n    }\n}\n\n/// Recursively extracts column names from filter expressions\nfn extract_columns(\n    expr: &spacetimedb_physical_plan::plan::PhysicalExpr,\n    schema: Option<&Arc<TableSchema>>,\n    columns: &mut Vec<String>,\n) {\n    use spacetimedb_physical_plan::plan::PhysicalExpr;\n\n    match expr {\n        PhysicalExpr::Field(tuple_field) => {\n            let col_name = schema\n                .and_then(|s| s.columns.get(tuple_field.field_pos))\n                .map(|col| col.col_name.to_string())\n                .unwrap_or_else(|| format!(\"col_{}\", tuple_field.field_pos));\n            columns.push(col_name);\n        }\n        PhysicalExpr::BinOp(_, lhs, rhs) => {\n            extract_columns(lhs, schema, columns);\n            extract_columns(rhs, schema, columns);\n        }\n        PhysicalExpr::LogOp(_, exprs) => {\n            for expr in exprs {\n                extract_columns(expr, schema, columns);\n            }\n        }\n        PhysicalExpr::Value(_) => {}\n    }\n}\n\n/// Analyzes subscription scan strategy and creates QueryMetrics\npub fn get_query_metrics(\n    table_name: TableName,\n    plan: &PhysicalPlan,\n    rows_scanned: u64,\n    execution_time_micros: u64,\n) -> QueryMetrics {\n    let has_table_scan = plan.any(&|p| matches!(p, PhysicalPlan::TableScan(..)));\n    let has_index_scan = plan.any(&|p| matches!(p, PhysicalPlan::IxScan(..)));\n    let has_post_filter = plan.any(&|p| matches!(p, PhysicalPlan::Filter(..)));\n\n    let strategy = if has_table_scan && has_index_scan {\n        ScanStrategy::Mixed\n    } else if has_table_scan {\n        ScanStrategy::Sequential\n    } else if has_index_scan && has_post_filter {\n        ScanStrategy::IndexedWithFilter\n    } else if has_index_scan {\n        ScanStrategy::FullyIndexed\n    } else {\n        ScanStrategy::Unknown\n    };\n\n    // Extract the schema from the plan\n    let mut schema: Option<Arc<TableSchema>> = None;\n    plan.visit(&mut |p| match p {\n        PhysicalPlan::TableScan(scan, _) => {\n            schema = Some(scan.schema.clone());\n        }\n        PhysicalPlan::IxScan(scan, _) => {\n            schema = Some(scan.schema.clone());\n        }\n        _ => {}\n    });\n\n    let mut columns = Vec::new();\n    plan.visit(&mut |p| {\n        if let PhysicalPlan::Filter(_, expr) = p {\n            extract_columns(expr, schema.as_ref(), &mut columns);\n        }\n    });\n\n    QueryMetrics {\n        scan_type: strategy.to_string(),\n        table_name,\n        unindexed_columns: columns.join(\",\"),\n        rows_scanned,\n        execution_time_micros,\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/mod.rs",
    "content": "use crate::subscription::websocket_building::{BuildableWebsocketFormat, RowListBuilder as _, RowListBuilderSource};\nuse crate::{error::DBError, worker_metrics::WORKER_METRICS};\nuse anyhow::Result;\nuse metrics::QueryMetrics;\nuse module_subscription_manager::Plan;\nuse prometheus::IntCounter;\nuse rayon::iter::{IntoParallelRefIterator, ParallelIterator};\nuse spacetimedb_client_api_messages::websocket::common::ByteListLen as _;\nuse spacetimedb_client_api_messages::websocket::v1::{self as ws_v1};\nuse spacetimedb_datastore::{\n    db_metrics::DB_METRICS, execution_context::WorkloadType, locking_tx_datastore::datastore::MetricsRecorder,\n};\nuse spacetimedb_execution::pipelined::ViewProject;\nuse spacetimedb_execution::{pipelined::PipelinedProject, Datastore, DeltaStore};\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_lib::{metrics::ExecutionMetrics, Identity};\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::bsatn::ToBsatn;\nuse spacetimedb_sats::Serialize;\nuse spacetimedb_schema::table_name::TableName;\nuse std::sync::Arc;\n\npub mod delta;\npub mod execution_unit;\npub mod metrics;\npub mod module_subscription_actor;\npub mod module_subscription_manager;\npub mod query;\npub mod row_list_builder_pool;\n#[allow(clippy::module_inception)] // it's right this isn't ideal :/\npub mod subscription;\npub mod tx;\npub mod websocket_building;\n\n#[derive(Debug)]\npub struct ExecutionCounters {\n    rdb_num_index_seeks: IntCounter,\n    rdb_num_rows_scanned: IntCounter,\n    rdb_num_bytes_scanned: IntCounter,\n    rdb_num_bytes_written: IntCounter,\n    bytes_sent_to_clients: IntCounter,\n    delta_queries_matched: IntCounter,\n    delta_queries_evaluated: IntCounter,\n    duplicate_rows_evaluated: IntCounter,\n    duplicate_rows_sent: IntCounter,\n}\n\nimpl ExecutionCounters {\n    pub fn new(workload: &WorkloadType, db: &Identity) -> Self {\n        Self {\n            rdb_num_index_seeks: DB_METRICS.rdb_num_index_seeks.with_label_values(workload, db),\n            rdb_num_rows_scanned: DB_METRICS.rdb_num_rows_scanned.with_label_values(workload, db),\n            rdb_num_bytes_scanned: DB_METRICS.rdb_num_bytes_scanned.with_label_values(workload, db),\n            rdb_num_bytes_written: DB_METRICS.rdb_num_bytes_written.with_label_values(workload, db),\n            bytes_sent_to_clients: WORKER_METRICS.bytes_sent_to_clients.with_label_values(workload, db),\n            delta_queries_matched: DB_METRICS.delta_queries_matched.with_label_values(db),\n            delta_queries_evaluated: DB_METRICS.delta_queries_evaluated.with_label_values(db),\n            duplicate_rows_evaluated: DB_METRICS.duplicate_rows_evaluated.with_label_values(db),\n            duplicate_rows_sent: DB_METRICS.duplicate_rows_sent.with_label_values(db),\n        }\n    }\n\n    /// Update the global system metrics with transaction-level execution metrics.\n    pub(crate) fn record(&self, metrics: &ExecutionMetrics) {\n        if metrics.index_seeks > 0 {\n            self.rdb_num_index_seeks.inc_by(metrics.index_seeks as u64);\n        }\n        if metrics.rows_scanned > 0 {\n            self.rdb_num_rows_scanned.inc_by(metrics.rows_scanned as u64);\n        }\n        if metrics.bytes_scanned > 0 {\n            self.rdb_num_bytes_scanned.inc_by(metrics.bytes_scanned as u64);\n        }\n        if metrics.bytes_written > 0 {\n            self.rdb_num_bytes_written.inc_by(metrics.bytes_written as u64);\n        }\n        if metrics.bytes_sent_to_clients > 0 {\n            self.bytes_sent_to_clients.inc_by(metrics.bytes_sent_to_clients as u64);\n        }\n        if metrics.delta_queries_matched > 0 {\n            self.delta_queries_matched.inc_by(metrics.delta_queries_matched);\n        }\n        if metrics.delta_queries_evaluated > 0 {\n            self.delta_queries_evaluated.inc_by(metrics.delta_queries_evaluated);\n        }\n        if metrics.duplicate_rows_evaluated > 0 {\n            self.duplicate_rows_evaluated.inc_by(metrics.duplicate_rows_evaluated);\n        }\n        if metrics.duplicate_rows_sent > 0 {\n            self.duplicate_rows_sent.inc_by(metrics.duplicate_rows_sent);\n        }\n    }\n}\n\nimpl MetricsRecorder for ExecutionCounters {\n    fn record(&self, metrics: &ExecutionMetrics) {\n        self.record(metrics);\n    }\n}\n\n/// Execute a subscription query over a view.\n///\n/// Specifically this utility is for queries that return rows from a view.\n/// Unlike user tables, views have internal columns that should not be returned to clients.\n/// The [`ViewProject`] operator implicitly drops these columns as part of its execution.\n///\n/// NOTE: This method was largely copied from [`execute_plan`].\n/// TODO: Merge with [`execute_plan`].\npub fn execute_plan_for_view<F: BuildableWebsocketFormat>(\n    plan_fragments: &[ViewProject],\n    tx: &(impl Datastore + DeltaStore),\n    rlb_pool: &impl RowListBuilderSource<F>,\n) -> Result<(F::List, u64, ExecutionMetrics)> {\n    build_list_with_executor(rlb_pool, |metrics, add| {\n        for fragment in plan_fragments {\n            fragment.execute(tx, metrics, add)?;\n        }\n        Ok(())\n    })\n}\n\n/// Execute a subscription query\npub fn execute_plan<F: BuildableWebsocketFormat>(\n    plan_fragments: &[PipelinedProject],\n    tx: &(impl Datastore + DeltaStore),\n    rlb_pool: &impl RowListBuilderSource<F>,\n) -> Result<(F::List, u64, ExecutionMetrics)> {\n    build_list_with_executor(rlb_pool, |metrics, add| {\n        for fragment in plan_fragments {\n            fragment.execute(tx, metrics, add)?;\n        }\n        Ok(())\n    })\n}\n\n/// Returns a list built by passing a function `add` to `driver`,\n/// which will call the former for every row it processes.\npub fn build_list_with_executor<F: BuildableWebsocketFormat, R: ToBsatn + Serialize>(\n    rlb_pool: &impl RowListBuilderSource<F>,\n    driver: impl FnOnce(&mut ExecutionMetrics, &mut dyn FnMut(R) -> Result<()>) -> Result<()>,\n) -> Result<(F::List, u64, ExecutionMetrics)> {\n    let mut count = 0;\n    let mut list = rlb_pool.take_row_list_builder();\n    let mut metrics = ExecutionMetrics::default();\n\n    driver(&mut metrics, &mut |row| {\n        count += 1;\n        list.push(row);\n        Ok(())\n    })?;\n\n    let list = list.finish();\n    metrics.bytes_scanned += list.num_bytes();\n    metrics.bytes_sent_to_clients += list.num_bytes();\n    Ok((list, count, metrics))\n}\n\n/// When collecting a table update are we inserting or deleting rows?\n/// For unsubscribe operations, we need to delete rows.\n#[derive(Debug, Clone, Copy)]\npub enum TableUpdateType {\n    Subscribe,\n    Unsubscribe,\n}\n\n/// Execute a subscription query over a view and collect the results in a [TableUpdate].\n///\n/// Specifically this utility is for queries that return rows from a view.\n/// Unlike user tables, views have internal columns that should not be returned to clients.\n/// The [`ViewProject`] operator implicitly drops these columns as part of its execution.\n///\n/// NOTE: This method was largely copied from [`collect_table_update`].\n/// TODO: Merge with [`collect_table_update`].\npub fn collect_table_update_for_view<Tx, F>(\n    plan_fragments: &[ViewProject],\n    table_id: TableId,\n    table_name: TableName,\n    tx: &Tx,\n    update_type: TableUpdateType,\n    rlb_pool: &impl RowListBuilderSource<F>,\n) -> Result<(ws_v1::TableUpdate<F>, ExecutionMetrics)>\nwhere\n    Tx: Datastore + DeltaStore,\n    F: BuildableWebsocketFormat,\n{\n    execute_plan_for_view::<F>(plan_fragments, tx, rlb_pool).map(|(rows, num_rows, metrics)| {\n        let empty = F::List::default();\n        let qu = match update_type {\n            TableUpdateType::Subscribe => ws_v1::QueryUpdate {\n                deletes: empty,\n                inserts: rows,\n            },\n            TableUpdateType::Unsubscribe => ws_v1::QueryUpdate {\n                deletes: rows,\n                inserts: empty,\n            },\n        };\n        // We will compress the outer server message,\n        // after we release the tx lock.\n        // There's no need to compress the inner table update too.\n        let update = F::into_query_update(qu, ws_v1::Compression::None);\n        (\n            ws_v1::TableUpdate::new(\n                table_id,\n                table_name.clone().into(),\n                ws_v1::SingleQueryUpdate { update, num_rows },\n            ),\n            metrics,\n        )\n    })\n}\n\n/// Execute a subscription query and collect the results in a [TableUpdate]\npub fn collect_table_update<F: BuildableWebsocketFormat>(\n    plan_fragments: &[PipelinedProject],\n    table_id: TableId,\n    table_name: TableName,\n    tx: &(impl Datastore + DeltaStore),\n    update_type: TableUpdateType,\n    rlb_pool: &impl RowListBuilderSource<F>,\n) -> Result<(ws_v1::TableUpdate<F>, ExecutionMetrics)> {\n    execute_plan::<F>(plan_fragments, tx, rlb_pool).map(|(rows, num_rows, metrics)| {\n        let empty = F::List::default();\n        let qu = match update_type {\n            TableUpdateType::Subscribe => ws_v1::QueryUpdate {\n                deletes: empty,\n                inserts: rows,\n            },\n            TableUpdateType::Unsubscribe => ws_v1::QueryUpdate {\n                deletes: rows,\n                inserts: empty,\n            },\n        };\n        // We will compress the outer server message,\n        // after we release the tx lock.\n        // There's no need to compress the inner table update too.\n        let update = F::into_query_update(qu, ws_v1::Compression::None);\n        (\n            ws_v1::TableUpdate::new(\n                table_id,\n                table_name.clone().into(),\n                ws_v1::SingleQueryUpdate { update, num_rows },\n            ),\n            metrics,\n        )\n    })\n}\n\n/// Execute a collection of subscription queries in parallel\npub fn execute_plans<F: BuildableWebsocketFormat>(\n    auth: &AuthCtx,\n    plans: &[Arc<Plan>],\n    tx: &(impl Datastore + DeltaStore + Sync),\n    update_type: TableUpdateType,\n    rlb_pool: &(impl Sync + RowListBuilderSource<F>),\n) -> Result<(ws_v1::DatabaseUpdate<F>, ExecutionMetrics, Vec<QueryMetrics>), DBError> {\n    plans\n        .par_iter()\n        .flat_map_iter(|plan| plan.plans_fragments().map(|fragment| (plan.sql(), fragment)))\n        .filter(|(_, plan)| {\n            // Since subscriptions only support selects and inner joins,\n            // we filter out any plans that read from an empty table.\n            plan.table_ids().all(|table_id| tx.row_count(table_id) > 0)\n        })\n        .map(|(sql, plan)| (sql, plan, plan.subscribed_table_id(), plan.subscribed_table_name()))\n        .map(|(sql, plan, table_id, table_name)| (sql, plan.optimized_physical_plan().clone(), table_id, table_name))\n        .map(|(sql, plan, table_id, table_name)| (sql, plan.optimize(auth), table_id, table_name))\n        .map(|(sql, plan, table_id, table_name)| {\n            plan.and_then(|plan| {\n                let start_time = std::time::Instant::now();\n\n                let result = if plan.returns_view_table() {\n                    match plan.return_table() {\n                        Some(schema) => {\n                            let pipelined_plan = PipelinedProject::from(plan.clone());\n                            let view_plan =\n                                ViewProject::new(pipelined_plan, schema.num_cols(), schema.num_private_cols());\n                            collect_table_update_for_view(\n                                &[view_plan],\n                                table_id,\n                                table_name.clone(),\n                                tx,\n                                update_type,\n                                rlb_pool,\n                            )?\n                        }\n                        _ => {\n                            let pipelined_plan = PipelinedProject::from(plan.clone());\n                            collect_table_update(\n                                &[pipelined_plan],\n                                table_id,\n                                table_name.clone(),\n                                tx,\n                                update_type,\n                                rlb_pool,\n                            )?\n                        }\n                    }\n                } else {\n                    let pipelined_plan = PipelinedProject::from(plan.clone());\n                    collect_table_update(\n                        &[pipelined_plan],\n                        table_id,\n                        table_name.clone(),\n                        tx,\n                        update_type,\n                        rlb_pool,\n                    )?\n                };\n\n                let elapsed = start_time.elapsed();\n\n                let (ref _table_update, ref metrics) = result;\n                let query_metrics = metrics::get_query_metrics(\n                    table_name.clone(),\n                    &plan,\n                    metrics.rows_scanned as u64,\n                    elapsed.as_micros() as u64,\n                );\n\n                Ok((result.0, result.1, Some(query_metrics)))\n            })\n            .map_err(|err| DBError::WithSql {\n                sql: sql.into(),\n                error: Box::new(DBError::Other(err)),\n            })\n        })\n        .collect::<Result<Vec<_>, _>>()\n        .map(|table_updates_with_metrics| {\n            let n = table_updates_with_metrics.len();\n            let mut tables = Vec::with_capacity(n);\n            let mut aggregated_metrics = ExecutionMetrics::default();\n            let mut query_metrics_vec = Vec::new();\n\n            for (update, metrics, query_metrics) in table_updates_with_metrics {\n                tables.push(update);\n                aggregated_metrics.merge(metrics);\n                if let Some(qm) = query_metrics {\n                    query_metrics_vec.push(qm);\n                }\n            }\n            (ws_v1::DatabaseUpdate { tables }, aggregated_metrics, query_metrics_vec)\n        })\n}\n"
  },
  {
    "path": "crates/core/src/subscription/module_subscription_actor.rs",
    "content": "use super::execution_unit::QueryHash;\nuse super::metrics::QueryMetrics;\nuse super::module_subscription_manager::{\n    from_tx_offset, spawn_send_worker, BroadcastError, BroadcastQueue, Plan, SubscriptionGaugeStats,\n    SubscriptionManager, TransactionOffset,\n};\nuse super::query::compile_query_with_hashes;\nuse super::tx::DeltaTx;\nuse super::{collect_table_update, TableUpdateType};\nuse crate::client::messages::{\n    ProcedureResultMessage, SerializableMessage, SubscriptionData, SubscriptionError, SubscriptionMessage,\n    SubscriptionResult, SubscriptionRows, SubscriptionUpdateMessage, TransactionUpdateMessage,\n};\nuse crate::client::{ClientActorId, ClientConnectionSender, Protocol, WsVersion};\nuse crate::db::relational_db::{MutTx, RelationalDB, Tx};\nuse crate::error::DBError;\nuse crate::estimation::{check_row_limit, estimate_rows_scanned};\nuse crate::host::module_host::{DatabaseUpdate, EventStatus, ModuleEvent, RefInstance, WasmInstance};\nuse crate::host::{self, ModuleHost};\nuse crate::subscription::query::is_subscribe_to_all_tables;\nuse crate::subscription::row_list_builder_pool::{BsatnRowListBuilderPool, JsonRowListBuilderFakePool};\nuse crate::subscription::{collect_table_update_for_view, execute_plans};\nuse crate::util::prometheus_handle::IntGaugeExt;\nuse crate::worker_metrics::WORKER_METRICS;\nuse core::panic;\nuse parking_lot::RwLock;\nuse prometheus::{Histogram, HistogramTimer, IntCounter, IntGauge};\nuse scopeguard::ScopeGuard;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_client_api_messages::websocket::v2 as ws_v2;\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashSet};\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::execution_context::{Workload, WorkloadType};\nuse spacetimedb_datastore::locking_tx_datastore::datastore::TxMetrics;\nuse spacetimedb_datastore::locking_tx_datastore::{MutTxId, TxId};\nuse spacetimedb_datastore::traits::{IsolationLevel, TxData};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_execution::pipelined::{PipelinedProject, ViewProject};\nuse spacetimedb_expr::expr::CollectViews;\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_lib::{bsatn, identity::AuthCtx};\nuse spacetimedb_primitives::ArgId;\nuse spacetimedb_schema::def::RawModuleDefVersion;\nuse spacetimedb_table::static_assert_size;\nuse std::{\n    sync::{\n        atomic::{AtomicU8, Ordering},\n        Arc,\n    },\n    time::Instant,\n};\nuse tokio::sync::oneshot;\n\ntype Subscriptions = Arc<RwLock<SubscriptionManager>>;\n\n#[derive(Clone)]\npub struct ModuleSubscriptions {\n    relational_db: Arc<RelationalDB>,\n    /// If taking a lock (tx) on the db at the same time, ALWAYS lock the db first.\n    /// You will deadlock otherwise.\n    subscriptions: Subscriptions,\n    broadcast_queue: BroadcastQueue,\n    pub bsatn_rlb_pool: BsatnRowListBuilderPool,\n    stats: Arc<SubscriptionGauges>,\n    metrics: Arc<SubscriptionMetricsForWorkloads>,\n    module_def_version: Arc<AtomicU8>,\n}\n\n#[derive(Debug, Clone)]\nstruct SubscriptionGauges {\n    db_identity: Identity,\n    num_queries: IntGauge,\n    num_connections: IntGauge,\n    num_subscription_sets: IntGauge,\n    num_query_subscriptions: IntGauge,\n    num_legacy_subscriptions: IntGauge,\n}\n\nimpl SubscriptionGauges {\n    fn new(db_identity: &Identity) -> Self {\n        let num_queries = WORKER_METRICS.subscription_queries.with_label_values(db_identity);\n        let num_connections = DB_METRICS.subscription_connections.with_label_values(db_identity);\n        let num_subscription_sets = DB_METRICS.subscription_sets.with_label_values(db_identity);\n        let num_query_subscriptions = DB_METRICS.total_query_subscriptions.with_label_values(db_identity);\n        let num_legacy_subscriptions = DB_METRICS.num_legacy_subscriptions.with_label_values(db_identity);\n        Self {\n            db_identity: *db_identity,\n            num_queries,\n            num_connections,\n            num_subscription_sets,\n            num_query_subscriptions,\n            num_legacy_subscriptions,\n        }\n    }\n\n    // Clear the subscription gauges for this database.\n    fn unregister(&self) {\n        let _ = WORKER_METRICS\n            .subscription_queries\n            .remove_label_values(&self.db_identity);\n        let _ = DB_METRICS\n            .subscription_connections\n            .remove_label_values(&self.db_identity);\n        let _ = DB_METRICS.subscription_sets.remove_label_values(&self.db_identity);\n        let _ = DB_METRICS\n            .total_query_subscriptions\n            .remove_label_values(&self.db_identity);\n        let _ = DB_METRICS\n            .num_legacy_subscriptions\n            .remove_label_values(&self.db_identity);\n    }\n\n    fn report(&self, stats: &SubscriptionGaugeStats) {\n        self.num_queries.set(stats.num_queries as i64);\n        self.num_connections.set(stats.num_connections as i64);\n        self.num_subscription_sets.set(stats.num_subscription_sets as i64);\n        self.num_query_subscriptions.set(stats.num_query_subscriptions as i64);\n        self.num_legacy_subscriptions.set(stats.num_legacy_subscriptions as i64);\n    }\n}\n\nstruct SubscriptionMetricsForWorkloads {\n    update: SubscriptionMetrics,\n    subscribe: SubscriptionMetrics,\n    unsubscribe: SubscriptionMetrics,\n}\n\nimpl SubscriptionMetricsForWorkloads {\n    fn new(db: &Identity) -> Self {\n        Self {\n            update: SubscriptionMetrics::new(db, WorkloadType::Update),\n            subscribe: SubscriptionMetrics::new(db, WorkloadType::Subscribe),\n            unsubscribe: SubscriptionMetrics::new(db, WorkloadType::Unsubscribe),\n        }\n    }\n}\n\nstruct SubscriptionMetrics {\n    lock_waiters: IntGauge,\n    lock_wait_time: Histogram,\n    compilation_time: Histogram,\n    num_queries_subscribed: IntCounter,\n    num_new_queries_subscribed: IntCounter,\n    num_queries_evaluated: IntCounter,\n}\n\nstatic_assert_size!(SubscriptionMetrics, 48);\n\nimpl SubscriptionMetrics {\n    fn new(db: &Identity, workload: WorkloadType) -> Self {\n        let workload = &workload;\n        Self {\n            lock_waiters: DB_METRICS.subscription_lock_waiters.with_label_values(db, workload),\n            lock_wait_time: DB_METRICS.subscription_lock_wait_time.with_label_values(db, workload),\n            compilation_time: DB_METRICS.subscription_compile_time.with_label_values(db, workload),\n            num_queries_subscribed: DB_METRICS.num_queries_subscribed.with_label_values(db),\n            num_new_queries_subscribed: DB_METRICS.num_new_queries_subscribed.with_label_values(db),\n            num_queries_evaluated: DB_METRICS.num_queries_evaluated.with_label_values(db, workload),\n        }\n    }\n}\n\n/// Records subscription query metrics\nfn record_query_metrics(database_identity: &Identity, query_metrics: Vec<QueryMetrics>) {\n    for qm in query_metrics {\n        WORKER_METRICS\n            .subscription_rows_examined\n            .with_label_values(database_identity, &qm.scan_type, &qm.table_name, &qm.unindexed_columns)\n            .observe(qm.rows_scanned as f64);\n        WORKER_METRICS\n            .subscription_query_execution_time_micros\n            .with_label_values(database_identity, &qm.scan_type, &qm.table_name, &qm.unindexed_columns)\n            .observe(qm.execution_time_micros as f64);\n        WORKER_METRICS\n            .subscription_queries_total\n            .with_label_values(database_identity, &qm.scan_type, &qm.table_name, &qm.unindexed_columns)\n            .inc();\n    }\n}\n\n/// Inner result type of [`ModuleSubscriptions::commit_and_broadcast_event`].\npub type CommitAndBroadcastEventResult = Result<CommitAndBroadcastEventSuccess, WriteConflict>;\n\n/// `Ok` side of a [`CommitAndBroadcastEventResult`].\npub struct CommitAndBroadcastEventSuccess {\n    pub tx_offset: TransactionOffset,\n    pub event: Arc<ModuleEvent>,\n    pub metrics: ExecutionMetrics,\n}\n\n/// Commits `tx`\n/// and evaluates and broadcasts subscriptions updates.\npub(crate) fn commit_and_broadcast_event(\n    subs: &ModuleSubscriptions,\n    client: Option<Arc<ClientConnectionSender>>,\n    event: ModuleEvent,\n    tx: MutTxId,\n) -> CommitAndBroadcastEventSuccess {\n    match subs.commit_and_broadcast_event(client, event, tx).unwrap() {\n        Ok(res) => res,\n        Err(WriteConflict) => todo!(\"Write skew, you need to implement retries my man, T-dawg.\"),\n    }\n}\n\ntype AssertTxFn = Arc<dyn Fn(&Tx) + Send + Sync + 'static>;\ntype SubscriptionUpdate =\n    ws_v1::FormatSwitch<ws_v1::TableUpdate<ws_v1::BsatnFormat>, ws_v1::TableUpdate<ws_v1::JsonFormat>>;\ntype FullSubscriptionUpdate =\n    ws_v1::FormatSwitch<ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>, ws_v1::DatabaseUpdate<ws_v1::JsonFormat>>;\n\nfn query_rows_from_update(\n    update: ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>,\n    use_deletes: bool,\n) -> Result<ws_v2::QueryRows, DBError> {\n    let tables = update\n        .tables\n        .into_iter()\n        .flat_map(|table_update| {\n            let table_name = table_update.table_name;\n            table_update.updates.into_iter().map(move |single_update| {\n                let ws_v1::CompressableQueryUpdate::Uncompressed(query_update) = single_update else {\n                    return Err(DBError::Other(anyhow::anyhow!(\n                        \"unexpected compressed v1 update for v2 subscribe\"\n                    )));\n                };\n                let rows = if use_deletes {\n                    query_update.deletes\n                } else {\n                    query_update.inserts\n                };\n                Ok(ws_v2::SingleTableRows {\n                    table: table_name.clone(),\n                    rows,\n                })\n            })\n        })\n        .collect::<Result<Vec<_>, DBError>>()?;\n\n    Ok(ws_v2::QueryRows {\n        tables: tables.into_boxed_slice(),\n    })\n}\n\n/// A utility for sending an error message to a client and returning early\nmacro_rules! return_on_err {\n    ($expr:expr, $handler:expr, $metrics:expr) => {\n        match $expr {\n            Ok(val) => val,\n            Err(e) => {\n                // TODO: Handle errors sending messages.\n                let _ = $handler(e.to_string().into());\n                return Ok($metrics);\n            }\n        }\n    };\n}\n\n/// A utility for sending an error message to a client and returning early\nmacro_rules! return_on_err_with_sql {\n    ($expr:expr, $sql:expr, $handler:expr) => {\n        match $expr.map_err(|err| DBError::WithSql {\n            sql: $sql.into(),\n            error: Box::new(DBError::Other(err.into())),\n        }) {\n            Ok(val) => val,\n            Err(e) => {\n                // TODO: Handle errors sending messages.\n                let _ = $handler(e.to_string().into());\n                return Ok(None);\n            }\n        }\n    };\n}\n\n/// A utility for sending an error message to a client and returning early\nmacro_rules! return_on_err_with_sql_bool {\n    ($expr:expr, $sql:expr, $handler:expr) => {\n        match $expr.map_err(|err| DBError::WithSql {\n            sql: $sql.into(),\n            error: Box::new(DBError::Other(err.into())),\n        }) {\n            Ok(val) => val,\n            Err(e) => {\n                // TODO: Handle errors sending messages.\n                let _ = $handler(e.to_string().into());\n                return Ok((None, false));\n            }\n        }\n    };\n}\n\nimpl ModuleSubscriptions {\n    pub fn new(\n        relational_db: Arc<RelationalDB>,\n        subscriptions: Subscriptions,\n        broadcast_queue: BroadcastQueue,\n        bsatn_rlb_pool: BsatnRowListBuilderPool,\n    ) -> Self {\n        let db = &relational_db.database_identity();\n        let stats = Arc::new(SubscriptionGauges::new(db));\n        let metrics = Arc::new(SubscriptionMetricsForWorkloads::new(db));\n\n        Self {\n            relational_db,\n            subscriptions,\n            broadcast_queue,\n            stats,\n            metrics,\n            bsatn_rlb_pool,\n            module_def_version: Arc::new(AtomicU8::new(Self::encode_module_def_version(\n                RawModuleDefVersion::V9OrEarlier,\n            ))),\n        }\n    }\n\n    pub fn set_module_def_version(&self, version: RawModuleDefVersion) {\n        self.module_def_version\n            .store(Self::encode_module_def_version(version), Ordering::Release);\n    }\n\n    pub fn module_def_version(&self) -> RawModuleDefVersion {\n        Self::decode_module_def_version(self.module_def_version.load(Ordering::Acquire))\n    }\n\n    fn encode_module_def_version(version: RawModuleDefVersion) -> u8 {\n        match version {\n            RawModuleDefVersion::V9OrEarlier => 0,\n            RawModuleDefVersion::V10 => 1,\n        }\n    }\n\n    fn decode_module_def_version(version: u8) -> RawModuleDefVersion {\n        match version {\n            1 => RawModuleDefVersion::V10,\n            _ => RawModuleDefVersion::V9OrEarlier,\n        }\n    }\n\n    fn send_reducer_failure_result_v2(\n        &self,\n        client: Arc<ClientConnectionSender>,\n        event: &ModuleEvent,\n        request_id: u32,\n    ) {\n        let error = match &event.status {\n            EventStatus::FailedUser(err) => err.clone(),\n            EventStatus::FailedInternal(err) => err.clone(),\n            EventStatus::OutOfEnergy => \"reducer ran out of energy\".into(),\n            EventStatus::Committed(_) => {\n                tracing::warn!(\"Unexpected committed status in reducer failure branch\");\n                \"reducer failed\".into()\n            }\n        };\n        let result = match &event.status {\n            EventStatus::FailedUser(_) => ws_v2::ReducerOutcome::Err(\n                bsatn::to_vec(&error)\n                    .expect(\"failed to bsatn encode reducer error\")\n                    .into(),\n            ),\n            _ => ws_v2::ReducerOutcome::InternalError(error.into()),\n        };\n        let message = ws_v2::ReducerResult {\n            request_id,\n            timestamp: event.timestamp,\n            result,\n        };\n        let _ = self.broadcast_queue.send_client_message_v2(\n            client,\n            None, // This should arguably have a tx_offset, but it shouldn't matter as long as we wait for previous messages to be sent.\n            message,\n        );\n    }\n\n    /// Construct a new [`ModuleSubscriptions`] for use in testing,\n    /// creating a new [`tokio::runtime::Runtime`] to run its send worker.\n    pub fn for_test_new_runtime(db: Arc<RelationalDB>) -> (ModuleSubscriptions, tokio::runtime::Runtime) {\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n        (Self::for_test_enclosing_runtime(db), runtime)\n    }\n\n    /// Construct a new [`ModuleSubscriptions`] for use in testing,\n    /// running its send worker on the dynamically enclosing [`tokio::runtime::Runtime`]\n    pub fn for_test_enclosing_runtime(db: Arc<RelationalDB>) -> ModuleSubscriptions {\n        let send_worker_queue = spawn_send_worker(None);\n        ModuleSubscriptions::new(\n            db,\n            SubscriptionManager::for_test_without_metrics_arc_rwlock(),\n            send_worker_queue,\n            BsatnRowListBuilderPool::new(),\n        )\n    }\n\n    /// Returns the [`RelationalDB`] of this [`ModuleSubscriptions`].\n    ///\n    /// This is used by [`ModuleInfo`] and in turn by `InstanceCommon`.\n    pub fn relational_db(&self) -> &Arc<RelationalDB> {\n        &self.relational_db\n    }\n\n    // Recompute gauges to update metrics.\n    pub fn update_gauges(&self) {\n        let num_queries = self.subscriptions.read().calculate_gauge_stats();\n        self.stats.report(&num_queries);\n    }\n\n    // Remove the subscription gauges for this database.\n    // TODO: This should be called when the database is shut down.\n    pub fn remove_gauges(&self) {\n        self.stats.unregister();\n    }\n\n    /// Run auth and row limit checks for a new subscriber, then compute the initial query results.\n    fn evaluate_initial_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        query: Arc<Plan>,\n        tx: &TxId,\n        auth: &AuthCtx,\n        update_type: TableUpdateType,\n    ) -> Result<(SubscriptionUpdate, ExecutionMetrics), DBError> {\n        check_row_limit(\n            &[&query],\n            &self.relational_db,\n            tx,\n            |plan, tx| {\n                plan.plans_fragments()\n                    .map(|plan_fragment| estimate_rows_scanned(tx, plan_fragment.optimized_physical_plan()))\n                    .fold(0, |acc, rows_scanned| acc.saturating_add(rows_scanned))\n            },\n            auth,\n        )?;\n\n        let table_id = query.subscribed_table_id();\n        let table_name = query.subscribed_table_name().clone();\n\n        let plans = query\n            .plans_fragments()\n            .map(|fragment| fragment.optimized_physical_plan())\n            .cloned()\n            .map(|plan| plan.optimize(auth))\n            .collect::<Result<Vec<_>, _>>()?;\n\n        let view_info = plans\n            .first()\n            .and_then(|plan| plan.return_table())\n            .and_then(|schema| schema.view_info);\n\n        let num_cols = plans\n            .first()\n            .and_then(|plan| plan.return_table())\n            .map(|schema| schema.num_cols())\n            .unwrap_or_default();\n\n        let tx = DeltaTx::from(tx);\n\n        // TODO: See the comment on `collect_table_update_for_view`.\n        // The following view and non-view branches should be merged together,\n        // since the only difference between them is the row type that is returned.\n        Ok(match (sender.config.protocol, view_info) {\n            (Protocol::Binary, Some(view_info)) => {\n                let plans = plans\n                    .into_iter()\n                    .map(PipelinedProject::from)\n                    .map(|plan| ViewProject::new(plan, num_cols, view_info.num_private_cols()))\n                    .collect::<Vec<_>>();\n                collect_table_update_for_view(\n                    &plans,\n                    table_id,\n                    table_name.clone(),\n                    &tx,\n                    update_type,\n                    &self.bsatn_rlb_pool,\n                )\n                .map(|(table_update, metrics)| (ws_v1::FormatSwitch::Bsatn(table_update), metrics))\n            }\n            (Protocol::Binary, None) => {\n                let plans = plans.into_iter().map(PipelinedProject::from).collect::<Vec<_>>();\n                collect_table_update(\n                    &plans,\n                    table_id,\n                    table_name.clone(),\n                    &tx,\n                    update_type,\n                    &self.bsatn_rlb_pool,\n                )\n                .map(|(table_update, metrics)| (ws_v1::FormatSwitch::Bsatn(table_update), metrics))\n            }\n            (Protocol::Text, Some(view_info)) => {\n                let plans = plans\n                    .into_iter()\n                    .map(PipelinedProject::from)\n                    .map(|plan| ViewProject::new(plan, num_cols, view_info.num_private_cols()))\n                    .collect::<Vec<_>>();\n                collect_table_update_for_view(\n                    &plans,\n                    table_id,\n                    table_name,\n                    &tx,\n                    update_type,\n                    &JsonRowListBuilderFakePool,\n                )\n                .map(|(table_update, metrics)| (ws_v1::FormatSwitch::Json(table_update), metrics))\n            }\n            (Protocol::Text, None) => {\n                let plans = plans.into_iter().map(PipelinedProject::from).collect::<Vec<_>>();\n                collect_table_update(\n                    &plans,\n                    table_id,\n                    table_name,\n                    &tx,\n                    update_type,\n                    &JsonRowListBuilderFakePool,\n                )\n                .map(|(table_update, metrics)| (ws_v1::FormatSwitch::Json(table_update), metrics))\n            }\n        }?)\n    }\n\n    fn evaluate_queries(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        queries: &[Arc<Plan>],\n        tx: &TxId,\n        auth: &AuthCtx,\n        update_type: TableUpdateType,\n    ) -> Result<(FullSubscriptionUpdate, ExecutionMetrics), DBError> {\n        check_row_limit(\n            queries,\n            &self.relational_db,\n            tx,\n            |plan, tx| {\n                plan.plans_fragments()\n                    .map(|plan_fragment| estimate_rows_scanned(tx, plan_fragment.optimized_physical_plan()))\n                    .fold(0, |acc, rows_scanned| acc.saturating_add(rows_scanned))\n            },\n            auth,\n        )?;\n\n        let database_identity = self.relational_db.database_identity();\n        let tx = DeltaTx::from(tx);\n        let (update, metrics, query_metrics) = match sender.config.protocol {\n            Protocol::Binary => {\n                let (update, metrics, query_metrics) =\n                    execute_plans(auth, queries, &tx, update_type, &self.bsatn_rlb_pool)?;\n                (ws_v1::FormatSwitch::Bsatn(update), metrics, query_metrics)\n            }\n            Protocol::Text => {\n                let (update, metrics, query_metrics) =\n                    execute_plans(auth, queries, &tx, update_type, &JsonRowListBuilderFakePool)?;\n                (ws_v1::FormatSwitch::Json(update), metrics, query_metrics)\n            }\n        };\n\n        record_query_metrics(&database_identity, query_metrics);\n\n        Ok((update, metrics))\n    }\n\n    /// Add a subscription for a single query.\n    ///\n    /// - If `host` is `Some`, the request is forwarded to the module host. The host\n    ///   will execute the subscription logic on the module thread and call back\n    ///   into `add_single_subscription_with_instance` .\n    /// - If `host` is `None`, the subscription is executed directly without involving\n    ///   a module.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn add_single_subscription(\n        &self,\n        host: Option<&ModuleHost>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeSingle,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        match host {\n            Some(host) => {\n                host.call_view_add_single_subscription(sender, auth, request, timer)\n                    .await\n            }\n            None => self\n                .add_single_subscription_inner::<host::wasmtime::WasmtimeInstance>(\n                    None, sender, auth, request, timer, _assert,\n                )\n                .map(|(metrics, _)| metrics),\n        }\n    }\n\n    // Add a subscription for a single query with access to a module instance.\n    ///\n    /// The execution logic will always be called from the module's thread.\n    pub(crate) fn add_single_subscription_with_instance<I: WasmInstance>(\n        &self,\n        instance: &mut RefInstance<I>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeSingle,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        self.add_single_subscription_inner(Some(instance), sender, auth, request, timer, _assert)\n    }\n\n    fn add_single_subscription_inner<I: WasmInstance>(\n        &self,\n        instance: Option<&mut RefInstance<I>>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeSingle,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        // Send an error message to the client\n        let send_err_msg = |message| {\n            self.broadcast_queue.send_client_message_v1(\n                sender.clone(),\n                None,\n                SubscriptionMessage {\n                    request_id: Some(request.request_id),\n                    query_id: Some(request.query_id),\n                    timer: Some(timer),\n                    result: SubscriptionResult::Error(SubscriptionError {\n                        table_id: None,\n                        message,\n                    }),\n                },\n            )\n        };\n\n        let sql = request.query;\n        let hash = QueryHash::from_string(&sql, auth.caller(), false);\n        let hash_with_param = QueryHash::from_string(&sql, auth.caller(), true);\n\n        let (mut_tx, _) = self.begin_mut_tx(Workload::Subscribe);\n\n        let existing_query = {\n            let guard = self.subscriptions.read();\n            guard.query(&hash)\n        };\n\n        let query = return_on_err_with_sql_bool!(\n            existing_query.map(Ok).unwrap_or_else(|| compile_query_with_hashes(\n                &auth,\n                &*mut_tx,\n                &sql,\n                hash,\n                hash_with_param\n            )\n            .map(Arc::new)),\n            sql,\n            send_err_msg\n        );\n\n        // V1 clients must not subscribe to event tables.\n        // Old codegen doesn't understand event tables and would accumulate rows in the client cache.\n        if query.returns_event_table() {\n            let _ = send_err_msg(\n                \"Subscribing to event tables requires WebSocket v2. \\\n                 Please upgrade your client SDK and regenerate your module bindings.\"\n                    .into(),\n            );\n            return Ok((None, false));\n        }\n\n        let mut_tx = ScopeGuard::<MutTxId, _>::into_inner(mut_tx);\n\n        let (tx, tx_offset, trapped) =\n            self.materialize_views_and_downgrade_tx(mut_tx, instance, &query, auth.caller())?;\n\n        let (table_rows, metrics) = return_on_err_with_sql_bool!(\n            self.evaluate_initial_subscription(sender.clone(), query.clone(), &tx, &auth, TableUpdateType::Subscribe),\n            query.sql(),\n            send_err_msg\n        );\n\n        // It acquires the subscription lock after `eval`, allowing `add_subscription` to run concurrently.\n        // This also makes it possible for `broadcast_event` to get scheduled before the subsequent part here\n        // but that should not pose an issue.\n        let mut subscriptions = self.subscriptions.write();\n        subscriptions.add_subscription(sender.clone(), query.clone(), request.query_id)?;\n\n        #[cfg(test)]\n        if let Some(assert) = _assert {\n            assert(&tx);\n        }\n\n        // Note: to make sure transaction updates are consistent, we need to put this in the broadcast\n        // queue while we are still holding a read-lock on the database.\n\n        // That will avoid race conditions because reducers first grab a write lock on the db, then\n        // grab a read lock on the subscriptions.\n\n        // Holding a write lock on `self.subscriptions` would also be sufficient.\n        let _ = self.broadcast_queue.send_client_message_v1(\n            sender.clone(),\n            Some(tx_offset),\n            SubscriptionMessage {\n                request_id: Some(request.request_id),\n                query_id: Some(request.query_id),\n                timer: Some(timer),\n                result: SubscriptionResult::Subscribe(SubscriptionRows {\n                    table_id: query.subscribed_table_id(),\n                    table_name: query.subscribed_table_name().clone(),\n                    table_rows,\n                }),\n            },\n        );\n        Ok((Some(metrics), trapped))\n    }\n\n    /// Remove a subscription for a single query.\n    pub fn remove_single_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::Unsubscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        // Send an error message to the client\n        let send_err_msg = |message| {\n            self.broadcast_queue.send_client_message_v1(\n                sender.clone(),\n                None,\n                SubscriptionMessage {\n                    request_id: Some(request.request_id),\n                    query_id: Some(request.query_id),\n                    timer: Some(timer),\n                    result: SubscriptionResult::Error(SubscriptionError {\n                        table_id: None,\n                        message,\n                    }),\n                },\n            )\n        };\n\n        let mut subscriptions = self.subscriptions.write();\n\n        let queries = return_on_err!(\n            subscriptions.remove_subscription((sender.id.identity, sender.id.connection_id), request.query_id),\n            // Apparently we ignore errors sending messages.\n            send_err_msg,\n            None\n        );\n        // This is technically a bug, since this could be empty if the client has another duplicate subscription.\n        // This whole function should be removed soon, so I don't think we need to fix it.\n        let [query] = &*queries else {\n            // Apparently we ignore errors sending messages.\n            let _ = send_err_msg(\"Internal error\".into());\n            return Ok(None);\n        };\n\n        let (tx, tx_offset) = self.unsubscribe_views(query, auth.caller())?;\n\n        let (table_rows, metrics) = return_on_err_with_sql!(\n            self.evaluate_initial_subscription(sender.clone(), query.clone(), &tx, &auth, TableUpdateType::Unsubscribe),\n            query.sql(),\n            send_err_msg\n        );\n\n        // Note: to make sure transaction updates are consistent, we need to put this in the broadcast\n        // queue while we are still holding a read-lock on the database.\n\n        // That will avoid race conditions because reducers first grab a write lock on the db, then\n        // grab a read lock on the subscriptions.\n\n        // Holding a write lock on `self.subscriptions` would also be sufficient.\n        let _ = self.broadcast_queue.send_client_message_v1(\n            sender.clone(),\n            Some(tx_offset),\n            SubscriptionMessage {\n                request_id: Some(request.request_id),\n                query_id: Some(request.query_id),\n                timer: Some(timer),\n                result: SubscriptionResult::Unsubscribe(SubscriptionRows {\n                    table_id: query.subscribed_table_id(),\n                    table_name: query.subscribed_table_name().clone(),\n                    table_rows,\n                }),\n            },\n        );\n        Ok(Some(metrics))\n    }\n\n    /// Remove a client's subscription for a set of queries.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn remove_multi_subscription(\n        &self,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::UnsubscribeMulti,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        // Send an error message to the client\n        let send_err_msg = |message| {\n            self.broadcast_queue.send_client_message_v1(\n                sender.clone(),\n                None,\n                SubscriptionMessage {\n                    request_id: Some(request.request_id),\n                    query_id: Some(request.query_id),\n                    timer: Some(timer),\n                    result: SubscriptionResult::Error(SubscriptionError {\n                        table_id: None,\n                        message,\n                    }),\n                },\n            )\n        };\n\n        let subscription_metrics = &self.metrics.unsubscribe;\n\n        // Always lock the db before the subscription lock to avoid deadlocks.\n        let (mut_tx, _) = self.begin_mut_tx(Workload::Unsubscribe);\n\n        let removed_queries = {\n            let _compile_timer = subscription_metrics.compilation_time.start_timer();\n            let mut subscriptions = {\n                // How contended is the lock?\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n\n            return_on_err!(\n                subscriptions.remove_subscription((sender.id.identity, sender.id.connection_id), request.query_id),\n                send_err_msg,\n                None\n            )\n        };\n\n        let mut_tx = ScopeGuard::<MutTxId, _>::into_inner(mut_tx);\n        let (tx, tx_offset) = self.unsubscribe_views_and_downgrade_tx(mut_tx, &removed_queries, auth.caller())?;\n\n        let (update, metrics) = return_on_err!(\n            self.evaluate_queries(\n                sender.clone(),\n                &removed_queries,\n                &tx,\n                &auth,\n                TableUpdateType::Unsubscribe,\n            ),\n            send_err_msg,\n            None\n        );\n\n        // How many queries did we evaluate?\n        subscription_metrics\n            .num_queries_evaluated\n            .inc_by(removed_queries.len() as _);\n\n        // Note: to make sure transaction updates are consistent, we need to put this in the broadcast\n        // queue while we are still holding a read-lock on the database.\n\n        // That will avoid race conditions because reducers first grab a write lock on the db, then\n        // grab a read lock on the subscriptions.\n\n        // Holding a write lock on `self.subscriptions` would also be sufficient.\n        let _ = self.broadcast_queue.send_client_message_v1(\n            sender,\n            Some(tx_offset),\n            SubscriptionMessage {\n                request_id: Some(request.request_id),\n                query_id: Some(request.query_id),\n                timer: Some(timer),\n                result: SubscriptionResult::UnsubscribeMulti(SubscriptionData { data: update }),\n            },\n        );\n\n        Ok(Some(metrics))\n    }\n\n    pub async fn remove_v2_subscription(\n        &self,\n        host: Option<&ModuleHost>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Unsubscribe,\n        timer: Instant,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        match host {\n            Some(host) => {\n                host.call_view_remove_v2_subscription(sender, auth, request, timer)\n                    .await\n            }\n            None => self\n                .remove_v2_subscription_inner::<host::wasmtime::WasmtimeInstance>(\n                    None, sender, auth, request, timer, None,\n                )\n                .map(|(metrics, _)| metrics),\n        }\n    }\n\n    pub(crate) fn remove_v2_subscription_with_instance<I: WasmInstance>(\n        &self,\n        instance: &mut RefInstance<I>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Unsubscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        self.remove_v2_subscription_inner(Some(instance), sender, auth, request, timer, _assert)\n    }\n\n    fn remove_v2_subscription_inner<I: WasmInstance>(\n        &self,\n        _instance: Option<&mut RefInstance<I>>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Unsubscribe,\n        _timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        let send_err_msg = |message| {\n            let _ = self.broadcast_queue.send_client_message_v2(\n                sender.clone(),\n                None,\n                ws_v2::SubscriptionError {\n                    request_id: Some(request.request_id),\n                    query_set_id: request.query_set_id,\n                    error: message,\n                },\n            );\n        };\n\n        let subscription_metrics = &self.metrics.unsubscribe;\n\n        // Always lock the db before the subscription lock to avoid deadlocks.\n        let (mut_tx, _) = self.begin_mut_tx(Workload::Unsubscribe);\n\n        let removed_queries = {\n            let _compile_timer = subscription_metrics.compilation_time.start_timer();\n            let mut subscriptions = {\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n\n            return_on_err!(\n                subscriptions\n                    .remove_subscription_v2((sender.id.identity, sender.id.connection_id), request.query_set_id,),\n                send_err_msg,\n                (None, false)\n            )\n        };\n\n        let mut_tx = ScopeGuard::<MutTxId, _>::into_inner(mut_tx);\n        let (tx, tx_offset) = self.unsubscribe_views_and_downgrade_tx(mut_tx, &removed_queries, auth.caller())?;\n\n        let (rows, metrics) = if request.flags == ws_v2::UnsubscribeFlags::SendDroppedRows {\n            let (update, metrics) = return_on_err!(\n                self.evaluate_queries(\n                    sender.clone(),\n                    &removed_queries,\n                    &tx,\n                    &auth,\n                    TableUpdateType::Unsubscribe,\n                ),\n                send_err_msg,\n                (None, false)\n            );\n            subscription_metrics\n                .num_queries_evaluated\n                .inc_by(removed_queries.len() as _);\n\n            let query_rows = match update {\n                ws_v1::FormatSwitch::Bsatn(update) => query_rows_from_update(update, true)?,\n                ws_v1::FormatSwitch::Json(_) => {\n                    return Err(DBError::Other(anyhow::anyhow!(\n                        \"v2 unsubscribe requires binary protocol\"\n                    )))\n                }\n            };\n            (Some(query_rows), Some(metrics))\n        } else {\n            (None, None)\n        };\n\n        let _ = self.broadcast_queue.send_client_message_v2(\n            sender.clone(),\n            Some(tx_offset),\n            ws_v2::UnsubscribeApplied {\n                request_id: request.request_id,\n                query_set_id: request.query_set_id,\n                rows,\n            },\n        );\n\n        Ok((metrics, false))\n    }\n\n    /// Compiles the queries in a [Subscribe] or [SubscribeMulti] message.\n    ///\n    /// Note, we hash queries to avoid recompilation,\n    /// but we need to know if a query is parameterized in order to hash it correctly.\n    /// This requires that we type check which in turn requires that we start a tx.\n    ///\n    /// Unfortunately parsing with sqlparser is quite expensive,\n    /// so we'd like to avoid that cost while holding the tx lock,\n    /// especially since all we're trying to do is generate a hash.\n    ///\n    /// Instead we generate two hashes and outside of the tx lock.\n    /// If either one is currently tracked, we can avoid recompilation.\n    #[allow(clippy::type_complexity)]\n    fn compile_queries(\n        &self,\n        sender: Identity,\n        auth: AuthCtx,\n        queries: &[Box<str>],\n        num_queries: usize,\n        metrics: &SubscriptionMetrics,\n    ) -> Result<(Vec<Arc<Plan>>, AuthCtx, MutTxId, HistogramTimer), DBError> {\n        let mut subscribe_to_all_tables = false;\n        let mut plans = Vec::with_capacity(num_queries);\n        let mut query_hashes = Vec::with_capacity(num_queries);\n\n        for sql in queries {\n            let sql = sql.trim();\n            if is_subscribe_to_all_tables(sql) {\n                subscribe_to_all_tables = true;\n                continue;\n            }\n            let hash = QueryHash::from_string(sql, sender, false);\n            let hash_with_param = QueryHash::from_string(sql, sender, true);\n            query_hashes.push((sql, hash, hash_with_param));\n        }\n\n        // We always get the db lock before the subscription lock to avoid deadlocks.\n        let (mut_tx, _tx_offset) = self.begin_mut_tx(Workload::Subscribe);\n\n        let compile_timer = metrics.compilation_time.start_timer();\n\n        let guard = {\n            // How contended is the lock?\n            let _wait_guard = metrics.lock_waiters.inc_scope();\n            let _wait_timer = metrics.lock_wait_time.start_timer();\n            self.subscriptions.read()\n        };\n\n        if subscribe_to_all_tables {\n            plans.extend(\n                super::subscription::get_all(\n                    |relational_db, tx| relational_db.get_all_tables_mut(tx).map(|schemas| schemas.into_iter()),\n                    &self.relational_db,\n                    &*mut_tx,\n                    &auth,\n                )?\n                .into_iter()\n                .map(Arc::new),\n            );\n        }\n\n        let mut new_queries = 0;\n\n        for (sql, hash, hash_with_param) in query_hashes {\n            match guard.query(&hash) {\n                Some(unit) => {\n                    plans.push(unit);\n                }\n                _ => match guard.query(&hash_with_param) {\n                    Some(unit) => {\n                        plans.push(unit);\n                    }\n                    _ => {\n                        plans.push(Arc::new(\n                            compile_query_with_hashes(&auth, &*mut_tx, sql, hash, hash_with_param).map_err(|err| {\n                                DBError::WithSql {\n                                    error: Box::new(DBError::Other(err.into())),\n                                    sql: sql.into(),\n                                }\n                            })?,\n                        ));\n                        new_queries += 1;\n                    }\n                },\n            }\n        }\n\n        // How many queries in this subscription are not cached?\n        metrics.num_new_queries_subscribed.inc_by(new_queries);\n\n        Ok((plans, auth, ScopeGuard::<MutTxId, _>::into_inner(mut_tx), compile_timer))\n    }\n\n    /// Send a message to a client connection.\n    /// This will eventually be sent by the send-worker.\n    /// This takes a `TxId`, because this should be called while still holding a lock on the database.\n    pub fn send_client_message(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        message: impl Into<SerializableMessage>,\n        (_tx, tx_offset): (&TxId, TransactionOffset),\n    ) -> Result<(), BroadcastError> {\n        self.broadcast_queue\n            .send_client_message_v1(recipient, Some(tx_offset), message)\n    }\n\n    /// Like [`Self::send_client_message`],\n    /// but doesn't require a `TxId` because procedures don't hold a transaction open.\n    pub fn send_procedure_message(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        message: ProcedureResultMessage,\n        tx_offset: Option<TransactionOffset>,\n    ) -> Result<(), BroadcastError> {\n        self.broadcast_queue\n            .send_client_message_v1(recipient, tx_offset, message)\n    }\n\n    /// Like [`Self::send_procedure_message`], but for v2 protocol messages.\n    pub fn send_procedure_message_v2(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        message: ws_v2::ProcedureResult,\n        tx_offset: Option<TransactionOffset>,\n    ) -> Result<(), BroadcastError> {\n        self.broadcast_queue\n            .send_client_message_v2(recipient, tx_offset, message)\n    }\n\n    pub fn send_one_off_query_message_v2(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        message: ws_v2::OneOffQueryResult,\n        tx_offset: TransactionOffset,\n    ) -> Result<(), BroadcastError> {\n        self.broadcast_queue\n            .send_client_message_v2(recipient, Some(tx_offset), message)\n    }\n\n    /// Add a subscription consisting of multiple queries.\n    ///\n    /// Read more in [`Self::add_single_subscription`].\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn add_v2_subscription(\n        &self,\n        host: Option<&ModuleHost>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Subscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        match host {\n            Some(host) => host.call_view_add_v2_subscription(sender, auth, request, timer).await,\n            None => panic!(\"v2 subscriptions without a module host are not supported yet\"),\n        }\n    }\n    /// Add a subscription consisting of multiple queries.\n    ///\n    /// Read more in [`Self::add_single_subscription`].\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn add_multi_subscription(\n        &self,\n        host: Option<&ModuleHost>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeMulti,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<Option<ExecutionMetrics>, DBError> {\n        match host {\n            Some(host) => {\n                host.call_view_add_multi_subscription(sender, auth, request, timer)\n                    .await\n            }\n            None => self\n                .add_multi_subscription_inner::<host::wasmtime::WasmtimeInstance>(\n                    None, sender, auth, request, timer, _assert,\n                )\n                .map(|(metrics, _)| metrics),\n        }\n    }\n\n    /// Similar to [`Self::add_single_subscription_with_instance`],\n    /// but for multiple queries.\n    pub(crate) fn add_v2_subscription_with_instance<I: WasmInstance>(\n        &self,\n        instance: &mut RefInstance<I>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Subscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        self.add_v2_subscription_inner(Some(instance), sender, auth, request, timer, _assert)\n    }\n    /// Similar to [`Self::add_single_subscription_with_instance`],\n    /// but for multiple queries.\n    pub(crate) fn add_multi_subscription_with_instance<I: WasmInstance>(\n        &self,\n        instance: &mut RefInstance<I>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeMulti,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        self.add_multi_subscription_inner(Some(instance), sender, auth, request, timer, _assert)\n    }\n\n    fn add_v2_subscription_inner<I: WasmInstance>(\n        &self,\n        instance: Option<&mut RefInstance<I>>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v2::Subscribe,\n        _timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        // Send an error message to the client\n        // TODO: update for v2\n        let send_err_msg = |message| {\n            let _ = self.broadcast_queue.send_client_message_v2(\n                sender.clone(),\n                None,\n                ws_v2::SubscriptionError {\n                    request_id: Some(request.request_id),\n                    query_set_id: request.query_set_id,\n                    error: message,\n                },\n            );\n        };\n        let subscription_metrics = &self.metrics.subscribe;\n        let num_queries = request.query_strings.len();\n        subscription_metrics.num_queries_subscribed.inc_by(num_queries as _);\n\n        let (queries, auth, mut_tx, _compile_timer) = return_on_err!(\n            self.compile_queries(\n                sender.id.identity,\n                auth,\n                &request.query_strings,\n                num_queries,\n                subscription_metrics\n            ),\n            send_err_msg,\n            (None, false)\n        );\n        let (mut_tx, _) = self.guard_mut_tx(mut_tx, <_>::default());\n\n        // We minimize locking so that other clients can add subscriptions concurrently.\n        // We are protected from race conditions with broadcasts, because we have the db lock,\n        // an `commit_and_broadcast_event` grabs a read lock on `subscriptions` while it still has a\n        // write lock on the db.\n        let queries = {\n            let mut subscriptions = {\n                // How contended is the lock?\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n\n            subscriptions.add_subscription_v2(sender.clone(), queries, request.query_set_id)?\n        };\n\n        let mut_tx = ScopeGuard::<MutTxId, _>::into_inner(mut_tx);\n\n        let (tx, tx_offset, trapped) =\n            self.materialize_views_and_downgrade_tx(mut_tx, instance, &queries, auth.caller())?;\n\n        let (update, metrics) =\n            self.evaluate_queries(sender.clone(), &queries, &tx, &auth, TableUpdateType::Subscribe)?;\n\n        subscription_metrics.num_queries_evaluated.inc_by(queries.len() as _);\n\n        let ws_v2::QueryRows { tables } = match update {\n            ws_v1::FormatSwitch::Bsatn(update) => query_rows_from_update(update, false)?,\n            ws_v1::FormatSwitch::Json(_) => {\n                return Err(DBError::Other(anyhow::anyhow!(\n                    \"v2 subscriptions require binary protocol\"\n                )))\n            }\n        };\n\n        let _ = self.broadcast_queue.send_client_message_v2(\n            sender.clone(),\n            Some(tx_offset),\n            ws_v2::SubscribeApplied {\n                request_id: request.request_id,\n                query_set_id: request.query_set_id,\n                rows: ws_v2::QueryRows { tables },\n            },\n        );\n\n        Ok((Some(metrics), trapped))\n    }\n    fn add_multi_subscription_inner<I: WasmInstance>(\n        &self,\n        instance: Option<&mut RefInstance<I>>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        request: ws_v1::SubscribeMulti,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(Option<ExecutionMetrics>, bool), DBError> {\n        // Send an error message to the client\n        let send_err_msg = |message| {\n            let _ = self.broadcast_queue.send_client_message_v1(\n                sender.clone(),\n                None,\n                SubscriptionMessage {\n                    request_id: Some(request.request_id),\n                    query_id: Some(request.query_id),\n                    timer: Some(timer),\n                    result: SubscriptionResult::Error(SubscriptionError {\n                        table_id: None,\n                        message,\n                    }),\n                },\n            );\n        };\n\n        // How many queries make up this subscription?\n        let subscription_metrics = &self.metrics.subscribe;\n        let num_queries = request.query_strings.len();\n        subscription_metrics.num_queries_subscribed.inc_by(num_queries as _);\n\n        let (queries, auth, mut_tx, compile_timer) = return_on_err!(\n            self.compile_queries(\n                sender.id.identity,\n                auth,\n                &request.query_strings,\n                num_queries,\n                subscription_metrics\n            ),\n            send_err_msg,\n            (None, false)\n        );\n\n        // V1 clients must not subscribe to event tables.\n        // Old codegen doesn't understand event tables and would accumulate rows in the client cache.\n        if queries.iter().any(|q| q.returns_event_table()) {\n            send_err_msg(\n                \"Subscribing to event tables requires WebSocket v2. \\\n                 Please upgrade your client SDK and regenerate your module bindings.\"\n                    .into(),\n            );\n            return Ok((None, false));\n        }\n\n        let (mut_tx, _) = self.guard_mut_tx(mut_tx, <_>::default());\n\n        // We minimize locking so that other clients can add subscriptions concurrently.\n        // We are protected from race conditions with broadcasts, because we have the db lock,\n        // an `commit_and_broadcast_event` grabs a read lock on `subscriptions` while it still has a\n        // write lock on the db.\n        let queries = {\n            let mut subscriptions = {\n                // How contended is the lock?\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n\n            subscriptions.add_subscription_multi(sender.clone(), queries, request.query_id)?\n        };\n\n        // Record how long it took to compile the subscription\n        drop(compile_timer);\n\n        let mut_tx = ScopeGuard::<MutTxId, _>::into_inner(mut_tx);\n\n        let (tx, tx_offset, trapped) =\n            self.materialize_views_and_downgrade_tx(mut_tx, instance, &queries, auth.caller())?;\n\n        let Ok((update, metrics)) =\n            self.evaluate_queries(sender.clone(), &queries, &tx, &auth, TableUpdateType::Subscribe)\n        else {\n            // If we fail the query, we need to remove the subscription.\n            let mut subscriptions = {\n                // How contended is the lock?\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n            {\n                let _compile_timer = subscription_metrics.compilation_time.start_timer();\n                subscriptions.remove_subscription((sender.id.identity, sender.id.connection_id), request.query_id)?;\n            }\n\n            send_err_msg(\"Internal error evaluating queries\".into());\n            return Ok((None, trapped));\n        };\n\n        // How many queries did we actually evaluate?\n        subscription_metrics.num_queries_evaluated.inc_by(queries.len() as _);\n\n        #[cfg(test)]\n        if let Some(assert) = _assert {\n            assert(&tx);\n        }\n\n        // Note: to make sure transaction updates are consistent, we need to put this in the broadcast\n        // queue while we are still holding a read-lock on the database.\n\n        // That will avoid race conditions because reducers first grab a write lock on the db, then\n        // grab a read lock on the subscriptions.\n\n        // Holding a write lock on `self.subscriptions` would also be sufficient.\n        let _ = self.broadcast_queue.send_client_message_v1(\n            sender.clone(),\n            Some(tx_offset),\n            SubscriptionMessage {\n                request_id: Some(request.request_id),\n                query_id: Some(request.query_id),\n                timer: Some(timer),\n                result: SubscriptionResult::SubscribeMulti(SubscriptionData { data: update }),\n            },\n        );\n\n        Ok((Some(metrics), trapped))\n    }\n\n    // Add a subscriber to the module. NOTE: this function is blocking.\n    /// This is used for the legacy subscription API which uses a set of queries.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub async fn add_legacy_subscriber(\n        &self,\n        host: Option<&ModuleHost>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        subscription: ws_v1::Subscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<ExecutionMetrics, DBError> {\n        match host {\n            Some(host) => host\n                .call_view_add_legacy_subscription(sender, auth, subscription, timer)\n                .await\n                .map(|metrics| metrics.unwrap_or_default()),\n            None => self\n                .add_legacy_subscriber_inner::<host::wasmtime::WasmtimeInstance>(\n                    None,\n                    sender,\n                    auth,\n                    subscription,\n                    timer,\n                    _assert,\n                )\n                .map(|(metrics, _)| metrics),\n        }\n    }\n\n    /// Similar to [`Self::add_single_subscription_with_instance`],\n    /// but for the legacy subscription API which uses a set of queries.\n    pub(crate) fn add_legacy_subscriber_with_instance<I: WasmInstance>(\n        &self,\n        instance: &mut RefInstance<I>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        subscription: ws_v1::Subscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(ExecutionMetrics, bool), DBError> {\n        self.add_legacy_subscriber_inner(Some(instance), sender, auth, subscription, timer, _assert)\n    }\n\n    fn add_legacy_subscriber_inner<I: WasmInstance>(\n        &self,\n        instance: Option<&mut RefInstance<I>>,\n        sender: Arc<ClientConnectionSender>,\n        auth: AuthCtx,\n        subscription: ws_v1::Subscribe,\n        timer: Instant,\n        _assert: Option<AssertTxFn>,\n    ) -> Result<(ExecutionMetrics, bool), DBError> {\n        // How many queries make up this subscription?\n        let subscription_metrics = &self.metrics.subscribe;\n        let num_queries = subscription.query_strings.len();\n        subscription_metrics.num_queries_subscribed.inc_by(num_queries as _);\n\n        let (queries, auth, mut_tx, compile_timer) = self.compile_queries(\n            sender.id.identity,\n            auth,\n            &subscription.query_strings,\n            num_queries,\n            subscription_metrics,\n        )?;\n\n        let (tx, tx_offset, trapped) =\n            self.materialize_views_and_downgrade_tx(mut_tx, instance, &queries, auth.caller())?;\n\n        check_row_limit(\n            &queries,\n            &self.relational_db,\n            &tx,\n            |plan, tx| {\n                plan.plans_fragments()\n                    .map(|plan_fragment| estimate_rows_scanned(tx, plan_fragment.optimized_physical_plan()))\n                    .fold(0, |acc, rows_scanned| acc.saturating_add(rows_scanned))\n            },\n            &auth,\n        )?;\n\n        // Record how long it took to compile the subscription\n        drop(compile_timer);\n\n        let tx = DeltaTx::from(&*tx);\n        let (database_update, metrics, query_metrics) = match sender.config.protocol {\n            Protocol::Binary => execute_plans(&auth, &queries, &tx, TableUpdateType::Subscribe, &self.bsatn_rlb_pool)\n                .map(|(table_update, metrics, query_metrics)| {\n                (ws_v1::FormatSwitch::Bsatn(table_update), metrics, query_metrics)\n            })?,\n            Protocol::Text => execute_plans(\n                &auth,\n                &queries,\n                &tx,\n                TableUpdateType::Subscribe,\n                &JsonRowListBuilderFakePool,\n            )\n            .map(|(table_update, metrics, query_metrics)| {\n                (ws_v1::FormatSwitch::Json(table_update), metrics, query_metrics)\n            })?,\n        };\n\n        record_query_metrics(&self.relational_db.database_identity(), query_metrics);\n\n        // It acquires the subscription lock after `eval`, allowing `add_subscription` to run concurrently.\n        // This also makes it possible for `broadcast_event` to get scheduled before the subsequent part here\n        // but that should not pose an issue.\n        {\n            let _compile_timer = subscription_metrics.compilation_time.start_timer();\n\n            let mut subscriptions = {\n                // How contended is the lock?\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n\n            subscriptions.set_legacy_subscription(sender.clone(), queries);\n        }\n\n        #[cfg(test)]\n        if let Some(assert) = _assert {\n            assert(&tx);\n        }\n\n        // Note: to make sure transaction updates are consistent, we need to put this in the broadcast\n        // queue while we are still holding a read-lock on the database.\n\n        // That will avoid race conditions because reducers first grab a write lock on the db, then\n        // grab a read lock on the subscriptions.\n\n        // Holding a write lock on `self.subscriptions` would also be sufficient.\n        let _ = self.broadcast_queue.send_client_message_v1(\n            sender,\n            Some(tx_offset),\n            SubscriptionUpdateMessage {\n                database_update,\n                request_id: Some(subscription.request_id),\n                timer: Some(timer),\n            },\n        );\n\n        Ok((metrics, trapped))\n    }\n\n    pub fn remove_subscriber(&self, client_id: ClientActorId) {\n        let removed_queries = {\n            let mut subscriptions = self.subscriptions.write();\n            subscriptions.remove_all_subscriptions(&(client_id.identity, client_id.connection_id))\n        };\n\n        if removed_queries.is_empty() {\n            return;\n        }\n\n        // TODO(perf): Removing a subscriber is currently O(subscribed_queries).\n        // Instead we should maintain an index to make this O(subscribed_views).\n        if let Err(err) = self.unsubscribe_views(&removed_queries, client_id.identity) {\n            log::error!(\n                \"failed to unsubscribe views for disconnected client ({}, {}): {err}\",\n                client_id.identity,\n                client_id.connection_id\n            );\n        }\n    }\n\n    /// Rolls back `tx` and returns the offset as it was before `tx`.\n    pub(crate) fn rollback_mut_tx(stdb: &RelationalDB, tx: MutTxId) -> TxOffset {\n        let (tx_offset, tx_metrics, reducer) = stdb.rollback_mut_tx(tx);\n        stdb.report_tx_metrics(reducer, None, Some(tx_metrics), None);\n        tx_offset\n    }\n\n    /// Commit a transaction and broadcast its ModuleEvent to all interested subscribers.\n    ///\n    /// The returned [`ExecutionMetrics`] are reported in this method via `report_tx_metrics`.\n    /// They are returned for testing purposes but should not be reported separately.\n    pub fn commit_and_broadcast_event(\n        &self,\n        caller: Option<Arc<ClientConnectionSender>>,\n        mut event: ModuleEvent,\n        tx: MutTx,\n    ) -> Result<CommitAndBroadcastEventResult, DBError> {\n        let subscription_metrics = &self.metrics.update;\n\n        // Take a read lock on `subscriptions` before committing tx\n        // else it can result in subscriber receiving duplicate updates.\n        let subscriptions = {\n            // How contended is the lock?\n            let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n            let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n            self.subscriptions.read()\n        };\n\n        let stdb = &self.relational_db;\n        // Downgrade mutable tx.\n        // We'll later ensure tx is released/cleaned up once out of scope.\n        let (read_tx, tx_data, tx_metrics_mut) = match &mut event.status {\n            EventStatus::Committed(db_update) => {\n                let (tx_data, tx_metrics, read_tx) = stdb.commit_tx_downgrade(tx, Workload::Update);\n                *db_update = DatabaseUpdate::from_writes(&tx_data);\n                (read_tx, tx_data, tx_metrics)\n            }\n            EventStatus::FailedUser(_) | EventStatus::FailedInternal(_) | EventStatus::OutOfEnergy => {\n                // If the transaction failed, we need to rollback the mutable tx.\n                // We don't need to do any subscription updates in this case, so we will exit early.\n\n                let event = Arc::new(event);\n                let tx_offset = Self::rollback_mut_tx(stdb, tx);\n                if let Some(client) = caller {\n                    match client.config.version {\n                        WsVersion::V1 => {\n                            let message = TransactionUpdateMessage {\n                                event: Some(event.clone()),\n                                database_update: SubscriptionUpdateMessage::default_for_protocol(\n                                    client.config.protocol,\n                                    None,\n                                ),\n                            };\n\n                            let _ = self.broadcast_queue.send_client_message_v1(\n                                client,\n                                Some(from_tx_offset(tx_offset)),\n                                message,\n                            );\n                        }\n                        WsVersion::V2 => {\n                            if let Some(request_id) = event.request_id {\n                                self.send_reducer_failure_result_v2(client, &event, request_id);\n                            }\n                        }\n                    }\n                } else {\n                    log::trace!(\"Reducer failed but there is no client to send the failure to!\")\n                }\n                return Ok(Ok(CommitAndBroadcastEventSuccess {\n                    tx_offset: from_tx_offset(tx_offset),\n                    event,\n                    metrics: ExecutionMetrics::default(),\n                }));\n            }\n        };\n        let event = Arc::new(event);\n\n        // When we're done with this method, release the tx and report metrics.\n        let (extra_tx_offset_sender, extra_tx_offset) = oneshot::channel();\n        let (mut read_tx, tx_offset) = self.guard_tx(\n            read_tx,\n            GuardTxOptions::full(extra_tx_offset_sender, Some(tx_data.clone()), tx_metrics_mut),\n        );\n        // Create the delta transaction we'll use to eval updates against.\n        let delta_read_tx = DeltaTx::new(&read_tx, tx_data.as_ref(), subscriptions.index_ids_for_subscriptions());\n        let (update_metrics, failed_v2_subscriptions) = subscriptions.eval_updates_sequential(\n            (&delta_read_tx, tx_offset),\n            &self.bsatn_rlb_pool,\n            self.module_def_version(),\n            event.clone(),\n            caller,\n        );\n        drop(subscriptions);\n        // For subscriptions that had an error, we have send the client an error message,\n        // but we also need to remove the subscription so that we don't keep trying to send updates.\n        if !failed_v2_subscriptions.is_empty() {\n            let mut subscriptions = {\n                let _wait_guard = subscription_metrics.lock_waiters.inc_scope();\n                let _wait_timer = subscription_metrics.lock_wait_time.start_timer();\n                self.subscriptions.write()\n            };\n            for (client_id, query_set_id) in failed_v2_subscriptions {\n                if let Err(err) = subscriptions.remove_subscription_v2(client_id, query_set_id) {\n                    tracing::warn!(?client_id, ?query_set_id, \"failed to remove v2 subscription: {err}\");\n                }\n            }\n        }\n        read_tx.metrics.merge(update_metrics);\n        Ok(Ok(CommitAndBroadcastEventSuccess {\n            tx_offset: extra_tx_offset,\n            event,\n            metrics: update_metrics,\n        }))\n    }\n\n    /// The same as [`Self::unsubscribe_views_and_downgrade_tx`] but doesn't take a tx handle.\n    fn unsubscribe_views(\n        &self,\n        view_collector: &impl CollectViews,\n        sender: Identity,\n    ) -> Result<(TxGuard<impl FnOnce(TxId) + '_>, TransactionOffset), DBError> {\n        let tx = self\n            .relational_db\n            .begin_mut_tx(IsolationLevel::Serializable, Workload::Unsubscribe);\n        self.unsubscribe_views_and_downgrade_tx(tx, view_collector, sender)\n    }\n\n    /// Unsubscribes a caller from the views returned by the `view_collector`,\n    /// and subsequently downgrades to a read-only transaction.\n    ///\n    /// Unlike [`Self::materialize_views_and_downgrade_tx`] which populates the views' backing tables,\n    /// this method just decrements the subscriber count in `st_view_sub`.\n    /// Views without any subscribers are cleaned up async.\n    fn unsubscribe_views_and_downgrade_tx(\n        &self,\n        mut tx: MutTxId,\n        view_collector: &impl CollectViews,\n        sender: Identity,\n    ) -> Result<(TxGuard<impl FnOnce(TxId) + '_>, TransactionOffset), DBError> {\n        Self::_unsubscribe_views(&mut tx, view_collector, sender)?;\n        let (tx_data, tx_metrics_mut, tx) = self.relational_db.commit_tx_downgrade(tx, Workload::Subscribe);\n        let opts = GuardTxOptions::from_mut(tx_data, tx_metrics_mut);\n        Ok(self.guard_tx(tx, opts))\n    }\n\n    /// We unsubscribe from views by decrementing the subscriber count in `st_view_sub`.\n    /// Views without any subscribers are cleaned up async.\n    fn _unsubscribe_views(\n        tx: &mut MutTxId,\n        view_collector: &impl CollectViews,\n        sender: Identity,\n    ) -> Result<(), DBError> {\n        let mut view_ids = HashSet::new();\n        view_collector.collect_views(&mut view_ids);\n        for view_id in view_ids {\n            tx.unsubscribe_view(view_id, ArgId::SENTINEL, sender)?;\n        }\n        Ok(())\n    }\n\n    /// Materialize the views returned by the `view_collector`, if not already materialized,\n    /// and subsequently downgrade to a read-only transaction.\n    #[allow(clippy::type_complexity)]\n    fn materialize_views_and_downgrade_tx<I: WasmInstance>(\n        &self,\n        mut tx: MutTxId,\n        instance: Option<&mut RefInstance<'_, I>>,\n        view_collector: &impl CollectViews,\n        sender: Identity,\n    ) -> Result<(TxGuard<impl FnOnce(TxId) + '_>, TransactionOffset, bool), DBError> {\n        let mut trapped = false;\n        if let Some(instance) = instance {\n            (tx, trapped) = ModuleHost::materialize_views(tx, instance, view_collector, sender, Workload::Subscribe)?;\n        };\n\n        let (tx_data, tx_metrics_mut, tx) = self.relational_db.commit_tx_downgrade(tx, Workload::Subscribe);\n\n        let opts = GuardTxOptions::from_mut(tx_data, tx_metrics_mut);\n        let (a, b) = self.guard_tx(tx, opts);\n        Ok((a, b, trapped))\n    }\n\n    /// Helper that starts a new mutable transaction, and guards it using\n    /// [`Self::guard_mut_tx`] with the default configuration.\n    fn begin_mut_tx(&self, workload: Workload) -> (MutTxGuard<impl FnOnce(MutTxId) + '_>, TransactionOffset) {\n        self.guard_mut_tx(\n            self.relational_db.begin_mut_tx(IsolationLevel::Serializable, workload),\n            <_>::default(),\n        )\n    }\n\n    /// Helper wrapping a [`TxId`] in a scopegard, with a configurable drop fn.\n    ///\n    /// By default, `tx` is released when the returned [`ScopeGuard`] is dropped,\n    /// and reports the transaction metrics via [`RelationalDB::report_tx_metrics`].\n    /// The `tx_data` and `tx_metrics_mut` parameters are passed to the metrics\n    /// reporting method as-is; they can be used to report additional metrics\n    /// about a previous mutable transaction that was downgraded to `tx` after\n    /// committing.\n    ///\n    /// The method returns a [`ScopeGuard`] along with a [`TransactionOffset`].\n    /// When the transaction commits, its transaction offset is sent to the\n    /// latter (a [`oneshot::Receiver`]).\n    /// If another receiver of the transaction offset is needed, its sending\n    /// side can be passed in as `extra_tx_offset_sender`. It will be sent the\n    /// offset as well.\n    fn guard_tx(&self, tx: TxId, opts: GuardTxOptions) -> (TxGuard<impl FnOnce(TxId) + '_>, TransactionOffset) {\n        let (offset_tx, offset_rx) = oneshot::channel();\n        let guard = scopeguard::guard(tx, |tx| {\n            let (tx_offset, tx_metrics, reducer) = self.relational_db.release_tx(tx);\n            log::trace!(\"read tx released with offset {tx_offset}\");\n            let _ = offset_tx.send(tx_offset);\n            if let Some(extra) = opts.extra_tx_offset_sender {\n                let _ = extra.send(tx_offset);\n            }\n            self.relational_db\n                .report_tx_metrics(reducer, opts.tx_data, opts.tx_metrics_mut, Some(tx_metrics));\n        });\n        (guard, offset_rx)\n    }\n\n    /// The same as [`Self::guard_tx`] but for mutable transactions.\n    ///\n    /// By default, `tx` is committed when the returned [`ScopeGuard`] is dropped,\n    /// and reports the transaction metrics via [`RelationalDB::report_tx_metrics`].\n    fn guard_mut_tx(\n        &self,\n        tx: MutTxId,\n        opts: GuardTxOptions,\n    ) -> (MutTxGuard<impl FnOnce(MutTxId) + '_>, TransactionOffset) {\n        let (offset_tx, offset_rx) = oneshot::channel();\n        let guard = scopeguard::guard(tx, |tx| {\n            if let Ok(Some((tx_offset, tx_data, tx_metrics_mut, reducer))) = self.relational_db.commit_tx(tx) {\n                log::trace!(\"mutable tx committed with offset {tx_offset}\");\n                let _ = offset_tx.send(tx_offset);\n                if let Some(extra) = opts.extra_tx_offset_sender {\n                    let _ = extra.send(tx_offset);\n                }\n                self.relational_db\n                    .report_tx_metrics(reducer, Some(tx_data), Some(tx_metrics_mut), None);\n            }\n        });\n        (guard, offset_rx)\n    }\n}\n\n/// Extra parameters for [`ModuleSubscriptions::guard_tx`].\n#[derive(Default)]\nstruct GuardTxOptions {\n    /// Sender for an extra [`oneshot::Receiver`] for the transaction offset.\n    extra_tx_offset_sender: Option<oneshot::Sender<TxOffset>>,\n    /// [`TxData`] of a preceding mutable transaction.\n    tx_data: Option<Arc<TxData>>,\n    /// [`TxMetrics`] of a preceding mutable transaction.\n    tx_metrics_mut: Option<TxMetrics>,\n}\n\nimpl GuardTxOptions {\n    fn full(\n        extra_tx_offset_sender: oneshot::Sender<TxOffset>,\n        tx_data: Option<Arc<TxData>>,\n        tx_metrics_mut: TxMetrics,\n    ) -> Self {\n        Self {\n            extra_tx_offset_sender: extra_tx_offset_sender.into(),\n            tx_data,\n            tx_metrics_mut: tx_metrics_mut.into(),\n        }\n    }\n\n    fn from_mut(tx_data: Arc<TxData>, tx_metrics_mut: TxMetrics) -> Self {\n        Self {\n            extra_tx_offset_sender: None,\n            tx_data: Some(tx_data),\n            tx_metrics_mut: tx_metrics_mut.into(),\n        }\n    }\n}\n\npub struct WriteConflict;\n\n/// A [`ScopeGuard`] for [`TxId`]\ntype TxGuard<F> = ScopeGuard<TxId, F>;\n\n/// A [`ScopeGuard`] for [`MutTxId`]\ntype MutTxGuard<F> = ScopeGuard<MutTxId, F>;\n\n#[cfg(test)]\nmod tests {\n    use super::{AssertTxFn, ModuleSubscriptions};\n    use crate::client::messages::{\n        SerializableMessage, SubscriptionData, SubscriptionError, SubscriptionMessage, SubscriptionResult,\n        SubscriptionUpdateMessage, TransactionUpdateMessage,\n    };\n    use crate::client::{\n        ClientActorId, ClientConfig, ClientConnectionReceiver, ClientConnectionSender, ClientName, OutboundMessage,\n        Protocol, WsVersion,\n    };\n    use crate::db::relational_db::tests_utils::{\n        begin_mut_tx, begin_tx, create_view_for_test, insert, insert_into_view, with_auto_commit, with_read_only,\n        TestDB,\n    };\n    use crate::db::relational_db::{Persistence, RelationalDB, Txdata};\n    use crate::error::DBError;\n    use crate::host::module_host::{DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall};\n    use crate::sql::execute::run;\n    use crate::subscription::module_subscription_actor::commit_and_broadcast_event;\n    use crate::subscription::module_subscription_manager::{spawn_send_worker, SubscriptionManager};\n    use crate::subscription::query::compile_read_only_query;\n    use crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\n    use crate::subscription::TableUpdateType;\n    use core::fmt;\n    use futures::FutureExt;\n    use itertools::Itertools;\n    use pretty_assertions::assert_matches;\n    use spacetimedb_client_api_messages::energy::EnergyQuanta;\n    use spacetimedb_client_api_messages::websocket::{common::RowListLen as _, v1 as ws_v1, v2 as ws_v2};\n    use spacetimedb_commitlog::{commitlog, repo};\n    use spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\n    use spacetimedb_datastore::system_tables::{StRowLevelSecurityRow, ST_ROW_LEVEL_SECURITY_ID};\n    use spacetimedb_durability::{Durability, EmptyHistory, Transaction, TxOffset};\n    use spacetimedb_execution::dml::MutDatastore;\n    use spacetimedb_lib::bsatn::ToBsatn;\n    use spacetimedb_lib::db::auth::StAccess;\n    use spacetimedb_lib::identity::AuthCtx;\n    use spacetimedb_lib::metrics::ExecutionMetrics;\n    use spacetimedb_lib::{bsatn, ConnectionId, ProductType, ProductValue, Timestamp};\n    use spacetimedb_lib::{error::ResultTest, AlgebraicType, Identity};\n    use spacetimedb_primitives::TableId;\n    use spacetimedb_sats::product;\n    use std::future::Future;\n    use std::pin::pin;\n    use std::sync::RwLock;\n    use std::task::Poll;\n    use std::time::Instant;\n    use std::{sync::Arc, time::Duration};\n    use tokio::sync::mpsc::{self};\n    use tokio::sync::watch;\n\n    const TEST_MESSAGE_TIMEOUT: Duration = Duration::from_millis(20);\n\n    fn add_subscriber(db: Arc<RelationalDB>, sql: &str, assert: Option<AssertTxFn>) -> Result<(), DBError> {\n        // Create and enter a Tokio runtime to run the `ModuleSubscriptions`' background workers in parallel.\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n        let owner = Identity::from_byte_array([1; 32]);\n        let client = ClientActorId::for_test(Identity::ZERO);\n        let config = ClientConfig::for_test();\n        let sender = Arc::new(ClientConnectionSender::dummy(client, config, db.clone()));\n        let send_worker_queue = spawn_send_worker(None);\n        let module_subscriptions = ModuleSubscriptions::new(\n            db.clone(),\n            SubscriptionManager::for_test_without_metrics_arc_rwlock(),\n            send_worker_queue,\n            BsatnRowListBuilderPool::new(),\n        );\n        let auth = AuthCtx::new(owner, sender.auth.claims.identity);\n\n        let subscribe = ws_v1::Subscribe {\n            query_strings: [sql.into()].into(),\n            request_id: 0,\n        };\n        runtime.block_on(module_subscriptions.add_legacy_subscriber(\n            None,\n            sender,\n            auth,\n            subscribe,\n            Instant::now(),\n            assert,\n        ))?;\n        Ok(())\n    }\n\n    /// A [`Durability`] for which the durable offset is marked manually.\n    struct ManualDurability {\n        commitlog: Arc<RwLock<commitlog::Generic<repo::Memory, Txdata>>>,\n        durable_offset: watch::Sender<Option<TxOffset>>,\n    }\n\n    impl ManualDurability {\n        #[allow(unused)]\n        fn mark_durable_at(&self, offset: TxOffset) {\n            assert!(\n                self.committed_offset().is_some_and(|committed| committed >= offset),\n                \"given offset is not in the commitlog\"\n            );\n            self.durable_offset.send_modify(|val| {\n                val.replace(offset);\n            });\n        }\n\n        fn mark_durable(&self) {\n            if let Some(offset) = self.committed_offset() {\n                self.durable_offset.send_modify(|val| {\n                    val.replace(offset);\n                });\n            }\n        }\n\n        fn committed_offset(&self) -> Option<TxOffset> {\n            self.commitlog.read().unwrap().max_committed_offset()\n        }\n    }\n\n    impl Durability for ManualDurability {\n        type TxData = Txdata;\n\n        fn append_tx(&self, tx: Transaction<Self::TxData>) {\n            let mut commitlog = self.commitlog.write().unwrap();\n            commitlog.commit([tx]).expect(\"commit failed\");\n            commitlog.flush().expect(\"error flushing commitlog\");\n        }\n\n        fn durable_tx_offset(&self) -> spacetimedb_durability::DurableOffset {\n            self.durable_offset.subscribe().into()\n        }\n\n        fn close(&self) -> spacetimedb_durability::Close {\n            let mut durable = self.durable_offset.subscribe();\n            let commitlog = self.commitlog.clone();\n            async move {\n                let durable_offset = durable\n                    .wait_for(\n                        |offset| match offset.zip(commitlog.read().unwrap().max_committed_offset()) {\n                            Some((durable_offset, committed_offset)) => durable_offset >= committed_offset,\n                            None => false,\n                        },\n                    )\n                    .await\n                    .unwrap();\n                *durable_offset\n            }\n            .boxed()\n        }\n    }\n\n    impl Default for ManualDurability {\n        fn default() -> Self {\n            let (durable_offset, ..) = watch::channel(None);\n            Self {\n                commitlog: Arc::new(RwLock::new(\n                    commitlog::Generic::open(repo::Memory::unlimited(), <_>::default()).unwrap(),\n                )),\n                durable_offset,\n            }\n        }\n    }\n\n    /// An in-memory `RelationalDB` for testing\n    fn relational_db() -> anyhow::Result<Arc<RelationalDB>> {\n        let TestDB { db, .. } = TestDB::in_memory()?;\n        Ok(db)\n    }\n\n    /// An in-memory `RelationalDB` with `ManualDurability`.\n    fn relational_db_with_manual_durability(\n        rt: tokio::runtime::Handle,\n    ) -> anyhow::Result<(Arc<RelationalDB>, Arc<ManualDurability>)> {\n        let durability = Arc::new(ManualDurability::default());\n        let db = TestDB::open_db(\n            EmptyHistory::new(),\n            Some(Persistence {\n                durability: durability.clone(),\n                disk_size: Arc::new(|| Ok(<_>::default())),\n                snapshots: None,\n                runtime: rt,\n            }),\n            None,\n            0,\n        )?;\n\n        Ok((Arc::new(db), durability))\n    }\n\n    /// A [SubscribeSingle] message for testing\n    fn single_subscribe(sql: &str, query_id: u32) -> ws_v1::SubscribeSingle {\n        ws_v1::SubscribeSingle {\n            query: sql.into(),\n            request_id: 0,\n            query_id: ws_v1::QueryId::new(query_id),\n        }\n    }\n\n    /// A [SubscribeMulti] message for testing\n    fn multi_subscribe(query_strings: &[&'static str], query_id: u32) -> ws_v1::SubscribeMulti {\n        ws_v1::SubscribeMulti {\n            query_strings: query_strings\n                .iter()\n                .map(|sql| String::from(*sql).into_boxed_str())\n                .collect(),\n            request_id: 0,\n            query_id: ws_v1::QueryId::new(query_id),\n        }\n    }\n\n    /// A [SubscribeMulti] message for testing\n    fn multi_unsubscribe(query_id: u32) -> ws_v1::UnsubscribeMulti {\n        ws_v1::UnsubscribeMulti {\n            request_id: 0,\n            query_id: ws_v1::QueryId::new(query_id),\n        }\n    }\n\n    /// An [Unsubscribe] message for testing\n    fn single_unsubscribe(query_id: u32) -> ws_v1::Unsubscribe {\n        ws_v1::Unsubscribe {\n            request_id: 0,\n            query_id: ws_v1::QueryId::new(query_id),\n        }\n    }\n\n    /// A dummy [ModuleEvent] for testing\n    fn module_event() -> ModuleEvent {\n        ModuleEvent {\n            timestamp: Timestamp::now(),\n            caller_identity: Identity::ZERO,\n            caller_connection_id: None,\n            function_call: ModuleFunctionCall::default(),\n            status: EventStatus::Committed(DatabaseUpdate::default()),\n            reducer_return_value: None,\n            energy_quanta_used: EnergyQuanta { quanta: 0 },\n            host_execution_duration: Duration::from_millis(0),\n            request_id: None,\n            timer: None,\n        }\n    }\n\n    /// Create an [Identity] from a [u8]\n    fn identity_from_u8(v: u8) -> Identity {\n        Identity::from_byte_array([v; 32])\n    }\n\n    /// Create an [ConnectionId] from a [u8]\n    fn connection_id_from_u8(v: u8) -> ConnectionId {\n        ConnectionId::from_be_byte_array([v; 16])\n    }\n\n    /// Create an [ClientActorId] from a [u8].\n    /// Calls [identity_from_u8] internally with the passed value.\n    fn client_id_from_u8(v: u8) -> ClientActorId {\n        ClientActorId {\n            identity: identity_from_u8(v),\n            connection_id: connection_id_from_u8(v),\n            name: ClientName(v as u64),\n        }\n    }\n\n    fn client_connection_with_config(\n        client_id: ClientActorId,\n        db: &Arc<RelationalDB>,\n        config: ClientConfig,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        let (sender, receiver) = ClientConnectionSender::dummy_with_channel(client_id, config, db.clone());\n        (Arc::new(sender), receiver)\n    }\n\n    /// Instantiate a client connection with compression\n    fn client_connection_with_compression(\n        client_id: ClientActorId,\n        db: &Arc<RelationalDB>,\n        compression: ws_v1::Compression,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        client_connection_with_config(\n            client_id,\n            db,\n            ClientConfig {\n                protocol: Protocol::Binary,\n                version: WsVersion::V1,\n                compression,\n                tx_update_full: true,\n                confirmed_reads: false,\n            },\n        )\n    }\n\n    /// Instantiate a client connection\n    fn client_connection(\n        client_id: ClientActorId,\n        db: &Arc<RelationalDB>,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        client_connection_with_compression(client_id, db, ws_v1::Compression::None)\n    }\n\n    /// Instantiate a client connection with confirmed reads turned on or off.\n    fn client_connection_with_confirmed_reads(\n        client_id: ClientActorId,\n        db: &Arc<RelationalDB>,\n        confirmed_reads: bool,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        client_connection_with_config(\n            client_id,\n            db,\n            ClientConfig {\n                protocol: Protocol::Binary,\n                version: WsVersion::V1,\n                compression: ws_v1::Compression::None,\n                tx_update_full: true,\n                confirmed_reads,\n            },\n        )\n    }\n\n    /// Instantiate a v2 client connection with the default test settings.\n    fn v2_client_connection(\n        client_id: ClientActorId,\n        db: &Arc<RelationalDB>,\n    ) -> (Arc<ClientConnectionSender>, ClientConnectionReceiver) {\n        client_connection_with_config(\n            client_id,\n            db,\n            ClientConfig {\n                protocol: Protocol::Binary,\n                version: WsVersion::V2,\n                compression: ws_v1::Compression::None,\n                tx_update_full: true,\n                confirmed_reads: false,\n            },\n        )\n    }\n\n    /// Insert rules into the RLS system table\n    fn insert_rls_rules(\n        db: &RelationalDB,\n        table_ids: impl IntoIterator<Item = TableId>,\n        rules: impl IntoIterator<Item = &'static str>,\n    ) -> anyhow::Result<()> {\n        with_auto_commit(db, |tx| {\n            for (table_id, sql) in table_ids.into_iter().zip(rules) {\n                db.insert(\n                    tx,\n                    ST_ROW_LEVEL_SECURITY_ID,\n                    &ProductValue::from(StRowLevelSecurityRow {\n                        table_id,\n                        sql: sql.into(),\n                    })\n                    .to_bsatn_vec()?,\n                )?;\n            }\n            Ok(())\n        })\n    }\n\n    /// Subscribe to a query as a client\n    async fn subscribe_single(\n        subs: &ModuleSubscriptions,\n        auth: AuthCtx,\n        sql: &'static str,\n        sender: Arc<ClientConnectionSender>,\n        counter: &mut u32,\n    ) -> anyhow::Result<()> {\n        *counter += 1;\n        subs.add_single_subscription(\n            None,\n            sender,\n            auth,\n            single_subscribe(sql, *counter),\n            Instant::now(),\n            None,\n        )\n        .await?;\n        Ok(())\n    }\n\n    /// Subscribe to a set of queries as a client\n    async fn subscribe_multi(\n        subs: &ModuleSubscriptions,\n        auth: AuthCtx,\n        queries: &[&'static str],\n        sender: Arc<ClientConnectionSender>,\n        counter: &mut u32,\n    ) -> anyhow::Result<ExecutionMetrics> {\n        *counter += 1;\n        let metrics = subs\n            .add_multi_subscription(\n                None,\n                sender,\n                auth,\n                multi_subscribe(queries, *counter),\n                Instant::now(),\n                None,\n            )\n            .await?\n            .unwrap_or_default();\n        Ok(metrics)\n    }\n\n    /// Unsubscribe from a single query\n    fn unsubscribe_single(\n        subs: &ModuleSubscriptions,\n        auth: AuthCtx,\n        sender: Arc<ClientConnectionSender>,\n        query_id: u32,\n    ) -> anyhow::Result<()> {\n        subs.remove_single_subscription(sender, auth, single_unsubscribe(query_id), Instant::now())?;\n        Ok(())\n    }\n\n    /// Test that clients receive v2 unsubscribe applied without rows.\n    #[tokio::test]\n    async fn unsubscribe_v2_default_no_rows() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (sender, mut rx) = v2_client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let table_id = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n        with_auto_commit(&db, |tx| -> anyhow::Result<_> {\n            db.insert(tx, table_id, &bsatn::to_vec(&product![1_u8])?)?;\n            Ok(())\n        })?;\n\n        subs.add_v2_subscription_inner::<crate::host::wasmtime::WasmtimeInstance>(\n            None,\n            sender.clone(),\n            auth.clone(),\n            ws_v2::Subscribe {\n                request_id: 1,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                query_strings: [\"select * from t\".into()].into(),\n            },\n            Instant::now(),\n            None,\n        )?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::SubscribeApplied(_))) => {}\n            other => panic!(\"Expected v2 SubscribeApplied, got: {other:?}\"),\n        }\n\n        subs.remove_v2_subscription(\n            None,\n            sender.clone(),\n            auth,\n            ws_v2::Unsubscribe {\n                request_id: 2,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                flags: ws_v2::UnsubscribeFlags::Default,\n            },\n            Instant::now(),\n        )\n        .await?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::UnsubscribeApplied(msg))) => {\n                assert_eq!(msg.request_id, 2);\n                assert_eq!(msg.query_set_id, ws_v2::QuerySetId::new(1));\n                assert!(msg.rows.is_none());\n            }\n            other => panic!(\"Expected v2 UnsubscribeApplied, got: {other:?}\"),\n        }\n\n        Ok(())\n    }\n\n    /// Test that v2 unsubscribe can return dropped rows when requested.\n    #[tokio::test]\n    async fn unsubscribe_v2_send_dropped_rows() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (sender, mut rx) = v2_client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let table_id = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n        with_auto_commit(&db, |tx| -> anyhow::Result<_> {\n            db.insert(tx, table_id, &bsatn::to_vec(&product![1_u8])?)?;\n            Ok(())\n        })?;\n\n        subs.add_v2_subscription_inner::<crate::host::wasmtime::WasmtimeInstance>(\n            None,\n            sender.clone(),\n            auth.clone(),\n            ws_v2::Subscribe {\n                request_id: 1,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                query_strings: [\"select * from t\".into()].into(),\n            },\n            Instant::now(),\n            None,\n        )?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::SubscribeApplied(_))) => {}\n            other => panic!(\"Expected v2 SubscribeApplied, got: {other:?}\"),\n        }\n\n        subs.remove_v2_subscription(\n            None,\n            sender.clone(),\n            auth,\n            ws_v2::Unsubscribe {\n                request_id: 2,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                flags: ws_v2::UnsubscribeFlags::SendDroppedRows,\n            },\n            Instant::now(),\n        )\n        .await?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::UnsubscribeApplied(msg))) => {\n                assert_eq!(msg.request_id, 2);\n                assert_eq!(msg.query_set_id, ws_v2::QuerySetId::new(1));\n                let rows = msg.rows.expect(\"expected dropped rows\");\n                assert_eq!(rows.tables.len(), 1);\n                assert!(rows.tables[0].rows.len() > 0);\n            }\n            other => panic!(\"Expected v2 UnsubscribeApplied, got: {other:?}\"),\n        }\n\n        Ok(())\n    }\n\n    /// Test that updates are no longer delivered after v2 unsubscribe.\n    #[tokio::test]\n    async fn unsubscribe_v2_stops_updates() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (sender, mut rx) = v2_client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let table_id = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n\n        subs.add_v2_subscription_inner::<crate::host::wasmtime::WasmtimeInstance>(\n            None,\n            sender.clone(),\n            auth.clone(),\n            ws_v2::Subscribe {\n                request_id: 1,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                query_strings: [\"select * from t\".into()].into(),\n            },\n            Instant::now(),\n            None,\n        )?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::SubscribeApplied(_))) => {}\n            other => panic!(\"Expected v2 SubscribeApplied, got: {other:?}\"),\n        }\n\n        subs.remove_v2_subscription(\n            None,\n            sender.clone(),\n            auth,\n            ws_v2::Unsubscribe {\n                request_id: 2,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                flags: ws_v2::UnsubscribeFlags::Default,\n            },\n            Instant::now(),\n        )\n        .await?;\n\n        match rx.recv().await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::UnsubscribeApplied(_))) => {}\n            other => panic!(\"Expected v2 UnsubscribeApplied, got: {other:?}\"),\n        }\n\n        let _ = commit_tx(&db, &subs, [], [(table_id, product![2_u8])])?;\n\n        assert_no_outbound_message(rx.recv()).await;\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn unsubscribe_v2_other_clients_receive_sender_view_updates() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let id_for_a = identity_from_u8(1);\n        let client_id_for_a = client_id_from_u8(1);\n        let client_id_for_b = client_id_from_u8(2);\n\n        let (tx_for_a, mut rx_for_a) = v2_client_connection(client_id_for_a, &db);\n        let (tx_for_b, mut rx_for_b) = v2_client_connection(client_id_for_b, &db);\n\n        let auth_for_a = AuthCtx::new(db.owner_identity(), client_id_for_a.identity);\n        let auth_for_b = AuthCtx::new(db.owner_identity(), client_id_for_b.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let (_, view_table_id) = create_view_for_test(&db, \"my_view\", &[(\"counter\", AlgebraicType::U8)], false)?;\n\n        // Seed a sender-scoped row that only client A should observe through the view.\n        with_auto_commit(&db, |tx| -> anyhow::Result<_> {\n            insert_into_view(&db, tx, view_table_id, Some(id_for_a), product![7_u8])?;\n            Ok(())\n        })?;\n\n        subs.add_v2_subscription_inner::<crate::host::wasmtime::WasmtimeInstance>(\n            None,\n            tx_for_a.clone(),\n            auth_for_a,\n            ws_v2::Subscribe {\n                request_id: 1,\n                query_set_id: ws_v2::QuerySetId::new(1),\n                query_strings: [\"select * from my_view\".into()].into(),\n            },\n            Instant::now(),\n            None,\n        )?;\n        subs.add_v2_subscription_inner::<crate::host::wasmtime::WasmtimeInstance>(\n            None,\n            tx_for_b.clone(),\n            auth_for_b,\n            ws_v2::Subscribe {\n                request_id: 2,\n                query_set_id: ws_v2::QuerySetId::new(2),\n                query_strings: [\"select * from my_view\".into()].into(),\n            },\n            Instant::now(),\n            None,\n        )?;\n\n        assert!(matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::SubscribeApplied(_)))\n        ));\n        assert!(matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::SubscribeApplied(_)))\n        ));\n\n        // Dropping client B must not break client A's sender-view bookkeeping.\n        subs.remove_subscriber(client_id_for_b);\n\n        // Delete the backing row and verify the surviving subscriber still receives the view delta.\n        let _ = commit_tx(&db, &subs, [(view_table_id, product![id_for_a, 7_u8])], [])?;\n\n        let schema = ProductType::from([AlgebraicType::U8]);\n        assert_v2_tx_update_for_table(\n            rx_for_a.recv(),\n            ws_v2::QuerySetId::new(1),\n            \"my_view\",\n            &schema,\n            [],\n            [product![7_u8]],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Unsubscribe from a set of queries\n    fn unsubscribe_multi(\n        subs: &ModuleSubscriptions,\n        auth: AuthCtx,\n        sender: Arc<ClientConnectionSender>,\n        query_id: u32,\n    ) -> anyhow::Result<()> {\n        subs.remove_multi_subscription(sender, auth, multi_unsubscribe(query_id), Instant::now())?;\n        Ok(())\n    }\n\n    fn update_row_counts<I, D, BI, BD>(\n        rows_received: &mut HashMap<ProductValue, i32>,\n        schema: &ProductType,\n        inserts: I,\n        deletes: D,\n    ) where\n        I: IntoIterator<Item = BI>,\n        D: IntoIterator<Item = BD>,\n        BI: AsRef<[u8]>,\n        BD: AsRef<[u8]>,\n    {\n        for row in inserts.into_iter().map(|bytes| {\n            let mut bytes = bytes.as_ref();\n            ProductValue::decode(schema, &mut bytes).unwrap()\n        }) {\n            *rows_received.entry(row).or_insert(0) += 1;\n        }\n\n        for row in deletes.into_iter().map(|bytes| {\n            let mut bytes = bytes.as_ref();\n            ProductValue::decode(schema, &mut bytes).unwrap()\n        }) {\n            *rows_received.entry(row).or_insert(0) -= 1;\n        }\n    }\n\n    fn assert_received_rows(\n        rows_received: HashMap<ProductValue, i32>,\n        inserts: impl IntoIterator<Item = ProductValue>,\n        deletes: impl IntoIterator<Item = ProductValue>,\n    ) {\n        assert_eq!(\n            rows_received\n                .iter()\n                .filter(|(_, n)| n > &&0)\n                .map(|(row, _)| row)\n                .cloned()\n                .sorted()\n                .collect::<Vec<_>>(),\n            inserts.into_iter().sorted().collect::<Vec<_>>()\n        );\n        assert_eq!(\n            rows_received\n                .iter()\n                .filter(|(_, n)| n < &&0)\n                .map(|(row, _)| row)\n                .cloned()\n                .sorted()\n                .collect::<Vec<_>>(),\n            deletes.into_iter().sorted().collect::<Vec<_>>()\n        );\n    }\n\n    /// Pull a message from receiver and assert that it is a `TxUpdate` with the expected rows\n    async fn assert_tx_update_for_table(\n        rx: impl Future<Output = Option<OutboundMessage>>,\n        table_id: TableId,\n        schema: &ProductType,\n        inserts: impl IntoIterator<Item = ProductValue>,\n        deletes: impl IntoIterator<Item = ProductValue>,\n    ) {\n        match recv_outbound_message(rx, \"TxUpdate\").await {\n            Some(OutboundMessage::V1(SerializableMessage::TxUpdate(TransactionUpdateMessage {\n                database_update:\n                    SubscriptionUpdateMessage {\n                        database_update: ws_v1::FormatSwitch::Bsatn(ws_v1::DatabaseUpdate { mut tables }),\n                        ..\n                    },\n                ..\n            }))) => {\n                // Assume an update for only one table\n                assert_eq!(tables.len(), 1);\n\n                let table_update = tables.pop().unwrap();\n\n                // We should not be sending empty updates to clients\n                assert_ne!(table_update.num_rows, 0);\n\n                // It should be the table we expect\n                assert_eq!(table_update.table_id, table_id);\n\n                let mut rows_received: HashMap<ProductValue, i32> = HashMap::new();\n\n                for uncompressed in table_update.updates {\n                    let ws_v1::CompressableQueryUpdate::Uncompressed(table_update) = uncompressed else {\n                        panic!(\"expected an uncompressed table update\")\n                    };\n\n                    update_row_counts(&mut rows_received, schema, &table_update.inserts, &table_update.deletes);\n                }\n\n                assert_received_rows(rows_received, inserts, deletes);\n            }\n            Some(msg) => panic!(\"expected a TxUpdate, but got {msg:#?}\"),\n            None => panic!(\"The receiver closed due to an error\"),\n        }\n    }\n\n    /// Pull a message from receiver and assert that it is a v2 `TransactionUpdate`\n    /// with the expected rows for a single table in a single query set.\n    async fn assert_v2_tx_update_for_table(\n        rx: impl Future<Output = Option<OutboundMessage>>,\n        query_set_id: ws_v2::QuerySetId,\n        table_name: &str,\n        schema: &ProductType,\n        inserts: impl IntoIterator<Item = ProductValue>,\n        deletes: impl IntoIterator<Item = ProductValue>,\n    ) {\n        match recv_outbound_message(rx, \"v2 TransactionUpdate\").await {\n            Some(OutboundMessage::V2(ws_v2::ServerMessage::TransactionUpdate(update))) => {\n                assert_eq!(update.query_sets.len(), 1);\n                let query_set = &update.query_sets[0];\n                assert_eq!(query_set.query_set_id, query_set_id);\n                assert_eq!(query_set.tables.len(), 1);\n\n                let table_update = &query_set.tables[0];\n                assert_eq!(table_update.table_name.as_ref(), table_name);\n\n                let mut rows_received: HashMap<ProductValue, i32> = HashMap::new();\n\n                for rows in table_update.rows.iter() {\n                    let ws_v2::TableUpdateRows::PersistentTable(rows) = rows else {\n                        panic!(\"expected a persistent-table update\")\n                    };\n\n                    update_row_counts(&mut rows_received, schema, &rows.inserts, &rows.deletes);\n                }\n\n                assert_received_rows(rows_received, inserts, deletes);\n            }\n            Some(msg) => panic!(\"expected a v2 TransactionUpdate, but got {msg:#?}\"),\n            None => panic!(\"The receiver closed due to an error\"),\n        }\n    }\n\n    async fn recv_outbound_message(\n        rx: impl Future<Output = Option<OutboundMessage>>,\n        expected: &str,\n    ) -> Option<OutboundMessage> {\n        tokio::time::timeout(TEST_MESSAGE_TIMEOUT, rx)\n            .await\n            .unwrap_or_else(|_| panic!(\"timed out waiting for {expected}\"))\n    }\n\n    async fn assert_no_outbound_message(rx: impl Future<Output = Option<OutboundMessage>>) {\n        match tokio::time::timeout(TEST_MESSAGE_TIMEOUT, rx).await {\n            Err(_) => {}\n            Ok(Some(msg)) => panic!(\"expected no message, got {msg:#?}\"),\n            Ok(None) => panic!(\"the receiver closed due to an error\"),\n        }\n    }\n\n    /// Assert that the future `f` completes only after `durability` is marked\n    /// durable.\n    ///\n    /// Namely:\n    ///\n    /// - assert that polling `f` once returns [`Poll::Pending`]\n    /// - call `durability.mark_durable()`\n    /// - assert that polling `f` returns [`Poll::Ready`].\n    ///\n    async fn assert_after_durable(durability: &ManualDurability, f: impl Future<Output: fmt::Debug>) {\n        let mut g = pin!(f);\n        assert_matches!(futures::poll!(&mut g), Poll::Pending);\n        durability.mark_durable();\n        assert_matches!(futures::poll!(g), Poll::Ready(_));\n    }\n\n    /// Commit a set of row updates and broadcast to subscribers\n    fn commit_tx(\n        db: &RelationalDB,\n        subs: &ModuleSubscriptions,\n        deletes: impl IntoIterator<Item = (TableId, ProductValue)>,\n        inserts: impl IntoIterator<Item = (TableId, ProductValue)>,\n    ) -> anyhow::Result<ExecutionMetrics> {\n        let mut tx = begin_mut_tx(db);\n        for (table_id, row) in deletes {\n            tx.delete_product_value(table_id, &row)?;\n        }\n        for (table_id, row) in inserts {\n            db.insert(&mut tx, table_id, &bsatn::to_vec(&row)?)?;\n        }\n\n        let success = commit_and_broadcast_event(subs, None, module_event(), tx);\n        Ok(success.metrics)\n    }\n\n    #[test]\n    fn test_subscribe_metrics() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (sender, _) = client_connection(client_id, &db);\n\n        let (subs, _runtime) = ModuleSubscriptions::for_test_new_runtime(db.clone());\n\n        // Create a table `t` with index on `id`\n        let table_id = db.create_table_for_test(\"t\", &[(\"id\", AlgebraicType::U64)], &[0.into()])?;\n        with_auto_commit(&db, |tx| -> anyhow::Result<_> {\n            db.insert(tx, table_id, &bsatn::to_vec(&product![1_u64])?)?;\n            Ok(())\n        })?;\n\n        let auth = AuthCtx::for_testing();\n        let sql = \"select * from t where id = 1\";\n        let tx = begin_tx(&db);\n        let plan = compile_read_only_query(&auth, &tx, sql)?;\n        let plan = Arc::new(plan);\n\n        let (_, metrics) = subs.evaluate_queries(sender, &[plan], &tx, &auth, TableUpdateType::Subscribe)?;\n\n        // We only probe the index once\n        assert_eq!(metrics.index_seeks, 1);\n        // We scan a single u64 when serializing the result\n        assert_eq!(metrics.bytes_scanned, 8);\n        // Subscriptions are read-only\n        assert_eq!(metrics.bytes_written, 0);\n        // Bytes scanned and bytes sent will always be the same for an initial subscription,\n        // because a subscription is initiated by a single client.\n        assert_eq!(metrics.bytes_sent_to_clients, 8);\n\n        // Note, rows scanned may be greater than one.\n        // It depends on the number of operators used to answer the query.\n        assert!(metrics.rows_scanned > 0);\n        Ok(())\n    }\n\n    fn check_subscription_err(sql: &str, result: Option<OutboundMessage>) {\n        if let Some(OutboundMessage::V1(SerializableMessage::Subscription(SubscriptionMessage {\n            result: SubscriptionResult::Error(SubscriptionError { message, .. }),\n            ..\n        }))) = result\n        {\n            assert!(\n                message.contains(sql),\n                \"Expected error message to contain the SQL query: {sql}, but got: {message}\",\n            );\n            return;\n        }\n        panic!(\"Expected a subscription error message, but got: {result:?}\");\n    }\n\n    /// Test that clients receive error messages on subscribe\n    #[tokio::test]\n    async fn subscribe_single_error() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n\n        // Subscribe to an invalid query (r is not in scope)\n        let sql = \"select r.* from t\";\n        subscribe_single(&subs, auth, sql, tx, &mut 0).await?;\n\n        check_subscription_err(sql, rx.recv().await);\n\n        Ok(())\n    }\n\n    /// Test that clients receive error messages on subscribe\n    #[tokio::test]\n    async fn subscribe_multi_error() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n\n        // Subscribe to an invalid query (r is not in scope)\n        let sql = \"select r.* from t\";\n        subscribe_multi(&subs, auth, &[sql], tx, &mut 0).await?;\n\n        check_subscription_err(sql, rx.recv().await);\n\n        Ok(())\n    }\n\n    /// Test that clients receive error messages on unsubscribe\n    #[tokio::test]\n    async fn unsubscribe_single_error() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        // Create a table `t` with an index on `id`\n        let table_id = db.create_table_for_test(\"t\", &[(\"id\", AlgebraicType::U8)], &[0.into()])?;\n        let index_id = with_read_only(&db, |tx| {\n            db.schema_for_table(&*tx, table_id).map(|schema| {\n                schema\n                    .indexes\n                    .first()\n                    .map(|index_schema| index_schema.index_id)\n                    .unwrap()\n            })\n        })?;\n\n        let mut query_id = 0;\n\n        // Subscribe to `t`\n        let sql = \"select * from t where id = 1\";\n        subscribe_single(&subs, auth.clone(), sql, tx.clone(), &mut query_id).await?;\n\n        // The initial subscription should succeed\n        assert!(matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::Subscribe(..),\n                    ..\n                }\n            )))\n        ));\n\n        // Remove the index from `id`\n        with_auto_commit(&db, |tx| db.drop_index(tx, index_id))?;\n\n        // Unsubscribe from `t`\n        unsubscribe_single(&subs, auth, tx, query_id)?;\n\n        // Why does the unsubscribe fail?\n        // This relies on some knowledge of the underlying implementation.\n        // Specifically that we do not recompile queries on unsubscribe.\n        // We execute the cached plan which in this case is an index scan.\n        // The index no longer exists, and therefore it fails.\n        check_subscription_err(sql, rx.recv().await);\n\n        Ok(())\n    }\n\n    /// Test that clients receive error messages on unsubscribe\n    ///\n    /// Needs a multi-threaded tokio runtime so that the module subscription worker can run in parallel.\n    #[tokio::test(flavor = \"multi_thread\", worker_threads = 1)]\n    async fn unsubscribe_multi_error() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        // Create a table `t` with an index on `id`\n        let table_id = db.create_table_for_test(\"t\", &[(\"id\", AlgebraicType::U8)], &[0.into()])?;\n        let index_id = with_read_only(&db, |tx| {\n            db.schema_for_table(&*tx, table_id).map(|schema| {\n                schema\n                    .indexes\n                    .first()\n                    .map(|index_schema| index_schema.index_id)\n                    .unwrap()\n            })\n        })?;\n\n        commit_tx(&db, &subs, [], [(table_id, product![0_u8])])?;\n\n        let mut query_id = 0;\n\n        // Subscribe to `t`\n        let sql = \"select * from t where id = 1\";\n        subscribe_multi(&subs, auth.clone(), &[sql], tx.clone(), &mut query_id).await?;\n\n        // The initial subscription should succeed\n        assert!(matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(..),\n                    ..\n                }\n            )))\n        ));\n\n        // Remove the index from `id`\n        with_auto_commit(&db, |tx| db.drop_index(tx, index_id))?;\n\n        // Unsubscribe from `t`\n        unsubscribe_multi(&subs, auth, tx, query_id)?;\n\n        // Why does the unsubscribe fail?\n        // This relies on some knowledge of the underlying implementation.\n        // Specifically that we do not recompile queries on unsubscribe.\n        // We execute the cached plan which in this case is an index scan.\n        // The index no longer exists, and therefore it fails.\n        check_subscription_err(sql, rx.recv().await);\n\n        Ok(())\n    }\n\n    /// Test that clients receive error messages on tx updates\n    #[tokio::test]\n    async fn tx_update_error() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        // Create two tables `t` and `s` with indexes on their `id` columns\n        let t_id = db.create_table_for_test(\"t\", &[(\"id\", AlgebraicType::U8)], &[0.into()])?;\n        let s_id = db.create_table_for_test(\"s\", &[(\"id\", AlgebraicType::U8)], &[0.into()])?;\n        let index_id = with_read_only(&db, |tx| {\n            db.schema_for_table(&*tx, s_id).map(|schema| {\n                schema\n                    .indexes\n                    .first()\n                    .map(|index_schema| index_schema.index_id)\n                    .unwrap()\n            })\n        })?;\n        let sql = \"select t.* from t join s on t.id = s.id\";\n        subscribe_single(&subs, auth, sql, tx, &mut 0).await?;\n\n        // The initial subscription should succeed\n        assert!(matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::Subscribe(..),\n                    ..\n                }\n            )))\n        ));\n\n        // Remove the index from `s`\n        with_auto_commit(&db, |tx| db.drop_index(tx, index_id))?;\n\n        // Start a new transaction and insert a new row into `t`\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, t_id, &bsatn::to_vec(&product![2_u8])?)?;\n\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n\n        // Why does the update fail?\n        // This relies on some knowledge of the underlying implementation.\n        // Specifically, plans are cached on the initial subscribe.\n        // Hence we execute a cached plan which happens to be an index join.\n        // We've removed the index on `s`, and therefore it fails.\n        check_subscription_err(sql, rx.recv().await);\n\n        Ok(())\n    }\n\n    /// Test that two clients can subscribe to a parameterized query and get the correct rows.\n    #[tokio::test]\n    async fn test_parameterized_subscription() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        // Create identities for two different clients\n        let id_for_a = identity_from_u8(1);\n        let id_for_b = identity_from_u8(2);\n\n        let client_id_for_a = client_id_from_u8(1);\n        let client_id_for_b = client_id_from_u8(2);\n\n        // Establish a connection for each client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_for_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_for_b, &db);\n\n        let auth_for_a = AuthCtx::new(db.owner_identity(), client_id_for_a.identity);\n        let auth_for_b = AuthCtx::new(db.owner_identity(), client_id_for_b.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let schema = [(\"identity\", AlgebraicType::identity())];\n\n        let table_id = db.create_table_for_test(\"t\", &schema, &[])?;\n\n        let mut query_ids = 0;\n\n        // Have each client subscribe to the same parameterized query.\n        // Each client should receive different rows.\n        subscribe_multi(\n            &subs,\n            auth_for_a,\n            &[\"select * from t where identity = :sender\"],\n            tx_for_a,\n            &mut query_ids,\n        )\n        .await?;\n        subscribe_multi(\n            &subs,\n            auth_for_b,\n            &[\"select * from t where identity = :sender\"],\n            tx_for_b,\n            &mut query_ids,\n        )\n        .await?;\n\n        // Wait for both subscriptions\n        assert!(matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n        assert!(matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n\n        // Insert two identities - one for each caller - into the table\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, table_id, &bsatn::to_vec(&product![id_for_a])?)?;\n        db.insert(&mut tx, table_id, &bsatn::to_vec(&product![id_for_b])?)?;\n\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n\n        let schema = ProductType::from([AlgebraicType::identity()]);\n\n        // Both clients should only receive their identities and not the other's.\n        assert_tx_update_for_table(rx_for_a.recv(), table_id, &schema, [product![id_for_a]], []).await;\n        assert_tx_update_for_table(rx_for_b.recv(), table_id, &schema, [product![id_for_b]], []).await;\n        Ok(())\n    }\n\n    /// Test that two clients can subscribe to a table with RLS rules and get the correct rows\n    #[tokio::test]\n    async fn test_rls_subscription() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        // Create identities for two different clients\n        let id_for_a = identity_from_u8(1);\n        let id_for_b = identity_from_u8(2);\n\n        let client_id_for_a = client_id_from_u8(1);\n        let client_id_for_b = client_id_from_u8(2);\n\n        // Establish a connection for each client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_for_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_for_b, &db);\n\n        let auth_for_a = AuthCtx::new(db.owner_identity(), client_id_for_a.identity);\n        let auth_for_b = AuthCtx::new(db.owner_identity(), client_id_for_b.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let schema = [(\"id\", AlgebraicType::identity())];\n\n        let u_id = db.create_table_for_test(\"u\", &schema, &[0.into()])?;\n        let v_id = db.create_table_for_test(\"v\", &schema, &[0.into()])?;\n        let w_id = db.create_table_for_test(\"w\", &schema, &[0.into()])?;\n\n        insert_rls_rules(\n            &db,\n            [u_id, v_id, w_id, w_id],\n            [\n                \"select * from u where id = :sender\",\n                \"select * from v where id = :sender\",\n                \"select w.* from u join w on u.id = w.id\",\n                \"select w.* from v join w on v.id = w.id\",\n            ],\n        )?;\n\n        let mut query_ids = 0;\n\n        // Have each client subscribe to `w`.\n        // Because `w` is gated using parameterized RLS rules,\n        // each client should receive different rows.\n        subscribe_multi(&subs, auth_for_a, &[\"select * from w\"], tx_for_a, &mut query_ids).await?;\n        subscribe_multi(&subs, auth_for_b, &[\"select * from w\"], tx_for_b, &mut query_ids).await?;\n\n        // Wait for both subscriptions\n        assert!(matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n        assert!(matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n\n        // Insert a row into `u` for client \"a\".\n        // Insert a row into `v` for client \"b\".\n        // Insert a row into `w` for both.\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, u_id, &bsatn::to_vec(&product![id_for_a])?)?;\n        db.insert(&mut tx, v_id, &bsatn::to_vec(&product![id_for_b])?)?;\n        db.insert(&mut tx, w_id, &bsatn::to_vec(&product![id_for_a])?)?;\n        db.insert(&mut tx, w_id, &bsatn::to_vec(&product![id_for_b])?)?;\n\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n\n        let schema = ProductType::from([AlgebraicType::identity()]);\n\n        // Both clients should only receive their identities and not the other's.\n        assert_tx_update_for_table(rx_for_a.recv(), w_id, &schema, [product![id_for_a]], []).await;\n        assert_tx_update_for_table(rx_for_b.recv(), w_id, &schema, [product![id_for_b]], []).await;\n        Ok(())\n    }\n\n    /// Test that a client and the database owner can subscribe to the same query\n    #[tokio::test]\n    async fn test_rls_for_owner() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id_for_a = client_id_from_u8(0);\n        let client_id_for_b = client_id_from_u8(1);\n\n        // Establish a connection for owner and client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_for_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_for_b, &db);\n\n        let auth_for_a = AuthCtx::new(db.owner_identity(), client_id_for_a.identity);\n        let auth_for_b = AuthCtx::new(db.owner_identity(), client_id_for_b.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        // Create table `t`\n        let table_id = db.create_table_for_test(\"t\", &[(\"id\", AlgebraicType::identity())], &[0.into()])?;\n\n        // Restrict access to `t`\n        insert_rls_rules(&db, [table_id], [\"select * from t where id = :sender\"])?;\n\n        let mut query_ids = 0;\n\n        // Have owner and client subscribe to `t`\n        subscribe_multi(&subs, auth_for_a, &[\"select * from t\"], tx_for_a, &mut query_ids).await?;\n        subscribe_multi(&subs, auth_for_b, &[\"select * from t\"], tx_for_b, &mut query_ids).await?;\n\n        // Wait for both subscriptions\n        assert_matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n        assert_matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        let schema = ProductType::from([AlgebraicType::identity()]);\n\n        let id_for_b = identity_from_u8(1);\n        let id_for_c = identity_from_u8(2);\n\n        commit_tx(\n            &db,\n            &subs,\n            [],\n            [\n                // Insert an identity for client `b` plus a random identity\n                (table_id, product![id_for_b]),\n                (table_id, product![id_for_c]),\n            ],\n        )?;\n\n        assert_tx_update_for_table(\n            rx_for_a.recv(),\n            table_id,\n            &schema,\n            // The owner should receive both identities\n            [product![id_for_b], product![id_for_c]],\n            [],\n        )\n        .await;\n\n        assert_tx_update_for_table(\n            rx_for_b.recv(),\n            table_id,\n            &schema,\n            // Client `b` should only receive its identity\n            [product![id_for_b]],\n            [],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Test that we do not send empty updates to clients\n    #[tokio::test]\n    async fn test_no_empty_updates() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n\n        // Establish a client connection\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let schema = [(\"x\", AlgebraicType::U8)];\n\n        let t_id = db.create_table_for_test(\"t\", &schema, &[])?;\n\n        // Subscribe to rows of `t` where `x` is 0\n        subscribe_multi(&subs, auth, &[\"select * from t where x = 0\"], tx, &mut 0).await?;\n\n        // Wait to receive the initial subscription message\n        assert!(matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n\n        // Insert a row that does not match the query\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, t_id, &bsatn::to_vec(&product![1_u8])?)?;\n\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n\n        // Insert a row that does match the query\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, t_id, &bsatn::to_vec(&product![0_u8])?)?;\n\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n\n        let schema = ProductType::from([AlgebraicType::U8]);\n\n        // If the server sends empty updates, this assertion will fail,\n        // because we will receive one for the first transaction.\n        assert_tx_update_for_table(rx.recv(), t_id, &schema, [product![0_u8]], []).await;\n        Ok(())\n    }\n\n    /// Test that we do not compress within a [SubscriptionMessage].\n    /// The message itself is compressed before being sent over the wire,\n    /// but we don't care about that for this test.\n    ///\n    /// Needs a multi-threaded tokio runtime so that the module subscription worker can run in parallel.\n    #[tokio::test(flavor = \"multi_thread\", worker_threads = 1)]\n    async fn test_no_compression_for_subscribe() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        // Establish a client connection with compression\n        let (tx, mut rx) = client_connection_with_compression(client_id, &db, ws_v1::Compression::Brotli);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let table_id = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U64)], &[])?;\n\n        let mut inserts = vec![];\n\n        for i in 0..16_000u64 {\n            inserts.push((table_id, product![i]));\n        }\n\n        // Insert a lot of rows into `t`.\n        // We want to insert enough to cross any threshold there might be for compression.\n        commit_tx(&db, &subs, [], inserts)?;\n\n        // Subscribe to the entire table\n        subscribe_multi(&subs, auth, &[\"select * from t\"], tx, &mut 0).await?;\n\n        // Assert the table updates within this message are all be uncompressed\n        match rx.recv().await {\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(SubscriptionMessage {\n                result:\n                    SubscriptionResult::SubscribeMulti(SubscriptionData {\n                        data: ws_v1::FormatSwitch::Bsatn(ws_v1::DatabaseUpdate { tables }),\n                    }),\n                ..\n            }))) => {\n                assert!(tables.iter().all(|ws_v1::TableUpdate { updates, .. }| updates\n                    .iter()\n                    .all(|query_update| matches!(query_update, ws_v1::CompressableQueryUpdate::Uncompressed(_)))));\n            }\n            Some(_) => panic!(\"unexpected message from subscription\"),\n            None => panic!(\"channel unexpectedly closed\"),\n        };\n\n        Ok(())\n    }\n\n    /// Test that we receive subscription updates for DML\n    #[tokio::test]\n    async fn test_updates_for_dml() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        // Establish a client connection\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n        let schema = [(\"x\", AlgebraicType::U8), (\"y\", AlgebraicType::U8)];\n        let t_id = db.create_table_for_test(\"t\", &schema, &[])?;\n\n        // Subscribe to `t`\n        subscribe_multi(&subs, auth, &[\"select * from t\"], tx, &mut 0).await?;\n\n        // Wait to receive the initial subscription message\n        assert_matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        );\n\n        let schema = ProductType::from([AlgebraicType::U8, AlgebraicType::U8]);\n\n        // Only the owner can invoke DML commands\n        let auth = AuthCtx::new(identity_from_u8(0), identity_from_u8(0));\n\n        run(\n            db.clone(),\n            \"INSERT INTO t (x, y) VALUES (0, 1)\".to_string(),\n            auth.clone(),\n            Some(subs.clone()),\n            None,\n            &mut vec![],\n        )\n        .await?;\n\n        // Client should receive insert\n        assert_tx_update_for_table(rx.recv(), t_id, &schema, [product![0_u8, 1_u8]], []).await;\n\n        run(\n            db.clone(),\n            \"UPDATE t SET y=2 WHERE x=0\".to_string(),\n            auth.clone(),\n            Some(subs.clone()),\n            None,\n            &mut vec![],\n        )\n        .await?;\n\n        // Client should receive update\n        assert_tx_update_for_table(rx.recv(), t_id, &schema, [product![0_u8, 2_u8]], [product![0_u8, 1_u8]]).await;\n\n        run(\n            db,\n            \"DELETE FROM t WHERE x=0\".to_string(),\n            auth,\n            Some(subs),\n            None,\n            &mut vec![],\n        )\n        .await?;\n\n        // Client should receive delete\n        assert_tx_update_for_table(rx.recv(), t_id, &schema, [], [product![0_u8, 2_u8]]).await;\n        Ok(())\n    }\n\n    /// Test that we do not compress within a [TransactionUpdateMessage].\n    /// The message itself is compressed before being sent over the wire,\n    /// but we don't care about that for this test.\n    #[tokio::test]\n    async fn test_no_compression_for_update() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        // Establish a client connection with compression\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection_with_compression(client_id, &db, ws_v1::Compression::Brotli);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let table_id = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U64)], &[])?;\n\n        let mut inserts = vec![];\n\n        for i in 0..16_000u64 {\n            inserts.push((table_id, product![i]));\n        }\n\n        // Subscribe to the entire table\n        subscribe_multi(&subs, auth, &[\"select * from t\"], tx, &mut 0).await?;\n\n        // Wait to receive the initial subscription message\n        assert!(matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n        ));\n\n        // Insert a lot of rows into `t`.\n        // We want to insert enough to cross any threshold there might be for compression.\n        commit_tx(&db, &subs, [], inserts)?;\n\n        // Assert the table updates within this message are all be uncompressed\n        match rx.recv().await {\n            Some(OutboundMessage::V1(SerializableMessage::TxUpdate(TransactionUpdateMessage {\n                database_update:\n                    SubscriptionUpdateMessage {\n                        database_update: ws_v1::FormatSwitch::Bsatn(ws_v1::DatabaseUpdate { tables }),\n                        ..\n                    },\n                ..\n            }))) => {\n                assert!(tables.iter().all(|ws_v1::TableUpdate { updates, .. }| updates\n                    .iter()\n                    .all(|query_update| matches!(query_update, ws_v1::CompressableQueryUpdate::Uncompressed(_)))));\n            }\n            Some(_) => panic!(\"unexpected message from subscription\"),\n            None => panic!(\"channel unexpectedly closed\"),\n        };\n\n        Ok(())\n    }\n\n    /// In this test we subscribe to a join query, update the lhs table,\n    /// and assert that the server sends the correct delta to the client.\n    #[tokio::test]\n    async fn test_update_for_join() -> anyhow::Result<()> {\n        async fn test_subscription_updates(queries: &[&'static str]) -> anyhow::Result<()> {\n            let db = relational_db()?;\n\n            // Establish a client connection\n            let client_id = client_id_from_u8(1);\n            let (sender, mut rx) = client_connection(client_id, &db);\n\n            let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n            let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n            let p_schema = [(\"id\", AlgebraicType::U64), (\"signed_in\", AlgebraicType::Bool)];\n            let l_schema = [\n                (\"id\", AlgebraicType::U64),\n                (\"x\", AlgebraicType::U64),\n                (\"z\", AlgebraicType::U64),\n            ];\n\n            let p_id = db.create_table_for_test(\"p\", &p_schema, &[0.into()])?;\n            let l_id = db.create_table_for_test(\"l\", &l_schema, &[0.into()])?;\n\n            subscribe_multi(&subs, auth, queries, sender, &mut 0).await?;\n\n            assert!(matches!(\n                rx.recv().await,\n                Some(OutboundMessage::V1(SerializableMessage::Subscription(_)))\n            ));\n\n            // Insert two matching player rows\n            commit_tx(\n                &db,\n                &subs,\n                [],\n                [\n                    (p_id, product![1_u64, true]),\n                    (p_id, product![2_u64, true]),\n                    (l_id, product![1_u64, 2_u64, 2_u64]),\n                    (l_id, product![2_u64, 3_u64, 3_u64]),\n                ],\n            )?;\n\n            let schema = ProductType::from(p_schema);\n\n            // We should receive both matching player rows\n            assert_tx_update_for_table(\n                rx.recv(),\n                p_id,\n                &schema,\n                [product![1_u64, true], product![2_u64, true]],\n                [],\n            )\n            .await;\n\n            // Update one of the matching player rows\n            commit_tx(\n                &db,\n                &subs,\n                [(p_id, product![2_u64, true])],\n                [(p_id, product![2_u64, false])],\n            )?;\n\n            // We should receive an update for it because it is still matching\n            assert_tx_update_for_table(\n                rx.recv(),\n                p_id,\n                &schema,\n                [product![2_u64, false]],\n                [product![2_u64, true]],\n            )\n            .await;\n\n            // Update the the same matching player row\n            commit_tx(\n                &db,\n                &subs,\n                [(p_id, product![2_u64, false])],\n                [(p_id, product![2_u64, true])],\n            )?;\n\n            // We should receive an update for it because it is still matching\n            assert_tx_update_for_table(\n                rx.recv(),\n                p_id,\n                &schema,\n                [product![2_u64, true]],\n                [product![2_u64, false]],\n            )\n            .await;\n\n            Ok(())\n        }\n\n        test_subscription_updates(&[\n            \"select * from p where id = 1\",\n            \"select p.* from p join l on p.id = l.id where l.x > 0 and l.x < 5 and l.z > 0 and l.z < 5\",\n        ])\n        .await?;\n        test_subscription_updates(&[\n            \"select * from p where id = 1\",\n            \"select p.* from p join l on p.id = l.id where 0 < l.x and l.x < 5 and 0 < l.z and l.z < 5\",\n        ])\n        .await?;\n        test_subscription_updates(&[\n            \"select * from p where id = 1\",\n            \"select p.* from p join l on p.id = l.id where l.x > 0 and l.x < 5 and l.x > 0 and l.z < 5 and l.id != 1\",\n        ])\n        .await?;\n        test_subscription_updates(&[\n            \"select * from p where id = 1\",\n            \"select p.* from p join l on p.id = l.id where 0 < l.x and l.x < 5 and 0 < l.z and l.z < 5 and l.id != 1\",\n        ])\n        .await?;\n\n        Ok(())\n    }\n\n    /// Test that we do not evaluate queries that we know will not match table update rows\n    ///\n    /// Needs a multi-threaded tokio runtime so that the module subscription worker can run in parallel.\n    #[tokio::test(flavor = \"multi_thread\", worker_threads = 1)]\n    async fn test_query_pruning() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id_a = client_id_from_u8(1);\n        let client_id_b = client_id_from_u8(2);\n        // Establish a connection for each client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_b, &db);\n\n        let auth_a = AuthCtx::new(db.owner_identity(), client_id_a.identity);\n        let auth_b = AuthCtx::new(db.owner_identity(), client_id_b.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let u_id = db.create_table_for_test(\n            \"u\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"a\", AlgebraicType::U64),\n                (\"b\", AlgebraicType::U64),\n            ],\n            &[0.into()],\n        )?;\n        let v_id = db.create_table_for_test(\n            \"v\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"x\", AlgebraicType::U64),\n                (\"y\", AlgebraicType::U64),\n            ],\n            &[0.into(), 1.into()],\n        )?;\n\n        commit_tx(\n            &db,\n            &subs,\n            [],\n            [\n                (u_id, product![0u64, 1u64, 1u64]),\n                (u_id, product![1u64, 2u64, 2u64]),\n                (u_id, product![2u64, 3u64, 3u64]),\n                (v_id, product![0u64, 4u64, 4u64]),\n                (v_id, product![1u64, 5u64, 5u64]),\n            ],\n        )?;\n\n        let mut query_ids = 0;\n\n        // Returns (i: 0, a: 1, b: 1)\n        subscribe_multi(\n            &subs,\n            auth_a.clone(),\n            &[\n                \"select u.* from u join v on u.i = v.i where v.x = 4\",\n                \"select u.* from u join v on u.i = v.i where v.x = 6\",\n            ],\n            tx_for_a,\n            &mut query_ids,\n        )\n        .await?;\n\n        // Returns (i: 1, a: 2, b: 2)\n        subscribe_multi(\n            &subs,\n            auth_b.clone(),\n            &[\n                \"select u.* from u join v on u.i = v.i where v.x = 5\",\n                \"select u.* from u join v on u.i = v.i where v.x = 7\",\n            ],\n            tx_for_b,\n            &mut query_ids,\n        )\n        .await?;\n\n        // Wait for both subscriptions\n        assert!(matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        ));\n        assert!(matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        ));\n\n        // Modify a single row in `v`\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![1u64, 5u64, 5u64])],\n            [(v_id, product![1u64, 5u64, 6u64])],\n        )?;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 0);\n\n        // Insert a new row into `v`\n        let metrics = commit_tx(&db, &subs, [], [(v_id, product![2u64, 6u64, 6u64])])?;\n\n        assert_tx_update_for_table(\n            rx_for_a.recv(),\n            u_id,\n            &ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]),\n            [product![2u64, 3u64, 3u64]],\n            [],\n        )\n        .await;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // Modify a matching row in `u`\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(u_id, product![1u64, 2u64, 2u64])],\n            [(u_id, product![1u64, 2u64, 3u64])],\n        )?;\n\n        assert_tx_update_for_table(\n            rx_for_b.recv(),\n            u_id,\n            &ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]),\n            [product![1u64, 2u64, 3u64]],\n            [product![1u64, 2u64, 2u64]],\n        )\n        .await;\n\n        // We should have evaluated all of the queries\n        assert_eq!(metrics.delta_queries_evaluated, 4);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // Insert a non-matching row in `u`\n        let metrics = commit_tx(&db, &subs, [], [(u_id, product![3u64, 0u64, 0u64])])?;\n\n        // We should have evaluated all of the queries\n        assert_eq!(metrics.delta_queries_evaluated, 4);\n        assert_eq!(metrics.delta_queries_matched, 0);\n\n        Ok(())\n    }\n\n    /// Test that we do not evaluate queries that we know will not match row updates\n    #[tokio::test]\n    async fn test_join_pruning() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let u_id = db.create_table_for_test_with_the_works(\n            \"u\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"a\", AlgebraicType::U64),\n                (\"b\", AlgebraicType::U64),\n            ],\n            &[0.into()],\n            // The join column for this table does not have to be unique,\n            // because pruning only requires us to probe the join index on `v`.\n            &[],\n            StAccess::Public,\n        )?;\n        let v_id = db.create_table_for_test_with_the_works(\n            \"v\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"x\", AlgebraicType::U64),\n                (\"y\", AlgebraicType::U64),\n            ],\n            &[0.into(), 1.into()],\n            &[0.into()],\n            StAccess::Public,\n        )?;\n\n        let schema = ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]);\n\n        commit_tx(\n            &db,\n            &subs,\n            [],\n            [\n                (v_id, product![1u64, 1u64, 1u64]),\n                (v_id, product![2u64, 2u64, 2u64]),\n                (v_id, product![3u64, 3u64, 3u64]),\n                (v_id, product![4u64, 4u64, 4u64]),\n                (v_id, product![5u64, 5u64, 5u64]),\n            ],\n        )?;\n\n        let mut query_ids = 0;\n\n        subscribe_multi(\n            &subs,\n            auth,\n            &[\n                \"select u.* from u join v on u.i = v.i where v.x = 1\",\n                \"select u.* from u join v on u.i = v.i where v.x = 2\",\n                \"select u.* from u join v on u.i = v.i where v.x = 3\",\n                \"select u.* from u join v on u.i = v.i where v.x = 4\",\n                \"select u.* from u join v on u.i = v.i where v.x = 5\",\n            ],\n            tx,\n            &mut query_ids,\n        )\n        .await?;\n\n        assert_matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        // Insert a new row into `u` that joins with `x = 1`\n        let metrics = commit_tx(&db, &subs, [], [(u_id, product![1u64, 2u64, 3u64])])?;\n\n        assert_tx_update_for_table(rx.recv(), u_id, &schema, [product![1u64, 2u64, 3u64]], []).await;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // UPDATE v SET y = 2 WHERE id = 1\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![1u64, 1u64, 1u64])],\n            [(v_id, product![1u64, 1u64, 2u64])],\n        )?;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 0);\n\n        // UPDATE v SET x = 2 WHERE id = 1\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![1u64, 1u64, 2u64])],\n            [(v_id, product![1u64, 2u64, 2u64])],\n        )?;\n\n        // Results in a no-op\n        assert_tx_update_for_table(rx.recv(), u_id, &schema, [], []).await;\n\n        // We should have evaluated queries for `x = 1` and `x = 2`\n        assert_eq!(metrics.delta_queries_evaluated, 2);\n        assert_eq!(metrics.delta_queries_matched, 2);\n\n        // Insert new row into `u` that joins with `x = 3`\n        // UPDATE v SET x = 4 WHERE id = 3\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![3u64, 3u64, 3u64])],\n            [(v_id, product![3u64, 4u64, 3u64]), (u_id, product![3u64, 4u64, 5u64])],\n        )?;\n\n        assert_tx_update_for_table(rx.recv(), u_id, &schema, [product![3u64, 4u64, 5u64]], []).await;\n\n        // We should have evaluated queries for `x = 3` and `x = 4`\n        assert_eq!(metrics.delta_queries_evaluated, 2);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // UPDATE v SET x = 0 WHERE id = 3\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![3u64, 4u64, 3u64])],\n            [(v_id, product![3u64, 0u64, 3u64])],\n        )?;\n\n        assert_tx_update_for_table(rx.recv(), u_id, &schema, [], [product![3u64, 4u64, 5u64]]).await;\n\n        // We should only have evaluated the query for `x = 4`\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // Insert new row into `u` that joins with `x = 5`\n        // UPDATE v SET x = 6 WHERE id = 5\n        // Should result in a no-op\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![5u64, 5u64, 5u64])],\n            [(v_id, product![5u64, 6u64, 6u64]), (u_id, product![5u64, 6u64, 7u64])],\n        )?;\n\n        // We should only have evaluated the query for `x = 5`\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 0);\n\n        Ok(())\n    }\n\n    /// Test that one client subscribing does not affect another\n    #[tokio::test]\n    async fn test_subscribe_distinct_queries_same_plan() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id_a = client_id_from_u8(1);\n        let client_id_b = client_id_from_u8(2);\n        // Establish a connection for each client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_b, &db);\n\n        let auth_a = AuthCtx::new(db.owner_identity(), client_id_a.identity);\n        let auth_b = AuthCtx::new(db.owner_identity(), client_id_b.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let u_id = db.create_table_for_test_with_the_works(\n            \"u\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"a\", AlgebraicType::U64),\n                (\"b\", AlgebraicType::U64),\n            ],\n            &[0.into()],\n            // The join column for this table does not have to be unique,\n            // because pruning only requires us to probe the join index on `v`.\n            &[],\n            StAccess::Public,\n        )?;\n        let v_id = db.create_table_for_test_with_the_works(\n            \"v\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"x\", AlgebraicType::U64),\n                (\"y\", AlgebraicType::U64),\n            ],\n            &[0.into(), 1.into()],\n            &[0.into()],\n            StAccess::Public,\n        )?;\n\n        commit_tx(&db, &subs, [], [(v_id, product![1u64, 1u64, 1u64])])?;\n\n        let mut query_ids = 0;\n\n        // Both clients subscribe to the same query modulo whitespace\n        subscribe_multi(\n            &subs,\n            auth_a,\n            &[\"select u.* from u join v on u.i = v.i where v.x = 1\"],\n            tx_for_a,\n            &mut query_ids,\n        )\n        .await?;\n        subscribe_multi(\n            &subs,\n            auth_b,\n            &[\"select u.* from u join v on u.i = v.i where v.x =  1\"],\n            tx_for_b.clone(),\n            &mut query_ids,\n        )\n        .await?;\n\n        // Wait for both subscriptions\n        assert_matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n        assert_matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        // Insert a new row into `u`\n        commit_tx(&db, &subs, [], [(u_id, product![1u64, 0u64, 0u64])])?;\n\n        assert_tx_update_for_table(\n            rx_for_a.recv(),\n            u_id,\n            &ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]),\n            [product![1u64, 0u64, 0u64]],\n            [],\n        )\n        .await;\n\n        assert_tx_update_for_table(\n            rx_for_b.recv(),\n            u_id,\n            &ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]),\n            [product![1u64, 0u64, 0u64]],\n            [],\n        )\n        .await;\n\n        Ok(())\n    }\n\n    /// Test that one client unsubscribing does not affect another\n    #[tokio::test]\n    async fn test_unsubscribe_distinct_queries_same_plan() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        let client_id_a = client_id_from_u8(1);\n        let client_id_b = client_id_from_u8(2);\n\n        // Establish a connection for each client\n        let (tx_for_a, mut rx_for_a) = client_connection(client_id_a, &db);\n        let (tx_for_b, mut rx_for_b) = client_connection(client_id_b, &db);\n\n        let auth_a = AuthCtx::new(db.owner_identity(), client_id_a.identity);\n        let auth_b = AuthCtx::new(db.owner_identity(), client_id_b.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let u_id = db.create_table_for_test_with_the_works(\n            \"u\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"a\", AlgebraicType::U64),\n                (\"b\", AlgebraicType::U64),\n            ],\n            &[0.into()],\n            // The join column for this table does not have to be unique,\n            // because pruning only requires us to probe the join index on `v`.\n            &[],\n            StAccess::Public,\n        )?;\n        let v_id = db.create_table_for_test_with_the_works(\n            \"v\",\n            &[\n                (\"i\", AlgebraicType::U64),\n                (\"x\", AlgebraicType::U64),\n                (\"y\", AlgebraicType::U64),\n            ],\n            &[0.into(), 1.into()],\n            &[0.into()],\n            StAccess::Public,\n        )?;\n\n        commit_tx(&db, &subs, [], [(v_id, product![1u64, 1u64, 1u64])])?;\n\n        let mut query_ids = 0;\n\n        subscribe_multi(\n            &subs,\n            auth_a,\n            &[\"select u.* from u join v on u.i = v.i where v.x = 1\"],\n            tx_for_a,\n            &mut query_ids,\n        )\n        .await?;\n        subscribe_multi(\n            &subs,\n            auth_b.clone(),\n            &[\"select u.* from u join v on u.i = v.i where  v.x = 1\"],\n            tx_for_b.clone(),\n            &mut query_ids,\n        )\n        .await?;\n\n        // Wait for both subscriptions\n        assert_matches!(\n            rx_for_a.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n        assert_matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        unsubscribe_multi(&subs, auth_b, tx_for_b, query_ids)?;\n\n        assert_matches!(\n            rx_for_b.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::UnsubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        // Insert a new row into `u`\n        let metrics = commit_tx(&db, &subs, [], [(u_id, product![1u64, 0u64, 0u64])])?;\n\n        assert_tx_update_for_table(\n            rx_for_a.recv(),\n            u_id,\n            &ProductType::from([AlgebraicType::U64, AlgebraicType::U64, AlgebraicType::U64]),\n            [product![1u64, 0u64, 0u64]],\n            [],\n        )\n        .await;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        // Modify a matching row in `v`\n        let metrics = commit_tx(\n            &db,\n            &subs,\n            [(v_id, product![1u64, 1u64, 1u64])],\n            [(v_id, product![1u64, 2u64, 2u64])],\n        )?;\n\n        // We should only have evaluated a single query\n        assert_eq!(metrics.delta_queries_evaluated, 1);\n        assert_eq!(metrics.delta_queries_matched, 1);\n\n        Ok(())\n    }\n\n    /// Test that we do not evaluate queries that return trivially empty results\n    ///\n    /// Needs a multi-threaded tokio runtime so that the module subscription worker can run in parallel.\n    #[tokio::test(flavor = \"multi_thread\", worker_threads = 1)]\n    async fn test_query_pruning_for_empty_tables() -> anyhow::Result<()> {\n        let db = relational_db()?;\n\n        // Establish a client connection\n        let client_id = client_id_from_u8(1);\n        let (tx, mut rx) = client_connection(client_id, &db);\n\n        let auth = AuthCtx::new(db.owner_identity(), client_id.identity);\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n\n        let schema = &[(\"id\", AlgebraicType::U64), (\"a\", AlgebraicType::U64)];\n        let indices = &[0.into()];\n        // Create tables `t` and `s` with `(i: u64, a: u64)`.\n        db.create_table_for_test(\"t\", schema, indices)?;\n        let s_id = db.create_table_for_test(\"s\", schema, indices)?;\n\n        // Insert one row into `s`, but leave `t` empty.\n        commit_tx(&db, &subs, [], [(s_id, product![0u64, 0u64])])?;\n\n        // Subscribe to queries that return empty results\n        let metrics = subscribe_multi(\n            &subs,\n            auth,\n            &[\n                \"select t.* from t where a = 0\",\n                \"select t.* from t join s on t.id = s.id where s.a = 0\",\n                \"select s.* from t join s on t.id = s.id where t.a = 0\",\n            ],\n            tx,\n            &mut 0,\n        )\n        .await?;\n\n        assert_matches!(\n            rx.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n\n        assert_eq!(metrics.rows_scanned, 0);\n        assert_eq!(metrics.index_seeks, 0);\n\n        Ok(())\n    }\n\n    /// Asserts that a subscription holds a tx handle for the entire length of its evaluation.\n    #[test]\n    fn test_tx_subscription_ordering() -> ResultTest<()> {\n        let test_db = TestDB::durable()?;\n\n        let runtime = test_db.runtime().cloned().unwrap();\n        let db = test_db.db.clone();\n\n        // Create table with one row\n        let table_id = db.create_table_for_test(\"T\", &[(\"a\", AlgebraicType::U8)], &[])?;\n        with_auto_commit(&db, |tx| insert(&db, tx, table_id, &product!(1_u8)).map(drop))?;\n\n        let (send, mut recv) = mpsc::unbounded_channel();\n\n        // Subscribing to T should return a single row.\n        let db2 = db.clone();\n        let query_handle = runtime.spawn_blocking(move || {\n            add_subscriber(\n                db.clone(),\n                \"select * from T\",\n                Some(Arc::new(move |tx: &_| {\n                    // Wake up writer thread after starting the reader tx\n                    let _ = send.send(());\n                    // Then go to sleep\n                    std::thread::sleep(Duration::from_secs(1));\n                    // Assuming subscription evaluation holds a lock on the db,\n                    // any mutations to T will necessarily occur after,\n                    // and therefore we should only see a single row returned.\n                    assert_eq!(1, db.iter(tx, table_id).unwrap().count());\n                })),\n            )\n        });\n\n        // Write a second row to T concurrently with the reader thread\n        let write_handle = runtime.spawn(async move {\n            let _ = recv.recv().await;\n            with_auto_commit(&db2, |tx| insert(&db2, tx, table_id, &product!(2_u8)).map(drop))\n        });\n\n        runtime.block_on(write_handle)??;\n        runtime.block_on(query_handle)??;\n\n        Ok(())\n    }\n\n    #[test]\n    fn subs_cannot_access_private_tables() -> ResultTest<()> {\n        let test_db = TestDB::durable()?;\n        let db = test_db.db.clone();\n\n        // Create a public table.\n        let indexes = &[0.into()];\n        let cols = &[(\"a\", AlgebraicType::U8)];\n        let _ = db.create_table_for_test(\"public\", cols, indexes)?;\n\n        // Create a private table.\n        let _ = db.create_table_for_test_with_access(\"private\", cols, indexes, StAccess::Private)?;\n\n        // We can subscribe to a public table.\n        let subscribe = |sql| add_subscriber(db.clone(), sql, None);\n        assert!(subscribe(\"SELECT * FROM public\").is_ok());\n\n        // We cannot subscribe when a private table is mentioned,\n        // not even when in a join where the projection doesn't mention the table,\n        // as the mere fact of joining can leak information from the private table.\n        for sql in [\n            \"SELECT * FROM private\",\n            // Even if the query will return no rows, we still reject it.\n            \"SELECT * FROM private WHERE false\",\n            \"SELECT private.* FROM private\",\n            \"SELECT public.* FROM public JOIN private ON public.a = private.a WHERE private.a = 1\",\n            \"SELECT private.* FROM private JOIN public ON private.a = public.a WHERE public.a = 1\",\n        ] {\n            assert!(subscribe(sql).is_err(),);\n        }\n\n        Ok(())\n    }\n\n    #[tokio::test]\n    async fn test_confirmed_reads() -> anyhow::Result<()> {\n        let (db, durability) = relational_db_with_manual_durability(tokio::runtime::Handle::current())?;\n\n        let client_id_confirmed = client_id_from_u8(1);\n        let client_id_unconfirmed = client_id_from_u8(2);\n\n        let (tx_for_confirmed, mut rx_for_confirmed) =\n            client_connection_with_confirmed_reads(client_id_confirmed, &db, true);\n        let (tx_for_unconfirmed, mut rx_for_unconfirmed) =\n            client_connection_with_confirmed_reads(client_id_unconfirmed, &db, false);\n\n        let auth_confirmed = AuthCtx::new(db.owner_identity(), client_id_confirmed.identity);\n        let auth_unconfirmed = AuthCtx::new(db.owner_identity(), client_id_unconfirmed.identity);\n\n        let subs = ModuleSubscriptions::for_test_enclosing_runtime(db.clone());\n        let table = db.create_table_for_test(\"t\", &[(\"x\", AlgebraicType::U8)], &[])?;\n        let schema = ProductType::from([AlgebraicType::U8]);\n\n        // Subscribe both clients.\n        subscribe_multi(&subs, auth_confirmed, &[\"select * from t\"], tx_for_confirmed, &mut 0).await?;\n        subscribe_multi(\n            &subs,\n            auth_unconfirmed,\n            &[\"select * from t\"],\n            tx_for_unconfirmed,\n            &mut 0,\n        )\n        .await?;\n\n        assert_matches!(\n            rx_for_unconfirmed.recv().await,\n            Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                SubscriptionMessage {\n                    result: SubscriptionResult::SubscribeMulti(_),\n                    ..\n                }\n            )))\n        );\n        assert_after_durable(&durability, async {\n            assert_matches!(\n                rx_for_confirmed.recv().await,\n                Some(OutboundMessage::V1(SerializableMessage::Subscription(\n                    SubscriptionMessage {\n                        result: SubscriptionResult::SubscribeMulti(_),\n                        ..\n                    }\n                )))\n            );\n        })\n        .await;\n\n        // Insert a row.\n        let mut tx = begin_mut_tx(&db);\n        db.insert(&mut tx, table, &bsatn::to_vec(&product![1_u8])?)?;\n        assert!(matches!(\n            subs.commit_and_broadcast_event(None, module_event(), tx),\n            Ok(Ok(_))\n        ));\n        // Insert another row, using SQL.\n        let auth = AuthCtx::new(identity_from_u8(0), identity_from_u8(0));\n        run(\n            db.clone(),\n            \"INSERT INTO t (x) VALUES (2)\".to_string(),\n            auth,\n            Some(subs),\n            None,\n            &mut vec![],\n        )\n        .await?;\n\n        // Unconfirmed client should have received both rows.\n        assert_tx_update_for_table(rx_for_unconfirmed.recv(), table, &schema, [product![1_u8]], []).await;\n        assert_tx_update_for_table(rx_for_unconfirmed.recv(), table, &schema, [product![2_u8]], []).await;\n\n        // Confirmed client should receive the rows after the tx becomes durable.\n        assert_after_durable(&durability, async {\n            assert_tx_update_for_table(rx_for_confirmed.recv(), table, &schema, [product![1_u8]], []).await;\n            assert_tx_update_for_table(rx_for_confirmed.recv(), table, &schema, [product![2_u8]], []).await\n        })\n        .await;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/module_subscription_manager.rs",
    "content": "use super::execution_unit::QueryHash;\nuse super::tx::DeltaTx;\nuse crate::client::messages::{\n    SerializableMessage, SubscriptionError, SubscriptionMessage, SubscriptionResult, SubscriptionUpdateMessage,\n    TransactionUpdateMessage,\n};\nuse crate::client::{ClientConnectionSender, OutboundMessage, Protocol, WsVersion};\nuse crate::error::DBError;\nuse crate::host::module_host::{DatabaseTableUpdate, ModuleEvent, UpdatesRelValue};\nuse crate::subscription::delta::eval_delta;\nuse crate::subscription::row_list_builder_pool::{BsatnRowListBuilderPool, JsonRowListBuilderFakePool};\nuse crate::subscription::websocket_building::{BuildableWebsocketFormat, RowListBuilderSource};\nuse crate::worker_metrics::WORKER_METRICS;\ntype V2EvalUpdatesResult = (Vec<V2ClientUpdate>, Vec<(SubscriptionIdV2, Box<str>)>, ExecutionMetrics);\nuse core::mem;\nuse itertools::Itertools;\nuse parking_lot::RwLock;\nuse prometheus::IntGauge;\nuse spacetimedb_client_api_messages::websocket::common::ByteListLen as _;\nuse spacetimedb_client_api_messages::websocket::v2::TableUpdateRows;\nuse spacetimedb_client_api_messages::websocket::{v1 as ws_v1, v2 as ws_v2};\nuse spacetimedb_data_structures::map::HashCollectionExt as _;\nuse spacetimedb_data_structures::map::{\n    hash_map::{Entry, OccupiedError},\n    HashMap, HashSet, IntMap,\n};\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_expr::expr::CollectViews;\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_lib::{AlgebraicValue, ConnectionId, Identity, ProductValue};\nuse spacetimedb_primitives::{ColId, IndexId, TableId, ViewId};\nuse spacetimedb_schema::def::RawModuleDefVersion;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_subscription::{JoinEdge, SubscriptionPlan};\nuse std::collections::BTreeMap;\nuse std::fmt::Debug;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse tokio::sync::{mpsc, oneshot};\n\n/// Clients are uniquely identified by their Identity and ConnectionId.\n/// Identity is insufficient because different ConnectionIds can use the same Identity.\n/// TODO: Determine if ConnectionId is sufficient for uniquely identifying a client.\ntype ClientId = (Identity, ConnectionId);\ntype Query = Arc<Plan>;\ntype Client = Arc<ClientConnectionSender>;\ntype SwitchedTableUpdate =\n    ws_v1::FormatSwitch<ws_v1::TableUpdate<ws_v1::BsatnFormat>, ws_v1::TableUpdate<ws_v1::JsonFormat>>;\ntype SwitchedDbUpdate =\n    ws_v1::FormatSwitch<ws_v1::DatabaseUpdate<ws_v1::BsatnFormat>, ws_v1::DatabaseUpdate<ws_v1::JsonFormat>>;\n\n/// ClientQueryId is an identifier for a query set by the client.\ntype ClientQuerySetId = ws_v2::QuerySetId;\n/// ClientQueryId is an identifier for a query set by the client.\ntype ClientQueryId = ws_v1::QueryId;\n/// SubscriptionId is a globally unique identifier for a subscription.\ntype SubscriptionId = (ClientId, ClientQueryId);\ntype SubscriptionIdV2 = (ClientId, ClientQuerySetId);\n\n#[derive(Debug)]\npub struct Plan {\n    hash: QueryHash,\n    sql: String,\n    plans: Vec<SubscriptionPlan>,\n}\n\nimpl CollectViews for Plan {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        for plan in &self.plans {\n            plan.collect_views(views);\n        }\n    }\n}\n\nimpl Plan {\n    /// Create a new subscription plan to be cached\n    pub fn new(plans: Vec<SubscriptionPlan>, hash: QueryHash, text: String) -> Self {\n        Self { plans, hash, sql: text }\n    }\n\n    /// Returns the query hash for this subscription\n    pub fn hash(&self) -> QueryHash {\n        self.hash\n    }\n\n    /// A subscription query return rows from a single table.\n    /// This method returns the id of that table.\n    pub fn subscribed_table_id(&self) -> TableId {\n        self.plans[0].subscribed_table_id()\n    }\n\n    /// A subscription query return rows from a single table.\n    /// This method returns the name of that table.\n    pub fn subscribed_table_name(&self) -> &TableName {\n        self.plans[0].subscribed_table_name()\n    }\n\n    /// Returns the index ids from which this subscription reads\n    pub fn index_ids(&self) -> impl Iterator<Item = (TableId, IndexId)> + use<> {\n        self.plans\n            .iter()\n            .flat_map(|plan| plan.index_ids())\n            .collect::<HashSet<_>>()\n            .into_iter()\n    }\n\n    /// Returns the table ids from which this subscription reads\n    pub fn table_ids(&self) -> impl Iterator<Item = TableId> + '_ {\n        self.plans\n            .iter()\n            .flat_map(|plan| plan.table_ids())\n            .collect::<HashSet<_>>()\n            .into_iter()\n    }\n\n    /// Return the search arguments for this query\n    fn search_args(&self) -> impl Iterator<Item = (TableId, ColId, AlgebraicValue)> + use<> {\n        let mut args = HashSet::new();\n        for arg in self\n            .plans\n            .iter()\n            .flat_map(|subscription| subscription.optimized_physical_plan().search_args())\n        {\n            args.insert(arg);\n        }\n        args.into_iter()\n    }\n\n    /// Returns the plan fragments that comprise this subscription.\n    /// Will only return one element unless there is a table with multiple RLS rules.\n    pub fn plans_fragments(&self) -> impl Iterator<Item = &SubscriptionPlan> + '_ {\n        self.plans.iter()\n    }\n\n    /// Returns the join edges for this plan, if any.\n    pub fn join_edges(&self) -> impl Iterator<Item = (JoinEdge, AlgebraicValue)> + '_ {\n        self.plans.iter().filter_map(|plan| plan.join_edge())\n    }\n\n    /// The `SQL` text of this subscription.\n    pub fn sql(&self) -> &str {\n        &self.sql\n    }\n\n    /// Does this plan return rows from an event table?\n    pub fn returns_event_table(&self) -> bool {\n        self.plans.iter().any(|p| p.returns_event_table())\n    }\n}\n\n/// For each client, we hold a handle for sending messages, and we track the queries they are subscribed to.\n#[derive(Debug)]\nstruct ClientInfo {\n    outbound_ref: Client,\n    v1_subscriptions: HashMap<SubscriptionId, HashSet<QueryHash>>,\n    v2_subscriptions: HashMap<SubscriptionIdV2, HashSet<QueryHash>>,\n    // This is used to ref count for both v1 subscriptions and v2 subscriptions.\n    subscription_ref_count: HashMap<QueryHash, usize>,\n    // This should be removed when we migrate to SubscribeSingle.\n    legacy_subscriptions: HashSet<QueryHash>,\n    /// This flag is set if an error occurs during a tx update.\n    /// It will be cleaned up async or on resubscribe.\n    ///\n    /// [`Arc`]ed so that this can be updated by the [`SendWorker`]\n    /// and observed by [`SubscriptionManager::remove_dropped_clients`].\n    dropped: Arc<AtomicBool>,\n}\n\nimpl ClientInfo {\n    fn new(outbound_ref: Client) -> Self {\n        Self {\n            outbound_ref,\n            v1_subscriptions: HashMap::default(),\n            v2_subscriptions: HashMap::default(),\n            subscription_ref_count: HashMap::default(),\n            legacy_subscriptions: HashSet::default(),\n            dropped: Arc::new(AtomicBool::new(false)),\n        }\n    }\n\n    /// Check that the subscription ref count matches the actual number of subscriptions.\n    #[cfg(test)]\n    fn assert_ref_count_consistency(&self) {\n        let mut expected_ref_count = HashMap::new();\n        for query_hashes in self.v1_subscriptions.values() {\n            for query_hash in query_hashes {\n                assert!(\n                    self.subscription_ref_count.contains_key(query_hash),\n                    \"Query hash not found: {query_hash:?}\"\n                );\n                expected_ref_count\n                    .entry(*query_hash)\n                    .and_modify(|count| *count += 1)\n                    .or_insert(1);\n            }\n        }\n        for query_hashes in self.v2_subscriptions.values() {\n            for query_hash in query_hashes {\n                assert!(\n                    self.subscription_ref_count.contains_key(query_hash),\n                    \"Query hash not found: {query_hash:?}\"\n                );\n                expected_ref_count\n                    .entry(*query_hash)\n                    .and_modify(|count| *count += 1)\n                    .or_insert(1);\n            }\n        }\n        assert_eq!(\n            self.subscription_ref_count, expected_ref_count,\n            \"Checking the reference totals failed\"\n        );\n    }\n}\n\n/// For each query that has subscribers, we track a set of legacy subscribers and individual subscriptions.\n#[derive(Debug)]\nstruct QueryState {\n    query: Query,\n    // For legacy clients that subscribe to a set of queries, we track them here.\n    legacy_subscribers: HashSet<ClientId>,\n    // For v1 clients that subscribe to a single query, we track them here.\n    subscriptions: HashSet<ClientId>,\n    // The set of (client, query_set_id) pairs that are subscribed to this query.\n    v2_subscriptions: HashSet<SubscriptionIdV2>,\n}\n\nimpl QueryState {\n    fn new(query: Query) -> Self {\n        Self {\n            query,\n            legacy_subscribers: HashSet::default(),\n            subscriptions: HashSet::default(),\n            v2_subscriptions: HashSet::default(),\n        }\n    }\n    fn has_subscribers(&self) -> bool {\n        !self.subscriptions.is_empty() || !self.legacy_subscribers.is_empty() || !self.v2_subscriptions.is_empty()\n    }\n\n    // This returns all of the clients listening to a query. If a client has multiple subscriptions for this query, it will appear twice.\n    fn all_v1_clients(&self) -> impl Iterator<Item = &ClientId> {\n        itertools::chain(&self.legacy_subscribers, &self.subscriptions)\n    }\n\n    /// Return the [`Query`] for this [`QueryState`]\n    pub fn query(&self) -> &Query {\n        &self.query\n    }\n\n    /// Return the search arguments for this query\n    fn search_args(&self) -> impl Iterator<Item = (TableId, ColId, AlgebraicValue)> + use<> {\n        self.query.search_args()\n    }\n}\n\n/// In this container, we keep track of parameterized subscription queries.\n/// This is used to prune unnecessary queries during subscription evaluation.\n///\n/// TODO: This container is populated on initial subscription.\n/// Ideally this information would be stored in the datastore,\n/// but because subscriptions are evaluated using a read only tx,\n/// we have to manage this memory separately.\n///\n/// If we stored this information in the datastore,\n/// we could encode pruning logic in the execution plan itself.\n#[derive(Debug, Default)]\npub struct SearchArguments {\n    /// We parameterize subscriptions if they have an equality selection.\n    /// In this case a parameter is a [TableId], [ColId] pair.\n    ///\n    /// Ex.\n    ///\n    /// ```sql\n    /// SELECT * FROM t WHERE id = <value>\n    /// ```\n    ///\n    /// This query is parameterized by `t.id`.\n    ///\n    /// Ex.\n    ///\n    /// ```sql\n    /// SELECT t.* FROM t JOIN s ON t.id = s.id WHERE s.x = <value>\n    /// ```\n    ///\n    /// This query is parameterized by `s.x`.\n    cols: HashMap<TableId, HashSet<ColId>>,\n    /// For each parameter we keep track of its possible values or arguments.\n    /// These arguments are the different values that clients subscribe with.\n    ///\n    /// Ex.\n    ///\n    /// ```sql\n    /// SELECT * FROM t WHERE id = 3\n    /// SELECT * FROM t WHERE id = 5\n    /// ```\n    ///\n    /// These queries will get parameterized by `t.id`,\n    /// and we will record the args `3` and `5` in this map.\n    args: BTreeMap<(TableId, ColId, AlgebraicValue), HashSet<QueryHash>>,\n}\n\nimpl SearchArguments {\n    /// Return the column ids by which a table is parameterized\n    fn search_params_for_table(&self, table_id: TableId) -> impl Iterator<Item = &ColId> + '_ {\n        self.cols.get(&table_id).into_iter().flatten()\n    }\n\n    /// Are there queries parameterized by this table and column?\n    /// If so, do we have a subscriber for this `search_arg`?\n    fn queries_for_search_arg(\n        &self,\n        table_id: TableId,\n        col_id: ColId,\n        search_arg: AlgebraicValue,\n    ) -> impl Iterator<Item = &QueryHash> {\n        self.args.get(&(table_id, col_id, search_arg)).into_iter().flatten()\n    }\n\n    /// Find the queries that need to be evaluated for this row.\n    fn queries_for_row<'a>(&'a self, table_id: TableId, row: &'a ProductValue) -> impl Iterator<Item = &'a QueryHash> {\n        self.search_params_for_table(table_id)\n            .filter_map(|col_id| row.get_field(col_id.idx(), None).ok().map(|arg| (col_id, arg.clone())))\n            .flat_map(move |(col_id, arg)| self.queries_for_search_arg(table_id, *col_id, arg))\n    }\n\n    /// Remove a query hash and its associated data from this container.\n    /// Note, a query hash may be associated with multiple column ids.\n    fn remove_query(&mut self, query: &Query) {\n        // Collect the column parameters for this query\n        let mut params = query.search_args().collect::<Vec<_>>();\n\n        // Remove the search argument entries for this query\n        for key in &params {\n            if let Some(hashes) = self.args.get_mut(key) {\n                hashes.remove(&query.hash);\n                if hashes.is_empty() {\n                    self.args.remove(key);\n                }\n            }\n        }\n\n        // Retain columns that no longer map to any search arguments\n        params.retain(|(table_id, col_id, _)| {\n            self.args\n                .range((*table_id, *col_id, AlgebraicValue::Min)..=(*table_id, *col_id, AlgebraicValue::Max))\n                .next()\n                .is_none()\n        });\n\n        // Remove columns that no longer map to any search arguments\n        for (table_id, col_id, _) in params {\n            if let Some(col_ids) = self.cols.get_mut(&table_id) {\n                col_ids.remove(&col_id);\n                if col_ids.is_empty() {\n                    self.cols.remove(&table_id);\n                }\n            }\n        }\n    }\n\n    /// Add a new mapping from search argument to query hash\n    fn insert_query(&mut self, table_id: TableId, col_id: ColId, arg: AlgebraicValue, query: QueryHash) {\n        self.args.entry((table_id, col_id, arg)).or_default().insert(query);\n        self.cols.entry(table_id).or_default().insert(col_id);\n    }\n}\n\n/// Keeps track of the indexes that are used in subscriptions.\n#[derive(Debug, Default)]\npub struct QueriedTableIndexIds {\n    ids: HashMap<TableId, HashMap<IndexId, usize>>,\n}\n\nimpl FromIterator<(TableId, IndexId)> for QueriedTableIndexIds {\n    fn from_iter<T: IntoIterator<Item = (TableId, IndexId)>>(iter: T) -> Self {\n        let mut index_ids = Self::default();\n        for (table_id, index_id) in iter {\n            index_ids.insert_index_id(table_id, index_id);\n        }\n        index_ids\n    }\n}\n\nimpl QueriedTableIndexIds {\n    /// Returns the index ids that are used in subscriptions for this table.\n    /// Note, it does not return all of the index ids that are defined on this table.\n    /// Only those that are used by at least one subscription query.\n    pub fn index_ids_for_table(&self, table_id: TableId) -> impl Iterator<Item = IndexId> + '_ {\n        self.ids\n            .get(&table_id)\n            .into_iter()\n            .flat_map(|index_ids| index_ids.keys())\n            .copied()\n    }\n\n    /// Insert a new `table_id` `index_id` pair into this container.\n    /// Note, different queries may read from the same index.\n    /// Hence we may already be tracking this index, in which case we bump its ref count.\n    pub fn insert_index_id(&mut self, table_id: TableId, index_id: IndexId) {\n        *self.ids.entry(table_id).or_default().entry(index_id).or_default() += 1;\n    }\n\n    /// Remove a `table_id` `index_id` pair from this container.\n    /// Note, different queries may read from the same index.\n    /// Hence we only remove this key from the map if its ref count goes to zero.\n    pub fn delete_index_id(&mut self, table_id: TableId, index_id: IndexId) {\n        if let Some(ids) = self.ids.get_mut(&table_id)\n            && let Some(n) = ids.get_mut(&index_id)\n        {\n            *n -= 1;\n\n            if *n == 0 {\n                ids.remove(&index_id);\n\n                if ids.is_empty() {\n                    self.ids.remove(&table_id);\n                }\n            }\n        }\n    }\n\n    /// Insert the index ids from which a query reads into this mapping.\n    /// Note, an index may already be tracked if another query is already using it.\n    /// In this case we just bump its ref count.\n    pub fn insert_index_ids_for_query(&mut self, query: &Query) {\n        for (table_id, index_id) in query.index_ids() {\n            self.insert_index_id(table_id, index_id);\n        }\n    }\n\n    /// Delete the index ids from which a query reads from this mapping\n    /// Note, we will not remove an index id from this mapping if another query is using it.\n    /// Instead we decrement its ref count.\n    pub fn delete_index_ids_for_query(&mut self, query: &Query) {\n        for (table_id, index_id) in query.index_ids() {\n            self.delete_index_id(table_id, index_id);\n        }\n    }\n}\n\n/// A sorted set of join edges used for pruning queries.\n/// See [`JoinEdge`] for more details.\n#[derive(Debug, Default)]\npub struct JoinEdges {\n    edges: BTreeMap<JoinEdge, HashMap<AlgebraicValue, HashSet<QueryHash>>>,\n}\n\nimpl JoinEdges {\n    /// If this query has any join edges, add them to the map.\n    fn add_query(&mut self, qs: &QueryState) -> bool {\n        let mut inserted = false;\n        for (edge, rhs_val) in qs.query.join_edges() {\n            inserted = true;\n            self.edges\n                .entry(edge)\n                .or_default()\n                .entry(rhs_val)\n                .or_default()\n                .insert(qs.query.hash);\n        }\n        inserted\n    }\n\n    /// If this query has any join edges, remove them from the map.\n    fn remove_query(&mut self, query: &Query) {\n        for (edge, rhs_val) in query.join_edges() {\n            if let Some(values) = self.edges.get_mut(&edge)\n                && let Some(hashes) = values.get_mut(&rhs_val)\n            {\n                hashes.remove(&query.hash);\n                if hashes.is_empty() {\n                    values.remove(&rhs_val);\n                    if values.is_empty() {\n                        self.edges.remove(&edge);\n                    }\n                }\n            }\n        }\n    }\n\n    /// Searches for queries that must be evaluated for this row,\n    /// and effectively prunes queries that do not.\n    fn queries_for_row<'a>(\n        &'a self,\n        table_id: TableId,\n        row: &'a ProductValue,\n        find_rhs_val: impl Fn(&JoinEdge, &ProductValue) -> Option<AlgebraicValue>,\n    ) -> impl Iterator<Item = &'a QueryHash> {\n        self.edges\n            .range(JoinEdge::range_for_table(table_id))\n            .filter_map(move |(edge, hashes)| find_rhs_val(edge, row).as_ref().and_then(|rhs_val| hashes.get(rhs_val)))\n            .flatten()\n    }\n}\n\n/// Responsible for the efficient evaluation of subscriptions.\n/// It performs basic multi-query optimization,\n/// in that if a query has N subscribers,\n/// it is only executed once,\n/// with the results copied to the N receivers.\n#[derive(Debug)]\npub struct SubscriptionManager {\n    /// State for each client.\n    clients: HashMap<ClientId, ClientInfo>,\n\n    /// Queries for which there is at least one subscriber.\n    queries: HashMap<QueryHash, QueryState>,\n\n    /// If a query reads from a table,\n    /// but does not have a simple equality filter on that table,\n    /// we map the table to the query in this inverted index.\n    tables: IntMap<TableId, HashSet<QueryHash>>,\n\n    /// Tracks the indices used across all subscriptions\n    /// to enable building the appropriate indexes for row updates.\n    indexes: QueriedTableIndexIds,\n\n    /// If a query reads from a table,\n    /// and has a simple equality filter on that table,\n    /// we map the filter values to the query in this lookup table.\n    search_args: SearchArguments,\n\n    /// A sorted set of join edges used for pruning queries.\n    /// See [`JoinEdge`] for more details.\n    join_edges: JoinEdges,\n\n    /// Transmit side of a channel to the manager's [`SendWorker`] task.\n    ///\n    /// The send worker runs in parallel and pops [`ComputedQueries`]es out in order,\n    /// aggregates each client's full set of updates,\n    /// then passes them to the clients' websocket workers.\n    /// This allows transaction processing to proceed on the main thread\n    /// ahead of post-processing and broadcasting updates\n    /// while still ensuring that those updates are sent in the correct serial order.\n    /// Additionally, it avoids starving the next reducer request of Tokio workers,\n    /// as it imposes a delay between unlocking the datastore\n    /// and waking the many per-client sender Tokio tasks.\n    send_worker_queue: BroadcastQueue,\n}\n\n/// A single update for one client and one query.\n#[derive(Debug)]\nstruct ClientUpdate {\n    id: ClientId,\n    table_id: TableId,\n    table_name: TableName,\n    update:\n        ws_v1::FormatSwitch<ws_v1::SingleQueryUpdate<ws_v1::BsatnFormat>, ws_v1::SingleQueryUpdate<ws_v1::JsonFormat>>,\n}\n\n/// A single update for one client and one query.\n#[derive(Debug)]\nstruct V2ClientUpdate {\n    id: ClientId,\n    query_set_id: ClientQuerySetId,\n    table_name: TableName,\n    rows: TableUpdateRows,\n}\n\n/// The computed incremental update queries with sufficient information\n/// to not depend on the transaction lock so that further work can be\n/// done in a separate worker: [`SubscriptionManager::send_worker`].\n/// The queries in this structure have not been aggregated yet\n/// but will be in the worker.\n#[derive(Debug)]\nstruct ComputedQueries {\n    updates: Vec<ClientUpdate>,\n    v2_updates: Vec<V2ClientUpdate>,\n    errs: Vec<(ClientId, Box<str>)>,\n    v2_errs: Vec<(SubscriptionIdV2, Box<str>)>,\n    event: Arc<ModuleEvent>,\n    caller: Option<Arc<ClientConnectionSender>>,\n    module_def_version: RawModuleDefVersion,\n}\n\n// Wraps a sender so that it will increment a gauge.\n#[derive(Debug)]\nstruct SenderWithGauge<T> {\n    tx: mpsc::UnboundedSender<T>,\n    metric: Option<IntGauge>,\n}\nimpl<T> Clone for SenderWithGauge<T> {\n    fn clone(&self) -> Self {\n        SenderWithGauge {\n            tx: self.tx.clone(),\n            metric: self.metric.clone(),\n        }\n    }\n}\n\nimpl<T> SenderWithGauge<T> {\n    fn new(tx: mpsc::UnboundedSender<T>, metric: Option<IntGauge>) -> Self {\n        Self { tx, metric }\n    }\n\n    /// Send a message to the worker and update the queue length metric.\n    pub fn send(&self, msg: T) -> Result<(), mpsc::error::SendError<T>> {\n        if let Some(metric) = &self.metric {\n            metric.inc();\n        }\n        // Note, this could number would be permanently off if the send call panics.\n        self.tx.send(msg)\n    }\n}\n\n/// The offset used to control visibility of the message if the client has\n/// requested confirmed reads.\n///\n/// [`SendWorkerMessage`]s are sent while holding the database lock, i.e.\n/// without committing the transaction. When the transaction commits, the\n/// message sender is expected to send the transaction offset along this channel.\n///\n/// NOTE: If the send end is dropped before sending the offset, the\n/// [`SendWorker`] will assume that the message sender was cancelled, and exit\n/// itself.\npub type TransactionOffset = oneshot::Receiver<TxOffset>;\n\n/// Create a TransactionOffset from a known TxOffset.\npub fn from_tx_offset(offset: TxOffset) -> TransactionOffset {\n    let (tx, rx) = oneshot::channel();\n    let _ = tx.send(offset);\n    rx\n}\n\n/// Message sent by the [`SubscriptionManager`] to the [`SendWorker`].\n#[derive(Debug)]\nenum SendWorkerMessage {\n    /// A transaction has completed and the [`SubscriptionManager`] has evaluated the incremental queries,\n    /// so the [`SendWorker`] should broadcast them to clients.\n    ///\n    /// The `tx_offset` of the transaction is used to control visibility of\n    /// the results if the client has requested confirmed reads.\n    Broadcast {\n        tx_offset: TransactionOffset,\n        queries: ComputedQueries,\n    },\n\n    /// A new client has been registered in the [`SubscriptionManager`],\n    /// so the [`SendWorker`] should also record its existence.\n    AddClient {\n        client_id: ClientId,\n        /// Shared handle on the `dropped` flag in the [`Subscriptionmanager`]'s [`ClientInfo`].\n        ///\n        /// Will be updated by [`SendWorker::run`] and read by [`SubscriptionManager::remove_dropped_clients`].\n        dropped: Arc<AtomicBool>,\n        outbound_ref: Client,\n    },\n\n    /// Send a message to a client.\n    ///\n    /// In some cases, `message` may contain query results. In this case,\n    /// `tx_offset` is `Some`, and later used to control visibility of the\n    /// message if the the client has requested confirmed reads.\n    SendMessage {\n        recipient: Arc<ClientConnectionSender>,\n        tx_offset: Option<TransactionOffset>,\n        // message: SerializableMessage,\n        message: OutboundMessage,\n    },\n\n    /// A client previously added by a [`Self::AddClient`] message has been removed from the [`SubscriptionManager`],\n    /// so the [`SendWorker`] should also forget it.\n    RemoveClient(ClientId),\n}\n\n// Tracks some gauges related to subscriptions.\npub struct SubscriptionGaugeStats {\n    // The number of unique queries with at least one subscriber.\n    pub num_queries: usize,\n    // The number of unique connections with at least one subscription.\n    pub num_connections: usize,\n    // The number of subscription sets across all clients.\n    pub num_subscription_sets: usize,\n    // The total number of subscriptions across all clients and queries.\n    pub num_query_subscriptions: usize,\n    // The total number of subscriptions across all clients and queries.\n    pub num_legacy_subscriptions: usize,\n}\n\nimpl SubscriptionManager {\n    pub fn for_test_without_metrics_arc_rwlock() -> Arc<RwLock<Self>> {\n        Arc::new(RwLock::new(Self::for_test_without_metrics()))\n    }\n\n    pub fn for_test_without_metrics() -> Self {\n        Self::new(SendWorker::spawn_new(None))\n    }\n\n    pub fn new(send_worker_queue: BroadcastQueue) -> Self {\n        Self {\n            clients: Default::default(),\n            queries: Default::default(),\n            indexes: Default::default(),\n            tables: Default::default(),\n            search_args: Default::default(),\n            join_edges: Default::default(),\n            send_worker_queue,\n        }\n    }\n\n    pub fn query(&self, hash: &QueryHash) -> Option<Query> {\n        self.queries.get(hash).map(|state| state.query.clone())\n    }\n\n    pub fn calculate_gauge_stats(&self) -> SubscriptionGaugeStats {\n        let num_queries = self.queries.len();\n        let num_connections = self.clients.len();\n        let num_query_subscriptions = self.queries.values().map(|state| state.subscriptions.len()).sum();\n        let num_subscription_sets = self.clients.values().map(|ci| ci.v1_subscriptions.len()).sum();\n        let num_legacy_subscriptions = self\n            .clients\n            .values()\n            .filter(|ci| !ci.legacy_subscriptions.is_empty())\n            .count();\n\n        SubscriptionGaugeStats {\n            num_queries,\n            num_connections,\n            num_query_subscriptions,\n            num_subscription_sets,\n            num_legacy_subscriptions,\n        }\n    }\n\n    /// Add a new [`ClientInfo`] to the `clients` map, and broadcast a message along `send_worker_tx`\n    /// that the [`SendWorker`] should also add this client.\n    ///\n    /// Horrible signature to enable split borrows on [`Self`].\n    fn get_or_make_client_info_and_inform_send_worker<'clients>(\n        clients: &'clients mut HashMap<ClientId, ClientInfo>,\n        send_worker_tx: &BroadcastQueue,\n        client_id: ClientId,\n        outbound_ref: Client,\n    ) -> &'clients mut ClientInfo {\n        clients.entry(client_id).or_insert_with(|| {\n            let info = ClientInfo::new(outbound_ref.clone());\n            send_worker_tx\n                .send(SendWorkerMessage::AddClient {\n                    client_id,\n                    dropped: info.dropped.clone(),\n                    outbound_ref,\n                })\n                .expect(\"send worker has panicked, or otherwise dropped its recv queue!\");\n            info\n        })\n    }\n\n    /// Remove a [`ClientInfo`] from the `clients` map,\n    /// and broadcast a message along `send_worker_tx` that the [`SendWorker`] should also remove it.\n    fn remove_client_and_inform_send_worker(&mut self, client_id: ClientId) -> Option<ClientInfo> {\n        self.clients.remove(&client_id).inspect(|_| {\n            self.send_worker_queue\n                .send(SendWorkerMessage::RemoveClient(client_id))\n                .expect(\"send worker has panicked, or otherwise dropped its recv queue!\");\n        })\n    }\n\n    pub fn num_unique_queries(&self) -> usize {\n        self.queries.len()\n    }\n\n    #[cfg(test)]\n    fn contains_query(&self, hash: &QueryHash) -> bool {\n        self.queries.contains_key(hash)\n    }\n\n    #[cfg(test)]\n    fn contains_client(&self, subscriber: &ClientId) -> bool {\n        self.clients.contains_key(subscriber)\n    }\n\n    #[cfg(test)]\n    fn contains_legacy_subscription(&self, subscriber: &ClientId, query: &QueryHash) -> bool {\n        self.queries\n            .get(query)\n            .is_some_and(|state| state.legacy_subscribers.contains(subscriber))\n    }\n\n    #[cfg(test)]\n    fn query_reads_from_table(&self, query: &QueryHash, table: &TableId) -> bool {\n        self.tables.get(table).is_some_and(|queries| queries.contains(query))\n    }\n\n    #[cfg(test)]\n    fn query_has_search_arg(&self, query: QueryHash, table_id: TableId, col_id: ColId, arg: AlgebraicValue) -> bool {\n        self.search_args\n            .queries_for_search_arg(table_id, col_id, arg)\n            .any(|hash| *hash == query)\n    }\n\n    #[cfg(test)]\n    fn table_has_search_param(&self, table_id: TableId, col_id: ColId) -> bool {\n        self.search_args\n            .search_params_for_table(table_id)\n            .any(|id| *id == col_id)\n    }\n\n    fn remove_legacy_subscriptions(&mut self, client: &ClientId) {\n        if let Some(ci) = self.clients.get_mut(client) {\n            let mut queries_to_remove = Vec::new();\n            for query_hash in ci.legacy_subscriptions.iter() {\n                let Some(query_state) = self.queries.get_mut(query_hash) else {\n                    tracing::warn!(\"Query state not found for query hash: {:?}\", query_hash);\n                    continue;\n                };\n\n                query_state.legacy_subscribers.remove(client);\n                if !query_state.has_subscribers() {\n                    SubscriptionManager::remove_query_from_tables(\n                        &mut self.tables,\n                        &mut self.join_edges,\n                        &mut self.indexes,\n                        &mut self.search_args,\n                        &query_state.query,\n                    );\n                    queries_to_remove.push(*query_hash);\n                }\n            }\n            ci.legacy_subscriptions.clear();\n            for query_hash in queries_to_remove {\n                self.queries.remove(&query_hash);\n            }\n        }\n    }\n\n    /// Remove any clients that have been marked for removal\n    pub fn remove_dropped_clients(&mut self) {\n        for id in self.clients.keys().copied().collect::<Vec<_>>() {\n            if let Some(client) = self.clients.get(&id)\n                && client.dropped.load(Ordering::Relaxed)\n            {\n                self.remove_all_subscriptions(&id);\n            }\n        }\n    }\n\n    /// Remove a single subscription for a client.\n    /// This will return an error if the client does not have a subscription with the given query id.\n    pub fn remove_subscription(&mut self, client_id: ClientId, query_id: ClientQueryId) -> Result<Vec<Query>, DBError> {\n        let subscription_id = (client_id, query_id);\n        let Some(ci) = self\n            .clients\n            .get_mut(&client_id)\n            .filter(|ci| !ci.dropped.load(Ordering::Acquire))\n        else {\n            return Err(anyhow::anyhow!(\"Client not found: {client_id:?}\").into());\n        };\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n\n        let Some(query_hashes) = ci.v1_subscriptions.remove(&subscription_id) else {\n            return Err(anyhow::anyhow!(\"Subscription not found: {subscription_id:?}\").into());\n        };\n        let mut queries_to_return = Vec::new();\n        for hash in query_hashes {\n            let remaining_refs = {\n                let Some(count) = ci.subscription_ref_count.get_mut(&hash) else {\n                    return Err(anyhow::anyhow!(\"Query count not found for query hash: {hash:?}\").into());\n                };\n                *count -= 1;\n                *count\n            };\n            if remaining_refs > 0 {\n                // The client is still subscribed to this query, so we are done for now.\n                continue;\n            }\n            // The client is no longer subscribed to this query.\n            ci.subscription_ref_count.remove(&hash);\n            let Some(query_state) = self.queries.get_mut(&hash) else {\n                return Err(anyhow::anyhow!(\"Query state not found for query hash: {hash:?}\").into());\n            };\n            queries_to_return.push(query_state.query.clone());\n            query_state.subscriptions.remove(&client_id);\n            if !query_state.has_subscribers() {\n                SubscriptionManager::remove_query_from_tables(\n                    &mut self.tables,\n                    &mut self.join_edges,\n                    &mut self.indexes,\n                    &mut self.search_args,\n                    &query_state.query,\n                );\n                self.queries.remove(&hash);\n            }\n        }\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n\n        Ok(queries_to_return)\n    }\n\n    pub fn remove_subscription_v2(\n        &mut self,\n        client_id: ClientId,\n        query_set_id: ClientQuerySetId,\n    ) -> Result<Vec<Query>, DBError> {\n        let subscription_id = (client_id, query_set_id);\n        let Some(ci) = self\n            .clients\n            .get_mut(&client_id)\n            .filter(|ci| !ci.dropped.load(Ordering::Acquire))\n        else {\n            return Err(anyhow::anyhow!(\"Client not found: {client_id:?}\").into());\n        };\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n\n        let Some(query_hashes) = ci.v2_subscriptions.remove(&subscription_id) else {\n            return Err(anyhow::anyhow!(\"Subscription not found: {subscription_id:?}\").into());\n        };\n\n        let mut queries_to_return = Vec::new();\n        for hash in query_hashes {\n            let Some(query_state) = self.queries.get_mut(&hash) else {\n                return Err(anyhow::anyhow!(\"Query state not found for query hash: {hash:?}\").into());\n            };\n            queries_to_return.push(query_state.query.clone());\n            query_state.v2_subscriptions.remove(&subscription_id);\n\n            let remaining_refs = {\n                let Some(count) = ci.subscription_ref_count.get_mut(&hash) else {\n                    return Err(anyhow::anyhow!(\"Query count not found for query hash: {hash:?}\").into());\n                };\n                *count -= 1;\n                *count\n            };\n            if remaining_refs > 0 {\n                continue;\n            }\n\n            ci.subscription_ref_count.remove(&hash);\n            if !query_state.has_subscribers() {\n                SubscriptionManager::remove_query_from_tables(\n                    &mut self.tables,\n                    &mut self.join_edges,\n                    &mut self.indexes,\n                    &mut self.search_args,\n                    &query_state.query,\n                );\n                self.queries.remove(&hash);\n            }\n        }\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n\n        Ok(queries_to_return)\n    }\n\n    /// Adds a single subscription for a client.\n    pub fn add_subscription(&mut self, client: Client, query: Query, query_id: ClientQueryId) -> Result<(), DBError> {\n        self.add_subscription_multi(client, vec![query], query_id).map(|_| ())\n    }\n\n    pub fn add_subscription_v2(\n        &mut self,\n        client: Client,\n        queries: Vec<Query>,\n        query_set_id: ClientQuerySetId,\n    ) -> Result<Vec<Query>, DBError> {\n        let client_id = (client.id.identity, client.id.connection_id);\n        // Clean up any dropped subscriptions\n        if self\n            .clients\n            .get(&client_id)\n            .is_some_and(|ci| ci.dropped.load(Ordering::Acquire))\n        {\n            self.remove_all_subscriptions(&client_id);\n        }\n        let ci = Self::get_or_make_client_info_and_inform_send_worker(\n            &mut self.clients,\n            &self.send_worker_queue,\n            client_id,\n            client,\n        );\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n        let subscription_id = (client_id, query_set_id);\n        let hash_set = match ci.v2_subscriptions.try_insert(subscription_id, HashSet::new()) {\n            Err(OccupiedError { .. }) => {\n                return Err(anyhow::anyhow!(\n                    \"Subscription with id {query_set_id:?} already exists for client: {client_id:?}\"\n                )\n                .into());\n            }\n            Ok(hash_set) => hash_set,\n        };\n\n        // We track the queries that are being added for this client.\n        let mut new_queries: Vec<Arc<Plan>> = Vec::new();\n\n        for query in &queries {\n            let hash = query.hash();\n            // Deduping queries within this single call.\n            if !hash_set.insert(hash) {\n                continue;\n            }\n            let query_state = self\n                .queries\n                .entry(hash)\n                .or_insert_with(|| QueryState::new(query.clone()));\n\n            Self::insert_query(\n                &mut self.tables,\n                &mut self.join_edges,\n                &mut self.indexes,\n                &mut self.search_args,\n                query_state,\n            );\n            let entry = ci.subscription_ref_count.entry(hash).or_insert(0);\n            *entry += 1;\n            query_state.v2_subscriptions.insert(subscription_id);\n            new_queries.push(query.clone());\n        }\n        Ok(new_queries)\n    }\n\n    pub fn add_subscription_multi(\n        &mut self,\n        client: Client,\n        queries: Vec<Query>,\n        query_id: ClientQueryId,\n    ) -> Result<Vec<Query>, DBError> {\n        let client_id = (client.id.identity, client.id.connection_id);\n\n        // Clean up any dropped subscriptions\n        if self\n            .clients\n            .get(&client_id)\n            .is_some_and(|ci| ci.dropped.load(Ordering::Acquire))\n        {\n            self.remove_all_subscriptions(&client_id);\n        }\n\n        let ci = Self::get_or_make_client_info_and_inform_send_worker(\n            &mut self.clients,\n            &self.send_worker_queue,\n            client_id,\n            client,\n        );\n\n        #[cfg(test)]\n        ci.assert_ref_count_consistency();\n        let subscription_id = (client_id, query_id);\n        let hash_set = match ci.v1_subscriptions.try_insert(subscription_id, HashSet::new()) {\n            Err(OccupiedError { .. }) => {\n                return Err(anyhow::anyhow!(\n                    \"Subscription with id {query_id:?} already exists for client: {client_id:?}\"\n                )\n                .into());\n            }\n            Ok(hash_set) => hash_set,\n        };\n        // We track the queries that are being added for this client.\n        let mut new_queries = Vec::new();\n\n        for query in &queries {\n            let hash = query.hash();\n            // Deduping queries within this single call.\n            if !hash_set.insert(hash) {\n                continue;\n            }\n            let query_state = self\n                .queries\n                .entry(hash)\n                .or_insert_with(|| QueryState::new(query.clone()));\n\n            Self::insert_query(\n                &mut self.tables,\n                &mut self.join_edges,\n                &mut self.indexes,\n                &mut self.search_args,\n                query_state,\n            );\n\n            let entry = ci.subscription_ref_count.entry(hash).or_insert(0);\n            *entry += 1;\n            let is_new_entry = *entry == 1;\n\n            let inserted = query_state.subscriptions.insert(client_id);\n            // This should arguably crash the server, as it indicates a bug.\n            if inserted != is_new_entry {\n                return Err(anyhow::anyhow!(\"Internal error, ref count and query_state mismatch\").into());\n            }\n            if inserted {\n                new_queries.push(query.clone());\n            }\n        }\n\n        #[cfg(test)]\n        {\n            ci.assert_ref_count_consistency();\n        }\n\n        Ok(new_queries)\n    }\n\n    /// Adds a client and its queries to the subscription manager.\n    /// Sets up the set of subscriptions for the client, replacing any existing legacy subscriptions.\n    ///\n    /// If a query is not already indexed,\n    /// its table ids added to the inverted index.\n    // #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn set_legacy_subscription(&mut self, client: Client, queries: impl IntoIterator<Item = Query>) {\n        let client_id = (client.id.identity, client.id.connection_id);\n        // First, remove any existing legacy subscriptions.\n        self.remove_legacy_subscriptions(&client_id);\n\n        // Now, add the new subscriptions.\n        let ci = Self::get_or_make_client_info_and_inform_send_worker(\n            &mut self.clients,\n            &self.send_worker_queue,\n            client_id,\n            client,\n        );\n\n        for unit in queries {\n            let hash = unit.hash();\n            ci.legacy_subscriptions.insert(hash);\n            let query_state = self\n                .queries\n                .entry(hash)\n                .or_insert_with(|| QueryState::new(unit.clone()));\n            Self::insert_query(\n                &mut self.tables,\n                &mut self.join_edges,\n                &mut self.indexes,\n                &mut self.search_args,\n                query_state,\n            );\n            query_state.legacy_subscribers.insert(client_id);\n        }\n    }\n\n    // Update the mapping from table id to related queries by removing the given query.\n    // If this removes all queries for a table, the map entry for that table is removed altogether.\n    // This takes a ref to the table map instead of `self` to avoid borrowing issues.\n    fn remove_query_from_tables(\n        tables: &mut IntMap<TableId, HashSet<QueryHash>>,\n        join_edges: &mut JoinEdges,\n        index_ids: &mut QueriedTableIndexIds,\n        search_args: &mut SearchArguments,\n        query: &Query,\n    ) {\n        let hash = query.hash();\n        join_edges.remove_query(query);\n        search_args.remove_query(query);\n        index_ids.delete_index_ids_for_query(query);\n        for table_id in query.table_ids() {\n            if let Entry::Occupied(mut entry) = tables.entry(table_id) {\n                let hashes = entry.get_mut();\n                if hashes.remove(&hash) && hashes.is_empty() {\n                    entry.remove();\n                }\n            }\n        }\n    }\n\n    // Update the mapping from table id to related queries by inserting the given query.\n    // Also add any search arguments the query may have.\n    // This takes a ref to the table map instead of `self` to avoid borrowing issues.\n    fn insert_query(\n        tables: &mut IntMap<TableId, HashSet<QueryHash>>,\n        join_edges: &mut JoinEdges,\n        index_ids: &mut QueriedTableIndexIds,\n        search_args: &mut SearchArguments,\n        query_state: &QueryState,\n    ) {\n        // If this is new, we need to update the table to query mapping.\n        if !query_state.has_subscribers() {\n            let hash = query_state.query.hash;\n            let query = query_state.query();\n            let return_table = query.subscribed_table_id();\n            let mut table_ids = query.table_ids().collect::<HashSet<_>>();\n\n            // Update the index id mapping\n            index_ids.insert_index_ids_for_query(query);\n\n            // Update the search arguments\n            for (table_id, col_id, arg) in query_state.search_args() {\n                table_ids.remove(&table_id);\n                search_args.insert_query(table_id, col_id, arg, hash);\n            }\n\n            // Update the join edges if the return table didn't have any search arguments\n            if table_ids.contains(&return_table) && join_edges.add_query(query_state) {\n                table_ids.remove(&return_table);\n            }\n\n            // Finally update the `tables` map if the query didn't have a search argument or a join edge for a table\n            for table_id in table_ids {\n                tables.entry(table_id).or_default().insert(hash);\n            }\n        }\n    }\n\n    /// Removes a client from the subscriber mapping.\n    /// If a query no longer has any subscribers,\n    /// it is removed from the index along with its table ids.\n    #[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn remove_all_subscriptions(&mut self, client: &ClientId) -> Vec<Query> {\n        let mut removed_query_hashes = HashSet::new();\n        if let Some(client_info) = self.clients.get(client) {\n            removed_query_hashes.extend(client_info.legacy_subscriptions.iter().copied());\n            removed_query_hashes.extend(client_info.subscription_ref_count.keys().copied());\n        }\n        let removed_queries = removed_query_hashes\n            .into_iter()\n            .filter_map(|hash| self.queries.get(&hash).map(|q| q.query.clone()))\n            .collect::<Vec<_>>();\n\n        self.remove_legacy_subscriptions(client);\n        let Some(client_info) = self.remove_client_and_inform_send_worker(*client) else {\n            return removed_queries;\n        };\n\n        debug_assert!(client_info.legacy_subscriptions.is_empty());\n        let mut queries_to_remove = Vec::new();\n        let v1_query_hashes = client_info\n            .v1_subscriptions\n            .values()\n            .flatten()\n            .copied()\n            .collect::<HashSet<_>>();\n        for (subscription_id, queries) in client_info.v2_subscriptions {\n            for query_hash in queries {\n                let Some(query_state) = self.queries.get_mut(&query_hash) else {\n                    tracing::warn!(\"Query state not found for query hash: {:?}\", query_hash);\n                    continue;\n                };\n                query_state.v2_subscriptions.remove(&subscription_id);\n                if !query_state.has_subscribers() {\n                    queries_to_remove.push(query_hash);\n                    SubscriptionManager::remove_query_from_tables(\n                        &mut self.tables,\n                        &mut self.join_edges,\n                        &mut self.indexes,\n                        &mut self.search_args,\n                        &query_state.query,\n                    );\n                }\n            }\n        }\n        // This loop can be removed once v1 subscriptions are removed.\n        for query_hash in v1_query_hashes {\n            let Some(query_state) = self.queries.get_mut(&query_hash) else {\n                // This can happen if they are cliented up in the v2 loop above.\n                continue;\n            };\n            query_state.subscriptions.remove(client);\n            // This could happen twice for the same hash if a client has a duplicate, but that's fine. It is idepotent.\n            if !query_state.has_subscribers() {\n                queries_to_remove.push(query_hash);\n                SubscriptionManager::remove_query_from_tables(\n                    &mut self.tables,\n                    &mut self.join_edges,\n                    &mut self.indexes,\n                    &mut self.search_args,\n                    &query_state.query,\n                );\n            }\n        }\n        for query_hash in queries_to_remove {\n            self.queries.remove(&query_hash);\n        }\n\n        removed_queries\n    }\n\n    /// Find the queries that need to be evaluated for this table update.\n    ///\n    /// Note, this tries to prune irrelevant queries from the subscription.\n    ///\n    /// When is this beneficial?\n    ///\n    /// If many different clients subscribe to the same parameterized query,\n    /// but they all subscribe with different parameter values,\n    /// and if these rows contain only a few unique values for this parameter,\n    /// most clients will not receive an update,\n    /// and so we can avoid evaluating queries for them entirely.\n    ///\n    /// Ex.\n    ///\n    /// 1000 clients subscribe to `SELECT * FROM t WHERE id = ?`,\n    /// each one with a different value for `?`.\n    /// If there are transactions that only ever update one row of `t` at a time,\n    /// we only pay the cost of evaluating one query.\n    ///\n    /// When is this not beneficial?\n    ///\n    /// If the table update contains a lot of unique values for a parameter,\n    /// we won't be able to prune very many queries from the subscription,\n    /// so this could add some overhead linear in the size of the table update.\n    ///\n    /// TODO: This logic should be expressed in the execution plan itself,\n    /// so that we don't have to preprocess the table update before execution.\n    fn queries_for_table_update<'a>(\n        &'a self,\n        table_update: &'a DatabaseTableUpdate,\n        find_rhs_val: impl Fn(&JoinEdge, &ProductValue) -> Option<AlgebraicValue>,\n    ) -> impl Iterator<Item = &'a QueryHash> {\n        let mut queries = HashSet::new();\n        for hash in table_update\n            .inserts\n            .iter()\n            .chain(table_update.deletes.iter())\n            .flat_map(|row| self.queries_for_row(table_update.table_id, row, &find_rhs_val))\n        {\n            queries.insert(hash);\n        }\n        for hash in self.tables.get(&table_update.table_id).into_iter().flatten() {\n            queries.insert(hash);\n        }\n        queries.into_iter()\n    }\n\n    /// Find the queries that need to be evaluated for this row.\n    fn queries_for_row<'a>(\n        &'a self,\n        table_id: TableId,\n        row: &'a ProductValue,\n        find_rhs_val: impl Fn(&JoinEdge, &ProductValue) -> Option<AlgebraicValue>,\n    ) -> impl Iterator<Item = &'a QueryHash> {\n        self.search_args\n            .queries_for_row(table_id, row)\n            .chain(self.join_edges.queries_for_row(table_id, row, find_rhs_val))\n    }\n\n    /// Returns the index ids that are used in subscription queries\n    pub fn index_ids_for_subscriptions(&self) -> &QueriedTableIndexIds {\n        &self.indexes\n    }\n\n    /// This method takes a set of delta tables,\n    /// evaluates only the necessary queries for those delta tables,\n    /// and then sends the results to each client.\n    ///\n    /// This previously used rayon to parallelize subscription evaluation.\n    /// However, in order to optimize for the common case of small updates,\n    /// we removed rayon and switched to a single-threaded execution,\n    /// which removed significant overhead associated with thread switching.\n    //#[tracing::instrument(level = \"trace\", skip_all)]\n    pub fn eval_updates_sequential(\n        &self,\n        (tx, tx_offset): (&DeltaTx, TransactionOffset),\n        bsatn_rlb_pool: &BsatnRowListBuilderPool,\n        module_def_version: RawModuleDefVersion,\n        event: Arc<ModuleEvent>,\n        caller: Option<Arc<ClientConnectionSender>>,\n    ) -> (ExecutionMetrics, Vec<SubscriptionIdV2>) {\n        //let span = tracing::info_span!(\"eval_incr\").entered();\n\n        let (updates, errs, mut metrics) = if self.queries.is_empty() {\n            // We have no queries, so do nothing.\n            <_>::default()\n        } else {\n            let tables = &event.status.database_update().unwrap().tables;\n            self.eval_updates_sequential_inner(tx, bsatn_rlb_pool, tables)\n        };\n\n        let (v2_updates, v2_errs, metrics_v2) = if self.queries.is_empty() {\n            // We have no queries, so do nothing.\n            <_>::default()\n        } else {\n            let tables = &event.status.database_update().unwrap().tables;\n            self.eval_updates_sequential_inner_v2(tx, bsatn_rlb_pool, tables)\n        };\n        metrics.merge(metrics_v2);\n\n        let failed_v2_subscriptions: HashSet<SubscriptionIdV2> = v2_errs.iter().map(|(id, _)| *id).collect();\n        let queries = ComputedQueries {\n            updates,\n            v2_updates,\n            errs,\n            v2_errs,\n            event,\n            caller,\n            module_def_version,\n        };\n\n        // We've now finished all of the work which needs to read from the datastore,\n        // so get this work off the main thread and over to the `send_worker`,\n        // then return ASAP in order to unlock the datastore and start running the next transaction.\n        // See comment on the `send_worker_tx` field in [`SubscriptionManager`] for more motivation.\n        self.send_worker_queue\n            .send(SendWorkerMessage::Broadcast { tx_offset, queries })\n            .expect(\"send worker has panicked, or otherwise dropped its recv queue!\");\n\n        //drop(span);\n\n        (metrics, failed_v2_subscriptions.into_iter().collect())\n    }\n\n    fn eval_updates_sequential_inner_v2(\n        &self,\n        tx: &DeltaTx,\n        bsatn_rlb_pool: &BsatnRowListBuilderPool,\n        tables: &[DatabaseTableUpdate],\n    ) -> V2EvalUpdatesResult {\n        #[derive(Default)]\n        struct FoldState {\n            updates: Vec<V2ClientUpdate>,\n            errs: Vec<(SubscriptionIdV2, Box<str>)>,\n            metrics: ExecutionMetrics,\n        }\n\n        /// Returns the value pointed to by this join edge\n        fn find_rhs_val(edge: &JoinEdge, row: &ProductValue, tx: &DeltaTx) -> Option<AlgebraicValue> {\n            tx.iter_by_col_eq(\n                edge.rhs_table,\n                edge.rhs_join_col,\n                &row.elements[edge.lhs_join_col.idx()],\n            )\n            .expect(\"This read should always succeed, and it's a bug if it doesn't\")\n            .next()\n            .map(|row| {\n                row.read_col(edge.rhs_col)\n                    .expect(\"This read should always succeed, and it's a bug if it doesn't\")\n            })\n        }\n\n        fn encode_v2_rows(\n            updates: &UpdatesRelValue<'_>,\n            metrics: &mut ExecutionMetrics,\n            rlb_pool: &impl RowListBuilderSource<ws_v1::BsatnFormat>,\n            is_event_table: bool,\n        ) -> TableUpdateRows {\n            let (inserts, nr_ins) = <ws_v1::BsatnFormat as BuildableWebsocketFormat>::encode_list(\n                rlb_pool.take_row_list_builder(),\n                updates.inserts.iter(),\n            );\n            if is_event_table {\n                // Event tables only have inserts (events); no deletes.\n                debug_assert!(updates.deletes.is_empty(), \"event tables should not produce deletes\");\n                metrics.bytes_scanned += inserts.num_bytes();\n                metrics.bytes_sent_to_clients += inserts.num_bytes();\n                TableUpdateRows::EventTable(ws_v2::EventTableRows { events: inserts })\n            } else {\n                let (deletes, nr_del) = <ws_v1::BsatnFormat as BuildableWebsocketFormat>::encode_list(\n                    rlb_pool.take_row_list_builder(),\n                    updates.deletes.iter(),\n                );\n                // TODO: Fix metrics. We only encode once, then we clone for other clients, so this isn't the place\n                // to report the metrics.\n                let _num_rows = nr_del + nr_ins;\n                let num_bytes = deletes.num_bytes() + inserts.num_bytes();\n                metrics.bytes_scanned += num_bytes;\n                metrics.bytes_sent_to_clients += num_bytes;\n                TableUpdateRows::PersistentTable(ws_v2::PersistentTableRows { inserts, deletes })\n            }\n        }\n\n        let FoldState { updates, errs, metrics } = tables\n            .iter()\n            .filter(|table| !table.inserts.is_empty() || !table.deletes.is_empty())\n            .flat_map(|table_update| {\n                self.queries_for_table_update(table_update, |edge, row| find_rhs_val(edge, row, tx))\n            })\n            // deduplicate queries by their hash\n            .filter({\n                let mut seen = HashSet::new();\n                move |&hash| seen.insert(hash)\n            })\n            .copied()\n            // Ignore queries that only have v1 subscriptions.\n            .filter(|&hash| !self.queries[&hash].v2_subscriptions.is_empty())\n            .flat_map(|hash| {\n                let qstate = &self.queries[&hash];\n                qstate\n                    .query\n                    .plans_fragments()\n                    .map(move |plan_fragment| (qstate, plan_fragment, hash))\n            })\n            .fold(FoldState::default(), |mut acc, (qstate, plan, _hash)| {\n                let table_name = plan.subscribed_table_name().clone();\n                match eval_delta(tx, &mut acc.metrics, plan) {\n                    Err(err) => {\n                        tracing::error!(\n                            message = \"Query errored during tx update\",\n                            sql = qstate.query.sql,\n                            reason = ?err,\n                        );\n                        let err = DBError::WithSql {\n                            sql: qstate.query.sql.as_str().into(),\n                            error: Box::new(err.into()),\n                        }\n                        .to_string()\n                        .into_boxed_str();\n\n                        // TODO: Remove these subscriptions from the subscription manager.\n                        acc.errs\n                            .extend(qstate.v2_subscriptions.iter().map(|id| (*id, err.clone())));\n                    }\n                    Ok(None) => {}\n                    Ok(Some(delta_updates)) => {\n                        let rows = encode_v2_rows(\n                            &delta_updates,\n                            &mut acc.metrics,\n                            bsatn_rlb_pool,\n                            plan.returns_event_table(),\n                        );\n                        for &(client_id, query_set_id) in qstate.v2_subscriptions.iter() {\n                            acc.updates.push(V2ClientUpdate {\n                                id: client_id,\n                                query_set_id,\n                                table_name: table_name.clone(),\n                                // This clone is cheap, since it is just cloning a reference to the row data.\n                                rows: rows.clone(),\n                            });\n                        }\n                    }\n                }\n\n                acc\n            });\n\n        (updates, errs, metrics)\n    }\n\n    #[allow(clippy::type_complexity)]\n    fn eval_updates_sequential_inner(\n        &self,\n        tx: &DeltaTx,\n        bsatn_rlb_pool: &BsatnRowListBuilderPool,\n        tables: &[DatabaseTableUpdate],\n    ) -> (Vec<ClientUpdate>, Vec<(ClientId, Box<str>)>, ExecutionMetrics) {\n        use ws_v1::FormatSwitch::{Bsatn, Json};\n\n        #[derive(Default)]\n        struct FoldState {\n            updates: Vec<ClientUpdate>,\n            errs: Vec<(ClientId, Box<str>)>,\n            metrics: ExecutionMetrics,\n        }\n\n        /// Returns the value pointed to by this join edge\n        fn find_rhs_val(edge: &JoinEdge, row: &ProductValue, tx: &DeltaTx) -> Option<AlgebraicValue> {\n            // What if the joining row was deleted in this tx?\n            // Will we prune a query that we shouldn't have?\n            //\n            // Ultimately no we will not.\n            // We may prune it for this row specifically,\n            // but we will eventually include it for the joining row.\n            tx.iter_by_col_eq(\n                edge.rhs_table,\n                edge.rhs_join_col,\n                &row.elements[edge.lhs_join_col.idx()],\n            )\n            .expect(\"This read should always succeed, and it's a bug if it doesn't\")\n            .next()\n            .map(|row| {\n                row.read_col(edge.rhs_col)\n                    .expect(\"This read should always succeed, and it's a bug if it doesn't\")\n            })\n        }\n\n        let FoldState { updates, errs, metrics } = tables\n            .iter()\n            .filter(|table| !table.inserts.is_empty() || !table.deletes.is_empty())\n            .flat_map(|table_update| {\n                self.queries_for_table_update(table_update, |edge, row| find_rhs_val(edge, row, tx))\n            })\n            // deduplicate queries by their hash\n            .filter({\n                let mut seen = HashSet::new();\n                // (HashSet::insert returns true for novel elements)\n                move |&hash| seen.insert(hash)\n            })\n            .flat_map(|hash| {\n                let qstate = &self.queries[hash];\n                qstate\n                    .query\n                    .plans_fragments()\n                    .map(move |plan_fragment| (qstate, plan_fragment))\n            })\n            // If N clients are subscribed to a query,\n            // we copy the DatabaseTableUpdate N times,\n            // which involves cloning BSATN (binary) or product values (json).\n            .fold(FoldState::default(), |mut acc, (qstate, plan)| {\n                let table_id = plan.subscribed_table_id();\n                let table_name = plan.subscribed_table_name().clone();\n                // Store at most one copy for both the serialization to BSATN and JSON.\n                // Each subscriber gets to pick which of these they want,\n                // but we only fill `ops_bin_uncompressed` and `ops_json` at most once.\n                // The former will be `Some(_)` if some subscriber uses `Protocol::Binary`\n                // and the latter `Some(_)` if some subscriber uses `Protocol::Text`.\n                //\n                // Previously we were compressing each `QueryUpdate` within a `TransactionUpdate`.\n                // The reason was simple - many clients can subscribe to the same query.\n                // If we compress `TransactionUpdate`s independently for each client,\n                // we could be doing a lot of redundant compression.\n                //\n                // However the risks associated with this approach include:\n                //   1. We have to hold the tx lock when compressing\n                //   2. A potentially worse compression ratio\n                //   3. Extra decompression overhead on the client\n                //\n                // Because transaction processing is currently single-threaded,\n                // the risks of holding the tx lock for longer than necessary,\n                // as well as additional the message processing overhead on the client,\n                // outweighed the benefit of reduced cpu with the former approach.\n                let mut ops_bin_uncompressed: Option<(ws_v1::CompressableQueryUpdate<ws_v1::BsatnFormat>, _, _)> = None;\n                let mut ops_json: Option<(ws_v1::QueryUpdate<ws_v1::JsonFormat>, _, _)> = None;\n\n                fn memo_encode<F: BuildableWebsocketFormat>(\n                    updates: &UpdatesRelValue<'_>,\n                    memory: &mut Option<(F::QueryUpdate, u64, usize)>,\n                    metrics: &mut ExecutionMetrics,\n                    rlb_pool: &impl RowListBuilderSource<F>,\n                ) -> ws_v1::SingleQueryUpdate<F> {\n                    let (update, num_rows, num_bytes) = memory\n                        .get_or_insert_with(|| {\n                            // TODO(centril): consider pushing the encoding of each row into\n                            // `eval_delta` instead, to avoid building the temporary `Vec`s in `UpdatesRelValue`.\n                            let encoded = updates.encode::<F>(rlb_pool);\n                            // The first time we insert into this map, we call encode.\n                            // This is when we serialize the rows to BSATN/JSON.\n                            // Hence this is where we increment `bytes_scanned`.\n                            metrics.bytes_scanned += encoded.2;\n                            encoded\n                        })\n                        .clone();\n                    // We call this function for each query,\n                    // and for each client subscribed to it.\n                    // Therefore every time we call this function,\n                    // we update the `bytes_sent_to_clients` metric.\n                    metrics.bytes_sent_to_clients += num_bytes;\n                    ws_v1::SingleQueryUpdate { update, num_rows }\n                }\n\n                let clients_for_query = qstate.all_v1_clients();\n\n                match eval_delta(tx, &mut acc.metrics, plan) {\n                    Err(err) => {\n                        tracing::error!(\n                            message = \"Query errored during tx update\",\n                            sql = qstate.query.sql,\n                            reason = ?err,\n                        );\n                        let err = DBError::WithSql {\n                            sql: qstate.query.sql.as_str().into(),\n                            error: Box::new(err.into()),\n                        }\n                        .to_string()\n                        .into_boxed_str();\n\n                        acc.errs.extend(clients_for_query.map(|id| (*id, err.clone())))\n                    }\n                    // The query didn't return any rows to update\n                    Ok(None) => {}\n                    // The query did return updates - process them and add them to the accumulator\n                    Ok(Some(delta_updates)) => {\n                        let row_iter = clients_for_query.map(|id| {\n                            let client = &self.clients[id].outbound_ref;\n                            let update = match client.config.protocol {\n                                Protocol::Binary => Bsatn(memo_encode::<ws_v1::BsatnFormat>(\n                                    &delta_updates,\n                                    &mut ops_bin_uncompressed,\n                                    &mut acc.metrics,\n                                    bsatn_rlb_pool,\n                                )),\n                                Protocol::Text => Json(memo_encode::<ws_v1::JsonFormat>(\n                                    &delta_updates,\n                                    &mut ops_json,\n                                    &mut acc.metrics,\n                                    &JsonRowListBuilderFakePool,\n                                )),\n                            };\n                            ClientUpdate {\n                                id: *id,\n                                table_id,\n                                table_name: table_name.clone(),\n                                update,\n                            }\n                        });\n                        acc.updates.extend(row_iter);\n                    }\n                }\n\n                acc\n            });\n\n        (updates, errs, metrics)\n    }\n}\n\nstruct SendWorkerClient {\n    /// This flag is set if an error occurs during a tx update.\n    /// It will be cleaned up async or on resubscribe.\n    ///\n    /// [`Arc`]ed so that this can be updated by [`Self::run`]\n    /// and observed by [`SubscriptionManager::remove_dropped_clients`].\n    dropped: Arc<AtomicBool>,\n    outbound_ref: Client,\n}\n\nimpl SendWorkerClient {\n    fn is_dropped(&self) -> bool {\n        self.dropped.load(Ordering::Relaxed)\n    }\n\n    fn is_cancelled(&self) -> bool {\n        self.outbound_ref.is_cancelled()\n    }\n}\n\n/// Asynchronous background worker which aggregates each of the clients' updates from a [`ComputedQueries`]\n/// into `DbUpdate`s and then sends them to the clients' WebSocket workers.\n///\n/// See comment on the `send_worker_tx` field in [`SubscriptionManager`] for motivation.\nstruct SendWorker {\n    /// Receiver end of the [`SubscriptionManager`]'s `send_worker_tx` channel.\n    rx: mpsc::UnboundedReceiver<SendWorkerMessage>,\n\n    /// `subscription_send_queue_length` metric labeled for this database's `Identity`.\n    ///\n    /// If `Some`, this metric will be decremented each time we pop a [`ComputedQueries`] from `rx`.\n    ///\n    /// Will be `None` in contexts where there is no database `Identity` to use as label,\n    /// i.e. in tests.\n    queue_length_metric: Option<IntGauge>,\n\n    /// Mirror of the [`SubscriptionManager`]'s `clients` map local to this actor.\n    ///\n    /// Updated by [`SendWorkerMessage::AddClient`] and [`SendWorkerMessage::RemoveClient`] messages\n    /// sent along `self.rx`.\n    clients: HashMap<ClientId, SendWorkerClient>,\n\n    /// The `Identity` which labels the `queue_length_metric`.\n    ///\n    /// If `Some`, this type's `drop` method will do `remove_label_values` to clean up the metric on exit.\n    database_identity_to_clean_up_metric: Option<Identity>,\n\n    /// A map (re)used by [`SendWorker::send_one_computed_queries`]\n    /// to avoid creating new allocations.\n    table_updates_client_id_table_id: HashMap<(ClientId, TableId), SwitchedTableUpdate>,\n\n    /// A map (re)used by [`SendWorker::send_one_computed_queries`]\n    /// to avoid creating new allocations.\n    table_updates_client_id: HashMap<ClientId, SwitchedDbUpdate>,\n}\n\nimpl Drop for SendWorker {\n    fn drop(&mut self) {\n        if let Some(identity) = self.database_identity_to_clean_up_metric {\n            let _ = WORKER_METRICS\n                .subscription_send_queue_length\n                .remove_label_values(&identity);\n        }\n    }\n}\n\nimpl SendWorker {\n    fn is_client_dropped_or_cancelled(&self, client_id: &ClientId) -> bool {\n        self.clients\n            .get(client_id)\n            .is_some_and(|client| client.is_cancelled() || client.is_dropped())\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct BroadcastQueue(SenderWithGauge<SendWorkerMessage>);\n\n#[derive(thiserror::Error, Debug)]\n#[error(transparent)]\npub struct BroadcastError(#[from] mpsc::error::SendError<SendWorkerMessage>);\n\nimpl BroadcastQueue {\n    fn send(&self, message: SendWorkerMessage) -> Result<(), BroadcastError> {\n        self.0.send(message)?;\n        Ok(())\n    }\n\n    pub fn send_client_message_v2(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        tx_offset: Option<TransactionOffset>,\n        message: impl Into<ws_v2::ServerMessage>,\n    ) -> Result<(), BroadcastError> {\n        self.0.send(SendWorkerMessage::SendMessage {\n            recipient,\n            tx_offset,\n            message: OutboundMessage::V2(message.into()),\n        })?;\n        Ok(())\n    }\n\n    pub fn send_client_message_v1(\n        &self,\n        recipient: Arc<ClientConnectionSender>,\n        tx_offset: Option<TransactionOffset>,\n        message: impl Into<SerializableMessage>,\n    ) -> Result<(), BroadcastError> {\n        self.0.send(SendWorkerMessage::SendMessage {\n            recipient,\n            tx_offset,\n            message: OutboundMessage::V1(message.into()),\n        })?;\n        Ok(())\n    }\n}\npub fn spawn_send_worker(metric_database_identity: Option<Identity>) -> BroadcastQueue {\n    SendWorker::spawn_new(metric_database_identity)\n}\nimpl SendWorker {\n    fn new(\n        rx: mpsc::UnboundedReceiver<SendWorkerMessage>,\n        queue_length_metric: Option<IntGauge>,\n        database_identity_to_clean_up_metric: Option<Identity>,\n    ) -> Self {\n        Self {\n            rx,\n            queue_length_metric,\n            clients: Default::default(),\n            database_identity_to_clean_up_metric,\n            table_updates_client_id_table_id: <_>::default(),\n            table_updates_client_id: <_>::default(),\n        }\n    }\n\n    // Spawn a new send worker.\n    // If a `metric_database_identity` is provided, we will decrement the corresponding\n    // `subscription_send_queue_length` metric, and clean it up on drop.\n    fn spawn_new(metric_database_identity: Option<Identity>) -> BroadcastQueue {\n        let metric = metric_database_identity.map(|identity| {\n            WORKER_METRICS\n                .subscription_send_queue_length\n                .with_label_values(&identity)\n        });\n        let (send_worker_tx, rx) = mpsc::unbounded_channel();\n        tokio::spawn(Self::new(rx, metric.clone(), metric_database_identity).run());\n        BroadcastQueue(SenderWithGauge::new(send_worker_tx, metric))\n    }\n\n    async fn run(mut self) {\n        while let Some(message) = self.rx.recv().await {\n            if let Some(metric) = &self.queue_length_metric {\n                metric.dec();\n            }\n\n            match message {\n                SendWorkerMessage::AddClient {\n                    client_id,\n                    dropped,\n                    outbound_ref,\n                } => {\n                    self.clients\n                        .insert(client_id, SendWorkerClient { dropped, outbound_ref });\n                }\n                SendWorkerMessage::SendMessage {\n                    recipient,\n                    tx_offset,\n                    message,\n                } => match tx_offset {\n                    None => send_to_client(&recipient, None, message),\n                    Some(tx_offset) => {\n                        let Ok(tx_offset) = tx_offset.await else {\n                            tracing::error!(\"tx offset sender dropped, exiting send worker\");\n                            return;\n                        };\n                        send_to_client(&recipient, Some(tx_offset), message);\n                    }\n                },\n                SendWorkerMessage::RemoveClient(client_id) => {\n                    self.clients.remove(&client_id);\n                }\n                SendWorkerMessage::Broadcast { tx_offset, queries } => {\n                    let Ok(tx_offset) = tx_offset.await else {\n                        tracing::error!(\"tx offset sender dropped, exiting send worker\");\n                        return;\n                    };\n                    self.send_one_computed_queries(tx_offset, queries);\n                }\n            }\n        }\n    }\n\n    fn send_v1_computed_queries(\n        &mut self,\n        tx_offset: TxOffset,\n        updates: Vec<ClientUpdate>,\n        errs: Vec<(ClientId, Box<str>)>,\n        event: Arc<ModuleEvent>,\n        caller: Option<Arc<ClientConnectionSender>>,\n        module_def_version: RawModuleDefVersion,\n    ) {\n        use ws_v1::FormatSwitch::{Bsatn, Json};\n\n        let clients_with_errors = errs.iter().map(|(id, _)| id).collect::<HashSet<_>>();\n\n        let span = tracing::info_span!(\"eval_incr_group_messages_by_client\");\n\n        // Reuse the aggregation maps from the worker.\n        let client_table_id_updates = mem::take(&mut self.table_updates_client_id_table_id);\n        let client_id_updates = mem::take(&mut self.table_updates_client_id);\n\n        // For each subscriber, aggregate all the updates for the same table.\n        // That is, we build a map `(subscriber_id, table_id) -> updates`.\n        // A particular subscriber uses only one format,\n        // so their `TableUpdate` will contain either JSON (`Protocol::Text`)\n        // or BSATN (`Protocol::Binary`).\n        let mut client_table_id_updates = updates\n            .into_iter()\n            // Filter out dropped or cancelled clients\n            .filter(|upd| !self.is_client_dropped_or_cancelled(&upd.id))\n            // Filter out clients whose subscriptions failed\n            .filter(|upd| !clients_with_errors.contains(&upd.id))\n            // Do the aggregation.\n            .fold(client_table_id_updates, |mut tables, upd| {\n                let table_name = upd.table_name.into();\n                match tables.entry((upd.id, upd.table_id)) {\n                    Entry::Occupied(mut entry) => match entry.get_mut().zip_mut(upd.update) {\n                        Bsatn((tbl_upd, update)) => tbl_upd.push(update),\n                        Json((tbl_upd, update)) => tbl_upd.push(update),\n                    },\n                    Entry::Vacant(entry) => drop(entry.insert(match upd.update {\n                        Bsatn(update) => Bsatn(ws_v1::TableUpdate::new(upd.table_id, table_name, update)),\n                        Json(update) => Json(ws_v1::TableUpdate::new(upd.table_id, table_name, update)),\n                    })),\n                }\n                tables\n            });\n\n        // Each client receives a single list of updates per transaction.\n        // So before sending the updates to each client,\n        // we must stitch together the `TableUpdate*`s into an aggregated list.\n        let mut client_id_updates = client_table_id_updates\n            .drain()\n            // Do the aggregation.\n            .fold(client_id_updates, |mut updates, ((id, _), update)| {\n                let entry = updates.entry(id);\n                let entry = entry.or_insert_with(|| match &update {\n                    Bsatn(_) => Bsatn(<_>::default()),\n                    Json(_) => Json(<_>::default()),\n                });\n                match entry.zip_mut(update) {\n                    Bsatn((list, elem)) => list.tables.push(elem),\n                    Json((list, elem)) => list.tables.push(elem),\n                }\n                updates\n            });\n\n        drop(clients_with_errors);\n        drop(span);\n\n        let _span = tracing::info_span!(\"eval_send\").entered();\n\n        // We might have a known caller that hasn't been hidden from here..\n        // This caller may have subscribed to some query.\n        // If they haven't, we'll send them an empty update.\n        // Regardless, the update that we send to the caller, if we send any,\n        // is a full tx update, rather than a light one.\n        // That is, in the case of the caller, we don't respect the light setting.\n        if let Some(caller) = caller {\n            let caller_id = (caller.id.identity, caller.id.connection_id);\n            let database_update = client_id_updates\n                .remove(&caller_id)\n                .map(|update| SubscriptionUpdateMessage::from_event_and_update(&event, update))\n                .unwrap_or_else(|| {\n                    SubscriptionUpdateMessage::default_for_protocol(caller.config.protocol, event.request_id)\n                });\n            let message = TransactionUpdateMessage {\n                event: Some(event.clone()),\n                database_update,\n            };\n            send_to_client_v1(&caller, Some(tx_offset), message);\n        }\n\n        // Send all the other updates.\n        let hide_reducer_info_for_non_callers = matches!(module_def_version, RawModuleDefVersion::V10);\n\n        for (id, update) in client_id_updates.drain() {\n            let database_update = SubscriptionUpdateMessage::from_event_and_update(&event, update);\n            let client = self.clients[&id].outbound_ref.clone();\n            // Conditionally send out a full update or a light one otherwise.\n            let event = if hide_reducer_info_for_non_callers {\n                None\n            } else {\n                client.config.tx_update_full.then(|| event.clone())\n            };\n            let message = TransactionUpdateMessage { event, database_update };\n            send_to_client_v1(&client, Some(tx_offset), message);\n        }\n\n        // Put back the aggregation maps into the worker.\n        self.table_updates_client_id_table_id = client_table_id_updates;\n        self.table_updates_client_id = client_id_updates;\n\n        // Send error messages and mark clients for removal\n        for (id, message) in errs {\n            if let Some(client) = self.clients.get(&id) {\n                client.dropped.store(true, Ordering::Release);\n                send_to_client_v1(\n                    &client.outbound_ref,\n                    None,\n                    SubscriptionMessage {\n                        request_id: None,\n                        query_id: None,\n                        timer: None,\n                        result: SubscriptionResult::Error(SubscriptionError {\n                            table_id: None,\n                            message,\n                        }),\n                    },\n                );\n            }\n        }\n    }\n\n    fn send_v2_computed_queries(\n        &mut self,\n        tx_offset: TxOffset,\n        v2_updates: Vec<V2ClientUpdate>,\n        v2_errs: Vec<(SubscriptionIdV2, Box<str>)>,\n        event: Arc<ModuleEvent>,\n        caller: Option<Arc<ClientConnectionSender>>,\n    ) {\n        // Send subscription errors first so clients can discard state before updates arrive.\n        for ((client_id, query_set_id), error) in &v2_errs {\n            if self.is_client_dropped_or_cancelled(client_id) {\n                continue;\n            }\n            if let Some(client) = self.clients.get(client_id) {\n                send_to_client(\n                    &client.outbound_ref,\n                    Some(tx_offset),\n                    OutboundMessage::V2(ws_v2::ServerMessage::SubscriptionError(ws_v2::SubscriptionError {\n                        request_id: None,\n                        query_set_id: *query_set_id,\n                        error: error.clone(),\n                    })),\n                );\n            }\n        }\n\n        // Track these just in case we also get some updates for these subscriptions.\n        let subscriptions_with_errors: HashSet<&SubscriptionIdV2> =\n            v2_errs.iter().map(|(id, _)| id).collect::<HashSet<_>>();\n\n        type UpdateMapKey = (ClientId, ClientQuerySetId, TableName);\n        let mut grouped_updates: BTreeMap<UpdateMapKey, Vec<ws_v2::TableUpdateRows>> = BTreeMap::new();\n\n        let mut sent_to_caller = false;\n\n        for V2ClientUpdate {\n            id: client_id,\n            query_set_id,\n            // table_update,\n            table_name,\n            rows,\n        } in v2_updates\n        {\n            if subscriptions_with_errors.contains(&(client_id, query_set_id)) {\n                continue;\n            }\n            grouped_updates\n                .entry((client_id, query_set_id, table_name))\n                .or_default()\n                // .push(table_update);\n                .push(rows);\n        }\n\n        let grouped_by_client = grouped_updates.into_iter().group_by(|((client_id, ..), _)| *client_id);\n\n        for (client, updates) in &grouped_by_client {\n            if self.is_client_dropped_or_cancelled(&client) {\n                continue;\n            }\n\n            let mut query_set_updates: Vec<ws_v2::QuerySetUpdate> = vec![];\n\n            let grouped_by_query_set = updates.group_by(|((_, query_set_id, _), _)| *query_set_id);\n\n            for (query_set_id, qs_updates) in &grouped_by_query_set {\n                let table_updates: Vec<ws_v2::TableUpdate> = qs_updates\n                    .into_iter()\n                    .map(|((_, _, table_name), rows)| ws_v2::TableUpdate {\n                        table_name: table_name.into(),\n                        rows: rows.into_boxed_slice(),\n                    })\n                    .collect();\n                query_set_updates.push(ws_v2::QuerySetUpdate {\n                    query_set_id,\n                    tables: table_updates.into_boxed_slice(),\n                });\n            }\n\n            let transaction_update: ws_v2::TransactionUpdate = ws_v2::TransactionUpdate {\n                query_sets: query_set_updates.into_boxed_slice(),\n            };\n            match caller {\n                Some(ref caller) if (caller.id.identity, caller.id.connection_id) == client => {\n                    // Don't send the update to the caller, since they already have the most up-to-date information.\n                    let rok = ws_v2::ReducerOk {\n                        ret_value: event.reducer_return_value.clone().unwrap_or_default(),\n                        transaction_update,\n                    };\n                    let server_message = ws_v2::ServerMessage::ReducerResult(ws_v2::ReducerResult {\n                        request_id: event.request_id.unwrap(), // TODO: Handle error here.\n                        timestamp: event.timestamp,\n                        result: ws_v2::ReducerOutcome::Ok(rok),\n                    });\n                    send_to_client(caller, Some(tx_offset), OutboundMessage::V2(server_message));\n                    sent_to_caller = true;\n                    continue;\n                }\n                _ => {\n                    // Send the update to the client.\n                    send_to_client(\n                        &self.clients[&client].outbound_ref.clone(),\n                        Some(tx_offset),\n                        OutboundMessage::V2(ws_v2::ServerMessage::TransactionUpdate(transaction_update)),\n                    );\n                }\n            }\n        }\n        if !sent_to_caller && let Some(caller) = caller {\n            let server_message = ws_v2::ServerMessage::ReducerResult(ws_v2::ReducerResult {\n                request_id: event.request_id.unwrap(), // TODO: Handle error here.\n                timestamp: event.timestamp,\n                result: ws_v2::ReducerOutcome::Ok(ws_v2::ReducerOk {\n                    ret_value: event.reducer_return_value.clone().unwrap_or_default(),\n                    transaction_update: ws_v2::TransactionUpdate {\n                        query_sets: vec![].into_boxed_slice(),\n                    },\n                }),\n            });\n            send_to_client(&caller, Some(tx_offset), OutboundMessage::V2(server_message));\n        }\n    }\n\n    fn send_one_computed_queries(\n        &mut self,\n        tx_offset: TxOffset,\n        ComputedQueries {\n            updates,\n            v2_updates,\n            errs,\n            v2_errs,\n            event,\n            caller,\n            module_def_version,\n        }: ComputedQueries,\n    ) {\n        let (v1_caller, v2_caller) = match caller {\n            Some(caller) if caller.config.version == WsVersion::V1 => (Some(caller), None),\n            Some(caller) => (None, Some(caller)),\n            None => (None, None),\n        };\n        self.send_v1_computed_queries(tx_offset, updates, errs, event.clone(), v1_caller, module_def_version);\n        self.send_v2_computed_queries(tx_offset, v2_updates, v2_errs, event, v2_caller);\n    }\n}\n\nfn send_to_client_v1(\n    client: &ClientConnectionSender,\n    tx_offset: Option<TxOffset>,\n    message: impl Into<SerializableMessage>,\n) {\n    if let Err(e) = client.send_message(tx_offset, OutboundMessage::V1(message.into())) {\n        tracing::warn!(%client.id, \"failed to send update message to client: {e}\")\n    }\n}\nfn send_to_client(\n    client: &ClientConnectionSender,\n    tx_offset: Option<TxOffset>,\n    message: OutboundMessage,\n    //message: impl Into<SerializableMessage>,\n) {\n    tracing::trace!(client = %client.id, tx_offset, \"send_to_client\");\n    if let Err(e) = client.send_message(tx_offset, message) {\n        tracing::warn!(%client.id, \"failed to send update message to client: {e}\")\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::{sync::Arc, time::Duration};\n\n    use spacetimedb_client_api_messages::websocket::{v1 as ws_v1, v2 as ws_v2};\n    use spacetimedb_lib::AlgebraicValue;\n    use spacetimedb_lib::{error::ResultTest, identity::AuthCtx, AlgebraicType, ConnectionId, Identity, Timestamp};\n    use spacetimedb_primitives::{ColId, TableId};\n    use spacetimedb_sats::product;\n    use spacetimedb_schema::def::RawModuleDefVersion;\n    use spacetimedb_schema::reducer_name::ReducerName;\n    use spacetimedb_schema::table_name::TableName;\n    use spacetimedb_subscription::SubscriptionPlan;\n    use tokio::sync::oneshot;\n\n    use super::{Plan, SubscriptionManager};\n    use crate::db::relational_db::tests_utils::with_read_only;\n    use crate::host::module_host::DatabaseTableUpdate;\n    use crate::sql::ast::SchemaViewer;\n    use crate::subscription::module_subscription_manager::ClientQueryId;\n    use crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\n    use crate::subscription::tx::DeltaTx;\n    use crate::{\n        client::{ClientActorId, ClientConfig, ClientConnectionSender, ClientName},\n        db::relational_db::{tests_utils::TestDB, RelationalDB},\n        energy::EnergyQuanta,\n        host::{\n            module_host::{DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall},\n            ArgsTuple,\n        },\n        subscription::execution_unit::QueryHash,\n    };\n    use spacetimedb_datastore::execution_context::Workload;\n\n    fn create_table(db: &RelationalDB, name: &str) -> ResultTest<TableId> {\n        Ok(db.create_table_for_test(name, &[(\"a\", AlgebraicType::U8)], &[])?)\n    }\n\n    fn compile_plan(db: &RelationalDB, sql: &str) -> ResultTest<Arc<Plan>> {\n        compile_plan_with_auth(db, sql, AuthCtx::for_testing())\n    }\n\n    fn compile_plan_with_auth(db: &RelationalDB, sql: &str, auth: AuthCtx) -> ResultTest<Arc<Plan>> {\n        with_read_only(db, |tx| {\n            let tx = SchemaViewer::new(&*tx, &auth);\n            let (plans, has_param) = SubscriptionPlan::compile(sql, &tx, &auth).unwrap();\n            let hash = QueryHash::from_string(sql, auth.caller(), has_param);\n            Ok(Arc::new(Plan::new(plans, hash, sql.into())))\n        })\n    }\n\n    fn id(connection_id: u128) -> (Identity, ConnectionId) {\n        (Identity::ZERO, ConnectionId::from_u128(connection_id))\n    }\n\n    fn client(connection_id: u128, db: &Arc<RelationalDB>) -> ClientConnectionSender {\n        let (identity, connection_id) = id(connection_id);\n        client_with_identity(identity, connection_id, db)\n    }\n\n    fn client_with_identity(\n        identity: Identity,\n        connection_id: ConnectionId,\n        db: &Arc<RelationalDB>,\n    ) -> ClientConnectionSender {\n        ClientConnectionSender::dummy(\n            ClientActorId {\n                identity,\n                connection_id,\n                name: ClientName(0),\n            },\n            ClientConfig::for_test(),\n            db.clone(),\n        )\n    }\n\n    #[test]\n    fn test_subscribe_legacy() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let id = id(0);\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.set_legacy_subscription(client.clone(), [plan.clone()]);\n\n        assert!(subscriptions.contains_query(&hash));\n        assert!(subscriptions.contains_legacy_subscription(&id, &hash));\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_single_adds_table_mapping() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), plan.clone(), query_id)?;\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_v2_adds_table_mapping() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let query_set_id: ws_v2::QuerySetId = ws_v2::QuerySetId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        let added = subscriptions.add_subscription_v2(client.clone(), vec![plan.clone()], query_set_id)?;\n        assert_eq!(added.len(), 1);\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        let subscription_id = (client_id, query_set_id);\n        let ci = subscriptions.clients.get(&client_id).expect(\"client not found\");\n        let query_hashes = ci\n            .v2_subscriptions\n            .get(&subscription_id)\n            .expect(\"subscription not found\");\n        assert!(query_hashes.contains(&hash));\n        assert!(subscriptions.queries[&hash].v2_subscriptions.contains(&subscription_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_v2_allows_same_query_multiple_query_sets() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        let added = subscriptions.add_subscription_v2(client.clone(), vec![plan.clone()], ws_v2::QuerySetId::new(1))?;\n        assert_eq!(added.len(), 1);\n        let added = subscriptions.add_subscription_v2(client.clone(), vec![plan.clone()], ws_v2::QuerySetId::new(2))?;\n        assert_eq!(added.len(), 1);\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        let query_state = &subscriptions.queries[&hash];\n        assert!(query_state\n            .v2_subscriptions\n            .contains(&(client_id, ws_v2::QuerySetId::new(1))));\n        assert!(query_state\n            .v2_subscriptions\n            .contains(&(client_id, ws_v2::QuerySetId::new(2))));\n\n        let ci = subscriptions.clients.get(&client_id).expect(\"client not found\");\n        assert_eq!(ci.subscription_ref_count[&hash], 2);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_remove_subscription_v2_returns_queries_and_keeps_shared_query() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription_v2(client.clone(), vec![plan.clone()], ws_v2::QuerySetId::new(1))?;\n        subscriptions.add_subscription_v2(client.clone(), vec![plan.clone()], ws_v2::QuerySetId::new(2))?;\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        let removed = subscriptions.remove_subscription_v2(client_id, ws_v2::QuerySetId::new(1))?;\n        assert_eq!(removed.len(), 1);\n        assert_eq!(removed[0].hash, hash);\n\n        let query_state = &subscriptions.queries[&hash];\n        assert!(!query_state\n            .v2_subscriptions\n            .contains(&(client_id, ws_v2::QuerySetId::new(1))));\n        assert!(query_state\n            .v2_subscriptions\n            .contains(&(client_id, ws_v2::QuerySetId::new(2))));\n        assert_eq!(subscriptions.clients[&client_id].subscription_ref_count[&hash], 1);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unsubscribe_from_the_only_subscription() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), plan.clone(), query_id)?;\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        subscriptions.remove_subscription(client_id, query_id)?;\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unsubscribe_with_unknown_query_id_fails() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), plan.clone(), query_id)?;\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        assert!(subscriptions\n            .remove_subscription(client_id, ws_v1::QueryId::new(2))\n            .is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_and_unsubscribe_with_duplicate_queries() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), plan.clone(), query_id)?;\n        subscriptions.add_subscription(client.clone(), plan.clone(), ws_v1::QueryId::new(2))?;\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        subscriptions.remove_subscription(client_id, query_id)?;\n\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    /// A very simple test case of a duplicate query.\n    #[test]\n    fn test_subscribe_and_unsubscribe_with_duplicate_queries_multi() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        let added_query = subscriptions.add_subscription_multi(client.clone(), vec![plan.clone()], query_id)?;\n        assert!(added_query.len() == 1);\n        assert_eq!(added_query[0].hash, hash);\n        let second_one =\n            subscriptions.add_subscription_multi(client.clone(), vec![plan.clone()], ws_v1::QueryId::new(2))?;\n        assert!(second_one.is_empty());\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        let removed_queries = subscriptions.remove_subscription(client_id, query_id)?;\n        assert!(removed_queries.is_empty());\n\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n        let removed_queries = subscriptions.remove_subscription(client_id, ws_v1::QueryId::new(2))?;\n        assert!(removed_queries.len() == 1);\n        assert_eq!(removed_queries[0].hash, hash);\n\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unsubscribe_doesnt_remove_other_clients() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let clients = (0..3).map(|i| Arc::new(client(i, &db))).collect::<Vec<_>>();\n\n        // All of the clients are using the same query id.\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(clients[0].clone(), plan.clone(), query_id)?;\n        subscriptions.add_subscription(clients[1].clone(), plan.clone(), query_id)?;\n        subscriptions.add_subscription(clients[2].clone(), plan.clone(), query_id)?;\n\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        let client_ids = clients\n            .iter()\n            .map(|client| (client.id.identity, client.id.connection_id))\n            .collect::<Vec<_>>();\n        subscriptions.remove_subscription(client_ids[0], query_id)?;\n        // There are still two left.\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n        subscriptions.remove_subscription(client_ids[1], query_id)?;\n        // There is still one left.\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n        subscriptions.remove_subscription(client_ids[2], query_id)?;\n        // Now there are no subscribers.\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unsubscribe_all_doesnt_remove_other_clients() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let clients = (0..3).map(|i| Arc::new(client(i, &db))).collect::<Vec<_>>();\n\n        // All of the clients are using the same query id.\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(clients[0].clone(), plan.clone(), query_id)?;\n        subscriptions.add_subscription(clients[1].clone(), plan.clone(), query_id)?;\n        subscriptions.add_subscription(clients[2].clone(), plan.clone(), query_id)?;\n\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        let client_ids = clients\n            .iter()\n            .map(|client| (client.id.identity, client.id.connection_id))\n            .collect::<Vec<_>>();\n        subscriptions.remove_all_subscriptions(&client_ids[0]);\n        assert!(!subscriptions.contains_client(&client_ids[0]));\n        // There are still two left.\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n        subscriptions.remove_all_subscriptions(&client_ids[1]);\n        // There is still one left.\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n        assert!(!subscriptions.contains_client(&client_ids[1]));\n        subscriptions.remove_all_subscriptions(&client_ids[2]);\n        // Now there are no subscribers.\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n        assert!(!subscriptions.contains_client(&client_ids[2]));\n\n        Ok(())\n    }\n\n    // This test has a single client with 3 queries of different tables, and tests removing them.\n    #[test]\n    fn test_multiple_queries() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_names = [\"T\", \"S\", \"U\"];\n        let table_ids = table_names\n            .iter()\n            .map(|name| create_table(&db, name))\n            .collect::<ResultTest<Vec<_>>>()?;\n        let queries = table_names\n            .iter()\n            .map(|name| format!(\"select * from {name}\"))\n            .map(|sql| compile_plan(&db, &sql))\n            .collect::<ResultTest<Vec<_>>>()?;\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), queries[0].clone(), ws_v1::QueryId::new(1))?;\n        subscriptions.add_subscription(client.clone(), queries[1].clone(), ws_v1::QueryId::new(2))?;\n        subscriptions.add_subscription(client.clone(), queries[2].clone(), ws_v1::QueryId::new(3))?;\n        for i in 0..3 {\n            assert!(subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        subscriptions.remove_subscription(client_id, ws_v1::QueryId::new(1))?;\n        assert!(!subscriptions.query_reads_from_table(&queries[0].hash(), &table_ids[0]));\n        // Assert that the rest are there.\n        for i in 1..3 {\n            assert!(subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        // Now remove the final two at once.\n        subscriptions.remove_all_subscriptions(&client_id);\n        for i in 0..3 {\n            assert!(!subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_multiple_query_sets() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_names = [\"T\", \"S\", \"U\"];\n        let table_ids = table_names\n            .iter()\n            .map(|name| create_table(&db, name))\n            .collect::<ResultTest<Vec<_>>>()?;\n        let queries = table_names\n            .iter()\n            .map(|name| format!(\"select * from {name}\"))\n            .map(|sql| compile_plan(&db, &sql))\n            .collect::<ResultTest<Vec<_>>>()?;\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        let added =\n            subscriptions.add_subscription_multi(client.clone(), vec![queries[0].clone()], ws_v1::QueryId::new(1))?;\n        assert_eq!(added.len(), 1);\n        assert_eq!(added[0].hash, queries[0].hash());\n        let added =\n            subscriptions.add_subscription_multi(client.clone(), vec![queries[1].clone()], ws_v1::QueryId::new(2))?;\n        assert_eq!(added.len(), 1);\n        assert_eq!(added[0].hash, queries[1].hash());\n        let added =\n            subscriptions.add_subscription_multi(client.clone(), vec![queries[2].clone()], ws_v1::QueryId::new(3))?;\n        assert_eq!(added.len(), 1);\n        assert_eq!(added[0].hash, queries[2].hash());\n        for i in 0..3 {\n            assert!(subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        let client_id = (client.id.identity, client.id.connection_id);\n        let removed = subscriptions.remove_subscription(client_id, ws_v1::QueryId::new(1))?;\n        assert_eq!(removed.len(), 1);\n        assert_eq!(removed[0].hash, queries[0].hash());\n        assert!(!subscriptions.query_reads_from_table(&queries[0].hash(), &table_ids[0]));\n        // Assert that the rest are there.\n        for i in 1..3 {\n            assert!(subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        // Now remove the final two at once.\n        subscriptions.remove_all_subscriptions(&client_id);\n        for i in 0..3 {\n            assert!(!subscriptions.query_reads_from_table(&queries[i].hash(), &table_ids[i]));\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_internals_for_search_args() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"t\")?;\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n\n        // Subscribe to queries that have search arguments\n        let queries = (0u8..5)\n            .map(|name| format!(\"select * from t where a = {name}\"))\n            .map(|sql| compile_plan(&db, &sql))\n            .collect::<ResultTest<Vec<_>>>()?;\n\n        for (i, query) in queries.iter().enumerate().take(5) {\n            let added = subscriptions.add_subscription_multi(\n                client.clone(),\n                vec![query.clone()],\n                ws_v1::QueryId::new(i as u32),\n            )?;\n            assert_eq!(added.len(), 1);\n            assert_eq!(added[0].hash, queries[i].hash);\n        }\n\n        // Assert this table has a search parameter\n        assert!(subscriptions.table_has_search_param(table_id, ColId(0)));\n\n        for (i, query) in queries.iter().enumerate().take(5) {\n            assert!(subscriptions.query_has_search_arg(query.hash, table_id, ColId(0), AlgebraicValue::U8(i as u8)));\n\n            // Only one of `query_reads_from_table` and `query_has_search_arg` can be true at any given time\n            assert!(!subscriptions.query_reads_from_table(&queries[i].hash, &table_id));\n        }\n\n        // Remove one of the subscriptions\n        let query_id = ws_v1::QueryId::new(2);\n        let client_id = (client.id.identity, client.id.connection_id);\n        let removed = subscriptions.remove_subscription(client_id, query_id)?;\n        assert_eq!(removed.len(), 1);\n\n        // We haven't removed the other subscriptions,\n        // so this table should still have a search parameter.\n        assert!(subscriptions.table_has_search_param(table_id, ColId(0)));\n\n        // We should have removed the search argument for this query\n        assert!(!subscriptions.query_reads_from_table(&queries[2].hash, &table_id));\n        assert!(!subscriptions.query_has_search_arg(queries[2].hash, table_id, ColId(0), AlgebraicValue::U8(2)));\n\n        for (i, query) in queries.iter().enumerate().take(5) {\n            if i != 2 {\n                assert!(subscriptions.query_has_search_arg(\n                    query.hash,\n                    table_id,\n                    ColId(0),\n                    AlgebraicValue::U8(i as u8)\n                ));\n            }\n        }\n\n        // Remove all of the subscriptions\n        subscriptions.remove_all_subscriptions(&client_id);\n\n        // We should no longer record a search parameter for this table\n        assert!(!subscriptions.table_has_search_param(table_id, ColId(0)));\n        for (i, query) in queries.iter().enumerate().take(5) {\n            assert!(!subscriptions.query_has_search_arg(query.hash, table_id, ColId(0), AlgebraicValue::U8(i as u8)));\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_search_args_for_selects() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"t\")?;\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n\n        let queries = (0u8..5)\n            .map(|name| format!(\"select * from t where a = {name}\"))\n            .chain(std::iter::once(String::from(\"select * from t\")))\n            .map(|sql| compile_plan(&db, &sql))\n            .collect::<ResultTest<Vec<_>>>()?;\n\n        for (i, query) in queries.iter().enumerate() {\n            subscriptions.add_subscription_multi(client.clone(), vec![query.clone()], ws_v1::QueryId::new(i as u32))?;\n        }\n\n        let hash_for_2 = queries[2].hash;\n        let hash_for_3 = queries[3].hash;\n        let hash_for_5 = queries[5].hash;\n\n        // Which queries are relevant for this table update? Only:\n        //\n        // select * from t where a = 2\n        // select * from t where a = 3\n        // select * from t\n        let table_update = DatabaseTableUpdate {\n            table_id,\n            table_name: TableName::for_test(\"t\"),\n            inserts: [product![2u8]].into(),\n            deletes: [product![3u8]].into(),\n        };\n\n        let hashes = subscriptions\n            .queries_for_table_update(&table_update, |_, _| None)\n            .collect::<Vec<_>>();\n\n        assert!(hashes.len() == 3);\n        assert!(hashes.contains(&&hash_for_2));\n        assert!(hashes.contains(&&hash_for_3));\n        assert!(hashes.contains(&&hash_for_5));\n\n        // Which queries are relevant for this table update?\n        // Only: select * from t\n        let table_update = DatabaseTableUpdate {\n            table_id,\n            table_name: TableName::for_test(\"t\"),\n            inserts: [product![8u8]].into(),\n            deletes: [product![9u8]].into(),\n        };\n\n        let hashes = subscriptions\n            .queries_for_table_update(&table_update, |_, _| None)\n            .collect::<Vec<_>>();\n\n        assert!(hashes.len() == 1);\n        assert!(hashes.contains(&&hash_for_5));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_search_args_for_join() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let schema = [(\"id\", AlgebraicType::U8), (\"a\", AlgebraicType::U8)];\n\n        let t_id = db.create_table_for_test(\"t\", &schema, &[0.into()])?;\n        let s_id = db.create_table_for_test(\"s\", &schema, &[0.into()])?;\n\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n\n        let plan = compile_plan(&db, \"select t.* from t join s on t.id = s.id where s.a = 1\")?;\n        let hash = plan.hash;\n\n        subscriptions.add_subscription_multi(client.clone(), vec![plan], ws_v1::QueryId::new(0))?;\n\n        // Do we need to evaluate the above join query for this table update?\n        // Yes, because the above query does not filter on `t`.\n        // Therefore we must evaluate it for any update on `t`.\n        let table_update = DatabaseTableUpdate {\n            table_id: t_id,\n            table_name: TableName::for_test(\"t\"),\n            inserts: [product![0u8, 0u8]].into(),\n            deletes: [].into(),\n        };\n\n        let hashes = subscriptions\n            .queries_for_table_update(&table_update, |_, _| None)\n            .cloned()\n            .collect::<Vec<_>>();\n\n        assert_eq!(hashes, vec![hash]);\n\n        // Do we need to evaluate the above join query for this table update?\n        // Yes, because `s.a = 1`.\n        let table_update = DatabaseTableUpdate {\n            table_id: s_id,\n            table_name: TableName::for_test(\"s\"),\n            inserts: [product![0u8, 1u8]].into(),\n            deletes: [].into(),\n        };\n\n        let hashes = subscriptions\n            .queries_for_table_update(&table_update, |_, _| None)\n            .cloned()\n            .collect::<Vec<_>>();\n\n        assert_eq!(hashes, vec![hash]);\n\n        // Do we need to evaluate the above join query for this table update?\n        // No, because `s.a != 1`.\n        let table_update = DatabaseTableUpdate {\n            table_id: s_id,\n            table_name: TableName::for_test(\"s\"),\n            inserts: [product![0u8, 2u8]].into(),\n            deletes: [].into(),\n        };\n\n        let hashes = subscriptions\n            .queries_for_table_update(&table_update, |_, _| None)\n            .cloned()\n            .collect::<Vec<_>>();\n\n        assert!(hashes.is_empty());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_fails_with_duplicate_request_id() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.add_subscription(client.clone(), plan.clone(), query_id)?;\n\n        assert!(subscriptions\n            .add_subscription(client.clone(), plan.clone(), query_id)\n            .is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_multi_fails_with_duplicate_request_id() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n\n        let client = Arc::new(client(0, &db));\n\n        let query_id: ClientQueryId = ws_v1::QueryId::new(1);\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        let result = subscriptions.add_subscription_multi(client.clone(), vec![plan.clone()], query_id)?;\n        assert_eq!(result[0].hash, plan.hash);\n\n        assert!(subscriptions\n            .add_subscription_multi(client.clone(), vec![plan.clone()], query_id)\n            .is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_unsubscribe() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let id = id(0);\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.set_legacy_subscription(client, [plan]);\n        subscriptions.remove_all_subscriptions(&id);\n\n        assert!(!subscriptions.contains_query(&hash));\n        assert!(!subscriptions.contains_legacy_subscription(&id, &hash));\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_subscribe_idempotent() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let id = id(0);\n        let client = Arc::new(client(0, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.set_legacy_subscription(client.clone(), [plan.clone()]);\n        subscriptions.set_legacy_subscription(client.clone(), [plan.clone()]);\n\n        assert!(subscriptions.contains_query(&hash));\n        assert!(subscriptions.contains_legacy_subscription(&id, &hash));\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        subscriptions.remove_all_subscriptions(&id);\n\n        assert!(!subscriptions.contains_query(&hash));\n        assert!(!subscriptions.contains_legacy_subscription(&id, &hash));\n        assert!(!subscriptions.query_reads_from_table(&hash, &table_id));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_share_queries_full() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let table_id = create_table(&db, \"T\")?;\n        let sql = \"select * from T\";\n        let plan = compile_plan(&db, sql)?;\n        let hash = plan.hash();\n\n        let id0 = id(0);\n        let client0 = Arc::new(client(0, &db));\n\n        let id1 = id(1);\n        let client1 = Arc::new(client(1, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.set_legacy_subscription(client0, [plan.clone()]);\n        subscriptions.set_legacy_subscription(client1, [plan.clone()]);\n\n        assert!(subscriptions.contains_query(&hash));\n        assert!(subscriptions.contains_legacy_subscription(&id0, &hash));\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash));\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        subscriptions.remove_all_subscriptions(&id0);\n\n        assert!(subscriptions.contains_query(&hash));\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash));\n        assert!(subscriptions.query_reads_from_table(&hash, &table_id));\n\n        assert!(!subscriptions.contains_legacy_subscription(&id0, &hash));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_share_queries_partial() -> ResultTest<()> {\n        let db = TestDB::durable()?;\n\n        let t = create_table(&db, \"T\")?;\n        let s = create_table(&db, \"S\")?;\n\n        let scan = \"select * from T\";\n        let select0 = \"select * from T where a = 0\";\n        let select1 = \"select * from S where a = 1\";\n\n        let plan_scan = compile_plan(&db, scan)?;\n        let plan_select0 = compile_plan(&db, select0)?;\n        let plan_select1 = compile_plan(&db, select1)?;\n\n        let hash_scan = plan_scan.hash();\n        let hash_select0 = plan_select0.hash();\n        let hash_select1 = plan_select1.hash();\n\n        let id0 = id(0);\n        let client0 = Arc::new(client(0, &db));\n\n        let id1 = id(1);\n        let client1 = Arc::new(client(1, &db));\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n        let mut subscriptions = SubscriptionManager::for_test_without_metrics();\n        subscriptions.set_legacy_subscription(client0, [plan_scan.clone(), plan_select0.clone()]);\n        subscriptions.set_legacy_subscription(client1, [plan_scan.clone(), plan_select1.clone()]);\n\n        assert!(subscriptions.contains_query(&hash_scan));\n        assert!(subscriptions.contains_query(&hash_select0));\n        assert!(subscriptions.contains_query(&hash_select1));\n\n        assert!(subscriptions.contains_legacy_subscription(&id0, &hash_scan));\n        assert!(subscriptions.contains_legacy_subscription(&id0, &hash_select0));\n\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash_scan));\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash_select1));\n\n        assert!(subscriptions.query_reads_from_table(&hash_scan, &t));\n        assert!(subscriptions.query_has_search_arg(hash_select0, t, ColId(0), AlgebraicValue::U8(0)));\n        assert!(subscriptions.query_has_search_arg(hash_select1, s, ColId(0), AlgebraicValue::U8(1)));\n\n        assert!(!subscriptions.query_reads_from_table(&hash_scan, &s));\n        assert!(!subscriptions.query_reads_from_table(&hash_select0, &t));\n        assert!(!subscriptions.query_reads_from_table(&hash_select1, &s));\n        assert!(!subscriptions.query_reads_from_table(&hash_select0, &s));\n        assert!(!subscriptions.query_reads_from_table(&hash_select1, &t));\n\n        subscriptions.remove_all_subscriptions(&id0);\n\n        assert!(subscriptions.contains_query(&hash_scan));\n        assert!(subscriptions.contains_query(&hash_select1));\n        assert!(!subscriptions.contains_query(&hash_select0));\n\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash_scan));\n        assert!(subscriptions.contains_legacy_subscription(&id1, &hash_select1));\n\n        assert!(!subscriptions.contains_legacy_subscription(&id0, &hash_scan));\n        assert!(!subscriptions.contains_legacy_subscription(&id0, &hash_select0));\n\n        assert!(subscriptions.query_reads_from_table(&hash_scan, &t));\n        assert!(subscriptions.query_has_search_arg(hash_select1, s, ColId(0), AlgebraicValue::U8(1)));\n\n        assert!(!subscriptions.query_reads_from_table(&hash_select1, &s));\n        assert!(!subscriptions.query_reads_from_table(&hash_scan, &s));\n        assert!(!subscriptions.query_reads_from_table(&hash_select1, &t));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_caller_transaction_update_without_subscription() -> ResultTest<()> {\n        // test if a transaction update is sent to the reducer caller even if\n        // the caller haven't subscribed to any updates\n        let db = TestDB::durable()?;\n\n        let id0 = Identity::ZERO;\n        let client0 = ClientActorId::for_test(id0);\n        let config = ClientConfig::for_test();\n        let (client0, mut rx) = ClientConnectionSender::dummy_with_channel(client0, config, (*db).clone());\n\n        let runtime = tokio::runtime::Runtime::new().unwrap();\n        let _rt = runtime.enter();\n        let subscriptions = SubscriptionManager::for_test_without_metrics();\n\n        let event = Arc::new(ModuleEvent {\n            timestamp: Timestamp::now(),\n            caller_identity: id0,\n            caller_connection_id: Some(client0.id.connection_id),\n            function_call: ModuleFunctionCall {\n                reducer: Some(ReducerName::for_test(\"DummyReducer\")),\n                reducer_id: u32::MAX.into(),\n                args: ArgsTuple::nullary(),\n            },\n            status: EventStatus::Committed(DatabaseUpdate::default()),\n            reducer_return_value: None,\n            energy_quanta_used: EnergyQuanta::ZERO,\n            host_execution_duration: Duration::default(),\n            request_id: None,\n            timer: None,\n        });\n\n        // This block ensures that the transaction is released before waiting\n        // for a message to appear on `rx`.\n        // The message won't be sent until the transaction offset is known,\n        // and it is known when the transaction commits.\n        {\n            let (offset_tx, offset_rx) = oneshot::channel();\n            let tx = scopeguard::guard(db.begin_tx(Workload::Update), |tx| {\n                let (tx_offset, tx_metrics, reducer) = db.release_tx(tx);\n                let _ = offset_tx.send(tx_offset);\n                db.report_read_tx_metrics(reducer, tx_metrics);\n            });\n            let delta_tx = DeltaTx::from(&*tx);\n            let bsatn_rlb_pool = BsatnRowListBuilderPool::new();\n            let _ = subscriptions.eval_updates_sequential(\n                (&delta_tx, offset_rx),\n                &bsatn_rlb_pool,\n                RawModuleDefVersion::V9OrEarlier,\n                event,\n                Some(Arc::new(client0)),\n            );\n        }\n\n        runtime.block_on(async move {\n            tokio::time::timeout(Duration::from_millis(20), async move {\n                rx.recv().await.expect(\"Expected at least one message\");\n            })\n            .await\n            .expect(\"Timed out waiting for a message to the client\");\n        });\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/query.rs",
    "content": "use super::execution_unit::QueryHash;\nuse super::module_subscription_manager::Plan;\nuse crate::db::relational_db::Tx;\nuse crate::error::{DBError, SubscriptionError};\nuse crate::sql::ast::SchemaViewer;\nuse once_cell::sync::Lazy;\nuse regex::Regex;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_execution::Datastore;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_subscription::SubscriptionPlan;\n\nstatic WHITESPACE: Lazy<Regex> = Lazy::new(|| Regex::new(r\"^\\s*$\").unwrap());\nstatic SUBSCRIBE_TO_ALL_TABLES_REGEX: Lazy<Regex> =\n    Lazy::new(|| Regex::new(r\"^\\s*(?i)\\bSELECT\\s+\\*\\s+FROM\\s+\\*\\s*$\").unwrap());\n\n/// Is this string all whitespace?\npub fn is_whitespace_or_empty(sql: &str) -> bool {\n    WHITESPACE.is_match_at(sql, 0)\n}\n\n/// Is this a `SELECT * FROM *` query?\npub fn is_subscribe_to_all_tables(sql: &str) -> bool {\n    SUBSCRIBE_TO_ALL_TABLES_REGEX.is_match_at(sql, 0)\n}\n\n/// Compile a string into a single read-only query.\npub fn compile_read_only_query(auth: &AuthCtx, tx: &Tx, input: &str) -> Result<Plan, DBError> {\n    if is_whitespace_or_empty(input) {\n        return Err(SubscriptionError::Empty.into());\n    }\n\n    let tx = SchemaViewer::new(tx, auth);\n    let (plans, has_param) = SubscriptionPlan::compile(input, &tx, auth)?;\n    let hash = QueryHash::from_string(input, auth.caller(), has_param);\n    Ok(Plan::new(plans, hash, input.to_owned()))\n}\n\n/// Compile a string into a single read-only query with externally-computed hashes.\npub fn compile_query_with_hashes<Tx: Datastore + StateView>(\n    auth: &AuthCtx,\n    tx: &Tx,\n    input: &str,\n    hash: QueryHash,\n    hash_with_param: QueryHash,\n) -> Result<Plan, DBError> {\n    if is_whitespace_or_empty(input) {\n        return Err(SubscriptionError::Empty.into());\n    }\n\n    let tx = SchemaViewer::new(tx, auth);\n    let (plans, has_param) = SubscriptionPlan::compile(input, &tx, auth)?;\n\n    if auth.bypass_rls() || has_param {\n        return Ok(Plan::new(plans, hash_with_param, input.to_owned()));\n    }\n    Ok(Plan::new(plans, hash, input.to_owned()))\n}\n"
  },
  {
    "path": "crates/core/src/subscription/row_list_builder_pool.rs",
    "content": "use crate::subscription::websocket_building::{BsatnRowListBuilder, BuildableWebsocketFormat, RowListBuilderSource};\nuse bytes::{Bytes, BytesMut};\nuse core::sync::atomic::{AtomicUsize, Ordering};\nuse derive_more::Deref;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_data_structures::object_pool::{Pool, PooledObject};\nuse spacetimedb_memory_usage::MemoryUsage;\n\n/// The default buffer capacity, currently 4 KiB.\nconst DEFAULT_BUFFER_CAPACITY: usize = 4096;\n\n/// The pool can store at most 4 MiB worth of buffers.\n/// NOTE(centril): This hasn't been measured yet,\n/// but this should be a fairly good initial guestimate\n/// as the server would need to handle half as many tables in total.\n/// If there are two queries mentioning the same table,\n/// that counts as two tables.\nconst DEFAULT_POOL_CAPACITY: usize = 1024;\n\n/// New-type for `BytesMut` to deal with the orphan check.\npub struct PooledBuffer(BytesMut);\n\nimpl MemoryUsage for PooledBuffer {\n    fn heap_usage(&self) -> usize {\n        self.0.heap_usage()\n    }\n}\n\nimpl PooledObject for PooledBuffer {\n    type ResidentBytesStorage = AtomicUsize;\n\n    fn resident_object_bytes(storage: &Self::ResidentBytesStorage, _: usize) -> usize {\n        storage.load(Ordering::Relaxed)\n    }\n\n    fn add_to_resident_object_bytes(storage: &Self::ResidentBytesStorage, bytes: usize) {\n        storage.fetch_add(bytes, Ordering::Relaxed);\n    }\n\n    fn sub_from_resident_object_bytes(storage: &Self::ResidentBytesStorage, bytes: usize) {\n        storage.fetch_sub(bytes, Ordering::Relaxed);\n    }\n}\n\n/// The pool for [`BsatnRowListBuilder`]s.\n#[derive(Clone, Deref, Debug)]\npub struct BsatnRowListBuilderPool {\n    pool: Pool<PooledBuffer>,\n}\n\nimpl BsatnRowListBuilderPool {\n    /// Returns a new pool with the default maximum capacity.\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        let pool = Pool::new(DEFAULT_POOL_CAPACITY);\n        Self { pool }\n    }\n\n    /// Tries to reclaim the allocation of `buffer` into the pool\n    /// to be used when building a new list.\n    ///\n    /// In most calls, this method will do nothing,\n    /// as `buffer` will be shared between clients subscribing to the same query.\n    /// It's only on the last client that the refcount will be 1\n    /// which will then cause `put` to add the allocation into the buffer.\n    pub fn try_put(&self, buffer: Bytes) {\n        if let Ok(bytes) = buffer.try_into_mut() {\n            self.put(PooledBuffer(bytes));\n        }\n    }\n}\n\nimpl RowListBuilderSource<ws_v1::BsatnFormat> for BsatnRowListBuilderPool {\n    fn take_row_list_builder(&self) -> BsatnRowListBuilder {\n        let PooledBuffer(buffer) = self.pool.take(\n            |buffer| buffer.0.clear(),\n            || PooledBuffer(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),\n        );\n        BsatnRowListBuilder::new_from_bytes(buffer)\n    }\n}\n\n/// The \"pool\" for the builder for the [`JsonFormat`].\npub(crate) struct JsonRowListBuilderFakePool;\n\nimpl RowListBuilderSource<ws_v1::JsonFormat> for JsonRowListBuilderFakePool {\n    fn take_row_list_builder(&self) -> <ws_v1::JsonFormat as BuildableWebsocketFormat>::ListBuilder {\n        Vec::new()\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/subscription.rs",
    "content": "use super::execution_unit::QueryHash;\nuse super::module_subscription_manager::Plan;\nuse crate::db::relational_db::RelationalDB;\nuse crate::error::DBError;\nuse crate::sql::ast::SchemaViewer;\nuse spacetimedb_datastore::locking_tx_datastore::state_view::StateView;\nuse spacetimedb_lib::db::auth::StTableType;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_subscription::SubscriptionPlan;\nuse std::sync::Arc;\n\n/// Queries all visible user tables right now and turns them into subscription plans.\npub(crate) fn get_all<T, F, I>(\n    get_all_tables: F,\n    relational_db: &RelationalDB,\n    tx: &T,\n    auth: &AuthCtx,\n) -> Result<Vec<Plan>, DBError>\nwhere\n    T: StateView,\n    F: Fn(&RelationalDB, &T) -> Result<I, DBError>,\n    I: Iterator<Item = Arc<TableSchema>>,\n{\n    Ok(get_all_tables(relational_db, tx)?\n        .filter(|t| t.table_type == StTableType::User && auth.has_read_access(t.table_access) && !t.is_event)\n        .map(|schema| {\n            let sql = format!(\"SELECT * FROM {}\", schema.table_name);\n            let tx = SchemaViewer::new(tx, auth);\n            SubscriptionPlan::compile(&sql, &tx, auth).map(|(plans, has_param)| {\n                Plan::new(\n                    plans,\n                    QueryHash::from_string(&sql, auth.caller(), auth.bypass_rls() || has_param),\n                    sql,\n                )\n            })\n        })\n        .collect::<Result<_, _>>()?)\n}\n"
  },
  {
    "path": "crates/core/src/subscription/tx.rs",
    "content": "use super::module_subscription_manager::QueriedTableIndexIds;\nuse itertools::Either;\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\nuse spacetimedb_datastore::{\n    locking_tx_datastore::{state_view::StateView, TxId},\n    traits::TxData,\n};\nuse spacetimedb_execution::{Datastore, DeltaStore, Row};\nuse spacetimedb_lib::{query::Delta, AlgebraicValue, ProductValue};\nuse spacetimedb_primitives::{IndexId, TableId};\nuse spacetimedb_table::table::{IndexScanPointIter, IndexScanRangeIter, TableScanIter};\nuse std::{\n    collections::BTreeMap,\n    ops::{Deref, RangeBounds},\n    sync::Arc,\n};\n\n/// If an index is defined on a set of columns,\n/// and if that index is used in a subscription query,\n/// we build the very same index for each delta table.\n///\n/// Here a column value maps to its position(s) in the delta table.\ntype DeltaTableIndex = BTreeMap<AlgebraicValue, SmallVec<[usize; 1]>>;\n\n/// The set of indexes that have been built over a [TxData] delta table.\n#[derive(Default, Debug)]\npub struct DeltaTableIndexes {\n    inserts: HashMap<(TableId, IndexId), DeltaTableIndex>,\n    deletes: HashMap<(TableId, IndexId), DeltaTableIndex>,\n}\n\nimpl DeltaTableIndexes {\n    /// Get the btree index corresponding to `index_id` for the inserts of this delta table.\n    fn get_index_for_inserts(&self, table_id: TableId, index_id: IndexId) -> Option<&DeltaTableIndex> {\n        self.inserts.get(&(table_id, index_id))\n    }\n\n    /// Get the btree index corresponding to `index_id` for the deletes of this delta table.\n    fn get_index_for_deletes(&self, table_id: TableId, index_id: IndexId) -> Option<&DeltaTableIndex> {\n        self.deletes.get(&(table_id, index_id))\n    }\n\n    /// Construct the btree indexes required by the subscription manager for this delta table.\n    fn from_tx_data(tx: &TxId, data: &TxData, meta: &QueriedTableIndexIds) -> Self {\n        fn build_indexes_for_rows<'a>(\n            tx: &'a TxId,\n            meta: &'a QueriedTableIndexIds,\n            rows: impl Iterator<Item = (TableId, &'a Arc<[ProductValue]>)>,\n        ) -> HashMap<(TableId, IndexId), DeltaTableIndex> {\n            let mut indexes: HashMap<(TableId, IndexId), DeltaTableIndex> = HashMap::new();\n            for (table_id, rows) in rows {\n                if let Some(schema) = tx.get_schema(table_id) {\n                    // Fetch the column ids for each index\n                    let mut cols_for_index = vec![];\n                    for index_id in meta.index_ids_for_table(table_id) {\n                        cols_for_index.push((index_id, schema.col_list_for_index_id(index_id)));\n                    }\n                    for (i, row) in rows.iter().enumerate() {\n                        for (index_id, col_list) in &cols_for_index {\n                            if !col_list.is_empty() {\n                                indexes\n                                    .entry((table_id, *index_id))\n                                    .or_default()\n                                    .entry(row.project(col_list).unwrap())\n                                    .or_default()\n                                    .push(i);\n                            }\n                        }\n                    }\n                }\n            }\n            indexes\n        }\n\n        Self {\n            inserts: build_indexes_for_rows(tx, meta, data.inserts()),\n            deletes: build_indexes_for_rows(tx, meta, data.deletes()),\n        }\n    }\n}\n\n/// A wrapper around a read only tx delta queries\npub struct DeltaTx<'a> {\n    tx: &'a TxId,\n    data: Option<&'a TxData>,\n    indexes: DeltaTableIndexes,\n}\n\nimpl<'a> DeltaTx<'a> {\n    pub fn new(tx: &'a TxId, data: &'a TxData, indexes: &QueriedTableIndexIds) -> Self {\n        Self {\n            tx,\n            data: Some(data),\n            indexes: DeltaTableIndexes::from_tx_data(tx, data, indexes),\n        }\n    }\n}\n\nimpl Deref for DeltaTx<'_> {\n    type Target = TxId;\n\n    fn deref(&self) -> &Self::Target {\n        self.tx\n    }\n}\n\nimpl<'a> From<&'a TxId> for DeltaTx<'a> {\n    fn from(tx: &'a TxId) -> Self {\n        Self {\n            tx,\n            data: None,\n            indexes: DeltaTableIndexes::default(),\n        }\n    }\n}\n\nimpl Datastore for DeltaTx<'_> {\n    type TableIter<'a>\n        = TableScanIter<'a>\n    where\n        Self: 'a;\n\n    type RangeIndexIter<'a>\n        = IndexScanRangeIter<'a>\n    where\n        Self: 'a;\n\n    type PointIndexIter<'a>\n        = IndexScanPointIter<'a>\n    where\n        Self: 'a;\n\n    fn row_count(&self, table_id: TableId) -> u64 {\n        self.tx.row_count(table_id)\n    }\n\n    fn table_scan<'a>(&'a self, table_id: TableId) -> anyhow::Result<Self::TableIter<'a>> {\n        self.tx.table_scan(table_id)\n    }\n\n    fn index_scan_range<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> anyhow::Result<Self::RangeIndexIter<'a>> {\n        self.tx.index_scan_range(table_id, index_id, range)\n    }\n\n    fn index_scan_point<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        point: &AlgebraicValue,\n    ) -> anyhow::Result<Self::PointIndexIter<'a>> {\n        self.tx.index_scan_point(table_id, index_id, point)\n    }\n}\n\nimpl DeltaStore for DeltaTx<'_> {\n    fn num_inserts(&self, table_id: TableId) -> usize {\n        self.data\n            .and_then(|data| data.inserts_for_table(table_id).map(|rows| rows.len()))\n            .unwrap_or_default()\n    }\n\n    fn num_deletes(&self, table_id: TableId) -> usize {\n        self.data\n            .and_then(|data| data.deletes_for_table(table_id).map(|rows| rows.len()))\n            .unwrap_or_default()\n    }\n\n    fn inserts_for_table(&self, table_id: TableId) -> Option<std::slice::Iter<'_, ProductValue>> {\n        self.data\n            .and_then(|data| data.inserts_for_table(table_id).map(|rows| rows.iter()))\n    }\n\n    fn deletes_for_table(&self, table_id: TableId) -> Option<std::slice::Iter<'_, ProductValue>> {\n        self.data\n            .and_then(|data| data.deletes_for_table(table_id).map(|rows| rows.iter()))\n    }\n\n    fn index_scan_range_for_delta(\n        &self,\n        table_id: TableId,\n        index_id: IndexId,\n        delta: Delta,\n        range: impl RangeBounds<AlgebraicValue>,\n    ) -> impl Iterator<Item = Row<'_>> {\n        fn scan_index<'a>(\n            data: Option<&'a TxData>,\n            indexes: &'a DeltaTableIndexes,\n            table_id: TableId,\n            index_id: IndexId,\n            range: impl RangeBounds<AlgebraicValue>,\n            get_index: impl Fn(&DeltaTableIndexes, TableId, IndexId) -> Option<&DeltaTableIndex>,\n            get_ith_row: impl Fn(&TxData, TableId, usize) -> Option<&ProductValue>,\n        ) -> impl Iterator<Item = Row<'a>> {\n            data.and_then(move |data| {\n                get_index(indexes, table_id, index_id).map(move |btree| {\n                    btree\n                        .range(range)\n                        .flat_map(|(_, positions)| positions)\n                        .filter_map(move |i| get_ith_row(data, table_id, *i))\n                        .map(Row::Ref)\n                })\n            })\n            .into_iter()\n            .flatten()\n        }\n        match delta {\n            Delta::Inserts => Either::Left(scan_index(\n                self.data,\n                &self.indexes,\n                table_id,\n                index_id,\n                range,\n                DeltaTableIndexes::get_index_for_inserts,\n                TxData::get_ith_insert,\n            )),\n            Delta::Deletes => Either::Right(scan_index(\n                self.data,\n                &self.indexes,\n                table_id,\n                index_id,\n                range,\n                DeltaTableIndexes::get_index_for_deletes,\n                TxData::get_ith_delete,\n            )),\n        }\n    }\n\n    fn index_scan_point_for_delta(\n        &self,\n        table_id: TableId,\n        index_id: IndexId,\n        delta: Delta,\n        point: &AlgebraicValue,\n    ) -> impl Iterator<Item = Row<'_>> {\n        self.index_scan_range_for_delta(table_id, index_id, delta, point)\n    }\n}\n"
  },
  {
    "path": "crates/core/src/subscription/websocket_building.rs",
    "content": "use bytes::BytesMut;\nuse bytestring::ByteString;\nuse core::mem;\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_sats::bsatn::{self, ToBsatn};\nuse spacetimedb_sats::ser::serde::SerializeWrapper;\nuse spacetimedb_sats::Serialize;\nuse std::io;\nuse std::io::Write as _;\n\n/// A source of row list builders for a given [`BuildableWebsocketFormat`].\npub trait RowListBuilderSource<F: BuildableWebsocketFormat> {\n    /// Returns a row list builder from the source `self`.\n    fn take_row_list_builder(&self) -> F::ListBuilder;\n}\n\n/// A list of rows being built.\npub trait RowListBuilder: Default {\n    type FinishedList;\n\n    /// Push a row to the list in a serialized format.\n    fn push(&mut self, row: impl ToBsatn + Serialize);\n\n    /// Finish the in flight list, throwing away the capability to mutate.\n    fn finish(self) -> Self::FinishedList;\n}\n\npub trait BuildableWebsocketFormat: ws_v1::WebsocketFormat {\n    /// The builder for [`WebsocketFormat::List`].\n    type ListBuilder: RowListBuilder<FinishedList = Self::List>;\n\n    /// Encodes the `elems` to a list in the format and also returns the length of the list.\n    ///\n    /// Needs to be provided with an empty [`WebsocketFormat::ListBuilder`].\n    fn encode_list<R: ToBsatn + Serialize>(\n        mut list: Self::ListBuilder,\n        elems: impl Iterator<Item = R>,\n    ) -> (Self::List, u64) {\n        let mut num_rows = 0;\n        for elem in elems {\n            num_rows += 1;\n            list.push(elem);\n        }\n        (list.finish(), num_rows)\n    }\n\n    /// Convert a `QueryUpdate` into [`WebsocketFormat::QueryUpdate`].\n    /// This allows some formats to e.g., compress the update.\n    fn into_query_update(qu: ws_v1::QueryUpdate<Self>, compression: ws_v1::Compression) -> Self::QueryUpdate;\n}\n\nimpl BuildableWebsocketFormat for ws_v1::JsonFormat {\n    type ListBuilder = Self::List;\n\n    fn into_query_update(qu: ws_v1::QueryUpdate<Self>, _: ws_v1::Compression) -> Self::QueryUpdate {\n        qu\n    }\n}\n\nimpl RowListBuilder for Vec<ByteString> {\n    type FinishedList = Self;\n    fn push(&mut self, row: impl ToBsatn + Serialize) {\n        let value = serde_json::to_string(&SerializeWrapper::new(row)).unwrap().into();\n        self.push(value);\n    }\n    fn finish(self) -> Self::FinishedList {\n        self\n    }\n}\n\n/// A [`BsatnRowList`] that can be added to.\n#[derive(Default)]\npub struct BsatnRowListBuilder {\n    /// A size hint about `rows_data`\n    /// intended to facilitate parallel decode purposes on large initial updates.\n    size_hint: RowSizeHintBuilder,\n    /// The flattened byte array for a list of rows.\n    rows_data: BytesMut,\n}\n\n/// A [`RowSizeHint`] under construction.\n#[derive(Default)]\npub enum RowSizeHintBuilder {\n    /// We haven't seen any rows yet.\n    #[default]\n    Empty,\n    /// Each row in `rows_data` is of the same fixed size as specified here\n    /// but we don't know whether the size fits in `RowSize`\n    /// and we don't know whether future rows will also have this size.\n    FixedSizeDyn(usize),\n    /// Each row in `rows_data` is of the same fixed size as specified here\n    /// and we know that this will be the case for future rows as well.\n    FixedSizeStatic(ws_v1::RowSize),\n    /// The offsets into `rows_data` defining the boundaries of each row.\n    /// Only stores the offset to the start of each row.\n    /// The ends of each row is inferred from the start of the next row, or `rows_data.len()`.\n    /// The behavior of this is identical to that of `PackedStr`.\n    RowOffsets(Vec<ws_v1::RowOffset>),\n}\n\nimpl BsatnRowListBuilder {\n    /// Returns a new builder using an empty [`BytesMut`] for the `rows_data` buffer.\n    pub fn new_from_bytes(rows_data: BytesMut) -> Self {\n        let size_hint = <_>::default();\n        Self { size_hint, rows_data }\n    }\n}\n\nimpl RowListBuilder for BsatnRowListBuilder {\n    type FinishedList = ws_v1::BsatnRowList;\n\n    fn push(&mut self, row: impl ToBsatn + Serialize) {\n        use RowSizeHintBuilder::*;\n\n        // Record the length before. It will be the starting offset of `row`.\n        let len_before = self.rows_data.len();\n        // BSATN-encode the row directly to the buffer.\n        row.to_bsatn_extend(&mut self.rows_data).unwrap();\n\n        let encoded_len = || self.rows_data.len() - len_before;\n        let push_row_offset = |mut offsets: Vec<_>| {\n            offsets.push(len_before as u64);\n            RowOffsets(offsets)\n        };\n\n        let hint = mem::replace(&mut self.size_hint, Empty);\n        self.size_hint = match hint {\n            // Static size that is unchanging.\n            h @ FixedSizeStatic(_) => h,\n            // Dynamic size that is unchanging.\n            h @ FixedSizeDyn(size) if size == encoded_len() => h,\n            // Size mismatch for the dynamic fixed size.\n            // Now we must construct `RowOffsets` for all rows thus far.\n            // We know that `size != 0` here, as this was excluded when we had `Empty`.\n            FixedSizeDyn(size) => RowOffsets(collect_offsets_from_num_rows(1 + len_before / size, size)),\n            // Once there's a size for each row, we'll just add to it.\n            RowOffsets(offsets) => push_row_offset(offsets),\n            // First time a row is seen. Use `encoded_len()` as the hint.\n            // If we have a static layout, we'll always have a fixed size.\n            // Otherwise, let's start out with a potentially fixed size.\n            // In either case, if `encoded_len() == 0`, we have to store offsets,\n            // as we cannot recover the number of elements otherwise.\n            Empty => match row.static_bsatn_size() {\n                Some(0) => push_row_offset(Vec::new()),\n                Some(size) => FixedSizeStatic(size),\n                None => match encoded_len() {\n                    0 => push_row_offset(Vec::new()),\n                    size => FixedSizeDyn(size),\n                },\n            },\n        };\n    }\n\n    fn finish(self) -> Self::FinishedList {\n        let Self { size_hint, rows_data } = self;\n        let size_hint = match size_hint {\n            RowSizeHintBuilder::Empty => ws_v1::RowSizeHint::RowOffsets([].into()),\n            RowSizeHintBuilder::FixedSizeStatic(fs) => ws_v1::RowSizeHint::FixedSize(fs),\n            RowSizeHintBuilder::FixedSizeDyn(fs) => match u16::try_from(fs) {\n                Ok(fs) => ws_v1::RowSizeHint::FixedSize(fs),\n                Err(_) => {\n                    ws_v1::RowSizeHint::RowOffsets(collect_offsets_from_num_rows(rows_data.len() / fs, fs).into())\n                }\n            },\n            RowSizeHintBuilder::RowOffsets(ro) => ws_v1::RowSizeHint::RowOffsets(ro.into()),\n        };\n        let rows_data = rows_data.into();\n        ws_v1::BsatnRowList::new(size_hint, rows_data)\n    }\n}\n\nfn collect_offsets_from_num_rows(num_rows: usize, size: usize) -> Vec<u64> {\n    (0..num_rows).map(|i| i * size).map(|o| o as u64).collect()\n}\n\nimpl BuildableWebsocketFormat for ws_v1::BsatnFormat {\n    type ListBuilder = BsatnRowListBuilder;\n\n    fn into_query_update(qu: ws_v1::QueryUpdate<Self>, compression: ws_v1::Compression) -> Self::QueryUpdate {\n        let qu_len_would_have_been = bsatn::to_len(&qu).unwrap();\n\n        match decide_compression(qu_len_would_have_been, compression) {\n            ws_v1::Compression::None => ws_v1::CompressableQueryUpdate::Uncompressed(qu),\n            ws_v1::Compression::Brotli => {\n                let bytes = bsatn::to_vec(&qu).unwrap();\n                let mut out = Vec::new();\n                brotli_compress(&bytes, &mut out);\n                ws_v1::CompressableQueryUpdate::Brotli(out.into())\n            }\n            ws_v1::Compression::Gzip => {\n                let bytes = bsatn::to_vec(&qu).unwrap();\n                let mut out = Vec::new();\n                gzip_compress(&bytes, &mut out);\n                ws_v1::CompressableQueryUpdate::Gzip(out.into())\n            }\n        }\n    }\n}\n\npub fn decide_compression(len: usize, compression: ws_v1::Compression) -> ws_v1::Compression {\n    /// The threshold beyond which we start to compress messages.\n    /// 1KiB was chosen without measurement.\n    /// TODO(perf): measure!\n    const COMPRESS_THRESHOLD: usize = 1024;\n\n    if len > COMPRESS_THRESHOLD {\n        compression\n    } else {\n        ws_v1::Compression::None\n    }\n}\n\npub fn brotli_compress(bytes: &[u8], out: &mut impl io::Write) {\n    // We are optimizing for compression speed,\n    // so we choose the lowest (fastest) level of compression.\n    // Experiments on internal workloads have shown compression ratios between 7:1 and 10:1\n    // for large `SubscriptionUpdate` messages at this level.\n    const COMPRESSION_LEVEL: i32 = 1;\n\n    let params = brotli::enc::BrotliEncoderParams {\n        quality: COMPRESSION_LEVEL,\n        ..<_>::default()\n    };\n    let reader = &mut &bytes[..];\n    brotli::BrotliCompress(reader, out, &params).expect(\"should be able to BrotliCompress\");\n}\n\npub fn gzip_compress(bytes: &[u8], out: &mut impl io::Write) {\n    let mut encoder = flate2::write::GzEncoder::new(out, flate2::Compression::fast());\n    encoder.write_all(bytes).unwrap();\n    encoder.finish().expect(\"should be able to gzip compress `bytes`\");\n}\n"
  },
  {
    "path": "crates/core/src/util/jobs.rs",
    "content": "use std::panic::AssertUnwindSafe;\nuse std::sync::{Arc, Mutex, Weak};\n\nuse core_affinity::CoreId;\nuse futures::future::LocalBoxFuture;\nuse futures::FutureExt;\nuse indexmap::IndexMap;\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::map::HashMap;\nuse tokio::runtime;\nuse tokio::sync::{mpsc, oneshot, watch};\nuse tracing::Instrument;\n\n/// A handle to a pool of Tokio executors for running database WASM code on.\n///\n/// Each database has a [`SingleCoreExecutor`],\n/// a handle to a single-threaded Tokio runtime which is pinned to a specific CPU core.\n/// In multi-tenant environments, multiple databases' [`SingleCoreExecutor`]s may be handles on the same runtime/core,\n/// and a [`SingleCoreExecutor`] may occasionally be migrated to a different runtime/core to balance load.\n///\n/// Construct a `JobCores` via [`Self::from_pinned_cores`] or [`Self::without_pinned_cores`].\n/// A `JobCores` constructed without core pinning, including `from_pinned_cores` on an empty set,\n/// will spawn threads that are not pinned to any cores.\n///\n/// This handle is cheaply cloneable, but at least one handle must be kept alive.\n/// If all instances of it are dropped, load-balancing will no longer occur when\n/// threads exit or new threads are spawned.\n#[derive(Clone)]\npub struct JobCores {\n    inner: JobCoresInner,\n}\n\n#[derive(Clone)]\nenum JobCoresInner {\n    PinnedCores(Arc<Mutex<PinnedCoresExecutorManager>>),\n    NoPinning,\n}\n\nstruct PinnedCoresExecutorManager {\n    /// Channels to request that a [`SingleCoreExecutor`] move to a different core.\n    ///\n    /// The [`CoreId`] that an executor is pinned to is used as an index into\n    /// `self.cores` to make load-balancing decisions when freeing a database\n    /// executor in [`Self::deallocate`].\n    database_executor_move: HashMap<SingleCoreExecutorId, watch::Sender<CoreId>>,\n    cores: IndexMap<CoreId, CoreInfo>,\n    /// An index into `cores` of the next core to put a new job onto.\n    ///\n    /// This acts as a partition point in `cores`; all cores in `..index` have\n    /// one fewer job on them than the cores in `index..`.\n    next_core: usize,\n    next_id: SingleCoreExecutorId,\n}\n\n/// Remembers the [`SingleCoreExecutorId`]s for all databases sharing that executor.\n#[derive(Default)]\nstruct CoreInfo {\n    jobs: SmallVec<[SingleCoreExecutorId; 4]>,\n}\n\n#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]\nstruct SingleCoreExecutorId(usize);\n\nimpl JobCores {\n    /// Get an [`AllocatedCore`] for a job thread.\n    pub fn take(&self) -> AllocatedJobCore {\n        match &self.inner {\n            JobCoresInner::NoPinning => AllocatedJobCore::default(),\n            JobCoresInner::PinnedCores(manager) => {\n                let manager_weak = Arc::downgrade(manager);\n                let (database_executor_id, pinner) = manager.lock().unwrap().allocate();\n                let guard = LoadBalanceOnDropGuard {\n                    inner: Some((manager_weak, database_executor_id)),\n                };\n                AllocatedJobCore { guard, pinner }\n            }\n        }\n    }\n\n    /// Construct a [`JobCores`] which runs one Tokio runtime on each of the `cores`,\n    /// and pins each database to a particular runtime/core.\n    ///\n    /// If `cores` is empty, this falls back to [`Self::without_pinned_cores`]\n    /// and runs all databases in the `global_runtime`.\n    pub fn from_pinned_cores(cores: impl IntoIterator<Item = CoreId>) -> Self {\n        let cores: IndexMap<_, _> = cores.into_iter().map(|id| (id, CoreInfo::default())).collect();\n        let inner = if cfg!(feature = \"no-job-core-pinning\") || cores.is_empty() {\n            JobCoresInner::NoPinning\n        } else {\n            JobCoresInner::PinnedCores(Arc::new(Mutex::new(PinnedCoresExecutorManager {\n                database_executor_move: HashMap::default(),\n                cores,\n                next_core: 0,\n                next_id: SingleCoreExecutorId(0),\n            })))\n        };\n\n        Self { inner }\n    }\n\n    /// Construct a [`JobCores`] which does not perform any core pinning,\n    /// and just runs all database jobs in `global_runtime`.\n    ///\n    /// This will be used in deployments where there aren't enough available CPU cores\n    /// to reserve specific cores for database WASM execution.\n    pub const fn without_pinned_cores() -> Self {\n        Self {\n            inner: JobCoresInner::NoPinning,\n        }\n    }\n}\n\nimpl PinnedCoresExecutorManager {\n    /// Get a core for running database operations on,\n    /// and store state in `self` necessary to move that database to a new core\n    /// for load-balancing purposes.\n    ///\n    /// The returned [`SingleCoreExecutorId`] is an index into internal data structures in `self` (namely, `self.cores`)\n    /// which should be passed to [`Self::deallocate`] when the database is no longer using this executor.\n    /// This is done automatically by [`LoadBalanceOnDropGuard`].\n    ///\n    /// The returned [`CorePinner`] stores the [`CoreId`] on which the database\n    /// should run its compute-intensive jobs. This may occasionally be\n    /// replaced to balance databases among available cores, so databases should\n    /// either spawn [`CorePinner::run`] as a thread-local async task, or call\n    /// [`CorePinner::pin_now`] frequently.\n    fn allocate(&mut self) -> (SingleCoreExecutorId, CorePinner) {\n        // Determine the next job ID.\n        let database_executor_id = self.next_id;\n        self.next_id.0 += 1;\n\n        // Put the job ID into the next core.\n        let core_id = {\n            let (&core_id, core_info) = self\n                .cores\n                .get_index_mut(self.next_core)\n                .expect(\"`self.next_core < self.cores.len()`\");\n            core_info.jobs.push(database_executor_id);\n            core_id\n        };\n        // Move the next core one ahead, wrapping around the number of cores we have.\n        self.next_core = (self.next_core + 1) % self.cores.len();\n\n        // Record channels and details for moving a job to a different core.\n        let (move_core_tx, move_core_rx) = watch::channel(core_id);\n        self.database_executor_move.insert(database_executor_id, move_core_tx);\n\n        let core_pinner = CorePinner {\n            move_core_rx: Some(move_core_rx),\n        };\n        (database_executor_id, core_pinner)\n    }\n\n    /// Mark the executor at `id` as no longer in use, free internal state which tracks it,\n    /// and move other executors to different cores as necessary to maintain a balanced distribution.\n    ///\n    /// Called by [`LoadBalanceOnDropGuard`] when a [`SingleCoreExecutor`] is no longer in use.\n    fn deallocate(&mut self, id: SingleCoreExecutorId) {\n        // Determine the `CoreId` that will now have one less job.\n        // The `id`s came from `self.allocate()`,\n        // so there must be a `database_executor_move` for it.\n        let freed_core_id = *self\n            .database_executor_move\n            .remove(&id)\n            .expect(\"there should be a `database_executor_move` for `id`\")\n            .borrow();\n\n        let core_index = self.cores.get_index_of(&freed_core_id).unwrap();\n\n        // This core is now less busy than it should be - bump `next_core` back\n        // by 1 and steal a thread from the core there.\n        //\n        // This wraps around in the 0 case, so the partition point is simply\n        // moved to the end of the ring buffer.\n\n        let steal_from_index = self.next_core.checked_sub(1).unwrap_or(self.cores.len() - 1);\n\n        // If this core was already at `next_core - 1`, we don't need to steal from anywhere.\n        let (core_info, steal_from) = match self.cores.get_disjoint_indices_mut([core_index, steal_from_index]) {\n            Ok([(_, core), (_, steal_from)]) => (core, Some(steal_from)),\n            Err(_) => (&mut self.cores[core_index], None),\n        };\n\n        let pos = core_info.jobs.iter().position(|x| *x == id).unwrap();\n        // Swap remove because we don't care about ordering within `core_info.jobs`\n        core_info.jobs.swap_remove(pos);\n\n        if let Some(steal_from) = steal_from {\n            // This unwrap will never fail, since cores below `next_core` always have\n            // at least 1 thread on them. Edge case: if `next_core` is 0, `steal_from`\n            // would wrap around to the end - but when `next_core` is 0, every core has\n            // the same number of threads; so, if the last core is empty, all the cores\n            // would be empty, but we know that's impossible because we're deallocating\n            // a thread right now.\n            let stolen = steal_from.jobs.pop().unwrap();\n            // the way we pop and push here means that older job threads will be less\n            // likely to be repinned, while younger ones are liable to bounce around.\n            // Our use of `swap_remove` above makes this not entirely predictable, however.\n            core_info.jobs.push(stolen);\n            let migrate_tx = &self.database_executor_move[&stolen];\n            migrate_tx.send_replace(freed_core_id);\n        }\n\n        self.next_core = steal_from_index;\n    }\n}\n\n/// Returned from [`JobCores::take`]; represents a job thread allocated to a\n/// specific core.\n///\n/// The `guard` should be dropped when the job thread is no longer running, and\n/// the `pinner` should be ran on the job thread.\n#[derive(Default)]\npub struct AllocatedJobCore {\n    pub guard: LoadBalanceOnDropGuard,\n    pub pinner: CorePinner,\n}\n\nimpl AllocatedJobCore {\n    /// Spawn a [`SingleCoreExecutor`] allocated to this core.\n    pub fn spawn_async_executor(self) -> SingleCoreExecutor {\n        SingleCoreExecutor::spawn(self)\n    }\n}\n\n/// Used for pinning a job thread to an appropriate core, as determined by\n/// [`JobCores`].\n///\n/// Obtained from [`AllocatedJobCore.pinner`][AllocatedJobCore::pinner].\n/// You can either call [`run()`][Self::run] and poll it from the job thread,\n/// or call [`pin_now()`][Self::pin_now] once and then\n/// [`pin_if_changed()`][Self::pin_if_changed] in a loop.\n#[derive(Default, Clone)]\npub struct CorePinner {\n    move_core_rx: Option<watch::Receiver<CoreId>>,\n}\n\nimpl CorePinner {\n    #[inline]\n    fn do_pin(move_core_rx: &mut watch::Receiver<CoreId>) {\n        let core_id = *move_core_rx.borrow_and_update();\n        core_affinity::set_for_current(core_id);\n    }\n\n    /// Pin the current thread to the appropriate core.\n    pub fn pin_now(&mut self) {\n        if let Some(move_core_rx) = &mut self.move_core_rx {\n            Self::do_pin(move_core_rx);\n        }\n    }\n\n    /// Repin the current thread to the new appropriate core, if it's changed\n    /// since the last call to `pin_now()` or `pin_if_changed()`.\n    pub fn pin_if_changed(&mut self) {\n        if let Some(move_core_rx) = &mut self.move_core_rx\n            && let Ok(true) = move_core_rx.has_changed()\n        {\n            Self::do_pin(move_core_rx);\n        }\n    }\n\n    /// In a loop, wait until [`JobCores`] decides that the current thread\n    /// needs to move and then repin to the new core.\n    pub async fn run(self) {\n        let _not_send = std::marker::PhantomData::<*const ()>;\n        if let Some(mut move_core_rx) = self.move_core_rx {\n            while move_core_rx.changed().await.is_ok() {\n                Self::do_pin(&mut move_core_rx);\n            }\n        }\n    }\n}\n\n/// A handle to a Tokio executor which can be used to run WASM compute for a particular database.\n///\n/// Use [`Self::run_job`] to run futures, and [`Self::run_sync_job`] to run functions.\n///\n/// This handle is cheaply cloneable.\n/// When all handles on this database executor have been dropped,\n/// its use of the core to which it is pinned will be released,\n/// and other databases may be migrated to that core to balance load.\n#[derive(Clone)]\npub struct SingleCoreExecutor {\n    inner: Arc<SingleCoreExecutorInner>,\n}\n\nstruct SingleCoreExecutorInner {\n    /// The sending end of a channel over which we send jobs.\n    job_tx: mpsc::UnboundedSender<Box<dyn FnOnce() -> LocalBoxFuture<'static, ()> + Send>>,\n}\n\nimpl SingleCoreExecutor {\n    /// Spawn a `SingleCoreExecutor` on the given core.\n    fn spawn(core: AllocatedJobCore) -> Self {\n        let AllocatedJobCore { guard, mut pinner } = core;\n\n        let (job_tx, mut job_rx) = mpsc::unbounded_channel();\n\n        let inner = Arc::new(SingleCoreExecutorInner { job_tx });\n\n        let rt = runtime::Handle::current();\n        std::thread::spawn(move || {\n            let _guard = guard;\n            pinner.pin_now();\n\n            let _entered = rt.enter();\n            let local = tokio::task::LocalSet::new();\n\n            let job_loop = async {\n                while let Some(job) = job_rx.recv().await {\n                    local.spawn_local(job());\n                }\n            };\n\n            // Run the pinner on the same task as the job loop, so that the pinner still\n            // being alive doesn't prevent the runtime thread from ending.\n            rt.block_on(local.run_until(super::also_poll(job_loop, pinner.run())));\n\n            // The sender has closed; finish out any remaining tasks left on the set.\n            // This is very important to do - otherwise, in-progress tasks will be\n            // dropped and cancelled.\n            rt.block_on(local)\n        });\n\n        Self { inner }\n    }\n\n    /// Create a `SingleCoreExecutor` which runs jobs in [`tokio::runtime::Handle::current`].\n    ///\n    /// Callers should most likely instead construct a `SingleCoreExecutor` via [`JobCores::take`],\n    /// which will intelligently pin each database to a particular core.\n    /// This method should only be used for short-lived instances which do not perform intense computation,\n    /// e.g. to extract the schema by calling `describe_module`.\n    pub fn in_current_tokio_runtime() -> Self {\n        Self::spawn(AllocatedJobCore::default())\n    }\n\n    /// Run a job for this database executor.\n    pub async fn run_job<F, R>(&self, f: F) -> R\n    where\n        F: AsyncFnOnce() -> R + Send + 'static,\n        R: Send + 'static,\n    {\n        let span = tracing::Span::current();\n        let (tx, rx) = oneshot::channel();\n\n        self.inner\n            .job_tx\n            .send(Box::new(move || {\n                async move {\n                    let result = AssertUnwindSafe(f().instrument(span)).catch_unwind().await;\n                    if let Err(Err(_panic)) = tx.send(result) {\n                        tracing::warn!(\"uncaught panic on `SingleCoreExecutor`\")\n                    }\n                }\n                .boxed_local()\n            }))\n            .unwrap_or_else(|_| panic!(\"job thread exited\"));\n\n        match rx.await.unwrap() {\n            Ok(r) => r,\n            Err(e) => std::panic::resume_unwind(e),\n        }\n    }\n\n    /// Run `f` on this database executor and return its result.\n    pub async fn run_sync_job<F, R>(&self, f: F) -> R\n    where\n        F: FnOnce() -> R + Send + 'static,\n        R: Send + 'static,\n    {\n        self.run_job(async || f()).await\n    }\n}\n\n/// On drop, tells the [`JobCores`] that this database is no longer occupying its core,\n/// allowing databases from more-contended runtimes/cores to migrate there.\n#[derive(Default)]\npub struct LoadBalanceOnDropGuard {\n    inner: Option<(Weak<Mutex<PinnedCoresExecutorManager>>, SingleCoreExecutorId)>,\n}\n\nimpl Drop for LoadBalanceOnDropGuard {\n    fn drop(&mut self) {\n        if let Some((manager, database_executor_id)) = &self.inner\n            && let Some(cores) = manager.upgrade()\n        {\n            cores.lock().unwrap().deallocate(*database_executor_id);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/util/mod.rs",
    "content": "use futures::{Future, FutureExt};\nuse std::borrow::Cow;\nuse std::pin::pin;\nuse tokio::sync::oneshot;\nuse tracing::Span;\n\npub mod prometheus_handle;\n\npub mod jobs;\npub mod notify_once;\n\n// TODO: use String::from_utf8_lossy_owned once stabilized\npub(crate) fn string_from_utf8_lossy_owned(v: Vec<u8>) -> String {\n    match String::from_utf8_lossy(&v) {\n        // SAFETY: from_utf8_lossy() returned Borrowed, which means the original buffer is valid utf8\n        Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(v) },\n        Cow::Owned(s) => s,\n    }\n}\n\n#[tracing::instrument(level = \"trace\", skip_all)]\npub fn spawn_rayon<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> impl Future<Output = R> {\n    let span = tracing::Span::current();\n    let (tx, rx) = oneshot::channel();\n    rayon::spawn(|| {\n        let _entered = span.entered();\n        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));\n        if let Err(Err(_panic)) = tx.send(result) {\n            tracing::warn!(\"uncaught panic on threadpool\")\n        }\n    });\n    rx.map(|res| res.unwrap().unwrap_or_else(|err| std::panic::resume_unwind(err)))\n}\n\n/// Ergonomic wrapper for `tokio::task::spawn_blocking(f).await`.\n///\n/// If `f` panics, it will be bubbled up to the calling task.\npub async fn asyncify<F, R>(f: F) -> R\nwhere\n    F: FnOnce() -> R + Send + 'static,\n    R: Send + 'static,\n{\n    // Ensure that `f` executes in the current span context.\n    // If there is no current span, or it is disabled, `span` is disabled.\n    let span = Span::current();\n    tokio::task::spawn_blocking(move || {\n        let _enter = span.enter();\n        f()\n    })\n    .await\n    .unwrap_or_else(|e| match e.try_into_panic() {\n        Ok(panic_payload) => std::panic::resume_unwind(panic_payload),\n        // the only other variant is cancelled, which shouldn't happen because we don't cancel it.\n        Err(e) => panic!(\"Unexpected JoinError: {e}\"),\n    })\n}\n\n/// Await `fut`, while also polling `also`.\npub async fn also_poll<Fut: Future>(fut: Fut, also: impl Future<Output = ()>) -> Fut::Output {\n    let mut also = pin!(also.fuse());\n    let mut fut = pin!(fut);\n    std::future::poll_fn(|cx| {\n        let _ = also.poll_unpin(cx);\n        fut.poll_unpin(cx)\n    })\n    .await\n}\n"
  },
  {
    "path": "crates/core/src/util/notify_once.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::atomic::{AtomicBool, Ordering::SeqCst};\nuse std::task::{ready, Context, Poll};\nuse tokio::sync::{futures::Notified, Notify};\n\npub struct NotifyOnce {\n    notify: Notify,\n    flag: AtomicBool,\n}\n\nimpl NotifyOnce {\n    pub const fn new() -> Self {\n        Self {\n            notify: Notify::const_new(),\n            flag: AtomicBool::new(false),\n        }\n    }\n\n    // returns true if this is the first time notify() has been called\n    pub fn notify(&self) -> bool {\n        let prev = self.flag.swap(true, SeqCst);\n        self.notify.notify_waiters();\n        !prev\n    }\n\n    pub fn notified(&self) -> NotifiedOnce<'_> {\n        NotifiedOnce {\n            notified: self.notify.notified(),\n            flag: &self.flag,\n        }\n    }\n}\n\nimpl Default for NotifyOnce {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npin_project_lite::pin_project! {\n    pub struct NotifiedOnce<'a> {\n        #[pin]\n        notified: Notified<'a>,\n        flag: &'a AtomicBool,\n    }\n}\n\nimpl Future for NotifiedOnce<'_> {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let mut me = self.project();\n        while !me.flag.load(SeqCst) {\n            ready!(me.notified.as_mut().poll(cx))\n        }\n        Poll::Ready(())\n    }\n}\n"
  },
  {
    "path": "crates/core/src/util/prometheus_handle.rs",
    "content": "use std::time::Instant;\n\nuse prometheus::{Histogram, IntGauge};\n\n/// Decrements the inner [`IntGauge`] on drop.\npub struct GaugeInc {\n    gauge: IntGauge,\n}\nimpl Drop for GaugeInc {\n    #[inline]\n    fn drop(&mut self) {\n        self.gauge.dec();\n    }\n}\n\n/// Increment the given [`IntGauge`], and decrement it when the returned value goes out of scope.\n#[inline]\npub fn inc_scope(gauge: &IntGauge) -> GaugeInc {\n    gauge.inc();\n    GaugeInc { gauge: gauge.clone() }\n}\n\npub trait IntGaugeExt {\n    fn inc_scope(&self) -> GaugeInc;\n}\n\nimpl IntGaugeExt for IntGauge {\n    fn inc_scope(&self) -> GaugeInc {\n        inc_scope(self)\n    }\n}\n\n/// A scope guard for a timer,\n/// the total duration of which is written to a Histogram metric on drop.\npub struct TimerGuard {\n    histogram: Histogram,\n    timer: Instant,\n}\n\nimpl Drop for TimerGuard {\n    fn drop(&mut self) {\n        self.histogram.observe(self.timer.elapsed().as_secs_f64());\n    }\n}\n\npub trait HistogramExt {\n    fn with_timer(self, timer: Instant) -> TimerGuard;\n}\n\nimpl HistogramExt for Histogram {\n    fn with_timer(self, timer: Instant) -> TimerGuard {\n        TimerGuard { histogram: self, timer }\n    }\n}\n"
  },
  {
    "path": "crates/core/src/worker_metrics/mod.rs",
    "content": "use crate::hash::Hash;\nuse crate::messages::control_db::HostType;\nuse crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse once_cell::sync::Lazy;\nuse prometheus::{GaugeVec, HistogramVec, IntCounterVec, IntGaugeVec};\nuse spacetimedb_datastore::execution_context::WorkloadType;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_metrics::metrics_group;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_table::page_pool::PagePool;\nuse std::{sync::Once, time::Duration};\nuse tokio::{spawn, time::sleep};\n\nmetrics_group!(\n    pub struct WorkerMetrics {\n        #[name = spacetime_worker_connected_clients]\n        #[help = \"Number of clients connected to the worker.\"]\n        #[labels(database_identity: Identity)]\n        pub connected_clients: IntGaugeVec,\n\n        #[name = spacetime_worker_ws_clients_spawned]\n        #[help = \"Number of new ws client connections spawned. Counted after any on_connect reducers are run.\"]\n        #[labels(database_identity: Identity)]\n        pub ws_clients_spawned: IntGaugeVec,\n\n        #[name = spacetime_worker_ws_clients_aborted]\n        #[help = \"Number of ws client connections aborted by either the server or the client\"]\n        #[labels(database_identity: Identity)]\n        pub ws_clients_aborted: IntGaugeVec,\n\n        #[name = spacetime_worker_ws_clients_closed_connection]\n        #[help = \"Number of ws client connections closed by the client as opposed to being termiated by the server\"]\n        #[labels(database_identity: Identity)]\n        pub ws_clients_closed_connection: IntGaugeVec,\n\n        #[name = spacetime_websocket_requests_total]\n        #[help = \"The cumulative number of websocket request messages\"]\n        #[labels(database_identity: Identity, protocol: str)]\n        pub websocket_requests: IntCounterVec,\n\n        #[name = spacetime_websocket_request_msg_size]\n        #[help = \"The size of messages received on connected sessions\"]\n        #[labels(database_identity: Identity, protocol: str)]\n        pub websocket_request_msg_size: HistogramVec,\n\n        #[name = jemalloc_active_bytes]\n        #[help = \"Number of bytes in jemallocs heap\"]\n        #[labels(node_id: str)]\n        pub jemalloc_active_bytes: IntGaugeVec,\n\n        #[name = jemalloc_allocated_bytes]\n        #[help = \"Number of bytes in use by the application\"]\n        #[labels(node_id: str)]\n        pub jemalloc_allocated_bytes: IntGaugeVec,\n\n        #[name = jemalloc_resident_bytes]\n        #[help = \"Total memory used by jemalloc\"]\n        #[labels(node_id: str)]\n        pub jemalloc_resident_bytes: IntGaugeVec,\n\n        #[name = page_pool_resident_bytes]\n        #[help = \"Total memory used by the page pool\"]\n        #[labels(node_id: str)]\n        pub page_pool_resident_bytes: IntGaugeVec,\n\n        #[name = page_pool_dropped_pages]\n        #[help = \"Total number of pages dropped by the page pool\"]\n        #[labels(node_id: str)]\n        pub page_pool_dropped_pages: IntGaugeVec,\n\n        #[name = page_pool_new_pages_allocated]\n        #[help = \"Total number of fresh pages allocated by the page pool\"]\n        #[labels(node_id: str)]\n        pub page_pool_new_pages_allocated: IntGaugeVec,\n\n        #[name = page_pool_pages_reused]\n        #[help = \"Total number of pages reused by the page pool\"]\n        #[labels(node_id: str)]\n        pub page_pool_pages_reused: IntGaugeVec,\n\n        #[name = page_pool_pages_returned]\n        #[help = \"Total number of pages returned to the page pool\"]\n        #[labels(node_id: str)]\n        pub page_pool_pages_returned: IntGaugeVec,\n\n        #[name = bsatn_rlb_pool_resident_bytes]\n        #[help = \"Total memory used by the `BsatnRowListBuilderPool`\"]\n        #[labels(node_id: str)]\n        pub bsatn_rlb_pool_resident_bytes: IntGaugeVec,\n\n        #[name = bsatn_rlb_pool_dropped]\n        #[help = \"Total number of buffers dropped by the `BsatnRowListBuilderPool`\"]\n        #[labels(node_id: str)]\n        pub bsatn_rlb_pool_dropped: IntGaugeVec,\n\n        #[name = bsatn_rlb_pool_new_allocated]\n        #[help = \"Total number of fresh buffers allocated by the `BsatnRowListBuilderPool`\"]\n        #[labels(node_id: str)]\n        pub bsatn_rlb_pool_new_allocated: IntGaugeVec,\n\n        #[name = bsatn_rlb_pool_reused]\n        #[help = \"Total number of buffers reused by the `BsatnRowListBuilderPool`\"]\n        #[labels(node_id: str)]\n        pub bsatn_rlb_pool_reused: IntGaugeVec,\n\n        #[name = bsatn_rlb_pool_returned]\n        #[help = \"Total number of buffers returned to the `BsatnRowListBuilderPool`\"]\n        #[labels(node_id: str)]\n        pub bsatn_rlb_pool_returned: IntGaugeVec,\n\n        #[name = tokio_num_workers]\n        #[help = \"Number of core tokio workers\"]\n        #[labels(node_id: str)]\n        pub tokio_num_workers: IntGaugeVec,\n\n        #[name = tokio_num_blocking_threads]\n        #[help = \"Number of extra tokio threads for blocking tasks\"]\n        #[labels(node_id: str)]\n        pub tokio_num_blocking_threads: IntGaugeVec,\n\n        #[name = tokio_num_idle_blocking_threads]\n        #[help = \"Number of tokio blocking threads that are idle\"]\n        #[labels(node_id: str)]\n        pub tokio_num_idle_blocking_threads: IntGaugeVec,\n\n        #[name = tokio_num_alive_tasks]\n        #[help = \"Number of tokio tasks that are still alive\"]\n        #[labels(node_id: str)]\n        pub tokio_num_alive_tasks: IntGaugeVec,\n\n        #[name = tokio_global_queue_depth]\n        #[help = \"Number of tasks in tokios global queue\"]\n        #[labels(node_id: str)]\n        pub tokio_global_queue_depth: IntGaugeVec,\n\n        #[name = tokio_blocking_queue_depth]\n        #[help = \"Number of tasks in tokios blocking task queue\"]\n        #[labels(node_id: str)]\n        pub tokio_blocking_queue_depth: IntGaugeVec,\n\n        #[name = tokio_spawned_tasks_count]\n        #[help = \"Number of tokio tasks spawned\"]\n        #[labels(node_id: str)]\n        pub tokio_spawned_tasks_count: IntCounterVec,\n\n        #[name = tokio_remote_schedule_count]\n        #[help = \"Number of tasks spawned from outside the tokio runtime\"]\n        #[labels(node_id: str)]\n        pub tokio_remote_schedule_count: IntCounterVec,\n\n        #[name = tokio_local_queue_depth_total]\n        #[help = \"Total size of all tokio workers local queues\"]\n        #[labels(node_id: str)]\n        pub tokio_local_queue_depth_total: IntGaugeVec,\n\n        #[name = tokio_local_queue_depth_max]\n        #[help = \"Length of the longest tokio worker local queue\"]\n        #[labels(node_id: str)]\n        pub tokio_local_queue_depth_max: IntGaugeVec,\n\n        #[name = tokio_local_queue_depth_min]\n        #[help = \"Length of the shortest tokio worker local queue\"]\n        #[labels(node_id: str)]\n        pub tokio_local_queue_depth_min: IntGaugeVec,\n\n        #[name = tokio_steal_total]\n        #[help = \"Total number of tasks stolen from other workers\"]\n        #[labels(node_id: str)]\n        pub tokio_steal_total: IntCounterVec,\n\n        #[name = tokio_steal_operations_total]\n        #[help = \"Total number of times a worker tried to steal a chunk of tasks\"]\n        #[labels(node_id: str)]\n        pub tokio_steal_operations_total: IntCounterVec,\n\n        #[name = tokio_local_schedule_total]\n        #[help = \"Total number of tasks scheduled from worker threads\"]\n        #[labels(node_id: str)]\n        pub tokio_local_schedule_total: IntCounterVec,\n\n        #[name = tokio_overflow_total]\n        #[help = \"Total number of times a tokio worker overflowed its local queue\"]\n        #[labels(node_id: str)]\n        pub tokio_overflow_total: IntCounterVec,\n\n        #[name = tokio_busy_ratio_min]\n        #[help = \"Busy ratio of the least busy tokio worker\"]\n        #[labels(node_id: str)]\n        pub tokio_busy_ratio_min: GaugeVec,\n\n        #[name = tokio_busy_ratio_max]\n        #[help = \"Busy ratio of the most busy tokio worker\"]\n        #[labels(node_id: str)]\n        pub tokio_busy_ratio_max: GaugeVec,\n\n        #[name = tokio_busy_ratio_avg]\n        #[help = \"Avg busy ratio of tokio workers\"]\n        #[labels(node_id: str)]\n        pub tokio_busy_ratio_avg: GaugeVec,\n\n        #[name = tokio_mean_polls_per_park]\n        #[help = \"Number of tasks polls divided by the times an idle worker was parked\"]\n        #[labels(node_id: str)]\n        pub tokio_mean_polls_per_park: GaugeVec,\n\n        #[name = spacetime_websocket_sent_msg_size_bytes]\n        #[help = \"The size of messages sent to connected sessions\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        //\n        // Therefore we define specific buckets for this metric,\n        // since it has a different unit and a different distribution.\n        //\n        // In particular incremental update payloads could be smaller than 1KB,\n        // whereas initial subscription payloads could exceed 10MB.\n        #[buckets(100, 500, 1e3, 10e3, 100e3, 500e3, 1e6, 5e6, 10e6, 25e6, 50e6, 75e6, 100e6, 500e6)]\n        pub websocket_sent_msg_size: HistogramVec,\n\n        #[name = spacetime_websocket_sent_num_rows]\n        #[help = \"The number of rows sent to connected sessions\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        //\n        // Therefore we define specific buckets for this metric,\n        // since it has a different unit and a different distribution.\n        //\n        // In particular incremental updates could have fewer than 10 rows,\n        // whereas initial subscriptions could exceed 100K rows.\n        #[buckets(5, 10, 50, 100, 500, 1e3, 5e3, 10e3, 50e3, 100e3, 250e3, 500e3, 750e3, 1e6, 5e6)]\n        pub websocket_sent_num_rows: HistogramVec,\n\n        #[name = spacetime_websocket_serialize_secs]\n        #[help = \"How long it took to serialize and maybe compress an outgoing websocket message\"]\n        #[labels(db: Identity)]\n        #[buckets(0.001, 0.01, 0.05, 0.1, 0.25, 0.5, 1.0)]\n        pub websocket_serialize_secs: HistogramVec,\n\n        #[name = spacetime_worker_instance_operation_queue_length]\n        #[help = \"Length of the wait queue for access to a module instance.\"]\n        #[labels(database_identity: Identity)]\n        pub instance_queue_length: IntGaugeVec,\n\n        #[name = spacetime_worker_instance_operation_queue_length_histogram]\n        #[help = \"Length of the wait queue for access to a module instance.\"]\n        #[labels(database_identity: Identity)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        // Hence we need to define specific buckets for queue length.\n        #[buckets(0, 1, 2, 5, 10, 25, 50, 75, 100, 200, 300, 400, 500, 1000)]\n        pub instance_queue_length_histogram: HistogramVec,\n\n        #[name = spacetime_reducer_wait_time_sec]\n        #[help = \"The amount of time (in seconds) a reducer spends in the queue waiting to run\"]\n        #[labels(db: Identity, reducer: str)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        //\n        // However we expect a different value distribution for this metric.\n        // In particular the smallest bucket value is 5ms by default.\n        // But we expect many wait times to be on the order of microseconds.\n        #[buckets(100e-6, 500e-6, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10)]\n        pub reducer_wait_time: HistogramVec,\n\n        #[name = spacetime_worker_wasm_instance_errors_total]\n        #[help = \"The number of fatal WASM instance errors, such as reducer panics.\"]\n        #[labels(database_identity: Identity, module_hash: Hash, reducer_symbol: str)]\n        pub wasm_instance_errors: IntCounterVec,\n\n        #[name = spacetime_worker_sender_errors_total]\n        #[help = \"The number of sender errors returned from reducers.\"]\n        #[labels(database_identity: Identity, module_hash: Hash, reducer_symbol: str)]\n        pub sender_errors: IntCounterVec,\n\n        #[name = spacetime_worker_wasm_memory_bytes]\n        #[help = \"The number of bytes of linear memory allocated by the database's WASM module instance\"]\n        #[labels(database_identity: Identity)]\n        pub wasm_memory_bytes: IntGaugeVec,\n\n        #[name = spacetime_active_queries]\n        #[help = \"The number of active subscription queries\"]\n        #[labels(database_identity: Identity)]\n        pub subscription_queries: IntGaugeVec,\n\n        #[name = spacetime_request_round_trip_time]\n        #[help = \"The total time it takes for request to complete\"]\n        #[labels(txn_type: WorkloadType, database_identity: Identity, reducer_symbol: str)]\n        pub request_round_trip: HistogramVec,\n\n        #[name = spacetime_reducer_plus_query_duration_sec]\n        #[help = \"The time spent executing a reducer (in seconds), plus the time spent evaluating its subscription queries\"]\n        #[labels(db: Identity, reducer: str)]\n        pub reducer_plus_query_duration: HistogramVec,\n\n        #[name = spacetime_num_bytes_sent_to_clients_total]\n        #[help = \"The cumulative number of bytes sent to clients\"]\n        #[labels(txn_type: WorkloadType, db: Identity)]\n        pub bytes_sent_to_clients: IntCounterVec,\n\n        #[name = spacetime_subscription_send_queue_length]\n        #[help = \"The number of `ComputedQueries` waiting in the queue to be aggregated and broadcast by the `send_worker`\"]\n        #[labels(database_identity: Identity)]\n        pub subscription_send_queue_length: IntGaugeVec,\n\n        #[name = spacetime_total_incoming_queue_length]\n        #[help = \"The number of client -> server WebSocket messages waiting any client's incoming queue\"]\n        #[labels(db: Identity)]\n        pub total_incoming_queue_length: IntGaugeVec,\n\n        #[name = spacetime_total_outgoing_queue_length]\n        #[help = \"The number of server -> client WebSocket messages waiting in any client's outgoing queue\"]\n        #[labels(db: Identity)]\n        pub total_outgoing_queue_length: IntGaugeVec,\n\n        #[name = spacetime_replay_total_time_seconds]\n        #[help = \"Total time spent replaying a database upon restart, including snapshot read, snapshot restore and commitlog replay\"]\n        #[labels(db: Identity)]\n        // We expect a small number of observations per label\n        // (exactly one, for non-replicated databases, and one per leader change for replicated databases)\n        // so we'll just store a `Gauge` with the most recent observation for each database.\n        pub replay_total_time_seconds: GaugeVec,\n\n        #[name = spacetime_replay_snapshot_read_time_seconds]\n        #[help = \"Time spent reading a snapshot from disk before restoring the snapshot upon restart\"]\n        #[labels(db: Identity)]\n        pub replay_snapshot_read_time_seconds: GaugeVec,\n\n        #[name = spacetime_replay_snapshot_restore_time_seconds]\n        #[help = \"Time spent restoring a database from a snapshot after reading the snapshot and before commitlog replay upon restart\"]\n        #[labels(db: Identity)]\n        pub replay_snapshot_restore_time_seconds: GaugeVec,\n\n        #[name = spacetime_replay_commitlog_time_seconds]\n        #[help = \"Time spent replaying the commitlog after restoring from a snapshot upon restart\"]\n        #[labels(db: Identity)]\n        pub replay_commitlog_time_seconds: GaugeVec,\n\n        #[name = spacetime_replay_commitlog_num_commits]\n        #[help = \"Number of commits replayed after restoring from a snapshot upon restart\"]\n        #[labels(db: Identity)]\n        pub replay_commitlog_num_commits: IntGaugeVec,\n\n        #[name = spacetime_module_create_instance_time_seconds]\n        #[help = \"Time taken to construct a WASM instance or V8 isolate to run module code\"]\n        #[labels(db: Identity, module_type: HostType)]\n        // As of writing (pgoldman 2025-09-25), calls to `create_instance` are rare,\n        // as they happen only when an instance traps (panics).\n        // However, this is not once-per-process, unlike the above replay metrics.\n        // I (pgoldman 2025-09-25) am not sure what range or distribution of values to expect,\n        // so I'm making up some buckets based on what I imagine are the upper and lower bounds of plausibility.\n        #[buckets(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10, 50, 100)]\n        pub module_create_instance_time_seconds: HistogramVec,\n\n        #[name = spacetime_snapshot_creation_time_total_sec]\n        #[help = \"The time (in seconds) it took to take and store a database snapshot, including scheduling overhead\"]\n        #[labels(db: Identity)]\n        // Snapshot creation should take in the order of milliseconds,\n        // but log data suggests that there are outliers.\n        // So let's track a wide range of buckets to get a better picture.\n        //\n        // We also track the timing without `asyncify` scheduling overhead\n        // (`snapshot_creation_time_inner`), and the snapshot compression\n        // timing with / without scheduling overhead (`snapshot_compression_time_total`\n        // and `snapshot_compression_time_inner`, respectively).\n        //\n        // Compression may have contributed to observed outliers, but is no\n        // longer included in the snapshot creation timing.\n        #[buckets(0.0005, 0.001, 0.005, 0.01, 0.1, 1.0, 5.0, 10.0)]\n        pub snapshot_creation_time_total: HistogramVec,\n\n        #[name = spacetime_snapshot_creation_time_inner_sec]\n        #[help = \"The time (in seconds) it took to take and store a database snapshot, excluding scheduling overhead\"]\n        #[labels(db: Identity)]\n        #[buckets(0.0005, 0.001, 0.005, 0.01, 0.1, 1.0, 5.0, 10.0)]\n        pub snapshot_creation_time_inner: HistogramVec,\n\n        #[name = spacetime_snapshot_compression_time_total_sec]\n        #[help = \"The time (in seconds) it took to do a compression pass on the snapshot repository, including scheduling overhead\"]\n        #[labels(db: Identity)]\n        // Not sure what range to expect, but certainly slower than snapshot\n        // creation.\n        #[buckets(0.001, 0.01, 0.1, 1.0, 5.0, 10.0)]\n        pub snapshot_compression_time_total: HistogramVec,\n\n        #[name = spacetime_snapshot_compression_time_inner_sec]\n        #[help = \"The time (in seconds) it took to do a compression pass on the snapshot repository, excluding scheduling overhead\"]\n        #[labels(db: Identity)]\n        #[buckets(0.001, 0.01, 0.1, 1.0, 5.0, 10.0)]\n        pub snapshot_compression_time_inner: HistogramVec,\n\n        #[name = spacetime_snapshot_compression_time_per_snapshot_sec]\n        #[help = \"The time (in seconds) it took to compress a single snapshot\"]\n        #[labels(db: Identity)]\n        #[buckets(0.001, 0.01, 0.1, 1.0, 5.0, 10.0)]\n        pub snapshot_compression_time_single: HistogramVec,\n\n        #[name = spacetime_snapshot_compression_skipped]\n        #[help = \"The number of snapshots skipped in a single compression pass because they were already compressed\"]\n        #[labels(db: Identity)]\n        pub snapshot_compression_skipped: IntGaugeVec,\n\n        #[name = spacetime_snapshot_compression_compressed]\n        #[help = \"The number of snapshots compressed in a single compression pass\"]\n        #[labels(db: Identity)]\n        pub snapshot_compression_compressed: IntGaugeVec,\n\n        #[name = spacetime_snapshot_compression_objects_compressed]\n        #[help = \"The number of snapshot objects compressed in a single compression pass\"]\n        #[labels(db: Identity)]\n        pub snapshot_compression_objects_compressed: IntGaugeVec,\n\n        #[name = spacetime_snapshot_compression_objects_hardlinked]\n        #[help = \"The number of snapshot objects hardlinked in a single compression pass\"]\n        #[labels(db: Identity)]\n        pub snapshot_compression_objects_hardlinked: IntGaugeVec,\n\n        #[name = spacetime_subscription_rows_examined]\n        #[help = \"Distribution of rows examined per subscription query\"]\n        #[labels(db: Identity, scan_type: str, table: str, unindexed_columns: str)]\n        #[buckets(100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000)]\n        pub subscription_rows_examined: HistogramVec,\n\n        #[name = spacetime_subscription_query_execution_time_micros]\n        #[help = \"Time taken to execute and fetch records for an initial subscription query (in microseconds)\"]\n        #[labels(db: Identity, scan_type: str, table: str, unindexed_columns: str)]\n        #[buckets(100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000)]\n        pub subscription_query_execution_time_micros: HistogramVec,\n\n        #[name = spacetime_subscription_queries_total]\n        #[help = \"Total number of subscription queries by scan strategy\"]\n        #[labels(db: Identity, scan_type: str, table: str, unindexed_columns: str)]\n        pub subscription_queries_total: IntCounterVec,\n    }\n);\n\npub static WORKER_METRICS: Lazy<WorkerMetrics> = Lazy::new(WorkerMetrics::new);\n\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemalloc_ctl::{epoch, stats};\n\n#[cfg(not(target_env = \"msvc\"))]\nstatic SPAWN_JEMALLOC_GUARD: Once = Once::new();\npub fn spawn_jemalloc_stats(_node_id: String) {\n    #[cfg(not(target_env = \"msvc\"))]\n    SPAWN_JEMALLOC_GUARD.call_once(|| {\n        spawn(async move {\n            let allocated_bytes = WORKER_METRICS.jemalloc_allocated_bytes.with_label_values(&_node_id);\n            let resident_bytes = WORKER_METRICS.jemalloc_resident_bytes.with_label_values(&_node_id);\n            let active_bytes = WORKER_METRICS.jemalloc_active_bytes.with_label_values(&_node_id);\n\n            let e = epoch::mib().unwrap();\n            loop {\n                e.advance().unwrap();\n\n                let allocated = stats::allocated::read().unwrap();\n                let resident = stats::resident::read().unwrap();\n                let active = stats::active::read().unwrap();\n\n                allocated_bytes.set(allocated as i64);\n                resident_bytes.set(resident as i64);\n                active_bytes.set(active as i64);\n\n                sleep(Duration::from_secs(10)).await;\n            }\n        });\n    });\n}\n\nstatic SPAWN_PAGE_POOL_GUARD: Once = Once::new();\npub fn spawn_page_pool_stats(node_id: String, page_pool: PagePool) {\n    SPAWN_PAGE_POOL_GUARD.call_once(|| {\n        spawn(async move {\n            let resident_bytes = WORKER_METRICS.page_pool_resident_bytes.with_label_values(&node_id);\n            let dropped_pages = WORKER_METRICS.page_pool_dropped_pages.with_label_values(&node_id);\n            let new_pages = WORKER_METRICS.page_pool_new_pages_allocated.with_label_values(&node_id);\n            let reused_pages = WORKER_METRICS.page_pool_pages_reused.with_label_values(&node_id);\n            let returned_pages = WORKER_METRICS.page_pool_pages_returned.with_label_values(&node_id);\n\n            loop {\n                resident_bytes.set(page_pool.heap_usage() as i64);\n                dropped_pages.set(page_pool.dropped_count() as i64);\n                new_pages.set(page_pool.new_allocated_count() as i64);\n                reused_pages.set(page_pool.reused_count() as i64);\n                returned_pages.set(page_pool.reused_count() as i64);\n\n                sleep(Duration::from_secs(10)).await;\n            }\n        });\n    });\n}\n\nstatic SPAWN_BSATN_RLB_POOL_GUARD: Once = Once::new();\npub fn spawn_bsatn_rlb_pool_stats(node_id: String, pool: BsatnRowListBuilderPool) {\n    SPAWN_BSATN_RLB_POOL_GUARD.call_once(|| {\n        spawn(async move {\n            let resident_bytes = WORKER_METRICS.bsatn_rlb_pool_resident_bytes.with_label_values(&node_id);\n            let dropped_pages = WORKER_METRICS.bsatn_rlb_pool_dropped.with_label_values(&node_id);\n            let new_pages = WORKER_METRICS.bsatn_rlb_pool_new_allocated.with_label_values(&node_id);\n            let reused_pages = WORKER_METRICS.bsatn_rlb_pool_reused.with_label_values(&node_id);\n            let returned_pages = WORKER_METRICS.bsatn_rlb_pool_returned.with_label_values(&node_id);\n\n            loop {\n                resident_bytes.set(pool.heap_usage() as i64);\n                dropped_pages.set(pool.dropped_count() as i64);\n                new_pages.set(pool.new_allocated_count() as i64);\n                reused_pages.set(pool.reused_count() as i64);\n                returned_pages.set(pool.reused_count() as i64);\n\n                sleep(Duration::from_secs(10)).await;\n            }\n        });\n    });\n}\n\n// How frequently to update the tokio stats.\n#[cfg(all(target_has_atomic = \"64\", tokio_unstable))]\nconst TOKIO_STATS_INTERVAL: Duration = Duration::from_secs(10);\n#[cfg(all(target_has_atomic = \"64\", tokio_unstable))]\nstatic SPAWN_TOKIO_STATS_GUARD: Once = Once::new();\npub fn spawn_tokio_stats(node_id: String) {\n    // Some of these metrics could still be reported without these settings,\n    // but it is simpler to just skip all the tokio metrics if they aren't set.\n\n    #[cfg(not(all(target_has_atomic = \"64\", tokio_unstable)))]\n    log::warn!(\"Skipping tokio metrics for {node_id}, as they are not enabled in this build.\");\n\n    #[cfg(all(target_has_atomic = \"64\", tokio_unstable))]\n    SPAWN_TOKIO_STATS_GUARD.call_once(|| {\n        spawn(async move {\n            // Set up our metric handles, so we don't keep calling `with_label_values`.\n            let num_worker_metric = WORKER_METRICS.tokio_num_workers.with_label_values(&node_id);\n            let num_blocking_threads_metric = WORKER_METRICS.tokio_num_blocking_threads.with_label_values(&node_id);\n            let num_alive_tasks_metric = WORKER_METRICS.tokio_num_alive_tasks.with_label_values(&node_id);\n            let global_queue_depth_metric = WORKER_METRICS.tokio_global_queue_depth.with_label_values(&node_id);\n            let num_idle_blocking_threads_metric = WORKER_METRICS\n                .tokio_num_idle_blocking_threads\n                .with_label_values(&node_id);\n            let blocking_queue_depth_metric = WORKER_METRICS.tokio_blocking_queue_depth.with_label_values(&node_id);\n            let spawned_tasks_count_metric = WORKER_METRICS.tokio_spawned_tasks_count.with_label_values(&node_id);\n            let remote_schedule_count_metric = WORKER_METRICS.tokio_remote_schedule_count.with_label_values(&node_id);\n\n            let local_queue_depth_total_metric =\n                WORKER_METRICS.tokio_local_queue_depth_total.with_label_values(&node_id);\n            let local_queue_depth_max_metric = WORKER_METRICS.tokio_local_queue_depth_max.with_label_values(&node_id);\n            let local_queue_depth_min_metric = WORKER_METRICS.tokio_local_queue_depth_min.with_label_values(&node_id);\n            let steal_total_metric = WORKER_METRICS.tokio_steal_total.with_label_values(&node_id);\n            let steal_operations_total_metric = WORKER_METRICS.tokio_steal_operations_total.with_label_values(&node_id);\n            let local_schedule_total_metric = WORKER_METRICS.tokio_local_schedule_total.with_label_values(&node_id);\n            let overflow_total_metric = WORKER_METRICS.tokio_overflow_total.with_label_values(&node_id);\n            let busy_ratio_min_metric = WORKER_METRICS.tokio_busy_ratio_min.with_label_values(&node_id);\n            let busy_ratio_max_metric = WORKER_METRICS.tokio_busy_ratio_max.with_label_values(&node_id);\n            let busy_ratio_avg_metric = WORKER_METRICS.tokio_busy_ratio_avg.with_label_values(&node_id);\n            let mean_polls_per_park_metric = WORKER_METRICS.tokio_mean_polls_per_park.with_label_values(&node_id);\n\n            let handle = tokio::runtime::Handle::current();\n            // The tokio_metrics library gives us some helpers for aggregating per-worker metrics.\n            let runtime_monitor = tokio_metrics::RuntimeMonitor::new(&handle);\n            let mut intervals = runtime_monitor.intervals();\n            loop {\n                let metrics = tokio::runtime::Handle::current().metrics();\n                let interval_delta = intervals.next();\n\n                num_worker_metric.set(metrics.num_workers() as i64);\n                num_alive_tasks_metric.set(metrics.num_alive_tasks() as i64);\n                global_queue_depth_metric.set(metrics.global_queue_depth() as i64);\n                num_blocking_threads_metric.set(metrics.num_blocking_threads() as i64);\n                num_idle_blocking_threads_metric.set(metrics.num_idle_blocking_threads() as i64);\n                blocking_queue_depth_metric.set(metrics.blocking_queue_depth() as i64);\n\n                // The spawned tasks count and remote schedule count are cumulative,\n                // so we need to increment them by the difference from the last value.\n                {\n                    let current_count = metrics.spawned_tasks_count();\n                    let previous_value = spawned_tasks_count_metric.get();\n                    // The tokio metric should be monotonically increasing, but we are checking just in case.\n                    if let Some(diff) = current_count.checked_sub(previous_value) {\n                        spawned_tasks_count_metric.inc_by(diff);\n                    }\n                }\n                {\n                    let current_count = metrics.remote_schedule_count();\n                    let previous_value = remote_schedule_count_metric.get();\n                    // The tokio metric should be monotonically increasing, but we are checking just in case.\n                    if let Some(diff) = current_count.checked_sub(previous_value) {\n                        remote_schedule_count_metric.inc_by(diff);\n                    }\n                }\n\n                if let Some(interval_delta) = interval_delta {\n                    local_queue_depth_total_metric.set(interval_delta.total_local_queue_depth as i64);\n                    local_queue_depth_max_metric.set(interval_delta.max_local_queue_depth as i64);\n                    local_queue_depth_min_metric.set(interval_delta.min_local_queue_depth as i64);\n                    steal_total_metric.inc_by(interval_delta.total_steal_count);\n                    steal_operations_total_metric.inc_by(interval_delta.total_steal_operations);\n                    local_schedule_total_metric.inc_by(interval_delta.total_local_schedule_count);\n                    overflow_total_metric.inc_by(interval_delta.total_overflow_count);\n                    mean_polls_per_park_metric.set(interval_delta.mean_polls_per_park());\n\n                    // This is mostly to make sure we don't divide by zero, but we also want to skip the first interval if it is very short.\n                    if interval_delta.elapsed.as_millis() > 100 {\n                        busy_ratio_avg_metric.set(interval_delta.busy_ratio());\n                        busy_ratio_min_metric.set(\n                            interval_delta.min_busy_duration.as_nanos() as f64\n                                / interval_delta.elapsed.as_nanos() as f64,\n                        );\n                        busy_ratio_max_metric.set(\n                            interval_delta.max_busy_duration.as_nanos() as f64\n                                / interval_delta.elapsed.as_nanos() as f64,\n                        );\n                    }\n                }\n                sleep(TOKIO_STATS_INTERVAL).await;\n            }\n        });\n    });\n}\n"
  },
  {
    "path": "crates/core/testdata/README.md",
    "content": "This has some data written by older versions of spacetimedb, so our unit tests can check compatibility.\n"
  },
  {
    "path": "crates/core/testdata/v1.2/replicas/22000001/db.lock",
    "content": ""
  },
  {
    "path": "crates/core/testdata/v1.2/replicas/22000001/module_logs/2025-08-18.log",
    "content": "{\"level\":\"Info\",\"ts\":1755532669152418,\"filename\":\"spacetimedb\",\"message\":\"Creating table `message`\"}\n{\"level\":\"Info\",\"ts\":1755532669152813,\"filename\":\"spacetimedb\",\"message\":\"Creating table `user`\"}\n{\"level\":\"Info\",\"ts\":1755532669155699,\"filename\":\"spacetimedb\",\"message\":\"Invoking `init` reducer\"}\n{\"level\":\"Info\",\"ts\":1755532669158503,\"filename\":\"spacetimedb\",\"message\":\"Database initialized\"}\n"
  },
  {
    "path": "crates/data-structures/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-data-structures\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Assorted data structures used in spacetimedb\"\nrust-version.workspace = true\n\n[features]\nmemory-usage = [\"dep:spacetimedb-memory-usage\"]\nserde = [\"dep:serde\", \"hashbrown/serde\"]\n\n[dependencies]\nspacetimedb-memory-usage = { workspace = true, optional = true, default-features = false }\nahash.workspace = true\ncrossbeam-queue.workspace = true\neither.workspace = true\nhashbrown.workspace = true\nnohash-hasher.workspace = true\nserde = { workspace = true, optional = true }\nsmallvec.workspace = true\nthiserror.workspace = true\n\n[dev-dependencies]\nproptest.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/data-structures/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/data-structures/src/error_stream.rs",
    "content": "//! Types, traits, and macros for working with non-empty streams of errors.\n//!\n//! The `ErrorStream<_>` type provides a collection that stores a non-empty, unordered stream of errors.\n//! This is valuable for collecting as many errors as possible before returning them to the user,\n//! which allows the user to work through the errors in the order of their choosing.\n//! This is particularly useful for CLI tools.\n//!\n//! Example usage:\n//! ```\n//! use spacetimedb_data_structures::error_stream::{\n//!     ErrorStream,\n//!     CombineErrors,\n//!     CollectAllErrors\n//! };\n//! use std::collections::HashSet;\n//!\n//! enum MyError { /* ... */ };\n//! type MyErrors = ErrorStream<MyError>;\n//!\n//! type Name =\n//!     /* ... */\n//! #   String\n//!     ;\n//!\n//! type Age =\n//!     /* ... */\n//! #   i32\n//!     ;\n//!\n//! fn validate_name(name: String) -> Result<Name, MyErrors> {\n//!     // ...\n//! #   Ok(name)\n//! }\n//!\n//! fn validate_age(age: i32) -> Result<Age, MyErrors> {\n//!     // ...\n//! #   Ok(age)\n//! }\n//!\n//! fn validate_person(\n//!     name: String,\n//!     age: i32,\n//!     friends: Vec<String>\n//! ) -> Result<(Name, Age, HashSet<Name>), MyErrors> {\n//!     // First, we perform some validation on various pieces\n//!     // of input data, WITHOUT using `?`.\n//!     let name: Result<Name, MyErrors> = validate_name(name);\n//!     let age: Result<Age, MyErrors> = validate_age(age);\n//!\n//!     // If we have multiple pieces of data, we can use\n//!     // `collect_all_errors` to build an arbitrary collection from them.\n//!     // If there are any errors, all of these errors\n//!     // will be returned in a single ErrorStream.\n//!     let friends: Result<HashSet<Name>, MyErrors> = friends\n//!         .into_iter()\n//!         .map(validate_name)\n//!         .collect_all_errors();\n//!\n//!     // Now, we can combine the results into a single result.\n//!     // If there are any errors, they will be returned in a\n//!     // single ErrorStream.\n//!     let (name, age, friends): (Name, Age, HashSet<Name>) =\n//!         (name, age, friends).combine_errors()?;\n//!\n//!     Ok((name, age, friends))\n//! }\n//! ```\n//!\n//! ## Best practices\n//!\n//! ### Use `ErrorStream` everywhere\n//! It is best to use `ErrorStream` everywhere in a multiple-error module, even\n//! for methods that return only a single error. `CombineAllErrors` and `CollectAllErrors`\n//! can only be implemented for types built from `Result<_, ErrorStream<_>>` due to trait conflicts.\n//! `ErrorStream` uses a `smallvec::SmallVec` internally, so it is efficient for single errors.\n//!\n//! You can convert an `E` to an `ErrorStream<E>` using `.into()`.\n//!\n//! ### Not losing any errors\n//! When using this module, it is best to avoid using `?` until as late as possible,\n//! and to completely avoid using the `Result<Collection<_>, _>::collect` method.\n//! Both of these may result in errors being discarded.\n//!\n//! Prefer using `Result::and_then` for chaining operations that may fail,\n//! and `CollectAllErrors::collect_all_errors` for collecting errors from iterators.\n\nuse crate::map::HashSet;\nuse std::{fmt, hash::Hash};\n\n/// A non-empty stream of errors.\n///\n/// Logically, this type is unordered, and it is not guaranteed that the errors will be returned in the order they were added.\n/// Attach identifying information to your errors if you want to sort them.\n///\n/// This struct is intended to be used with:\n/// - The [CombineErrors] trait, which allows you to combine a tuples of results.\n/// - The [CollectAllErrors] trait, which allows you to collect errors from an iterator of results.\n///\n/// To create an `ErrorStream` from a single error, you can use `from` or `into`:\n/// ```\n/// use spacetimedb_data_structures::error_stream::ErrorStream;\n///\n/// enum MyError {\n///     A(u32),\n///     B\n/// }\n///\n/// let error: ErrorStream<MyError> = MyError::A(1).into();\n/// // or\n/// let error = ErrorStream::from(MyError::A(1));\n/// ```\n///\n/// This does not allocate (unless your error allocates, of course).\n#[derive(thiserror::Error, Debug, Clone, Default, PartialEq, Eq)]\npub struct ErrorStream<E>(smallvec::SmallVec<[E; 1]>);\n\nimpl<E> ErrorStream<E> {\n    /// Build an error stream from a non-empty collection.\n    /// If the collection is empty, panic.\n    pub fn expect_nonempty<I: IntoIterator<Item = E>>(errors: I) -> Self {\n        let mut errors = errors.into_iter();\n        let first = errors.next().expect(\"expected at least one error\");\n        let mut stream = Self::from(first);\n        stream.extend(errors);\n        stream\n    }\n\n    /// Add some extra errors to a result.\n    ///\n    /// If there are no errors, the result is not modified.\n    /// If there are errors, and the result is `Err`, the errors are added to the stream.\n    /// If there are errors, and the result is `Ok`, the `Ok` value is discarded, and the errors are returned in a stream.\n    pub fn add_extra_errors<T>(\n        result: Result<T, ErrorStream<E>>,\n        extra_errors: impl IntoIterator<Item = E>,\n    ) -> Result<T, ErrorStream<E>> {\n        match result {\n            Ok(value) => {\n                let errors: SmallVec<[E; 1]> = extra_errors.into_iter().collect();\n                if errors.is_empty() {\n                    Ok(value)\n                } else {\n                    Err(ErrorStream(errors))\n                }\n            }\n            Err(mut errors) => {\n                errors.extend(extra_errors);\n                Err(errors)\n            }\n        }\n    }\n\n    /// Returns an iterator over the errors in the stream.\n    pub fn iter(&self) -> impl Iterator<Item = &E> {\n        self.0.iter()\n    }\n\n    /// Returns a mutable iterator over the errors in the stream.\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut E> {\n        self.0.iter_mut()\n    }\n\n    /// Returns an iterator over the errors in the stream, consuming the stream.\n    pub fn drain(&mut self) -> impl Iterator<Item = E> + '_ {\n        self.0.drain(..)\n    }\n\n    /// Push an error onto the stream.\n    pub fn push(&mut self, error: E) {\n        self.0.push(error);\n    }\n\n    /// Extend the stream with another stream.\n    pub fn extend(&mut self, other: impl IntoIterator<Item = E>) {\n        self.0.extend(other);\n    }\n\n    /// Unpack an error into `self`, returning `None` if there was an error.\n    /// This is not exposed because `CombineErrors` is more convenient.\n    #[inline(never)] // don't optimize this too much\n    #[cold]\n    fn unpack<T, ES: Into<ErrorStream<E>>>(&mut self, result: Result<T, ES>) -> Option<T> {\n        match result {\n            Ok(value) => Some(value),\n            Err(error) => {\n                self.0.extend(error.into().0);\n                None\n            }\n        }\n    }\n}\nimpl<E: fmt::Display> fmt::Display for ErrorStream<E> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(f, \"Errors occurred:\")?;\n        for error in self.iter() {\n            writeln!(f, \"{error}\\n\")?;\n        }\n        Ok(())\n    }\n}\n\nimpl<E: Ord + Eq> ErrorStream<E> {\n    /// Sort and deduplicate the errors in the error stream.\n    pub fn sort_deduplicate(mut self) -> Self {\n        self.0.sort_unstable();\n        self.0.dedup();\n        self\n    }\n}\nimpl<E: Eq + Hash> ErrorStream<E> {\n    /// Hash and deduplicate the errors in the error stream.\n    /// The resulting error stream has an arbitrary order.\n    pub fn hash_deduplicate(mut self) -> Self {\n        let set = self.0.drain(..).collect::<HashSet<_>>();\n        self.0.extend(set);\n        self\n    }\n}\n\nimpl<E> IntoIterator for ErrorStream<E> {\n    type Item = <smallvec::SmallVec<[E; 1]> as IntoIterator>::Item;\n    type IntoIter = <smallvec::SmallVec<[E; 1]> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.0.into_iter()\n    }\n}\n\nimpl<E> From<E> for ErrorStream<E> {\n    fn from(error: E) -> Self {\n        Self(smallvec::smallvec![error])\n    }\n}\n\n/// A trait for converting a tuple of `Result<_, ErrorStream<_>>`s\n/// into a `Result` of a tuple or combined `ErrorStream`.\npub trait CombineErrors {\n    /// The type of the output if all results are `Ok`.\n    type Ok;\n    /// The type of the output if any result is `Err`.\n    type Error;\n\n    /// Combine errors from multiple places into one.\n    /// This can be thought of as a kind of parallel `?`.\n    ///\n    /// If your goal is to show the user as many errors as possible, you should\n    /// call this as late as possible, on as wide a tuple as you can.\n    ///\n    /// Example usage:\n    ///\n    /// ```\n    /// use spacetimedb_data_structures::error_stream::{ErrorStream, CombineErrors};\n    ///\n    /// struct MyError { cause: String };\n    ///\n    /// fn age() -> Result<i32, ErrorStream<MyError>> {\n    ///     //...\n    /// # Ok(1)\n    /// }\n    ///\n    /// fn name() -> Result<String, ErrorStream<MyError>> {\n    ///     // ...\n    /// # Ok(\"hi\".into())\n    /// }\n    ///\n    /// fn likes_dogs() -> Result<bool, ErrorStream<MyError>> {\n    ///     // ...\n    /// # Ok(false)\n    /// }\n    ///\n    /// fn description() -> Result<String, ErrorStream<MyError>> {\n    ///     // A typical usage of the API:\n    ///     // Collect multiple `Result`s in parallel, only using\n    ///     // `.combine_errors()?` once no more progress can be made.\n    ///     let (age, name, likes_dogs) =\n    ///         (age(), name(), likes_dogs()).combine_errors()?;\n    ///\n    ///     Ok(format!(\n    ///         \"{} is {} years old and {}\",\n    ///         name,\n    ///         age,\n    ///         if likes_dogs { \"likes dogs\" } else { \"does not like dogs\" }\n    ///     ))\n    /// }\n    /// ```\n    fn combine_errors(self) -> Result<Self::Ok, ErrorStream<Self::Error>>;\n}\n\nmacro_rules! tuple_combine_errors {\n    ($($T:ident),*) => {\n        impl<$($T,)* E> CombineErrors for ($(Result<$T, ErrorStream<E>>,)*) {\n            type Ok = ($($T,)*);\n            type Error = E;\n\n            #[allow(non_snake_case)]\n            fn combine_errors(self) -> Result<Self::Ok, ErrorStream<Self::Error>> {\n                let mut errors = ErrorStream(Default::default());\n                let ($($T,)* ) = self;\n                $(\n                    let $T = errors.unpack($T);\n                )*\n                if errors.0.is_empty() {\n                    // correctness: none of these pushed an error to `errors`, so by the contract of `unpack`, they must all be `Some`.\n                    Ok(($($T.unwrap(),)*))\n                } else {\n                    Err(errors)\n                }\n            }\n        }\n    };\n}\n\ntuple_combine_errors!(T1, T2);\ntuple_combine_errors!(T1, T2, T3);\ntuple_combine_errors!(T1, T2, T3, T4);\ntuple_combine_errors!(T1, T2, T3, T4, T5);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7, T8);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7, T8, T9);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);\ntuple_combine_errors!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);\n\n/// A trait for collecting errors from an iterator of results,\n/// returning all errors if anything failed.\npub trait CollectAllErrors {\n    /// The item type we are aggregating.\n    type Item;\n\n    /// The error type we are aggregating.\n    type Error;\n\n    /// Collect errors from an iterator of results into a single error stream.\n    /// If all results are `Ok`, returns the collected values. Otherwise,\n    /// combine all errors into a single error stream, and return it.\n    ///\n    /// You CANNOT use the standard library function `Result<T, ErrorStream<E>>::collect()` for this,\n    /// as it will return the FIRST error it encounters, rather than collecting all errors!\n    ///\n    /// The collection can be anything that implements `FromIterator`.\n    ///\n    /// Example usage:\n    /// ```\n    /// use spacetimedb_data_structures::error_stream::{\n    ///     ErrorStream,\n    ///     CollectAllErrors\n    /// };\n    /// use std::collections::HashSet;\n    ///\n    /// enum MyError { /* ... */ }\n    ///\n    /// fn operation(\n    ///     data: String,\n    ///     checksum: u32\n    /// ) -> Result<i32, ErrorStream<MyError>> {\n    ///     /* ... */\n    /// #   Ok(1)\n    /// }\n    ///\n    /// fn many_operations(\n    ///     data: Vec<(String, u32)>\n    /// ) -> Result<HashSet<i32>, ErrorStream<MyError>> {\n    ///     data\n    ///         .into_iter()\n    ///         .map(|(data, checksum)| operation(data, checksum))\n    ///         .collect_all_errors::<HashSet<_>>()\n    /// }\n    /// ```\n    fn collect_all_errors<C: FromIterator<Self::Item>>(self) -> Result<C, ErrorStream<Self::Error>>;\n}\n\nimpl<T, E, I: Iterator<Item = Result<T, ErrorStream<E>>>> CollectAllErrors for I {\n    type Item = T;\n    type Error = E;\n\n    fn collect_all_errors<Collection: FromIterator<Self::Item>>(self) -> Result<Collection, ErrorStream<Self::Error>> {\n        // not in a valid state: contains no errors!\n        let mut all_errors = ErrorStream(Default::default());\n\n        let collection = self\n            .filter_map(|result| match result {\n                Ok(value) => Some(value),\n                Err(errors) => {\n                    all_errors.extend(errors);\n                    None\n                }\n            })\n            .collect::<Collection>();\n\n        if all_errors.0.is_empty() {\n            // invalid state is not returned.\n            Ok(collection)\n        } else {\n            // not empty, so we're good to return it.\n            Err(all_errors)\n        }\n    }\n}\n\n/// Helper macro to match against an error stream, expecting a specific error.\n/// For use in tests.\n/// Panics if a matching error is not found.\n/// Multiple matches are allowed.\n///\n/// Parameters:\n/// - `$result` must be a `Result<_, ErrorStream<E>>`.\n/// - `$expected` is a pattern to match against the error.\n/// - `$cond` is an optional expression that should evaluate to `true` if the error matches.\n///   Variables from `$expected` are bound in `$cond` behind references.\n///   Do not use any asserts in `$cond` as it may be called against multiple errors.\n///\n/// ```\n/// use spacetimedb_data_structures::error_stream::{\n///     ErrorStream,\n///     CollectAllErrors,\n///     expect_error_matching\n/// };\n///\n/// #[derive(PartialEq, Eq, Clone, Copy, Debug)]\n/// struct CaseRef(u32);\n///\n/// #[derive(Debug)]\n/// enum MyError {\n///     InsufficientSwag { amount: u32 },\n///     TooMuchSwag { reason: String, precedent: CaseRef },\n///     SomethingElse(String)\n/// }\n///\n/// let result: Result<(), ErrorStream<MyError>> = vec![\n///     Err(MyError::TooMuchSwag {\n///         reason: \"sunglasses indoors\".into(),\n///         precedent: CaseRef(37)\n///     }.into()),\n///     Err(MyError::TooMuchSwag {\n///         reason: \"fur coat\".into(),\n///         precedent: CaseRef(55)\n///     }.into()),\n///     Err(MyError::SomethingElse(\n///         \"non-service animals forbidden\".into()\n///     ).into())\n/// ].into_iter().collect_all_errors();\n///\n/// // This will panic if the error stream does not contain\n/// // an error matching `MyError::SomethingElse`.\n/// expect_error_matching!(\n///     result,\n///     MyError::SomethingElse(_)\n/// );\n///\n/// // This will panic if the error stream does not contain\n/// // an error matching `MyError::TooMuchSwag`, plus some\n/// // extra conditions.\n/// expect_error_matching!(\n///     result,\n///     MyError::TooMuchSwag { reason, precedent } =>\n///         precedent == &CaseRef(37) && reason.contains(\"sunglasses\")\n/// );\n/// ```\n#[macro_export]\nmacro_rules! expect_error_matching (\n    ($result:expr, $expected:pat => $cond:expr) => {\n        let result: &::std::result::Result<\n            _,\n            $crate::error_stream::ErrorStream<_>\n        > = &$result;\n        match result {\n            Ok(_) => panic!(\"expected error, but got Ok\"),\n            Err(errors) => {\n                let err = errors.iter().find(|error|\n                    if let $expected = error {\n                        $cond\n                    } else {\n                        false\n                    }\n                );\n                if let None = err {\n                    panic!(\"expected error matching `{}` satisfying `{}`,\\n but got {:#?}\", stringify!($expected), stringify!($cond), errors);\n                }\n            }\n        }\n    };\n    ($result:expr, $expected:pat) => {\n        let result: &::std::result::Result<\n            _,\n            $crate::error_stream::ErrorStream<_>\n        > = &$result;\n        match result {\n            Ok(_) => panic!(\"expected error, but got Ok\"),\n            Err(errors) => {\n                let err = errors.iter().find(|error| matches!(error, $expected));\n                if let None = err {\n                    panic!(\"expected error matching `{}`,\\n but got {:#?}\", stringify!($expected), errors);\n                }\n            }\n        }\n    };\n);\n// Make available in this module as well as crate root.\npub use expect_error_matching;\nuse smallvec::SmallVec;\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[derive(Debug, PartialEq)]\n    enum MyError {\n        A(u32),\n        B,\n    }\n\n    type Result<T> = std::result::Result<T, ErrorStream<MyError>>;\n\n    #[test]\n    fn combine_errors() {\n        type ResultTuple = (Result<i32>, Result<String>, Result<u8>);\n        let tuple_1: ResultTuple = (Ok(1), Ok(\"hi\".into()), Ok(3));\n        assert_eq!(tuple_1.combine_errors(), Ok((1, \"hi\".into(), 3)));\n\n        let tuple_2: ResultTuple = (Err(MyError::A(1).into()), Ok(\"hi\".into()), Ok(3));\n        assert_eq!(tuple_2.combine_errors(), Err(MyError::A(1).into()));\n\n        let tuple_3: ResultTuple = (Err(MyError::A(1).into()), Err(MyError::A(2).into()), Ok(3));\n        assert_eq!(\n            tuple_3.combine_errors(),\n            Err(ErrorStream(smallvec::smallvec![MyError::A(1), MyError::A(2)]))\n        );\n\n        let tuple_4: ResultTuple = (\n            Err(MyError::A(1).into()),\n            Err(MyError::A(2).into()),\n            Err(MyError::A(3).into()),\n        );\n        assert_eq!(\n            tuple_4.combine_errors(),\n            Err(ErrorStream(smallvec::smallvec![\n                MyError::A(1),\n                MyError::A(2),\n                MyError::A(3)\n            ]))\n        );\n    }\n\n    #[test]\n    fn collect_all_errors() {\n        let data: Vec<Result<i32>> = vec![Ok(1), Ok(2), Ok(3)];\n        assert_eq!(data.into_iter().collect_all_errors::<Vec<_>>(), Ok(vec![1, 2, 3]));\n\n        let data = vec![Ok(1), Err(MyError::A(0).into()), Ok(3)];\n        assert_eq!(\n            data.into_iter().collect_all_errors::<Vec<_>>(),\n            Err(ErrorStream([MyError::A(0)].into()))\n        );\n\n        let data: Vec<Result<i32>> = vec![\n            Err(MyError::A(1).into()),\n            Err(MyError::A(2).into()),\n            Err(MyError::A(3).into()),\n        ];\n        assert_eq!(\n            data.into_iter().collect_all_errors::<Vec<_>>(),\n            Err(ErrorStream(smallvec::smallvec![\n                MyError::A(1),\n                MyError::A(2),\n                MyError::A(3)\n            ]))\n        );\n    }\n\n    #[test]\n    #[should_panic]\n    fn expect_error_matching_without_cond_panics() {\n        let data: Result<()> = Err(ErrorStream(vec![MyError::B].into()));\n        expect_error_matching!(data, MyError::A(_));\n    }\n\n    #[test]\n    #[should_panic]\n    fn expect_error_matching_with_cond_panics() {\n        let data: Result<()> = Err(ErrorStream(vec![MyError::A(5), MyError::A(10)].into()));\n        expect_error_matching!(data, MyError::A(n) => n == &12);\n    }\n}\n"
  },
  {
    "path": "crates/data-structures/src/lib.rs",
    "content": "//! This crate provides assorted data structures that are used in spacetimedb.\n//! These structures are general and could be used outside spacetimedb.\n\npub mod error_stream;\npub mod map;\npub mod nstr;\npub mod object_pool;\npub mod slim_slice;\npub mod small_map;\n"
  },
  {
    "path": "crates/data-structures/src/map.rs",
    "content": "use core::hash::{BuildHasher, BuildHasherDefault};\nuse nohash_hasher::BuildNoHashHasher;\n\npub use hashbrown::Equivalent;\npub use nohash_hasher::IsEnabled as ValidAsIdentityHash;\n\npub mod hash_set {\n    pub use super::HashSet;\n    pub use hashbrown::hash_set::*;\n}\n\npub mod hash_map {\n    pub use super::HashMap;\n    pub use hashbrown::hash_map::*;\n}\n\npub type DefaultHashBuilder = BuildHasherDefault<ahash::AHasher>;\n// TODO(centril): expose two maps instead,\n// one `map::fast::HashMap` and one `map::ddos::HashMap`.\n// In the first case we won't care about DDoS protection at all and can use `foldhash::fast`.\n// In the lattr, we can use e.g., randomized AHash.\npub type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultHashBuilder>;\npub type HashSet<T> = hashbrown::HashSet<T, DefaultHashBuilder>;\n\n/// A version of [`HashMap<K, V>`] using the identity hash function,\n/// which is valid for any key type that can be converted to a `u64` without truncation.\npub type IntMap<K, V> = hashbrown::HashMap<K, V, BuildNoHashHasher<K>>;\n\n/// A version of [`HashSet<K>`] using the identity hash function,\n/// which is valid for any key type that can be converted to a `u64` without truncation.\npub type IntSet<K> = hashbrown::HashSet<K, BuildNoHashHasher<K>>;\n\npub trait HashCollectionExt {\n    /// Returns a new collection with default capacity, using `S::default()` to build the hasher.\n    fn new() -> Self;\n\n    /// Returns a new collection with `capacity`, using `S::default()` to build the hasher.\n    fn with_capacity(capacity: usize) -> Self;\n}\n\nimpl<K, V, S: BuildHasher + Default> HashCollectionExt for hashbrown::HashMap<K, V, S> {\n    fn new() -> Self {\n        Self::with_hasher(S::default())\n    }\n\n    fn with_capacity(capacity: usize) -> Self {\n        Self::with_capacity_and_hasher(capacity, S::default())\n    }\n}\n\nimpl<K, S: BuildHasher + Default> HashCollectionExt for hashbrown::HashSet<K, S> {\n    fn new() -> Self {\n        Self::with_hasher(S::default())\n    }\n\n    fn with_capacity(capacity: usize) -> Self {\n        Self::with_capacity_and_hasher(capacity, S::default())\n    }\n}\n"
  },
  {
    "path": "crates/data-structures/src/nstr.rs",
    "content": "//! Provides UTF-8 strings of compile-time-known lengths.\n//!\n//! The strings can be constructed with `nstr!(string_literal)`\n//! producing a type `NStr<N>` where `const N: usize`.\n//! An example would be `nstr!(\"spacetime\"): NStr<9>`.\n\nuse core::{fmt, ops::Deref, str};\nuse std::ops::DerefMut;\n\n/// A UTF-8 string of known length `N`.\n///\n/// The notion of length is that of the standard library's `String` type.\n/// That is, the length is in bytes, not chars or graphemes.\n///\n/// It holds that `size_of::<NStr<N>>() == N`\n/// but `&NStr<N>` is always word sized.\n#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct NStr<const N: usize>([u8; N]);\n\n// This function exists due to macro-privacy interactions.\n#[doc(hidden)]\npub const fn __nstr<const N: usize>(s: &str) -> NStr<N> {\n    // Ensure that the string was `N` in length.\n    // This has no runtime cost as the `nstr!` macro forces compile-time eval.\n    if N != s.len() {\n        panic!(\"string does not match claimed length\");\n    };\n\n    // Convert the string to bytes.\n    // Need to use `while` to do this at compile time.\n    let src = s.as_bytes();\n    let mut dst = [0; N];\n    let mut i = 0;\n    while i < N {\n        dst[i] = src[i];\n        i += 1;\n    }\n\n    NStr(dst)\n}\n\n/// Constructs an `NStr<N>` given a string literal of `N` bytes in length.\n///\n/// # Example\n///\n/// ```\n/// use spacetimedb_data_structures::{nstr, nstr::NStr};\n/// let s: NStr<3> = nstr!(\"foo\");\n/// assert_eq!(&*s, \"foo\");\n/// assert_eq!(s.len(), 3);\n/// assert_eq!(format!(\"{s}\"), \"foo\");\n/// assert_eq!(format!(\"{s:?}\"), \"foo\");\n/// ```\n#[macro_export]\nmacro_rules! nstr {\n    ($lit:literal) => {\n        $crate::nstr::__nstr::<{ $lit.len() }>($lit).into()\n    };\n}\n\nimpl<const N: usize> Deref for NStr<N> {\n    type Target = str;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: An `NStr<N>` can only be made through `__nstr(..)`\n        // and which receives an `&str` which is valid UTF-8.\n        unsafe { str::from_utf8_unchecked(&self.0) }\n    }\n}\n\nimpl<const N: usize> DerefMut for NStr<N> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // SAFETY: An `NStr<N>` can only be made through `__nstr(..)`\n        // and which receives an `&str` which is valid UTF-8.\n        unsafe { str::from_utf8_unchecked_mut(&mut self.0) }\n    }\n}\n\nimpl<const N: usize> fmt::Debug for NStr<N> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(self.deref())\n    }\n}\n\nimpl<const N: usize> fmt::Display for NStr<N> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(self.deref())\n    }\n}\n"
  },
  {
    "path": "crates/data-structures/src/object_pool.rs",
    "content": "use core::any::type_name;\nuse core::fmt;\nuse core::sync::atomic::{AtomicUsize, Ordering};\nuse crossbeam_queue::ArrayQueue;\nuse std::sync::Arc;\n\n#[cfg(not(feature = \"memory-usage\"))]\n/// An object that can be put into a [`Pool<T>`].\npub trait PooledObject {}\n\n#[cfg(feature = \"memory-usage\")]\n/// An object that can be put into a [`Pool<T>`].\n///\n/// The trait exposes hooks that the pool needs\n/// so that it can e.g., implement `MemoryUsage`.\npub trait PooledObject: spacetimedb_memory_usage::MemoryUsage {\n    /// The storage for the number of bytes in the pool.\n    ///\n    /// When each object in the pool takes up the same size, this can be `()`.\n    /// Otherwise, it will typically be [`AtomicUsize`].\n    type ResidentBytesStorage: Default;\n\n    /// Returns the number of bytes resident in the pool.\n    ///\n    /// The `storage` is provided as well as the `num_objects` in the pool.\n    /// Typically, exactly one of these will be used.\n    fn resident_object_bytes(storage: &Self::ResidentBytesStorage, num_objects: usize) -> usize;\n\n    /// Called by the pool to add `bytes` to `storage`, if necessary.\n    fn add_to_resident_object_bytes(storage: &Self::ResidentBytesStorage, bytes: usize);\n\n    /// Called by the pool to subtract `bytes` from `storage`, if necessary.\n    fn sub_from_resident_object_bytes(storage: &Self::ResidentBytesStorage, bytes: usize);\n}\n\n/// A pool of some objects of type `T`.\npub struct Pool<T: PooledObject> {\n    inner: Arc<Inner<T>>,\n}\n\nimpl<T: PooledObject> fmt::Debug for Pool<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let dropped = self.dropped_count();\n        let new = self.new_allocated_count();\n        let reused = self.reused_count();\n        let returned = self.returned_count();\n\n        #[cfg(feature = \"memory-usage\")]\n        let bytes = T::resident_object_bytes(&self.inner.resident_object_bytes, self.inner.objects.len());\n\n        let mut builder = f.debug_struct(&format!(\"Pool<{}>\", type_name::<T>()));\n\n        #[cfg(feature = \"memory-usage\")]\n        let builder = builder.field(\"resident_object_bytes\", &bytes);\n\n        builder\n            .field(\"dropped_count\", &dropped)\n            .field(\"new_allocated_count\", &new)\n            .field(\"reused_count\", &reused)\n            .field(\"returned_count\", &returned)\n            .finish()\n    }\n}\n\nimpl<T: PooledObject> Clone for Pool<T> {\n    fn clone(&self) -> Self {\n        let inner = self.inner.clone();\n        Self { inner }\n    }\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl<T: PooledObject> spacetimedb_memory_usage::MemoryUsage for Pool<T> {\n    fn heap_usage(&self) -> usize {\n        let Self { inner } = self;\n        inner.heap_usage()\n    }\n}\n\nimpl<T: PooledObject> Pool<T> {\n    /// Returns a new pool with a maximum capacity of `cap`.\n    /// This capacity is fixed over the lifetime of the pool.\n    pub fn new(cap: usize) -> Self {\n        let inner = Arc::new(Inner::new(cap));\n        Self { inner }\n    }\n\n    /// Puts back an object into the pool.\n    pub fn put(&self, object: T) {\n        self.inner.put(object);\n    }\n\n    /// Puts back an object into the pool.\n    pub fn put_many(&self, objects: impl Iterator<Item = T>) {\n        for obj in objects {\n            self.put(obj);\n        }\n    }\n\n    /// Takes an object from the pool or creates a new one.\n    pub fn take(&self, clear: impl FnOnce(&mut T), new: impl FnOnce() -> T) -> T {\n        self.inner.take(clear, new)\n    }\n\n    /// Returns the number of pages dropped by the pool because the pool was at capacity.\n    pub fn dropped_count(&self) -> usize {\n        self.inner.dropped_count.load(Ordering::Relaxed)\n    }\n\n    /// Returns the number of fresh objects allocated through the pool.\n    pub fn new_allocated_count(&self) -> usize {\n        self.inner.new_allocated_count.load(Ordering::Relaxed)\n    }\n\n    /// Returns the number of objects reused from the pool.\n    pub fn reused_count(&self) -> usize {\n        self.inner.reused_count.load(Ordering::Relaxed)\n    }\n\n    /// Returns the number of objects returned to the pool.\n    pub fn returned_count(&self) -> usize {\n        self.inner.returned_count.load(Ordering::Relaxed)\n    }\n}\n\n/// The inner actual page pool containing all the logic.\nstruct Inner<T: PooledObject> {\n    objects: ArrayQueue<T>,\n    dropped_count: AtomicUsize,\n    new_allocated_count: AtomicUsize,\n    reused_count: AtomicUsize,\n    returned_count: AtomicUsize,\n\n    #[cfg(feature = \"memory-usage\")]\n    resident_object_bytes: T::ResidentBytesStorage,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl<T: PooledObject> spacetimedb_memory_usage::MemoryUsage for Inner<T> {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            objects,\n            dropped_count,\n            new_allocated_count,\n            reused_count,\n            returned_count,\n            resident_object_bytes,\n        } = self;\n        dropped_count.heap_usage() +\n        new_allocated_count.heap_usage() +\n        reused_count.heap_usage() +\n        returned_count.heap_usage() +\n        // This is the amount the queue itself takes up on the heap.\n        objects.capacity() * size_of::<(AtomicUsize, T)>() +\n        // This is the amount the objects take up on the heap, excluding the static size.\n        T::resident_object_bytes(resident_object_bytes, objects.len())\n    }\n}\n\n#[inline]\nfn inc(atomic: &AtomicUsize) {\n    atomic.fetch_add(1, Ordering::Relaxed);\n}\n\nimpl<T: PooledObject> Inner<T> {\n    /// Creates a new pool capable of holding `cap` objects.\n    fn new(cap: usize) -> Self {\n        let objects = ArrayQueue::new(cap);\n        Self {\n            objects,\n            dropped_count: <_>::default(),\n            new_allocated_count: <_>::default(),\n            reused_count: <_>::default(),\n            returned_count: <_>::default(),\n\n            #[cfg(feature = \"memory-usage\")]\n            resident_object_bytes: <_>::default(),\n        }\n    }\n\n    /// Puts back an object into the pool.\n    fn put(&self, object: T) {\n        #[cfg(feature = \"memory-usage\")]\n        let bytes = object.heap_usage();\n        // Add it to the pool if there's room, or just drop it.\n        if self.objects.push(object).is_ok() {\n            #[cfg(feature = \"memory-usage\")]\n            T::add_to_resident_object_bytes(&self.resident_object_bytes, bytes);\n\n            inc(&self.returned_count);\n        } else {\n            inc(&self.dropped_count);\n        }\n    }\n\n    /// Takes an object from the pool or creates a new one.\n    ///\n    /// The closure `clear` provides the opportunity to clear the object before use.\n    /// The closure `new` is called to create a new object when the pool is empty.\n    fn take(&self, clear: impl FnOnce(&mut T), new: impl FnOnce() -> T) -> T {\n        self.objects\n            .pop()\n            .map(|mut object| {\n                #[cfg(feature = \"memory-usage\")]\n                T::sub_from_resident_object_bytes(&self.resident_object_bytes, object.heap_usage());\n\n                inc(&self.reused_count);\n                clear(&mut object);\n                object\n            })\n            .unwrap_or_else(|| {\n                inc(&self.new_allocated_count);\n                new()\n            })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use core::{iter, ptr::addr_eq};\n\n    // The type of pools used for testing.\n    // We want to include a `Box` so that we can do pointer comparisons.\n    type P = Pool<Box<i32>>;\n\n    #[cfg(not(feature = \"memory-usage\"))]\n    impl PooledObject for Box<i32> {}\n\n    #[cfg(feature = \"memory-usage\")]\n    impl PooledObject for Box<i32> {\n        type ResidentBytesStorage = ();\n        fn add_to_resident_object_bytes(_: &Self::ResidentBytesStorage, _: usize) {}\n        fn sub_from_resident_object_bytes(_: &Self::ResidentBytesStorage, _: usize) {}\n        fn resident_object_bytes(_: &Self::ResidentBytesStorage, num_objects: usize) -> usize {\n            num_objects * size_of::<i32>()\n        }\n    }\n\n    fn new() -> P {\n        P::new(100)\n    }\n\n    fn assert_metrics(pool: &P, dropped: usize, new: usize, reused: usize, returned: usize) {\n        assert_eq!(pool.dropped_count(), dropped);\n        assert_eq!(pool.new_allocated_count(), new);\n        assert_eq!(pool.reused_count(), reused);\n        assert_eq!(pool.returned_count(), returned);\n    }\n\n    fn take(pool: &P) -> Box<i32> {\n        pool.take(|_| {}, || Box::new(0))\n    }\n\n    #[test]\n    fn pool_returns_same_obj() {\n        let pool = new();\n        assert_metrics(&pool, 0, 0, 0, 0);\n\n        // Create an object and put it back.\n        let obj1 = take(&pool);\n        assert_metrics(&pool, 0, 1, 0, 0);\n        let obj1_ptr = &*obj1 as *const _;\n        pool.put(obj1);\n        assert_metrics(&pool, 0, 1, 0, 1);\n\n        // Extract an object again.\n        let obj2 = take(&pool);\n        assert_metrics(&pool, 0, 1, 1, 1);\n        let obj2_ptr = &*obj2 as *const _;\n        // It should be the same as the previous one.\n        assert!(addr_eq(obj1_ptr, obj2_ptr));\n        pool.put(obj2);\n        assert_metrics(&pool, 0, 1, 1, 2);\n\n        // Extract an object again.\n        let obj3 = take(&pool);\n        assert_metrics(&pool, 0, 1, 2, 2);\n        let obj3_ptr = &*obj3 as *const _;\n        // It should be the same as the previous one.\n        assert!(addr_eq(obj1_ptr, obj3_ptr));\n\n        // Manually create an object and put it in.\n        let obj4 = Box::new(0);\n        let obj4_ptr = &*obj4 as *const _;\n        pool.put(obj4);\n        pool.put(obj3);\n        assert_metrics(&pool, 0, 1, 2, 4);\n        // When we take out an object, it should be the same as `obj4` and not `obj1`.\n        let obj5 = take(&pool);\n        assert_metrics(&pool, 0, 1, 3, 4);\n        let obj5_ptr = &*obj5 as *const _;\n        // Same as obj4.\n        assert!(!addr_eq(obj5_ptr, obj1_ptr));\n        assert!(addr_eq(obj5_ptr, obj4_ptr));\n    }\n\n    #[test]\n    fn pool_drops_past_max_size() {\n        const N: usize = 3;\n        let pool = P::new(N);\n\n        let pages = iter::repeat_with(|| take(&pool)).take(N + 1).collect::<Vec<_>>();\n        assert_metrics(&pool, 0, N + 1, 0, 0);\n\n        pool.put_many(pages.into_iter());\n        assert_metrics(&pool, 1, N + 1, 0, N);\n        assert_eq!(pool.inner.objects.len(), N);\n    }\n}\n"
  },
  {
    "path": "crates/data-structures/src/slim_slice.rs",
    "content": "//! Defines slimmer versions of slices, both shared, mutable, and owned.\n//!\n//! They are slimmer in the sense that whereas e.g.,\n//! `size_of::<Box<[T]>>() == 16`, on a 64-bit machine,\n//! a `SlimSliceBox<T>` only takes up 12 bytes.\n//! These 4 bytes in difference can help\n//! when these types are stored in enum variants\n//! due to alignment and the space needed for storing tags.\n//!\n//! The difference size (4 bytes), is due to storing the length as a `u32`\n//! rather than storing the length as a `usize` (`u64` on 64-bit machine).\n//! This implies that the length can be at most `u32::MAX`,\n//! so no more elements than that can be stored or pointed to with these types.\n//!\n//! Because hitting `u32::MAX` is substantially more likely than `u64::MAX`,\n//! the risk of overflow is greater.\n//! To mitigate this issue, rather than default to panicking,\n//! this module tries, for the most part,\n//! to force its user to handle any overflow\n//! when converting to the slimmer types.\n//!\n//! The slimmer types include:\n//!\n//! - [`SlimSliceBox<T>`], a slimmer version of `Box<[T]>`\n//! - [`SlimSmallSliceBox<T, N>`], a slimmer version of `SmallVec<[T; N]>`\n//!   but without the growing functionality.\n//! - [`SlimStrBox`], a slimmer version of `Box<str>`\n//! - [`SlimSlice<'a, T>`], a slimmer version of `&'a [T]`\n//! - [`SlimSliceMut<'a, T>`], a slimmer version of `&'a mut [T]`\n//! - [`SlimStr<'a>`], a slimmer version of `&'a str`\n//! - [`SlimStrMut<'a>`], a slimmer version of `&'a mut str`\n//!\n//! The following convenience conversion functions are provided:\n//!\n//! - [`from_slice`] converts `&[T] -> SlimSlice<T>`, panicking on overflow\n//! - [`from_slice_mut`] converts `&mut [T] -> SlimSliceMut<T>`, panicking on overflow\n//! - [`from_str`] converts `&str -> SlimStr`, panicking on overflow\n//! - [`from_str_mut`] converts `&mut str -> SlimStrMut`, panicking on overflow\n//! - [`from_string`] converts `&str -> SlimStrBox`, panicking on overflow\n//!\n//! These conversions should be reserved for cases where it is known\n//! that the length `<= u32::MAX` and should be used sparingly.\n//!\n//! Some auxiliary and utility functionality is provided:\n//!\n//! - [`SlimSliceBoxCollected<T>`] exists to indirectly provide `FromIterator<A>`\n//!   for [`SlimSliceBox<T>`]\n//!\n//! - [`LenTooLong<T>`], the error type when a conversion to a slimmer type\n//!   would result in a length overflow.\n//!   Optionally, the to-convert object is provided back to the user\n//!   for handling\n//!\n//! - [`try_into`] tries to convert the input to a slim type\n//!   and forgets the input if an error occurred\n//!\n//! - [`SafelyExchangeable<T>`] is implemented to assert that `Self`\n//!   is safely transmutable, including when stored in a collection, to type `T`\n\nuse core::{\n    borrow::Borrow,\n    cmp::Ordering,\n    fmt::{self, Debug, Display},\n    hash::{Hash, Hasher},\n    marker::PhantomData,\n    mem::{self, ManuallyDrop},\n    ops::{Deref, DerefMut},\n    ptr::{slice_from_raw_parts_mut, NonNull},\n    slice,\n    str::{from_utf8_unchecked, from_utf8_unchecked_mut},\n};\nuse smallvec::SmallVec;\nuse thiserror::Error;\n\n// =============================================================================\n// Errors\n// =============================================================================\n\n/// An error signifying that a container's size was over `u32::MAX`.\n///\n/// Optionally, the to-convert object is provided back to the user for handling.\n/// This is what the generic parameter `T` is for.\n#[derive(Error, Debug)]\n#[error(\"The length `{len}` was too long\")]\npub struct LenTooLong<T = ()> {\n    /// The size of the container that was too large.\n    pub len: usize,\n    /// The container that was too large for into-slim conversion.\n    pub too_long: T,\n}\n\nimpl<T> LenTooLong<T> {\n    /// Forgets the container part of the error.\n    pub fn forget(self) -> LenTooLong {\n        self.map(drop)\n    }\n\n    /// Maps the container part of the error.\n    pub fn map<U>(self, with: impl FnOnce(T) -> U) -> LenTooLong<U> {\n        LenTooLong {\n            len: self.len,\n            too_long: with(self.too_long),\n        }\n    }\n}\n\n/// Try to convert `x` into `B` and forget the container part of the error if any.\n#[inline]\npub fn try_into<A, B: TryFrom<A, Error = LenTooLong<A>>>(x: A) -> Result<B, LenTooLong> {\n    x.try_into().map_err(|e: LenTooLong<A>| e.forget())\n}\n\n// =============================================================================\n// Utils\n// =============================================================================\n\n/// Ensures that `$thing.len() <= u32::MAX`.\nmacro_rules! ensure_len_fits {\n    ($thing:expr) => {\n        let Ok(_) = u32::try_from($thing.len()) else {\n            return Err(LenTooLong {\n                len: $thing.len(),\n                too_long: $thing,\n            });\n        };\n    };\n}\n\n/// Convert to `A` but panic if `x.len() > u32::MAX`.\n#[inline]\nfn expect_fit<A, E, B: TryInto<A, Error = LenTooLong<E>>>(x: B) -> A {\n    x.try_into().map_err(|e| e.len).expect(\"length didn't fit in `u32`\")\n}\n\n#[inline]\nfn into_box<T, U: Into<Box<[T]>>>(x: U) -> Box<[T]> {\n    x.into()\n}\n\n// Asserts, in the type system, that `N <= u32::MAX`.\nstruct AssertU32<const N: usize>;\nimpl<const N: usize> AssertU32<N> {\n    const OK: () = assert!(N <= u32::MAX as usize);\n}\n\n// =============================================================================\n// Raw slice utility type\n// =============================================================================\n\n/// Implementors decree that `Self` can be *safely* transmuted to `T`,\n/// including covariantly under a pointer.\n///\n/// # Safety\n///\n/// It is not sufficient that ´Self` and `T` have the same representation.\n/// That is, validity requirements are not enough.\n/// The safety requirements must also be the same.\npub unsafe trait SafelyExchangeable<T> {}\n\n/// Implementation detail of the other types.\n/// Provides some convenience but users of the type are responsible\n/// for safety, invariants and variance.\n#[repr(Rust, packed)]\nstruct SlimRawSlice<T> {\n    /// A valid pointer to the slice data.\n    ptr: NonNull<T>,\n    /// The length of the slice.\n    len: u32,\n}\n\nimpl<T> SlimRawSlice<T> {\n    /// Returns a dangling slim raw slice.\n    #[inline]\n    fn dangling() -> Self {\n        let ptr = NonNull::dangling();\n        Self { len: 0, ptr }\n    }\n\n    /// Casts this raw slice `SlimRawSlice<T>` to `SlimRawSlice<U>`.\n    ///\n    /// That is, a cast from elements of `T` to elements of `U`.\n    /// The caller has ensured by `U: SafelyExchangeable<T>`\n    /// that `T` is safely exchangeable for `U`.\n    #[inline]\n    fn cast<U: SafelyExchangeable<T>>(self) -> SlimRawSlice<U> {\n        SlimRawSlice {\n            ptr: self.ptr.cast(),\n            len: self.len,\n        }\n    }\n\n    /// Split `self` into a raw pointer and the slice length.\n    #[inline]\n    fn split(self) -> (*mut T, usize) {\n        (self.ptr.as_ptr(), self.len as usize)\n    }\n\n    /// Dereferences this raw slice into a shared slice.\n    ///\n    /// SAFETY: `self.ptr` and `self.len`\n    /// must satisfy [`std::slice::from_raw_parts`]'s requirements.\n    /// That is,\n    /// * `self.ptr` must be valid for reads\n    ///   for `self.len * size_of::<T>` many bytes and must be aligned.\n    ///\n    /// * `self.ptr` must point to `self.len`\n    ///   consecutive properly initialized values of type `T`.\n    ///\n    /// * The memory referenced by the returned slice\n    ///   must not be mutated for the duration of lifetime `'a`,\n    ///   except inside an `UnsafeCell`.\n    ///\n    /// * The total size `self.len * mem::size_of::<T>()`\n    ///   of the slice must be no larger than `isize::MAX`,\n    ///   and adding that size to `data`\n    ///   must not \"wrap around\" the address space.\n    #[allow(clippy::needless_lifetimes)]\n    #[inline]\n    unsafe fn deref<'a>(&'a self) -> &'a [T] {\n        let (ptr, len) = self.split();\n        // SAFETY: caller is responsible for these.\n        unsafe { slice::from_raw_parts(ptr, len) }\n    }\n\n    /// Dereferences this raw slice into a mutable slice.\n    ///\n    /// SAFETY: `self.ptr` and `self.len`\n    /// must satisfy [`std::slice::from_raw_parts_mut`]'s requirements.\n    /// That is,\n    /// * `self.ptr` must be [valid] for both reads and writes\n    ///   for `self.len * mem::size_of::<T>()` many bytes,\n    ///   and it must be properly aligned.\n    ///\n    /// * `self.ptr` must point to `self.len`\n    ///   consecutive properly initialized values of type `T`.\n    ///\n    /// * The memory referenced by the returned slice\n    ///   must not be accessed through any other pointer\n    ///   (not derived from the return value) for the duration of lifetime `'a`.\n    ///   Both read and write accesses are forbidden.\n    ///\n    /// * The total size `self.len * mem::size_of::<T>()`\n    ///   of the slice must be no larger than `isize::MAX`,\n    ///   and adding that size to `data` must not \"wrap around\" the address space.\n    #[allow(clippy::needless_lifetimes)]\n    #[inline]\n    unsafe fn deref_mut<'a>(&'a mut self) -> &'a mut [T] {\n        let (ptr, len) = self.split();\n        // SAFETY: caller is responsible for these.\n        unsafe { slice::from_raw_parts_mut(ptr, len) }\n    }\n\n    /// Creates the raw slice from a pointer to the data and a length.\n    ///\n    /// It is assumed that `len <= u32::MAX`.\n    /// The caller must ensure that `ptr != NULL`.\n    #[inline]\n    const unsafe fn from_len_ptr(len: usize, ptr: *mut T) -> Self {\n        unsafe {\n            // SAFETY: caller ensured that `!ptr.is_null()`.\n            let ptr = NonNull::new_unchecked(ptr);\n            let len = len as u32;\n            Self { ptr, len }\n        }\n    }\n}\n\nimpl<T> Copy for SlimRawSlice<T> {}\nimpl<T> Clone for SlimRawSlice<T> {\n    #[inline]\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\n// =============================================================================\n// Owned boxed slice\n// =============================================================================\n\npub use slim_slice_box::*;\nmod slim_slice_box {\n    // ^-- In the interest of soundness,\n    // this module exists to limit access to the private fields\n    // of `SlimSliceBox<T>` to a few key functions.\n    use super::*;\n\n    /// Provides a slimmer version of `Box<[T]>`\n    /// using `u32` for its length instead of `usize`.\n    #[repr(transparent)]\n    pub struct SlimSliceBox<T> {\n        /// The representation of this boxed slice.\n        ///\n        /// To convert to a `SlimSliceBox<T>` we must first have a `Box<[T]>`.\n        raw: SlimRawSlice<T>,\n        /// Marker to ensure covariance and dropck ownership.\n        owned: PhantomData<T>,\n    }\n\n    impl<T> Drop for SlimSliceBox<T> {\n        #[inline]\n        fn drop(&mut self) {\n            // Get us an owned `SlimSliceBox<T>`\n            // by replacing `self` with garbage that won't be dropped\n            // as the drop glue for the constituent fields does nothing.\n            let raw = SlimRawSlice::dangling();\n            let owned = PhantomData;\n            let this = mem::replace(self, Self { raw, owned });\n\n            // Convert into `Box<[T]>` and let it deal with dropping.\n            drop(into_box(this));\n        }\n    }\n\n    impl<T> SlimSliceBox<T> {\n        /// Converts `boxed` to `Self` without checking `boxed.len() <= u32::MAX`.\n        ///\n        /// # Safety\n        ///\n        /// The caller must ensure that `boxed.len() <= u32::MAX`.\n        #[allow(clippy::boxed_local)]\n        #[inline]\n        // Clippy doesn't seem to consider unsafe code here.\n        pub unsafe fn from_boxed_unchecked(boxed: Box<[T]>) -> Self {\n            unsafe {\n                let len = boxed.len();\n                let ptr = Box::into_raw(boxed) as *mut T;\n                // SAFETY: `Box<T>`'s ptr was a `NonNull<T>` already.\n                // and our caller has promised that `boxed.len() <= u32::MAX`.\n                let raw = SlimRawSlice::from_len_ptr(len, ptr);\n                let owned = PhantomData;\n                Self { raw, owned }\n            }\n        }\n\n        /// Returns a limited shared slice to this boxed slice.\n        #[allow(clippy::needless_lifetimes)]\n        #[inline]\n        pub fn shared_ref<'a>(&'a self) -> &'a SlimSlice<'a, T> {\n            // SAFETY: The reference lives as long as `self`.\n            // By virtue of `repr(transparent)` we're also allowed these reference casts.\n            unsafe { mem::transmute(self) }\n        }\n\n        /// Returns a limited mutable slice to this boxed slice.\n        #[allow(clippy::needless_lifetimes)]\n        #[inline]\n        pub fn exclusive_ref<'a>(&'a mut self) -> &'a mut SlimSliceMut<'a, T> {\n            // SAFETY: The reference lives as long as `self`\n            // and we have exclusive access to the heap data thanks to `&'a mut self`.\n            // By virtue of `repr(transparent)` we're also allowed these reference casts.\n            unsafe { mem::transmute(self) }\n        }\n\n        /// Map every element `x: T` to `U` by transmuting.\n        ///\n        /// This will not reallocate.\n        #[inline]\n        pub fn map_safely_exchangeable<U: SafelyExchangeable<T>>(self) -> SlimSliceBox<U> {\n            // SAFETY: By `U: SafelyExchangeable<T>`,\n            // the caller has proven that we can exchange `T -> U`\n            // even under an owned covariant pointer.\n            SlimSliceBox {\n                raw: self.raw.cast(),\n                owned: PhantomData,\n            }\n        }\n    }\n\n    impl<T> From<SlimSliceBox<T>> for Box<[T]> {\n        #[inline]\n        fn from(slice: SlimSliceBox<T>) -> Self {\n            let slice = ManuallyDrop::new(slice);\n            let (ptr, len) = slice.raw.split();\n            // SAFETY: All paths to creating a `SlimSliceBox`\n            // go through `SlimSliceBox::from_boxed_unchecked`\n            // which requires a valid `Box<[T]>`.\n            // The function also uses `Box::into_raw`\n            // and the original length is kept.\n            //\n            // It therefore follows that if we reuse the same\n            // pointer and length as given to us by a valid `Box<[T]>`,\n            // we can use `Box::from_raw` to reconstruct the `Box<[T]>`.\n            //\n            // We also no longer claim ownership of the data pointed to by `ptr`\n            // by virtue of `ManuallyDrop` preventing `Drop for SlimSliceBox<T>`.\n            unsafe { Box::from_raw(slice_from_raw_parts_mut(ptr, len)) }\n        }\n    }\n}\n\n/// `SlimSliceBox<T>` is `Send` if `T` is `Send` because the data is owned.\nunsafe impl<T: Send> Send for SlimSliceBox<T> {}\n\n/// `SlimSliceBox<T>` is `Sync` if `T` is `Sync` because the data is owned.\nunsafe impl<T: Sync> Sync for SlimSliceBox<T> {}\n\nimpl<T> Deref for SlimSliceBox<T> {\n    type Target = [T];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.shared_ref().deref()\n    }\n}\n\nimpl<T> DerefMut for SlimSliceBox<T> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.exclusive_ref().deref_mut()\n    }\n}\n\nimpl<T> SlimSliceBox<T> {\n    /// Converts `boxed: Box<[T]>` into `SlimSliceBox<T>`.\n    ///\n    /// Panics when `boxed.len() > u32::MAX`.\n    #[inline]\n    pub fn from_boxed(boxed: Box<[T]>) -> Self {\n        expect_fit(boxed)\n    }\n\n    /// Converts `vec: Vec<T>` into `SlimSliceBox<T>`.\n    ///\n    /// Panics when `vec.len() > u32::MAX`.\n    #[inline]\n    pub fn from_vec(vec: Vec<T>) -> Self {\n        Self::from_boxed(vec.into())\n    }\n\n    /// Maps all elements with `by` from `T` to `U`.\n    ///\n    /// Will allocate once for the new boxed slice.\n    #[inline]\n    pub fn map<U>(self, by: impl FnMut(T) -> U) -> SlimSliceBox<U> {\n        let mapped = self.into_iter().map(by).collect::<Box<_>>();\n        // SAFETY: Doing `.map(..)` can never change the length.\n        unsafe { SlimSliceBox::from_boxed_unchecked(mapped) }\n    }\n\n    /// Maps all elements with `by` from `&T` to `U`.\n    ///\n    /// Will allocate once for the new boxed slice.\n    #[inline]\n    pub fn map_borrowed<U>(&self, by: impl FnMut(&T) -> U) -> SlimSliceBox<U> {\n        let mapped = self.iter().map(by).collect::<Box<_>>();\n        // SAFETY: Doing `.map(..)` can never change the length.\n        unsafe { SlimSliceBox::from_boxed_unchecked(mapped) }\n    }\n}\n\nimpl<T> TryFrom<Box<[T]>> for SlimSliceBox<T> {\n    type Error = LenTooLong<Box<[T]>>;\n\n    #[inline]\n    fn try_from(boxed: Box<[T]>) -> Result<Self, Self::Error> {\n        ensure_len_fits!(boxed);\n        // SAFETY: Checked above that `len <= u32::MAX`.\n        Ok(unsafe { Self::from_boxed_unchecked(boxed) })\n    }\n}\n\nimpl<T> TryFrom<Vec<T>> for SlimSliceBox<T> {\n    type Error = LenTooLong<Vec<T>>;\n\n    #[inline]\n    fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {\n        ensure_len_fits!(vec);\n        // SAFETY: Checked above that `len <= u32::MAX`.\n        Ok(unsafe { Self::from_boxed_unchecked(vec.into_boxed_slice()) })\n    }\n}\n\nimpl<T, const N: usize> From<[T; N]> for SlimSliceBox<T> {\n    #[inline]\n    fn from(arr: [T; N]) -> Self {\n        #[allow(clippy::let_unit_value)]\n        let () = AssertU32::<N>::OK;\n\n        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.\n        unsafe { Self::from_boxed_unchecked(into_box(arr)) }\n    }\n}\n\nimpl<T> From<SlimSliceBox<T>> for Vec<T> {\n    #[inline]\n    fn from(slice: SlimSliceBox<T>) -> Self {\n        into_box(slice).into()\n    }\n}\n\nimpl<T: Debug> Debug for SlimSliceBox<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl<T: Clone> Clone for SlimSliceBox<T> {\n    #[inline]\n    fn clone(&self) -> Self {\n        // Allocate exactly the right amount\n        // so we later don't reallocate due to excess capacity.\n        let mut vec = Vec::with_capacity(self.len());\n        vec.extend_from_slice(self);\n        // SAFETY: We know `self.len() <= u32::MAX`.\n        unsafe { Self::from_boxed_unchecked(into_box(vec)) }\n    }\n}\n\nimpl<R: Deref, T> PartialEq<R> for SlimSliceBox<T>\nwhere\n    [T]: PartialEq<R::Target>,\n{\n    #[inline]\n    fn eq(&self, other: &R) -> bool {\n        **self == **other\n    }\n}\n\nimpl<T: Eq> Eq for SlimSliceBox<T> {}\n\nimpl<R: Deref, T> PartialOrd<R> for SlimSliceBox<T>\nwhere\n    [T]: PartialOrd<R::Target>,\n{\n    #[inline]\n    fn partial_cmp(&self, other: &R) -> Option<Ordering> {\n        (**self).partial_cmp(&**other)\n    }\n}\n\nimpl<T: Ord> Ord for SlimSliceBox<T> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        (**self).cmp(&**other)\n    }\n}\n\nimpl<T: Hash> Hash for SlimSliceBox<T> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(&**self, state)\n    }\n}\n\nimpl<T> IntoIterator for SlimSliceBox<T> {\n    type Item = T;\n    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        Vec::from(self).into_iter()\n    }\n}\n\n/// A wrapper to achieve `FromIterator<T> for Result<SlimSliceBox<T>, Vec<T>>`.\n///\n/// We cannot do this directly due to orphan rules.\npub struct SlimSliceBoxCollected<T> {\n    /// The result of `from_iter`.\n    pub inner: Result<SlimSliceBox<T>, LenTooLong<Vec<T>>>,\n}\n\nimpl<T: Debug> SlimSliceBoxCollected<T> {\n    #[inline]\n    pub fn unwrap(self) -> SlimSliceBox<T> {\n        self.inner.expect(\"number of elements overflowed `u32::MAX`\")\n    }\n}\n\nimpl<A> FromIterator<A> for SlimSliceBoxCollected<A> {\n    #[inline]\n    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {\n        let inner = iter.into_iter().collect::<Vec<_>>().try_into();\n        SlimSliceBoxCollected { inner }\n    }\n}\n\n// =============================================================================\n// Owned boxed slice with SSO\n// =============================================================================\n\n#[derive(Clone)]\npub struct SlimSmallSliceBox<T, const N: usize>(SlimSmallSliceBoxData<T, N>);\n\n/// The representation of [`SlimSmallSliceBox<T>`].\n///\n/// The parameter `N` is the number of elements that can be inline.\n#[derive(Clone)]\nenum SlimSmallSliceBoxData<T, const N: usize> {\n    /// The data is inline, not using any indirections.\n    Inline([T; N]),\n    /// The data is boxed up.\n    Heap(SlimSliceBox<T>),\n}\n\nimpl<T, const N: usize> From<[T; N]> for SlimSmallSliceBox<T, N> {\n    fn from(value: [T; N]) -> Self {\n        #[allow(clippy::let_unit_value)]\n        let () = AssertU32::<N>::OK;\n\n        Self(SlimSmallSliceBoxData::Inline(value))\n    }\n}\n\nimpl<T, const N: usize> From<SlimSliceBox<T>> for SlimSmallSliceBox<T, N> {\n    fn from(value: SlimSliceBox<T>) -> Self {\n        Self(SlimSmallSliceBoxData::Heap(value))\n    }\n}\n\nimpl<T, const N: usize> From<SlimSmallSliceBox<T, N>> for SlimSliceBox<T> {\n    fn from(SlimSmallSliceBox(value): SlimSmallSliceBox<T, N>) -> Self {\n        match value {\n            SlimSmallSliceBoxData::Inline(i) => i.into(),\n            SlimSmallSliceBoxData::Heap(h) => h,\n        }\n    }\n}\n\nimpl<T, const N: usize> Deref for SlimSmallSliceBox<T, N> {\n    type Target = [T];\n    fn deref(&self) -> &Self::Target {\n        match &self.0 {\n            SlimSmallSliceBoxData::Inline(i) => i,\n            SlimSmallSliceBoxData::Heap(h) => h,\n        }\n    }\n}\n\nimpl<T, const N: usize> DerefMut for SlimSmallSliceBox<T, N> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        match &mut self.0 {\n            SlimSmallSliceBoxData::Inline(i) => i,\n            SlimSmallSliceBoxData::Heap(h) => h,\n        }\n    }\n}\n\nimpl<T: PartialEq, const N: usize> PartialEq for SlimSmallSliceBox<T, N> {\n    fn eq(&self, other: &Self) -> bool {\n        self.deref().eq(other.deref())\n    }\n}\n\nimpl<T: Eq, const N: usize> Eq for SlimSmallSliceBox<T, N> {}\n\nimpl<T: Debug, const N: usize> fmt::Debug for SlimSmallSliceBox<T, N> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl<T, const N: usize> From<SmallVec<[T; N]>> for SlimSmallSliceBox<T, N> {\n    fn from(value: SmallVec<[T; N]>) -> Self {\n        match value.into_inner() {\n            Ok(inline) => inline.into(),\n            Err(heap) => SlimSliceBox::from_boxed(heap.into_boxed_slice()).into(),\n        }\n    }\n}\n\n// =============================================================================\n// Owned boxed string slice\n// =============================================================================\n\n/// Provides a slimmer version of `Box<str>`\n/// using `u32` for its length instead of `usize`.\n#[repr(transparent)]\npub struct SlimStrBox {\n    /// The underlying byte slice.\n    raw: SlimSliceBox<u8>,\n}\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for SlimStrBox {\n    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {\n        s.serialize_str(self.deref())\n    }\n}\n\nimpl SlimStrBox {\n    /// Converts `boxed` to `Self` without checking `boxed.len() <= u32::MAX`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that `boxed.len() <= u32::MAX`.\n    #[inline]\n    pub unsafe fn from_boxed_unchecked(boxed: Box<str>) -> Self {\n        // SAFETY: Caller has promised that `boxed.len() <= u32::MAX`.\n        let raw = unsafe { SlimSliceBox::from_boxed_unchecked(into_box(boxed)) };\n        Self { raw }\n    }\n\n    /// Converts `boxed: Box<str>` into `SlimStrBox`.\n    ///\n    /// Panics when `boxed.len() > u32::MAX`.\n    #[inline]\n    pub fn from_boxed(boxed: Box<str>) -> Self {\n        expect_fit(boxed)\n    }\n\n    /// Converts `str: String` into `SlimStrBox`.\n    ///\n    /// Panics when `str.len() > u32::MAX`.\n    #[inline]\n    pub fn from_string(str: String) -> Self {\n        Self::from_boxed(str.into())\n    }\n\n    /// Returns a limited shared string slice to this boxed string slice.\n    #[allow(clippy::needless_lifetimes)]\n    #[inline]\n    pub fn shared_ref<'a>(&'a self) -> &'a SlimStr<'a> {\n        // SAFETY: The reference lives as long as `self`,\n        // we have shared access already,\n        // and by construction we know it's UTF-8.\n        unsafe { mem::transmute(self.raw.shared_ref()) }\n    }\n\n    /// Returns a limited mutable string slice to this boxed string slice.\n    #[allow(clippy::needless_lifetimes)]\n    #[inline]\n    pub fn exclusive_ref<'a>(&'a mut self) -> &'a mut SlimStrMut<'a> {\n        // SAFETY: The reference lives as long as `self`,\n        // we had `&mut self`,\n        // and by construction we know it's UTF-8.\n        unsafe { mem::transmute(self.raw.exclusive_ref()) }\n    }\n}\n\nimpl Deref for SlimStrBox {\n    type Target = str;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.shared_ref().deref()\n    }\n}\n\nimpl DerefMut for SlimStrBox {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.exclusive_ref().deref_mut()\n    }\n}\n\nimpl<const N: usize> From<NStr<N>> for SlimStrBox {\n    #[inline]\n    fn from(arr: NStr<N>) -> Self {\n        (&arr).into()\n    }\n}\n\nimpl<const N: usize> From<&NStr<N>> for SlimStrBox {\n    #[inline]\n    fn from(arr: &NStr<N>) -> Self {\n        <SlimStr<'_>>::from(arr).into()\n    }\n}\n\nimpl TryFrom<Box<str>> for SlimStrBox {\n    type Error = LenTooLong<Box<str>>;\n\n    #[inline]\n    fn try_from(boxed: Box<str>) -> Result<Self, Self::Error> {\n        ensure_len_fits!(boxed);\n        // SAFETY: Checked above that `len <= u32::MAX`.\n        Ok(unsafe { Self::from_boxed_unchecked(boxed) })\n    }\n}\n\nimpl TryFrom<String> for SlimStrBox {\n    type Error = LenTooLong<String>;\n\n    #[inline]\n    fn try_from(str: String) -> Result<Self, Self::Error> {\n        ensure_len_fits!(str);\n        // SAFETY: Checked above that `len <= u32::MAX`.\n        Ok(unsafe { Self::from_boxed_unchecked(str.into_boxed_str()) })\n    }\n}\n\nimpl<'a> TryFrom<&'a str> for SlimStrBox {\n    type Error = LenTooLong<&'a str>;\n\n    #[inline]\n    fn try_from(str: &'a str) -> Result<Self, Self::Error> {\n        str.try_into().map(|s: SlimStr<'_>| s.into())\n    }\n}\n\nimpl From<SlimStrBox> for Box<str> {\n    #[inline]\n    fn from(str: SlimStrBox) -> Self {\n        let raw_box = into_box(str.raw);\n        // SAFETY: By construction, `SlimStrBox` is valid UTF-8.\n        unsafe { Box::from_raw(Box::into_raw(raw_box) as *mut str) }\n    }\n}\n\nimpl From<SlimStrBox> for String {\n    #[inline]\n    fn from(str: SlimStrBox) -> Self {\n        <Box<str>>::from(str).into()\n    }\n}\n\nimpl Debug for SlimStrBox {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl Display for SlimStrBox {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(self.deref(), f)\n    }\n}\n\nimpl Clone for SlimStrBox {\n    #[inline]\n    fn clone(&self) -> Self {\n        Self { raw: self.raw.clone() }\n    }\n}\n\nimpl<R: Deref> PartialEq<R> for SlimStrBox\nwhere\n    str: PartialEq<R::Target>,\n{\n    #[inline]\n    fn eq(&self, other: &R) -> bool {\n        self.deref() == other.deref()\n    }\n}\n\nimpl Eq for SlimStrBox {}\n\nimpl<R: Deref> PartialOrd<R> for SlimStrBox\nwhere\n    str: PartialOrd<R::Target>,\n{\n    #[inline]\n    fn partial_cmp(&self, other: &R) -> Option<Ordering> {\n        self.deref().partial_cmp(other.deref())\n    }\n}\n\nimpl Ord for SlimStrBox {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.deref().cmp(other.deref())\n    }\n}\n\nimpl Hash for SlimStrBox {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(self.deref(), state)\n    }\n}\n\nimpl Borrow<str> for SlimStrBox {\n    #[inline]\n    fn borrow(&self) -> &str {\n        self\n    }\n}\n\n// =============================================================================\n// Shared slice reference\n// =============================================================================\n\n#[allow(clippy::module_inception)]\nmod slim_slice {\n    use super::*;\n\n    /// A shared reference to `[T]` limited to `u32::MAX` in length.\n    #[repr(transparent)]\n    #[derive(Clone, Copy)]\n    pub struct SlimSlice<'a, T> {\n        /// The representation of this shared slice.\n        raw: SlimRawSlice<T>,\n        /// Marker to ensure covariance for `'a`.\n        covariant: PhantomData<&'a [T]>,\n    }\n\n    impl<'a, T> SlimSlice<'a, T> {\n        /// Converts a `&[T]` to the limited version without length checking.\n        ///\n        /// SAFETY: `slice.len() <= u32::MAX` must hold.\n        pub(super) const unsafe fn from_slice_unchecked(slice: &'a [T]) -> Self {\n            unsafe {\n                let len = slice.len();\n                let ptr = slice.as_ptr().cast_mut();\n                // SAFETY: `&mut [T]` implies that the pointer is non-null.\n                let raw = SlimRawSlice::from_len_ptr(len, ptr);\n                // SAFETY: Our length invariant is satisfied by the caller.\n                let covariant = PhantomData;\n                Self { raw, covariant }\n            }\n        }\n    }\n\n    impl<T> Deref for SlimSlice<'_, T> {\n        type Target = [T];\n\n        fn deref(&self) -> &Self::Target {\n            // SAFETY: `ptr` and `len` are either\n            // a) derived from a live `Box<[T]>` valid for `'self`\n            // b) derived from a live `&'self [T]`\n            // so we satisfy all safety requirements for `from_raw_parts`.\n            unsafe { self.raw.deref() }\n        }\n    }\n}\npub use slim_slice::*;\n\nuse crate::nstr::NStr;\n\n// SAFETY: Same rules as for `&[T]`.\nunsafe impl<T: Send + Sync> Send for SlimSlice<'_, T> {}\n\n// SAFETY: Same rules as for `&[T]`.\nunsafe impl<T: Sync> Sync for SlimSlice<'_, T> {}\n\nimpl<T> SlimSlice<'_, T> {\n    /// Falibly maps all elements with `by` to `Result<U, E>`.\n    ///\n    /// Returns `Err(_)` if any call to `by` did.\n    #[inline]\n    pub fn try_map<U, E>(&self, by: impl FnMut(&T) -> Result<U, E>) -> Result<SlimSliceBox<U>, E> {\n        let mapped = self.iter().map(by).collect::<Result<_, _>>()?;\n        // SAFETY: Doing `.map(..)` can never change the length.\n        Ok(unsafe { SlimSliceBox::from_boxed_unchecked(mapped) })\n    }\n}\n\nimpl<T: Debug> Debug for SlimSlice<'_, T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl<T: Hash> Hash for SlimSlice<'_, T> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(self.deref(), state)\n    }\n}\n\nimpl<T: Eq> Eq for SlimSlice<'_, T> {}\nimpl<T: PartialEq> PartialEq for SlimSlice<'_, T> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.deref() == other.deref()\n    }\n}\nimpl<T: PartialEq> PartialEq<[T]> for SlimSlice<'_, T> {\n    #[inline]\n    fn eq(&self, other: &[T]) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl<T: Ord> Ord for SlimSlice<'_, T> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.deref().cmp(other)\n    }\n}\nimpl<T: PartialOrd> PartialOrd for SlimSlice<'_, T> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        self.deref().partial_cmp(other.deref())\n    }\n}\nimpl<T: PartialOrd> PartialOrd<[T]> for SlimSlice<'_, T> {\n    #[inline]\n    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {\n        self.deref().partial_cmp(other)\n    }\n}\n\nimpl<T: Clone> From<&SlimSlice<'_, T>> for SlimSliceBox<T> {\n    #[inline]\n    fn from(slice: &SlimSlice<'_, T>) -> Self {\n        let boxed = into_box(slice.deref());\n        // SAFETY: `slice` is limited to `len: u32` by construction.\n        unsafe { Self::from_boxed_unchecked(boxed) }\n    }\n}\nimpl<T: Clone> From<&SlimSlice<'_, T>> for Box<[T]> {\n    #[inline]\n    fn from(slice: &SlimSlice<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl<T: Clone> From<&SlimSlice<'_, T>> for Vec<T> {\n    #[inline]\n    fn from(slice: &SlimSlice<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<T: Clone> From<SlimSlice<'_, T>> for SlimSliceBox<T> {\n    #[inline]\n    fn from(slice: SlimSlice<'_, T>) -> Self {\n        (&slice).into()\n    }\n}\nimpl<T: Clone> From<SlimSlice<'_, T>> for Box<[T]> {\n    #[inline]\n    fn from(slice: SlimSlice<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl<T: Clone> From<SlimSlice<'_, T>> for Vec<T> {\n    #[inline]\n    fn from(slice: SlimSlice<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<'a, T> TryFrom<&'a [T]> for SlimSlice<'a, T> {\n    type Error = LenTooLong<&'a [T]>;\n\n    #[inline]\n    fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {\n        ensure_len_fits!(slice);\n        // SAFETY: ^-- satisfies `len <= u32::MAX`.\n        Ok(unsafe { Self::from_slice_unchecked(slice) })\n    }\n}\n\n/// Converts `&[T]` into the slim limited version.\n///\n/// Panics when `slice.len() > u32::MAX`.\n#[inline]\npub fn from_slice<T>(s: &[T]) -> SlimSlice<'_, T> {\n    expect_fit(s)\n}\n\n// =============================================================================\n// Mutable slice reference\n// =============================================================================\n\n/// A mutable reference to `[T]` limited to `u32::MAX` in length.\n#[repr(transparent)]\npub struct SlimSliceMut<'a, T> {\n    /// The representation of this mutable slice.\n    raw: SlimRawSlice<T>,\n    /// Marker to ensure invariance for `'a`.\n    invariant: PhantomData<&'a mut [T]>,\n}\n\n// SAFETY: Same rules as for `&mut [T]`.\nunsafe impl<T: Send> Send for SlimSliceMut<'_, T> {}\n\n// SAFETY: Same rules as for `&mut [T]`.\nunsafe impl<T: Sync> Sync for SlimSliceMut<'_, T> {}\n\nimpl<'a, T> SlimSliceMut<'a, T> {\n    /// Convert this mutable reference to a shared one.\n    #[inline]\n    pub fn shared(&'a self) -> &'a SlimSlice<'a, T> {\n        // SAFETY: By virtue of `&'a mut X -> &'a X` being sound, this is also.\n        // The types and references to them have the same layout as well.\n        unsafe { mem::transmute(self) }\n    }\n\n    /// Converts a `&mut [T]` to the limited version without length checking.\n    ///\n    /// SAFETY: `slice.len() <= u32::MAX` must hold.\n    #[inline]\n    unsafe fn from_slice_unchecked(slice: &'a mut [T]) -> Self {\n        unsafe {\n            // SAFETY: `&mut [T]` implies that the pointer is non-null.\n            let raw = SlimRawSlice::from_len_ptr(slice.len(), slice.as_mut_ptr());\n            // SAFETY: Our invariants are satisfied by the caller\n            // and that `&mut [T]` implies exclusive access to the data.\n            let invariant = PhantomData;\n            Self { raw, invariant }\n        }\n    }\n}\n\nimpl<T> Deref for SlimSliceMut<'_, T> {\n    type Target = [T];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.shared().deref()\n    }\n}\n\nimpl<T> DerefMut for SlimSliceMut<'_, T> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // SAFETY: `ptr` and `len` are either\n        // a) derived from a live `Box<[T]>` valid for `'self`\n        // b) derived from a live `&'self [T]`\n        // and additionally, we have the only pointer to the data.\n        // so we satisfy all safety requirements for `from_raw_parts_mut`.\n        unsafe { self.raw.deref_mut() }\n    }\n}\n\nimpl<T: Debug> Debug for SlimSliceMut<'_, T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl<T: Hash> Hash for SlimSliceMut<'_, T> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(self.deref(), state)\n    }\n}\n\nimpl<T: Eq> Eq for SlimSliceMut<'_, T> {}\nimpl<T: PartialEq> PartialEq for SlimSliceMut<'_, T> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.deref() == other.deref()\n    }\n}\nimpl<T: PartialEq> PartialEq<[T]> for SlimSliceMut<'_, T> {\n    #[inline]\n    fn eq(&self, other: &[T]) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl<T: Ord> Ord for SlimSliceMut<'_, T> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.deref().cmp(other)\n    }\n}\nimpl<T: PartialOrd> PartialOrd for SlimSliceMut<'_, T> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        self.deref().partial_cmp(other.deref())\n    }\n}\nimpl<T: PartialOrd> PartialOrd<[T]> for SlimSliceMut<'_, T> {\n    #[inline]\n    fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {\n        self.deref().partial_cmp(other)\n    }\n}\n\nimpl<T: Clone> From<&SlimSliceMut<'_, T>> for SlimSliceBox<T> {\n    #[inline]\n    fn from(slice: &SlimSliceMut<'_, T>) -> Self {\n        // SAFETY: `slice` is limited to `len: u32` by construction.\n        unsafe { Self::from_boxed_unchecked(into_box(slice.deref())) }\n    }\n}\nimpl<T: Clone> From<&SlimSliceMut<'_, T>> for Box<[T]> {\n    #[inline]\n    fn from(slice: &SlimSliceMut<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl<T: Clone> From<&SlimSliceMut<'_, T>> for Vec<T> {\n    #[inline]\n    fn from(slice: &SlimSliceMut<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<T: Clone> From<SlimSliceMut<'_, T>> for SlimSliceBox<T> {\n    #[inline]\n    fn from(slice: SlimSliceMut<'_, T>) -> Self {\n        (&slice).into()\n    }\n}\nimpl<T: Clone> From<SlimSliceMut<'_, T>> for Box<[T]> {\n    #[inline]\n    fn from(slice: SlimSliceMut<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl<T: Clone> From<SlimSliceMut<'_, T>> for Vec<T> {\n    #[inline]\n    fn from(slice: SlimSliceMut<'_, T>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<'a, T> TryFrom<&'a mut [T]> for SlimSliceMut<'a, T> {\n    type Error = LenTooLong<&'a mut [T]>;\n\n    #[inline]\n    fn try_from(slice: &'a mut [T]) -> Result<Self, Self::Error> {\n        ensure_len_fits!(slice);\n        // SAFETY: ^-- satisfies `len <= u32::MAX`.\n        Ok(unsafe { Self::from_slice_unchecked(slice) })\n    }\n}\n\n/// Converts `&mut [T]` into the slim limited version.\n///\n/// Panics when `slice.len() > u32::MAX`.\n#[inline]\npub fn from_slice_mut<T>(s: &mut [T]) -> SlimSliceMut<'_, T> {\n    expect_fit(s)\n}\n\n// =============================================================================\n// Shared string slice reference\n// =============================================================================\n\n/// A shared reference to `str` limited to `u32::MAX` in length.\n#[repr(transparent)]\n#[derive(Clone, Copy)]\npub struct SlimStr<'a> {\n    /// The raw byte slice.\n    raw: SlimSlice<'a, u8>,\n}\n\nimpl<'a> SlimStr<'a> {\n    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that `s.len() <= u32::MAX`.\n    #[inline]\n    const unsafe fn from_str_unchecked(s: &'a str) -> Self {\n        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.\n        let raw = unsafe { SlimSlice::from_slice_unchecked(s.as_bytes()) };\n        // SAFETY: `s: &str` is always UTF-8.\n        Self { raw }\n    }\n}\n\nimpl Deref for SlimStr<'_> {\n    type Target = str;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.\n        unsafe { from_utf8_unchecked(self.raw.deref()) }\n    }\n}\n\nimpl Debug for SlimStr<'_> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl Display for SlimStr<'_> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(self.deref(), f)\n    }\n}\n\nimpl Hash for SlimStr<'_> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(self.deref(), state)\n    }\n}\n\nimpl Eq for SlimStr<'_> {}\nimpl PartialEq for SlimStr<'_> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.deref() == other.deref()\n    }\n}\nimpl PartialEq<str> for SlimStr<'_> {\n    #[inline]\n    fn eq(&self, other: &str) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl Ord for SlimStr<'_> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.deref().cmp(other)\n    }\n}\nimpl PartialOrd for SlimStr<'_> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\nimpl PartialOrd<str> for SlimStr<'_> {\n    #[inline]\n    fn partial_cmp(&self, other: &str) -> Option<Ordering> {\n        self.deref().partial_cmp(other)\n    }\n}\n\nimpl From<&SlimStr<'_>> for SlimStrBox {\n    #[inline]\n    fn from(slice: &SlimStr<'_>) -> Self {\n        (*slice).into()\n    }\n}\nimpl From<&SlimStr<'_>> for Box<str> {\n    #[inline]\n    fn from(slice: &SlimStr<'_>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl From<&SlimStr<'_>> for String {\n    #[inline]\n    fn from(slice: &SlimStr<'_>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl From<SlimStr<'_>> for SlimStrBox {\n    #[inline]\n    fn from(slice: SlimStr<'_>) -> Self {\n        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.\n        Self { raw: slice.raw.into() }\n    }\n}\nimpl From<SlimStr<'_>> for Box<str> {\n    #[inline]\n    fn from(slice: SlimStr<'_>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl From<SlimStr<'_>> for String {\n    #[inline]\n    fn from(slice: SlimStr<'_>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<'a, const N: usize> From<&'a NStr<N>> for SlimStr<'a> {\n    #[inline]\n    fn from(arr: &'a NStr<N>) -> Self {\n        #[allow(clippy::let_unit_value)]\n        let () = AssertU32::<N>::OK;\n\n        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.\n        unsafe { Self::from_str_unchecked(arr) }\n    }\n}\nimpl<'a> TryFrom<&'a str> for SlimStr<'a> {\n    type Error = LenTooLong<&'a str>;\n\n    #[inline]\n    fn try_from(s: &'a str) -> Result<Self, Self::Error> {\n        ensure_len_fits!(s);\n        // SAFETY: ^-- satisfies `len <= u32::MAX`.\n        Ok(unsafe { Self::from_str_unchecked(s) })\n    }\n}\n\n/// Converts `&str` into the slim limited version.\n///\n/// Panics when `str.len() > u32::MAX`.\n#[inline]\npub const fn from_str(s: &str) -> SlimStr<'_> {\n    if s.len() > u32::MAX as usize {\n        panic!(\"length didn't fit in `u32`\");\n    }\n\n    // SAFETY: ^-- satisfies `len <= u32::MAX`.\n    unsafe { SlimStr::from_str_unchecked(s) }\n}\n\n/// Converts `&str` into the owned slim limited version.\n///\n/// Panics when `str.len() > u32::MAX`.\n#[inline]\npub fn from_string(s: &str) -> SlimStrBox {\n    from_str(s).into()\n}\n\n// =============================================================================\n// Mutable string slice reference\n// =============================================================================\n\n/// A mutable reference to `str` limited to `u32::MAX` in length.\n#[repr(transparent)]\npub struct SlimStrMut<'a> {\n    /// The raw byte slice.\n    raw: SlimSliceMut<'a, u8>,\n}\n\nimpl<'a> SlimStrMut<'a> {\n    /// Converts `s` to `Self` without checking `s.len() <= u32::MAX`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that `s.len() <= u32::MAX`.\n    #[inline]\n    unsafe fn from_str_unchecked(s: &'a mut str) -> Self {\n        // SAFETY: Caller has promised that `s.len() <= u32::MAX`.\n        let raw = unsafe { SlimSliceMut::from_slice_unchecked(s.as_bytes_mut()) };\n        // SAFETY: `s: &mut str` is always UTF-8.\n        Self { raw }\n    }\n}\n\nimpl Deref for SlimStrMut<'_> {\n    type Target = str;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.\n        unsafe { from_utf8_unchecked(self.raw.deref()) }\n    }\n}\n\nimpl DerefMut for SlimStrMut<'_> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // SAFETY: Data is derived from `str` originally so it's valid UTF-8.\n        unsafe { from_utf8_unchecked_mut(self.raw.deref_mut()) }\n    }\n}\n\nimpl Debug for SlimStrMut<'_> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Debug::fmt(self.deref(), f)\n    }\n}\n\nimpl Display for SlimStrMut<'_> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        Display::fmt(self.deref(), f)\n    }\n}\n\nimpl Hash for SlimStrMut<'_> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        Hash::hash(self.deref(), state)\n    }\n}\n\nimpl Eq for SlimStrMut<'_> {}\nimpl PartialEq for SlimStrMut<'_> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.deref() == other.deref()\n    }\n}\nimpl PartialEq<str> for SlimStrMut<'_> {\n    #[inline]\n    fn eq(&self, other: &str) -> bool {\n        self.deref() == other\n    }\n}\n\nimpl Ord for SlimStrMut<'_> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.deref().cmp(other)\n    }\n}\nimpl PartialOrd for SlimStrMut<'_> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\nimpl PartialOrd<str> for SlimStrMut<'_> {\n    #[inline]\n    fn partial_cmp(&self, other: &str) -> Option<Ordering> {\n        self.deref().partial_cmp(other)\n    }\n}\n\nimpl From<&SlimStrMut<'_>> for SlimStrBox {\n    #[inline]\n    fn from(slice: &SlimStrMut<'_>) -> Self {\n        // SAFETY: `slice` is limited to `len: u32` by construction + UTF-8.\n        Self {\n            raw: (&slice.raw).into(),\n        }\n    }\n}\nimpl From<&SlimStrMut<'_>> for Box<str> {\n    #[inline]\n    fn from(slice: &SlimStrMut<'_>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl From<&SlimStrMut<'_>> for String {\n    #[inline]\n    fn from(slice: &SlimStrMut<'_>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl From<SlimStrMut<'_>> for SlimStrBox {\n    #[inline]\n    fn from(slice: SlimStrMut<'_>) -> Self {\n        (&slice).into()\n    }\n}\nimpl From<SlimStrMut<'_>> for Box<str> {\n    #[inline]\n    fn from(slice: SlimStrMut<'_>) -> Self {\n        slice.deref().into()\n    }\n}\nimpl From<SlimStrMut<'_>> for String {\n    #[inline]\n    fn from(slice: SlimStrMut<'_>) -> Self {\n        slice.deref().into()\n    }\n}\n\nimpl<'a, const N: usize> From<&'a mut NStr<N>> for SlimStrMut<'a> {\n    #[inline]\n    fn from(arr: &'a mut NStr<N>) -> Self {\n        #[allow(clippy::let_unit_value)]\n        let () = AssertU32::<N>::OK;\n\n        // SAFETY: We verified statically by `AssertU32<N>` above that `N` fits in u32.\n        unsafe { Self::from_str_unchecked(arr) }\n    }\n}\nimpl<'a> TryFrom<&'a mut str> for SlimStrMut<'a> {\n    type Error = LenTooLong<&'a mut str>;\n\n    #[inline]\n    fn try_from(slice: &'a mut str) -> Result<Self, Self::Error> {\n        ensure_len_fits!(slice);\n        // SAFETY: ^-- satisfies `len <= u32::MAX`.\n        Ok(unsafe { Self::from_str_unchecked(slice) })\n    }\n}\n\n/// Converts `&mut str` into the slim limited version.\n///\n/// Panics when `str.len() > u32::MAX`.\n#[inline]\npub fn from_str_mut(s: &mut str) -> SlimStrMut<'_> {\n    expect_fit(s)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::hash::BuildHasher;\n\n    use super::*;\n    use crate::map::DefaultHashBuilder;\n    use crate::nstr;\n\n    fn hash_of<T: Hash>(x: T) -> u64 {\n        DefaultHashBuilder::default().hash_one(&x)\n    }\n\n    fn hash_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)\n    where\n        T: Hash + Debug + Deref,\n        <T as Deref>::Target: Hash,\n    {\n        assert_eq!(hash_of(a), hash_of(a_deref));\n        assert_eq!(hash_of(b), hash_of(b_deref));\n        assert_ne!(hash_of(a), hash_of(b));\n    }\n\n    fn ord_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)\n    where\n        T: Ord + Debug + Deref + PartialOrd<<T as Deref>::Target>,\n    {\n        assert_eq!(a.partial_cmp(b), Some(Ordering::Less));\n        assert_eq!(b.partial_cmp(a), Some(Ordering::Greater));\n        assert_eq!(a.partial_cmp(a), Some(Ordering::Equal));\n        assert_eq!(b.partial_cmp(b), Some(Ordering::Equal));\n        assert_eq!(a.partial_cmp(b_deref), Some(Ordering::Less));\n        assert_eq!(b.partial_cmp(a_deref), Some(Ordering::Greater));\n        assert_eq!(a.partial_cmp(a_deref), Some(Ordering::Equal));\n        assert_eq!(b.partial_cmp(b_deref), Some(Ordering::Equal));\n\n        assert_eq!(a.cmp(b), Ordering::Less);\n        assert_eq!(b.cmp(a), Ordering::Greater);\n        assert_eq!(a.cmp(a), Ordering::Equal);\n        assert_eq!(b.cmp(b), Ordering::Equal);\n    }\n\n    #[allow(clippy::eq_op)]\n    fn eq_properties<T>(a: &T, b: &T, a_deref: &T::Target, b_deref: &T::Target)\n    where\n        T: Eq + Debug + Deref + PartialEq<<T as Deref>::Target>,\n    {\n        assert!(a != b);\n        assert!(b != a);\n        assert_eq!(a, a);\n        assert!(a != b_deref);\n        assert!(a == a_deref);\n        assert!(b != a_deref);\n        assert!(b == b_deref);\n    }\n\n    fn debug_properties<T: Debug, U: ?Sized + Debug>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {\n        assert_eq!(format!(\"{a:?}\"), format!(\"{:?}\", a_cmp));\n        assert_eq!(format!(\"{b:?}\"), format!(\"{:?}\", b_cmp));\n    }\n\n    fn display_properties<T: Debug + Display, U: ?Sized + Display>(a: &T, b: &T, a_cmp: &U, b_cmp: &U) {\n        assert_eq!(a.to_string(), a_cmp.to_string());\n        assert_eq!(b.to_string(), b_cmp.to_string());\n    }\n\n    fn general_properties<T, U>(a: &T, b: &T, a_deref: &U, b_deref: &U)\n    where\n        T: Deref<Target = U> + Debug + Eq + PartialEq<U> + PartialOrd<U> + Ord + Hash,\n        U: ?Sized + Debug + Eq + Ord + Hash,\n    {\n        eq_properties(a, b, a_deref, b_deref);\n        ord_properties(a, b, a_deref, b_deref);\n        hash_properties(a, b, a_deref, b_deref);\n        debug_properties(a, b, a_deref, b_deref);\n    }\n\n    const TEST_STR: &str = \"foo\";\n    const TEST_STR2: &str = \"fop\";\n    const TEST_SLICE: &[u8] = TEST_STR.as_bytes();\n    const TEST_SLICE2: &[u8] = TEST_STR2.as_bytes();\n\n    fn test_strings() -> [String; 2] {\n        [TEST_STR.to_string(), TEST_STR2.to_string()]\n    }\n\n    fn test_slices() -> [Vec<u8>; 2] {\n        [TEST_SLICE.to_owned(), TEST_SLICE2.to_owned()]\n    }\n\n    fn various_boxed_slices() -> [[SlimSliceBox<u8>; 2]; 5] {\n        [\n            test_slices().map(SlimSliceBox::from_vec),\n            test_slices().map(Box::from).map(SlimSliceBox::from_boxed),\n            test_slices().map(|s| SlimSliceBox::try_from(s).unwrap()),\n            test_slices().map(|s| SlimSliceBox::try_from(s.into_boxed_slice()).unwrap()),\n            test_slices().map(|s| SlimSliceBox::from(<[u8; 3]>::try_from(s).unwrap())),\n        ]\n    }\n\n    fn various_boxed_strs() -> [[SlimStrBox; 2]; 7] {\n        [\n            [nstr!(\"foo\"), nstr!(\"fop\")],\n            test_strings().map(|s| from_string(&s)),\n            test_strings().map(SlimStrBox::from_string),\n            test_strings().map(Box::from).map(SlimStrBox::from_boxed),\n            test_strings().map(|s| SlimStrBox::try_from(s).unwrap()),\n            test_strings().map(|s| SlimStrBox::try_from(s.into_boxed_str()).unwrap()),\n            test_strings().map(|s| SlimStrBox::try_from(s.deref()).unwrap()),\n        ]\n    }\n\n    fn assert_str_mut_properties(s1: &mut SlimStrMut<'_>, s2: &mut SlimStrMut<'_>) {\n        let a: &SlimStrMut<'_> = s1;\n        let b: &SlimStrMut<'_> = s2;\n\n        assert_eq!(a.deref(), TEST_STR);\n        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);\n        assert_eq!(b.deref(), TEST_STR2);\n\n        assert_eq!(String::from(a), TEST_STR);\n        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);\n        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);\n        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);\n\n        general_properties(a, b, TEST_STR, TEST_STR2);\n        display_properties(a, b, TEST_STR, TEST_STR2);\n\n        s1.deref_mut().make_ascii_uppercase();\n        assert_eq!(&**s1, TEST_STR.to_uppercase());\n    }\n\n    #[test]\n    fn str_mut_call() {\n        let [mut s1, mut s2] = test_strings();\n        let s1 = &mut from_str_mut(s1.as_mut_str());\n        let s2 = &mut from_str_mut(s2.as_mut_str());\n        assert_str_mut_properties(s1, s2);\n    }\n\n    #[test]\n    fn str_mut_try_into() {\n        let [mut s1, mut s2] = test_strings();\n        let s1: &mut SlimStrMut = &mut s1.as_mut().try_into().unwrap();\n        let s2: &mut SlimStrMut = &mut s2.as_mut().try_into().unwrap();\n        assert_str_mut_properties(s1, s2);\n    }\n\n    #[test]\n    fn str_mut_exclusive_ref_various() {\n        for [mut a, mut b] in various_boxed_strs() {\n            assert_str_mut_properties(a.exclusive_ref(), b.exclusive_ref())\n        }\n    }\n\n    fn assert_str_properties(a: &SlimStr<'_>, b: &SlimStr<'_>) {\n        assert_eq!(a.deref(), TEST_STR);\n        assert_eq!(SlimStrBox::from(a).clone().deref(), TEST_STR);\n        assert_eq!(b.deref(), TEST_STR2);\n\n        assert_eq!(String::from(a), TEST_STR);\n        assert_eq!(<Box<str>>::from(a).deref(), TEST_STR);\n        assert_eq!(SlimStrBox::from(a).deref(), TEST_STR);\n        assert_eq!(String::from(SlimStrBox::from(a)).deref(), TEST_STR);\n        assert_eq!(<Box<str>>::from(SlimStrBox::from(a)).deref(), TEST_STR);\n\n        general_properties(a, b, TEST_STR, TEST_STR2);\n        display_properties(a, b, TEST_STR, TEST_STR2);\n    }\n\n    #[test]\n    fn str_call() {\n        let [s1, s2] = test_strings();\n        assert_str_properties(&from_str(&s1), &from_str(&s2));\n    }\n\n    #[test]\n    fn str_try_into() {\n        let [s1, s2] = test_strings();\n        let s1: &SlimStr = &mut s1.deref().try_into().unwrap();\n        let s2: &SlimStr = &mut s2.deref().try_into().unwrap();\n        assert_str_properties(s1, s2);\n    }\n\n    #[test]\n    fn str_shared_ref_various() {\n        for [a, b] in various_boxed_strs() {\n            assert_str_properties(a.shared_ref(), b.shared_ref())\n        }\n    }\n\n    fn assert_slice_mut_properties(s1: &mut SlimSliceMut<'_, u8>, s2: &mut SlimSliceMut<'_, u8>) {\n        let a: &SlimSliceMut<'_, u8> = s1;\n        let b: &SlimSliceMut<'_, u8> = s2;\n\n        assert_eq!(a.deref(), TEST_SLICE);\n        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);\n        assert_eq!(b.deref(), TEST_SLICE2);\n\n        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);\n        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);\n        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);\n        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);\n\n        general_properties(a, b, TEST_SLICE, TEST_SLICE2);\n\n        s1.deref_mut().make_ascii_uppercase();\n        let mut upper = TEST_SLICE.to_owned();\n        upper.iter_mut().for_each(|x| x.make_ascii_uppercase());\n        assert_eq!(&**s1, upper);\n    }\n\n    #[test]\n    fn slice_mut_call() {\n        let [mut s1, mut s2] = test_slices();\n        let s1 = &mut from_slice_mut(s1.as_mut());\n        let s2 = &mut from_slice_mut(s2.as_mut());\n        assert_slice_mut_properties(s1, s2);\n    }\n\n    #[test]\n    fn slice_mut_try_into() {\n        let [mut s1, mut s2] = test_slices();\n        let s1: &mut SlimSliceMut<u8> = &mut s1.deref_mut().try_into().unwrap();\n        let s2: &mut SlimSliceMut<u8> = &mut s2.deref_mut().try_into().unwrap();\n        assert_slice_mut_properties(s1, s2);\n    }\n\n    #[test]\n    fn slice_mut_exclusive_ref_various() {\n        for [mut a, mut b] in various_boxed_slices() {\n            assert_slice_mut_properties(a.exclusive_ref(), b.exclusive_ref());\n        }\n    }\n\n    fn assert_slice_properties(a: &SlimSlice<'_, u8>, b: &SlimSlice<'_, u8>) {\n        assert_eq!(a.deref(), TEST_SLICE);\n        assert_eq!(SlimSliceBox::from(a).clone().deref(), TEST_SLICE);\n        assert_eq!(b.deref(), TEST_SLICE2);\n\n        assert_eq!(<Vec<u8>>::from(a), TEST_SLICE);\n        assert_eq!(<Box<[u8]>>::from(a).deref(), TEST_SLICE);\n        assert_eq!(<SlimSliceBox<u8>>::from(a).deref(), TEST_SLICE);\n        assert_eq!(<Vec<u8>>::from(<SlimSliceBox<u8>>::from(a)).deref(), TEST_SLICE);\n\n        general_properties(a, b, TEST_SLICE, TEST_SLICE2);\n    }\n\n    #[test]\n    fn slice_call() {\n        let [s1, s2] = test_slices();\n        assert_slice_properties(&from_slice(&s1), &from_slice(&s2));\n    }\n\n    #[test]\n    fn slice_try_into() {\n        let [s1, s2] = test_slices();\n        let s1: &SlimSlice<u8> = &s1.deref().try_into().unwrap();\n        let s2: &SlimSlice<u8> = &s2.deref().try_into().unwrap();\n        assert_slice_properties(s1, s2);\n    }\n\n    #[test]\n    fn slice_shared_ref_various() {\n        for [a, b] in various_boxed_slices() {\n            assert_slice_properties(a.shared_ref(), b.shared_ref())\n        }\n    }\n}\n"
  },
  {
    "path": "crates/data-structures/src/small_map.rs",
    "content": "use crate::map::{HashCollectionExt as _, HashMap};\nuse core::hash::Hash;\nuse core::mem;\nuse either::Either;\nuse smallvec::SmallVec;\n\n/// A hash map optimized for small sizes,\n/// with a small size optimization for up to `N` entries\n/// and then a vector for up to `M`\n/// and then finally falling back to a real hash map after `M`.\n///\n/// The inline and heap based vectors use linear scans,\n/// which is faster than hashing as long as the map is small.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum SmallHashMap<K: Eq + Hash, V, const N: usize, const M: usize> {\n    Small(SmallVec<[(K, V); N]>),\n    Large(HashMap<K, V>),\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl<\n        K: Eq + Hash + spacetimedb_memory_usage::MemoryUsage,\n        V: spacetimedb_memory_usage::MemoryUsage,\n        const N: usize,\n        const M: usize,\n    > spacetimedb_memory_usage::MemoryUsage for SmallHashMap<K, V, N, M>\n{\n    fn heap_usage(&self) -> usize {\n        match self {\n            SmallHashMap::Small(vec) => vec.heap_usage(),\n            SmallHashMap::Large(map) => map.heap_usage(),\n        }\n    }\n}\n\nimpl<K: Eq + Hash, V, const N: usize, const M: usize> Default for SmallHashMap<K, V, N, M> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<K: Eq + Hash, V, const N: usize, const M: usize> SmallHashMap<K, V, N, M> {\n    pub fn new() -> Self {\n        Self::Small(SmallVec::new())\n    }\n\n    /// Inserts the association `key -> value` into the map,\n    /// returning the previous value for `key`, if any.\n    pub fn insert(&mut self, key: K, value: V) -> Option<V> {\n        // Possibly convert to large map first.\n        self.maybe_convert_to_large();\n\n        match self {\n            Self::Small(list) => {\n                if let Some(idx) = Self::key_pos(list, &key) {\n                    // SAFETY: `idx` was given by `key_pos`, so it must be in-bounds.\n                    let (_, val) = unsafe { list.get_unchecked_mut(idx) };\n                    return Some(mem::replace(val, value));\n                }\n\n                list.push((key, value));\n                None\n            }\n            Self::Large(map) => map.insert(key, value),\n        }\n    }\n\n    /// Returns either the existing value for `key`\n    /// or inserts into `key` using `or_insert`.\n    pub fn get_or_insert(&mut self, key: K, or_insert: impl FnOnce() -> V) -> &mut V {\n        // Possibly convert to large map first.\n        self.maybe_convert_to_large();\n\n        match self {\n            Self::Small(list) => {\n                if let Some(idx) = Self::key_pos(list, &key) {\n                    // SAFETY: `idx` was given by `key_pos`, so it must be in-bounds.\n                    let (_, val) = unsafe { list.get_unchecked_mut(idx) };\n                    return val;\n                }\n\n                list.push((key, or_insert()));\n                let last = list.last_mut();\n                // SAFETY: just inserted one element so `list` cannot be empty.\n                let (_, val) = unsafe { last.unwrap_unchecked() };\n                val\n            }\n            Self::Large(map) => map.entry(key).or_insert_with(or_insert),\n        }\n    }\n\n    #[inline]\n    fn maybe_convert_to_large(&mut self) {\n        if let Self::Small(list) = self\n            && list.len() > M\n        {\n            let list = mem::take(list);\n            self.convert_to_large(list);\n        }\n    }\n\n    #[cold]\n    #[inline(never)]\n    fn convert_to_large(&mut self, list: SmallVec<[(K, V); N]>) {\n        let mut map = HashMap::with_capacity(list.len());\n        map.extend(list);\n        *self = Self::Large(map);\n    }\n\n    pub fn remove(&mut self, key: &K) -> Option<V> {\n        match self {\n            Self::Small(list) => Self::key_pos(list, key).map(|idx| list.swap_remove(idx).1),\n            Self::Large(map) => map.remove(key),\n        }\n    }\n\n    /// Returns the position of `key` in `list`, if any.\n    fn key_pos(list: &[(K, V)], key: &K) -> Option<usize> {\n        list.iter().position(|(k, _)| k == key)\n    }\n\n    /// Clears all entries from the map.\n    pub fn clear(&mut self) {\n        match self {\n            Self::Small(list) => list.clear(),\n            Self::Large(map) => map.clear(),\n        }\n    }\n\n    /// Returns the number of entries in the map.\n    pub fn len(&self) -> usize {\n        match self {\n            Self::Small(list) => list.len(),\n            Self::Large(map) => map.len(),\n        }\n    }\n\n    /// Returns whether the map is empty.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns an iterator over all the key-value pairs in the map.\n    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&K, &V)> {\n        match self {\n            Self::Small(list) => Either::Left(list.iter().map(|(k, v)| (k, v))),\n            Self::Large(map) => Either::Right(map.iter()),\n        }\n    }\n\n    /// Returns an iterator over all the keys in the map.\n    pub fn keys(&self) -> impl ExactSizeIterator<Item = &K> {\n        match self {\n            Self::Small(list) => Either::Left(list.iter().map(|(k, _)| k)),\n            Self::Large(map) => Either::Right(map.keys()),\n        }\n    }\n\n    /// Returns an iterator over all the values in the map.\n    pub fn values(&self) -> impl ExactSizeIterator<Item = &V> {\n        match self {\n            Self::Small(list) => Either::Left(list.iter().map(|(_, v)| v)),\n            Self::Large(map) => Either::Right(map.values()),\n        }\n    }\n\n    /// Returns whether `key` is in the map.\n    pub fn contains_key(&self, key: &K) -> bool {\n        match self {\n            Self::Small(list) => list.iter().any(|(k, _)| k == key),\n            Self::Large(map) => map.contains_key(key),\n        }\n    }\n\n    /// Returns the value for `key`, if any.\n    pub fn get(&self, key: &K) -> Option<&V> {\n        match self {\n            Self::Small(list) => list.iter().find_map(|(k, v)| (k == key).then_some(v)),\n            Self::Large(map) => map.get(key),\n        }\n    }\n\n    /// Returns the value for `key`, mutably, if any.\n    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {\n        match self {\n            Self::Small(list) => list.iter_mut().find_map(|(k, v)| (k == key).then_some(v)),\n            Self::Large(map) => map.get_mut(key),\n        }\n    }\n}\n\nimpl<K: Eq + Hash, V, const N: usize, const M: usize> Extend<(K, V)> for SmallHashMap<K, V, N, M> {\n    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {\n        for (k, v) in iter {\n            self.insert(k, v);\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::collections::HashSet;\n\n    use super::SmallHashMap;\n    use proptest::collection::hash_set;\n    use proptest::prelude::*;\n\n    type K = u32;\n    type V = u32;\n    type Map<const N: usize, const M: usize> = SmallHashMap<K, V, N, M>;\n\n    /// Asserts that the map behaves consistently as an empty map.\n    fn assert_empty<const N: usize, const M: usize>(map: &mut Map<N, { M }>, key: &K, val: V) {\n        assert!(map.is_empty());\n        assert_eq!(map.len(), 0);\n\n        assert_eq!(map.iter().count(), 0);\n        assert_eq!(map.keys().count(), 0);\n        assert_eq!(map.values().count(), 0);\n\n        assert_eq!(Map::<N, M>::key_pos(&[], key), None);\n        assert!(!map.contains_key(key));\n        assert_eq!(map.get(key), None);\n        assert_eq!(map.get_mut(key), None);\n\n        assert_eq!(map.remove(key), None);\n        assert_eq!(map.insert(*key, val), None);\n    }\n\n    /// Asserts that the map behaves consistently as a non-empty map.\n    fn assert_not_empty<const N: usize, const M: usize>(map: &mut Map<N, M>, len: usize) {\n        assert!(!map.is_empty());\n        assert_eq!(map.len(), len);\n\n        assert_eq!(map.iter().count(), len);\n        assert_eq!(map.keys().count(), len);\n        assert_eq!(map.values().count(), len);\n    }\n\n    /// Extends the map with entries and then clears it,\n    /// asserting correct behavior before and after.\n    fn extend_clear<const N: usize, const M: usize>(map: &mut Map<N, M>, entries: &HashSet<(K, V)>) {\n        map.extend(entries.iter().cloned());\n        assert_not_empty(map, entries.len());\n\n        map.clear();\n        assert_empty(map, &0, 0);\n    }\n\n    /// Asserts that the map contains `key` with value `val`.\n    fn assert_key_eq<const N: usize, const M: usize>(map: &mut Map<N, M>, key: K, mut val: V) {\n        assert!(map.contains_key(&key));\n        assert_eq!(map.get(&key), Some(&val));\n        assert_eq!(map.get_mut(&key), Some(&mut val));\n    }\n\n    /// Asserts that the map does not contain `key`.\n    fn assert_key_none<const N: usize, const M: usize>(map: &mut Map<N, M>, key: K) {\n        assert!(!map.contains_key(&key));\n        assert_eq!(map.get(&key), None);\n        assert_eq!(map.get_mut(&key), None);\n    }\n\n    /// Inserting `key` twice returns the old value.\n    fn insert_returns_old_inner<const N: usize, const M: usize>(key: K, val1: V, val2: V, val3: V) {\n        let mut map = Map::<N, M>::new();\n\n        assert_key_none(&mut map, key);\n\n        assert_eq!(map.insert(key, val1), None);\n        assert_key_eq(&mut map, key, val1);\n\n        assert_eq!(map.insert(key, val2), Some(val1));\n        assert_key_eq(&mut map, key, val2);\n\n        assert_eq!(map.get_or_insert(key, || val3), &val2);\n    }\n\n    /// Mutating the value via `get_mut` has effect.\n    fn mutation_via_get_mut_inner<const N: usize, const M: usize>(key: K, val1: V, val2: V) {\n        let mut map = Map::<N, M>::new();\n\n        assert_eq!(map.insert(key, val1), None);\n        if let Some(slot) = map.get_mut(&key) {\n            *slot = val2;\n        }\n\n        assert_key_eq(&mut map, key, val2);\n    }\n\n    /// Mutating the value via `get_or_insert` has effect.\n    fn mutation_via_get_or_insert_inner<const N: usize, const M: usize>(key: K, val1: V, val2: V) {\n        let mut map = Map::<N, M>::new();\n\n        assert_eq!(map.insert(key, val1), None);\n        let slot = map.get_or_insert(key, || val2);\n        *slot = val2;\n\n        assert_eq!(map.get(&key), Some(&val2));\n    }\n\n    /// Collects `iter` into a sorted `Vec<T>`.\n    fn sorted<T: Ord>(iter: impl Iterator<Item = T>) -> Vec<T> {\n        let mut vec: Vec<T> = iter.collect();\n        vec.sort();\n        vec\n    }\n\n    /// Tests insertion, retrieval, and deletion together.\n    fn insert_get_remove_inner<const N: usize, const M: usize>(entries: &[(K, V)]) {\n        let mut map = Map::<N, M>::new();\n\n        // Initially all keys are absent.\n        for (k, _) in entries.iter() {\n            assert_key_none(&mut map, *k);\n        }\n\n        // Insert all entries.\n        for (k, v) in entries.iter() {\n            map.insert(*k, *v);\n        }\n\n        // Now all keys are present.\n        assert_not_empty(&mut map, entries.len());\n        for (k, v) in entries.iter().cloned() {\n            assert_key_eq(&mut map, k, v);\n        }\n\n        // Iterators return all entries.\n        assert_eq!(\n            sorted(map.iter().map(|(k, v)| (*k, *v))),\n            sorted(entries.iter().cloned())\n        );\n        assert_eq!(sorted(map.keys().cloned()), sorted(entries.iter().map(|(k, _)| *k)));\n        assert_eq!(sorted(map.values().cloned()), sorted(entries.iter().map(|(_, v)| *v)));\n\n        // Removal results in absence.\n        for (k, _) in entries.iter() {\n            assert!(map.remove(k).is_some());\n            assert_eq!(map.get(k), None);\n        }\n\n        // Finally the map is empty again.\n        assert!(map.is_empty());\n    }\n\n    #[test]\n    fn new_is_same_as_default() {\n        assert_eq!(Map::<8, 16>::new(), <_>::default());\n        assert_eq!(Map::<0, 16>::new(), <_>::default());\n        assert_eq!(Map::<0, 0>::new(), <_>::default());\n    }\n\n    proptest! {\n        #[test]\n        fn new_is_empty(key: K, val: V) {\n            assert_empty(&mut Map::<4, 8>::new(), &key, val);\n            assert_empty(&mut Map::<0, 8>::new(), &key, val);\n            assert_empty(&mut Map::<0, 0>::new(), &key, val);\n        }\n\n        #[test]\n        fn cleared_is_empty(entries in hash_set(any::<(K, V)>(), 1..50)) {\n            extend_clear(&mut Map::<4, 8>::new(), &entries);\n            extend_clear(&mut Map::<0, 8>::new(), &entries);\n            extend_clear(&mut Map::<0, 0>::new(), &entries);\n        }\n\n        #[test]\n        fn insert_returns_old(key: K, val1: V, val2: V) {\n            insert_returns_old_inner::<4, 8>(key, val1, val2, val2);\n            insert_returns_old_inner::<0, 8>(key, val1, val2, val2);\n            insert_returns_old_inner::<0, 0>(key, val1, val2, val2);\n        }\n\n        #[test]\n        fn mutation_via_get_mut(key: K, val1: V, val2: V) {\n            mutation_via_get_mut_inner::<4, 8>(key, val1, val2);\n            mutation_via_get_mut_inner::<0, 8>(key, val1, val2);\n            mutation_via_get_mut_inner::<0, 0>(key, val1, val2);\n        }\n\n        #[test]\n        fn mutation_via_get_or_insert(key: K, val1: V, val2: V) {\n            mutation_via_get_or_insert_inner::<4, 8>(key, val1, val2);\n            mutation_via_get_or_insert_inner::<0, 8>(key, val1, val2);\n            mutation_via_get_or_insert_inner::<0, 0>(key, val1, val2);\n        }\n\n        #[test]\n        fn insert_get_remove(entries in hash_set(any::<(K, V)>(), 1..50)) {\n            let entries: Vec<(K, V)> = entries.into_iter().collect();\n            insert_get_remove_inner::<4, 8>(&entries);\n            insert_get_remove_inner::<0, 8>(&entries);\n            insert_get_remove_inner::<0, 0>(&entries);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/datastore/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-datastore\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The datastore library for SpacetimeDB\"\nrust-version.workspace = true\n\n[dependencies]\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib = { workspace = true, features = [\"serde\", \"metrics_impls\"] }\nspacetimedb-commitlog.workspace = true\nspacetimedb-durability.workspace = true\nspacetimedb-metrics.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-sats = { workspace = true, features = [\"serde\"] }\nspacetimedb-schema.workspace = true\nspacetimedb-table.workspace = true\nspacetimedb-snapshot.workspace = true\nspacetimedb-execution.workspace = true\n\nanyhow = { workspace = true, features = [\"backtrace\"] }\nbytes.workspace = true\nderive_more.workspace = true\nenum-as-inner.workspace = true\nenum-map.workspace = true\nitertools.workspace = true\nlazy_static.workspace = true\nlog.workspace = true\nonce_cell.workspace = true\nparking_lot.workspace = true\nprometheus.workspace = true\nsmallvec.workspace = true\nstrum.workspace = true\nthiserror.workspace = true\nthin-vec.workspace = true\n\n[features]\n# Print a warning when doing an unindexed `iter_by_col_range` on a large table.\nunindexed_iter_by_col_range_warn = []\ndefault = [\"unindexed_iter_by_col_range_warn\"]\n# Enable test helpers and utils\ntest = [\"spacetimedb-commitlog/test\", \"spacetimedb-schema/test\"]\n\n[dev-dependencies]\nspacetimedb-lib = { path = \"../lib\", features = [\"proptest\"] }\nspacetimedb-sats = { path = \"../sats\", features = [\"proptest\"] }\nspacetimedb-commitlog = { path = \"../commitlog\", features = [\"test\"] }\n\n# Also as dev-dependencies for use in _this_ crate's tests.\nproptest.workspace = true\npretty_assertions.workspace = true\n"
  },
  {
    "path": "crates/datastore/src/db_metrics/data_size.rs",
    "content": "use once_cell::sync::Lazy;\nuse prometheus::IntGaugeVec;\nuse spacetimedb_lib::Identity;\nuse spacetimedb_metrics::metrics_group;\n\nmetrics_group!(\n    #[non_exhaustive]\n    pub struct DbDataSize {\n        #[name = spacetime_data_size_table_num_rows]\n        #[help = \"The number of rows in a table\"]\n        #[labels(db: Identity, table_name: str)]\n        pub data_size_table_num_rows: IntGaugeVec,\n\n        #[name = spacetime_data_size_bytes_used_by_rows]\n        #[help = \"The number of bytes used by rows in pages in a table\"]\n        #[labels(db: Identity, table_name: str)]\n        pub data_size_table_bytes_used_by_rows: IntGaugeVec,\n\n        #[name = spacetime_data_size_table_num_rows_in_indexes]\n        #[help = \"The number of rows stored in indexes in a table\"]\n        // TODO: Consider partitioning by index ID or index name.\n        #[labels(db: Identity, table_name: str)]\n        pub data_size_table_num_rows_in_indexes: IntGaugeVec,\n\n        #[name = spacetime_data_size_table_bytes_used_by_index_keys]\n        #[help = \"The number of bytes used by keys stored in indexes in a table\"]\n        #[labels(db: Identity, table_name: str)]\n        pub data_size_table_bytes_used_by_index_keys: IntGaugeVec,\n\n        #[name = spacetime_data_size_blob_store_num_blobs]\n        #[help = \"The number of large blobs stored in a database's blob store\"]\n        #[labels(db: Identity)]\n        pub data_size_blob_store_num_blobs: IntGaugeVec,\n\n        #[name = spacetime_data_size_blob_store_bytes_used_by_blobs]\n        #[help = \"The number of bytes used by large blobs stored in a database's blob store\"]\n        #[labels(db: Identity)]\n        pub data_size_blob_store_bytes_used_by_blobs: IntGaugeVec,\n    }\n);\n\npub static DATA_SIZE_METRICS: Lazy<DbDataSize> = Lazy::new(DbDataSize::new);\n"
  },
  {
    "path": "crates/datastore/src/db_metrics/mod.rs",
    "content": "use crate::execution_context::WorkloadType;\nuse once_cell::sync::Lazy;\nuse prometheus::{HistogramVec, IntCounterVec, IntGaugeVec};\nuse spacetimedb_lib::Identity;\nuse spacetimedb_metrics::metrics_group;\nuse spacetimedb_primitives::TableId;\n\npub mod data_size;\n\nmetrics_group!(\n    #[non_exhaustive]\n    pub struct DbMetrics {\n        #[name = spacetime_num_table_rows]\n        #[help = \"The number of rows in a table\"]\n        #[labels(db: Identity, table_id: u32, table_name: str)]\n        pub rdb_num_table_rows: IntGaugeVec,\n\n        #[name = spacetime_num_rows_inserted_total]\n        #[help = \"The cumulative number of rows inserted into a table\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)]\n        pub rdb_num_rows_inserted: IntCounterVec,\n\n        #[name = spacetime_num_index_rows_inserted_total]\n        #[help = \"The cumulative number of index entries inserted. Does not count schema changes.\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)]\n        pub rdb_num_index_entries_inserted: IntCounterVec,\n\n        #[name = spacetime_num_rows_deleted_total]\n        #[help = \"The cumulative number of rows deleted from a table\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)]\n        pub rdb_num_rows_deleted: IntCounterVec,\n\n        #[name = spacetime_num_index_rows_deleted_total]\n        #[help = \"The cumulative number of index entries deleted. Does not count schema changes.\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)]\n        pub rdb_num_index_entries_deleted: IntCounterVec,\n\n        #[name = spacetime_num_rows_scanned_total]\n        #[help = \"The cumulative number of rows scanned from the database\"]\n        #[labels(txn_type: WorkloadType, db: Identity)]\n        pub rdb_num_rows_scanned: IntCounterVec,\n\n        #[name = spacetime_num_bytes_scanned_total]\n        #[help = \"The cumulative number of bytes scanned from the database\"]\n        #[labels(txn_type: WorkloadType, db: Identity)]\n        pub rdb_num_bytes_scanned: IntCounterVec,\n\n        #[name = spacetime_num_bytes_written_total]\n        #[help = \"The cumulative number of bytes written to the database\"]\n        #[labels(txn_type: WorkloadType, db: Identity)]\n        pub rdb_num_bytes_written: IntCounterVec,\n\n        #[name = spacetime_num_index_seeks_total]\n        #[help = \"The cumulative number of index seeks\"]\n        #[labels(txn_type: WorkloadType, db: Identity)]\n        pub rdb_num_index_seeks: IntCounterVec,\n\n        #[name = spacetime_num_txns_total]\n        #[help = \"The cumulative number of transactions, including both commits and rollbacks\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer: str, committed: bool)]\n        pub rdb_num_txns: IntCounterVec,\n\n        #[name = spacetime_txn_elapsed_time_sec]\n        #[help = \"The total elapsed (wall) time of a transaction (in seconds)\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer: str)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        //\n        // However we expect a different value distribution for OLTP workloads.\n        // In particular the smallest bucket value is 5ms by default.\n        // But we expect many transactions to be on the order of microseconds.\n        #[buckets(10e-6, 50e-6, 100e-6, 500e-6, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10)]\n        pub rdb_txn_elapsed_time_sec: HistogramVec,\n\n        #[name = spacetime_txn_cpu_time_sec]\n        #[help = \"The time spent executing a transaction (in seconds), excluding time spent waiting to acquire database locks\"]\n        #[labels(txn_type: WorkloadType, db: Identity, reducer: str)]\n        // Prometheus histograms have default buckets,\n        // which broadly speaking,\n        // are tailored to measure the response time of a network service.\n        //\n        // However we expect a different value distribution for OLTP workloads.\n        // In particular the smallest bucket value is 5ms by default.\n        // But we expect many transactions to be on the order of microseconds.\n        #[buckets(10e-6, 50e-6, 100e-6, 500e-6, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10)]\n        pub rdb_txn_cpu_time_sec: HistogramVec,\n\n        #[name = spacetime_message_log_size_bytes]\n        #[help = \"For a given database, the number of bytes occupied by its message log\"]\n        #[labels(db: Identity)]\n        pub message_log_size: IntGaugeVec,\n\n        #[name = spacetime_message_log_size_blocks]\n        #[help = \"For a given database, the number of 512-byte blocks allocated by its message log\"]\n        #[labels(db: Identity)]\n        pub message_log_blocks: IntGaugeVec,\n\n        #[name = spacetime_module_log_file_size_bytes]\n        #[help = \"For a given module, the size of its log file (in bytes)\"]\n        #[labels(db: Identity)]\n        pub module_log_file_size: IntGaugeVec,\n\n        #[name = spacetime_table_size_bytes]\n        #[help = \"The number of bytes in a table with the precision of a page size\"]\n        #[labels(db: Identity, table_id: u32, table_name: str)]\n        pub rdb_table_size: IntGaugeVec,\n\n        #[name = reducer_wasmtime_fuel_used]\n        #[help = \"The total wasmtime fuel used\"]\n        #[labels(db: Identity, reducer: str)]\n        pub reducer_wasmtime_fuel_used: IntCounterVec,\n\n        #[name = reducer_wasm_time_usec]\n        #[help = \"The total runtime of reducer calls\"]\n        #[labels(db: Identity, reducer: str)]\n        pub reducer_duration_usec: IntCounterVec,\n\n        #[name = reducer_abi_time_usec]\n        #[help = \"The total time spent in reducer ABI calls\"]\n        #[labels(db: Identity, reducer: str)]\n        pub reducer_abi_time_usec: IntCounterVec,\n\n        #[name = spacetime_num_delta_queries_evaluated]\n        #[help = \"The total number of times we performed incremental evaluation of a query\"]\n        #[labels(db: Identity)]\n        pub delta_queries_evaluated: IntCounterVec,\n\n        #[name = spacetime_num_delta_queries_matched]\n        #[help = \"The total number of times incremental evaluation resulted in a subscription update\"]\n        #[labels(db: Identity)]\n        pub delta_queries_matched: IntCounterVec,\n\n        #[name = spacetime_num_duplicate_rows_evaluated]\n        #[help = \"The number of times we evaluate the same row in a subscription update\"]\n        #[labels(db: Identity)]\n        pub duplicate_rows_evaluated: IntCounterVec,\n\n        #[name = spacetime_num_duplicate_rows_sent]\n        #[help = \"The number of duplicate rows we send in a subscription update\"]\n        #[labels(db: Identity)]\n        pub duplicate_rows_sent: IntCounterVec,\n\n        #[name = spacetime_subscription_connections]\n        #[help = \"Number of connections with active subscriptions\"]\n        #[labels(database_identity: Identity)]\n        pub subscription_connections: IntGaugeVec,\n\n        #[name = spacetime_subscription_sets]\n        #[help = \"Number of different subscription sets\"]\n        #[labels(database_identity: Identity)]\n        pub subscription_sets: IntGaugeVec,\n\n        #[name = spacetime_query_subscriptions]\n        #[help = \"Total number of subscriptions across all clients and queries\"]\n        #[labels(database_identity: Identity)]\n        pub total_query_subscriptions: IntGaugeVec,\n\n        #[name = spacetime_legacy_subscriptions]\n        #[help = \"Number of subscriptions via the legacy api\"]\n        #[labels(database_identity: Identity)]\n        pub num_legacy_subscriptions: IntGaugeVec,\n\n        #[name = spacetime_subscription_compile_time_sec]\n        #[help = \"How much time (in seconds) do we spend compiling subscriptions\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        pub subscription_compile_time: HistogramVec,\n\n        #[name = spacetime_subscription_lock_num_waiters]\n        #[help = \"The number of clients waiting to acquire the subscription lock\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        pub subscription_lock_waiters: IntGaugeVec,\n\n        #[name = spacetime_subscription_lock_wait_time_sec]\n        #[help = \"How much time (in seconds) do we spend waiting to acquire the subscription lock\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        pub subscription_lock_wait_time: HistogramVec,\n\n        #[name = spacetime_num_queries_subscribed]\n        #[help = \"How many total queries make up each subscribe call\"]\n        #[labels(db: Identity)]\n        pub num_queries_subscribed: IntCounterVec,\n\n        #[name = spacetime_num_new_queries_subscribed]\n        #[help = \"How many new (uncached) queries are make up each subscribe call\"]\n        #[labels(db: Identity)]\n        pub num_new_queries_subscribed: IntCounterVec,\n\n        #[name = spacetime_num_queries_evaluated]\n        #[help = \"How many queries are evaluated in each subscribe and unsubscribe\"]\n        #[labels(db: Identity, workload: WorkloadType)]\n        pub num_queries_evaluated: IntCounterVec,\n\n        #[name = spacetime_procedure_http_request_size_bytes]\n        #[help = \"Size in bytes of HTTP requests performed by procedures running in databases.\n\nAn individual HTTP request's size in bytes is the sum of the sizes of the URI, header names, header values and body.\"]\n        #[labels(db: Identity)]\n        pub procedure_http_request_size_bytes: IntCounterVec,\n\n        #[name = spacetime_procedure_http_response_size_bytes]\n        #[help = \"Size in bytes of HTTP responses to requests performed by procedures running in databases.\n\nAn individual HTTP response's size in bytes is the sum of the sizes of the header names, header values and body.\"]\n        #[labels(db: Identity)]\n        pub procedure_http_response_size_bytes: IntCounterVec,\n\n        #[name = spacetime_procedure_num_http_requests]\n        #[help = \"Number of HTTP requests performed by procedures running in databases.\n\nShould be the sum of `spacetime_procedure_num_successful_http_requests`,\n`spacetime_procedure_num_failed_http_requests`, `spacetime_procedure_num_timeout_http_requests`\nand `spacetime_procedure_num_in_progress_http_requests`.\"]\n        #[labels(db: Identity)]\n        pub procedure_num_http_requests: IntCounterVec,\n\n        #[name = spacetime_procedure_num_successful_http_requests]\n        #[help = \"Number of HTTP requests performed by procedures which terminate successfully, returning a response.\n\nEach HTTP request performed by a database will be counted either here, in `spacetime_procedure_num_failed_http_requests`,\n`spacetime_procedure_num_timeout_http_requests` or in `spacetime_procedure_num_in_progress_http_requests`.\"]\n        #[labels(db: Identity)]\n        pub procedure_num_successful_http_requests: IntCounterVec,\n\n        #[name = spacetime_procedure_num_failed_http_requests]\n        #[help = \"Number of HTTP requests performed by procedures which fail for reasons other than a timeout.\n\nEach HTTP request performed by a database will be counted either here, in `spacetime_procedure_num_successful_http_requests`,\n`spacetime_procedure_num_timeout_http_requests` or in `spacetime_procedure_num_in_progress_http_requests`.\"]\n        #[labels(db: Identity)]\n        pub procedure_num_failed_http_requests: IntCounterVec,\n\n        #[name = spacetime_procedure_num_timeout_http_requests]\n        #[help = \"Number of HTTP requests performed by procedures which fail due to a timeout.\n\nEach HTTP request performed by a database will be counted either here, in `spacetime_procedure_num_successful_http_requests`,\n`spacetime_procedure_num_failed_http_requests`, or in `spacetime_procedure_num_in_progress_http_requests`.\"]\n        #[labels(db: Identity)]\n        pub procedure_num_timeout_http_requests: IntCounterVec,\n\n        #[name = spacetime_procedure_num_in_progress_http_requests]\n        #[help = \"Number of HTTP requests currently in progress within procedures.\n\nEach HTTP request performed by a database will be counted either here, in `spacetime_procedure_num_successful_http_requests`,\n`spacetime_procedure_num_failed_http_requests`, or in `spacetime_procedure_num_timeout_http_requests`.\"]\n        #[labels(db: Identity)]\n        pub procedure_num_in_progress_http_requests: IntGaugeVec,\n    }\n);\n\npub static DB_METRICS: Lazy<DbMetrics> = Lazy::new(DbMetrics::new);\n\n/// Returns the number of committed rows in the table named by `table_name` and identified by `table_id` in the database `db_identity`.\npub fn table_num_rows(db_identity: Identity, table_id: TableId, table_name: &str) -> u64 {\n    DB_METRICS\n        .rdb_num_table_rows\n        .with_label_values(&db_identity, &table_id.0, table_name)\n        .get() as _\n}\n"
  },
  {
    "path": "crates/datastore/src/error.rs",
    "content": "use super::system_tables::SystemTable;\nuse spacetimedb_lib::db::raw_def::{v9::RawSql, RawIndexDefV8};\nuse spacetimedb_primitives::{ColId, ColList, IndexId, SequenceId, TableId, ViewId};\nuse spacetimedb_sats::buffer::DecodeError;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicValue};\nuse spacetimedb_schema::def::error::LibError;\nuse spacetimedb_snapshot::SnapshotError;\nuse spacetimedb_table::{\n    bflatn_to, read_column,\n    table::{self, ReadViaBsatnError, UniqueConstraintViolation},\n};\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum DatastoreError {\n    #[error(\"LibError: {0}\")]\n    Lib(#[from] LibError),\n    #[error(\"TableError: {0}\")]\n    Table(#[from] TableError),\n    #[error(\"IndexError: {0}\")]\n    Index(#[from] IndexError),\n    #[error(\"SequenceError: {0}\")]\n    Sequence(#[from] SequenceError),\n    #[error(transparent)]\n    // Box the inner [`SnapshotError`] to keep Clippy quiet about large `Err` variants.\n    Snapshot(#[from] Box<SnapshotError>),\n    // TODO(cloutiertyler): should this be a TableError? I couldn't get it to compile\n    #[error(\"Error reading a value from a table through BSATN: {0}\")]\n    ReadViaBsatnError(#[from] ReadViaBsatnError),\n\n    #[error(\"ViewError: {0}\")]\n    View(#[from] ViewError),\n\n    #[error(transparent)]\n    Other(#[from] anyhow::Error),\n}\n\n#[derive(Error, Debug)]\npub enum ViewError {\n    #[error(\"view '{0}' not found\")]\n    NotFound(RawIdentifier),\n    #[error(\"Table backing View '{0}' not found\")]\n    TableNotFound(ViewId),\n    #[error(\"failed to deserialize view arguments from row\")]\n    DeserializeArgs,\n    #[error(\"failed to deserialize view return value: {0}\")]\n    DeserializeReturn(String),\n    #[error(\"failed to serialize row to BSATN\")]\n    SerializeRow,\n    #[error(\"invalid return type: expected Array or Option, got {0:?}\")]\n    InvalidReturnType(AlgebraicType),\n    #[error(\"return type is Array but deserialized value is not Array\")]\n    TypeMismatchArray,\n    #[error(\"return type is Option but deserialized value is not Option\")]\n    TypeMismatchOption,\n    #[error(\"expected ProductValue in view result\")]\n    ExpectedProduct,\n    #[error(\"failed to serialize view arguments\")]\n    SerializeArgs,\n}\n\n#[derive(Error, Debug)]\npub enum TableError {\n    #[error(\"Table with name `{0}` not found.\")]\n    NotFound(String),\n    #[error(\"Table with ID `{1}` not found in `{0}`.\")]\n    IdNotFound(SystemTable, u32),\n    #[error(\"Sql `{1}` not found in `{0}`.\")]\n    RawSqlNotFound(SystemTable, RawSql),\n    #[error(\"Table with ID `{0}` not found in `TxState`.\")]\n    IdNotFoundState(TableId),\n    #[error(\"Column `{0}` not found\")]\n    ColumnNotFound(ColId),\n    #[error(transparent)]\n    Bflatn(#[from] bflatn_to::Error),\n    #[error(transparent)]\n    Duplicate(#[from] table::DuplicateError),\n    #[error(transparent)]\n    ReadColTypeError(#[from] read_column::TypeError),\n    #[error(transparent)]\n    // Error here is `Box`ed to avoid triggering https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err .\n    ChangeColumnsError(#[from] Box<table::ChangeColumnsError>),\n    #[error(transparent)]\n    AddColumnsError(#[from] Box<table::AddColumnsError>),\n}\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum IndexError {\n    #[error(\"Index not found: {0:?}\")]\n    NotFound(IndexId),\n    #[error(\"Column not found: {0:?}\")]\n    ColumnNotFound(RawIndexDefV8),\n    #[error(transparent)]\n    UniqueConstraintViolation(#[from] UniqueConstraintViolation),\n    #[error(\"Attempt to define a index with more than 1 auto_inc column: Table: {0:?}, Columns: {1:?}\")]\n    OneAutoInc(TableId, Vec<String>),\n    #[error(\"Could not decode arguments to index scan\")]\n    Decode(DecodeError),\n    #[error(\"Index was not unique: {0:?}\")]\n    NotUnique(IndexId),\n    #[error(\"Key {1:?} was not found in index {0:?}\")]\n    KeyNotFound(IndexId, AlgebraicValue),\n    #[error(\"IndexId {0:?} does not support seeking for a range\")]\n    IndexCannotSeekRange(IndexId),\n}\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum SequenceError {\n    #[error(\"Sequence with name `{0}` already exists.\")]\n    Exist(String),\n    #[error(\"Sequence `{0}`: The increment is 0, and this means the sequence can't advance.\")]\n    IncrementIsZero(String),\n    #[error(\"Sequence `{0}`: The min_value {1} must < max_value {2}.\")]\n    MinMax(String, i128, i128),\n    #[error(\"Sequence `{0}`: The start value {1} must be >= min_value {2}.\")]\n    MinStart(String, i128, i128),\n    #[error(\"Sequence `{0}`: The start value {1} must be <= min_value {2}.\")]\n    MaxStart(String, i128, i128),\n    #[error(\"Sequence `{0}` failed to decode value from Sled (not a u128).\")]\n    SequenceValue(String),\n    #[error(\"Sequence ID `{0}` not found.\")]\n    NotFound(SequenceId),\n    #[error(\"Sequence applied to a non-integer field. Column `{col}` is of type {{found.to_sats()}}.\")]\n    NotInteger { col: String, found: AlgebraicType },\n    #[error(\"Sequence ID `{0}` still had no values left after allocation.\")]\n    UnableToAllocate(SequenceId),\n    #[error(\"Autoinc constraint on table {0:?} spans more than one column: {1:?}\")]\n    MultiColumnAutoInc(TableId, ColList),\n}\n\nimpl From<InvalidFieldError> for DatastoreError {\n    fn from(value: InvalidFieldError) -> Self {\n        LibError::from(value).into()\n    }\n}\n\nimpl From<spacetimedb_table::read_column::TypeError> for DatastoreError {\n    fn from(err: spacetimedb_table::read_column::TypeError) -> Self {\n        TableError::from(err).into()\n    }\n}\n\nimpl From<table::InsertError> for DatastoreError {\n    fn from(err: table::InsertError) -> Self {\n        match err {\n            table::InsertError::Duplicate(e) => TableError::from(e).into(),\n            table::InsertError::Bflatn(e) => TableError::from(e).into(),\n            table::InsertError::IndexError(e) => IndexError::from(e).into(),\n        }\n    }\n}\n\nimpl From<bflatn_to::Error> for DatastoreError {\n    fn from(err: bflatn_to::Error) -> Self {\n        Self::Table(err.into())\n    }\n}\n\nimpl From<SnapshotError> for DatastoreError {\n    fn from(e: SnapshotError) -> Self {\n        DatastoreError::Snapshot(Box::new(e))\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/execution_context.rs",
    "content": "use std::sync::Arc;\n\nuse bytes::Bytes;\nuse derive_more::Display;\nuse spacetimedb_commitlog::{payload::txdata, Varchar};\nuse spacetimedb_lib::{ConnectionId, Identity, Timestamp};\nuse spacetimedb_sats::{bsatn, raw_identifier::RawIdentifier};\nuse spacetimedb_schema::{identifier::Identifier, reducer_name::ReducerName};\n\n/// Represents the context under which a database runtime method is executed.\n/// In particular it provides details about the currently executing txn to runtime operations.\n/// More generally it acts as a container for information that database operations may require to function correctly.\n#[derive(Clone)]\npub struct ExecutionContext {\n    /// The identity of the database on which a transaction is being executed.\n    pub database_identity: Identity,\n    /// The reducer from which the current transaction originated.\n    pub reducer: Option<ReducerContext>,\n    /// The type of workload that is being executed.\n    pub workload: WorkloadType,\n}\n\n/// If an [`ExecutionContext`] is a reducer context, describes the reducer.\n///\n/// Note that this information is written to persistent storage.\n#[derive(Clone)]\npub struct ReducerContext {\n    /// The name of the reducer.\n    pub name: ReducerName,\n    /// The [`Identity`] of the caller.\n    pub caller_identity: Identity,\n    /// The [`ConnectionId`] of the caller.\n    pub caller_connection_id: ConnectionId,\n    /// The timestamp of the reducer invocation.\n    pub timestamp: Timestamp,\n    /// The BSATN-encoded arguments given to the reducer.\n    ///\n    /// Note that [`Bytes`] is a refcounted value, but the memory it points to\n    /// can be large-ish. The reference should be freed as soon as possible.\n    pub arg_bsatn: Bytes,\n}\n\nimpl From<ReducerContext> for txdata::Inputs {\n    fn from(\n        ReducerContext {\n            name,\n            caller_identity,\n            caller_connection_id,\n            timestamp,\n            arg_bsatn,\n        }: ReducerContext,\n    ) -> Self {\n        let reducer_name = Arc::new(Varchar::from_str_truncate(&name));\n        let cap = arg_bsatn.len()\n        /* caller_identity */\n        + 32\n        /* caller_connection_id */\n        + 16\n        /* timestamp */\n        + 8;\n        let mut buf = Vec::with_capacity(cap);\n        bsatn::to_writer(&mut buf, &caller_identity).unwrap();\n        bsatn::to_writer(&mut buf, &caller_connection_id).unwrap();\n        bsatn::to_writer(&mut buf, &timestamp).unwrap();\n        buf.extend_from_slice(&arg_bsatn);\n\n        txdata::Inputs {\n            reducer_name,\n            reducer_args: buf.into(),\n        }\n    }\n}\n\nimpl TryFrom<&txdata::Inputs> for ReducerContext {\n    type Error = bsatn::DecodeError;\n\n    fn try_from(inputs: &txdata::Inputs) -> Result<Self, Self::Error> {\n        let args = &mut inputs.reducer_args.as_ref();\n        let caller_identity = bsatn::from_reader(args)?;\n        let caller_connection_id = bsatn::from_reader(args)?;\n        let timestamp = bsatn::from_reader(args)?;\n\n        let name = RawIdentifier::new(&**inputs.reducer_name);\n        let name = ReducerName::new(Identifier::new_assume_valid(name));\n\n        Ok(Self {\n            name,\n            caller_identity,\n            caller_connection_id,\n            timestamp,\n            arg_bsatn: Bytes::from(args.to_owned()),\n        })\n    }\n}\n\n/// Represents the type of workload that is being executed.\n///\n/// Used as constructor helper for [ExecutionContext].\n#[derive(Clone)]\npub enum Workload {\n    #[cfg(any(test, feature = \"test\"))]\n    ForTests,\n    Reducer(ReducerContext),\n    Sql,\n    Subscribe,\n    Unsubscribe,\n    Update,\n    Internal,\n}\n\nimpl Workload {\n    /// Returns a reducer workload with no arguments to the reducer\n    /// and the current timestamp.\n    pub fn reducer_no_args(name: ReducerName, id: Identity, conn_id: ConnectionId) -> Self {\n        Self::Reducer(ReducerContext {\n            name,\n            caller_identity: id,\n            caller_connection_id: conn_id,\n            timestamp: Timestamp::now(),\n            arg_bsatn: Bytes::new(),\n        })\n    }\n\n    /// Returns the workload's type/kind, without any of the data.\n    pub fn workload_type(&self) -> WorkloadType {\n        match self {\n            #[cfg(any(test, feature = \"test\"))]\n            Self::ForTests => WorkloadType::Internal,\n            Self::Reducer(_) => WorkloadType::Reducer,\n            Self::Sql => WorkloadType::Sql,\n            Self::Subscribe => WorkloadType::Subscribe,\n            Self::Unsubscribe => WorkloadType::Unsubscribe,\n            Self::Update => WorkloadType::Update,\n            Self::Internal => WorkloadType::Internal,\n        }\n    }\n}\n\n/// Classifies a transaction according to its workload.\n/// A transaction can be executing a reducer.\n/// It can be used to satisfy a one-off sql query or subscription.\n/// It can also be an internal operation that is not associated with a reducer or sql request.\n#[derive(Clone, Copy, Display, Hash, PartialEq, Eq, Default, strum::AsRefStr, enum_map::Enum)]\npub enum WorkloadType {\n    Reducer,\n    Sql,\n    Subscribe,\n    Unsubscribe,\n    Update,\n    #[default]\n    Internal,\n    View,\n    Procedure,\n}\n\nimpl ExecutionContext {\n    /// Returns an [ExecutionContext] with the provided parameters and empty metrics.\n    fn new(database_identity: Identity, reducer: Option<ReducerContext>, workload: WorkloadType) -> Self {\n        Self {\n            database_identity,\n            reducer,\n            workload,\n        }\n    }\n\n    /// Returns an [ExecutionContext] with the provided [Workload] and empty metrics.\n    pub fn with_workload(database: Identity, workload: Workload) -> Self {\n        match workload {\n            #[cfg(any(test, feature = \"test\"))]\n            Workload::ForTests => Self::new(database, None, WorkloadType::Internal),\n            Workload::Internal => Self::new(database, None, WorkloadType::Internal),\n            Workload::Reducer(ctx) => Self::new(database, Some(ctx), WorkloadType::Reducer),\n            Workload::Sql => Self::new(database, None, WorkloadType::Sql),\n            Workload::Subscribe => Self::new(database, None, WorkloadType::Subscribe),\n            Workload::Unsubscribe => Self::new(database, None, WorkloadType::Unsubscribe),\n            Workload::Update => Self::new(database, None, WorkloadType::Update),\n        }\n    }\n\n    /// Returns the identity of the database on which we are operating.\n    #[inline]\n    pub fn database_identity(&self) -> Identity {\n        self.database_identity\n    }\n\n    /// If this is a reducer context, returns the name of the reducer.\n    #[inline]\n    pub fn into_reducer_name(self) -> Option<ReducerName> {\n        self.reducer.map(|ctx| ctx.name)\n    }\n\n    /// If this is a reducer context, returns the full reducer metadata.\n    #[inline]\n    pub fn reducer_context(&self) -> Option<&ReducerContext> {\n        self.reducer.as_ref()\n    }\n\n    /// Returns the type of workload that is being executed.\n    #[inline]\n    pub fn workload(&self) -> WorkloadType {\n        self.workload\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/lib.rs",
    "content": "pub mod db_metrics;\npub mod error;\npub mod execution_context;\npub mod locking_tx_datastore;\npub mod system_tables;\npub mod traits;\n\nuse error::DatastoreError;\n\npub type Result<T> = core::result::Result<T, DatastoreError>;\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/committed_state.rs",
    "content": "use super::{\n    datastore::Result,\n    delete_table::DeleteTable,\n    sequence::{Sequence, SequencesState},\n    state_view::StateView,\n    tx_state::{IndexIdMap, PendingSchemaChange, TxState},\n    IterByColEqTx,\n};\nuse crate::{\n    db_metrics::DB_METRICS,\n    error::{DatastoreError, IndexError, TableError, ViewError},\n    execution_context::ExecutionContext,\n    locking_tx_datastore::{\n        mut_tx::ViewReadSets,\n        state_view::{iter_st_column_for_table, ApplyFilter, EqOnColumn, RangeOnColumn, ScanOrIndex},\n        IterByColRangeTx,\n    },\n    system_tables::{\n        is_built_in_meta_row, system_tables, table_id_is_reserved, StColumnRow, StConstraintData, StConstraintRow,\n        StFields, StIndexRow, StSequenceFields, StSequenceRow, StTableFields, StTableRow, StViewRow, SystemTable,\n        ST_CLIENT_ID, ST_CLIENT_IDX, ST_COLUMN_ID, ST_COLUMN_IDX, ST_COLUMN_NAME, ST_CONSTRAINT_ID, ST_CONSTRAINT_IDX,\n        ST_CONSTRAINT_NAME, ST_INDEX_ID, ST_INDEX_IDX, ST_INDEX_NAME, ST_MODULE_ID, ST_MODULE_IDX,\n        ST_ROW_LEVEL_SECURITY_ID, ST_ROW_LEVEL_SECURITY_IDX, ST_SCHEDULED_ID, ST_SCHEDULED_IDX, ST_SEQUENCE_ID,\n        ST_SEQUENCE_IDX, ST_SEQUENCE_NAME, ST_TABLE_ID, ST_TABLE_IDX, ST_VAR_ID, ST_VAR_IDX, ST_VIEW_ARG_ID,\n        ST_VIEW_ARG_IDX,\n    },\n    traits::{EphemeralTables, TxData},\n};\nuse crate::{\n    locking_tx_datastore::ViewCallInfo,\n    system_tables::{\n        ST_COLUMN_ACCESSOR_ID, ST_COLUMN_ACCESSOR_IDX, ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_IDX,\n        ST_EVENT_TABLE_ID, ST_EVENT_TABLE_IDX, ST_INDEX_ACCESSOR_ID, ST_INDEX_ACCESSOR_IDX, ST_TABLE_ACCESSOR_ID,\n        ST_TABLE_ACCESSOR_IDX, ST_VIEW_COLUMN_ID, ST_VIEW_COLUMN_IDX, ST_VIEW_ID, ST_VIEW_IDX, ST_VIEW_PARAM_ID,\n        ST_VIEW_PARAM_IDX, ST_VIEW_SUB_ID, ST_VIEW_SUB_IDX,\n    },\n};\nuse anyhow::anyhow;\nuse core::{convert::Infallible, ops::RangeBounds};\nuse spacetimedb_data_structures::map::{HashMap, HashSet, IntMap, IntSet};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_lib::{db::auth::StTableType, Identity};\nuse spacetimedb_primitives::{ColId, ColList, ColSet, IndexId, SequenceId, TableId, ViewId};\nuse spacetimedb_sats::{algebraic_value::de::ValueDeserializer, memory_usage::MemoryUsage, Deserialize};\nuse spacetimedb_sats::{AlgebraicValue, ProductValue};\nuse spacetimedb_schema::{\n    def::IndexAlgorithm,\n    schema::{ColumnSchema, TableSchema},\n};\nuse spacetimedb_table::{\n    blob_store::{BlobStore, HashMapBlobStore},\n    indexes::{RowPointer, SquashedOffset},\n    page_pool::PagePool,\n    table::{IndexScanPointIter, IndexScanRangeIter, InsertError, RowRef, Table, TableAndIndex, TableScanIter},\n    table_index::IndexSeekRangeResult,\n};\nuse std::collections::BTreeMap;\nuse std::sync::Arc;\nuse thin_vec::ThinVec;\n\n/// Contains the live, in-memory snapshot of a database. This structure\n/// is exposed in order to support tools wanting to process the commit\n/// logs directly. For normal usage, see the RelationalDB struct instead.\n///\n/// NOTE: unstable API, this may change at any point in the future.\n///\n/// Fields whose names are prefixed with `replay_` are used only while replaying a commitlog,\n/// and are unused during live transaction processing.\n/// TODO(centril): Only used during bootstrap and is otherwise unused.\n/// We should split `CommittedState` into two types\n/// where one, e.g., `ReplayCommittedState`, has this field.\npub struct CommittedState {\n    pub(crate) next_tx_offset: u64,\n    pub(crate) tables: IntMap<TableId, Table>,\n    pub(crate) blob_store: HashMapBlobStore,\n    /// Provides fast lookup for index id -> an index.\n    pub(super) index_id_map: IndexIdMap,\n    /// The page pool used to retrieve new/unused pages for tables.\n    ///\n    /// Between transactions, this is untouched.\n    /// During transactions, the [`MutTxId`] can steal pages from the committed state.\n    ///\n    /// This is a handle on a shared structure.\n    /// Pages are shared between all modules running on a particular host,\n    /// not allocated per-module.\n    pub(super) page_pool: PagePool,\n    /// We track the read sets for each view in the committed state.\n    /// We check each reducer's write set against these read sets.\n    /// Any overlap will trigger a re-evaluation of the affected view,\n    /// and its read set will be updated accordingly.\n    read_sets: ViewReadSets,\n\n    /// Tables which do not need to be made persistent.\n    /// These include:\n    ///     - system tables: `st_view_sub`, `st_view_arg`\n    ///     - Tables which back views.\n    pub(super) ephemeral_tables: EphemeralTables,\n\n    /// Whether the table was dropped within the current transaction during replay.\n    ///\n    /// While processing a transaction which drops a table, we'll first see the `st_table` delete,\n    /// then a series of deletes from the table itself.\n    /// We track the table's ID here so we know to ignore the deletes.\n    ///\n    /// Cleared after the end of processing each transaction,\n    /// as it should be impossible to ever see another reference to the table after that point.\n    replay_table_dropped: IntSet<TableId>,\n\n    /// Rows within `st_column` which should be ignored during replay\n    /// due to having been superseded by a new row representing the same column.\n    ///\n    /// During replay, we visit all of the inserts table-by-table, followed by all of the deletes table-by-table.\n    /// This means that, when multiple columns of a table change type within the same transaction,\n    /// we see all of the newly-inserted `st_column` rows first, and then later, all of the deleted rows.\n    /// We may even see inserts into the altered table before seeing the `st_column` deletes!\n    ///\n    /// In order to maintain a proper view of the schema of tables during replay,\n    /// we must remember the old versions of the `st_column` rows when we insert the new ones,\n    /// so that we can respect only the new versions.\n    ///\n    /// We insert into this set during [`Self::replay_insert`] of `st_column` rows\n    /// and delete from it during [`Self::replay_delete`] of `st_column` rows.\n    /// We assert this is empty at the end of each transaction.\n    replay_columns_to_ignore: HashSet<RowPointer>,\n\n    /// Set of tables whose `st_table` entries have been updated during the currently-replaying transaction,\n    /// mapped to the current most-recent `st_table` row.\n    ///\n    /// When processing an insert to `st_table`, if the table already exists, we'll record it here.\n    /// Then, when we see a corresponding delete, we know that the table has not been dropped,\n    /// and so we won't delete the in-memory structure or insert its ID into [`Self::replay_table_dropped`].\n    ///\n    /// When looking up the `st_table` row for a table, if it has an entry here,\n    /// that means there are two rows resident in `st_table` at this point in replay.\n    /// We return the row recorded here rather than inspecting `st_table`.\n    ///\n    /// We remove from this set when we reach the matching delete,\n    /// and assert this set is empty at the end of each transaction.\n    ///\n    /// [`RowPointer`]s from this set are passed to the `unsafe` [`Table::get_row_ref_unchecked`],\n    /// so it's important to properly maintain only [`RowPointer`]s to valid, extant, non-deleted rows.\n    replay_table_updated: IntMap<TableId, RowPointer>,\n}\n\nimpl CommittedState {\n    /// Returns whether there are no views.\n    pub(super) fn has_no_views_for_table_scans(&self) -> bool {\n        self.read_sets.is_empty()\n    }\n\n    /// Returns the views that perform a full scan of this table\n    pub(super) fn views_for_table_scan(&self, table_id: &TableId) -> impl Iterator<Item = &ViewCallInfo> + use<'_> {\n        self.read_sets.views_for_table_scan(table_id)\n    }\n\n    /// Returns the views that perform an precise index seek on given `row_ref` of `table_id`\n    pub fn views_for_index_seek<'a>(\n        &'a self,\n        table_id: &TableId,\n        row_ref: RowRef<'a>,\n    ) -> impl Iterator<Item = &'a ViewCallInfo> + use<'a> {\n        self.read_sets.views_for_index_seek(table_id, row_ref)\n    }\n}\n\nimpl MemoryUsage for CommittedState {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            next_tx_offset,\n            tables,\n            blob_store,\n            index_id_map,\n            page_pool: _,\n            read_sets,\n            ephemeral_tables,\n            replay_table_dropped,\n            replay_columns_to_ignore,\n            replay_table_updated,\n        } = self;\n        // NOTE(centril): We do not want to include the heap usage of `page_pool` as it's a shared resource.\n        next_tx_offset.heap_usage()\n            + tables.heap_usage()\n            + blob_store.heap_usage()\n            + index_id_map.heap_usage()\n            + read_sets.heap_usage()\n            + ephemeral_tables.heap_usage()\n            + replay_columns_to_ignore.heap_usage()\n            + replay_table_dropped.heap_usage()\n            + replay_table_updated.heap_usage()\n    }\n}\n\nimpl StateView for CommittedState {\n    type Iter<'a> = TableScanIter<'a>;\n    type IterByColRange<'a, R: RangeBounds<AlgebraicValue>> = IterByColRangeTx<'a, R>;\n    type IterByColEq<'a, 'r>\n        = IterByColEqTx<'a, 'r>\n    where\n        Self: 'a;\n\n    fn get_schema(&self, table_id: TableId) -> Option<&Arc<TableSchema>> {\n        self.tables.get(&table_id).map(|table| table.get_schema())\n    }\n\n    fn table_row_count(&self, table_id: TableId) -> Option<u64> {\n        self.get_table(table_id).map(|table| table.row_count)\n    }\n\n    fn iter(&self, table_id: TableId) -> Result<Self::Iter<'_>> {\n        self.table_scan(table_id)\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_table, table_id.0).into())\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the values of `cols` are contained in `range`.\n    fn iter_by_col_range<R: RangeBounds<AlgebraicValue>>(\n        &self,\n        table_id: TableId,\n        cols: ColList,\n        range: R,\n    ) -> Result<Self::IterByColRange<'_, R>> {\n        match self.index_seek_range(table_id, &cols, &range) {\n            Some(Ok(iter)) => Ok(ScanOrIndex::Index(iter)),\n            None | Some(Err(_)) => Ok(ScanOrIndex::Scan(ApplyFilter::new(\n                RangeOnColumn { cols, range },\n                self.iter(table_id)?,\n            ))),\n        }\n    }\n\n    fn iter_by_col_eq<'a, 'r>(\n        &'a self,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        val: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEq<'a, 'r>> {\n        let cols = cols.into();\n        match self.index_seek_point(table_id, &cols, val) {\n            Some(iter) => Ok(ScanOrIndex::Index(iter)),\n            None => Ok(ScanOrIndex::Scan(ApplyFilter::new(\n                EqOnColumn { cols, val },\n                self.iter(table_id)?,\n            ))),\n        }\n    }\n\n    /// Find the `st_table` row for `table_id`, first inspecting [`Self::replay_table_updated`],\n    /// then falling back to [`Self::iter_by_col_eq`] of `st_table`.\n    fn find_st_table_row(&self, table_id: TableId) -> Result<StTableRow> {\n        let row_ref = if let Some(row_ptr) = self.replay_table_updated.get(&table_id) {\n            let (table, blob_store, _) = self.get_table_and_blob_store(table_id)?;\n            // Safety: `row_ptr` is stored in `self.replay_table_updated`,\n            // meaning it was inserted into `st_table` by `replay_insert`\n            // and has not yet been deleted by `replay_delete_by_rel`.\n            unsafe { table.get_row_ref_unchecked(blob_store, *row_ptr) }\n        } else {\n            self.iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &table_id.into())?\n                .next()\n                .ok_or_else(|| TableError::IdNotFound(SystemTable::st_table, table_id.into()))?\n        };\n\n        StTableRow::try_from(row_ref)\n    }\n}\n\nimpl CommittedState {\n    pub(super) fn new(page_pool: PagePool) -> Self {\n        Self {\n            next_tx_offset: <_>::default(),\n            tables: <_>::default(),\n            blob_store: <_>::default(),\n            index_id_map: <_>::default(),\n            read_sets: <_>::default(),\n            page_pool,\n            ephemeral_tables: <_>::default(),\n            replay_table_dropped: <_>::default(),\n            replay_columns_to_ignore: <_>::default(),\n            replay_table_updated: <_>::default(),\n        }\n    }\n\n    /// Delete all but the highest-allocation `st_sequence` row for each system sequence.\n    ///\n    /// Prior versions of `RelationalDb::migrate_system_tables` (defined in the `core` crate)\n    /// initialized newly-created system sequences to `allocation: 4097`,\n    /// while `committed_state::bootstrap_system_tables` sets `allocation: 4096`.\n    /// This affected the system table migration which added\n    /// `st_view_view_id_seq` and `st_view_arg_id_seq`.\n    /// As a result, when replaying these databases' commitlogs without a snapshot,\n    /// we will end up with two rows in `st_sequence` for each of these sequences,\n    /// resulting in a unique constraint violation in `CommittedState::build_indexes`.\n    /// We call this method in [`super::datastore::Locking::rebuild_state_after_replay`]\n    /// to avoid that unique constraint violation.\n    pub(super) fn fixup_delete_duplicate_system_sequence_rows(&mut self) {\n        struct StSequenceRowInfo {\n            sequence_id: SequenceId,\n            allocated: i128,\n            row_pointer: RowPointer,\n        }\n\n        // Get all the `st_sequence` rows which refer to sequences on system tables,\n        // including any duplicates caused by the bug described above.\n        let sequence_rows = self\n            .table_scan(ST_SEQUENCE_ID)\n            .expect(\"`st_sequence` should exist\")\n            .filter_map(|row_ref| {\n                // Read the table ID to which the sequence refers,\n                // in order to determine if this is a system sequence or not.\n                let table_id = row_ref\n                    .read_col::<TableId>(StSequenceFields::TableId)\n                    .expect(\"`st_sequence` row should conform to `st_sequence` schema\");\n\n                // If this sequence refers to a system table, it may need a fixup.\n                // User tables' sequences will never need fixups.\n                table_id_is_reserved(table_id).then(|| {\n                    let allocated = row_ref\n                        .read_col::<i128>(StSequenceFields::Allocated)\n                        .expect(\"`st_sequence` row should conform to `st_sequence` schema\");\n                    let sequence_id = row_ref\n                        .read_col::<SequenceId>(StSequenceFields::SequenceId)\n                        .expect(\"`st_sequence` row should conform to `st_sequence` schema\");\n                    StSequenceRowInfo {\n                        allocated,\n                        sequence_id,\n                        row_pointer: row_ref.pointer(),\n                    }\n                })\n            })\n            .collect::<Vec<_>>();\n\n        let (st_sequence, blob_store, ..) = self\n            .get_table_and_blob_store_mut(ST_SEQUENCE_ID)\n            .expect(\"`st_sequence` should exist\");\n\n        // Track the row with the highest allocation for each sequence.\n        let mut highest_allocations: HashMap<SequenceId, (i128, RowPointer)> = HashMap::default();\n\n        for StSequenceRowInfo {\n            sequence_id,\n            allocated,\n            row_pointer,\n        } in sequence_rows\n        {\n            // For each `st_sequence` row which refers to a system table,\n            // if we've already seen a row for the same sequence,\n            // keep only the row with the higher allocation.\n            if let Some((prev_allocated, prev_row_pointer)) =\n                highest_allocations.insert(sequence_id, (allocated, row_pointer))\n            {\n                // We have a duplicate row. We want to keep whichever has the higher `allocated`,\n                // and delete the other.\n                let row_pointer_to_delete = if prev_allocated > allocated {\n                    // The previous row has a higher allocation than the new row,\n                    // so delete the new row and restore `previous` to `highest_allocations`.\n                    highest_allocations.insert(sequence_id, (prev_allocated, prev_row_pointer));\n                    row_pointer\n                } else {\n                    // The previous row does not have a higher allocation than the new,\n                    // so delete the previous row and keep the new one.\n                    prev_row_pointer\n                };\n\n                st_sequence.delete(blob_store, row_pointer_to_delete, |_| ())\n                    .expect(\"Duplicated `st_sequence` row at `row_pointer_to_delete` should be present in `st_sequence` during fixup\");\n            }\n        }\n    }\n\n    /// Extremely delicate function to bootstrap the system tables.\n    /// Don't update this unless you know what you're doing.\n    pub(super) fn bootstrap_system_tables(&mut self, database_identity: Identity) -> Result<()> {\n        // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer,\n        // and therefore has performance implications and must not be disabled.\n        let with_label_values = |table_id: TableId, table_name: &str| {\n            DB_METRICS\n                .rdb_num_table_rows\n                .with_label_values(&database_identity, &table_id.0, table_name)\n        };\n\n        let schemas = system_tables().map(Arc::new);\n        let ref_schemas = schemas.each_ref().map(|s| &**s);\n\n        // Insert the table row into st_tables, creating st_tables if it's missing.\n        let (st_tables, blob_store, pool) =\n            self.get_table_and_blob_store_or_create(ST_TABLE_ID, &schemas[ST_TABLE_IDX]);\n        // Insert the table row into `st_tables` for all system tables\n        for schema in ref_schemas {\n            let table_id = schema.table_id;\n            // Metric for this system table.\n            with_label_values(table_id, &schema.table_name).set(0);\n\n            let row = StTableRow {\n                table_id,\n                table_name: schema.table_name.clone(),\n                table_type: StTableType::System,\n                table_access: schema.table_access,\n                table_primary_key: schema.primary_key.map(Into::into),\n            };\n            let row = ProductValue::from(row);\n            // Insert the meta-row into the in-memory ST_TABLES.\n            st_tables.insert(pool, blob_store, &row)?;\n        }\n\n        // Insert the columns into `st_columns`\n        let (st_columns, blob_store, pool) =\n            self.get_table_and_blob_store_or_create(ST_COLUMN_ID, &schemas[ST_COLUMN_IDX]);\n        for col in ref_schemas.iter().flat_map(|x| x.columns()).cloned() {\n            let row = StColumnRow {\n                table_id: col.table_id,\n                col_pos: col.col_pos,\n                col_name: col.col_name,\n                col_type: col.col_type.into(),\n            };\n            let row = ProductValue::from(row);\n            // Insert the meta-row into the in-memory ST_COLUMNS.\n            st_columns.insert(pool, blob_store, &row)?;\n            // Increment row count for st_columns.\n            with_label_values(ST_COLUMN_ID, ST_COLUMN_NAME).inc();\n        }\n\n        // Insert the FK sorted by table/column so it show together when queried.\n\n        // Insert constraints into `st_constraints`\n        let (st_constraints, blob_store, pool) =\n            self.get_table_and_blob_store_or_create(ST_CONSTRAINT_ID, &schemas[ST_CONSTRAINT_IDX]);\n        for constraint in ref_schemas.iter().flat_map(|x| &x.constraints) {\n            let row = StConstraintRow {\n                constraint_id: constraint.constraint_id,\n                constraint_name: constraint.constraint_name.clone(),\n                table_id: constraint.table_id,\n                constraint_data: constraint.data.clone().into(),\n            };\n            let row = ProductValue::from(row);\n            // Insert the meta-row into the in-memory ST_CONSTRAINTS.\n            st_constraints.insert(pool, blob_store, &row)?;\n            // Increment row count for st_constraints.\n            with_label_values(ST_CONSTRAINT_ID, ST_CONSTRAINT_NAME).inc();\n        }\n\n        // Insert the indexes into `st_indexes`\n        let (st_indexes, blob_store, pool) =\n            self.get_table_and_blob_store_or_create(ST_INDEX_ID, &schemas[ST_INDEX_IDX]);\n\n        for index in ref_schemas.iter().flat_map(|x| &x.indexes) {\n            let row: StIndexRow = index.clone().into();\n            let row = ProductValue::from(row);\n            // Insert the meta-row into the in-memory ST_INDEXES.\n            st_indexes.insert(pool, blob_store, &row)?;\n            // Increment row count for st_indexes.\n            with_label_values(ST_INDEX_ID, ST_INDEX_NAME).inc();\n        }\n\n        // We don't add the row here but with `MutProgrammable::set_program_hash`, but we need to register the table\n        // in the internal state.\n        self.create_table(ST_MODULE_ID, schemas[ST_MODULE_IDX].clone());\n\n        self.create_table(ST_CLIENT_ID, schemas[ST_CLIENT_IDX].clone());\n\n        self.create_table(ST_VAR_ID, schemas[ST_VAR_IDX].clone());\n\n        self.create_table(ST_SCHEDULED_ID, schemas[ST_SCHEDULED_IDX].clone());\n\n        self.create_table(ST_ROW_LEVEL_SECURITY_ID, schemas[ST_ROW_LEVEL_SECURITY_IDX].clone());\n        self.create_table(\n            ST_CONNECTION_CREDENTIALS_ID,\n            schemas[ST_CONNECTION_CREDENTIALS_IDX].clone(),\n        );\n\n        self.create_table(ST_VIEW_ID, schemas[ST_VIEW_IDX].clone());\n        self.create_table(ST_VIEW_PARAM_ID, schemas[ST_VIEW_PARAM_IDX].clone());\n        self.create_table(ST_VIEW_COLUMN_ID, schemas[ST_VIEW_COLUMN_IDX].clone());\n        self.create_table(ST_VIEW_SUB_ID, schemas[ST_VIEW_SUB_IDX].clone());\n        self.create_table(ST_VIEW_ARG_ID, schemas[ST_VIEW_ARG_IDX].clone());\n\n        self.create_table(ST_EVENT_TABLE_ID, schemas[ST_EVENT_TABLE_IDX].clone());\n        self.create_table(ST_TABLE_ACCESSOR_ID, schemas[ST_TABLE_ACCESSOR_IDX].clone());\n        self.create_table(ST_INDEX_ACCESSOR_ID, schemas[ST_INDEX_ACCESSOR_IDX].clone());\n        self.create_table(ST_COLUMN_ACCESSOR_ID, schemas[ST_COLUMN_ACCESSOR_IDX].clone());\n\n        // Insert the sequences into `st_sequences`\n        let (st_sequences, blob_store, pool) =\n            self.get_table_and_blob_store_or_create(ST_SEQUENCE_ID, &schemas[ST_SEQUENCE_IDX]);\n        for seq in ref_schemas.iter().flat_map(|x| &x.sequences) {\n            let row = StSequenceRow {\n                sequence_id: seq.sequence_id,\n                sequence_name: seq.sequence_name.clone(),\n                table_id: seq.table_id,\n                col_pos: seq.col_pos,\n                increment: seq.increment,\n                min_value: seq.min_value,\n                max_value: seq.max_value,\n                start: seq.start,\n                // In practice, this means we will actually start at start - 1, since `allocated`\n                // overrides start, but we keep these fields set this way to match databases\n                // that were bootstrapped before 1.4 and don't have a snapshot.\n                // This is covered with the test:\n                //   db::relational_db::tests::load_1_2_quickstart_without_snapshot_test\n                allocated: seq.start - 1,\n            };\n            let row = ProductValue::from(row);\n            // Insert the meta-row into the in-memory ST_SEQUENCES.\n            st_sequences.insert(pool, blob_store, &row)?;\n            // Increment row count for st_sequences\n            with_label_values(ST_SEQUENCE_ID, ST_SEQUENCE_NAME).inc();\n        }\n\n        // This is purely a sanity check to ensure that we are setting the ids correctly.\n        self.assert_system_table_schemas_match()?;\n        Ok(())\n    }\n\n    pub(super) fn assert_system_table_schemas_match(&self) -> Result<()> {\n        for schema in system_tables() {\n            let table_id = schema.table_id;\n            let in_memory = self\n                .tables\n                .get(&table_id)\n                .ok_or(TableError::IdNotFoundState(table_id))?\n                .schema\n                .clone();\n            let mut in_st_tables = self.schema_for_table_raw(table_id)?;\n            // We normalize so that the orders will match when checking for equality.\n            in_st_tables.normalize();\n            let in_memory = {\n                let mut s = in_memory.as_ref().clone();\n                s.normalize();\n                s\n            };\n\n            if in_memory != in_st_tables {\n                return Err(anyhow!(\n                    \"System table schema mismatch for table id {table_id}. Expected: {schema:?}, found: {in_memory:?}\"\n                )\n                .into());\n            }\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn replay_truncate(&mut self, table_id: TableId) -> Result<()> {\n        // (1) Table dropped? Avoid an error and just ignore the row instead.\n        if self.replay_table_dropped.contains(&table_id) {\n            return Ok(());\n        }\n\n        // Get the table for mutation.\n        let (table, blob_store, ..) = self.get_table_and_blob_store_mut(table_id)?;\n\n        // We do not need to consider a truncation of `st_table` itself,\n        // as if that happens, the database is bricked.\n\n        table.clear(blob_store);\n\n        Ok(())\n    }\n\n    pub(super) fn replay_delete_by_rel(&mut self, table_id: TableId, row: &ProductValue) -> Result<()> {\n        // (1) Table dropped? Avoid an error and just ignore the row instead.\n        if self.replay_table_dropped.contains(&table_id) {\n            return Ok(());\n        }\n\n        // Get the table for mutation.\n        let (table, blob_store, _, page_pool) = self.get_table_and_blob_store_mut(table_id)?;\n\n        // Delete the row.\n        let row_ptr = table\n            .delete_equal_row(page_pool, blob_store, row)\n            .map_err(TableError::Bflatn)?\n            .ok_or_else(|| anyhow!(\"Delete for non-existent row when replaying transaction\"))?;\n\n        if table_id == ST_TABLE_ID {\n            let referenced_table_id = row\n                .elements\n                .get(StTableFields::TableId.col_idx())\n                .expect(\"`st_table` row should conform to `st_table` schema\")\n                .as_u32()\n                .expect(\"`st_table` row should conform to `st_table` schema\");\n            if self\n                .replay_table_updated\n                .remove(&TableId::from(*referenced_table_id))\n                .is_some()\n            {\n                // This delete is part of an update to an `st_table` row,\n                // i.e. earlier in this transaction we inserted a new version of the row.\n                // That means it's not a dropped table.\n            } else {\n                // A row was removed from `st_table`, so a table was dropped.\n                // Remove that table from the in-memory structures.\n                let dropped_table_id = Self::read_table_id(row);\n                // It's safe to ignore the case where we don't have an in-memory structure for the deleted table.\n                // This can happen if a table is initially empty at the snapshot or its creation,\n                // and never has any rows inserted into or deleted from it.\n                self.tables.remove(&dropped_table_id);\n\n                // Mark the table as dropped so that when\n                // processing row deletions for that table later,\n                // they are simply ignored in (1).\n                self.replay_table_dropped.insert(dropped_table_id);\n            }\n        }\n\n        if table_id == ST_COLUMN_ID {\n            // We may have reached the corresponding delete to an insert in `st_column`\n            // as the result of a column-type-altering migration.\n            // Now that the outdated `st_column` row isn't present any more,\n            // we can stop ignoring it.\n            //\n            // It's also possible that we're deleting this column as the result of a deleted table,\n            // and that there wasn't any corresponding insert at all.\n            // If that's the case, `row_ptr` won't be in `self.replay_columns_to_ignore`,\n            // which is fine.\n            self.replay_columns_to_ignore.remove(&row_ptr);\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn replay_insert(\n        &mut self,\n        table_id: TableId,\n        schema: &Arc<TableSchema>,\n        row: &ProductValue,\n    ) -> Result<()> {\n        // Event table rows in the commitlog are preserved for future replay features\n        // but don't rebuild state — event tables have no committed state.\n        if schema.is_event {\n            return Ok(());\n        }\n\n        let (table, blob_store, pool) = self.get_table_and_blob_store_or_create(table_id, schema);\n\n        let (_, row_ref) = match table.insert(pool, blob_store, row) {\n            Ok(stuff) => stuff,\n            Err(InsertError::Duplicate(e)) => {\n                if is_built_in_meta_row(table_id, row)? {\n                    // If this is a meta-descriptor for a system object,\n                    // and it already exists exactly, then we can safely ignore the insert.\n                    // Any error other than `Duplicate` means the commitlog\n                    // has system table schemas which do not match our expectations,\n                    // which is almost certainly an unrecoverable error.\n                    return Ok(());\n                } else {\n                    return Err(TableError::Duplicate(e).into());\n                }\n            }\n            Err(InsertError::Bflatn(e)) => return Err(TableError::Bflatn(e).into()),\n            Err(InsertError::IndexError(e)) => return Err(IndexError::UniqueConstraintViolation(e).into()),\n        };\n\n        // `row_ref` is treated as having a mutable borrow on `self`\n        // because it derives from `self.get_table_and_blob_store_or_create`,\n        // so we have to downgrade it to a pointer and then re-upgrade it again as an immutable row pointer later.\n        let row_ptr = row_ref.pointer();\n\n        if table_id == ST_TABLE_ID {\n            // For `st_table` inserts, we need to check if this is a new table or an update to an existing table.\n            // For new tables there's nothing more to do, as we'll automatically create it later on\n            // when we first `get_table_and_blob_store_or_create` on that table,\n            // but for updates to existing tables we need additional bookkeeping.\n\n            // Upgrade `row_ptr` back again, to break the mutable borrow.\n            let (table, blob_store, _) = self.get_table_and_blob_store(ST_TABLE_ID)?;\n\n            // Safety: We got `row_ptr` from a valid `RowRef` just above, and haven't done any mutations since,\n            // so it must still be valid.\n            let row_ref = unsafe { table.get_row_ref_unchecked(blob_store, row_ptr) };\n\n            if self.replay_does_table_already_exist(row_ref) {\n                // We've inserted a new `st_table` row for an existing table.\n                // We'll expect to see the previous row deleted later in this transaction.\n                // For now, mark the table as updated so that we don't confuse it for a deleted table in `replay_delete_by_rel`.\n\n                let st_table_row = StTableRow::try_from(row_ref)?;\n                let referenced_table_id = st_table_row.table_id;\n                self.replay_table_updated.insert(referenced_table_id, row_ptr);\n                self.reschema_table_for_st_table_update(st_table_row)?;\n            }\n        }\n\n        if table_id == ST_COLUMN_ID {\n            // We've made a modification to `st_column`.\n            // The type of a table has changed, so figure out which.\n            // The first column in `StColumnRow` is `table_id`.\n            let referenced_table_id = self.ignore_previous_versions_of_column(row, row_ptr)?;\n            self.st_column_changed(referenced_table_id)?;\n        }\n\n        Ok(())\n    }\n\n    /// Does another row other than `new_st_table_entry` exist in `st_table`\n    /// which refers to the same [`TableId`] as `new_st_table_entry`?\n    ///\n    /// Used during [`Self::replay_insert`] of `st_table` rows to maintain [`Self::replay_table_updated`].\n    fn replay_does_table_already_exist(&self, new_st_table_entry: RowRef<'_>) -> bool {\n        fn get_table_id(row_ref: RowRef<'_>) -> TableId {\n            row_ref\n                .read_col(StTableFields::TableId)\n                .expect(\"`st_table` row should conform to `st_table` schema\")\n        }\n\n        let referenced_table_id = get_table_id(new_st_table_entry);\n        self.iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &referenced_table_id.into())\n            .expect(\"`st_table` should exist\")\n            .any(|row_ref| row_ref.pointer() != new_st_table_entry.pointer())\n    }\n\n    /// Update the in-memory table structure for the table described by `row`,\n    /// in response to replay of a schema-altering migration.\n    fn reschema_table_for_st_table_update(&mut self, row: StTableRow) -> Result<()> {\n        // We only need to update if we've already constructed the in-memory table structure.\n        // If we haven't yet, then `self.get_table_and_blob_store_or_create` will see the correct schema\n        // when it eventually runs.\n        if let Ok((table, ..)) = self.get_table_and_blob_store_mut(row.table_id) {\n            table.with_mut_schema(|schema| -> Result<()> {\n                schema.table_access = row.table_access;\n                schema.primary_key = row.table_primary_key.map(|col_list| col_list.as_singleton().ok_or_else(|| anyhow::anyhow!(\"When replaying `st_column` update: `table_primary_key` should be a single column, but found {col_list:?}\"))).transpose()?;\n                schema.table_name = row.table_name;\n                if row.table_type == schema.table_type {\n                    Ok(())\n                } else {\n                    Err(anyhow::anyhow!(\n                    \"When replaying `st_column` update: `table_type` should not have changed, but previous schema has {:?} and new schema has {:?}\",\n                    schema.table_type,\n                    row.table_type,\n                ).into())\n}\n            })?;\n        }\n        Ok(())\n    }\n\n    /// Mark all `st_column` rows which refer to the same column as `st_column_row`\n    /// other than the one at `row_pointer` as outdated\n    /// by storing them in [`Self::replay_columns_to_ignore`].\n    ///\n    /// Returns the ID of the table to which `st_column_row` belongs.\n    fn ignore_previous_versions_of_column(\n        &mut self,\n        st_column_row: &ProductValue,\n        row_ptr: RowPointer,\n    ) -> Result<TableId> {\n        let target_table_id = Self::read_table_id(st_column_row);\n        let target_col_id = ColId::deserialize(ValueDeserializer::from_ref(&st_column_row.elements[1]))\n            .expect(\"second field in `st_column` should decode to a `ColId`\");\n\n        let outdated_st_column_rows = iter_st_column_for_table(self, &target_table_id.into())?\n            .filter_map(|row_ref| {\n                StColumnRow::try_from(row_ref)\n                    .map(|c| (c.col_pos == target_col_id && row_ref.pointer() != row_ptr).then(|| row_ref.pointer()))\n                    .transpose()\n            })\n            .collect::<Result<Vec<RowPointer>>>()?;\n\n        for row in outdated_st_column_rows {\n            self.replay_columns_to_ignore.insert(row);\n        }\n\n        Ok(target_table_id)\n    }\n\n    /// Refreshes the columns and layout of a table\n    /// when a `row` has been inserted from `st_column`.\n    ///\n    /// The `row_ptr` is a pointer to `row`.\n    fn st_column_changed(&mut self, table_id: TableId) -> Result<()> {\n        let table_name = self.find_st_table_row(table_id)?.table_name;\n\n        // We're replaying and we don't have unique constraints yet.\n        // Due to replay handling all inserts first and deletes after,\n        // when processing `st_column` insert/deletes,\n        // we may end up with two definitions for the same `col_pos`.\n        // Of those two, we're interested in the one we just inserted\n        // and not the other one, as it is being replaced.\n        // `Self::ignore_previous_version_of_column` has marked the old version as ignored,\n        // so filter only the non-ignored columns.\n        let mut columns = iter_st_column_for_table(self, &table_id.into())?\n            .filter(|row_ref| !self.replay_columns_to_ignore.contains(&row_ref.pointer()))\n            .map(|row_ref| {\n                let row = StColumnRow::try_from(row_ref)?;\n                let mut column_schema = ColumnSchema::from(row);\n                let alias = self\n                    .find_st_column_accessor_row(table_name.as_ref(), &column_schema.col_name)?\n                    .map(|row| row.accessor_name);\n                column_schema.alias = alias;\n                Ok(column_schema)\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        // Columns in `st_column` are not in general sorted by their `col_pos`,\n        // though they will happen to be for tables which have never undergone migrations\n        // because their initial insertion order matches their `col_pos` order.\n        columns.sort_by_key(|col: &ColumnSchema| col.col_pos);\n\n        // Update the columns and layout of the the in-memory table.\n        if let Some(table) = self.tables.get_mut(&table_id) {\n            table.change_columns_to(columns).map_err(TableError::from)?;\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn replay_end_tx(&mut self) -> Result<()> {\n        self.next_tx_offset += 1;\n\n        if !self.replay_columns_to_ignore.is_empty() {\n            return Err(anyhow::anyhow!(\n                \"`CommittedState::replay_columns_to_ignore` should be empty at the end of a commit, but found {} entries\",\n                self.replay_columns_to_ignore.len(),\n            ).into());\n        }\n\n        if !self.replay_table_updated.is_empty() {\n            return Err(anyhow::anyhow!(\n                \"`CommittedState::replay_table_updated` should be empty at the end of a commit, but found {} entries\",\n                self.replay_table_updated.len(),\n            )\n            .into());\n        }\n\n        // Any dropped tables should be fully gone by the end of a transaction;\n        // if we see any reference to them in the future we should error, not ignore.\n        self.replay_table_dropped.clear();\n\n        Ok(())\n    }\n\n    /// Assuming that a `TableId` is stored as the first field in `row`, read it.\n    fn read_table_id(row: &ProductValue) -> TableId {\n        TableId::deserialize(ValueDeserializer::from_ref(&row.elements[0]))\n            .expect(\"first field in `st_column` should decode to a `TableId`\")\n    }\n\n    /// Builds the in-memory state of sequences from `st_sequence` system table.\n    /// The tables store the lasted allocated value, which tells us where to start generating.\n    pub(super) fn build_sequence_state(&mut self) -> Result<SequencesState> {\n        let mut sequence_state = SequencesState::default();\n        let st_sequences = self.tables.get(&ST_SEQUENCE_ID).unwrap();\n        for row_ref in st_sequences.scan_rows(&self.blob_store) {\n            let sequence = StSequenceRow::try_from(row_ref)?;\n            let seq = Sequence::new(sequence.clone().into(), Some(sequence.allocated));\n\n            // Clobber any existing in-memory `Sequence`.\n            // Such a value may exist because, when replaying without a snapshot,\n            // `build_sequence_state` is called twice:\n            // once when bootstrapping the empty datastore,\n            // and then again after replaying the commitlog.\n            // At this latter time, `sequence_state.get(seq.id())` for the system table sequences\n            // will return a sequence with incorrect `allocated`,\n            // as it will reflect the state after initializing the system tables,\n            // but before creating any user tables.\n            // The `sequence` we read out of `row_ref` above, and used to construct `seq`,\n            // will correctly reflect the state after creating user tables.\n            sequence_state.insert(seq);\n        }\n        Ok(sequence_state)\n    }\n\n    pub(super) fn build_indexes(&mut self) -> Result<()> {\n        let st_indexes = self.tables.get(&ST_INDEX_ID).unwrap();\n        let rows = st_indexes\n            .scan_rows(&self.blob_store)\n            .map(StIndexRow::try_from)\n            .collect::<Result<Vec<_>>>()?;\n\n        let st_constraints = self.tables.get(&ST_CONSTRAINT_ID).unwrap();\n        let unique_constraints: HashSet<(TableId, ColSet)> = st_constraints\n            .scan_rows(&self.blob_store)\n            .map(StConstraintRow::try_from)\n            .filter_map(Result::ok)\n            .filter_map(|constraint| match constraint.constraint_data {\n                StConstraintData::Unique { columns } => Some((constraint.table_id, columns)),\n                _ => None,\n            })\n            .collect();\n\n        for index_row in rows {\n            let index_id = index_row.index_id;\n            let table_id = index_row.table_id;\n            let (table, blob_store, index_id_map, _) = self\n                .get_table_and_blob_store_mut(table_id)\n                .expect(\"index should exist in committed state; cannot create it\");\n            let algo: IndexAlgorithm = index_row.index_algorithm.into();\n            let columns: ColSet = algo.columns().into();\n            let is_unique = unique_constraints.contains(&(table_id, columns));\n\n            let index = table.new_index(&algo, is_unique)?;\n            // SAFETY: `index` was derived from `table`.\n            unsafe { table.insert_index(blob_store, index_id, index) };\n            index_id_map.insert(index_id, table_id);\n        }\n        Ok(())\n    }\n\n    pub(super) fn collect_ephemeral_tables(&mut self) -> Result<()> {\n        self.ephemeral_tables = self.ephemeral_tables()?.into_iter().collect();\n        Ok(())\n    }\n\n    fn ephemeral_tables(&self) -> Result<Vec<TableId>> {\n        let mut tables = vec![ST_VIEW_SUB_ID, ST_VIEW_ARG_ID];\n\n        let Some(st_view) = self.tables.get(&ST_VIEW_ID) else {\n            return Ok(tables);\n        };\n        let backing_tables = st_view\n            .scan_rows(&self.blob_store)\n            .map(|row_ref| {\n                let view_row = StViewRow::try_from(row_ref)?;\n                view_row\n                    .table_id\n                    .ok_or_else(|| DatastoreError::View(ViewError::TableNotFound(view_row.view_id)))\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        tables.extend(backing_tables);\n\n        Ok(tables)\n    }\n\n    /// After replaying all old transactions,\n    /// inserts and deletes into the system tables\n    /// might not be reflected in the schemas of the built tables.\n    /// So we must re-schema every built table.\n    pub(super) fn reschema_tables(&mut self) -> Result<()> {\n        // For already built tables, we need to reschema them to account for constraints et al.\n        let mut schemas = Vec::with_capacity(self.tables.len());\n        for table_id in self.tables.keys().copied() {\n            schemas.push(self.schema_for_table_raw(table_id)?);\n        }\n        for (table, schema) in self.tables.values_mut().zip(schemas) {\n            table.with_mut_schema(|s| *s = schema);\n        }\n        Ok(())\n    }\n\n    /// After replaying all old transactions, tables which have rows will\n    /// have been created in memory, but tables with no rows will not have\n    /// been created. This function ensures that they are created.\n    pub(super) fn build_missing_tables(&mut self) -> Result<()> {\n        // Find all ids of tables that are in `st_tables` but haven't been built.\n        let table_ids = self\n            .get_table(ST_TABLE_ID)\n            .unwrap()\n            .scan_rows(&self.blob_store)\n            .map(|r| r.read_col(StTableFields::TableId).unwrap())\n            .filter(|table_id| self.get_table(*table_id).is_none())\n            .collect::<Vec<_>>();\n\n        // Construct their schemas and insert tables for them.\n        for table_id in table_ids {\n            let schema = self.schema_for_table(table_id)?;\n            self.create_table(table_id, schema);\n        }\n        Ok(())\n    }\n\n    /// Returns an iterator doing a full table scan on `table_id`.\n    pub(super) fn table_scan<'a>(&'a self, table_id: TableId) -> Option<TableScanIter<'a>> {\n        Some(self.get_table(table_id)?.scan_rows(&self.blob_store))\n    }\n\n    /// When there's an index on `cols`,\n    /// returns an iterator over the [TableIndex] that yields all the [`RowRef`]s\n    /// that match the specified `range` in the indexed column.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    ///\n    /// For a unique index this will always yield at most one `RowRef`\n    /// when `range` is a point.\n    /// When there is no index this returns `None`.\n    pub(super) fn index_seek_range<'a>(\n        &'a self,\n        table_id: TableId,\n        cols: &ColList,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> Option<IndexSeekRangeResult<IndexScanRangeIter<'a>>> {\n        self.tables\n            .get(&table_id)?\n            .get_index_by_cols_with_table(&self.blob_store, cols)\n            .map(|i| i.seek_range(range))\n    }\n\n    /// When there's an index on `cols`,\n    /// returns an iterator over the [TableIndex] that yields all the [`RowRef`]s\n    /// that equal `value` in the indexed column.\n    ///\n    /// Matching is defined by `Eq for AlgebraicValue`.\n    ///\n    /// For a unique index this will always yield at most one `RowRef`.\n    /// When there is no index this returns `None`.\n    pub(super) fn index_seek_point<'a>(\n        &'a self,\n        table_id: TableId,\n        cols: &ColList,\n        value: &AlgebraicValue,\n    ) -> Option<IndexScanPointIter<'a>> {\n        self.tables\n            .get(&table_id)?\n            .get_index_by_cols_with_table(&self.blob_store, cols)\n            .map(|i| i.seek_point(value))\n    }\n\n    /// Returns the table associated with the given `index_id`, if any.\n    pub(super) fn get_table_for_index(&self, index_id: IndexId) -> Option<TableId> {\n        self.index_id_map.get(&index_id).copied()\n    }\n\n    /// Returns the table for `table_id` combined with the index for `index_id`, if both exist.\n    pub(super) fn get_index_by_id_with_table(&self, table_id: TableId, index_id: IndexId) -> Option<TableAndIndex<'_>> {\n        self.tables\n            .get(&table_id)?\n            .get_index_by_id_with_table(&self.blob_store, index_id)\n    }\n\n    // TODO(perf, deep-integration): Make this method `unsafe`. Add the following to the docs:\n    //\n    // # Safety\n    //\n    // `pointer` must refer to a row within the table at `table_id`\n    // which was previously inserted and has not been deleted since.\n    //\n    // See [`RowRef::new`] for more detailed requirements.\n    //\n    // Showing that `pointer` was the result of a call to `self.insert`\n    // with `table_id`\n    // within the current transaction (i.e. without an intervening call to self.merge)\n    // is sufficient to demonstrate that a call to `self.get` is safe.\n    pub(super) fn get(&self, table_id: TableId, row_ptr: RowPointer) -> RowRef<'_> {\n        debug_assert!(\n            row_ptr.squashed_offset().is_committed_state(),\n            \"Cannot get TX_STATE RowPointer from CommittedState.\",\n        );\n        let table = self\n            .get_table(table_id)\n            .expect(\"Attempt to get COMMITTED_STATE row from table not present in tables.\");\n        // TODO(perf, deep-integration): Use `get_row_ref_unchecked`.\n        table.get_row_ref(&self.blob_store, row_ptr).unwrap()\n    }\n\n    /// True if the transaction `(tx_data, ctx)` will be written to the commitlog,\n    /// and therefore consumes a value from `self.next_tx_offset`.\n    ///\n    /// A TX is written to the logs if any of the following holds:\n    /// - The TX inserted at least one row.\n    /// - The TX deleted at least one row.\n    /// - The TX was the result of the reducers `__identity_connected__` or `__identity_disconnected__`.\n    fn tx_consumes_offset(&self, tx_data: &TxData, ctx: &ExecutionContext) -> bool {\n        // Avoid appending transactions to the commitlog which don't modify\n        // any tables.\n        //\n        // An exception are connect / disconnect calls, which we always want\n        // paired in the log, so as to be able to disconnect clients\n        // automatically after a server crash. See:\n        // [`crate::host::ModuleHost::call_identity_connected_disconnected`]\n        //\n        // Note that this may change in the future: some analytics and/or\n        // timetravel queries may benefit from seeing all inputs, even if\n        // the database state did not change.\n        tx_data.has_rows_or_connect_disconnect(ctx.reducer_context().map(|rcx| &rcx.name))\n    }\n\n    pub(super) fn drop_view_from_read_sets(&mut self, view_id: ViewId, sender: Option<Identity>) {\n        self.read_sets.remove_view(view_id, sender)\n    }\n\n    pub(super) fn merge(&mut self, tx_state: TxState, read_sets: ViewReadSets, ctx: &ExecutionContext) -> TxData {\n        let mut tx_data = TxData::default();\n        let mut truncates = IntSet::default();\n\n        // First, apply deletes. This will free up space in the committed tables.\n        self.merge_apply_deletes(\n            &mut tx_data,\n            tx_state.delete_tables,\n            tx_state.pending_schema_changes,\n            &mut truncates,\n        );\n\n        // Then, apply inserts. This will re-fill the holes freed by deletions\n        // before allocating new pages.\n        self.merge_apply_inserts(\n            &mut tx_data,\n            tx_state.insert_tables,\n            tx_state.blob_store,\n            &mut truncates,\n        );\n\n        // Record any truncated tables in the `TxData`.\n        tx_data.set_truncates(truncates);\n\n        // Merge read sets from the `MutTxId` into the `CommittedState`.\n        // It's important that this happens after applying the changes to `tx_data`,\n        // which implies `tx_data` already contains inserts and deletes for view tables\n        // so that we can pass updated set of table ids.\n        self.merge_read_sets(read_sets);\n\n        // Store in `tx_data` which of the updated tables are ephemeral.\n        // NOTE: This must be called before `tx_consumes_offset`, so that\n        // all-ephemeral transactions do not consume a tx offset.\n        tx_data.set_ephemeral_tables(&self.ephemeral_tables);\n\n        // If the TX will be logged, record its projected tx offset,\n        // then increment the counter.\n        if self.tx_consumes_offset(&tx_data, ctx) {\n            tx_data.set_tx_offset(self.next_tx_offset);\n            self.next_tx_offset += 1;\n        }\n\n        tx_data\n    }\n\n    fn merge_read_sets(&mut self, read_sets: ViewReadSets) {\n        self.read_sets.merge(read_sets)\n    }\n\n    fn merge_apply_deletes(\n        &mut self,\n        tx_data: &mut TxData,\n        delete_tables: BTreeMap<TableId, DeleteTable>,\n        pending_schema_changes: ThinVec<PendingSchemaChange>,\n        truncates: &mut IntSet<TableId>,\n    ) {\n        fn delete_rows(\n            tx_data: &mut TxData,\n            table_id: TableId,\n            table: &mut Table,\n            blob_store: &mut dyn BlobStore,\n            row_ptrs_len: usize,\n            row_ptrs: impl Iterator<Item = RowPointer>,\n            truncates: &mut IntSet<TableId>,\n        ) {\n            let mut deletes = Vec::with_capacity(row_ptrs_len);\n\n            // Note: we maintain the invariant that the delete_tables\n            // holds only committed rows which should be deleted,\n            // i.e. `RowPointer`s with `SquashedOffset::COMMITTED_STATE`,\n            // so no need to check before applying the deletes.\n            for row_ptr in row_ptrs {\n                debug_assert!(row_ptr.squashed_offset().is_committed_state());\n\n                // TODO: re-write `TxData` to remove `ProductValue`s\n                let pv = table\n                    .delete(blob_store, row_ptr, |row| row.to_product_value())\n                    .expect(\"Delete for non-existent row!\");\n                deletes.push(pv);\n            }\n\n            if !deletes.is_empty() {\n                let table_name = &table.get_schema().table_name;\n                tx_data.set_deletes_for_table(table_id, table_name, deletes.into());\n                let truncated = table.row_count == 0;\n                if truncated {\n                    truncates.insert(table_id);\n                }\n            }\n        }\n\n        for (table_id, row_ptrs) in delete_tables {\n            match self.get_table_and_blob_store_mut(table_id) {\n                Ok((table, blob_store, ..)) => delete_rows(\n                    tx_data,\n                    table_id,\n                    table,\n                    blob_store,\n                    row_ptrs.len(),\n                    row_ptrs.iter(),\n                    truncates,\n                ),\n                Err(_) if !row_ptrs.is_empty() => panic!(\"Deletion for non-existent table {table_id:?}... huh?\"),\n                Err(_) => {}\n            }\n        }\n\n        // Delete all tables marked for deletion.\n        // The order here does not matter as once a `table_id` has been dropped\n        // it will never be re-created.\n        for change in pending_schema_changes {\n            if let PendingSchemaChange::TableRemoved(table_id, mut table) = change {\n                let row_ptrs = table.scan_all_row_ptrs();\n                truncates.insert(table_id);\n                delete_rows(\n                    tx_data,\n                    table_id,\n                    &mut table,\n                    &mut self.blob_store,\n                    row_ptrs.len(),\n                    row_ptrs.into_iter(),\n                    truncates,\n                );\n            }\n        }\n    }\n\n    fn merge_apply_inserts(\n        &mut self,\n        tx_data: &mut TxData,\n        insert_tables: BTreeMap<TableId, Table>,\n        tx_blob_store: impl BlobStore,\n        truncates: &mut IntSet<TableId>,\n    ) {\n        // TODO(perf): Consider moving whole pages from the `insert_tables` into the committed state,\n        //             rather than copying individual rows out of them.\n        //             This will require some magic to get the indexes right,\n        //             and may lead to a large number of mostly-empty pages in the committed state.\n        //             Likely we want to decide dynamically whether to move a page or copy its contents,\n        //             based on the available holes in the committed state\n        //             and the fullness of the page.\n\n        for (table_id, tx_table) in insert_tables {\n            // For each newly-inserted row, serialize to a product value.\n            let mut inserts = Vec::with_capacity(tx_table.row_count as usize);\n            inserts.extend(tx_table.scan_rows(&tx_blob_store).map(|row| row.to_product_value()));\n\n            // For each newly-inserted row, serialize to a product value.\n            // This doesn't apply to event tables,\n            // which are only recorded in `TxData`,\n            // but never the committed state.\n            let schema = tx_table.get_schema();\n            if !schema.is_event {\n                let (commit_table, commit_blob_store, page_pool) =\n                    self.get_table_and_blob_store_or_create(table_id, tx_table.get_schema());\n                for row in &inserts {\n                    commit_table\n                        .insert(page_pool, commit_blob_store, row)\n                        .expect(\"Failed to insert when merging commit\");\n                }\n            }\n\n            // Add the table to `TxData` if there were insertions.\n            if !inserts.is_empty() {\n                tx_data.set_inserts_for_table(table_id, &schema.table_name, inserts.into());\n\n                // If table has inserted rows, it cannot be truncated.\n                if truncates.contains(&table_id) {\n                    truncates.remove(&table_id);\n                }\n            }\n\n            let (.., pages) = tx_table.consume_for_merge();\n\n            // Put all the pages in the table back into the pool.\n            self.page_pool.put_many(pages);\n        }\n    }\n\n    /// Rolls back the changes immediately made to the committed state during a transaction.\n    pub(super) fn rollback(&mut self, seq_state: &mut SequencesState, tx_state: TxState) -> TxOffset {\n        // Roll back the changes in the reverse order in which they were made\n        // so that e.g., the last change is undone first.\n        for change in tx_state.pending_schema_changes.into_iter().rev() {\n            self.rollback_pending_schema_change(seq_state, change);\n        }\n        self.next_tx_offset.saturating_sub(1)\n    }\n\n    fn rollback_pending_schema_change(\n        &mut self,\n        seq_state: &mut SequencesState,\n        change: PendingSchemaChange,\n    ) -> Option<()> {\n        use PendingSchemaChange::*;\n        match change {\n            // An index was removed. Add it back.\n            IndexRemoved(table_id, index_id, table_index, index_schema) => {\n                let table = self.tables.get_mut(&table_id)?;\n                // SAFETY: `table_index` was derived from `table`.\n                unsafe { table.add_index(index_id, table_index) };\n                table.with_mut_schema(|s| s.update_index(index_schema));\n                self.index_id_map.insert(index_id, table_id);\n            }\n            // An index was added. Remove it.\n            IndexAdded(table_id, index_id, pointer_map) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.delete_index(&self.blob_store, index_id, pointer_map);\n                table.with_mut_schema(|s| s.remove_index(index_id));\n                self.index_id_map.remove(&index_id);\n            }\n            // A table was removed. Add it back.\n            TableRemoved(table_id, table) => {\n                let is_view_table = table.schema.is_view();\n                // We don't need to deal with sub-components.\n                // That is, we don't need to add back indices and such.\n                // Instead, there will be separate pending schema changes like `IndexRemoved`.\n                self.tables.insert(table_id, table);\n\n                // Incase, the table was ephemeral, add it back to that set as well.\n                if is_view_table {\n                    self.ephemeral_tables.insert(table_id);\n                }\n            }\n            // A table was added. Remove it.\n            TableAdded(table_id) => {\n                // We don't need to deal with sub-components.\n                // That is, we don't need to remove indices and such.\n                // Instead, there will be separate pending schema changes like `IndexAdded`.\n                self.tables.remove(&table_id);\n                // Incase, the table was ephemeral, remove it from that set as well.\n                self.ephemeral_tables.remove(&table_id);\n            }\n            // A table's access was changed. Change back to the old one.\n            TableAlterAccess(table_id, access) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.with_mut_schema(|s| s.table_access = access);\n            }\n            // A table's row type was changed. Change back to the old one.\n            // The row representation of old rows hasn't changed,\n            // so it's safe to not rewrite the rows and merely change the type back.\n            TableAlterRowType(table_id, column_schemas) => {\n                let table = self.tables.get_mut(&table_id)?;\n                // SAFETY:\n                // Let the \"old\" type/schema be the one in `column_schemas`.\n                // Let the \"new\" type/schema be the one used by the table which we are rolling back.\n                // There's no need to validate \"old\",\n                // as it was the row type prior to the change which we're rolling back.\n                // We can use \"old\", as this is the commit table,\n                // which is immutable to row addition during a transaction,\n                // and thus will only have rows compatible with it.\n                // The rows in the tx state might not be, as they may use e.g., a new variant.\n                // However, we don't care about that, as the tx state is being discarded.\n                unsafe { table.change_columns_to_unchecked(column_schemas, |_, _, _| Ok::<_, Infallible>(())) }\n                    .unwrap_or_else(|e| match e {});\n            }\n            // A constraint was removed. Add it back.\n            ConstraintRemoved(table_id, constraint_schema) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.with_mut_schema(|s| s.update_constraint(constraint_schema));\n            }\n            // A constraint was added. Remove it.\n            ConstraintAdded(table_id, constraint_id) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.with_mut_schema(|s| s.remove_constraint(constraint_id));\n            }\n            // A sequence was removed. Add it back.\n            SequenceRemoved(table_id, seq, schema) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.with_mut_schema(|s| s.update_sequence(schema));\n                seq_state.insert(seq);\n            }\n            // A sequence was added. Remove it.\n            SequenceAdded(table_id, sequence_id) => {\n                let table = self.tables.get_mut(&table_id)?;\n                table.with_mut_schema(|s| s.remove_sequence(sequence_id));\n                seq_state.remove(sequence_id);\n            }\n        }\n\n        Some(())\n    }\n\n    pub(super) fn get_table(&self, table_id: TableId) -> Option<&Table> {\n        self.tables.get(&table_id)\n    }\n\n    #[allow(clippy::unnecessary_lazy_evaluations)]\n    pub fn get_table_and_blob_store(&self, table_id: TableId) -> Result<CommitTableForInsertion<'_>> {\n        let table = self\n            .get_table(table_id)\n            .ok_or_else(|| TableError::IdNotFoundState(table_id))?;\n        Ok((table, &self.blob_store as &dyn BlobStore, &self.index_id_map))\n    }\n\n    pub(super) fn get_table_and_blob_store_mut(\n        &mut self,\n        table_id: TableId,\n    ) -> Result<(&mut Table, &mut dyn BlobStore, &mut IndexIdMap, &PagePool)> {\n        // NOTE(centril): `TableError` is a fairly large type.\n        // Not making this lazy made `TableError::drop` show up in perf.\n        // TODO(centril): Box all the errors.\n        #[allow(clippy::unnecessary_lazy_evaluations)]\n        let table = self\n            .tables\n            .get_mut(&table_id)\n            .ok_or_else(|| TableError::IdNotFoundState(table_id))?;\n        Ok((\n            table,\n            &mut self.blob_store as &mut dyn BlobStore,\n            &mut self.index_id_map,\n            &self.page_pool,\n        ))\n    }\n\n    fn make_table(schema: Arc<TableSchema>) -> Table {\n        Table::new(schema, SquashedOffset::COMMITTED_STATE)\n    }\n\n    fn create_table(&mut self, table_id: TableId, schema: Arc<TableSchema>) {\n        self.tables.insert(table_id, Self::make_table(schema));\n    }\n\n    pub(super) fn get_table_and_blob_store_or_create<'this>(\n        &'this mut self,\n        table_id: TableId,\n        schema: &Arc<TableSchema>,\n    ) -> (&'this mut Table, &'this mut dyn BlobStore, &'this PagePool) {\n        let table = self\n            .tables\n            .entry(table_id)\n            .or_insert_with(|| Self::make_table(schema.clone()));\n        let blob_store = &mut self.blob_store;\n        let pool = &self.page_pool;\n        (table, blob_store, pool)\n    }\n\n    /// Returns an iterator over all persistent tables (i.e., non-ephemeral tables)\n    pub(super) fn persistent_tables_and_blob_store(&mut self) -> (impl Iterator<Item = &mut Table>, &HashMapBlobStore) {\n        (\n            self.tables\n                .iter_mut()\n                .filter(|(table_id, _)| !self.ephemeral_tables.contains(*table_id))\n                .map(|(_, table)| table),\n            &self.blob_store,\n        )\n    }\n\n    pub fn report_data_size(&self, database_identity: Identity) {\n        use crate::db_metrics::data_size::DATA_SIZE_METRICS;\n\n        for (_, table) in &self.tables {\n            let table_name = &table.schema.table_name;\n            DATA_SIZE_METRICS\n                .data_size_table_num_rows\n                .with_label_values(&database_identity, table_name)\n                .set(table.num_rows() as _);\n            DATA_SIZE_METRICS\n                .data_size_table_bytes_used_by_rows\n                .with_label_values(&database_identity, table_name)\n                .set(table.bytes_used_by_rows() as _);\n            DATA_SIZE_METRICS\n                .data_size_table_num_rows_in_indexes\n                .with_label_values(&database_identity, table_name)\n                .set(table.num_rows_in_indexes() as _);\n            DATA_SIZE_METRICS\n                .data_size_table_bytes_used_by_index_keys\n                .with_label_values(&database_identity, table_name)\n                .set(table.bytes_used_by_index_keys() as _);\n        }\n\n        DATA_SIZE_METRICS\n            .data_size_blob_store_num_blobs\n            .with_label_values(&database_identity)\n            .set(self.blob_store.num_blobs() as _);\n        DATA_SIZE_METRICS\n            .data_size_blob_store_bytes_used_by_blobs\n            .with_label_values(&database_identity)\n            .set(self.blob_store.bytes_used_by_blobs() as _);\n    }\n}\n\npub(super) type CommitTableForInsertion<'a> = (&'a Table, &'a dyn BlobStore, &'a IndexIdMap);\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/datastore.rs",
    "content": "use super::{\n    committed_state::CommittedState, mut_tx::MutTxId, sequence::SequencesState, state_view::StateView, tx::TxId,\n    tx_state::TxState,\n};\nuse crate::{\n    db_metrics::DB_METRICS,\n    error::{DatastoreError, TableError},\n    locking_tx_datastore::{\n        state_view::{IterByColEqMutTx, IterByColRangeMutTx, IterMutTx},\n        IterByColEqTx, IterByColRangeTx,\n    },\n    traits::{InsertFlags, UpdateFlags},\n};\nuse crate::{\n    execution_context::ExecutionContext,\n    system_tables::{\n        read_hash_from_col, read_identity_from_col, system_table_schema, StClientRow, StModuleFields, StModuleRow,\n        StTableFields, ST_CLIENT_ID, ST_MODULE_ID, ST_TABLE_ID,\n    },\n    traits::{\n        DataRow, IsolationLevel, Metadata, MutTx, MutTxDatastore, Program, RowTypeForTable, Tx, TxData, TxDatastore,\n    },\n};\nuse crate::{\n    execution_context::{Workload, WorkloadType},\n    system_tables::StTableRow,\n};\nuse anyhow::{anyhow, Context};\nuse core::{cell::RefCell, ops::RangeBounds};\nuse parking_lot::{Mutex, RwLock, RwLockReadGuard};\nuse spacetimedb_commitlog::payload::txdata;\nuse spacetimedb_data_structures::map::{HashCollectionExt, HashMap, IntMap};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_lib::{db::auth::StAccess, metrics::ExecutionMetrics};\nuse spacetimedb_lib::{ConnectionId, Identity};\nuse spacetimedb_paths::server::SnapshotDirPath;\nuse spacetimedb_primitives::{ColList, ConstraintId, IndexId, SequenceId, TableId, ViewId};\nuse spacetimedb_sats::{\n    algebraic_value::de::ValueDeserializer, bsatn, buffer::BufReader, AlgebraicValue, ProductValue,\n};\nuse spacetimedb_sats::{memory_usage::MemoryUsage, Deserialize};\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_schema::{\n    reducer_name::ReducerName,\n    schema::{ColumnSchema, IndexSchema, SequenceSchema, TableSchema},\n};\nuse spacetimedb_snapshot::{ReconstructedSnapshot, SnapshotRepository};\nuse spacetimedb_table::{\n    indexes::RowPointer,\n    page_pool::PagePool,\n    table::{RowRef, TableScanIter},\n};\nuse std::borrow::Cow;\nuse std::sync::Arc;\nuse std::time::{Duration, Instant};\nuse thiserror::Error;\n\npub type Result<T> = std::result::Result<T, DatastoreError>;\n\n/// Struct contains various database states, each protected by\n/// their own lock. To avoid deadlocks, it is crucial to acquire these locks\n/// in a consistent order throughout the application.\n///\n/// Lock Acquisition Order:\n/// 1. `memory`\n/// 2. `committed_state`\n/// 3. `sequence_state`\n///\n/// All locking mechanisms are encapsulated within the struct through local methods.\n#[derive(Clone)]\npub struct Locking {\n    /// The state of the database up to the point of the last committed transaction.\n    // TODO(cloutiertyler): This was made `pub` for the datastore split. This should be\n    // made private again.\n    pub committed_state: Arc<RwLock<CommittedState>>,\n    /// The state of sequence generation in this database.\n    sequence_state: Arc<Mutex<SequencesState>>,\n    /// The identity of this database.\n    pub(crate) database_identity: Identity,\n}\n\nimpl MemoryUsage for Locking {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            committed_state,\n            sequence_state,\n            database_identity,\n        } = self;\n        std::mem::size_of_val(&**committed_state)\n            + committed_state.read().heap_usage()\n            + std::mem::size_of_val(&**sequence_state)\n            + sequence_state.lock().heap_usage()\n            + database_identity.heap_usage()\n    }\n}\n\nimpl Locking {\n    pub fn new(database_identity: Identity, page_pool: PagePool) -> Self {\n        Self {\n            committed_state: Arc::new(RwLock::new(CommittedState::new(page_pool))),\n            sequence_state: <_>::default(),\n            database_identity,\n        }\n    }\n\n    /// IMPORTANT! This the most delicate function in the entire codebase.\n    /// DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING!!!\n    pub fn bootstrap(database_identity: Identity, page_pool: PagePool) -> Result<Self> {\n        log::trace!(\"DATABASE: BOOTSTRAPPING SYSTEM TABLES...\");\n\n        // NOTE! The bootstrapping process does not take plan in a transaction.\n        // This is intentional.\n        let datastore = Self::new(database_identity, page_pool);\n        let mut commit_state = datastore.committed_state.write_arc();\n        // TODO(cloutiertyler): One thing to consider in the future is, should\n        // we persist the bootstrap transaction in the message log? My intuition\n        // is no, because then if we change the schema of the system tables we\n        // would need to migrate that data, whereas since the tables are defined\n        // in the code we don't have that issue. We may have other issues though\n        // for code that relies on the old schema...\n\n        // Create the system tables and insert information about themselves into\n        commit_state.bootstrap_system_tables(database_identity)?;\n        // The database tables are now initialized with the correct data.\n        // Now we have to build our in memory structures.\n        {\n            let sequence_state = commit_state.build_sequence_state()?;\n            // Reset our sequence state so that they start in the right places.\n            *datastore.sequence_state.lock() = sequence_state;\n        }\n\n        // We don't want to build indexes here; we'll build those later,\n        // in `rebuild_state_after_replay`.\n        // We actively do not want indexes to exist during replay,\n        // as they break replaying TX 0.\n\n        log::trace!(\"DATABASE:BOOTSTRAPPING SYSTEM TABLES DONE\");\n        Ok(datastore)\n    }\n\n    /// The purpose of this is to rebuild the state of the datastore\n    /// after having inserted all of rows from the message log.\n    /// This is necessary because, for example, inserting a row into `st_table`\n    /// is not equivalent to calling `create_table`.\n    /// There may eventually be better way to do this, but this will have to do for now.\n    pub fn rebuild_state_after_replay(&self) -> Result<()> {\n        let mut committed_state = self.committed_state.write_arc();\n\n        // Prior versions of `RelationalDb::migrate_system_tables` (defined in the `core` crate)\n        // initialized newly-created system sequences to `allocation: 4097`,\n        // while `committed_state::bootstrap_system_tables` sets `allocation: 4096`.\n        // This affected the system table migration which added\n        // `st_view_view_id_seq` and `st_view_arg_id_seq`.\n        // As a result, when replaying these databases' commitlogs without a snapshot,\n        // we will end up with two rows in `st_sequence` for each of these sequences,\n        // resulting in a unique constraint violation in `CommittedState::build_indexes`.\n        // We fix this by, for each system sequence, deleting all but the row with the highest allocation.\n        committed_state.fixup_delete_duplicate_system_sequence_rows();\n\n        // `build_missing_tables` must be called before indexes.\n        // Honestly this should maybe just be one big procedure.\n        // See John Carmack's philosophy on this.\n        committed_state.reschema_tables()?;\n        committed_state.build_missing_tables()?;\n        committed_state.build_indexes()?;\n        // Figure out where to pick up for each sequence.\n        *self.sequence_state.lock() = committed_state.build_sequence_state()?;\n\n        committed_state.collect_ephemeral_tables()?;\n        Ok(())\n    }\n\n    /// Obtain a [`spacetimedb_commitlog::Decoder`] suitable for replaying a\n    /// [`spacetimedb_durability::History`] onto the currently committed state.\n    ///\n    /// The provided closure will be called for each transaction found in the\n    /// history, the parameter is the transaction's offset. The closure is called\n    /// _before_ the transaction is applied to the database state.\n    pub fn replay<F: FnMut(u64)>(&self, progress: F, error_behavior: ErrorBehavior) -> Replay<F> {\n        Replay {\n            database_identity: self.database_identity,\n            committed_state: self.committed_state.clone(),\n            progress: RefCell::new(progress),\n            error_behavior,\n        }\n    }\n\n    /// Construct a new [`Locking`] datastore containing the state stored in `snapshot`.\n    ///\n    /// - Construct all the tables referenced by `snapshot`, computing their schemas\n    ///   either from known system table schemas or from `st_table` and friends.\n    /// - Populate those tables with all rows in `snapshot`.\n    /// - Construct a [`HashMapBlobStore`] containing all the large blobs referenced by `snapshot`,\n    ///   with reference counts specified in `snapshot`.\n    /// - Notably, **do not** construct indexes or sequences.\n    ///   This should be done by [`Self::rebuild_state_after_replay`],\n    ///   after replaying the suffix of the commitlog.\n    pub fn restore_from_snapshot(snapshot: ReconstructedSnapshot, page_pool: PagePool) -> Result<Self> {\n        let ReconstructedSnapshot {\n            database_identity,\n            tx_offset,\n            blob_store,\n            tables,\n            ..\n        } = snapshot;\n\n        let datastore = Self::new(database_identity, page_pool);\n        let mut committed_state = datastore.committed_state.write_arc();\n        committed_state.blob_store = blob_store;\n\n        // Note that `tables` is a `BTreeMap`, and so iterates in increasing order.\n        // This means that we will instantiate and populate the system tables before any user tables.\n        for (table_id, pages) in tables {\n            let schema = match system_table_schema(table_id) {\n                Some(schema) => Arc::new(schema),\n                // In this case, `schema_for_table` will never see a cached schema,\n                // as the committed state is newly constructed and we have not accessed this schema yet.\n                // As such, this call will compute and save the schema from `st_table` and friends.\n                None => committed_state.schema_for_table(table_id)?,\n            };\n            let (table, blob_store, _) = committed_state.get_table_and_blob_store_or_create(table_id, &schema);\n            unsafe {\n                // Safety:\n                // - The snapshot is uncorrupted because reconstructing it verified its hashes.\n                // - The schema in `table` is either derived from the st_table and st_column,\n                //   which were restored from the snapshot,\n                //   or it is a known schema for a system table.\n                // - We trust that the snapshot was consistent when created,\n                //   so the layout used in the `pages` must be consistent with the schema.\n                table.set_pages(pages, blob_store);\n            }\n\n            // Set the `rdb_num_table_rows` metric for the table.\n            // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer,\n            // and therefore has performance implications and must not be disabled.\n            DB_METRICS\n                .rdb_num_table_rows\n                .with_label_values(&database_identity, &table_id.0, &schema.table_name)\n                .set(table.row_count as i64);\n\n            // Also set the `rdb_table_size` metric for the table.\n            let table_size = table.bytes_occupied_overestimate();\n            DB_METRICS\n                .rdb_table_size\n                .with_label_values(&database_identity, &table_id.into(), &schema.table_name)\n                .set(table_size as i64);\n        }\n\n        // Double check that our in-memory system table ids match the on-disk schemas.\n        // committed_state.assert_system_table_schemas_match()?;\n\n        // Set the sequence state. In practice we will end up doing this again after replaying\n        // the commit log, but we do it here too just to avoid having an incorrectly restored\n        // snapshot.\n        {\n            let sequence_state = committed_state.build_sequence_state()?;\n            // Reset our sequence state so that they start in the right places.\n            *datastore.sequence_state.lock() = sequence_state;\n        }\n\n        // The next TX offset after restoring from a snapshot is one greater than the snapshotted offset.\n        committed_state.next_tx_offset = tx_offset + 1;\n\n        Ok(datastore)\n    }\n\n    /// Take a snapshot of this [`Locking`] datastore's [`CommittedState`]\n    /// and store it in `repo`.\n    ///\n    /// On success, returns:\n    ///\n    /// - `None` if the committed state is empty\n    ///   (i.e. no transactions have been committed yet)\n    ///   and therefore no snapshot was created\n    ///\n    /// - or `Some` path to the newly created snapshot directory\n    ///\n    /// Returns an error if [`SnapshotRepository::create_snapshot`] returns an\n    /// error.\n    pub fn take_snapshot(&self, repo: &SnapshotRepository) -> Result<Option<SnapshotDirPath>> {\n        let maybe_offset_and_path = Self::take_snapshot_internal(&self.committed_state, repo)?;\n        Ok(maybe_offset_and_path.map(|(_, path)| path))\n    }\n\n    pub fn assert_system_tables_match(&self) -> Result<()> {\n        let committed_state = self.committed_state.read_arc();\n        committed_state.assert_system_table_schemas_match()\n    }\n\n    pub fn take_snapshot_internal(\n        committed_state: &RwLock<CommittedState>,\n        repo: &SnapshotRepository,\n    ) -> Result<Option<(TxOffset, SnapshotDirPath)>> {\n        let mut committed_state = committed_state.write();\n        let Some(tx_offset) = committed_state.next_tx_offset.checked_sub(1) else {\n            return Ok(None);\n        };\n\n        log::info!(\n            \"Capturing snapshot of database {:?} at TX offset {}\",\n            repo.database_identity(),\n            tx_offset,\n        );\n\n        let (tables, blob_store) = committed_state.persistent_tables_and_blob_store();\n        let snapshot_dir = repo.create_snapshot(tables, blob_store, tx_offset)?;\n\n        Ok(Some((tx_offset, snapshot_dir)))\n    }\n\n    /// Returns a list over all the currently connected clients,\n    /// reading from the `st_clients` system table.\n    pub fn connected_clients<'a>(\n        &'a self,\n        tx: &'a TxId,\n    ) -> Result<impl Iterator<Item = Result<(Identity, ConnectionId)>> + 'a> {\n        let iter = self.iter_tx(tx, ST_CLIENT_ID)?.map(|row_ref| {\n            let row = StClientRow::try_from(row_ref)?;\n            Ok((row.identity.0, row.connection_id.0))\n        });\n\n        Ok(iter)\n    }\n\n    pub fn alter_table_access_mut_tx(&self, tx: &mut MutTxId, name: &str, access: StAccess) -> Result<()> {\n        let table_id = self\n            .table_id_from_name_mut_tx(tx, name)?\n            .ok_or_else(|| TableError::NotFound(name.into()))?;\n\n        tx.alter_table_access(table_id, access)\n    }\n\n    pub fn alter_table_row_type_mut_tx(\n        &self,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        column_schemas: Vec<ColumnSchema>,\n    ) -> Result<()> {\n        tx.alter_table_row_type(table_id, column_schemas)\n    }\n\n    pub fn add_columns_to_table_mut_tx(\n        &self,\n        tx: &mut MutTxId,\n        table_id: TableId,\n        column_schemas: Vec<ColumnSchema>,\n        defaults: Vec<AlgebraicValue>,\n    ) -> Result<TableId> {\n        tx.add_columns_to_table(table_id, column_schemas, defaults)\n    }\n}\n\nimpl DataRow for Locking {\n    type RowId = RowPointer;\n    type RowRef<'a> = RowRef<'a>;\n\n    fn read_table_id(&self, row_ref: Self::RowRef<'_>) -> Result<TableId> {\n        Ok(row_ref.read_col(StTableFields::TableId)?)\n    }\n}\n\nimpl Tx for Locking {\n    type Tx = TxId;\n\n    /// Begins a read-only transaction under the given `workload`.\n    ///\n    /// While this transaction is pending,\n    /// other read-only transactions may be started,\n    /// but new mutable transactions will block until there are no read-only transactions left.\n    ///\n    /// Blocks if a mutable transaction is pending.\n    fn begin_tx(&self, workload: Workload) -> Self::Tx {\n        let metrics = ExecutionMetrics::default();\n        let ctx = ExecutionContext::with_workload(self.database_identity, workload);\n\n        let timer = Instant::now();\n        let committed_state_shared_lock = self.committed_state.read_arc();\n        let lock_wait_time = timer.elapsed();\n\n        Self::Tx {\n            committed_state_shared_lock,\n            lock_wait_time,\n            timer,\n            ctx,\n            metrics,\n        }\n    }\n\n    /// Release this read-only transaction,\n    /// allowing new mutable transactions to start if this was the last read-only transaction.\n    ///\n    /// Returns:\n    /// - [`TxOffset`], the smallest transaction offset visible to this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran within this transaction.\n    fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        tx.release()\n    }\n}\n\nimpl TxDatastore for Locking {\n    type IterTx<'a>\n        = TableScanIter<'a>\n    where\n        Self: 'a;\n    type IterByColRangeTx<'a, R: RangeBounds<AlgebraicValue>>\n        = IterByColRangeTx<'a, R>\n    where\n        Self: 'a;\n    type IterByColEqTx<'a, 'r>\n        = IterByColEqTx<'a, 'r>\n    where\n        Self: 'a;\n\n    fn iter_tx<'a>(&'a self, tx: &'a Self::Tx, table_id: TableId) -> Result<Self::IterTx<'a>> {\n        tx.iter(table_id)\n    }\n\n    fn iter_by_col_range_tx<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a Self::Tx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<Self::IterByColRangeTx<'a, R>> {\n        tx.iter_by_col_range(table_id, cols.into(), range)\n    }\n\n    fn iter_by_col_eq_tx<'a, 'r>(\n        &'a self,\n        tx: &'a Self::Tx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEqTx<'a, 'r>> {\n        tx.iter_by_col_eq(table_id, cols, value)\n    }\n\n    fn table_id_exists_tx(&self, tx: &Self::Tx, table_id: &TableId) -> bool {\n        tx.table_name(*table_id).is_some()\n    }\n\n    fn table_id_from_name_tx(&self, tx: &Self::Tx, table_name: &str) -> Result<Option<TableId>> {\n        tx.table_id_from_name(table_name)\n    }\n\n    fn table_name_from_id_tx<'a>(&'a self, tx: &'a Self::Tx, table_id: TableId) -> Result<Option<Cow<'a, str>>> {\n        Ok(tx.table_name(table_id).map(Cow::Borrowed))\n    }\n\n    fn schema_for_table_tx(&self, tx: &Self::Tx, table_id: TableId) -> Result<Arc<TableSchema>> {\n        tx.schema_for_table(table_id)\n    }\n\n    fn get_all_tables_tx(&self, tx: &Self::Tx) -> Result<Vec<Arc<TableSchema>>> {\n        self.iter_tx(tx, ST_TABLE_ID)?\n            .map(|row_ref| {\n                let table_id = row_ref.read_col(StTableFields::TableId)?;\n                self.schema_for_table_tx(tx, table_id)\n            })\n            .collect()\n    }\n\n    fn metadata(&self, tx: &Self::Tx) -> Result<Option<Metadata>> {\n        self.iter_tx(tx, ST_MODULE_ID)?\n            .next()\n            .map(metadata_from_row)\n            .transpose()\n    }\n\n    fn program(&self, tx: &Self::Tx) -> Result<Option<Program>> {\n        self.iter_tx(tx, ST_MODULE_ID)?\n            .next()\n            .map(|row_ref| {\n                let StModuleRow {\n                    program_kind,\n                    program_hash,\n                    program_bytes,\n                    ..\n                } = row_ref.try_into()?;\n                Ok(Program {\n                    hash: program_hash,\n                    bytes: program_bytes,\n                    kind: program_kind,\n                })\n            })\n            .transpose()\n    }\n}\n\nimpl MutTxDatastore for Locking {\n    type IterMutTx<'a>\n        = IterMutTx<'a>\n    where\n        Self: 'a;\n    type IterByColRangeMutTx<'a, R: RangeBounds<AlgebraicValue>> = IterByColRangeMutTx<'a, R>;\n    type IterByColEqMutTx<'a, 'r>\n        = IterByColEqMutTx<'a, 'r>\n    where\n        Self: 'a;\n\n    fn create_table_mut_tx(&self, tx: &mut Self::MutTx, schema: TableSchema) -> Result<TableId> {\n        tx.create_table(schema)\n    }\n\n    /// This function is used to get the `ProductType` of the rows in a\n    /// particular table.  This will be the `ProductType` as viewed through the\n    /// lens of the current transaction. Because it is expensive to compute the\n    /// `ProductType` in the context of the transaction, we cache the current\n    /// `ProductType` as long as you have not made any changes to the schema of\n    /// the table for in the current transaction.  If the cache is invalid, we\n    /// fallback to computing the `ProductType` from the underlying datastore.\n    ///\n    /// NOTE: If you change the system tables directly rather than using the\n    /// provided functions for altering tables, then the cache may incorrectly\n    /// reflect the schema of the table.q\n    ///\n    /// This function is known to be called quite frequently.\n    fn row_type_for_table_mut_tx<'tx>(&self, tx: &'tx Self::MutTx, table_id: TableId) -> Result<RowTypeForTable<'tx>> {\n        tx.row_type_for_table(table_id)\n    }\n\n    /// IMPORTANT! This function is relatively expensive, and much more\n    /// expensive than `row_type_for_table_mut_tx`.  Prefer\n    /// `row_type_for_table_mut_tx` if you only need to access the `ProductType`\n    /// of the table.\n    fn schema_for_table_mut_tx(&self, tx: &Self::MutTx, table_id: TableId) -> Result<Arc<TableSchema>> {\n        tx.schema_for_table(table_id)\n    }\n\n    /// This function is relatively expensive because it needs to be\n    /// transactional, however we don't expect to be dropping tables very often.\n    fn drop_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId) -> Result<()> {\n        tx.drop_table(table_id)\n    }\n\n    fn rename_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId, new_name: TableName) -> Result<()> {\n        tx.rename_table(table_id, new_name)\n    }\n\n    fn view_id_from_name_mut_tx(&self, tx: &Self::MutTx, view_name: &str) -> Result<Option<ViewId>> {\n        tx.view_id_from_name(view_name)\n    }\n\n    fn table_id_from_name_mut_tx(&self, tx: &Self::MutTx, table_name: &str) -> Result<Option<TableId>> {\n        tx.table_id_from_name_or_alias(table_name)\n    }\n\n    fn table_id_exists_mut_tx(&self, tx: &Self::MutTx, table_id: &TableId) -> bool {\n        tx.table_name(*table_id).is_some()\n    }\n\n    fn table_name_from_id_mut_tx<'a>(&'a self, tx: &'a Self::MutTx, table_id: TableId) -> Result<Option<Cow<'a, str>>> {\n        tx.table_name_from_id(table_id)\n            .map(|opt| opt.map(|s| Cow::Owned(s.into())))\n    }\n\n    fn create_index_mut_tx(&self, tx: &mut Self::MutTx, index_schema: IndexSchema, is_unique: bool) -> Result<IndexId> {\n        tx.create_index(index_schema, is_unique)\n    }\n\n    fn drop_index_mut_tx(&self, tx: &mut Self::MutTx, index_id: IndexId) -> Result<()> {\n        tx.drop_index(index_id)\n    }\n\n    fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> Result<Option<IndexId>> {\n        tx.index_id_from_name_or_alias(index_name)\n    }\n\n    fn get_next_sequence_value_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result<i128> {\n        tx.get_next_sequence_value(seq_id)\n    }\n\n    fn create_sequence_mut_tx(&self, tx: &mut Self::MutTx, sequence_schema: SequenceSchema) -> Result<SequenceId> {\n        tx.create_sequence(sequence_schema)\n    }\n\n    fn drop_sequence_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result<()> {\n        tx.drop_sequence(seq_id)\n    }\n\n    fn sequence_id_from_name_mut_tx(&self, tx: &Self::MutTx, sequence_name: &str) -> Result<Option<SequenceId>> {\n        tx.sequence_id_from_name(sequence_name)\n    }\n\n    fn drop_constraint_mut_tx(&self, tx: &mut Self::MutTx, constraint_id: ConstraintId) -> Result<()> {\n        tx.drop_constraint(constraint_id)\n    }\n\n    fn constraint_id_from_name(&self, tx: &Self::MutTx, constraint_name: &str) -> Result<Option<ConstraintId>> {\n        tx.constraint_id_from_name(constraint_name)\n    }\n\n    fn iter_mut_tx<'a>(&'a self, tx: &'a Self::MutTx, table_id: TableId) -> Result<Self::IterMutTx<'a>> {\n        tx.iter(table_id)\n    }\n\n    fn iter_by_col_range_mut_tx<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<Self::IterByColRangeMutTx<'a, R>> {\n        tx.iter_by_col_range(table_id, cols.into(), range)\n    }\n\n    fn iter_by_col_eq_mut_tx<'a, 'r>(\n        &'a self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEqMutTx<'a, 'r>> {\n        tx.iter_by_col_eq(table_id, cols.into(), value)\n    }\n\n    fn get_mut_tx<'a>(\n        &self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        row_ptr: &'a Self::RowId,\n    ) -> Result<Option<Self::RowRef<'a>>> {\n        // TODO(perf, deep-integration): Rework this interface so that `row_ptr` can be trusted.\n        tx.get(table_id, *row_ptr)\n    }\n\n    fn delete_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        row_ptrs: impl IntoIterator<Item = Self::RowId>,\n    ) -> u32 {\n        let mut num_deleted = 0;\n        for row_ptr in row_ptrs {\n            match tx.delete(table_id, row_ptr) {\n                Err(e) => log::error!(\"delete_mut_tx: {e:?}\"),\n                Ok(b) => num_deleted += b as u32,\n            }\n        }\n        num_deleted\n    }\n\n    fn delete_by_rel_mut_tx(\n        &self,\n        tx: &mut Self::MutTx,\n        table_id: TableId,\n        relation: impl IntoIterator<Item = ProductValue>,\n    ) -> u32 {\n        let mut num_deleted = 0;\n        for row in relation {\n            match tx.delete_by_row_value(table_id, &row) {\n                Err(e) => log::error!(\"delete_by_rel_mut_tx: {e:?}\"),\n                Ok(b) => num_deleted += b as u32,\n            }\n        }\n        num_deleted\n    }\n\n    fn insert_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, InsertFlags)> {\n        let (gens, row_ref, insert_flags) = tx.insert::<true>(table_id, row)?;\n        Ok((gens, row_ref.collapse(), insert_flags))\n    }\n\n    fn update_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        index_id: IndexId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, UpdateFlags)> {\n        let (gens, row_ref, update_flags) = tx.update(table_id, index_id, row)?;\n        Ok((gens, row_ref.collapse(), update_flags))\n    }\n\n    fn metadata_mut_tx(&self, tx: &Self::MutTx) -> Result<Option<Metadata>> {\n        tx.iter(ST_MODULE_ID)?.next().map(metadata_from_row).transpose()\n    }\n\n    fn update_program(&self, tx: &mut Self::MutTx, program: Program) -> Result<()> {\n        let old = tx\n            .iter(ST_MODULE_ID)?\n            .next()\n            .map(|row| {\n                let ptr = row.pointer();\n                let row = StModuleRow::try_from(row)?;\n                Ok::<_, DatastoreError>((ptr, row))\n            })\n            .transpose()?;\n        match old {\n            Some((ptr, mut row)) => {\n                row.program_kind = program.kind;\n                row.program_hash = program.hash;\n                row.program_bytes = program.bytes;\n\n                tx.delete(ST_MODULE_ID, ptr)?;\n                tx.insert_via_serialize_bsatn(ST_MODULE_ID, &row).map(drop)\n            }\n\n            None => Err(anyhow!(\n                \"database {} improperly initialized: no metadata\",\n                self.database_identity\n            )\n            .into()),\n        }\n    }\n}\n\n/// Various measurements, needed for metrics, of the work performed by a transaction.\n#[must_use = \"TxMetrics should be reported\"]\npub struct TxMetrics {\n    /// The transaction metrics for a particular table.\n    /// The value `None` for a [`TableId`] means that it was deleted.\n    table_stats: HashMap<TableId, Option<TableStats>>,\n    workload: WorkloadType,\n    database_identity: Identity,\n    elapsed_time: Duration,\n    lock_wait_time: Duration,\n    committed: bool,\n    exec_metrics: ExecutionMetrics,\n}\n\nstruct TableStats {\n    /// The number of rows in the table after this transaction.\n    ///\n    /// Derived from [`Table::row_count`](spacetimedb_table::table::Table::row_count).\n    row_count: u64,\n    /// The number of bytes occupied by the pages and the blob store after this transaction.\n    ///\n    /// Derived from [`Table::bytes_occupied_overestimate`](spacetimedb_table::table::Table::bytes_occupied_overestimate).\n    bytes_occupied_overestimate: usize,\n    /// The number of indices after this transaction.\n    ///\n    /// Derived from [`Table::num_indices`](spacetimedb_table::table::Table::num_indices).\n    num_indices: usize,\n}\n\npub trait MetricsRecorder {\n    fn record(&self, metrics: &ExecutionMetrics);\n}\n\nimpl TxMetrics {\n    /// Compute transaction metrics that we can report once the tx lock is released.\n    pub(super) fn new(\n        ctx: &ExecutionContext,\n        tx_timer: Instant,\n        lock_wait_time: Duration,\n        exec_metrics: ExecutionMetrics,\n        committed: bool,\n        tx_data: Option<&TxData>,\n        committed_state: &CommittedState,\n    ) -> TxMetrics {\n        let workload = ctx.workload();\n        let database_identity = ctx.database_identity();\n        let elapsed_time = tx_timer.elapsed();\n\n        // For each table, collect the extra stats, that we don't have in `tx_data`.\n        let table_stats = tx_data\n            .map(|tx_data| {\n                let mut table_stats = HashMap::with_capacity(tx_data.num_tables_affected());\n                for (table_id, _) in tx_data.table_ids_and_names() {\n                    let stats = committed_state.get_table(table_id).map(|table| TableStats {\n                        row_count: table.row_count,\n                        bytes_occupied_overestimate: table.bytes_occupied_overestimate(),\n                        num_indices: table.num_indices(),\n                    });\n                    table_stats.insert(table_id, stats);\n                }\n                table_stats\n            })\n            .unwrap_or_default();\n\n        TxMetrics {\n            table_stats,\n            workload,\n            database_identity,\n            elapsed_time,\n            lock_wait_time,\n            exec_metrics,\n            committed,\n        }\n    }\n\n    /// Reports the metrics for `reducer` using `get_exec_counter` to retrieve the metrics counters.\n    pub fn report<'a, R: MetricsRecorder + 'a>(\n        &self,\n        tx_data: Option<&TxData>,\n        reducer: Option<&ReducerName>,\n        get_exec_counter: impl FnOnce(WorkloadType) -> &'a R,\n    ) {\n        let workload = &self.workload;\n        let db = &self.database_identity;\n\n        let cpu_time = self.elapsed_time - self.lock_wait_time;\n\n        let elapsed_time = self.elapsed_time.as_secs_f64();\n        let cpu_time = cpu_time.as_secs_f64();\n\n        let reducer = reducer.map(|r| &**r).unwrap_or_default();\n\n        // Increment tx counter\n        DB_METRICS\n            .rdb_num_txns\n            .with_label_values(workload, db, reducer, &self.committed)\n            .inc();\n        // Record tx cpu time\n        DB_METRICS\n            .rdb_txn_cpu_time_sec\n            .with_label_values(workload, db, reducer)\n            .observe(cpu_time);\n        // Record tx elapsed time\n        DB_METRICS\n            .rdb_txn_elapsed_time_sec\n            .with_label_values(workload, db, reducer)\n            .observe(elapsed_time);\n\n        get_exec_counter(self.workload).record(&self.exec_metrics);\n\n        if let Some(tx_data) = tx_data {\n            for (table_id, table_entry) in tx_data.iter_table_entries() {\n                let table_name = &table_entry.table_name;\n\n                if let Some(stats) = self.table_stats.get(&table_id).unwrap() {\n                    // Update table rows and table size gauges.\n                    DB_METRICS\n                        .rdb_num_table_rows\n                        .with_label_values(db, &table_id.0, table_name)\n                        .set(stats.row_count as i64);\n                    DB_METRICS\n                        .rdb_table_size\n                        .with_label_values(db, &table_id.0, table_name)\n                        .set(stats.bytes_occupied_overestimate as i64);\n\n                    // Record inserts.\n                    let num_inserts = table_entry.inserts.len() as u64;\n                    let num_indices = stats.num_indices as u64;\n                    // Increment rows inserted counter.\n                    DB_METRICS\n                        .rdb_num_rows_inserted\n                        .with_label_values(workload, db, reducer, &table_id.0, table_name)\n                        .inc_by(num_inserts);\n                    // We don't have sparse indexes, so we can just multiply by the number of indexes.\n                    if stats.num_indices > 0 {\n                        // Increment index rows inserted counter\n                        DB_METRICS\n                            .rdb_num_index_entries_inserted\n                            .with_label_values(workload, db, reducer, &table_id.0, table_name)\n                            .inc_by(num_inserts * num_indices);\n                    }\n\n                    // Record deletes.\n                    let num_deletes = table_entry.deletes.len() as u64;\n                    let num_indices = stats.num_indices as u64;\n\n                    // Increment rows deleted counter.\n                    DB_METRICS\n                        .rdb_num_rows_deleted\n                        .with_label_values(workload, db, reducer, &table_id.0, table_name)\n                        .inc_by(num_deletes);\n\n                    // We don't have sparse indexes, so we can just multiply by the number of indexes.\n                    if num_indices > 0 {\n                        // Increment index rows deleted counter.\n                        DB_METRICS\n                            .rdb_num_index_entries_deleted\n                            .with_label_values(workload, db, reducer, &table_id.0, table_name)\n                            .inc_by(num_deletes * num_indices);\n                    }\n                } else {\n                    // Table was dropped, remove the metrics.\n                    let _ = DB_METRICS\n                        .rdb_num_table_rows\n                        .remove_label_values(db, &table_id.0, table_name);\n                    let _ = DB_METRICS\n                        .rdb_table_size\n                        .remove_label_values(db, &table_id.0, table_name);\n                    let _ = DB_METRICS.rdb_num_rows_inserted.remove_label_values(\n                        workload,\n                        db,\n                        reducer,\n                        &table_id.0,\n                        table_name,\n                    );\n                    let _ = DB_METRICS.rdb_num_index_entries_inserted.remove_label_values(\n                        workload,\n                        db,\n                        reducer,\n                        &table_id.0,\n                        table_name,\n                    );\n                    let _ = DB_METRICS.rdb_num_rows_deleted.remove_label_values(\n                        workload,\n                        db,\n                        reducer,\n                        &table_id.0,\n                        table_name,\n                    );\n                    let _ = DB_METRICS.rdb_num_index_entries_deleted.remove_label_values(\n                        workload,\n                        db,\n                        reducer,\n                        &table_id.0,\n                        table_name,\n                    );\n                }\n            }\n        }\n    }\n}\n\nimpl MutTx for Locking {\n    type MutTx = MutTxId;\n\n    /// Begins a mutable transaction under the given `isolation_level` and `workload`.\n    ///\n    /// While this transaction is pending,\n    /// no other read-only  mutable transaction may happen or\n    ///\n    /// Blocks if a read-only or mutable transaction is pending.\n    ///\n    /// Note: We do not use the isolation level here because this implementation\n    /// guarantees the highest isolation level, Serializable.\n    fn begin_mut_tx(&self, _isolation_level: IsolationLevel, workload: Workload) -> Self::MutTx {\n        let metrics = ExecutionMetrics::default();\n        let ctx = ExecutionContext::with_workload(self.database_identity, workload);\n\n        let timer = Instant::now();\n        let committed_state_write_lock = self.committed_state.write_arc();\n        let sequence_state_lock = self.sequence_state.lock_arc();\n        let lock_wait_time = timer.elapsed();\n\n        MutTxId {\n            committed_state_write_lock,\n            sequence_state_lock,\n            tx_state: TxState::default(),\n            lock_wait_time,\n            read_sets: <_>::default(),\n            timer,\n            ctx,\n            metrics,\n            _not_send: std::marker::PhantomData,\n        }\n    }\n\n    fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        tx.rollback()\n    }\n\n    /// This method only updates the in-memory `committed_state`.\n    /// For durability, see `RelationalDB::commit_tx`.\n    fn commit_mut_tx(&self, tx: Self::MutTx) -> Result<Option<(TxOffset, TxData, TxMetrics, Option<ReducerName>)>> {\n        Ok(Some(tx.commit()))\n    }\n}\n\nimpl Locking {\n    pub fn rollback_mut_tx_downgrade(&self, tx: MutTxId, workload: Workload) -> (TxMetrics, TxId) {\n        tx.rollback_downgrade(workload)\n    }\n\n    /// This method only updates the in-memory `committed_state`.\n    /// For durability, see `RelationalDB::commit_tx_downgrade`.\n    pub fn commit_mut_tx_downgrade(&self, tx: MutTxId, workload: Workload) -> (TxData, TxMetrics, TxId) {\n        tx.commit_downgrade(workload)\n    }\n}\n\n#[derive(Debug, Error)]\npub enum ReplayError {\n    #[error(\"Expected tx offset {expected}, encountered {encountered}\")]\n    InvalidOffset { expected: u64, encountered: u64 },\n    #[error(transparent)]\n    Decode(#[from] bsatn::DecodeError),\n    #[error(transparent)]\n    Db(#[from] DatastoreError),\n    #[error(transparent)]\n    Any(#[from] anyhow::Error),\n}\n\n/// A [`spacetimedb_commitlog::Decoder`] suitable for replaying a transaction\n/// history into the database state.\npub struct Replay<F> {\n    database_identity: Identity,\n    committed_state: Arc<RwLock<CommittedState>>,\n    progress: RefCell<F>,\n    error_behavior: ErrorBehavior,\n}\n\nimpl<F> Replay<F> {\n    fn using_visitor<T>(&self, f: impl FnOnce(&mut ReplayVisitor<'_, F>) -> T) -> T {\n        let mut committed_state = self.committed_state.write();\n        let mut visitor = ReplayVisitor {\n            database_identity: &self.database_identity,\n            committed_state: &mut committed_state,\n            progress: &mut *self.progress.borrow_mut(),\n            dropped_table_names: IntMap::default(),\n            error_behavior: self.error_behavior,\n        };\n        f(&mut visitor)\n    }\n\n    pub fn next_tx_offset(&self) -> u64 {\n        self.committed_state.read_arc().next_tx_offset\n    }\n\n    pub fn committed_state(&self) -> RwLockReadGuard<'_, CommittedState> {\n        self.committed_state.read()\n    }\n}\n\nimpl<F: FnMut(u64)> spacetimedb_commitlog::Decoder for &mut Replay<F> {\n    type Record = txdata::Txdata<ProductValue>;\n    type Error = txdata::DecoderError<ReplayError>;\n\n    fn decode_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        tx_offset: u64,\n        reader: &mut R,\n    ) -> std::result::Result<Self::Record, Self::Error> {\n        self.using_visitor(|visitor| txdata::decode_record_fn(visitor, version, tx_offset, reader))\n    }\n\n    fn consume_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        tx_offset: u64,\n        reader: &mut R,\n    ) -> std::result::Result<(), Self::Error> {\n        self.using_visitor(|visitor| txdata::consume_record_fn(visitor, version, tx_offset, reader))\n    }\n\n    fn skip_record<'a, R: BufReader<'a>>(\n        &self,\n        version: u8,\n        _tx_offset: u64,\n        reader: &mut R,\n    ) -> std::result::Result<(), Self::Error> {\n        self.using_visitor(|visitor| txdata::skip_record_fn(visitor, version, reader))\n    }\n}\n\n// n.b. (Tyler) We actually **do not** want to check constraints at replay\n// time because not only is it a pain, but actually **subtly wrong** the\n// way we have it implemented. It's wrong because the actual constraints of\n// the database may change as different transactions are added to the\n// schema and you would actually have to change your indexes and\n// constraints as you replayed the log. This we are not currently doing\n// (we're building all the non-bootstrapped indexes at the end after\n// replaying), and thus aren't implementing constraint checking correctly\n// as it stands.\n//\n// However, the above is all rendered moot anyway because we don't need to\n// check constraints while replaying if we just assume that they were all\n// checked prior to the transaction committing in the first place.\n//\n// Note also that operation/mutation ordering **does not** matter for\n// operations inside a transaction of the message log assuming we only ever\n// insert **OR** delete a unique row in one transaction. If we ever insert\n// **AND** delete then order **does** matter. The issue caused by checking\n// constraints for each operation while replaying does not imply that order\n// matters. Ordering of operations would **only** matter if you wanted to\n// view the state of the database as of a partially applied transaction. We\n// never actually want to do this, because after a transaction has been\n// committed, it is assumed that all operations happen instantaneously and\n// atomically at the timestamp of the transaction. The only time that we\n// actually want to view the state of a database while a transaction is\n// partially applied is while the transaction is running **before** it\n// commits. Thus, we only care about operation ordering while the\n// transaction is running, but we do not care about it at all in the\n// context of the commit log.\n//\n// Not caring about the order in the log, however, requires that we **do\n// not** check index constraints during replay of transaction operations.\n// We **could** check them in between transactions if we wanted to update\n// the indexes and constraints as they changed during replay, but that is\n// unnecessary.\n\n/// What to do when encountering an error during commitlog replay due to an invalid TX.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub enum ErrorBehavior {\n    /// Return an error and refuse to continue.\n    ///\n    /// This is the behavior in production, as we don't want to reconstruct an incorrect state.\n    FailFast,\n    /// Log a warning and continue replay.\n    ///\n    /// This behavior is used when inspecting broken commitlogs during debugging.\n    Warn,\n}\n\nstruct ReplayVisitor<'a, F> {\n    database_identity: &'a Identity,\n    committed_state: &'a mut CommittedState,\n    progress: &'a mut F,\n    // Since deletes are handled before truncation / drop, sometimes the schema\n    // info is gone. We save the name on the first delete of that table so metrics\n    // can still show a name.\n    dropped_table_names: IntMap<TableId, TableName>,\n    error_behavior: ErrorBehavior,\n}\n\nimpl<F> ReplayVisitor<'_, F> {\n    /// Process `err` according to `self.error_behavior`,\n    /// either warning about it or returning it.\n    ///\n    /// If this method returns an `Err`, the caller should bubble up that error with `?`.\n    fn process_error(&self, err: ReplayError) -> std::result::Result<(), ReplayError> {\n        match self.error_behavior {\n            ErrorBehavior::FailFast => Err(err),\n            ErrorBehavior::Warn => {\n                log::warn!(\"{err:?}\");\n                Ok(())\n            }\n        }\n    }\n}\n\nimpl<F: FnMut(u64)> spacetimedb_commitlog::payload::txdata::Visitor for ReplayVisitor<'_, F> {\n    type Error = ReplayError;\n    // NOTE: Technically, this could be `()` if and when we can extract the\n    // row data without going through `ProductValue` (PV).\n    // To accommodate auxiliary traversals (e.g. for analytics), we may want to\n    // provide a separate visitor yielding PVs.\n    type Row = ProductValue;\n\n    fn skip_row<'a, R: BufReader<'a>>(\n        &mut self,\n        table_id: TableId,\n        reader: &mut R,\n    ) -> std::result::Result<(), Self::Error> {\n        let schema = self.committed_state.schema_for_table(table_id)?;\n        ProductValue::decode(schema.get_row_type(), reader)?;\n        Ok(())\n    }\n\n    fn visit_insert<'a, R: BufReader<'a>>(\n        &mut self,\n        table_id: TableId,\n        reader: &mut R,\n    ) -> std::result::Result<Self::Row, Self::Error> {\n        let schema = self.committed_state.schema_for_table(table_id)?;\n        let row = ProductValue::decode(schema.get_row_type(), reader)?;\n\n        if let Err(e) = self\n            .committed_state\n            .replay_insert(table_id, &schema, &row)\n            .with_context(|| {\n                format!(\n                    \"Error inserting row {:?} during transaction {:?} playback\",\n                    row, self.committed_state.next_tx_offset\n                )\n            })\n        {\n            self.process_error(e.into())?;\n        }\n        // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer,\n        // and therefore has performance implications and must not be disabled.\n        DB_METRICS\n            .rdb_num_table_rows\n            .with_label_values(self.database_identity, &table_id.into(), &schema.table_name)\n            .inc();\n\n        Ok(row)\n    }\n\n    fn visit_delete<'a, R: BufReader<'a>>(\n        &mut self,\n        table_id: TableId,\n        reader: &mut R,\n    ) -> std::result::Result<Self::Row, Self::Error> {\n        let schema = self.committed_state.schema_for_table(table_id)?;\n        // TODO: avoid clone\n        let table_name = schema.table_name.clone();\n        let row = ProductValue::decode(schema.get_row_type(), reader)?;\n\n        // If this is a delete from the `st_table` system table, save the name\n        if table_id == ST_TABLE_ID {\n            let ab = AlgebraicValue::Product(row.clone());\n            let st_table_row = StTableRow::deserialize(ValueDeserializer::from_ref(&ab)).unwrap();\n            self.dropped_table_names\n                .insert(st_table_row.table_id, st_table_row.table_name);\n        }\n\n        if let Err(e) = self\n            .committed_state\n            .replay_delete_by_rel(table_id, &row)\n            .with_context(|| {\n                format!(\n                    \"Error deleting row {:?} from table {:?} during transaction {:?} playback\",\n                    row, table_name, self.committed_state.next_tx_offset\n                )\n            })\n        {\n            self.process_error(e.into())?;\n        }\n        // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer,\n        // and therefore has performance implications and must not be disabled.\n        DB_METRICS\n            .rdb_num_table_rows\n            .with_label_values(self.database_identity, &table_id.into(), &table_name)\n            .dec();\n\n        Ok(row)\n    }\n\n    fn visit_truncate(&mut self, table_id: TableId) -> std::result::Result<(), Self::Error> {\n        let table_name = match self.committed_state.schema_for_table(table_id) {\n            // TODO: avoid clone\n            Ok(schema) => schema.table_name.clone(),\n\n            Err(_) => match self.dropped_table_names.remove(&table_id) {\n                Some(name) => name,\n                _ => {\n                    return self\n                        .process_error(anyhow!(\"Error looking up name for truncated table {table_id:?}\").into());\n                }\n            },\n        };\n\n        if let Err(e) = self.committed_state.replay_truncate(table_id).with_context(|| {\n            format!(\n                \"Error truncating table {:?} during transaction {:?} playback\",\n                table_id, self.committed_state.next_tx_offset\n            )\n        }) {\n            self.process_error(e.into())?;\n        }\n\n        // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer,\n        // and therefore has performance implications and must not be disabled.\n        DB_METRICS\n            .rdb_num_table_rows\n            .with_label_values(self.database_identity, &table_id.into(), &table_name)\n            .set(0);\n\n        Ok(())\n    }\n\n    fn visit_tx_start(&mut self, offset: u64) -> std::result::Result<(), Self::Error> {\n        // The first transaction in a history must have the same offset as the\n        // committed state.\n        //\n        // (Technically, the history should guarantee that all subsequent\n        // transaction offsets are contiguous, but we don't currently have a\n        // good way to only check the first transaction.)\n        //\n        // Note that this is not a panic, because the starting offset can be\n        // chosen at runtime.\n        if offset != self.committed_state.next_tx_offset {\n            return Err(ReplayError::InvalidOffset {\n                expected: self.committed_state.next_tx_offset,\n                encountered: offset,\n            });\n        }\n        (self.progress)(offset);\n\n        Ok(())\n    }\n\n    fn visit_tx_end(&mut self) -> std::result::Result<(), Self::Error> {\n        self.committed_state.replay_end_tx().map_err(Into::into)\n    }\n}\n\n/// Construct a [`Metadata`] from the given [`RowRef`],\n/// reading only the columns necessary to construct the value.\nfn metadata_from_row(row: RowRef<'_>) -> Result<Metadata> {\n    Ok(Metadata {\n        database_identity: read_identity_from_col(row, StModuleFields::DatabaseIdentity)?,\n        owner_identity: read_identity_from_col(row, StModuleFields::OwnerIdentity)?,\n        program_hash: read_hash_from_col(row, StModuleFields::ProgramHash)?,\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::error::IndexError;\n    use crate::locking_tx_datastore::tx_state::PendingSchemaChange;\n    use crate::system_tables::{\n        system_tables, StColumnRow, StConnectionCredentialsFields, StConstraintData, StConstraintFields,\n        StConstraintRow, StEventTableFields, StIndexAlgorithm, StIndexFields, StIndexRow, StRowLevelSecurityFields,\n        StScheduledFields, StSequenceFields, StSequenceRow, StTableRow, StVarFields, StViewArgFields, StViewFields,\n        ST_CLIENT_ID, ST_CLIENT_NAME, ST_COLUMN_ACCESSOR_ID, ST_COLUMN_ACCESSOR_NAME, ST_COLUMN_ID, ST_COLUMN_NAME,\n        ST_CONNECTION_CREDENTIALS_ID, ST_CONNECTION_CREDENTIALS_NAME, ST_CONSTRAINT_ID, ST_CONSTRAINT_NAME,\n        ST_EVENT_TABLE_ID, ST_EVENT_TABLE_NAME, ST_INDEX_ACCESSOR_ID, ST_INDEX_ACCESSOR_NAME, ST_INDEX_ID,\n        ST_INDEX_NAME, ST_MODULE_NAME, ST_RESERVED_SEQUENCE_RANGE, ST_ROW_LEVEL_SECURITY_ID,\n        ST_ROW_LEVEL_SECURITY_NAME, ST_SCHEDULED_ID, ST_SCHEDULED_NAME, ST_SEQUENCE_ID, ST_SEQUENCE_NAME,\n        ST_TABLE_ACCESSOR_ID, ST_TABLE_ACCESSOR_NAME, ST_TABLE_NAME, ST_VAR_ID, ST_VAR_NAME, ST_VIEW_ARG_ID,\n        ST_VIEW_ARG_NAME, ST_VIEW_COLUMN_ID, ST_VIEW_COLUMN_NAME, ST_VIEW_ID, ST_VIEW_NAME, ST_VIEW_PARAM_ID,\n        ST_VIEW_PARAM_NAME, ST_VIEW_SUB_ID, ST_VIEW_SUB_NAME,\n    };\n    use crate::traits::{IsolationLevel, MutTx};\n    use crate::Result;\n    use bsatn::to_vec;\n    use core::{fmt, mem};\n    use itertools::Itertools;\n    use pretty_assertions::{assert_eq, assert_matches};\n    use spacetimedb_execution::dml::MutDatastore as _;\n    use spacetimedb_execution::Datastore;\n    use spacetimedb_lib::db::auth::{StAccess, StTableType};\n    use spacetimedb_lib::error::ResultTest;\n    use spacetimedb_lib::st_var::StVarValue;\n    use spacetimedb_lib::{resolved_type_via_v9, ScheduleAt, TimeDuration};\n    use spacetimedb_primitives::{col_list, ArgId, ColId, ScheduleId, ViewId};\n    use spacetimedb_sats::algebraic_value::ser::value_serialize;\n    use spacetimedb_sats::bsatn::ToBsatn;\n    use spacetimedb_sats::layout::RowTypeLayout;\n    use spacetimedb_sats::raw_identifier::RawIdentifier;\n    use spacetimedb_sats::{product, AlgebraicType, GroundSpacetimeType, SumTypeVariant, SumValue};\n    use spacetimedb_schema::def::BTreeAlgorithm;\n    use spacetimedb_schema::identifier::Identifier;\n    use spacetimedb_schema::schema::{\n        columns_to_row_type, ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, ScheduleSchema,\n        SequenceSchema,\n    };\n    use spacetimedb_schema::table_name::TableName;\n\n    /// For the first user-created table, sequences in the system tables start\n    /// from this value.\n    /// N.B. This used to be one higher (before 1.4) because of how we treated `allocated`.\n    const FIRST_NON_SYSTEM_ID: u32 = ST_RESERVED_SEQUENCE_RANGE;\n\n    /// Utility to query the system tables and return their concrete table row\n    pub struct SystemTableQuery<'a> {\n        db: &'a MutTxId,\n    }\n\n    fn query_st_tables(tx: &MutTxId) -> SystemTableQuery<'_> {\n        SystemTableQuery { db: tx }\n    }\n\n    impl SystemTableQuery<'_> {\n        pub fn scan_st_tables(&self) -> Result<Vec<StTableRow>> {\n            Ok(self\n                .db\n                .iter(ST_TABLE_ID)?\n                .map(|row| StTableRow::try_from(row).unwrap())\n                .sorted_by_key(|x| x.table_id)\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_tables_by_col(\n            &self,\n            cols: impl Into<ColList>,\n            value: &AlgebraicValue,\n        ) -> Result<Vec<StTableRow>> {\n            Ok(self\n                .db\n                .iter_by_col_eq(ST_TABLE_ID, cols.into(), value)?\n                .map(|row| StTableRow::try_from(row).unwrap())\n                .sorted_by_key(|x| x.table_id)\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_columns(&self) -> Result<Vec<StColumnRow>> {\n            Ok(self\n                .db\n                .iter(ST_COLUMN_ID)?\n                .map(|row| StColumnRow::try_from(row).unwrap())\n                .sorted_by_key(|x| (x.table_id, x.col_pos))\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_columns_by_col(\n            &self,\n            cols: impl Into<ColList>,\n            value: &AlgebraicValue,\n        ) -> Result<Vec<StColumnRow>> {\n            Ok(self\n                .db\n                .iter_by_col_eq(ST_COLUMN_ID, cols.into(), value)?\n                .map(|row| StColumnRow::try_from(row).unwrap())\n                .sorted_by_key(|x| (x.table_id, x.col_pos))\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_constraints(&self) -> Result<Vec<StConstraintRow>> {\n            Ok(self\n                .db\n                .iter(ST_CONSTRAINT_ID)?\n                .map(|row| StConstraintRow::try_from(row).unwrap())\n                .sorted_by_key(|x| x.constraint_id)\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_sequences(&self) -> Result<Vec<StSequenceRow>> {\n            Ok(self\n                .db\n                .iter(ST_SEQUENCE_ID)?\n                .map(|row| StSequenceRow::try_from(row).unwrap())\n                .sorted_by_key(|x| (x.table_id, x.sequence_id))\n                .collect::<Vec<_>>())\n        }\n\n        pub fn scan_st_indexes(&self) -> Result<Vec<StIndexRow>> {\n            Ok(self\n                .db\n                .iter(ST_INDEX_ID)?\n                .map(|row| StIndexRow::try_from(row).unwrap())\n                .sorted_by_key(|x| x.index_id)\n                .collect::<Vec<_>>())\n        }\n    }\n\n    fn u32_str_u32(a: u32, b: &str, c: u32) -> ProductValue {\n        product![a, b, c]\n    }\n\n    fn get_datastore() -> Result<Locking> {\n        Locking::bootstrap(Identity::ZERO, PagePool::new_for_test())\n    }\n\n    fn col(col: u16) -> ColList {\n        col.into()\n    }\n\n    fn map_array<A, B: From<A>, const N: usize>(a: [A; N]) -> Vec<B> {\n        map_array_fn(a, Into::into)\n    }\n\n    fn map_array_fn<A, B, F: Fn(A) -> B, const N: usize>(a: [A; N], f: F) -> Vec<B> {\n        a.map(f).into()\n    }\n\n    struct IndexRow<'a> {\n        id: u32,\n        table: u32,\n        col: ColList,\n        name: &'a str,\n    }\n    impl From<IndexRow<'_>> for StIndexRow {\n        fn from(value: IndexRow<'_>) -> Self {\n            Self {\n                index_id: value.id.into(),\n                table_id: value.table.into(),\n                index_name: RawIdentifier::new(value.name),\n                index_algorithm: StIndexAlgorithm::BTree { columns: value.col },\n            }\n        }\n    }\n    impl From<IndexRow<'_>> for IndexSchema {\n        fn from(value: IndexRow<'_>) -> Self {\n            let st = StIndexRow::from(value);\n            st.into()\n        }\n    }\n\n    struct TableRow<'a> {\n        id: u32,\n        name: &'a str,\n        ty: StTableType,\n        access: StAccess,\n        primary_key: Option<ColId>,\n    }\n    impl From<TableRow<'_>> for StTableRow {\n        fn from(value: TableRow<'_>) -> Self {\n            Self {\n                table_id: value.id.into(),\n                table_name: TableName::for_test(value.name),\n                table_type: value.ty,\n                table_access: value.access,\n                table_primary_key: value.primary_key.map(ColList::new),\n            }\n        }\n    }\n\n    struct ColRow<'a> {\n        table: u32,\n        pos: u16,\n        name: &'a str,\n        ty: AlgebraicType,\n    }\n    impl From<ColRow<'_>> for StColumnRow {\n        fn from(value: ColRow<'_>) -> Self {\n            Self {\n                table_id: value.table.into(),\n                col_pos: value.pos.into(),\n                col_name: Identifier::for_test(value.name),\n                col_type: value.ty.into(),\n            }\n        }\n    }\n    impl From<ColRow<'_>> for ColumnSchema {\n        fn from(value: ColRow<'_>) -> Self {\n            Self {\n                table_id: value.table.into(),\n                col_pos: value.pos.into(),\n                col_name: Identifier::for_test(value.name),\n                col_type: value.ty,\n                alias: None,\n            }\n        }\n    }\n\n    struct SequenceRow<'a> {\n        id: u32,\n        name: &'a str,\n        table: u32,\n        col_pos: u16,\n        start: i128,\n    }\n    impl From<SequenceRow<'_>> for StSequenceRow {\n        fn from(value: SequenceRow<'_>) -> Self {\n            Self {\n                sequence_id: value.id.into(),\n                sequence_name: RawIdentifier::new(value.name),\n                table_id: value.table.into(),\n                col_pos: value.col_pos.into(),\n                increment: 1,\n                start: value.start,\n                min_value: 1,\n                max_value: i128::MAX,\n                allocated: value.start,\n            }\n        }\n    }\n\n    impl From<SequenceRow<'_>> for SequenceSchema {\n        fn from(value: SequenceRow<'_>) -> Self {\n            Self {\n                sequence_id: value.id.into(),\n                sequence_name: RawIdentifier::new(value.name),\n                table_id: value.table.into(),\n                col_pos: value.col_pos.into(),\n                increment: 1,\n                start: value.start,\n                min_value: 1,\n                max_value: i128::MAX,\n            }\n        }\n    }\n\n    struct ConstraintRow<'a> {\n        constraint_id: u32,\n        constraint_name: &'a str,\n        table_id: u32,\n        unique_columns: ColList,\n    }\n    impl From<ConstraintRow<'_>> for StConstraintRow {\n        fn from(value: ConstraintRow<'_>) -> Self {\n            Self {\n                constraint_id: value.constraint_id.into(),\n                constraint_name: RawIdentifier::new(value.constraint_name),\n                table_id: value.table_id.into(),\n                constraint_data: StConstraintData::Unique {\n                    columns: value.unique_columns.into(),\n                },\n            }\n        }\n    }\n\n    impl From<ConstraintRow<'_>> for ConstraintSchema {\n        fn from(value: ConstraintRow<'_>) -> Self {\n            let st = StConstraintRow::from(value);\n            st.into()\n        }\n    }\n\n    fn begin_tx(datastore: &Locking) -> TxId {\n        datastore.begin_tx(Workload::ForTests)\n    }\n\n    fn begin_mut_tx(datastore: &Locking) -> MutTxId {\n        datastore.begin_mut_tx(IsolationLevel::Serializable, Workload::ForTests)\n    }\n\n    fn commit(datastore: &Locking, tx: MutTxId) -> ResultTest<TxData> {\n        let (_, tx_data, _, _) = datastore.commit_mut_tx(tx)?.expect(\"commit should produce `TxData`\");\n        Ok(tx_data)\n    }\n\n    #[rustfmt::skip]\n    fn basic_table_schema_cols() -> [ColRow<'static>; 3] {\n        let table = FIRST_NON_SYSTEM_ID;\n        [\n            ColRow { table, pos: 0, name: \"id\", ty: AlgebraicType::U32 },\n            ColRow { table, pos: 1, name: \"name\", ty: AlgebraicType::String },\n            ColRow { table, pos: 2, name: \"age\", ty: AlgebraicType::U32 },\n        ]\n    }\n\n    fn basic_indices() -> Vec<IndexSchema> {\n        vec![\n            IndexSchema::for_test(\"Foo_id_idx_btree\", BTreeAlgorithm::from(0)),\n            IndexSchema::for_test(\"Foo_name_idx_btree\", BTreeAlgorithm::from(1)),\n        ]\n    }\n\n    fn extract_index_id(datastore: &Locking, tx: &MutTxId, index: &IndexSchema) -> ResultTest<IndexId> {\n        let index_id = datastore.index_id_from_name_mut_tx(tx, &index.index_name)?;\n        Ok(index_id.expect(\"the index should exist\"))\n    }\n\n    fn basic_constraints() -> Vec<ConstraintSchema> {\n        vec![\n            ConstraintSchema::unique_for_test(\"Foo_id_key\", 0),\n            ConstraintSchema::unique_for_test(\"Foo_name_key\", 1),\n        ]\n    }\n\n    fn user_public_table(\n        cols: impl Into<Vec<ColumnSchema>>,\n        indices: impl Into<Vec<IndexSchema>>,\n        constraints: impl Into<Vec<ConstraintSchema>>,\n        seqs: impl Into<Vec<SequenceSchema>>,\n        schedule: Option<ScheduleSchema>,\n        pk: Option<ColId>,\n    ) -> TableSchema {\n        TableSchema::new(\n            TableId::SENTINEL,\n            TableName::for_test(\"Foo\"),\n            None,\n            cols.into(),\n            indices.into(),\n            constraints.into(),\n            seqs.into(),\n            StTableType::User,\n            StAccess::Public,\n            schedule,\n            pk,\n            false,\n            None,\n        )\n    }\n\n    fn basic_table_schema_with_indices(\n        indices: impl Into<Vec<IndexSchema>>,\n        constraints: impl Into<Vec<ConstraintSchema>>,\n    ) -> TableSchema {\n        let seq = SequenceSchema {\n            sequence_id: SequenceId::SENTINEL,\n            table_id: TableId::SENTINEL,\n            col_pos: 0.into(),\n            sequence_name: \"Foo_id_seq\".into(),\n            start: 1,\n            increment: 1,\n            min_value: 1,\n            max_value: i128::MAX,\n        };\n        user_public_table(\n            map_array(basic_table_schema_cols()),\n            indices,\n            constraints,\n            vec![seq],\n            None,\n            None,\n        )\n    }\n\n    #[rustfmt::skip]\n    fn basic_table_schema_created() -> TableSchema {\n        let table = TableId::SENTINEL.into();\n        let seq_start = FIRST_NON_SYSTEM_ID;\n\n        user_public_table(\n            map_array(basic_table_schema_cols()),\n             map_array([\n                IndexRow { id: seq_start,     table, col: ColList::new(0.into()), name: \"Foo_id_idx_btree\", },\n                IndexRow { id: seq_start + 1, table, col: ColList::new(1.into()), name: \"Foo_name_idx_btree\", },\n            ]),\n            map_array([\n                ConstraintRow { constraint_id: seq_start,     table_id: table, unique_columns: col(0), constraint_name: \"Foo_id_key\" },\n                ConstraintRow { constraint_id: seq_start + 1, table_id: table, unique_columns: col(1), constraint_name: \"Foo_name_key\" }\n            ]),\n             map_array([\n                SequenceRow { id: seq_start, table, col_pos: 0, name: \"Foo_id_seq\", start: 1 }\n            ]),\n            None,\n            None\n        )\n    }\n\n    fn setup_table_with_indices(\n        indices: impl Into<Vec<IndexSchema>>,\n        constraints: impl Into<Vec<ConstraintSchema>>,\n    ) -> ResultTest<(Locking, MutTxId, TableId)> {\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        let schema = basic_table_schema_with_indices(indices, constraints);\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        Ok((datastore, tx, table_id))\n    }\n\n    fn setup_table() -> ResultTest<(Locking, MutTxId, TableId)> {\n        setup_table_with_indices(basic_indices(), basic_constraints())\n    }\n\n    fn random_row() -> ProductValue {\n        u32_str_u32(42, \"foo\", 24)\n    }\n\n    fn all_rows(datastore: &Locking, tx: &MutTxId, table_id: TableId) -> Vec<ProductValue> {\n        datastore\n            .iter_mut_tx(tx, table_id)\n            .unwrap()\n            .map(|r| r.to_product_value().clone())\n            .collect()\n    }\n\n    fn all_rows_tx(tx: &TxId, table_id: TableId) -> Vec<ProductValue> {\n        tx.iter(table_id)\n            .unwrap()\n            .map(|r| r.to_product_value().clone())\n            .collect()\n    }\n\n    fn insert<'a>(\n        datastore: &'a Locking,\n        tx: &'a mut MutTxId,\n        table_id: TableId,\n        row: &ProductValue,\n    ) -> Result<(AlgebraicValue, RowRef<'a>)> {\n        let row = to_vec(&row).unwrap();\n        let (gen_cols, row_ref, _) = datastore.insert_mut_tx(tx, table_id, &row)?;\n        let gen_cols = row_ref.project(&gen_cols)?;\n        Ok((gen_cols, row_ref))\n    }\n\n    fn update<'a>(\n        datastore: &'a Locking,\n        tx: &'a mut MutTxId,\n        table_id: TableId,\n        index_id: IndexId,\n        row: &ProductValue,\n    ) -> Result<(AlgebraicValue, RowRef<'a>)> {\n        let row = to_vec(&row).unwrap();\n        let (gen_cols, row_ref, _) = datastore.update_mut_tx(tx, table_id, index_id, &row)?;\n        let gen_cols = row_ref.project(&gen_cols)?;\n        Ok((gen_cols, row_ref))\n    }\n\n    #[test]\n    fn test_bootstrapping_sets_up_tables() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n        let tx = begin_mut_tx(&datastore);\n        let query = query_st_tables(&tx);\n        #[rustfmt::skip]\n        assert_eq!(query.scan_st_tables()?, map_array([\n            TableRow { id: ST_TABLE_ID.into(), name: ST_TABLE_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StTableFields::TableId.into()) },\n            TableRow { id: ST_COLUMN_ID.into(), name: ST_COLUMN_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_SEQUENCE_ID.into(), name: ST_SEQUENCE_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StSequenceFields::SequenceId.into()) },\n            TableRow { id: ST_INDEX_ID.into(), name: ST_INDEX_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StIndexFields::IndexId.into()) },\n            TableRow { id: ST_CONSTRAINT_ID.into(), name: ST_CONSTRAINT_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StConstraintFields::ConstraintId.into()) },\n            TableRow { id: ST_MODULE_ID.into(), name: ST_MODULE_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_CLIENT_ID.into(), name: ST_CLIENT_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_VAR_ID.into(), name: ST_VAR_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StVarFields::Name.into()) },\n            TableRow { id: ST_SCHEDULED_ID.into(), name: ST_SCHEDULED_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StScheduledFields::ScheduleId.into()) },\n            TableRow { id: ST_ROW_LEVEL_SECURITY_ID.into(), name: ST_ROW_LEVEL_SECURITY_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StRowLevelSecurityFields::Sql.into()) },\n            TableRow { id: ST_CONNECTION_CREDENTIALS_ID.into(), name: ST_CONNECTION_CREDENTIALS_NAME, ty: StTableType::System, access: StAccess::Private, primary_key: Some(StConnectionCredentialsFields::ConnectionId.into()) },\n            TableRow { id: ST_VIEW_ID.into(), name: ST_VIEW_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StViewFields::ViewId.into()) },\n            TableRow { id: ST_VIEW_PARAM_ID.into(), name: ST_VIEW_PARAM_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_VIEW_COLUMN_ID.into(), name: ST_VIEW_COLUMN_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_VIEW_SUB_ID.into(), name: ST_VIEW_SUB_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_VIEW_ARG_ID.into(), name: ST_VIEW_ARG_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StViewArgFields::Id.into()) },\n            TableRow { id: ST_EVENT_TABLE_ID.into(), name: ST_EVENT_TABLE_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: Some(StEventTableFields::TableId.into()) },\n            TableRow { id: ST_TABLE_ACCESSOR_ID.into(), name: ST_TABLE_ACCESSOR_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_INDEX_ACCESSOR_ID.into(), name: ST_INDEX_ACCESSOR_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n            TableRow { id: ST_COLUMN_ACCESSOR_ID.into(), name: ST_COLUMN_ACCESSOR_NAME, ty: StTableType::System, access: StAccess::Public, primary_key: None },\n\n        ]));\n        #[rustfmt::skip]\n        assert_eq!(query.scan_st_columns()?, map_array([\n            ColRow { table: ST_TABLE_ID.into(), pos: 0, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_TABLE_ID.into(), pos: 1, name: \"table_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_TABLE_ID.into(), pos: 2, name: \"table_type\", ty: AlgebraicType::String },\n            ColRow { table: ST_TABLE_ID.into(), pos: 3, name: \"table_access\", ty: AlgebraicType::String },\n            ColRow { table: ST_TABLE_ID.into(), pos: 4, name: \"table_primary_key\", ty: AlgebraicType::option(resolved_type_via_v9::<ColList>()) },\n\n            ColRow { table: ST_COLUMN_ID.into(), pos: 0, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_COLUMN_ID.into(), pos: 1, name: \"col_pos\", ty: ColId::get_type() },\n            ColRow { table: ST_COLUMN_ID.into(), pos: 2, name: \"col_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_COLUMN_ID.into(), pos: 3, name: \"col_type\", ty: AlgebraicType::bytes() },\n\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 0, name: \"sequence_id\", ty: SequenceId::get_type() },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 1, name: \"sequence_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 2, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 3, name: \"col_pos\", ty: ColId::get_type() },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 4, name: \"increment\", ty: AlgebraicType::I128 },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 5, name: \"start\", ty: AlgebraicType::I128 },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 6, name: \"min_value\", ty: AlgebraicType::I128 },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 7, name: \"max_value\", ty: AlgebraicType::I128 },\n            ColRow { table: ST_SEQUENCE_ID.into(), pos: 8, name: \"allocated\", ty: AlgebraicType::I128 },\n\n            ColRow { table: ST_INDEX_ID.into(), pos: 0, name: \"index_id\", ty: IndexId::get_type() },\n            ColRow { table: ST_INDEX_ID.into(), pos: 1, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_INDEX_ID.into(), pos: 2, name: \"index_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_INDEX_ID.into(), pos: 3, name: \"index_algorithm\", ty: resolved_type_via_v9::<StIndexAlgorithm>() },\n\n            ColRow { table: ST_CONSTRAINT_ID.into(), pos: 0, name: \"constraint_id\", ty: ConstraintId::get_type() },\n            ColRow { table: ST_CONSTRAINT_ID.into(), pos: 1, name: \"constraint_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_CONSTRAINT_ID.into(), pos: 2, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_CONSTRAINT_ID.into(), pos: 3, name: \"constraint_data\", ty: resolved_type_via_v9::<StConstraintData>() },\n\n            ColRow { table: ST_MODULE_ID.into(), pos: 0, name: \"database_identity\", ty: AlgebraicType::U256 },\n            ColRow { table: ST_MODULE_ID.into(), pos: 1, name: \"owner_identity\", ty: AlgebraicType::U256 },\n            ColRow { table: ST_MODULE_ID.into(), pos: 2, name: \"program_kind\", ty: AlgebraicType::U8 },\n            ColRow { table: ST_MODULE_ID.into(), pos: 3, name: \"program_hash\", ty: AlgebraicType::U256 },\n            ColRow { table: ST_MODULE_ID.into(), pos: 4, name: \"program_bytes\", ty: AlgebraicType::bytes() },\n            ColRow { table: ST_MODULE_ID.into(), pos: 5, name: \"module_version\", ty: AlgebraicType::String },\n\n            ColRow { table: ST_CLIENT_ID.into(), pos: 0, name: \"identity\", ty: AlgebraicType::U256},\n            ColRow { table: ST_CLIENT_ID.into(), pos: 1, name: \"connection_id\", ty: AlgebraicType::U128},\n\n            ColRow { table: ST_VAR_ID.into(), pos: 0, name: \"name\", ty: AlgebraicType::String },\n            ColRow { table: ST_VAR_ID.into(), pos: 1, name: \"value\", ty: resolved_type_via_v9::<StVarValue>() },\n\n            ColRow { table: ST_SCHEDULED_ID.into(), pos: 0, name: \"schedule_id\", ty: ScheduleId::get_type() },\n            ColRow { table: ST_SCHEDULED_ID.into(), pos: 1, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_SCHEDULED_ID.into(), pos: 2, name: \"reducer_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_SCHEDULED_ID.into(), pos: 3, name: \"schedule_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_SCHEDULED_ID.into(), pos: 4, name: \"at_column\", ty: AlgebraicType::U16 },\n\n            ColRow { table: ST_ROW_LEVEL_SECURITY_ID.into(), pos: 0, name: \"table_id\", ty: TableId::get_type() },\n            ColRow { table: ST_ROW_LEVEL_SECURITY_ID.into(), pos: 1, name: \"sql\", ty: AlgebraicType::String },\n\n            ColRow { table: ST_CONNECTION_CREDENTIALS_ID.into(), pos: 0, name: \"connection_id\", ty: AlgebraicType::U128 },\n            ColRow { table: ST_CONNECTION_CREDENTIALS_ID.into(), pos: 1, name: \"jwt_payload\", ty: AlgebraicType::String },\n\n            ColRow { table: ST_VIEW_ID.into(), pos: 0, name: \"view_id\", ty: ViewId::get_type() },\n            ColRow { table: ST_VIEW_ID.into(), pos: 1, name: \"view_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_VIEW_ID.into(), pos: 2, name: \"table_id\", ty: AlgebraicType::option(TableId::get_type()) },\n            ColRow { table: ST_VIEW_ID.into(), pos: 3, name: \"is_public\", ty: AlgebraicType::Bool },\n            ColRow { table: ST_VIEW_ID.into(), pos: 4, name: \"is_anonymous\", ty: AlgebraicType::Bool },\n\n            ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 0, name: \"view_id\", ty: ViewId::get_type() },\n            ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 1, name: \"param_pos\", ty: ColId::get_type() },\n            ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 2, name: \"param_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_VIEW_PARAM_ID.into(), pos: 3, name: \"param_type\", ty: AlgebraicType::bytes() },\n\n            ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 0, name: \"view_id\", ty: ViewId::get_type() },\n            ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 1, name: \"col_pos\", ty: ColId::get_type() },\n            ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 2, name: \"col_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_VIEW_COLUMN_ID.into(), pos: 3, name: \"col_type\", ty: AlgebraicType::bytes() },\n\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 0, name: \"view_id\", ty: ViewId::get_type() },\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 1, name: \"arg_id\", ty: ArgId::get_type() },\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 2, name: \"identity\", ty: AlgebraicType::U256 },\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 3, name: \"num_subscribers\", ty: AlgebraicType::U64 },\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 4, name: \"has_subscribers\", ty: AlgebraicType::Bool },\n            ColRow { table: ST_VIEW_SUB_ID.into(), pos: 5, name: \"last_called\", ty: AlgebraicType::I64 },\n\n            ColRow { table: ST_VIEW_ARG_ID.into(), pos: 0, name: \"id\", ty: AlgebraicType::U64 },\n            ColRow { table: ST_VIEW_ARG_ID.into(), pos: 1, name: \"bytes\", ty: AlgebraicType::bytes() },\n\n            ColRow { table: ST_EVENT_TABLE_ID.into(), pos: 0, name: \"table_id\", ty: TableId::get_type() },\n\n            ColRow { table: ST_TABLE_ACCESSOR_ID.into(), pos: 0, name: \"table_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_TABLE_ACCESSOR_ID.into(), pos: 1, name: \"accessor_name\", ty: AlgebraicType::String },\n\n            ColRow { table: ST_INDEX_ACCESSOR_ID.into(), pos: 0, name: \"index_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_INDEX_ACCESSOR_ID.into(), pos: 1, name: \"accessor_name\", ty: AlgebraicType::String },\n\n            ColRow { table: ST_COLUMN_ACCESSOR_ID.into(), pos: 0, name: \"table_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_COLUMN_ACCESSOR_ID.into(), pos: 1, name: \"col_name\", ty: AlgebraicType::String },\n            ColRow { table: ST_COLUMN_ACCESSOR_ID.into(), pos: 2, name: \"accessor_name\", ty: AlgebraicType::String },\n        ]));\n        #[rustfmt::skip]\n        assert_eq!(query.scan_st_indexes()?, map_array([\n            IndexRow { id: 1, table: ST_TABLE_ID.into(), col: col(0), name: \"st_table_table_id_idx_btree\", },\n            IndexRow { id: 2, table: ST_TABLE_ID.into(), col: col(1), name: \"st_table_table_name_idx_btree\", },\n            IndexRow { id: 3, table: ST_COLUMN_ID.into(), col: col_list![0, 1], name: \"st_column_table_id_col_pos_idx_btree\", },\n            IndexRow { id: 4, table: ST_SEQUENCE_ID.into(), col: col(0), name: \"st_sequence_sequence_id_idx_btree\", },\n            IndexRow { id: 5, table: ST_INDEX_ID.into(), col: col(0), name: \"st_index_index_id_idx_btree\", },\n            IndexRow { id: 6, table: ST_CONSTRAINT_ID.into(), col: col(0), name: \"st_constraint_constraint_id_idx_btree\", },\n            IndexRow { id: 7, table: ST_CLIENT_ID.into(), col: col_list![0, 1], name: \"st_client_identity_connection_id_idx_btree\", },\n            IndexRow { id: 8, table: ST_VAR_ID.into(), col: col(0), name: \"st_var_name_idx_btree\", },\n            IndexRow { id: 9, table: ST_SCHEDULED_ID.into(), col: col(0), name: \"st_scheduled_schedule_id_idx_btree\", },\n            IndexRow { id: 10, table: ST_SCHEDULED_ID.into(), col: col(1), name: \"st_scheduled_table_id_idx_btree\", },\n            IndexRow { id: 11, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(0), name: \"st_row_level_security_table_id_idx_btree\", },\n            IndexRow { id: 12, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(1), name: \"st_row_level_security_sql_idx_btree\", },\n            IndexRow { id: 13, table: ST_CONNECTION_CREDENTIALS_ID.into(), col: col(0), name: \"st_connection_credentials_connection_id_idx_btree\", },\n            IndexRow { id: 14, table: ST_VIEW_ID.into(), col: col(0), name: \"st_view_view_id_idx_btree\", },\n            IndexRow { id: 15, table: ST_VIEW_ID.into(), col: col(1), name: \"st_view_view_name_idx_btree\", },\n            IndexRow { id: 16, table: ST_VIEW_PARAM_ID.into(), col: col_list![0, 1], name: \"st_view_param_view_id_param_pos_idx_btree\", },\n            IndexRow { id: 17, table: ST_VIEW_COLUMN_ID.into(), col: col_list![0, 1], name: \"st_view_column_view_id_col_pos_idx_btree\", },\n            IndexRow { id: 18, table: ST_VIEW_SUB_ID.into(), col: col(2), name: \"st_view_sub_identity_idx_btree\", },\n            IndexRow { id: 19, table: ST_VIEW_SUB_ID.into(), col: col(4), name: \"st_view_sub_has_subscribers_idx_btree\", },\n            IndexRow { id: 20, table: ST_VIEW_SUB_ID.into(), col: col_list![0, 1, 2], name: \"st_view_sub_view_id_arg_id_identity_idx_btree\", },\n            IndexRow { id: 21, table: ST_VIEW_ARG_ID.into(), col: col(0), name: \"st_view_arg_id_idx_btree\", },\n            IndexRow { id: 22, table: ST_VIEW_ARG_ID.into(), col: col(1), name: \"st_view_arg_bytes_idx_btree\", },\n            IndexRow { id: 23, table: ST_EVENT_TABLE_ID.into(), col: col(0), name: \"st_event_table_table_id_idx_btree\", },\n            IndexRow { id: 24, table: ST_TABLE_ACCESSOR_ID.into(), col: col(0), name: \"st_table_accessor_table_name_idx_btree\", },\n            IndexRow { id: 25, table: ST_TABLE_ACCESSOR_ID.into(), col: col(1), name: \"st_table_accessor_accessor_name_idx_btree\", },\n            IndexRow { id: 26, table: ST_INDEX_ACCESSOR_ID.into(), col: col(0), name: \"st_index_accessor_index_name_idx_btree\", },\n            IndexRow { id: 27, table: ST_INDEX_ACCESSOR_ID.into(), col: col(1), name: \"st_index_accessor_accessor_name_idx_btree\", },\n            IndexRow { id: 28, table: ST_COLUMN_ACCESSOR_ID.into(), col: col_list![0, 1], name: \"st_column_accessor_table_name_col_name_idx_btree\", },\n            IndexRow { id: 29, table: ST_COLUMN_ACCESSOR_ID.into(), col: col_list![0, 2], name: \"st_column_accessor_table_name_accessor_name_idx_btree\", },\n        ]));\n        let start = ST_RESERVED_SEQUENCE_RANGE as i128 + 1;\n        #[rustfmt::skip]\n        assert_eq!(query.scan_st_sequences()?, map_array_fn(\n            [\n                SequenceRow { id: 1, table: ST_TABLE_ID.into(), col_pos: 0, name: \"st_table_table_id_seq\", start },\n                SequenceRow { id: 5, table: ST_SEQUENCE_ID.into(), col_pos: 0, name: \"st_sequence_sequence_id_seq\", start },\n                SequenceRow { id: 2, table: ST_INDEX_ID.into(), col_pos: 0, name: \"st_index_index_id_seq\", start },\n                SequenceRow { id: 3, table: ST_CONSTRAINT_ID.into(), col_pos: 0, name: \"st_constraint_constraint_id_seq\", start },\n                SequenceRow { id: 4, table: ST_SCHEDULED_ID.into(), col_pos: 0, name: \"st_scheduled_schedule_id_seq\", start },\n                SequenceRow { id: 6, table: ST_VIEW_ID.into(), col_pos: 0, name: \"st_view_view_id_seq\", start },\n                SequenceRow { id: 7, table: ST_VIEW_ARG_ID.into(), col_pos: 0, name: \"st_view_arg_id_seq\", start },\n            ],\n            |row| StSequenceRow {\n                allocated: start - 1,\n                ..StSequenceRow::from(row)\n            }\n        ));\n        #[rustfmt::skip]\n        assert_eq!(query.scan_st_constraints()?, map_array([\n            ConstraintRow { constraint_id: 1, table_id: ST_TABLE_ID.into(), unique_columns: col(0), constraint_name: \"st_table_table_id_key\", },\n            ConstraintRow { constraint_id: 2, table_id: ST_TABLE_ID.into(), unique_columns: col(1), constraint_name: \"st_table_table_name_key\", },\n            ConstraintRow { constraint_id: 3, table_id: ST_COLUMN_ID.into(), unique_columns: col_list![0, 1], constraint_name: \"st_column_table_id_col_pos_key\", },\n            ConstraintRow { constraint_id: 4, table_id: ST_SEQUENCE_ID.into(), unique_columns: col(0), constraint_name: \"st_sequence_sequence_id_key\", },\n            ConstraintRow { constraint_id: 5, table_id: ST_INDEX_ID.into(), unique_columns: col(0), constraint_name: \"st_index_index_id_key\", },\n            ConstraintRow { constraint_id: 6, table_id: ST_CONSTRAINT_ID.into(), unique_columns: col(0), constraint_name: \"st_constraint_constraint_id_key\", },\n            ConstraintRow { constraint_id: 7, table_id: ST_CLIENT_ID.into(), unique_columns: col_list![0, 1], constraint_name: \"st_client_identity_connection_id_key\", },\n            ConstraintRow { constraint_id: 8, table_id: ST_VAR_ID.into(), unique_columns: col(0), constraint_name: \"st_var_name_key\", },\n            ConstraintRow { constraint_id: 9, table_id: ST_SCHEDULED_ID.into(), unique_columns: col(0), constraint_name: \"st_scheduled_schedule_id_key\", },\n            ConstraintRow { constraint_id: 10, table_id: ST_SCHEDULED_ID.into(), unique_columns: col(1), constraint_name: \"st_scheduled_table_id_key\", },\n            ConstraintRow { constraint_id: 11, table_id: ST_ROW_LEVEL_SECURITY_ID.into(), unique_columns: col(1), constraint_name: \"st_row_level_security_sql_key\", },\n            ConstraintRow { constraint_id: 12, table_id: ST_CONNECTION_CREDENTIALS_ID.into(), unique_columns: col(0), constraint_name: \"st_connection_credentials_connection_id_key\", },\n            ConstraintRow { constraint_id: 13, table_id: ST_VIEW_ID.into(), unique_columns: col(0), constraint_name: \"st_view_view_id_key\", },\n            ConstraintRow { constraint_id: 14, table_id: ST_VIEW_ID.into(), unique_columns: col(1), constraint_name: \"st_view_view_name_key\", },\n            ConstraintRow { constraint_id: 15, table_id: ST_VIEW_PARAM_ID.into(), unique_columns: col_list![0, 1], constraint_name: \"st_view_param_view_id_param_pos_key\", },\n            ConstraintRow { constraint_id: 16, table_id: ST_VIEW_COLUMN_ID.into(), unique_columns: col_list![0, 1], constraint_name: \"st_view_column_view_id_col_pos_key\", },\n            ConstraintRow { constraint_id: 17, table_id: ST_VIEW_ARG_ID.into(), unique_columns: col(0), constraint_name: \"st_view_arg_id_key\", },\n            ConstraintRow { constraint_id: 18, table_id: ST_VIEW_ARG_ID.into(), unique_columns: col(1), constraint_name: \"st_view_arg_bytes_key\", },\n            ConstraintRow { constraint_id: 19, table_id: ST_EVENT_TABLE_ID.into(), unique_columns: col(0), constraint_name: \"st_event_table_table_id_key\", },\n            ConstraintRow { constraint_id: 20, table_id: ST_TABLE_ACCESSOR_ID.into(), unique_columns: col(0), constraint_name: \"st_table_accessor_table_name_key\", },\n            ConstraintRow { constraint_id: 21, table_id: ST_TABLE_ACCESSOR_ID.into(), unique_columns: col(1), constraint_name: \"st_table_accessor_accessor_name_key\", },\n            ConstraintRow { constraint_id: 22, table_id: ST_INDEX_ACCESSOR_ID.into(), unique_columns: col(0), constraint_name: \"st_index_accessor_index_name_key\", },\n            ConstraintRow { constraint_id: 23, table_id: ST_INDEX_ACCESSOR_ID.into(), unique_columns: col(1), constraint_name: \"st_index_accessor_accessor_name_key\", },\n            ConstraintRow { constraint_id: 24, table_id: ST_COLUMN_ACCESSOR_ID.into(), unique_columns: col_list![0, 1], constraint_name: \"st_column_accessor_table_name_col_name_key\", },\n            ConstraintRow { constraint_id: 25, table_id: ST_COLUMN_ACCESSOR_ID.into(), unique_columns: col_list![0, 2], constraint_name: \"st_column_accessor_table_name_accessor_name_key\", },\n            ]));\n\n        // Verify we get back the tables correctly with the proper ids...\n        let cols = query.scan_st_columns()?;\n        let idx = query.scan_st_indexes()?;\n        let seq = query.scan_st_sequences()?;\n        let ct = query.scan_st_constraints()?;\n\n        for st in system_tables() {\n            let schema = datastore.schema_for_table_mut_tx(&tx, st.table_id).unwrap();\n            assert_eq!(\n                schema.columns().to_vec(),\n                cols.iter()\n                    .filter(|x| x.table_id == st.table_id)\n                    .cloned()\n                    .map(Into::into)\n                    .collect::<Vec<_>>(),\n                \"Columns for {}\",\n                schema.table_name\n            );\n\n            assert_eq!(\n                schema\n                    .indexes\n                    .clone()\n                    .into_iter()\n                    .sorted_by_key(|x| x.index_id)\n                    .collect::<Vec<_>>(),\n                idx.iter()\n                    .sorted_by_key(|x| x.index_id)\n                    .filter(|x| x.table_id == st.table_id)\n                    .cloned()\n                    .map(Into::into)\n                    .collect::<Vec<_>>(),\n                \"Indexes for {}\",\n                schema.table_name\n            );\n\n            assert_eq!(\n                schema\n                    .sequences\n                    .clone()\n                    .into_iter()\n                    .sorted_by_key(|x| x.sequence_id)\n                    .collect::<Vec<_>>(),\n                seq.iter()\n                    .sorted_by_key(|x| x.sequence_id)\n                    .filter(|x| x.table_id == st.table_id)\n                    .cloned()\n                    .map(Into::into)\n                    .collect::<Vec<_>>(),\n                \"Sequences for {}\",\n                schema.table_name\n            );\n\n            assert_eq!(\n                schema\n                    .constraints\n                    .clone()\n                    .into_iter()\n                    .sorted_by_key(|x| x.constraint_id)\n                    .collect::<Vec<_>>(),\n                ct.iter()\n                    .sorted_by_key(|x| x.constraint_id)\n                    .filter(|x| x.table_id == st.table_id)\n                    .cloned()\n                    .map(Into::into)\n                    .collect::<Vec<_>>(),\n                \"Constraints for {}\",\n                schema.table_name\n            );\n        }\n\n        let _ = datastore.rollback_mut_tx(tx);\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_pre_commit() -> ResultTest<()> {\n        let (_, tx, table_id) = setup_table()?;\n        let query = query_st_tables(&tx);\n\n        let table_rows = query.scan_st_tables_by_col(ColId(0), &table_id.into())?;\n        #[rustfmt::skip]\n        assert_eq!(table_rows, map_array([\n            TableRow { id: FIRST_NON_SYSTEM_ID, name: \"Foo\", ty: StTableType::User, access: StAccess::Public, primary_key: None }\n        ]));\n        let column_rows = query.scan_st_columns_by_col(ColId(0), &table_id.into())?;\n        #[rustfmt::skip]\n        assert_eq!(column_rows, map_array(basic_table_schema_cols()));\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_post_commit() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        datastore.commit_mut_tx(tx)?;\n        let tx = begin_mut_tx(&datastore);\n        let query = query_st_tables(&tx);\n\n        let table_rows = query.scan_st_tables_by_col(ColId(0), &table_id.into())?;\n        #[rustfmt::skip]\n        assert_eq!(table_rows, map_array([\n            TableRow { id: FIRST_NON_SYSTEM_ID, name: \"Foo\", ty: StTableType::User, access: StAccess::Public, primary_key: None }\n        ]));\n        let column_rows = query.scan_st_columns_by_col(ColId(0), &table_id.into())?;\n        #[rustfmt::skip]\n        assert_eq!(column_rows, map_array(basic_table_schema_cols()));\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_post_rollback() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        let _ = datastore.rollback_mut_tx(tx);\n        let tx = begin_mut_tx(&datastore);\n        assert!(\n            !datastore.table_id_exists_mut_tx(&tx, &table_id),\n            \"Table should not exist\"\n        );\n        let query = query_st_tables(&tx);\n\n        let table_rows = query.scan_st_tables_by_col(ColId(0), &table_id.into())?;\n        assert_eq!(table_rows, []);\n        let column_rows = query.scan_st_columns_by_col(ColId(0), &table_id.into())?;\n        assert_eq!(column_rows, []);\n        Ok(())\n    }\n\n    fn verify_schemas_consistent(tx: &mut MutTxId, table_id: TableId) {\n        let s1 = tx.get_schema(table_id).expect(\"should exist\");\n        let s2 = tx.schema_for_table_raw(table_id).expect(\"should exist\");\n        assert_eq!(**s1, s2);\n    }\n\n    #[test]\n    fn test_schema_for_table_pre_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let schema = &*datastore.schema_for_table_mut_tx(&tx, table_id)?;\n\n        verify_schemas_consistent(&mut tx, table_id);\n\n        let mut expected_schema = basic_table_schema_created();\n        expected_schema.update_table_id(table_id);\n        assert_eq!(schema, &expected_schema);\n        Ok(())\n    }\n\n    #[test]\n    fn test_schema_for_table_post_commit() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        datastore.commit_mut_tx(tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        verify_schemas_consistent(&mut tx, table_id);\n        let schema = &*datastore.schema_for_table_mut_tx(&tx, table_id)?;\n        let mut expected_schema = basic_table_schema_created();\n        expected_schema.update_table_id(table_id);\n        assert_eq!(schema, &expected_schema);\n        Ok(())\n    }\n\n    #[test]\n    fn test_schema_for_table_alter_indexes() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        datastore.commit_mut_tx(tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let schema = datastore.schema_for_table_mut_tx(&tx, table_id)?;\n\n        assert_eq!(tx.pending_schema_changes(), []);\n        let mut dropped_indexes = 0;\n        for (pos, index) in schema.indexes.iter().enumerate() {\n            datastore.drop_index_mut_tx(&mut tx, index.index_id)?;\n            dropped_indexes += 1;\n\n            let psc = &tx.pending_schema_changes()[pos];\n            let PendingSchemaChange::IndexRemoved(tid, iid, _, schema) = psc else {\n                panic!(\"wrong pending schema change: {psc:?}\");\n            };\n            assert_eq!(table_id, *tid);\n            assert_eq!(index.index_id, *iid);\n            assert_eq!(index, schema);\n        }\n        assert!(\n            datastore.schema_for_table_mut_tx(&tx, table_id)?.indexes.is_empty(),\n            \"no indexes should be left in the schema pre-commit\"\n        );\n        datastore.commit_mut_tx(tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert!(\n            datastore.schema_for_table_mut_tx(&tx, table_id)?.indexes.is_empty(),\n            \"no indexes should be left in the schema post-commit\"\n        );\n\n        verify_schemas_consistent(&mut tx, table_id);\n\n        datastore.create_index_mut_tx(\n            &mut tx,\n            IndexSchema {\n                index_id: IndexId::SENTINEL,\n                table_id,\n                index_name: \"Foo_id_idx_btree\".into(),\n                index_algorithm: BTreeAlgorithm::from(0).into(),\n                alias: None,\n            },\n            true,\n        )?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::IndexAdded(tid, _, Some(_))]\n            if *tid == table_id\n        );\n\n        verify_schemas_consistent(&mut tx, table_id);\n\n        let expected_indexes = [IndexRow {\n            id: FIRST_NON_SYSTEM_ID + dropped_indexes,\n            table: FIRST_NON_SYSTEM_ID,\n            col: col_list![0],\n            name: \"Foo_id_idx_btree\",\n        }]\n        .map(Into::into);\n        assert_eq!(\n            datastore.schema_for_table_mut_tx(&tx, table_id)?.indexes,\n            expected_indexes,\n            \"created index should be present in schema pre-commit\"\n        );\n\n        datastore.commit_mut_tx(tx)?;\n\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert_eq!(\n            datastore.schema_for_table_mut_tx(&tx, table_id)?.indexes,\n            expected_indexes,\n            \"created index should be present in schema post-commit\"\n        );\n\n        datastore.commit_mut_tx(tx)?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_schema_for_table_rollback() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        assert_eq!(tx.pending_schema_changes().len(), 6);\n        let _ = datastore.rollback_mut_tx(tx);\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        let schema = datastore.schema_for_table_mut_tx(&tx, table_id);\n        assert!(schema.is_err());\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_pre_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_wrong_schema_pre_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = product!(0, \"Foo\");\n        assert!(insert(&datastore, &mut tx, table_id, &row).is_err());\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_post_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &u32_str_u32(0, \"Foo\", 18))?;\n        datastore.commit_mut_tx(tx)?;\n        let tx = begin_mut_tx(&datastore);\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_post_rollback() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        let row = u32_str_u32(15, \"Foo\", 18); // 15 is ignored.\n        datastore.commit_mut_tx(tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        insert(&datastore, &mut tx, table_id, &row)?;\n        let _ = datastore.rollback_mut_tx(tx);\n        let tx = begin_mut_tx(&datastore);\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_commit_delete_insert() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        datastore.commit_mut_tx(tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        let created_row = u32_str_u32(1, \"Foo\", 18);\n        let num_deleted = datastore.delete_by_rel_mut_tx(&mut tx, table_id, [created_row]);\n        assert_eq!(num_deleted, 1);\n        assert_eq!(all_rows(&datastore, &tx, table_id).len(), 0);\n        let created_row = u32_str_u32(1, \"Foo\", 19);\n        insert(&datastore, &mut tx, table_id, &created_row)?;\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 19)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_insert_delete_insert_delete_insert() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(1, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        for i in 0..2 {\n            assert_eq!(\n                all_rows(&datastore, &tx, table_id),\n                vec![row.clone()],\n                \"Found unexpected set of rows before deleting\",\n            );\n            let num_deleted = datastore.delete_by_rel_mut_tx(&mut tx, table_id, [row.clone()]);\n            assert_eq!(\n                num_deleted, 1,\n                \"delete_by_rel deleted an unexpected number of rows on iter {i}\",\n            );\n            assert_eq!(\n                &all_rows(&datastore, &tx, table_id),\n                &[],\n                \"Found rows present after deleting\",\n            );\n            insert(&datastore, &mut tx, table_id, &row)?;\n            assert_eq!(\n                all_rows(&datastore, &tx, table_id),\n                vec![row.clone()],\n                \"Found unexpected set of rows after inserting\",\n            );\n        }\n        Ok(())\n    }\n\n    #[test]\n    fn test_unique_constraint_pre_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        let result = insert(&datastore, &mut tx, table_id, &row);\n        match result {\n            Err(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => (),\n            _ => panic!(\"Expected an unique constraint violation error.\"),\n        }\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_unique_constraint_post_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        datastore.commit_mut_tx(tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        let result = insert(&datastore, &mut tx, table_id, &row);\n        match result {\n            Err(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => (),\n            _ => panic!(\"Expected an unique constraint violation error.\"),\n        }\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_unique_constraint_post_rollback() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        commit(&datastore, tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        let _ = datastore.rollback_mut_tx(tx);\n        let mut tx = begin_mut_tx(&datastore);\n        insert(&datastore, &mut tx, table_id, &row)?;\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(2, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    fn assert_st_indices(tx: &MutTxId, include_age: bool) -> ResultTest<()> {\n        let seq_start = FIRST_NON_SYSTEM_ID;\n        #[rustfmt::skip]\n        let mut known_index_rows = [\n            IndexRow { id: 1, table: ST_TABLE_ID.into(), col: col(0), name: \"st_table_table_id_idx_btree\", },\n            IndexRow { id: 2, table: ST_TABLE_ID.into(), col: col(1), name: \"st_table_table_name_idx_btree\", },\n            IndexRow { id: 3, table: ST_COLUMN_ID.into(), col: col_list![0, 1], name: \"st_column_table_id_col_pos_idx_btree\", },\n            IndexRow { id: 4, table: ST_SEQUENCE_ID.into(), col: col(0), name: \"st_sequence_sequence_id_idx_btree\", },\n            IndexRow { id: 5, table: ST_INDEX_ID.into(), col: col(0), name: \"st_index_index_id_idx_btree\", },\n            IndexRow { id: 6, table: ST_CONSTRAINT_ID.into(), col: col(0), name: \"st_constraint_constraint_id_idx_btree\", },\n            IndexRow { id: 7, table: ST_CLIENT_ID.into(), col: col_list![0, 1], name: \"st_client_identity_connection_id_idx_btree\", },\n            IndexRow { id: 8, table: ST_VAR_ID.into(), col: col(0), name: \"st_var_name_idx_btree\", },\n            IndexRow { id: 9, table: ST_SCHEDULED_ID.into(), col: col(0), name: \"st_scheduled_schedule_id_idx_btree\", },\n            IndexRow { id: 10, table: ST_SCHEDULED_ID.into(), col: col(1), name: \"st_scheduled_table_id_idx_btree\", },\n            IndexRow { id: 11, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(0), name: \"st_row_level_security_table_id_idx_btree\", },\n            IndexRow { id: 12, table: ST_ROW_LEVEL_SECURITY_ID.into(), col: col(1), name: \"st_row_level_security_sql_idx_btree\", },\n            IndexRow { id: 13, table: ST_CONNECTION_CREDENTIALS_ID.into(), col: col(0), name: \"st_connection_credentials_connection_id_idx_btree\", },\n            IndexRow { id: 14, table: ST_VIEW_ID.into(), col: col(0), name: \"st_view_view_id_idx_btree\", },\n            IndexRow { id: 15, table: ST_VIEW_ID.into(), col: col(1), name: \"st_view_view_name_idx_btree\", },\n            IndexRow { id: 16, table: ST_VIEW_PARAM_ID.into(), col: col_list![0, 1], name: \"st_view_param_view_id_param_pos_idx_btree\", },\n            IndexRow { id: 17, table: ST_VIEW_COLUMN_ID.into(), col: col_list![0, 1], name: \"st_view_column_view_id_col_pos_idx_btree\", },\n            IndexRow { id: 18, table: ST_VIEW_SUB_ID.into(), col: col(2), name: \"st_view_sub_identity_idx_btree\", },\n            IndexRow { id: 19, table: ST_VIEW_SUB_ID.into(), col: col(4), name: \"st_view_sub_has_subscribers_idx_btree\", },\n            IndexRow { id: 20, table: ST_VIEW_SUB_ID.into(), col: col_list![0, 1, 2], name: \"st_view_sub_view_id_arg_id_identity_idx_btree\", },\n            IndexRow { id: 21, table: ST_VIEW_ARG_ID.into(), col: col(0), name: \"st_view_arg_id_idx_btree\", },\n            IndexRow { id: 22, table: ST_VIEW_ARG_ID.into(), col: col(1), name: \"st_view_arg_bytes_idx_btree\", },\n            IndexRow { id: 23, table: ST_EVENT_TABLE_ID.into(), col: col(0), name: \"st_event_table_table_id_idx_btree\", },\n            IndexRow { id: 24, table: ST_TABLE_ACCESSOR_ID.into(), col: col(0), name: \"st_table_accessor_table_name_idx_btree\", },\n            IndexRow { id: 25, table: ST_TABLE_ACCESSOR_ID.into(), col: col(1), name: \"st_table_accessor_accessor_name_idx_btree\", },\n            IndexRow { id: 26, table: ST_INDEX_ACCESSOR_ID.into(), col: col(0), name: \"st_index_accessor_index_name_idx_btree\", },\n            IndexRow { id: 27, table: ST_INDEX_ACCESSOR_ID.into(), col: col(1), name: \"st_index_accessor_accessor_name_idx_btree\", },\n            IndexRow { id: 28, table: ST_COLUMN_ACCESSOR_ID.into(), col: col_list![0, 1], name: \"st_column_accessor_table_name_col_name_idx_btree\", },\n            IndexRow { id: 29, table: ST_COLUMN_ACCESSOR_ID.into(), col: col_list![0, 2], name: \"st_column_accessor_table_name_accessor_name_idx_btree\", },\n            IndexRow { id: seq_start,     table: FIRST_NON_SYSTEM_ID, col: col(0), name: \"Foo_id_idx_btree\",  },\n            IndexRow { id: seq_start + 1, table: FIRST_NON_SYSTEM_ID, col: col(1), name: \"Foo_name_idx_btree\",  },\n            IndexRow { id: seq_start + 2, table: FIRST_NON_SYSTEM_ID, col: col(2), name: \"Foo_age_idx_btree\",  },\n        ].map(Into::into).to_vec();\n\n        if !include_age {\n            known_index_rows.pop();\n        }\n\n        let query = query_st_tables(tx);\n        let index_rows = query.scan_st_indexes()?;\n        assert_eq!(index_rows, known_index_rows);\n        Ok(())\n    }\n\n    fn create_foo_age_idx_btree(datastore: &Locking, tx: &mut MutTxId, table_id: TableId) -> ResultTest<()> {\n        let index_def = IndexSchema {\n            index_id: IndexId::SENTINEL,\n            table_id,\n            index_name: \"Foo_age_idx_btree\".into(),\n            index_algorithm: BTreeAlgorithm::from(2).into(),\n            alias: None,\n        };\n        // TODO: it's slightly incorrect to create an index with `is_unique: true` without creating a corresponding constraint.\n        // But the `Table` crate allows it for now.\n        datastore.create_index_mut_tx(tx, index_def, true)?;\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_index_ignores_deleted_committed_rows() -> ResultTest<()> {\n        // Setup a table, insert a row, and commit.\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row_to_delete = u32_str_u32(1, \"Foo\", 18); // 1 to avoid autoinc.\n        insert(&datastore, &mut tx, table_id, &row_to_delete)?;\n        commit(&datastore, tx)?;\n\n        // Delete `row` but don't commit.\n        let mut tx = begin_mut_tx(&datastore);\n        assert!(tx.delete_by_row_value(table_id, &row_to_delete)?);\n        // Ensure that deleting again is a no-op.\n        assert!(!tx.delete_by_row_value(table_id, &row_to_delete)?);\n\n        // Insert a row that could conflict with the deleted one.\n        let row_potential_conflict = u32_str_u32(1, \"Bar\", 18);\n        insert(&datastore, &mut tx, table_id, &row_potential_conflict)?;\n\n        // Create the unique index on the last field. This should not error.\n        create_foo_age_idx_btree(&datastore, &mut tx, table_id)?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_index_pre_commit() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        create_foo_age_idx_btree(&datastore, &mut tx, table_id)?;\n        assert_matches!(tx.pending_schema_changes(), [PendingSchemaChange::IndexAdded(.., None)]);\n        assert_st_indices(&tx, true)?;\n        let row = u32_str_u32(0, \"Bar\", 18); // 0 will be ignored.\n        let result = insert(&datastore, &mut tx, table_id, &row);\n        match result {\n            Err(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => (),\n            e => panic!(\"Expected an unique constraint violation error but got {e:?}.\"),\n        }\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_index_post_commit() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        create_foo_age_idx_btree(&datastore, &mut tx, table_id)?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert_st_indices(&tx, true)?;\n        let row = u32_str_u32(0, \"Bar\", 18); // 0 will be ignored.\n        let result = insert(&datastore, &mut tx, table_id, &row);\n        match result {\n            Err(DatastoreError::Index(IndexError::UniqueConstraintViolation(_))) => (),\n            _ => panic!(\"Expected an unique constraint violation error.\"),\n        }\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![u32_str_u32(1, \"Foo\", 18)]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_index_post_rollback() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        datastore.commit_mut_tx(tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        create_foo_age_idx_btree(&datastore, &mut tx, table_id)?;\n\n        let _ = datastore.rollback_mut_tx(tx);\n        let mut tx = begin_mut_tx(&datastore);\n        assert_st_indices(&tx, false)?;\n        let row = u32_str_u32(0, \"Bar\", 18); // 0 will be ignored.\n        insert(&datastore, &mut tx, table_id, &row)?;\n        #[rustfmt::skip]\n        assert_eq!(all_rows(&datastore, &tx, table_id), vec![\n            u32_str_u32(1, \"Foo\", 18),\n            u32_str_u32(2, \"Bar\", 18),\n        ]);\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_drop_sequence_transactionality() -> ResultTest<()> {\n        // Start a transaction. Schema changes empty so far.\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n\n        // Make the table and witness `TableAdded`. Commit.\n        let column = ColumnSchema::for_test(0, \"id\", AlgebraicType::I32);\n        let schema = user_public_table([column], [], [], [], None, None);\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        assert_matches!(tx.pending_schema_changes(), [PendingSchemaChange::TableAdded(..)]);\n        commit(&datastore, tx)?;\n\n        // Start a new tx. Insert a row and witness that a sequence isn't used.\n        let mut tx = begin_mut_tx(&datastore);\n        let zero = product![0];\n        let one = product![1];\n        let insert_assert_and_remove = |tx: &mut MutTxId, ins: &ProductValue, exp: &ProductValue| -> ResultTest<()> {\n            insert(&datastore, tx, table_id, ins)?;\n            assert_eq!(all_rows(&datastore, tx, table_id), std::slice::from_ref(exp));\n            datastore.delete_by_rel_mut_tx(tx, table_id, [exp.clone()]);\n            assert_eq!(all_rows(&datastore, tx, table_id), []);\n            Ok(())\n        };\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n\n        // Add the sequence, and witness that it works.\n        let sequence = SequenceSchema {\n            sequence_id: SequenceId::SENTINEL,\n            table_id,\n            col_pos: 0.into(),\n            sequence_name: \"seq\".into(),\n            start: 1,\n            increment: 1,\n            min_value: 1,\n            max_value: i128::MAX,\n        };\n        let seq_id = datastore.create_sequence_mut_tx(&mut tx, sequence.clone())?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::SequenceAdded(_, added_seq_id)]\n                if *added_seq_id == seq_id\n        );\n        insert_assert_and_remove(&mut tx, &zero, &one)?;\n\n        // Drop the uncommitted sequence.\n        datastore.drop_sequence_mut_tx(&mut tx, seq_id)?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [\n                PendingSchemaChange::SequenceAdded(..),\n                PendingSchemaChange::SequenceRemoved(.., schema),\n            ]\n                if schema.sequence_id == seq_id\n        );\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n\n        // Add the sequence again and rollback, witnessing that this had no effect in the next tx.\n        let seq_id = datastore.create_sequence_mut_tx(&mut tx, sequence.clone())?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [\n                PendingSchemaChange::SequenceAdded(..),\n                PendingSchemaChange::SequenceRemoved(..),\n                PendingSchemaChange::SequenceAdded(_, added_seq_id),\n            ]\n                if *added_seq_id == seq_id\n        );\n        let _ = datastore.rollback_mut_tx(tx);\n        let mut tx: MutTxId = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n\n        // Add the sequence and this time actually commit. Check that it exists in next tx.\n        assert_eq!(tx.pending_schema_changes(), []);\n        let seq_id = datastore.create_sequence_mut_tx(&mut tx, sequence.clone())?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::SequenceAdded(_, added_seq_id)]\n                if *added_seq_id == seq_id\n        );\n        commit(&datastore, tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        insert_assert_and_remove(&mut tx, &zero, &one)?;\n\n        // We have the sequence in committed state.\n        // Drop it and then rollback, so in the next tx the seq is still there.\n        datastore.drop_sequence_mut_tx(&mut tx, seq_id)?;\n        assert_matches!(tx.pending_schema_changes(), [PendingSchemaChange::SequenceRemoved(..)]);\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n        let _ = datastore.rollback_mut_tx(tx);\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        insert_assert_and_remove(&mut tx, &zero, &product![2])?;\n\n        // Drop the seq and commit this time around. In the next tx, we witness that there's no seq.\n        datastore.drop_sequence_mut_tx(&mut tx, seq_id)?;\n        assert_matches!(tx.pending_schema_changes(), [PendingSchemaChange::SequenceRemoved(..)]);\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n        commit(&datastore, tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        insert_assert_and_remove(&mut tx, &zero, &zero)?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_update_reinsert() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert a row and commit the tx.\n        let row = u32_str_u32(0, \"Foo\", 18); // 0 will be ignored.\n                                             // Because of auto_inc columns, we will get a slightly different\n                                             // value than the one we inserted.\n        let row = insert(&datastore, &mut tx, table_id, &row)?.1.to_product_value();\n        datastore.commit_mut_tx(tx)?;\n\n        let all_rows_col_0_eq_1 = |tx: &MutTxId| {\n            datastore\n                .iter_by_col_eq_mut_tx(tx, table_id, ColId(0), &AlgebraicValue::U32(1))\n                .unwrap()\n                .map(|row_ref| row_ref.to_product_value())\n                .collect::<Vec<_>>()\n        };\n\n        // Update the db with the same actual value for that row, in a new tx.\n        let mut tx = begin_mut_tx(&datastore);\n        // Iterate over all rows with the value 1 (from the auto_inc) in column 0.\n        let rows = all_rows_col_0_eq_1(&tx);\n        assert_eq!(rows.len(), 1);\n        assert_eq!(row, rows[0]);\n        // Delete the row.\n        let count_deleted = datastore.delete_by_rel_mut_tx(&mut tx, table_id, rows);\n        assert_eq!(count_deleted, 1);\n\n        // We shouldn't see the row when iterating now that it's deleted.\n        assert_eq!(all_rows_col_0_eq_1(&tx).len(), 0);\n\n        // Reinsert the row.\n        let reinserted_row = insert(&datastore, &mut tx, table_id, &row)?.1.to_product_value();\n        assert_eq!(reinserted_row, row);\n\n        // The actual test: we should be able to iterate again, while still in the\n        // second transaction, and see exactly one row.\n        assert_eq!(all_rows_col_0_eq_1(&tx).len(), 1);\n\n        datastore.commit_mut_tx(tx)?;\n\n        Ok(())\n    }\n\n    fn expect_index_err(res: Result<impl fmt::Debug>) -> IndexError {\n        match res.expect_err(\"`res` should be an error\") {\n            DatastoreError::Index(err) => err,\n            _ => panic!(\"the error should be an `IndexError`\"),\n        }\n    }\n\n    fn test_under_tx_and_commit(\n        datastore: &Locking,\n        mut tx: MutTxId,\n        mut test: impl FnMut(&mut MutTxId) -> ResultTest<()>,\n    ) -> ResultTest<()> {\n        // Test the tx state.\n        test(&mut tx)?;\n\n        // Test the commit state.\n        commit(datastore, tx)?;\n        test(&mut begin_mut_tx(datastore))\n    }\n\n    /// Checks that update validates the row against the row type.\n    #[test]\n    fn test_update_wrong_row_type() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table_with_indices([], [])?;\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            // We provide an index that doesn't exist on purpose.\n            let index_id = 0.into();\n            // Remove the last field of the row, invalidating it.\n            let mut row = Vec::from(random_row().elements);\n            let _ = row.pop().expect(\"there should be an element to remove\");\n            let row = row.into();\n            // Now attempt the update.\n            let err = update(&datastore, tx, table_id, index_id, &row).expect_err(\"the update should fail\");\n\n            assert_matches!(\n                err,\n                DatastoreError::Table(TableError::Bflatn(spacetimedb_table::bflatn_to::Error::Decode(..)))\n            );\n            Ok(())\n        })\n    }\n\n    /// Checks that update checks if the index exists.\n    #[test]\n    fn test_regression_2134() -> ResultTest<()> {\n        // Get us a datastore and tx.\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n\n        // Create the table. The minimal repro is a one column table with a unique constraint.\n        let table_schema = user_public_table(\n            [ColumnSchema::for_test(0, \"id\", AlgebraicType::I32)],\n            [IndexSchema::for_test(\"btree\", BTreeAlgorithm::from(0))],\n            [ConstraintSchema::unique_for_test(\"constraint\", 0)],\n            [],\n            None,\n            None,\n        );\n        let table_id = datastore.create_table_mut_tx(&mut tx, table_schema)?;\n        commit(&datastore, tx)?;\n\n        // A \"reducer\" that deletes and then inserts the same row.\n        let row = &product![1];\n        let update = |datastore| -> ResultTest<_> {\n            let mut tx = begin_mut_tx(datastore);\n            // Delete the row.\n            let deleted = tx.delete_by_row_value(table_id, row)?;\n            // Insert it again.\n            insert(datastore, &mut tx, table_id, row)?;\n            let tx_data = commit(datastore, tx)?;\n            Ok((deleted, tx_data))\n        };\n\n        // In two separate transactions, we update a row to itself.\n        // What should happen is that the row is added to the committed state the first time,\n        // as the delete does nothing.\n        //\n        // The second time however,\n        // the delete should first mark the committed row as deleted in the delete tables,\n        // and then it should remove it from the delete tables upon insertion,\n        // rather than actually inserting it in the tx state.\n        // So the second transaction should be observationally a no-op.\n        // There was a bug in the datastore that did not respect this in the presence of a unique index.\n        let (deleted_1, tx_data_1) = update(&datastore)?;\n        let (deleted_2, tx_data_2) = update(&datastore)?;\n\n        // In the first tx, the row is not deleted, but it is inserted, so we end up with the row committed.\n        assert_eq!(deleted_1, false);\n        let entries_1 = tx_data_1.iter_table_entries().collect_vec();\n        assert_eq!(entries_1.len(), 1);\n        assert_eq!(entries_1[0].0, table_id);\n        assert_eq!(entries_1[0].1.deletes.len(), 0);\n        assert_eq!(entries_1[0].1.inserts, [row.clone()].into());\n\n        // In the second tx, the row is deleted from the commit state,\n        // by marking it in the delete tables.\n        // Then, when inserting, it is un-deleted by un-marking.\n        // This sequence results in an empty tx-data.\n        assert_eq!(deleted_2, true);\n        assert_eq!(tx_data_2.iter_table_entries().count(), 0);\n        Ok(())\n    }\n\n    #[test]\n    fn test_update_brings_back_deleted_commit_row_repro_2296() -> ResultTest<()> {\n        // Get us a datastore and tx.\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n\n        // Create the table. The minimal repro is a two column table with a unique constraint.\n        let table_schema = user_public_table(\n            [0, 1].map(|pos| ColumnSchema::for_test(pos, format!(\"c{pos}\"), AlgebraicType::U32)),\n            [IndexSchema::for_test(\"index\", BTreeAlgorithm::from(0))],\n            [ConstraintSchema::unique_for_test(\"constraint\", 0)],\n            [],\n            None,\n            None,\n        );\n        let table_id = datastore.create_table_mut_tx(&mut tx, table_schema)?;\n        let index_id = datastore.index_id_from_name_mut_tx(&tx, \"index\")?.unwrap();\n        let find_row_by_key = |tx: &MutTxId, key: u32| {\n            let key: AlgebraicValue = key.into();\n            Datastore::index_scan_range(tx, table_id, index_id, &key)\n                .unwrap()\n                .map(|row| row.pointer())\n                .collect::<Vec<_>>()\n        };\n\n        // Insert `Foo { c0: 0, c1: 0 }`.\n        const KEY: u32 = 0;\n        const DATA: u32 = 0;\n        let row = &product![KEY, DATA];\n        let row_prime = &product![KEY, DATA + 1];\n        insert(&datastore, &mut tx, table_id, row)?;\n\n        // It's important for the test that the row is committed.\n        commit(&datastore, tx)?;\n\n        // Start a new transaction where we:\n        let mut tx = begin_mut_tx(&datastore);\n        // 1. delete the row.\n        let row_to_del = find_row_by_key(&tx, KEY);\n        datastore.delete_mut_tx(&mut tx, table_id, row_to_del.iter().copied());\n        // 2. insert a new row with the same key as the one we deleted but different extra field.\n        // We should now have a committed row `row` marked as deleted and a row `row_prime`.\n        // These share `KEY`.\n        insert(&datastore, &mut tx, table_id, row_prime)?;\n        // 3. update `row_prime` -> `row`.\n        // Because `row` exists in the committed state but was marked as deleted,\n        // it should be undeleted, without a new row being added to the tx state.\n        let (_, row_ref) = update(&datastore, &mut tx, table_id, index_id, row)?;\n        assert_eq!([row_ref.pointer()], &*row_to_del);\n        assert_eq!(row_to_del, find_row_by_key(&tx, KEY));\n\n        // Commit the transaction.\n        // We expect the transaction to be a noop.\n        let tx_data = commit(&datastore, tx)?;\n        assert_eq!(tx_data.iter_table_entries().count(), 0);\n        Ok(())\n    }\n\n    #[test]\n    fn test_update_no_such_index() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table_with_indices([], [])?;\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            let index_id = 0.into();\n            let err = expect_index_err(update(&datastore, tx, table_id, index_id, &random_row()));\n            assert_eq!(err, IndexError::NotFound(index_id));\n            Ok(())\n        })\n    }\n\n    /// Checks that update checks if the index exists and that this considers tx-state index deletion.\n    #[test]\n    fn test_update_no_such_index_because_deleted() -> ResultTest<()> {\n        // Setup and immediately commit.\n        let (datastore, tx, table_id) = setup_table()?;\n        assert_eq!(tx.pending_schema_changes().len(), 6);\n        commit(&datastore, tx)?;\n\n        // Remove index in tx state.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        let index_id = extract_index_id(&datastore, &tx, &basic_indices()[0])?;\n        tx.drop_index(index_id)?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::IndexRemoved(tid, iid, _, _)]\n            if *tid == table_id && *iid == index_id\n        );\n\n        test_under_tx_and_commit(&datastore, tx, |tx: &mut _| {\n            let err = expect_index_err(update(&datastore, tx, table_id, index_id, &random_row()));\n            assert_eq!(err, IndexError::NotFound(index_id));\n            Ok(())\n        })\n    }\n\n    /// Checks that update ensures the index is unique.\n    #[test]\n    fn test_update_index_not_unique() -> ResultTest<()> {\n        let indices = basic_indices();\n        let (datastore, mut tx, table_id) = setup_table_with_indices(indices.clone(), [])?;\n        let row = &random_row();\n        insert(&datastore, &mut tx, table_id, row)?;\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            for index in &indices {\n                let index_id = extract_index_id(&datastore, tx, index)?;\n                let err = expect_index_err(update(&datastore, tx, table_id, index_id, row));\n                assert_eq!(err, IndexError::NotUnique(index_id));\n            }\n            Ok(())\n        })\n    }\n\n    /// Checks that update ensures that the row-to-update exists.\n    #[test]\n    fn test_update_no_such_row() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            let row = &random_row();\n            for (index_pos, index) in basic_indices().into_iter().enumerate() {\n                let index_id = extract_index_id(&datastore, tx, &index)?;\n                let err = expect_index_err(update(&datastore, tx, table_id, index_id, row));\n                let needle = row.get_field(index_pos, None).unwrap().clone();\n                assert_eq!(err, IndexError::KeyNotFound(index_id, needle));\n            }\n            Ok(())\n        })\n    }\n\n    /// Checks that update ensures that the row-to-update exists and considers delete tables.\n    #[test]\n    fn test_update_no_such_row_because_deleted() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert the row, commit, and delete it.\n        let row = &random_row();\n        insert(&datastore, &mut tx, table_id, row)?;\n        commit(&datastore, tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(1, datastore.delete_by_rel_mut_tx(&mut tx, table_id, [row.clone()]));\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            for (index_pos, index) in basic_indices().into_iter().enumerate() {\n                let index_id = extract_index_id(&datastore, tx, &index)?;\n                let err = expect_index_err(update(&datastore, tx, table_id, index_id, row));\n                let needle = row.get_field(index_pos, None).unwrap().clone();\n                assert_eq!(err, IndexError::KeyNotFound(index_id, needle));\n            }\n            Ok(())\n        })\n    }\n\n    /// Checks that update ensures that the row-to-update exists and considers delete tables.\n    #[test]\n    fn test_update_no_such_row_because_deleted_new_index_in_tx() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table_with_indices([], [])?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [\n                PendingSchemaChange::TableAdded(_),\n                PendingSchemaChange::SequenceAdded(..),\n            ]\n        );\n\n        // Insert the row and commit.\n        let row = &random_row();\n        insert(&datastore, &mut tx, table_id, row)?;\n        commit(&datastore, tx)?;\n\n        // Now add the indices and then delete the row.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        let mut indices = basic_indices();\n        for (pos, index) in indices.iter_mut().enumerate() {\n            index.table_id = table_id;\n            index.index_id = datastore.create_index_mut_tx(&mut tx, index.clone(), true)?;\n            assert_matches!(\n                &tx.pending_schema_changes()[pos],\n                PendingSchemaChange::IndexAdded(_, iid, _)\n                if *iid == index.index_id\n            );\n        }\n        assert_eq!(1, datastore.delete_by_rel_mut_tx(&mut tx, table_id, [row.clone()]));\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            for (index_pos, index) in indices.iter().enumerate() {\n                let err = expect_index_err(update(&datastore, tx, table_id, index.index_id, row));\n                let needle = row.get_field(index_pos, None).unwrap().clone();\n                assert_eq!(err, IndexError::KeyNotFound(index.index_id, needle));\n            }\n            Ok(())\n        })\n    }\n\n    /// Checks that update ensures that the row-to-update exists and that sequences were used.\n    #[test]\n    fn test_update_no_such_row_seq_triggered() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_table()?;\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            let mut row = random_row();\n            let field_before = mem::replace(&mut row.elements[0], 0u32.into());\n\n            // Use the index on the first u32 field as it's unique auto_inc.\n            let index_id = extract_index_id(&datastore, tx, &basic_indices()[0])?;\n\n            // Attempt the update.\n            let err = expect_index_err(update(&datastore, tx, table_id, index_id, &row));\n            assert_matches!(err, IndexError::KeyNotFound(_, key) if key != field_before);\n            Ok(())\n        })\n    }\n\n    /// Checks that update checks other unique constraints against the committed state.\n    #[test]\n    fn test_update_violates_commit_unique_constraints() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table_with_indices([], [])?;\n\n        // Insert two rows.\n        let mut row = random_row();\n        insert(&datastore, &mut tx, table_id, &row)?;\n        row.elements[0] = 24u32.into();\n        let original_string = mem::replace(&mut row.elements[1], \"bar\".into());\n        insert(&datastore, &mut tx, table_id, &row)?;\n        row.elements[1] = original_string;\n\n        // Add the index on the string field.\n        let mut indices = basic_indices();\n        for index in &mut indices {\n            index.table_id = table_id;\n        }\n        datastore.create_index_mut_tx(&mut tx, indices.swap_remove(1), true)?;\n        // Commit.\n        commit(&datastore, tx)?;\n\n        // *After committing*, add the u32 field index.\n        // We'll use that index to seek whilst changing the second field to the first row we added.\n        let mut tx = begin_mut_tx(&datastore);\n        let index_id = datastore.create_index_mut_tx(&mut tx, indices.swap_remove(0), true)?;\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            // Attempt the update. There should be a unique constraint violation on the string field.\n            let err = expect_index_err(update(&datastore, tx, table_id, index_id, &row));\n            assert_matches!(err, IndexError::UniqueConstraintViolation(_));\n            Ok(())\n        })\n    }\n\n    /// Checks that update checks other unique constraints against the committed state.\n    #[test]\n    fn test_update_violates_tx_unique_constraints() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert two rows.\n        let mut row = random_row();\n        insert(&datastore, &mut tx, table_id, &row)?;\n        row.elements[0] = 24u32.into();\n        let original_string = mem::replace(&mut row.elements[1], \"bar\".into());\n        insert(&datastore, &mut tx, table_id, &row)?;\n        row.elements[1] = original_string;\n\n        // Seek the index on the first u32 field.\n        let index_id = extract_index_id(&datastore, &tx, &basic_indices()[0])?;\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            // Attempt the update. There should be a unique constraint violation on the string field.\n            let err = expect_index_err(update(&datastore, tx, table_id, index_id, &row));\n            assert_matches!(err, IndexError::UniqueConstraintViolation(_));\n            Ok(())\n        })\n    }\n\n    /// Checks that update is idempotent.\n    #[test]\n    fn test_update_idempotent() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert a row.\n        let row = &random_row();\n        insert(&datastore, &mut tx, table_id, row)?;\n        // Seek the index on the first u32 field.\n        let index_id = extract_index_id(&datastore, &tx, &basic_indices()[0])?;\n\n        // Test before commit:\n        let (_, new_row) = update(&datastore, &mut tx, table_id, index_id, row).expect(\"update should have succeeded\");\n        assert_eq!(row, &new_row.to_product_value());\n        // Commit.\n        let tx_data_1 = commit(&datastore, tx)?;\n        let mut tx = begin_mut_tx(&datastore);\n        // Test after commit:\n        let (_, new_row) = update(&datastore, &mut tx, table_id, index_id, row).expect(\"update should have succeeded\");\n        assert_eq!(row, &new_row.to_product_value());\n        let tx_data_2 = commit(&datastore, tx)?;\n        // Ensure that none of the commits deleted rows in our table.\n        for tx_data in [&tx_data_1, &tx_data_2] {\n            assert_eq!(\n                tx_data\n                    .iter_table_entries()\n                    .find(|(tid, e)| *tid == table_id && !e.deletes.is_empty()),\n                None\n            );\n        }\n        // Ensure that the first commit added the row but that the second didn't.\n        for (tx_data, expected_rows) in [(&tx_data_1, vec![row.clone()]), (&tx_data_2, vec![])] {\n            let inserted_rows = tx_data\n                .iter_table_entries()\n                .find(|(tid, _)| *tid == table_id)\n                .map(|(_, e)| e.inserts.to_vec())\n                .unwrap_or_default();\n            assert_eq!(inserted_rows, expected_rows);\n        }\n\n        Ok(())\n    }\n\n    /// Checks that update successfully uses sequences.\n    #[test]\n    fn test_update_uses_sequences() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert a row.\n        let mut row = random_row();\n        row.elements[0] = 0u32.into();\n        insert(&datastore, &mut tx, table_id, &row)?;\n\n        // Seek the index on the string field.\n        let index_id = extract_index_id(&datastore, &tx, &basic_indices()[1])?;\n\n        test_under_tx_and_commit(&datastore, tx, |tx| {\n            let mut row = row.clone();\n            let (seq_val, new_row) =\n                update(&datastore, tx, table_id, index_id, &row).expect(\"update should have succeeded\");\n            let new_row = new_row.to_product_value();\n            assert_eq!(&seq_val, &new_row.elements[0]);\n            row.elements[0] = seq_val;\n            assert_eq!(row, new_row);\n            Ok(())\n        })\n    }\n\n    #[test]\n    /// Test that two read-only TXes can operate concurrently without deadlock or blocking,\n    /// and that both observe correct results for a simple table scan.\n    fn test_read_only_tx_shared_lock() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n        let row1 = u32_str_u32(1, \"Foo\", 18);\n        insert(&datastore, &mut tx, table_id, &row1)?;\n        let row2 = u32_str_u32(2, \"Bar\", 20);\n        insert(&datastore, &mut tx, table_id, &row2)?;\n        datastore.commit_mut_tx(tx)?;\n\n        // create multiple read only tx, and use them together.\n        let read_tx_1 = begin_tx(&datastore);\n        let read_tx_2 = begin_tx(&datastore);\n        let rows = &[row1, row2];\n        assert_eq!(&all_rows_tx(&read_tx_2, table_id), rows);\n        assert_eq!(&all_rows_tx(&read_tx_1, table_id), rows);\n        let _ = read_tx_2.release();\n        let _ = read_tx_1.release();\n        Ok(())\n    }\n\n    #[test]\n    fn test_scheduled_table_insert_and_update() -> ResultTest<()> {\n        // Build the minimal schema that is a valid scheduler table.\n        let schema = user_public_table(\n            [\n                ColumnSchema::for_test(0, \"id\", AlgebraicType::U64),\n                ColumnSchema::for_test(1, \"at\", ScheduleAt::get_type()),\n            ],\n            [IndexSchema::for_test(\"id_idx\", BTreeAlgorithm::from(0))],\n            [ConstraintSchema::unique_for_test(\"id_unique\", 0)],\n            [],\n            Some(ScheduleSchema::for_test(\"schedule\", \"reducer\", 1)),\n            Some(ColId(0)),\n        );\n\n        // Create the table.\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        let index_id = datastore\n            .index_id_from_name_mut_tx(&tx, \"id_idx\")?\n            .expect(\"there should be an index with this name\");\n\n        // Make us a row and insert + identity update.\n        let row = &product![\n            24u64,\n            value_serialize(&ScheduleAt::Interval(TimeDuration::from_micros(42)))\n        ];\n        let row = &to_vec(row).unwrap();\n        let (_, _, insert_flags) = datastore.insert_mut_tx(&mut tx, table_id, row)?;\n        let (_, _, update_flags) = datastore.update_mut_tx(&mut tx, table_id, index_id, row)?;\n\n        // The whole point of the test.\n        assert!(insert_flags.is_scheduler_table);\n        assert!(update_flags.is_scheduler_table);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_row_level_security() -> ResultTest<()> {\n        let (_, mut tx, table_id) = setup_table()?;\n\n        let rls = RowLevelSecuritySchema {\n            sql: \"SELECT * FROM bar\".into(),\n            table_id,\n        };\n        tx.create_row_level_security(rls.clone())?;\n\n        let result = tx.row_level_security_for_table_id(table_id)?;\n        assert_eq!(\n            result,\n            vec![RowLevelSecuritySchema {\n                sql: \"SELECT * FROM bar\".into(),\n                table_id,\n            }]\n        );\n\n        tx.drop_row_level_security(rls.sql)?;\n        assert_eq!(tx.row_level_security_for_table_id(table_id)?, []);\n\n        Ok(())\n    }\n\n    fn create_table(schema: TableSchema) -> ResultTest<(Locking, TableId)> {\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        datastore.commit_mut_tx(tx)?;\n        Ok((datastore, table_id))\n    }\n\n    #[test]\n    fn test_set_semantics() -> ResultTest<()> {\n        // Create a table schema for (a: u8, b: u8)\n        let table_schema = |index: Option<_>, constraint: Option<_>| {\n            user_public_table(\n                [(\"a\", 0), (\"b\", 1)].map(|(name, pos)| ColumnSchema::for_test(pos, name, AlgebraicType::U8)),\n                Vec::from_iter(index),\n                Vec::from_iter(constraint),\n                [],\n                None,\n                None,\n            )\n        };\n        let table_schema_no_constraints = || table_schema(None, None);\n        let table_schema_unique_constraint = || {\n            table_schema(\n                Some(IndexSchema::for_test(\"a_index\", BTreeAlgorithm::from(0))),\n                Some(ConstraintSchema::unique_for_test(\"a_unique\", 0)),\n            )\n        };\n\n        fn insert_rows(datastore: &Locking, rows: Vec<ProductValue>, table_id: TableId) -> ResultTest<()> {\n            let mut tx = begin_mut_tx(datastore);\n            for row in rows {\n                insert(datastore, &mut tx, table_id, &row)?;\n            }\n            commit(datastore, tx)?;\n            Ok(())\n        }\n\n        fn assert_rows(datastore: &Locking, table_id: TableId, rows: Vec<ProductValue>) -> ResultTest<()> {\n            let tx = begin_tx(datastore);\n            for (actual, expected) in datastore.iter_tx(&tx, table_id)?.zip_eq(rows.into_iter()) {\n                assert_eq!(actual.to_bsatn_vec()?, expected.to_bsatn_vec()?);\n            }\n            Ok(())\n        }\n\n        // Assert the datastore implements set semantics when inserting the same row\n        fn assert_set_semantics_for_table(schema: impl Fn() -> TableSchema) -> ResultTest<()> {\n            |(datastore, table_id)| -> ResultTest<()> {\n                insert_rows(\n                    &datastore,\n                    vec![\n                        // Insert one row\n                        product!(1u8, 2u8),\n                    ],\n                    table_id,\n                )?;\n                assert_rows(\n                    &datastore,\n                    table_id,\n                    vec![\n                        // Assert one row\n                        product!(1u8, 2u8),\n                    ],\n                )?;\n                Ok(())\n            }(create_table(schema())?)?;\n\n            |(datastore, table_id)| -> ResultTest<()> {\n                insert_rows(\n                    &datastore,\n                    vec![\n                        // Insert two equal rows in the same tx\n                        product!(1u8, 2u8),\n                        product!(1u8, 2u8),\n                    ],\n                    table_id,\n                )?;\n                assert_rows(\n                    &datastore,\n                    table_id,\n                    vec![\n                        // Assert one row\n                        product!(1u8, 2u8),\n                    ],\n                )?;\n                Ok(())\n            }(create_table(schema())?)?;\n\n            |(datastore, table_id)| -> ResultTest<()> {\n                insert_rows(\n                    &datastore,\n                    vec![\n                        // Insert one row\n                        product!(1u8, 2u8),\n                    ],\n                    table_id,\n                )?;\n                insert_rows(\n                    &datastore,\n                    vec![\n                        // Insert same row in different tx\n                        product!(1u8, 2u8),\n                    ],\n                    table_id,\n                )?;\n                assert_rows(\n                    &datastore,\n                    table_id,\n                    vec![\n                        // Assert one row\n                        product!(1u8, 2u8),\n                    ],\n                )?;\n                Ok(())\n            }(create_table(schema())?)?;\n\n            Ok(())\n        }\n\n        assert_set_semantics_for_table(table_schema_unique_constraint)?;\n        assert_set_semantics_for_table(table_schema_no_constraints)?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn add_twice_and_find_issue_2601() -> ResultTest<()> {\n        let schema = user_public_table(\n            [ColumnSchema::for_test(0, \"field\", AlgebraicType::I32)],\n            [IndexSchema::for_test(\"index\", BTreeAlgorithm::from(0))],\n            [ConstraintSchema::unique_for_test(\"constraint\", 0)],\n            [],\n            None,\n            None,\n        );\n\n        let (datastore, table_id) = create_table(schema)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n\n        let row = &product![42];\n        let (_, first) = insert(&datastore, &mut tx, table_id, row)?;\n        let first = first.pointer();\n        // There was a bug where this insertion caused a removal of the first row.\n        insert(&datastore, &mut tx, table_id, row)?;\n\n        assert_eq!(\n            datastore\n                .iter_by_col_eq_mut_tx(&tx, table_id, 0, &42i32.into())\n                .unwrap()\n                .map(|r| r.pointer())\n                .collect::<Vec<_>>(),\n            [first],\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_drop_table_is_transactional() -> ResultTest<()> {\n        let (datastore, mut tx, table_id) = setup_table()?;\n\n        // Insert a row and commit.\n        let row: ProductValue = random_row();\n        insert(&datastore, &mut tx, table_id, &row)?;\n        commit(&datastore, tx)?;\n\n        // Create a transaction and drop the table and roll back.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert!(datastore.drop_table_mut_tx(&mut tx, table_id).is_ok());\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [\n                PendingSchemaChange::IndexRemoved(..),\n                PendingSchemaChange::IndexRemoved(..),\n                PendingSchemaChange::SequenceRemoved(..),\n                PendingSchemaChange::ConstraintRemoved(..),\n                PendingSchemaChange::ConstraintRemoved(..),\n                PendingSchemaChange::TableRemoved(removed_table_id, _)\n            ]\n                if *removed_table_id == table_id\n        );\n        let _ = datastore.rollback_mut_tx(tx);\n\n        // Ensure the table still exists in the next transaction.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert!(\n            datastore.table_id_exists_mut_tx(&tx, &table_id),\n            \"Table should still exist\",\n        );\n        assert_eq!(all_rows(&datastore, &tx, table_id), std::slice::from_ref(&row));\n\n        // Now drop the table again and commit.\n        assert!(datastore.drop_table_mut_tx(&mut tx, table_id).is_ok());\n        let tx_data = commit(&datastore, tx)?;\n        let (_, entry) = tx_data\n            .iter_table_entries()\n            .find(|(id, _)| *id == table_id)\n            .expect(\"should have an entry for `table_id`\");\n        assert_eq!(&*entry.deletes, [row]);\n        assert!(entry.truncated, \"table should be truncated\");\n\n        // In the next transaction, the table doesn't exist.\n        assert!(\n            !datastore.table_id_exists_mut_tx(&begin_mut_tx(&datastore), &table_id),\n            \"Table should be removed\",\n        );\n        assert!(\n            !datastore.table_id_exists_tx(&begin_tx(&datastore), &table_id),\n            \"Table should be removed\",\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_create_table_is_transactional() -> ResultTest<()> {\n        // Create a table in a failed transaction.\n        let (datastore, tx, table_id) = setup_table()?;\n        assert_matches!(\n            tx.pending_schema_changes(),\n            [\n                PendingSchemaChange::TableAdded(added_table_id),\n                PendingSchemaChange::IndexAdded(.., Some(_)),\n                PendingSchemaChange::IndexAdded(.., None),\n                PendingSchemaChange::ConstraintAdded(..),\n                PendingSchemaChange::ConstraintAdded(..),\n                PendingSchemaChange::SequenceAdded(..),\n            ]\n                if *added_table_id == table_id\n        );\n        let _ = datastore.rollback_mut_tx(tx);\n\n        // Nothing should have happened.\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.pending_schema_changes(), []);\n        assert!(\n            !datastore.table_id_exists_mut_tx(&tx, &table_id),\n            \"Table should not exist\"\n        );\n        Ok(())\n    }\n\n    #[test]\n    fn test_alter_table_access_is_transactional() -> ResultTest<()> {\n        // Create a table.\n        let (datastore, tx, table_id) = setup_table()?;\n        commit(&datastore, tx)?;\n\n        // In a new transaction, alter the table access and then rollback.\n        let mut tx = begin_mut_tx(&datastore);\n        let assert_access =\n            |tx: &MutTxId, access| assert_eq!(tx.get_schema(table_id).map(|s| s.table_access), Some(access));\n        assert_access(&tx, StAccess::Public);\n        tx.alter_table_access(table_id, StAccess::Private)?;\n        assert_eq!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::TableAlterAccess(table_id, StAccess::Public)]\n        );\n        let _ = datastore.rollback_mut_tx(tx);\n\n        // Check that the access was reverted.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_access(&tx, StAccess::Public);\n\n        // Commit the change this time and check that it persisted.\n        tx.alter_table_access(table_id, StAccess::Private)?;\n        commit(&datastore, tx)?;\n        let tx = begin_mut_tx(&datastore);\n        assert_access(&tx, StAccess::Private);\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_alter_table_row_type_rejects_some_bad_changes() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n\n        // Setup the initial table.\n        let mut tx = begin_mut_tx(&datastore);\n        let schedule = ScheduleSchema {\n            table_id: TableId::SENTINEL,\n            schedule_id: ScheduleId::SENTINEL,\n            schedule_name: Identifier::for_test(\"schedule\"),\n            function_name: Identifier::for_test(\"reducer\"),\n            at_column: 1.into(),\n        };\n        let sum_ty = AlgebraicType::sum([(\"foo\", AlgebraicType::Bool), (\"bar\", AlgebraicType::U16)]);\n        let columns = [\n            ColumnSchema::for_test(0, \"id\", AlgebraicType::U64),\n            ColumnSchema::for_test(1, \"at\", ScheduleAt::get_type()),\n            ColumnSchema::for_test(2, \"sum\", sum_ty),\n        ];\n        let mut columns = columns.to_vec();\n        let schema = user_public_table(columns.clone(), [], [], [], Some(schedule), Some(0.into()));\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n\n        let mut change_add_restore = |idx: usize, add| {\n            let col_ty = &mut columns[idx].col_type;\n            let col_ty_old = col_ty.clone();\n\n            let vars_ref = &mut col_ty.as_sum_mut().unwrap().variants;\n            let mut vars = Vec::from(mem::take(vars_ref));\n            vars.push(SumTypeVariant::new_named(add, \"bad\"));\n\n            assert!(datastore\n                .alter_table_row_type_mut_tx(&mut tx, table_id, columns.clone())\n                .is_err());\n            columns[idx].col_type = col_ty_old;\n        };\n\n        // Add a variant to `ScheduleAt`.\n        change_add_restore(1, AlgebraicType::time_duration());\n\n        // Add a variant to `sum` with an incompatible layout.\n        change_add_restore(2, AlgebraicType::U32);\n\n        // Add another field.\n        columns.push(ColumnSchema::for_test(3, \"bad\", AlgebraicType::U8));\n        assert!(datastore\n            .alter_table_row_type_mut_tx(&mut tx, table_id, columns.clone())\n            .is_err());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_alter_table_row_type_is_transactional() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n\n        // Setup the initial table.\n        let mut tx = begin_mut_tx(&datastore);\n        let sum_original = AlgebraicType::sum([(\"ba\", AlgebraicType::U16)]);\n        let columns = [\n            ColumnSchema::for_test(0, \"a\", AlgebraicType::U64),\n            ColumnSchema::for_test(1, \"b\", sum_original.clone()),\n        ];\n        let indices = [\n            IndexSchema::for_test(\"index_a\", BTreeAlgorithm::from(0)),\n            IndexSchema::for_test(\"index_b\", BTreeAlgorithm::from(1)),\n        ];\n        let schema = user_public_table(columns, indices, [], [], None, None);\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        let columns_original = tx.get_schema(table_id).unwrap().columns.to_vec();\n        let mut columns = columns_original.clone();\n        commit(&datastore, tx)?;\n\n        // Change the `b` column, adding a variant.\n        let vars_ref = &mut columns[1].col_type.as_sum_mut().unwrap().variants;\n        let mut vars = Vec::from(mem::take(vars_ref));\n        vars.push(SumTypeVariant::new_named(AlgebraicType::U8, \"bb\"));\n        *vars_ref = vars.into();\n\n        // Change the columns in datastore and roll back.\n        let mut tx = begin_mut_tx(&datastore);\n        datastore.alter_table_row_type_mut_tx(&mut tx, table_id, columns.clone())?;\n        assert_eq!(\n            tx.pending_schema_changes(),\n            [PendingSchemaChange::TableAlterRowType(\n                table_id,\n                columns_original.clone()\n            )]\n        );\n        assert_eq!(tx.get_schema(table_id).unwrap().columns, columns.clone());\n        let _ = datastore.rollback_mut_tx(tx);\n\n        // Assert no change to the schema type and index keys.\n        let mut tx = begin_mut_tx(&datastore);\n        assert_eq!(tx.get_schema(table_id).unwrap().columns, columns_original);\n        let index_key_types = |tx: &MutTxId| {\n            tx.committed_state_write_lock\n                .get_table(table_id)\n                .unwrap()\n                .indexes\n                .values()\n                .map(|i| i.key_type.clone())\n                .collect::<Vec<_>>()\n        };\n        assert_eq!(index_key_types(&tx), [AlgebraicType::U64, sum_original]);\n\n        // Alter successfully this time and insert a row.\n        datastore.alter_table_row_type_mut_tx(&mut tx, table_id, columns.clone())?;\n        let expected_row_type = columns_to_row_type(&columns);\n        let schema = tx.get_schema(table_id).unwrap();\n        assert_eq!(schema.columns, columns);\n        assert_eq!(schema.get_row_type(), &expected_row_type);\n        let sum_val = SumValue::new(1, 42u8);\n        let id = 24u64;\n        let (_, row_ref) = insert(&datastore, &mut tx, table_id, &product![id, sum_val.clone()])?;\n        assert_eq!(row_ref.row_layout(), &RowTypeLayout::from(expected_row_type));\n        assert_eq!(\n            index_key_types(&tx),\n            [\n                AlgebraicType::U64,\n                AlgebraicType::sum([(\"ba\", AlgebraicType::U16), (\"bb\", AlgebraicType::U8)])\n            ]\n        );\n        let tx_data = commit(&datastore, tx)?;\n        // Ensure the change has been persisted in the commitlog.\n        let to_product = |col: &ColumnSchema| value_serialize(&StColumnRow::from(col.clone())).into_product().unwrap();\n        let entry = tx_data.entry_for(ST_COLUMN_ID).unwrap();\n        assert_eq!(&*entry.inserts, [to_product(&columns[1])].as_slice());\n        assert_eq!(&*entry.deletes, [to_product(&columns_original[1])].as_slice());\n        assert!(!entry.truncated, \"table should not be truncated\");\n\n        // Check that we can successfully scan using the new schema type post commit.\n        let tx = begin_tx(&datastore);\n        let row_ref = datastore\n            .iter_by_col_eq_tx(&tx, table_id, 1, &sum_val.into())?\n            .next()\n            .unwrap();\n        assert_eq!(row_ref.read_col::<u64>(0).unwrap(), id);\n\n        Ok(())\n    }\n    // TODO: Add the following tests\n    // - Create a tx that inserts 2000 rows with an auto_inc column\n    // - Create a tx that inserts 2000 rows with an auto_inc column and then rolls back\n\n    #[test]\n    fn test_add_columns_to_table() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n\n        let mut tx = begin_mut_tx(&datastore);\n\n        let initial_sum_type = AlgebraicType::sum([(\"ba\", AlgebraicType::U16)]);\n        let initial_columns = [\n            ColumnSchema::for_test(0, \"a\", AlgebraicType::U64),\n            ColumnSchema::for_test(1, \"b\", initial_sum_type.clone()),\n        ];\n\n        let initial_indices = [\n            IndexSchema::for_test(\"index_a\", BTreeAlgorithm::from(0)),\n            IndexSchema::for_test(\"index_b\", BTreeAlgorithm::from(1)),\n        ];\n\n        let sequence = SequenceRow {\n            id: SequenceId::SENTINEL.into(),\n            table: 0,\n            col_pos: 0,\n            name: \"Foo_id_seq\",\n            start: 5,\n        };\n\n        let schema = user_public_table(\n            initial_columns,\n            initial_indices.clone(),\n            [],\n            map_array([sequence]),\n            None,\n            None,\n        );\n\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n\n        let mut columns_original = tx.get_schema(table_id).unwrap().columns.to_vec();\n\n        // Insert initial rows\n        let initial_row = product![0u64, AlgebraicValue::sum(0, AlgebraicValue::U16(1))];\n        insert(&datastore, &mut tx, table_id, &initial_row).unwrap();\n        insert(&datastore, &mut tx, table_id, &initial_row).unwrap();\n        commit(&datastore, tx)?;\n\n        // Alter Table: Add Variant and Column\n        //\n        // Change the `b` column, adding a variant.\n        let vars_ref = &mut columns_original[1].col_type.as_sum_mut().unwrap().variants;\n        let mut vars = Vec::from(mem::take(vars_ref));\n        vars.push(SumTypeVariant::new_named(AlgebraicType::U8, \"bb\"));\n        *vars_ref = vars.into();\n        // Add column `c`\n        let mut new_columns = columns_original.clone();\n        new_columns.push(ColumnSchema::for_test(2, \"c\", AlgebraicType::U8));\n        let defaults = vec![AlgebraicValue::U8(42)];\n\n        let mut tx = begin_mut_tx(&datastore);\n        // insert a row in tx_state before adding column\n        tx.insert_product_value(table_id, &initial_row).unwrap();\n        // add column and then rollback\n        let rollback_table_id =\n            datastore.add_columns_to_table_mut_tx(&mut tx, table_id, new_columns.clone(), defaults.clone())?;\n        let _ = tx.rollback();\n\n        let old_rows = [\n            product![5u64, AlgebraicValue::sum(0, 1u16.into())],\n            product![6u64, AlgebraicValue::sum(0, 1u16.into())],\n        ];\n\n        let mut tx = begin_mut_tx(&datastore);\n        // check rollback was successful\n        let rows = tx\n            .table_scan(table_id)\n            .unwrap()\n            .map(|row| row.to_product_value())\n            .collect::<Vec<_>>();\n        assert_eq!(rows, old_rows, \"Rows shouldn't be changed if rolledback\");\n        let table = tx.table_name(rollback_table_id);\n        assert!(table.is_none(), \"new table shouldn't be created if rolledback\");\n\n        // Add column and actually commit this time.\n        tx.insert_product_value(table_id, &initial_row).unwrap();\n        let new_table_id = datastore.add_columns_to_table_mut_tx(&mut tx, table_id, new_columns.clone(), defaults)?;\n\n        let tx_data = commit(&datastore, tx)?;\n\n        assert_ne!(\n            new_table_id, table_id,\n            \"New table ID after migration should differ from old one\"\n        );\n\n        //  Validate Commitlog Changes\n        let entry = tx_data\n            .entry_for(table_id)\n            .expect(\"Expected delete log for original table\");\n\n        assert_eq!(\n            &*entry.deletes, &old_rows,\n            \"Unexpected delete entries after altering the table\"\n        );\n\n        let inserted_rows = [\n            product![5u64, AlgebraicValue::sum(0, 1u16.into()), 42u8],\n            product![6u64, AlgebraicValue::sum(0, 1u16.into()), 42u8],\n            product![8u64, AlgebraicValue::sum(0, 1u16.into()), 42u8],\n        ];\n\n        let new_entry = tx_data\n            .entry_for(new_table_id)\n            .expect(\"Expected insert log for new table\");\n\n        assert_eq!(\n            &*new_entry.inserts, &inserted_rows,\n            \"Unexpected insert entries after altering the table\"\n        );\n\n        // Insert Rows into New Table\n        let mut tx = begin_mut_tx(&datastore);\n\n        let new_row = product![0u64, AlgebraicValue::sum(0, 1u16.into()), 0u8];\n        tx.insert_product_value(new_table_id, &new_row).unwrap();\n        commit(&datastore, tx)?;\n\n        // test for auto_inc feields\n        let tx = begin_mut_tx(&datastore);\n        let rows = tx.table_scan(new_table_id).unwrap().map(|row| row.to_product_value());\n\n        let mut last_row_auto_inc = 0;\n        for row in rows {\n            let auto_inc_col = row.get_field(0, None)?;\n            if let AlgebraicValue::U64(val) = auto_inc_col {\n                assert!(val > &last_row_auto_inc, \"Auto-increment value did not increase\");\n                last_row_auto_inc = *val;\n            }\n        }\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_committed_and_rollback_metrics() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n\n        let tx = begin_mut_tx(&datastore);\n        let (_, _, metrics, _) = tx.commit();\n        assert!(metrics.committed);\n\n        let tx = begin_mut_tx(&datastore);\n        let (_, metrics, _) = tx.rollback();\n        assert!(!metrics.committed);\n        Ok(())\n    }\n\n    /// Create an event table with the basic schema (id: u32, name: String, age: u32).\n    fn setup_event_table() -> ResultTest<(Locking, MutTxId, TableId)> {\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        let mut schema = basic_table_schema_with_indices(basic_indices(), basic_constraints());\n        schema.is_event = true;\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        Ok((datastore, tx, table_id))\n    }\n\n    #[test]\n    fn test_event_table_insert_delete_noop() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        // Commit the table-creation tx first.\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row = u32_str_u32(1, \"Alice\", 30);\n        insert(&datastore, &mut tx, table_id, &row)?;\n        datastore.delete_by_rel_mut_tx(&mut tx, table_id, [row]);\n\n        let tx_data = commit(&datastore, tx)?;\n\n        // Insert+delete in same tx should cancel out: no TxData entry for this table.\n        assert!(\n            tx_data.inserts_for_table(table_id).is_none(),\n            \"insert+delete should cancel: no inserts in TxData\"\n        );\n        assert!(\n            tx_data.deletes_for_table(table_id).is_none(),\n            \"insert+delete should cancel: no deletes in TxData\"\n        );\n\n        // Committed state should be empty for event tables.\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(all_rows(&datastore, &tx, table_id).len(), 0);\n        Ok(())\n    }\n\n    #[test]\n    fn test_event_table_update_only_final_row() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        // Commit the table-creation tx first.\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row_v1 = u32_str_u32(1, \"Alice\", 30);\n        insert(&datastore, &mut tx, table_id, &row_v1)?;\n\n        // Update via the index on column 0 (id) — replaces the row with same PK.\n        let idx = extract_index_id(&datastore, &tx, &basic_indices()[0])?;\n        let row_v2 = u32_str_u32(1, \"Alice\", 31);\n        update(&datastore, &mut tx, table_id, idx, &row_v2)?;\n\n        let tx_data = commit(&datastore, tx)?;\n\n        // The update replaces the original insert, so TxData should have only the final row.\n        let inserts = tx_data.inserts_for_table(table_id).expect(\"should have inserts\");\n        assert_eq!(inserts.len(), 1, \"update should leave exactly 1 insert\");\n        assert_eq!(inserts[0], row_v2);\n\n        // Committed state should still be empty for event tables.\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(all_rows(&datastore, &tx, table_id).len(), 0);\n        Ok(())\n    }\n\n    #[test]\n    fn test_event_table_insert_records_txdata_not_committed_state() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        // Commit the table-creation tx first.\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row = u32_str_u32(1, \"Bob\", 25);\n        insert(&datastore, &mut tx, table_id, &row)?;\n\n        let tx_data = commit(&datastore, tx)?;\n\n        // TxData should record the insert.\n        let inserts = tx_data\n            .inserts_for_table(table_id)\n            .expect(\"event table insert should appear in TxData\");\n        assert_eq!(inserts.len(), 1);\n        assert_eq!(inserts[0], row);\n\n        // But committed state should be empty.\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(all_rows(&datastore, &tx, table_id).len(), 0);\n        Ok(())\n    }\n\n    #[test]\n    fn test_event_table_replay_ignores_inserts() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        // Commit the table-creation tx so the schema exists.\n        commit(&datastore, tx)?;\n\n        // Get the schema for this event table.\n        let tx = begin_mut_tx(&datastore);\n        let schema = datastore.schema_for_table_mut_tx(&tx, table_id)?;\n        let _ = datastore.rollback_mut_tx(tx);\n\n        // Directly call replay_insert on committed state.\n        let row = u32_str_u32(1, \"Carol\", 40);\n        {\n            let mut committed_state = datastore.committed_state.write();\n            committed_state.replay_insert(table_id, &schema, &row)?;\n        }\n\n        // After replay, the event table should still have no committed rows.\n        let tx = begin_mut_tx(&datastore);\n        assert_eq!(\n            all_rows(&datastore, &tx, table_id).len(),\n            0,\n            \"replay_insert should be a no-op for event tables\"\n        );\n        Ok(())\n    }\n\n    /// Inserting a duplicate primary key within the same transaction should fail for event tables.\n    #[test]\n    fn test_event_table_primary_key_enforced_within_tx() -> ResultTest<()> {\n        let datastore = get_datastore()?;\n        let mut tx = begin_mut_tx(&datastore);\n        let mut schema = basic_table_schema_with_indices(basic_indices(), basic_constraints());\n        schema.is_event = true;\n        schema.primary_key = Some(0.into()); // PK on column 0 (id)\n        let table_id = datastore.create_table_mut_tx(&mut tx, schema)?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row1 = u32_str_u32(1, \"Alice\", 30);\n        insert(&datastore, &mut tx, table_id, &row1)?;\n\n        // Duplicate PK in same TX should error (unique constraint on col 0).\n        let row2 = u32_str_u32(1, \"Bob\", 25);\n        let result = insert(&datastore, &mut tx, table_id, &row2);\n        assert!(result.is_err(), \"duplicate PK in same TX should be rejected\");\n        Ok(())\n    }\n\n    /// Inserting a duplicate unique column value within the same transaction should fail for event tables.\n    #[test]\n    fn test_event_table_unique_constraint_within_tx() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row1 = u32_str_u32(1, \"Alice\", 30);\n        insert(&datastore, &mut tx, table_id, &row1)?;\n\n        // Duplicate unique name in same TX should error (unique constraint on col 1).\n        let row2 = u32_str_u32(2, \"Alice\", 25);\n        let result = insert(&datastore, &mut tx, table_id, &row2);\n        assert!(result.is_err(), \"duplicate unique column in same TX should be rejected\");\n        Ok(())\n    }\n\n    /// Btree index lookups should work within a transaction for event tables.\n    /// We verify this indirectly: if the index works, an update via that index succeeds.\n    #[test]\n    fn test_event_table_index_lookup_within_tx() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        let row1 = u32_str_u32(1, \"Alice\", 30);\n        let row2 = u32_str_u32(2, \"Bob\", 25);\n        insert(&datastore, &mut tx, table_id, &row1)?;\n        insert(&datastore, &mut tx, table_id, &row2)?;\n\n        // Update via the btree index on column 0 (id) — this exercises index lookup.\n        let idx = extract_index_id(&datastore, &tx, &basic_indices()[0])?;\n        let row1_updated = u32_str_u32(1, \"Alice\", 31);\n        update(&datastore, &mut tx, table_id, idx, &row1_updated)?;\n\n        // Verify both rows are visible in the tx.\n        let rows = all_rows(&datastore, &tx, table_id);\n        assert_eq!(rows.len(), 2, \"should have 2 rows after insert+update\");\n        assert!(rows.contains(&row1_updated), \"updated row should be present\");\n        assert!(rows.contains(&row2), \"other row should be present\");\n        Ok(())\n    }\n\n    /// Auto-increment should generate distinct values within a single transaction for event tables.\n    #[test]\n    fn test_event_table_auto_inc_within_tx() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        commit(&datastore, tx)?;\n\n        let mut tx = begin_mut_tx(&datastore);\n        // Insert with id=0 to trigger auto_inc on column 0.\n        let row1 = u32_str_u32(0, \"Alice\", 30);\n        let (gen1, _) = insert(&datastore, &mut tx, table_id, &row1)?;\n        let row2 = u32_str_u32(0, \"Bob\", 25);\n        let (gen2, _) = insert(&datastore, &mut tx, table_id, &row2)?;\n\n        // Both auto-incremented ids should be distinct.\n        assert_ne!(gen1, gen2, \"auto_inc should produce distinct values within the same TX\");\n        Ok(())\n    }\n\n    /// Constraints on event tables should reset across transactions (no committed state carryover).\n    #[test]\n    fn test_event_table_constraints_reset_across_txs() -> ResultTest<()> {\n        let (datastore, tx, table_id) = setup_event_table()?;\n        commit(&datastore, tx)?;\n\n        // TX1: insert row with id=1.\n        let mut tx1 = begin_mut_tx(&datastore);\n        let row = u32_str_u32(1, \"Alice\", 30);\n        insert(&datastore, &mut tx1, table_id, &row)?;\n        commit(&datastore, tx1)?;\n\n        // TX2: insert row with same id=1 — should succeed because event tables\n        // don't carry committed state.\n        let mut tx2 = begin_mut_tx(&datastore);\n        let row = u32_str_u32(1, \"Bob\", 25);\n        let result = insert(&datastore, &mut tx2, table_id, &row);\n        assert!(\n            result.is_ok(),\n            \"same PK in a new TX should succeed for event tables (no committed state)\"\n        );\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/delete_table.rs",
    "content": "use spacetimedb_sats::{layout::Size, memory_usage::MemoryUsage};\nuse spacetimedb_table::{\n    fixed_bit_set::FixedBitSet,\n    indexes::{max_rows_in_page, PageIndex, PageOffset, RowPointer, SquashedOffset},\n};\n\n/// A table recording which rows of a table in the [`CommittedState`] that have been deleted.\npub struct DeleteTable {\n    /// Keeps track of all the deleted row pointers.\n    deleted: Vec<Option<FixedBitSet>>,\n    /// The number of deleted row pointers.\n    ///\n    /// This is stored for efficiency, but can be derived from `self.deleted` otherwise.\n    len: usize,\n    /// The size of a row in the table.\n    fixed_row_size: Size,\n}\n\nimpl MemoryUsage for DeleteTable {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            deleted,\n            len,\n            fixed_row_size,\n        } = self;\n        deleted.heap_usage() + len.heap_usage() + fixed_row_size.heap_usage()\n    }\n}\n\nimpl DeleteTable {\n    /// Returns a new deletion table where the rows have `fixed_row_size`.\n    ///\n    /// The table is initially empty.\n    pub fn new(fixed_row_size: Size) -> Self {\n        Self {\n            deleted: <_>::default(),\n            len: 0,\n            fixed_row_size,\n        }\n    }\n\n    /// Returns whether `ptr`, belonging to a table in [`CommittedState`], is recorded as deleted.\n    pub fn contains(&self, ptr: RowPointer) -> bool {\n        let page_idx = ptr.page_index().idx();\n        match self.deleted.get(page_idx) {\n            Some(Some(set)) => set.get(ptr.page_offset() / self.fixed_row_size),\n            _ => false,\n        }\n    }\n\n    /// Marks `ptr`, belonging to a table in [`CommittedState`], as deleted.\n    ///\n    /// Returns `true` if `ptr` was not previously marked.\n    pub fn insert(&mut self, ptr: RowPointer) -> bool {\n        let fixed_row_size = self.fixed_row_size;\n        let page_idx = ptr.page_index().idx();\n        let bitset_idx = ptr.page_offset() / fixed_row_size;\n\n        let new_set = || {\n            let mut bs = FixedBitSet::new(max_rows_in_page(fixed_row_size));\n            bs.set(bitset_idx, true);\n            bs\n        };\n\n        match self.deleted.get_mut(page_idx) {\n            // Already got a bitset for this page, just set the bit.\n            Some(Some(set)) => {\n                let added = !set.get(bitset_idx);\n                set.set(bitset_idx, true);\n                if added {\n                    self.len += 1;\n                }\n                added\n            }\n            // No bitset yet, initialize the slot with a new one.\n            Some(slot) => {\n                *slot = Some(new_set());\n                self.len += 1;\n                true\n            }\n            // We haven't reached this page index before,\n            // Make uninitialized slots for all the pages before this one\n            // that do not have bitsets.\n            // Add an initialized bitset for this page index.\n            None => {\n                let pages = self.deleted.len();\n                let after = 1 + page_idx;\n                self.deleted.reserve(after - pages);\n                for _ in pages..page_idx {\n                    self.deleted.push(None);\n                }\n                self.deleted.push(Some(new_set()));\n                self.len += 1;\n                true\n            }\n        }\n    }\n\n    /// Un-marks `ptr`, belonging to a table in [`CommittedState`], as deleted.\n    pub fn remove(&mut self, ptr: RowPointer) -> bool {\n        let fixed_row_size = self.fixed_row_size;\n        let page_idx = ptr.page_index().idx();\n        let bitset_idx = ptr.page_offset() / fixed_row_size;\n        if let Some(Some(set)) = self.deleted.get_mut(page_idx) {\n            let removed = set.get(bitset_idx);\n            if removed {\n                self.len -= 1;\n            }\n            set.set(bitset_idx, false);\n            removed\n        } else {\n            false\n        }\n    }\n\n    /// Yields all the row pointers marked in this table.\n    pub fn iter(&self) -> impl '_ + Iterator<Item = RowPointer> {\n        (0..)\n            .map(PageIndex)\n            .zip(self.deleted.iter())\n            .filter_map(|(pi, set)| Some((pi, set.as_ref()?)))\n            .flat_map(move |(pi, set)| {\n                set.iter_set().map(move |idx| {\n                    let po = PageOffset(idx as u16 * self.fixed_row_size.0);\n                    // It's a committed state pointer that has been deleted.\n                    RowPointer::new(false, pi, po, SquashedOffset::COMMITTED_STATE)\n                })\n            })\n    }\n\n    /// Returns the number of rows marked for deletion.\n    pub fn len(&self) -> usize {\n        self.len\n    }\n\n    /// Returns whether there are any rows to delete.\n    pub fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use core::cmp::Ordering;\n    use proptest::array::uniform;\n    use proptest::collection::vec;\n    use proptest::prelude::*;\n    use std::collections::BTreeSet;\n\n    #[derive(Copy, Clone, Eq, PartialEq, Debug)]\n    struct OrdRowPtr(RowPointer);\n    impl Ord for OrdRowPtr {\n        fn cmp(&self, other: &Self) -> Ordering {\n            self.0\n                .page_index()\n                .cmp(&other.0.page_index())\n                .then_with(|| self.0.page_offset().cmp(&other.0.page_offset()))\n        }\n    }\n    impl PartialOrd for OrdRowPtr {\n        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n            Some(self.cmp(other))\n        }\n    }\n    impl PartialEq<OrdRowPtr> for RowPointer {\n        fn eq(&self, other: &OrdRowPtr) -> bool {\n            *self == other.0\n        }\n    }\n\n    /// The `DeleteTable` is really just a set specialized for `RowPointer`.\n    /// To harden the tests, we mirror the operations with a `BTreeSet`\n    /// and assert equivalent observable results.\n    ///\n    /// The setup here can cause some redundancy in our checks,\n    /// but that's fine, as testing is not perf sensitive.\n    struct TestDT {\n        dt: DeleteTable,\n        bs: BTreeSet<OrdRowPtr>,\n    }\n\n    impl TestDT {\n        fn new(fixed_row_size: Size) -> Self {\n            let dt = DeleteTable::new(fixed_row_size);\n            let bs = BTreeSet::new();\n            Self { dt, bs }\n        }\n        fn contains(&self, ptr: RowPointer) -> bool {\n            let dt = self.dt.contains(ptr);\n            let bs = self.bs.contains(&OrdRowPtr(ptr));\n            assert_eq!(dt, bs);\n            dt\n        }\n        fn insert(&mut self, ptr: RowPointer) -> bool {\n            let dt = self.dt.insert(ptr);\n            let bs = self.bs.insert(OrdRowPtr(ptr));\n            assert_eq!(dt, bs);\n            self.check_state();\n            dt\n        }\n        fn remove(&mut self, ptr: RowPointer) -> bool {\n            let dt = self.dt.remove(ptr);\n            let bs = self.bs.remove(&OrdRowPtr(ptr));\n            assert_eq!(dt, bs);\n            self.check_state();\n            dt\n        }\n        fn iter(&self) -> impl Iterator<Item = RowPointer> + use<> {\n            let dt = self.dt.iter().collect::<Vec<_>>();\n            let bs = self.bs.iter().copied().collect::<Vec<_>>();\n            assert_eq!(dt, bs);\n            assert_eq!(self.len(), bs.len());\n            dt.into_iter()\n        }\n        fn check_state(&self) {\n            let _ = self.iter();\n        }\n        fn len(&self) -> usize {\n            let dt = self.dt.len();\n            let bs = self.bs.len();\n            assert_eq!(dt, bs);\n            dt\n        }\n        fn is_empty(&self) -> bool {\n            let dt = self.dt.is_empty();\n            let bs = self.bs.is_empty();\n            assert_eq!(dt, bs);\n            dt\n        }\n    }\n\n    fn gen_size() -> impl Strategy<Value = Size> {\n        (2..100u16).prop_map(Size)\n    }\n\n    fn gen_ptr(row_size: Size) -> impl Strategy<Value = RowPointer> {\n        let page_offset = (0..100u16).prop_map(move |num| PageOffset(row_size.0 * num));\n        let page_index = (0..100u64).prop_map(PageIndex);\n        (page_index, page_offset).prop_map(|(pi, po)| RowPointer::new(false, pi, po, SquashedOffset::COMMITTED_STATE))\n    }\n\n    fn gen_size_and_ptrs<const N: usize>() -> impl Strategy<Value = (Size, [RowPointer; N])> {\n        gen_size().prop_flat_map(|s| uniform(gen_ptr(s)).prop_map(move |ptr| (s, ptr)))\n    }\n\n    fn gen_two_ptr_vecs() -> impl Strategy<Value = (Size, [Vec<RowPointer>; 2])> {\n        gen_size().prop_flat_map(|s| uniform(vec(gen_ptr(s), 0..100)).prop_map(move |vs| (s, vs)))\n    }\n\n    proptest! {\n        #[test]\n        fn insertion_entails_contained((size, [ptr_a, ptr_b]) in gen_size_and_ptrs()) {\n            prop_assume!(ptr_a != ptr_b);\n\n            let mut dt = TestDT::new(size);\n\n            // Initially we have nothing.\n            prop_assert!(dt.is_empty());\n            prop_assert!(!dt.contains(ptr_a));\n            prop_assert!(!dt.contains(ptr_b));\n\n            // Add `ptr_a` and expect it but not `ptr_b`.\n            prop_assert!(dt.insert(ptr_a));\n            prop_assert!(!dt.is_empty());\n            prop_assert!(dt.contains(ptr_a));\n            prop_assert!(!dt.contains(ptr_b));\n        }\n\n        #[test]\n        fn insertion_is_state_idempotent((size, [ptr_a]) in gen_size_and_ptrs()) {\n            let mut dt = TestDT::new(size);\n\n            prop_assert!(dt.insert(ptr_a));\n            prop_assert!(dt.contains(ptr_a));\n\n            prop_assert!(!dt.insert(ptr_a)); // Idempotence.\n            prop_assert!(dt.contains(ptr_a));\n        }\n\n        #[test]\n        fn deleting_non_existent_does_nothing((size, [ptr_a, ptr_b]) in gen_size_and_ptrs()) {\n            prop_assume!(ptr_a != ptr_b);\n\n            let mut dt = TestDT::new(size);\n\n            prop_assert!(!dt.remove(ptr_b));\n            prop_assert!(dt.insert(ptr_a));\n            prop_assert!(!dt.remove(ptr_b));\n        }\n\n        #[test]\n        fn insertion_followed_by_deletion_is_no_op((size, [ptr_a]) in gen_size_and_ptrs()) {\n            let mut dt = TestDT::new(size);\n\n            prop_assert!(dt.insert(ptr_a));\n            prop_assert!(dt.contains(ptr_a));\n\n            prop_assert!(dt.remove(ptr_a));\n            prop_assert!(!dt.contains(ptr_a));\n        }\n\n        #[test]\n        fn set_intersection_behaves((size, [ptrs_a, ptrs_b]) in gen_two_ptr_vecs()) {\n            let mut dt_a = TestDT::new(size);\n            let mut dt_b = TestDT::new(size);\n\n            // Fill first set.\n            for ptr in ptrs_a {\n                dt_a.insert(ptr);\n            }\n            // Fill second set.\n            for ptr in ptrs_b {\n                dt_b.insert(ptr);\n            }\n\n            // Intersect first and second sets.\n            let expected_intersection = dt_a.bs.intersection(&dt_b.bs).copied().collect::<BTreeSet<_>>();\n            for ptr in dt_a.iter() {\n                if !dt_b.contains(ptr) {\n                    prop_assert!(dt_a.remove(ptr));\n                }\n            }\n            prop_assert_eq!(dt_a.iter().collect::<Vec<_>>(), expected_intersection.into_iter().collect::<Vec<_>>());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/mod.rs",
    "content": "#![deny(unsafe_op_in_unsafe_fn)]\n\npub mod committed_state;\npub mod datastore;\nmod mut_tx;\npub use mut_tx::{FuncCallType, MutTxId, ViewCallInfo};\nmod sequence;\npub mod state_view;\npub use state_view::{IterByColEqTx, IterByColRangeTx};\npub mod delete_table;\nmod tx;\npub use tx::{NumDistinctValues, TxId};\nmod tx_state;\n#[cfg(any(test, feature = \"test\"))]\npub use tx_state::PendingSchemaChange;\n\nuse parking_lot::{\n    lock_api::{ArcMutexGuard, ArcRwLockReadGuard, ArcRwLockWriteGuard},\n    RawMutex, RawRwLock,\n};\n\n// Type aliases for lock guards\ntype SharedWriteGuard<T> = ArcRwLockWriteGuard<RawRwLock, T>;\ntype SharedMutexGuard<T> = ArcMutexGuard<RawMutex, T>;\ntype SharedReadGuard<T> = ArcRwLockReadGuard<RawRwLock, T>;\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/mut_tx.rs",
    "content": "use super::{\n    committed_state::{CommitTableForInsertion, CommittedState},\n    datastore::{Result, TxMetrics},\n    delete_table::DeleteTable,\n    sequence::{Sequence, SequencesState},\n    state_view::{IterByColEqMutTx, IterByColRangeMutTx, IterMutTx, StateView},\n    tx::TxId,\n    tx_state::{IndexIdMap, PendingSchemaChange, TxState, TxTableForInsertion},\n    SharedMutexGuard, SharedWriteGuard,\n};\nuse crate::{\n    error::ViewError,\n    locking_tx_datastore::state_view::EqOnColumn,\n    system_tables::{\n        system_tables, ConnectionIdViaU128, IdentityViaU256, StConnectionCredentialsFields, StConnectionCredentialsRow,\n        StViewColumnFields, StViewFields, StViewParamFields, StViewParamRow, StViewSubFields, StViewSubRow,\n        ST_CONNECTION_CREDENTIALS_ID, ST_VIEW_COLUMN_ID, ST_VIEW_ID, ST_VIEW_PARAM_ID, ST_VIEW_SUB_ID,\n    },\n};\nuse crate::{\n    error::{IndexError, SequenceError, TableError},\n    system_tables::{\n        with_sys_table_buf, StClientFields, StClientRow, StColumnAccessorFields, StColumnAccessorRow, StColumnFields,\n        StColumnRow, StConstraintFields, StConstraintRow, StEventTableRow, StFields as _, StIndexAccessorFields,\n        StIndexAccessorRow, StIndexFields, StIndexRow, StRowLevelSecurityFields, StRowLevelSecurityRow,\n        StScheduledFields, StScheduledRow, StSequenceFields, StSequenceRow, StTableAccessorFields, StTableAccessorRow,\n        StTableFields, StTableRow, SystemTable, ST_CLIENT_ID, ST_COLUMN_ACCESSOR_ID, ST_COLUMN_ID, ST_CONSTRAINT_ID,\n        ST_EVENT_TABLE_ID, ST_INDEX_ACCESSOR_ID, ST_INDEX_ID, ST_ROW_LEVEL_SECURITY_ID, ST_SCHEDULED_ID,\n        ST_SEQUENCE_ID, ST_TABLE_ACCESSOR_ID, ST_TABLE_ID,\n    },\n};\nuse crate::{execution_context::ExecutionContext, system_tables::StViewColumnRow};\nuse crate::{execution_context::Workload, system_tables::StViewRow};\nuse crate::{\n    locking_tx_datastore::state_view::{ApplyFilter, RangeOnColumn, ScanOrIndex},\n    traits::{InsertFlags, RowTypeForTable, TxData, UpdateFlags},\n};\nuse core::ops::RangeBounds;\nuse core::{cell::RefCell, mem};\nuse core::{iter, ops::Bound};\nuse itertools::Either;\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::map::{HashMap, HashSet, IntMap};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_execution::{dml::MutDatastore, Datastore, DeltaStore, Row};\nuse spacetimedb_lib::{db::raw_def::v9::RawSql, metrics::ExecutionMetrics, Timestamp};\nuse spacetimedb_lib::{\n    db::{auth::StAccess, raw_def::SEQUENCE_ALLOCATION_STEP},\n    ConnectionId, Identity,\n};\nuse spacetimedb_primitives::{\n    col_list, ArgId, ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewFnPtr, ViewId,\n};\nuse spacetimedb_sats::{\n    bsatn::{self, to_writer, DecodeError, Deserializer},\n    de::{DeserializeSeed, WithBound},\n    memory_usage::MemoryUsage,\n    raw_identifier::RawIdentifier,\n    ser::Serialize,\n    AlgebraicType, AlgebraicValue, ProductType, ProductValue, WithTypespace,\n};\nuse spacetimedb_schema::{\n    def::{ModuleDef, ViewColumnDef, ViewDef, ViewParamDef},\n    identifier::Identifier,\n    reducer_name::ReducerName,\n    schema::{ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, SequenceSchema, TableSchema},\n    table_name::TableName,\n};\nuse spacetimedb_table::{\n    blob_store::BlobStore,\n    indexes::{RowPointer, SquashedOffset},\n    static_assert_size,\n    table::{\n        BlobNumBytes, DuplicateError, IndexScanPointIter, IndexScanRangeIter, InsertError, RowRef, Table,\n        TableAndIndex, UniqueConstraintViolation,\n    },\n    table_index::{IndexCannotSeekRange, IndexSeekRangeResult, TableIndex},\n};\nuse std::{\n    marker::PhantomData,\n    sync::Arc,\n    time::{Duration, Instant},\n};\n\ntype DecodeResult<T> = core::result::Result<T, DecodeError>;\n\n#[derive(Clone, Debug, Eq, PartialEq, Hash)]\npub struct ViewCallInfo {\n    pub view_id: ViewId,\n    pub table_id: TableId,\n    pub fn_ptr: ViewFnPtr,\n    pub sender: Option<Identity>,\n}\n\n/// A data structure for tracking the database rows/keys that are read by views\n#[derive(Default)]\npub struct ViewReadSets {\n    tables: IntMap<TableId, TableReadSet>,\n}\n\nimpl MemoryUsage for ViewReadSets {\n    fn heap_usage(&self) -> usize {\n        0 // TODO: Implement memory tracking for read sets\n    }\n}\n\nimpl ViewReadSets {\n    /// Returns whether there are no read sets recorded.\n    pub fn is_empty(&self) -> bool {\n        self.tables.is_empty()\n    }\n\n    /// Returns the views that perform a full scan of this table\n    pub fn views_for_table_scan(&self, table_id: &TableId) -> impl Iterator<Item = &ViewCallInfo> + use<'_> {\n        self.tables\n            .get(table_id)\n            .into_iter()\n            .flat_map(TableReadSet::views_for_table_scan)\n    }\n\n    /// Record that a view performs a full scan of this table\n    pub fn insert_full_table_scan(&mut self, table_id: TableId, call: ViewCallInfo) {\n        self.tables.entry(table_id).or_default().insert_table_scan(call);\n    }\n\n    /// Removes keys for `view_id` from the read set\n    pub fn remove_view(&mut self, view_id: ViewId, sender: Option<Identity>) {\n        self.tables.retain(|_, readset| {\n            readset.remove_view(view_id, sender);\n            !readset.is_empty()\n        });\n    }\n\n    /// Merge or union read sets together\n    pub fn merge(&mut self, readset: Self) {\n        for (table_id, rs) in readset.tables {\n            self.tables.entry(table_id).or_default().merge(rs);\n        }\n    }\n\n    /// Record that a view reads a specific index key\n    pub fn insert_index_scan(&mut self, table_id: TableId, cols: ColList, proj: AlgebraicValue, call: ViewCallInfo) {\n        let table_readset = self.tables.entry(table_id).or_default();\n        table_readset.insert_index_scan(cols, proj, call);\n    }\n\n    /// Returns the views that index seek on the given row pointer\n    pub fn views_for_index_seek<'a>(\n        &'a self,\n        table_id: &TableId,\n        row_ptr: RowRef<'a>,\n    ) -> impl Iterator<Item = &'a ViewCallInfo> + use<'a> {\n        self.tables\n            .get(table_id)\n            .into_iter()\n            .flat_map(move |ts| ts.views_for_index_seek(row_ptr))\n    }\n}\n\ntype IndexKeyReadSet = HashMap<AlgebraicValue, HashSet<ViewCallInfo>>;\ntype IndexColReadSet = HashMap<ColList, IndexKeyReadSet>;\n\n/// A table-level read set for views\n#[derive(Default)]\nstruct TableReadSet {\n    table_scans: HashSet<ViewCallInfo>,\n    index_reads: IndexColReadSet,\n}\n\nimpl TableReadSet {\n    /// Record that this view performs a full scan of this read set's table\n    fn insert_table_scan(&mut self, call: ViewCallInfo) {\n        self.table_scans.insert(call);\n    }\n\n    /// Returns the views that perform a full scan of this read set's table\n    fn views_for_table_scan(&self) -> impl Iterator<Item = &ViewCallInfo> {\n        self.table_scans.iter()\n    }\n\n    /// Record that a view reads a specific index key for this table\n    fn insert_index_scan(&mut self, cols: ColList, key: AlgebraicValue, call: ViewCallInfo) {\n        let key_map = self.index_reads.entry(cols).or_default();\n        key_map.entry(key).or_default().insert(call);\n    }\n\n    /// Returns the views that index seek on the given row pointer (for this table)\n    fn views_for_index_seek<'a>(&'a self, row_ptr: RowRef<'a>) -> impl Iterator<Item = &'a ViewCallInfo> {\n        self.index_reads.iter().flat_map(move |(cols, av_set)| {\n            row_ptr\n                .project(cols)\n                .ok()\n                .and_then(|av| av_set.get(&av))\n                .into_iter()\n                .flat_map(|views| views.iter())\n        })\n    }\n\n    /// Is this read set empty? (no table scans and no index reads)\n    fn is_empty(&self) -> bool {\n        self.table_scans.is_empty() && self.index_reads.is_empty()\n    }\n\n    /// Removes keys for `view_id` from the read set, optionally filtering by `sender`\n    fn remove_view(&mut self, view_id: ViewId, sender: Option<Identity>) {\n        let matches_call = |call: &ViewCallInfo| {\n            call.view_id == view_id && sender.as_ref().is_none_or(|s| call.sender.as_ref() == Some(s))\n        };\n\n        // Remove from table_scans\n        self.table_scans.retain(|call| !matches_call(call));\n\n        // Remove from index_reads\n        self.index_reads.retain(|_cols, key_map| {\n            key_map.retain(|_key, views| {\n                views.retain(|call| !matches_call(call));\n                !views.is_empty()\n            });\n            !key_map.is_empty()\n        });\n    }\n\n    /// Merge (union) another table read set into this one\n    fn merge(&mut self, other: TableReadSet) {\n        // merge table scans\n        self.table_scans.extend(other.table_scans);\n\n        // merge index reads: for each cols, for each av, extend the view set\n        for (cols, other_av_set) in other.index_reads {\n            let av_set = self.index_reads.entry(cols).or_default();\n            for (av, other_views) in other_av_set {\n                av_set.entry(av).or_default().extend(other_views);\n            }\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\n/// The type of operation being performed against the datastore.\npub enum FuncCallType {\n    Reducer,\n    Procedure,\n    View(ViewCallInfo),\n}\n\n/// Represents a Mutable transaction. Holds locks for its duration\n///\n/// The initialization of this struct is sensitive because improper\n/// handling can lead to deadlocks. Therefore, it is strongly recommended to use\n/// `Locking::begin_mut_tx()` for instantiation to ensure safe acquisition of locks.\npub struct MutTxId {\n    pub(super) tx_state: TxState,\n    pub(super) committed_state_write_lock: SharedWriteGuard<CommittedState>,\n    pub(super) sequence_state_lock: SharedMutexGuard<SequencesState>,\n    pub(super) lock_wait_time: Duration,\n    pub(super) read_sets: ViewReadSets,\n    // TODO(cloutiertyler): The below were made `pub` for the datastore split. We should\n    // make these private again.\n    pub timer: Instant,\n    pub ctx: ExecutionContext,\n    pub metrics: ExecutionMetrics,\n    // Marks `MutTxId` as `!Send` by embedding a non-`Send` type.\n    pub(crate) _not_send: PhantomData<std::rc::Rc<()>>,\n}\n\nstatic_assert_size!(MutTxId, 432);\n\nimpl MutTxId {\n    /// Record that a view performs a table scan in this transaction's read set\n    pub fn record_table_scan(&mut self, op: &FuncCallType, table_id: TableId) {\n        if let FuncCallType::View(view) = op {\n            self.read_sets.insert_full_table_scan(table_id, view.clone());\n        }\n    }\n\n    /// Record that a view performs a ranged index scan in this transaction's read set.\n    #[inline]\n    pub fn record_index_scan_range(\n        &mut self,\n        op: &FuncCallType,\n        table_id: TableId,\n        index_id: IndexId,\n        lower: Bound<AlgebraicValue>,\n        upper: Bound<AlgebraicValue>,\n    ) {\n        if let FuncCallType::View(view) = op {\n            self.record_index_scan_range_inner(view, table_id, index_id, lower, upper);\n        };\n    }\n\n    // This is cold as we don't want it to be inlined in case it doesn't end up getting called.\n    // This previously showed up in flamegraphs.\n    #[cold]\n    #[inline(never)]\n    pub fn record_index_scan_range_inner(\n        &mut self,\n        view: &ViewCallInfo,\n        table_id: TableId,\n        index_id: IndexId,\n        lower: Bound<AlgebraicValue>,\n        upper: Bound<AlgebraicValue>,\n    ) {\n        // Check for precise index seek.\n        if let (Bound::Included(low_val), Bound::Included(up_val)) = (&lower, &upper)\n            && low_val == up_val\n        {\n            self.record_index_scan_point_inner(view, table_id, index_id, low_val.clone());\n            return;\n        }\n\n        // Everything else is treated as a table scan.\n        self.read_sets.insert_full_table_scan(table_id, view.clone());\n    }\n\n    /// Record that a view performs a point index scan in this transaction's read set.\n    #[inline]\n    pub fn record_index_scan_point(\n        &mut self,\n        op: &FuncCallType,\n        table_id: TableId,\n        index_id: IndexId,\n        val: AlgebraicValue,\n    ) {\n        if let FuncCallType::View(view) = op {\n            self.record_index_scan_point_inner(view, table_id, index_id, val);\n        };\n    }\n\n    // This is cold as we don't want it to be inlined in case it doesn't end up getting called.\n    // This previously showed up in flamegraphs.\n    #[cold]\n    fn record_index_scan_point_inner(\n        &mut self,\n        view: &ViewCallInfo,\n        table_id: TableId,\n        index_id: IndexId,\n        val: AlgebraicValue,\n    ) {\n        // Fetch index metadata\n        let Some((_, idx, _)) = self.get_table_and_index(index_id) else {\n            return;\n        };\n\n        let cols = idx.index().indexed_columns.clone();\n        self.read_sets.insert_index_scan(table_id, cols, val, view.clone());\n    }\n\n    /// Returns the views whose read sets overlaps with this transaction's write set\n    pub fn views_for_refresh(&self) -> impl Iterator<Item = &ViewCallInfo> + '_ {\n        // Return early if there are no views.\n        // This is profitable as the method is also called for reducers.\n        if self.committed_state_write_lock.has_no_views_for_table_scans() {\n            return Either::Left(iter::empty());\n        }\n\n        let mut res = self\n            .tx_state\n            .insert_tables\n            .keys()\n            .filter(|table_id| !self.tx_state.delete_tables.contains_key(table_id))\n            .chain(self.tx_state.delete_tables.keys())\n            .flat_map(|table_id| self.committed_state_write_lock.views_for_table_scan(table_id))\n            .collect::<HashSet<_>>();\n\n        // Include views that perform precise index seeks.\n        let mut process_views = |table_id: &_, row_ref| {\n            for view_call in self.committed_state_write_lock.views_for_index_seek(table_id, row_ref) {\n                res.insert(view_call);\n            }\n        };\n\n        for (table_id, deleted_table) in &self.tx_state.delete_tables {\n            let (table, blob_store, _) = self\n                .committed_state_write_lock\n                .get_table_and_blob_store(*table_id)\n                .expect(\"table must exist in committed state for deleted table\");\n\n            if table.indexes.is_empty() {\n                continue;\n            }\n\n            for ptr in deleted_table.iter() {\n                if let Some(row_ref) = table.get_row_ref(blob_store, ptr) {\n                    process_views(table_id, row_ref);\n                }\n            }\n        }\n\n        for (table_id, inserted_table) in &self.tx_state.insert_tables {\n            let (table, blob_store, _) = self\n                .committed_state_write_lock\n                .get_table_and_blob_store(*table_id)\n                .expect(\"table must exist in committed state for inserted table\");\n\n            if table.indexes.is_empty() {\n                continue;\n            }\n\n            for row_ref in inserted_table.scan_rows(blob_store) {\n                process_views(table_id, row_ref);\n            }\n        }\n        Either::Right(res.into_iter())\n    }\n    /// Removes keys for `view_id` from the committed read set.\n    /// Used for dropping views in an auto-migration.\n    pub fn drop_view_from_committed_read_set(&mut self, view_id: ViewId) {\n        self.committed_state_write_lock.drop_view_from_read_sets(view_id, None)\n    }\n\n    /// Removes a specific view call from the committed read set.\n    pub fn drop_view_with_sender_from_committed_read_set(&mut self, view_id: ViewId, sender: Identity) {\n        self.committed_state_write_lock\n            .drop_view_from_read_sets(view_id, Some(sender))\n    }\n}\n\nimpl Datastore for MutTxId {\n    type TableIter<'a>\n        = IterMutTx<'a>\n    where\n        Self: 'a;\n\n    type RangeIndexIter<'a>\n        = IndexScanRanged<'a>\n    where\n        Self: 'a;\n\n    type PointIndexIter<'a>\n        = IndexScanPoint<'a>\n    where\n        Self: 'a;\n\n    fn row_count(&self, table_id: TableId) -> u64 {\n        self.table_row_count(table_id).unwrap_or_default()\n    }\n\n    fn table_scan<'a>(&'a self, table_id: TableId) -> anyhow::Result<Self::TableIter<'a>> {\n        Ok(self.iter(table_id)?)\n    }\n\n    fn index_scan_range<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> anyhow::Result<Self::RangeIndexIter<'a>> {\n        // Extract the table id, and commit/tx indices.\n        let (_, commit_index, tx_index) = self\n            .get_table_and_index(index_id)\n            .ok_or_else(|| IndexError::NotFound(index_id))?;\n\n        self.index_scan_range_inner(table_id, tx_index, commit_index, range)\n            .map_err(|IndexCannotSeekRange| IndexError::IndexCannotSeekRange(index_id).into())\n    }\n\n    fn index_scan_point<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        point: &AlgebraicValue,\n    ) -> anyhow::Result<Self::PointIndexIter<'a>> {\n        // Extract the table id, and commit/tx indices.\n        let (_, commit_index, tx_index) = self\n            .get_table_and_index(index_id)\n            .ok_or_else(|| IndexError::NotFound(index_id))?;\n\n        Ok(self.index_scan_point_inner(table_id, tx_index, commit_index, point))\n    }\n}\n\n/// Note, deltas are evaluated using read-only transactions, not mutable ones.\n/// Nevertheless this contract is still required for query evaluation.\nimpl DeltaStore for MutTxId {\n    fn num_inserts(&self, _: TableId) -> usize {\n        0\n    }\n\n    fn num_deletes(&self, _: TableId) -> usize {\n        0\n    }\n\n    fn inserts_for_table(&self, _: TableId) -> Option<std::slice::Iter<'_, ProductValue>> {\n        None\n    }\n\n    fn deletes_for_table(&self, _: TableId) -> Option<std::slice::Iter<'_, ProductValue>> {\n        None\n    }\n\n    /// Subscriptions are currently evaluated using read-only transactions.\n    /// Hence this will never be called on a mutable transaction.\n    fn index_scan_range_for_delta(\n        &self,\n        _: TableId,\n        _: IndexId,\n        _: spacetimedb_lib::query::Delta,\n        _: impl RangeBounds<AlgebraicValue>,\n    ) -> impl Iterator<Item = Row<'_>> {\n        std::iter::empty()\n    }\n\n    /// Subscriptions are currently evaluated using read-only transactions.\n    /// Hence this will never be called on a mutable transaction.\n    fn index_scan_point_for_delta(\n        &self,\n        _: TableId,\n        _: IndexId,\n        _: spacetimedb_lib::query::Delta,\n        _: &AlgebraicValue,\n    ) -> impl Iterator<Item = Row<'_>> {\n        std::iter::empty()\n    }\n}\n\nimpl MutDatastore for MutTxId {\n    fn insert_product_value(&mut self, table_id: TableId, row: &ProductValue) -> anyhow::Result<bool> {\n        Ok(match self.insert_via_serialize_bsatn(table_id, row)?.1 {\n            RowRefInsertion::Inserted(_) => true,\n            RowRefInsertion::Existed(_) => false,\n        })\n    }\n\n    fn delete_product_value(&mut self, table_id: TableId, row: &ProductValue) -> anyhow::Result<bool> {\n        Ok(self.delete_by_row_value(table_id, row)?)\n    }\n}\n\nimpl MutTxId {\n    /// Push a pending schema change.\n    fn push_schema_change(&mut self, change: PendingSchemaChange) {\n        self.tx_state.pending_schema_changes.push(change);\n    }\n\n    /// Get the list of current pending schema changes, for testing.\n    #[cfg(any(test, feature = \"test\"))]\n    pub fn pending_schema_changes(&self) -> &[PendingSchemaChange] {\n        &self.tx_state.pending_schema_changes\n    }\n\n    /// Deletes all the rows in table with `table_id`\n    /// where the column with `col_pos` equals `value`.\n    fn delete_col_eq(&mut self, table_id: TableId, col_pos: ColId, value: &AlgebraicValue) -> Result<()> {\n        let rows = self.iter_by_col_eq(table_id, col_pos, value)?;\n        let ptrs_to_delete = rows.map(|row_ref| row_ref.pointer()).collect::<Vec<_>>();\n\n        for ptr in ptrs_to_delete {\n            // TODO(error-handling,bikeshedding): Consider correct failure semantics here.\n            // We can't really roll back the operation,\n            // but we could conceivably attempt all the deletions rather than stopping at the first error.\n            self.delete(table_id, ptr)?;\n        }\n\n        Ok(())\n    }\n\n    /// Create a backing table for a view and update the system tables.\n    ///\n    /// Requires:\n    /// - Everything [`Self::create_table`] requires.\n    ///\n    /// Ensures:\n    /// - Everything [`Self::create_table`] ensures.\n    /// - The returned [`ViewId`] is unique and not [`ViewId::SENTINEL`].\n    /// - All view metadata maintained by the datastore is created atomically\n    pub fn create_view(&mut self, module_def: &ModuleDef, view_def: &ViewDef) -> Result<(ViewId, TableId)> {\n        let table_schema = TableSchema::from_view_def_for_datastore(module_def, view_def);\n        let table_id = self.create_table(table_schema)?;\n\n        let ViewDef {\n            name,\n            param_columns,\n            return_columns,\n            ..\n        } = view_def;\n\n        let view_name: RawIdentifier = name.clone().into();\n\n        // `create_table` inserts into `st_view` and updates the table schema.\n        let view_id = self\n            .view_id_from_name(&view_name)?\n            .ok_or(ViewError::NotFound(view_name))?;\n\n        self.insert_into_st_view_param(view_id, param_columns)?;\n        self.insert_into_st_view_column(view_id, return_columns)?;\n\n        self.committed_state_write_lock.ephemeral_tables.insert(table_id);\n\n        Ok((view_id, table_id))\n    }\n\n    /// Drop the backing table of a view and update the system tables.\n    pub fn drop_view(&mut self, view_id: ViewId) -> Result<()> {\n        let st_view_row = self.lookup_st_view(view_id)?;\n        // Drop the view's metadata\n        self.drop_st_view(view_id)?;\n        self.drop_st_view_param(view_id)?;\n        self.drop_st_view_column(view_id)?;\n        self.drop_st_view_sub(view_id)?;\n        self.drop_view_from_committed_read_set(view_id);\n\n        // Drop the view's backing table if materialized\n        if let StViewRow {\n            table_id: Some(table_id),\n            ..\n        } = st_view_row\n        {\n            return self.drop_table(table_id);\n        };\n\n        Ok(())\n    }\n\n    /// Create a table.\n    ///\n    /// Requires:\n    /// - All system IDs in the `table_schema` must be set to `SENTINEL`.\n    /// - All names in the `table_schema` must be unique among named entities in the database.\n    ///\n    /// Ensures:\n    /// - An in-memory insert table is created for the transaction, allowing the transaction to insert rows into the table.\n    /// - The table metadata is inserted into the system tables.\n    /// - The returned ID is unique and not `TableId::SENTINEL`.\n    pub fn create_table(&mut self, mut table_schema: TableSchema) -> Result<TableId> {\n        let matching_system_table_schema = system_tables().iter().find(|s| **s == table_schema).cloned();\n        if table_schema.table_id != TableId::SENTINEL && matching_system_table_schema.is_none() {\n            return Err(anyhow::anyhow!(\"`table_id` must be `TableId::SENTINEL` in `{table_schema:#?}`\").into());\n            // checks for children are performed in the relevant `create_...` functions.\n        }\n\n        let table_name = table_schema.table_name.clone();\n        log::trace!(\"TABLE CREATING: {table_name}\");\n\n        // Insert the table row into `st_tables`\n        // NOTE: Because `st_tables` has a unique index on `table_name`, this will\n        // fail if the table already exists.\n        let row = StTableRow {\n            table_id: table_schema.table_id,\n            table_name: table_name.clone(),\n            table_type: table_schema.table_type,\n            table_access: table_schema.table_access,\n            table_primary_key: table_schema.primary_key.map(Into::into),\n        };\n        let table_id = self\n            .insert_via_serialize_bsatn(ST_TABLE_ID, &row)?\n            .1\n            .collapse()\n            .read_col(StTableFields::TableId)?;\n\n        table_schema.update_table_id(table_id);\n        self.insert_st_table_accessor(&table_name, table_schema.alias.as_ref())?;\n\n        if let Some(info) = table_schema.view_info.as_mut() {\n            info.view_id = self.insert_into_st_view(table_name.clone(), table_id, true, info.is_anonymous)?;\n        }\n\n        // Generate the full definition of the table, with the generated indexes, constraints, sequences...\n\n        // Insert the columns into `st_column`.\n        self.insert_st_column(&table_name, table_schema.columns())?;\n\n        let schedule = table_schema.schedule.clone();\n        let is_event = table_schema.is_event;\n        let mut schema_internal = table_schema;\n        // Extract all indexes, constraints, and sequences from the schema.\n        // We will add them back later with correct ids.\n        let (indices, sequences, constraints) = schema_internal.take_adjacent_schemas();\n\n        // Create the in memory representation of the table\n        // NOTE: This should be done before creating the indexes\n        // NOTE: This `TableSchema` will be updated when we call `create_...` below.\n        //       This allows us to create the indexes, constraints, and sequences with the correct `index_id`, ...\n        self.create_table_internal(schema_internal.into());\n\n        // Insert the scheduled table entry into `st_scheduled`\n        if let Some(schedule) = schedule {\n            let row = StScheduledRow {\n                table_id: schedule.table_id,\n                schedule_id: schedule.schedule_id,\n                schedule_name: schedule.schedule_name,\n                reducer_name: schedule.function_name,\n                at_column: schedule.at_column,\n            };\n            let id = self\n                .insert_via_serialize_bsatn(ST_SCHEDULED_ID, &row)?\n                .1\n                .collapse()\n                .read_col::<ScheduleId>(StScheduledFields::ScheduleId)?;\n            let ((table, ..), _) = self.get_or_create_insert_table_mut(table_id)?;\n            table.with_mut_schema(|s| s.schedule.as_mut().unwrap().schedule_id = id);\n        }\n\n        // Insert into st_event_table if this is an event table.\n        if is_event {\n            let row = StEventTableRow { table_id };\n            self.insert_via_serialize_bsatn(ST_EVENT_TABLE_ID, &row)?;\n        }\n\n        // Create the indexes for the table.\n        for index in indices {\n            let col_set = ColSet::from(index.index_algorithm.columns());\n            let is_unique = constraints.iter().any(|c| c.data.unique_columns() == Some(&col_set));\n            self.create_index(index, is_unique)?;\n        }\n\n        // Insert constraints into `st_constraints`.\n        for constraint in constraints {\n            self.create_constraint(constraint)?;\n        }\n\n        // Insert sequences into `st_sequences`.\n        for seq in sequences {\n            self.create_sequence(seq)?;\n        }\n\n        log::trace!(\"TABLE CREATED: {table_name}, table_id: {table_id}\");\n\n        Ok(table_id)\n    }\n\n    /// Insert `columns` into `st_column`, and their accessors into `st_column_accessor`.\n    fn insert_st_column(&mut self, table_name: &TableName, columns: &[ColumnSchema]) -> Result<()> {\n        columns.iter().try_for_each(|col| {\n            let row: StColumnRow = col.clone().into();\n            self.insert_via_serialize_bsatn(ST_COLUMN_ID, &row)?;\n            self.insert_st_column_accessor(table_name, &col.col_name, col.alias.as_ref())?;\n            Ok(())\n        })\n    }\n\n    /// Insert a row into `st_table_accessor` for `table_name`, if an alias is present.\n    fn insert_st_table_accessor(&mut self, table_name: &TableName, alias: Option<&Identifier>) -> Result<()> {\n        let Some(accessor_name) = alias.cloned() else {\n            return Ok(());\n        };\n        let row = StTableAccessorRow {\n            table_name: table_name.clone(),\n            accessor_name,\n        };\n        self.insert_via_serialize_bsatn(ST_TABLE_ACCESSOR_ID, &row)?;\n        Ok(())\n    }\n\n    /// Insert a row into `st_column_accessor` for `(table_name, col_name)`, if an alias is present.\n    fn insert_st_column_accessor(\n        &mut self,\n        table_name: &TableName,\n        col_name: &Identifier,\n        alias: Option<&Identifier>,\n    ) -> Result<()> {\n        let Some(accessor_name) = alias.cloned() else {\n            return Ok(());\n        };\n        let row = StColumnAccessorRow {\n            table_name: table_name.clone(),\n            col_name: col_name.clone(),\n            accessor_name,\n        };\n        self.insert_via_serialize_bsatn(ST_COLUMN_ACCESSOR_ID, &row)?;\n        Ok(())\n    }\n\n    /// Insert a row into `st_index_accessor` for `index_name`, if an alias is present.\n    fn insert_st_index_accessor(&mut self, index_name: &RawIdentifier, alias: Option<&RawIdentifier>) -> Result<()> {\n        let Some(accessor_name) = alias.cloned() else {\n            return Ok(());\n        };\n        let row = StIndexAccessorRow {\n            index_name: index_name.clone(),\n            accessor_name,\n        };\n        self.insert_via_serialize_bsatn(ST_INDEX_ACCESSOR_ID, &row)?;\n        Ok(())\n    }\n\n    pub fn lookup_st_view(&self, view_id: ViewId) -> Result<StViewRow> {\n        let row = self\n            .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewId, &view_id.into())?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_view, view_id.into()))?;\n\n        StViewRow::try_from(row)\n    }\n\n    pub fn lookup_st_view_by_name(&self, view: &str) -> Result<StViewRow> {\n        let st_view_row = self\n            .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewName, &view.into())?\n            .next()\n            .unwrap();\n\n        StViewRow::try_from(st_view_row)\n    }\n\n    /// Check if view has parameters.\n    pub fn is_view_parameterized(&self, view_id: ViewId) -> Result<bool> {\n        let view_id = view_id.into();\n        let mut iter = self.iter_by_col_eq(ST_VIEW_PARAM_ID, StViewParamFields::ViewId, &view_id)?;\n        Ok(iter.next().is_some())\n    }\n\n    /// Insert a row into `st_view`, auto-increments and returns the [`ViewId`].\n    fn insert_into_st_view(\n        &mut self,\n        view_name: TableName,\n        table_id: TableId,\n        is_public: bool,\n        is_anonymous: bool,\n    ) -> Result<ViewId> {\n        Ok(self\n            .insert_via_serialize_bsatn(\n                ST_VIEW_ID,\n                &StViewRow {\n                    view_id: ViewId::SENTINEL,\n                    view_name,\n                    table_id: Some(table_id),\n                    is_public,\n                    is_anonymous,\n                },\n            )?\n            .1\n            .collapse()\n            .read_col(StViewFields::ViewId)?)\n    }\n\n    /// For each parameter of a view, insert a row into `st_view_param`.\n    /// This does not include the context parameter.\n    fn insert_into_st_view_param(&mut self, view_id: ViewId, params: &[ViewParamDef]) -> Result<()> {\n        for ViewParamDef { name, col_id, ty, .. } in params {\n            self.insert_via_serialize_bsatn(\n                ST_VIEW_PARAM_ID,\n                &StViewParamRow {\n                    view_id,\n                    param_pos: *col_id,\n                    param_name: name.clone().into(),\n                    param_type: ty.clone().into(),\n                },\n            )?;\n        }\n        Ok(())\n    }\n\n    /// For each column or field returned in a view, insert a row into `st_view_column`.\n    fn insert_into_st_view_column(&mut self, view_id: ViewId, columns: &[ViewColumnDef]) -> Result<()> {\n        for def in columns {\n            self.insert_via_serialize_bsatn(\n                ST_VIEW_COLUMN_ID,\n                &StViewColumnRow {\n                    view_id,\n                    col_pos: def.col_id,\n                    col_name: def.name.clone(),\n                    col_type: def.ty.clone().into(),\n                },\n            )?;\n        }\n        Ok(())\n    }\n\n    fn create_table_internal(&mut self, schema: Arc<TableSchema>) {\n        // Construct the in memory tables.\n        let table_id = schema.table_id;\n        let commit_table = Table::new(schema, SquashedOffset::COMMITTED_STATE);\n        let tx_table = commit_table.clone_structure(SquashedOffset::TX_STATE);\n\n        // Add them to the committed and tx states.\n        self.committed_state_write_lock.tables.insert(table_id, commit_table);\n        self.tx_state.insert_tables.insert(table_id, tx_table);\n\n        // Record that the committed state table is pending.\n        self.push_schema_change(PendingSchemaChange::TableAdded(table_id));\n    }\n\n    fn get_row_type(&self, table_id: TableId) -> Option<&ProductType> {\n        self.committed_state_write_lock\n            .get_table(table_id)\n            .map(|table| table.get_row_type())\n    }\n\n    pub fn row_type_for_table(&self, table_id: TableId) -> Result<RowTypeForTable<'_>> {\n        // Fetch the `ProductType` from the in memory table if it exists.\n        // The `ProductType` is invalidated if the schema of the table changes.\n        if let Some(row_type) = self.get_row_type(table_id) {\n            return Ok(RowTypeForTable::Ref(row_type));\n        }\n\n        // TODO(centril): if the table exists, this is now dead code,\n        // as we will immediately insert a table into the committed state upon creation.\n        // So simplify this and merge with `get_row_type`.\n        //\n        // Look up the columns for the table in question.\n        // NOTE: This is quite an expensive operation, although we only need\n        // to do this in situations where there is not currently an in memory\n        // representation of a table. This would happen in situations where\n        // we have created the table in the database, but have not yet\n        // represented in memory or inserted any rows into it.\n        Ok(RowTypeForTable::Arc(self.schema_for_table(table_id)?))\n    }\n\n    /// Drops all the columns of `table_id` in `st_column`.\n    fn drop_st_column(&mut self, table_id: TableId) -> Result<()> {\n        self.delete_col_eq(ST_COLUMN_ID, StColumnFields::TableId.col_id(), &table_id.into())\n    }\n\n    /// Drops rows in `st_column_accessor` for this canonical `table_name`.\n    fn drop_st_column_accessor(&mut self, table_name: &TableName) -> Result<()> {\n        let value = table_name.as_ref().into();\n        self.delete_col_eq(\n            ST_COLUMN_ACCESSOR_ID,\n            StColumnAccessorFields::TableName.col_id(),\n            &value,\n        )\n    }\n\n    /// Drops the row in `st_table` for this `table_id`\n    fn drop_st_table(&mut self, table_id: TableId) -> Result<()> {\n        self.delete_col_eq(ST_TABLE_ID, StTableFields::TableId.col_id(), &table_id.into())\n    }\n\n    /// Drops rows in `st_table_accessor` for this canonical `table_name`.\n    fn drop_st_table_accessor(&mut self, table_name: &TableName) -> Result<()> {\n        let value = table_name.as_ref().into();\n        self.delete_col_eq(ST_TABLE_ACCESSOR_ID, StTableAccessorFields::TableName.col_id(), &value)\n    }\n\n    /// Drops rows in `st_index_accessor` for this canonical `index_name`.\n    fn drop_st_index_accessor(&mut self, index_name: &RawIdentifier) -> Result<()> {\n        let value = index_name.as_ref().into();\n        self.delete_col_eq(ST_INDEX_ACCESSOR_ID, StIndexAccessorFields::IndexName.col_id(), &value)\n    }\n\n    /// Drops the row in `st_view` for this `view_id`\n    fn drop_st_view(&mut self, view_id: ViewId) -> Result<()> {\n        self.delete_col_eq(ST_VIEW_ID, StViewFields::ViewId.col_id(), &view_id.into())\n    }\n\n    /// Drops the rows in `st_view_param` for this `view_id`\n    fn drop_st_view_param(&mut self, view_id: ViewId) -> Result<()> {\n        self.delete_col_eq(ST_VIEW_PARAM_ID, StViewParamFields::ViewId.col_id(), &view_id.into())\n    }\n\n    /// Drops the rows in `st_view_column` for this `view_id`\n    fn drop_st_view_column(&mut self, view_id: ViewId) -> Result<()> {\n        self.delete_col_eq(ST_VIEW_COLUMN_ID, StViewColumnFields::ViewId.col_id(), &view_id.into())\n    }\n\n    /// Drops the rows in `st_view_sub` for this `view_id`\n    fn drop_st_view_sub(&mut self, view_id: ViewId) -> Result<()> {\n        self.delete_col_eq(ST_VIEW_SUB_ID, StViewSubFields::ViewId.col_id(), &view_id.into())\n    }\n\n    pub fn drop_table(&mut self, table_id: TableId) -> Result<()> {\n        self.clear_table(table_id)?;\n\n        let schema = &*self.schema_for_table(table_id)?;\n\n        for row in &schema.indexes {\n            self.drop_index(row.index_id)?;\n        }\n\n        for row in &schema.sequences {\n            self.drop_sequence(row.sequence_id)?;\n        }\n\n        for row in &schema.constraints {\n            self.drop_constraint(row.constraint_id)?;\n        }\n\n        // Drop the table and their columns\n        self.drop_st_table_accessor(&schema.table_name)?;\n        self.drop_st_column_accessor(&schema.table_name)?;\n        self.drop_st_table(table_id)?;\n        self.drop_st_column(table_id)?;\n\n        if let Some(schedule) = &schema.schedule {\n            self.delete_col_eq(\n                ST_SCHEDULED_ID,\n                StScheduledFields::ScheduleId.col_id(),\n                &schedule.schedule_id.into(),\n            )?;\n        }\n\n        // Delete the table from memory, both in the tx an committed states.\n        self.tx_state.insert_tables.remove(&table_id);\n        // No need to keep the delete tables.\n        // By seeing `PendingSchemaChange::TableRemoved`, `merge` knows that all rows were deleted.\n        self.tx_state.delete_tables.remove(&table_id);\n        let commit_table = self\n            .committed_state_write_lock\n            .tables\n            .remove(&table_id)\n            .expect(\"there should be a schema in the committed state if we reach here\");\n        self.push_schema_change(PendingSchemaChange::TableRemoved(table_id, commit_table));\n\n        Ok(())\n    }\n\n    // TODO(centril): remove this. It doesn't seem to be used by anything.\n    pub fn rename_table(&mut self, table_id: TableId, new_name: TableName) -> Result<()> {\n        // Update the table's name in st_tables.\n        self.update_st_table_row(table_id, |st| st.table_name = new_name)\n    }\n\n    fn update_st_table_row<R>(&mut self, table_id: TableId, updater: impl FnOnce(&mut StTableRow) -> R) -> Result<R> {\n        // Fetch the row.\n        let st_table_ref = self\n            .iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &table_id.into())?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_table, table_id.into()))?;\n        let mut row = StTableRow::try_from(st_table_ref)?;\n        let ptr = st_table_ref.pointer();\n\n        // Delete the row, run updates, and insert again.\n        self.delete(ST_TABLE_ID, ptr)?;\n        let ret = updater(&mut row);\n        self.insert_via_serialize_bsatn(ST_TABLE_ID, &row)?;\n\n        Ok(ret)\n    }\n\n    pub fn view_id_from_name(&self, view_name: &str) -> Result<Option<ViewId>> {\n        let view_name = &view_name.into();\n        let row = self\n            .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewName, view_name)?\n            .next();\n        Ok(row.map(|row| row.read_col(StViewFields::ViewId).unwrap()))\n    }\n\n    pub fn view_from_name(&self, view_name: &str) -> Result<Option<StViewRow>> {\n        let view_name = &view_name.into();\n        let row = self\n            .iter_by_col_eq(ST_VIEW_ID, StViewFields::ViewName, view_name)?\n            .next();\n        Ok(row.map(|row| row.try_into().expect(\"st_view row should be valid\")))\n    }\n\n    pub fn table_id_from_name(&self, table_name: &str) -> Result<Option<TableId>> {\n        let table_name = &table_name.into();\n        let row = self\n            .iter_by_col_eq(ST_TABLE_ID, StTableFields::TableName, table_name)?\n            .next();\n        Ok(row.map(|row| row.read_col(StTableFields::TableId).unwrap()))\n    }\n\n    pub fn table_name_from_id(&self, table_id: TableId) -> Result<Option<Box<str>>> {\n        self.iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &table_id.into())\n            .map(|mut iter| iter.next().map(|row| row.read_col(StTableFields::TableName).unwrap()))\n    }\n\n    /// Retrieves or creates the insert tx table for `table_id`.\n    #[allow(clippy::type_complexity)]\n    fn get_or_create_insert_table_mut(\n        &mut self,\n        table_id: TableId,\n    ) -> Result<(\n        TxTableForInsertion<'_>,\n        (&mut Table, &mut dyn BlobStore, &mut IndexIdMap),\n    )> {\n        let (commit_table, commit_bs, idx_map, _) =\n            self.committed_state_write_lock.get_table_and_blob_store_mut(table_id)?;\n        // Get the insert table, so we can write the row into it.\n        let tx = self\n            .tx_state\n            .get_table_and_blob_store_or_create_from(table_id, commit_table);\n\n        let commit = (commit_table, commit_bs, idx_map);\n\n        Ok((tx, commit))\n    }\n}\n\nimpl MutTxId {\n    /// Set the table access of `table_id` to `access`.\n    pub(crate) fn alter_table_access(&mut self, table_id: TableId, access: StAccess) -> Result<()> {\n        // Write to the table in the tx state.\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n        tx_table.with_mut_schema_and_clone(commit_table, |s| s.table_access = access);\n\n        // Update system tables.\n        let old_access = self.update_st_table_row(table_id, |st| mem::replace(&mut st.table_access, access))?;\n\n        // Remember the pending change so we can undo if necessary.\n        self.push_schema_change(PendingSchemaChange::TableAlterAccess(table_id, old_access));\n\n        Ok(())\n    }\n\n    /// Change the row type of the table identified by `table_id`.\n    ///\n    /// In practice, this should not error,\n    /// as the update machinery should disallow any incompatible change.\n    /// However, for redundancy and internal soundness of the datastore,\n    /// the compatibility is also checked here.\n    ///\n    /// A compatible change is restricted to adding variants to the tail of a sum type\n    /// while preserving the [`Layout`](spacetimedb_table::layout::Layout) of the row type.\n    /// For timer/scheduler tables, the `id` an `at` columns must not change at all.\n    /// All other changes, including reordering of columns, are incompatible.\n    pub(crate) fn alter_table_row_type(\n        &mut self,\n        table_id: TableId,\n        mut column_schemas: Vec<ColumnSchema>,\n    ) -> Result<()> {\n        // Write to the table in the tx state.\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n\n        // Ensure the columns have the right `table_id`.\n        // NOTE(centril): This should already be done by the update machinery,\n        // but do it redundantly here too, just in case.\n        // This is not performance critical, so we don't care that there is overhead.\n        column_schemas.iter_mut().for_each(|c| c.table_id = table_id);\n\n        // Try to change the tables into what we want.\n        let old_column_schemas = tx_table\n            .change_columns_to(column_schemas.clone())\n            .map_err(TableError::from)?;\n        // SAFETY: `commit_table` should have a schema identical to that of `tx_table`\n        // prior to changing it just now.\n        unsafe { commit_table.set_layout_and_schema_to(tx_table) };\n\n        // Update system tables.\n        // We'll simply remove all rows in `st_columns` and then add the new ones.\n        // The datastore takes care of not persisting any no-op delete/inserts to the commitlog.\n        let table_name = self.find_st_table_row(table_id)?.table_name;\n        self.drop_st_column(table_id)?;\n        self.drop_st_column_accessor(&table_name)?;\n        self.insert_st_column(&table_name, &column_schemas)?;\n\n        // Remember the pending change so we can undo if necessary.\n        self.push_schema_change(PendingSchemaChange::TableAlterRowType(table_id, old_column_schemas));\n\n        Ok(())\n    }\n\n    /// Change the row type of the table identified by `table_id`.\n    ///\n    /// This is an incompatible change that requires a new table to be created.\n    /// The existing rows are copied over to the new table,\n    /// with `default_values` appended to each row.\n    ///\n    /// `column_schemas` must contain all the old columns in same order as before,\n    /// followed by the new columns to be added.\n    /// All new columns must have a default value in `default_values`.\n    ///\n    /// After calling this method, Table referred by `table_id` is dropped,\n    /// and a new table is created with the same name but a new `table_id`.\n    /// The new `table_id` is returned.\n    pub(crate) fn add_columns_to_table(\n        &mut self,\n        table_id: TableId,\n        column_schemas: Vec<ColumnSchema>,\n        mut default_values: Vec<AlgebraicValue>,\n    ) -> Result<TableId> {\n        let original_table_schema: TableSchema = (*self.schema_for_table(table_id)?).clone();\n\n        // Only keep the default values of new columns.\n        let new_cols = column_schemas\n            .len()\n            .checked_sub(original_table_schema.columns.len())\n            .ok_or_else(|| {\n                anyhow::anyhow!(\"new column schemas must be more than existing ones for table_id: {table_id}\")\n            })?;\n        let older_defaults = default_values.len().checked_sub(new_cols).ok_or_else(|| {\n            anyhow::anyhow!(\"not enough default values provided for new columns for table_id: {table_id}\")\n        })?;\n        default_values.drain(..older_defaults);\n\n        let ((tx_table, ..), _) = self.get_or_create_insert_table_mut(table_id)?;\n\n        tx_table\n            .validate_add_columns_schema(&column_schemas, &default_values)\n            .map_err(TableError::from)?;\n\n        // Copy all rows as `ProductValue` before dropping the table.\n        // This approach isn't ideal, as allocating a large contiguous block of memory\n        // can lead to memory overflow errors.\n        // Ideally, we should use iterators to avoid this, but that's not sufficient on its own.\n        // `CommittedState::merge_apply_inserts` also allocates a similar `Vec` to hold the data.\n        // So, if we really find this problematic in practice, this should be fixed in both places.\n        let mut table_rows: Vec<ProductValue> = iter(&self.tx_state, &self.committed_state_write_lock, table_id)?\n            .map(|r| r.to_product_value())\n            .collect();\n\n        log::debug!(\n            \"ADDING TABLE COLUMN (incompatible layout): {}, table_id: {}\",\n            original_table_schema.table_name,\n            table_id\n        );\n\n        // Store sequence values to restore them later with new table.\n        // Using a map from name to value as the new sequence ids will be different.\n        // and I am not sure if we should rely on the order of sequences in the table schema.\n        let seq_values: HashMap<_, i128> = original_table_schema\n            .sequences\n            .iter()\n            .map(|s| {\n                (\n                    s.sequence_name.clone(),\n                    self.sequence_state_lock\n                        .get_sequence_mut(s.sequence_id)\n                        .expect(\"sequence exists in original schema and should in sequence state.\")\n                        .get_value(),\n                )\n            })\n            .collect();\n\n        // Drop existing table first due to unique constraints on table name in `st_table`\n        self.drop_table(table_id)?;\n\n        // Update existing (dropped) table schema with provided columns, reset Ids.\n        let mut updated_table_schema = original_table_schema;\n        updated_table_schema.columns = column_schemas;\n        updated_table_schema.reset();\n        let new_table_id = self.create_table_and_update_seq(updated_table_schema, seq_values)?;\n\n        // Populate rows with default values for new columns\n        for product_value in table_rows.iter_mut() {\n            let mut row_elements = product_value.elements.to_vec();\n            row_elements.extend(default_values.iter().cloned());\n            product_value.elements = row_elements.into();\n        }\n\n        let (new_table, tx_blob_store) = self\n            .tx_state\n            .get_table_and_blob_store(new_table_id)\n            .ok_or(TableError::IdNotFoundState(new_table_id))?;\n\n        for row in table_rows {\n            new_table.insert(&self.committed_state_write_lock.page_pool, tx_blob_store, &row)?;\n        }\n\n        Ok(new_table_id)\n    }\n\n    fn create_table_and_update_seq(\n        &mut self,\n        table_schema: TableSchema,\n        seq_values: HashMap<RawIdentifier, i128>,\n    ) -> Result<TableId> {\n        let table_id = self.create_table(table_schema)?;\n        let table_schema = self.schema_for_table(table_id)?;\n\n        for seq in table_schema.sequences.iter() {\n            let new_seq = self\n                .sequence_state_lock\n                .get_sequence_mut(seq.sequence_id)\n                .expect(\"sequence just created\");\n            let value = *seq_values\n                .get(&seq.sequence_name)\n                .ok_or_else(|| SequenceError::NotFound(seq.sequence_id))?;\n            new_seq.update_value(value);\n        }\n\n        Ok(table_id)\n    }\n\n    /// Create an index.\n    ///\n    /// Requires:\n    /// - `index.index_name` must not be used for any other database entity.\n    /// - `index.index_id == IndexId::SENTINEL`\n    /// - `index.table_id != TableId::SENTINEL`\n    /// - `is_unique` must be `true` if and only if a unique constraint will exist on\n    ///   `ColSet::from(&index.index_algorithm.columns())` after this transaction is committed.\n    ///\n    /// Ensures:\n    /// - The index metadata is inserted into the system tables (and other data structures reflecting them).\n    /// - The returned ID is unique and is not `IndexId::SENTINEL`.\n    pub fn create_index(&mut self, mut index_schema: IndexSchema, is_unique: bool) -> Result<IndexId> {\n        let table_id = index_schema.table_id;\n        if table_id == TableId::SENTINEL {\n            return Err(anyhow::anyhow!(\"`table_id` must not be `TableId::SENTINEL` in `{index_schema:#?}`\").into());\n        }\n\n        log::trace!(\n            \"INDEX CREATING: {} for table: {} and algorithm: {:?}\",\n            index_schema.index_name,\n            table_id,\n            index_schema.index_algorithm\n        );\n        if self.table_name(table_id).is_none() {\n            return Err(TableError::IdNotFoundState(table_id).into());\n        }\n\n        // Insert the index row into `st_indexes` and write back the `IndexId`.\n        // NOTE: Because `st_indexes` has a unique index on `index_name`,\n        // this will fail if the index already exists.\n        let row: StIndexRow = index_schema.clone().into();\n        let index_id = self\n            .insert_via_serialize_bsatn(ST_INDEX_ID, &row)?\n            .1\n            .collapse()\n            .read_col(StIndexFields::IndexId)?;\n        index_schema.index_id = index_id;\n        self.insert_st_index_accessor(&index_schema.index_name, index_schema.alias.as_ref())?;\n\n        // Add the index to the transaction's insert table.\n        let ((table, blob_store, delete_table), (commit_table, commit_blob_store, idx_map)) =\n            self.get_or_create_insert_table_mut(table_id)?;\n\n        // Create and build the indices.\n        let map_violation = |violation, index: &TableIndex, table: &Table, bs: &dyn BlobStore| {\n            let violation = table\n                .get_row_ref(bs, violation)\n                .expect(\"row came from scanning the table\")\n                .project(&index.indexed_columns)\n                .expect(\"`cols` should consist of valid columns for this table\");\n\n            let schema = table.get_schema();\n            let violation = UniqueConstraintViolation::build_with_index_schema(schema, index, &index_schema, violation);\n            IndexError::from(violation).into()\n        };\n        // Builds the index and ensures that `table`'s row won't cause a unique constraint violation\n        // due to the existing rows having the same value for some column(s).\n        let build_from_rows = |index: &mut TableIndex, table: &Table, bs: &dyn BlobStore| -> Result<()> {\n            let rows = table.scan_rows(bs);\n            // SAFETY: (1) `tx_index` / `commit_index` was derived from `table` / `commit_table`\n            // which in turn was derived from `commit_table`.\n            let violation = unsafe { index.build_from_rows(rows) };\n            violation.map_err(|v| map_violation(v, index, table, bs))\n        };\n        // Build the tx index.\n        let mut tx_index = table.new_index(&index_schema.index_algorithm, is_unique)?;\n        build_from_rows(&mut tx_index, table, blob_store)?;\n        // Build the commit index.\n        let mut commit_index = tx_index.clone_structure();\n        build_from_rows(&mut commit_index, commit_table, commit_blob_store)?;\n        // Make sure the two indices can be merged.\n        let is_deleted = |ptr: &RowPointer| delete_table.contains(*ptr);\n        commit_index\n            .can_merge(&tx_index, is_deleted)\n            .map_err(|v| map_violation(v, &commit_index, commit_table, commit_blob_store))?;\n\n        log::trace!(\n            \"INDEX CREATED: {} for table: {} and algorithm: {:?}\",\n            index_id,\n            table_id,\n            index_schema.index_algorithm\n        );\n\n        // Associate `index_id -> table_id` for fast lookup.\n        idx_map.insert(index_id, table_id);\n        // SAFETY: same as (1).\n        unsafe { table.add_index(index_id, tx_index) };\n        let pointer_map = unsafe { commit_table.add_index(index_id, commit_index) };\n        // Update the table's schema.\n        table.with_mut_schema_and_clone(commit_table, |s| s.indexes.push(index_schema.clone()));\n        // Note the index in pending schema changes.\n        self.push_schema_change(PendingSchemaChange::IndexAdded(table_id, index_id, pointer_map));\n\n        Ok(index_id)\n    }\n\n    pub fn drop_index(&mut self, index_id: IndexId) -> Result<()> {\n        log::trace!(\"INDEX DROPPING: {index_id}\");\n        // Find the index in `st_indexes`.\n        let st_index_ref = self\n            .iter_by_col_eq(ST_INDEX_ID, StIndexFields::IndexId, &index_id.into())?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_index, index_id.into()))?;\n        let st_index_row = StIndexRow::try_from(st_index_ref)?;\n        let st_index_ptr = st_index_ref.pointer();\n        let table_id = st_index_row.table_id;\n\n        // Remove the index from st_indexes.\n        self.delete(ST_INDEX_ID, st_index_ptr)?;\n        self.drop_st_index_accessor(&st_index_row.index_name)?;\n\n        // Remove the index in the transaction's insert table and the commit table.\n        let ((tx_table, tx_bs, _), (commit_table, commit_bs, idx_map)) =\n            self.get_or_create_insert_table_mut(table_id)?;\n        tx_table.delete_index(tx_bs, index_id, None);\n        let commit_index = commit_table\n            .delete_index(commit_bs, index_id, None)\n            .expect(\"there should be a schema in the committed state if we reach here\");\n\n        // Remove index from schema.\n        let index_schema = commit_table\n            .with_mut_schema_and_clone(tx_table, |s| s.remove_index(index_id))\n            .expect(\"there should be an index with `index_id`\");\n\n        // Remove the `index_id -> (table_id, col_list)` association.\n        idx_map.remove(&index_id);\n        // Note the index in pending schema changes.\n        self.push_schema_change(PendingSchemaChange::IndexRemoved(\n            table_id,\n            index_id,\n            commit_index,\n            index_schema,\n        ));\n\n        log::trace!(\"INDEX DROPPED: {index_id}\");\n        Ok(())\n    }\n\n    pub fn index_id_from_name(&self, index_name: &str) -> Result<Option<IndexId>> {\n        let name = &index_name.into();\n        let row = self.iter_by_col_eq(ST_INDEX_ID, StIndexFields::IndexName, name)?.next();\n        Ok(row.map(|row| row.read_col(StIndexFields::IndexId).unwrap()))\n    }\n\n    /// Looks up a index id by the index's canonical name or its accessor/alias name.\n    pub fn index_id_from_name_or_alias(&self, index_name_or_alias: &str) -> Result<Option<IndexId>> {\n        if let Some(index_id) = self.index_id_from_name(index_name_or_alias)? {\n            return Ok(Some(index_id));\n        }\n        let Some(row) = self.find_st_index_accessor_row(index_name_or_alias)? else {\n            return Ok(None);\n        };\n        self.index_id_from_name(&row.index_name)\n    }\n\n    /// Returns an iterator yielding rows by performing a point index scan\n    /// on the index identified by `index_id`.\n    pub fn index_scan_point<'a>(\n        &'a self,\n        index_id: IndexId,\n        mut point: &[u8],\n    ) -> Result<(TableId, AlgebraicValue, IndexScanPoint<'a>)> {\n        // Extract the table id, and commit/tx indices.\n        let (table_id, commit_index, tx_index) = self\n            .get_table_and_index(index_id)\n            .ok_or_else(|| IndexError::NotFound(index_id))?;\n        // Extract the index type.\n        let index_ty = &commit_index.index().key_type;\n\n        // We have the index key type, so we can decode the key.\n        let index_ty = WithTypespace::empty(index_ty);\n        let point = index_ty\n            .deserialize(Deserializer::new(&mut point))\n            .map_err(IndexError::Decode)?;\n\n        // Get an index seek iterator for the tx and committed state.\n        let tx_iter = tx_index.map(|i| i.seek_point(&point));\n        let commit_iter = commit_index.seek_point(&point);\n\n        let dt = self.tx_state.get_delete_table(table_id);\n        let iter = ScanMutTx::combine(dt, tx_iter, commit_iter);\n\n        Ok((table_id, point, iter))\n    }\n\n    /// See [`MutTxId::index_scan_point`].\n    fn index_scan_point_inner<'a>(\n        &'a self,\n        table_id: TableId,\n        tx_index: Option<TableAndIndex<'a>>,\n        commit_index: TableAndIndex<'a>,\n        point: &AlgebraicValue,\n    ) -> IndexScanPoint<'a> {\n        // Get an index seek iterator for the tx and committed state.\n        let tx_iter = tx_index.map(|i| i.seek_point(point));\n        let commit_iter = commit_index.seek_point(point);\n\n        // Combine it all.\n        let dt = self.tx_state.get_delete_table(table_id);\n        ScanMutTx::combine(dt, tx_iter, commit_iter)\n    }\n\n    /// Returns an iterator yielding rows by performing a range index scan\n    /// on the range-scan-compatible index identified by `index_id`.\n    ///\n    /// The `prefix` is equated to the first `prefix_elems` values of the index key\n    /// and then `prefix_elem`th value is bounded to the left bys `rstart`\n    /// and to the right by `rend`.\n    pub fn index_scan_range<'a>(\n        &'a self,\n        index_id: IndexId,\n        prefix: &[u8],\n        prefix_elems: ColId,\n        rstart: &[u8],\n        rend: &[u8],\n    ) -> Result<(\n        TableId,\n        Bound<AlgebraicValue>,\n        Bound<AlgebraicValue>,\n        IndexScanRanged<'a>,\n    )> {\n        // Extract the table id, and commit/tx indices.\n        let (table_id, commit_index, tx_index) = self\n            .get_table_and_index(index_id)\n            .ok_or_else(|| IndexError::NotFound(index_id))?;\n        // Extract the index type.\n        let index_ty = &commit_index.index().key_type;\n\n        // We have the index key type, so we can decode everything.\n        let bounds =\n            Self::range_scan_decode_bounds(index_ty, prefix, prefix_elems, rstart, rend).map_err(IndexError::Decode)?;\n\n        let iter = self\n            .index_scan_range_inner(table_id, tx_index, commit_index, &bounds)\n            .map_err(|IndexCannotSeekRange| IndexError::IndexCannotSeekRange(index_id))?;\n\n        let (lower, upper) = bounds;\n        Ok((table_id, lower, upper, iter))\n    }\n\n    /// See [`MutTxId::index_scan_range`].\n    #[inline(always)]\n    fn index_scan_range_inner<'a>(\n        &'a self,\n        table_id: TableId,\n        tx_index: Option<TableAndIndex<'a>>,\n        commit_index: TableAndIndex<'a>,\n        bounds: &impl RangeBounds<AlgebraicValue>,\n    ) -> IndexSeekRangeResult<IndexScanRanged<'a>> {\n        // Get an index seek iterator for the tx and committed state.\n        let tx_iter = tx_index.map(|i| i.seek_range(bounds)).transpose();\n        let commit_iter = commit_index.seek_range(bounds);\n\n        // If we don't have a range-capable index, return an error.\n        let (tx_iter, commit_iter) = match (tx_iter, commit_iter) {\n            (Ok(t), Ok(c)) => (t, c),\n            (Err(e), _) | (_, Err(e)) => return Err(e),\n        };\n\n        // Combine it all.\n        let dt = self.tx_state.get_delete_table(table_id);\n        Ok(ScanMutTx::combine(dt, tx_iter, commit_iter))\n    }\n\n    /// Translate `index_id` to the table id, and commit/tx indices.\n    fn get_table_and_index(\n        &self,\n        index_id: IndexId,\n    ) -> Option<(TableId, TableAndIndex<'_>, Option<TableAndIndex<'_>>)> {\n        // Figure out what table the index belongs to.\n        let table_id = self.committed_state_write_lock.get_table_for_index(index_id)?;\n\n        // Find the index for the commit state.\n        // If we cannot find it, there's a bug.\n        let commit_index = self\n            .committed_state_write_lock\n            .get_index_by_id_with_table(table_id, index_id)?;\n\n        // Find the index for the tx state, if any.\n        let tx_index = self.tx_state.get_index_by_id_with_table(table_id, index_id);\n\n        Some((table_id, commit_index, tx_index))\n    }\n\n    /// Decode the bounds for a ranged index scan for an index typed at `key_type`.\n    fn range_scan_decode_bounds(\n        key_type: &AlgebraicType,\n        mut prefix: &[u8],\n        prefix_elems: ColId,\n        rstart: &[u8],\n        rend: &[u8],\n    ) -> DecodeResult<(Bound<AlgebraicValue>, Bound<AlgebraicValue>)> {\n        match key_type {\n            // Multi-column index case.\n            AlgebraicType::Product(key_types) => {\n                let key_types = &key_types.elements;\n                // Split into types for the prefix and for the rest.\n                let (prefix_types, rest_types) = key_types\n                    .split_at_checked(prefix_elems.idx())\n                    .ok_or_else(|| DecodeError::Other(\"index key type has too few fields compared to prefix\".into()))?;\n\n                // The `rstart` and `rend`s must be typed at `Bound<range_type>`.\n                // Extract that type and determine the length of the suffix.\n                let Some((range_type, suffix_types)) = rest_types.split_first() else {\n                    return Err(DecodeError::Other(\n                        \"prefix length leaves no room for a range in ranged index scan\".into(),\n                    ));\n                };\n                let suffix_len = suffix_types.len();\n\n                // We now have the types,\n                // so proceed to decoding the prefix, and the start/end bounds.\n                // Finally combine all of these to a single bound pair.\n                let prefix = bsatn::decode(prefix_types, &mut prefix)?;\n                let (start, end) = Self::range_scan_decode_start_end(&range_type.algebraic_type, rstart, rend)?;\n                Ok(Self::range_scan_combine_prefix_and_bounds(\n                    prefix, start, end, suffix_len,\n                ))\n            }\n            // Single-column index case. We implicitly have a PT of len 1.\n            _ if !prefix.is_empty() && prefix_elems.idx() != 0 => Err(DecodeError::Other(\n                \"a single-column index cannot be prefix scanned\".into(),\n            )),\n            ty => Self::range_scan_decode_start_end(ty, rstart, rend),\n        }\n    }\n\n    /// Decode `rstart` and `rend` as `Bound<ty>`.\n    fn range_scan_decode_start_end(\n        ty: &AlgebraicType,\n        mut rstart: &[u8],\n        mut rend: &[u8],\n    ) -> DecodeResult<(Bound<AlgebraicValue>, Bound<AlgebraicValue>)> {\n        let range_type = WithBound(WithTypespace::empty(ty));\n        let range_start = range_type.deserialize(Deserializer::new(&mut rstart))?;\n        let range_end = range_type.deserialize(Deserializer::new(&mut rend))?;\n        Ok((range_start, range_end))\n    }\n\n    /// Combines `prefix` equality constraints with `start` and `end` bounds\n    /// filling with `suffix_len` to ensure that the number of fields matches\n    /// that of the index type.\n    fn range_scan_combine_prefix_and_bounds(\n        prefix: ProductValue,\n        start: Bound<AlgebraicValue>,\n        end: Bound<AlgebraicValue>,\n        suffix_len: usize,\n    ) -> (Bound<AlgebraicValue>, Bound<AlgebraicValue>) {\n        let prefix_is_empty = prefix.elements.is_empty();\n        // Concatenate prefix, value, and the most permissive value for the suffix.\n        let concat = |prefix: ProductValue, val, fill| {\n            let mut vals: Vec<_> = prefix.elements.into();\n            vals.reserve(1 + suffix_len);\n            vals.push(val);\n            vals.extend(iter::repeat_n(fill, suffix_len));\n            AlgebraicValue::product(vals)\n        };\n        // The start endpoint needs `Min` as the suffix-filling element,\n        // as it imposes the least and acts like `Unbounded`.\n        let concat_start = |val| concat(prefix.clone(), val, AlgebraicValue::Min);\n        let range_start = match start {\n            Bound::Included(r) => Bound::Included(concat_start(r)),\n            Bound::Excluded(r) => Bound::Excluded(concat_start(r)),\n            // Prefix is empty, and suffix will be `Min`,\n            // so simplify `(Min, Min, ...)` to `Unbounded`.\n            Bound::Unbounded if prefix_is_empty => Bound::Unbounded,\n            Bound::Unbounded => Bound::Included(concat_start(AlgebraicValue::Min)),\n        };\n        // The end endpoint needs `Max` as the suffix-filling element,\n        // as it imposes the least and acts like `Unbounded`.\n        let concat_end = |val| concat(prefix, val, AlgebraicValue::Max);\n        let range_end = match end {\n            Bound::Included(r) => Bound::Included(concat_end(r)),\n            Bound::Excluded(r) => Bound::Excluded(concat_end(r)),\n            // Prefix is empty, and suffix will be `Max`,\n            // so simplify `(Max, Max, ...)` to `Unbounded`.\n            Bound::Unbounded if prefix_is_empty => Bound::Unbounded,\n            Bound::Unbounded => Bound::Included(concat_end(AlgebraicValue::Max)),\n        };\n        (range_start, range_end)\n    }\n\n    pub fn get_next_sequence_value(&mut self, seq_id: SequenceId) -> Result<i128> {\n        get_next_sequence_value(\n            &mut self.tx_state,\n            &self.committed_state_write_lock,\n            &mut self.sequence_state_lock,\n            seq_id,\n        )\n    }\n}\n\nfn get_sequence_mut(seq_state: &mut SequencesState, seq_id: SequenceId) -> Result<&mut Sequence> {\n    seq_state\n        .get_sequence_mut(seq_id)\n        .ok_or_else(|| SequenceError::NotFound(seq_id).into())\n}\n\nfn get_next_sequence_value(\n    tx_state: &mut TxState,\n    committed_state: &CommittedState,\n    seq_state: &mut SequencesState,\n    seq_id: SequenceId,\n) -> Result<i128> {\n    {\n        let sequence = get_sequence_mut(seq_state, seq_id)?;\n\n        // If there are allocated sequence values, return the new value.\n        // `gen_next_value` internally checks that the new allocation is acceptable,\n        // i.e. is less than or equal to the allocation amount.\n        // Note that on restart we start one after the allocation amount.\n        if let Some(value) = sequence.gen_next_value() {\n            return Ok(value);\n        }\n    }\n\n    // Allocate new sequence values\n    // If we're out of allocations, then update the sequence row in st_sequences to allocate a fresh batch of sequences.\n    let old_seq_row_ref = iter_by_col_eq(\n        tx_state,\n        committed_state,\n        ST_SEQUENCE_ID,\n        StSequenceFields::SequenceId,\n        &seq_id.into(),\n    )?\n    .last()\n    .unwrap();\n    let old_seq_row_ptr = old_seq_row_ref.pointer();\n    let seq_row = {\n        let mut seq_row = StSequenceRow::try_from(old_seq_row_ref)?;\n\n        let sequence = get_sequence_mut(seq_state, seq_id)?;\n        let new_allocated = sequence.allocate_steps(SEQUENCE_ALLOCATION_STEP as usize);\n        seq_row.allocated = new_allocated;\n        seq_row\n    };\n\n    delete(tx_state, committed_state, ST_SEQUENCE_ID, old_seq_row_ptr)?;\n    // `insert::<GENERATE = false>` rather than `GENERATE = true` because:\n    // - We have already checked unique constraints during `create_sequence`.\n    // - Similarly, we have already applied autoinc sequences.\n    // - We do not want to apply autoinc sequences again,\n    //   since the system table sequence `seq_st_table_table_id_primary_key_auto`\n    //   has ID 0, and would otherwise trigger autoinc.\n    with_sys_table_buf(|buf| {\n        to_writer(buf, &seq_row).unwrap();\n        insert::<false>(tx_state, committed_state, seq_state, ST_SEQUENCE_ID, buf)\n    })?;\n\n    get_sequence_mut(seq_state, seq_id)?\n        .gen_next_value()\n        .ok_or_else(|| SequenceError::UnableToAllocate(seq_id).into())\n}\n\nimpl MutTxId {\n    /// Create a sequence.\n    /// Requires:\n    /// - `seq.sequence_id == SequenceId::SENTINEL`\n    /// - `seq.table_id != TableId::SENTINEL`\n    /// - `seq.sequence_name` must not be used for any other database entity.\n    ///\n    /// Ensures:\n    /// - The sequence metadata is inserted into the system tables (and other data structures reflecting them).\n    /// - The returned ID is unique and not `SequenceId::SENTINEL`.\n    pub fn create_sequence(&mut self, seq: SequenceSchema) -> Result<SequenceId> {\n        if seq.table_id == TableId::SENTINEL {\n            return Err(anyhow::anyhow!(\"`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`\").into());\n        }\n\n        let table_id = seq.table_id;\n        let matching_system_table_schema = system_tables().iter().find(|s| s.table_id == table_id).cloned();\n\n        if seq.sequence_id != SequenceId::SENTINEL && matching_system_table_schema.is_none() {\n            return Err(anyhow::anyhow!(\"`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`\", seq).into());\n        }\n\n        let sequence_id = seq.sequence_id;\n\n        log::trace!(\n            \"SEQUENCE CREATING: {} for table: {} and col: {}\",\n            seq.sequence_name,\n            table_id,\n            seq.col_pos\n        );\n\n        // Insert the sequence row into st_sequences\n        // NOTE: Because st_sequences has a unique index on sequence_name, this will\n        // fail if the table already exists.\n        let mut sequence_row = StSequenceRow {\n            sequence_id,\n            sequence_name: seq.sequence_name,\n            table_id,\n            col_pos: seq.col_pos,\n            allocated: seq.start,\n            increment: seq.increment,\n            start: seq.start,\n            min_value: seq.min_value,\n            max_value: seq.max_value,\n        };\n        let row = self.insert_via_serialize_bsatn(ST_SEQUENCE_ID, &sequence_row)?;\n        let seq_id = row.1.collapse().read_col(StSequenceFields::SequenceId)?;\n        sequence_row.sequence_id = seq_id;\n\n        let schema: SequenceSchema = sequence_row.into();\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n        // This won't clone-write when creating a table but likely to otherwise.\n        tx_table.with_mut_schema_and_clone(commit_table, |s| s.update_sequence(schema.clone()));\n        self.sequence_state_lock.insert(Sequence::new(schema, None));\n        self.push_schema_change(PendingSchemaChange::SequenceAdded(table_id, seq_id));\n\n        log::trace!(\"SEQUENCE CREATED: id = {seq_id}\");\n\n        Ok(seq_id)\n    }\n\n    pub fn drop_sequence(&mut self, sequence_id: SequenceId) -> Result<()> {\n        // Ensure the sequence exists.\n        let st_sequence_ref = self\n            .iter_by_col_eq(ST_SEQUENCE_ID, StSequenceFields::SequenceId, &sequence_id.into())?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_sequence, sequence_id.into()))?;\n        let table_id = st_sequence_ref.read_col(StSequenceFields::TableId)?;\n\n        // Delete from system tables.\n        self.delete(ST_SEQUENCE_ID, st_sequence_ref.pointer())?;\n\n        // Drop the sequence from in-memory tables.\n        let sequence = self\n            .sequence_state_lock\n            .remove(sequence_id)\n            .expect(\"there should be a sequence in the committed state if we reach here\");\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n        // This likely will do a clone-write as over time?\n        // The schema might have found other referents.\n        let schema = commit_table\n            .with_mut_schema_and_clone(tx_table, |s| s.remove_sequence(sequence_id))\n            .expect(\"there should be a schema in the committed state if we reach here\");\n        self.push_schema_change(PendingSchemaChange::SequenceRemoved(table_id, sequence, schema));\n\n        Ok(())\n    }\n\n    pub fn sequence_id_from_name(&self, seq_name: &str) -> Result<Option<SequenceId>> {\n        let name = &<Box<str>>::from(seq_name).into();\n        self.iter_by_col_eq(ST_SEQUENCE_ID, StSequenceFields::SequenceName, name)\n            .map(|mut iter| {\n                iter.next()\n                    .map(|row| row.read_col(StSequenceFields::SequenceId).unwrap())\n            })\n    }\n\n    /// Create a constraint.\n    ///\n    /// Requires:\n    /// - `constraint.constraint_name` must not be used for any other database entity.\n    /// - `constraint.constraint_id == ConstraintId::SENTINEL`\n    /// - `constraint.table_id != TableId::SENTINEL`\n    /// - `is_unique` must be `true` if and only if a unique constraint will exist on\n    ///   `ColSet::from(&constraint.constraint_algorithm.columns())` after this transaction is committed.\n    ///\n    /// Ensures:\n    /// - The constraint metadata is inserted into the system tables (and other data structures reflecting them).\n    /// - The returned ID is unique and is not `constraintId::SENTINEL`.\n    fn create_constraint(&mut self, mut constraint: ConstraintSchema) -> Result<ConstraintId> {\n        if constraint.table_id == TableId::SENTINEL {\n            return Err(anyhow::anyhow!(\"`table_id` must not be `TableId::SENTINEL` in `{constraint:#?}`\").into());\n        }\n\n        let table_id = constraint.table_id;\n\n        log::trace!(\n            \"CONSTRAINT CREATING: {} for table: {} and data: {:?}\",\n            constraint.constraint_name,\n            table_id,\n            constraint.data\n        );\n\n        // Insert the constraint row into `st_constraint`.\n        // NOTE: Because `st_constraint` has a unique index on constraint_name,\n        // this will fail if the table already exists.\n        let constraint_row = StConstraintRow {\n            table_id,\n            constraint_id: constraint.constraint_id,\n            constraint_name: constraint.constraint_name.clone(),\n            constraint_data: constraint.data.clone().into(),\n        };\n\n        let constraint_row = self.insert_via_serialize_bsatn(ST_CONSTRAINT_ID, &constraint_row)?;\n        let constraint_id = constraint_row.1.collapse().read_col(StConstraintFields::ConstraintId)?;\n        if let RowRefInsertion::Existed(_) = constraint_row.1 {\n            log::trace!(\"CONSTRAINT ALREADY EXISTS: {constraint_id}\");\n            return Ok(constraint_id);\n        }\n\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n        constraint.constraint_id = constraint_id;\n        // This won't clone-write when creating a table but likely to otherwise.\n        tx_table.with_mut_schema_and_clone(commit_table, |s| s.update_constraint(constraint.clone()));\n        self.push_schema_change(PendingSchemaChange::ConstraintAdded(table_id, constraint_id));\n\n        log::trace!(\"CONSTRAINT CREATED: {constraint_id}\");\n        Ok(constraint_id)\n    }\n\n    pub fn drop_constraint(&mut self, constraint_id: ConstraintId) -> Result<()> {\n        // Delete row in `st_constraint`.\n        let st_constraint_ref = self\n            .iter_by_col_eq(\n                ST_CONSTRAINT_ID,\n                StConstraintFields::ConstraintId,\n                &constraint_id.into(),\n            )?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_constraint, constraint_id.into()))?;\n        let table_id = st_constraint_ref.read_col(StConstraintFields::TableId)?;\n        self.delete(ST_CONSTRAINT_ID, st_constraint_ref.pointer())?;\n\n        // Remove constraint in transaction's insert table.\n        let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;\n        // This likely will do a clone-write as over time?\n        // The schema might have found other referents.\n        let schema = commit_table\n            .with_mut_schema_and_clone(tx_table, |s| s.remove_constraint(constraint_id))\n            .expect(\"there should be a schema in the committed state if we reach here\");\n        self.push_schema_change(PendingSchemaChange::ConstraintRemoved(table_id, schema));\n        // TODO(1.0): we should also re-initialize `table` without a unique constraint.\n        // unless some other unique constraint on the same columns exists.\n        // NOTE(centril): is this already handled by dropping the corresponding index?\n        // Probably not in the case where an index\n        // with the same name goes from being unique to not unique.\n\n        Ok(())\n    }\n\n    pub fn constraint_id_from_name(&self, constraint_name: &str) -> Result<Option<ConstraintId>> {\n        self.iter_by_col_eq(\n            ST_CONSTRAINT_ID,\n            StConstraintFields::ConstraintName,\n            &<Box<str>>::from(constraint_name).into(),\n        )\n        .map(|mut iter| {\n            iter.next()\n                .map(|row| row.read_col(StConstraintFields::ConstraintId).unwrap())\n        })\n    }\n\n    /// Create a row level security policy.\n    ///\n    /// Requires:\n    /// - `row_level_security_schema.table_id != TableId::SENTINEL`\n    /// - `row_level_security_schema.sql` must be unique.\n    ///\n    /// Ensures:\n    ///\n    /// - The row level security policy metadata is inserted into the system tables (and other data structures reflecting them).\n    /// - The returned `sql` is unique.\n    pub fn create_row_level_security(&mut self, row_level_security_schema: RowLevelSecuritySchema) -> Result<RawSql> {\n        if row_level_security_schema.table_id == TableId::SENTINEL {\n            return Err(anyhow::anyhow!(\n                \"`table_id` must not be `TableId::SENTINEL` in `{row_level_security_schema:#?}`\"\n            )\n            .into());\n        }\n\n        log::trace!(\n            \"ROW LEVEL SECURITY CREATING for table: {}\",\n            row_level_security_schema.table_id\n        );\n\n        // Insert the row into st_row_level_security\n        // NOTE: Because st_row_level_security has a unique index on sql, this will\n        // fail if already exists.\n        let row = StRowLevelSecurityRow {\n            table_id: row_level_security_schema.table_id,\n            sql: row_level_security_schema.sql,\n        };\n\n        let row = self.insert_via_serialize_bsatn(ST_ROW_LEVEL_SECURITY_ID, &row)?;\n        let row_level_security_sql = row.1.collapse().read_col(StRowLevelSecurityFields::Sql)?;\n        let existed = matches!(row.1, RowRefInsertion::Existed(_));\n\n        // Add the row level security to the transaction's insert table.\n        self.get_or_create_insert_table_mut(row_level_security_schema.table_id)?;\n\n        if existed {\n            log::trace!(\"ROW LEVEL SECURITY ALREADY EXISTS: {row_level_security_sql}\");\n        } else {\n            log::trace!(\"ROW LEVEL SECURITY CREATED: {row_level_security_sql}\");\n        }\n\n        Ok(row_level_security_sql)\n    }\n\n    pub fn row_level_security_for_table_id(&self, table_id: TableId) -> Result<Vec<RowLevelSecuritySchema>> {\n        Ok(self\n            .iter_by_col_eq(\n                ST_ROW_LEVEL_SECURITY_ID,\n                StRowLevelSecurityFields::TableId,\n                &table_id.into(),\n            )?\n            .map(|row| {\n                let row = StRowLevelSecurityRow::try_from(row).unwrap();\n                row.into()\n            })\n            .collect())\n    }\n\n    pub fn drop_row_level_security(&mut self, sql: RawSql) -> Result<()> {\n        let st_rls_ref = self\n            .iter_by_col_eq(\n                ST_ROW_LEVEL_SECURITY_ID,\n                StRowLevelSecurityFields::Sql,\n                &sql.clone().into(),\n            )?\n            .next()\n            .ok_or(TableError::RawSqlNotFound(SystemTable::st_row_level_security, sql))?;\n        self.delete(ST_ROW_LEVEL_SECURITY_ID, st_rls_ref.pointer())?;\n\n        Ok(())\n    }\n\n    // TODO(perf, deep-integration):\n    //   When all of [`Table::read_row`], [`RowRef::new`], [`CommittedState::get`]\n    //   and [`TxState::get`] become unsafe,\n    //   make this method `unsafe` as well.\n    //   Add the following to the docs:\n    //\n    // # Safety\n    //\n    // `pointer` must refer to a row within the table at `table_id`\n    // which was previously inserted and has not been deleted since.\n    //\n    // See [`RowRef::new`] for more detailed requirements.\n    //\n    // Showing that `pointer` was the result of a call to `self.insert`\n    // with `table_id`\n    // and has not been passed to `self.delete`\n    // is sufficient to demonstrate that a call to `self.get` is safe.\n    pub fn get(&self, table_id: TableId, row_ptr: RowPointer) -> Result<Option<RowRef<'_>>> {\n        if self.table_name(table_id).is_none() {\n            return Err(TableError::IdNotFound(SystemTable::st_table, table_id.0).into());\n        }\n        Ok(match row_ptr.squashed_offset() {\n            SquashedOffset::TX_STATE => Some(\n                // TODO(perf, deep-integration):\n                // See above. Once `TxState::get` is unsafe, justify with:\n                //\n                // Our invariants satisfy `TxState::get`.\n                self.tx_state.get(table_id, row_ptr),\n            ),\n            SquashedOffset::COMMITTED_STATE => {\n                if self.tx_state.is_deleted(table_id, row_ptr) {\n                    None\n                } else {\n                    Some(\n                        // TODO(perf, deep-integration):\n                        // See above. Once `CommittedState::get` is unsafe, justify with:\n                        //\n                        // Our invariants satisfy `CommittedState::get`.\n                        self.committed_state_write_lock.get(table_id, row_ptr),\n                    )\n                }\n            }\n            _ => unreachable!(\"Invalid SquashedOffset for row pointer: {:?}\", row_ptr),\n        })\n    }\n\n    /// Commits this transaction in memory, applying its changes to the committed state.\n    /// This doesn't handle the persistence layer at all.\n    ///\n    /// IMPORTANT: This method updates the in-memory state of the database but does not make it durable.\n    /// That is, the tx will not be persisted to the commitlog.\n    /// Hence you should be careful when calling this method directly.\n    /// In most cases you'll want to use `RelationalDB::commit_tx` which makes the tx durable.\n    ///\n    /// Returns:\n    /// - [`TxData`], the set of inserts and deletes performed by this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `String`, the name of the reducer which ran during this transaction.\n    pub(super) fn commit(mut self) -> (TxOffset, TxData, TxMetrics, Option<ReducerName>) {\n        let tx_offset = self.committed_state_write_lock.next_tx_offset;\n        let tx_data = self\n            .committed_state_write_lock\n            .merge(self.tx_state, self.read_sets, &self.ctx);\n\n        // Compute and keep enough info that we can\n        // record metrics after the transaction has ended\n        // and after the lock has been dropped.\n        // Recording metrics when holding the lock is too expensive.\n        let tx_metrics = TxMetrics::new(\n            &self.ctx,\n            self.timer,\n            self.lock_wait_time,\n            self.metrics,\n            true,\n            Some(&tx_data),\n            &self.committed_state_write_lock,\n        );\n        let reducer = self.ctx.into_reducer_name();\n\n        // If the transaction didn't consume an offset (i.e. it was empty),\n        // report the previous offset.\n        //\n        // Note that technically the tx could have run against an empty database,\n        // in which case we'd wrongly return zero (a non-existent transaction).\n        // This doesn't happen in practice, however, as [RelationalDB::set_initialized]\n        // creates a transaction.\n        let tx_offset = if tx_offset == self.committed_state_write_lock.next_tx_offset {\n            tx_offset.saturating_sub(1)\n        } else {\n            tx_offset\n        };\n\n        (tx_offset, tx_data, tx_metrics, reducer)\n    }\n\n    /// Commits this transaction, applying its changes to the committed state.\n    /// The lock on the committed state is converted into a read lock,\n    /// and returned as a new read-only transaction.\n    ///\n    /// IMPORTANT: This method updates the in-memory state of the database but does not make it durable.\n    /// That is, the tx will not be persisted to the commitlog.\n    /// Hence you should be careful when calling this method directly.\n    /// In most cases you'll want to use `RelationalDB::commit_tx_downgrade` which makes the tx durable.\n    ///\n    /// Returns:\n    /// - [`TxData`], the set of inserts and deletes performed by this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - [`TxId`], a read-only transaction with a shared lock on the committed state.\n    pub(super) fn commit_downgrade(mut self, workload: Workload) -> (TxData, TxMetrics, TxId) {\n        let tx_data = self\n            .committed_state_write_lock\n            .merge(self.tx_state, self.read_sets, &self.ctx);\n\n        // Compute and keep enough info that we can\n        // record metrics after the transaction has ended\n        // and after the lock has been dropped.\n        // Recording metrics when holding the lock is too expensive.\n        let tx_metrics = TxMetrics::new(\n            &self.ctx,\n            self.timer,\n            self.lock_wait_time,\n            self.metrics,\n            true,\n            Some(&tx_data),\n            &self.committed_state_write_lock,\n        );\n\n        // Update the workload type of the execution context\n        self.ctx.workload = workload.workload_type();\n        let tx = TxId {\n            committed_state_shared_lock: SharedWriteGuard::downgrade(self.committed_state_write_lock),\n            lock_wait_time: Duration::ZERO,\n            timer: Instant::now(),\n            ctx: self.ctx,\n            metrics: ExecutionMetrics::default(),\n        };\n        (tx_data, tx_metrics, tx)\n    }\n\n    /// Rolls back this transaction, discarding its changes.\n    ///\n    /// Returns:\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran during this transaction.\n    pub fn rollback(mut self) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        let offset = self\n            .committed_state_write_lock\n            .rollback(&mut self.sequence_state_lock, self.tx_state);\n\n        // Compute and keep enough info that we can\n        // record metrics after the transaction has ended\n        // and after the lock has been dropped.\n        // Recording metrics when holding the lock is too expensive.\n        let tx_metrics = TxMetrics::new(\n            &self.ctx,\n            self.timer,\n            self.lock_wait_time,\n            self.metrics,\n            false,\n            None,\n            &self.committed_state_write_lock,\n        );\n        let reducer = self.ctx.into_reducer_name();\n        (offset, tx_metrics, reducer)\n    }\n\n    /// Roll back this transaction, discarding its changes.\n    /// The lock on the committed state is converted into a read lock,\n    /// and returned as a new read-only transaction.\n    ///\n    /// Returns:\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - [`TxId`], a read-only transaction with a shared lock on the committed state.\n    pub fn rollback_downgrade(mut self, workload: Workload) -> (TxMetrics, TxId) {\n        self.committed_state_write_lock\n            .rollback(&mut self.sequence_state_lock, self.tx_state);\n\n        // Compute and keep enough info that we can\n        // record metrics after the transaction has ended\n        // and after the lock has been dropped.\n        // Recording metrics when holding the lock is too expensive.\n        let tx_metrics = TxMetrics::new(\n            &self.ctx,\n            self.timer,\n            self.lock_wait_time,\n            self.metrics,\n            false,\n            None,\n            &self.committed_state_write_lock,\n        );\n\n        // Update the workload type of the execution context\n        self.ctx.workload = workload.workload_type();\n        let tx = TxId {\n            committed_state_shared_lock: SharedWriteGuard::downgrade(self.committed_state_write_lock),\n            lock_wait_time: Duration::ZERO,\n            timer: Instant::now(),\n            ctx: self.ctx,\n            metrics: ExecutionMetrics::default(),\n        };\n\n        (tx_metrics, tx)\n    }\n}\n\n/// Either a row just inserted to a table or a row that already existed in some table.\n#[derive(Clone, Copy)]\npub enum RowRefInsertion<'a> {\n    /// The row was just inserted.\n    Inserted(RowRef<'a>),\n    /// The row already existed.\n    Existed(RowRef<'a>),\n}\n\nimpl<'a> RowRefInsertion<'a> {\n    /// Returns a row,\n    /// collapsing the distinction between inserted and existing rows.\n    pub(super) fn collapse(&self) -> RowRef<'a> {\n        let (Self::Inserted(row) | Self::Existed(row)) = *self;\n        row\n    }\n}\n\n/// The iterator returned by [`MutTxId::index_scan_range`].\npub type IndexScanRanged<'a> = ScanMutTx<'a, IndexScanRangeIter<'a>>;\n\n/// The iterator returned by [`MutTxId::index_scan_point`].\npub type IndexScanPoint<'a> = ScanMutTx<'a, IndexScanPointIter<'a>>;\n\n/// The iterator returned by e.g., [`MutTxId::index_scan_range`]\n/// and [`MutTxId::index_scan_point`].\n///\n/// This layer only handles the transactionality aspects of [`MutTxId`].\n/// The specifics of a point vs. range scan or a non-index scan\n/// are dealt with by `I`.\npub struct ScanMutTx<'a, I> {\n    inner: ScanMutTxInner<'a, I>,\n}\n\nenum ScanMutTxInner<'a, I> {\n    CommitOnly(I),\n    CommitOnlyWithDeletes(FilterDeleted<'a, I>),\n    Both(iter::Chain<I, I>),\n    BothWithDeletes(iter::Chain<I, FilterDeleted<'a, I>>),\n}\n\npub(super) struct FilterDeleted<'a, I> {\n    pub(super) iter: I,\n    pub(super) deletes: &'a DeleteTable,\n}\n\nimpl<'a, I: Iterator<Item = RowRef<'a>>> ScanMutTx<'a, I> {\n    /// Combine together a `tx_iter`, with its potential `delete_table`,\n    /// with a `commit_iter`, creating a single iterator.\n    fn combine(delete_table: Option<&'a DeleteTable>, tx_iter: Option<I>, commit_iter: I) -> Self {\n        // Chain together the indexed rows in the tx and committed state,\n        // but don't yield rows deleted in the tx state.\n        use itertools::Either::*;\n        use ScanMutTxInner::*;\n        let commit_iter = match delete_table {\n            None => Left(commit_iter),\n            Some(deletes) => Right(FilterDeleted {\n                iter: commit_iter,\n                deletes,\n            }),\n        };\n        // This is effectively just `tx_iter.into_iter().flatten().chain(commit_iter)`,\n        // but with all the branching and `Option`s flattened to just one layer.\n        let iter = match (tx_iter, commit_iter) {\n            (None, Left(commit_iter)) => CommitOnly(commit_iter),\n            (None, Right(commit_iter)) => CommitOnlyWithDeletes(commit_iter),\n            (Some(tx_iter), Left(commit_iter)) => Both(tx_iter.chain(commit_iter)),\n            (Some(tx_iter), Right(commit_iter)) => BothWithDeletes(tx_iter.chain(commit_iter)),\n        };\n        ScanMutTx { inner: iter }\n    }\n}\n\nimpl<'a, I: Iterator<Item = RowRef<'a>>> Iterator for ScanMutTx<'a, I> {\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        use ScanMutTxInner::*;\n        match &mut self.inner {\n            CommitOnly(it) => it.next(),\n            CommitOnlyWithDeletes(it) => it.next(),\n            Both(it) => it.next(),\n            BothWithDeletes(it) => it.next(),\n        }\n    }\n}\n\nimpl<'a, I: Iterator<Item = RowRef<'a>>> Iterator for FilterDeleted<'a, I> {\n    type Item = RowRef<'a>;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.find(|row| !self.deletes.contains(row.pointer()))\n    }\n}\n\nimpl MutTxId {\n    /// Does this caller have an entry for `view_id` in `st_view_sub`?\n    pub fn is_view_materialized(&self, view_id: ViewId, arg_id: ArgId, sender: Identity) -> Result<bool> {\n        use StViewSubFields::*;\n        let sender = IdentityViaU256(sender);\n        let cols = col_list![ViewId, ArgId, Identity];\n        let value = AlgebraicValue::product([view_id.into(), arg_id.into(), sender.into()]);\n        Ok(self.iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?.next().is_some())\n    }\n\n    /// Does any `st_view_sub` row exist for this anonymous view?\n    pub fn is_anonymous_view_materialized(&self, view_id: ViewId) -> Result<bool> {\n        let cols = StViewSubFields::ViewId;\n        let value = view_id.into();\n        Ok(self.iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?.next().is_some())\n    }\n\n    /// Updates the `last_called` timestamp in `st_view_sub`.\n    /// Inserts a row into `st_view_sub` with no subscribers if the row does not exist.\n    ///\n    /// This is invoked when calling a view, but not subscribing to it.\n    /// Such is the case for the sql http api.\n    pub fn update_view_timestamp(&mut self, view_id: ViewId, arg_id: ArgId, sender: Identity) -> Result<()> {\n        self.update_view_timestamp_at(view_id, arg_id, sender, Timestamp::now())\n    }\n\n    /// Updates the `last_called` timestamp in `st_view_sub` to an explicit value.\n    pub fn update_view_timestamp_at(\n        &mut self,\n        view_id: ViewId,\n        arg_id: ArgId,\n        sender: Identity,\n        last_called: Timestamp,\n    ) -> Result<()> {\n        use StViewSubFields::*;\n\n        let identity = IdentityViaU256(sender);\n        let cols = col_list![ViewId, ArgId, Identity];\n        let value = AlgebraicValue::product([view_id.into(), arg_id.into(), identity.into()]);\n        let last_called = last_called.into();\n\n        // Update `last_called` of `st_view_sub` row\n        if let Some((row, ptr)) = self\n            .iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .next()\n            .map(|row_ref| StViewSubRow::try_from(row_ref).map(|row| (row, row_ref.pointer())))\n            .transpose()?\n        {\n            self.delete(ST_VIEW_SUB_ID, ptr)?;\n            self.insert_via_serialize_bsatn(ST_VIEW_SUB_ID, &StViewSubRow { last_called, ..row })?;\n            return Ok(());\n        }\n\n        // Insert `st_view_sub` row with 0 subscribers\n        self.insert_via_serialize_bsatn(\n            ST_VIEW_SUB_ID,\n            &StViewSubRow {\n                view_id,\n                arg_id,\n                identity,\n                num_subscribers: 0,\n                has_subscribers: false,\n                last_called,\n            },\n        )?;\n        Ok(())\n    }\n\n    /// Increment `num_subscribers` in `st_view_sub` to effectively subscribe a caller to a view.\n    /// We insert a row if there are no current subscribers and the row does not exist.\n    pub fn subscribe_view(&mut self, view_id: ViewId, arg_id: ArgId, sender: Identity) -> Result<()> {\n        use StViewSubFields::*;\n\n        let identity = IdentityViaU256(sender);\n        let cols = col_list![ViewId, ArgId, Identity];\n        let value = AlgebraicValue::product([view_id.into(), arg_id.into(), identity.into()]);\n        let last_called = Timestamp::now().into();\n\n        // Update `last_called` of `st_view_sub` row\n        if let Some((row, ptr)) = self\n            .iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .next()\n            .map(|row_ref| StViewSubRow::try_from(row_ref).map(|row| (row, row_ref.pointer())))\n            .transpose()?\n        {\n            self.delete(ST_VIEW_SUB_ID, ptr)?;\n            self.insert_via_serialize_bsatn(\n                ST_VIEW_SUB_ID,\n                &StViewSubRow {\n                    num_subscribers: row.num_subscribers + 1,\n                    has_subscribers: true,\n                    last_called,\n                    ..row\n                },\n            )?;\n            return Ok(());\n        }\n\n        // Insert `st_view_sub` row with 1 subscriber\n        self.insert_via_serialize_bsatn(\n            ST_VIEW_SUB_ID,\n            &StViewSubRow {\n                view_id,\n                arg_id,\n                identity,\n                num_subscribers: 1,\n                has_subscribers: true,\n                last_called,\n            },\n        )?;\n        Ok(())\n    }\n\n    /// Clean up views that have no subscribers and haven’t been called recently.\n    ///\n    /// This function will scan for subscription entries in `st_view_sub` where:\n    /// - `has_subscribers == false`, `num_subscribers == 0`.\n    /// - `last_called` is older than `expiration_duration`.\n    ///\n    /// For each such expired row:\n    /// 1. It deletes the expired `st_view_sub` row.\n    /// 2. If that row was the last remaining materialization entry for the view,\n    ///    it clears the backing table and removes the view from the committed read set.\n    ///\n    /// The cleanup is bounded by a total `max_duration`. The function stops when either:\n    /// - all expired views have been processed, or\n    /// - the `max_duration` budget is reached.\n    ///\n    /// Returns a tuple `(cleaned, total_expired)`:\n    /// - `cleaned`: Number of expired `st_view_sub` rows deleted in this run.\n    /// - `total_expired`: Total number of expired rows found (even if not all were cleaned due to time budget).\n    pub fn clear_expired_views(\n        &mut self,\n        expiration_duration: Duration,\n        max_duration: Duration,\n    ) -> Result<(usize, usize)> {\n        let start = std::time::Instant::now();\n        let now = Timestamp::now();\n        let expiration_threshold = now - expiration_duration;\n        let mut cleaned_count = 0;\n\n        // Collect all expired views from st_view_sub\n        let expired_items: Vec<(ViewId, Identity, RowPointer)> = self\n            .iter_by_col_eq(\n                ST_VIEW_SUB_ID,\n                StViewSubFields::HasSubscribers,\n                &AlgebraicValue::from(false),\n            )?\n            .filter_map(|row_ref| {\n                let row = StViewSubRow::try_from(row_ref).expect(\"Failed to deserialize st_view_sub row\");\n\n                if !row.has_subscribers && row.num_subscribers == 0 && row.last_called.0 < expiration_threshold {\n                    Some((row.view_id, row.identity.into(), row_ref.pointer()))\n                } else {\n                    None\n                }\n            })\n            .collect();\n\n        let total_expired = expired_items.len();\n\n        // For each expired subscription row, clear the backing table only if that row\n        // was the last remaining entry for the shared materialization.\n        for (view_id, sender, sub_row_ptr) in expired_items {\n            // Check if we've exceeded our time budget\n            if start.elapsed() >= max_duration {\n                break;\n            }\n\n            let StViewRow {\n                table_id, is_anonymous, ..\n            } = self.lookup_st_view(view_id)?;\n            let table_id = table_id.expect(\"views have backing table\");\n\n            if is_anonymous {\n                if !self.has_other_st_view_sub_entries(view_id, sub_row_ptr)? {\n                    self.clear_table(table_id)?;\n                    self.drop_view_from_committed_read_set(view_id);\n                }\n            } else {\n                let rows_to_delete = self\n                    .iter_by_col_eq(table_id, 0, &sender.into())?\n                    .map(|res| res.pointer())\n                    .collect::<Vec<_>>();\n\n                for row_ptr in rows_to_delete {\n                    self.delete(table_id, row_ptr)?;\n                }\n\n                self.drop_view_with_sender_from_committed_read_set(view_id, sender);\n            }\n\n            // Finally, delete the subscription row\n            self.delete(ST_VIEW_SUB_ID, sub_row_ptr)?;\n            cleaned_count += 1;\n        }\n\n        Ok((cleaned_count, total_expired))\n    }\n\n    /// Decrement `num_subscribers` in `st_view_sub` to effectively unsubscribe a caller from a view.\n    pub fn unsubscribe_view(&mut self, view_id: ViewId, arg_id: ArgId, sender: Identity) -> Result<()> {\n        use StViewSubFields::*;\n\n        let identity = IdentityViaU256(sender);\n        let cols = col_list![ViewId, ArgId, Identity];\n        let value = AlgebraicValue::product([view_id.into(), arg_id.into(), identity.into()]);\n        let last_called = Timestamp::now().into();\n\n        // Update `last_called` of `st_view_sub` row\n        if let Some((row, ptr)) = self\n            .iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .next()\n            .map(|row_ref| StViewSubRow::try_from(row_ref).map(|row| (row, row_ref.pointer())))\n            .transpose()?\n        {\n            self.delete(ST_VIEW_SUB_ID, ptr)?;\n            self.insert_via_serialize_bsatn(\n                ST_VIEW_SUB_ID,\n                &StViewSubRow {\n                    num_subscribers: row.num_subscribers - 1,\n                    has_subscribers: row.num_subscribers > 1,\n                    last_called,\n                    ..row\n                },\n            )?;\n        }\n        Ok(())\n    }\n\n    /// To effectively unsubscribe a caller from all of their subscribed views,\n    /// we decrement `num_subscribers` in `st_view_sub` for all of a caller's views.\n    pub fn unsubscribe_views(&mut self, sender: Identity) -> Result<()> {\n        let sender = IdentityViaU256(sender);\n        let cols = col_list![StViewSubFields::Identity];\n        let value = sender.into();\n\n        // Collect the rows for this identity.\n        // These are rows for which we will decrement the subscriber count.\n        let rows_to_delete = self\n            .iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .map(|row_ref| StViewSubRow::try_from(row_ref).map(|row| (row, row_ref.pointer())))\n            .filter(|result| match result {\n                Ok((row, _)) => row.has_subscribers && row.num_subscribers > 0,\n                _ => true,\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        // Copy the rows to delete and decrement their subscriber count.\n        // These are the rows that we will insert.\n        let rows_to_insert = rows_to_delete\n            .iter()\n            .map(|(row, _)| row.clone())\n            .map(|row| StViewSubRow {\n                num_subscribers: row.num_subscribers - 1,\n                has_subscribers: row.num_subscribers > 1,\n                ..row\n            })\n            .collect::<Vec<_>>();\n\n        // Delete the old rows\n        for (_, ptr) in rows_to_delete {\n            self.delete(ST_VIEW_SUB_ID, ptr)?;\n        }\n\n        // Insert the new rows\n        for row in rows_to_insert {\n            self.insert_via_serialize_bsatn(ST_VIEW_SUB_ID, &row)?;\n        }\n\n        Ok(())\n    }\n\n    /// Clear all rows from all view tables without dropping them.\n    pub fn clear_all_views(&mut self) -> Result<()> {\n        for table_id in self\n            .iter(ST_VIEW_ID)?\n            .map(StViewRow::try_from)\n            .collect::<Result<Vec<_>>>()?\n            .into_iter()\n            .filter_map(|row| row.table_id)\n        {\n            self.clear_table(table_id)?;\n        }\n        Ok(())\n    }\n\n    /// Get all view subscriptions for a given view.\n    pub fn lookup_st_view_subs(&self, view_id: ViewId) -> Result<Vec<StViewSubRow>> {\n        let cols = StViewSubFields::ViewId;\n        let value = view_id.into();\n        self.iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .map(StViewSubRow::try_from)\n            .collect::<Result<Vec<_>>>()\n    }\n\n    /// Does this `view_id` have other entries in `st_view_sub` besides `current_ptr`?\n    /// Can be true for anonymous views with multiple subscribers.\n    fn has_other_st_view_sub_entries(&self, view_id: ViewId, current_ptr: RowPointer) -> Result<bool> {\n        let cols = StViewSubFields::ViewId;\n        let value = view_id.into();\n        Ok(self\n            .iter_by_col_eq(ST_VIEW_SUB_ID, cols, &value)?\n            .any(|row_ref| row_ref.pointer() != current_ptr))\n    }\n\n    /// Lookup a row in `st_view` by its primary key\n    fn st_view_row(&self, view_id: ViewId) -> Result<Option<StViewRow>> {\n        self.iter_by_col_eq(ST_VIEW_ID, col_list![StViewFields::ViewId], &view_id.into())?\n            .next()\n            .map(StViewRow::try_from)\n            .transpose()\n    }\n\n    /// Get the [`TableId`] for this view's backing table by probing `st_view`.\n    /// Note, all views with at least one subscriber are materialized.\n    pub fn get_table_id_for_view(&self, view_id: ViewId) -> Result<Option<(TableId, bool)>> {\n        Ok(self\n            .st_view_row(view_id)?\n            .and_then(|row| row.table_id.map(|id| (id, row.is_anonymous))))\n    }\n\n    pub fn insert_st_client(\n        &mut self,\n        identity: Identity,\n        connection_id: ConnectionId,\n        jwt_payload: &str,\n    ) -> Result<()> {\n        let row = &StClientRow {\n            identity: identity.into(),\n            connection_id: connection_id.into(),\n        };\n        self.insert_via_serialize_bsatn(ST_CLIENT_ID, row)\n            .map(|_| ())\n            .inspect_err(|e| {\n                log::error!(\n                    \"[{identity}]: insert_st_client: failed to insert client ({identity}, {connection_id}), error: {e}\"\n                );\n            })?;\n        self.insert_st_client_credentials(connection_id, jwt_payload)\n    }\n\n    fn insert_st_client_credentials(&mut self, connection_id: ConnectionId, jwt_payload: &str) -> Result<()> {\n        let row = &StConnectionCredentialsRow {\n            connection_id: connection_id.into(),\n            jwt_payload: jwt_payload.to_owned(),\n        };\n        self.insert_via_serialize_bsatn(ST_CONNECTION_CREDENTIALS_ID, row)\n            .map(|_| ())\n            .inspect_err(|e| {\n                log::error!(\"[{connection_id}]: insert_st_client_credentials: failed to insert client credentials for connection id ({connection_id}), error: {e}\");\n            })\n    }\n\n    fn delete_st_client_credentials(&mut self, database_identity: Identity, connection_id: ConnectionId) -> Result<()> {\n        if let Err(e) = self.delete_col_eq(\n            ST_CONNECTION_CREDENTIALS_ID,\n            StConnectionCredentialsFields::ConnectionId.col_id(),\n            &ConnectionIdViaU128::from(connection_id).into(),\n        ) {\n            // This is possible on restart if the database was previously running a version\n            // before this system table was added.\n            log::error!(\n                \"[{database_identity}]: delete_st_client_credentials: attempting to delete credentials for missing connection id ({connection_id}), error: {e}\"\n            );\n        }\n        Ok(())\n    }\n\n    pub fn delete_st_client(\n        &mut self,\n        identity: Identity,\n        connection_id: ConnectionId,\n        database_identity: Identity,\n    ) -> Result<()> {\n        let row = &StClientRow {\n            identity: identity.into(),\n            connection_id: connection_id.into(),\n        };\n        match self\n            .iter_by_col_eq(\n                ST_CLIENT_ID,\n                // TODO(perf, minor, centril): consider a `const_col_list([x, ..])`\n                // so we know this is not computed at runtime.\n                col_list![StClientFields::Identity, StClientFields::ConnectionId],\n                &AlgebraicValue::product(row),\n            )?\n            .next()\n            .map(|row| row.pointer())\n        {\n            Some(ptr) => self.delete(ST_CLIENT_ID, ptr).map(drop)?,\n            _ => {\n                log::error!(\n                    \"[{database_identity}]: delete_st_client: attempting to delete client ({identity}, {connection_id}), but no st_client row for that client is resident\"\n                );\n            }\n        }\n        self.delete_st_client_credentials(database_identity, connection_id)\n    }\n\n    /// Look up a client row by identity and connection ID in the `st_clients` system table.\n    pub fn st_client_row(&self, identity: Identity, connection_id: ConnectionId) -> Option<RowPointer> {\n        let row = StClientRow {\n            identity: identity.into(),\n            connection_id: connection_id.into(),\n        };\n\n        self.iter_by_col_eq(\n            ST_CLIENT_ID,\n            col_list![StClientFields::Identity, StClientFields::ConnectionId],\n            &AlgebraicValue::product(row),\n        )\n        .expect(\"failed to read from st_client system table\")\n        .next()\n        .map(|row| row.pointer())\n    }\n\n    pub fn insert_via_serialize_bsatn<'a, T: Serialize>(\n        &'a mut self,\n        table_id: TableId,\n        row: &T,\n    ) -> Result<(ColList, RowRefInsertion<'a>, InsertFlags)> {\n        thread_local! {\n            static BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };\n        }\n        BUF.with_borrow_mut(|buf| {\n            buf.clear();\n            to_writer(buf, row).unwrap();\n            self.insert::<true>(table_id, buf)\n        })\n    }\n\n    /// Insert a row, encoded in BSATN, into a table.\n    ///\n    /// Zero placeholders, i.e., sequence triggers,\n    /// in auto-inc columns in the new row will be replaced with generated values\n    /// if and only if `GENERATE` is true.\n    /// This method is called with `GENERATE` false when updating the `st_sequence` system table.\n    ///\n    /// Requires:\n    /// - `table_id` must refer to a valid table for the database at `database_identity`.\n    /// - `row` must be a valid row for the table at `table_id`.\n    ///\n    /// Returns:\n    /// - a list of columns which have been replaced with generated values.\n    /// - a ref to the inserted row.\n    /// - any insert flags.\n    pub(super) fn insert<const GENERATE: bool>(\n        &mut self,\n        table_id: TableId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRefInsertion<'_>, InsertFlags)> {\n        insert::<GENERATE>(\n            &mut self.tx_state,\n            &self.committed_state_write_lock,\n            &mut self.sequence_state_lock,\n            table_id,\n            row,\n        )\n    }\n}\n\n/// Insert a row, encoded in BSATN, into a table.\n///\n/// Zero placeholders, i.e., sequence triggers,\n/// in auto-inc columns in the new row will be replaced with generated values\n/// if and only if `GENERATE` is true.\n/// This method is called with `GENERATE` false when updating the `st_sequence` system table.\n///\n/// Requires:\n/// - `table_id` must refer to a valid table for the database at `database_identity`.\n/// - `row` must be a valid row for the table at `table_id`.\n///\n/// Returns:\n/// - list of columns which have been replaced with generated values.\n/// - a pointer to the inserted row.\n/// - the number of bytes added to the tx blob store.\n/// - The \"tx table for insertion\" for further processing.\n/// - The \"commit table for insertion\" for further processing.\nfn insert_physically_maybe_generate<'a, const GENERATE: bool>(\n    tx_state: &'a mut TxState,\n    committed_state: &'a CommittedState,\n    seq_state: &mut SequencesState,\n    table_id: TableId,\n    row: &[u8],\n) -> Result<(\n    RowPointer,\n    ColList,\n    BlobNumBytes,\n    TxTableForInsertion<'a>,\n    CommitTableForInsertion<'a>,\n)> {\n    // Get commit table and friends.\n    let commit_parts = committed_state.get_table_and_blob_store(table_id)?;\n    let (commit_table, ..) = commit_parts;\n\n    // Get the insert table, so we can write the row into it.\n    let (tx_table, tx_blob_store, _) = tx_state.get_table_and_blob_store_or_create_from(table_id, commit_table);\n\n    // 1. Insert the physical row.\n    let page_pool = &committed_state.page_pool;\n    let (tx_row_ref, blob_bytes) = tx_table.insert_physically_bsatn(page_pool, tx_blob_store, row)?;\n    let tx_row_ptr = tx_row_ref.pointer();\n    // 2. Optionally: Detect, generate, write sequence values.\n    let (tx_parts, gen_cols) = if GENERATE {\n        // When `GENERATE` is enabled, we're instructed to deal with sequence value generation.\n        // Collect all the columns with sequences that need generation.\n        let (cols_to_gen, seqs_to_use) = unsafe { tx_table.sequence_triggers_for(tx_blob_store, tx_row_ptr) };\n\n        // Generate a value for every column in the row that needs it.\n        let mut seq_vals: SmallVec<[i128; 1]> = <_>::default();\n        for sequence_id in seqs_to_use {\n            seq_vals.push(get_next_sequence_value(\n                tx_state,\n                committed_state,\n                seq_state,\n                sequence_id,\n            )?);\n        }\n\n        // Write the generated values to the physical row at `tx_row_ptr`.\n        // We assume here that column with a sequence is of a sequence-compatible type.\n        // SAFETY: After `get_table_and_blob_store_or_create_from` there's a insert and delete table.\n        let (tx_table, tx_blob_store, delete_table) = unsafe { tx_state.assume_present_get_mut_table(table_id) };\n        for (col_id, seq_val) in cols_to_gen.iter().zip(seq_vals) {\n            // SAFETY:\n            // - `self.is_row_present(row)` holds as we haven't deleted the row.\n            // - `col_id` is a valid column, and has a sequence, so it must have a primitive type.\n            unsafe { tx_table.write_gen_val_to_col(col_id, tx_row_ptr, seq_val) };\n        }\n\n        ((tx_table, tx_blob_store, delete_table), cols_to_gen)\n    } else {\n        // When `GENERATE` is not enabled, avoid sequence generation.\n        // This branch is hit when inside sequence generation itself, to avoid infinite recursion.\n        // SAFETY: After `get_table_and_blob_store_or_create_from` there's a insert and delete table.\n        let tx_parts = unsafe { tx_state.assume_present_get_mut_table(table_id) };\n        (tx_parts, ColList::empty())\n    };\n\n    Ok((tx_row_ptr, gen_cols, blob_bytes, tx_parts, commit_parts))\n}\n\n/// Insert a row, encoded in BSATN, into a table.\n///\n/// Zero placeholders, i.e., sequence triggers,\n/// in auto-inc columns in the new row will be replaced with generated values\n/// if and only if `GENERATE` is true.\n/// This method is called with `GENERATE` false when updating the `st_sequence` system table.\n///\n/// Requires:\n/// - `table_id` must refer to a valid table for the database at `database_identity`.\n/// - `row` must be a valid row for the table at `table_id`.\n///\n/// Returns:\n/// - a list of columns which have been replaced with generated values.\n/// - a ref to the inserted row.\n/// - any insert flags.\npub(super) fn insert<'a, const GENERATE: bool>(\n    tx_state: &'a mut TxState,\n    committed_state: &'a CommittedState,\n    seq_state: &mut SequencesState,\n    table_id: TableId,\n    row: &[u8],\n) -> Result<(ColList, RowRefInsertion<'a>, InsertFlags)> {\n    let (\n        tx_row_ptr,\n        gen_cols,\n        blob_bytes,\n        (tx_table, tx_blob_store, delete_table),\n        (commit_table, commit_blob_store, _),\n    ) = insert_physically_maybe_generate::<GENERATE>(tx_state, committed_state, seq_state, table_id, row)?;\n\n    let insert_flags = InsertFlags {\n        is_scheduler_table: tx_table.is_scheduler(),\n    };\n    let ok = |row_ref| Ok((gen_cols, row_ref, insert_flags));\n\n    // `CHECK_SAME_ROW = true`, as there might be an identical row already in the tx state.\n    // SAFETY: `tx_table.is_row_present(row)` holds as we still haven't deleted the row,\n    // in particular, the `write_gen_val_to_col` call does not remove the row.\n    let res = unsafe { tx_table.confirm_insertion::<true>(tx_blob_store, tx_row_ptr, blob_bytes) };\n\n    match res {\n        Ok((tx_row_hash, tx_row_ptr)) => {\n            // The `tx_row_ref` was not previously present in insert tables,\n            // but may still be a set-semantic conflict\n            // or may violate a unique constraint with a row in the committed state.\n            // We'll check the set-semantic aspect in (1) and the constraint in (2).\n\n            // (1) Rule out a set-semantic conflict with the committed state.\n            // SAFETY:\n            // - `commit_table` and `tx_table` use the same schema\n            //   because `tx_table` is derived from `commit_table`.\n            // - `tx_row_ptr` is correct per post-condition of `tx_table.confirm_insertion(...)`.\n            if let (_, Some(commit_ptr)) =\n                unsafe { Table::find_same_row(commit_table, tx_table, tx_blob_store, tx_row_ptr, tx_row_hash) }\n            {\n                // (insert_undelete)\n                // -----------------------------------------------------\n                // If `row` was already present in the committed state,\n                // either this is a set-semantic duplicate,\n                // or the row is marked as deleted, so we will undelete it\n                // and leave it in the committed state.\n                // Either way, it should not appear in the insert tables,\n                // so roll back the insertion.\n                //\n                // NOTE for future MVCC implementors:\n                // In MVCC, it is no longer valid to elide inserts in this way.\n                // When a transaction inserts a row, that row *must* appear in its insert tables,\n                // even if the row is already present in the committed state.\n                //\n                // Imagine a chain of committed but un-squashed transactions:\n                // `Committed 0: Insert Row A` - `Committed 1: Delete Row A`\n                // where `Committed 1` happens after `Committed 0`.\n                // Imagine a transaction `Running 2: Insert Row A`,\n                // which began before `Committed 1` was committed.\n                // Because `Committed 1` has since been committed,\n                // `Running 2` *must* happen after `Committed 1`.\n                // Therefore, the correct sequence of events is:\n                // - Insert Row A\n                // - Delete Row A\n                // - Insert Row A\n                // This is impossible to recover if `Running 2` elides its insert.\n                tx_table\n                    .delete(tx_blob_store, tx_row_ptr, |_| ())\n                    .expect(\"Failed to delete a row we just inserted\");\n\n                // It's possible that `row` appears in the committed state,\n                // but is marked as deleted.\n                // In this case, undelete it, so it remains in the committed state.\n                delete_table.remove(commit_ptr);\n\n                // No new row was inserted, but return `committed_ptr`.\n                // SAFETY: `find_same_row` told us that `ptr` refers to a valid row in `commit_table`.\n                let row_ref = unsafe { commit_table.get_row_ref_unchecked(commit_blob_store, commit_ptr) };\n                return ok(RowRefInsertion::Existed(row_ref));\n            }\n\n            // Pacify the borrow checker.\n            // SAFETY: `tx_row_ptr` is still correct for `tx_table` per (PC.INS.1).\n            // as there haven't been any interleaving `&mut` calls that could invalidate the pointer.\n            let tx_row_ref = unsafe { tx_table.get_row_ref_unchecked(tx_blob_store, tx_row_ptr) };\n\n            // (2) The `tx_row_ref` did not violate a unique constraint *within* the `tx_table`,\n            // but it could do so wrt., `commit_table`,\n            // assuming the conflicting row hasn't been deleted since.\n            // Ensure that it doesn't, or roll back the insertion.\n            let is_deleted = |commit_ptr| delete_table.contains(commit_ptr);\n            // SAFETY: `commit_table.row_layout() == tx_row_ref.row_layout()` holds\n            // as the `tx_table` is derived from `commit_table`.\n            let res = unsafe { commit_table.check_unique_constraints(tx_row_ref, |ixs| ixs, is_deleted) };\n            if let Err(e) = res {\n                // There was a constraint violation, so undo the insertion.\n                tx_table.delete(tx_blob_store, tx_row_ptr, |_| {});\n                return Err(IndexError::from(e).into());\n            }\n\n            // SAFETY: `tx_row_ptr` is still correct for `tx_table` per (PC.INS.1).\n            // as there haven't been any interleaving `&mut` calls that could invalidate the pointer.\n            let row_ref = unsafe { tx_table.get_row_ref_unchecked(tx_blob_store, tx_row_ptr) };\n            ok(RowRefInsertion::Inserted(row_ref))\n        }\n        // `row` previously present in insert tables; do nothing but return `ptr`.\n        Err(InsertError::Duplicate(DuplicateError(ptr))) => {\n            // SAFETY: `tx_table` told us that `ptr` refers to a valid row in it.\n            let row_ref = unsafe { tx_table.get_row_ref_unchecked(tx_blob_store, ptr) };\n            ok(RowRefInsertion::Existed(row_ref))\n        }\n        // Unwrap these error into `TableError::{IndexError, Bflatn}`:\n        Err(InsertError::IndexError(e)) => Err(IndexError::from(e).into()),\n        Err(InsertError::Bflatn(e)) => Err(TableError::Bflatn(e).into()),\n    }\n}\n\nimpl MutTxId {\n    /// Update a row, encoded in BSATN, into a table.\n    ///\n    /// Zero placeholders, i.e., sequence triggers,\n    /// in auto-inc columns in the new row will be replaced with generated values.\n    ///\n    /// The old row is found by projecting `row` to the columns of `index_id`.\n    ///\n    /// Requires:\n    /// - `table_id` must refer to a valid table for the database at `database_identity`.\n    /// - `index_id` must refer to a valid index in the table.\n    /// - `row` must be a valid row for the table at `table_id`.\n    ///\n    /// Returns:\n    /// - a list of columns which have been replaced with generated values.\n    /// - a ref to the new row.\n    /// - any update flags.\n    pub(crate) fn update(\n        &mut self,\n        table_id: TableId,\n        index_id: IndexId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRefInsertion<'_>, UpdateFlags)> {\n        // Insert the physical row into the tx insert table\n        // and possibly generate sequence values.\n        //\n        // As we are provided the `row` encoded in BSATN,\n        // and since we don't have a convenient way to BSATN to a set of columns,\n        // we cannot really do an in-place update in the row-was-in-tx-state case.\n        // So we will begin instead by inserting the row physically to the tx state and project that.\n        let (\n            tx_row_ptr,\n            cols_to_gen,\n            blob_bytes,\n            (tx_table, tx_blob_store, del_table),\n            (commit_table, commit_blob_store, _),\n        ) = insert_physically_maybe_generate::<true>(\n            &mut self.tx_state,\n            &self.committed_state_write_lock,\n            &mut self.sequence_state_lock,\n            table_id,\n            row,\n        )?;\n\n        let update_flags = UpdateFlags {\n            is_scheduler_table: tx_table.is_scheduler(),\n        };\n        let ok = |row_ref| Ok((cols_to_gen, row_ref, update_flags));\n\n        // SAFETY: `tx_table.is_row_present(tx_row_ptr)` holds as we just inserted it.\n        let tx_row_ref = unsafe { tx_table.get_row_ref_unchecked(tx_blob_store, tx_row_ptr) };\n\n        let err = 'error: {\n            // This macros can be thought of as a `throw $e` within `'error`.\n            // TODO(centril): Get rid of this once we have stable `try` blocks or polonius.\n            macro_rules! throw {\n                ($e:expr) => {\n                    break 'error $e.into()\n                };\n            }\n\n            // Check that the index exists and is unique.\n            // It's sufficient to check the committed state.\n            let Some(commit_index) = commit_table.get_index_by_id(index_id) else {\n                throw!(IndexError::NotFound(index_id));\n            };\n            if !commit_index.is_unique() {\n                throw!(IndexError::NotUnique(index_id));\n            }\n\n            // Project the row to the index's type.\n            // SAFETY: `tx_row_ref`'s table is derived from `commit_index`'s table,\n            // so all `index.indexed_columns` will be in-bounds of the row layout.\n            let index_key = unsafe { tx_row_ref.project_unchecked(&commit_index.indexed_columns) };\n\n            // Try to find the old row first in the committed state using the `index_key`.\n            let mut old_commit_del_ptr = None;\n            let commit_old_ptr = commit_index.seek_point(&index_key).next().filter(|&ptr| {\n                // Was committed row previously deleted in this TX?\n                let deleted = del_table.contains(ptr);\n                // If so, remember it in case it was identical to the new row.\n                old_commit_del_ptr = deleted.then_some(ptr);\n                !deleted\n            });\n\n            // Ensure that the new row does not violate other commit table unique constraints.\n            let is_deleted = |commit_ptr| {\n                commit_old_ptr.is_some_and(|old_ptr| old_ptr == commit_ptr) || del_table.contains(commit_ptr)\n            };\n            // SAFETY: `commit_table.row_layout() == new_row.row_layout()` holds\n            // as the `tx_table` is derived from `commit_table`.\n            if let Err(e) = unsafe {\n                commit_table.check_unique_constraints(\n                    tx_row_ref,\n                    // Don't check this index since we'll do a 1-1 old/new replacement.\n                    |ixs| ixs.filter(|&(&id, _)| id != index_id),\n                    is_deleted,\n                )\n            } {\n                throw!(IndexError::from(e));\n            }\n\n            let tx_row_ptr = if let Some(old_ptr) = commit_old_ptr {\n                // Row was found in the committed state!\n                //\n                // If the new row is the same as the old,\n                // skip the update altogether to match the semantics of `Self::insert`.\n                //\n                // SAFETY:\n                // 1. `tx_table` is derived from `commit_table` so they have the same layouts.\n                // 2. `old_ptr` was found in an index of `commit_table`, so we know it is valid.\n                // 3. we just inserted `tx_row_ptr` into `tx_table`, so we know it is valid.\n                if unsafe { Table::eq_row_in_page(commit_table, old_ptr, tx_table, tx_row_ptr) } {\n                    // SAFETY: `tx_table.is_row_present(tx_row_ptr)` holds, as noted in 3.\n                    unsafe { tx_table.delete_internal_skip_pointer_map(tx_blob_store, tx_row_ptr) };\n                    // SAFETY: `commit_table.is_row_present(old_ptr)` holds, as noted in 2.\n                    let row_ref = unsafe { commit_table.get_row_ref_unchecked(commit_blob_store, old_ptr) };\n                    return ok(RowRefInsertion::Existed(row_ref));\n                }\n\n                // Check constraints and confirm the insertion of the new row.\n                //\n                // `CHECK_SAME_ROW = false`,\n                // as we know there's a row (`old_ptr`) in the committed state with,\n                // for columns `C`, a unique value X.\n                // For `row` to be identical to another row in the tx state,\n                // it must have the value `X` for `C`,\n                // but it cannot, as the committed state already has `X` for `C`.\n                // So we don't need to check the tx state for a duplicate row.\n                //\n                // SAFETY: `tx_table.is_row_present(row)` holds as we still haven't deleted the row,\n                // in particular, the `write_gen_val_to_col` call does not remove the row.\n                // On error, `tx_row_ptr` has already been removed, so don't do it again.\n                let (_, tx_row_ptr) =\n                    unsafe { tx_table.confirm_insertion::<false>(tx_blob_store, tx_row_ptr, blob_bytes) }?;\n\n                // Delete the old row.\n                del_table.insert(old_ptr);\n                tx_row_ptr\n            } else if let Some(old_ptr) = tx_table\n                .get_index_by_id(index_id)\n                .and_then(|index| index.seek_point(&index_key).next())\n            {\n                // Row was found in the tx state!\n                //\n                // Check constraints and confirm the update of the new row.\n                // This ensures that the old row is removed from the indices\n                // before attempting to insert the new row into the indices.\n                //\n                // SAFETY: `tx_table.is_row_present(tx_row_ptr)` and `tx_table.is_row_present(old_ptr)` both hold\n                // as we've deleted neither.\n                // In particular, the `write_gen_val_to_col` call does not remove the row.\n                let tx_row_ptr = unsafe { tx_table.confirm_update(tx_blob_store, tx_row_ptr, old_ptr, blob_bytes) }?;\n\n                if let Some(old_commit_del_ptr) = old_commit_del_ptr {\n                    // If we have an identical deleted row in the committed state,\n                    // we need to undelete it, just like in `Self::insert`.\n                    // The same note (`insert_undelete`) there re. MVCC applies here as well.\n                    //\n                    // SAFETY:\n                    // 1. `tx_table` is derived from `commit_table` so they have the same layouts.\n                    // 2. `old_commit_del_ptr` was found in an index of `commit_table`.\n                    // 3. we just inserted `tx_row_ptr` into `tx_table`, so we know it is valid.\n                    if unsafe { Table::eq_row_in_page(commit_table, old_commit_del_ptr, tx_table, tx_row_ptr) } {\n                        // It is important that we `confirm_update` first,\n                        // as we must ensure that undeleting the row causes no tx state conflict.\n                        tx_table\n                            .delete(tx_blob_store, tx_row_ptr, |_| ())\n                            .expect(\"Failed to delete a row we just inserted\");\n\n                        // Undelete.\n                        del_table.remove(old_commit_del_ptr);\n\n                        // Return the undeleted committed state row.\n                        // SAFETY: `commit_table.is_row_present(old_commit_del_ptr)` holds.\n                        let row_ref =\n                            unsafe { commit_table.get_row_ref_unchecked(commit_blob_store, old_commit_del_ptr) };\n                        return ok(RowRefInsertion::Existed(row_ref));\n                    }\n                }\n\n                tx_row_ptr\n            } else {\n                throw!(IndexError::KeyNotFound(index_id, index_key));\n            };\n\n            // SAFETY: `tx_table.is_row_present(tx_row_ptr)` holds\n            // per post-condition of `confirm_insertion` and `confirm_update`\n            // in the if/else branches respectively.\n            let row_ref = unsafe { tx_table.get_row_ref_unchecked(tx_blob_store, tx_row_ptr) };\n            return ok(RowRefInsertion::Inserted(row_ref));\n        };\n\n        // When we reach here, we had an error and we need to revert the insertion of `tx_row_ref`.\n        // SAFETY: `tx_table.is_row_present(tx_row_ptr)` holds,\n        // as we still haven't deleted the row physically.\n        unsafe { tx_table.delete_internal_skip_pointer_map(tx_blob_store, tx_row_ptr) };\n        Err(err)\n    }\n\n    pub(super) fn delete(&mut self, table_id: TableId, row_pointer: RowPointer) -> Result<bool> {\n        delete(\n            &mut self.tx_state,\n            &self.committed_state_write_lock,\n            table_id,\n            row_pointer,\n        )\n    }\n\n    // Clears the table for `table_id`, removing all rows.\n    pub fn clear_table(&mut self, table_id: TableId) -> Result<usize> {\n        // Get the commit table.\n        let (commit_table, commit_bs, ..) = self.committed_state_write_lock.get_table_and_blob_store(table_id)?;\n\n        // Get the insert table and delete all rows from it.\n        let (tx_table, tx_blob_store, delete_table) = self\n            .tx_state\n            .get_table_and_blob_store_or_create_from(table_id, commit_table);\n        let mut rows_removed = tx_table.clear(tx_blob_store);\n\n        // Mark every row in the committed state as deleted.\n        for row in commit_table.scan_rows(commit_bs) {\n            delete_table.insert(row.pointer());\n            rows_removed += 1;\n        }\n\n        Ok(rows_removed)\n    }\n}\n\npub(super) fn delete(\n    tx_state: &mut TxState,\n    committed_state: &CommittedState,\n    table_id: TableId,\n    row_pointer: RowPointer,\n) -> Result<bool> {\n    match row_pointer.squashed_offset() {\n        // For newly-inserted rows,\n        // just delete them from the insert tables\n        // - there's no reason to have them in both the insert and delete tables.\n        SquashedOffset::TX_STATE => {\n            let (table, blob_store) = tx_state\n                .get_table_and_blob_store(table_id)\n                .ok_or(TableError::IdNotFoundState(table_id))?;\n            Ok(table.delete(blob_store, row_pointer, |_| ()).is_some())\n        }\n        SquashedOffset::COMMITTED_STATE => {\n            let commit_table = committed_state\n                .get_table(table_id)\n                .expect(\"there's a row in committed state so there should be a committed table\");\n            // NOTE: We trust the `row_pointer` refers to an extant row.\n            // It could have been deleted already in this transaction,\n            // in which case we don't want to return that we deleted it.\n            let deleted = tx_state\n                .get_delete_table_mut(table_id, commit_table)\n                .insert(row_pointer);\n            Ok(deleted)\n        }\n        _ => unreachable!(\"Invalid SquashedOffset for RowPointer: {:?}\", row_pointer),\n    }\n}\n\nimpl MutTxId {\n    pub(super) fn delete_by_row_value(&mut self, table_id: TableId, rel: &ProductValue) -> Result<bool> {\n        // Get commit table and page pool.\n        let page_pool = &self.committed_state_write_lock.page_pool;\n        let (commit_table, ..) = self.committed_state_write_lock.get_table_and_blob_store(table_id)?;\n\n        // Temporarily insert the row into the tx insert table.\n        let (tx_table, tx_blob_store, _) = self\n            .tx_state\n            .get_table_and_blob_store_or_create_from(table_id, commit_table);\n\n        // We only want to physically insert the row here to get a row pointer.\n        // We'd like to avoid any set semantic and unique constraint checks.\n        let (temp_row_ref, _) = tx_table.insert_physically_pv(page_pool, tx_blob_store, rel)?;\n        let temp_ptr = temp_row_ref.pointer();\n\n        // First, check if a matching row exists in the `commit_table`.\n        // If it does, no need to check the `tx_table`.\n        //\n        // We start with `commit_table` as, in most cases,\n        // we'll likely have a transaction that deletes a committed row\n        // rather than deleting a row that was inserted in the same transaction.\n        //\n        // SAFETY:\n        // - `commit_table` and `tx_table` use the same schema.\n        // - `temp_ptr` is valid because we just inserted it.\n        let (hash, to_delete) = unsafe { Table::find_same_row(commit_table, tx_table, tx_blob_store, temp_ptr, None) };\n        let to_delete = to_delete\n            // Not present in commit table? Check if present in the tx table.\n            .or_else(|| {\n                // SAFETY:\n                // - `tx_table` and `tx_table` trivially use the same schema.\n                // - `temp_ptr` is valid because we just inserted it.\n                let (_, to_delete) = unsafe { Table::find_same_row(tx_table, tx_table, tx_blob_store, temp_ptr, hash) };\n                to_delete\n            });\n\n        // Remove the temporary entry from the tx table.\n        // Do this before actually deleting to drop the borrows on the table.\n        // SAFETY: `temp_ptr` is valid because we just inserted it and haven't deleted it since.\n        unsafe {\n            tx_table.delete_internal_skip_pointer_map(tx_blob_store, temp_ptr);\n        }\n\n        // Delete the found row either by marking (commit table)\n        // or by deleting directly (tx table).\n        to_delete\n            .map(|to_delete| self.delete(table_id, to_delete))\n            .unwrap_or(Ok(false))\n    }\n}\n\nimpl StateView for MutTxId {\n    type Iter<'a> = IterMutTx<'a>;\n    type IterByColRange<'a, R: RangeBounds<AlgebraicValue>> = IterByColRangeMutTx<'a, R>;\n    type IterByColEq<'a, 'r>\n        = IterByColEqMutTx<'a, 'r>\n    where\n        Self: 'a;\n\n    fn get_schema(&self, table_id: TableId) -> Option<&Arc<TableSchema>> {\n        // TODO(bikeshedding, docs): should this also check if the schema is in the system tables,\n        // but the table hasn't been constructed yet?\n        // If not, document why.\n\n        // No need to check the tx state.\n        // If the table is not in the committed state, it doesn't exist.\n        self.committed_state_write_lock.get_schema(table_id)\n    }\n\n    fn table_row_count(&self, table_id: TableId) -> Option<u64> {\n        table_row_count(&self.tx_state, &self.committed_state_write_lock, table_id)\n    }\n\n    fn iter(&self, table_id: TableId) -> Result<Self::Iter<'_>> {\n        iter(&self.tx_state, &self.committed_state_write_lock, table_id)\n    }\n\n    fn iter_by_col_range<R: RangeBounds<AlgebraicValue>>(\n        &self,\n        table_id: TableId,\n        cols: ColList,\n        range: R,\n    ) -> Result<Self::IterByColRange<'_, R>> {\n        iter_by_col_range(&self.tx_state, &self.committed_state_write_lock, table_id, cols, range)\n    }\n\n    fn iter_by_col_eq<'r>(\n        &self,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEq<'_, 'r>> {\n        iter_by_col_eq(&self.tx_state, &self.committed_state_write_lock, table_id, cols, value)\n    }\n}\n\nfn table_row_count(tx_state: &TxState, committed_state: &CommittedState, table_id: TableId) -> Option<u64> {\n    let commit_count = committed_state.table_row_count(table_id);\n    let (tx_ins_count, tx_del_count) = tx_state.table_row_count(table_id);\n    let commit_count = commit_count.map(|cc| cc - tx_del_count);\n    // Keep track of whether `table_id` exists.\n    match (commit_count, tx_ins_count) {\n        (Some(cc), Some(ic)) => Some(cc + ic),\n        (Some(c), None) | (None, Some(c)) => Some(c),\n        (None, None) => None,\n    }\n}\n\nfn iter<'a>(tx_state: &'a TxState, committed_state: &'a CommittedState, table_id: TableId) -> Result<IterMutTx<'a>> {\n    IterMutTx::new(table_id, tx_state, committed_state)\n}\n\nfn iter_by_col_range<'a, R: RangeBounds<AlgebraicValue>>(\n    tx_state: &'a TxState,\n    committed_state: &'a CommittedState,\n    table_id: TableId,\n    cols: ColList,\n    range: R,\n) -> Result<IterByColRangeMutTx<'a, R>> {\n    // If there's an index, use that.\n    // It's sufficient to check that the committed state has an index\n    // as index schema changes are applied immediately.\n    if let Some(Ok(commit_iter)) = committed_state.index_seek_range(table_id, &cols, &range) {\n        let tx_iter = tx_state\n            .index_seek_range_by_cols(table_id, &cols, &range)\n            .map(|r| r.expect(\"got a commit index so we should have a compatible tx index\"));\n        let delete_table = tx_state.get_delete_table(table_id);\n        let iter = ScanMutTx::combine(delete_table, tx_iter, commit_iter);\n        Ok(ScanOrIndex::Index(iter))\n    } else {\n        unindexed_iter_by_col_range_warn(tx_state, committed_state, table_id, &cols);\n        let iter = iter(tx_state, committed_state, table_id)?;\n        let filter = RangeOnColumn { cols, range };\n        let iter = ApplyFilter::new(filter, iter);\n        Ok(ScanOrIndex::Scan(iter))\n    }\n}\n\n#[cfg(not(feature = \"unindexed_iter_by_col_range_warn\"))]\nfn unindexed_iter_by_col_range_warn(_: &TxState, _: &CommittedState, _: TableId, _: &ColList) {}\n\n#[cfg(feature = \"unindexed_iter_by_col_range_warn\")]\nfn unindexed_iter_by_col_range_warn(\n    tx_state: &TxState,\n    committed_state: &CommittedState,\n    table_id: TableId,\n    cols: &ColList,\n) {\n    match table_row_count(tx_state, committed_state, table_id) {\n        // TODO(ux): log these warnings to the module logs rather than host logs.\n        None => log::error!(\"iter_by_col_range on unindexed column, but couldn't fetch table `{table_id}`s row count\",),\n        Some(num_rows) => too_many_rows_for_scan_do(committed_state, num_rows, table_id, cols, |name, cols| {\n            log::warn!(\"iter_by_col_range without index: table {name} has {num_rows} rows; scanning columns {cols:?}\",);\n        }),\n    }\n}\n\nfn iter_by_col_eq<'a, 'r>(\n    tx_state: &'a TxState,\n    committed_state: &'a CommittedState,\n    table_id: TableId,\n    cols: impl Into<ColList>,\n    val: &'r AlgebraicValue,\n) -> Result<IterByColEqMutTx<'a, 'r>> {\n    // If there's an index, use that.\n    // It's sufficient to check that the committed state has an index\n    // as index schema changes are applied immediately.\n    let cols = cols.into();\n    if let Some(commit_iter) = committed_state.index_seek_point(table_id, &cols, val) {\n        let tx_iter = tx_state.index_seek_point_by_cols(table_id, &cols, val);\n        let delete_table = tx_state.get_delete_table(table_id);\n        let iter = ScanMutTx::combine(delete_table, tx_iter, commit_iter);\n        Ok(ScanOrIndex::Index(iter))\n    } else {\n        unindexed_iter_by_col_eq_warn(tx_state, committed_state, table_id, &cols);\n        let iter = iter(tx_state, committed_state, table_id)?;\n        let filter = EqOnColumn { cols, val };\n        let iter = ApplyFilter::new(filter, iter);\n        Ok(ScanOrIndex::Scan(iter))\n    }\n}\n\n#[cfg(not(feature = \"unindexed_iter_by_col_range_warn\"))]\nfn unindexed_iter_by_col_eq_warn(_: &TxState, _: &CommittedState, _: TableId, _: &ColList) {}\n\n#[cfg(feature = \"unindexed_iter_by_col_range_warn\")]\nfn unindexed_iter_by_col_eq_warn(\n    tx_state: &TxState,\n    committed_state: &CommittedState,\n    table_id: TableId,\n    cols: &ColList,\n) {\n    match table_row_count(tx_state, committed_state, table_id) {\n        // TODO(ux): log these warnings to the module logs rather than host logs.\n        None => log::error!(\"iter_by_col_eq on unindexed column, but couldn't fetch table `{table_id}`s row count\",),\n        Some(num_rows) => too_many_rows_for_scan_do(committed_state, num_rows, table_id, cols, |name, cols| {\n            log::warn!(\"iter_by_col_eq without index: table {name} has {num_rows} rows; scanning columns {cols:?}\",);\n        }),\n    }\n}\n\n#[cfg(feature = \"unindexed_iter_by_col_range_warn\")]\nfn too_many_rows_for_scan_do(\n    committed_state: &CommittedState,\n    num_rows: u64,\n    table_id: TableId,\n    cols: &ColList,\n    logic: impl FnOnce(&str, &[&str]),\n) {\n    const TOO_MANY_ROWS_FOR_SCAN: u64 = 1000;\n    if num_rows >= TOO_MANY_ROWS_FOR_SCAN {\n        let schema: &Arc<TableSchema> = committed_state.get_schema(table_id).unwrap();\n        let table_name = &schema.table_name;\n        let col_names = cols\n            .iter()\n            .map(|col_id| {\n                schema\n                    .columns()\n                    .get(col_id.idx())\n                    .map(|col| &col.col_name[..])\n                    .unwrap_or(\"[unknown column]\")\n            })\n            .collect::<Vec<_>>();\n        logic(table_name, &col_names);\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/sequence.rs",
    "content": "use spacetimedb_data_structures::map::IntMap;\nuse spacetimedb_primitives::SequenceId;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_schema::schema::SequenceSchema;\n\n#[derive(Debug, PartialEq)]\n// TODO(cloutiertyler): The below was made `pub` for the datastore split. We should\n// investigate if this should be private again.\npub struct Sequence {\n    schema: SequenceSchema,\n    // The next value to be returned by this sequence.\n    value: i128,\n    // The number we have persisted as a lower bound for the next restart.\n    // This is the first value to be returned after a restart, so when we\n    // reach this value, the user needs to call allocate_steps and update\n    // the corresponding system table row.\n    allocated: i128,\n}\n\nimpl MemoryUsage for Sequence {\n    fn heap_usage(&self) -> usize {\n        // MEMUSE: intentionally ignoring schema\n        self.value.heap_usage()\n    }\n}\n\nimpl Sequence {\n    pub(super) fn new(schema: SequenceSchema, previous_allocation: Option<i128>) -> Self {\n        if schema.start < schema.min_value || schema.start > schema.max_value {\n            panic!(\n                \"Invalid sequence: start value {} is out of bounds for sequence with min_value {} and max_value {}\",\n                schema.start, schema.min_value, schema.max_value\n            );\n        }\n        if schema.max_value <= schema.min_value {\n            panic!(\"Invalid sequence: max_value must be greater than min_value\");\n        }\n        if schema.increment == 0 {\n            panic!(\"Invalid sequence: increment must be non-zero\");\n        }\n        if schema.increment.unsigned_abs() >= (schema.max_value - schema.min_value) as u128 {\n            panic!(\n                \"Invalid sequence: increment must be less than or equal to the range between min_value and max_value\"\n            );\n        }\n        let start = if let Some(prev) = previous_allocation {\n            if prev < schema.min_value || prev > schema.max_value {\n                // Previous versions set allocated to 0 as a default,\n                // so we have this special case.\n                if prev == 0 {\n                    schema.start\n                } else {\n                    panic!(\n                        \"Invalid sequence: previous allocation value {prev} is out of bounds for sequence with min_value {} and max_value {}\",\n                        schema.min_value, schema.max_value\n                    );\n                }\n            } else {\n                prev\n            }\n        } else {\n            schema.start\n        };\n        // We will always need to allocate before generating any values.\n        Self {\n            value: start,\n            allocated: start,\n            schema,\n        }\n    }\n\n    /// Update the current value of the sequence.\n    /// This is used on very specific occasions,\n    /// such as cloning a sequence\n    pub(super) fn update_value(&mut self, new_value: i128) {\n        if new_value < self.schema.min_value || new_value > self.schema.max_value {\n            panic!(\n                \"Invalid sequence update: new value {} is out of bounds for sequence with min_value {} and max_value {}\",\n                new_value, self.schema.min_value, self.schema.max_value\n            );\n        }\n        self.value = new_value;\n    }\n\n    pub(super) fn get_value(&self) -> i128 {\n        self.value\n    }\n\n    pub(super) fn id(&self) -> SequenceId {\n        self.schema.sequence_id\n    }\n\n    /// Returns the next value in the sequence given the params.\n    ///\n    /// Examples:\n    /// (min: 1, max: 10, increment: 1, value: 9) -> 1\n    /// (min: 1, max: 10, increment: 20, value: 5) -> 5\n    /// (min: 1, max: 10, increment: 3, value: 5) -> 8\n    /// (min: 1, max: 10, increment: 3, value: 9) -> 2\n    /// (min: 1, max: 10, increment: -3, value: 4) -> 1\n    /// (min: 1, max: 10, increment: -3, value: 1) -> 8\n    fn next_in_sequence(min: i128, max: i128, increment: i128, value: i128) -> i128 {\n        // calculate the next value\n        let mut next = value + increment;\n        // handle wrapping around the sequence\n        if increment > 0 {\n            if next > max {\n                next = min + (next - max - 1) % (max - min + 1);\n            }\n        } else if next < min {\n            next = max - (min - next - 1) % (max - min + 1);\n        }\n        next\n    }\n\n    /// Returns the next value iff no allocation is needed.\n    pub(super) fn gen_next_value(&mut self) -> Option<i128> {\n        if self.needs_allocation() {\n            return None;\n        }\n        let value = self.value;\n        self.value = self.next_value();\n        Some(value)\n    }\n\n    fn next_value(&self) -> i128 {\n        self.nth_value(1)\n    }\n\n    fn nth_value(&self, n: usize) -> i128 {\n        let mut value = self.value;\n        for _ in 0..n {\n            value = Self::next_in_sequence(\n                self.schema.min_value,\n                self.schema.max_value,\n                self.schema.increment,\n                value,\n            );\n        }\n        value\n    }\n\n    /// The allocated value represents the place where the sequence would\n    /// start from if the system memory was lost. Therefore we cannot generate\n    /// the next value in the sequence without the risk of using the same\n    /// value twice in two separate transactions.\n    /// e.g.\n    /// 1. incr = 1, allocated = 10, value = 10\n    /// 2. next_value() -> 11\n    /// 3. commit transaction\n    /// 4. crash\n    /// 5. restart\n    /// 6. incr = 1 allocated = 10, value = 10\n    /// 7. next_value() -> 11\n    fn needs_allocation(&self) -> bool {\n        // On restart we are allowed to begin at the allocation amount, so we stop before we\n        // reach it. It is important that the allocated value is one that would be returned\n        // by the sequence, so we can use equality here. Otherwise, to handle wrapping sequences\n        // correctly, we would need to check if the next value is on the same side of the allocated\n        // value as the current value, which seems more complex.\n        self.value == self.allocated\n    }\n\n    /// Allocate up to `steps` new values in the sequence. This returns the new allocated value,\n    /// which should be written to the corresponding system table row, so that we start generating\n    /// at that value on the next restart.\n    /// This may allocate fewer steps if it is possible to fully loop around the sequence in that\n    /// many steps.\n    pub(super) fn allocate_steps(&mut self, steps: usize) -> i128 {\n        if !self.needs_allocation() {\n            // No allocation needed, return the current allocation.\n            return self.allocated;\n        }\n        let original_allocation = self.allocated;\n        for _ in 0..steps {\n            let next = Self::next_in_sequence(\n                self.schema.min_value,\n                self.schema.max_value,\n                self.schema.increment,\n                self.allocated,\n            );\n            if next == original_allocation {\n                // We have looped all the way around, stop here.\n                break;\n            }\n            self.allocated = next;\n        }\n        if self.needs_allocation() {\n            // This should only be possible if |max - min| == |increment|.\n            // This should be unreachable, since `new` will panic if this would happen.\n            panic!(\"Unable to allocate new sequence value. Sequence parameters are invalid.\")\n        }\n        self.allocated\n    }\n}\n\n/// A map of [`SequenceId`] -> [`Sequence`].\n#[derive(Default, Debug)]\npub(super) struct SequencesState {\n    sequences: IntMap<SequenceId, Sequence>,\n}\n\nimpl MemoryUsage for SequencesState {\n    fn heap_usage(&self) -> usize {\n        let Self { sequences } = self;\n        sequences.heap_usage()\n    }\n}\n\nimpl SequencesState {\n    pub(super) fn get_sequence_mut(&mut self, seq_id: SequenceId) -> Option<&mut Sequence> {\n        self.sequences.get_mut(&seq_id)\n    }\n\n    pub(super) fn insert(&mut self, seq: Sequence) {\n        self.sequences.insert(seq.id(), seq);\n    }\n\n    pub(super) fn remove(&mut self, seq_id: SequenceId) -> Option<Sequence> {\n        self.sequences.remove(&seq_id)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use crate::locking_tx_datastore::sequence::Sequence;\n    use spacetimedb_primitives::{ColId, SequenceId, TableId};\n    use spacetimedb_schema::schema::SequenceSchema;\n\n    #[derive(Clone, Copy)]\n    struct SequenceParams {\n        min: i128,\n        max: i128,\n        increment: i128,\n        start: i128,\n        previous_allocation: Option<i128>,\n    }\n    fn make_test_sequence_schema(params: SequenceParams) -> Sequence {\n        let schema = SequenceSchema {\n            sequence_id: SequenceId(1),\n            min_value: params.min,\n            max_value: params.max,\n            increment: params.increment,\n            start: params.start,\n            col_pos: ColId(1),\n            table_id: TableId(1),\n            sequence_name: \"test_sequence\".into(),\n        };\n        Sequence::new(schema, params.previous_allocation)\n    }\n    #[test]\n    fn test_double_allocation_noops() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 1,\n            start: 1,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_eq!(seq.gen_next_value(), None);\n        let new_alloc = seq.allocate_steps(1);\n        assert_eq!(new_alloc, 2);\n        // Check that trying to allocate again will do nothing if we haven't exhausted\n        // the existing allocation.\n        let new_alloc = seq.allocate_steps(2);\n        assert_eq!(new_alloc, 2);\n        assert_eq!(seq.gen_next_value(), Some(1));\n        assert_eq!(seq.gen_next_value(), None);\n    }\n\n    #[test]\n    fn test_simple_loop() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 1,\n            start: 1,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_sequence_works(&mut seq, seq_params, seq_params.start, 100);\n    }\n\n    #[test]\n    fn test_loop_with_odd_increment() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 100,\n            increment: 3,\n            start: 1,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_sequence_works(&mut seq, seq_params, seq_params.start, 100);\n    }\n\n    #[test]\n    fn test_loop_with_odd_increment_and_even_start() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 100,\n            increment: 3,\n            start: 10,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_sequence_works(&mut seq, seq_params, seq_params.start, 100);\n    }\n\n    #[test]\n    fn test_loop_with_fully_negative_range() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: -100,\n            max: -1,\n            increment: 3,\n            start: -50,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_sequence_works(&mut seq, seq_params, seq_params.start, 100);\n    }\n\n    #[test]\n    fn test_simple_negative_loop() {\n        // A simple sequence that increments by 1 from 1 to 10.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: -1,\n            start: 1,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert_sequence_works(&mut seq, seq_params, seq_params.start, 100);\n    }\n\n    // This function tests that a sequence works correctly by generating `steps` values.\n    // This uses a different way of calculating the next value is that will be more likely to hit\n    // overflow issues, so it won't work if steps * increment overflows i128.\n    fn assert_sequence_works(seq: &mut Sequence, seq_params: SequenceParams, initial_value: i128, steps: i128) {\n        for i in 0..steps {\n            if seq.needs_allocation() {\n                seq.allocate_steps(10);\n            }\n            let val = seq.gen_next_value().unwrap();\n            assert!(\n                val >= seq_params.min && val <= seq_params.max,\n                \"Generated value {val} out of bounds [{}, {}]\",\n                seq_params.min,\n                seq_params.max\n            );\n\n            let range = seq_params.max - seq_params.min + 1;\n            let raw_next = initial_value + i * seq_params.increment;\n            // This is an alternate way to handling wrapping. Since the mod operator can return\n            // negative values in rust, we do the `(n % max + max) % max` trick.\n            let wrapped_next = ((raw_next - seq_params.min) % range + range) % range + seq_params.min;\n            assert_eq!(val, wrapped_next, \"Failed at iteration {i} (0 indexed)\");\n        }\n    }\n\n    #[test]\n    fn test_restarting_after_allocation() {\n        // A simple sequence that increments by 1 from 1 to 100.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 100,\n            increment: 1,\n            start: 1,\n            previous_allocation: None,\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert!(seq.needs_allocation());\n        // We are picking a number lower than the max to avoid wrapping.\n        let new_allocation = seq.allocate_steps(40);\n        let mut previous_value = 0;\n        // Keep going until we exhaust the allocation.\n        while !seq.needs_allocation() {\n            previous_value = seq.gen_next_value().unwrap();\n            // Since this won't wrap, we should get values strictly less than the allocation.\n            assert!(previous_value <= new_allocation);\n        }\n        assert_eq!(previous_value, new_allocation - 1);\n        let restarted_params = SequenceParams {\n            previous_allocation: Some(new_allocation),\n            ..seq_params\n        };\n        let mut restarted_seq = make_test_sequence_schema(restarted_params);\n        assert!(restarted_seq.needs_allocation());\n        restarted_seq.allocate_steps(1);\n        let next_value = restarted_seq.gen_next_value().unwrap();\n        assert_eq!(next_value, new_allocation);\n    }\n\n    #[test]\n    fn test_first_value_is_prev_allocation() {\n        // A simple sequence that increments by 1 from 1 to 100.\n        // The start is set to 1, but this is overridden by the previous allocation of 7.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 100,\n            increment: 1,\n            start: 1,\n            previous_allocation: Some(7),\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert!(seq.needs_allocation());\n        // We are picking a number lower than the max to avoid wrapping.\n        let _ = seq.allocate_steps(1);\n        assert_eq!(7, seq.gen_next_value().unwrap());\n    }\n    #[test]\n    #[should_panic(expected = \"Invalid sequence:\")]\n    fn test_increment_range() {\n        // This is a sequence that would only ever be able to generate one value.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 10,\n            start: 1,\n            previous_allocation: None,\n        };\n        make_test_sequence_schema(seq_params);\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid sequence:\")]\n    fn test_previous_out_of_range() {\n        // This is a sequence that would only ever be able to generate one value.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 1,\n            start: 1,\n            previous_allocation: Some(100),\n        };\n        make_test_sequence_schema(seq_params);\n    }\n\n    #[test]\n    fn test_previous_out_of_range_but_zero() {\n        // This is a sequence that would only ever be able to generate one value.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 1,\n            start: 1,\n            previous_allocation: Some(0),\n        };\n        let mut seq = make_test_sequence_schema(seq_params);\n        assert!(seq.needs_allocation());\n        seq.allocate_steps(1);\n        assert_eq!(1, seq.gen_next_value().unwrap());\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid sequence:\")]\n    fn test_start_out_of_range() {\n        // This is a sequence that would only ever be able to generate one value.\n        let seq_params = SequenceParams {\n            min: 1,\n            max: 10,\n            increment: 1,\n            start: 100,\n            previous_allocation: None,\n        };\n        make_test_sequence_schema(seq_params);\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/state_view.rs",
    "content": "use super::mut_tx::FilterDeleted;\nuse super::{committed_state::CommittedState, datastore::Result, tx_state::TxState};\nuse crate::error::{DatastoreError, TableError};\nuse crate::locking_tx_datastore::mut_tx::{IndexScanPoint, IndexScanRanged};\nuse crate::system_tables::{\n    ConnectionIdViaU128, StColumnAccessorFields, StColumnAccessorRow, StColumnFields, StColumnRow,\n    StConnectionCredentialsFields, StConnectionCredentialsRow, StConstraintFields, StConstraintRow, StEventTableFields,\n    StIndexAccessorFields, StIndexAccessorRow, StIndexFields, StIndexRow, StScheduledFields, StScheduledRow,\n    StSequenceFields, StSequenceRow, StTableAccessorFields, StTableAccessorRow, StTableFields, StTableRow,\n    StViewFields, StViewParamFields, StViewRow, SystemTable, ST_COLUMN_ACCESSOR_ID, ST_COLUMN_ID,\n    ST_CONNECTION_CREDENTIALS_ID, ST_CONSTRAINT_ID, ST_EVENT_TABLE_ID, ST_INDEX_ACCESSOR_ID, ST_INDEX_ID,\n    ST_SCHEDULED_ID, ST_SEQUENCE_ID, ST_TABLE_ACCESSOR_ID, ST_TABLE_ID, ST_VIEW_ID, ST_VIEW_PARAM_ID,\n};\nuse anyhow::anyhow;\nuse core::ops::RangeBounds;\nuse spacetimedb_lib::ConnectionId;\nuse spacetimedb_primitives::{ColList, TableId};\nuse spacetimedb_sats::AlgebraicValue;\nuse spacetimedb_schema::schema::{ColumnSchema, TableSchema, ViewDefInfo};\nuse spacetimedb_table::table::IndexScanPointIter;\nuse spacetimedb_table::{\n    blob_store::HashMapBlobStore,\n    table::{IndexScanRangeIter, RowRef, Table, TableScanIter},\n};\nuse std::sync::Arc;\n\n// StateView trait, is designed to define the behavior of viewing internal datastore states.\n// Currently, it applies to: CommittedState, MutTxId, and TxId.\npub trait StateView {\n    type Iter<'a>: Iterator<Item = RowRef<'a>>\n    where\n        Self: 'a;\n    type IterByColRange<'a, R: RangeBounds<AlgebraicValue>>: Iterator<Item = RowRef<'a>>\n    where\n        Self: 'a;\n    type IterByColEq<'a, 'r>: Iterator<Item = RowRef<'a>>\n    where\n        Self: 'a;\n\n    fn get_schema(&self, table_id: TableId) -> Option<&Arc<TableSchema>>;\n\n    fn table_id_from_name(&self, table_name: &str) -> Result<Option<TableId>> {\n        let name = &<Box<str>>::from(table_name).into();\n        let row = self.iter_by_col_eq(ST_TABLE_ID, StTableFields::TableName, name)?.next();\n        Ok(row.map(|row| row.read_col(StTableFields::TableId).unwrap()))\n    }\n\n    /// Looks up a table id by the table's canonical name or its accessor/alias name.\n    fn table_id_from_name_or_alias(&self, table_name_or_alias: &str) -> Result<Option<TableId>> {\n        if let Some(table_id) = self.table_id_from_name(table_name_or_alias)? {\n            return Ok(Some(table_id));\n        }\n        let Some(row) = self.find_st_table_accessor_row(table_name_or_alias)? else {\n            return Ok(None);\n        };\n        self.table_id_from_name(&row.table_name)\n    }\n\n    /// Returns the number of rows in the table identified by `table_id`.\n    fn table_row_count(&self, table_id: TableId) -> Option<u64>;\n\n    fn iter(&self, table_id: TableId) -> Result<Self::Iter<'_>>;\n\n    fn table_name(&self, table_id: TableId) -> Option<&str> {\n        self.get_schema(table_id).map(|s| &*s.table_name)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the values of `cols` are contained in `range`.\n    fn iter_by_col_range<R: RangeBounds<AlgebraicValue>>(\n        &self,\n        table_id: TableId,\n        cols: ColList,\n        range: R,\n    ) -> Result<Self::IterByColRange<'_, R>>;\n\n    fn iter_by_col_eq<'a, 'r>(\n        &'a self,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEq<'a, 'r>>;\n\n    /// Look up the `st_table` row which describes `table_id`.\n    ///\n    /// Default method calls `iter_by_col_eq`.\n    /// The implementation for [`super::committed_state::CommittedState`]\n    /// overwrites this method to inspect an additional side table during replay\n    /// to handle replay of schema-altering migrations.\n    fn find_st_table_row(&self, table_id: TableId) -> Result<StTableRow> {\n        let row_ref = self\n            .iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &table_id.into())?\n            .next()\n            .ok_or_else(|| TableError::IdNotFound(SystemTable::st_table, table_id.into()))?;\n        StTableRow::try_from(row_ref)\n    }\n\n    /// Look up an `st_table_accessor` row by its accessor name\n    fn find_st_table_accessor_row(&self, accessor_name: &str) -> Result<Option<StTableAccessorRow>> {\n        self.iter_by_col_eq(\n            ST_TABLE_ACCESSOR_ID,\n            StTableAccessorFields::AccessorName,\n            &accessor_name.into(),\n        )?\n        .next()\n        .map(StTableAccessorRow::try_from)\n        .transpose()\n    }\n\n    /// Look up an `st_index_accessor` row by its accessor name\n    fn find_st_index_accessor_row(&self, accessor_name: &str) -> Result<Option<StIndexAccessorRow>> {\n        self.iter_by_col_eq(\n            ST_INDEX_ACCESSOR_ID,\n            StIndexAccessorFields::AccessorName,\n            &accessor_name.into(),\n        )?\n        .next()\n        .map(StIndexAccessorRow::try_from)\n        .transpose()\n    }\n\n    /// Look up an `st_column_accessor` row by its canonical table and column names\n    fn find_st_column_accessor_row(&self, table_name: &str, col_name: &str) -> Result<Option<StColumnAccessorRow>> {\n        match self.iter_by_col_eq(\n            ST_COLUMN_ACCESSOR_ID,\n            [StColumnAccessorFields::TableName, StColumnAccessorFields::ColName],\n            &AlgebraicValue::product([table_name.into(), col_name.into()]),\n        ) {\n            Ok(mut iter) => iter.next().map(StColumnAccessorRow::try_from).transpose(),\n            // `schema_for_table_raw` is called while restoring snapshots,\n            // before `migrate_system_tables` creates newer system tables.\n            // We therefore treat a missing `st_column_accessor` as \"no aliases yet\".\n            //\n            // Note this is different behavior from `find_st_table_accessor_row`,\n            // because that utility is used for name resolution **after** startup,\n            // where missing accessor tables should be surfaced as real errors.\n            Err(DatastoreError::Table(TableError::IdNotFound(..))) => Ok(None),\n            Err(e) => Err(e),\n        }\n    }\n\n    /// Reads the schema information for the specified `table_id` directly from the database.\n    fn schema_for_table_raw(&self, table_id: TableId) -> Result<TableSchema> {\n        // Look up the table_name for the table in question.\n        let row = self.find_st_table_row(table_id)?;\n        let table_name = row.table_name;\n        let table_id: TableId = row.table_id;\n        let table_type = row.table_type;\n        let table_access = row.table_access;\n        let table_primary_key = row.table_primary_key.as_ref().and_then(ColList::as_singleton);\n\n        // Look up the columns for the table in question.\n        let mut columns: Vec<ColumnSchema> = iter_st_column_for_table(self, &table_id.into())?\n            .map(|row_ref| {\n                let row = StColumnRow::try_from(row_ref)?;\n                let mut column_schema = ColumnSchema::from(row);\n                let alias = self\n                    .find_st_column_accessor_row(table_name.as_ref(), &column_schema.col_name)?\n                    .map(|row| row.accessor_name);\n                column_schema.alias = alias;\n                Ok(column_schema)\n            })\n            .collect::<Result<Vec<_>>>()?;\n        columns.sort_by_key(|col| col.col_pos);\n\n        let value_eq = &AlgebraicValue::from(table_id);\n\n        // Look up the constraints for the table in question.\n        let constraints = self\n            .iter_by_col_eq(ST_CONSTRAINT_ID, StConstraintFields::TableId, value_eq)?\n            .map(|row| {\n                let row = StConstraintRow::try_from(row)?;\n                Ok(row.into())\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        // Look up the sequences for the table in question.\n        let sequences = self\n            .iter_by_col_eq(ST_SEQUENCE_ID, StSequenceFields::TableId, value_eq)?\n            .map(|row| {\n                let row = StSequenceRow::try_from(row)?;\n                Ok(row.into())\n            })\n            .collect::<Result<Vec<_>>>()?;\n\n        // Look up the indexes for the table in question.\n        let indexes = self\n            .iter_by_col_eq(ST_INDEX_ID, StIndexFields::TableId, value_eq)?\n            .map(|row| StIndexRow::try_from(row).map(Into::into))\n            .collect::<Result<Vec<_>>>()?;\n\n        let schedule = self\n            .iter_by_col_eq(ST_SCHEDULED_ID, StScheduledFields::TableId, value_eq)?\n            .next()\n            .map(|row| -> Result<_> {\n                let row = StScheduledRow::try_from(row)?;\n                Ok(row.into())\n            })\n            .transpose()?;\n\n        // Look up the view info for the table in question, if any.\n        let view_info: Option<ViewDefInfo> = self\n            .iter_by_col_eq(\n                ST_VIEW_ID,\n                StViewFields::TableId,\n                &AlgebraicValue::OptionSome(value_eq.clone()),\n            )\n            .map(|mut iter| {\n                iter.next().map(|row| -> Result<_> {\n                    let row = StViewRow::try_from(row)?;\n                    let has_args = self\n                        .iter_by_col_eq(ST_VIEW_PARAM_ID, StViewParamFields::ViewId, &row.view_id.into())?\n                        .next()\n                        .is_some();\n\n                    Ok(ViewDefInfo {\n                        view_id: row.view_id,\n                        has_args,\n                        is_anonymous: row.is_anonymous,\n                    })\n                })\n            })\n            .unwrap_or(None)\n            .transpose()?;\n\n        // Check if this table is an event table by looking up `st_event_table`.\n        //\n        // When restoring a pre-event-tables snapshot, `st_event_table` won't exist yet:\n        //   1. `restore_from_snapshot` restores pages and calls `schema_for_table_raw` (here)\n        //   2. `apply_history` replays the commit log\n        //   3. `migrate_system_tables` creates any missing system tables, including `st_event_table`\n        //\n        // We're in step 1, so `st_event_table` may not exist. Default to `false` in that case.\n        // After step 3 it will be a proper (empty) system table.\n        let is_event = match self.iter_by_col_eq(ST_EVENT_TABLE_ID, StEventTableFields::TableId, value_eq) {\n            Ok(mut iter) => iter.next().is_some(),\n            Err(DatastoreError::Table(TableError::IdNotFound(..))) => false,\n            Err(e) => return Err(e),\n        };\n        // During restore from snapshots produced before `st_table_accessor` existed,\n        // this system table is missing until `migrate_system_tables` runs.\n        // Handle that here so schema reconstruction can proceed during restore.\n        let table_alias = match self.iter_by_col_eq(\n            ST_TABLE_ACCESSOR_ID,\n            StTableAccessorFields::TableName,\n            &table_name.as_ref().into(),\n        ) {\n            Ok(mut iter) => iter\n                .next()\n                .map(StTableAccessorRow::try_from)\n                .transpose()?\n                .map(|row| row.accessor_name),\n            Err(DatastoreError::Table(TableError::IdNotFound(..))) => None,\n            Err(e) => return Err(e),\n        };\n        Ok(TableSchema::new(\n            table_id,\n            table_name,\n            view_info,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            table_type,\n            table_access,\n            schedule,\n            table_primary_key,\n            is_event,\n            table_alias,\n        ))\n    }\n\n    /// Reads the schema information for the specified `table_id`, consulting the `cache` first.\n    ///\n    /// If the schema is not found in the cache, the method calls [Self::schema_for_table_raw].\n    ///\n    /// Note: The responsibility of populating the cache is left to the caller.\n    fn schema_for_table(&self, table_id: TableId) -> Result<Arc<TableSchema>> {\n        if let Some(schema) = self.get_schema(table_id) {\n            return Ok(schema.clone());\n        }\n\n        self.schema_for_table_raw(table_id).map(Arc::new)\n    }\n\n    fn get_jwt_payload(&self, connection_id: ConnectionId) -> Result<Option<String>> {\n        log::info!(\"Getting JWT payload for connection id: {}\", connection_id.to_hex());\n        let mut buf: Vec<u8> = Vec::new();\n        self.iter_by_col_eq(\n            ST_CONNECTION_CREDENTIALS_ID,\n            StConnectionCredentialsFields::ConnectionId,\n            &ConnectionIdViaU128::from(connection_id).into(),\n        )?\n            .next()\n            .map(|row| row.read_via_bsatn::<StConnectionCredentialsRow>(&mut buf).map(|r| r.jwt_payload))\n            .transpose()\n            .map_err(|e| {\n                log::error!(\n                    \"[{connection_id}]: get_jwt_payload: failed to get JWT payload for connection id ({connection_id}), error: {e}\"\n                );\n                DatastoreError::Other(\n                    anyhow!(\n                        \"Failed to get JWT payload for connection id ({connection_id}): {e}\"\n                    )\n                )\n            })\n    }\n}\n\n/// Returns an iterator over all `st_column` rows for `table_id`.\npub(crate) fn iter_st_column_for_table<'a>(\n    this: &'a (impl StateView + ?Sized),\n    table_id: &'a AlgebraicValue,\n) -> Result<impl 'a + Iterator<Item = RowRef<'a>>> {\n    this.iter_by_col_eq(ST_COLUMN_ID, StColumnFields::TableId, table_id)\n}\n\npub struct IterMutTx<'a> {\n    tx_state_ins: Option<(&'a Table, &'a HashMapBlobStore)>,\n    stage: ScanStage<'a>,\n}\n\nimpl<'a> IterMutTx<'a> {\n    pub(super) fn new(table_id: TableId, tx_state: &'a TxState, committed_state: &'a CommittedState) -> Result<Self> {\n        // If the table exist, the committed state has it as we apply schema changes immediately.\n        let Some(commit_table) = committed_state.get_table(table_id) else {\n            return Err(TableError::IdNotFound(SystemTable::st_table, table_id.0).into());\n        };\n\n        // I can neither confirm nor deny that we have a tx insert table.\n        let tx_state_ins = tx_state\n            .insert_tables\n            .get(&table_id)\n            .map(|table| (table, &tx_state.blob_store));\n\n        let iter = commit_table.scan_rows(&committed_state.blob_store);\n        let stage = if let Some(deletes) = tx_state.get_delete_table(table_id) {\n            // There are deletes in the tx state\n            // so we must exclude those (1b).\n            let iter = FilterDeleted { iter, deletes };\n            ScanStage::CommittedWithTxDeletes { iter }\n        } else {\n            // There are no deletes in the tx state\n            // so we don't need to care about those (1a).\n            ScanStage::CommittedNoTxDeletes { iter }\n        };\n\n        Ok(Self { tx_state_ins, stage })\n    }\n}\n\nenum ScanStage<'a> {\n    /// Yielding rows from the current tx.\n    CurrentTx { iter: TableScanIter<'a> },\n    /// Yielding rows from the committed state\n    /// without considering tx state deletes as there are none.\n    CommittedNoTxDeletes { iter: TableScanIter<'a> },\n    /// Yielding rows from the committed state\n    /// but there are deleted rows in the tx state,\n    /// so we must check against those.\n    CommittedWithTxDeletes { iter: FilterDeleted<'a, TableScanIter<'a>> },\n}\n\nimpl<'a> Iterator for IterMutTx<'a> {\n    type Item = RowRef<'a>;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        // The finite state machine goes:\n        //\n        //  CommittedNoTxDeletes ------\\\n        //                             |----> CurrentTx ---> STOP\n        //  CommittedWithTxDeletes ----/\n        loop {\n            match &mut self.stage {\n                ScanStage::CommittedNoTxDeletes { iter } => {\n                    // (1a) Go through the committed state for this table\n                    // but do not consider deleted rows.\n                    if let next @ Some(_) = iter.next() {\n                        return next;\n                    }\n                }\n                ScanStage::CommittedWithTxDeletes { iter } => {\n                    // (1b) Check the committed row's state in the current tx.\n                    // If it's been deleted, skip it.\n                    // If it's still present, yield it.\n                    // Note that the committed state and the insert tables are disjoint sets,\n                    // so at this point we know the row will not be yielded in (3).\n                    //\n                    // NOTE for future MVCC implementors:\n                    // In MVCC, it is no longer valid to elide inserts in this way.\n                    // When a transaction inserts a row, that row *must* appear in its insert tables,\n                    // even if the row is already present in the committed state.\n                    //\n                    // Imagine a chain of committed but un-squashed transactions:\n                    // `Committed 0: Insert Row A` - `Committed 1: Delete Row A`\n                    // where `Committed 1` happens after `Committed 0`.\n                    // Imagine a transaction `Running 2: Insert Row A`,\n                    // which began before `Committed 1` was committed.\n                    // Because `Committed 1` has since been committed,\n                    // `Running 2` *must* happen after `Committed 1`.\n                    // Therefore, the correct sequence of events is:\n                    // - Insert Row A\n                    // - Delete Row A\n                    // - Insert Row A\n                    // This is impossible to recover if `Running 2` elides its insert.\n                    //\n                    // As a result, in MVCC, this branch will need to check if the `row_ref`\n                    // also exists in the `tx_state.insert_tables` and ensure it is yielded only once.\n                    if let next @ Some(_) = iter.next() {\n                        return next;\n                    }\n                }\n                ScanStage::CurrentTx { iter } => {\n                    // (3) look for inserts in the current tx.\n                    return iter.next();\n                }\n            }\n\n            // (2) We got here, so we must've exhausted the committed changes.\n            // Start looking in the current tx for inserts, if any, in (3).\n            let (insert_table, blob_store) = self.tx_state_ins?;\n            let iter = insert_table.scan_rows(blob_store);\n            self.stage = ScanStage::CurrentTx { iter };\n        }\n    }\n}\n\n/// A filter on a row.\npub trait RowFilter {\n    /// Does this filter include `row`?\n    fn filter<'a>(&self, row: RowRef<'a>) -> bool;\n}\n\n/// A row filter that matches `range` for the given `cols` of rows.\npub struct RangeOnColumn<R> {\n    pub cols: ColList,\n    pub range: R,\n}\n\nimpl<R: RangeBounds<AlgebraicValue>> RowFilter for RangeOnColumn<R> {\n    fn filter<'a>(&self, row: RowRef<'a>) -> bool {\n        self.range.contains(&row.project(&self.cols).unwrap())\n    }\n}\n\n/// A row filter that matches `val` for the given `cols` of rows.\npub struct EqOnColumn<'r> {\n    pub cols: ColList,\n    pub val: &'r AlgebraicValue,\n}\n\nimpl RowFilter for EqOnColumn<'_> {\n    fn filter<'a>(&self, row: RowRef<'a>) -> bool {\n        self.val == &row.project(&self.cols).unwrap()\n    }\n}\n\n/// Applies filter `F` to `I`, producing another iterator.\npub struct ApplyFilter<F, I> {\n    iter: I,\n    filter: F,\n}\n\nimpl<F, I> ApplyFilter<F, I> {\n    /// Returns an iterator that applies `filer` to `iter`.\n    pub(super) fn new(filter: F, iter: I) -> Self {\n        Self { iter, filter }\n    }\n}\n\nimpl<'a, F: RowFilter, I: Iterator<Item = RowRef<'a>>> Iterator for ApplyFilter<F, I> {\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.find(|row| self.filter.filter(*row))\n    }\n}\n\ntype ScanFilterTx<'a, F> = ApplyFilter<F, TableScanIter<'a>>;\npub type IterByColRangeTx<'a, R> = ScanOrIndex<ScanFilterTx<'a, RangeOnColumn<R>>, IndexScanRangeIter<'a>>;\npub type IterByColEqTx<'a, 'r> = ScanOrIndex<ScanFilterTx<'a, EqOnColumn<'r>>, IndexScanPointIter<'a>>;\n\ntype ScanFilterMutTx<'a, F> = ApplyFilter<F, IterMutTx<'a>>;\npub type IterByColRangeMutTx<'a, R> = ScanOrIndex<ScanFilterMutTx<'a, RangeOnColumn<R>>, IndexScanRanged<'a>>;\npub type IterByColEqMutTx<'a, 'r> = ScanOrIndex<ScanFilterMutTx<'a, EqOnColumn<'r>>, IndexScanPoint<'a>>;\n\n/// An iterator that either scans or index scans.\npub enum ScanOrIndex<S, I> {\n    /// When the column in question does not have an index.\n    Scan(S),\n\n    /// When the column has an index.\n    Index(I),\n}\n\nimpl<'a, S, I> Iterator for ScanOrIndex<S, I>\nwhere\n    S: Iterator<Item = RowRef<'a>>,\n    I: Iterator<Item = RowRef<'a>>,\n{\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            Self::Scan(iter) => iter.next(),\n            Self::Index(iter) => iter.next(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/tx.rs",
    "content": "use super::{\n    committed_state::CommittedState,\n    datastore::{Result, TxMetrics},\n    state_view::{IterByColRangeTx, StateView},\n    IterByColEqTx, SharedReadGuard,\n};\nuse crate::{error::IndexError, execution_context::ExecutionContext};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_execution::Datastore;\nuse spacetimedb_lib::metrics::ExecutionMetrics;\nuse spacetimedb_primitives::{ColList, IndexId, TableId};\nuse spacetimedb_sats::AlgebraicValue;\nuse spacetimedb_schema::{reducer_name::ReducerName, schema::TableSchema};\nuse spacetimedb_table::{\n    table::{IndexScanPointIter, IndexScanRangeIter, TableAndIndex, TableScanIter},\n    table_index::IndexCannotSeekRange,\n};\nuse std::sync::Arc;\nuse std::{future, num::NonZeroU64};\nuse std::{\n    ops::RangeBounds,\n    time::{Duration, Instant},\n};\n\n/// A read-only transaction with a shared lock on the committed state.\npub struct TxId {\n    pub(super) committed_state_shared_lock: SharedReadGuard<CommittedState>,\n    pub(super) lock_wait_time: Duration,\n    pub(super) timer: Instant,\n    // TODO(cloutiertyler): The below were made `pub` for the datastore split. We should\n    // make these private again.\n    pub ctx: ExecutionContext,\n    pub metrics: ExecutionMetrics,\n}\n\nimpl Datastore for TxId {\n    type TableIter<'a>\n        = TableScanIter<'a>\n    where\n        Self: 'a;\n\n    type RangeIndexIter<'a>\n        = IndexScanRangeIter<'a>\n    where\n        Self: 'a;\n\n    type PointIndexIter<'a>\n        = IndexScanPointIter<'a>\n    where\n        Self: 'a;\n\n    fn row_count(&self, table_id: TableId) -> u64 {\n        self.committed_state_shared_lock\n            .table_row_count(table_id)\n            .unwrap_or_default()\n    }\n\n    fn table_scan<'a>(&'a self, table_id: TableId) -> anyhow::Result<Self::TableIter<'a>> {\n        self.committed_state_shared_lock\n            .table_scan(table_id)\n            .ok_or_else(|| anyhow::anyhow!(\"TableId `{table_id}` does not exist\"))\n    }\n\n    fn index_scan_range<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> anyhow::Result<Self::RangeIndexIter<'a>> {\n        self.with_index(table_id, index_id, |i| i.seek_range(range))?\n            .map_err(|IndexCannotSeekRange| IndexError::IndexCannotSeekRange(index_id).into())\n    }\n\n    fn index_scan_point<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        point: &AlgebraicValue,\n    ) -> anyhow::Result<Self::PointIndexIter<'a>> {\n        self.with_index(table_id, index_id, |i| i.seek_point(point))\n    }\n}\n\nimpl StateView for TxId {\n    type Iter<'a> = TableScanIter<'a>;\n    type IterByColRange<'a, R: RangeBounds<AlgebraicValue>> = IterByColRangeTx<'a, R>;\n    type IterByColEq<'a, 'r>\n        = IterByColEqTx<'a, 'r>\n    where\n        Self: 'a;\n\n    fn get_schema(&self, table_id: TableId) -> Option<&Arc<TableSchema>> {\n        self.committed_state_shared_lock.get_schema(table_id)\n    }\n\n    fn table_row_count(&self, table_id: TableId) -> Option<u64> {\n        self.committed_state_shared_lock.table_row_count(table_id)\n    }\n\n    fn iter(&self, table_id: TableId) -> Result<Self::Iter<'_>> {\n        self.committed_state_shared_lock.iter(table_id)\n    }\n\n    /// Returns an iterator,\n    /// yielding every row in the table identified by `table_id`,\n    /// where the values of `cols` are contained in `range`.\n    fn iter_by_col_range<R: RangeBounds<AlgebraicValue>>(\n        &self,\n        table_id: TableId,\n        cols: ColList,\n        range: R,\n    ) -> Result<Self::IterByColRange<'_, R>> {\n        self.committed_state_shared_lock\n            .iter_by_col_range(table_id, cols, range)\n    }\n\n    fn iter_by_col_eq<'a, 'r>(\n        &'a self,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEq<'a, 'r>> {\n        self.committed_state_shared_lock.iter_by_col_eq(table_id, cols, value)\n    }\n}\n\nimpl TxId {\n    fn with_index<'a, R>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        seek: impl FnOnce(TableAndIndex<'a>) -> R,\n    ) -> anyhow::Result<R> {\n        self.committed_state_shared_lock\n            .get_table(table_id)\n            .ok_or_else(|| anyhow::anyhow!(\"TableId `{table_id}` does not exist\"))\n            .and_then(|table| {\n                table\n                    .get_index_by_id_with_table(&self.committed_state_shared_lock.blob_store, index_id)\n                    .map(seek)\n                    .ok_or_else(|| anyhow::anyhow!(\"IndexId `{index_id}` does not exist\"))\n            })\n    }\n\n    /// Release this read-only transaction,\n    /// allowing new mutable transactions to start if this was the last read-only transaction.\n    ///\n    /// Returns:\n    /// - [`TxOffset`], the smallest transaction offset visible to this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran within this transaction.\n    pub(super) fn release(self) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        // A read tx doesn't consume `next_tx_offset`, so subtract one to obtain\n        // the offset that was visible to the transaction.\n        //\n        // Note that technically the tx could have run against an empty database,\n        // in which case we'd wrongly return zero (a non-existent transaction).\n        // This doesn not happen in practice, however, as [RelationalDB::set_initialized]\n        // creates a transaction.\n        let tx_offset = self.committed_state_shared_lock.next_tx_offset.saturating_sub(1);\n        let tx_metrics = TxMetrics::new(\n            &self.ctx,\n            self.timer,\n            self.lock_wait_time,\n            self.metrics,\n            true,\n            None,\n            &self.committed_state_shared_lock,\n        );\n        let reducer = self.ctx.into_reducer_name();\n        (tx_offset, tx_metrics, reducer)\n    }\n\n    /// The Number of Distinct Values (NDV) for a column or list of columns,\n    /// if there's an index available on `cols`.\n    ///\n    /// Returns `Error` if:\n    /// - No such table as `table_id` exists.\n    /// - The table `table_id` does not have an index on exactly the `cols`.\n    ///\n    /// Returns `Zero` if:\n    /// - The table `table_id` contains zero rows (i.e. the index is empty).\n    ///\n    /// Otherwise, `NonZero` is returned.\n    ///\n    // This method must never return 0, as it's used as the divisor in quotients.\n    // Do not change its return type to a bare `u64`.\n    pub fn num_distinct_values(&self, table_id: TableId, cols: &ColList) -> NumDistinctValues {\n        let Some((_, index)) = self\n            .committed_state_shared_lock\n            .get_table(table_id)\n            .and_then(|table| table.get_index_by_cols(cols))\n        else {\n            return NumDistinctValues::Error;\n        };\n\n        match NonZeroU64::new(index.num_keys() as u64) {\n            Some(val) => NumDistinctValues::NonZero(val),\n            None => NumDistinctValues::Zero,\n        }\n    }\n\n    pub fn tx_offset(&self) -> future::Ready<TxOffset> {\n        future::ready(self.committed_state_shared_lock.next_tx_offset)\n    }\n}\n\n/// The Number of Distinct Values (NDV) for an index.\npub enum NumDistinctValues {\n    /// There was an error in computing the NDV.\n    Error,\n    /// Zero distinct values. The table has zero rows.\n    Zero,\n    /// Non-zero distinct values.\n    NonZero(NonZeroU64),\n}\n"
  },
  {
    "path": "crates/datastore/src/locking_tx_datastore/tx_state.rs",
    "content": "use super::{delete_table::DeleteTable, sequence::Sequence};\nuse core::ops::RangeBounds;\nuse spacetimedb_data_structures::map::IntMap;\nuse spacetimedb_lib::db::auth::StAccess;\nuse spacetimedb_primitives::{ColList, ConstraintId, IndexId, SequenceId, TableId};\nuse spacetimedb_sats::{memory_usage::MemoryUsage, AlgebraicValue};\nuse spacetimedb_schema::schema::{ColumnSchema, ConstraintSchema, IndexSchema, SequenceSchema};\nuse spacetimedb_table::{\n    blob_store::{BlobStore, HashMapBlobStore},\n    indexes::{RowPointer, SquashedOffset},\n    pointer_map::PointerMap,\n    static_assert_size,\n    table::{IndexScanPointIter, IndexScanRangeIter, RowRef, Table, TableAndIndex},\n    table_index::{IndexSeekRangeResult, TableIndex},\n};\nuse std::collections::{btree_map, BTreeMap};\nuse thin_vec::ThinVec;\n\n/// A mapping to find the actual index given an `IndexId`.\npub(super) type IndexIdMap = IntMap<IndexId, TableId>;\n\n/// `TxState` tracks all of the modifications made during a particular transaction.\n/// Rows inserted during a transaction will be added to insert_tables, and similarly,\n/// rows deleted in the transaction will be added to delete_tables.\n///\n/// Note that the state of a row at the beginning of a transaction is not tracked here,\n/// but rather in the `CommittedState` structure.\n///\n/// Note that because a transaction may have several operations performed on the same\n/// row, it is not the case that a call to insert a row guarantees that the row\n/// will be present in `insert_tables`. Rather, a row will be present in `insert_tables`\n/// if the cumulative effect of all the calls results in the row being inserted during\n/// this transaction. The same holds for delete tables.\n///\n/// For a concrete example, suppose a row is already present in a table at the start\n/// of a transaction. A call to delete that row will enter it into `delete_tables`.\n/// A subsequent call to reinsert that row will not put it into `insert_tables`, but\n/// instead remove it from `delete_tables`, as the cumulative effect is to do nothing.\n///\n/// This data structure also tracks modifications beyond inserting and deleting rows.\n/// In particular, creating indexes and sequences is tracked by `insert_tables`.\n///\n/// This means that we have the following invariants, within `TxState` and also\n/// the corresponding `CommittedState`:\n///   - any row in `insert_tables` must not be in the associated `CommittedState`\n///   - any row in `delete_tables` must be in the associated `CommittedState`\n///   - any row cannot be in both `insert_tables` and `delete_tables`\n#[derive(Default)]\npub(super) struct TxState {\n    //NOTE: Need to preserve order to correctly restore the db after reopen\n    /// For any `TableId` that has had a row inserted into it in this TX\n    /// (which may have since been deleted),\n    /// a separate `Table` containing only the new insertions.\n    ///\n    /// `RowPointer`s into the `insert_tables` use `SquashedOffset::TX_STATE`.\n    pub(super) insert_tables: BTreeMap<TableId, Table>,\n\n    /// For any `TableId` that has had a previously-committed row deleted from it,\n    /// a set of the deleted previously-committed rows.\n    ///\n    /// Any `RowPointer` in this set will have `SquashedOffset::COMMITTED_STATE`.\n    pub(super) delete_tables: BTreeMap<TableId, DeleteTable>,\n\n    /// A blob store for those blobs referred to by the `insert_tables`.\n    ///\n    /// When committing the TX, these blobs will be copied into the committed state blob store.\n    /// Keeping the two separate makes rolling back a TX faster,\n    /// as otherwise we'd have to either:\n    /// - Maintain the set of newly-referenced blob hashes in the `TxState`,\n    ///   and free each of them during rollback.\n    /// - Traverse all rows in the `insert_tables` and free each of their blobs during rollback.\n    pub(super) blob_store: HashMapBlobStore,\n\n    /// All of the immediately applied schema changes to the committed state during this transaction.\n    ///\n    /// This is stored as a `ThinVec` as it would be very uncommon to add anything to this list.\n    pub(super) pending_schema_changes: ThinVec<PendingSchemaChange>,\n}\n\nstatic_assert_size!(TxState, 88);\n\nimpl MemoryUsage for TxState {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            insert_tables,\n            delete_tables,\n            blob_store,\n            pending_schema_changes,\n        } = self;\n        insert_tables.heap_usage()\n            + delete_tables.heap_usage()\n            + blob_store.heap_usage()\n            + pending_schema_changes.heap_usage()\n    }\n}\n\n/// A pending schema change is a change to a `TableSchema`\n/// that has been applied immediately to the [`CommittedState`](super::committed_state::CommittedState)\n/// and which need to be reverted if the transaction fails.\n///\n/// The goal here is that by applying changes immediately,\n/// most of the datastore does not have to care about schema change transactionality.\n/// The places that do need to care about changes are those that make them, and merge/rollback.\n/// Architecting this way should benefit performance both during transactions and merge.\n/// On rollback, it should be fairly cheap to e.g., just re-add an index or drop it on the floor.\n#[derive(Debug, PartialEq)]\npub enum PendingSchemaChange {\n    /// The [`TableIndex`] / [`IndexSchema`] with `IndexId`\n    /// was removed from the table with [`TableId`].\n    IndexRemoved(TableId, IndexId, TableIndex, IndexSchema),\n    /// The index with [`IndexId`] was added.\n    /// If adding this index caused the pointer map to be removed,\n    /// it will be present here.\n    IndexAdded(TableId, IndexId, Option<PointerMap>),\n    /// The [`Table`] with [`TableId`] was removed.\n    TableRemoved(TableId, Table),\n    /// The table with [`TableId`] was added.\n    TableAdded(TableId),\n    /// The access of the table with [`TableId`] was changed.\n    /// The old access was stored.\n    TableAlterAccess(TableId, StAccess),\n    /// The row type of the table with [`TableId`] was changed.\n    /// The old column schemas was stored.\n    /// Only non-representational row-type changes are allowed here,\n    /// so existing rows in the table will be compatible with the new row type.\n    TableAlterRowType(TableId, Vec<ColumnSchema>),\n    /// The constraint with [`ConstraintSchema`] was added to the table with [`TableId`].\n    ConstraintRemoved(TableId, ConstraintSchema),\n    /// The constraint with [`ConstraintId`] was added to the table with [`TableId`].\n    ConstraintAdded(TableId, ConstraintId),\n    /// The [`Sequence`] with [`SequenceSchema`] was added to the table with [`TableId`].\n    SequenceRemoved(TableId, Sequence, SequenceSchema),\n    /// The sequence with [`SequenceId`] was added to the table with [`TableId`].\n    SequenceAdded(TableId, SequenceId),\n}\n\nimpl MemoryUsage for PendingSchemaChange {\n    fn heap_usage(&self) -> usize {\n        match self {\n            Self::IndexRemoved(table_id, index_id, table_index, index_schema) => {\n                table_id.heap_usage() + index_id.heap_usage() + table_index.heap_usage() + index_schema.heap_usage()\n            }\n            Self::IndexAdded(table_id, index_id, pointer_map) => {\n                table_id.heap_usage() + index_id.heap_usage() + pointer_map.heap_usage()\n            }\n            Self::TableRemoved(table_id, table) => table_id.heap_usage() + table.heap_usage(),\n            Self::TableAdded(table_id) => table_id.heap_usage(),\n            Self::TableAlterAccess(table_id, st_access) => table_id.heap_usage() + st_access.heap_usage(),\n            Self::TableAlterRowType(table_id, column_schemas) => table_id.heap_usage() + column_schemas.heap_usage(),\n            Self::ConstraintRemoved(table_id, constraint_schema) => {\n                table_id.heap_usage() + constraint_schema.heap_usage()\n            }\n            Self::ConstraintAdded(table_id, constraint_id) => table_id.heap_usage() + constraint_id.heap_usage(),\n            Self::SequenceRemoved(table_id, sequence, sequence_schema) => {\n                table_id.heap_usage() + sequence.heap_usage() + sequence_schema.heap_usage()\n            }\n            Self::SequenceAdded(table_id, sequence_id) => table_id.heap_usage() + sequence_id.heap_usage(),\n        }\n    }\n}\n\nimpl TxState {\n    /// Returns the row count in insert tables\n    /// and the number of rows deleted from committed state.\n    pub(super) fn table_row_count(&self, table_id: TableId) -> (Option<u64>, u64) {\n        let del_count = self.delete_tables.get(&table_id).map(|dt| dt.len() as u64).unwrap_or(0);\n        let ins_count = self.insert_tables.get(&table_id).map(|it| it.row_count);\n        (ins_count, del_count)\n    }\n\n    /// When there's an index on `cols`,\n    /// returns an iterator over the `TableIndex` that yields all the [`RowRef`]s\n    /// that match the specified `range` in the indexed column.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    ///\n    /// For a unique index this will always yield at most one `RowRef`\n    /// when `range` is a point.\n    /// When there is no index this returns `None`.\n    pub(super) fn index_seek_range_by_cols<'a>(\n        &'a self,\n        table_id: TableId,\n        cols: &ColList,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> Option<IndexSeekRangeResult<IndexScanRangeIter<'a>>> {\n        self.insert_tables\n            .get(&table_id)?\n            .get_index_by_cols_with_table(&self.blob_store, cols)\n            .map(|i| i.seek_range(range))\n    }\n\n    /// When there's an index on `cols`,\n    /// returns an iterator over the `TableIndex` that yields all the [`RowRef`]s\n    /// that match the specified `range` in the indexed column.\n    ///\n    /// Matching is defined by `Eq for AlgebraicValue`.\n    ///\n    /// For a unique index this will always yield at most one `RowRef`.\n    /// When there is no index this returns `None`.\n    pub(super) fn index_seek_point_by_cols<'a>(\n        &'a self,\n        table_id: TableId,\n        cols: &ColList,\n        point: &AlgebraicValue,\n    ) -> Option<IndexScanPointIter<'a>> {\n        self.insert_tables\n            .get(&table_id)?\n            .get_index_by_cols_with_table(&self.blob_store, cols)\n            .map(|i| i.seek_point(point))\n    }\n\n    /// Returns the table for `table_id` combined with the index for `index_id`, if both exist.\n    pub(super) fn get_index_by_id_with_table(&self, table_id: TableId, index_id: IndexId) -> Option<TableAndIndex<'_>> {\n        self.insert_tables\n            .get(&table_id)?\n            .get_index_by_id_with_table(&self.blob_store, index_id)\n    }\n\n    // TODO(perf, deep-integration): Make this unsafe. Add the following to the docs:\n    //\n    // # Safety\n    //\n    // `pointer` must refer to a row within the table at `table_id`\n    // which was previously inserted and has not been deleted since.\n    //\n    // See [`RowRef::new`] for more detailed requirements.\n    //\n    // Showing that `pointer` was the result of a call to `self.insert`\n    // with `table_id`\n    // and has not been passed to `self.delete`\n    // is sufficient to demonstrate that a call to `self.get` is safe.\n    pub(super) fn get(&self, table_id: TableId, row_ptr: RowPointer) -> RowRef<'_> {\n        debug_assert!(\n            row_ptr.squashed_offset().is_tx_state(),\n            \"Cannot get COMMITTED_STATE row_ptr from TxState.\",\n        );\n        let table = self\n            .insert_tables\n            .get(&table_id)\n            .expect(\"Attempt to get TX_STATE row from table not present in insert_tables.\");\n\n        // TODO(perf, deep-integration): Use `get_row_ref_unchecked`.\n        table.get_row_ref(&self.blob_store, row_ptr).unwrap()\n    }\n\n    pub(super) fn is_deleted(&self, table_id: TableId, row_ptr: RowPointer) -> bool {\n        debug_assert!(\n            row_ptr.squashed_offset().is_committed_state(),\n            \"Not meaningful to have a deleted TX_STATE row; it would just be removed from the insert_tables.\",\n        );\n        self.delete_tables\n            .get(&table_id)\n            .map(|tbl| tbl.contains(row_ptr))\n            .unwrap_or(false)\n    }\n\n    /// Returns the [DeleteTable] for the given `table_id`, checking if it's empty.\n    pub(super) fn get_delete_table(&self, table_id: TableId) -> Option<&DeleteTable> {\n        self.delete_tables.get(&table_id).filter(|x| !x.is_empty())\n    }\n\n    /// Guarantees that the `table_id` returns a `DeleteTable`.\n    pub(super) fn get_delete_table_mut(&mut self, table_id: TableId, commit_table: &Table) -> &mut DeleteTable {\n        get_delete_table_mut(&mut self.delete_tables, table_id, commit_table)\n    }\n\n    pub(super) fn get_table_and_blob_store(&mut self, table_id: TableId) -> Option<(&mut Table, &mut dyn BlobStore)> {\n        let table = self.insert_tables.get_mut(&table_id)?;\n        let blob_store = &mut self.blob_store;\n        Some((table, blob_store))\n    }\n\n    pub(super) fn get_table_and_blob_store_or_create_from(\n        &mut self,\n        table_id: TableId,\n        template: &Table,\n    ) -> TxTableForInsertion<'_> {\n        let insert_tables = &mut self.insert_tables;\n        let blob_store = &mut self.blob_store;\n        let table = match insert_tables.entry(table_id) {\n            btree_map::Entry::Vacant(e) => {\n                let new_table = template.clone_structure(SquashedOffset::TX_STATE);\n                e.insert(new_table)\n            }\n            btree_map::Entry::Occupied(e) => e.into_mut(),\n        };\n        let delete_table = get_delete_table_mut(&mut self.delete_tables, table_id, table);\n        (table, blob_store, delete_table)\n    }\n\n    /// Assumes that the insert and delete tables exist for `table_id` and fetches them.\n    ///\n    /// # Safety\n    ///\n    /// The insert and delete tables must exist.\n    pub unsafe fn assume_present_get_mut_table(&mut self, table_id: TableId) -> TxTableForInsertion<'_> {\n        let tx_blob_store: &mut dyn BlobStore = &mut self.blob_store;\n        let tx_table = self.insert_tables.get_mut(&table_id);\n        // SAFETY: we successfully got a `tx_table` before and haven't removed it since.\n        let tx_table = unsafe { tx_table.unwrap_unchecked() };\n        let delete_table = self.delete_tables.get_mut(&table_id);\n        // SAFETY: we successfully got a `delete_table` before and haven't removed it since.\n        let delete_table = unsafe { delete_table.unwrap_unchecked() };\n        (tx_table, tx_blob_store, delete_table)\n    }\n}\n\npub(super) type TxTableForInsertion<'a> = (&'a mut Table, &'a mut dyn BlobStore, &'a mut DeleteTable);\n\nfn get_delete_table_mut<'a>(\n    delete_tables: &'a mut BTreeMap<TableId, DeleteTable>,\n    table_id: TableId,\n    table: &Table,\n) -> &'a mut DeleteTable {\n    delete_tables\n        .entry(table_id)\n        .or_insert_with(|| DeleteTable::new(table.row_size()))\n}\n"
  },
  {
    "path": "crates/datastore/src/system_tables.rs",
    "content": "//! Schema definitions and accesses to the system tables,\n//! which store metadata about a SpacetimeDB database.\n//!\n//! When defining a new system table, remember to:\n//! - Define constants for its ID and name.\n//! - Name it in singular (`st_column` not `st_columns`).\n//! - Add a type `St(...)Row` to define its schema, deriving SpacetimeType.\n//!     - You will probably need to add a new ID type in `spacetimedb_primitives`,\n//!       with trait implementations in `spacetimedb_sats::{typespace, de::impl, ser::impl}`.\n//! - Add it to [`system_tables`], and define a constant for its index there.\n//! - Use [`st_fields_enum`] to define its column enum.\n//! - Register its schema in [`system_module_def`], making sure to call `validate_system_table` at the end of the function.\n\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\nuse spacetimedb_lib::db::auth::{StAccess, StTableType};\nuse spacetimedb_lib::db::raw_def::v9::{btree, RawSql};\nuse spacetimedb_lib::db::raw_def::*;\nuse spacetimedb_lib::de::{Deserialize, DeserializeOwned, Error};\nuse spacetimedb_lib::ser::Serialize;\nuse spacetimedb_lib::st_var::StVarValue;\nuse spacetimedb_lib::{ConnectionId, Identity, ProductValue, SpacetimeType, Timestamp};\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::algebraic_value::de::ValueDeserializer;\nuse spacetimedb_sats::algebraic_value::ser::value_serialize;\nuse spacetimedb_sats::hash::Hash;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, u256, AlgebraicType, AlgebraicValue, ArrayValue};\nuse spacetimedb_schema::def::{\n    BTreeAlgorithm, ConstraintData, DirectAlgorithm, HashAlgorithm, IndexAlgorithm, ModuleDef, UniqueConstraintData,\n};\nuse spacetimedb_schema::identifier::Identifier;\nuse spacetimedb_schema::schema::{\n    ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, ScheduleSchema, Schema, SequenceSchema,\n    TableSchema,\n};\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_table::table::RowRef;\nuse std::borrow::Cow;\nuse std::cell::RefCell;\nuse std::str::FromStr;\nuse strum::Display;\nuse v9::{RawModuleDefV9Builder, TableType};\n\nuse super::error::DatastoreError;\n\n/// The static ID of the table that defines tables\npub const ST_TABLE_ID: TableId = TableId(1);\n/// The static ID of the table that defines columns\npub const ST_COLUMN_ID: TableId = TableId(2);\n/// The static ID of the table that defines sequences\npub const ST_SEQUENCE_ID: TableId = TableId(3);\n/// The static ID of the table that defines indexes\npub const ST_INDEX_ID: TableId = TableId(4);\n/// The static ID of the table that defines constraints\npub const ST_CONSTRAINT_ID: TableId = TableId(5);\n/// The static ID of the table that defines the stdb module associated with\n/// the database\npub const ST_MODULE_ID: TableId = TableId(6);\n/// The static ID of the table that defines connected clients\npub const ST_CLIENT_ID: TableId = TableId(7);\n/// The static ID of the table that defines system variables\npub const ST_VAR_ID: TableId = TableId(8);\n/// The static ID of the table that defines scheduled tables\npub const ST_SCHEDULED_ID: TableId = TableId(9);\n\n/// The static ID of the table that defines the row level security (RLS) policies\npub const ST_ROW_LEVEL_SECURITY_ID: TableId = TableId(10);\n\n/// The static ID of the table that stores the credentials for each connection.\npub const ST_CONNECTION_CREDENTIALS_ID: TableId = TableId(11);\n\n/// The static ID of the table that tracks views\npub const ST_VIEW_ID: TableId = TableId(12);\n/// The static ID of the table that tracks view parameters\npub const ST_VIEW_PARAM_ID: TableId = TableId(13);\n/// The static ID of the table that tracks view columns\npub const ST_VIEW_COLUMN_ID: TableId = TableId(14);\n/// The static ID of the table that tracks the number of clients subscribed to each view\npub const ST_VIEW_SUB_ID: TableId = TableId(15);\n/// The static ID of the table that tracks view arguments\npub const ST_VIEW_ARG_ID: TableId = TableId(16);\n/// The static ID of the table that tracks which tables are event tables\npub const ST_EVENT_TABLE_ID: TableId = TableId(17);\n/// The static ID of the table that maps canonical table names to accessor names\npub const ST_TABLE_ACCESSOR_ID: TableId = TableId(18);\n/// The static ID of the table that maps canonical index names to accessor names\npub const ST_INDEX_ACCESSOR_ID: TableId = TableId(19);\n/// The static ID of the table that maps canonical column names to accessor names\npub const ST_COLUMN_ACCESSOR_ID: TableId = TableId(20);\n\npub(crate) const ST_CONNECTION_CREDENTIALS_NAME: &str = \"st_connection_credentials\";\npub const ST_TABLE_NAME: &str = \"st_table\";\npub const ST_COLUMN_NAME: &str = \"st_column\";\npub const ST_SEQUENCE_NAME: &str = \"st_sequence\";\npub const ST_INDEX_NAME: &str = \"st_index\";\npub(crate) const ST_CONSTRAINT_NAME: &str = \"st_constraint\";\npub(crate) const ST_MODULE_NAME: &str = \"st_module\";\npub(crate) const ST_CLIENT_NAME: &str = \"st_client\";\npub(crate) const ST_SCHEDULED_NAME: &str = \"st_scheduled\";\npub(crate) const ST_VAR_NAME: &str = \"st_var\";\npub(crate) const ST_ROW_LEVEL_SECURITY_NAME: &str = \"st_row_level_security\";\npub(crate) const ST_VIEW_NAME: &str = \"st_view\";\npub(crate) const ST_VIEW_PARAM_NAME: &str = \"st_view_param\";\npub(crate) const ST_VIEW_COLUMN_NAME: &str = \"st_view_column\";\npub(crate) const ST_VIEW_SUB_NAME: &str = \"st_view_sub\";\npub(crate) const ST_VIEW_ARG_NAME: &str = \"st_view_arg\";\npub(crate) const ST_EVENT_TABLE_NAME: &str = \"st_event_table\";\npub(crate) const ST_TABLE_ACCESSOR_NAME: &str = \"st_table_accessor\";\npub(crate) const ST_INDEX_ACCESSOR_NAME: &str = \"st_index_accessor\";\npub(crate) const ST_COLUMN_ACCESSOR_NAME: &str = \"st_column_accessor\";\n/// Reserved range of sequence values used for system tables.\n///\n/// Ids for user-created tables will start at `ST_RESERVED_SEQUENCE_RANGE`.\n/// Versions before 1.4 started at one more that number.\n///\n/// The range applies to all sequences allocated by system tables, i.e. table-,\n/// sequence-, index-, and constraint-ids.\n/// > Note that column-ids are positional indices and not based on a sequence.\n///\n/// These ids can be referred to statically even for system tables introduced\n/// after a database was created, so as long as the range is not exceeded.\n///\n/// However unlikely it may seem, it is advisable to check for overflow in the\n/// test suite when adding sequences to system tables.\npub const ST_RESERVED_SEQUENCE_RANGE: u32 = 4096;\n\n/// Is `table_id` reserved as a system table?\npub fn table_id_is_reserved(table_id: TableId) -> bool {\n    table_id.0 <= ST_RESERVED_SEQUENCE_RANGE\n}\n\n/// For a `row` in the table `table_id`, is `row` a schema meta-descriptor for a reserved system table?\n///\n/// Returns true e.g. for the `st_table` row `{ table_id: ST_VIEW_ID, table_name: \"st_view\", .. }`,\n/// but false e.g. for the `st_table` row `{ table_id: ST_RESERVED_SEQUENCE_RANGE + 1, table_name: \"some_user_table\", .. }`.\n/// Also returns false if `table_id` is not a system table.\npub fn is_built_in_meta_row(table_id: TableId, row: &ProductValue) -> Result<bool, anyhow::Error> {\n    fn to_typed_row<T: DeserializeOwned>(row: &ProductValue) -> Result<T, anyhow::Error> {\n        T::deserialize(ValueDeserializer::new(AlgebraicValue::Product(row.clone())))\n            .map_err(|e| anyhow::anyhow!(\"Failed to deserialize {row:?} as {}: {e:?}\", std::any::type_name::<T>()))\n    }\n\n    Ok(match table_id {\n        ST_TABLE_ID => {\n            let row: StTableRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_COLUMN_ID => {\n            let row: StColumnRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_SEQUENCE_ID => {\n            let row: StSequenceRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_INDEX_ID => {\n            let row: StIndexRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_CONSTRAINT_ID => {\n            let row: StConstraintRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_MODULE_ID | ST_CLIENT_ID | ST_VAR_ID => false,\n        ST_SCHEDULED_ID => {\n            // We don't have any scheduled system tables as of writing (pgoldman 2025-12-16),\n            // but no harm in future-proofing.\n            let row: StScheduledRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_ROW_LEVEL_SECURITY_ID => {\n            // We don't install any RLS rules on system tables automatically, but users can.\n            // This means that `st_row_level_security` rules are never system meta-descriptors,\n            // in the sense that if they exist, they come from users.\n            false\n        }\n        ST_CONNECTION_CREDENTIALS_ID => false,\n        // We don't define any system views, so none of the view-related tables can be system meta-descriptors.\n        ST_VIEW_ID | ST_VIEW_PARAM_ID | ST_VIEW_COLUMN_ID | ST_VIEW_SUB_ID | ST_VIEW_ARG_ID => false,\n        ST_EVENT_TABLE_ID => {\n            let row: StEventTableRow = to_typed_row(row)?;\n            table_id_is_reserved(row.table_id)\n        }\n        ST_TABLE_ACCESSOR_ID | ST_INDEX_ACCESSOR_ID | ST_COLUMN_ACCESSOR_ID => false,\n        TableId(..ST_RESERVED_SEQUENCE_RANGE) => {\n            log::warn!(\"Unknown system table {table_id:?}\");\n            false\n        }\n        _ => false,\n    })\n}\n\n// This help to keep the correct order when bootstrapping\n#[allow(non_camel_case_types)]\n#[derive(Debug, Display)]\npub enum SystemTable {\n    st_table,\n    st_view,\n    st_column,\n    st_sequence,\n    st_index,\n    st_constraint,\n    st_row_level_security,\n    st_table_accessor,\n}\n\npub fn system_tables() -> [TableSchema; 20] {\n    [\n        // The order should match the `id` of the system table, that start with [ST_TABLE_IDX].\n        st_table_schema(),\n        st_column_schema(),\n        st_index_schema(),\n        st_constraint_schema(),\n        st_module_schema(),\n        st_client_schema(),\n        st_var_schema(),\n        st_scheduled_schema(),\n        st_row_level_security_schema(),\n        st_sequence_schema(),\n        st_connection_credential_schema(),\n        st_view_schema(),\n        st_view_param_schema(),\n        st_view_column_schema(),\n        st_view_sub_schema(),\n        st_view_arg_schema(),\n        st_event_table_schema(),\n        st_table_accessor_schema(),\n        st_index_accessor_schema(),\n        st_column_accessor_schema(),\n    ]\n}\n\n/// Types that represent the fields / columns of a system table.\npub trait StFields: Copy + Sized {\n    /// Returns the column position of the system table field.\n    fn col_id(self) -> ColId;\n\n    /// Returns the column index of the system table field.\n    #[inline]\n    fn col_idx(self) -> usize {\n        self.col_id().idx()\n    }\n\n    /// Returns the column name of the system table field a static string slice.\n    fn name(self) -> &'static str;\n\n    /// Returns the column name of the system table field as a [`RawIdentifier`].\n    #[inline]\n    fn col_name(self) -> Identifier {\n        Identifier::new_assume_valid(self.name().into())\n    }\n\n    /// Return all fields of this type, in order.\n    fn fields() -> &'static [Self];\n}\n\n// The following are indices into the array returned by [`system_tables`].\npub(crate) const ST_TABLE_IDX: usize = 0;\npub(crate) const ST_COLUMN_IDX: usize = 1;\npub(crate) const ST_INDEX_IDX: usize = 2;\npub(crate) const ST_CONSTRAINT_IDX: usize = 3;\npub(crate) const ST_MODULE_IDX: usize = 4;\npub(crate) const ST_CLIENT_IDX: usize = 5;\npub(crate) const ST_VAR_IDX: usize = 6;\npub(crate) const ST_SCHEDULED_IDX: usize = 7;\npub(crate) const ST_ROW_LEVEL_SECURITY_IDX: usize = 8;\npub(crate) const ST_SEQUENCE_IDX: usize = 9;\npub(crate) const ST_CONNECTION_CREDENTIALS_IDX: usize = 10;\npub(crate) const ST_VIEW_IDX: usize = 11;\npub(crate) const ST_VIEW_PARAM_IDX: usize = 12;\npub(crate) const ST_VIEW_COLUMN_IDX: usize = 13;\npub(crate) const ST_VIEW_SUB_IDX: usize = 14;\npub(crate) const ST_VIEW_ARG_IDX: usize = 15;\npub(crate) const ST_EVENT_TABLE_IDX: usize = 16;\npub(crate) const ST_TABLE_ACCESSOR_IDX: usize = 17;\npub(crate) const ST_INDEX_ACCESSOR_IDX: usize = 18;\npub(crate) const ST_COLUMN_ACCESSOR_IDX: usize = 19;\n\nmacro_rules! st_fields_enum {\n    ($(#[$attr:meta])* enum $ty_name:ident { $($name:expr, $var:ident = $discr:expr,)* }) => {\n        #[derive(Copy, Clone, Debug)]\n        $(#[$attr])*\n        pub enum $ty_name {\n            $($var = $discr,)*\n        }\n\n        impl StFields for $ty_name {\n            #[inline]\n            fn col_id(self) -> ColId {\n                ColId(self as _)\n            }\n\n            #[inline]\n            fn name(self) -> &'static str {\n                match self {\n                    $(Self::$var => $name,)*\n                }\n            }\n\n            fn fields() -> &'static [$ty_name] {\n                &[$($ty_name::$var,)*]\n            }\n        }\n\n        impl From<$ty_name> for ColId {\n            fn from(value: $ty_name) -> Self {\n                value.col_id()\n            }\n        }\n    }\n}\n\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StTableFields {\n    \"table_id\", TableId = 0,\n    \"table_name\", TableName = 1,\n    \"table_type\", TableType = 2,\n    \"table_access\", TablesAccess = 3,\n    \"table_primary_key\", PrimaryKey = 4,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StViewFields {\n    \"view_id\", ViewId = 0,\n    \"view_name\", ViewName = 1,\n    \"table_id\", TableId = 2,\n    \"is_public\", IsPublic = 3,\n    \"is_anonymous\", IsAnonymous = 4,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StColumnFields {\n    \"table_id\", TableId = 0,\n    \"col_pos\", ColPos = 1,\n    \"col_name\", ColName = 2,\n    \"col_type\", ColType = 3,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StViewColumnFields {\n    \"view_id\", ViewId = 0,\n    \"col_pos\", ColPos = 1,\n    \"col_name\", ColName = 2,\n    \"col_type\", ColType = 3,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StViewSubFields {\n    \"view_id\", ViewId = 0,\n    \"arg_id\", ArgId = 1,\n    \"identity\", Identity = 2,\n    \"num_subscribers\", NumSubscribers = 3,\n    \"has_subscribers\", HasSubscribers = 4,\n    \"last_called\", LastCalled = 5,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StViewArgFields {\n    \"id\", Id = 0,\n    \"bytes\", Bytes = 1,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StViewParamFields {\n    \"view_id\", ViewId = 0,\n    \"param_pos\", ParamPos = 1,\n    \"param_name\", ParamName = 2,\n    \"param_type\", ParamType = 3,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StIndexFields {\n    \"index_id\", IndexId = 0,\n    \"table_id\", TableId = 1,\n    \"index_name\", IndexName = 2,\n    \"index_algorithm\", IndexAlgorithm = 3,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(\n    /// The fields that define the internal table [crate::db::relational_db::ST_SEQUENCES_NAME].\n    enum StSequenceFields {\n    \"sequence_id\", SequenceId = 0,\n    \"sequence_name\", SequenceName = 1,\n    \"table_id\", TableId = 2,\n    \"col_pos\", ColPos = 3,\n    \"increment\", Increment = 4,\n    \"start\", Start = 5,\n    \"min_value\", MinValue = 6,\n    \"max_value\", MaxValue = 7,\n    \"allocated\", Allocated = 8,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StConstraintFields {\n    \"constraint_id\", ConstraintId = 0,\n    \"constraint_name\", ConstraintName = 1,\n    \"table_id\", TableId = 2,\n    \"constraint_data\", ConstraintData = 3,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StRowLevelSecurityFields {\n    \"table_id\", TableId = 0,\n    \"sql\", Sql = 1,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StModuleFields {\n    \"database_identity\", DatabaseIdentity = 0,\n    \"owner_identity\", OwnerIdentity = 1,\n    \"program_kind\", ProgramKind = 2,\n    \"program_hash\", ProgramHash = 3,\n    \"program_bytes\", ProgramBytes = 4,\n    \"module_version\", ModuleVersion = 5,\n});\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StClientFields {\n    \"identity\", Identity = 0,\n    \"connection_id\", ConnectionId = 1,\n});\n\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StConnectionCredentialsFields {\n    \"connection_id\", ConnectionId = 0,\n    \"jwt_payload\", JwtPayload = 1,\n});\n\n// WARNING: For a stable schema, don't change the field names and discriminants.\nst_fields_enum!(enum StVarFields {\n    \"name\", Name = 0,\n    \"value\", Value = 1,\n});\n\nst_fields_enum!(enum StScheduledFields {\n    \"schedule_id\", ScheduleId = 0,\n    \"table_id\", TableId = 1,\n    \"reducer_name\", ReducerName = 2,\n    \"schedule_name\", ScheduleName = 3,\n    \"at_column\", AtColumn = 4,\n});\n\nst_fields_enum!(enum StEventTableFields {\n    \"table_id\", TableId = 0,\n});\n\nst_fields_enum!(enum StTableAccessorFields {\n    \"table_name\", TableName = 0,\n    \"accessor_name\", AccessorName = 1,\n});\n\nst_fields_enum!(enum StIndexAccessorFields {\n    \"index_name\", IndexName = 0,\n    \"accessor_name\", AccessorName = 1,\n});\n\nst_fields_enum!(enum StColumnAccessorFields {\n    \"table_name\", TableName = 0,\n    \"col_name\", ColName = 1,\n    \"accessor_name\", AccessorName = 2,\n});\n\n/// Helper method to check that a system table has the correct fields.\n/// Does not check field types since those aren't included in `StFields` types.\n/// If anything in here is not true, the system is completely broken, so it's fine to assert.\nfn validate_system_table<T: StFields + 'static>(def: &ModuleDef, table_name: &str) {\n    let table = def.table(table_name).expect(\"missing system table definition\");\n    let fields = T::fields();\n    assert_eq!(table.columns.len(), fields.len());\n    for field in T::fields() {\n        let col = table\n            .columns\n            .get(field.col_id().idx())\n            .expect(\"missing system table field\");\n        assert_eq!(&col.name[..], field.name());\n    }\n}\n\n/// See the comment on [`SYSTEM_MODULE_DEF`].\nfn system_module_def() -> ModuleDef {\n    let mut builder = RawModuleDefV9Builder::new();\n\n    let st_table_type = builder.add_type::<StTableRow>();\n    builder\n        .build_table(ST_TABLE_NAME, *st_table_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StTableFields::TableId)\n        .with_index_no_accessor_name(btree(StTableFields::TableId))\n        .with_unique_constraint(StTableFields::TableName)\n        .with_index_no_accessor_name(btree(StTableFields::TableName));\n\n    let st_view_type = builder.add_type::<StViewRow>();\n    builder\n        .build_table(ST_VIEW_NAME, *st_view_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StViewFields::ViewId)\n        .with_index_no_accessor_name(btree(StViewFields::ViewId))\n        .with_unique_constraint(StViewFields::ViewName)\n        .with_index_no_accessor_name(btree(StViewFields::ViewName));\n\n    let st_raw_column_type = builder.add_type::<StColumnRow>();\n    let st_col_row_unique_cols = [StColumnFields::TableId.col_id(), StColumnFields::ColPos.col_id()];\n    builder\n        .build_table(ST_COLUMN_NAME, *st_raw_column_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(st_col_row_unique_cols)\n        .with_index_no_accessor_name(btree(st_col_row_unique_cols));\n\n    let st_view_col_type = builder.add_type::<StViewColumnRow>();\n    let st_view_col_unique_cols = [StViewColumnFields::ViewId.col_id(), StViewColumnFields::ColPos.col_id()];\n    builder\n        .build_table(ST_VIEW_COLUMN_NAME, *st_view_col_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(st_view_col_unique_cols)\n        .with_index_no_accessor_name(btree(st_view_col_unique_cols));\n\n    let st_view_param_type = builder.add_type::<StViewParamRow>();\n    let st_view_param_unique_cols = [StViewParamFields::ViewId.col_id(), StViewParamFields::ParamPos.col_id()];\n    builder\n        .build_table(ST_VIEW_PARAM_NAME, *st_view_param_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(st_view_param_unique_cols)\n        .with_index_no_accessor_name(btree(st_view_param_unique_cols));\n\n    let st_view_sub_type = builder.add_type::<StViewSubRow>();\n    builder\n        .build_table(ST_VIEW_SUB_NAME, *st_view_sub_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_index_no_accessor_name(btree(StViewSubFields::Identity))\n        .with_index_no_accessor_name(btree(StViewSubFields::HasSubscribers))\n        .with_index_no_accessor_name(btree([\n            StViewSubFields::ViewId,\n            StViewSubFields::ArgId,\n            StViewSubFields::Identity,\n        ]));\n\n    let st_view_arg_type = builder.add_type::<StViewArgRow>();\n    builder\n        .build_table(ST_VIEW_ARG_NAME, *st_view_arg_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StViewArgFields::Id)\n        .with_index_no_accessor_name(btree(StViewArgFields::Id))\n        .with_unique_constraint(StViewArgFields::Bytes)\n        .with_index_no_accessor_name(btree(StViewArgFields::Bytes));\n\n    let st_index_type = builder.add_type::<StIndexRow>();\n    builder\n        .build_table(ST_INDEX_NAME, *st_index_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StIndexFields::IndexId)\n        .with_index_no_accessor_name(btree(StIndexFields::IndexId));\n    // TODO(1.0): unique constraint on name?\n\n    let st_sequence_type = builder.add_type::<StSequenceRow>();\n    builder\n        .build_table(ST_SEQUENCE_NAME, *st_sequence_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StSequenceFields::SequenceId)\n        .with_index_no_accessor_name(btree(StSequenceFields::SequenceId));\n    // TODO(1.0): unique constraint on name?\n\n    let st_constraint_type = builder.add_type::<StConstraintRow>();\n    builder\n        .build_table(ST_CONSTRAINT_NAME, *st_constraint_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_auto_inc_primary_key(StConstraintFields::ConstraintId)\n        .with_index_no_accessor_name(btree(StConstraintFields::ConstraintId));\n    // TODO(1.0): unique constraint on name?\n\n    let st_row_level_security_type = builder.add_type::<StRowLevelSecurityRow>();\n    builder\n        .build_table(\n            ST_ROW_LEVEL_SECURITY_NAME,\n            *st_row_level_security_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_primary_key(StRowLevelSecurityFields::Sql)\n        .with_unique_constraint(StRowLevelSecurityFields::Sql)\n        .with_index_no_accessor_name(btree(StRowLevelSecurityFields::Sql))\n        .with_index_no_accessor_name(btree(StRowLevelSecurityFields::TableId));\n\n    let st_module_type = builder.add_type::<StModuleRow>();\n    builder\n        .build_table(ST_MODULE_NAME, *st_module_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System);\n    // TODO: add empty unique constraint here, once we've implemented those.\n\n    let st_connection_credentials_type = builder.add_type::<StConnectionCredentialsRow>();\n    builder\n        .build_table(\n            ST_CONNECTION_CREDENTIALS_NAME,\n            *st_connection_credentials_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_unique_constraint(StConnectionCredentialsFields::ConnectionId)\n        .with_index_no_accessor_name(btree(StConnectionCredentialsFields::ConnectionId))\n        .with_access(v9::TableAccess::Private)\n        .with_primary_key(StConnectionCredentialsFields::ConnectionId);\n\n    let st_client_type = builder.add_type::<StClientRow>();\n    let st_client_unique_cols = [StClientFields::Identity, StClientFields::ConnectionId];\n    builder\n        .build_table(ST_CLIENT_NAME, *st_client_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(st_client_unique_cols) // FIXME: this is a noop?\n        .with_index_no_accessor_name(btree(st_client_unique_cols));\n\n    let st_schedule_type = builder.add_type::<StScheduledRow>();\n    builder\n        .build_table(ST_SCHEDULED_NAME, *st_schedule_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(StScheduledFields::TableId) // FIXME: this is a noop?\n        .with_index_no_accessor_name(btree(StScheduledFields::TableId))\n        .with_auto_inc_primary_key(StScheduledFields::ScheduleId) // FIXME: this is a noop?\n        .with_index_no_accessor_name(btree(StScheduledFields::ScheduleId));\n    // TODO(1.0): unique constraint on name?\n\n    let st_var_type = builder.add_type::<StVarRow>();\n    builder\n        .build_table(ST_VAR_NAME, *st_var_type.as_ref().expect(\"should be ref\"))\n        .with_type(TableType::System)\n        .with_unique_constraint(StVarFields::Name) // FIXME: this is a noop?\n        .with_index_no_accessor_name(btree(StVarFields::Name))\n        .with_primary_key(StVarFields::Name);\n\n    let st_event_table_type = builder.add_type::<StEventTableRow>();\n    builder\n        .build_table(\n            ST_EVENT_TABLE_NAME,\n            *st_event_table_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_primary_key(StEventTableFields::TableId)\n        .with_unique_constraint(StEventTableFields::TableId)\n        .with_index_no_accessor_name(btree(StEventTableFields::TableId));\n\n    let st_table_accessor_type = builder.add_type::<StTableAccessorRow>();\n    builder\n        .build_table(\n            ST_TABLE_ACCESSOR_NAME,\n            *st_table_accessor_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_unique_constraint(StTableAccessorFields::TableName)\n        .with_index_no_accessor_name(btree(StTableAccessorFields::TableName))\n        .with_unique_constraint(StTableAccessorFields::AccessorName)\n        .with_index_no_accessor_name(btree(StTableAccessorFields::AccessorName));\n\n    let st_index_accessor_type = builder.add_type::<StIndexAccessorRow>();\n    builder\n        .build_table(\n            ST_INDEX_ACCESSOR_NAME,\n            *st_index_accessor_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_unique_constraint(StIndexAccessorFields::IndexName)\n        .with_index_no_accessor_name(btree(StIndexAccessorFields::IndexName))\n        .with_unique_constraint(StIndexAccessorFields::AccessorName)\n        .with_index_no_accessor_name(btree(StIndexAccessorFields::AccessorName));\n\n    let st_column_accessor_type = builder.add_type::<StColumnAccessorRow>();\n    let st_column_accessor_table_col_cols = [\n        StColumnAccessorFields::TableName.col_id(),\n        StColumnAccessorFields::ColName.col_id(),\n    ];\n    let st_column_accessor_table_alias_cols = [\n        StColumnAccessorFields::TableName.col_id(),\n        StColumnAccessorFields::AccessorName.col_id(),\n    ];\n    builder\n        .build_table(\n            ST_COLUMN_ACCESSOR_NAME,\n            *st_column_accessor_type.as_ref().expect(\"should be ref\"),\n        )\n        .with_type(TableType::System)\n        .with_unique_constraint(st_column_accessor_table_col_cols)\n        .with_index_no_accessor_name(btree(st_column_accessor_table_col_cols))\n        .with_unique_constraint(st_column_accessor_table_alias_cols)\n        .with_index_no_accessor_name(btree(st_column_accessor_table_alias_cols));\n\n    let result = builder\n        .finish()\n        .try_into()\n        .expect(\"system table module is invalid, did you change it or add a validation rule it doesn't meet?\");\n\n    validate_system_table::<StTableFields>(&result, ST_TABLE_NAME);\n    validate_system_table::<StColumnFields>(&result, ST_COLUMN_NAME);\n    validate_system_table::<StIndexFields>(&result, ST_INDEX_NAME);\n    validate_system_table::<StSequenceFields>(&result, ST_SEQUENCE_NAME);\n    validate_system_table::<StConstraintFields>(&result, ST_CONSTRAINT_NAME);\n    validate_system_table::<StRowLevelSecurityFields>(&result, ST_ROW_LEVEL_SECURITY_NAME);\n    validate_system_table::<StModuleFields>(&result, ST_MODULE_NAME);\n    validate_system_table::<StClientFields>(&result, ST_CLIENT_NAME);\n    validate_system_table::<StVarFields>(&result, ST_VAR_NAME);\n    validate_system_table::<StScheduledFields>(&result, ST_SCHEDULED_NAME);\n    validate_system_table::<StConnectionCredentialsFields>(&result, ST_CONNECTION_CREDENTIALS_NAME);\n    validate_system_table::<StViewFields>(&result, ST_VIEW_NAME);\n    validate_system_table::<StViewParamFields>(&result, ST_VIEW_PARAM_NAME);\n    validate_system_table::<StViewColumnFields>(&result, ST_VIEW_COLUMN_NAME);\n    validate_system_table::<StViewSubFields>(&result, ST_VIEW_SUB_NAME);\n    validate_system_table::<StViewArgFields>(&result, ST_VIEW_ARG_NAME);\n    validate_system_table::<StEventTableFields>(&result, ST_EVENT_TABLE_NAME);\n    validate_system_table::<StTableAccessorFields>(&result, ST_TABLE_ACCESSOR_NAME);\n    validate_system_table::<StIndexAccessorFields>(&result, ST_INDEX_ACCESSOR_NAME);\n    validate_system_table::<StColumnAccessorFields>(&result, ST_COLUMN_ACCESSOR_NAME);\n\n    result\n}\n\nlazy_static::lazy_static! {\n    /// The canonical definition of the system tables.\n    ///\n    /// It's important not to leak this `ModuleDef` or the `Def`s it contains outside this file.\n    /// You should only return `Schema`s from this file, not `Def`s!\n    ///\n    /// This is because `SYSTEM_MODULE_DEF` has a `Typespace` that is DISTINCT from the typespace used in the client module.\n    /// System `TableDef`s refer to this typespace, but client `TableDef`s refer to the client typespace.\n    /// This could easily result in confusing errors!\n    /// Fortunately, when converting from `TableDef` to `TableSchema`, all `AlgebraicType`s are resolved,\n    /// so that they are self-contained and do not refer to any `Typespace`.\n    static ref SYSTEM_MODULE_DEF: ModuleDef = system_module_def();\n}\n\nlazy_static::lazy_static! {\n// We enumerate the constraints used by system tables here, so that we can assign them stable IDs.\n// When adding a new index, we just need to make sure we are incrementing the last ID used.\n    pub static ref CONSTRAINT_IDS: HashMap<&'static str, ConstraintId> = {\n        let mut m = HashMap::new();\n        m.insert(\"st_table_table_id_key\", ConstraintId(1));\n        m.insert(\"st_table_table_name_key\", ConstraintId(2));\n        m.insert(\"st_column_table_id_col_pos_key\", ConstraintId(3));\n        m.insert(\"st_sequence_sequence_id_key\", ConstraintId(4));\n        m.insert(\"st_index_index_id_key\", ConstraintId(5));\n        m.insert(\"st_constraint_constraint_id_key\", ConstraintId(6));\n        m.insert(\"st_client_identity_connection_id_key\", ConstraintId(7));\n        m.insert(\"st_var_name_key\", ConstraintId(8));\n        m.insert(\"st_scheduled_schedule_id_key\", ConstraintId(9));\n        m.insert(\"st_scheduled_table_id_key\", ConstraintId(10));\n        m.insert(\"st_row_level_security_sql_key\", ConstraintId(11));\n        m.insert(\"st_connection_credentials_connection_id_key\", ConstraintId(12));\n        m.insert(\"st_view_view_id_key\", ConstraintId(13));\n        m.insert(\"st_view_view_name_key\", ConstraintId(14));\n        m.insert(\"st_view_param_view_id_param_pos_key\", ConstraintId(15));\n        m.insert(\"st_view_column_view_id_col_pos_key\", ConstraintId(16));\n        m.insert(\"st_view_arg_id_key\", ConstraintId(17));\n        m.insert(\"st_view_arg_bytes_key\", ConstraintId(18));\n        m.insert(\"st_event_table_table_id_key\", ConstraintId(19));\n        m.insert(\"st_table_accessor_table_name_key\", ConstraintId(20));\n        m.insert(\"st_table_accessor_accessor_name_key\", ConstraintId(21));\n        m.insert(\"st_index_accessor_index_name_key\", ConstraintId(22));\n        m.insert(\"st_index_accessor_accessor_name_key\", ConstraintId(23));\n        m.insert(\"st_column_accessor_table_name_col_name_key\", ConstraintId(24));\n        m.insert(\"st_column_accessor_table_name_accessor_name_key\", ConstraintId(25));\n        m\n    };\n}\n\nlazy_static::lazy_static! {\n// We enumerate the indexes used by system tables here, so that we can assign them stable IDs.\n// When adding a new index, we just need to make sure we are incrementing the last ID used.\n    pub static ref INDEX_IDS: HashMap<&'static str, IndexId> = {\n        let mut m = HashMap::new();\n        m.insert(\"st_table_table_id_idx_btree\", IndexId(1));\n        m.insert(\"st_table_table_name_idx_btree\", IndexId(2));\n        m.insert(\"st_column_table_id_col_pos_idx_btree\", IndexId(3));\n        m.insert(\"st_sequence_sequence_id_idx_btree\", IndexId(4));\n        m.insert(\"st_index_index_id_idx_btree\", IndexId(5));\n        m.insert(\"st_constraint_constraint_id_idx_btree\", IndexId(6));\n        m.insert(\"st_client_identity_connection_id_idx_btree\", IndexId(7));\n        m.insert(\"st_var_name_idx_btree\", IndexId(8));\n        m.insert(\"st_scheduled_schedule_id_idx_btree\", IndexId(9));\n        m.insert(\"st_scheduled_table_id_idx_btree\", IndexId(10));\n        m.insert(\"st_row_level_security_table_id_idx_btree\", IndexId(11));\n        m.insert(\"st_row_level_security_sql_idx_btree\", IndexId(12));\n        m.insert(\"st_connection_credentials_connection_id_idx_btree\", IndexId(13));\n        m.insert(\"st_view_view_id_idx_btree\", IndexId(14));\n        m.insert(\"st_view_view_name_idx_btree\", IndexId(15));\n        m.insert(\"st_view_param_view_id_param_pos_idx_btree\", IndexId(16));\n        m.insert(\"st_view_column_view_id_col_pos_idx_btree\", IndexId(17));\n        m.insert(\"st_view_sub_identity_idx_btree\", IndexId(18));\n        m.insert(\"st_view_sub_has_subscribers_idx_btree\", IndexId(19));\n        m.insert(\"st_view_sub_view_id_arg_id_identity_idx_btree\", IndexId(20));\n        m.insert(\"st_view_arg_id_idx_btree\", IndexId(21));\n        m.insert(\"st_view_arg_bytes_idx_btree\", IndexId(22));\n        m.insert(\"st_event_table_table_id_idx_btree\", IndexId(23));\n        m.insert(\"st_table_accessor_table_name_idx_btree\", IndexId(24));\n        m.insert(\"st_table_accessor_accessor_name_idx_btree\", IndexId(25));\n        m.insert(\"st_index_accessor_index_name_idx_btree\", IndexId(26));\n        m.insert(\"st_index_accessor_accessor_name_idx_btree\", IndexId(27));\n        m.insert(\"st_column_accessor_table_name_col_name_idx_btree\", IndexId(28));\n        m.insert(\"st_column_accessor_table_name_accessor_name_idx_btree\", IndexId(29));\n        m\n    };\n}\n\n// We enumerate of the sequences used by system tables here, so that we can assign them stable IDs.\n// When adding a new sequence, we just need to make sure we are incrementing the last ID used.\nlazy_static::lazy_static! {\n    pub static ref SEQUENCE_IDS: HashMap<&'static str, SequenceId> = {\n        let mut m = HashMap::new();\n        m.insert(\"st_table_table_id_seq\", SequenceId(1));\n        m.insert(\"st_index_index_id_seq\", SequenceId(2));\n        m.insert(\"st_constraint_constraint_id_seq\", SequenceId(3));\n        m.insert(\"st_scheduled_schedule_id_seq\", SequenceId(4));\n        m.insert(\"st_sequence_sequence_id_seq\", SequenceId(5));\n        m.insert(\"st_view_view_id_seq\", SequenceId(6));\n        m.insert(\"st_view_arg_id_seq\", SequenceId(7));\n        m\n    };\n}\n\nfn st_schema(name: &str, id: TableId) -> TableSchema {\n    let mut result = TableSchema::from_module_def(\n        &SYSTEM_MODULE_DEF,\n        SYSTEM_MODULE_DEF.table(name).expect(\"missing system table definition\"),\n        (),\n        id,\n    );\n    // Accessor aliases are not persisted for system tables in `st_*` metadata rows yet.\n    // Keep canonical system schemas alias-free so raw reconstruction and cached schemas agree.\n    result.alias = None;\n    for column in &mut result.columns {\n        column.alias = None;\n    }\n    for index in &mut result.indexes {\n        index.alias = None;\n    }\n    // The result we get will have sentinel ids filled in the constraints, indexes, and sequences.\n    // We replace them here with stable values in the reserved range.\n    for index in &mut result.indexes {\n        index.index_id = INDEX_IDS.get(&index.index_name[..]).copied().unwrap_or_else(|| {\n            panic!(\n                \"missing system table index id for index {} of table {}\",\n                index.index_name, result.table_name\n            )\n        });\n    }\n    for constraint in &mut result.constraints {\n        constraint.constraint_id = CONSTRAINT_IDS\n            .get(&constraint.constraint_name[..])\n            .copied()\n            .unwrap_or_else(|| {\n                panic!(\n                    \"missing system table constraint id for constraint {} of table {}\",\n                    constraint.constraint_name, result.table_name\n                )\n            });\n    }\n    for sequence in &mut result.sequences {\n        sequence.sequence_id = SEQUENCE_IDS\n            .get(&sequence.sequence_name[..])\n            .copied()\n            .unwrap_or_else(|| {\n                panic!(\n                    \"missing system table sequence id for sequence {} of table {}\",\n                    sequence.sequence_name, result.table_name\n                )\n            });\n        sequence.start = ST_RESERVED_SEQUENCE_RANGE as i128 + 1;\n        // sequence.allocated = ST_RESERVED_SEQUENCE_RANGE as i128;\n    }\n    if let Some(sch) = result.schedule {\n        panic!(\n            \"system tables cannot have schedules, but table {}: {sch:?}\",\n            result.table_name\n        );\n    }\n    result.normalize();\n    // Note, if we ever added system tables with schedules, we would need to set their IDs here too.\n    result\n}\n\nfn st_table_schema() -> TableSchema {\n    st_schema(ST_TABLE_NAME, ST_TABLE_ID)\n}\n\nfn st_column_schema() -> TableSchema {\n    st_schema(ST_COLUMN_NAME, ST_COLUMN_ID)\n}\n\nfn st_index_schema() -> TableSchema {\n    st_schema(ST_INDEX_NAME, ST_INDEX_ID)\n}\n\nfn st_sequence_schema() -> TableSchema {\n    st_schema(ST_SEQUENCE_NAME, ST_SEQUENCE_ID)\n}\n\nfn st_constraint_schema() -> TableSchema {\n    st_schema(ST_CONSTRAINT_NAME, ST_CONSTRAINT_ID)\n}\n\nfn st_row_level_security_schema() -> TableSchema {\n    st_schema(ST_ROW_LEVEL_SECURITY_NAME, ST_ROW_LEVEL_SECURITY_ID)\n}\n\npub(crate) fn st_module_schema() -> TableSchema {\n    st_schema(ST_MODULE_NAME, ST_MODULE_ID)\n}\n\nfn st_client_schema() -> TableSchema {\n    st_schema(ST_CLIENT_NAME, ST_CLIENT_ID)\n}\n\nfn st_connection_credential_schema() -> TableSchema {\n    st_schema(ST_CONNECTION_CREDENTIALS_NAME, ST_CONNECTION_CREDENTIALS_ID)\n}\n\nfn st_scheduled_schema() -> TableSchema {\n    st_schema(ST_SCHEDULED_NAME, ST_SCHEDULED_ID)\n}\n\npub fn st_var_schema() -> TableSchema {\n    st_schema(ST_VAR_NAME, ST_VAR_ID)\n}\n\npub fn st_view_schema() -> TableSchema {\n    st_schema(ST_VIEW_NAME, ST_VIEW_ID)\n}\n\npub fn st_view_param_schema() -> TableSchema {\n    st_schema(ST_VIEW_PARAM_NAME, ST_VIEW_PARAM_ID)\n}\n\npub fn st_view_column_schema() -> TableSchema {\n    st_schema(ST_VIEW_COLUMN_NAME, ST_VIEW_COLUMN_ID)\n}\n\npub fn st_view_sub_schema() -> TableSchema {\n    st_schema(ST_VIEW_SUB_NAME, ST_VIEW_SUB_ID)\n}\n\npub fn st_view_arg_schema() -> TableSchema {\n    st_schema(ST_VIEW_ARG_NAME, ST_VIEW_ARG_ID)\n}\n\nfn st_event_table_schema() -> TableSchema {\n    st_schema(ST_EVENT_TABLE_NAME, ST_EVENT_TABLE_ID)\n}\n\nfn st_table_accessor_schema() -> TableSchema {\n    st_schema(ST_TABLE_ACCESSOR_NAME, ST_TABLE_ACCESSOR_ID)\n}\n\nfn st_index_accessor_schema() -> TableSchema {\n    st_schema(ST_INDEX_ACCESSOR_NAME, ST_INDEX_ACCESSOR_ID)\n}\n\nfn st_column_accessor_schema() -> TableSchema {\n    st_schema(ST_COLUMN_ACCESSOR_NAME, ST_COLUMN_ACCESSOR_ID)\n}\n\n/// If `table_id` refers to a known system table, return its schema.\n///\n/// Used when restoring from a snapshot; system tables are reinstantiated with this schema,\n/// whereas user tables are reinstantiated with a schema computed from the snapshotted system tables.\n///\n/// This must be kept in sync with the set of system tables.\npub(crate) fn system_table_schema(table_id: TableId) -> Option<TableSchema> {\n    match table_id {\n        ST_TABLE_ID => Some(st_table_schema()),\n        ST_COLUMN_ID => Some(st_column_schema()),\n        ST_SEQUENCE_ID => Some(st_sequence_schema()),\n        ST_INDEX_ID => Some(st_index_schema()),\n        ST_CONSTRAINT_ID => Some(st_constraint_schema()),\n        ST_ROW_LEVEL_SECURITY_ID => Some(st_row_level_security_schema()),\n        ST_MODULE_ID => Some(st_module_schema()),\n        ST_CLIENT_ID => Some(st_client_schema()),\n        ST_CONNECTION_CREDENTIALS_ID => Some(st_connection_credential_schema()),\n        ST_VAR_ID => Some(st_var_schema()),\n        ST_SCHEDULED_ID => Some(st_scheduled_schema()),\n        ST_VIEW_ID => Some(st_view_schema()),\n        ST_VIEW_PARAM_ID => Some(st_view_param_schema()),\n        ST_VIEW_COLUMN_ID => Some(st_view_column_schema()),\n        ST_VIEW_SUB_ID => Some(st_view_sub_schema()),\n        ST_VIEW_ARG_ID => Some(st_view_arg_schema()),\n        ST_EVENT_TABLE_ID => Some(st_event_table_schema()),\n        ST_TABLE_ACCESSOR_ID => Some(st_table_accessor_schema()),\n        ST_INDEX_ACCESSOR_ID => Some(st_index_accessor_schema()),\n        ST_COLUMN_ACCESSOR_ID => Some(st_column_accessor_schema()),\n        _ => None,\n    }\n}\n\n/// System Table [ST_TABLE_NAME]\n///\n/// | table_id | table_name  | table_type | table_access |\n/// |----------|-------------|----------- |------------- |\n/// | 4        | \"customers\" | \"user\"     | \"public\"     |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StTableRow {\n    pub table_id: TableId,\n    pub table_name: TableName,\n    pub table_type: StTableType,\n    pub table_access: StAccess,\n    /// The primary key of the table.\n    /// This is a `ColId` everywhere else, but we make it a `ColList` here\n    /// for future compatibility in case we ever have composite primary keys.\n    pub table_primary_key: Option<ColList>,\n}\n\nimpl TryFrom<RowRef<'_>> for StTableRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StTableRow> for ProductValue {\n    fn from(x: StTableRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\n/// System Table [ST_VIEW_NAME]\n///\n/// | view_id | view_name | table_id | is_public | is_anonymous |\n/// |---------|-----------|----------|-----------|--------------|\n/// | 1       | \"player\"  | 4        | true      | true         |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StViewRow {\n    /// An auto-inc id for each view\n    pub view_id: ViewId,\n    /// The name of the view function as defined in the module\n    pub view_name: TableName,\n    /// The [`TableId`] for this view if materialized.\n    /// Currently all views are materialized and therefore are assigned a [`TableId`] by default.\n    pub table_id: Option<TableId>,\n    /// Is this a public or a private view?\n    /// Currently only public views are supported.\n    /// Private views may be supported in the future.\n    pub is_public: bool,\n    /// Is this view anonymous?\n    /// An anonymous view does not know who called it.\n    /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument.\n    /// This type does not have access to the [`Identity`] of the caller.\n    pub is_anonymous: bool,\n}\n\nimpl TryFrom<RowRef<'_>> for StViewRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\n/// A wrapper around `AlgebraicType` that acts like `AlgegbraicType::bytes()` for serialization purposes.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct AlgebraicTypeViaBytes(pub AlgebraicType);\nimpl_st!([] AlgebraicTypeViaBytes, AlgebraicType::bytes());\nimpl<'de> Deserialize<'de> for AlgebraicTypeViaBytes {\n    fn deserialize<D: spacetimedb_lib::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        let bytes = <Cow<'_, [u8]>>::deserialize(deserializer)?;\n        let ty = AlgebraicType::decode(&mut &*bytes).map_err(D::Error::custom)?;\n        Ok(AlgebraicTypeViaBytes(ty))\n    }\n}\nthread_local! {\n    static ALGEBRAIC_TYPE_WRITE_BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };\n}\nimpl_serialize!([] AlgebraicTypeViaBytes, (self, ser) => {\n    ALGEBRAIC_TYPE_WRITE_BUF.with_borrow_mut(|buf| {\n        buf.clear();\n        self.0.encode(buf);\n        buf[..].serialize(ser)\n    })\n});\nimpl From<AlgebraicType> for AlgebraicTypeViaBytes {\n    fn from(ty: AlgebraicType) -> Self {\n        Self(ty)\n    }\n}\n\n/// System Table [ST_COLUMN_NAME]\n///\n/// | table_id | col_id | col_name | col_type            |\n/// |----------|---------|----------|--------------------|\n/// | 1        | 0       | \"id\"     | AlgebraicType::U32 |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StColumnRow {\n    pub table_id: TableId,\n    pub col_pos: ColId,\n    pub col_name: Identifier,\n    pub col_type: AlgebraicTypeViaBytes,\n}\n\nimpl TryFrom<RowRef<'_>> for StColumnRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StColumnRow> for ProductValue {\n    fn from(x: StColumnRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StColumnRow> for ColumnSchema {\n    fn from(column: StColumnRow) -> Self {\n        Self {\n            table_id: column.table_id,\n            col_pos: column.col_pos,\n            col_name: column.col_name,\n            col_type: column.col_type.0,\n            alias: None,\n        }\n    }\n}\n\nimpl From<ColumnSchema> for StColumnRow {\n    fn from(column: ColumnSchema) -> Self {\n        Self {\n            table_id: column.table_id,\n            col_pos: column.col_pos,\n            col_name: column.col_name,\n            col_type: column.col_type.into(),\n        }\n    }\n}\n\n/// System Table [ST_VIEW_COLUMN_NAME]\n///\n/// | view_id | col_pos | col_name | col_type           |\n/// |---------|---------|----------|--------------------|\n/// | 1       | 0       | \"x\"      | AlgebraicType::U32 |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StViewColumnRow {\n    /// A foreign key referencing [`ST_VIEW_NAME`].\n    pub view_id: ViewId,\n    pub col_pos: ColId,\n    pub col_name: Identifier,\n    pub col_type: AlgebraicTypeViaBytes,\n}\n\n/// System Table [ST_VIEW_PARAM_NAME]\n///\n/// | view_id | param_pos | param_name | param_type            |\n/// |---------|-----------|------------|-----------------------|\n/// | 1       | 0         | \"y\"        | AlgebraicType::U32    |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StViewParamRow {\n    /// A foreign key referencing [`ST_VIEW_NAME`].\n    pub view_id: ViewId,\n    pub param_pos: ColId,\n    pub param_name: RawIdentifier,\n    pub param_type: AlgebraicTypeViaBytes,\n}\n\n/// System table [ST_VIEW_SUB_NAME]\n///\n/// | view_id | arg_id | identity | num_subscribers | has_subscribers | last_called |\n/// |---------|--------|----------|-----------------|-----------------|-------------|\n/// | 1       | 2      | 0x...    | 3               | true            | <timestamp> |\n#[derive(Debug, Clone, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StViewSubRow {\n    pub view_id: ViewId,\n    pub arg_id: ArgId,\n    pub identity: IdentityViaU256,\n    pub num_subscribers: u64,\n    pub has_subscribers: bool,\n    pub last_called: TimestampViaI64,\n}\n\nimpl TryFrom<RowRef<'_>> for StViewSubRow {\n    type Error = DatastoreError;\n\n    fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {\n        read_via_bsatn(row)\n    }\n}\n\n/// System table [ST_VIEW_ARG_NAME]\n///\n/// | id | bytes   |\n/// |----|---------|\n/// | 1  | <bytes> |\n#[derive(Debug, Clone, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StViewArgRow {\n    pub id: u64,\n    pub bytes: Box<[u8]>,\n}\n\n/// System Table [ST_INDEX_NAME]\n///\n/// | index_id | table_id | index_name  | index_algorithm            |\n/// |----------|----------|-------------|----------------------------|\n/// | 1        |          | \"ix_sample\" | btree({\"columns\": [1, 2]}) |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StIndexRow {\n    pub index_id: IndexId,\n    pub table_id: TableId,\n    pub index_name: RawIdentifier,\n    pub index_algorithm: StIndexAlgorithm,\n}\n\n/// An index algorithm for storing in the system tables.\n///\n/// It is critical that this type never grow in layout, as it is stored in the system tables.\n/// This is checked by (TODO(1.0): add a test!)\n///\n/// It is forbidden to add data to any of the variants of this type.\n/// You have to add a NEW variant.\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum StIndexAlgorithm {\n    /// Unused variant to reserve space.\n    Unused(u128),\n\n    /// A BTree index.\n    BTree { columns: ColList },\n\n    /// A Direct index.\n    Direct { column: ColId },\n\n    /// A Hash index.\n    Hash { columns: ColList },\n}\n\nimpl From<IndexAlgorithm> for StIndexAlgorithm {\n    fn from(algorithm: IndexAlgorithm) -> Self {\n        match algorithm {\n            IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => Self::BTree { columns },\n            IndexAlgorithm::Hash(HashAlgorithm { columns }) => Self::Hash { columns },\n            IndexAlgorithm::Direct(DirectAlgorithm { column }) => Self::Direct { column },\n            algo => unreachable!(\"unexpected `{algo:?}`, did you add a new one?\"),\n        }\n    }\n}\n\nimpl From<StIndexAlgorithm> for IndexAlgorithm {\n    fn from(algorithm: StIndexAlgorithm) -> Self {\n        match algorithm {\n            StIndexAlgorithm::BTree { columns } => BTreeAlgorithm { columns }.into(),\n            StIndexAlgorithm::Hash { columns } => HashAlgorithm { columns }.into(),\n            StIndexAlgorithm::Direct { column } => DirectAlgorithm { column }.into(),\n            algo => unreachable!(\"unexpected `{algo:?}` in system table `st_indexes`\"),\n        }\n    }\n}\n\nimpl TryFrom<RowRef<'_>> for StIndexRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl TryFrom<RowRef<'_>> for StViewArgRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StIndexRow> for ProductValue {\n    fn from(x: StIndexRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StIndexRow> for IndexSchema {\n    fn from(x: StIndexRow) -> Self {\n        Self {\n            index_id: x.index_id,\n            table_id: x.table_id,\n            index_name: x.index_name,\n            index_algorithm: x.index_algorithm.into(),\n            alias: None,\n        }\n    }\n}\n\nimpl From<IndexSchema> for StIndexRow {\n    fn from(x: IndexSchema) -> Self {\n        Self {\n            index_id: x.index_id,\n            table_id: x.table_id,\n            index_name: x.index_name,\n            index_algorithm: x.index_algorithm.into(),\n        }\n    }\n}\n\n/// System Table [ST_SEQUENCE_NAME]\n///\n/// | sequence_id | sequence_name     | increment | start | min_value | max_value | table_id | col_pos| allocated |\n/// |-------------|-------------------|-----------|-------|-----------|-----------|----------|--------|-----------|\n/// | 1           | \"seq_customer_id\" | 1         | 100   | 10        | 1200      | 1        | 1      | 200       |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StSequenceRow {\n    pub sequence_id: SequenceId,\n    pub sequence_name: RawIdentifier,\n    pub table_id: TableId,\n    pub col_pos: ColId,\n    pub increment: i128,\n    // The original starting value of this sequence.\n    // This is actually not useful, since allocated tells us where to start generating new values.\n    pub start: i128,\n    pub min_value: i128,\n    pub max_value: i128,\n    // Allocated is a lower bound on the next value of the sequence.\n    // This exists so that we don't need to update this row every time we allocate a value from the sequence.\n    pub allocated: i128,\n}\n\nimpl TryFrom<RowRef<'_>> for StSequenceRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StSequenceRow> for ProductValue {\n    fn from(x: StSequenceRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StSequenceRow> for SequenceSchema {\n    fn from(sequence: StSequenceRow) -> Self {\n        Self {\n            sequence_id: sequence.sequence_id,\n            sequence_name: sequence.sequence_name,\n            table_id: sequence.table_id,\n            col_pos: sequence.col_pos,\n            start: sequence.start,\n            increment: sequence.increment,\n            min_value: sequence.min_value,\n            max_value: sequence.max_value,\n        }\n    }\n}\n\n/// System Table [ST_CONSTRAINT_NAME]\n///\n/// | constraint_id | constraint_name      | table_id    | constraint_data    -------------|\n/// |---------------|-------------------- -|-------------|---------------------------------|\n/// | 1             | \"unique_customer_id\" | 1           | unique({\"columns\": [1, 2]})     |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StConstraintRow {\n    pub(crate) constraint_id: ConstraintId,\n    pub(crate) constraint_name: RawIdentifier,\n    pub table_id: TableId,\n    pub(crate) constraint_data: StConstraintData,\n}\n\n/// Constraint data for storing in the system tables.\n///\n/// It is critical that this type never grow in layout, as it is stored in the system tables.\n/// This is checked by (TODO: add a check in this PR!)\n///\n/// It is forbidden to add data to any of the variants of this type.\n/// You have to add a NEW variant.\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum StConstraintData {\n    /// Unused variant to reserve space.\n    Unused(u128),\n\n    /// A BTree index.\n    Unique { columns: ColSet },\n}\n\nimpl From<ConstraintData> for StConstraintData {\n    fn from(data: ConstraintData) -> Self {\n        match data {\n            ConstraintData::Unique(UniqueConstraintData { columns }) => StConstraintData::Unique { columns },\n            _ => unimplemented!(),\n        }\n    }\n}\n\nimpl TryFrom<RowRef<'_>> for StConstraintRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StConstraintRow> for ProductValue {\n    fn from(x: StConstraintRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StConstraintRow> for ConstraintSchema {\n    fn from(x: StConstraintRow) -> Self {\n        Self {\n            constraint_id: x.constraint_id,\n            constraint_name: x.constraint_name,\n            table_id: x.table_id,\n            data: match x.constraint_data {\n                StConstraintData::Unique { columns } => ConstraintData::Unique(UniqueConstraintData { columns }),\n                StConstraintData::Unused(_) => panic!(\"Someone put a forbidden variant in the system table!\"),\n            },\n        }\n    }\n}\n\n/// System Table [ST_ROW_LEVEL_SECURITY_NAME]\n///\n/// | table_id | sql          |\n/// |----------|--------------|\n/// | 1        | \"SELECT ...\" |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StRowLevelSecurityRow {\n    pub table_id: TableId,\n    pub sql: RawSql,\n}\n\nimpl TryFrom<RowRef<'_>> for StRowLevelSecurityRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StRowLevelSecurityRow> for ProductValue {\n    fn from(x: StRowLevelSecurityRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StRowLevelSecurityRow> for RowLevelSecuritySchema {\n    fn from(x: StRowLevelSecurityRow) -> Self {\n        Self {\n            table_id: x.table_id,\n            sql: x.sql,\n        }\n    }\n}\n/// Indicates the kind of module the `program_bytes` of a [`StModuleRow`]\n/// describes.\n///\n/// More or less a placeholder to allow for future non-WASM hosts without\n/// having to change the [`st_module_schema`].\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct ModuleKind(u8);\n\nimpl ModuleKind {\n    /// The [`ModuleKind`] of WASM-based modules.\n    pub const WASM: ModuleKind = ModuleKind(0);\n    /// The [`ModuleKind`] of JS modules.\n    pub const JS: ModuleKind = ModuleKind(1);\n}\n\nimpl_serialize!([] ModuleKind, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] ModuleKind, de => u8::deserialize(de).map(Self));\nimpl_st!([] ModuleKind, AlgebraicType::U8);\n\n/// A wrapper for [`ConnectionId`] that acts like [`AlgebraicType::U128`] for serialization purposes.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct ConnectionIdViaU128(pub ConnectionId);\nimpl_serialize!([] ConnectionIdViaU128, (self, ser) => self.0.to_u128().serialize(ser));\nimpl_deserialize!([] ConnectionIdViaU128, de => <u128>::deserialize(de).map(ConnectionId::from_u128).map(ConnectionIdViaU128));\nimpl_st!([] ConnectionIdViaU128, AlgebraicType::U128);\nimpl From<ConnectionId> for ConnectionIdViaU128 {\n    fn from(id: ConnectionId) -> Self {\n        Self(id)\n    }\n}\n\nimpl From<ConnectionIdViaU128> for AlgebraicValue {\n    fn from(val: ConnectionIdViaU128) -> Self {\n        AlgebraicValue::U128(val.0.to_u128().into())\n    }\n}\n\n/// A wrapper for [`Identity`] that acts like [`AlgebraicType::U256`] for serialization purposes.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct IdentityViaU256(pub Identity);\nimpl_serialize!([] IdentityViaU256, (self, ser) => self.0.to_u256().serialize(ser));\nimpl_deserialize!([] IdentityViaU256, de => <u256>::deserialize(de).map(Identity::from_u256).map(IdentityViaU256));\nimpl_st!([] IdentityViaU256, AlgebraicType::U256);\nimpl From<Identity> for IdentityViaU256 {\n    fn from(id: Identity) -> Self {\n        Self(id)\n    }\n}\n\nimpl From<IdentityViaU256> for Identity {\n    fn from(id: IdentityViaU256) -> Self {\n        id.0\n    }\n}\n\nimpl From<IdentityViaU256> for AlgebraicValue {\n    fn from(val: IdentityViaU256) -> Self {\n        AlgebraicValue::U256(val.0.to_u256().into())\n    }\n}\n\n/// A wrapper for [`Timestamp`] that acts like [`AlgebraicType::I64`] for serialization purposes.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct TimestampViaI64(pub Timestamp);\nimpl_serialize!([] TimestampViaI64, (self, ser) => self.0.to_micros_since_unix_epoch().serialize(ser));\nimpl_deserialize!([] TimestampViaI64, de => <i64>::deserialize(de).map(Timestamp::from_micros_since_unix_epoch).map(TimestampViaI64));\nimpl_st!([] TimestampViaI64, AlgebraicType::I64);\nimpl From<Timestamp> for TimestampViaI64 {\n    fn from(ts: Timestamp) -> Self {\n        Self(ts)\n    }\n}\n\nimpl From<TimestampViaI64> for AlgebraicValue {\n    fn from(val: TimestampViaI64) -> Self {\n        AlgebraicValue::I64(val.0.to_micros_since_unix_epoch())\n    }\n}\n\n/// System table [ST_MODULE_NAME]\n/// This table holds exactly one row, describing the latest version of the\n/// SpacetimeDB module associated with the database:\n///\n/// * `database_identity` is the [`Identity`] of the database.\n/// * `owner_identity` is the [`Identity`] of the owner of the database.\n/// * `program_kind` is the [`ModuleKind`] (currently always [`ModuleKind::WASM`]).\n/// * `program_hash` is the [`Hash`] of the raw bytes of the (compiled) module.\n/// * `program_bytes` are the raw bytes of the (compiled) module.\n/// * `module_version` is the version of the module.\n///\n/// | identity | owner_identity |  program_kind | program_bytes | program_hash        | module_version |\n/// |------------------|----------------|---------------|---------------|---------------------|----------------|\n/// | <bytes>          | <bytes>        |  0            | <bytes>       | <bytes>             | <string>       |\n#[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StModuleRow {\n    pub database_identity: IdentityViaU256,\n    pub owner_identity: IdentityViaU256,\n    pub program_kind: ModuleKind,\n    pub program_hash: Hash,\n    pub program_bytes: Box<[u8]>,\n    pub module_version: Box<str>,\n}\n\n/// Read bytes directly from the column `col` in `row`.\npub fn read_bytes_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Box<[u8]>, DatastoreError> {\n    let bytes = row.read_col::<ArrayValue>(col.col_id())?;\n    if let ArrayValue::U8(bytes) = bytes {\n        Ok(bytes)\n    } else {\n        Err(InvalidFieldError {\n            name: Some(col.name()),\n            col_pos: col.col_id(),\n        }\n        .into())\n    }\n}\n\n/// Read an [`Identity`] directly from the column `col` in `row`.\n///\n/// The [`Identity`] is assumed to be stored as a flat byte array.\npub fn read_identity_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Identity, DatastoreError> {\n    Ok(Identity::from_u256(row.read_col(col.col_id())?))\n}\n\n/// Read a [`Hash`] directly from the column `col` in `row`.\n///\n/// The [`Hash`] is assumed to be stored as a flat byte array.\npub fn read_hash_from_col(row: RowRef<'_>, col: impl StFields) -> Result<Hash, DatastoreError> {\n    Ok(Hash::from_u256(row.read_col(col.col_id())?))\n}\n\nimpl TryFrom<RowRef<'_>> for StModuleRow {\n    type Error = DatastoreError;\n\n    fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StModuleRow> for ProductValue {\n    fn from(row: StModuleRow) -> Self {\n        to_product_value(&row)\n    }\n}\n\n/// System table [ST_CLIENT_NAME]\n///\n/// | identity                                                           | connection_id                      |\n/// |--------------------------------------------------------------------+------------------------------------|\n/// | 0x7452047061ea2502003412941d85a42f89b0702588b823ab55fc4f12e9ea8363 | 0x6bdea3ab517f5857dc9b1b5fe99e1b14 |\n#[derive(Clone, Copy, Debug, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StClientRow {\n    pub identity: IdentityViaU256,\n    pub connection_id: ConnectionIdViaU128,\n}\n\n/// System table [ST_CONNECTION_CREDENTIALS_NAME]\n///\n/// | connection_id                      | jwt_payload                                             |\n/// |------------------------------------|---------------------------------------------------------|\n/// | 0x6bdea3ab517f5857dc9b1b5fe99e1b14 | '{\"iss\":\"issuer\",\"sub\":\"user-id\",\"iat\":1629212345,...}' |\n#[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StConnectionCredentialsRow {\n    pub connection_id: ConnectionIdViaU128,\n    pub jwt_payload: String,\n}\n\nimpl From<StClientRow> for ProductValue {\n    fn from(var: StClientRow) -> Self {\n        to_product_value(&var)\n    }\n}\nimpl From<&StClientRow> for ProductValue {\n    fn from(var: &StClientRow) -> Self {\n        to_product_value(var)\n    }\n}\n\nimpl TryFrom<RowRef<'_>> for StClientRow {\n    type Error = DatastoreError;\n\n    fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StClientRow> for (Identity, ConnectionId) {\n    fn from(value: StClientRow) -> Self {\n        (value.identity.0, value.connection_id.0)\n    }\n}\n\n/// System table [ST_VAR_NAME]\n///\n/// | name        | value     |\n/// |-------------|-----------|\n/// | \"row_limit\" | (U64 = 5) |\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StVarRow {\n    pub name: StVarName,\n    pub value: StVarValue,\n}\n\nimpl From<StVarRow> for ProductValue {\n    fn from(var: StVarRow) -> Self {\n        to_product_value(&var)\n    }\n}\n\nimpl From<StVarRow> for AlgebraicValue {\n    fn from(row: StVarRow) -> Self {\n        AlgebraicValue::Product(row.into())\n    }\n}\n\n/// A system variable that defines a row limit for queries and subscriptions.\n/// If the cardinality of a query is estimated to exceed this limit,\n/// it will be rejected before being executed.\npub const ST_VARNAME_ROW_LIMIT: &str = \"row_limit\";\n\n/// The name of a system variable in `st_var`\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum StVarName {\n    RowLimit,\n}\nimpl From<StVarName> for &'static str {\n    fn from(value: StVarName) -> Self {\n        match value {\n            StVarName::RowLimit => ST_VARNAME_ROW_LIMIT,\n        }\n    }\n}\nimpl From<StVarName> for AlgebraicValue {\n    fn from(value: StVarName) -> Self {\n        let value: &'static str = value.into();\n        AlgebraicValue::String(value.into())\n    }\n}\nimpl FromStr for StVarName {\n    type Err = anyhow::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            ST_VARNAME_ROW_LIMIT => Ok(StVarName::RowLimit),\n            _ => Err(anyhow::anyhow!(\"Invalid system variable {s}\")),\n        }\n    }\n}\nimpl_st!([] StVarName, AlgebraicType::String);\nimpl_serialize!([] StVarName, (self, ser) => <&'static str>::from(*self).serialize(ser));\nimpl<'de> Deserialize<'de> for StVarName {\n    fn deserialize<D: spacetimedb_lib::de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {\n        let s = <&str>::deserialize(de)?;\n        s.parse().map_err(D::Error::custom)\n    }\n}\n\nimpl StVarName {\n    pub fn type_of(&self) -> AlgebraicType {\n        match self {\n            StVarName::RowLimit => AlgebraicType::U64,\n        }\n    }\n}\n\nimpl TryFrom<RowRef<'_>> for StVarRow {\n    type Error = DatastoreError;\n\n    fn try_from(row: RowRef<'_>) -> Result<Self, Self::Error> {\n        // The position of the `value` column in `st_var`\n        let col_pos = StVarFields::Value.col_id();\n\n        // An error when reading the `value` column in `st_var`\n        let invalid_value = InvalidFieldError {\n            col_pos,\n            name: Some(StVarFields::Value.name()),\n        };\n\n        let name = row.read_col::<Box<str>>(StVarFields::Name.col_id())?;\n        let name = StVarName::from_str(&name)?;\n        match row.read_col::<AlgebraicValue>(col_pos)? {\n            AlgebraicValue::Sum(sum) => Ok(StVarRow {\n                name,\n                value: sum.try_into().map_err(|_| invalid_value)?,\n            }),\n            _ => Err(invalid_value.into()),\n        }\n    }\n}\n\n/// System table [ST_SCHEDULED_NAME]\n/// | schedule_id | table_id | reducer_name | schedule_name |\n#[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StScheduledRow {\n    pub(crate) schedule_id: ScheduleId,\n    pub(crate) table_id: TableId,\n    /// The name of the reducer or procedure which will run when this table's rows reach their execution time.\n    ///\n    /// Note that, despite the column name, this may refer to either a reducer or a procedure.\n    /// We cannot change the schema of existing system tables,\n    /// so we are unable to rename this column.\n    pub(crate) reducer_name: Identifier,\n    pub(crate) schedule_name: Identifier,\n    pub(crate) at_column: ColId,\n}\n\nimpl TryFrom<RowRef<'_>> for StScheduledRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StScheduledRow> for ProductValue {\n    fn from(x: StScheduledRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nimpl From<StScheduledRow> for ScheduleSchema {\n    fn from(row: StScheduledRow) -> Self {\n        Self {\n            table_id: row.table_id,\n            function_name: row.reducer_name,\n            schedule_id: row.schedule_id,\n            schedule_name: row.schedule_name,\n            at_column: row.at_column,\n        }\n    }\n}\n\n/// System Table [ST_EVENT_TABLE_NAME]\n///\n/// Tracks which tables are event tables.\n/// Event tables persist to commitlog but are not merged into committed state.\n///\n/// | table_id |\n/// |----------|\n/// | 4097     |\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StEventTableRow {\n    pub(crate) table_id: TableId,\n}\n\nimpl TryFrom<RowRef<'_>> for StEventTableRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StEventTableRow> for ProductValue {\n    fn from(x: StEventTableRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\n/// System Table [ST_TABLE_ACCESSOR_NAME]\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StTableAccessorRow {\n    pub table_name: TableName,\n    pub accessor_name: Identifier,\n}\n\nimpl TryFrom<RowRef<'_>> for StTableAccessorRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StTableAccessorRow> for ProductValue {\n    fn from(x: StTableAccessorRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\n/// System Table [ST_INDEX_ACCESSOR_NAME]\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StIndexAccessorRow {\n    pub index_name: RawIdentifier,\n    pub accessor_name: RawIdentifier,\n}\n\nimpl TryFrom<RowRef<'_>> for StIndexAccessorRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StIndexAccessorRow> for ProductValue {\n    fn from(x: StIndexAccessorRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\n/// System Table [ST_COLUMN_ACCESSOR_NAME]\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub struct StColumnAccessorRow {\n    pub table_name: TableName,\n    pub col_name: Identifier,\n    pub accessor_name: Identifier,\n}\n\nimpl TryFrom<RowRef<'_>> for StColumnAccessorRow {\n    type Error = DatastoreError;\n    fn try_from(row: RowRef<'_>) -> Result<Self, DatastoreError> {\n        read_via_bsatn(row)\n    }\n}\n\nimpl From<StColumnAccessorRow> for ProductValue {\n    fn from(x: StColumnAccessorRow) -> Self {\n        to_product_value(&x)\n    }\n}\n\nthread_local! {\n    static READ_BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };\n}\n\n/// Provides access to a buffer to which bytes can be written.\npub(crate) fn with_sys_table_buf<R>(run: impl FnOnce(&mut Vec<u8>) -> R) -> R {\n    READ_BUF.with_borrow_mut(|buf| {\n        buf.clear();\n        run(buf)\n    })\n}\n\n/// Read a value from a system table via BSATN.\nfn read_via_bsatn<T: DeserializeOwned>(row: RowRef<'_>) -> Result<T, DatastoreError> {\n    with_sys_table_buf(|buf| Ok(row.read_via_bsatn::<T>(buf)?))\n}\n\n/// Convert a value to a product value.\n/// Panics if the value does not serialize to a product value.\n/// It's fine to call this on system table types, because `validate_system_table` checks that\n/// they are `ProductType`s.\n///\n/// TODO: this performs some unnecessary allocation. We may want to reimplement the conversions manually for\n/// performance eventually.\nfn to_product_value<T: Serialize>(value: &T) -> ProductValue {\n    value_serialize(&value).into_product().expect(\"should be product\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_data_structures::map::HashSet;\n\n    #[test]\n    fn test_index_ids_are_unique() {\n        let mut ids = HashSet::new();\n        for table in system_tables() {\n            for index in table.indexes.iter() {\n                assert!(\n                    ids.insert(index.index_id),\n                    \"duplicate index id {:?} for index {}\",\n                    index.index_id,\n                    index.index_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_index_ids_are_valid() {\n        for table in system_tables() {\n            for index in table.indexes.iter() {\n                assert!(\n                    index.index_id.0 < ST_RESERVED_SEQUENCE_RANGE,\n                    \"index id {:?} for index {} is too large for reserved range\",\n                    index.index_id,\n                    index.index_name\n                );\n                assert_ne!(\n                    index.index_id,\n                    IndexId::SENTINEL,\n                    \"index {} has the sentinel id\",\n                    index.index_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_constraint_ids_are_valid() {\n        for table in system_tables() {\n            for constraint in table.constraints.iter() {\n                assert!(\n                    constraint.constraint_id.0 < ST_RESERVED_SEQUENCE_RANGE,\n                    \"id {:?} for constraint {} is too large for reserved range\",\n                    constraint.constraint_id,\n                    constraint.constraint_name\n                );\n                assert_ne!(\n                    constraint.constraint_id,\n                    ConstraintId::SENTINEL,\n                    \"constraint {} has the sentinel id\",\n                    constraint.constraint_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_constraint_ids_are_unique() {\n        let mut ids = HashSet::new();\n        for table in system_tables() {\n            for constraint in table.constraints.iter() {\n                assert!(\n                    ids.insert(constraint.constraint_id),\n                    \"duplicate id {:?} for constraint {}\",\n                    constraint.constraint_id,\n                    constraint.constraint_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_sequence_ids_are_valid() {\n        for table in system_tables() {\n            for sequence in table.sequences.iter() {\n                assert!(\n                    sequence.sequence_id.0 < ST_RESERVED_SEQUENCE_RANGE,\n                    \"id {:?} for sequence {} is too large for reserved range\",\n                    sequence.sequence_id,\n                    sequence.sequence_name\n                );\n                assert_ne!(\n                    sequence.sequence_id,\n                    SequenceId::SENTINEL,\n                    \"sequence {} has the sentinel id\",\n                    sequence.sequence_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_sequence_ids_are_unique() {\n        let mut ids = HashSet::new();\n        for table in system_tables() {\n            for sequence in table.sequences.iter() {\n                assert!(\n                    ids.insert(sequence.sequence_id),\n                    \"duplicate id {:?} for sequence {}\",\n                    sequence.sequence_id,\n                    sequence.sequence_name\n                );\n            }\n        }\n    }\n\n    #[test]\n    fn test_sequences_within_reserved_range() {\n        let mut num_tables = 0;\n        let mut num_indexes = 0;\n        let mut num_constraints = 0;\n        let mut num_sequences = 0;\n\n        for table in system_tables() {\n            num_tables += 1;\n            num_indexes += table.indexes.len();\n            num_constraints += table.constraints.len();\n            num_sequences += table.sequences.len();\n        }\n\n        assert!(\n            num_tables < ST_RESERVED_SEQUENCE_RANGE,\n            \"number of system tables exceeds reserved sequence range\"\n        );\n        assert!(\n            num_indexes < ST_RESERVED_SEQUENCE_RANGE as usize,\n            \"number of system indexes exceeds reserved sequence range\"\n        );\n        assert!(\n            num_constraints < ST_RESERVED_SEQUENCE_RANGE as usize,\n            \"number of system constraints exceeds reserved sequence range\"\n        );\n        assert!(\n            num_sequences < ST_RESERVED_SEQUENCE_RANGE as usize,\n            \"number of system sequences exceeds reserved sequence range\"\n        );\n    }\n\n    /// Regression test: StIndexAlgorithm round-trips must preserve the algorithm.\n    /// A bug in #3976 converted Hash -> BTreeAlgorithm on read-back, which caused\n    /// `check_compatible` to fail on any republish of a module with hash indexes\n    /// after a server restart.\n    #[test]\n    fn test_index_algorithm_roundtrip() {\n        use spacetimedb_primitives::col_list;\n\n        let cases = [\n            IndexAlgorithm::BTree(BTreeAlgorithm {\n                columns: col_list![0, 1],\n            }),\n            IndexAlgorithm::Hash(HashAlgorithm {\n                columns: col_list![2, 3],\n            }),\n            IndexAlgorithm::Direct(DirectAlgorithm { column: ColId(0) }),\n        ];\n\n        for original in &cases {\n            let st: StIndexAlgorithm = original.clone().into();\n            let roundtripped: IndexAlgorithm = st.into();\n            assert_eq!(\n                *original, roundtripped,\n                \"IndexAlgorithm round-trip failed: {original:?} -> {roundtripped:?}\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/datastore/src/traits.rs",
    "content": "use core::ops::Deref;\nuse std::borrow::Cow;\nuse std::{ops::RangeBounds, sync::Arc};\n\nuse super::locking_tx_datastore::datastore::TxMetrics;\nuse super::system_tables::ModuleKind;\nuse super::Result;\nuse crate::execution_context::Workload;\nuse crate::system_tables::ST_TABLE_ID;\nuse spacetimedb_data_structures::map::IntSet;\nuse spacetimedb_data_structures::small_map::SmallHashMap;\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_lib::{hash_bytes, Identity};\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::hash::Hash;\nuse spacetimedb_sats::{AlgebraicValue, ProductType, ProductValue};\nuse spacetimedb_schema::reducer_name::ReducerName;\nuse spacetimedb_schema::schema::{IndexSchema, SequenceSchema, TableSchema};\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_table::static_assert_size;\nuse spacetimedb_table::table::RowRef;\n\n/// The `IsolationLevel` enum specifies the degree to which a transaction is\n/// isolated from concurrently running transactions. The higher the isolation\n/// level, the more protection a transaction has from the effects of other\n/// transactions. The highest isolation level, `Serializable`, guarantees that\n/// transactions produce effects which are indistinguishable from the effects\n/// that would have been created by running the transactions one at a time, in\n/// some order, even though they may not actually have been run one at a time.\n///\n/// NOTE: It is always possible to achieve `Serializable` isolation by running\n/// transactions one at a time, although it is not necessarily performant.\n///\n/// Relaxing the isolation level can allow certain implementations to improve\n/// performance at the cost of allowing the produced transactions to include\n/// isolation anomalies. An isolation anomaly is a situation in which the\n/// results of a transaction are affected by the presence of other transactions\n/// running concurrently. Isolation anomalies can cause the database to violate\n/// the Isolation properties of the ACID guarantee, but not Atomicity or\n/// Durability.\n///\n/// Whether relaxing isolation level should be allowed to violate Consistency\n/// guarantees of the datastore is of some debate, although most databases\n/// choose to maintain consistency guarantees regardless of the isolation level,\n/// and we should too even at the cost of performance. See the following for a\n/// nuanced example of how postgres deals with consistency guarantees at lower\n/// isolation levels.\n///\n/// - https://stackoverflow.com/questions/55254236/do-i-need-higher-transaction-isolation-to-make-constraints-work-reliably-in-post\n///\n/// Thus from an application perspective, isolation anomalies may cause the data\n/// to be inconsistent or incorrect but will **not** cause it to violate the\n/// consistency constraints of the database like referential integrity,\n/// uniqueness, check constraints, etc.\n///\n/// NOTE: The datastore must treat unsupported isolation levels as though they\n/// ran at the strongest supported level.\n///\n/// The SQL standard defines four levels of transaction isolation.\n/// - Read Uncommitted\n/// - Read Committed\n/// - Repeatable Read\n/// - Serializable\n///\n/// We include an additional isolation level, `Snapshot`, which is not part of\n/// the SQL standard which offers a higher level of isolation than `Repeatable\n/// Read`. Snapshot is the same as Serializable, but permits certain\n/// serialization anomalies, such as write skew, to occur.\n///\n/// The ANSI SQL standard defined three anomalies in 1992:\n///\n/// - Dirty Reads: Occur when a transaction reads data written by a concurrent\n///   uncommitted transaction.\n///\n/// - Non-repeatable Reads: Occur when a transaction reads the same row twice\n///   and gets different data each time because another transaction has modified\n///   the data in between the reads.\n///\n/// - Phantom Reads: Occur when a transaction re-executes a query returning a\n///   set of rows that satisfy a search condition and finds that the set of rows\n///   satisfying the condition has changed due to another recently-committed\n///   transaction.\n///\n/// However since then database researchers have identified and cataloged many\n/// more. See:\n///\n/// - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf\n/// - https://pmg.csail.mit.edu/papers/adya-phd.pdf\n///\n/// See the following table of anomalies for a more complete list used as a\n/// reference for database implementers:\n///\n/// - https://github.com/ept/hermitage?tab=readme-ov-file#summary-of-test-results\n///\n/// The following anomalies are not part of the SQL standard, but are important:\n///\n/// - Write Skew: Occurs when two transactions concurrently read the same data,\n///   make decisions based on that data, and then write back modifications that\n///   are mutually inconsistent with the decisions made by the other transaction,\n///   despite no direct conflict on the same row being detected. e.g. I read what\n///   you write and you read what I write.\n///\n/// - Serialization Anomalies: Occur when the results of a set of transactions\n///   are inconsistent with any serial execution of those transactions.\n///\n/// PostgreSQL's documentation provides a good summary of the anomalies and\n/// isolation levels that it supports:\n///\n/// - https://www.postgresql.org/docs/current/transaction-iso.html\n///\n/// IMPORTANT!!! The order of these isolation levels in the enum is significant\n/// because we often must check if one isolation level is higher (offers more\n/// protection) than another, and the order is derived based on the lexical\n/// order of the enum variants.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub enum IsolationLevel {\n    /// ReadUncommitted allows transactions to see changes made by other\n    /// transactions even if those changes have not been committed. This level does\n    /// not protect against any of the isolation anomalies, including dirty reads.\n    ReadUncommitted,\n\n    /// ReadCommitted guarantees that any data read is committed at the moment\n    /// it is read.  Thus, it prevents dirty reads but does not prevent\n    /// non-repeatable reads or phantom reads.\n    ReadCommitted,\n\n    /// RepeatableRead ensures that if a transaction reads the same data more\n    /// than once, it will read the same data each time, thereby preventing\n    /// non-repeatable reads.  However, it does not necessarily prevent phantom\n    /// reads.\n    RepeatableRead,\n\n    /// Snapshot isolation provides a view of the database as it was at the\n    /// beginning of the transaction, ensuring that the transaction can only see\n    /// data committed before it started. This level of isolation guarantees\n    /// consistency across multiple reads by providing each transaction with a\n    /// \"snapshot\" of the database, preventing dirty reads, non-repeatable\n    /// reads, and phantom reads. However, snapshot isolation does not\n    /// completely eliminate all concurrency-related anomalies. One such anomaly\n    /// is write skew, a situation where two transactions concurrently read the\n    /// same data, make decisions based on that data, and then write back\n    /// modifications that are mutually inconsistent with the decisions made by\n    /// the other transaction, despite no direct conflict on the same row being\n    /// detected. This can occur because each transaction operates on its\n    /// snapshot without being aware of the other's uncommitted changes.  For\n    /// instance, in a scheduling application, two transactions might\n    /// concurrently check a condition (e.g., that a shift is not overstaffed),\n    /// and both decide to add a worker based on that condition, leading to an\n    /// overstaffing situation because they are unaware of each other's\n    /// decisions. Snapshot isolation requires additional mechanisms, such as\n    /// explicit locking or application-level checks, to prevent write skew\n    /// anomalies.\n    ///\n    /// NOTE: Snapshot isolation does not permit write-write conflicts and any\n    /// implementations of snapshot isolation must ensure that write-write\n    /// conflicts cannot occur.\n    Snapshot,\n\n    /// Serializable is the highest isolation level, where transactions are\n    /// executed with the illusion of being the only transaction running in the\n    /// system. This level prevents dirty reads, non-repeatable reads, and\n    /// phantom reads, effectively serializing access to the database to ensure\n    /// complete isolation.\n    ///\n    /// Correct implementations of Serializable isolation must either actually\n    /// permit only one transaction to run at a time , or track reads to ensure\n    /// that the data which has been read by one transaction has not been\n    /// modified by another transaction before the first transaction commits.\n    Serializable,\n}\n\npub type EphemeralTables = IntSet<TableId>;\n\n/// The [`TxData`] entry for one table.\n///\n/// All information about a table is stored in one place\n/// as the access pattern is to write as fast as possible\n/// and because it all fits within a single cache line\n/// and so that we can use `SmallVec`.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct TxDataTableEntry {\n    /// The name of the table for which there were deletions and/or insertions.\n    pub table_name: TableName,\n\n    /// The set of inserts for this table in the transaction.\n    // Note: `Arc<[ProductValue]>` allows to cheaply\n    // use the values from `TxData` without cloning the\n    // contained `ProductValue`s.\n    pub inserts: Arc<[ProductValue]>,\n\n    /// The set of deletes for this table in the transaction.\n    pub deletes: Arc<[ProductValue]>,\n\n    /// Was the entire table truncated?\n    ///\n    /// *Truncating* means that all rows in the table have been deleted.\n    /// In other words, a truncated table is a cleared table.\n    ///\n    /// Note that when this is `true`,\n    /// `deletes` will be non-empty and contain all deleted rows.\n    pub truncated: bool,\n\n    /// Is this table ephemeral?\n    ///\n    /// An ephemeral table is only populated when a view is executed\n    /// and does not need to be persisted to disk.\n    pub ephemeral: bool,\n}\n\nstatic_assert_size!(TxDataTableEntry, 56);\n\nimpl TxDataTableEntry {\n    /// Create a new, empty `TxDataTableEntry` for `table_name`.\n    pub fn new(table_name: TableName) -> Self {\n        Self {\n            table_name,\n            inserts: [].into(),\n            deletes: [].into(),\n            truncated: false,\n            ephemeral: false,\n        }\n    }\n}\n\n/// A record of all the operations within a transaction.\n///\n/// Some extra information is embedded here\n/// so that the recording of execution metrics can be done without holding the tx lock.\n#[derive(Default)]\npub struct TxData {\n    entries: SmallHashMap<TableId, TxDataTableEntry, 1, 8>,\n\n    /// Tx offset of the transaction which performed these operations.\n    ///\n    /// `None` implies that `inserts` and `deletes` are both empty,\n    /// but `Some` does not necessarily imply that either is non-empty.\n    tx_offset: Option<u64>,\n}\n\nimpl TxData {\n    /// Set `tx_offset` as the expected on-disk transaction offset of this transaction.\n    pub fn set_tx_offset(&mut self, tx_offset: u64) {\n        self.tx_offset = Some(tx_offset);\n    }\n\n    /// Read the expected on-disk transaction offset of this transaction.\n    ///\n    /// `None` implies that this [`TxData`] contains zero inserted or deleted rows,\n    /// but the inverse is not necessarily true;\n    /// a [`TxData`] may have a `tx_offset` but no row operations.\n    pub fn tx_offset(&self) -> Option<u64> {\n        self.tx_offset\n    }\n\n    /// Ensures that an entry for `table_id` exists\n    /// or initializes it with `table_name`.\n    fn init_entry(&mut self, table_id: TableId, table_name: &TableName) -> &mut TxDataTableEntry {\n        self.entries\n            .get_or_insert(table_id, || TxDataTableEntry::new(table_name.clone()))\n    }\n\n    /// Set `rows` as the inserted rows for `(table_id, table_name)`.\n    pub fn set_inserts_for_table(&mut self, table_id: TableId, table_name: &TableName, rows: Arc<[ProductValue]>) {\n        self.init_entry(table_id, table_name).inserts = rows;\n    }\n\n    /// Set `rows` as the deleted rows for `(table_id, table_name)`.\n    ///\n    /// When `truncated` is set, the table has been emptied in this transaction.\n    pub fn set_deletes_for_table(&mut self, table_id: TableId, table_name: &TableName, rows: Arc<[ProductValue]>) {\n        self.init_entry(table_id, table_name).deletes = rows;\n    }\n\n    /// Mark the given `truncated_tables` as truncated in this transaction.\n    pub fn set_truncates(&mut self, truncated_tables: impl IntoIterator<Item = TableId>) {\n        for table_id in truncated_tables {\n            if let Some(entry) = self.entries.get_mut(&table_id) {\n                entry.truncated = true;\n            }\n        }\n    }\n\n    /// Determines which ephemeral tables were modified in this transaction.\n    ///\n    /// Iterates over the tables updated in this transaction and records those that\n    /// also appear in `all_ephemeral_tables`.\n    pub fn set_ephemeral_tables(&mut self, all_ephemeral_tables: &EphemeralTables) {\n        for tid in all_ephemeral_tables {\n            if let Some(entry) = self.entries.get_mut(tid) {\n                entry.ephemeral = true;\n            }\n        }\n    }\n\n    /// Returns an iterator over the persistent inserted rows per table.\n    pub fn persistent_inserts(&self) -> impl Iterator<Item = (TableId, Arc<[ProductValue]>)> + '_ {\n        self.entries\n            .iter()\n            .filter(|(_, entry)| !entry.ephemeral && !entry.inserts.is_empty())\n            .map(|(table_id, entry)| (*table_id, entry.inserts.clone()))\n    }\n\n    /// Returns an iterator over the inserted rows per table.\n    pub fn inserts(&self) -> impl Iterator<Item = (TableId, &Arc<[ProductValue]>)> + '_ {\n        self.entries\n            .iter()\n            .filter(|(_, entry)| !entry.inserts.is_empty())\n            .map(|(table_id, entry)| (*table_id, &entry.inserts))\n    }\n\n    /// Get the `i`th inserted row for `table_id` if it exists\n    pub fn get_ith_insert(&self, table_id: TableId, i: usize) -> Option<&ProductValue> {\n        self.inserts_for_table(table_id).and_then(|is| is.get(i))\n    }\n\n    /// Returns an iterator over the persistent deleted rows per table.\n    ///\n    /// Truncated tables are not included.\n    pub fn persistent_deletes(&self) -> impl Iterator<Item = (TableId, Arc<[ProductValue]>)> + '_ {\n        self.entries\n            .iter()\n            .filter(|(_, entry)| !entry.ephemeral && !entry.truncated && !entry.deletes.is_empty())\n            .map(|(table_id, entry)| (*table_id, entry.deletes.clone()))\n    }\n\n    /// Returns an iterator over the deleted rows per table.\n    pub fn deletes(&self) -> impl Iterator<Item = (TableId, &Arc<[ProductValue]>)> + '_ {\n        self.entries\n            .iter()\n            .filter(|(_, entry)| !entry.deletes.is_empty())\n            .map(|(table_id, entry)| (*table_id, &entry.deletes))\n    }\n\n    /// Get the `i`th deleted row for `table_id` if it exists\n    pub fn get_ith_delete(&self, table_id: TableId, i: usize) -> Option<&ProductValue> {\n        self.deletes_for_table(table_id).and_then(|ds| ds.get(i))\n    }\n\n    /// Returns an iterator over all the non-emphemeral tables\n    /// that were truncated in this transaction.\n    pub fn persistent_truncates(&self) -> impl Iterator<Item = TableId> + '_ {\n        self.entries\n            .iter()\n            .filter(|(_, entry)| !entry.ephemeral && entry.truncated)\n            .map(|(table_id, _)| *table_id)\n    }\n\n    /// Check if this [`TxData`] contains any `inserted | deleted` rows\n    /// or `connect/disconnect` operations.\n    ///\n    /// Mutations of ephemeral tables are excluded, i.e. if the transaction\n    /// modifies only ephemeral tables (and is not a connect/disconnect operation),\n    /// the method returns `false`.\n    ///\n    /// This is used to determine if a transaction should be written to disk.\n    pub fn has_rows_or_connect_disconnect(&self, reducer_name: Option<&ReducerName>) -> bool {\n        // Persist if the table is non-emphemeral and had any inserts or deletes.\n        self.entries.values().any(|e|\n            !e.ephemeral\n            && (!e.inserts.is_empty() || !e.deletes.is_empty())\n        )\n        // Also persist for connect/disconnect reducers.\n            || matches!(\n                reducer_name.map(|rn| rn.strip_prefix(\"__identity_\")),\n                Some(Some(\"connected__\" | \"disconnected__\"))\n            )\n    }\n\n    /// Returns a list of tables affected in this transaction.\n    pub fn table_ids_and_names(&self) -> impl '_ + Iterator<Item = (TableId, &str)> {\n        self.entries.iter().map(|(k, e)| (*k, &*e.table_name))\n    }\n\n    /// Returns the number o tables affected in this transaction.\n    pub fn num_tables_affected(&self) -> usize {\n        self.entries.len()\n    }\n\n    /// Returns the entry for `table_id`.\n    pub fn entry_for(&self, table_id: TableId) -> Option<&TxDataTableEntry> {\n        self.entries.get(&table_id)\n    }\n\n    /// Returns the inserts for `table_id`.\n    pub fn inserts_for_table(&self, table_id: TableId) -> Option<&[ProductValue]> {\n        self.entry_for(table_id).map(|entry| &*entry.inserts)\n    }\n\n    /// Returns the inserts for `table_id`.\n    pub fn deletes_for_table(&self, table_id: TableId) -> Option<&[ProductValue]> {\n        self.entry_for(table_id).map(|entry| &*entry.deletes)\n    }\n\n    /// Returns an iterator over all [`TxDataTableEntry`]s in this transaction.\n    pub fn iter_table_entries(&self) -> impl '_ + ExactSizeIterator<Item = (TableId, &TxDataTableEntry)> {\n        self.entries.iter().map(|(k, v)| (*k, v))\n    }\n}\n\n/// The result of [`MutTxDatastore::row_type_for_table_mut_tx`] and friends.\n/// This is a smart pointer returning a `&ProductType`.\npub enum RowTypeForTable<'a> {\n    /// A reference can be stored to the type.\n    Ref(&'a ProductType),\n    /// The type is within the schema.\n    Arc(Arc<TableSchema>),\n}\n\nimpl Deref for RowTypeForTable<'_> {\n    type Target = ProductType;\n\n    fn deref(&self) -> &Self::Target {\n        match self {\n            Self::Ref(x) => x,\n            Self::Arc(x) => x.get_row_type(),\n        }\n    }\n}\n\npub trait Data: Into<ProductValue> {\n    fn view(&self) -> Cow<'_, ProductValue>;\n}\n\npub trait DataRow: Send + Sync {\n    type RowId: Copy;\n\n    type RowRef<'a>;\n\n    /// Assuming `row_ref` refers to a row in `st_tables`,\n    /// read out the table id from the row.\n    fn read_table_id(&self, row_ref: Self::RowRef<'_>) -> Result<TableId>;\n}\n\npub trait Tx {\n    type Tx;\n\n    /// Begins a read-only transaction under the given `workload`.\n    fn begin_tx(&self, workload: Workload) -> Self::Tx;\n\n    /// Release this read-only transaction.\n    ///\n    /// Returns:\n    /// - [`TxOffset`], the smallest transaction offset visible to this transaction.\n    ///\n    ///   Note that, if the transaction was running under an isolation level\n    ///   weaker than [`IsolationLevel::Snapshot`], it may have observed\n    ///   transactions at a later offset than when it started.\n    ///\n    ///   Implementations must uphold that the returned transaction offset\n    ///   accounts for such read anomalies, i.e. the offset must include the\n    ///   observed transactions.\n    ///\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran within this transaction.\n    fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, Option<ReducerName>);\n}\n\npub trait MutTx {\n    type MutTx;\n\n    /// Begins a mutable transaction under the given `isolation_level` and `workload`.\n    fn begin_mut_tx(&self, isolation_level: IsolationLevel, workload: Workload) -> Self::MutTx;\n\n    /// Commits `tx`, applying its changes to the committed state.\n    ///\n    /// Returns:\n    /// - [`TxOffset`], the offset this transaction was committed at.\n    ///\n    ///   Note that, if the transaction was running under an isolation level\n    ///   weaker than [`IsolationLevel::Snapshot`], it may have observed\n    ///   transactions at a later offset than when it started.\n    ///\n    ///   Implementations must uphold that the returned transaction offset\n    ///   accounts for such read anomalies, i.e. the offset must include the\n    ///   observed transactions.\n    ///\n    /// - [`TxData`], the set of inserts and deletes performed by this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran during this transaction.\n    #[allow(clippy::type_complexity)]\n    fn commit_mut_tx(&self, tx: Self::MutTx) -> Result<Option<(TxOffset, TxData, TxMetrics, Option<ReducerName>)>>;\n\n    /// Rolls back this transaction, discarding its changes.\n    ///\n    /// Returns:\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran within this transaction.\n    fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, Option<ReducerName>);\n}\n\n/// Standard metadata associated with a database.\n#[derive(Debug)]\npub struct Metadata {\n    /// The stable [`Identity`] of the database.\n    pub database_identity: Identity,\n    /// The identity of the database's owner.\n    pub owner_identity: Identity,\n    /// The hash of the binary module set for the database.\n    pub program_hash: Hash,\n}\n\n/// Program associated with a database.\n#[derive(Clone)]\npub struct Program {\n    /// Hash over the program's bytes.\n    pub hash: Hash,\n    /// The raw bytes of the program.\n    pub bytes: Box<[u8]>,\n    /// The kind (host type) of the program.\n    pub kind: ModuleKind,\n}\n\nimpl Program {\n    /// Create a [`Program`] from its raw bytes.\n    ///\n    /// This computes the hash over `bytes`, so prefer constructing [`Program`]\n    /// directly if the hash is already known.\n    pub fn from_bytes(kind: ModuleKind, bytes: impl Into<Box<[u8]>>) -> Self {\n        let bytes = bytes.into();\n        let hash = hash_bytes(&bytes);\n        Self { hash, bytes, kind }\n    }\n\n    /// Create a [`Program`] with no bytes.\n    pub fn empty(kind: ModuleKind) -> Self {\n        Self::from_bytes(kind, [])\n    }\n}\n\n/// Additional information about an insert operation.\npub struct InsertFlags {\n    /// Is the table a scheduler table?\n    pub is_scheduler_table: bool,\n}\n\n/// Additional information about an update operation.\n// TODO(centril): consider fusing this with `InsertFlags`.\npub struct UpdateFlags {\n    /// Is the table a scheduler table?\n    pub is_scheduler_table: bool,\n}\n\npub trait TxDatastore: DataRow + Tx {\n    type IterTx<'a>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n\n    type IterByColRangeTx<'a, R: RangeBounds<AlgebraicValue>>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n    type IterByColEqTx<'a, 'r>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n\n    fn iter_tx<'a>(&'a self, tx: &'a Self::Tx, table_id: TableId) -> Result<Self::IterTx<'a>>;\n\n    fn iter_by_col_range_tx<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a Self::Tx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<Self::IterByColRangeTx<'a, R>>;\n\n    fn iter_by_col_eq_tx<'a, 'r>(\n        &'a self,\n        tx: &'a Self::Tx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEqTx<'a, 'r>>;\n\n    fn table_id_exists_tx(&self, tx: &Self::Tx, table_id: &TableId) -> bool;\n    fn table_id_from_name_tx(&self, tx: &Self::Tx, table_name: &str) -> Result<Option<TableId>>;\n    fn table_name_from_id_tx<'a>(&'a self, tx: &'a Self::Tx, table_id: TableId) -> Result<Option<Cow<'a, str>>>;\n    fn schema_for_table_tx(&self, tx: &Self::Tx, table_id: TableId) -> super::Result<Arc<TableSchema>>;\n    fn get_all_tables_tx(&self, tx: &Self::Tx) -> super::Result<Vec<Arc<TableSchema>>>;\n\n    /// Obtain the [`Metadata`] for this datastore.\n    ///\n    /// A `None` return value means that the datastore is not fully initialized yet.\n    fn metadata(&self, tx: &Self::Tx) -> Result<Option<Metadata>>;\n\n    /// Obtain the compiled module associated with this datastore.\n    ///\n    /// A `None` return value means that the datastore is not fully initialized yet.\n    fn program(&self, tx: &Self::Tx) -> Result<Option<Program>>;\n}\n\npub trait MutTxDatastore: TxDatastore + MutTx {\n    type IterMutTx<'a>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n\n    type IterByColRangeMutTx<'a, R: RangeBounds<AlgebraicValue>>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n\n    type IterByColEqMutTx<'a, 'r>: Iterator<Item = Self::RowRef<'a>>\n    where\n        Self: 'a;\n\n    // Tables\n    fn create_table_mut_tx(&self, tx: &mut Self::MutTx, schema: TableSchema) -> Result<TableId>;\n    // In these methods, we use `'tx` because the return type must borrow data\n    // from `Inner` in the `Locking` implementation,\n    // and `Inner` lives in `tx: &MutTxId`.\n    fn row_type_for_table_mut_tx<'tx>(&self, tx: &'tx Self::MutTx, table_id: TableId) -> Result<RowTypeForTable<'tx>>;\n    fn schema_for_table_mut_tx(&self, tx: &Self::MutTx, table_id: TableId) -> Result<Arc<TableSchema>>;\n    fn drop_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId) -> Result<()>;\n    fn rename_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId, new_name: TableName) -> Result<()>;\n    fn view_id_from_name_mut_tx(&self, tx: &Self::MutTx, view_name: &str) -> Result<Option<ViewId>>;\n    fn table_id_from_name_mut_tx(&self, tx: &Self::MutTx, table_name: &str) -> Result<Option<TableId>>;\n    fn table_id_exists_mut_tx(&self, tx: &Self::MutTx, table_id: &TableId) -> bool;\n    fn table_name_from_id_mut_tx<'a>(&'a self, tx: &'a Self::MutTx, table_id: TableId) -> Result<Option<Cow<'a, str>>>;\n    fn get_all_tables_mut_tx(&self, tx: &Self::MutTx) -> super::Result<Vec<Arc<TableSchema>>> {\n        let mut tables = Vec::new();\n        let table_rows = self.iter_mut_tx(tx, ST_TABLE_ID)?.collect::<Vec<_>>();\n        for row in table_rows {\n            let table_id = self.read_table_id(row)?;\n            tables.push(self.schema_for_table_mut_tx(tx, table_id)?);\n        }\n        Ok(tables)\n    }\n\n    // Indexes\n\n    fn create_index_mut_tx(&self, tx: &mut Self::MutTx, index_schema: IndexSchema, is_unique: bool) -> Result<IndexId>;\n    fn drop_index_mut_tx(&self, tx: &mut Self::MutTx, index_id: IndexId) -> Result<()>;\n    fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> super::Result<Option<IndexId>>;\n\n    // TODO: Index data\n    // - index_scan_mut_tx\n    // - index_range_scan_mut_tx\n    // - index_seek_mut_tx\n\n    // Sequences\n    fn get_next_sequence_value_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result<i128>;\n    fn create_sequence_mut_tx(&self, tx: &mut Self::MutTx, sequence_schema: SequenceSchema) -> Result<SequenceId>;\n    fn drop_sequence_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result<()>;\n    fn sequence_id_from_name_mut_tx(&self, tx: &Self::MutTx, sequence_name: &str) -> super::Result<Option<SequenceId>>;\n\n    // Constraints\n    fn drop_constraint_mut_tx(&self, tx: &mut Self::MutTx, constraint_id: ConstraintId) -> super::Result<()>;\n    fn constraint_id_from_name(&self, tx: &Self::MutTx, constraint_name: &str) -> super::Result<Option<ConstraintId>>;\n\n    // Data\n    fn iter_mut_tx<'a>(&'a self, tx: &'a Self::MutTx, table_id: TableId) -> Result<Self::IterMutTx<'a>>;\n    fn iter_by_col_range_mut_tx<'a, R: RangeBounds<AlgebraicValue>>(\n        &'a self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        range: R,\n    ) -> Result<Self::IterByColRangeMutTx<'a, R>>;\n    fn iter_by_col_eq_mut_tx<'a, 'r>(\n        &'a self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        cols: impl Into<ColList>,\n        value: &'r AlgebraicValue,\n    ) -> Result<Self::IterByColEqMutTx<'a, 'r>>;\n    fn get_mut_tx<'a>(\n        &self,\n        tx: &'a Self::MutTx,\n        table_id: TableId,\n        row_id: &'a Self::RowId,\n    ) -> Result<Option<Self::RowRef<'a>>>;\n    fn delete_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        row_ids: impl IntoIterator<Item = Self::RowId>,\n    ) -> u32;\n    fn delete_by_rel_mut_tx(\n        &self,\n        tx: &mut Self::MutTx,\n        table_id: TableId,\n        relation: impl IntoIterator<Item = ProductValue>,\n    ) -> u32;\n    /// Inserts `row`, encoded in BSATN, into the table identified by `table_id`.\n    ///\n    /// Returns the list of columns with sequence-trigger values that were replaced with generated ones\n    /// and a reference to the row as a [`RowRef`].\n    /// Also returns any additional insert flags.\n    ///\n    /// Generated columns are columns with an auto-inc sequence\n    /// and where the column was `0` in `row`.\n    // TODO(centril): consider making the tuple into a struct.\n    fn insert_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, InsertFlags)>;\n    /// Updates a row to `row`, encoded in BSATN, into the table identified by `table_id`\n    /// using the index identified by `index_id`.\n    ///\n    /// Returns the list of columns with sequence-trigger values that were replaced with generated ones\n    /// and a reference to the row as a [`RowRef`].\n    /// Also returns any additional update flags.\n    ///\n    /// Generated columns are columns with an auto-inc sequence\n    /// and where the column was `0` in `row`.\n    // TODO(centril): consider making the tuple into a struct.\n    fn update_mut_tx<'a>(\n        &'a self,\n        tx: &'a mut Self::MutTx,\n        table_id: TableId,\n        index_id: IndexId,\n        row: &[u8],\n    ) -> Result<(ColList, RowRef<'a>, UpdateFlags)>;\n\n    /// Obtain the [`Metadata`] for this datastore.\n    ///\n    /// Like [`TxDatastore`], but in a mutable transaction context.\n    fn metadata_mut_tx(&self, tx: &Self::MutTx) -> Result<Option<Metadata>>;\n\n    /// Update the datastore with the supplied binary program.\n    fn update_program(&self, tx: &mut Self::MutTx, program: Program) -> Result<()>;\n}\n"
  },
  {
    "path": "crates/durability/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-durability\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\n\ndescription = \"Traits and single-node implementation of durability for SpacetimeDB.\"\n\n[features]\ntest = []\nfallocate = [\"spacetimedb-commitlog/fallocate\"]\n\n[dependencies]\nanyhow.workspace = true\nfutures.workspace = true\nitertools.workspace = true\nlog.workspace = true\nscopeguard.workspace = true\nspacetimedb-commitlog.workspace = true\nspacetimedb-fs-utils.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-sats.workspace = true\nthiserror.workspace = true\ntokio.workspace = true\ntracing.workspace = true\n\n[dev-dependencies]\nspacetimedb-commitlog = { workspace = true, features = [\"test\"] }\ntempfile.workspace = true\ntokio.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/durability/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/durability/src/imp/local.rs",
    "content": "use std::{\n    io,\n    num::NonZeroUsize,\n    path::PathBuf,\n    sync::{\n        atomic::{AtomicU64, Ordering::Relaxed},\n        Arc,\n    },\n};\n\nuse futures::{FutureExt as _, TryFutureExt as _};\nuse itertools::Itertools as _;\nuse log::{info, trace, warn};\nuse scopeguard::ScopeGuard;\nuse spacetimedb_commitlog::{error, payload::Txdata, Commit, Commitlog, Decoder, Encode, Transaction};\nuse spacetimedb_fs_utils::lockfile::advisory::{LockError, LockedFile};\nuse spacetimedb_paths::server::ReplicaDir;\nuse thiserror::Error;\nuse tokio::{\n    sync::{futures::OwnedNotified, mpsc, oneshot, watch, Notify},\n    task::{spawn_blocking, AbortHandle},\n};\nuse tracing::{instrument, Span};\n\nuse crate::{Close, Durability, DurableOffset, History, TxOffset};\n\npub use spacetimedb_commitlog::repo::{OnNewSegmentFn, SizeOnDisk};\n\n/// [`Local`] configuration.\n#[derive(Clone, Copy, Debug)]\npub struct Options {\n    /// The number of elements to reserve for batching transactions.\n    ///\n    /// This puts an upper bound on the buffer capacity, while not preventing\n    /// reallocations when the number of queued transactions exceeds it.\n    ///\n    /// In other words, the durability actor will attempt to receive all\n    /// transactions that are currently in the queue, but shrink the buffer to\n    /// `batch_capacity` if it had to make additional space during a burst.\n    ///\n    /// Default: 4096\n    pub batch_capacity: NonZeroUsize,\n    /// [`Commitlog`] configuration.\n    pub commitlog: spacetimedb_commitlog::Options,\n}\n\nimpl Options {\n    pub const DEFAULT_BATCH_CAPACITY: NonZeroUsize = NonZeroUsize::new(4096).unwrap();\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Self {\n            batch_capacity: Self::DEFAULT_BATCH_CAPACITY,\n            commitlog: Default::default(),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum OpenError {\n    #[error(\"commitlog directory is locked\")]\n    Lock(#[from] LockError),\n    #[error(\"failed to open commitlog\")]\n    Commitlog(#[from] io::Error),\n}\n\ntype ShutdownReply = oneshot::Sender<OwnedNotified>;\n\n/// [`Durability`] implementation backed by a [`Commitlog`] on local storage.\n///\n/// The commitlog is constrained to store the canonical [`Txdata`] payload,\n/// where the generic parameter `T` is the type of the row data stored in\n/// the mutations section.\n///\n/// `T` is left generic in order to allow bypassing the `ProductValue`\n/// intermediate representation in the future.\n///\n/// Note, however, that instantiating `T` to a different type may require to\n/// change the log format version!\npub struct Local<T> {\n    /// The [`Commitlog`] this [`Durability`] and [`History`] impl wraps.\n    clog: Arc<Commitlog<Txdata<T>>>,\n    /// The durable transaction offset, as reported by the background\n    /// [`FlushAndSyncTask`].\n    durable_offset: watch::Receiver<Option<TxOffset>>,\n    /// Backlog of transactions to be written to disk by the background\n    /// [`PersisterTask`].\n    ///\n    /// Note that this is unbounded!\n    queue: mpsc::UnboundedSender<Transaction<Txdata<T>>>,\n    /// How many transactions are sitting in the `queue`.\n    ///\n    /// This is mainly for observability purposes, and can thus be updated with\n    /// relaxed memory ordering.\n    queue_depth: Arc<AtomicU64>,\n    /// Channel to request the actor to exit.\n    shutdown: mpsc::Sender<ShutdownReply>,\n    /// [AbortHandle] to force cancellation of the [Actor].\n    abort: AbortHandle,\n}\n\nimpl<T: Encode + Send + Sync + 'static> Local<T> {\n    /// Create a [`Local`] instance at the `replica_dir`.\n    ///\n    /// `replica_dir` must already exist.\n    ///\n    /// Background tasks are spawned onto the provided tokio runtime.\n    ///\n    /// We will send a message down the `on_new_segment` channel whenever we begin a new commitlog segment.\n    /// This is used to capture a snapshot each new segment.\n    pub fn open(\n        replica_dir: ReplicaDir,\n        rt: tokio::runtime::Handle,\n        opts: Options,\n        on_new_segment: Option<Arc<OnNewSegmentFn>>,\n    ) -> Result<Self, OpenError> {\n        info!(\"open local durability\");\n\n        // We could just place a lock on the commitlog directory,\n        // yet for backwards-compatibility, we keep using the `db.lock` file.\n        let lock = Lock::create(replica_dir.0.join(\"db.lock\"))?;\n\n        let clog = Arc::new(Commitlog::open(\n            replica_dir.commit_log(),\n            opts.commitlog,\n            on_new_segment,\n        )?);\n        let (queue, txdata_rx) = mpsc::unbounded_channel();\n        let queue_depth = Arc::new(AtomicU64::new(0));\n        let (durable_tx, durable_rx) = watch::channel(clog.max_committed_offset());\n        let (shutdown_tx, shutdown_rx) = mpsc::channel(1);\n\n        let abort = rt\n            .spawn(\n                Actor {\n                    clog: clog.clone(),\n\n                    durable_offset: durable_tx,\n                    queue_depth: queue_depth.clone(),\n\n                    batch_capacity: opts.batch_capacity,\n\n                    lock,\n                }\n                .run(txdata_rx, shutdown_rx),\n            )\n            .abort_handle();\n\n        Ok(Self {\n            clog,\n            durable_offset: durable_rx,\n            queue,\n            shutdown: shutdown_tx,\n            queue_depth,\n            abort,\n        })\n    }\n\n    /// Obtain a read-only copy of the durable state that implements [History].\n    pub fn as_history(&self) -> impl History<TxData = Txdata<T>> + use<T> {\n        self.clog.clone()\n    }\n}\n\nimpl<T: Send + Sync + 'static> Local<T> {\n    /// Inspect how many transactions added via [`Self::append_tx`] are pending\n    /// to be applied to the underlying [`Commitlog`].\n    pub fn queue_depth(&self) -> u64 {\n        self.queue_depth.load(Relaxed)\n    }\n\n    /// Obtain an iterator over the [`Commit`]s in the underlying log.\n    pub fn commits_from(&self, offset: TxOffset) -> impl Iterator<Item = Result<Commit, error::Traversal>> + use<T> {\n        self.clog.commits_from(offset).map_ok(Commit::from)\n    }\n\n    /// Get a list of segment offsets, sorted in ascending order.\n    pub fn existing_segment_offsets(&self) -> io::Result<Vec<TxOffset>> {\n        self.clog.existing_segment_offsets()\n    }\n\n    /// Compress the segments at the offsets provided, marking them as immutable.\n    pub fn compress_segments(&self, offsets: &[TxOffset]) -> io::Result<()> {\n        self.clog.compress_segments(offsets)\n    }\n\n    /// Get the size on disk of the underlying [`Commitlog`].\n    pub fn size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        self.clog.size_on_disk()\n    }\n}\n\nstruct Actor<T> {\n    clog: Arc<Commitlog<Txdata<T>>>,\n\n    durable_offset: watch::Sender<Option<TxOffset>>,\n    queue_depth: Arc<AtomicU64>,\n\n    batch_capacity: NonZeroUsize,\n\n    #[allow(unused)]\n    lock: Lock,\n}\n\nimpl<T: Encode + Send + Sync + 'static> Actor<T> {\n    #[instrument(name = \"durability::local::actor\", skip_all)]\n    async fn run(\n        self,\n        mut transactions_rx: mpsc::UnboundedReceiver<Transaction<Txdata<T>>>,\n        mut shutdown_rx: mpsc::Receiver<oneshot::Sender<OwnedNotified>>,\n    ) {\n        info!(\"starting durability actor\");\n\n        let mut tx_buf = Vec::with_capacity(self.batch_capacity.get());\n        // `flush_and_sync` when the loop exits without panicking,\n        // or `flush_and_sync` inside the loop failed.\n        let mut sync_on_exit = true;\n\n        loop {\n            tokio::select! {\n                // Biased towards the shutdown channel,\n                // so that we stop accepting new data promptly after\n                // `Durability::close` was called.\n                biased;\n\n                Some(reply) = shutdown_rx.recv() => {\n                    transactions_rx.close();\n                    let _ = reply.send(self.lock.notified());\n                },\n\n                // Pop as many elements from the channel as possible,\n                // potentially requiring the `tx_buf` to allocate additional\n                // capacity.\n                // We'll reclaim capacity in excess of `self.batch_size` below.\n                n = transactions_rx.recv_many(&mut tx_buf, usize::MAX) => {\n                    if n == 0 {\n                        break;\n                    }\n                    self.queue_depth.fetch_sub(n as u64, Relaxed);\n                    let clog = self.clog.clone();\n                    tx_buf = spawn_blocking(move || -> io::Result<Vec<Transaction<Txdata<T>>>> {\n                        for tx in tx_buf.drain(..) {\n                            clog.commit([tx])?;\n                        }\n                        Ok(tx_buf)\n                    })\n                    .await\n                    .expect(\"commitlog write panicked\")\n                    .expect(\"commitlog write failed\");\n                    if self.flush_and_sync().await.is_err() {\n                        sync_on_exit = false;\n                        break;\n                    }\n                    // Reclaim burst capacity.\n                    if n < self.batch_capacity.get() {\n                        tx_buf.shrink_to(self.batch_capacity.get());\n                    }\n                },\n            }\n        }\n\n        if sync_on_exit {\n            let _ = self.flush_and_sync().await;\n        }\n\n        info!(\"exiting durability actor\");\n    }\n\n    #[instrument(skip_all)]\n    async fn flush_and_sync(&self) -> io::Result<Option<TxOffset>> {\n        // Skip if nothing changed.\n        if let Some((committed, durable)) = self.clog.max_committed_offset().zip(*self.durable_offset.borrow())\n            && committed == durable\n        {\n            return Ok(None);\n        }\n\n        let clog = self.clog.clone();\n        let span = Span::current();\n        spawn_blocking(move || {\n            let _span = span.enter();\n            clog.flush_and_sync()\n        })\n        .await\n        .expect(\"commitlog flush-and-sync blocking task panicked\")\n        .inspect_err(|e| warn!(\"error flushing commitlog: {e:#}\"))\n        .inspect(|maybe_offset| {\n            if let Some(new_offset) = maybe_offset {\n                trace!(\"synced to offset {new_offset}\");\n                self.durable_offset.send_modify(|val| {\n                    val.replace(*new_offset);\n                });\n            }\n        })\n    }\n}\n\nstruct Lock {\n    file: Option<LockedFile>,\n    notify_on_drop: Arc<Notify>,\n}\n\nimpl Lock {\n    pub fn create(path: PathBuf) -> Result<Self, LockError> {\n        let file = LockedFile::lock(path).map(Some)?;\n        let notify_on_drop = Arc::new(Notify::new());\n\n        Ok(Self { file, notify_on_drop })\n    }\n\n    pub fn notified(&self) -> OwnedNotified {\n        self.notify_on_drop.clone().notified_owned()\n    }\n}\n\nimpl Drop for Lock {\n    fn drop(&mut self) {\n        // Ensure the file lock is dropped before notifying.\n        if let Some(file) = self.file.take() {\n            drop(file);\n        }\n        self.notify_on_drop.notify_waiters();\n    }\n}\n\nimpl<T: Send + Sync + 'static> Durability for Local<T> {\n    type TxData = Txdata<T>;\n\n    fn append_tx(&self, tx: Transaction<Self::TxData>) {\n        self.queue.send(tx).expect(\"durability actor crashed\");\n        self.queue_depth.fetch_add(1, Relaxed);\n    }\n\n    fn durable_tx_offset(&self) -> DurableOffset {\n        self.durable_offset.clone().into()\n    }\n\n    fn close(&self) -> Close {\n        info!(\"close local durability\");\n\n        let durable_offset = self.durable_tx_offset();\n        let shutdown = self.shutdown.clone();\n        // Abort actor if shutdown future is dropped.\n        let abort = scopeguard::guard(self.abort.clone(), |actor| {\n            warn!(\"close future dropped, aborting durability actor\");\n            actor.abort();\n        });\n\n        async move {\n            let (done_tx, done_rx) = oneshot::channel();\n            // Ignore channel errors - those just mean the actor is already gone.\n            let _ = shutdown\n                .send(done_tx)\n                .map_err(drop)\n                .and_then(|()| done_rx.map_err(drop))\n                .and_then(|done| async move {\n                    done.await;\n                    Ok(())\n                })\n                .await;\n            // Don't abort if we completed normally.\n            let _ = ScopeGuard::into_inner(abort);\n\n            durable_offset.last_seen()\n        }\n        .boxed()\n    }\n}\n\nimpl<T: Encode + 'static> History for Commitlog<Txdata<T>> {\n    type TxData = Txdata<T>;\n\n    fn fold_transactions_from<D>(&self, offset: TxOffset, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        self.fold_transactions_from(offset, decoder)\n    }\n\n    fn transactions_from<'a, D>(\n        &self,\n        offset: TxOffset,\n        decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<Self::TxData>, D::Error>>\n    where\n        D: Decoder<Record = Self::TxData>,\n        D::Error: From<error::Traversal>,\n        Self::TxData: 'a,\n    {\n        self.transactions_from(offset, decoder)\n    }\n\n    fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n        let min = self.min_committed_offset().unwrap_or_default();\n        let max = self.max_committed_offset();\n\n        (min, max)\n    }\n}\n"
  },
  {
    "path": "crates/durability/src/imp/local.rs.orig",
    "content": "use std::{\n    io,\n    num::NonZeroUsize,\n    path::PathBuf,\n    sync::{\n        atomic::{AtomicU64, Ordering::Relaxed},\n        Arc,\n    },\n};\n\nuse futures::{FutureExt as _, TryFutureExt as _};\nuse itertools::Itertools as _;\nuse log::{info, trace, warn};\nuse scopeguard::ScopeGuard;\nuse spacetimedb_commitlog::{error, payload::Txdata, Commit, Commitlog, Decoder, Encode, Transaction};\nuse spacetimedb_fs_utils::lockfile::advisory::{LockError, LockedFile};\nuse spacetimedb_paths::server::ReplicaDir;\nuse thiserror::Error;\nuse tokio::{\n    sync::{futures::OwnedNotified, mpsc, oneshot, watch, Notify},\n    task::{spawn_blocking, AbortHandle},\n};\nuse tracing::{instrument, Span};\n\nuse crate::{Close, Durability, DurableOffset, History, TxOffset};\n\npub use spacetimedb_commitlog::repo::{OnNewSegmentFn, SizeOnDisk};\n\n/// [`Local`] configuration.\n#[derive(Clone, Copy, Debug)]\npub struct Options {\n<<<<<<< conflict 1 of 2\n+++++++ ssmwmsoq d6e2ba51 \"durability: Flush batches (#4478)\"\n    /// The number of elements to reserve for batching transactions.\n    ///\n    /// This puts an upper bound on the buffer capacity, while not preventing\n    /// reallocations when the number of queued transactions exceeds it.\n    ///\n    /// In other words, the durability actor will attempt to receive all\n    /// transactions that are currently in the queue, but shrink the buffer to\n    /// `batch_capacity` if it had to make additional space during a burst.\n    ///\n    /// Default: 4096\n    pub batch_capacity: NonZeroUsize,\n%%%%%%% diff from: ttnusruw 6fea15f7 \"[C#] Update RawTableIterBase.Enumerator to use `ArrayPool` for buffer (#4385)\"\n\\\\\\\\\\\\\\        to: ppnmwost abbcec4a \"keynote-2: alpha -> 1.5, withConfirmedReads(true), remove warmup (#4492)\"\n     /// Periodically flush and sync the log this often.\n     ///\n-    /// Default: 50ms\n+    /// Default: 10ms\n     pub sync_interval: Duration,\n>>>>>>> conflict 1 of 2 ends\n    /// [`Commitlog`] configuration.\n    pub commitlog: spacetimedb_commitlog::Options,\n}\n\nimpl Options {\n    pub const DEFAULT_BATCH_CAPACITY: NonZeroUsize = NonZeroUsize::new(4096).unwrap();\n}\n\nimpl Default for Options {\n    fn default() -> Self {\n        Self {\n<<<<<<< conflict 2 of 2\n+++++++ ssmwmsoq d6e2ba51 \"durability: Flush batches (#4478)\"\n            batch_capacity: Self::DEFAULT_BATCH_CAPACITY,\n%%%%%%% diff from: ttnusruw 6fea15f7 \"[C#] Update RawTableIterBase.Enumerator to use `ArrayPool` for buffer (#4385)\"\n\\\\\\\\\\\\\\        to: ppnmwost abbcec4a \"keynote-2: alpha -> 1.5, withConfirmedReads(true), remove warmup (#4492)\"\n-            sync_interval: Duration::from_millis(50),\n+            sync_interval: Duration::from_millis(10),\n>>>>>>> conflict 2 of 2 ends\n            commitlog: Default::default(),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\npub enum OpenError {\n    #[error(\"commitlog directory is locked\")]\n    Lock(#[from] LockError),\n    #[error(\"failed to open commitlog\")]\n    Commitlog(#[from] io::Error),\n}\n\ntype ShutdownReply = oneshot::Sender<OwnedNotified>;\n\n/// [`Durability`] implementation backed by a [`Commitlog`] on local storage.\n///\n/// The commitlog is constrained to store the canonical [`Txdata`] payload,\n/// where the generic parameter `T` is the type of the row data stored in\n/// the mutations section.\n///\n/// `T` is left generic in order to allow bypassing the `ProductValue`\n/// intermediate representation in the future.\n///\n/// Note, however, that instantiating `T` to a different type may require to\n/// change the log format version!\npub struct Local<T> {\n    /// The [`Commitlog`] this [`Durability`] and [`History`] impl wraps.\n    clog: Arc<Commitlog<Txdata<T>>>,\n    /// The durable transaction offset, as reported by the background\n    /// [`FlushAndSyncTask`].\n    durable_offset: watch::Receiver<Option<TxOffset>>,\n    /// Backlog of transactions to be written to disk by the background\n    /// [`PersisterTask`].\n    ///\n    /// Note that this is unbounded!\n    queue: mpsc::UnboundedSender<Transaction<Txdata<T>>>,\n    /// How many transactions are sitting in the `queue`.\n    ///\n    /// This is mainly for observability purposes, and can thus be updated with\n    /// relaxed memory ordering.\n    queue_depth: Arc<AtomicU64>,\n    /// Channel to request the actor to exit.\n    shutdown: mpsc::Sender<ShutdownReply>,\n    /// [AbortHandle] to force cancellation of the [Actor].\n    abort: AbortHandle,\n}\n\nimpl<T: Encode + Send + Sync + 'static> Local<T> {\n    /// Create a [`Local`] instance at the `replica_dir`.\n    ///\n    /// `replica_dir` must already exist.\n    ///\n    /// Background tasks are spawned onto the provided tokio runtime.\n    ///\n    /// We will send a message down the `on_new_segment` channel whenever we begin a new commitlog segment.\n    /// This is used to capture a snapshot each new segment.\n    pub fn open(\n        replica_dir: ReplicaDir,\n        rt: tokio::runtime::Handle,\n        opts: Options,\n        on_new_segment: Option<Arc<OnNewSegmentFn>>,\n    ) -> Result<Self, OpenError> {\n        info!(\"open local durability\");\n\n        // We could just place a lock on the commitlog directory,\n        // yet for backwards-compatibility, we keep using the `db.lock` file.\n        let lock = Lock::create(replica_dir.0.join(\"db.lock\"))?;\n\n        let clog = Arc::new(Commitlog::open(\n            replica_dir.commit_log(),\n            opts.commitlog,\n            on_new_segment,\n        )?);\n        let (queue, txdata_rx) = mpsc::unbounded_channel();\n        let queue_depth = Arc::new(AtomicU64::new(0));\n        let (durable_tx, durable_rx) = watch::channel(clog.max_committed_offset());\n        let (shutdown_tx, shutdown_rx) = mpsc::channel(1);\n\n        let abort = rt\n            .spawn(\n                Actor {\n                    clog: clog.clone(),\n\n                    durable_offset: durable_tx,\n                    queue_depth: queue_depth.clone(),\n\n                    batch_capacity: opts.batch_capacity,\n\n                    lock,\n                }\n                .run(txdata_rx, shutdown_rx),\n            )\n            .abort_handle();\n\n        Ok(Self {\n            clog,\n            durable_offset: durable_rx,\n            queue,\n            shutdown: shutdown_tx,\n            queue_depth,\n            abort,\n        })\n    }\n\n    /// Obtain a read-only copy of the durable state that implements [History].\n    pub fn as_history(&self) -> impl History<TxData = Txdata<T>> {\n        self.clog.clone()\n    }\n}\n\nimpl<T: Send + Sync + 'static> Local<T> {\n    /// Inspect how many transactions added via [`Self::append_tx`] are pending\n    /// to be applied to the underlying [`Commitlog`].\n    pub fn queue_depth(&self) -> u64 {\n        self.queue_depth.load(Relaxed)\n    }\n\n    /// Obtain an iterator over the [`Commit`]s in the underlying log.\n    pub fn commits_from(&self, offset: TxOffset) -> impl Iterator<Item = Result<Commit, error::Traversal>> {\n        self.clog.commits_from(offset).map_ok(Commit::from)\n    }\n\n    /// Get a list of segment offsets, sorted in ascending order.\n    pub fn existing_segment_offsets(&self) -> io::Result<Vec<TxOffset>> {\n        self.clog.existing_segment_offsets()\n    }\n\n    /// Compress the segments at the offsets provided, marking them as immutable.\n    pub fn compress_segments(&self, offsets: &[TxOffset]) -> io::Result<()> {\n        self.clog.compress_segments(offsets)\n    }\n\n    /// Get the size on disk of the underlying [`Commitlog`].\n    pub fn size_on_disk(&self) -> io::Result<SizeOnDisk> {\n        self.clog.size_on_disk()\n    }\n}\n\nstruct Actor<T> {\n    clog: Arc<Commitlog<Txdata<T>>>,\n\n    durable_offset: watch::Sender<Option<TxOffset>>,\n    queue_depth: Arc<AtomicU64>,\n\n    batch_capacity: NonZeroUsize,\n\n    #[allow(unused)]\n    lock: Lock,\n}\n\nimpl<T: Encode + Send + Sync + 'static> Actor<T> {\n    #[instrument(name = \"durability::local::actor\", skip_all)]\n    async fn run(\n        self,\n        mut transactions_rx: mpsc::UnboundedReceiver<Transaction<Txdata<T>>>,\n        mut shutdown_rx: mpsc::Receiver<oneshot::Sender<OwnedNotified>>,\n    ) {\n        info!(\"starting durability actor\");\n\n        let mut tx_buf = Vec::with_capacity(self.batch_capacity.get());\n        // `flush_and_sync` when the loop exits without panicking,\n        // or `flush_and_sync` inside the loop failed.\n        let mut sync_on_exit = true;\n\n        loop {\n            tokio::select! {\n                // Biased towards the shutdown channel,\n                // so that we stop accepting new data promptly after\n                // `Durability::close` was called.\n                biased;\n\n                Some(reply) = shutdown_rx.recv() => {\n                    transactions_rx.close();\n                    let _ = reply.send(self.lock.notified());\n                },\n\n                // Pop as many elements from the channel as possible,\n                // potentially requiring the `tx_buf` to allocate additional\n                // capacity.\n                // We'll reclaim capacity in excess of `self.batch_size` below.\n                n = transactions_rx.recv_many(&mut tx_buf, usize::MAX) => {\n                    if n == 0 {\n                        break;\n                    }\n                    self.queue_depth.fetch_sub(n as u64, Relaxed);\n                    let clog = self.clog.clone();\n                    tx_buf = spawn_blocking(move || -> io::Result<Vec<Transaction<Txdata<T>>>> {\n                        for tx in tx_buf.drain(..) {\n                            clog.commit([tx])?;\n                        }\n                        Ok(tx_buf)\n                    })\n                    .await\n                    .expect(\"commitlog write panicked\")\n                    .expect(\"commitlog write failed\");\n                    if self.flush_and_sync().await.is_err() {\n                        sync_on_exit = false;\n                        break;\n                    }\n                    // Reclaim burst capacity.\n                    tx_buf.shrink_to(self.batch_capacity.get());\n                },\n            }\n        }\n\n        if sync_on_exit {\n            let _ = self.flush_and_sync().await;\n        }\n\n        info!(\"exiting durability actor\");\n    }\n\n    #[instrument(skip_all)]\n    async fn flush_and_sync(&self) -> io::Result<Option<TxOffset>> {\n        // Skip if nothing changed.\n        if let Some((committed, durable)) = self.clog.max_committed_offset().zip(*self.durable_offset.borrow()) {\n            if committed == durable {\n                return Ok(None);\n            }\n        }\n\n        let clog = self.clog.clone();\n        let span = Span::current();\n        spawn_blocking(move || {\n            let _span = span.enter();\n            clog.flush_and_sync()\n        })\n        .await\n        .expect(\"commitlog flush-and-sync blocking task panicked\")\n        .inspect_err(|e| warn!(\"error flushing commitlog: {e:#}\"))\n        .inspect(|maybe_offset| {\n            if let Some(new_offset) = maybe_offset {\n                trace!(\"synced to offset {new_offset}\");\n                self.durable_offset.send_modify(|val| {\n                    val.replace(*new_offset);\n                });\n            }\n        })\n    }\n}\n\nstruct Lock {\n    file: Option<LockedFile>,\n    notify_on_drop: Arc<Notify>,\n}\n\nimpl Lock {\n    pub fn create(path: PathBuf) -> Result<Self, LockError> {\n        let file = LockedFile::lock(path).map(Some)?;\n        let notify_on_drop = Arc::new(Notify::new());\n\n        Ok(Self { file, notify_on_drop })\n    }\n\n    pub fn notified(&self) -> OwnedNotified {\n        self.notify_on_drop.clone().notified_owned()\n    }\n}\n\nimpl Drop for Lock {\n    fn drop(&mut self) {\n        // Ensure the file lock is dropped before notifying.\n        if let Some(file) = self.file.take() {\n            drop(file);\n        }\n        self.notify_on_drop.notify_waiters();\n    }\n}\n\nimpl<T: Send + Sync + 'static> Durability for Local<T> {\n    type TxData = Txdata<T>;\n\n    fn append_tx(&self, tx: Transaction<Self::TxData>) {\n        self.queue.send(tx).expect(\"durability actor crashed\");\n        self.queue_depth.fetch_add(1, Relaxed);\n    }\n\n    fn durable_tx_offset(&self) -> DurableOffset {\n        self.durable_offset.clone().into()\n    }\n\n    fn close(&self) -> Close {\n        info!(\"close local durability\");\n\n        let durable_offset = self.durable_tx_offset();\n        let shutdown = self.shutdown.clone();\n        // Abort actor if shutdown future is dropped.\n        let abort = scopeguard::guard(self.abort.clone(), |actor| {\n            warn!(\"close future dropped, aborting durability actor\");\n            actor.abort();\n        });\n\n        async move {\n            let (done_tx, done_rx) = oneshot::channel();\n            // Ignore channel errors - those just mean the actor is already gone.\n            let _ = shutdown\n                .send(done_tx)\n                .map_err(drop)\n                .and_then(|()| done_rx.map_err(drop))\n                .and_then(|done| async move {\n                    done.await;\n                    Ok(())\n                })\n                .await;\n            // Don't abort if we completed normally.\n            let _ = ScopeGuard::into_inner(abort);\n\n            durable_offset.last_seen()\n        }\n        .boxed()\n    }\n}\n\nimpl<T: Encode + 'static> History for Commitlog<Txdata<T>> {\n    type TxData = Txdata<T>;\n\n    fn fold_transactions_from<D>(&self, offset: TxOffset, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        self.fold_transactions_from(offset, decoder)\n    }\n\n    fn transactions_from<'a, D>(\n        &self,\n        offset: TxOffset,\n        decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<Self::TxData>, D::Error>>\n    where\n        D: Decoder<Record = Self::TxData>,\n        D::Error: From<error::Traversal>,\n        Self::TxData: 'a,\n    {\n        self.transactions_from(offset, decoder)\n    }\n\n    fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n        let min = self.min_committed_offset().unwrap_or_default();\n        let max = self.max_committed_offset();\n\n        (min, max)\n    }\n}\n"
  },
  {
    "path": "crates/durability/src/imp/mod.rs",
    "content": "pub mod local;\npub use local::Local;\n\n#[cfg(any(test, feature = \"test\"))]\npub use testing::NoDurability;\n\n#[cfg(any(test, feature = \"test\"))]\nmod testing {\n    use std::{\n        future,\n        marker::PhantomData,\n        sync::atomic::{AtomicBool, Ordering},\n    };\n\n    use futures::FutureExt as _;\n    use tokio::sync::watch;\n\n    use crate::{Close, Durability, DurableOffset, Transaction, TxOffset};\n\n    /// A [`Durability`] impl that sends all transactions into the void.\n    ///\n    /// This should only be used for testing, and is thus only available when\n    /// the `test` feature is enabled.\n    pub struct NoDurability<T> {\n        durable_offset: watch::Sender<Option<TxOffset>>,\n        closed: AtomicBool,\n        _txdata: PhantomData<T>,\n    }\n\n    impl<T> Default for NoDurability<T> {\n        fn default() -> Self {\n            let (durable_offset, _) = watch::channel(None);\n            Self {\n                durable_offset,\n                closed: AtomicBool::new(false),\n                _txdata: PhantomData,\n            }\n        }\n    }\n\n    impl<T: Send + Sync> Durability for NoDurability<T> {\n        type TxData = T;\n\n        fn append_tx(&self, _: Transaction<Self::TxData>) {\n            if self.closed.load(Ordering::Relaxed) {\n                panic!(\"`close` was called on this `NoDurability` instance\");\n            }\n        }\n\n        fn durable_tx_offset(&self) -> DurableOffset {\n            self.durable_offset.subscribe().into()\n        }\n\n        fn close(&self) -> Close {\n            self.closed.store(true, Ordering::Relaxed);\n            future::ready(*self.durable_offset.borrow()).boxed()\n        }\n    }\n}\n"
  },
  {
    "path": "crates/durability/src/lib.rs",
    "content": "use std::{iter, marker::PhantomData, sync::Arc};\n\nuse futures::future::BoxFuture;\nuse thiserror::Error;\nuse tokio::sync::watch;\n\npub use spacetimedb_commitlog::{error, payload::Txdata, Decoder, Transaction};\n\nmod imp;\npub use imp::*;\n\n/// Transaction offset.\n///\n/// The transaction offset is essentially a monotonic counter of all\n/// transactions submitted to the durability layer, starting from zero.\n///\n/// While the implementation may not guarantee that the sequence contains no\n/// gaps, it must guarantee that a higher transaction offset implies durability\n/// of all offsets smaller than it.\npub type TxOffset = u64;\n\n#[derive(Debug, Error)]\n#[error(\"the database's durability layer went away\")]\npub struct DurabilityExited;\n\n/// Handle to the durable offset, obtained via [`Durability::durable_tx_offset`].\n///\n/// The handle can be used to read the current durable offset, or wait for a\n/// provided offset to be reached.\n///\n/// The handle is valid for as long as the [`Durability`] instance it was\n/// obtained from is live, i.e. able to persist transactions. When the instance\n/// shuts down or crashes, methods will return errors of type [`DurabilityExited`].\n#[derive(Clone)]\npub struct DurableOffset {\n    // TODO: `watch::Receiver::wait_for` will hold a shared lock until all\n    // subscribers have seen the current value. Although it may skip entries,\n    // this may cause unacceptable contention. We may consider a custom watch\n    // channel that operates on an `AtomicU64` instead of an `RwLock`.\n    inner: watch::Receiver<Option<TxOffset>>,\n}\n\nimpl DurableOffset {\n    /// Get the current durable offset, or `None` if no transaction has been\n    /// made durable yet.\n    ///\n    /// Returns `Err` if the associated durablity is no longer live.\n    pub fn get(&self) -> Result<Option<TxOffset>, DurabilityExited> {\n        self.guard_closed().map(|()| self.inner.borrow().as_ref().copied())\n    }\n\n    /// Get the current durable offset, even if the associated durability is\n    /// no longer live.\n    pub fn last_seen(&self) -> Option<TxOffset> {\n        self.inner.borrow().as_ref().copied()\n    }\n\n    /// Wait for `offset` to become durable, i.e.\n    ///\n    /// ```ignore\n    ///     self.get().unwrap().is_some_and(|durable| durable >= offset)\n    /// ```\n    ///\n    /// Returns the actual durable offset at which above condition evaluated to\n    /// `true`, or an `Err` if the durability is no longer live.\n    ///\n    /// Returns immediately if the condition evaluates to `true` for the current\n    /// durable offset.\n    pub async fn wait_for(&mut self, offset: TxOffset) -> Result<TxOffset, DurabilityExited> {\n        self.inner\n            .wait_for(|durable| durable.is_some_and(|val| val >= offset))\n            .await\n            .map(|r| r.as_ref().copied().unwrap())\n            .map_err(|_| DurabilityExited)\n    }\n\n    fn guard_closed(&self) -> Result<(), DurabilityExited> {\n        self.inner.has_changed().map(drop).map_err(|_| DurabilityExited)\n    }\n}\n\nimpl From<watch::Receiver<Option<TxOffset>>> for DurableOffset {\n    fn from(inner: watch::Receiver<Option<TxOffset>>) -> Self {\n        Self { inner }\n    }\n}\n\n/// Future created by [Durability::close].\n///\n/// This is a boxed future rather than an associated type, so that [Durability]\n/// can be used as a trait object without knowing the type of the `close` future.\npub type Close = BoxFuture<'static, Option<TxOffset>>;\n\n/// The durability API.\n///\n/// NOTE: This is a preliminary definition, still under consideration.\n///\n/// A durability implementation accepts a [Transaction] to be made durable via\n/// the [Durability::append_tx] method in a non-blocking fashion.\n///\n/// Once a transaction becomes durable, the [DurableOffset] is updated.\n/// What durable means depends on the implementation, informally it can be\n/// thought of as \"written to disk\".\npub trait Durability: Send + Sync {\n    /// The payload representing a single transaction.\n    type TxData;\n\n    /// Submit a [Transaction] to be made durable.\n    ///\n    /// This method must never block, and accept new transactions even if they\n    /// cannot be made durable immediately.\n    ///\n    /// Errors may be signalled by panicking.\n    //\n    // TODO: Support batches of txs, i.e. commits.\n    //\n    // The commitlog supports this, but allocation overhead in the durability\n    // API is too high given we don't make any use of it.\n    //\n    // We don't make any use of it because a commit is an atomic unit of storage\n    // (i.e. a torn write will corrupt all transactions contained in it), and it\n    // is very unclear when it is both correct and beneficial to bundle more\n    // than a single transaction into a commit.\n    fn append_tx(&self, tx: Transaction<Self::TxData>);\n\n    /// Obtain a handle to the [DurableOffset].\n    fn durable_tx_offset(&self) -> DurableOffset;\n\n    /// Asynchronously request the durability to shut down, without dropping it.\n    ///\n    /// Shall close any internal channels, such that it is no longer possible to\n    /// append new data (i.e. [Durability::append_tx] shall panic).\n    /// Then, drains the internal queues and attempts to make the remaining data\n    /// durable. Resolves to the durable [TxOffset].\n    ///\n    /// When the returned future resolves, calls to [Durability::append_tx] must\n    /// panic, and calling [DurableOffset::last_seen] must return the same value\n    /// as the future's output.\n    ///\n    /// Repeatedly calling `close` on an already closed [Durability] shall\n    /// return the same [TxOffset].\n    ///\n    /// Note that errors are not propagated, as the [Durability] may already be\n    /// closed.\n    ///\n    /// # Cancellation\n    ///\n    /// Dropping the [Close] future shall abort the shutdown process,\n    /// and leave the [Durability] in a closed state.\n    fn close(&self) -> Close;\n}\n\n/// Access to the durable history.\n///\n/// The durable history is the sequence of transactions in the order\n/// [`Durability::append_tx`] was called.\n///\n/// Some [`Durability`] implementations will be able to also implement this\n/// trait, but others may not. A database may also use a [`Durability`]\n/// implementation to persist transactions, but a separate [`History`]\n/// implementation to obtain the history.\npub trait History {\n    type TxData;\n\n    /// Traverse the history of transactions from `offset` and \"fold\" it into\n    /// the provided [`Decoder`].\n    fn fold_transactions_from<D>(&self, offset: TxOffset, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>;\n\n    /// Obtain an iterator over the history of transactions, starting from `offset`.\n    fn transactions_from<'a, D>(\n        &self,\n        offset: TxOffset,\n        decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<Self::TxData>, D::Error>>\n    where\n        D: Decoder<Record = Self::TxData>,\n        D::Error: From<error::Traversal>,\n        Self::TxData: 'a;\n\n    /// Get the maximum transaction offset contained in this history.\n    ///\n    /// Similar to [`std::iter::Iterator::size_hint`], this is considered an\n    /// estimation: the upper bound may not be known, or it may change after\n    /// this method was called because more data was added to the log.\n    ///\n    /// Callers should thus only rely on it for informational purposes.\n    ///\n    /// The default implementation returns `(0, None)`, which is correct for any\n    /// history implementation.\n    fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n        (0, None)\n    }\n}\n\nimpl<T: History> History for Arc<T> {\n    type TxData = T::TxData;\n\n    fn fold_transactions_from<D>(&self, offset: TxOffset, decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        (**self).fold_transactions_from(offset, decoder)\n    }\n\n    fn transactions_from<'a, D>(\n        &self,\n        offset: TxOffset,\n        decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<Self::TxData>, D::Error>>\n    where\n        D: Decoder<Record = Self::TxData>,\n        D::Error: From<error::Traversal>,\n        Self::TxData: 'a,\n    {\n        (**self).transactions_from(offset, decoder)\n    }\n\n    fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n        (**self).tx_range_hint()\n    }\n}\n\n#[derive(Default)]\npub struct EmptyHistory<T> {\n    _txdata: PhantomData<T>,\n}\n\nimpl<T> EmptyHistory<T> {\n    pub const fn new() -> Self {\n        Self { _txdata: PhantomData }\n    }\n}\n\nimpl<T> History for EmptyHistory<T> {\n    type TxData = T;\n\n    fn fold_transactions_from<D>(&self, _offset: TxOffset, _decoder: D) -> Result<(), D::Error>\n    where\n        D: Decoder,\n        D::Error: From<error::Traversal>,\n    {\n        Ok(())\n    }\n\n    fn transactions_from<'a, D>(\n        &self,\n        _offset: TxOffset,\n        _decoder: &'a D,\n    ) -> impl Iterator<Item = Result<Transaction<Self::TxData>, D::Error>>\n    where\n        D: Decoder<Record = Self::TxData>,\n        D::Error: From<error::Traversal>,\n        Self::TxData: 'a,\n    {\n        iter::empty()\n    }\n\n    fn tx_range_hint(&self) -> (TxOffset, Option<TxOffset>) {\n        (0, Some(0))\n    }\n}\n"
  },
  {
    "path": "crates/durability/tests/io/fallocate.rs",
    "content": "//! Demonstrates the crash behaviour of `spacetimedb_durability::Local`\n//! if the `fallocate` feature is enabled and when there is not enough disk\n//! space to pre-allocate commitlog segments.\n//!\n//! Requires `target_os = \"linux\"`.\n//!\n//! The setup involves mounting a file as a loop device. For this, it invokes\n//! the `mount`, `umount` and `chmod` commands via `sudo`. The caller must\n//! ensure that they have the appropriate entries in `sudoers(5)` to do that\n//! without `sudo` prompting for a password. For example:\n//!\n//! ```ignore\n//! %sudo   ALL=(ALL)   NOPASSWD:    /usr/bin/mount, /usr/bin/umount, /usr/bin/chmod\n//! ```\n//!\n//! The `fallocate` feature is not enabled by default. To run, use:\n//!\n//! ```ignore\n//! cargo test --features fallocate\n//! ```\nuse std::{\n    fs::File,\n    io,\n    path::{Path, PathBuf},\n    process,\n    sync::Arc,\n    time::Duration,\n};\n\nuse anyhow::{anyhow, Context as _};\nuse log::{error, info};\nuse scopeguard::ScopeGuard;\nuse spacetimedb_commitlog::{\n    payload::txdata::{Mutations, Ops},\n    repo::{self, OnNewSegmentFn, Repo},\n    segment,\n    tests::helpers::enable_logging,\n};\nuse spacetimedb_durability::{local::OpenError, Durability, Txdata};\nuse spacetimedb_paths::{server::ReplicaDir, FromPathUnchecked};\nuse tempfile::{NamedTempFile, TempDir};\nuse tokio::{sync::watch, time::sleep};\n\nconst MB: u64 = 1024 * 1024;\n\n#[tokio::test]\nasync fn local_durability_cannot_be_created_if_not_enough_space() -> anyhow::Result<()> {\n    enable_logging();\n\n    let Tmp {\n        device_file,\n        mountpoint,\n    } = Tmp::create()?;\n    {\n        let file_path = device_file.path();\n        let mountpoint = mountpoint.path();\n\n        let _guard = mount(file_path, mountpoint, 512 * MB)?;\n        let replica_dir = ReplicaDir::from_path_unchecked(mountpoint);\n\n        match local_durability(replica_dir, 1024 * MB, None).await {\n            Err(e) if is_no_space_error(&e) => Ok(()),\n            Err(e) => Err(e).context(\"unexpected error\"),\n            Ok(durability) => {\n                durability.close().await;\n                Err(anyhow!(\"unexpected success\"))\n            }\n        }\n    }\n}\n\n// NOTE: This test is set up to proceed more or less sequentially.\n// In reality, `append_tx` will fail at some point in the future.\n// I.e. transactions can be lost when the host runs out of disk space.\n#[tokio::test]\n#[should_panic = \"durability actor crashed\"]\nasync fn local_durability_crashes_on_new_segment_if_not_enough_space() {\n    enable_logging();\n\n    // Inner run fn to allow the use of `?`,\n    // `should_panic` tests must return unit.\n    async fn run() -> anyhow::Result<()> {\n        let Tmp {\n            device_file,\n            mountpoint,\n        } = Tmp::create()?;\n        {\n            let _guard = mount(device_file.path(), mountpoint.path(), 512 * MB)?;\n            let replica_dir = ReplicaDir::from_path_unchecked(mountpoint.path());\n\n            let (new_segment_tx, mut new_segment_rx) = watch::channel(());\n            let on_new_segment = Arc::new(move || {\n                new_segment_tx.send_replace(());\n            });\n            let durability = local_durability(replica_dir, 256 * MB, Some(on_new_segment)).await?;\n            let txdata = txdata();\n\n            // Mark initial segment as seen.\n            new_segment_rx.borrow_and_update();\n            // Write past available space.\n            for offset in 0..256 {\n                durability.append_tx((offset, txdata.clone()).into());\n            }\n            // Ensure new segment is created.\n            new_segment_rx.changed().await?;\n            // Yield to give fallocate a chance to run (and fail).\n            sleep(Duration::from_millis(5)).await;\n            // Durability actor should have crashed, so this should panic.\n            info!(\"trying append on crashed durability\");\n            durability.append_tx((256, txdata.clone()).into());\n        }\n\n        Ok(())\n    }\n\n    run().await.unwrap()\n}\n\n/// Approximates the case where a commitlog has segments that were created\n/// without `fallocate`.\n///\n/// Resuming a segment when there is insufficient space should fail.\n#[tokio::test]\nasync fn local_durability_crashes_on_resume_with_insuffient_space() -> anyhow::Result<()> {\n    enable_logging();\n\n    let Tmp {\n        device_file,\n        mountpoint,\n    } = Tmp::create()?;\n    {\n        let _guard = mount(device_file.path(), mountpoint.path(), 512 * MB)?;\n        let replica_dir = ReplicaDir::from_path_unchecked(mountpoint.path());\n\n        // Write a segment with only a header and no `fallocate` reservation.\n        {\n            let repo = repo::Fs::new(replica_dir.commit_log(), None)?;\n            let mut segment = repo.create_segment(0)?;\n            segment::Header::default().write(&mut segment)?;\n            segment.sync_data()?;\n        }\n\n        // Try to open local durability with a 1GiB segment size,\n        // which is larger than the available disk space.\n        match local_durability(replica_dir, 1024 * MB, None).await {\n            Err(e) if is_no_space_error(&e) => Ok(()),\n            Err(e) => Err(e).context(\"unexpected error\"),\n            Ok(durability) => {\n                durability.close().await;\n                Err(anyhow!(\"unexpected success\"))\n            }\n        }\n    }\n}\n\nfn is_no_space_error(e: &OpenError) -> bool {\n    matches!(e, OpenError::Commitlog(io) if io.kind() == io::ErrorKind::StorageFull)\n}\n\nasync fn local_durability(\n    dir: ReplicaDir,\n    max_segment_size: u64,\n    on_new_segment: Option<Arc<OnNewSegmentFn>>,\n) -> Result<spacetimedb_durability::Local<[u8; 1024 * 1024]>, spacetimedb_durability::local::OpenError> {\n    spacetimedb_durability::Local::open(\n        dir,\n        tokio::runtime::Handle::current(),\n        spacetimedb_durability::local::Options {\n            commitlog: spacetimedb_commitlog::Options {\n                max_segment_size,\n                preallocate_segments: true,\n                ..<_>::default()\n            },\n            ..<_>::default()\n        },\n        on_new_segment,\n    )\n}\n\nfn txdata() -> Txdata<[u8; 1024 * 1024]> {\n    Txdata {\n        inputs: None,\n        outputs: None,\n        mutations: Some(Mutations {\n            inserts: [Ops {\n                table_id: 8000.into(),\n                rowdata: Arc::new([[42u8; 1024 * 1024]]),\n            }]\n            .into(),\n            deletes: [].into(),\n            truncates: [].into(),\n        }),\n    }\n}\n\nstruct Tmp {\n    device_file: NamedTempFile,\n    mountpoint: TempDir,\n}\n\nimpl Tmp {\n    fn create() -> io::Result<Self> {\n        let device_file = tempfile::Builder::new().prefix(\"disk-\").tempfile()?;\n        let mountpoint = tempfile::Builder::new().prefix(\"mnt-\").tempdir()?;\n\n        Ok(Self {\n            device_file,\n            mountpoint,\n        })\n    }\n}\n\nfn mount(device_file: &Path, mountpoint: &Path, len: u64) -> anyhow::Result<ScopeGuard<PathBuf, impl FnOnce(PathBuf)>> {\n    info!(\"creating empty file at {} with len {}\", device_file.display(), len);\n    {\n        let file = File::options()\n            .create(true)\n            .write(true)\n            .truncate(true)\n            .open(device_file)?;\n        file.set_len(len)?;\n        file.sync_data()?;\n    }\n\n    info!(\"creating filesystem\");\n    process::Command::new(\"mkfs\")\n        .args([\"-t\", \"ext4\"])\n        .arg(device_file)\n        .status()\n        .success()?;\n\n    info!(\"mounting {} at {}\", device_file.display(), mountpoint.display());\n    sudo(|cmd| {\n        cmd.args([\"mount\", \"-t\", \"ext4\", \"-o\", \"loop\"])\n            .arg(device_file)\n            .arg(mountpoint)\n            .status()\n    })\n    .success()?;\n\n    let guard = scopeguard::guard(mountpoint.to_path_buf(), |mountpoint| {\n        if let Err(e) = umount(&mountpoint) {\n            error!(\"failed to umount {}: {}\", mountpoint.display(), e)\n        }\n    });\n\n    sudo(|cmd| cmd.args([\"chmod\", \"-R\", \"777\"]).arg(mountpoint).status()).success()?;\n\n    Ok(guard)\n}\n\nfn umount(mountpoint: &Path) -> io::Result<()> {\n    sudo(|cmd| cmd.arg(\"umount\").arg(mountpoint).status()).success()\n}\n\nfn sudo<T>(f: impl FnOnce(&mut process::Command) -> T) -> T {\n    f(process::Command::new(\"sudo\").arg(\"--non-interactive\"))\n}\n\ntrait ExitStatusExt {\n    fn success(self) -> io::Result<()>;\n}\n\nimpl ExitStatusExt for io::Result<process::ExitStatus> {\n    fn success(self) -> io::Result<()> {\n        let status = self?;\n        match status.success() {\n            true => Ok(()),\n            false => Err(io::Error::from_raw_os_error(status.code().unwrap())),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/durability/tests/io/mod.rs",
    "content": "#[cfg(all(target_os = \"linux\", feature = \"fallocate\"))]\nmod fallocate;\n"
  },
  {
    "path": "crates/durability/tests/main.rs",
    "content": "mod io;\n"
  },
  {
    "path": "crates/execution/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-execution\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The SpacetimeDB query engine\"\n\n[dependencies]\nanyhow.workspace = true\nitertools.workspace = true\nspacetimedb-expr.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-sats.workspace = true\nspacetimedb-physical-plan.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-sql-parser.workspace = true\nspacetimedb-table.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/execution/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/execution/src/dml.rs",
    "content": "use anyhow::Result;\nuse spacetimedb_lib::{metrics::ExecutionMetrics, AlgebraicValue, ProductValue};\nuse spacetimedb_physical_plan::dml::{DeletePlan, InsertPlan, MutationPlan, UpdatePlan};\nuse spacetimedb_primitives::{ColId, TableId};\nuse spacetimedb_sats::size_of::SizeOf;\n\nuse crate::{pipelined::PipelinedProject, Datastore, DeltaStore};\n\n/// A mutable datastore can read as well as insert and delete rows\npub trait MutDatastore: Datastore + DeltaStore {\n    fn insert_product_value(&mut self, table_id: TableId, row: &ProductValue) -> Result<bool>;\n    fn delete_product_value(&mut self, table_id: TableId, row: &ProductValue) -> Result<bool>;\n}\n\n/// Executes a physical mutation plan\npub enum MutExecutor {\n    Insert(InsertExecutor),\n    Delete(DeleteExecutor),\n    Update(UpdateExecutor),\n}\n\nimpl From<MutationPlan> for MutExecutor {\n    fn from(plan: MutationPlan) -> Self {\n        match plan {\n            MutationPlan::Insert(plan) => Self::Insert(plan.into()),\n            MutationPlan::Delete(plan) => Self::Delete(plan.into()),\n            MutationPlan::Update(plan) => Self::Update(plan.into()),\n        }\n    }\n}\n\nimpl MutExecutor {\n    pub fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {\n        match self {\n            Self::Insert(exec) => exec.execute(tx, metrics),\n            Self::Delete(exec) => exec.execute(tx, metrics),\n            Self::Update(exec) => exec.execute(tx, metrics),\n        }\n    }\n}\n\n/// Executes row insertions\npub struct InsertExecutor {\n    table_id: TableId,\n    rows: Vec<ProductValue>,\n}\n\nimpl From<InsertPlan> for InsertExecutor {\n    fn from(plan: InsertPlan) -> Self {\n        Self {\n            rows: plan.rows,\n            table_id: plan.table.table_id,\n        }\n    }\n}\n\nimpl InsertExecutor {\n    fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {\n        for row in &self.rows {\n            if tx.insert_product_value(self.table_id, row)? {\n                metrics.rows_inserted += 1;\n            }\n        }\n        // TODO: It would be better to get this metric from the bsatn buffer.\n        // But we haven't been concerned with optimizing DML up to this point.\n        metrics.bytes_written += self.rows.iter().map(|row| row.size_of()).sum::<usize>();\n        Ok(())\n    }\n}\n\n/// Executes row deletions\npub struct DeleteExecutor {\n    table_id: TableId,\n    filter: PipelinedProject,\n}\n\nimpl From<DeletePlan> for DeleteExecutor {\n    fn from(plan: DeletePlan) -> Self {\n        Self {\n            table_id: plan.table.table_id,\n            filter: plan.filter.into(),\n        }\n    }\n}\n\nimpl DeleteExecutor {\n    fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {\n        // TODO: Delete by row id instead of product value\n        let mut deletes = vec![];\n        self.filter.execute(tx, metrics, &mut |row| {\n            deletes.push(row.to_product_value());\n            Ok(())\n        })?;\n        // TODO: This metric should be updated inline when we serialize.\n        // Note, that we don't update bytes written,\n        // because deletes don't actually write out any bytes.\n        metrics.bytes_scanned += deletes.iter().map(|row| row.size_of()).sum::<usize>();\n        for row in &deletes {\n            if tx.delete_product_value(self.table_id, row)? {\n                metrics.rows_deleted += 1;\n            }\n        }\n        Ok(())\n    }\n}\n\n/// Executes row updates\npub struct UpdateExecutor {\n    table_id: TableId,\n    columns: Vec<(ColId, AlgebraicValue)>,\n    filter: PipelinedProject,\n}\n\nimpl From<UpdatePlan> for UpdateExecutor {\n    fn from(plan: UpdatePlan) -> Self {\n        Self {\n            columns: plan.columns,\n            table_id: plan.table.table_id,\n            filter: plan.filter.into(),\n        }\n    }\n}\n\nimpl UpdateExecutor {\n    fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {\n        let mut deletes = vec![];\n        self.filter.execute(tx, metrics, &mut |row| {\n            deletes.push(row.to_product_value());\n            Ok(())\n        })?;\n        for row in &deletes {\n            tx.delete_product_value(self.table_id, row)?;\n        }\n        // TODO: This metric should be updated inline when we serialize.\n        metrics.bytes_scanned = deletes.iter().map(|row| row.size_of()).sum::<usize>();\n        metrics.rows_updated += deletes.len() as u64;\n        for row in &deletes {\n            let row = ProductValue::from_iter(\n                row\n                    // Update the deleted rows with the new field values\n                    .into_iter()\n                    .cloned()\n                    .enumerate()\n                    .map(|(i, elem)| {\n                        self.columns\n                            .iter()\n                            .find(|(col_id, _)| i == col_id.idx())\n                            .map(|(_, value)| value.clone())\n                            .unwrap_or_else(|| elem)\n                    }),\n            );\n            tx.insert_product_value(self.table_id, &row)?;\n            metrics.bytes_written += row.size_of();\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/execution/src/lib.rs",
    "content": "use anyhow::Result;\nuse core::hash::{Hash, Hasher};\nuse core::ops::RangeBounds;\nuse spacetimedb_lib::query::Delta;\nuse spacetimedb_physical_plan::plan::{ProjectField, TupleField};\nuse spacetimedb_primitives::{ColList, IndexId, TableId};\nuse spacetimedb_sats::bsatn::{BufReservedFill, EncodeError, ToBsatn};\nuse spacetimedb_sats::buffer::BufWriter;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_sats::{impl_serialize, AlgebraicValue, ProductValue};\nuse spacetimedb_table::{static_assert_size, table::RowRef};\n\npub mod dml;\npub mod pipelined;\n\npub trait Datastore {\n    /// Iterator type for table scans\n    type TableIter<'a>: Iterator<Item = RowRef<'a>> + 'a\n    where\n        Self: 'a;\n\n    /// Iterator type for ranged index scans.\n    type RangeIndexIter<'a>: Iterator<Item = RowRef<'a>> + 'a\n    where\n        Self: 'a;\n\n    /// Iterator type for point index scans.\n    type PointIndexIter<'a>: Iterator<Item = RowRef<'a>> + 'a\n    where\n        Self: 'a;\n\n    /// Returns the number of rows in this table\n    fn row_count(&self, table_id: TableId) -> u64;\n\n    /// Scans and returns all of the rows in a table\n    fn table_scan<'a>(&'a self, table_id: TableId) -> Result<Self::TableIter<'a>>;\n\n    /// Scans a range of keys from an index returning a [`RowRef`] iterator.\n    fn index_scan_range<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> Result<Self::RangeIndexIter<'a>>;\n\n    /// Scans a key from an index returning a [`RowRef`] iterator.\n    fn index_scan_point<'a>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        point: &AlgebraicValue,\n    ) -> Result<Self::PointIndexIter<'a>>;\n}\n\npub trait DeltaStore {\n    fn num_inserts(&self, table_id: TableId) -> usize;\n    fn num_deletes(&self, table_id: TableId) -> usize;\n\n    fn has_inserts(&self, table_id: TableId) -> bool {\n        self.num_inserts(table_id) != 0\n    }\n\n    fn has_deletes(&self, table_id: TableId) -> bool {\n        self.num_deletes(table_id) != 0\n    }\n\n    fn inserts_for_table(&self, table_id: TableId) -> Option<std::slice::Iter<'_, ProductValue>>;\n    fn deletes_for_table(&self, table_id: TableId) -> Option<std::slice::Iter<'_, ProductValue>>;\n\n    fn index_scan_range_for_delta(\n        &self,\n        table_id: TableId,\n        index_id: IndexId,\n        delta: Delta,\n        range: impl RangeBounds<AlgebraicValue>,\n    ) -> impl Iterator<Item = Row<'_>>;\n\n    fn index_scan_point_for_delta(\n        &self,\n        table_id: TableId,\n        index_id: IndexId,\n        delta: Delta,\n        point: &AlgebraicValue,\n    ) -> impl Iterator<Item = Row<'_>>;\n\n    fn delta_scan(&self, table_id: TableId, inserts: bool) -> DeltaScanIter<'_> {\n        match inserts {\n            true => DeltaScanIter {\n                iter: self.inserts_for_table(table_id),\n            },\n            false => DeltaScanIter {\n                iter: self.deletes_for_table(table_id),\n            },\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum Row<'a> {\n    Ptr(RowRef<'a>),\n    Ref(&'a ProductValue),\n}\n\nimpl PartialEq for Row<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        match (self, other) {\n            (Self::Ptr(x), Self::Ptr(y)) => x == y,\n            (Self::Ref(x), Self::Ref(y)) => x == y,\n            (Self::Ptr(x), Self::Ref(y)) => x == *y,\n            (Self::Ref(x), Self::Ptr(y)) => y == *x,\n        }\n    }\n}\n\nimpl Eq for Row<'_> {}\n\nimpl Hash for Row<'_> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self {\n            Self::Ptr(x) => x.hash(state),\n            Self::Ref(x) => x.hash(state),\n        }\n    }\n}\n\nimpl Row<'_> {\n    pub fn to_product_value(&self) -> ProductValue {\n        match self {\n            Self::Ptr(ptr) => ptr.to_product_value(),\n            Self::Ref(val) => (*val).clone(),\n        }\n    }\n\n    pub fn project_product(self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {\n        match self {\n            Self::Ptr(ptr) => ptr.project_product(cols),\n            Self::Ref(val) => val.project_product(cols),\n        }\n    }\n}\n\nimpl_serialize!(['a] Row<'a>, (self, ser) => match self {\n    Self::Ptr(row) => row.serialize(ser),\n    Self::Ref(row) => row.serialize(ser),\n});\n\nimpl ToBsatn for Row<'_> {\n    fn static_bsatn_size(&self) -> Option<u16> {\n        match self {\n            Self::Ptr(ptr) => ptr.static_bsatn_size(),\n            Self::Ref(val) => val.static_bsatn_size(),\n        }\n    }\n\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> std::result::Result<(), EncodeError> {\n        match self {\n            Self::Ptr(ptr) => ptr.to_bsatn_extend(buf),\n            Self::Ref(val) => val.to_bsatn_extend(buf),\n        }\n    }\n\n    fn to_bsatn_vec(&self) -> std::result::Result<Vec<u8>, EncodeError> {\n        match self {\n            Self::Ptr(ptr) => ptr.to_bsatn_vec(),\n            Self::Ref(val) => val.to_bsatn_vec(),\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum RelValue<'a> {\n    Row(Row<'a>),\n    Projection(ProductValue),\n}\n\nimpl<'a> From<Row<'a>> for RelValue<'a> {\n    fn from(value: Row<'a>) -> Self {\n        Self::Row(value)\n    }\n}\n\nimpl From<ProductValue> for RelValue<'_> {\n    fn from(value: ProductValue) -> Self {\n        Self::Projection(value)\n    }\n}\n\nimpl PartialEq for RelValue<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        match (self, other) {\n            (Self::Row(x), Self::Row(y)) => x == y,\n            (Self::Projection(x), Self::Projection(y)) => x == y,\n            (Self::Row(x), Self::Projection(y)) | (Self::Projection(y), Self::Row(x)) => x.to_product_value() == *y,\n        }\n    }\n}\n\nimpl Eq for RelValue<'_> {}\n\nimpl Hash for RelValue<'_> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self {\n            Self::Row(x) => x.hash(state),\n            Self::Projection(x) => x.hash(state),\n        }\n    }\n}\n\nimpl_serialize!(['a] RelValue<'a>, (self, ser) => match self {\n    Self::Row(row) => row.serialize(ser),\n    Self::Projection(row) => row.serialize(ser),\n});\n\nimpl ToBsatn for RelValue<'_> {\n    fn static_bsatn_size(&self) -> Option<u16> {\n        match self {\n            Self::Row(row) => row.static_bsatn_size(),\n            Self::Projection(row) => row.static_bsatn_size(),\n        }\n    }\n\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> std::result::Result<(), EncodeError> {\n        match self {\n            Self::Row(row) => row.to_bsatn_extend(buf),\n            Self::Projection(row) => row.to_bsatn_extend(buf),\n        }\n    }\n\n    fn to_bsatn_vec(&self) -> std::result::Result<Vec<u8>, EncodeError> {\n        match self {\n            Self::Row(row) => row.to_bsatn_vec(),\n            Self::Projection(row) => row.to_bsatn_vec(),\n        }\n    }\n}\n\nimpl ProjectField for Row<'_> {\n    fn project(&self, field: &TupleField) -> AlgebraicValue {\n        match self {\n            Self::Ptr(ptr) => ptr.project(field),\n            Self::Ref(val) => val.project(field),\n        }\n    }\n}\n\n/// Each query operator returns a tuple of [RowRef]s\n#[derive(Clone)]\npub enum Tuple<'a> {\n    /// A pointer to a row in a base table\n    Row(Row<'a>),\n    /// A temporary returned by a join operator\n    Join(Vec<Row<'a>>),\n}\n\nstatic_assert_size!(Tuple, 40);\n\nimpl ProjectField for Tuple<'_> {\n    fn project(&self, field: &TupleField) -> AlgebraicValue {\n        match self {\n            Self::Row(row) => row.project(field),\n            Self::Join(ptrs) => field\n                .label_pos\n                .and_then(|i| ptrs.get(i))\n                .map(|ptr| ptr.project(field))\n                .unwrap(),\n        }\n    }\n}\n\nimpl<'a> Tuple<'a> {\n    /// Select the tuple element at position `i`\n    fn select(self, i: usize) -> Option<Row<'a>> {\n        match self {\n            Self::Row(_) => None,\n            Self::Join(mut ptrs) => Some(ptrs.swap_remove(i)),\n        }\n    }\n\n    /// Append a [Row] to a tuple\n    fn append(self, ptr: Row<'a>) -> Self {\n        match self {\n            Self::Row(row) => Self::Join(vec![row, ptr]),\n            Self::Join(mut rows) => {\n                rows.push(ptr);\n                Self::Join(rows)\n            }\n        }\n    }\n\n    fn join(self, with: Self) -> Self {\n        match with {\n            Self::Row(ptr) => self.append(ptr),\n            Self::Join(ptrs) => ptrs.into_iter().fold(self, |tup, ptr| tup.append(ptr)),\n        }\n    }\n}\n\npub struct DeltaScanIter<'a> {\n    iter: Option<std::slice::Iter<'a, ProductValue>>,\n}\n\nimpl<'a> Iterator for DeltaScanIter<'a> {\n    type Item = &'a ProductValue;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.as_mut().and_then(|iter| iter.next())\n    }\n}\n"
  },
  {
    "path": "crates/execution/src/pipelined.rs",
    "content": "use std::{\n    collections::{HashMap, HashSet},\n    ops::Bound,\n};\n\nuse anyhow::Result;\nuse itertools::Either;\nuse spacetimedb_expr::expr::AggType;\nuse spacetimedb_lib::{metrics::ExecutionMetrics, query::Delta, sats::size_of::SizeOf, AlgebraicValue, ProductValue};\nuse spacetimedb_physical_plan::plan::{\n    HashJoin, IxJoin, IxScan, PhysicalExpr, PhysicalPlan, ProjectField, ProjectListPlan, ProjectPlan, Sarg, Semi,\n    TableScan, TupleField,\n};\nuse spacetimedb_primitives::{ColId, ColList, IndexId, TableId};\nuse spacetimedb_sats::product;\n\nuse crate::{Datastore, DeltaStore, Row, Tuple};\n\n/// An executor for explicit column projections.\n/// Note, this plan can only be constructed from the http api,\n/// which is not considered performance critical.\n/// Hence this operator is not particularly optimized.\npub enum ProjectListExecutor {\n    Name(Vec<PipelinedProject>),\n    View(Vec<ViewProject>),\n    List(Vec<PipelinedExecutor>, Vec<TupleField>),\n    Limit(Box<ProjectListExecutor>, u64),\n    Agg(Vec<PipelinedExecutor>, AggType),\n}\n\nimpl From<ProjectListPlan> for ProjectListExecutor {\n    fn from(plan: ProjectListPlan) -> Self {\n        /// A helper that checks if a [`ProjectListPlan`] returns an unprojected view table\n        fn returns_view_table(plans: &[ProjectPlan]) -> bool {\n            plans.first().is_some_and(|plan| plan.returns_view_table())\n        }\n\n        /// A helper that returns the number of columns returned by this [`ProjectListPlan`]\n        fn num_cols(plans: &[ProjectPlan]) -> usize {\n            plans\n                .first()\n                .and_then(|plan| plan.return_table())\n                .map(|schema| schema.num_cols())\n                .unwrap_or_default()\n        }\n\n        /// A helper that returns the number of private columns returned by this [`ProjectListPlan`]\n        fn num_private_cols(plans: &[ProjectPlan]) -> usize {\n            plans\n                .first()\n                .and_then(|plan| plan.return_table())\n                .map(|schema| schema.num_private_cols())\n                .unwrap_or_default()\n        }\n\n        match plan {\n            ProjectListPlan::Name(plans) if returns_view_table(&plans) => {\n                let num_cols = num_cols(&plans);\n                let num_private_cols = num_private_cols(&plans);\n                Self::View(\n                    plans\n                        .into_iter()\n                        .map(PipelinedProject::from)\n                        .map(|plan| ViewProject::new(plan, num_cols, num_private_cols))\n                        .collect(),\n                )\n            }\n            ProjectListPlan::Name(plan) => Self::Name(plan.into_iter().map(PipelinedProject::from).collect()),\n            ProjectListPlan::List(plan, fields) => {\n                Self::List(plan.into_iter().map(PipelinedExecutor::from).collect(), fields)\n            }\n            ProjectListPlan::Limit(plan, n) => Self::Limit(Box::new((*plan).into()), n),\n            ProjectListPlan::Agg(plan, AggType::Count) => {\n                Self::Agg(plan.into_iter().map(PipelinedExecutor::from).collect(), AggType::Count)\n            }\n        }\n    }\n}\n\nimpl ProjectListExecutor {\n    pub fn execute<Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(ProductValue) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut bytes_scanned = 0;\n        match self {\n            Self::Name(plans) => {\n                for plan in plans {\n                    plan.execute(tx, metrics, &mut |row| {\n                        n += 1;\n                        let row = row.to_product_value();\n                        bytes_scanned += row.size_of();\n                        f(row)\n                    })?;\n                }\n            }\n            Self::View(plans) => {\n                for plan in plans {\n                    plan.execute(tx, metrics, &mut |row| {\n                        n += 1;\n                        f(row)\n                    })?;\n                }\n            }\n            Self::List(plans, fields) => {\n                for plan in plans {\n                    plan.execute(tx, metrics, &mut |t| {\n                        n += 1;\n                        let row = ProductValue::from_iter(fields.iter().map(|field| t.project(field)));\n                        bytes_scanned += row.size_of();\n                        f(row)\n                    })?;\n                }\n            }\n            Self::Limit(plan, limit) => {\n                plan.execute(tx, metrics, &mut |row| {\n                    n += 1;\n                    if n <= *limit as usize {\n                        f(row)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self::Agg(plans, AggType::Count) => {\n                for plan in plans {\n                    match plan {\n                        // TODO: This is a hack that needs to be removed.\n                        // We check if this is a COUNT on a physical table,\n                        // and if so, we retrieve the count from table metadata.\n                        // It's a valid optimization but one that should be done by the optimizer.\n                        // There should be no optimizations performed during execution.\n                        PipelinedExecutor::TableScan(table_scan) => {\n                            n += tx.row_count(table_scan.table) as usize;\n                        }\n                        _ => {\n                            plan.execute(tx, metrics, &mut |_| {\n                                n += 1;\n                                Ok(())\n                            })?;\n                        }\n                    }\n                }\n                f(product![n as u64])?;\n            }\n        }\n        metrics.rows_scanned += n;\n        metrics.bytes_scanned += bytes_scanned;\n        Ok(())\n    }\n}\n\n/// An executor for a query that returns rows from a view.\n/// Essentially just a projection that drops the view's private columns.\n///\n/// Unlike user tables, view tables can have private columns.\n/// For example, if a view is not anonymous, its backing table will have a `sender` column.\n/// This column tracks which rows belong to which caller of the view.\n/// However we must remove this column before sending rows from the view to a client.\n///\n/// See `TableSchema::from_view_def_for_datastore` for more details.\n#[derive(Debug)]\npub struct ViewProject {\n    num_cols: usize,\n    num_private_cols: usize,\n    inner: PipelinedProject,\n}\n\nimpl ViewProject {\n    pub fn new(inner: PipelinedProject, num_cols: usize, num_private_cols: usize) -> Self {\n        Self {\n            inner,\n            num_cols,\n            num_private_cols,\n        }\n    }\n\n    pub fn execute<Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(ProductValue) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut bytes_scanned = 0;\n        self.inner.execute(tx, metrics, &mut |row| match row {\n            Row::Ptr(ptr) => {\n                n += 1;\n                let col_list = ColList::from_iter(self.num_private_cols..self.num_cols);\n                let row = ptr.project_product(&col_list)?;\n                bytes_scanned += row.size_of();\n                f(row)\n            }\n            Row::Ref(val) => {\n                n += 1;\n                let col_list = ColList::from_iter(self.num_private_cols..self.num_cols);\n                let row = val.project_product(&col_list)?;\n                bytes_scanned += row.size_of();\n                f(row)\n            }\n        })?;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// Implements a projection on top of a pipelined executor\n#[derive(Debug)]\npub enum PipelinedProject {\n    None(PipelinedExecutor),\n    Some(PipelinedExecutor, usize),\n}\n\nimpl From<ProjectPlan> for PipelinedProject {\n    fn from(plan: ProjectPlan) -> Self {\n        match plan {\n            ProjectPlan::None(plan) => Self::None(plan.into()),\n            ProjectPlan::Name(plan, _, None) => Self::None(plan.into()),\n            ProjectPlan::Name(plan, _, Some(i)) => Self::Some(plan.into(), i),\n        }\n    }\n}\n\nimpl PipelinedProject {\n    /// Walks and visits each executor in the tree\n    pub fn visit(&self, f: &mut impl FnMut(&PipelinedExecutor)) {\n        match self {\n            Self::Some(plan, _) | Self::None(plan) => {\n                plan.visit(f);\n            }\n        }\n    }\n\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self {\n            Self::None(plan) | Self::Some(plan, _) => plan.is_empty(tx),\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Row<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        match self {\n            Self::None(plan) => {\n                // No explicit projection.\n                // This means the input does not return tuples.\n                // It returns either row ids or product values.\n                plan.execute(tx, metrics, &mut |t| {\n                    n += 1;\n                    if let Tuple::Row(row) = t {\n                        f(row)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self::Some(plan, i) => {\n                // The contrary is true for explicit projections.\n                // They return a tuple of row ids or product values.\n                plan.execute(tx, metrics, &mut |t| {\n                    n += 1;\n                    if let Some(row) = t.select(*i) {\n                        f(row)?;\n                    }\n                    Ok(())\n                })?;\n            }\n        }\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// Executes a query plan in a streaming fashion.\n/// Avoids materializing intermediate results when possible.\n/// Note that unlike a tuple at a time iterator,\n/// the caller has no way to interrupt its forward progress.\n#[derive(Debug)]\npub enum PipelinedExecutor {\n    TableScan(PipelinedScan),\n    IxScanEq(PipelinedIxScanEq),\n    IxScanRange(PipelinedIxScanRange),\n    IxJoin(PipelinedIxJoin),\n    IxDeltaScanEq(PipelinedIxDeltaScanEq),\n    IxDeltaScanRange(PipelinedIxDeltaScanRange),\n    IxDeltaJoin(PipelinedIxDeltaJoin),\n    HashJoin(BlockingHashJoin),\n    NLJoin(BlockingNLJoin),\n    Filter(PipelinedFilter),\n    Limit(PipelinedLimit),\n}\n\nimpl From<PhysicalPlan> for PipelinedExecutor {\n    fn from(plan: PhysicalPlan) -> Self {\n        match plan {\n            PhysicalPlan::TableScan(TableScan { schema, limit, delta }, _) => Self::TableScan(PipelinedScan {\n                table: schema.table_id,\n                limit,\n                delta,\n            }),\n            PhysicalPlan::IxScan(\n                scan @ IxScan {\n                    delta: None,\n                    arg: Sarg::Eq(..),\n                    ..\n                },\n                _,\n            ) => Self::IxScanEq(scan.into()),\n            PhysicalPlan::IxScan(\n                scan @ IxScan {\n                    delta: None,\n                    arg: Sarg::Range(..),\n                    ..\n                },\n                _,\n            ) => Self::IxScanRange(scan.into()),\n            PhysicalPlan::IxScan(\n                scan @ IxScan {\n                    delta: Some(_),\n                    arg: Sarg::Eq(..),\n                    ..\n                },\n                _,\n            ) => Self::IxDeltaScanEq(scan.into()),\n            PhysicalPlan::IxScan(\n                scan @ IxScan {\n                    delta: Some(_),\n                    arg: Sarg::Range(..),\n                    ..\n                },\n                _,\n            ) => Self::IxDeltaScanRange(scan.into()),\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_index,\n                    rhs_prefix,\n                    rhs_field,\n                    unique,\n                    lhs_field,\n                    rhs_delta: None,\n                    ..\n                },\n                semijoin,\n            ) => Self::IxJoin(PipelinedIxJoin {\n                lhs: Box::new(Self::from(*lhs)),\n                rhs_table: rhs.table_id,\n                rhs_index,\n                rhs_prefix,\n                rhs_field,\n                lhs_field,\n                unique,\n                semijoin,\n            }),\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_index,\n                    rhs_prefix,\n                    rhs_field,\n                    unique,\n                    lhs_field,\n                    rhs_delta: Some(rhs_delta),\n                    ..\n                },\n                semijoin,\n            ) => Self::IxDeltaJoin(PipelinedIxDeltaJoin {\n                lhs: Box::new(Self::from(*lhs)),\n                rhs_table: rhs.table_id,\n                rhs_index,\n                rhs_prefix,\n                rhs_field,\n                rhs_delta,\n                lhs_field,\n                unique,\n                semijoin,\n            }),\n            PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field,\n                    rhs_field,\n                    unique,\n                },\n                semijoin,\n            ) => Self::HashJoin(BlockingHashJoin {\n                lhs: Box::new(PipelinedExecutor::from(*lhs)),\n                rhs: Box::new(PipelinedExecutor::from(*rhs)),\n                lhs_field,\n                rhs_field,\n                unique,\n                semijoin,\n            }),\n            PhysicalPlan::NLJoin(lhs, rhs) => Self::NLJoin(BlockingNLJoin {\n                lhs: Box::new(PipelinedExecutor::from(*lhs)),\n                rhs: Box::new(PipelinedExecutor::from(*rhs)),\n            }),\n            PhysicalPlan::Filter(input, expr) => Self::Filter(PipelinedFilter {\n                input: Box::new(PipelinedExecutor::from(*input)),\n                expr,\n            }),\n        }\n    }\n}\n\nimpl PipelinedExecutor {\n    /// Walks and visits each executor in the tree\n    pub fn visit(&self, f: &mut impl FnMut(&Self)) {\n        f(self);\n        match self {\n            Self::IxJoin(PipelinedIxJoin { lhs: input, .. })\n            | Self::IxDeltaJoin(PipelinedIxDeltaJoin { lhs: input, .. })\n            | Self::Filter(PipelinedFilter { input, .. })\n            | Self::Limit(PipelinedLimit { input, .. }) => {\n                input.visit(f);\n            }\n            Self::NLJoin(BlockingNLJoin { lhs, rhs }) | Self::HashJoin(BlockingHashJoin { lhs, rhs, .. }) => {\n                lhs.visit(f);\n                rhs.visit(f);\n            }\n            Self::TableScan(..)\n            | Self::IxScanEq(..)\n            | Self::IxScanRange(..)\n            | Self::IxDeltaScanEq(..)\n            | Self::IxDeltaScanRange(..) => {}\n        }\n    }\n\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self {\n            Self::TableScan(scan) => scan.is_empty(tx),\n            Self::IxScanEq(scan) => scan.is_empty(tx),\n            Self::IxScanRange(scan) => scan.is_empty(tx),\n            Self::IxDeltaScanEq(scan) => scan.is_empty(tx),\n            Self::IxDeltaScanRange(scan) => scan.is_empty(tx),\n            Self::IxJoin(join) => join.is_empty(tx),\n            Self::IxDeltaJoin(join) => join.is_empty(tx),\n            Self::HashJoin(join) => join.is_empty(tx),\n            Self::NLJoin(join) => join.is_empty(tx),\n            Self::Filter(filter) => filter.is_empty(tx),\n            Self::Limit(limit) => limit.is_empty(tx),\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        match self {\n            Self::TableScan(scan) => scan.execute(tx, metrics, f),\n            Self::IxScanEq(scan) => scan.execute(tx, metrics, f),\n            Self::IxScanRange(scan) => scan.execute(tx, metrics, f),\n            Self::IxDeltaScanEq(scan) => scan.execute(tx, metrics, f),\n            Self::IxDeltaScanRange(scan) => scan.execute(tx, metrics, f),\n            Self::IxJoin(join) => join.execute(tx, metrics, f),\n            Self::IxDeltaJoin(join) => join.execute(tx, metrics, f),\n            Self::HashJoin(join) => join.execute(tx, metrics, f),\n            Self::NLJoin(join) => join.execute(tx, metrics, f),\n            Self::Filter(filter) => filter.execute(tx, metrics, f),\n            Self::Limit(limit) => limit.execute(tx, metrics, f),\n        }\n    }\n}\n\n/// A pipelined executor for scanning both physical and delta tables\n#[derive(Debug)]\npub struct PipelinedScan {\n    pub table: TableId,\n    pub limit: Option<u64>,\n    pub delta: Option<Delta>,\n}\n\nimpl PipelinedScan {\n    /// Is this an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self.delta {\n            Some(Delta::Inserts) => !tx.has_inserts(self.table),\n            Some(Delta::Deletes) => !tx.has_deletes(self.table),\n            None => false,\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        // A physical table scan\n        let table_scan = || tx.table_scan(self.table);\n        // A physical table scan with optional row limit\n        let table_limit_scan = |limit| match limit {\n            None => table_scan().map(Either::Left),\n            Some(n) => table_scan().map(|iter| iter.take(n)).map(Either::Right),\n        };\n        // A delta table scan\n        let delta_scan = |inserts| tx.delta_scan(self.table, inserts);\n        // A delta table scan with optional row limit\n        let delta_limit_scan = |limit, inserts| match limit {\n            None => Either::Left(delta_scan(inserts)),\n            Some(n) => Either::Right(delta_scan(inserts).take(n)),\n        };\n        let mut n = 0;\n        let mut f = |t| {\n            n += 1;\n            f(t)\n        };\n        match self.delta {\n            None => {\n                for tuple in table_limit_scan(self.limit.map(|n| n as usize))?\n                    .map(Row::Ptr)\n                    .map(Tuple::Row)\n                {\n                    f(tuple)?;\n                }\n            }\n            Some(Delta::Inserts) => {\n                for tuple in delta_limit_scan(self.limit.map(|n| n as usize), true)\n                    .map(Row::Ref)\n                    .map(Tuple::Row)\n                {\n                    f(tuple)?;\n                }\n            }\n            Some(Delta::Deletes) => {\n                for tuple in delta_limit_scan(self.limit.map(|n| n as usize), false)\n                    .map(Row::Ref)\n                    .map(Tuple::Row)\n                {\n                    f(tuple)?;\n                }\n            }\n        }\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A range index scan executor for a delta table.\n///\n/// TODO: There is much overlap between this executor and [PipelinedIxScanRange].\n/// But merging them requires merging the [Datastore] and [DeltaStore] traits,\n/// since the index scan interface is right now split between both.\n#[derive(Debug)]\npub struct PipelinedIxDeltaScanRange {\n    /// The table id\n    pub table_id: TableId,\n    /// The index id\n    pub index_id: IndexId,\n    /// An equality prefix for multi-column scans\n    pub prefix: Vec<AlgebraicValue>,\n    /// The lower index bound\n    pub lower: Bound<AlgebraicValue>,\n    /// The upper index bound\n    pub upper: Bound<AlgebraicValue>,\n    /// Inserts or deletes?\n    pub delta: Delta,\n}\n\nimpl From<IxScan> for PipelinedIxDeltaScanRange {\n    fn from(scan: IxScan) -> Self {\n        match scan {\n            IxScan {\n                schema,\n                index_id,\n                prefix,\n                arg: Sarg::Eq(_, v),\n                delta: Some(delta),\n                ..\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                prefix: prefix.into_iter().map(|(_, v)| v).collect(),\n                lower: Bound::Included(v.clone()),\n                upper: Bound::Included(v),\n                delta,\n            },\n            IxScan {\n                schema,\n                index_id,\n                prefix,\n                arg: Sarg::Range(_, lower, upper),\n                delta: Some(delta),\n                ..\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                prefix: prefix.into_iter().map(|(_, v)| v).collect(),\n                lower,\n                upper,\n                delta,\n            },\n            IxScan { delta: None, .. } => unreachable!(),\n        }\n    }\n}\n\nimpl PipelinedIxDeltaScanRange {\n    /// Is the delta table empty?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self.delta {\n            Delta::Inserts => !tx.has_inserts(self.table_id),\n            Delta::Deletes => !tx.has_deletes(self.table_id),\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut f = |t| {\n            n += 1;\n            f(t)\n        };\n        match self.prefix.as_slice() {\n            [] => {\n                for ptr in tx\n                    .index_scan_range_for_delta(\n                        self.table_id,\n                        self.index_id,\n                        self.delta,\n                        (self.lower.as_ref(), self.upper.as_ref()),\n                    )\n                    .map(Tuple::Row)\n                {\n                    f(ptr)?;\n                }\n            }\n            prefix => {\n                for ptr in tx\n                    .index_scan_range_for_delta(\n                        self.table_id,\n                        self.index_id,\n                        self.delta,\n                        (\n                            self.lower\n                                .as_ref()\n                                .map(std::iter::once)\n                                .map(|iter| prefix.iter().chain(iter))\n                                .map(|iter| iter.cloned())\n                                .map(ProductValue::from_iter)\n                                .map(AlgebraicValue::Product),\n                            self.upper\n                                .as_ref()\n                                .map(std::iter::once)\n                                .map(|iter| prefix.iter().chain(iter))\n                                .map(|iter| iter.cloned())\n                                .map(ProductValue::from_iter)\n                                .map(AlgebraicValue::Product),\n                        ),\n                    )\n                    .map(Tuple::Row)\n                {\n                    f(ptr)?;\n                }\n            }\n        }\n        metrics.index_seeks += 1;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// An equality index scan executor for a delta table.\n///\n/// TODO: There is much overlap between this executor and [PipelinedIxScanEq].\n/// But merging them requires merging the [Datastore] and [DeltaStore] traits,\n/// since the index scan interface is right now split between both.\n#[derive(Debug)]\npub struct PipelinedIxDeltaScanEq {\n    /// The table id\n    pub table_id: TableId,\n    /// The index id\n    pub index_id: IndexId,\n    /// The point to scan the index for.\n    pub point: AlgebraicValue,\n    /// Inserts or deletes?\n    pub delta: Delta,\n}\n\nimpl From<IxScan> for PipelinedIxDeltaScanEq {\n    fn from(scan: IxScan) -> Self {\n        match scan {\n            IxScan {\n                schema,\n                index_id,\n                prefix,\n                arg: Sarg::Eq(_, last),\n                delta: Some(delta),\n                ..\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                point: combine_prefix_and_last(prefix, last),\n                delta,\n            },\n            IxScan { .. } => unreachable!(),\n        }\n    }\n}\n\nimpl PipelinedIxDeltaScanEq {\n    /// Is the delta table empty?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self.delta {\n            Delta::Inserts => !tx.has_inserts(self.table_id),\n            Delta::Deletes => !tx.has_deletes(self.table_id),\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut f = |t| {\n            n += 1;\n            f(t)\n        };\n        for ptr in tx\n            .index_scan_point_for_delta(self.table_id, self.index_id, self.delta, &self.point)\n            .map(Tuple::Row)\n        {\n            f(ptr)?;\n        }\n\n        metrics.index_seeks += 1;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A pipelined executor for range scanning an index\n#[derive(Debug)]\npub struct PipelinedIxScanRange {\n    /// The table id\n    pub table_id: TableId,\n    /// The index id\n    pub index_id: IndexId,\n    pub limit: Option<u64>,\n    /// An equality prefix for multi-column scans\n    pub prefix: Vec<AlgebraicValue>,\n    /// The lower index bound\n    pub lower: Bound<AlgebraicValue>,\n    /// The upper index bound\n    pub upper: Bound<AlgebraicValue>,\n}\n\nimpl From<IxScan> for PipelinedIxScanRange {\n    fn from(scan: IxScan) -> Self {\n        match scan {\n            IxScan {\n                schema,\n                limit,\n                delta: None,\n                index_id,\n                prefix,\n                arg: Sarg::Eq(_, v),\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                limit,\n                prefix: prefix.into_iter().map(|(_, v)| v).collect(),\n                lower: Bound::Included(v.clone()),\n                upper: Bound::Included(v),\n            },\n            IxScan {\n                schema,\n                limit,\n                delta: None,\n                index_id,\n                prefix,\n                arg: Sarg::Range(_, lower, upper),\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                limit,\n                prefix: prefix.into_iter().map(|(_, v)| v).collect(),\n                lower,\n                upper,\n            },\n            IxScan { .. } => unreachable!(),\n        }\n    }\n}\n\nimpl PipelinedIxScanRange {\n    /// We don't know statically if an index scan will return rows\n    pub fn is_empty(&self, _: &impl DeltaStore) -> bool {\n        false\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        // A single column index scan\n        let single_col_scan = || {\n            tx.index_scan_range(\n                self.table_id,\n                self.index_id,\n                &(self.lower.as_ref(), self.upper.as_ref()),\n            )\n        };\n        // A single column index scan with optional row limit\n        let single_col_limit_scan = |limit| match limit {\n            None => single_col_scan().map(Either::Left),\n            Some(n) => single_col_scan().map(|iter| iter.take(n)).map(Either::Right),\n        };\n        // A multi-column index scan\n        let multi_col_scan = |prefix: &[AlgebraicValue]| {\n            tx.index_scan_range(\n                self.table_id,\n                self.index_id,\n                &(\n                    self.lower\n                        .as_ref()\n                        .map(std::iter::once)\n                        .map(|iter| prefix.iter().chain(iter))\n                        .map(|iter| iter.cloned())\n                        .map(ProductValue::from_iter)\n                        .map(AlgebraicValue::Product),\n                    self.upper\n                        .as_ref()\n                        .map(std::iter::once)\n                        .map(|iter| prefix.iter().chain(iter))\n                        .map(|iter| iter.cloned())\n                        .map(ProductValue::from_iter)\n                        .map(AlgebraicValue::Product),\n                ),\n            )\n        };\n        // A multi-column index scan with optional row limit\n        let multi_col_limit_scan = |prefix, limit| match limit {\n            None => multi_col_scan(prefix).map(Either::Left),\n            Some(n) => multi_col_scan(prefix).map(|iter| iter.take(n)).map(Either::Right),\n        };\n        let mut n = 0;\n        let mut f = |t| {\n            n += 1;\n            f(t)\n        };\n        match self.prefix.as_slice() {\n            [] => {\n                for ptr in single_col_limit_scan(self.limit.map(|n| n as usize))?\n                    .map(Row::Ptr)\n                    .map(Tuple::Row)\n                {\n                    f(ptr)?;\n                }\n            }\n            prefix => {\n                for ptr in multi_col_limit_scan(prefix, self.limit.map(|n| n as usize))?\n                    .map(Row::Ptr)\n                    .map(Tuple::Row)\n                {\n                    f(ptr)?;\n                }\n            }\n        }\n        metrics.index_seeks += 1;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A pipelined executor for equality scanning an index\n#[derive(Debug)]\npub struct PipelinedIxScanEq {\n    /// The table id\n    pub table_id: TableId,\n    /// The index id\n    pub index_id: IndexId,\n    pub limit: Option<u64>,\n    /// The point to scan the index for.\n    pub point: AlgebraicValue,\n}\n\nimpl From<IxScan> for PipelinedIxScanEq {\n    fn from(scan: IxScan) -> Self {\n        match scan {\n            IxScan {\n                schema,\n                limit,\n                delta: None,\n                index_id,\n                prefix,\n                arg: Sarg::Eq(_, last),\n            } => Self {\n                table_id: schema.table_id,\n                index_id,\n                limit,\n                point: combine_prefix_and_last(prefix, last),\n            },\n            IxScan { .. } => unreachable!(),\n        }\n    }\n}\n\nfn combine_prefix_and_last(prefix: Vec<(ColId, AlgebraicValue)>, last: AlgebraicValue) -> AlgebraicValue {\n    if prefix.is_empty() {\n        last\n    } else {\n        let mut elems = Vec::with_capacity(prefix.len() + 1);\n        elems.extend(prefix.into_iter().map(|(_, v)| v));\n        elems.push(last);\n        AlgebraicValue::product(elems)\n    }\n}\n\nfn combine_probe_prefix_and_last(prefix: &[AlgebraicValue], last: AlgebraicValue) -> AlgebraicValue {\n    if prefix.is_empty() {\n        last\n    } else {\n        AlgebraicValue::product(ProductValue::from_iter(\n            prefix.iter().cloned().chain(std::iter::once(last)),\n        ))\n    }\n}\n\nimpl PipelinedIxScanEq {\n    /// We don't know statically if an index scan will return rows\n    pub fn is_empty(&self, _: &impl DeltaStore) -> bool {\n        false\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        // Scan without a row limit.\n        let scan = || tx.index_scan_point(self.table_id, self.index_id, &self.point);\n        // Scan with an optional row limit.\n        let scan_opt_limit = |limit| match limit {\n            None => scan().map(Either::Left),\n            Some(n) => scan().map(|iter| iter.take(n)).map(Either::Right),\n        };\n        let mut n = 0;\n        let mut f = |t| {\n            n += 1;\n            f(t)\n        };\n        for ptr in scan_opt_limit(self.limit.map(|n| n as usize))?\n            .map(Row::Ptr)\n            .map(Tuple::Row)\n        {\n            f(ptr)?;\n        }\n\n        metrics.index_seeks += 1;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A pipelined index join executor\n#[derive(Debug)]\npub struct PipelinedIxJoin {\n    /// The executor for the lhs of the join\n    pub lhs: Box<PipelinedExecutor>,\n    /// The rhs table\n    pub rhs_table: TableId,\n    /// The rhs index\n    pub rhs_index: IndexId,\n    /// Constant prefix values for multi-column index probes.\n    pub rhs_prefix: Vec<AlgebraicValue>,\n    /// The rhs join field\n    pub rhs_field: ColId,\n    /// The lhs join field\n    pub lhs_field: TupleField,\n    /// Is the index unique?\n    pub unique: bool,\n    /// Is this a semijoin?\n    pub semijoin: Semi,\n}\n\nimpl PipelinedIxJoin {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        self.lhs.is_empty(tx)\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut index_seeks = 0;\n        let mut bytes_scanned = 0;\n\n        let iter_rhs = |u: &Tuple, lhs_field: &TupleField, bytes_scanned: &mut usize| -> Result<_> {\n            let key = combine_probe_prefix_and_last(&self.rhs_prefix, project(u, lhs_field, bytes_scanned));\n            Ok(tx\n                .index_scan_point(self.rhs_table, self.rhs_index, &key)?\n                .map(Row::Ptr)\n                .map(Tuple::Row))\n        };\n\n        let probe_rhs = |u: &Tuple, lhs_field: &TupleField, bytes_scanned: &mut usize| -> Result<_> {\n            Ok(iter_rhs(u, lhs_field, bytes_scanned)?.next())\n        };\n\n        match self {\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::Lhs,\n                ..\n            } => {\n                // Should we evaluate the lhs tuple?\n                // Probe the index to see if there is a matching row.\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    if probe_rhs(&u, lhs_field, &mut bytes_scanned)?.is_some() {\n                        f(u)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::Rhs,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs row\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    if let Some(v) = probe_rhs(&u, lhs_field, &mut bytes_scanned)? {\n                        f(v)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::All,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs row\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    if let Some(v) = probe_rhs(&u, lhs_field, &mut bytes_scanned)? {\n                        f(u.join(v))?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::Lhs,\n                ..\n            } => {\n                // How many times should we evaluate the lhs tuple?\n                // Probe the index for the number of matching rows.\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    for _ in iter_rhs(&u, lhs_field, &mut bytes_scanned)? {\n                        f(u.clone())?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::Rhs,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs rows\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    for v in iter_rhs(&u, lhs_field, &mut bytes_scanned)? {\n                        f(v)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::All,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs rows\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    for v in iter_rhs(&u, lhs_field, &mut bytes_scanned)? {\n                        f(u.clone().join(v))?;\n                    }\n                    Ok(())\n                })?;\n            }\n        }\n        metrics.index_seeks += index_seeks;\n        metrics.rows_scanned += n;\n        metrics.bytes_scanned += bytes_scanned;\n        Ok(())\n    }\n}\n\n/// An index join executor where the index (rhs) side is a delta table.\n///\n/// TODO: There is much overlap between this executor and [PipelinedIxJoin].\n/// But merging them requires merging the [Datastore] and [DeltaStore] traits,\n/// since the index scan interface is right now split between both.\n#[derive(Debug)]\npub struct PipelinedIxDeltaJoin {\n    /// The executor for the lhs of the join\n    pub lhs: Box<PipelinedExecutor>,\n    /// The rhs table\n    pub rhs_table: TableId,\n    /// Inserts or deletes?\n    pub rhs_delta: Delta,\n    /// The rhs index\n    pub rhs_index: IndexId,\n    /// Constant prefix values for multi-column index probes.\n    pub rhs_prefix: Vec<AlgebraicValue>,\n    /// The rhs join field\n    pub rhs_field: ColId,\n    /// The lhs join field\n    pub lhs_field: TupleField,\n    /// Is the index unique?\n    pub unique: bool,\n    /// Is this a semijoin?\n    pub semijoin: Semi,\n}\n\nimpl PipelinedIxDeltaJoin {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        match self.rhs_delta {\n            Delta::Inserts => !tx.has_inserts(self.rhs_table) || self.lhs.is_empty(tx),\n            Delta::Deletes => !tx.has_deletes(self.rhs_table) || self.lhs.is_empty(tx),\n        }\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut index_seeks = 0;\n        let mut bytes_scanned = 0;\n\n        match self {\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::Lhs,\n                ..\n            } => {\n                // Should we evaluate the lhs tuple?\n                // Probe the index to see if there is a matching row.\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    if tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .next()\n                        .is_some()\n                    {\n                        f(u)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::Rhs,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs row\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    if let Some(v) = tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .next()\n                        .map(Tuple::Row)\n                    {\n                        f(v)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: true,\n                semijoin: Semi::All,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs row\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    if let Some(v) = tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .next()\n                        .map(Tuple::Row)\n                    {\n                        f(u.join(v))?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::Lhs,\n                ..\n            } => {\n                // How many times should we evaluate the lhs tuple?\n                // Probe the index for the number of matching rows.\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    for _ in 0..tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .count()\n                    {\n                        f(u.clone())?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::Rhs,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs rows\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    for v in tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .map(Tuple::Row)\n                    {\n                        f(v)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                lhs_field,\n                unique: false,\n                semijoin: Semi::All,\n                ..\n            } => {\n                // Probe the index and evaluate the matching rhs rows\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    index_seeks += 1;\n                    let key =\n                        combine_probe_prefix_and_last(&self.rhs_prefix, project(&u, lhs_field, &mut bytes_scanned));\n                    for v in tx\n                        .index_scan_point_for_delta(self.rhs_table, self.rhs_index, self.rhs_delta, &key)\n                        .map(Tuple::Row)\n                    {\n                        f(u.clone().join(v.clone()))?;\n                    }\n                    Ok(())\n                })?;\n            }\n        }\n        metrics.index_seeks += index_seeks;\n        metrics.rows_scanned += n;\n        metrics.bytes_scanned += bytes_scanned;\n        Ok(())\n    }\n}\n\n/// An executor for a hash join.\n/// Note, this executor is a pipeline breaker,\n/// because it must fully materialize the rhs.\n#[derive(Debug)]\npub struct BlockingHashJoin {\n    pub lhs: Box<PipelinedExecutor>,\n    pub rhs: Box<PipelinedExecutor>,\n    pub lhs_field: TupleField,\n    pub rhs_field: TupleField,\n    pub unique: bool,\n    pub semijoin: Semi,\n}\n\nimpl BlockingHashJoin {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        self.lhs.is_empty(tx) || self.rhs.is_empty(tx)\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut bytes_scanned = 0;\n        match self {\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: true,\n                semijoin: Semi::Lhs,\n            } => {\n                let mut rhs_table = HashSet::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    rhs_table.insert(project(&v, rhs_field, &mut bytes_scanned));\n                    Ok(())\n                })?;\n\n                // How many rows did we pull from the rhs?\n                n += rhs_table.len();\n\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if rhs_table.contains(&project(&u, lhs_field, &mut bytes_scanned)) {\n                        f(u)?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: true,\n                semijoin: Semi::Rhs,\n            } => {\n                let mut rhs_table = HashMap::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    rhs_table.insert(project(&v, rhs_field, &mut bytes_scanned), v);\n                    Ok(())\n                })?;\n\n                // How many rows did we pull from the rhs?\n                n += rhs_table.len();\n\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if let Some(v) = rhs_table.get(&project(&u, lhs_field, &mut bytes_scanned)) {\n                        f(v.clone())?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: true,\n                semijoin: Semi::All,\n            } => {\n                let mut rhs_table = HashMap::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    rhs_table.insert(project(&v, rhs_field, &mut bytes_scanned), v);\n                    Ok(())\n                })?;\n\n                // How many rows did we pull from the rhs?\n                n += rhs_table.len();\n\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if let Some(v) = rhs_table.get(&project(&u, lhs_field, &mut bytes_scanned)) {\n                        f(u.clone().join(v.clone()))?;\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: false,\n                semijoin: Semi::Lhs,\n            } => {\n                let mut rhs_table = HashMap::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    n += 1;\n                    rhs_table\n                        .entry(project(&v, rhs_field, &mut bytes_scanned))\n                        .and_modify(|n| *n += 1)\n                        .or_insert(1);\n                    Ok(())\n                })?;\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if let Some(n) = rhs_table.get(&project(&u, lhs_field, &mut bytes_scanned)).copied() {\n                        for _ in 0..n {\n                            f(u.clone())?;\n                        }\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: false,\n                semijoin: Semi::Rhs,\n            } => {\n                let mut rhs_table: HashMap<AlgebraicValue, Vec<_>> = HashMap::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    n += 1;\n                    let key = project(&v, rhs_field, &mut bytes_scanned);\n                    if let Some(tuples) = rhs_table.get_mut(&key) {\n                        tuples.push(v);\n                    } else {\n                        rhs_table.insert(key, vec![v]);\n                    }\n                    Ok(())\n                })?;\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if let Some(rhs_tuples) = rhs_table.get(&project(&u, lhs_field, &mut bytes_scanned)) {\n                        for v in rhs_tuples {\n                            f(v.clone())?;\n                        }\n                    }\n                    Ok(())\n                })?;\n            }\n            Self {\n                lhs,\n                rhs,\n                lhs_field,\n                rhs_field,\n                unique: false,\n                semijoin: Semi::All,\n            } => {\n                let mut rhs_table: HashMap<AlgebraicValue, Vec<_>> = HashMap::new();\n                rhs.execute(tx, metrics, &mut |v| {\n                    n += 1;\n                    let key = project(&v, rhs_field, &mut bytes_scanned);\n                    if let Some(tuples) = rhs_table.get_mut(&key) {\n                        tuples.push(v);\n                    } else {\n                        rhs_table.insert(key, vec![v]);\n                    }\n                    Ok(())\n                })?;\n                lhs.execute(tx, metrics, &mut |u| {\n                    n += 1;\n                    if let Some(rhs_tuples) = rhs_table.get(&project(&u, lhs_field, &mut bytes_scanned)) {\n                        for v in rhs_tuples {\n                            f(u.clone().join(v.clone()))?;\n                        }\n                    }\n                    Ok(())\n                })?;\n            }\n        }\n        metrics.rows_scanned += n;\n        metrics.bytes_scanned += bytes_scanned;\n        Ok(())\n    }\n}\n\n/// An executor for a nested loop join.\n/// Note, this is a pipeline breaker,\n/// because it fully materializes the rhs.\n#[derive(Debug)]\npub struct BlockingNLJoin {\n    pub lhs: Box<PipelinedExecutor>,\n    pub rhs: Box<PipelinedExecutor>,\n}\n\nimpl BlockingNLJoin {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        self.lhs.is_empty(tx) || self.rhs.is_empty(tx)\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut rhs = vec![];\n        self.rhs.execute(tx, metrics, &mut |v| {\n            rhs.push(v);\n            Ok(())\n        })?;\n\n        // How many rows did we pull from the rhs?\n        let mut n = rhs.len();\n\n        self.lhs.execute(tx, metrics, &mut |u| {\n            n += 1;\n            for v in rhs.iter() {\n                f(u.clone().join(v.clone()))?;\n            }\n            Ok(())\n        })?;\n\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A pipelined filter executor\n#[derive(Debug)]\npub struct PipelinedFilter {\n    pub input: Box<PipelinedExecutor>,\n    pub expr: PhysicalExpr,\n}\n\nimpl PipelinedFilter {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        self.input.is_empty(tx)\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        let mut bytes_scanned = 0;\n        self.input.execute(tx, metrics, &mut |t| {\n            n += 1;\n            if self.expr.eval_bool_with_metrics(&t, &mut bytes_scanned) {\n                f(t)?;\n            }\n            Ok(())\n        })?;\n        metrics.rows_scanned += n;\n        metrics.bytes_scanned += bytes_scanned;\n        Ok(())\n    }\n}\n\n/// A pipelined limit operator that does not short-circuit.\n/// Input rows will be scanned even after the limit has been reached.\n#[derive(Debug)]\npub struct PipelinedLimit {\n    pub input: Box<PipelinedExecutor>,\n    pub limit: u64,\n}\n\nimpl PipelinedLimit {\n    /// Does this operation contain an empty delta scan?\n    pub fn is_empty(&self, tx: &impl DeltaStore) -> bool {\n        self.input.is_empty(tx)\n    }\n\n    pub fn execute<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Tuple<'a>) -> Result<()>,\n    ) -> Result<()> {\n        let mut n = 0;\n        self.input.execute(tx, metrics, &mut |t| {\n            n += 1;\n            if n <= self.limit as usize {\n                f(t)?;\n            }\n            Ok(())\n        })?;\n        metrics.rows_scanned += n;\n        Ok(())\n    }\n}\n\n/// A wrapper around [ProjectField] that increments a counter by the size of the projected value\nfn project(row: &impl ProjectField, field: &TupleField, bytes_scanned: &mut usize) -> AlgebraicValue {\n    let value = row.project(field);\n    *bytes_scanned += value.size_of();\n    value\n}\n"
  },
  {
    "path": "crates/expr/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-expr\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The logical expression representation for the SpacetimeDB query engine\"\n\n[dependencies]\nanyhow.workspace = true\nbigdecimal.workspace = true\nderive_more.workspace = true\nethnum.workspace = true\nthiserror.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-sats.workspace = true\nspacetimedb-schema.workspace = true\nspacetimedb-sql-parser.workspace = true\nbytes.workspace = true\n\n[dev-dependencies]\npretty_assertions.workspace = true\nspacetimedb = { path = \"../bindings\", features = [\"unstable\"] }\nspacetimedb-lib = { path = \"../lib\" }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/expr/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/expr/src/check.rs",
    "content": "use crate::expr::LeftDeepJoin;\nuse crate::expr::{Expr, ProjectList, ProjectName, Relvar};\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_lib::AlgebraicType;\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_schema::schema::TableOrViewSchema;\nuse spacetimedb_sql_parser::ast::BinOp;\nuse spacetimedb_sql_parser::{\n    ast::{sub::SqlSelect, SqlFrom, SqlIdent, SqlJoin},\n    parser::sub::parse_subscription,\n};\nuse std::ops::{Deref, DerefMut};\nuse std::sync::Arc;\n\nuse super::{\n    errors::{DuplicateName, TypingError, Unresolved, Unsupported},\n    expr::RelExpr,\n    type_expr, type_proj, type_select,\n};\n\n/// The result of type checking and name resolution\npub type TypingResult<T> = core::result::Result<T, TypingError>;\n\n/// A view of the database schema\npub trait SchemaView {\n    fn table_id(&self, name: &str) -> Option<TableId>;\n    fn schema_for_table(&self, table_id: TableId) -> Option<Arc<TableOrViewSchema>>;\n    fn rls_rules_for_table(&self, table_id: TableId) -> anyhow::Result<Vec<Box<str>>>;\n\n    fn schema(&self, name: &str) -> Option<Arc<TableOrViewSchema>> {\n        self.table_id(name).and_then(|table_id| self.schema_for_table(table_id))\n    }\n}\n\n#[derive(Default)]\npub struct Relvars(HashMap<RawIdentifier, Arc<TableOrViewSchema>>);\n\nimpl Deref for Relvars {\n    type Target = HashMap<RawIdentifier, Arc<TableOrViewSchema>>;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for Relvars {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\npub trait TypeChecker {\n    type Ast;\n    type Set;\n\n    fn type_ast(ast: Self::Ast, tx: &impl SchemaView) -> TypingResult<ProjectList>;\n\n    fn type_set(ast: Self::Set, vars: &mut Relvars, tx: &impl SchemaView) -> TypingResult<ProjectList>;\n\n    fn type_from(from: SqlFrom, vars: &mut Relvars, tx: &impl SchemaView) -> TypingResult<RelExpr> {\n        match from {\n            SqlFrom::Expr(SqlIdent(name), SqlIdent(alias)) => {\n                let schema = Self::type_relvar(tx, &name)?;\n                vars.insert(alias.clone(), schema.clone());\n                Ok(RelExpr::RelVar(Relvar {\n                    schema,\n                    alias,\n                    delta: None,\n                }))\n            }\n            SqlFrom::Join(SqlIdent(name), SqlIdent(alias), joins) => {\n                let schema = Self::type_relvar(tx, &name)?;\n                vars.insert(alias.clone(), schema.clone());\n                let mut join = RelExpr::RelVar(Relvar {\n                    schema,\n                    alias,\n                    delta: None,\n                });\n\n                for SqlJoin {\n                    var: SqlIdent(name),\n                    alias: SqlIdent(alias),\n                    on,\n                } in joins\n                {\n                    // Check for duplicate aliases\n                    if vars.contains_key(&alias) {\n                        return Err(DuplicateName(alias.clone()).into());\n                    }\n\n                    let lhs = Box::new(join);\n                    let rhs = Relvar {\n                        schema: Self::type_relvar(tx, &name)?,\n                        alias,\n                        delta: None,\n                    };\n\n                    vars.insert(rhs.alias.clone(), rhs.schema.clone());\n\n                    if let Some(on) = on {\n                        if let Expr::BinOp(BinOp::Eq, a, b) = type_expr(vars, on, Some(&AlgebraicType::Bool))?\n                            && let (Expr::Field(a), Expr::Field(b)) = (*a, *b)\n                        {\n                            join = RelExpr::EqJoin(LeftDeepJoin { lhs, rhs }, a, b);\n                            continue;\n                        }\n                        unreachable!(\"Unreachability guaranteed by parser\")\n                    }\n\n                    join = RelExpr::LeftDeepJoin(LeftDeepJoin { lhs, rhs });\n                }\n\n                Ok(join)\n            }\n        }\n    }\n\n    fn type_relvar(tx: &impl SchemaView, name: &str) -> TypingResult<Arc<TableOrViewSchema>> {\n        tx.schema(name)\n            .ok_or_else(|| Unresolved::table(name))\n            .map_err(TypingError::from)\n    }\n}\n\n/// Type checker for subscriptions\nstruct SubChecker;\n\nimpl TypeChecker for SubChecker {\n    type Ast = SqlSelect;\n    type Set = SqlSelect;\n\n    fn type_ast(ast: Self::Ast, tx: &impl SchemaView) -> TypingResult<ProjectList> {\n        Self::type_set(ast, &mut Relvars::default(), tx)\n    }\n\n    fn type_set(ast: Self::Set, vars: &mut Relvars, tx: &impl SchemaView) -> TypingResult<ProjectList> {\n        match ast {\n            SqlSelect {\n                project,\n                from,\n                filter: None,\n            } => {\n                let input = Self::type_from(from, vars, tx)?;\n                type_proj(input, project, vars)\n            }\n            SqlSelect {\n                project,\n                from,\n                filter: Some(expr),\n            } => {\n                let input = Self::type_from(from, vars, tx)?;\n                type_proj(type_select(input, expr, vars)?, project, vars)\n            }\n        }\n    }\n}\n\n/// Parse and type check a subscription query\npub fn parse_and_type_sub(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> TypingResult<(ProjectName, bool)> {\n    let ast = parse_subscription(sql)?;\n    let has_param = ast.has_parameter();\n    let ast = ast.resolve_sender(auth.caller());\n    expect_table_type(SubChecker::type_ast(ast, tx)?).map(|plan| (plan, has_param))\n}\n\n/// Returns an error if the input type is not a table type or relvar\nfn expect_table_type(expr: ProjectList) -> TypingResult<ProjectName> {\n    match expr {\n        // Note, this is called before we do any RLS resolution.\n        // Hence this length should always be 1.\n        ProjectList::Name(mut proj) if proj.len() == 1 => Ok(proj.pop().unwrap()),\n        ProjectList::Limit(input, _) => expect_table_type(*input),\n        ProjectList::Name(..) | ProjectList::List(..) | ProjectList::Agg(..) => Err(Unsupported::ReturnType.into()),\n    }\n}\n\npub mod test_utils {\n    use spacetimedb_lib::{db::raw_def::v9::RawModuleDefV9Builder, ProductType};\n    use spacetimedb_primitives::TableId;\n    use spacetimedb_sats::raw_identifier::RawIdentifier;\n    use spacetimedb_schema::{\n        def::ModuleDef,\n        schema::{Schema, TableOrViewSchema, TableSchema},\n    };\n    use std::sync::Arc;\n\n    use super::SchemaView;\n\n    pub fn build_module_def(types: Vec<(&str, ProductType)>) -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n        for (name, ty) in types {\n            builder.build_table_with_new_type(RawIdentifier::new(name), ty, true);\n        }\n        builder.finish().try_into().expect(\"failed to generate module def\")\n    }\n\n    pub struct SchemaViewer(pub ModuleDef);\n\n    impl SchemaView for SchemaViewer {\n        fn table_id(&self, name: &str) -> Option<TableId> {\n            match name {\n                \"t\" => Some(TableId(0)),\n                \"s\" => Some(TableId(1)),\n                _ => None,\n            }\n        }\n\n        fn schema_for_table(&self, table_id: TableId) -> Option<Arc<TableOrViewSchema>> {\n            match table_id.idx() {\n                0 => Some((TableId(0), \"t\")),\n                1 => Some((TableId(1), \"s\")),\n                _ => None,\n            }\n            .and_then(|(table_id, name)| {\n                self.0\n                    .table(name)\n                    .map(|def| Arc::new(TableSchema::from_module_def(&self.0, def, (), table_id)))\n                    .map(TableOrViewSchema::from)\n                    .map(Arc::new)\n            })\n        }\n\n        fn rls_rules_for_table(&self, _: TableId) -> anyhow::Result<Vec<Box<str>>> {\n            Ok(vec![])\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{\n        check::test_utils::{build_module_def, SchemaViewer},\n        expr::ProjectName,\n    };\n    use spacetimedb_lib::{identity::AuthCtx, AlgebraicType, ProductType};\n    use spacetimedb_schema::def::ModuleDef;\n\n    use super::{SchemaView, TypingResult};\n\n    fn module_def() -> ModuleDef {\n        build_module_def(vec![\n            (\n                \"t\",\n                ProductType::from([\n                    (\"ts\", AlgebraicType::timestamp()),\n                    (\"i8\", AlgebraicType::I8),\n                    (\"u8\", AlgebraicType::U8),\n                    (\"i16\", AlgebraicType::I16),\n                    (\"u16\", AlgebraicType::U16),\n                    (\"i32\", AlgebraicType::I32),\n                    (\"u32\", AlgebraicType::U32),\n                    (\"i64\", AlgebraicType::I64),\n                    (\"u64\", AlgebraicType::U64),\n                    (\"int\", AlgebraicType::U32),\n                    (\"f32\", AlgebraicType::F32),\n                    (\"f64\", AlgebraicType::F64),\n                    (\"i128\", AlgebraicType::I128),\n                    (\"u128\", AlgebraicType::U128),\n                    (\"i256\", AlgebraicType::I256),\n                    (\"u256\", AlgebraicType::U256),\n                    (\"str\", AlgebraicType::String),\n                    (\"arr\", AlgebraicType::array(AlgebraicType::String)),\n                ]),\n            ),\n            (\n                \"s\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::identity()),\n                    (\"u32\", AlgebraicType::U32),\n                    (\"arr\", AlgebraicType::array(AlgebraicType::String)),\n                    (\"bytes\", AlgebraicType::bytes()),\n                ]),\n            ),\n        ])\n    }\n\n    /// A wrapper around [super::parse_and_type_sub] that takes a dummy [AuthCtx]\n    fn parse_and_type_sub(sql: &str, tx: &impl SchemaView) -> TypingResult<ProjectName> {\n        super::parse_and_type_sub(sql, tx, &AuthCtx::for_testing()).map(|(plan, _)| plan)\n    }\n\n    #[test]\n    fn valid_literals() {\n        let tx = SchemaViewer(module_def());\n\n        struct TestCase {\n            sql: &'static str,\n            msg: &'static str,\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select * from t where i32 = -1\",\n                msg: \"Leading `-`\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = +1\",\n                msg: \"Leading `+`\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = 1e3\",\n                msg: \"Scientific notation\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = 1E3\",\n                msg: \"Case insensitive scientific notation\",\n            },\n            TestCase {\n                sql: \"select * from t where f32 = 1e3\",\n                msg: \"Integers can parse as floats\",\n            },\n            TestCase {\n                sql: \"select * from t where f32 = 1e-3\",\n                msg: \"Negative exponent\",\n            },\n            TestCase {\n                sql: \"select * from t where f32 = 0.1\",\n                msg: \"Standard decimal notation\",\n            },\n            TestCase {\n                sql: \"select * from t where f32 = .1\",\n                msg: \"Leading `.`\",\n            },\n            TestCase {\n                sql: \"select * from t where f32 = 1e40\",\n                msg: \"Infinity\",\n            },\n            TestCase {\n                sql: \"select * from t where u256 = 1e40\",\n                msg: \"u256\",\n            },\n            TestCase {\n                sql: \"select * from t where ts = '2025-02-10T15:45:30Z'\",\n                msg: \"timestamp\",\n            },\n            TestCase {\n                sql: \"select * from t where ts = '2025-02-10T15:45:30.123Z'\",\n                msg: \"timestamp ms\",\n            },\n            TestCase {\n                sql: \"select * from t where ts = '2025-02-10T15:45:30.123456789Z'\",\n                msg: \"timestamp ns\",\n            },\n            TestCase {\n                sql: \"select * from t where ts = '2025-02-10 15:45:30+02:00'\",\n                msg: \"timestamp with timezone\",\n            },\n            TestCase {\n                sql: \"select * from t where ts = '2025-02-10 15:45:30.123+02:00'\",\n                msg: \"timestamp ms with timezone\",\n            },\n        ] {\n            let result = parse_and_type_sub(sql, &tx);\n            assert!(result.is_ok(), \"name: {}, error: {}\", msg, result.unwrap_err());\n        }\n    }\n\n    #[test]\n    fn valid_literals_for_type() {\n        let tx = SchemaViewer(module_def());\n\n        for ty in [\n            \"i8\", \"u8\", \"i16\", \"u16\", \"i32\", \"u32\", \"i64\", \"u64\", \"f32\", \"f64\", \"i128\", \"u128\", \"i256\", \"u256\",\n        ] {\n            let sql = format!(\"select * from t where {ty} = 127\");\n            let result = parse_and_type_sub(&sql, &tx);\n            assert!(result.is_ok(), \"Failed to parse {ty}: {}\", result.unwrap_err());\n        }\n    }\n\n    #[test]\n    fn invalid_literals() {\n        let tx = SchemaViewer(module_def());\n\n        struct TestCase {\n            sql: &'static str,\n            msg: &'static str,\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select * from t where u8 = -1\",\n                msg: \"Negative integer for unsigned column\",\n            },\n            TestCase {\n                sql: \"select * from t where u8 = 1e3\",\n                msg: \"Out of bounds\",\n            },\n            TestCase {\n                sql: \"select * from t where u8 = 0.1\",\n                msg: \"Float as integer\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = 1e-3\",\n                msg: \"Float as integer\",\n            },\n            TestCase {\n                sql: \"select * from t where i32 = 1e-3\",\n                msg: \"Float as integer\",\n            },\n        ] {\n            let result = parse_and_type_sub(sql, &tx);\n            assert!(result.is_err(), \"{msg}\");\n        }\n    }\n\n    #[test]\n    fn valid() {\n        let tx = SchemaViewer(module_def());\n\n        struct TestCase {\n            sql: &'static str,\n            msg: &'static str,\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select * from t\",\n                msg: \"Can select * on any table\",\n            },\n            TestCase {\n                sql: \"select * from t where true\",\n                msg: \"Boolean literals are valid in WHERE clause\",\n            },\n            TestCase {\n                sql: \"select * from t where t.u32 = 1\",\n                msg: \"Can qualify column references with table name\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = 1\",\n                msg: \"Can leave columns unqualified when unambiguous\",\n            },\n            TestCase {\n                sql: \"select * from s where id = :sender\",\n                msg: \"Can use :sender as an Identity\",\n            },\n            TestCase {\n                sql: \"select * from s where bytes = :sender\",\n                msg: \"Can use :sender as a byte array\",\n            },\n            TestCase {\n                sql: \"select * from t where t.u32 = 1 or t.str = ''\",\n                msg: \"Type OR with qualified column references\",\n            },\n            TestCase {\n                sql: \"select * from s where s.bytes = 0xABCD or bytes = X'ABCD'\",\n                msg: \"Type OR with mixed qualified and unqualified column references\",\n            },\n            TestCase {\n                sql: \"select * from s as r where r.bytes = 0xABCD or bytes = X'ABCD'\",\n                msg: \"Type OR with table alias\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s\",\n                msg: \"Type cross join + projection\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s join s as r where t.u32 = s.u32 and s.u32 = r.u32\",\n                msg: \"Type self join + projection\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s on t.u32 = s.u32 where t.f32 = 0.1\",\n                msg: \"Type inner join + projection\",\n            },\n        ] {\n            let result = parse_and_type_sub(sql, &tx);\n            assert!(result.is_ok(), \"{msg}\");\n        }\n    }\n\n    #[test]\n    fn invalid() {\n        let tx = SchemaViewer(module_def());\n\n        struct TestCase {\n            sql: &'static str,\n            msg: &'static str,\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select * from r\",\n                msg: \"Table r does not exist\",\n            },\n            TestCase {\n                sql: \"select * from t where arr = :sender\",\n                msg: \"The :sender param is an identity\",\n            },\n            TestCase {\n                sql: \"select * from t where t.a = 1\",\n                msg: \"Field a does not exist on table t\",\n            },\n            TestCase {\n                sql: \"select * from t as r where r.a = 1\",\n                msg: \"Field a does not exist on table t\",\n            },\n            TestCase {\n                sql: \"select * from t where u32 = 'str'\",\n                msg: \"Field u32 is not a string\",\n            },\n            TestCase {\n                sql: \"select * from t where t.u32 = 1.3\",\n                msg: \"Field u32 is not a float\",\n            },\n            TestCase {\n                sql: \"select * from t as r where t.u32 = 5\",\n                msg: \"t is not in scope after alias\",\n            },\n            TestCase {\n                sql: \"select u32 from t\",\n                msg: \"Subscriptions must be typed to a single table\",\n            },\n            TestCase {\n                sql: \"select * from t join s\",\n                msg: \"Subscriptions must be typed to a single table\",\n            },\n            TestCase {\n                sql: \"select t.* from t join t\",\n                msg: \"Self join requires aliases\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s on t.arr = s.arr\",\n                msg: \"Product values are not comparable\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s on t.u32 = r.u32 join s as r\",\n                msg: \"Alias r is not in scope when it is referenced\",\n            },\n            TestCase {\n                sql: \"select * from t limit 5\",\n                msg: \"Subscriptions do not support limit\",\n            },\n            TestCase {\n                sql: \"select t.* from t join s on t.u32 = s.u32 where bytes = 0xABCD\",\n                msg: \"Columns must be qualified in join expressions\",\n            },\n        ] {\n            let result = parse_and_type_sub(sql, &tx);\n            assert!(result.is_err(), \"{msg}\");\n        }\n    }\n}\n"
  },
  {
    "path": "crates/expr/src/errors.rs",
    "content": "use super::statement::InvalidVar;\nuse spacetimedb_lib::AlgebraicType;\nuse spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_sql_parser::ast::BinOp;\nuse spacetimedb_sql_parser::parser::errors::SqlParseError;\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum Unresolved {\n    #[error(\"`{0}` is not in scope\")]\n    Var(String),\n    #[error(\"no such table: `{0}`. If the table exists, it may be marked private.\")]\n    Table(String),\n    #[error(\"`{0}` does not have a field `{1}`\")]\n    Field(TableName, String),\n    #[error(\"Cannot resolve type for literal expression\")]\n    Literal,\n}\n\nimpl Unresolved {\n    /// Cannot resolve name\n    pub fn var(name: &str) -> Self {\n        Self::Var(name.to_owned())\n    }\n\n    /// Cannot resolve table name\n    pub fn table(name: &str) -> Self {\n        Self::Table(name.to_owned())\n    }\n\n    /// Cannot resolve field name within table\n    pub fn field(table: TableName, field: &str) -> Self {\n        Self::Field(table, field.to_owned())\n    }\n}\n\n#[derive(Error, Debug)]\npub enum InvalidWildcard {\n    #[error(\"SELECT * is not supported for joins\")]\n    Join,\n}\n\n#[derive(Error, Debug)]\npub enum Unsupported {\n    #[error(\"Column projections are not supported in subscriptions; Subscriptions must return a table type\")]\n    ReturnType,\n    #[error(\"Unsupported expression in projection\")]\n    ProjectExpr,\n}\n\n// TODO: It might be better to return the missing/extra fields\n#[derive(Error, Debug)]\n#[error(\"Inserting a row with {values} values into `{table}` which has {fields} fields\")]\npub struct InsertValuesError {\n    pub table: TableName,\n    pub values: usize,\n    pub fields: usize,\n}\n\n// TODO: It might be better to return the missing/extra fields\n#[derive(Error, Debug)]\n#[error(\"The number of fields ({nfields}) in the INSERT does not match the number of columns ({ncols}) of the table `{table}`\")]\npub struct InsertFieldsError {\n    pub table: TableName,\n    pub ncols: usize,\n    pub nfields: usize,\n}\n\n#[derive(Debug, Error)]\n#[error(\"Invalid binary operator `{op}` for type `{ty}`\")]\npub struct InvalidOp {\n    op: BinOp,\n    ty: String,\n}\n\nimpl InvalidOp {\n    pub fn new(op: BinOp, ty: &AlgebraicType) -> Self {\n        Self {\n            op,\n            ty: fmt_algebraic_type(ty).to_string(),\n        }\n    }\n}\n\n#[derive(Error, Debug)]\n#[error(\"The literal expression `{literal}` cannot be parsed as type `{ty}`\")]\npub struct InvalidLiteral {\n    literal: String,\n    ty: String,\n}\n\nimpl InvalidLiteral {\n    pub fn new(literal: String, expected: &AlgebraicType) -> Self {\n        Self {\n            literal,\n            ty: fmt_algebraic_type(expected).to_string(),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\n#[error(\"Unexpected type: (expected) {expected} != {inferred} (inferred)\")]\npub struct UnexpectedType {\n    expected: String,\n    inferred: String,\n}\n\nimpl UnexpectedType {\n    pub fn new(expected: &AlgebraicType, inferred: &AlgebraicType) -> Self {\n        Self {\n            expected: fmt_algebraic_type(expected).to_string(),\n            inferred: fmt_algebraic_type(inferred).to_string(),\n        }\n    }\n}\n\n#[derive(Debug, Error)]\n#[error(\"Duplicate name `{0}`\")]\npub struct DuplicateName(pub RawIdentifier);\n\n#[derive(Debug, Error)]\n#[error(\"`filter!` does not support column projections; Must return table rows\")]\npub struct FilterReturnType;\n\n#[derive(Debug, Error)]\n#[error(\"`{view_name}` is a view; DML on views is not supported\")]\npub struct DmlOnView {\n    pub view_name: TableName,\n}\n\n#[derive(Error, Debug)]\npub enum TypingError {\n    #[error(transparent)]\n    Unsupported(#[from] Unsupported),\n    #[error(transparent)]\n    Unresolved(#[from] Unresolved),\n    #[error(transparent)]\n    InvalidVar(#[from] InvalidVar),\n    #[error(transparent)]\n    InsertValues(#[from] InsertValuesError),\n    #[error(transparent)]\n    InsertFields(#[from] InsertFieldsError),\n    #[error(transparent)]\n    ParseError(#[from] SqlParseError),\n\n    #[error(transparent)]\n    DmlOnView(#[from] DmlOnView),\n    #[error(transparent)]\n    InvalidOp(#[from] InvalidOp),\n    #[error(transparent)]\n    Literal(#[from] InvalidLiteral),\n    #[error(transparent)]\n    Unexpected(#[from] UnexpectedType),\n    #[error(transparent)]\n    Wildcard(#[from] InvalidWildcard),\n    #[error(transparent)]\n    DuplicateName(#[from] DuplicateName),\n    #[error(transparent)]\n    FilterReturnType(#[from] FilterReturnType),\n}\n"
  },
  {
    "path": "crates/expr/src/expr.rs",
    "content": "use spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_lib::{query::Delta, AlgebraicType, AlgebraicValue};\nuse spacetimedb_primitives::{TableId, ViewId};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_schema::{identifier::Identifier, schema::TableOrViewSchema};\nuse spacetimedb_sql_parser::ast::{BinOp, LogOp};\nuse std::sync::Arc;\n\npub trait CollectViews {\n    fn collect_views(&self, views: &mut HashSet<ViewId>);\n}\n\nimpl<T: CollectViews> CollectViews for Arc<T> {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        self.as_ref().collect_views(views);\n    }\n}\n\nimpl<T: CollectViews> CollectViews for Vec<T> {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        for item in self {\n            item.collect_views(views);\n        }\n    }\n}\n\n/// A projection is the root of any relational expression.\n/// This type represents a projection that returns relvars.\n///\n/// For example:\n///\n/// ```sql\n/// select * from t\n/// ```\n///\n/// and\n///\n/// ```sql\n/// select t.* from t join s ...\n/// ```\n#[derive(Debug, PartialEq, Eq)]\npub enum ProjectName {\n    None(RelExpr),\n    Some(RelExpr, RawIdentifier),\n}\n\nimpl CollectViews for ProjectName {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        match self {\n            Self::None(expr) | Self::Some(expr, _) => expr.collect_views(views),\n        }\n    }\n}\n\nimpl ProjectName {\n    /// Unwrap the outer projection, returning the inner expression\n    pub fn unwrap(self) -> RelExpr {\n        match self {\n            Self::None(expr) | Self::Some(expr, _) => expr,\n        }\n    }\n\n    /// What is the name of the return table?\n    /// This is either the table name itself or its alias.\n    pub fn return_name(&self) -> Option<&RawIdentifier> {\n        match self {\n            Self::None(input) => input.return_name(),\n            Self::Some(_, name) => Some(name),\n        }\n    }\n\n    /// The [`TableOrViewSchema`] of the returned rows.\n    /// Note this expression returns rows from a relvar.\n    /// Hence it this method should never return [None].\n    pub fn return_table(&self) -> Option<&TableOrViewSchema> {\n        match self {\n            Self::None(input) => input.return_table(),\n            Self::Some(input, alias) => input.find_table_schema(alias),\n        }\n    }\n\n    /// The [TableId] of the returned rows.\n    /// Note this expression returns rows from a relvar.\n    /// Hence it this method should never return [None].\n    pub fn return_table_id(&self) -> Option<TableId> {\n        match self {\n            Self::None(input) => input.return_table_id(),\n            Self::Some(input, alias) => input.find_table_id(alias),\n        }\n    }\n\n    /// Iterate over the returned column names and types\n    pub fn for_each_return_field(&self, mut f: impl FnMut(&Identifier, &AlgebraicType)) {\n        if let Some(schema) = self.return_table() {\n            for schema in schema.public_columns() {\n                f(&schema.col_name, &schema.col_type);\n            }\n        }\n    }\n}\n\n/// A projection is the root of any relational expression.\n/// This type represents a projection that returns fields.\n///\n/// For example:\n///\n/// ```sql\n/// select a, b from t\n/// ```\n///\n/// and\n///\n/// ```sql\n/// select t.a as x from t join s ...\n/// ```\n///\n/// Note that RLS takes a single expression and produces a list of expressions.\n/// Hence why these variants take lists rather than single expressions.\n///\n/// Why does RLS take an expression and produce a list?\n///\n/// There may be multiple RLS rules associated to a single table.\n/// Semantically these rules represent a UNION over that table,\n/// and this corresponds to a UNION in the original expression.\n///\n/// TODO: We should model the UNION explicitly in the physical plan.\n///\n/// Ex.\n///\n/// Let's say we have the following rules for the `users` table:\n/// ```rust\n/// use spacetimedb::client_visibility_filter;\n/// use spacetimedb::Filter;\n///\n/// #[client_visibility_filter]\n/// const USER_FILTER: Filter = Filter::Sql(\n///     \"SELECT users.* FROM users WHERE identity = :sender\"\n/// );\n///\n/// #[client_visibility_filter]\n/// const ADMIN_FILTER: Filter = Filter::Sql(\n///     \"SELECT users.* FROM users JOIN admins\"\n/// );\n/// ```\n///\n/// The user query\n/// ```sql\n/// SELECT * FROM users WHERE level > 5\n/// ```\n///\n/// essentially resolves to\n/// ```sql\n/// SELECT users.*\n/// FROM users\n/// WHERE identity = :sender AND level > 5\n///\n/// UNION ALL\n///\n/// SELECT users.*\n/// FROM users JOIN admins\n/// WHERE users.level > 5\n/// ```\n#[derive(Debug)]\npub enum ProjectList {\n    Name(Vec<ProjectName>),\n    List(Vec<RelExpr>, Vec<(RawIdentifier, FieldProject)>),\n    Limit(Box<ProjectList>, u64),\n    Agg(Vec<RelExpr>, AggType, RawIdentifier, AlgebraicType),\n}\n\n#[derive(Debug)]\npub enum AggType {\n    Count,\n}\n\nimpl CollectViews for ProjectList {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        match self {\n            Self::Limit(proj, _) => {\n                proj.collect_views(views);\n            }\n            Self::Name(exprs) => {\n                for expr in exprs {\n                    expr.collect_views(views);\n                }\n            }\n            Self::List(exprs, _) | Self::Agg(exprs, ..) => {\n                for expr in exprs {\n                    expr.collect_views(views);\n                }\n            }\n        }\n    }\n}\n\nimpl ProjectList {\n    /// Does this expression project a single relvar?\n    /// If so, we return it's [`TableOrViewSchema`].\n    /// If not, it projects a list of columns, so we return [None].\n    pub fn return_table(&self) -> Option<&TableOrViewSchema> {\n        match self {\n            Self::Name(project) => project.first().and_then(|expr| expr.return_table()),\n            Self::Limit(input, _) => input.return_table(),\n            Self::List(..) | Self::Agg(..) => None,\n        }\n    }\n\n    /// Does this expression project a single relvar?\n    /// If so, we return it's [TableId].\n    /// If not, it projects a list of columns, so we return [None].\n    pub fn return_table_id(&self) -> Option<TableId> {\n        match self {\n            Self::Name(project) => project.first().and_then(|expr| expr.return_table_id()),\n            Self::Limit(input, _) => input.return_table_id(),\n            Self::List(..) | Self::Agg(..) => None,\n        }\n    }\n\n    /// Iterate over the projected column names and types\n    pub fn for_each_return_field(&self, mut f: impl FnMut(&RawIdentifier, &AlgebraicType)) {\n        match self {\n            Self::Name(input) => {\n                input\n                    .first()\n                    .inspect(|expr| expr.for_each_return_field(|n, at| f(n.as_raw(), at)));\n            }\n            Self::Limit(input, _) => {\n                input.for_each_return_field(f);\n            }\n            Self::List(_, fields) => {\n                for (name, FieldProject { ty, .. }) in fields {\n                    f(name, ty);\n                }\n            }\n            Self::Agg(_, _, name, ty) => f(name, ty),\n        }\n    }\n}\n\n/// A logical relational expression\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum RelExpr {\n    /// A relvar or table reference\n    RelVar(Relvar),\n    /// A logical select for filter\n    Select(Box<RelExpr>, Expr),\n    /// A left deep binary cross product\n    LeftDeepJoin(LeftDeepJoin),\n    /// A left deep binary equi-join\n    EqJoin(LeftDeepJoin, FieldProject, FieldProject),\n}\n\n/// A table reference\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Relvar {\n    /// The table schema of this relvar\n    pub schema: Arc<TableOrViewSchema>,\n    /// The name of this relvar\n    pub alias: RawIdentifier,\n    /// Does this relvar represent a delta table?\n    pub delta: Option<Delta>,\n}\n\nimpl CollectViews for RelExpr {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        self.visit(&mut |expr| {\n            if let Self::RelVar(Relvar { schema, .. }) = expr\n                && let Some(info) = &schema.view_info\n            {\n                views.insert(info.view_id);\n            }\n        });\n    }\n}\n\nimpl RelExpr {\n    /// Walk the expression tree and call `f` on each node\n    pub fn visit(&self, f: &mut impl FnMut(&Self)) {\n        f(self);\n        match self {\n            Self::Select(lhs, _)\n            | Self::LeftDeepJoin(LeftDeepJoin { lhs, .. })\n            | Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => {\n                lhs.visit(f);\n            }\n            Self::RelVar(..) => {}\n        }\n    }\n\n    /// Walk the expression tree and call `f` on each node\n    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {\n        f(self);\n        match self {\n            Self::Select(lhs, _)\n            | Self::LeftDeepJoin(LeftDeepJoin { lhs, .. })\n            | Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => {\n                lhs.visit_mut(f);\n            }\n            Self::RelVar(..) => {}\n        }\n    }\n\n    /// The number of fields this expression returns\n    pub fn nfields(&self) -> usize {\n        match self {\n            Self::RelVar(..) => 1,\n            Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => join.lhs.nfields() + 1,\n            Self::Select(input, _) => input.nfields(),\n        }\n    }\n\n    /// Does this expression return this field?\n    pub fn has_field(&self, field: &str) -> bool {\n        match self {\n            Self::RelVar(Relvar { alias, .. }) => alias.as_ref() == field,\n            Self::LeftDeepJoin(join) | Self::EqJoin(join, ..) => {\n                join.rhs.alias.as_ref() == field || join.lhs.has_field(field)\n            }\n            Self::Select(input, _) => input.has_field(field),\n        }\n    }\n\n    /// Return the [`TableOrViewSchema`] for a relvar in the expression\n    pub fn find_table_schema(&self, alias: &str) -> Option<&TableOrViewSchema> {\n        match self {\n            Self::RelVar(relvar) if relvar.alias.as_ref() == alias => Some(&relvar.schema),\n            Self::Select(input, _) => input.find_table_schema(alias),\n            Self::EqJoin(LeftDeepJoin { rhs, .. }, ..) if rhs.alias.as_ref() == alias => Some(&rhs.schema),\n            Self::EqJoin(LeftDeepJoin { lhs, .. }, ..) => lhs.find_table_schema(alias),\n            Self::LeftDeepJoin(LeftDeepJoin { rhs, .. }) if rhs.alias.as_ref() == alias => Some(&rhs.schema),\n            Self::LeftDeepJoin(LeftDeepJoin { lhs, .. }) => lhs.find_table_schema(alias),\n            _ => None,\n        }\n    }\n\n    /// Return the [TableId] for a relvar in the expression\n    pub fn find_table_id(&self, alias: &str) -> Option<TableId> {\n        self.find_table_schema(alias).map(|schema| schema.table_id)\n    }\n\n    /// Does this expression return a single relvar?\n    /// If so, return it's [`TableOrViewSchema`], otherwise return [None].\n    pub fn return_table(&self) -> Option<&TableOrViewSchema> {\n        match self {\n            Self::RelVar(Relvar { schema, .. }) => Some(schema),\n            Self::Select(input, _) => input.return_table(),\n            _ => None,\n        }\n    }\n\n    /// Does this expression return a single relvar?\n    /// If so, return it's [TableId], otherwise return [None].\n    pub fn return_table_id(&self) -> Option<TableId> {\n        self.return_table().map(|schema| schema.table_id)\n    }\n\n    /// Does this expression return a single relvar?\n    /// If so, return its name or equivalently its alias.\n    pub fn return_name(&self) -> Option<&RawIdentifier> {\n        match self {\n            Self::RelVar(Relvar { alias, .. }) => Some(alias),\n            Self::Select(input, _) => input.return_name(),\n            _ => None,\n        }\n    }\n}\n\n/// A left deep binary cross product\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LeftDeepJoin {\n    /// The lhs is recursive\n    pub lhs: Box<RelExpr>,\n    /// The rhs is a relvar\n    pub rhs: Relvar,\n}\n\n/// A typed scalar expression\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Expr {\n    /// A binary expression\n    BinOp(BinOp, Box<Expr>, Box<Expr>),\n    /// A binary logic expression\n    LogOp(LogOp, Box<Expr>, Box<Expr>),\n    /// A typed literal expression\n    Value(AlgebraicValue, AlgebraicType),\n    /// A field projection\n    Field(FieldProject),\n}\n\nimpl Expr {\n    /// Walk the expression tree and call `f` on each node\n    pub fn visit(&self, f: &impl Fn(&Self)) {\n        f(self);\n        match self {\n            Self::BinOp(_, a, b) | Self::LogOp(_, a, b) => {\n                a.visit(f);\n                b.visit(f);\n            }\n            Self::Value(..) | Self::Field(..) => {}\n        }\n    }\n\n    /// Walk the expression tree and call `f` on each node\n    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {\n        f(self);\n        match self {\n            Self::BinOp(_, a, b) | Self::LogOp(_, a, b) => {\n                a.visit_mut(f);\n                b.visit_mut(f);\n            }\n            Self::Value(..) | Self::Field(..) => {}\n        }\n    }\n\n    /// A literal boolean value\n    pub const fn bool(v: bool) -> Self {\n        Self::Value(AlgebraicValue::Bool(v), AlgebraicType::Bool)\n    }\n\n    /// A literal string value\n    pub const fn str(v: Box<str>) -> Self {\n        Self::Value(AlgebraicValue::String(v), AlgebraicType::String)\n    }\n\n    /// The [AlgebraicType] of this scalar expression\n    pub fn ty(&self) -> &AlgebraicType {\n        match self {\n            Self::BinOp(..) | Self::LogOp(..) => &AlgebraicType::Bool,\n            Self::Value(_, ty) | Self::Field(FieldProject { ty, .. }) => ty,\n        }\n    }\n}\n\n/// A typed qualified field projection\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct FieldProject {\n    pub table: RawIdentifier,\n    pub field: usize,\n    pub ty: AlgebraicType,\n}\n"
  },
  {
    "path": "crates/expr/src/lib.rs",
    "content": "use crate::statement::Statement;\nuse anyhow::anyhow;\nuse anyhow::bail;\nuse anyhow::Context;\nuse bigdecimal::BigDecimal;\nuse bigdecimal::ToPrimitive;\nuse check::{Relvars, TypingResult};\nuse errors::{DuplicateName, InvalidLiteral, InvalidOp, InvalidWildcard, UnexpectedType, Unresolved};\nuse ethnum::i256;\nuse ethnum::u256;\nuse expr::AggType;\nuse expr::{Expr, FieldProject, ProjectList, ProjectName, RelExpr};\nuse spacetimedb_data_structures::map::HashCollectionExt as _;\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_lib::ser::Serialize;\nuse spacetimedb_lib::Timestamp;\nuse spacetimedb_lib::{from_hex_pad, AlgebraicType, AlgebraicValue, ConnectionId, Identity};\nuse spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;\nuse spacetimedb_sats::algebraic_value::ser::ValueSerializer;\nuse spacetimedb_sats::uuid::Uuid;\nuse spacetimedb_schema::schema::ColumnSchema;\nuse spacetimedb_sql_parser::ast::{self, BinOp, ProjectElem, SqlExpr, SqlIdent, SqlLiteral};\nuse spacetimedb_sql_parser::parser::recursion;\nuse std::{ops::Deref, str::FromStr};\n\npub mod check;\npub mod errors;\npub mod expr;\npub mod rls;\npub mod statement;\n\n/// Type check and lower a [SqlExpr]\npub(crate) fn type_select(input: RelExpr, expr: SqlExpr, vars: &Relvars) -> TypingResult<RelExpr> {\n    Ok(RelExpr::Select(\n        Box::new(input),\n        type_expr(vars, expr, Some(&AlgebraicType::Bool))?,\n    ))\n}\n\n/// Type check a LIMIT clause\npub(crate) fn type_limit(input: ProjectList, limit: &str) -> TypingResult<ProjectList> {\n    Ok(\n        parse_int(limit, AlgebraicType::U64, BigDecimal::to_u64, AlgebraicValue::U64)\n            .map_err(|_| InvalidLiteral::new(limit.to_owned(), &AlgebraicType::U64))\n            .and_then(|n| {\n                n.into_u64()\n                    .map_err(|_| InvalidLiteral::new(limit.to_owned(), &AlgebraicType::U64))\n            })\n            .map(|n| ProjectList::Limit(Box::new(input), n))?,\n    )\n}\n\n/// Type check and lower a [ast::Project]\npub(crate) fn type_proj(input: RelExpr, proj: ast::Project, vars: &Relvars) -> TypingResult<ProjectList> {\n    match proj {\n        ast::Project::Star(None) if input.nfields() > 1 => Err(InvalidWildcard::Join.into()),\n        ast::Project::Star(None) => Ok(ProjectList::Name(vec![ProjectName::None(input)])),\n        ast::Project::Star(Some(SqlIdent(var))) if input.has_field(&var) => {\n            Ok(ProjectList::Name(vec![ProjectName::Some(input, var)]))\n        }\n        ast::Project::Star(Some(SqlIdent(var))) => Err(Unresolved::var(&var).into()),\n        ast::Project::Count(SqlIdent(alias)) => {\n            Ok(ProjectList::Agg(vec![input], AggType::Count, alias, AlgebraicType::U64))\n        }\n        ast::Project::Exprs(elems) => {\n            let mut projections = vec![];\n            let mut names = HashSet::new();\n\n            for ProjectElem(expr, SqlIdent(alias)) in elems {\n                if !names.insert(alias.clone()) {\n                    return Err(DuplicateName(alias.clone()).into());\n                }\n\n                if let Expr::Field(p) = type_expr(vars, expr.into(), None)? {\n                    projections.push((alias, p));\n                }\n            }\n\n            Ok(ProjectList::List(vec![input], projections))\n        }\n    }\n}\n\n// These types determine the size of each stack frame during type checking.\n// Changing their sizes will require updating the recursion limit to avoid stack overflows.\nconst _: () = assert!(size_of::<TypingResult<Expr>>() == 64);\nconst _: () = assert!(size_of::<SqlExpr>() == 32);\n\nfn _type_expr(vars: &Relvars, expr: SqlExpr, expected: Option<&AlgebraicType>, depth: usize) -> TypingResult<Expr> {\n    recursion::guard(depth, recursion::MAX_RECURSION_TYP_EXPR, \"expr::type_expr\")?;\n\n    match (expr, expected) {\n        (SqlExpr::Lit(SqlLiteral::Bool(v)), None | Some(AlgebraicType::Bool)) => Ok(Expr::bool(v)),\n        (SqlExpr::Lit(SqlLiteral::Bool(_)), Some(ty)) => Err(UnexpectedType::new(&AlgebraicType::Bool, ty).into()),\n        (SqlExpr::Lit(SqlLiteral::Str(_) | SqlLiteral::Num(_) | SqlLiteral::Hex(_)), None) => {\n            Err(Unresolved::Literal.into())\n        }\n        (SqlExpr::Lit(SqlLiteral::Str(v) | SqlLiteral::Num(v) | SqlLiteral::Hex(v)), Some(ty)) => Ok(Expr::Value(\n            parse(&v, ty).map_err(|_| InvalidLiteral::new(v.into_string(), ty))?,\n            ty.clone(),\n        )),\n        (SqlExpr::Field(SqlIdent(table), SqlIdent(field)), expected) => {\n            let table_type = vars.deref().get(&*table).ok_or_else(|| Unresolved::var(&table))?;\n            let ColumnSchema { col_pos, col_type, .. } = table_type\n                .as_ref()\n                .get_column_by_name_or_alias(&field)\n                .ok_or_else(|| Unresolved::var(&field))?;\n\n            if let Some(ty) = expected\n                && col_type != ty\n            {\n                return Err(UnexpectedType::new(col_type, ty).into());\n            }\n\n            Ok(Expr::Field(FieldProject {\n                table,\n                field: col_pos.idx(),\n                ty: col_type.clone(),\n            }))\n        }\n        (SqlExpr::Log(a, b, op), None | Some(AlgebraicType::Bool)) => {\n            let a = _type_expr(vars, *a, Some(&AlgebraicType::Bool), depth + 1)?;\n            let b = _type_expr(vars, *b, Some(&AlgebraicType::Bool), depth + 1)?;\n            Ok(Expr::LogOp(op, Box::new(a), Box::new(b)))\n        }\n        (SqlExpr::Bin(a, b, op), None | Some(AlgebraicType::Bool)) if matches!(&*a, SqlExpr::Lit(_)) => {\n            let b = _type_expr(vars, *b, None, depth + 1)?;\n            let a = _type_expr(vars, *a, Some(b.ty()), depth + 1)?;\n            if !op_supports_type(op, a.ty()) {\n                return Err(InvalidOp::new(op, a.ty()).into());\n            }\n            Ok(Expr::BinOp(op, Box::new(a), Box::new(b)))\n        }\n        (SqlExpr::Bin(a, b, op), None | Some(AlgebraicType::Bool)) => {\n            let a = _type_expr(vars, *a, None, depth + 1)?;\n            let b = _type_expr(vars, *b, Some(a.ty()), depth + 1)?;\n            if !op_supports_type(op, a.ty()) {\n                return Err(InvalidOp::new(op, a.ty()).into());\n            }\n            Ok(Expr::BinOp(op, Box::new(a), Box::new(b)))\n        }\n        (SqlExpr::Bin(..) | SqlExpr::Log(..), Some(ty)) => Err(UnexpectedType::new(&AlgebraicType::Bool, ty).into()),\n        // Both unqualified names as well as parameters are syntactic constructs.\n        // Unqualified names are qualified and parameters are resolved before type checking.\n        (SqlExpr::Var(_) | SqlExpr::Param(_), _) => unreachable!(),\n    }\n}\n\n/// Type check and lower a [SqlExpr] into a logical [Expr].\npub(crate) fn type_expr(vars: &Relvars, expr: SqlExpr, expected: Option<&AlgebraicType>) -> TypingResult<Expr> {\n    _type_expr(vars, expr, expected, 0)\n}\n\n/// Is this type compatible with this binary operator?\nfn op_supports_type(_op: BinOp, t: &AlgebraicType) -> bool {\n    t.is_bool()\n        || t.is_integer()\n        || t.is_float()\n        || t.is_string()\n        || t.is_bytes()\n        || t.is_identity()\n        || t.is_connection_id()\n        || t.is_timestamp()\n        || t.is_uuid()\n}\n\n/// Parse an integer literal into an [AlgebraicValue]\nfn parse_int<Int, Val, ToInt, ToVal>(\n    literal: &str,\n    ty: AlgebraicType,\n    to_int: ToInt,\n    to_val: ToVal,\n) -> anyhow::Result<AlgebraicValue>\nwhere\n    Int: Into<Val>,\n    ToInt: FnOnce(&BigDecimal) -> Option<Int>,\n    ToVal: FnOnce(Val) -> AlgebraicValue,\n{\n    // Why are we using an arbitrary precision type?\n    // For scientific notation as well as i256 and u256.\n    BigDecimal::from_str(literal)\n        .ok()\n        .filter(|decimal| decimal.is_integer())\n        .ok_or_else(|| anyhow!(\"{literal} is not an integer\"))\n        .map(|decimal| to_int(&decimal).map(|val| val.into()).map(to_val))\n        .transpose()\n        .ok_or_else(|| anyhow!(\"{literal} is out of bounds for type {}\", fmt_algebraic_type(&ty)))?\n}\n\n/// Parse a floating point literal into an [AlgebraicValue]\nfn parse_float<Float, Value, ToFloat, ToValue>(\n    literal: &str,\n    ty: AlgebraicType,\n    to_float: ToFloat,\n    to_value: ToValue,\n) -> anyhow::Result<AlgebraicValue>\nwhere\n    Float: Into<Value>,\n    ToFloat: FnOnce(&BigDecimal) -> Option<Float>,\n    ToValue: FnOnce(Value) -> AlgebraicValue,\n{\n    BigDecimal::from_str(literal)\n        .ok()\n        .and_then(|decimal| to_float(&decimal))\n        .map(|value| value.into())\n        .map(to_value)\n        .ok_or_else(|| anyhow!(\"{literal} is not a valid {}\", fmt_algebraic_type(&ty)))\n}\n\n/// Parses a source text literal as a particular type\npub(crate) fn parse(value: &str, ty: &AlgebraicType) -> anyhow::Result<AlgebraicValue> {\n    let to_timestamp = || {\n        Timestamp::parse_from_rfc3339(value)?\n            .serialize(ValueSerializer)\n            .with_context(|| \"Could not parse timestamp\")\n    };\n    let to_bytes = || {\n        from_hex_pad::<Vec<u8>, _>(value)\n            .map(|v| v.into_boxed_slice())\n            .map(AlgebraicValue::Bytes)\n            .with_context(|| \"Could not parse hex value\")\n    };\n    let to_identity = || {\n        Identity::from_hex(value)\n            .map(AlgebraicValue::from)\n            .with_context(|| \"Could not parse identity\")\n    };\n    let to_connection_id = || {\n        ConnectionId::from_hex(value)\n            .map(AlgebraicValue::from)\n            .with_context(|| \"Could not parse connection id\")\n    };\n    let to_uuid = || {\n        Uuid::parse_str(value)\n            .map(AlgebraicValue::from)\n            .map_err(|err| anyhow!(err))\n    };\n    let to_i256 = |decimal: &BigDecimal| {\n        i256::from_str_radix(\n            // Convert to decimal notation\n            &decimal.to_plain_string(),\n            10,\n        )\n        .ok()\n    };\n    let to_u256 = |decimal: &BigDecimal| {\n        u256::from_str_radix(\n            // Convert to decimal notation\n            &decimal.to_plain_string(),\n            10,\n        )\n        .ok()\n    };\n    match ty {\n        AlgebraicType::I8 => parse_int(\n            // Parse literal as I8\n            value,\n            AlgebraicType::I8,\n            BigDecimal::to_i8,\n            AlgebraicValue::I8,\n        ),\n        AlgebraicType::U8 => parse_int(\n            // Parse literal as U8\n            value,\n            AlgebraicType::U8,\n            BigDecimal::to_u8,\n            AlgebraicValue::U8,\n        ),\n        AlgebraicType::I16 => parse_int(\n            // Parse literal as I16\n            value,\n            AlgebraicType::I16,\n            BigDecimal::to_i16,\n            AlgebraicValue::I16,\n        ),\n        AlgebraicType::U16 => parse_int(\n            // Parse literal as U16\n            value,\n            AlgebraicType::U16,\n            BigDecimal::to_u16,\n            AlgebraicValue::U16,\n        ),\n        AlgebraicType::I32 => parse_int(\n            // Parse literal as I32\n            value,\n            AlgebraicType::I32,\n            BigDecimal::to_i32,\n            AlgebraicValue::I32,\n        ),\n        AlgebraicType::U32 => parse_int(\n            // Parse literal as U32\n            value,\n            AlgebraicType::U32,\n            BigDecimal::to_u32,\n            AlgebraicValue::U32,\n        ),\n        AlgebraicType::I64 => parse_int(\n            // Parse literal as I64\n            value,\n            AlgebraicType::I64,\n            BigDecimal::to_i64,\n            AlgebraicValue::I64,\n        ),\n        AlgebraicType::U64 => parse_int(\n            // Parse literal as U64\n            value,\n            AlgebraicType::U64,\n            BigDecimal::to_u64,\n            AlgebraicValue::U64,\n        ),\n        AlgebraicType::F32 => parse_float(\n            // Parse literal as F32\n            value,\n            AlgebraicType::F32,\n            BigDecimal::to_f32,\n            AlgebraicValue::F32,\n        ),\n        AlgebraicType::F64 => parse_float(\n            // Parse literal as F64\n            value,\n            AlgebraicType::F64,\n            BigDecimal::to_f64,\n            AlgebraicValue::F64,\n        ),\n        AlgebraicType::I128 => parse_int(\n            // Parse literal as I128\n            value,\n            AlgebraicType::I128,\n            BigDecimal::to_i128,\n            AlgebraicValue::I128,\n        ),\n        AlgebraicType::U128 => parse_int(\n            // Parse literal as U128\n            value,\n            AlgebraicType::U128,\n            BigDecimal::to_u128,\n            AlgebraicValue::U128,\n        ),\n        AlgebraicType::I256 => parse_int(\n            // Parse literal as I256\n            value,\n            AlgebraicType::I256,\n            to_i256,\n            AlgebraicValue::I256,\n        ),\n        AlgebraicType::U256 => parse_int(\n            // Parse literal as U256\n            value,\n            AlgebraicType::U256,\n            to_u256,\n            AlgebraicValue::U256,\n        ),\n        AlgebraicType::String => Ok(AlgebraicValue::String(value.into())),\n        t if t.is_timestamp() => to_timestamp(),\n        t if t.is_bytes() => to_bytes(),\n        t if t.is_identity() => to_identity(),\n        t if t.is_connection_id() => to_connection_id(),\n        t if t.is_uuid() => to_uuid(),\n        t => bail!(\"Literal values for type {} are not supported\", fmt_algebraic_type(t)),\n    }\n}\n\n/// The source of a statement\npub enum StatementSource {\n    Subscription,\n    Query,\n}\n\n/// A statement context.\n///\n/// This is a wrapper around a statement, its source, and the original SQL text.\npub struct StatementCtx<'a> {\n    pub statement: Statement,\n    pub sql: &'a str,\n    pub source: StatementSource,\n}\n"
  },
  {
    "path": "crates/expr/src/rls.rs",
    "content": "use std::rc::Rc;\n\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sql_parser::ast::BinOp;\n\nuse crate::{\n    check::{parse_and_type_sub, SchemaView},\n    expr::{AggType, Expr, FieldProject, LeftDeepJoin, ProjectList, ProjectName, RelExpr, Relvar},\n};\n\n/// The main driver of RLS resolution for subscription queries.\n/// Mainly a wrapper around [resolve_views_for_expr].\npub fn resolve_views_for_sub(\n    tx: &impl SchemaView,\n    expr: ProjectName,\n    auth: &AuthCtx,\n    has_param: &mut bool,\n) -> anyhow::Result<Vec<ProjectName>> {\n    // RLS does not apply to the database owner\n    if auth.bypass_rls() {\n        return Ok(vec![expr]);\n    }\n\n    let Some(return_name) = expr.return_name().cloned() else {\n        anyhow::bail!(\"Could not determine return type during RLS resolution\")\n    };\n\n    // Unwrap the underlying `RelExpr`\n    let expr = match expr {\n        ProjectName::None(expr) | ProjectName::Some(expr, _) => expr,\n    };\n\n    resolve_views_for_expr(\n        tx,\n        expr,\n        // Do not ignore the return table when checking for RLS rules\n        None,\n        // Resolve list is empty as we are not yet resolving any RLS rules\n        Rc::new(ResolveList::None),\n        has_param,\n        &mut 0,\n        auth,\n    )\n    .map(|fragments| {\n        fragments\n            .into_iter()\n            // The expanded fragments could be join trees,\n            // so wrap each of them in an outer project.\n            .map(|expr| ProjectName::Some(expr, return_name.clone()))\n            .collect()\n    })\n}\n\n/// The main driver of RLS resolution for sql queries.\n/// Mainly a wrapper around [resolve_views_for_expr].\npub fn resolve_views_for_sql(tx: &impl SchemaView, expr: ProjectList, auth: &AuthCtx) -> anyhow::Result<ProjectList> {\n    // RLS does not apply to the database owner\n    if auth.bypass_rls() {\n        return Ok(expr);\n    }\n    // The subscription language is a subset of the sql language.\n    // Use the subscription helper if this is a compliant expression.\n    // Use the generic resolver otherwise.\n    let resolve_for_sub = |expr| resolve_views_for_sub(tx, expr, auth, &mut false);\n    let resolve_for_sql = |expr| {\n        resolve_views_for_expr(\n            // Use all default values\n            tx,\n            expr,\n            None,\n            Rc::new(ResolveList::None),\n            &mut false,\n            &mut 0,\n            auth,\n        )\n    };\n    match expr {\n        ProjectList::Limit(expr, n) => Ok(ProjectList::Limit(Box::new(resolve_views_for_sql(tx, *expr, auth)?), n)),\n        ProjectList::Name(exprs) => Ok(ProjectList::Name(\n            exprs\n                .into_iter()\n                .map(resolve_for_sub)\n                .collect::<Result<Vec<_>, _>>()?\n                .into_iter()\n                .flatten()\n                .collect(),\n        )),\n        ProjectList::List(exprs, fields) => Ok(ProjectList::List(\n            exprs\n                .into_iter()\n                .map(resolve_for_sql)\n                .collect::<Result<Vec<_>, _>>()?\n                .into_iter()\n                .flatten()\n                .collect(),\n            fields,\n        )),\n        ProjectList::Agg(exprs, AggType::Count, name, ty) => Ok(ProjectList::Agg(\n            exprs\n                .into_iter()\n                .map(resolve_for_sql)\n                .collect::<Result<Vec<_>, _>>()?\n                .into_iter()\n                .flatten()\n                .collect(),\n            AggType::Count,\n            name,\n            ty,\n        )),\n    }\n}\n\n/// A list for detecting cycles during RLS resolution.\nenum ResolveList {\n    None,\n    Some(TableId, Rc<ResolveList>),\n}\n\nimpl ResolveList {\n    fn new(table_id: TableId, list: Rc<Self>) -> Rc<Self> {\n        Rc::new(Self::Some(table_id, list))\n    }\n\n    fn contains(&self, table_id: &TableId) -> bool {\n        match self {\n            Self::None => false,\n            Self::Some(id, suffix) if id != table_id => suffix.contains(table_id),\n            Self::Some(..) => true,\n        }\n    }\n}\n\n/// The main utility responsible for view resolution.\n///\n/// But what is view resolution and why do we need it?\n///\n/// A view is a named query that can be referenced as though it were just a regular table.\n/// In SpacetimeDB, Row Level Security (RLS) is implemented using views.\n/// We must resolve/expand these views in order to guarantee the correct access controls.\n///\n/// Before we discuss the implementation, a quick word on `return_table_id`.\n///\n/// Why do we care about it?\n/// What does it mean for it to be `None`?\n///\n/// If this IS NOT a user query, it must be a view definition.\n/// In SpacetimeDB this means we're expanding an RLS filter.\n/// RLS filters cannot be self-referential, meaning that within a filter,\n/// we cannot recursively expand references to its return table.\n///\n/// However, a `None` value implies that this expression is a user query,\n/// and so we should attempt to expand references to the return table.\n///\n/// Now back to the implementation.\n///\n/// Take the following join tree as an example:\n/// ```text\n///     x\n///    / \\\n///   x   c\n///  / \\\n/// a   b\n/// ```\n///\n/// Let's assume b is a view with the following structure:\n/// ```text\n///     x\n///    / \\\n///   x   f\n///  / \\\n/// d   e\n/// ```\n///\n/// Logically we just want to expand the tree like so:\n/// ```text\n///     x\n///    / \\\n///   x   c\n///  / \\\n/// a   x\n///    / \\\n///   x   f\n///  / \\\n/// d   e\n/// ```\n///\n/// However the join trees at this level are left deep.\n/// To maintain this invariant, the correct expansion would be:\n/// ```text\n///         x\n///        / \\\n///       x   c\n///      / \\\n///     x   f\n///    / \\\n///   x   e\n///  / \\\n/// a   d\n/// ```\n///\n/// That is, the subtree whose root is the left sibling of the node being expanded,\n/// i.e. the subtree rooted at `a` in the above example,\n/// must be pushed below the leftmost leaf node of the view expansion.\nfn resolve_views_for_expr(\n    tx: &impl SchemaView,\n    view: RelExpr,\n    return_table_id: Option<TableId>,\n    resolving: Rc<ResolveList>,\n    has_param: &mut bool,\n    suffix: &mut usize,\n    auth: &AuthCtx,\n) -> anyhow::Result<Vec<RelExpr>> {\n    let is_return_table = |relvar: &Relvar| return_table_id.is_some_and(|id| relvar.schema.table_id == id);\n\n    // Collect the table ids queried by this view.\n    // Ignore the id of the return table, since RLS views cannot be recursive.\n    let mut names = vec![];\n    view.visit(&mut |expr| match expr {\n        RelExpr::RelVar(rhs)\n        | RelExpr::LeftDeepJoin(LeftDeepJoin { rhs, .. })\n        | RelExpr::EqJoin(LeftDeepJoin { rhs, .. }, ..)\n            if !is_return_table(rhs) =>\n        {\n            names.push((rhs.schema.table_id, rhs.alias.clone()));\n        }\n        _ => {}\n    });\n\n    // Are we currently resolving any of them?\n    if let Some(table_id) = names\n        .iter()\n        .map(|(table_id, _)| table_id)\n        .find(|table_id| resolving.contains(table_id))\n    {\n        anyhow::bail!(\"Discovered cyclic dependency when resolving RLS rules for table id `{table_id}`\");\n    }\n\n    let return_name = |expr: &ProjectName| {\n        expr.return_name()\n            .map(|name| name.to_owned())\n            .ok_or_else(|| anyhow::anyhow!(\"Could not resolve table reference in RLS filter\"))\n    };\n\n    let mut view_def_fragments = vec![];\n\n    for (table_id, alias) in names {\n        let mut view_fragments = vec![];\n\n        for sql in tx.rls_rules_for_table(table_id)? {\n            // Parse and type check the RLS filter\n            let (expr, is_parameterized) = parse_and_type_sub(&sql, tx, auth)?;\n\n            // Are any of the RLS rules parameterized?\n            *has_param = *has_param || is_parameterized;\n\n            // We need to know which relvar is being returned for alpha-renaming\n            let return_name = return_name(&expr)?;\n\n            // Resolve views within the RLS filter itself\n            let fragments = resolve_views_for_expr(\n                tx,\n                expr.unwrap(),\n                Some(table_id),\n                ResolveList::new(table_id, resolving.clone()),\n                has_param,\n                suffix,\n                auth,\n            )?;\n\n            // Run alpha conversion on each view definition\n            alpha_rename_fragments(\n                // The revlar returned from the inner expression\n                &return_name,\n                // Its corresponding alias in the outer expression\n                &alias,\n                fragments,\n                &mut view_fragments,\n                suffix,\n            );\n        }\n\n        if !view_fragments.is_empty() {\n            view_def_fragments.push((table_id, alias, view_fragments));\n        }\n    }\n\n    /// After we collect all the necessary view definitions and run alpha conversion,\n    /// this function handles the actual replacement of the view with its definition.\n    fn expand_views(\n        expr: RelExpr,\n        view_def_fragments: &[(TableId, RawIdentifier, Vec<RelExpr>)],\n        out: &mut Vec<RelExpr>,\n    ) {\n        match view_def_fragments {\n            [] => out.push(expr),\n            [(table_id, alias, fragments), view_def_fragments @ ..] => {\n                for fragment in fragments {\n                    let expanded = expand_leaf(expr.clone(), *table_id, alias, fragment);\n                    expand_views(expanded, view_def_fragments, out);\n                }\n            }\n        }\n    }\n\n    let mut resolved = vec![];\n    expand_views(view, &view_def_fragments, &mut resolved);\n    Ok(resolved)\n}\n\n/// This is the main driver of alpha conversion.\n///\n/// For each expression that we alpha convert,\n/// we append a unique suffix to the names in that expression,\n/// with the one exception being the name of the return table.\n/// The return table is aliased in the outer expression,\n/// and so we use the same alias in the inner expression.\n///\n/// Ex.\n///\n/// Let `v` be a view defined as:\n/// ```sql\n/// SELECT r.* FROM r JOIN s ON r.id = s.id\n/// ```\n///\n/// Take the following user query:\n/// ```sql\n/// SELECT t.* FROM v JOIN t ON v.id = t.id WHERE v.x = 0\n/// ```\n///\n/// After alpha conversion, the expansion becomes:\n/// ```sql\n/// SELECT t.*\n/// FROM r AS v\n/// JOIN s AS s_1 ON v.id = s_1.id\n/// JOIN t AS t   ON t.id = v.id WHERE v.x = 0\n/// ```\nfn alpha_rename_fragments(\n    return_name: &RawIdentifier,\n    outer_alias: &RawIdentifier,\n    inputs: Vec<RelExpr>,\n    output: &mut Vec<RelExpr>,\n    suffix: &mut usize,\n) {\n    for mut fragment in inputs {\n        *suffix += 1;\n        alpha_rename(&mut fragment, &mut |name: &RawIdentifier| {\n            if name == return_name {\n                return outer_alias.clone();\n            }\n            RawIdentifier::new(name.to_string() + \"_\" + &suffix.to_string())\n        });\n        output.push(fragment);\n    }\n}\n\n/// When expanding a view, we must do an alpha conversion on the view definition.\n/// This involves renaming the table aliases before replacing the view reference.\nfn alpha_rename(expr: &mut RelExpr, f: &mut impl FnMut(&RawIdentifier) -> RawIdentifier) {\n    /// Helper for renaming a relvar\n    fn rename(relvar: &mut Relvar, f: &mut impl FnMut(&RawIdentifier) -> RawIdentifier) {\n        relvar.alias = f(&relvar.alias);\n    }\n    /// Helper for renaming a field reference\n    fn rename_field(field: &mut FieldProject, f: &mut impl FnMut(&RawIdentifier) -> RawIdentifier) {\n        field.table = f(&field.table);\n    }\n    expr.visit_mut(&mut |expr| match expr {\n        RelExpr::RelVar(rhs) | RelExpr::LeftDeepJoin(LeftDeepJoin { rhs, .. }) => {\n            rename(rhs, f);\n        }\n        RelExpr::EqJoin(LeftDeepJoin { rhs, .. }, a, b) => {\n            rename(rhs, f);\n            rename_field(a, f);\n            rename_field(b, f);\n        }\n        RelExpr::Select(_, expr) => {\n            expr.visit_mut(&mut |expr| {\n                if let Expr::Field(field) = expr {\n                    rename_field(field, f);\n                }\n            });\n        }\n    });\n}\n\n/// Extends a left deep join tree with another.\n///\n/// Ex.\n///\n/// Assume `expr` is given by:\n/// ```text\n///     x\n///    / \\\n///   x   f\n///  / \\\n/// d   e\n/// ```\n///\n/// Assume `with` is given by:\n/// ```text\n///     x\n///    / \\\n///   x   c\n///  / \\\n/// a   b\n/// ```\n///\n/// This function extends `expr` by pushing `with` to the left-most leaf node:\n/// ```text\n///           x\n///          / \\\n///         x   f\n///        / \\\n///       x   e\n///      / \\\n///     x   d\n///    / \\\n///   x   c\n///  / \\\n/// a   b\n/// ```\nfn extend_lhs(expr: RelExpr, with: RelExpr) -> RelExpr {\n    match expr {\n        RelExpr::RelVar(rhs) => RelExpr::LeftDeepJoin(LeftDeepJoin {\n            lhs: Box::new(with),\n            rhs,\n        }),\n        RelExpr::Select(input, expr) => RelExpr::Select(Box::new(extend_lhs(*input, with)), expr),\n        RelExpr::LeftDeepJoin(join) => RelExpr::LeftDeepJoin(LeftDeepJoin {\n            lhs: Box::new(extend_lhs(*join.lhs, with)),\n            ..join\n        }),\n        RelExpr::EqJoin(join, a, b) => RelExpr::EqJoin(\n            LeftDeepJoin {\n                lhs: Box::new(extend_lhs(*join.lhs, with)),\n                ..join\n            },\n            a,\n            b,\n        ),\n    }\n}\n\n/// Replaces the leaf node determined by `table_id` and `alias` with the subtree `with`.\n/// Ensures the expanded tree stays left deep.\nfn expand_leaf(expr: RelExpr, table_id: TableId, alias: &str, with: &RelExpr) -> RelExpr {\n    let ok = |relvar: &Relvar| relvar.schema.table_id == table_id && relvar.alias.as_ref() == alias;\n    match expr {\n        RelExpr::RelVar(relvar, ..) if ok(&relvar) => with.clone(),\n        RelExpr::RelVar(..) => expr,\n        RelExpr::Select(input, expr) => RelExpr::Select(Box::new(expand_leaf(*input, table_id, alias, with)), expr),\n        RelExpr::LeftDeepJoin(join) if ok(&join.rhs) => extend_lhs(with.clone(), *join.lhs),\n        RelExpr::LeftDeepJoin(LeftDeepJoin { lhs, rhs }) => RelExpr::LeftDeepJoin(LeftDeepJoin {\n            lhs: Box::new(expand_leaf(*lhs, table_id, alias, with)),\n            rhs,\n        }),\n        RelExpr::EqJoin(join, a, b) if ok(&join.rhs) => RelExpr::Select(\n            Box::new(extend_lhs(with.clone(), *join.lhs)),\n            Expr::BinOp(BinOp::Eq, Box::new(Expr::Field(a)), Box::new(Expr::Field(b))),\n        ),\n        RelExpr::EqJoin(LeftDeepJoin { lhs, rhs }, a, b) => RelExpr::EqJoin(\n            LeftDeepJoin {\n                lhs: Box::new(expand_leaf(*lhs, table_id, alias, with)),\n                rhs,\n            },\n            a,\n            b,\n        ),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use pretty_assertions as pretty;\n\n    use spacetimedb_lib::{identity::AuthCtx, AlgebraicType, AlgebraicValue, Identity, ProductType};\n    use spacetimedb_primitives::TableId;\n    use spacetimedb_schema::{\n        def::ModuleDef,\n        schema::{Schema, TableOrViewSchema, TableSchema},\n    };\n    use spacetimedb_sql_parser::ast::BinOp;\n\n    use crate::{\n        check::{parse_and_type_sub, test_utils::build_module_def, SchemaView},\n        expr::{Expr, FieldProject, LeftDeepJoin, ProjectName, RelExpr, Relvar},\n    };\n\n    use super::resolve_views_for_sub;\n\n    pub struct SchemaViewer(pub ModuleDef);\n\n    impl SchemaView for SchemaViewer {\n        fn table_id(&self, name: &str) -> Option<TableId> {\n            match name {\n                \"users\" => Some(TableId(0)),\n                \"admins\" => Some(TableId(1)),\n                \"player\" => Some(TableId(2)),\n                _ => None,\n            }\n        }\n\n        fn schema_for_table(&self, table_id: TableId) -> Option<Arc<TableOrViewSchema>> {\n            match table_id.idx() {\n                0 => Some((TableId(0), \"users\")),\n                1 => Some((TableId(1), \"admins\")),\n                2 => Some((TableId(2), \"player\")),\n                _ => None,\n            }\n            .and_then(|(table_id, name)| {\n                self.0\n                    .table(name)\n                    .map(|def| Arc::new(TableSchema::from_module_def(&self.0, def, (), table_id)))\n                    .map(TableOrViewSchema::from)\n                    .map(Arc::new)\n            })\n        }\n\n        fn rls_rules_for_table(&self, table_id: TableId) -> anyhow::Result<Vec<Box<str>>> {\n            match table_id {\n                TableId(0) => Ok(vec![\"select * from users where identity = :sender\".into()]),\n                TableId(1) => Ok(vec![\"select * from admins where identity = :sender\".into()]),\n                TableId(2) => Ok(vec![\n                    \"select player.* from player join users u on player.id = u.id\".into(),\n                    \"select player.* from player join admins\".into(),\n                ]),\n                _ => Ok(vec![]),\n            }\n        }\n    }\n\n    fn module_def() -> ModuleDef {\n        build_module_def(vec![\n            (\n                \"users\",\n                ProductType::from([(\"identity\", AlgebraicType::identity()), (\"id\", AlgebraicType::U64)]),\n            ),\n            (\n                \"admins\",\n                ProductType::from([(\"identity\", AlgebraicType::identity()), (\"id\", AlgebraicType::U64)]),\n            ),\n            (\n                \"player\",\n                ProductType::from([(\"id\", AlgebraicType::U64), (\"level_num\", AlgebraicType::U64)]),\n            ),\n        ])\n    }\n\n    /// Parse, type check, and resolve RLS rules\n    fn resolve(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> anyhow::Result<Vec<ProjectName>> {\n        let (expr, _) = parse_and_type_sub(sql, tx, auth)?;\n        resolve_views_for_sub(tx, expr, auth, &mut false)\n    }\n\n    #[test]\n    fn test_rls_for_owner() -> anyhow::Result<()> {\n        let tx = SchemaViewer(module_def());\n        let auth = AuthCtx::new(Identity::ONE, Identity::ONE);\n        let sql = \"select * from users\";\n        let resolved = resolve(sql, &tx, &auth)?;\n\n        let users_schema = tx.schema(\"users\").unwrap();\n\n        pretty::assert_eq!(\n            resolved,\n            vec![ProjectName::None(RelExpr::RelVar(Relvar {\n                schema: users_schema,\n                alias: \"users\".into(),\n                delta: None,\n            }))]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_rls_for_non_owner() -> anyhow::Result<()> {\n        let tx = SchemaViewer(module_def());\n        let auth = AuthCtx::new(Identity::ZERO, Identity::ONE);\n        let sql = \"select * from users\";\n        let resolved = resolve(sql, &tx, &auth)?;\n\n        let users_schema = tx.schema(\"users\").unwrap();\n\n        pretty::assert_eq!(\n            resolved,\n            vec![ProjectName::Some(\n                RelExpr::Select(\n                    Box::new(RelExpr::RelVar(Relvar {\n                        schema: users_schema,\n                        alias: \"users\".into(),\n                        delta: None,\n                    })),\n                    Expr::BinOp(\n                        BinOp::Eq,\n                        Box::new(Expr::Field(FieldProject {\n                            table: \"users\".into(),\n                            field: 0,\n                            ty: AlgebraicType::identity(),\n                        })),\n                        Box::new(Expr::Value(Identity::ONE.into(), AlgebraicType::identity()))\n                    )\n                ),\n                \"users\".into()\n            )]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_multiple_rls_rules_for_table() -> anyhow::Result<()> {\n        let tx = SchemaViewer(module_def());\n        let auth = AuthCtx::new(Identity::ZERO, Identity::ONE);\n        let sql = \"select * from player where level_num = 5\";\n        let resolved = resolve(sql, &tx, &auth)?;\n\n        let users_schema = tx.schema(\"users\").unwrap();\n        let admins_schema = tx.schema(\"admins\").unwrap();\n        let player_schema = tx.schema(\"player\").unwrap();\n\n        pretty::assert_eq!(\n            resolved,\n            vec![\n                ProjectName::Some(\n                    RelExpr::Select(\n                        Box::new(RelExpr::Select(\n                            Box::new(RelExpr::Select(\n                                Box::new(RelExpr::LeftDeepJoin(LeftDeepJoin {\n                                    lhs: Box::new(RelExpr::RelVar(Relvar {\n                                        schema: player_schema.clone(),\n                                        alias: \"player\".into(),\n                                        delta: None,\n                                    })),\n                                    rhs: Relvar {\n                                        schema: users_schema.clone(),\n                                        alias: \"u_2\".into(),\n                                        delta: None,\n                                    },\n                                })),\n                                Expr::BinOp(\n                                    BinOp::Eq,\n                                    Box::new(Expr::Field(FieldProject {\n                                        table: \"u_2\".into(),\n                                        field: 0,\n                                        ty: AlgebraicType::identity(),\n                                    })),\n                                    Box::new(Expr::Value(Identity::ONE.into(), AlgebraicType::identity())),\n                                ),\n                            )),\n                            Expr::BinOp(\n                                BinOp::Eq,\n                                Box::new(Expr::Field(FieldProject {\n                                    table: \"player\".into(),\n                                    field: 0,\n                                    ty: AlgebraicType::U64,\n                                })),\n                                Box::new(Expr::Field(FieldProject {\n                                    table: \"u_2\".into(),\n                                    field: 1,\n                                    ty: AlgebraicType::U64,\n                                })),\n                            ),\n                        )),\n                        Expr::BinOp(\n                            BinOp::Eq,\n                            Box::new(Expr::Field(FieldProject {\n                                table: \"player\".into(),\n                                field: 1,\n                                ty: AlgebraicType::U64,\n                            })),\n                            Box::new(Expr::Value(AlgebraicValue::U64(5), AlgebraicType::U64)),\n                        ),\n                    ),\n                    \"player\".into(),\n                ),\n                ProjectName::Some(\n                    RelExpr::Select(\n                        Box::new(RelExpr::Select(\n                            Box::new(RelExpr::LeftDeepJoin(LeftDeepJoin {\n                                lhs: Box::new(RelExpr::RelVar(Relvar {\n                                    schema: player_schema.clone(),\n                                    alias: \"player\".into(),\n                                    delta: None,\n                                })),\n                                rhs: Relvar {\n                                    schema: admins_schema.clone(),\n                                    alias: \"admins_4\".into(),\n                                    delta: None,\n                                },\n                            })),\n                            Expr::BinOp(\n                                BinOp::Eq,\n                                Box::new(Expr::Field(FieldProject {\n                                    table: \"admins_4\".into(),\n                                    field: 0,\n                                    ty: AlgebraicType::identity(),\n                                })),\n                                Box::new(Expr::Value(Identity::ONE.into(), AlgebraicType::identity())),\n                            ),\n                        )),\n                        Expr::BinOp(\n                            BinOp::Eq,\n                            Box::new(Expr::Field(FieldProject {\n                                table: \"player\".into(),\n                                field: 1,\n                                ty: AlgebraicType::U64,\n                            })),\n                            Box::new(Expr::Value(AlgebraicValue::U64(5), AlgebraicType::U64)),\n                        ),\n                    ),\n                    \"player\".into(),\n                ),\n            ]\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/expr/src/statement.rs",
    "content": "use std::sync::Arc;\n\nuse spacetimedb_lib::{identity::AuthCtx, st_var::StVarValue, AlgebraicType, AlgebraicValue, ProductValue};\nuse spacetimedb_primitives::{ColId, TableId};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_schema::schema::{ColumnSchema, TableOrViewSchema};\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_sql_parser::{\n    ast::{\n        sql::{SqlAst, SqlDelete, SqlInsert, SqlSelect, SqlSet, SqlShow, SqlUpdate},\n        BinOp, SqlIdent, SqlLiteral,\n    },\n    parser::sql::parse_sql,\n};\nuse thiserror::Error;\n\nuse crate::{\n    check::Relvars,\n    errors::{DmlOnView, InvalidLiteral},\n    expr::{FieldProject, ProjectList, RelExpr, Relvar},\n    type_limit,\n};\n\nuse super::{\n    check::{SchemaView, TypeChecker, TypingResult},\n    errors::{InsertFieldsError, InsertValuesError, TypingError, UnexpectedType, Unresolved},\n    expr::Expr,\n    parse, type_expr, type_proj, type_select, StatementCtx, StatementSource,\n};\n\npub enum Statement {\n    Select(ProjectList),\n    DML(DML),\n}\n\npub enum DML {\n    Insert(TableInsert),\n    Update(TableUpdate),\n    Delete(TableDelete),\n}\n\nimpl DML {\n    /// Returns the schema of the table on which this mutation applies\n    pub fn table_schema(&self) -> &TableOrViewSchema {\n        match self {\n            Self::Insert(insert) => &insert.table,\n            Self::Delete(delete) => &delete.table,\n            Self::Update(update) => &update.table,\n        }\n    }\n\n    /// Returns the id of the table on which this mutation applies\n    pub fn table_id(&self) -> TableId {\n        self.table_schema().table_id\n    }\n\n    /// Returns the name of the table on which this mutation applies\n    pub fn table_name(&self) -> &TableName {\n        &self.table_schema().table_name\n    }\n}\n\npub struct TableInsert {\n    pub table: Arc<TableOrViewSchema>,\n    pub rows: Box<[ProductValue]>,\n}\n\npub struct TableDelete {\n    pub table: Arc<TableOrViewSchema>,\n    pub filter: Option<Expr>,\n}\n\npub struct TableUpdate {\n    pub table: Arc<TableOrViewSchema>,\n    pub columns: Box<[(ColId, AlgebraicValue)]>,\n    pub filter: Option<Expr>,\n}\n\npub struct SetVar {\n    pub name: String,\n    pub value: AlgebraicValue,\n}\n\npub struct ShowVar {\n    pub name: String,\n}\n\n/// Type check an INSERT statement\npub fn type_insert(insert: SqlInsert, tx: &impl SchemaView) -> TypingResult<TableInsert> {\n    let SqlInsert {\n        table: SqlIdent(table_name),\n        fields,\n        values,\n    } = insert;\n\n    let schema = tx\n        .schema(&table_name)\n        .ok_or_else(|| Unresolved::table(&table_name))\n        .map_err(TypingError::from)?;\n    let table_name = &schema.table_name;\n\n    if schema.is_view() {\n        return Err(TypingError::DmlOnView(DmlOnView {\n            view_name: table_name.clone(),\n        }));\n    }\n\n    // Expect n fields\n    let n = schema.public_columns().len();\n    if fields.len() != schema.public_columns().len() {\n        return Err(TypingError::from(InsertFieldsError {\n            table: table_name.clone(),\n            nfields: fields.len(),\n            ncols: schema.public_columns().len(),\n        }));\n    }\n\n    let mut rows = Vec::new();\n    for row in values.0 {\n        // Expect each row to have n values\n        if row.len() != n {\n            return Err(TypingError::from(InsertValuesError {\n                table: table_name.clone(),\n                values: row.len(),\n                fields: n,\n            }));\n        }\n        let mut values = Vec::new();\n        for (value, ty) in row.into_iter().zip(\n            schema\n                .as_ref()\n                .public_columns()\n                .iter()\n                .map(|ColumnSchema { col_type, .. }| col_type),\n        ) {\n            match (value, ty) {\n                (SqlLiteral::Bool(v), AlgebraicType::Bool) => {\n                    values.push(AlgebraicValue::Bool(v));\n                }\n                (SqlLiteral::Str(v), AlgebraicType::String) => {\n                    values.push(AlgebraicValue::String(v));\n                }\n                (SqlLiteral::Bool(_), _) => {\n                    return Err(UnexpectedType::new(&AlgebraicType::Bool, ty).into());\n                }\n                (SqlLiteral::Str(_), _) => {\n                    return Err(UnexpectedType::new(&AlgebraicType::String, ty).into());\n                }\n                (SqlLiteral::Hex(v), ty) | (SqlLiteral::Num(v), ty) => {\n                    values.push(parse(&v, ty).map_err(|_| InvalidLiteral::new(v.into_string(), ty))?);\n                }\n            }\n        }\n        rows.push(ProductValue::from(values));\n    }\n    let into = schema;\n    let rows = rows.into_boxed_slice();\n    Ok(TableInsert { table: into, rows })\n}\n\n/// Type check a DELETE statement\npub fn type_delete(delete: SqlDelete, tx: &impl SchemaView) -> TypingResult<TableDelete> {\n    let SqlDelete {\n        table: SqlIdent(query_table_name),\n        filter,\n    } = delete;\n    let from = tx\n        .schema(&query_table_name)\n        .ok_or_else(|| Unresolved::table(&query_table_name))\n        .map_err(TypingError::from)?;\n    let table_name = &from.table_name;\n\n    if from.is_view() {\n        return Err(TypingError::DmlOnView(DmlOnView {\n            view_name: table_name.clone(),\n        }));\n    }\n    let mut vars = Relvars::default();\n    vars.insert(query_table_name, from.clone());\n    vars.insert(table_name.clone().into(), from.clone());\n    if let Some(alias) = from.inner().alias.as_ref() {\n        vars.insert(alias.clone().into(), from.clone());\n    }\n    let expr = filter\n        .map(|expr| type_expr(&vars, expr, Some(&AlgebraicType::Bool)))\n        .transpose()?;\n    Ok(TableDelete {\n        table: from,\n        filter: expr,\n    })\n}\n\n/// Type check an UPDATE statement\npub fn type_update(update: SqlUpdate, tx: &impl SchemaView) -> TypingResult<TableUpdate> {\n    let SqlUpdate {\n        table: SqlIdent(query_table_name),\n        assignments,\n        filter,\n    } = update;\n    let schema = tx\n        .schema(&query_table_name)\n        .ok_or_else(|| Unresolved::table(&query_table_name))\n        .map_err(TypingError::from)?;\n    let table_name = &schema.table_name;\n\n    if schema.is_view() {\n        return Err(TypingError::DmlOnView(DmlOnView {\n            view_name: table_name.clone(),\n        }));\n    }\n    let mut values = Vec::new();\n    for SqlSet(SqlIdent(field), lit) in assignments {\n        let ColumnSchema {\n            col_pos: col_id,\n            col_type: ty,\n            ..\n        } = schema\n            .as_ref()\n            .get_column_by_name_or_alias(&field)\n            .ok_or_else(|| Unresolved::field(table_name.clone(), &field))?;\n        match (lit, ty) {\n            (SqlLiteral::Bool(v), AlgebraicType::Bool) => {\n                values.push((*col_id, AlgebraicValue::Bool(v)));\n            }\n            (SqlLiteral::Str(v), AlgebraicType::String) => {\n                values.push((*col_id, AlgebraicValue::String(v)));\n            }\n            (SqlLiteral::Bool(_), _) => {\n                return Err(UnexpectedType::new(&AlgebraicType::Bool, ty).into());\n            }\n            (SqlLiteral::Str(_), _) => {\n                return Err(UnexpectedType::new(&AlgebraicType::String, ty).into());\n            }\n            (SqlLiteral::Hex(v), ty) | (SqlLiteral::Num(v), ty) => {\n                values.push((\n                    *col_id,\n                    parse(&v, ty).map_err(|_| InvalidLiteral::new(v.into_string(), ty))?,\n                ));\n            }\n        }\n    }\n    let mut vars = Relvars::default();\n    vars.insert(query_table_name, schema.clone());\n    vars.insert(table_name.clone().into(), schema.clone());\n    if let Some(alias) = schema.inner().alias.as_ref() {\n        vars.insert(alias.clone().into(), schema.clone());\n    }\n    let values = values.into_boxed_slice();\n    let filter = filter\n        .map(|expr| type_expr(&vars, expr, Some(&AlgebraicType::Bool)))\n        .transpose()?;\n    Ok(TableUpdate {\n        table: schema,\n        columns: values,\n        filter,\n    })\n}\n\n#[derive(Error, Debug)]\n#[error(\"{name} is not a valid system variable\")]\npub struct InvalidVar {\n    pub name: RawIdentifier,\n}\n\nconst VAR_ROW_LIMIT: &str = \"row_limit\";\n\nfn is_var_valid(var: &str) -> bool {\n    var == VAR_ROW_LIMIT\n}\n\nconst ST_VAR_NAME: &str = \"st_var\";\nconst VALUE_COLUMN: &str = \"value\";\n\n/// The concept of `SET` only exists in the ast.\n/// We translate it here to an `INSERT` on the `st_var` system table.\n/// That is:\n///\n/// ```sql\n/// SET var TO ...\n/// ```\n///\n/// is rewritten as\n///\n/// ```sql\n/// INSERT INTO st_var (name, value) VALUES ('var', ...)\n/// ```\npub fn type_and_rewrite_set(set: SqlSet, tx: &impl SchemaView) -> TypingResult<TableInsert> {\n    let SqlSet(SqlIdent(var_name), lit) = set;\n    if !is_var_valid(&var_name) {\n        return Err(InvalidVar { name: var_name }.into());\n    }\n\n    match lit {\n        SqlLiteral::Bool(_) => Err(UnexpectedType::new(&AlgebraicType::U64, &AlgebraicType::Bool).into()),\n        SqlLiteral::Str(_) => Err(UnexpectedType::new(&AlgebraicType::U64, &AlgebraicType::String).into()),\n        SqlLiteral::Hex(_) => Err(UnexpectedType::new(&AlgebraicType::U64, &AlgebraicType::bytes()).into()),\n        SqlLiteral::Num(n) => {\n            let table = tx.schema(ST_VAR_NAME).ok_or_else(|| Unresolved::table(ST_VAR_NAME))?;\n            let var_name = AlgebraicValue::String(var_name.as_ref().into());\n            let sum_value = StVarValue::try_from_primitive(\n                parse(&n, &AlgebraicType::U64)\n                    .map_err(|_| InvalidLiteral::new(n.clone().into_string(), &AlgebraicType::U64))?,\n            )\n            .map_err(|_| InvalidLiteral::new(n.into_string(), &AlgebraicType::U64))?\n            .into();\n            Ok(TableInsert {\n                table,\n                rows: Box::new([ProductValue::from_iter([var_name, sum_value])]),\n            })\n        }\n    }\n}\n\n/// The concept of `SHOW` only exists in the ast.\n/// We translate it here to a `SELECT` on the `st_var` system table.\n/// That is:\n///\n/// ```sql\n/// SHOW var\n/// ```\n///\n/// is rewritten as\n///\n/// ```sql\n/// SELECT value FROM st_var WHERE name = 'var'\n/// ```\npub fn type_and_rewrite_show(show: SqlShow, tx: &impl SchemaView) -> TypingResult<ProjectList> {\n    let SqlShow(SqlIdent(var_name)) = show;\n    if !is_var_valid(&var_name) {\n        return Err(InvalidVar { name: var_name }.into());\n    }\n\n    let table_schema = tx.schema(ST_VAR_NAME).ok_or_else(|| Unresolved::table(ST_VAR_NAME))?;\n    let table_name = &table_schema.table_name;\n\n    let value_col_ty = table_schema\n        .as_ref()\n        .get_column_by_name(VALUE_COLUMN)\n        .map(|ColumnSchema { col_type, .. }| col_type)\n        .ok_or_else(|| Unresolved::field(table_name.clone(), VALUE_COLUMN))?;\n\n    // -------------------------------------------\n    // SELECT value FROM st_var WHERE name = 'var'\n    //                                ^^^^\n    // -------------------------------------------\n    let var_name_field = Expr::Field(FieldProject {\n        table: table_name.clone().into(),\n        // TODO: Avoid hard coding the field position.\n        // See `StVarFields` for the schema of `st_var`.\n        field: 0,\n        ty: AlgebraicType::String,\n    });\n\n    // -------------------------------------------\n    // SELECT value FROM st_var WHERE name = 'var'\n    //                                        ^^^\n    // -------------------------------------------\n    let var_name_value = Expr::Value(AlgebraicValue::String(var_name.as_ref().into()), AlgebraicType::String);\n\n    // -------------------------------------------\n    // SELECT value FROM st_var WHERE name = 'var'\n    //        ^^^^^\n    // -------------------------------------------\n    let column_list = vec![(\n        VALUE_COLUMN.into(),\n        FieldProject {\n            table: table_name.clone().into(),\n            // TODO: Avoid hard coding the field position.\n            // See `StVarFields` for the schema of `st_var`.\n            field: 1,\n            ty: value_col_ty.clone(),\n        },\n    )];\n\n    // -------------------------------------------\n    // SELECT value FROM st_var WHERE name = 'var'\n    //                   ^^^^^^\n    // -------------------------------------------\n    let relvar = RelExpr::RelVar(Relvar {\n        schema: table_schema.clone(),\n        alias: table_name.clone().into(),\n        delta: None,\n    });\n\n    let filter = Expr::BinOp(\n        // -------------------------------------------\n        // SELECT value FROM st_var WHERE name = 'var'\n        //                                    ^^^\n        // -------------------------------------------\n        BinOp::Eq,\n        Box::new(var_name_field),\n        Box::new(var_name_value),\n    );\n\n    Ok(ProjectList::List(\n        vec![RelExpr::Select(Box::new(relvar), filter)],\n        column_list,\n    ))\n}\n\n/// Type-checker for regular `SQL` queries\nstruct SqlChecker;\n\nimpl TypeChecker for SqlChecker {\n    type Ast = SqlSelect;\n    type Set = SqlSelect;\n\n    fn type_ast(ast: Self::Ast, tx: &impl SchemaView) -> TypingResult<ProjectList> {\n        Self::type_set(ast, &mut Relvars::default(), tx)\n    }\n\n    fn type_set(ast: Self::Set, vars: &mut Relvars, tx: &impl SchemaView) -> TypingResult<ProjectList> {\n        match ast {\n            SqlSelect {\n                project,\n                from,\n                filter: None,\n                limit: None,\n            } => type_proj(Self::type_from(from, vars, tx)?, project, vars),\n            SqlSelect {\n                project,\n                from,\n                filter: None,\n                limit: Some(n),\n            } => type_limit(type_proj(Self::type_from(from, vars, tx)?, project, vars)?, &n),\n            SqlSelect {\n                project,\n                from,\n                filter: Some(expr),\n                limit: None,\n            } => type_proj(\n                type_select(Self::type_from(from, vars, tx)?, expr, vars)?,\n                project,\n                vars,\n            ),\n            SqlSelect {\n                project,\n                from,\n                filter: Some(expr),\n                limit: Some(n),\n            } => type_limit(\n                type_proj(\n                    type_select(Self::type_from(from, vars, tx)?, expr, vars)?,\n                    project,\n                    vars,\n                )?,\n                &n,\n            ),\n        }\n    }\n}\n\npub fn parse_and_type_sql(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> TypingResult<Statement> {\n    match parse_sql(sql)?.resolve_sender(auth.caller()) {\n        SqlAst::Select(ast) => Ok(Statement::Select(SqlChecker::type_ast(ast, tx)?)),\n        SqlAst::Insert(insert) => Ok(Statement::DML(DML::Insert(type_insert(insert, tx)?))),\n        SqlAst::Delete(delete) => Ok(Statement::DML(DML::Delete(type_delete(delete, tx)?))),\n        SqlAst::Update(update) => Ok(Statement::DML(DML::Update(type_update(update, tx)?))),\n        SqlAst::Set(set) => Ok(Statement::DML(DML::Insert(type_and_rewrite_set(set, tx)?))),\n        SqlAst::Show(show) => Ok(Statement::Select(type_and_rewrite_show(show, tx)?)),\n    }\n}\n\n/// Parse and type check a *general* query into a [StatementCtx].\npub fn compile_sql_stmt<'a>(sql: &'a str, tx: &impl SchemaView, auth: &AuthCtx) -> TypingResult<StatementCtx<'a>> {\n    let statement = parse_and_type_sql(sql, tx, auth)?;\n    Ok(StatementCtx {\n        statement,\n        sql,\n        source: StatementSource::Query,\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use super::Statement;\n    use crate::ast::LogOp;\n    use crate::check::{\n        test_utils::{build_module_def, SchemaViewer},\n        Relvars, SchemaView, TypingResult,\n    };\n    use crate::type_expr;\n    use spacetimedb::TableId;\n    use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9Builder;\n    use spacetimedb_lib::{identity::AuthCtx, AlgebraicType, ProductType};\n    use spacetimedb_sats::raw_identifier::RawIdentifier;\n    use spacetimedb_schema::def::ModuleDef;\n    use spacetimedb_schema::schema::{TableOrViewSchema, TableSchema};\n    use spacetimedb_sql_parser::ast::{SqlExpr, SqlLiteral};\n\n    fn module_def() -> ModuleDef {\n        build_module_def(vec![\n            (\n                \"t\",\n                ProductType::from([\n                    (\"u32\", AlgebraicType::U32),\n                    (\"f32\", AlgebraicType::F32),\n                    (\"str\", AlgebraicType::String),\n                    (\"arr\", AlgebraicType::array(AlgebraicType::String)),\n                ]),\n            ),\n            (\n                \"s\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::identity()),\n                    (\"u32\", AlgebraicType::U32),\n                    (\"arr\", AlgebraicType::array(AlgebraicType::String)),\n                    (\"bytes\", AlgebraicType::bytes()),\n                ]),\n            ),\n        ])\n    }\n\n    /// A wrapper around [super::parse_and_type_sql] that takes a dummy [AuthCtx]\n    fn parse_and_type_sql(sql: &str, tx: &impl SchemaView) -> TypingResult<Statement> {\n        super::parse_and_type_sql(sql, tx, &AuthCtx::for_testing())\n    }\n\n    #[test]\n    fn valid() {\n        let tx = SchemaViewer(module_def());\n\n        for sql in [\n            \"select str from t\",\n            \"select str, arr from t\",\n            \"select t.str, arr from t\",\n            \"select * from t limit 5\",\n        ] {\n            let result = parse_and_type_sql(sql, &tx);\n            assert!(result.is_ok());\n        }\n    }\n\n    #[test]\n    fn invalid() {\n        let tx = SchemaViewer(module_def());\n\n        for sql in [\n            // Unqualified columns in a join\n            \"select id, str from s join t\",\n            // Wrong type for limit\n            \"select * from t limit '5'\",\n            // Unqualified name in join expression\n            \"select t.* from t join s on t.u32 = s.u32 where bytes = 0xABCD\",\n        ] {\n            let result = parse_and_type_sql(sql, &tx);\n            assert!(result.is_err());\n        }\n    }\n\n    /// Manually build the AST for a recursive query,\n    /// because we limit the length of the query to prevent stack overflow on parsing.\n    /// Exercise the limit [`recursion::MAX_RECURSION_TYP_EXPR`]\n    #[test]\n    fn typing_recursion() {\n        let build_query = |total, sep: char| {\n            let mut expr = SqlExpr::Lit(SqlLiteral::Bool(true));\n            for _ in 1..total {\n                let next = SqlExpr::Log(\n                    Box::new(SqlExpr::Lit(SqlLiteral::Bool(true))),\n                    Box::new(SqlExpr::Lit(SqlLiteral::Bool(false))),\n                    LogOp::And,\n                );\n                expr = SqlExpr::Log(Box::new(expr), Box::new(next), LogOp::And);\n            }\n            type_expr(&Relvars::default(), expr, Some(&AlgebraicType::Bool))\n                .map_err(|e| e.to_string().split(sep).next().unwrap_or_default().to_string())\n        };\n        assert_eq!(build_query(2_501, ','), Err(\"Recursion limit exceeded\".to_string()));\n\n        assert!(build_query(2_500, ',').is_ok());\n    }\n\n    #[test]\n    fn views() {\n        struct SchemaViewer {\n            module_def: ModuleDef,\n        }\n\n        impl SchemaViewer {\n            fn schema_for_view(&self, name: &str) -> Option<Arc<TableOrViewSchema>> {\n                self.module_def\n                    .view(name)\n                    .map(|def| TableSchema::from_view_def_for_datastore(&self.module_def, def))\n                    .map(Arc::new)\n                    .map(TableOrViewSchema::from)\n                    .map(Arc::new)\n            }\n        }\n\n        impl SchemaView for SchemaViewer {\n            fn table_id(&self, _: &str) -> Option<TableId> {\n                None\n            }\n\n            fn schema(&self, name: &str) -> Option<Arc<TableOrViewSchema>> {\n                self.schema_for_view(name)\n            }\n\n            fn schema_for_table(&self, _: TableId) -> Option<Arc<TableOrViewSchema>> {\n                self.schema_for_view(\"v\")\n            }\n\n            fn rls_rules_for_table(&self, _: TableId) -> anyhow::Result<Vec<Box<str>>> {\n                Ok(vec![])\n            }\n        }\n\n        fn build_view_def(\n            builder: &mut RawModuleDefV9Builder,\n            name: &str,\n            columns: impl Into<ProductType>,\n            is_anonymous: bool,\n        ) {\n            let name = RawIdentifier::new(name);\n            let product_type = AlgebraicType::from(columns.into());\n            let type_ref = builder.add_algebraic_type([], name.clone(), product_type, true);\n            let return_type = AlgebraicType::array(AlgebraicType::Ref(type_ref));\n            builder.add_view(name, 0, true, is_anonymous, ProductType::unit(), return_type);\n        }\n\n        let mut builder = RawModuleDefV9Builder::new();\n        build_view_def(&mut builder, \"v\", [(\"a\", AlgebraicType::String)], true);\n        let module_def: ModuleDef = builder.finish().try_into().expect(\"failed to generate module def\");\n\n        let tx = SchemaViewer { module_def };\n\n        struct TestCase {\n            sql: &'static str,\n            msg: &'static str,\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select a from v\",\n                msg: \"Column projection on view\",\n            },\n            TestCase {\n                sql: \"select * from v where a = 'hello'\",\n                msg: \"Column selection on view\",\n            },\n        ] {\n            let result = parse_and_type_sql(sql, &tx);\n            assert!(result.is_ok(), \"{msg}\");\n        }\n\n        for TestCase { sql, msg } in [\n            TestCase {\n                sql: \"select b from v\",\n                msg: \"`v` does not have a column named `b`\",\n            },\n            TestCase {\n                sql: \"select sender from v\",\n                msg: \"`v` does not have a column named `sender`\",\n            },\n            TestCase {\n                sql: \"select arg_id from v\",\n                msg: \"`v` does not have a column named `arg_id`\",\n            },\n        ] {\n            let result = parse_and_type_sql(sql, &tx);\n            assert!(result.is_err(), \"{msg}\");\n        }\n    }\n}\n"
  },
  {
    "path": "crates/fs-utils/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-fs-utils\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Assorted utilities for filesystem operations used in SpacetimeDB\"\n\n[dependencies]\nanyhow.workspace = true\nfs2.workspace = true\nhex.workspace = true\nrand.workspace = true\nthiserror.workspace = true\ntokio.workspace = true\nzstd-framed.workspace = true\n\n[dev-dependencies]\ntempdir.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/fs-utils/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/fs-utils/src/compression.rs",
    "content": "use std::fs::File;\nuse std::io;\nuse std::io::{BufReader, Read, Seek, SeekFrom};\nuse tokio::io::AsyncSeek;\nuse zstd_framed;\nuse zstd_framed::{ZstdReader, ZstdWriter};\n\npub const ZSTD_MAGIC_BYTES: [u8; 4] = [0x28, 0xB5, 0x2F, 0xFD];\n\n/// Helper struct to keep track of the number of files compressed using each algorithm\n#[derive(Debug, Copy, Clone, PartialEq, Default)]\npub struct CompressCount {\n    pub none: usize,\n    pub zstd: usize,\n}\n\n/// Supported compression algorithms.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum CompressionAlgorithm {\n    Zstd,\n}\n\n/// Compression type\n///\n/// if `None`, the file is not compressed, otherwise it will be compressed using the specified algorithm.\n#[derive(Debug, Copy, Clone, PartialEq)]\npub enum CompressType {\n    None,\n    Algorithm(CompressionAlgorithm),\n}\n\nimpl CompressType {\n    pub fn algorithm(&self) -> Option<CompressionAlgorithm> {\n        match self {\n            Self::None => None,\n            Self::Algorithm(algo) => Some(*algo),\n        }\n    }\n\n    pub fn zstd() -> Self {\n        Self::Algorithm(CompressionAlgorithm::Zstd)\n    }\n\n    pub fn is_compressed(&self) -> bool {\n        matches!(self, Self::Algorithm(_))\n    }\n}\n\n/// A reader that can read compressed files\npub enum CompressReader {\n    None(BufReader<File>),\n    Zstd(Box<ZstdReader<'static, BufReader<File>>>),\n}\n\nimpl CompressReader {\n    /// Create a new CompressReader from a File\n    ///\n    /// It will detect the compression type using `magic bytes` and return the appropriate reader.\n    ///\n    /// **Note**: The reader will be return to the original position after detecting the compression type.\n    pub fn new(mut inner: File) -> io::Result<Self> {\n        let current_pos = inner.stream_position()?;\n\n        let mut magic_bytes = [0u8; 4];\n        let bytes_read = inner.read(&mut magic_bytes)?;\n\n        // Restore the original position\n        inner.seek(SeekFrom::Start(current_pos))?;\n\n        // Determine compression type\n        Ok(if bytes_read == 4 {\n            match magic_bytes {\n                ZSTD_MAGIC_BYTES => {\n                    let table = zstd_framed::table::read_seek_table(&mut inner)?;\n                    let mut builder = ZstdReader::builder(inner);\n                    if let Some(table) = table {\n                        builder = builder.with_seek_table(table);\n                    }\n                    CompressReader::Zstd(Box::new(builder.build()?))\n                }\n                _ => CompressReader::None(BufReader::new(inner)),\n            }\n        } else {\n            CompressReader::None(BufReader::new(inner))\n        })\n    }\n\n    pub fn file_size(&self) -> io::Result<usize> {\n        Ok(match self {\n            Self::None(inner) => inner.get_ref().metadata()?.len() as usize,\n            //TODO: Can't see how to get the file size from ZstdReader\n            Self::Zstd(_inner) => 0,\n        })\n    }\n\n    pub fn compress_type(&self) -> CompressType {\n        match self {\n            CompressReader::None(_) => CompressType::None,\n            CompressReader::Zstd(_) => CompressType::Algorithm(CompressionAlgorithm::Zstd),\n        }\n    }\n\n    pub fn is_compressed(&self) -> bool {\n        self.compress_type().is_compressed()\n    }\n}\n\nimpl Read for CompressReader {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        match self {\n            CompressReader::None(inner) => inner.read(buf),\n            CompressReader::Zstd(inner) => inner.read(buf),\n        }\n    }\n}\n\nimpl io::BufRead for CompressReader {\n    fn fill_buf(&mut self) -> io::Result<&[u8]> {\n        match self {\n            CompressReader::None(inner) => inner.fill_buf(),\n            CompressReader::Zstd(inner) => inner.fill_buf(),\n        }\n    }\n\n    fn consume(&mut self, amt: usize) {\n        match self {\n            CompressReader::None(inner) => inner.consume(amt),\n            CompressReader::Zstd(inner) => inner.consume(amt),\n        }\n    }\n}\n\nimpl Seek for CompressReader {\n    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {\n        match self {\n            CompressReader::None(inner) => inner.seek(pos),\n            CompressReader::Zstd(inner) => inner.seek(pos),\n        }\n    }\n}\n\npub fn new_zstd_writer<'a, W: io::Write>(inner: W, max_frame_size: Option<u32>) -> io::Result<ZstdWriter<'a, W>> {\n    let writer = ZstdWriter::builder(inner).with_compression_level(0);\n    if let Some(max_frame_size) = max_frame_size {\n        writer.with_seek_table(max_frame_size)\n    } else {\n        writer\n    }\n    .build()\n}\n\npub fn compress_with_zstd<W: io::Write, R: io::Read>(\n    mut src: R,\n    mut dst: W,\n    max_frame_size: Option<u32>,\n) -> io::Result<()> {\n    let mut writer = new_zstd_writer(&mut dst, max_frame_size)?;\n    io::copy(&mut src, &mut writer)?;\n    writer.shutdown()?;\n    drop(writer);\n    Ok(())\n}\n\npub use async_impls::AsyncCompressReader;\n\npub async fn segment_len<T: AsyncSeek + Unpin>(r: &mut T) -> tokio::io::Result<u64> {\n    use tokio::io::AsyncSeekExt;\n    let old_pos = r.stream_position().await?;\n    let len = r.seek(tokio::io::SeekFrom::End(0)).await?;\n    // If we're already at the end of the file, avoid seeking.\n    if old_pos != len {\n        r.seek(tokio::io::SeekFrom::Start(old_pos)).await?;\n    }\n\n    Ok(len)\n}\n\nmod async_impls {\n    use super::*;\n    use std::pin::Pin;\n    use std::task::{Context, Poll};\n    use tokio::io::{self, AsyncBufRead, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt};\n    use zstd_framed::{AsyncZstdReader, AsyncZstdSeekableReader};\n\n    pub enum AsyncCompressReader<R> {\n        None(io::BufReader<R>),\n        Zstd(Box<AsyncZstdSeekableReader<'static, io::BufReader<R>>>),\n    }\n\n    impl<R: AsyncRead + AsyncSeek + Unpin> AsyncCompressReader<R> {\n        /// Create a new AsyncCompressReader from a reader\n        ///\n        /// It will detect the compression type using `magic bytes` and return the appropriate reader.\n        ///\n        /// **Note**: The reader will be return to the start after detecting the compression type.\n        pub async fn new(mut inner: R) -> io::Result<Self> {\n            let mut magic_bytes = [0u8; 4];\n            let magic_bytes = match inner.read_exact(&mut magic_bytes).await {\n                Ok(_) => Some(magic_bytes),\n                Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None,\n                Err(e) => return Err(e),\n            };\n\n            // Restore the original position\n            inner.seek(io::SeekFrom::Start(0)).await?;\n\n            // Determine compression type\n            Ok(match magic_bytes {\n                Some(ZSTD_MAGIC_BYTES) => {\n                    let table = zstd_framed::table::tokio::read_seek_table(&mut inner).await?;\n                    let mut builder = AsyncZstdReader::builder_tokio(inner);\n                    if let Some(table) = table {\n                        builder = builder.with_seek_table(table);\n                    }\n                    AsyncCompressReader::Zstd(Box::new(builder.build()?.seekable()))\n                }\n                _ => AsyncCompressReader::None(io::BufReader::new(inner)),\n            })\n        }\n\n        pub fn compress_type(&self) -> CompressType {\n            match self {\n                AsyncCompressReader::None(_) => CompressType::None,\n                AsyncCompressReader::Zstd(_) => CompressType::Algorithm(CompressionAlgorithm::Zstd),\n            }\n        }\n    }\n\n    impl AsyncCompressReader<tokio::fs::File> {\n        pub async fn file_size(&mut self) -> io::Result<u64> {\n            match self {\n                AsyncCompressReader::None(inner) => inner.get_ref().metadata().await.map(|m| m.len()),\n                AsyncCompressReader::Zstd(inner) => segment_len(inner).await,\n            }\n        }\n    }\n    macro_rules! forward_reader {\n    ($self:ident.$method:ident($($args:expr),*)) => {\n        match $self.get_mut() {\n            AsyncCompressReader::None(r) => Pin::new(r).$method($($args),*),\n            AsyncCompressReader::Zstd(r) => Pin::new(r).$method($($args),*),\n        }\n    };\n}\n    impl<R: AsyncRead + AsyncSeek + Unpin> AsyncRead for AsyncCompressReader<R> {\n        fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut io::ReadBuf<'_>) -> Poll<io::Result<()>> {\n            forward_reader!(self.poll_read(cx, buf))\n        }\n    }\n    impl<R: AsyncRead + AsyncSeek + Unpin> AsyncBufRead for AsyncCompressReader<R> {\n        fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<&[u8]>> {\n            forward_reader!(self.poll_fill_buf(cx))\n        }\n\n        fn consume(self: Pin<&mut Self>, amt: usize) {\n            forward_reader!(self.consume(amt))\n        }\n    }\n    impl<R: AsyncRead + AsyncSeek + Unpin> AsyncSeek for AsyncCompressReader<R> {\n        fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> {\n            forward_reader!(self.start_seek(position))\n        }\n\n        fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<u64>> {\n            forward_reader!(self.poll_complete(cx))\n        }\n    }\n}\n"
  },
  {
    "path": "crates/fs-utils/src/dir_trie.rs",
    "content": "//! Shallow byte-partitied directory tries.\n//!\n//! This is an implementation of the same on-disk data structure as\n//! [the git object store](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects),\n//! used for storing objects in an object store keyed on a content-addressed 32-byte hash.\n//!\n//! Objects' location in the trie is computed based on the (64 character) hexadecimal encoding of their (32 byte) Blake3 hash.\n//! The leading 2 digits of this hash are the name of the directory,\n//! and the remaining 62 digits are the name of the file itself.\n//!\n//! Storing files in this way is beneficial because directories internally are unsorted arrays of file entries,\n//! so searching for a file within a directory is O(directory_size).\n//! The trie structure implemented here is still O(n), but with a drastically reduced constant factor (1/128th),\n//! which we expect to shrink the linear lookups to an acceptable size.\n\nuse crate::compression::{AsyncCompressReader, CompressReader};\nuse std::fs::File;\nuse std::io::BufWriter;\nuse std::{\n    fs::{create_dir_all, OpenOptions},\n    io::{self, Read, Write},\n    path::{Path, PathBuf},\n};\n\n/// [`OpenOptions`] corresponding to opening a file with `O_EXCL`,\n/// i.e. creating a new writeable file, failing if it already exists.\npub fn o_excl() -> OpenOptions {\n    let mut options = OpenOptions::new();\n    options.create_new(true).write(true);\n    options\n}\n\n/// [`OpenOptions`] corresponding to opening a file with `O_RDONLY`,\n/// i.e. opening an existing file for reading.\npub fn o_rdonly() -> OpenOptions {\n    let mut options = OpenOptions::new();\n    options.read(true);\n    options\n}\n\n/// [`OpenOptions`] corresponding to opening a file with `O_RDONLY`,\n/// i.e. opening an existing file for reading.\npub fn o_rdonly_async() -> tokio::fs::OpenOptions {\n    let mut options = tokio::fs::OpenOptions::new();\n    options.read(true);\n    options\n}\n\n/// Counter for objects written newly to disk versus hardlinked,\n/// for diagnostic purposes with operations that may hardlink or write.\n///\n/// See [`DirTrie::hardlink_or_write`].\n#[derive(Default, Debug)]\npub struct CountCreated {\n    pub objects_written: u64,\n    pub objects_hardlinked: u64,\n}\n\n/// A directory trie.\npub struct DirTrie {\n    /// The directory name at which the dir trie is stored.\n    root: PathBuf,\n}\n\nconst FILE_ID_BYTES: usize = 32;\nconst FILE_ID_HEX_CHARS: usize = FILE_ID_BYTES * 2;\n\ntype FileId = [u8; FILE_ID_BYTES];\n\n/// The number of leading hex chars taken from a `FileId` as the subdirectory name.\nconst DIR_HEX_CHARS: usize = 2;\n\nimpl DirTrie {\n    /// Returns the root of this `DirTrie` on disk.\n    pub fn root(&self) -> &Path {\n        &self.root\n    }\n\n    /// Open the directory trie at `root`, creating the root directory if it doesn't exist.\n    ///\n    /// Returns an error if the `root` cannot be created as a directory.\n    /// See documentation on [`create_dir_all`] for more details.\n    pub fn open(root: PathBuf) -> Result<Self, io::Error> {\n        create_dir_all(&root)?;\n        Ok(Self { root })\n    }\n\n    pub fn file_path(&self, file_id: &FileId) -> PathBuf {\n        // TODO(perf, bikeshedding): avoid allocating a `String`.\n        let file_id_hex = hex::encode(file_id);\n\n        let mut file_path = self.root.clone();\n        // Two additional chars for slashes.\n        file_path.reserve(FILE_ID_HEX_CHARS + 2);\n\n        // The path will look like `root/xx/yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy`,\n        // where `xx` are the leading hex characters of the file id,\n        // and the `y`s are the remaining 62 hex characters.\n        file_path.push(&file_id_hex[..DIR_HEX_CHARS]);\n        file_path.push(&file_id_hex[DIR_HEX_CHARS..]);\n\n        file_path\n    }\n\n    /// Hardlink the entry for `file_id` from `src_repo` into `self`.\n    ///\n    /// Hardlinking makes the object shared between both [`DirTrie`]s\n    /// without copying the data on-disk.\n    /// Note that this is only possible within a single file system;\n    /// this method will likely return an error if `self` and `src_repo`\n    /// are in different file systems.\n    /// See [Wikipedia](https://en.wikipedia.org/wiki/Hard_link) for more information on hard links.\n    ///\n    /// Returns `Ok(true)` if the `file_id` existed in `src_repo` and was successfully linked into `self`,\n    /// `Ok(false)` if the `file_id` did not exist in `src_repo`,\n    /// or an `Err` if a filesystem operation failed.\n    ///\n    /// The object's hash is not verified against its `file_id`,\n    /// so if `file_id` is corrupted within `src_repo`,\n    /// the corrupted object will be hardlinked into `self`.\n    pub fn try_hardlink_from(&self, src_repo: &DirTrie, file_id: &FileId) -> Result<bool, io::Error> {\n        let src_file = src_repo.file_path(file_id);\n        if src_file.is_file() {\n            let dst_file = self.file_path(file_id);\n            Self::create_parent(&dst_file)?;\n            std::fs::hard_link(src_file, dst_file)?;\n            Ok(true)\n        } else {\n            Ok(false)\n        }\n    }\n\n    /// True if `file_id` is the key for an object present in this trie.\n    ///\n    /// Internally calls [`Path::is_file`]; see documentation on that method for detailed semantics.\n    pub fn contains_entry(&self, file_id: &FileId) -> bool {\n        let path = self.file_path(file_id);\n\n        path.is_file()\n    }\n\n    fn create_parent(file: &Path) -> Result<(), io::Error> {\n        // Known to succeed because `self.file_path` creates a path with a parent.\n        let dir = file.parent().unwrap();\n\n        create_dir_all(dir)?;\n        Ok(())\n    }\n\n    /// Hardlink `file_id` from `src_repo` into `self`, or create it in `self` containing `contents`.\n    ///\n    /// `contents` is a thunk which will be called only if the `src_repo` does not contain `file_id`\n    /// in order to compute the file contents.\n    /// This allows callers to avoid expensive serialization if the object already exists in `src_repo`.\n    ///\n    /// If the source file exists but the hardlink operation fails, this method returns an error.\n    /// In this case, the destination file is not created.\n    /// See [`Self::try_hardlink_from`].\n    pub fn hardlink_or_write<Bytes: AsRef<[u8]>>(\n        &self,\n        src_repo: Option<&DirTrie>,\n        file_id: &FileId,\n        contents: impl FnOnce() -> Bytes,\n        counter: &mut CountCreated,\n    ) -> Result<(), io::Error> {\n        if self.contains_entry(file_id) {\n            return Ok(());\n        }\n\n        if let Some(src_repo) = src_repo\n            && self.try_hardlink_from(src_repo, file_id)?\n        {\n            counter.objects_hardlinked += 1;\n            return Ok(());\n        }\n\n        let mut file = self.open_entry_writer(file_id)?;\n        let contents = contents();\n        file.write_all(contents.as_ref())?;\n        file.flush()?;\n        counter.objects_written += 1;\n        Ok(())\n    }\n\n    /// Open the file keyed with `file_id` for reading.\n    ///\n    /// It will be decompressed based on the file's magic bytes.\n    ///\n    /// It will be opened with [`o_rdonly`].\n    pub fn open_entry_reader(&self, file_id: &FileId) -> Result<CompressReader, io::Error> {\n        let path = self.file_path(file_id);\n        Self::create_parent(&path)?;\n        CompressReader::new(o_rdonly().open(path)?)\n    }\n\n    /// Open the file keyed with `file_id` for reading.\n    ///\n    /// It will be decompressed based on the file's magic bytes.\n    ///\n    /// It will be opened with [`o_rdonly_async`].\n    pub async fn open_entry_reader_async(\n        &self,\n        file_id: &FileId,\n    ) -> Result<AsyncCompressReader<tokio::fs::File>, io::Error> {\n        let path = self.file_path(file_id);\n        Self::create_parent(&path)?;\n        AsyncCompressReader::new(o_rdonly_async().open(path).await?).await\n    }\n\n    /// Open the file keyed with `file_id` for writing.\n    ///\n    /// If `compress_type` is [`CompressType::None`], the file will be written uncompressed.\n    ///\n    /// The file will be opened with [`o_excl`].\n    pub fn open_entry_writer(&self, file_id: &FileId) -> Result<BufWriter<File>, io::Error> {\n        let path = self.file_path(file_id);\n        Self::create_parent(&path)?;\n        Ok(BufWriter::new(o_excl().open(path)?))\n    }\n\n    /// Open the entry keyed with `file_id` and read it into a `Vec<u8>`.\n    pub fn read_entry(&self, file_id: &FileId) -> Result<Vec<u8>, io::Error> {\n        let mut file = self.open_entry_reader(file_id)?;\n        let mut buf = Vec::with_capacity(file.file_size()?);\n        // TODO(perf): Async IO?\n        file.read_to_end(&mut buf)?;\n        Ok(buf)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use std::io::Read;\n\n    const TEST_ID: FileId = [0xa5; FILE_ID_BYTES];\n    const TEST_STRING: &[u8] = b\"test string\";\n\n    fn with_test_dir_trie(f: impl FnOnce(DirTrie)) {\n        let root = tempdir::TempDir::new(\"test_dir_trie\").unwrap();\n        let trie = DirTrie::open(root.path().to_path_buf()).unwrap();\n        f(trie)\n    }\n\n    /// Write the [`TEST_STRING`] into the entry [`TEST_ID`].\n    fn write_test_string(trie: &DirTrie) {\n        let mut file = trie.open_entry_writer(&TEST_ID).unwrap();\n        file.write_all(TEST_STRING).unwrap();\n    }\n\n    /// Read the entry [`TEST_ID`] and assert that its contents match the [`TEST_STRING`].\n    fn read_test_string(trie: &DirTrie) {\n        let mut file = trie.open_entry_reader(&TEST_ID).unwrap();\n        let mut contents = Vec::new();\n        file.read_to_end(&mut contents).unwrap();\n        assert_eq!(&contents, TEST_STRING);\n    }\n\n    #[test]\n    fn create_retrieve() {\n        with_test_dir_trie(|trie| {\n            // The trie starts empty, so it doesn't contain the `TEST_ID`'s file.\n            assert!(!trie.contains_entry(&TEST_ID));\n\n            // Create an entry in the trie and write some data to it.\n            write_test_string(&trie);\n\n            // The trie now has that entry.\n            assert!(trie.contains_entry(&TEST_ID));\n\n            // Open the entry and read its data back.\n            read_test_string(&trie);\n        })\n    }\n\n    #[test]\n    fn hardlink() {\n        with_test_dir_trie(|src| {\n            with_test_dir_trie(|dst| {\n                // Both tries starts empty, so they don't contain the `TEST_ID`'s file.\n                assert!(!src.contains_entry(&TEST_ID));\n                assert!(!dst.contains_entry(&TEST_ID));\n\n                // Create an entry in `src` and write some data to it.\n                write_test_string(&src);\n\n                // The `src` now contains the entry, but the `dst` still doesn't.\n                assert!(src.contains_entry(&TEST_ID));\n                assert!(!dst.contains_entry(&TEST_ID));\n\n                // Hardlink the entry from `src` into `dst`.\n                assert!(dst.try_hardlink_from(&src, &TEST_ID).unwrap());\n\n                // After hardlinking, the file is now in `dst`.\n                assert!(dst.contains_entry(&TEST_ID));\n                // Open the entry in `dst` and read its data back.\n                read_test_string(&dst);\n\n                // The file is still also in `src`, and its data hasn't changed.\n                assert!(src.contains_entry(&TEST_ID));\n                read_test_string(&src);\n            })\n        })\n    }\n\n    #[test]\n    fn open_options() {\n        with_test_dir_trie(|trie| {\n            // The trie starts empty, so it doesn't contain the `TEST_ID`'s file.\n            assert!(!trie.contains_entry(&TEST_ID));\n\n            // Because the file isn't there, we can't open it.\n            assert!(trie.open_entry_reader(&TEST_ID).is_err());\n\n            // Create an entry in the trie and write some data to it.\n            write_test_string(&trie);\n\n            // The trie now has that entry.\n            assert!(trie.contains_entry(&TEST_ID));\n\n            // Because the file is there, we can't create it.\n            assert!(trie.open_entry_writer(&TEST_ID).is_err());\n        })\n    }\n}\n"
  },
  {
    "path": "crates/fs-utils/src/lib.rs",
    "content": "use std::io::Write;\nuse std::path::Path;\n\npub mod compression;\npub mod dir_trie;\npub mod lockfile;\n\npub fn create_parent_dir(file: &Path) -> Result<(), std::io::Error> {\n    // If the path doesn't have a parent,\n    // i.e. is a single-component path with just a root or is empty,\n    // do nothing.\n    let Some(parent) = file.parent() else {\n        return Ok(());\n    };\n\n    // If the `file` path is a relative path with no directory component,\n    // `parent` will be the empty path.\n    // In this case, do not attempt to create a directory.\n    if parent == Path::new(\"\") {\n        return Ok(());\n    }\n\n    // If the `file` path has a directory component,\n    // do `create_dir_all` to ensure it exists.\n    // If `parent` already exists as a directory, this is a no-op.\n    std::fs::create_dir_all(parent)\n}\n\npub fn atomic_write(file_path: &Path, data: String) -> anyhow::Result<()> {\n    let mut temp_path = file_path.to_path_buf();\n    let mut temp_file: std::fs::File;\n    loop {\n        temp_path.set_extension(format!(\".tmp{}\", rand::random::<u32>()));\n        let opened = std::fs::OpenOptions::new()\n            .write(true)\n            .create_new(true)\n            .open(&temp_path);\n        if let Ok(file) = opened {\n            temp_file = file;\n            break;\n        }\n    }\n    temp_file.write_all(data.as_bytes())?;\n    std::fs::rename(&temp_path, file_path)?;\n    Ok(())\n}\n"
  },
  {
    "path": "crates/fs-utils/src/lockfile.rs",
    "content": "use crate::create_parent_dir;\nuse std::path::{Path, PathBuf};\n\n#[derive(thiserror::Error, Debug)]\npub enum LockfileError {\n    #[error(\"Failed to acquire lock on {file_path:?}: failed to create lockfile {lock_path:?}: {cause}\")]\n    Acquire {\n        file_path: PathBuf,\n        lock_path: PathBuf,\n        #[source]\n        cause: std::io::Error,\n    },\n    #[error(\"Failed to release lock: failed to delete lockfile {lock_path:?}: {cause}\")]\n    Release {\n        lock_path: PathBuf,\n        #[source]\n        cause: std::io::Error,\n    },\n}\n\n#[derive(Debug)]\n/// A file used as an exclusive lock on access to another file.\n///\n/// Constructing a `Lockfile` creates the `path` with [`std::fs::File::create_new`],\n/// a.k.a. `O_EXCL`, erroring if the file already exists.\n///\n/// Dropping a `Lockfile` deletes the `path`, releasing the lock.\n///\n/// Used to guarantee exclusive access to the system config file,\n/// in order to prevent racy concurrent modifications.\npub struct Lockfile {\n    path: PathBuf,\n}\n\nimpl Lockfile {\n    /// Acquire an exclusive lock on the file `file_path`.\n    ///\n    /// `file_path` should be the full path of the file to which to acquire exclusive access.\n    pub fn for_file<P: AsRef<Path>>(file_path: P) -> Result<Self, LockfileError> {\n        let file_path = file_path.as_ref();\n        // TODO: Someday, it would be nice to use OS locks to minimize edge cases (see\n        // https://github.com/clockworklabs/SpacetimeDB/pull/1341#issuecomment-2151018992).\n        //\n        // Currently, our files can be left around if a process is unceremoniously killed (most\n        // commonly with Ctrl-C, but this would also apply to e.g. power failure).\n        // See https://github.com/clockworklabs/SpacetimeDB/issues/1339.\n        let path = Self::lock_path(file_path);\n\n        let fail = |cause| LockfileError::Acquire {\n            lock_path: path.clone(),\n            file_path: file_path.to_path_buf(),\n            cause,\n        };\n        // Ensure the directory exists before attempting to create the lockfile.\n        create_parent_dir(file_path).map_err(fail)?;\n        // Open with `create_new`, which fails if the file already exists.\n        std::fs::File::create_new(&path).map_err(fail)?;\n        Ok(Lockfile { path })\n    }\n\n    /// Returns the path of a lockfile for the file `file_path`,\n    /// without actually acquiring the lock.\n    pub fn lock_path<P: AsRef<Path>>(file_path: P) -> PathBuf {\n        file_path.as_ref().with_extension(\"lock\")\n    }\n\n    fn release_internal(path: &Path) -> Result<(), LockfileError> {\n        std::fs::remove_file(path).map_err(|cause| LockfileError::Release {\n            lock_path: path.to_path_buf(),\n            cause,\n        })\n    }\n\n    /// Release the lock, with the opportunity to handle the error from failing to delete the lockfile.\n    ///\n    /// Dropping a [`Lockfile`] will release the lock, but will panic on failure rather than returning `Err`.\n    pub fn release(self) -> Result<(), LockfileError> {\n        // Don't run the destructor, which does the same thing, but panics on failure.\n        let mut this = std::mem::ManuallyDrop::new(self);\n        let path = std::mem::take(&mut this.path);\n        let res = Self::release_internal(&path);\n        drop(path);\n        res\n    }\n}\n\nimpl Drop for Lockfile {\n    fn drop(&mut self) {\n        Self::release_internal(&self.path).unwrap();\n    }\n}\n\npub mod advisory {\n    use std::{\n        fmt,\n        fs::{self, File},\n        io,\n        path::{Path, PathBuf},\n    };\n\n    use fs2::FileExt as _;\n    use thiserror::Error;\n\n    use crate::create_parent_dir;\n\n    #[derive(Debug, Error)]\n    #[error(\"failed to lock {}\", path.display())]\n    pub struct LockError {\n        pub path: PathBuf,\n        #[source]\n        pub source: io::Error,\n    }\n\n    /// A file locked with an exclusive, filesystem-level lock.\n    ///\n    /// Uses [`flock(2)`] on Unix platforms, and [`LockFile`] on Windows systems.\n    ///\n    /// The file is created if it does not exist.\n    /// Dropping `Lockfile` releases the lock, but, unlike [super::Lockfile],\n    /// does not delete the file.\n    ///\n    /// [`flock(2)`]: https://man7.org/linux/man-pages/man2/flock.2.html\n    /// [`LockFile`]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfile?redirectedfrom=MSDN\n    pub struct LockedFile {\n        path: PathBuf,\n        #[allow(unused)]\n        lock: File,\n    }\n\n    impl LockedFile {\n        /// Attempt to lock `path` using an exclusive lock.\n        ///\n        /// The file will be created,\n        /// including its parent directories,\n        /// if it does not exist.\n        ///\n        /// Note that, unlike [super::Lockfile::for_file],\n        /// the exact `path` is used -- no extra adjacent `.lock` file is\n        /// created.\n        pub fn lock(path: impl AsRef<Path>) -> Result<Self, LockError> {\n            let path = path.as_ref();\n            Self::lock_inner(path).map_err(|source| LockError {\n                path: path.into(),\n                source,\n            })\n        }\n\n        fn lock_inner(path: &Path) -> io::Result<Self> {\n            create_parent_dir(path)?;\n            let lock = File::create(path)?;\n            // TODO: Use `File::lock` (available since rust 1.89) instead?\n            lock.try_lock_exclusive()?;\n\n            Ok(Self {\n                path: path.to_path_buf(),\n                lock,\n            })\n        }\n\n        /// Release the lock and optionally remove the locked file.\n        pub fn release(self, remove: bool) -> io::Result<()> {\n            if remove {\n                fs::remove_file(&self.path)?;\n            }\n            Ok(())\n        }\n    }\n\n    impl fmt::Debug for LockedFile {\n        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n            f.debug_struct(\"LockedFile\").field(\"path\", &self.path).finish()\n        }\n    }\n}\n"
  },
  {
    "path": "crates/guard/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-guard\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nportpicker = \"0.1\"\nreqwest = { workspace = true, features = [\"blocking\", \"json\"] }\ntempfile.workspace = true\n\n[lints]\nworkspace = true\n\n\n"
  },
  {
    "path": "crates/guard/src/lib.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n\nuse std::{\n    env,\n    io::{BufRead, BufReader},\n    net::SocketAddr,\n    path::{Path, PathBuf},\n    process::{Child, Command, Stdio},\n    sync::{\n        atomic::{AtomicU64, Ordering},\n        Arc, Mutex, OnceLock,\n    },\n    thread::{self, sleep},\n    time::{Duration, Instant},\n};\n\n/// Global counter for spawn IDs to correlate log messages across threads.\nstatic SPAWN_COUNTER: AtomicU64 = AtomicU64::new(0);\n\nfn next_spawn_id() -> u64 {\n    SPAWN_COUNTER.fetch_add(1, Ordering::SeqCst)\n}\n\n/// Returns the workspace root directory.\n// TODO: Should this use something like `git rev-parse --show-toplevel` to avoid being directory-relative? Or perhaps `CARGO_WORKSPACE_DIR` is set?\nfn workspace_root() -> PathBuf {\n    let manifest_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"));\n    manifest_dir\n        .parent() // crates/\n        .and_then(|p| p.parent()) // workspace root\n        .expect(\"Failed to find workspace root\")\n        .to_path_buf()\n}\n\n/// Returns the target directory.\nfn target_dir() -> PathBuf {\n    let workspace_root = workspace_root();\n    env::var(\"CARGO_TARGET_DIR\")\n        .map(PathBuf::from)\n        .unwrap_or_else(|_| workspace_root.join(\"target\"))\n}\n\n/// Returns the expected CLI binary path.\nfn cli_binary_path() -> PathBuf {\n    let profile = \"release\";\n    let cli_name = if cfg!(windows) {\n        \"spacetimedb-cli.exe\"\n    } else {\n        \"spacetimedb-cli\"\n    };\n    target_dir().join(profile).join(cli_name)\n}\n\n/// Lazily-initialized path to the pre-built CLI binary.\nstatic CLI_BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();\n\n/// Returns the path to the pre-built CLI binary.\n///\n/// **This function does NOT build anything.** The binary must already exist.\n/// Use `cargo smoketest` to build binaries before running tests.\n///\n/// # Panics\n///\n/// Panics if the binary does not exist.\npub fn ensure_binaries_built() -> PathBuf {\n    CLI_BINARY_PATH\n        .get_or_init(|| {\n            let cli_path = cli_binary_path();\n\n            if !cli_path.exists() {\n                panic!(\n                    \"\\n\\\n                    ========================================================================\\n\\\n                    ERROR: CLI binary not found at {}\\n\\\n                    \\n\\\n                    Smoketests require pre-built binaries. Run:\\n\\\n                    \\n\\\n                    cargo smoketest\\n\\\n                    \\n\\\n                    Or build manually:\\n\\\n                    \\n\\\n                    cargo build -p spacetimedb-cli -p spacetimedb-standalone --features spacetimedb-standalone/allow_loopback_http_for_tests\\n\\\n                    ========================================================================\\n\",\n                    cli_path.display()\n                );\n            }\n\n            cli_path\n        })\n        .clone()\n}\n\nuse reqwest::blocking::Client;\n\npub struct SpacetimeDbGuard {\n    pub child: Child,\n    pub host_url: String,\n    pub logs: Arc<Mutex<String>>,\n    /// The PostgreSQL wire protocol port, if enabled.\n    pub pg_port: Option<u16>,\n    /// The data directory path (for restart scenarios).\n    pub data_dir: PathBuf,\n    /// Owns the temporary data directory (if created by spawn_in_temp_data_dir).\n    /// When this is Some, dropping the guard will clean up the temp dir.\n    _data_dir_handle: Option<tempfile::TempDir>,\n    /// Reader thread handles for stdout/stderr - joined on drop to prevent leaks.\n    reader_threads: Vec<thread::JoinHandle<()>>,\n    use_installed_cli: bool,\n}\n\n// Remove all Cargo-provided env vars from a child process. These are set by the fact that we're running in a cargo\n// command (e.g. `cargo test`). We don't want to inherit any of these to a child cargo process, because it causes\n// unnecessary rebuilds.\nimpl SpacetimeDbGuard {\n    /// Start `spacetimedb` in a temporary data directory via:\n    /// cargo run -p spacetimedb-cli -- start --data-dir <temp-dir> --listen-addr <addr>\n    pub fn spawn_in_temp_data_dir() -> Self {\n        Self::spawn_in_temp_data_dir_with_pg_port(None)\n    }\n\n    /// Start `spacetimedb` in a temporary data directory with optional PostgreSQL wire protocol.\n    pub fn spawn_in_temp_data_dir_with_pg_port(pg_port: Option<u16>) -> Self {\n        let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n        let data_dir_path = temp_dir.path().to_path_buf();\n\n        Self::spawn_spacetime_start_with_data_dir(false, pg_port, data_dir_path, Some(temp_dir))\n    }\n\n    /// Start `spacetimedb` in a temporary data directory via:\n    /// spacetime start --data-dir <temp-dir> --listen-addr <addr>\n    pub fn spawn_in_temp_data_dir_use_cli() -> Self {\n        let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n        let data_dir_path = temp_dir.path().to_path_buf();\n\n        Self::spawn_spacetime_start_with_data_dir(true, None, data_dir_path, Some(temp_dir))\n    }\n\n    /// Start `spacetimedb` with an explicit data directory (for restart scenarios).\n    ///\n    /// Unlike `spawn_in_temp_data_dir`, this method does not create a temporary directory.\n    /// The caller is responsible for managing the data directory lifetime.\n    pub fn spawn_with_data_dir(data_dir: PathBuf, pg_port: Option<u16>) -> Self {\n        Self::spawn_spacetime_start_with_data_dir(false, pg_port, data_dir, None)\n    }\n\n    fn spawn_spacetime_start_with_data_dir(\n        use_installed_cli: bool,\n        pg_port: Option<u16>,\n        data_dir: PathBuf,\n        _data_dir_handle: Option<tempfile::TempDir>,\n    ) -> Self {\n        let spawn_id = next_spawn_id();\n        let (child, logs, host_url, reader_threads) =\n            Self::spawn_server(&data_dir, pg_port, spawn_id, use_installed_cli);\n        SpacetimeDbGuard {\n            child,\n            host_url,\n            logs,\n            pg_port,\n            data_dir,\n            _data_dir_handle,\n            reader_threads,\n            use_installed_cli,\n        }\n    }\n\n    /// Stop the server process without dropping the guard.\n    ///\n    /// This kills the server process but preserves the data directory.\n    /// Use `restart()` to start the server again with the same data.\n    pub fn stop(&mut self) {\n        self.kill_process();\n    }\n\n    /// Restart the server with the same data directory.\n    ///\n    /// This stops the current server process and starts a new one\n    /// with the same data directory, preserving all data.\n    pub fn restart(&mut self) {\n        let spawn_id = next_spawn_id();\n        let old_pid = self.child.id();\n        eprintln!(\"[RESTART-{:03}] Starting restart, old pid={}\", spawn_id, old_pid);\n\n        self.stop();\n        eprintln!(\"[RESTART-{:03}] Old process stopped, sleeping 100ms\", spawn_id);\n\n        // Brief pause to ensure system resources are fully released\n        sleep(Duration::from_millis(100));\n\n        eprintln!(\"[RESTART-{:03}] Spawning new server\", spawn_id);\n        let (child, logs, host_url, reader_threads) =\n            Self::spawn_server(&self.data_dir, self.pg_port, spawn_id, self.use_installed_cli);\n        eprintln!(\n            \"[RESTART-{:03}] New server ready, pid={}, url={}\",\n            spawn_id,\n            child.id(),\n            host_url\n        );\n        // Ensure logs from the killed server are retained.\n        {\n            let mut new_logs = logs.lock().unwrap();\n            let old_logs = self.logs.lock().unwrap();\n            new_logs.insert_str(0, old_logs.as_str());\n        }\n\n        self.child = child;\n        self.logs = logs;\n        self.host_url = host_url;\n        self.reader_threads = reader_threads;\n    }\n\n    /// Kills the current server process and waits for it to exit.\n    fn kill_process(&mut self) {\n        let pid = self.child.id();\n        eprintln!(\"[KILL] Killing process tree for pid={}\", pid);\n\n        // Kill the process tree to ensure all child processes are terminated.\n        // On Windows, child.kill() only kills the direct child (spacetimedb-cli),\n        // leaving spacetimedb-standalone running as an orphan.\n        #[cfg(windows)]\n        {\n            let status = Command::new(\"taskkill\")\n                .args([\"/F\", \"/T\", \"/PID\", &pid.to_string()])\n                .stdout(Stdio::null())\n                .stderr(Stdio::null())\n                .status();\n            eprintln!(\"[KILL] taskkill result for pid={}: {:?}\", pid, status);\n        }\n\n        #[cfg(not(windows))]\n        {\n            let result = self.child.kill();\n            eprintln!(\"[KILL] kill result for pid={}: {:?}\", pid, result);\n        }\n\n        let wait_result = self.child.wait();\n        eprintln!(\"[KILL] wait() result for pid={}: {:?}\", pid, wait_result);\n\n        // Join reader threads to prevent leaks.\n        // The threads will exit naturally once the process is killed and pipes close.\n        let threads = std::mem::take(&mut self.reader_threads);\n        for handle in threads {\n            let _ = handle.join();\n        }\n        eprintln!(\"[KILL] Reader threads joined for pid={}\", pid);\n    }\n\n    /// Spawns a new server process with the given data directory.\n    /// Returns (child, logs, host_url, reader_threads).\n    fn spawn_server(\n        data_dir: &Path,\n        pg_port: Option<u16>,\n        spawn_id: u64,\n        use_installed_cli: bool,\n    ) -> (Child, Arc<Mutex<String>>, String, Vec<thread::JoinHandle<()>>) {\n        let cmd = if use_installed_cli {\n            eprintln!(\"[SPAWN-{:03}] START Using installed CLI\", spawn_id);\n            Command::new(\"spacetime\")\n        } else {\n            // Use the built CLI (common case)\n            let cli_path = ensure_binaries_built();\n            Command::new(&cli_path)\n        };\n        eprintln!(\n            \"[SPAWN-{:03}] START data_dir={:?} pg_port={:?}\",\n            spawn_id, data_dir, pg_port\n        );\n\n        let data_dir_str = data_dir.display().to_string();\n        let pg_port_str = pg_port.map(|p| p.to_string());\n\n        let address = \"127.0.0.1:0\".to_string();\n\n        let mut args = vec![\n            \"start\",\n            \"--jwt-key-dir\",\n            &data_dir_str,\n            \"--data-dir\",\n            &data_dir_str,\n            \"--listen-addr\",\n            &address,\n        ];\n        if let Some(ref port) = pg_port_str {\n            args.extend([\"--pg-port\", port]);\n        }\n\n        eprintln!(\"[SPAWN-{:03}] Spawning child process\", spawn_id);\n        let (child, logs, reader_threads) = Self::spawn_child(cmd, &args, spawn_id);\n        eprintln!(\"[SPAWN-{:03}] Child spawned pid={}\", spawn_id, child.id());\n\n        // Wait for the server to be ready\n        eprintln!(\"[SPAWN-{:03}] Waiting for listen address\", spawn_id);\n        let listen_addr = wait_for_listen_addr(&logs, Duration::from_secs(10), spawn_id).unwrap_or_else(|| {\n            // Dump diagnostic info on failure\n            let buf = logs.lock().unwrap();\n            eprintln!(\"[SPAWN-{:03}] TIMEOUT after 10s\", spawn_id);\n            eprintln!(\n                \"[SPAWN-{:03}] Captured {} bytes, {} lines\",\n                spawn_id,\n                buf.len(),\n                buf.lines().count()\n            );\n            eprintln!(\n                \"[SPAWN-{:03}] Contains 'Starting SpacetimeDB': {}\",\n                spawn_id,\n                buf.contains(\"Starting SpacetimeDB\")\n            );\n            // Check if process is still running\n            drop(buf); // Release lock before try_wait\n            panic!(\"Timed out waiting for SpacetimeDB to report listen address\")\n        });\n        eprintln!(\"[SPAWN-{:03}] Got listen_addr={}\", spawn_id, listen_addr);\n\n        let host_url = format!(\"http://{}\", listen_addr);\n\n        // Wait until HTTP is ready\n        eprintln!(\"[SPAWN-{:03}] Waiting for HTTP ready\", spawn_id);\n        let client = Client::new();\n        let deadline = Instant::now() + Duration::from_secs(10);\n        while Instant::now() < deadline {\n            let url = format!(\"{}/v1/ping\", host_url);\n            if let Ok(resp) = client.get(&url).send()\n                && resp.status().is_success()\n            {\n                eprintln!(\"[SPAWN-{:03}] HTTP ready at {}\", spawn_id, host_url);\n                return (child, logs, host_url, reader_threads);\n            }\n            sleep(Duration::from_millis(50));\n        }\n        panic!(\"Timed out waiting for SpacetimeDB HTTP /v1/ping at {}\", host_url);\n    }\n\n    fn spawn_child(\n        mut cmd: Command,\n        args: &[&str],\n        spawn_id: u64,\n    ) -> (Child, Arc<Mutex<String>>, Vec<thread::JoinHandle<()>>) {\n        eprintln!(\"[SPAWN-{:03}] spawn_child: about to spawn\", spawn_id);\n\n        let mut child = cmd\n            .args(args)\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped())\n            .spawn()\n            .expect(\"failed to spawn spacetimedb-cli\");\n\n        let pid = child.id();\n        eprintln!(\"[SPAWN-{:03}] spawn_child: spawned pid={}\", spawn_id, pid);\n\n        let logs = Arc::new(Mutex::new(String::new()));\n        let mut reader_threads = Vec::new();\n\n        // Attach stdout logger with diagnostic logging\n        if let Some(stdout) = child.stdout.take() {\n            let logs_clone = logs.clone();\n            let handle = thread::spawn(move || {\n                eprintln!(\"[READER-{:03}] stdout reader started for pid={}\", spawn_id, pid);\n                let reader = BufReader::new(stdout);\n                let mut line_count = 0;\n                for line in reader.lines().map_while(Result::ok) {\n                    line_count += 1;\n                    // Log the first few lines and any line containing the listen address\n                    if line_count <= 5 || line.contains(\"Starting SpacetimeDB\") {\n                        eprintln!(\"[READER-{:03}] stdout line {}: {:.100}\", spawn_id, line_count, line);\n                    }\n                    let mut buf = logs_clone.lock().unwrap();\n                    buf.push_str(\"[STDOUT] \");\n                    buf.push_str(&line);\n                    buf.push('\\n');\n                }\n                eprintln!(\n                    \"[READER-{:03}] stdout reader ended, {} lines total\",\n                    spawn_id, line_count\n                );\n            });\n            reader_threads.push(handle);\n        }\n\n        // Attach stderr logger with diagnostic logging\n        if let Some(stderr) = child.stderr.take() {\n            let logs_clone = logs.clone();\n            let handle = thread::spawn(move || {\n                eprintln!(\"[READER-{:03}] stderr reader started for pid={}\", spawn_id, pid);\n                let reader = BufReader::new(stderr);\n                let mut line_count = 0;\n                for line in reader.lines().map_while(Result::ok) {\n                    line_count += 1;\n                    // Log the first few lines and any errors\n                    if line_count <= 5 || line.contains(\"error\") || line.contains(\"Error\") {\n                        eprintln!(\"[READER-{:03}] stderr line {}: {:.100}\", spawn_id, line_count, line);\n                    }\n                    let mut buf = logs_clone.lock().unwrap();\n                    buf.push_str(\"[STDERR] \");\n                    buf.push_str(&line);\n                    buf.push('\\n');\n                }\n                eprintln!(\n                    \"[READER-{:03}] stderr reader ended, {} lines total\",\n                    spawn_id, line_count\n                );\n            });\n            reader_threads.push(handle);\n        }\n\n        eprintln!(\"[SPAWN-{:03}] spawn_child: readers attached\", spawn_id);\n        (child, logs, reader_threads)\n    }\n}\n\n/// Wait for a line like:\n/// \"... Starting SpacetimeDB listening on 0.0.0.0:24326\"\nfn wait_for_listen_addr(logs: &Arc<Mutex<String>>, timeout: Duration, spawn_id: u64) -> Option<SocketAddr> {\n    let start = Instant::now();\n    let deadline = start + timeout;\n    let mut last_len = 0;\n    let mut last_report = Instant::now();\n\n    while Instant::now() < deadline {\n        // Always search the entire log buffer to avoid missing lines that\n        // might be split across multiple reader iterations.\n        let buf = logs.lock().unwrap().clone();\n\n        for line in buf.lines() {\n            if let Some(addr) = parse_listen_addr_from_line(line) {\n                eprintln!(\"[SPAWN-{:03}] Found listen addr after {:?}\", spawn_id, start.elapsed());\n                return Some(addr);\n            }\n        }\n\n        // Progress report every 2 seconds\n        let current_len = buf.len();\n        if last_report.elapsed() > Duration::from_secs(2) {\n            let delta = current_len.saturating_sub(last_len);\n            eprintln!(\n                \"[SPAWN-{:03}] Waiting: {} bytes (+{}), {} lines, {:?} elapsed\",\n                spawn_id,\n                current_len,\n                delta,\n                buf.lines().count(),\n                start.elapsed()\n            );\n            last_len = current_len;\n            last_report = Instant::now();\n        }\n\n        sleep(Duration::from_millis(25));\n    }\n\n    // Debug output on timeout\n    let buf = logs.lock().unwrap().clone();\n    eprintln!(\n        \"[SPAWN-{:03}] wait_for_listen_addr TIMEOUT: {} bytes, {} lines, elapsed {:?}\",\n        spawn_id,\n        buf.len(),\n        buf.lines().count(),\n        start.elapsed()\n    );\n    eprintln!(\n        \"[SPAWN-{:03}] Contains 'Starting SpacetimeDB': {}\",\n        spawn_id,\n        buf.contains(\"Starting SpacetimeDB\")\n    );\n    // Show first 500 chars\n    let preview: String = buf.chars().take(500).collect();\n    eprintln!(\"[SPAWN-{:03}] First 500 chars: {:?}\", spawn_id, preview);\n\n    None\n}\n\nfn parse_listen_addr_from_line(line: &str) -> Option<SocketAddr> {\n    const PREFIX: &str = \"Starting SpacetimeDB listening on \";\n\n    let i = line.find(PREFIX)?;\n    let rest = line[i + PREFIX.len()..].trim();\n\n    // Next token should be the socket address (e.g. \"0.0.0.0:24326\" or \"[::]:24326\")\n    let token = rest.split_whitespace().next()?;\n    token.parse::<SocketAddr>().ok()\n}\n\nimpl Drop for SpacetimeDbGuard {\n    fn drop(&mut self) {\n        self.kill_process();\n\n        // Only print logs if the test is currently panicking\n        if std::thread::panicking()\n            && let Ok(logs) = self.logs.lock()\n        {\n            eprintln!(\n                    \"\\n===== SpacetimeDB child logs (only on failure) =====\\n{}\\n====================================================\",\n                    *logs\n                );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-lib\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"A common library for SpacetimeDB\"\nrust-version.workspace = true\n\n[lib]\n# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\nbench = false\n\n[[test]]\nname = \"serde\"\nrequired-features = [\"serde\"]\n\n[features]\ndefault = [\"serde\", \"metrics_impls\"]\nserde = [\"dep:serde\", \"spacetimedb-sats/serde\"]\n# Allows using `Arbitrary` impls defined in this crate.\nproptest = [\"dep:proptest\", \"dep:proptest-derive\"]\n# Allows using additional test methods.\ntest = [\"proptest\", \"spacetimedb-sats/test\"]\nmetrics_impls = [\"dep:spacetimedb-metrics\", \"spacetimedb-sats/metrics_impls\"]\nenum-map = [\"dep:enum-map\"]\n# Gated to avoid including this in `spacetimedb_bindings`.\nmemory-usage = [\n    \"dep:spacetimedb-memory-usage\",\n    \"spacetimedb-primitives/memory-usage\",\n    \"spacetimedb-sats/memory-usage\",\n]\n\n[dependencies]\nspacetimedb-bindings-macro.workspace = true\nspacetimedb-sats.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-memory-usage = { workspace = true, optional = true }\nspacetimedb-metrics = { workspace = true, optional = true }\n\nanyhow.workspace = true\nbitflags.workspace = true\nchrono.workspace = true\nderive_more.workspace = true\nenum-as-inner.workspace = true\nhex.workspace = true\nitertools.workspace = true\nlog.workspace = true\nserde = { workspace = true, optional = true }\nthiserror.workspace = true\nblake3.workspace = true\nenum-map = { workspace = true, optional = true }\n\n# For the 'proptest' feature.\nproptest = { workspace = true, optional = true }\nproptest-derive = { workspace = true, optional = true }\n\n[dev-dependencies]\nspacetimedb-sats = { path = \"../sats\", features = [\"test\"] }\nbytes.workspace = true\nserde_json.workspace = true\ninsta.workspace = true\nron.workspace = true\n\n# Also as dev-dependencies for use in _this_ crate's tests.\nproptest.workspace = true\nproptest-derive.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/lib/README.md",
    "content": "> ⚠️ **Unstable Crate** ⚠️\n>\n> The interface of this crate is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/lib/build.rs",
    "content": "use std::process::Command;\n\n// https://stackoverflow.com/questions/43753491/include-git-commit-hash-as-string-into-rust-program\n#[allow(clippy::disallowed_macros)]\nfn main() {\n    let git_hash = find_git_hash();\n    println!(\"cargo:rustc-env=GIT_HASH={git_hash}\");\n}\n\nfn nix_injected_commit_hash() -> Option<String> {\n    use std::env::VarError;\n    // Our flake.nix sets this environment variable to be our git commit hash during the build.\n    // This is important because git metadata is otherwise not available within the nix build sandbox,\n    // and we don't install the git command-line tool in our build.\n    match std::env::var(\"SPACETIMEDB_NIX_BUILD_GIT_COMMIT\") {\n        Ok(commit_sha) => {\n            // Var is set, we're building under Nix.\n            Some(commit_sha)\n        }\n\n        Err(VarError::NotPresent) => {\n            // Var is not set, we're not in Nix.\n            None\n        }\n        Err(VarError::NotUnicode(gross)) => {\n            // Var is set but is invalid unicode, something is very wrong.\n            panic!(\"Injected commit hash is not valid unicode: {gross:?}\")\n        }\n    }\n}\n\nfn find_git_hash() -> String {\n    nix_injected_commit_hash().unwrap_or_else(|| {\n        // When we're *not* building in Nix, we can assume that git metadata is still present in the filesystem,\n        // and that the git command-line tool is installed.\n        let output = Command::new(\"git\").args([\"rev-parse\", \"HEAD\"]).output().unwrap();\n        String::from_utf8(output.stdout).unwrap().trim().to_string()\n    })\n}\n"
  },
  {
    "path": "crates/lib/proptest-regressions/address.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 4dc1661cd0b78ee7036f894d2c7cf52955f05e41bb0322095ec00edf9b6fca77 # shrinks to val = 18446744073709551616\n"
  },
  {
    "path": "crates/lib/proptest-regressions/db/column_ordering.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc d098ce789585509a02ec9ebca8809bb0515e306ecdcbf4d7f883b571ff56b919 # shrinks to v = [42, 42]\n"
  },
  {
    "path": "crates/lib/proptest-regressions/identity.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 28ebdabf28ac09f38e0e0ddd728d32273f0dcffd43fa53bf1eaa8a01d1b38d90 # shrinks to w0 = 0, w1 = 1\n"
  },
  {
    "path": "crates/lib/src/connection_id.rs",
    "content": "use anyhow::Context as _;\nuse core::{fmt, net::Ipv6Addr};\nuse spacetimedb_bindings_macro::{Deserialize, Serialize};\nuse spacetimedb_lib::from_hex_pad;\nuse spacetimedb_sats::hex::HexString;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, AlgebraicType, AlgebraicValue};\n\n/// A unique identifier for a client connection to a SpacetimeDB database.\n///\n/// This is a special type.\n///\n/// A `ConnectionId` is a 128-bit unsigned integer. This can be serialized in various ways.\n/// - In JSON, an `ConnectionId` is represented as a BARE DECIMAL number.\n///   This requires some care when deserializing; see\n///   <https://stackoverflow.com/questions/69644298/how-to-make-json-parse-to-treat-all-the-numbers-as-bigint>\n/// - In BSATN, a `ConnectionId` is represented as a LITTLE-ENDIAN number 16 bytes long.\n/// - In memory, a `ConnectionId` is stored as a 128-bit number with the endianness of the host system.\n//\n// If you are manually converting a hexadecimal string to a byte array like so:\n// ```ignore\n// \"0xb0b1b2...\"\n// ->\n// [0xb0, 0xb1, 0xb2, ...]\n// ```\n// Make sure you call `ConnectionId::from_be_byte_array` and NOT `ConnectionId::from_le_byte_array`.\n// The standard way of writing hexadecimal numbers follows a big-endian convention, if you\n// index the characters in written text in increasing order from left to right.\n#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]\npub struct ConnectionId {\n    __connection_id__: u128,\n}\n\nimpl_st!([] ConnectionId, AlgebraicType::connection_id());\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for ConnectionId {}\n\n#[cfg(feature = \"metrics_impls\")]\nimpl spacetimedb_metrics::typed_prometheus::AsPrometheusLabel for ConnectionId {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n        self.to_hex()\n    }\n}\n\nimpl fmt::Display for ConnectionId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.pad(&self.to_hex())\n    }\n}\n\nimpl fmt::Debug for ConnectionId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"ConnectionId\").field(&format_args!(\"{self}\")).finish()\n    }\n}\n\nimpl ConnectionId {\n    pub const ZERO: Self = Self::from_u128(0);\n\n    pub const fn from_u128(__connection_id__: u128) -> Self {\n        Self { __connection_id__ }\n    }\n\n    pub const fn to_u128(&self) -> u128 {\n        self.__connection_id__\n    }\n\n    /// Create an `ConnectionId` from a little-endian byte array.\n    ///\n    /// If you are parsing an `ConnectionId` from a string,\n    /// you probably want [`Self::from_be_byte_array`] instead.\n    /// But if you need to convert a hexadecimal string to a `ConnectionId`,\n    /// just use [`Self::from_hex`].\n    pub const fn from_le_byte_array(arr: [u8; 16]) -> Self {\n        Self::from_u128(u128::from_le_bytes(arr))\n    }\n\n    /// Create an `ConnectionId` from a big-endian byte array.\n    ///\n    /// This method is the correct choice\n    /// if you have converted the bytes of a hexadecimal-formatted `ConnectionId`\n    /// to a byte array in the following way:\n    ///\n    /// ```ignore\n    /// \"0xb0b1b2...\"\n    /// ->\n    /// [0xb0, 0xb1, 0xb2, ...]\n    /// ```\n    ///\n    /// But if you need to convert a hexadecimal string to a `ConnectionId`,\n    /// just use [`Self::from_hex`].\n    pub const fn from_be_byte_array(arr: [u8; 16]) -> Self {\n        Self::from_u128(u128::from_be_bytes(arr))\n    }\n\n    /// Convert a `ConnectionId` to a little-endian byte array.\n    pub const fn as_le_byte_array(&self) -> [u8; 16] {\n        self.__connection_id__.to_le_bytes()\n    }\n\n    /// Convert a `ConnectionId` to a big-endian byte array.\n    ///\n    /// This is a format suitable for printing as a hexadecimal string.\n    /// But if you need to convert a `ConnectionId` to a hexadecimal string,\n    /// just use [`Self::to_hex`].\n    pub const fn as_be_byte_array(&self) -> [u8; 16] {\n        self.__connection_id__.to_be_bytes()\n    }\n\n    /// Parse a hexadecimal string into a `ConnectionId`.\n    pub fn from_hex(hex: &str) -> Result<Self, anyhow::Error> {\n        from_hex_pad::<[u8; 16], _>(hex)\n            .context(\"ConnectionIds must be 32 hex characters (16 bytes) in length.\")\n            .map(Self::from_be_byte_array)\n    }\n\n    /// Convert this `ConnectionId` to a hexadecimal string.\n    pub fn to_hex(self) -> HexString<16> {\n        spacetimedb_sats::hex::encode(&self.as_be_byte_array())\n    }\n\n    /// Extract the first 8 bytes of this `ConnectionId` as if it was stored in big-endian\n    /// format. (That is, the most significant bytes.)\n    pub fn abbreviate(&self) -> [u8; 8] {\n        self.as_be_byte_array()[..8].try_into().unwrap()\n    }\n\n    /// Extract the first 16 characters of this `ConnectionId`'s hexadecimal representation.\n    pub fn to_abbreviated_hex(self) -> HexString<8> {\n        spacetimedb_sats::hex::encode(&self.abbreviate())\n    }\n\n    /// Create an `ConnectionId` from a slice, assumed to be in big-endian format.\n    pub fn from_be_slice(slice: impl AsRef<[u8]>) -> Self {\n        let slice = slice.as_ref();\n        let mut dst = [0u8; 16];\n        dst.copy_from_slice(slice);\n        Self::from_be_byte_array(dst)\n    }\n\n    pub fn to_ipv6(self) -> Ipv6Addr {\n        Ipv6Addr::from(self.__connection_id__)\n    }\n\n    #[allow(dead_code)]\n    pub fn to_ipv6_string(self) -> String {\n        self.to_ipv6().to_string()\n    }\n\n    pub fn none_if_zero(self) -> Option<Self> {\n        (self != Self::ZERO).then_some(self)\n    }\n}\n\nimpl From<u128> for ConnectionId {\n    fn from(value: u128) -> Self {\n        Self::from_u128(value)\n    }\n}\n\nimpl From<ConnectionId> for AlgebraicValue {\n    fn from(value: ConnectionId) -> Self {\n        AlgebraicValue::product([value.to_u128().into()])\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct ConnectionIdForUrl(u128);\n\nimpl From<ConnectionId> for ConnectionIdForUrl {\n    fn from(addr: ConnectionId) -> Self {\n        ConnectionIdForUrl(addr.to_u128())\n    }\n}\n\nimpl From<ConnectionIdForUrl> for ConnectionId {\n    fn from(addr: ConnectionIdForUrl) -> Self {\n        ConnectionId::from_u128(addr.0)\n    }\n}\n\nimpl_serialize!([] ConnectionIdForUrl, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] ConnectionIdForUrl, de => u128::deserialize(de).map(Self));\nimpl_st!([] ConnectionIdForUrl, AlgebraicType::U128);\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for ConnectionIdForUrl {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        spacetimedb_sats::ser::serde::serialize_to(&ConnectionId::from(*self).as_be_byte_array(), serializer)\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<'de> serde::Deserialize<'de> for ConnectionIdForUrl {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let arr = spacetimedb_sats::de::serde::deserialize_from(deserializer)?;\n        Ok(ConnectionId::from_be_byte_array(arr).into())\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for ConnectionId {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        spacetimedb_sats::ser::serde::serialize_to(&self.as_be_byte_array(), serializer)\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<'de> serde::Deserialize<'de> for ConnectionId {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let arr = spacetimedb_sats::de::serde::deserialize_from(deserializer)?;\n        Ok(ConnectionId::from_be_byte_array(arr))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n    use spacetimedb_sats::bsatn;\n    use spacetimedb_sats::ser::serde::SerializeWrapper;\n    use spacetimedb_sats::GroundSpacetimeType as _;\n\n    #[test]\n    fn connection_id_json_serialization_big_endian() {\n        let conn_id = ConnectionId::from_be_byte_array([0xff, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);\n\n        let hex = conn_id.to_hex();\n        assert!(\n            hex.as_str().starts_with(\"ff01\"),\n            \"expected {hex:?} to start with \\\"ff01\\\"\"\n        );\n\n        let json1 = serde_json::to_string(&conn_id).unwrap();\n        let json2 = serde_json::to_string(&ConnectionIdForUrl::from(conn_id)).unwrap();\n\n        assert!(\n            json1.contains(hex.as_str()),\n            \"expected {json1} to contain {hex} but it didn't\"\n        );\n        assert!(\n            json2.contains(hex.as_str()),\n            \"expected {json2} to contain {hex} but it didn't\"\n        );\n\n        // Serde made the slightly odd choice to serialize u128 as decimals in JSON.\n        // So we have an incompatibility between our formats here :/\n        // The implementation of serialization for `sats` types via `SerializeWrapper` just calls\n        // the `serde` implementation to serialize primitives, so we can't fix this\n        // unless we make a custom implementation of `Serialize` and `Deserialize` for `ConnectionId`.\n        let decimal = conn_id.to_u128().to_string();\n        let json3 = serde_json::to_string(SerializeWrapper::from_ref(&conn_id)).unwrap();\n        assert!(\n            json3.contains(decimal.as_str()),\n            \"expected {json3} to contain {decimal} but it didn't\"\n        );\n    }\n\n    proptest! {\n        #[test]\n        fn test_bsatn_roundtrip(val: u128) {\n            let conn_id = ConnectionId::from_u128(val);\n            let ser = bsatn::to_vec(&conn_id).unwrap();\n            let de = bsatn::from_slice(&ser).unwrap();\n            assert_eq!(conn_id, de);\n        }\n\n        #[test]\n        fn connection_id_conversions(a: u128) {\n            let v = ConnectionId::from_u128(a);\n\n            prop_assert_eq!(ConnectionId::from_le_byte_array(v.as_le_byte_array()), v);\n            prop_assert_eq!(ConnectionId::from_be_byte_array(v.as_be_byte_array()), v);\n            prop_assert_eq!(ConnectionId::from_hex(v.to_hex().as_str()).unwrap(), v);\n        }\n    }\n\n    #[test]\n    fn connection_id_is_special() {\n        assert!(ConnectionId::get_type().is_special());\n    }\n\n    #[cfg(feature = \"serde\")]\n    mod serde {\n        use super::*;\n        use crate::sats::{algebraic_value::de::ValueDeserializer, de::Deserialize, Typespace};\n        use crate::ser::serde::SerializeWrapper;\n        use crate::WithTypespace;\n\n        proptest! {\n            /// Tests the round-trip used when using the `spacetime subscribe`\n            /// CLI command.\n            /// Somewhat confusingly, this is distinct from the ser-de path\n            /// in `test_serde_roundtrip`.\n            #[test]\n            fn test_wrapper_roundtrip(val: u128) {\n                let conn_id = ConnectionId::from_u128(val);\n                let wrapped = SerializeWrapper::new(&conn_id);\n\n                let ser = serde_json::to_string(&wrapped).unwrap();\n                let empty = Typespace::default();\n                let conn_id_ty = ConnectionId::get_type();\n                let conn_id_ty = WithTypespace::new(&empty, &conn_id_ty);\n                let row = serde_json::from_str::<serde_json::Value>(&ser[..])?;\n                let de = ::serde::de::DeserializeSeed::deserialize(\n                    crate::de::serde::SeedWrapper(\n                        conn_id_ty\n                    ),\n                    row)?;\n                let de = ConnectionId::deserialize(ValueDeserializer::new(de)).unwrap();\n                prop_assert_eq!(conn_id, de);\n            }\n        }\n\n        proptest! {\n            #[test]\n            fn test_serde_roundtrip(val: u128) {\n                let conn_id = ConnectionId::from_u128(val);\n                let to_url = ConnectionIdForUrl::from(conn_id);\n                let ser = serde_json::to_vec(&to_url).unwrap();\n                let de = serde_json::from_slice::<ConnectionIdForUrl>(&ser).unwrap();\n                let from_url = ConnectionId::from(de);\n                prop_assert_eq!(conn_id, from_url);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/db/attr.rs",
    "content": "pub use spacetimedb_primitives::ColumnAttribute;\n"
  },
  {
    "path": "crates/lib/src/db/auth.rs",
    "content": "use crate::de::Error;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, AlgebraicType};\nuse std::borrow::Cow;\n\n/// Describe the visibility of the table\n#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum StAccess {\n    /// Visible to all\n    Public,\n    /// Visible only to the owner\n    Private,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for StAccess {}\n\nimpl StAccess {\n    pub fn as_str(&self) -> &'static str {\n        match self {\n            Self::Public => \"public\",\n            Self::Private => \"private\",\n        }\n    }\n}\n\nimpl<'a> TryFrom<&'a str> for StAccess {\n    type Error = &'a str;\n\n    fn try_from(value: &'a str) -> Result<Self, Self::Error> {\n        Ok(match value {\n            \"public\" => Self::Public,\n            \"private\" => Self::Private,\n            x => return Err(x),\n        })\n    }\n}\n\nimpl_serialize!([] StAccess, (self, ser) => ser.serialize_str(self.as_str()));\nimpl_deserialize!([] StAccess, de => {\n    let value = Cow::<'_, str>::deserialize(de)?;\n    StAccess::try_from(&*value).map_err(|x| {\n        Error::custom(format!(\n            \"DecodeError for StAccess: `{x}`. Expected `public` | 'private'\"\n        ))\n    })\n});\nimpl_st!([] StAccess, AlgebraicType::String);\n\n/// Describe is the table is a `system table` or not.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum StTableType {\n    /// Created by the system\n    ///\n    /// System tables are `StAccess::Public` by default\n    System,\n    /// Created by the User\n    User,\n}\n\nimpl StTableType {\n    pub fn as_str(&self) -> &'static str {\n        match self {\n            Self::System => \"system\",\n            Self::User => \"user\",\n        }\n    }\n}\n\nimpl<'a> TryFrom<&'a str> for StTableType {\n    type Error = &'a str;\n\n    fn try_from(value: &'a str) -> Result<Self, Self::Error> {\n        Ok(match value {\n            \"system\" => Self::System,\n            \"user\" => Self::User,\n            x => return Err(x),\n        })\n    }\n}\n\nimpl_serialize!([] StTableType, (self, ser) => ser.serialize_str(self.as_str()));\nimpl_deserialize!([] StTableType, de => {\n    let value = Cow::<'_, str>::deserialize(de)?;\n    StTableType::try_from(&*value).map_err(|x| {\n        Error::custom(format!(\n            \"DecodeError for StTableType: `{x}`. Expected 'system' | 'user'\"\n        ))\n    })\n});\nimpl_st!([] StTableType, AlgebraicType::String);\n"
  },
  {
    "path": "crates/lib/src/db/default_element_ordering.rs",
    "content": "//! This module defines the default ordering for fields of a `ProductType` and variants of a `SumType`.\n//!\n//! - In ABI version 8, the default ordering was not applied.\n//! - In ABI version 9, the default ordering is applied to all types in a spacetime module, unless they explicitly declare a custom ordering.\n\nuse spacetimedb_sats::{ProductType, ProductTypeElement, SumType, SumTypeVariant};\n\n/// A label for a field of a `ProductType` or a variant of a `SumType`.\n///\n/// The ordering on this type defines the default ordering for the fields of a `ProductType` and the variants of a `SumType`.\n#[derive(PartialEq, Eq, PartialOrd, Ord)]\npub enum ElementLabel<'a> {\n    /// An unnamed field with a position.\n    /// The unnamed fields in a type do not necessarily have contiguous positions.\n    Unnamed(usize),\n    /// A named field.\n    /// Names are required to be unique within the product type.\n    Named(&'a str),\n}\n\nimpl<'a> From<(usize, &'a ProductTypeElement)> for ElementLabel<'a> {\n    fn from((i, element): (usize, &'a ProductTypeElement)) -> Self {\n        match &element.name {\n            Some(name) => ElementLabel::Named(&name[..]),\n            None => ElementLabel::Unnamed(i),\n        }\n    }\n}\nimpl<'a> From<(usize, &'a SumTypeVariant)> for ElementLabel<'a> {\n    fn from((i, element): (usize, &'a SumTypeVariant)) -> Self {\n        match &element.name {\n            Some(name) => ElementLabel::Named(&name[..]),\n            None => ElementLabel::Unnamed(i),\n        }\n    }\n}\n\n/// Checks if a sum type has the default ordering.\n///\n/// Not a recursive check.\npub fn sum_type_has_default_ordering(ty: &SumType) -> bool {\n    ty.variants.iter().enumerate().map(ElementLabel::from).is_sorted()\n}\n\n/// Checks if a product type has the default ordering.\n///\n/// Not a recursive check.\npub fn product_type_has_default_ordering(ty: &ProductType) -> bool {\n    ty.elements.iter().enumerate().map(ElementLabel::from).is_sorted()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_element_label_comparison() {\n        let a = ElementLabel::Unnamed(0);\n        let b = ElementLabel::Unnamed(2);\n        let c = ElementLabel::Named(\"apples\");\n        let d = ElementLabel::Named(\"oranges\");\n        let e = ElementLabel::Named(\"oranges_tomorrow\");\n\n        assert!(a == a);\n        assert!(a < b);\n        assert!(a < c);\n        assert!(a < d);\n        assert!(a < e);\n\n        assert!(b > a);\n        assert!(b == b);\n        assert!(b < c);\n        assert!(b < d);\n        assert!(b < e);\n\n        assert!(c > a);\n        assert!(c > b);\n        assert!(c == c);\n        assert!(c < d);\n        assert!(c < e);\n\n        assert!(d > a);\n        assert!(d > b);\n        assert!(d > c);\n        assert!(d == d);\n        assert!(d < e);\n\n        assert!(e > a);\n        assert!(e > b);\n        assert!(e > c);\n        assert!(e > d);\n        assert!(e == e);\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/db/mod.rs",
    "content": "//! Defines all the typed database objects & schemas.\n\npub mod attr;\npub mod auth;\npub mod default_element_ordering;\npub mod raw_def;\npub mod view;\n"
  },
  {
    "path": "crates/lib/src/db/raw_def/v10.rs",
    "content": "//! ABI Version 10 of the raw module definitions.\n//!\n//! This is a refactored version of V9 with a section-based structure.\n//! V10 moves schedules, lifecycle reducers, and default values out of their V9 locations\n//! into dedicated sections for cleaner organization.\n//! It allows easier future extensibility to add new kinds of definitions.\n\nuse crate::db::raw_def::v9::{Lifecycle, RawIndexAlgorithm, TableAccess, TableType};\nuse core::fmt;\nuse spacetimedb_primitives::{ColId, ColList};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::typespace::TypespaceBuilder;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, AlgebraicValue, ProductType, SpacetimeType, Typespace};\nuse std::any::TypeId;\nuse std::collections::{btree_map, BTreeMap};\n\n/// A possibly-invalid raw module definition.\n///\n/// ABI Version 10.\n///\n/// These \"raw definitions\" may contain invalid data, and are validated by the `validate` module\n/// into a proper `spacetimedb_schema::ModuleDef`, or a collection of errors.\n///\n/// The module definition maintains the same logical global namespace as V9, mapping `Identifier`s to:\n///\n/// - database-level objects:\n///     - logical schema objects: tables, constraints, sequence definitions\n///     - physical schema objects: indexes\n/// - module-level objects: reducers, procedures, schedule definitions\n/// - binding-level objects: type aliases\n///\n/// All of these types of objects must have unique names within the module.\n/// The exception is columns, which need unique names only within a table.\n#[derive(Default, Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawModuleDefV10 {\n    /// The sections comprising this module definition.\n    ///\n    /// Sections can appear in any order and are optional.\n    pub sections: Vec<RawModuleDefV10Section>,\n}\n\n/// A section of a V10 module definition.\n///\n/// New variants MUST be added to the END of this enum, to maintain ABI compatibility.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[non_exhaustive]\npub enum RawModuleDefV10Section {\n    /// The `Typespace` used by the module.\n    ///\n    /// `AlgebraicTypeRef`s in other sections refer to this typespace.\n    /// See [`crate::db::raw_def::v9::RawModuleDefV9::typespace`] for validation requirements.\n    Typespace(Typespace),\n\n    /// Type definitions exported by the module.\n    Types(Vec<RawTypeDefV10>),\n\n    /// Table definitions.\n    Tables(Vec<RawTableDefV10>),\n\n    /// Reducer definitions.\n    Reducers(Vec<RawReducerDefV10>),\n\n    /// Procedure definitions.\n    Procedures(Vec<RawProcedureDefV10>),\n\n    /// View definitions.\n    Views(Vec<RawViewDefV10>),\n\n    /// Schedule definitions.\n    ///\n    /// Unlike V9 where schedules were embedded in table definitions,\n    /// V10 stores them in a dedicated section.\n    Schedules(Vec<RawScheduleDefV10>),\n\n    /// Lifecycle reducer assignments.\n    ///\n    /// Unlike V9 where lifecycle was a field on reducers,\n    /// V10 stores lifecycle-to-reducer mappings separately.\n    LifeCycleReducers(Vec<RawLifeCycleReducerDefV10>),\n\n    RowLevelSecurity(Vec<RawRowLevelSecurityDefV10>), //TODO: Add section for Event tables, and Case conversion before exposing this from module\n\n    /// Case conversion policy for identifiers in this module.\n    CaseConversionPolicy(CaseConversionPolicy),\n\n    /// Names provided explicitly by the user that do not follow from the case conversion policy.\n    ExplicitNames(ExplicitNames),\n}\n\n#[derive(Debug, Clone, Copy, Default, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\n#[non_exhaustive]\npub enum CaseConversionPolicy {\n    /// No conversion - names used verbatim as canonical names\n    None,\n    /// Convert to snake_case (SpacetimeDB default)\n    #[default]\n    SnakeCase,\n}\n\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, Ord, PartialOrd))]\n#[non_exhaustive]\npub struct NameMapping {\n    /// The original name as defined or generated inside module.\n    ///\n    /// Generated as:\n    /// - Tables: value from `#[spacetimedb::table(accessor = ...)]`.\n    /// - Reducers/Procedures/Views: function name\n    /// - Indexes: `{table_name}_{column_names}_idx_{algorithm}`\n    ///\n    /// During validation, this may be replaced by `canonical_name`\n    /// if an explicit or policy-based name is applied.\n    pub source_name: RawIdentifier,\n\n    /// The canonical identifier used in system tables and client code generation.\n    ///\n    /// Set via:\n    /// - `#[spacetimedb::table(name = \"...\")]` for tables\n    /// - `#[spacetimedb::reducer(name = \"...\")]` for reducers\n    /// - `#[name(\"...\")]` for other entities\n    ///\n    /// If not explicitly provided, this defaults to `source_name`\n    /// after validation. No particular format should be assumed.\n    pub canonical_name: RawIdentifier,\n}\n\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, Ord, PartialOrd))]\n#[non_exhaustive]\npub enum ExplicitNameEntry {\n    Table(NameMapping),\n    Function(NameMapping),\n    Index(NameMapping),\n}\n\n#[derive(Debug, Default, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, Ord, PartialOrd))]\n#[non_exhaustive]\npub struct ExplicitNames {\n    /// Explicit name mappings defined in the module.\n    ///\n    /// These override policy-based or auto-generated names\n    /// during schema validation.\n    entries: Vec<ExplicitNameEntry>,\n}\n\nimpl ExplicitNames {\n    fn insert(&mut self, entry: ExplicitNameEntry) {\n        self.entries.push(entry);\n    }\n\n    pub fn insert_table(&mut self, source_name: impl Into<RawIdentifier>, canonical_name: impl Into<RawIdentifier>) {\n        self.insert(ExplicitNameEntry::Table(NameMapping {\n            source_name: source_name.into(),\n            canonical_name: canonical_name.into(),\n        }));\n    }\n\n    pub fn insert_function(&mut self, source_name: impl Into<RawIdentifier>, canonical_name: impl Into<RawIdentifier>) {\n        self.insert(ExplicitNameEntry::Function(NameMapping {\n            source_name: source_name.into(),\n            canonical_name: canonical_name.into(),\n        }));\n    }\n\n    pub fn insert_index(&mut self, source_name: impl Into<RawIdentifier>, canonical_name: impl Into<RawIdentifier>) {\n        self.insert(ExplicitNameEntry::Index(NameMapping {\n            source_name: source_name.into(),\n            canonical_name: canonical_name.into(),\n        }));\n    }\n\n    pub fn merge(&mut self, other: ExplicitNames) {\n        self.entries.extend(other.entries);\n    }\n\n    pub fn into_entries(self) -> Vec<ExplicitNameEntry> {\n        self.entries\n    }\n}\n\npub type RawRowLevelSecurityDefV10 = crate::db::raw_def::v9::RawRowLevelSecurityDefV9;\n\n/// The definition of a database table.\n///\n/// This struct holds information about the table, including its name, columns, indexes,\n/// constraints, sequences, type, and access rights.\n///\n/// Validation rules are the same as V9, except:\n/// - Default values are stored inline rather than in `MiscModuleExport`\n/// - Schedules are stored in a separate section rather than embedded here\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawTableDefV10 {\n    /// The name of the table.\n    /// Unique within a module, acts as the table's identifier.\n    /// Must be a valid `spacetimedb_schema::identifier::Identifier`.\n    pub source_name: RawIdentifier,\n\n    /// A reference to a `ProductType` containing the columns of this table.\n    /// This is the single source of truth for the table's columns.\n    /// All elements of the `ProductType` must have names.\n    ///\n    /// Like all types in the module, this must have the [default element ordering](crate::db::default_element_ordering),\n    /// UNLESS a custom ordering is declared via a `RawTypeDefV10` for this type.\n    pub product_type_ref: AlgebraicTypeRef,\n\n    /// The primary key of the table, if present. Must refer to a valid column.\n    ///\n    /// Currently, there must be a unique constraint and an index corresponding to the primary key.\n    /// Eventually, we may remove the requirement for an index.\n    ///\n    /// The database engine does not actually care about this, but client code generation does.\n    ///\n    /// A list of length 0 means no primary key. Currently, a list of length >1 is not supported.\n    pub primary_key: ColList,\n\n    /// The indices of the table.\n    pub indexes: Vec<RawIndexDefV10>,\n\n    /// Any unique constraints on the table.\n    pub constraints: Vec<RawConstraintDefV10>,\n\n    /// The sequences for the table.\n    pub sequences: Vec<RawSequenceDefV10>,\n\n    /// Whether this is a system- or user-created table.\n    pub table_type: TableType,\n\n    /// Whether this table is public or private.\n    pub table_access: TableAccess,\n\n    /// Default values for columns in this table.\n    pub default_values: Vec<RawColumnDefaultValueV10>,\n\n    /// Whether this is an event table.\n    ///\n    /// Event tables are write-only: their rows are persisted to the commitlog\n    /// but are NOT merged into committed state. They are only visible to V2\n    /// subscribers in the transaction that inserted them.\n    pub is_event: bool,\n}\n\n/// Marks a particular table column as having a particular default value.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawColumnDefaultValueV10 {\n    /// Identifies which column has the default value.\n    pub col_id: ColId,\n\n    /// A BSATN-encoded [`AlgebraicValue`] valid at the column's type.\n    /// (We cannot use `AlgebraicValue` directly as it isn't `SpacetimeType`.)\n    pub value: Box<[u8]>,\n}\n\n/// A reducer definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawReducerDefV10 {\n    /// The name of the reducer.\n    pub source_name: RawIdentifier,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// Whether this reducer is callable from clients or is internal-only.\n    pub visibility: FunctionVisibility,\n\n    /// The type of the `Ok` return value.\n    pub ok_return_type: AlgebraicType,\n\n    /// The type of the `Err` return value.\n    pub err_return_type: AlgebraicType,\n}\n\n/// The visibility of a function (reducer or procedure).\n#[derive(Debug, Copy, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub enum FunctionVisibility {\n    /// Not callable by arbitrary clients.\n    ///\n    /// Still callable by the module owner, collaborators,\n    /// and internal module code.\n    ///\n    /// Enabled for lifecycle reducers and scheduled functions by default.\n    Private,\n\n    /// Callable from client code.\n    ClientCallable,\n}\n\n/// A schedule definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawScheduleDefV10 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    /// If `None`, a nicely-formatted unique default will be chosen.\n    pub source_name: Option<RawIdentifier>,\n\n    /// The name of the table containing the schedule.\n    pub table_name: RawIdentifier,\n\n    /// The column of the `scheduled_at` field in the table.\n    pub schedule_at_col: ColId,\n\n    /// The name of the reducer or procedure to call.\n    pub function_name: RawIdentifier,\n}\n\n/// A lifecycle reducer assignment.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawLifeCycleReducerDefV10 {\n    /// Which lifecycle event this reducer handles.\n    pub lifecycle_spec: Lifecycle,\n\n    /// The name of the reducer to call for this lifecycle event.\n    pub function_name: RawIdentifier,\n}\n\n/// A procedure definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawProcedureDefV10 {\n    /// The name of the procedure.\n    pub source_name: RawIdentifier,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// The type of the return value.\n    ///\n    /// If this is a user-defined product or sum type,\n    /// it should be registered in the typespace and indirected through an [`AlgebraicType::Ref`].\n    pub return_type: AlgebraicType,\n\n    /// Whether this procedure is callable from clients or is internal-only.\n    pub visibility: FunctionVisibility,\n}\n\n/// A sequence definition for a database table column.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawSequenceDefV10 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    /// If `None`, a nicely-formatted unique default will be chosen.\n    pub source_name: Option<RawIdentifier>,\n\n    /// The position of the column associated with this sequence.\n    /// This refers to a column in the same `RawTableDef` that contains this `RawSequenceDef`.\n    /// The column must have integral type.\n    /// This must be the unique `RawSequenceDef` for this column.\n    pub column: ColId,\n\n    /// The value to start assigning to this column.\n    /// Will be incremented by 1 for each new row.\n    /// If not present, an arbitrary start point may be selected.\n    pub start: Option<i128>,\n\n    /// The minimum allowed value in this column.\n    /// If not present, no minimum.\n    pub min_value: Option<i128>,\n\n    /// The maximum allowed value in this column.\n    /// If not present, no maximum.\n    pub max_value: Option<i128>,\n\n    /// The increment used when updating the SequenceDef.\n    pub increment: i128,\n}\n\n/// The definition of a database index.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawIndexDefV10 {\n    /// Must be supplied as `{table_name}_{column_names}_idx_{algorithm}`.\n    /// Where `{table_name}` is the name of the table containing in `RawTableDefV10`.\n    pub source_name: Option<RawIdentifier>,\n\n    /// `accessor_name` is the name of the index accessor function that is used inside the module\n    /// code.\n    pub accessor_name: Option<RawIdentifier>,\n\n    /// The algorithm parameters for the index.\n    pub algorithm: RawIndexAlgorithm,\n}\n\n/// A constraint definition attached to a table.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawConstraintDefV10 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    pub source_name: Option<RawIdentifier>,\n\n    /// The data for the constraint.\n    pub data: RawConstraintDataV10,\n}\n\ntype RawConstraintDataV10 = crate::db::raw_def::v9::RawConstraintDataV9;\ntype RawUniqueConstraintDataV10 = crate::db::raw_def::v9::RawUniqueConstraintDataV9;\n\n/// A type declaration.\n///\n/// Exactly of these must be attached to every `Product` and `Sum` type used by a module.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawTypeDefV10 {\n    /// The name of the type declaration.\n    pub source_name: RawScopedTypeNameV10,\n\n    /// The type to which the declaration refers.\n    /// This must point to an `AlgebraicType::Product` or an `AlgebraicType::Sum` in the module's typespace.\n    pub ty: AlgebraicTypeRef,\n\n    /// Whether this type has a custom ordering.\n    pub custom_ordering: bool,\n}\n\n/// A scoped type name, in the form `scope0::scope1::...::scopeN::name`.\n///\n/// These are the names that will be used *in client code generation*, NOT the names used for types\n/// in the module source code.\n#[derive(Clone, SpacetimeType, PartialEq, Eq, PartialOrd, Ord)]\n#[sats(crate = crate)]\npub struct RawScopedTypeNameV10 {\n    /// The scope for this type.\n    ///\n    /// Empty unless a sats `name` attribute is used, e.g.\n    /// `#[sats(name = \"namespace.name\")]` in Rust.\n    pub scope: Box<[RawIdentifier]>,\n\n    /// The name of the type. This must be unique within the module.\n    ///\n    /// Eventually, we may add more information to this, such as generic arguments.\n    pub source_name: RawIdentifier,\n}\n\nimpl fmt::Debug for RawScopedTypeNameV10 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        for module in self.scope.iter() {\n            fmt::Debug::fmt(module, f)?;\n            f.write_str(\"::\")?;\n        }\n        fmt::Debug::fmt(&self.source_name, f)?;\n        Ok(())\n    }\n}\n\n/// A view definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawViewDefV10 {\n    /// The name of the view function as defined in the module\n    pub source_name: RawIdentifier,\n\n    /// The index of the view in the module's list of views.\n    pub index: u32,\n\n    /// Is this a public or a private view?\n    /// Currently only public views are supported.\n    /// Private views may be supported in the future.\n    pub is_public: bool,\n\n    /// Is this view anonymous?\n    /// An anonymous view does not know who called it.\n    /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument.\n    /// This type does not have access to the `Identity` of the caller.\n    pub is_anonymous: bool,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// The return type of the view.\n    /// Either `T`, `Option<T>`, or `Vec<T>` where `T` is a `SpacetimeType`.\n    ///\n    /// More strictly `T` must be a SATS `ProductType`,\n    /// however this will be validated by the server on publish.\n    ///\n    /// This is the single source of truth for the views's columns.\n    /// All elements of the inner `ProductType` must have names.\n    /// This again will be validated by the server on publish.\n    pub return_type: AlgebraicType,\n}\n\nimpl RawModuleDefV10 {\n    /// Get the types section, if present.\n    pub fn types(&self) -> Option<&Vec<RawTypeDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Types(types) => Some(types),\n            _ => None,\n        })\n    }\n\n    /// Get the tables section, if present.\n    pub fn tables(&self) -> Option<&Vec<RawTableDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Tables(tables) => Some(tables),\n            _ => None,\n        })\n    }\n\n    /// Get the typespace section, if present.\n    pub fn typespace(&self) -> Option<&Typespace> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Typespace(ts) => Some(ts),\n            _ => None,\n        })\n    }\n\n    /// Get the reducers section, if present.\n    pub fn reducers(&self) -> Option<&Vec<RawReducerDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Reducers(reducers) => Some(reducers),\n            _ => None,\n        })\n    }\n\n    /// Get the procedures section, if present.\n    pub fn procedures(&self) -> Option<&Vec<RawProcedureDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Procedures(procedures) => Some(procedures),\n            _ => None,\n        })\n    }\n\n    /// Get the views section, if present.\n    pub fn views(&self) -> Option<&Vec<RawViewDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Views(views) => Some(views),\n            _ => None,\n        })\n    }\n\n    /// Get the schedules section, if present.\n    pub fn schedules(&self) -> Option<&Vec<RawScheduleDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::Schedules(schedules) => Some(schedules),\n            _ => None,\n        })\n    }\n\n    /// Get the lifecycle reducers section, if present.\n    pub fn lifecycle_reducers(&self) -> Option<&Vec<RawLifeCycleReducerDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::LifeCycleReducers(lcrs) => Some(lcrs),\n            _ => None,\n        })\n    }\n\n    pub fn tables_mut_for_tests(&mut self) -> &mut Vec<RawTableDefV10> {\n        self.sections\n            .iter_mut()\n            .find_map(|s| match s {\n                RawModuleDefV10Section::Tables(tables) => Some(tables),\n                _ => None,\n            })\n            .expect(\"Tables section must exist for tests\")\n    }\n\n    // Get the row-level security section, if present.\n    pub fn row_level_security(&self) -> Option<&Vec<RawRowLevelSecurityDefV10>> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::RowLevelSecurity(rls) => Some(rls),\n            _ => None,\n        })\n    }\n\n    pub fn case_conversion_policy(&self) -> CaseConversionPolicy {\n        self.sections\n            .iter()\n            .find_map(|s| match s {\n                RawModuleDefV10Section::CaseConversionPolicy(policy) => Some(*policy),\n                _ => None,\n            })\n            .unwrap_or_default()\n    }\n\n    pub fn explicit_names(&self) -> Option<&ExplicitNames> {\n        self.sections.iter().find_map(|s| match s {\n            RawModuleDefV10Section::ExplicitNames(names) => Some(names),\n            _ => None,\n        })\n    }\n}\n\n/// A builder for a [`RawModuleDefV10`].\n#[derive(Default)]\npub struct RawModuleDefV10Builder {\n    /// The module definition being built.\n    module: RawModuleDefV10,\n\n    /// The type map from `T: 'static` Rust types to sats types.\n    type_map: BTreeMap<TypeId, AlgebraicTypeRef>,\n}\n\nimpl RawModuleDefV10Builder {\n    /// Create a new, empty `RawModuleDefV10Builder`.\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Get mutable access to the typespace section, creating it if missing.\n    fn typespace_mut(&mut self) -> &mut Typespace {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Typespace(_)))\n            .unwrap_or_else(|| {\n                self.module\n                    .sections\n                    .push(RawModuleDefV10Section::Typespace(Typespace::EMPTY.clone()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Typespace(ts) => ts,\n            _ => unreachable!(\"Just ensured Typespace section exists\"),\n        }\n    }\n\n    /// Get mutable access to the reducers section, creating it if missing.\n    fn reducers_mut(&mut self) -> &mut Vec<RawReducerDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Reducers(_)))\n            .unwrap_or_else(|| {\n                self.module.sections.push(RawModuleDefV10Section::Reducers(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Reducers(reducers) => reducers,\n            _ => unreachable!(\"Just ensured Reducers section exists\"),\n        }\n    }\n\n    /// Get mutable access to the procedures section, creating it if missing.\n    fn procedures_mut(&mut self) -> &mut Vec<RawProcedureDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Procedures(_)))\n            .unwrap_or_else(|| {\n                self.module\n                    .sections\n                    .push(RawModuleDefV10Section::Procedures(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Procedures(procedures) => procedures,\n            _ => unreachable!(\"Just ensured Procedures section exists\"),\n        }\n    }\n\n    /// Get mutable access to the views section, creating it if missing.\n    fn views_mut(&mut self) -> &mut Vec<RawViewDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Views(_)))\n            .unwrap_or_else(|| {\n                self.module.sections.push(RawModuleDefV10Section::Views(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Views(views) => views,\n            _ => unreachable!(\"Just ensured Views section exists\"),\n        }\n    }\n\n    /// Get mutable access to the schedules section, creating it if missing.\n    fn schedules_mut(&mut self) -> &mut Vec<RawScheduleDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Schedules(_)))\n            .unwrap_or_else(|| {\n                self.module.sections.push(RawModuleDefV10Section::Schedules(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Schedules(schedules) => schedules,\n            _ => unreachable!(\"Just ensured Schedules section exists\"),\n        }\n    }\n\n    /// Get mutable access to the lifecycle reducers section, creating it if missing.\n    fn lifecycle_reducers_mut(&mut self) -> &mut Vec<RawLifeCycleReducerDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::LifeCycleReducers(_)))\n            .unwrap_or_else(|| {\n                self.module\n                    .sections\n                    .push(RawModuleDefV10Section::LifeCycleReducers(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::LifeCycleReducers(lcrs) => lcrs,\n            _ => unreachable!(\"Just ensured LifeCycleReducers section exists\"),\n        }\n    }\n\n    /// Get mutable access to the types section, creating it if missing.\n    fn types_mut(&mut self) -> &mut Vec<RawTypeDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::Types(_)))\n            .unwrap_or_else(|| {\n                self.module.sections.push(RawModuleDefV10Section::Types(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::Types(types) => types,\n            _ => unreachable!(\"Just ensured Types section exists\"),\n        }\n    }\n\n    /// Add a type to the in-progress module.\n    ///\n    /// The returned type must satisfy `AlgebraicType::is_valid_for_client_type_definition` or `AlgebraicType::is_valid_for_client_type_use`.\n    pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {\n        TypespaceBuilder::add_type::<T>(self)\n    }\n\n    /// Get mutable access to the row-level security section, creating it if missing.\n    fn row_level_security_mut(&mut self) -> &mut Vec<RawRowLevelSecurityDefV10> {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::RowLevelSecurity(_)))\n            .unwrap_or_else(|| {\n                self.module\n                    .sections\n                    .push(RawModuleDefV10Section::RowLevelSecurity(Vec::new()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::RowLevelSecurity(rls) => rls,\n            _ => unreachable!(\"Just ensured RowLevelSecurity section exists\"),\n        }\n    }\n\n    /// Get mutable access to the case conversion policy, creating it if missing.\n    fn explicit_names_mut(&mut self) -> &mut ExplicitNames {\n        let idx = self\n            .module\n            .sections\n            .iter()\n            .position(|s| matches!(s, RawModuleDefV10Section::ExplicitNames(_)))\n            .unwrap_or_else(|| {\n                self.module\n                    .sections\n                    .push(RawModuleDefV10Section::ExplicitNames(ExplicitNames::default()));\n                self.module.sections.len() - 1\n            });\n\n        match &mut self.module.sections[idx] {\n            RawModuleDefV10Section::ExplicitNames(names) => names,\n            _ => unreachable!(\"Just ensured ExplicitNames section exists\"),\n        }\n    }\n\n    /// Create a table builder.\n    ///\n    /// Does not validate that the product_type_ref is valid; this is left to the module validation code.\n    pub fn build_table(\n        &mut self,\n        source_name: impl Into<RawIdentifier>,\n        product_type_ref: AlgebraicTypeRef,\n    ) -> RawTableDefBuilderV10<'_> {\n        let source_name = source_name.into();\n        RawTableDefBuilderV10 {\n            module: &mut self.module,\n            table: RawTableDefV10 {\n                source_name,\n                product_type_ref,\n                indexes: vec![],\n                constraints: vec![],\n                sequences: vec![],\n                primary_key: ColList::empty(),\n                table_type: TableType::User,\n                table_access: TableAccess::Public,\n                default_values: vec![],\n                is_event: false,\n            },\n        }\n    }\n\n    /// Build a new table with a product type.\n    /// Adds the type to the module.\n    pub fn build_table_with_new_type(\n        &mut self,\n        table_name: impl Into<RawIdentifier>,\n        product_type: impl Into<ProductType>,\n        custom_ordering: bool,\n    ) -> RawTableDefBuilderV10<'_> {\n        let table_name = table_name.into();\n\n        let product_type_ref = self.add_algebraic_type(\n            [],\n            table_name.clone(),\n            AlgebraicType::from(product_type.into()),\n            custom_ordering,\n        );\n\n        self.build_table(table_name, product_type_ref)\n    }\n\n    /// Build a new table with a product type, for testing.\n    /// Adds the type to the module.\n    pub fn build_table_with_new_type_for_tests(\n        &mut self,\n        table_name: impl Into<RawIdentifier>,\n        mut product_type: ProductType,\n        custom_ordering: bool,\n    ) -> RawTableDefBuilderV10<'_> {\n        self.add_expand_product_type_for_tests(&mut 0, &mut product_type);\n        self.build_table_with_new_type(table_name, product_type, custom_ordering)\n    }\n\n    fn add_expand_type_for_tests(&mut self, name_gen: &mut usize, ty: &mut AlgebraicType) {\n        if ty.is_valid_for_client_type_use() {\n            return;\n        }\n\n        match ty {\n            AlgebraicType::Product(prod_ty) => self.add_expand_product_type_for_tests(name_gen, prod_ty),\n            AlgebraicType::Sum(sum_type) => {\n                if let Some(wrapped) = sum_type.as_option_mut() {\n                    self.add_expand_type_for_tests(name_gen, wrapped);\n                } else {\n                    for elem in sum_type.variants.iter_mut() {\n                        self.add_expand_type_for_tests(name_gen, &mut elem.algebraic_type);\n                    }\n                }\n            }\n            AlgebraicType::Array(ty) => {\n                self.add_expand_type_for_tests(name_gen, &mut ty.elem_ty);\n                return;\n            }\n            _ => return,\n        }\n\n        // Make the type into a ref.\n        let name = *name_gen;\n        let add_ty = core::mem::replace(ty, AlgebraicType::U8);\n        *ty = AlgebraicType::Ref(self.add_algebraic_type([], RawIdentifier::new(format!(\"gen_{name}\")), add_ty, true));\n        *name_gen += 1;\n    }\n\n    fn add_expand_product_type_for_tests(&mut self, name_gen: &mut usize, ty: &mut ProductType) {\n        for elem in ty.elements.iter_mut() {\n            self.add_expand_type_for_tests(name_gen, &mut elem.algebraic_type);\n        }\n    }\n\n    /// Add a type to the typespace, along with a type alias declaring its name.\n    /// This method should only be used for `AlgebraicType`s not corresponding to a Rust\n    /// type that implements `SpacetimeType`.\n    ///\n    /// Returns a reference to the newly-added type.\n    ///\n    /// NOT idempotent, calling this twice with the same name will cause errors during validation.\n    ///\n    /// You must set `custom_ordering` if you're not using the default element ordering.\n    pub fn add_algebraic_type(\n        &mut self,\n        scope: impl IntoIterator<Item = RawIdentifier>,\n        source_name: impl Into<RawIdentifier>,\n        ty: AlgebraicType,\n        custom_ordering: bool,\n    ) -> AlgebraicTypeRef {\n        let ty_ref = self.typespace_mut().add(ty);\n        let scope = scope.into_iter().collect();\n        let source_name = source_name.into();\n        self.types_mut().push(RawTypeDefV10 {\n            source_name: RawScopedTypeNameV10 { source_name, scope },\n            ty: ty_ref,\n            custom_ordering,\n        });\n        // We don't add a `TypeId` to `self.type_map`, because there may not be a corresponding Rust type!\n        // e.g. if we are randomly generating types in proptests.\n        ty_ref\n    }\n\n    /// Add a reducer to the in-progress module.\n    /// Accepts a `ProductType` of reducer arguments for convenience.\n    /// The `ProductType` need not be registered in the typespace.\n    ///\n    /// Importantly, if the reducer's first argument is a `ReducerContext`, that\n    /// information should not be provided to this method.\n    /// That is an implementation detail handled by the module bindings and can be ignored.\n    /// As far as the module definition is concerned, the reducer's arguments\n    /// start with the first non-`ReducerContext` argument.\n    ///\n    /// (It is impossible, with the current implementation of `ReducerContext`, to\n    /// have more than one `ReducerContext` argument, at least in Rust.\n    /// This is because `SpacetimeType` is not implemented for `ReducerContext`,\n    /// so it can never act like an ordinary argument.)\n    pub fn add_reducer(&mut self, source_name: impl Into<RawIdentifier>, params: ProductType) {\n        self.reducers_mut().push(RawReducerDefV10 {\n            source_name: source_name.into(),\n            params,\n            visibility: FunctionVisibility::ClientCallable,\n            ok_return_type: reducer_default_ok_return_type(),\n            err_return_type: reducer_default_err_return_type(),\n        });\n    }\n\n    /// Add a procedure to the in-progress module.\n    ///\n    /// Accepts a `ProductType` of arguments.\n    /// The arguments `ProductType` need not be registered in the typespace.\n    ///\n    /// Also accepts an `AlgebraicType` return type.\n    /// If this is a user-defined product or sum type,\n    /// it should be registered in the typespace and indirected through an `AlgebraicType::Ref`.\n    ///\n    /// The `&mut ProcedureContext` first argument to the procedure should not be included in the `params`.\n    pub fn add_procedure(\n        &mut self,\n        source_name: impl Into<RawIdentifier>,\n        params: ProductType,\n        return_type: AlgebraicType,\n    ) {\n        self.procedures_mut().push(RawProcedureDefV10 {\n            source_name: source_name.into(),\n            params,\n            return_type,\n            visibility: FunctionVisibility::ClientCallable,\n        })\n    }\n\n    /// Add a view to the in-progress module.\n    pub fn add_view(\n        &mut self,\n        source_name: impl Into<RawIdentifier>,\n        index: usize,\n        is_public: bool,\n        is_anonymous: bool,\n        params: ProductType,\n        return_type: AlgebraicType,\n    ) {\n        self.views_mut().push(RawViewDefV10 {\n            source_name: source_name.into(),\n            index: index as u32,\n            is_public,\n            is_anonymous,\n            params,\n            return_type,\n        });\n    }\n\n    /// Add a lifecycle reducer assignment to the module.\n    ///\n    /// The function must be a previously-added reducer.\n    pub fn add_lifecycle_reducer(\n        &mut self,\n        lifecycle_spec: Lifecycle,\n        function_name: impl Into<RawIdentifier>,\n        params: ProductType,\n    ) {\n        let function_name = function_name.into();\n        self.lifecycle_reducers_mut().push(RawLifeCycleReducerDefV10 {\n            lifecycle_spec,\n            function_name: function_name.clone(),\n        });\n\n        self.reducers_mut().push(RawReducerDefV10 {\n            source_name: function_name,\n            params,\n            visibility: FunctionVisibility::Private,\n            ok_return_type: reducer_default_ok_return_type(),\n            err_return_type: reducer_default_err_return_type(),\n        });\n    }\n\n    /// Add a schedule definition to the module.\n    ///\n    /// The `function_name` should name a reducer or procedure\n    /// which accepts one argument, a row of the specified table.\n    ///\n    /// The table must have the appropriate columns for a scheduled table.\n    pub fn add_schedule(\n        &mut self,\n        table: impl Into<RawIdentifier>,\n        column: impl Into<ColId>,\n        function: impl Into<RawIdentifier>,\n    ) {\n        self.schedules_mut().push(RawScheduleDefV10 {\n            source_name: None,\n            table_name: table.into(),\n            schedule_at_col: column.into(),\n            function_name: function.into(),\n        });\n    }\n\n    /// Add a row-level security policy to the module.\n    ///\n    /// The `sql` expression should be a valid SQL expression that will be used to filter rows.\n    ///\n    /// **NOTE**: The `sql` expression must be unique within the module.\n    pub fn add_row_level_security(&mut self, sql: &str) {\n        self.row_level_security_mut()\n            .push(RawRowLevelSecurityDefV10 { sql: sql.into() });\n    }\n\n    pub fn add_explicit_names(&mut self, names: ExplicitNames) {\n        self.explicit_names_mut().merge(names);\n    }\n\n    /// Set the case conversion policy for this module.\n    ///\n    /// By default, SpacetimeDB applies `SnakeCase` conversion to table names,\n    /// column names, reducer names, etc. Use `CaseConversionPolicy::None` to\n    /// disable all case conversion (useful for modules with existing data that\n    /// was stored under the original naming convention).\n    pub fn set_case_conversion_policy(&mut self, policy: CaseConversionPolicy) {\n        // Remove any existing policy section.\n        self.module\n            .sections\n            .retain(|s| !matches!(s, RawModuleDefV10Section::CaseConversionPolicy(_)));\n        self.module\n            .sections\n            .push(RawModuleDefV10Section::CaseConversionPolicy(policy));\n    }\n\n    /// Finish building, consuming the builder and returning the module.\n    /// The module should be validated before use.\n    pub fn finish(self) -> RawModuleDefV10 {\n        self.module\n    }\n}\n\n/// Implement TypespaceBuilder for V10\nimpl TypespaceBuilder for RawModuleDefV10Builder {\n    fn add(\n        &mut self,\n        typeid: TypeId,\n        source_name: Option<&'static str>,\n        make_ty: impl FnOnce(&mut Self) -> AlgebraicType,\n    ) -> AlgebraicType {\n        if let btree_map::Entry::Occupied(o) = self.type_map.entry(typeid) {\n            AlgebraicType::Ref(*o.get())\n        } else {\n            let slot_ref = {\n                let ts = self.typespace_mut();\n                // Bind a fresh alias to the unit type.\n                let slot_ref = ts.add(AlgebraicType::unit());\n                // Relate `typeid -> fresh alias`.\n                self.type_map.insert(typeid, slot_ref);\n\n                // Alias provided? Relate `name -> slot_ref`.\n                if let Some(sats_name) = source_name {\n                    let source_name = sats_name_to_scoped_name_v10(sats_name);\n\n                    self.types_mut().push(RawTypeDefV10 {\n                        source_name,\n                        ty: slot_ref,\n                        // TODO(1.0): we need to update the `TypespaceBuilder` trait to include\n                        // a `custom_ordering` parameter.\n                        // For now, we assume all types have custom orderings, since the derive\n                        // macro doesn't know about the default ordering yet.\n                        custom_ordering: true,\n                    });\n                }\n                slot_ref\n            };\n\n            // Borrow of `v` has ended here, so we can now convince the borrow checker.\n            let ty = make_ty(self);\n            self.typespace_mut()[slot_ref] = ty;\n            AlgebraicType::Ref(slot_ref)\n        }\n    }\n}\n\npub fn reducer_default_ok_return_type() -> AlgebraicType {\n    AlgebraicType::unit()\n}\n\npub fn reducer_default_err_return_type() -> AlgebraicType {\n    AlgebraicType::String\n}\n\n/// Convert a string from a sats type-name annotation like `#[sats(name = \"namespace.name\")]` to a `RawScopedTypeNameV9`.\n/// We split the input on the strings `\"::\"` and `\".\"` to split up module paths.\n///\npub fn sats_name_to_scoped_name_v10(sats_name: &str) -> RawScopedTypeNameV10 {\n    // We can't use `&[char]: Pattern` for `split` here because \"::\" is not a char :/\n    let mut scope: Vec<RawIdentifier> = sats_name\n        .split(\"::\")\n        .flat_map(|s| s.split('.'))\n        .map(RawIdentifier::new)\n        .collect();\n    // Unwrapping to \"\" will result in a validation error down the line, which is exactly what we want.\n    let source_name = scope.pop().unwrap_or_default();\n    RawScopedTypeNameV10 {\n        scope: scope.into(),\n        source_name,\n    }\n}\n\n/// Builder for a `RawTableDefV10`.\npub struct RawTableDefBuilderV10<'a> {\n    module: &'a mut RawModuleDefV10,\n    table: RawTableDefV10,\n}\n\nimpl RawTableDefBuilderV10<'_> {\n    /// Set the table type.\n    ///\n    /// This is not about column algebraic types, but about whether the table\n    /// was created by the system or the user.\n    pub fn with_type(mut self, table_type: TableType) -> Self {\n        self.table.table_type = table_type;\n        self\n    }\n\n    /// Sets the access rights for the table and return it.\n    pub fn with_access(mut self, table_access: TableAccess) -> Self {\n        self.table.table_access = table_access;\n        self\n    }\n\n    /// Sets whether this table is an event table.\n    pub fn with_event(mut self, is_event: bool) -> Self {\n        self.table.is_event = is_event;\n        self\n    }\n\n    /// Generates a `RawConstraintDefV10` using the supplied `columns`.\n    pub fn with_unique_constraint(mut self, columns: impl Into<ColList>) -> Self {\n        let columns = columns.into();\n        self.table.constraints.push(RawConstraintDefV10 {\n            source_name: None,\n            data: RawConstraintDataV10::Unique(RawUniqueConstraintDataV10 { columns }),\n        });\n\n        self\n    }\n\n    /// Adds a primary key to the table.\n    /// You must also add a unique constraint on the primary key column.\n    pub fn with_primary_key(mut self, column: impl Into<ColId>) -> Self {\n        self.table.primary_key = ColList::new(column.into());\n        self\n    }\n\n    /// Adds a primary key to the table, with corresponding unique constraint and sequence definitions.\n    /// You will also need to call [`Self::with_index`] to create an index on `column`.\n    pub fn with_auto_inc_primary_key(self, column: impl Into<ColId>) -> Self {\n        let column = column.into();\n        self.with_primary_key(column)\n            .with_unique_constraint(column)\n            .with_column_sequence(column)\n    }\n\n    /// Generates a [RawIndexDefV10] using the supplied `columns`.\n    pub fn with_index(\n        mut self,\n        algorithm: RawIndexAlgorithm,\n        source_name: impl Into<RawIdentifier>,\n        accessor_name: impl Into<RawIdentifier>,\n    ) -> Self {\n        self.table.indexes.push(RawIndexDefV10 {\n            source_name: Some(source_name.into()),\n            accessor_name: Some(accessor_name.into()),\n            algorithm,\n        });\n        self\n    }\n\n    /// Generates a [RawIndexDefV10] using the supplied `columns`.\n    pub fn with_index_no_accessor_name(\n        mut self,\n        algorithm: RawIndexAlgorithm,\n        source_name: impl Into<RawIdentifier>,\n    ) -> Self {\n        self.table.indexes.push(RawIndexDefV10 {\n            source_name: Some(source_name.into()),\n            accessor_name: None,\n            algorithm,\n        });\n        self\n    }\n\n    /// Adds a [RawSequenceDefV10] on the supplied `column`.\n    pub fn with_column_sequence(mut self, column: impl Into<ColId>) -> Self {\n        let column = column.into();\n        self.table.sequences.push(RawSequenceDefV10 {\n            source_name: None,\n            column,\n            start: None,\n            min_value: None,\n            max_value: None,\n            increment: 1,\n        });\n\n        self\n    }\n\n    /// Adds a default value for a column.\n    pub fn with_default_column_value(mut self, column: impl Into<ColId>, value: AlgebraicValue) -> Self {\n        let col_id = column.into();\n        self.table.default_values.push(RawColumnDefaultValueV10 {\n            col_id,\n            value: spacetimedb_sats::bsatn::to_vec(&value).unwrap().into(),\n        });\n\n        self\n    }\n\n    /// Build the table and add it to the module, returning the `product_type_ref` of the table.\n    pub fn finish(self) -> AlgebraicTypeRef {\n        let product_type_ref = self.table.product_type_ref;\n\n        let tables = match self\n            .module\n            .sections\n            .iter_mut()\n            .find(|s| matches!(s, RawModuleDefV10Section::Tables(_)))\n        {\n            Some(RawModuleDefV10Section::Tables(t)) => t,\n            _ => {\n                self.module.sections.push(RawModuleDefV10Section::Tables(Vec::new()));\n                match self.module.sections.last_mut().expect(\"Just pushed Tables section\") {\n                    RawModuleDefV10Section::Tables(t) => t,\n                    _ => unreachable!(),\n                }\n            }\n        };\n\n        tables.push(self.table);\n        product_type_ref\n    }\n\n    /// Find a column position by its name in the table's product type.\n    pub fn find_col_pos_by_name(&self, column: impl AsRef<str>) -> Option<ColId> {\n        let column = column.as_ref();\n\n        let typespace = self.module.sections.iter().find_map(|s| {\n            if let RawModuleDefV10Section::Typespace(ts) = s {\n                Some(ts)\n            } else {\n                None\n            }\n        })?;\n\n        typespace\n            .get(self.table.product_type_ref)?\n            .as_product()?\n            .elements\n            .iter()\n            .position(|x| x.has_name(column.as_ref()))\n            .map(|i| ColId(i as u16))\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/db/raw_def/v8.rs",
    "content": "//! Database definitions v8, the last version before they were wrapped in an enum.\n//!\n//! Nothing to do with Chrome.\n\nuse crate::db::auth::{StAccess, StTableType};\nuse crate::{AlgebraicType, ProductType, SpacetimeType};\nuse derive_more::Display;\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\n\n// TODO(1.0): move these definitions into this file,\n// along with the other structs contained in it,\n// which are currently in the crate root.\npub use crate::ModuleDefBuilder as RawModuleDefV8Builder;\npub use crate::RawModuleDefV8;\n\n/// The amount sequences allocate each time they over-run their allocation.\n///\n/// Note that we do not perform an initial allocation during `create_sequence` or at startup.\n/// Newly-created sequences will allocate the first time they are advanced.\npub const SEQUENCE_ALLOCATION_STEP: i128 = 4096;\n\n/// Represents a sequence definition for a database table column.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct RawSequenceDefV8 {\n    /// The name of the sequence.\n    pub sequence_name: RawIdentifier,\n    /// The position of the column associated with this sequence.\n    pub col_pos: ColId,\n    /// The increment value for the sequence.\n    pub increment: i128,\n    /// The starting value for the sequence.\n    pub start: Option<i128>,\n    /// The minimum value for the sequence.\n    pub min_value: Option<i128>,\n    /// The maximum value for the sequence.\n    pub max_value: Option<i128>,\n    /// The number of values to preallocate for the sequence.\n    /// Deprecated, in the future this concept will no longer exist.\n    pub allocated: i128,\n}\n\nimpl RawSequenceDefV8 {\n    /// Creates a new [RawSequenceDefV8] instance for a specific table and column.\n    ///\n    /// # Parameters\n    ///\n    /// * `table` - The name of the table.\n    /// * `seq_name` - The name of the sequence.\n    /// * `col_pos` - The position of the column in the `table`.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use spacetimedb_lib::db::raw_def::*;\n    ///\n    /// let sequence_def = RawSequenceDefV8::for_column(\"my_table\", \"my_sequence\", 1.into());\n    /// assert_eq!(&*sequence_def.sequence_name, \"seq_my_table_my_sequence\");\n    /// ```\n    pub fn for_column(table: &str, column_or_name: &str, col_pos: ColId) -> Self {\n        //removes the auto-generated suffix...\n        let seq_name = column_or_name.trim_start_matches(&format!(\"ct_{table}_\"));\n\n        RawSequenceDefV8 {\n            sequence_name: RawIdentifier::new(format!(\"seq_{table}_{seq_name}\")),\n            col_pos,\n            increment: 1,\n            start: None,\n            min_value: None,\n            max_value: None,\n            // Start with no values allocated. The first time we advance the sequence,\n            // we will allocate [`SEQUENCE_ALLOCATION_STEP`] values.\n            allocated: 0,\n        }\n    }\n}\n\n/// Which type of index to create.\n///\n/// Currently only `IndexType::BTree` is allowed.\n#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Display, SpacetimeType)]\n#[sats(crate = crate)]\npub enum IndexType {\n    /// A BTree index.\n    BTree = 0,\n    /// A Hash index.\n    Hash = 1,\n}\n\nimpl From<IndexType> for u8 {\n    fn from(value: IndexType) -> Self {\n        value as u8\n    }\n}\n\nimpl TryFrom<u8> for IndexType {\n    type Error = ();\n    fn try_from(v: u8) -> Result<Self, Self::Error> {\n        match v {\n            0 => Ok(IndexType::BTree),\n            1 => Ok(IndexType::Hash),\n            _ => Err(()),\n        }\n    }\n}\n\n/// A struct representing the definition of a database index.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct RawIndexDefV8 {\n    /// The name of the index.\n    /// This should not be assumed to follow any particular format.\n    pub index_name: RawIdentifier,\n    /// Whether the index is unique.\n    pub is_unique: bool,\n    /// The type of the index.\n    pub index_type: IndexType,\n    /// List of column positions that compose the index.\n    pub columns: ColList,\n}\n\nimpl RawIndexDefV8 {\n    /// Creates a new [RawIndexDefV8] with the provided parameters.\n    ///\n    /// # Parameters\n    ///\n    /// * `index_name`: The name of the index.\n    /// * `columns`: List of column positions that compose the index.\n    /// * `is_unique`: Indicates whether the index enforces uniqueness.\n    pub fn btree(index_name: RawIdentifier, columns: impl Into<ColList>, is_unique: bool) -> Self {\n        Self {\n            columns: columns.into(),\n            index_name,\n            is_unique,\n            index_type: IndexType::BTree,\n        }\n    }\n\n    /// Creates an [RawIndexDefV8] for a specific column of a table.\n    ///\n    /// This method generates an index name based on the table name, index name, column positions, and uniqueness constraint.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use spacetimedb_primitives::ColList;\n    /// use spacetimedb_lib::db::raw_def::*;\n    ///\n    /// let index_def = RawIndexDefV8::for_column(\"my_table\", \"test\", 1, true);\n    /// assert_eq!(&*index_def.index_name, \"idx_my_table_test_unique\");\n    /// ```\n    pub fn for_column(table: &str, index_or_name: &str, columns: impl Into<ColList>, is_unique: bool) -> Self {\n        let unique = if is_unique { \"unique\" } else { \"non_unique\" };\n\n        // Removes the auto-generated suffix from the index name.\n        let name = index_or_name.trim_start_matches(&format!(\"ct_{table}_\"));\n\n        // Constructs the index name using a predefined format.\n        // No duplicate the `kind_name` that was added by an constraint\n        let name = if name.ends_with(&unique) {\n            format!(\"idx_{table}_{name}\")\n        } else {\n            format!(\"idx_{table}_{name}_{unique}\")\n        };\n        Self::btree(RawIdentifier::new(name), columns, is_unique)\n    }\n}\n\n/// A struct representing the definition of a database column.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct RawColumnDefV8 {\n    /// The name of the column.\n    pub col_name: RawIdentifier,\n    /// The type of the column.\n    ///\n    /// Must satisfy [AlgebraicType::is_valid_for_client_type_use].\n    pub col_type: AlgebraicType,\n}\n\nimpl RawColumnDefV8 {\n    /// Convert a product type to a list of column definitions.\n    pub fn from_product_type(value: ProductType) -> Vec<RawColumnDefV8> {\n        Vec::from(value.elements)\n            .into_iter()\n            .enumerate()\n            .map(|(pos, col)| {\n                let col_name = if let Some(name) = col.name {\n                    name\n                } else {\n                    RawIdentifier::new(format!(\"col_{pos}\"))\n                };\n\n                RawColumnDefV8 {\n                    col_name,\n                    col_type: col.algebraic_type,\n                }\n            })\n            .collect()\n    }\n}\n\nimpl RawColumnDefV8 {\n    /// Creates a new [RawColumnDefV8] for a system field with the specified data type.\n    ///\n    /// This method is typically used to define system columns with predefined names and data types.\n    ///\n    /// # Parameters\n    ///\n    /// * `field_name`: The name for which to create a column definition.\n    /// * `col_type`: The [AlgebraicType] of the column.\n    ///\n    /// If `type_` is not `AlgebraicType::Builtin` or `AlgebraicType::Ref`, an error will result at validation time.\n    pub fn sys(field_name: &str, col_type: AlgebraicType) -> Self {\n        Self {\n            col_name: RawIdentifier::new(field_name),\n            col_type,\n        }\n    }\n}\n\n/// A struct representing the definition of a database constraint.\n/// Associated with a unique `TableDef`, the one that contains it.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct RawConstraintDefV8 {\n    /// The name of the constraint.\n    pub constraint_name: RawIdentifier,\n    /// The constraints applied to the columns.\n    pub constraints: Constraints,\n    /// List of column positions associated with the constraint.\n    pub columns: ColList,\n}\n\nimpl RawConstraintDefV8 {\n    /// Creates a new [RawConstraintDefV8] with the specified parameters.\n    ///\n    /// # Parameters\n    ///\n    /// * `constraint_name`: The name of the constraint.\n    /// * `constraints`: The constraints.\n    /// * `columns`: List of column positions associated with the constraint.\n    pub fn new(constraint_name: RawIdentifier, constraints: Constraints, columns: impl Into<ColList>) -> Self {\n        Self {\n            constraint_name,\n            constraints,\n            columns: columns.into(),\n        }\n    }\n\n    /// Creates a `ConstraintDef` for a specific column of a table.\n    ///\n    /// This method generates a constraint name based on the table name, column name, and constraint type.\n    ///\n    /// # Parameters\n    ///\n    /// * `table`: The name of the table to which the constraint belongs.\n    /// * `column_name`: The name of the column associated with the constraint.\n    /// * `constraints`: The constraints.\n    /// * `columns`: List of column positions associated with the constraint.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use spacetimedb_primitives::{Constraints, ColList};\n    /// use spacetimedb_lib::db::raw_def::*;\n    ///\n    /// let constraint_def = RawConstraintDefV8::for_column(\"my_table\", \"test\", Constraints::identity(), 1);\n    /// assert_eq!(&*constraint_def.constraint_name, \"ct_my_table_test_identity\");\n    /// ```\n    pub fn for_column(\n        table: &str,\n        column_or_name: &str,\n        constraints: Constraints,\n        columns: impl Into<ColList>,\n    ) -> Self {\n        //removes the auto-generated suffix...\n        let name = column_or_name.trim_start_matches(&format!(\"idx_{table}_\"));\n\n        let kind_name = format!(\"{:?}\", constraints.kind()).to_lowercase();\n        // No duplicate the `kind_name` that was added by an index\n        let name = if name.ends_with(&kind_name) {\n            format!(\"ct_{table}_{name}\")\n        } else {\n            format!(\"ct_{table}_{name}_{kind_name}\")\n        };\n        Self::new(RawIdentifier::new(name), constraints, columns)\n    }\n}\n\n/// Concatenate the column names from the `columns`\n///\n/// WARNING: If the `ColId` not exist, is skipped.\n/// TODO(Tyler): This should return an error and not allow this to be constructed\n/// if there is an invalid `ColId`\npub fn generate_cols_name<'a>(columns: &ColList, col_name: impl Fn(ColId) -> Option<&'a str>) -> String {\n    let mut column_name = Vec::with_capacity(columns.len() as usize);\n    column_name.extend(columns.iter().filter_map(col_name));\n    column_name.join(\"_\")\n}\n\n/// A data structure representing the definition of a database table.\n///\n/// This struct holds information about the table, including its name, columns, indexes,\n/// constraints, sequences, type, and access rights.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct RawTableDefV8 {\n    /// The name of the table.\n    pub table_name: RawIdentifier,\n    /// The columns of the table.\n    /// The ordering of the columns is significant. Columns are frequently identified by `ColId`, that is, position in this list.\n    pub columns: Vec<RawColumnDefV8>,\n    /// The indexes on the table.\n    pub indexes: Vec<RawIndexDefV8>,\n    /// The constraints on the table.\n    pub constraints: Vec<RawConstraintDefV8>,\n    /// The sequences attached to the table.\n    pub sequences: Vec<RawSequenceDefV8>,\n    /// Whether the table was created by a user or by the system.\n    pub table_type: StTableType,\n    /// The visibility of the table.\n    pub table_access: StAccess,\n    /// If this is a schedule table, the reducer it is scheduled for.\n    pub scheduled: Option<RawIdentifier>,\n}\n\nimpl RawTableDefV8 {\n    /// Create a new `TableDef` instance with the specified `table_name` and `columns`.\n    ///\n    /// # Parameters\n    ///\n    /// - `table_name`: The name of the table.\n    /// - `columns`: A `vec` of `ColumnDef` instances representing the columns of the table.\n    ///\n    pub fn new(table_name: RawIdentifier, columns: Vec<RawColumnDefV8>) -> Self {\n        Self {\n            table_name,\n            columns,\n            indexes: vec![],\n            constraints: vec![],\n            sequences: vec![],\n            table_type: StTableType::User,\n            table_access: StAccess::Public,\n            scheduled: None,\n        }\n    }\n\n    #[cfg(feature = \"test\")]\n    pub fn new_for_tests(table_name: impl Into<RawIdentifier>, columns: ProductType) -> Self {\n        Self::new(table_name.into(), RawColumnDefV8::from_product_type(columns))\n    }\n\n    /// Set the type of the table and return a new `TableDef` instance with the updated type.\n    pub fn with_type(self, table_type: StTableType) -> Self {\n        let mut x = self;\n        x.table_type = table_type;\n        x\n    }\n\n    /// Set the access rights for the table and return a new `TableDef` instance with the updated access rights.\n    pub fn with_access(self, table_access: StAccess) -> Self {\n        let mut x = self;\n        x.table_access = table_access;\n        x\n    }\n\n    /// Set the constraints for the table and return a new `TableDef` instance with the updated constraints.\n    pub fn with_constraints(self, constraints: Vec<RawConstraintDefV8>) -> Self {\n        let mut x = self;\n        x.constraints = constraints;\n        x\n    }\n\n    /// Concatenate the column names from the `columns`\n    ///\n    /// WARNING: If the `ColId` not exist, is skipped.\n    /// TODO(Tyler): This should return an error and not allow this to be constructed\n    /// if there is an invalid `ColId`\n    fn generate_cols_name(&self, columns: &ColList) -> String {\n        generate_cols_name(columns, |p| self.get_column(p.idx()).map(|c| &*c.col_name))\n    }\n\n    /// Generate a [RawConstraintDefV8] using the supplied `columns`.\n    pub fn with_column_constraint(mut self, kind: Constraints, columns: impl Into<ColList>) -> Self {\n        self.constraints.push(self.gen_constraint_def(kind, columns));\n        self\n    }\n\n    pub fn gen_constraint_def(&self, kind: Constraints, columns: impl Into<ColList>) -> RawConstraintDefV8 {\n        let columns = columns.into();\n        RawConstraintDefV8::for_column(&self.table_name, &self.generate_cols_name(&columns), kind, columns)\n    }\n\n    /// Set the indexes for the table and return a new `TableDef` instance with the updated indexes.\n    pub fn with_indexes(self, indexes: Vec<RawIndexDefV8>) -> Self {\n        let mut x = self;\n        x.indexes = indexes;\n        x\n    }\n\n    /// Generate a [RawIndexDefV8] using the supplied `columns`.\n    pub fn with_column_index(self, columns: impl Into<ColList>, is_unique: bool) -> Self {\n        let mut x = self;\n        let columns = columns.into();\n        x.indexes.push(RawIndexDefV8::for_column(\n            &x.table_name,\n            &x.generate_cols_name(&columns),\n            columns,\n            is_unique,\n        ));\n        x\n    }\n\n    /// Set the sequences for the table and return a new `TableDef` instance with the updated sequences.\n    pub fn with_sequences(self, sequences: Vec<RawSequenceDefV8>) -> Self {\n        let mut x = self;\n        x.sequences = sequences;\n        x\n    }\n\n    /// Generate a [RawSequenceDefV8] using the supplied `columns`.\n    pub fn with_column_sequence(self, columns: ColId) -> Self {\n        let mut x = self;\n\n        x.sequences.push(RawSequenceDefV8::for_column(\n            &x.table_name,\n            &x.generate_cols_name(&ColList::new(columns)),\n            columns,\n        ));\n        x\n    }\n\n    /// Set the reducer name for scheduled tables and return updated `TableDef`.\n    pub fn with_scheduled(mut self, scheduled: Option<RawIdentifier>) -> Self {\n        self.scheduled = scheduled;\n        self\n    }\n\n    /// Create a `TableDef` from a product type and table name.\n    ///\n    /// NOTE: If the [ProductType.name] is `None` then it auto-generate a name like `col_{col_pos}`\n    pub fn from_product(table_name: RawIdentifier, row: ProductType) -> Self {\n        Self::new(\n            table_name,\n            Vec::from(row.elements)\n                .into_iter()\n                .enumerate()\n                .map(|(col_pos, e)| RawColumnDefV8 {\n                    col_name: e.name.unwrap_or_else(|| RawIdentifier::new(format!(\"col_{col_pos}\"))),\n                    col_type: e.algebraic_type,\n                })\n                .collect::<Vec<_>>(),\n        )\n    }\n\n    /// Get a column by its position in the table.\n    pub fn get_column(&self, pos: usize) -> Option<&RawColumnDefV8> {\n        self.columns.get(pos)\n    }\n\n    /// Check if the `col_name` exist on this [RawTableDefV8]\n    ///\n    /// Warning: It ignores the `table_name`\n    pub fn get_column_by_name(&self, col_name: &str) -> Option<&RawColumnDefV8> {\n        self.columns.iter().find(|x| &*x.col_name == col_name)\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/db/raw_def/v9.rs",
    "content": "//! ABI Version 9 of the raw module definitions.\n//!\n//! This is the ABI that will be used for 1.0.\n//! We are keeping around the old ABI (v8) for now, to allow ourselves to convert the codebase\n//! a component-at-a-time.\n\nuse std::any::TypeId;\nuse std::collections::btree_map;\nuse std::collections::BTreeMap;\nuse std::fmt;\n\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::typespace::TypespaceBuilder;\nuse spacetimedb_sats::AlgebraicType;\nuse spacetimedb_sats::AlgebraicTypeRef;\nuse spacetimedb_sats::AlgebraicValue;\nuse spacetimedb_sats::ProductType;\nuse spacetimedb_sats::ProductTypeElement;\nuse spacetimedb_sats::SpacetimeType;\nuse spacetimedb_sats::Typespace;\n\nuse crate::db::auth::StAccess;\nuse crate::db::auth::StTableType;\nuse crate::db::raw_def::v10::RawConstraintDefV10;\nuse crate::db::raw_def::v10::RawScopedTypeNameV10;\nuse crate::db::raw_def::v10::RawSequenceDefV10;\nuse crate::db::raw_def::v10::RawTypeDefV10;\nuse crate::db::view::extract_view_return_product_type_ref;\n\n/// A not-yet-validated `sql`.\npub type RawSql = Box<str>;\n\n/// A possibly-invalid raw module definition.\n///\n/// ABI Version 9.\n///\n/// These \"raw definitions\" may contain invalid data, and are validated by the `validate` module into a proper `spacetimedb_schema::ModuleDef`, or a collection of errors.\n///\n/// The module definition has a single logical global namespace, which maps `Identifier`s to:\n///\n/// - database-level objects:\n///     - logical schema objects:\n///         - tables\n///         - constraints\n///         - sequence definitions\n///     - physical schema objects:\n///         - indexes\n/// - module-level objects:\n///     - reducers\n///     - schedule definitions\n/// - binding-level objects:\n///     - type aliases\n///\n/// All of these types of objects must have unique names within the module.\n/// The exception is columns, which need unique names only within a table.\n#[derive(Debug, Clone, Default, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawModuleDefV9 {\n    /// The `Typespace` used by the module.\n    ///\n    /// `AlgebraicTypeRef`s in the table, reducer, and type alias declarations refer to this typespace.\n    ///\n    /// The typespace must satisfy `Typespace::is_valid_for_client_code_generation`. That is, all types stored in the typespace must either:\n    /// 1. satisfy `AlgebraicType::is_valid_for_client_type_definition`\n    /// 2. and/or `AlgebraicType::is_valid_for_client_type_use`.\n    ///\n    /// Types satisfying condition 1 correspond to generated classes in client code.\n    /// (Types satisfying condition 2 are an artifact of the module bindings, and do not affect the semantics of the module definition.)\n    ///\n    /// Types satisfying condition 1 are required to have corresponding `RawTypeDefV9` declarations in the module.\n    pub typespace: Typespace,\n\n    /// The tables of the database definition used in the module.\n    ///\n    /// Each table must have a unique name.\n    pub tables: Vec<RawTableDefV9>,\n\n    /// The reducers exported by the module.\n    pub reducers: Vec<RawReducerDefV9>,\n\n    /// The types exported by the module.\n    pub types: Vec<RawTypeDefV9>,\n\n    /// Miscellaneous additional module exports.\n    ///\n    /// The enum [`RawMiscModuleExportV9`] can have new variants added\n    /// without breaking existing compiled modules.\n    /// As such, this acts as a sort of dumping ground for any exports added after we defined `RawModuleDefV9`.\n    ///\n    /// If/when we define `RawModuleDefV10`, these should be moved out of `misc_exports` and into their own fields,\n    /// and the new `misc_exports` should once again be initially empty.\n    pub misc_exports: Vec<RawMiscModuleExportV9>,\n\n    /// Row level security definitions.\n    ///\n    /// Each definition must have a unique name.\n    pub row_level_security: Vec<RawRowLevelSecurityDefV9>,\n}\n\nimpl RawModuleDefV9 {\n    /// Find a [`RawTableDefV9`] by name in this raw module def\n    fn find_table_def(&self, table_name: &str) -> Option<&RawTableDefV9> {\n        self.tables\n            .iter()\n            .find(|table_def| table_def.name.as_ref() == table_name)\n    }\n\n    /// Find a [`RawViewDefV9`] by name in this raw module def\n    fn find_view_def(&self, view_name: &str) -> Option<&RawViewDefV9> {\n        self.misc_exports.iter().find_map(|misc_export| match misc_export {\n            RawMiscModuleExportV9::View(view_def) if view_def.name.as_ref() == view_name => Some(view_def),\n            _ => None,\n        })\n    }\n\n    /// Find and return the product type ref for a table in this module def\n    fn type_ref_for_table(&self, table_name: &str) -> Option<AlgebraicTypeRef> {\n        self.find_table_def(table_name)\n            .map(|table_def| table_def.product_type_ref)\n    }\n\n    /// Find and return the product type ref for a view in this module def\n    fn type_ref_for_view(&self, view_name: &str) -> Option<AlgebraicTypeRef> {\n        self.find_view_def(view_name)\n            .map(|view_def| &view_def.return_type)\n            .and_then(|return_type| extract_view_return_product_type_ref(return_type).map(|(ref_, _)| ref_))\n    }\n\n    /// Find and return the product type ref for a table or view in this module def\n    pub fn type_ref_for_table_like(&self, name: &str) -> Option<AlgebraicTypeRef> {\n        self.type_ref_for_table(name).or_else(|| self.type_ref_for_view(name))\n    }\n}\n\n/// The definition of a database table.\n///\n/// This struct holds information about the table, including its name, columns, indexes,\n/// constraints, sequences, type, and access rights.\n///\n/// Validation rules:\n/// - The table name must be a valid `spacetimedb_schema::identifier::Identifier`.\n/// - The table's indexes, constraints, and sequences need not be sorted; they will be sorted according to their respective ordering rules.\n/// - The table's column types may refer only to types in the containing `RawModuleDefV9`'s typespace.\n/// - The table's column names must be unique.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawTableDefV9 {\n    /// The name of the table.\n    /// Unique within a module, acts as the table's identifier.\n    /// Must be a valid `spacetimedb_schema::identifier::Identifier`.\n    pub name: RawIdentifier,\n\n    /// A reference to a `ProductType` containing the columns of this table.\n    /// This is the single source of truth for the table's columns.\n    /// All elements of the `ProductType` must have names.\n    ///\n    /// Like all types in the module, this must have the [default element ordering](crate::db::default_element_ordering),\n    /// UNLESS a custom ordering is declared via a `RawTypeDefv9` for this type.\n    pub product_type_ref: AlgebraicTypeRef,\n\n    /// The primary key of the table, if present. Must refer to a valid column.\n    ///\n    /// Currently, there must be a unique constraint and an index corresponding to the primary key.\n    /// Eventually, we may remove the requirement for an index.\n    ///\n    /// The database engine does not actually care about this, but client code generation does.\n    ///\n    /// A list of length 0 means no primary key. Currently, a list of length >1 is not supported.\n    pub primary_key: ColList,\n\n    /// The indices of the table.\n    pub indexes: Vec<RawIndexDefV9>,\n\n    /// Any unique constraints on the table.\n    pub constraints: Vec<RawConstraintDefV9>,\n\n    /// The sequences for the table.\n    pub sequences: Vec<RawSequenceDefV9>,\n\n    /// The schedule for the table.\n    pub schedule: Option<RawScheduleDefV9>,\n\n    /// Whether this is a system- or user-created table.\n    pub table_type: TableType,\n\n    /// Whether this table is public or private.\n    pub table_access: TableAccess,\n}\n\n/// Whether the table was created by the system or the user.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub enum TableType {\n    /// Created by the system.\n    System,\n    /// Created by the user.\n    User,\n}\nimpl From<StTableType> for TableType {\n    fn from(t: StTableType) -> Self {\n        match t {\n            StTableType::System => TableType::System,\n            StTableType::User => TableType::User,\n        }\n    }\n}\nimpl From<TableType> for StTableType {\n    fn from(t: TableType) -> Self {\n        match t {\n            TableType::System => StTableType::System,\n            TableType::User => StTableType::User,\n        }\n    }\n}\n\n/// The visibility of the table.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub enum TableAccess {\n    /// Visible to all\n    Public,\n    /// Visible only to the owner\n    Private,\n}\nimpl From<StAccess> for TableAccess {\n    fn from(t: StAccess) -> Self {\n        match t {\n            StAccess::Public => TableAccess::Public,\n            StAccess::Private => TableAccess::Private,\n        }\n    }\n}\nimpl From<TableAccess> for StAccess {\n    fn from(t: TableAccess) -> Self {\n        match t {\n            TableAccess::Public => StAccess::Public,\n            TableAccess::Private => StAccess::Private,\n        }\n    }\n}\n\n/// A sequence definition for a database table column.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawSequenceDefV9 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    /// If `None`, a nicely-formatted unique default will be chosen.\n    pub name: Option<RawIdentifier>,\n\n    /// The position of the column associated with this sequence.\n    /// This refers to a column in the same `RawTableDef` that contains this `RawSequenceDef`.\n    /// The column must have integral type.\n    /// This must be the unique `RawSequenceDef` for this column.\n    pub column: ColId,\n\n    /// The value to start assigning to this column.\n    /// Will be incremented by 1 for each new row.\n    /// If not present, an arbitrary start point may be selected.\n    pub start: Option<i128>,\n\n    /// The minimum allowed value in this column.\n    /// If not present, no minimum.\n    pub min_value: Option<i128>,\n\n    /// The maximum allowed value in this column.\n    /// If not present, no maximum.\n    pub max_value: Option<i128>,\n\n    /// The increment used when updating the SequenceDef.\n    pub increment: i128,\n}\n\n/// The definition of a database index.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawIndexDefV9 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    pub name: Option<RawIdentifier>,\n\n    /// Accessor name for the index used in client codegen.\n    ///\n    /// This is set the user and should not be assumed to follow\n    /// any particular format.\n    ///\n    /// May be set to `None` if this is an auto-generated index for which the user\n    /// has not supplied a name. In this case, no client code generation for this index\n    /// will be performed.\n    ///\n    /// This name is not visible in the system tables, it is only used for client codegen.\n    pub accessor_name: Option<RawIdentifier>,\n\n    /// The algorithm parameters for the index.\n    pub algorithm: RawIndexAlgorithm,\n}\n\n/// Data specifying an index algorithm.\n/// New fields MUST be added to the END of this enum, to maintain ABI compatibility.\n#[non_exhaustive]\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub enum RawIndexAlgorithm {\n    /// Implemented using a B-Tree.\n    ///\n    /// Currently, this uses a rust `std::collections::BTreeMap`.\n    BTree {\n        /// The columns to index on. These are ordered.\n        columns: ColList,\n    },\n    /// Implemented using a hashmap.\n    Hash {\n        /// The columns to index on. These are ordered.\n        columns: ColList,\n    },\n    /// Implemented using direct indexing in list(s) of `RowPointer`s.\n    /// The column this is placed on must also have a unique constraint.\n    Direct {\n        /// The column to index on.\n        /// Only one is allowed, as direct indexing with more is nonsensical.\n        column: ColId,\n    },\n}\n\n/// Returns a btree index algorithm for the columns `cols`.\npub fn btree(cols: impl Into<ColList>) -> RawIndexAlgorithm {\n    RawIndexAlgorithm::BTree { columns: cols.into() }\n}\n\n/// Returns a hash index algorithm for the columns `cols`.\npub fn hash(cols: impl Into<ColList>) -> RawIndexAlgorithm {\n    RawIndexAlgorithm::Hash { columns: cols.into() }\n}\n\n/// Returns a direct index algorithm for the column `col`.\npub fn direct(col: impl Into<ColId>) -> RawIndexAlgorithm {\n    RawIndexAlgorithm::Direct { column: col.into() }\n}\n\n/// Marks a table as a timer table for a scheduled reducer or procedure.\n///\n/// The table must have columns:\n/// - `scheduled_id` of type `u64`.\n/// - `scheduled_at` of type `ScheduleAt`.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawScheduleDefV9 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    pub name: Option<RawIdentifier>,\n\n    /// The name of the reducer or procedure to call.\n    ///\n    /// Despite the field name here, this may be either a reducer or a procedure.\n    pub reducer_name: RawIdentifier,\n\n    /// The column of the `scheduled_at` field of this scheduled table.\n    pub scheduled_at_column: ColId,\n}\n\n/// A constraint definition attached to a table.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawConstraintDefV9 {\n    /// In the future, the user may FOR SOME REASON want to override this.\n    /// Even though there is ABSOLUTELY NO REASON TO.\n    pub name: Option<RawIdentifier>,\n\n    /// The data for the constraint.\n    pub data: RawConstraintDataV9,\n}\n\n/// Raw data attached to a constraint.\n/// New fields MUST be added to the END of this enum, to maintain ABI compatibility.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[non_exhaustive]\npub enum RawConstraintDataV9 {\n    Unique(RawUniqueConstraintDataV9),\n}\n\n/// Requires that the projection of the table onto these `columns` is a bijection.\n///\n/// That is, there must be a one-to-one relationship between a row and the `columns` of that row.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawUniqueConstraintDataV9 {\n    /// The columns that must be unique.\n    pub columns: ColList,\n}\n\n/// Data for the `RLS` policy on a table.\n#[derive(Debug, Clone, PartialEq, Eq, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialOrd, Ord))]\npub struct RawRowLevelSecurityDefV9 {\n    /// The `sql` expression to use for row-level security.\n    pub sql: RawSql,\n}\n\n/// A miscellaneous module export.\n///\n/// All of the variants here were added after the format of [`RawModuleDefV9`] was already stabilized.\n/// If/when we define `RawModuleDefV10`, these should allbe moved out of `misc_exports` and into their own fields.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord, derive_more::From))]\n#[non_exhaustive]\npub enum RawMiscModuleExportV9 {\n    /// A default value for a column added during a supervised automigration.\n    ColumnDefaultValue(RawColumnDefaultValueV9),\n    /// A procedure definition.\n    Procedure(RawProcedureDefV9),\n    /// A view definition.\n    View(RawViewDefV9),\n}\n\n/// Marks a particular table's column as having a particular default.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawColumnDefaultValueV9 {\n    /// Identifies which table that has the default value.\n    /// This corresponds to `name` in `RawTableDefV9`.\n    pub table: RawIdentifier,\n    /// Identifies which column of `table` that has the default value.\n    pub col_id: ColId,\n    /// A BSATN-encoded [`AlgebraicValue`] valid at the table column's type.\n    /// (We cannot use `AlgebraicValue` directly as it isn't `Spacetimetype`.)\n    pub value: Box<[u8]>,\n}\n\n/// A type declaration.\n///\n/// Exactly of these must be attached to every `Product` and `Sum` type used by a module.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawTypeDefV9 {\n    /// The name of the type declaration.\n    pub name: RawScopedTypeNameV9,\n\n    /// The type to which the declaration refers.\n    /// This must point to an `AlgebraicType::Product` or an `AlgebraicType::Sum` in the module's typespace.\n    pub ty: AlgebraicTypeRef,\n\n    /// Whether this type has a custom ordering.\n    pub custom_ordering: bool,\n}\n\n/// A scoped type name, in the form `scope0::scope1::...::scopeN::name`.\n///\n/// These are the names that will be used *in client code generation*, NOT the names used for types\n/// in the module source code.\n#[derive(Clone, SpacetimeType, PartialEq, Eq, PartialOrd, Ord)]\n#[sats(crate = crate)]\npub struct RawScopedTypeNameV9 {\n    /// The scope for this type.\n    ///\n    /// Empty unless a sats `name` attribute is used, e.g.\n    /// `#[sats(name = \"namespace.name\")]` in Rust.\n    pub scope: Box<[RawIdentifier]>,\n\n    /// The name of the type. This must be unique within the module.\n    ///\n    /// Eventually, we may add more information to this, such as generic arguments.\n    pub name: RawIdentifier,\n}\n\nimpl fmt::Debug for RawScopedTypeNameV9 {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        for module in self.scope.iter() {\n            fmt::Debug::fmt(module, f)?;\n            f.write_str(\"::\")?;\n        }\n        fmt::Debug::fmt(&self.name, f)?;\n        Ok(())\n    }\n}\n\n/// A view definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawViewDefV9 {\n    /// The name of the view function as defined in the module\n    pub name: RawIdentifier,\n\n    /// The index of the view in the module's list of views.\n    pub index: u32,\n\n    /// Is this a public or a private view?\n    /// Currently only public views are supported.\n    /// Private views may be supported in the future.\n    pub is_public: bool,\n\n    /// Is this view anonymous?\n    /// An anonymous view does not know who called it.\n    /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument.\n    /// This type does not have access to the `Identity` of the caller.\n    pub is_anonymous: bool,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// The return type of the view.\n    /// Either `T`, `Option<T>`, or `Vec<T>` where `T` is a `SpacetimeType`.\n    ///\n    /// More strictly `T` must be a SATS `ProductType`,\n    /// however this will be validated by the server on publish.\n    ///\n    /// This is the single source of truth for the views's columns.\n    /// All elements of the inner `ProductType` must have names.\n    /// This again will be validated by the server on publish.\n    pub return_type: AlgebraicType,\n}\n\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\npub enum ViewResultHeader {\n    // This means the row data will follow, as a bsatn-encoded Vec<RowType>.\n    // We could make RowData contain an Vec<u8> of the bytes, but that forces us to make an extra copy when we serialize and\n    // when we deserialize.\n    RowData,\n    // This means we the view wants to return the results of the sql query.\n    RawSql(String),\n    // We can add an option for parameterized queries later,\n    // which would make it easier to cache query plans on the host side.\n}\n\n/// A reducer definition.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawReducerDefV9 {\n    /// The name of the reducer.\n    pub name: RawIdentifier,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// If the reducer has a special role in the module lifecycle, it should be marked here.\n    pub lifecycle: Option<Lifecycle>,\n}\n\n/// Special roles a reducer can play in the module lifecycle.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SpacetimeType)]\n#[cfg_attr(feature = \"enum-map\", derive(enum_map::Enum))]\n#[sats(crate = crate)]\n#[non_exhaustive]\npub enum Lifecycle {\n    /// The reducer will be invoked upon module initialization.\n    Init,\n    /// The reducer will be invoked when a client connects.\n    OnConnect,\n    /// The reducer will be invoked when a client disconnects.\n    OnDisconnect,\n}\n\n/// A procedure definition.\n///\n/// Will be wrapped in [`RawMiscModuleExportV9`] and included in the [`RawModuleDefV9`]'s `misc_exports` vec.\n#[derive(Debug, Clone, SpacetimeType)]\n#[sats(crate = crate)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\npub struct RawProcedureDefV9 {\n    /// The name of the procedure.\n    pub name: RawIdentifier,\n\n    /// The types and optional names of the parameters, in order.\n    /// This `ProductType` need not be registered in the typespace.\n    pub params: ProductType,\n\n    /// The type of the return value.\n    ///\n    /// If this is a user-defined product or sum type,\n    /// it should be registered in the typespace and indirected through an [`AlgebraicType::Ref`].\n    pub return_type: AlgebraicType,\n}\n\n/// A builder for a [`RawModuleDefV9`].\n#[derive(Default)]\npub struct RawModuleDefV9Builder {\n    /// The module definition.\n    module: RawModuleDefV9,\n    /// The type map from `T: 'static` Rust types to sats types.\n    type_map: BTreeMap<TypeId, AlgebraicTypeRef>,\n}\n\nimpl RawModuleDefV9Builder {\n    /// Create a new, empty `RawModuleDefBuilder`.\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Add a type to the in-progress module.\n    ///\n    /// The returned type must satisfy `AlgebraicType::is_valid_for_client_type_definition` or  `AlgebraicType::is_valid_for_client_type_use` .\n    pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {\n        TypespaceBuilder::add_type::<T>(self)\n    }\n\n    /// Create a table builder.\n    ///\n    /// Does not validate that the product_type_ref is valid; this is left to the module validation code.\n    pub fn build_table(\n        &mut self,\n        name: impl Into<RawIdentifier>,\n        product_type_ref: AlgebraicTypeRef,\n    ) -> RawTableDefBuilder<'_> {\n        let name = name.into();\n        RawTableDefBuilder {\n            module_def: &mut self.module,\n            table: RawTableDefV9 {\n                name,\n                product_type_ref,\n                indexes: vec![],\n                constraints: vec![],\n                sequences: vec![],\n                schedule: None,\n                primary_key: ColList::empty(),\n                table_type: TableType::User,\n                table_access: TableAccess::Public,\n            },\n        }\n    }\n\n    /// Build a new table with a product type.\n    /// Adds the type to the module.\n    pub fn build_table_with_new_type(\n        &mut self,\n        table_name: impl Into<RawIdentifier>,\n        product_type: impl Into<spacetimedb_sats::ProductType>,\n        custom_ordering: bool,\n    ) -> RawTableDefBuilder<'_> {\n        let table_name = table_name.into();\n\n        let product_type_ref = self.add_algebraic_type(\n            [],\n            table_name.clone(),\n            AlgebraicType::from(product_type.into()),\n            custom_ordering,\n        );\n\n        self.build_table(table_name, product_type_ref)\n    }\n\n    /// Build a new table with a product type, for testing.\n    /// Adds the type to the module.\n    pub fn build_table_with_new_type_for_tests(\n        &mut self,\n        table_name: impl Into<RawIdentifier>,\n        mut product_type: spacetimedb_sats::ProductType,\n        custom_ordering: bool,\n    ) -> RawTableDefBuilder<'_> {\n        self.add_expand_product_type_for_tests(&mut 0, &mut product_type);\n\n        self.build_table_with_new_type(table_name, product_type, custom_ordering)\n    }\n\n    fn add_expand_type_for_tests(&mut self, name_gen: &mut usize, ty: &mut AlgebraicType) {\n        if ty.is_valid_for_client_type_use() {\n            return;\n        }\n\n        match ty {\n            AlgebraicType::Product(prod_ty) => self.add_expand_product_type_for_tests(name_gen, prod_ty),\n            AlgebraicType::Sum(sum_type) => {\n                if let Some(wrapped) = sum_type.as_option_mut() {\n                    self.add_expand_type_for_tests(name_gen, wrapped);\n                } else {\n                    for elem in sum_type.variants.iter_mut() {\n                        self.add_expand_type_for_tests(name_gen, &mut elem.algebraic_type);\n                    }\n                }\n            }\n            AlgebraicType::Array(ty) => {\n                self.add_expand_type_for_tests(name_gen, &mut ty.elem_ty);\n                return;\n            }\n            _ => return,\n        }\n\n        // Make the type into a ref.\n        let name = *name_gen;\n        let add_ty = core::mem::replace(ty, AlgebraicType::U8);\n        *ty = AlgebraicType::Ref(self.add_algebraic_type([], RawIdentifier::new(format!(\"gen_{name}\")), add_ty, true));\n        *name_gen += 1;\n    }\n\n    fn add_expand_product_type_for_tests(&mut self, name_gen: &mut usize, ty: &mut ProductType) {\n        for elem in ty.elements.iter_mut() {\n            self.add_expand_type_for_tests(name_gen, &mut elem.algebraic_type);\n        }\n    }\n\n    /// Add a type to the typespace, along with a type alias declaring its name.\n    /// This method should only be use for `AlgebraicType`s not corresponding to a Rust\n    /// type that implements `SpacetimeType`.\n    ///\n    /// Returns a reference to the newly-added type.\n    ///\n    /// NOT idempotent, calling this twice with the same name will cause errors during\n    /// validation.\n    ///\n    /// You must set `custom_ordering` if you're not using the default element ordering.\n    pub fn add_algebraic_type(\n        &mut self,\n        scope: impl IntoIterator<Item = RawIdentifier>,\n        name: impl Into<RawIdentifier>,\n        ty: spacetimedb_sats::AlgebraicType,\n        custom_ordering: bool,\n    ) -> AlgebraicTypeRef {\n        let ty = self.module.typespace.add(ty);\n        let scope = scope.into_iter().collect();\n        let name = name.into();\n        self.module.types.push(RawTypeDefV9 {\n            name: RawScopedTypeNameV9 { name, scope },\n            ty,\n            custom_ordering,\n        });\n        // We don't add a `TypeId` to `self.type_map`, because there may not be a corresponding Rust type! e.g. if we are randomly generating types in proptests.\n        ty\n    }\n\n    /// Add a reducer to the in-progress module.\n    /// Accepts a `ProductType` of reducer arguments for convenience.\n    /// The `ProductType` need not be registered in the typespace.\n    ///\n    /// Importantly, if the reducer's first argument is a `ReducerContext`, that\n    /// information should not be provided to this method.\n    /// That is an implementation detail handled by the module bindings and can be ignored.\n    /// As far as the module definition is concerned, the reducer's arguments\n    /// start with the first non-`ReducerContext` argument.\n    ///\n    /// (It is impossible, with the current implementation of `ReducerContext`, to\n    /// have more than one `ReducerContext` argument, at least in Rust.\n    /// This is because `SpacetimeType` is not implemented for `ReducerContext`,\n    /// so it can never act like an ordinary argument.)\n    pub fn add_reducer(\n        &mut self,\n        name: impl Into<RawIdentifier>,\n        params: spacetimedb_sats::ProductType,\n        lifecycle: Option<Lifecycle>,\n    ) {\n        self.module.reducers.push(RawReducerDefV9 {\n            name: name.into(),\n            params,\n            lifecycle,\n        });\n    }\n\n    /// Add a procedure to the in-progress module.\n    ///\n    /// Accepts a `ProductType` of arguments.\n    /// The arguments `ProductType` need not be registered in the typespace.\n    ///\n    /// Also accepts an `AlgebraicType` return type.\n    /// If this is a user-defined product or sum type,\n    /// it should be registered in the typespace and indirected through an `AlgebraicType::Ref`.\n    ///\n    /// The `&mut ProcedureContext` first argument to the procedure should not be included in the `params`.\n    pub fn add_procedure(\n        &mut self,\n        name: impl Into<RawIdentifier>,\n        params: spacetimedb_sats::ProductType,\n        return_type: spacetimedb_sats::AlgebraicType,\n    ) {\n        self.module\n            .misc_exports\n            .push(RawMiscModuleExportV9::Procedure(RawProcedureDefV9 {\n                name: name.into(),\n                params,\n                return_type,\n            }))\n    }\n\n    pub fn add_view(\n        &mut self,\n        name: impl Into<RawIdentifier>,\n        index: usize,\n        is_public: bool,\n        is_anonymous: bool,\n        params: ProductType,\n        return_type: AlgebraicType,\n    ) {\n        self.module.misc_exports.push(RawMiscModuleExportV9::View(RawViewDefV9 {\n            name: name.into(),\n            index: index as u32,\n            is_public,\n            is_anonymous,\n            params,\n            return_type,\n        }));\n    }\n\n    /// Add a row-level security policy to the module.\n    ///\n    /// The `sql` expression should be a valid SQL expression that will be used to filter rows.\n    ///\n    /// **NOTE**: The `sql` expression must be unique within the module.\n    pub fn add_row_level_security(&mut self, sql: &str) {\n        self.module\n            .row_level_security\n            .push(RawRowLevelSecurityDefV9 { sql: sql.into() });\n    }\n\n    /// Get the typespace of the module.\n    pub fn typespace(&self) -> &Typespace {\n        &self.module.typespace\n    }\n\n    /// Finish building, consuming the builder and returning the module.\n    /// The module should be validated before use.\n    pub fn finish(self) -> RawModuleDefV9 {\n        self.module\n    }\n}\n\n/// Convert a string from a sats type-name annotation like `#[sats(name = \"namespace.name\")]` to a `RawScopedTypeNameV9`.\n/// We split the input on the strings `\"::\"` and `\".\"` to split up module paths.\n///\n/// TODO(1.0): build namespacing directly into the bindings macros so that we don't need to do this.\npub fn sats_name_to_scoped_name(sats_name: &str) -> RawScopedTypeNameV9 {\n    // We can't use `&[char]: Pattern` for `split` here because \"::\" is not a char :/\n    let mut scope: Vec<RawIdentifier> = sats_name\n        .split(\"::\")\n        .flat_map(|s| s.split('.'))\n        .map(RawIdentifier::new)\n        .collect();\n    // Unwrapping to \"\" will result in a validation error down the line, which is exactly what we want.\n    let name = scope.pop().unwrap_or_default();\n    RawScopedTypeNameV9 {\n        scope: scope.into(),\n        name,\n    }\n}\n\nimpl TypespaceBuilder for RawModuleDefV9Builder {\n    fn add(\n        &mut self,\n        typeid: TypeId,\n        name: Option<&'static str>,\n        make_ty: impl FnOnce(&mut Self) -> AlgebraicType,\n    ) -> AlgebraicType {\n        let r = match self.type_map.entry(typeid) {\n            btree_map::Entry::Occupied(o) => *o.get(),\n            btree_map::Entry::Vacant(v) => {\n                // Bind a fresh alias to the unit type.\n                let slot_ref = self.module.typespace.add(AlgebraicType::unit());\n                // Relate `typeid -> fresh alias`.\n                v.insert(slot_ref);\n\n                // Alias provided? Relate `name -> slot_ref`.\n                if let Some(sats_name) = name {\n                    let name = sats_name_to_scoped_name(sats_name);\n\n                    self.module.types.push(RawTypeDefV9 {\n                        name,\n                        ty: slot_ref,\n                        // TODO(1.0): we need to update the `TypespaceBuilder` trait to include\n                        // a `custom_ordering` parameter.\n                        // For now, we assume all types have custom orderings, since the derive\n                        // macro doesn't know about the default ordering yet.\n                        custom_ordering: true,\n                    });\n                }\n\n                // Borrow of `v` has ended here, so we can now convince the borrow checker.\n                let ty = make_ty(self);\n                self.module.typespace[slot_ref] = ty;\n                slot_ref\n            }\n        };\n        AlgebraicType::Ref(r)\n    }\n}\n\n/// Builder for a `RawTableDef`.\npub struct RawTableDefBuilder<'a> {\n    module_def: &'a mut RawModuleDefV9,\n    table: RawTableDefV9,\n}\n\nimpl RawTableDefBuilder<'_> {\n    /// Sets the type of the table and return it.\n    ///\n    /// This is not about column algebraic types, but about whether the table\n    /// was created by the system or the user.\n    pub fn with_type(mut self, table_type: TableType) -> Self {\n        self.table.table_type = table_type;\n        self\n    }\n\n    /// Sets the access rights for the table and return it.\n    pub fn with_access(mut self, table_access: TableAccess) -> Self {\n        self.table.table_access = table_access;\n        self\n    }\n\n    /// Generates a [RawConstraintDefV9] using the supplied `columns`.\n    pub fn with_unique_constraint(mut self, columns: impl Into<ColList>) -> Self {\n        let columns = columns.into();\n        self.table.constraints.push(RawConstraintDefV9 {\n            name: None,\n            data: RawConstraintDataV9::Unique(RawUniqueConstraintDataV9 { columns }),\n        });\n        self\n    }\n\n    /// Adds a primary key to the table.\n    /// You must also add a unique constraint on the primary key column.\n    pub fn with_primary_key(mut self, column: impl Into<ColId>) -> Self {\n        self.table.primary_key = ColList::new(column.into());\n        self\n    }\n\n    /// Adds a primary key to the table, with corresponding unique constraint and sequence definitions.\n    /// You will also need to call [`Self::with_index`] to create an index on `column`.\n    pub fn with_auto_inc_primary_key(self, column: impl Into<ColId>) -> Self {\n        let column = column.into();\n        self.with_primary_key(column)\n            .with_unique_constraint(column)\n            .with_column_sequence(column)\n    }\n\n    /// Generates a [RawIndexDefV9] using the supplied `columns`.\n    pub fn with_index(mut self, algorithm: RawIndexAlgorithm, accessor_name: impl Into<RawIdentifier>) -> Self {\n        let accessor_name = accessor_name.into();\n\n        self.table.indexes.push(RawIndexDefV9 {\n            name: None,\n            accessor_name: Some(accessor_name),\n            algorithm,\n        });\n        self\n    }\n\n    /// Generates a [RawIndexDefV9] using the supplied `columns` but with no `accessor_name`.\n    pub fn with_index_no_accessor_name(mut self, algorithm: RawIndexAlgorithm) -> Self {\n        self.table.indexes.push(RawIndexDefV9 {\n            name: None,\n            accessor_name: None,\n            algorithm,\n        });\n        self\n    }\n\n    /// Adds a [RawSequenceDefV9] on the supplied `column`.\n    pub fn with_column_sequence(mut self, column: impl Into<ColId>) -> Self {\n        let column = column.into();\n        self.table.sequences.push(RawSequenceDefV9 {\n            name: None,\n            column,\n            start: None,\n            min_value: None,\n            max_value: None,\n            increment: 1,\n        });\n\n        self\n    }\n\n    /// Adds a schedule definition to the table.\n    ///\n    /// The `function_name` should name a reducer or procedure\n    /// which accepts one argument, a row of this table.\n    ///\n    /// The table must have the appropriate columns for a scheduled table.\n    pub fn with_schedule(\n        mut self,\n        function_name: impl Into<RawIdentifier>,\n        scheduled_at_column: impl Into<ColId>,\n    ) -> Self {\n        let reducer_name = function_name.into();\n        let scheduled_at_column = scheduled_at_column.into();\n        self.table.schedule = Some(RawScheduleDefV9 {\n            name: None,\n            reducer_name,\n            scheduled_at_column,\n        });\n        self\n    }\n\n    /// Adds a default value for the `column`.\n    pub fn with_default_column_value(self, column: impl Into<ColId>, value: AlgebraicValue) -> Self {\n        // Added to `misc_exports` for backwards-compatibility reasons.\n        self.module_def\n            .misc_exports\n            .push(RawMiscModuleExportV9::ColumnDefaultValue(RawColumnDefaultValueV9 {\n                table: self.table.name.clone(),\n                col_id: column.into(),\n                value: spacetimedb_sats::bsatn::to_vec(&value).unwrap().into(),\n            }));\n        self\n    }\n\n    /// Build the table and add it to the module, returning the `product_type_ref` of the table.\n    pub fn finish(self) -> AlgebraicTypeRef {\n        self.table.product_type_ref\n        // self is now dropped.\n    }\n\n    /// Get the column ID of the column with the specified name, if any.\n    ///\n    /// Returns `None` if this `TableDef` has been constructed with an invalid `ProductTypeRef`,\n    /// or if no column exists with that name.\n    pub fn find_col_pos_by_name(&self, column: impl AsRef<str>) -> Option<ColId> {\n        let column = column.as_ref();\n        self.columns()?\n            .iter()\n            .position(|x| x.has_name(column.as_ref()))\n            .map(|x| x.into())\n    }\n\n    /// Get the columns of this type.\n    ///\n    /// Returns `None` if this `TableDef` has been constructed with an invalid `ProductTypeRef`.\n    fn columns(&self) -> Option<&[ProductTypeElement]> {\n        self.module_def\n            .typespace\n            .get(self.table.product_type_ref)\n            .and_then(|ty| ty.as_product())\n            .map(|p| &p.elements[..])\n    }\n}\n\nimpl Drop for RawTableDefBuilder<'_> {\n    fn drop(&mut self) {\n        self.module_def.tables.push(self.table.clone());\n    }\n}\n\nimpl From<RawTypeDefV10> for RawTypeDefV9 {\n    fn from(raw: RawTypeDefV10) -> Self {\n        RawTypeDefV9 {\n            name: raw.source_name.into(),\n            ty: raw.ty,\n            custom_ordering: raw.custom_ordering,\n        }\n    }\n}\n\nimpl From<RawScopedTypeNameV10> for RawScopedTypeNameV9 {\n    fn from(raw: RawScopedTypeNameV10) -> Self {\n        RawScopedTypeNameV9 {\n            scope: raw.scope,\n            name: raw.source_name,\n        }\n    }\n}\n\nimpl From<RawConstraintDefV10> for RawConstraintDefV9 {\n    fn from(raw: RawConstraintDefV10) -> Self {\n        RawConstraintDefV9 {\n            name: raw.source_name,\n            data: raw.data,\n        }\n    }\n}\n\nimpl From<RawSequenceDefV10> for RawSequenceDefV9 {\n    fn from(raw: RawSequenceDefV10) -> Self {\n        RawSequenceDefV9 {\n            name: raw.source_name,\n            column: raw.column,\n            start: raw.start,\n            min_value: raw.min_value,\n            max_value: raw.max_value,\n            increment: raw.increment,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/db/raw_def.rs",
    "content": "//! Raw definitions of the database schema.\n//!\n//! Modules serialize these types and send them across the ABI boundary to describe to the database what tables they expect.\n//! (Wrapped in the type `spacetimedb_lib::ModuleDef`.)\n//!\n//! There will eventually be multiple versions of these types wrapped in a top-level enum.\n//! This is because the only backwards-compatible schema changes allowed by BSATN is adding variants to an existing enum.\n//! The `spacetimedb_schema` crate will in the future perform validation and normalization of these `Raw` types to a canonical form,\n//! which will be used everywhere.\n\npub mod v8;\n\n// for backwards-compatibility\npub use v8::*;\n\npub mod v9;\n\npub mod v10;\n"
  },
  {
    "path": "crates/lib/src/db/view.rs",
    "content": "use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef};\n\npub const QUERY_VIEW_RETURN_TAG: &str = \"__query__\";\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ViewKind {\n    Procedural,\n    Query,\n}\n\npub fn extract_view_return_product_type_ref(return_type: &AlgebraicType) -> Option<(AlgebraicTypeRef, ViewKind)> {\n    // Query-builder views (`Query<T>`) are encoded as: { __query__: T }.\n    if let Some(product) = return_type.as_product()\n        && product.elements.len() == 1\n        && product.elements[0].name.as_deref() == Some(QUERY_VIEW_RETURN_TAG)\n        && let Some(product_type_ref) = product.elements[0].algebraic_type.as_ref().copied()\n    {\n        return Some((product_type_ref, ViewKind::Query));\n    }\n\n    return_type\n        .as_option()\n        .and_then(AlgebraicType::as_ref)\n        .or_else(|| {\n            return_type\n                .as_array()\n                .map(|array_type| array_type.elem_ty.as_ref())\n                .and_then(AlgebraicType::as_ref)\n        })\n        .copied()\n        .map(|product_type_ref| (product_type_ref, ViewKind::Procedural))\n}\n"
  },
  {
    "path": "crates/lib/src/direct_index_key.rs",
    "content": "#[diagnostic::on_unimplemented(\n    message = \"column type must be a one of: `u8`, `u16`, `u32`, `u64`, or plain `enum`\",\n    label = \"should be `u8`, `u16`, `u32`, `u64`, or plain `enum`, not `{Self}`\"\n)]\npub trait DirectIndexKey {}\nimpl DirectIndexKey for u8 {}\nimpl DirectIndexKey for u16 {}\nimpl DirectIndexKey for u32 {}\nimpl DirectIndexKey for u64 {}\n\n/// Assert that `T` is a valid column to use direct index on.\npub const fn assert_column_type_valid_for_direct_index<T: DirectIndexKey>() {}\n"
  },
  {
    "path": "crates/lib/src/error.rs",
    "content": "use std::fmt;\n\n/// A wrapper for using on test so the error display nicely\npub struct TestError {\n    pub error: Box<dyn std::error::Error>,\n}\n\nimpl fmt::Debug for TestError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        // Format the error in yellow\n        write!(f, \"\\x1b[93m{}\\x1b[0m\", self.error)\n    }\n}\n\nimpl<E: std::error::Error + 'static> From<E> for TestError {\n    fn from(e: E) -> Self {\n        Self { error: Box::new(e) }\n    }\n}\n\n/// A wrapper for using [Result] in tests, so it display nicely\npub type ResultTest<T> = Result<T, TestError>;\n"
  },
  {
    "path": "crates/lib/src/filterable_value.rs",
    "content": "use crate::{ConnectionId, Identity, Uuid};\nuse core::ops;\nuse spacetimedb_sats::bsatn;\nuse spacetimedb_sats::{hash::Hash, i256, u256, Serialize};\n\n/// Types which can appear as an argument to an index filtering operation\n/// for a column of type `Column`.\n///\n/// Types which can appear specifically as a terminating bound in a BTree index,\n/// which may be a range, instead use [`IndexScanRangeBoundsTerminator`].\n///\n/// Because SpacetimeDB supports a only restricted set of types as index keys,\n/// only a small set of `Column` types have corresponding `FilterableValue` implementations.\n/// Specifically, these types are:\n/// - Signed and unsigned integers of various widths.\n/// - [`bool`].\n/// - [`String`], which is also filterable with `&str`.\n/// - [`Identity`].\n/// - [`Uuid`].\n/// - [`ConnectionId`].\n/// - [`Hash`](struct@Hash).\n/// - No-payload enums annotated with `#[derive(SpacetimeType)]`.\n///   No-payload enums are sometimes called \"plain,\" \"simple\" or \"C-style.\"\n///   They are enums where no variant has any payload data.\n///\n/// Because SpacetimeDB indexes are present both on the server\n/// and in clients which use our various SDKs,\n/// implementing `FilterableValue` for a new column type is a significant burden,\n/// and **is not as simple** as adding a new `impl FilterableValue` block to our Rust code.\n/// To implement `FilterableValue` for a new column type, you must also:\n/// - Ensure (with automated tests) that the `spacetimedb-codegen` crate\n///   and accompanying SpacetimeDB client SDK can equality-compare and ordering-compare values of the column type,\n///   and that the resulting ordering is the same as the canonical ordering\n///   implemented by `spacetimedb-sats` for [`spacetimedb_sats::AlgebraicValue`].\n///   This will nearly always require implementing bespoke comparison methods for the type in question,\n///   as most languages do not automatically make product types (structs or classes) sortable.\n/// - Extend our other supported module languages' bindings libraries.\n///   so that they can also define tables with indexes keyed by the new filterable type.\n//\n// General rules for implementors of this type:\n// - See above doc comment for requirements to add implementations for new column types.\n// - It should only be implemented for owned values if those values are `Copy`.\n//   Otherwise it should only be implemented for references.\n//   This is so that rustc and IDEs will recommend rewriting `x` to `&x` rather than `x.clone()`.\n// - `Arg: FilterableValue<Column = Col>`\n//   for any pair of types `(Arg, Col)` which meet the above criteria\n//   is desirable if `Arg` and `Col` have the same BSATN layout.\n//   E.g. `&str: FilterableValue<Column = String>` is desirable.\n#[diagnostic::on_unimplemented(\n    message = \"`{Self}` cannot appear as an argument to an index filtering operation\",\n    label = \"should be an integer type, `bool`, `String`, `&str`, `Identity`, `Uuid`, `ConnectionId`, `Hash` or a no-payload enum which derives `SpacetimeType`, not `{Self}`\",\n    note = \"The allowed set of types are limited to integers, bool, strings, `Identity`, `Uuid`, `ConnectionId`, `Hash` and no-payload enums which derive `SpacetimeType`,\"\n)]\npub trait FilterableValue: Serialize + Private {\n    type Column;\n}\n\n/// Hidden supertrait for [`FilterableValue`],\n/// to discourage users from hand-writing implementations.\n///\n/// We want to expose [`FilterableValue`] in the docs, but to prevent users from implementing it.\n/// Normally, we would just make this `Private` trait inaccessible,\n/// but we need to macro-generate implementations, so it must be `pub`.\n/// We mark it `doc(hidden)` to discourage use.\n#[doc(hidden)]\npub trait Private {}\n\nmacro_rules! impl_filterable_value {\n    (@one $arg:ty => $col:ty) => {\n        impl Private for $arg {}\n        impl FilterableValue for $arg {\n            type Column = $col;\n        }\n    };\n    (@one $arg:ty: Copy) => {\n        impl_filterable_value!(@one $arg => $arg);\n        impl_filterable_value!(@one &$arg => $arg);\n    };\n    (@one $arg:ty) => {\n        impl_filterable_value!(@one &$arg => $arg);\n    };\n    ($($arg:ty $(: $copy:ident)? $(=> $col:ty)?),* $(,)?) => {\n        $(impl_filterable_value!(@one $arg $(: $copy)? $(=> $col)?);)*\n    };\n}\n\nimpl_filterable_value! {\n    u8: Copy,\n    u16: Copy,\n    u32: Copy,\n    u64: Copy,\n    u128: Copy,\n    u256: Copy,\n\n    i8: Copy,\n    i16: Copy,\n    i32: Copy,\n    i64: Copy,\n    i128: Copy,\n    i256: Copy,\n\n    bool: Copy,\n\n    String,\n    &str => String,\n\n    Identity: Copy,\n    Uuid: Copy,\n    ConnectionId: Copy,\n    Hash: Copy,\n\n    // Some day we will likely also want to support `Vec<u8>` and `[u8]`,\n    // as they have trivial portable equality and ordering,\n    // but @RReverser's proposed filtering rules do not include them.\n    // Vec<u8>,\n    // &[u8] => Vec<u8>,\n}\n\npub enum TermBound<T> {\n    Single(ops::Bound<T>),\n    Range(ops::Bound<T>, ops::Bound<T>),\n}\nimpl<Bound: FilterableValue> TermBound<&Bound> {\n    #[inline]\n    /// If `self` is [`TermBound::Range`], returns the `rend_idx` value for `IndexScanRangeArgs`,\n    /// i.e. the index in `buf` of the first byte in the end range\n    pub fn serialize_into(&self, buf: &mut Vec<u8>) -> Option<usize> {\n        let (start, end) = match self {\n            TermBound::Single(elem) => (elem, None),\n            TermBound::Range(start, end) => (start, Some(end)),\n        };\n        bsatn::to_writer(buf, start).unwrap();\n        end.map(|end| {\n            let rend_idx = buf.len();\n            bsatn::to_writer(buf, end).unwrap();\n            rend_idx\n        })\n    }\n}\npub trait IndexScanRangeBoundsTerminator {\n    /// Whether this bound terminator is a point.\n    const POINT: bool = false;\n\n    /// The key type of the bound.\n    type Arg;\n\n    /// Returns the point bound, assuming `POINT == true`.\n    fn point(&self) -> &Self::Arg {\n        unimplemented!()\n    }\n\n    /// Returns the terminal bound for the range scan.\n    /// This bound can either be a point, as in most cases, or an actual bound.\n    fn bounds(&self) -> TermBound<&Self::Arg>;\n}\n\nimpl<Col, Arg: FilterableValue<Column = Col>> IndexScanRangeBoundsTerminator for Arg {\n    const POINT: bool = true;\n    type Arg = Arg;\n    fn point(&self) -> &Arg {\n        self\n    }\n    fn bounds(&self) -> TermBound<&Arg> {\n        TermBound::Single(ops::Bound::Included(self))\n    }\n}\n\nmacro_rules! impl_terminator {\n    ($($range:ty),* $(,)?) => {\n        $(impl<T: FilterableValue> IndexScanRangeBoundsTerminator for $range {\n            type Arg = T;\n            fn bounds(&self) -> TermBound<&T> {\n                TermBound::Range(\n                    ops::RangeBounds::start_bound(self),\n                    ops::RangeBounds::end_bound(self),\n                )\n            }\n        })*\n    };\n}\n\nimpl_terminator!(\n    ops::Range<T>,\n    ops::RangeFrom<T>,\n    ops::RangeInclusive<T>,\n    ops::RangeTo<T>,\n    ops::RangeToInclusive<T>,\n    (ops::Bound<T>, ops::Bound<T>),\n);\n"
  },
  {
    "path": "crates/lib/src/http.rs",
    "content": "//! `SpacetimeType`-ified HTTP request and response types,\n//! for use in the procedure HTTP API.\n//!\n//! The types here are all mirrors of various types within the `http` crate.\n//! That crate's types don't have stable representations or `pub`lic interiors,\n//! so we're forced to define our own representation for the SATS serialization.\n//! These types are that representation.\n//!\n//! Users aren't intended to interact with these types,\n//! Our user-facing APIs should use the `http` crate's types directly, and convert to and from these types internally.\n//!\n//! These types are used in BSATN encoding for interchange between the SpacetimeDB host\n//! and guest WASM modules in the `procedure_http_request` ABI call.\n//! For that reason, the layout of these types must not change.\n//! Because we want, to the extent possible,\n//! to support both (old host, new guest) and (new host, old guest) pairings,\n//! we can't meaningfully make these types extensible, even with tricks like version enum wrappers.\n//! Instead, if/when we want to add new functionality which requires sending additional information,\n//! we'll define a new versioned ABI call which uses new types for interchange.\n\nuse spacetimedb_sats::{time_duration::TimeDuration, SpacetimeType};\n\n/// Represents an HTTP request which can be made from a procedure running in a SpacetimeDB database.\n#[derive(Clone, SpacetimeType)]\n#[sats(crate = crate, name = \"HttpRequest\")]\npub struct Request {\n    pub method: Method,\n    pub headers: Headers,\n    pub timeout: Option<TimeDuration>,\n    /// A valid URI, sourced from an already-validated `http::Uri`.\n    pub uri: String,\n    pub version: Version,\n}\n\nimpl Request {\n    /// Return the size of this request's URI and [`Headers`]\n    /// for purposes of metrics reporting.\n    ///\n    /// Ignores the size of the [`Method`] and [`Version`] as they are effectively constant.\n    ///\n    /// As the body is stored externally to the `Request`, metrics reporting must count its size separately.\n    pub fn size_in_bytes(&self) -> usize {\n        self.uri.len() + self.headers.size_in_bytes()\n    }\n}\n\n/// Represents an HTTP method.\n#[derive(Clone, SpacetimeType, PartialEq, Eq)]\n#[sats(crate = crate, name = \"HttpMethod\")]\npub enum Method {\n    Get,\n    Head,\n    Post,\n    Put,\n    Delete,\n    Connect,\n    Options,\n    Trace,\n    Patch,\n    Extension(String),\n}\n\n/// An HTTP version.\n#[derive(Clone, SpacetimeType, PartialEq, Eq)]\n#[sats(crate = crate, name = \"HttpVersion\")]\npub enum Version {\n    Http09,\n    Http10,\n    Http11,\n    Http2,\n    Http3,\n}\n\n/// A set of HTTP headers.\n#[derive(Clone, SpacetimeType)]\n#[sats(crate = crate, name = \"HttpHeaders\")]\npub struct Headers {\n    // SATS doesn't (and won't) have a multimap type, so just use an array of pairs for the ser/de format.\n    entries: Box<[HttpHeaderPair]>,\n}\n\n// `http::header::IntoIter` only returns the `HeaderName` for the first\n// `HeaderValue` with that name, so we have to manually assign the names.\nstruct HeaderIter<I, T> {\n    prev: Option<(Box<str>, T)>,\n    inner: I,\n}\n\nimpl<I, T> Iterator for HeaderIter<I, T>\nwhere\n    I: Iterator<Item = (Option<Box<str>>, T)>,\n{\n    type Item = (Box<str>, T);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let (prev_k, prev_v) = self\n            .prev\n            .take()\n            .or_else(|| self.inner.next().map(|(k, v)| (k.unwrap(), v)))?;\n        self.prev = self\n            .inner\n            .next()\n            .map(|(next_k, next_v)| (next_k.unwrap_or_else(|| prev_k.clone()), next_v));\n        Some((prev_k, prev_v))\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\nimpl FromIterator<(Option<Box<str>>, Box<[u8]>)> for Headers {\n    fn from_iter<T: IntoIterator<Item = (Option<Box<str>>, Box<[u8]>)>>(iter: T) -> Self {\n        let inner = iter.into_iter();\n        let entries = HeaderIter { prev: None, inner }\n            .map(|(name, value)| HttpHeaderPair { name, value })\n            .collect();\n        Self { entries }\n    }\n}\n\nimpl Headers {\n    #[allow(clippy::should_implement_trait)]\n    pub fn into_iter(self) -> impl Iterator<Item = (Box<str>, Box<[u8]>)> {\n        IntoIterator::into_iter(self.entries).map(|HttpHeaderPair { name, value }| (name, value))\n    }\n\n    /// The sum of the lengths of all the header names and header values.\n    ///\n    /// For headers with multiple values for the same header name,\n    /// the length of the header name is counted once for each occurrence.\n    fn size_in_bytes(&self) -> usize {\n        self.entries\n            .iter()\n            .map(|HttpHeaderPair { name, value }| name.len() + value.len())\n            .sum::<usize>()\n    }\n}\n\n#[derive(Clone, SpacetimeType)]\n#[sats(crate = crate, name = \"HttpHeaderPair\")]\nstruct HttpHeaderPair {\n    /// A valid HTTP header name, sourced from an already-validated `http::HeaderName`.\n    name: Box<str>,\n    /// A valid HTTP header value, sourced from an already-validated `http::HeaderValue`.\n    value: Box<[u8]>,\n}\n\n#[derive(Clone, SpacetimeType)]\n#[sats(crate = crate, name = \"HttpResponse\")]\npub struct Response {\n    pub headers: Headers,\n    pub version: Version,\n    /// A valid HTTP response status code, sourced from an already-validated `http::StatusCode`.\n    pub code: u16,\n}\n\nimpl Response {\n    /// Return the size of this request's [`Headers`] for purposes of metrics reporting.\n    ///\n    /// Ignores the size of the `code` and [`Version`] as they are effectively constant.\n    ///\n    /// As the body is stored externally to the `Response`, metrics reporting must count its size separately.\n    pub fn size_in_bytes(&self) -> usize {\n        self.headers.size_in_bytes()\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/identity.rs",
    "content": "use crate::db::auth::StAccess;\nuse crate::from_hex_pad;\nuse blake3;\nuse core::mem;\nuse spacetimedb_bindings_macro::{Deserialize, Serialize};\nuse spacetimedb_sats::hex::HexString;\nuse spacetimedb_sats::{impl_st, u256, AlgebraicType, AlgebraicValue};\nuse std::sync::Arc;\nuse std::{fmt, str::FromStr};\n\npub type RequestId = u32;\n\n/// Set of permissions the SQL engine may ask for.\npub enum SqlPermission {\n    /// Read permissions given the [StAccess] of a table.\n    ///\n    /// [StAccess] must be passed in order to allow external implementations\n    /// to fail compilation should the [StAccess] enum ever gain additional\n    /// variants. Implementations should always do an exhaustive match thus.\n    ///\n    /// [SqlAuthorization::has_sql_permission] must return true if\n    /// [StAccess::Public].\n    Read(StAccess),\n    /// Write access, i.e. executing DML.\n    Write,\n    /// If granted, no row limit checks will be performed for subscription queries.\n    ExceedRowLimit,\n    /// RLS does not apply to database owners (for some definition of owner).\n    /// If the subject qualifies as an owner, the permission should be granted.\n    BypassRLS,\n}\n\n/// Types than can grant or deny [SqlPermission]s.\npub trait SqlAuthorization {\n    /// Returns `true` if permission `p` is granted, `false` otherwise.\n    fn has_sql_permission(&self, p: SqlPermission) -> bool;\n}\n\nimpl<T: Fn(SqlPermission) -> bool> SqlAuthorization for T {\n    fn has_sql_permission(&self, p: SqlPermission) -> bool {\n        self(p)\n    }\n}\n\n/// [SqlAuthorization] trait object.\npub type SqlPermissions = Arc<dyn SqlAuthorization + Send + Sync + 'static>;\n\n/// The legacy permissions (sans \"teams\") grant everything if the owner is\n/// equal to the caller.\nfn owner_permissions(owner: Identity, caller: Identity) -> SqlPermissions {\n    let is_owner = owner == caller;\n    Arc::new(move |p| match p {\n        SqlPermission::Read(access) => match access {\n            StAccess::Public => true,\n            StAccess::Private => is_owner,\n        },\n        _ => is_owner,\n    })\n}\n\n/// Authorization for SQL operations (queries, DML, subscription queries).\n#[derive(Clone)]\npub struct AuthCtx {\n    caller: Identity,\n    permissions: SqlPermissions,\n}\n\nimpl AuthCtx {\n    pub fn new(owner: Identity, caller: Identity) -> Self {\n        Self::with_permissions(caller, owner_permissions(owner, caller))\n    }\n\n    pub fn with_permissions(caller: Identity, permissions: SqlPermissions) -> Self {\n        Self { caller, permissions }\n    }\n\n    /// For when the owner == caller\n    pub fn for_current(owner: Identity) -> Self {\n        Self::new(owner, owner)\n    }\n\n    pub fn has_permission(&self, p: SqlPermission) -> bool {\n        self.permissions.has_sql_permission(p)\n    }\n\n    pub fn has_read_access(&self, table_access: StAccess) -> bool {\n        self.has_permission(SqlPermission::Read(table_access))\n    }\n\n    pub fn has_write_access(&self) -> bool {\n        self.has_permission(SqlPermission::Write)\n    }\n\n    pub fn exceed_row_limit(&self) -> bool {\n        self.has_permission(SqlPermission::ExceedRowLimit)\n    }\n\n    pub fn bypass_rls(&self) -> bool {\n        self.has_permission(SqlPermission::BypassRLS)\n    }\n\n    pub fn caller(&self) -> Identity {\n        self.caller\n    }\n\n    /// WARNING: Use this only for simple test were the `auth` don't matter\n    pub fn for_testing() -> Self {\n        Self::new(Identity::__dummy(), Identity::__dummy())\n    }\n}\n\n/// An `Identity` for something interacting with the database.\n///\n/// <!-- TODO: docs for OpenID stuff. -->\n///\n/// An `Identity` is a 256-bit unsigned integer. These are encoded in various ways.\n/// - In JSON, an `Identity` is represented as a hexadecimal number wrapped in a string, `\"0x[64 hex characters]\"`.\n/// - In BSATN, an `Identity` is represented as a LITTLE-ENDIAN number 32 bytes long.\n/// - In memory, an `Identity` is stored as a 256-bit number with the endianness of the host system.\n///\n/// If you are manually converting a hexadecimal string to a byte array like so:\n/// ```ignore\n/// \"0xb0b1b2...\"\n/// ->\n/// [0xb0, 0xb1, 0xb2, ...]\n/// ```\n/// Make sure you call `Identity::from_be_byte_array` and NOT `Identity::from_byte_array`.\n/// The standard way of writing hexadecimal numbers follows a big-endian convention, if you\n/// index the characters in written text in increasing order from left to right.\n#[derive(Default, Eq, PartialEq, PartialOrd, Ord, Clone, Copy, Hash, Serialize, Deserialize)]\npub struct Identity {\n    __identity__: u256,\n}\n\nimpl_st!([] Identity, AlgebraicType::identity());\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for Identity {}\n\n#[cfg(feature = \"metrics_impls\")]\nimpl spacetimedb_metrics::typed_prometheus::AsPrometheusLabel for Identity {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n        self.to_hex()\n    }\n}\n\nimpl Identity {\n    /// The 0x0 `Identity`\n    pub const ZERO: Self = Self::from_u256(u256::ZERO);\n\n    /// The 0x1 `Identity`\n    pub const ONE: Self = Self::from_u256(u256::ONE);\n\n    /// Create an `Identity` from a LITTLE-ENDIAN byte array.\n    ///\n    /// If you are parsing an `Identity` from a string, you probably want `from_be_byte_array` instead.\n    pub const fn from_byte_array(bytes: [u8; 32]) -> Self {\n        // SAFETY: The transmute is an implementation of `u256::from_le_bytes`,\n        // but works in a const context.\n        Self::from_u256(u256::from_le(unsafe { mem::transmute::<[u8; 32], u256>(bytes) }))\n    }\n\n    /// Create an `Identity` from a BIG-ENDIAN byte array.\n    ///\n    /// This method is the correct choice if you have converted the bytes of a hexadecimal-formatted `Identity`\n    /// to a byte array in the following way:\n    /// ```ignore\n    /// \"0xb0b1b2...\"\n    /// ->\n    /// [0xb0, 0xb1, 0xb2, ...]\n    /// ```\n    pub const fn from_be_byte_array(bytes: [u8; 32]) -> Self {\n        // SAFETY: The transmute is an implementation of `u256::from_be_bytes`,\n        // but works in a const context.\n        Self::from_u256(u256::from_be(unsafe { mem::transmute::<[u8; 32], u256>(bytes) }))\n    }\n\n    /// Converts `__identity__: u256` to `Identity`.\n    pub const fn from_u256(__identity__: u256) -> Self {\n        Self { __identity__ }\n    }\n\n    /// Converts this identity to an `u256`.\n    pub const fn to_u256(&self) -> u256 {\n        self.__identity__\n    }\n\n    #[doc(hidden)]\n    pub fn __dummy() -> Self {\n        Self::ZERO\n    }\n\n    /// Derives an identity from a [JWT] `issuer` and a `subject`.\n    ///\n    /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token\n    pub fn from_claims(issuer: &str, subject: &str) -> Self {\n        let input = format!(\"{issuer}|{subject}\");\n        let first_hash = blake3::hash(input.as_bytes());\n        let id_hash = &first_hash.as_bytes()[..26];\n        let mut checksum_input = [0u8; 28];\n        // TODO: double check this gets the right number...\n        checksum_input[2..].copy_from_slice(id_hash);\n        checksum_input[0] = 0xc2;\n        checksum_input[1] = 0x00;\n        let checksum_hash = &blake3::hash(&checksum_input);\n\n        let mut final_bytes = [0u8; 32];\n        final_bytes[0] = 0xc2;\n        final_bytes[1] = 0x00;\n        final_bytes[2..6].copy_from_slice(&checksum_hash.as_bytes()[..4]);\n        final_bytes[6..].copy_from_slice(id_hash);\n\n        // We want the leading two bytes of the Identity to be `c200` when formatted.\n        // This means that these should be the MOST significant bytes.\n        // This corresponds to a BIG-ENDIAN byte order of our buffer above.\n        Identity::from_be_byte_array(final_bytes)\n    }\n\n    /// Returns this `Identity` as a byte array.\n    pub fn to_byte_array(&self) -> [u8; 32] {\n        self.__identity__.to_le_bytes()\n    }\n\n    /// Convert this `Identity` to a BIG-ENDIAN byte array.\n    pub fn to_be_byte_array(&self) -> [u8; 32] {\n        self.__identity__.to_be_bytes()\n    }\n\n    /// Convert this `Identity` to a hexadecimal string.\n    pub fn to_hex(&self) -> HexString<32> {\n        spacetimedb_sats::hex::encode(&self.to_be_byte_array())\n    }\n\n    /// Extract the first 8 bytes of this `Identity` as if it was stored in BIG-ENDIAN\n    /// format. (That is, the most significant bytes.)\n    pub fn abbreviate(&self) -> [u8; 8] {\n        self.to_be_byte_array()[..8].try_into().unwrap()\n    }\n\n    /// Extract the first 16 characters of this `Identity`'s hexadecimal representation.\n    pub fn to_abbreviated_hex(&self) -> HexString<8> {\n        spacetimedb_sats::hex::encode(&self.abbreviate())\n    }\n\n    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, hex::FromHexError> {\n        hex::FromHex::from_hex(hex)\n    }\n}\n\nimpl fmt::Display for Identity {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.pad(&self.to_hex())\n    }\n}\n\nimpl fmt::Debug for Identity {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"Identity\").field(&self.to_hex()).finish()\n    }\n}\n\nimpl hex::FromHex for Identity {\n    type Error = hex::FromHexError;\n\n    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {\n        from_hex_pad(hex).map(Identity::from_be_byte_array)\n    }\n}\n\nimpl FromStr for Identity {\n    type Err = <Self as hex::FromHex>::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Self::from_hex(s)\n    }\n}\n\nimpl From<Identity> for AlgebraicValue {\n    fn from(value: Identity) -> Self {\n        AlgebraicValue::product([value.to_u256().into()])\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for Identity {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        spacetimedb_sats::ser::serde::serialize_to(&self.to_be_byte_array(), serializer)\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl<'de> serde::Deserialize<'de> for Identity {\n    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        let arr = spacetimedb_sats::de::serde::deserialize_from(deserializer)?;\n        Ok(Identity::from_be_byte_array(arr))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n    use proptest::string::string_regex;\n    use spacetimedb_sats::{de::serde::DeserializeWrapper, ser::serde::SerializeWrapper, GroundSpacetimeType as _};\n\n    #[test]\n    fn identity_is_special() {\n        assert!(Identity::get_type().is_special());\n    }\n\n    #[test]\n    fn identity_json_serialization_big_endian() {\n        let id = Identity::from_be_byte_array([\n            0xff, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\n            28, 29, 30, 31,\n        ]);\n\n        let hex = id.to_hex();\n        assert!(\n            hex.as_str().starts_with(\"ff01\"),\n            \"expected {hex:?} to start with \\\"ff01\\\"\"\n        );\n\n        let json1 = serde_json::to_string(&id).unwrap();\n        let json2 = serde_json::to_string(SerializeWrapper::from_ref(&id)).unwrap();\n\n        assert!(\n            json1.contains(hex.as_str()),\n            \"expected {json1} to contain {hex} but it didn't\"\n        );\n        assert!(\n            json2.contains(hex.as_str()),\n            \"expected {json2} to contain {hex} but it didn't\"\n        );\n    }\n\n    /// Make sure the checksum is valid.\n    fn validate_checksum(id: &[u8; 32]) -> bool {\n        let checksum_input = &id[6..];\n        let mut checksum_input_with_prefix = [0u8; 28];\n        checksum_input_with_prefix[2..].copy_from_slice(checksum_input);\n        checksum_input_with_prefix[0] = 0xc2;\n        checksum_input_with_prefix[1] = 0x00;\n        let checksum_hash = &blake3::hash(&checksum_input_with_prefix);\n        checksum_hash.as_bytes()[0..4] == id[2..6]\n    }\n\n    proptest! {\n        #[test]\n        fn identity_conversions(w0: u128, w1: u128) {\n            let v = Identity::from_u256(u256::from_words(w0, w1));\n\n            prop_assert_eq!(Identity::from_byte_array(v.to_byte_array()), v);\n            prop_assert_eq!(Identity::from_be_byte_array(v.to_be_byte_array()), v);\n            prop_assert_eq!(Identity::from_hex(v.to_hex()).unwrap(), v);\n\n            let de1: Identity = serde_json::from_str(&serde_json::to_string(&v).unwrap()).unwrap();\n            prop_assert_eq!(de1, v);\n            let DeserializeWrapper(de2): DeserializeWrapper<Identity> = serde_json::from_str(&serde_json::to_string(SerializeWrapper::from_ref(&v)).unwrap()).unwrap();\n            prop_assert_eq!(de2, v);\n        }\n\n        #[test]\n        fn from_claims_formats_correctly(s1 in string_regex(r\".{3,5}\").unwrap(), s2 in string_regex(r\".{3,5}\").unwrap()) {\n            let id = Identity::from_claims(&s1, &s2);\n            prop_assert!(id.to_hex().starts_with(\"c200\"));\n            prop_assert!(validate_checksum(&id.to_be_byte_array()));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/lib.rs",
    "content": "use crate::db::raw_def::v9::RawModuleDefV9Builder;\nuse crate::db::raw_def::RawTableDefV8;\n\n#[doc(hidden)]\npub use crate::db::raw_def::v10::ExplicitNames;\nuse anyhow::Context;\nuse sats::typespace::TypespaceBuilder;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::WithTypespace;\nuse std::any::TypeId;\nuse std::collections::{btree_map, BTreeMap};\n\npub mod connection_id;\npub mod db;\nmod direct_index_key;\npub mod error;\nmod filterable_value;\npub mod http;\npub mod identity;\npub mod metrics;\npub mod operator;\npub mod query;\npub mod scheduler;\npub mod st_var;\npub mod version;\n\npub mod type_def {\n    pub use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, SumType};\n}\npub mod type_value {\n    pub use spacetimedb_sats::{AlgebraicValue, ProductValue};\n}\n\npub use connection_id::ConnectionId;\npub use direct_index_key::{assert_column_type_valid_for_direct_index, DirectIndexKey};\n#[doc(hidden)]\npub use filterable_value::Private;\npub use filterable_value::{FilterableValue, IndexScanRangeBoundsTerminator, TermBound};\npub use identity::Identity;\npub use scheduler::ScheduleAt;\npub use spacetimedb_sats::hash::{self, hash_bytes, Hash};\npub use spacetimedb_sats::time_duration::TimeDuration;\npub use spacetimedb_sats::timestamp::Timestamp;\npub use spacetimedb_sats::uuid::Uuid;\npub use spacetimedb_sats::SpacetimeType;\npub use spacetimedb_sats::__make_register_reftype;\npub use spacetimedb_sats::{self as sats, bsatn, buffer, de, ser};\npub use spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, SumType};\npub use spacetimedb_sats::{AlgebraicValue, ProductValue};\n\npub const MODULE_ABI_MAJOR_VERSION: u16 = 10;\n\n// if it ends up we need more fields in the future, we can split one of them in two\n#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]\npub struct VersionTuple {\n    /// Breaking change; different major versions are not at all compatible with each other.\n    pub major: u16,\n    /// Non-breaking change; a host can run a module that requests an older minor version than the\n    /// host implements, but not the other way around\n    pub minor: u16,\n}\n\nimpl VersionTuple {\n    pub const fn new(major: u16, minor: u16) -> Self {\n        Self { major, minor }\n    }\n\n    #[inline]\n    pub const fn eq(self, other: Self) -> bool {\n        self.major == other.major && self.minor == other.minor\n    }\n\n    /// Checks if a host implementing this version can run a module that expects `module_version`\n    #[inline]\n    pub const fn supports(self, module_version: VersionTuple) -> bool {\n        self.major == module_version.major && self.minor >= module_version.minor\n    }\n\n    #[inline]\n    pub const fn from_u32(v: u32) -> Self {\n        let major = (v >> 16) as u16;\n        let minor = (v & 0xFF) as u16;\n        Self { major, minor }\n    }\n\n    #[inline]\n    pub const fn to_u32(self) -> u32 {\n        (self.major as u32) << 16 | self.minor as u32\n    }\n}\n\nimpl std::fmt::Display for VersionTuple {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let Self { major, minor } = *self;\n        write!(f, \"{major}.{minor}\")\n    }\n}\n\nextern crate self as spacetimedb_lib;\n\n//WARNING: Change this structure(or any of their members) is an ABI change.\n#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, SpacetimeType)]\n#[sats(crate = crate)]\npub struct TableDesc {\n    pub schema: RawTableDefV8,\n    /// data should always point to a ProductType in the typespace\n    pub data: sats::AlgebraicTypeRef,\n}\n\nimpl TableDesc {\n    pub fn into_table_def(table: WithTypespace<'_, TableDesc>) -> anyhow::Result<RawTableDefV8> {\n        let schema = table\n            .map(|t| &t.data)\n            .resolve_refs()\n            .context(\"recursive types not yet supported\")?;\n        let schema = schema.into_product().ok().context(\"table not a product type?\")?;\n        let table = table.ty();\n        anyhow::ensure!(\n            table.schema.columns.len() == schema.elements.len(),\n            \"mismatched number of columns\"\n        );\n\n        Ok(table.schema.clone())\n    }\n}\n\n#[derive(Debug, Clone, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\npub struct ReducerDef {\n    pub name: RawIdentifier,\n    pub args: Vec<ProductTypeElement>,\n}\n\n//WARNING: Change this structure (or any of their members) is an ABI change.\n#[derive(Debug, Clone, Default, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\npub struct RawModuleDefV8 {\n    pub typespace: sats::Typespace,\n    pub tables: Vec<TableDesc>,\n    pub reducers: Vec<ReducerDef>,\n    pub misc_exports: Vec<MiscModuleExport>,\n}\n\nimpl RawModuleDefV8 {\n    pub fn builder() -> ModuleDefBuilder {\n        ModuleDefBuilder::default()\n    }\n\n    pub fn with_builder(f: impl FnOnce(&mut ModuleDefBuilder)) -> Self {\n        let mut builder = Self::builder();\n        f(&mut builder);\n        builder.finish()\n    }\n}\n\n/// A versioned raw module definition.\n///\n/// This is what is actually returned by the module when `__describe_module__` is called, serialized to BSATN.\n#[derive(Debug, Clone, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\n#[non_exhaustive]\npub enum RawModuleDef {\n    V8BackCompat(RawModuleDefV8),\n    V9(db::raw_def::v9::RawModuleDefV9),\n    V10(db::raw_def::v10::RawModuleDefV10),\n    // TODO(jgilles): It would be nice to have a custom error message if this fails with an unknown variant,\n    // but I'm not sure if that can be done via the Deserialize trait.\n}\n\n/// A builder for a [`RawModuleDefV8`].\n/// Deprecated.\n#[derive(Default)]\npub struct ModuleDefBuilder {\n    /// The module definition.\n    module: RawModuleDefV8,\n    /// The type map from `T: 'static` Rust types to sats types.\n    type_map: BTreeMap<TypeId, sats::AlgebraicTypeRef>,\n}\n\nimpl ModuleDefBuilder {\n    pub fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType {\n        TypespaceBuilder::add_type::<T>(self)\n    }\n\n    /// Add a type that may not correspond to a Rust type.\n    /// Used only in tests.\n    #[cfg(feature = \"test\")]\n    pub fn add_type_for_tests(&mut self, name: &str, ty: AlgebraicType) -> spacetimedb_sats::AlgebraicTypeRef {\n        let slot_ref = self.module.typespace.add(ty);\n        self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {\n            name: RawIdentifier::new(name),\n            ty: slot_ref,\n        }));\n        slot_ref\n    }\n\n    /// Add a table that may not correspond to a Rust type.\n    /// Wraps it in a `TableDesc` and generates a corresponding `ProductType` in the typespace.\n    /// Used only in tests.\n    /// Returns the `AlgebraicTypeRef` of the generated `ProductType`.\n    #[cfg(feature = \"test\")]\n    pub fn add_table_for_tests(&mut self, schema: RawTableDefV8) -> spacetimedb_sats::AlgebraicTypeRef {\n        let ty: ProductType = schema\n            .columns\n            .iter()\n            .map(|c| ProductTypeElement {\n                name: Some(c.col_name.clone()),\n                algebraic_type: c.col_type.clone(),\n            })\n            .collect();\n        let data = self.module.typespace.add(ty.into());\n        self.add_type_alias(TypeAlias {\n            name: schema.table_name.clone(),\n            ty: data,\n        });\n        self.add_table(TableDesc { schema, data });\n        data\n    }\n\n    pub fn add_table(&mut self, table: TableDesc) {\n        self.module.tables.push(table)\n    }\n\n    pub fn add_reducer(&mut self, reducer: ReducerDef) {\n        self.module.reducers.push(reducer)\n    }\n\n    #[cfg(feature = \"test\")]\n    pub fn add_reducer_for_tests(&mut self, name: impl AsRef<str>, args: ProductType) {\n        self.add_reducer(ReducerDef {\n            name: RawIdentifier::new(name.as_ref()),\n            args: args.elements.to_vec(),\n        });\n    }\n\n    pub fn add_misc_export(&mut self, misc_export: MiscModuleExport) {\n        self.module.misc_exports.push(misc_export)\n    }\n\n    pub fn add_type_alias(&mut self, type_alias: TypeAlias) {\n        self.add_misc_export(MiscModuleExport::TypeAlias(type_alias))\n    }\n\n    pub fn typespace(&self) -> &sats::Typespace {\n        &self.module.typespace\n    }\n\n    pub fn finish(self) -> RawModuleDefV8 {\n        self.module\n    }\n}\n\nimpl TypespaceBuilder for ModuleDefBuilder {\n    fn add(\n        &mut self,\n        typeid: TypeId,\n        name: Option<&'static str>,\n        make_ty: impl FnOnce(&mut Self) -> AlgebraicType,\n    ) -> AlgebraicType {\n        let r = match self.type_map.entry(typeid) {\n            btree_map::Entry::Occupied(o) => *o.get(),\n            btree_map::Entry::Vacant(v) => {\n                // Bind a fresh alias to the unit type.\n                let slot_ref = self.module.typespace.add(AlgebraicType::unit());\n                // Relate `typeid -> fresh alias`.\n                v.insert(slot_ref);\n\n                // Alias provided? Relate `name -> slot_ref`.\n                if let Some(name) = name {\n                    self.module.misc_exports.push(MiscModuleExport::TypeAlias(TypeAlias {\n                        name: name.into(),\n                        ty: slot_ref,\n                    }));\n                }\n\n                // Borrow of `v` has ended here, so we can now convince the borrow checker.\n                let ty = make_ty(self);\n                self.module.typespace[slot_ref] = ty;\n                slot_ref\n            }\n        };\n        AlgebraicType::Ref(r)\n    }\n}\n\n// an enum to keep it extensible without breaking abi\n#[derive(Debug, Clone, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\npub enum MiscModuleExport {\n    TypeAlias(TypeAlias),\n}\n\n#[derive(Debug, Clone, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\npub struct TypeAlias {\n    pub name: RawIdentifier,\n    pub ty: sats::AlgebraicTypeRef,\n}\n\n/// Converts a hexadecimal string reference to a byte array.\n///\n/// This function takes a reference to a hexadecimal string and attempts to convert it into a byte array.\n///\n/// If the hexadecimal string starts with \"0x\", these characters are ignored.\npub fn from_hex_pad<R: hex::FromHex<Error = hex::FromHexError>, T: AsRef<[u8]>>(\n    hex: T,\n) -> Result<R, hex::FromHexError> {\n    let hex = hex.as_ref();\n    let hex = if hex.starts_with(b\"0x\") {\n        &hex[2..]\n    } else if hex.starts_with(b\"X'\") {\n        &hex[2..hex.len()]\n    } else {\n        hex\n    };\n    hex::FromHex::from_hex(hex)\n}\n\n/// Returns a resolved `AlgebraicType` (containing no `AlgebraicTypeRefs`) for a given `SpacetimeType`,\n/// using the v9 moduledef infrastructure.\n/// Panics if the type is recursive.\n///\n/// TODO: we could implement something like this in `sats` itself, but would need a lightweight `TypespaceBuilder` implementation there.\npub fn resolved_type_via_v9<T: SpacetimeType>() -> AlgebraicType {\n    let mut builder = RawModuleDefV9Builder::new();\n    let ty = T::make_type(&mut builder);\n    let module = builder.finish();\n\n    WithTypespace::new(&module.typespace, &ty)\n        .resolve_refs()\n        .expect(\"recursive types not supported\")\n}\n"
  },
  {
    "path": "crates/lib/src/metrics.rs",
    "content": "/// Metrics collected during the course of a transaction.\n#[derive(Debug, Default, Copy, Clone)]\npub struct ExecutionMetrics {\n    /// How many times is an index probed?\n    ///\n    /// Note that a single btree scan may return many values,\n    /// but will only result in a single index seek.\n    pub index_seeks: usize,\n    /// How many rows are iterated over?\n    ///\n    /// It is independent of the number of rows returned.\n    /// A query for example may return a single row,\n    /// but if it scans the entire table to find that row,\n    /// this metric will reflect that.\n    pub rows_scanned: usize,\n    /// How many bytes are read?\n    ///\n    /// This metric is incremented anytime we dereference a `RowPointer`.\n    ///\n    /// For reducers this happens at the WASM boundary,\n    /// when serializing entire rows via the BSATN encoding.\n    ///\n    /// In addition to the same BSATN serialization of the output rows,\n    /// queries will dereference a `RowPointer` for column projections.\n    /// Such is the case for filters as well as index and hash joins.\n    ///\n    /// One place where this metric is not tracked is index scans.\n    /// Specifically the key comparisons that occur during the scan.\n    pub bytes_scanned: usize,\n    /// How many bytes are written?\n    ///\n    /// Note, this is the same as bytes inserted,\n    /// because deletes just update a free list in the datastore.\n    /// They don't actually write or clear page memory.\n    pub bytes_written: usize,\n    /// How many bytes did we send to clients?\n    ///\n    /// This is not necessarily the same as bytes scanned,\n    /// since a single query may send bytes to multiple clients.\n    ///\n    /// In general, these are BSATN bytes, but JSON is also possible.\n    pub bytes_sent_to_clients: usize,\n    /// How many rows were inserted?\n    pub rows_inserted: u64,\n    /// How many rows were deleted?\n    pub rows_deleted: u64,\n    /// How many rows were updated?\n    pub rows_updated: u64,\n    /// How many subscription updates did we execute?\n    pub delta_queries_evaluated: u64,\n    /// How many subscriptions had some updates?\n    pub delta_queries_matched: u64,\n    /// How many times do we evaluate the same row in a subscription update?\n    pub duplicate_rows_evaluated: u64,\n    /// How many duplicate rows do we send in a subscription update?\n    pub duplicate_rows_sent: u64,\n}\n\nimpl ExecutionMetrics {\n    pub fn merge(\n        &mut self,\n        ExecutionMetrics {\n            index_seeks,\n            rows_scanned,\n            bytes_scanned,\n            bytes_written,\n            bytes_sent_to_clients,\n            rows_inserted,\n            rows_deleted,\n            rows_updated,\n            delta_queries_evaluated,\n            delta_queries_matched,\n            duplicate_rows_evaluated,\n            duplicate_rows_sent,\n        }: ExecutionMetrics,\n    ) {\n        self.index_seeks += index_seeks;\n        self.rows_scanned += rows_scanned;\n        self.bytes_scanned += bytes_scanned;\n        self.bytes_written += bytes_written;\n        self.bytes_sent_to_clients += bytes_sent_to_clients;\n        self.rows_inserted += rows_inserted;\n        self.rows_deleted += rows_deleted;\n        self.rows_updated += rows_updated;\n        self.delta_queries_evaluated += delta_queries_evaluated;\n        self.delta_queries_matched += delta_queries_matched;\n        self.duplicate_rows_evaluated += duplicate_rows_evaluated;\n        self.duplicate_rows_sent += duplicate_rows_sent;\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::ExecutionMetrics;\n\n    #[test]\n    fn test_merge() {\n        let mut a = ExecutionMetrics::default();\n\n        a.merge(ExecutionMetrics {\n            index_seeks: 1,\n            rows_scanned: 1,\n            bytes_scanned: 1,\n            bytes_written: 1,\n            bytes_sent_to_clients: 1,\n            rows_inserted: 1,\n            rows_deleted: 1,\n            rows_updated: 1,\n            delta_queries_evaluated: 2,\n            delta_queries_matched: 3,\n            duplicate_rows_evaluated: 4,\n            duplicate_rows_sent: 2,\n        });\n\n        assert_eq!(a.index_seeks, 1);\n        assert_eq!(a.rows_scanned, 1);\n        assert_eq!(a.bytes_scanned, 1);\n        assert_eq!(a.bytes_written, 1);\n        assert_eq!(a.bytes_sent_to_clients, 1);\n        assert_eq!(a.rows_inserted, 1);\n        assert_eq!(a.rows_deleted, 1);\n        assert_eq!(a.delta_queries_evaluated, 2);\n        assert_eq!(a.delta_queries_matched, 3);\n        assert_eq!(a.duplicate_rows_evaluated, 4);\n        assert_eq!(a.duplicate_rows_sent, 2);\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/operator.rs",
    "content": "//! Operator support for the query macro.\n\nuse derive_more::From;\nuse spacetimedb_lib::de::Deserialize;\nuse spacetimedb_lib::ser::Serialize;\nuse std::fmt;\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]\npub enum OpCmp {\n    Eq,\n    NotEq,\n    Lt,\n    LtEq,\n    Gt,\n    GtEq,\n}\n\nimpl From<OpCmp> for &str {\n    fn from(x: OpCmp) -> Self {\n        match x {\n            OpCmp::Eq => \"std::cmp::eq\",\n            OpCmp::NotEq => \"std::cmp::neq\",\n            OpCmp::Lt => \"std::cmp::lt\",\n            OpCmp::LtEq => \"std::cmp::le\",\n            OpCmp::Gt => \"std::cmp::gt\",\n            OpCmp::GtEq => \"std::cmp::ge\",\n        }\n    }\n}\n\nimpl OpCmp {\n    /// Reverse the order of the `cmp`, to helps in reducing the cases on evaluation, ie:\n    pub fn reverse(self) -> Self {\n        match self {\n            OpCmp::Eq => self,\n            OpCmp::NotEq => self,\n            OpCmp::Lt => OpCmp::Gt,\n            OpCmp::LtEq => OpCmp::GtEq,\n            OpCmp::Gt => OpCmp::Lt,\n            OpCmp::GtEq => OpCmp::LtEq,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]\npub enum OpUnary {\n    Not,\n}\n\nimpl From<OpUnary> for &str {\n    fn from(x: OpUnary) -> Self {\n        match x {\n            OpUnary::Not => \"std::ops::not\",\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]\npub enum OpMath {\n    Add,\n    Minus,\n    Mul,\n    Div,\n}\n\nimpl From<OpMath> for &str {\n    fn from(x: OpMath) -> Self {\n        match x {\n            OpMath::Add => \"std::math::add\",\n            OpMath::Minus => \"std::math::minus\",\n            OpMath::Mul => \"std::math::mul\",\n            OpMath::Div => \"std::math::div\",\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]\npub enum OpLogic {\n    And,\n    Or,\n}\n\nimpl From<OpLogic> for &str {\n    fn from(x: OpLogic) -> Self {\n        match x {\n            OpLogic::And => \"std::ops::and\",\n            OpLogic::Or => \"std::ops::or\",\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, From)]\npub enum OpQuery {\n    Cmp(OpCmp),\n    Logic(OpLogic),\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, From)]\npub enum Op {\n    Cmp(OpCmp),\n    Logic(OpLogic),\n    Unary(OpUnary),\n    Math(OpMath),\n}\n\nimpl Op {\n    pub fn is_logical(&self) -> bool {\n        matches!(self, Op::Cmp(_) | Op::Logic(_))\n    }\n}\n\nimpl fmt::Display for OpCmp {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let x = match self {\n            OpCmp::Eq => \"==\",\n            OpCmp::NotEq => \"!=\",\n            OpCmp::Lt => \"<\",\n            OpCmp::LtEq => \"<=\",\n            OpCmp::Gt => \">\",\n            OpCmp::GtEq => \">=\",\n        };\n        write!(f, \"{x}\")\n    }\n}\n\nimpl fmt::Display for OpLogic {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let x = match self {\n            OpLogic::And => \"and\",\n            OpLogic::Or => \"or\",\n        };\n        write!(f, \"{x}\")\n    }\n}\n\nimpl fmt::Display for OpUnary {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let x = match self {\n            OpUnary::Not => \"not\",\n        };\n        write!(f, \"{x}\")\n    }\n}\n\nimpl fmt::Display for OpMath {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let x = match self {\n            OpMath::Add => \"+\",\n            OpMath::Minus => \"-\",\n            OpMath::Mul => \"*\",\n            OpMath::Div => \"/\",\n        };\n        write!(f, \"{x}\")\n    }\n}\n\nimpl fmt::Display for Op {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Op::Cmp(x) => {\n                write!(f, \"{x}\")\n            }\n            Op::Logic(x) => {\n                write!(f, \"{x}\")\n            }\n            Op::Unary(x) => {\n                write!(f, \"{x}\")\n            }\n            Op::Math(x) => {\n                write!(f, \"{x}\")\n            }\n        }\n    }\n}\n\nimpl fmt::Display for OpQuery {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            OpQuery::Cmp(x) => {\n                write!(f, \"{x}\")\n            }\n            OpQuery::Logic(x) => {\n                write!(f, \"{x}\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/query.rs",
    "content": "/// A type used by the query planner for incremental evaluation\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Delta {\n    Inserts,\n    Deletes,\n}\n"
  },
  {
    "path": "crates/lib/src/scheduler.rs",
    "content": "use std::fmt::Debug;\n\nuse spacetimedb_lib::{TimeDuration, Timestamp};\nuse spacetimedb_sats::{\n    algebraic_value::de::{ValueDeserializeError, ValueDeserializer},\n    de::Deserialize,\n    impl_st,\n    ser::Serialize,\n    sum_type::{SCHEDULE_AT_INTERVAL_TAG, SCHEDULE_AT_TIME_TAG},\n    AlgebraicType, AlgebraicValue,\n};\n\n/// When a scheduled reducer should execute,\n/// either at a specific point in time,\n/// or at regular intervals for repeating schedules.\n///\n/// Stored in reducer-scheduling tables as a column.\n///\n/// This is a special type.\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]\npub enum ScheduleAt {\n    /// A regular interval at which the repeated reducer is scheduled.\n    /// Value is a [`TimeDuration`], which has nanosecond precision.\n    Interval(TimeDuration),\n    /// A specific time to which the reducer is scheduled.\n    Time(Timestamp),\n}\nimpl_st!([] ScheduleAt, ScheduleAt::get_type());\n\nimpl ScheduleAt {\n    /// Converts the `ScheduleAt` to a `std::time::Duration` from now.\n    ///\n    /// Returns [`std::time::Duration::ZERO`] if `self` represents a time in the past.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub fn to_duration_from(&self, from: Timestamp) -> std::time::Duration {\n        use std::time::Duration;\n        match self {\n            ScheduleAt::Time(time) => time.duration_since(from).unwrap_or(Duration::ZERO),\n            // TODO(correctness): Determine useful behavior on negative intervals,\n            // as that's the case where `to_duration` fails.\n            // Currently, we use the magnitude / absolute value,\n            // which seems at least less stupid than clamping to zero.\n            ScheduleAt::Interval(dur) => dur.to_duration_abs(),\n        }\n    }\n\n    /// Converts the `ScheduleAt` to a `Timestamp`.\n    ///\n    /// If `self` is an interval, returns `from + dur`.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub fn to_timestamp_from(&self, from: Timestamp) -> Timestamp {\n        match *self {\n            ScheduleAt::Time(time) => time,\n            ScheduleAt::Interval(dur) => from + dur.abs(),\n        }\n    }\n\n    /// Get the special `AlgebraicType` for `ScheduleAt`.\n    pub fn get_type() -> AlgebraicType {\n        AlgebraicType::sum([\n            (SCHEDULE_AT_INTERVAL_TAG, AlgebraicType::time_duration()),\n            (SCHEDULE_AT_TIME_TAG, AlgebraicType::timestamp()),\n        ])\n    }\n}\n\nimpl From<TimeDuration> for ScheduleAt {\n    fn from(value: TimeDuration) -> Self {\n        ScheduleAt::Interval(value)\n    }\n}\n\nimpl From<std::time::Duration> for ScheduleAt {\n    fn from(value: std::time::Duration) -> Self {\n        ScheduleAt::Interval(TimeDuration::from_duration(value))\n    }\n}\n\nimpl From<std::time::SystemTime> for ScheduleAt {\n    fn from(value: std::time::SystemTime) -> Self {\n        Timestamp::from(value).into()\n    }\n}\n\nimpl From<crate::Timestamp> for ScheduleAt {\n    fn from(value: crate::Timestamp) -> Self {\n        ScheduleAt::Time(value)\n    }\n}\n\nimpl TryFrom<AlgebraicValue> for ScheduleAt {\n    type Error = ValueDeserializeError;\n    fn try_from(value: AlgebraicValue) -> Result<Self, Self::Error> {\n        ScheduleAt::deserialize(ValueDeserializer::new(value))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_sats::bsatn;\n\n    #[test]\n    fn test_bsatn_roundtrip() {\n        let schedule_at = ScheduleAt::Interval(TimeDuration::from_micros(10000));\n        let ser = bsatn::to_vec(&schedule_at).unwrap();\n        let de = bsatn::from_slice(&ser).unwrap();\n        assert_eq!(schedule_at, de);\n    }\n\n    #[test]\n    fn schedule_at_is_special() {\n        assert!(ScheduleAt::get_type().is_special());\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/st_var.rs",
    "content": "use derive_more::From;\nuse spacetimedb_sats::{AlgebraicValue, SpacetimeType, SumValue};\n\n/// The value of a system variable in `st_var`.\n/// Defined here because it is used in both the datastore and query.\n#[derive(Debug, Clone, From, SpacetimeType)]\n#[sats(crate = spacetimedb_lib)]\npub enum StVarValue {\n    Bool(bool),\n    I8(i8),\n    U8(u8),\n    I16(i16),\n    U16(u16),\n    I32(i32),\n    U32(u32),\n    I64(i64),\n    U64(u64),\n    I128(i128),\n    U128(u128),\n    // No support for u/i256 added here as it seems unlikely to be useful.\n    F32(f32),\n    F64(f64),\n    String(Box<str>),\n}\n\nimpl StVarValue {\n    pub fn try_from_primitive(value: AlgebraicValue) -> Result<Self, AlgebraicValue> {\n        match value {\n            AlgebraicValue::Bool(v) => Ok(StVarValue::Bool(v)),\n            AlgebraicValue::I8(v) => Ok(StVarValue::I8(v)),\n            AlgebraicValue::U8(v) => Ok(StVarValue::U8(v)),\n            AlgebraicValue::I16(v) => Ok(StVarValue::I16(v)),\n            AlgebraicValue::U16(v) => Ok(StVarValue::U16(v)),\n            AlgebraicValue::I32(v) => Ok(StVarValue::I32(v)),\n            AlgebraicValue::U32(v) => Ok(StVarValue::U32(v)),\n            AlgebraicValue::I64(v) => Ok(StVarValue::I64(v)),\n            AlgebraicValue::U64(v) => Ok(StVarValue::U64(v)),\n            AlgebraicValue::I128(v) => Ok(StVarValue::I128(v.0)),\n            AlgebraicValue::U128(v) => Ok(StVarValue::U128(v.0)),\n            AlgebraicValue::F32(v) => Ok(StVarValue::F32(v.into_inner())),\n            AlgebraicValue::F64(v) => Ok(StVarValue::F64(v.into_inner())),\n            AlgebraicValue::String(v) => Ok(StVarValue::String(v)),\n            _ => Err(value),\n        }\n    }\n\n    pub fn try_from_sum(value: AlgebraicValue) -> Result<Self, AlgebraicValue> {\n        value.into_sum()?.try_into()\n    }\n}\n\nimpl TryFrom<SumValue> for StVarValue {\n    type Error = AlgebraicValue;\n\n    fn try_from(sum: SumValue) -> Result<Self, Self::Error> {\n        match sum.tag {\n            0 => Ok(StVarValue::Bool(sum.value.into_bool()?)),\n            1 => Ok(StVarValue::I8(sum.value.into_i8()?)),\n            2 => Ok(StVarValue::U8(sum.value.into_u8()?)),\n            3 => Ok(StVarValue::I16(sum.value.into_i16()?)),\n            4 => Ok(StVarValue::U16(sum.value.into_u16()?)),\n            5 => Ok(StVarValue::I32(sum.value.into_i32()?)),\n            6 => Ok(StVarValue::U32(sum.value.into_u32()?)),\n            7 => Ok(StVarValue::I64(sum.value.into_i64()?)),\n            8 => Ok(StVarValue::U64(sum.value.into_u64()?)),\n            9 => Ok(StVarValue::I128(sum.value.into_i128()?.0)),\n            10 => Ok(StVarValue::U128(sum.value.into_u128()?.0)),\n            11 => Ok(StVarValue::F32(sum.value.into_f32()?.into_inner())),\n            12 => Ok(StVarValue::F64(sum.value.into_f64()?.into_inner())),\n            13 => Ok(StVarValue::String(sum.value.into_string()?)),\n            _ => Err(*sum.value),\n        }\n    }\n}\n\nimpl From<StVarValue> for AlgebraicValue {\n    fn from(value: StVarValue) -> Self {\n        AlgebraicValue::Sum(value.into())\n    }\n}\n\nimpl From<StVarValue> for SumValue {\n    fn from(value: StVarValue) -> Self {\n        match value {\n            StVarValue::Bool(v) => SumValue::new(0, v),\n            StVarValue::I8(v) => SumValue::new(1, v),\n            StVarValue::U8(v) => SumValue::new(2, v),\n            StVarValue::I16(v) => SumValue::new(3, v),\n            StVarValue::U16(v) => SumValue::new(4, v),\n            StVarValue::I32(v) => SumValue::new(5, v),\n            StVarValue::U32(v) => SumValue::new(6, v),\n            StVarValue::I64(v) => SumValue::new(7, v),\n            StVarValue::U64(v) => SumValue::new(8, v),\n            StVarValue::I128(v) => SumValue::new(9, v),\n            StVarValue::U128(v) => SumValue::new(10, v),\n            StVarValue::F32(v) => SumValue::new(11, v),\n            StVarValue::F64(v) => SumValue::new(12, v),\n            StVarValue::String(v) => SumValue::new(13, v),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/lib/src/version.rs",
    "content": "const CLI_VERSION: &str = env!(\"CARGO_PKG_VERSION\");\npub const GIT_HASH: &str = env!(\"GIT_HASH\");\n\npub fn spacetimedb_lib_version() -> &'static str {\n    CLI_VERSION\n}\n"
  },
  {
    "path": "crates/lib/tests/serde.rs",
    "content": "use spacetimedb_lib::de::serde::SerdeDeserializer;\nuse spacetimedb_lib::de::DeserializeSeed;\nuse spacetimedb_lib::{AlgebraicType, Identity, ProductType, ProductTypeElement, ProductValue, SumType};\nuse spacetimedb_sats::algebraic_value::de::ValueDeserializer;\nuse spacetimedb_sats::algebraic_value::ser::value_serialize;\nuse spacetimedb_sats::{satn::Satn, GroundSpacetimeType as _, SumTypeVariant, Typespace, WithTypespace};\n\nmacro_rules! de_json_snapshot {\n    ($schema:expr, $json:expr) => {\n        let (schema, json) = (&$schema, &$json);\n        let value = de_json(schema, json).unwrap();\n        let value = WithTypespace::new(&EMPTY_TYPESPACE, schema)\n            .with_value(&value)\n            .to_satn_pretty();\n        let debug_expr = format!(\"de_json({})\", json.trim());\n        insta::assert_snapshot!(insta::internals::AutoName, value, &debug_expr);\n    };\n}\n\n#[derive(\n    Debug,\n    PartialEq,\n    spacetimedb_sats::de::Deserialize,\n    spacetimedb_sats::ser::Serialize,\n    serde::Serialize,\n    serde::Deserialize,\n)]\nstruct Sample {\n    identity: Identity,\n}\n\n#[test]\nfn test_roundtrip() {\n    let original = Sample {\n        identity: Identity::__dummy(),\n    };\n\n    let s = value_serialize(&original);\n    let result: Sample = spacetimedb_sats::de::Deserialize::deserialize(ValueDeserializer::new(s)).unwrap();\n    assert_eq!(&original, &result);\n\n    let s = serde_json::ser::to_string(&original).unwrap();\n    let result: Sample = serde_json::from_str(&s).unwrap();\n    assert_eq!(&original, &result);\n}\n\n#[test]\nfn test_roundtrip_ron() {\n    let original = Sample {\n        identity: Identity::__dummy(),\n    };\n\n    let s = value_serialize(&original);\n    let result: Sample = spacetimedb_sats::de::Deserialize::deserialize(ValueDeserializer::new(s)).unwrap();\n    assert_eq!(&original, &result);\n\n    let s = ron::to_string(&original).unwrap();\n    let result: Sample = ron::from_str(&s).unwrap();\n    assert_eq!(&original, &result);\n}\n\n#[test]\nfn test_json_mappings() {\n    let schema = tuple([\n        (\"foo\", AlgebraicType::U32),\n        (\"bar\", AlgebraicType::bytes()),\n        (\"baz\", AlgebraicType::array(AlgebraicType::String)),\n        (\n            \"quux\",\n            enumm([(\"Hash\", AlgebraicType::bytes()), (\"Unit\", AlgebraicType::unit())]).into(),\n        ),\n        (\"and_peggy\", AlgebraicType::option(AlgebraicType::F64)),\n        (\n            \"result\",\n            AlgebraicType::result(AlgebraicType::U32, AlgebraicType::String),\n        ),\n        (\"identity\", Identity::get_type()),\n    ]);\n\n    let data = r#\"\n{\n    \"foo\": 42,\n    \"bar\": \"404040FFFF0A48656C6C6F\",\n    \"baz\": [\"heyyyyyy\", \"hooo\"],\n    \"quux\": { \"Hash\": \"54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa\" },\n    \"and_peggy\": { \"some\": 3.141592653589793238426 },\n    \"result\": { \"ok\": 1 },\n    \"identity\": [\"0x0\"]\n}\n\"#; // all of those ^^^^^^ digits are from memory\n    de_json_snapshot!(schema, data);\n\n    let data = r#\"\n{\n    \"foo\": 5654,\n    \"bar\": [1, 15, 44],\n    \"baz\": [\"it's 🥶°C\"],\n    \"quux\": { \"Unit\": [] },\n    \"and_peggy\": null,\n    \"result\": { \"err\": \"sorry\" },\n    \"identity\": [\"0x0\"]\n}\n\"#;\n    de_json_snapshot!(schema, data);\n}\n\nfn tuple(elems: impl IntoIterator<Item = (&'static str, AlgebraicType)>) -> ProductType {\n    ProductType {\n        elements: elems\n            .into_iter()\n            .map(|(name, ty)| ProductTypeElement::new_named(ty, name))\n            .collect(),\n    }\n}\nfn enumm(elems: impl IntoIterator<Item = (&'static str, AlgebraicType)>) -> SumType {\n    SumType {\n        variants: elems\n            .into_iter()\n            .map(|(name, ty)| SumTypeVariant::new_named(ty, name))\n            .collect(),\n    }\n}\n\nstatic EMPTY_TYPESPACE: Typespace = Typespace::new(Vec::new());\n\nfn in_space<T>(x: &T) -> WithTypespace<'_, T> {\n    WithTypespace::new(&EMPTY_TYPESPACE, x)\n}\n\nfn de_json(schema: &ProductType, data: &str) -> serde_json::Result<ProductValue> {\n    let mut de = serde_json::Deserializer::from_str(data);\n    let val = in_space(schema)\n        .deserialize(SerdeDeserializer::new(&mut de))\n        .map_err(|e| e.0)?;\n    de.end()?;\n    Ok(val)\n}\n"
  },
  {
    "path": "crates/lib/tests/snapshots/serde__json_mappings-2.snap",
    "content": "---\nsource: crates/lib/tests/serde.rs\nexpression: \"de_json({\\n    \\\"foo\\\": 5654,\\n    \\\"bar\\\": [1, 15, 44],\\n    \\\"baz\\\": [\\\"it's 🥶°C\\\"],\\n    \\\"quux\\\": { \\\"Unit\\\": [] },\\n    \\\"and_peggy\\\": null,\\n    \\\"result\\\": { \\\"err\\\": \\\"sorry\\\" },\\n    \\\"identity\\\": [\\\"0x0\\\"]\\n})\"\n---\n(\n    foo = 5654,\n    bar = 0x010f2c,\n    baz = [\n        \"it's 🥶°C\",\n    ],\n    quux = (\n        Unit = (),\n    ),\n    and_peggy = (\n        none = (),\n    ),\n    result = (\n        err = \"sorry\",\n    ),\n    identity = (\n        __identity__ = 0,\n    ),\n)\n"
  },
  {
    "path": "crates/lib/tests/snapshots/serde__json_mappings.snap",
    "content": "---\nsource: crates/lib/tests/serde.rs\nexpression: \"de_json({\\n    \\\"foo\\\": 42,\\n    \\\"bar\\\": \\\"404040FFFF0A48656C6C6F\\\",\\n    \\\"baz\\\": [\\\"heyyyyyy\\\", \\\"hooo\\\"],\\n    \\\"quux\\\": { \\\"Hash\\\": \\\"54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa\\\" },\\n    \\\"and_peggy\\\": { \\\"some\\\": 3.141592653589793238426 },\\n    \\\"result\\\": { \\\"ok\\\": 1 },\\n    \\\"identity\\\": [\\\"0x0\\\"]\\n})\"\n---\n(\n    foo = 42,\n    bar = 0x404040ffff0a48656c6c6f,\n    baz = [\n        \"heyyyyyy\",\n        \"hooo\",\n    ],\n    quux = (\n        Hash = 0x54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa,\n    ),\n    and_peggy = (\n        some = 3.141592653589793,\n    ),\n    result = (\n        ok = 1,\n    ),\n    identity = (\n        __identity__ = 0,\n    ),\n)\n"
  },
  {
    "path": "crates/memory-usage/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-memory-usage\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Provides the trait `MemoryUsage`\"\n\n[features]\nhash_map = [\"dep:hashbrown\"]\nsmallvec = [\"dep:smallvec\"]\nethnum = [\"dep:ethnum\"]\ndecorum = [\"dep:decorum\"]\ndefault = [\"hash_map\", \"smallvec\", \"ethnum\", \"decorum\"]\n\n[dependencies]\nhashbrown = { workspace = true, optional = true }\nsmallvec = { workspace = true, optional = true }\nethnum = { workspace = true, optional = true }\ndecorum = { workspace = true, optional = true }\n"
  },
  {
    "path": "crates/memory-usage/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/memory-usage/src/lib.rs",
    "content": "use core::mem;\nuse core::sync::atomic::AtomicUsize;\n\n/// For inspecting how much memory a value is using.\n///\n/// This trait specifically measures heap memory. If you want to measure stack memory too, add\n/// `mem::size_of_val()` to it. (This only really matters for the outermost type in a hierarchy.)\npub trait MemoryUsage {\n    /// The **heap** memory usage of this type. The default implementation returns 0.\n    #[inline(always)]\n    fn heap_usage(&self) -> usize {\n        0\n    }\n}\n\nimpl MemoryUsage for () {}\nimpl MemoryUsage for bool {}\nimpl MemoryUsage for u8 {}\nimpl MemoryUsage for u16 {}\nimpl MemoryUsage for u32 {}\nimpl MemoryUsage for u64 {}\nimpl MemoryUsage for u128 {}\n#[cfg(feature = \"ethnum\")]\nimpl MemoryUsage for ethnum::u256 {}\nimpl MemoryUsage for usize {}\nimpl MemoryUsage for AtomicUsize {}\nimpl MemoryUsage for i8 {}\nimpl MemoryUsage for i16 {}\nimpl MemoryUsage for i32 {}\nimpl MemoryUsage for i64 {}\nimpl MemoryUsage for i128 {}\n#[cfg(feature = \"ethnum\")]\nimpl MemoryUsage for ethnum::i256 {}\nimpl MemoryUsage for isize {}\nimpl MemoryUsage for f32 {}\nimpl MemoryUsage for f64 {}\n#[cfg(feature = \"decorum\")]\nimpl MemoryUsage for decorum::Total<f32> {}\n#[cfg(feature = \"decorum\")]\nimpl MemoryUsage for decorum::Total<f64> {}\n\nimpl<T: MemoryUsage + ?Sized> MemoryUsage for &T {\n    fn heap_usage(&self) -> usize {\n        (*self).heap_usage()\n    }\n}\n\nimpl<T: MemoryUsage + ?Sized> MemoryUsage for Box<T> {\n    fn heap_usage(&self) -> usize {\n        mem::size_of_val::<T>(self) + T::heap_usage(self)\n    }\n}\n\nimpl<T: MemoryUsage + ?Sized> MemoryUsage for std::sync::Arc<T> {\n    fn heap_usage(&self) -> usize {\n        let refcounts = mem::size_of::<usize>() * 2;\n        refcounts + mem::size_of_val::<T>(self) + T::heap_usage(self)\n    }\n}\n\nimpl<T: MemoryUsage + ?Sized> MemoryUsage for std::rc::Rc<T> {\n    fn heap_usage(&self) -> usize {\n        let refcounts = mem::size_of::<usize>() * 2;\n        refcounts + mem::size_of_val::<T>(self) + T::heap_usage(self)\n    }\n}\n\nimpl<T: MemoryUsage> MemoryUsage for [T] {\n    fn heap_usage(&self) -> usize {\n        self.iter().map(T::heap_usage).sum()\n    }\n}\n\nimpl<T: MemoryUsage, const N: usize> MemoryUsage for [T; N] {\n    fn heap_usage(&self) -> usize {\n        self.iter().map(T::heap_usage).sum()\n    }\n}\n\nimpl MemoryUsage for str {}\n\nimpl<T: MemoryUsage> MemoryUsage for Option<T> {\n    fn heap_usage(&self) -> usize {\n        self.as_ref().map_or(0, T::heap_usage)\n    }\n}\n\nimpl<A: MemoryUsage, B: MemoryUsage> MemoryUsage for (A, B) {\n    fn heap_usage(&self) -> usize {\n        self.0.heap_usage() + self.1.heap_usage()\n    }\n}\n\nimpl MemoryUsage for String {\n    fn heap_usage(&self) -> usize {\n        self.capacity()\n    }\n}\n\nimpl<T: MemoryUsage> MemoryUsage for Vec<T> {\n    fn heap_usage(&self) -> usize {\n        self.capacity() * mem::size_of::<T>() + self.iter().map(T::heap_usage).sum::<usize>()\n    }\n}\n\n#[cfg(feature = \"hash_map\")]\nimpl<K: MemoryUsage + Eq + core::hash::Hash, V: MemoryUsage, S: core::hash::BuildHasher> MemoryUsage\n    for hashbrown::HashMap<K, V, S>\n{\n    fn heap_usage(&self) -> usize {\n        self.allocation_size() + self.iter().map(|(k, v)| k.heap_usage() + v.heap_usage()).sum::<usize>()\n    }\n}\n\n#[cfg(feature = \"hash_map\")]\nimpl<K: MemoryUsage + Eq + core::hash::Hash, S: core::hash::BuildHasher> MemoryUsage for hashbrown::HashSet<K, S> {\n    fn heap_usage(&self) -> usize {\n        self.allocation_size() + self.iter().map(|k| k.heap_usage()).sum::<usize>()\n    }\n}\n\nimpl<K: MemoryUsage, V: MemoryUsage> MemoryUsage for std::collections::BTreeMap<K, V> {\n    fn heap_usage(&self) -> usize {\n        // NB: this is best-effort, since we don't have a `capacity()` method on `BTreeMap`.\n        self.len() * mem::size_of::<(K, V)>() + self.iter().map(|(k, v)| k.heap_usage() + v.heap_usage()).sum::<usize>()\n    }\n}\n\n#[cfg(feature = \"smallvec\")]\nimpl<A: smallvec::Array> MemoryUsage for smallvec::SmallVec<A>\nwhere\n    A::Item: MemoryUsage,\n{\n    fn heap_usage(&self) -> usize {\n        self.as_slice().heap_usage()\n            + if self.spilled() {\n                self.capacity() * mem::size_of::<A::Item>()\n            } else {\n                0\n            }\n    }\n}\n"
  },
  {
    "path": "crates/metrics/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-metrics\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Prometheus utilities for SpacetimeDB\"\nrust-version.workspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nitertools.workspace = true\npaste.workspace = true\nprometheus.workspace = true\narrayvec.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/metrics/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/metrics/src/lib.rs",
    "content": "pub mod typed_prometheus;\n"
  },
  {
    "path": "crates/metrics/src/typed_prometheus.rs",
    "content": "use prometheus::core::{Metric, MetricVec, MetricVecBuilder};\n\n#[macro_export]\nmacro_rules! metrics_group {\n    ($(#[$attr:meta])* $type_vis:vis struct $type_name:ident {\n        $(#[name = $name:ident] #[help = $help:expr] $(#[labels($($labels:ident: $labelty:ty),*)])? $(#[buckets($($bucket:literal),*)])? $vis:vis $field:ident: $ty:ident,)*\n    }) => {\n        $(#[$attr])*\n        $type_vis struct $type_name {\n            $($vis $field: $crate::metrics_group!(@fieldtype $field $ty $(($($labels)*))?),)*\n        }\n        $($crate::metrics_group!(@maketype $vis $field $ty $(($($labels: $labelty),*))? $(($($bucket)*))?);)*\n        impl $type_name {\n            #[allow(clippy::new_without_default)]\n            pub fn new() -> Self {\n                Self {\n                    $($field: $crate::make_collector!($crate::metrics_group!(@fieldtype $field $ty $(($($labels)*))?), stringify!($name), $help),)*\n                }\n            }\n        }\n\n        impl prometheus::core::Collector for $type_name {\n            fn desc(&self) -> Vec<&prometheus::core::Desc> {\n                $crate::typed_prometheus::itertools::concat([ $(prometheus::core::Collector::desc(&self.$field)),* ])\n            }\n\n            fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {\n                $crate::typed_prometheus::itertools::concat([ $(prometheus::core::Collector::collect(&self.$field)),* ])\n            }\n        }\n        impl prometheus::core::Collector for &$type_name {\n            fn desc(&self) -> Vec<&prometheus::core::Desc> {\n                (**self).desc()\n            }\n\n            fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {\n                (**self).collect()\n            }\n        }\n    };\n    (@fieldtype $field:ident $ty:ident ($($labels:tt)*)) => { $crate::typed_prometheus::paste! { [< $field:camel $ty >] } };\n    (@fieldtype $field:ident $ty:ident) => { $ty };\n    (@maketype $vis:vis $field:ident $ty:ident ($($labels:tt)*)) => {\n        $crate::typed_prometheus::paste! {\n            $crate::metrics_vec!($vis [< $field:camel $ty >]: $ty($($labels)*));\n        }\n    };\n    (@maketype $vis:vis $field:ident $ty:ident ($($labels:tt)*) ($($bucket:literal)*)) => {\n        $crate::typed_prometheus::paste! {\n            $crate::metrics_histogram_vec!($vis [< $field:camel $ty >]: $ty($($labels)*) ($($bucket)*));\n        }\n    };\n    (@maketype $vis:vis $field:ident $ty:ident) => {};\n}\npub use metrics_group;\n#[doc(hidden)]\npub use {itertools, paste::paste};\n\n#[macro_export]\nmacro_rules! make_collector {\n    ($ty:ty, $name:expr, $help:expr $(,)?) => {\n        <$ty>::with_opts(prometheus::Opts::new($name, $help).into()).unwrap()\n    };\n    ($ty:ty, $name:expr, $help:expr, $labels:expr $(,)?) => {\n        <$ty>::new(prometheus::Opts::new($name, $help).into(), $labels).unwrap()\n    };\n}\npub use make_collector;\n\n#[macro_export]\nmacro_rules! metrics_histogram_vec {\n    ($vis:vis $name:ident: $vecty:ident($($labels:ident: $labelty:ty),+ $(,)?) ($($bucket:literal)*)) => {\n        #[derive(Clone)]\n        $vis struct $name($vecty);\n        impl $name {\n            pub fn with_opts(opts: prometheus::Opts) -> prometheus::Result<Self> {\n                let opts = prometheus::HistogramOpts::from(opts).buckets(vec![$(f64::from($bucket)),*]);\n                $vecty::new(opts.into(), &[$(stringify!($labels)),+]).map(Self)\n            }\n\n            pub fn with_label_values(&self, $($labels: &$labelty),+) -> <$vecty as $crate::typed_prometheus::ExtractMetricVecT>::M {\n                use $crate::typed_prometheus::AsPrometheusLabel as _;\n                self.0.with_label_values(&[ $($labels.as_prometheus_str().as_ref()),+ ])\n            }\n\n            pub fn remove_label_values(&self, $($labels: &$labelty),+) -> prometheus::Result<()> {\n                use $crate::typed_prometheus::AsPrometheusLabel as _;\n                self.0.remove_label_values(&[ $($labels.as_prometheus_str().as_ref()),+ ])\n            }\n        }\n\n        impl prometheus::core::Collector for $name {\n            fn desc(&self) -> Vec<&prometheus::core::Desc> {\n                prometheus::core::Collector::desc(&self.0)\n            }\n\n            fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {\n                prometheus::core::Collector::collect(&self.0)\n            }\n        }\n    };\n}\npub use metrics_histogram_vec;\n\n#[macro_export]\nmacro_rules! metrics_vec {\n    ($vis:vis $name:ident: $vecty:ident($($labels:ident: $labelty:ty),+ $(,)?)) => {\n        #[derive(Clone)]\n        $vis struct $name($vecty);\n        impl $name {\n            pub fn with_opts(opts: prometheus::Opts) -> prometheus::Result<Self> {\n                $vecty::new(opts.into(), &[$(stringify!($labels)),+]).map(Self)\n            }\n\n            pub fn with_label_values(&self, $($labels: &$labelty),+) -> <$vecty as $crate::typed_prometheus::ExtractMetricVecT>::M {\n                use $crate::typed_prometheus::AsPrometheusLabel as _;\n                self.0.with_label_values(&[ $($labels.as_prometheus_str().as_ref()),+ ])\n            }\n            pub fn remove_label_values(&self, $($labels: &$labelty),+) -> prometheus::Result<()> {\n                use $crate::typed_prometheus::AsPrometheusLabel as _;\n                self.0.remove_label_values(&[ $($labels.as_prometheus_str().as_ref()),+ ])\n            }\n        }\n\n        impl prometheus::core::Collector for $name {\n            fn desc(&self) -> Vec<&prometheus::core::Desc> {\n                prometheus::core::Collector::desc(&self.0)\n            }\n\n            fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {\n                prometheus::core::Collector::collect(&self.0)\n            }\n        }\n    };\n}\npub use metrics_vec;\n\npub trait AsPrometheusLabel {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_;\n}\nimpl<T: AsRef<str> + ?Sized> AsPrometheusLabel for &T {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n        self\n    }\n}\n\nimpl AsPrometheusLabel for bool {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n        match *self {\n            true => \"true\",\n            false => \"false\",\n        }\n    }\n}\n\nmacro_rules! impl_prometheusvalue_itoa {\n    ($($ty:ident),*) => {\n        $(impl $crate::typed_prometheus::AsPrometheusLabel for $ty {\n            fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n                use std::fmt::Write;\n                // max # of chars = log10 of MAX, rounded up (std ilog10 rounds down),\n                // + 1 if signed (for the `-`)\n                const CAP: usize = ($ty::MAX.ilog10() as usize + 1) + ($ty::MIN != 0) as usize;\n                let mut buf = arrayvec::ArrayString::<CAP>::new();\n                write!(buf, \"{self}\").unwrap();\n                buf\n            }\n        })*\n    }\n}\n\nimpl_prometheusvalue_itoa!(u8, u16, u32, u64, i8, i16, i32, i64);\n\n#[doc(hidden)]\npub trait ExtractMetricVecT {\n    type M: Metric;\n}\n\nimpl<T: MetricVecBuilder> ExtractMetricVecT for MetricVec<T> {\n    type M = T::M;\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_arraystring_fmt() {\n        macro_rules! tst {\n            ($($ty:ident),*) => {\n                $(\n                    assert_eq!($ty::MIN.as_prometheus_str().as_ref(), $ty::MIN.to_string().as_str());\n                    assert_eq!($ty::as_prometheus_str(&0).as_ref(), $ty::to_string(&0).as_str());\n                    assert_eq!($ty::MAX.as_prometheus_str().as_ref(), $ty::MAX.to_string().as_str());\n                )*\n            }\n        }\n        tst!(u8, u16, u32, u64, i8, i16, i32, i64);\n    }\n}\n"
  },
  {
    "path": "crates/paths/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-paths\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The spacetimedb directory structure, represented as a type hierarchy\"\n\n[dependencies]\nanyhow.workspace = true\nchrono = { workspace = true, features = [\"now\"] }\nfs2.workspace = true\nitoa.workspace = true\nserde.workspace = true\nthiserror.workspace = true\n\n[target.'cfg(windows)'.dependencies]\ndirs.workspace = true\njunction.workspace = true\n\n[target.'cfg(not(windows))'.dependencies]\nxdg.workspace = true\n\n[dev-dependencies]\ntempfile.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/paths/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/paths/src/cli.rs",
    "content": "use std::path::Path;\n\nuse anyhow::Context;\n\nuse crate::utils::{path_type, PathBufExt};\n\npath_type! {\n    /// The configuration directory for the CLI & keyfiles.\n    ConfigDir: dir\n}\n\nimpl ConfigDir {\n    pub fn jwt_priv_key(&self) -> PrivKeyPath {\n        PrivKeyPath(self.0.join(\"id_ecdsa\"))\n    }\n    pub fn jwt_pub_key(&self) -> PubKeyPath {\n        PubKeyPath(self.0.join(\"id_ecdsa.pub\"))\n    }\n    pub fn cli_toml(&self) -> CliTomlPath {\n        CliTomlPath(self.0.join(\"cli.toml\"))\n    }\n}\n\n// TODO: replace cfg(any()) with cfg(false) once stabilized\npath_type!(#[non_exhaustive(any())] PrivKeyPath: file);\npath_type!(#[non_exhaustive(any())] PubKeyPath: file);\n\npath_type!(CliTomlPath: file);\n\npath_type!(BinFile: file);\n\npath_type!(BinDir: dir);\n\nimpl BinDir {\n    pub fn version_dir(&self, version: &str) -> VersionBinDir {\n        VersionBinDir(self.0.join(version))\n    }\n\n    pub const CURRENT_VERSION_DIR_NAME: &str = \"current\";\n    pub fn current_version_dir(&self) -> VersionBinDir {\n        VersionBinDir(self.0.join(Self::CURRENT_VERSION_DIR_NAME))\n    }\n\n    pub fn set_current_version(&self, version: &str) -> anyhow::Result<()> {\n        self.current_version_dir().link_to(self.version_dir(version).as_ref())\n    }\n\n    pub fn current_version(&self) -> anyhow::Result<Option<String>> {\n        match std::fs::read_link(self.current_version_dir()) {\n            Ok(path) => path.into_os_string().into_string().ok().context(\"not utf8\").map(Some),\n            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),\n            Err(e) => Err(e.into()),\n        }\n    }\n\n    pub fn installed_versions(&self) -> anyhow::Result<Vec<String>> {\n        self.read_dir()?\n            .filter_map(|r| match r {\n                Ok(entry) => {\n                    let name = entry.file_name();\n                    if name == Self::CURRENT_VERSION_DIR_NAME {\n                        None\n                    } else {\n                        entry.file_name().into_string().ok().map(Ok)\n                    }\n                }\n                Err(e) => Some(Err(e.into())),\n            })\n            .collect()\n    }\n}\n\npath_type!(VersionBinDir: dir);\n\nimpl VersionBinDir {\n    pub fn spacetimedb_cli(self) -> SpacetimedbCliBin {\n        SpacetimedbCliBin(self.0.joined(\"spacetimedb-cli\").with_exe_ext())\n    }\n\n    pub fn create_custom(&self, path: &Path) -> anyhow::Result<()> {\n        if std::fs::symlink_metadata(self).is_ok_and(|m| m.file_type().is_dir()) {\n            anyhow::bail!(\"version already exists\");\n        }\n        self.link_to(path)\n    }\n\n    fn link_to(&self, path: &Path) -> anyhow::Result<()> {\n        let rel_path = path.strip_prefix(self.0.parent().unwrap()).unwrap_or(path);\n        #[cfg(unix)]\n        {\n            // remove the link if it already exists\n            std::fs::remove_file(self).ok();\n            std::os::unix::fs::symlink(rel_path, self)?;\n        }\n        #[cfg(windows)]\n        {\n            junction::delete(self).ok();\n            std::fs::remove_dir(self).ok();\n            // We won't be able to create a junction if the fs isn't NTFS, so fall back to trying\n            // to make a symlink.\n            junction::create(path, self)\n                .or_else(|err| std::os::windows::fs::symlink_dir(rel_path, self).or(Err(err)))?;\n        }\n        Ok(())\n    }\n}\n\npath_type!(SpacetimedbCliBin: file);\n"
  },
  {
    "path": "crates/paths/src/lib.rs",
    "content": "//! The spacetimedb directory structure, represented as a type hierarchy.\n//!\n//! # Directory Structure of the Database.\n//!\n//! [`SpacetimePaths`] holds the paths to the various directories used by the CLI & database.\n//!\n//! * **cli-bin-dir**: a directory under which all versions of all\n//!   SpacetimeDB binaries is be stored. Each binary is stored in a\n//!   directory named with version number of the binary in this directory. If a\n//!   binary has any related files required by that binary which are specific to\n//!   that version, for example, template configuration files, these files will be\n//!   installed in this folder as well.\n//!\n//! * **cli-config-dir**: a directory where configuration and state for the CLI,\n//!   as well as the keyfiles used by the server, are stored.\n//!\n//! * **cli-bin-file**: the location of the default spacetime CLI executable, which\n//!   is a symlink to the actual `spacetime` binary in the cli-bin-dir.\n//!\n//! * **data-dir**: the directory where all persistent server & database files\n//!   are stored.\n//!\n//! ## Unix Directory Structure\n//!\n//! On Unix-like platforms, such as Linux and macOS, the installation paths follow the\n//! XDG conventions by default:\n//!\n//! * `cli-config-dir`: `$XDG_CONFIG_HOME/spacetime/`\n//! * `cli-bin-dir`: `$XDG_DATA_HOME/spacetime/bin/`\n//! * `cli-bin-file`: `$XDG_BIN_HOME/spacetime`\n//! * `data-dir`: `$XDG_DATA_HOME/spacetime/data`\n//!\n//! As per the XDG base directory specification, those base directories fall back to\n//! to the following defaults if the corresponding environment variable is not set:\n//!\n//! * `$XDG_CONFIG_HOME`: `$HOME/.config`\n//! * `$XDG_DATA_HOME`: `$HOME/.local/share`\n//! * `$XDG_BIN_HOME`: `$HOME/.local/bin`\n//!\n//! For reference, the below is an example installation using the default paths:\n//!\n//!```sh\n//! $HOME\n//! ├── .local\n//! │   ├── bin\n//! │   │   └── spacetime -> $HOME/.local/share/spacetime/bin/1.10.1/spacetimedb-update # Current, in $PATH\n//! │   └── share\n//! │       └── spacetime\n//! │           ├── bin\n//! │           |   └── 1.10.1\n//! │           |       ├── spacetimedb-update # Version manager\n//! │           |       ├── spacetimedb-cli # CLI\n//! │           |       ├── spacetimedb-standalone # Server\n//! │           |       ├── spacetimedb-cloud # Server\n//! │           |       ├── cli.default.toml # Template CLI configuration file\n//! │           |       └── config.default.toml # Template server configuration file\n//! |           └── data/\n//! |\n//! └── .config\n//!     └── spacetime\n//!         ├── id_ecdsa # Private key\n//!         ├── id_ecdsa.pub # Public key\n//!         └── cli.toml # CLI configuration\n//! ```\n//!\n//!## Windows Directory Structure\n//!\n//! On Windows the installation paths follow Windows conventions, and is equivalent\n//! to a Root Directory (as defined below) at `%LocalAppData%\\SpacetimeDB\\`.\n//!\n//! > **Note**: the `SpacetimeDB` directory is in `%LocalAppData%` and not `%AppData%`.\n//! > This is intentional so that different users on Windows can have different\n//! > configuration and binaries. This also allows you to install SpacetimeDB on Windows\n//! > even if you are not a privileged user.\n//!\n//! ## Custom Root Directory\n//!\n//! Users on all platforms must be allowed to override the default installation\n//! paths entirely with a single `--root-dir` argument passed to the initial\n//! installation commands.\n//!\n//! If users specify a `--root-dir` flag, then the installation paths should be\n//! defined relative to the `root-dir` as follows:\n//!\n//! * `cli-config-dir`: `{root-dir}/config/`\n//! * `cli-bin-dir`: `{root-dir}/bin/`\n//! * `cli-bin-file`: `{root-dir}/spacetime[.exe]`\n//! * `data-dir`: `{root-dir}/data/`\n//!\n//! For reference, the below is an example installation using the `--root-dir` argument:\n//!\n//! ```sh\n//! {root-dir}\n//! ├── spacetime -> {root-dir}/bin/1.10.1/spacetimedb-update # Current, in $PATH\n//! ├── config\n//! │   ├── id_ecdsa # Private key\n//! │   ├── id_ecdsa.pub # Public key\n//! │   └── cli.toml # CLI configuration\n//! ├── bin\n//! |   └── 1.10.1\n//! |       ├── spacetimedb-update.exe # Version manager\n//! |       ├── spacetimedb-cli.exe # CLI\n//! |       ├── spacetimedb-standalone.exe # Server\n//! |       ├── spacetimedb-cloud.exe # Server\n//! |       ├── cli.default.toml # Template CLI configuration file\n//! |       └── config.default.toml # Template server configuration file\n//! └── data/\n//! ```\n//!\n//! # Data directory structure\n//!\n//! The following is an example of the internal structure of data-dir. Note that this is not\n//! a stable hierarchy, and users should not rely on it being stable from version to version.\n//!\n//! ```sh\n//! {data-dir} # {Data}: CLI (--data-dir)\n//! ├── spacetime.pid # Lock file to prevent multiple instances, should be set to the pid of the running instance\n//! ├── config.toml # Server configuration (Human written, machine read only)\n//! ├── metadata.toml # Contains the edition, the MAJOR.MINOR.PATCH version number of the SpacetimeDB executable that created this directory. (Human readable, machine written only)\n//! ├── program-bytes # STANDALONE ONLY! Wasm modules aka `ProgramStorage` /var/lib/spacetime/data/standalone/2/program_bytes (NOTE: renamed from program_bytes)\n//! │   └── d6\n//! │       └── d9e66a8a285a416abd87e847c48b0990c6db6a5e0d5670c79a13f75dcabbe6\n//! ├── control-db # STANDALONE ONLY! Store information about the SpacetimeDB instances (NOTE: renamed from control_db)\n//! │   ├── blobs/ # Blobs storage\n//! │   ├── conf # Configuration for the Sled database\n//! │   └── db # Sled database\n//! ├── cache\n//! │   └── wasmtime\n//! ├── logs\n//! │   └── spacetimedb-standalone.2024-07-08.log  # filename format: `spacetimedb-{edition}.YYYY-MM-DD.log`\n//! └── replicas\n//!     ├── 1 # Database `replica_id`, unique per cluster\n//!     │   ├── clog # `CommitLog` files\n//!     │   │   └── 00000000000000000000.stdb.log\n//!     │   ├── module_logs # Module logs\n//!     │   │   └── 2024-07-08.log # filename format: `YYYY-MM-DD.log`\n//!     │   └── snapshots # Snapshots of the database\n//!     │       └── 00000000000000000000.snapshot_dir # BSATN-encoded `Snapshot`\n//!     │           ├── 00000000000000000000.snapshot_bsatn\n//!     │           └── objects # Objects storage\n//!     │               └── 01\n//!     │                   └── 040a8585e6dc2c579c0c8f6017c7e6a0179a5d0410cd8db4b4affbd7d4d04f\n//!     └── 34 # Database `replica_id`, unique per cluster\n//!         ├── clog # `CommitLog` files\n//!         │   └── 00000000000000000000.stdb.log\n//!         ├── module_logs # Module logs\n//!         │   └── 2024-07-08.log # filename format: `YYYY-MM-DD.log`\n//!         └── snapshots # Snapshots of the database\n//!             └── 00000000000000000000.snapshot_dir # BSATN-encoded `Snapshot`\n//!                 ├── 00000000000000000000.snapshot_bsatn\n//!                 └── objects # Objects storage directory trie\n//!                     └── 01\n//!                         └── 040a8585e6dc2c579c0c8f6017c7e6a0179a5d0410cd8db4b4affbd7d4d04f\n//! ```\n\nuse crate::utils::PathBufExt;\n\npub mod cli;\npub mod server;\npub mod standalone;\nmod utils;\n\n#[doc(hidden)]\npub use serde as __serde;\n\n/// Implemented for path types. Use `from_path_unchecked()` to construct a strongly-typed\n/// path directly from a `PathBuf`.\npub trait FromPathUnchecked {\n    /// The responsibility is on the caller to verify that the path is valid\n    /// for this directory structure node.\n    fn from_path_unchecked(path: impl Into<std::path::PathBuf>) -> Self;\n}\n\npath_type! {\n    /// The --root-dir for the spacetime installation, if specified.\n    // TODO: replace cfg(any()) with cfg(false) once stabilized\n    #[non_exhaustive(any())]\n    RootDir\n}\n\nimpl RootDir {\n    pub fn cli_config_dir(&self) -> cli::ConfigDir {\n        cli::ConfigDir(self.0.join(\"config\"))\n    }\n\n    pub fn cli_bin_file(&self) -> cli::BinFile {\n        cli::BinFile(self.0.join(\"spacetime\").with_exe_ext())\n    }\n\n    pub fn cli_bin_dir(&self) -> cli::BinDir {\n        cli::BinDir(self.0.join(\"bin\"))\n    }\n\n    pub fn data_dir(&self) -> server::ServerDataDir {\n        server::ServerDataDir(self.0.join(\"data\"))\n    }\n\n    fn from_paths(paths: &SpacetimePaths) -> Option<Self> {\n        let SpacetimePaths {\n            cli_config_dir,\n            cli_bin_file,\n            cli_bin_dir,\n            data_dir,\n        } = paths;\n        let parent = cli_config_dir.0.parent()?;\n        let parents = [cli_bin_file.0.parent()?, cli_bin_dir.0.parent()?, data_dir.0.parent()?];\n        parents.iter().all(|x| *x == parent).then(|| Self(parent.to_owned()))\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct SpacetimePaths {\n    pub cli_config_dir: cli::ConfigDir,\n    pub cli_bin_file: cli::BinFile,\n    pub cli_bin_dir: cli::BinDir,\n    pub data_dir: server::ServerDataDir,\n}\n\nimpl SpacetimePaths {\n    /// Get the default directories for the current platform.\n    ///\n    /// Returns an error if the platform director(y/ies) cannot be found.\n    pub fn platform_defaults() -> anyhow::Result<Self> {\n        #[cfg(windows)]\n        {\n            let data_dir = dirs::data_local_dir().ok_or_else(|| anyhow::anyhow!(\"Could not find LocalAppData\"))?;\n            let root_dir = RootDir(data_dir.joined(\"SpacetimeDB\"));\n            Ok(Self::from_root_dir(&root_dir))\n        }\n        #[cfg(not(windows))]\n        {\n            // `dirs` doesn't use XDG base dirs on macOS, which we want to do,\n            // so we use the `xdg` crate instead.\n            let base_dirs = xdg::BaseDirectories::with_prefix(\"spacetime\")?;\n            // bin_home should really be in the xdg crate\n            let xdg_bin_home = std::env::var_os(\"XDG_BIN_HOME\")\n                .map(std::path::PathBuf::from)\n                .filter(|p| p.is_absolute())\n                .unwrap_or_else(|| {\n                    #[allow(deprecated)] // this is fine on non-windows platforms\n                    std::env::home_dir().unwrap().joined(\".local/bin\")\n                });\n\n            let exe_name = \"spacetime\";\n\n            Ok(Self {\n                cli_config_dir: cli::ConfigDir(base_dirs.get_config_home()),\n                cli_bin_file: cli::BinFile(xdg_bin_home.join(exe_name)),\n                cli_bin_dir: cli::BinDir(base_dirs.get_data_file(\"bin\")),\n                data_dir: server::ServerDataDir(base_dirs.get_data_file(\"data\")),\n            })\n        }\n    }\n\n    pub fn from_root_dir(dir: &RootDir) -> Self {\n        Self {\n            cli_config_dir: dir.cli_config_dir(),\n            cli_bin_file: dir.cli_bin_file(),\n            cli_bin_dir: dir.cli_bin_dir(),\n            data_dir: dir.data_dir(),\n        }\n    }\n\n    pub fn to_root_dir(&self) -> Option<RootDir> {\n        RootDir::from_paths(self)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{PathBufExt, RootDir, SpacetimePaths};\n    use std::path::Path;\n\n    #[cfg(not(windows))]\n    mod vars {\n        use std::ffi::{OsStr, OsString};\n        struct ResetVar<'a>(&'a str, Option<OsString>);\n        impl Drop for ResetVar<'_> {\n            fn drop(&mut self) {\n                maybe_set_var(self.0, self.1.as_deref())\n            }\n        }\n        fn maybe_set_var(var: &str, val: Option<impl AsRef<OsStr>>) {\n            if let Some(val) = val {\n                // TODO: Audit that the environment access only happens in single-threaded code.\n                unsafe { std::env::set_var(var, val) };\n            } else {\n                // TODO: Audit that the environment access only happens in single-threaded code.\n                unsafe { std::env::remove_var(var) };\n            }\n        }\n        pub(super) fn with_vars<const N: usize, R>(vars: [(&str, Option<&str>); N], f: impl FnOnce() -> R) -> R {\n            let _guard = vars.map(|(var, val)| {\n                let prev_val = std::env::var_os(var);\n                maybe_set_var(var, val);\n                ResetVar(var, prev_val)\n            });\n            f()\n        }\n    }\n\n    #[cfg(not(windows))]\n    #[test]\n    fn xdg() {\n        let p = Path::new;\n        let paths = vars::with_vars(\n            [\n                (\"XDG_CONFIG_HOME\", Some(\"/__config_home\")),\n                (\"XDG_DATA_HOME\", Some(\"/__data_home\")),\n                (\"XDG_BIN_HOME\", Some(\"/__bin_home\")),\n            ],\n            SpacetimePaths::platform_defaults,\n        )\n        .unwrap();\n        assert_eq!(paths.cli_config_dir.0, p(\"/__config_home/spacetime\"));\n        assert_eq!(paths.cli_bin_file.0, p(\"/__bin_home/spacetime\"));\n        assert_eq!(paths.cli_bin_dir.0, p(\"/__data_home/spacetime/bin\"));\n        assert_eq!(paths.data_dir.0, p(\"/__data_home/spacetime/data\"));\n    }\n\n    #[cfg(windows)]\n    #[test]\n    fn windows() {\n        use crate::SpacetimePaths;\n\n        let paths = SpacetimePaths::platform_defaults().unwrap();\n        let appdata_local = dirs::data_local_dir().unwrap();\n        assert_eq!(paths.cli_config_dir.0, appdata_local.join(\"config\"));\n        assert_eq!(paths.cli_bin_file.0, appdata_local.join(\"spacetime.exe\"));\n        assert_eq!(paths.cli_bin_dir.0, appdata_local.join(\"bin\"));\n        assert_eq!(paths.data_dir.0, appdata_local.join(\"data\"));\n    }\n\n    #[test]\n    fn custom() {\n        let root = Path::new(\"/custom/path\");\n        let paths = SpacetimePaths::from_root_dir(&RootDir(root.to_owned()));\n        assert_eq!(paths.cli_config_dir.0, root.join(\"config\"));\n        assert_eq!(paths.cli_bin_file.0, root.join(\"spacetime\").with_exe_ext());\n        assert_eq!(paths.cli_bin_dir.0, root.join(\"bin\"));\n        assert_eq!(paths.data_dir.0, root.join(\"data\"));\n    }\n}\n"
  },
  {
    "path": "crates/paths/src/server.rs",
    "content": "use std::{fs, io};\n\nuse crate::utils::{path_type, PathBufExt};\nuse chrono::NaiveDate;\n\npath_type! {\n    /// The data-dir, where all database data is stored for a spacetime server process.\n    ServerDataDir: dir\n}\n\nimpl ServerDataDir {\n    pub fn config_toml(&self) -> ConfigToml {\n        ConfigToml(self.0.join(\"config.toml\"))\n    }\n\n    pub fn logs(&self) -> LogsDir {\n        LogsDir(self.0.join(\"logs\"))\n    }\n\n    pub fn wasmtime_cache(&self) -> WasmtimeCacheDir {\n        WasmtimeCacheDir(self.0.join(\"cache/wasmtime\"))\n    }\n\n    pub fn metadata_toml(&self) -> MetadataTomlPath {\n        MetadataTomlPath(self.0.join(\"metadata.toml\"))\n    }\n\n    pub fn pid_file(&self) -> Result<PidFile, PidFileError> {\n        use fs2::FileExt;\n        use io::{Read, Write};\n        self.create()?;\n        let path = self.0.join(\"spacetime.pid\");\n        let mut file = fs::File::options()\n            .create(true)\n            .write(true)\n            .truncate(false)\n            .read(true)\n            .open(&path)?;\n        match file.try_lock_exclusive() {\n            Ok(()) => {}\n            Err(e) if e.kind() == io::ErrorKind::WouldBlock => {\n                let mut s = String::new();\n                let pid = file.read_to_string(&mut s).ok().and_then(|_| s.trim().parse().ok());\n                return Err(PidFileError::Exists { pid });\n            }\n            Err(e) => return Err(e.into()),\n        }\n        let mut pidfile = PidFile { file, path };\n        pidfile.file.set_len(0)?;\n        write!(pidfile.file, \"{}\", std::process::id())?;\n        pidfile.file.flush()?;\n        Ok(pidfile)\n    }\n\n    pub fn replica(&self, replica_id: u64) -> ReplicaDir {\n        ReplicaDir(self.0.join(\"replicas\").joined_int(replica_id))\n    }\n}\n\npath_type! {\n    /// The `config.toml` file, where server configuration is stored.\n    ConfigToml: file\n}\n\npath_type! {\n    /// The directory in which server logs are to be stored.\n    ///\n    /// The files in this directory have the naming format `spacetime-{edition}.YYYY-MM-DD.log`.\n    LogsDir: dir\n}\n\nimpl LogsDir {\n    // we can't be as strongly typed as we might like here, because `tracing_subscriber`'s\n    // `RollingFileAppender` specifically takes the prefix and suffix of the filename, and\n    // sticks the date in between them - so we have to expose those components of the\n    // filename separately, rather than `fn logfile(&self, edition, date) -> LogFilePath`\n\n    /// The prefix before the first `.` of a logfile name.\n    pub fn filename_prefix(edition: &str) -> String {\n        format!(\"spacetime-{edition}\")\n    }\n\n    /// The file extension of a logfile name.\n    pub fn filename_extension() -> String {\n        \"log\".to_owned()\n    }\n}\n\npath_type! {\n    /// The directory we give to wasmtime to cache its compiled artifacts in.\n    WasmtimeCacheDir: dir\n}\n\npath_type! {\n    /// The `metadata.toml` file, where metadata about the server that owns this data-dir\n    /// is stored. Machine-writable only.\n    MetadataTomlPath: file\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum PidFileError {\n    #[error(\"error while taking database lock on spacetime.pid\")]\n    Io(#[from] io::Error),\n    #[error(\"cannot take lock on database; spacetime.pid already exists (owned by pid {pid:?})\")]\n    Exists { pid: Option<u32> },\n}\n\n/// Removes file upon drop\npub struct PidFile {\n    file: fs::File,\n    path: std::path::PathBuf,\n}\n\nimpl Drop for PidFile {\n    fn drop(&mut self) {\n        let _ = fs::remove_file(&self.path);\n    }\n}\n\npath_type! {\n    /// A replica directory, where all the data for a module's database is stored.\n    /// `{data-dir}/replicas/$replica_id/`\n    ReplicaDir: dir\n}\n\nimpl ReplicaDir {\n    pub fn module_logs(self) -> ModuleLogsDir {\n        ModuleLogsDir(self.0.joined(\"module_logs\"))\n    }\n\n    pub fn snapshots(&self) -> SnapshotsPath {\n        SnapshotsPath(self.0.join(\"snapshots\"))\n    }\n\n    pub fn commit_log(&self) -> CommitLogDir {\n        CommitLogDir(self.0.join(\"clog\"))\n    }\n}\n\npath_type! {\n    /// The directory where module logs are stored\n    ModuleLogsDir: dir\n}\n\nimpl ModuleLogsDir {\n    /// `date` should be in UTC.\n    pub fn logfile(self, date: NaiveDate) -> ModuleLogPath {\n        ModuleLogPath(self.0.joined(format!(\"{date}.log\")))\n    }\n\n    pub fn today(self) -> ModuleLogPath {\n        self.logfile(chrono::Utc::now().date_naive())\n    }\n\n    pub fn most_recent(self) -> io::Result<Option<ModuleLogPath>> {\n        let mut max_file_name = None;\n        for entry in std::fs::read_dir(&self)? {\n            let file_name = entry?.file_name();\n            max_file_name = std::cmp::max(max_file_name, Some(file_name));\n        }\n        Ok(max_file_name.map(|file_name| ModuleLogPath(self.0.joined(file_name))))\n    }\n}\n\npath_type! {\n    /// A module log from a specific date.\n    ModuleLogPath: file\n}\n\nimpl ModuleLogPath {\n    pub fn date(&self) -> NaiveDate {\n        self.0\n            .file_stem()\n            .and_then(|s| s.to_str()?.parse().ok())\n            .expect(\"ModuleLogPath should always have a filename of the form `{date}.log`\")\n    }\n\n    pub fn with_date(&self, date: NaiveDate) -> Self {\n        Self(self.0.with_file_name(format!(\"{date}.log\")))\n    }\n\n    pub fn yesterday(&self) -> Self {\n        self.with_date(self.date().pred_opt().unwrap())\n    }\n\n    pub fn popped(mut self) -> ModuleLogsDir {\n        self.0.pop();\n        ModuleLogsDir(self.0)\n    }\n}\n\npath_type! {\n    /// The snapshots directory. `{data-dir}/replica/$replica_id/snapshots`\n    SnapshotsPath: dir\n}\n\nimpl SnapshotsPath {\n    pub fn snapshot_dir(&self, tx_offset: u64) -> SnapshotDirPath {\n        let dir_name = format!(\"{tx_offset:0>20}.snapshot_dir\");\n        SnapshotDirPath(self.0.join(dir_name))\n    }\n}\n\npath_type! {\n    /// A snapshot directory. `{data-dir}/replica/$replica_id/snapshots/$tx_offset.snapshot_dir`\n    SnapshotDirPath: dir\n}\n\npath_type! {\n    /// An archived snapshot directory. `{data-dir}/replica/$replica_id/snapshots/$tx_offset.archived_snapshot`\n    ArchivedSnapshotDirPath: dir\n}\n\nimpl SnapshotDirPath {\n    pub fn snapshot_file(&self, tx_offset: u64) -> SnapshotFilePath {\n        let file_name = format!(\"{tx_offset:0>20}.snapshot_bsatn\");\n        SnapshotFilePath(self.0.join(file_name))\n    }\n\n    pub fn objects(&self) -> SnapshotObjectsPath {\n        SnapshotObjectsPath(self.0.join(\"objects\"))\n    }\n\n    pub fn rename_invalid(&self) -> io::Result<()> {\n        let invalid_path = self.0.with_extension(\"invalid_snapshot\");\n        fs::rename(self, invalid_path)\n    }\n\n    pub fn rename_as_archived(&self) -> io::Result<ArchivedSnapshotDirPath> {\n        let path = self.0.with_extension(\"archived_snapshot\");\n        fs::rename(self, &path)?;\n        Ok(ArchivedSnapshotDirPath(path))\n    }\n\n    pub fn tx_offset(&self) -> Option<u64> {\n        self.0\n            .file_stem()\n            .and_then(|s| s.to_str()?.split('.').next()?.parse::<u64>().ok())\n    }\n}\n\npath_type! {\n    /// A snapshot file.\n    /// `{data-dir}/replica/$replica_id/snapshots/$tx_offset.snapshot_dir/$tx_offset.snapshot_bsatn`\n    SnapshotFilePath: file\n}\npath_type! {\n    /// The objects directory for a snapshot.\n    /// `{data-dir}/replica/$replica_id/snapshots/$tx_offset.snapshot_dir/objects`\n    SnapshotObjectsPath: dir\n}\n\npath_type! {\n    /// The commit log directory. `{data-dir}/replica/$replica_id/clog`\n    CommitLogDir: dir\n}\n\nimpl CommitLogDir {\n    /// By convention, the file name of a segment consists of the minimum\n    /// transaction offset contained in it, left-padded with zeroes to 20 digits,\n    /// and the file extension `.stdb.log`.\n    pub fn segment(&self, offset: u64) -> SegmentFile {\n        let file_name = format!(\"{offset:0>20}.stdb.log\");\n        SegmentFile(self.0.join(file_name))\n    }\n\n    /// Returns the offset index file path based on the root path and offset\n    pub fn index(&self, offset: u64) -> OffsetIndexFile {\n        let file_name = format!(\"{offset:0>20}.stdb.ofs\");\n        OffsetIndexFile(self.0.join(file_name))\n    }\n}\n\npath_type!(SegmentFile: file);\npath_type!(OffsetIndexFile: file);\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use anyhow::Result;\n    use std::fs;\n    use tempfile::TempDir;\n\n    #[test]\n    fn test_pid_file_is_written() -> Result<()> {\n        let tempdir = TempDir::new()?;\n        let sdd = ServerDataDir(tempdir.path().to_path_buf());\n\n        let lock = sdd.pid_file()?;\n\n        // Make sure we wrote the pid file.\n        let pidstring = fs::read_to_string(lock.path.clone())?;\n        let _pid = pidstring.trim().parse::<u32>()?;\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_pid_is_exclusive() -> Result<()> {\n        let tempdir = TempDir::new()?;\n        let sdd = ServerDataDir(tempdir.path().to_path_buf());\n\n        let lock = sdd.pid_file()?;\n\n        // Make sure we wrote the pid file.\n        let pidstring = fs::read_to_string(lock.path.clone())?;\n        let _pid = pidstring.trim().parse::<u32>()?;\n\n        let attempt = sdd.pid_file();\n        assert!(attempt.is_err());\n\n        drop(lock);\n        // Make sure it can be acquired now.\n        sdd.pid_file()?;\n        Ok(())\n    }\n\n    #[test]\n    fn test_snapshot_parsing() -> Result<()> {\n        let tempdir = TempDir::new()?;\n        let sdd = ServerDataDir(tempdir.path().to_path_buf());\n        const SNAPSHOT_OFFSET: u64 = 123456;\n        let snapshot_dir = sdd.replica(1).snapshots().snapshot_dir(SNAPSHOT_OFFSET);\n        assert_eq!(Some(SNAPSHOT_OFFSET), snapshot_dir.tx_offset());\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/paths/src/standalone.rs",
    "content": "use crate::server::ServerDataDir;\nuse crate::utils::path_type;\n\npub trait StandaloneDataDirExt: AsRef<std::path::Path> {\n    fn program_bytes(&self) -> ProgramBytesDir {\n        ProgramBytesDir(self.as_ref().join(\"program-bytes\"))\n    }\n    fn control_db(&self) -> ControlDbDir {\n        ControlDbDir(self.as_ref().join(\"control-db\"))\n    }\n}\n\nimpl StandaloneDataDirExt for ServerDataDir {}\n\npath_type!(ProgramBytesDir: dir);\npath_type!(ControlDbDir: dir);\n"
  },
  {
    "path": "crates/paths/src/utils.rs",
    "content": "use std::borrow::BorrowMut;\nuse std::path::{Path, PathBuf};\n\npub(crate) trait PathBufExt: BorrowMut<PathBuf> + Sized {\n    fn joined<P: AsRef<Path>>(mut self, path: P) -> Self {\n        self.borrow_mut().push(path);\n        self\n    }\n    fn with_exe_ext(mut self) -> Self {\n        self.borrow_mut().set_extension(std::env::consts::EXE_EXTENSION);\n        self\n    }\n    fn joined_int<I: itoa::Integer>(self, path_seg: I) -> Self {\n        self.joined(itoa::Buffer::new().format(path_seg))\n    }\n}\n\nimpl PathBufExt for PathBuf {}\n\n/// Declares a new strongly-typed path newtype.\n///\n/// ```\n/// # use spacetimedb_paths::path_type;\n/// path_type! {\n///     /// optional docs\n///     // optional. if false, makes the type's constructor public.\n/// #   // TODO: replace cfg(any()) with cfg(false) once stabilized (rust-lang/rust#131204)\n///     #[non_exhaustive(any())]\n///     FooPath: dir // or file. adds extra utility methods for manipulating the file/dir\n/// }\n/// ```\n#[macro_export]\nmacro_rules! path_type {\n    ($(#[doc = $doc:literal])* $(#[non_exhaustive($($non_exhaustive:tt)+)])? $name:ident) => {\n        $(#[doc = $doc])*\n        #[derive(Clone, Debug, $crate::__serde::Serialize, $crate::__serde::Deserialize)]\n        #[serde(transparent)]\n        #[cfg_attr(all($($($non_exhaustive)+)?), non_exhaustive)]\n        pub struct $name(pub std::path::PathBuf);\n\n        impl AsRef<std::path::Path> for $name {\n            #[inline]\n            fn as_ref(&self) -> &std::path::Path {\n                &self.0\n            }\n        }\n        impl AsRef<std::ffi::OsStr> for $name {\n            #[inline]\n            fn as_ref(&self) -> &std::ffi::OsStr {\n                self.0.as_ref()\n            }\n        }\n\n        impl From<std::ffi::OsString> for $name {\n            fn from(s: std::ffi::OsString) -> Self {\n                Self(s.into())\n            }\n        }\n\n        impl $crate::FromPathUnchecked for $name {\n            fn from_path_unchecked(path: impl Into<std::path::PathBuf>) -> Self {\n                Self(path.into())\n            }\n        }\n\n        impl $name {\n            #[inline]\n            pub fn display(&self) -> std::path::Display<'_> {\n                self.0.display()\n            }\n\n            #[inline]\n            pub fn metadata(&self) -> std::io::Result<std::fs::Metadata> {\n                self.0.metadata()\n            }\n        }\n    };\n    ($(#[$($attr:tt)+])* $name:ident: dir) => {\n        path_type!($(#[$($attr)+])* $name);\n        impl $name {\n            #[inline]\n            pub fn create(&self) -> std::io::Result<()> {\n                std::fs::create_dir_all(self)\n            }\n            #[inline]\n            pub fn read_dir(&self) -> std::io::Result<std::fs::ReadDir> {\n                self.0.read_dir()\n            }\n            #[inline]\n            pub fn is_dir(&self) -> bool {\n                self.0.is_dir()\n            }\n        }\n    };\n    ($(#[$($attr:tt)+])* $name:ident: file) => {\n        path_type!($(#[$($attr)+])* $name);\n        impl $name {\n            pub fn read(&self) -> std::io::Result<Vec<u8>> {\n                std::fs::read(self)\n            }\n\n            pub fn read_to_string(&self) -> std::io::Result<String> {\n                std::fs::read_to_string(self)\n            }\n\n            pub fn write(&self, contents: impl AsRef<[u8]>) -> std::io::Result<()> {\n                self.create_parent()?;\n                std::fs::write(self, contents)\n            }\n\n            /// Opens a file at this path with the given options, ensuring its parent directory exists.\n            #[inline]\n            pub fn open_file(&self, options: &std::fs::OpenOptions) -> std::io::Result<std::fs::File> {\n                self.create_parent()?;\n                options.open(self)\n            }\n\n            /// Create the parent directory of this path if it doesn't already exist.\n            #[inline]\n            pub fn create_parent(&self) -> std::io::Result<()> {\n                if let Some(parent) = self.0.parent() {\n                    if parent != std::path::Path::new(\"\") {\n                        std::fs::create_dir_all(parent)?;\n                    }\n                }\n                Ok(())\n            }\n        }\n    };\n}\npub(crate) use path_type;\n"
  },
  {
    "path": "crates/pg/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-pg\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Postgres wire protocol Server support for SpacetimeDB\"\n\n[dependencies]\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-client-api.workspace = true\nspacetimedb-lib.workspace = true\n\nanyhow.workspace = true\nasync-trait.workspace = true\naxum.workspace = true\nfutures.workspace = true\nhttp.workspace = true\nlog.workspace = true\npgwire.workspace = true\nthiserror.workspace = true\ntokio.workspace = true\n"
  },
  {
    "path": "crates/pg/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/pg/src/encoder.rs",
    "content": "use crate::pg_server::PgError;\nuse pgwire::api::portal::Format;\nuse pgwire::api::results::{DataRowEncoder, FieldInfo};\nuse pgwire::api::Type;\nuse spacetimedb_lib::sats::satn::{PsqlChars, PsqlPrintFmt, PsqlType, TypedWriter};\nuse spacetimedb_lib::sats::{satn, ValueWithType};\nuse spacetimedb_lib::{\n    ser, AlgebraicType, AlgebraicValue, ProductType, ProductTypeElement, ProductValue, TimeDuration, Timestamp, Uuid,\n};\nuse std::borrow::Cow;\nuse std::sync::Arc;\n\npub(crate) fn row_desc(schema: &ProductType, format: &Format) -> Arc<Vec<FieldInfo>> {\n    Arc::new(\n        schema\n            .elements\n            .iter()\n            .enumerate()\n            .map(|(pos, ty)| {\n                let field_name = ty\n                    .name\n                    .clone()\n                    .map(|name| name.to_string())\n                    .unwrap_or_else(|| format!(\"col_{pos}\"));\n                let field_type = type_of(schema, ty);\n                FieldInfo::new(field_name, None, None, field_type, format.format_for(pos))\n            })\n            .collect(),\n    )\n}\n\npub(crate) fn type_of(schema: &ProductType, ty: &ProductTypeElement) -> Type {\n    let format = PsqlPrintFmt::use_fmt(schema, ty, ty.name().map(|n| &**n));\n    match &ty.algebraic_type {\n        AlgebraicType::String => Type::VARCHAR,\n        AlgebraicType::Bool => Type::BOOL,\n        AlgebraicType::U8 | AlgebraicType::I8 | AlgebraicType::I16 => Type::INT2,\n        AlgebraicType::U16 | AlgebraicType::I32 => Type::INT4,\n        AlgebraicType::U32 | AlgebraicType::I64 => Type::INT8,\n        AlgebraicType::U64 | AlgebraicType::I128 | AlgebraicType::U128 | AlgebraicType::I256 | AlgebraicType::U256 => {\n            Type::NUMERIC\n        }\n        AlgebraicType::F32 => Type::FLOAT4,\n        AlgebraicType::F64 => Type::FLOAT8,\n        AlgebraicType::Array(ty) => match *ty.elem_ty {\n            AlgebraicType::String => Type::VARCHAR_ARRAY,\n            AlgebraicType::Bool => Type::BOOL_ARRAY,\n            AlgebraicType::U8 => Type::BYTEA,\n            AlgebraicType::I8 | AlgebraicType::I16 => Type::INT2_ARRAY,\n            AlgebraicType::U16 | AlgebraicType::I32 => Type::INT4_ARRAY,\n            AlgebraicType::U32 | AlgebraicType::I64 => Type::INT8_ARRAY,\n            AlgebraicType::U64\n            | AlgebraicType::I128\n            | AlgebraicType::U128\n            | AlgebraicType::I256\n            | AlgebraicType::U256 => Type::NUMERIC_ARRAY,\n            _ => Type::ANYARRAY,\n        },\n        AlgebraicType::Product(_) => match format {\n            PsqlPrintFmt::Hex => Type::BYTEA,\n            PsqlPrintFmt::Timestamp => Type::TIMESTAMP,\n            PsqlPrintFmt::Duration => Type::INTERVAL,\n            PsqlPrintFmt::Uuid => Type::UUID,\n            _ => Type::JSON,\n        },\n        AlgebraicType::Sum(sum) if sum.is_simple_enum() => Type::ANYENUM,\n        AlgebraicType::Sum(_) => Type::JSON,\n        _ => Type::UNKNOWN,\n    }\n}\n\nimpl ser::Error for PgError {\n    fn custom<T: std::fmt::Display>(msg: T) -> Self {\n        PgError::Other(anyhow::anyhow!(msg.to_string()))\n    }\n}\n\npub(crate) struct PsqlFormatter<'a> {\n    pub(crate) encoder: &'a mut DataRowEncoder,\n}\n\nimpl TypedWriter for PsqlFormatter<'_> {\n    type Error = PgError;\n\n    fn write<W: std::fmt::Display>(&mut self, value: W) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value.to_string())?;\n        Ok(())\n    }\n\n    fn write_bool(&mut self, value: bool) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value)?;\n        Ok(())\n    }\n\n    fn write_string(&mut self, value: &str) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value)?;\n        Ok(())\n    }\n\n    fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value)?;\n        Ok(())\n    }\n\n    fn write_hex(&mut self, value: &[u8]) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value)?;\n        Ok(())\n    }\n\n    fn write_timestamp(&mut self, value: Timestamp) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value.to_rfc3339()?)?;\n        Ok(())\n    }\n\n    fn write_duration(&mut self, value: TimeDuration) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value.to_iso8601())?;\n        Ok(())\n    }\n\n    fn write_uuid(&mut self, value: Uuid) -> Result<(), Self::Error> {\n        self.encoder.encode_field(&value.to_string())?;\n        Ok(())\n    }\n\n    fn write_alt_record(\n        &mut self,\n        ty: &PsqlType,\n        value: &ValueWithType<'_, ProductValue>,\n    ) -> Result<bool, Self::Error> {\n        let json = satn::PsqlWrapper { ty: ty.clone(), value }.to_string();\n        self.encoder.encode_field(&json)?;\n        Ok(true)\n    }\n\n    fn write_record(\n        &mut self,\n        _fields: Vec<(Cow<str>, PsqlType, ValueWithType<AlgebraicValue>)>,\n    ) -> Result<(), Self::Error> {\n        unreachable!(\"Use `write_alt_record` for records in PSQL format\");\n    }\n\n    fn write_variant(\n        &mut self,\n        tag: u8,\n        ty: PsqlType,\n        name: Option<&str>,\n        value: ValueWithType<AlgebraicValue>,\n    ) -> Result<(), Self::Error> {\n        // Is a simple enum?\n        if let AlgebraicType::Sum(sum) = &ty.field.algebraic_type\n            && sum.is_simple_enum()\n            && let Some(variant_name) = name\n        {\n            self.encoder.encode_field(&variant_name)?;\n            return Ok(());\n        }\n\n        let PsqlChars { start, sep, end, quote } = ty.client.format_chars();\n        let name = name.map(Cow::from).unwrap_or_else(|| Cow::from(tag.to_string()));\n        let json = format!(\n            \"{start}{quote}{name}{quote}{sep} {}{end}\",\n            satn::PsqlWrapper { ty, value }\n        );\n        self.encoder.encode_field(&json)?;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::pg_server::to_rows;\n    use futures::StreamExt;\n    use spacetimedb_client_api_messages::http::SqlStmtResult;\n    use spacetimedb_lib::sats::algebraic_value::Packed;\n    use spacetimedb_lib::sats::{i256, product, u256, AlgebraicType, ProductType, SumTypeVariant};\n    use spacetimedb_lib::{ConnectionId, Identity};\n\n    async fn run(schema: ProductType, row: ProductValue) -> String {\n        let header = row_desc(&schema, &Format::UnifiedText);\n\n        let stmt = SqlStmtResult {\n            schema,\n            rows: vec![row],\n            total_duration_micros: 0,\n            stats: Default::default(),\n        };\n        let mut stream = to_rows(stmt, header).unwrap();\n        let mut result = String::new();\n        if let Some(row) = stream.next().await {\n            result = String::from_utf8_lossy(row.unwrap().data.freeze().as_ref()).to_string();\n        }\n        result\n    }\n\n    #[tokio::test]\n    async fn test_primitives() {\n        let schema = ProductType::from([\n            AlgebraicType::U8,\n            AlgebraicType::I8,\n            AlgebraicType::I16,\n            AlgebraicType::U16,\n            AlgebraicType::I32,\n            AlgebraicType::U32,\n            AlgebraicType::I64,\n            AlgebraicType::U64,\n            AlgebraicType::I128,\n            AlgebraicType::U128,\n            AlgebraicType::I256,\n            AlgebraicType::U256,\n            AlgebraicType::F32,\n            AlgebraicType::F64,\n            AlgebraicType::String,\n            AlgebraicType::Bool,\n        ]);\n        let value = product![\n            1u8,\n            -1i8,\n            -2i16,\n            3u16,\n            -4i32,\n            5u32,\n            -6i64,\n            7u64,\n            Packed::from(-8i128),\n            Packed::from(9u128),\n            i256::from(-10),\n            u256::from(11u128),\n            12.34f32,\n            56.78f64,\n            \"test\".to_string(),\n            true,\n        ];\n\n        let row = run(schema, value).await;\n        assert_eq!(row, \"\\0\\0\\0\\u{1}1\\0\\0\\0\\u{2}-1\\0\\0\\0\\u{2}-2\\0\\0\\0\\u{1}3\\0\\0\\0\\u{2}-4\\0\\0\\0\\u{1}5\\0\\0\\0\\u{2}-6\\0\\0\\0\\u{1}7\\0\\0\\0\\u{2}-8\\0\\0\\0\\u{1}9\\0\\0\\0\\u{3}-10\\0\\0\\0\\u{2}11\\0\\0\\0\\u{5}12.34\\0\\0\\0\\u{5}56.78\\0\\0\\0\\u{4}test\\0\\0\\0\\u{1}t\");\n    }\n\n    #[tokio::test]\n    async fn test_enum() {\n        let some = AlgebraicType::option(AlgebraicType::I64);\n        let schema = ProductType::from([some.clone(), some]);\n        let value = product![\n            AlgebraicValue::sum(0, AlgebraicValue::I64(1)), // Some(1)\n            AlgebraicValue::sum(1, AlgebraicValue::unit()), // None\n        ];\n\n        let row = run(schema, value).await;\n        assert_eq!(row, \"\\0\\0\\0\\u{b}{\\\"some\\\": 1}\\0\\0\\0\\u{c}{\\\"none\\\": {}}\");\n\n        let result = AlgebraicType::result(AlgebraicType::I64, AlgebraicType::String);\n        let schema = ProductType::from([result.clone(), result.clone()]);\n        let value = product![\n            AlgebraicValue::sum(0, AlgebraicValue::I64(1)),                 // Ok(1)\n            AlgebraicValue::sum(1, AlgebraicValue::String(\"error\".into())), // Err(\"error\")\n        ];\n        let row = run(schema, value).await;\n        assert_eq!(row, \"\\0\\0\\0\\t{\\\"ok\\\": 1}\\0\\0\\0\\u{10}{\\\"err\\\": \\\"error\\\"}\");\n\n        let color = AlgebraicType::Sum([SumTypeVariant::new_named(AlgebraicType::I64, \"Gray\")].into());\n        let nested = AlgebraicType::option(color.clone());\n        let schema = ProductType::from([color, nested]);\n        // {\"Gray\": 1}, {\"some\": {\"Gray\": 2}}\n        let value = product![\n            AlgebraicValue::sum(0, AlgebraicValue::I64(1)), // Gray(1)\n            AlgebraicValue::sum(0, AlgebraicValue::sum(0, AlgebraicValue::I64(2))), // Some(Gray(2))\n        ];\n        let row = run(schema.clone(), value.clone()).await;\n        assert_eq!(row, \"\\0\\0\\0\\u{b}{\\\"Gray\\\": 1}\\0\\0\\0\\u{15}{\\\"some\\\": {\\\"Gray\\\": 2}}\");\n\n        // Now nested product\n        let product = AlgebraicType::product([(\"x\", AlgebraicType::Product(schema)), (\"y\", AlgebraicType::String)]);\n        let schema = ProductType::from([product.clone()]);\n        let value = product![AlgebraicValue::product(vec![\n            value.into(),\n            AlgebraicValue::String(\"a\".into()),\n        ])];\n        let row = run(schema, value).await;\n        assert_eq!(\n            row,\n            \"\\0\\0\\0G{\\\"x\\\": {\\\"col_0\\\": {\\\"Gray\\\": 1}, \\\"col_1\\\": {\\\"some\\\": {\\\"Gray\\\": 2}}}, \\\"y\\\": \\\"a\\\"}\"\n        );\n\n        // Now a simple enum\n        let names = AlgebraicType::simple_enum([\"A\", \"B\", \"C\"].into_iter());\n        let schema = ProductType::from([names.clone(), names.clone(), names]);\n        let value = product![\n            AlgebraicValue::enum_simple(0), // A\n            AlgebraicValue::enum_simple(1), // B\n            AlgebraicValue::enum_simple(2), // C\n        ];\n        let row = run(schema, value).await;\n        assert_eq!(row, \"\\0\\0\\0\\u{1}A\\0\\0\\0\\u{1}B\\0\\0\\0\\u{1}C\");\n    }\n\n    #[tokio::test]\n    async fn test_special_types() {\n        let schema = ProductType::from([\n            AlgebraicType::identity(),\n            AlgebraicType::connection_id(),\n            AlgebraicType::time_duration(),\n            AlgebraicType::timestamp(),\n            AlgebraicType::bytes(),\n        ]);\n        let value = product![\n            Identity::ZERO,\n            ConnectionId::ZERO,\n            TimeDuration::from_micros(0),\n            Timestamp::from_micros_since_unix_epoch(1622545800000),\n            AlgebraicValue::Bytes(\"test\".as_bytes().into()),\n        ];\n\n        let row = run(schema, value).await;\n        assert_eq!(row, \"\\0\\0\\0B\\\\x0000000000000000000000000000000000000000000000000000000000000000\\0\\0\\0\\\"\\\\x00000000000000000000000000000000\\0\\0\\0\\u{3}P0D\\0\\0\\0\\u{1d}1970-01-19T18:42:25.800+00:00\\0\\0\\0\\n\\\\x74657374\");\n    }\n}\n"
  },
  {
    "path": "crates/pg/src/lib.rs",
    "content": "mod encoder;\npub mod pg_server;\n"
  },
  {
    "path": "crates/pg/src/pg_server.rs",
    "content": "use std::fmt::Debug;\nuse std::sync::Arc;\n\nuse crate::encoder::{row_desc, PsqlFormatter};\nuse async_trait::async_trait;\nuse axum::body::to_bytes;\nuse axum::response::IntoResponse;\nuse futures::{stream, Sink};\nuse futures::{SinkExt, Stream};\nuse http::StatusCode;\nuse pgwire::api::auth::{\n    finish_authentication, protocol_negotiation, save_startup_parameters_to_metadata, DefaultServerParameterProvider,\n    LoginInfo, StartupHandler,\n};\nuse pgwire::api::portal::Format;\nuse pgwire::api::query::SimpleQueryHandler;\nuse pgwire::api::results::{DataRowEncoder, FieldInfo, QueryResponse, Response, Tag};\nuse pgwire::api::{ClientInfo, METADATA_DATABASE};\nuse pgwire::api::{PgWireConnectionState, PgWireServerHandlers};\nuse pgwire::error::{ErrorInfo, PgWireError, PgWireResult};\nuse pgwire::messages::data::DataRow;\nuse pgwire::messages::startup::Authentication;\nuse pgwire::messages::{PgWireBackendMessage, PgWireFrontendMessage};\nuse pgwire::tokio::process_socket;\nuse spacetimedb_client_api::auth::validate_token;\nuse spacetimedb_client_api::routes::database;\nuse spacetimedb_client_api::routes::database::{SqlParams, SqlQueryParams};\nuse spacetimedb_client_api::{Authorization, ControlStateReadAccess, ControlStateWriteAccess, NodeDelegate};\nuse spacetimedb_client_api_messages::http::SqlStmtResult;\nuse spacetimedb_client_api_messages::name::DatabaseName;\nuse spacetimedb_lib::sats::satn::{PsqlClient, TypedSerializer};\nuse spacetimedb_lib::sats::{satn, Serialize, Typespace};\nuse spacetimedb_lib::version::spacetimedb_lib_version;\nuse spacetimedb_lib::{Identity, ProductValue};\nuse thiserror::Error;\nuse tokio::net::TcpListener;\nuse tokio::sync::{Mutex, Notify};\n\n#[derive(Error, Debug)]\npub(crate) enum PgError {\n    #[error(\"(metadata) {0}\")]\n    MetadataError(anyhow::Error),\n    #[error(\"(Sql) {0}\")]\n    Sql(String),\n    #[error(\"Database name is required\")]\n    DatabaseNameRequired,\n    #[error(transparent)]\n    Pg(#[from] PgWireError),\n    #[error(transparent)]\n    Other(#[from] anyhow::Error),\n}\n\nimpl From<PgError> for PgWireError {\n    fn from(err: PgError) -> Self {\n        if let PgError::Pg(err) = err {\n            err\n        } else {\n            PgWireError::ApiError(Box::new(err))\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct Metadata {\n    database: String,\n    caller_identity: Identity,\n}\n\npub(crate) fn to_rows(\n    stmt: SqlStmtResult<ProductValue>,\n    header: Arc<Vec<FieldInfo>>,\n) -> Result<impl Stream<Item = PgWireResult<DataRow>>, PgError> {\n    let mut results = Vec::with_capacity(stmt.rows.len());\n    let ty = Typespace::EMPTY.with_type(&stmt.schema);\n\n    let mut encoder = DataRowEncoder::new(header.clone());\n    for row in stmt.rows {\n        for (idx, value) in ty.with_values(&row).enumerate() {\n            let ty = satn::PsqlType {\n                client: PsqlClient::Postgres,\n                tuple: ty.ty(),\n                field: &ty.ty().elements[idx],\n                idx,\n            };\n            let mut fmt = PsqlFormatter { encoder: &mut encoder };\n            value.serialize(TypedSerializer { ty: &ty, f: &mut fmt })?;\n        }\n        results.push(Ok(encoder.take_row()));\n    }\n    Ok(stream::iter(results))\n}\n\nfn stats(stmt: &SqlStmtResult<ProductValue>) -> String {\n    let mut info = Vec::new();\n    if stmt.stats.rows_inserted != 0 {\n        info.push(format!(\"inserted: {}\", stmt.stats.rows_inserted));\n    }\n    if stmt.stats.rows_deleted != 0 {\n        info.push(format!(\"deleted: {}\", stmt.stats.rows_deleted));\n    }\n    if stmt.stats.rows_updated != 0 {\n        info.push(format!(\"updated: {}\", stmt.stats.rows_updated));\n    }\n    info.push(format!(\n        \"server: {:.2?}\",\n        std::time::Duration::from_micros(stmt.total_duration_micros)\n    ));\n\n    info.join(\", \")\n}\n\nstruct ResponseWrapper<T>(T);\nimpl<T> IntoResponse for ResponseWrapper<T> {\n    fn into_response(self) -> axum::response::Response {\n        unreachable!(\"Blank impl to satisfy IntoResponse\")\n    }\n}\n\nasync fn response<T>(res: axum::response::Result<T>, database: &str) -> Result<T, PgError> {\n    match res.map(ResponseWrapper) {\n        Ok(sql) => Ok(sql.0),\n        err => {\n            let res = err.into_response();\n            if res.status() == StatusCode::NOT_FOUND {\n                log::error!(\"PG: Database not found: {database}\");\n                return Err(PgWireError::UserError(Box::new(ErrorInfo::new(\n                    \"FATAL\".to_string(),\n                    \"3D000\".to_string(),\n                    format!(\"database \\\"{database}\\\" does not exist\"),\n                )))\n                .into());\n            }\n            let bytes = to_bytes(res.into_body(), usize::MAX)\n                .await\n                .map_err(|err| PgWireError::ApiError(Box::new(err)))?;\n            let err = String::from_utf8_lossy(&bytes);\n            log::error!(\"PG: Error for database {database}: {err}\");\n            Err(PgError::Sql(format!(\"{err}\")))\n        }\n    }\n}\n\nstruct PgSpacetimeDB<T> {\n    ctx: T,\n    cached: Mutex<Option<Metadata>>,\n    parameter_provider: DefaultServerParameterProvider,\n}\n\nimpl<T> PgSpacetimeDB<T>\nwhere\n    T: ControlStateReadAccess + ControlStateWriteAccess + NodeDelegate + Authorization + Clone,\n{\n    async fn exe_sql(&self, query: String) -> PgWireResult<Vec<Response>> {\n        let params = self.cached.lock().await.clone().unwrap();\n        let db = SqlParams {\n            name_or_identity: database::NameOrIdentity::Name(DatabaseName(params.database.clone())),\n        };\n\n        let sql = match response(\n            database::sql_direct(\n                self.ctx.clone(),\n                db,\n                SqlQueryParams { confirmed: Some(true) },\n                params.caller_identity,\n                query.to_string(),\n            )\n            .await,\n            &params.database,\n        )\n        .await\n        {\n            Ok(sql) => sql,\n            Err(PgError::Pg(PgWireError::UserError(err))) => {\n                return Ok(vec![Response::Error(err)]);\n            }\n            Err(err) => {\n                return Err(err.into());\n            }\n        };\n\n        let mut result = Vec::with_capacity(sql.len());\n        for sql_result in sql {\n            let header = row_desc(&sql_result.schema, &Format::UnifiedText);\n            if sql_result.rows.is_empty() && !query.to_uppercase().contains(\"SELECT\") {\n                let tag = Tag::new(&stats(&sql_result));\n                result.push(Response::Execution(tag));\n            } else {\n                let rows = to_rows(sql_result, header.clone())?;\n                let q = QueryResponse::new(header, rows);\n                result.push(Response::Query(q));\n            }\n        }\n        Ok(result)\n    }\n}\n\nasync fn close_client<C, E>(client: &mut C, err: E) -> PgWireResult<()>\nwhere\n    C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,\n    C::Error: Debug,\n    PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,\n    pgwire::messages::response::ErrorResponse: From<E>,\n{\n    let err = pgwire::messages::response::ErrorResponse::from(err);\n    client.feed(PgWireBackendMessage::ErrorResponse(err)).await?;\n    client.close().await?;\n    Ok(())\n}\n\n#[async_trait]\nimpl<T: Sync + Send + ControlStateReadAccess + ControlStateWriteAccess + NodeDelegate> StartupHandler\n    for PgSpacetimeDB<T>\n{\n    async fn on_startup<C>(&self, client: &mut C, message: PgWireFrontendMessage) -> PgWireResult<()>\n    where\n        C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send,\n        C::Error: Debug,\n        PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>,\n    {\n        match message {\n            PgWireFrontendMessage::Startup(ref startup) => {\n                protocol_negotiation(client, startup).await?;\n                save_startup_parameters_to_metadata(client, startup);\n                client.set_state(PgWireConnectionState::AuthenticationInProgress);\n\n                let login_info = LoginInfo::from_client_info(client);\n\n                if login_info.database().is_none() {\n                    return Err(PgError::DatabaseNameRequired.into());\n                }\n\n                client\n                    .send(PgWireBackendMessage::Authentication(Authentication::CleartextPassword))\n                    .await?;\n            }\n            PgWireFrontendMessage::PasswordMessageFamily(pwd) => {\n                let params = client.metadata();\n                let param = |param: &str| {\n                    params\n                        .get(param)\n                        .map(String::from)\n                        .ok_or_else(|| PgError::MetadataError(anyhow::anyhow!(\"Missing parameter: {param}\")))\n                };\n\n                // We don't support `METADATA_USER` because we don't have a user management system.\n                let database = param(METADATA_DATABASE)?;\n                let pwd = pwd.into_password()?;\n                match param(\"application_name\") {\n                    Ok(application_name) => {\n                        log::info!(\"PG: Connecting to database: {database}, by {application_name}\",);\n                    }\n                    _ => {\n                        log::info!(\"PG: Connecting to database: {database}\");\n                    }\n                }\n\n                let name = database::NameOrIdentity::Name(DatabaseName(database.clone()));\n                match response(name.resolve(&self.ctx).await, &database).await {\n                    Ok(identity) => identity,\n                    Err(PgError::Pg(PgWireError::UserError(err))) => {\n                        return close_client(client, *err).await;\n                    }\n                    Err(err) => {\n                        return Err(err.into());\n                    }\n                };\n\n                let caller_identity = match validate_token(&self.ctx, &pwd.password).await {\n                    Ok(claims) => claims.identity,\n                    Err(err) => {\n                        log::error!(\n                            \"PG: Authentication failed for identity `{}` on database {database}: {err}\",\n                            pwd.password\n                        );\n                        let err = ErrorInfo::new(\"FATAL\".to_owned(), \"28P01\".to_owned(), err.to_string());\n                        return close_client(client, err).await;\n                    }\n                };\n\n                log::info!(\"PG: Connected to database: {database} using identity `{caller_identity}`\");\n\n                let metadata = Metadata {\n                    database,\n                    caller_identity,\n                };\n                self.cached.lock().await.clone_from(&Some(metadata));\n                finish_authentication(client, &self.parameter_provider).await?;\n            }\n            // The other messages are for features not supported by SpacetimeDB, that are rejected by the parser.\n            // This includes TLS negotiation - any TLS negotiation done with the client will happen before\n            // this point, and because we pass `tls_acceptor: None` for `process_socket()`, pgwire will reject\n            // TLS for us.\n            _ => {\n                unreachable!(\"Unsupported startup message: {message:?}\");\n            }\n        }\n        Ok(())\n    }\n}\n\n#[async_trait]\nimpl<T> SimpleQueryHandler for PgSpacetimeDB<T>\nwhere\n    T: Sync + Send + ControlStateReadAccess + ControlStateWriteAccess + NodeDelegate + Authorization + Clone,\n{\n    async fn do_query<C>(&self, _client: &mut C, query: &str) -> PgWireResult<Vec<Response>>\n    where\n        C: ClientInfo + Unpin + Send + Sync,\n    {\n        self.exe_sql(query.to_string()).await\n    }\n}\n\n#[derive(Clone)]\npub struct PgSpacetimeDBFactory<T> {\n    handler: Arc<PgSpacetimeDB<T>>,\n}\n\nimpl<T> PgSpacetimeDBFactory<T> {\n    pub fn new(ctx: T) -> Self {\n        let mut parameter_provider = DefaultServerParameterProvider::default();\n        parameter_provider.server_version = format!(\"spacetime {}\", spacetimedb_lib_version());\n\n        Self {\n            handler: Arc::new(PgSpacetimeDB {\n                ctx,\n                // This is a placeholder, it will be set in the startup handler\n                cached: None.into(),\n                parameter_provider,\n            }),\n        }\n    }\n}\n\nimpl<T> PgWireServerHandlers for PgSpacetimeDBFactory<T>\nwhere\n    T: Sync + Send + ControlStateReadAccess + ControlStateWriteAccess + NodeDelegate + Authorization + Clone,\n{\n    fn simple_query_handler(&self) -> Arc<impl SimpleQueryHandler> {\n        self.handler.clone()\n    }\n\n    // TODO: fn extended_query_handler(&self) -> Arc<impl ExtendedQueryHandler> {}\n\n    fn startup_handler(&self) -> Arc<impl StartupHandler> {\n        self.handler.clone()\n    }\n}\n\npub async fn start_pg<T>(shutdown: Arc<Notify>, ctx: T, tcp: TcpListener)\nwhere\n    T: ControlStateReadAccess + ControlStateWriteAccess + NodeDelegate + Authorization + Clone + 'static,\n{\n    let factory = Arc::new(PgSpacetimeDBFactory::new(ctx));\n\n    log::debug!(\n        \"PG: Starting SpacetimeDB Protocol listening on {}\",\n        tcp.local_addr().unwrap()\n    );\n    loop {\n        tokio::select! {\n            accept_result = tcp.accept() => {\n                match accept_result {\n                    Ok((stream, _addr)) => {\n                        let factory_ref = factory.clone();\n                        tokio::spawn(async move {\n                            process_socket(stream, None, factory_ref).await.inspect_err(|err|{\n                                log::error!(\"PG: Error processing socket: {err:?}\");\n                            })\n                        });\n                    }\n                    Err(e) => {\n                       log::error!(\"PG: Accept error: {e}\");\n                    }\n                }\n            }\n            _ = shutdown.notified() => {\n                log::info!(\"PG: Shutting down PostgreSQL server.\");\n                break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/physical-plan/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-physical-plan\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The physical query plan for the SpacetimeDB query engine\"\n\n[dependencies]\nanyhow.workspace = true\nderive_more.workspace = true\neither.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-schema.workspace = true\nspacetimedb-expr.workspace = true\nspacetimedb-sql-parser.workspace = true\nspacetimedb-table.workspace = true\n\n[dev-dependencies]\npretty_assertions.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/physical-plan/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/physical-plan/src/compile.rs",
    "content": "//! Lowering from the logical plan to the physical plan.\n\nuse crate::dml::{DeletePlan, MutationPlan, UpdatePlan};\nuse crate::plan::{\n    HashJoin, Label, PhysicalExpr, PhysicalPlan, ProjectListPlan, ProjectPlan, Semi, TableScan, TupleField,\n};\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_expr::expr::{Expr, FieldProject, LeftDeepJoin, ProjectList, ProjectName, RelExpr, Relvar};\nuse spacetimedb_expr::statement::DML;\n\npub trait VarLabel {\n    fn label(&mut self, name: &str) -> Label;\n}\n\nfn compile_expr(expr: Expr, var: &mut impl VarLabel) -> PhysicalExpr {\n    match expr {\n        Expr::LogOp(op, a, b) => PhysicalExpr::LogOp(op, vec![compile_expr(*a, var), compile_expr(*b, var)]),\n        Expr::BinOp(op, a, b) => {\n            let a = Box::new(compile_expr(*a, var));\n            let b = Box::new(compile_expr(*b, var));\n            PhysicalExpr::BinOp(op, a, b)\n        }\n        Expr::Value(v, _) => PhysicalExpr::Value(v),\n        Expr::Field(proj) => PhysicalExpr::Field(compile_field_project(var, proj)),\n    }\n}\n\nfn compile_project_list(var: &mut impl VarLabel, expr: ProjectList) -> ProjectListPlan {\n    match expr {\n        ProjectList::Name(proj) => {\n            ProjectListPlan::Name(proj.into_iter().map(|proj| compile_project_name(var, proj)).collect())\n        }\n        ProjectList::Limit(input, n) => ProjectListPlan::Limit(Box::new(compile_project_list(var, *input)), n),\n        ProjectList::Agg(expr, agg, ..) => {\n            ProjectListPlan::Agg(expr.into_iter().map(|expr| compile_rel_expr(var, expr)).collect(), agg)\n        }\n        ProjectList::List(proj, fields) => ProjectListPlan::List(\n            proj.into_iter().map(|proj| compile_rel_expr(var, proj)).collect(),\n            fields\n                .into_iter()\n                .map(|(_, expr)| compile_field_project(var, expr))\n                .collect(),\n        ),\n    }\n}\n\nfn compile_project_name(var: &mut impl VarLabel, proj: ProjectName) -> ProjectPlan {\n    match proj {\n        ProjectName::None(input) => ProjectPlan::None(compile_rel_expr(var, input)),\n        ProjectName::Some(input, name) => ProjectPlan::Name(compile_rel_expr(var, input), var.label(&name), None),\n    }\n}\n\nfn compile_field_project(var: &mut impl VarLabel, expr: FieldProject) -> TupleField {\n    TupleField {\n        label: var.label(&expr.table),\n        label_pos: None,\n        field_pos: expr.field,\n    }\n}\n\nfn compile_rel_expr(var: &mut impl VarLabel, ast: RelExpr) -> PhysicalPlan {\n    match ast {\n        RelExpr::RelVar(Relvar { schema, alias, delta }) => {\n            let label = var.label(alias.as_ref());\n            let schema = schema.inner();\n            PhysicalPlan::TableScan(\n                TableScan {\n                    schema,\n                    limit: None,\n                    delta,\n                },\n                label,\n            )\n        }\n        RelExpr::Select(input, expr) => {\n            let input = compile_rel_expr(var, *input);\n            let input = Box::new(input);\n            PhysicalPlan::Filter(input, compile_expr(expr, var))\n        }\n        RelExpr::EqJoin(\n            LeftDeepJoin {\n                lhs,\n                rhs:\n                    Relvar {\n                        schema: rhs_schema,\n                        alias: rhs_alias,\n                        delta,\n                        ..\n                    },\n            },\n            FieldProject { table: u, field: a, .. },\n            FieldProject { table: v, field: b, .. },\n        ) => PhysicalPlan::HashJoin(\n            HashJoin {\n                lhs: Box::new(compile_rel_expr(var, *lhs)),\n                rhs: Box::new(PhysicalPlan::TableScan(\n                    TableScan {\n                        schema: rhs_schema.inner(),\n                        limit: None,\n                        delta,\n                    },\n                    var.label(&rhs_alias),\n                )),\n                lhs_field: TupleField {\n                    label: var.label(u.as_ref()),\n                    label_pos: None,\n                    field_pos: a,\n                },\n                rhs_field: TupleField {\n                    label: var.label(v.as_ref()),\n                    label_pos: None,\n                    field_pos: b,\n                },\n                unique: false,\n            },\n            Semi::All,\n        ),\n        RelExpr::LeftDeepJoin(LeftDeepJoin {\n            lhs,\n            rhs:\n                Relvar {\n                    schema: rhs_schema,\n                    alias: rhs_alias,\n                    delta,\n                    ..\n                },\n        }) => {\n            let lhs = compile_rel_expr(var, *lhs);\n            let rhs = PhysicalPlan::TableScan(\n                TableScan {\n                    schema: rhs_schema.inner(),\n                    limit: None,\n                    delta,\n                },\n                var.label(&rhs_alias),\n            );\n            let lhs = Box::new(lhs);\n            let rhs = Box::new(rhs);\n            PhysicalPlan::NLJoin(lhs, rhs)\n        }\n    }\n}\n\n/// Generates unique ids for named entities in a query plan\n#[derive(Default)]\nstruct NamesToIds {\n    next_id: usize,\n    map: HashMap<String, usize>,\n}\n\nimpl VarLabel for NamesToIds {\n    fn label(&mut self, name: &str) -> Label {\n        if let Some(id) = self.map.get(name) {\n            return Label(*id);\n        }\n        self.next_id += 1;\n        self.map.insert(name.to_owned(), self.next_id);\n        self.next_id.into()\n    }\n}\n\n/// Converts a logical selection into a physical plan.\n/// Note, this utility is specific to subscriptions,\n/// in that it does not support explicit column projections.\npub fn compile_select(project: ProjectName) -> ProjectPlan {\n    compile_project_name(&mut NamesToIds::default(), project)\n}\n\n/// Converts a logical selection into a physical plan.\n/// Note, this utility is applicable to a generic selections.\n/// In particular, it supports explicit column projections.\npub fn compile_select_list(project: ProjectList) -> ProjectListPlan {\n    compile_project_list(&mut NamesToIds::default(), project)\n}\n\n/// Converts a logical DML statement into a physical plan,\n/// but does not optimize it.\npub fn compile_dml_plan(stmt: DML) -> MutationPlan {\n    match stmt {\n        DML::Insert(insert) => MutationPlan::Insert(insert.into()),\n        DML::Delete(delete) => MutationPlan::Delete(DeletePlan::compile(delete)),\n        DML::Update(update) => MutationPlan::Update(UpdatePlan::compile(update)),\n    }\n}\n"
  },
  {
    "path": "crates/physical-plan/src/dml.rs",
    "content": "use std::sync::Arc;\n\nuse anyhow::Result;\nuse spacetimedb_expr::{\n    expr::{ProjectName, RelExpr, Relvar},\n    statement::{TableDelete, TableInsert, TableUpdate},\n};\nuse spacetimedb_lib::{identity::AuthCtx, AlgebraicValue, ProductValue};\nuse spacetimedb_primitives::ColId;\nuse spacetimedb_schema::schema::TableOrViewSchema;\n\nuse crate::{compile::compile_select, plan::ProjectPlan};\n\n/// A plan for mutating a table in the database\npub enum MutationPlan {\n    Insert(InsertPlan),\n    Delete(DeletePlan),\n    Update(UpdatePlan),\n}\n\nimpl MutationPlan {\n    /// Optimizes the filters in updates and deletes\n    pub fn optimize(self, auth: &AuthCtx) -> Result<Self> {\n        match self {\n            Self::Insert(..) => Ok(self),\n            Self::Delete(plan) => Ok(Self::Delete(plan.optimize(auth)?)),\n            Self::Update(plan) => Ok(Self::Update(plan.optimize(auth)?)),\n        }\n    }\n}\n\n/// A plan for inserting rows into a table\npub struct InsertPlan {\n    pub table: Arc<TableOrViewSchema>,\n    pub rows: Vec<ProductValue>,\n}\n\nimpl From<TableInsert> for InsertPlan {\n    fn from(insert: TableInsert) -> Self {\n        let TableInsert { table, rows } = insert;\n        let rows = rows.into_vec();\n        Self { table, rows }\n    }\n}\n\n/// A plan for deleting rows from a table\npub struct DeletePlan {\n    pub table: Arc<TableOrViewSchema>,\n    pub filter: ProjectPlan,\n}\n\nimpl DeletePlan {\n    /// Optimize the filter part of the delete\n    fn optimize(self, auth: &AuthCtx) -> Result<Self> {\n        let Self { table, filter } = self;\n        let filter = filter.optimize(auth)?;\n        Ok(Self { table, filter })\n    }\n\n    /// Logical to physical conversion\n    pub(crate) fn compile(delete: TableDelete) -> Self {\n        let TableDelete { table, filter } = delete;\n        let schema = table.clone();\n        let alias = table.table_name.clone().into();\n        let relvar = RelExpr::RelVar(Relvar {\n            schema,\n            alias,\n            delta: None,\n        });\n        let project = match filter {\n            None => ProjectName::None(relvar),\n            Some(expr) => ProjectName::None(RelExpr::Select(Box::new(relvar), expr)),\n        };\n        let filter = compile_select(project);\n        Self { table, filter }\n    }\n}\n\n/// A plan for updating rows in a table\npub struct UpdatePlan {\n    pub table: Arc<TableOrViewSchema>,\n    pub columns: Vec<(ColId, AlgebraicValue)>,\n    pub filter: ProjectPlan,\n}\n\nimpl UpdatePlan {\n    /// Optimize the filter part of the update\n    fn optimize(self, auth: &AuthCtx) -> Result<Self> {\n        let Self { table, columns, filter } = self;\n        let filter = filter.optimize(auth)?;\n        Ok(Self { columns, table, filter })\n    }\n\n    /// Logical to physical conversion\n    pub(crate) fn compile(update: TableUpdate) -> Self {\n        let TableUpdate { table, columns, filter } = update;\n        let schema = table.clone();\n        let alias = table.table_name.clone().into();\n        let relvar = RelExpr::RelVar(Relvar {\n            schema,\n            alias,\n            delta: None,\n        });\n        let project = match filter {\n            None => ProjectName::None(relvar),\n            Some(expr) => ProjectName::None(RelExpr::Select(Box::new(relvar), expr)),\n        };\n        let filter = compile_select(project);\n        let columns = columns.into_vec();\n        Self { columns, table, filter }\n    }\n}\n"
  },
  {
    "path": "crates/physical-plan/src/lib.rs",
    "content": "pub mod compile;\npub mod dml;\npub mod plan;\npub mod rules;\n"
  },
  {
    "path": "crates/physical-plan/src/plan.rs",
    "content": "use anyhow::{bail, Result};\nuse derive_more::From;\nuse either::Either;\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_expr::{\n    expr::{AggType, CollectViews},\n    StatementSource,\n};\nuse spacetimedb_lib::{identity::AuthCtx, query::Delta, sats::size_of::SizeOf, AlgebraicValue, ProductValue};\nuse spacetimedb_primitives::{ColId, ColSet, IndexId, TableId, ViewId};\nuse spacetimedb_schema::schema::{IndexSchema, TableSchema};\nuse spacetimedb_sql_parser::ast::{BinOp, LogOp};\nuse spacetimedb_table::table::RowRef;\nuse std::{\n    borrow::Cow,\n    ops::{Bound, Deref, DerefMut},\n    sync::Arc,\n};\n\nuse crate::rules::{\n    ComputePositions, HashToIxJoin, IxScanAnd, IxScanEq, IxScanEq2Col, IxScanEq3Col, PullFilterAboveHashJoin,\n    PushConstAnd, PushConstEq, PushLimit, ReorderDeltaJoinRhs, ReorderHashJoin, RewriteRule, UniqueHashJoinRule,\n    UniqueIxJoinRule,\n};\n\n/// Table aliases are replaced with labels in the physical plan\n#[derive(Debug, Clone, Copy, PartialEq, Eq, From)]\npub struct Label(pub usize);\n\n/// Physical plans always terminate with a projection.\n/// This type of projection returns row ids.\n///\n/// It can represent:\n///\n/// ```sql\n/// select * from t\n/// ```\n///\n/// and\n///\n/// ```sql\n/// select t.* from t join ...\n/// ```\n///\n/// but not\n///\n/// ```sql\n/// select a from t\n/// ```\n#[derive(Debug, Clone)]\npub enum ProjectPlan {\n    None(PhysicalPlan),\n    Name(PhysicalPlan, Label, Option<usize>),\n}\n\nimpl Deref for ProjectPlan {\n    type Target = PhysicalPlan;\n\n    fn deref(&self) -> &Self::Target {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan,\n        }\n    }\n}\n\nimpl DerefMut for ProjectPlan {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan,\n        }\n    }\n}\n\nimpl CollectViews for ProjectPlan {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan.collect_views(views),\n        }\n    }\n}\n\nimpl ProjectPlan {\n    pub fn optimize(self, auth: &AuthCtx) -> Result<Self> {\n        match self {\n            Self::None(plan) => Ok(Self::None(plan.optimize(auth, vec![])?)),\n            Self::Name(plan, label, _) => {\n                let plan = plan.optimize(auth, vec![label])?;\n                let n = plan.nfields();\n                let pos = plan.position(&label);\n                Ok(match n {\n                    1 => Self::None(plan),\n                    _ => Self::Name(plan, label, pos),\n                })\n            }\n        }\n    }\n\n    /// Unwrap the underlying physical plan\n    pub fn physical_plan(&self) -> &PhysicalPlan {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan,\n        }\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a single table?\n    pub fn return_table(&self) -> Option<Arc<TableSchema>> {\n        match self {\n            Self::None(plan) => plan.return_table(),\n            Self::Name(plan, label, _) => plan.find_table_schema(label),\n        }\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a view?\n    pub fn returns_view_table(&self) -> bool {\n        self.return_table().is_some_and(|schema| schema.is_view())\n    }\n\n    /// Does this plan read from an (anonymous) view?\n    pub fn reads_from_view(&self, anonymous: bool) -> bool {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan.reads_from_view(anonymous),\n        }\n    }\n\n    /// Does this plan use an event table as the lookup (rhs) table in a semi-join?\n    pub fn reads_from_event_table(&self) -> bool {\n        match self {\n            Self::None(plan) | Self::Name(plan, ..) => plan.reads_from_event_table(),\n        }\n    }\n}\n\n/// Physical plans always terminate with a projection.\n/// This type can project fields within a table.\n///\n/// That is, it can represent:\n///\n/// ```sql\n/// select a from t\n/// ```\n///\n/// as well as\n///\n/// ```sql\n/// select t.a, s.b from t join s ...\n/// ```\n///\n/// TODO: LIMIT and COUNT were added rather hastily.\n/// We should rethink having separate plan types for projections and selections,\n/// as it makes optimization more difficult the more they diverge.\n///\n/// Note that RLS takes a single expression and produces a list of expressions.\n/// Hence why these variants take lists rather than single expressions.\n/// See [spacetimedb_expr::ProjectList] for details.\n#[derive(Debug)]\npub enum ProjectListPlan {\n    /// A plan that returns physical rows\n    Name(Vec<ProjectPlan>),\n    /// A plan that returns virtual rows\n    List(Vec<PhysicalPlan>, Vec<TupleField>),\n    /// A plan that limits rows\n    Limit(Box<ProjectListPlan>, u64),\n    /// An aggregate function\n    Agg(Vec<PhysicalPlan>, AggType),\n}\n\nimpl ProjectListPlan {\n    pub fn optimize(self, auth: &AuthCtx) -> Result<Self> {\n        match self {\n            Self::Name(plan) => Ok(Self::Name(\n                plan.into_iter()\n                    .map(|plan| plan.optimize(auth))\n                    .collect::<Result<_>>()?,\n            )),\n            Self::Limit(plan, n) => {\n                let mut limit = Self::Limit(Box::new(plan.optimize(auth)?), n);\n                // Merge a limit with a scan if possible\n                if PushLimit::matches(&limit).is_some() {\n                    limit = PushLimit::rewrite(limit, ())?;\n                }\n                Ok(limit)\n            }\n            Self::Agg(plan, agg_type) => Ok(Self::Agg(\n                plan.into_iter()\n                    .map(|plan| plan.optimize(auth, vec![]))\n                    .collect::<Result<_>>()?,\n                agg_type,\n            )),\n            Self::List(plans, mut fields) => {\n                let mut optimized_plans = Vec::with_capacity(plans.len());\n                for plan in plans {\n                    // Collect the names of the relvars\n                    let labels = fields.iter().map(|field| field.label).collect();\n                    // Optimize each plan\n                    let optimized_plan = plan.optimize(auth, labels)?;\n                    // Compute the position of each relvar referenced in the projection\n                    for TupleField { label, label_pos, .. } in &mut fields {\n                        *label_pos = optimized_plan.position(label);\n                    }\n                    optimized_plans.push(optimized_plan);\n                }\n                Ok(Self::List(optimized_plans, fields))\n            }\n        }\n    }\n\n    /// Returns an iterator over the underlying physical plans\n    pub fn plan_iter(&self) -> impl Iterator<Item = &PhysicalPlan> + '_ {\n        match self {\n            Self::List(plans, _) | Self::Agg(plans, _) => Either::Left(plans.iter()),\n            Self::Name(plans) => Either::Right(plans.iter().map(|plan| plan.physical_plan())),\n            Self::Limit(plan, _) => plan.plan_iter(),\n        }\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a single table?\n    pub fn return_table(&self) -> Option<Arc<TableSchema>> {\n        match self {\n            Self::Name(plans) => plans.first().and_then(ProjectPlan::return_table),\n            Self::Limit(plan, _) => plan.return_table(),\n            Self::List(..) | Self::Agg(..) => None,\n        }\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a view?\n    pub fn returns_view_table(&self) -> bool {\n        self.return_table().is_some_and(|schema| schema.is_view())\n    }\n\n    /// Does this plan read from an (anonymous) view?\n    pub fn reads_from_view(&self, anonymous: bool) -> bool {\n        match self {\n            Self::Limit(plan, _) => plan.reads_from_view(anonymous),\n            Self::Name(plans) => plans.iter().any(|plan| plan.reads_from_view(anonymous)),\n            Self::List(plans, ..) | Self::Agg(plans, ..) => plans.iter().any(|plan| plan.reads_from_view(anonymous)),\n        }\n    }\n\n    /// Does this plan use an event table as the lookup (rhs) table in a semi-join?\n    pub fn reads_from_event_table(&self) -> bool {\n        match self {\n            Self::Limit(plan, _) => plan.reads_from_event_table(),\n            Self::Name(plans) => plans.iter().any(|plan| plan.reads_from_event_table()),\n            Self::List(plans, ..) | Self::Agg(plans, ..) => plans.iter().any(|plan| plan.reads_from_event_table()),\n        }\n    }\n}\n\n/// Query operators return tuples of rows.\n/// And this type refers to a field of a row within a tuple.\n///\n/// Note that from the perspective of the optimizer,\n/// tuple elements have names or labels,\n/// so as to preserve query semantics across rewrites.\n///\n/// However from the perspective of the query engine,\n/// tuple elements are entirely positional.\n/// Hence the need for both `label` and `label_pos`.\n///\n/// The former is consistent across rewrites.\n/// The latter is only computed once after optimization.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct TupleField {\n    pub label: Label,\n    pub label_pos: Option<usize>,\n    pub field_pos: usize,\n}\n\n/// A physical plan represents a concrete evaluation strategy.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum PhysicalPlan {\n    /// Scan a table row by row, returning row ids\n    TableScan(TableScan, Label),\n    /// Fetch row ids from an index\n    IxScan(IxScan, Label),\n    /// An index join + projection\n    IxJoin(IxJoin, Semi),\n    /// A hash join + projection\n    HashJoin(HashJoin, Semi),\n    /// A nested loop join\n    NLJoin(Box<PhysicalPlan>, Box<PhysicalPlan>),\n    /// A tuple-at-a-time filter\n    Filter(Box<PhysicalPlan>, PhysicalExpr),\n}\n\nimpl CollectViews for PhysicalPlan {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        self.visit(&mut |plan| {\n            let view_info = match plan {\n                Self::TableScan(scan, _) => &scan.schema.view_info,\n                Self::IxScan(scan, _) => &scan.schema.view_info,\n                Self::IxJoin(join, _) => &join.rhs.view_info,\n                _ => return,\n            };\n            if let Some(info) = view_info {\n                views.insert(info.view_id);\n            }\n        });\n    }\n}\n\nimpl PhysicalPlan {\n    /// Walks the plan tree and calls `f` on every op\n    pub fn visit(&self, f: &mut impl FnMut(&Self)) {\n        f(self);\n        match self {\n            Self::IxJoin(IxJoin { lhs: input, .. }, _) | Self::Filter(input, _) => {\n                input.visit(f);\n            }\n            Self::NLJoin(lhs, rhs) | Self::HashJoin(HashJoin { lhs, rhs, .. }, _) => {\n                lhs.visit(f);\n                rhs.visit(f);\n            }\n            Self::TableScan(..) | Self::IxScan(..) => {}\n        }\n    }\n\n    /// Walks the plan tree and calls `f` on every op\n    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {\n        f(self);\n        match self {\n            Self::IxJoin(IxJoin { lhs: input, .. }, _) | Self::Filter(input, _) => {\n                input.visit_mut(f);\n            }\n            Self::NLJoin(lhs, rhs) | Self::HashJoin(HashJoin { lhs, rhs, .. }, _) => {\n                lhs.visit_mut(f);\n                rhs.visit_mut(f);\n            }\n            Self::TableScan(..) | Self::IxScan(..) => {}\n        }\n    }\n\n    /// Is there any subplan where `f` returns true?\n    pub fn any(&self, f: &impl Fn(&Self) -> bool) -> bool {\n        let mut ok = false;\n        self.visit(&mut |plan| {\n            ok = ok || f(plan);\n        });\n        ok\n    }\n\n    /// Applies `f` recursively to all subplans\n    pub fn map(self, f: &impl Fn(Self) -> Self) -> Self {\n        match f(self) {\n            Self::Filter(input, expr) => Self::Filter(Box::new(input.map(f)), expr),\n            Self::NLJoin(lhs, rhs) => Self::NLJoin(Box::new(lhs.map(f)), Box::new(rhs.map(f))),\n            Self::HashJoin(join, semi) => Self::HashJoin(\n                HashJoin {\n                    lhs: Box::new(join.lhs.map(f)),\n                    rhs: Box::new(join.rhs.map(f)),\n                    ..join\n                },\n                semi,\n            ),\n            Self::IxJoin(join, semi) => Self::IxJoin(\n                IxJoin {\n                    lhs: Box::new(join.lhs.map(f)),\n                    ..join\n                },\n                semi,\n            ),\n            plan @ Self::TableScan(..) | plan @ Self::IxScan(..) => plan,\n        }\n    }\n\n    /// Applies `f` to a subplan if `ok` returns a match.\n    /// Recurses until an `ok` match is found.\n    pub fn map_if<Info>(\n        self,\n        f: impl FnOnce(Self, Info) -> Result<Self>,\n        ok: impl Fn(&Self) -> Option<Info>,\n    ) -> Result<Self> {\n        if let Some(info) = ok(&self) {\n            return f(self, info);\n        }\n        let matches = |plan: &PhysicalPlan| {\n            // Does `ok` match a subplan?\n            plan.any(&|plan| ok(plan).is_some())\n        };\n        Ok(match self {\n            Self::TableScan(..) | Self::IxScan(..) => self,\n            Self::NLJoin(lhs, rhs) => {\n                if matches(&lhs) {\n                    return Ok(Self::NLJoin(Box::new(lhs.map_if(f, ok)?), rhs));\n                }\n                if matches(&rhs) {\n                    return Ok(Self::NLJoin(lhs, Box::new(rhs.map_if(f, ok)?)));\n                }\n                Self::NLJoin(lhs, rhs)\n            }\n            Self::HashJoin(join, semi) => {\n                if matches(&join.lhs) {\n                    return Ok(Self::HashJoin(\n                        HashJoin {\n                            lhs: Box::new(join.lhs.map_if(f, ok)?),\n                            ..join\n                        },\n                        semi,\n                    ));\n                }\n                if matches(&join.rhs) {\n                    return Ok(Self::HashJoin(\n                        HashJoin {\n                            rhs: Box::new(join.rhs.map_if(f, ok)?),\n                            ..join\n                        },\n                        semi,\n                    ));\n                }\n                Self::HashJoin(join, semi)\n            }\n            Self::IxJoin(join, semi) => {\n                if matches(&join.lhs) {\n                    return Ok(Self::IxJoin(\n                        IxJoin {\n                            lhs: Box::new(join.lhs.map_if(f, ok)?),\n                            ..join\n                        },\n                        semi,\n                    ));\n                }\n                Self::IxJoin(join, semi)\n            }\n            Self::Filter(input, expr) => {\n                if matches(&input) {\n                    return Ok(Self::Filter(Box::new(input.map_if(f, ok)?), expr));\n                }\n                Self::Filter(input, expr)\n            }\n        })\n    }\n\n    /// Applies a rewrite rule once to this plan.\n    /// Updates indicator variable if plan was modified.\n    pub fn apply_once<R: RewriteRule<Plan = PhysicalPlan>>(self, ok: &mut bool) -> Result<Self> {\n        if let Some(info) = R::matches(&self) {\n            *ok = true;\n            return R::rewrite(self, info);\n        }\n        Ok(self)\n    }\n\n    /// Recursively apply a rule to all subplans until a fixedpoint is reached.\n    pub fn apply_rec<R: RewriteRule<Plan = PhysicalPlan>>(self) -> Result<Self> {\n        let mut ok = false;\n        let plan = self.map_if(\n            |plan, info| {\n                ok = true;\n                R::rewrite(plan, info)\n            },\n            R::matches,\n        )?;\n        if ok {\n            return plan.apply_rec::<R>();\n        }\n        Ok(plan)\n    }\n\n    /// Repeatedly apply a rule until a fixedpoint is reached.\n    /// It does not apply rule recursively to subplans.\n    pub fn apply_until<R: RewriteRule<Plan = PhysicalPlan>>(self) -> Result<Self> {\n        let mut ok = false;\n        let plan = self.apply_once::<R>(&mut ok)?;\n        if ok {\n            return plan.apply_until::<R>();\n        }\n        Ok(plan)\n    }\n\n    /// Optimize a plan using the following rewrites:\n    ///\n    /// 1. Canonicalize the plan\n    /// 2. Push filters to the leaves\n    /// 3. Turn filters into index scans if possible\n    /// 4. Determine index and semijoins\n    /// 5. Compute positions for tuple labels\n    pub fn optimize(self, auth: &AuthCtx, reqs: Vec<Label>) -> Result<Self> {\n        let optimized = self\n            .expand_views(auth)\n            .map(&Self::canonicalize)\n            .apply_rec::<PushConstAnd>()?\n            .apply_rec::<PushConstEq>()?\n            .apply_rec::<ReorderDeltaJoinRhs>()?\n            .apply_rec::<PullFilterAboveHashJoin>()?\n            .apply_rec::<IxScanEq3Col>()?\n            .apply_rec::<IxScanEq2Col>()?\n            .apply_rec::<IxScanEq>()?\n            .apply_rec::<IxScanAnd>()?\n            .apply_rec::<ReorderHashJoin>()?\n            .apply_rec::<HashToIxJoin>()?\n            .apply_rec::<UniqueIxJoinRule>()?\n            .apply_rec::<UniqueHashJoinRule>()?\n            .introduce_semijoins(reqs)\n            .apply_rec::<ComputePositions>()?;\n\n        let mut unresolved_name = false;\n\n        // Check that we've derived positional values for all named arguments\n        optimized.visit(&mut |plan| {\n            match plan {\n                Self::Filter(_, expr) => {\n                    expr.visit(&mut |expr| {\n                        if let PhysicalExpr::Field(TupleField { label_pos: None, .. }) = expr {\n                            unresolved_name = true;\n                        }\n                    });\n                }\n                Self::IxJoin(\n                    IxJoin {\n                        lhs_field: TupleField { label_pos: None, .. },\n                        ..\n                    },\n                    _,\n                )\n                | Self::HashJoin(\n                    HashJoin {\n                        lhs_field: TupleField { label_pos: None, .. },\n                        ..\n                    },\n                    _,\n                )\n                | Self::HashJoin(\n                    HashJoin {\n                        rhs_field: TupleField { label_pos: None, .. },\n                        ..\n                    },\n                    _,\n                ) => {\n                    unresolved_name = true;\n                }\n                _ => {}\n            };\n        });\n\n        if unresolved_name {\n            bail!(\"Could not compute positional arguments during query planning\")\n        }\n\n        Ok(optimized)\n    }\n\n    /// If a view is not anonymous, its backing table has a `sender` column.\n    /// This column tracks which rows belong to which caller.\n    ///\n    /// As a result, queries over such views cannot read the entire backing table.\n    /// They must only select the rows corresponding to the caller of the query.\n    /// Hence we must add an implicit selection over these types of views.\n    ///\n    /// Ex.\n    /// ```sql\n    /// SELECT * FROM my_view\n    /// ```\n    ///\n    /// becomes\n    /// ```sql\n    /// SELECT * FROM my_view WHERE sender = :sender\n    /// ```\n    fn expand_views(self, auth: &AuthCtx) -> Self {\n        match self {\n            Self::TableScan(scan, label) if scan.schema.is_view() && !scan.schema.is_anonymous_view() => Self::Filter(\n                Box::new(Self::TableScan(scan, label)),\n                PhysicalExpr::BinOp(\n                    BinOp::Eq,\n                    Box::new(PhysicalExpr::Value(auth.caller().into())),\n                    Box::new(PhysicalExpr::Field(TupleField {\n                        label,\n                        label_pos: None,\n                        field_pos: 0,\n                    })),\n                ),\n            ),\n            Self::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_label,\n                    rhs_index,\n                    rhs_prefix,\n                    rhs_field,\n                    unique,\n                    lhs_field,\n                    rhs_delta,\n                },\n                semi,\n            ) => Self::IxJoin(\n                IxJoin {\n                    lhs: Box::new(lhs.expand_views(auth)),\n                    rhs,\n                    rhs_label,\n                    rhs_index,\n                    rhs_prefix,\n                    rhs_field,\n                    unique,\n                    lhs_field,\n                    rhs_delta,\n                },\n                semi,\n            ),\n            Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field,\n                    rhs_field,\n                    unique,\n                },\n                semi,\n            ) => Self::HashJoin(\n                HashJoin {\n                    lhs: Box::new(lhs.expand_views(auth)),\n                    rhs: Box::new(rhs.expand_views(auth)),\n                    lhs_field,\n                    rhs_field,\n                    unique,\n                },\n                semi,\n            ),\n            Self::Filter(input, expr) => Self::Filter(Box::new(input.expand_views(auth)), expr),\n            Self::NLJoin(lhs, rhs) => Self::NLJoin(Box::new(lhs.expand_views(auth)), Box::new(rhs.expand_views(auth))),\n            Self::TableScan(..) | Self::IxScan(..) => self,\n        }\n    }\n\n    /// The rewriter assumes a canonicalized plan.\n    /// And this means:\n    ///\n    /// 1. Literals are always on the rhs of a sargable predicate.\n    /// 2. Nested ANDs and ORs are flattened.\n    /// 3. The lhs(rhs) expr corresponds to the lhs(rhs) of an equijoin.\n    ///\n    /// Examples:\n    ///\n    /// 1. Move values to rhs\n    /// ```sql\n    /// select * from a where 3 = a.x\n    /// ```\n    ///\n    /// ... to ..\n    ///\n    /// ```sql\n    /// select * from a where a.x = 3\n    /// ```\n    ///\n    /// 2. Flatten ANDs and ORs\n    /// ```sql\n    /// select * from a where (a.x = 3 and a.y = 4) and a.z = 5\n    /// ```\n    ///\n    /// ... to ..\n    ///\n    /// ```sql\n    /// select * from a where a.x = 3 and a.y = 4 and a.z = 5\n    /// ```\n    ///\n    /// 3. Canonicalize equijoin\n    /// ```sql\n    /// select a.* from a join b on b.id = a.id\n    /// ```\n    ///\n    /// ... to ...\n    ///\n    /// ```sql\n    /// select a.* from a join b on a.id = b.id\n    /// ```\n    fn canonicalize(self) -> Self {\n        match self {\n            Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field,\n                    rhs_field,\n                    unique,\n                },\n                semi,\n            ) if rhs.has_label(&lhs_field.label) || lhs.has_label(&rhs_field.label) => Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field: rhs_field,\n                    rhs_field: lhs_field,\n                    unique,\n                },\n                semi,\n            ),\n            Self::Filter(input, expr) => {\n                let move_value_to_rhs = |expr| match expr {\n                    PhysicalExpr::BinOp(op, value, expr)\n                        if matches!(&*value, PhysicalExpr::Value(_)) && matches!(&*expr, PhysicalExpr::Field(..)) =>\n                    {\n                        match op {\n                            BinOp::Eq => PhysicalExpr::BinOp(BinOp::Eq, expr, value),\n                            BinOp::Ne => PhysicalExpr::BinOp(BinOp::Ne, expr, value),\n                            BinOp::Lt => PhysicalExpr::BinOp(BinOp::Gt, expr, value),\n                            BinOp::Gt => PhysicalExpr::BinOp(BinOp::Lt, expr, value),\n                            BinOp::Lte => PhysicalExpr::BinOp(BinOp::Gte, expr, value),\n                            BinOp::Gte => PhysicalExpr::BinOp(BinOp::Lte, expr, value),\n                        }\n                    }\n                    _ => expr,\n                };\n                // Flatten ANDs and ORs, and move values to rhs\n                Self::Filter(input, expr.flatten().map(&move_value_to_rhs))\n            }\n            _ => self,\n        }\n    }\n\n    /// Introduce semijoins in the plan.\n    ///\n    /// Example:\n    ///\n    /// p:  project\n    /// x:  join\n    /// sx: semijoin\n    ///\n    /// ```text\n    ///    p(c)\n    ///     |\n    ///     x\n    ///    / \\\n    ///   x   c\n    ///  / \\\n    /// a   b\n    ///\n    /// ... to ...\n    ///\n    ///    p(c)\n    ///     |\n    ///     x\n    ///    / \\\n    ///  p(b) c\n    ///   |\n    ///   x\n    ///  / \\\n    /// a   b\n    ///\n    /// ... to ..\n    ///\n    ///     sx\n    ///    /  \\\n    ///   sx   c\n    ///  /  \\\n    /// a    b\n    /// ```\n    ///\n    /// ```sql\n    /// select c.*\n    /// from (select * from a where a.x = 3) a\n    /// join b on a.id = b.id\n    /// join c on b.id = c.id\n    /// ```\n    ///\n    /// ... to ...\n    ///\n    /// ```sql\n    /// select c.*\n    /// from (\n    ///   select b.*\n    ///   from (select * from a where a.x = 3) a\n    ///   join b on a.id = b.id\n    /// ) b\n    /// join c on b.id = c.id\n    /// ```\n    fn introduce_semijoins(self, mut reqs: Vec<Label>) -> Self {\n        let append_required_label = |plan: &PhysicalPlan, reqs: &mut Vec<Label>, label: Label| {\n            if !reqs.contains(&label) && plan.has_label(&label) {\n                reqs.push(label);\n            }\n        };\n        match self {\n            Self::Filter(input, expr) => {\n                expr.visit(&mut |expr| {\n                    if let PhysicalExpr::Field(TupleField { label: var, .. }) = expr\n                        && !reqs.contains(var)\n                    {\n                        reqs.push(*var);\n                    }\n                });\n                Self::Filter(Box::new(input.introduce_semijoins(reqs)), expr)\n            }\n            Self::NLJoin(lhs, rhs) => {\n                let mut lhs_reqs = vec![];\n                let mut rhs_reqs = vec![];\n\n                for var in reqs {\n                    append_required_label(&lhs, &mut lhs_reqs, var);\n                    append_required_label(&rhs, &mut rhs_reqs, var);\n                }\n                let lhs = lhs.introduce_semijoins(lhs_reqs);\n                let rhs = rhs.introduce_semijoins(rhs_reqs);\n                let lhs = Box::new(lhs);\n                let rhs = Box::new(rhs);\n                Self::NLJoin(lhs, rhs)\n            }\n            Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field: lhs_field @ TupleField { label: u, .. },\n                    rhs_field: rhs_field @ TupleField { label: v, .. },\n                    unique,\n                },\n                Semi::All,\n            ) => {\n                let semi = reqs\n                    .iter()\n                    .all(|label| lhs.has_label(label))\n                    .then_some(Semi::Lhs)\n                    .or_else(|| reqs.iter().all(|label| rhs.has_label(label)).then_some(Semi::Rhs))\n                    .unwrap_or(Semi::All);\n                let mut lhs_reqs = vec![u];\n                let mut rhs_reqs = vec![v];\n                for var in reqs {\n                    append_required_label(&lhs, &mut lhs_reqs, var);\n                    append_required_label(&rhs, &mut rhs_reqs, var);\n                }\n                let lhs = lhs.introduce_semijoins(lhs_reqs);\n                let rhs = rhs.introduce_semijoins(rhs_reqs);\n                let lhs = Box::new(lhs);\n                let rhs = Box::new(rhs);\n                Self::HashJoin(\n                    HashJoin {\n                        lhs,\n                        rhs,\n                        lhs_field,\n                        rhs_field,\n                        unique,\n                    },\n                    semi,\n                )\n            }\n            Self::IxJoin(join, Semi::All) if reqs.len() == 1 && join.rhs_label == reqs[0] => {\n                let lhs = join.lhs.introduce_semijoins(vec![join.lhs_field.label]);\n                let lhs = Box::new(lhs);\n                Self::IxJoin(IxJoin { lhs, ..join }, Semi::Rhs)\n            }\n            Self::IxJoin(join, Semi::All) if reqs.iter().all(|var| *var != join.rhs_label) => {\n                if !reqs.contains(&join.lhs_field.label) {\n                    reqs.push(join.lhs_field.label);\n                }\n                let lhs = join.lhs.introduce_semijoins(reqs);\n                let lhs = Box::new(lhs);\n                Self::IxJoin(IxJoin { lhs, ..join }, Semi::Lhs)\n            }\n            Self::IxJoin(join, Semi::All) => {\n                let mut reqs: Vec<_> = reqs.into_iter().filter(|label| label != &join.rhs_label).collect();\n                if !reqs.contains(&join.lhs_field.label) {\n                    reqs.push(join.lhs_field.label);\n                }\n                let lhs = join.lhs.introduce_semijoins(reqs);\n                let lhs = Box::new(lhs);\n                Self::IxJoin(IxJoin { lhs, ..join }, Semi::All)\n            }\n            _ => self,\n        }\n    }\n\n    // Does this plan return distinct values for these columns?\n    pub(crate) fn returns_distinct_values(&self, label: &Label, cols: &ColSet) -> bool {\n        match self {\n            // Is there a unique constraint for these cols?\n            Self::TableScan(TableScan { schema, .. }, var) => var == label && schema.as_ref().is_unique(&**cols),\n            // Is there a unique constraint for these cols + the index cols?\n            Self::IxScan(\n                IxScan {\n                    schema,\n                    prefix,\n                    arg: Sarg::Eq(col, _),\n                    ..\n                },\n                var,\n            ) => {\n                var == label\n                    && schema.as_ref().is_unique(&*ColSet::from_iter(\n                        cols.iter()\n                            .chain(prefix.iter().map(|(col_id, _)| *col_id))\n                            .chain(vec![*col]),\n                    ))\n            }\n            // If the table in question is on the lhs,\n            // and if the lhs returns distinct values,\n            // we need the rhs to return at most one row when probed.\n            // But this is a unique index join,\n            // so by definition this requirement is satisfied.\n            Self::IxJoin(IxJoin { lhs, unique: true, .. }, _) if lhs.has_label(label) => {\n                lhs.returns_distinct_values(label, cols)\n            }\n            // If the table in question is on the rhs,\n            // and if the rhs returns distinct values,\n            // we must not probe the rhs for the same value more than once.\n            // Hence the lhs must be distinct w.r.t the probe field.\n            Self::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    lhs_field:\n                        TupleField {\n                            label: lhs_label,\n                            field_pos: lhs_field_pos,\n                            ..\n                        },\n                    ..\n                },\n                _,\n            ) => {\n                lhs.returns_distinct_values(lhs_label, &ColSet::from(ColId(*lhs_field_pos as u16)))\n                    && rhs.as_ref().is_unique(&**cols)\n            }\n            // If the table in question is on the lhs,\n            // and if the lhs returns distinct values,\n            // we need the rhs to return at most one row when probed.\n            // Hence the rhs must be distinct w.r.t the probe field.\n            Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    rhs_field:\n                        TupleField {\n                            label: rhs_label,\n                            field_pos: rhs_field_pos,\n                            ..\n                        },\n                    ..\n                },\n                _,\n            ) if lhs.has_label(label) => {\n                lhs.returns_distinct_values(label, cols)\n                    && rhs.returns_distinct_values(rhs_label, &ColSet::from(ColId(*rhs_field_pos as u16)))\n            }\n            // If the table in question is on the rhs,\n            // and if the rhs returns distinct values,\n            // we must not probe the rhs for the same value more than once.\n            // Hence the lhs must be distinct w.r.t the probe field.\n            Self::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field:\n                        TupleField {\n                            label: lhs_label,\n                            field_pos: lhs_field_pos,\n                            ..\n                        },\n                    ..\n                },\n                _,\n            ) => {\n                rhs.returns_distinct_values(label, cols)\n                    && lhs.returns_distinct_values(lhs_label, &ColSet::from(ColId(*lhs_field_pos as u16)))\n            }\n            // For the columns in question,\n            // the base table may not return distinct values,\n            // but given the necessary equality conditions,\n            // the filter can return distinct values for them.\n            Self::Filter(input, expr) => {\n                let mut cols: Vec<_> = cols.iter().collect();\n                expr.visit(&mut |plan| {\n                    if let PhysicalExpr::BinOp(BinOp::Eq, expr, value) = plan\n                        && let (PhysicalExpr::Field(proj), PhysicalExpr::Value(..)) = (&**expr, &**value)\n                        && proj.label == *label\n                    {\n                        cols.push(proj.field_pos.into());\n                    }\n                });\n                input.returns_distinct_values(label, &ColSet::from_iter(cols))\n            }\n            _ => false,\n        }\n    }\n\n    pub fn index_on_field(&self, label: &Label, field: usize) -> bool {\n        self.any(&|plan| match plan {\n            Self::TableScan(TableScan { schema, .. }, alias)\n            | Self::IxScan(IxScan { schema, .. }, alias)\n            | Self::IxJoin(\n                IxJoin {\n                    rhs: schema,\n                    rhs_label: alias,\n                    ..\n                },\n                _,\n            ) if alias == label => schema.indexes.iter().any(|IndexSchema { index_algorithm, .. }| {\n                index_algorithm\n                    .columns()\n                    .as_singleton()\n                    .is_some_and(|col_id| col_id.idx() == field)\n            }),\n            _ => false,\n        })\n    }\n\n    /// Does this plan introduce this label?\n    fn has_label(&self, label: &Label) -> bool {\n        self.any(&|plan| match plan {\n            Self::TableScan(_, var) | Self::IxScan(_, var) | Self::IxJoin(IxJoin { rhs_label: var, .. }, _) => {\n                var == label\n            }\n            _ => false,\n        })\n    }\n\n    /// How many fields do the tuples returned by this plan have?\n    fn nfields(&self) -> usize {\n        match self {\n            Self::TableScan(..) | Self::IxScan(..) | Self::IxJoin(_, Semi::Rhs) => 1,\n            Self::Filter(input, _) => input.nfields(),\n            Self::IxJoin(join, Semi::Lhs) => join.lhs.nfields(),\n            Self::IxJoin(join, Semi::All) => join.lhs.nfields() + 1,\n            Self::HashJoin(join, Semi::Rhs) => join.rhs.nfields(),\n            Self::HashJoin(join, Semi::Lhs) => join.lhs.nfields(),\n            Self::HashJoin(join, Semi::All) => join.lhs.nfields() + join.rhs.nfields(),\n            Self::NLJoin(lhs, rhs) => lhs.nfields() + rhs.nfields(),\n        }\n    }\n\n    /// What is the position of this label in the return tuple?\n    pub(crate) fn position(&self, label: &Label) -> Option<usize> {\n        self.labels()\n            .into_iter()\n            .enumerate()\n            .find(|(_, name)| name == label)\n            .map(|(i, _)| i)\n    }\n\n    /// Returns the names of the relvars that this operation returns\n    fn labels(&self) -> Vec<Label> {\n        fn find(plan: &PhysicalPlan, labels: &mut Vec<Label>) {\n            match plan {\n                PhysicalPlan::TableScan(_, alias)\n                | PhysicalPlan::IxScan(_, alias)\n                | PhysicalPlan::IxJoin(IxJoin { rhs_label: alias, .. }, Semi::Rhs) => {\n                    labels.push(*alias);\n                }\n                PhysicalPlan::Filter(input, _)\n                | PhysicalPlan::IxJoin(IxJoin { lhs: input, .. }, Semi::Lhs)\n                | PhysicalPlan::HashJoin(HashJoin { lhs: input, .. }, Semi::Lhs)\n                | PhysicalPlan::HashJoin(HashJoin { rhs: input, .. }, Semi::Rhs) => {\n                    find(input, labels);\n                }\n                PhysicalPlan::IxJoin(IxJoin { lhs, rhs_label, .. }, Semi::All) => {\n                    find(lhs, labels);\n                    labels.push(*rhs_label);\n                }\n                PhysicalPlan::NLJoin(lhs, rhs) | PhysicalPlan::HashJoin(HashJoin { lhs, rhs, .. }, Semi::All) => {\n                    find(lhs, labels);\n                    find(rhs, labels);\n                }\n            }\n        }\n        let mut labels = vec![];\n        find(self, &mut labels);\n        labels\n    }\n\n    /// Is this operator a table scan with optional label?\n    pub fn is_table_scan(&self, label: Option<&Label>) -> bool {\n        match self {\n            Self::TableScan(_, var) => label.map(|label| var == label).unwrap_or(true),\n            _ => false,\n        }\n    }\n\n    /// Does this plan scan a table with optional label?\n    pub fn has_table_scan(&self, label: Option<&Label>) -> bool {\n        self.any(&|plan| match plan {\n            Self::TableScan(_, var) => label.map(|label| var == label).unwrap_or(true),\n            _ => false,\n        })\n    }\n\n    /// Is this operator a filter?\n    fn is_filter(&self) -> bool {\n        matches!(self, Self::Filter(..))\n    }\n\n    /// Does this plan contain a filter?\n    pub fn has_filter(&self) -> bool {\n        self.any(&|plan| plan.is_filter())\n    }\n\n    /// Is this operator a scan, index or otherwise, of a delta table?\n    pub fn is_delta_scan(&self) -> bool {\n        match self {\n            Self::TableScan(scan, _) => scan.delta.is_some(),\n            Self::IxScan(scan, _) => scan.delta.is_some(),\n            Self::Filter(input, _) => input.is_delta_scan(),\n            _ => false,\n        }\n    }\n\n    /// If this plan has any simple equality filters such as `x = 0`,\n    /// this method returns the values along with the appropriate table and column.\n    /// Note, this excludes compound equality filters such as `x = 0 and y = 1`.\n    /// Note, this must be called on an optimized plan.\n    /// Hence we must assume index scans have already been generated.\n    pub fn search_args(&self) -> Vec<(TableId, ColId, AlgebraicValue)> {\n        let mut args = vec![];\n        self.visit(&mut |op| match op {\n            PhysicalPlan::IxScan(\n                scan @ IxScan {\n                    arg: Sarg::Eq(col_id, value),\n                    ..\n                },\n                _,\n            ) if scan.prefix.is_empty() => {\n                args.push((scan.schema.table_id, *col_id, value.clone()));\n            }\n            PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, a, b)) => {\n                if let (PhysicalExpr::Field(field), PhysicalExpr::Value(value)) = (&**a, &**b) {\n                    input.visit(&mut |op| match op {\n                        PhysicalPlan::TableScan(scan, name) if *name == field.label => {\n                            args.push((scan.schema.table_id, field.field_pos.into(), value.clone()));\n                        }\n                        _ => {}\n                    });\n                }\n            }\n            _ => {}\n        });\n        args\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a single table?\n    pub fn return_table(&self) -> Option<Arc<TableSchema>> {\n        match self {\n            Self::TableScan(scan, _) => Some(scan.schema.clone()),\n            Self::IxScan(scan, _) => Some(scan.schema.clone()),\n            Self::Filter(input, _) => input.return_table(),\n            Self::IxJoin(join, Semi::Lhs) => join.lhs.return_table(),\n            Self::IxJoin(join, Semi::Rhs) => Some(join.rhs.clone()),\n            Self::HashJoin(join, Semi::Lhs) => join.lhs.return_table(),\n            Self::HashJoin(join, Semi::Rhs) => join.rhs.return_table(),\n            Self::IxJoin(_, Semi::All) | Self::HashJoin(_, Semi::All) | Self::NLJoin(..) => None,\n        }\n    }\n\n    /// Returns the [`TableSchema`] for a return label.\n    /// Returns `None` if the plan does not return this label.\n    pub fn find_table_schema(&self, name: &Label) -> Option<Arc<TableSchema>> {\n        match self {\n            Self::TableScan(scan, label) if name == label => Some(scan.schema.clone()),\n            Self::IxScan(scan, label) if name == label => Some(scan.schema.clone()),\n            Self::Filter(input, _) => input.find_table_schema(name),\n            Self::IxJoin(join, Semi::Rhs | Semi::All) if name == &join.rhs_label => Some(join.rhs.clone()),\n            Self::IxJoin(join, Semi::Lhs | Semi::All) => join.lhs.find_table_schema(name),\n            Self::HashJoin(join, Semi::Lhs) => join.lhs.find_table_schema(name),\n            Self::HashJoin(join, Semi::Rhs) => join.rhs.find_table_schema(name),\n            Self::HashJoin(HashJoin { lhs, rhs, .. }, Semi::All) | Self::NLJoin(lhs, rhs) => {\n                lhs.find_table_schema(name).or_else(|| rhs.find_table_schema(name))\n            }\n            _ => None,\n        }\n    }\n\n    /// Does this plan select or return whole (unprojected) rows from a view?\n    pub fn returns_view_table(&self) -> bool {\n        self.return_table().is_some_and(|schema| schema.is_view())\n    }\n\n    /// Does this plan read from an (anonymous) view?\n    pub fn reads_from_view(&self, anonymous: bool) -> bool {\n        self.any(&|plan| match plan {\n            Self::TableScan(scan, _) if anonymous => scan.schema.is_anonymous_view(),\n            Self::TableScan(scan, _) => scan.schema.is_view() && !scan.schema.is_anonymous_view(),\n            Self::IxScan(scan, _) if anonymous => scan.schema.is_anonymous_view(),\n            Self::IxScan(scan, _) => scan.schema.is_view() && !scan.schema.is_anonymous_view(),\n            Self::IxJoin(join, _) if anonymous => join.rhs.is_anonymous_view(),\n            Self::IxJoin(join, _) => join.rhs.is_view() && !join.rhs.is_anonymous_view(),\n            _ => false,\n        })\n    }\n\n    /// Does this plan use an event table as the lookup (rhs) table in a semi-join?\n    ///\n    /// Note, we only care about index joins because this method is only relevant for subscriptions,\n    /// and index joins are the only type of join allowed in subscriptions.\n    pub fn reads_from_event_table(&self) -> bool {\n        self.any(&|plan| match plan {\n            Self::IxJoin(join, _) => join.rhs.is_event,\n            _ => false,\n        })\n    }\n}\n\n/// Scan a table row by row, returning row ids\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct TableScan {\n    /// The table on which this index is defined\n    pub schema: Arc<TableSchema>,\n    /// Limit the number of rows scanned\n    pub limit: Option<u64>,\n    /// Is this a delta table?\n    pub delta: Option<Delta>,\n}\n\n/// Fetch and return row ids from a btree index\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct IxScan {\n    /// The table on which this index is defined\n    pub schema: Arc<TableSchema>,\n    /// Limit the number of rows scanned\n    pub limit: Option<u64>,\n    /// Is this an index scan over a delta table?\n    pub delta: Option<Delta>,\n    /// The index id\n    pub index_id: IndexId,\n    /// An equality prefix for multi-column scans\n    pub prefix: Vec<(ColId, AlgebraicValue)>,\n    /// The index argument\n    pub arg: Sarg,\n}\n\n/// An index [S]earch [arg]ument\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Sarg {\n    Eq(ColId, AlgebraicValue),\n    /// NOTE(centril): We currently never construct this variant.\n    /// We do have non-ranged hash indices.\n    /// This means that when we get around to using this variant,\n    /// we must change the rewrite rules such that we do not emit\n    /// [`IxScan`] on a hash index.\n    ///\n    /// Moreover, an equality scan (the variant above)\n    /// `(a0, b0)` on an index `(a, b, c)` is actually a ranged scan.\n    /// We also currently do not emit such `IxScan`s where the number\n    /// of equalities provided are fewer than the number of columns in the index.\n    /// When we do, we must also account for hash indices in the rewrite rules.\n    Range(ColId, Bound<AlgebraicValue>, Bound<AlgebraicValue>),\n}\n\n/// A join of two relations on a single equality condition.\n/// It builds a hash table for the rhs and streams the lhs.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct HashJoin {\n    pub lhs: Box<PhysicalPlan>,\n    pub rhs: Box<PhysicalPlan>,\n    pub lhs_field: TupleField,\n    pub rhs_field: TupleField,\n    pub unique: bool,\n}\n\n/// An index join is a left deep join tree,\n/// where the lhs is a relation,\n/// and the rhs is a relvar or base table,\n/// whose rows are fetched using an index.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct IxJoin {\n    /// The lhs input used to probe the index\n    pub lhs: Box<PhysicalPlan>,\n    /// The rhs indexed table\n    pub rhs: Arc<TableSchema>,\n    /// The rhs relvar label\n    pub rhs_label: Label,\n    /// The index id\n    pub rhs_index: IndexId,\n    /// Optional constant prefix values for multi-column index probes.\n    pub rhs_prefix: Vec<AlgebraicValue>,\n    /// The index field\n    pub rhs_field: ColId,\n    /// Is the index a unique constraint index?\n    pub unique: bool,\n    /// The expression for computing probe values.\n    /// Values are projected from the lhs,\n    /// and used to probe the index on the rhs.\n    pub lhs_field: TupleField,\n    // Is the rhs a delta table?\n    pub rhs_delta: Option<Delta>,\n}\n\n/// Is this a semijoin?\n/// If so, which side is projected?\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Semi {\n    Lhs,\n    Rhs,\n    All,\n}\n\n/// A physical scalar expression\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum PhysicalExpr {\n    /// An n-ary logic expression\n    LogOp(LogOp, Vec<PhysicalExpr>),\n    /// A binary expression\n    BinOp(BinOp, Box<PhysicalExpr>, Box<PhysicalExpr>),\n    /// A constant algebraic value\n    Value(AlgebraicValue),\n    /// A field projection expression\n    Field(TupleField),\n}\n\n/// A trait for projecting values from a tuple.\n/// This is needed because not all tuples are created equal.\n/// Some operators return [RowRef]s.\n/// Some joins return tuples of combined [RowRef]s.\npub trait ProjectField {\n    fn project(&self, field: &TupleField) -> AlgebraicValue;\n}\n\nimpl ProjectField for RowRef<'_> {\n    fn project(&self, field: &TupleField) -> AlgebraicValue {\n        self.read_col(field.field_pos).unwrap()\n    }\n}\n\nimpl ProjectField for &'_ ProductValue {\n    fn project(&self, field: &TupleField) -> AlgebraicValue {\n        self.elements[field.field_pos].clone()\n    }\n}\n\nimpl PhysicalExpr {\n    /// Walks the expression tree and calls `f` on every subexpression\n    pub fn visit(&self, f: &mut impl FnMut(&Self)) {\n        f(self);\n        match self {\n            Self::BinOp(_, a, b) => {\n                a.visit(f);\n                b.visit(f);\n            }\n            Self::LogOp(_, exprs) => {\n                for expr in exprs {\n                    expr.visit(f);\n                }\n            }\n            _ => {}\n        }\n    }\n\n    /// Walks the expression tree and calls `f` on every subexpression\n    pub fn visit_mut(&mut self, f: &mut impl FnMut(&mut Self)) {\n        f(self);\n        match self {\n            Self::BinOp(_, a, b) => {\n                a.visit_mut(f);\n                b.visit_mut(f);\n            }\n            Self::LogOp(_, exprs) => {\n                for expr in exprs {\n                    expr.visit_mut(f);\n                }\n            }\n            _ => {}\n        }\n    }\n\n    /// Applies the transformation `f` to all subplans\n    pub fn map(self, f: &impl Fn(Self) -> Self) -> Self {\n        match f(self) {\n            value @ Self::Value(..) => value,\n            field @ Self::Field(..) => field,\n            Self::BinOp(op, a, b) => Self::BinOp(op, Box::new(a.map(f)), Box::new(b.map(f))),\n            Self::LogOp(op, exprs) => Self::LogOp(op, exprs.into_iter().map(|expr| expr.map(f)).collect()),\n        }\n    }\n\n    /// Evaluate this boolean expression over `row`\n    pub fn eval_bool(&self, row: &impl ProjectField) -> bool {\n        self.eval(row).as_bool().copied().unwrap_or(false)\n    }\n\n    /// Evaluate this boolean expression over `row`\n    pub fn eval_bool_with_metrics(&self, row: &impl ProjectField, bytes_scanned: &mut usize) -> bool {\n        self.eval_with_metrics(row, bytes_scanned)\n            .as_bool()\n            .copied()\n            .unwrap_or(false)\n    }\n\n    /// Evaluate this expression over `row`\n    fn eval(&self, row: &impl ProjectField) -> Cow<'_, AlgebraicValue> {\n        self.eval_with_metrics(row, &mut 0)\n    }\n\n    /// Evaluate this expression over `row`\n    fn eval_with_metrics(&self, row: &impl ProjectField, bytes_scanned: &mut usize) -> Cow<'_, AlgebraicValue> {\n        fn eval_bin_op(op: BinOp, a: &AlgebraicValue, b: &AlgebraicValue) -> bool {\n            match op {\n                BinOp::Eq => a == b,\n                BinOp::Ne => a != b,\n                BinOp::Lt => a < b,\n                BinOp::Lte => a <= b,\n                BinOp::Gt => a > b,\n                BinOp::Gte => a >= b,\n            }\n        }\n        let into = |b| Cow::Owned(AlgebraicValue::Bool(b));\n        match self {\n            Self::BinOp(op, a, b) => into(eval_bin_op(\n                *op,\n                &a.eval_with_metrics(row, bytes_scanned),\n                &b.eval_with_metrics(row, bytes_scanned),\n            )),\n            Self::LogOp(LogOp::And, exprs) => into(\n                exprs\n                    .iter()\n                    // ALL is equivalent to AND\n                    .all(|expr| expr.eval_bool_with_metrics(row, bytes_scanned)),\n            ),\n            Self::LogOp(LogOp::Or, exprs) => into(\n                exprs\n                    .iter()\n                    // ANY is equivalent to OR\n                    .any(|expr| expr.eval_bool_with_metrics(row, bytes_scanned)),\n            ),\n            Self::Field(field) => {\n                let value = row.project(field);\n                *bytes_scanned += value.size_of();\n                Cow::Owned(value)\n            }\n            Self::Value(v) => Cow::Borrowed(v),\n        }\n    }\n\n    /// Flatten nested ANDs and ORs\n    fn flatten(self) -> Self {\n        match self {\n            Self::LogOp(op, exprs) => Self::LogOp(\n                op,\n                exprs\n                    .into_iter()\n                    .map(Self::flatten)\n                    .flat_map(|expr| match expr {\n                        Self::LogOp(nested, exprs) if nested == op => exprs,\n                        _ => vec![expr],\n                    })\n                    .collect(),\n            ),\n            Self::BinOp(op, a, b) => Self::BinOp(op, Box::new(a.flatten()), Box::new(b.flatten())),\n            Self::Field(..) | Self::Value(..) => self,\n        }\n    }\n}\n\n/// A physical context for the result of a query compilation.\npub struct PhysicalCtx<'a> {\n    pub plan: ProjectListPlan,\n    pub sql: &'a str,\n    pub source: StatementSource,\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use pretty_assertions::assert_eq;\n    use spacetimedb_expr::{\n        check::{SchemaView, TypingResult},\n        expr::ProjectName,\n        statement::{parse_and_type_sql, Statement},\n    };\n    use spacetimedb_lib::{\n        db::auth::{StAccess, StTableType},\n        identity::AuthCtx,\n        AlgebraicType, AlgebraicValue,\n    };\n    use spacetimedb_primitives::{ColId, ColList, ColSet, TableId};\n    use spacetimedb_schema::{\n        def::{BTreeAlgorithm, ConstraintData, IndexAlgorithm, UniqueConstraintData},\n        identifier::Identifier,\n        schema::{ColumnSchema, ConstraintSchema, IndexSchema, TableOrViewSchema, TableSchema},\n        table_name::TableName,\n    };\n    use spacetimedb_sql_parser::ast::BinOp;\n\n    use crate::{\n        compile::{compile_select, compile_select_list},\n        plan::{HashJoin, IxJoin, IxScan, PhysicalPlan, ProjectListPlan, Sarg, Semi, TupleField},\n    };\n\n    use super::{PhysicalExpr, ProjectPlan, TableScan};\n\n    struct SchemaViewer {\n        schemas: Vec<Arc<TableOrViewSchema>>,\n    }\n\n    impl SchemaView for SchemaViewer {\n        fn table_id(&self, name: &str) -> Option<TableId> {\n            self.schemas\n                .iter()\n                .find(|schema| schema.table_name.as_ref() == name)\n                .map(|schema| schema.table_id)\n        }\n\n        fn schema_for_table(&self, table_id: TableId) -> Option<Arc<TableOrViewSchema>> {\n            self.schemas.iter().find(|schema| schema.table_id == table_id).cloned()\n        }\n\n        fn rls_rules_for_table(&self, _: TableId) -> anyhow::Result<Vec<Box<str>>> {\n            Ok(vec![])\n        }\n    }\n\n    fn schema(\n        table_id: TableId,\n        table_name: &str,\n        columns: &[(&str, AlgebraicType)],\n        indexes: &[&[usize]],\n        unique: &[&[usize]],\n        primary_key: Option<usize>,\n    ) -> TableOrViewSchema {\n        TableOrViewSchema::from(Arc::new(TableSchema::new(\n            table_id,\n            TableName::new(Identifier::for_test(table_name)),\n            None,\n            columns\n                .iter()\n                .enumerate()\n                .map(|(i, (name, ty))| ColumnSchema {\n                    table_id,\n                    col_name: Identifier::for_test(*name),\n                    col_pos: i.into(),\n                    col_type: ty.clone(),\n                    alias: None,\n                })\n                .collect(),\n            indexes\n                .iter()\n                .enumerate()\n                .map(|(i, cols)| IndexSchema {\n                    table_id,\n                    index_id: i.into(),\n                    index_name: \"\".into(),\n                    index_algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {\n                        columns: ColList::from_iter(cols.iter().copied()),\n                    }),\n                    alias: None,\n                })\n                .collect(),\n            unique\n                .iter()\n                .enumerate()\n                .map(|(i, cols)| ConstraintSchema {\n                    table_id,\n                    constraint_id: i.into(),\n                    constraint_name: \"\".into(),\n                    data: ConstraintData::Unique(UniqueConstraintData {\n                        columns: ColSet::from_iter(cols.iter().copied()),\n                    }),\n                })\n                .collect(),\n            vec![],\n            StTableType::User,\n            StAccess::Public,\n            None,\n            primary_key.map(ColId::from),\n            false,\n            None,\n        )))\n    }\n\n    /// A wrapper around [spacetimedb_expr::check::parse_and_type_sub] that takes a dummy [AuthCtx]\n    fn parse_and_type_sub(sql: &str, tx: &impl SchemaView) -> TypingResult<ProjectName> {\n        spacetimedb_expr::check::parse_and_type_sub(sql, tx, &AuthCtx::for_testing()).map(|(plan, _)| plan)\n    }\n\n    /// No rewrites applied to a simple table scan\n    #[test]\n    fn table_scan_noop() {\n        let t_id = TableId(1);\n\n        let t = Arc::new(schema(\n            t_id,\n            \"t\",\n            &[(\"id\", AlgebraicType::U64), (\"x\", AlgebraicType::U64)],\n            &[&[0]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![t.clone()],\n        };\n\n        let sql = \"select * from t\";\n\n        let auth = AuthCtx::for_testing();\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        match pp {\n            ProjectPlan::None(PhysicalPlan::TableScan(TableScan { schema, .. }, _)) => {\n                assert_eq!(schema.table_id, t_id);\n            }\n            proj => panic!(\"unexpected project: {proj:#?}\"),\n        };\n    }\n\n    /// No rewrites applied to a table scan + filter\n    #[test]\n    fn filter_noop() {\n        let t_id = TableId(1);\n\n        let t = Arc::new(schema(\n            t_id,\n            \"t\",\n            &[(\"id\", AlgebraicType::U64), (\"x\", AlgebraicType::U64)],\n            &[&[0]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![t.clone()],\n        };\n\n        let sql = \"select * from t where x = 5\";\n\n        let auth = AuthCtx::for_testing();\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        match pp {\n            ProjectPlan::None(PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, field, value))) => {\n                assert!(matches!(*field, PhysicalExpr::Field(TupleField { field_pos: 1, .. })));\n                assert!(matches!(*value, PhysicalExpr::Value(AlgebraicValue::U64(5))));\n\n                match *input {\n                    PhysicalPlan::TableScan(TableScan { schema, .. }, _) => {\n                        assert_eq!(schema.table_id, t_id);\n                    }\n                    plan => panic!(\"unexpected plan: {plan:#?}\"),\n                }\n            }\n            proj => panic!(\"unexpected project: {proj:#?}\"),\n        };\n    }\n\n    /// Given the following operator notation:\n    ///\n    /// x:  join\n    /// p:  project\n    /// s:  select\n    /// ix: index scan\n    /// rx: right index semijoin\n    ///\n    /// This test takes the following logical plan:\n    ///\n    /// ```text\n    ///       p(b)\n    ///        |\n    ///        x\n    ///       / \\\n    ///      x   b\n    ///     / \\\n    ///    x   l\n    ///   / \\\n    /// s(u) l\n    ///  |\n    ///  u\n    /// ```\n    ///\n    /// And turns it into the following physical plan:\n    ///\n    /// ```text\n    ///         rx\n    ///        /  \\\n    ///       rx   b\n    ///      /  \\\n    ///     rx   l\n    ///    /  \\\n    /// ix(u)  l\n    /// ```\n    #[test]\n    fn index_semijoins_1() {\n        let u_id = TableId(1);\n        let l_id = TableId(2);\n        let b_id = TableId(3);\n\n        let u = Arc::new(schema(\n            u_id,\n            \"u\",\n            &[(\"identity\", AlgebraicType::U64), (\"entity_id\", AlgebraicType::U64)],\n            &[&[0], &[1]],\n            &[&[0], &[1]],\n            Some(0),\n        ));\n\n        let l = Arc::new(schema(\n            l_id,\n            \"l\",\n            &[(\"entity_id\", AlgebraicType::U64), (\"chunk\", AlgebraicType::U64)],\n            &[&[0], &[1]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let b = Arc::new(schema(\n            b_id,\n            \"b\",\n            &[(\"entity_id\", AlgebraicType::U64), (\"misc\", AlgebraicType::U64)],\n            &[&[0]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![u.clone(), l.clone(), b.clone()],\n        };\n\n        let sql = \"\n            select b.*\n            from u\n            join l as p on u.entity_id = p.entity_id\n            join l as q on p.chunk = q.chunk\n            join b on q.entity_id = b.entity_id\n            where u.identity = 5\n        \";\n        let auth = AuthCtx::for_testing();\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Plan:\n        //         rx\n        //        /  \\\n        //       rx   b\n        //      /  \\\n        //     rx   l\n        //    /  \\\n        // ix(u)  l\n        let plan = match pp {\n            ProjectPlan::None(plan) => plan,\n            proj => panic!(\"unexpected project: {proj:#?}\"),\n        };\n\n        // Plan:\n        //         rx\n        //        /  \\\n        //       rx   b\n        //      /  \\\n        //     rx   l\n        //    /  \\\n        // ix(u)  l\n        let plan = match plan {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(0),\n                    unique: true,\n                    lhs_field: TupleField { field_pos: 0, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, b_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan:\n        //       rx\n        //      /  \\\n        //     rx   l\n        //    /  \\\n        // ix(u)  l\n        let plan = match plan {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(1),\n                    unique: false,\n                    lhs_field: TupleField { field_pos: 1, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, l_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan:\n        //     rx\n        //    /  \\\n        // ix(u)  l\n        let plan = match plan {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(0),\n                    unique: true,\n                    lhs_field: TupleField { field_pos: 1, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, l_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan: ix(u)\n        match plan {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema,\n                    prefix,\n                    arg: Sarg::Eq(ColId(0), AlgebraicValue::U64(5)),\n                    ..\n                },\n                _,\n            ) => {\n                assert!(prefix.is_empty());\n                assert_eq!(schema.table_id, u_id);\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        }\n    }\n\n    /// Given the following operator notation:\n    ///\n    /// x:  join\n    /// p:  project\n    /// s:  select\n    /// ix: index scan\n    /// rx: right index semijoin\n    /// rj: right hash semijoin\n    ///\n    /// This test takes the following logical plan:\n    ///\n    /// ```text\n    ///         p(p)\n    ///          |\n    ///          x\n    ///         / \\\n    ///        x   p\n    ///       / \\\n    ///      x   s(w)\n    ///     / \\   |\n    ///    x   w  w\n    ///   / \\\n    /// s(m) m\n    ///  |\n    ///  m\n    /// ```\n    ///\n    /// And turns it into the following physical plan:\n    ///\n    /// ```text\n    ///           rx\n    ///          /  \\\n    ///         rj   p\n    ///        /  \\\n    ///       rx  ix(w)\n    ///      /  \\\n    ///     rx   w\n    ///    /  \\\n    /// ix(m)  m\n    /// ```\n    #[test]\n    fn index_semijoins_2() {\n        let m_id = TableId(1);\n        let w_id = TableId(2);\n        let p_id = TableId(3);\n\n        let m = Arc::new(schema(\n            m_id,\n            \"m\",\n            &[(\"employee\", AlgebraicType::U64), (\"manager\", AlgebraicType::U64)],\n            &[&[0], &[1]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let w = Arc::new(schema(\n            w_id,\n            \"w\",\n            &[(\"employee\", AlgebraicType::U64), (\"project\", AlgebraicType::U64)],\n            &[&[0], &[1], &[0, 1]],\n            &[&[0, 1]],\n            None,\n        ));\n\n        let p = Arc::new(schema(\n            p_id,\n            \"p\",\n            &[(\"id\", AlgebraicType::U64), (\"name\", AlgebraicType::String)],\n            &[&[0]],\n            &[&[0]],\n            Some(0),\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![m.clone(), w.clone(), p.clone()],\n        };\n\n        let sql = \"\n            select p.*\n            from m\n            join m as n on m.manager = n.manager\n            join w as u on n.employee = u.employee\n            join w as v on u.project = v.project\n            join p on p.id = v.project\n            where 5 = m.employee and 5 = v.employee\n        \";\n        let auth = AuthCtx::for_testing();\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Plan:\n        //           rx\n        //          /  \\\n        //         rj   p\n        //        /  \\\n        //       rx  ix(w)\n        //      /  \\\n        //     rx   w\n        //    /  \\\n        // ix(m)  m\n        let plan = match pp {\n            ProjectPlan::None(plan) => plan,\n            proj => panic!(\"unexpected project: {proj:#?}\"),\n        };\n\n        // Plan:\n        //           rx\n        //          /  \\\n        //         rj   p\n        //        /  \\\n        //       rx  ix(w)\n        //      /  \\\n        //     rx   w\n        //    /  \\\n        // ix(m)  m\n        let plan = match plan {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(0),\n                    unique: true,\n                    lhs_field: TupleField { field_pos: 1, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, p_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan:\n        //         rj\n        //        /  \\\n        //       rx  ix(w)\n        //      /  \\\n        //     rx   w\n        //    /  \\\n        // ix(m)  m\n        let (rhs, lhs) = match plan {\n            PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs,\n                    rhs,\n                    lhs_field: TupleField { field_pos: 1, .. },\n                    rhs_field: TupleField { field_pos: 1, .. },\n                    unique: true,\n                },\n                Semi::Rhs,\n            ) => (*rhs, *lhs),\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan: ix(w)\n        match rhs {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema,\n                    prefix,\n                    arg: Sarg::Eq(ColId(0), AlgebraicValue::U64(5)),\n                    ..\n                },\n                _,\n            ) => {\n                assert!(prefix.is_empty());\n                assert_eq!(schema.table_id, w_id);\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        }\n\n        // Plan:\n        //       rx\n        //      /  \\\n        //     rx   w\n        //    /  \\\n        // ix(m)  m\n        let plan = match lhs {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(0),\n                    unique: false,\n                    lhs_field: TupleField { field_pos: 0, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, w_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan:\n        //     rx\n        //    /  \\\n        // ix(m)  m\n        let plan = match plan {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: ColId(1),\n                    unique: false,\n                    lhs_field: TupleField { field_pos: 1, .. },\n                    ..\n                },\n                Semi::Rhs,\n            ) => {\n                assert_eq!(rhs.table_id, m_id);\n                *lhs\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        // Plan: ix(m)\n        match plan {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema,\n                    prefix,\n                    arg: Sarg::Eq(ColId(0), AlgebraicValue::U64(5)),\n                    ..\n                },\n                _,\n            ) => {\n                assert!(prefix.is_empty());\n                assert_eq!(schema.table_id, m_id);\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        }\n    }\n\n    /// Test single and multi-column index selections\n    #[test]\n    fn index_scans() {\n        let t_id = TableId(1);\n\n        let t = Arc::new(schema(\n            t_id,\n            \"t\",\n            &[\n                (\"w\", AlgebraicType::U8),\n                (\"x\", AlgebraicType::U8),\n                (\"y\", AlgebraicType::U8),\n                (\"z\", AlgebraicType::U8),\n            ],\n            &[&[1], &[2, 3], &[1, 2, 3]],\n            &[],\n            None,\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![t.clone()],\n        };\n\n        let sql = \"select * from t where x = 3 and y = 4 and z = 5\";\n        let auth = AuthCtx::for_testing();\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Select index on (x, y, z)\n        match pp {\n            ProjectPlan::None(PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            )) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(3), AlgebraicValue::U8(5)));\n                assert_eq!(\n                    prefix,\n                    vec![(ColId(1), AlgebraicValue::U8(3)), (ColId(2), AlgebraicValue::U8(4))]\n                );\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        // Test permutations of the same query\n        let sql = \"select * from t where z = 5 and y = 4 and x = 3\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        match pp {\n            ProjectPlan::None(PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            )) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(3), AlgebraicValue::U8(5)));\n                assert_eq!(\n                    prefix,\n                    vec![(ColId(1), AlgebraicValue::U8(3)), (ColId(2), AlgebraicValue::U8(4))]\n                );\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        let sql = \"select * from t where x = 3 and y = 4\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Select index on x\n        let plan = match pp {\n            ProjectPlan::None(PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, field, value))) => {\n                assert!(matches!(*field, PhysicalExpr::Field(TupleField { field_pos: 2, .. })));\n                assert!(matches!(*value, PhysicalExpr::Value(AlgebraicValue::U8(4))));\n                *input\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        match plan {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            ) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(1), AlgebraicValue::U8(3)));\n                assert!(prefix.is_empty());\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        let sql = \"select * from t where w = 5 and x = 4\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Select index on x\n        let plan = match pp {\n            ProjectPlan::None(PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, field, value))) => {\n                assert!(matches!(*field, PhysicalExpr::Field(TupleField { field_pos: 0, .. })));\n                assert!(matches!(*value, PhysicalExpr::Value(AlgebraicValue::U8(5))));\n                *input\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        match plan {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            ) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(1), AlgebraicValue::U8(4)));\n                assert!(prefix.is_empty());\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n\n        let sql = \"select * from t where y = 1\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        // Do not select index on (y, z)\n        match pp {\n            ProjectPlan::None(PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, field, value))) => {\n                assert!(matches!(*input, PhysicalPlan::TableScan(..)));\n                assert!(matches!(*field, PhysicalExpr::Field(TupleField { field_pos: 2, .. })));\n                assert!(matches!(*value, PhysicalExpr::Value(AlgebraicValue::U8(1))));\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        // Select index on [y, z]\n        let sql = \"select * from t where y = 1 and z = 2\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        match pp {\n            ProjectPlan::None(PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            )) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(3), AlgebraicValue::U8(2)));\n                assert_eq!(prefix, vec![(ColId(2), AlgebraicValue::U8(1))]);\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        // Check permutations of the same query\n        let sql = \"select * from t where z = 2 and y = 1\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        match pp {\n            ProjectPlan::None(PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            )) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(3), AlgebraicValue::U8(2)));\n                assert_eq!(prefix, vec![(ColId(2), AlgebraicValue::U8(1))]);\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        // Select index on (y, z) and filter on (w)\n        let sql = \"select * from t where w = 1 and y = 2 and z = 3\";\n        let lp = parse_and_type_sub(sql, &db).unwrap();\n        let pp = compile_select(lp).optimize(&auth).unwrap();\n\n        let plan = match pp {\n            ProjectPlan::None(PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, field, value))) => {\n                assert!(matches!(*field, PhysicalExpr::Field(TupleField { field_pos: 0, .. })));\n                assert!(matches!(*value, PhysicalExpr::Value(AlgebraicValue::U8(1))));\n                *input\n            }\n            proj => panic!(\"unexpected plan: {proj:#?}\"),\n        };\n\n        match plan {\n            PhysicalPlan::IxScan(\n                IxScan {\n                    schema, prefix, arg, ..\n                },\n                _,\n            ) => {\n                assert_eq!(schema.table_id, t_id);\n                assert_eq!(arg, Sarg::Eq(ColId(3), AlgebraicValue::U8(3)));\n                assert_eq!(prefix, vec![(ColId(2), AlgebraicValue::U8(2))]);\n            }\n            plan => panic!(\"unexpected plan: {plan:#?}\"),\n        };\n    }\n\n    #[test]\n    fn limit() {\n        let t_id = TableId(1);\n\n        let t = Arc::new(schema(\n            t_id,\n            \"t\",\n            &[(\"x\", AlgebraicType::U8), (\"y\", AlgebraicType::U8)],\n            &[&[0]],\n            &[],\n            None,\n        ));\n\n        let db = SchemaViewer {\n            schemas: vec![t.clone()],\n        };\n\n        let compile = |sql| {\n            let stmt = parse_and_type_sql(sql, &db, &AuthCtx::for_testing()).unwrap();\n            let Statement::Select(select) = stmt else {\n                unreachable!()\n            };\n            let auth = AuthCtx::for_testing();\n            compile_select_list(select).optimize(&auth).unwrap()\n        };\n\n        let plan = compile(\"select * from t limit 5\");\n\n        let ProjectListPlan::Name(mut plans) = plan else {\n            panic!(\"expected a qualified wildcard projection {{table_name}}.*\")\n        };\n\n        assert_eq!(plans.len(), 1);\n        assert!(matches!(\n            plans.pop().unwrap(),\n            ProjectPlan::None(PhysicalPlan::TableScan(TableScan { limit: Some(5), .. }, _))\n        ));\n\n        let plan = compile(\"select * from t where x = 1 limit 5\");\n\n        let ProjectListPlan::Name(mut plans) = plan else {\n            panic!(\"expected a qualified wildcard projection {{table_name}}.*\")\n        };\n\n        assert_eq!(plans.len(), 1);\n        assert!(matches!(\n            plans.pop().unwrap(),\n            ProjectPlan::None(PhysicalPlan::IxScan(IxScan { limit: Some(5), .. }, _))\n        ));\n\n        let plan = compile(\"select * from t where y = 1 limit 5\");\n\n        let ProjectListPlan::Limit(plan, 5) = plan else {\n            panic!(\"expected an outer LIMIT\")\n        };\n\n        assert!(plan.plan_iter().any(|plan| plan.has_filter()));\n        assert!(plan.plan_iter().any(|plan| plan.has_table_scan(None)));\n    }\n}\n"
  },
  {
    "path": "crates/physical-plan/src/rules.rs",
    "content": "//! This module defines the rewrite rules used for query optimization.\n//!\n//! These include:\n//!\n//! * [PushConstEq]\n//!   Push down predicates of the form `x=1`\n//! * [PushConstAnd]\n//!   Push down predicates of the form `x=1 and y=2`\n//! * [IxScanEq]\n//!   Generate 1-column index scan for `x=1`\n//! * [IxScanAnd]\n//!   Generate 1-column index scan for `x=1 and y=2`\n//! * [IxScanEq2Col]\n//!   Generate 2-column index scan\n//! * [IxScanEq3Col]\n//!   Generate 3-column index scan\n//! * [ReorderHashJoin]\n//!   Reorder the sides of a hash join\n//! * [ReorderDeltaJoinRhs]\n//!   Reorder the sides of a hash join with delta tables\n//! * [PullFilterAboveHashJoin]\n//!   Pull a filter above a hash join with delta tables\n//! * [HashToIxJoin]\n//!   Convert hash join to index join\n//! * [UniqueIxJoinRule]\n//!   Mark index join as unique\n//! * [UniqueHashJoinRule]\n//!   Mark hash join as unique\nuse anyhow::{bail, Result};\nuse spacetimedb_lib::AlgebraicValue;\nuse spacetimedb_primitives::{ColId, ColSet, IndexId};\nuse spacetimedb_schema::schema::{IndexSchema, TableSchema};\nuse spacetimedb_sql_parser::ast::{BinOp, LogOp};\n\nuse crate::plan::{\n    HashJoin, IxJoin, IxScan, Label, PhysicalExpr, PhysicalPlan, ProjectListPlan, ProjectPlan, Sarg, Semi, TableScan,\n    TupleField,\n};\n\n/// A rewrite will only fail due to an internal logic bug.\n/// However we don't want to panic in such a situation.\n/// Instead we leave it to the caller to determine how to proceed.\nconst INVARIANT_VIOLATION: &str = \"Invariant violation during query planning\";\n\npub trait RewriteRule {\n    type Plan;\n    type Info;\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info>;\n    fn rewrite(plan: Self::Plan, info: Self::Info) -> Result<Self::Plan>;\n}\n\n/// To preserve semantics while reordering operations,\n/// the physical plan assumes tuples with named labels.\n/// However positions are computed for them before execution.\n/// Note, this should always be the last rewrite.\npub(crate) struct ComputePositions;\n\nimpl RewriteRule for ComputePositions {\n    type Plan = PhysicalPlan;\n    type Info = (Label, usize);\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info> {\n        match plan {\n            PhysicalPlan::Filter(input, expr) => {\n                let mut name_and_position = None;\n                expr.visit(&mut |expr| {\n                    if let PhysicalExpr::Field(TupleField {\n                        label, label_pos: None, ..\n                    }) = expr\n                    {\n                        name_and_position = input.position(label).map(|i| (*label, i));\n                    }\n                });\n                name_and_position\n            }\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs: input,\n                    lhs_field: TupleField {\n                        label, label_pos: None, ..\n                    },\n                    ..\n                },\n                _,\n            )\n            | PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs: input,\n                    lhs_field: TupleField {\n                        label, label_pos: None, ..\n                    },\n                    ..\n                },\n                _,\n            )\n            | PhysicalPlan::HashJoin(\n                HashJoin {\n                    rhs: input,\n                    rhs_field: TupleField {\n                        label, label_pos: None, ..\n                    },\n                    ..\n                },\n                _,\n            ) => input.position(label).map(|i| (*label, i)),\n            _ => None,\n        }\n    }\n\n    fn rewrite(mut plan: Self::Plan, (name, pos): Self::Info) -> Result<Self::Plan> {\n        match &mut plan {\n            PhysicalPlan::Filter(_, expr) => {\n                expr.visit_mut(&mut |expr| match expr {\n                    PhysicalExpr::Field(t @ TupleField { label_pos: None, .. }) if t.label == name => {\n                        t.label_pos = Some(pos);\n                    }\n                    _ => {}\n                });\n            }\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs_field: t @ TupleField { label_pos: None, .. },\n                    ..\n                },\n                _,\n            )\n            | PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs_field: t @ TupleField { label_pos: None, .. },\n                    ..\n                },\n                _,\n            )\n            | PhysicalPlan::HashJoin(\n                HashJoin {\n                    rhs_field: t @ TupleField { label_pos: None, .. },\n                    ..\n                },\n                _,\n            ) => {\n                t.label_pos = Some(pos);\n            }\n            _ => {}\n        }\n        Ok(plan)\n    }\n}\n\n/// Merge a limit with a table or index scan.\n///\n/// Note that for pull-based, tuple at a time iterators,\n/// a limit is a short circuiting operator,\n/// and therefore this optimization is essentially a no-op.\n///\n/// However for executors that materialize intermediate results,\n/// this will avoid scanning the entire table.\npub(crate) struct PushLimit;\n\nimpl RewriteRule for PushLimit {\n    type Plan = ProjectListPlan;\n    type Info = ();\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info> {\n        match plan {\n            ProjectListPlan::Limit(scan, _) => {\n                match &**scan {\n                    ProjectListPlan::Name(plans) => plans.iter().any(|plan| {\n                        matches!(\n                            plan,\n                            ProjectPlan::None(\n                                PhysicalPlan::TableScan(TableScan { limit: None, .. }, _)\n                                    | PhysicalPlan::IxScan(IxScan { limit: None, .. }, _)\n                            )\n                        )\n                    }),\n                    _ => false,\n                }\n            }\n            .then_some(()),\n            _ => None,\n        }\n    }\n\n    fn rewrite(plan: Self::Plan, _: ()) -> Result<Self::Plan> {\n        let select = |plan| ProjectPlan::None(plan);\n        let limit_scan = |scan, n| match scan {\n            PhysicalPlan::TableScan(scan, alias) => {\n                select(PhysicalPlan::TableScan(\n                    TableScan {\n                        // Push limit into table scan\n                        limit: Some(n),\n                        ..scan\n                    },\n                    alias,\n                ))\n            }\n            PhysicalPlan::IxScan(scan, alias) => select(PhysicalPlan::IxScan(\n                IxScan {\n                    // Push limit into index scan\n                    limit: Some(n),\n                    ..scan\n                },\n                alias,\n            )),\n            _ => select(scan),\n        };\n        match plan {\n            ProjectListPlan::Limit(scan, n) => match *scan {\n                ProjectListPlan::Name(plans) => Ok(ProjectListPlan::Name(\n                    plans\n                        .into_iter()\n                        .map(|plan| match plan {\n                            ProjectPlan::None(\n                                scan @ PhysicalPlan::TableScan(TableScan { limit: None, .. }, _)\n                                | scan @ PhysicalPlan::IxScan(IxScan { limit: None, .. }, _),\n                            ) => limit_scan(scan, n),\n                            _ => plan,\n                        })\n                        .collect(),\n                )),\n                input => Ok(ProjectListPlan::Limit(Box::new(input), n)),\n            },\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Push constant comparisons down to the leaves.\n///\n/// Example:\n///\n/// ```sql\n/// select b.*\n/// from a join b on a.id = b.id\n/// where a.x = 3\n/// ```\n///\n/// ... to ...\n///\n/// ```sql\n/// select b.*\n/// from (select * from a where x = 3) a\n/// join b on a.id = b.id\n/// ```\n///\n/// Example:\n///\n/// ```text\n///  s(a)\n///   |\n///   x\n///  / \\\n/// a   b\n///\n/// ... to ...\n///\n///    x\n///   / \\\n/// s(a) b\n///  |\n///  a\n/// ```\npub(crate) struct PushConstEq;\n\nimpl RewriteRule for PushConstEq {\n    type Plan = PhysicalPlan;\n    type Info = Label;\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        // Is this plan a table scan followed by a sequence of filters?\n        // If so, it's already in a normalized state, so no need to push.\n        let is_filter = |plan: &PhysicalPlan| {\n            !plan.any(&|plan| !matches!(plan, PhysicalPlan::TableScan(..) | PhysicalPlan::Filter(..)))\n        };\n        if let PhysicalPlan::Filter(input, PhysicalExpr::BinOp(_, expr, value)) = plan\n            && let (PhysicalExpr::Field(TupleField { label, .. }), PhysicalExpr::Value(_)) = (&**expr, &**value)\n        {\n            return (input.has_table_scan(Some(label)) && !is_filter(input)).then_some(*label);\n        }\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, relvar: Self::Info) -> Result<PhysicalPlan> {\n        match plan {\n            PhysicalPlan::Filter(input, expr) => input.map_if(\n                |scan, _| Ok(PhysicalPlan::Filter(Box::new(scan), expr)),\n                |plan| plan.is_table_scan(Some(&relvar)).then_some(()),\n            ),\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Push constant conjunctions down to the leaves.\n///\n/// Example:\n///\n/// ```sql\n/// select b.*\n/// from a join b on a.id = b.id\n/// where a.x = 3 and a.y = 5\n/// ```\n///\n/// ... to ...\n///\n/// ```sql\n/// select b.*\n/// from (select * from a where x = 3 and y = 5) a\n/// join b on a.id = b.id\n/// ```\n///\n/// Example:\n///\n/// ```text\n///  s(a)\n///   |\n///   x\n///  / \\\n/// a   b\n///\n/// ... to ...\n///\n///    x\n///   / \\\n/// s(a) b\n///  |\n///  a\n/// ```\npub(crate) struct PushConstAnd;\n\nimpl RewriteRule for PushConstAnd {\n    type Plan = PhysicalPlan;\n    type Info = Label;\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        // Is this plan a table scan followed by a sequence of filters?\n        // If so, it's already in a normalized state, so no need to push.\n        let is_filter = |plan: &PhysicalPlan| {\n            !plan.any(&|plan| !matches!(plan, PhysicalPlan::TableScan(..) | PhysicalPlan::Filter(..)))\n        };\n        if let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan {\n            return exprs.iter().find_map(|expr| {\n                if let PhysicalExpr::BinOp(_, expr, value) = expr\n                    && let (PhysicalExpr::Field(TupleField { label, .. }), PhysicalExpr::Value(_)) = (&**expr, &**value)\n                {\n                    return (input.has_table_scan(Some(label)) && !is_filter(input)).then_some(*label);\n                }\n                None\n            });\n        }\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, relvar: Self::Info) -> Result<PhysicalPlan> {\n        match plan {\n            PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) => {\n                let mut leaf_exprs = vec![];\n                let mut root_exprs = vec![];\n                for expr in exprs {\n                    if let PhysicalExpr::BinOp(_, lhs, value) = &expr\n                        && let (PhysicalExpr::Field(TupleField { label: var, .. }), PhysicalExpr::Value(_)) =\n                            (&**lhs, &**value)\n                        && var == &relvar\n                    {\n                        leaf_exprs.push(expr);\n                        continue;\n                    }\n                    root_exprs.push(expr);\n                }\n                // Match on table or delta scan\n                let ok = |plan: &PhysicalPlan| plan.is_table_scan(Some(&relvar)).then_some(());\n                // Map scan to scan + filter\n                let f = |scan, _| {\n                    Ok(PhysicalPlan::Filter(\n                        Box::new(scan),\n                        match leaf_exprs.len() {\n                            1 => leaf_exprs.swap_remove(0),\n                            _ => PhysicalExpr::LogOp(LogOp::And, leaf_exprs),\n                        },\n                    ))\n                };\n                // Remove top level filter if all conditions were pushable\n                if root_exprs.is_empty() {\n                    return input.map_if(f, ok);\n                }\n                // Otherwise remove exprs from top level filter and push\n                Ok(PhysicalPlan::Filter(\n                    Box::new(input.map_if(f, ok)?),\n                    match root_exprs.len() {\n                        1 => root_exprs.swap_remove(0),\n                        _ => PhysicalExpr::LogOp(LogOp::And, root_exprs),\n                    },\n                ))\n            }\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Match single field equality predicates such as:\n///\n/// ```sql\n/// select * from t where x = 1\n/// ```\n///\n/// Rewrite as an index scan if applicable.\n///\n/// NOTE: This rule does not consider multi-column indexes.\npub(crate) struct IxScanEq;\n\npub(crate) struct IxScanInfo {\n    index_id: IndexId,\n    cols: Vec<(usize, ColId)>,\n}\n\nimpl RewriteRule for IxScanEq {\n    type Plan = PhysicalPlan;\n    type Info = (IndexId, ColId);\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        if let PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, expr, value)) = plan\n            && let PhysicalPlan::TableScan(\n                TableScan {\n                    schema,\n                    limit: None,\n                    delta: _,\n                },\n                _,\n            ) = &**input\n            && let (PhysicalExpr::Field(TupleField { field_pos: pos, .. }), PhysicalExpr::Value(_)) =\n                (&**expr, &**value)\n        {\n            return schema.indexes.iter().find_map(\n                |IndexSchema {\n                     index_id,\n                     index_algorithm,\n                     ..\n                 }| {\n                    // TODO: Support prefix scans.\n                    // Remember to consider non-ranged indices.\n                    if index_algorithm.columns().len() == 1 {\n                        Some((*index_id, index_algorithm.find_col_index(*pos)?))\n                    } else {\n                        None\n                    }\n                },\n            );\n        }\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, (index_id, col_id): Self::Info) -> Result<PhysicalPlan> {\n        if let PhysicalPlan::Filter(input, PhysicalExpr::BinOp(BinOp::Eq, _, value)) = plan\n            && let PhysicalPlan::TableScan(TableScan { schema, limit, delta }, var) = *input\n            && let PhysicalExpr::Value(v) = *value\n        {\n            return Ok(PhysicalPlan::IxScan(\n                IxScan {\n                    schema,\n                    limit,\n                    delta,\n                    index_id,\n                    prefix: vec![],\n                    arg: Sarg::Eq(col_id, v),\n                },\n                var,\n            ));\n        }\n        bail!(\"{INVARIANT_VIOLATION}: Failed to create single column index scan from equality condition\")\n    }\n}\n\n/// Match multi-field equality predicates such as:\n///\n/// ```sql\n/// select * from t where x = 1 and y = 1\n/// ```\n///\n/// Create an index scan for one of the equality conditions.\n///\n/// NOTE: This rule does not consider multi-column indexes.\npub(crate) struct IxScanAnd;\n\nimpl RewriteRule for IxScanAnd {\n    type Plan = PhysicalPlan;\n    type Info = (IndexId, usize, ColId);\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        if let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan\n            && let PhysicalPlan::TableScan(\n                TableScan {\n                    schema,\n                    limit: None,\n                    delta: _,\n                },\n                _,\n            ) = &**input\n        {\n            return exprs.iter().enumerate().find_map(|(i, expr)| {\n                if let PhysicalExpr::BinOp(BinOp::Eq, lhs, value) = expr\n                    && let (PhysicalExpr::Field(TupleField { field_pos: pos, .. }), PhysicalExpr::Value(_)) =\n                        (&**lhs, &**value)\n                {\n                    return schema.indexes.iter().find_map(\n                        |IndexSchema {\n                             index_id,\n                             index_algorithm,\n                             ..\n                         }| {\n                            index_algorithm\n                                .columns()\n                                // TODO: Support prefix scans\n                                .as_singleton()\n                                .filter(|col_id| col_id.idx() == *pos)\n                                .map(|col_id| (*index_id, i, col_id))\n                        },\n                    );\n                }\n                None\n            });\n        }\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, (index_id, i, col_id): Self::Info) -> Result<PhysicalPlan> {\n        if let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, mut exprs)) = plan\n            && let PhysicalPlan::TableScan(TableScan { schema, limit, delta }, label) = *input\n            && let PhysicalExpr::BinOp(BinOp::Eq, _, value) = exprs.swap_remove(i)\n            && let PhysicalExpr::Value(v) = *value\n        {\n            return Ok(PhysicalPlan::Filter(\n                Box::new(PhysicalPlan::IxScan(\n                    IxScan {\n                        schema,\n                        limit,\n                        delta,\n                        index_id,\n                        prefix: vec![],\n                        arg: Sarg::Eq(col_id, v),\n                    },\n                    label,\n                )),\n                match exprs.len() {\n                    1 => exprs.swap_remove(0),\n                    _ => PhysicalExpr::LogOp(LogOp::And, exprs),\n                },\n            ));\n        }\n        bail!(\"{INVARIANT_VIOLATION}: Failed to create single column index scan from conjunction\")\n    }\n}\n\n/// Match multi-field equality predicates such as:\n///\n/// ```sql\n/// select * from t where x = 1 and y = 1\n/// ```\n///\n/// Rewrite as a multi-column index scan if applicable.\n///\n/// NOTE: This rule does not consider indexes on 3 or more columns.\npub(crate) struct IxScanEq2Col;\n\nimpl RewriteRule for IxScanEq2Col {\n    type Plan = PhysicalPlan;\n    type Info = IxScanInfo;\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan else {\n            return None;\n        };\n\n        let PhysicalPlan::TableScan(\n            TableScan {\n                schema,\n                limit: None,\n                delta: _,\n            },\n            _,\n        ) = &**input\n        else {\n            return None;\n        };\n\n        for (i, a) in exprs.iter().enumerate() {\n            for (j, b) in exprs.iter().enumerate().filter(|(j, _)| i != *j) {\n                let (PhysicalExpr::BinOp(BinOp::Eq, a, u), PhysicalExpr::BinOp(BinOp::Eq, b, v)) = (a, b) else {\n                    continue;\n                };\n\n                let (PhysicalExpr::Field(u), PhysicalExpr::Value(_), PhysicalExpr::Field(v), PhysicalExpr::Value(_)) =\n                    (&**a, &**u, &**b, &**v)\n                else {\n                    continue;\n                };\n\n                if let Some(scan) = schema\n                    .indexes\n                    .iter()\n                    .filter(|idx| idx.index_algorithm.columns().len() == 2) // TODO: Support prefix scans\n                    .map(|idx| (idx.index_id, idx.index_algorithm.columns()))\n                    .find_map(|(index_id, columns)| {\n                        let mut columns = columns.iter();\n                        let x = columns.next()?;\n                        if x.idx() != u.field_pos {\n                            return None;\n                        }\n                        let y = columns.next()?;\n                        if y.idx() != v.field_pos {\n                            return None;\n                        }\n                        Some(IxScanInfo {\n                            index_id,\n                            cols: vec![(i, x), (j, y)],\n                        })\n                    })\n                {\n                    return Some(scan);\n                }\n            }\n        }\n\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, info: Self::Info) -> Result<PhysicalPlan> {\n        match info.cols.as_slice() {\n            [(i, a), (j, b)] => {\n                if let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan\n                    && let PhysicalPlan::TableScan(TableScan { schema, limit, delta }, label) = *input\n                    && let (Some(PhysicalExpr::BinOp(BinOp::Eq, _, u)), Some(PhysicalExpr::BinOp(BinOp::Eq, _, v))) =\n                        (exprs.get(*i), exprs.get(*j))\n                    && let (PhysicalExpr::Value(u), PhysicalExpr::Value(v)) = (&**u, &**v)\n                {\n                    return Ok(match exprs.len() {\n                        n @ 0 | n @ 1 => {\n                            bail!(\"{INVARIANT_VIOLATION}: Cannot create 2-column index scan from {n} conditions\")\n                        }\n                        // If there are only 2 conditions in this filter,\n                        // we replace the filter with an index scan.\n                        2 => PhysicalPlan::IxScan(\n                            IxScan {\n                                schema,\n                                limit,\n                                delta,\n                                index_id: info.index_id,\n                                prefix: vec![(*a, u.clone())],\n                                arg: Sarg::Eq(*b, v.clone()),\n                            },\n                            label,\n                        ),\n                        // If there are 3 conditions in this filter,\n                        // we create an index scan from 2 of them.\n                        // The original conjunction is no longer well defined,\n                        // because it only has a single operand now.\n                        // Hence we must replace it with its operand.\n                        3 => PhysicalPlan::Filter(\n                            Box::new(PhysicalPlan::IxScan(\n                                IxScan {\n                                    schema,\n                                    limit,\n                                    delta,\n                                    index_id: info.index_id,\n                                    prefix: vec![(*a, u.clone())],\n                                    arg: Sarg::Eq(*b, v.clone()),\n                                },\n                                label,\n                            )),\n                            exprs\n                                .into_iter()\n                                .enumerate()\n                                .find(|(pos, _)| pos != i && pos != j)\n                                .map(|(_, expr)| expr)\n                                .unwrap(),\n                        ),\n                        // If there are more than 3 conditions in this filter,\n                        // we remove the 2 conditions used in the index scan.\n                        // The remaining conditions still form a conjunction.\n                        _ => PhysicalPlan::Filter(\n                            Box::new(PhysicalPlan::IxScan(\n                                IxScan {\n                                    schema,\n                                    limit,\n                                    delta,\n                                    index_id: info.index_id,\n                                    prefix: vec![(*a, u.clone())],\n                                    arg: Sarg::Eq(*b, v.clone()),\n                                },\n                                label,\n                            )),\n                            PhysicalExpr::LogOp(\n                                LogOp::And,\n                                exprs\n                                    .into_iter()\n                                    .enumerate()\n                                    .filter(|(pos, _)| pos != i && pos != j)\n                                    .map(|(_, expr)| expr)\n                                    .collect(),\n                            ),\n                        ),\n                    });\n                }\n                bail!(\"{INVARIANT_VIOLATION}: Failed to create 2-column index scan\")\n            }\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Match multi-field equality predicates such as:\n///\n/// ```sql\n/// select * from t where x = 1 and y = 1 and z = 1\n/// ```\n///\n/// Rewrite as a multi-column index scan if applicable.\n///\n/// NOTE: This rule does not consider indexes on 4 or more columns.\npub(crate) struct IxScanEq3Col;\n\nimpl RewriteRule for IxScanEq3Col {\n    type Plan = PhysicalPlan;\n    type Info = IxScanInfo;\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        // Match outer plan structure\n        let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan else {\n            return None;\n        };\n\n        let PhysicalPlan::TableScan(\n            TableScan {\n                schema,\n                limit: None,\n                delta: _,\n            },\n            _,\n        ) = &**input\n        else {\n            return None;\n        };\n\n        for (i, a) in exprs.iter().enumerate() {\n            for (j, b) in exprs.iter().enumerate().filter(|(j, _)| i != *j) {\n                for (k, c) in exprs.iter().enumerate().filter(|(k, _)| i != *k && j != *k) {\n                    let (\n                        PhysicalExpr::BinOp(BinOp::Eq, a, u),\n                        PhysicalExpr::BinOp(BinOp::Eq, b, v),\n                        PhysicalExpr::BinOp(BinOp::Eq, c, w),\n                    ) = (a, b, c)\n                    else {\n                        continue;\n                    };\n\n                    let (\n                        PhysicalExpr::Field(u),\n                        PhysicalExpr::Value(_),\n                        PhysicalExpr::Field(v),\n                        PhysicalExpr::Value(_),\n                        PhysicalExpr::Field(w),\n                        PhysicalExpr::Value(_),\n                    ) = (&**a, &**u, &**b, &**v, &**c, &**w)\n                    else {\n                        continue;\n                    };\n\n                    if let Some(scan) = schema\n                        .indexes\n                        .iter()\n                        .filter(|idx| idx.index_algorithm.columns().len() == 3)\n                        .map(|idx| (idx.index_id, idx.index_algorithm.columns()))\n                        .find_map(|(index_id, columns)| {\n                            let mut columns = columns.iter();\n                            let x = columns.next()?;\n                            if x.idx() != u.field_pos {\n                                return None;\n                            }\n                            let y = columns.next()?;\n                            if y.idx() != v.field_pos {\n                                return None;\n                            }\n                            let z = columns.next()?;\n                            if z.idx() != w.field_pos {\n                                return None;\n                            }\n                            Some(IxScanInfo {\n                                index_id,\n                                cols: vec![(i, x), (j, y), (k, z)],\n                            })\n                        })\n                    {\n                        return Some(scan);\n                    }\n                }\n            }\n        }\n\n        None\n    }\n\n    fn rewrite(plan: PhysicalPlan, info: Self::Info) -> Result<PhysicalPlan> {\n        match info.cols.as_slice() {\n            [(i, a), (j, b), (k, c)] => {\n                if let PhysicalPlan::Filter(input, PhysicalExpr::LogOp(LogOp::And, exprs)) = plan\n                    && let PhysicalPlan::TableScan(TableScan { schema, limit, delta }, label) = *input\n                    && let (\n                        Some(PhysicalExpr::BinOp(BinOp::Eq, _, u)),\n                        Some(PhysicalExpr::BinOp(BinOp::Eq, _, v)),\n                        Some(PhysicalExpr::BinOp(BinOp::Eq, _, w)),\n                    ) = (exprs.get(*i), exprs.get(*j), exprs.get(*k))\n                    && let (PhysicalExpr::Value(u), PhysicalExpr::Value(v), PhysicalExpr::Value(w)) = (&**u, &**v, &**w)\n                {\n                    return Ok(match exprs.len() {\n                        n @ 0 | n @ 1 | n @ 2 => {\n                            bail!(\"{INVARIANT_VIOLATION}: Cannot create 3-column index scan from {n} conditions\")\n                        }\n                        // If there are only 3 conditions in this filter,\n                        // we replace the filter with an index scan.\n                        3 => PhysicalPlan::IxScan(\n                            IxScan {\n                                schema,\n                                limit,\n                                delta,\n                                index_id: info.index_id,\n                                prefix: vec![(*a, u.clone()), (*b, v.clone())],\n                                arg: Sarg::Eq(*c, w.clone()),\n                            },\n                            label,\n                        ),\n                        // If there are 4 conditions in this filter,\n                        // we create an index scan from 3 of them.\n                        // The original conjunction is no longer well defined,\n                        // because it only has a single operand now.\n                        // Hence we must replace it with its operand.\n                        4 => PhysicalPlan::Filter(\n                            Box::new(PhysicalPlan::IxScan(\n                                IxScan {\n                                    schema,\n                                    limit,\n                                    delta,\n                                    index_id: info.index_id,\n                                    prefix: vec![(*a, u.clone()), (*b, v.clone())],\n                                    arg: Sarg::Eq(*c, w.clone()),\n                                },\n                                label,\n                            )),\n                            exprs\n                                .into_iter()\n                                .enumerate()\n                                .find(|(pos, _)| pos != i && pos != j && pos != k)\n                                .map(|(_, expr)| expr)\n                                .unwrap(),\n                        ),\n                        // If there are more than 4 conditions in this filter,\n                        // we remove the 3 conditions used in the index scan.\n                        // The remaining conditions still form a conjunction.\n                        _ => PhysicalPlan::Filter(\n                            Box::new(PhysicalPlan::IxScan(\n                                IxScan {\n                                    schema,\n                                    limit,\n                                    delta,\n                                    index_id: info.index_id,\n                                    prefix: vec![(*a, u.clone()), (*b, v.clone())],\n                                    arg: Sarg::Eq(*c, w.clone()),\n                                },\n                                label,\n                            )),\n                            PhysicalExpr::LogOp(\n                                LogOp::And,\n                                exprs\n                                    .into_iter()\n                                    .enumerate()\n                                    .filter(|(pos, _)| pos != i && pos != j && pos != k)\n                                    .map(|(_, expr)| expr)\n                                    .collect(),\n                            ),\n                        ),\n                    });\n                }\n                bail!(\"{INVARIANT_VIOLATION}: Failed to create 3-column index scan\")\n            }\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Reorder hash joins so that selections are on the lhs.\n///\n/// ```text\n///   x\n///  / \\\n/// a  s(b)\n///     |\n///     b\n///\n/// ... to ...\n///\n///    x\n///   / \\\n/// s(b) a\n///  |\n///  b\n/// ```\npub(crate) struct ReorderHashJoin;\n\nimpl RewriteRule for ReorderHashJoin {\n    type Plan = PhysicalPlan;\n    type Info = ();\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info> {\n        match plan {\n            PhysicalPlan::HashJoin(HashJoin { lhs, rhs, .. }, Semi::All) => {\n                (matches!(&**lhs, PhysicalPlan::TableScan(TableScan { delta: None, .. }, _))\n                    && !matches!(&**rhs, PhysicalPlan::TableScan(..)))\n                .then_some(())\n            }\n            _ => None,\n        }\n    }\n\n    // Swaps both the inputs and the fields\n    fn rewrite(plan: Self::Plan, _: Self::Info) -> Result<Self::Plan> {\n        match plan {\n            PhysicalPlan::HashJoin(join, Semi::All) => Ok(PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs: join.rhs,\n                    rhs: join.lhs,\n                    lhs_field: join.rhs_field,\n                    rhs_field: join.lhs_field,\n                    unique: join.unique,\n                },\n                Semi::All,\n            )),\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Reorder a hash join if the rhs is a delta scan, but the lhs is not.\n///\n/// ```text\n///    x\n///   / \\\n/// s(b) a\n///  |\n///  b\n///\n/// ... to ...\n///\n///   x\n///  / \\\n/// a  s(b)\n///     |\n///     b\n/// ```\npub(crate) struct ReorderDeltaJoinRhs;\n\nimpl RewriteRule for ReorderDeltaJoinRhs {\n    type Plan = PhysicalPlan;\n    type Info = ();\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info> {\n        match plan {\n            PhysicalPlan::HashJoin(join, Semi::All) if join.rhs.is_delta_scan() && !join.lhs.is_delta_scan() => {\n                Some(())\n            }\n            _ => None,\n        }\n    }\n\n    // Swaps both the inputs and the fields\n    fn rewrite(plan: Self::Plan, _: Self::Info) -> Result<Self::Plan> {\n        match plan {\n            PhysicalPlan::HashJoin(join, Semi::All) => Ok(PhysicalPlan::HashJoin(\n                HashJoin {\n                    lhs: join.rhs,\n                    rhs: join.lhs,\n                    lhs_field: join.rhs_field,\n                    rhs_field: join.lhs_field,\n                    unique: join.unique,\n                },\n                Semi::All,\n            )),\n            _ => Ok(plan),\n        }\n    }\n}\n\n/// Pull a filter above a hash join if:\n///\n/// 1. The lhs is a delta scan or a selection\n/// 2. The rhs has an index for the join\n///\n/// ```text\n///   x\n///  / \\\n/// a  s(b)\n///     |\n///     b\n///\n/// ... to ...\n///\n///  s(b)\n///   |\n///   x\n///  / \\\n/// a   b\n/// ```\npub(crate) struct PullFilterAboveHashJoin;\n\nimpl RewriteRule for PullFilterAboveHashJoin {\n    type Plan = PhysicalPlan;\n    type Info = ();\n\n    fn matches(plan: &Self::Plan) -> Option<Self::Info> {\n        if let PhysicalPlan::HashJoin(\n            HashJoin {\n                lhs, rhs, rhs_field, ..\n            },\n            Semi::All,\n        ) = plan\n            && let PhysicalPlan::Filter(input, _) = &**rhs\n            && let PhysicalPlan::TableScan(TableScan { schema, .. }, _) = &**input\n        {\n            return ((lhs.is_delta_scan()\n                || matches!(**lhs, PhysicalPlan::Filter(..))\n                || matches!(**lhs, PhysicalPlan::IxScan(..)))\n                && schema.indexes.iter().any(|schema| {\n                    schema\n                        .index_algorithm\n                        .columns()\n                        .as_singleton()\n                        .is_some_and(|col_id| col_id.idx() == rhs_field.field_pos)\n                }))\n            .then_some(());\n        }\n        None\n    }\n\n    fn rewrite(plan: Self::Plan, _: Self::Info) -> Result<Self::Plan> {\n        if let PhysicalPlan::HashJoin(join, semi) = plan\n            && let PhysicalPlan::Filter(rhs, expr) = *join.rhs\n        {\n            return Ok(PhysicalPlan::Filter(\n                Box::new(PhysicalPlan::HashJoin(\n                    HashJoin {\n                        lhs: join.lhs,\n                        rhs,\n                        ..join\n                    },\n                    semi,\n                )),\n                expr,\n            ));\n        }\n        bail!(\"{INVARIANT_VIOLATION}: Failed to pull filter above hash join\")\n    }\n}\n\n/// Always prefer an index join to a hash join\npub(crate) struct HashToIxJoin;\n\n/// A filter term of the form `rhs_col = constant`.\ntype EqConstFilterTerm = (ColId, AlgebraicValue);\n\n/// Planning metadata derived while proving `HashJoin -> IxJoin` is valid.\n#[derive(Clone)]\npub(crate) struct HashToIxJoinInfo {\n    /// The index to probe on the RHS table.\n    rhs_index: IndexId,\n    /// The join column on the RHS.\n    /// This must be the final probe component for the chosen index.\n    rhs_field: ColId,\n    /// Constant leading key components for a multi-column index probe.\n    /// For an index `(a, b, c)` and join on `c`, this stores `[a_const, b_const]`.\n    rhs_prefix: Vec<AlgebraicValue>,\n    /// RHS filter terms consumed into `rhs_prefix` and therefore removable\n    /// from the residual filter.\n    consumed_filter_terms: Vec<EqConstFilterTerm>,\n}\n\n/// Collect RHS predicates of the form `rhs.col = constant`.\n///\n/// This is intentionally narrow: only equality predicates over RHS fields are\n/// useful for building exact prefix probes into multi-column indexes.\nfn rhs_eq_constants(expr: &PhysicalExpr, rhs_label: Label) -> Vec<EqConstFilterTerm> {\n    match expr {\n        PhysicalExpr::BinOp(BinOp::Eq, lhs, rhs)\n            if matches!(\n                (&**lhs, &**rhs),\n                (PhysicalExpr::Field(TupleField { label, .. }), PhysicalExpr::Value(_)) if *label == rhs_label\n            ) =>\n        {\n            match (&**lhs, &**rhs) {\n                (PhysicalExpr::Field(TupleField { field_pos, .. }), PhysicalExpr::Value(value)) => {\n                    vec![(ColId(*field_pos as u16), value.clone())]\n                }\n                _ => vec![],\n            }\n        }\n        PhysicalExpr::LogOp(LogOp::And, exprs) => exprs\n            .iter()\n            .flat_map(|expr| rhs_eq_constants(expr, rhs_label))\n            .collect(),\n        _ => vec![],\n    }\n}\n\n/// Collect equality constants from a list of filter expressions.\nfn rhs_eq_constants_from_filters<'a>(\n    filters: impl IntoIterator<Item = &'a PhysicalExpr>,\n    rhs_label: Label,\n) -> Vec<EqConstFilterTerm> {\n    filters\n        .into_iter()\n        .flat_map(|expr| rhs_eq_constants(expr, rhs_label))\n        .collect()\n}\n\n/// Peel a chain of `Filter` nodes and return:\n/// 1) the non-filter base plan\n/// 2) all filter expressions in that chain\nfn peel_filters_ref(mut plan: &PhysicalPlan) -> (&PhysicalPlan, Vec<&PhysicalExpr>) {\n    let mut filters = Vec::new();\n    while let PhysicalPlan::Filter(input, expr) = plan {\n        filters.push(expr);\n        plan = input;\n    }\n    (plan, filters)\n}\n\n/// Owned variant of `peel_filters_ref`.\nfn peel_filters_owned(mut plan: PhysicalPlan) -> (PhysicalPlan, Vec<PhysicalExpr>) {\n    let mut filters = Vec::new();\n    while let PhysicalPlan::Filter(input, expr) = plan {\n        filters.push(expr);\n        plan = *input;\n    }\n    (plan, filters)\n}\n\nfn and_expr(exprs: Vec<PhysicalExpr>) -> Option<PhysicalExpr> {\n    match exprs.len() {\n        0 => None,\n        1 => exprs.into_iter().next(),\n        _ => Some(PhysicalExpr::LogOp(LogOp::And, exprs)),\n    }\n}\n\nfn ix_join_candidate(\n    schema: &TableSchema,\n    rhs_field_pos: usize,\n    constants: &[EqConstFilterTerm],\n) -> Option<HashToIxJoinInfo> {\n    let rhs_field = ColId(rhs_field_pos as u16);\n    schema.indexes.iter().find_map(|ix| {\n        let cols = ix.index_algorithm.columns();\n        // For point-probe index joins we require:\n        // 1) join key is the last index column,\n        // 2) every leading index column is fixed by an RHS equality constant.\n        if cols.iter().last()? != rhs_field {\n            return None;\n        };\n\n        let mut rhs_prefix: Vec<AlgebraicValue> = vec![];\n        let mut consumed_filter_terms: Vec<EqConstFilterTerm> = vec![];\n        for col in cols.iter().take(cols.len().saturating_sub(1).into()) {\n            let (_, value) = constants.iter().find(|(candidate, _)| *candidate == col)?;\n            rhs_prefix.push(value.clone());\n            consumed_filter_terms.push((col, value.clone()));\n        }\n\n        Some(HashToIxJoinInfo {\n            rhs_index: ix.index_id,\n            rhs_field,\n            rhs_prefix,\n            consumed_filter_terms,\n        })\n    })\n}\n\n/// Extract equality constants encoded directly in an `IxScan`.\n///\n/// We only support `Eq` SARGs here because index joins require exact probe\n/// values for all leading index columns.\nfn rhs_eq_constants_from_ixscan(scan: &IxScan) -> Option<Vec<EqConstFilterTerm>> {\n    let mut constants = scan\n        .prefix\n        .iter()\n        .map(|(col, value)| (*col, value.clone()))\n        .collect::<Vec<_>>();\n\n    let Sarg::Eq(col, value) = &scan.arg else {\n        return None;\n    };\n    constants.push((*col, value.clone()));\n    Some(constants)\n}\n\n/// Ensure we do not drop predicates that were previously represented by an\n/// `IxScan` when rewriting to an `IxJoin`.\nfn all_terms_consumed(required: &[EqConstFilterTerm], consumed: &[EqConstFilterTerm]) -> bool {\n    required\n        .iter()\n        .all(|term| consumed.iter().any(|consumed_term| consumed_term == term))\n}\n\nfn remove_consumed_filter_terms(\n    expr: PhysicalExpr,\n    rhs_label: Label,\n    consumed_filter_terms: &[EqConstFilterTerm],\n) -> Option<PhysicalExpr> {\n    // These terms are now represented by `rhs_prefix`; keeping them in a\n    // residual filter is redundant work.\n    let is_consumed = |col_id: ColId, value: &AlgebraicValue| {\n        consumed_filter_terms\n            .iter()\n            .any(|(consumed_col, consumed_val)| consumed_col == &col_id && consumed_val == value)\n    };\n\n    match expr {\n        PhysicalExpr::BinOp(BinOp::Eq, lhs, rhs)\n            if matches!(\n                (&*lhs, &*rhs),\n                (PhysicalExpr::Field(TupleField { label, field_pos, .. }), PhysicalExpr::Value(value))\n                    if *label == rhs_label && is_consumed(ColId(*field_pos as u16), value)\n            ) =>\n        {\n            None\n        }\n        PhysicalExpr::LogOp(LogOp::And, exprs) => {\n            let mut kept: Vec<_> = exprs\n                .into_iter()\n                .filter_map(|expr| remove_consumed_filter_terms(expr, rhs_label, consumed_filter_terms))\n                .collect();\n\n            match kept.len() {\n                0 => None,\n                1 => Some(kept.swap_remove(0)),\n                _ => Some(PhysicalExpr::LogOp(LogOp::And, kept)),\n            }\n        }\n        expr => Some(expr),\n    }\n}\n\nimpl RewriteRule for HashToIxJoin {\n    type Plan = PhysicalPlan;\n    type Info = HashToIxJoinInfo;\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        match plan {\n            PhysicalPlan::HashJoin(\n                HashJoin {\n                    rhs,\n                    rhs_field: TupleField { field_pos, .. },\n                    ..\n                },\n                _,\n            ) => match &**rhs {\n                PhysicalPlan::TableScan(\n                    TableScan {\n                        schema,\n                        limit: None,\n                        delta: _,\n                    },\n                    _,\n                ) => ix_join_candidate(schema, *field_pos, &[]),\n                PhysicalPlan::IxScan(\n                    scan @ IxScan {\n                        schema,\n                        limit: None,\n                        delta: _,\n                        ..\n                    },\n                    _,\n                ) => {\n                    // If earlier rules already lowered an RHS filter to `IxScan(sender = const)`,\n                    // reuse those constants when proving that a multi-column join index can be probed.\n                    let constants = rhs_eq_constants_from_ixscan(scan)?;\n                    let info = ix_join_candidate(schema, *field_pos, &constants)?;\n                    // Guardrail: do not rewrite unless every scan-encoded predicate is\n                    // represented in the chosen `rhs_prefix` + join key.\n                    all_terms_consumed(&constants, &info.consumed_filter_terms).then_some(info)\n                }\n                _ => {\n                    // We prefer to pull filters above an index join where possible.\n                    let (base, filters) = peel_filters_ref(rhs);\n                    match base {\n                        PhysicalPlan::TableScan(\n                            TableScan {\n                                schema,\n                                limit: None,\n                                delta: _,\n                            },\n                            rhs_label,\n                        ) => {\n                            let constants = rhs_eq_constants_from_filters(filters, *rhs_label);\n                            ix_join_candidate(schema, *field_pos, &constants)\n                        }\n                        _ => None,\n                    }\n                }\n            },\n            PhysicalPlan::Filter(input, expr) => {\n                let PhysicalPlan::HashJoin(\n                    HashJoin {\n                        rhs,\n                        rhs_field: TupleField { field_pos, .. },\n                        ..\n                    },\n                    _,\n                ) = &**input\n                else {\n                    return None;\n                };\n\n                let PhysicalPlan::TableScan(\n                    TableScan {\n                        schema,\n                        limit: None,\n                        delta: _,\n                    },\n                    rhs_label,\n                ) = &**rhs\n                else {\n                    return None;\n                };\n\n                let constants = rhs_eq_constants(expr, *rhs_label);\n                ix_join_candidate(schema, *field_pos, &constants)\n            }\n            _ => None,\n        }\n    }\n\n    fn rewrite(plan: PhysicalPlan, info: Self::Info) -> Result<PhysicalPlan> {\n        match plan {\n            PhysicalPlan::HashJoin(join, semi) => {\n                let HashToIxJoinInfo {\n                    rhs_index,\n                    rhs_field,\n                    rhs_prefix,\n                    consumed_filter_terms,\n                } = info;\n\n                let (rhs_plan, rhs_filters) = peel_filters_owned(*join.rhs);\n                let (rhs, rhs_label, rhs_delta) = match rhs_plan {\n                    PhysicalPlan::TableScan(\n                        TableScan {\n                            schema: rhs,\n                            limit: None,\n                            delta: rhs_delta,\n                        },\n                        rhs_label,\n                    ) => (rhs, rhs_label, rhs_delta),\n                    PhysicalPlan::IxScan(\n                        IxScan {\n                            schema: rhs,\n                            limit: None,\n                            delta: rhs_delta,\n                            ..\n                        },\n                        rhs_label,\n                    ) => (rhs, rhs_label, rhs_delta),\n                    _ => bail!(\"{INVARIANT_VIOLATION}: Failed to rewrite hash join as index join\"),\n                };\n\n                let ix_join = PhysicalPlan::IxJoin(\n                    IxJoin {\n                        lhs: join.lhs,\n                        rhs,\n                        rhs_label,\n                        rhs_index,\n                        rhs_prefix,\n                        rhs_field,\n                        rhs_delta,\n                        unique: false,\n                        lhs_field: join.lhs_field,\n                    },\n                    semi,\n                );\n\n                let residual_filters = rhs_filters\n                    .into_iter()\n                    .filter_map(|expr| remove_consumed_filter_terms(expr, rhs_label, &consumed_filter_terms))\n                    .collect();\n\n                if let Some(expr) = and_expr(residual_filters) {\n                    Ok(PhysicalPlan::Filter(Box::new(ix_join), expr))\n                } else {\n                    Ok(ix_join)\n                }\n            }\n            PhysicalPlan::Filter(input, expr) => {\n                let HashToIxJoinInfo {\n                    rhs_index,\n                    rhs_field,\n                    rhs_prefix,\n                    consumed_filter_terms,\n                } = info;\n\n                let PhysicalPlan::HashJoin(join, semi) = *input else {\n                    bail!(\"{INVARIANT_VIOLATION}: Failed to rewrite filtered hash join as index join\");\n                };\n\n                let PhysicalPlan::TableScan(\n                    TableScan {\n                        schema: rhs,\n                        limit: None,\n                        delta: rhs_delta,\n                    },\n                    rhs_label,\n                ) = *join.rhs\n                else {\n                    bail!(\"{INVARIANT_VIOLATION}: Failed to rewrite filtered hash join as index join\");\n                };\n\n                let ix_join = PhysicalPlan::IxJoin(\n                    IxJoin {\n                        lhs: join.lhs,\n                        rhs,\n                        rhs_label,\n                        rhs_index,\n                        rhs_prefix,\n                        rhs_field,\n                        rhs_delta,\n                        unique: false,\n                        lhs_field: join.lhs_field,\n                    },\n                    semi,\n                );\n\n                if let Some(expr) = remove_consumed_filter_terms(expr, rhs_label, &consumed_filter_terms) {\n                    Ok(PhysicalPlan::Filter(Box::new(ix_join), expr))\n                } else {\n                    Ok(ix_join)\n                }\n            }\n            _ => bail!(\"{INVARIANT_VIOLATION}: Failed to rewrite hash join as index join\"),\n        }\n    }\n}\n\n/// Does this index join use a unique index?\npub(crate) struct UniqueIxJoinRule;\n\nimpl RewriteRule for UniqueIxJoinRule {\n    type Plan = PhysicalPlan;\n    type Info = ();\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        if let PhysicalPlan::IxJoin(\n            IxJoin {\n                unique: false,\n                rhs,\n                rhs_field,\n                ..\n            },\n            _,\n        ) = plan\n        {\n            return rhs\n                .constraints\n                .iter()\n                .filter_map(|cs| cs.data.unique_columns())\n                .filter_map(|cols| cols.as_singleton())\n                .find(|col_id| col_id == rhs_field)\n                .map(|_| ());\n        }\n        None\n    }\n\n    fn rewrite(mut plan: PhysicalPlan, _: Self::Info) -> Result<PhysicalPlan> {\n        if let PhysicalPlan::IxJoin(IxJoin { unique, .. }, _) = &mut plan {\n            *unique = true;\n        }\n        Ok(plan)\n    }\n}\n\n/// Does probing the hash table return at most one element?\npub(crate) struct UniqueHashJoinRule;\n\nimpl RewriteRule for UniqueHashJoinRule {\n    type Plan = PhysicalPlan;\n    type Info = ();\n\n    fn matches(plan: &PhysicalPlan) -> Option<Self::Info> {\n        if let PhysicalPlan::HashJoin(\n            HashJoin {\n                unique: false,\n                rhs,\n                rhs_field:\n                    TupleField {\n                        label: rhs_label,\n                        field_pos: rhs_field_pos,\n                        ..\n                    },\n                ..\n            },\n            _,\n        ) = plan\n        {\n            return rhs\n                .returns_distinct_values(rhs_label, &ColSet::from(ColId(*rhs_field_pos as u16)))\n                .then_some(());\n        }\n        None\n    }\n\n    fn rewrite(mut plan: PhysicalPlan, _: Self::Info) -> Result<PhysicalPlan> {\n        if let PhysicalPlan::HashJoin(HashJoin { unique, .. }, _) = &mut plan {\n            *unique = true;\n        }\n        Ok(plan)\n    }\n}\n"
  },
  {
    "path": "crates/primitives/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-primitives\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Primitives such as TableId and ColumnIndexAttribute\"\nrust-version.workspace = true\n\n[features]\nmemory-usage = [\"dep:spacetimedb-memory-usage\"]\n\n[dependencies]\nbitflags.workspace = true\neither.workspace = true\nenum-as-inner.workspace = true\nnohash-hasher.workspace = true\nitertools.workspace = true\nspacetimedb-memory-usage = { workspace = true, optional = true, default-features = false }\n\n[dev-dependencies]\nproptest.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/primitives/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/primitives/proptest-regressions/col_list.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 70b4e531ebb215f2a8efd7d316970aa75d0ec8d555411bd73d1c63c33d8ea1ed # shrinks to cols = [ColId(19), ColId(0)]\ncc 4f5950efc2ad78c3b31cc3f50158d88c10da3229fabdca6ce3e6ceab665e19c8 # shrinks to cols = [ColId(39), ColId(39)]\n"
  },
  {
    "path": "crates/primitives/src/attr.rs",
    "content": "//! Constraints and Column attributes.\n//!\n//! ## For tables\n//!\n//! The database engine support a sub-set of constraints defined by the `SQL` standard:\n//!\n//! ### UNIQUE:\n//!\n//!  One or many columns with enforced uniqueness using an auto-generate index.\n//!\n//! ### IDENTITY:\n//!\n//! A column that is made up of values generated by the database using a sequence.\n//!\n//!  Differs from `PRIMARY_KEY` in that its values are managed by the database and usually cannot be modified.\n//!\n//! ### PRIMARY_KEY:\n//!\n//! One or many columns that uniquely identifies each record in a table.\n//!\n//! Enforce uniqueness using an auto-generate index.\n//!\n//! Can only be one per-table.\n//!\n//! ## For Columns\n//!\n//! Additionally, is possible to add markers to columns as:\n//!\n//! - AUTO_INC: Auto-generate a sequence\n//! - INDEXED: Auto-generate a non-unique index\n//! - PRIMARY_KEY_AUTO: Make it a `PRIMARY_KEY` + `AUTO_INC`\n//! - PRIMARY_KEY_IDENTITY: Make it a `PRIMARY_KEY` + `IDENTITY`\n//!\n//! NOTE: We have [ConstraintKind] and [AttributeKind] intentionally semi-duplicated because\n//! the first is for the [Constrains] that are per-table and the second is for markers of the column.\n\n//TODO: This needs a proper refactor, and use types for `column attributes` and `table tributes`\n/// The assigned constraint for a `Table`\n#[allow(non_camel_case_types)]\n#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]\npub enum ConstraintKind {\n    UNSET,\n    ///  Index no unique\n    INDEXED,\n    /// Index unique\n    UNIQUE,\n    /// Unique + AutoInc\n    IDENTITY,\n    /// Primary key column (implies Unique)\n    PRIMARY_KEY,\n    /// PrimaryKey + AutoInc\n    PRIMARY_KEY_AUTO,\n    /// PrimaryKey + Identity\n    PRIMARY_KEY_IDENTITY,\n}\n\n/// The assigned constraint OR auto-inc marker for a `Column`\n#[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n#[derive(Eq, PartialEq)]\npub enum AttributeKind {\n    UNSET,\n    ///  Index no unique\n    INDEXED,\n    ///  Auto Increment\n    AUTO_INC,\n    /// Index unique\n    UNIQUE,\n    /// Unique + AutoInc\n    IDENTITY,\n    /// Primary key column (implies Unique).\n    PRIMARY_KEY,\n    /// PrimaryKey + AutoInc\n    PRIMARY_KEY_AUTO,\n    /// PrimaryKey + Identity\n    PRIMARY_KEY_IDENTITY,\n}\n\nbitflags::bitflags! {\n    #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]\n    pub struct ColumnAttribute: u8 {\n        const UNSET = Self::empty().bits();\n        ///  Index no unique\n        const INDEXED = 0b0001;\n        /// Generate the next [Sequence]\n        const AUTO_INC = 0b0010;\n        /// Index unique\n        const UNIQUE = Self::INDEXED.bits() | 0b0100;\n        /// Unique + AutoInc\n        const IDENTITY = Self::UNIQUE.bits() | Self::AUTO_INC.bits();\n        /// Primary key column (implies Unique)\n        const PRIMARY_KEY = Self::UNIQUE.bits() | 0b1000;\n        /// PrimaryKey + AutoInc\n        const PRIMARY_KEY_AUTO = Self::PRIMARY_KEY.bits() | Self::AUTO_INC.bits();\n         /// PrimaryKey + Identity\n        const PRIMARY_KEY_IDENTITY = Self::PRIMARY_KEY.bits() | Self::IDENTITY.bits() ;\n    }\n}\n\nimpl ColumnAttribute {\n    /// Checks if either 'IDENTITY' or 'PRIMARY_KEY_AUTO' constraints are set because the imply the use of\n    /// auto increment sequence.\n    pub const fn has_autoinc(&self) -> bool {\n        self.contains(Self::IDENTITY) || self.contains(Self::PRIMARY_KEY_AUTO) || self.contains(Self::AUTO_INC)\n    }\n\n    /// Checks if the 'UNIQUE' constraint is set.\n    pub const fn has_unique(&self) -> bool {\n        self.contains(Self::UNIQUE)\n    }\n\n    /// Checks if the 'INDEXED' constraint is set.\n    pub const fn has_indexed(&self) -> bool {\n        self.contains(ColumnAttribute::INDEXED)\n    }\n\n    /// Checks if the 'PRIMARY_KEY' constraint is set.\n    pub const fn has_primary_key(&self) -> bool {\n        self.contains(ColumnAttribute::PRIMARY_KEY)\n            || self.contains(ColumnAttribute::PRIMARY_KEY_AUTO)\n            || self.contains(ColumnAttribute::PRIMARY_KEY_IDENTITY)\n    }\n\n    /// Returns the [ColumnAttribute] of constraints as an enum variant.\n    ///\n    /// NOTE: This represent the higher possible representation of a constraints, so for example\n    /// `IDENTITY` imply that is `INDEXED, UNIQUE`\n    pub fn kind(&self) -> AttributeKind {\n        match *self {\n            x if x == Self::UNSET => AttributeKind::UNSET,\n            x if x == Self::INDEXED => AttributeKind::INDEXED,\n            x if x == Self::UNIQUE => AttributeKind::UNIQUE,\n            x if x == Self::AUTO_INC => AttributeKind::AUTO_INC,\n            x if x == Self::IDENTITY => AttributeKind::IDENTITY,\n            x if x == Self::PRIMARY_KEY => AttributeKind::PRIMARY_KEY,\n            x if x == Self::PRIMARY_KEY_AUTO => AttributeKind::PRIMARY_KEY_AUTO,\n            x if x == Self::PRIMARY_KEY_IDENTITY => AttributeKind::PRIMARY_KEY_IDENTITY,\n            x => unreachable!(\"Unexpected value {x:?}\"),\n        }\n    }\n}\n\n/// Represents constraints for a database table. May apply to multiple columns.\n#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]\npub struct Constraints {\n    attr: ColumnAttribute,\n}\n\nimpl Constraints {\n    /// Creates a new `Constraints` instance with the given `attr` flags.\n    #[inline(always)]\n    const fn new(attr: ColumnAttribute) -> Self {\n        Self { attr }\n    }\n\n    /// Creates a new `Constraints` instance that is [`Self::unique`] if `is_unique`\n    /// and [`Self::indexed`] otherwise.\n    pub const fn from_is_unique(is_unique: bool) -> Self {\n        if is_unique {\n            Self::unique()\n        } else {\n            Self::indexed()\n        }\n    }\n\n    /// Creates a new `Constraints` instance with no constraints set.\n    pub const fn unset() -> Self {\n        Self::new(ColumnAttribute::UNSET)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::INDEXED] set.\n    pub const fn indexed() -> Self {\n        Self::new(ColumnAttribute::INDEXED)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::UNIQUE] constraint set.\n    pub const fn unique() -> Self {\n        Self::new(ColumnAttribute::UNIQUE)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::IDENTITY] set.\n    pub const fn identity() -> Self {\n        Self::new(ColumnAttribute::IDENTITY)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::PRIMARY_KEY] set.\n    pub const fn primary_key() -> Self {\n        Self::new(ColumnAttribute::PRIMARY_KEY)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::PRIMARY_KEY_AUTO] set.\n    pub const fn primary_key_auto() -> Self {\n        Self::new(ColumnAttribute::PRIMARY_KEY_AUTO)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::PRIMARY_KEY_IDENTITY] set.\n    pub const fn primary_key_identity() -> Self {\n        Self::new(ColumnAttribute::PRIMARY_KEY_IDENTITY)\n    }\n\n    /// Creates a new `Constraints` instance with [ColumnAttribute::AUTO_INC] set.\n    pub const fn auto_inc() -> Self {\n        Self::new(ColumnAttribute::AUTO_INC)\n    }\n\n    /// Adds a constraint to the existing constraints.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use spacetimedb_primitives::Constraints;\n    ///\n    /// let constraints = Constraints::unset().push(Constraints::indexed());\n    /// assert!(constraints.has_indexed());\n    /// ```\n    pub fn push(self, other: Constraints) -> Self {\n        Self::new(self.attr | other.attr)\n    }\n\n    /// Add auto-increment constraint to the existing constraints.\n    /// Returns Err if the result would not be valid.\n    #[allow(clippy::result_unit_err)]\n    pub fn push_auto_inc(self) -> Result<Self, ()> {\n        Self::try_from(self.attr | ColumnAttribute::AUTO_INC)\n    }\n\n    /// Returns the bits representing the constraints.\n    pub const fn bits(&self) -> u8 {\n        self.attr.bits()\n    }\n\n    /// Returns the [ConstraintKind] of constraints as an enum variant.\n    ///\n    /// NOTE: This represent the higher possible representation of a constraints, so for example\n    /// `IDENTITY` imply that is `INDEXED, UNIQUE`\n    pub fn kind(&self) -> ConstraintKind {\n        match self {\n            x if x.attr == ColumnAttribute::UNSET => ConstraintKind::UNSET,\n            x if x.attr == ColumnAttribute::INDEXED => ConstraintKind::INDEXED,\n            x if x.attr == ColumnAttribute::UNIQUE => ConstraintKind::UNIQUE,\n            x if x.attr == ColumnAttribute::IDENTITY => ConstraintKind::IDENTITY,\n            x if x.attr == ColumnAttribute::PRIMARY_KEY => ConstraintKind::PRIMARY_KEY,\n            x if x.attr == ColumnAttribute::PRIMARY_KEY_AUTO => ConstraintKind::PRIMARY_KEY_AUTO,\n            x if x.attr == ColumnAttribute::PRIMARY_KEY_IDENTITY => ConstraintKind::PRIMARY_KEY_IDENTITY,\n            x => unreachable!(\"Unexpected value {x:?}\"),\n        }\n    }\n\n    pub fn contains(&self, other: &Self) -> bool {\n        self.attr.contains(other.attr)\n    }\n\n    /// Checks if the 'UNIQUE' constraint is set.\n    pub const fn has_unique(&self) -> bool {\n        self.attr.has_unique()\n    }\n\n    /// Checks if the 'INDEXED' constraint is set.\n    pub const fn has_indexed(&self) -> bool {\n        self.attr.has_indexed()\n    }\n\n    /// Checks if either 'IDENTITY' or 'PRIMARY_KEY_AUTO' constraints are set because the imply the use of\n    /// auto increment sequence.\n    pub const fn has_autoinc(&self) -> bool {\n        self.attr.has_autoinc()\n    }\n\n    /// Checks if the 'PRIMARY_KEY' constraint is set.\n    pub const fn has_primary_key(&self) -> bool {\n        self.attr.has_primary_key()\n    }\n}\n\nimpl TryFrom<u8> for Constraints {\n    type Error = ();\n    fn try_from(v: u8) -> Result<Self, Self::Error> {\n        ColumnAttribute::from_bits(v).ok_or(()).map(Self::new)\n    }\n}\n\nimpl TryFrom<ColumnAttribute> for Constraints {\n    type Error = ();\n\n    fn try_from(value: ColumnAttribute) -> Result<Self, Self::Error> {\n        Ok(match value.kind() {\n            AttributeKind::UNSET => Self::unset(),\n            AttributeKind::INDEXED => Self::indexed(),\n            AttributeKind::UNIQUE => Self::unique(),\n            AttributeKind::IDENTITY => Self::identity(),\n            AttributeKind::PRIMARY_KEY => Self::primary_key(),\n            AttributeKind::PRIMARY_KEY_AUTO => Self::primary_key_auto(),\n            AttributeKind::PRIMARY_KEY_IDENTITY => Self::primary_key_identity(),\n            AttributeKind::AUTO_INC => return Err(()),\n        })\n    }\n}\n"
  },
  {
    "path": "crates/primitives/src/col_list.rs",
    "content": "#![forbid(unsafe_op_in_unsafe_fn)]\n\nextern crate alloc;\n\nuse crate::ColId;\nuse alloc::alloc::{alloc, dealloc, handle_alloc_error, realloc};\nuse core::{\n    alloc::Layout,\n    cmp::Ordering,\n    fmt,\n    hash::{Hash, Hasher},\n    iter,\n    mem::{size_of, ManuallyDrop},\n    ops::{Deref, DerefMut},\n    ptr::NonNull,\n    slice::{from_raw_parts, from_raw_parts_mut},\n};\nuse either::Either;\nuse itertools::Itertools;\n\n/// Constructs a `ColList` like so `col_list![0, 2]`.\n///\n/// Mostly provided for testing.\n#[macro_export]\nmacro_rules! col_list {\n    ($($elem:expr),* $(,)?) => {{\n        $crate::ColList::from([$($elem),*])\n    }};\n}\n\n/// This represents a list of [`ColId`]s\n/// but packed into a `u64` in a way that takes advantage of the fact that\n/// in almost all cases, we won't store a `ColId` larger than 62.\n/// In the rare case that we store larger ids, we fall back to a thin vec approach.\n///\n/// We also fall back to a thin vec if the ids stored are not in sorted order, from low to high,\n/// or if the list contains duplicates.\n///\n/// If you want a set of columns, use [`ColSet`] instead. It is more likely to be compressed,\n/// and so is a better choice if you don't require ordering information.\n#[repr(C)]\npub union ColList {\n    /// Used to determine whether the list is stored inline or not.\n    check: usize,\n    /// The list is stored inline as a bitset.\n    inline: ColListInline,\n    /// A heap allocated version of the list.\n    heap: ManuallyDrop<ColListVec>,\n}\n\n// SAFETY: The data is owned by `ColList` so this is OK.\nunsafe impl Sync for ColList {}\n// SAFETY: The data is owned by `ColList` so this is OK.\nunsafe impl Send for ColList {}\n\nimpl<C: Into<ColId>> From<C> for ColList {\n    fn from(value: C) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl<C: Into<ColId>, const N: usize> From<[C; N]> for ColList {\n    fn from(cols: [C; N]) -> Self {\n        cols.map(|c| c.into()).into_iter().collect()\n    }\n}\n\nimpl<C: Into<ColId>> FromIterator<C> for ColList {\n    fn from_iter<I: IntoIterator<Item = C>>(iter: I) -> Self {\n        let iter = iter.into_iter();\n        let (lower_bound, _) = iter.size_hint();\n        let mut list = Self::with_capacity(lower_bound as u16);\n        list.extend(iter);\n        list\n    }\n}\n\nimpl<C: Into<ColId>> Extend<C> for ColList {\n    fn extend<T: IntoIterator<Item = C>>(&mut self, iter: T) {\n        let iter = iter.into_iter();\n        for col in iter {\n            self.push(col.into());\n        }\n    }\n}\n\nimpl Default for ColList {\n    fn default() -> Self {\n        Self::with_capacity(0)\n    }\n}\n\nimpl ColList {\n    /// Returns an empty list.\n    pub fn empty() -> Self {\n        Self::from_inline(0)\n    }\n\n    /// Returns a list with a single column.\n    /// As long `col` is below `62`, this will not allocate.\n    pub fn new(col: ColId) -> Self {\n        let mut list = Self::from_inline(0);\n        list.push_inner(col, true);\n        list\n    }\n\n    /// Returns an empty list with a capacity to hold `cap` elements.\n    pub fn with_capacity(cap: u16) -> Self {\n        // We speculate that all elements < `Self::FIRST_HEAP_COL`.\n        if cap < Self::FIRST_HEAP_COL_U16 {\n            Self::from_inline(0)\n        } else {\n            Self::from_heap(ColListVec::with_capacity(cap))\n        }\n    }\n\n    /// Constructs a list from a `u64` bitset\n    /// where the highest bit is unset.\n    ///\n    /// Panics in debug mode if the highest bit is set.\n    fn from_inline(list: u64) -> Self {\n        debug_assert_eq!(list & (1 << Self::FIRST_HEAP_COL), 0);\n        // (1) Move the whole inline bitset by one bit to the left.\n        // Mark the now-zero lowest bit so we know the list is inline.\n        let inline = ColListInline(list << 1 | 1);\n        // SAFETY: Lowest bit is set, so this will be interpreted as inline and not a pointer.\n        let ret = Self { inline };\n        debug_assert!(ret.is_inline());\n        ret\n    }\n\n    /// Constructs a list in heap form from `vec`.\n    fn from_heap(vec: ColListVec) -> Self {\n        let heap = ManuallyDrop::new(vec);\n        Self { heap }\n    }\n\n    /// Returns `head` if that is the only element.\n    pub fn as_singleton(&self) -> Option<ColId> {\n        let mut iter = self.iter();\n        match (iter.next(), iter.next()) {\n            (h @ Some(_), None) => h,\n            _ => None,\n        }\n    }\n\n    /// Returns the head of the list, if any.\n    pub fn head(&self) -> Option<ColId> {\n        self.iter().next()\n    }\n\n    /// Returns the last of the list, if any.\n    pub fn last(&self) -> Option<ColId> {\n        match self.as_inline() {\n            Ok(inline) => inline.last(),\n            Err(heap) => heap.last().copied(),\n        }\n    }\n\n    /// Returns whether `needle` is contained in the list.\n    ///\n    /// This an be faster than using `list.iter().any(|c| c == needle)`.\n    pub fn contains(&self, needle: ColId) -> bool {\n        match self.as_inline() {\n            Ok(inline) => inline.contains(needle),\n            Err(heap) => heap.contains(&needle),\n        }\n    }\n\n    /// Returns an iterator over all the columns in this list.\n    pub fn iter(&self) -> impl '_ + Clone + Iterator<Item = ColId> {\n        match self.as_inline() {\n            Ok(inline) => Either::Left(inline.iter()),\n            Err(heap) => Either::Right(heap.iter().copied()),\n        }\n    }\n\n    /// Convert to a `Box<[u16]>`.\n    pub fn to_u16_vec(&self) -> alloc::boxed::Box<[u16]> {\n        self.iter().map(u16::from).collect()\n    }\n\n    /// Returns the length of the list.\n    pub fn len(&self) -> u16 {\n        match self.as_inline() {\n            Ok(inline) => inline.len(),\n            Err(heap) => heap.len(),\n        }\n    }\n\n    /// Returns whether the list is empty.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Push `col` onto the list.\n    ///\n    /// If `col >= 63` or `col <= last_col`, the list will become heap allocated if not already.\n    pub fn push(&mut self, col: ColId) {\n        self.push_inner(col, self.last().is_none_or(|l| l < col));\n    }\n\n    /// Sort and deduplicate the list.\n    /// If the list is already sorted and deduplicated, does nothing.\n    /// This will typically result in an inline list unless there are large `ColId`s in play.\n    fn sort_dedup(&mut self) {\n        if let Err(heap) = self.as_inline_mut() {\n            heap.sort();\n\n            // Don't reallocate if the list is already sorted and deduplicated.\n            let is_deduped = is_sorted_and_deduped(heap);\n            let wants_inline = heap.last().unwrap_or(&ColId(0)).0 < Self::FIRST_HEAP_COL_U16;\n            if !is_deduped || wants_inline {\n                *self = Self::from_iter(heap.iter().copied().dedup());\n            }\n        }\n    }\n\n    /// Push `col` onto the list.\n    ///\n    /// If `col >= 63` or `!preserves_set_order`,\n    /// the list will become heap allocated if not already.\n    #[inline]\n    fn push_inner(&mut self, col: ColId, preserves_set_order: bool) {\n        let val = u16::from(col) as u64;\n        match (val < Self::FIRST_HEAP_COL && preserves_set_order, self.as_inline_mut()) {\n            (true, Ok(inline)) => inline.0 |= 1 << (val + 1),\n            // Converts the list to its non-inline heap form.\n            // This is unlikely to happen.\n            (false, Ok(inline)) => *self = Self::from_heap(inline.heapify_and_push(col)),\n            (_, Err(heap)) => heap.push(col),\n        }\n    }\n\n    /// The first `ColId` that would make the list heap allocated.\n    const FIRST_HEAP_COL: u64 = size_of::<u64>() as u64 * 8 - 1;\n\n    /// The first `ColId` that would make the list heap allocated.\n    const FIRST_HEAP_COL_U16: u16 = Self::FIRST_HEAP_COL as u16;\n\n    /// Returns the list either as inline or heap based.\n    #[inline]\n    fn as_inline(&self) -> Result<&ColListInline, &ManuallyDrop<ColListVec>> {\n        if self.is_inline() {\n            // SAFETY: confirmed that it is inline so this field is active.\n            Ok(unsafe { &self.inline })\n        } else {\n            // SAFETY: confirmed it's not, so `heap` is active instead.\n            Err(unsafe { &self.heap })\n        }\n    }\n\n    /// Returns the list either as inline or heap based.\n    #[inline]\n    fn as_inline_mut(&mut self) -> Result<&mut ColListInline, &mut ManuallyDrop<ColListVec>> {\n        if self.is_inline() {\n            // SAFETY: confirmed that it is inline so this field is active.\n            Ok(unsafe { &mut self.inline })\n        } else {\n            // SAFETY: confirmed it's not, so `heap` is active instead.\n            Err(unsafe { &mut self.heap })\n        }\n    }\n\n    #[inline]\n    fn is_inline(&self) -> bool {\n        // Check whether the lowest bit has been marked.\n        // This bit is unused by the heap case as the pointer must be aligned for `u16`.\n        // That is, we know that if the `self.heap` variant is active,\n        // then `self.heap.addr() % align_of::<u16> == 0`.\n        // So if `self.check % align_of::<u16> == 1`, as checked below,\n        // we now it's the inline case and not the heap case.\n\n        // SAFETY: Even when `inline`, and on a < 64-bit target,\n        // we can treat the union as a `usize` to check the lowest bit.\n        let addr = unsafe { self.check };\n        addr & 1 != 0\n    }\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for ColList {\n    fn heap_usage(&self) -> usize {\n        match self.as_inline() {\n            Ok(_) => 0,\n            Err(heap) => heap.capacity() as usize,\n        }\n    }\n}\n\nimpl Drop for ColList {\n    fn drop(&mut self) {\n        if let Err(heap) = self.as_inline_mut() {\n            // SAFETY: Only called once, so we will not have use-after-free or double-free.\n            unsafe { ManuallyDrop::drop(heap) };\n        }\n    }\n}\n\nimpl Clone for ColList {\n    fn clone(&self) -> Self {\n        match self.as_inline() {\n            Ok(inline) => Self { inline: *inline },\n            Err(heap) => Self { heap: heap.clone() },\n        }\n    }\n}\n\nimpl Eq for ColList {}\nimpl PartialEq for ColList {\n    fn eq(&self, other: &Self) -> bool {\n        match (self.as_inline(), other.as_inline()) {\n            (Ok(lhs), Ok(rhs)) => lhs == rhs,\n            (Err(lhs), Err(rhs)) => ***lhs == ***rhs,\n            _ => false,\n        }\n    }\n}\n\nimpl Ord for ColList {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.iter().cmp(other.iter())\n    }\n}\nimpl PartialOrd for ColList {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Hash for ColList {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self.as_inline() {\n            Ok(inline) => inline.0.hash(state),\n            Err(heap) => heap.hash(state),\n        }\n    }\n}\n\nimpl fmt::Debug for ColList {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_list().entries(self.iter()).finish()\n    }\n}\n\nimpl From<ColSet> for ColList {\n    fn from(value: ColSet) -> Self {\n        value.0\n    }\n}\n\n/// A borrowed list of columns or a single one.\npub enum ColOrCols<'a> {\n    /// A single column.\n    Col(ColId),\n    /// A list of columns.\n    ColList(&'a ColList),\n}\n\nimpl ColOrCols<'_> {\n    /// Returns `Some(col)` iff `self` is singleton.\n    pub fn as_singleton(&self) -> Option<ColId> {\n        match self {\n            Self::Col(col) => Some(*col),\n            Self::ColList(cols) => cols.as_singleton(),\n        }\n    }\n\n    /// Returns an iterator over all the columns in this list.\n    pub fn iter(&self) -> impl '_ + Iterator<Item = ColId> {\n        match self {\n            Self::Col(col) => Either::Left(iter::once(*col)),\n            Self::ColList(cols) => Either::Right(cols.iter()),\n        }\n    }\n\n    /// Returns the length of this list.\n    pub fn len(&self) -> u16 {\n        match self {\n            Self::Col(_) => 1,\n            Self::ColList(cols) => cols.len(),\n        }\n    }\n\n    /// Returns whether the list is empty.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Converts to a [`ColList`].\n    pub fn to_owned(self) -> ColList {\n        match self {\n            Self::Col(col) => [col].into(),\n            Self::ColList(list) => list.clone(),\n        }\n    }\n}\n\nimpl PartialEq<ColList> for ColOrCols<'_> {\n    fn eq(&self, other: &ColList) -> bool {\n        self.iter().eq(other.iter())\n    }\n}\nimpl PartialEq for ColOrCols<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        self.iter().eq(other.iter())\n    }\n}\n\nimpl Eq for ColOrCols<'_> {}\nimpl Ord for ColOrCols<'_> {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.iter().cmp(other.iter())\n    }\n}\nimpl PartialOrd for ColOrCols<'_> {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\n/// A compressed set of columns. Like a `ColList`, but guaranteed to be sorted and to contain no duplicate entries.\n/// Dereferences to a `ColList` for convenience.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct ColSet(ColList);\n\nimpl ColSet {\n    /// Check if a `ColSet` contains a given column.\n    pub fn contains(&self, needle: ColId) -> bool {\n        match self.as_inline() {\n            Ok(inline) => inline.contains(needle),\n            // We can use binary search because the vector is guaranteed to be sorted.\n            Err(heap) => heap.binary_search(&needle).is_ok(),\n        }\n    }\n\n    // Don't implement `insert` because repeated insertions will be O(n^2) if we want to keep the set sorted on the heap.\n    // Use iterator methods to create a new `ColSet` instead.\n}\n\nimpl<C: Into<ColId>> FromIterator<C> for ColSet {\n    fn from_iter<T: IntoIterator<Item = C>>(iter: T) -> Self {\n        // TODO: implement a fast path here that avoids allocation, by lying about\n        // `preserves_set_order` to `push_inner`.\n        Self::from(iter.into_iter().collect::<ColList>())\n    }\n}\n\nimpl From<ColList> for ColSet {\n    fn from(mut list: ColList) -> Self {\n        list.sort_dedup();\n        Self(list)\n    }\n}\n\nimpl From<&ColList> for ColSet {\n    fn from(value: &ColList) -> Self {\n        value.iter().collect()\n    }\n}\n\nimpl From<ColOrCols<'_>> for ColSet {\n    fn from(value: ColOrCols<'_>) -> Self {\n        match value {\n            ColOrCols::Col(col) => ColSet(col.into()),\n            ColOrCols::ColList(cols) => cols.into(),\n        }\n    }\n}\n\nimpl<C: Into<ColId>> From<C> for ColSet {\n    fn from(value: C) -> Self {\n        Self::from(ColList::new(value.into()))\n    }\n}\n\nimpl Deref for ColSet {\n    type Target = ColList;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl fmt::Debug for ColSet {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_set().entries(self.iter()).finish()\n    }\n}\n\n/// The inline version of a [`ColList`].\n#[derive(Clone, Copy, PartialEq)]\nstruct ColListInline(u64);\n\nimpl ColListInline {\n    /// Returns whether `needle` is part of this list.\n    fn contains(&self, needle: ColId) -> bool {\n        let col = needle.0;\n        let inline = self.undo_mark();\n        col < ColList::FIRST_HEAP_COL_U16 && inline & (1u64 << col) != 0\n    }\n\n    /// Returns an iterator over the [`ColId`]s stored by this list.\n    fn iter(&self) -> impl '_ + Clone + Iterator<Item = ColId> {\n        let mut value = self.undo_mark();\n        iter::from_fn(move || {\n            if value == 0 {\n                // No set bits; quit!\n                None\n            } else {\n                // Count trailing zeros and then zero out the first set bit.\n                // For e.g., `0b11001`, this would yield `[0, 3, 4]` as expected.\n                let id = ColId(value.trailing_zeros() as u16);\n                value &= value.wrapping_sub(1);\n                Some(id)\n            }\n        })\n    }\n\n    /// Returns the last element of the list.\n    fn last(&self) -> Option<ColId> {\n        (u64::BITS - self.undo_mark().leading_zeros())\n            .checked_sub(1)\n            .map(|c| ColId(c as _))\n    }\n\n    /// Returns the length of the list.\n    fn len(&self) -> u16 {\n        self.undo_mark().count_ones() as u16\n    }\n\n    /// Undoes the shift in (1).\n    #[inline]\n    fn undo_mark(&self) -> u64 {\n        self.0 >> 1\n    }\n\n    /// Returns an equivalent list in heap form instead of inline, and adds `col` to it.\n    /// The capacity of the vec will be `2 * (self.len() + 1)`\n    fn heapify_and_push(&self, col: ColId) -> ColListVec {\n        let mut vec = ColListVec::with_capacity(2 * (self.len() + 1));\n        for col in self.iter() {\n            vec.push(col)\n        }\n        vec.push(col);\n        vec\n    }\n}\n\n/// The thin-vec heap based version of a [`ColList`].\nstruct ColListVec(NonNull<u16>);\n\nimpl ColListVec {\n    /// Returns an empty vector with `capacity`.\n    fn with_capacity(capacity: u16) -> Self {\n        // Allocate the vector using the global allocator.\n        let layout = Self::layout(capacity);\n        // SAFETY: the size of `[u16; 2 + capacity]` is always non-zero.\n        let ptr = unsafe { alloc(layout) }.cast::<u16>();\n        let Some(ptr_non_null) = NonNull::new(ptr) else {\n            handle_alloc_error(layout)\n        };\n\n        let mut this = Self(ptr_non_null);\n        // SAFETY: `0 <= capacity` and claiming no elements are init trivially holds.\n        unsafe {\n            this.set_len(0);\n        }\n        // SAFETY: `capacity` matches that of the allocation.\n        unsafe { this.set_capacity(capacity) };\n        this\n    }\n\n    /// Returns the length of the list.\n    fn len(&self) -> u16 {\n        let ptr = self.0.as_ptr();\n        // SAFETY: `ptr` is properly aligned for `u16` and is valid for reads.\n        unsafe { *ptr }\n    }\n\n    /// SAFETY: `new_len <= self.capacity()` and `new_len` <= number of initialized elements.\n    unsafe fn set_len(&mut self, new_len: u16) {\n        let ptr = self.0.as_ptr();\n        // SAFETY:\n        // - `ptr` is valid for writes as we have exclusive access.\n        // - It's also properly aligned for `u16`.\n        unsafe {\n            *ptr = new_len;\n        }\n    }\n\n    /// Returns the capacity of the allocation in terms of elements.\n    fn capacity(&self) -> u16 {\n        let ptr = self.0.as_ptr();\n        // SAFETY: `ptr + 1 u16` is in bounds of the allocation and it doesn't overflow isize.\n        let capacity_ptr = unsafe { ptr.add(1) };\n        // SAFETY: `capacity_ptr` is properly aligned for `u16` and is valid for reads.\n        unsafe { *capacity_ptr }\n    }\n\n    /// Sets the capacity of the allocation in terms of elements.\n    ///\n    /// SAFETY: `cap` must match the actual capacity of the allocation.\n    unsafe fn set_capacity(&mut self, cap: u16) {\n        let ptr = self.0.as_ptr();\n        // SAFETY: `ptr + 1 u16` is in bounds of the allocation and it doesn't overflow isize.\n        let cap_ptr = unsafe { ptr.add(1) };\n        // SAFETY: `cap_ptr` is valid for writes as we have ownership of the allocation.\n        // It's also properly aligned for `u16`.\n        unsafe {\n            *cap_ptr = cap;\n        }\n    }\n\n    /// Push an element to the list.\n    fn push(&mut self, val: ColId) {\n        let len = self.len();\n        let cap = self.capacity();\n\n        if len == cap {\n            // We're at capacity, reallocate using standard * 2 exponential factor.\n            let new_cap = cap.checked_mul(2).expect(\"capacity overflow\");\n            let new_layout = Self::layout(new_cap);\n            // Reallocation will will move the data as well.\n            let old_layout = Self::layout(cap);\n            let old_ptr = self.0.as_ptr().cast();\n            // SAFETY:\n            // - `base_ptr` came from the global allocator\n            // - `old_layout` is the same layout used for the original allocation.\n            // - `new_layout.size()` is non-zero and <= `isize::MAX`.\n            let new_ptr = unsafe { realloc(old_ptr, old_layout, new_layout.size()) }.cast();\n            let Some(ptr_non_null) = NonNull::new(new_ptr) else {\n                handle_alloc_error(new_layout);\n            };\n            // Use new pointer and set capacity.\n            self.0 = ptr_non_null;\n            // SAFETY: `new_cap` matches that of the allocation.\n            unsafe { self.set_capacity(new_cap) };\n        }\n\n        // Write the element and increase the length.\n        let base_ptr = self.0.as_ptr();\n        let elem_offset = 2 + len as usize;\n        // SAFETY: Allocated for `2 + capacity` `u16`s and `len <= capacity`, so we're in bounds.\n        let elem_ptr = unsafe { base_ptr.add(elem_offset) }.cast();\n        // SAFETY: `elem_ptr` is valid for writes and is properly aligned for `ColId`.\n        unsafe {\n            *elem_ptr = val;\n        }\n        // SAFETY: the length <= the capacity and we just init the `len + 1`th element.\n        unsafe {\n            self.set_len(len + 1);\n        }\n    }\n\n    /// Computes a layout for the following struct:\n    /// ```rust,ignore\n    /// struct ColListVecData {\n    ///     len: u16,\n    ///     capacity: u16,\n    ///     data: [ColId],\n    /// }\n    /// ```\n    ///\n    /// Panics if `cap` would result in an allocation larger than `isize::MAX`.\n    fn layout(cap: u16) -> Layout {\n        Layout::array::<u16>(cap.checked_add(2).expect(\"capacity overflow\") as usize).unwrap()\n    }\n}\n\nimpl Deref for ColListVec {\n    type Target = [ColId];\n\n    fn deref(&self) -> &Self::Target {\n        let len = self.len() as usize;\n        let ptr = self.0.as_ptr();\n        // SAFETY: `ptr + 2` is always in bounds of the allocation and `ptr <= isize::MAX`.\n        let ptr = unsafe { ptr.add(2) }.cast::<ColId>();\n        // SAFETY:\n        // - `ptr` is valid for reads for `len * size_of::<ColId>` and it is properly aligned.\n        // - `len`  elements are initialized.\n        // - For the lifetime of `'0`, the memory won't be mutated.\n        // - `len * size_of::<ColId> <= isize::MAX` holds.\n        unsafe { from_raw_parts(ptr, len) }\n    }\n}\n\nimpl DerefMut for ColListVec {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        let len = self.len() as usize;\n        let ptr = self.0.as_ptr();\n        // SAFETY: `ptr + 2` is always in bounds of the allocation and `ptr <= isize::MAX`.\n        let ptr = unsafe { ptr.add(2) }.cast::<ColId>();\n        // SAFETY:\n        // - `ptr` is valid for reads and writes for `len * size_of::<ColId>` and it is properly aligned.\n        // - `len`  elements are initialized.\n        // - `len * size_of::<ColId> <= isize::MAX` holds.\n        unsafe { from_raw_parts_mut(ptr, len) }\n    }\n}\n\nimpl Drop for ColListVec {\n    fn drop(&mut self) {\n        let capacity = self.capacity();\n        let layout = Self::layout(capacity);\n        let ptr = self.0.as_ptr().cast();\n        // SAFETY: `ptr` was allocated by the global allocator\n        // and `layout` was the one the memory was allocated with.\n        unsafe { dealloc(ptr, layout) };\n    }\n}\n\nimpl Clone for ColListVec {\n    fn clone(&self) -> Self {\n        let mut vec = ColListVec::with_capacity(self.len());\n        for col in self.iter().copied() {\n            vec.push(col);\n        }\n        vec\n    }\n}\n\n/// Check if a buffer is sorted and deduplicated.\nfn is_sorted_and_deduped(data: &[ColId]) -> bool {\n    match data {\n        [] => true,\n        &[mut prev, ref rest @ ..] => !rest.iter().any(|elem| {\n            let bad = prev >= *elem;\n            prev = *elem;\n            bad\n        }),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::collection::vec;\n    use proptest::prelude::*;\n\n    fn contains(list: &ColList, x: &ColId) -> bool {\n        list.iter().any(|y| y == *x)\n    }\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n\n        #[test]\n        fn test_inline(cols in vec((0..ColList::FIRST_HEAP_COL_U16).prop_map_into(), 1..100)) {\n            let [head, tail @ ..] = &*cols else { unreachable!() };\n\n            let mut list = ColList::new(*head);\n            let mut is_inline = list.is_inline();\n            prop_assert!(is_inline);\n            prop_assert!(!list.is_empty());\n            prop_assert_eq!(list.len(), 1);\n            prop_assert_eq!(list.head(), Some(*head));\n            prop_assert_eq!(list.last(), Some(*head));\n            prop_assert_eq!(list.iter().collect::<Vec<_>>(), [*head]);\n\n\n            for col in tail {\n                is_inline &= list.last().unwrap() < *col;\n                list.push(*col);\n\n                prop_assert_eq!(is_inline, list.is_inline());\n                prop_assert!(!list.is_empty());\n                prop_assert_eq!(list.head(), Some(*head));\n                prop_assert_eq!(list.last(), Some(*col));\n                prop_assert_eq!(list.last(), list.iter().last());\n                prop_assert!(contains(&list, col));\n            }\n\n            prop_assert_eq!(&list.clone(), &list);\n            prop_assert_eq!(list.iter().collect::<Vec<_>>(), cols);\n        }\n\n        #[test]\n        fn test_heap(cols in vec((ColList::FIRST_HEAP_COL_U16..).prop_map_into(), 1..100)) {\n            let contains = |list: &ColList, x| list.iter().collect::<Vec<_>>().contains(x);\n\n            let head = ColId(0);\n            let mut list = ColList::new(head);\n            prop_assert!(list.is_inline());\n            prop_assert_eq!(list.len(), 1);\n\n            for (idx, col) in cols.iter().enumerate() {\n                list.push(*col);\n                prop_assert!(!list.is_inline());\n                prop_assert!(!list.is_empty());\n                prop_assert_eq!(list.len() as usize, idx + 2);\n                prop_assert_eq!(list.head(), Some(head));\n                prop_assert_eq!(list.last(), Some(*col));\n                prop_assert!(contains(&list, col));\n            }\n\n            prop_assert_eq!(&list.clone(), &list);\n\n            let mut cols = cols;\n            cols.insert(0, head);\n            prop_assert_eq!(list.iter().collect::<Vec<_>>(), cols);\n        }\n\n        #[test]\n        fn test_collect(cols in vec((0..100).prop_map_into(), 0..100)) {\n            let list = cols.iter().copied().collect::<ColList>();\n            prop_assert!(list.iter().eq(cols));\n            prop_assert_eq!(&list, &list.iter().collect::<ColList>());\n        }\n\n        #[test]\n        fn test_as_singleton(cols in vec((0..100).prop_map_into(), 0..10)) {\n            let list = cols.iter().copied().collect::<ColList>();\n            match cols.len() {\n                1 => {\n                    prop_assert_eq!(list.as_singleton(), Some(cols[0]));\n                    prop_assert_eq!(list.as_singleton(), list.head());\n                },\n                _ => prop_assert_eq!(list.as_singleton(), None),\n            }\n        }\n\n        #[test]\n        fn test_set_inlines(mut cols in vec((0..ColList::FIRST_HEAP_COL_U16).prop_map_into(), 1..100)) {\n            prop_assume!(!is_sorted_and_deduped(&cols[..]));\n\n            let list = ColList::from_iter(cols.iter().copied());\n            prop_assert!(!list.is_inline());\n            let set = ColSet::from(list);\n            prop_assert!(set.is_inline());\n\n            for col in cols.iter() {\n                prop_assert!(set.contains(*col));\n            }\n\n            cols.sort();\n            cols.dedup();\n            prop_assert_eq!(set.iter().collect::<Vec<_>>(), cols);\n        }\n\n        #[test]\n        fn test_set_heap(mut cols in vec((ColList::FIRST_HEAP_COL_U16..).prop_map_into(), 1..100)) {\n            prop_assume!(!is_sorted_and_deduped(&cols[..]));\n\n            let list = ColList::from_iter(cols.iter().copied());\n            prop_assert!(!list.is_inline());\n            let set = ColSet::from(list);\n            prop_assert!(!set.is_inline());\n\n            for col in cols.iter() {\n                prop_assert!(set.contains(*col));\n            }\n\n            cols.sort();\n            cols.dedup();\n            prop_assert_eq!(set.iter().collect::<Vec<_>>(), cols);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/primitives/src/errno.rs",
    "content": "//! Error numbers for the wasm abi.\n\nuse core::num::NonZeroU16;\n\n/// Takes a macro that expects `$($err_name:ident($errno:literal, $errmsg:literal),)*` and invokes\n/// it with the errnos defined in this module.\n#[macro_export]\nmacro_rules! errnos {\n    ($mac:ident) => {\n        $mac!(\n            HOST_CALL_FAILURE(1, \"ABI called by host returned an error\"),\n            NOT_IN_TRANSACTION(2, \"ABI call can only be made while in a transaction\"),\n            BSATN_DECODE_ERROR(3, \"Couldn't decode the BSATN to the expected type\"),\n            NO_SUCH_TABLE(4, \"No such table\"),\n            NO_SUCH_INDEX(5, \"No such index\"),\n            NO_SUCH_ITER(6, \"The provided row iterator is not valid\"),\n            NO_SUCH_CONSOLE_TIMER(7, \"The provided console timer does not exist\"),\n            NO_SUCH_BYTES(8, \"The provided bytes source or sink is not valid\"),\n            NO_SPACE(9, \"The provided sink has no more space left\"),\n            WRONG_INDEX_ALGO(10, \"The Index does not support range scans\"),\n            BUFFER_TOO_SMALL(11, \"The provided buffer is not large enough to store the data\"),\n            UNIQUE_ALREADY_EXISTS(12, \"Value with given unique identifier already exists\"),\n            SCHEDULE_AT_DELAY_TOO_LONG(13, \"Specified delay in scheduling row was too long\"),\n            INDEX_NOT_UNIQUE(14, \"The index was not unique\"),\n            NO_SUCH_ROW(15, \"The row was not found, e.g., in an update call\"),\n            AUTO_INC_OVERFLOW(16, \"The auto-increment sequence overflowed\"),\n            WOULD_BLOCK_TRANSACTION(\n                17,\n                \"Attempted async or blocking op while holding open a transaction\"\n            ),\n            TRANSACTION_NOT_ANONYMOUS(18, \"Not in an anonymous transaction. Called by a reducer?\"),\n            TRANSACTION_IS_READ_ONLY(19, \"ABI call can only be made while within a mutable transaction\"),\n            TRANSACTION_IS_MUT(\n                20,\n                \"ABI call can only be made while within a read-only transaction\"\n            ),\n            HTTP_ERROR(21, \"The HTTP request failed\"),\n        );\n    };\n}\npub use errnos;\n\nconst fn nz(n: u16) -> NonZeroU16 {\n    match NonZeroU16::new(n) {\n        Some(n) => n,\n        None => panic!(),\n    }\n}\n\nmacro_rules! def_errnos {\n    ($($err_name:ident($errno:literal, $errmsg:literal),)*) => {\n        $(#[doc = $errmsg] pub const $err_name: NonZeroU16 = nz($errno);)*\n\n        /// Get the error message for an error number, if it exists.\n        pub const fn strerror(num: NonZeroU16) -> Option<&'static str> {\n            match num.get() {\n                $($errno => Some($errmsg),)*\n                _ => None,\n            }\n        }\n    };\n}\nerrnos!(def_errnos);\n"
  },
  {
    "path": "crates/primitives/src/ids.rs",
    "content": "//! Provides identifiers such as `TableId`.\n\nuse core::fmt;\n\nuse enum_as_inner::EnumAsInner;\n\nmacro_rules! system_id {\n    ($(#[$($doc_comment:tt)*])* pub struct $name:ident(pub $backing_ty:ty);) => {\n\n        $(#[$($doc_comment)*])*\n        #[derive(Debug, Default, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]\n        #[repr(transparent)]\n        pub struct $name(pub $backing_ty);\n\n        impl From<$backing_ty> for $name {\n            fn from(value: $backing_ty) -> Self {\n                Self(value)\n            }\n        }\n        impl From<$name> for $backing_ty {\n            fn from(value: $name) -> Self {\n                value.0\n            }\n        }\n\n        impl $name {\n            /// Convert `self` to a `usize` suitable for indexing into an array.\n            pub fn idx(self) -> usize {\n                self.0 as usize\n            }\n\n        }\n\n        impl nohash_hasher::IsEnabled for $name {}\n\n        impl From<usize> for $name {\n            fn from(value: usize) -> Self {\n                Self(value as _)\n            }\n        }\n\n        impl fmt::Display for $name {\n            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n                write!(f, \"{}\", self.0)\n            }\n        }\n\n        // Defined so that e.g., `0.into()` is possible.\n        impl From<i32> for $name {\n            fn from(value: i32) -> Self {\n                Self(value as _)\n            }\n        }\n\n        #[cfg(feature = \"memory-usage\")]\n        impl spacetimedb_memory_usage::MemoryUsage for $name {}\n    };\n}\n// TODO(1.0): convert this into a proper trait.\nmacro_rules! auto_inc_system_id {\n    ($name:ident) => {\n        impl $name {\n            /// The sentinel value for this type.\n            /// This will be initialized to a valid ID upon insertion into a system table as a primary key.\n            pub const SENTINEL: Self = Self(0);\n\n            /// Check if this ID is the sentinel value.\n            pub fn is_sentinel(self) -> bool {\n                self == Self::SENTINEL\n            }\n        }\n    };\n}\n\nsystem_id! {\n    /// An identifier for a table, unique within a database.\n    pub struct TableId(pub u32);\n}\nauto_inc_system_id!(TableId);\n\nsystem_id! {\n    /// An identifier for a view, unique within a database.\n    /// It is stored in the db as the primary key column of `st_view`.\n    pub struct ViewId(pub u32);\n}\nauto_inc_system_id!(ViewId);\n\nsystem_id! {\n    /// An identifier for a list of arguments passed to a view.\n    pub struct ArgId(pub u64);\n}\nauto_inc_system_id!(ArgId);\n\nsystem_id! {\n    /// An identifier for a sequence, unique within a database.\n    pub struct SequenceId(pub u32);\n}\nauto_inc_system_id!(SequenceId);\n\nsystem_id! {\n    /// An identifier for an index, unique within a database.\n    pub struct IndexId(pub u32);\n}\nauto_inc_system_id!(IndexId);\n\nsystem_id! {\n    /// An identifier for a constraint, unique within a database.\n    pub struct ConstraintId(pub u32);\n}\nauto_inc_system_id!(ConstraintId);\n\nsystem_id! {\n    /// An identifier for a schedule, unique within a database.\n    pub struct ScheduleId(pub u32);\n}\nauto_inc_system_id!(ScheduleId);\n\nsystem_id! {\n    /// The position of a column within a table.\n    ///\n    /// A `ColId` does NOT uniquely identify a column within a database!\n    /// A pair `(TableId, ColId)` is required for this.\n    /// Each table will have columns with `ColId` values ranging from `0` to `n-1`, where `n` is the number of columns in the table.\n    /// A table may have at most `u16::MAX` columns.\n    pub struct ColId(pub u16);\n}\n// ColId works differently from other system IDs and is not auto-incremented.\n\nsystem_id! {\n    /// The index of a reducer as defined in a module's reducers list.\n    // This is never stored in a system table, but is useful to have defined here.\n    pub struct ReducerId(pub u32);\n}\n\nsystem_id! {\n    /// The index of a procedure as defined in a module's procedure list.\n    // This is never stored in a system table, but is useful to have defined here.\n    pub struct ProcedureId(pub u32);\n}\n\nsystem_id! {\n    /// The index of a view as defined in a module's view lists.\n    ///\n    /// Unlike reducers and procedures, the module maintains two lists for views.\n    /// One for `ViewContext` and the other for `AnonymousViewContext`.\n    /// As such, this index does not uniquely identify a view on its own.\n    /// You must know which list this index refers to.\n    ///\n    // This is never stored in a system table, but is useful to have defined here.\n    pub struct ViewFnPtr(pub u32);\n}\n\n/// An id for a function exported from a module, which may be a reducer or a procedure.\n// This is never stored in a system table,\n// but is useful to have defined here to provide a shared language for downstream crates.\n#[derive(Clone, Copy, Debug, EnumAsInner)]\npub enum FunctionId {\n    Reducer(ReducerId),\n    Procedure(ProcedureId),\n}\n"
  },
  {
    "path": "crates/primitives/src/lib.rs",
    "content": "#![cfg_attr(not(test), no_std)]\n\nmod attr;\nmod col_list;\npub mod errno;\nmod ids;\n\npub use attr::{AttributeKind, ColumnAttribute, ConstraintKind, Constraints};\npub use col_list::{ColList, ColOrCols, ColSet};\npub use ids::{\n    ArgId, ColId, ConstraintId, FunctionId, IndexId, ProcedureId, ReducerId, ScheduleId, SequenceId, TableId,\n    ViewFnPtr, ViewId,\n};\n\n/// The minimum size of a chunk yielded by a wasm abi RowIter.\npub const ROW_ITER_CHUNK_SIZE: usize = 32 * 1024;\n"
  },
  {
    "path": "crates/query/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-query\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Top level crate for invoking the query engine and optimizer\"\n\n[dependencies]\nanyhow.workspace = true\nitertools.workspace = true\nrayon.workspace = true\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-execution.workspace = true\nspacetimedb-expr.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-physical-plan.workspace = true\nspacetimedb-schema.workspace = true\nspacetimedb-sql-parser.workspace = true\nspacetimedb-table.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/query/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/query/src/lib.rs",
    "content": "use anyhow::{bail, Result};\nuse spacetimedb_execution::{\n    dml::{MutDatastore, MutExecutor},\n    pipelined::ProjectListExecutor,\n    Datastore, DeltaStore,\n};\nuse spacetimedb_expr::{\n    check::{parse_and_type_sub, SchemaView},\n    expr::ProjectList,\n    rls::{resolve_views_for_sql, resolve_views_for_sub},\n    statement::{parse_and_type_sql, Statement, DML},\n};\nuse spacetimedb_lib::{identity::AuthCtx, metrics::ExecutionMetrics, ProductValue};\nuse spacetimedb_physical_plan::{\n    compile::{compile_dml_plan, compile_select, compile_select_list},\n    plan::{ProjectListPlan, ProjectPlan},\n};\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_schema::table_name::TableName;\n\n/// DIRTY HACK ALERT: Maximum allowed length, in UTF-8 bytes, of SQL queries.\n/// Any query longer than this will be rejected.\n/// This prevents a stack overflow when compiling queries with deeply-nested `AND` and `OR` conditions.\nconst MAX_SQL_LENGTH: usize = 50_000;\n\npub fn compile_subscription(\n    sql: &str,\n    tx: &impl SchemaView,\n    auth: &AuthCtx,\n) -> Result<(Vec<ProjectPlan>, TableId, TableName, bool)> {\n    if sql.len() > MAX_SQL_LENGTH {\n        bail!(\"SQL query exceeds maximum allowed length: \\\"{sql:.120}...\\\"\")\n    }\n\n    let (plan, mut has_param) = parse_and_type_sub(sql, tx, auth)?;\n\n    let Some(return_id) = plan.return_table_id() else {\n        bail!(\"Failed to determine TableId for query\")\n    };\n\n    let Some(return_name) = tx.schema_for_table(return_id).map(|schema| schema.table_name.clone()) else {\n        bail!(\"TableId `{return_id}` does not exist\")\n    };\n\n    // Resolve any RLS filters\n    let plan_fragments = resolve_views_for_sub(tx, plan, auth, &mut has_param)?\n        .into_iter()\n        .map(compile_select)\n        .collect::<Vec<_>>();\n\n    // Does this subscription read from a client-specific view?\n    // If so, it is as if the view is parameterized by `:sender`.\n    // We must know this in order to generate the correct query hash.\n    let reads_view = plan_fragments.iter().any(|plan| plan.reads_from_view(false));\n\n    Ok((plan_fragments, return_id, return_name, has_param || reads_view))\n}\n\n/// A utility for parsing and type checking a sql statement\npub fn compile_sql_stmt(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> Result<Statement> {\n    if sql.len() > MAX_SQL_LENGTH {\n        bail!(\"SQL query exceeds maximum allowed length: \\\"{sql:.120}...\\\"\")\n    }\n\n    match parse_and_type_sql(sql, tx, auth)? {\n        stmt @ Statement::DML(_) => Ok(stmt),\n        Statement::Select(expr) => Ok(Statement::Select(resolve_views_for_sql(tx, expr, auth)?)),\n    }\n}\n\n/// A utility for executing a sql select statement\npub fn execute_select_stmt<Tx: Datastore + DeltaStore>(\n    auth: &AuthCtx,\n    stmt: ProjectList,\n    tx: &Tx,\n    metrics: &mut ExecutionMetrics,\n    check_row_limit: impl Fn(ProjectListPlan) -> Result<ProjectListPlan>,\n) -> Result<Vec<ProductValue>> {\n    let plan = compile_select_list(stmt).optimize(auth)?;\n    let plan = check_row_limit(plan)?;\n    let plan = ProjectListExecutor::from(plan);\n    let mut rows = vec![];\n    plan.execute(tx, metrics, &mut |row| {\n        rows.push(row);\n        Ok(())\n    })?;\n    Ok(rows)\n}\n\n/// A utility for executing a sql dml statement\npub fn execute_dml_stmt<Tx: MutDatastore>(\n    auth: &AuthCtx,\n    stmt: DML,\n    tx: &mut Tx,\n    metrics: &mut ExecutionMetrics,\n) -> Result<()> {\n    let plan = compile_dml_plan(stmt).optimize(auth)?;\n    let plan = MutExecutor::from(plan);\n    plan.execute(tx, metrics)\n}\n"
  },
  {
    "path": "crates/query-builder/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-query-builder\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Rust query builder for SpacetimeDB\"\n\n[dependencies]\nspacetimedb-lib = { workspace = true }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/query-builder/src/expr.rs",
    "content": "use spacetimedb_lib::{\n    sats::{i256, u256},\n    ConnectionId, Identity, Timestamp,\n};\n\nuse crate::{Col, ColumnRef};\n\npub enum Operand<T> {\n    Column(ColumnRef<T>),\n    Literal(LiteralValue),\n}\n\npub enum BoolExpr<T> {\n    Eq(Operand<T>, Operand<T>),\n    Ne(Operand<T>, Operand<T>),\n    Gt(Operand<T>, Operand<T>),\n    Lt(Operand<T>, Operand<T>),\n    Gte(Operand<T>, Operand<T>),\n    Lte(Operand<T>, Operand<T>),\n    And(Box<BoolExpr<T>>, Box<BoolExpr<T>>),\n    Or(Box<BoolExpr<T>>, Box<BoolExpr<T>>),\n    Not(Box<BoolExpr<T>>),\n}\n\nimpl<T> BoolExpr<T> {\n    pub fn and(self, other: BoolExpr<T>) -> BoolExpr<T> {\n        BoolExpr::And(Box::new(self), Box::new(other))\n    }\n\n    pub fn or(self, other: BoolExpr<T>) -> BoolExpr<T> {\n        BoolExpr::Or(Box::new(self), Box::new(other))\n    }\n\n    #[allow(clippy::should_implement_trait)]\n    pub fn not(self) -> BoolExpr<T> {\n        BoolExpr::Not(Box::new(self))\n    }\n}\n\nimpl<T> From<Col<T, bool>> for BoolExpr<T> {\n    fn from(col: Col<T, bool>) -> Self {\n        col.eq(true)\n    }\n}\n\nimpl<T> From<bool> for BoolExpr<T> {\n    fn from(value: bool) -> Self {\n        if value {\n            BoolExpr::Eq(\n                Operand::Literal(LiteralValue(\"TRUE\".to_string())),\n                Operand::Literal(LiteralValue(\"TRUE\".to_string())),\n            )\n        } else {\n            BoolExpr::Eq(\n                Operand::Literal(LiteralValue(\"FALSE\".to_string())),\n                Operand::Literal(LiteralValue(\"TRUE\".to_string())),\n            )\n        }\n    }\n}\n\n/// Trait for types that can be used as the right-hand side of a comparison with a column of type V\n/// in table T.\n///\n/// This trait is implemented for Col<T, V> and various literal types.\npub trait RHS<T, V> {\n    fn to_expr(self) -> Operand<T>;\n}\n\nimpl<T, V> RHS<T, V> for Col<T, V> {\n    fn to_expr(self) -> Operand<T> {\n        Operand::Column(self.col)\n    }\n}\n\nfn format_bool_expr<T>(v: &Operand<T>) -> String {\n    match v {\n        Operand::Column(col) => col.fmt(),\n        Operand::Literal(lit) => lit.0.clone(),\n    }\n}\n\npub fn format_expr<T>(expr: &BoolExpr<T>) -> String {\n    match expr {\n        BoolExpr::Eq(l, r) => format!(\"({} = {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::Ne(l, r) => format!(\"({} <> {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::Gt(l, r) => format!(\"({} > {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::Lt(l, r) => format!(\"({} < {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::Gte(l, r) => format!(\"({} >= {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::Lte(l, r) => format!(\"({} <= {})\", format_bool_expr(l), format_bool_expr(r)),\n        BoolExpr::And(a, b) => format!(\"({} AND {})\", format_expr(a), format_expr(b)),\n        BoolExpr::Or(a, b) => format!(\"({} OR {})\", format_expr(a), format_expr(b)),\n        BoolExpr::Not(inner) => format!(\"(NOT {})\", format_expr(inner)),\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct LiteralValue(String);\n\nimpl LiteralValue {\n    pub fn new(s: String) -> Self {\n        Self(s)\n    }\n}\n\nmacro_rules! impl_rhs {\n    ($ty:ty, $formatter:expr) => {\n        impl<T> RHS<T, $ty> for $ty {\n            fn to_expr(self) -> Operand<T> {\n                Operand::Literal(LiteralValue($formatter(self)))\n            }\n        }\n    };\n}\n\nimpl_rhs!(String, |v: String| format!(\"'{}'\", v.replace('\\'', \"''\")));\nimpl_rhs!(&str, |v: &str| format!(\"'{}'\", v.replace('\\'', \"''\")));\n\nimpl_rhs!(i8, |v: i8| v.to_string());\nimpl_rhs!(i16, |v: i16| v.to_string());\nimpl_rhs!(i32, |v: i32| v.to_string());\nimpl_rhs!(i64, |v: i64| v.to_string());\nimpl_rhs!(i128, |v: i128| v.to_string());\n\nimpl_rhs!(u8, |v: u8| v.to_string());\nimpl_rhs!(u16, |v: u16| v.to_string());\nimpl_rhs!(u32, |v: u32| v.to_string());\nimpl_rhs!(u64, |v: u64| v.to_string());\nimpl_rhs!(u128, |v: u128| v.to_string());\nimpl_rhs!(usize, |v: usize| v.to_string());\n\nimpl_rhs!(u256, |v: u256| v.to_string());\nimpl_rhs!(i256, |v: i256| v.to_string());\n\nimpl_rhs!(f32, |v: f32| (v as f64).to_string());\nimpl_rhs!(f64, |v: f64| v.to_string());\n\nimpl_rhs!(bool, |b: bool| if b { \"TRUE\".into() } else { \"FALSE\".into() });\n\nimpl_rhs!(Identity, |id: Identity| format!(\"0x{}\", id.to_hex()));\nimpl_rhs!(ConnectionId, |id: ConnectionId| format!(\"0x{}\", id.to_hex()));\nimpl_rhs!(Timestamp, |ts: Timestamp| format!(\"'{}'\", ts));\n\nimpl_rhs!(Vec<u8>, |b: Vec<u8>| {\n    let hex: String = b.iter().map(|x| format!(\"{:02x}\", x)).collect();\n    format!(\"0x{}\", hex)\n});\n"
  },
  {
    "path": "crates/query-builder/src/join.rs",
    "content": "use crate::TableNameStr;\n\nuse super::{\n    expr::{format_expr, BoolExpr},\n    table::{CanBeLookupTable, ColumnRef, HasCols, HasIxCols, Table},\n    Query, RawQuery,\n};\nuse std::marker::PhantomData;\n\n/// Indexed columns for joins\n///\n/// Joins are performed on indexed columns, Tables that implement `HasIxCols`\n/// provide access to their indexed columns.\npub struct IxCol<T, V> {\n    pub(super) col: ColumnRef<T>,\n    _marker: PhantomData<V>,\n}\n\nimpl<T, V> IxCol<T, V> {\n    pub fn new(table_name: TableNameStr, column: &'static str) -> Self {\n        Self {\n            col: ColumnRef::new(table_name, column),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<T, V> Copy for IxCol<T, V> {}\nimpl<T, V> Clone for IxCol<T, V> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\npub struct IxJoinEq<L, R, V> {\n    pub(super) lhs_col: ColumnRef<L>,\n    pub(super) rhs_col: ColumnRef<R>,\n    _marker: PhantomData<V>,\n}\n\nimpl<T, V> IxCol<T, V> {\n    pub fn eq<R: HasIxCols>(self, rhs: IxCol<R, V>) -> IxJoinEq<T, R, V> {\n        IxJoinEq {\n            lhs_col: self.col,\n            rhs_col: rhs.col,\n            _marker: PhantomData,\n        }\n    }\n}\n\n// Left semijoin: filters and returns left table rows\npub struct LeftSemiJoin<L> {\n    pub(super) left_col: ColumnRef<L>,\n    pub(super) right_table: &'static str,\n    pub(super) right_col: &'static str,\n    pub(super) where_expr: Option<BoolExpr<L>>,\n}\n\n// Right semijoin: returns right table rows, but remembers left conditions\npub struct RightSemiJoin<R, L> {\n    pub(super) left_col: ColumnRef<L>,\n    pub(super) right_col: ColumnRef<R>,\n    pub(super) left_where_expr: Option<BoolExpr<L>>,\n    pub(super) right_where_expr: Option<BoolExpr<R>>,\n    _left_marker: PhantomData<L>,\n}\n\nimpl<L: HasIxCols> Table<L> {\n    pub fn left_semijoin<R: CanBeLookupTable, V>(\n        self,\n        right: Table<R>,\n        on: impl Fn(&L::IxCols, &R::IxCols) -> IxJoinEq<L, R, V>,\n    ) -> LeftSemiJoin<L> {\n        let join = on(&L::ix_cols(self.name()), &R::ix_cols(right.name()));\n        LeftSemiJoin {\n            left_col: join.lhs_col,\n            right_table: right.name(),\n            right_col: join.rhs_col.column_name(),\n            where_expr: None,\n        }\n    }\n\n    pub fn right_semijoin<R: CanBeLookupTable, V>(\n        self,\n        right: Table<R>,\n        on: impl Fn(&L::IxCols, &R::IxCols) -> IxJoinEq<L, R, V>,\n    ) -> RightSemiJoin<R, L> {\n        let join = on(&L::ix_cols(self.name()), &R::ix_cols(right.name()));\n        RightSemiJoin {\n            left_col: join.lhs_col,\n            right_col: join.rhs_col,\n            left_where_expr: None,\n            right_where_expr: None,\n            _left_marker: PhantomData,\n        }\n    }\n}\n\nimpl<L: HasIxCols> super::FromWhere<L> {\n    pub fn left_semijoin<R: CanBeLookupTable, V>(\n        self,\n        right: Table<R>,\n        on: impl Fn(&L::IxCols, &R::IxCols) -> IxJoinEq<L, R, V>,\n    ) -> LeftSemiJoin<L> {\n        let join = on(&L::ix_cols(self.table_name), &R::ix_cols(right.name()));\n        LeftSemiJoin {\n            left_col: join.lhs_col,\n            right_table: right.name(),\n            right_col: join.rhs_col.column_name(),\n            where_expr: Some(self.expr),\n        }\n    }\n\n    pub fn right_semijoin<R: CanBeLookupTable, V>(\n        self,\n        right: Table<R>,\n        on: impl Fn(&L::IxCols, &R::IxCols) -> IxJoinEq<L, R, V>,\n    ) -> RightSemiJoin<R, L> {\n        let join = on(&L::ix_cols(self.table_name), &R::ix_cols(right.name()));\n        RightSemiJoin {\n            left_col: join.lhs_col,\n            right_col: join.rhs_col,\n            left_where_expr: Some(self.expr),\n            right_where_expr: None,\n            _left_marker: PhantomData,\n        }\n    }\n}\n\nimpl<L: HasCols> Query<L> for LeftSemiJoin<L> {\n    fn into_sql(self) -> String {\n        self.build().into_sql()\n    }\n}\n\nimpl<R: HasCols, L: HasCols> Query<R> for RightSemiJoin<R, L> {\n    fn into_sql(self) -> String {\n        self.build().into_sql()\n    }\n}\n\n// LeftSemiJoin where() operates on L\nimpl<L: HasCols> LeftSemiJoin<L> {\n    pub fn r#where<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&L::Cols) -> E,\n        E: Into<BoolExpr<L>>,\n    {\n        let extra = f(&L::cols(self.left_col.table_name())).into();\n        let new = match self.where_expr {\n            Some(existing) => Some(existing.and(extra)),\n            None => Some(extra),\n        };\n        Self {\n            left_col: self.left_col,\n            right_table: self.right_table,\n            right_col: self.right_col,\n            where_expr: new,\n        }\n    }\n\n    // Filter is an alias for where\n    pub fn filter<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&L::Cols) -> E,\n        E: Into<BoolExpr<L>>,\n    {\n        self.r#where(f)\n    }\n\n    pub fn build(self) -> RawQuery<L> {\n        let where_clause = self\n            .where_expr\n            .map(|e| format!(\" WHERE {}\", format_expr(&e)))\n            .unwrap_or_default();\n\n        let sql = format!(\n            r#\"SELECT \"{}\".* FROM \"{}\" JOIN \"{}\" ON \"{}\".\"{}\" = \"{}\".\"{}\"{}\"#,\n            self.left_col.table_name(),\n            self.left_col.table_name(),\n            self.right_table,\n            self.left_col.table_name(),\n            self.left_col.column_name(),\n            self.right_table,\n            self.right_col,\n            where_clause\n        );\n        RawQuery::new(sql)\n    }\n}\n\n// RightSemiJoin where() operates on R\nimpl<R: HasCols, L: HasCols> RightSemiJoin<R, L> {\n    pub fn r#where<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&R::Cols) -> E,\n        E: Into<BoolExpr<R>>,\n    {\n        let extra = f(&R::cols(self.right_col.table_name())).into();\n        let new = match self.right_where_expr {\n            Some(existing) => Some(existing.and(extra)),\n            None => Some(extra),\n        };\n        Self {\n            left_col: self.left_col,\n            right_col: self.right_col,\n            left_where_expr: self.left_where_expr,\n            right_where_expr: new,\n            _left_marker: PhantomData,\n        }\n    }\n\n    // Filter is an alias for where\n    pub fn filter<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&R::Cols) -> E,\n        E: Into<BoolExpr<R>>,\n    {\n        self.r#where(f)\n    }\n\n    pub fn build(self) -> RawQuery<R> {\n        let mut where_parts = Vec::new();\n\n        if let Some(left_expr) = self.left_where_expr {\n            where_parts.push(format_expr(&left_expr));\n        }\n\n        if let Some(right_expr) = self.right_where_expr {\n            where_parts.push(format_expr(&right_expr));\n        }\n\n        let where_clause = if !where_parts.is_empty() {\n            format!(\" WHERE {}\", where_parts.join(\" AND \"))\n        } else {\n            String::new()\n        };\n\n        let sql = format!(\n            r#\"SELECT \"{}\".* FROM \"{}\" JOIN \"{}\" ON \"{}\".\"{}\" = \"{}\".\"{}\"{}\"#,\n            self.right_col.table_name(),\n            self.left_col.table_name(),\n            self.right_col.table_name(),\n            self.left_col.table_name(),\n            self.left_col.column_name(),\n            self.right_col.table_name(),\n            self.right_col.column_name(),\n            where_clause\n        );\n        RawQuery::new(sql)\n    }\n}\n"
  },
  {
    "path": "crates/query-builder/src/lib.rs",
    "content": "pub mod expr;\npub mod join;\npub mod table;\n\npub use expr::*;\npub use join::*;\nuse spacetimedb_lib::{sats::impl_st, AlgebraicType, SpacetimeType};\npub use table::*;\n\nconst QUERY_VIEW_RETURN_TAG: &str = \"__query__\";\n\n/// Trait implemented by all query builder types. Use `impl Query<T>` as a\n/// return type for view functions and helpers.\npub trait Query<T> {\n    fn into_sql(self) -> String;\n}\n\n/// The concrete SQL query produced by calling `.build()` on a builder.\npub struct RawQuery<T> {\n    pub(crate) sql: String,\n    _marker: std::marker::PhantomData<T>,\n}\n\nimpl<T> RawQuery<T> {\n    pub fn new(sql: String) -> Self {\n        Self {\n            sql,\n            _marker: std::marker::PhantomData,\n        }\n    }\n\n    pub fn sql(&self) -> &str {\n        &self.sql\n    }\n}\n\nimpl<T> Query<T> for RawQuery<T> {\n    fn into_sql(self) -> String {\n        self.sql\n    }\n}\n\nimpl_st!(\n    [T: SpacetimeType] RawQuery<T>,\n    ts => AlgebraicType::product([(QUERY_VIEW_RETURN_TAG, T::make_type(ts))])\n);\n\n#[cfg(test)]\nmod tests {\n    use spacetimedb_lib::{sats::i256, TimeDuration};\n\n    use super::*;\n    struct User;\n    #[derive(Clone)]\n    struct UserCols {\n        pub id: Col<User, i32>,\n        pub name: Col<User, String>,\n        pub age: Col<User, i32>,\n        pub online: Col<User, bool>,\n    }\n    impl UserCols {\n        fn new(table_name: &'static str) -> Self {\n            Self {\n                id: Col::new(table_name, \"id\"),\n                name: Col::new(table_name, \"name\"),\n                age: Col::new(table_name, \"age\"),\n                online: Col::new(table_name, \"online\"),\n            }\n        }\n    }\n    impl HasCols for User {\n        type Cols = UserCols;\n        fn cols(table_name: &'static str) -> Self::Cols {\n            UserCols::new(table_name)\n        }\n    }\n    fn users() -> Table<User> {\n        Table::new(\"users\")\n    }\n    fn other() -> Table<Other> {\n        Table::new(\"other\")\n    }\n    struct OtherCols {\n        pub uid: Col<Other, i32>,\n    }\n\n    impl HasCols for Other {\n        type Cols = OtherCols;\n        fn cols(table: &'static str) -> Self::Cols {\n            OtherCols {\n                uid: Col::new(table, \"uid\"),\n            }\n        }\n    }\n    struct IxUserCols {\n        pub id: IxCol<User, i32>,\n    }\n    impl HasIxCols for User {\n        type IxCols = IxUserCols;\n        fn ix_cols(table_name: &'static str) -> Self::IxCols {\n            IxUserCols {\n                id: IxCol::new(table_name, \"id\"),\n            }\n        }\n    }\n    struct Other;\n    #[derive(Clone)]\n    struct IxOtherCols {\n        pub uid: IxCol<Other, i32>,\n    }\n    impl HasIxCols for Other {\n        type IxCols = IxOtherCols;\n        fn ix_cols(table_name: &'static str) -> Self::IxCols {\n            IxOtherCols {\n                uid: IxCol::new(table_name, \"uid\"),\n            }\n        }\n    }\n    impl CanBeLookupTable for User {}\n    impl CanBeLookupTable for Other {}\n    fn norm(s: &str) -> String {\n        s.split_whitespace().collect::<Vec<_>>().join(\" \")\n    }\n    #[test]\n    fn test_simple_select() {\n        let q = users().build();\n        assert_eq!(q.sql(), r#\"SELECT * FROM \"users\"\"#);\n    }\n    #[test]\n    fn test_where_literal() {\n        let q = users().r#where(|c| c.id.eq(10)).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE (\"users\".\"id\" = 10)\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n    #[test]\n    fn test_where_multiple_predicates() {\n        let q = users().r#where(|c| c.id.eq(10)).r#where(|c| c.age.gt(18)).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE ((\"users\".\"id\" = 10) AND (\"users\".\"age\" > 18))\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_where_bool_column_directly() {\n        let q = users().r#where(|c| c.online).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE (\"users\".\"online\" = TRUE)\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_where_gte_lte() {\n        let q = users().r#where(|c| c.age.gte(18)).r#where(|c| c.age.lte(30)).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE ((\"users\".\"age\" >= 18) AND (\"users\".\"age\" <= 30))\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_column_column_comparison() {\n        let q = users().r#where(|c| c.age.gt(c.id)).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE (\"users\".\"age\" > \"users\".\"id\")\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n    #[test]\n    fn test_ne_comparison() {\n        let q = users().r#where(|c| c.name.ne(\"Shub\".to_string())).build();\n        assert!(q.sql().contains(\"name\"), \"Expected a name comparison\");\n        assert!(q.sql().contains(\"<>\"));\n    }\n\n    #[test]\n    fn test_not_comparison() {\n        let q = users().r#where(|c| c.name.eq(\"Alice\".to_string()).not()).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE (NOT (\"users\".\"name\" = 'Alice'))\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_not_with_and() {\n        let q = users()\n            .r#where(|c| c.name.eq(\"Alice\".to_string()).not().and(c.age.gt(18)))\n            .build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE ((NOT (\"users\".\"name\" = 'Alice')) AND (\"users\".\"age\" > 18))\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_filter_alias() {\n        let q = users().filter(|c| c.id.eq(5)).filter(|c| c.age.lt(30)).build();\n        let expected = r#\"SELECT * FROM \"users\" WHERE ((\"users\".\"id\" = 5) AND (\"users\".\"age\" < 30))\"#;\n        assert_eq!(norm(q.sql()), norm(expected));\n    }\n\n    #[test]\n    fn test_or_comparison() {\n        let q = users()\n            .r#where(|c| c.name.ne(\"Shub\".to_string()).or(c.name.ne(\"Pop\".to_string())))\n            .build();\n\n        let expected = r#\"SELECT * FROM \"users\" WHERE ((\"users\".\"name\" <> 'Shub') OR (\"users\".\"name\" <> 'Pop'))\"#;\n        assert_eq!(q.sql, expected);\n    }\n\n    #[test]\n    fn test_format_expr_column_literal() {\n        let expr = BoolExpr::Eq(\n            Operand::Column(ColumnRef::<User>::new(\"user\", \"id\")),\n            Operand::Literal(LiteralValue::new(\"42\".to_string())),\n        );\n        let sql = format_expr(&expr);\n        assert!(sql.contains(\"id\"), \"Missing col\");\n        assert!(sql.contains(\"42\"), \"Missing literal\");\n    }\n\n    #[test]\n    fn test_format_semi_join_expr() {\n        let user = users();\n        let other = other();\n        let sql = user.left_semijoin(other, |u, o| u.id.eq(o.uid)).build().sql;\n        let expected = r#\"SELECT \"users\".* FROM \"users\" JOIN \"other\" ON \"users\".\"id\" = \"other\".\"uid\"\"#;\n        assert_eq!(sql, expected);\n    }\n\n    #[test]\n    fn test_left_semijoin_with_where_expr() {\n        let user = users();\n        let o = other();\n        let sql = user\n            .left_semijoin(o, |u, o| u.id.eq(o.uid))\n            .r#where(|u| u.id.eq(1i32))\n            .r#where(|u| u.id.gt(10))\n            .build()\n            .sql;\n        let expected = r#\"SELECT \"users\".* FROM \"users\" JOIN \"other\" ON \"users\".\"id\" = \"other\".\"uid\" WHERE ((\"users\".\"id\" = 1) AND (\"users\".\"id\" > 10))\"#;\n        assert_eq!(sql, expected);\n        let user = users();\n        let other = other();\n        let sql2 = user\n            .r#where(|u| u.id.eq(1))\n            .r#where(|u| u.id.gt(10))\n            .left_semijoin(other, |u, o| u.id.eq(o.uid))\n            .build()\n            .sql;\n        assert_eq!(sql2, expected);\n    }\n    #[test]\n    fn test_right_semijoin_with_where_expr() {\n        let user = users();\n        let o = other();\n        let sql = user\n            .right_semijoin(o, |u, o| u.id.eq(o.uid))\n            .r#where(|o| o.uid.eq(1))\n            .r#where(|o| o.uid.gt(10))\n            .build()\n            .sql;\n        let expected = r#\"SELECT \"other\".* FROM \"users\" JOIN \"other\" ON \"users\".\"id\" = \"other\".\"uid\" WHERE ((\"other\".\"uid\" = 1) AND (\"other\".\"uid\" > 10))\"#;\n        assert_eq!(sql, expected);\n    }\n\n    #[test]\n    fn test_right_semijoin_with_left_and_right_where_expr() {\n        let user = users();\n        let o = other();\n        let sql = user\n            .r#where(|u| u.id.eq(1))\n            .right_semijoin(o, |u, o| u.id.eq(o.uid))\n            .r#where(|o| o.uid.gt(10))\n            .build()\n            .sql;\n        let expected = r#\"SELECT \"other\".* FROM \"users\" JOIN \"other\" ON \"users\".\"id\" = \"other\".\"uid\" WHERE (\"users\".\"id\" = 1) AND (\"other\".\"uid\" > 10)\"#;\n        assert_eq!(sql, expected);\n    }\n\n    #[test]\n    fn test_literals() {\n        use spacetimedb_lib::{ConnectionId, Identity};\n\n        struct Player;\n        struct PlayerCols {\n            score: Col<Player, i32>,\n            name: Col<Player, String>,\n            active: Col<Player, bool>,\n            connection_id: Col<Player, ConnectionId>,\n            cells: Col<Player, i256>,\n            identity: Col<Player, Identity>,\n            ts: Col<Player, spacetimedb_lib::Timestamp>,\n            bytes: Col<Player, Vec<u8>>,\n        }\n\n        impl HasCols for Player {\n            type Cols = PlayerCols;\n            fn cols(table_name: &'static str) -> Self::Cols {\n                PlayerCols {\n                    score: Col::new(table_name, \"score\"),\n                    name: Col::new(table_name, \"name\"),\n                    active: Col::new(table_name, \"active\"),\n                    connection_id: Col::new(table_name, \"connection_id\"),\n                    cells: Col::new(table_name, \"cells\"),\n                    identity: Col::new(table_name, \"identity\"),\n                    ts: Col::new(table_name, \"ts\"),\n                    bytes: Col::new(table_name, \"bytes\"),\n                }\n            }\n        }\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.score.eq(100)).build();\n\n        assert_eq!(q.sql, r#\"SELECT * FROM \"player\" WHERE (\"player\".\"score\" = 100)\"#);\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.name.ne(\"Alice\".to_string())).build();\n\n        assert_eq!(q.sql, r#\"SELECT * FROM \"player\" WHERE (\"player\".\"name\" <> 'Alice')\"#);\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.active.eq(true)).build();\n\n        assert_eq!(q.sql, r#\"SELECT * FROM \"player\" WHERE (\"player\".\"active\" = TRUE)\"#);\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.connection_id.eq(ConnectionId::ZERO)).build();\n\n        assert_eq!(\n            q.sql,\n            r#\"SELECT * FROM \"player\" WHERE (\"player\".\"connection_id\" = 0x00000000000000000000000000000000)\"#\n        );\n\n        let big_int: i256 = (i256::ONE << 120) * i256::from(-1);\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.cells.gt(big_int)).build();\n\n        assert_eq!(\n            q.sql,\n            r#\"SELECT * FROM \"player\" WHERE (\"player\".\"cells\" > -1329227995784915872903807060280344576)\"#,\n        );\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.identity.ne(Identity::ONE)).build();\n\n        assert_eq!(\n            q.sql,\n            r#\"SELECT * FROM \"player\" WHERE (\"player\".\"identity\" <> 0x0000000000000000000000000000000000000000000000000000000000000001)\"#\n        );\n\n        let ts = spacetimedb_lib::Timestamp::UNIX_EPOCH + TimeDuration::from_micros(1000);\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.ts.eq(ts)).build();\n        assert_eq!(\n            q.sql,\n            r#\"SELECT * FROM \"player\" WHERE (\"player\".\"ts\" = '1970-01-01T00:00:00.001+00:00')\"#\n        );\n\n        let table = Table::<Player>::new(\"player\");\n        let q = table.r#where(|c| c.bytes.eq(vec![1, 2, 3, 4, 255])).build();\n\n        assert_eq!(\n            q.sql,\n            r#\"SELECT * FROM \"player\" WHERE (\"player\".\"bytes\" = 0x01020304ff)\"#\n        );\n    }\n}\n"
  },
  {
    "path": "crates/query-builder/src/table.rs",
    "content": "use std::marker::PhantomData;\n\nuse crate::Operand;\n\nuse super::{format_expr, BoolExpr, Query, RawQuery, RHS};\n\npub type TableNameStr = &'static str;\n\npub trait HasCols {\n    type Cols;\n    fn cols(name: TableNameStr) -> Self::Cols;\n}\n\npub trait HasIxCols {\n    type IxCols;\n    fn ix_cols(name: TableNameStr) -> Self::IxCols;\n}\n\n/// Marker trait for tables that can appear as the right/inner/lookup\n/// table in a semi-join. Event tables do NOT implement this trait,\n/// preventing them from being used as the lookup side of a join.\npub trait CanBeLookupTable: HasIxCols {}\n\npub struct Table<T> {\n    pub(super) table_name: TableNameStr,\n    _marker: PhantomData<T>,\n}\n\nimpl<T> Table<T> {\n    pub fn new(table_name: TableNameStr) -> Self {\n        Self {\n            table_name,\n            _marker: PhantomData,\n        }\n    }\n\n    pub(super) fn name(&self) -> TableNameStr {\n        self.table_name\n    }\n}\n\n/// Represents a column of type V in table T.\npub struct Col<T, V> {\n    pub(super) col: ColumnRef<T>,\n    _marker: PhantomData<V>,\n}\n\nimpl<T, V> Col<T, V> {\n    pub fn new(table_name: &'static str, column_name: &'static str) -> Self {\n        Self {\n            col: ColumnRef::new(table_name, column_name),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<T, V> Copy for Col<T, V> {}\nimpl<T, V> Clone for Col<T, V> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T, V> Col<T, V> {\n    pub fn eq<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Eq(self.into(), rhs.to_expr())\n    }\n    pub fn ne<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Ne(self.into(), rhs.to_expr())\n    }\n    pub fn gt<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Gt(self.into(), rhs.to_expr())\n    }\n    pub fn lt<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Lt(self.into(), rhs.to_expr())\n    }\n    pub fn gte<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Gte(self.into(), rhs.to_expr())\n    }\n    pub fn lte<R: RHS<T, V>>(self, rhs: R) -> BoolExpr<T> {\n        BoolExpr::Lte(self.into(), rhs.to_expr())\n    }\n}\n\nimpl<T, V> From<Col<T, V>> for Operand<T> {\n    fn from(col: Col<T, V>) -> Self {\n        Operand::Column(col.col)\n    }\n}\n\npub struct ColumnRef<T> {\n    table_name: &'static str,\n    column_name: &'static str,\n    _marker: PhantomData<T>,\n}\n\nimpl<T> ColumnRef<T> {\n    pub(super) fn new(table_name: &'static str, column_name: &'static str) -> Self {\n        Self {\n            table_name,\n            column_name,\n            _marker: PhantomData,\n        }\n    }\n\n    pub(super) fn fmt(&self) -> String {\n        format!(\"\\\"{}\\\".\\\"{}\\\"\", self.table_name, self.column_name)\n    }\n\n    pub(super) fn column_name(&self) -> &'static str {\n        self.column_name\n    }\n\n    pub(super) fn table_name(&self) -> &'static str {\n        self.table_name\n    }\n}\n\nimpl<T> Copy for ColumnRef<T> {}\nimpl<T> Clone for ColumnRef<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\npub struct FromWhere<T> {\n    pub(super) table_name: TableNameStr,\n    pub(super) expr: BoolExpr<T>,\n}\n\nimpl<T: HasCols> Query<T> for Table<T> {\n    fn into_sql(self) -> String {\n        format!(r#\"SELECT * FROM \"{}\"\"#, self.table_name)\n    }\n}\n\nimpl<T: HasCols> Query<T> for FromWhere<T> {\n    fn into_sql(self) -> String {\n        format!(\n            r#\"SELECT * FROM \"{}\" WHERE {}\"#,\n            self.table_name,\n            format_expr(&self.expr)\n        )\n    }\n}\n\nimpl<T: HasCols> Table<T> {\n    pub fn build(self) -> RawQuery<T> {\n        RawQuery::new(format!(r#\"SELECT * FROM \"{}\"\"#, self.table_name))\n    }\n\n    pub fn r#where<F, E>(self, f: F) -> FromWhere<T>\n    where\n        F: Fn(&T::Cols) -> E,\n        E: Into<BoolExpr<T>>,\n    {\n        let expr = f(&T::cols(self.table_name)).into();\n        FromWhere {\n            table_name: self.table_name,\n            expr,\n        }\n    }\n\n    // Filter is an alias for where\n    pub fn filter<F, E>(self, f: F) -> FromWhere<T>\n    where\n        F: Fn(&T::Cols) -> E,\n        E: Into<BoolExpr<T>>,\n    {\n        self.r#where(f)\n    }\n}\n\nimpl<T: HasCols> FromWhere<T> {\n    pub fn r#where<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&T::Cols) -> E,\n        E: Into<BoolExpr<T>>,\n    {\n        let extra = f(&T::cols(self.table_name)).into();\n        Self {\n            table_name: self.table_name,\n            expr: self.expr.and(extra),\n        }\n    }\n\n    // Filter is an alias for where\n    pub fn filter<F, E>(self, f: F) -> Self\n    where\n        F: Fn(&T::Cols) -> E,\n        E: Into<BoolExpr<T>>,\n    {\n        self.r#where(f)\n    }\n\n    pub fn build(self) -> RawQuery<T> {\n        let sql = format!(\n            r#\"SELECT * FROM \"{}\" WHERE {}\"#,\n            self.table_name,\n            format_expr(&self.expr)\n        );\n        RawQuery::new(sql)\n    }\n}\n"
  },
  {
    "path": "crates/sats/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-sats\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Spacetime Algebraic Type Notation\"\nrust-version.workspace = true\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[features]\nserde = [\"dep:serde\"]\n# Allows using `Arbitrary` impls defined in this crate.\nproptest = [\"dep:proptest\", \"dep:proptest-derive\"]\n# Allows using additional test-only methods defined in this crate.\ntest = [\"proptest\"]\n# Impls `Serialize` and `Deserialize` for `blake3::Hash`.\n# Used by `spacetimedb_table`,\n# which serializes and deserializes Blake3 hashes during snapshotting.\nblake3 = [\"dep:blake3\"]\n# Impls `Serialize`, `Deserialize` and `SpacetimeType` for `ByteString`.\n# Used by `spacetimedb_client_api_messages`,\n# which encodes rows and reducer arguments as `ByteString`s containing JSON.\n# Feature-gated because `bytestring` depends on `serde`,\n# which we don't want in `spacetimedb_bindings`.\nbytestring = [\"dep:bytestring\"]\nmetrics_impls = [\"dep:spacetimedb-metrics\"]\n# Gated to avoid including this in `spacetimedb_bindings`.\nmemory-usage = [\"dep:spacetimedb-memory-usage\", \"spacetimedb-primitives/memory-usage\"]\n\n[dependencies]\nspacetimedb-bindings-macro.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-memory-usage = { workspace = true, optional = true, default-features = false, features = [\n    \"ethnum\",\n    \"decorum\",\n] }\nspacetimedb-metrics = { workspace = true, optional = true }\n\nanyhow.workspace = true\narrayvec.workspace = true\nbitflags.workspace = true\nbytes.workspace = true\nbytemuck.workspace = true\nbytestring = { workspace = true, optional = true }\nchrono = { workspace = true, features = [\"alloc\"] }\ndecorum.workspace = true\nderive_more.workspace = true\nenum-as-inner.workspace = true\nethnum.workspace = true\nhex.workspace = true\nitertools.workspace = true\nlean_string.workspace = true\n# For the 'proptest' feature.\nproptest = { workspace = true, optional = true }\nproptest-derive = { workspace = true, optional = true }\nsha3.workspace = true\nsecond-stack.workspace = true\nserde = { workspace = true, optional = true }\nsmallvec.workspace = true\nthiserror.workspace = true\nuuid.workspace = true\n\n[target.'cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))'.dependencies]\nuuid = { workspace = true, features = [\"v4\", \"v7\"] }\nrand = { workspace = true, features = [\"std\"] }\n\n# For the `blake3` feature.\nblake3 = { workspace = true, optional = true }\n\n[dev-dependencies]\nahash.workspace = true\nbytes.workspace = true\nrand.workspace = true\n# Also as dev-dependencies for use in _this_ crate's tests.\nproptest.workspace = true\nproptest-derive.workspace = true\nserde_json.workspace = true\nserde.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/sats/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/sats/proptest-regressions/algebraic_value_hash.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc aaa05e16925268348653cb8c1945d820f2f8da931fd7ff9a895178d443e0e64f # shrinks to (ty, val) = (Builtin(Array(ArrayType { elem_ty: Builtin(U8) })), Array([]))\ncc f7d1b5754a5194cf0e82be30da1ea9469ba18ab1a813342baecf8127bc28dfdf # shrinks to (ty, val) = (Builtin(Array(ArrayType { elem_ty: Builtin(U256) })), Array([]))\ncc 1d08298a411c269c6d969f50e19c1022f00bc5e9cbf4600382aa00b200e1cd82 # shrinks to (ty, val) = (Builtin(Array(ArrayType { elem_ty: Builtin(U256) })), Array([]))\n"
  },
  {
    "path": "crates/sats/proptest-regressions/timestamp.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 9260b4651caa8f40a8bad964329b762b19e6c6d4acf56702caf6f4f160184a5d # shrinks to micros = 910692730085477581\n"
  },
  {
    "path": "crates/sats/proptest-regressions/typespace.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 9b3c83dd8794aa57dc9f2a3dd07111510ab1c537a23d6cdbcaa6d8dda3445c4c # shrinks to typespace = Typespace { types: [Builtin(Bool), Builtin(Bool), Builtin(Bool), Builtin(Bool), Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }] })] }\n"
  },
  {
    "path": "crates/sats/src/algebraic_type/fmt.rs",
    "content": "use super::{AlgebraicType, ProductType, SumType};\nuse crate::de::fmt_fn;\nuse core::fmt::Display;\nuse fmt_algebraic_type as fmt;\n\n/// Wraps the algebraic `ty` into a `Display`able.\n///\n/// NOTE: You might ask: Why do we have a formatter and a notation for\n/// `AlgebraicType`s if we don't have an encoding for `AlgebraicType`s?\n///\n/// This is because we just want an easier to read text format for algebraic\n/// types. This could just as easily take in an algebraic value, which\n/// represents an algebraic type and format it that way. It's just more\n/// convenient to format it from the Rust type.\npub fn fmt_algebraic_type(ty: &AlgebraicType) -> impl '_ + Display {\n    fmt_fn(move |f| match ty {\n        AlgebraicType::Ref(r) => write!(f, \"{r}\"),\n        AlgebraicType::Sum(ty) => write!(f, \"{}\", fmt_sum_type(ty)),\n        AlgebraicType::Product(ty) => write!(f, \"{}\", fmt_product_type(ty)),\n        AlgebraicType::Array(a) => write!(f, \"Array<{}>\", fmt(&a.elem_ty)),\n        AlgebraicType::Bool => write!(f, \"Bool\"),\n        AlgebraicType::I8 => write!(f, \"I8\"),\n        AlgebraicType::U8 => write!(f, \"U8\"),\n        AlgebraicType::I16 => write!(f, \"I16\"),\n        AlgebraicType::U16 => write!(f, \"U16\"),\n        AlgebraicType::I32 => write!(f, \"I32\"),\n        AlgebraicType::U32 => write!(f, \"U32\"),\n        AlgebraicType::I64 => write!(f, \"I64\"),\n        AlgebraicType::U64 => write!(f, \"U64\"),\n        AlgebraicType::I128 => write!(f, \"I128\"),\n        AlgebraicType::U128 => write!(f, \"U128\"),\n        AlgebraicType::I256 => write!(f, \"I256\"),\n        AlgebraicType::U256 => write!(f, \"U256\"),\n        AlgebraicType::F32 => write!(f, \"F32\"),\n        AlgebraicType::F64 => write!(f, \"F64\"),\n        AlgebraicType::String => write!(f, \"String\"),\n    })\n}\n\n/// Wraps the builtin `ty` into a `Display`able.\npub fn fmt_product_type(ty: &ProductType) -> impl '_ + Display {\n    fmt_fn(move |f| {\n        write!(f, \"(\")?;\n        for (i, e) in ty.elements.iter().enumerate() {\n            if let Some(name) = &e.name {\n                write!(f, \"{name}\")?;\n            } else {\n                write!(f, \"{i}\")?;\n            }\n            write!(f, \": \")?;\n            write!(f, \"{}\", fmt(&e.algebraic_type))?;\n            if i < ty.elements.len() - 1 {\n                write!(f, \", \")?;\n            }\n        }\n        write!(f, \")\")\n    })\n}\n\n/// Wraps the builtin `ty` into a `Display`able.\nfn fmt_sum_type(ty: &SumType) -> impl '_ + Display {\n    fmt_fn(move |f| {\n        if ty.variants.is_empty() {\n            return write!(f, \"(|)\");\n        }\n        write!(f, \"(\")?;\n        for (i, e) in ty.variants.iter().enumerate() {\n            if let Some(name) = &e.name {\n                write!(f, \"{name}\")?;\n                write!(f, \": \")?;\n            }\n            write!(f, \"{}\", fmt(&e.algebraic_type))?;\n            if i < ty.variants.len() - 1 {\n                write!(f, \" | \")?;\n            }\n        }\n        write!(f, \")\")\n    })\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_type/map_notation.rs",
    "content": "use crate::{de::fmt_fn, AlgebraicType};\nuse core::fmt;\n\n/// Wraps an algebraic `ty` in a `Display` impl using a object/map JSON-like notation.\npub fn fmt_algebraic_type(ty: &AlgebraicType) -> impl '_ + fmt::Display {\n    use fmt_algebraic_type as fmt;\n\n    // Format name/index + type.\n    let fmt_name_ty = |f: &mut fmt::Formatter<'_>, i, name, ty| match name {\n        Some(name) => write!(f, \"{}: {}\", name, fmt(ty)),\n        None => write!(f, \"{}: {}\", i, fmt(ty)),\n    };\n\n    fmt_fn(move |f| match ty {\n        AlgebraicType::Ref(r) => write!(f, \"{{ ty_: Ref, 0: {} }}\", r.0),\n        AlgebraicType::Sum(ty) => {\n            write!(f, \"{{ ty_: Sum\")?;\n            for (i, e_ty) in ty.variants.iter().enumerate() {\n                write!(f, \", \")?;\n                fmt_name_ty(f, i, e_ty.name.as_deref(), &e_ty.algebraic_type)?;\n            }\n            write!(f, \" }}\")\n        }\n        AlgebraicType::Product(ty) => {\n            write!(f, \"{{ ty_: Product\")?;\n            for (i, e_ty) in ty.elements.iter().enumerate() {\n                write!(f, \", \")?;\n                fmt_name_ty(f, i, e_ty.name.as_deref(), &e_ty.algebraic_type)?;\n            }\n            write!(f, \" }}\")\n        }\n        AlgebraicType::Array(ty) => write!(f, \"{{ ty_: Array, 0: {} }}\", fmt(&ty.elem_ty)),\n        AlgebraicType::Bool => write!(f, \"{{ ty_: Bool }}\"),\n        AlgebraicType::I8 => write!(f, \"{{ ty_: I8 }}\"),\n        AlgebraicType::U8 => write!(f, \"{{ ty_: U8 }}\"),\n        AlgebraicType::I16 => write!(f, \"{{ ty_: I16 }}\"),\n        AlgebraicType::U16 => write!(f, \"{{ ty_: U16 }}\"),\n        AlgebraicType::I32 => write!(f, \"{{ ty_: I32 }}\"),\n        AlgebraicType::U32 => write!(f, \"{{ ty_: U32 }}\"),\n        AlgebraicType::I64 => write!(f, \"{{ ty_: I64 }}\"),\n        AlgebraicType::U64 => write!(f, \"{{ ty_: U64 }}\"),\n        AlgebraicType::I128 => write!(f, \"{{ ty_: I128 }}\"),\n        AlgebraicType::U128 => write!(f, \"{{ ty_: U128 }}\"),\n        AlgebraicType::I256 => write!(f, \"{{ ty_: I256 }}\"),\n        AlgebraicType::U256 => write!(f, \"{{ ty_: U256 }}\"),\n        AlgebraicType::F32 => write!(f, \"{{ ty_: F32 }}\"),\n        AlgebraicType::F64 => write!(f, \"{{ ty_: F64 }}\"),\n        AlgebraicType::String => write!(f, \"{{ ty_: String }}\"),\n    })\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_type.rs",
    "content": "pub mod fmt;\npub mod map_notation;\n\nuse crate::algebraic_value::de::{ValueDeserializeError, ValueDeserializer};\nuse crate::algebraic_value::ser::value_serialize;\nuse crate::de::Deserialize;\nuse crate::meta_type::MetaType;\nuse crate::product_type::{CONNECTION_ID_TAG, IDENTITY_TAG, TIMESTAMP_TAG, TIME_DURATION_TAG, UUID_TAG};\nuse crate::sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG, RESULT_ERR_TAG, RESULT_OK_TAG};\nuse crate::typespace::Typespace;\nuse crate::{i256, u256};\nuse crate::{AlgebraicTypeRef, AlgebraicValue, ArrayType, ProductType, SpacetimeType, SumType, SumTypeVariant};\nuse derive_more::From;\nuse enum_as_inner::EnumAsInner;\n\n/// The SpacetimeDB Algebraic Type System (SATS) is a structural type system in\n/// which a nominal type system can be constructed.\n///\n/// The type system unifies the concepts sum types, product types, scalar value types,\n/// and convenience types strings, arrays, and maps,\n/// into a single type system.\n#[derive(EnumAsInner, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType, From)]\n#[sats(crate = crate)]\npub enum AlgebraicType {\n    /// A type where the definition is given by the typing context (`Typespace`).\n    /// In other words, this is defined by a pointer to another `AlgebraicType`.\n    ///\n    /// This should not be conflated with reference and pointer types in languages like Rust,\n    /// In other words, this is not `&T` or `*const T`.\n    Ref(AlgebraicTypeRef),\n    /// A structural sum type.\n    ///\n    /// Unlike most languages, sums in SATs are *[structural]* and not nominal.\n    /// When checking whether two nominal types are the same,\n    /// their names and/or declaration sites (e.g., module / namespace) are considered.\n    /// Meanwhile, a structural type system would only check the structure of the type itself,\n    /// e.g., the names of its variants and their inner data types in the case of a sum.\n    ///\n    /// This is also known as a discriminated union (implementation) or disjoint union.\n    /// Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).\n    ///\n    /// These structures are known as sum types because the number of possible values a sum\n    /// ```text\n    /// { N_0(T_0), N_1(T_1), ..., N_n(T_n) }\n    /// ```\n    /// is:\n    /// ```text\n    /// Σ (i ∈ 0..n). values(T_i)\n    /// ```\n    /// so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.\n    ///\n    /// See also:\n    /// - <https://en.wikipedia.org/wiki/Tagged_union>\n    /// - <https://ncatlab.org/nlab/show/sum+type>\n    ///\n    /// [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n    Sum(SumType),\n    /// A structural product type.\n    ///\n    /// This is also known as `struct` and `tuple` in many languages,\n    /// but note that unlike most languages, sums in SATs are *[structural]* and not nominal.\n    /// When checking whether two nominal types are the same,\n    /// their names and/or declaration sites (e.g., module / namespace) are considered.\n    /// Meanwhile, a structural type system would only check the structure of the type itself,\n    /// e.g., the names of its fields and their types in the case of a record.\n    /// The name \"product\" comes from category theory.\n    ///\n    /// See also:\n    /// - <https://en.wikipedia.org/wiki/Record_(computer_science)>\n    /// - <https://ncatlab.org/nlab/show/product+type>\n    ///\n    /// These structures are known as product types because the number of possible values in product\n    /// ```text\n    /// { N_0: T_0, N_1: T_1, ..., N_n: T_n }\n    /// ```\n    /// is:\n    /// ```text\n    /// Π (i ∈ 0..n). values(T_i)\n    /// ```\n    /// so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.\n    ///\n    /// [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n    Product(ProductType),\n    /// The type of array values where elements are of a base type `elem_ty`.\n    /// Values [`AlgebraicValue::Array(array)`](crate::AlgebraicValue::Array) will have this type.\n    Array(ArrayType),\n    /// The UTF-8 encoded `String` type.\n    /// Values [`AlgebraicValue::String(s)`](crate::AlgebraicValue::String) will have this type.\n    ///\n    /// This type exists for convenience and because it is easy to just use Rust's `String` (UTF-8)\n    /// as opposed to rolling your own equivalent byte-array based UTF-8 encoding.\n    String,\n    /// The bool type. Values [`AlgebraicValue::Bool(b)`](crate::AlgebraicValue::Bool) will have this type.\n    Bool,\n    /// The `I8` type. Values [`AlgebraicValue::I8(v)`](crate::AlgebraicValue::I8) will have this type.\n    I8,\n    /// The `U8` type. Values [`AlgebraicValue::U8(v)`](crate::AlgebraicValue::U8) will have this type.\n    U8,\n    /// The `I16` type. Values [`AlgebraicValue::I16(v)`](crate::AlgebraicValue::I16) will have this type.\n    I16,\n    /// The `U16` type. Values [`AlgebraicValue::U16(v)`](crate::AlgebraicValue::U16) will have this type.\n    U16,\n    /// The `I32` type. Values [`AlgebraicValue::I32(v)`](crate::AlgebraicValue::I32) will have this type.\n    I32,\n    /// The `U32` type. Values [`AlgebraicValue::U32(v)`](crate::AlgebraicValue::U32) will have this type.\n    U32,\n    /// The `I64` type. Values [`AlgebraicValue::I64(v)`](crate::AlgebraicValue::I64) will have this type.\n    I64,\n    /// The `U64` type. Values [`AlgebraicValue::U64(v)`](crate::AlgebraicValue::U64) will have this type.\n    U64,\n    /// The `I128` type. Values [`AlgebraicValue::I128(v)`](crate::AlgebraicValue::I128) will have this type.\n    I128,\n    /// The `U128` type. Values [`AlgebraicValue::U128(v)`](crate::AlgebraicValue::U128) will have this type.\n    U128,\n    /// The `I256` type. Values [`AlgebraicValue::I256(v)`](crate::AlgebraicValue::I256) will have this type.\n    I256,\n    /// The `U256` type. Values [`AlgebraicValue::U256(v)`](crate::AlgebraicValue::U256) will have this type.\n    U256,\n    /// The `F32` type. Values [`AlgebraicValue::F32(v)`](crate::AlgebraicValue::F32) will have this type.\n    F32,\n    /// The `F64` type. Values [`AlgebraicValue::F64(v)`](crate::AlgebraicValue::F64) will have this type.\n    F64,\n}\n\nimpl MetaType for AlgebraicType {\n    /// This is a static function that constructs the type of `AlgebraicType`\n    /// and returns it as an `AlgebraicType`.\n    ///\n    /// This could alternatively be implemented\n    /// as a regular AlgebraicValue or as a static variable.\n    fn meta_type() -> Self {\n        AlgebraicType::sum([\n            (\"ref\", AlgebraicTypeRef::meta_type()),\n            (\"sum\", SumType::meta_type()),\n            (\"product\", ProductType::meta_type()),\n            (\"array\", ArrayType::meta_type()),\n            (\"string\", AlgebraicType::unit()),\n            (\"bool\", AlgebraicType::unit()),\n            (\"i8\", AlgebraicType::unit()),\n            (\"u8\", AlgebraicType::unit()),\n            (\"i16\", AlgebraicType::unit()),\n            (\"u16\", AlgebraicType::unit()),\n            (\"i32\", AlgebraicType::unit()),\n            (\"u32\", AlgebraicType::unit()),\n            (\"i64\", AlgebraicType::unit()),\n            (\"u64\", AlgebraicType::unit()),\n            (\"i128\", AlgebraicType::unit()),\n            (\"u128\", AlgebraicType::unit()),\n            (\"i256\", AlgebraicType::unit()),\n            (\"u256\", AlgebraicType::unit()),\n            (\"f32\", AlgebraicType::unit()),\n            (\"f64\", AlgebraicType::unit()),\n        ])\n    }\n}\n\n/// Provided to enable `mem::take`.\nimpl Default for AlgebraicType {\n    fn default() -> Self {\n        Self::ZERO_REF\n    }\n}\n\nimpl AlgebraicType {\n    /// The first type in the typespace.\n    pub const ZERO_REF: Self = Self::Ref(AlgebraicTypeRef(0));\n\n    /// Returns whether this type is the `ConnectionId` type.\n    ///\n    /// Construct an instance of this type with [`Self::connection_id`]\n    pub fn is_connection_id(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_connection_id())\n    }\n\n    /// Returns whether this type is the conventional identity type.\n    pub fn is_identity(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_identity())\n    }\n\n    /// Returns whether this type is the conventional point-in-time `Timestamp` type.\n    pub fn is_timestamp(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_timestamp())\n    }\n\n    /// Returns whether this type is the conventional time-delta `TimeDuration` type.\n    pub fn is_time_duration(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_time_duration())\n    }\n\n    /// Returns whether this type is the conventional `UUID` type.\n    pub fn is_uuid(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_uuid())\n    }\n\n    /// Returns whether this type is the conventional `ScheduleAt` type.\n    pub fn is_schedule_at(&self) -> bool {\n        matches!(self, Self::Sum(p) if p.is_schedule_at())\n    }\n\n    /// Returns whether this type is a unit type.\n    pub fn is_unit(&self) -> bool {\n        matches!(self, Self::Product(p) if p.is_unit())\n    }\n\n    /// Returns whether this type is a never type.\n    pub fn is_never(&self) -> bool {\n        matches!(self, Self::Sum(p) if p.is_empty())\n    }\n\n    /// Returns whether this type is an option type.\n    pub fn is_option(&self) -> bool {\n        matches!(self, Self::Sum(p) if p.is_option())\n    }\n\n    /// If this type is the standard option type, returns the type of the `some` variant.\n    /// Otherwise, returns `None`.\n    pub fn as_option(&self) -> Option<&AlgebraicType> {\n        self.as_sum()?.as_option()\n    }\n\n    /// Returns whether this type is a result type.\n    pub fn is_result(&self) -> bool {\n        matches!(self, Self::Sum(p) if p.is_result())\n    }\n\n    /// If this type is the standard result type, returns the types of the `ok` and `err` variants.\n    /// Otherwise, returns `None`.\n    pub fn as_result(&self) -> Option<(&AlgebraicType, &AlgebraicType)> {\n        self.as_sum()?.as_result()\n    }\n\n    /// Returns whether this type is scalar or a string type.\n    pub fn is_scalar_or_string(&self) -> bool {\n        self.is_scalar() || self.is_string()\n    }\n\n    /// Returns whether this type is one which holds a scalar value.\n    ///\n    /// A scalar value is one not made up of other values, i.e., not composite.\n    /// These are all integer and float values,\n    /// i.e., integer and float types are scalar.\n    /// References to other types, i.e., [`AlgebraicType::Ref`]s are not scalar.\n    pub fn is_scalar(&self) -> bool {\n        self.is_bool() || self.is_integer() || self.is_float()\n    }\n\n    /// Returns whether the type is a signed integer type.\n    pub fn is_signed(&self) -> bool {\n        matches!(\n            self,\n            Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::I256\n        )\n    }\n\n    /// Returns whether the type is an unsigned integer type.\n    pub fn is_unsigned(&self) -> bool {\n        matches!(\n            self,\n            Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::U256\n        )\n    }\n\n    /// Returns whether this type is one of the integer types, e.g., `U64` and `I32`.\n    pub fn is_integer(&self) -> bool {\n        self.is_signed() || self.is_unsigned()\n    }\n\n    /// Returns whether the type is a float type.\n    pub fn is_float(&self) -> bool {\n        matches!(self, Self::F32 | Self::F64)\n    }\n\n    /// The canonical 0-element unit type.\n    pub fn unit() -> Self {\n        let fs: [AlgebraicType; 0] = [];\n        Self::product(fs)\n    }\n\n    /// The canonical 0-variant \"never\" / \"absurd\" / \"void\" type.\n    pub fn never() -> Self {\n        let vs: [SumTypeVariant; 0] = [];\n        Self::sum(vs)\n    }\n\n    /// A type representing an array of `U8`s.\n    pub fn bytes() -> Self {\n        Self::array(Self::U8)\n    }\n\n    /// Returns whether this type is `AlgebraicType::bytes()`.\n    pub fn is_bytes(&self) -> bool {\n        self.as_array().is_some_and(|ty| ty.elem_ty.is_u8())\n    }\n\n    /// Whether this type, or the types it references, contain any `AlgebraicTypeRef`s.\n    pub fn contains_refs(&self) -> bool {\n        match self {\n            AlgebraicType::Ref(_) => true,\n            AlgebraicType::Product(ProductType { elements }) => {\n                elements.iter().any(|elem| elem.algebraic_type.contains_refs())\n            }\n            AlgebraicType::Sum(SumType { variants }) => {\n                variants.iter().any(|variant| variant.algebraic_type.contains_refs())\n            }\n            AlgebraicType::Array(array) => array.elem_ty.contains_refs(),\n            _ => false,\n        }\n    }\n\n    /// Returns a sum type with the given `sum`.\n    pub fn sum<S: Into<SumType>>(sum: S) -> Self {\n        AlgebraicType::Sum(sum.into())\n    }\n\n    /// Returns a product type with the given `prod`.\n    pub fn product<P: Into<ProductType>>(prod: P) -> Self {\n        AlgebraicType::Product(prod.into())\n    }\n\n    /// Returns a structural option type where `some_type` is the type for the `some` variant.\n    pub fn option(some_type: Self) -> Self {\n        Self::sum([(OPTION_SOME_TAG, some_type), (OPTION_NONE_TAG, AlgebraicType::unit())])\n    }\n\n    /// Returns a structural result type where `ok_type` is the type for the `ok` variant\n    /// and `err_type` is the type for the `err` variant.\n    pub fn result(ok_type: Self, err_type: Self) -> Self {\n        Self::sum([(RESULT_OK_TAG, ok_type), (RESULT_ERR_TAG, err_type)])\n    }\n\n    /// Returns an unsized array type where the element type is `ty`.\n    pub fn array(ty: Self) -> Self {\n        ArrayType { elem_ty: Box::new(ty) }.into()\n    }\n\n    /// Construct a copy of the `Identity` type.\n    pub fn identity() -> Self {\n        AlgebraicType::product([(IDENTITY_TAG, AlgebraicType::U256)])\n    }\n\n    /// Construct a copy of the `ConnectionId` type.\n    pub fn connection_id() -> Self {\n        AlgebraicType::product([(CONNECTION_ID_TAG, AlgebraicType::U128)])\n    }\n\n    /// Construct a copy of the point-in-time `Timestamp` type.\n    pub fn timestamp() -> Self {\n        AlgebraicType::product([(TIMESTAMP_TAG, AlgebraicType::I64)])\n    }\n\n    /// Construct a copy of the time-delta `TimeDuration` type.\n    pub fn time_duration() -> Self {\n        AlgebraicType::product([(TIME_DURATION_TAG, AlgebraicType::I64)])\n    }\n\n    /// Construct a copy of the `UUID` type.\n    pub fn uuid() -> Self {\n        AlgebraicType::product([(UUID_TAG, AlgebraicType::U128)])\n    }\n\n    /// Returns a sum type of unit variants with names taken from `var_names`.\n    pub fn simple_enum(var_names: impl Iterator<Item = &'static str>) -> Self {\n        Self::sum(var_names.into_iter().map(SumTypeVariant::unit).collect::<Box<[_]>>())\n    }\n\n    pub fn as_value(&self) -> AlgebraicValue {\n        value_serialize(self)\n    }\n\n    pub fn from_value(value: &AlgebraicValue) -> Result<Self, ValueDeserializeError> {\n        Self::deserialize(ValueDeserializer::from_ref(value))\n    }\n\n    #[inline]\n    /// Given an AlgebraicType, returns the min value for that type.\n    pub fn min_value(&self) -> Option<AlgebraicValue> {\n        match *self {\n            Self::I8 => Some(i8::MIN.into()),\n            Self::U8 => Some(u8::MIN.into()),\n            Self::I16 => Some(i16::MIN.into()),\n            Self::U16 => Some(u16::MIN.into()),\n            Self::I32 => Some(i32::MIN.into()),\n            Self::U32 => Some(u32::MIN.into()),\n            Self::I64 => Some(i64::MIN.into()),\n            Self::U64 => Some(u64::MIN.into()),\n            Self::I128 => Some(i128::MIN.into()),\n            Self::U128 => Some(u128::MIN.into()),\n            Self::I256 => Some(i256::MIN.into()),\n            Self::U256 => Some(u256::MIN.into()),\n            Self::F32 => Some(f32::MIN.into()),\n            Self::F64 => Some(f64::MIN.into()),\n            _ => None,\n        }\n    }\n\n    #[inline]\n    /// Given an AlgebraicType, returns the max value for that type.\n    pub fn max_value(&self) -> Option<AlgebraicValue> {\n        match *self {\n            Self::I8 => Some(i8::MAX.into()),\n            Self::U8 => Some(u8::MAX.into()),\n            Self::I16 => Some(i16::MAX.into()),\n            Self::U16 => Some(u16::MAX.into()),\n            Self::I32 => Some(i32::MAX.into()),\n            Self::U32 => Some(u32::MAX.into()),\n            Self::I64 => Some(i64::MAX.into()),\n            Self::U64 => Some(u64::MAX.into()),\n            Self::I128 => Some(i128::MAX.into()),\n            Self::U128 => Some(u128::MAX.into()),\n            Self::I256 => Some(i256::MAX.into()),\n            Self::U256 => Some(u256::MAX.into()),\n            Self::F32 => Some(f32::MAX.into()),\n            Self::F64 => Some(f64::MAX.into()),\n            _ => None,\n        }\n    }\n\n    /// Check if the type is one of a small number of special, known types\n    /// with specific layouts.\n    /// See also [`ProductType::is_special`] and [`SumType::is_special`].\n    pub fn is_special(&self) -> bool {\n        match self {\n            AlgebraicType::Product(product) => product.is_special(),\n            AlgebraicType::Sum(sum) => sum.is_special(),\n            _ => false,\n        }\n    }\n\n    /// Validates that the type can be used to generate a type definition\n    /// in a `SpacetimeDB` client module.\n    ///\n    /// Such a type must be a non-special sum or product type.\n    /// All of the elements of the type must satisfy [`AlgebraicType::is_valid_for_client_type_use`].\n    ///\n    /// This method does not actually follow `Ref`s to check the types they point to,\n    /// it only checks the structure of this type.\n    pub fn is_valid_for_client_type_definition(&self) -> bool {\n        // Special types should not be used to generate type definitions.\n        if self.is_special() {\n            return false;\n        }\n        match self {\n            AlgebraicType::Sum(sum) => sum\n                .variants\n                .iter()\n                .all(|variant| variant.algebraic_type.is_valid_for_client_type_use()),\n            AlgebraicType::Product(product) => product\n                .elements\n                .iter()\n                .all(|elem| elem.algebraic_type.is_valid_for_client_type_use()),\n            _ => false,\n        }\n    }\n\n    /// Validates that the type can be used to generate a *use* of a type in a `SpacetimeDB` client module.\n    /// (As opposed to a *definition* of a type.)\n    ///\n    /// This means that the type is either:\n    /// - a reference\n    /// - a special, known type\n    /// - a non-compound type like `U8`, `I32`, `F64`, etc.\n    /// - or a map, array, option, or result built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`]\n    ///\n    /// This method does not actually follow `Ref`s to check the types they point to,\n    /// it only checks the structure of the type.\n    pub fn is_valid_for_client_type_use(&self) -> bool {\n        match self {\n            AlgebraicType::Sum(sum) => {\n                if let Some(wrapped) = sum.as_option() {\n                    wrapped.is_valid_for_client_type_use()\n                } else if let Some((ok_ty, err_ty)) = sum.as_result() {\n                    ok_ty.is_valid_for_client_type_use() && err_ty.is_valid_for_client_type_use()\n                } else {\n                    sum.is_special() || sum.is_empty()\n                }\n            }\n            AlgebraicType::Product(product) => product.is_special() || product.is_unit(),\n            AlgebraicType::Array(array) => array.elem_ty.is_valid_for_client_type_use(),\n            AlgebraicType::Ref(_) => true,\n            _ => true,\n        }\n    }\n\n    pub fn type_check(&self, value: &AlgebraicValue, typespace: &Typespace) -> bool {\n        match (self, value) {\n            (_, AlgebraicValue::Min | AlgebraicValue::Max) => true,\n            (AlgebraicType::Ref(r), _) => {\n                if let Some(resolved_ty) = typespace.get(*r) {\n                    resolved_ty.type_check(value, typespace)\n                } else {\n                    false\n                }\n            }\n            (AlgebraicType::Sum(sum_ty), AlgebraicValue::Sum(sv)) => sum_ty.type_check(sv, typespace),\n            (AlgebraicType::Product(product_ty), AlgebraicValue::Product(pv)) => product_ty.type_check(pv, typespace),\n            (AlgebraicType::Array(array_ty), AlgebraicValue::Array(arr)) => array_ty.type_check(arr, typespace),\n\n            (AlgebraicType::String, AlgebraicValue::String(_))\n            | (AlgebraicType::Bool, AlgebraicValue::Bool(_))\n            | (AlgebraicType::I8, AlgebraicValue::I8(_))\n            | (AlgebraicType::U8, AlgebraicValue::U8(_))\n            | (AlgebraicType::I16, AlgebraicValue::I16(_))\n            | (AlgebraicType::U16, AlgebraicValue::U16(_))\n            | (AlgebraicType::I32, AlgebraicValue::I32(_))\n            | (AlgebraicType::U32, AlgebraicValue::U32(_))\n            | (AlgebraicType::I64, AlgebraicValue::I64(_))\n            | (AlgebraicType::U64, AlgebraicValue::U64(_))\n            | (AlgebraicType::I128, AlgebraicValue::I128(_))\n            | (AlgebraicType::U128, AlgebraicValue::U128(_))\n            | (AlgebraicType::I256, AlgebraicValue::I256(_))\n            | (AlgebraicType::U256, AlgebraicValue::U256(_))\n            | (AlgebraicType::F32, AlgebraicValue::F32(_))\n            | (AlgebraicType::F64, AlgebraicValue::F64(_)) => true,\n            _ => false,\n        }\n    }\n}\n#[cfg(test)]\nmod tests {\n    use super::AlgebraicType;\n    use crate::meta_type::MetaType;\n    use crate::satn::Satn;\n    use crate::{\n        algebraic_type::fmt::fmt_algebraic_type, algebraic_type::map_notation::fmt_algebraic_type as fmt_map,\n        algebraic_type_ref::AlgebraicTypeRef, typespace::Typespace,\n    };\n    use crate::{product, AlgebraicValue, ValueWithType, WithTypespace};\n\n    #[test]\n    fn never() {\n        assert_eq!(\"(|)\", fmt_algebraic_type(&AlgebraicType::never()).to_string());\n    }\n\n    #[test]\n    fn never_map() {\n        assert_eq!(\"{ ty_: Sum }\", fmt_map(&AlgebraicType::never()).to_string());\n    }\n\n    #[test]\n    fn unit() {\n        assert_eq!(\"()\", fmt_algebraic_type(&AlgebraicType::unit()).to_string());\n    }\n\n    #[test]\n    fn unit_map() {\n        assert_eq!(\"{ ty_: Product }\", fmt_map(&AlgebraicType::unit()).to_string());\n    }\n\n    #[test]\n    fn primitive() {\n        assert_eq!(\"U8\", fmt_algebraic_type(&AlgebraicType::U8).to_string());\n    }\n\n    #[test]\n    fn primitive_map() {\n        assert_eq!(\"{ ty_: U8 }\", fmt_map(&AlgebraicType::U8).to_string());\n    }\n\n    #[test]\n    fn option() {\n        let option = AlgebraicType::option(AlgebraicType::never());\n        assert_eq!(\"(some: (|) | none: ())\", fmt_algebraic_type(&option).to_string());\n    }\n\n    #[test]\n    fn option_map() {\n        let option = AlgebraicType::option(AlgebraicType::never());\n        assert_eq!(\n            \"{ ty_: Sum, some: { ty_: Sum }, none: { ty_: Product } }\",\n            fmt_map(&option).to_string()\n        );\n    }\n\n    #[test]\n    fn result() {\n        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);\n        assert_eq!(\"(ok: U8 | err: String)\", fmt_algebraic_type(&result).to_string());\n    }\n\n    #[test]\n    fn result_map() {\n        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);\n        assert_eq!(\n            \"{ ty_: Sum, ok: { ty_: U8 }, err: { ty_: String } }\",\n            fmt_map(&result).to_string()\n        );\n    }\n\n    #[test]\n    fn algebraic_type() {\n        let algebraic_type = AlgebraicType::meta_type();\n        assert_eq!(\n            \"(\\\n                ref: U32 \\\n                | sum: (variants: Array<(\\\n                    name: (some: String | none: ()), \\\n                    algebraic_type: &0\\\n                )>) \\\n                | product: (elements: Array<(\\\n                    name: (some: String | none: ()), \\\n                    algebraic_type: &0\\\n                )>) \\\n                | array: &0 \\\n                | string: () \\\n                | bool: () \\\n                | i8: () | u8: () \\\n                | i16: () | u16: () \\\n                | i32: () | u32: () \\\n                | i64: () | u64: () \\\n                | i128: () | u128: () \\\n                | i256: () | u256: () \\\n                | f32: () | f64: ()\\\n            )\",\n            fmt_algebraic_type(&algebraic_type).to_string()\n        );\n    }\n\n    #[test]\n    fn algebraic_type_map() {\n        let algebraic_type = AlgebraicType::meta_type();\n        assert_eq!(\n            \"{ \\\n                ty_: Sum, \\\n                ref: { ty_: U32 }, \\\n                sum: { \\\n                    ty_: Product, \\\n                    variants: { \\\n                        ty_: Array, \\\n                        0: { \\\n                            ty_: Product, \\\n                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \\\n                            algebraic_type: { ty_: Ref, 0: 0 } \\\n                        } \\\n                    } \\\n                }, \\\n                product: { \\\n                    ty_: Product, \\\n                    elements: { \\\n                        ty_: Array, \\\n                        0: { \\\n                            ty_: Product, \\\n                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \\\n                            algebraic_type: { ty_: Ref, 0: 0 } \\\n                        } \\\n                    } \\\n                }, \\\n                array: { ty_: Ref, 0: 0 }, \\\n                string: { ty_: Product }, \\\n                bool: { ty_: Product }, \\\n                i8: { ty_: Product }, u8: { ty_: Product }, \\\n                i16: { ty_: Product }, u16: { ty_: Product }, \\\n                i32: { ty_: Product }, u32: { ty_: Product }, \\\n                i64: { ty_: Product }, u64: { ty_: Product }, \\\n                i128: { ty_: Product }, u128: { ty_: Product }, \\\n                i256: { ty_: Product }, u256: { ty_: Product }, \\\n                f32: { ty_: Product }, f64: { ty_: Product } \\\n            }\",\n            fmt_map(&algebraic_type).to_string()\n        );\n    }\n\n    #[test]\n    fn nested_products_and_sums() {\n        let builtin = AlgebraicType::U8;\n        let product = AlgebraicType::product([(\"thing\", AlgebraicType::U8)]);\n        let sum = AlgebraicType::sum([builtin.clone(), builtin.clone(), product]);\n        let next = AlgebraicType::product([\n            (Some(\"test\"), builtin.clone()),\n            (None, sum),\n            (None, builtin),\n            (Some(\"never\"), AlgebraicType::never()),\n        ]);\n        assert_eq!(\n            \"(test: U8, 1: (U8 | U8 | (thing: U8)), 2: U8, never: (|))\",\n            fmt_algebraic_type(&next).to_string()\n        );\n    }\n\n    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {\n        WithTypespace::new(ts, ty).with_value(val)\n    }\n\n    #[test]\n    fn option_as_value() {\n        let option = AlgebraicType::option(AlgebraicType::never());\n        let algebraic_type = AlgebraicType::meta_type();\n        let typespace = Typespace::new(vec![algebraic_type]);\n        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));\n        assert_eq!(\n            r#\"(sum = (variants = [(name = (some = \"some\"), algebraic_type = (sum = (variants = []))), (name = (some = \"none\"), algebraic_type = (product = (elements = [])))]))\"#,\n            in_space(&typespace, &at_ref, &option.as_value()).to_satn()\n        );\n    }\n\n    #[test]\n    fn result_as_value() {\n        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);\n        let algebraic_type = AlgebraicType::meta_type();\n        let typespace = Typespace::new(vec![algebraic_type]);\n        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));\n        assert_eq!(\n            r#\"(sum = (variants = [(name = (some = \"ok\"), algebraic_type = (u8 = ())), (name = (some = \"err\"), algebraic_type = (string = ()))]))\"#,\n            in_space(&typespace, &at_ref, &result.as_value()).to_satn()\n        );\n    }\n\n    #[test]\n    fn algebraic_type_as_value() {\n        let algebraic_type = AlgebraicType::meta_type();\n        let typespace = Typespace::new(vec![algebraic_type.clone()]);\n        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));\n\n        let ref0 = \"algebraic_type = (ref = 0)\";\n        let unit = \"algebraic_type = (product = (elements = []))\";\n        let aggr_elems_ty = format!(\n            \"algebraic_type = (array = (product = (elements = [\\\n                (\\\n                    name = (some = \\\"name\\\"), \\\n                    algebraic_type = (sum = (variants = [\\\n                        (name = (some = \\\"some\\\"), algebraic_type = (string = ())), \\\n                        (name = (some = \\\"none\\\"), {unit})\\\n                    ]))\\\n                ), \\\n                (name = (some = \\\"algebraic_type\\\"), {ref0})\\\n            ])))\"\n        );\n\n        assert_eq!(\n            format!(\n                \"(\\\n                sum = (\\\n                    variants = [\\\n                        (name = (some = \\\"ref\\\"), algebraic_type = (u32 = ())), \\\n                        (\\\n                            name = (some = \\\"sum\\\"), \\\n                            algebraic_type = (product = (elements = [\\\n                                (name = (some = \\\"variants\\\"), {aggr_elems_ty})\\\n                            ]))\\\n                        ), \\\n                        (\\\n                            name = (some = \\\"product\\\"), \\\n                            algebraic_type = (product = (elements = [\\\n                                (name = (some = \\\"elements\\\"), {aggr_elems_ty})\\\n                            ]))\\\n                        ), \\\n                        (name = (some = \\\"array\\\"), {ref0}), \\\n                        (name = (some = \\\"string\\\"), {unit}), \\\n                        (name = (some = \\\"bool\\\"), {unit}), \\\n                        (name = (some = \\\"i8\\\"), {unit}), \\\n                        (name = (some = \\\"u8\\\"), {unit}), \\\n                        (name = (some = \\\"i16\\\"), {unit}), \\\n                        (name = (some = \\\"u16\\\"), {unit}), \\\n                        (name = (some = \\\"i32\\\"), {unit}), \\\n                        (name = (some = \\\"u32\\\"), {unit}), \\\n                        (name = (some = \\\"i64\\\"), {unit}), \\\n                        (name = (some = \\\"u64\\\"), {unit}), \\\n                        (name = (some = \\\"i128\\\"), {unit}), \\\n                        (name = (some = \\\"u128\\\"), {unit}), \\\n                        (name = (some = \\\"i256\\\"), {unit}), \\\n                        (name = (some = \\\"u256\\\"), {unit}), \\\n                        (name = (some = \\\"f32\\\"), {unit}), \\\n                        (name = (some = \\\"f64\\\"), {unit})\\\n                    ]\\\n                )\\\n            )\"\n            ),\n            in_space(&typespace, &at_ref, &algebraic_type.as_value()).to_satn()\n        );\n    }\n\n    #[test]\n    fn option_from_value() {\n        let option = AlgebraicType::option(AlgebraicType::never());\n        AlgebraicType::from_value(&option.as_value()).expect(\"No errors.\");\n    }\n\n    #[test]\n    fn result_from_value() {\n        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);\n        AlgebraicType::from_value(&result.as_value()).expect(\"No errors.\");\n    }\n\n    #[test]\n    fn builtin_from_value() {\n        let u8 = AlgebraicType::U8;\n        AlgebraicType::from_value(&u8.as_value()).expect(\"No errors.\");\n    }\n\n    #[test]\n    fn algebraic_type_from_value() {\n        let algebraic_type = AlgebraicType::meta_type();\n        AlgebraicType::from_value(&algebraic_type.as_value()).expect(\"No errors.\");\n    }\n\n    #[test]\n    fn special_types_are_special() {\n        assert!(AlgebraicType::identity().is_identity());\n        assert!(AlgebraicType::identity().is_special());\n        assert!(AlgebraicType::connection_id().is_connection_id());\n        assert!(AlgebraicType::connection_id().is_special());\n        assert!(AlgebraicType::timestamp().is_timestamp());\n        assert!(AlgebraicType::timestamp().is_special());\n        assert!(AlgebraicType::time_duration().is_special());\n        assert!(AlgebraicType::time_duration().is_time_duration());\n        assert!(AlgebraicType::uuid().is_uuid());\n        assert!(AlgebraicType::uuid().is_special());\n    }\n\n    #[test]\n    fn type_check() {\n        let av = AlgebraicValue::sum(1, AlgebraicValue::from(product![0u16, 1u32]));\n        let at = AlgebraicType::sum([\n            (\"a\", AlgebraicType::U8),\n            (\"b\", AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U32])),\n        ]);\n\n        at.type_check(&av, Typespace::EMPTY);\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_type_ref.rs",
    "content": "use crate::impl_st;\nuse crate::{algebraic_type::AlgebraicType, impl_deserialize, impl_serialize, meta_type::MetaType};\nuse std::fmt::Display;\n\n/// A reference to an [`AlgebraicType`] within a `Typespace`.\n///\n/// Using this in a different `Typespace` than its maker\n/// will most likely result in a panic.\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct AlgebraicTypeRef(\n    /// The index into the specific `Typespace`'s list of types.\n    pub u32,\n);\n\nimpl AlgebraicTypeRef {\n    /// Returns the index into the specific `Typespace`'s list of types.\n    pub const fn idx(self) -> usize {\n        self.0 as usize\n    }\n}\n\nimpl_serialize!([] AlgebraicTypeRef, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] AlgebraicTypeRef, de => u32::deserialize(de).map(Self));\nimpl_st!([] AlgebraicTypeRef, AlgebraicType::U32);\n\nimpl Display for AlgebraicTypeRef {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        // For example: `&42`.\n        write!(f, \"&{}\", self.0)\n    }\n}\n\nimpl MetaType for AlgebraicTypeRef {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::U32\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_value/de.rs",
    "content": "use crate::array_value::{ArrayValueIntoIter, ArrayValueIterCloned};\nuse crate::{de, AlgebraicValue, SumValue};\nuse crate::{i256, u256};\nuse derive_more::From;\n\n/// An implementation of [`Deserializer`](de::Deserializer)\n/// where the input of deserialization is an `AlgebraicValue`.\n#[repr(transparent)]\n#[derive(From)]\npub struct ValueDeserializer {\n    /// The value to deserialize to some `T`.\n    val: AlgebraicValue,\n}\n\nimpl ValueDeserializer {\n    /// Returns a `ValueDeserializer` with `val` as the input for deserialization.\n    pub fn new(val: AlgebraicValue) -> Self {\n        Self { val }\n    }\n\n    /// Converts `&AlgebraicValue` to `&ValueDeserializer`.\n    pub fn from_ref(val: &AlgebraicValue) -> &Self {\n        // SAFETY: The conversion is OK due to `repr(transparent)`.\n        unsafe { &*(val as *const AlgebraicValue as *const ValueDeserializer) }\n    }\n}\n\n/// Errors that can occur when deserializing the `AlgebraicValue`.\n#[derive(Debug)]\npub enum ValueDeserializeError {\n    /// The input type does not match the target type.\n    MismatchedType,\n    /// An unstructured error message.\n    Custom(String),\n}\n\nimpl de::Error for ValueDeserializeError {\n    fn custom(msg: impl std::fmt::Display) -> Self {\n        Self::Custom(msg.to_string())\n    }\n}\n\n/// Turns any error into `ValueDeserializeError::MismatchedType`.\nfn map_err<T, E>(res: Result<T, E>) -> Result<T, ValueDeserializeError> {\n    res.map_err(|_| ValueDeserializeError::MismatchedType)\n}\n\n/// Turns any option into `ValueDeserializeError::MismatchedType`.\nfn ok_or<T>(res: Option<T>) -> Result<T, ValueDeserializeError> {\n    res.ok_or(ValueDeserializeError::MismatchedType)\n}\n\nimpl<'de> de::Deserializer<'de> for ValueDeserializer {\n    type Error = ValueDeserializeError;\n\n    fn deserialize_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let vals = map_err(self.val.into_product())?.into_iter();\n        visitor.visit_seq_product(ProductAccess { vals })\n    }\n\n    fn validate_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        let vals = map_err(self.val.into_product())?.into_iter();\n        visitor.validate_seq_product(ProductAccess { vals })\n    }\n\n    fn deserialize_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let sum = map_err(self.val.into_sum())?;\n        visitor.visit_sum(SumAccess { sum })\n    }\n\n    fn validate_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        let sum = map_err(self.val.into_sum())?;\n        visitor.validate_sum(SumAccess { sum })\n    }\n\n    fn deserialize_bool(self) -> Result<bool, Self::Error> {\n        map_err(self.val.into_bool())\n    }\n\n    fn deserialize_u8(self) -> Result<u8, Self::Error> {\n        map_err(self.val.into_u8())\n    }\n\n    fn deserialize_u16(self) -> Result<u16, Self::Error> {\n        map_err(self.val.into_u16())\n    }\n\n    fn deserialize_u32(self) -> Result<u32, Self::Error> {\n        map_err(self.val.into_u32())\n    }\n\n    fn deserialize_u64(self) -> Result<u64, Self::Error> {\n        map_err(self.val.into_u64())\n    }\n\n    fn deserialize_u128(self) -> Result<u128, Self::Error> {\n        map_err(self.val.into_u128().map(|x| x.0))\n    }\n\n    fn deserialize_u256(self) -> Result<u256, Self::Error> {\n        map_err(self.val.into_u256().map(|x| *x))\n    }\n\n    fn deserialize_i8(self) -> Result<i8, Self::Error> {\n        map_err(self.val.into_i8())\n    }\n\n    fn deserialize_i16(self) -> Result<i16, Self::Error> {\n        map_err(self.val.into_i16())\n    }\n\n    fn deserialize_i32(self) -> Result<i32, Self::Error> {\n        map_err(self.val.into_i32())\n    }\n\n    fn deserialize_i64(self) -> Result<i64, Self::Error> {\n        map_err(self.val.into_i64())\n    }\n\n    fn deserialize_i128(self) -> Result<i128, Self::Error> {\n        map_err(self.val.into_i128().map(|x| x.0))\n    }\n\n    fn deserialize_i256(self) -> Result<i256, Self::Error> {\n        map_err(self.val.into_i256().map(|x| *x))\n    }\n\n    fn deserialize_f32(self) -> Result<f32, Self::Error> {\n        map_err(self.val.into_f32().map(f32::from))\n    }\n\n    fn deserialize_f64(self) -> Result<f64, Self::Error> {\n        map_err(self.val.into_f64().map(f64::from))\n    }\n\n    fn deserialize_str<V: de::SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        visitor.visit_owned(map_err(self.val.into_string().map(Into::into))?)\n    }\n\n    fn deserialize_bytes<V: de::SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        visitor.visit_owned(map_err(self.val.into_bytes().map(Vec::from))?)\n    }\n\n    fn deserialize_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        let iter = map_err(self.val.into_array())?.into_iter();\n        visitor.visit(ArrayAccess { iter, seed })\n    }\n\n    fn validate_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<(), Self::Error> {\n        let iter = map_err(self.val.into_array())?.into_iter();\n        visitor.validate(ArrayAccess { iter, seed })\n    }\n}\n\n/// Defines deserialization for [`ValueDeserializer`] where product elements are in the input.\nstruct ProductAccess {\n    /// The element values of the product as an iterator of owned values.\n    vals: std::vec::IntoIter<AlgebraicValue>,\n}\n\nimpl<'de> de::SeqProductAccess<'de> for ProductAccess {\n    type Error = ValueDeserializeError;\n\n    fn next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Output>, Self::Error> {\n        self.vals\n            .next()\n            .map(|val| seed.deserialize(ValueDeserializer { val }))\n            .transpose()\n    }\n\n    fn validate_next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<()>, Self::Error> {\n        self.vals\n            .next()\n            .map(|val| seed.validate(ValueDeserializer { val }))\n            .transpose()\n    }\n}\n\n/// Defines deserialization for [`ValueDeserializer`] where a sum value is in the input.\n#[repr(transparent)]\nstruct SumAccess {\n    /// The input sum value to deserialize.\n    sum: SumValue,\n}\n\nimpl SumAccess {\n    /// Converts `&SumValue` to `&SumAccess`.\n    fn from_ref(sum: &SumValue) -> &Self {\n        // SAFETY: `repr(transparent)` allows this.\n        unsafe { &*(sum as *const SumValue as *const SumAccess) }\n    }\n}\n\nimpl<'de> de::SumAccess<'de> for SumAccess {\n    type Error = ValueDeserializeError;\n\n    type Variant = ValueDeserializer;\n\n    fn variant<V: de::VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        let tag = visitor.visit_tag(self.sum.tag)?;\n        let val = *self.sum.value;\n        Ok((tag, ValueDeserializer { val }))\n    }\n}\n\nimpl<'de> de::VariantAccess<'de> for ValueDeserializer {\n    type Error = ValueDeserializeError;\n\n    fn deserialize_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(self)\n    }\n\n    fn validate_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<(), Self::Error> {\n        seed.validate(self)\n    }\n}\n\n/// Defines deserialization for [`ValueDeserializer`] where an array value is in the input.\nstruct ArrayAccess<T> {\n    /// The elements of the array as an iterator of owned elements.\n    iter: ArrayValueIntoIter,\n    /// A seed value provided by the caller of\n    /// [`deserialize_array_seed`](de::Deserializer::deserialize_array_seed).\n    seed: T,\n}\n\nimpl<'de, T: de::DeserializeSeed<'de> + Clone> de::ArrayAccess<'de> for ArrayAccess<T> {\n    type Element = T::Output;\n    type Error = ValueDeserializeError;\n\n    fn next_element(&mut self) -> Result<Option<Self::Element>, Self::Error> {\n        self.iter\n            .next()\n            .map(|val| self.seed.clone().deserialize(ValueDeserializer { val }))\n            .transpose()\n    }\n\n    fn validate_next_element(&mut self) -> Result<Option<()>, Self::Error> {\n        self.iter\n            .next()\n            .map(|val| self.seed.clone().validate(ValueDeserializer { val }))\n            .transpose()\n    }\n}\n\nimpl<'de> de::Deserializer<'de> for &'de ValueDeserializer {\n    type Error = ValueDeserializeError;\n\n    fn deserialize_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let vals = ok_or(self.val.as_product())?.elements.iter();\n        visitor.visit_seq_product(RefProductAccess { vals })\n    }\n\n    fn validate_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        let vals = ok_or(self.val.as_product())?.elements.iter();\n        visitor.validate_seq_product(RefProductAccess { vals })\n    }\n\n    fn deserialize_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let sum = ok_or(self.val.as_sum())?;\n        visitor.visit_sum(SumAccess::from_ref(sum))\n    }\n\n    fn validate_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        let sum = ok_or(self.val.as_sum())?;\n        visitor.validate_sum(SumAccess::from_ref(sum))\n    }\n\n    fn deserialize_bool(self) -> Result<bool, Self::Error> {\n        ok_or(self.val.as_bool().copied())\n    }\n    fn deserialize_u8(self) -> Result<u8, Self::Error> {\n        ok_or(self.val.as_u8().copied())\n    }\n    fn deserialize_u16(self) -> Result<u16, Self::Error> {\n        ok_or(self.val.as_u16().copied())\n    }\n    fn deserialize_u32(self) -> Result<u32, Self::Error> {\n        ok_or(self.val.as_u32().copied())\n    }\n    fn deserialize_u64(self) -> Result<u64, Self::Error> {\n        ok_or(self.val.as_u64().copied())\n    }\n    fn deserialize_u128(self) -> Result<u128, Self::Error> {\n        ok_or(self.val.as_u128().copied().map(|x| x.0))\n    }\n    fn deserialize_u256(self) -> Result<u256, Self::Error> {\n        ok_or(self.val.as_u256().map(|x| **x))\n    }\n    fn deserialize_i8(self) -> Result<i8, Self::Error> {\n        ok_or(self.val.as_i8().copied())\n    }\n    fn deserialize_i16(self) -> Result<i16, Self::Error> {\n        ok_or(self.val.as_i16().copied())\n    }\n    fn deserialize_i32(self) -> Result<i32, Self::Error> {\n        ok_or(self.val.as_i32().copied())\n    }\n    fn deserialize_i64(self) -> Result<i64, Self::Error> {\n        ok_or(self.val.as_i64().copied())\n    }\n    fn deserialize_i128(self) -> Result<i128, Self::Error> {\n        ok_or(self.val.as_i128().copied().map(|x| x.0))\n    }\n    fn deserialize_i256(self) -> Result<i256, Self::Error> {\n        ok_or(self.val.as_i256().map(|x| **x))\n    }\n    fn deserialize_f32(self) -> Result<f32, Self::Error> {\n        ok_or(self.val.as_f32().copied().map(f32::from))\n    }\n    fn deserialize_f64(self) -> Result<f64, Self::Error> {\n        ok_or(self.val.as_f64().copied().map(f64::from))\n    }\n\n    fn deserialize_str<V: de::SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        visitor.visit_borrowed(ok_or(self.val.as_string())?)\n    }\n\n    fn deserialize_bytes<V: de::SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        visitor.visit_borrowed(ok_or(self.val.as_bytes())?)\n    }\n\n    fn deserialize_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        let iter = ok_or(self.val.as_array())?.iter_cloned();\n        visitor.visit(RefArrayAccess { iter, seed })\n    }\n\n    fn validate_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<(), Self::Error> {\n        let iter = ok_or(self.val.as_array())?.iter_cloned();\n        visitor.validate(RefArrayAccess { iter, seed })\n    }\n}\n\n/// Defines deserialization for [`&'de ValueDeserializer`] where product elements are in the input.\nstruct RefProductAccess<'a> {\n    /// The element values of the product as an iterator of borrowed values.\n    vals: std::slice::Iter<'a, AlgebraicValue>,\n}\n\nimpl<'de> de::SeqProductAccess<'de> for RefProductAccess<'de> {\n    type Error = ValueDeserializeError;\n\n    fn next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Output>, Self::Error> {\n        self.vals\n            .next()\n            .map(|val| seed.deserialize(ValueDeserializer::from_ref(val)))\n            .transpose()\n    }\n\n    fn validate_next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<()>, Self::Error> {\n        self.vals\n            .next()\n            .map(|val| seed.validate(ValueDeserializer::from_ref(val)))\n            .transpose()\n    }\n}\n\nimpl<'de> de::SumAccess<'de> for &'de SumAccess {\n    type Error = ValueDeserializeError;\n\n    type Variant = &'de ValueDeserializer;\n\n    fn variant<V: de::VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        let tag = visitor.visit_tag(self.sum.tag)?;\n        Ok((tag, ValueDeserializer::from_ref(&self.sum.value)))\n    }\n}\n\nimpl<'de> de::VariantAccess<'de> for &'de ValueDeserializer {\n    type Error = ValueDeserializeError;\n\n    fn deserialize_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(self)\n    }\n\n    fn validate_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<(), Self::Error> {\n        seed.validate(self)\n    }\n}\n\n/// Defines deserialization for [`&'de ValueDeserializer`] where an array value is in the input.\nstruct RefArrayAccess<'a, T> {\n    // TODO: idk this kinda sucks\n    /// The elements of the array as an iterator of cloned elements.\n    iter: ArrayValueIterCloned<'a>,\n    /// A seed value provided by the caller of\n    /// [`deserialize_array_seed`](de::Deserializer::deserialize_array_seed).\n    seed: T,\n}\n\nimpl<'de, T: de::DeserializeSeed<'de> + Clone> de::ArrayAccess<'de> for RefArrayAccess<'de, T> {\n    type Element = T::Output;\n    type Error = ValueDeserializeError;\n\n    fn next_element(&mut self) -> Result<Option<Self::Element>, Self::Error> {\n        self.iter\n            .next()\n            .map(|val| self.seed.clone().deserialize(ValueDeserializer { val }))\n            .transpose()\n    }\n\n    fn validate_next_element(&mut self) -> Result<Option<()>, Self::Error> {\n        self.iter\n            .next()\n            .map(|val| self.seed.clone().validate(ValueDeserializer { val }))\n            .transpose()\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_value/ser.rs",
    "content": "use crate::bsatn::decode;\nuse crate::de::DeserializeSeed;\nuse crate::ser::{self, ForwardNamedToSeqProduct, Serialize};\nuse crate::{i256, u256, WithTypespace};\nuse crate::{AlgebraicValue, ArrayValue, F32, F64};\nuse core::convert::Infallible;\nuse core::mem::MaybeUninit;\nuse core::ptr;\nuse second_stack::uninit_slice;\nuse std::alloc::{self, Layout};\n\n/// Serialize `x` as an [`AlgebraicValue`].\npub fn value_serialize(x: &(impl Serialize + ?Sized)) -> AlgebraicValue {\n    x.serialize(ValueSerializer).unwrap_or_else(|e| match e {})\n}\n\n/// An implementation of [`Serializer`](ser::Serializer)\n/// where the output of serialization is an `AlgebraicValue`.\npub struct ValueSerializer;\n\nmacro_rules! method {\n    ($name:ident -> $t:ty) => {\n        fn $name(self, v: $t) -> Result<Self::Ok, Self::Error> {\n            Ok(v.into())\n        }\n    };\n}\n\nimpl ser::Serializer for ValueSerializer {\n    type Ok = AlgebraicValue;\n    type Error = Infallible;\n\n    type SerializeArray = SerializeArrayValue;\n    type SerializeSeqProduct = SerializeProductValue;\n    type SerializeNamedProduct = ForwardNamedToSeqProduct<SerializeProductValue>;\n\n    method!(serialize_bool -> bool);\n    method!(serialize_u8 -> u8);\n    method!(serialize_u16 -> u16);\n    method!(serialize_u32 -> u32);\n    method!(serialize_u64 -> u64);\n    method!(serialize_u128 -> u128);\n    method!(serialize_u256 -> u256);\n    method!(serialize_i8 -> i8);\n    method!(serialize_i16 -> i16);\n    method!(serialize_i32 -> i32);\n    method!(serialize_i64 -> i64);\n    method!(serialize_i128 -> i128);\n    method!(serialize_i256 -> i256);\n    method!(serialize_f32 -> f32);\n    method!(serialize_f64 -> f64);\n\n    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {\n        Ok(AlgebraicValue::String(v.into()))\n    }\n    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {\n        Ok(AlgebraicValue::Bytes(v.into()))\n    }\n\n    fn serialize_array(self, len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        Ok(SerializeArrayValue {\n            len: Some(len),\n            array: Default::default(),\n        })\n    }\n\n    fn serialize_seq_product(self, len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        Ok(SerializeProductValue {\n            elements: Vec::with_capacity(len),\n        })\n    }\n\n    fn serialize_named_product(self, len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        ForwardNamedToSeqProduct::forward(self, len)\n    }\n\n    fn serialize_variant<T: ser::Serialize + ?Sized>(\n        self,\n        tag: u8,\n        _name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        value.serialize(self).map(|v| AlgebraicValue::sum(tag, v))\n    }\n\n    unsafe fn serialize_bsatn<Ty>(self, ty: &Ty, mut bsatn: &[u8]) -> Result<Self::Ok, Self::Error>\n    where\n        for<'a, 'de> WithTypespace<'a, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        let res = decode(ty, &mut bsatn);\n        // SAFETY: Caller promised that `res.is_ok()`.\n        let val = unsafe { res.unwrap_unchecked() };\n        Ok(val.into())\n    }\n\n    unsafe fn serialize_bsatn_in_chunks<'a, Ty, I: Iterator<Item = &'a [u8]>>(\n        self,\n        ty: &Ty,\n        total_bsatn_len: usize,\n        chunks: I,\n    ) -> Result<Self::Ok, Self::Error>\n    where\n        for<'b, 'de> WithTypespace<'b, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        // SAFETY: Caller promised `total_bsatn_len == chunks.map(|c| c.len()).sum() <= isize::MAX`.\n        unsafe {\n            concat_byte_chunks_buf(total_bsatn_len, chunks, |bsatn| {\n                // SAFETY: Caller promised `AlgebraicValue::decode(ty, &mut bytes).is_ok()`.\n                ValueSerializer.serialize_bsatn(ty, bsatn)\n            })\n        }\n    }\n\n    unsafe fn serialize_str_in_chunks<'a, I: Iterator<Item = &'a [u8]>>(\n        self,\n        total_len: usize,\n        string: I,\n    ) -> Result<Self::Ok, Self::Error> {\n        // SAFETY: Caller promised `total_len == string.map(|c| c.len()).sum() <= isize::MAx`.\n        let bytes = unsafe { concat_byte_chunks(total_len, string) };\n\n        // SAFETY: Caller promised `bytes` is UTF-8.\n        let string = unsafe { String::from_utf8_unchecked(bytes) };\n        Ok(string.into_boxed_str().into())\n    }\n}\n\n/// Returns the concatenation of `chunks` that must be of `total_len` as a `Vec<u8>`.\n///\n/// # Safety\n///\n/// - `total_len == chunks.map(|c| c.len()).sum() <= isize::MAX`\nunsafe fn concat_byte_chunks<'a>(total_len: usize, chunks: impl Iterator<Item = &'a [u8]>) -> Vec<u8> {\n    if total_len == 0 {\n        return Vec::new();\n    }\n\n    // Allocate space for `[u8; total_len]` on the heap.\n    let layout = Layout::array::<u8>(total_len);\n    // SAFETY: Caller promised that `total_len <= isize`.\n    let layout = unsafe { layout.unwrap_unchecked() };\n    // SAFETY: We checked above that `layout.size() != 0`.\n    let ptr = unsafe { alloc::alloc(layout) };\n    if ptr.is_null() {\n        alloc::handle_alloc_error(layout);\n    }\n\n    // Copy over each `chunk`.\n    // SAFETY:\n    // 1. `ptr` is valid for writes as we own it\n    //    caller promised that all `chunk`s will fit in `total_len`.\n    // 2. `ptr` points to a new allocation so it cannot overlap with any in `chunks`.\n    unsafe { write_byte_chunks(ptr, chunks) };\n\n    // Convert allocation to a `Vec<u8>`.\n    // SAFETY:\n    // - `ptr` was allocated using global allocator.\n    // - `u8` and `ptr`'s allocation both have alignment of 1.\n    // - `ptr`'s allocation is `total_len <= isize::MAX`.\n    // - `total_len <= total_len` holds.\n    // - `total_len` values were initialized at type `u8`\n    //    as we know `total_len == chunks.map(|c| c.len()).sum()`.\n    unsafe { Vec::from_raw_parts(ptr, total_len, total_len) }\n}\n\n/// Returns the concatenation of `chunks` that must be of `total_len` as a `Vec<u8>`.\n///\n/// # Safety\n///\n/// - `total_len == chunks.map(|c| c.len()).sum() <= isize::MAX`\npub unsafe fn concat_byte_chunks_buf<'a, R>(\n    total_len: usize,\n    chunks: impl Iterator<Item = &'a [u8]>,\n    run: impl FnOnce(&[u8]) -> R,\n) -> R {\n    uninit_slice(total_len, |buf: &mut [MaybeUninit<u8>]| {\n        let dst = buf.as_mut_ptr().cast();\n        debug_assert_eq!(total_len, buf.len());\n        // SAFETY:\n        // 1. `buf.len() == total_len`\n        // 2. `buf` cannot overlap with anything yielded by `var_iter`.\n        unsafe { write_byte_chunks(dst, chunks) }\n        // SAFETY: Every byte of `buf` was initialized in the previous call\n        // as we know that `total_len == var_iter.map(|c| c.len()).sum()`.\n        let bytes = unsafe { slice_assume_init_ref(buf) };\n        run(bytes)\n    })\n}\n\n/// Copies over each `chunk` in `chunks` to `dst`, writing `total_len` bytes to `dst`.\n///\n/// # Safety\n///\n/// Let `total_len == chunks.map(|c| c.len()).sum()`.\n/// 1. `dst` must be valid for writes for `total_len` bytes.\n/// 2. `dst..(dst + total_len)` does not overlap with any slice yielded by `chunks`.\nunsafe fn write_byte_chunks<'a>(mut dst: *mut u8, chunks: impl Iterator<Item = &'a [u8]>) {\n    // Copy over each `chunk`, moving `dst` by `chunk.len()` time.\n    for chunk in chunks {\n        let len = chunk.len();\n        // SAFETY:\n        // - By line above, `chunk` is valid for reads for `len` bytes.\n        // - By (1) `dst` is valid for writes as promised by caller\n        //   and that all `chunk`s will fit in `total_len`.\n        //   This entails that `dst..dst + len` is always in bounds of the allocation.\n        // - `chunk` and `dst` are trivially properly aligned (`align_of::<u8>() == 1`).\n        // - By (2) derived pointers of `dst` cannot overlap with `chunk`.\n        unsafe {\n            ptr::copy_nonoverlapping(chunk.as_ptr(), dst, len);\n        }\n        // SAFETY: Same as (1).\n        dst = unsafe { dst.add(len) };\n    }\n}\n\n/// Convert a `[MaybeUninit<T>]` into a `[T]` by asserting all elements are initialized.\n///\n/// Identical copy of the source of `MaybeUninit::slice_assume_init_ref`, but that's not stabilized.\n/// <https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref>\n///\n/// # Safety\n///\n/// All elements of `slice` must be initialized.\npub const unsafe fn slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {\n    // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that\n    // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`.\n    // The pointer obtained is valid since it refers to memory owned by `slice` which is a\n    // reference and thus guaranteed to be valid for reads.\n    unsafe { &*(slice as *const [MaybeUninit<T>] as *const [T]) }\n}\n\n/// Continuation for serializing an array.\npub struct SerializeArrayValue {\n    /// For efficiency, the first time `serialize_element` is done,\n    /// this is used to allocate with capacity.\n    len: Option<usize>,\n    /// The array being built.\n    array: ArrayValueBuilder,\n}\n\nimpl ser::SerializeArray for SerializeArrayValue {\n    type Ok = AlgebraicValue;\n    type Error = <ValueSerializer as ser::Serializer>::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.array\n            .push(value_serialize(elem), self.len.take())\n            .expect(\"heterogeneous array\");\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(ArrayValue::from(self.array).into())\n    }\n}\n\n/// A builder for [`ArrayValue`]s\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\nenum ArrayValueBuilder {\n    /// An array of [`SumValue`](crate::SumValue)s.\n    Sum(Vec<crate::SumValue>),\n    /// An array of [`ProductValue`](crate::ProductValue)s.\n    Product(Vec<crate::ProductValue>),\n    /// An array of [`bool`]s.\n    Bool(Vec<bool>),\n    /// An array of [`i8`]s.\n    I8(Vec<i8>),\n    /// An array of [`u8`]s.\n    U8(Vec<u8>),\n    /// An array of [`i16`]s.\n    I16(Vec<i16>),\n    /// An array of [`u16`]s.\n    U16(Vec<u16>),\n    /// An array of [`i32`]s.\n    I32(Vec<i32>),\n    /// An array of [`u32`]s.\n    U32(Vec<u32>),\n    /// An array of [`i64`]s.\n    I64(Vec<i64>),\n    /// An array of [`u64`]s.\n    U64(Vec<u64>),\n    /// An array of [`i128`]s.\n    I128(Vec<i128>),\n    /// An array of [`u128`]s.\n    U128(Vec<u128>),\n    /// An array of [`i256`]s.\n    I256(Vec<i256>),\n    /// An array of [`u256`]s.\n    U256(Vec<u256>),\n    /// An array of totally ordered [`F32`]s.\n    F32(Vec<F32>),\n    /// An array of totally ordered [`F64`]s.\n    F64(Vec<F64>),\n    /// An array of UTF-8 strings.\n    String(Vec<Box<str>>),\n    /// An array of arrays.\n    Array(Vec<ArrayValue>),\n}\n\nimpl ArrayValueBuilder {\n    /// Returns the length of the array.\n    fn len(&self) -> usize {\n        match self {\n            Self::Sum(v) => v.len(),\n            Self::Product(v) => v.len(),\n            Self::Bool(v) => v.len(),\n            Self::I8(v) => v.len(),\n            Self::U8(v) => v.len(),\n            Self::I16(v) => v.len(),\n            Self::U16(v) => v.len(),\n            Self::I32(v) => v.len(),\n            Self::U32(v) => v.len(),\n            Self::I64(v) => v.len(),\n            Self::U64(v) => v.len(),\n            Self::I128(v) => v.len(),\n            Self::U128(v) => v.len(),\n            Self::I256(v) => v.len(),\n            Self::U256(v) => v.len(),\n            Self::F32(v) => v.len(),\n            Self::F64(v) => v.len(),\n            Self::String(v) => v.len(),\n            Self::Array(v) => v.len(),\n        }\n    }\n\n    /// Returns whether the array is empty.\n    #[must_use]\n    fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns a singleton array with `val` as its only element.\n    ///\n    /// Optionally allocates the backing `Vec<_>`s with `capacity`.\n    fn from_one_with_capacity(val: AlgebraicValue, capacity: Option<usize>) -> Self {\n        fn vec<T>(e: T, c: Option<usize>) -> Vec<T> {\n            let mut vec = c.map_or(Vec::new(), Vec::with_capacity);\n            vec.push(e);\n            vec\n        }\n\n        match val {\n            AlgebraicValue::Sum(x) => vec(x, capacity).into(),\n            AlgebraicValue::Product(x) => vec(x, capacity).into(),\n            AlgebraicValue::Bool(x) => vec(x, capacity).into(),\n            AlgebraicValue::I8(x) => vec(x, capacity).into(),\n            AlgebraicValue::U8(x) => vec(x, capacity).into(),\n            AlgebraicValue::I16(x) => vec(x, capacity).into(),\n            AlgebraicValue::U16(x) => vec(x, capacity).into(),\n            AlgebraicValue::I32(x) => vec(x, capacity).into(),\n            AlgebraicValue::U32(x) => vec(x, capacity).into(),\n            AlgebraicValue::I64(x) => vec(x, capacity).into(),\n            AlgebraicValue::U64(x) => vec(x, capacity).into(),\n            AlgebraicValue::I128(x) => vec(x.0, capacity).into(),\n            AlgebraicValue::U128(x) => vec(x.0, capacity).into(),\n            AlgebraicValue::I256(x) => vec(*x, capacity).into(),\n            AlgebraicValue::U256(x) => vec(*x, capacity).into(),\n            AlgebraicValue::F32(x) => vec(x, capacity).into(),\n            AlgebraicValue::F64(x) => vec(x, capacity).into(),\n            AlgebraicValue::String(x) => vec(x, capacity).into(),\n            AlgebraicValue::Array(x) => vec(x, capacity).into(),\n            AlgebraicValue::Min | AlgebraicValue::Max => panic!(\"not defined for Min/Max\"),\n        }\n    }\n\n    /// Pushes the value `val` onto the array `self`\n    /// or returns back `Err(val)` if there was a type mismatch\n    /// between the base type of the array and `val`.\n    ///\n    /// Optionally allocates the backing `Vec<_>`s with `capacity`.\n    fn push(&mut self, val: AlgebraicValue, capacity: Option<usize>) -> Result<(), AlgebraicValue> {\n        match (self, val) {\n            (Self::Sum(v), AlgebraicValue::Sum(val)) => v.push(val),\n            (Self::Product(v), AlgebraicValue::Product(val)) => v.push(val),\n            (Self::Bool(v), AlgebraicValue::Bool(val)) => v.push(val),\n            (Self::I8(v), AlgebraicValue::I8(val)) => v.push(val),\n            (Self::U8(v), AlgebraicValue::U8(val)) => v.push(val),\n            (Self::I16(v), AlgebraicValue::I16(val)) => v.push(val),\n            (Self::U16(v), AlgebraicValue::U16(val)) => v.push(val),\n            (Self::I32(v), AlgebraicValue::I32(val)) => v.push(val),\n            (Self::U32(v), AlgebraicValue::U32(val)) => v.push(val),\n            (Self::I64(v), AlgebraicValue::I64(val)) => v.push(val),\n            (Self::U64(v), AlgebraicValue::U64(val)) => v.push(val),\n            (Self::I128(v), AlgebraicValue::I128(val)) => v.push(val.0),\n            (Self::U128(v), AlgebraicValue::U128(val)) => v.push(val.0),\n            (Self::I256(v), AlgebraicValue::I256(val)) => v.push(*val),\n            (Self::U256(v), AlgebraicValue::U256(val)) => v.push(*val),\n            (Self::F32(v), AlgebraicValue::F32(val)) => v.push(val),\n            (Self::F64(v), AlgebraicValue::F64(val)) => v.push(val),\n            (Self::String(v), AlgebraicValue::String(val)) => v.push(val),\n            (Self::Array(v), AlgebraicValue::Array(val)) => v.push(val),\n            (me, val) if me.is_empty() => *me = Self::from_one_with_capacity(val, capacity),\n            (_, val) => return Err(val),\n        }\n        Ok(())\n    }\n}\n\nimpl From<ArrayValueBuilder> for ArrayValue {\n    fn from(value: ArrayValueBuilder) -> Self {\n        use ArrayValueBuilder::*;\n        match value {\n            Sum(v) => Self::Sum(v.into()),\n            Product(v) => Self::Product(v.into()),\n            Bool(v) => Self::Bool(v.into()),\n            I8(v) => Self::I8(v.into()),\n            U8(v) => Self::U8(v.into()),\n            I16(v) => Self::I16(v.into()),\n            U16(v) => Self::U16(v.into()),\n            I32(v) => Self::I32(v.into()),\n            U32(v) => Self::U32(v.into()),\n            I64(v) => Self::I64(v.into()),\n            U64(v) => Self::U64(v.into()),\n            I128(v) => Self::I128(v.into()),\n            U128(v) => Self::U128(v.into()),\n            I256(v) => Self::I256(v.into()),\n            U256(v) => Self::U256(v.into()),\n            F32(v) => Self::F32(v.into()),\n            F64(v) => Self::F64(v.into()),\n            String(v) => Self::String(v.into()),\n            Array(v) => Self::Array(v.into()),\n        }\n    }\n}\n\nimpl Default for ArrayValueBuilder {\n    /// The default `ArrayValue` is an empty array of sum values.\n    fn default() -> Self {\n        Self::from(Vec::<crate::SumValue>::default())\n    }\n}\n\nmacro_rules! impl_from_array {\n    ($el:ty, $var:ident) => {\n        impl From<Vec<$el>> for ArrayValueBuilder {\n            fn from(v: Vec<$el>) -> Self {\n                Self::$var(v)\n            }\n        }\n    };\n}\n\nimpl_from_array!(crate::SumValue, Sum);\nimpl_from_array!(crate::ProductValue, Product);\nimpl_from_array!(bool, Bool);\nimpl_from_array!(i8, I8);\nimpl_from_array!(u8, U8);\nimpl_from_array!(i16, I16);\nimpl_from_array!(u16, U16);\nimpl_from_array!(i32, I32);\nimpl_from_array!(u32, U32);\nimpl_from_array!(i64, I64);\nimpl_from_array!(u64, U64);\nimpl_from_array!(i128, I128);\nimpl_from_array!(u128, U128);\nimpl_from_array!(i256, I256);\nimpl_from_array!(u256, U256);\nimpl_from_array!(F32, F32);\nimpl_from_array!(F64, F64);\nimpl_from_array!(Box<str>, String);\nimpl_from_array!(ArrayValue, Array);\n\n/// Continuation for serializing a map value.\npub struct SerializeProductValue {\n    /// The elements serialized so far.\n    elements: Vec<AlgebraicValue>,\n}\n\nimpl ser::SerializeSeqProduct for SerializeProductValue {\n    type Ok = AlgebraicValue;\n    type Error = <ValueSerializer as ser::Serializer>::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.elements.push(value_serialize(elem));\n        Ok(())\n    }\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(AlgebraicValue::product(self.elements))\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_value.rs",
    "content": "pub mod de;\npub mod ser;\n\nuse crate::{impl_deserialize, AlgebraicType, ArrayValue, Deserialize, ProductValue, SumValue};\nuse core::mem;\nuse core::ops::{Bound, RangeBounds};\nuse derive_more::From;\nuse enum_as_inner::EnumAsInner;\n\npub use ethnum::{i256, u256};\n\n/// Totally ordered [`f32`] allowing all IEEE-754 floating point values.\npub type F32 = decorum::Total<f32>;\n\n/// Totally ordered [`f64`] allowing all IEEE-754 floating point values.\npub type F64 = decorum::Total<f64>;\n\n/// A value in SATS typed at some [`AlgebraicType`].\n///\n/// Values are type erased, so they do not store their type.\n/// This is important mainly for space efficiency,\n/// including network latency and bandwidth.\n///\n/// These are only values and not expressions.\n/// That is, they are canonical and cannot be simplified further by some evaluation.\n/// So forms like `42 + 24` are not represented in an `AlgebraicValue`.\n#[derive(EnumAsInner, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, From)]\npub enum AlgebraicValue {\n    /// The minimum value in the total ordering.\n    /// Cannot be serialized and only exists to facilitate range index scans.\n    /// This variant must always be first.\n    Min,\n\n    /// A structural sum value.\n    ///\n    /// Given a sum type `{ N_0(T_0), N_1(T_1), ..., N_n(T_n) }`\n    /// where `N_i` denotes a variant name\n    /// and where `T_i` denotes the type the variant stores,\n    /// a sum value makes a specific choice as to the variant.\n    /// So for example, we might chose `N_1(T_1)`\n    /// and represent this choice with `(1, v)` where `v` is a value of type `T_1`.\n    Sum(SumValue),\n    /// A structural product value.\n    ///\n    /// Given a product type `{ N_0: T_0, N_1: T_1, ..., N_n: T_n }`\n    /// where `N_i` denotes a field / element name\n    /// and where `T_i` denotes the type the field stores,\n    /// a product value stores a value `v_i` of type `T_i` for each field `N_i`.\n    Product(ProductValue),\n    /// A homogeneous array of `AlgebraicValue`s.\n    /// The array has the type [`AlgebraicType::Array(elem_ty)`][AlgebraicType::Array].\n    ///\n    /// The contained values are stored packed in a representation appropriate for their type.\n    /// See [`ArrayValue`] for details on the representation.\n    Array(ArrayValue),\n    /// A [`bool`] value of type [`AlgebraicType::Bool`].\n    Bool(bool),\n    /// An [`i8`] value of type [`AlgebraicType::I8`].\n    I8(i8),\n    /// A [`u8`] value of type [`AlgebraicType::U8`].\n    U8(u8),\n    /// An [`i16`] value of type [`AlgebraicType::I16`].\n    I16(i16),\n    /// A [`u16`] value of type [`AlgebraicType::U16`].\n    U16(u16),\n    /// An [`i32`] value of type [`AlgebraicType::I32`].\n    I32(i32),\n    /// A [`u32`] value of type [`AlgebraicType::U32`].\n    U32(u32),\n    /// An [`i64`] value of type [`AlgebraicType::I64`].\n    I64(i64),\n    /// A [`u64`] value of type [`AlgebraicType::U64`].\n    U64(u64),\n    /// An [`i128`] value of type [`AlgebraicType::I128`].\n    ///\n    /// We pack these to shrink `AlgebraicValue`.\n    I128(Packed<i128>),\n    /// A [`u128`] value of type [`AlgebraicType::U128`].\n    ///\n    /// We pack these to to shrink `AlgebraicValue`.\n    U128(Packed<u128>),\n    /// An [`i256`] value of type [`AlgebraicType::I256`].\n    ///\n    /// We box these up to shrink `AlgebraicValue`.\n    I256(Box<i256>),\n    /// A [`u256`] value of type [`AlgebraicType::U256`].\n    ///\n    /// We pack these to shrink `AlgebraicValue`.\n    U256(Box<u256>),\n    /// A totally ordered [`F32`] value of type [`AlgebraicType::F32`].\n    ///\n    /// All floating point values defined in IEEE-754 are supported.\n    /// However, unlike the primitive [`f32`], a [total order] is established.\n    ///\n    /// [total order]: https://docs.rs/decorum/0.3.1/decorum/#total-ordering\n    F32(F32),\n    /// A totally ordered [`F64`] value of type [`AlgebraicType::F64`].\n    ///\n    /// All floating point values defined in IEEE-754 are supported.\n    /// However, unlike the primitive [`f64`], a [total order] is established.\n    ///\n    /// [total order]: https://docs.rs/decorum/0.3.1/decorum/#total-ordering\n    F64(F64),\n    /// A UTF-8 string value of type [`AlgebraicType::String`].\n    ///\n    /// Uses Rust's standard representation of strings.\n    String(Box<str>),\n\n    /// The maximum value in the total ordering.\n    /// Cannot be serialized and only exists to facilitate range index scans.\n    /// This variant must always be last.\n    Max,\n}\n\n/// Wraps `T` making the outer type packed with alignment 1.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(Rust, packed)]\npub struct Packed<T>(pub T);\n\nimpl_deserialize!([T: Deserialize<'de>] Packed<T>, de => <_>::deserialize(de).map(Packed));\n\nimpl<T> From<T> for Packed<T> {\n    fn from(value: T) -> Self {\n        Self(value)\n    }\n}\n\n#[allow(non_snake_case)]\nimpl AlgebraicValue {\n    /// Extract the value and replace it with a dummy one that is cheap to make.\n    pub fn take(&mut self) -> Self {\n        mem::replace(self, Self::U8(0))\n    }\n\n    /// Interpret the value as a byte slice or `None` if it isn't a byte slice.\n    #[inline]\n    pub fn as_bytes(&self) -> Option<&[u8]> {\n        match self {\n            Self::Array(ArrayValue::U8(a)) => Some(a),\n            _ => None,\n        }\n    }\n\n    /// The canonical unit value defined as the nullary product value `()`.\n    ///\n    /// The type of `UNIT` is `()`.\n    pub fn unit() -> Self {\n        Self::product([])\n    }\n\n    /// Returns an [`AlgebraicValue`] representing `v: Box<[u8]>`.\n    #[inline]\n    pub const fn Bytes(v: Box<[u8]>) -> Self {\n        Self::Array(ArrayValue::U8(v))\n    }\n\n    /// Converts `self` into a byte string, if applicable.\n    pub fn into_bytes(self) -> Result<Box<[u8]>, Self> {\n        match self {\n            Self::Array(ArrayValue::U8(v)) => Ok(v),\n            _ => Err(self),\n        }\n    }\n\n    /// Converts `self` into an `Option<AlgebraicValue>`, if applicable.\n    pub fn into_option(self) -> Result<Option<Self>, Self> {\n        match self {\n            AlgebraicValue::Sum(sum_value) => match sum_value.tag {\n                0 => Ok(Some(*sum_value.value)),\n                1 => Ok(None),\n                _ => Err(AlgebraicValue::Sum(sum_value)),\n            },\n            _ => Err(self),\n        }\n    }\n\n    /// Returns an [`AlgebraicValue`] for `some: v`.\n    ///\n    /// The `some` variant is assigned the tag `0`.\n    #[inline]\n    pub fn OptionSome(v: Self) -> Self {\n        Self::sum(0, v)\n    }\n\n    /// Returns an [`AlgebraicValue`] for `none`.\n    ///\n    /// The `none` variant is assigned the tag `1`.\n    #[inline]\n    pub fn OptionNone() -> Self {\n        Self::sum(1, Self::unit())\n    }\n\n    /// Converts `self` into `Result<Result<AlgebraicValue, AlgebraicValue>, Self>`, if applicable.\n    pub fn into_result(self) -> Result<Result<Self, Self>, Self> {\n        match self {\n            AlgebraicValue::Sum(sum_value) => match sum_value.tag {\n                0 => Ok(Ok(*sum_value.value)),\n                1 => Ok(Err(*sum_value.value)),\n                _ => Err(AlgebraicValue::Sum(sum_value)),\n            },\n            _ => Err(self),\n        }\n    }\n\n    /// Returns an [`AlgebraicValue`] for ` Ok: v`.\n    ///\n    /// The `Ok` variant is assigned the tag `0`.\n    #[inline]\n    pub fn ResultOk(v: Self) -> Self {\n        Self::sum(0, v)\n    }\n\n    /// Returns an [`AlgebraicValue`] for ` Err: v`.\n    ///\n    /// The `Err` variant is assigned the tag `1`.\n    #[inline]\n    pub fn ResultErr(v: Self) -> Self {\n        Self::sum(1, v)\n    }\n\n    /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and `value`.\n    pub fn sum(tag: u8, value: Self) -> Self {\n        Self::Sum(SumValue::new(tag, value))\n    }\n\n    /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and empty [AlgebraicValue::product], that is\n    /// valid for simple enums without payload.\n    pub fn enum_simple(tag: u8) -> Self {\n        Self::Sum(SumValue::new_simple(tag))\n    }\n\n    /// Returns an [`AlgebraicValue`] representing a product value with the given `elements`.\n    pub fn product(elements: impl Into<ProductValue>) -> Self {\n        Self::Product(elements.into())\n    }\n\n    /// Returns the [`AlgebraicType`] of the product value `x`.\n    pub(crate) fn type_of_product(x: &ProductValue) -> Option<AlgebraicType> {\n        let mut elems = Vec::with_capacity(x.elements.len());\n        for elem in &*x.elements {\n            elems.push(elem.type_of()?.into());\n        }\n        Some(AlgebraicType::product(elems.into_boxed_slice()))\n    }\n\n    /// Infer the [`AlgebraicType`] of an [`AlgebraicValue`].\n    ///\n    /// This function is partial\n    /// as type inference is not possible for `AlgebraicValue` in the case of sums.\n    /// Thus the method only answers for the decidable subset.\n    ///\n    /// # A note on sums\n    ///\n    /// The type of a sum value must be a sum type and *not* a product type.\n    /// Suppose `x.tag` is for the variant `VarName(VarType)`.\n    /// Then `VarType` is *not* the same type as `{ VarName(VarType) | r }`\n    /// where `r` represents a polymorphic variants component.\n    ///\n    /// To assign this a correct type we either have to store the type with the value\n    /// r alternatively, we must have polymorphic variants (see row polymorphism)\n    /// *and* derive the correct variant name.\n    pub fn type_of(&self) -> Option<AlgebraicType> {\n        match self {\n            Self::Sum(_) => None,\n            Self::Product(x) => Self::type_of_product(x),\n            Self::Array(x) => x.type_of().map(Into::into),\n            Self::Bool(_) => Some(AlgebraicType::Bool),\n            Self::I8(_) => Some(AlgebraicType::I8),\n            Self::U8(_) => Some(AlgebraicType::U8),\n            Self::I16(_) => Some(AlgebraicType::I16),\n            Self::U16(_) => Some(AlgebraicType::U16),\n            Self::I32(_) => Some(AlgebraicType::I32),\n            Self::U32(_) => Some(AlgebraicType::U32),\n            Self::I64(_) => Some(AlgebraicType::I64),\n            Self::U64(_) => Some(AlgebraicType::U64),\n            Self::I128(_) => Some(AlgebraicType::I128),\n            Self::U128(_) => Some(AlgebraicType::U128),\n            Self::I256(_) => Some(AlgebraicType::I256),\n            Self::U256(_) => Some(AlgebraicType::U256),\n            Self::F32(_) => Some(AlgebraicType::F32),\n            Self::F64(_) => Some(AlgebraicType::F64),\n            Self::String(_) => Some(AlgebraicType::String),\n            AlgebraicValue::Min | AlgebraicValue::Max => None,\n        }\n    }\n\n    /// Returns whether this value represents a numeric zero.\n    ///\n    /// Can only be true where the type is numeric.\n    pub fn is_numeric_zero(&self) -> bool {\n        match *self {\n            Self::I8(x) => x == 0,\n            Self::U8(x) => x == 0,\n            Self::I16(x) => x == 0,\n            Self::U16(x) => x == 0,\n            Self::I32(x) => x == 0,\n            Self::U32(x) => x == 0,\n            Self::I64(x) => x == 0,\n            Self::U64(x) => x == 0,\n            Self::I128(x) => x.0 == 0,\n            Self::U128(x) => x.0 == 0,\n            Self::I256(ref x) => **x == i256::ZERO,\n            Self::U256(ref x) => **x == u256::ZERO,\n            Self::F32(x) => x == 0.0,\n            Self::F64(x) => x == 0.0,\n            _ => false,\n        }\n    }\n\n    /// Constructs an `AlgebraicValue` from an `i128` according to the given `AlgebraicType`.\n    ///\n    /// Returns `None` if the type is not a supported integer type.\n    pub fn from_i128(ty: &AlgebraicType, value: i128) -> Option<Self> {\n        let val = match ty {\n            AlgebraicType::I8 => (value as i8).into(),\n            AlgebraicType::I16 => (value as i16).into(),\n            AlgebraicType::I32 => (value as i32).into(),\n            AlgebraicType::I64 => (value as i64).into(),\n            AlgebraicType::I128 => value.into(),\n            AlgebraicType::I256 => i256::from(value).into(),\n\n            AlgebraicType::U8 => (value as u8).into(),\n            AlgebraicType::U16 => (value as u16).into(),\n            AlgebraicType::U32 => (value as u32).into(),\n            AlgebraicType::U64 => (value as u64).into(),\n            AlgebraicType::U128 => (value as u128).into(),\n            AlgebraicType::U256 => (u256::from(value as u128)).into(),\n\n            _ => return None,\n        };\n        Some(val)\n    }\n}\n\nimpl<T: Into<AlgebraicValue>> From<Option<T>> for AlgebraicValue {\n    fn from(value: Option<T>) -> Self {\n        match value {\n            None => AlgebraicValue::OptionNone(),\n            Some(x) => AlgebraicValue::OptionSome(x.into()),\n        }\n    }\n}\n\n/// An AlgebraicValue can be interpreted as a range containing a only the value itself.\n/// This is useful for BTrees where single key scans are still viewed range scans.\nimpl RangeBounds<AlgebraicValue> for &AlgebraicValue {\n    fn start_bound(&self) -> Bound<&AlgebraicValue> {\n        Bound::Included(self)\n    }\n    fn end_bound(&self) -> Bound<&AlgebraicValue> {\n        Bound::Included(self)\n    }\n}\n\nimpl RangeBounds<AlgebraicValue> for AlgebraicValue {\n    fn start_bound(&self) -> Bound<&AlgebraicValue> {\n        Bound::Included(self)\n    }\n    fn end_bound(&self) -> Bound<&AlgebraicValue> {\n        Bound::Included(self)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::satn::Satn;\n    use crate::{AlgebraicType, AlgebraicValue, ArrayValue, Typespace, ValueWithType, WithTypespace};\n\n    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {\n        WithTypespace::new(ts, ty).with_value(val)\n    }\n\n    #[test]\n    fn unit() {\n        let val = AlgebraicValue::unit();\n        let unit = AlgebraicType::unit();\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(in_space(&typespace, &unit, &val).to_satn(), \"()\");\n    }\n\n    #[test]\n    fn product_value() {\n        let product_type = AlgebraicType::product([(\"foo\", AlgebraicType::I32)]);\n        let typespace = Typespace::new(vec![]);\n        let product_value = AlgebraicValue::product([AlgebraicValue::I32(42)]);\n        assert_eq!(\n            \"(foo = 42)\",\n            in_space(&typespace, &product_type, &product_value).to_satn(),\n        );\n    }\n\n    #[test]\n    fn option_some() {\n        let option = AlgebraicType::option(AlgebraicType::never());\n        let sum_value = AlgebraicValue::OptionNone();\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(\"(none = ())\", in_space(&typespace, &option, &sum_value).to_satn(),);\n    }\n\n    #[test]\n    fn result() {\n        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);\n        let ok = AlgebraicValue::ResultOk(AlgebraicValue::U8(42));\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(\"(ok = 42)\", in_space(&typespace, &result, &ok).to_satn(),);\n\n        let err = AlgebraicValue::ResultErr(AlgebraicValue::String(\"error\".into()));\n        assert_eq!(\"(err = \\\"error\\\")\", in_space(&typespace, &result, &err).to_satn(),);\n    }\n\n    #[test]\n    fn primitive() {\n        let u8 = AlgebraicType::U8;\n        let value = AlgebraicValue::U8(255);\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(in_space(&typespace, &u8, &value).to_satn(), \"255\");\n    }\n\n    #[test]\n    fn array() {\n        let array = AlgebraicType::array(AlgebraicType::U8);\n        let value = AlgebraicValue::Array(ArrayValue::Sum([].into()));\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(in_space(&typespace, &array, &value).to_satn(), \"[]\");\n    }\n\n    #[test]\n    fn array_of_values() {\n        let array = AlgebraicType::array(AlgebraicType::U8);\n        let value = AlgebraicValue::Array([3u8].into());\n        let typespace = Typespace::new(vec![]);\n        assert_eq!(in_space(&typespace, &array, &value).to_satn(), \"0x03\");\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/algebraic_value_hash.rs",
    "content": "//! Defines hash functions for `AlgebraicValue` and friends.\n\nuse crate::{\n    bsatn::Deserializer,\n    buffer::{BufReader, DecodeError},\n    de::{Deserialize, Deserializer as _},\n    i256, u256, AlgebraicType, AlgebraicValue, ArrayValue, ProductType, ProductValue, SumType, F32, F64,\n};\nuse bytemuck::{must_cast_slice, NoUninit};\nuse core::hash::{Hash, Hasher};\nuse core::{mem, slice};\n\n// We only manually implement those hash functions that cannot be `#[derive(Hash)]`ed.\n// Those that can be are:\n//\n// - `sum: SumValue`: The generated impl will first write the `sum.tag: u8`,\n//   and then proceed to write the `sum.value`, which delegates to our custom impl below.\n//   The tag is hashed so that e.g., `Result<u32, u32>` represented as an AV\n//   results in different hashes for `Ok(x)` and `Err(x)`.\n//\n// - `map: MapValue`: Uses the hash function for `BTreeMap<AV, AV>`,\n//   which is length prefixed and then writes each `(key, value)` sequentially.\n//   Eventually, this delegates to our custom impl below.\n//\n// - `str: Box<str>`: Uses the standard hash function for `str`.\n//\n// - Primitive types: Trivially what we want,\n//   except for `U256` and `I256` which hash like `[u/i128; 2]` do when outside arrays.\n\n/// The hash function for an [`AlgebraicValue`] only hashes its domain types\n/// and avoids length prefixing for product values.\n/// This avoids the hashing `Discriminant<AlgebraicValue>`\n/// which is OK as a table column will only ever have the same type (and so the same discriminant).\nimpl Hash for AlgebraicValue {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self {\n            AlgebraicValue::Sum(x) => x.hash(state),\n            AlgebraicValue::Product(x) => x.hash(state),\n            AlgebraicValue::Array(x) => x.hash(state),\n            AlgebraicValue::Bool(x) => x.hash(state),\n            AlgebraicValue::I8(x) => x.hash(state),\n            AlgebraicValue::U8(x) => x.hash(state),\n            AlgebraicValue::I16(x) => x.hash(state),\n            AlgebraicValue::U16(x) => x.hash(state),\n            AlgebraicValue::I32(x) => x.hash(state),\n            AlgebraicValue::U32(x) => x.hash(state),\n            AlgebraicValue::I64(x) => x.hash(state),\n            AlgebraicValue::U64(x) => x.hash(state),\n            AlgebraicValue::I128(x) => x.hash(state),\n            AlgebraicValue::U128(x) => x.hash(state),\n            AlgebraicValue::I256(x) => x.hash(state),\n            AlgebraicValue::U256(x) => x.hash(state),\n            AlgebraicValue::F32(x) => x.hash(state),\n            AlgebraicValue::F64(x) => x.hash(state),\n            AlgebraicValue::String(s) => s.hash(state),\n            AlgebraicValue::Min | AlgebraicValue::Max => panic!(\"not defined for Min/Max\"),\n        }\n    }\n}\n\n/// The hash function for `ProductValue` does *not* length prefix.\nimpl Hash for ProductValue {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        for field in self.elements.iter() {\n            field.hash(state);\n        }\n    }\n}\n\n/// Hashes `slice` by converting to bytes first,\n/// as done in the standard library.\nfn hash_bytes_of(state: &mut impl Hasher, slice: &[impl NoUninit]) {\n    hash_len_and_bytes(state, slice.len(), must_cast_slice(slice))\n}\n\n/// Hashes `slice` by converting to bytes first,\n/// as done in the standard library.\n///\n/// SAFETY: The type `T` must have no padding.\nunsafe fn unchecked_hash_bytes_of<T>(state: &mut impl Hasher, slice: &[T]) {\n    let newlen = mem::size_of_val(slice);\n    let ptr = slice.as_ptr() as *const u8;\n    // SAFETY: `ptr` is valid and aligned, as `T` has no padding.\n    // The new slice only spans across `data` and is never mutated,\n    // and its total size is the same as the original `data` so it can't be over `isize::MAX`.\n    let bytes = unsafe { slice::from_raw_parts(ptr, newlen) };\n\n    hash_len_and_bytes(state, slice.len(), bytes)\n}\n\n/// The hash function for an [`ArrayValue`] only hashes its domain types.\n/// This avoids the hashing `Discriminant<ArrayValue>`\n/// which is OK as a table column will only ever have the same type (and so the same discriminant).\n/// The hash function will however length-prefix as the value is of variable length.\nimpl Hash for ArrayValue {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self {\n            ArrayValue::Sum(es) => es.hash(state),\n            ArrayValue::Product(es) => es.hash(state),\n            ArrayValue::Bool(es) => es.hash(state),\n            ArrayValue::I8(es) => hash_bytes_of(state, es),\n            ArrayValue::U8(es) => hash_bytes_of(state, es),\n            ArrayValue::I16(es) => hash_bytes_of(state, es),\n            ArrayValue::U16(es) => hash_bytes_of(state, es),\n            ArrayValue::I32(es) => hash_bytes_of(state, es),\n            ArrayValue::U32(es) => hash_bytes_of(state, es),\n            ArrayValue::I64(es) => hash_bytes_of(state, es),\n            ArrayValue::U64(es) => hash_bytes_of(state, es),\n            ArrayValue::I128(es) => hash_bytes_of(state, es),\n            ArrayValue::U128(es) => hash_bytes_of(state, es),\n            // SAFETY: The following two types are `repr(transparent)`\n            // over `[u/i128; 2]` which have no padding.\n            ArrayValue::I256(es) => unsafe { unchecked_hash_bytes_of(state, es) },\n            ArrayValue::U256(es) => unsafe { unchecked_hash_bytes_of(state, es) },\n            ArrayValue::F32(es) => es.hash(state),\n            ArrayValue::F64(es) => es.hash(state),\n            ArrayValue::String(es) => es.hash(state),\n            ArrayValue::Array(es) => es.hash(state),\n        }\n    }\n}\n\ntype HR = Result<(), DecodeError>;\n\nfn hash_bsatn<'a>(state: &mut impl Hasher, ty: &AlgebraicType, de: Deserializer<'_, impl BufReader<'a>>) -> HR {\n    match ty {\n        AlgebraicType::Ref(_) => unreachable!(\"hash_bsatn does not have a typespace\"),\n        AlgebraicType::Sum(ty) => hash_bsatn_sum(state, ty, de),\n        AlgebraicType::Product(ty) => hash_bsatn_prod(state, ty, de),\n        AlgebraicType::Array(ty) => hash_bsatn_array(state, &ty.elem_ty, de),\n        AlgebraicType::Bool => hash_bsatn_de::<bool>(state, de),\n        AlgebraicType::I8 => hash_bsatn_de::<i8>(state, de),\n        AlgebraicType::U8 => hash_bsatn_de::<u8>(state, de),\n        AlgebraicType::I16 => hash_bsatn_de::<i16>(state, de),\n        AlgebraicType::U16 => hash_bsatn_de::<u16>(state, de),\n        AlgebraicType::I32 => hash_bsatn_de::<i32>(state, de),\n        AlgebraicType::U32 => hash_bsatn_de::<u32>(state, de),\n        AlgebraicType::I64 => hash_bsatn_de::<i64>(state, de),\n        AlgebraicType::U64 => hash_bsatn_de::<u64>(state, de),\n        AlgebraicType::I128 => hash_bsatn_de::<i128>(state, de),\n        AlgebraicType::U128 => hash_bsatn_de::<u128>(state, de),\n        AlgebraicType::I256 => hash_bsatn_de::<i256>(state, de),\n        AlgebraicType::U256 => hash_bsatn_de::<u256>(state, de),\n        AlgebraicType::F32 => hash_bsatn_de::<F32>(state, de),\n        AlgebraicType::F64 => hash_bsatn_de::<F64>(state, de),\n        AlgebraicType::String => hash_bsatn_de::<&str>(state, de),\n    }\n}\n\n/// Hashes the tag and payload of the BSATN-encoded sum value.\nfn hash_bsatn_sum<'a>(state: &mut impl Hasher, ty: &SumType, mut de: Deserializer<'_, impl BufReader<'a>>) -> HR {\n    // Read + hash the tag.\n    let tag = de.reborrow().deserialize_u8()?;\n    tag.hash(state);\n\n    // Hash the payload.\n    let data_ty = &ty.variants[tag as usize].algebraic_type;\n    hash_bsatn(state, data_ty, de)\n}\n\n/// Hashes every field in the BSATN-encoded product value.\nfn hash_bsatn_prod<'a>(state: &mut impl Hasher, ty: &ProductType, mut de: Deserializer<'_, impl BufReader<'a>>) -> HR {\n    ty.elements\n        .iter()\n        .try_for_each(|f| hash_bsatn(state, &f.algebraic_type, de.reborrow()))\n}\n\n/// Hashes every elem in the BSATN-encoded array value.\npub fn hash_bsatn_array<'a>(\n    state: &mut impl Hasher,\n    ty: &AlgebraicType,\n    de: Deserializer<'_, impl BufReader<'a>>,\n) -> HR {\n    // The BSATN is length-prefixed.\n    // `Hash for &[T]` also does length-prefixing.\n    match ty {\n        AlgebraicType::Ref(_) => unreachable!(\"hash_bsatn does not have a typespace\"),\n        AlgebraicType::Sum(ty) => hash_bsatn_seq(state, de, |s, d| hash_bsatn_sum(s, ty, d)),\n        AlgebraicType::Product(ty) => hash_bsatn_seq(state, de, |s, d| hash_bsatn_prod(s, ty, d)),\n        AlgebraicType::Array(ty) => hash_bsatn_seq(state, de, |s, d| hash_bsatn_array(s, &ty.elem_ty, d)),\n        AlgebraicType::Bool => hash_bsatn_seq(state, de, hash_bsatn_de::<bool>),\n        AlgebraicType::I8 | AlgebraicType::U8 => hash_bsatn_int_seq(state, de, 1),\n        AlgebraicType::I16 | AlgebraicType::U16 => hash_bsatn_int_seq(state, de, 2),\n        AlgebraicType::I32 | AlgebraicType::U32 => hash_bsatn_int_seq(state, de, 4),\n        AlgebraicType::I64 | AlgebraicType::U64 => hash_bsatn_int_seq(state, de, 8),\n        AlgebraicType::I128 | AlgebraicType::U128 => hash_bsatn_int_seq(state, de, 16),\n        AlgebraicType::I256 | AlgebraicType::U256 => hash_bsatn_int_seq(state, de, 32),\n        AlgebraicType::F32 => hash_bsatn_seq(state, de, hash_bsatn_de::<F32>),\n        AlgebraicType::F64 => hash_bsatn_seq(state, de, hash_bsatn_de::<F64>),\n        AlgebraicType::String => hash_bsatn_seq(state, de, hash_bsatn_de::<&str>),\n    }\n}\n\n/// Hashes elements in the BSATN-encoded element sequence.\n/// The sequence is prefixed with its length and the hash will as well.\nfn hash_bsatn_seq<'a, H: Hasher, R: BufReader<'a>>(\n    state: &mut H,\n    mut de: Deserializer<'_, R>,\n    mut elem_hash: impl FnMut(&mut H, Deserializer<'_, R>) -> Result<(), DecodeError>,\n) -> HR {\n    // The BSATN is length-prefixed.\n    // The Hash also needs to be length-prefixed.\n    let len = de.reborrow().deserialize_len()?;\n    state.write_usize(len);\n\n    // Hash each element.\n    (0..len).try_for_each(|_| elem_hash(state, de.reborrow()))\n}\n\n/// Hashes the BSATN-encoded integer sequence where each integer is `width` bytes wide.\n/// The sequence is prefixed with its length and the hash will as well.\nfn hash_bsatn_int_seq<'a, H: Hasher, R: BufReader<'a>>(state: &mut H, mut de: Deserializer<'_, R>, width: usize) -> HR {\n    // The BSATN is length-prefixed.\n    // The Hash also needs to be length-prefixed.\n    let len = de.reborrow().deserialize_len()?;\n\n    // Extract and hash the bytes.\n    // This is consistent with what `<$int_primitive>::hash_slice` will do\n    // and for `U/I256` we provide special logic in `impl Hash for ArrayValue` above\n    // and handle it the same way for `spacetimedb_table::table::RowRef`s.\n    let bytes = de.get_slice(len * width)?;\n\n    hash_len_and_bytes(state, len, bytes);\n    Ok(())\n}\n\n/// Hashes a `len` prefix as well as `bytes`.\nfn hash_len_and_bytes(state: &mut impl Hasher, len: usize, bytes: &[u8]) {\n    state.write_usize(len);\n    state.write(bytes);\n}\n\n/// Deserializes from `de` an `x: T` and then proceeds to hash `x`.\nfn hash_bsatn_de<'a, T: Hash + Deserialize<'a>>(\n    state: &mut impl Hasher,\n    de: Deserializer<'_, impl BufReader<'a>>,\n) -> HR {\n    T::deserialize(de).map(|x| x.hash(state))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::hash_bsatn;\n    use crate::{\n        bsatn::{to_vec, Deserializer},\n        proptest::generate_typed_value,\n        AlgebraicType, AlgebraicValue,\n    };\n    use proptest::prelude::*;\n    use std::hash::{BuildHasher, Hasher as _};\n\n    fn hash_one_bsatn_av(bh: &impl BuildHasher, ty: &AlgebraicType, val: &AlgebraicValue) -> u64 {\n        let mut bsatn = &*to_vec(&val).unwrap();\n        let de = Deserializer::new(&mut bsatn);\n        let mut hasher = bh.build_hasher();\n        hash_bsatn(&mut hasher, ty, de).unwrap();\n        hasher.finish()\n    }\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(2048))]\n        #[test]\n        fn av_bsatn_hash_same_std_random_state((ty, val) in generate_typed_value()) {\n            let rs = std::hash::RandomState::new();\n            let hash_av = rs.hash_one(&val);\n            let hash_av_bsatn = hash_one_bsatn_av(&rs, &ty, &val);\n            prop_assert_eq!(hash_av, hash_av_bsatn);\n        }\n\n        #[test]\n        fn av_bsatn_hash_same_ahash((ty, val) in generate_typed_value()) {\n            let rs = ahash::RandomState::new();\n            let hash_av = rs.hash_one(&val);\n            let hash_av_bsatn = hash_one_bsatn_av(&rs, &ty, &val);\n            prop_assert_eq!(hash_av, hash_av_bsatn);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/array_type.rs",
    "content": "use crate::algebraic_type::AlgebraicType;\nuse crate::de::Deserialize;\nuse crate::meta_type::MetaType;\nuse crate::{impl_deserialize, impl_serialize, impl_st, ArrayValue, Typespace};\n\n/// An array type is a homogeneous product type of dynamic length.\n///\n/// That is, it is a product type\n/// where every element / factor / field is of the same type\n/// and where the length is statically unknown.\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct ArrayType {\n    /// The base type every element of the array has.\n    pub elem_ty: Box<AlgebraicType>,\n}\n\nimpl_serialize!([] ArrayType, (self, ser) => self.elem_ty.serialize(ser));\nimpl_deserialize!([] ArrayType, de => Deserialize::deserialize(de).map(|elem_ty| Self { elem_ty }));\nimpl_st!([] ArrayType, ts => AlgebraicType::make_type(ts));\n\nimpl MetaType for ArrayType {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::ZERO_REF\n    }\n}\n\nimpl ArrayType {\n    pub fn type_check(&self, array_value: &ArrayValue, typespace: &Typespace) -> bool {\n        array_value\n            .iter_cloned()\n            .all(|elem| self.elem_ty.type_check(&elem, typespace))\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/array_value.rs",
    "content": "use crate::{i256, u256};\nuse crate::{AlgebraicType, AlgebraicValue, ArrayType, ProductValue, SumValue, F32, F64};\nuse core::fmt;\n\n/// An array value in \"monomorphized form\".\n///\n/// Arrays are represented in this way monomorphized fashion for efficiency\n/// rather than unnecessary indirections and tags of `AlgebraicValue`.\n/// We can do this as we know statically that the type of each element is the same\n/// as arrays are homogenous dynamically sized product types.\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]\npub enum ArrayValue {\n    /// An array of [`SumValue`]s.\n    Sum(Box<[SumValue]>),\n    /// An array of [`ProductValue`]s.\n    Product(Box<[ProductValue]>),\n    /// An array of [`bool`]s.\n    Bool(Box<[bool]>),\n    /// An array of [`i8`]s.\n    I8(Box<[i8]>),\n    /// An array of [`u8`]s.\n    U8(Box<[u8]>),\n    /// An array of [`i16`]s.\n    I16(Box<[i16]>),\n    /// An array of [`u16`]s.\n    U16(Box<[u16]>),\n    /// An array of [`i32`]s.\n    I32(Box<[i32]>),\n    /// An array of [`u32`]s.\n    U32(Box<[u32]>),\n    /// An array of [`i64`]s.\n    I64(Box<[i64]>),\n    /// An array of [`u64`]s.\n    U64(Box<[u64]>),\n    /// An array of [`i128`]s.\n    I128(Box<[i128]>),\n    /// An array of [`u128`]s.\n    U128(Box<[u128]>),\n    /// An array of [`i256`]s.\n    I256(Box<[i256]>),\n    /// An array of [`u256`]s.\n    U256(Box<[u256]>),\n    /// An array of totally ordered [`F32`]s.\n    F32(Box<[F32]>),\n    /// An array of totally ordered [`F64`]s.\n    F64(Box<[F64]>),\n    /// An array of UTF-8 strings.\n    String(Box<[Box<str>]>),\n    /// An array of arrays.\n    Array(Box<[ArrayValue]>),\n}\n\nimpl crate::Value for ArrayValue {\n    type Type = ArrayType;\n}\n\nimpl ArrayValue {\n    /// Determines (infers / synthesises) the type of the value.\n    pub(crate) fn type_of(&self) -> Option<ArrayType> {\n        let elem_ty = Box::new(match self {\n            Self::Sum(_) => None,\n            Self::Product(v) => AlgebraicValue::type_of_product(v.first()?),\n            Self::Bool(_) => Some(AlgebraicType::Bool),\n            Self::I8(_) => Some(AlgebraicType::I8),\n            Self::U8(_) => Some(AlgebraicType::U8),\n            Self::I16(_) => Some(AlgebraicType::I16),\n            Self::U16(_) => Some(AlgebraicType::U16),\n            Self::I32(_) => Some(AlgebraicType::I32),\n            Self::U32(_) => Some(AlgebraicType::U32),\n            Self::I64(_) => Some(AlgebraicType::I64),\n            Self::U64(_) => Some(AlgebraicType::U64),\n            Self::I128(_) => Some(AlgebraicType::I128),\n            Self::U128(_) => Some(AlgebraicType::U128),\n            Self::I256(_) => Some(AlgebraicType::I256),\n            Self::U256(_) => Some(AlgebraicType::U256),\n            Self::F32(_) => Some(AlgebraicType::F32),\n            Self::F64(_) => Some(AlgebraicType::F64),\n            Self::String(_) => Some(AlgebraicType::String),\n            Self::Array(v) => Some(v.first()?.type_of()?.into()),\n        }?);\n        Some(ArrayType { elem_ty })\n    }\n\n    /// Returns the length of the array.\n    pub fn len(&self) -> usize {\n        match self {\n            Self::Sum(v) => v.len(),\n            Self::Product(v) => v.len(),\n            Self::Bool(v) => v.len(),\n            Self::I8(v) => v.len(),\n            Self::U8(v) => v.len(),\n            Self::I16(v) => v.len(),\n            Self::U16(v) => v.len(),\n            Self::I32(v) => v.len(),\n            Self::U32(v) => v.len(),\n            Self::I64(v) => v.len(),\n            Self::U64(v) => v.len(),\n            Self::I128(v) => v.len(),\n            Self::U128(v) => v.len(),\n            Self::I256(v) => v.len(),\n            Self::U256(v) => v.len(),\n            Self::F32(v) => v.len(),\n            Self::F64(v) => v.len(),\n            Self::String(v) => v.len(),\n            Self::Array(v) => v.len(),\n        }\n    }\n\n    /// Returns whether the array is empty.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns a cloning iterator on the elements of `self` as `AlgebraicValue`s.\n    pub fn iter_cloned(&self) -> ArrayValueIterCloned<'_> {\n        match self {\n            ArrayValue::Sum(v) => ArrayValueIterCloned::Sum(v.iter()),\n            ArrayValue::Product(v) => ArrayValueIterCloned::Product(v.iter()),\n            ArrayValue::Bool(v) => ArrayValueIterCloned::Bool(v.iter()),\n            ArrayValue::I8(v) => ArrayValueIterCloned::I8(v.iter()),\n            ArrayValue::U8(v) => ArrayValueIterCloned::U8(v.iter()),\n            ArrayValue::I16(v) => ArrayValueIterCloned::I16(v.iter()),\n            ArrayValue::U16(v) => ArrayValueIterCloned::U16(v.iter()),\n            ArrayValue::I32(v) => ArrayValueIterCloned::I32(v.iter()),\n            ArrayValue::U32(v) => ArrayValueIterCloned::U32(v.iter()),\n            ArrayValue::I64(v) => ArrayValueIterCloned::I64(v.iter()),\n            ArrayValue::U64(v) => ArrayValueIterCloned::U64(v.iter()),\n            ArrayValue::I128(v) => ArrayValueIterCloned::I128(v.iter()),\n            ArrayValue::U128(v) => ArrayValueIterCloned::U128(v.iter()),\n            ArrayValue::I256(v) => ArrayValueIterCloned::I256(v.iter()),\n            ArrayValue::U256(v) => ArrayValueIterCloned::U256(v.iter()),\n            ArrayValue::F32(v) => ArrayValueIterCloned::F32(v.iter()),\n            ArrayValue::F64(v) => ArrayValueIterCloned::F64(v.iter()),\n            ArrayValue::String(v) => ArrayValueIterCloned::String(v.iter()),\n            ArrayValue::Array(v) => ArrayValueIterCloned::Array(v.iter()),\n        }\n    }\n}\n\nimpl Default for ArrayValue {\n    /// The default `ArrayValue` is an empty array of sum values.\n    fn default() -> Self {\n        Self::from(<[crate::SumValue; 0]>::default())\n    }\n}\n\nmacro_rules! impl_from_array {\n    ($el:ty, $var:ident) => {\n        impl<const N: usize> From<[$el; N]> for ArrayValue {\n            fn from(v: [$el; N]) -> Self {\n                let vec: Box<[_]> = v.into();\n                vec.into()\n            }\n        }\n\n        // Exists for convenience.\n        impl From<Box<[$el]>> for ArrayValue {\n            fn from(v: Box<[$el]>) -> Self {\n                Self::$var(v)\n            }\n        }\n    };\n}\n\nimpl_from_array!(crate::SumValue, Sum);\nimpl_from_array!(crate::ProductValue, Product);\nimpl_from_array!(bool, Bool);\nimpl_from_array!(i8, I8);\nimpl_from_array!(u8, U8);\nimpl_from_array!(i16, I16);\nimpl_from_array!(u16, U16);\nimpl_from_array!(i32, I32);\nimpl_from_array!(u32, U32);\nimpl_from_array!(i64, I64);\nimpl_from_array!(u64, U64);\nimpl_from_array!(i128, I128);\nimpl_from_array!(u128, U128);\nimpl_from_array!(i256, I256);\nimpl_from_array!(u256, U256);\nimpl_from_array!(F32, F32);\nimpl_from_array!(F64, F64);\nimpl_from_array!(Box<str>, String);\nimpl_from_array!(ArrayValue, Array);\n\nimpl ArrayValue {\n    /// Returns `self` as `&dyn Debug`.\n    fn as_dyn_debug(&self) -> &dyn fmt::Debug {\n        match self {\n            Self::Sum(v) => v,\n            Self::Product(v) => v,\n            Self::Bool(v) => v,\n            Self::I8(v) => v,\n            Self::U8(v) => v,\n            Self::I16(v) => v,\n            Self::U16(v) => v,\n            Self::I32(v) => v,\n            Self::U32(v) => v,\n            Self::I64(v) => v,\n            Self::U64(v) => v,\n            Self::I128(v) => v,\n            Self::U128(v) => v,\n            Self::I256(v) => v,\n            Self::U256(v) => v,\n            Self::F32(v) => v,\n            Self::F64(v) => v,\n            Self::String(v) => v,\n            Self::Array(v) => v,\n        }\n    }\n}\nimpl fmt::Debug for ArrayValue {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.as_dyn_debug().fmt(f)\n    }\n}\n\nimpl IntoIterator for ArrayValue {\n    type Item = AlgebraicValue;\n\n    type IntoIter = ArrayValueIntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        match self {\n            ArrayValue::Sum(v) => ArrayValueIntoIter::Sum(Vec::from(v).into_iter()),\n            ArrayValue::Product(v) => ArrayValueIntoIter::Product(Vec::from(v).into_iter()),\n            ArrayValue::Bool(v) => ArrayValueIntoIter::Bool(Vec::from(v).into_iter()),\n            ArrayValue::I8(v) => ArrayValueIntoIter::I8(Vec::from(v).into_iter()),\n            ArrayValue::U8(v) => ArrayValueIntoIter::U8(Vec::from(v).into_iter()),\n            ArrayValue::I16(v) => ArrayValueIntoIter::I16(Vec::from(v).into_iter()),\n            ArrayValue::U16(v) => ArrayValueIntoIter::U16(Vec::from(v).into_iter()),\n            ArrayValue::I32(v) => ArrayValueIntoIter::I32(Vec::from(v).into_iter()),\n            ArrayValue::U32(v) => ArrayValueIntoIter::U32(Vec::from(v).into_iter()),\n            ArrayValue::I64(v) => ArrayValueIntoIter::I64(Vec::from(v).into_iter()),\n            ArrayValue::U64(v) => ArrayValueIntoIter::U64(Vec::from(v).into_iter()),\n            ArrayValue::I128(v) => ArrayValueIntoIter::I128(Vec::from(v).into_iter()),\n            ArrayValue::U128(v) => ArrayValueIntoIter::U128(Vec::from(v).into_iter()),\n            ArrayValue::I256(v) => ArrayValueIntoIter::I256(Vec::from(v).into_iter()),\n            ArrayValue::U256(v) => ArrayValueIntoIter::U256(Vec::from(v).into_iter()),\n            ArrayValue::F32(v) => ArrayValueIntoIter::F32(Vec::from(v).into_iter()),\n            ArrayValue::F64(v) => ArrayValueIntoIter::F64(Vec::from(v).into_iter()),\n            ArrayValue::String(v) => ArrayValueIntoIter::String(Vec::from(v).into_iter()),\n            ArrayValue::Array(v) => ArrayValueIntoIter::Array(Vec::from(v).into_iter()),\n        }\n    }\n}\n\n/// A by-value iterator on the elements of an `ArrayValue` as `AlgebraicValue`s.\npub enum ArrayValueIntoIter {\n    /// An iterator on a sum value array.\n    Sum(std::vec::IntoIter<SumValue>),\n    /// An iterator on a product value array.\n    Product(std::vec::IntoIter<ProductValue>),\n    /// An iterator on a [`bool`] array.\n    Bool(std::vec::IntoIter<bool>),\n    /// An iterator on an [`i8`] array.\n    I8(std::vec::IntoIter<i8>),\n    /// An iterator on a [`u8`] array.\n    U8(std::vec::IntoIter<u8>),\n    /// An iterator on an [`i16`] array.\n    I16(std::vec::IntoIter<i16>),\n    /// An iterator on a [`u16`] array.\n    U16(std::vec::IntoIter<u16>),\n    /// An iterator on an [`i32`] array.\n    I32(std::vec::IntoIter<i32>),\n    /// An iterator on a [`u32`] array.\n    U32(std::vec::IntoIter<u32>),\n    /// An iterator on an [`i64`] array.\n    I64(std::vec::IntoIter<i64>),\n    /// An iterator on a [`u64`] array.\n    U64(std::vec::IntoIter<u64>),\n    /// An iterator on an [`i128`] array.\n    I128(std::vec::IntoIter<i128>),\n    /// An iterator on a [`u128`] array.\n    U128(std::vec::IntoIter<u128>),\n    /// An iterator on an [`i256`] array.\n    I256(std::vec::IntoIter<i256>),\n    /// An iterator on a [`u256`] array.\n    U256(std::vec::IntoIter<u256>),\n    /// An iterator on a [`F32`] array.\n    F32(std::vec::IntoIter<F32>),\n    /// An iterator on a [`F64`] array.\n    F64(std::vec::IntoIter<F64>),\n    /// An iterator on an array of UTF-8 strings.\n    String(std::vec::IntoIter<Box<str>>),\n    /// An iterator on an array of arrays.\n    Array(std::vec::IntoIter<ArrayValue>),\n}\n\nimpl Iterator for ArrayValueIntoIter {\n    type Item = AlgebraicValue;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            ArrayValueIntoIter::Sum(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::Product(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::Bool(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I8(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U8(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I16(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U16(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I32(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U32(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I64(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U64(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I128(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U128(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::I256(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::U256(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::F32(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::F64(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::String(it) => it.next().map(Into::into),\n            ArrayValueIntoIter::Array(it) => it.next().map(Into::into),\n        }\n    }\n}\n\npub enum ArrayValueIterCloned<'a> {\n    Sum(std::slice::Iter<'a, SumValue>),\n    Product(std::slice::Iter<'a, ProductValue>),\n    Bool(std::slice::Iter<'a, bool>),\n    I8(std::slice::Iter<'a, i8>),\n    U8(std::slice::Iter<'a, u8>),\n    I16(std::slice::Iter<'a, i16>),\n    U16(std::slice::Iter<'a, u16>),\n    I32(std::slice::Iter<'a, i32>),\n    U32(std::slice::Iter<'a, u32>),\n    I64(std::slice::Iter<'a, i64>),\n    U64(std::slice::Iter<'a, u64>),\n    I128(std::slice::Iter<'a, i128>),\n    U128(std::slice::Iter<'a, u128>),\n    I256(std::slice::Iter<'a, i256>),\n    U256(std::slice::Iter<'a, u256>),\n    F32(std::slice::Iter<'a, F32>),\n    F64(std::slice::Iter<'a, F64>),\n    String(std::slice::Iter<'a, Box<str>>),\n    Array(std::slice::Iter<'a, ArrayValue>),\n}\n\nimpl Iterator for ArrayValueIterCloned<'_> {\n    type Item = AlgebraicValue;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            ArrayValueIterCloned::Sum(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::Product(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::Bool(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I8(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U8(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I16(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U16(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I32(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U32(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I64(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U64(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I128(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U128(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::I256(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::U256(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::F32(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::F64(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::String(it) => it.next().cloned().map(Into::into),\n            ArrayValueIterCloned::Array(it) => it.next().cloned().map(Into::into),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/bsatn/de.rs",
    "content": "use crate::buffer::{BufReader, DecodeError};\nuse crate::de::{self, Deserializer as _, SeqProductAccess, SumAccess, VariantAccess};\nuse crate::{i256, u256};\n\n/// Deserializer from the BSATN data format.\npub struct Deserializer<'a, R> {\n    // The input to deserialize.\n    reader: &'a mut R,\n}\n\nimpl<'a, 'de, R: BufReader<'de>> Deserializer<'a, R> {\n    /// Returns a deserializer using the given `reader`.\n    pub fn new(reader: &'a mut R) -> Self {\n        Self { reader }\n    }\n\n    /// Reborrows the deserializer.\n    #[inline]\n    pub(crate) fn reborrow(&mut self) -> Deserializer<'_, R> {\n        Deserializer { reader: self.reader }\n    }\n\n    /// Reads a length as a `u32` then converted to `usize`.\n    pub(crate) fn deserialize_len(self) -> Result<usize, DecodeError> {\n        Ok(self.deserialize_u32()? as usize)\n    }\n\n    /// Reads a slice of `len` elements.\n    #[inline]\n    #[doc(hidden)]\n    pub fn get_slice(&mut self, len: usize) -> Result<&'de [u8], DecodeError> {\n        self.reader.get_slice(len)\n    }\n\n    /// Reads a byte slice from the `reader`.\n    fn deserialize_bytes_inner(mut self) -> Result<&'de [u8], DecodeError> {\n        let len = self.reborrow().deserialize_len()?;\n        self.get_slice(len)\n    }\n}\n\nimpl de::Error for DecodeError {\n    fn custom(msg: impl std::fmt::Display) -> Self {\n        Self::Other(msg.to_string())\n    }\n\n    fn unknown_variant_tag<'de, T: de::SumVisitor<'de>>(tag: u8, expected: &T) -> Self {\n        let sum_name = expected.sum_name().map(|x| x.to_owned());\n        Self::InvalidTag { tag, sum_name }\n    }\n\n    fn allocation_failed(size: usize) -> Self {\n        Self::AllocationFailed(size)\n    }\n}\n\nimpl<'de, R: BufReader<'de>> de::Deserializer<'de> for Deserializer<'_, R> {\n    type Error = DecodeError;\n\n    fn deserialize_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, DecodeError> {\n        visitor.visit_seq_product(self)\n    }\n\n    fn validate_product<V: de::ProductVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        visitor.validate_seq_product(self)\n    }\n\n    fn deserialize_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, DecodeError> {\n        visitor.visit_sum(self)\n    }\n\n    fn validate_sum<V: de::SumVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        visitor.validate_sum(self)\n    }\n\n    fn deserialize_bool(self) -> Result<bool, Self::Error> {\n        let byte = self.reader.get_u8()?;\n        match byte {\n            0 => Ok(false),\n            1 => Ok(true),\n            b => Err(DecodeError::InvalidBool(b)),\n        }\n    }\n    fn deserialize_u8(self) -> Result<u8, DecodeError> {\n        self.reader.get_u8()\n    }\n    fn deserialize_u16(self) -> Result<u16, DecodeError> {\n        self.reader.get_u16()\n    }\n    fn deserialize_u32(self) -> Result<u32, DecodeError> {\n        self.reader.get_u32()\n    }\n    fn deserialize_u64(self) -> Result<u64, DecodeError> {\n        self.reader.get_u64()\n    }\n    fn deserialize_u128(self) -> Result<u128, DecodeError> {\n        self.reader.get_u128()\n    }\n    fn deserialize_u256(self) -> Result<u256, DecodeError> {\n        self.reader.get_u256()\n    }\n    fn deserialize_i8(self) -> Result<i8, DecodeError> {\n        self.reader.get_i8()\n    }\n    fn deserialize_i16(self) -> Result<i16, DecodeError> {\n        self.reader.get_i16()\n    }\n    fn deserialize_i32(self) -> Result<i32, DecodeError> {\n        self.reader.get_i32()\n    }\n    fn deserialize_i64(self) -> Result<i64, DecodeError> {\n        self.reader.get_i64()\n    }\n    fn deserialize_i128(self) -> Result<i128, DecodeError> {\n        self.reader.get_i128()\n    }\n    fn deserialize_i256(self) -> Result<i256, DecodeError> {\n        self.reader.get_i256()\n    }\n    fn deserialize_f32(self) -> Result<f32, Self::Error> {\n        self.reader.get_u32().map(f32::from_bits)\n    }\n    fn deserialize_f64(self) -> Result<f64, Self::Error> {\n        self.reader.get_u64().map(f64::from_bits)\n    }\n\n    fn deserialize_str<V: de::SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let slice = self.deserialize_bytes_inner()?;\n        let slice = core::str::from_utf8(slice)?;\n        visitor.visit_borrowed(slice)\n    }\n\n    fn deserialize_bytes<V: de::SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        let slice = self.deserialize_bytes_inner()?;\n        visitor.visit_borrowed(slice)\n    }\n\n    fn deserialize_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        mut self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        let len = self.reborrow().deserialize_len()?;\n        let seeds = itertools::repeat_n(seed, len);\n        visitor.visit(ArrayAccess { de: self, seeds })\n    }\n\n    fn validate_array_seed<V: de::ArrayVisitor<'de, T::Output>, T: de::DeserializeSeed<'de> + Clone>(\n        mut self,\n        visitor: V,\n        seed: T,\n    ) -> Result<(), Self::Error> {\n        let len = self.reborrow().deserialize_len()?;\n        let seeds = itertools::repeat_n(seed, len);\n        visitor.validate(ArrayAccess { de: self, seeds })\n    }\n}\n\nimpl<'de, R: BufReader<'de>> SeqProductAccess<'de> for Deserializer<'_, R> {\n    type Error = DecodeError;\n\n    fn next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Output>, DecodeError> {\n        seed.deserialize(self.reborrow()).map(Some)\n    }\n\n    fn validate_next_element_seed<T: de::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<()>, Self::Error> {\n        seed.validate(self.reborrow()).map(Some)\n    }\n}\n\nimpl<'de, R: BufReader<'de>> SumAccess<'de> for Deserializer<'_, R> {\n    type Error = DecodeError;\n    type Variant = Self;\n\n    fn variant<V: de::VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        let tag = self.reader.get_u8()?;\n        visitor.visit_tag(tag).map(|variant| (variant, self))\n    }\n}\n\nimpl<'de, R: BufReader<'de>> VariantAccess<'de> for Deserializer<'_, R> {\n    type Error = DecodeError;\n    fn deserialize_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(self)\n    }\n    fn validate_seed<T: de::DeserializeSeed<'de>>(self, seed: T) -> Result<(), Self::Error> {\n        seed.validate(self)\n    }\n}\n\n/// Deserializer for array elements.\npub struct ArrayAccess<'a, R, T> {\n    de: Deserializer<'a, R>,\n    seeds: itertools::RepeatN<T>,\n}\n\nimpl<'de, R: BufReader<'de>, T: de::DeserializeSeed<'de> + Clone> de::ArrayAccess<'de> for ArrayAccess<'_, R, T> {\n    type Element = T::Output;\n    type Error = DecodeError;\n\n    fn next_element(&mut self) -> Result<Option<T::Output>, Self::Error> {\n        self.seeds\n            .next()\n            .map(|seed| seed.deserialize(self.de.reborrow()))\n            .transpose()\n    }\n\n    fn validate_next_element(&mut self) -> Result<Option<()>, Self::Error> {\n        self.seeds\n            .next()\n            .map(|seed| seed.validate(self.de.reborrow()))\n            .transpose()\n    }\n\n    fn size_hint(&self) -> Option<usize> {\n        Some(self.seeds.len())\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/bsatn/eq.rs",
    "content": "//! Defines the function [`eq_bsatn`] which equates `lhs: &AlgebraicValue` to `rhs` defined in BSATN.\n//!\n//! The lifetime `'r` in `eq_bsatn` is the lifetime of `rhs`'s backing data, i.e., the BSATN itself.\n\nuse super::Deserializer;\nuse crate::{buffer::BufReader, de::Deserialize, AlgebraicValue, ArrayValue, ProductValue, SumValue};\nuse core::{mem, slice};\n\n/// Equates `lhs` to a BSATN-encoded `AlgebraicValue` of the same type.\npub fn eq_bsatn<'r>(lhs: &AlgebraicValue, rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    match lhs {\n        AlgebraicValue::Sum(lhs) => eq_bsatn_sum(lhs, rhs),\n        AlgebraicValue::Product(lhs) => eq_bsatn_prod(lhs, rhs),\n        AlgebraicValue::Array(lhs) => eq_bsatn_array(lhs, rhs),\n        AlgebraicValue::Bool(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::I8(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::U8(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::I16(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::U16(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::I32(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::U32(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::I64(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::U64(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::I128(lhs) => eq_bsatn_de(&{ lhs.0 }, rhs),\n        AlgebraicValue::U128(lhs) => eq_bsatn_de(&{ lhs.0 }, rhs),\n        AlgebraicValue::I256(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::U256(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::F32(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::F64(lhs) => eq_bsatn_de(lhs, rhs),\n        AlgebraicValue::String(lhs) => eq_bsatn_str(lhs, rhs),\n        AlgebraicValue::Min | AlgebraicValue::Max => panic!(\"not defined for Min/Max\"),\n    }\n}\n\n/// Equates the tag and payload to that of the BSATN-encoded sum value.\nfn eq_bsatn_sum<'r>(lhs: &SumValue, mut rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    eq_bsatn_de(&lhs.tag, rhs.reborrow()) && eq_bsatn(&lhs.value, rhs)\n}\n\n/// Equates every field `lhs` to those in the BSATN-encoded product value.\nfn eq_bsatn_prod<'r>(lhs: &ProductValue, mut rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    lhs.elements.iter().all(|f| eq_bsatn(f, rhs.reborrow()))\n}\n\n/// Equates every elem in `lhs` to those in the BSATN-encoded array value.\nfn eq_bsatn_array<'r>(lhs: &ArrayValue, rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    match lhs {\n        ArrayValue::Sum(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_sum),\n        ArrayValue::Product(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_prod),\n        ArrayValue::Bool(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_de),\n        ArrayValue::F32(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_de),\n        ArrayValue::F64(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_de),\n        ArrayValue::String(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_str),\n        ArrayValue::Array(lhs) => eq_bsatn_seq(&**lhs, rhs, eq_bsatn_array),\n        // SAFETY: For all of the below, the element types are integer types, as required.\n        ArrayValue::I8(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U8(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::I16(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U16(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::I32(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U32(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::I64(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U64(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::I128(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U128(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::I256(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n        ArrayValue::U256(lhs) => unsafe { eq_bsatn_int_seq(lhs, rhs) },\n    }\n}\n\n/// Equates the integer slice `lhs` to the BSATN-encoded one in `rhs`.\n///\n/// SAFETY: `T` must be an integer type.\nunsafe fn eq_bsatn_int_seq<'r, T>(lhs: &[T], mut rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    // The BSATN is length-prefixed.\n    let Ok(len) = rhs.reborrow().deserialize_len() else {\n        return false;\n    };\n\n    // Extract the rhs bytes.\n    let Ok(rhs_bytes) = rhs.get_slice(len * mem::size_of::<T>()) else {\n        return false;\n    };\n\n    // Convert `lhs` to `&[u8]`.\n    let ptr = lhs.as_ptr().cast::<u8>();\n    // SAFETY: Caller promised that `T` is an integer type.\n    // Thus it has no safety requirements and no padding,\n    // so it is legal to convert `&[IntType] -> &[u8]`.\n    let lhs_bytes = unsafe { slice::from_raw_parts(ptr, mem::size_of_val(lhs)) };\n\n    lhs_bytes == rhs_bytes\n}\n\n/// Equates the string `lhs` to the BSATN-encoded one in `rhs`.\n#[allow(clippy::borrowed_box)]\nfn eq_bsatn_str<'r>(lhs: &Box<str>, rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    <&str>::deserialize(rhs).map(|rhs| &**lhs == rhs).unwrap_or(false)\n}\n\n/// Equates elements in `lhs` to the BSATN-encoded element sequence in `rhs`.\n/// The sequence is prefixed with its length.\nfn eq_bsatn_seq<'r, T, I: ExactSizeIterator<Item = T>, R: BufReader<'r>>(\n    lhs: impl IntoIterator<IntoIter = I>,\n    mut rhs: Deserializer<'_, R>,\n    elem_eq: impl Fn(T, Deserializer<'_, R>) -> bool,\n) -> bool {\n    let mut lhs = lhs.into_iter();\n    // The BSATN is length-prefixed.\n    // Compare against length first.\n    match rhs.reborrow().deserialize_len() {\n        Ok(len) if lhs.len() == len => lhs.all(|e| elem_eq(e, rhs.reborrow())),\n        _ => false,\n    }\n}\n\n/// Deserializes from `de` an `rhs: T` and then proceeds to `lhs == rhs`.\nfn eq_bsatn_de<'r, T: Eq + Deserialize<'r>>(lhs: &T, rhs: Deserializer<'_, impl BufReader<'r>>) -> bool {\n    T::deserialize(rhs).map(|rhs| lhs == &rhs).unwrap_or(false)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::eq_bsatn;\n    use crate::{\n        bsatn::{to_vec, Deserializer},\n        proptest::generate_typed_value,\n    };\n    use proptest::prelude::*;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(2048))]\n        #[test]\n        fn encoded_val_eq_to_self((_, val) in generate_typed_value()) {\n            let mut bsatn = &*to_vec(&val).unwrap();\n            let de = Deserializer::new(&mut bsatn);\n            prop_assert!(eq_bsatn(&val, de));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/bsatn/ser.rs",
    "content": "use crate::buffer::BufWriter;\nuse crate::de::DeserializeSeed;\nuse crate::ser::{self, Error, ForwardNamedToSeqProduct, SerializeArray, SerializeSeqProduct};\nuse crate::{i256, u256};\nuse crate::{AlgebraicValue, WithTypespace};\nuse core::fmt;\n\n/// Defines the BSATN serialization data format.\npub struct Serializer<'a, W> {\n    writer: &'a mut W,\n}\n\nimpl<'a, W> Serializer<'a, W> {\n    /// Returns a serializer using the given `writer`.\n    pub fn new(writer: &'a mut W) -> Self {\n        Self { writer }\n    }\n\n    /// Reborrows the serializer.\n    #[inline]\n    fn reborrow(&mut self) -> Serializer<'_, W> {\n        Serializer { writer: self.writer }\n    }\n}\n\nimpl<W: BufWriter> Serializer<'_, W> {\n    /// Directly write `bytes` to the writer.\n    /// This is a raw API. Only use this if you know what you are doing.\n    #[inline(always)]\n    #[doc(hidden)]\n    pub fn raw_write_bytes(self, bytes: &[u8]) {\n        self.writer.put_slice(bytes);\n    }\n}\n\n/// An error during BSATN serialization.\n#[derive(Debug, Clone)]\n// TODO: rename to EncodeError\npub struct BsatnError {\n    /// The error message for the BSATN error.\n    custom: String,\n}\n\nimpl fmt::Display for BsatnError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(&self.custom)\n    }\n}\nimpl std::error::Error for BsatnError {}\n\nimpl Error for BsatnError {\n    fn custom<T: fmt::Display>(msg: T) -> Self {\n        let custom = msg.to_string();\n        Self { custom }\n    }\n}\n\n/// Writes `len` converted to a `u32` to `writer`.\n///\n/// Errors if `len` would not fit in a `u32`.\nfn put_len(writer: &mut impl BufWriter, len: usize) -> Result<(), BsatnError> {\n    let len = len.try_into().map_err(|_| BsatnError::custom(\"len too long\"))?;\n    writer.put_u32(len);\n    Ok(())\n}\n\nimpl<W: BufWriter> ser::Serializer for Serializer<'_, W> {\n    type Ok = ();\n    type Error = BsatnError;\n    type SerializeArray = Self;\n    type SerializeSeqProduct = Self;\n    type SerializeNamedProduct = ForwardNamedToSeqProduct<Self>;\n\n    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u8(v as u8);\n        Ok(())\n    }\n    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u8(v);\n        Ok(())\n    }\n    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u16(v);\n        Ok(())\n    }\n    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u32(v);\n        Ok(())\n    }\n    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u64(v);\n        Ok(())\n    }\n    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u128(v);\n        Ok(())\n    }\n    fn serialize_u256(self, v: u256) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u256(v);\n        Ok(())\n    }\n    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i8(v);\n        Ok(())\n    }\n    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i16(v);\n        Ok(())\n    }\n    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i32(v);\n        Ok(())\n    }\n    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i64(v);\n        Ok(())\n    }\n    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i128(v);\n        Ok(())\n    }\n    fn serialize_i256(self, v: i256) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_i256(v);\n        Ok(())\n    }\n    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u32(v.to_bits());\n        Ok(())\n    }\n    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u64(v.to_bits());\n        Ok(())\n    }\n    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {\n        self.serialize_bytes(v.as_bytes())\n    }\n    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {\n        put_len(self.writer, v.len())?; // N.B. `v.len() > u32::MAX` isn't allowed.\n        self.writer.put_slice(v);\n        Ok(())\n    }\n    fn serialize_array(self, len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        put_len(self.writer, len)?; // N.B. `len > u32::MAX` isn't allowed.\n        Ok(self)\n    }\n    fn serialize_seq_product(self, _len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        Ok(self)\n    }\n    fn serialize_named_product(self, len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        // Serialize named like unnamed.\n        self.serialize_seq_product(len).map(ForwardNamedToSeqProduct::new)\n    }\n    fn serialize_variant<T: super::Serialize + ?Sized>(\n        self,\n        tag: u8,\n        _name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        self.writer.put_u8(tag);\n        value.serialize(self)\n    }\n\n    unsafe fn serialize_bsatn<Ty>(self, ty: &Ty, bsatn: &[u8]) -> Result<Self::Ok, Self::Error>\n    where\n        for<'a, 'de> WithTypespace<'a, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        debug_assert!(crate::bsatn::decode(ty, &mut { bsatn }).is_ok());\n        self.writer.put_slice(bsatn);\n        Ok(())\n    }\n\n    unsafe fn serialize_bsatn_in_chunks<'a, Ty, I: Clone + Iterator<Item = &'a [u8]>>(\n        self,\n        ty: &Ty,\n        total_bsatn_len: usize,\n        bsatn: I,\n    ) -> Result<Self::Ok, Self::Error>\n    where\n        for<'b, 'de> WithTypespace<'b, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        debug_assert!(total_bsatn_len <= isize::MAX as usize);\n        debug_assert!(crate::bsatn::decode(ty, &mut &*concat_bytes_slow(total_bsatn_len, bsatn.clone())).is_ok());\n\n        for chunk in bsatn {\n            self.writer.put_slice(chunk);\n        }\n        Ok(())\n    }\n\n    unsafe fn serialize_str_in_chunks<'a, I: Clone + Iterator<Item = &'a [u8]>>(\n        self,\n        total_len: usize,\n        string: I,\n    ) -> Result<Self::Ok, Self::Error> {\n        debug_assert!(total_len <= isize::MAX as usize);\n        debug_assert!(String::from_utf8(concat_bytes_slow(total_len, string.clone())).is_ok());\n\n        // We need to len-prefix to make this BSATN.\n        put_len(self.writer, total_len)?;\n\n        for chunk in string {\n            self.writer.put_slice(chunk);\n        }\n        Ok(())\n    }\n}\n\n/// Concatenates `chunks` into a `Vec<u8>`.\nfn concat_bytes_slow<'a>(cap: usize, chunks: impl Iterator<Item = &'a [u8]>) -> Vec<u8> {\n    let mut bytes = Vec::with_capacity(cap);\n    for chunk in chunks {\n        bytes.extend(chunk);\n    }\n    assert_eq!(bytes.len(), cap);\n    bytes\n}\n\nimpl<W: BufWriter> SerializeArray for Serializer<'_, W> {\n    type Ok = ();\n    type Error = BsatnError;\n\n    fn serialize_element<T: super::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        elem.serialize(self.reborrow())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(())\n    }\n}\n\nimpl<W: BufWriter> SerializeSeqProduct for Serializer<'_, W> {\n    type Ok = ();\n    type Error = BsatnError;\n\n    fn serialize_element<T: super::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        elem.serialize(self.reborrow())\n    }\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/bsatn.rs",
    "content": "use core::mem::MaybeUninit;\n\nuse crate::buffer::{BufReader, BufWriter, CountWriter};\nuse crate::de::{BasicSmallVecVisitor, Deserialize, DeserializeSeed, Deserializer as _};\nuse crate::ser::Serialize;\nuse crate::{ProductValue, Typespace, WithTypespace};\nuse bytes::BytesMut;\nuse ser::BsatnError;\nuse smallvec::SmallVec;\n\npub mod de;\npub mod eq;\npub mod ser;\n\npub use de::Deserializer;\npub use ser::Serializer;\n\npub use crate::buffer::DecodeError;\npub use ser::BsatnError as EncodeError;\n\n/// Serialize `value` into the buffered writer `w` in the BSATN format.\n#[inline]\npub fn to_writer<W: BufWriter, T: Serialize + ?Sized>(w: &mut W, value: &T) -> Result<(), EncodeError> {\n    value.serialize_into_bsatn(Serializer::new(w))\n}\n\n/// Serialize `value` into a `Vec<u8>` in the BSATN format.\npub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, EncodeError> {\n    let mut v = Vec::new();\n    to_writer(&mut v, value)?;\n    Ok(v)\n}\n\n/// Computes the size of `val` when BSATN encoding without actually encoding.\npub fn to_len<T: Serialize + ?Sized>(value: &T) -> Result<usize, EncodeError> {\n    let mut writer = CountWriter::default();\n    to_writer(&mut writer, value)?;\n    Ok(writer.finish())\n}\n\n/// Deserialize a `T` from the BSATN format in the buffered `reader`.\npub fn from_reader<'de, T: Deserialize<'de>>(reader: &mut impl BufReader<'de>) -> Result<T, DecodeError> {\n    T::deserialize(Deserializer::new(reader))\n}\n\n/// Deserialize a `T` from the BSATN format in `bytes`.\npub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, DecodeError> {\n    from_reader(&mut &*bytes)\n}\n\n/// Decode `bytes` to the value type of `ty: S`.\npub fn decode<'a, 'de, S: ?Sized>(\n    ty: &'a S,\n    bytes: &mut impl BufReader<'de>,\n) -> Result<<WithTypespace<'a, S> as DeserializeSeed<'de>>::Output, DecodeError>\nwhere\n    WithTypespace<'a, S>: DeserializeSeed<'de>,\n{\n    crate::WithTypespace::empty(ty).deserialize(Deserializer::new(bytes))\n}\n\nmacro_rules! codec_funcs {\n    ($ty:ty) => {\n        impl $ty {\n            pub fn decode<'a>(bytes: &mut impl BufReader<'a>) -> Result<Self, DecodeError> {\n                from_reader(bytes)\n            }\n\n            pub fn encode(&self, bytes: &mut impl BufWriter) {\n                to_writer(bytes, self).unwrap()\n            }\n        }\n    };\n    (val: $ty:ty) => {\n        impl $ty {\n            /// Decode a value from `bytes` typed at `ty`.\n            pub fn decode<'a>(\n                ty: &<Self as crate::Value>::Type,\n                bytes: &mut impl BufReader<'a>,\n            ) -> Result<Self, DecodeError> {\n                decode(ty, bytes)\n            }\n\n            /// Decode a vector of values from `bytes` with each value typed at `ty`.\n            pub fn decode_smallvec<'a>(\n                ty: &<Self as crate::Value>::Type,\n                bytes: &mut impl BufReader<'a>,\n            ) -> Result<SmallVec<[Self; 1]>, DecodeError> {\n                Deserializer::new(bytes).deserialize_array_seed(\n                    BasicSmallVecVisitor,\n                    crate::WithTypespace::new(&Typespace::new(Vec::new()), ty),\n                )\n            }\n\n            pub fn encode(&self, bytes: &mut impl BufWriter) {\n                to_writer(bytes, self).unwrap()\n            }\n        }\n    };\n}\n\ncodec_funcs!(crate::AlgebraicType);\ncodec_funcs!(crate::ProductType);\ncodec_funcs!(crate::SumType);\ncodec_funcs!(crate::ProductTypeElement);\ncodec_funcs!(crate::SumTypeVariant);\n\ncodec_funcs!(val: crate::AlgebraicValue);\ncodec_funcs!(val: crate::ProductValue);\ncodec_funcs!(val: crate::SumValue);\n\n/// Provides a view over a buffer that can reserve an additional `len` bytes\n/// and then provide those as an uninitialized buffer to write into.\npub trait BufReservedFill {\n    /// Reserves space for `len` in `self` and then runs `fill` to fill it,\n    /// adding `len` to the total length of `self`.\n    ///\n    /// # Safety\n    ///\n    /// `fill` must initialize every byte in the slice.\n    unsafe fn reserve_and_fill(&mut self, len: usize, fill: impl FnOnce(&mut [MaybeUninit<u8>]));\n}\n\nimpl BufReservedFill for Vec<u8> {\n    unsafe fn reserve_and_fill(&mut self, len: usize, fill: impl FnOnce(&mut [MaybeUninit<u8>])) {\n        // Get an uninitialized slice within `self` of `len` bytes.\n        let start = self.len();\n        self.reserve(len);\n        let sink = &mut self.spare_capacity_mut()[..len];\n\n        // Run the filling logic.\n        fill(sink);\n\n        // SAFETY: Caller promised that `sink` was fully initialized,\n        // which entails that we initialized `start .. start + len`,\n        // so now we have initialized up to `start + len`.\n        unsafe { self.set_len(start + len) }\n    }\n}\n\nimpl BufReservedFill for BytesMut {\n    unsafe fn reserve_and_fill(&mut self, len: usize, fill: impl FnOnce(&mut [MaybeUninit<u8>])) {\n        // Get an uninitialized slice within `self` of `len` bytes.\n        let start = self.len();\n        self.reserve(len);\n        let sink = &mut self.spare_capacity_mut()[..len];\n\n        // Run the filling logic.\n        fill(sink);\n\n        // SAFETY: Caller promised that `sink` was fully initialized,\n        // which entails that we initialized `start .. start + len`,\n        // so now we have initialized up to `start + len`.\n        unsafe { self.set_len(start + len) }\n    }\n}\n\n/// Types that can be encoded to BSATN.\n///\n/// Implementations of this trait may be more efficient than directly calling [`to_vec`].\n/// In particular, for `spacetimedb_table::table::RowRef`, this method will use a `StaticLayout` if one is available,\n/// avoiding expensive runtime type dispatch.\npub trait ToBsatn {\n    /// BSATN-encode the row referred to by `self` into a freshly-allocated `Vec<u8>`.\n    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError>;\n\n    /// BSATN-encode the row referred to by `self` into `buf`,\n    /// pushing `self`'s bytes onto the end of `buf`, similar to [`Vec::extend`].\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> Result<(), BsatnError>;\n\n    /// Returns the static size of the type of this object.\n    ///\n    /// When this returns `Some(_)` there is also a `StaticLayout`.\n    fn static_bsatn_size(&self) -> Option<u16>;\n}\n\nimpl<T: ToBsatn> ToBsatn for &T {\n    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {\n        T::to_bsatn_vec(*self)\n    }\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> Result<(), BsatnError> {\n        T::to_bsatn_extend(*self, buf)\n    }\n    fn static_bsatn_size(&self) -> Option<u16> {\n        T::static_bsatn_size(*self)\n    }\n}\n\nimpl ToBsatn for ProductValue {\n    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {\n        to_vec(self)\n    }\n\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> Result<(), BsatnError> {\n        to_writer(buf, self)\n    }\n\n    fn static_bsatn_size(&self) -> Option<u16> {\n        None\n    }\n}\n\nmod private_is_primitive_type {\n    pub trait Sealed {}\n}\n/// A primitive type.\n/// This is purely intended for use in `crates/bindings-macro`.\n///\n/// # Safety\n///\n/// Implementing this guarantees that the type has no padding, recursively.\n#[doc(hidden)]\npub unsafe trait IsPrimitiveType: private_is_primitive_type::Sealed {}\nmacro_rules! is_primitive_type {\n    ($($prim:ty),*) => {\n        $(\n            impl private_is_primitive_type::Sealed for $prim {}\n            // SAFETY:  the type is primitive and has no padding.\n            unsafe impl IsPrimitiveType for $prim {}\n        )*\n    };\n}\nis_primitive_type!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64);\n\n/// Enforces that a type is a primitive.\n/// This is purely intended for use in `crates/bindings-macro`.\npub const fn assert_is_primitive_type<T: IsPrimitiveType>() {}\n\n#[cfg(test)]\nmod tests {\n    use super::{to_vec, DecodeError, Deserializer};\n    use crate::de::DeserializeSeed;\n    use crate::proptest::{generate_algebraic_type, generate_typed_value};\n    use crate::{meta_type::MetaType, AlgebraicType, AlgebraicValue, WithTypespace};\n    use proptest::prelude::*;\n    use proptest::{collection::vec, proptest};\n\n    #[test]\n    fn type_to_binary_equivalent() {\n        check_type(&AlgebraicType::meta_type());\n    }\n\n    #[track_caller]\n    fn check_type(ty: &AlgebraicType) {\n        let mut through_value = Vec::new();\n        ty.as_value().encode(&mut through_value);\n        let mut direct = Vec::new();\n        ty.encode(&mut direct);\n        assert_eq!(direct, through_value);\n    }\n\n    fn type_non_empty(ty: &AlgebraicType) -> bool {\n        match ty {\n            AlgebraicType::Ref(_) => unreachable!(),\n            AlgebraicType::Array(elem_ty) => type_non_empty(&elem_ty.elem_ty),\n            AlgebraicType::Product(elems) => elems.iter().any(|e| type_non_empty(&e.algebraic_type)),\n            AlgebraicType::Sum(vars) => !vars.is_empty(),\n            _ => true,\n        }\n    }\n\n    proptest! {\n        #[test]\n        fn bsatn_enc_de_roundtrips((ty, val) in generate_typed_value()) {\n            let bytes = to_vec(&val).unwrap();\n            prop_assert_eq!(WithTypespace::empty(&ty).validate(Deserializer::new(&mut &bytes[..])), Ok(()));\n            let val_decoded = AlgebraicValue::decode(&ty, &mut &bytes[..]).unwrap();\n            prop_assert_eq!(val, val_decoded);\n        }\n\n        #[test]\n        fn bsatn_invalid_wont_decode(ty in generate_algebraic_type(), bytes in vec(any::<u8>(), 0..4096)) {\n            prop_assume!(type_non_empty(&ty));\n            prop_assume!(WithTypespace::empty(&ty).validate(Deserializer::new(&mut &bytes[..])).is_err());\n            prop_assert!(AlgebraicValue::decode(&ty, &mut &bytes[..]).is_err());\n        }\n\n        #[test]\n        fn bsatn_non_zero_one_u8_aint_bool(val in 2u8..) {\n            let bytes = [val];\n            prop_assert_eq!(\n                AlgebraicValue::decode(&AlgebraicType::Bool, &mut &bytes[..]),\n                Err(DecodeError::InvalidBool(val))\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/buffer.rs",
    "content": "//! Minimal utility for reading & writing the types we need to internal storage,\n//! without relying on types in third party libraries like `bytes::Bytes`, etc.\n//! Meant to be kept slim and trim for use across both native and WASM.\n\nuse crate::{i256, u256};\nuse bytes::{BufMut, BytesMut};\nuse core::cell::Cell;\nuse core::fmt;\nuse core::str::Utf8Error;\n\n/// An error that occurred when decoding.\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]\npub enum DecodeError {\n    /// Not enough data was provided in the input.\n    BufferLength {\n        for_type: &'static str,\n        expected: usize,\n        given: usize,\n    },\n    /// Length did not match the statically expected length.\n    InvalidLen { expected: usize, given: usize },\n    /// The tag does not exist for the sum.\n    InvalidTag { tag: u8, sum_name: Option<String> },\n    /// Expected data to be UTF-8 but it wasn't.\n    InvalidUtf8,\n    /// Expected the byte to be 0 or 1 to be a valid bool.\n    InvalidBool(u8),\n    /// Allocation of `size` elements failed.\n    AllocationFailed(usize),\n    /// Custom error not in the other variants of `DecodeError`.\n    Other(String),\n}\n\npub type DecodeResult<T> = Result<T, DecodeError>;\n\nimpl fmt::Display for DecodeError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::BufferLength {\n                for_type,\n                expected,\n                given,\n            } => write!(f, \"data too short for {for_type}: Expected {expected}, given {given}\"),\n            Self::InvalidLen { expected, given } => {\n                write!(f, \"unexpected data length: Expected {expected}, given {given}\")\n            }\n            Self::InvalidTag { tag, sum_name } => {\n                write!(\n                    f,\n                    \"unknown tag {tag:#x} for sum type {}\",\n                    sum_name.as_deref().unwrap_or(\"<unknown>\")\n                )\n            }\n            Self::InvalidUtf8 => f.write_str(\"invalid utf8\"),\n            Self::InvalidBool(byte) => write!(f, \"byte {byte} not valid as `bool` (must be 0 or 1)\"),\n            Self::AllocationFailed(size) => write!(f, \"allocation of {size} elements failed\"),\n            Self::Other(err) => f.write_str(err),\n        }\n    }\n}\nimpl From<DecodeError> for String {\n    fn from(err: DecodeError) -> Self {\n        err.to_string()\n    }\n}\nimpl std::error::Error for DecodeError {}\n\nimpl From<Utf8Error> for DecodeError {\n    fn from(_: Utf8Error) -> Self {\n        DecodeError::InvalidUtf8\n    }\n}\n\n/// A buffered writer of some kind.\npub trait BufWriter {\n    /// Writes the `slice` to the buffer.\n    ///\n    /// This is the only method implementations are required to define.\n    /// All other methods are provided.\n    fn put_slice(&mut self, slice: &[u8]);\n\n    /// Writes a `u8` to the buffer in little-endian (LE) encoding.\n    fn put_u8(&mut self, val: u8) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes a `u16` to the buffer in little-endian (LE) encoding.\n    fn put_u16(&mut self, val: u16) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes a `u32` to the buffer in little-endian (LE) encoding.\n    fn put_u32(&mut self, val: u32) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes a `u64` to the buffer in little-endian (LE) encoding.\n    fn put_u64(&mut self, val: u64) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes a `u128` to the buffer in little-endian (LE) encoding.\n    fn put_u128(&mut self, val: u128) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes a `u256` to the buffer in little-endian (LE) encoding.\n    fn put_u256(&mut self, val: u256) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i8` to the buffer in little-endian (LE) encoding.\n    fn put_i8(&mut self, val: i8) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i16` to the buffer in little-endian (LE) encoding.\n    fn put_i16(&mut self, val: i16) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i32` to the buffer in little-endian (LE) encoding.\n    fn put_i32(&mut self, val: i32) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i64` to the buffer in little-endian (LE) encoding.\n    fn put_i64(&mut self, val: i64) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i128` to the buffer in little-endian (LE) encoding.\n    fn put_i128(&mut self, val: i128) {\n        self.put_slice(&val.to_le_bytes())\n    }\n\n    /// Writes an `i256` to the buffer in little-endian (LE) encoding.\n    fn put_i256(&mut self, val: i256) {\n        self.put_slice(&val.to_le_bytes())\n    }\n}\n\nmacro_rules! get_int {\n    ($self:ident, $int:ident) => {\n        match $self.get_array_chunk() {\n            Some(&arr) => Ok($int::from_le_bytes(arr)),\n            None => Err(DecodeError::BufferLength {\n                for_type: stringify!($int),\n                expected: std::mem::size_of::<$int>(),\n                given: $self.remaining(),\n            }),\n        }\n    };\n}\n\n/// A buffered reader of some kind.\n///\n/// The lifetime `'de` allows the output of deserialization to borrow from the input.\npub trait BufReader<'de> {\n    /// Reads and returns a chunk of `.len() = size` advancing the cursor iff `self.remaining() >= size`.\n    fn get_chunk(&mut self, size: usize) -> Option<&'de [u8]>;\n\n    /// Returns the number of bytes left to read in the input.\n    fn remaining(&self) -> usize;\n\n    /// Reads and returns a chunk of `.len() = N` as an array, advancing the cursor.\n    #[inline]\n    fn get_array_chunk<const N: usize>(&mut self) -> Option<&'de [u8; N]> {\n        self.get_chunk(N)?.try_into().ok()\n    }\n\n    /// Reads and returns a byte slice of `.len() = size` advancing the cursor.\n    #[inline]\n    fn get_slice(&mut self, size: usize) -> Result<&'de [u8], DecodeError> {\n        self.get_chunk(size).ok_or_else(|| DecodeError::BufferLength {\n            for_type: \"[u8]\",\n            expected: size,\n            given: self.remaining(),\n        })\n    }\n\n    /// Reads an array of type `[u8; N]` from the input.\n    #[inline]\n    fn get_array<const N: usize>(&mut self) -> Result<&'de [u8; N], DecodeError> {\n        self.get_array_chunk().ok_or_else(|| DecodeError::BufferLength {\n            for_type: \"[u8; _]\",\n            expected: N,\n            given: self.remaining(),\n        })\n    }\n\n    /// Reads a `u8` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u8(&mut self) -> Result<u8, DecodeError> {\n        get_int!(self, u8)\n    }\n\n    /// Reads a `u16` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u16(&mut self) -> Result<u16, DecodeError> {\n        get_int!(self, u16)\n    }\n\n    /// Reads a `u32` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u32(&mut self) -> Result<u32, DecodeError> {\n        get_int!(self, u32)\n    }\n\n    /// Reads a `u64` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u64(&mut self) -> Result<u64, DecodeError> {\n        get_int!(self, u64)\n    }\n\n    /// Reads a `u128` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u128(&mut self) -> Result<u128, DecodeError> {\n        get_int!(self, u128)\n    }\n\n    /// Reads a `u256` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_u256(&mut self) -> Result<u256, DecodeError> {\n        get_int!(self, u256)\n    }\n\n    /// Reads an `i8` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i8(&mut self) -> Result<i8, DecodeError> {\n        get_int!(self, i8)\n    }\n\n    /// Reads an `i16` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i16(&mut self) -> Result<i16, DecodeError> {\n        get_int!(self, i16)\n    }\n\n    /// Reads an `i32` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i32(&mut self) -> Result<i32, DecodeError> {\n        get_int!(self, i32)\n    }\n\n    /// Reads an `i64` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i64(&mut self) -> Result<i64, DecodeError> {\n        get_int!(self, i64)\n    }\n\n    /// Reads an `i128` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i128(&mut self) -> Result<i128, DecodeError> {\n        get_int!(self, i128)\n    }\n\n    /// Reads an `i256` in little endian (LE) encoding from the input.\n    ///\n    /// This method is provided for convenience\n    /// and is derived from [`get_chunk`](BufReader::get_chunk)'s definition.\n    #[inline]\n    fn get_i256(&mut self) -> Result<i256, DecodeError> {\n        get_int!(self, i256)\n    }\n}\n\nimpl BufWriter for Vec<u8> {\n    fn put_slice(&mut self, slice: &[u8]) {\n        self.extend_from_slice(slice);\n    }\n}\n\nimpl BufWriter for &mut [u8] {\n    fn put_slice(&mut self, slice: &[u8]) {\n        if self.len() < slice.len() {\n            panic!(\"not enough buffer space\")\n        }\n        let (buf, rest) = std::mem::take(self).split_at_mut(slice.len());\n        buf.copy_from_slice(slice);\n        *self = rest;\n    }\n}\n\nimpl BufWriter for BytesMut {\n    fn put_slice(&mut self, slice: &[u8]) {\n        BufMut::put_slice(self, slice);\n    }\n}\n\n/// A [`BufWriter`] that only counts the bytes.\n#[derive(Default)]\npub struct CountWriter {\n    /// The number of bytes counted thus far.\n    num_bytes: usize,\n}\n\nimpl CountWriter {\n    /// Consumes the counter and returns the final count.\n    pub fn finish(self) -> usize {\n        self.num_bytes\n    }\n}\n\nimpl BufWriter for CountWriter {\n    fn put_slice(&mut self, slice: &[u8]) {\n        self.num_bytes += slice.len();\n    }\n}\n\n/// A [`BufWriter`] that writes the bytes to two writers `W1` and `W2`.\npub struct TeeWriter<W1, W2> {\n    pub w1: W1,\n    pub w2: W2,\n}\n\nimpl<W1: BufWriter, W2: BufWriter> TeeWriter<W1, W2> {\n    pub fn new(w1: W1, w2: W2) -> Self {\n        Self { w1, w2 }\n    }\n}\n\nimpl<W1: BufWriter, W2: BufWriter> BufWriter for TeeWriter<W1, W2> {\n    fn put_slice(&mut self, slice: &[u8]) {\n        self.w1.put_slice(slice);\n        self.w2.put_slice(slice);\n    }\n}\n\nimpl<'de> BufReader<'de> for &'de [u8] {\n    #[inline]\n    fn get_chunk(&mut self, size: usize) -> Option<&'de [u8]> {\n        let (ret, rest) = self.split_at_checked(size)?;\n        *self = rest;\n        Some(ret)\n    }\n\n    #[inline]\n    fn get_array_chunk<const N: usize>(&mut self) -> Option<&'de [u8; N]> {\n        let (ret, rest) = self.split_first_chunk()?;\n        *self = rest;\n        Some(ret)\n    }\n\n    #[inline(always)]\n    fn remaining(&self) -> usize {\n        self.len()\n    }\n}\n\n/// A cursor based [`BufReader<'de>`] implementation.\n#[derive(Debug)]\npub struct Cursor<I> {\n    /// The underlying input read from.\n    pub buf: I,\n    /// The position within the reader.\n    pub pos: Cell<usize>,\n}\n\nimpl<I> Cursor<I> {\n    /// Returns a new cursor on the provided `buf` input.\n    ///\n    /// The cursor starts at the beginning of `buf`.\n    pub fn new(buf: I) -> Self {\n        Self { buf, pos: 0.into() }\n    }\n}\n\nimpl<'de, I: AsRef<[u8]>> BufReader<'de> for &'de Cursor<I> {\n    #[inline]\n    fn get_chunk(&mut self, size: usize) -> Option<&'de [u8]> {\n        // \"Read\" the slice `buf[pos..size]`.\n        let buf = &self.buf.as_ref()[self.pos.get()..];\n        let ret = buf.get(..size)?;\n\n        // Advance the cursor by `size` bytes.\n        self.pos.set(self.pos.get() + size);\n\n        Some(ret)\n    }\n\n    #[inline]\n    fn get_array_chunk<const N: usize>(&mut self) -> Option<&'de [u8; N]> {\n        // \"Read\" the slice `buf[pos..size]`.\n        let buf = &self.buf.as_ref()[self.pos.get()..];\n        let ret = buf.first_chunk()?;\n\n        // Advance the cursor by `size` bytes.\n        self.pos.set(self.pos.get() + N);\n\n        Some(ret)\n    }\n\n    fn remaining(&self) -> usize {\n        self.buf.as_ref().len() - self.pos.get()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::buffer::{BufReader, BufWriter};\n\n    #[test]\n    fn test_simple_encode_decode() {\n        let mut writer: Vec<u8> = vec![];\n        writer.put_u8(5);\n        writer.put_u32(6);\n        writer.put_u64(7);\n\n        let arr_val = [1, 2, 3, 4];\n        writer.put_slice(&arr_val[..]);\n\n        let mut reader = writer.as_slice();\n        assert_eq!(reader.get_u8().unwrap(), 5);\n        assert_eq!(reader.get_u32().unwrap(), 6);\n        assert_eq!(reader.get_u64().unwrap(), 7);\n\n        let slice = reader.get_slice(4).unwrap();\n        assert_eq!(slice, arr_val);\n\n        // reading beyond buffer end should fail\n        assert!(reader.get_slice(1).is_err());\n        assert!(reader.get_slice(123).is_err());\n        assert!(reader.get_u64().is_err());\n        assert!(reader.get_u32().is_err());\n        assert!(reader.get_u8().is_err());\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/convert.rs",
    "content": "use crate::sum_value::SumTag;\nuse crate::{i256, u256};\nuse crate::{AlgebraicType, AlgebraicValue, ProductType, ProductValue};\nuse spacetimedb_primitives::{ArgId, ColId, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewId};\n\nimpl crate::Value for AlgebraicValue {\n    type Type = AlgebraicType;\n}\n\nimpl<X: Into<Box<[AlgebraicValue]>>> From<X> for ProductValue {\n    fn from(elements: X) -> Self {\n        let elements = elements.into();\n        ProductValue { elements }\n    }\n}\n\nimpl From<AlgebraicValue> for ProductValue {\n    fn from(x: AlgebraicValue) -> Self {\n        [x].into()\n    }\n}\n\nimpl From<AlgebraicType> for ProductType {\n    fn from(x: AlgebraicType) -> Self {\n        Self::new([x.into()].into())\n    }\n}\n\nimpl From<()> for AlgebraicValue {\n    fn from((): ()) -> Self {\n        AlgebraicValue::unit()\n    }\n}\n\nmacro_rules! built_in_into {\n    ($native:ty, $kind:ident) => {\n        impl From<$native> for AlgebraicValue {\n            fn from(x: $native) -> Self {\n                Self::$kind(x.into())\n            }\n        }\n    };\n}\n\nbuilt_in_into!(u128, U128);\nbuilt_in_into!(i128, I128);\nbuilt_in_into!(u256, U256);\nbuilt_in_into!(i256, I256);\nbuilt_in_into!(f32, F32);\nbuilt_in_into!(f64, F64);\nbuilt_in_into!(&str, String);\nbuilt_in_into!(String, String);\nbuilt_in_into!(&[u8], Bytes);\nbuilt_in_into!(Box<[u8]>, Bytes);\nbuilt_in_into!(SumTag, Sum);\n\nmacro_rules! system_id {\n    ($name:ident) => {\n        impl From<$name> for AlgebraicValue {\n            fn from(value: $name) -> Self {\n                value.0.into()\n            }\n        }\n    };\n}\nsystem_id!(ArgId);\nsystem_id!(TableId);\nsystem_id!(ViewId);\nsystem_id!(ColId);\nsystem_id!(SequenceId);\nsystem_id!(IndexId);\nsystem_id!(ConstraintId);\nsystem_id!(ScheduleId);\n"
  },
  {
    "path": "crates/sats/src/de/impls.rs",
    "content": "use super::{\n    BasicSmallVecVisitor, BasicVecVisitor, Deserialize, DeserializeSeed, Deserializer, Error, FieldNameVisitor,\n    ProductKind, ProductVisitor, SeqProductAccess, SliceVisitor, SumAccess, SumVisitor, VariantAccess, VariantVisitor,\n};\nuse crate::{\n    de::{array_validate, array_visit, ArrayAccess, ArrayVisitor, BasicArrayVisitor, GrowingVec},\n    AlgebraicType, AlgebraicValue, ArrayType, ArrayValue, ProductType, ProductTypeElement, ProductValue, SumType,\n    SumValue, WithTypespace, F32, F64,\n};\nuse crate::{i256, u256};\nuse core::{iter, marker::PhantomData, ops::Bound};\nuse lean_string::LeanString;\nuse smallvec::SmallVec;\nuse spacetimedb_primitives::{ColId, ColList};\nuse std::{borrow::Cow, rc::Rc, sync::Arc};\n\n/// Implements [`Deserialize`] for a type in a simplified manner.\n///\n/// An example:\n/// ```ignore\n/// impl_deserialize!(\n/// //     Type parameters  Optional where  Impl type\n/// //            v               v             v\n/// //   ----------------  --------------- ----------\n///     [T: Deserialize<'de>] where [T: Copy] std::rc::Rc<T>,\n/// //  The `deserialize` implementation where `de` is the `Deserializer<'de>`\n/// //  and the expression right of `=>` is the body of `deserialize`.\n///     de => T::deserialize(de).map(std::rc::Rc::new)\n/// );\n/// ```\n#[macro_export]\nmacro_rules! impl_deserialize {\n    (\n        [$($generics:tt)*] $(where [$($wc:tt)*])? $typ:ty,\n        $de:ident => $body:expr\n        $(, $validate_de:ident => $validate:expr)?\n    ) => {\n        impl<'de, $($generics)*> $crate::de::Deserialize<'de> for $typ {\n            fn deserialize<D: $crate::de::Deserializer<'de>>($de: D) -> Result<Self, D::Error> { $body }\n            $(\n                fn validate<D: $crate::de::Deserializer<'de>>($validate_de: D) -> Result<(), D::Error> { $validate }\n            )?\n        }\n    };\n}\n\n/// Implements [`Deserialize`] for a primitive type.\n///\n/// The `$method` is a parameterless method on `deserializer` to call.\nmacro_rules! impl_prim {\n    ($(($prim:ty, $method:ident))*) => {\n        $(impl_deserialize!([] $prim, de => de.$method());)*\n    };\n}\n\nimpl_prim! {\n    (bool, deserialize_bool)\n    /*(u8, deserialize_u8)*/ (u16, deserialize_u16) (u32, deserialize_u32) (u64, deserialize_u64) (u128, deserialize_u128) (u256, deserialize_u256)\n    (i8, deserialize_i8)     (i16, deserialize_i16) (i32, deserialize_i32) (i64, deserialize_i64) (i128, deserialize_i128) (i256, deserialize_i256)\n    (f32, deserialize_f32) (f64, deserialize_f64)\n}\n\nstruct TupleVisitor<A>(PhantomData<A>);\n#[derive(Copy, Clone)]\nstruct TupleNameVisitorMax(usize);\n\nimpl FieldNameVisitor<'_> for TupleNameVisitorMax {\n    // The index of the field name.\n    type Output = usize;\n\n    fn field_names(&self) -> impl '_ + Iterator<Item = Option<&str>> {\n        iter::repeat_n(None, self.0)\n    }\n\n    fn kind(&self) -> ProductKind {\n        ProductKind::Normal\n    }\n\n    fn visit<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        let err = || Error::unknown_field_name(name, &self);\n        // Convert `name` to an index.\n        let Ok(index) = name.parse() else {\n            return Err(err());\n        };\n        // Confirm that the index exists or error.\n        if index < self.0 {\n            Ok(index)\n        } else {\n            Err(err())\n        }\n    }\n\n    fn visit_seq(self, index: usize) -> Self::Output {\n        // Assert that the index exists.\n        assert!(index < self.0);\n        index\n    }\n}\n\nmacro_rules! impl_deserialize_tuple {\n    ($($ty_name:ident => $const_val:literal),*) => {\n        impl<'de, $($ty_name: Deserialize<'de>),*> ProductVisitor<'de> for TupleVisitor<($($ty_name,)*)> {\n            type Output = ($($ty_name,)*);\n            fn product_name(&self) -> Option<&str> { None }\n            fn product_len(&self) -> usize { crate::count!($($ty_name)*) }\n            fn visit_seq_product<A: SeqProductAccess<'de>>(self, mut _prod: A) -> Result<Self::Output, A::Error> {\n                $(\n                    #[allow(non_snake_case)]\n                    let $ty_name = _prod\n                        .next_element()?\n                        .ok_or_else(|| Error::invalid_product_length($const_val, &self))?;\n                )*\n\n                Ok(($($ty_name,)*))\n            }\n            fn validate_seq_product<A: SeqProductAccess<'de>>(self, mut _prod: A) -> Result<(), A::Error> {\n                $(\n                    #[allow(non_snake_case)]\n                    _prod\n                        .validate_next_element_seed(PhantomData::<$ty_name>)?\n                        .ok_or_else(|| Error::invalid_product_length($const_val, &self))?;\n                )*\n\n                Ok(())\n            }\n            fn visit_named_product<A: super::NamedProductAccess<'de>>(self, mut prod: A) -> Result<Self::Output, A::Error> {\n                $(\n                    #[allow(non_snake_case)]\n                    let mut $ty_name = None;\n                )*\n\n                let visit = TupleNameVisitorMax(self.product_len());\n                while let Some(index) = prod.get_field_ident(visit)? {\n                    match index {\n                        $($const_val => {\n                            if $ty_name.is_some() {\n                                return Err(A::Error::duplicate_field($const_val, None, &self))\n                            }\n                            $ty_name = Some(prod.get_field_value()?);\n                        })*\n                        index => return Err(Error::invalid_product_length(index, &self)),\n                    }\n                }\n                Ok(($(\n                    $ty_name.ok_or_else(|| A::Error::missing_field($const_val, None, &self))?,\n                )*))\n            }\n            fn validate_named_product<A: super::NamedProductAccess<'de>>(self, mut prod: A) -> Result<(), A::Error> {\n                $(\n                    #[allow(non_snake_case)]\n                    let mut $ty_name = false;\n                )*\n\n                let visit = TupleNameVisitorMax(self.product_len());\n                while let Some(index) = prod.get_field_ident(visit)? {\n                    match index {\n                        $($const_val => {\n                            if $ty_name {\n                                return Err(A::Error::duplicate_field($const_val, None, &self))\n                            }\n                            prod.validate_field_value::<$ty_name>()?;\n                            $ty_name = true;\n                        })*\n                        index => return Err(Error::invalid_product_length(index, &self)),\n                    }\n                }\n\n                $(\n                    if !$ty_name {\n                        return Err(A::Error::missing_field($const_val, None, &self))\n                    }\n                )*\n\n                Ok(())\n            }\n        }\n\n        impl_deserialize!([$($ty_name: Deserialize<'de>),*] ($($ty_name,)*), de => {\n            de.deserialize_product(TupleVisitor::<($($ty_name,)*)>(PhantomData))\n        });\n    };\n}\n\nimpl_deserialize_tuple!();\nimpl_deserialize_tuple!(T0 => 0);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, T9 => 9);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, T9 => 9, T10 => 10);\nimpl_deserialize_tuple!(T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, T9 => 9, T10 => 10, T11 => 11);\n\nimpl<'de> Deserialize<'de> for u8 {\n    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        deserializer.deserialize_u8()\n    }\n\n    // Specialize `Vec<u8>` deserialization.\n    // This is more likely to compile down to a `memcpy`.\n    fn __deserialize_vec<D: Deserializer<'de>>(deserializer: D) -> Result<Vec<Self>, D::Error> {\n        deserializer.deserialize_bytes(OwnedSliceVisitor)\n    }\n\n    fn __deserialize_array<D: Deserializer<'de>, const N: usize>(deserializer: D) -> Result<[Self; N], D::Error> {\n        deserializer.deserialize_bytes(ByteArrayVisitor)\n    }\n}\n\nimpl_deserialize!([] F32, de => f32::deserialize(de).map(Into::into));\nimpl_deserialize!([] F64, de => f64::deserialize(de).map(Into::into));\nimpl_deserialize!(\n    [] String,\n    de => de.deserialize_str(OwnedSliceVisitor),\n    de => <&str>::validate(de)\n);\nimpl_deserialize!(\n    [] LeanString,\n    de => <Cow<'_, str>>::deserialize(de).map(|s| (&*s).into()),\n    de => <&str>::validate(de)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>] Vec<T>,\n    de => T::__deserialize_vec(de),\n    de => de.validate_array_seed(BasicVecVisitor, PhantomData::<T>)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>, const N: usize] SmallVec<[T; N]>,\n    de => de.deserialize_array(BasicSmallVecVisitor),\n    de => de.validate_array_seed(BasicVecVisitor, PhantomData::<T>)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>, const N: usize] [T; N],\n    de => T::__deserialize_array(de),\n    de => de.validate_array_seed(BasicArrayVisitor::<N>, PhantomData::<T>)\n);\nimpl_deserialize!(\n    [] Box<str>,\n    de => String::deserialize(de).map(|s| s.into_boxed_str()),\n    de => String::validate(de)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>] Box<[T]>,\n    de => Vec::deserialize(de).map(|s| s.into_boxed_slice()),\n    de => Vec::<T>::validate(de)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>] Rc<[T]>,\n    de => Vec::deserialize(de).map(|s| s.into()),\n    de => Vec::<T>::validate(de)\n);\nimpl_deserialize!(\n    [T: Deserialize<'de>] Arc<[T]>,\n    de => Vec::deserialize(de).map(|s| s.into()),\n    de => Vec::<T>::validate(de)\n);\n\n/// The visitor converts the slice to its owned version.\nstruct OwnedSliceVisitor;\n\nimpl<T: ToOwned + ?Sized> SliceVisitor<'_, T> for OwnedSliceVisitor {\n    type Output = T::Owned;\n\n    fn visit<E: Error>(self, slice: &T) -> Result<Self::Output, E> {\n        Ok(slice.to_owned())\n    }\n\n    fn visit_owned<E: Error>(self, buf: T::Owned) -> Result<Self::Output, E> {\n        Ok(buf)\n    }\n}\n\n/// The visitor will convert the byte slice to `[u8; N]`.\n///\n/// When `slice.len() != N` an error will be raised.\nstruct ByteArrayVisitor<const N: usize>;\n\nimpl<const N: usize> SliceVisitor<'_, [u8]> for ByteArrayVisitor<N> {\n    type Output = [u8; N];\n\n    fn visit<E: Error>(self, slice: &[u8]) -> Result<Self::Output, E> {\n        slice.try_into().map_err(|_| {\n            Error::custom(if slice.len() > N {\n                \"too many elements for array\"\n            } else {\n                \"too few elements for array\"\n            })\n        })\n    }\n}\n\nimpl_deserialize!([] &'de str, de => de.deserialize_str(BorrowedSliceVisitor));\nimpl_deserialize!([] &'de [u8], de => de.deserialize_bytes(BorrowedSliceVisitor));\n\n/// The visitor returns the slice as-is and borrowed.\npub(crate) struct BorrowedSliceVisitor;\n\nimpl<'de, T: ToOwned + ?Sized + 'de> SliceVisitor<'de, T> for BorrowedSliceVisitor {\n    type Output = &'de T;\n\n    fn visit<E: Error>(self, _: &T) -> Result<Self::Output, E> {\n        Err(E::custom(\"expected *borrowed* slice\"))\n    }\n\n    fn visit_borrowed<E: Error>(self, borrowed_slice: &'de T) -> Result<Self::Output, E> {\n        Ok(borrowed_slice)\n    }\n}\n\nimpl_deserialize!(\n    [] Cow<'de, str>,\n    de => de.deserialize_str(CowSliceVisitor),\n    de => <&str>::validate(de)\n);\nimpl_deserialize!(\n    [] Cow<'de, [u8]>,\n    de => de.deserialize_bytes(CowSliceVisitor),\n    de => <&[u8]>::validate(de)\n);\n\n/// The visitor works with either owned or borrowed versions to produce `Cow<'de, T>`.\nstruct CowSliceVisitor;\n\nimpl<'de, T: ToOwned + ?Sized + 'de> SliceVisitor<'de, T> for CowSliceVisitor {\n    type Output = Cow<'de, T>;\n\n    fn visit<E: Error>(self, slice: &T) -> Result<Self::Output, E> {\n        self.visit_owned(slice.to_owned())\n    }\n\n    fn visit_owned<E: Error>(self, buf: <T as ToOwned>::Owned) -> Result<Self::Output, E> {\n        Ok(Cow::Owned(buf))\n    }\n\n    fn visit_borrowed<E: Error>(self, borrowed_slice: &'de T) -> Result<Self::Output, E> {\n        Ok(Cow::Borrowed(borrowed_slice))\n    }\n}\n\nimpl_deserialize!(\n    [T: Deserialize<'de>] Box<T>,\n    de => T::deserialize(de).map(Box::new),\n    de => T::validate(de)\n);\nimpl_deserialize!([T: Deserialize<'de>] Option<T>, de => de.deserialize_sum(OptionVisitor(PhantomData)));\n\n/// The visitor deserializes an `Option<T>`.\nstruct OptionVisitor<T>(PhantomData<T>);\n\nimpl<'de, T: Deserialize<'de>> SumVisitor<'de> for OptionVisitor<T> {\n    type Output = Option<T>;\n\n    fn sum_name(&self) -> Option<&str> {\n        Some(\"option\")\n    }\n\n    fn is_option(&self) -> bool {\n        true\n    }\n\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {\n        // Determine the variant.\n        let (some, data) = data.variant(self)?;\n\n        // Deserialize contents for it.\n        Ok(if some {\n            Some(data.deserialize()?)\n        } else {\n            data.deserialize::<()>()?;\n            None\n        })\n    }\n\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error> {\n        // Determine the variant.\n        let (some, data) = data.variant(self)?;\n\n        // Validate contents for it.\n        if some {\n            data.validate::<T>()\n        } else {\n            data.validate::<()>()\n        }\n    }\n}\n\nimpl<'de, T: Deserialize<'de>> VariantVisitor<'de> for OptionVisitor<T> {\n    type Output = bool;\n\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n        [\"some\", \"none\"].into_iter()\n    }\n\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {\n        match tag {\n            0 => Ok(true),\n            1 => Ok(false),\n            _ => Err(E::unknown_variant_tag(tag, &self)),\n        }\n    }\n\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        match name {\n            \"some\" => Ok(true),\n            \"none\" => Ok(false),\n            _ => Err(E::unknown_variant_name(name, &self)),\n        }\n    }\n}\n\nimpl_deserialize!([T: Deserialize<'de>, E: Deserialize<'de>] Result<T, E>, de =>\n    de.deserialize_sum(ResultVisitor(PhantomData))\n);\n\n/// Visitor to deserialize a `Result<T, E>`.\nstruct ResultVisitor<T, E>(PhantomData<(T, E)>);\n\n/// Variant determined by the [`VariantVisitor`] for `Result<T, E>`.\nenum ResultVariant {\n    Ok,\n    Err,\n}\n\nimpl<'de, T: Deserialize<'de>, E: Deserialize<'de>> SumVisitor<'de> for ResultVisitor<T, E> {\n    type Output = Result<T, E>;\n\n    fn sum_name(&self) -> Option<&str> {\n        Some(\"result\")\n    }\n\n    fn is_option(&self) -> bool {\n        false\n    }\n\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {\n        let (variant, data) = data.variant(self)?;\n        Ok(match variant {\n            ResultVariant::Ok => Ok(data.deserialize()?),\n            ResultVariant::Err => Err(data.deserialize()?),\n        })\n    }\n\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error> {\n        let (variant, data) = data.variant(self)?;\n        match variant {\n            ResultVariant::Ok => data.validate::<T>(),\n            ResultVariant::Err => data.validate::<E>(),\n        }\n    }\n}\n\nimpl<'de, T: Deserialize<'de>, U: Deserialize<'de>> VariantVisitor<'de> for ResultVisitor<T, U> {\n    type Output = ResultVariant;\n\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n        [\"ok\", \"err\"].into_iter()\n    }\n\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {\n        match tag {\n            0 => Ok(ResultVariant::Ok),\n            1 => Ok(ResultVariant::Err),\n            _ => Err(E::unknown_variant_tag(tag, &self)),\n        }\n    }\n\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        match name {\n            \"ok\" => Ok(ResultVariant::Ok),\n            \"err\" => Ok(ResultVariant::Err),\n            _ => Err(E::unknown_variant_name(name, &self)),\n        }\n    }\n}\n\n/// The visitor deserializes a `Bound<T>`.\n#[derive(Clone, Copy)]\npub struct WithBound<S>(pub S);\n\nimpl<'de, S: Copy + DeserializeSeed<'de>> DeserializeSeed<'de> for WithBound<S> {\n    type Output = Bound<S::Output>;\n\n    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {\n        de.deserialize_sum(BoundVisitor(self.0))\n    }\n}\n\n/// The visitor deserializes a `Bound<T>`.\nstruct BoundVisitor<S>(S);\n\n/// Variant determined by the [`BoundVisitor`] for `Bound<T>`.\nenum BoundVariant {\n    Included,\n    Excluded,\n    Unbounded,\n}\n\nimpl<'de, S: Copy + DeserializeSeed<'de>> SumVisitor<'de> for BoundVisitor<S> {\n    type Output = Bound<S::Output>;\n\n    fn sum_name(&self) -> Option<&str> {\n        Some(\"bound\")\n    }\n\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {\n        // Determine the variant.\n        let this = self.0;\n        let (variant, data) = data.variant(self)?;\n\n        // Deserialize contents for it.\n        match variant {\n            BoundVariant::Included => data.deserialize_seed(this).map(Bound::Included),\n            BoundVariant::Excluded => data.deserialize_seed(this).map(Bound::Excluded),\n            BoundVariant::Unbounded => data.deserialize::<()>().map(|_| Bound::Unbounded),\n        }\n    }\n\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error> {\n        // Determine the variant.\n        let this = self.0;\n        let (variant, data) = data.variant(self)?;\n\n        // Validate contents for it.\n        match variant {\n            BoundVariant::Included | BoundVariant::Excluded => data.validate_seed(this),\n            BoundVariant::Unbounded => data.validate::<()>(),\n        }\n    }\n}\n\nimpl<'de, T: Copy + DeserializeSeed<'de>> VariantVisitor<'de> for BoundVisitor<T> {\n    type Output = BoundVariant;\n\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n        [\"included\", \"excluded\", \"unbounded\"].into_iter()\n    }\n\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {\n        match tag {\n            0 => Ok(BoundVariant::Included),\n            1 => Ok(BoundVariant::Excluded),\n            // if this ever changes, edit crates/bindings/src/table.rs\n            2 => Ok(BoundVariant::Unbounded),\n            _ => Err(E::unknown_variant_tag(tag, &self)),\n        }\n    }\n\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        match name {\n            \"included\" => Ok(BoundVariant::Included),\n            \"excluded\" => Ok(BoundVariant::Excluded),\n            \"unbounded\" => Ok(BoundVariant::Unbounded),\n            _ => Err(E::unknown_variant_name(name, &self)),\n        }\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for WithTypespace<'_, AlgebraicType> {\n    type Output = AlgebraicValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {\n        match self.ty() {\n            AlgebraicType::Ref(r) => self.resolve(*r).deserialize(de),\n            AlgebraicType::Sum(sum) => self.with(sum).deserialize(de).map(Into::into),\n            AlgebraicType::Product(prod) => self.with(prod).deserialize(de).map(Into::into),\n            AlgebraicType::Array(ty) => self.with(ty).deserialize(de).map(Into::into),\n            AlgebraicType::Bool => bool::deserialize(de).map(Into::into),\n            AlgebraicType::I8 => i8::deserialize(de).map(Into::into),\n            AlgebraicType::U8 => u8::deserialize(de).map(Into::into),\n            AlgebraicType::I16 => i16::deserialize(de).map(Into::into),\n            AlgebraicType::U16 => u16::deserialize(de).map(Into::into),\n            AlgebraicType::I32 => i32::deserialize(de).map(Into::into),\n            AlgebraicType::U32 => u32::deserialize(de).map(Into::into),\n            AlgebraicType::I64 => i64::deserialize(de).map(Into::into),\n            AlgebraicType::U64 => u64::deserialize(de).map(Into::into),\n            AlgebraicType::I128 => i128::deserialize(de).map(Into::into),\n            AlgebraicType::U128 => u128::deserialize(de).map(Into::into),\n            AlgebraicType::I256 => i256::deserialize(de).map(Into::into),\n            AlgebraicType::U256 => u256::deserialize(de).map(Into::into),\n            AlgebraicType::F32 => f32::deserialize(de).map(Into::into),\n            AlgebraicType::F64 => f64::deserialize(de).map(Into::into),\n            AlgebraicType::String => <Box<str>>::deserialize(de).map(Into::into),\n        }\n    }\n\n    fn validate<D: Deserializer<'de>>(self, de: D) -> Result<(), D::Error> {\n        match self.ty() {\n            AlgebraicType::Ref(r) => self.resolve(*r).validate(de),\n            AlgebraicType::Sum(sum) => self.with(sum).validate(de),\n            AlgebraicType::Product(prod) => self.with(prod).validate(de),\n            AlgebraicType::Array(ty) => self.with(ty).validate(de),\n            AlgebraicType::Bool => bool::validate(de),\n            AlgebraicType::I8 => i8::validate(de),\n            AlgebraicType::U8 => u8::validate(de),\n            AlgebraicType::I16 => i16::validate(de),\n            AlgebraicType::U16 => u16::validate(de),\n            AlgebraicType::I32 => i32::validate(de),\n            AlgebraicType::U32 => u32::validate(de),\n            AlgebraicType::I64 => i64::validate(de),\n            AlgebraicType::U64 => u64::validate(de),\n            AlgebraicType::I128 => i128::validate(de),\n            AlgebraicType::U128 => u128::validate(de),\n            AlgebraicType::I256 => i256::validate(de),\n            AlgebraicType::U256 => u256::validate(de),\n            AlgebraicType::F32 => f32::validate(de),\n            AlgebraicType::F64 => f64::validate(de),\n            AlgebraicType::String => <Box<str>>::validate(de),\n        }\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for WithTypespace<'_, SumType> {\n    type Output = SumValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        deserializer.deserialize_sum(self)\n    }\n\n    fn validate<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {\n        deserializer.validate_sum(self)\n    }\n}\n\nimpl<'de> SumVisitor<'de> for WithTypespace<'_, SumType> {\n    type Output = SumValue;\n\n    fn sum_name(&self) -> Option<&str> {\n        None\n    }\n\n    fn is_option(&self) -> bool {\n        self.ty().as_option().is_some()\n    }\n\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {\n        let (tag, data) = data.variant(self)?;\n        // Find the variant type by `tag`.\n        let variant_ty = self.map(|ty| &ty.variants[tag as usize].algebraic_type);\n\n        let value = Box::new(data.deserialize_seed(variant_ty)?);\n        Ok(SumValue { tag, value })\n    }\n\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error> {\n        let (tag, data) = data.variant(self)?;\n        // Find the variant type by `tag`.\n        let variant_ty = self.map(|ty| &ty.variants[tag as usize].algebraic_type);\n\n        data.validate_seed(variant_ty)\n    }\n}\n\nimpl VariantVisitor<'_> for WithTypespace<'_, SumType> {\n    type Output = u8;\n\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n        // Provide the names known from the `SumType`.\n        self.ty().variants.iter().filter_map(|v| v.name().map(|n| &**n))\n    }\n\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {\n        // Verify that tag identifies a valid variant in `SumType`.\n        self.ty()\n            .variants\n            .get(tag as usize)\n            .ok_or_else(|| E::unknown_variant_tag(tag, &self))?;\n\n        Ok(tag)\n    }\n\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        // Translate the variant `name` to its tag.\n        self.ty()\n            .variants\n            .iter()\n            .position(|var| var.has_name(name))\n            .map(|pos| pos as u8)\n            .ok_or_else(|| E::unknown_variant_name(name, &self))\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for WithTypespace<'_, ProductType> {\n    type Output = ProductValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        deserializer.deserialize_product(self.map(|pt| &*pt.elements))\n    }\n\n    fn validate<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {\n        deserializer.validate_product(self.map(|pt| &*pt.elements))\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for WithTypespace<'_, [ProductTypeElement]> {\n    type Output = ProductValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        deserializer.deserialize_product(self)\n    }\n\n    fn validate<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {\n        deserializer.validate_product(self)\n    }\n}\n\nimpl<'de> ProductVisitor<'de> for WithTypespace<'_, [ProductTypeElement]> {\n    type Output = ProductValue;\n\n    fn product_name(&self) -> Option<&str> {\n        None\n    }\n    fn product_len(&self) -> usize {\n        self.ty().len()\n    }\n\n    fn visit_seq_product<A: SeqProductAccess<'de>>(self, tup: A) -> Result<Self::Output, A::Error> {\n        visit_seq_product(self, &self, tup)\n    }\n\n    fn validate_seq_product<A: SeqProductAccess<'de>>(self, prod: A) -> Result<(), A::Error> {\n        validate_seq_product(self, &self, prod)\n    }\n\n    fn visit_named_product<A: super::NamedProductAccess<'de>>(self, tup: A) -> Result<Self::Output, A::Error> {\n        visit_named_product(self, &self, tup)\n    }\n\n    fn validate_named_product<A: super::NamedProductAccess<'de>>(self, prod: A) -> Result<(), A::Error> {\n        validate_named_product(self, &self, prod)\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for WithTypespace<'_, ArrayType> {\n    type Output = ArrayValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        /// Deserialize a vector and `map` it to the appropriate `ArrayValue` variant.\n        fn de_array<'de, D: Deserializer<'de>, T: Deserialize<'de>>(\n            de: D,\n            map: impl FnOnce(Box<[T]>) -> ArrayValue,\n        ) -> Result<ArrayValue, D::Error> {\n            de.deserialize_array(BasicVecVisitor).map(<Box<[_]>>::from).map(map)\n        }\n\n        let mut ty = &*self.ty().elem_ty;\n\n        // Loop, resolving `Ref`s, until we reach a non-`Ref` type.\n        loop {\n            break match ty {\n                AlgebraicType::Ref(r) => {\n                    // The only arm that will loop.\n                    ty = self.resolve(*r).ty();\n                    continue;\n                }\n                AlgebraicType::Sum(ty) => deserializer\n                    .deserialize_array_seed(BasicVecVisitor, self.with(ty))\n                    .map(<Box<[_]>>::from)\n                    .map(ArrayValue::Sum),\n                AlgebraicType::Product(ty) => deserializer\n                    .deserialize_array_seed(BasicVecVisitor, self.with(ty))\n                    .map(<Box<[_]>>::from)\n                    .map(ArrayValue::Product),\n                AlgebraicType::Array(ty) => deserializer\n                    .deserialize_array_seed(BasicVecVisitor, self.with(ty))\n                    .map(<Box<[_]>>::from)\n                    .map(ArrayValue::Array),\n                &AlgebraicType::Bool => de_array(deserializer, ArrayValue::Bool),\n                &AlgebraicType::I8 => de_array(deserializer, ArrayValue::I8),\n                &AlgebraicType::U8 => deserializer\n                    .deserialize_bytes(OwnedSliceVisitor)\n                    .map(<Box<[_]>>::from)\n                    .map(ArrayValue::U8),\n                &AlgebraicType::I16 => de_array(deserializer, ArrayValue::I16),\n                &AlgebraicType::U16 => de_array(deserializer, ArrayValue::U16),\n                &AlgebraicType::I32 => de_array(deserializer, ArrayValue::I32),\n                &AlgebraicType::U32 => de_array(deserializer, ArrayValue::U32),\n                &AlgebraicType::I64 => de_array(deserializer, ArrayValue::I64),\n                &AlgebraicType::U64 => de_array(deserializer, ArrayValue::U64),\n                &AlgebraicType::I128 => de_array(deserializer, ArrayValue::I128),\n                &AlgebraicType::U128 => de_array(deserializer, ArrayValue::U128),\n                &AlgebraicType::I256 => de_array(deserializer, ArrayValue::I256),\n                &AlgebraicType::U256 => de_array(deserializer, ArrayValue::U256),\n                &AlgebraicType::F32 => de_array(deserializer, ArrayValue::F32),\n                &AlgebraicType::F64 => de_array(deserializer, ArrayValue::F64),\n                &AlgebraicType::String => de_array(deserializer, ArrayValue::String),\n            };\n        }\n    }\n\n    fn validate<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {\n        /// Validate a vector for the appropriate `ArrayValue` variant.\n        fn val_array<'de, D: Deserializer<'de>, T: Deserialize<'de>>(de: D) -> Result<(), D::Error> {\n            de.validate_array_seed(BasicVecVisitor, PhantomData::<T>)\n        }\n\n        let mut ty = &*self.ty().elem_ty;\n\n        // Loop, resolving `Ref`s, until we reach a non-`Ref` type.\n        loop {\n            break match ty {\n                AlgebraicType::Ref(r) => {\n                    // The only arm that will loop.\n                    ty = self.resolve(*r).ty();\n                    continue;\n                }\n                AlgebraicType::Sum(ty) => deserializer.validate_array_seed(BasicVecVisitor, self.with(ty)),\n                AlgebraicType::Product(ty) => deserializer.validate_array_seed(BasicVecVisitor, self.with(ty)),\n                AlgebraicType::Array(ty) => deserializer.validate_array_seed(BasicVecVisitor, self.with(ty)),\n                &AlgebraicType::Bool => val_array::<_, bool>(deserializer),\n                &AlgebraicType::I8 => val_array::<_, i8>(deserializer),\n                &AlgebraicType::U8 => val_array::<_, u8>(deserializer),\n                &AlgebraicType::I16 => val_array::<_, i16>(deserializer),\n                &AlgebraicType::U16 => val_array::<_, u16>(deserializer),\n                &AlgebraicType::I32 => val_array::<_, i32>(deserializer),\n                &AlgebraicType::U32 => val_array::<_, u32>(deserializer),\n                &AlgebraicType::I64 => val_array::<_, i64>(deserializer),\n                &AlgebraicType::U64 => val_array::<_, u64>(deserializer),\n                &AlgebraicType::I128 => val_array::<_, i128>(deserializer),\n                &AlgebraicType::U128 => val_array::<_, u128>(deserializer),\n                &AlgebraicType::I256 => val_array::<_, i256>(deserializer),\n                &AlgebraicType::U256 => val_array::<_, u256>(deserializer),\n                &AlgebraicType::F32 => val_array::<_, f32>(deserializer),\n                &AlgebraicType::F64 => val_array::<_, f64>(deserializer),\n                &AlgebraicType::String => val_array::<_, String>(deserializer),\n            };\n        }\n    }\n}\n\n/// Deserialize, provided the fields' types, a product value with unnamed fields.\npub fn visit_seq_product<'de, A: SeqProductAccess<'de>>(\n    elems: WithTypespace<[ProductTypeElement]>,\n    visitor: &impl ProductVisitor<'de>,\n    mut tup: A,\n) -> Result<ProductValue, A::Error> {\n    let elements = elems.ty().iter().enumerate().map(|(i, el)| {\n        tup.next_element_seed(elems.with(&el.algebraic_type))?\n            .ok_or_else(|| Error::invalid_product_length(i, visitor))\n    });\n    let elements = elements.collect::<Result<_, _>>()?;\n    Ok(ProductValue { elements })\n}\n\n/// Validate, provided the fields' types, a product value with unnamed fields.\npub fn validate_seq_product<'de, A: SeqProductAccess<'de>>(\n    elems: WithTypespace<[ProductTypeElement]>,\n    visitor: &impl ProductVisitor<'de>,\n    mut tup: A,\n) -> Result<(), A::Error> {\n    for (i, el) in elems.ty().iter().enumerate() {\n        tup.validate_next_element_seed(elems.with(&el.algebraic_type))?\n            .ok_or_else(|| Error::invalid_product_length(i, visitor))?;\n    }\n    Ok(())\n}\n\n/// Deserialize, provided the fields' types, a product value with named fields.\npub fn visit_named_product<'de, A: super::NamedProductAccess<'de>>(\n    elems_tys: WithTypespace<[ProductTypeElement]>,\n    visitor: &impl ProductVisitor<'de>,\n    mut tup: A,\n) -> Result<ProductValue, A::Error> {\n    let elems = elems_tys.ty();\n    let mut elements = vec![None; elems.len()];\n    let kind = visitor.product_kind();\n\n    // Deserialize a product value corresponding to each product type field.\n    // This is worst case quadratic in complexity\n    // as fields can be specified out of order (value side) compared to `elems` (type side).\n    for _ in 0..elems.len() {\n        // Deserialize a field name, match against the element types.\n        let index = tup.get_field_ident(TupleNameVisitor { elems, kind })?.ok_or_else(|| {\n            // Couldn't deserialize a field name.\n            // Find the first field name we haven't filled an element for.\n            let missing = elements.iter().position(|field| field.is_none()).unwrap();\n            let field_name = elems[missing].name().map(|n| &**n);\n            Error::missing_field(missing, field_name, visitor)\n        })?;\n\n        let element = &elems[index];\n\n        // By index we can select which element to deserialize a value for.\n        let slot = &mut elements[index];\n        if slot.is_some() {\n            return Err(Error::duplicate_field(index, element.name().map(|n| &**n), visitor));\n        }\n\n        // Deserialize the value for this field's type.\n        *slot = Some(tup.get_field_value_seed(elems_tys.with(&element.algebraic_type))?);\n    }\n\n    // Get rid of the `Option<_>` layer.\n    let elements = elements\n        .into_iter()\n        // We reached here, so we know nothing was missing, i.e., `None`.\n        .map(|x| x.unwrap_or_else(|| unreachable!(\"visit_named_product\")))\n        .collect();\n\n    Ok(ProductValue { elements })\n}\n\n/// Validate, provided the fields' types, a product value with named fields.\npub fn validate_named_product<'de, A: super::NamedProductAccess<'de>>(\n    elems_tys: WithTypespace<[ProductTypeElement]>,\n    visitor: &impl ProductVisitor<'de>,\n    mut tup: A,\n) -> Result<(), A::Error> {\n    let elems = elems_tys.ty();\n    // TODO(perf): replace with bitset.\n    let mut elements = vec![false; elems.len()];\n    let kind = visitor.product_kind();\n\n    // Deserialize a product value corresponding to each product type field.\n    // This is worst case quadratic in complexity\n    // as fields can be specified out of order (value side) compared to `elems` (type side).\n    for _ in 0..elems.len() {\n        // Deserialize a field name, match against the element types.\n        let index = tup.get_field_ident(TupleNameVisitor { elems, kind })?.ok_or_else(|| {\n            // Couldn't deserialize a field name.\n            // Find the first field name we haven't filled an element for.\n            let missing = elements.iter().position(|&field| !field).unwrap();\n            let field_name = elems[missing].name().map(|n| &**n);\n            Error::missing_field(missing, field_name, visitor)\n        })?;\n\n        let element = &elems[index];\n\n        // By index we can select which element to deserialize a value for.\n        let slot = &mut elements[index];\n        if *slot {\n            return Err(Error::duplicate_field(index, element.name().map(|n| &**n), visitor));\n        }\n\n        // Deserialize the value for this field's type.\n        tup.validate_field_value_seed(elems_tys.with(&element.algebraic_type))?;\n        *slot = true;\n    }\n\n    Ok(())\n}\n\n/// A visitor for extracting indices of field names in the elements of a [`ProductType`].\nstruct TupleNameVisitor<'a> {\n    /// The elements of a product type, in order.\n    elems: &'a [ProductTypeElement],\n    /// The kind of product this is.\n    kind: ProductKind,\n}\n\nimpl FieldNameVisitor<'_> for TupleNameVisitor<'_> {\n    // The index of the field name.\n    type Output = usize;\n\n    fn field_names(&self) -> impl '_ + Iterator<Item = Option<&str>> {\n        self.elems.iter().map(|f| f.name().map(|n| &**n))\n    }\n\n    fn kind(&self) -> ProductKind {\n        self.kind\n    }\n\n    fn visit<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        // Finds the index of a field with `name`.\n        self.elems\n            .iter()\n            .position(|f| f.has_name(name))\n            .ok_or_else(|| Error::unknown_field_name(name, &self))\n    }\n\n    fn visit_seq(self, index: usize) -> Self::Output {\n        // Confirm that the index exists.\n        self.elems\n            .get(index)\n            .expect(\"`index` should exist when `visit_seq` is called\");\n\n        index\n    }\n}\n\nimpl_deserialize!([] spacetimedb_primitives::ArgId, de => u64::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::TableId, de => u32::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::ViewId, de => u32::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::SequenceId, de => u32::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::IndexId, de => u32::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::ConstraintId, de => u32::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::ColId, de => u16::deserialize(de).map(Self));\nimpl_deserialize!([] spacetimedb_primitives::ScheduleId, de => u32::deserialize(de).map(Self));\n\nimpl GrowingVec<ColId> for ColList {\n    fn try_with_capacity<E: Error>(cap: usize) -> Result<Self, E> {\n        Ok(Self::with_capacity(cap as u16))\n    }\n    fn push(&mut self, elem: ColId) {\n        self.push(elem);\n    }\n}\nimpl_deserialize!([] spacetimedb_primitives::ColList, de => {\n    struct ColListVisitor;\n    impl<'de> ArrayVisitor<'de, ColId> for ColListVisitor {\n        type Output = ColList;\n\n        fn visit<A: ArrayAccess<'de, Element = ColId>>(self, vec: A) -> Result<Self::Output, A::Error> {\n            array_visit(vec)\n        }\n\n        fn validate<A: ArrayAccess<'de, Element = ColId>>(self, vec: A) -> Result<(), A::Error> {\n            array_validate(vec)\n        }\n    }\n    de.deserialize_array(ColListVisitor)\n});\nimpl_deserialize!(\n    [] spacetimedb_primitives::ColSet,\n    de => ColList::deserialize(de).map(Into::into),\n    de => ColList::validate(de)\n);\n\n#[cfg(feature = \"blake3\")]\nimpl_deserialize!([] blake3::Hash, de => <[u8; blake3::OUT_LEN]>::deserialize(de).map(blake3::Hash::from_bytes));\n\n// TODO(perf): integrate Bytes with Deserializer to reduce copying\nimpl_deserialize!(\n    [] bytes::Bytes,\n    de => <Vec<u8>>::deserialize(de).map(Into::into),\n    de => <&[u8]>::validate(de)\n);\n\n#[cfg(feature = \"bytestring\")]\nimpl_deserialize!(\n    [] bytestring::ByteString,\n    de => <String>::deserialize(de).map(Into::into),\n    de => <&str>::validate(de)\n);\n\n#[cfg(test)]\nmod test {\n    use crate::{\n        algebraic_value::{de::ValueDeserializer, ser::value_serialize},\n        bsatn,\n        serde::SerdeWrapper,\n        Deserialize, Serialize,\n    };\n    use core::fmt::Debug;\n\n    #[test]\n    fn roundtrip_tuples_in_different_data_formats() {\n        fn test<T: Serialize + for<'de> Deserialize<'de> + Eq + Debug>(x: T) {\n            let bsatn = bsatn::to_vec(&x).unwrap();\n            let y: T = bsatn::from_slice(&bsatn).unwrap();\n            assert_eq!(x, y);\n\n            let val = value_serialize(&x);\n            let y = T::deserialize(ValueDeserializer::new(val)).unwrap();\n            assert_eq!(x, y);\n\n            let json = serde_json::to_string(SerdeWrapper::from_ref(&x)).unwrap();\n            let SerdeWrapper(y) = serde_json::from_str::<SerdeWrapper<T>>(&json).unwrap();\n            assert_eq!(x, y);\n        }\n\n        test(());\n        test((true,));\n        test((1337u64, false));\n        test(((7331u64, false), 42u32, 24u8));\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/de/serde.rs",
    "content": "use super::Deserializer;\nuse crate::serde::{SerdeError, SerdeWrapper};\nuse crate::{i256, u256};\nuse core::fmt;\nuse serde::de as serde;\n\n/// Converts any [`serde::Deserializer`] to a SATS [`Deserializer`]\n/// so that Serde's data formats can be reused.\n///\n/// In order for successful round-trip deserialization, the `serde::Deserializer`\n/// that this type wraps must support `deserialize_any()`.\npub struct SerdeDeserializer<D> {\n    /// A deserialization data format in Serde.\n    de: D,\n}\n\nimpl<D> SerdeDeserializer<D> {\n    /// Wraps a Serde deserializer.\n    pub fn new(de: D) -> Self {\n        Self { de }\n    }\n}\n\n#[inline]\nfn unwrap_error<E>(err: SerdeError<E>) -> E {\n    let SerdeError(err) = err;\n    err\n}\n\nimpl<E: serde::Error> super::Error for SerdeError<E> {\n    fn custom(msg: impl fmt::Display) -> Self {\n        SerdeError(E::custom(msg))\n    }\n\n    fn invalid_product_length<'de, T: super::ProductVisitor<'de>>(len: usize, expected: &T) -> Self {\n        SerdeError(E::invalid_length(len, &super::fmt_invalid_len(expected)))\n    }\n}\n\n/// Deserialize a `T` provided a serde deserializer `D`.\nfn deserialize<'de, D: serde::Deserializer<'de>, T: serde::Deserialize<'de>>(de: D) -> Result<T, SerdeError<D::Error>> {\n    serde::Deserialize::deserialize(de).map_err(SerdeError)\n}\n\nimpl<'de, D: serde::Deserializer<'de>> Deserializer<'de> for SerdeDeserializer<D> {\n    type Error = SerdeError<D::Error>;\n\n    fn deserialize_product<V: super::ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        self.de.deserialize_any(TupleVisitor { visitor }).map_err(SerdeError)\n    }\n\n    fn deserialize_sum<V: super::SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        self.de.deserialize_any(EnumVisitor { visitor }).map_err(SerdeError)\n    }\n\n    fn deserialize_bool(self) -> Result<bool, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u8(self) -> Result<u8, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u16(self) -> Result<u16, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u32(self) -> Result<u32, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u64(self) -> Result<u64, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u128(self) -> Result<u128, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_u256(self) -> Result<u256, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i8(self) -> Result<i8, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i16(self) -> Result<i16, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i32(self) -> Result<i32, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i64(self) -> Result<i64, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i128(self) -> Result<i128, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_i256(self) -> Result<i256, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_f32(self) -> Result<f32, Self::Error> {\n        deserialize(self.de)\n    }\n    fn deserialize_f64(self) -> Result<f64, Self::Error> {\n        deserialize(self.de)\n    }\n\n    fn deserialize_str<V: super::SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        self.de.deserialize_str(StrVisitor { visitor }).map_err(SerdeError)\n    }\n\n    fn deserialize_bytes<V: super::SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        if self.de.is_human_readable() {\n            self.de.deserialize_any(BytesVisitor::<_, true> { visitor })\n        } else {\n            self.de.deserialize_bytes(BytesVisitor::<_, false> { visitor })\n        }\n        .map_err(SerdeError)\n    }\n\n    fn deserialize_array_seed<V: super::ArrayVisitor<'de, T::Output>, T: super::DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        self.de\n            .deserialize_seq(ArrayVisitor { visitor, seed })\n            .map_err(SerdeError)\n    }\n}\n\npub use crate::serde::SerdeWrapper as SeedWrapper;\n\nimpl<'de, T: super::DeserializeSeed<'de>> serde::DeserializeSeed<'de> for SerdeWrapper<T> {\n    type Value = T::Output;\n\n    fn deserialize<D>(self, de: D) -> Result<Self::Value, D::Error>\n    where\n        D: ::serde::Deserializer<'de>,\n    {\n        self.0.deserialize(SerdeDeserializer { de }).map_err(unwrap_error)\n    }\n}\n\n/// Converts a `ProductVisitor` to a `serde::Visitor`.\nstruct TupleVisitor<V> {\n    /// The `ProductVisitor` to convert.\n    visitor: V,\n}\n\nimpl<'de, V: super::ProductVisitor<'de>> serde::Visitor<'de> for TupleVisitor<V> {\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        if let Some(name) = self.visitor.product_name() {\n            write!(f, \"a {name} tuple\")\n        } else {\n            write!(f, \"a {}-element tuple\", self.visitor.product_len())\n        }\n    }\n\n    fn visit_map<A: serde::MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {\n        self.visitor\n            .visit_named_product(NamedTupleAccess { map })\n            .map_err(unwrap_error)\n    }\n\n    fn visit_seq<A: serde::SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {\n        self.visitor\n            .visit_seq_product(SeqTupleAccess { seq })\n            .map_err(unwrap_error)\n    }\n}\n\n/// Turns Serde's style of deserializing map entries\n/// into deserializing field names and their values.\nstruct NamedTupleAccess<A> {\n    /// An implementation of `serde::MapAccess<'de>` to convert.\n    map: A,\n}\n\nimpl<'de, A: serde::MapAccess<'de>> super::NamedProductAccess<'de> for NamedTupleAccess<A> {\n    type Error = SerdeError<A::Error>;\n\n    fn get_field_ident<V: super::FieldNameVisitor<'de>>(\n        &mut self,\n        visitor: V,\n    ) -> Result<Option<V::Output>, Self::Error> {\n        self.map.next_key_seed(FieldNameVisitor { visitor }).map_err(SerdeError)\n    }\n\n    fn get_field_value_seed<T: super::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<T::Output, Self::Error> {\n        self.map.next_value_seed(SeedWrapper(seed)).map_err(SerdeError)\n    }\n}\n\n/// Converts a SATS field name visitor for use in [`NamedTupleAccess`].\nstruct FieldNameVisitor<V> {\n    /// The underlying field name visitor.\n    visitor: V,\n}\n\nimpl<'de, V: super::FieldNameVisitor<'de>> serde::DeserializeSeed<'de> for FieldNameVisitor<V> {\n    type Value = V::Output;\n\n    fn deserialize<D: ::serde::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {\n        deserializer.deserialize_str(self)\n    }\n}\n\nimpl<'de, V: super::FieldNameVisitor<'de>> serde::Visitor<'de> for FieldNameVisitor<V> {\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match super::one_of_names(|| self.visitor.field_names()) {\n            Some(one_of) => {\n                write!(f, \"a tuple field ({one_of})\")\n            }\n            _ => f.write_str(\"a tuple field, but there are no fields\"),\n        }\n    }\n\n    fn visit_str<E: serde::Error>(self, v: &str) -> Result<Self::Value, E> {\n        self.visitor.visit(v).map_err(unwrap_error)\n    }\n}\n\n/// Turns `serde::SeqAccess` deserializing the elements of a sequence\n/// into `SeqProductAccess`.\nstruct SeqTupleAccess<A> {\n    /// The `serde::SeqAccess` to convert.\n    seq: A,\n}\n\nimpl<'de, A: serde::SeqAccess<'de>> super::SeqProductAccess<'de> for SeqTupleAccess<A> {\n    type Error = SerdeError<A::Error>;\n\n    fn next_element_seed<T: super::DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Output>, Self::Error> {\n        let res = self.seq.next_element_seed(SeedWrapper(seed)).map_err(SerdeError)?;\n        Ok(res)\n    }\n}\n\n/// Converts a SATS `SumVisitor` to `serde::Visitor`.\nstruct EnumVisitor<V> {\n    /// The `SumVisitor`.\n    visitor: V,\n}\n\nimpl<'de, V: super::SumVisitor<'de>> serde::Visitor<'de> for EnumVisitor<V> {\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match self.visitor.sum_name() {\n            Some(name) => write!(f, \"sum type {name}\"),\n            None => f.write_str(\"sum type\"),\n        }\n    }\n\n    fn visit_map<A>(self, access: A) -> Result<Self::Value, A::Error>\n    where\n        A: serde::MapAccess<'de>,\n    {\n        self.visitor.visit_sum(EnumAccess { access }).map_err(unwrap_error)\n    }\n\n    fn visit_seq<A>(self, access: A) -> Result<Self::Value, A::Error>\n    where\n        A: serde::SeqAccess<'de>,\n    {\n        self.visitor.visit_sum(SeqEnumAccess { access }).map_err(unwrap_error)\n    }\n\n    fn visit_unit<E: serde::Error>(self) -> Result<Self::Value, E> {\n        if self.visitor.is_option() {\n            self.visitor.visit_sum(super::NoneAccess::new()).map_err(unwrap_error)\n        } else {\n            Err(E::invalid_type(serde::Unexpected::Unit, &self))\n        }\n    }\n}\n\n/// Converts SATS way of identifying a variant to Serde's way.\nstruct VariantVisitor<V> {\n    /// The SATS `VariantVisitor` to convert.\n    visitor: V,\n}\n\nimpl<'de, V: super::VariantVisitor<'de>> serde::DeserializeSeed<'de> for VariantVisitor<V> {\n    type Value = V::Output;\n\n    fn deserialize<D: serde::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {\n        deserializer.deserialize_any(self)\n    }\n}\n\nimpl<'de, V: super::VariantVisitor<'de>> serde::Visitor<'de> for VariantVisitor<V> {\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"variant identifier (string or int)\")\n    }\n\n    fn visit_u8<E: serde::Error>(self, v: u8) -> Result<Self::Value, E> {\n        self.visitor.visit_tag(v).map_err(unwrap_error)\n    }\n    fn visit_u64<E: serde::Error>(self, v: u64) -> Result<Self::Value, E> {\n        let v: u8 = v\n            .try_into()\n            .map_err(|_| E::invalid_value(serde::Unexpected::Unsigned(v), &\"a u8 tag\"))?;\n        self.visit_u8(v)\n    }\n\n    fn visit_str<E: serde::Error>(self, v: &str) -> Result<Self::Value, E> {\n        if let Ok(tag) = v.parse::<u8>() {\n            self.visit_u8(tag)\n        } else {\n            self.visitor.visit_name(v).map_err(unwrap_error)\n        }\n    }\n}\n\n/// Converts Serde's `EnumAccess` to SATS `SumAccess`.\nstruct EnumAccess<A> {\n    /// The Serde `EnumAccess`.\n    access: A,\n}\n\nimpl<'de, A: serde::MapAccess<'de>> super::SumAccess<'de> for EnumAccess<A> {\n    type Error = SerdeError<A::Error>;\n    type Variant = Self;\n\n    fn variant<V: super::VariantVisitor<'de>>(mut self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        let errmsg = \"expected map representing sum type to have exactly one field\";\n        let key = self\n            .access\n            .next_key_seed(VariantVisitor { visitor })\n            .map_err(SerdeError)?\n            .ok_or_else(|| SerdeError(serde::Error::custom(errmsg)))?;\n        Ok((key, self))\n    }\n}\n\nimpl<'de, A: serde::MapAccess<'de>> super::VariantAccess<'de> for EnumAccess<A> {\n    type Error = SerdeError<A::Error>;\n\n    fn deserialize_seed<T: super::DeserializeSeed<'de>>(mut self, seed: T) -> Result<T::Output, Self::Error> {\n        self.access.next_value_seed(SeedWrapper(seed)).map_err(SerdeError)\n    }\n}\n\nstruct SeqEnumAccess<A> {\n    access: A,\n}\n\nconst SEQ_ENUM_ERR: &str = \"expected seq representing sum type to have exactly two fields\";\nimpl<'de, A: serde::SeqAccess<'de>> super::SumAccess<'de> for SeqEnumAccess<A> {\n    type Error = SerdeError<A::Error>;\n    type Variant = Self;\n\n    fn variant<V: super::VariantVisitor<'de>>(mut self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        let key = self\n            .access\n            .next_element_seed(VariantVisitor { visitor })\n            .map_err(SerdeError)?\n            .ok_or_else(|| SerdeError(serde::Error::custom(SEQ_ENUM_ERR)))?;\n        Ok((key, self))\n    }\n}\n\nimpl<'de, A: serde::SeqAccess<'de>> super::VariantAccess<'de> for SeqEnumAccess<A> {\n    type Error = SerdeError<A::Error>;\n\n    fn deserialize_seed<T: super::DeserializeSeed<'de>>(mut self, seed: T) -> Result<T::Output, Self::Error> {\n        self.access\n            .next_element_seed(SeedWrapper(seed))\n            .map_err(SerdeError)?\n            .ok_or_else(|| SerdeError(serde::Error::custom(SEQ_ENUM_ERR)))\n    }\n}\n\n/// Translates a `SliceVisitor<'de, str>` to `serde::Visitor<'de>`\n/// for implementing `deserialize_str`.\nstruct StrVisitor<V> {\n    /// The `SliceVisitor<'de, str>`.\n    visitor: V,\n}\n\nimpl<'de, V: super::SliceVisitor<'de, str>> serde::Visitor<'de> for StrVisitor<V> {\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"a string\")\n    }\n\n    fn visit_str<E: serde::Error>(self, v: &str) -> Result<Self::Value, E> {\n        self.visitor.visit(v).map_err(unwrap_error)\n    }\n\n    fn visit_borrowed_str<E: serde::Error>(self, v: &'de str) -> Result<Self::Value, E> {\n        self.visitor.visit_borrowed(v).map_err(unwrap_error)\n    }\n\n    fn visit_string<E: serde::Error>(self, v: String) -> Result<Self::Value, E> {\n        self.visitor.visit_owned(v).map_err(unwrap_error)\n    }\n}\n\n/// Translates a `SliceVisitor<'de, str>` to `serde::Visitor<'de>`\n/// for implementing `deserialize_bytes`.\nstruct BytesVisitor<V, const HUMAN_READABLE: bool> {\n    /// The `SliceVisitor<'de, [u8]>`.\n    visitor: V,\n}\n\nimpl<'de, V: super::SliceVisitor<'de, [u8]>, const HUMAN_READABLE: bool> serde::Visitor<'de>\n    for BytesVisitor<V, HUMAN_READABLE>\n{\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(if HUMAN_READABLE {\n            \"a byte array or hex string\"\n        } else {\n            \"a byte array\"\n        })\n    }\n\n    fn visit_bytes<E: serde::Error>(self, v: &[u8]) -> Result<Self::Value, E> {\n        self.visitor.visit(v).map_err(unwrap_error)\n    }\n\n    fn visit_borrowed_bytes<E: serde::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {\n        self.visitor.visit_borrowed(v).map_err(unwrap_error)\n    }\n\n    fn visit_byte_buf<E: serde::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {\n        self.visitor.visit_owned(v).map_err(unwrap_error)\n    }\n\n    fn visit_str<E: serde::Error>(self, v: &str) -> Result<Self::Value, E> {\n        let data = hex_string(v, &self)?;\n        self.visitor.visit_owned(data).map_err(unwrap_error)\n    }\n\n    fn visit_seq<A: serde::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {\n        let mut v = Vec::with_capacity(std::cmp::min(seq.size_hint().unwrap_or(0), 4096));\n        while let Some(val) = seq.next_element()? {\n            v.push(val);\n        }\n        self.visitor.visit_owned(v).map_err(unwrap_error)\n    }\n}\n\n/// Hex decodes the string `v`.\nfn hex_string<T: hex::FromHex<Error = hex::FromHexError>, E: serde::Error>(\n    v: &str,\n    exp: &dyn serde::Expected,\n) -> Result<T, E> {\n    T::from_hex(v).map_err(|_| serde::Error::invalid_value(serde::Unexpected::Str(v), exp))\n}\n\n// struct HashVisitor;\n\n// impl<'de> serde::Visitor<'de> for HashVisitor {\n//     type Value = crate::Hash;\n\n//     fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n//         f.write_str(\"a hex string representing a 32-byte hash\")\n//     }\n\n//     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>\n//     where\n//         E: serde::Error,\n//     {\n//         let data = hex_string(v, &self)?;\n//         Ok(crate::Hash { data })\n//     }\n\n//     fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>\n//     where\n//         E: serde::Error,\n//     {\n//         let data = v\n//             .try_into()\n//             .map_err(|_| serde::Error::invalid_value(serde::Unexpected::Bytes(v), &\"a 32-byte hash\"))?;\n//         Ok(crate::Hash { data })\n//     }\n// }\n\n/// Translates `ArrayVisitor<'de, T::Output>` (the trait) to `serde::Visitor<'de>`\n/// for implementing `deserialize_array`.\nstruct ArrayVisitor<V, T> {\n    /// The SATS visitor to translate to a Serde visitor.\n    visitor: V,\n    /// The seed value to provide to `DeserializeSeed`.\n    seed: T,\n}\n\nimpl<'de, T: super::DeserializeSeed<'de> + Clone, V: super::ArrayVisitor<'de, T::Output>> serde::Visitor<'de>\n    for ArrayVisitor<V, T>\n{\n    type Value = V::Output;\n\n    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"a vec\")\n    }\n\n    fn visit_seq<A: serde::SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {\n        self.visitor\n            .visit(ArrayAccess { seq, seed: self.seed })\n            .map_err(unwrap_error)\n    }\n}\n\n/// Translates `serde::SeqAccess<'de>` (the trait) to `ArrayAccess<'de>`\n/// for implementing deserialization of array elements.\nstruct ArrayAccess<A, T> {\n    /// The `serde::SeqAccess<'de>` implementation.\n    seq: A,\n    /// The seed to pass onto `DeserializeSeed`.\n    seed: T,\n}\n\nimpl<'de, A: serde::SeqAccess<'de>, T: super::DeserializeSeed<'de> + Clone> super::ArrayAccess<'de>\n    for ArrayAccess<A, T>\n{\n    type Element = T::Output;\n    type Error = SerdeError<A::Error>;\n\n    fn next_element(&mut self) -> Result<Option<T::Output>, Self::Error> {\n        self.seq\n            .next_element_seed(SeedWrapper(self.seed.clone()))\n            .map_err(SerdeError)\n    }\n\n    fn size_hint(&self) -> Option<usize> {\n        self.seq.size_hint()\n    }\n}\n\nimpl<F: Fn(&mut fmt::Formatter) -> fmt::Result> serde::Expected for super::FDisplay<F> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        (self.0)(f)\n    }\n}\n\n/// Deserializes `T` as a SATS object from `deserializer: D`\n/// where `D` is a serde data format.\npub fn deserialize_from<'de, T: super::Deserialize<'de>, D: serde::Deserializer<'de>>(\n    deserializer: D,\n) -> Result<T, D::Error> {\n    T::deserialize(SerdeDeserializer::new(deserializer)).map_err(unwrap_error)\n}\n\npub use crate::serde::SerdeWrapper as DeserializeWrapper;\n\nimpl<'de, T: super::Deserialize<'de>> serde::Deserialize<'de> for SerdeWrapper<T> {\n    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        deserialize_from(deserializer).map(Self)\n    }\n}\n\nmacro_rules! delegate_serde {\n    ($($t:ty),*) => {\n        $(impl<'de> serde::Deserialize<'de> for $t {\n            fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n                deserialize_from(deserializer)\n            }\n        })*\n    };\n}\n\ndelegate_serde!(crate::AlgebraicType, crate::ProductType, crate::SumType);\n"
  },
  {
    "path": "crates/sats/src/de.rs",
    "content": "// Some parts copyright Serde developers under the MIT / Apache-2.0 licenses at your option.\n// See `serde` version `v1.0.169` for the parts where MIT / Apache-2.0 applies.\n\nmod impls;\n#[cfg(any(test, feature = \"serde\"))]\npub mod serde;\n\n#[doc(hidden)]\npub use impls::{visit_named_product, visit_seq_product, WithBound};\n\nuse crate::buffer::BufReader;\nuse crate::{bsatn, i256, u256};\nuse core::fmt;\nuse core::marker::PhantomData;\nuse smallvec::SmallVec;\nuse std::borrow::Borrow;\n\n/// A data format that can deserialize any data structure supported by SATS.\n///\n/// The `Deserializer` trait in SATS performs the same function as `serde::Deserializer` in [`serde`].\n/// See the documentation of `serde::Deserializer` for more information of the data model.\n///\n/// Implementations of `Deserialize` map themselves into this data model\n/// by passing to the `Deserializer` a visitor that can receive the necessary types.\n/// The kind of visitor depends on the `deserialize_*` method.\n/// Unlike in Serde, there isn't a single monolithic `Visitor` trait,\n/// but rather, this functionality is split up into more targeted traits such as `SumVisitor<'de>`.\n///\n/// The lifetime `'de` allows us to deserialize lifetime-generic types in a zero-copy fashion.\n///\n/// [`serde`]: https://crates.io/crates/serde\npub trait Deserializer<'de>: Sized {\n    /// The error type that can be returned if some error occurs during deserialization.\n    type Error: Error;\n\n    /// Deserializes a product value from the input.\n    fn deserialize_product<V: ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error>;\n\n    /// Validates a product value from the input.\n    fn validate_product<V: ProductVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        self.deserialize_product(visitor).map(|_| ())\n    }\n\n    /// Deserializes a sum value from the input.\n    ///\n    /// The entire process of deserializing a sum, starting from `deserialize(args...)`, is roughly:\n    ///\n    /// - [`deserialize`][Deserialize::deserialize] calls this method,\n    ///   [`deserialize_sum(sum_visitor)`](Deserializer::deserialize_sum),\n    ///   providing us with a [`sum_visitor`](SumVisitor).\n    ///\n    /// - This method calls [`sum_visitor.visit_sum(sum_access)`](SumVisitor::visit_sum),\n    ///   where [`sum_access`](SumAccess) deals with extracting the tag and the variant data,\n    ///   with the latter provided as [`VariantAccess`]).\n    ///   The `SumVisitor` will then assemble these into the representation of a sum value\n    ///   that the [`Deserialize`] implementation wants.\n    ///\n    /// - [`visit_sum`](SumVisitor::visit_sum) then calls\n    ///   [`sum_access.variant(variant_visitor)`](SumAccess::variant),\n    ///   and uses the provided `variant_visitor` to translate extracted variant names / tags\n    ///   into something that is meaningful for `visit_sum`, e.g., an index.\n    ///\n    ///   The call to `variant` will also return [`variant_access`](VariantAccess)\n    ///   that can deserialize the contents of the variant.\n    ///\n    /// - Finally, after `variant` returns,\n    ///   `visit_sum` deserializes the variant data using\n    ///   [`variant_access.deserialize_seed(seed)`](VariantAccess::deserialize_seed)\n    ///   or [`variant_access.deserialize()`](VariantAccess::deserialize).\n    ///   This part may require some conditional logic depending on the identified variant.\n    ///\n    ///\n    /// The data format will also return an object ([`VariantAccess`])\n    /// that can deserialize the contents of the variant.\n    fn deserialize_sum<V: SumVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error>;\n\n    /// Validates a sum value from the input.\n    ///\n    /// The entire process of validating a sum, starting from `validate(args...)`, is roughly:\n    ///\n    /// - [`validate`][Deserialize::validate] calls this method,\n    ///   [`validate_sum(sum_visitor)`](Deserializer::validate_sum),\n    ///   providing us with a [`sum_visitor`](SumVisitor).\n    ///\n    /// - This method calls [`sum_visitor.validate_sum(sum_access)`](SumVisitor::validate_sum),\n    ///   where [`sum_access`](SumAccess) deals with extracting the tag and the variant data,\n    ///   with the latter provided as [`VariantAccess`]).\n    ///   The `SumVisitor` will then assemble these into the representation of a sum value\n    ///   that the [`Deserialize`] implementation wants.\n    ///\n    /// - [`validate_sum`](SumVisitor::validate_sum) then calls\n    ///   [`sum_access.variant(variant_visitor)`](SumAccess::variant),\n    ///   and uses the provided `variant_visitor` to translate extracted variant names / tags\n    ///   into something that is meaningful for `validate_sum`, e.g., an index.\n    ///\n    ///   The call to `variant` will also return [`variant_access`](VariantAccess)\n    ///   that can validate the contents of the variant.\n    ///\n    /// - Finally, after `variant` returns,\n    ///   `validate_sum` validates the variant data using\n    ///   [`variant_access.validate_seed(seed)`](VariantAccess::validate_seed)\n    ///   or [`variant_access.validate()`](VariantAccess::validate).\n    ///   This part may require some conditional logic depending on the identified variant.\n    ///\n    /// The data format will also return an object ([`VariantAccess`])\n    /// that can validate the contents of the variant.\n    fn validate_sum<V: SumVisitor<'de>>(self, visitor: V) -> Result<(), Self::Error> {\n        self.deserialize_sum(visitor).map(|_| ())\n    }\n\n    /// Deserializes a `bool` value from the input.\n    fn deserialize_bool(self) -> Result<bool, Self::Error>;\n\n    /// Deserializes a `u8` value from the input.\n    fn deserialize_u8(self) -> Result<u8, Self::Error>;\n\n    /// Deserializes a `u16` value from the input.\n    fn deserialize_u16(self) -> Result<u16, Self::Error>;\n\n    /// Deserializes a `u32` value from the input.\n    fn deserialize_u32(self) -> Result<u32, Self::Error>;\n\n    /// Deserializes a `u64` value from the input.\n    fn deserialize_u64(self) -> Result<u64, Self::Error>;\n\n    /// Deserializes a `u128` value from the input.\n    fn deserialize_u128(self) -> Result<u128, Self::Error>;\n\n    /// Deserializes a `u256` value from the input.\n    fn deserialize_u256(self) -> Result<u256, Self::Error>;\n\n    /// Deserializes an `i8` value from the input.\n    fn deserialize_i8(self) -> Result<i8, Self::Error>;\n\n    /// Deserializes an `i16` value from the input.\n    fn deserialize_i16(self) -> Result<i16, Self::Error>;\n\n    /// Deserializes an `i32` value from the input.\n    fn deserialize_i32(self) -> Result<i32, Self::Error>;\n\n    /// Deserializes an `i64` value from the input.\n    fn deserialize_i64(self) -> Result<i64, Self::Error>;\n\n    /// Deserializes an `i128` value from the input.\n    fn deserialize_i128(self) -> Result<i128, Self::Error>;\n\n    /// Deserializes an `i256` value from the input.\n    fn deserialize_i256(self) -> Result<i256, Self::Error>;\n\n    /// Deserializes an `f32` value from the input.\n    fn deserialize_f32(self) -> Result<f32, Self::Error>;\n\n    /// Deserializes an `f64` value from the input.\n    fn deserialize_f64(self) -> Result<f64, Self::Error>;\n\n    /// Deserializes a string-like object the input.\n    fn deserialize_str<V: SliceVisitor<'de, str>>(self, visitor: V) -> Result<V::Output, Self::Error>;\n\n    /// Deserializes an `&str` string value.\n    fn deserialize_str_slice(self) -> Result<&'de str, Self::Error> {\n        self.deserialize_str(BorrowedSliceVisitor)\n    }\n\n    /// Deserializes a byte slice-like value.\n    fn deserialize_bytes<V: SliceVisitor<'de, [u8]>>(self, visitor: V) -> Result<V::Output, Self::Error>;\n\n    /// Deserializes an array value.\n    ///\n    /// This is typically the same as [`deserialize_array_seed`](Deserializer::deserialize_array_seed)\n    /// with an uninteresting `seed` value.\n    fn deserialize_array<V: ArrayVisitor<'de, T>, T: Deserialize<'de>>(\n        self,\n        visitor: V,\n    ) -> Result<V::Output, Self::Error> {\n        self.deserialize_array_seed(visitor, PhantomData)\n    }\n\n    /// Deserializes an array value.\n    ///\n    /// The deserialization is provided with a `seed` value.\n    fn deserialize_array_seed<V: ArrayVisitor<'de, T::Output>, T: DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<V::Output, Self::Error>;\n\n    /// Validates an array value.\n    ///\n    /// The validation is provided with a `seed` value.\n    fn validate_array_seed<V: ArrayVisitor<'de, T::Output>, T: DeserializeSeed<'de> + Clone>(\n        self,\n        visitor: V,\n        seed: T,\n    ) -> Result<(), Self::Error> {\n        self.deserialize_array_seed(visitor, seed).map(|_| ())\n    }\n}\n\n/// The `Error` trait allows [`Deserialize`] implementations to create descriptive error messages\n/// belonging to the [`Deserializer`] against which they are currently running.\n///\n/// Every [`Deserializer`] declares an [`Error`] type\n/// that encompasses both general-purpose deserialization errors\n/// as well as errors specific to the particular deserialization format.\n///\n/// Most deserializers should only need to provide the [`Error::custom`] method\n/// and inherit the default behavior for the other methods.\npub trait Error: Sized {\n    /// Raised when there is general error when deserializing a type.\n    fn custom(msg: impl fmt::Display) -> Self;\n\n    /// Deserializing named products are not supported for this visitor.\n    fn named_products_not_supported() -> Self {\n        Self::custom(\"named products not supported\")\n    }\n\n    /// The product length was not as promised.\n    fn invalid_product_length<'de, T: ProductVisitor<'de>>(len: usize, expected: &T) -> Self {\n        Self::custom(format_args!(\n            \"invalid length {}, expected {}\",\n            len,\n            fmt_invalid_len(expected)\n        ))\n    }\n\n    /// There was a missing field at `index`.\n    fn missing_field<'de, T: ProductVisitor<'de>>(index: usize, field_name: Option<&str>, prod: &T) -> Self {\n        Self::custom(error_on_field(\"missing \", index, field_name, prod))\n    }\n\n    /// A field with `index` was specified more than once.\n    fn duplicate_field<'de, T: ProductVisitor<'de>>(index: usize, field_name: Option<&str>, prod: &T) -> Self {\n        Self::custom(error_on_field(\"duplicate \", index, field_name, prod))\n    }\n\n    /// A field with name `field_name` does not exist.\n    fn unknown_field_name<'de, T: FieldNameVisitor<'de>>(field_name: &str, expected: &T) -> Self {\n        let el_ty = match expected.kind() {\n            ProductKind::Normal => \"field\",\n            ProductKind::ReducerArgs => \"reducer argument\",\n        };\n        match one_of_names(|| expected.field_names()) {\n            Some(one_of) => Self::custom(format_args!(\"unknown {el_ty} `{field_name}`, expected {one_of}\")),\n            _ => Self::custom(format_args!(\"unknown {el_ty} `{field_name}`, there are no {el_ty}s\")),\n        }\n    }\n\n    /// The `tag` does not specify a variant of the sum type.\n    fn unknown_variant_tag<'de, T: SumVisitor<'de>>(tag: u8, expected: &T) -> Self {\n        Self::custom(format_args!(\n            \"unknown tag {tag:#x} for sum type {}\",\n            expected.sum_name().unwrap_or(\"<unknown>\"),\n        ))\n    }\n\n    /// The `name` is not that of a variant of the sum type.\n    fn unknown_variant_name<'de, T: VariantVisitor<'de>>(name: &str, expected: &T) -> Self {\n        match one_of_names(|| expected.variant_names().map(Some)) {\n            Some(one_of) => Self::custom(format_args!(\"unknown variant `{name}`, expected {one_of}\",)),\n            _ => Self::custom(format_args!(\"unknown variant `{name}`, there are no variants\")),\n        }\n    }\n\n    /// An allocation of `size` elements failed during deserialization.\n    fn allocation_failed(size: usize) -> Self {\n        Self::custom(format_args!(\"allocation of {size} elements failed\"))\n    }\n}\n\n/// Turns a closure `impl Fn(&mut Formatter) -> Result` into a `Display`able object.\npub struct FDisplay<F>(F);\n\nimpl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for FDisplay<F> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        (self.0)(f)\n    }\n}\n\n/// Turns a closure `F: Fn(&mut Formatter) -> Result` into a `Display`able object.\npub fn fmt_fn<F: Fn(&mut fmt::Formatter) -> fmt::Result>(f: F) -> FDisplay<F> {\n    FDisplay(f)\n}\n\n/// Returns an error message for a `problem` with field at `index` and an optional `name`.\nfn error_on_field<'a, 'de>(\n    problem: &'static str,\n    index: usize,\n    name: Option<&'a str>,\n    prod: &impl ProductVisitor<'de>,\n) -> impl fmt::Display + 'a {\n    let field_kind = match prod.product_kind() {\n        ProductKind::Normal => \"field\",\n        ProductKind::ReducerArgs => \"reducer argument\",\n    };\n    fmt_fn(move |f| {\n        // e.g. \"missing field `foo`\"\n        f.write_str(problem)?;\n        f.write_str(field_kind)?;\n        if let Some(name) = name {\n            write!(f, \" `{name}`\")\n        } else {\n            write!(f, \" (index {index})\")\n        }\n    })\n}\n\n/// Returns an error message for invalid product type lengths.\nfn fmt_invalid_len<'de>(\n    expected: &impl ProductVisitor<'de>,\n) -> FDisplay<impl '_ + Fn(&mut fmt::Formatter) -> fmt::Result> {\n    fmt_fn(|f| {\n        let ty = match expected.product_kind() {\n            ProductKind::Normal => \"product type\",\n            ProductKind::ReducerArgs => \"reducer args for\",\n        };\n        let name = expected.product_name().unwrap_or(\"<product>\");\n        let len = expected.product_len();\n\n        write!(f, \"{ty} {name} with {len} elements\")\n    })\n}\n\n/// A visitor walking through a [`Deserializer`] for products.\npub trait ProductVisitor<'de>: Sized {\n    /// The resulting product.\n    type Output;\n\n    /// Returns the name of the product, if any.\n    fn product_name(&self) -> Option<&str>;\n\n    /// Returns the length of the product.\n    fn product_len(&self) -> usize;\n\n    /// Returns the kind of the product.\n    fn product_kind(&self) -> ProductKind {\n        ProductKind::Normal\n    }\n\n    /// The input contains an unnamed product.\n    fn visit_seq_product<A: SeqProductAccess<'de>>(self, prod: A) -> Result<Self::Output, A::Error>;\n\n    /// The input contains a named product.\n    fn visit_named_product<A: NamedProductAccess<'de>>(self, prod: A) -> Result<Self::Output, A::Error>;\n\n    /// The input contains an unnamed product.\n    fn validate_seq_product<A: SeqProductAccess<'de>>(self, prod: A) -> Result<(), A::Error> {\n        self.visit_seq_product(prod).map(|_| ())\n    }\n\n    /// The input contains a named product.\n    fn validate_named_product<A: NamedProductAccess<'de>>(self, prod: A) -> Result<(), A::Error> {\n        self.visit_named_product(prod).map(|_| ())\n    }\n}\n\n/// What kind of product is this?\n#[derive(Clone, Copy)]\npub enum ProductKind {\n    // A normal product.\n    Normal,\n    /// A product in the context of reducer arguments.\n    ReducerArgs,\n}\n\n/// Provides a [`ProductVisitor`] with access to each element of the unnamed product in the input.\n///\n/// This is a trait that a [`Deserializer`] passes to a [`ProductVisitor`] implementation.\npub trait SeqProductAccess<'de> {\n    /// The error type that can be returned if some error occurs during deserialization.\n    type Error: Error;\n\n    /// Deserializes an `T` from the input.\n    ///\n    /// Returns `Ok(Some(value))` for the next element in the product,\n    /// or `Ok(None)` if there are no more remaining items.\n    ///\n    /// This method exists as a convenience for [`Deserialize`] implementations.\n    /// [`SeqProductAccess`] implementations should not override the default behavior.\n    fn next_element<T: Deserialize<'de>>(&mut self) -> Result<Option<T>, Self::Error> {\n        self.next_element_seed(PhantomData)\n    }\n\n    /// Statefully validates `T::Output` from the input provided a `seed` value.\n    ///\n    /// Returns `Ok(Some(()))` for the next element in the unnamed product,\n    /// or `Ok(None)` if there are no more remaining items.\n    fn validate_next_element<T: Deserialize<'de>>(&mut self) -> Result<Option<()>, Self::Error> {\n        self.validate_next_element_seed(PhantomData::<T>)\n    }\n\n    /// Statefully deserializes `T::Output` from the input provided a `seed` value.\n    ///\n    /// Returns `Ok(Some(value))` for the next element in the unnamed product,\n    /// or `Ok(None)` if there are no more remaining items.\n    ///\n    /// [`Deserialize`] implementations should typically use\n    /// [`next_element`](SeqProductAccess::next_element) instead.\n    fn next_element_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<T::Output>, Self::Error>;\n\n    /// Statefully validates `T::Output` from the input provided a `seed` value.\n    ///\n    /// Returns `Ok(Some(()))` for the next element in the unnamed product,\n    /// or `Ok(None)` if there are no more remaining items.\n    fn validate_next_element_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<Option<()>, Self::Error> {\n        self.next_element_seed(seed).map(|opt| opt.map(|_| ()))\n    }\n}\n\n/// Provides a [`ProductVisitor`] with access to each element of the named product in the input.\n///\n/// This is a trait that a [`Deserializer`] passes to a [`ProductVisitor`] implementation.\npub trait NamedProductAccess<'de> {\n    /// The error type that can be returned if some error occurs during deserialization.\n    type Error: Error;\n\n    /// Deserializes field name of type `V::Output`\n    /// from the input using a visitor provided by the deserializer.\n    fn get_field_ident<V: FieldNameVisitor<'de>>(&mut self, visitor: V) -> Result<Option<V::Output>, Self::Error>;\n\n    /// Deserializes field value of type `T` from the input.\n    ///\n    /// This method exists as a convenience for [`Deserialize`] implementations.\n    /// [`NamedProductAccess`] implementations should not override the default behavior.\n    fn get_field_value<T: Deserialize<'de>>(&mut self) -> Result<T, Self::Error> {\n        self.get_field_value_seed(PhantomData)\n    }\n\n    /// Deserializes field value of type `T` from the input.\n    ///\n    /// This method exists as a convenience for [`Deserialize`] implementations.\n    /// [`NamedProductAccess`] implementations should not override the default behavior.\n    fn validate_field_value<T: Deserialize<'de>>(&mut self) -> Result<(), Self::Error> {\n        self.validate_field_value_seed(PhantomData::<T>)\n    }\n\n    /// Statefully deserializes the field value `T::Output` from the input provided a `seed` value.\n    ///\n    /// [`Deserialize`] implementations should typically use\n    /// [`next_element`](NamedProductAccess::get_field_value) instead.\n    fn get_field_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<T::Output, Self::Error>;\n\n    /// Statefully validates the field value `T::Output` from the input provided a `seed` value.\n    fn validate_field_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<(), Self::Error> {\n        self.get_field_value_seed(seed).map(|_| ())\n    }\n}\n\n/// Visitor used to deserialize the name of a field.\npub trait FieldNameVisitor<'de> {\n    /// The resulting field name.\n    type Output;\n\n    /// The sort of product deserialized.\n    fn kind(&self) -> ProductKind {\n        ProductKind::Normal\n    }\n\n    /// Provides a list of valid field names.\n    ///\n    /// Where `None` is yielded, this indicates a nameless field.\n    fn field_names(&self) -> impl '_ + Iterator<Item = Option<&str>>;\n\n    /// Deserializes the name of a field using `name`.\n    fn visit<E: Error>(self, name: &str) -> Result<Self::Output, E>;\n\n    /// Deserializes the name of a field using `index`.\n    ///\n    /// Should only be called when `index` is already known to exist\n    /// and is expected to panic otherwise.\n    fn visit_seq(self, index: usize) -> Self::Output;\n}\n\n/// A visitor walking through a [`Deserializer`] for sums.\n///\n/// This side is provided by a [`Deserialize`] implementation\n/// when calling [`Deserializer::deserialize_sum`].\npub trait SumVisitor<'de> {\n    /// The resulting sum.\n    type Output;\n\n    /// Returns the name of the sum, if any.\n    fn sum_name(&self) -> Option<&str>;\n\n    /// Returns whether an option is expected.\n    ///\n    /// The provided implementation does not.\n    fn is_option(&self) -> bool {\n        false\n    }\n\n    /// Drives the deserialization of a sum value.\n    ///\n    /// This method will ask the data format ([`A: SumAccess`][SumAccess])\n    /// which variant of the sum to select in terms of a variant name / tag.\n    /// `A` will use a [`VariantVisitor`], that `SumVisitor` has provided,\n    /// to translate into something that is meaningful for `visit_sum`, e.g., an index.\n    ///\n    /// The data format will also return an object ([`VariantAccess`])\n    /// that can deserialize the contents of the variant.\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error>;\n\n    /// Drives the validation of a sum value.\n    ///\n    /// This method will ask the data format ([`A: SumAccess`][SumAccess])\n    /// which variant of the sum to select in terms of a variant name / tag.\n    /// `A` will use a [`VariantVisitor`], that `SumVisitor` has provided,\n    /// to translate into something that is meaningful for `visit_sum`, e.g., an index.\n    ///\n    /// The data format will also return an object ([`VariantAccess`])\n    /// that can validate the contents of the variant.\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error>;\n}\n\n/// Provides a [`SumVisitor`] access to the data of a sum in the input.\n///\n/// An `A: SumAccess` object is created by the [`D: Deserializer`]\n/// which passes `A` to a [`V: SumVisitor`] that `D` in turn was passed.\n/// `A` is then used by `V` to split tag and value input apart.\npub trait SumAccess<'de> {\n    /// The error type that can be returned if some error occurs during deserialization.\n    type Error: Error;\n\n    /// The visitor used to deserialize the content of the sum variant.\n    type Variant: VariantAccess<'de, Error = Self::Error>;\n\n    /// Called to identify which variant to deserialize.\n    /// Returns a tuple with the result of identification (`V::Output`)\n    /// and the input to variant data deserialization.\n    ///\n    /// The `visitor` is provided by the [`Deserializer`].\n    /// This method is typically called from [`SumVisitor::visit_sum`]\n    /// which will provide the [`V: VariantVisitor`](VariantVisitor).\n    fn variant<V: VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error>;\n}\n\n/// A visitor passed from [`SumVisitor`] to [`SumAccess::variant`]\n/// which the latter uses to decide what variant to deserialize.\npub trait VariantVisitor<'de> {\n    /// The result of identifying a variant, e.g., some index type.\n    type Output;\n\n    /// Provides a list of variant names.\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str>;\n\n    /// Identify the variant based on `tag`.\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E>;\n\n    /// Identify the variant based on `name`.\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E>;\n}\n\n/// A visitor passed from [`SumAccess`] to [`SumVisitor::visit_sum`]\n/// which the latter uses to deserialize the data of a selected variant.\npub trait VariantAccess<'de>: Sized {\n    type Error: Error;\n\n    /// Called when deserializing the contents of a sum variant.\n    ///\n    /// This method exists as a convenience for [`Deserialize`] implementations.\n    fn deserialize<T: Deserialize<'de>>(self) -> Result<T, Self::Error> {\n        self.deserialize_seed(PhantomData)\n    }\n\n    /// Called when deserializing the contents of a sum variant, and provided with a `seed` value.\n    fn deserialize_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error>;\n\n    /// Called when validating the contents of a sum variant.\n    ///\n    /// This method exists as a convenience for [`Deserialize`] implementations.\n    fn validate<T: Deserialize<'de>>(self) -> Result<(), Self::Error> {\n        self.validate_seed(PhantomData::<T>)\n    }\n\n    /// Called when validating the contents of a sum variant, and provided with a `seed` value.\n    fn validate_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<(), Self::Error> {\n        self.deserialize_seed(seed).map(|_| ())\n    }\n}\n\n/// A `SliceVisitor` is provided a slice `T` of some elements by a [`Deserializer`]\n/// and is tasked with translating this slice to the `Output` type.\npub trait SliceVisitor<'de, T: ToOwned + ?Sized>: Sized {\n    /// The output produced by this visitor.\n    type Output;\n\n    /// The input contains a slice.\n    ///\n    /// The lifetime of the slice is ephemeral\n    /// and it may be destroyed after this method returns.\n    fn visit<E: Error>(self, slice: &T) -> Result<Self::Output, E>;\n\n    /// The input contains a slice and ownership of the slice is being given to the [`SliceVisitor`].\n    fn visit_owned<E: Error>(self, buf: T::Owned) -> Result<Self::Output, E> {\n        self.visit(buf.borrow())\n    }\n\n    /// The input contains a slice that lives at least as long (`'de`) as the [`Deserializer`].\n    fn visit_borrowed<E: Error>(self, borrowed_slice: &'de T) -> Result<Self::Output, E> {\n        self.visit(borrowed_slice)\n    }\n}\n\n/// A visitor walking through a [`Deserializer`] for arrays.\npub trait ArrayVisitor<'de, T>: Sized {\n    /// The output produced by this visitor.\n    type Output;\n\n    /// The input contains an array, deserialize it.\n    fn visit<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<Self::Output, A::Error>;\n\n    /// The input contains an array, but just validate it, don't deserialize.\n    fn validate<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<(), A::Error> {\n        let _ = self.visit(vec)?;\n        Ok(())\n    }\n}\n\n/// Provides an [`ArrayVisitor`] with access to each element of the array in the input.\n///\n/// This is a trait that a [`Deserializer`] passes to an [`ArrayVisitor`] implementation.\npub trait ArrayAccess<'de> {\n    /// The element / base type of the array.\n    type Element;\n\n    /// The error type that can be returned if some error occurs during deserialization.\n    type Error: Error;\n\n    /// This returns `Ok(Some(value))` for the next element in the array,\n    /// or `Ok(None)` if there are no more remaining elements.\n    fn next_element(&mut self) -> Result<Option<Self::Element>, Self::Error>;\n\n    /// This returns `Ok(Some(()))` for the next element in the array,\n    /// or `Ok(None)` if there are no more remaining elements.\n    fn validate_next_element(&mut self) -> Result<Option<()>, Self::Error> {\n        let opt = self.next_element()?;\n        Ok(opt.map(|_| ()))\n    }\n\n    /// Returns the number of elements remaining in the array, if known.\n    fn size_hint(&self) -> Option<usize> {\n        None\n    }\n}\n\n/// `DeserializeSeed` is the stateful form of the [`Deserialize`] trait.\npub trait DeserializeSeed<'de>: Sized {\n    /// The type produced by using this seed.\n    type Output;\n\n    /// Equivalent to the more common [`Deserialize::deserialize`] associated function,\n    /// except with some initial piece of data (the seed `self`) passed in.\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error>;\n\n    /// Validate that the input is of the correct form for this seed.\n    ///\n    /// The default implementation simply deserializes the input and discards the result,\n    /// but implementations can override this to perform more efficient validation\n    /// without fully deserializing the input.\n    fn validate<D: Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {\n        let _ = self.deserialize(deserializer)?;\n        Ok(())\n    }\n}\n\nuse crate::de::impls::BorrowedSliceVisitor;\npub use spacetimedb_bindings_macro::Deserialize;\n\n/// A data structure that can be deserialized from any data format supported by the SpacetimeDB Algebraic Type System.\n///\n/// In most cases, implementations of `Deserialize` may be `#[derive(Deserialize)]`d.\n///\n/// The `Deserialize` trait in SATS performs the same function as `serde::Deserialize` in [`serde`].\n/// See the documentation of `serde::Deserialize` for more information of the data model.\n///\n/// The lifetime `'de` allows us to deserialize lifetime-generic types in a zero-copy fashion.\n///\n/// Do not manually implement this trait unless you know what you are doing.\n/// Implementations must be consistent with `Serialize for T`, `SpacetimeType for T` and `Serialize, Deserialize for AlgebraicValue`.\n/// Implementations that are inconsistent across these traits may result in data loss.\n///\n/// [`serde`]: https://crates.io/crates/serde\npub trait Deserialize<'de>: Sized {\n    /// Deserialize this value from the given `deserializer`.\n    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>;\n\n    #[doc(hidden)]\n    /// Deserialize this value from the given the BSATN `deserializer`.\n    fn deserialize_from_bsatn<R: BufReader<'de>>(\n        deserializer: bsatn::Deserializer<'de, R>,\n    ) -> Result<Self, bsatn::DecodeError> {\n        Self::deserialize(deserializer)\n    }\n\n    /// used in the Deserialize for Vec<T> impl to allow specializing deserializing Vec<T> as bytes\n    #[doc(hidden)]\n    #[inline(always)]\n    fn __deserialize_vec<D: Deserializer<'de>>(deserializer: D) -> Result<Vec<Self>, D::Error> {\n        deserializer.deserialize_array(BasicVecVisitor)\n    }\n\n    #[doc(hidden)]\n    #[inline(always)]\n    fn __deserialize_array<D: Deserializer<'de>, const N: usize>(deserializer: D) -> Result<[Self; N], D::Error> {\n        deserializer.deserialize_array(BasicArrayVisitor)\n    }\n\n    #[doc(hidden)]\n    #[inline(always)]\n    /// Validate that the input is of the correct form for this type.\n    ///\n    /// The default implementation simply deserializes the input and discards the result,\n    /// but implementations can override this to perform more efficient validation\n    /// without fully deserializing the input.\n    fn validate<D: Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> {\n        let _ = Self::deserialize(deserializer)?;\n        Ok(())\n    }\n}\n\n/// A data structure that can be deserialized in SATS\n/// without borrowing any data from the deserializer.\npub trait DeserializeOwned: for<'de> Deserialize<'de> {}\nimpl<T: for<'de> Deserialize<'de>> DeserializeOwned for T {}\n\nimpl<'de, T: Deserialize<'de>> DeserializeSeed<'de> for PhantomData<T> {\n    type Output = T;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        T::deserialize(deserializer)\n    }\n}\n\n/// A vector with two operations: `with_capacity` and `push`.\npub trait GrowingVec<T> {\n    /// Create the collection with the given capacity.\n    fn try_with_capacity<E: Error>(cap: usize) -> Result<Self, E>\n    where\n        Self: Sized;\n\n    /// Push to the vector the `elem`.\n    fn push(&mut self, elem: T);\n}\n\nimpl<T> GrowingVec<T> for Vec<T> {\n    fn try_with_capacity<E: Error>(cap: usize) -> Result<Self, E>\n    where\n        Self: Sized,\n    {\n        let mut vec = Vec::new();\n        vec.try_reserve_exact(cap).map_err(|_| E::allocation_failed(cap))?;\n        Ok(vec)\n    }\n    fn push(&mut self, elem: T) {\n        self.push(elem)\n    }\n}\n\nimpl<T, const N: usize> GrowingVec<T> for SmallVec<[T; N]> {\n    fn try_with_capacity<E: Error>(cap: usize) -> Result<Self, E>\n    where\n        Self: Sized,\n    {\n        let mut vec = Self::new();\n        vec.try_reserve_exact(cap).map_err(|_| E::allocation_failed(cap))?;\n        Ok(vec)\n    }\n    fn push(&mut self, elem: T) {\n        self.push(elem)\n    }\n}\n\n/// A basic implementation of `ArrayVisitor::visit` using the provided size hint.\npub fn array_visit<'de, A: ArrayAccess<'de>, V: GrowingVec<A::Element>>(mut access: A) -> Result<V, A::Error> {\n    let mut v = V::try_with_capacity(access.size_hint().unwrap_or(0))?;\n    while let Some(x) = access.next_element()? {\n        v.push(x)\n    }\n    Ok(v)\n}\n\n/// A basic implementation of `ArrayVisitor::validate`.\npub fn array_validate<'de, A: ArrayAccess<'de>>(mut access: A) -> Result<(), A::Error> {\n    while access.validate_next_element()?.is_some() {}\n    Ok(())\n}\n\n/// An implementation of [`ArrayVisitor<'de, T>`] where the output is a `Vec<T>`.\npub struct BasicVecVisitor;\n\nimpl<'de, T> ArrayVisitor<'de, T> for BasicVecVisitor {\n    type Output = Vec<T>;\n\n    fn visit<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<Self::Output, A::Error> {\n        array_visit(vec)\n    }\n\n    fn validate<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<(), A::Error> {\n        array_validate(vec)\n    }\n}\n\n/// An implementation of [`ArrayVisitor<'de, T>`] where the output is a `SmallVec<[T; N]>`.\npub struct BasicSmallVecVisitor<const N: usize>;\n\nimpl<'de, T, const N: usize> ArrayVisitor<'de, T> for BasicSmallVecVisitor<N> {\n    type Output = SmallVec<[T; N]>;\n\n    fn visit<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<Self::Output, A::Error> {\n        array_visit(vec)\n    }\n\n    fn validate<A: ArrayAccess<'de, Element = T>>(self, vec: A) -> Result<(), A::Error> {\n        array_validate(vec)\n    }\n}\n\n/// An implementation of [`ArrayVisitor<'de, T>`] where the output is a `[T; N]`.\nstruct BasicArrayVisitor<const N: usize>;\n\nimpl<'de, T, const N: usize> ArrayVisitor<'de, T> for BasicArrayVisitor<N> {\n    type Output = [T; N];\n\n    fn visit<A: ArrayAccess<'de, Element = T>>(self, mut vec: A) -> Result<Self::Output, A::Error> {\n        let mut v = arrayvec::ArrayVec::<T, N>::new();\n        while let Some(el) = vec.next_element()? {\n            v.try_push(el)\n                .map_err(|_| Error::custom(\"too many elements for array\"))?\n        }\n        v.into_inner().map_err(|_| Error::custom(\"too few elements for array\"))\n    }\n\n    fn validate<A: ArrayAccess<'de, Element = T>>(self, mut vec: A) -> Result<(), A::Error> {\n        // Validate each element and count.\n        let mut count = 0;\n        while vec.next_element()?.is_some() {\n            count += 1;\n        }\n        // Don't do this in the loop,\n        // as we bias towards there not being any errors.\n        if count > N {\n            return Err(Error::custom(\"too many elements for array\"));\n        }\n        if count < N {\n            return Err(Error::custom(\"too few elements for array\"));\n        }\n        Ok(())\n    }\n}\n\n/// Provided a list of names,\n/// returns a human readable list of all the names,\n/// or `None` in the case of an empty list of names.\nfn one_of_names<'a, I: Iterator<Item = Option<&'a str>>>(names: impl Fn() -> I) -> Option<impl fmt::Display> {\n    // Count how many names there are.\n    let count = names().count();\n\n    // There was at least one name; render those names.\n    (count != 0).then(move || {\n        fmt_fn(move |f| {\n            let mut anon_name = 0;\n            // An example of what happens for names \"foo\", \"bar\", and \"baz\":\n            //\n            // count = 1 -> \"`foo`\"\n            //       = 2 -> \"`foo` or `bar`\"\n            //       > 2 -> \"one of `foo`, `bar`, or `baz`\"\n            for (index, mut name) in names().enumerate() {\n                let mut name_buf: String = String::new();\n                let name = name.get_or_insert_with(|| {\n                    name_buf = format!(\"{anon_name}\");\n                    anon_name += 1;\n                    &name_buf\n                });\n                match (count, index) {\n                    (1, _) => write!(f, \"`{name}`\"),\n                    (2, 1) => write!(f, \"`{name}`\"),\n                    (2, 2) => write!(f, \"`or `{name}`\"),\n                    (_, 1) => write!(f, \"one of `{name}`\"),\n                    (c, i) if i < c => write!(f, \", `{name}`\"),\n                    (_, _) => write!(f, \", `, or {name}`\"),\n                }?;\n            }\n\n            Ok(())\n        })\n    })\n}\n\n/// Deserializes `none` variant of an optional value.\npub struct NoneAccess<E>(PhantomData<E>);\n\nimpl<E: Error> NoneAccess<E> {\n    /// Returns a new [`NoneAccess`].\n    pub fn new() -> Self {\n        Self(PhantomData)\n    }\n}\n\nimpl<E: Error> Default for NoneAccess<E> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'de, E: Error> SumAccess<'de> for NoneAccess<E> {\n    type Error = E;\n    type Variant = Self;\n\n    fn variant<V: VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        visitor.visit_name(\"none\").map(|var| (var, self))\n    }\n}\nimpl<'de, E: Error> VariantAccess<'de> for NoneAccess<E> {\n    type Error = E;\n    fn deserialize_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(UnitAccess::new())\n    }\n}\n\n/// Deserializes `some` variant of an optional value.\npub struct SomeAccess<D>(D);\n\nimpl<D> SomeAccess<D> {\n    /// Returns a new [`SomeAccess`] with a given deserializer for the `some` variant.\n    pub fn new(de: D) -> Self {\n        Self(de)\n    }\n}\n\nimpl<'de, D: Deserializer<'de>> SumAccess<'de> for SomeAccess<D> {\n    type Error = D::Error;\n    type Variant = Self;\n\n    fn variant<V: VariantVisitor<'de>>(self, visitor: V) -> Result<(V::Output, Self::Variant), Self::Error> {\n        visitor.visit_name(\"some\").map(|var| (var, self))\n    }\n}\n\nimpl<'de, D: Deserializer<'de>> VariantAccess<'de> for SomeAccess<D> {\n    type Error = D::Error;\n    fn deserialize_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Output, Self::Error> {\n        seed.deserialize(self.0)\n    }\n    fn validate_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<(), Self::Error> {\n        seed.validate(self.0)\n    }\n}\n\n/// A `Deserializer` that represents a unit value.\n// used in the implementation of `VariantAccess for NoneAccess`\npub struct UnitAccess<E>(PhantomData<E>);\n\nimpl<E: Error> UnitAccess<E> {\n    /// Returns a new [`UnitAccess`].\n    pub fn new() -> Self {\n        Self(PhantomData)\n    }\n}\n\nimpl<E: Error> Default for UnitAccess<E> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'de, E: Error> SeqProductAccess<'de> for UnitAccess<E> {\n    type Error = E;\n\n    fn next_element_seed<T: DeserializeSeed<'de>>(&mut self, _seed: T) -> Result<Option<T::Output>, Self::Error> {\n        Ok(None)\n    }\n}\n\nimpl<'de, E: Error> NamedProductAccess<'de> for UnitAccess<E> {\n    type Error = E;\n\n    fn get_field_ident<V: FieldNameVisitor<'de>>(&mut self, _visitor: V) -> Result<Option<V::Output>, Self::Error> {\n        Ok(None)\n    }\n\n    fn get_field_value_seed<T: DeserializeSeed<'de>>(&mut self, _seed: T) -> Result<T::Output, Self::Error> {\n        unreachable!()\n    }\n}\n\nimpl<'de, E: Error> Deserializer<'de> for UnitAccess<E> {\n    type Error = E;\n\n    fn deserialize_product<V: ProductVisitor<'de>>(self, visitor: V) -> Result<V::Output, Self::Error> {\n        visitor.visit_seq_product(self)\n    }\n\n    fn deserialize_sum<V: SumVisitor<'de>>(self, _visitor: V) -> Result<V::Output, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_bool(self) -> Result<bool, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u8(self) -> Result<u8, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u16(self) -> Result<u16, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u32(self) -> Result<u32, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u64(self) -> Result<u64, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u128(self) -> Result<u128, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_u256(self) -> Result<u256, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i8(self) -> Result<i8, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i16(self) -> Result<i16, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i32(self) -> Result<i32, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i64(self) -> Result<i64, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i128(self) -> Result<i128, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_i256(self) -> Result<i256, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_f32(self) -> Result<f32, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_f64(self) -> Result<f64, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_str<V: SliceVisitor<'de, str>>(self, _visitor: V) -> Result<V::Output, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_bytes<V: SliceVisitor<'de, [u8]>>(self, _visitor: V) -> Result<V::Output, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n\n    fn deserialize_array_seed<V: ArrayVisitor<'de, T::Output>, T: DeserializeSeed<'de> + Clone>(\n        self,\n        _visitor: V,\n        _seed: T,\n    ) -> Result<V::Output, Self::Error> {\n        Err(E::custom(\"invalid type\"))\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/hash.rs",
    "content": "use crate::hex::HexString;\nuse crate::{impl_deserialize, impl_serialize, impl_st, u256, AlgebraicType};\nuse core::fmt;\nuse sha3::{Digest, Keccak256};\n\npub const HASH_SIZE: usize = 32;\n\n#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Copy, Hash)]\n#[cfg_attr(any(test, feature = \"proptest\"), derive(proptest_derive::Arbitrary))]\npub struct Hash {\n    pub data: [u8; HASH_SIZE],\n}\n\nimpl_st!([] Hash, AlgebraicType::U256);\nimpl_serialize!([] Hash, (self, ser) => u256::from_le_bytes(self.data).serialize(ser));\nimpl_deserialize!([] Hash, de => Ok(Self { data: <_>::deserialize(de).map(u256::to_le_bytes)? }));\n\n#[cfg(feature = \"metrics_impls\")]\nimpl spacetimedb_metrics::typed_prometheus::AsPrometheusLabel for Hash {\n    fn as_prometheus_str(&self) -> impl AsRef<str> + '_ {\n        self.to_hex()\n    }\n}\n\nimpl Hash {\n    pub const ZERO: Self = Self::from_byte_array([0; HASH_SIZE]);\n\n    pub const fn from_byte_array(data: [u8; HASH_SIZE]) -> Self {\n        Self { data }\n    }\n\n    pub fn from_u256(val: u256) -> Self {\n        Self::from_byte_array(val.to_le_bytes())\n    }\n\n    pub fn to_u256(self) -> u256 {\n        u256::from_le_bytes(self.data)\n    }\n\n    pub fn to_hex(&self) -> HexString<32> {\n        crate::hex::encode(&self.data)\n    }\n\n    pub fn abbreviate(&self) -> &[u8; 16] {\n        self.data[..16].try_into().unwrap()\n    }\n\n    pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, hex::FromHexError> {\n        hex::FromHex::from_hex(hex)\n    }\n}\n\npub fn hash_bytes(bytes: impl AsRef<[u8]>) -> Hash {\n    Hash::from_byte_array(Keccak256::digest(bytes).into())\n}\n\nimpl fmt::Display for Hash {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.pad(&self.to_hex())\n    }\n}\n\nimpl fmt::Debug for Hash {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_tuple(\"Hash\").field(&format_args!(\"{self}\")).finish()\n    }\n}\n\nimpl hex::FromHex for Hash {\n    type Error = hex::FromHexError;\n\n    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {\n        let data = hex::FromHex::from_hex(hex)?;\n        Ok(Hash { data })\n    }\n}\n\n#[cfg(feature = \"serde\")]\nimpl serde::Serialize for Hash {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        crate::ser::serde::serialize_to(self, serializer)\n    }\n}\n#[cfg(feature = \"serde\")]\nimpl<'de> serde::Deserialize<'de> for Hash {\n    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        crate::de::serde::deserialize_from(deserializer)\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/hex.rs",
    "content": "//! Allocation-free hex formatting.\n//!\n//! Given that most, if not all, of the types that we hex-format are of constant byte size (Hash,\n//! ConnectionId, Identity), this hex implementation lets you format to hex without needing to allocate\n//! a `String` on the heap.\n\nuse core::{fmt, ops, str};\n\n#[derive(Copy, Clone)]\npub struct HexString<const N: usize> {\n    s: [HexByte; N],\n}\n\npub fn encode<const N: usize>(bytes: &[u8; N]) -> HexString<N> {\n    let s = bytes.map(HexByte::from_byte);\n    HexString { s }\n}\n\nimpl<const N: usize> HexString<N> {\n    #[inline(always)]\n    pub fn as_str(&self) -> &str {\n        self\n    }\n\n    #[inline(always)]\n    pub fn as_bytes(&self) -> &[u8] {\n        str::as_bytes(self)\n    }\n}\n\nimpl<const N: usize> ops::Deref for HexString<N> {\n    type Target = str;\n    #[inline(always)]\n    fn deref(&self) -> &Self::Target {\n        HexByte::as_str(&self.s)\n    }\n}\n\nimpl<const N: usize> From<&'_ HexString<N>> for String {\n    fn from(hex: &HexString<N>) -> Self {\n        hex.as_str().into()\n    }\n}\n\nimpl<const N: usize> From<HexString<N>> for String {\n    fn from(hex: HexString<N>) -> Self {\n        hex.as_str().into()\n    }\n}\n\nimpl<const N: usize> AsRef<str> for HexString<N> {\n    fn as_ref(&self) -> &str {\n        self\n    }\n}\n\nimpl<const N: usize> AsRef<[u8]> for HexString<N> {\n    fn as_ref(&self) -> &[u8] {\n        self.as_bytes()\n    }\n}\n\nimpl<const N: usize> fmt::Display for HexString<N> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.pad(self)\n    }\n}\n\nimpl<const N: usize> fmt::Debug for HexString<N> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.pad(self)\n    }\n}\n\n#[derive(Copy, Clone)]\n#[repr(u8)]\nenum HexNybble {\n    _0 = b'0',\n    _1 = b'1',\n    _2 = b'2',\n    _3 = b'3',\n    _4 = b'4',\n    _5 = b'5',\n    _6 = b'6',\n    _7 = b'7',\n    _8 = b'8',\n    _9 = b'9',\n    _A = b'a',\n    _B = b'b',\n    _C = b'c',\n    _D = b'd',\n    _E = b'e',\n    _F = b'f',\n}\n\n#[rustfmt::skip]\nconst NYBBLE_LOOKUP: [HexNybble; 16] = [\n    HexNybble::_0, HexNybble::_1, HexNybble::_2, HexNybble::_3, HexNybble::_4, HexNybble::_5, HexNybble::_6, HexNybble::_7,\n    HexNybble::_8, HexNybble::_9, HexNybble::_A, HexNybble::_B, HexNybble::_C, HexNybble::_D, HexNybble::_E, HexNybble::_F,\n];\n\n#[derive(Copy, Clone)]\n#[repr(transparent)]\nstruct HexByte([HexNybble; 2]);\n\nstatic BYTE_LOOKUP: [HexByte; 256] = {\n    let mut arr = [HexByte::ZERO; 256];\n    let mut i = 0;\n    while i < arr.len() {\n        let (hi, lo) = (i >> 4, i & 0x0F);\n        let byte = HexByte([NYBBLE_LOOKUP[hi], NYBBLE_LOOKUP[lo]]);\n        arr[i] = byte;\n        i += 1;\n    }\n    arr\n};\n\nimpl HexByte {\n    const ZERO: HexByte = HexByte([HexNybble::_0, HexNybble::_0]);\n\n    #[inline(always)]\n    fn from_byte(b: u8) -> Self {\n        BYTE_LOOKUP[b as usize]\n    }\n\n    #[inline(always)]\n    fn as_nybbles(this: &[Self]) -> &[HexNybble] {\n        // SAFETY: HexByte is repr(transparent) over [HexNybble; 2]\n        let arrays = unsafe { &*(this as *const [HexByte] as *const [[HexNybble; 2]]) };\n        arrays.as_flattened()\n    }\n    #[inline(always)]\n    fn as_str(this: &[Self]) -> &str {\n        let nybbles = HexByte::as_nybbles(this);\n        // SAFETY: a HexNybble can only be valid ascii, which is always valid utf8\n        unsafe { str::from_utf8_unchecked(HexNybble::as_bytes(nybbles)) }\n    }\n}\n\nimpl HexNybble {\n    #[inline(always)]\n    fn as_bytes(this: &[Self]) -> &[u8] {\n        // SAFETY: HexNybble is repr(u8)\n        unsafe { &*(this as *const [HexNybble] as *const [u8]) }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/layout.rs",
    "content": "//! Defines [`Layout`], which encompasses the fixed size and alignment of an object,\n//! e.g., a row, or a column, or some other sub-division of a row.\n//!\n//! `Layout` annotated versions of SATS types are also provided,\n//! such as [`ProductTypeLayout`] and [`AlgebraicTypeLayout`].\n//! These, and others, determine what the layout of objects typed at those types are.\n//! They also implement [`HasLayout`] which generalizes over layout annotated types.\n\nuse crate::{\n    de::{\n        Deserialize, DeserializeSeed, Deserializer, Error, NamedProductAccess, ProductVisitor, SeqProductAccess,\n        SumAccess, SumVisitor, VariantAccess as _, VariantVisitor,\n    },\n    i256, impl_deserialize, impl_serialize,\n    raw_identifier::RawIdentifier,\n    sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG},\n    u256, AlgebraicType, AlgebraicValue, ArrayType, ProductType, ProductTypeElement, ProductValue, SumType,\n    SumTypeVariant, SumValue, WithTypespace,\n};\nuse core::ops::{Index, Mul};\nuse core::{mem, ops::Deref};\nuse derive_more::{Add, Sub};\nuse enum_as_inner::EnumAsInner;\nuse std::sync::Arc;\n\n/// Aligns a `base` offset to the `required_alignment` (in the positive direction) and returns it.\n///\n/// When `base` is already aligned, `base` will be returned.\npub const fn align_to(base: usize, required_alignment: usize) -> usize {\n    if required_alignment == 0 {\n        // Avoid computing the remainder below, as that panics.\n        // This path is reachable for e.g., uninhabited types.\n        base\n    } else {\n        let misalignment = base % required_alignment;\n        if misalignment == 0 {\n            base\n        } else {\n            let padding = required_alignment - misalignment;\n            base + padding\n        }\n    }\n}\n\n/// The size of something in page storage in bytes.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Add, Sub)]\npub struct Size(pub u16);\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for Size {}\n\n// We need to be able to serialize and deserialize `Size` because they appear in the `PageHeader`.\nimpl_serialize!([] Size, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] Size, de => u16::deserialize(de).map(Size));\n\nimpl Size {\n    /// Returns the size for use in `usize` computations.\n    #[inline]\n    #[allow(clippy::len_without_is_empty)]\n    pub const fn len(self) -> usize {\n        self.0 as usize\n    }\n}\n\nimpl Mul<usize> for Size {\n    type Output = Size;\n\n    #[inline]\n    fn mul(self, rhs: usize) -> Self::Output {\n        Size((self.len() * rhs) as u16)\n    }\n}\n\n// TODO(perf): try out using just an offset relative to the row start itself.\n// The main drawback is that nested types start at non-zero.\n// Primitives and var-len refs now also need to store more data\n// but this shouldn't cost anything as this would be padding anyways.\n// The main upside is that ser/de/eq/row_hash\n// need not do any alignment adjustments and carry a current offset.\n// This removes a data dependence and could possibly improve instruction-level parallelism.\n\n/// The layout of a fixed object\n/// or the layout that fixed objects of a type will have.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct Layout {\n    /// The size object / expected object in bytes.\n    pub size: u16,\n    /// The alignment of the object / expected object in bytes.\n    pub align: u16,\n    /// Whether this is the layout of a fixed object\n    /// and not the layout of a var-len type's fixed component.\n    pub fixed: bool,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for Layout {}\n\n/// A type which knows what its layout is.\n///\n/// This does not refer to layout in Rust.\npub trait HasLayout {\n    /// Returns the layout for objects of this type.\n    fn layout(&self) -> &Layout;\n\n    /// Returns the size, in bytes, for objects of this type.\n    ///\n    /// Intentionally returns `usize` rather than [`Size`],\n    /// so callers will have to explicitly convert\n    /// with [`row_size_for_bytes`].\n    fn size(&self) -> usize {\n        self.layout().size as usize\n    }\n\n    /// Returns the alignment, in bytes, for objects of this type.\n    ///\n    /// Intentionally returns `usize` rather than [`Size`],\n    /// so callers will have to explicitly convert\n    /// with [`row_size_for_bytes`].\n    fn align(&self) -> usize {\n        self.layout().align as usize\n    }\n}\n\n/// Mostly a mirror of [`AlgebraicType`] annotated with a [`Layout`].\n///\n/// Notable differences from `AlgebraicType`:\n///\n/// - `Ref`s are not supported.\n///   Supporting recursive types remains a TODO(future-work).\n///   Note that the previous Spacetime datastore did not support recursive types in tables.\n///\n/// - Scalar types (`ty.is_scalar()`) are separated into [`PrimitiveType`] (atomically-sized types like integers).\n/// - Variable length types are separated into [`VarLenType`] (strings, arrays, and maps).\n///   This separation allows cleaner pattern-matching, e.g. in `HasLayout::layout`,\n///   where `VarLenType` returns a static ref to [`VAR_LEN_REF_LAYOUT`],\n///   and `PrimitiveType` dispatches on its variant to return a static ref\n///   to a type-specific `Layout`.\n#[derive(Debug, PartialEq, Eq, Clone, EnumAsInner)]\npub enum AlgebraicTypeLayout {\n    /// A sum type, annotated with its layout.\n    Sum(SumTypeLayout),\n    /// A product type, annotated with its layout.\n    Product(ProductTypeLayout),\n    /// A primitive type, annotated with its layout.\n    Primitive(PrimitiveType),\n    /// A variable length type, annotated with its layout.\n    VarLen(VarLenType),\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for AlgebraicTypeLayout {\n    fn heap_usage(&self) -> usize {\n        match self {\n            AlgebraicTypeLayout::Sum(x) => x.heap_usage(),\n            AlgebraicTypeLayout::Product(x) => x.heap_usage(),\n            AlgebraicTypeLayout::Primitive(x) => x.heap_usage(),\n            AlgebraicTypeLayout::VarLen(x) => x.heap_usage(),\n        }\n    }\n}\n\nimpl HasLayout for AlgebraicTypeLayout {\n    fn layout(&self) -> &Layout {\n        match self {\n            Self::Sum(ty) => ty.layout(),\n            Self::Product(ty) => ty.layout(),\n            Self::Primitive(ty) => ty.layout(),\n            Self::VarLen(ty) => ty.layout(),\n        }\n    }\n}\n\n#[allow(non_upper_case_globals)]\nimpl AlgebraicTypeLayout {\n    pub const Bool: Self = Self::Primitive(PrimitiveType::Bool);\n    pub const I8: Self = Self::Primitive(PrimitiveType::I8);\n    pub const U8: Self = Self::Primitive(PrimitiveType::U8);\n    pub const I16: Self = Self::Primitive(PrimitiveType::I16);\n    pub const U16: Self = Self::Primitive(PrimitiveType::U16);\n    pub const I32: Self = Self::Primitive(PrimitiveType::I32);\n    pub const U32: Self = Self::Primitive(PrimitiveType::U32);\n    pub const I64: Self = Self::Primitive(PrimitiveType::I64);\n    pub const U64: Self = Self::Primitive(PrimitiveType::U64);\n    pub const I128: Self = Self::Primitive(PrimitiveType::I128);\n    pub const U128: Self = Self::Primitive(PrimitiveType::U128);\n    pub const I256: Self = Self::Primitive(PrimitiveType::I256);\n    pub const U256: Self = Self::Primitive(PrimitiveType::U256);\n    pub const F32: Self = Self::Primitive(PrimitiveType::F32);\n    pub const F64: Self = Self::Primitive(PrimitiveType::F64);\n    pub const String: Self = Self::VarLen(VarLenType::String);\n\n    /// Can `self` be changed compatibly to `new`?\n    ///\n    /// See comment on [`IncompatibleTypeLayoutError`] about [`Box`]ing the error return.\n    fn ensure_compatible_with(&self, new: &Self) -> Result<(), Box<IncompatibleTypeLayoutError>> {\n        match (self, new) {\n            (Self::Sum(old), Self::Sum(new)) => old.ensure_compatible_with(new),\n            (Self::Product(old), Self::Product(new)) => old.view().ensure_compatible_with(new.view()),\n            (Self::Primitive(old), Self::Primitive(new)) => {\n                if old == new {\n                    Ok(())\n                } else {\n                    Err(Box::new(IncompatibleTypeLayoutError::DifferentPrimitiveTypes {\n                        old: old.algebraic_type(),\n                        new: new.algebraic_type(),\n                    }))\n                }\n            }\n            (Self::VarLen(VarLenType::Array(old)), Self::VarLen(VarLenType::Array(new))) => {\n                // NOTE(perf, centril): This might clone and heap allocate,\n                // but we don't care to avoid that and optimize right now,\n                // as this is only executed during upgrade / migration,\n                // and that doesn't need to be fast right now.\n                let old = AlgebraicTypeLayout::from(old.elem_ty.deref().clone());\n                let new = AlgebraicTypeLayout::from(new.elem_ty.deref().clone());\n                old.ensure_compatible_with(&new).map_err(|err| {\n                    Box::new(IncompatibleTypeLayoutError::IncompatibleArrayElements {\n                        old: old.algebraic_type(),\n                        new: new.algebraic_type(),\n                        err,\n                    })\n                })\n            }\n            (Self::VarLen(VarLenType::String), Self::VarLen(VarLenType::String)) => Ok(()),\n            _ => Err(Box::new(IncompatibleTypeLayoutError::DifferentKind {\n                old: self.algebraic_type(),\n                new: new.algebraic_type(),\n            })),\n        }\n    }\n}\n\n/// A collection of items, so that we can easily swap out the backing type.\ntype Collection<T> = Box<[T]>;\n\n/// Fixed-length row portions must be at least large enough to store a `FreeCellRef`.\npub const MIN_ROW_SIZE: Size = Size(2);\n\n/// Fixed-length row portions must also be sufficiently aligned to store a `FreeCellRef`.\npub const MIN_ROW_ALIGN: Size = Size(2);\n\n/// Returns the minimum row size needed to store `required_bytes`\n/// accounting for the minimum row size and alignment.\npub const fn row_size_for_bytes(required_bytes: usize) -> Size {\n    // Manual `Ord::max` because that function is not `const`.\n    if required_bytes > MIN_ROW_SIZE.len() {\n        Size(align_to(required_bytes, MIN_ROW_ALIGN.len()) as u16)\n    } else {\n        MIN_ROW_SIZE\n    }\n}\n\n/// Returns the minimum row size needed to store a `T`,\n/// accounting for the minimum row size and alignment.\npub const fn row_size_for_type<T>() -> Size {\n    row_size_for_bytes(mem::size_of::<T>())\n}\n\n/// The type of a row, annotated with a [`Layout`].\n///\n/// This type ensures that the minimum row size is adhered to.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct RowTypeLayout {\n    /// The memoized layout of the product type.\n    pub layout: Layout,\n    /// The fields of the product type with their own layout annotations.\n    ///\n    /// This is `Arc`ed at the row type level\n    /// as clones and drops of this showed up in flamegraphs.\n    /// A [`RowTypeLayout`] will typically be cloned once per table per transaction,\n    /// assuming the table is touched during that transaction.\n    /// This can be expensive for modules that have a lot of reducer calls per second.\n    pub elements: Arc<[ProductTypeElementLayout]>,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for RowTypeLayout {\n    fn heap_usage(&self) -> usize {\n        let Self { layout, elements } = self;\n        layout.heap_usage() + elements.heap_usage()\n    }\n}\n\nimpl RowTypeLayout {\n    /// Returns a view of this row type as a product type.\n    pub fn product(&self) -> ProductTypeLayoutView<'_> {\n        let elements = &*self.elements;\n        let layout = self.layout;\n        ProductTypeLayoutView { layout, elements }\n    }\n\n    /// Returns the row size for this row type.\n    pub fn size(&self) -> Size {\n        Size(self.product().size() as u16)\n    }\n\n    /// Can `self` be changed compatibly to `new`?\n    ///\n    /// If the types are incompatible, returns the incompatible sub-part and the reason.\n    /// See comment on [`IncompatibleTypeLayoutError`] about [`Box`]ing the error return.\n    pub fn ensure_compatible_with(&self, new: &RowTypeLayout) -> Result<(), Box<IncompatibleTypeLayoutError>> {\n        if self.layout != new.layout {\n            return Err(Box::new(IncompatibleTypeLayoutError::LayoutsNotEqual {\n                old: self.layout,\n                new: new.layout,\n            }));\n        }\n        self.product().ensure_compatible_with(new.product())\n    }\n}\n\n/// Reason why two [`RowTypeLayout`]s are incompatible for an auto-migration.\n///\n/// Reported by [`RowTypeLayout::ensure_compatible_with`] and friends.\n/// These methods are expected to return `Ok(())` except in the case of internal SpacetimeDB bugs,\n/// as migrations are validated by `spacetimedb_schema::auto_migrate` before executing,\n/// so we will [`Box`] these errors to keep the returned [`Result`] small and the happy path fast.\n#[derive(thiserror::Error, Debug, Clone)]\npub enum IncompatibleTypeLayoutError {\n    #[error(\"Layout of new type {new:?} does not match layout of old type {old:?}\")]\n    LayoutsNotEqual { old: Layout, new: Layout },\n    #[error(\"Product type elements at index {index} are incompatible: {err}\")]\n    IncompatibleProductElements {\n        index: usize,\n        err: Box<IncompatibleTypeLayoutError>,\n    },\n    #[error(\"Sum type elements in variant {index} are incompatible: {err}\")]\n    IncompatibleSumVariants {\n        index: usize,\n        err: Box<IncompatibleTypeLayoutError>,\n    },\n    #[error(\"New product type {new:?} has {} elements while old product type {old:?} has {} elements\", .new.elements.len(), .old.elements.len())]\n    DifferentElementCounts { old: ProductType, new: ProductType },\n    #[error(\"New sum type {new:?} has {} variants, which is fewer than old sum type {old:?} with {} variants\", .new.variants.len(), .old.variants.len())]\n    RemovedVariants { old: SumType, new: SumType },\n    #[error(\"New primitive type {new:?} is not the same as old primitive type {old:?}\")]\n    DifferentPrimitiveTypes { old: AlgebraicType, new: AlgebraicType },\n    #[error(\"New array element type {new:?} is incompatible with old array element type {old:?}: {err}\")]\n    IncompatibleArrayElements {\n        new: AlgebraicType,\n        old: AlgebraicType,\n        err: Box<IncompatibleTypeLayoutError>,\n    },\n    #[error(\"New type {new:?} is not the same kind (sum, product, primitive, etc.) as old type {old:?}\")]\n    DifferentKind { old: AlgebraicType, new: AlgebraicType },\n}\n\npub enum IncompatibleTypeReason {\n    LayoutsNotEqual,\n}\n\nimpl HasLayout for RowTypeLayout {\n    fn layout(&self) -> &Layout {\n        &self.layout\n    }\n}\n\nimpl Index<usize> for RowTypeLayout {\n    type Output = AlgebraicTypeLayout;\n    fn index(&self, index: usize) -> &Self::Output {\n        &self.elements[index].ty\n    }\n}\n\n/// A mirror of [`ProductType`] annotated with a [`Layout`].\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\npub struct ProductTypeLayoutView<'a> {\n    /// The memoized layout of the product type.\n    pub layout: Layout,\n    /// The fields of the product type with their own layout annotations.\n    pub elements: &'a [ProductTypeElementLayout],\n}\n\nimpl HasLayout for ProductTypeLayoutView<'_> {\n    fn layout(&self) -> &Layout {\n        &self.layout\n    }\n}\n\nimpl ProductTypeLayoutView<'_> {\n    /// Can `self` be changed compatibly to `new`?\n    ///\n    /// See comment on [`IncompatibleTypeLayoutError`] about [`Box`]ing the error return.\n    // Intentionally fail fast rather than combining errors with [`spacetimedb_data_structures::error_stream`]\n    // because we've (at least theoretically) already passed through\n    // `spacetimedb_schema::auto_migrate::ensure_old_ty_upgradable_to_new` to get here,\n    // and that method has proper pretty error reporting with `ErrorStream`.\n    // The error here is for internal debugging.\n    fn ensure_compatible_with(self, new: Self) -> Result<(), Box<IncompatibleTypeLayoutError>> {\n        if self.elements.len() != new.elements.len() {\n            return Err(Box::new(IncompatibleTypeLayoutError::DifferentElementCounts {\n                old: self.product_type(),\n                new: new.product_type(),\n            }));\n        }\n        for (index, (old, new)) in self.elements.iter().zip(new.elements.iter()).enumerate() {\n            if let Err(err) = old.ty.ensure_compatible_with(&new.ty) {\n                return Err(Box::new(IncompatibleTypeLayoutError::IncompatibleProductElements {\n                    index,\n                    err,\n                }));\n            }\n        }\n        Ok(())\n    }\n}\n\n/// A mirror of [`ProductType`] annotated with a [`Layout`].\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct ProductTypeLayout {\n    /// The memoized layout of the product type.\n    pub layout: Layout,\n    /// The fields of the product type with their own layout annotations.\n    pub elements: Collection<ProductTypeElementLayout>,\n}\n\nimpl ProductTypeLayout {\n    /// Returns a view of this row type as a product type.\n    pub fn view(&self) -> ProductTypeLayoutView<'_> {\n        let elements = &*self.elements;\n        let layout = self.layout;\n        ProductTypeLayoutView { layout, elements }\n    }\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for ProductTypeLayout {\n    fn heap_usage(&self) -> usize {\n        let Self { layout, elements } = self;\n        layout.heap_usage() + elements.heap_usage()\n    }\n}\n\nimpl HasLayout for ProductTypeLayout {\n    fn layout(&self) -> &Layout {\n        &self.layout\n    }\n}\n\n/// A mirrior of [`ProductTypeElement`] annotated with a [`Layout`].\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct ProductTypeElementLayout {\n    /// The relative offset of a field's value to its parent product value.\n    pub offset: u16,\n\n    /// The type of the field.\n    pub ty: AlgebraicTypeLayout,\n\n    /// An optional name of the field.\n    ///\n    /// This allows us to convert back to `ProductTypeElement`,\n    /// which we do when reporting type errors.\n    pub name: Option<RawIdentifier>,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for ProductTypeElementLayout {\n    fn heap_usage(&self) -> usize {\n        let Self { offset, ty, name } = self;\n        offset.heap_usage() + ty.heap_usage() + name.heap_usage()\n    }\n}\n\n/// A mirrior of [`SumType`] annotated with a [`Layout`].\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct SumTypeLayout {\n    /// The layout of a sum value of this sum type.\n    pub layout: Layout,\n    /// The variants of the sum type.\n    pub variants: Collection<SumTypeVariantLayout>,\n    /// The relative offset of a sum value's payload for sums of this type.\n    /// Sum value tags are always at offset 0.\n    pub payload_offset: u16,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for SumTypeLayout {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            layout,\n            variants,\n            payload_offset,\n        } = self;\n        layout.heap_usage() + variants.heap_usage() + payload_offset.heap_usage()\n    }\n}\n\nimpl HasLayout for SumTypeLayout {\n    fn layout(&self) -> &Layout {\n        &self.layout\n    }\n}\n\n/// A mirrior of [`SumTypeVariant`] annotated with a [`Layout`].\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct SumTypeVariantLayout {\n    /// The type of the variant.\n    pub ty: AlgebraicTypeLayout,\n\n    /// An optional name of the variant.\n    ///\n    /// This allows us to convert back to `SumTypeVariant`,\n    /// which we do when reporting type errors.\n    pub name: Option<RawIdentifier>,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for SumTypeVariantLayout {\n    fn heap_usage(&self) -> usize {\n        let Self { ty, name } = self;\n        ty.heap_usage() + name.heap_usage()\n    }\n}\n\n/// Scalar types, i.e. bools, integers and floats.\n/// These types do not require a `VarLenRef` indirection when stored in a `spacetimedb_table::table::Table`.\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\npub enum PrimitiveType {\n    Bool,\n    I8,\n    U8,\n    I16,\n    U16,\n    I32,\n    U32,\n    I64,\n    U64,\n    I128,\n    U128,\n    I256,\n    U256,\n    F32,\n    F64,\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for PrimitiveType {}\n\nimpl PrimitiveType {\n    pub fn algebraic_type(&self) -> AlgebraicType {\n        match self {\n            PrimitiveType::Bool => AlgebraicType::Bool,\n            PrimitiveType::I8 => AlgebraicType::I8,\n            PrimitiveType::U8 => AlgebraicType::U8,\n            PrimitiveType::I16 => AlgebraicType::I16,\n            PrimitiveType::U16 => AlgebraicType::U16,\n            PrimitiveType::I32 => AlgebraicType::I32,\n            PrimitiveType::U32 => AlgebraicType::U32,\n            PrimitiveType::I64 => AlgebraicType::I64,\n            PrimitiveType::U64 => AlgebraicType::U64,\n            PrimitiveType::I128 => AlgebraicType::I128,\n            PrimitiveType::U128 => AlgebraicType::U128,\n            PrimitiveType::I256 => AlgebraicType::I256,\n            PrimitiveType::U256 => AlgebraicType::U256,\n            PrimitiveType::F32 => AlgebraicType::F32,\n            PrimitiveType::F64 => AlgebraicType::F64,\n        }\n    }\n}\n\nimpl HasLayout for PrimitiveType {\n    fn layout(&self) -> &'static Layout {\n        match self {\n            Self::Bool | Self::I8 | Self::U8 => &Layout {\n                size: 1,\n                align: 1,\n                fixed: true,\n            },\n            Self::I16 | Self::U16 => &Layout {\n                size: 2,\n                align: 2,\n                fixed: true,\n            },\n            Self::I32 | Self::U32 | Self::F32 => &Layout {\n                size: 4,\n                align: 4,\n                fixed: true,\n            },\n            Self::I64 | Self::U64 | Self::F64 => &Layout {\n                size: 8,\n                align: 8,\n                fixed: true,\n            },\n            Self::I128 | Self::U128 => &Layout {\n                size: 16,\n                align: 16,\n                fixed: true,\n            },\n            Self::I256 | Self::U256 => &Layout {\n                size: 32,\n                align: 32,\n                fixed: true,\n            },\n        }\n    }\n}\n\n/// Types requiring a `VarLenRef` indirection,\n/// i.e. strings, arrays, and maps.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub enum VarLenType {\n    /// The string type corresponds to `AlgebraicType::String`.\n    String,\n    /// An array type. The inner `AlgebraicType` is stored here.\n    ///\n    /// Previously, the outer type, i.e., `AlgebraicType::Array` was stored.\n    /// However, this is both more inefficient and bug prone.\n    Array(ArrayType),\n}\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for VarLenType {\n    fn heap_usage(&self) -> usize {\n        match self {\n            VarLenType::String => 0,\n            VarLenType::Array(x) => x.heap_usage(),\n        }\n    }\n}\n\n/// The layout of var-len objects. Aligned at a `u16` which it has 2 of.\npub const VAR_LEN_REF_LAYOUT: Layout = Layout {\n    size: 4,\n    align: 2,\n    fixed: false,\n};\n\nimpl HasLayout for VarLenType {\n    fn layout(&self) -> &Layout {\n        &VAR_LEN_REF_LAYOUT\n    }\n}\n\n// # Conversions from `AlgebraicType` and friends\n\nimpl From<AlgebraicType> for AlgebraicTypeLayout {\n    fn from(ty: AlgebraicType) -> Self {\n        match ty {\n            AlgebraicType::Sum(sum) => AlgebraicTypeLayout::Sum(sum.into()),\n            AlgebraicType::Product(prod) => AlgebraicTypeLayout::Product(prod.into()),\n\n            AlgebraicType::String => AlgebraicTypeLayout::VarLen(VarLenType::String),\n            AlgebraicType::Array(array) => AlgebraicTypeLayout::VarLen(VarLenType::Array(array)),\n\n            AlgebraicType::Bool => AlgebraicTypeLayout::Bool,\n            AlgebraicType::I8 => AlgebraicTypeLayout::I8,\n            AlgebraicType::U8 => AlgebraicTypeLayout::U8,\n            AlgebraicType::I16 => AlgebraicTypeLayout::I16,\n            AlgebraicType::U16 => AlgebraicTypeLayout::U16,\n            AlgebraicType::I32 => AlgebraicTypeLayout::I32,\n            AlgebraicType::U32 => AlgebraicTypeLayout::U32,\n            AlgebraicType::I64 => AlgebraicTypeLayout::I64,\n            AlgebraicType::U64 => AlgebraicTypeLayout::U64,\n            AlgebraicType::I128 => AlgebraicTypeLayout::I128,\n            AlgebraicType::U128 => AlgebraicTypeLayout::U128,\n            AlgebraicType::I256 => AlgebraicTypeLayout::I256,\n            AlgebraicType::U256 => AlgebraicTypeLayout::U256,\n            AlgebraicType::F32 => AlgebraicTypeLayout::F32,\n            AlgebraicType::F64 => AlgebraicTypeLayout::F64,\n\n            AlgebraicType::Ref(_) => todo!(\"Refs unsupported without typespace context\"),\n        }\n    }\n}\n\n/// Constructs the layout form of `ty`, returning the elements as `C` as well as the memoized `Layout`.\nfn product_type_layout<C: FromIterator<ProductTypeElementLayout>>(ty: ProductType) -> (C, Layout) {\n    let mut current_offset: usize = 0;\n\n    // Minimum possible alignment is 1, even though minimum possible size is 0.\n    // This is consistent with Rust.\n    let mut max_child_align = 1;\n\n    let mut fixed = true;\n    let elements = Vec::from(ty.elements)\n        .into_iter()\n        .map(|elem| {\n            let layout_type: AlgebraicTypeLayout = elem.algebraic_type.into();\n            fixed &= layout_type.layout().fixed;\n            let this_offset = align_to(current_offset, layout_type.align());\n            max_child_align = usize::max(max_child_align, layout_type.align());\n\n            current_offset = this_offset + layout_type.size();\n\n            ProductTypeElementLayout {\n                offset: this_offset as u16,\n                name: elem.name,\n                ty: layout_type,\n            }\n        })\n        .collect();\n\n    let layout = Layout {\n        align: max_child_align as u16,\n        size: align_to(current_offset, max_child_align) as u16,\n        fixed,\n    };\n\n    (elements, layout)\n}\n\nimpl From<ProductType> for RowTypeLayout {\n    fn from(ty: ProductType) -> Self {\n        let (elements, mut layout) = product_type_layout(ty);\n        layout.size = row_size_for_bytes(layout.size as usize).0;\n        Self { layout, elements }\n    }\n}\n\nimpl From<ProductType> for ProductTypeLayout {\n    fn from(ty: ProductType) -> Self {\n        let (elements, layout) = product_type_layout(ty);\n        Self { layout, elements }\n    }\n}\n\nimpl From<SumType> for SumTypeLayout {\n    fn from(ty: SumType) -> Self {\n        let mut max_child_size = 0;\n\n        // Minimum possible alignment is 1, even though minimum possible size is 0.\n        // This is consistent with Rust.\n        let mut max_child_align = 0;\n\n        let mut fixed = true;\n        let variants = Vec::from(ty.variants)\n            .into_iter()\n            .map(|variant| {\n                let layout_type: AlgebraicTypeLayout = variant.algebraic_type.into();\n                fixed &= layout_type.layout().fixed;\n\n                max_child_align = usize::max(max_child_align, layout_type.align());\n                max_child_size = usize::max(max_child_size, layout_type.size());\n\n                SumTypeVariantLayout {\n                    ty: layout_type,\n                    name: variant.name,\n                }\n            })\n            .collect::<Vec<_>>()\n            .into();\n\n        // Guarantees that tag fits inside align.\n        let align = u16::max(max_child_align as u16, 1);\n\n        // Ensure the payload field is sufficiently aligned for all its members.\n        // `max_child_size` and `max_child_align` will already be consistent\n        // if the most-aligned variant is also the largest,\n        // but this is not necessarily the case.\n        // E.g. if variant A is a product of 31 `u8`s, and variant B is a single `u64`,\n        // `max_child_size` will be 31 and `max_child_align` will be 8.\n        // Note that `payload_size` may be 0.\n        let payload_size = align_to(max_child_size, max_child_align);\n\n        // [tag | pad to align | payload]\n        let size = align + payload_size as u16;\n        let payload_offset = align;\n        let layout = Layout { align, size, fixed };\n        Self {\n            layout,\n            payload_offset,\n            variants,\n        }\n    }\n}\n\n// # Conversions to `AlgebraicType` and friends\n// Used for error reporting.\n\nimpl AlgebraicTypeLayout {\n    /// Convert an `AlgebraicTypeLayout` back into an `AlgebraicType`,\n    /// removing layout information.\n    ///\n    /// This operation is O(n) in the number of nodes in the argument,\n    /// and may heap-allocate.\n    /// It is intended for use in error paths, where performance is a secondary concern.\n    pub fn algebraic_type(&self) -> AlgebraicType {\n        match self {\n            Self::Primitive(prim) => prim.algebraic_type(),\n            Self::VarLen(VarLenType::String) => AlgebraicType::String,\n            Self::VarLen(VarLenType::Array(array)) => AlgebraicType::Array(array.clone()),\n            Self::Product(prod) => AlgebraicType::Product(prod.view().product_type()),\n            Self::Sum(sum) => AlgebraicType::Sum(sum.sum_type()),\n        }\n    }\n}\n\nimpl ProductTypeLayoutView<'_> {\n    pub fn product_type(&self) -> ProductType {\n        ProductType {\n            elements: self\n                .elements\n                .iter()\n                .map(ProductTypeElementLayout::product_type_element)\n                .collect(),\n        }\n    }\n\n    /// Convert a `ProductTypeLayout` back into an `AlgebraicType::Product`,\n    /// removing layout information.\n    ///\n    /// This operation is O(n) in the number of nodes in the argument,\n    /// and will heap-allocate.\n    /// It is intended for use in error paths, where performance is a secondary concern.\n    pub fn algebraic_type(&self) -> AlgebraicType {\n        AlgebraicType::Product(self.product_type())\n    }\n}\n\nimpl ProductTypeElementLayout {\n    fn product_type_element(&self) -> ProductTypeElement {\n        ProductTypeElement {\n            algebraic_type: self.ty.algebraic_type(),\n            name: self.name.clone(),\n        }\n    }\n}\n\nimpl SumTypeLayout {\n    fn sum_type(&self) -> SumType {\n        SumType {\n            variants: self\n                .variants\n                .iter()\n                .map(SumTypeVariantLayout::sum_type_variant)\n                .collect(),\n        }\n    }\n\n    /// Can `self` be changed compatibly to `new`?\n    ///\n    /// In the case of sums, the old variants need only be a prefix of the new.\n    ///\n    /// See comment on [`IncompatibleTypeLayoutError`] about [`Box`]ing the error return.\n    // Intentionally fail fast rather than combining errors with [`spacetimedb_data_structures::error_stream`]\n    // because we've (at least theoretically) already passed through\n    // `spacetimedb_schema::auto_migrate::ensure_old_ty_upgradable_to_new` to get here,\n    // and that method has proper pretty error reporting with `ErrorStream`.\n    // The error here is for internal debugging.\n    fn ensure_compatible_with(&self, new: &SumTypeLayout) -> Result<(), Box<IncompatibleTypeLayoutError>> {\n        if self.variants.len() > new.variants.len() {\n            return Err(Box::new(IncompatibleTypeLayoutError::RemovedVariants {\n                old: self.sum_type(),\n                new: new.sum_type(),\n            }));\n        }\n        for (index, (old, new)) in self.variants.iter().zip(self.variants.iter()).enumerate() {\n            if let Err(err) = old.ty.ensure_compatible_with(&new.ty) {\n                return Err(Box::new(IncompatibleTypeLayoutError::IncompatibleSumVariants {\n                    index,\n                    err,\n                }));\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl SumTypeVariantLayout {\n    fn sum_type_variant(&self) -> SumTypeVariant {\n        SumTypeVariant {\n            algebraic_type: self.ty.algebraic_type(),\n            name: self.name.clone(),\n        }\n    }\n\n    /// Returns whether the variant has the given name.\n    pub fn has_name(&self, name: &str) -> bool {\n        self.name.as_deref() == Some(name)\n    }\n\n    /// Returns whether this is a unit variant.\n    pub fn is_unit(&self) -> bool {\n        self.ty.as_product().is_some_and(|ty| ty.elements.is_empty())\n    }\n}\n\n// # Inspecting layout\n\nimpl SumTypeLayout {\n    pub fn offset_of_variant_data(&self, _variant_tag: u8) -> usize {\n        // Store the tag at the start, similar to BSATN.\n        // Unlike BSATN, there is also padding.\n        //\n        // ```ignore\n        // [ tag | padding to variant data align | variant data ]\n        // ```\n        //\n        self.payload_offset as usize\n    }\n\n    pub fn offset_of_tag(&self) -> usize {\n        // Store the tag at the start, similar to BSATN.\n        //\n        // ```ignore\n        // [ tag | padding to variant data align | variant data ]\n        // ```\n        //\n        0\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for &AlgebraicTypeLayout {\n    type Output = AlgebraicValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {\n        match self {\n            AlgebraicTypeLayout::Sum(ty) => ty.deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Product(ty) => ty.view().deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::Bool) => bool::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I8) => i8::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U8) => u8::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I16) => i16::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U16) => u16::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I32) => i32::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U32) => u32::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I64) => i64::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U64) => u64::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I128) => i128::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U128) => u128::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::I256) => i256::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::U256) => u256::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::F32) => f32::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::Primitive(PrimitiveType::F64) => f64::deserialize(de).map(Into::into),\n            AlgebraicTypeLayout::VarLen(VarLenType::Array(ty)) => {\n                WithTypespace::empty(ty).deserialize(de).map(AlgebraicValue::Array)\n            }\n            AlgebraicTypeLayout::VarLen(VarLenType::String) => <Box<str>>::deserialize(de).map(Into::into),\n        }\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for ProductTypeLayoutView<'_> {\n    type Output = ProductValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {\n        de.deserialize_product(self)\n    }\n}\n\nimpl<'de> ProductVisitor<'de> for ProductTypeLayoutView<'_> {\n    type Output = ProductValue;\n\n    fn product_name(&self) -> Option<&str> {\n        None\n    }\n    fn product_len(&self) -> usize {\n        self.elements.len()\n    }\n\n    fn visit_seq_product<A: SeqProductAccess<'de>>(self, mut tup: A) -> Result<Self::Output, A::Error> {\n        let mut elems: Vec<AlgebraicValue> = Vec::with_capacity(self.product_len());\n        for (i, elem_ty) in self.elements.iter().enumerate() {\n            let Some(elem_val) = tup.next_element_seed(&elem_ty.ty)? else {\n                return Err(A::Error::invalid_product_length(i, &self));\n            };\n            elems.push(elem_val);\n        }\n        Ok(elems.into())\n    }\n\n    fn validate_seq_product<A: SeqProductAccess<'de>>(self, mut tup: A) -> Result<(), A::Error> {\n        for (i, elem_ty) in self.elements.iter().enumerate() {\n            if tup.validate_next_element_seed(&elem_ty.ty)?.is_none() {\n                return Err(A::Error::invalid_product_length(i, &self));\n            }\n        }\n        Ok(())\n    }\n\n    fn visit_named_product<A: NamedProductAccess<'de>>(self, _: A) -> Result<Self::Output, A::Error> {\n        unreachable!()\n    }\n\n    fn validate_named_product<A: NamedProductAccess<'de>>(self, _: A) -> Result<(), A::Error> {\n        unreachable!()\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for &SumTypeLayout {\n    type Output = SumValue;\n\n    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        deserializer.deserialize_sum(self)\n    }\n}\n\nimpl<'de> SumVisitor<'de> for &SumTypeLayout {\n    type Output = SumValue;\n\n    fn sum_name(&self) -> Option<&str> {\n        None\n    }\n\n    fn is_option(&self) -> bool {\n        match &*self.variants {\n            [first, second]\n                if second.is_unit() // Done first to avoid pointer indirection when it doesn't matter.\n                    && first.has_name(OPTION_SOME_TAG)\n                    && second.has_name(OPTION_NONE_TAG) =>\n            {\n                true\n            }\n            _ => false,\n        }\n    }\n\n    fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {\n        let (tag, data) = data.variant(self)?;\n        // Find the variant type by `tag`.\n        let variant_ty = &self.variants[tag as usize].ty;\n\n        let value = data.deserialize_seed(variant_ty)?;\n        Ok(SumValue::new(tag, value))\n    }\n\n    fn validate_sum<A: SumAccess<'de>>(self, data: A) -> Result<(), A::Error> {\n        let (tag, data) = data.variant(self)?;\n        // Find the variant type by `tag`.\n        let variant_ty = &self.variants[tag as usize].ty;\n\n        data.validate_seed(variant_ty)\n    }\n}\n\nimpl VariantVisitor<'_> for &SumTypeLayout {\n    type Output = u8;\n\n    fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {\n        // Provide the names known from the `SumType`.\n        self.variants.iter().filter_map(|v| v.name.as_deref())\n    }\n\n    fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {\n        // Verify that tag identifies a valid variant in `SumType`.\n        self.variants\n            .get(tag as usize)\n            .ok_or_else(|| E::unknown_variant_tag(tag, &self))?;\n\n        Ok(tag)\n    }\n\n    fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {\n        // Translate the variant `name` to its tag.\n        self.variants\n            .iter()\n            .position(|var| var.has_name(name))\n            .map(|pos| pos as u8)\n            .ok_or_else(|| E::unknown_variant_name(name, &self))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::proptest::generate_algebraic_type;\n    use itertools::Itertools as _;\n    use proptest::collection::vec;\n    use proptest::prelude::*;\n\n    #[test]\n    fn align_to_expected() {\n        fn assert_alignment(offset: usize, alignment: usize, expected: usize) {\n            assert_eq!(\n                align_to(offset, alignment),\n                expected,\n                \"align_to({}, {}): expected {} but found {}\",\n                offset,\n                alignment,\n                expected,\n                align_to(offset, alignment)\n            );\n        }\n\n        for align in [1usize, 2, 4, 8, 16, 32, 64] {\n            assert_alignment(0, align, 0);\n\n            for offset in 1..=align {\n                assert_alignment(offset, align, align);\n            }\n            for offset in (align + 1)..=(align * 2) {\n                assert_alignment(offset, align, align * 2);\n            }\n        }\n    }\n\n    fn assert_size_align(ty: AlgebraicType, size: usize, align: usize) {\n        let layout = AlgebraicTypeLayout::from(ty);\n        assert_eq!(layout.size(), size);\n        assert_eq!(layout.align(), align);\n    }\n\n    #[test]\n    fn known_product_expected_size_align() {\n        for (ty, size, align) in [\n            (AlgebraicType::product::<[AlgebraicType; 0]>([]), 0, 1),\n            (AlgebraicType::product([AlgebraicType::U8]), 1, 1),\n            (AlgebraicType::product([AlgebraicType::I8]), 1, 1),\n            (AlgebraicType::product([AlgebraicType::Bool]), 1, 1),\n            (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),\n            (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),\n            (\n                AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8, AlgebraicType::U16]),\n                4,\n                2,\n            ),\n            (\n                AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16, AlgebraicType::U8]),\n                6,\n                2,\n            ),\n            (\n                AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8, AlgebraicType::U8]),\n                4,\n                2,\n            ),\n            (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),\n            (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),\n            (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),\n            (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),\n            (AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),\n            (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),\n            (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),\n            (\n                AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U128]),\n                32,\n                16,\n            ),\n            (AlgebraicType::product([AlgebraicType::String]), 4, 2),\n            (\n                AlgebraicType::product([AlgebraicType::String, AlgebraicType::U16]),\n                6,\n                2,\n            ),\n            (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),\n            (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),\n            (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),\n            (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),\n            (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),\n            (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),\n            (AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),\n            (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),\n            (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),\n            (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),\n            (\n                AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I128]),\n                32,\n                16,\n            ),\n            (\n                AlgebraicType::product([AlgebraicType::I256, AlgebraicType::U256]),\n                64,\n                32,\n            ),\n            (\n                AlgebraicType::product([AlgebraicType::String, AlgebraicType::I16]),\n                6,\n                2,\n            ),\n        ] {\n            assert_size_align(ty, size, align);\n        }\n    }\n\n    #[test]\n    fn known_sum_expected_size_align() {\n        for (ty, size, align) in [\n            (AlgebraicType::sum([AlgebraicType::U8]), 2, 1),\n            (AlgebraicType::sum([AlgebraicType::I8]), 2, 1),\n            (AlgebraicType::sum([AlgebraicType::Bool]), 2, 1),\n            (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),\n            (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U128]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::String]), 6, 2),\n            (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::U16]), 6, 2),\n            (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),\n            (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),\n            (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),\n            (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),\n            (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I128]), 32, 16),\n            (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::I128]), 64, 32),\n            (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::U256]), 64, 32),\n            (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::I16]), 6, 2),\n        ] {\n            assert_size_align(ty, size, align);\n        }\n    }\n\n    proptest! {\n        fn variant_order_irrelevant_for_layout(\n            variants in vec(generate_algebraic_type(), 0..5)\n        ) {\n            use crate::SumTypeVariant;\n\n            let len = variants.len();\n            // Compute all permutations of the sum type with `variants`.\n            let sum_permutations = variants\n                .into_iter()\n                .permutations(len)\n                .map(|vars| vars.into_iter().map(SumTypeVariant::from).collect::<Box<[_]>>())\n                .map(AlgebraicType::sum);\n            // Compute the layouts of each equivalent sum type.\n            let mut sum_layout_perms = sum_permutations\n                .map(AlgebraicTypeLayout::from)\n                .map(|ty| *ty.layout());\n            // Assert that they are in fact equal in terms of layout.\n            prop_assert!(sum_layout_perms.all_equal());\n        }\n\n        #[test]\n        fn size_always_multiple_of_align(ty in generate_algebraic_type()) {\n            let layout = AlgebraicTypeLayout::from(ty);\n\n            if layout.size() == 0 {\n                assert_eq!(layout.align(), 1);\n            } else {\n                assert_eq!(layout.size() % layout.align(), 0);\n            }\n        }\n    }\n\n    #[test]\n    fn infinite_recursion_in_ensure_compatible_with_with_array_type() {\n        let ty = AlgebraicTypeLayout::from(AlgebraicType::array(AlgebraicType::U64));\n        // This would previously cause an infinite recursion / stack overflow\n        // due the setup where `AlgebraicTypeLayout::VarLen(Array(x))` stored\n        // `x = Box::new(AlgebraicType::Array(elem_ty))`.\n        // The method `AlgebraicTypeLayout::ensure_compatible_with` was not setup to handle that.\n        // To avoid such bugs in the future, `x` is now `elem_ty` instead.\n        assert!(ty.ensure_compatible_with(&ty).is_ok());\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/lib.rs",
    "content": "pub mod algebraic_type;\nmod algebraic_type_ref;\npub mod algebraic_value;\nmod algebraic_value_hash;\npub mod array_type;\npub mod array_value;\npub mod bsatn;\npub mod buffer;\npub mod convert;\npub mod de;\npub mod hash;\npub mod hex;\npub mod layout;\n#[cfg(feature = \"memory-usage\")]\nmod memory_usage_impls;\n#[cfg(feature = \"memory-usage\")]\npub use spacetimedb_memory_usage as memory_usage;\npub mod meta_type;\npub mod primitives;\npub mod product_type;\npub mod product_type_element;\npub mod product_value;\npub mod raw_identifier;\nmod resolve_refs;\npub mod satn;\npub mod ser;\npub mod size_of;\npub mod sum_type;\npub mod sum_type_variant;\npub mod sum_value;\npub mod time_duration;\npub mod timestamp;\npub mod typespace;\npub mod uuid;\n\n#[cfg(any(test, feature = \"proptest\"))]\npub mod proptest;\n\n#[cfg(any(test, feature = \"serde\"))]\npub mod serde {\n    pub use crate::de::serde::{deserialize_from as deserialize, SerdeDeserializer};\n    pub use crate::ser::serde::{serialize_to as serialize, SerdeSerializer};\n\n    /// A wrapper around a `serde` error which occurred while translating SATS <-> serde.\n    #[repr(transparent)]\n    pub struct SerdeError<E>(pub E);\n\n    /// A wrapper type that implements `serde` traits when `T` implements SATS traits.\n    ///\n    /// Specifically:\n    /// - <code>T: [sats::Serialize][crate::ser::Serialize] => `SerializeWrapper<T>`: [serde::Serialize]</code>\n    /// - <code>T: [sats::Deserialize<'de>][crate::de::Deserialize] => `SerializeWrapper<T>`: [serde::Deserialize<'de>]</code>\n    /// - <code>T: [sats::DeserializeSeed<'de>][crate::de::DeserializeSeed] => `SerializeWrapper<T>`: [serde::DeserializeSeed<'de>]</code>\n    #[repr(transparent)]\n    pub struct SerdeWrapper<T: ?Sized>(pub T);\n\n    impl<T: ?Sized> SerdeWrapper<T> {\n        /// Wraps a value in `SerdeWrapper`.\n        pub fn new(t: T) -> Self\n        where\n            T: Sized,\n        {\n            Self(t)\n        }\n\n        /// Converts `&T` to `&SerializeWrapper<T>`.\n        pub fn from_ref(t: &T) -> &Self {\n            // SAFETY: OK because of `repr(transparent)`.\n            unsafe { &*(t as *const T as *const SerdeWrapper<T>) }\n        }\n    }\n}\n\n/// Allows the macros in [`spacetimedb_bindings_macro`] to accept `crate = spacetimedb_sats`,\n/// which will then emit `$krate::sats`.\n#[doc(hidden)]\npub use crate as sats;\nuse crate::raw_identifier::RawIdentifier;\n\npub use algebraic_type::AlgebraicType;\npub use algebraic_type_ref::AlgebraicTypeRef;\npub use algebraic_value::{i256, u256, AlgebraicValue, F32, F64};\npub use algebraic_value_hash::hash_bsatn_array;\npub use array_type::ArrayType;\npub use array_value::ArrayValue;\npub use product_type::ProductType;\npub use product_type_element::ProductTypeElement;\npub use product_value::ProductValue;\npub use sum_type::SumType;\npub use sum_type_variant::SumTypeVariant;\npub use sum_value::SumValue;\npub use typespace::{GroundSpacetimeType, SpacetimeType, Typespace};\n\npub use de::Deserialize;\npub use ser::Serialize;\n\n/// The `Value` trait provides an abstract notion of a value.\n///\n/// All we know about values abstractly is that they have a `Type`.\npub trait Value {\n    /// The type of this value.\n    type Type;\n}\n\nimpl<T: Value> Value for Box<[T]> {\n    // TODO(centril/phoebe): This looks weird; shouldn't it be ArrayType?\n    type Type = T::Type;\n}\n\n/// A borrowed value combined with its type and typing context (`Typespace`).\npub struct ValueWithType<'a, T: Value> {\n    /// The type combined with the context of this `val`ue.\n    ty: WithTypespace<'a, T::Type>,\n    /// The borrowed value.\n    val: &'a T,\n}\n\nimpl<T: Value> Copy for ValueWithType<'_, T> {}\nimpl<T: Value> Clone for ValueWithType<'_, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'a, T: Value> ValueWithType<'a, T> {\n    /// Wraps the borrowed value `val` with its type combined with context.\n    pub fn new(ty: WithTypespace<'a, T::Type>, val: &'a T) -> Self {\n        Self { ty, val }\n    }\n\n    /// Returns the borrowed value.\n    pub fn value(&self) -> &'a T {\n        self.val\n    }\n\n    /// Returns the type of the value.\n    pub fn ty(&self) -> &'a T::Type {\n        self.ty.ty\n    }\n\n    pub fn ty_s(&self) -> WithTypespace<'a, T::Type> {\n        self.ty\n    }\n\n    /// Returns the typing context (`Typespace`).\n    pub fn typespace(&self) -> &'a Typespace {\n        self.ty.typespace\n    }\n\n    /// Reuses the typespace we already have and returns `val` and `ty` wrapped with it.\n    pub fn with<'b, U: Value>(&self, ty: &'b U::Type, val: &'b U) -> ValueWithType<'b, U>\n    where\n        'a: 'b,\n    {\n        ValueWithType {\n            ty: self.ty.with(ty),\n            val,\n        }\n    }\n}\n\nimpl<'a, T: Value> ValueWithType<'a, Box<[T]>> {\n    pub fn iter(&self) -> impl Iterator<Item = ValueWithType<'a, T>> + use<'_, 'a, T> {\n        self.value().iter().map(|val| ValueWithType { ty: self.ty, val })\n    }\n}\n\nimpl<T: Value + PartialEq> PartialEq<T> for ValueWithType<'_, T> {\n    fn eq(&self, other: &T) -> bool {\n        self.val == other\n    }\n}\n\nuse core::fmt;\n\nimpl<T: fmt::Debug + Value<Type: fmt::Debug>> fmt::Debug for ValueWithType<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"ValueWithType\")\n            .field(\"type\", self.ty())\n            .field(\"value\", self.value())\n            .finish()\n    }\n}\n\n/// Adds a `Typespace` context atop of a borrowed type.\n#[derive(Debug)]\npub struct WithTypespace<'a, T: ?Sized> {\n    /// The typespace context that has been added to `ty`.\n    typespace: &'a Typespace,\n    /// What we've added the context to.\n    ty: &'a T,\n}\n\nimpl<T: ?Sized> Copy for WithTypespace<'_, T> {}\nimpl<T: ?Sized> Clone for WithTypespace<'_, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'a, T: ?Sized> WithTypespace<'a, T> {\n    /// Wraps `ty` in a context combined with the `typespace`.\n    pub const fn new(typespace: &'a Typespace, ty: &'a T) -> Self {\n        Self { typespace, ty }\n    }\n\n    /// Wraps `ty` in an empty context.\n    pub const fn empty(ty: &'a T) -> Self {\n        Self::new(Typespace::EMPTY, ty)\n    }\n\n    /// Returns the object that the context was created with.\n    pub const fn ty(&self) -> &'a T {\n        self.ty\n    }\n\n    /// Returns the typespace context.\n    pub const fn typespace(&self) -> &'a Typespace {\n        self.typespace\n    }\n\n    /// Reuses the typespace we already have and returns `ty: U` wrapped with it.\n    pub fn with<'b, U>(&self, ty: &'b U) -> WithTypespace<'b, U>\n    where\n        'a: 'b,\n    {\n        WithTypespace {\n            typespace: self.typespace,\n            ty,\n        }\n    }\n\n    pub(crate) fn iter_with<U: 'a, I: IntoIterator<Item = &'a U>>(&self, tys: I) -> IterWithTypespace<'a, I::IntoIter> {\n        IterWithTypespace {\n            typespace: self.typespace,\n            iter: tys.into_iter(),\n        }\n    }\n\n    /// Wraps `val` with the type and typespace context in `self`.\n    pub fn with_value<'b, V: Value<Type = T>>(&self, val: &'b V) -> ValueWithType<'b, V>\n    where\n        'a: 'b,\n    {\n        ValueWithType::new(*self, val)\n    }\n\n    /// Returns the `AlgebraicType` that `r` resolves to in the context of our `Typespace`.\n    ///\n    /// Panics if `r` is not known by our `Typespace`.\n    pub fn resolve(&self, r: AlgebraicTypeRef) -> WithTypespace<'a, AlgebraicType> {\n        WithTypespace {\n            typespace: self.typespace,\n            ty: &self.typespace[r],\n        }\n    }\n\n    /// Maps the object we've wrapped from `&T -> &U` in our context.\n    ///\n    /// This can be used to e.g., project fields and through a structure.\n    /// This provides an implementation of functor mapping for `WithTypespace`.\n    pub fn map<U: ?Sized>(&self, f: impl FnOnce(&'a T) -> &'a U) -> WithTypespace<'a, U> {\n        WithTypespace {\n            typespace: self.typespace,\n            ty: f(self.ty),\n        }\n    }\n}\n\npub struct IterWithTypespace<'a, I> {\n    typespace: &'a Typespace,\n    iter: I,\n}\n\nimpl<'a, I, T: 'a> Iterator for IterWithTypespace<'a, I>\nwhere\n    I: Iterator<Item = &'a T>,\n{\n    type Item = WithTypespace<'a, T>;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next().map(|ty| self.typespace.with_type(ty))\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.iter.size_hint()\n    }\n}\n\nimpl<'a, I, T: 'a> ExactSizeIterator for IterWithTypespace<'a, I>\nwhere\n    I: ExactSizeIterator<Item = &'a T>,\n{\n    fn len(&self) -> usize {\n        self.iter.len()\n    }\n}\n\n/// Required for derive(SpacetimeType) to work outside of a module\n#[macro_export]\n#[doc(hidden)]\nmacro_rules! __make_register_reftype {\n    ($ty:ty, $name:literal) => {};\n}\n\n/// A helper for prettier Debug implementation, without extra indirection around Some(\"name\").\nfn dbg_aggregate_name(opt: &Option<RawIdentifier>) -> &dyn std::fmt::Debug {\n    opt.as_ref().map_or(opt, |s| s)\n}\n"
  },
  {
    "path": "crates/sats/src/memory_usage_impls.rs",
    "content": "use crate::{\n    algebraic_value::Packed, raw_identifier::RawIdentifier, AlgebraicType, AlgebraicValue, ArrayType, ArrayValue,\n    ProductType, ProductTypeElement, ProductValue, SumType, SumTypeVariant, SumValue,\n};\nuse core::mem;\nuse spacetimedb_memory_usage::MemoryUsage;\n\nimpl MemoryUsage for AlgebraicValue {\n    fn heap_usage(&self) -> usize {\n        match self {\n            AlgebraicValue::Sum(x) => x.heap_usage(),\n            AlgebraicValue::Product(x) => x.heap_usage(),\n            AlgebraicValue::Array(x) => x.heap_usage(),\n            AlgebraicValue::String(x) => x.heap_usage(),\n            _ => 0,\n        }\n    }\n}\nimpl MemoryUsage for SumValue {\n    fn heap_usage(&self) -> usize {\n        self.value.heap_usage()\n    }\n}\nimpl MemoryUsage for ProductValue {\n    fn heap_usage(&self) -> usize {\n        self.elements.heap_usage()\n    }\n}\nimpl MemoryUsage for ArrayValue {\n    fn heap_usage(&self) -> usize {\n        match self {\n            ArrayValue::Sum(v) => v.heap_usage(),\n            ArrayValue::Product(v) => v.heap_usage(),\n            ArrayValue::Bool(v) => v.heap_usage(),\n            ArrayValue::I8(v) => v.heap_usage(),\n            ArrayValue::U8(v) => v.heap_usage(),\n            ArrayValue::I16(v) => v.heap_usage(),\n            ArrayValue::U16(v) => v.heap_usage(),\n            ArrayValue::I32(v) => v.heap_usage(),\n            ArrayValue::U32(v) => v.heap_usage(),\n            ArrayValue::I64(v) => v.heap_usage(),\n            ArrayValue::U64(v) => v.heap_usage(),\n            ArrayValue::I128(v) => v.heap_usage(),\n            ArrayValue::U128(v) => v.heap_usage(),\n            ArrayValue::I256(v) => v.heap_usage(),\n            ArrayValue::U256(v) => v.heap_usage(),\n            ArrayValue::F32(v) => v.heap_usage(),\n            ArrayValue::F64(v) => v.heap_usage(),\n            ArrayValue::String(v) => v.heap_usage(),\n            ArrayValue::Array(v) => v.heap_usage(),\n        }\n    }\n}\nimpl MemoryUsage for AlgebraicType {\n    fn heap_usage(&self) -> usize {\n        match self {\n            AlgebraicType::Ref(_) => 0,\n            AlgebraicType::Sum(x) => x.heap_usage(),\n            AlgebraicType::Product(x) => x.heap_usage(),\n            AlgebraicType::Array(x) => x.heap_usage(),\n            AlgebraicType::String\n            | AlgebraicType::Bool\n            | AlgebraicType::I8\n            | AlgebraicType::U8\n            | AlgebraicType::I16\n            | AlgebraicType::U16\n            | AlgebraicType::I32\n            | AlgebraicType::U32\n            | AlgebraicType::I64\n            | AlgebraicType::U64\n            | AlgebraicType::I128\n            | AlgebraicType::U128\n            | AlgebraicType::I256\n            | AlgebraicType::U256\n            | AlgebraicType::F32\n            | AlgebraicType::F64 => 0,\n        }\n    }\n}\nimpl MemoryUsage for SumType {\n    fn heap_usage(&self) -> usize {\n        self.variants.heap_usage()\n    }\n}\nimpl MemoryUsage for SumTypeVariant {\n    fn heap_usage(&self) -> usize {\n        self.name.heap_usage() + self.algebraic_type.heap_usage()\n    }\n}\nimpl MemoryUsage for ProductType {\n    fn heap_usage(&self) -> usize {\n        self.elements.heap_usage()\n    }\n}\nimpl MemoryUsage for ProductTypeElement {\n    fn heap_usage(&self) -> usize {\n        self.name.heap_usage() + self.algebraic_type.heap_usage()\n    }\n}\nimpl MemoryUsage for ArrayType {\n    fn heap_usage(&self) -> usize {\n        self.elem_ty.heap_usage()\n    }\n}\n\nimpl<T: MemoryUsage + Copy> MemoryUsage for Packed<T> {\n    fn heap_usage(&self) -> usize {\n        { self.0 }.heap_usage()\n    }\n}\n\nimpl MemoryUsage for RawIdentifier {\n    fn heap_usage(&self) -> usize {\n        if self.0.len() <= 15 {\n            0\n        } else {\n            self.0.len() + 2 * mem::size_of::<usize>()\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/meta_type.rs",
    "content": "//! Provides the `MetaType` trait.\n\nuse crate::AlgebraicType;\n\n/// Rust types which represent components of the SATS type system\n/// and can themselves be represented as algebraic objects will implement [`MetaType`].\n///\n/// A type's meta-type is an [`AlgebraicType`]\n/// which can store the data associated with a definition of that type.\n///\n/// For example, the `MetaType` of [`ProductType`](crate::ProductType) is\n/// ```ignore\n/// AlgebraicType::product([(\n///     \"elements\",\n///     AlgebraicType::array(ProductTypeElement::meta_type()),\n/// )])\n/// ```\npub trait MetaType {\n    /// Returns the type structure of this type as an `AlgebraicType`.\n    fn meta_type() -> AlgebraicType;\n}\n"
  },
  {
    "path": "crates/sats/src/primitives.rs",
    "content": "use crate::{de, impl_deserialize, impl_serialize, impl_st, AlgebraicType};\npub use spacetimedb_primitives::ColumnAttribute;\npub use spacetimedb_primitives::Constraints;\n\nimpl_deserialize!([] ColumnAttribute, de =>\n    Self::from_bits(de.deserialize_u8()?)\n        .ok_or_else(|| de::Error::custom(\"invalid bitflags for `ColumnAttribute`\"))\n);\nimpl_serialize!([] ColumnAttribute, (self, ser) => ser.serialize_u8(self.bits()));\nimpl_st!([] ColumnAttribute, AlgebraicType::U8);\n\nimpl_deserialize!([] Constraints, de => Self::try_from(de.deserialize_u8()?)\n    .map_err(|_| de::Error::custom(\"invalid bitflags for `Constraints`\"))\n);\nimpl_serialize!([] Constraints, (self, ser) => ser.serialize_u8(self.bits()));\nimpl_st!([] Constraints, AlgebraicType::U8);\n"
  },
  {
    "path": "crates/sats/src/product_type.rs",
    "content": "use crate::algebraic_value::de::{ValueDeserializeError, ValueDeserializer};\nuse crate::algebraic_value::ser::value_serialize;\nuse crate::de::Deserialize;\nuse crate::meta_type::MetaType;\nuse crate::product_value::InvalidFieldError;\nuse crate::raw_identifier::RawIdentifier;\nuse crate::{\n    AlgebraicType, AlgebraicValue, ProductTypeElement, ProductValue, SpacetimeType, Typespace, ValueWithType,\n    WithTypespace,\n};\nuse core::ops::Deref;\nuse spacetimedb_primitives::{ColId, ColList};\n\n/// The tag used inside the special `Identity` product type.\npub const IDENTITY_TAG: &str = \"__identity__\";\n/// The tag used inside the special `ConnectionId` product type.\npub const CONNECTION_ID_TAG: &str = \"__connection_id__\";\n/// The tag used inside the special `Timestamp` product type.\npub const TIMESTAMP_TAG: &str = \"__timestamp_micros_since_unix_epoch__\";\n/// The tag used inside the special `TimeDuration` product type.\npub const TIME_DURATION_TAG: &str = \"__time_duration_micros__\";\n\n/// The tag used inside the special `UUID` product type.\npub const UUID_TAG: &str = \"__uuid__\";\n\n/// A structural product type  of the factors given by `elements`.\n///\n/// This is also known as `struct` and `tuple` in many languages,\n/// but note that unlike most languages, products in SATs are *[structural]* and not nominal.\n/// When checking whether two nominal types are the same,\n/// their names and/or declaration sites (e.g., module / namespace) are considered.\n/// Meanwhile, a structural type system would only check the structure of the type itself,\n/// e.g., the names of its fields and their types in the case of a record.\n/// The name \"product\" comes from category theory.\n///\n/// See also:\n/// - <https://en.wikipedia.org/wiki/Record_(computer_science)>\n/// - <https://ncatlab.org/nlab/show/product+type>\n///\n/// These structures are known as product types because the number of possible values in product\n/// ```text\n/// { N_0: T_0, N_1: T_1, ..., N_n: T_n }\n/// ```\n/// is:\n/// ```text\n/// Π (i ∈ 0..n). values(T_i)\n/// ```\n/// so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.\n///\n/// [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType)]\n#[sats(crate = crate)]\npub struct ProductType {\n    /// The factors of the product type.\n    ///\n    /// These factors can either be named or unnamed.\n    /// When all the factors are unnamed, we can regard this as a plain tuple type.\n    pub elements: Box<[ProductTypeElement]>,\n}\n\nimpl Deref for ProductType {\n    type Target = [ProductTypeElement];\n\n    fn deref(&self) -> &Self::Target {\n        &self.elements\n    }\n}\n\nimpl std::fmt::Debug for ProductType {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"ProductType \")?;\n        f.debug_map()\n            .entries(\n                self.elements\n                    .iter()\n                    .map(|elem| (crate::dbg_aggregate_name(&elem.name), &elem.algebraic_type)),\n            )\n            .finish()\n    }\n}\n\nimpl ProductType {\n    /// Returns a product type with the given `elements` as its factors.\n    pub const fn new(elements: Box<[ProductTypeElement]>) -> Self {\n        Self { elements }\n    }\n\n    /// Returns the unit product type.\n    pub fn unit() -> Self {\n        Self::new([].into())\n    }\n\n    /// Returns whether this is a \"newtype\" with `label` and satisfying `inner`.\n    /// Does not follow `Ref`s.\n    fn is_newtype(&self, check: &str, inner: impl FnOnce(&AlgebraicType) -> bool) -> bool {\n        match &*self.elements {\n            [ProductTypeElement {\n                name: Some(name),\n                algebraic_type,\n            }] => &**name == check && inner(algebraic_type),\n            _ => false,\n        }\n    }\n\n    /// Returns whether this is the special case of `spacetimedb_lib::Identity`.\n    /// Does not follow `Ref`s.\n    pub fn is_identity(&self) -> bool {\n        self.is_newtype(IDENTITY_TAG, |i| i.is_u256())\n    }\n\n    /// Returns whether this is the special case of `spacetimedb_lib::ConnectionId`.\n    /// Does not follow `Ref`s.\n    pub fn is_connection_id(&self) -> bool {\n        self.is_newtype(CONNECTION_ID_TAG, |i| i.is_u128())\n    }\n\n    fn is_newtype_of(&self, expected_tag: &str, of: AlgebraicType) -> bool {\n        match &*self.elements {\n            [ProductTypeElement {\n                name: Some(name),\n                algebraic_type,\n            }] => &**name == expected_tag && *algebraic_type == of,\n            _ => false,\n        }\n    }\n\n    fn is_i64_newtype(&self, expected_tag: &str) -> bool {\n        self.is_newtype_of(expected_tag, AlgebraicType::I64)\n    }\n\n    /// Returns whether this is the special case of `spacetimedb_lib::Timestamp`.\n    /// Does not follow `Ref`s.\n    pub fn is_timestamp(&self) -> bool {\n        self.is_i64_newtype(TIMESTAMP_TAG)\n    }\n\n    /// Returns whether this is the special case of `spacetimedb_lib::TimeDuration`.\n    /// Does not follow `Ref`s.\n    pub fn is_time_duration(&self) -> bool {\n        self.is_i64_newtype(TIME_DURATION_TAG)\n    }\n\n    /// Returns whether this is the special tag of `Identity`.\n    pub fn is_identity_tag(tag_name: &str) -> bool {\n        tag_name == IDENTITY_TAG\n    }\n\n    /// Returns whether this is the special tag of `ConnectionId`.\n    pub fn is_connection_id_tag(tag_name: &str) -> bool {\n        tag_name == CONNECTION_ID_TAG\n    }\n\n    /// Returns whether this is the special tag of [`crate::timestamp::Timestamp`].\n    pub fn is_timestamp_tag(tag_name: &str) -> bool {\n        tag_name == TIMESTAMP_TAG\n    }\n\n    /// Returns whether this is the special tag of [`crate::time_duration::TimeDuration`].\n    pub fn is_time_duration_tag(tag_name: &str) -> bool {\n        tag_name == TIME_DURATION_TAG\n    }\n\n    /// Returns whether this is the special tag of [`crate::uuid::Uuid`].\n    pub fn is_uuid_tag(tag_name: &str) -> bool {\n        tag_name == UUID_TAG\n    }\n\n    /// Returns whether this is the special case of  [`crate::uuid::Uuid`].\n    pub fn is_uuid(&self) -> bool {\n        self.is_newtype_of(UUID_TAG, AlgebraicType::U128)\n    }\n\n    /// Returns whether this is a special known `tag`,\n    /// currently `Address`, `Identity`, `Timestamp`, `TimeDuration`, `ConnectionId` or `UUID`.\n    pub fn is_special_tag(tag_name: &str) -> bool {\n        [\n            IDENTITY_TAG,\n            CONNECTION_ID_TAG,\n            TIMESTAMP_TAG,\n            TIME_DURATION_TAG,\n            UUID_TAG,\n        ]\n        .contains(&tag_name)\n    }\n\n    /// Returns whether this is a special known type,\n    /// currently `Identity`, `Timestamp`, `TimeDuration`, `ConnectionId` or `UUID`.\n    /// Does not follow `Ref`s.\n    pub fn is_special(&self) -> bool {\n        self.is_identity()\n            || self.is_connection_id()\n            || self.is_timestamp()\n            || self.is_time_duration()\n            || self.is_uuid()\n    }\n\n    /// Returns whether this is a unit type, that is, has no elements.\n    pub fn is_unit(&self) -> bool {\n        self.elements.is_empty()\n    }\n\n    /// Returns index of the field with the given `name`.\n    pub fn index_of_field_name(&self, name: &str) -> Option<usize> {\n        self.elements\n            .iter()\n            .position(|field| field.name.as_deref() == Some(name))\n    }\n\n    /// This utility function is designed to project fields based on the supplied `indexes`.\n    ///\n    /// **Important:**\n    ///\n    /// The resulting [AlgebraicType] will wrap into a [ProductType] when projecting multiple\n    /// (including zero) fields, otherwise it will consist of a single [AlgebraicType].\n    ///\n    /// **Parameters:**\n    /// - `cols`: A [ColList] containing the indexes of fields to be projected.\n    pub fn project(&self, cols: &ColList) -> Result<AlgebraicType, InvalidFieldError> {\n        let get_field = |col_pos: ColId| {\n            self.elements\n                .get(col_pos.idx())\n                .ok_or(InvalidFieldError { col_pos, name: None })\n        };\n        if let Some(head) = cols.as_singleton() {\n            get_field(head).map(|f| f.algebraic_type.clone())\n        } else {\n            let mut fields = Vec::with_capacity(cols.len() as usize);\n            for col in cols.iter() {\n                fields.push(get_field(col)?.clone());\n            }\n            Ok(AlgebraicType::product(fields.into_boxed_slice()))\n        }\n    }\n\n    /// Returns whether `value` is compatible with this type.\n    pub fn type_check(&self, value: &ProductValue, typespace: &Typespace) -> bool {\n        value.elements.len() == self.elements.len()\n            && self\n                .elements\n                .iter()\n                .zip(&value.elements)\n                .all(|(ty, val)| ty.algebraic_type.type_check(val, typespace))\n    }\n}\n\nimpl<I: Into<ProductTypeElement>> FromIterator<I> for ProductType {\n    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {\n        Self::new(iter.into_iter().map(Into::into).collect())\n    }\n}\nimpl<Id: Into<RawIdentifier>, I: Into<AlgebraicType>> FromIterator<(Id, I)> for ProductType {\n    fn from_iter<T: IntoIterator<Item = (Id, I)>>(iter: T) -> Self {\n        iter.into_iter()\n            .map(|(name, ty)| ProductTypeElement::new_named(ty.into(), name))\n            .collect()\n    }\n}\n\nimpl<I: Into<AlgebraicType>> FromIterator<(Option<&'static str>, I)> for ProductType {\n    fn from_iter<T: IntoIterator<Item = (Option<&'static str>, I)>>(iter: T) -> Self {\n        iter.into_iter()\n            .map(|(name, ty)| ProductTypeElement::new(ty.into(), name.map(Into::into)))\n            .collect()\n    }\n}\n\nimpl From<Box<[ProductTypeElement]>> for ProductType {\n    fn from(fields: Box<[ProductTypeElement]>) -> Self {\n        ProductType::new(fields)\n    }\n}\nimpl<const N: usize> From<[ProductTypeElement; N]> for ProductType {\n    fn from(fields: [ProductTypeElement; N]) -> Self {\n        ProductType::new(fields.into())\n    }\n}\nimpl<const N: usize> From<[(Option<&'static str>, AlgebraicType); N]> for ProductType {\n    fn from(fields: [(Option<&'static str>, AlgebraicType); N]) -> Self {\n        fields.into_iter().collect()\n    }\n}\nimpl<Id: Into<RawIdentifier>, const N: usize> From<[(Id, AlgebraicType); N]> for ProductType {\n    fn from(fields: [(Id, AlgebraicType); N]) -> Self {\n        fields.into_iter().collect()\n    }\n}\nimpl<const N: usize> From<[AlgebraicType; N]> for ProductType {\n    fn from(fields: [AlgebraicType; N]) -> Self {\n        fields.into_iter().collect()\n    }\n}\n\nimpl MetaType for ProductType {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::product([(\"elements\", AlgebraicType::array(ProductTypeElement::meta_type()))])\n    }\n}\n\nimpl ProductType {\n    pub fn as_value(&self) -> AlgebraicValue {\n        value_serialize(self)\n    }\n\n    pub fn from_value(value: &AlgebraicValue) -> Result<ProductType, ValueDeserializeError> {\n        Self::deserialize(ValueDeserializer::from_ref(value))\n    }\n}\n\nimpl<'a> WithTypespace<'a, ProductType> {\n    #[inline]\n    pub fn elements(&self) -> ElementsWithTypespace<'a> {\n        self.iter_with(&*self.ty().elements)\n    }\n\n    #[inline]\n    pub fn with_values<I: IntoIterator<Item = &'a AlgebraicValue>>(\n        &self,\n        vals: I,\n    ) -> ElementValuesWithType<'a, I::IntoIter>\n    where\n        I::IntoIter: ExactSizeIterator,\n    {\n        let elements = self.elements();\n        let vals = vals.into_iter();\n        assert_eq!(elements.len(), vals.len());\n        ElementValuesWithType {\n            inner: std::iter::zip(elements, vals),\n        }\n    }\n}\n\nimpl<'a> IntoIterator for WithTypespace<'a, ProductType> {\n    type Item = WithTypespace<'a, ProductTypeElement>;\n    type IntoIter = ElementsWithTypespace<'a>;\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        self.elements()\n    }\n}\n\npub type ElementsWithTypespace<'a> = crate::IterWithTypespace<'a, std::slice::Iter<'a, ProductTypeElement>>;\n\npub struct ElementValuesWithType<'a, I> {\n    inner: std::iter::Zip<ElementsWithTypespace<'a>, I>,\n}\n\nimpl<'a, I> Iterator for ElementValuesWithType<'a, I>\nwhere\n    I: ExactSizeIterator<Item = &'a AlgebraicValue>,\n{\n    type Item = ValueWithType<'a, AlgebraicValue>;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.next().map(|(ty, val)| ty.algebraic_type().with_value(val))\n    }\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\nimpl<'a, I> ExactSizeIterator for ElementValuesWithType<'a, I> where I: ExactSizeIterator<Item = &'a AlgebraicValue> {}\n"
  },
  {
    "path": "crates/sats/src/product_type_element.rs",
    "content": "use crate::meta_type::MetaType;\nuse crate::raw_identifier::RawIdentifier;\nuse crate::{AlgebraicType, SpacetimeType, WithTypespace};\n\n/// A factor / element of a product type.\n///\n/// An element consist of an optional name and a type.\n///\n/// NOTE: Each element has an implicit element tag based on its order.\n/// Uniquely identifies an element similarly to protobuf tags.\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType)]\n#[sats(crate = crate)]\npub struct ProductTypeElement {\n    /// The name of the field / element.\n    ///\n    /// As our type system is structural,\n    /// a type like `{ foo: U8 }`, where `foo: U8` is the `ProductTypeElement`,\n    /// is inequal to `{ bar: U8 }`, although their `algebraic_type`s (`U8`) match.\n    pub name: Option<RawIdentifier>,\n    /// The type of the element.\n    ///\n    /// Only values of this type can be stored in the element.\n    pub algebraic_type: AlgebraicType,\n}\n\nimpl ProductTypeElement {\n    /// Returns an element with the given `name` and `algebraic_type`.\n    pub const fn new(algebraic_type: AlgebraicType, name: Option<RawIdentifier>) -> Self {\n        Self { algebraic_type, name }\n    }\n\n    /// Returns a named element with `name` and `algebraic_type`.\n    pub fn new_named(algebraic_type: AlgebraicType, name: impl Into<RawIdentifier>) -> Self {\n        Self::new(algebraic_type, Some(name.into()))\n    }\n\n    /// Returns the name of the field.\n    pub fn name(&self) -> Option<&RawIdentifier> {\n        self.name.as_ref()\n    }\n\n    /// Returns whether the field has the given name.\n    pub fn has_name(&self, name: &str) -> bool {\n        self.name().is_some_and(|n| &**n == name)\n    }\n}\n\nimpl MetaType for ProductTypeElement {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::product([\n            (\"name\", AlgebraicType::option(AlgebraicType::String)),\n            (\"algebraic_type\", AlgebraicType::ZERO_REF),\n        ])\n    }\n}\n\nimpl From<AlgebraicType> for ProductTypeElement {\n    fn from(value: AlgebraicType) -> Self {\n        ProductTypeElement::new(value, None)\n    }\n}\n\nimpl<'a> WithTypespace<'a, ProductTypeElement> {\n    #[inline]\n    pub fn algebraic_type(&self) -> WithTypespace<'a, AlgebraicType> {\n        self.with(&self.ty().algebraic_type)\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/product_value.rs",
    "content": "use crate::algebraic_value::AlgebraicValue;\nuse crate::product_type::ProductType;\nuse crate::{ArrayValue, SumValue, ValueWithType};\nuse spacetimedb_primitives::{ColId, ColList};\n\n/// A product value is made of a list of\n/// \"elements\" / \"fields\" / \"factors\" of other `AlgebraicValue`s.\n///\n/// The type of a product value is a [product type](`ProductType`).\n#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]\npub struct ProductValue {\n    /// The values that make up this product value.\n    pub elements: Box<[AlgebraicValue]>,\n}\n\n/// Constructs a product value from a list of fields with syntax `product![v1, v2, ...]`.\n///\n/// Repeat notation from `vec![x; n]` is not supported.\n#[macro_export]\nmacro_rules! product {\n    [$($elems:expr),*$(,)?] => {\n        $crate::ProductValue {\n            elements: [$($crate::AlgebraicValue::from($elems)),*].into(),\n        }\n    }\n}\n\nimpl FromIterator<AlgebraicValue> for ProductValue {\n    fn from_iter<T: IntoIterator<Item = AlgebraicValue>>(iter: T) -> Self {\n        let elements = iter.into_iter().collect();\n        Self { elements }\n    }\n}\n\nimpl IntoIterator for ProductValue {\n    type Item = AlgebraicValue;\n    type IntoIter = std::vec::IntoIter<AlgebraicValue>;\n    fn into_iter(self) -> Self::IntoIter {\n        Vec::from(self.elements).into_iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a ProductValue {\n    type Item = &'a AlgebraicValue;\n    type IntoIter = std::slice::Iter<'a, AlgebraicValue>;\n    fn into_iter(self) -> Self::IntoIter {\n        self.elements.iter()\n    }\n}\n\nimpl crate::Value for ProductValue {\n    type Type = ProductType;\n}\n\n/// An error that occurs when a field, of a product value, is accessed that doesn't exist.\n#[derive(thiserror::Error, Debug, Copy, Clone)]\n#[error(\"Field at position {col_pos} named: {name:?} not found or has an invalid type\")]\npub struct InvalidFieldError {\n    /// The claimed col_pos of the field within the product value.\n    pub col_pos: ColId,\n    /// The name of the field, if any.\n    pub name: Option<&'static str>,\n}\n\nimpl From<ColId> for InvalidFieldError {\n    fn from(col_pos: ColId) -> Self {\n        Self { col_pos, name: None }\n    }\n}\n\nimpl ProductValue {\n    /// Borrow the value at field of `self` identified by `col_pos`.\n    ///\n    /// The `name` is non-functional and is only used for error-messages.\n    pub fn get_field(&self, col_pos: usize, name: Option<&'static str>) -> Result<&AlgebraicValue, InvalidFieldError> {\n        self.elements.get(col_pos).ok_or(InvalidFieldError {\n            col_pos: col_pos.into(),\n            name,\n        })\n    }\n\n    /// This utility function is designed to project fields based on the supplied `indexes`.\n    ///\n    /// **Important:**\n    ///\n    /// The resulting [AlgebraicValue] will wrap into a [ProductValue] when projecting multiple\n    /// (including zero) fields, otherwise it will consist of a single [AlgebraicValue].\n    /// If you want to wrap single elements in a [ProductValue] as well, see [Self::project_product].\n    ///\n    /// **Parameters:**\n    /// - `cols`: A [ColList] containing the indexes of fields to be projected.\n    pub fn project(&self, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {\n        if let Some(head) = cols.as_singleton() {\n            self.get_field(head.idx(), None).cloned()\n        } else {\n            let mut fields = Vec::with_capacity(cols.len() as usize);\n            for col in cols.iter() {\n                fields.push(self.get_field(col.idx(), None)?.clone());\n            }\n            Ok(AlgebraicValue::product(fields))\n        }\n    }\n\n    /// This utility function is designed to project fields based on the supplied `indexes`.\n    ///\n    /// **Important:**\n    ///\n    /// Returns a [ProductValue] even when projecting a single element.\n    /// If you don't want to wrap a single element in a [ProductValue], see [Self::project].\n    ///\n    /// **Parameters:**\n    /// - `cols`: A [ColList] containing the indexes of fields to be projected.\n    pub fn project_product(&self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {\n        let mut fields = Vec::with_capacity(cols.len() as usize);\n        for col in cols.iter() {\n            fields.push(self.get_field(col.idx(), None)?.clone());\n        }\n        Ok(ProductValue::from(fields))\n    }\n\n    /// Extracts the `value` at field of `self` identified by `index`\n    /// and then runs it through the function `f` which possibly returns a `T` derived from `value`.\n    pub fn extract_field<'a, T>(\n        &'a self,\n        col_pos: usize,\n        name: Option<&'static str>,\n        f: impl 'a + Fn(&'a AlgebraicValue) -> Option<T>,\n    ) -> Result<T, InvalidFieldError> {\n        f(self.get_field(col_pos, name)?).ok_or(InvalidFieldError {\n            col_pos: col_pos.into(),\n            name,\n        })\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `bool`.\n    pub fn field_as_bool(&self, index: usize, named: Option<&'static str>) -> Result<bool, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_bool().copied())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `u8`.\n    pub fn field_as_u8(&self, index: usize, named: Option<&'static str>) -> Result<u8, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_u8().copied())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `u32`.\n    pub fn field_as_u32(&self, index: usize, named: Option<&'static str>) -> Result<u32, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_u32().copied())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `u64`.\n    pub fn field_as_u64(&self, index: usize, named: Option<&'static str>) -> Result<u64, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_u64().copied())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `i64`.\n    pub fn field_as_i64(&self, index: usize, named: Option<&'static str>) -> Result<i64, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_i64().copied())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `i128`.\n    pub fn field_as_i128(&self, index: usize, named: Option<&'static str>) -> Result<i128, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_i128().copied().map(|x| x.0))\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `u128`.\n    pub fn field_as_u128(&self, index: usize, named: Option<&'static str>) -> Result<u128, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_u128().copied().map(|x| x.0))\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a string slice.\n    pub fn field_as_str(&self, index: usize, named: Option<&'static str>) -> Result<&str, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_string()).map(|x| &**x)\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a byte slice.\n    pub fn field_as_bytes(&self, index: usize, named: Option<&'static str>) -> Result<&[u8], InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_bytes())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as an `ArrayValue`.\n    pub fn field_as_array(&self, index: usize, named: Option<&'static str>) -> Result<&ArrayValue, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_array())\n    }\n\n    /// Interprets the value at field of `self` identified by `index` as a `SumValue`.\n    pub fn field_as_sum(&self, index: usize, named: Option<&'static str>) -> Result<SumValue, InvalidFieldError> {\n        self.extract_field(index, named, |f| f.as_sum().cloned())\n    }\n}\n\nimpl<'a> ValueWithType<'a, ProductValue> {\n    pub fn elements(&self) -> impl ExactSizeIterator<Item = ValueWithType<'a, AlgebraicValue>> + use<'a> {\n        self.ty_s().with_values(self.value())\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/proptest.rs",
    "content": "//! Proptest generators for the subset of `AlgebraicType` and `AlgebraicValue` which our tables can store.\n//!\n//! This notably excludes `Ref` types.\n\nuse crate::raw_identifier::RawIdentifier;\nuse crate::{i256, u256, ProductTypeElement, SumTypeVariant};\nuse crate::{\n    AlgebraicType, AlgebraicTypeRef, AlgebraicValue, ArrayValue, ProductType, ProductValue, SumType, SumValue,\n    Typespace, F32, F64,\n};\nuse core::fmt::Debug;\nuse proptest::{\n    collection::{vec, SizeRange},\n    prelude::*,\n    prop_oneof,\n};\n\npub const SIZE: usize = 16;\n\n/// Generates primitive `AlgebraicType`s.\n///\n/// These are bool, the integer types, and the float types.\npub fn generate_primitive_algebraic_type() -> impl Strategy<Value = AlgebraicType> {\n    prop_oneof![\n        Just(AlgebraicType::Bool),\n        Just(AlgebraicType::U8),\n        Just(AlgebraicType::I8),\n        Just(AlgebraicType::U16),\n        Just(AlgebraicType::I16),\n        Just(AlgebraicType::U32),\n        Just(AlgebraicType::I32),\n        Just(AlgebraicType::U64),\n        Just(AlgebraicType::I64),\n        Just(AlgebraicType::U128),\n        Just(AlgebraicType::I128),\n        Just(AlgebraicType::U256),\n        Just(AlgebraicType::I256),\n        Just(AlgebraicType::F32),\n        Just(AlgebraicType::F64),\n    ]\n}\n\n/// Generates leaf (i.e. non-compound) `AlgebraicType`s.\n///\n/// These are types which do not contain other types,\n/// i.e. bools, integers, floats, strings and units.\n///\n/// All other generated `AlgebraicType`s  are compound,\n/// i.e. contain at least one child `AlgebraicType`,\n/// and thus require recursive generation.\nfn generate_non_compound_algebraic_type() -> impl Strategy<Value = AlgebraicType> {\n    prop_oneof![\n        generate_primitive_algebraic_type(),\n        Just(AlgebraicType::String),\n        Just(AlgebraicType::unit()),\n    ]\n}\n\n/// Generate an algebraic type wrapping leaf types.\nfn generate_algebraic_type_from_leaves(\n    leaves: impl Strategy<Value = AlgebraicType> + 'static,\n    depth: u32,\n) -> impl Strategy<Value = AlgebraicType> {\n    leaves.prop_recursive(depth, SIZE as u32, SIZE as u32, |gen_element| {\n        prop_oneof![\n            gen_element.clone().prop_map(AlgebraicType::array),\n            // No need to generate units here;\n            // we already generate them in `generate_non_compound_algebraic_type`.\n            vec(gen_element.clone().prop_map_into(), 1..=SIZE)\n                .prop_map(|vec| vec\n                    .into_iter()\n                    .enumerate()\n                    .map(|(i, ty)| ProductTypeElement {\n                        // Generate names because the validation code in the `schema` crate requires them.\n                        name: Some(RawIdentifier::new(format!(\"field_{i}\"))),\n                        algebraic_type: ty\n                    })\n                    .collect())\n                .prop_map(Vec::into_boxed_slice)\n                .prop_map(AlgebraicType::product),\n            // Do not generate never here; we can't store never in a page.\n            vec(gen_element.clone().prop_map_into(), 1..=SIZE)\n                .prop_map(|vec| vec\n                    .into_iter()\n                    .enumerate()\n                    .map(|(i, ty)| SumTypeVariant {\n                        name: Some(RawIdentifier::new(format!(\"variant_{i}\"))),\n                        algebraic_type: ty\n                    })\n                    .collect::<Vec<_>>())\n                .prop_map(Vec::into_boxed_slice)\n                .prop_map(AlgebraicType::sum),\n        ]\n    })\n}\n\n/// Generates `AlgebraicType`s, not including recursive (i.e. `Ref` types),\n/// but including compound types (i.e. `Product` and `Sum` types).\n///\n/// Any type generated here is valid as a column in a row type.\npub fn generate_algebraic_type() -> impl Strategy<Value = AlgebraicType> {\n    generate_algebraic_type_from_leaves(generate_non_compound_algebraic_type(), 4)\n}\n\n/// Generates a `ProductType` that is good as a row type.\npub fn generate_row_type(range: impl Into<SizeRange>) -> impl Strategy<Value = ProductType> {\n    vec(generate_algebraic_type().prop_map_into(), range)\n        .prop_map(Vec::into_boxed_slice)\n        .prop_map_into()\n}\n\n/// Generates an `AlgebraicValue` for values `Val: Arbitrary`.\nfn generate_non_compound<Val: Arbitrary + Into<AlgebraicValue> + 'static>() -> BoxedStrategy<AlgebraicValue> {\n    any::<Val>().prop_map(Into::into).boxed()\n}\n\n/// Generates any `u256`.\npub fn any_u256() -> impl Strategy<Value = u256> {\n    any::<(u128, u128)>().prop_map(|(hi, lo)| u256::from_words(hi, lo))\n}\n\n/// Generates any `i256`.\npub fn any_i256() -> impl Strategy<Value = i256> {\n    any::<(i128, i128)>().prop_map(|(hi, lo)| i256::from_words(hi, lo))\n}\n\n/// Generates an `AlgebraicValue` typed at `ty`.\npub fn generate_algebraic_value(ty: AlgebraicType) -> impl Strategy<Value = AlgebraicValue> {\n    match ty {\n        AlgebraicType::Bool => generate_non_compound::<bool>(),\n        AlgebraicType::I8 => generate_non_compound::<i8>(),\n        AlgebraicType::U8 => generate_non_compound::<u8>(),\n        AlgebraicType::I16 => generate_non_compound::<i16>(),\n        AlgebraicType::U16 => generate_non_compound::<u16>(),\n        AlgebraicType::I32 => generate_non_compound::<i32>(),\n        AlgebraicType::U32 => generate_non_compound::<u32>(),\n        AlgebraicType::I64 => generate_non_compound::<i64>(),\n        AlgebraicType::U64 => generate_non_compound::<u64>(),\n        AlgebraicType::I128 => generate_non_compound::<i128>(),\n        AlgebraicType::U128 => generate_non_compound::<u128>(),\n        AlgebraicType::I256 => any_i256().prop_map_into().boxed(),\n        AlgebraicType::U256 => any_u256().prop_map_into().boxed(),\n        AlgebraicType::F32 => generate_non_compound::<f32>(),\n        AlgebraicType::F64 => generate_non_compound::<f64>(),\n        AlgebraicType::String => generate_non_compound::<Box<str>>(),\n\n        AlgebraicType::Array(ty) => generate_array_value(*ty.elem_ty).prop_map_into().boxed(),\n\n        AlgebraicType::Product(ty) => generate_product_value(ty).prop_map_into().boxed(),\n\n        AlgebraicType::Sum(ty) => generate_sum_value(ty).prop_map_into().boxed(),\n\n        AlgebraicType::Ref(_) => unreachable!(),\n    }\n}\n\n/// Generates a `ProductValue` typed at `ty`.\npub fn generate_product_value(ty: ProductType) -> impl Strategy<Value = ProductValue> {\n    Vec::from(ty.elements)\n        .into_iter()\n        .map(|elem| generate_algebraic_value(elem.algebraic_type))\n        .collect::<Vec<_>>()\n        .prop_map_into()\n}\n\n/// Generates a `SumValue` typed at `ty`.\nfn generate_sum_value(ty: SumType) -> impl Strategy<Value = SumValue> {\n    // A dependent problem, generate a tag\n    // and then generate a value typed at the tag' data type.\n    (0..ty.variants.len()).prop_flat_map(move |tag: usize| {\n        let variant_ty = ty.variants[tag].clone();\n        let gen_variant = generate_algebraic_value(variant_ty.algebraic_type);\n        gen_variant.prop_map(move |value| SumValue::new(tag as u8, value))\n    })\n}\n\n/// Generates an array value given an element generator `gen_elem`.\nfn generate_array_of<S>(gen_elem: S) -> BoxedStrategy<ArrayValue>\nwhere\n    S: Strategy + 'static,\n    Box<[S::Value]>: 'static + Into<ArrayValue>,\n{\n    vec(gen_elem, 0..=SIZE)\n        .prop_map(Vec::into_boxed_slice)\n        .prop_map_into()\n        .boxed()\n}\n\n/// Generates an array value with elements typed at `ty`.\nfn generate_array_value(ty: AlgebraicType) -> BoxedStrategy<ArrayValue> {\n    match ty {\n        AlgebraicType::Bool => generate_array_of(any::<bool>()),\n        AlgebraicType::I8 => generate_array_of(any::<i8>()),\n        AlgebraicType::U8 => generate_array_of(any::<u8>()),\n        AlgebraicType::I16 => generate_array_of(any::<i16>()),\n        AlgebraicType::U16 => generate_array_of(any::<u16>()),\n        AlgebraicType::I32 => generate_array_of(any::<i32>()),\n        AlgebraicType::U32 => generate_array_of(any::<u32>()),\n        AlgebraicType::I64 => generate_array_of(any::<i64>()),\n        AlgebraicType::U64 => generate_array_of(any::<u64>()),\n        AlgebraicType::I128 => generate_array_of(any::<i128>()),\n        AlgebraicType::U128 => generate_array_of(any::<u128>()),\n        AlgebraicType::I256 => generate_array_of(any_i256()),\n        AlgebraicType::U256 => generate_array_of(any_u256()),\n        AlgebraicType::F32 => generate_array_of(any::<f32>().prop_map_into::<F32>()),\n        AlgebraicType::F64 => generate_array_of(any::<f64>().prop_map_into::<F64>()),\n        AlgebraicType::String => generate_array_of(any::<Box<str>>()),\n        AlgebraicType::Product(ty) => generate_array_of(generate_product_value(ty)),\n        AlgebraicType::Sum(ty) => generate_array_of(generate_sum_value(ty)),\n        AlgebraicType::Array(ty) => generate_array_of(generate_array_value(*ty.elem_ty)),\n        AlgebraicType::Ref(_) => unreachable!(),\n    }\n}\n\nfn gen_with<T: Clone + Debug, US: Strategy>(\n    with: impl Strategy<Value = T>,\n    then: impl Fn(T) -> US,\n) -> impl Strategy<Value = (T, US::Value)> {\n    with.prop_flat_map(move |val| (Just(val.clone()), then(val)))\n}\n\n/// Generates a row type `ty` and a row value typed at `ty`.\npub fn generate_typed_row() -> impl Strategy<Value = (ProductType, ProductValue)> {\n    gen_with(generate_row_type(0..=SIZE), generate_product_value)\n}\n\npub fn generate_typed_row_vec(\n    size: impl Into<SizeRange>,\n    num_rows_min: usize,\n    num_rows_max: usize,\n) -> impl Strategy<Value = (ProductType, Vec<ProductValue>)> {\n    gen_with(generate_row_type(size), move |ty| {\n        vec(generate_product_value(ty), num_rows_min..num_rows_max)\n    })\n}\n\n/// Generates a type `ty` and a value typed at `ty`.\npub fn generate_typed_value() -> impl Strategy<Value = (AlgebraicType, AlgebraicValue)> {\n    gen_with(generate_algebraic_type(), generate_algebraic_value)\n}\n\n/// Generate a `Ref` to something in a `Typespace` of this length.\nfn generate_ref(typespace_len: u32) -> BoxedStrategy<AlgebraicType> {\n    (0..typespace_len).prop_map(|n| AlgebraicTypeRef(n).into()).boxed()\n}\n\n/// Generate a type valid to be used to generate a type *use* in a client module.\n/// That is, a ref, non-compound type, a special type, or an array, map, option, result of the same.\nfn generate_type_valid_for_client_use() -> impl Strategy<Value = AlgebraicType> {\n    let leaf = prop_oneof![\n        generate_non_compound_algebraic_type(),\n        Just(AlgebraicType::identity()),\n        Just(AlgebraicType::connection_id()),\n    ];\n\n    let size = 3;\n\n    leaf.prop_recursive(size, size, size, |gen_element| {\n        prop_oneof![\n            gen_element.clone().prop_map(AlgebraicType::array),\n            gen_element.clone().prop_map(AlgebraicType::option),\n            gen_element.clone().prop_map(|x| AlgebraicType::result(x.clone(), x)),\n        ]\n    })\n}\n\n/// Generate a `Typespace` valid for client code generation with `size` elements.\n///\n/// We don't prop_map on the size because it supposedly can lead to exponential shrinking times.\n///\n/// Does not generate nested arrays or maps currently, although these would be allowed.\npub fn generate_typespace_valid_for_codegen(size: u32) -> impl Strategy<Value = Typespace> {\n    let generate_value = generate_type_valid_for_client_use().boxed();\n\n    let types = (0..size)\n        .map(|current_len| {\n            let leaf = if current_len == 0 {\n                generate_value.clone()\n            } else {\n                generate_value.clone().prop_union(generate_ref(current_len)).boxed()\n            };\n            // depth 1 means these will either be leaves or a single level of nesting.\n            generate_algebraic_type_from_leaves(leaf, 1)\n        })\n        .collect::<Vec<_>>();\n\n    types.prop_map(FromIterator::from_iter)\n}\n"
  },
  {
    "path": "crates/sats/src/raw_identifier.rs",
    "content": "use crate::algebraic_type::AlgebraicType;\nuse crate::{impl_deserialize, impl_serialize, impl_st};\nuse core::borrow::Borrow;\nuse core::fmt;\nuse core::ops::Deref;\nuse lean_string::{LeanString, ToLeanString};\n\n/// A not-yet-validated identifier.\n#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]\npub struct RawIdentifier(pub(crate) LeanString);\n\nimpl_st!([] RawIdentifier, _ts => AlgebraicType::String);\nimpl_serialize!([] RawIdentifier, (self, ser) => ser.serialize_str(&self.0));\nimpl_deserialize!([] RawIdentifier, de => LeanString::deserialize(de).map(Self));\nimpl RawIdentifier {\n    /// Creates a new `RawIdentifier` from a string.\n    pub fn new(name: impl Into<LeanString>) -> Self {\n        Self(name.into())\n    }\n\n    pub fn into_inner(self) -> LeanString {\n        self.0\n    }\n}\n\nimpl Deref for RawIdentifier {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl AsRef<str> for RawIdentifier {\n    fn as_ref(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl Borrow<str> for RawIdentifier {\n    fn borrow(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl fmt::Debug for RawIdentifier {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&self.0, f)\n    }\n}\n\nimpl fmt::Display for RawIdentifier {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.0, f)\n    }\n}\n\nimpl From<&'static str> for RawIdentifier {\n    fn from(s: &'static str) -> Self {\n        RawIdentifier(LeanString::from_static_str(s))\n    }\n}\n\nimpl From<String> for RawIdentifier {\n    fn from(s: String) -> Self {\n        RawIdentifier(s.to_lean_string())\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/resolve_refs.rs",
    "content": "use crate::{\n    typespace::TypeRefError, AlgebraicType, AlgebraicTypeRef, ArrayType, ProductType, ProductTypeElement, SumType,\n    SumTypeVariant, WithTypespace,\n};\n\n/// Resolver for [`AlgebraicTypeRef`]s within a structure.\n#[derive(Default)]\npub struct ResolveRefState {\n    /// The stack used to handle cycle detection for [recursive types] (`μα. T`).\n    ///\n    /// [recursive types]: https://en.wikipedia.org/wiki/Recursive_data_type#Theory\n    stack: Vec<AlgebraicTypeRef>,\n}\n\n/// A trait for types that know how to resolve their [`AlgebraicTypeRef`]s\n/// provided a typing context and the resolver `state`.\npub trait ResolveRefs {\n    /// Output type after type references have been resolved.\n    type Output;\n\n    /// Returns, if possible, an output with all [`AlgebraicTypeRef`]s\n    /// within `this` (typing context carried) resolved\n    /// using the provided resolver `state`.\n    ///\n    /// `Err` is returned if a cycle was detected, or if any `AlgebraicTypeRef` touched was invalid.\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError>;\n}\n\n// -----------------------------------------------------------------------------\n// The interesting logic:\n// -----------------------------------------------------------------------------\n\nimpl ResolveRefs for AlgebraicTypeRef {\n    type Output = AlgebraicType;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        // Suppose we have `&0 = { Nil, Cons({ elem: U8, tail: &0 }) }`.\n        // This is our standard cons-list type.\n        // In this setup, when getting to `tail`,\n        // we would recurse back to expanding `tail` again, and so or...\n        // So we will never halt. This check breaks that cycle.\n        if state.stack.contains(this.ty()) {\n            return Err(TypeRefError::RecursiveTypeRef(*this.ty()));\n        }\n\n        // Push ourselves to the stack.\n        state.stack.push(*this.ty());\n\n        // Extract the `at: AlgebraicType` pointed to by `this` and then resolve `at`.\n        let ret = this\n            .typespace()\n            .get(*this.ty())\n            .ok_or(TypeRefError::InvalidTypeRef(*this.ty()))\n            .and_then(|at| this.with(at)._resolve_refs(state));\n\n        // Remove ourselves.\n        state.stack.pop();\n        ret\n    }\n}\n\n// -----------------------------------------------------------------------------\n// All the below is just plumbing:\n// -----------------------------------------------------------------------------\n\nimpl ResolveRefs for AlgebraicType {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        match this.ty() {\n            Self::Ref(r) => this.with(r)._resolve_refs(state),\n            Self::Sum(sum) => this.with(sum)._resolve_refs(state).map(Into::into),\n            Self::Product(prod) => this.with(prod)._resolve_refs(state).map(Into::into),\n            Self::Array(ty) => this.with(ty)._resolve_refs(state).map(Into::into),\n            // These types are plain and cannot have refs in them.\n            x => Ok(x.clone()),\n        }\n    }\n}\n\nimpl ResolveRefs for ArrayType {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        Ok(Self {\n            elem_ty: Box::new(this.map(|m| &*m.elem_ty)._resolve_refs(state)?),\n        })\n    }\n}\n\nimpl ResolveRefs for ProductType {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        let elements = this\n            .ty()\n            .elements\n            .iter()\n            .map(|el| this.with(el)._resolve_refs(state))\n            .collect::<Result<_, _>>()?;\n        Ok(ProductType { elements })\n    }\n}\n\nimpl ResolveRefs for ProductTypeElement {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        Ok(Self {\n            algebraic_type: this.map(|e| &e.algebraic_type)._resolve_refs(state)?,\n            name: this.ty().name.clone(),\n        })\n    }\n}\n\nimpl ResolveRefs for SumType {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        let variants = this\n            .ty()\n            .variants\n            .iter()\n            .map(|v| this.with(v)._resolve_refs(state))\n            .collect::<Result<_, _>>()?;\n        Ok(Self { variants })\n    }\n}\n\nimpl ResolveRefs for SumTypeVariant {\n    type Output = Self;\n    fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {\n        Ok(Self {\n            algebraic_type: this.map(|v| &v.algebraic_type)._resolve_refs(state)?,\n            name: this.ty().name.clone(),\n        })\n    }\n}\n\nimpl<T: ResolveRefs> WithTypespace<'_, T> {\n    pub fn resolve_refs(self) -> Result<T::Output, TypeRefError> {\n        T::resolve_refs(self, &mut ResolveRefState::default())\n    }\n    fn _resolve_refs(self, state: &mut ResolveRefState) -> Result<T::Output, TypeRefError> {\n        T::resolve_refs(self, state)\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/satn.rs",
    "content": "use crate::time_duration::TimeDuration;\nuse crate::timestamp::Timestamp;\nuse crate::uuid::Uuid;\nuse crate::{i256, u256, AlgebraicType, AlgebraicValue, ProductValue, Serialize, SumValue, ValueWithType};\nuse crate::{ser, ProductType, ProductTypeElement};\nuse core::fmt;\nuse core::fmt::Write as _;\nuse derive_more::{Display, From, Into};\nuse std::borrow::Cow;\nuse std::marker::PhantomData;\n\n/// An extension trait for [`Serialize`] providing formatting methods.\npub trait Satn: ser::Serialize {\n    /// Formats the value using the SATN data format into the formatter `f`.\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        Writer::with(f, |f| self.serialize(SatnFormatter { f }))?;\n        Ok(())\n    }\n\n    /// Formats the value using the postgres SATN(PsqlFormatter { f }, /* PsqlType */) formatter `f`.\n    fn fmt_psql(&self, f: &mut fmt::Formatter, ty: &PsqlType<'_>) -> fmt::Result {\n        Writer::with(f, |f| {\n            self.serialize(TypedSerializer {\n                ty,\n                f: &mut SqlFormatter {\n                    fmt: SatnFormatter { f },\n                    ty,\n                },\n            })\n        })?;\n        Ok(())\n    }\n\n    /// Formats the value using the SATN data format into the returned `String`.\n    fn to_satn(&self) -> String {\n        Wrapper::from_ref(self).to_string()\n    }\n\n    /// Pretty prints the value using the SATN data format into the returned `String`.\n    fn to_satn_pretty(&self) -> String {\n        format!(\"{:#}\", Wrapper::from_ref(self))\n    }\n}\n\nimpl<T: ser::Serialize + ?Sized> Satn for T {}\n\n/// A wrapper around a `T: Satn`\n/// providing `Display` and `Debug` implementations\n/// that uses the SATN formatting for `T`.\n#[repr(transparent)]\npub struct Wrapper<T: ?Sized>(pub T);\n\nimpl<T: ?Sized> Wrapper<T> {\n    /// Converts `&T` to `&Wrapper<T>`.\n    pub fn from_ref(t: &T) -> &Self {\n        // SAFETY: `repr(transparent)` turns the ABI of `T`\n        // into the same as `Self` so we can also cast `&T` to `&Self`.\n        unsafe { &*(t as *const T as *const Self) }\n    }\n}\n\nimpl<T: Satn + ?Sized> fmt::Display for Wrapper<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nimpl<T: Satn + ?Sized> fmt::Debug for Wrapper<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n/// A wrapper around a `T: Satn`\n/// providing `Display` and `Debug` implementations\n/// that uses postgres SATN formatting for `T`.\npub struct PsqlWrapper<'a, T: ?Sized> {\n    pub ty: PsqlType<'a>,\n    pub value: T,\n}\n\nimpl<T: ?Sized> PsqlWrapper<'_, T> {\n    /// Converts `&T` to `&PsqlWrapper<T>`.\n    pub fn from_ref(t: &T) -> &Self {\n        // SAFETY: `repr(transparent)` turns the ABI of `T`\n        // into the same as `Self` so we can also cast `&T` to `&Self`.\n        unsafe { &*(t as *const T as *const Self) }\n    }\n}\n\nimpl<T: Satn + ?Sized> fmt::Display for PsqlWrapper<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.value.fmt_psql(f, &self.ty)\n    }\n}\n\nimpl<T: Satn + ?Sized> fmt::Debug for PsqlWrapper<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.value.fmt_psql(f, &self.ty)\n    }\n}\n\n/// Wraps a writer for formatting lists separated by `SEP` into it.\nstruct EntryWrapper<'a, 'f, const SEP: char> {\n    /// The writer we're formatting into.\n    fmt: Writer<'a, 'f>,\n    /// Whether there were any fields.\n    /// Initially `false` and then `true` after calling [`.entry(..)`](EntryWrapper::entry).\n    has_fields: bool,\n}\n\nimpl<'a, 'f, const SEP: char> EntryWrapper<'a, 'f, SEP> {\n    /// Constructs the entry wrapper using the writer `fmt`.\n    fn new(fmt: Writer<'a, 'f>) -> Self {\n        Self { fmt, has_fields: false }\n    }\n\n    /// Formats another entry in the larger structure.\n    ///\n    /// The formatting for the element / entry itself is provided by the function `entry`.\n    fn entry(&mut self, entry: impl FnOnce(Writer) -> fmt::Result) -> fmt::Result {\n        let res = (|| match &mut self.fmt {\n            Writer::Pretty(f) => {\n                if !self.has_fields {\n                    f.write_char('\\n')?;\n                }\n                f.state.indent += 1;\n                entry(Writer::Pretty(f.as_mut()))?;\n                f.write_char(SEP)?;\n                f.write_char('\\n')?;\n                f.state.indent -= 1;\n                Ok(())\n            }\n            Writer::Normal(f) => {\n                if self.has_fields {\n                    f.write_char(SEP)?;\n                    f.write_char(' ')?;\n                }\n                entry(Writer::Normal(f))\n            }\n        })();\n        self.has_fields = true;\n        res\n    }\n}\n\n/// An implementation of [`fmt::Write`] supporting indented and non-idented formatting.\nenum Writer<'a, 'f> {\n    /// Uses the standard library's formatter i.e. plain formatting.\n    Normal(&'a mut fmt::Formatter<'f>),\n    /// Uses indented formatting.\n    Pretty(IndentedWriter<'a, 'f>),\n}\n\nimpl<'f> Writer<'_, 'f> {\n    /// Provided with a formatter `f`, runs `func` provided with a `Writer`.\n    fn with<R>(f: &mut fmt::Formatter<'_>, func: impl FnOnce(Writer<'_, '_>) -> R) -> R {\n        let mut state;\n        // We use `alternate`, i.e., the `#` flag to let the user trigger pretty printing.\n        let f = if f.alternate() {\n            state = IndentState {\n                indent: 0,\n                on_newline: true,\n            };\n            Writer::Pretty(IndentedWriter { f, state: &mut state })\n        } else {\n            Writer::Normal(f)\n        };\n        func(f)\n    }\n\n    /// Returns a sub-writer without moving `self`.\n    fn as_mut(&mut self) -> Writer<'_, 'f> {\n        match self {\n            Writer::Normal(f) => Writer::Normal(f),\n            Writer::Pretty(f) => Writer::Pretty(f.as_mut()),\n        }\n    }\n}\n\n/// A formatter that adds decoration atop of the standard library's formatter.\nstruct IndentedWriter<'a, 'f> {\n    f: &'a mut fmt::Formatter<'f>,\n    state: &'a mut IndentState,\n}\n\n/// The indentation state.\nstruct IndentState {\n    /// Number of tab indentations to make.\n    indent: u32,\n    /// Whether we were last on a newline.\n    on_newline: bool,\n}\n\nimpl<'f> IndentedWriter<'_, 'f> {\n    /// Returns a sub-writer without moving `self`.\n    fn as_mut(&mut self) -> IndentedWriter<'_, 'f> {\n        IndentedWriter {\n            f: self.f,\n            state: self.state,\n        }\n    }\n}\n\nimpl fmt::Write for IndentedWriter<'_, '_> {\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        for s in s.split_inclusive('\\n') {\n            if self.state.on_newline {\n                // Indent 4 characters times the indentation level.\n                for _ in 0..self.state.indent {\n                    self.f.write_str(\"    \")?;\n                }\n            }\n\n            self.state.on_newline = s.ends_with('\\n');\n            self.f.write_str(s)?;\n        }\n        Ok(())\n    }\n}\n\nimpl fmt::Write for Writer<'_, '_> {\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        match self {\n            Writer::Normal(f) => f.write_str(s),\n            Writer::Pretty(f) => f.write_str(s),\n        }\n    }\n}\n\n/// Provides the SATN data format implementing [`Serializer`](ser::Serializer).\nstruct SatnFormatter<'a, 'f> {\n    /// The sink / writer / output / formatter.\n    f: Writer<'a, 'f>,\n}\n\nimpl SatnFormatter<'_, '_> {\n    fn ser_variant<T: ser::Serialize + ?Sized>(\n        &mut self,\n        _tag: u8,\n        name: Option<&str>,\n        value: &T,\n    ) -> Result<(), SatnError> {\n        write!(self, \"(\")?;\n        EntryWrapper::<','>::new(self.f.as_mut()).entry(|mut f| {\n            if let Some(name) = name {\n                write!(f, \"{name}\")?;\n            }\n            write!(f, \" = \")?;\n            value.serialize(SatnFormatter { f })?;\n            Ok(())\n        })?;\n        write!(self, \")\")?;\n\n        Ok(())\n    }\n}\n/// An error occurred during serialization to the SATS data format.\n#[derive(From, Into)]\npub struct SatnError(fmt::Error);\n\nimpl ser::Error for SatnError {\n    fn custom<T: fmt::Display>(_msg: T) -> Self {\n        Self(fmt::Error)\n    }\n}\n\nimpl SatnFormatter<'_, '_> {\n    /// Writes `args` formatted to `self`.\n    #[inline(always)]\n    fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), SatnError> {\n        self.f.write_fmt(args)?;\n        Ok(())\n    }\n}\n\nimpl<'a, 'f> ser::Serializer for SatnFormatter<'a, 'f> {\n    type Ok = ();\n    type Error = SatnError;\n    type SerializeArray = ArrayFormatter<'a, 'f>;\n    type SerializeSeqProduct = SeqFormatter<'a, 'f>;\n    type SerializeNamedProduct = NamedFormatter<'a, 'f>;\n\n    fn serialize_bool(mut self, v: bool) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u8(mut self, v: u8) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u16(mut self, v: u16) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u32(mut self, v: u32) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u64(mut self, v: u64) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u128(mut self, v: u128) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_u256(mut self, v: u256) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i8(mut self, v: i8) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i16(mut self, v: i16) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i32(mut self, v: i32) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i64(mut self, v: i64) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i128(mut self, v: i128) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_i256(mut self, v: i256) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_f32(mut self, v: f32) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n    fn serialize_f64(mut self, v: f64) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"{v}\")\n    }\n\n    fn serialize_str(mut self, v: &str) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"\\\"{v}\\\"\")\n    }\n\n    fn serialize_bytes(mut self, v: &[u8]) -> Result<Self::Ok, Self::Error> {\n        write!(self, \"0x{}\", hex::encode(v))\n    }\n\n    fn serialize_array(mut self, _len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        write!(self, \"[\")?; // Closed via `.end()`.\n        Ok(ArrayFormatter {\n            f: EntryWrapper::new(self.f),\n        })\n    }\n\n    fn serialize_seq_product(self, len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        // Delegate to named products handling of element formatting.\n        self.serialize_named_product(len).map(|inner| SeqFormatter { inner })\n    }\n\n    fn serialize_named_product(mut self, _len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        write!(self, \"(\")?; // Closed via `.end()`.\n        Ok(NamedFormatter {\n            f: EntryWrapper::new(self.f),\n            idx: 0,\n        })\n    }\n\n    fn serialize_variant<T: ser::Serialize + ?Sized>(\n        mut self,\n        tag: u8,\n        name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        self.ser_variant(tag, name, value)\n    }\n}\n\n/// Defines the SATN formatting for arrays.\nstruct ArrayFormatter<'a, 'f> {\n    /// The formatter for each element separating elements by a `,`.\n    f: EntryWrapper<'a, 'f, ','>,\n}\n\nimpl ser::SerializeArray for ArrayFormatter<'_, '_> {\n    type Ok = ();\n    type Error = SatnError;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.f.entry(|f| elem.serialize(SatnFormatter { f }).map_err(|e| e.0))?;\n        Ok(())\n    }\n\n    fn end(mut self) -> Result<Self::Ok, Self::Error> {\n        write!(self.f.fmt, \"]\")?;\n        Ok(())\n    }\n}\n\n/// Provides the data format for unnamed products for SATN.\nstruct SeqFormatter<'a, 'f> {\n    /// Delegates to the named format.\n    inner: NamedFormatter<'a, 'f>,\n}\n\nimpl ser::SerializeSeqProduct for SeqFormatter<'_, '_> {\n    type Ok = ();\n    type Error = SatnError;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        ser::SerializeNamedProduct::serialize_element(&mut self.inner, None, elem)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        ser::SerializeNamedProduct::end(self.inner)\n    }\n}\n\n/// Provides the data format for named products for SATN.\nstruct NamedFormatter<'a, 'f> {\n    /// The formatter for each element separating elements by a `,`.\n    f: EntryWrapper<'a, 'f, ','>,\n    /// The index of the element.\n    idx: usize,\n}\n\nimpl ser::SerializeNamedProduct for NamedFormatter<'_, '_> {\n    type Ok = ();\n    type Error = SatnError;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(\n        &mut self,\n        name: Option<&str>,\n        elem: &T,\n    ) -> Result<(), Self::Error> {\n        let res = self.f.entry(|mut f| {\n            // Format the name or use the index if unnamed.\n            if let Some(name) = name {\n                write!(f, \"{name}\")?;\n            } else {\n                write!(f, \"{}\", self.idx)?;\n            }\n            write!(f, \" = \")?;\n            elem.serialize(SatnFormatter { f })?;\n            Ok(())\n        });\n        self.idx += 1;\n        res?;\n        Ok(())\n    }\n\n    fn end(mut self) -> Result<Self::Ok, Self::Error> {\n        write!(self.f.fmt, \")\")?;\n        Ok(())\n    }\n}\n\n/// Which client is used to format the `SQL` output?\n#[derive(PartialEq, Copy, Clone, Debug)]\npub enum PsqlClient {\n    SpacetimeDB,\n    Postgres,\n}\n\npub struct PsqlChars {\n    pub start: char,\n    pub sep: &'static str,\n    pub end: char,\n    pub quote: &'static str,\n}\n\nimpl PsqlClient {\n    pub fn format_chars(&self) -> PsqlChars {\n        match self {\n            PsqlClient::SpacetimeDB => PsqlChars {\n                start: '(',\n                sep: \" =\",\n                end: ')',\n                quote: \"\",\n            },\n            PsqlClient::Postgres => PsqlChars {\n                start: '{',\n                sep: \":\",\n                end: '}',\n                quote: \"\\\"\",\n            },\n        }\n    }\n}\n\n/// How format of the `SQL` output?\n#[derive(Debug, Copy, Clone, PartialEq, Display)]\npub enum PsqlPrintFmt {\n    /// Print as `hex` format\n    Hex,\n    /// Print as [`Timestamp`] format\n    Timestamp,\n    /// Print as [`TimeDuration`] format\n    Duration,\n    /// Print as `UUID` format\n    Uuid,\n    /// Print as `Satn` format\n    Satn,\n}\n\nimpl PsqlPrintFmt {\n    pub fn is_special(&self) -> bool {\n        self != &PsqlPrintFmt::Satn\n    }\n    /// Returns if the type is a special type\n    ///\n    /// Is required to check both the enclosing type and the inner element type\n    pub fn use_fmt(tuple: &ProductType, field: &ProductTypeElement, name: Option<&str>) -> PsqlPrintFmt {\n        if tuple.is_identity()\n            || tuple.is_connection_id()\n            || field.algebraic_type.is_identity()\n            || field.algebraic_type.is_connection_id()\n            || name.map(ProductType::is_identity_tag).unwrap_or_default()\n            || name.map(ProductType::is_connection_id_tag).unwrap_or_default()\n        {\n            return PsqlPrintFmt::Hex;\n        };\n\n        if tuple.is_timestamp()\n            || field.algebraic_type.is_timestamp()\n            || name.map(ProductType::is_timestamp_tag).unwrap_or_default()\n        {\n            return PsqlPrintFmt::Timestamp;\n        };\n\n        if tuple.is_time_duration()\n            || field.algebraic_type.is_time_duration()\n            || name.map(ProductType::is_time_duration_tag).unwrap_or_default()\n        {\n            return PsqlPrintFmt::Duration;\n        };\n\n        if tuple.is_uuid() || field.algebraic_type.is_uuid() || name.map(ProductType::is_uuid_tag).unwrap_or_default() {\n            return PsqlPrintFmt::Uuid;\n        };\n\n        PsqlPrintFmt::Satn\n    }\n}\n\n/// A wrapper that remember the `header` of the tuple/struct and the current field\n#[derive(Debug, Clone)]\npub struct PsqlType<'a> {\n    /// The client used to format the output\n    pub client: PsqlClient,\n    /// The header of the tuple/struct\n    pub tuple: &'a ProductType,\n    /// The current field\n    pub field: &'a ProductTypeElement,\n    /// The index of the field in the tuple/struct\n    pub idx: usize,\n}\n\nimpl PsqlType<'_> {\n    /// Returns if the type is a special type\n    ///\n    /// Is required to check both the enclosing type and the inner element type\n    pub fn use_fmt(&self) -> PsqlPrintFmt {\n        PsqlPrintFmt::use_fmt(self.tuple, self.field, None)\n    }\n}\n\n/// An implementation of [`Serializer`](ser::Serializer) for `SQL` output.\npub struct SqlFormatter<'a, 'f> {\n    fmt: SatnFormatter<'a, 'f>,\n    ty: &'a PsqlType<'a>,\n}\n\n/// A trait for writing values, after the special types has been determined.\n///\n/// This is used to write values that could have different representations depending on the output format,\n/// as defined by [`PsqlClient`] and [`PsqlPrintFmt`].\npub trait TypedWriter {\n    type Error: ser::Error;\n\n    /// Writes a value using [`ser::Serializer`]\n    fn write<W: fmt::Display>(&mut self, value: W) -> Result<(), Self::Error>;\n\n    // Values that need special handling:\n\n    fn write_bool(&mut self, value: bool) -> Result<(), Self::Error>;\n    fn write_string(&mut self, value: &str) -> Result<(), Self::Error>;\n    fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error>;\n    fn write_hex(&mut self, value: &[u8]) -> Result<(), Self::Error>;\n    fn write_timestamp(&mut self, value: Timestamp) -> Result<(), Self::Error>;\n    fn write_duration(&mut self, value: TimeDuration) -> Result<(), Self::Error>;\n    fn write_uuid(&mut self, value: Uuid) -> Result<(), Self::Error>;\n    /// Writes a value as an alternative record format, e.g., for use `JSON` inside `SQL`.\n    fn write_alt_record(\n        &mut self,\n        _ty: &PsqlType,\n        _value: &ValueWithType<'_, ProductValue>,\n    ) -> Result<bool, Self::Error> {\n        Ok(false)\n    }\n\n    fn write_record(\n        &mut self,\n        fields: Vec<(Cow<str>, PsqlType, ValueWithType<AlgebraicValue>)>,\n    ) -> Result<(), Self::Error>;\n\n    fn write_variant(\n        &mut self,\n        tag: u8,\n        ty: PsqlType,\n        name: Option<&str>,\n        value: ValueWithType<AlgebraicValue>,\n    ) -> Result<(), Self::Error>;\n}\n\n/// A formatter for arrays that uses the `TypedWriter` trait to write elements.\npub struct TypedArrayFormatter<'a, 'f, F> {\n    ty: &'a PsqlType<'a>,\n    f: &'f mut F,\n}\n\nimpl<F: TypedWriter> ser::SerializeArray for TypedArrayFormatter<'_, '_, F> {\n    type Ok = ();\n    type Error = F::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        elem.serialize(TypedSerializer { ty: self.ty, f: self.f })?;\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(())\n    }\n}\n\n/// A formatter for sequences that uses the `TypedWriter` trait to write elements.\npub struct TypedSeqFormatter<'a, 'f, F> {\n    ty: &'a PsqlType<'a>,\n    f: &'f mut F,\n}\n\nimpl<F: TypedWriter> ser::SerializeSeqProduct for TypedSeqFormatter<'_, '_, F> {\n    type Ok = ();\n    type Error = F::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        elem.serialize(TypedSerializer { ty: self.ty, f: self.f })?;\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(())\n    }\n}\n\n/// A formatter for named products that uses the `TypedWriter` trait to write elements.\npub struct TypedNamedProductFormatter<F> {\n    f: PhantomData<F>,\n}\n\nimpl<F: TypedWriter> ser::SerializeNamedProduct for TypedNamedProductFormatter<F> {\n    type Ok = ();\n    type Error = F::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(\n        &mut self,\n        _name: Option<&str>,\n        _elem: &T,\n    ) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(())\n    }\n}\n\n/// A serializer that uses the `TypedWriter` trait to serialize values\npub struct TypedSerializer<'a, 'f, F> {\n    pub ty: &'a PsqlType<'a>,\n    pub f: &'f mut F,\n}\n\nimpl<'a, 'f, F: TypedWriter> ser::Serializer for TypedSerializer<'a, 'f, F> {\n    type Ok = ();\n    type Error = F::Error;\n    type SerializeArray = TypedArrayFormatter<'a, 'f, F>;\n    type SerializeSeqProduct = TypedSeqFormatter<'a, 'f, F>;\n    type SerializeNamedProduct = TypedNamedProductFormatter<F>;\n\n    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {\n        self.f.write_bool(v)\n    }\n\n    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {\n        match self.ty.use_fmt() {\n            PsqlPrintFmt::Hex => self.f.write_hex(&v.to_be_bytes()),\n            PsqlPrintFmt::Uuid => self.f.write_uuid(Uuid::from_u128(v)),\n            _ => self.f.write(v),\n        }\n    }\n\n    fn serialize_u256(self, v: u256) -> Result<Self::Ok, Self::Error> {\n        match self.ty.use_fmt() {\n            PsqlPrintFmt::Hex => self.f.write_hex(&v.to_be_bytes()),\n            _ => self.f.write(v),\n        }\n    }\n\n    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {\n        match self.ty.use_fmt() {\n            PsqlPrintFmt::Duration => self.f.write_duration(TimeDuration::from_micros(v)),\n            PsqlPrintFmt::Timestamp => self.f.write_timestamp(Timestamp::from_micros_since_unix_epoch(v)),\n            _ => self.f.write(v),\n        }\n    }\n\n    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_i256(self, v: i256) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {\n        self.f.write(v)\n    }\n\n    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {\n        self.f.write_string(v)\n    }\n\n    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {\n        if self.ty.use_fmt() == PsqlPrintFmt::Satn {\n            self.f.write_hex(v)\n        } else {\n            self.f.write_bytes(v)\n        }\n    }\n\n    fn serialize_array(self, _len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        Ok(TypedArrayFormatter { ty: self.ty, f: self.f })\n    }\n\n    fn serialize_seq_product(self, _len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        Ok(TypedSeqFormatter { ty: self.ty, f: self.f })\n    }\n\n    fn serialize_named_product(self, _len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        unreachable!(\"This should never be called, use `serialize_named_product_raw` instead.\");\n    }\n\n    fn serialize_named_product_raw(self, value: &ValueWithType<'_, ProductValue>) -> Result<Self::Ok, Self::Error> {\n        let val = &value.val.elements;\n        assert_eq!(val.len(), value.ty().elements.len());\n        // If the value is a special type, we can write it directly\n        if self.ty.use_fmt().is_special() {\n            // Is a nested product type?\n            // We need to check for both the  enclosing(`self.ty`) type and the inner element type.\n            let (tuple, field) = if let Some(product) = self.ty.field.algebraic_type.as_product() {\n                (product, &product.elements[0])\n            } else {\n                (self.ty.tuple, self.ty.field)\n            };\n            return value.val.serialize(TypedSerializer {\n                ty: &PsqlType {\n                    client: self.ty.client,\n                    tuple,\n                    field,\n                    idx: self.ty.idx,\n                },\n                f: self.f,\n            });\n        }\n        // Allow to switch to an alternative record format, for example to write a `JSON` record.\n        if self.f.write_alt_record(self.ty, value)? {\n            return Ok(());\n        }\n        let mut record = Vec::with_capacity(val.len());\n\n        for (idx, (val, field)) in val.iter().zip(&*value.ty().elements).enumerate() {\n            let ty = PsqlType {\n                client: self.ty.client,\n                tuple: value.ty(),\n                field,\n                idx,\n            };\n            record.push((\n                field\n                    .name()\n                    .map(|n| &**n)\n                    .map(Cow::from)\n                    .unwrap_or_else(|| Cow::from(format!(\"col_{idx}\"))),\n                ty,\n                value.with(&field.algebraic_type, val),\n            ));\n        }\n        self.f.write_record(record)\n    }\n\n    fn serialize_variant_raw(self, sum: &ValueWithType<'_, SumValue>) -> Result<Self::Ok, Self::Error> {\n        let sv = sum.value();\n        let (tag, val) = (sv.tag, &*sv.value);\n        let var_ty = &sum.ty().variants[tag as usize]; // Extract the variant type by tag.\n        let product = ProductType::from([AlgebraicType::sum(sum.ty().clone())]);\n        let ty = PsqlType {\n            client: self.ty.client,\n            tuple: &product,\n            field: &product.elements[0],\n            idx: 0,\n        };\n        self.f.write_variant(\n            tag,\n            ty,\n            var_ty.name().map(|n| &**n),\n            sum.with(&var_ty.algebraic_type, val),\n        )\n    }\n\n    fn serialize_variant<T: Serialize + ?Sized>(\n        self,\n        _tag: u8,\n        _name: Option<&str>,\n        _value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        unreachable!(\"Use `serialize_variant_raw` instead.\");\n    }\n}\n\nimpl TypedWriter for SqlFormatter<'_, '_> {\n    type Error = SatnError;\n\n    fn write<W: fmt::Display>(&mut self, value: W) -> Result<(), Self::Error> {\n        write!(self.fmt, \"{value}\")\n    }\n\n    fn write_bool(&mut self, value: bool) -> Result<(), Self::Error> {\n        write!(self.fmt, \"{value}\")\n    }\n\n    fn write_string(&mut self, value: &str) -> Result<(), Self::Error> {\n        write!(self.fmt, \"\\\"{value}\\\"\")\n    }\n\n    fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error> {\n        self.write_hex(value)\n    }\n\n    fn write_hex(&mut self, value: &[u8]) -> Result<(), Self::Error> {\n        match self.ty.client {\n            PsqlClient::SpacetimeDB => write!(self.fmt, \"0x{}\", hex::encode(value)),\n            PsqlClient::Postgres => write!(self.fmt, \"\\\"0x{}\\\"\", hex::encode(value)),\n        }\n    }\n\n    fn write_timestamp(&mut self, value: Timestamp) -> Result<(), Self::Error> {\n        match self.ty.client {\n            PsqlClient::SpacetimeDB => write!(self.fmt, \"{}\", value.to_rfc3339().unwrap()),\n            PsqlClient::Postgres => write!(self.fmt, \"\\\"{}\\\"\", value.to_rfc3339().unwrap()),\n        }\n    }\n\n    fn write_duration(&mut self, value: TimeDuration) -> Result<(), Self::Error> {\n        match self.ty.client {\n            PsqlClient::SpacetimeDB => write!(self.fmt, \"{value}\"),\n            PsqlClient::Postgres => write!(self.fmt, \"\\\"{}\\\"\", value.to_iso8601()),\n        }\n    }\n\n    fn write_uuid(&mut self, value: Uuid) -> Result<(), Self::Error> {\n        write!(self.fmt, \"\\\"{value}\\\"\")\n    }\n\n    fn write_record(\n        &mut self,\n        fields: Vec<(Cow<str>, PsqlType<'_>, ValueWithType<AlgebraicValue>)>,\n    ) -> Result<(), Self::Error> {\n        let PsqlChars { start, sep, end, quote } = self.ty.client.format_chars();\n        write!(self.fmt, \"{start}\")?;\n        for (idx, (name, ty, value)) in fields.into_iter().enumerate() {\n            if idx > 0 {\n                write!(self.fmt, \", \")?;\n            }\n            write!(self.fmt, \"{quote}{name}{quote}{sep} \")?;\n\n            // Serialize the value\n            value.serialize(TypedSerializer { ty: &ty, f: self })?;\n        }\n        write!(self.fmt, \"{end}\")?;\n        Ok(())\n    }\n\n    fn write_variant(\n        &mut self,\n        tag: u8,\n        ty: PsqlType,\n        name: Option<&str>,\n        value: ValueWithType<AlgebraicValue>,\n    ) -> Result<(), Self::Error> {\n        self.write_record(vec![(\n            name.map(Cow::from).unwrap_or_else(|| Cow::from(format!(\"col_{tag}\"))),\n            ty,\n            value,\n        )])\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/ser/impls.rs",
    "content": "use super::{Serialize, SerializeArray, SerializeSeqProduct, Serializer};\nuse crate::{i256, u256};\nuse crate::{AlgebraicType, AlgebraicValue, ArrayValue, ProductValue, SumValue, ValueWithType, F32, F64};\nuse core::ops::Bound;\nuse lean_string::LeanString;\nuse smallvec::SmallVec;\nuse spacetimedb_primitives::{ColList, ColSet};\nuse std::rc::Rc;\nuse std::sync::Arc;\n\n/// Implements [`Serialize`] for a type in a simplified manner.\n///\n/// An example:\n/// ```ignore\n/// struct Foo<'a, T: Copy>(&'a T, u8);\n/// impl_serialize!(\n/// //     Type parameters  Optional where  Impl type\n/// //            v               v             v\n/// //   ----------------  --------------- ----------\n///     ['a, T: Serialize] where [T: Copy] Foo<'a, T>,\n/// //  The `serialize` implementation where `self` is serialized into `ser`\n/// //  and the expression right of `=>` is the body of `serialize`.\n///     (self, ser) => {\n///         let mut prod = ser.serialize_seq_product(2)?;\n///         prod.serialize_element(&self.0)?;\n///         prod.serialize_element(&self.1)?;\n///         prod.end()\n///     }\n/// );\n/// ```\n#[macro_export]\nmacro_rules! impl_serialize {\n    ([$($generics:tt)*] $(where [$($wc:tt)*])? $typ:ty, ($self:ident, $ser:ident) => $body:expr) => {\n        impl<$($generics)*> $crate::ser::Serialize for $typ $(where $($wc)*)? {\n            fn serialize<S: $crate::ser::Serializer>($self: &Self, $ser: S) -> Result<S::Ok, S::Error> {\n                $body\n            }\n        }\n    };\n}\n\nmacro_rules! impl_prim {\n    ($(($prim:ty, $method:ident))*) => {\n        $(impl_serialize!([] $prim, (self, ser) => ser.$method((*self).into()));)*\n    };\n}\n\n// All the tuple types:\n#[macro_export]\nmacro_rules! count {\n    () => (0usize);\n    ( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));\n}\nmacro_rules! impl_serialize_tuple {\n    ($($ty_name:ident),*) => {\n        impl_serialize!([$($ty_name: Serialize),*] ($($ty_name,)*), (self, ser) => {\n            let mut _tup = ser.serialize_seq_product(count!($($ty_name)*))?;\n            #[allow(non_snake_case)]\n            let ($($ty_name,)*) = self;\n            $(_tup.serialize_element($ty_name)?;)*\n            _tup.end()\n        });\n    };\n}\nimpl_serialize_tuple!();\nimpl_serialize_tuple!(T0);\nimpl_serialize_tuple!(T0, T1);\nimpl_serialize_tuple!(T0, T1, T2);\nimpl_serialize_tuple!(T0, T1, T2, T3);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);\nimpl_serialize_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);\n\n// `u8` is implemented below as we wish to provide different `__serialize_array` impl (see below).\nimpl_prim! {\n    (bool, serialize_bool)\n                       (u16, serialize_u16) (u32, serialize_u32) (u64, serialize_u64) (u128, serialize_u128) (u256, serialize_u256)\n    (i8, serialize_i8) (i16, serialize_i16) (i32, serialize_i32) (i64, serialize_i64) (i128, serialize_i128) (i256, serialize_i256)\n    (f32, serialize_f32) (f64, serialize_f64) (str, serialize_str)\n}\n\n// TODO(Centril): this special case doesn't seem well motivated.\n// Consider generalizing this to apply to all primitive types\n// so that we can move this into `impl_prim!`.\n// This will make BSATN-serializing `[u32]` faster for example.\nimpl Serialize for u8 {\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serializer.serialize_u8(*self)\n    }\n\n    fn __serialize_array<S: Serializer>(this: &[Self], serializer: S) -> Result<S::Ok, S::Error>\n    where\n        Self: Sized,\n    {\n        serializer.serialize_bytes(this)\n    }\n}\n\nimpl_serialize!([] F32, (self, ser) => f32::from(*self).serialize(ser));\nimpl_serialize!([] F64, (self, ser) => f64::from(*self).serialize(ser));\nimpl_serialize!([T: Serialize] Vec<T>, (self, ser)  => (**self).serialize(ser));\nimpl_serialize!([T: Serialize, const N: usize] SmallVec<[T; N]>, (self, ser)  => (**self).serialize(ser));\nimpl_serialize!([T: Serialize] [T], (self, ser) => T::__serialize_array(self, ser));\nimpl_serialize!([T: Serialize, const N: usize] [T; N], (self, ser) => T::__serialize_array(self, ser));\nimpl_serialize!([T: Serialize + ?Sized] Box<T>, (self, ser) => (**self).serialize(ser));\nimpl_serialize!([T: Serialize + ?Sized] Rc<T>, (self, ser) => (**self).serialize(ser));\nimpl_serialize!([T: Serialize + ?Sized] Arc<T>, (self, ser) => (**self).serialize(ser));\nimpl_serialize!([T: Serialize + ?Sized] &T, (self, ser) => (**self).serialize(ser));\nimpl_serialize!([] String, (self, ser) => ser.serialize_str(self));\nimpl_serialize!([] LeanString, (self, ser) => ser.serialize_str(self));\nimpl_serialize!([T: Serialize] Option<T>, (self, ser) => match self {\n    Some(v) => ser.serialize_variant(0, Some(\"some\"), v),\n    None => ser.serialize_variant(1, Some(\"none\"), &()),\n});\nimpl_serialize!([T: Serialize, E: Serialize] Result<T, E>, (self, ser) => match self {\n    Ok(v) => ser.serialize_variant(0, Some(\"ok\"), v),\n    Err(e) => ser.serialize_variant(1, Some(\"err\"), e),\n});\nimpl_serialize!([T: Serialize] Bound<T>, (self, ser) => match self {\n    Bound::Included(x) => ser.serialize_variant(0, Some(\"included\"), x),\n    Bound::Excluded(x) => ser.serialize_variant(1, Some(\"excluded\"), x),\n    Bound::Unbounded => ser.serialize_variant(2, Some(\"unbounded\"), &()),\n});\nimpl_serialize!([] AlgebraicValue, (self, ser) => match self {\n    Self::Sum(sum) => sum.serialize(ser),\n    Self::Product(prod) => prod.serialize(ser),\n    Self::Array(arr) => arr.serialize(ser),\n    Self::Bool(v) => ser.serialize_bool(*v),\n    Self::I8(v) => ser.serialize_i8(*v),\n    Self::U8(v) => ser.serialize_u8(*v),\n    Self::I16(v) => ser.serialize_i16(*v),\n    Self::U16(v) => ser.serialize_u16(*v),\n    Self::I32(v) => ser.serialize_i32(*v),\n    Self::U32(v) => ser.serialize_u32(*v),\n    Self::I64(v) => ser.serialize_i64(*v),\n    Self::U64(v) => ser.serialize_u64(*v),\n    Self::I128(v) => ser.serialize_i128(v.0),\n    Self::U128(v) => ser.serialize_u128(v.0),\n    Self::I256(v) => ser.serialize_i256(**v),\n    Self::U256(v) => ser.serialize_u256(**v),\n    Self::F32(v) => ser.serialize_f32((*v).into()),\n    Self::F64(v) => ser.serialize_f64((*v).into()),\n    // Self::Bytes(v) => ser.serialize_bytes(v),\n    Self::String(v) => ser.serialize_str(v),\n    Self::Min | Self::Max => panic!(\"not defined for Min/Max\"),\n});\nimpl_serialize!([] ProductValue, (self, ser) => {\n    let mut tup = ser.serialize_seq_product(self.elements.len())?;\n    for elem in &*self.elements {\n        tup.serialize_element(elem)?;\n    }\n    tup.end()\n});\nimpl_serialize!([] SumValue, (self, ser) => ser.serialize_variant(self.tag, None, &*self.value));\nimpl_serialize!([] ArrayValue, (self, ser) => match self {\n    Self::Sum(v) => v.serialize(ser),\n    Self::Product(v) => v.serialize(ser),\n    Self::Bool(v) => v.serialize(ser),\n    Self::I8(v) => v.serialize(ser),\n    Self::U8(v) => v.serialize(ser),\n    Self::I16(v) => v.serialize(ser),\n    Self::U16(v) => v.serialize(ser),\n    Self::I32(v) => v.serialize(ser),\n    Self::U32(v) => v.serialize(ser),\n    Self::I64(v) => v.serialize(ser),\n    Self::U64(v) => v.serialize(ser),\n    Self::I128(v) => v.serialize(ser),\n    Self::U128(v) => v.serialize(ser),\n    Self::I256(v) => v.serialize(ser),\n    Self::U256(v) => v.serialize(ser),\n    Self::F32(v) => v.serialize(ser),\n    Self::F64(v) => v.serialize(ser),\n    Self::String(v) => v.serialize(ser),\n    Self::Array(v) => v.serialize(ser),\n});\nimpl_serialize!([] ValueWithType<'_, AlgebraicValue>, (self, ser) => {\n    let mut ty = self.ty();\n    loop { // We're doing this because of `Ref`s.\n        break match (self.value(), ty) {\n            (_, &AlgebraicType::Ref(r)) => {\n                ty = &self.typespace()[r];\n                continue;\n            }\n            (AlgebraicValue::Sum(val), AlgebraicType::Sum(ty)) => self.with(ty, val).serialize(ser),\n            (AlgebraicValue::Product(val), AlgebraicType::Product(ty)) => self.with(ty, val).serialize(ser),\n            (AlgebraicValue::Array(val), AlgebraicType::Array(ty)) => self.with(ty, val).serialize(ser),\n            (AlgebraicValue::Bool(v), AlgebraicType::Bool) => ser.serialize_bool(*v),\n            (AlgebraicValue::I8(v), AlgebraicType::I8) => ser.serialize_i8(*v),\n            (AlgebraicValue::U8(v), AlgebraicType::U8) => ser.serialize_u8(*v),\n            (AlgebraicValue::I16(v), AlgebraicType::I16) => ser.serialize_i16(*v),\n            (AlgebraicValue::U16(v), AlgebraicType::U16) => ser.serialize_u16(*v),\n            (AlgebraicValue::I32(v), AlgebraicType::I32) => ser.serialize_i32(*v),\n            (AlgebraicValue::U32(v), AlgebraicType::U32) => ser.serialize_u32(*v),\n            (AlgebraicValue::I64(v), AlgebraicType::I64) => ser.serialize_i64(*v),\n            (AlgebraicValue::U64(v), AlgebraicType::U64) => ser.serialize_u64(*v),\n            (AlgebraicValue::I128(v), AlgebraicType::I128) => ser.serialize_i128(v.0),\n            (AlgebraicValue::U128(v), AlgebraicType::U128) => ser.serialize_u128(v.0),\n            (AlgebraicValue::I256(v), AlgebraicType::I256) => ser.serialize_i256(**v),\n            (AlgebraicValue::U256(v), AlgebraicType::U256) => ser.serialize_u256(**v),\n            (AlgebraicValue::F32(v), AlgebraicType::F32) => ser.serialize_f32((*v).into()),\n            (AlgebraicValue::F64(v), AlgebraicType::F64) => ser.serialize_f64((*v).into()),\n            (AlgebraicValue::String(s), AlgebraicType::String) => ser.serialize_str(s),\n            (val, ty) => panic!(\"mismatched value and schema : {val:?} {ty:?}\"),\n        };\n    }\n});\nimpl_serialize!(\n    [T: crate::Value] where [for<'a> ValueWithType<'a, T>: Serialize]\n    ValueWithType<'_, Box<[T]>>,\n    (self, ser) => {\n        let mut vec = ser.serialize_array(self.value().len())?;\n        for val in self.iter() {\n            vec.serialize_element(&val)?;\n        }\n        vec.end()\n    }\n);\nimpl_serialize!([] ValueWithType<'_, SumValue>, (self, ser) => {\n   ser.serialize_variant_raw(self)\n});\nimpl_serialize!([] ValueWithType<'_, ProductValue>, (self, ser) => {\n    ser.serialize_named_product_raw(self)\n});\nimpl_serialize!([] ValueWithType<'_, ArrayValue>, (self, ser) => {\n    let mut ty = &*self.ty().elem_ty;\n    loop { // We're doing this because of `Ref`s.\n        break match (self.value(), ty) {\n            (_, &AlgebraicType::Ref(r)) => {\n                ty = &self.typespace()[r];\n                continue;\n            }\n            (ArrayValue::Sum(v), AlgebraicType::Sum(ty)) => self.with(ty, v).serialize(ser),\n            (ArrayValue::Product(v), AlgebraicType::Product(ty)) => self.with(ty, v).serialize(ser),\n            (ArrayValue::Bool(v), AlgebraicType::Bool) => v.serialize(ser),\n            (ArrayValue::I8(v), AlgebraicType::I8) => v.serialize(ser),\n            (ArrayValue::U8(v), AlgebraicType::U8) => v.serialize(ser),\n            (ArrayValue::I16(v), AlgebraicType::I16) => v.serialize(ser),\n            (ArrayValue::U16(v), AlgebraicType::U16) => v.serialize(ser),\n            (ArrayValue::I32(v), AlgebraicType::I32) => v.serialize(ser),\n            (ArrayValue::U32(v), AlgebraicType::U32) => v.serialize(ser),\n            (ArrayValue::I64(v), AlgebraicType::I64) => v.serialize(ser),\n            (ArrayValue::U64(v), AlgebraicType::U64) => v.serialize(ser),\n            (ArrayValue::I128(v), AlgebraicType::I128) => v.serialize(ser),\n            (ArrayValue::U128(v), AlgebraicType::U128) => v.serialize(ser),\n            (ArrayValue::I256(v), AlgebraicType::I256) => v.serialize(ser),\n            (ArrayValue::U256(v), AlgebraicType::U256) => v.serialize(ser),\n            (ArrayValue::F32(v), AlgebraicType::F32) => v.serialize(ser),\n            (ArrayValue::F64(v), AlgebraicType::F64) => v.serialize(ser),\n            (ArrayValue::String(v), AlgebraicType::String) => v.serialize(ser),\n            (ArrayValue::Array(v), AlgebraicType::Array(ty)) => self.with(ty, v).serialize(ser),\n            (val, _) if val.is_empty() => ser.serialize_array(0)?.end(),\n            (val, ty) => panic!(\"mismatched value and schema: {val:?} {ty:?}\"),\n        }\n    }\n});\n\nimpl_serialize!([] spacetimedb_primitives::ArgId, (self, ser) => ser.serialize_u64(self.0));\nimpl_serialize!([] spacetimedb_primitives::TableId, (self, ser) => ser.serialize_u32(self.0));\nimpl_serialize!([] spacetimedb_primitives::ViewId, (self, ser) => ser.serialize_u32(self.0));\nimpl_serialize!([] spacetimedb_primitives::SequenceId, (self, ser) => ser.serialize_u32(self.0));\nimpl_serialize!([] spacetimedb_primitives::IndexId, (self, ser) => ser.serialize_u32(self.0));\nimpl_serialize!([] spacetimedb_primitives::ConstraintId, (self, ser) => ser.serialize_u32(self.0));\nimpl_serialize!([] spacetimedb_primitives::ColId, (self, ser) => ser.serialize_u16(self.0));\nimpl_serialize!([] spacetimedb_primitives::ScheduleId, (self, ser) => ser.serialize_u32(self.0));\n\nimpl_serialize!([] ColList, (self, ser) => {\n    let mut arr = ser.serialize_array(self.len() as usize)?;\n       for x in self.iter() {\n           arr.serialize_element(&x)?;\n       }\n       arr.end()\n});\nimpl_serialize!([] ColSet, (self, ser) => {\n    let list: &ColList = self;\n    list.serialize(ser)\n});\n\n#[cfg(feature = \"blake3\")]\nimpl_serialize!([] blake3::Hash, (self, ser) => self.as_bytes().serialize(ser));\n\nimpl_serialize!([] bytes::Bytes, (self, ser) => ser.serialize_bytes(self));\n\n#[cfg(feature = \"bytestring\")]\nimpl_serialize!([] bytestring::ByteString, (self, ser) => ser.serialize_str(self));\n"
  },
  {
    "path": "crates/sats/src/ser/serde.rs",
    "content": "use crate::{i256, u256};\nuse crate::{\n    ser::{self, Serializer},\n    serde::{SerdeError, SerdeWrapper},\n};\nuse core::fmt;\nuse serde::ser as serde;\n\n/// Converts any [`serde::Serializer`] to a SATS [`Serializer`]\n/// so that Serde's data formats can be reused.\npub struct SerdeSerializer<S> {\n    /// A serialization data format in Serde.\n    ser: S,\n}\n\nimpl<S: serde::Serializer> SerdeSerializer<S> {\n    /// Returns a wrapped serializer.\n    pub fn new(ser: S) -> Self {\n        Self { ser }\n    }\n}\n\nimpl<E: serde::Error> ser::Error for SerdeError<E> {\n    fn custom<T: fmt::Display>(msg: T) -> Self {\n        Self(E::custom(msg))\n    }\n}\n\nimpl<S: serde::Serializer> Serializer for SerdeSerializer<S> {\n    type Ok = S::Ok;\n    type Error = SerdeError<S::Error>;\n    type SerializeArray = SerializeArray<S::SerializeSeq>;\n    type SerializeSeqProduct = SerializeSeqProduct<S::SerializeTuple>;\n    type SerializeNamedProduct = SerializeNamedProduct<S::SerializeMap>;\n\n    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_bool(v).map_err(SerdeError)\n    }\n    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_u8(v).map_err(SerdeError)\n    }\n    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_u16(v).map_err(SerdeError)\n    }\n    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_u32(v).map_err(SerdeError)\n    }\n    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_u64(v).map_err(SerdeError)\n    }\n    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_u128(v).map_err(SerdeError)\n    }\n    fn serialize_u256(self, v: u256) -> Result<Self::Ok, Self::Error> {\n        serde::Serialize::serialize(&v, self.ser).map_err(SerdeError)\n    }\n    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_i8(v).map_err(SerdeError)\n    }\n    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_i16(v).map_err(SerdeError)\n    }\n    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_i32(v).map_err(SerdeError)\n    }\n    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_i64(v).map_err(SerdeError)\n    }\n    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_i128(v).map_err(SerdeError)\n    }\n    fn serialize_i256(self, v: i256) -> Result<Self::Ok, Self::Error> {\n        serde::Serialize::serialize(&v, self.ser).map_err(SerdeError)\n    }\n    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_f32(v).map_err(SerdeError)\n    }\n    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_f64(v).map_err(SerdeError)\n    }\n    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {\n        self.ser.serialize_str(v).map_err(SerdeError)\n    }\n    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {\n        let s = hex::encode(v);\n        self.ser.serialize_str(&s).map_err(SerdeError)\n    }\n\n    fn serialize_array(self, len: usize) -> Result<Self::SerializeArray, Self::Error> {\n        let seq = self.ser.serialize_seq(Some(len)).map_err(SerdeError)?;\n        Ok(SerializeArray { seq })\n    }\n\n    fn serialize_seq_product(self, len: usize) -> Result<Self::SerializeSeqProduct, Self::Error> {\n        let tup = self.ser.serialize_tuple(len).map_err(SerdeError)?;\n        Ok(SerializeSeqProduct { tup })\n    }\n\n    fn serialize_named_product(self, len: usize) -> Result<Self::SerializeNamedProduct, Self::Error> {\n        let map = self.ser.serialize_map(Some(len)).map_err(SerdeError)?;\n        Ok(SerializeNamedProduct { map })\n    }\n\n    fn serialize_variant<T: ser::Serialize + ?Sized>(\n        self,\n        tag: u8,\n        name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error> {\n        // can't use serialize_variant cause we're too dynamic :(\n        use serde::{SerializeMap, SerializeTuple};\n        let value = SerializeWrapper::from_ref(value);\n        if let Some(name) = name {\n            let mut map = self.ser.serialize_map(Some(1)).map_err(SerdeError)?;\n            map.serialize_entry(name, value).map_err(SerdeError)?;\n            map.end().map_err(SerdeError)\n        } else {\n            let mut seq = self.ser.serialize_tuple(2).map_err(SerdeError)?;\n            seq.serialize_element(&tag).map_err(SerdeError)?;\n            seq.serialize_element(value).map_err(SerdeError)?;\n            seq.end().map_err(SerdeError)\n        }\n    }\n}\n\n/// Serializes array elements by forwarding to `S: serde::SerializeSeq`.\npub struct SerializeArray<S> {\n    /// An implementation of `serde::SerializeSeq`.\n    seq: S,\n}\n\nimpl<S: serde::SerializeSeq> ser::SerializeArray for SerializeArray<S> {\n    type Ok = S::Ok;\n    type Error = SerdeError<S::Error>;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.seq\n            .serialize_element(SerializeWrapper::from_ref(elem))\n            .map_err(SerdeError)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        self.seq.end().map_err(SerdeError)\n    }\n}\n\n/// Serializes unnamed product elements by forwarding to `S: serde::SerializeTuple`.\npub struct SerializeSeqProduct<S> {\n    /// An implementation of `serde::SerializeTuple`.\n    tup: S,\n}\n\nimpl<S: serde::SerializeTuple> ser::SerializeSeqProduct for SerializeSeqProduct<S> {\n    type Ok = S::Ok;\n    type Error = SerdeError<S::Error>;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.tup\n            .serialize_element(SerializeWrapper::from_ref(elem))\n            .map_err(SerdeError)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        self.tup.end().map_err(SerdeError)\n    }\n}\n\n/// Serializes named product elements by forwarding to `S: serde::SerializeMap`.\npub struct SerializeNamedProduct<S> {\n    /// An implementation of `serde::SerializeMap`.\n    map: S,\n}\n\nimpl<S: serde::SerializeMap> ser::SerializeNamedProduct for SerializeNamedProduct<S> {\n    type Ok = S::Ok;\n    type Error = SerdeError<S::Error>;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(\n        &mut self,\n        name: Option<&str>,\n        elem: &T,\n    ) -> Result<(), Self::Error> {\n        let name = name.ok_or_else(|| ser::Error::custom(\"tuple element has no name\"))?;\n        self.map\n            .serialize_entry(name, SerializeWrapper::from_ref(elem))\n            .map_err(SerdeError)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        self.map.end().map_err(SerdeError)\n    }\n}\n\n/// Serializes `T` as a SATS object into `serializer: S`\n/// where `S` is a serde data format.\npub fn serialize_to<T: super::Serialize + ?Sized, S: serde::Serializer>(\n    value: &T,\n    serializer: S,\n) -> Result<S::Ok, S::Error> {\n    value\n        .serialize(SerdeSerializer::new(serializer))\n        .map_err(|SerdeError(e)| e)\n}\n\npub use crate::serde::SerdeWrapper as SerializeWrapper;\n\nimpl<T: ser::Serialize + ?Sized> serde::Serialize for SerdeWrapper<T> {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serialize_to(&self.0, serializer)\n    }\n}\n\nmacro_rules! delegate_serde {\n    ($($t:ty),*) => {\n        $(impl serde::Serialize for $t {\n            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n            where\n                S: serde::Serializer,\n            {\n                serialize_to(self, serializer)\n            }\n        })*\n    };\n}\n\ndelegate_serde! {\n    crate::AlgebraicType, crate::ProductType, crate::ProductTypeElement, crate::SumType, crate::SumTypeVariant,\n    crate::AlgebraicValue, crate::ProductValue, crate::SumValue\n}\n"
  },
  {
    "path": "crates/sats/src/ser.rs",
    "content": "// Some parts copyright Serde developers under the MIT / Apache-2.0 licenses at your option.\n// See `serde` version `v1.0.169` for the parts where MIT / Apache-2.0 applies.\n\nmod impls;\n#[cfg(any(test, feature = \"serde\"))]\npub mod serde;\n\nuse crate::de::DeserializeSeed;\nuse crate::{algebraic_value::ser::ValueSerializer, bsatn, buffer::BufWriter, ProductValue, SumValue, ValueWithType};\nuse crate::{AlgebraicValue, WithTypespace};\nuse core::marker::PhantomData;\nuse core::{convert::Infallible, fmt};\nuse ethnum::{i256, u256};\npub use spacetimedb_bindings_macro::Serialize;\n\n/// A data format that can deserialize any data structure supported by SATs.\n///\n/// The `Serializer` trait in SATS performs the same function as `serde::Serializer` in [`serde`].\n/// See the documentation of `serde::Serializer` for more information on the data model.\n///\n/// [`serde`]: https://crates.io/crates/serde\npub trait Serializer: Sized {\n    /// The output type produced by this `Serializer` during successful serialization.\n    ///\n    /// Most serializers that produce text or binary output should set `Ok = ()`\n    /// and serialize into an [`io::Write`] or buffer contained within the `Serializer` instance.\n    /// Serializers that build in-memory data structures may be simplified by using `Ok` to propagate\n    /// the data structure around.\n    ///\n    /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html\n    type Ok;\n\n    /// The error type when some error occurs during serialization.\n    type Error: Error;\n\n    /// Type returned from [`serialize_array`](Serializer::serialize_array)\n    /// for serializing the contents of the array.\n    type SerializeArray: SerializeArray<Ok = Self::Ok, Error = Self::Error>;\n\n    /// Type returned from [`serialize_seq_product`](Serializer::serialize_seq_product)\n    /// for serializing the contents of the *unnamed* product.\n    type SerializeSeqProduct: SerializeSeqProduct<Ok = Self::Ok, Error = Self::Error>;\n\n    /// Type returned from [`serialize_named_product`](Serializer::serialize_named_product)\n    /// for serializing the contents of the *named* product.\n    type SerializeNamedProduct: SerializeNamedProduct<Ok = Self::Ok, Error = Self::Error>;\n\n    /// Serialize a `bool` value.\n    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u8` value.\n    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u16` value.\n    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u32` value.\n    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u64` value.\n    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u128` value.\n    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `u256` value.\n    fn serialize_u256(self, v: u256) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i8` value.\n    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i16` value.\n    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i32` value.\n    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i64` value.\n    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i128` value.\n    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `i256` value.\n    fn serialize_i256(self, v: i256) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `f32` value.\n    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize an `f64` value.\n    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `&str` string slice.\n    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize a `&[u8]` byte slice.\n    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error>;\n\n    /// Begin to serialize a variably sized array.\n    /// This call must be followed by zero or more calls to [`SerializeArray::serialize_element`],\n    /// then a call to [`SerializeArray::end`].\n    ///\n    /// The argument is the number of elements in the sequence.\n    fn serialize_array(self, len: usize) -> Result<Self::SerializeArray, Self::Error>;\n\n    /// Begin to serialize a product with unnamed fields.\n    /// This call must be followed by zero or more calls to [`SerializeSeqProduct::serialize_element`],\n    /// then a call to [`SerializeSeqProduct::end`].\n    ///\n    /// The argument is the number of fields in the product.\n    fn serialize_seq_product(self, len: usize) -> Result<Self::SerializeSeqProduct, Self::Error>;\n\n    /// Begin to serialize a product with named fields.\n    /// This call must be followed by zero or more calls to [`SerializeNamedProduct::serialize_element`],\n    /// then a call to [`SerializeNamedProduct::end`].\n    ///\n    /// The argument is the number of fields in the product.\n    fn serialize_named_product(self, len: usize) -> Result<Self::SerializeNamedProduct, Self::Error>;\n\n    /// Serialize a product with named fields.\n    ///\n    /// Allow to override the default serialization for where we need to switch the output format,\n    /// see [`crate::satn::TypedWriter`].\n    fn serialize_named_product_raw(self, value: &ValueWithType<'_, ProductValue>) -> Result<Self::Ok, Self::Error> {\n        let val = &value.val.elements;\n        assert_eq!(val.len(), value.ty().elements.len());\n        let mut prod = self.serialize_named_product(val.len())?;\n        for (val, el_ty) in val.iter().zip(&*value.ty().elements) {\n            prod.serialize_element(el_ty.name().map(|n| &**n), &value.with(&el_ty.algebraic_type, val))?\n        }\n        prod.end()\n    }\n\n    /// Serialize a sum value\n    ///\n    /// Allow to override the default serialization for where we need to switch the output format,\n    /// see [`crate::satn::TypedWriter`].\n    fn serialize_variant_raw(self, sum: &ValueWithType<'_, SumValue>) -> Result<Self::Ok, Self::Error> {\n        let sv = sum.value();\n        let (tag, val) = (sv.tag, &*sv.value);\n        let var_ty = &sum.ty().variants[tag as usize]; // Extract the variant type by tag.\n        self.serialize_variant(tag, var_ty.name().map(|n| &**n), &sum.with(&var_ty.algebraic_type, val))\n    }\n\n    /// Serialize a sum value provided the chosen `tag`, `name`, and `value`.\n    fn serialize_variant<T: Serialize + ?Sized>(\n        self,\n        tag: u8,\n        name: Option<&str>,\n        value: &T,\n    ) -> Result<Self::Ok, Self::Error>;\n\n    /// Serialize the given `bsatn` encoded data of type `ty`.\n    ///\n    /// This is a concession to performance,\n    /// allowing some implementations to write the buffer directly.\n    ///\n    /// # Safety\n    ///\n    /// - `decode(ty, &mut bsatn).is_ok()`.\n    ///   That is, `bsatn` encodes a valid element of `ty`.\n    ///   It's up to the caller to arrange `Ty` such that this holds.\n    unsafe fn serialize_bsatn<Ty>(self, ty: &Ty, bsatn: &[u8]) -> Result<Self::Ok, Self::Error>\n    where\n        for<'a, 'de> WithTypespace<'a, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        // TODO(Centril): Consider instead deserializing the `bsatn` through a\n        // deserializer that serializes into `self` directly.\n\n        // First convert the BSATN to an `AlgebraicValue`.\n        // SAFETY: Forward caller requirements of this method to that we are calling.\n        let res = unsafe { ValueSerializer.serialize_bsatn(ty, bsatn) };\n        let value = res.unwrap_or_else(|x| match x {});\n\n        // Then serialize that.\n        value.serialize(self)\n    }\n\n    /// Serialize the given `bsatn` encoded data of type `ty`.\n    ///\n    /// The data is provided as an iterator of chunks, at arbitrary boundaries,\n    /// with a total concatenated length of `total_bsatn_len` which callers can assume.\n    ///\n    /// An implementation of this method is semantically the same as:\n    /// ```rust,ignore\n    /// let mut buf = Vec::new();\n    /// for chunk in bsatn {\n    ///     buf.extend(chunk);\n    /// }\n    /// ser.serialize_bsatn(&buf);\n    /// ```\n    ///\n    /// This method is a concession to performance,\n    /// allowing some implementations to write the buffer directly.\n    ///\n    /// The parameter `I` is required to be `Clone` only for `debug_assert!` purposes.\n    ///\n    /// # Safety\n    ///\n    /// - `total_bsatn_len == bsatn.map(|c| c.len()).sum() <= isize::MAX`\n    /// - Let `buf` be defined as above, i.e., the bytes of `bsatn` concatenated.\n    ///   Then `decode(ty, &mut buf).is_ok()`.\n    ///   That is, `buf` encodes a valid element of `ty`.\n    ///   It's up to the caller to arrange `Ty` such that this holds.\n    unsafe fn serialize_bsatn_in_chunks<'a, Ty, I: Clone + Iterator<Item = &'a [u8]>>(\n        self,\n        ty: &Ty,\n        total_bsatn_len: usize,\n        bsatn: I,\n    ) -> Result<Self::Ok, Self::Error>\n    where\n        for<'b, 'de> WithTypespace<'b, Ty>: DeserializeSeed<'de, Output: Into<AlgebraicValue>>,\n    {\n        // TODO(Centril): Unlike above, in this case we must at minimum concatenate `bsatn`\n        // before we can do the piping mentioned above, but that's better than\n        // serializing to `AlgebraicValue` first, so consider that.\n\n        // First convert the BSATN to an `AlgebraicValue`.\n        // SAFETY: Forward caller requirements of this method to that we are calling.\n        let res = unsafe { ValueSerializer.serialize_bsatn_in_chunks(ty, total_bsatn_len, bsatn) };\n        let value = res.unwrap_or_else(|x| match x {});\n\n        // Then serialize that.\n        value.serialize(self)\n    }\n\n    /// Serialize the given `string`.\n    ///\n    /// The string is provided as an iterator of chunks, at arbitrary boundaries,\n    /// with a total concatenated length of `total_len` which callers can trust.\n    ///\n    /// An implementation of this method is semantically the same as:\n    /// ```rust,ignore\n    /// let mut buf = Vec::new();\n    /// for chunk in string {\n    ///     buf.extend(chunk);\n    /// }\n    /// let str = unsafe { core::str::from_utf8_unchecked(&buf) };\n    /// ser.serialize_str(str);\n    /// ```\n    ///\n    /// This method is a concession to performance,\n    /// allowing some implementations to write the buffer directly.\n    ///\n    /// The parameter `I` is required to be `Clone` only for `debug_assert!` purposes.\n    ///\n    /// # Safety\n    ///\n    /// - `total_len == string.map(|c| c.len()).sum() <= isize::MAX`\n    /// - Let `buf` be the bytes of `string` concatenated.\n    ///   Then `core::str::from_utf8(&buf).is_ok()`.\n    ///   That is, `buf` is valid UTF-8.\n    ///   Note however that individual chunks need not be valid UTF-8,\n    ///   as multi-byte characters may be split across chunk boundaries.\n    unsafe fn serialize_str_in_chunks<'a, I: Clone + Iterator<Item = &'a [u8]>>(\n        self,\n        total_len: usize,\n        string: I,\n    ) -> Result<Self::Ok, Self::Error> {\n        // First convert the `string` to an `AlgebraicValue`.\n        // SAFETY: Forward caller requirements of this method to that we are calling.\n        let res = unsafe { ValueSerializer.serialize_str_in_chunks(total_len, string) };\n        let value = res.unwrap_or_else(|x| match x {});\n\n        // Then serialize that.\n        // This incurs a very minor cost of branching on `AlgebraicValue::String`.\n        value.serialize(self)\n    }\n}\n\n/// A **data structure** that can be serialized into any data format supported by\n/// the SpacetimeDB Algebraic Type System.\n///\n/// In most cases, implementations of `Serialize` may be `#[derive(Serialize)]`d.\n///\n/// The `Serialize` trait in SATS performs the same function as `serde::Serialize` in [`serde`].\n/// See the documentation of `serde::Serialize` for more information of the data model.\n///\n/// Do not manually implement this trait unless you know what you are doing.\n/// Implementations must be consistent with `Deserialize<'de> for T`, `SpacetimeType for T` and `Serialize, Deserialize for AlgebraicValue`.\n/// Implementations that are inconsistent across these traits may result in data loss.\n///\n/// [`serde`]: https://crates.io/crates/serde\npub trait Serialize {\n    /// Serialize `self` in the data format of `S` using the provided `serializer`.\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;\n\n    #[doc(hidden)]\n    /// Serialize `self` in the data format BSATN using the provided BSATN `serializer`.\n    fn serialize_into_bsatn<W: BufWriter>(\n        &self,\n        serializer: bsatn::Serializer<'_, W>,\n    ) -> Result<(), bsatn::EncodeError> {\n        self.serialize(serializer)\n    }\n\n    /// Used in the `Serialize for Vec<T>` implementation\n    /// to allow a specialized serialization of `Vec<T>` as bytes.\n    #[doc(hidden)]\n    #[inline(always)]\n    fn __serialize_array<S: Serializer>(this: &[Self], serializer: S) -> Result<S::Ok, S::Error>\n    where\n        Self: Sized,\n    {\n        let mut vec = serializer.serialize_array(this.len())?;\n        for elem in this {\n            vec.serialize_element(elem)?;\n        }\n        vec.end()\n    }\n}\n\n/// The base trait serialization error types must implement.\npub trait Error {\n    /// Returns an error derived from `msg: impl Display`.\n    fn custom<T: fmt::Display>(msg: T) -> Self;\n}\n\nimpl Error for String {\n    fn custom<T: fmt::Display>(msg: T) -> Self {\n        msg.to_string()\n    }\n}\n\nimpl Error for std::convert::Infallible {\n    fn custom<T: fmt::Display>(msg: T) -> Self {\n        panic!(\"error generated for Infallible serializer: {msg}\")\n    }\n}\n\n/// Returned from [`Serializer::serialize_array`].\n///\n/// This provides a continuation of sorts\n/// where you can call [`serialize_element`](SerializeArray::serialize_element) however many times\n/// and then finally the [`end`](SerializeArray::end) is reached.\npub trait SerializeArray {\n    /// Must match the `Ok` type of any `Serializer` that uses this type.\n    type Ok;\n\n    /// Must match the `Error` type of any `Serializer` that uses this type.\n    type Error: Error;\n\n    /// Serialize an array `element`.\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, element: &T) -> Result<(), Self::Error>;\n\n    /// Consumes and finalizes the array serializer returning the `Self::Ok` data.\n    fn end(self) -> Result<Self::Ok, Self::Error>;\n}\n\n/// Returned from [`Serializer::serialize_seq_product`].\n///\n/// This provides a continuation of sorts\n/// where you can call [`serialize_element`](SerializeSeqProduct::serialize_element) however many times\n/// and then finally the [`end`](SerializeSeqProduct::end) is reached.\npub trait SerializeSeqProduct {\n    /// Must match the `Ok` type of any `Serializer` that uses this type.\n    type Ok;\n\n    /// Must match the `Error` type of any `Serializer` that uses this type.\n    type Error: Error;\n\n    /// Serialize an unnamed product `element`.\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, element: &T) -> Result<(), Self::Error>;\n\n    /// Consumes and finalizes the product serializer returning the `Self::Ok` data.\n    fn end(self) -> Result<Self::Ok, Self::Error>;\n}\n\n/// Returned from [`Serializer::serialize_named_product`].\n///\n/// This provides a continuation of sorts\n/// where you can call [`serialize_element`](SerializeNamedProduct::serialize_element) however many times\n/// and then finally the [`end`](SerializeNamedProduct::end) is reached.\npub trait SerializeNamedProduct {\n    /// Must match the `Ok` type of any `Serializer` that uses this type.\n    type Ok;\n\n    /// Must match the `Error` type of any `Serializer` that uses this type.\n    type Error: Error;\n\n    /// Serialize a named product `element` with `name`.\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, name: Option<&str>, elem: &T) -> Result<(), Self::Error>;\n\n    /// Consumes and finalizes the product serializer returning the `Self::Ok` data.\n    fn end(self) -> Result<Self::Ok, Self::Error>;\n}\n\n/// Forwards the implementation of a named product value\n/// to the implementation of the unnamed kind,\n/// thereby ignoring any field names.\npub struct ForwardNamedToSeqProduct<S> {\n    /// The unnamed product serializer.\n    tup: S,\n}\n\nimpl<S> ForwardNamedToSeqProduct<S> {\n    /// Returns a forwarder based on the provided unnamed product serializer.\n    pub fn new(tup: S) -> Self {\n        Self { tup }\n    }\n\n    /// Forwards the serialization of a named product of `len` fields\n    /// to an unnamed serialization format.\n    pub fn forward<Ser>(ser: Ser, len: usize) -> Result<Self, Ser::Error>\n    where\n        Ser: Serializer<SerializeSeqProduct = S>,\n    {\n        ser.serialize_seq_product(len).map(Self::new)\n    }\n}\n\nimpl<S: SerializeSeqProduct> SerializeNamedProduct for ForwardNamedToSeqProduct<S> {\n    type Ok = S::Ok;\n    type Error = S::Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, _name: Option<&str>, elem: &T) -> Result<(), Self::Error> {\n        self.tup.serialize_element(elem)\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        self.tup.end()\n    }\n}\n\n/// A type usable in one of the associated types of [`Serializer`]\n/// when the data format does not support the data.\npub struct Impossible<Ok, Error> {\n    // They gave each other a pledge. Unheard of, absurd.\n    absurd: Infallible,\n    marker: PhantomData<(Ok, Error)>,\n}\n\nimpl<Ok, Error: self::Error> SerializeArray for Impossible<Ok, Error> {\n    type Ok = Ok;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, _: &T) -> Result<(), Self::Error> {\n        match self.absurd {}\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        match self.absurd {}\n    }\n}\n\nimpl<Ok, Error: self::Error> SerializeSeqProduct for Impossible<Ok, Error> {\n    type Ok = Ok;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, _: &T) -> Result<(), Self::Error> {\n        match self.absurd {}\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        match self.absurd {}\n    }\n}\n\nimpl<Ok, Error: self::Error> SerializeNamedProduct for Impossible<Ok, Error> {\n    type Ok = Ok;\n    type Error = Error;\n\n    fn serialize_element<T: Serialize + ?Sized>(&mut self, _: Option<&str>, _: &T) -> Result<(), Self::Error> {\n        match self.absurd {}\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        match self.absurd {}\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/size_of.rs",
    "content": "use ethnum::{i256, u256};\n\nuse crate::{algebraic_value::Packed, AlgebraicValue, ArrayValue, ProductValue, SumValue, F32, F64};\n\npub trait SizeOf {\n    /// Returns the unpadded size in bytes of an [AlgebraicValue] or primitive\n    fn size_of(&self) -> usize;\n}\n\nmacro_rules! impl_size_of_primitive {\n    ($prim:ty) => {\n        impl SizeOf for $prim {\n            fn size_of(&self) -> usize {\n                std::mem::size_of::<Self>()\n            }\n        }\n    };\n    ($($prim:ty,)*) => {\n        $(impl_size_of_primitive!($prim);)*\n    };\n}\n\nimpl_size_of_primitive!(\n    bool,\n    u8,\n    i8,\n    u16,\n    i16,\n    u32,\n    i32,\n    u64,\n    i64,\n    u128,\n    i128,\n    Packed<u128>,\n    Packed<i128>,\n    u256,\n    i256,\n    F32,\n    F64,\n);\n\nimpl SizeOf for Box<str> {\n    fn size_of(&self) -> usize {\n        self.len()\n    }\n}\n\nimpl SizeOf for AlgebraicValue {\n    fn size_of(&self) -> usize {\n        match self {\n            Self::Min | Self::Max => unreachable!(),\n            Self::String(x) => x.size_of(),\n            Self::Bool(x) => x.size_of(),\n            Self::U8(x) => x.size_of(),\n            Self::I8(x) => x.size_of(),\n            Self::U16(x) => x.size_of(),\n            Self::I16(x) => x.size_of(),\n            Self::U32(x) => x.size_of(),\n            Self::I32(x) => x.size_of(),\n            Self::U64(x) => x.size_of(),\n            Self::I64(x) => x.size_of(),\n            Self::U128(x) => x.size_of(),\n            Self::I128(x) => x.size_of(),\n            Self::U256(x) => x.size_of(),\n            Self::I256(x) => x.size_of(),\n            Self::F32(x) => x.size_of(),\n            Self::F64(x) => x.size_of(),\n            Self::Sum(x) => x.size_of(),\n            Self::Product(x) => x.size_of(),\n            Self::Array(x) => x.size_of(),\n        }\n    }\n}\n\nimpl SizeOf for SumValue {\n    fn size_of(&self) -> usize {\n        1 + self.value.size_of()\n    }\n}\n\nimpl SizeOf for ProductValue {\n    fn size_of(&self) -> usize {\n        self.elements.size_of()\n    }\n}\n\nimpl<T> SizeOf for [T]\nwhere\n    T: SizeOf,\n{\n    fn size_of(&self) -> usize {\n        self.iter().map(|elt| elt.size_of()).sum()\n    }\n}\n\nimpl SizeOf for ArrayValue {\n    fn size_of(&self) -> usize {\n        match self {\n            Self::Sum(elts) => elts.size_of(),\n            Self::Product(elts) => elts.size_of(),\n            Self::Bool(elts) => elts.size_of(),\n            Self::I8(elts) => elts.size_of(),\n            Self::U8(elts) => elts.size_of(),\n            Self::I16(elts) => elts.size_of(),\n            Self::U16(elts) => elts.size_of(),\n            Self::I32(elts) => elts.size_of(),\n            Self::U32(elts) => elts.size_of(),\n            Self::I64(elts) => elts.size_of(),\n            Self::U64(elts) => elts.size_of(),\n            Self::I128(elts) => elts.size_of(),\n            Self::U128(elts) => elts.size_of(),\n            Self::I256(elts) => elts.size_of(),\n            Self::U256(elts) => elts.size_of(),\n            Self::F32(elts) => elts.size_of(),\n            Self::F64(elts) => elts.size_of(),\n            Self::String(elts) => elts.size_of(),\n            Self::Array(elts) => elts.size_of(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/sum_type.rs",
    "content": "use crate::algebraic_value::de::{ValueDeserializeError, ValueDeserializer};\nuse crate::algebraic_value::ser::value_serialize;\nuse crate::de::Deserialize;\nuse crate::meta_type::MetaType;\nuse crate::raw_identifier::RawIdentifier;\nuse crate::{AlgebraicType, AlgebraicValue, SpacetimeType, SumTypeVariant, SumValue, Typespace};\n\n/// The tag used for the `Interval` variant of the special `ScheduleAt` sum type.\npub const SCHEDULE_AT_INTERVAL_TAG: &str = \"Interval\";\n/// The tag used for the `Time` variant of the special `ScheduleAt` sum type.\npub const SCHEDULE_AT_TIME_TAG: &str = \"Time\";\n/// The tag used for the `some` variant of the special `option` sum type.\npub const OPTION_SOME_TAG: &str = \"some\";\n/// The tag used for the `none` variant of the special `option` sum type.\npub const OPTION_NONE_TAG: &str = \"none\";\n/// The tag used for the `ok` variant of the special `result` sum type.\npub const RESULT_OK_TAG: &str = \"ok\";\n/// The tag used for the `err` variant of the special `result` sum type.\npub const RESULT_ERR_TAG: &str = \"err\";\n\n/// A structural sum type.\n///\n/// Unlike most languages, sums in SATS are *[structural]* and not nominal.\n/// When checking whether two nominal types are the same,\n/// their names and/or declaration sites (e.g., module / namespace) are considered.\n/// Meanwhile, a structural type system would only check the structure of the type itself,\n/// e.g., the names of its variants and their inner data types in the case of a sum.\n///\n/// This is also known as a discriminated union (implementation) or disjoint union.\n/// Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).\n///\n/// These structures are known as sum types because the number of possible values a sum\n/// ```text\n/// { N_0(T_0), N_1(T_1), ..., N_n(T_n) }\n/// ```\n/// is:\n/// ```text\n/// Σ (i ∈ 0..n). values(T_i)\n/// ```\n/// so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.\n///\n/// See also:\n/// - <https://en.wikipedia.org/wiki/Tagged_union>\n/// - <https://ncatlab.org/nlab/show/sum+type>\n///\n/// [structural]: https://en.wikipedia.org/wiki/Structural_type_system\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType)]\n#[sats(crate = crate)]\npub struct SumType {\n    /// The possible variants of the sum type.\n    ///\n    /// The order is relevant as it defines the tags of the variants at runtime.\n    pub variants: Box<[SumTypeVariant]>,\n}\n\nimpl std::fmt::Debug for SumType {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"SumType \")?;\n        f.debug_map()\n            .entries(\n                self.variants\n                    .iter()\n                    .map(|variant| (crate::dbg_aggregate_name(&variant.name), &variant.algebraic_type)),\n            )\n            .finish()\n    }\n}\n\nimpl SumType {\n    /// Returns a sum type with these possible `variants`.\n    pub const fn new(variants: Box<[SumTypeVariant]>) -> Self {\n        Self { variants }\n    }\n\n    /// Returns a sum type of unnamed variants taken from `types`.\n    pub fn new_unnamed(types: Box<[AlgebraicType]>) -> Self {\n        let variants = Vec::from(types).into_iter().map(|ty| ty.into()).collect();\n        Self { variants }\n    }\n\n    /// Check whether this sum type is a structural option type.\n    ///\n    /// A structural option type has `some(T)` as its first variant and `none` as its second.\n    /// That is, `{ some(T), none }` or `some: T | none` depending on your notation.\n    /// Note that `some` and `none` are lowercase, unlike Rust's `Option`.\n    /// Order matters, and an option type with these variants in the opposite order will not be recognized.\n    ///\n    /// If the type does look like a structural option type, returns the type `T`.\n    pub fn as_option(&self) -> Option<&AlgebraicType> {\n        match &*self.variants {\n            [first, second] if Self::are_variants_option(first, second) => Some(&first.algebraic_type),\n            _ => None,\n        }\n    }\n\n    /// Check whether this sum type is a structural option type.\n    ///\n    /// A structural option type has `some(T)` as its first variant and `none` as its second.\n    /// That is, `{ some(T), none }` or `some: T | none` depending on your notation.\n    /// Note that `some` and `none` are lowercase, unlike Rust's `Option`.\n    /// Order matters, and an option type with these variants in the opposite order will not be recognized.\n    ///\n    /// If the type does look like a structural option type, returns the type `T`.\n    pub fn as_option_mut(&mut self) -> Option<&mut AlgebraicType> {\n        match &mut *self.variants {\n            [first, second] if Self::are_variants_option(first, second) => Some(&mut first.algebraic_type),\n            _ => None,\n        }\n    }\n\n    fn are_variants_option(first: &SumTypeVariant, second: &SumTypeVariant) -> bool {\n        second.is_unit() // Done first to avoid pointer indirection when it doesn't matter.\n        && first.has_name(OPTION_SOME_TAG)\n        && second.has_name(OPTION_NONE_TAG)\n    }\n\n    /// Check whether this sum type is a structural option type.\n    ///\n    /// A structural option type has `some(T)` as its first variant and `none` as its second.\n    /// That is, `{ some(T), none }` or `some: T | none` depending on your notation.\n    /// Note that `some` and `none` are lowercase, unlike Rust's `Option`.\n    /// Order matters, and an option type with these variants in the opposite order will not be recognized.\n    pub fn is_option(&self) -> bool {\n        self.as_option().is_some()\n    }\n\n    /// Check whether this sum type is a structural result type.\n    ///\n    /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second.\n    /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation.\n    /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`.\n    /// Order matters, and a result type with these variants in the opposite order will not be recognized.\n    ///\n    /// If the type does look like a structural result type, returns the types `T` and `E`.\n    pub fn as_result(&self) -> Option<(&AlgebraicType, &AlgebraicType)> {\n        match &*self.variants {\n            [first, second] if Self::are_variants_result(first, second) => {\n                Some((&first.algebraic_type, &second.algebraic_type))\n            }\n            _ => None,\n        }\n    }\n\n    /// Check whether this sum type is a structural result type.\n    ///\n    /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second.\n    /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation.\n    /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`.\n    /// Order matters, and a result type with these variants in the opposite order will not be recognized.\n    ///\n    /// If the type does look like a structural result type, returns the types `T` and `E`.\n    pub fn as_result_mut(&mut self) -> Option<(&mut AlgebraicType, &mut AlgebraicType)> {\n        match &mut *self.variants {\n            [first, second] if Self::are_variants_result(first, second) => {\n                Some((&mut first.algebraic_type, &mut second.algebraic_type))\n            }\n            _ => None,\n        }\n    }\n\n    fn are_variants_result(first: &SumTypeVariant, second: &SumTypeVariant) -> bool {\n        first.has_name(RESULT_OK_TAG) && second.has_name(RESULT_ERR_TAG)\n    }\n\n    /// Check whether this sum type is a structural result type.\n    ///\n    /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second.\n    /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation.\n    /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`.\n    ///\n    /// Order matters, and a result type with these variants in the opposite order will not be recognized.\n    pub fn is_result(&self) -> bool {\n        self.as_result().is_some()\n    }\n\n    /// Return whether this sum type is empty, that is, has no variants.\n    pub fn is_empty(&self) -> bool {\n        self.variants.is_empty()\n    }\n\n    /// Return whether this sum type is the special `ScheduleAt` type,\n    /// `Interval(u64) | Time(u64)`.\n    /// Does not follow `Ref`s.\n    pub fn is_schedule_at(&self) -> bool {\n        match &*self.variants {\n            [first, second] => {\n                first.has_name(SCHEDULE_AT_INTERVAL_TAG)\n                    && first.algebraic_type.is_time_duration()\n                    && second.has_name(SCHEDULE_AT_TIME_TAG)\n                    && second.algebraic_type.is_timestamp()\n            }\n            _ => false,\n        }\n    }\n\n    /// Returns whether this sum type is a special known type, currently `Option`, `ScheduleAt`, or `Result`.\n    pub fn is_special(&self) -> bool {\n        self.is_option() || self.is_schedule_at() || self.is_result()\n    }\n\n    /// Returns whether this sum type is like on in C without data attached to the variants.\n    pub fn is_simple_enum(&self) -> bool {\n        self.variants.iter().all(SumTypeVariant::is_unit)\n    }\n\n    /// Returns the sum type variant using `tag_name` with their tag position.\n    pub fn get_variant(&self, tag_name: &str) -> Option<(u8, &SumTypeVariant)> {\n        self.variants.iter().enumerate().find_map(|(pos, x)| {\n            if x.name.as_deref() == Some(tag_name) {\n                Some((pos as u8, x))\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Returns the sum type variant using `tag_name` with their tag position, if this is a [Self::is_simple_enum]\n    pub fn get_variant_simple(&self, tag_name: &str) -> Option<(u8, &SumTypeVariant)> {\n        if self.is_simple_enum() {\n            self.get_variant(tag_name)\n        } else {\n            None\n        }\n    }\n\n    /// Returns the sum type variant with the given `tag`.\n    pub fn get_variant_by_tag(&self, tag: u8) -> Option<&SumTypeVariant> {\n        self.variants.get(tag as usize)\n    }\n\n    pub fn type_check(&self, sv: &SumValue, typespace: &Typespace) -> bool {\n        self.get_variant_by_tag(sv.tag)\n            .is_some_and(|var| var.algebraic_type.type_check(&sv.value, typespace))\n    }\n}\n\nimpl From<Box<[SumTypeVariant]>> for SumType {\n    fn from(fields: Box<[SumTypeVariant]>) -> Self {\n        SumType::new(fields)\n    }\n}\nimpl<const N: usize> From<[SumTypeVariant; N]> for SumType {\n    fn from(fields: [SumTypeVariant; N]) -> Self {\n        SumType::new(fields.into())\n    }\n}\nimpl<const N: usize> From<[(Option<&'static str>, AlgebraicType); N]> for SumType {\n    fn from(fields: [(Option<&'static str>, AlgebraicType); N]) -> Self {\n        fields.map(|(s, t)| SumTypeVariant::new(t, s.map(Into::into))).into()\n    }\n}\nimpl<Id: Into<RawIdentifier>, const N: usize> From<[(Id, AlgebraicType); N]> for SumType {\n    fn from(fields: [(Id, AlgebraicType); N]) -> Self {\n        fields.map(|(s, t)| SumTypeVariant::new_named(t, s)).into()\n    }\n}\nimpl<const N: usize> From<[AlgebraicType; N]> for SumType {\n    fn from(fields: [AlgebraicType; N]) -> Self {\n        fields.map(SumTypeVariant::from).into()\n    }\n}\n\nimpl MetaType for SumType {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::product([(\"variants\", AlgebraicType::array(SumTypeVariant::meta_type()))])\n    }\n}\n\nimpl SumType {\n    pub fn as_value(&self) -> AlgebraicValue {\n        value_serialize(self)\n    }\n\n    pub fn from_value(value: &AlgebraicValue) -> Result<SumType, ValueDeserializeError> {\n        Self::deserialize(ValueDeserializer::from_ref(value))\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/sum_type_variant.rs",
    "content": "use crate::algebraic_type::AlgebraicType;\nuse crate::meta_type::MetaType;\nuse crate::raw_identifier::RawIdentifier;\nuse crate::SpacetimeType;\n\n/// A variant of a sum type.\n///\n/// NOTE: Each element has an implicit element tag based on its order.\n/// Uniquely identifies an element similarly to protobuf tags.\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType)]\n#[sats(crate = crate)]\npub struct SumTypeVariant {\n    /// The name of the variant, if any.\n    pub name: Option<RawIdentifier>,\n    /// The type of the variant.\n    ///\n    /// Unlike a language like Rust,\n    /// where we can have `enum _ { V1 { foo: A, bar: B, .. }, .. }`,\n    /// the product within the variant `V1`, i.e., `{ foo: A, bar: B, .. }`\n    /// is separated out in SATS into a separate product type.\n    /// So we would express this as `{ V1({ foo: A, bar: B, .. }), .. }`.\n    pub algebraic_type: AlgebraicType,\n}\n\nimpl SumTypeVariant {\n    /// Returns a sum type variant with an optional `name` and `algebraic_type`.\n    pub const fn new(algebraic_type: AlgebraicType, name: Option<RawIdentifier>) -> Self {\n        Self { algebraic_type, name }\n    }\n\n    /// Returns a sum type variant with `name` and `algebraic_type`.\n    pub fn new_named(algebraic_type: AlgebraicType, name: impl Into<RawIdentifier>) -> Self {\n        Self {\n            algebraic_type,\n            name: Some(name.into()),\n        }\n    }\n\n    /// Returns a unit variant with `name`.\n    pub fn unit(name: impl Into<RawIdentifier>) -> Self {\n        Self::new_named(AlgebraicType::unit(), name)\n    }\n\n    /// Returns the name of the variant.\n    pub fn name(&self) -> Option<&RawIdentifier> {\n        self.name.as_ref()\n    }\n\n    /// Returns whether the variant has the given name.\n    pub fn has_name(&self, name: &str) -> bool {\n        self.name().is_some_and(|n| &**n == name)\n    }\n\n    /// Returns whether this is a unit variant.\n    pub fn is_unit(&self) -> bool {\n        self.algebraic_type == AlgebraicType::unit()\n    }\n}\n\nimpl MetaType for SumTypeVariant {\n    fn meta_type() -> AlgebraicType {\n        AlgebraicType::product([\n            (\"name\", AlgebraicType::option(AlgebraicType::String)),\n            (\"algebraic_type\", AlgebraicType::ZERO_REF),\n        ])\n    }\n}\n\nimpl From<AlgebraicType> for SumTypeVariant {\n    fn from(algebraic_type: AlgebraicType) -> Self {\n        Self::new(algebraic_type, None)\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/sum_value.rs",
    "content": "use crate::algebraic_value::AlgebraicValue;\nuse crate::impl_deserialize;\nuse crate::sum_type::SumType;\n\n/// A value of a sum type choosing a specific variant of the type.\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct SumValue {\n    /// A tag representing the choice of one variant of the sum type's variants.\n    pub tag: u8,\n    /// Given a variant `Var(Ty)` in a sum type `{ Var(Ty), ... }`,\n    /// this provides the `value` for `Ty`.\n    pub value: Box<AlgebraicValue>,\n}\n\nimpl crate::Value for SumValue {\n    type Type = SumType;\n}\n\nimpl SumValue {\n    /// Returns a new `SumValue` with the given `tag` and `value`.\n    pub fn new(tag: u8, value: impl Into<AlgebraicValue>) -> Self {\n        let value = Box::from(value.into());\n        Self { tag, value }\n    }\n\n    /// Returns a new `SumValue` with the given `tag` and unit value.\n    pub fn new_simple(tag: u8) -> Self {\n        Self::new(tag, ())\n    }\n}\n\n/// The tag of a `SumValue`.\n/// Can be used to read out the tag of a sum value without reading the payload.\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\n#[repr(transparent)]\npub struct SumTag(pub u8);\n\nimpl_deserialize!([] SumTag, de => <_>::deserialize(de).map(SumTag));\n\n#[cfg(feature = \"memory-usage\")]\nimpl spacetimedb_memory_usage::MemoryUsage for SumTag {}\n\nimpl From<&u8> for &SumTag {\n    fn from(value: &u8) -> Self {\n        // SAFETY: `SumTag` is `repr(transparent)` of `u8`.\n        unsafe { core::mem::transmute(value) }\n    }\n}\n\nimpl From<SumTag> for SumValue {\n    fn from(SumTag(tag): SumTag) -> Self {\n        SumValue::new_simple(tag)\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/time_duration.rs",
    "content": "use crate::timestamp::MICROSECONDS_PER_SECOND;\nuse crate::{de::Deserialize, impl_st, ser::Serialize, AlgebraicType, AlgebraicValue};\nuse std::fmt;\nuse std::ops::{Add, AddAssign, Sub, SubAssign};\nuse std::time::Duration;\n\n#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, Debug)]\n#[sats(crate = crate)]\n/// A span or delta in time, measured in microseconds.\n///\n/// Analogous to [`std::time::Duration`], and to C#'s `TimeSpan`.\n/// Name chosen to avoid ambiguity with either of those types.\n///\n/// Unlike [`Duration`], but like C#'s `TimeSpan`,\n/// `TimeDuration` can represent negative values.\n/// It also offers less range than [`Duration`], so conversions in both directions may fail.\npub struct TimeDuration {\n    __time_duration_micros__: i64,\n}\n\nimpl_st!([] TimeDuration, AlgebraicType::time_duration());\n\nimpl TimeDuration {\n    pub const ZERO: TimeDuration = TimeDuration {\n        __time_duration_micros__: 0,\n    };\n\n    /// Get the number of microseconds `self` represents.\n    pub fn to_micros(self) -> i64 {\n        self.__time_duration_micros__\n    }\n\n    /// Construct a [`TimeDuration`] which is `micros` microseconds.\n    pub fn from_micros(micros: i64) -> Self {\n        Self {\n            __time_duration_micros__: micros,\n        }\n    }\n\n    /// Returns `Err(abs(self) as Duration)` if `self` is negative.\n    pub fn to_duration(self) -> Result<Duration, Duration> {\n        let micros = self.to_micros();\n        let duration = Duration::from_micros(micros.unsigned_abs());\n        if micros >= 0 {\n            Ok(duration)\n        } else {\n            Err(duration)\n        }\n    }\n\n    /// Returns a `Duration` representing the absolute magnitude of `self`.\n    ///\n    /// Regardless of whether `self` is positive or negative, the returned `Duration` is positive.\n    pub fn to_duration_abs(self) -> Duration {\n        match self.to_duration() {\n            Ok(dur) | Err(dur) => dur,\n        }\n    }\n\n    /// Converts `self` to `Duration`, clamping to 0 if negative.\n    pub fn to_duration_saturating(self) -> Duration {\n        self.to_duration().unwrap_or(Duration::ZERO)\n    }\n\n    /// Returns a positive `TimeDuration` with the magnitude of `self`.\n    pub fn abs(self) -> Self {\n        Self::from_micros(self.to_micros().saturating_abs())\n    }\n\n    /// Return a [`TimeDuration`] which represents the same span as `duration`.\n    ///\n    /// Panics if `duration.as_micros` overflows an `i64`\n    pub fn from_duration(duration: Duration) -> Self {\n        Self::from_micros(\n            duration\n                .as_micros()\n                .try_into()\n                .expect(\"Duration overflows i64 microseconds\"),\n        )\n    }\n\n    /// Returns `Some(self + other)`, or `None` if that value would be out of bounds for [`TimeDuration`].\n    pub fn checked_add(self, other: Self) -> Option<Self> {\n        self.to_micros().checked_add(other.to_micros()).map(Self::from_micros)\n    }\n\n    /// Returns `Some(self - other)`, or `None` if that value would be out of bounds for [`TimeDuration`].\n    pub fn checked_sub(self, other: Self) -> Option<Self> {\n        self.to_micros().checked_sub(other.to_micros()).map(Self::from_micros)\n    }\n\n    /// Generate an `iso8601` format string.\n    ///\n    /// This is the better supported format for use for the `pg wire protocol`.\n    ///\n    /// Example:\n    /// ```rust\n    /// use std::time::Duration;\n    /// use spacetimedb_sats::time_duration::TimeDuration;\n    /// assert_eq!( TimeDuration::from_micros(0).to_iso8601().as_str(), \"P0D\");\n    /// assert_eq!( TimeDuration::from_micros(-1_000_000).to_iso8601().as_str(), \"-PT1S\");\n    /// assert_eq!( TimeDuration::from_duration(Duration::from_secs(60 * 24)).to_iso8601().as_str(), \"PT1440S\");\n    /// ```\n    pub fn to_iso8601(self) -> String {\n        chrono::Duration::microseconds(self.to_micros()).to_string()\n    }\n}\n\nimpl From<Duration> for TimeDuration {\n    fn from(d: Duration) -> TimeDuration {\n        TimeDuration::from_duration(d)\n    }\n}\n\nimpl TryFrom<TimeDuration> for Duration {\n    type Error = Duration;\n    /// If `d` is negative, returns its magnitude as the `Err` variant.\n    fn try_from(d: TimeDuration) -> Result<Duration, Duration> {\n        d.to_duration()\n    }\n}\n\nimpl fmt::Display for TimeDuration {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let micros = self.to_micros();\n        let sign = if micros < 0 { \"-\" } else { \"+\" };\n        let pos = micros.abs();\n        let secs = pos / MICROSECONDS_PER_SECOND;\n        let micros_remaining = pos % MICROSECONDS_PER_SECOND;\n        write!(f, \"{sign}{secs}.{micros_remaining:06}\")\n    }\n}\n\nimpl Add for TimeDuration {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        self.checked_add(rhs).unwrap()\n    }\n}\n\nimpl Sub for TimeDuration {\n    type Output = Self;\n\n    fn sub(self, rhs: Self) -> Self::Output {\n        self.checked_sub(rhs).unwrap()\n    }\n}\n\nimpl AddAssign for TimeDuration {\n    fn add_assign(&mut self, rhs: Self) {\n        *self = *self + rhs;\n    }\n}\n\nimpl SubAssign for TimeDuration {\n    fn sub_assign(&mut self, rhs: Self) {\n        *self = *self - rhs;\n    }\n}\n\n// `std::time::Duration` has implementations of `Mul<u32>` and `Div<u32>`,\n// plus checked methods and assign traits.\n// It also has methods for division with floats,\n// both `Duration -> Duration -> float` and `Duration -> float -> Duration`.\n// We could provide some or all of these, but so far have not seen the need to.\n\nimpl From<TimeDuration> for AlgebraicValue {\n    fn from(value: TimeDuration) -> Self {\n        AlgebraicValue::product([value.to_micros().into()])\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::GroundSpacetimeType;\n    use proptest::prelude::*;\n    use std::time::SystemTime;\n\n    #[test]\n    fn timestamp_type_matches() {\n        assert_eq!(AlgebraicType::time_duration(), TimeDuration::get_type());\n        assert!(TimeDuration::get_type().is_time_duration());\n        assert!(TimeDuration::get_type().is_special());\n    }\n\n    #[test]\n    fn round_trip_duration_through_time_duration() {\n        let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();\n        let rounded = Duration::from_micros(now.as_micros() as _);\n        let time_duration = TimeDuration::from_duration(rounded);\n        let now_prime = time_duration.to_duration().unwrap();\n        assert_eq!(rounded, now_prime);\n    }\n\n    proptest! {\n        #[test]\n        fn round_trip_time_duration_through_systemtime(micros in any::<i64>().prop_map(|n| n.abs())) {\n            let time_duration = TimeDuration::from_micros(micros);\n            let duration = time_duration.to_duration().unwrap();\n            let time_duration_prime = TimeDuration::from_duration(duration);\n            prop_assert_eq!(time_duration_prime, time_duration);\n            prop_assert_eq!(time_duration_prime.to_micros(), micros);\n        }\n\n        #[test]\n        fn arithmetic_as_expected(lhs in any::<i64>(), rhs in any::<i64>()) {\n            let lhs_time_duration = TimeDuration::from_micros(lhs);\n            let rhs_time_duration = TimeDuration::from_micros(rhs);\n\n            if let Some(sum) = lhs.checked_add(rhs) {\n                let sum_time_duration = lhs_time_duration.checked_add(rhs_time_duration);\n                prop_assert!(sum_time_duration.is_some());\n                prop_assert_eq!(sum_time_duration.unwrap().to_micros(), sum);\n\n                prop_assert_eq!((lhs_time_duration + rhs_time_duration).to_micros(), sum);\n\n                let mut sum_assign = lhs_time_duration;\n                sum_assign += rhs_time_duration;\n                prop_assert_eq!(sum_assign.to_micros(), sum);\n            } else {\n                prop_assert!(lhs_time_duration.checked_add(rhs_time_duration).is_none());\n            }\n\n            if let Some(diff) = lhs.checked_sub(rhs) {\n                let diff_time_duration = lhs_time_duration.checked_sub(rhs_time_duration);\n                prop_assert!(diff_time_duration.is_some());\n                prop_assert_eq!(diff_time_duration.unwrap().to_micros(), diff);\n\n                prop_assert_eq!((lhs_time_duration - rhs_time_duration).to_micros(), diff);\n\n                let mut diff_assign = lhs_time_duration;\n                diff_assign -= rhs_time_duration;\n                prop_assert_eq!(diff_assign.to_micros(), diff);\n            } else {\n                prop_assert!(lhs_time_duration.checked_sub(rhs_time_duration).is_none());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/timestamp.rs",
    "content": "use anyhow::Context;\nuse chrono::DateTime;\n\nuse crate::{de::Deserialize, impl_st, ser::Serialize, time_duration::TimeDuration, AlgebraicType, AlgebraicValue};\nuse std::fmt;\nuse std::ops::{Add, AddAssign, Sub, SubAssign};\nuse std::time::{Duration, SystemTime};\n\n#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, Debug)]\n#[sats(crate = crate)]\n/// A point in time, measured in microseconds since the Unix epoch.\npub struct Timestamp {\n    __timestamp_micros_since_unix_epoch__: i64,\n}\n\nimpl_st!([] Timestamp, AlgebraicType::timestamp());\n\nimpl Timestamp {\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    pub fn now() -> Self {\n        Self::from_system_time(SystemTime::now())\n    }\n\n    #[cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))]\n    #[deprecated = \"Timestamp::now() is stubbed and will panic. Read the `.timestamp` field of a `ReducerContext` instead.\"]\n    pub fn now() -> Self {\n        unimplemented!()\n    }\n\n    pub const UNIX_EPOCH: Self = Self {\n        __timestamp_micros_since_unix_epoch__: 0,\n    };\n\n    /// Get the number of microseconds `self` is offset from [`Self::UNIX_EPOCH`].\n    ///\n    /// A positive value means a time after the Unix epoch,\n    /// and a negative value means a time before.\n    pub fn to_micros_since_unix_epoch(self) -> i64 {\n        self.__timestamp_micros_since_unix_epoch__\n    }\n\n    /// Construct a [`Timestamp`] which is `micros` microseconds offset from [`Self::UNIX_EPOCH`].\n    ///\n    /// A positive value means a time after the Unix epoch,\n    /// and a negative value means a time before.\n    pub fn from_micros_since_unix_epoch(micros: i64) -> Self {\n        Self {\n            __timestamp_micros_since_unix_epoch__: micros,\n        }\n    }\n\n    pub fn from_time_duration_since_unix_epoch(time_duration: TimeDuration) -> Self {\n        Self::from_micros_since_unix_epoch(time_duration.to_micros())\n    }\n\n    pub fn to_time_duration_since_unix_epoch(self) -> TimeDuration {\n        TimeDuration::from_micros(self.to_micros_since_unix_epoch())\n    }\n\n    /// Returns `Err(duration_before_unix_epoch)` if `self` is before `Self::UNIX_EPOCH`.\n    pub fn to_duration_since_unix_epoch(self) -> Result<Duration, Duration> {\n        let micros = self.to_micros_since_unix_epoch();\n        if micros >= 0 {\n            Ok(Duration::from_micros(micros as u64))\n        } else {\n            Err(Duration::from_micros((-micros) as u64))\n        }\n    }\n\n    /// Return a [`Timestamp`] which is [`Timestamp::UNIX_EPOCH`] plus `duration`.\n    ///\n    /// Panics if `duration.as_micros` overflows an `i64`\n    pub fn from_duration_since_unix_epoch(duration: Duration) -> Self {\n        Self::from_micros_since_unix_epoch(\n            duration\n                .as_micros()\n                .try_into()\n                .expect(\"Duration since Unix epoch overflows i64 microseconds\"),\n        )\n    }\n\n    /// Convert `self` into a [`SystemTime`] which refers to approximately the same point in time.\n    ///\n    /// This conversion may lose precision, as [`SystemTime`]'s prevision varies depending on platform.\n    /// E.g. Unix targets have microsecond precision, but Windows only 100-microsecond precision.\n    ///\n    /// This conversion may panic if `self` is out of bounds for [`SystemTime`].\n    /// We are not aware of any platforms for which [`SystemTime`] offers a smaller range than [`Timestamp`],\n    /// but such a platform may exist.\n    pub fn to_system_time(self) -> SystemTime {\n        match self.to_duration_since_unix_epoch() {\n            Ok(positive) => SystemTime::UNIX_EPOCH\n                .checked_add(positive)\n                .expect(\"Timestamp with i64 microseconds since Unix epoch overflows SystemTime\"),\n            Err(negative) => SystemTime::UNIX_EPOCH\n                .checked_sub(negative)\n                .expect(\"Timestamp with i64 microseconds before Unix epoch overflows SystemTime\"),\n        }\n    }\n\n    /// Convert a [`SystemTime`] into a [`Timestamp`] which refers to approximately the same point in time.\n    ///\n    /// This conversion may panic if `system_time` is out of bounds for [`Duration`].\n    /// [`SystemTime`]'s range is larger than [`Timestamp`] on both Unix and Windows targets,\n    /// so times in the far past or far future may panic.\n    /// [`Timestamp`]'s range is approximately 292 years before and after the Unix epoch.\n    pub fn from_system_time(system_time: SystemTime) -> Self {\n        let duration = system_time\n            .duration_since(SystemTime::UNIX_EPOCH)\n            .expect(\"SystemTime predates the Unix epoch\");\n        Self::from_duration_since_unix_epoch(duration)\n    }\n\n    /// Returns the [`Duration`] delta between `self` and `earlier`, if `earlier` predates `self`.\n    ///\n    /// Returns `None` if `earlier` is strictly greater than `self`,\n    /// or if the difference between `earlier` and `self` overflows an `i64`.\n    pub fn duration_since(self, earlier: Timestamp) -> Option<Duration> {\n        self.time_duration_since(earlier)?.to_duration().ok()\n    }\n\n    /// Returns the [`TimeDuration`] delta between `self` and `earlier`.\n    ///\n    /// The result may be negative if `earlier` is actually later than `self`.\n    ///\n    /// Returns `None` if the subtraction overflows or underflows `i64` microseconds.\n    pub fn time_duration_since(self, earlier: Timestamp) -> Option<TimeDuration> {\n        let delta = self\n            .to_micros_since_unix_epoch()\n            .checked_sub(earlier.to_micros_since_unix_epoch())?;\n        Some(TimeDuration::from_micros(delta))\n    }\n\n    /// Parses an RFC 3339 formatted timestamp string\n    pub fn parse_from_rfc3339(str: &str) -> anyhow::Result<Timestamp> {\n        DateTime::parse_from_rfc3339(str)\n            .map_err(|err| anyhow::anyhow!(err))\n            .with_context(|| \"Invalid timestamp format. Expected RFC 3339 format (e.g. '2025-02-10 15:45:30').\")\n            .map(|dt| dt.timestamp_micros())\n            .map(Timestamp::from_micros_since_unix_epoch)\n    }\n\n    /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as a `Timestamp`,\n    /// i.e. a 64-bit signed number of microseconds before or after the Unix epoch.\n    pub fn checked_add(&self, duration: TimeDuration) -> Option<Self> {\n        self.__timestamp_micros_since_unix_epoch__\n            .checked_add(duration.to_micros())\n            .map(Timestamp::from_micros_since_unix_epoch)\n    }\n\n    /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as a `Timestamp`,\n    /// i.e. a 64-bit signed number of microseconds before or after the Unix epoch.\n    pub fn checked_sub(&self, duration: TimeDuration) -> Option<Self> {\n        self.__timestamp_micros_since_unix_epoch__\n            .checked_sub(duration.to_micros())\n            .map(Timestamp::from_micros_since_unix_epoch)\n    }\n\n    /// Returns `Some(self + duration)`, or `None` if that value would be out-of-bounds for `Timestamp`.\n    ///\n    /// Converts `duration` into a [`TimeDuration`] before the arithmetic.\n    /// Depending on the target platform's representation of [`Duration`], this may lose precision.\n    pub fn checked_add_duration(&self, duration: Duration) -> Option<Self> {\n        self.checked_add(TimeDuration::from_duration(duration))\n    }\n\n    /// Returns `Some(self - duration)`, or `None` if that value would be out-of-bounds for `Timestamp`.\n    ///\n    /// Converts `duration` into a [`TimeDuration`] before the arithmetic.\n    /// Depending on the target platform's representation of [`Duration`], this may lose precision.\n    pub fn checked_sub_duration(&self, duration: Duration) -> Option<Self> {\n        self.checked_sub(TimeDuration::from_duration(duration))\n    }\n\n    pub fn to_chrono_date_time(&self) -> anyhow::Result<DateTime<chrono::Utc>> {\n        DateTime::from_timestamp_micros(self.to_micros_since_unix_epoch())\n            .ok_or_else(|| anyhow::anyhow!(\"Timestamp with i64 microseconds since Unix epoch overflows DateTime\"))\n            .with_context(|| self.to_micros_since_unix_epoch())\n    }\n\n    /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`.\n    pub fn to_rfc3339(&self) -> anyhow::Result<String> {\n        Ok(self.to_chrono_date_time()?.to_rfc3339())\n    }\n}\n\nimpl Add<TimeDuration> for Timestamp {\n    type Output = Self;\n\n    fn add(self, other: TimeDuration) -> Self::Output {\n        self.checked_add(other).unwrap()\n    }\n}\n\nimpl Add<Duration> for Timestamp {\n    type Output = Self;\n\n    fn add(self, other: Duration) -> Self::Output {\n        self.checked_add_duration(other).unwrap()\n    }\n}\n\nimpl Sub<TimeDuration> for Timestamp {\n    type Output = Self;\n\n    fn sub(self, other: TimeDuration) -> Self::Output {\n        self.checked_sub(other).unwrap()\n    }\n}\n\nimpl Sub<Duration> for Timestamp {\n    type Output = Self;\n\n    fn sub(self, other: Duration) -> Self::Output {\n        self.checked_sub_duration(other).unwrap()\n    }\n}\n\nimpl AddAssign<TimeDuration> for Timestamp {\n    fn add_assign(&mut self, other: TimeDuration) {\n        *self = *self + other;\n    }\n}\n\nimpl AddAssign<Duration> for Timestamp {\n    fn add_assign(&mut self, other: Duration) {\n        *self = *self + other;\n    }\n}\n\nimpl SubAssign<TimeDuration> for Timestamp {\n    fn sub_assign(&mut self, rhs: TimeDuration) {\n        *self = *self - rhs;\n    }\n}\n\nimpl SubAssign<Duration> for Timestamp {\n    fn sub_assign(&mut self, rhs: Duration) {\n        *self = *self - rhs;\n    }\n}\n\npub(crate) const MICROSECONDS_PER_SECOND: i64 = 1_000_000;\n\nimpl fmt::Display for Timestamp {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.to_rfc3339().unwrap())\n    }\n}\n\nimpl From<SystemTime> for Timestamp {\n    fn from(system_time: SystemTime) -> Self {\n        Self::from_system_time(system_time)\n    }\n}\n\nimpl From<Timestamp> for SystemTime {\n    fn from(timestamp: Timestamp) -> Self {\n        timestamp.to_system_time()\n    }\n}\n\nimpl From<Timestamp> for AlgebraicValue {\n    fn from(value: Timestamp) -> Self {\n        AlgebraicValue::product([value.to_micros_since_unix_epoch().into()])\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::GroundSpacetimeType;\n    use proptest::prelude::*;\n\n    fn round_to_micros(st: SystemTime) -> SystemTime {\n        let duration = st.duration_since(SystemTime::UNIX_EPOCH).unwrap();\n        let micros = duration.as_micros();\n        SystemTime::UNIX_EPOCH + Duration::from_micros(micros as _)\n    }\n\n    #[test]\n    fn timestamp_type_matches() {\n        assert_eq!(AlgebraicType::timestamp(), Timestamp::get_type());\n        assert!(Timestamp::get_type().is_timestamp());\n        assert!(Timestamp::get_type().is_special());\n    }\n\n    #[test]\n    fn round_trip_systemtime_through_timestamp() {\n        let now = round_to_micros(SystemTime::now());\n        let timestamp = Timestamp::from(now);\n        let now_prime = SystemTime::from(timestamp);\n        assert_eq!(now, now_prime);\n    }\n\n    proptest! {\n        #[test]\n        fn round_trip_timestamp_through_systemtime(micros in any::<i64>().prop_map(|n| n.abs())) {\n            let timestamp = Timestamp::from_micros_since_unix_epoch(micros);\n            let system_time = SystemTime::from(timestamp);\n            let timestamp_prime = Timestamp::from(system_time);\n            prop_assert_eq!(timestamp_prime, timestamp);\n            prop_assert_eq!(timestamp_prime.to_micros_since_unix_epoch(), micros);\n        }\n\n        #[test]\n        fn arithmetic_with_timeduration(lhs in any::<i64>(), rhs in any::<i64>()) {\n            let lhs_timestamp = Timestamp::from_micros_since_unix_epoch(lhs);\n            let rhs_time_duration = TimeDuration::from_micros(rhs);\n\n            if let Some(sum) = lhs.checked_add(rhs) {\n                let sum_timestamp = lhs_timestamp.checked_add(rhs_time_duration);\n                prop_assert!(sum_timestamp.is_some());\n                prop_assert_eq!(sum_timestamp.unwrap().to_micros_since_unix_epoch(), sum);\n\n                prop_assert_eq!((lhs_timestamp + rhs_time_duration).to_micros_since_unix_epoch(), sum);\n\n                let mut sum_assign = lhs_timestamp;\n                sum_assign += rhs_time_duration;\n                prop_assert_eq!(sum_assign.to_micros_since_unix_epoch(), sum);\n            } else {\n                prop_assert!(lhs_timestamp.checked_add(rhs_time_duration).is_none());\n            }\n\n            if let Some(diff) = lhs.checked_sub(rhs) {\n                let diff_timestamp = lhs_timestamp.checked_sub(rhs_time_duration);\n                prop_assert!(diff_timestamp.is_some());\n                prop_assert_eq!(diff_timestamp.unwrap().to_micros_since_unix_epoch(), diff);\n\n                prop_assert_eq!((lhs_timestamp - rhs_time_duration).to_micros_since_unix_epoch(), diff);\n\n                let mut diff_assign = lhs_timestamp;\n                diff_assign -= rhs_time_duration;\n                prop_assert_eq!(diff_assign.to_micros_since_unix_epoch(), diff);\n            } else {\n                prop_assert!(lhs_timestamp.checked_sub(rhs_time_duration).is_none());\n            }\n        }\n\n        // TODO: determine what guarantees we provide for arithmetic with `Duration`,\n        // then write tests that we uphold said guarantees.\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/typespace.rs",
    "content": "use crate::algebraic_type::AlgebraicType;\nuse crate::algebraic_type_ref::AlgebraicTypeRef;\nuse crate::WithTypespace;\nuse core::any::TypeId;\nuse core::ops::{Index, IndexMut};\nuse ethnum::{i256, u256};\nuse smallvec::SmallVec;\nuse std::rc::Rc;\nuse std::sync::Arc;\n\n/// An error that occurs when attempting to resolve a type.\n#[derive(thiserror::Error, Debug, PartialOrd, Ord, PartialEq, Eq)]\npub enum TypeRefError {\n    // TODO: ideally this should give some useful type name or path.\n    // Figure out if we can provide that even though it's not encoded in SATS.\n    #[error(\"Found recursive type reference {0}\")]\n    RecursiveTypeRef(AlgebraicTypeRef),\n\n    #[error(\"Type reference {0} out of bounds\")]\n    InvalidTypeRef(AlgebraicTypeRef),\n}\n\n/// A `Typespace` represents the typing context in SATS.\n///\n/// That is, this is the `Δ` or `Γ` you'll see in type theory literature.\n///\n/// We use (sort of) [deBrujin indices](https://en.wikipedia.org/wiki/De_Bruijn_index)\n/// to represent our type variables.\n/// Notably however, these are given for the entire module\n/// and there are no universal quantifiers (i.e., `Δ, α ⊢ τ | Δ ⊢ ∀ α. τ`)\n/// nor are there type lambdas (i.e., `Λτ. v`).\n/// See [System F], the second-order lambda calculus, for more on `∀` and `Λ`.\n///\n/// There are however recursive types in SATs,\n/// e.g., `&0 = { Cons({ v: U8, t: &0 }), Nil }` represents a basic cons list\n/// where `&0` is the type reference at index `0`.\n///\n/// [System F]: https://en.wikipedia.org/wiki/System_F\n#[derive(Clone, SpacetimeType)]\n#[cfg_attr(feature = \"test\", derive(PartialEq, Eq, PartialOrd, Ord))]\n#[sats(crate = crate)]\npub struct Typespace {\n    /// The types in our typing context that can be referred to with [`AlgebraicTypeRef`]s.\n    pub types: Vec<AlgebraicType>,\n}\n\nimpl std::fmt::Debug for Typespace {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"Typespace \")?;\n        f.debug_list().entries(&self.types).finish()\n    }\n}\n\nimpl Default for Typespace {\n    fn default() -> Self {\n        Self::new(Vec::new())\n    }\n}\n\nimpl Index<AlgebraicTypeRef> for Typespace {\n    type Output = AlgebraicType;\n\n    fn index(&self, index: AlgebraicTypeRef) -> &Self::Output {\n        &self.types[index.idx()]\n    }\n}\nimpl IndexMut<AlgebraicTypeRef> for Typespace {\n    fn index_mut(&mut self, index: AlgebraicTypeRef) -> &mut Self::Output {\n        &mut self.types[index.idx()]\n    }\n}\n\nimpl Typespace {\n    pub const EMPTY: &'static Typespace = &Self::new(Vec::new());\n\n    /// Returns a context ([`Typespace`]) with the given `types`.\n    pub const fn new(types: Vec<AlgebraicType>) -> Self {\n        Self { types }\n    }\n\n    /// Returns the [`AlgebraicType`] referred to by `r` within this context.\n    pub fn get(&self, r: AlgebraicTypeRef) -> Option<&AlgebraicType> {\n        self.types.get(r.idx())\n    }\n\n    /// Returns a mutable reference to the [`AlgebraicType`] referred to by `r` within this context.\n    pub fn get_mut(&mut self, r: AlgebraicTypeRef) -> Option<&mut AlgebraicType> {\n        self.types.get_mut(r.idx())\n    }\n\n    /// Inserts an `AlgebraicType` into the typespace\n    /// and returns an `AlgebraicTypeRef` that refers to the inserted `AlgebraicType`.\n    ///\n    /// This allows for self referential,\n    /// recursive or other complex types to be declared in the typespace.\n    ///\n    /// You can also use this to later change the meaning of the returned `AlgebraicTypeRef`\n    /// when you cannot provide the full definition of the type yet.\n    ///\n    /// Panics if the number of type references exceeds an `u32`.\n    pub fn add(&mut self, ty: AlgebraicType) -> AlgebraicTypeRef {\n        let index = self\n            .types\n            .len()\n            .try_into()\n            .expect(\"ran out of space for `AlgebraicTypeRef`s\");\n\n        self.types.push(ty);\n        AlgebraicTypeRef(index)\n    }\n\n    /// Returns `ty` combined with the context `self`.\n    pub const fn with_type<'a, T: ?Sized>(&'a self, ty: &'a T) -> WithTypespace<'a, T> {\n        WithTypespace::new(self, ty)\n    }\n\n    /// Returns the `AlgebraicType` that `r` resolves to in the context of the `Typespace`.\n    ///\n    /// Panics if `r` is not known by the `Typespace`.\n    ///\n    /// Note, this is not recursive.\n    /// To resolve all nested refs, call `resolve_refs()` on the result.\n    pub fn resolve(&self, r: AlgebraicTypeRef) -> WithTypespace<'_, AlgebraicType> {\n        self.with_type(&self[r])\n    }\n\n    /// Inlines all type references in `ty` recursively using the current typeset.\n    pub fn inline_typerefs_in_type(&mut self, ty: &mut AlgebraicType) -> Result<(), TypeRefError> {\n        match ty {\n            AlgebraicType::Sum(sum_ty) => {\n                for variant in &mut *sum_ty.variants {\n                    self.inline_typerefs_in_type(&mut variant.algebraic_type)?;\n                }\n            }\n            AlgebraicType::Product(product_ty) => {\n                for element in &mut *product_ty.elements {\n                    self.inline_typerefs_in_type(&mut element.algebraic_type)?;\n                }\n            }\n            AlgebraicType::Array(array_ty) => {\n                self.inline_typerefs_in_type(&mut array_ty.elem_ty)?;\n            }\n            AlgebraicType::Ref(r) => {\n                // Lazily resolve any nested references first.\n                let resolved_ty = self.inline_typerefs_in_ref(*r)?;\n                // Now we can clone the fully-resolved type.\n                *ty = resolved_ty.clone();\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    /// Inlines all nested references behind the current [`AlgebraicTypeRef`] recursively using the current typeset.\n    ///\n    /// Returns the fully-resolved type or an error if the type reference is invalid or self-referential.\n    fn inline_typerefs_in_ref(&mut self, r: AlgebraicTypeRef) -> Result<&AlgebraicType, TypeRefError> {\n        let resolved_ty = match self.get_mut(r) {\n            None => return Err(TypeRefError::InvalidTypeRef(r)),\n            // If we encountered a type reference, that means one of the parent calls\n            // to `inline_typerefs_in_ref(r)` swapped its definition out,\n            // i.e. the type referred to by `r` is recursive.\n            // Note that it doesn't necessarily need to be the current call,\n            // e.g. A -> B -> A dependency also forms a recursive cycle.\n            // Our database can't handle recursive types, so return an error.\n            // TODO: support recursive types in the future.\n            Some(AlgebraicType::Ref(_)) => return Err(TypeRefError::RecursiveTypeRef(r)),\n            Some(resolved_ty) => resolved_ty,\n        };\n        // First, swap the type with a reference.\n        // This allows us to:\n        // 1. Recurse into each type mutably while holding a mutable\n        //    reference to the typespace as well, without cloning.\n        // 2. Easily detect self-references at arbitrary depth without\n        //    having to keep a separate `seen: HashSet<_>` or something.\n        let mut resolved_ty = std::mem::replace(resolved_ty, AlgebraicType::Ref(r));\n        // Next, recurse into the type and inline any nested type references.\n        self.inline_typerefs_in_type(&mut resolved_ty)?;\n        // Resolve the place again, since we couldn't hold the mutable reference across the call above.\n        let place = &mut self[r];\n        // Now we can put the fully-resolved type back and return that place.\n        *place = resolved_ty;\n        Ok(place)\n    }\n\n    /// Inlines all type references in the typespace recursively.\n    ///\n    /// Errors out if any type reference is invalid or self-referential.\n    pub fn inline_all_typerefs(&mut self) -> Result<(), TypeRefError> {\n        // We need to use indices here to allow mutable reference on each iteration.\n        for r in 0..self.types.len() as u32 {\n            self.inline_typerefs_in_ref(AlgebraicTypeRef(r))?;\n        }\n        Ok(())\n    }\n\n    /// Iterate over types in the typespace with their references.\n    pub fn refs_with_types(&self) -> impl Iterator<Item = (AlgebraicTypeRef, &AlgebraicType)> {\n        self.types\n            .iter()\n            .enumerate()\n            .map(|(idx, ty)| (AlgebraicTypeRef(idx as _), ty))\n    }\n\n    /// Check that the entire typespace is valid for generating a `SpacetimeDB` client module.\n    /// See also the `spacetimedb_schema` crate, which layers additional validation on top\n    /// of these checks.\n    ///\n    /// All types in the typespace must either satisfy\n    /// [`is_valid_for_client_type_definition`](AlgebraicType::is_valid_for_client_type_definition) or\n    /// [`is_valid_for_client_type_use`](AlgebraicType::is_valid_for_client_type_use).\n    /// (Only the types that are `valid_for_client_type_definition` will have types generated in\n    /// the client, but the other types are allowed for the convenience of module binding codegen.)\n    pub fn is_valid_for_client_code_generation(&self) -> bool {\n        self.types\n            .iter()\n            .all(|ty| ty.is_valid_for_client_type_definition() || ty.is_valid_for_client_type_use())\n    }\n}\n\nimpl FromIterator<AlgebraicType> for Typespace {\n    fn from_iter<T: IntoIterator<Item = AlgebraicType>>(iter: T) -> Self {\n        Self {\n            types: iter.into_iter().collect(),\n        }\n    }\n}\n\n/// A trait for Rust types that can be represented as an [`AlgebraicType`]\n/// with an empty typing context.\n///\n/// The returned `AlgebraicType` must have no free variables,\n/// that is, no `AlgebraicTypeRef`s in its tree at all.\npub trait GroundSpacetimeType {\n    /// Returns the `AlgebraicType` representation of `Self`.\n    fn get_type() -> AlgebraicType;\n}\n\n/// This trait makes types self-describing, allowing them to automatically register their structure\n/// with SpacetimeDB. This is used to tell SpacetimeDB about the structure of a module's tables and\n/// reducers.\n///\n/// Deriving this trait also derives [`Serialize`](crate::ser::Serialize), [`Deserialize`](crate::de::Deserialize),\n/// and [`Debug`](std::fmt::Debug). (There are currently no trait bounds on `SpacetimeType` documenting this fact.)\n/// `Serialize` and `Deserialize` are used to convert Rust data structures to other formats, suitable for storing on disk or passing over the network. `Debug` is simply for debugging convenience.\n///\n/// Any Rust type implementing `SpacetimeType` can be used as a table column or reducer argument. A derive macro is provided, and can be used on both structs and enums:\n///\n/// ```rust\n/// # use spacetimedb_sats::SpacetimeType;\n///\n/// #[derive(SpacetimeType)]\n/// # #[sats(crate = spacetimedb_sats)]\n/// struct Location {\n///     x: u64,\n///     y: u64\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// # #[sats(crate = spacetimedb_sats)]\n/// struct PlasticCrate {\n///     count: u32,\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// # #[sats(crate = spacetimedb_sats)]\n/// struct AppleCrate {\n///     variety: String,\n///     count: u32,\n///     freshness: u32,\n/// }\n///\n/// #[derive(SpacetimeType)]\n/// # #[sats(crate = spacetimedb_sats)]\n/// enum FruitCrate {\n///     Apples(AppleCrate),\n///     Plastic(PlasticCrate),\n/// }\n/// ```\n///\n/// The fields of the struct/enum must also implement `SpacetimeType`.\n///\n/// Any type annotated with `#[table(..)]` automatically derives `SpacetimeType`.\n///\n/// SpacetimeType is implemented for many of the primitive types in the standard library:\n///\n/// - `bool`\n/// - `u8`, `u16`, `u32`, `u64`, `u128`\n/// - `i8`, `i16`, `i32`, `i64`, `i128`\n/// - `f32`, `f64`\n///\n/// And common data structures:\n///\n/// - `String` and `&str`, utf-8 string data\n/// - `()`, the unit type\n/// - `Option<T> where T: SpacetimeType`\n/// - `Result<T, E> where T: SpacetimeType, E: SpacetimeType`\n/// - `Vec<T> where T: SpacetimeType`\n///\n/// (Storing collections in rows of a database table is a form of [denormalization](https://en.wikipedia.org/wiki/Denormalization).)\n///\n/// Do not manually implement this trait unless you are VERY sure you know what you're doing.\n/// Implementations must be consistent with `Deserialize<'de> for T`, `Serialize for T` and `Serialize, Deserialize for AlgebraicValue`.\n/// Implementations that are inconsistent across these traits may result in data loss.\n///\n/// N.B.: It's `SpacetimeType`, not `SpaceTimeType`.\n// TODO: we might want to have a note about what to do if you're trying to use a type from another crate in your table.\n// keep this note in sync with the ones on spacetimedb::rt::{ReducerArg, TableColumn}\n#[diagnostic::on_unimplemented(note = \"if you own the type, try adding `#[derive(SpacetimeType)]` to its definition\")]\npub trait SpacetimeType {\n    /// Returns an `AlgebraicType` representing the type for `Self` in SATS\n    /// and in the typing context in `typespace`. This is used by the\n    /// automatic type registration system in Rust modules.\n    ///\n    /// The resulting `AlgebraicType` may contain `Ref`s that only make sense\n    /// within the context of this particular `typespace`.\n    fn make_type<S: TypespaceBuilder>(typespace: &mut S) -> AlgebraicType;\n}\n\npub use spacetimedb_bindings_macro::SpacetimeType;\n\n/// A trait for types that can build a [`Typespace`].\npub trait TypespaceBuilder {\n    /// Returns and adds a representation of type `T: 'static` as an [`AlgebraicType`]\n    /// with an optional `name` to the typing context in `self`.\n    fn add(\n        &mut self,\n        typeid: TypeId,\n        name: Option<&'static str>,\n        make_ty: impl FnOnce(&mut Self) -> AlgebraicType,\n    ) -> AlgebraicType;\n\n    fn add_type<T: SpacetimeType>(&mut self) -> AlgebraicType\n    where\n        Self: Sized,\n    {\n        T::make_type(self)\n    }\n}\n\n/// Implements [`SpacetimeType`] for a type in a simplified manner.\n///\n/// An example:\n/// ```ignore\n/// struct Foo<'a, T>(&'a T, u8);\n/// impl_st!(\n/// //     Type parameters      Impl type\n/// //            v                 v\n/// //   --------------------  ----------\n///     ['a, T: SpacetimeType] Foo<'a, T>,\n/// //  The `make_type` implementation where `ts: impl TypespaceBuilder`\n/// //  and the expression right of `=>` is an `AlgebraicType`.\n///     ts => AlgebraicType::product([T::make_type(ts), AlgebraicType::U8])\n/// );\n/// ```\n#[macro_export]\nmacro_rules! impl_st {\n    ([ $($generic_wrapped:ident $($other_generics:tt)*)? ] $rty:ty, $stty:expr) => {\n        impl<$($generic_wrapped $($other_generics)*)?> $crate::GroundSpacetimeType for $rty\n            $(where $generic_wrapped: $crate::GroundSpacetimeType)?\n        {\n            fn get_type() -> $crate::AlgebraicType {\n                $stty\n            }\n        }\n\n        impl_st!([ $($generic $($other_generics)*)? ] $rty, _ts => $stty);\n    };\n    ([ $($generic_wrapped:ident $($other_generics:tt)*)? ] $rty:ty, $ts:ident => $stty:expr) => {\n        impl<$($generic_wrapped $($other_generics)*)?> $crate::SpacetimeType for $rty\n            $(where $generic_wrapped: $crate::SpacetimeType)?\n        {\n            fn make_type<S: $crate::typespace::TypespaceBuilder>($ts: &mut S) -> $crate::AlgebraicType {\n                $stty\n            }\n        }\n    };\n}\n\nmacro_rules! impl_primitives {\n    ($($t:ty => $x:ident,)*) => {\n        $(impl_st!([] $t, AlgebraicType::$x);)*\n    };\n}\n\nimpl_primitives! {\n    bool => Bool,\n    u8 => U8,\n    i8 => I8,\n    u16 => U16,\n    i16 => I16,\n    u32 => U32,\n    i32 => I32,\n    u64 => U64,\n    i64 => I64,\n    u128 => U128,\n    i128 => I128,\n    u256 => U256,\n    i256 => I256,\n    f32 => F32,\n    f64 => F64,\n    String => String,\n}\n\nimpl_st!([](), AlgebraicType::unit());\nimpl_st!([] str, AlgebraicType::String);\nimpl_st!([T] [T], ts => AlgebraicType::array(T::make_type(ts)));\nimpl_st!([T: ?Sized] &T, ts => T::make_type(ts));\nimpl_st!([T: ?Sized] Box<T>, ts => T::make_type(ts));\nimpl_st!([T: ?Sized] Rc<T>, ts => T::make_type(ts));\nimpl_st!([T: ?Sized] Arc<T>, ts => T::make_type(ts));\nimpl_st!([T] Vec<T>, ts => <[T]>::make_type(ts));\nimpl_st!([T, const N: usize] SmallVec<[T; N]>, ts => <[T]>::make_type(ts));\nimpl_st!([T] Option<T>, ts => AlgebraicType::option(T::make_type(ts)));\n\nimpl_st!([] spacetimedb_primitives::ArgId, AlgebraicType::U64);\nimpl_st!([] spacetimedb_primitives::ColId, AlgebraicType::U16);\nimpl_st!([] spacetimedb_primitives::TableId, AlgebraicType::U32);\nimpl_st!([] spacetimedb_primitives::ViewId, AlgebraicType::U32);\nimpl_st!([] spacetimedb_primitives::IndexId, AlgebraicType::U32);\nimpl_st!([] spacetimedb_primitives::SequenceId, AlgebraicType::U32);\nimpl_st!([] spacetimedb_primitives::ConstraintId, AlgebraicType::U32);\nimpl_st!([] spacetimedb_primitives::ScheduleId, AlgebraicType::U32);\n\nimpl_st!([] spacetimedb_primitives::ColList, ts => AlgebraicType::array(spacetimedb_primitives::ColId::make_type(ts)));\nimpl_st!([] spacetimedb_primitives::ColSet, ts => AlgebraicType::array(spacetimedb_primitives::ColId::make_type(ts)));\n\nimpl_st!([] bytes::Bytes, AlgebraicType::bytes());\n\n#[cfg(feature = \"bytestring\")]\nimpl_st!([] bytestring::ByteString, AlgebraicType::String);\n\nimpl<T, E> SpacetimeType for Result<T, E>\nwhere\n    T: SpacetimeType,\n    E: SpacetimeType,\n{\n    fn make_type<S: TypespaceBuilder>(typespace: &mut S) -> AlgebraicType {\n        AlgebraicType::result(T::make_type(typespace), E::make_type(typespace))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::proptest::generate_typespace_valid_for_codegen;\n    use proptest::prelude::*;\n\n    use super::*;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(512))]\n        #[test]\n        fn is_valid_for_client_code_generation(typespace in generate_typespace_valid_for_codegen(5)) {\n            prop_assert!(typespace.is_valid_for_client_code_generation());\n        }\n    }\n\n    #[test]\n    fn is_not_valid_for_client_code_generation() {\n        let bad_inner_1 = AlgebraicType::sum([(\"red\", AlgebraicType::U8), (\"green\", AlgebraicType::U8)]);\n        let bad_inner_2 = AlgebraicType::product([(\"red\", AlgebraicType::U8), (\"green\", AlgebraicType::U8)]);\n\n        fn assert_not_valid(ty: AlgebraicType) {\n            let typespace = Typespace::new(vec![ty.clone()]);\n            assert!(!typespace.is_valid_for_client_code_generation(), \"{ty:?}\");\n        }\n        assert_not_valid(AlgebraicType::product([AlgebraicType::U8, bad_inner_1.clone()]));\n        assert_not_valid(AlgebraicType::product([AlgebraicType::U8, bad_inner_2.clone()]));\n\n        assert_not_valid(AlgebraicType::sum([AlgebraicType::U8, bad_inner_1.clone()]));\n        assert_not_valid(AlgebraicType::sum([AlgebraicType::U8, bad_inner_2.clone()]));\n\n        assert_not_valid(AlgebraicType::array(bad_inner_1.clone()));\n        assert_not_valid(AlgebraicType::array(bad_inner_2.clone()));\n\n        assert_not_valid(AlgebraicType::option(bad_inner_1.clone()));\n        assert_not_valid(AlgebraicType::option(bad_inner_2.clone()));\n\n        assert_not_valid(AlgebraicType::option(AlgebraicType::array(AlgebraicType::option(\n            bad_inner_1.clone(),\n        ))));\n\n        assert_not_valid(AlgebraicType::result(bad_inner_1.clone(), AlgebraicType::U8));\n        assert_not_valid(AlgebraicType::result(AlgebraicType::U8, bad_inner_2.clone()));\n\n        assert_not_valid(AlgebraicType::result(\n            AlgebraicType::array(AlgebraicType::result(bad_inner_1.clone(), AlgebraicType::U8)),\n            AlgebraicType::U8,\n        ));\n\n        assert_not_valid(AlgebraicType::result(\n            AlgebraicType::U8,\n            AlgebraicType::array(AlgebraicType::result(AlgebraicType::U8, bad_inner_2.clone())),\n        ));\n    }\n}\n"
  },
  {
    "path": "crates/sats/src/uuid.rs",
    "content": "use std::cell::Cell;\nuse std::fmt;\n\nuse crate::timestamp::Timestamp;\nuse crate::{de::Deserialize, impl_st, ser::Serialize, AlgebraicType, AlgebraicValue};\nuse uuid::{Builder, Uuid as UUID, Variant};\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum Version {\n    /// The \"nil\" UUID (all zeros).\n    Nil = 0u8,\n    /// Version 4: Random.\n    V4 = 4,\n    /// Version 7: Timestamp + counter + random.\n    V7 = 7,\n    /// The \"max\" (all ones) UUID.\n    Max = 0xff,\n}\n\n/// A universally unique identifier (UUID).\n///\n/// Support for UUID [`Version::Nil`], [`Version::Max`], [`Version::V4`] (random) and [`Version::V7`] (timestamp + counter + random).\n#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]\n#[sats(crate = crate)]\npub struct Uuid {\n    __uuid__: u128,\n}\n\nimpl Uuid {\n    /// The nil UUID (all zeros).\n    ///\n    /// Example:\n    ///\n    /// ```\n    /// # use spacetimedb_sats::uuid::Uuid;\n    /// let uuid = Uuid::NIL;\n    ///\n    /// assert_eq!(\n    ///     \"00000000-0000-0000-0000-000000000000\",\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub const NIL: Self = Self {\n        __uuid__: UUID::nil().as_u128(),\n    };\n\n    /// The max UUID (all ones).\n    ///\n    /// Example:\n    /// ```\n    /// # use spacetimedb_sats::uuid::Uuid;\n    /// let uuid = Uuid::MAX;\n    ///\n    /// assert_eq!(\n    ///     \"ffffffff-ffff-ffff-ffff-ffffffffffff\",\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub const MAX: Self = Self {\n        __uuid__: UUID::max().as_u128(),\n    };\n\n    /// Create a UUID `v4` from explicit random bytes.\n    ///\n    /// This method assumes the bytes are already sufficiently random, it will only\n    /// set the appropriate bits for the UUID version and variant.\n    ///\n    /// # Example\n    /// ```\n    /// # use spacetimedb_sats::uuid::Uuid;\n    /// // Use the `ReducerContext::rng()` method to generate random bytes in reducers,\n    /// // or call `ReducerContext::new_uuid_v4`\n    /// let random_bytes = [0u8; 16];\n    /// let uuid = Uuid::from_random_bytes_v4(random_bytes);\n    ///\n    /// assert_eq!(\n    ///     \"00000000-0000-4000-8000-000000000000\",\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub fn from_random_bytes_v4(counter_random_bytes: [u8; 16]) -> Self {\n        Self {\n            __uuid__: Builder::from_random_bytes(counter_random_bytes).into_uuid().as_u128(),\n        }\n    }\n\n    /// Generate a UUID `v7` using a monotonic counter from 0 to 2^31-1, a timestamp, and 4 random bytes.\n    ///\n    /// The counter will wrap around on overflow.\n    ///\n    /// The UUID `v7` is structured as follows:\n    ///```ascii\n    /// ┌───────────────────────────────────────────────┬───────────────────┐\n    /// | B0  | B1  | B2  | B3  | B4  | B5              |         B6        |\n    /// ├───────────────────────────────────────────────┼───────────────────┤\n    /// |                 unix_ts_ms                    |      version 7    |\n    /// └───────────────────────────────────────────────┴───────────────────┘\n    /// ┌──────────────┬─────────┬──────────────────┬───────────────────────┐\n    /// | B7           | B8      | B9  | B10 | B11  | B12 | B13 | B14 | B15 |\n    /// ├──────────────┼─────────┼──────────────────┼───────────────────────┤\n    /// | counter_high | variant |    counter_low   |        random         |\n    /// └──────────────┴─────────┴──────────────────┴───────────────────────┘\n    /// ```\n    /// # Panics\n    ///\n    /// Panics if the counter value is negative, or the timestamp is before the Unix epoch.\n    ///\n    /// # Example\n    ///```\n    /// use spacetimedb_sats::uuid::Uuid;\n    /// use spacetimedb_sats::timestamp::Timestamp;\n    ///\n    /// let now = Timestamp::from_micros_since_unix_epoch(1_686_000_000_000);\n    /// let counter = std::cell::Cell::new(1);\n    /// // Use the `ReducerContext::rng()` | `ProcedureContext::rng()` to generate random bytes,\n    /// // or call `ReducerContext::new_uuid_v7()` / `ProcedureContext::new_uuid_v7()`\n    /// let random_bytes = [0u8; 4];\n    /// let uuid = Uuid::from_counter_v7(&counter, now, &random_bytes).unwrap();\n    ///\n    /// assert_eq!(\n    ///     \"0000647e-5180-7000-8000-000200000000\",\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub fn from_counter_v7(counter: &Cell<u32>, now: Timestamp, random_bytes: &[u8; 4]) -> anyhow::Result<Self> {\n        // Monotonic counter value (31 bits)\n        let counter_val = counter.get();\n        counter.set(counter_val.wrapping_add(1) & 0x7FFF_FFFF);\n\n        let ts_ms = now\n            .to_duration_since_unix_epoch()\n            .expect(\"timestamp before unix epoch\")\n            .as_millis() as i64\n            & 0xFFFFFFFFFFFF;\n\n        let mut bytes = [0u8; 16];\n\n        // unix_ts_ms (48 bits)\n        bytes[0] = (ts_ms >> 40) as u8;\n        bytes[1] = (ts_ms >> 32) as u8;\n        bytes[2] = (ts_ms >> 24) as u8;\n        bytes[3] = (ts_ms >> 16) as u8;\n        bytes[4] = (ts_ms >> 8) as u8;\n        bytes[5] = ts_ms as u8;\n\n        // version & variant\n        // bytes[6] = uuid::Version::SortRand;\n        // bytes[8] = Variant::RFC4122\n\n        // Counter bits\n        bytes[7] = ((counter_val >> 23) & 0xFF) as u8;\n        bytes[9] = ((counter_val >> 15) & 0xFF) as u8;\n        bytes[10] = ((counter_val >> 7) & 0xFF) as u8;\n        bytes[11] = ((counter_val & 0x7F) << 1) as u8;\n\n        // Random bytes\n        bytes[12] |= random_bytes[0] & 0x7F;\n        bytes[13] = random_bytes[1];\n        bytes[14] = random_bytes[2];\n        bytes[15] = random_bytes[3];\n\n        let uuid = Builder::from_bytes(bytes)\n            .with_variant(Variant::RFC4122)\n            .with_version(uuid::Version::SortRand)\n            .into_uuid();\n\n        Ok(Self {\n            __uuid__: uuid.as_u128(),\n        })\n    }\n\n    /// Extract the monotonic counter from a UUIDv7.\n    #[cfg(test)]\n    fn get_counter(&self) -> i32 {\n        let bytes: [u8; 16] = self.__uuid__.to_be_bytes();\n\n        let high = bytes[7] as u32; // bits 30..23\n        let mid1 = bytes[9] as u32; // bits 22..15\n        let mid2 = bytes[10] as u32; // bits 14..7\n        let low = (bytes[11] as u32) >> 1; // bits 6..0\n\n        // reconstruct 31-bit counter\n        ((high << 23) | (mid1 << 15) | (mid2 << 7) | low) as i32\n    }\n\n    /// Parse a UUID from a string representation.\n    ///\n    /// Any of the formats generated by this module (simple, hyphenated, urn,\n    /// Microsoft GUID) are supported by this parsing function.\n    ///\n    /// # Example\n    /// ```\n    /// # use spacetimedb_sats::uuid::Uuid;\n    /// let s = \"01888d6e-5c00-7000-8000-000000000000\";\n    /// let uuid = Uuid::parse_str(s).unwrap();\n    ///\n    /// assert_eq!(\n    ///     s,\n    ///     uuid.to_string(),\n    /// );\n    /// ```\n    pub fn parse_str(s: &str) -> Result<Self, uuid::Error> {\n        Ok(Self {\n            __uuid__: UUID::parse_str(s)?.as_u128(),\n        })\n    }\n\n    /// Returns the version of the UUID.\n    ///\n    /// This represents the algorithm used to generate the value.\n    /// If the version field doesn't contain a recognized version then `None`\n    /// is returned.\n    pub fn get_version(&self) -> Option<Version> {\n        match self.to_uuid().get_version() {\n            Some(uuid::Version::Nil) => Some(Version::Nil),\n            Some(uuid::Version::Random) => Some(Version::V4),\n            Some(uuid::Version::SortRand) => Some(Version::V7),\n            Some(uuid::Version::Max) => Some(Version::Max),\n            _ => None,\n        }\n    }\n\n    #[cfg(test)]\n    fn get_variant(&self) -> Variant {\n        self.to_uuid().get_variant()\n    }\n\n    /// Convert to the `uuid` crate's `Uuid` type.\n    pub fn to_uuid(self) -> UUID {\n        UUID::from_u128(self.__uuid__)\n    }\n\n    pub fn from_u128(u: u128) -> Self {\n        Self { __uuid__: u }\n    }\n\n    pub fn as_u128(&self) -> u128 {\n        self.__uuid__\n    }\n}\n\nimpl_st!([] Uuid, AlgebraicType::uuid());\n\nimpl fmt::Display for Uuid {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.to_uuid())\n    }\n}\n\nimpl From<Uuid> for AlgebraicValue {\n    fn from(value: Uuid) -> Self {\n        AlgebraicValue::product([value.as_u128().into()])\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::timestamp::Timestamp;\n    use crate::GroundSpacetimeType;\n    use rand::RngCore;\n\n    #[test]\n    fn uuid_type_matches() {\n        assert_eq!(AlgebraicType::uuid(), Uuid::get_type());\n        assert!(Uuid::get_type().is_uuid());\n        assert!(Uuid::get_type().is_special());\n    }\n\n    #[test]\n    fn round_trip() {\n        let u1 = Uuid::NIL;\n        let s = u1.to_string();\n        let u2 = Uuid::parse_str(&s).unwrap();\n        assert_eq!(u1, u2);\n        assert_eq!(u1.as_u128(), u2.as_u128());\n        assert_eq!(u1.to_uuid(), u2.to_uuid());\n        assert_eq!(s, u2.to_string());\n    }\n\n    #[test]\n    fn to_string() {\n        for u in [\n            Uuid::NIL,\n            Uuid::from_u128(0x0102_0304_0506_0708_090a_0b0c_0d0e_0f10_u128),\n            Uuid::MAX,\n        ] {\n            let s = u.to_string();\n            let u2 = Uuid::parse_str(&s).unwrap();\n            assert_eq!(u, u2);\n        }\n    }\n\n    #[test]\n    fn version() {\n        let u_nil = Uuid::NIL;\n        assert_eq!(u_nil.get_version(), Some(Version::Nil));\n\n        let u_max = Uuid::MAX;\n        assert_eq!(u_max.get_version(), Some(Version::Max));\n\n        assert_eq!(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, u128::MAX);\n\n        let u_v4 = Uuid::from_random_bytes_v4([0u8; 16]);\n        assert_eq!(u_v4.get_version(), Some(Version::V4));\n\n        let counter = Cell::new(0);\n        let ts = Timestamp::from_micros_since_unix_epoch(1_686_000_000_000);\n        let u_v7 = Uuid::from_counter_v7(&counter, ts, &[0u8; 4]).unwrap();\n        assert_eq!(u_v7.get_version(), Some(Version::V7));\n    }\n\n    #[test]\n    fn wrap_around() {\n        // Check wraparound behavior\n        let counter = Cell::new(u32::MAX);\n        let ts = Timestamp::now();\n        let _u1 = Uuid::from_counter_v7(&counter, ts, &[0u8; 4]).unwrap();\n        assert_eq!(0, counter.get());\n    }\n\n    #[test]\n    #[should_panic(expected = \"timestamp before unix epoch\")]\n    fn negative_timestamp_panics() {\n        let counter = Cell::new(0);\n        let ts = Timestamp::from_micros_since_unix_epoch(-1);\n        let _u = Uuid::from_counter_v7(&counter, ts, &[0u8; 4]).unwrap();\n    }\n\n    #[test]\n    fn ordered() {\n        let u1 = Uuid::from_u128(1);\n        let u2 = Uuid::from_u128(2);\n        assert!(u1 < u2);\n        assert!(u2 > u1);\n        assert_eq!(u1, u1);\n        assert_ne!(u1, u2);\n        // Check we start from zero\n        let counter = Cell::new(0);\n        let ts = Timestamp::now();\n        let u_start = Uuid::from_counter_v7(&counter, ts, &[0u8; 4]).unwrap();\n        assert_eq!(u_start.get_counter(), 0);\n        // Check ordering over many UUIDs up to the max counter value\n        let total = 10_000_000;\n        let counter = Cell::new(u32::MAX - total);\n        let ts = Timestamp::now();\n        let uuids = (0..total)\n            .map(|_| {\n                let mut bytes = [0u8; 4];\n                rand::rng().fill_bytes(&mut bytes);\n                Uuid::from_counter_v7(&counter, ts, &bytes).unwrap()\n            })\n            .collect::<Vec<Uuid>>();\n\n        for (pos, pair) in uuids.windows(2).enumerate() {\n            assert_eq!(pair[0].get_version(), Some(Version::V7));\n            assert_eq!(pair[0].get_variant(), Variant::RFC4122);\n            assert!(\n                pair[0] < pair[1],\n                \"UUIDs are not ordered at {pos}: {} !< {}\",\n                pair[0],\n                pair[1]\n            );\n\n            assert!(\n                pair[0].get_counter() < pair[1].get_counter(),\n                \"UUID counters are not ordered at {pos}: {} !< {}\",\n                pair[0].get_counter(),\n                pair[1].get_counter()\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sats/tests/encoding_roundtrip.proptest-regressions",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc fb91a359dd5b9190b8b08914ac20beed16c9e16fa63c878de36503bb955fe28d # shrinks to enc = Builtin(Array { val: [Builtin(Array { val: [Builtin(U16(0)), Builtin(Bool(false))] })] })\ncc d6f0b97690c595b145bd074d9f23c5b19dc5075b5e6b9197f80945e2888501cb # shrinks to enc = Builtin(Array { val: [Builtin(Array { val: [Builtin(U8(0))] })] })\ncc d46a7105059c3fbce79ddd41e3e7d5386ced1f7fb16e7a7a3e1e07eb8744dbd8 # shrinks to enc = Builtin(Map { val: {Builtin(Bool(false)): Product(ProductValue { elements: [Builtin(Array { val: [] })] })} })\n"
  },
  {
    "path": "crates/schema/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-schema\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Schema library for SpacetimeDB\"\nrust-version.workspace = true\n\n[features]\ntest = []\n\n[dependencies]\nspacetimedb-lib = { workspace = true, features = [\"enum-map\"] }\nspacetimedb-primitives = { workspace = true, features = [\"memory-usage\"] }\nspacetimedb-sats = { workspace = true, features = [\"memory-usage\"] }\nspacetimedb-data-structures.workspace = true\nspacetimedb-memory-usage.workspace = true\nspacetimedb-sql-parser.workspace = true\n\nanyhow.workspace = true\nderive_more.workspace = true\nindexmap.workspace = true\nitertools.workspace = true\nlazy_static.workspace = true\nlean_string.workspace = true\nthiserror.workspace = true\nunicode-ident.workspace = true\nunicode-normalization.workspace = true\npetgraph.workspace = true\nserde_json.workspace = true\nsmallvec.workspace = true\nenum-as-inner.workspace = true\nenum-map.workspace = true\ninsta.workspace = true\ntermcolor.workspace = true\nconvert_case.workspace = true\n\n[dev-dependencies]\nspacetimedb-lib = { path = \"../lib\", features = [\"test\"] }\n# these are circular dependencies, but only in tests, so it's fine\nspacetimedb-testing = { path = \"../testing\" }\n\nproptest.workspace = true\nserial_test.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/schema/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/schema/proptest-regressions/type_for_generate.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 50cf163ac81228385b27f96ba1801355e39bc722a937a0cf6ec0d4b27d23ef14 # shrinks to t = Typespace { types: [Bool, Bool, Bool, Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Bool }] }), Bool] }\n"
  },
  {
    "path": "crates/schema/src/auto_migrate/formatter.rs",
    "content": "//! This module provides enhanced functionality for rendering automatic migration plans to strings.\n\nuse std::io;\n\nuse super::{AutoMigratePlan, ModuleDefLookup, TableDef};\nuse crate::{\n    auto_migrate::AutoMigrateStep,\n    def::{ConstraintData, FunctionKind, ModuleDef, ScheduleDef, ViewDef},\n    identifier::Identifier,\n};\nuse itertools::Itertools;\nuse spacetimedb_lib::db::raw_def::v9::{RawRowLevelSecurityDefV9, TableAccess, TableType};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicValue, WithTypespace};\nuse thiserror::Error;\n\npub fn format_plan<F: MigrationFormatter>(f: &mut F, plan: &AutoMigratePlan) -> Result<(), FormattingErrors> {\n    f.format_header()?;\n\n    for step in &plan.steps {\n        format_step(f, step, plan)?;\n    }\n\n    Ok(())\n}\n\nfn format_step<F: MigrationFormatter>(\n    f: &mut F,\n    step: &AutoMigrateStep,\n    plan: &super::AutoMigratePlan,\n) -> Result<(), FormattingErrors> {\n    match step {\n        AutoMigrateStep::AddView(view) => {\n            let view_info = extract_view_info(*view, plan.new)?;\n            f.format_view(&view_info, Action::Created)\n        }\n        AutoMigrateStep::RemoveView(view) => {\n            let view_info = extract_view_info(*view, plan.old)?;\n            f.format_view(&view_info, Action::Removed)\n        }\n        // This means the body of the view may have been updated.\n        // So we must recompute it and send any updates to clients.\n        // No need to include this step in the formatted plan.\n        AutoMigrateStep::UpdateView(_) => Ok(()),\n        AutoMigrateStep::AddTable(t) => {\n            let table_info = extract_table_info(*t, plan)?;\n            f.format_add_table(&table_info)\n        }\n        AutoMigrateStep::AddIndex(index) => {\n            let index_info = extract_index_info(*index, plan.new)?;\n            f.format_index(&index_info, Action::Created)\n        }\n        AutoMigrateStep::RemoveIndex(index) => {\n            let index_info = extract_index_info(*index, plan.old)?;\n            f.format_index(&index_info, Action::Removed)\n        }\n        AutoMigrateStep::RemoveConstraint(constraint) => {\n            let constraint_info = extract_constraint_info(*constraint, plan.old)?;\n            f.format_constraint(&constraint_info, Action::Removed)\n        }\n        AutoMigrateStep::AddSequence(sequence) => {\n            let sequence_info = extract_sequence_info(*sequence, plan.new)?;\n            f.format_sequence(&sequence_info, Action::Created)\n        }\n        AutoMigrateStep::RemoveSequence(sequence) => {\n            let sequence_info = extract_sequence_info(*sequence, plan.old)?;\n            f.format_sequence(&sequence_info, Action::Removed)\n        }\n        AutoMigrateStep::ChangeAccess(table) => {\n            let access_info = extract_access_change_info(*table, plan)?;\n            f.format_change_access(&access_info)\n        }\n        AutoMigrateStep::AddSchedule(schedule) => {\n            let schedule_info = extract_schedule_info(*schedule, plan.new)?;\n            f.format_schedule(&schedule_info, Action::Created)\n        }\n        AutoMigrateStep::RemoveSchedule(schedule) => {\n            let schedule_info = extract_schedule_info(*schedule, plan.old)?;\n            f.format_schedule(&schedule_info, Action::Removed)\n        }\n        AutoMigrateStep::AddRowLevelSecurity(rls) => {\n            if let Some(rls_info) = extract_rls_info(*rls, plan)? {\n                f.format_rls(&rls_info, Action::Created)?;\n            }\n            Ok(())\n        }\n        AutoMigrateStep::RemoveRowLevelSecurity(rls) => {\n            if let Some(rls_info) = extract_rls_info(*rls, plan)? {\n                f.format_rls(&rls_info, Action::Removed)?;\n            }\n            Ok(())\n        }\n        AutoMigrateStep::ChangeColumns(table) => {\n            let column_changes = extract_column_changes(*table, plan)?;\n            f.format_change_columns(&column_changes)\n        }\n        AutoMigrateStep::AddColumns(table) => {\n            let new_columns = extract_new_columns(*table, plan)?;\n            f.format_add_columns(&new_columns)\n        }\n        AutoMigrateStep::DisconnectAllUsers => f.format_disconnect_warning(),\n    }?;\n\n    Ok(())\n}\n\n#[derive(Error, Debug)]\npub enum FormattingErrors {\n    #[error(\"Table not found: {table}\")]\n    TableNotFound { table: Box<str> },\n    #[error(\"View not found: {view}\")]\n    ViewNotFound { view: Box<str> },\n    #[error(\"Index not found\")]\n    IndexNotFound,\n    #[error(\"Constraint not found\")]\n    ConstraintNotFound,\n    #[error(\"Sequence not found\")]\n    SequenceNotFound,\n    #[error(\"Schedule not found\")]\n    ScheduleNotFound,\n    #[error(\"Type resolution failed\")]\n    TypeResolution,\n    #[error(\"Column not found\")]\n    ColumnNotFound,\n    #[error(\"IO error: {0}\")]\n    IO(#[from] std::io::Error),\n}\n\n/// Action types for database operations\n#[derive(Debug, Clone, PartialEq)]\npub enum Action {\n    Created,\n    Removed,\n    Changed,\n}\n\n/// Trait for formatting migration steps\n/// This trait defines methods for formatting various components of a migration plan.\n/// It allows for different implementations, such as ANSI formatting or plain text formatting.\npub trait MigrationFormatter {\n    fn format_header(&mut self) -> io::Result<()>;\n    fn format_add_table(&mut self, table_info: &TableInfo) -> io::Result<()>;\n    fn format_view(&mut self, view_info: &ViewInfo, action: Action) -> io::Result<()>;\n    fn format_index(&mut self, index_info: &IndexInfo, action: Action) -> io::Result<()>;\n    fn format_constraint(&mut self, constraint_info: &ConstraintInfo, action: Action) -> io::Result<()>;\n    fn format_sequence(&mut self, sequence_info: &SequenceInfo, action: Action) -> io::Result<()>;\n    fn format_change_access(&mut self, access_info: &AccessChangeInfo) -> io::Result<()>;\n    fn format_schedule(&mut self, schedule_info: &ScheduleInfo, action: Action) -> io::Result<()>;\n    fn format_rls(&mut self, rls_info: &RlsInfo, action: Action) -> io::Result<()>;\n    fn format_change_columns(&mut self, column_changes: &ColumnChanges) -> io::Result<()>;\n    fn format_add_columns(&mut self, new_columns: &NewColumns) -> io::Result<()>;\n    fn format_disconnect_warning(&mut self) -> io::Result<()>;\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct TableInfo {\n    pub name: RawIdentifier,\n    pub is_system: bool,\n    pub access: TableAccess,\n    pub columns: Vec<ColumnInfo>,\n    pub constraints: Vec<ConstraintInfo>,\n    pub indexes: Vec<IndexInfo>,\n    pub sequences: Vec<SequenceInfo>,\n    pub schedule: Option<ScheduleInfo>,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ViewInfo {\n    pub name: RawIdentifier,\n    pub params: Vec<ViewParamInfo>,\n    pub columns: Vec<ViewColumnInfo>,\n    pub is_anonymous: bool,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ViewColumnInfo {\n    pub name: Identifier,\n    pub type_name: AlgebraicType,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ViewParamInfo {\n    pub name: Identifier,\n    pub type_name: AlgebraicType,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ColumnInfo {\n    pub name: Identifier,\n    pub type_name: AlgebraicType,\n    pub default_value: Option<AlgebraicValue>,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ConstraintInfo {\n    pub name: RawIdentifier,\n    pub columns: Vec<Identifier>,\n    pub table_name: Identifier,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct IndexInfo {\n    pub name: RawIdentifier,\n    pub columns: Vec<Identifier>,\n    pub table_name: RawIdentifier,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct SequenceInfo {\n    pub name: RawIdentifier,\n    pub column_name: Identifier,\n    pub table_name: Identifier,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct AccessChangeInfo {\n    pub table_name: Identifier,\n    pub new_access: TableAccess,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ScheduleInfo {\n    pub table_name: Identifier,\n    pub function_name: Identifier,\n    pub function_kind: FunctionKind,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct RlsInfo {\n    pub policy: String,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct ColumnChanges {\n    pub table_name: Identifier,\n    pub changes: Vec<ColumnChange>,\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub enum ColumnChange {\n    Renamed {\n        old_name: Identifier,\n        new_name: Identifier,\n    },\n    TypeChanged {\n        name: Identifier,\n        old_type: AlgebraicType,\n        new_type: AlgebraicType,\n    },\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct NewColumns {\n    pub table_name: Identifier,\n    pub columns: Vec<ColumnInfo>,\n}\n\n// Data extraction functions (these replace the original print functions' data gathering logic)\nfn extract_table_info(\n    table: <TableDef as crate::def::ModuleDefLookup>::Key<'_>,\n    plan: &super::AutoMigratePlan,\n) -> Result<TableInfo, FormattingErrors> {\n    let table_def = plan.new.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n\n    let columns = table_def\n        .columns\n        .iter()\n        .map(|column| {\n            let type_name = WithTypespace::new(plan.new.typespace(), &column.ty)\n                .resolve_refs()\n                .map_err(|_| FormattingErrors::TypeResolution)?;\n            Ok(ColumnInfo {\n                name: column.name.clone(),\n                type_name,\n                default_value: column.default_value.clone(),\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    let constraints = table_def\n        .constraints\n        .values()\n        .sorted_by_key(|c| c.name.clone())\n        .map(|constraint| {\n            let ConstraintData::Unique(unique) = &constraint.data;\n            Ok(ConstraintInfo {\n                name: constraint.name.clone(),\n                columns: unique\n                    .columns\n                    .iter()\n                    .map(|col_id| {\n                        let column = table_def.get_column(col_id).ok_or(FormattingErrors::ColumnNotFound)?;\n                        Ok(column.name.clone())\n                    })\n                    .collect::<Result<Vec<_>, FormattingErrors>>()?,\n                table_name: table_def.name.clone(),\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    let indexes = table_def\n        .indexes\n        .values()\n        .sorted_by_key(|c| c.name.clone())\n        .map(|index| {\n            let columns = index\n                .algorithm\n                .columns()\n                .iter()\n                .map(|col_id| {\n                    let column = table_def.get_column(col_id).ok_or(FormattingErrors::ColumnNotFound)?;\n                    Ok(column.name.clone())\n                })\n                .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n            Ok(IndexInfo {\n                name: index.name.clone(),\n                columns,\n                table_name: table_def.name.clone().into(),\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    let sequences = table_def\n        .sequences\n        .values()\n        .sorted_by_key(|c| c.name.clone())\n        .map(|sequence| {\n            let column = table_def\n                .get_column(sequence.column)\n                .ok_or(FormattingErrors::ColumnNotFound)?;\n            Ok(SequenceInfo {\n                name: sequence.name.clone(),\n                column_name: column.name.clone(),\n                table_name: table_def.name.clone(),\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    let schedule = table_def.schedule.as_ref().map(|schedule| ScheduleInfo {\n        table_name: table_def.name.clone(),\n        function_name: schedule.function_name.clone(),\n        function_kind: schedule.function_kind,\n    });\n\n    Ok(TableInfo {\n        name: table_def.name.clone().into(),\n        is_system: table_def.table_type == TableType::System,\n        access: table_def.table_access,\n        columns,\n        constraints,\n        indexes,\n        sequences,\n        schedule,\n    })\n}\n\nfn extract_view_info(\n    view: <ViewDef as crate::def::ModuleDefLookup>::Key<'_>,\n    module_def: &ModuleDef,\n) -> Result<ViewInfo, FormattingErrors> {\n    let view_def = module_def.view(view).ok_or_else(|| FormattingErrors::ViewNotFound {\n        view: view.to_string().into(),\n    })?;\n\n    let name = view_def.name.clone().into();\n    let is_anonymous = view_def.is_anonymous;\n\n    let params = view_def\n        .param_columns\n        .iter()\n        .map(|column| {\n            let type_name = WithTypespace::new(module_def.typespace(), &column.ty)\n                .resolve_refs()\n                .map_err(|_| FormattingErrors::TypeResolution)?;\n            Ok(ViewParamInfo {\n                name: column.name.clone(),\n                type_name,\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    let columns = view_def\n        .return_columns\n        .iter()\n        .map(|column| {\n            let type_name = WithTypespace::new(module_def.typespace(), &column.ty)\n                .resolve_refs()\n                .map_err(|_| FormattingErrors::TypeResolution)?;\n            Ok(ViewColumnInfo {\n                name: column.name.clone(),\n                type_name,\n            })\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    Ok(ViewInfo {\n        name,\n        params,\n        columns,\n        is_anonymous,\n    })\n}\n\nfn extract_index_info(\n    index: <crate::def::IndexDef as ModuleDefLookup>::Key<'_>,\n    module_def: &ModuleDef,\n) -> Result<IndexInfo, FormattingErrors> {\n    let table_def = module_def\n        .stored_in_table_def(index)\n        .ok_or(FormattingErrors::IndexNotFound)?;\n    let index_def = table_def.indexes.get(index).ok_or(FormattingErrors::IndexNotFound)?;\n\n    let columns = index_def\n        .algorithm\n        .columns()\n        .iter()\n        .map(|col_id| {\n            let column = table_def.get_column(col_id).ok_or(FormattingErrors::ColumnNotFound)?;\n            Ok(column.name.clone())\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    Ok(IndexInfo {\n        name: index_def.name.clone(),\n        columns,\n        table_name: table_def.name.clone().into(),\n    })\n}\n\nfn extract_constraint_info(\n    constraint: <crate::def::ConstraintDef as ModuleDefLookup>::Key<'_>,\n    module_def: &ModuleDef,\n) -> Result<ConstraintInfo, FormattingErrors> {\n    let table_def = module_def\n        .stored_in_table_def(constraint)\n        .ok_or(FormattingErrors::ConstraintNotFound)?;\n    let constraint_def = table_def\n        .constraints\n        .get(constraint)\n        .ok_or(FormattingErrors::ConstraintNotFound)?;\n\n    let ConstraintData::Unique(unique_constraint_data) = &constraint_def.data;\n    let columns = unique_constraint_data\n        .columns\n        .iter()\n        .map(|col_id| {\n            let column = table_def.get_column(col_id).ok_or(FormattingErrors::ColumnNotFound)?;\n            Ok(column.name.clone())\n        })\n        .collect::<Result<Vec<_>, FormattingErrors>>()?;\n\n    Ok(ConstraintInfo {\n        name: constraint_def.name.clone(),\n        columns,\n        table_name: table_def.name.clone(),\n    })\n}\n\nfn extract_sequence_info(\n    sequence: <crate::def::SequenceDef as ModuleDefLookup>::Key<'_>,\n    module_def: &ModuleDef,\n) -> Result<SequenceInfo, FormattingErrors> {\n    let table_def = module_def\n        .stored_in_table_def(sequence)\n        .ok_or(FormattingErrors::SequenceNotFound)?;\n    let sequence_def = table_def\n        .sequences\n        .get(sequence)\n        .ok_or(FormattingErrors::SequenceNotFound)?;\n\n    let column = table_def\n        .get_column(sequence_def.column)\n        .ok_or(FormattingErrors::ColumnNotFound)?;\n\n    Ok(SequenceInfo {\n        name: sequence_def.name.clone(),\n        column_name: column.name.clone(),\n        table_name: table_def.name.clone(),\n    })\n}\n\nfn extract_access_change_info(\n    table: <TableDef as ModuleDefLookup>::Key<'_>,\n    plan: &super::AutoMigratePlan,\n) -> Result<AccessChangeInfo, FormattingErrors> {\n    let table_def = plan.new.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n\n    Ok(AccessChangeInfo {\n        table_name: table_def.name.clone(),\n        new_access: table_def.table_access,\n    })\n}\n\nfn extract_schedule_info(\n    schedule_table: <ScheduleDef as ModuleDefLookup>::Key<'_>,\n    module_def: &ModuleDef,\n) -> Result<ScheduleInfo, FormattingErrors> {\n    let schedule_def: &ScheduleDef = module_def\n        .lookup(schedule_table)\n        .ok_or(FormattingErrors::ScheduleNotFound)?;\n\n    Ok(ScheduleInfo {\n        table_name: schedule_def.name.clone(),\n        function_name: schedule_def.function_name.clone(),\n        function_kind: schedule_def.function_kind,\n    })\n}\n\nfn extract_rls_info(\n    rls: <RawRowLevelSecurityDefV9 as ModuleDefLookup>::Key<'_>,\n    plan: &super::AutoMigratePlan,\n) -> Result<Option<RlsInfo>, FormattingErrors> {\n    // Skip if policy unchanged (implementation detail workaround)\n    if plan.old.lookup::<RawRowLevelSecurityDefV9>(rls) == plan.new.lookup::<RawRowLevelSecurityDefV9>(rls) {\n        return Ok(None);\n    }\n\n    Ok(Some(RlsInfo {\n        policy: rls.to_string(),\n    }))\n}\n\nfn extract_column_changes(\n    table: <TableDef as ModuleDefLookup>::Key<'_>,\n    plan: &super::AutoMigratePlan,\n) -> Result<ColumnChanges, FormattingErrors> {\n    let old_table = plan.old.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n    let new_table = plan.new.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n\n    let mut changes = Vec::new();\n\n    // Find modified columns\n    for new_col in &new_table.columns {\n        if let Some(old_col) = old_table.columns.iter().find(|c| c.col_id == new_col.col_id) {\n            if old_col.name != new_col.name {\n                changes.push(ColumnChange::Renamed {\n                    old_name: old_col.name.clone(),\n                    new_name: new_col.name.clone(),\n                });\n            }\n            if old_col.ty != new_col.ty {\n                let old_type = WithTypespace::new(plan.old.typespace(), &old_col.ty)\n                    .resolve_refs()\n                    .map_err(|_| FormattingErrors::TypeResolution)?;\n                let new_type = WithTypespace::new(plan.new.typespace(), &new_col.ty)\n                    .resolve_refs()\n                    .map_err(|_| FormattingErrors::TypeResolution)?;\n                changes.push(ColumnChange::TypeChanged {\n                    name: new_col.name.clone(),\n                    old_type,\n                    new_type,\n                });\n            }\n        }\n    }\n\n    Ok(ColumnChanges {\n        table_name: new_table.name.clone(),\n        changes,\n    })\n}\n\nfn extract_new_columns(\n    table: <TableDef as ModuleDefLookup>::Key<'_>,\n    plan: &super::AutoMigratePlan,\n) -> Result<NewColumns, FormattingErrors> {\n    let table_def = plan.new.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n    let old_table_def = plan.old.table(table).ok_or_else(|| FormattingErrors::TableNotFound {\n        table: table.to_string().into(),\n    })?;\n\n    let mut new_columns = Vec::new();\n    for column in &table_def.columns {\n        if !old_table_def.columns.iter().any(|c| c.col_id == column.col_id) {\n            let type_name = WithTypespace::new(plan.new.typespace(), &column.ty)\n                .resolve_refs()\n                .map_err(|_| FormattingErrors::TypeResolution)?;\n            new_columns.push(ColumnInfo {\n                name: column.name.clone(),\n                type_name,\n                default_value: column.default_value.clone(),\n            });\n        }\n    }\n\n    Ok(NewColumns {\n        table_name: table_def.name.clone(),\n        columns: new_columns,\n    })\n}\n"
  },
  {
    "path": "crates/schema/src/auto_migrate/termcolor_formatter.rs",
    "content": "use std::io::Write;\nuse std::{fmt, io};\n\nuse spacetimedb_lib::{db::raw_def::v9::TableAccess, AlgebraicType};\nuse spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;\nuse termcolor::{Buffer, Color, ColorChoice, ColorSpec, WriteColor};\n\nuse crate::auto_migrate::formatter::ViewInfo;\n\nuse super::formatter::{\n    AccessChangeInfo, Action, ColumnChange, ColumnChanges, ConstraintInfo, IndexInfo, MigrationFormatter, NewColumns,\n    RlsInfo, ScheduleInfo, SequenceInfo, TableInfo,\n};\n\n/// Color scheme for consistent formatting\n#[derive(Debug, Clone)]\npub struct ColorScheme {\n    pub created: Color,\n    pub removed: Color,\n    pub changed: Color,\n    pub header: Color,\n    pub table_name: Color,\n    pub column_type: Color,\n    pub section_header: Color,\n    pub access: Color,\n    pub warning: Color,\n}\n\nimpl Default for ColorScheme {\n    fn default() -> Self {\n        Self {\n            created: Color::Green,\n            removed: Color::Red,\n            changed: Color::Yellow,\n            header: Color::Blue,\n            table_name: Color::Cyan,\n            column_type: Color::Magenta,\n            section_header: Color::Blue,\n            access: Color::Green,\n            warning: Color::Red,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct TermColorFormatter {\n    buffer: Buffer,\n    colors: ColorScheme,\n    indent_level: usize,\n}\n\nimpl fmt::Display for TermColorFormatter {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let buffer_content = std::str::from_utf8(self.buffer.as_slice()).map_err(|_| fmt::Error)?;\n        write!(f, \"{buffer_content}\")\n    }\n}\n\nimpl TermColorFormatter {\n    pub fn new(colors: ColorScheme, choice: ColorChoice) -> Self {\n        Self {\n            buffer: if choice == ColorChoice::Never {\n                Buffer::no_color()\n            } else {\n                Buffer::ansi()\n            },\n            colors,\n            indent_level: 0,\n        }\n    }\n\n    fn write_indent(&mut self) -> io::Result<()> {\n        let indent = \"    \".repeat(self.indent_level);\n        self.buffer.write_all(indent.as_bytes())\n    }\n\n    fn write_line(&mut self, text: impl AsRef<str>) -> io::Result<()> {\n        self.write_indent()?;\n        self.buffer.write_all(text.as_ref().as_bytes())?;\n        self.buffer.write_all(b\"\\n\")\n    }\n\n    fn write_colored(&mut self, text: &str, color: Option<Color>, bold: bool) -> io::Result<()> {\n        let mut spec = ColorSpec::new();\n        if let Some(c) = color {\n            spec.set_fg(Some(c));\n        }\n        if bold {\n            spec.set_bold(true);\n        }\n        self.buffer.set_color(&spec)?;\n        self.buffer.write_all(text.as_bytes())?;\n        self.buffer.reset()?;\n        Ok(())\n    }\n\n    fn write_colored_line(&mut self, text: &str, color: Option<Color>, bold: bool) -> io::Result<()> {\n        self.write_indent()?;\n        self.write_colored(text, color, bold)?;\n        self.buffer.write_all(b\"\\n\")\n    }\n\n    fn write_with_background(&mut self, text: &str, bg: Color, bold: bool) -> io::Result<()> {\n        let mut spec = ColorSpec::new();\n        spec.set_bg(Some(bg));\n        if bold {\n            spec.set_bold(true);\n        }\n        self.buffer.set_color(&spec)?;\n        self.buffer.write_all(text.as_bytes())?;\n        self.buffer.reset()?;\n        Ok(())\n    }\n\n    fn write_bullet(&mut self, text: &str) -> io::Result<()> {\n        self.write_line(format!(\"• {text}\"))\n    }\n\n    fn write_action_prefix(&mut self, action: &Action) -> io::Result<()> {\n        self.write_indent()?;\n        self.buffer.write_all(\"▸ \".to_string().as_bytes())?;\n        action.write_with_color(&mut self.buffer, &self.colors)\n    }\n\n    fn indent(&mut self) {\n        self.indent_level += 1;\n    }\n\n    fn dedent(&mut self) {\n        if self.indent_level > 0 {\n            self.indent_level -= 1;\n        }\n    }\n\n    fn format_type_name(&self, ty: &AlgebraicType) -> String {\n        fmt_algebraic_type(ty).to_string()\n    }\n\n    fn write_type_name(&mut self, ty: &AlgebraicType) -> io::Result<()> {\n        let s = self.format_type_name(ty);\n        self.write_colored(&s, Some(self.colors.column_type), false)\n    }\n\n    fn format_access(&self, access: TableAccess) -> &'static str {\n        match access {\n            TableAccess::Private => \"private\",\n            TableAccess::Public => \"public\",\n        }\n    }\n\n    fn write_access(&mut self, access: TableAccess) -> io::Result<()> {\n        let s = self.format_access(access);\n        self.write_colored(s, Some(self.colors.access), false)\n    }\n}\n\nimpl MigrationFormatter for TermColorFormatter {\n    fn format_header(&mut self) -> io::Result<()> {\n        let line = \"━\".repeat(60);\n        self.write_line(&line)?;\n        self.write_colored_line(\"Database Migration Plan\", Some(self.colors.header), true)?;\n        self.write_line(&line)?;\n        self.write_line(\"\")\n    }\n\n    fn format_add_table(&mut self, table: &TableInfo) -> io::Result<()> {\n        // Table header\n        self.write_indent()?;\n        self.buffer.write_all(\"▸ \".to_string().as_bytes())?;\n        Action::Created.write_with_color(&mut self.buffer, &self.colors)?;\n        let kind = if table.is_system { \"system\" } else { \"user\" };\n        self.buffer.write_all(format!(\" {kind} table: \").as_bytes())?;\n        self.write_colored(&table.name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\" (\")?;\n        self.write_access(table.access)?;\n        self.buffer.write_all(b\")\\n\")?;\n\n        self.indent();\n\n        if !table.columns.is_empty() {\n            self.write_colored_line(\"Columns:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for col in &table.columns {\n                self.write_indent()?;\n                self.buffer.write_all(format!(\"• {}: \", col.name).as_bytes())?;\n                self.write_type_name(&col.type_name)?;\n                self.buffer.write_all(b\"\\n\")?;\n            }\n            self.dedent();\n        }\n\n        if !table.constraints.is_empty() {\n            self.write_colored_line(\"Unique constraints:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for c in &table.constraints {\n                let cols = c.columns.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(\", \");\n                self.write_bullet(&format!(\"{} on [{}]\", c.name, cols))?;\n            }\n            self.dedent();\n        }\n\n        if !table.indexes.is_empty() {\n            self.write_colored_line(\"Indexes:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for i in &table.indexes {\n                let cols = i.columns.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(\", \");\n                self.write_bullet(&format!(\"{} on [{}]\", i.name, cols))?;\n            }\n            self.dedent();\n        }\n\n        if !table.sequences.is_empty() {\n            self.write_colored_line(\"Auto-increment constraints:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for s in &table.sequences {\n                self.write_bullet(&format!(\"{} on {}\", s.name, s.column_name))?;\n            }\n            self.dedent();\n        }\n\n        if let Some(s) = &table.schedule {\n            self.write_colored_line(\"Schedule:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            self.write_bullet(&format!(\"Calls {}: {}\", s.function_kind, s.function_name))?;\n            self.dedent();\n        }\n\n        self.dedent();\n        self.write_line(\"\")\n    }\n\n    fn format_view(&mut self, view: &ViewInfo, action: Action) -> io::Result<()> {\n        self.write_indent()?;\n        self.buffer.write_all(\"▸ \".to_string().as_bytes())?;\n        self.write_action_prefix(&action)?;\n        self.buffer.write_all(if view.is_anonymous {\n            b\" anonymous view: \"\n        } else {\n            b\" view: \"\n        })?;\n        self.write_colored(&view.name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")?;\n\n        self.indent();\n\n        if !view.params.is_empty() {\n            self.write_colored_line(\"Parameters:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for col in &view.params {\n                self.write_indent()?;\n                self.buffer.write_all(format!(\"• {}: \", col.name).as_bytes())?;\n                self.write_type_name(&col.type_name)?;\n                self.buffer.write_all(b\"\\n\")?;\n            }\n            self.dedent();\n        }\n\n        if !view.columns.is_empty() {\n            self.write_colored_line(\"Columns:\", Some(self.colors.section_header), true)?;\n            self.indent();\n            for col in &view.columns {\n                self.write_indent()?;\n                self.buffer.write_all(format!(\"• {}: \", col.name).as_bytes())?;\n                self.write_type_name(&col.type_name)?;\n                self.buffer.write_all(b\"\\n\")?;\n            }\n            self.dedent();\n        }\n\n        self.dedent();\n        self.write_line(\"\")\n    }\n\n    fn format_constraint(&mut self, c: &ConstraintInfo, action: Action) -> io::Result<()> {\n        self.write_action_prefix(&action)?;\n        let cols = c.columns.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(\", \");\n        self.buffer\n            .write_all(format!(\" unique constraint {} on [{}] of table \", c.name, cols).as_bytes())?;\n        self.write_colored(&c.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")\n    }\n\n    fn format_index(&mut self, i: &IndexInfo, action: Action) -> io::Result<()> {\n        self.write_action_prefix(&action)?;\n        let cols = i.columns.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(\", \");\n        self.buffer\n            .write_all(format!(\" index {} on [{}] of table \", i.name, cols).as_bytes())?;\n        self.write_colored(&i.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")\n    }\n\n    fn format_sequence(&mut self, s: &SequenceInfo, action: Action) -> io::Result<()> {\n        self.write_action_prefix(&action)?;\n        self.buffer.write_all(\n            format!(\n                \" auto-increment constraint {} on column {} of table \",\n                s.name, s.column_name\n            )\n            .as_bytes(),\n        )?;\n        self.write_colored(&s.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")\n    }\n\n    fn format_change_access(&mut self, a: &AccessChangeInfo) -> io::Result<()> {\n        let direction = match a.new_access {\n            TableAccess::Private => \"public → private\",\n            TableAccess::Public => \"private → public\",\n        };\n        self.write_action_prefix(&Action::Changed)?;\n        self.buffer.write_all(b\" access for table \")?;\n        self.write_colored(&a.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\" (\")?;\n        self.write_colored(direction, Some(self.colors.access), false)?;\n        self.buffer.write_all(b\")\\n\")\n    }\n\n    fn format_schedule(&mut self, s: &ScheduleInfo, action: Action) -> io::Result<()> {\n        self.write_action_prefix(&action)?;\n        self.buffer.write_all(b\" schedule for table \")?;\n        self.write_colored(&s.table_name, Some(self.colors.table_name), true)?;\n        self.buffer\n            .write_all(format!(\" calling {} {}\\n\", s.function_kind, s.function_name).as_bytes())\n    }\n\n    fn format_rls(&mut self, r: &RlsInfo, action: Action) -> io::Result<()> {\n        self.write_action_prefix(&action)?;\n        self.buffer.write_all(b\" row level security policy:\\n\")?;\n        self.indent();\n        self.write_indent()?;\n        self.buffer.write_all(b\"`\")?;\n        self.write_colored(&r.policy, Some(self.colors.section_header), false)?;\n        self.buffer.write_all(b\"`\\n\")?;\n        self.dedent();\n        Ok(())\n    }\n\n    fn format_change_columns(&mut self, cs: &ColumnChanges) -> io::Result<()> {\n        self.write_action_prefix(&Action::Changed)?;\n        self.buffer.write_all(b\" columns for table \")?;\n        self.write_colored(&cs.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")?;\n\n        self.indent();\n        for ch in &cs.changes {\n            self.write_indent()?;\n            match ch {\n                ColumnChange::Renamed { old_name, new_name } => {\n                    self.buffer\n                        .write_all(format!(\"~ Renamed: {old_name} → {new_name}\\n\").as_bytes())?;\n                }\n                ColumnChange::TypeChanged {\n                    name,\n                    old_type,\n                    new_type,\n                } => {\n                    self.buffer.write_all(format!(\"~ Modified: {name} (\").as_bytes())?;\n                    self.write_type_name(old_type)?;\n                    self.buffer.write_all(\" → \".to_string().as_bytes())?;\n                    self.write_type_name(new_type)?;\n                    self.buffer.write_all(b\")\\n\")?;\n                }\n            }\n        }\n        self.dedent();\n        Ok(())\n    }\n\n    fn format_add_columns(&mut self, nc: &NewColumns) -> io::Result<()> {\n        let plural = if nc.columns.len() > 1 { \"s\" } else { \"\" };\n        self.write_action_prefix(&Action::Created)?;\n        self.buffer.write_all(format!(\" column{plural} in table \").as_bytes())?;\n        self.write_colored(&nc.table_name, Some(self.colors.table_name), true)?;\n        self.buffer.write_all(b\"\\n\")?;\n\n        self.indent();\n        for col in &nc.columns {\n            let default = col\n                .default_value\n                .as_ref()\n                .map(|v| format!(\" (default: {v:#?})\"))\n                .unwrap_or_default();\n            self.write_indent()?;\n            self.buffer.write_all(format!(\"+ {}: \", col.name).as_bytes())?;\n            self.write_type_name(&col.type_name)?;\n            self.buffer.write_all(format!(\"{default}\\n\").as_bytes())?;\n        }\n        self.dedent();\n        Ok(())\n    }\n\n    fn format_disconnect_warning(&mut self) -> io::Result<()> {\n        self.write_indent()?;\n        self.write_with_background(\n            \"!!! Warning: All clients will be disconnected due to breaking schema changes\",\n            self.colors.warning,\n            true,\n        )?;\n        self.buffer.write_all(b\"\\n\")\n    }\n}\n\ntrait ActionColorExt {\n    fn write_with_color(&self, buffer: &mut Buffer, colors: &ColorScheme) -> io::Result<()>;\n}\n\nimpl ActionColorExt for Action {\n    fn write_with_color(&self, buffer: &mut Buffer, colors: &ColorScheme) -> io::Result<()> {\n        let (text, color) = match self {\n            Action::Created => (\"Created\", colors.created),\n            Action::Removed => (\"Removed\", colors.removed),\n            Action::Changed => (\"Changed\", colors.changed),\n        };\n        let mut spec = ColorSpec::new();\n        spec.set_fg(Some(color)).set_bold(true);\n        buffer.set_color(&spec)?;\n        buffer.write_all(text.as_bytes())?;\n        buffer.reset()?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/auto_migrate.rs",
    "content": "use core::{cmp::Ordering, ops::BitOr};\n\nuse crate::{def::*, error::PrettyAlgebraicType, identifier::Identifier};\nuse formatter::format_plan;\nuse spacetimedb_data_structures::{\n    error_stream::{CollectAllErrors, CombineErrors, ErrorStream},\n    map::{HashCollectionExt as _, HashSet},\n};\nuse spacetimedb_lib::{\n    db::raw_def::v9::{RawRowLevelSecurityDefV9, TableType},\n    hash_bytes, Identity,\n};\nuse spacetimedb_sats::{\n    layout::{HasLayout, SumTypeLayout},\n    raw_identifier::RawIdentifier,\n    AlgebraicType, WithTypespace,\n};\nuse termcolor_formatter::{ColorScheme, TermColorFormatter};\nuse thiserror::Error;\nmod formatter;\nmod termcolor_formatter;\n\npub type Result<T> = std::result::Result<T, ErrorStream<AutoMigrateError>>;\n\n/// A plan for a migration.\n#[derive(Debug)]\npub enum MigratePlan<'def> {\n    Manual(ManualMigratePlan<'def>),\n    Auto(AutoMigratePlan<'def>),\n}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\npub enum PrettyPrintStyle {\n    AnsiColor,\n    NoColor,\n}\n\nimpl<'def> MigratePlan<'def> {\n    /// Get the old `ModuleDef` for this migration plan.\n    pub fn old_def(&self) -> &'def ModuleDef {\n        match self {\n            MigratePlan::Manual(plan) => plan.old,\n            MigratePlan::Auto(plan) => plan.old,\n        }\n    }\n\n    /// Get the new `ModuleDef` for this migration plan.\n    pub fn new_def(&self) -> &'def ModuleDef {\n        match self {\n            MigratePlan::Manual(plan) => plan.new,\n            MigratePlan::Auto(plan) => plan.new,\n        }\n    }\n\n    pub fn breaks_client(&self) -> bool {\n        match self {\n            //TODO: fix it when support for manual migration plans is added.\n            MigratePlan::Manual(_) => true,\n            MigratePlan::Auto(plan) => plan\n                .steps\n                .iter()\n                .any(|step| matches!(step, AutoMigrateStep::DisconnectAllUsers)),\n        }\n    }\n\n    pub fn pretty_print(&self, style: PrettyPrintStyle) -> anyhow::Result<String> {\n        use PrettyPrintStyle::*;\n        match self {\n            MigratePlan::Manual(_) => {\n                anyhow::bail!(\"Manual migration plans are not yet supported for pretty printing.\")\n            }\n\n            MigratePlan::Auto(plan) => match style {\n                NoColor => {\n                    let mut fmt = TermColorFormatter::new(ColorScheme::default(), termcolor::ColorChoice::Never);\n                    format_plan(&mut fmt, plan).map(|_| fmt.to_string())\n                }\n                AnsiColor => {\n                    let mut fmt = TermColorFormatter::new(ColorScheme::default(), termcolor::ColorChoice::AlwaysAnsi);\n                    format_plan(&mut fmt, plan).map(|_| fmt.to_string())\n                }\n            }\n            .map_err(|e| anyhow::anyhow!(\"Failed to format migration plan: {e}\")),\n        }\n    }\n}\n\n/// A migration policy that determines whether a module update is allowed to break client compatibility.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum MigrationPolicy {\n    /// Migration must maintain backward compatibility with existing clients.\n    Compatible,\n    /// To use this, a valid [`MigrationToken`] must be provided.\n    /// The token is issued through the pre-publish API (see the `client-api` crate)\n    /// and proves that the publisher explicitly acknowledged the breaking change.\n    BreakClients(spacetimedb_lib::Hash),\n}\n\nimpl MigrationPolicy {\n    /// Verifies whether the given migration plan is allowed under the current policy.\n    ///\n    /// Returns `Ok(())` if allowed, otherwise an appropriate `MigrationPolicyError`\n    fn permits_plan(&self, plan: &MigratePlan<'_>, token: &MigrationToken) -> anyhow::Result<(), MigrationPolicyError> {\n        match self {\n            MigrationPolicy::Compatible => {\n                if plan.breaks_client() {\n                    Err(MigrationPolicyError::ClientBreakingChangeDisallowed)\n                } else {\n                    Ok(())\n                }\n            }\n            MigrationPolicy::BreakClients(expected_hash) => {\n                if token.hash() == *expected_hash {\n                    Ok(())\n                } else {\n                    Err(MigrationPolicyError::InvalidToken)\n                }\n            }\n        }\n    }\n\n    /// Attempts to generate a migration plan and validate it under this policy.\n    ///\n    /// Fails if migration is not permitted by the policy or migration planning fails.\n    pub fn try_migrate<'def>(\n        &self,\n        database_identity: Identity,\n        old_module_hash: spacetimedb_lib::Hash,\n        old_module_def: &'def ModuleDef,\n        new_module_hash: spacetimedb_lib::Hash,\n        new_module_def: &'def ModuleDef,\n    ) -> anyhow::Result<MigratePlan<'def>, MigrationPolicyError> {\n        let plan = ponder_migrate(old_module_def, new_module_def).map_err(MigrationPolicyError::AutoMigrateFailure)?;\n\n        let token = MigrationToken {\n            database_identity,\n            old_module_hash,\n            new_module_hash,\n        };\n        self.permits_plan(&plan, &token)?;\n        Ok(plan)\n    }\n}\n\n#[derive(Debug, Error)]\npub enum MigrationPolicyError {\n    #[error(\"Automatic migration planning failed\")]\n    AutoMigrateFailure(ErrorStream<AutoMigrateError>),\n\n    #[error(\"Token provided is invalid or does not match expected hash\")]\n    InvalidToken,\n\n    #[error(\"Migration plan contains a client-breaking change which is disallowed under current policy\")]\n    ClientBreakingChangeDisallowed,\n}\n\n/// A token acknowledging a breaking migration.\n///\n/// Note: This token is only intended as a UX safeguard, not as a security measure.\n/// No secret is used in its generation, which means anyone can reproduce it given\n/// the inputs. That is acceptable for our purposes since it only signals user intent,\n/// not authorization.\npub struct MigrationToken {\n    pub database_identity: Identity,\n    pub old_module_hash: spacetimedb_lib::Hash,\n    pub new_module_hash: spacetimedb_lib::Hash,\n}\n\nimpl MigrationToken {\n    pub fn hash(&self) -> spacetimedb_lib::Hash {\n        hash_bytes(\n            format!(\n                \"{}{}{}\",\n                self.database_identity.to_hex(),\n                self.old_module_hash.to_hex(),\n                self.new_module_hash.to_hex()\n            )\n            .as_str(),\n        )\n    }\n}\n\n/// A plan for a manual migration.\n/// `new` must have a reducer marked with `Lifecycle::Update`.\n#[derive(Debug)]\npub struct ManualMigratePlan<'def> {\n    pub old: &'def ModuleDef,\n    pub new: &'def ModuleDef,\n}\n\n/// A plan for an automatic migration.\n#[derive(Debug)]\npub struct AutoMigratePlan<'def> {\n    /// The old database definition.\n    pub old: &'def ModuleDef,\n    /// The new database definition.\n    pub new: &'def ModuleDef,\n    /// The checks to perform before the automatic migration.\n    /// There is also an implied check: that the schema in the database is compatible with the old ModuleDef.\n    pub prechecks: Vec<AutoMigratePrecheck<'def>>,\n    /// The migration steps to perform.\n    /// Order matters: `Remove`s of a particular `Def` must be ordered before `Add`s.\n    pub steps: Vec<AutoMigrateStep<'def>>,\n}\n\nimpl AutoMigratePlan<'_> {\n    fn any_step(&self, f: impl Fn(&AutoMigrateStep) -> bool) -> bool {\n        self.steps.iter().any(f)\n    }\n\n    fn disconnects_all_users(&self) -> bool {\n        self.any_step(|step| matches!(step, AutoMigrateStep::DisconnectAllUsers))\n    }\n}\n\n/// Checks that must be performed before performing an automatic migration.\n/// These checks can access table contents and other database state.\n#[derive(PartialEq, Eq, Debug, PartialOrd, Ord)]\npub enum AutoMigratePrecheck<'def> {\n    /// Perform a check that adding a sequence is valid (the relevant column contains no values\n    /// greater than the sequence's start value).\n    CheckAddSequenceRangeValid(<SequenceDef as ModuleDefLookup>::Key<'def>),\n}\n\n/// A step in an automatic migration.\n#[derive(PartialEq, Eq, Debug, PartialOrd, Ord)]\npub enum AutoMigrateStep<'def> {\n    // It is important FOR CORRECTNESS that `Remove` variants are declared before `Add` variants in this enum!\n    //\n    // The ordering is used to sort the steps of an auto-migration.\n    // If adds go before removes, and the user tries to remove an index and then re-add it with new configuration,\n    // the following can occur:\n    //\n    // 1. `AddIndex(\"indexname\")`\n    // 2. `RemoveIndex(\"indexname\")`\n    //\n    // This results in the existing index being re-added -- which, at time of writing, does nothing -- and then removed,\n    // resulting in the intended index not being created.\n    //\n    // For now, we just ensure that we declare all `Remove` variants before `Add` variants\n    // and let `#[derive(PartialOrd)]` take care of the rest.\n    //\n    // TODO: when this enum is made serializable, a more durable fix will be needed here.\n    // Probably we will want to have separate arrays of add and remove steps.\n    //\n    /// Remove an index.\n    RemoveIndex(<IndexDef as ModuleDefLookup>::Key<'def>),\n    /// Remove a constraint.\n    RemoveConstraint(<ConstraintDef as ModuleDefLookup>::Key<'def>),\n    /// Remove a sequence.\n    RemoveSequence(<SequenceDef as ModuleDefLookup>::Key<'def>),\n    /// Remove a schedule annotation from a table.\n    RemoveSchedule(<ScheduleDef as ModuleDefLookup>::Key<'def>),\n    /// Remove a view and corresponding view table\n    RemoveView(<ViewDef as ModuleDefLookup>::Key<'def>),\n    /// Remove a row-level security query.\n    RemoveRowLevelSecurity(<RawRowLevelSecurityDefV9 as ModuleDefLookup>::Key<'def>),\n\n    /// Change the column types of a table, in a layout compatible way.\n    ///\n    /// This should be done before any new indices are added.\n    ChangeColumns(<TableDef as ModuleDefLookup>::Key<'def>),\n    /// Add columns to a table, in a layout-INCOMPATIBLE way.\n    ///\n    /// This is a destructive operation that requires first running a `DisconnectAllUsers`.\n    ///\n    /// The added columns are guaranteed to be contiguous\n    /// and at the end of the table.\n    /// They are also guaranteed to have default values set.\n    ///\n    /// When this step is present,\n    /// no `ChangeColumns` steps will be, for the same table.\n    AddColumns(<TableDef as ModuleDefLookup>::Key<'def>),\n\n    /// Add a table, including all indexes, constraints, and sequences.\n    /// There will NOT be separate steps in the plan for adding indexes, constraints, and sequences.\n    AddTable(<TableDef as ModuleDefLookup>::Key<'def>),\n    /// Add an index.\n    AddIndex(<IndexDef as ModuleDefLookup>::Key<'def>),\n    /// Add a sequence.\n    AddSequence(<SequenceDef as ModuleDefLookup>::Key<'def>),\n    /// Add a schedule annotation to a table.\n    AddSchedule(<ScheduleDef as ModuleDefLookup>::Key<'def>),\n    /// Add a view and corresponding view table\n    AddView(<ViewDef as ModuleDefLookup>::Key<'def>),\n    /// Add a row-level security query.\n    AddRowLevelSecurity(<RawRowLevelSecurityDefV9 as ModuleDefLookup>::Key<'def>),\n\n    /// Change the access of a table.\n    ChangeAccess(<TableDef as ModuleDefLookup>::Key<'def>),\n\n    /// Recompute a view, update its backing table, and push updates to clients\n    UpdateView(<ViewDef as ModuleDefLookup>::Key<'def>),\n\n    /// Disconnect all users connected to the module.\n    DisconnectAllUsers,\n}\n\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]\npub struct ChangeColumnTypeParts {\n    pub table: Identifier,\n    pub column: Identifier,\n    pub type1: PrettyAlgebraicType,\n    pub type2: PrettyAlgebraicType,\n}\n\n/// Something that might prevent an automatic migration.\n#[derive(thiserror::Error, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum AutoMigrateError {\n    #[error(\"Adding a column {column} to table {table} requires a default value annotation\")]\n    AddColumn { table: Identifier, column: Identifier },\n\n    #[error(\"Removing a column {column} from table {table} requires a manual migration\")]\n    RemoveColumn { table: Identifier, column: Identifier },\n\n    #[error(\"Reordering table {table} requires a manual migration\")]\n    ReorderTable { table: Identifier },\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?} requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnType(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing a type within column {} in table {} from {:?} to {:?} requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnType(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with fewer variants, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeFewerVariants(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing a type within column {} in table {} from {:?} to {:?}, with fewer variants, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeFewerVariants(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with a renamed variant, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeRenamedVariant(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with a renamed variant, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeRenamedVariant(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, requires a manual migration, due to size mismatch\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeSizeMismatch(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing a type within column {} in table {} from {:?} to {:?}, requires a manual migration, due to size mismatch\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeSizeMismatch(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, requires a manual migration, due to alignment mismatch\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeAlignMismatch(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing a type within column {} in table {} from {:?} to {:?}, requires a manual migration, due to alignment mismatch\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeAlignMismatch(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with fewer fields, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeFewerFields(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing a type within column {} in table {} from {:?} to {:?}, with fewer fields, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeFewerFields(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with a renamed field, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeColumnTypeRenamedField(ChangeColumnTypeParts),\n\n    #[error(\n        \"Changing the type of column {} in table {} from {:?} to {:?}, with a renamed field, requires a manual migration\",\n        .0.column, .0.table, .0.type1, .0.type2\n    )]\n    ChangeWithinColumnTypeRenamedField(ChangeColumnTypeParts),\n\n    #[error(\"Adding a unique constraint {constraint} requires a manual migration\")]\n    AddUniqueConstraint { constraint: RawIdentifier },\n\n    #[error(\"Changing a unique constraint {constraint} requires a manual migration\")]\n    ChangeUniqueConstraint { constraint: RawIdentifier },\n\n    #[error(\"Removing the table {table} requires a manual migration\")]\n    RemoveTable { table: Identifier },\n\n    #[error(\"Changing the table type of table {table} from {type1:?} to {type2:?} requires a manual migration\")]\n    ChangeTableType {\n        table: Identifier,\n        type1: TableType,\n        type2: TableType,\n    },\n\n    #[error(\"Changing the event flag of table {table} requires a manual migration\")]\n    ChangeTableEventFlag { table: Identifier },\n\n    #[error(\n        \"Changing the accessor name on index {index} from {old_accessor:?} to {new_accessor:?} requires a manual migration\"\n    )]\n    ChangeIndexAccessor {\n        index: RawIdentifier,\n        old_accessor: Option<Identifier>,\n        new_accessor: Option<Identifier>,\n    },\n}\n\n/// Construct a migration plan.\n/// If `new` has an `__update__` reducer, return a manual migration plan.\n/// Otherwise, try to plan an automatic migration. This may fail.\npub fn ponder_migrate<'def>(old: &'def ModuleDef, new: &'def ModuleDef) -> Result<MigratePlan<'def>> {\n    // TODO(1.0): Implement this function.\n    // Currently we only can do automatic migrations.\n    ponder_auto_migrate(old, new).map(MigratePlan::Auto)\n}\n\n/// Construct an automatic migration plan, or reject with reasons why automatic migration can't be performed.\npub fn ponder_auto_migrate<'def>(old: &'def ModuleDef, new: &'def ModuleDef) -> Result<AutoMigratePlan<'def>> {\n    // Both the old and new database definitions have already been validated (this is enforced by the types).\n    // All we have to do is walk through and compare them.\n    let mut plan = AutoMigratePlan {\n        old,\n        new,\n        steps: Vec::new(),\n        prechecks: Vec::new(),\n    };\n\n    let views_ok = auto_migrate_views(&mut plan);\n    let tables_ok = auto_migrate_tables(&mut plan);\n\n    // Our diffing algorithm will detect added constraints / indexes / sequences in new tables, we use this to filter those out.\n    // They're handled by adding the root table.\n    let new_tables: HashSet<&Identifier> = diff(plan.old, plan.new, ModuleDef::tables)\n        .filter_map(|diff| match diff {\n            Diff::Add { new } => Some(&new.name),\n            _ => None,\n        })\n        .collect();\n    let indexes_ok = auto_migrate_indexes(&mut plan, &new_tables);\n    let sequences_ok = auto_migrate_sequences(&mut plan, &new_tables);\n    let constraints_ok = auto_migrate_constraints(&mut plan, &new_tables);\n    // IMPORTANT: RLS auto-migrate steps must come last,\n    // since they assume that any schema changes, like adding or dropping tables,\n    // have already been reflected in the database state.\n    let rls_ok = auto_migrate_row_level_security(&mut plan);\n\n    let ((), (), (), (), (), ()) =\n        (views_ok, tables_ok, indexes_ok, sequences_ok, constraints_ok, rls_ok).combine_errors()?;\n\n    plan.steps.sort();\n    plan.prechecks.sort();\n\n    Ok(plan)\n}\n\n/// A diff between two items.\n/// `Add` means the item is present in the new `ModuleDef` but not the old.\n/// `Remove` means the item is present in the old `ModuleDef` but not the new.\n/// `MaybeChange` indicates the item is present in both.\n#[derive(Debug)]\nenum Diff<'def, T> {\n    Add { new: &'def T },\n    Remove { old: &'def T },\n    MaybeChange { old: &'def T, new: &'def T },\n}\n\n/// Diff a collection of items, looking them up in both the old and new `ModuleDef` by their `ModuleDefLookup::Key`.\n/// Keys are required to be stable across migrations, which makes this possible.\nfn diff<'def, T: ModuleDefLookup, I: Iterator<Item = &'def T>>(\n    old: &'def ModuleDef,\n    new: &'def ModuleDef,\n    iter: impl Fn(&'def ModuleDef) -> I,\n) -> impl Iterator<Item = Diff<'def, T>> {\n    iter(old)\n        .map(move |old_item| match T::lookup(new, old_item.key()) {\n            Some(new_item) => Diff::MaybeChange {\n                old: old_item,\n                new: new_item,\n            },\n            None => Diff::Remove { old: old_item },\n        })\n        .chain(iter(new).filter_map(move |new_item| {\n            if T::lookup(old, new_item.key()).is_none() {\n                Some(Diff::Add { new: new_item })\n            } else {\n                None\n            }\n        }))\n}\n\nfn auto_migrate_views(plan: &mut AutoMigratePlan<'_>) -> Result<()> {\n    diff(plan.old, plan.new, ModuleDef::views)\n        .map(|table_diff| -> Result<()> {\n            match table_diff {\n                Diff::Add { new } => {\n                    plan.steps.push(AutoMigrateStep::AddView(new.key()));\n                    Ok(())\n                }\n                // From the user's perspective, views do not have persistent state.\n                // Hence removal does not require a manual migration - just disconnecting clients.\n                Diff::Remove { old } if plan.disconnects_all_users() => {\n                    plan.steps.push(AutoMigrateStep::RemoveView(old.key()));\n                    Ok(())\n                }\n                Diff::Remove { old } => {\n                    plan.steps.push(AutoMigrateStep::RemoveView(old.key()));\n                    plan.steps.push(AutoMigrateStep::DisconnectAllUsers);\n                    Ok(())\n                }\n                Diff::MaybeChange { old, new } => auto_migrate_view(plan, old, new),\n            }\n        })\n        .collect_all_errors()\n}\n\nfn auto_migrate_view<'def>(plan: &mut AutoMigratePlan<'def>, old: &'def ViewDef, new: &'def ViewDef) -> Result<()> {\n    let key = old.key();\n\n    if old.is_public != new.is_public {\n        plan.steps.push(AutoMigrateStep::ChangeAccess(key));\n    }\n\n    // We can always auto-migrate a view because we can always re-compute it.\n    // However certain things require us to disconnect clients:\n    // 1. If we add or remove a column or parameter\n    // 2. If we change the order of the columns or parameters\n    // 3. If we change the types of the columns or parameters\n    // 4. If we change the context parameter\n    let Any(incompatible_return_type) = diff(plan.old, plan.new, |def| {\n        def.lookup_expect::<ViewDef>(key).return_columns.iter()\n    })\n    .map(|col_diff| {\n        match col_diff {\n            // We must disconnect clients if we add or remove a parameter or column\n            Diff::Add { .. } | Diff::Remove { .. } => Any(true),\n            Diff::MaybeChange { old, new } => {\n                if old.col_id != new.col_id {\n                    return Any(true);\n                };\n\n                ensure_old_ty_upgradable_to_new(\n                    false,\n                    &|| old.view_name.clone(),\n                    &|| old.name.clone(),\n                    &WithTypespace::new(plan.old.typespace(), &old.ty)\n                        .resolve_refs()\n                        .expect(\"valid ViewDefs must have valid type refs\"),\n                    &WithTypespace::new(plan.new.typespace(), &new.ty)\n                        .resolve_refs()\n                        .expect(\"valid ViewDefs must have valid type refs\"),\n                )\n                .unwrap_or(Any(true))\n            }\n        }\n    })\n    .collect();\n\n    let Any(incompatible_param_types) = diff(plan.old, plan.new, |def| {\n        def.lookup_expect::<ViewDef>(key).param_columns.iter()\n    })\n    .map(|col_diff| {\n        match col_diff {\n            // We must disconnect clients if we add or remove a parameter or column\n            Diff::Add { .. } | Diff::Remove { .. } => Any(true),\n            Diff::MaybeChange { old, new } => {\n                if old.col_id != new.col_id {\n                    return Any(true);\n                };\n\n                ensure_old_ty_upgradable_to_new(\n                    false,\n                    &|| old.view_name.clone(),\n                    &|| old.name.clone(),\n                    &WithTypespace::new(plan.old.typespace(), &old.ty)\n                        .resolve_refs()\n                        .expect(\"valid ViewDefs must have valid type refs\"),\n                    &WithTypespace::new(plan.new.typespace(), &new.ty)\n                        .resolve_refs()\n                        .expect(\"valid ViewDefs must have valid type refs\"),\n                )\n                .unwrap_or(Any(true))\n            }\n        }\n    })\n    .collect();\n\n    if old.is_anonymous != new.is_anonymous || incompatible_return_type || incompatible_param_types {\n        plan.steps.push(AutoMigrateStep::AddView(new.key()));\n        plan.steps.push(AutoMigrateStep::RemoveView(old.key()));\n\n        if !plan.disconnects_all_users() {\n            plan.steps.push(AutoMigrateStep::DisconnectAllUsers);\n        }\n    } else {\n        plan.steps.push(AutoMigrateStep::UpdateView(new.key()));\n    }\n\n    Ok(())\n}\n\nfn auto_migrate_tables(plan: &mut AutoMigratePlan<'_>) -> Result<()> {\n    diff(plan.old, plan.new, ModuleDef::tables)\n        .map(|table_diff| -> Result<()> {\n            match table_diff {\n                Diff::Add { new } => {\n                    plan.steps.push(AutoMigrateStep::AddTable(new.key()));\n                    Ok(())\n                }\n                // TODO: When we remove tables, we should also remove their dependencies, including row-level security.\n                Diff::Remove { old } => Err(AutoMigrateError::RemoveTable {\n                    table: old.name.clone(),\n                }\n                .into()),\n                Diff::MaybeChange { old, new } => auto_migrate_table(plan, old, new),\n            }\n        })\n        .collect_all_errors()\n}\n\nfn auto_migrate_table<'def>(plan: &mut AutoMigratePlan<'def>, old: &'def TableDef, new: &'def TableDef) -> Result<()> {\n    let key = old.key();\n    let type_ok: Result<()> = if old.table_type == new.table_type {\n        Ok(())\n    } else {\n        Err(AutoMigrateError::ChangeTableType {\n            table: old.name.clone(),\n            type1: old.table_type,\n            type2: new.table_type,\n        }\n        .into())\n    };\n    let event_ok: Result<()> = if old.is_event == new.is_event {\n        Ok(())\n    } else {\n        Err(AutoMigrateError::ChangeTableEventFlag {\n            table: old.name.clone(),\n        }\n        .into())\n    };\n    if old.table_access != new.table_access {\n        plan.steps.push(AutoMigrateStep::ChangeAccess(key));\n    }\n    if old.schedule != new.schedule {\n        // Note: this handles the case where there's an altered ScheduleDef for some reason.\n        if let Some(old_schedule) = old.schedule.as_ref() {\n            plan.steps.push(AutoMigrateStep::RemoveSchedule(old_schedule.key()));\n        }\n        if let Some(new_schedule) = new.schedule.as_ref() {\n            plan.steps.push(AutoMigrateStep::AddSchedule(new_schedule.key()));\n        }\n    }\n\n    let columns_ok = diff(plan.old, plan.new, |def| {\n        def.lookup_expect::<TableDef>(key).columns.iter()\n    })\n    .map(|col_diff| -> Result<_> {\n        match col_diff {\n            Diff::Add { new } => {\n                if new.default_value.is_some() {\n                    // `row_type_changed`, `columns_added`\n                    Ok(ProductMonoid(Any(false), Any(true)))\n                } else {\n                    Err(AutoMigrateError::AddColumn {\n                        table: new.table_name.clone(),\n                        column: new.name.clone(),\n                    }\n                    .into())\n                }\n            }\n            Diff::Remove { old } => Err(AutoMigrateError::RemoveColumn {\n                table: old.table_name.clone(),\n                column: old.name.clone(),\n            }\n            .into()),\n            Diff::MaybeChange { old, new } => {\n                // Check column type upgradability.\n                let old_ty = WithTypespace::new(plan.old.typespace(), &old.ty)\n                    .resolve_refs()\n                    .expect(\"valid TableDef must have valid type refs\");\n                let new_ty = WithTypespace::new(plan.new.typespace(), &new.ty)\n                    .resolve_refs()\n                    .expect(\"valid TableDef must have valid type refs\");\n                let types_ok = ensure_old_ty_upgradable_to_new(\n                    false,\n                    &|| old.table_name.clone(),\n                    &|| old.name.clone(),\n                    &old_ty,\n                    &new_ty,\n                );\n\n                // Note that the diff algorithm relies on `ModuleDefLookup` for `ColumnDef`,\n                // which looks up columns by NAME, NOT position: precisely to allow this step to work!\n\n                // Note: We reject changes to positions. This means that, if a column was present in the old version of the table,\n                // it must be in the same place in the new version of the table.\n                // This guarantees that any added columns live at the end of the table.\n                let positions_ok = if old.col_id == new.col_id {\n                    Ok(())\n                } else {\n                    Err(AutoMigrateError::ReorderTable {\n                        table: old.table_name.clone(),\n                    }\n                    .into())\n                };\n\n                (types_ok, positions_ok)\n                    .combine_errors()\n                    // row_type_changed, column_added\n                    .map(|(x, _)| ProductMonoid(x, Any(false)))\n            }\n        }\n    })\n    .collect_all_errors::<ProductMonoid<Any, Any>>();\n\n    let ((), (), ProductMonoid(Any(row_type_changed), Any(columns_added))) =\n        (type_ok, event_ok, columns_ok).combine_errors()?;\n\n    // If we're adding a column, we'll rewrite the whole table.\n    // That makes any `ChangeColumns` moot, so we can skip it.\n    if columns_added {\n        if !plan.disconnects_all_users() {\n            plan.steps.push(AutoMigrateStep::DisconnectAllUsers);\n        }\n        plan.steps.push(AutoMigrateStep::AddColumns(key));\n    } else if row_type_changed {\n        plan.steps.push(AutoMigrateStep::ChangeColumns(key));\n    }\n\n    Ok(())\n}\n\n/// An \"any\" monoid with `false` as identity and `|` as the operator.\n#[derive(Default)]\nstruct Any(bool);\n\nimpl FromIterator<Any> for Any {\n    fn from_iter<T: IntoIterator<Item = Any>>(iter: T) -> Self {\n        Any(iter.into_iter().any(|Any(x)| x))\n    }\n}\n\nimpl BitOr for Any {\n    type Output = Self;\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self(self.0 | rhs.0)\n    }\n}\n\n/// A monoid that allows running two `Any`s in parallel.\n#[derive(Default)]\nstruct ProductMonoid<M1, M2>(M1, M2);\n\nimpl<M1: BitOr<Output = M1>, M2: BitOr<Output = M2>> BitOr for ProductMonoid<M1, M2> {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self(self.0 | rhs.0, self.1 | rhs.1)\n    }\n}\n\nimpl<M1: BitOr<Output = M1> + Default, M2: BitOr<Output = M2> + Default> FromIterator<ProductMonoid<M1, M2>>\n    for ProductMonoid<M1, M2>\n{\n    fn from_iter<T: IntoIterator<Item = ProductMonoid<M1, M2>>>(iter: T) -> Self {\n        iter.into_iter().reduce(|p1, p2| p1 | p2).unwrap_or_default()\n    }\n}\n\nfn ensure_old_ty_upgradable_to_new(\n    within: bool,\n    old_container_name: &impl Fn() -> Identifier,\n    old_column_name: &impl Fn() -> Identifier,\n    old_ty: &AlgebraicType,\n    new_ty: &AlgebraicType,\n) -> Result<Any> {\n    use AutoMigrateError::*;\n    // Ensures an `old_ty` within `old` is upgradable to `new_ty`.\n    let ensure =\n        |(old_ty, new_ty)| ensure_old_ty_upgradable_to_new(true, old_container_name, old_column_name, old_ty, new_ty);\n\n    // Returns a `ChangeColumnTypeParts` error using the current `old_ty` and `new_ty`.\n    let parts_for_error = || ChangeColumnTypeParts {\n        table: old_container_name(),\n        column: old_column_name(),\n        type1: old_ty.clone().into(),\n        type2: new_ty.clone().into(),\n    };\n\n    match (old_ty, new_ty) {\n        // For sums, we allow the variants in `old_ty` to be a prefix of `new_ty`.\n        (AlgebraicType::Sum(old_ty), AlgebraicType::Sum(new_ty)) => {\n            let old_vars = &*old_ty.variants;\n            let new_vars = &*new_ty.variants;\n\n            // The number of variants in `new_ty` cannot decrease.\n            let var_lens_ok = match old_vars.len().cmp(&new_vars.len()) {\n                Ordering::Less => Ok(Any(true)),\n                Ordering::Equal => Ok(Any(false)),\n                Ordering::Greater if within => Err(ChangeWithinColumnTypeFewerVariants(parts_for_error()).into()),\n                Ordering::Greater => Err(ChangeColumnTypeFewerVariants(parts_for_error()).into()),\n            };\n\n            // The variants in `old_ty` must be upgradable to those in `old_ty`.\n            // Strict equality is *not* imposed in the prefix!\n            let prefix_ok = old_vars\n                .iter()\n                .zip(new_vars)\n                .map(|(o, n)| {\n                    // Ensure type compatibility.\n                    let res_ty = ensure((&o.algebraic_type, &n.algebraic_type));\n                    // Ensure name doesn't change.\n                    let res_name = if o.name() == n.name() {\n                        Ok(())\n                    } else if within {\n                        Err(ChangeWithinColumnTypeRenamedVariant(parts_for_error()).into())\n                    } else {\n                        Err(ChangeColumnTypeRenamedVariant(parts_for_error()).into())\n                    };\n                    (res_ty, res_name).combine_errors().map(|(c, ())| c)\n                })\n                .collect_all_errors::<Any>();\n\n            // The old and the new sum types must have matching layout sizes and alignments.\n            let old_ty = SumTypeLayout::from(old_ty.clone());\n            let new_ty = SumTypeLayout::from(new_ty.clone());\n            let old_layout = old_ty.layout();\n            let new_layout = new_ty.layout();\n            let size_ok = if old_layout.size == new_layout.size {\n                Ok(())\n            } else if within {\n                Err(ChangeWithinColumnTypeSizeMismatch(parts_for_error()).into())\n            } else {\n                Err(ChangeColumnTypeSizeMismatch(parts_for_error()).into())\n            };\n            let align_ok = if old_layout.align == new_layout.align {\n                Ok(())\n            } else if within {\n                Err(ChangeWithinColumnTypeAlignMismatch(parts_for_error()).into())\n            } else {\n                Err(ChangeColumnTypeAlignMismatch(parts_for_error()).into())\n            };\n\n            let (len_changed, prefix_changed, ..) = (var_lens_ok, prefix_ok, size_ok, align_ok).combine_errors()?;\n            Ok(len_changed | prefix_changed)\n        }\n\n        // For products,\n        // we need to check each field's upgradability due to sums,\n        // and there must be as many fields.\n        // Note that we don't care about field names.\n        (AlgebraicType::Product(old_ty), AlgebraicType::Product(new_ty)) => {\n            // The number of variants in `new_ty` cannot decrease.\n            let len_eq_ok = if old_ty.len() == new_ty.len() {\n                Ok(())\n            } else {\n                Err(if within {\n                    ChangeWithinColumnTypeFewerFields(parts_for_error())\n                } else {\n                    ChangeColumnTypeFewerFields(parts_for_error())\n                }\n                .into())\n            };\n\n            // The fields in `old_ty` must be upgradable to those in `old_ty`.\n            let fields_ok = old_ty\n                .iter()\n                .zip(new_ty.iter())\n                .map(|(o, n)| {\n                    // Ensure type compatibility.\n                    let res_ty = ensure((&o.algebraic_type, &n.algebraic_type));\n                    // Ensure name doesn't change.\n                    let res_name = if o.name() == n.name() {\n                        Ok(())\n                    } else if within {\n                        Err(ChangeWithinColumnTypeRenamedField(parts_for_error()).into())\n                    } else {\n                        Err(ChangeColumnTypeRenamedField(parts_for_error()).into())\n                    };\n                    (res_ty, res_name).combine_errors().map(|(c, ())| c)\n                })\n                .collect_all_errors::<Any>();\n\n            (len_eq_ok, fields_ok).combine_errors().map(|(_, x)| x)\n        }\n\n        // For arrays, we need to check each field's upgradability due to sums.\n        (AlgebraicType::Array(old_ty), AlgebraicType::Array(new_ty)) => ensure_old_ty_upgradable_to_new(\n            true,\n            old_container_name,\n            old_column_name,\n            &old_ty.elem_ty,\n            &new_ty.elem_ty,\n        ),\n\n        // We only have the simple cases left, and there, no change is good change.\n        (old_ty, new_ty) if old_ty == new_ty => Ok(Any(false)),\n        _ => Err(if within {\n            ChangeWithinColumnType(parts_for_error())\n        } else {\n            ChangeColumnType(parts_for_error())\n        }\n        .into()),\n    }\n}\n\nfn auto_migrate_indexes(plan: &mut AutoMigratePlan<'_>, new_tables: &HashSet<&Identifier>) -> Result<()> {\n    diff(plan.old, plan.new, ModuleDef::indexes)\n        .map(|index_diff| -> Result<()> {\n            match index_diff {\n                Diff::Add { new } => {\n                    if !new_tables.contains(&plan.new.stored_in_table_def(&new.name).unwrap().name) {\n                        plan.steps.push(AutoMigrateStep::AddIndex(new.key()));\n                    }\n                    Ok(())\n                }\n                Diff::Remove { old } => {\n                    plan.steps.push(AutoMigrateStep::RemoveIndex(old.key()));\n                    Ok(())\n                }\n                Diff::MaybeChange { old, new } => {\n                    if old.accessor_name != new.accessor_name {\n                        Err(AutoMigrateError::ChangeIndexAccessor {\n                            index: old.name.clone(),\n                            old_accessor: old.accessor_name.clone(),\n                            new_accessor: new.accessor_name.clone(),\n                        }\n                        .into())\n                    } else {\n                        if old.algorithm != new.algorithm {\n                            plan.steps.push(AutoMigrateStep::RemoveIndex(old.key()));\n                            plan.steps.push(AutoMigrateStep::AddIndex(old.key()));\n                        }\n                        Ok(())\n                    }\n                }\n            }\n        })\n        .collect_all_errors()\n}\n\nfn auto_migrate_sequences(plan: &mut AutoMigratePlan, new_tables: &HashSet<&Identifier>) -> Result<()> {\n    diff(plan.old, plan.new, ModuleDef::sequences)\n        .map(|sequence_diff| -> Result<()> {\n            match sequence_diff {\n                Diff::Add { new } => {\n                    if !new_tables.contains(&plan.new.stored_in_table_def(&new.name).unwrap().name) {\n                        plan.prechecks\n                            .push(AutoMigratePrecheck::CheckAddSequenceRangeValid(new.key()));\n                        plan.steps.push(AutoMigrateStep::AddSequence(new.key()));\n                    }\n                    Ok(())\n                }\n                Diff::Remove { old } => {\n                    plan.steps.push(AutoMigrateStep::RemoveSequence(old.key()));\n                    Ok(())\n                }\n                Diff::MaybeChange { old, new } => {\n                    // we do not need to check column ids, since in an automigrate, column ids are not changed.\n                    if old != new {\n                        plan.prechecks\n                            .push(AutoMigratePrecheck::CheckAddSequenceRangeValid(new.key()));\n                        plan.steps.push(AutoMigrateStep::RemoveSequence(old.key()));\n                        plan.steps.push(AutoMigrateStep::AddSequence(new.key()));\n                    }\n                    Ok(())\n                }\n            }\n        })\n        .collect_all_errors()\n}\n\nfn auto_migrate_constraints(plan: &mut AutoMigratePlan, new_tables: &HashSet<&Identifier>) -> Result<()> {\n    diff(plan.old, plan.new, ModuleDef::constraints)\n        .map(|constraint_diff| -> Result<()> {\n            match constraint_diff {\n                Diff::Add { new } => {\n                    if new_tables.contains(&plan.new.stored_in_table_def(&new.name).unwrap().name) {\n                        // it's okay to add a constraint in a new table.\n                        Ok(())\n                    } else {\n                        // it's not okay to add a new constraint to an existing table.\n                        Err(AutoMigrateError::AddUniqueConstraint {\n                            constraint: new.name.clone(),\n                        }\n                        .into())\n                    }\n                }\n                Diff::Remove { old } => {\n                    plan.steps.push(AutoMigrateStep::RemoveConstraint(old.key()));\n                    Ok(())\n                }\n                Diff::MaybeChange { old, new } => {\n                    if old == new {\n                        Ok(())\n                    } else {\n                        Err(AutoMigrateError::ChangeUniqueConstraint {\n                            constraint: old.name.clone(),\n                        }\n                        .into())\n                    }\n                }\n            }\n        })\n        .collect_all_errors()\n}\n\n// Because we can refer to many tables and fields on the row level-security query, we need to remove all of them,\n// then add the new ones, instead of trying to track the graph of dependencies.\nfn auto_migrate_row_level_security(plan: &mut AutoMigratePlan) -> Result<()> {\n    // Track if any RLS rules were changed.\n    let mut old_rls = HashSet::new();\n    let mut new_rls = HashSet::new();\n\n    for rls in plan.old.row_level_security() {\n        old_rls.insert(rls.key());\n        plan.steps.push(AutoMigrateStep::RemoveRowLevelSecurity(rls.key()));\n    }\n    for rls in plan.new.row_level_security() {\n        new_rls.insert(rls.key());\n        plan.steps.push(AutoMigrateStep::AddRowLevelSecurity(rls.key()));\n    }\n\n    // We can force flush the cache by force disconnecting all clients if an RLS rule has been added, removed, or updated.\n    if old_rls != new_rls && !plan.disconnects_all_users() {\n        plan.steps.push(AutoMigrateStep::DisconnectAllUsers);\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_data_structures::expect_error_matching;\n    use spacetimedb_lib::{\n        db::raw_def::{v9::btree, *},\n        AlgebraicType, AlgebraicValue, ProductType, ScheduleAt,\n    };\n    use spacetimedb_primitives::ColId;\n    use v9::{RawModuleDefV9Builder, TableAccess};\n    use validate::tests::expect_identifier;\n\n    fn create_module_def(build_module: impl Fn(&mut RawModuleDefV9Builder)) -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n        build_module(&mut builder);\n        builder\n            .finish()\n            .try_into()\n            .expect(\"new_def should be a valid database definition\")\n    }\n\n    fn initial_module_def() -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n        let schedule_at = builder.add_type::<ScheduleAt>();\n        let sum_ty = AlgebraicType::sum([(\"v1\", AlgebraicType::U64)]);\n        let sum_refty = builder.add_algebraic_type([], \"sum\", sum_ty, true);\n        builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                    (\"sum\", sum_refty.into()),\n                ]),\n                true,\n            )\n            .with_column_sequence(0)\n            .with_unique_constraint(ColId(0))\n            .with_index(btree(0), \"id_index\")\n            .with_index(btree([0, 1]), \"id_name_index\")\n            .finish();\n\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                ]),\n                true,\n            )\n            .with_access(TableAccess::Public)\n            .finish();\n\n        let deliveries_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"scheduled_id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at.clone()),\n                    (\"sum\", AlgebraicType::array(sum_refty.into())),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(0)\n            .with_index_no_accessor_name(btree(0))\n            .with_schedule(\"check_deliveries\", 1)\n            .finish();\n        builder.add_reducer(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", AlgebraicType::Ref(deliveries_type))]),\n            None,\n        );\n\n        // Add a view and add its return type to the typespace\n        let view_return_ty = AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::U64)]);\n        let view_return_ty_ref = builder.add_algebraic_type([], \"my_view_return\", view_return_ty, true);\n        builder.add_view(\n            \"my_view\",\n            0,\n            true,\n            true,\n            ProductType::from([(\"x\", AlgebraicType::U32), (\"y\", AlgebraicType::U32)]),\n            AlgebraicType::option(AlgebraicType::Ref(view_return_ty_ref)),\n        );\n\n        builder\n            .build_table_with_new_type(\n                \"Inspections\",\n                ProductType::from([\n                    (\"scheduled_id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at.clone()),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(0)\n            .with_index_no_accessor_name(btree(0))\n            .finish();\n\n        builder.add_row_level_security(\"SELECT * FROM Apples\");\n\n        builder\n            .finish()\n            .try_into()\n            .expect(\"old_def should be a valid database definition\")\n    }\n\n    fn updated_module_def() -> ModuleDef {\n        let mut builder = RawModuleDefV9Builder::new();\n        let _ = builder.add_type::<u32>(); // reposition ScheduleAt in the typespace, should have no effect.\n        let schedule_at = builder.add_type::<ScheduleAt>();\n        let sum_ty = AlgebraicType::sum([(\"v1\", AlgebraicType::U64), (\"v2\", AlgebraicType::Bool)]);\n        let sum_refty = builder.add_algebraic_type([], \"sum\", sum_ty, true);\n        builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                    (\"sum\", sum_refty.into()),\n                ]),\n                true,\n            )\n            // remove sequence\n            // remove unique constraint\n            .with_index(btree(0), \"id_index\")\n            // remove [\"id\", \"name\"] index\n            // add [\"id\", \"count\"] index\n            .with_index(btree([0, 2]), \"id_count_index\")\n            .finish();\n\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                    (\"freshness\", AlgebraicType::U32), // added column!\n                ]),\n                true,\n            )\n            // add column sequence\n            .with_column_sequence(0)\n            .with_default_column_value(3, AlgebraicValue::U32(5))\n            // change access\n            .with_access(TableAccess::Private)\n            .finish();\n\n        let deliveries_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"scheduled_id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at.clone()),\n                    (\"sum\", AlgebraicType::array(sum_refty.into())),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(0)\n            .with_index_no_accessor_name(btree(0))\n            // remove schedule def\n            .finish();\n\n        builder.add_reducer(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", AlgebraicType::Ref(deliveries_type))]),\n            None,\n        );\n\n        // Add a view and add its return type to the typespace\n        let view_return_ty = AlgebraicType::product([(\"a\", AlgebraicType::U64)]);\n        let view_return_ty_ref = builder.add_algebraic_type([], \"my_view_return\", view_return_ty, true);\n        builder.add_view(\n            \"my_view\",\n            0,\n            true,\n            true,\n            ProductType::from([(\"x\", AlgebraicType::U32)]),\n            AlgebraicType::option(AlgebraicType::Ref(view_return_ty_ref)),\n        );\n\n        let new_inspections_type = builder\n            .build_table_with_new_type(\n                \"Inspections\",\n                ProductType::from([\n                    (\"scheduled_id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at.clone()),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(0)\n            .with_index_no_accessor_name(btree(0))\n            // add schedule def\n            .with_schedule(\"perform_inspection\", 1)\n            .finish();\n\n        // add reducer.\n        builder.add_reducer(\n            \"perform_inspection\",\n            ProductType::from([(\"a\", AlgebraicType::Ref(new_inspections_type))]),\n            None,\n        );\n\n        // Add new table\n        builder\n            .build_table_with_new_type(\"Oranges\", ProductType::from([(\"id\", AlgebraicType::U32)]), true)\n            .with_index(btree(0), \"id_index\")\n            .with_column_sequence(0)\n            .with_unique_constraint(0)\n            .with_primary_key(0)\n            .finish();\n\n        builder.add_row_level_security(\"SELECT * FROM Bananas\");\n\n        builder\n            .finish()\n            .try_into()\n            .expect(\"new_def should be a valid database definition\")\n    }\n\n    #[test]\n    fn successful_auto_migration() {\n        let old_def = initial_module_def();\n        let new_def = updated_module_def();\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n\n        let apples = expect_identifier(\"Apples\");\n        let bananas = expect_identifier(\"Bananas\");\n        let deliveries = expect_identifier(\"Deliveries\");\n        let oranges = expect_identifier(\"Oranges\");\n        let my_view = expect_identifier(\"my_view\");\n\n        let bananas_sequence: RawIdentifier = \"Bananas_id_seq\".into();\n        let apples_unique_constraint: RawIdentifier = \"Apples_id_key\".into();\n        let apples_sequence: RawIdentifier = \"Apples_id_seq\".into();\n        let apples_id_name_index: RawIdentifier = \"Apples_id_name_idx_btree\".into();\n        let apples_id_count_index: RawIdentifier = \"Apples_id_count_idx_btree\".into();\n        let deliveries_schedule = expect_identifier(\"Deliveries_sched\");\n        let inspections_schedule = expect_identifier(\"Inspections_sched\");\n\n        assert!(plan.prechecks.is_sorted());\n\n        assert_eq!(plan.prechecks.len(), 1);\n        assert_eq!(\n            plan.prechecks[0],\n            AutoMigratePrecheck::CheckAddSequenceRangeValid(&bananas_sequence)\n        );\n        let sql_old = RawRowLevelSecurityDefV9 {\n            sql: \"SELECT * FROM Apples\".into(),\n        };\n\n        let sql_new = RawRowLevelSecurityDefV9 {\n            sql: \"SELECT * FROM Bananas\".into(),\n        };\n\n        let steps = &plan.steps[..];\n\n        assert!(steps.is_sorted());\n\n        assert!(\n            steps.contains(&AutoMigrateStep::RemoveSequence(&apples_sequence)),\n            \"{steps:?}\"\n        );\n        assert!(\n            steps.contains(&AutoMigrateStep::RemoveConstraint(&apples_unique_constraint)),\n            \"{steps:?}\"\n        );\n        assert!(\n            steps.contains(&AutoMigrateStep::RemoveIndex(&apples_id_name_index)),\n            \"{steps:?}\"\n        );\n        assert!(\n            steps.contains(&AutoMigrateStep::AddIndex(&apples_id_count_index)),\n            \"{steps:?}\"\n        );\n\n        assert!(steps.contains(&AutoMigrateStep::ChangeAccess(&bananas)), \"{steps:?}\");\n        assert!(\n            steps.contains(&AutoMigrateStep::AddSequence(&bananas_sequence)),\n            \"{steps:?}\"\n        );\n\n        assert!(steps.contains(&AutoMigrateStep::AddTable(&oranges)), \"{steps:?}\");\n\n        assert!(\n            steps.contains(&AutoMigrateStep::RemoveSchedule(&deliveries_schedule)),\n            \"{steps:?}\"\n        );\n        assert!(\n            steps.contains(&AutoMigrateStep::AddSchedule(&inspections_schedule)),\n            \"{steps:?}\"\n        );\n\n        assert!(\n            steps.contains(&AutoMigrateStep::RemoveRowLevelSecurity(&sql_old.sql)),\n            \"{steps:?}\"\n        );\n        assert!(\n            steps.contains(&AutoMigrateStep::AddRowLevelSecurity(&sql_new.sql)),\n            \"{steps:?}\"\n        );\n\n        assert!(steps.contains(&AutoMigrateStep::ChangeColumns(&apples)), \"{steps:?}\");\n        assert!(\n            steps.contains(&AutoMigrateStep::ChangeColumns(&deliveries)),\n            \"{steps:?}\"\n        );\n\n        assert!(steps.contains(&AutoMigrateStep::DisconnectAllUsers), \"{steps:?}\");\n        assert!(steps.contains(&AutoMigrateStep::AddColumns(&bananas)), \"{steps:?}\");\n        // Column is changed but it will not reflect in steps due to `AutoMigrateStep::AddColumns`\n        assert!(!steps.contains(&AutoMigrateStep::ChangeColumns(&bananas)), \"{steps:?}\");\n\n        assert!(steps.contains(&AutoMigrateStep::RemoveView(&my_view)), \"{steps:?}\");\n        assert!(steps.contains(&AutoMigrateStep::AddView(&my_view)), \"{steps:?}\");\n    }\n\n    #[test]\n    fn auto_migration_errors() {\n        let mut old_builder = RawModuleDefV9Builder::new();\n\n        let foo2_ty = AlgebraicType::sum([\n            (\"foo21\", AlgebraicType::Bool),\n            (\"foo22\", AlgebraicType::U32),\n            (\"foo23\", AlgebraicType::U32),\n        ]);\n        let foo2_refty = old_builder.add_algebraic_type([], \"foo2\", foo2_ty.clone(), true);\n        let foo_ty = AlgebraicType::product([\n            (\"foo1\", AlgebraicType::String),\n            (\"foo2\", foo2_refty.into()),\n            (\"foo3\", AlgebraicType::I32),\n        ]);\n        let foo_refty = old_builder.add_algebraic_type([], \"foo\", foo_ty.clone(), true);\n        let sum1_ty = AlgebraicType::sum([\n            (\"foo\", AlgebraicType::array(foo_refty.into())),\n            (\"bar\", AlgebraicType::U128),\n        ]);\n        let sum1_refty = old_builder.add_algebraic_type([], \"sum1\", sum1_ty.clone(), true);\n\n        let prod1_ty = AlgebraicType::product([\n            (\"baz\", AlgebraicType::Bool),\n            // We'll remove this field.\n            (\"qux\", AlgebraicType::Bool),\n        ]);\n        let prod1_refty = old_builder.add_algebraic_type([], \"prod1\", prod1_ty.clone(), true);\n\n        old_builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"sum1\", sum1_refty.into()),\n                    (\"prod1\", prod1_refty.into()),\n                    (\"count\", AlgebraicType::U16),\n                ]),\n                true,\n            )\n            .with_index(btree(0), \"id_index\")\n            .with_unique_constraint([1, 2])\n            .with_index_no_accessor_name(btree([1, 2]))\n            .with_type(TableType::User)\n            .finish();\n\n        old_builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                ]),\n                true,\n            )\n            .finish();\n\n        let old_def: ModuleDef = old_builder\n            .finish()\n            .try_into()\n            .expect(\"old_def should be a valid database definition\");\n        let resolve_old = |ty| old_def.typespace().with_type(ty).resolve_refs().unwrap();\n\n        let mut new_builder = RawModuleDefV9Builder::new();\n\n        // Remove variant `foo23` and rename variant `foo21` to `bad`.\n        let new_foo2_ty = AlgebraicType::sum([\n            (\"bad\", AlgebraicType::Bool),\n            // U32 -> U64\n            (\"foo22\", AlgebraicType::U64),\n        ]);\n        let new_foo2_refty = new_builder.add_algebraic_type([], \"foo2\", new_foo2_ty.clone(), true);\n        let new_foo_ty = AlgebraicType::product([\n            // Remove field `foo3` and rename `foo1` to `bad`.\n            (\"bad\", AlgebraicType::String),\n            (\"foo2\", new_foo2_refty.into()),\n        ]);\n        let new_foo_refty = new_builder.add_algebraic_type([], \"foo\", new_foo_ty.clone(), true);\n        let new_sum1_ty = AlgebraicType::sum([\n            // Remove variant `bar` and rename `foo` to `bad`.\n            (\"bad\", AlgebraicType::array(new_foo_refty.into())),\n        ]);\n        let new_sum1_refty = new_builder.add_algebraic_type([], \"sum1\", new_sum1_ty.clone(), true);\n\n        let new_prod1_ty = AlgebraicType::product([\n            // Removed field `qux` and renamed `baz` to `bad`.\n            (\"bad\", AlgebraicType::Bool),\n        ]);\n        let new_prod1_refty = new_builder.add_algebraic_type([], \"prod1\", new_prod1_ty.clone(), true);\n\n        new_builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"name\", AlgebraicType::U32), // change type of `name`\n                    (\"id\", AlgebraicType::U64),   // change order\n                    (\"sum1\", new_sum1_refty.into()),\n                    (\"prod1\", new_prod1_refty.into()),\n                    // remove count\n                    (\"weight\", AlgebraicType::U16), // add weight; we don't set a default, which makes this an error.\n                ]),\n                true,\n            )\n            .with_index(\n                btree(1),\n                \"id_index_new_accessor\", // change accessor name\n            )\n            .with_unique_constraint([1, 0])\n            .with_index_no_accessor_name(btree([1, 0]))\n            .with_unique_constraint(0)\n            .with_index_no_accessor_name(btree(0)) // add unique constraint\n            .with_type(TableType::System) // change type\n            .finish();\n\n        // Invalid row-level security queries can't be detected in the ponder_auto_migrate function, they\n        // are detected when executing the plan because they depend on the database state.\n        // new_builder.add_row_level_security(\"SELECT wrong\");\n\n        // remove Bananas\n        let new_def: ModuleDef = new_builder\n            .finish()\n            .try_into()\n            .expect(\"new_def should be a valid database definition\");\n        let resolve_new = |ty| new_def.typespace().with_type(ty).resolve_refs().unwrap();\n\n        let result = ponder_auto_migrate(&old_def, &new_def);\n\n        let apples = expect_identifier(\"Apples\");\n        let bananas = expect_identifier(\"Bananas\");\n\n        let apples_name_unique_constraint = \"Apples_name_key\";\n\n        let weight = expect_identifier(\"weight\");\n        let count = expect_identifier(\"count\");\n        let name = expect_identifier(\"name\");\n        let sum1 = expect_identifier(\"sum1\");\n        let prod1 = expect_identifier(\"prod1\");\n\n        expect_error_matching!(\n            result,\n            // This is an error because we didn't set a default value.\n            AutoMigrateError::AddColumn {\n                table,\n                column\n            } => table == &apples && column == &weight\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::RemoveColumn {\n                table,\n                column\n            } => table == &apples && column == &count\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ReorderTable { table } => table == &apples\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnType(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &name && type1.0 == AlgebraicType::String && type2.0 == AlgebraicType::U32\n        );\n\n        // Rename variant `foo21`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeRenamedVariant(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == foo2_ty && type2.0 == new_foo2_ty\n        );\n\n        // foo22: U32 -> U64.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnType(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == AlgebraicType::U32 && type2.0 == AlgebraicType::U64\n        );\n\n        // Remove variant `foo23`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeFewerVariants(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == foo2_ty && type2.0 == new_foo2_ty\n        );\n\n        // Size of inner sum changed.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeSizeMismatch(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == foo2_ty && type2.0 == new_foo2_ty\n        );\n\n        // Align of inner sum changed.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeAlignMismatch(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == foo2_ty && type2.0 == new_foo2_ty\n        );\n\n        // Rename field `foo1`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeRenamedField(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&foo_ty) && type2.0 == resolve_new(&new_foo_ty)\n        );\n\n        // Remove field `foo3`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeWithinColumnTypeFewerFields(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&foo_ty) && type2.0 == resolve_new(&new_foo_ty)\n        );\n\n        // Rename variant `bar`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeRenamedVariant(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&sum1_ty) && type2.0 == resolve_new(&new_sum1_ty)\n        );\n\n        // Remove variant `bar`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeFewerVariants(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&sum1_ty) && type2.0 == resolve_new(&new_sum1_ty)\n        );\n\n        // Size of outer sum changed.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeSizeMismatch(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&sum1_ty) && type2.0 == resolve_new(&new_sum1_ty)\n        );\n\n        // Align of outer sum changed.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeAlignMismatch(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &sum1\n            && type1.0 == resolve_old(&sum1_ty) && type2.0 == resolve_new(&new_sum1_ty)\n        );\n\n        // Rename field `baz`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeRenamedField(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &prod1\n            && type1.0 == prod1_ty && type2.0 == new_prod1_ty\n        );\n\n        // Remove field `qux`.\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeColumnTypeFewerFields(ChangeColumnTypeParts {\n                table,\n                column,\n                type1,\n                type2\n            }) => table == &apples && column == &prod1\n            && type1.0 == prod1_ty && type2.0 == new_prod1_ty\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::AddUniqueConstraint { constraint } => &constraint[..] == apples_name_unique_constraint\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeTableType { table, type1, type2 } => table == &apples && type1 == &TableType::User && type2 == &TableType::System\n        );\n\n        expect_error_matching!(\n            result,\n            AutoMigrateError::RemoveTable { table } => table == &bananas\n        );\n\n        let apples_id_index = \"Apples_id_idx_btree\";\n        let accessor_old = expect_identifier(\"id_index\");\n        let accessor_new = expect_identifier(\"id_index_new_accessor\");\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeIndexAccessor {\n                index,\n                old_accessor,\n                new_accessor\n            } => &index[..] == apples_id_index && old_accessor.as_ref() == Some(&accessor_old) && new_accessor.as_ref() == Some(&accessor_new)\n        );\n\n        // It is not currently possible to test for `ChangeUniqueConstraint`, because unique constraint names are now generated during validation,\n        // and are determined by their columns and table name. So it's impossible to create a unique constraint with the same name\n        // but different columns from an old one.\n        // We've left the check in, just in case this changes in the future.\n    }\n    #[test]\n    fn print_empty_to_populated_schema_migration() {\n        // Start with completely empty schema\n        let old_builder = RawModuleDefV9Builder::new();\n        let old_def: ModuleDef = old_builder\n            .finish()\n            .try_into()\n            .expect(\"old_def should be a valid database definition\");\n\n        let new_def = initial_module_def();\n        let plan = ponder_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n\n        insta::assert_snapshot!(\n            \"empty_to_populated_migration\",\n            plan.pretty_print(PrettyPrintStyle::AnsiColor)\n                .expect(\"should pretty print\")\n        );\n    }\n\n    #[test]\n    fn print_supervised_migration() {\n        let old_def = initial_module_def();\n        let new_def = updated_module_def();\n        let plan = ponder_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n\n        insta::assert_snapshot!(\n            \"updated pretty print\",\n            plan.pretty_print(PrettyPrintStyle::AnsiColor)\n                .expect(\"should pretty print\")\n        );\n    }\n\n    #[test]\n    fn no_color_print_supervised_migration() {\n        let old_def = initial_module_def();\n        let new_def = updated_module_def();\n        let plan = ponder_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n\n        insta::assert_snapshot!(\n            \"updated pretty print no color\",\n            plan.pretty_print(PrettyPrintStyle::NoColor)\n                .expect(\"should pretty print\")\n        );\n    }\n\n    #[test]\n    fn add_view() {\n        let old_def = create_module_def(|_| {});\n        let new_def = create_module_def(|builder| {\n            let return_type_ref = builder.add_algebraic_type(\n                [],\n                \"my_view_return_type\",\n                AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                true,\n            );\n            builder.add_view(\n                \"my_view\",\n                0,\n                true,\n                true,\n                ProductType::from([(\"x\", AlgebraicType::U32)]),\n                AlgebraicType::array(AlgebraicType::Ref(return_type_ref)),\n            );\n        });\n\n        let my_view = expect_identifier(\"my_view\");\n\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        let steps = &plan.steps[..];\n\n        assert!(!plan.disconnects_all_users(), \"{plan:#?}\");\n        assert!(steps.contains(&AutoMigrateStep::AddView(&my_view)), \"{steps:?}\");\n        assert!(!steps.contains(&AutoMigrateStep::RemoveView(&my_view)), \"{steps:?}\");\n    }\n\n    #[test]\n    fn remove_view() {\n        let old_def = create_module_def(|builder| {\n            let return_type_ref = builder.add_algebraic_type(\n                [],\n                \"my_view_return_type\",\n                AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                true,\n            );\n            builder.add_view(\n                \"my_view\",\n                0,\n                true,\n                true,\n                ProductType::from([(\"x\", AlgebraicType::U32)]),\n                AlgebraicType::array(AlgebraicType::Ref(return_type_ref)),\n            );\n        });\n        let new_def = create_module_def(|_| {});\n\n        let my_view = expect_identifier(\"my_view\");\n\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        let steps = &plan.steps[..];\n\n        assert!(plan.disconnects_all_users(), \"{plan:#?}\");\n        assert!(steps.contains(&AutoMigrateStep::RemoveView(&my_view)), \"{steps:?}\");\n        assert!(!steps.contains(&AutoMigrateStep::AddView(&my_view)), \"{steps:?}\");\n    }\n\n    #[test]\n    fn migrate_view_recompute() {\n        struct TestCase {\n            desc: &'static str,\n            old_def: ModuleDef,\n            new_def: ModuleDef,\n        }\n\n        for TestCase {\n            desc: name,\n            old_def,\n            new_def,\n        } in [\n            TestCase {\n                desc: \"Return `Vec<T>` instead of `Option<T>`\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::array(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"No change; recompute view\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n        ] {\n            let my_view = expect_identifier(\"my_view\");\n\n            let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n            let steps = &plan.steps[..];\n\n            assert!(!plan.disconnects_all_users(), \"{name}, plan: {plan:#?}\");\n\n            assert!(\n                steps.contains(&AutoMigrateStep::UpdateView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n            assert!(\n                !steps.contains(&AutoMigrateStep::AddView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n            assert!(\n                !steps.contains(&AutoMigrateStep::RemoveView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n        }\n    }\n\n    #[test]\n    fn migrate_view_disconnect_clients() {\n        struct TestCase {\n            desc: &'static str,\n            old_def: ModuleDef,\n            new_def: ModuleDef,\n        }\n\n        for TestCase {\n            desc: name,\n            old_def,\n            new_def,\n        } in [\n            TestCase {\n                desc: \"Change context parameter\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        false,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Add parameter\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32), (\"y\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Remove parameter\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32), (\"y\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Reorder parameters\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32), (\"y\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"y\", AlgebraicType::U32), (\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Change parameter type\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::String)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Add column\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Remove column\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Reorder columns\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"b\", AlgebraicType::U64), (\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n            TestCase {\n                desc: \"Change column type\",\n                old_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::U64)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n                new_def: create_module_def(|builder| {\n                    let return_type_ref = builder.add_algebraic_type(\n                        [],\n                        \"my_view_return_type\",\n                        AlgebraicType::product([(\"a\", AlgebraicType::String)]),\n                        true,\n                    );\n                    builder.add_view(\n                        \"my_view\",\n                        0,\n                        true,\n                        true,\n                        ProductType::from([(\"x\", AlgebraicType::U32)]),\n                        AlgebraicType::option(AlgebraicType::Ref(return_type_ref)),\n                    );\n                }),\n            },\n        ] {\n            let my_view = expect_identifier(\"my_view\");\n\n            let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n            let steps = &plan.steps[..];\n\n            assert!(plan.disconnects_all_users(), \"{name}, plan: {plan:?}\");\n\n            assert!(\n                steps.contains(&AutoMigrateStep::AddView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n            assert!(\n                steps.contains(&AutoMigrateStep::RemoveView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n            assert!(\n                !steps.contains(&AutoMigrateStep::UpdateView(&my_view)),\n                \"{name}, steps: {steps:?}\"\n            );\n        }\n    }\n\n    #[test]\n    fn change_rls_disconnect_clients() {\n        let old_def = create_module_def(|_builder| {});\n\n        let new_def = create_module_def(|_builder| {});\n\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        assert!(!plan.disconnects_all_users(), \"{plan:#?}\");\n\n        let old_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT true;\");\n        });\n        let new_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT false;\");\n        });\n\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        assert!(plan.disconnects_all_users(), \"{plan:#?}\");\n\n        let old_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT true;\");\n        });\n\n        let new_def = create_module_def(|_builder| {\n            // Remove RLS\n        });\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        assert!(plan.disconnects_all_users(), \"{plan:#?}\");\n\n        let old_def = create_module_def(|_builder| {});\n\n        let new_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT false;\");\n        });\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        assert!(plan.disconnects_all_users(), \"{plan:#?}\");\n\n        let old_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT true;\");\n        });\n\n        let new_def = create_module_def(|builder| {\n            builder.add_row_level_security(\"SELECT true;\");\n        });\n        let plan = ponder_auto_migrate(&old_def, &new_def).expect(\"auto migration should succeed\");\n        assert!(!plan.disconnects_all_users(), \"{plan:#?}\");\n    }\n\n    fn create_v10_module_def(build_module: impl Fn(&mut v10::RawModuleDefV10Builder)) -> ModuleDef {\n        let mut builder = v10::RawModuleDefV10Builder::new();\n        build_module(&mut builder);\n        builder\n            .finish()\n            .try_into()\n            .expect(\"should be a valid module definition\")\n    }\n\n    #[test]\n    fn test_change_event_flag_rejected() {\n        // non-event → event\n        let old = create_v10_module_def(|builder| {\n            builder\n                .build_table_with_new_type(\"Events\", ProductType::from([(\"id\", AlgebraicType::U64)]), true)\n                .finish();\n        });\n        let new = create_v10_module_def(|builder| {\n            builder\n                .build_table_with_new_type(\"events\", ProductType::from([(\"id\", AlgebraicType::U64)]), true)\n                .with_event(true)\n                .finish();\n        });\n\n        let result = ponder_auto_migrate(&old, &new);\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeTableEventFlag { table } => &table[..] == \"events\"\n        );\n\n        // event → non-event (reverse direction)\n        let result = ponder_auto_migrate(&new, &old);\n        expect_error_matching!(\n            result,\n            AutoMigrateError::ChangeTableEventFlag { table } => &table[..] == \"events\"\n        );\n    }\n\n    #[test]\n    fn test_same_event_flag_accepted() {\n        // Both event → no error\n        let old = create_v10_module_def(|builder| {\n            builder\n                .build_table_with_new_type(\"Events\", ProductType::from([(\"id\", AlgebraicType::U64)]), true)\n                .with_event(true)\n                .finish();\n        });\n        let new = create_v10_module_def(|builder| {\n            builder\n                .build_table_with_new_type(\"Events\", ProductType::from([(\"id\", AlgebraicType::U64)]), true)\n                .with_event(true)\n                .finish();\n        });\n\n        ponder_auto_migrate(&old, &new).expect(\"same event flag should succeed\");\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def/deserialize.rs",
    "content": "//! Helpers to allow deserializing data using a ReducerDef.\n\nuse crate::{\n    def::{ProcedureDef, ReducerDef, ViewDef},\n    identifier::Identifier,\n};\nuse spacetimedb_lib::{\n    sats::{self, de, impl_serialize, ser, ProductValue},\n    ProductType,\n};\n\n/// Wrapper around a function def that allows deserializing to a [`ProductValue`] at the type of the def's parameter [`ProductType`].\n///\n/// Sensible instantiations for `Def` are [`ProcedureDef`], [`ReducerDef`] and [`ViewDef`].\npub struct ArgsSeed<'a, Def>(pub sats::WithTypespace<'a, Def>);\n\n// Manual impls of traits rather than derives,\n// 'cause derives are always constrained on all type parameters,\n// even though `ArgsSeed<Def: ?Copy>: Copy` in our case.\nimpl<Def> Clone for ArgsSeed<'_, Def> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\nimpl<Def> Copy for ArgsSeed<'_, Def> {}\n\npub trait FunctionDef {\n    fn params(&self) -> &ProductType;\n    fn name(&self) -> &Identifier;\n}\n\nimpl FunctionDef for ReducerDef {\n    fn params(&self) -> &ProductType {\n        &self.params\n    }\n    fn name(&self) -> &Identifier {\n        self.name.as_identifier()\n    }\n}\n\nimpl FunctionDef for ProcedureDef {\n    fn params(&self) -> &ProductType {\n        &self.params\n    }\n    fn name(&self) -> &Identifier {\n        &self.name\n    }\n}\n\nimpl FunctionDef for ViewDef {\n    fn params(&self) -> &ProductType {\n        &self.params\n    }\n    fn name(&self) -> &Identifier {\n        &self.name\n    }\n}\n\nimpl<Def: FunctionDef> ArgsSeed<'_, Def> {\n    pub fn name(&self) -> &Identifier {\n        self.0.ty().name()\n    }\n\n    pub fn params(&self) -> &ProductType {\n        self.0.ty().params()\n    }\n}\n\nimpl<'de, Def: FunctionDef> de::DeserializeSeed<'de> for ArgsSeed<'_, Def> {\n    type Output = ProductValue;\n\n    fn deserialize<D: de::Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {\n        deserializer.deserialize_product(self)\n    }\n}\n\nimpl<'de, Def: FunctionDef> de::ProductVisitor<'de> for ArgsSeed<'_, Def> {\n    type Output = ProductValue;\n\n    fn product_name(&self) -> Option<&str> {\n        Some(self.0.ty().name())\n    }\n\n    fn product_len(&self) -> usize {\n        self.0.ty().params().elements.len()\n    }\n\n    fn product_kind(&self) -> de::ProductKind {\n        de::ProductKind::ReducerArgs\n    }\n\n    fn visit_seq_product<A: de::SeqProductAccess<'de>>(self, tup: A) -> Result<Self::Output, A::Error> {\n        de::visit_seq_product(self.0.map(|r| &*r.params().elements), &self, tup)\n    }\n\n    fn visit_named_product<A: de::NamedProductAccess<'de>>(self, tup: A) -> Result<Self::Output, A::Error> {\n        de::visit_named_product(self.0.map(|r| &*r.params().elements), &self, tup)\n    }\n}\n\npub struct ReducerArgsWithSchema<'a> {\n    value: &'a ProductValue,\n    ty: sats::WithTypespace<'a, ReducerDef>,\n}\nimpl_serialize!([] ReducerArgsWithSchema<'_>, (self, ser) => {\n    use itertools::Itertools;\n    use ser::SerializeSeqProduct;\n    let mut seq = ser.serialize_seq_product(self.value.elements.len())?;\n    for (value, elem) in self.value.elements.iter().zip_eq(&*self.ty.ty().params.elements) {\n        seq.serialize_element(&self.ty.with(&elem.algebraic_type).with_value(value))?;\n    }\n    seq.end()\n});\n"
  },
  {
    "path": "crates/schema/src/def/error.rs",
    "content": "use crate::identifier::Identifier;\nuse crate::relation::{FieldName, Header};\nuse crate::table_name::TableName;\nuse derive_more::Display;\nuse spacetimedb_lib::db::raw_def::IndexType;\nuse spacetimedb_primitives::{ColId, ColList};\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::satn::Satn as _;\nuse spacetimedb_sats::{buffer, AlgebraicType, AlgebraicValue};\nuse std::fmt;\nuse std::string::FromUtf8Error;\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum TypeError {\n    #[error(\"The type of `{{value.to_satns()}}` cannot be inferred\")]\n    CannotInferType { value: AlgebraicValue },\n}\n\n#[derive(Error, Debug, Clone)]\npub enum DecodeError {\n    #[error(\"Decode UTF8: {0}\")]\n    Utf8(#[from] FromUtf8Error),\n    #[error(\"AlgebraicType::decode: Unknown: {0}\")]\n    AlgebraicTypeUnknown(u8),\n    #[error(\"AlgebraicType::decode: Byte array has invalid length: {0:?}\")]\n    AlgebraicType(usize),\n    #[error(\"SumType::decode: Byte array has invalid length: {0:?}\")]\n    SumType(usize),\n    #[error(\"ProductType::decode: Byte array has invalid length: {0:?}\")]\n    ProductType(usize),\n    #[error(\"ProductTypeElement::decode: Byte array has invalid length: {0:?}\")]\n    ProductTypeElement(usize),\n    #[error(\"AlgebraicValue::decode: byte array length not long enough to decode {0:?}\")]\n    AlgebraicValue(AlgebraicType),\n    #[error(\"AlgebraicValue::decode: byte array length not long enough to get length of {0:?}\")]\n    AlgebraicValueGetLength(AlgebraicType),\n    #[error(\n    \"AlgebraicValue::decode: buffer has no room to decode any more elements from this {kind:?}. (len: {len} <= read:{read})\"\n    )]\n    AlgebraicValueRoom {\n        kind: AlgebraicType,\n        len: usize,\n        read: usize,\n    },\n    #[error(\"AlgebraicValue::decode: Cannot decode {kind:?}, buffer not long enough. (len: {len}, read:{read})\")]\n    TypeBufferSmall {\n        kind: AlgebraicType,\n        len: usize,\n        read: usize,\n    },\n    #[error(\n        \"AlgebraicValue::decode: byte array length not long enough to decode {kind:?}. (expect: {expect}, read:{read})\"\n    )]\n    TypeTooSmall {\n        kind: AlgebraicType,\n        expect: usize,\n        read: usize,\n    },\n    #[error(\"EnumValue::decode: Byte array length is invalid.\")]\n    EnumValue,\n}\n\n#[derive(Error, Debug, Clone)]\npub enum LibError {\n    #[error(\"DecodeError: {0}\")]\n    Decode(#[from] DecodeError),\n    #[error(\"BufferError: {0}\")]\n    Buffer(#[from] buffer::DecodeError),\n    #[error(transparent)]\n    TupleFieldInvalid(#[from] InvalidFieldError),\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum AuthError {\n    #[error(\"Table `{named}` is private\")]\n    TablePrivate { named: String },\n    #[error(\"Index `{named}` is private\")]\n    IndexPrivate { named: String },\n    #[error(\"Sequence `{named}` is private\")]\n    SequencePrivate { named: String },\n    #[error(\"Insufficient privileges to perform the requested operation\")]\n    InsuffientPrivileges,\n    #[error(\"Constraint `{named}` is private\")]\n    ConstraintPrivate { named: String },\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum RelationError {\n    #[error(\"Field `{1}` not found. Must be one of {0}\")]\n    FieldNotFound(Header, FieldName),\n    #[error(\"Field `{0}` fail to infer the type: {1}\")]\n    TypeInference(FieldName, TypeError),\n    #[error(\"Field with value `{}` was not a `bool`\", val.to_satn())]\n    NotBoolValue { val: AlgebraicValue },\n    #[error(\"Field `{field}` was expected to be `bool` but is `{}`\", ty.to_satn())]\n    NotBoolType { field: FieldName, ty: AlgebraicType },\n    #[error(\"Field declaration only support `table.field` or `field`. It gets instead `{0}`\")]\n    FieldPathInvalid(String),\n}\n\n#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Display)]\npub enum DefType {\n    Column,\n    Index,\n    Sequence,\n    Constraint,\n}\n\n#[derive(thiserror::Error, Debug, PartialEq)]\npub enum SchemaError {\n    #[error(\"Multiple primary columns defined for table: {table} columns: {pks:?}\")]\n    MultiplePrimaryKeys { table: Box<str>, pks: Vec<String> },\n    #[error(\"{ty} {name} columns `{columns:?}` not found  in table `{table}`\")]\n    ColumnsNotFound {\n        name: RawIdentifier,\n        table: TableName,\n        columns: Vec<ColId>,\n        ty: DefType,\n    },\n    #[error(\"table `{table}` {ty} should have name. {ty} id: {id}\")]\n    EmptyName { table: TableName, ty: DefType, id: u32 },\n    #[error(\"table `{table}` have `Constraints::unset()` for columns: {columns:?}\")]\n    ConstraintUnset {\n        table: TableName,\n        name: RawIdentifier,\n        columns: ColList,\n    },\n    #[error(\"Attempt to define a column with more than 1 auto_inc sequence: Table: `{table}`, Field: `{field}`\")]\n    OneAutoInc { table: TableName, field: Identifier },\n    #[error(\"Only Btree Indexes are supported: Table: `{table}`, Index: `{index}` is a `{index_type}`\")]\n    OnlyBtree {\n        table: TableName,\n        index: RawIdentifier,\n        index_type: IndexType,\n    },\n}\n\n#[derive(thiserror::Error, Debug, PartialEq)]\npub struct SchemaErrors(pub Vec<SchemaError>);\n\nimpl fmt::Display for SchemaErrors {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_list().entries(self.0.iter()).finish()\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def/validate/v10.rs",
    "content": "use spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_lib::bsatn::Deserializer;\nuse spacetimedb_lib::db::raw_def::v10::*;\nuse spacetimedb_lib::db::view::{extract_view_return_product_type_ref, ViewKind};\nuse spacetimedb_lib::de::DeserializeSeed as _;\nuse spacetimedb_sats::{Typespace, WithTypespace};\n\nuse crate::def::validate::v9::{\n    check_function_names_are_unique, check_scheduled_functions_exist, generate_schedule_name,\n    generate_unique_constraint_name, identifier, CoreValidator, TableValidator, ViewValidator,\n};\nuse crate::def::*;\nuse crate::error::ValidationError;\nuse crate::type_for_generate::ProductTypeDef;\nuse crate::{def::validate::Result, error::TypeLocation};\n\n// Utitility struct to look up canonical names for tables, functions, and indexes based on the\n// explicit names provided in the `RawModuleDefV10`.\n#[derive(Default)]\npub struct ExplicitNamesLookup {\n    pub tables: HashMap<RawIdentifier, RawIdentifier>,\n    pub functions: HashMap<RawIdentifier, RawIdentifier>,\n    pub indexes: HashMap<RawIdentifier, RawIdentifier>,\n}\n\nimpl ExplicitNamesLookup {\n    fn new(ex: ExplicitNames) -> Self {\n        let mut tables = HashMap::default();\n        let mut functions = HashMap::default();\n        let mut indexes = HashMap::default();\n\n        for entry in ex.into_entries() {\n            match entry {\n                ExplicitNameEntry::Table(m) => {\n                    tables.insert(m.source_name, m.canonical_name);\n                }\n                ExplicitNameEntry::Function(m) => {\n                    functions.insert(m.source_name, m.canonical_name);\n                }\n                ExplicitNameEntry::Index(m) => {\n                    indexes.insert(m.source_name, m.canonical_name);\n                }\n                _ => {}\n            }\n        }\n\n        ExplicitNamesLookup {\n            tables,\n            functions,\n            indexes,\n        }\n    }\n}\n\n/// Internal representation of case conversion policy, used during validation to determine how to\n/// apply case conversion to names.\n#[derive(Clone, Copy, Debug)]\npub(crate) enum ValidationCase {\n    None,\n    SnakeCase,\n    CamelCase,\n}\n\nimpl From<CaseConversionPolicy> for ValidationCase {\n    fn from(policy: CaseConversionPolicy) -> Self {\n        match policy {\n            CaseConversionPolicy::None => ValidationCase::None,\n            CaseConversionPolicy::SnakeCase => ValidationCase::SnakeCase,\n            _ => panic!(\"Unsupported case conversion policy: {:?}\", policy),\n        }\n    }\n}\n/// Validate a `RawModuleDefV9` and convert it into a `ModuleDef`,\n/// or return a stream of errors if the definition is invalid.\npub fn validate(def: RawModuleDefV10) -> Result<ModuleDef> {\n    let mut typespace = def.typespace().cloned().unwrap_or_else(|| Typespace::EMPTY.clone());\n    let known_type_definitions = def.types().into_iter().flatten().map(|def| def.ty);\n    let case_policy = def.case_conversion_policy().into();\n    let explicit_names = def\n        .explicit_names()\n        .cloned()\n        .map(ExplicitNamesLookup::new)\n        .unwrap_or_default();\n\n    // Original `typespace` needs to be preserved to be assign `accesor_name`s to columns.\n    let typespace_with_accessor_names = typespace.clone();\n    // Apply case conversion to `typespace`.\n    CoreValidator::typespace_case_conversion(case_policy, &mut typespace);\n\n    let mut validator = ModuleValidatorV10 {\n        core: CoreValidator {\n            typespace: &typespace,\n            stored_in_table_def: Default::default(),\n            type_namespace: Default::default(),\n            lifecycle_reducers: Default::default(),\n            typespace_for_generate: TypespaceForGenerate::builder(\n                &typespace_with_accessor_names,\n                known_type_definitions,\n            ),\n            case_policy,\n            explicit_names,\n        },\n    };\n\n    // Important general note:\n    // This file uses the `ErrorStream` combinator to return *multiple errors\n    // at once* when validating a definition.\n    // The general pattern is that we use `collect_all_errors` when building\n    // a collection, and `combine_errors` when we have multiple\n    // things to validate that are independent of each other.\n    // We try to avoid using `?` until the end of a function, after we've called\n    // `combine_errors` or `collect_all_errors` on all the things we need to validate.\n    // Sometimes it is unavoidable to use `?` early and this should be commented on.\n\n    let reducers = def\n        .reducers()\n        .cloned()\n        .into_iter()\n        .flatten()\n        .map(|reducer| validator.validate_reducer_def(reducer))\n        // Collect into a `Vec` first to preserve duplicate names.\n        // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`.\n        .collect_all_errors::<Vec<_>>();\n\n    let procedures = def\n        .procedures()\n        .cloned()\n        .into_iter()\n        .flatten()\n        .map(|procedure| {\n            validator\n                .validate_procedure_def(procedure)\n                .map(|procedure_def| (procedure_def.name.clone(), procedure_def))\n        })\n        // Collect into a `Vec` first to preserve duplicate names.\n        // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`.\n        .collect_all_errors::<Vec<_>>();\n\n    let views = def\n        .views()\n        .cloned()\n        .into_iter()\n        .flatten()\n        .map(|view| {\n            validator\n                .validate_view_def(view, &typespace_with_accessor_names)\n                .map(|view_def| (view_def.name.clone(), view_def))\n        })\n        .collect_all_errors();\n\n    let tables = def\n        .tables()\n        .cloned()\n        .into_iter()\n        .flatten()\n        .map(|table| {\n            validator\n                .validate_table_def(table, &typespace_with_accessor_names)\n                .map(|table_def| (table_def.name.clone(), table_def))\n        })\n        .collect_all_errors();\n\n    let mut refmap = HashMap::default();\n    let types = def\n        .types()\n        .cloned()\n        .into_iter()\n        .flatten()\n        .map(|ty| {\n            validator.core.validate_type_def(ty.into()).map(|type_def| {\n                refmap.insert(type_def.ty, type_def.accessor_name.clone());\n                (type_def.accessor_name.clone(), type_def)\n            })\n        })\n        .collect_all_errors::<HashMap<_, _>>();\n\n    // Validate schedules - they need the validated tables to exist first\n    let schedules = tables\n        .as_ref()\n        .ok()\n        .map(|tables_map| {\n            def.schedules()\n                .cloned()\n                .into_iter()\n                .flatten()\n                .map(|schedule| validator.validate_schedule_def(schedule, tables_map))\n                .collect_all_errors::<Vec<_>>()\n        })\n        .unwrap_or_else(|| Ok(Vec::new()));\n\n    // Validate lifecycle reducers - they reference reducers by name\n    let lifecycle_validations = reducers\n        .as_ref()\n        .ok()\n        .map(|reducers_vec| {\n            def.lifecycle_reducers()\n                .cloned()\n                .into_iter()\n                .flatten()\n                .map(|lifecycle_def| {\n                    let function_name = ReducerName::new(\n                        validator\n                            .core\n                            .resolve_function_ident(lifecycle_def.function_name.clone())?,\n                    );\n\n                    let (pos, _) = reducers_vec\n                        .iter()\n                        .enumerate()\n                        .find(|(_, (_, r))| r.name == function_name)\n                        .ok_or_else(|| ValidationError::LifecycleWithoutReducer {\n                            lifecycle: lifecycle_def.lifecycle_spec,\n                        })?;\n\n                    let reducer_id = ReducerId(pos as u32);\n\n                    validator.validate_lifecycle_reducer(lifecycle_def.clone(), reducer_id)?;\n\n                    Ok((reducer_id, lifecycle_def.lifecycle_spec))\n                })\n                .collect_all_errors::<Vec<_>>()\n        })\n        .unwrap_or_else(|| Ok(Vec::new()));\n    // Combine all validation results\n    let tables_types_reducers_procedures_views = (\n        tables,\n        types,\n        reducers,\n        procedures,\n        views,\n        schedules,\n        lifecycle_validations,\n    )\n        .combine_errors()\n        .and_then(\n            |(mut tables, types, reducers, procedures, views, schedules, lifecycles)| {\n                let (mut reducers, mut procedures, mut views) =\n                    check_function_names_are_unique(reducers, procedures, views)?;\n                // Attach lifecycles to their respective reducers\n                attach_lifecycles_to_reducers(&mut reducers, lifecycles)?;\n\n                // Attach schedules to their respective tables\n                attach_schedules_to_tables(&mut tables, schedules)?;\n\n                check_scheduled_functions_exist(&mut tables, &reducers, &procedures)?;\n                change_scheduled_functions_and_lifetimes_visibility(&tables, &mut reducers, &mut procedures)?;\n                assign_query_view_primary_keys(&tables, &mut views);\n\n                Ok((tables, types, reducers, procedures, views))\n            },\n        );\n    let CoreValidator {\n        stored_in_table_def,\n        typespace_for_generate,\n        lifecycle_reducers,\n        ..\n    } = validator.core;\n\n    let row_level_security_raw = def\n        .row_level_security()\n        .into_iter()\n        .flatten()\n        .map(|rls| (rls.sql.clone(), rls.to_owned()))\n        .collect();\n\n    let (tables, types, reducers, procedures, views) =\n        (tables_types_reducers_procedures_views).map_err(|errors| errors.sort_deduplicate())?;\n\n    let typespace_for_generate = typespace_for_generate.finish();\n\n    Ok(ModuleDef {\n        tables,\n        reducers,\n        views,\n        types,\n        typespace,\n        typespace_for_generate,\n        stored_in_table_def,\n        refmap,\n        row_level_security_raw,\n        lifecycle_reducers,\n        procedures,\n        raw_module_def_version: RawModuleDefVersion::V10,\n    })\n}\n\n/// Change the visibility of scheduled functions and lifecycle reducers to Internal.\n///\nfn change_scheduled_functions_and_lifetimes_visibility(\n    tables: &HashMap<Identifier, TableDef>,\n    reducers: &mut IndexMap<Identifier, ReducerDef>,\n    procedures: &mut IndexMap<Identifier, ProcedureDef>,\n) -> Result<()> {\n    for sched_def in tables.iter().filter_map(|(_, t)| t.schedule.as_ref()) {\n        match sched_def.function_kind {\n            FunctionKind::Reducer => {\n                let def = reducers.get_mut(&sched_def.function_name).ok_or_else(|| {\n                    ValidationError::MissingScheduledFunction {\n                        schedule: sched_def.name.clone(),\n                        function: sched_def.function_name.clone(),\n                    }\n                })?;\n\n                def.visibility = crate::def::FunctionVisibility::Private;\n            }\n\n            FunctionKind::Procedure => {\n                let def = procedures.get_mut(&sched_def.function_name).ok_or_else(|| {\n                    ValidationError::MissingScheduledFunction {\n                        schedule: sched_def.name.clone(),\n                        function: sched_def.function_name.clone(),\n                    }\n                })?;\n\n                def.visibility = crate::def::FunctionVisibility::Private;\n            }\n\n            FunctionKind::Unknown => {}\n        }\n    }\n\n    for red_def in reducers.iter_mut().map(|(_, r)| r) {\n        if red_def.lifecycle.is_some() {\n            red_def.visibility = crate::def::FunctionVisibility::Private;\n        }\n    }\n\n    Ok(())\n}\n\nstruct ModuleValidatorV10<'a> {\n    core: CoreValidator<'a>,\n}\n\nimpl<'a> ModuleValidatorV10<'a> {\n    fn validate_table_def(&mut self, table: RawTableDefV10, typespace_with_accessor: &Typespace) -> Result<TableDef> {\n        let RawTableDefV10 {\n            source_name: raw_table_name,\n            product_type_ref,\n            primary_key,\n            indexes,\n            constraints,\n            sequences,\n            table_type,\n            table_access,\n            default_values,\n            is_event,\n        } = table;\n\n        let product_type: &ProductType = self\n            .core\n            .typespace\n            .get(product_type_ref)\n            .and_then(AlgebraicType::as_product)\n            .ok_or_else(|| {\n                ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                    table: raw_table_name.clone(),\n                    ref_: product_type_ref,\n                })\n            })?;\n\n        let mut table_validator =\n            TableValidator::new(raw_table_name.clone(), product_type_ref, product_type, &mut self.core)?;\n\n        let table_ident = table_validator.table_ident.clone();\n\n        // Validate columns first\n        let mut columns: Vec<ColumnDef> = (0..product_type.elements.len())\n            .map(|id| {\n                let product_type_for_column: &ProductType = typespace_with_accessor\n                    .get(product_type_ref)\n                    .and_then(AlgebraicType::as_product)\n                    .ok_or_else(|| {\n                        ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                            table: raw_table_name.clone(),\n                            ref_: product_type_ref,\n                        })\n                    })?;\n\n                table_validator.validate_column_def(id.into(), product_type_for_column)\n            })\n            .collect_all_errors()?;\n\n        let indexes = indexes\n            .into_iter()\n            .map(|index| {\n                table_validator\n                    .validate_index_def_v10(index)\n                    .map(|index| (index.name.clone(), index))\n            })\n            .collect_all_errors::<StrMap<_>>();\n\n        let constraints_primary_key = constraints\n            .into_iter()\n            .map(|constraint| {\n                table_validator\n                    .validate_constraint_def(constraint.into(), |_source_name, cols| {\n                        generate_unique_constraint_name(&table_ident, product_type, cols)\n                    })\n                    .map(|constraint| (constraint.name.clone(), constraint))\n            })\n            .collect_all_errors()\n            .and_then(|constraints: StrMap<ConstraintDef>| {\n                table_validator.validate_primary_key(constraints, primary_key)\n            });\n\n        let constraints_backed_by_indices =\n            if let (Ok((constraints, _)), Ok(indexes)) = (&constraints_primary_key, &indexes) {\n                constraints\n                    .values()\n                    .filter_map(|c| c.data.unique_columns().map(|cols| (c, cols)))\n                    .filter(|(_, unique_cols)| {\n                        !indexes\n                            .values()\n                            .any(|i| ColSet::from(i.algorithm.columns()) == **unique_cols)\n                    })\n                    .map(|(c, cols)| {\n                        let constraint = c.name.clone();\n                        let columns = cols.clone();\n                        Err(ValidationError::UniqueConstraintWithoutIndex { constraint, columns }.into())\n                    })\n                    .collect_all_errors()\n            } else {\n                Ok(())\n            };\n\n        let sequences = sequences\n            .into_iter()\n            .map(|sequence| {\n                table_validator\n                    .validate_sequence_def(sequence.into())\n                    .map(|sequence| (sequence.name.clone(), sequence))\n            })\n            .collect_all_errors();\n\n        // `raw_table_name` should also go in global namespace as it will be used as alias\n        let raw_table_name = table_validator.add_to_global_namespace(raw_table_name.clone())?;\n\n        let name = {\n            let name = table_validator\n                .module_validator\n                .resolve_table_ident(raw_table_name.clone())?;\n            if table_type != TableType::System && name.starts_with(\"st_\") {\n                Err(ValidationError::TableNameReserved { table: name }.into())\n            } else {\n                let mut name = name.as_raw().clone();\n                if name != raw_table_name {\n                    name = table_validator.add_to_global_namespace(name)?;\n                }\n\n                Ok(name)\n            }\n        };\n\n        // Validate default values inline and attach them to columns\n        let validated_defaults: Result<HashMap<ColId, AlgebraicValue>> = default_values\n            .iter()\n            .map(|cdv| {\n                let col_id = cdv.col_id;\n                let Some(col_elem) = product_type.elements.get(col_id.idx()) else {\n                    return Err(ValidationError::ColumnNotFound {\n                        table: raw_table_name.clone(),\n                        def: raw_table_name.clone(),\n                        column: col_id,\n                    }\n                    .into());\n                };\n\n                let mut reader = &cdv.value[..];\n                let ty = WithTypespace::new(self.core.typespace, &col_elem.algebraic_type);\n                let field_value = ty.deserialize(Deserializer::new(&mut reader)).map_err(|decode_error| {\n                    ValidationError::ColumnDefaultValueMalformed {\n                        table: raw_table_name.clone(),\n                        col_id,\n                        err: decode_error,\n                    }\n                })?;\n\n                Ok((col_id, field_value))\n            })\n            .collect_all_errors();\n\n        let validated_defaults = validated_defaults?;\n        // Attach default values to columns\n        for column in &mut columns {\n            if let Some(default_value) = validated_defaults.get(&column.col_id) {\n                column.default_value = Some(default_value.clone());\n            }\n        }\n\n        let (name, indexes, (constraints, primary_key), (), sequences) = (\n            name,\n            indexes,\n            constraints_primary_key,\n            constraints_backed_by_indices,\n            sequences,\n        )\n            .combine_errors()?;\n\n        Ok(TableDef {\n            name: identifier(name)?,\n            product_type_ref,\n            primary_key,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            schedule: None, // V10 handles schedules separately\n            table_type,\n            table_access,\n            is_event,\n            accessor_name: identifier(raw_table_name)?,\n        })\n    }\n\n    fn validate_reducer_def(&mut self, reducer_def: RawReducerDefV10) -> Result<(Identifier, ReducerDef)> {\n        let RawReducerDefV10 {\n            source_name,\n            params,\n            visibility,\n            ok_return_type,\n            err_return_type,\n        } = reducer_def;\n        let accessor_name = identifier(source_name.clone()).map(ReducerName::new);\n\n        let params_for_generate =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ReducerArg {\n                    reducer_name: source_name.clone(),\n                    position,\n                    arg_name,\n                });\n\n        let name_result = self.core.resolve_function_ident(source_name.clone());\n\n        let return_res: Result<_> = (ok_return_type.is_unit() && err_return_type.is_string())\n            .then_some((ok_return_type.clone(), err_return_type.clone()))\n            .ok_or_else(move || {\n                ValidationError::InvalidReducerReturnType {\n                    reducer_name: source_name.clone(),\n                    ok_type: ok_return_type.into(),\n                    err_type: err_return_type.into(),\n                }\n                .into()\n            });\n\n        let (name_result, accessor_name, params_for_generate, return_res) =\n            (name_result, accessor_name, params_for_generate, return_res).combine_errors()?;\n        let (ok_return_type, err_return_type) = return_res;\n        let reducer_name = ReducerName::new(name_result.clone());\n\n        Ok(ReducerDef {\n            name: reducer_name.clone(),\n            accessor_name,\n            params: params.clone(),\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false, // A ProductTypeDef not stored in a Typespace cannot be recursive.\n            },\n            lifecycle: None, // V10 handles lifecycle separately\n            visibility: visibility.into(),\n            ok_return_type,\n            err_return_type,\n        })\n        .map(|reducer_def| (name_result, reducer_def))\n    }\n\n    fn validate_schedule_def(\n        &mut self,\n        schedule: RawScheduleDefV10,\n        tables: &HashMap<Identifier, TableDef>,\n    ) -> Result<(ScheduleDef, Identifier)> {\n        let RawScheduleDefV10 {\n            source_name: _,\n            table_name,\n            schedule_at_col,\n            function_name,\n        } = schedule;\n\n        let table_ident = self.core.resolve_table_ident(table_name.clone())?;\n\n        // Look up the table to validate the schedule\n        let table = tables.get(&table_ident).ok_or_else(|| ValidationError::TableNotFound {\n            table: table_name.clone(),\n        })?;\n\n        let product_type = self\n            .core\n            .typespace\n            .get(table.product_type_ref)\n            .and_then(AlgebraicType::as_product)\n            .ok_or_else(|| ValidationError::InvalidProductTypeRef {\n                table: table_name.clone(),\n                ref_: table.product_type_ref,\n            })?;\n\n        let source_name = generate_schedule_name(&table_ident);\n        self.core\n            .validate_schedule_def(\n                table_name.clone(),\n                source_name,\n                function_name,\n                product_type,\n                schedule_at_col,\n                table.primary_key,\n            )\n            .map(|schedule_def| (schedule_def, table_ident))\n    }\n\n    fn validate_lifecycle_reducer(\n        &mut self,\n        lifecycle_def: RawLifeCycleReducerDefV10,\n        reducer_id: ReducerId,\n    ) -> Result<Lifecycle> {\n        let RawLifeCycleReducerDefV10 {\n            lifecycle_spec,\n            function_name: _,\n        } = lifecycle_def;\n\n        self.core.register_lifecycle(lifecycle_spec, reducer_id)?;\n        Ok(lifecycle_spec)\n    }\n\n    fn validate_procedure_def(&mut self, procedure_def: RawProcedureDefV10) -> Result<ProcedureDef> {\n        let RawProcedureDefV10 {\n            source_name,\n            params,\n            return_type,\n            visibility,\n        } = procedure_def;\n\n        let params_for_generate =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ProcedureArg {\n                    procedure_name: source_name.clone(),\n                    position,\n                    arg_name,\n                });\n\n        let return_type_for_generate = self.core.validate_for_type_use(\n            || TypeLocation::ProcedureReturn {\n                procedure_name: source_name.clone(),\n            },\n            &return_type,\n        );\n\n        let accessor_name = identifier(source_name.clone());\n        let name_result = self.core.resolve_function_ident(source_name);\n\n        let (name_result, accessor_name, params_for_generate, return_type_for_generate) = (\n            name_result,\n            accessor_name,\n            params_for_generate,\n            return_type_for_generate,\n        )\n            .combine_errors()?;\n\n        Ok(ProcedureDef {\n            name: name_result.clone(),\n            accessor_name,\n            params,\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false,\n            },\n            return_type,\n            return_type_for_generate,\n            visibility: visibility.into(),\n        })\n    }\n\n    fn validate_view_def(&mut self, view_def: RawViewDefV10, typespace_with_accessor: &Typespace) -> Result<ViewDef> {\n        let RawViewDefV10 {\n            source_name: accessor_name,\n            is_public,\n            is_anonymous,\n            params,\n            return_type,\n            index,\n        } = view_def;\n\n        let invalid_return_type = || {\n            ValidationErrors::from(ValidationError::InvalidViewReturnType {\n                view: accessor_name.clone(),\n                ty: return_type.clone().into(),\n            })\n        };\n\n        let (product_type_ref, return_kind) =\n            extract_view_return_product_type_ref(&return_type).ok_or_else(invalid_return_type)?;\n\n        let product_type = self\n            .core\n            .typespace\n            .get(product_type_ref)\n            .and_then(AlgebraicType::as_product)\n            .ok_or_else(|| {\n                ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                    table: accessor_name.clone(),\n                    ref_: product_type_ref,\n                })\n            })?;\n\n        let params_for_generate =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ViewArg {\n                    view_name: accessor_name.clone(),\n                    position,\n                    arg_name,\n                })?;\n\n        let return_type_for_generate_input = match return_kind {\n            ViewKind::Procedural => return_type.clone(),\n            // Query-builder views still return rows from the client's perspective.\n            // For codegen purposes we model this as `Vec<T>`.\n            ViewKind::Query => AlgebraicType::array(product_type_ref.into()),\n        };\n\n        let return_type_for_generate = self.core.validate_for_type_use(\n            || TypeLocation::ViewReturn {\n                view_name: accessor_name.clone(),\n            },\n            &return_type_for_generate_input,\n        );\n\n        let name = self.core.resolve_function_ident(accessor_name.clone())?;\n\n        let mut view_validator = ViewValidator::new(\n            accessor_name.clone(),\n            product_type_ref,\n            product_type,\n            &params,\n            &params_for_generate,\n            &mut self.core,\n        )?;\n\n        let _ = view_validator.add_to_global_namespace(name.as_raw().clone())?;\n\n        let n = product_type.elements.len();\n        let return_columns = (0..n)\n            .map(|id| {\n                let product_type = typespace_with_accessor\n                    .get(product_type_ref)\n                    .and_then(AlgebraicType::as_product)\n                    .ok_or_else(|| {\n                        ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                            table: accessor_name.clone(),\n                            ref_: product_type_ref,\n                        })\n                    })?;\n                view_validator.validate_view_column_def(id.into(), product_type)\n            })\n            .collect_all_errors();\n\n        let n = params.elements.len();\n        let param_columns = (0..n)\n            .map(|id| view_validator.validate_param_column_def(id.into()))\n            .collect_all_errors();\n\n        let (return_type_for_generate, return_columns, param_columns) =\n            (return_type_for_generate, return_columns, param_columns).combine_errors()?;\n\n        Ok(ViewDef {\n            name,\n            accessor_name: identifier(accessor_name)?,\n            is_anonymous,\n            is_public,\n            params,\n            fn_ptr: index.into(),\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false, // A `ProductTypeDef` not stored in a `Typespace` cannot be recursive.\n            },\n            return_type,\n            return_type_for_generate,\n            product_type_ref,\n            primary_key: None,\n            return_columns,\n            param_columns,\n        })\n    }\n}\n\nfn attach_lifecycles_to_reducers(\n    reducers: &mut IndexMap<Identifier, ReducerDef>,\n    lifecycles: Vec<(ReducerId, Lifecycle)>,\n) -> Result<()> {\n    for lifecycle in lifecycles {\n        let (reducer_id, lifecycle) = lifecycle;\n        let reducer = reducers\n            .values_mut()\n            .nth(reducer_id.idx())\n            .ok_or_else(|| ValidationError::LifecycleWithoutReducer { lifecycle })?;\n\n        // Enforce invariant: only one lifecycle per reducer\n        if reducer.lifecycle.is_some() {\n            return Err(ValidationError::DuplicateLifecycle { lifecycle }.into());\n        }\n\n        reducer.lifecycle = Some(lifecycle);\n    }\n\n    Ok(())\n}\n\nfn attach_schedules_to_tables(\n    tables: &mut HashMap<Identifier, TableDef>,\n    schedules: Vec<(ScheduleDef, Identifier)>,\n) -> Result<()> {\n    for schedule in schedules {\n        let (schedule, table_name) = schedule;\n        let table = tables.values_mut().find(|t| *t.name == *table_name).ok_or_else(|| {\n            ValidationError::MissingScheduleTable {\n                table_name: table_name.as_raw().clone(),\n                schedule_name: schedule.name.clone(),\n            }\n        })?;\n\n        // Enforce invariant: only one schedule per table\n        if table.schedule.is_some() {\n            return Err(ValidationError::DuplicateSchedule {\n                table: table.name.clone(),\n            }\n            .into());\n        }\n\n        table.schedule = Some(schedule);\n    }\n\n    Ok(())\n}\n\nfn assign_query_view_primary_keys(tables: &IdentifierMap<TableDef>, views: &mut IndexMap<Identifier, ViewDef>) {\n    let primary_key_for_product_type_ref = |product_type_ref: AlgebraicTypeRef| {\n        let mut primary_key = None;\n        for table in tables\n            .values()\n            .filter(|table| table.product_type_ref == product_type_ref)\n        {\n            let Some(table_primary_key) = table.primary_key else {\n                continue;\n            };\n            match primary_key {\n                Some(existing) if existing != table_primary_key => {\n                    // Ambiguous source table: keep the view without a primary key.\n                    return None;\n                }\n                None => primary_key = Some(table_primary_key),\n                Some(_) => {}\n            }\n        }\n        primary_key\n    };\n\n    for view in views.values_mut() {\n        view.primary_key = match extract_view_return_product_type_ref(&view.return_type) {\n            Some((_, ViewKind::Procedural)) => None,\n            Some((product_type_ref, ViewKind::Query)) => primary_key_for_product_type_ref(product_type_ref),\n            None => None,\n        };\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::def::validate::tests::{\n        check_product_type, expect_identifier, expect_raw_type_name, expect_resolve, expect_type_name,\n    };\n    use crate::def::{validate::Result, ModuleDef};\n    use crate::def::{\n        BTreeAlgorithm, ConstraintData, DirectAlgorithm, FunctionKind, FunctionVisibility, IndexAlgorithm, IndexDef,\n        UniqueConstraintData,\n    };\n    use crate::error::*;\n    use crate::identifier::Identifier;\n    use crate::type_for_generate::ClientCodegenError;\n\n    use itertools::Itertools;\n    use spacetimedb_data_structures::expect_error_matching;\n    use spacetimedb_lib::db::raw_def::v10::{CaseConversionPolicy, RawModuleDefV10Builder};\n    use spacetimedb_lib::db::raw_def::v9::{btree, direct, hash};\n    use spacetimedb_lib::db::raw_def::*;\n    use spacetimedb_lib::ScheduleAt;\n    use spacetimedb_primitives::{ColId, ColList, ColSet};\n    use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, AlgebraicValue, ProductType, SumValue};\n    use v9::{Lifecycle, TableAccess, TableType};\n\n    /// This test attempts to exercise every successful path in the validation code.\n    #[test]\n    fn test_valid_definition_with_default_policy() {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        let product_type = AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::String)]);\n        let product_type_ref = builder.add_algebraic_type(\n            [\"Scope1\".into(), \"Scope2\".into()],\n            \"ReferencedProduct\",\n            product_type.clone(),\n            false,\n        );\n\n        let sum_type = AlgebraicType::simple_enum([\"Gala\", \"GrannySmith\", \"RedDelicious\"].into_iter());\n        let sum_type_ref = builder.add_algebraic_type([], \"ReferencedSum\", sum_type.clone(), false);\n\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n\n        let red_delicious = AlgebraicValue::Sum(SumValue::new(2, ()));\n\n        builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"Apple_name\", AlgebraicType::String),\n                    (\"countFresh\", AlgebraicType::U16),\n                    (\"type\", sum_type_ref.into()),\n                ]),\n                true,\n            )\n            .with_index_no_accessor_name(btree([1, 2]), \"apples_id\")\n            .with_index_no_accessor_name(direct(2), \"Apples_count_direct\")\n            .with_unique_constraint(2)\n            .with_index_no_accessor_name(btree(3), \"Apples_type_btree\")\n            .with_unique_constraint(3)\n            .with_default_column_value(2, AlgebraicValue::U16(37))\n            .with_default_column_value(3, red_delicious.clone())\n            .finish();\n\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([\n                    (\"count\", AlgebraicType::U16),\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\n                        \"optional_product_column\",\n                        AlgebraicType::option(product_type_ref.into()),\n                    ),\n                ]),\n                false,\n            )\n            .with_column_sequence(0)\n            .with_unique_constraint(ColId(0))\n            .with_primary_key(0)\n            .with_access(TableAccess::Private)\n            .with_index_no_accessor_name(btree(0), \"bananas_count\")\n            .with_index_no_accessor_name(btree([0, 1, 2]), \"bananas_count_id_name\")\n            .finish();\n\n        let deliveries_product_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index_no_accessor_name(btree(2), \"scheduled_id_index\")\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_lifecycle_reducer(Lifecycle::Init, \"init\", ProductType::unit());\n        builder.add_lifecycle_reducer(Lifecycle::OnConnect, \"on_connect\", ProductType::unit());\n        builder.add_lifecycle_reducer(Lifecycle::OnDisconnect, \"on_disconnect\", ProductType::unit());\n        builder.add_reducer(\"extra_reducer\", ProductType::from([(\"a\", AlgebraicType::U64)]));\n        builder.add_reducer(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", deliveries_product_type.into())]),\n        );\n        builder.add_schedule(\"Deliveries\", 1, \"check_deliveries\");\n\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n\n        let casing_policy = CaseConversionPolicy::default();\n        assert_eq!(casing_policy, CaseConversionPolicy::SnakeCase);\n        let apples = Identifier::for_test(\"apples\");\n        let bananas = Identifier::for_test(\"bananas\");\n        let deliveries = Identifier::for_test(\"deliveries\");\n\n        assert_eq!(def.tables.len(), 3);\n\n        let apples_def = &def.tables[&apples];\n\n        assert_eq!(apples_def.name, apples);\n        assert_eq!(apples_def.table_type, TableType::User);\n        assert_eq!(apples_def.table_access, TableAccess::Public);\n\n        assert_eq!(apples_def.columns.len(), 4);\n        assert_eq!(apples_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(apples_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(apples_def.columns[0].default_value, None);\n        assert_eq!(apples_def.columns[1].name, expect_identifier(\"apple_name\"));\n        assert_eq!(apples_def.columns[1].ty, AlgebraicType::String);\n        assert_eq!(apples_def.columns[1].default_value, None);\n        assert_eq!(apples_def.columns[2].name, expect_identifier(\"count_fresh\"));\n        assert_eq!(apples_def.columns[2].ty, AlgebraicType::U16);\n        assert_eq!(apples_def.columns[2].default_value, Some(AlgebraicValue::U16(37)));\n        assert_eq!(apples_def.columns[3].name, expect_identifier(\"type\"));\n        assert_eq!(apples_def.columns[3].ty, sum_type_ref.into());\n        assert_eq!(apples_def.columns[3].default_value, Some(red_delicious));\n        let expected_sum_type = AlgebraicType::simple_enum([\"gala\", \"grannySmith\", \"redDelicious\"].into_iter());\n        assert_eq!(\n            expect_resolve(&def.typespace, &apples_def.columns[3].ty),\n            expected_sum_type\n        );\n\n        assert_eq!(apples_def.primary_key, None);\n\n        assert_eq!(apples_def.constraints.len(), 2);\n        let apples_unique_constraint = \"apples_type_key\";\n        assert_eq!(\n            apples_def.constraints[apples_unique_constraint].data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(3).into()\n            })\n        );\n        assert_eq!(\n            &apples_def.constraints[apples_unique_constraint].name[..],\n            apples_unique_constraint\n        );\n\n        assert_eq!(apples_def.indexes.len(), 3);\n        assert_eq!(\n            apples_def\n                .indexes\n                .values()\n                .sorted_by_key(|id| &id.name)\n                .collect::<Vec<_>>(),\n            [\n                &IndexDef {\n                    name: \"apples_apple_name_count_fresh_idx_btree\".into(),\n                    source_name: \"apples_id\".into(),\n                    accessor_name: None,\n                    algorithm: BTreeAlgorithm {\n                        columns: [ColId(1), ColId(2)].into(),\n                    }\n                    .into(),\n                },\n                &IndexDef {\n                    name: \"apples_count_fresh_idx_direct\".into(),\n                    accessor_name: None,\n                    source_name: \"Apples_count_direct\".into(),\n                    algorithm: DirectAlgorithm { column: ColId(2) }.into()\n                },\n                &IndexDef {\n                    name: \"apples_type_idx_btree\".into(),\n                    source_name: \"Apples_type_btree\".into(),\n                    accessor_name: None,\n                    algorithm: BTreeAlgorithm {\n                        columns: [ColId(3)].into()\n                    }\n                    .into()\n                }\n            ]\n        );\n\n        let bananas_def = &def.tables[&bananas];\n\n        assert_eq!(bananas_def.name, bananas);\n        assert_eq!(bananas_def.table_access, TableAccess::Private);\n        assert_eq!(bananas_def.table_type, TableType::User);\n        assert_eq!(bananas_def.columns.len(), 4);\n        assert_eq!(bananas_def.columns[0].name, expect_identifier(\"count\"));\n        assert_eq!(bananas_def.columns[0].ty, AlgebraicType::U16);\n        assert_eq!(bananas_def.columns[1].name, expect_identifier(\"id\"));\n        assert_eq!(bananas_def.columns[1].ty, AlgebraicType::U64);\n        assert_eq!(bananas_def.columns[2].name, expect_identifier(\"name\"));\n        assert_eq!(bananas_def.columns[2].ty, AlgebraicType::String);\n        assert_eq!(\n            bananas_def.columns[3].name,\n            expect_identifier(\"optional_product_column\")\n        );\n        assert_eq!(\n            bananas_def.columns[3].ty,\n            AlgebraicType::option(product_type_ref.into())\n        );\n        assert_eq!(bananas_def.primary_key, Some(0.into()));\n        assert_eq!(bananas_def.indexes.len(), 2);\n        assert_eq!(bananas_def.constraints.len(), 1);\n        let (bananas_constraint_name, bananas_constraint) = bananas_def.constraints.iter().next().unwrap();\n        assert_eq!(bananas_constraint_name, &bananas_constraint.name);\n        assert_eq!(\n            bananas_constraint.data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(0).into()\n            })\n        );\n\n        let delivery_def = &def.tables[&deliveries];\n        assert_eq!(delivery_def.name, deliveries);\n        assert_eq!(delivery_def.table_access, TableAccess::Public);\n        assert_eq!(delivery_def.table_type, TableType::System);\n        assert_eq!(delivery_def.columns.len(), 3);\n        assert_eq!(delivery_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(delivery_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.columns[1].name, expect_identifier(\"scheduled_at\"));\n        assert_eq!(delivery_def.columns[1].ty, schedule_at_type);\n        assert_eq!(delivery_def.columns[2].name, expect_identifier(\"scheduled_id\"));\n        assert_eq!(delivery_def.columns[2].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.schedule.as_ref().unwrap().at_column, 1.into());\n        assert_eq!(\n            &delivery_def.schedule.as_ref().unwrap().function_name[..],\n            \"check_deliveries\"\n        );\n        assert_eq!(\n            delivery_def.schedule.as_ref().unwrap().function_kind,\n            FunctionKind::Reducer\n        );\n        assert_eq!(delivery_def.primary_key, Some(ColId(2)));\n\n        assert_eq!(def.typespace.get(product_type_ref), Some(&product_type));\n        assert_eq!(def.typespace.get(sum_type_ref), Some(&expected_sum_type));\n\n        check_product_type(&def, apples_def);\n        check_product_type(&def, bananas_def);\n        check_product_type(&def, delivery_def);\n\n        let product_type_name = expect_type_name(\"Scope1::Scope2::ReferencedProduct\");\n        let sum_type_name = expect_type_name(\"ReferencedSum\");\n        let apples_type_name = expect_type_name(\"Apples\");\n        let bananas_type_name = expect_type_name(\"Bananas\");\n        let deliveries_type_name = expect_type_name(\"Deliveries\");\n\n        assert_eq!(def.types[&product_type_name].ty, product_type_ref);\n        assert_eq!(def.types[&sum_type_name].ty, sum_type_ref);\n        assert_eq!(def.types[&apples_type_name].ty, apples_def.product_type_ref);\n        assert_eq!(def.types[&bananas_type_name].ty, bananas_def.product_type_ref);\n        assert_eq!(def.types[&deliveries_type_name].ty, delivery_def.product_type_ref);\n\n        let init_name = expect_identifier(\"init\");\n        assert_eq!(&*def.reducers[&init_name].name, &*init_name);\n        assert_eq!(def.reducers[&init_name].lifecycle, Some(Lifecycle::Init));\n\n        let on_connect_name = expect_identifier(\"on_connect\");\n        assert_eq!(&*def.reducers[&on_connect_name].name, &*on_connect_name);\n        assert_eq!(def.reducers[&on_connect_name].lifecycle, Some(Lifecycle::OnConnect));\n\n        let on_disconnect_name = expect_identifier(\"on_disconnect\");\n        assert_eq!(&*def.reducers[&on_disconnect_name].name, &*on_disconnect_name);\n        assert_eq!(\n            def.reducers[&on_disconnect_name].lifecycle,\n            Some(Lifecycle::OnDisconnect)\n        );\n\n        let extra_reducer_name = expect_identifier(\"extra_reducer\");\n        assert_eq!(&*def.reducers[&extra_reducer_name].name, &*extra_reducer_name);\n        assert_eq!(def.reducers[&extra_reducer_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&extra_reducer_name].params,\n            ProductType::from([(\"a\", AlgebraicType::U64)])\n        );\n\n        let check_deliveries_name = expect_identifier(\"check_deliveries\");\n        assert_eq!(&*def.reducers[&check_deliveries_name].name, &*check_deliveries_name);\n        assert_eq!(def.reducers[&check_deliveries_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&check_deliveries_name].params,\n            ProductType::from([(\"a\", deliveries_product_type.into())])\n        );\n\n        assert_eq!(\n            def.reducers[&check_deliveries_name].visibility,\n            FunctionVisibility::Private,\n        );\n        assert_eq!(def.reducers[&init_name].visibility, FunctionVisibility::Private);\n        assert_eq!(\n            def.reducers[&extra_reducer_name].visibility,\n            FunctionVisibility::ClientCallable\n        );\n    }\n\n    #[test]\n    fn invalid_product_type_ref() {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        // `build_table` does NOT initialize table.product_type_ref, which should result in an error.\n        builder.build_table(\"Bananas\", AlgebraicTypeRef(1337)).finish();\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidProductTypeRef { table, ref_ } => {\n            &table[..] == \"Bananas\" && ref_ == &AlgebraicTypeRef(1337)\n        });\n    }\n\n    #[test]\n    fn not_canonically_ordered_columns() {\n        let mut builder = RawModuleDefV10Builder::new();\n        let product_type = ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]);\n        builder\n            .build_table_with_new_type(\"Bananas\", product_type.clone(), false)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::TypeHasIncorrectOrdering { type_name, ref_, bad_type } => {\n            type_name == &expect_raw_type_name(\"Bananas\") &&\n            ref_ == &AlgebraicTypeRef(0) &&\n            bad_type == &product_type.clone().into()\n        });\n    }\n\n    #[test]\n    fn invalid_table_name() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_column_name() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_ref() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_index_no_accessor_name(btree([0, 55]), \"Bananas_a_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"bananas_b_col_55_idx_btree\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_ref() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_unique_constraint(ColId(55))\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"bananas_col_55_key\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_sequence_column_ref() {\n        // invalid column id\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_column_sequence(55)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"bananas_col_55_seq\" &&\n            column == &55.into()\n        });\n\n        // incorrect column type\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::String)]),\n                true,\n            )\n            .with_column_sequence(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidSequenceColumnType { sequence, column, column_type } => {\n            &sequence[..] == \"bananas_a_seq\" &&\n            column == &RawColumnName::new(\"Bananas\", \"a\") &&\n            column_type.0 == AlgebraicType::String\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_duplicates() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_index_no_accessor_name(btree([0, 0]), \"bananas_b_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ def, columns } => {\n            &def[..] == \"bananas_b_b_idx_btree\" && columns == &ColList::from_iter([0, 0])\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_duplicates() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"a\", AlgebraicType::U16), (\"b\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_unique_constraint(ColList::from_iter([1, 1]))\n            .with_unique_constraint(ColList::from_iter([1, 1]))\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ def, columns } => {\n            &def[..] == \"bananas_b_b_key\" && columns == &ColList::from_iter([1, 1])\n        });\n    }\n    #[test]\n    fn recursive_ref() {\n        let recursive_type = AlgebraicType::product([(\"a\", AlgebraicTypeRef(0).into())]);\n\n        let mut builder = RawModuleDefV10Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Recursive\", recursive_type.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: ModuleDef = builder.finish().try_into().unwrap();\n\n        assert!(result.typespace_for_generate[ref_].is_recursive());\n    }\n\n    #[test]\n    fn out_of_bounds_ref() {\n        let invalid_type_1 = AlgebraicType::product([(\"a\", AlgebraicTypeRef(31).into())]);\n        let mut builder = RawModuleDefV10Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Invalid\", invalid_type_1.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ClientCodegenError { location, error: ClientCodegenError::TypeRefError(_)  } => {\n            location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) }\n        });\n    }\n\n    #[test]\n    fn not_valid_for_client_code_generation() {\n        let inner_type_invalid_for_use = AlgebraicType::product([(\"b\", AlgebraicType::U32)]);\n        let invalid_type = AlgebraicType::product([(\"a\", inner_type_invalid_for_use.clone())]);\n        let mut builder = RawModuleDefV10Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Invalid\", invalid_type.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(\n            result,\n            ValidationError::ClientCodegenError {\n                location,\n                error: ClientCodegenError::NonSpecialTypeNotAUse { ty }\n            } => {\n                location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) } &&\n                ty.0 == inner_type_invalid_for_use\n            }\n        );\n    }\n\n    #[test]\n    fn hash_index_supported() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_index_no_accessor_name(hash(0), \"bananas_b\")\n            .finish();\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n        let indexes = def.indexes().collect::<Vec<_>>();\n        assert_eq!(indexes.len(), 1);\n        assert_eq!(indexes[0].algorithm, IndexAlgorithm::Hash(0.into()));\n    }\n\n    #[test]\n    fn unique_constrain_without_index() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"a\", AlgebraicType::U16), (\"b\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_unique_constraint(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(\n            result,\n            ValidationError::UniqueConstraintWithoutIndex { constraint, columns } => {\n                &**constraint == \"bananas_b_key\" && *columns == ColSet::from(1)\n            }\n        );\n    }\n\n    #[test]\n    fn direct_index_only_u8_to_u64() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::I32), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_index_no_accessor_name(direct(0), \"bananas_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DirectIndexOnBadType { index, .. } => {\n            &index[..] == \"bananas_b_idx_direct\"\n        });\n    }\n\n    #[test]\n    fn one_auto_inc() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_column_sequence(1)\n            .with_column_sequence(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::OneAutoInc { column } => {\n            column == &RawColumnName::new(\"Bananas\", \"a\")\n        });\n    }\n\n    #[test]\n    fn invalid_primary_key() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_primary_key(44)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"Bananas\" &&\n            column == &44.into()\n        });\n    }\n\n    #[test]\n    fn missing_primary_key_unique_constraint() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_primary_key(0)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::MissingPrimaryKeyUniqueConstraint { column } => {\n            column == &RawColumnName::new(\"Bananas\", \"b\")\n        });\n    }\n\n    #[test]\n    fn duplicate_type_name() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder.add_algebraic_type(\n            [\"scope1\".into(), \"scope2\".into()],\n            \"Duplicate\",\n            AlgebraicType::U64,\n            false,\n        );\n        builder.add_algebraic_type(\n            [\"scope1\".into(), \"scope2\".into()],\n            \"Duplicate\",\n            AlgebraicType::U32,\n            false,\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateTypeName { name } => {\n            name == &expect_type_name(\"Scope1::Scope2::Duplicate\")\n        });\n    }\n\n    #[test]\n    fn duplicate_lifecycle() {\n        let mut builder = RawModuleDefV10Builder::new();\n        builder.add_lifecycle_reducer(Lifecycle::Init, \"init1\", ProductType::unit());\n        builder.add_lifecycle_reducer(Lifecycle::Init, \"init1\", ProductType::unit());\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateLifecycle { lifecycle } => {\n            lifecycle == &Lifecycle::Init\n        });\n    }\n\n    #[test]\n    fn missing_scheduled_reducer() {\n        let mut builder = RawModuleDefV10Builder::new();\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n        builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index_no_accessor_name(btree(2), \"scheduled_id_index\")\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_schedule(\"Deliveries\", 1, \"check_deliveries\");\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::MissingScheduledFunction { schedule, function } => {\n            &schedule[..] == \"deliveries_sched\" &&\n                function == &expect_identifier(\"check_deliveries\")\n        });\n    }\n\n    #[test]\n    fn incorrect_scheduled_reducer_args() {\n        let mut builder = RawModuleDefV10Builder::new();\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n        let deliveries_product_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index_no_accessor_name(direct(2), \"scheduled_id_idx\")\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_schedule(\"Deliveries\", 1, \"check_deliveries\");\n        builder.add_reducer(\"check_deliveries\", ProductType::from([(\"a\", AlgebraicType::U64)]));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IncorrectScheduledFunctionParams { function_name, function_kind, expected, actual } => {\n            &function_name[..] == \"check_deliveries\" &&\n                *function_kind == FunctionKind::Reducer &&\n                expected.0 == AlgebraicType::product([AlgebraicType::Ref(deliveries_product_type)]) &&\n                actual.0 == ProductType::from([(\"a\", AlgebraicType::U64)]).into()\n        });\n    }\n\n    #[test]\n    fn duplicate_reducer_names() {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        builder.add_reducer(\"foo\", [(\"i\", AlgebraicType::I32)].into());\n        builder.add_reducer(\"foo\", [(\"name\", AlgebraicType::String)].into());\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n\n    #[test]\n    fn duplicate_procedure_names() {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        builder.add_procedure(\"foo\", [(\"i\", AlgebraicType::I32)].into(), AlgebraicType::unit());\n        builder.add_procedure(\"foo\", [(\"name\", AlgebraicType::String)].into(), AlgebraicType::unit());\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n\n    #[test]\n    fn duplicate_procedure_and_reducer_name() {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        builder.add_reducer(\"foo\", [(\"i\", AlgebraicType::I32)].into());\n        builder.add_procedure(\"foo\", [(\"i\", AlgebraicType::I32)].into(), AlgebraicType::unit());\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n\n    fn make_case_conversion_builder() -> (RawModuleDefV10Builder, AlgebraicTypeRef) {\n        let mut builder = RawModuleDefV10Builder::new();\n\n        // Sum type: PascalCase variants → camelCase after conversion.\n        let color_sum = AlgebraicType::simple_enum([\"RedApple\", \"GreenApple\", \"YellowApple\"].into_iter());\n        let color_ref = builder.add_algebraic_type([], \"FruitColor\", color_sum, true);\n\n        // Product type with scope: scope segments stay unchanged, unscoped name → PascalCase.\n        builder.add_algebraic_type(\n            [\"myLib\".into(), \"utils\".into()],\n            \"metaInfo\",\n            AlgebraicType::product([(\"kind\", AlgebraicType::U8)]),\n            false,\n        );\n\n        // Table 1: \"FruitBasket\"\n        //   [0] BasketId     PascalCase → \"basket_id\"\n        //   [1] fruitName    camelCase  → \"fruit_name\"\n        //   [2] ItemCount    PascalCase → \"item_count\"\n        //   [3] color_label  snake_case → \"color_label\"\n        builder\n            .build_table_with_new_type(\n                \"FruitBasket\",\n                ProductType::from([\n                    (\"BasketId\", AlgebraicType::U64),\n                    (\"fruitName\", AlgebraicType::String),\n                    (\"ItemCount\", AlgebraicType::U32),\n                    (\"color_label\", color_ref.into()),\n                ]),\n                true,\n            )\n            .with_index(btree([0, 1]), \"RawBasketLookup\", \"fruitNameIndex\")\n            .with_index(direct(2), \"RawCountDirect\", \"ItemCount\")\n            .with_unique_constraint(ColId(2))\n            .with_column_sequence(0)\n            .finish();\n\n        // Table 2: \"deliveryRecord\"\n        //   [0] recordId    camelCase  → \"record_id\"\n        //   [1] ScheduledAt PascalCase → \"scheduled_at\"\n        //   [2] SeqId       PascalCase → \"seq_id\"\n        let schedule_at_type = builder.add_type::<spacetimedb_lib::ScheduleAt>();\n\n        let builder_type_ref = builder\n            .build_table_with_new_type(\n                \"deliveryRecord\",\n                ProductType::from([\n                    (\"recordId\", AlgebraicType::U64),\n                    (\"ScheduledAt\", schedule_at_type),\n                    (\"SeqId\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index(btree(2), \"SeqIdIndex\", \"SeqId\")\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_reducer(\"doDelivery\", ProductType::from([(\"a\", builder_type_ref.into())]));\n        builder.add_reducer(\"ProcessItem\", ProductType::from([(\"b\", AlgebraicType::U32)]));\n        builder.add_schedule(\"deliveryRecord\", 1, \"doDelivery\");\n\n        (builder, color_ref)\n    }\n\n    /// Exhaustive test for case-conversion under the default [`CaseConversionPolicy::SnakeCase`].\n    ///\n    /// Rules under verification:\n    ///\n    /// | Entity          | Source style     | Canonical style           | Notes                          |\n    /// |-----------------|------------------|---------------------------|--------------------------------|\n    /// | Table name      | any              | snake_case                | raw name preserved as accessor |\n    /// | Column name     | any              | snake_case                | raw name preserved as accessor |\n    /// | Reducer name    | any              | snake_case                | —                              |\n    /// | Type name       | any (unscoped)   | PascalCase                | scope segments unchanged       |\n    /// | Enum variant    | any              | camelCase                 | —                              |\n    /// | Index name      | autogenerated    | `{tbl}_{cols}_idx_{algo}` | uses canonical table+col names |\n    /// | Index accessor  | raw source_name  | **unchanged**             | no conversion applied          |\n    /// | Constraint name | autogenerated    | `{tbl}_{cols}_key`        | uses canonical table+col names |\n    /// | Sequence name   | autogenerated    | `{tbl}_{col}_seq`         | uses canonical table+col names |\n    /// | Schedule name   | autogenerated    | `{tbl}_sched`             | uses canonical table name      |\n    #[test]\n    fn test_case_conversion_snake_case_policy() {\n        use crate::def::*;\n        use crate::identifier::Identifier;\n        use itertools::Itertools;\n        use spacetimedb_lib::db::raw_def::v10::CaseConversionPolicy;\n        use spacetimedb_sats::AlgebraicType;\n\n        let id = |s: &str| Identifier::for_test(s);\n\n        let (builder, color_ref) = make_case_conversion_builder();\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n\n        // Sanity: policy is SnakeCase by default.\n        assert_eq!(CaseConversionPolicy::default(), CaseConversionPolicy::SnakeCase);\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // TABLE NAMES\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(def.tables.len(), 2);\n\n        // \"FruitBasket\" → canonical \"fruit_basket\"\n        let fruit_basket = id(\"fruit_basket\");\n        assert!(def.tables.contains_key(&fruit_basket), \"table 'fruit_basket' not found\");\n        let fb = &def.tables[&fruit_basket];\n        assert_eq!(fb.name, fruit_basket, \"table canonical name\");\n        assert_eq!(\n            &*fb.accessor_name, \"FruitBasket\",\n            \"table accessor_name must preserve raw source\"\n        );\n\n        // \"deliveryRecord\" → canonical \"delivery_record\"\n        let delivery_record = id(\"delivery_record\");\n        assert!(\n            def.tables.contains_key(&delivery_record),\n            \"table 'delivery_record' not found\"\n        );\n        let dr = &def.tables[&delivery_record];\n        assert_eq!(dr.name, delivery_record, \"table canonical name\");\n        assert_eq!(\n            &*dr.accessor_name, \"deliveryRecord\",\n            \"table accessor_name must preserve raw source\"\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // COLUMN NAMES — FruitBasket\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(fb.columns.len(), 4);\n\n        // [0] \"BasketId\" (PascalCase) → \"basket_id\"\n        assert_eq!(fb.columns[0].name, id(\"basket_id\"), \"col 0 canonical\");\n        assert_eq!(&*fb.columns[0].accessor_name, \"BasketId\", \"col 0 accessor\");\n        assert_eq!(fb.columns[0].ty, AlgebraicType::U64);\n\n        // [1] \"fruitName\" (camelCase) → \"fruit_name\"\n        assert_eq!(fb.columns[1].name, id(\"fruit_name\"), \"col 1 canonical\");\n        assert_eq!(&*fb.columns[1].accessor_name, \"fruitName\", \"col 1 accessor\");\n        assert_eq!(fb.columns[1].ty, AlgebraicType::String);\n\n        // [2] \"ItemCount\" (PascalCase) → \"item_count\"\n        assert_eq!(fb.columns[2].name, id(\"item_count\"), \"col 2 canonical\");\n        assert_eq!(&*fb.columns[2].accessor_name, \"ItemCount\", \"col 2 accessor\");\n        assert_eq!(fb.columns[2].ty, AlgebraicType::U32);\n\n        // [3] \"color_label\" (already snake) → \"color_label\"\n        assert_eq!(fb.columns[3].name, id(\"color_label\"), \"col 3 canonical\");\n        assert_eq!(&*fb.columns[3].accessor_name, \"color_label\", \"col 3 accessor\");\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // COLUMN NAMES — deliveryRecord\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(dr.columns.len(), 3);\n\n        // [0] \"recordId\" (camelCase) → \"record_id\"\n        assert_eq!(dr.columns[0].name, id(\"record_id\"), \"dr col 0 canonical\");\n        assert_eq!(&*dr.columns[0].accessor_name, \"recordId\", \"dr col 0 accessor\");\n\n        // [1] \"ScheduledAt\" (PascalCase) → \"scheduled_at\"\n        assert_eq!(dr.columns[1].name, id(\"scheduled_at\"), \"dr col 1 canonical\");\n        assert_eq!(&*dr.columns[1].accessor_name, \"ScheduledAt\", \"dr col 1 accessor\");\n\n        // [2] \"SeqId\" (PascalCase) → \"seq_id\"\n        assert_eq!(dr.columns[2].name, id(\"seq_id\"), \"dr col 2 canonical\");\n        assert_eq!(&*dr.columns[2].accessor_name, \"SeqId\", \"dr col 2 accessor\");\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // REDUCER NAMES\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        // \"doDelivery\" (camelCase) → \"do_delivery\"\n        let do_delivery = id(\"do_delivery\");\n        assert!(\n            def.reducers.contains_key(&do_delivery),\n            \"reducer 'do_delivery' not found\"\n        );\n        assert_eq!(def.reducers[&do_delivery].name.as_identifier(), &do_delivery);\n\n        // \"ProcessItem\" (PascalCase) → \"process_item\"\n        let process_item = id(\"process_item\");\n        assert!(\n            def.reducers.contains_key(&process_item),\n            \"reducer 'process_item' not found\"\n        );\n        assert_eq!(def.reducers[&process_item].name.as_identifier(), &process_item);\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // TYPE NAMES — PascalCase; scoped names keep their scope segments unchanged\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        // \"FruitColor\" (already Pascal) → \"FruitColor\"\n        assert!(\n            def.types.contains_key(&expect_type_name(\"FruitColor\")),\n            \"type 'FruitColor' not found\"\n        );\n\n        // \"metaInfo\" (lower-camel unscoped) → \"MetaInfo\"; scope \"myLib\",\"utils\" → unchanged\n        assert!(\n            def.types.contains_key(&expect_type_name(\"MyLib::Utils::MetaInfo\")),\n            \"type 'myLib::utils::MetaInfo' not found\"\n        );\n\n        // Anonymous table types keep the raw source name as-is.\n        assert!(def.types.contains_key(&expect_type_name(\"FruitBasket\")));\n        assert!(\n            def.types.contains_key(&expect_type_name(\"deliveryRecord\"))\n                || def.types.contains_key(&expect_type_name(\"DeliveryRecord\")),\n            \"anonymous type for deliveryRecord not found\"\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // ENUM VARIANT NAMES — camelCase\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        // \"RedApple\" → \"redApple\", \"GreenApple\" → \"greenApple\", \"YellowApple\" → \"yellowApple\"\n        let expected_color_sum = AlgebraicType::simple_enum([\"redApple\", \"greenApple\", \"yellowApple\"].into_iter());\n        assert_eq!(\n            def.typespace.get(color_ref),\n            Some(&expected_color_sum),\n            \"enum variants should be camelCase\"\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // INDEX NAMES — autogenerated from canonical table + canonical column names\n        // ═══════════════════════════════════════════════════════════════════════════\n        //\n        // \"FruitBasket\" → \"fruit_basket\"; cols [0]=\"basket_id\" [1]=\"fruit_name\" [2]=\"item_count\"\n        //   btree([0,1]) → \"fruit_basket_basket_id_fruit_name_idx_btree\"\n        //   direct(2)    → \"fruit_basket_item_count_idx_direct\"\n        //\n        // accessor_name = raw source_name passed to with_index(), never converted.\n\n        assert_eq!(fb.indexes.len(), 2);\n\n        let fb_indexes = fb.indexes.values().sorted_by_key(|i| &i.name).collect::<Vec<_>>();\n\n        // btree([0,1]) sorts first alphabetically\n        assert_eq!(\n            fb_indexes[0].name,\n            \"fruit_basket_basket_id_fruit_name_idx_btree\".into(),\n            \"btree index name uses canonical table and col names\"\n        );\n        assert_eq!(\n            fb_indexes[0].accessor_name,\n            Some(id(\"fruitNameIndex\")),\n            \"btree index accessor_name is the raw source_name, never converted\"\n        );\n        assert_eq!(\n            fb_indexes[0].source_name,\n            \"RawBasketLookup\".into(),\n            \"sourcename == autogenerated name in V10\"\n        );\n\n        // direct(2) sorts second\n        assert_eq!(\n            fb_indexes[1].name,\n            \"fruit_basket_item_count_idx_direct\".into(),\n            \"direct index name uses canonical table and col names\"\n        );\n        assert_eq!(\n            fb_indexes[1].accessor_name,\n            Some(id(\"ItemCount\")),\n            \"direct index accessor_name is the raw source_name, never converted\"\n        );\n        assert_eq!(fb_indexes[1].source_name, \"RawCountDirect\".into(),);\n        // ═══════════════════════════════════════════════════════════════════════════\n        // CONSTRAINT NAMES — autogenerated from canonical table + canonical col name\n        // ═══════════════════════════════════════════════════════════════════════════\n        //\n        // unique on FruitBasket col [2] \"ItemCount\" → \"item_count\"\n        //   → \"fruit_basket_item_count_key\"\n\n        assert_eq!(fb.constraints.len(), 1);\n        let (constraint_key, constraint) = fb.constraints.iter().next().unwrap();\n        assert_eq!(\n            &**constraint_key, \"fruit_basket_item_count_key\",\n            \"constraint name uses canonical table and col names\"\n        );\n        assert_eq!(\n            constraint.data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(2).into()\n            }),\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // SEQUENCE NAMES — autogenerated from canonical table + canonical col name\n        // ═══════════════════════════════════════════════════════════════════════════\n        //\n        // sequence on FruitBasket col [0] \"BasketId\" → \"basket_id\"\n        //   → \"fruit_basket_basket_id_seq\"\n\n        assert_eq!(fb.sequences.len(), 1);\n        let (seq_key, _seq) = fb.sequences.iter().next().unwrap();\n        assert_eq!(\n            &**seq_key, \"fruit_basket_basket_id_seq\",\n            \"sequence name uses canonical table and col names\"\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // SCHEDULE NAMES — autogenerated from canonical table name\n        // ═══════════════════════════════════════════════════════════════════════════\n        //\n        // \"deliveryRecord\" → \"delivery_record\" → \"delivery_record_sched\"\n\n        let schedule = dr.schedule.as_ref().expect(\"deliveryRecord should have a schedule\");\n        assert_eq!(\n            &*schedule.name, \"delivery_record_sched\",\n            \"schedule name uses canonical table name\"\n        );\n        assert_eq!(\n            schedule.function_name, do_delivery,\n            \"schedule function_name is the canonical reducer name\"\n        );\n        assert_eq!(schedule.at_column, 1.into());\n        assert_eq!(schedule.function_kind, FunctionKind::Reducer);\n    }\n\n    /// Tests that explicit name overrides bypass case-conversion policy,\n    /// using the same schema as [`test_case_conversion_snake_case_policy`].\n    ///\n    /// Three overrides are applied on top of that schema:\n    ///\n    /// | Source name         | Kind     | Explicit canonical |\n    /// |---------------------|----------|--------------------|\n    /// | `\"FruitBasket\"`     | table    | `\"FB\"`             |\n    /// | `\"doDelivery\"`      | function | `\"Deliver\"`        |\n    /// | `\"RawBasketLookup\"` | index    | `\"fb_lookuP\"`      |\n    ///\n    /// Everything else is left to the default `SnakeCase` policy,\n    /// proving overrides are scoped only to what was explicitly mapped.\n    #[test]\n    fn test_explicit_name_overrides() {\n        use crate::def::*;\n        use spacetimedb_lib::db::raw_def::v10::ExplicitNames;\n\n        let id = |s: &str| Identifier::for_test(s);\n\n        let (mut builder, _color_ref) = make_case_conversion_builder();\n\n        let mut explicit = ExplicitNames::default();\n        explicit.insert_table(\"FruitBasket\", \"FB\"); // bypasses → \"fruit_basket\"\n        explicit.insert_function(\"doDelivery\", \"Deliver\"); // bypasses → \"do_delivery\"\n        explicit.insert_index(\"RawBasketLookup\", \"fb_lookuP\"); // bypasses autogenerated name\n        builder.add_explicit_names(explicit);\n\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // TABLE — explicit \"FB\" replaces policy-derived \"fruit_basket\"\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(def.tables.len(), 2);\n\n        let fb_ident = id(\"FB\");\n        assert!(def.tables.contains_key(&fb_ident), \"table 'FB' not found\");\n        assert!(\n            !def.tables.contains_key(&id(\"fruit_basket\")),\n            \"'fruit_basket' must not exist when overridden\"\n        );\n\n        let fb = &def.tables[&fb_ident];\n        assert_eq!(fb.name, fb_ident, \"canonical name is the explicit value\");\n        assert_eq!(&*fb.accessor_name, \"FruitBasket\", \"accessor_name preserves raw source\");\n\n        // Non-overridden table still follows SnakeCase.\n        let delivery_record = id(\"delivery_record\");\n        assert!(def.tables.contains_key(&delivery_record));\n        let dr = &def.tables[&delivery_record];\n        assert_eq!(&*dr.accessor_name, \"deliveryRecord\");\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // COLUMNS — no explicit override; SnakeCase still applies\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(fb.columns[0].name, id(\"basket_id\"), \"col 0: SnakeCase unchanged\");\n        assert_eq!(fb.columns[1].name, id(\"fruit_name\"), \"col 1: SnakeCase unchanged\");\n        assert_eq!(fb.columns[2].name, id(\"item_count\"), \"col 2: SnakeCase unchanged\");\n        assert_eq!(fb.columns[3].name, id(\"color_label\"), \"col 3: SnakeCase unchanged\");\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // INDEXES — one explicitly overridden, one not\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        assert_eq!(fb.indexes.len(), 2);\n\n        // \"RawBasketLookup\" → explicit \"fb_lookuP\"\n        let idx_explicit = fb\n            .indexes\n            .values()\n            .find(|i| i.accessor_name == Some(id(\"fruitNameIndex\")))\n            .expect(\"index with accessor 'RawBasketLookup' not found\");\n        assert_eq!(\n            idx_explicit.name,\n            \"fb_lookuP\".into(),\n            \"explicit index name used verbatim\"\n        );\n        assert_eq!(\n            idx_explicit.accessor_name,\n            Some(id(\"fruitNameIndex\")),\n            \"accessor_name preserves raw source\"\n        );\n        //\n        // Non-overridden index on deliveryRecord still uses policy-derived table name.\n        let dr_index = dr.indexes.values().next().unwrap();\n        assert_eq!(dr_index.name, \"delivery_record_seq_id_idx_btree\".into());\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // AUTOGENERATED NAMES — all derived from explicit canonical table name \"FB\"\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        // constraint: col [2] \"ItemCount\" → \"item_count\" under table \"FB\"\n        //   → \"FB_item_count_key\"  (not \"fruit_basket_item_count_key\")\n        assert_eq!(fb.constraints.len(), 1);\n        let (constraint_key, constraint) = fb.constraints.iter().next().unwrap();\n        assert_eq!(\n            &**constraint_key, \"FB_item_count_key\",\n            \"constraint autogenerated from explicit canonical table name\"\n        );\n        assert_eq!(\n            constraint.data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(2).into()\n            }),\n        );\n\n        // sequence: col [0] \"BasketId\" → \"basket_id\" under table \"FB\"\n        //   → \"FB_basket_id_seq\"  (not \"fruit_basket_basket_id_seq\")\n        assert_eq!(fb.sequences.len(), 1);\n        let (seq_key, _) = fb.sequences.iter().next().unwrap();\n        assert_eq!(\n            &**seq_key, \"FB_basket_id_seq\",\n            \"sequence autogenerated from explicit canonical table name\"\n        );\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // REDUCER — explicit \"Deliver\" replaces policy-derived \"do_delivery\"\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        let deliver_ident = id(\"Deliver\");\n        assert!(def.reducers.contains_key(&deliver_ident), \"reducer 'Deliver' not found\");\n        assert!(\n            !def.reducers.contains_key(&id(\"do_delivery\")),\n            \"'do_delivery' must not exist when overridden\"\n        );\n        assert_eq!(def.reducers[&deliver_ident].name.as_identifier(), &deliver_ident);\n\n        // Non-overridden reducer still follows SnakeCase.\n        assert!(def.reducers.contains_key(&id(\"process_item\")));\n        assert!(!def.reducers.contains_key(&id(\"ProcessItem\")));\n\n        // ═══════════════════════════════════════════════════════════════════════════\n        // SCHEDULE — function_name resolves to the explicit canonical reducer name\n        // ═══════════════════════════════════════════════════════════════════════════\n\n        let schedule = dr.schedule.as_ref().expect(\"deliveryRecord should have a schedule\");\n        assert_eq!(&*schedule.name, \"delivery_record_sched\");\n        assert_eq!(\n            schedule.function_name, deliver_ident,\n            \"schedule function_name uses the explicit canonical reducer name\"\n        );\n        assert_eq!(schedule.at_column, 1.into());\n        assert_eq!(schedule.function_kind, FunctionKind::Reducer);\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def/validate/v8.rs",
    "content": "//! Backwards-compatibility for the previous version of the schema definition format.\n//! This will be removed before 1.0.\n\nuse crate::def::{validate::Result, ModuleDef};\nuse crate::error::{RawColumnName, ValidationError, ValidationErrors};\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_lib::db::raw_def::{v8::*, v9::*};\nuse spacetimedb_lib::{\n    // TODO: rename these types globally in a followup PR\n    AlgebraicType,\n    MiscModuleExport as RawMiscModuleExportV8,\n    ProductType,\n    ReducerDef as RawReducerDefV8,\n    TableDesc as RawTableDescV8,\n    TypeAlias as RawTypeAliasV8,\n};\nuse spacetimedb_primitives::{ColId, ColList, ConstraintKind, Constraints};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicTypeRef, Typespace, WithTypespace};\n\nconst INIT_NAME: &str = \"__init__\";\nconst IDENTITY_CONNECTED_NAME: &str = \"__identity_connected__\";\nconst IDENTITY_DISCONNECTED_NAME: &str = \"__identity_disconnected__\";\n\n/// Validate a `RawModuleDefV8` and convert it into a `ModuleDef`,\n/// or return a stream of errors if the definition is invalid.\npub fn validate(def: RawModuleDefV8) -> Result<ModuleDef> {\n    // The logic here is slightly odd.\n    // Most of our errors will come from the v9 validation code.\n    // But, there are some errors that can only happen in v8, e.g. multiple primary keys.\n    // So, we collect those in a side buffer, do the v9 validation, and then merge the two streams.\n    let mut extra_errors = vec![];\n\n    let v9 = upgrade_module(def, &mut extra_errors);\n\n    // Now defer to the v9 validation.\n    let result: Result<ModuleDef> = crate::def::validate::v9::validate(v9);\n    ValidationErrors::add_extra_errors(result, extra_errors).map_err(ValidationErrors::sort_deduplicate)\n}\n\n/// Upgrade a module, returning a v9 module definition.\n/// Most of our validation happens in v9, but there are some errors that can only happen in v8;\n/// these are pushed to the secondary stream of errors.\nfn upgrade_module(def: RawModuleDefV8, extra_errors: &mut Vec<ValidationError>) -> RawModuleDefV9 {\n    let RawModuleDefV8 {\n        typespace,\n        tables,\n        reducers,\n        misc_exports,\n    } = def;\n\n    let tables = convert_all(tables, |table| upgrade_table(table, &typespace, extra_errors));\n    let reducers = convert_all(reducers, upgrade_reducer);\n    let types = misc_exports.into_iter().map(upgrade_misc_export_to_type).collect();\n\n    RawModuleDefV9 {\n        typespace,\n        tables,\n        reducers,\n        types,\n        // V8 module defs don't have procedures or column default values,\n        // which are all we use the `misc_exports` for at this time (pgoldman 2025-10-09).\n        misc_exports: Default::default(),\n        row_level_security: vec![], // v8 doesn't have row-level security\n    }\n}\n\n/// Get an iterator deriving [RawIndexDefV8]s from the constraints that require them like `UNIQUE`.\n///\n/// It looks into [Self::constraints] for possible duplicates and remove them from the result\npub fn generated_indexes(table: &RawTableDefV8) -> impl Iterator<Item = RawIndexDefV8> + '_ {\n    table\n        .constraints\n        .iter()\n        // We are only interested in constraints implying an index.\n        .filter(|x| x.constraints.has_indexed())\n        // Create the `IndexDef`.\n        .map(|x| {\n            let is_unique = x.constraints.has_unique();\n            RawIndexDefV8::for_column(&table.table_name, &x.constraint_name, x.columns.clone(), is_unique)\n        })\n        // Only keep those we don't yet have in the list of indices (checked by name).\n        .filter(|idx| table.indexes.iter().all(|x| x.index_name != idx.index_name))\n}\n\n/// Get an iterator deriving [RawSequenceDefV8] from the constraints that require them like `IDENTITY`.\n///\n/// It looks into [Self::constraints] for possible duplicates and remove them from the result\npub fn generated_sequences(table: &RawTableDefV8) -> impl Iterator<Item = RawSequenceDefV8> + '_ {\n    let cols: HashSet<_> = table.sequences.iter().map(|seq| ColList::new(seq.col_pos)).collect();\n\n    table\n        .constraints\n        .iter()\n        // We are only interested in constraints implying a sequence.\n        .filter(move |x| !cols.contains(&x.columns) && x.constraints.has_autoinc())\n        // Create the `SequenceDef`.\n        .map(|x| RawSequenceDefV8::for_column(&table.table_name, &x.constraint_name, x.columns.head().unwrap()))\n        // Only keep those we don't yet have in the list of sequences (checked by name).\n        .filter(|seq| table.sequences.iter().all(|x| x.sequence_name != seq.sequence_name))\n}\n\n/// Get an iterator deriving [RawConstraintDefV8] from the indexes that require them like `UNIQUE`.\n///\n/// It looks into Self::constraints for possible duplicates and remove them from the result\npub fn generated_constraints(table: &RawTableDefV8) -> impl Iterator<Item = RawConstraintDefV8> + '_ {\n    // Collect the set of all col-lists with a constraint.\n    let cols: HashSet<_> = table\n        .constraints\n        .iter()\n        .filter(|x| x.constraints.kind() != ConstraintKind::UNSET)\n        .map(|x| &x.columns)\n        .collect();\n\n    // Those indices that are not present in the constraints above\n    // have constraints generated for them.\n    // When `idx.is_unique`, a unique constraint is generated rather than an indexed one.\n    table\n        .indexes\n        .iter()\n        .filter(move |idx: &&RawIndexDefV8| !cols.contains(&idx.columns))\n        .map(|idx| table.gen_constraint_def(Constraints::from_is_unique(idx.is_unique), idx.columns.clone()))\n}\n\n/// Upgrade a table, returning a v9 table definition and a stream of v8-only validation errors.\nfn upgrade_table(\n    table: RawTableDescV8,\n    typespace: &Typespace,\n    extra_errors: &mut Vec<ValidationError>,\n) -> RawTableDefV9 {\n    // First, generate all the various things that are needed.\n    // This is the hairiest part of v8.\n    let generated_constraints = generated_constraints(&table.schema).collect::<Vec<_>>();\n    let generated_sequences = generated_sequences(&table.schema).collect::<Vec<_>>();\n    let generated_indexes = generated_indexes(&table.schema).collect::<Vec<_>>();\n\n    let RawTableDescV8 {\n        schema:\n            RawTableDefV8 {\n                table_name,\n                columns,\n                indexes,\n                constraints,\n                sequences,\n                table_type,\n                table_access,\n                scheduled,\n            },\n        data: product_type_ref,\n    } = table;\n\n    // Check all column defs, then discard them.\n    let scheduled_at_col = columns\n        .iter()\n        .position(|x| &*x.col_name == \"scheduled_at\")\n        .map(|i| i as u16);\n    check_all_column_defs(product_type_ref, columns, &table_name, typespace, extra_errors);\n\n    // Now we're ready to go through the various definitions and upgrade them.\n    let indexes = convert_all(indexes.into_iter().chain(generated_indexes), upgrade_index);\n    let sequences = convert_all(sequences.into_iter().chain(generated_sequences), upgrade_sequence);\n    let schedule = upgrade_schedule(scheduled, scheduled_at_col);\n\n    // Constraints are pretty hairy, which is why we're getting rid of v8.\n    let mut primary_key = None;\n    let unique_constraints = constraints\n        .into_iter()\n        .chain(generated_constraints)\n        .filter_map(|constraint| upgrade_constraint(constraint, &table_name, &mut primary_key, extra_errors))\n        .collect();\n\n    let table_type = table_type.into();\n    let table_access = table_access.into();\n\n    RawTableDefV9 {\n        name: table_name,\n        product_type_ref,\n        primary_key: ColList::from_iter(primary_key),\n        indexes,\n        constraints: unique_constraints,\n        sequences,\n        schedule,\n        table_type,\n        table_access,\n    }\n}\n\n/// Check all column definitions.\n/// This is a v8-only validation step, since v9 has no notion of a column definition, relying solely on the product_type_ref to define columns.\nfn check_all_column_defs(\n    product_type_ref: AlgebraicTypeRef,\n    columns: Vec<RawColumnDefV8>,\n    table_name: &RawIdentifier,\n    typespace: &Typespace,\n    extra_errors: &mut Vec<ValidationError>,\n) {\n    // Next, check that the ColumnDefs are compatible with the product_type_ref.\n    // In v8, sometimes the types in ColumnDefs were resolved.\n    // So, we need to resolve everything here before validationg.\n    // First, we resolve the product type.\n    let resolved_product_type = typespace\n        .get(product_type_ref)\n        .and_then(AlgebraicType::as_product)\n        .map(|product_type| WithTypespace::new(typespace, product_type).resolve_refs());\n\n    match resolved_product_type {\n        Some(Ok(resolved_product_type)) => {\n            // We've found a useful product type, check the column definitions and discard them.\n            for (i, column) in columns.into_iter().enumerate() {\n                check_column(\n                    i.into(),\n                    column,\n                    &resolved_product_type,\n                    table_name,\n                    typespace,\n                    extra_errors,\n                );\n            }\n        }\n        _ => {\n            extra_errors.push(ValidationError::InvalidProductTypeRef {\n                table: table_name.clone(),\n                ref_: product_type_ref,\n            });\n        }\n    }\n}\n\n/// Check a column definition.\nfn check_column(\n    id: ColId,\n    column: RawColumnDefV8,\n    resolved_product_type: &ProductType,\n    table_name: &RawIdentifier,\n    typespace: &Typespace,\n    extra_errors: &mut Vec<ValidationError>,\n) {\n    let RawColumnDefV8 { col_name, col_type } = column;\n\n    // for some reason, the original `RawColumnDefv8` sometimes stored *resolved* types.\n    // so, resolve before checking for equality.\n\n    let element = resolved_product_type.elements.get(id.idx());\n\n    let resolved_col_ty = WithTypespace::new(typespace, &col_type).resolve_refs();\n\n    match (element, resolved_col_ty) {\n        (Some(element), Ok(resolved_col_ty)) => {\n            if !element.has_name(&col_name) || element.algebraic_type != resolved_col_ty {\n                extra_errors.push(ValidationError::ColumnDefMalformed {\n                    column: RawColumnName::new(table_name.clone(), col_name),\n                    ty: resolved_col_ty.into(),\n                    pos: id,\n                    product_type: resolved_product_type.clone().into(),\n                });\n            }\n        }\n        _ => extra_errors.push(ValidationError::ColumnDefMalformed {\n            column: RawColumnName::new(table_name.clone(), col_name),\n            ty: col_type.into(),\n            pos: id,\n            product_type: resolved_product_type.clone().into(),\n        }),\n    }\n}\n\n/// Upgrade an index.\nfn upgrade_index(index: RawIndexDefV8) -> RawIndexDefV9 {\n    let RawIndexDefV8 {\n        index_name,\n        is_unique: _, // handled by generated_constraints\n        index_type,\n        columns,\n    } = index;\n\n    let algorithm = match index_type {\n        IndexType::BTree => RawIndexAlgorithm::BTree { columns },\n        IndexType::Hash => RawIndexAlgorithm::Hash { columns },\n    };\n    // The updated bindings macros will correctly distinguish between accessor name and index name as specified in the\n    // ABI stability proposal. The old macros don't make this distinction, so we just reuse the name for them.\n    let accessor_name = Some(index_name.clone());\n    RawIndexDefV9 {\n        name: Some(index_name),\n        // Set the accessor name to be the same as the index name.\n        accessor_name,\n        algorithm,\n    }\n}\n\n/// Upgrade a constraint.\n///\n/// `primary_key` is mutable and will be set to `Some(constraint.columns.as_singleton())` if the constraint is a primary key.\n/// If it has already been set, an error will be pushed to `extra_errors`.\nfn upgrade_constraint(\n    constraint: RawConstraintDefV8,\n    table_name: &RawIdentifier,\n    primary_key: &mut Option<ColId>,\n    extra_errors: &mut Vec<ValidationError>,\n) -> Option<RawConstraintDefV9> {\n    let RawConstraintDefV8 {\n        constraint_name, // not used in v9.\n        constraints,\n        columns,\n    } = constraint;\n\n    if constraints.has_primary_key() {\n        if let Some(col) = columns.as_singleton() {\n            let replaced = primary_key.replace(col);\n            if replaced.is_some() {\n                extra_errors.push(ValidationError::RepeatedPrimaryKey {\n                    table: table_name.clone(),\n                });\n            }\n        } else {\n            // There is a primary key annotation on multiple columns.\n            // client codegen can't handle this.\n            extra_errors.push(ValidationError::RepeatedPrimaryKey {\n                table: table_name.clone(),\n            });\n        }\n    }\n\n    if constraints.has_unique() {\n        Some(RawConstraintDefV9 {\n            name: Some(constraint_name),\n            data: RawConstraintDataV9::Unique(RawUniqueConstraintDataV9 { columns }),\n        })\n    } else {\n        // other constraints are implemented by `generated_sequences`.\n        // Note that `Constraints::unset` will not trigger any of the preceding branches, so will be ignored.\n        // This is consistent with the original `TableSchema::from_(raw_)def`, which also ignored `Constraints::unset`.\n        None\n    }\n}\n\nfn upgrade_schedule(schedule: Option<RawIdentifier>, scheduled_at_col: Option<u16>) -> Option<RawScheduleDefV9> {\n    let scheduled_at_col = scheduled_at_col?;\n    schedule.map(|reducer_name| RawScheduleDefV9 {\n        name: None,\n        reducer_name,\n        scheduled_at_column: scheduled_at_col.into(),\n    })\n}\n\nfn upgrade_sequence(sequence: RawSequenceDefV8) -> RawSequenceDefV9 {\n    let RawSequenceDefV8 {\n        sequence_name,\n        col_pos,\n        increment,\n        start,\n        min_value,\n        max_value,\n        allocated: _, // not used in v9.\n    } = sequence;\n\n    RawSequenceDefV9 {\n        name: Some(sequence_name),\n        column: col_pos,\n        start,\n        increment,\n        min_value,\n        max_value,\n    }\n}\n\nfn upgrade_reducer(reducer: RawReducerDefV8) -> RawReducerDefV9 {\n    let RawReducerDefV8 { name, args } = reducer;\n    let lifecycle = match &name[..] {\n        INIT_NAME => Some(Lifecycle::Init),\n        IDENTITY_CONNECTED_NAME => Some(Lifecycle::OnConnect),\n        IDENTITY_DISCONNECTED_NAME => Some(Lifecycle::OnDisconnect),\n        _ => None,\n    };\n    RawReducerDefV9 {\n        name,\n        // v9 uses the correct name :-)\n        params: ProductType::from_iter(args),\n        lifecycle,\n    }\n}\n\n/// The only possible `RawMiscModuleExportV8` is a type name.\nfn upgrade_misc_export_to_type(misc_export: RawMiscModuleExportV8) -> RawTypeDefV9 {\n    let RawMiscModuleExportV8::TypeAlias(RawTypeAliasV8 { name, ty }) = misc_export;\n\n    let name = sats_name_to_scoped_name(&name);\n\n    RawTypeDefV9 {\n        name,\n        ty,\n        // all types have a custom ordering in v8\n        custom_ordering: true,\n    }\n}\n\nfn convert_all<T, U>(input: impl IntoIterator<Item = T>, f: impl FnMut(T) -> U) -> Vec<U> {\n    input.into_iter().map(f).collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::def::validate::tests::{check_product_type, expect_identifier, expect_type_name};\n    use crate::def::validate::v8::{IDENTITY_CONNECTED_NAME, IDENTITY_DISCONNECTED_NAME, INIT_NAME};\n    use crate::def::IndexAlgorithm;\n    use crate::def::{validate::Result, ModuleDef};\n    use crate::error::*;\n    use crate::type_for_generate::ClientCodegenError;\n\n    use spacetimedb_data_structures::expect_error_matching;\n    use spacetimedb_lib::db::raw_def::*;\n    use spacetimedb_lib::{ScheduleAt, TableDesc};\n    use spacetimedb_primitives::{ColId, ColList, Constraints};\n    use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, ProductType};\n    use v8::RawModuleDefV8Builder;\n    use v9::Lifecycle;\n\n    /// This test attempts to exercise every successful path in the validation code.\n    #[test]\n    fn valid_definition() {\n        let mut builder = RawModuleDefV8Builder::default();\n\n        let product_type = AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::String)]);\n        let product_type_ref = builder.add_type_for_tests(\"scope1.scope2.ReferencedProduct\", product_type.clone());\n\n        let sum_type = AlgebraicType::simple_enum([\"Gala\", \"GrannySmith\", \"RedDelicious\"].into_iter());\n        let sum_type_ref = builder.add_type_for_tests(\"ReferencedSum\", sum_type.clone());\n\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n\n        builder.add_table_for_tests(RawTableDefV8::new_for_tests(\n            \"Apples\",\n            ProductType::from([\n                (\"id\", AlgebraicType::U64),\n                (\"name\", AlgebraicType::String),\n                (\"count\", AlgebraicType::U16),\n                (\"type\", sum_type_ref.into()),\n            ]),\n        ));\n\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([\n                    (\"count\", AlgebraicType::U16),\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\n                        \"optional_product_column\",\n                        AlgebraicType::option(product_type_ref.into()),\n                    ),\n                ]),\n            )\n            .with_column_constraint(Constraints::primary_key_auto(), 0)\n            .with_column_index([0, 1, 2], false),\n        );\n\n        let deliveries_product_type = builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n            )\n            .with_column_constraint(Constraints::primary_key_auto(), 2)\n            .with_scheduled(Some(\"check_deliveries\".into())),\n        );\n\n        builder.add_reducer_for_tests(INIT_NAME, ProductType::unit());\n        builder.add_reducer_for_tests(IDENTITY_CONNECTED_NAME, ProductType::unit());\n        builder.add_reducer_for_tests(IDENTITY_DISCONNECTED_NAME, ProductType::unit());\n        builder.add_reducer_for_tests(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", deliveries_product_type.into())]),\n        );\n        builder.add_reducer_for_tests(\"extra_reducer\", ProductType::from([(\"a\", AlgebraicType::U64)]));\n\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n\n        let apples = expect_identifier(\"Apples\");\n        let bananas = expect_identifier(\"Bananas\");\n        let deliveries = expect_identifier(\"Deliveries\");\n\n        assert_eq!(def.tables.len(), 3);\n\n        let apples_def = &def.tables[&apples];\n\n        assert_eq!(&apples_def.name, &apples);\n        assert_eq!(apples_def.columns.len(), 4);\n        assert_eq!(apples_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(apples_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(apples_def.columns[1].name, expect_identifier(\"name\"));\n        assert_eq!(apples_def.columns[1].ty, AlgebraicType::String);\n        assert_eq!(apples_def.columns[2].name, expect_identifier(\"count\"));\n        assert_eq!(apples_def.columns[2].ty, AlgebraicType::U16);\n        assert_eq!(apples_def.columns[3].name, expect_identifier(\"type\"));\n        assert_eq!(apples_def.columns[3].ty, sum_type_ref.into());\n        assert_eq!(apples_def.primary_key, None);\n\n        let bananas_def = &def.tables[&bananas];\n\n        assert_eq!(&bananas_def.name, &bananas);\n        assert_eq!(bananas_def.columns.len(), 4);\n        assert_eq!(bananas_def.columns[0].name, expect_identifier(\"count\"));\n        assert_eq!(bananas_def.columns[0].ty, AlgebraicType::U16);\n        assert_eq!(bananas_def.columns[1].name, expect_identifier(\"id\"));\n        assert_eq!(bananas_def.columns[1].ty, AlgebraicType::U64);\n        assert_eq!(bananas_def.columns[2].name, expect_identifier(\"name\"));\n        assert_eq!(bananas_def.columns[2].ty, AlgebraicType::String);\n        assert_eq!(\n            bananas_def.columns[3].name,\n            expect_identifier(\"optional_product_column\")\n        );\n        assert_eq!(\n            bananas_def.columns[3].ty,\n            AlgebraicType::option(product_type_ref.into())\n        );\n        assert_eq!(bananas_def.primary_key, Some(0.into()));\n\n        let delivery_def = &def.tables[&deliveries];\n        assert_eq!(&delivery_def.name, &deliveries);\n        assert_eq!(delivery_def.columns.len(), 3);\n        assert_eq!(delivery_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(delivery_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.columns[1].name, expect_identifier(\"scheduled_at\"));\n        assert_eq!(delivery_def.columns[1].ty, schedule_at_type);\n        assert_eq!(delivery_def.columns[2].name, expect_identifier(\"scheduled_id\"));\n        assert_eq!(delivery_def.columns[2].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.schedule.as_ref().unwrap().at_column, 1.into());\n        assert_eq!(\n            &delivery_def.schedule.as_ref().unwrap().function_name[..],\n            \"check_deliveries\"\n        );\n        assert_eq!(delivery_def.primary_key, Some(ColId(2)));\n\n        assert_eq!(def.typespace.get(product_type_ref), Some(&product_type));\n        assert_eq!(def.typespace.get(sum_type_ref), Some(&sum_type));\n\n        check_product_type(&def, apples_def);\n        check_product_type(&def, bananas_def);\n        check_product_type(&def, delivery_def);\n\n        let product_type_name = expect_type_name(\"scope1::scope2::ReferencedProduct\");\n        let sum_type_name = expect_type_name(\"ReferencedSum\");\n        let apples_type_name = expect_type_name(\"Apples\");\n        let bananas_type_name = expect_type_name(\"Bananas\");\n        let deliveries_type_name = expect_type_name(\"Deliveries\");\n\n        assert_eq!(def.types[&product_type_name].ty, product_type_ref);\n        assert_eq!(def.types[&sum_type_name].ty, sum_type_ref);\n        assert_eq!(def.types[&apples_type_name].ty, apples_def.product_type_ref);\n        assert_eq!(def.types[&bananas_type_name].ty, bananas_def.product_type_ref);\n        assert_eq!(def.types[&deliveries_type_name].ty, delivery_def.product_type_ref);\n\n        let init_name = expect_identifier(INIT_NAME);\n        assert_eq!(&*def.reducers[&init_name].name, &*init_name);\n        assert_eq!(def.reducers[&init_name].lifecycle, Some(Lifecycle::Init));\n\n        let identity_connected_name = expect_identifier(IDENTITY_CONNECTED_NAME);\n        assert_eq!(&*def.reducers[&identity_connected_name].name, &*identity_connected_name);\n        assert_eq!(\n            def.reducers[&identity_connected_name].lifecycle,\n            Some(Lifecycle::OnConnect)\n        );\n\n        let identity_disconnected_name = expect_identifier(IDENTITY_DISCONNECTED_NAME);\n        assert_eq!(\n            &*def.reducers[&identity_disconnected_name].name,\n            &*identity_disconnected_name\n        );\n        assert_eq!(\n            def.reducers[&identity_disconnected_name].lifecycle,\n            Some(Lifecycle::OnDisconnect)\n        );\n\n        let extra_reducer_name = expect_identifier(\"extra_reducer\");\n        assert_eq!(&*def.reducers[&extra_reducer_name].name, &*extra_reducer_name);\n        assert_eq!(def.reducers[&extra_reducer_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&extra_reducer_name].params,\n            ProductType::from([(\"a\", AlgebraicType::U64)])\n        );\n\n        let check_deliveries_name = expect_identifier(\"check_deliveries\");\n        assert_eq!(&*def.reducers[&check_deliveries_name].name, &*check_deliveries_name);\n        assert_eq!(def.reducers[&check_deliveries_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&check_deliveries_name].params,\n            ProductType::from([(\"a\", deliveries_product_type.into())])\n        );\n    }\n\n    #[test]\n    fn invalid_product_type_ref() {\n        let mut builder = RawModuleDefV8Builder::default();\n\n        // `add_table` does NOT initialize table.product_type_ref, which should result in an error.\n        builder.add_table(TableDesc {\n            schema: RawTableDefV8::new_for_tests(\"Bananas\", ProductType::from([(\"count\", AlgebraicType::U32)])),\n            data: AlgebraicTypeRef(1337),\n        });\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidProductTypeRef { table, ref_ } => {\n            &table[..] == \"Bananas\" && ref_ == &AlgebraicTypeRef(1337)\n        });\n    }\n\n    #[test]\n    fn invalid_table_name() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(RawTableDefV8::new_for_tests(\n            \"\",\n            ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n        ));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_column_name() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(RawTableDefV8::new_for_tests(\n            \"\",\n            ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n        ));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_ref() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_index(ColList::from_iter([0, 55]), false),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, column, .. } => {\n            &table[..] == \"Bananas\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_ref() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_constraint(Constraints::unique(), ColList::from_iter([55])),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, column, .. } => {\n            &table[..] == \"Bananas\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_sequence_column_ref() {\n        // invalid column id\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_sequence(ColId(55)),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, column, .. } => {\n            &table[..] == \"Bananas\" &&\n            column == &55.into()\n        });\n\n        // incorrect column type\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::String)]),\n            )\n            .with_column_sequence(ColId(1)),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidSequenceColumnType { column, column_type, .. } => {\n            column == &RawColumnName::new(\"Bananas\", \"a\") &&\n            column_type.0 == AlgebraicType::String\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_duplicates() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_index(ColList::from_iter([0, 0]), false),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ columns, .. } => {\n            columns == &ColList::from_iter([0, 0])\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_duplicates() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_constraint(Constraints::unique(), ColList::from_iter([1, 1])),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ columns, .. } => {\n            columns == &ColList::from_iter([1, 1])\n        });\n    }\n\n    #[test]\n    fn recursive_ref() {\n        let recursive_type = AlgebraicType::product([(\"a\", AlgebraicTypeRef(0).into())]);\n\n        let mut builder = RawModuleDefV8Builder::default();\n        let ref_ = builder.add_type_for_tests(\"Recursive\", recursive_type.clone());\n        builder.add_reducer_for_tests(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: ModuleDef = builder.finish().try_into().unwrap();\n\n        assert!(result.typespace_for_generate[ref_].is_recursive());\n    }\n\n    #[test]\n    fn out_of_bounds_ref() {\n        let invalid_type_1 = AlgebraicType::product([(\"a\", AlgebraicTypeRef(31).into())]);\n        let mut builder = RawModuleDefV8Builder::default();\n        let ref_ = builder.add_type_for_tests(\"Invalid\", invalid_type_1.clone());\n        builder.add_reducer_for_tests(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ClientCodegenError { location, error: ClientCodegenError::TypeRefError(_)  } => {\n            location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) }\n        });\n    }\n\n    #[test]\n    fn invalid_use() {\n        let inner_type_invalid_for_use = AlgebraicType::product([(\"b\", AlgebraicType::U32)]);\n        let invalid_type = AlgebraicType::product([(\"a\", inner_type_invalid_for_use.clone())]);\n        let mut builder = RawModuleDefV8Builder::default();\n        let ref_ = builder.add_type_for_tests(\"Invalid\", invalid_type.clone());\n        builder.add_reducer_for_tests(\"silly\", ProductType::from([(\"a\", ref_.into())]));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(\n            result,\n            ValidationError::ClientCodegenError {\n                location,\n                error: ClientCodegenError::NonSpecialTypeNotAUse { ty }\n            } => {\n                location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) } &&\n                ty.0 == inner_type_invalid_for_use\n            }\n        );\n    }\n\n    #[test]\n    fn allows_hash_indexes() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_indexes(vec![RawIndexDefV8 {\n                columns: [0].into(),\n                is_unique: false,\n                index_name: \"Bananas_index\".into(),\n                index_type: IndexType::Hash,\n            }]),\n        );\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n        let indexes = def.indexes().collect::<Vec<_>>();\n        assert_eq!(indexes.len(), 1);\n        assert_eq!(indexes[0].algorithm, IndexAlgorithm::Hash(0.into()));\n    }\n\n    #[test]\n    fn invalid_primary_key() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_table_for_tests(\n            RawTableDefV8::new_for_tests(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n            )\n            .with_column_constraint(Constraints::primary_key(), ColList::from_iter([44])),\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, column, .. } => {\n            &table[..] == \"Bananas\" &&\n            column == &44.into()\n        });\n    }\n\n    #[test]\n    fn duplicate_type_name() {\n        let mut builder = RawModuleDefV8Builder::default();\n        builder.add_type_for_tests(\"scope1.scope2.Duplicate\", AlgebraicType::U64);\n        builder.add_type_for_tests(\"scope1::scope2::Duplicate\", AlgebraicType::U32);\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateTypeName { name } => {\n            name == &expect_type_name(\"scope1::scope2::Duplicate\")\n        });\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def/validate/v9.rs",
    "content": "use crate::def::validate::v10::{ExplicitNamesLookup, ValidationCase};\nuse crate::def::*;\nuse crate::error::{RawColumnName, ValidationError};\nuse crate::type_for_generate::{ClientCodegenError, ProductTypeDef, TypespaceForGenerateBuilder};\nuse crate::{def::validate::Result, error::TypeLocation};\nuse convert_case::{Case, Casing};\nuse lean_string::LeanString;\nuse spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors};\nuse spacetimedb_data_structures::map::{HashMap, HashSet};\nuse spacetimedb_lib::db::default_element_ordering::{product_type_has_default_ordering, sum_type_has_default_ordering};\nuse spacetimedb_lib::db::raw_def::v10::{reducer_default_err_return_type, reducer_default_ok_return_type};\nuse spacetimedb_lib::db::raw_def::v9::RawViewDefV9;\nuse spacetimedb_lib::db::view::{extract_view_return_product_type_ref, ViewKind};\nuse spacetimedb_lib::ProductType;\nuse spacetimedb_primitives::col_list;\nuse spacetimedb_sats::{bsatn::de::Deserializer, de::DeserializeSeed, WithTypespace};\n\n/// Validate a `RawModuleDefV9` and convert it into a `ModuleDef`,\n/// or return a stream of errors if the definition is invalid.\npub fn validate(def: RawModuleDefV9) -> Result<ModuleDef> {\n    let RawModuleDefV9 {\n        typespace,\n        tables,\n        reducers,\n        types,\n        misc_exports,\n        row_level_security,\n    } = def;\n\n    let known_type_definitions = types.iter().map(|def| def.ty);\n\n    let mut validator = ModuleValidatorV9 {\n        core: CoreValidator {\n            typespace: &typespace,\n            stored_in_table_def: Default::default(),\n            type_namespace: Default::default(),\n            lifecycle_reducers: Default::default(),\n            typespace_for_generate: TypespaceForGenerate::builder(&typespace, known_type_definitions),\n            case_policy: ValidationCase::None,\n            explicit_names: ExplicitNamesLookup::default(),\n        },\n    };\n\n    // Important general note:\n    // This file uses the `ErrorStream` combinator to return *multiple errors\n    // at once* when validating a definition.\n    // The general pattern is that we use `collect_all_errors` when building\n    // a collection, and `combine_errors` when we have multiple\n    // things to validate that are independent of each other.\n    // We try to avoid using `?` until the end of a function, after we've called\n    // `combine_errors` or `collect_all_errors` on all the things we need to validate.\n    // Sometimes it is unavoidable to use `?` early and this should be commented on.\n\n    let reducers = reducers\n        .into_iter()\n        .enumerate()\n        .map(|(idx, reducer)| validator.validate_reducer_def(reducer, ReducerId(idx as u32)))\n        // Collect into a `Vec` first to preserve duplicate names.\n        // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`.\n        .collect_all_errors::<Vec<_>>();\n\n    let (procedures, misc_exports) =\n        misc_exports\n            .into_iter()\n            .partition::<Vec<RawMiscModuleExportV9>, _>(|misc_export| {\n                matches!(misc_export, RawMiscModuleExportV9::Procedure(_))\n            });\n\n    let (views, misc_exports) = misc_exports\n        .into_iter()\n        .partition::<Vec<RawMiscModuleExportV9>, _>(|misc_export| {\n            matches!(misc_export, RawMiscModuleExportV9::View(_))\n        });\n\n    let procedures = procedures\n        .into_iter()\n        .map(|procedure| {\n            let RawMiscModuleExportV9::Procedure(procedure) = procedure else {\n                unreachable!(\"Already partitioned procedures separate from other `RawMiscModuleExportV9` variants\");\n            };\n            procedure\n        })\n        .map(|procedure| {\n            validator\n                .validate_procedure_def(procedure)\n                .map(|procedure_def| (procedure_def.name.clone(), procedure_def))\n        })\n        // Collect into a `Vec` first to preserve duplicate names.\n        // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`.\n        .collect_all_errors::<Vec<_>>();\n\n    let views = views\n        .into_iter()\n        .map(|view| {\n            let RawMiscModuleExportV9::View(view) = view else {\n                unreachable!(\"Already partitioned views separate from other `RawMiscModuleExportV9` variants\");\n            };\n            view\n        })\n        .map(|view| {\n            validator\n                .validate_view_def(view)\n                .map(|view_def| (view_def.name.clone(), view_def))\n        })\n        .collect_all_errors();\n\n    let tables = tables\n        .into_iter()\n        .map(|table| {\n            validator\n                .validate_table_def(table)\n                .map(|table_def| (table_def.name.clone(), table_def))\n        })\n        .collect_all_errors();\n\n    let row_level_security_raw = row_level_security\n        .into_iter()\n        .map(|rls| (rls.sql.clone(), rls))\n        .collect();\n\n    let mut refmap = HashMap::default();\n    let types = types\n        .into_iter()\n        .map(|ty| {\n            validator.core.validate_type_def(ty).map(|type_def| {\n                refmap.insert(type_def.ty, type_def.accessor_name.clone());\n                (type_def.accessor_name.clone(), type_def)\n            })\n        })\n        .collect_all_errors::<HashMap<_, _>>();\n\n    let tables_types_reducers_procedures_views = (tables, types, reducers, procedures, views)\n        .combine_errors()\n        .and_then(|(mut tables, types, reducers, procedures, views)| {\n            let ((reducers, procedures, views), ()) = (\n                check_function_names_are_unique(reducers, procedures, views),\n                check_non_procedure_misc_exports(misc_exports, &validator, &mut tables),\n            )\n                .combine_errors()?;\n            check_scheduled_functions_exist(&mut tables, &reducers, &procedures)?;\n            Ok((tables, types, reducers, procedures, views))\n        });\n\n    let CoreValidator {\n        stored_in_table_def,\n        typespace_for_generate,\n        lifecycle_reducers,\n        ..\n    } = validator.core;\n\n    let (tables, types, reducers, procedures, views) =\n        (tables_types_reducers_procedures_views).map_err(|errors| errors.sort_deduplicate())?;\n\n    let typespace_for_generate = typespace_for_generate.finish();\n\n    Ok(ModuleDef {\n        tables,\n        reducers,\n        views,\n        types,\n        typespace,\n        typespace_for_generate,\n        stored_in_table_def,\n        refmap,\n        row_level_security_raw,\n        lifecycle_reducers,\n        procedures,\n        raw_module_def_version: RawModuleDefVersion::V9OrEarlier,\n    })\n}\n\nstruct ModuleValidatorV9<'a> {\n    core: CoreValidator<'a>,\n}\n\nimpl ModuleValidatorV9<'_> {\n    fn validate_table_def(&mut self, table: RawTableDefV9) -> Result<TableDef> {\n        let RawTableDefV9 {\n            name: raw_table_name,\n            product_type_ref,\n            primary_key,\n            indexes,\n            constraints,\n            sequences,\n            schedule,\n            table_type,\n            table_access,\n        } = table;\n\n        // We exit early if we don't find the product type ref,\n        // since this breaks all the other checks.\n        let product_type: &ProductType = self\n            .core\n            .typespace\n            .get(product_type_ref)\n            .and_then(AlgebraicType::as_product)\n            .ok_or_else(|| {\n                ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                    table: raw_table_name.clone(),\n                    ref_: product_type_ref,\n                })\n            })?;\n\n        let mut table_in_progress =\n            TableValidator::new(raw_table_name.clone(), product_type_ref, product_type, &mut self.core)?;\n\n        let table_ident = table_in_progress.table_ident.clone();\n\n        let columns = (0..product_type.elements.len())\n            .map(|id| table_in_progress.validate_column_def(id.into(), product_type))\n            .collect_all_errors();\n\n        let indexes = indexes\n            .into_iter()\n            .map(|index| {\n                table_in_progress\n                    .validate_index_def_v9(index)\n                    .map(|index| (index.name.clone(), index))\n            })\n            .collect_all_errors::<StrMap<_>>();\n\n        // We can't validate the primary key without validating the unique constraints first.\n        let primary_key_head = primary_key.head();\n        let constraints_primary_key = constraints\n            .into_iter()\n            .map(|constraint| {\n                table_in_progress\n                    .validate_constraint_def(constraint, |name, cols| {\n                        name.unwrap_or_else(|| generate_unique_constraint_name(&table_ident, product_type, cols))\n                    })\n                    .map(|constraint| (constraint.name.clone(), constraint))\n            })\n            .collect_all_errors()\n            .and_then(|constraints: StrMap<ConstraintDef>| {\n                table_in_progress.validate_primary_key(constraints, primary_key)\n            });\n\n        // Now that we've validated indices and constraints separately,\n        // we can validate their interactions.\n        // More specifically, a direct index requires a unique constraint.\n        let constraints_backed_by_indices =\n            if let (Ok((constraints, _)), Ok(indexes)) = (&constraints_primary_key, &indexes) {\n                constraints\n                    .values()\n                    .filter_map(|c| c.data.unique_columns().map(|cols| (c, cols)))\n                    // TODO(centril): this check is actually too strict\n                    // and ends up unnecessarily inducing extra indices.\n                    //\n                    // It is sufficient for `unique_cols` to:\n                    // a) be a permutation of `index`'s columns,\n                    //    as a permutation of a set is still the same set,\n                    //    so when we use the index to check the constraint,\n                    //    the order in the index does not matter for the purposes of the constraint.\n                    //\n                    // b) for `unique_cols` to form a prefix of `index`'s columns,\n                    //    if the index provides efficient prefix scans.\n                    //\n                    // Currently, b) is unsupported,\n                    // as we cannot decouple unique constraints from indices in the datastore today,\n                    // and we cannot mark the entire index unique,\n                    // as that would not be a sound representation of what the user wanted.\n                    // If we wanted to, we could make the constraints merely use indices,\n                    // rather than be indices.\n                    .filter(|(_, unique_cols)| {\n                        !indexes\n                            .values()\n                            .any(|i| ColSet::from(i.algorithm.columns()) == **unique_cols)\n                    })\n                    .map(|(c, cols)| {\n                        let constraint = c.name.clone();\n                        let columns = cols.clone();\n                        Err(ValidationError::UniqueConstraintWithoutIndex { constraint, columns }.into())\n                    })\n                    .collect_all_errors()\n            } else {\n                Ok(())\n            };\n\n        let sequences = sequences\n            .into_iter()\n            .map(|sequence| {\n                table_in_progress\n                    .validate_sequence_def(sequence)\n                    .map(|sequence| (sequence.name.clone(), sequence))\n            })\n            .collect_all_errors();\n\n        let schedule = schedule\n            .map(|schedule| table_in_progress.validate_schedule_def(schedule, primary_key_head))\n            .transpose();\n\n        let name = table_in_progress\n            .add_to_global_namespace(raw_table_name.clone())\n            .and_then(|name| {\n                let name = identifier(name)?;\n                if table_type != TableType::System && name.starts_with(\"st_\") {\n                    Err(ValidationError::TableNameReserved { table: name }.into())\n                } else {\n                    Ok(name)\n                }\n            });\n\n        let (name, columns, indexes, (constraints, primary_key), (), sequences, schedule) = (\n            name,\n            columns,\n            indexes,\n            constraints_primary_key,\n            constraints_backed_by_indices,\n            sequences,\n            schedule,\n        )\n            .combine_errors()?;\n\n        Ok(TableDef {\n            name: name.clone(),\n            product_type_ref,\n            primary_key,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            schedule,\n            table_type,\n            table_access,\n            is_event: false, // V9 does not support event tables\n            accessor_name: name,\n        })\n    }\n\n    /// Validate a reducer definition.\n    fn validate_reducer_def(\n        &mut self,\n        reducer_def: RawReducerDefV9,\n        reducer_id: ReducerId,\n    ) -> Result<(Identifier, ReducerDef)> {\n        let RawReducerDefV9 {\n            name,\n            params,\n            lifecycle,\n        } = reducer_def;\n\n        let params_for_generate: Result<_> =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ReducerArg {\n                    reducer_name: name.clone(),\n                    position,\n                    arg_name,\n                });\n\n        // Reducers share the \"function namespace\" with procedures.\n        // Uniqueness is validated in a later pass, in `check_function_names_are_unique`.\n        let name = identifier(name);\n\n        let lifecycle = lifecycle\n            .map(|lifecycle| match &mut self.core.lifecycle_reducers[lifecycle] {\n                x @ None => {\n                    *x = Some(reducer_id);\n                    Ok(lifecycle)\n                }\n                Some(_) => Err(ValidationError::DuplicateLifecycle { lifecycle }.into()),\n            })\n            .transpose();\n        let (reducer_name, params_for_generate, lifecycle) = (name, params_for_generate, lifecycle).combine_errors()?;\n        let name = ReducerName::new(reducer_name.clone());\n        let def = ReducerDef {\n            name: name.clone(),\n            accessor_name: name.clone(),\n            params: params.clone(),\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false, // A ProductTypeDef not stored in a Typespace cannot be recursive.\n            },\n            lifecycle,\n            visibility: FunctionVisibility::ClientCallable,\n            ok_return_type: reducer_default_ok_return_type(),\n            err_return_type: reducer_default_err_return_type(),\n        };\n        Ok((reducer_name, def))\n    }\n\n    fn validate_procedure_def(&mut self, procedure_def: RawProcedureDefV9) -> Result<ProcedureDef> {\n        let RawProcedureDefV9 {\n            name,\n            params,\n            return_type,\n        } = procedure_def;\n\n        let params_for_generate =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ProcedureArg {\n                    procedure_name: name.clone(),\n                    position,\n                    arg_name,\n                });\n\n        let return_type_for_generate = self.core.validate_for_type_use(\n            || TypeLocation::ProcedureReturn {\n                procedure_name: name.clone(),\n            },\n            &return_type,\n        );\n\n        // Procedures share the \"function namespace\" with reducers.\n        // Uniqueness is validated in a later pass, in `check_function_names_are_unique`.\n        let name = identifier(name);\n\n        let (name, params_for_generate, return_type_for_generate) =\n            (name, params_for_generate, return_type_for_generate).combine_errors()?;\n\n        Ok(ProcedureDef {\n            name: name.clone(),\n            accessor_name: name,\n            params,\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false, // A ProductTypeDef not stored in a Typespace cannot be recursive.\n            },\n            return_type,\n            return_type_for_generate,\n            visibility: FunctionVisibility::ClientCallable,\n        })\n    }\n\n    /// Validate a view definition.\n    fn validate_view_def(&mut self, view_def: RawViewDefV9) -> Result<ViewDef> {\n        let RawViewDefV9 {\n            name,\n            is_public,\n            is_anonymous,\n            params,\n            return_type,\n            index,\n        } = view_def;\n\n        let invalid_return_type = || {\n            ValidationErrors::from(ValidationError::InvalidViewReturnType {\n                view: name.clone(),\n                ty: return_type.clone().into(),\n            })\n        };\n\n        // The possible return types of a view are `Vec<T>`, `Option<T>`, or `Query<T>`,\n        // where `T` is a `ProductType` in the `Typespace`.\n        // Here we extract the inner product type ref `T`.\n        // We exit early for errors since this breaks all the other checks.\n        let (product_type_ref, return_kind) =\n            extract_view_return_product_type_ref(&return_type).ok_or_else(invalid_return_type)?;\n\n        let product_type = self\n            .core\n            .typespace\n            .get(product_type_ref)\n            .and_then(AlgebraicType::as_product)\n            .ok_or_else(|| {\n                ValidationErrors::from(ValidationError::InvalidProductTypeRef {\n                    table: name.clone(),\n                    ref_: product_type_ref,\n                })\n            })?;\n\n        let params_for_generate =\n            self.core\n                .params_for_generate(&params, |position, arg_name| TypeLocation::ViewArg {\n                    view_name: name.clone(),\n                    position,\n                    arg_name,\n                })?;\n\n        let return_type_for_generate_input = match return_kind {\n            ViewKind::Procedural => return_type.clone(),\n            // Query-builder views still return rows from the client's perspective.\n            // For codegen purposes we model this as `Vec<T>`.\n            ViewKind::Query => AlgebraicType::array(product_type_ref.into()),\n        };\n\n        let return_type_for_generate = self.core.validate_for_type_use(\n            || TypeLocation::ViewReturn {\n                view_name: name.clone(),\n            },\n            &return_type_for_generate_input,\n        );\n\n        let mut view_in_progress = ViewValidator::new(\n            name.clone(),\n            product_type_ref,\n            product_type,\n            &params,\n            &params_for_generate,\n            &mut self.core,\n        )?;\n\n        // Views have the same interface as tables and therefore must be registered in the global namespace.\n        //\n        // Note, views also share the \"function namespace\" with reducers and procedures.\n        // While this isn't strictly necessary because reducers and views have different calling contexts,\n        // we may want to support calling views in the same context as reducers in the future (e.g. `spacetime call`).\n        // Hence we validate uniqueness among reducer, procedure, and view names in a later pass.\n        // See `check_function_names_are_unique`.\n        let name = view_in_progress.add_to_global_namespace(name).and_then(identifier);\n\n        let n = product_type.elements.len();\n        let return_columns = (0..n)\n            .map(|id| view_in_progress.validate_view_column_def(id.into(), product_type))\n            .collect_all_errors();\n\n        let n = params.elements.len();\n        let param_columns = (0..n)\n            .map(|id| view_in_progress.validate_param_column_def(id.into()))\n            .collect_all_errors();\n\n        let (name, return_type_for_generate, return_columns, param_columns) =\n            (name, return_type_for_generate, return_columns, param_columns).combine_errors()?;\n\n        Ok(ViewDef {\n            name: name.clone(),\n            is_anonymous,\n            is_public,\n            params,\n            fn_ptr: index.into(),\n            params_for_generate: ProductTypeDef {\n                elements: params_for_generate,\n                recursive: false, // A `ProductTypeDef` not stored in a `Typespace` cannot be recursive.\n            },\n            return_type,\n            return_type_for_generate,\n            product_type_ref,\n            primary_key: None,\n            return_columns,\n            param_columns,\n            accessor_name: name,\n        })\n    }\n\n    fn validate_column_default_value(\n        &self,\n        tables: &HashMap<Identifier, TableDef>,\n        cdv: &RawColumnDefaultValueV9,\n    ) -> Result<AlgebraicValue> {\n        let table_name = self.core.resolve_identifier_with_case(cdv.table.clone())?;\n\n        // Extract the table. We cannot make progress otherwise.\n        let table = tables.get(&table_name).ok_or_else(|| ValidationError::TableNotFound {\n            table: cdv.table.clone(),\n        })?;\n\n        // Get the column that a default is being added to.\n        let Some(col) = table.columns.get(cdv.col_id.idx()) else {\n            return Err(ValidationError::ColumnNotFound {\n                table: cdv.table.clone(),\n                def: cdv.table.clone(),\n                column: cdv.col_id,\n            }\n            .into());\n        };\n\n        // First time the type of the default value is known, so decode it.\n        let mut reader = &cdv.value[..];\n        let ty = WithTypespace::new(self.core.typespace, &col.ty);\n        let field_value: Result<AlgebraicValue> =\n            ty.deserialize(Deserializer::new(&mut reader)).map_err(|decode_error| {\n                ValidationError::ColumnDefaultValueMalformed {\n                    table: cdv.table.clone(),\n                    col_id: cdv.col_id,\n                    err: decode_error,\n                }\n                .into()\n            });\n\n        field_value\n    }\n}\n\n/// Collects state used during validation.\npub(crate) struct CoreValidator<'a> {\n    /// The typespace of the module.\n    ///\n    /// Behind a reference to ensure we don't accidentally mutate it.\n    pub(crate) typespace: &'a Typespace,\n\n    /// The in-progress typespace used to generate client types.\n    pub(crate) typespace_for_generate: TypespaceForGenerateBuilder<'a>,\n\n    /// Names we have seen so far.\n    ///\n    /// It would be nice if we could have span information here, but currently it isn't passed\n    /// through the ABI boundary.\n    /// We could add it as a `MiscModuleExport` later without breaking the ABI.\n    pub(crate) stored_in_table_def: StrMap<Identifier>,\n\n    /// Module-scoped type names we have seen so far.\n    pub(crate) type_namespace: HashMap<ScopedTypeName, AlgebraicTypeRef>,\n\n    /// Reducers that play special lifecycle roles.\n    pub(crate) lifecycle_reducers: EnumMap<Lifecycle, Option<ReducerId>>,\n\n    pub(crate) case_policy: ValidationCase,\n\n    pub(crate) explicit_names: ExplicitNamesLookup,\n}\n\npub(crate) fn identifier(raw: RawIdentifier) -> Result<Identifier> {\n    Identifier::new(RawIdentifier::new(LeanString::from_utf8(raw.as_bytes()).unwrap()))\n        .map_err(|error| ValidationError::IdentifierError { error }.into())\n}\n\nimpl CoreValidator<'_> {\n    fn resolve_identifier(\n        &self,\n        source: RawIdentifier,\n        lookup: &HashMap<RawIdentifier, RawIdentifier>,\n    ) -> Result<Identifier> {\n        if let Some(canonical_name) = lookup.get(&source) {\n            Identifier::new(canonical_name.clone()).map_err(|error| ValidationError::IdentifierError { error }.into())\n        } else {\n            self.resolve_identifier_with_case(source)\n        }\n    }\n\n    pub(crate) fn resolve_table_ident(&self, source: RawIdentifier) -> Result<Identifier> {\n        self.resolve_identifier(source, &self.explicit_names.tables)\n    }\n\n    pub(crate) fn resolve_function_ident(&self, source: RawIdentifier) -> Result<Identifier> {\n        self.resolve_identifier(source, &self.explicit_names.functions)\n    }\n\n    pub(crate) fn resolve_index_ident(&self, source: RawIdentifier) -> Result<Identifier> {\n        self.resolve_identifier(source, &self.explicit_names.indexes)\n    }\n\n    /// Apply case conversion to an identifier.\n    pub(crate) fn resolve_identifier_with_case(&self, raw: RawIdentifier) -> Result<Identifier> {\n        let ident = convert(raw, self.case_policy);\n\n        Identifier::new(ident.into()).map_err(|error| ValidationError::IdentifierError { error }.into())\n    }\n\n    /// Convert a raw identifier to a canonical type name.\n    ///\n    /// IMPORTANT: For all policies except `None`, type names are converted to PascalCase,\n    /// unless explicitly specified by the user.\n    pub(crate) fn resolve_type_with_case(&self, raw: RawIdentifier) -> Result<Identifier> {\n        let mut ident = raw.to_string();\n        if !matches!(self.case_policy, ValidationCase::None) {\n            ident = ident.to_case(Case::Pascal);\n        }\n\n        Identifier::new(ident.into()).map_err(|error| ValidationError::IdentifierError { error }.into())\n    }\n\n    // Recursive function to change typenames in the typespace according to the case conversion\n    // policy.\n    pub(crate) fn typespace_case_conversion(case_policy: ValidationCase, typespace: &mut Typespace) {\n        let case_policy_for_enum_variants = if matches!(case_policy, ValidationCase::SnakeCase) {\n            ValidationCase::CamelCase\n        } else {\n            case_policy\n        };\n\n        for ty in &mut typespace.types {\n            Self::convert_algebraic_type(ty, case_policy, case_policy_for_enum_variants);\n        }\n    }\n\n    // Recursively convert names in an AlgebraicType\n    fn convert_algebraic_type(\n        ty: &mut AlgebraicType,\n        case_policy: ValidationCase,\n        case_policy_for_enum_variants: ValidationCase,\n    ) {\n        if ty.is_special() {\n            return;\n        }\n        match ty {\n            AlgebraicType::Product(product) => {\n                for element in &mut product.elements.iter_mut() {\n                    // Convert the element name if it exists\n                    if let Some(name) = element.name() {\n                        let new_name = convert(name.clone(), case_policy);\n                        element.name = Some(new_name.into());\n                    }\n                    // Recursively convert the element's type\n                    Self::convert_algebraic_type(\n                        &mut element.algebraic_type,\n                        case_policy,\n                        case_policy_for_enum_variants,\n                    );\n                }\n            }\n            AlgebraicType::Sum(sum) => {\n                for variant in &mut sum.variants.iter_mut() {\n                    // Convert the variant name if it exists\n                    if let Some(name) = variant.name() {\n                        let new_name = convert(name.clone(), case_policy_for_enum_variants);\n                        variant.name = Some(new_name.into())\n                    }\n                    // Recursively convert the variant's type\n                    Self::convert_algebraic_type(\n                        &mut variant.algebraic_type,\n                        case_policy,\n                        case_policy_for_enum_variants,\n                    );\n                }\n            }\n            AlgebraicType::Array(array) => {\n                // Arrays contain a base type that might need conversion\n                Self::convert_algebraic_type(&mut array.elem_ty, case_policy, case_policy_for_enum_variants);\n            }\n            _ => {}\n        }\n    }\n\n    pub(crate) fn params_for_generate(\n        &mut self,\n        params: &ProductType,\n        make_type_location: impl Fn(usize, Option<RawIdentifier>) -> TypeLocation,\n    ) -> Result<Box<[(Identifier, AlgebraicTypeUse)]>> {\n        params\n            .elements\n            .iter()\n            .enumerate()\n            .map(|(position, param)| {\n                let location = || make_type_location(position, param.name().cloned());\n                let param_name = param\n                    .name()\n                    .cloned()\n                    .ok_or_else(|| {\n                        ValidationError::ClientCodegenError {\n                            location: location(),\n                            error: ClientCodegenError::NamelessReducerParam,\n                        }\n                        .into()\n                    })\n                    .and_then(|s| self.resolve_identifier_with_case(s));\n                let ty_use = self.validate_for_type_use(location, &param.algebraic_type);\n                (param_name, ty_use).combine_errors()\n            })\n            .collect_all_errors()\n    }\n\n    /// Add a `name` to the global namespace.\n    ///\n    /// If it has already been added, return an error.\n    ///\n    /// This is not used for all `Def` types.\n    pub(crate) fn add_to_global_namespace(&mut self, name: RawIdentifier, ident: Identifier) -> Result<RawIdentifier> {\n        // This may report the table_name as invalid multiple times, but this will be removed\n        // when we sort and deduplicate the error stream.\n        if self.stored_in_table_def.contains_key(&name) {\n            Err(ValidationError::DuplicateName { name }.into())\n        } else {\n            self.stored_in_table_def.insert(name.clone(), ident);\n            Ok(name)\n        }\n    }\n\n    /// Validate a type definition.\n    pub(crate) fn validate_type_def(&mut self, type_def: RawTypeDefV9) -> Result<TypeDef> {\n        let RawTypeDefV9 {\n            name,\n            ty,\n            custom_ordering,\n        } = type_def;\n\n        // Do these together since they are related.\n        let ty_custom_ordering: Result<(AlgebraicTypeRef, bool)> = self\n            .typespace\n            .get(ty)\n            .ok_or_else(|| {\n                ValidationError::InvalidTypeRef {\n                    type_name: name.clone(),\n                    ref_: ty,\n                }\n                .into()\n            })\n            .and_then(|pointed_to| {\n                let ordering_ok = if custom_ordering {\n                    Ok(())\n                } else {\n                    let correct = match pointed_to {\n                        AlgebraicType::Sum(sum) => sum_type_has_default_ordering(sum),\n                        AlgebraicType::Product(product) => product_type_has_default_ordering(product),\n                        _ => true,\n                    };\n                    if correct {\n                        Ok(())\n                    } else {\n                        Err(ValidationError::TypeHasIncorrectOrdering {\n                            type_name: name.clone(),\n                            ref_: ty,\n                            bad_type: pointed_to.clone().into(),\n                        }\n                        .into())\n                    }\n                };\n\n                // Now check the definition is valid\n                let def_ok = self.validate_for_type_definition(ty);\n\n                let ((), ()) = (ordering_ok, def_ok).combine_errors()?;\n\n                // note: we return the reference `ty`, not the pointed-to type `pointed_to`.\n                // The reference is semantically important.\n                Ok((ty, custom_ordering))\n            });\n\n        let RawScopedTypeNameV9 {\n            name: unscoped_name,\n            scope,\n        } = name;\n\n        let unscoped_name = self.resolve_type_with_case(unscoped_name);\n        let scope = Vec::from(scope)\n            .into_iter()\n            .map(|t| self.resolve_type_with_case(t))\n            .collect();\n\n        let name = (unscoped_name, scope)\n            .combine_errors()\n            .and_then(|(unscoped_name, scope)| {\n                let result = ScopedTypeName {\n                    name: unscoped_name,\n                    scope,\n                };\n                match self.type_namespace.insert(result.clone(), ty) {\n                    Some(_) => Err(ValidationError::DuplicateTypeName { name: result.clone() }.into()),\n                    None => Ok(result),\n                }\n            });\n\n        let (name, (ty, custom_ordering)) = (name, ty_custom_ordering).combine_errors()?;\n\n        Ok(TypeDef {\n            accessor_name: name,\n            ty,\n            custom_ordering,\n        })\n    }\n\n    /// Validates that a type can be used to generate a client type use.\n    pub(crate) fn validate_for_type_use(\n        &mut self,\n        mut location: impl FnMut() -> TypeLocation,\n        ty: &AlgebraicType,\n    ) -> Result<AlgebraicTypeUse> {\n        self.typespace_for_generate.parse_use(ty).map_err(|err| {\n            ErrorStream::expect_nonempty(err.into_iter().map(|error| ValidationError::ClientCodegenError {\n                location: location(),\n                error,\n            }))\n        })\n    }\n\n    /// Validates that a type can be used to generate a client type definition.\n    pub(crate) fn validate_for_type_definition(&mut self, ref_: AlgebraicTypeRef) -> Result<()> {\n        self.typespace_for_generate.add_definition(ref_).map_err(|err| {\n            ErrorStream::expect_nonempty(err.into_iter().map(|error| ValidationError::ClientCodegenError {\n                location: TypeLocation::InTypespace { ref_ },\n                error,\n            }))\n        })\n    }\n\n    pub(crate) fn register_lifecycle(&mut self, lifecycle: Lifecycle, reducer_id: ReducerId) -> Result<()> {\n        match &mut self.lifecycle_reducers[lifecycle] {\n            x @ None => {\n                *x = Some(reducer_id);\n                Ok(())\n            }\n            Some(_) => Err(ValidationError::DuplicateLifecycle { lifecycle }.into()),\n        }\n    }\n\n    pub(crate) fn validate_schedule_def(\n        &mut self,\n        table_name: RawIdentifier,\n        name: RawIdentifier,\n        function_name: RawIdentifier,\n        product_type: &ProductType,\n        schedule_at_col: ColId,\n        primary_key: Option<ColId>,\n    ) -> Result<ScheduleDef> {\n        let at_column = product_type\n            .elements\n            .get(schedule_at_col.idx())\n            .is_some_and(|ty| ty.algebraic_type.is_schedule_at())\n            .then_some(schedule_at_col);\n\n        let id_column = primary_key.filter(|pk| {\n            product_type\n                .elements\n                .get(pk.idx())\n                .is_some_and(|ty| ty.algebraic_type == AlgebraicType::U64)\n        });\n\n        // Error if either column is missing.\n        let at_id = at_column.zip(id_column).ok_or_else(|| {\n            ValidationError::ScheduledIncorrectColumns {\n                table: table_name.clone(),\n                columns: product_type.clone(),\n            }\n            .into()\n        });\n        let table_name = self.resolve_table_ident(table_name)?;\n        let name_res = self.add_to_global_namespace(name.clone(), table_name);\n        let function_name = self.resolve_function_ident(function_name);\n\n        let (_, (at_column, id_column), function_name) = (name_res, at_id, function_name).combine_errors()?;\n\n        Ok(ScheduleDef {\n            name: Identifier::new(name).map_err(|error| ValidationError::IdentifierError { error })?,\n            at_column,\n            id_column,\n            function_name,\n\n            // Fill this in as a placeholder now.\n            // It will be populated with the correct `FunctionKind` later,\n            // in `check_scheduled_functions_exist`.\n            function_kind: FunctionKind::Unknown,\n        })\n    }\n}\n\n/// A partially validated view.\n///\n/// This is just a small wrapper around [`TableValidator`] so that we can:\n/// 1. Validate column defs\n/// 2. Insert view names into the global namespace.\npub(crate) struct ViewValidator<'a, 'b> {\n    inner: TableValidator<'a, 'b>,\n    params: &'a ProductType,\n    params_for_generate: &'a [(Identifier, AlgebraicTypeUse)],\n}\n\nimpl<'a, 'b> ViewValidator<'a, 'b> {\n    pub(crate) fn new(\n        raw_name: RawIdentifier,\n        product_type_ref: AlgebraicTypeRef,\n        product_type: &'a ProductType,\n        params: &'a ProductType,\n        params_for_generate: &'a [(Identifier, AlgebraicTypeUse)],\n        module_validator: &'a mut CoreValidator<'b>,\n    ) -> Result<Self> {\n        Ok(Self {\n            inner: TableValidator::new(raw_name, product_type_ref, product_type, module_validator)?,\n            params,\n            params_for_generate,\n        })\n    }\n\n    pub(crate) fn validate_param_column_def(&mut self, col_id: ColId) -> Result<ViewParamDef> {\n        let column = &self\n            .params\n            .elements\n            .get(col_id.idx())\n            .expect(\"enumerate is generating an out-of-range index...\");\n\n        let (_, ty_for_generate) = self\n            .params_for_generate\n            .get(col_id.idx())\n            .expect(\"enumerate is generating an out-of-range index...\");\n\n        let name: Result<Identifier> = self.inner.module_validator.resolve_identifier_with_case(\n            column\n                .name()\n                .cloned()\n                .unwrap_or_else(|| RawIdentifier::new(format!(\"param_{}\", col_id))),\n        );\n\n        // This error will be created multiple times if the view name is invalid,\n        // but we sort and deduplicate the error stream afterwards,\n        // so it isn't a huge deal.\n        //\n        // This is necessary because we require `ErrorStream` to be nonempty.\n        // We need to put something in there if the view name is invalid.\n        let view_name = self\n            .inner\n            .module_validator\n            .resolve_identifier_with_case(self.inner.raw_name.clone());\n\n        let (name, view_name) = (name, view_name).combine_errors()?;\n\n        Ok(ViewParamDef {\n            name,\n            ty: column.algebraic_type.clone(),\n            ty_for_generate: ty_for_generate.clone(),\n            col_id,\n            view_name,\n        })\n    }\n\n    pub(crate) fn validate_view_column_def(\n        &mut self,\n        col_id: ColId,\n        product_type: &'a ProductType,\n    ) -> Result<ViewColumnDef> {\n        self.inner\n            .validate_column_def(col_id, product_type)\n            .map(ViewColumnDef::from)\n    }\n\n    pub(crate) fn add_to_global_namespace(&mut self, name: RawIdentifier) -> Result<RawIdentifier> {\n        self.inner.add_to_global_namespace(name)\n    }\n}\n\n/// A partially validated table.\npub(crate) struct TableValidator<'a, 'b> {\n    pub(crate) module_validator: &'a mut CoreValidator<'b>,\n    raw_name: RawIdentifier,\n    product_type_ref: AlgebraicTypeRef,\n    product_type: &'a ProductType,\n    has_sequence: HashSet<ColId>,\n    pub(crate) table_ident: Identifier,\n}\n\nimpl<'a, 'b> TableValidator<'a, 'b> {\n    pub(crate) fn new(\n        raw_name: RawIdentifier,\n        product_type_ref: AlgebraicTypeRef,\n        product_type: &'a ProductType,\n        module_validator: &'a mut CoreValidator<'b>,\n    ) -> Result<Self> {\n        let table_ident = module_validator.resolve_table_ident(raw_name.clone())?;\n        Ok(Self {\n            raw_name,\n            product_type_ref,\n            product_type,\n            module_validator,\n            has_sequence: Default::default(),\n            table_ident,\n        })\n    }\n    /// Validate a column.\n    ///\n    /// Note that this accepts a `ProductTypeElement` rather than a `ColumnDef`,\n    /// because all information about columns is stored in the `Typespace` in ABI version 9.\n    pub(crate) fn validate_column_def(&mut self, col_id: ColId, product_type: &'a ProductType) -> Result<ColumnDef> {\n        let column = product_type\n            .elements\n            .get(col_id.idx())\n            .expect(\"enumerate is generating an out-of-range index...\");\n\n        let accessor_name = column.name().cloned().ok_or_else(|| {\n            ValidationError::UnnamedColumn {\n                column: self.raw_column_name(col_id),\n            }\n            .into()\n        });\n\n        let ty_for_generate = self.module_validator.validate_for_type_use(\n            || TypeLocation::InTypespace {\n                ref_: self.product_type_ref,\n            },\n            &column.algebraic_type,\n        );\n\n        let (accessor_name, ty_for_generate) = (accessor_name, ty_for_generate).combine_errors()?;\n\n        Ok(ColumnDef {\n            accessor_name: identifier(accessor_name.clone())?,\n            name: self.module_validator.resolve_identifier_with_case(accessor_name)?,\n            ty: column.algebraic_type.clone(),\n            ty_for_generate,\n            col_id,\n            table_name: self.table_ident.clone(),\n            default_value: None, // filled in later\n        })\n    }\n\n    pub(crate) fn validate_primary_key(\n        &mut self,\n        validated_constraints: StrMap<ConstraintDef>,\n        primary_key: ColList,\n    ) -> Result<(StrMap<ConstraintDef>, Option<ColId>)> {\n        if primary_key.len() > 1 {\n            return Err(ValidationError::RepeatedPrimaryKey {\n                table: self.raw_name.clone(),\n            }\n            .into());\n        }\n        let pk = primary_key\n            .head()\n            .map(|pk| -> Result<ColId> {\n                let pk = self.validate_col_id(&self.raw_name, pk)?;\n                let pk_col_list = ColSet::from(pk);\n                if validated_constraints.values().any(|constraint| {\n                    let ConstraintData::Unique(UniqueConstraintData { columns }) = &constraint.data;\n                    columns == &pk_col_list\n                }) {\n                    Ok(pk)\n                } else {\n                    Err(ValidationError::MissingPrimaryKeyUniqueConstraint {\n                        column: self.raw_column_name(pk),\n                    }\n                    .into())\n                }\n            })\n            .transpose()?;\n        Ok((validated_constraints, pk))\n    }\n\n    pub(crate) fn validate_sequence_def(&mut self, sequence: RawSequenceDefV9) -> Result<SequenceDef> {\n        let RawSequenceDefV9 {\n            column,\n            min_value,\n            start,\n            max_value,\n            increment,\n            name,\n        } = sequence;\n\n        let name = name.unwrap_or_else(|| generate_sequence_name(&self.table_ident, self.product_type, column));\n\n        // The column for the sequence exists and is an appropriate type.\n        let column = self.validate_col_id(&name, column).and_then(|col_id| {\n            let ty = &self.product_type.elements[col_id.idx()].algebraic_type;\n\n            if !ty.is_integer() {\n                Err(ValidationError::InvalidSequenceColumnType {\n                    sequence: name.clone(),\n                    column: self.raw_column_name(col_id),\n                    column_type: ty.clone().into(),\n                }\n                .into())\n            } else if !self.has_sequence.insert(col_id) {\n                Err(ValidationError::OneAutoInc {\n                    column: self.raw_column_name(col_id),\n                }\n                .into())\n            } else {\n                Ok(col_id)\n            }\n        });\n\n        /// Compare two `Option<i128>` values, returning `true` if `lo <= hi`,\n        /// or if either is `None`.\n        pub(crate) fn le(lo: Option<i128>, hi: Option<i128>) -> bool {\n            match (lo, hi) {\n                (Some(lo), Some(hi)) => lo <= hi,\n                _ => true,\n            }\n        }\n        let valid = le(min_value, start) && le(start, max_value) && le(min_value, max_value);\n\n        let min_start_max = if valid {\n            Ok((min_value, start, max_value))\n        } else {\n            Err(ValidationError::InvalidSequenceRange {\n                sequence: name.clone(),\n                min_value,\n                start,\n                max_value,\n            }\n            .into())\n        };\n\n        let name = self.add_to_global_namespace(name);\n\n        let (name, column, (min_value, start, max_value)) = (name, column, min_start_max).combine_errors()?;\n\n        Ok(SequenceDef {\n            name,\n            column,\n            min_value,\n            start,\n            max_value,\n            increment,\n        })\n    }\n\n    /// Validates an index definition for V9 and earlier versions\n    pub(crate) fn validate_index_def_v9(&mut self, index: RawIndexDefV9) -> Result<IndexDef> {\n        let RawIndexDefV9 {\n            name,\n            algorithm: algorithm_raw,\n            accessor_name,\n        } = index;\n\n        let name = name.unwrap_or_else(|| generate_index_name(&self.table_ident, self.product_type, &algorithm_raw));\n\n        let name = self.add_to_global_namespace(name)?;\n\n        let algorithm = self.validate_algorithm(&name, algorithm_raw)?;\n\n        // In V9, accessor_name is used for codegen\n        let codegen_name = accessor_name\n            .map(|s| self.module_validator.resolve_identifier_with_case(s))\n            .transpose()?;\n\n        Ok(IndexDef {\n            name: name.clone(),\n            accessor_name: codegen_name,\n            source_name: name,\n            algorithm,\n        })\n    }\n\n    /// Validates an index definition for V10 and later versions\n    pub(crate) fn validate_index_def_v10(&mut self, index: RawIndexDefV10) -> Result<IndexDef> {\n        let RawIndexDefV10 {\n            source_name,\n            algorithm: algorithm_raw,\n            accessor_name,\n        } = index;\n\n        //source_name will be used as alias, hence we need to add it to the global namespace as\n        //well.\n        let source_name = source_name.expect(\"source_name should be provided in V10, accessor_names inside module\");\n        let source_name = self.add_to_global_namespace(source_name.clone())?;\n\n        let name = if self.module_validator.explicit_names.indexes.get(&source_name).is_some() {\n            self.module_validator.resolve_index_ident(source_name.clone())?\n        } else {\n            identifier(generate_index_name(\n                &self.table_ident,\n                self.product_type,\n                &algorithm_raw,\n            ))?\n        };\n\n        let name = if *name.as_raw() != source_name {\n            self.add_to_global_namespace(name.as_raw().clone())?\n        } else {\n            name.as_raw().clone()\n        };\n\n        let algorithm = self.validate_algorithm(&name, algorithm_raw.clone())?;\n\n        Ok(IndexDef {\n            name: name.clone(),\n            accessor_name: accessor_name.map(identifier).transpose()?,\n            source_name,\n            algorithm,\n        })\n    }\n\n    /// Common validation logic for index algorithms\n    fn validate_algorithm(&mut self, name: &RawIdentifier, algorithm_raw: RawIndexAlgorithm) -> Result<IndexAlgorithm> {\n        match algorithm_raw {\n            RawIndexAlgorithm::BTree { columns } => self\n                .validate_col_ids(name, columns)\n                .map(|columns| BTreeAlgorithm { columns }.into()),\n\n            RawIndexAlgorithm::Hash { columns } => self\n                .validate_col_ids(name, columns)\n                .map(|columns| HashAlgorithm { columns }.into()),\n\n            RawIndexAlgorithm::Direct { column } => self.validate_col_id(name, column).and_then(|column| {\n                let field = &self.product_type.elements[column.idx()];\n                let ty = &field.algebraic_type;\n                let is_bad_type = match ty {\n                    AlgebraicType::U8 | AlgebraicType::U16 | AlgebraicType::U32 | AlgebraicType::U64 => false,\n                    AlgebraicType::Ref(r) => self.module_validator.typespace[*r]\n                        .as_sum()\n                        .is_none_or(|s| !s.is_simple_enum()),\n                    AlgebraicType::Sum(sum) if sum.is_simple_enum() => false,\n                    _ => true,\n                };\n                if is_bad_type {\n                    return Err(ValidationError::DirectIndexOnBadType {\n                        index: name.clone(),\n                        column: field\n                            .name\n                            .clone()\n                            .unwrap_or_else(|| RawIdentifier::new(format!(\"{}\", column.idx()))),\n                        ty: ty.clone().into(),\n                    }\n                    .into());\n                }\n\n                Ok(DirectAlgorithm { column }.into())\n            }),\n\n            algo => unreachable!(\"unknown algorithm {algo:?}\"),\n        }\n    }\n\n    /// Validate a unique constraint definition.\n    pub(crate) fn validate_constraint_def<F>(\n        &mut self,\n        constraint: RawConstraintDefV9,\n        make_name: F,\n    ) -> Result<ConstraintDef>\n    where\n        F: FnOnce(Option<RawIdentifier>, &ColList) -> RawIdentifier,\n    {\n        let RawConstraintDefV9 { name, data } = constraint;\n\n        if let RawConstraintDataV9::Unique(RawUniqueConstraintDataV9 { columns }) = data {\n            let name = make_name(name, &columns);\n\n            let columns: Result<ColList> = self.validate_col_ids(&name, columns);\n            let name = self.add_to_global_namespace(name);\n\n            let (name, columns) = (name, columns).combine_errors()?;\n            let columns: ColSet = columns.into();\n            Ok(ConstraintDef {\n                name,\n                data: ConstraintData::Unique(UniqueConstraintData { columns }),\n            })\n        } else {\n            unimplemented!(\"Unknown constraint type\")\n        }\n    }\n\n    /// Validate a schedule definition.\n    pub(crate) fn validate_schedule_def(\n        &mut self,\n        schedule: RawScheduleDefV9,\n        primary_key: Option<ColId>,\n    ) -> Result<ScheduleDef> {\n        let RawScheduleDefV9 {\n            // Despite the field name, a `RawScheduleDefV9` may refer to either a reducer or a function.\n            reducer_name: function_name,\n            scheduled_at_column,\n            name,\n        } = schedule;\n\n        let name = name.unwrap_or_else(|| generate_schedule_name(&self.table_ident.clone()));\n\n        self.module_validator.validate_schedule_def(\n            self.raw_name.clone(),\n            name,\n            function_name,\n            self.product_type,\n            scheduled_at_column,\n            primary_key,\n        )\n    }\n\n    /// Validate `name` as an `Identifier` and add it to the global namespace, registering the corresponding `Def` as being stored in a  particular `TableDef`.\n    ///\n    /// If it has already been added, return an error.\n    ///\n    /// This is not used for all `Def` types.\n    pub(crate) fn add_to_global_namespace(&mut self, name: RawIdentifier) -> Result<RawIdentifier> {\n        // This may report the table_name as invalid multiple times, but this will be removed\n        // when we sort and deduplicate the error stream.\n        self.module_validator\n            .add_to_global_namespace(name, self.table_ident.clone())\n    }\n\n    /// Validate a `ColId` for this table, returning it unmodified if valid.\n    /// `def_name` is the name of the definition being validated and is used in errors.\n    pub(crate) fn validate_col_id(&self, def_name: &RawIdentifier, col_id: ColId) -> Result<ColId> {\n        if self.product_type.elements.get(col_id.idx()).is_some() {\n            Ok(col_id)\n        } else {\n            Err(ValidationError::ColumnNotFound {\n                column: col_id,\n                table: self.raw_name.clone(),\n                def: def_name.clone(),\n            }\n            .into())\n        }\n    }\n\n    /// Validate a `ColList` for this table, returning it unmodified if valid.\n    /// `def_name` is the name of the definition being validated and is used in errors.\n    pub(crate) fn validate_col_ids(&self, def_name: &RawIdentifier, ids: ColList) -> Result<ColList> {\n        let mut collected: Vec<ColId> = ids\n            .iter()\n            .map(|column| self.validate_col_id(def_name, column))\n            .collect_all_errors()?;\n\n        collected.sort();\n        collected.dedup();\n\n        if collected.len() != ids.len() as usize {\n            Err(ValidationError::DuplicateColumns {\n                columns: ids,\n                def: def_name.clone(),\n            }\n            .into())\n        } else {\n            Ok(ids)\n        }\n    }\n\n    /// Return a best effort name for this column, to be used in errors.\n    /// If we can't find a string name for it, use an integer instead.\n    ///\n    /// (It's generally preferable to avoid integer names, since types using the default\n    /// ordering are implicitly shuffled!)\n    pub(crate) fn raw_column_name(&self, col_id: ColId) -> RawColumnName {\n        let column: RawIdentifier = self\n            .product_type\n            .elements\n            .get(col_id.idx())\n            .and_then(|col| col.name())\n            .cloned()\n            .unwrap_or_else(|| RawIdentifier::new(format!(\"{col_id}\")));\n\n        RawColumnName {\n            table: self.raw_name.clone(),\n            column,\n        }\n    }\n}\n\n/// Get the name of a column in the typespace.\n///\n/// Only used for generating names for indexes, sequences, and unique constraints.\n///\n/// Generates `col_{column}` if the column has no name or if the `RawTableDef`'s `table_type_ref`\n/// was initialized incorrectly.\nfn column_name(table_type: &ProductType, column: ColId) -> String {\n    table_type\n        .elements\n        .get(column.idx())\n        .and_then(|column| column.name().map(ToString::to_string))\n        .unwrap_or_else(|| format!(\"col_{}\", column.0))\n}\n\n/// Concatenate a list of column names.\nfn concat_column_names(table_type: &ProductType, selected: &ColList) -> String {\n    selected.iter().map(|col| column_name(table_type, col)).join(\"_\")\n}\n\n/// All indexes have this name format.\n///\n/// Generated name should not go through case conversion.\npub fn generate_index_name(\n    table_name: &Identifier,\n    table_type: &ProductType,\n    algorithm: &RawIndexAlgorithm,\n) -> RawIdentifier {\n    let (label, columns) = match algorithm {\n        RawIndexAlgorithm::BTree { columns } => (\"btree\", columns),\n        RawIndexAlgorithm::Direct { column } => (\"direct\", &col_list![*column]),\n        RawIndexAlgorithm::Hash { columns } => (\"hash\", columns),\n        _ => unimplemented!(\"Unknown index algorithm {:?}\", algorithm),\n    };\n    let column_names = concat_column_names(table_type, columns);\n    RawIdentifier::new(format!(\"{table_name}_{column_names}_idx_{label}\"))\n}\n\n/// All sequences have this name format.\n///\n/// Generated name should not go through case conversion.\npub fn generate_sequence_name(table_name: &Identifier, table_type: &ProductType, column: ColId) -> RawIdentifier {\n    let column_name = column_name(table_type, column);\n    RawIdentifier::new(format!(\"{table_name}_{column_name}_seq\"))\n}\n\n/// All schedules have this name format.\n///\n/// Generated name should not go through case conversion.\npub fn generate_schedule_name(table_name: &Identifier) -> RawIdentifier {\n    RawIdentifier::new(format!(\"{table_name}_sched\"))\n}\n\n/// All unique constraints have this name format.\n///\n/// Generated name should not go through case conversion.\npub fn generate_unique_constraint_name(\n    table_name: &Identifier,\n    product_type: &ProductType,\n    columns: &ColList,\n) -> RawIdentifier {\n    let column_names = concat_column_names(product_type, columns);\n    RawIdentifier::new(format!(\"{table_name}_{column_names}_key\"))\n}\n\n/// Helper to create an `Identifier` from a `RawIdentifier` with the appropriate error type.\n/// TODO: memoize this.\n//pub(crate) fn identifier(name: RawIdentifier) -> Result<Identifier> {\n//    Identifier::new(name).map_err(|error| ValidationError::IdentifierError { error }.into())\n//}\npub(crate) fn convert(identifier: RawIdentifier, policy: ValidationCase) -> String {\n    let identifier = identifier.to_string();\n\n    match policy {\n        ValidationCase::SnakeCase => identifier.to_case(Case::Snake),\n        ValidationCase::CamelCase => identifier.to_case(Case::Camel),\n        ValidationCase::None => identifier,\n    }\n}\n\n/// Check that every [`ScheduleDef`]'s `function_name` refers to a real reducer or procedure\n/// and that the function's arguments are appropriate for the table,\n/// then record the scheduled function's [`FunctionKind`] in the [`ScheduleDef`].\npub(crate) fn check_scheduled_functions_exist(\n    tables: &mut IdentifierMap<TableDef>,\n    reducers: &IndexMap<Identifier, ReducerDef>,\n    procedures: &IndexMap<Identifier, ProcedureDef>,\n) -> Result<()> {\n    let validate_params =\n        |params_from_function: &ProductType, table_row_type_ref: AlgebraicTypeRef, function_name: Identifier| {\n            if params_from_function.elements.len() == 1\n                && params_from_function.elements[0].algebraic_type == table_row_type_ref.into()\n            {\n                Ok(())\n            } else {\n                Err(ValidationError::IncorrectScheduledFunctionParams {\n                    function_name: function_name.into(),\n                    function_kind: FunctionKind::Reducer,\n                    expected: AlgebraicType::product([AlgebraicType::Ref(table_row_type_ref)]).into(),\n                    actual: params_from_function.clone().into(),\n                })\n            }\n        };\n\n    tables\n        .values_mut()\n        .map(|table| -> Result<()> {\n            if let Some(schedule) = &mut table.schedule {\n                if let Some(reducer) = reducers.get(&schedule.function_name) {\n                    schedule.function_kind = FunctionKind::Reducer;\n                    validate_params(&reducer.params, table.product_type_ref, reducer.name.clone().into())\n                        .map_err(Into::into)\n                } else if let Some(procedure) = procedures.get(&schedule.function_name) {\n                    schedule.function_kind = FunctionKind::Procedure;\n                    validate_params(&procedure.params, table.product_type_ref, procedure.name.clone())\n                        .map_err(Into::into)\n                } else {\n                    Err(ValidationError::MissingScheduledFunction {\n                        schedule: schedule.name.clone(),\n                        function: schedule.function_name.clone(),\n                    }\n                    .into())\n                }\n            } else {\n                Ok(())\n            }\n        })\n        .collect_all_errors()\n}\n\n/// Check that all function (reducer, procedure, or view) names are unique,\n/// then re-organize the reducers and procedures into [`IndexMap`]s\n/// for storage in the [`ModuleDef`].\n#[allow(clippy::type_complexity)]\npub fn check_function_names_are_unique(\n    reducers: Vec<(Identifier, ReducerDef)>,\n    procedures: Vec<(Identifier, ProcedureDef)>,\n    views: Vec<(Identifier, ViewDef)>,\n) -> Result<(\n    IndexMap<Identifier, ReducerDef>,\n    IndexMap<Identifier, ProcedureDef>,\n    IndexMap<Identifier, ViewDef>,\n)> {\n    let mut errors = vec![];\n\n    let mut reducers_map = IndexMap::with_capacity(reducers.len());\n\n    for (name, def) in reducers {\n        if reducers_map.contains_key(&name) {\n            errors.push(ValidationError::DuplicateFunctionName { name });\n        } else {\n            reducers_map.insert(name, def);\n        }\n    }\n\n    let mut procedures_map = IndexMap::with_capacity(procedures.len());\n\n    for (name, def) in procedures {\n        if reducers_map.contains_key(&name) || procedures_map.contains_key(&name) {\n            errors.push(ValidationError::DuplicateFunctionName { name });\n        } else {\n            procedures_map.insert(name, def);\n        }\n    }\n\n    let mut views_map = IndexMap::with_capacity(views.len());\n\n    for (name, def) in views {\n        if reducers_map.contains_key(&name) || procedures_map.contains_key(&name) || views_map.contains_key(&name) {\n            errors.push(ValidationError::DuplicateFunctionName { name });\n        } else {\n            views_map.insert(name, def);\n        }\n    }\n\n    ErrorStream::add_extra_errors(Ok((reducers_map, procedures_map, views_map)), errors)\n}\n\nfn check_non_procedure_misc_exports(\n    misc_exports: Vec<RawMiscModuleExportV9>,\n    validator: &ModuleValidatorV9,\n    tables: &mut IdentifierMap<TableDef>,\n) -> Result<()> {\n    misc_exports\n        .into_iter()\n        .map(|export| match export {\n            RawMiscModuleExportV9::ColumnDefaultValue(cdv) => process_column_default_value(&cdv, validator, tables),\n            RawMiscModuleExportV9::Procedure(_proc) => {\n                unreachable!(\"Procedure defs should already have been sorted out of `misc_exports`\")\n            }\n            _ => unimplemented!(\"unknown misc export\"),\n        })\n        .collect_all_errors::<()>()\n}\n\nfn process_column_default_value(\n    cdv: &RawColumnDefaultValueV9,\n    validator: &ModuleValidatorV9,\n    tables: &mut IdentifierMap<TableDef>,\n) -> Result<()> {\n    // Validate the default value\n    let validated_value = validator.validate_column_default_value(tables, cdv)?;\n\n    let table_name = validator.core.resolve_identifier_with_case(cdv.table.clone())?;\n    let table = tables\n        .get_mut(&table_name)\n        .ok_or_else(|| ValidationError::TableNotFound {\n            table: cdv.table.clone(),\n        })?;\n\n    let column = table\n        .columns\n        .get_mut(cdv.col_id.idx())\n        .ok_or_else(|| ValidationError::ColumnNotFound {\n            table: cdv.table.clone(),\n            def: cdv.table.clone(),\n            column: cdv.col_id,\n        })?;\n\n    // Ensure there's only one default value.\n    if column.default_value.is_some() {\n        return Err(ValidationError::MultipleColumnDefaultValues {\n            table: cdv.table.clone(),\n            col_id: cdv.col_id,\n        }\n        .into());\n    }\n\n    // Set the default value\n    column.default_value = Some(validated_value);\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::def::validate::tests::{\n        check_product_type, expect_identifier, expect_raw_type_name, expect_resolve, expect_type_name,\n    };\n    use crate::def::{validate::Result, ModuleDef};\n    use crate::def::{\n        BTreeAlgorithm, ConstraintData, ConstraintDef, DirectAlgorithm, FunctionKind, IndexAlgorithm, IndexDef,\n        SequenceDef, UniqueConstraintData,\n    };\n    use crate::error::*;\n    use crate::type_for_generate::ClientCodegenError;\n\n    use itertools::Itertools;\n    use spacetimedb_data_structures::expect_error_matching;\n    use spacetimedb_lib::db::raw_def::v9::{btree, direct, hash};\n    use spacetimedb_lib::db::raw_def::*;\n    use spacetimedb_lib::ScheduleAt;\n    use spacetimedb_primitives::{ColId, ColList, ColSet};\n    use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, AlgebraicValue, ProductType, SumValue};\n    use v9::{Lifecycle, RawModuleDefV9Builder, TableAccess, TableType};\n\n    /// This test attempts to exercise every successful path in the validation code.\n    #[test]\n    fn valid_definition() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        let product_type = AlgebraicType::product([(\"a\", AlgebraicType::U64), (\"b\", AlgebraicType::String)]);\n        let product_type_ref = builder.add_algebraic_type(\n            [\"scope1\".into(), \"scope2\".into()],\n            \"ReferencedProduct\",\n            product_type.clone(),\n            false,\n        );\n\n        let sum_type = AlgebraicType::simple_enum([\"Gala\", \"GrannySmith\", \"RedDelicious\"].into_iter());\n        let sum_type_ref = builder.add_algebraic_type([], \"ReferencedSum\", sum_type.clone(), false);\n\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n\n        let red_delicious = AlgebraicValue::Sum(SumValue::new(2, ()));\n\n        builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\"count\", AlgebraicType::U16),\n                    (\"type\", sum_type_ref.into()),\n                ]),\n                true,\n            )\n            .with_index(btree([1, 2]), \"apples_id\")\n            .with_index(direct(2), \"Apples_count_direct\")\n            .with_unique_constraint(2)\n            .with_index(btree(3), \"Apples_type_btree\")\n            .with_unique_constraint(3)\n            .with_default_column_value(2, AlgebraicValue::U16(37))\n            .with_default_column_value(3, red_delicious.clone())\n            .finish();\n\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([\n                    (\"count\", AlgebraicType::U16),\n                    (\"id\", AlgebraicType::U64),\n                    (\"name\", AlgebraicType::String),\n                    (\n                        \"optional_product_column\",\n                        AlgebraicType::option(product_type_ref.into()),\n                    ),\n                ]),\n                false,\n            )\n            .with_column_sequence(0)\n            .with_unique_constraint(ColId(0))\n            .with_primary_key(0)\n            .with_access(TableAccess::Private)\n            .with_index(btree(0), \"bananas_count\")\n            .with_index(btree([0, 1, 2]), \"bananas_count_id_name\")\n            .finish();\n\n        let deliveries_product_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index(btree(2), \"scheduled_id_index\")\n            .with_schedule(\"check_deliveries\", 1)\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_reducer(\"init\", ProductType::unit(), Some(Lifecycle::Init));\n        builder.add_reducer(\"on_connect\", ProductType::unit(), Some(Lifecycle::OnConnect));\n        builder.add_reducer(\"on_disconnect\", ProductType::unit(), Some(Lifecycle::OnDisconnect));\n        builder.add_reducer(\"extra_reducer\", ProductType::from([(\"a\", AlgebraicType::U64)]), None);\n        builder.add_reducer(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", deliveries_product_type.into())]),\n            None,\n        );\n\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n\n        let apples = expect_identifier(\"Apples\");\n        let bananas = expect_identifier(\"Bananas\");\n        let deliveries = expect_identifier(\"Deliveries\");\n\n        assert_eq!(def.tables.len(), 3);\n\n        let apples_def = &def.tables[&apples];\n\n        assert_eq!(apples_def.name, apples);\n        assert_eq!(apples_def.table_type, TableType::User);\n        assert_eq!(apples_def.table_access, TableAccess::Public);\n\n        assert_eq!(apples_def.columns.len(), 4);\n        assert_eq!(apples_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(apples_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(apples_def.columns[0].default_value, None);\n        assert_eq!(apples_def.columns[1].name, expect_identifier(\"name\"));\n        assert_eq!(apples_def.columns[1].ty, AlgebraicType::String);\n        assert_eq!(apples_def.columns[1].default_value, None);\n        assert_eq!(apples_def.columns[2].name, expect_identifier(\"count\"));\n        assert_eq!(apples_def.columns[2].ty, AlgebraicType::U16);\n        assert_eq!(apples_def.columns[2].default_value, Some(AlgebraicValue::U16(37)));\n        assert_eq!(apples_def.columns[3].name, expect_identifier(\"type\"));\n        assert_eq!(apples_def.columns[3].ty, sum_type_ref.into());\n        assert_eq!(apples_def.columns[3].default_value, Some(red_delicious));\n        assert_eq!(expect_resolve(&def.typespace, &apples_def.columns[3].ty), sum_type);\n\n        assert_eq!(apples_def.primary_key, None);\n\n        assert_eq!(apples_def.constraints.len(), 2);\n        let apples_unique_constraint = \"Apples_type_key\";\n        assert_eq!(\n            apples_def.constraints[apples_unique_constraint].data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(3).into()\n            })\n        );\n        assert_eq!(\n            &apples_def.constraints[apples_unique_constraint].name[..],\n            apples_unique_constraint\n        );\n\n        assert_eq!(apples_def.indexes.len(), 3);\n        assert_eq!(\n            apples_def\n                .indexes\n                .values()\n                .sorted_by_key(|id| &id.name)\n                .collect::<Vec<_>>(),\n            [\n                &IndexDef {\n                    name: \"Apples_count_idx_direct\".into(),\n                    accessor_name: Some(expect_identifier(\"Apples_count_direct\")),\n                    algorithm: DirectAlgorithm { column: 2.into() }.into(),\n                    source_name: \"Apples_count_idx_direct\".into(),\n                },\n                &IndexDef {\n                    name: \"Apples_name_count_idx_btree\".into(),\n                    accessor_name: Some(expect_identifier(\"apples_id\")),\n                    algorithm: BTreeAlgorithm { columns: [1, 2].into() }.into(),\n                    source_name: \"Apples_name_count_idx_btree\".into(),\n                },\n                &IndexDef {\n                    name: \"Apples_type_idx_btree\".into(),\n                    accessor_name: Some(expect_identifier(\"Apples_type_btree\")),\n                    algorithm: BTreeAlgorithm { columns: 3.into() }.into(),\n                    source_name: \"Apples_type_idx_btree\".into(),\n                }\n            ]\n        );\n\n        let bananas_def = &def.tables[&bananas];\n\n        assert_eq!(bananas_def.name, bananas);\n        assert_eq!(bananas_def.table_access, TableAccess::Private);\n        assert_eq!(bananas_def.table_type, TableType::User);\n        assert_eq!(bananas_def.columns.len(), 4);\n        assert_eq!(bananas_def.columns[0].name, expect_identifier(\"count\"));\n        assert_eq!(bananas_def.columns[0].ty, AlgebraicType::U16);\n        assert_eq!(bananas_def.columns[1].name, expect_identifier(\"id\"));\n        assert_eq!(bananas_def.columns[1].ty, AlgebraicType::U64);\n        assert_eq!(bananas_def.columns[2].name, expect_identifier(\"name\"));\n        assert_eq!(bananas_def.columns[2].ty, AlgebraicType::String);\n        assert_eq!(\n            bananas_def.columns[3].name,\n            expect_identifier(\"optional_product_column\")\n        );\n        assert_eq!(\n            bananas_def.columns[3].ty,\n            AlgebraicType::option(product_type_ref.into())\n        );\n        assert_eq!(bananas_def.primary_key, Some(0.into()));\n        assert_eq!(bananas_def.indexes.len(), 2);\n        assert_eq!(bananas_def.constraints.len(), 1);\n        let (bananas_constraint_name, bananas_constraint) = bananas_def.constraints.iter().next().unwrap();\n        assert_eq!(bananas_constraint_name, &bananas_constraint.name);\n        assert_eq!(\n            bananas_constraint.data,\n            ConstraintData::Unique(UniqueConstraintData {\n                columns: ColId(0).into()\n            })\n        );\n\n        let delivery_def = &def.tables[&deliveries];\n        assert_eq!(delivery_def.name, deliveries);\n        assert_eq!(delivery_def.table_access, TableAccess::Public);\n        assert_eq!(delivery_def.table_type, TableType::System);\n        assert_eq!(delivery_def.columns.len(), 3);\n        assert_eq!(delivery_def.columns[0].name, expect_identifier(\"id\"));\n        assert_eq!(delivery_def.columns[0].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.columns[1].name, expect_identifier(\"scheduled_at\"));\n        assert_eq!(delivery_def.columns[1].ty, schedule_at_type);\n        assert_eq!(delivery_def.columns[2].name, expect_identifier(\"scheduled_id\"));\n        assert_eq!(delivery_def.columns[2].ty, AlgebraicType::U64);\n        assert_eq!(delivery_def.schedule.as_ref().unwrap().at_column, 1.into());\n        assert_eq!(\n            &delivery_def.schedule.as_ref().unwrap().function_name[..],\n            \"check_deliveries\"\n        );\n        assert_eq!(\n            delivery_def.schedule.as_ref().unwrap().function_kind,\n            FunctionKind::Reducer\n        );\n        assert_eq!(delivery_def.primary_key, Some(ColId(2)));\n\n        assert_eq!(def.typespace.get(product_type_ref), Some(&product_type));\n        assert_eq!(def.typespace.get(sum_type_ref), Some(&sum_type));\n\n        check_product_type(&def, apples_def);\n        check_product_type(&def, bananas_def);\n        check_product_type(&def, delivery_def);\n\n        let product_type_name = expect_type_name(\"scope1::scope2::ReferencedProduct\");\n        let sum_type_name = expect_type_name(\"ReferencedSum\");\n        let apples_type_name = expect_type_name(\"Apples\");\n        let bananas_type_name = expect_type_name(\"Bananas\");\n        let deliveries_type_name = expect_type_name(\"Deliveries\");\n\n        assert_eq!(def.types[&product_type_name].ty, product_type_ref);\n        assert_eq!(def.types[&sum_type_name].ty, sum_type_ref);\n        assert_eq!(def.types[&apples_type_name].ty, apples_def.product_type_ref);\n        assert_eq!(def.types[&bananas_type_name].ty, bananas_def.product_type_ref);\n        assert_eq!(def.types[&deliveries_type_name].ty, delivery_def.product_type_ref);\n\n        let init_name = expect_identifier(\"init\");\n        assert_eq!(&*def.reducers[&init_name].name, &*init_name);\n        assert_eq!(def.reducers[&init_name].lifecycle, Some(Lifecycle::Init));\n\n        let on_connect_name = expect_identifier(\"on_connect\");\n        assert_eq!(&*def.reducers[&on_connect_name].name, &*on_connect_name);\n        assert_eq!(def.reducers[&on_connect_name].lifecycle, Some(Lifecycle::OnConnect));\n\n        let on_disconnect_name = expect_identifier(\"on_disconnect\");\n        assert_eq!(&*def.reducers[&on_disconnect_name].name, &*on_disconnect_name);\n        assert_eq!(\n            def.reducers[&on_disconnect_name].lifecycle,\n            Some(Lifecycle::OnDisconnect)\n        );\n\n        let extra_reducer_name = expect_identifier(\"extra_reducer\");\n        assert_eq!(&*def.reducers[&extra_reducer_name].name, &*extra_reducer_name);\n        assert_eq!(def.reducers[&extra_reducer_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&extra_reducer_name].params,\n            ProductType::from([(\"a\", AlgebraicType::U64)])\n        );\n\n        let check_deliveries_name = expect_identifier(\"check_deliveries\");\n        assert_eq!(&*def.reducers[&check_deliveries_name].name, &*check_deliveries_name);\n        assert_eq!(def.reducers[&check_deliveries_name].lifecycle, None);\n        assert_eq!(\n            def.reducers[&check_deliveries_name].params,\n            ProductType::from([(\"a\", deliveries_product_type.into())])\n        );\n    }\n\n    #[test]\n    fn invalid_product_type_ref() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        // `build_table` does NOT initialize table.product_type_ref, which should result in an error.\n        builder.build_table(\"Bananas\", AlgebraicTypeRef(1337)).finish();\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidProductTypeRef { table, ref_ } => {\n            &table[..] == \"Bananas\" && ref_ == &AlgebraicTypeRef(1337)\n        });\n    }\n\n    #[test]\n    fn not_canonically_ordered_columns() {\n        let mut builder = RawModuleDefV9Builder::new();\n        let product_type = ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]);\n        builder\n            .build_table_with_new_type(\"Bananas\", product_type.clone(), false)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::TypeHasIncorrectOrdering { type_name, ref_, bad_type } => {\n            type_name == &expect_raw_type_name(\"Bananas\") &&\n            ref_ == &AlgebraicTypeRef(0) &&\n            bad_type == &product_type.clone().into()\n        });\n    }\n\n    #[test]\n    fn invalid_table_name() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_column_name() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IdentifierError { error } => {\n            error == &IdentifierError::Empty {}\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_ref() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_index(btree([0, 55]), \"bananas_a_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"Bananas_b_col_55_idx_btree\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_ref() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_unique_constraint(ColId(55))\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"Bananas_col_55_key\" &&\n            column == &55.into()\n        });\n    }\n\n    #[test]\n    fn invalid_sequence_column_ref() {\n        // invalid column id\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_column_sequence(55)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"Bananas_col_55_seq\" &&\n            column == &55.into()\n        });\n\n        // incorrect column type\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::String)]),\n                false,\n            )\n            .with_column_sequence(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::InvalidSequenceColumnType { sequence, column, column_type } => {\n            &sequence[..] == \"Bananas_a_seq\" &&\n            column == &RawColumnName::new(\"Bananas\", \"a\") &&\n            column_type.0 == AlgebraicType::String\n        });\n    }\n\n    #[test]\n    fn invalid_index_column_duplicates() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_index(btree([0, 0]), \"bananas_b_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ def, columns } => {\n            &def[..] == \"Bananas_b_b_idx_btree\" && columns == &ColList::from_iter([0, 0])\n        });\n    }\n\n    #[test]\n    fn invalid_unique_constraint_column_duplicates() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_unique_constraint(ColList::from_iter([1, 1]))\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateColumns{ def, columns } => {\n            &def[..] == \"Bananas_a_a_key\" && columns == &ColList::from_iter([1, 1])\n        });\n    }\n\n    #[test]\n    fn recursive_ref() {\n        let recursive_type = AlgebraicType::product([(\"a\", AlgebraicTypeRef(0).into())]);\n\n        let mut builder = RawModuleDefV9Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Recursive\", recursive_type.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]), None);\n        let result: ModuleDef = builder.finish().try_into().unwrap();\n\n        assert!(result.typespace_for_generate[ref_].is_recursive());\n    }\n\n    #[test]\n    fn out_of_bounds_ref() {\n        let invalid_type_1 = AlgebraicType::product([(\"a\", AlgebraicTypeRef(31).into())]);\n        let mut builder = RawModuleDefV9Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Invalid\", invalid_type_1.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]), None);\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ClientCodegenError { location, error: ClientCodegenError::TypeRefError(_)  } => {\n            location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) }\n        });\n    }\n\n    #[test]\n    fn not_valid_for_client_code_generation() {\n        let inner_type_invalid_for_use = AlgebraicType::product([(\"b\", AlgebraicType::U32)]);\n        let invalid_type = AlgebraicType::product([(\"a\", inner_type_invalid_for_use.clone())]);\n        let mut builder = RawModuleDefV9Builder::new();\n        let ref_ = builder.add_algebraic_type([], \"Invalid\", invalid_type.clone(), false);\n        builder.add_reducer(\"silly\", ProductType::from([(\"a\", ref_.into())]), None);\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(\n            result,\n            ValidationError::ClientCodegenError {\n                location,\n                error: ClientCodegenError::NonSpecialTypeNotAUse { ty }\n            } => {\n                location == &TypeLocation::InTypespace { ref_: AlgebraicTypeRef(0) } &&\n                ty.0 == inner_type_invalid_for_use\n            }\n        );\n    }\n\n    #[test]\n    fn hash_index_supported() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                true,\n            )\n            .with_index(hash(0), \"bananas_b\")\n            .finish();\n        let def: ModuleDef = builder.finish().try_into().unwrap();\n        let indexes = def.indexes().collect::<Vec<_>>();\n        assert_eq!(indexes.len(), 1);\n        assert_eq!(indexes[0].algorithm, IndexAlgorithm::Hash(0.into()));\n    }\n\n    #[test]\n    fn unique_constrain_without_index() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_unique_constraint(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(\n            result,\n            ValidationError::UniqueConstraintWithoutIndex { constraint, columns } => {\n                &**constraint == \"Bananas_a_key\" && *columns == ColSet::from(1)\n            }\n        );\n    }\n\n    #[test]\n    fn direct_index_only_u8_to_u64() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::I32), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_index(direct(0), \"bananas_b\")\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DirectIndexOnBadType { index, .. } => {\n            &index[..] == \"Bananas_b_idx_direct\"\n        });\n    }\n\n    #[test]\n    fn one_auto_inc() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_column_sequence(1)\n            .with_column_sequence(1)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::OneAutoInc { column } => {\n            column == &RawColumnName::new(\"Bananas\", \"a\")\n        });\n    }\n\n    #[test]\n    fn invalid_primary_key() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_primary_key(44)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::ColumnNotFound { table, def, column } => {\n            &table[..] == \"Bananas\" &&\n            &def[..] == \"Bananas\" &&\n            column == &44.into()\n        });\n    }\n\n    #[test]\n    fn missing_primary_key_unique_constraint() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                \"Bananas\",\n                ProductType::from([(\"b\", AlgebraicType::U16), (\"a\", AlgebraicType::U64)]),\n                false,\n            )\n            .with_primary_key(0)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::MissingPrimaryKeyUniqueConstraint { column } => {\n            column == &RawColumnName::new(\"Bananas\", \"b\")\n        });\n    }\n\n    #[test]\n    fn duplicate_type_name() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder.add_algebraic_type(\n            [\"scope1\".into(), \"scope2\".into()],\n            \"Duplicate\",\n            AlgebraicType::U64,\n            false,\n        );\n        builder.add_algebraic_type(\n            [\"scope1\".into(), \"scope2\".into()],\n            \"Duplicate\",\n            AlgebraicType::U32,\n            false,\n        );\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateTypeName { name } => {\n            name == &expect_type_name(\"scope1::scope2::Duplicate\")\n        });\n    }\n\n    #[test]\n    fn duplicate_lifecycle() {\n        let mut builder = RawModuleDefV9Builder::new();\n        builder.add_reducer(\"init1\", ProductType::unit(), Some(Lifecycle::Init));\n        builder.add_reducer(\"init1\", ProductType::unit(), Some(Lifecycle::Init));\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateLifecycle { lifecycle } => {\n            lifecycle == &Lifecycle::Init\n        });\n    }\n\n    #[test]\n    fn missing_scheduled_reducer() {\n        let mut builder = RawModuleDefV9Builder::new();\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n        builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index(btree(2), \"scheduled_id_index\")\n            .with_schedule(\"check_deliveries\", 1)\n            .with_type(TableType::System)\n            .finish();\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::MissingScheduledFunction { schedule, function } => {\n            &schedule[..] == \"Deliveries_sched\" &&\n                function == &expect_identifier(\"check_deliveries\")\n        });\n    }\n\n    #[test]\n    fn incorrect_scheduled_reducer_args() {\n        let mut builder = RawModuleDefV9Builder::new();\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n        let deliveries_product_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index(direct(2), \"scheduled_id_idx\")\n            .with_schedule(\"check_deliveries\", 1)\n            .with_type(TableType::System)\n            .finish();\n        builder.add_reducer(\"check_deliveries\", ProductType::from([(\"a\", AlgebraicType::U64)]), None);\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::IncorrectScheduledFunctionParams { function_name, function_kind, expected, actual } => {\n            &function_name[..] == \"check_deliveries\" &&\n                *function_kind == FunctionKind::Reducer &&\n                expected.0 == AlgebraicType::product([AlgebraicType::Ref(deliveries_product_type)]) &&\n                actual.0 == ProductType::from([(\"a\", AlgebraicType::U64)]).into()\n        });\n    }\n\n    #[test]\n    fn wacky_names() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        let schedule_at_type = builder.add_type::<ScheduleAt>();\n\n        let deliveries_product_type = builder\n            .build_table_with_new_type(\n                \"Deliveries\",\n                ProductType::from([\n                    (\"id\", AlgebraicType::U64),\n                    (\"scheduled_at\", schedule_at_type.clone()),\n                    (\"scheduled_id\", AlgebraicType::U64),\n                ]),\n                true,\n            )\n            .with_auto_inc_primary_key(2)\n            .with_index(direct(2), \"scheduled_id_index\")\n            .with_index(btree([0, 2]), \"nice_index_name\")\n            .with_schedule(\"check_deliveries\", 1)\n            .with_type(TableType::System)\n            .finish();\n\n        builder.add_reducer(\n            \"check_deliveries\",\n            ProductType::from([(\"a\", deliveries_product_type.into())]),\n            None,\n        );\n\n        // Our builder methods ignore the possibility of setting names at the moment.\n        // But, it could be done in the future for some reason.\n        // Check if it works.\n        let mut raw_def = builder.finish();\n        raw_def.tables[0].constraints[0].name = Some(\"wacky.constraint()\".into());\n        raw_def.tables[0].indexes[0].name = Some(\"wacky.index()\".into());\n        raw_def.tables[0].sequences[0].name = Some(\"wacky.sequence()\".into());\n\n        let def: ModuleDef = raw_def.try_into().unwrap();\n        assert!(def.lookup::<ConstraintDef>(&\"wacky.constraint()\".into()).is_some());\n        assert!(def.lookup::<IndexDef>(&\"wacky.index()\".into()).is_some());\n        assert!(def.lookup::<SequenceDef>(&\"wacky.sequence()\".into()).is_some());\n    }\n\n    #[test]\n    fn duplicate_reducer_names() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        builder.add_reducer(\"foo\", [(\"i\", AlgebraicType::I32)].into(), None);\n        builder.add_reducer(\"foo\", [(\"name\", AlgebraicType::String)].into(), None);\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n\n    #[test]\n    fn duplicate_procedure_names() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        builder.add_procedure(\"foo\", [(\"i\", AlgebraicType::I32)].into(), AlgebraicType::unit());\n        builder.add_procedure(\"foo\", [(\"name\", AlgebraicType::String)].into(), AlgebraicType::unit());\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n\n    #[test]\n    fn duplicate_procedure_and_reducer_name() {\n        let mut builder = RawModuleDefV9Builder::new();\n\n        builder.add_reducer(\"foo\", [(\"i\", AlgebraicType::I32)].into(), None);\n        builder.add_procedure(\"foo\", [(\"i\", AlgebraicType::I32)].into(), AlgebraicType::unit());\n\n        let result: Result<ModuleDef> = builder.finish().try_into();\n\n        expect_error_matching!(result, ValidationError::DuplicateFunctionName { name } => {\n            &name[..] == \"foo\"\n        });\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def/validate.rs",
    "content": "//! Validation code for various versions of ModuleDef.\n\nuse crate::error::ValidationErrors;\n\npub mod v10;\npub mod v8;\npub mod v9;\n\npub type Result<T> = std::result::Result<T, ValidationErrors>;\n\n/// Helpers used in tests for validation modules.\n#[cfg(test)]\npub mod tests {\n    use spacetimedb_lib::{db::raw_def::v9::RawScopedTypeNameV9, AlgebraicType};\n    use spacetimedb_sats::{raw_identifier::RawIdentifier, Typespace, WithTypespace};\n\n    use crate::{\n        def::{ModuleDef, ScopedTypeName, TableDef},\n        identifier::Identifier,\n    };\n\n    /// Create an identifier, panicking if invalid.\n    pub fn expect_identifier(data: impl AsRef<str>) -> Identifier {\n        let raw = RawIdentifier::new(data.as_ref());\n        Identifier::new(raw).expect(\"invalid identifier\")\n    }\n\n    /// Expect a name in the form \"(scope::)*name\".\n    /// Panics if the input is invalid.\n    pub fn expect_type_name(scoped_name: &str) -> ScopedTypeName {\n        let mut scope = scoped_name\n            .split(\"::\")\n            .map(|module| {\n                let raw = RawIdentifier::new(module);\n                Identifier::new(raw).expect(\"all components of a scoped name must be valid identifiers.\")\n            })\n            .collect::<Vec<_>>();\n        let name = scope.pop().expect(\"scoped names must contain at least one identifier\");\n        let scope = scope.into();\n\n        ScopedTypeName { name, scope }\n    }\n\n    /// Expect a name in the form \"(scope::)*name\".\n    /// Panics if the input is invalid.\n    pub fn expect_raw_type_name(scoped_name: &str) -> RawScopedTypeNameV9 {\n        let mut scope = scoped_name.split(\"::\").map(RawIdentifier::new).collect::<Vec<_>>();\n        let name = scope.pop().expect(\"scoped names must contain at least one identifier\");\n        let scope = scope.into();\n\n        RawScopedTypeNameV9 { name, scope }\n    }\n\n    /// Resolve a type in a typespace, expecting success.\n    pub fn expect_resolve(typespace: &Typespace, ty: &AlgebraicType) -> AlgebraicType {\n        WithTypespace::new(typespace, ty)\n            .resolve_refs()\n            .expect(\"failed to resolve type\")\n    }\n\n    /// Check that the columns of a `TableDef` correctly correspond the the `TableDef`'s\n    /// `product_type_ref`.\n    pub fn check_product_type(module_def: &ModuleDef, table_def: &TableDef) {\n        let product_type = module_def\n            .typespace\n            .get(table_def.product_type_ref)\n            .unwrap()\n            .as_product()\n            .unwrap();\n\n        for (element, column) in product_type.elements.iter().zip(table_def.columns.iter()) {\n            assert_eq!(Some(&column.name.clone().into()), element.name());\n            assert_eq!(column.ty, element.algebraic_type);\n        }\n    }\n\n    #[test]\n    fn test_expect_type_name() {\n        assert_eq!(\n            expect_raw_type_name(\"foo::bar::baz\"),\n            RawScopedTypeNameV9 {\n                scope: Box::new([\"foo\", \"bar\"].map(Into::into)),\n                name: \"baz\".into(),\n            }\n        );\n        assert_eq!(\n            expect_raw_type_name(\"foo\"),\n            RawScopedTypeNameV9 {\n                scope: Default::default(),\n                name: \"foo\".into(),\n            }\n        );\n        assert_eq!(\n            expect_type_name(\"foo::bar::baz\"),\n            ScopedTypeName {\n                scope: Box::new([expect_identifier(\"foo\"), expect_identifier(\"bar\")]),\n                name: expect_identifier(\"baz\"),\n            }\n        );\n        assert_eq!(\n            expect_type_name(\"foo\"),\n            ScopedTypeName {\n                name: expect_identifier(\"foo\"),\n                scope: Default::default()\n            }\n        );\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/def.rs",
    "content": "//! Canonicalized module definitions.\n//!\n//! This module contains a set of types that represent the canonical form of SpacetimeDB module definitions.\n//! These types are immutable to prevent accidental introduction of errors.\n//! The internal data structures of this module are not considered public API or ABI and may change\n//! at any time.\n//!\n//! Different module ABI versions correspond to different submodules of `spacetimedb_lib::db::raw_def`.\n//! All of these ABI versions can be converted to the standard form in this module via `TryFrom`.\n//! We provide streams of errors in case the conversion fails, to provide as much information\n//! to the user as possible about why their module is invalid.\n//!\n//! The `ModuleDef` type is the main type in this module. It contains all the information about a module, including its tables, reducers, typespace, and type metadata.\n//!\n//! After validation, a `ModuleDef` can be converted to the `*Schema` types in `crate::schema` for use in the database.\n//! (Eventually, we may unify these types...)\n\nuse std::collections::BTreeMap;\nuse std::fmt::{self, Debug, Write};\nuse std::hash::Hash;\n\nuse crate::error::{IdentifierError, ValidationErrors};\nuse crate::identifier::Identifier;\nuse crate::reducer_name::ReducerName;\nuse crate::schema::{Schema, TableSchema};\nuse crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate};\nuse deserialize::ArgsSeed;\nuse enum_map::EnumMap;\nuse indexmap::IndexMap;\nuse itertools::Itertools;\nuse spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors, ErrorStream};\nuse spacetimedb_data_structures::map::{Equivalent, HashMap};\nuse spacetimedb_lib::db::raw_def;\nuse spacetimedb_lib::db::raw_def::v10::{\n    ExplicitNames, RawConstraintDefV10, RawIndexDefV10, RawLifeCycleReducerDefV10, RawModuleDefV10,\n    RawModuleDefV10Section, RawProcedureDefV10, RawReducerDefV10, RawRowLevelSecurityDefV10, RawScheduleDefV10,\n    RawScopedTypeNameV10, RawSequenceDefV10, RawTableDefV10, RawTypeDefV10, RawViewDefV10,\n};\nuse spacetimedb_lib::db::raw_def::v9::{\n    Lifecycle, RawColumnDefaultValueV9, RawConstraintDataV9, RawConstraintDefV9, RawIndexAlgorithm, RawIndexDefV9,\n    RawMiscModuleExportV9, RawModuleDefV9, RawProcedureDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9,\n    RawScheduleDefV9, RawScopedTypeNameV9, RawSequenceDefV9, RawSql, RawTableDefV9, RawTypeDefV9,\n    RawUniqueConstraintDataV9, RawViewDefV9, TableAccess, TableType,\n};\nuse spacetimedb_lib::{ProductType, RawModuleDef};\nuse spacetimedb_primitives::{ColId, ColList, ColOrCols, ColSet, ProcedureId, ReducerId, TableId, ViewFnPtr};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, AlgebraicValue, Typespace};\n\npub mod deserialize;\npub mod error;\npub mod validate;\n\n/// A map from `Identifier`s to values of type `T`.\npub type IdentifierMap<T> = HashMap<Identifier, T>;\n\n/// A map from `RawIdentifier`s to values of type `T`.\npub type StrMap<T> = HashMap<RawIdentifier, T>;\n\n// We may eventually want to reorganize this module to look more\n// like the system tables, with numeric IDs used for lookups\n// in addition to `Identifier`s.\n//\n// If that path is taken, it might be possible to have this type\n// entirely subsume the various `Schema` types, which would be cool.\n\n/// A validated, canonicalized, immutable module definition.\n///\n/// Cannot be created directly. Instead, create/deserialize a [spacetimedb_lib::RawModuleDef] and call [ModuleDef::try_from].\n///\n/// ```rust\n/// use spacetimedb_lib::RawModuleDef;\n/// use spacetimedb_sats::raw_identifier::RawIdentifier;\n/// use spacetimedb_schema::def::{ModuleDef, TableDef, IndexDef, TypeDef, ModuleDefLookup, ScopedTypeName};\n/// use spacetimedb_schema::identifier::Identifier;\n///\n/// fn read_raw_module_def_from_file() -> RawModuleDef {\n///     // ...\n/// #   RawModuleDef::V9(Default::default())\n/// }\n///\n/// let raw_module_def = read_raw_module_def_from_file();\n/// let module_def = ModuleDef::try_from(raw_module_def).expect(\"valid module def\");\n///\n/// let table_name = Identifier::new(\"my_table\".into()).expect(\"valid table name\");\n/// let index_name: RawIdentifier = \"my_table_my_column_idx_btree\".into();\n/// let scoped_type_name = ScopedTypeName::try_new([], \"MyType\").expect(\"valid scoped type name\");\n///\n/// let table: Option<&TableDef> = module_def.lookup(&table_name);\n/// let index: Option<&IndexDef> = module_def.lookup(&index_name);\n/// let type_def: Option<&TypeDef> = module_def.lookup(&scoped_type_name);\n/// // etc.\n/// ```\n///\n/// Author's apology:\n/// If you find yourself asking:\n/// \"Why are we using strings to look up everything here, rather than integer indexes?\"\n/// The answer is \"I tried to get rid of the strings, but people thought it would be too confusing to have multiple\n/// kinds of integer index.\" Because the system tables and stuff would be using a different sort of integer index.\n/// shrug emoji.\n#[derive(Debug, Clone)]\n#[non_exhaustive]\npub struct ModuleDef {\n    /// The tables of the module definition.\n    tables: IdentifierMap<TableDef>,\n\n    /// The reducers of the module definition.\n    /// Note: this is using IndexMap because reducer order is important\n    /// and must be preserved for future calls to `__call_reducer__`.\n    reducers: IndexMap<Identifier, ReducerDef>,\n\n    /// The procedures of the module definition.\n    ///\n    /// Like `reducers`, this uses [`IndexMap`] to preserve order\n    /// so that `__call_procedure__` receives stable integer IDs.\n    procedures: IndexMap<Identifier, ProcedureDef>,\n\n    /// The views of the module definition.\n    ///\n    /// Like `reducers`, this uses [`IndexMap`] to preserve order\n    /// so that `__call_view__` receives stable integer IDs.\n    views: IndexMap<Identifier, ViewDef>,\n\n    /// A map from lifecycle reducer kind to reducer id.\n    lifecycle_reducers: EnumMap<Lifecycle, Option<ReducerId>>,\n\n    /// The type definitions of the module definition.\n    types: HashMap<ScopedTypeName, TypeDef>,\n\n    /// The typespace of the module definition.\n    typespace: Typespace,\n\n    /// The typespace, restructured to be useful for client codegen.\n    typespace_for_generate: TypespaceForGenerate,\n\n    /// The global namespace of the module:\n    /// tables, indexes, constraints, schedules, and sequences live in the global namespace.\n    /// Concretely, though, they're stored in the `TableDef` data structures.\n    /// This map allows looking up which `TableDef` stores the `Def` you're looking for.\n    stored_in_table_def: StrMap<Identifier>,\n\n    /// A map from type defs to their names.\n    refmap: HashMap<AlgebraicTypeRef, ScopedTypeName>,\n\n    /// The row-level security policies.\n    ///\n    /// **Note**: Are only validated syntax-wise.\n    row_level_security_raw: HashMap<RawSql, RawRowLevelSecurityDefV9>,\n\n    /// Indicates which raw module definition semantics this module\n    /// was authored under.\n    #[allow(unused)]\n    raw_module_def_version: RawModuleDefVersion,\n}\n\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum RawModuleDefVersion {\n    /// Represents [`RawModuleDefV9`] and earlier.\n    V9OrEarlier,\n    /// Represents [`RawModuleDefV10`].\n    V10,\n}\n\nimpl ModuleDef {\n    /// The raw module definition version this module was authored under.\n    pub fn raw_module_def_version(&self) -> RawModuleDefVersion {\n        self.raw_module_def_version\n    }\n\n    /// The tables of the module definition.\n    pub fn tables(&self) -> impl Iterator<Item = &TableDef> {\n        self.tables.values()\n    }\n\n    /// The indexes of the module definition.\n    pub fn indexes(&self) -> impl Iterator<Item = &IndexDef> {\n        self.tables().flat_map(|table| table.indexes.values())\n    }\n\n    /// The constraints of the module definition.\n    pub fn constraints(&self) -> impl Iterator<Item = &ConstraintDef> {\n        self.tables().flat_map(|table| table.constraints.values())\n    }\n\n    /// The sequences of the module definition.\n    pub fn sequences(&self) -> impl Iterator<Item = &SequenceDef> {\n        self.tables().flat_map(|table| table.sequences.values())\n    }\n\n    /// The schedules of the module definition.\n    pub fn schedules(&self) -> impl Iterator<Item = &ScheduleDef> {\n        self.tables().filter_map(|table| table.schedule.as_ref())\n    }\n\n    /// The reducers of the module definition.\n    pub fn reducers(&self) -> impl Iterator<Item = &ReducerDef> {\n        self.reducers.values()\n    }\n\n    /// Returns an iterator over all reducer ids and definitions.\n    pub fn reducer_ids_and_defs(&self) -> impl ExactSizeIterator<Item = (ReducerId, &ReducerDef)> {\n        self.reducers.values().enumerate().map(|(idx, def)| (idx.into(), def))\n    }\n\n    /// The procedures of the module definition.\n    pub fn procedures(&self) -> impl Iterator<Item = &ProcedureDef> {\n        self.procedures.values()\n    }\n\n    /// The views of the module definition.\n    pub fn views(&self) -> impl Iterator<Item = &ViewDef> {\n        self.views.values()\n    }\n\n    /// The type definitions of the module definition.\n    pub fn types(&self) -> impl Iterator<Item = &TypeDef> {\n        self.types.values()\n    }\n\n    /// The row-level security policies of the module definition.\n    pub fn row_level_security(&self) -> impl Iterator<Item = &RawRowLevelSecurityDefV9> {\n        self.row_level_security_raw.values()\n    }\n\n    /// The `Typespace` used by the module.\n    ///\n    /// `AlgebraicTypeRef`s in the table, reducer, and type alias declarations refer to this typespace.\n    ///\n    /// The typespace must satisfy `Typespace::is_valid_for_client_code_generation`. That is, all types stored in the typespace must either:\n    /// 1. satisfy `AlgebraicType::is_valid_for_client_type_definition`\n    /// 2. and/or `AlgebraicType::is_valid_for_client_type_use`.\n    ///\n    /// Types satisfying condition 1 correspond to generated classes in client code.\n    /// (Types satisfying condition 2 are an artifact of the module bindings, and do not affect the semantics of the module definition.)\n    ///\n    /// Types satisfying condition 1 are required to have corresponding `RawTypeDefV9` declarations in the module.\n    pub fn typespace(&self) -> &Typespace {\n        &self.typespace\n    }\n\n    /// The typespace of the module from a different perspective, one useful for client code generation.\n    pub fn typespace_for_generate(&self) -> &TypespaceForGenerate {\n        &self.typespace_for_generate\n    }\n\n    /// The `TableDef` an entity in the global namespace is stored in, if any.\n    ///\n    /// Generally, you will want to use the `lookup` method on the entity type instead.\n    pub fn stored_in_table_def(&self, name: &RawIdentifier) -> Option<&TableDef> {\n        self.stored_in_table_def\n            .get(name)\n            .and_then(|table_name| self.tables.get(table_name))\n    }\n\n    /// Lookup a definition by its key in `self`.\n    pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {\n        T::lookup(self, key)\n    }\n\n    /// Lookup a definition by its key in `self`, panicking if not found.\n    /// Only use this method if you are sure the key exists in the module definition.\n    pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {\n        T::lookup(self, key).expect(\"expected ModuleDef to contain key, but it does not\")\n    }\n\n    /// Convenience method to look up a table, possibly by a string.\n    pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.tables.get(name)\n    }\n\n    /// Convenience method to look up a view, possibly by a string.\n    pub fn view<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ViewDef> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.views.get(name)\n    }\n\n    /// Convenience method to look up a view, possibly by a string, returning its id as well.\n    pub fn view_full<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<(ViewFnPtr, &ViewDef)> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.views.get(name).map(|def| (def.fn_ptr, def))\n    }\n\n    /// Convenience method to look up a reducer, possibly by a string.\n    pub fn reducer<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ReducerDef> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.reducers.get(name)\n    }\n\n    /// Convenience method to look up a reducer, possibly by a string, returning its id as well.\n    pub fn reducer_full<K: ?Sized + Hash + Equivalent<Identifier>>(\n        &self,\n        name: &K,\n    ) -> Option<(ReducerId, &ReducerDef)> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.reducers.get_full(name).map(|(idx, _, def)| (idx.into(), def))\n    }\n\n    /// Look up a reducer by its id.\n    pub fn reducer_by_id(&self, id: ReducerId) -> &ReducerDef {\n        &self.reducers[id.idx()]\n    }\n\n    /// Look up a reducer by its id.\n    pub fn get_reducer_by_id(&self, id: ReducerId) -> Option<&ReducerDef> {\n        self.reducers.get_index(id.idx()).map(|(_, def)| def)\n    }\n\n    /// Look up a view by its id, and whether it is anonymous.\n    pub fn get_view_by_id(&self, id: ViewFnPtr, is_anonymous: bool) -> Option<&ViewDef> {\n        self.views\n            .iter()\n            .find(|(_, def)| def.fn_ptr == id && def.is_anonymous == is_anonymous)\n            .map(|(_, def)| def)\n    }\n\n    /// Convenience method to look up a procedure, possibly by a string.\n    pub fn procedure<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ProcedureDef> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.procedures.get(name)\n    }\n\n    /// Convenience method to look up a procedure, possibly by a string, returning its id as well.\n    pub fn procedure_full<K: ?Sized + Hash + Equivalent<Identifier>>(\n        &self,\n        name: &K,\n    ) -> Option<(ProcedureId, &ProcedureDef)> {\n        // If the string IS a valid identifier, we can just look it up.\n        self.procedures.get_full(name).map(|(idx, _, def)| (idx.into(), def))\n    }\n\n    /// Look up a procuedure by its id, panicking if it doesn't exist.\n    pub fn procedure_by_id(&self, id: ProcedureId) -> &ProcedureDef {\n        &self.procedures[id.idx()]\n    }\n\n    /// Look up a procuedure by its id, returning `None` if it doesn't exist.\n    pub fn get_procedure_by_id(&self, id: ProcedureId) -> Option<&ProcedureDef> {\n        self.procedures.get_index(id.idx()).map(|(_, def)| def)\n    }\n\n    /// Looks up a lifecycle reducer defined in the module.\n    pub fn lifecycle_reducer(&self, lifecycle: Lifecycle) -> Option<(ReducerId, &ReducerDef)> {\n        self.lifecycle_reducers[lifecycle].map(|i| (i, &self.reducers[i.idx()]))\n    }\n\n    /// Returns a `DeserializeSeed` that can pull data from a `Deserializer` for `def`.\n    pub fn arg_seed_for<'a, T>(&'a self, def: &'a T) -> ArgsSeed<'a, T> {\n        ArgsSeed(self.typespace.with_type(def))\n    }\n\n    /// Look up the name corresponding to an `AlgebraicTypeRef`.\n    pub fn type_def_from_ref(&self, r: AlgebraicTypeRef) -> Option<(&ScopedTypeName, &TypeDef)> {\n        let name = self.refmap.get(&r)?;\n        let def = self\n            .types\n            .get(name)\n            .expect(\"if it was in refmap, it should be in types\");\n\n        Some((name, def))\n    }\n\n    /// Convenience method to look up a table and convert it to a `TableSchema`.\n    /// All indexes, constraints, etc inside the table will have ID 0!\n    pub fn table_schema<K: ?Sized + Hash + Equivalent<Identifier>>(\n        &self,\n        name: &K,\n        table_id: TableId,\n    ) -> Option<TableSchema> {\n        // If the string IS a valid identifier, we can just look it up.\n        let table_def = self.tables.get(name)?;\n        Some(TableSchema::from_module_def(self, table_def, (), table_id))\n    }\n\n    /// Lookup a definition by its key in `self`, panicking if it is not found.\n    pub fn expect_lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {\n        if let Some(result) = T::lookup(self, key) {\n            result\n        } else {\n            panic!(\"expected ModuleDef to contain {key:?}, but it does not\");\n        }\n    }\n\n    /// Expect that this module definition contains a definition.\n    pub fn expect_contains<Def: ModuleDefLookup>(&self, def: &Def) {\n        if let Some(my_def) = self.lookup(def.key()) {\n            assert_eq!(\n                def as *const Def, my_def as *const Def,\n                \"expected ModuleDef to contain {def:?}, but it contained {my_def:?}\"\n            );\n        } else {\n            panic!(\"expected ModuleDef to contain {:?}, but it does not\", def.key());\n        }\n    }\n}\n\nimpl TryFrom<RawModuleDef> for ModuleDef {\n    type Error = ValidationErrors;\n\n    fn try_from(raw: RawModuleDef) -> Result<Self, Self::Error> {\n        match raw {\n            RawModuleDef::V8BackCompat(v8_mod) => Self::try_from(v8_mod),\n            RawModuleDef::V9(v9_mod) => Self::try_from(v9_mod),\n            RawModuleDef::V10(v10_mod) => Self::try_from(v10_mod),\n            _ => unimplemented!(),\n        }\n    }\n}\nimpl TryFrom<raw_def::v8::RawModuleDefV8> for ModuleDef {\n    type Error = ValidationErrors;\n\n    fn try_from(v8_mod: raw_def::v8::RawModuleDefV8) -> Result<Self, Self::Error> {\n        // it is not necessary to generate indexes for a v8 mod, since the validation\n        // handles index generation.\n        validate::v8::validate(v8_mod)\n    }\n}\nimpl TryFrom<raw_def::v9::RawModuleDefV9> for ModuleDef {\n    type Error = ValidationErrors;\n\n    fn try_from(v9_mod: raw_def::v9::RawModuleDefV9) -> Result<Self, Self::Error> {\n        validate::v9::validate(v9_mod)\n    }\n}\nimpl From<ModuleDef> for RawModuleDefV9 {\n    fn from(val: ModuleDef) -> Self {\n        let ModuleDef {\n            tables,\n            views,\n            reducers,\n            lifecycle_reducers: _,\n            types,\n            typespace,\n            stored_in_table_def: _,\n            typespace_for_generate: _,\n            refmap: _,\n            row_level_security_raw,\n            procedures,\n            raw_module_def_version: _,\n        } = val;\n\n        // Extract column defaults from tables before consuming tables\n        let column_defaults: Vec<_> = tables\n            .iter()\n            .flat_map(|(table_name, table_def)| {\n                table_def.columns.iter().enumerate().filter_map(|(col_id, col)| {\n                    col.default_value.as_ref().map(|default_val| {\n                        RawMiscModuleExportV9::ColumnDefaultValue(RawColumnDefaultValueV9 {\n                            table: table_name.clone().into(),\n                            col_id: ColId(col_id as u16),\n                            value: spacetimedb_sats::bsatn::to_vec(default_val).unwrap().into(),\n                        })\n                    })\n                })\n            })\n            .collect();\n\n        RawModuleDefV9 {\n            tables: to_raw(tables),\n            reducers: reducers.into_iter().map(|(_, def)| def.into()).collect(),\n            types: to_raw(types),\n            misc_exports: column_defaults\n                .into_iter()\n                .chain(procedures.into_iter().map(|(_, def)| def.into()))\n                .chain(views.into_iter().map(|(_, def)| def.into()))\n                .collect(),\n            typespace,\n            row_level_security: row_level_security_raw.into_iter().map(|(_, def)| def).collect(),\n        }\n    }\n}\n\nimpl TryFrom<raw_def::v10::RawModuleDefV10> for ModuleDef {\n    type Error = ValidationErrors;\n\n    fn try_from(v10_mod: raw_def::v10::RawModuleDefV10) -> Result<Self, Self::Error> {\n        validate::v10::validate(v10_mod)\n    }\n}\n\nimpl From<ModuleDef> for RawModuleDefV10 {\n    fn from(val: ModuleDef) -> Self {\n        let ModuleDef {\n            tables,\n            views,\n            reducers,\n            lifecycle_reducers,\n            types,\n            typespace,\n            stored_in_table_def: _,\n            typespace_for_generate: _,\n            refmap: _,\n            row_level_security_raw,\n            procedures,\n            raw_module_def_version: _,\n        } = val;\n\n        let mut sections = Vec::new();\n        let mut explicit_names = ExplicitNames::default();\n\n        sections.push(RawModuleDefV10Section::Typespace(typespace));\n\n        // Extract lifecycle reducer names before consuming reducers.\n        let raw_lifecycle: Vec<RawLifeCycleReducerDefV10> = lifecycle_reducers\n            .into_iter()\n            .filter_map(|(lifecycle, reducer_id)| {\n                let id = reducer_id?;\n                let (name, _) = reducers.get_index(id.idx())?;\n                Some(RawLifeCycleReducerDefV10 {\n                    lifecycle_spec: lifecycle,\n                    function_name: name.clone().into(),\n                })\n            })\n            .collect();\n\n        let raw_types: Vec<RawTypeDefV10> = types.into_values().map(Into::into).collect();\n        if !raw_types.is_empty() {\n            sections.push(RawModuleDefV10Section::Types(raw_types));\n        }\n\n        // Collect schedules from tables (V10 stores them in a separate section).\n        // Also collect ExplicitNames for tables: accessor_name → source_name, name → canonical_name.\n        let mut schedules = Vec::new();\n        let raw_tables: Vec<RawTableDefV10> = tables\n            .into_values()\n            .map(|td| {\n                // Always emit name as ExplicitNames canonical_name.\n                explicit_names.insert_table(\n                    RawIdentifier::from(td.accessor_name.clone()),\n                    RawIdentifier::from(td.name.clone()),\n                );\n                if let Some(sched) = td.schedule.clone() {\n                    schedules.push(RawScheduleDefV10 {\n                        source_name: Some(sched.name.into()),\n                        table_name: td.name.clone().into(),\n                        schedule_at_col: sched.at_column,\n                        function_name: sched.function_name.into(),\n                    });\n                }\n                td.into()\n            })\n            .collect();\n        if !raw_tables.is_empty() {\n            sections.push(RawModuleDefV10Section::Tables(raw_tables));\n        }\n\n        // Collect ExplicitNames for reducers: accessor_name → source_name, name → canonical_name.\n        let raw_reducers: Vec<RawReducerDefV10> = reducers\n            .into_values()\n            .map(|rd| {\n                explicit_names.insert_function(\n                    RawIdentifier::from(rd.accessor_name.clone()),\n                    RawIdentifier::from(rd.name.clone()),\n                );\n                rd.into()\n            })\n            .collect();\n        if !raw_reducers.is_empty() {\n            sections.push(RawModuleDefV10Section::Reducers(raw_reducers));\n        }\n\n        // Collect ExplicitNames for procedures: accessor_name → source_name, name → canonical_name.\n        let raw_procedures: Vec<RawProcedureDefV10> = procedures\n            .into_values()\n            .map(|pd| {\n                explicit_names.insert_function(\n                    RawIdentifier::from(pd.accessor_name.clone()),\n                    RawIdentifier::from(pd.name.clone()),\n                );\n                pd.into()\n            })\n            .collect();\n        if !raw_procedures.is_empty() {\n            sections.push(RawModuleDefV10Section::Procedures(raw_procedures));\n        }\n\n        // Collect ExplicitNames for views: accessor_name → source_name, name → canonical_name.\n        let raw_views: Vec<RawViewDefV10> = views\n            .into_values()\n            .map(|vd| {\n                explicit_names.insert_function(\n                    RawIdentifier::from(vd.accessor_name.clone()),\n                    RawIdentifier::from(vd.name.clone()),\n                );\n                vd.into()\n            })\n            .collect();\n        if !raw_views.is_empty() {\n            sections.push(RawModuleDefV10Section::Views(raw_views));\n        }\n\n        if !schedules.is_empty() {\n            sections.push(RawModuleDefV10Section::Schedules(schedules));\n        }\n\n        if !raw_lifecycle.is_empty() {\n            sections.push(RawModuleDefV10Section::LifeCycleReducers(raw_lifecycle));\n        }\n\n        let raw_rls: Vec<RawRowLevelSecurityDefV10> = row_level_security_raw.into_values().collect();\n        if !raw_rls.is_empty() {\n            sections.push(RawModuleDefV10Section::RowLevelSecurity(raw_rls));\n        }\n\n        // Always emit ExplicitNames so canonical names survive the round-trip.\n        sections.push(RawModuleDefV10Section::ExplicitNames(explicit_names));\n\n        RawModuleDefV10 { sections }\n    }\n}\n\n/// Implemented by definitions stored in a `ModuleDef`.\n/// Allows looking definitions up in a `ModuleDef`, and across\n/// `ModuleDef`s during migrations.\npub trait ModuleDefLookup: Sized + Debug + 'static {\n    /// A reference to a definition of this type within a module def. This reference should be portable across migrations.\n    type Key<'a>: Debug + Copy;\n\n    /// Get a reference to this definition.\n    fn key(&self) -> Self::Key<'_>;\n\n    /// Look up this entity in the module def.\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self>;\n}\n/// A data structure representing the validated definition of a database table.\n///\n/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,\n/// and then access the tables from there.\n///\n/// This struct holds information about the table, including its name, columns, indexes,\n/// constraints, sequences, type, and access rights.\n///\n/// Validation rules:\n/// - The table name must be a valid identifier.\n/// - The table's columns must be sorted according to [crate::db::ordering::canonical_ordering].\n/// - The table's indexes, constraints, and sequences must be sorted by their keys.\n/// - The table's column types may refer only to types in the containing DatabaseDef's typespace.\n/// - The table's column names must be unique.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct TableDef {\n    /// The name of the table.\n    /// Unique within a module, acts as the table's identifier.\n    /// Must be a valid [crate::db::identifier::Identifier].\n    pub name: Identifier,\n    /// The table identifier as defined in the module source.\n    ///\n    /// All the codegens should use `accessor_name` to derive the table identifier for clients.\n    /// However, the `name` field should still be used when communicating with the\n    /// database over the network, since the database does not necessarily know\n    /// about accessor names.\n    pub accessor_name: Identifier,\n    /// A reference to a `ProductType` containing the columns of this table.\n    /// This is the single source of truth for the table's columns.\n    /// All elements of the `ProductType` must have names.\n    ///\n    /// Like all types in the module, this must have the [default element ordering](crate::db::default_element_ordering), UNLESS a custom ordering is declared via `ModuleDef.misc_exports` for this type.\n    pub product_type_ref: AlgebraicTypeRef,\n\n    /// The primary key of the table, if present. Must refer to a valid column.\n    ///\n    /// Currently, there must be a unique constraint and an index corresponding to the primary key.\n    /// Eventually, we may remove the requirement for an index.\n    ///\n    /// The database engine does not actually care about this, but client code generation does.\n    pub primary_key: Option<ColId>,\n\n    /// The columns of this table. This stores the information in\n    /// `product_type_ref` in a more convenient-to-access format.\n    pub columns: Vec<ColumnDef>,\n\n    /// The indices on the table, indexed by name.\n    pub indexes: StrMap<IndexDef>,\n\n    /// The unique constraints on the table, indexed by name.\n    pub constraints: StrMap<ConstraintDef>,\n\n    /// The sequences for the table, indexed by name.\n    pub sequences: StrMap<SequenceDef>,\n\n    /// The schedule for the table, if present.\n    pub schedule: Option<ScheduleDef>,\n\n    /// Whether this is a system- or user-created table.\n    pub table_type: TableType,\n\n    /// Whether this table is public or private.\n    pub table_access: TableAccess,\n\n    /// Whether this is an event table.\n    ///\n    /// Event tables persist to the commitlog but are not merged into committed state.\n    /// Their rows are only visible to V2 subscribers in the transaction that inserted them.\n    pub is_event: bool,\n}\n\nimpl TableDef {\n    /// Get a column of the `TableDef`.\n    pub fn get_column(&self, id: ColId) -> Option<&ColumnDef> {\n        self.columns.get(id.idx())\n    }\n    /// Get a column by the column's name.\n    pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ColumnDef> {\n        self.columns.iter().find(|c| &c.name == name)\n    }\n}\n\nimpl From<TableDef> for RawTableDefV9 {\n    fn from(val: TableDef) -> Self {\n        let TableDef {\n            name,\n            product_type_ref,\n            primary_key,\n            columns: _, // will be reconstructed from the product type.\n            indexes,\n            constraints,\n            sequences,\n            schedule,\n            table_type,\n            table_access,\n            is_event: _, // V9 does not support event tables; ignore when converting back\n            ..\n        } = val;\n\n        RawTableDefV9 {\n            name: name.into(),\n            product_type_ref,\n            primary_key: ColList::from_iter(primary_key),\n            indexes: to_raw(indexes),\n            constraints: to_raw(constraints),\n            sequences: to_raw(sequences),\n            schedule: schedule.map(Into::into),\n            table_type,\n            table_access,\n        }\n    }\n}\n\nimpl From<TableDef> for RawTableDefV10 {\n    fn from(val: TableDef) -> Self {\n        let TableDef {\n            name: _,\n            product_type_ref,\n            primary_key,\n            columns: _, // will be reconstructed from the product type.\n            indexes,\n            constraints,\n            sequences,\n            schedule: _, // V10 stores schedules in a separate section; handled in From<ModuleDef>.\n            table_type,\n            table_access,\n            is_event,\n            accessor_name,\n        } = val;\n\n        RawTableDefV10 {\n            source_name: accessor_name.into(),\n            product_type_ref,\n            primary_key: ColList::from_iter(primary_key),\n            indexes: indexes.into_values().map(Into::into).collect(),\n            constraints: constraints.into_values().map(Into::into).collect(),\n            sequences: sequences.into_values().map(Into::into).collect(),\n            table_type,\n            table_access,\n            default_values: Vec::new(),\n            is_event,\n        }\n    }\n}\n\nimpl From<ViewDef> for TableDef {\n    fn from(def: ViewDef) -> Self {\n        use TableAccess::*;\n        let ViewDef {\n            name,\n            is_public,\n            product_type_ref,\n            primary_key,\n            return_columns,\n            accessor_name,\n            ..\n        } = def;\n        Self {\n            name,\n            product_type_ref,\n            primary_key,\n            columns: return_columns.into_iter().map(ColumnDef::from).collect(),\n            indexes: <_>::default(),\n            constraints: <_>::default(),\n            sequences: <_>::default(),\n            schedule: None,\n            table_type: TableType::User,\n            table_access: if is_public { Public } else { Private },\n            is_event: false,\n            accessor_name,\n        }\n    }\n}\n\n/// A sequence definition for a database table column.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct SequenceDef {\n    /// The name of the sequence. Must be unique within the containing `ModuleDef`.\n    pub name: RawIdentifier,\n\n    /// The position of the column associated with this sequence.\n    /// This refers to a column in the same `RawTableDef` that contains this `RawSequenceDef`.\n    /// The column must have integral type.\n    /// This must be the unique `RawSequenceDef` for this column.\n    pub column: ColId,\n\n    /// The value to start assigning to this column.\n    /// Will be incremented by 1 for each new row.\n    /// If not present, an arbitrary start point may be selected.\n    pub start: Option<i128>,\n\n    /// The minimum allowed value in this column.\n    /// If not present, no minimum.\n    pub min_value: Option<i128>,\n\n    /// The maximum allowed value in this column.\n    /// If not present, no maximum.\n    pub max_value: Option<i128>,\n\n    /// The increment to use when updating the sequence.\n    pub increment: i128,\n}\n\nimpl From<SequenceDef> for RawSequenceDefV9 {\n    fn from(val: SequenceDef) -> Self {\n        RawSequenceDefV9 {\n            name: Some(val.name),\n            column: val.column,\n            start: val.start,\n            min_value: val.min_value,\n            max_value: val.max_value,\n            increment: val.increment,\n        }\n    }\n}\n\nimpl From<SequenceDef> for RawSequenceDefV10 {\n    fn from(val: SequenceDef) -> Self {\n        RawSequenceDefV10 {\n            source_name: Some(val.name),\n            column: val.column,\n            start: val.start,\n            min_value: val.min_value,\n            max_value: val.max_value,\n            increment: val.increment,\n        }\n    }\n}\n\n/// A struct representing the validated definition of a database index.\n///\n/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,\n/// and then access the index from there.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct IndexDef {\n    /// The name of the index. Must be unique within the containing `ModuleDef`.\n    ///\n    /// In V9 and earlier, this was system generated.\n    /// in V10, this can be optionally passed in by the user, but if not passed in, it will be\n    /// generated by the system using the same algorithm as V9 and earlier.\n    pub name: RawIdentifier,\n\n    /// This will be the same as `name` for [`RawModuleDefV9`].\n    /// For [`RawModuleDefV10`], this is an auto-generated globally unique name\n    /// derived from the module source.\n    ///\n    /// This exists as a hacky workaround to make the `index_name_from_id`\n    /// syscall work without requiring the module to know about the canonical\n    /// index name field. It serves as an alias for `name` in Database.\n    pub source_name: RawIdentifier,\n\n    /// The name of the index to use for client code generation.\n    ///\n    /// In V9 and earlier, this could be supplied by the user via the `accessor`\n    /// macro in bindings. In those versions, this may be `None` if the index\n    /// was auto-generated and no accessor name was provided by the user. In\n    /// that case, no client code will be generated for this index.\n    pub accessor_name: Option<Identifier>,\n    /// The algorithm parameters for the index.\n    pub algorithm: IndexAlgorithm,\n}\n\nimpl IndexDef {\n    /// Whether this index was generated by the system.\n    pub fn generated(&self) -> bool {\n        self.accessor_name.is_none()\n    }\n}\n\nimpl From<IndexDef> for RawIndexDefV9 {\n    fn from(val: IndexDef) -> Self {\n        RawIndexDefV9 {\n            name: Some(val.name),\n            algorithm: match val.algorithm {\n                IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },\n                IndexAlgorithm::Hash(HashAlgorithm { columns }) => RawIndexAlgorithm::Hash { columns },\n                IndexAlgorithm::Direct(DirectAlgorithm { column }) => RawIndexAlgorithm::Direct { column },\n            },\n            accessor_name: val.accessor_name.map(Into::into),\n        }\n    }\n}\n\nimpl From<IndexDef> for RawIndexDefV10 {\n    fn from(val: IndexDef) -> Self {\n        RawIndexDefV10 {\n            source_name: Some(val.source_name),\n            accessor_name: val.accessor_name.map(Into::into),\n            algorithm: val.algorithm.into(),\n        }\n    }\n}\n\n/// Data specifying a supported index algorithm.\n#[non_exhaustive]\n#[derive(Debug, Clone, Eq, PartialEq)]\npub enum IndexAlgorithm {\n    /// Implemented using a rust `std::collections::BTreeMap`.\n    BTree(BTreeAlgorithm),\n    /// Implemented using a rust `HashMap`.\n    Hash(HashAlgorithm),\n    /// Implemented using `DirectUniqueIndex`.\n    Direct(DirectAlgorithm),\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for IndexAlgorithm {\n    fn heap_usage(&self) -> usize {\n        match self {\n            Self::BTree(a) => a.heap_usage(),\n            Self::Direct(a) => a.heap_usage(),\n            Self::Hash(a) => a.heap_usage(),\n        }\n    }\n}\n\nimpl IndexAlgorithm {\n    /// Get the columns of the index.\n    pub fn columns(&self) -> ColOrCols<'_> {\n        match self {\n            Self::BTree(btree) => ColOrCols::ColList(&btree.columns),\n            Self::Hash(hash) => ColOrCols::ColList(&hash.columns),\n            Self::Direct(direct) => ColOrCols::Col(direct.column),\n        }\n    }\n    /// Find the column index for a given field.\n    ///\n    /// *NOTE*: This take in account the possibility of permutations.\n    pub fn find_col_index(&self, pos: usize) -> Option<ColId> {\n        self.columns().iter().find(|col_id| col_id.idx() == pos)\n    }\n}\n\nimpl From<IndexAlgorithm> for RawIndexAlgorithm {\n    fn from(val: IndexAlgorithm) -> Self {\n        match val {\n            IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => Self::BTree { columns },\n            IndexAlgorithm::Hash(HashAlgorithm { columns }) => Self::Hash { columns },\n            IndexAlgorithm::Direct(DirectAlgorithm { column }) => Self::Direct { column },\n        }\n    }\n}\n\n/// Data specifying a BTree index.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct BTreeAlgorithm {\n    /// The columns to index.\n    pub columns: ColList,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for BTreeAlgorithm {\n    fn heap_usage(&self) -> usize {\n        self.columns.heap_usage()\n    }\n}\n\nimpl<CL: Into<ColList>> From<CL> for BTreeAlgorithm {\n    fn from(columns: CL) -> Self {\n        let columns = columns.into();\n        Self { columns }\n    }\n}\n\nimpl From<BTreeAlgorithm> for IndexAlgorithm {\n    fn from(val: BTreeAlgorithm) -> Self {\n        IndexAlgorithm::BTree(val)\n    }\n}\n\n/// Data specifying a Hash index.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct HashAlgorithm {\n    /// The columns to index.\n    pub columns: ColList,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for HashAlgorithm {\n    fn heap_usage(&self) -> usize {\n        self.columns.heap_usage()\n    }\n}\n\nimpl<CL: Into<ColList>> From<CL> for HashAlgorithm {\n    fn from(columns: CL) -> Self {\n        let columns = columns.into();\n        Self { columns }\n    }\n}\n\nimpl From<HashAlgorithm> for IndexAlgorithm {\n    fn from(val: HashAlgorithm) -> Self {\n        IndexAlgorithm::Hash(val)\n    }\n}\n\n/// Data specifying a Direct index.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct DirectAlgorithm {\n    /// The column to index.\n    pub column: ColId,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for DirectAlgorithm {\n    fn heap_usage(&self) -> usize {\n        self.column.heap_usage()\n    }\n}\n\nimpl<C: Into<ColId>> From<C> for DirectAlgorithm {\n    fn from(column: C) -> Self {\n        let column = column.into();\n        Self { column }\n    }\n}\n\nimpl From<DirectAlgorithm> for IndexAlgorithm {\n    fn from(val: DirectAlgorithm) -> Self {\n        IndexAlgorithm::Direct(val)\n    }\n}\n\n/// A struct representing the validated definition of a database column.\n///\n/// Cannot be created directly. Construct a [`ModuleDef`] by validating a [`RawModuleDef`] instead,\n/// and then access the column from there.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ColumnDef {\n    /// The name of the column.\n    /// Unique within the containing `TableDef`, but\n    /// NOT within the containing `ModuleDef`.\n    pub name: Identifier,\n\n    /// The name of the column as define in the module source code.\n    ///\n    /// This should be used in client codegens and Not `name` field.\n    pub accessor_name: Identifier,\n\n    /// The ID of this column.\n    pub col_id: ColId,\n\n    /// The type of this column. May refer to the containing `ModuleDef`'s `Typespace`.\n    /// Must satisfy `AlgebraicType::is_valid_for_client_type_use`.\n    ///\n    /// Will always correspond to the corresponding element of this table's\n    /// `product_type_ref`, that is, the element at index `col_id.idx()`\n    /// with name `Some(name.as_str())`.\n    pub ty: AlgebraicType,\n\n    /// The type of the column, formatted for client code generation.\n    pub ty_for_generate: AlgebraicTypeUse,\n\n    /// The table this `ColumnDef` is stored in.\n    pub table_name: Identifier,\n\n    /// The default value of this column, if present.\n    pub default_value: Option<AlgebraicValue>,\n}\n\nimpl From<ViewColumnDef> for ColumnDef {\n    fn from(def: ViewColumnDef) -> Self {\n        let ViewColumnDef {\n            name,\n            col_id,\n            ty,\n            ty_for_generate,\n            view_name: table_name,\n            accessor_name,\n        } = def;\n        Self {\n            name,\n            col_id,\n            ty,\n            ty_for_generate,\n            table_name,\n            default_value: None,\n            accessor_name,\n        }\n    }\n}\n\n/// A struct representing a validated view column\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ViewColumnDef {\n    /// The name of the column.\n    pub name: Identifier,\n\n    pub accessor_name: Identifier,\n\n    /// The position of this column in the view's return type.\n    pub col_id: ColId,\n\n    /// The type of this column.\n    pub ty: AlgebraicType,\n\n    /// The type of the column, formatted for client code generation.\n    pub ty_for_generate: AlgebraicTypeUse,\n\n    /// The view this def is stored in.\n    pub view_name: Identifier,\n}\n\nimpl From<ColumnDef> for ViewColumnDef {\n    fn from(\n        ColumnDef {\n            name,\n            col_id,\n            ty,\n            ty_for_generate,\n            table_name: view_name,\n            accessor_name,\n            ..\n        }: ColumnDef,\n    ) -> Self {\n        Self {\n            name,\n            col_id,\n            ty,\n            ty_for_generate,\n            view_name,\n            accessor_name,\n        }\n    }\n}\n\n/// A struct representing a validated view parameter\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ViewParamDef {\n    /// The name of the parameter.\n    pub name: Identifier,\n\n    /// The position of this parameter in the view's parameter list.\n    pub col_id: ColId,\n\n    /// The type of this parameter.\n    pub ty: AlgebraicType,\n\n    /// The type of the parameter, formatted for client code generation.\n    pub ty_for_generate: AlgebraicTypeUse,\n\n    /// The view this def is stored in.\n    pub view_name: Identifier,\n}\n\n/// A constraint definition attached to a table.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct ConstraintDef {\n    /// The name of the constraint. Unique within the containing `ModuleDef`.\n    pub name: RawIdentifier,\n\n    /// The data for the constraint.\n    pub data: ConstraintData,\n}\n\nimpl From<ConstraintDef> for RawConstraintDefV9 {\n    fn from(val: ConstraintDef) -> Self {\n        RawConstraintDefV9 {\n            name: Some(val.name),\n            data: val.data.into(),\n        }\n    }\n}\n\nimpl From<ConstraintDef> for RawConstraintDefV10 {\n    fn from(val: ConstraintDef) -> Self {\n        RawConstraintDefV10 {\n            source_name: Some(val.name),\n            data: val.data.into(),\n        }\n    }\n}\n\n/// Data for a constraint attached to a table.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub enum ConstraintData {\n    Unique(UniqueConstraintData),\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for ConstraintData {\n    fn heap_usage(&self) -> usize {\n        match self {\n            ConstraintData::Unique(d) => d.heap_usage(),\n        }\n    }\n}\n\nimpl ConstraintData {\n    /// If this is a unique constraint, returns the columns that must be unique.\n    /// Otherwise, returns `None`.\n    pub fn unique_columns(&self) -> Option<&ColSet> {\n        match &self {\n            ConstraintData::Unique(UniqueConstraintData { columns }) => Some(columns),\n        }\n    }\n}\n\nimpl From<ConstraintData> for RawConstraintDataV9 {\n    fn from(val: ConstraintData) -> Self {\n        match val {\n            ConstraintData::Unique(unique) => RawConstraintDataV9::Unique(unique.into()),\n        }\n    }\n}\n\n/// Requires that the projection of the table onto these columns is an bijection.\n///\n/// That is, there must be a one-to-one relationship between a row and the `columns` of that row.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct UniqueConstraintData {\n    /// The columns on the containing `TableDef`\n    pub columns: ColSet,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for UniqueConstraintData {\n    fn heap_usage(&self) -> usize {\n        self.columns.heap_usage()\n    }\n}\n\nimpl From<UniqueConstraintData> for RawUniqueConstraintDataV9 {\n    fn from(val: UniqueConstraintData) -> Self {\n        RawUniqueConstraintDataV9 {\n            columns: val.columns.into(),\n        }\n    }\n}\n\nimpl From<UniqueConstraintData> for ConstraintData {\n    fn from(val: UniqueConstraintData) -> Self {\n        ConstraintData::Unique(val)\n    }\n}\n\n/// Data for the `RLS` policy on a table.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct RowLevelSecurityDef {\n    /// The `sql` expression to use for row-level security.\n    pub sql: RawSql,\n}\n\nimpl From<RowLevelSecurityDef> for RawRowLevelSecurityDefV9 {\n    fn from(val: RowLevelSecurityDef) -> Self {\n        RawRowLevelSecurityDefV9 { sql: val.sql }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]\npub enum FunctionKind {\n    /// Functions which have not yet been determined to be reducers or procedures.\n    ///\n    /// Used as a placeholder during module validation,\n    /// when pre-processing [`ScheduleDef`]s prior to validating their scheduled functions.\n    /// Will never appear in a fully-validated [`ModuleDef`],\n    /// and should not be placed in errors either.\n    Unknown,\n    Reducer,\n    Procedure,\n}\n\nimpl fmt::Display for FunctionKind {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(match self {\n            FunctionKind::Unknown => \"exported function\",\n            FunctionKind::Reducer => \"reducer\",\n            FunctionKind::Procedure => \"procedure\",\n        })\n    }\n}\n\n/// Marks a table as a timer table for a scheduled reducer or procedure.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ScheduleDef {\n    /// The name of the schedule. Must be unique within the containing `ModuleDef`.\n    pub name: Identifier,\n\n    /// The name of the column that stores the desired invocation time.\n    ///\n    /// Must be named `scheduled_at` and be of type `ScheduleAt`.\n    pub at_column: ColId,\n\n    /// The name of the column that stores the invocation ID.\n    ///\n    /// Must be named `scheduled_id` and be of type `u64`.\n    pub id_column: ColId,\n\n    /// The name of the reducer or procedure to call.\n    pub function_name: Identifier,\n\n    /// Whether the `function_name` refers to a reducer or a procedure.\n    pub function_kind: FunctionKind,\n}\n\nimpl From<ScheduleDef> for RawScheduleDefV9 {\n    fn from(val: ScheduleDef) -> Self {\n        RawScheduleDefV9 {\n            name: Some(val.name.into()),\n            reducer_name: val.function_name.into(),\n            scheduled_at_column: val.at_column,\n        }\n    }\n}\n\n/// A type exported by the module.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct TypeDef {\n    /// The (scoped) name of the type.\n    pub accessor_name: ScopedTypeName,\n\n    /// The type to which the alias refers.\n    /// Look in `ModuleDef.typespace` for the actual type,\n    /// or in `ModuleDef.typespace_for_generate` for the client codegen version.\n    pub ty: AlgebraicTypeRef,\n\n    /// Whether this type has a custom ordering.\n    pub custom_ordering: bool,\n}\nimpl From<TypeDef> for RawTypeDefV9 {\n    fn from(val: TypeDef) -> Self {\n        RawTypeDefV9 {\n            name: val.accessor_name.into(),\n            ty: val.ty,\n            custom_ordering: val.custom_ordering,\n        }\n    }\n}\n\nimpl From<TypeDef> for RawTypeDefV10 {\n    fn from(val: TypeDef) -> Self {\n        RawTypeDefV10 {\n            source_name: val.accessor_name.into(),\n            ty: val.ty,\n            custom_ordering: val.custom_ordering,\n        }\n    }\n}\n\n/// A scoped type name, in the form `scope0::scope1::...::scopeN::name`.\n///\n/// These are the names that will be used *in client code generation*, NOT the names used for types\n/// in the module source code.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct ScopedTypeName {\n    /// The scope for this type.\n    ///\n    /// Empty unless a sats `name` attribute is used, e.g.\n    /// `#[sats(name = \"namespace.name\")]` in Rust.\n    scope: Box<[Identifier]>,\n\n    /// The name of the type.\n    ///\n    /// Eventually, we may add more information to this, such as generic arguments.\n    name: Identifier,\n}\nimpl ScopedTypeName {\n    /// Create a new `ScopedTypeName` from a scope and a name.\n    pub fn new(scope: Box<[Identifier]>, name: Identifier) -> Self {\n        ScopedTypeName { scope, name }\n    }\n\n    /// Try to create a new `ScopedTypeName` from a scope and a name.\n    /// Errors if the scope or name are invalid.\n    pub fn try_new(\n        scope: impl IntoIterator<Item = RawIdentifier>,\n        name: impl Into<RawIdentifier>,\n    ) -> Result<Self, ErrorStream<IdentifierError>> {\n        let scope = scope\n            .into_iter()\n            .map(|chunk| Identifier::new(chunk).map_err(ErrorStream::from))\n            .collect_all_errors();\n        let name = Identifier::new(name.into()).map_err(ErrorStream::from);\n        let (scope, name) = (scope, name).combine_errors()?;\n        Ok(ScopedTypeName { scope, name })\n    }\n\n    /// Create a new `ScopedTypeName` with an empty scope.\n    pub fn from_name(name: Identifier) -> Self {\n        ScopedTypeName {\n            scope: Box::new([]),\n            name,\n        }\n    }\n\n    /// Retrieve the name of this type.\n    pub fn name(&self) -> &Identifier {\n        &self.name\n    }\n\n    /// Retrieve the name of this type, if the scope is empty.\n    pub fn as_identifier(&self) -> Option<&Identifier> {\n        self.scope.is_empty().then_some(&self.name)\n    }\n\n    /// Iterate over the segments of this name.\n    pub fn name_segments(&self) -> impl Iterator<Item = &Identifier> {\n        self.scope.iter().chain(std::iter::once(&self.name))\n    }\n}\nimpl fmt::Debug for ScopedTypeName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        // we can wrap this in a pair of double quotes, since we know\n        // none of its elements contain quotes.\n        f.write_char('\"')?;\n        for scope in &*self.scope {\n            write!(f, \"{scope}::\")?;\n        }\n        write!(f, \"{}\\\"\", self.name)\n    }\n}\nimpl fmt::Display for ScopedTypeName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        for scope in &*self.scope {\n            write!(f, \"{scope}::\")?;\n        }\n        fmt::Display::fmt(&self.name, f)\n    }\n}\nimpl TryFrom<RawScopedTypeNameV9> for ScopedTypeName {\n    type Error = ErrorStream<IdentifierError>;\n\n    fn try_from(value: RawScopedTypeNameV9) -> Result<Self, Self::Error> {\n        Self::try_new(value.scope.into_vec(), value.name)\n    }\n}\nimpl From<ScopedTypeName> for RawScopedTypeNameV9 {\n    fn from(val: ScopedTypeName) -> Self {\n        RawScopedTypeNameV9 {\n            scope: val.scope.into_vec().into_iter().map(|id| id.into()).collect(),\n            name: val.name.into(),\n        }\n    }\n}\n\nimpl From<ScopedTypeName> for RawScopedTypeNameV10 {\n    fn from(val: ScopedTypeName) -> Self {\n        RawScopedTypeNameV10 {\n            scope: val.scope.into_vec().into_iter().map(|id| id.into()).collect(),\n            source_name: val.name.into(),\n        }\n    }\n}\n\n/// A view exported by the module.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ViewDef {\n    /// The name of the view. This must be unique within the module.\n    pub name: Identifier,\n\n    /// The view identifier as defined in the module source.\n    ///\n    /// Similar to `[TableDef.accessor_name]`.\n    pub accessor_name: Identifier,\n\n    /// Is this a public or a private view?\n    /// Currently only public views are supported.\n    /// Private views may be supported in the future.\n    pub is_public: bool,\n\n    /// Is this view anonymous?\n    /// An anonymous view does not know who called it.\n    /// Specifically, it is a view that has an `AnonymousViewContext` as its first argument.\n    /// This type does not have access to the `Identity` of the caller.\n    pub is_anonymous: bool,\n\n    /// It represents the unique index of this view within the module.\n    /// Module contains separate list for anonymous and non-anonymous views,\n    /// so `is_anonymous` is needed to fully identify the view along with this index.\n    pub fn_ptr: ViewFnPtr,\n\n    /// The parameters of the view.\n    ///\n    /// This `ProductType` need not be registered in the module's `Typespace`.\n    pub params: ProductType,\n\n    /// The parameters of the view, formatted for client codegen.\n    ///\n    /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`.\n    pub params_for_generate: ProductTypeDef,\n\n    /// The return type of the view.\n    /// Either `Option<T>`, `Vec<T>`, or `Query<T>` where:\n    ///\n    /// 1. `T` is a [`ProductType`] containing the columns of the view\n    /// 2. `T` is registered in the module's typespace\n    /// 3. `Option<T>` refers to [`AlgebraicType::option()`]\n    /// 4. `Vec<T>` refers to [`AlgebraicType::array()`]\n    /// 5. `Query<T>` is a special [`ProductType`] `{ __query__: T }`\n    pub return_type: AlgebraicType,\n\n    /// The return type of the view, formatted for client codegen.\n    pub return_type_for_generate: AlgebraicTypeUse,\n\n    /// The single source of truth for the view's columns.\n    ///\n    /// If a view can return only `Option<T>`, `Vec<T>`, or `Query<T>`,\n    /// this is a reference to the inner product type `T`.\n    /// All elements of `T` must have names.\n    pub product_type_ref: AlgebraicTypeRef,\n\n    /// The primary key of the view.\n    ///\n    /// This is set for query-builder views when the underlying table has a primary key.\n    /// The database engine does not actually care about this, but client code generation does.\n    pub primary_key: Option<ColId>,\n\n    /// The return columns of this view.\n    /// The same information is stored in `product_type_ref`.\n    /// This is just a more convenient-to-access format.\n    pub return_columns: Vec<ViewColumnDef>,\n\n    /// The columns that track the arguments of this view.\n    /// The same information is stored in `params`.\n    /// This is just a more convenient-to-access format.\n    pub param_columns: Vec<ViewParamDef>,\n}\n\nimpl ViewDef {\n    /// Get a column by the column's name.\n    pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ViewColumnDef> {\n        self.return_columns.iter().find(|c| &c.name == name)\n    }\n\n    /// Get a parameter by the parameter's name.\n    pub fn get_param_by_name(&self, name: &Identifier) -> Option<&ViewParamDef> {\n        self.param_columns.iter().find(|c| &c.name == name)\n    }\n}\n\nimpl From<ViewDef> for RawViewDefV9 {\n    fn from(val: ViewDef) -> Self {\n        let ViewDef {\n            name,\n            is_anonymous,\n            is_public,\n            params,\n            return_type,\n            fn_ptr: index,\n            ..\n        } = val;\n        RawViewDefV9 {\n            name: name.into(),\n            index: index.into(),\n            is_anonymous,\n            is_public,\n            params,\n            return_type,\n        }\n    }\n}\n\nimpl From<ViewDef> for RawViewDefV10 {\n    fn from(val: ViewDef) -> Self {\n        let ViewDef {\n            accessor_name,\n            is_anonymous,\n            is_public,\n            params,\n            return_type,\n            fn_ptr,\n            ..\n        } = val;\n        RawViewDefV10 {\n            source_name: accessor_name.into(),\n            index: fn_ptr.into(),\n            is_public,\n            is_anonymous,\n            params,\n            return_type,\n        }\n    }\n}\n\nimpl From<ViewDef> for RawMiscModuleExportV9 {\n    fn from(def: ViewDef) -> Self {\n        Self::View(def.into())\n    }\n}\n\n/// The visibility of a function (reducer or procedure).\n#[derive(Debug, Clone, Eq, PartialEq)]\npub enum FunctionVisibility {\n    /// Not callable by arbitrary clients.\n    ///\n    /// Still callable by the module owner, collaborators,\n    /// and internal module code.\n    Private,\n\n    /// Callable from client code.\n    ClientCallable,\n}\n\nimpl FunctionVisibility {\n    pub fn is_private(&self) -> bool {\n        matches!(self, FunctionVisibility::Private)\n    }\n}\n\nuse spacetimedb_lib::db::raw_def::v10::FunctionVisibility as RawFunctionVisibility;\nimpl From<RawFunctionVisibility> for FunctionVisibility {\n    fn from(val: RawFunctionVisibility) -> Self {\n        match val {\n            RawFunctionVisibility::Private => FunctionVisibility::Private,\n            RawFunctionVisibility::ClientCallable => FunctionVisibility::ClientCallable,\n        }\n    }\n}\n\nimpl From<FunctionVisibility> for RawFunctionVisibility {\n    fn from(val: FunctionVisibility) -> Self {\n        match val {\n            FunctionVisibility::Private => RawFunctionVisibility::Private,\n            FunctionVisibility::ClientCallable => RawFunctionVisibility::ClientCallable,\n        }\n    }\n}\n\n/// A reducer exported by the module.\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ReducerDef {\n    /// The name of the reducer. This must be unique within the module's set of reducers and procedures.\n    pub name: ReducerName,\n\n    /// The reducer name as defined in the module source.\n    ///\n    /// All the codegens should use `accessor_name` to generate reducers.\n    /// However, the `name` field should still be used when communicating with the\n    /// database over the network, since the database does not necessarily know\n    /// about accessor names.\n    pub accessor_name: ReducerName,\n\n    /// The parameters of the reducer.\n    ///\n    /// This `ProductType` need not be registered in the module's `Typespace`.\n    pub params: ProductType,\n\n    /// The parameters of the reducer, formatted for client codegen.\n    ///\n    /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`.\n    pub params_for_generate: ProductTypeDef,\n\n    /// The special role of this reducer in the module lifecycle, if any.\n    pub lifecycle: Option<Lifecycle>,\n\n    /// The visibility of this reducer.\n    pub visibility: FunctionVisibility,\n\n    /// The return type of the reducer on success.\n    pub ok_return_type: AlgebraicType,\n\n    /// The return type of the reducer on error.\n    pub err_return_type: AlgebraicType,\n}\n\nimpl From<ReducerDef> for RawReducerDefV9 {\n    fn from(val: ReducerDef) -> Self {\n        RawReducerDefV9 {\n            name: val.name.into(),\n            params: val.params,\n            lifecycle: val.lifecycle,\n        }\n    }\n}\n\nimpl From<ReducerDef> for RawReducerDefV10 {\n    fn from(val: ReducerDef) -> Self {\n        RawReducerDefV10 {\n            source_name: val.accessor_name.into(),\n            params: val.params,\n            visibility: val.visibility.into(),\n            ok_return_type: val.ok_return_type,\n            err_return_type: val.err_return_type,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Eq, PartialEq)]\n#[non_exhaustive]\npub struct ProcedureDef {\n    /// The name of the procedure.\n    ///\n    /// This must be unique within the module's set of reducers and procedures.\n    pub name: Identifier,\n\n    /// The procedure name as defined in the module source.\n    ///\n    /// Similar to `[ReducerDef.accessor_name]`.\n    pub accessor_name: Identifier,\n    /// The parameters of the procedure.\n    ///\n    /// This `ProductType` need not be registered in the module's `Typespace`.\n    pub params: ProductType,\n\n    /// The parameters of the procedure, formatted for client codegen.\n    ///\n    /// This `ProductType` need not be registered in the module's `TypespaceForGenerate`.\n    pub params_for_generate: ProductTypeDef,\n\n    /// The return type of the procedure.\n    ///\n    /// If this is a non-special compound type, it should be registered in the module's `Typespace`\n    /// and indirected through an [`AlgebraicType::Ref`].\n    pub return_type: AlgebraicType,\n\n    /// The return type of the procedure.\n    ///\n    /// If this is a non-special compound type, it should be registered in the module's `TypespaceForGenerate`\n    /// and indirected through an [`AlgebraicTypeUse::Ref`].\n    pub return_type_for_generate: AlgebraicTypeUse,\n\n    /// The visibility of this procedure.\n    pub visibility: FunctionVisibility,\n}\n\nimpl From<ProcedureDef> for RawProcedureDefV9 {\n    fn from(val: ProcedureDef) -> Self {\n        RawProcedureDefV9 {\n            name: val.name.into(),\n            params: val.params,\n            return_type: val.return_type,\n        }\n    }\n}\n\nimpl From<ProcedureDef> for RawProcedureDefV10 {\n    fn from(val: ProcedureDef) -> Self {\n        RawProcedureDefV10 {\n            source_name: val.accessor_name.into(),\n            params: val.params,\n            return_type: val.return_type,\n            visibility: val.visibility.into(),\n        }\n    }\n}\n\nimpl From<ProcedureDef> for RawMiscModuleExportV9 {\n    fn from(def: ProcedureDef) -> Self {\n        Self::Procedure(def.into())\n    }\n}\n\nimpl ModuleDefLookup for TableDef {\n    type Key<'a> = &'a Identifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.tables.get(key)\n    }\n}\n\nimpl ModuleDefLookup for SequenceDef {\n    type Key<'a> = &'a RawIdentifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.stored_in_table_def(key)?.sequences.get(key)\n    }\n}\n\nimpl ModuleDefLookup for IndexDef {\n    type Key<'a> = &'a RawIdentifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.stored_in_table_def(key)?.indexes.get(key)\n    }\n}\n\nimpl ModuleDefLookup for ColumnDef {\n    // (table_name, column_name).\n    // We don't use `ColId` here because we want this to be portable\n    // across migrations.\n    type Key<'a> = (&'a Identifier, &'a Identifier);\n\n    fn key(&self) -> Self::Key<'_> {\n        (&self.table_name, &self.name)\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, (table_name, name): Self::Key<'_>) -> Option<&'a Self> {\n        module_def\n            .tables\n            .get(table_name)\n            .and_then(|table| table.get_column_by_name(name))\n    }\n}\n\nimpl ModuleDefLookup for ViewColumnDef {\n    // We don't use `ColId` here because we want this to be portable\n    // across migrations.\n    type Key<'a> = (&'a Identifier, &'a Identifier);\n\n    fn key(&self) -> Self::Key<'_> {\n        (&self.view_name, &self.name)\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, (view_name, name): Self::Key<'_>) -> Option<&'a Self> {\n        module_def\n            .views\n            .get(view_name)\n            .and_then(|view| view.get_column_by_name(name))\n    }\n}\n\nimpl ModuleDefLookup for ViewParamDef {\n    // We don't use `ColId` here because we want this to be portable\n    // across migrations.\n    type Key<'a> = (&'a Identifier, &'a Identifier);\n\n    fn key(&self) -> Self::Key<'_> {\n        (&self.view_name, &self.name)\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, (view_name, name): Self::Key<'_>) -> Option<&'a Self> {\n        module_def\n            .views\n            .get(view_name)\n            .and_then(|view| view.get_param_by_name(name))\n    }\n}\n\nimpl ModuleDefLookup for ConstraintDef {\n    type Key<'a> = &'a RawIdentifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.stored_in_table_def(key)?.constraints.get(key)\n    }\n}\n\nimpl ModuleDefLookup for RawRowLevelSecurityDefV9 {\n    type Key<'a> = &'a RawSql;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.sql\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.row_level_security_raw.get(key)\n    }\n}\n\nimpl ModuleDefLookup for ScheduleDef {\n    type Key<'a> = &'a Identifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        let schedule = module_def.stored_in_table_def(key.as_raw())?.schedule.as_ref()?;\n        if &schedule.name == key {\n            Some(schedule)\n        } else {\n            None\n        }\n    }\n}\n\nimpl ModuleDefLookup for TypeDef {\n    type Key<'a> = &'a ScopedTypeName;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.accessor_name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        module_def.types.get(key)\n    }\n}\n\nimpl ModuleDefLookup for ReducerDef {\n    type Key<'a> = &'a ReducerName;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        let key = &**key;\n        module_def.reducers.get(key)\n    }\n}\n\nimpl ModuleDefLookup for ProcedureDef {\n    type Key<'a> = &'a Identifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        let key = &**key;\n        module_def.procedures.get(key)\n    }\n}\n\nimpl ModuleDefLookup for ViewDef {\n    type Key<'a> = &'a Identifier;\n\n    fn key(&self) -> Self::Key<'_> {\n        &self.name\n    }\n\n    fn lookup<'a>(view_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {\n        view_def.views.get(key)\n    }\n}\n\nfn to_raw<Def, RawDef, Name>(data: HashMap<Name, Def>) -> Vec<RawDef>\nwhere\n    Def: ModuleDefLookup + Into<RawDef>,\n    Name: Eq + Ord + 'static,\n{\n    let sorted: BTreeMap<Name, Def> = data.into_iter().collect();\n    sorted.into_values().map_into().collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{def::validate::tests::expect_identifier, error::ValidationError};\n\n    use super::*;\n    use proptest::prelude::*;\n    use spacetimedb_data_structures::{expect_error_matching, map::HashCollectionExt as _};\n    use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9Builder;\n\n    proptest! {\n        #[test]\n        fn to_raw_deterministic(vec in prop::collection::vec(any::<u32>(), 0..5)) {\n            let mut map = HashMap::new();\n            let name = ScopedTypeName::try_new([], \"fake_name\").unwrap();\n            for k in vec {\n                let def = TypeDef { accessor_name: name.clone(), ty: AlgebraicTypeRef(k), custom_ordering: false };\n                map.insert(k, def);\n            }\n            let raw: Vec<RawTypeDefV9> = to_raw(map.clone());\n            let raw2: Vec<RawTypeDefV9> = to_raw(map);\n            prop_assert_eq!(raw, raw2);\n        }\n    }\n\n    #[test]\n    fn validate_new_column_with_multiple_values() {\n        let mut old_builder = RawModuleDefV9Builder::new();\n        old_builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([(\"id\", AlgebraicType::U64), (\"count\", AlgebraicType::U16)]),\n                true,\n            )\n            .with_default_column_value(1, AlgebraicValue::U16(12))\n            .with_default_column_value(1, AlgebraicValue::U16(10))\n            .finish();\n\n        let result: Result<ModuleDef, ValidationErrors> = old_builder.finish().try_into();\n        let apples = expect_identifier(\"Apples\");\n\n        expect_error_matching!(\n            result,\n            ValidationError::MultipleColumnDefaultValues {\n                table,\n                ..\n            } => *table == apples.clone().into()\n        );\n    }\n\n    #[test]\n    fn validate_new_column_with_malformed_value() {\n        let mut old_builder = RawModuleDefV9Builder::new();\n        old_builder\n            .build_table_with_new_type(\n                \"Apples\",\n                ProductType::from([(\"id\", AlgebraicType::U64), (\"count\", AlgebraicType::U16)]),\n                true,\n            )\n            .with_default_column_value(1, AlgebraicValue::Bool(false))\n            .with_default_column_value(1, AlgebraicValue::unit())\n            .finish();\n\n        let result: Result<ModuleDef, ValidationErrors> = old_builder.finish().try_into();\n        let apples = expect_identifier(\"Apples\");\n\n        expect_error_matching!(\n            result,\n            ValidationError::ColumnDefaultValueMalformed { table, col_id, .. } => *table == apples.clone().into() && *col_id == ColId(1)\n        );\n        assert!(result.is_err_and(|e| e\n            .into_iter()\n            .filter(|e| matches!(e, ValidationError::ColumnDefaultValueMalformed { .. }))\n            .count()\n            == 2))\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/error.rs",
    "content": "use spacetimedb_data_structures::error_stream::ErrorStream;\nuse spacetimedb_lib::db::raw_def::v9::{Lifecycle, RawScopedTypeNameV9};\nuse spacetimedb_lib::{ProductType, SumType};\nuse spacetimedb_primitives::{ColId, ColList, ColSet};\nuse spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type;\nuse spacetimedb_sats::{bsatn::DecodeError, raw_identifier::RawIdentifier, AlgebraicType, AlgebraicTypeRef};\nuse std::fmt;\n\nuse crate::def::{FunctionKind, ScopedTypeName};\nuse crate::identifier::Identifier;\nuse crate::type_for_generate::ClientCodegenError;\n\n/// A stream of validation errors, defined using the `ErrorStream` type.\npub type ValidationErrors = ErrorStream<ValidationError>;\n\n/// A single validation error.\n///\n/// Many variants of this enum store `RawIdentifier`s rather than `Identifier`s.\n/// This is because we want to support reporting errors about module entities with invalid names.\n#[derive(thiserror::Error, Debug, PartialOrd, Ord, PartialEq, Eq)]\n#[non_exhaustive]\npub enum ValidationError {\n    #[error(\"name `{name}` is used for multiple entities\")]\n    DuplicateName { name: RawIdentifier },\n    #[error(\"name `{name}` is used for multiple types\")]\n    DuplicateTypeName { name: ScopedTypeName },\n    #[error(\"Multiple reducers defined for lifecycle event {lifecycle:?}\")]\n    DuplicateLifecycle { lifecycle: Lifecycle },\n    #[error(\"module contains invalid identifier: {error}\")]\n    IdentifierError { error: IdentifierError },\n    #[error(\"table `{}` has unnamed column `{}`, which is forbidden.\", column.table, column.column)]\n    UnnamedColumn { column: RawColumnName },\n    #[error(\"type `{type_name:?}` is not annotated with a custom ordering, but uses one: {bad_type}\")]\n    TypeHasIncorrectOrdering {\n        type_name: RawScopedTypeNameV9,\n        ref_: AlgebraicTypeRef,\n        /// Could be a sum or product.\n        bad_type: PrettyAlgebraicType,\n    },\n    #[error(\"column `{column}` referenced by def {def} not found in table `{table}`\")]\n    ColumnNotFound {\n        table: RawIdentifier,\n        def: RawIdentifier,\n        column: ColId,\n    },\n    #[error(\n        \"{column} type {ty} does not match corresponding element at position {pos} in product type `{product_type}`\"\n    )]\n    ColumnDefMalformed {\n        column: RawColumnName,\n        ty: PrettyAlgebraicType,\n        pos: ColId,\n        product_type: PrettyAlgebraicType,\n    },\n    #[error(\"table `{table}` has multiple primary key annotations\")]\n    RepeatedPrimaryKey { table: RawIdentifier },\n    #[error(\"Attempt to define {column} with more than 1 auto_inc sequence\")]\n    OneAutoInc { column: RawColumnName },\n    #[error(\"No index found to support unique constraint `{constraint}` for columns `{columns:?}`\")]\n    UniqueConstraintWithoutIndex { constraint: RawIdentifier, columns: ColSet },\n    #[error(\"Direct index does not support type `{ty}` in column `{column}` in index `{index}`\")]\n    DirectIndexOnBadType {\n        index: RawIdentifier,\n        column: RawIdentifier,\n        ty: PrettyAlgebraicType,\n    },\n    #[error(\"def `{def}` has duplicate columns: {columns:?}\")]\n    DuplicateColumns { def: RawIdentifier, columns: ColList },\n    #[error(\"invalid sequence column type: {column} with type `{column_type:?}` in sequence `{sequence}`\")]\n    InvalidSequenceColumnType {\n        sequence: RawIdentifier,\n        column: RawColumnName,\n        column_type: PrettyAlgebraicType,\n    },\n    #[error(\"invalid sequence range information: expected {min_value:?} <= {start:?} <= {max_value:?} in sequence `{sequence}`\")]\n    InvalidSequenceRange {\n        sequence: RawIdentifier,\n        min_value: Option<i128>,\n        start: Option<i128>,\n        max_value: Option<i128>,\n    },\n    #[error(\"View {view} has invalid return type {ty}\")]\n    InvalidViewReturnType {\n        view: RawIdentifier,\n        ty: PrettyAlgebraicType,\n    },\n    #[error(\"Table {table} has invalid product_type_ref {ref_}\")]\n    InvalidProductTypeRef {\n        table: RawIdentifier,\n        ref_: AlgebraicTypeRef,\n    },\n    #[error(\"Type {type_name:?} has invalid ref: {ref_}\")]\n    InvalidTypeRef {\n        type_name: RawScopedTypeNameV9,\n        ref_: AlgebraicTypeRef,\n    },\n    #[error(\"A scheduled table must have columns `scheduled_id: u64` and `scheduled_at: ScheduledAt`, but table `{table}` has columns {columns:?}\")]\n    ScheduledIncorrectColumns { table: RawIdentifier, columns: ProductType },\n    #[error(\"error at {location}: {error}\")]\n    ClientCodegenError {\n        location: TypeLocation,\n        error: ClientCodegenError,\n    },\n    #[error(\"Missing type definition for ref: {ref_}, holds type: {ty}\")]\n    MissingTypeDef {\n        ref_: AlgebraicTypeRef,\n        ty: PrettyAlgebraicType,\n    },\n    #[error(\"{column} is primary key but has no unique constraint\")]\n    MissingPrimaryKeyUniqueConstraint { column: RawColumnName },\n    #[error(\"Table {table} should have a type definition for its product_type_element, but does not\")]\n    TableTypeNameMismatch { table: Identifier },\n    #[error(\"Schedule {schedule} refers to a scheduled reducer or procedure {function} that does not exist\")]\n    MissingScheduledFunction { schedule: Identifier, function: Identifier },\n    #[error(\"Scheduled {function_kind} {function_name} expected to have type {expected}, but has type {actual}\")]\n    IncorrectScheduledFunctionParams {\n        function_name: RawIdentifier,\n        function_kind: FunctionKind,\n        expected: PrettyAlgebraicType,\n        actual: PrettyAlgebraicType,\n    },\n    #[error(\"Table name is reserved for system use: {table}\")]\n    TableNameReserved { table: Identifier },\n    #[error(\"Row-level security invalid: `{error}`, query: `{sql}\")]\n    InvalidRowLevelQuery { sql: String, error: String },\n    #[error(\"Failed to deserialize default value for table {table} column {col_id}: {err}\")]\n    ColumnDefaultValueMalformed {\n        table: RawIdentifier,\n        col_id: ColId,\n        err: DecodeError,\n    },\n    #[error(\"Multiple default values for table {table} column {col_id}\")]\n    MultipleColumnDefaultValues { table: RawIdentifier, col_id: ColId },\n    #[error(\"Table {table} not found\")]\n    TableNotFound { table: RawIdentifier },\n    #[error(\"Name {name} is used for multiple reducers, procedures and/or views\")]\n    DuplicateFunctionName { name: Identifier },\n    #[error(\"lifecycle event {lifecycle:?} without reducer\")]\n    LifecycleWithoutReducer { lifecycle: Lifecycle },\n    #[error(\"lifecycle event {lifecycle:?} assigned multiple reducers\")]\n    DuplicateLifeCycle { lifecycle: Lifecycle },\n    #[error(\"table {table} is assigned in multiple schedules\")]\n    DuplicateSchedule { table: Identifier },\n    #[error(\"table {} corresponding to schedule {} not found\", table_name, schedule_name)]\n    MissingScheduleTable {\n        table_name: RawIdentifier,\n        schedule_name: Identifier,\n    },\n    #[error(\"reducer {reducer_name} has invalid return type: found Result<{ok_type}, {err_type}>\")]\n    InvalidReducerReturnType {\n        reducer_name: RawIdentifier,\n        ok_type: PrettyAlgebraicType,\n        err_type: PrettyAlgebraicType,\n    },\n}\n\n/// A wrapper around an `AlgebraicType` that implements `fmt::Display`.\n#[derive(PartialOrd, Ord, PartialEq, Eq)]\npub struct PrettyAlgebraicType(pub AlgebraicType);\n\nimpl fmt::Display for PrettyAlgebraicType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt_algebraic_type(&self.0).fmt(f)\n    }\n}\nimpl fmt::Debug for PrettyAlgebraicType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        <Self as fmt::Display>::fmt(self, f)\n    }\n}\nimpl From<AlgebraicType> for PrettyAlgebraicType {\n    fn from(ty: AlgebraicType) -> Self {\n        Self(ty)\n    }\n}\nimpl From<ProductType> for PrettyAlgebraicType {\n    fn from(ty: ProductType) -> Self {\n        let ty: AlgebraicType = ty.into();\n        Self(ty)\n    }\n}\nimpl From<SumType> for PrettyAlgebraicType {\n    fn from(ty: SumType) -> Self {\n        let ty: AlgebraicType = ty.into();\n        Self(ty)\n    }\n}\n\n/// A place a type can be located in a module.\n#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum TypeLocation {\n    /// A reducer argument.\n    ReducerArg {\n        reducer_name: RawIdentifier,\n        position: usize,\n        arg_name: Option<RawIdentifier>,\n    },\n    /// A procedure argument.\n    ProcedureArg {\n        procedure_name: RawIdentifier,\n        position: usize,\n        arg_name: Option<RawIdentifier>,\n    },\n    /// A view argument.\n    ViewArg {\n        view_name: RawIdentifier,\n        position: usize,\n        arg_name: Option<RawIdentifier>,\n    },\n    /// A procedure return type.\n    ProcedureReturn { procedure_name: RawIdentifier },\n    /// A view return type.\n    ViewReturn { view_name: RawIdentifier },\n    /// A type in the typespace.\n    InTypespace {\n        /// The reference to the type within the typespace.\n        ref_: AlgebraicTypeRef,\n    },\n}\n\nimpl fmt::Display for TypeLocation {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            TypeLocation::ReducerArg {\n                reducer_name,\n                position,\n                arg_name,\n            } => {\n                write!(f, \"reducer `{reducer_name}` argument {position}\")?;\n                if let Some(arg_name) = arg_name {\n                    write!(f, \" (`{arg_name}`)\")?;\n                }\n                Ok(())\n            }\n            TypeLocation::ProcedureArg {\n                procedure_name,\n                position,\n                arg_name,\n            } => {\n                write!(f, \"procedure `{procedure_name}` argument {position}\")?;\n                if let Some(arg_name) = arg_name {\n                    write!(f, \" (`{arg_name}`)\")?;\n                }\n                Ok(())\n            }\n            TypeLocation::ViewArg {\n                view_name,\n                position,\n                arg_name,\n            } => {\n                write!(f, \"view `{view_name}` argument {position}\")?;\n                if let Some(arg_name) = arg_name {\n                    write!(f, \" (`{arg_name}`)\")?;\n                }\n                Ok(())\n            }\n            TypeLocation::ProcedureReturn { procedure_name } => {\n                write!(f, \"procedure `{procedure_name}` return value\")\n            }\n            TypeLocation::ViewReturn { view_name } => {\n                write!(f, \"view `{view_name}` return value\")\n            }\n            TypeLocation::InTypespace { ref_ } => {\n                write!(f, \"typespace ref `{ref_}`\")\n            }\n        }\n    }\n}\n\n/// The name of a column.\n#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]\npub struct RawColumnName {\n    /// The table the column is in.\n    pub table: RawIdentifier,\n    /// The name of the column. This may be an integer if the column is unnamed.\n    pub column: RawIdentifier,\n}\n\nimpl RawColumnName {\n    /// Create a new `RawColumnName`.\n    pub fn new(table: impl Into<RawIdentifier>, column: impl Into<RawIdentifier>) -> Self {\n        Self {\n            table: table.into(),\n            column: column.into(),\n        }\n    }\n}\n\nimpl fmt::Display for RawColumnName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"table `{}` column `{}`\", self.table, self.column)\n    }\n}\n\n/// A reason that a string the user used is not allowed.\n#[derive(thiserror::Error, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum IdentifierError {\n    /// The identifier is not in Unicode Normalization Form C.\n    ///\n    /// TODO(1.0): We should *canonicalize* identifiers,\n    /// rather than simply *rejecting* non-canonicalized identifiers.\n    /// However, this will require careful testing of codegen in both modules and clients,\n    /// to ensure that the canonicalization is done consistently.\n    /// Otherwise, strange name errors will result.\n    #[error(\n        \"Identifier `{name}` is not in normalization form C according to Unicode Standard Annex 15 \\\n        (http://www.unicode.org/reports/tr15/) and cannot be used for entities in a module.\"\n    )]\n    NotCanonicalized { name: RawIdentifier },\n\n    /// The identifier is reserved.\n    #[error(\"Identifier `{name}` is reserved by spacetimedb and cannot be used for entities in a module.\")]\n    Reserved { name: RawIdentifier },\n\n    #[error(\n        \"Identifier `{name}`'s starting character '{invalid_start}' is neither an underscore ('_') nor a \\\n        Unicode XID_start character (according to Unicode Standard Annex 31, https://www.unicode.org/reports/tr31/) \\\n        and cannot be used for entities in a module.\"\n    )]\n    InvalidStart { name: RawIdentifier, invalid_start: char },\n\n    #[error(\n        \"Identifier `{name}` contains a character '{invalid_continue}' that is not an XID_continue character \\\n        (according to Unicode Standard Annex 31, https://www.unicode.org/reports/tr31/) \\\n        and cannot be used for entities in a module.\"\n    )]\n    InvalidContinue {\n        name: RawIdentifier,\n        invalid_continue: char,\n    },\n    // This is not a particularly useful error without a link to WHICH identifier is empty.\n    #[error(\"Empty identifiers are forbidden.\")]\n    Empty {},\n}\n"
  },
  {
    "path": "crates/schema/src/identifier.rs",
    "content": "use crate::error::IdentifierError;\nuse spacetimedb_data_structures::map::{Equivalent, HashSet};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st};\nuse std::fmt::{self, Debug, Display};\nuse std::ops::Deref;\nuse unicode_ident::{is_xid_continue, is_xid_start};\nuse unicode_normalization::UnicodeNormalization;\n\nlazy_static::lazy_static! {\n    /// TODO(1.0): Pull in the rest of the reserved identifiers from the Identifier Proposal once that's merged.\n    static ref RESERVED_IDENTIFIERS: HashSet<&'static str> = include_str!(\"reserved_identifiers.txt\").lines().collect();\n}\n\n/// A valid SpacetimeDB Identifier.\n///\n/// Identifiers must be normalized according to [Unicode Standard Annex 15](https://www.unicode.org/reports/tr15/), normalization form C\n/// (Canonical Decomposition followed by Canonical Composition).\n/// Following Rust, we use the identifier rules defined by [Unicode Standard Annex 31](https://www.unicode.org/reports/tr31/tr31-37.html) to validate identifiers.\n/// We allow underscores as well as any XID_Start character to start an identifier.\n///\n/// In addition, we forbid the use of any identifier reserved by [PostgreSQL](https://www.postgresql.org/docs/current/sql-keywords-appendix.html).\n/// Any string that is converted into a reserved word by the Rust function\n/// [`String::to_uppercase`](https://doc.rust-lang.org/std/string/struct.String.html#method.to_uppercase) will be rejected.\n///\n/// The list of reserved words can be found in the file `SpacetimeDB/crates/sats/db/reserved_identifiers.txt`.\n///\n/// Internally, this is just a raw identifier with some validation on construction.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct Identifier {\n    id: RawIdentifier,\n}\n\nimpl_st!([] Identifier, ts => RawIdentifier::make_type(ts));\nimpl_serialize!([] Identifier, (self, ser) => ser.serialize_str(&self.id));\nimpl_deserialize!([] Identifier, de => RawIdentifier::deserialize(de).map(Self::new_assume_valid));\n\nimpl Identifier {\n    /// Returns a new identifier without validating the input.\n    pub fn new_assume_valid(name: RawIdentifier) -> Self {\n        Self { id: name }\n    }\n\n    /// Validates that the input string is a valid identifier.\n    ///\n    /// Currently, this rejects non-canonicalized identifiers.\n    /// Eventually, it will be changed to canonicalize the input string.\n    pub fn new(name: RawIdentifier) -> Result<Self, IdentifierError> {\n        if name.is_empty() {\n            return Err(IdentifierError::Empty {});\n        }\n\n        // Convert to Unicode Normalization Form C (canonical decomposition followed by composition).\n        if name.nfc().zip(name.chars()).any(|(a, b)| a != b) {\n            return Err(IdentifierError::NotCanonicalized { name });\n        }\n\n        let mut chars = name.chars();\n\n        let start = chars.next().ok_or(IdentifierError::Empty {})?;\n        if !is_xid_start(start) && start != '_' {\n            return Err(IdentifierError::InvalidStart {\n                name,\n                invalid_start: start,\n            });\n        }\n\n        for char_ in chars {\n            if !is_xid_continue(char_) {\n                return Err(IdentifierError::InvalidContinue {\n                    name,\n                    invalid_continue: char_,\n                });\n            }\n        }\n\n        if Identifier::is_reserved(&name) {\n            return Err(IdentifierError::Reserved { name });\n        }\n\n        Ok(Identifier { id: name })\n    }\n\n    pub fn for_test(name: impl AsRef<str>) -> Self {\n        Identifier::new(RawIdentifier::new(name.as_ref())).unwrap()\n    }\n\n    /// Returns the raw identifier of this identifier.\n    pub fn as_raw(&self) -> &RawIdentifier {\n        &self.id\n    }\n\n    /// Check if a string is a reserved identifier.\n    pub fn is_reserved(name: &str) -> bool {\n        RESERVED_IDENTIFIERS.contains(&*name.to_uppercase())\n    }\n}\n\nimpl Debug for Identifier {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Debug::fmt(&self.id, f)\n    }\n}\n\nimpl Display for Identifier {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&self.id, f)\n    }\n}\n\nimpl Deref for Identifier {\n    type Target = str;\n\n    fn deref(&self) -> &str {\n        &self.id\n    }\n}\n\nimpl Equivalent<Identifier> for str {\n    fn equivalent(&self, other: &Identifier) -> bool {\n        self == &other.id[..]\n    }\n}\n\nimpl From<Identifier> for RawIdentifier {\n    fn from(id: Identifier) -> Self {\n        id.id\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n\n    fn new(s: &str) -> Result<Identifier, IdentifierError> {\n        Identifier::new(RawIdentifier::new(s))\n    }\n\n    #[test]\n    fn test_a_bunch_of_identifiers() {\n        assert!(new(\"friends\").is_ok());\n        assert!(new(\"Oysters\").is_ok());\n        assert!(new(\"_hello\").is_ok());\n        assert!(new(\"bananas_there_\").is_ok());\n        assert!(new(\"Москва\").is_ok());\n        assert!(new(\"東京\").is_ok());\n        assert!(new(\"bees123\").is_ok());\n\n        assert!(new(\"\").is_err());\n        assert!(new(\"123bees\").is_err());\n        assert!(new(\"\\u{200B}hello\").is_err()); // zero-width space\n        assert!(new(\" hello\").is_err());\n        assert!(new(\"hello \").is_err());\n        assert!(new(\"🍌\").is_err()); // ;-; the unicode committee is no fun\n        assert!(new(\"\").is_err());\n    }\n\n    #[test]\n    fn test_canonicalization() {\n        assert!(new(\"_\\u{0041}\\u{030A}\").is_err());\n        // canonicalized version of the above.\n        assert!(new(\"_\\u{00C5}\").is_ok());\n    }\n\n    proptest! {\n        #[test]\n        fn test_standard_ascii_identifiers(s in \"[a-zA-Z_][a-zA-Z0-9_]*\") {\n            // Ha! Proptest will reliably find these.\n            prop_assume!(!Identifier::is_reserved(&s));\n\n            prop_assert!(new(&s).is_ok());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/lib.rs",
    "content": "//! Internal SpacetimeDB schema handling.\n//!\n//! Handles validation and normalization of raw schema definitions from the `spacetimedb_lib` crate.\n\npub mod auto_migrate;\npub mod def;\npub mod error;\npub mod identifier;\npub mod reducer_name;\npub mod relation;\npub mod schema;\npub mod table_name;\npub mod type_for_generate;\n"
  },
  {
    "path": "crates/schema/src/reducer_name.rs",
    "content": "use crate::identifier::Identifier;\nuse core::fmt;\nuse core::ops::Deref;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\n\n/// The name of a reducer.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct ReducerName(pub Identifier);\n\nimpl ReducerName {\n    pub fn new(id: Identifier) -> Self {\n        Self(id)\n    }\n\n    pub fn for_test(name: &str) -> Self {\n        Self(Identifier::for_test(name))\n    }\n\n    pub fn as_identifier(&self) -> &Identifier {\n        &self.0\n    }\n}\n\nimpl Deref for ReducerName {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl AsRef<str> for ReducerName {\n    fn as_ref(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl From<ReducerName> for Identifier {\n    fn from(id: ReducerName) -> Self {\n        id.0\n    }\n}\n\nimpl From<ReducerName> for RawIdentifier {\n    fn from(id: ReducerName) -> Self {\n        Identifier::from(id).into()\n    }\n}\n\nimpl fmt::Debug for ReducerName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&self.0, f)\n    }\n}\n\nimpl fmt::Display for ReducerName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.0, f)\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/relation.rs",
    "content": "use crate::def::error::{RelationError, TypeError};\nuse crate::table_name::TableName;\nuse core::fmt;\nuse core::hash::Hash;\nuse derive_more::From;\nuse spacetimedb_data_structures::map::HashSet;\nuse spacetimedb_lib::db::auth::{StAccess, StTableType};\nuse spacetimedb_primitives::{ColId, ColList, ColSet, Constraints, TableId};\nuse spacetimedb_sats::algebraic_value::AlgebraicValue;\nuse spacetimedb_sats::satn::Satn;\nuse spacetimedb_sats::{algebraic_type, AlgebraicType};\nuse std::collections::BTreeMap;\nuse std::sync::Arc;\n\n#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]\npub struct FieldName {\n    pub table: TableId,\n    pub col: ColId,\n}\n\nimpl FieldName {\n    pub fn new(table: TableId, col: ColId) -> Self {\n        Self { table, col }\n    }\n\n    pub fn table(&self) -> TableId {\n        self.table\n    }\n\n    pub fn field(&self) -> ColId {\n        self.col\n    }\n}\n\n// TODO(perf): Remove `Clone` derivation.\n#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, From)]\npub enum ColExpr {\n    Col(ColId),\n    Value(AlgebraicValue),\n}\n\nimpl ColExpr {\n    /// Returns a borrowed version of `ColExpr`.\n    pub fn borrowed(&self) -> ColExprRef<'_> {\n        match self {\n            Self::Col(x) => ColExprRef::Col(*x),\n            Self::Value(x) => ColExprRef::Value(x),\n        }\n    }\n}\n\nimpl fmt::Debug for FieldName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(self, f)\n    }\n}\n\nimpl fmt::Display for FieldName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"table#{}.col#{}\", self.table, self.col)\n    }\n}\n\nimpl fmt::Display for ColExpr {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            ColExpr::Col(x) => write!(f, \"{x}\"),\n            ColExpr::Value(x) => write!(f, \"{}\", x.to_satn()),\n        }\n    }\n}\n\n/// A borrowed version of `FieldExpr`.\n#[derive(Clone, Copy)]\npub enum ColExprRef<'a> {\n    Col(ColId),\n    Value(&'a AlgebraicValue),\n}\n\n// TODO(perf): Remove `Clone` derivation.\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct Column {\n    pub field: FieldName,\n    pub algebraic_type: AlgebraicType,\n}\n\nimpl Column {\n    pub fn new(field: FieldName, algebraic_type: AlgebraicType) -> Self {\n        Self { field, algebraic_type }\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Hash, Clone)]\npub struct Header {\n    pub table_id: TableId,\n    pub table_name: TableName,\n    pub fields: Vec<Column>,\n    pub constraints: BTreeMap<ColList, Constraints>,\n}\n\nimpl Header {\n    /// Create a new header.\n    ///\n    /// `uncombined_constraints` will be normalized using [`combine_constraints`].\n    pub fn new(\n        table_id: TableId,\n        table_name: TableName,\n        fields: Vec<Column>,\n        uncombined_constraints: impl IntoIterator<Item = (ColList, Constraints)>,\n    ) -> Self {\n        Self {\n            table_id,\n            table_name,\n            fields,\n            constraints: combine_constraints(uncombined_constraints),\n        }\n    }\n\n    /// Equivalent to what [`Clone::clone`] would do.\n    ///\n    /// `Header` intentionally does not implement `Clone`,\n    /// as we can't afford to clone it in normal execution paths.\n    /// However, we don't care about performance in error paths,\n    /// and we need to embed owned `Header`s in error objects to report useful messages.\n    pub fn clone_for_error(&self) -> Self {\n        Header::new(\n            self.table_id,\n            self.table_name.clone(),\n            self.fields.clone(),\n            self.constraints.clone(),\n        )\n    }\n\n    /// Finds the index of the column with a matching `FieldName`.\n    pub fn column_pos(&self, col: FieldName) -> Option<ColId> {\n        self.fields.iter().position(|f| f.field == col).map(Into::into)\n    }\n\n    pub fn column_pos_or_err(&self, col: FieldName) -> Result<ColId, RelationError> {\n        self.column_pos(col)\n            .ok_or_else(|| RelationError::FieldNotFound(self.clone_for_error(), col))\n    }\n\n    pub fn field_name(&self, col: FieldName) -> Option<(ColId, FieldName)> {\n        self.column_pos(col).map(|id| (id, self.fields[id.idx()].field))\n    }\n\n    /// Copy the [Constraints] that are referenced in the list of `for_columns`\n    fn retain_constraints(&self, for_columns: &ColList) -> BTreeMap<ColList, Constraints> {\n        // Copy the constraints of the selected columns and retain the multi-column ones...\n        self.constraints\n            .iter()\n            // Keep constraints with a col list where at least one col is in `for_columns`.\n            .filter(|(cols, _)| cols.iter().any(|c| for_columns.contains(c)))\n            .map(|(cols, constraints)| (cols.clone(), *constraints))\n            .collect()\n    }\n\n    pub fn has_constraint(&self, field: ColId, constraint: Constraints) -> bool {\n        self.constraints\n            .iter()\n            .any(|(col, ct)| col.contains(field) && ct.contains(&constraint))\n    }\n\n    /// Project the [ColExpr]s & the [Constraints] that referenced them\n    pub fn project(&self, cols: &[ColExpr]) -> Result<Self, RelationError> {\n        let mut fields = Vec::with_capacity(cols.len());\n        let mut to_keep = ColList::with_capacity(cols.len() as _);\n\n        for (pos, col) in cols.iter().enumerate() {\n            match col {\n                ColExpr::Col(col) => {\n                    to_keep.push(*col);\n                    fields.push(self.fields[col.idx()].clone());\n                }\n                ColExpr::Value(val) => {\n                    // TODO: why should this field name be relevant?\n                    // We should generate immediate names instead.\n                    let field = FieldName::new(self.table_id, pos.into());\n                    let ty = val.type_of().ok_or_else(|| {\n                        RelationError::TypeInference(field, TypeError::CannotInferType { value: val.clone() })\n                    })?;\n                    fields.push(Column::new(field, ty));\n                }\n            }\n        }\n\n        let constraints = self.retain_constraints(&to_keep);\n\n        Ok(Self::new(self.table_id, self.table_name.clone(), fields, constraints))\n    }\n\n    /// Project the ourself onto the `ColList`, keeping constraints that reference the columns in the ColList.\n    /// Does not change `ColIDs`.\n    pub fn project_col_list(&self, cols: &ColList) -> Self {\n        let mut fields = Vec::with_capacity(cols.len() as usize);\n\n        for col in cols.iter() {\n            fields.push(self.fields[col.idx()].clone());\n        }\n\n        let constraints = self.retain_constraints(cols);\n        Self::new(self.table_id, self.table_name.clone(), fields, constraints)\n    }\n\n    /// Adds the fields &  [Constraints] from `right` to this [`Header`],\n    /// renaming duplicated fields with a counter like `a, a => a, a0`.\n    pub fn extend(&self, right: &Self) -> Self {\n        // Increase the positions of the columns in `right.constraints`, adding the count of fields on `left`\n        let mut constraints = self.constraints.clone();\n        let len_lhs = self.fields.len() as u16;\n        constraints.extend(right.constraints.iter().map(|(cols, c)| {\n            let cols = cols.iter().map(|col| ColId(col.0 + len_lhs)).collect::<ColList>();\n            (cols, *c)\n        }));\n\n        let mut fields = self.fields.clone();\n        fields.extend(right.fields.iter().cloned());\n\n        Self::new(self.table_id, self.table_name.clone(), fields, constraints)\n    }\n}\n\n/// Combine constraints.\n/// The result is a map from `ColList` to `Constraints`.\n/// In particular, it includes all indexes, and tells you which of them are unique.\n/// The result MAY contain multiple `ColList`s with the same columns in different orders.\n/// This corresponds to differently-ordered indices.\n///\n/// Unique constraints are considered logically unordered. Information from them will\n/// be propagated to all indices that contain the same columns.\npub fn combine_constraints(\n    uncombined: impl IntoIterator<Item = (ColList, Constraints)>,\n) -> BTreeMap<ColList, Constraints> {\n    let mut constraints = BTreeMap::new();\n    for (col_list, constraint) in uncombined {\n        let slot = constraints.entry(col_list).or_insert(Constraints::unset());\n        *slot = slot.push(constraint);\n    }\n\n    let mut uniques: HashSet<ColSet> = HashSet::default();\n    for (col_list, constraint) in &constraints {\n        if constraint.has_unique() {\n            uniques.insert(col_list.into());\n        }\n    }\n\n    for (cols, constraint) in constraints.iter_mut() {\n        if uniques.contains(&ColSet::from(cols)) {\n            *constraint = constraint.push(Constraints::unique());\n        }\n    }\n\n    constraints\n}\n\nimpl fmt::Display for Header {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[\")?;\n        for (pos, col) in self.fields.iter().enumerate() {\n            write!(\n                f,\n                \"{}: {}\",\n                col.field,\n                algebraic_type::fmt::fmt_algebraic_type(&col.algebraic_type)\n            )?;\n\n            if pos + 1 < self.fields.len() {\n                write!(f, \", \")?;\n            }\n        }\n        write!(f, \"]\")\n    }\n}\n\n#[derive(Debug, Clone, Eq, PartialEq, Hash)]\npub struct DbTable {\n    pub head: Arc<Header>,\n    pub table_id: TableId,\n    pub table_type: StTableType,\n    pub table_access: StAccess,\n}\n\nimpl DbTable {\n    pub fn new(head: Arc<Header>, table_id: TableId, table_type: StTableType, table_access: StAccess) -> Self {\n        Self {\n            head,\n            table_id,\n            table_type,\n            table_access,\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use spacetimedb_primitives::col_list;\n\n    /// Build a [Header] using the initial `start_pos` as the column position for the [Constraints]\n    fn head(id: impl Into<TableId>, name: &str, fields: (ColId, ColId), start_pos: u16) -> Header {\n        let pos_lhs = start_pos;\n        let pos_rhs = start_pos + 1;\n\n        let ct = vec![\n            (ColId(pos_lhs).into(), Constraints::indexed()),\n            (ColId(pos_rhs).into(), Constraints::identity()),\n            (col_list![pos_lhs, pos_rhs], Constraints::primary_key()),\n            (col_list![pos_rhs, pos_lhs], Constraints::unique()),\n        ];\n\n        let id = id.into();\n        let fields = [fields.0, fields.1].map(|col| Column::new(FieldName::new(id, col), AlgebraicType::I8));\n        Header::new(id, TableName::for_test(name), fields.into(), ct)\n    }\n\n    #[test]\n    fn test_project() {\n        let a = 0.into();\n        let b = 1.into();\n\n        let head = head(0, \"t1\", (a, b), 0);\n        let new = head.project(&[] as &[ColExpr]).unwrap();\n\n        let mut empty = head.clone_for_error();\n        empty.fields.clear();\n        empty.constraints.clear();\n        assert_eq!(empty, new);\n\n        let all = head.clone_for_error();\n        let new = head.project(&[a, b].map(ColExpr::Col)).unwrap();\n        assert_eq!(all, new);\n\n        let mut first = head.clone_for_error();\n        first.fields.pop();\n        first.constraints = first.retain_constraints(&a.into());\n        let new = head.project(&[a].map(ColExpr::Col)).unwrap();\n        assert_eq!(first, new);\n\n        let mut second = head.clone_for_error();\n        second.fields.remove(0);\n        second.constraints = second.retain_constraints(&b.into());\n        let new = head.project(&[b].map(ColExpr::Col)).unwrap();\n        assert_eq!(second, new);\n    }\n\n    #[test]\n    fn test_extend() {\n        let t1 = 0.into();\n        let t2: TableId = 1.into();\n        let a = 0.into();\n        let b = 1.into();\n        let c = 0.into();\n        let d = 1.into();\n\n        let head_lhs = head(t1, \"t1\", (a, b), 0);\n        let head_rhs = head(t2, \"t2\", (c, d), 0);\n\n        let new = head_lhs.extend(&head_rhs);\n\n        let lhs = new.project(&[a, b].map(ColExpr::Col)).unwrap();\n        assert_eq!(head_lhs, lhs);\n\n        let mut head_rhs = head(t2, \"t2\", (c, d), 2);\n        head_rhs.table_id = t1;\n        head_rhs.table_name = head_lhs.table_name.clone();\n        let rhs = new.project(&[2, 3].map(ColId).map(ColExpr::Col)).unwrap();\n        assert_eq!(head_rhs, rhs);\n    }\n\n    #[test]\n    fn test_combine_constraints() {\n        let raw = vec![\n            (col_list![0], Constraints::indexed()),\n            (col_list![0], Constraints::unique()),\n            (col_list![1], Constraints::identity()),\n            (col_list![1, 0], Constraints::primary_key()),\n            (col_list![0, 1], Constraints::unique()),\n            (col_list![2], Constraints::indexed()),\n            (col_list![3], Constraints::unique()),\n        ];\n        let expected = vec![\n            (col_list![0], Constraints::indexed().push(Constraints::unique())),\n            (col_list![1], Constraints::identity()),\n            (col_list![1, 0], Constraints::primary_key().push(Constraints::unique())),\n            (col_list![0, 1], Constraints::unique()),\n            (col_list![2], Constraints::indexed()),\n            (col_list![3], Constraints::unique()),\n        ]\n        .into_iter()\n        .collect::<BTreeMap<_, _>>();\n        assert_eq!(combine_constraints(raw), expected);\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/reserved_identifiers.txt",
    "content": ""
  },
  {
    "path": "crates/schema/src/schema.rs",
    "content": "//! Schema data structures.\n//! These are used at runtime by the vm to store the schema of the database.\n//! They are mirrored in the system tables -- see `spacetimedb_core::db::datastore::system_tables`.\n//! Types in this file are not public ABI or API and may be changed at any time; it's the system tables that cannot.\n\n// TODO(1.0): change all the `RawIdentifier`s in this file to `Identifier`.\n// This doesn't affect the ABI so can wait until 1.0.\n\nuse crate::def::error::{DefType, SchemaError};\nuse crate::relation::{combine_constraints, Column, DbTable, FieldName, Header};\nuse crate::table_name::TableName;\nuse core::mem;\nuse itertools::Itertools;\nuse spacetimedb_lib::db::auth::{StAccess, StTableType};\nuse spacetimedb_lib::db::raw_def::v9::RawSql;\nuse spacetimedb_lib::db::raw_def::{generate_cols_name, RawConstraintDefV8};\nuse spacetimedb_primitives::*;\nuse spacetimedb_sats::product_value::InvalidFieldError;\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, ProductType, ProductTypeElement, WithTypespace};\nuse std::collections::BTreeMap;\nuse std::sync::Arc;\n\nuse crate::def::{\n    ColumnDef, ConstraintData, ConstraintDef, IndexAlgorithm, IndexDef, ModuleDef, ModuleDefLookup,\n    RawModuleDefVersion, ScheduleDef, SequenceDef, TableDef, UniqueConstraintData, ViewColumnDef, ViewDef,\n};\nuse crate::identifier::Identifier;\n\n/// Helper trait documenting allowing schema entities to be built from a validated `ModuleDef`.\npub trait Schema: Sized {\n    /// The `Def` type corresponding to this schema type.\n    type Def: ModuleDefLookup;\n    /// The `Id` type corresponding to this schema type.\n    type Id;\n    /// The `Id` type corresponding to the parent of this schema type.\n    /// Set to `()` if there is no parent.\n    type ParentId;\n\n    /// Construct a schema entity from a validated `ModuleDef`.\n    /// Panics if `module_def` does not contain `def`.\n    ///\n    /// If this schema entity contains children (e.g. if it is a table schema), they should be constructed with\n    /// IDs set to `ChildId::SENTINEL`.\n    ///\n    /// If this schema entity contains `AlgebraicType`s, they should be fully resolved by this function (via\n    /// `WithTypespace::resolve_refs`). This means they will no longer contain references to any typespace (and be non-recursive).\n    /// This is necessary because the database does not currently attempt to handle typespaces / recursive types.\n    fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self;\n\n    /// Check that a schema entity is compatible with a definition.\n    fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error>;\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct ViewDefInfo {\n    pub view_id: ViewId,\n    pub has_args: bool,\n    pub is_anonymous: bool,\n}\n\nimpl ViewDefInfo {\n    pub fn num_private_cols(&self) -> usize {\n        (if self.is_anonymous { 0 } else { 1 }) + (if self.has_args { 1 } else { 0 })\n    }\n}\n\n/// A wrapper around a [`TableSchema`] for views.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct TableOrViewSchema {\n    pub table_id: TableId,\n    pub view_info: Option<ViewDefInfo>,\n    pub table_name: TableName,\n    pub table_access: StAccess,\n    inner: Arc<TableSchema>,\n}\n\nimpl From<Arc<TableSchema>> for TableOrViewSchema {\n    fn from(inner: Arc<TableSchema>) -> Self {\n        Self {\n            table_id: inner.table_id,\n            view_info: inner.view_info,\n            table_name: inner.table_name.clone(),\n            table_access: inner.table_access,\n            inner,\n        }\n    }\n}\n\nimpl TableOrViewSchema {\n    /// Is this schema that of a view?\n    pub fn is_view(&self) -> bool {\n        self.view_info.is_some()\n    }\n\n    /// Is this schema that of an anonymous view?\n    pub fn is_anonymous_view(&self) -> bool {\n        self.view_info.as_ref().is_some_and(|view_info| view_info.is_anonymous)\n    }\n\n    /// Returns the [`TableSchema`] of the underlying datastore table.\n    /// For views, this schema will include the internal `sender` and `arg_id` columns.\n    pub fn inner(&self) -> Arc<TableSchema> {\n        self.inner.clone()\n    }\n\n    /// Returns the public columns of this table.\n    ///\n    /// The [`ColId`]s in this list do not necessarily correspond to their position in this list.\n    /// Rather they correspond to the position of the column in the physical datastore table.\n    /// This is important since this method may not return all columns recorded in the datastore.\n    /// For views in particular it will not include the internal `sender` and `arg_id` columns.\n    /// Hence columns in this list should be looked up by their [`ColId`] - not their position.\n    pub fn public_columns(&self) -> &[ColumnSchema] {\n        match self.view_info {\n            Some(ViewDefInfo {\n                has_args: true,\n                is_anonymous: false,\n                ..\n            }) => &self.inner.columns[2..],\n            Some(ViewDefInfo {\n                has_args: true,\n                is_anonymous: true,\n                ..\n            }) => &self.inner.columns[1..],\n            Some(ViewDefInfo {\n                has_args: false,\n                is_anonymous: false,\n                ..\n            }) => &self.inner.columns[1..],\n            Some(ViewDefInfo {\n                has_args: false,\n                is_anonymous: true,\n                ..\n            })\n            | None => &self.inner.columns,\n        }\n    }\n\n    /// Check if the `col_name` exist on this [`TableOrViewSchema`]\n    pub fn get_column_by_name(&self, col_name: &str) -> Option<&ColumnSchema> {\n        self.public_columns().iter().find(|x| &*x.col_name == col_name)\n    }\n\n    /// Check if the `col_name` exists on this [`TableOrViewSchema`], prioritizing alias over canonical name.\n    pub fn get_column_by_name_or_alias(&self, col_name: &str) -> Option<&ColumnSchema> {\n        self.public_columns()\n            .iter()\n            .find(|col| col.alias.as_deref().is_some_and(|alias| alias == col_name))\n            .or_else(|| self.get_column_by_name(col_name))\n    }\n}\n\n/// A data structure representing the schema of a database table.\n///\n/// This struct holds information about the table, including its identifier,\n/// name, columns, indexes, constraints, sequences, type, and access rights.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct TableSchema {\n    /// The unique identifier of the table within the database.\n    pub table_id: TableId,\n\n    /// The name of the table.\n    pub table_name: TableName,\n\n    pub alias: Option<Identifier>,\n\n    /// Is this the backing table of a view?\n    pub view_info: Option<ViewDefInfo>,\n\n    /// The columns of the table.\n    /// The ordering of the columns is significant. Columns are frequently identified by `ColId`, that is, position in this list.\n    pub columns: Vec<ColumnSchema>,\n\n    /// The primary key of the table, if present. Must refer to a valid column.\n    ///\n    /// Currently, there must be a unique constraint and an index corresponding to the primary key.\n    /// Eventually, we may remove the requirement for an index.\n    ///\n    /// The database engine does not actually care about this, but client code generation does.\n    pub primary_key: Option<ColId>,\n\n    /// The indexes on the table.\n    pub indexes: Vec<IndexSchema>,\n\n    /// The constraints on the table.\n    pub constraints: Vec<ConstraintSchema>,\n\n    /// The sequences on the table.\n    pub sequences: Vec<SequenceSchema>,\n\n    /// Whether the table was created by a user or by the system.\n    pub table_type: StTableType,\n\n    /// The visibility of the table.\n    pub table_access: StAccess,\n\n    /// The schedule for the table, if present.\n    pub schedule: Option<ScheduleSchema>,\n\n    /// Whether this is an event table.\n    pub is_event: bool,\n\n    /// Cache for `row_type_for_table` in the data store.\n    pub row_type: ProductType,\n}\n\n/// Converts a list of columns to a table's row type.\npub fn columns_to_row_type(columns: &[ColumnSchema]) -> ProductType {\n    ProductType::new(columns.iter().map(ProductTypeElement::from).collect())\n}\n\nimpl TableSchema {\n    /// Create a table schema.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        table_id: TableId,\n        table_name: TableName,\n        view_info: Option<ViewDefInfo>,\n        columns: Vec<ColumnSchema>,\n        indexes: Vec<IndexSchema>,\n        constraints: Vec<ConstraintSchema>,\n        sequences: Vec<SequenceSchema>,\n        table_type: StTableType,\n        table_access: StAccess,\n        schedule: Option<ScheduleSchema>,\n        primary_key: Option<ColId>,\n        is_event: bool,\n        alias: Option<Identifier>,\n    ) -> Self {\n        Self {\n            row_type: columns_to_row_type(&columns),\n            table_id,\n            table_name,\n            view_info,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            table_type,\n            table_access,\n            schedule,\n            primary_key,\n            is_event,\n            alias,\n        }\n    }\n\n    /// Create a `TableSchema` corresponding to a product type.\n    /// For use in tests.\n    #[cfg(any(test, feature = \"test\"))]\n    pub fn from_product_type(ty: ProductType) -> TableSchema {\n        let columns = ty\n            .elements\n            .iter()\n            .enumerate()\n            .map(|(col_pos, element)| ColumnSchema {\n                table_id: TableId::SENTINEL,\n                col_pos: ColId(col_pos as _),\n                col_name: element\n                    .name\n                    .clone()\n                    .map(Identifier::new_assume_valid)\n                    .unwrap_or_else(|| Identifier::for_test(format!(\"col{col_pos}\"))),\n                col_type: element.algebraic_type.clone(),\n                alias: None,\n            })\n            .collect();\n\n        TableSchema::new(\n            TableId::SENTINEL,\n            TableName::for_test(\"TestTable\"),\n            None,\n            columns,\n            vec![],\n            vec![],\n            vec![],\n            StTableType::User,\n            StAccess::Public,\n            None,\n            None,\n            false,\n            None,\n        )\n    }\n\n    /// Is this the backing table for a view?\n    pub fn is_view(&self) -> bool {\n        self.view_info.is_some()\n    }\n\n    /// Is this the backing table for an anonymous view?\n    pub fn is_anonymous_view(&self) -> bool {\n        self.view_info.as_ref().is_some_and(|view_info| view_info.is_anonymous)\n    }\n\n    /// How many private columns does this table have?\n    /// Will only be non-zero in the case of views.\n    pub fn num_private_cols(&self) -> usize {\n        self.view_info\n            .as_ref()\n            .map(|view_info| view_info.num_private_cols())\n            .unwrap_or_default()\n    }\n\n    /// Update the table id of this schema.\n    /// For use by the core database engine after assigning a table id.\n    pub fn update_table_id(&mut self, id: TableId) {\n        self.table_id = id;\n        self.columns.iter_mut().for_each(|c| c.table_id = id);\n        self.indexes.iter_mut().for_each(|i| i.table_id = id);\n        self.constraints.iter_mut().for_each(|c| c.table_id = id);\n        self.sequences.iter_mut().for_each(|s| s.table_id = id);\n        if let Some(s) = self.schedule.as_mut() {\n            s.table_id = id;\n        }\n    }\n\n    /// Reset all the ids in this schema to sentinel values.\n    /// It is useful when cloning a schema to create a new table.\n    pub fn reset(&mut self) {\n        self.update_table_id(TableId::SENTINEL);\n        self.indexes.iter_mut().for_each(|i| i.index_id = IndexId::SENTINEL);\n        self.sequences\n            .iter_mut()\n            .for_each(|i| i.sequence_id = SequenceId::SENTINEL);\n        self.constraints\n            .iter_mut()\n            .for_each(|i| i.constraint_id = ConstraintId::SENTINEL);\n        self.row_type = columns_to_row_type(&self.columns);\n    }\n\n    /// Convert a table schema into a list of columns.\n    pub fn into_columns(self) -> Vec<ColumnSchema> {\n        self.columns\n    }\n\n    /// Get the columns of the table. Only immutable access to the columns is provided.\n    /// The ordering of the columns is significant. Columns are frequently identified by `ColId`, that is, position in this list.\n    pub fn columns(&self) -> &[ColumnSchema] {\n        &self.columns\n    }\n\n    /// How many columns does this table have?\n    pub fn num_cols(&self) -> usize {\n        self.columns.len()\n    }\n\n    /// Extracts all the [Self::indexes], [Self::sequences], and [Self::constraints].\n    pub fn take_adjacent_schemas(&mut self) -> (Vec<IndexSchema>, Vec<SequenceSchema>, Vec<ConstraintSchema>) {\n        (\n            mem::take(&mut self.indexes),\n            mem::take(&mut self.sequences),\n            mem::take(&mut self.constraints),\n        )\n    }\n\n    // Crud operation on adjacent schemas\n\n    /// Add OR replace the [SequenceSchema]\n    pub fn update_sequence(&mut self, of: SequenceSchema) {\n        if let Some(x) = self.sequences.iter_mut().find(|x| x.sequence_id == of.sequence_id) {\n            *x = of;\n        } else {\n            self.sequences.push(of);\n        }\n    }\n\n    /// Removes the given `sequence_id`\n    pub fn remove_sequence(&mut self, sequence_id: SequenceId) -> Option<SequenceSchema> {\n        find_remove(&mut self.sequences, |x| x.sequence_id == sequence_id)\n    }\n\n    /// Add OR replace the [IndexSchema]\n    pub fn update_index(&mut self, of: IndexSchema) {\n        if let Some(x) = self.indexes.iter_mut().find(|x| x.index_id == of.index_id) {\n            *x = of;\n        } else {\n            self.indexes.push(of);\n        }\n    }\n\n    /// Removes the given `index_id`\n    pub fn remove_index(&mut self, index_id: IndexId) -> Option<IndexSchema> {\n        find_remove(&mut self.indexes, |x| x.index_id == index_id)\n    }\n\n    /// Add OR replace the [ConstraintSchema]\n    pub fn update_constraint(&mut self, of: ConstraintSchema) {\n        if let Some(x) = self\n            .constraints\n            .iter_mut()\n            .find(|x| x.constraint_id == of.constraint_id)\n        {\n            *x = of;\n        } else {\n            self.constraints.push(of);\n        }\n    }\n\n    /// Removes the given `index_id`\n    pub fn remove_constraint(&mut self, constraint_id: ConstraintId) -> Option<ConstraintSchema> {\n        find_remove(&mut self.constraints, |x| x.constraint_id == constraint_id)\n    }\n\n    /// Concatenate the column names from the `columns`\n    ///\n    /// WARNING: If the `ColId` not exist, is skipped.\n    /// TODO(Tyler): This should return an error and not allow this to be constructed\n    /// if there is an invalid `ColId`\n    pub fn generate_cols_name(&self, columns: &ColList) -> String {\n        generate_cols_name(columns, |p| self.get_column(p.idx()).map(|c| &*c.col_name))\n    }\n\n    /// Check if the specified `field` exists in this [TableSchema].\n    ///\n    /// # Warning\n    ///\n    /// This function ignores the `table_id` when searching for a column.\n    pub fn get_column_by_field(&self, field: FieldName) -> Option<&ColumnSchema> {\n        self.get_column(field.col.idx())\n    }\n\n    /// Look up a list of columns by their positions in the table.\n    /// Invalid column positions are permitted.\n    pub fn get_columns<'a>(\n        &'a self,\n        columns: &'a ColList,\n    ) -> impl 'a + Iterator<Item = (ColId, Option<&'a ColumnSchema>)> {\n        columns.iter().map(|col| (col, self.columns.get(col.idx())))\n    }\n\n    /// Get a reference to a column by its position (`pos`) in the table.\n    pub fn get_column(&self, pos: usize) -> Option<&ColumnSchema> {\n        self.columns.get(pos)\n    }\n\n    /// Check if the `col_name` exist on this [TableSchema]\n    pub fn get_column_by_name(&self, col_name: &str) -> Option<&ColumnSchema> {\n        self.columns.iter().find(|x| &*x.col_name == col_name)\n    }\n\n    /// Check if the `col_name` exists on this [TableSchema], prioritizing alias over canonical name.\n    pub fn get_column_by_name_or_alias(&self, col_name: &str) -> Option<&ColumnSchema> {\n        self.columns\n            .iter()\n            .find(|col| col.alias.as_deref().is_some_and(|alias| alias == col_name))\n            .or_else(|| self.get_column_by_name(col_name))\n    }\n\n    /// Check if the `col_name` exist on this [TableSchema]\n    ///\n    /// Warning: It ignores the `table_name`\n    pub fn get_column_id_by_name(&self, col_name: &str) -> Option<ColId> {\n        self.columns\n            .iter()\n            .position(|x| &*x.col_name == col_name)\n            .map(|x| x.into())\n    }\n\n    /// Check if the `col_name` exists on this [TableSchema], prioritizing alias over canonical name.\n    ///\n    /// Warning: It ignores the `table_name`.\n    pub fn get_column_id_by_name_or_alias(&self, col_name: &str) -> Option<ColId> {\n        self.columns\n            .iter()\n            .position(|col| col.alias.as_deref().is_some_and(|alias| alias == col_name))\n            .or_else(|| self.get_column_id_by_name(col_name).map(|id| id.idx()))\n            .map(Into::into)\n    }\n\n    /// Check whether `name` matches table alias or canonical table name.\n    pub fn matches_name_or_alias(&self, name: &str) -> bool {\n        self.alias.as_deref().is_some_and(|alias| alias == name) || self.table_name.as_ref() == name\n    }\n\n    /// Retrieve the column ids for this index id\n    pub fn col_list_for_index_id(&self, index_id: IndexId) -> ColList {\n        self.indexes\n            .iter()\n            .find(|schema| schema.index_id == index_id)\n            .map(|schema| schema.index_algorithm.columns())\n            .map(|cols| ColList::from_iter(cols.iter()))\n            .unwrap_or_else(ColList::empty)\n    }\n\n    /// Is there a unique constraint for this set of columns?\n    pub fn is_unique(&self, cols: &impl PartialEq<ColList>) -> bool {\n        self.constraints\n            .iter()\n            .filter_map(|cs| cs.data.unique_columns())\n            .any(|unique_cols| *cols == **unique_cols)\n    }\n\n    /// Project the fields from the supplied `indexes`.\n    pub fn project(&self, indexes: impl Iterator<Item = ColId>) -> Result<Vec<&ColumnSchema>, InvalidFieldError> {\n        indexes\n            .map(|index| self.get_column(index.0 as usize).ok_or_else(|| index.into()))\n            .collect()\n    }\n\n    /// Utility for project the fields from the supplied `indexes` that is a [ColList],\n    /// used for when the list of field indexes have at least one value.\n    pub fn project_not_empty(&self, indexes: ColList) -> Result<Vec<&ColumnSchema>, InvalidFieldError> {\n        self.project(indexes.iter())\n    }\n\n    /// IMPORTANT: Is required to have this cached to avoid a perf drop on datastore operations\n    pub fn get_row_type(&self) -> &ProductType {\n        &self.row_type\n    }\n\n    /// Utility to avoid cloning in `row_type_for_table`\n    pub fn into_row_type(self) -> ProductType {\n        self.row_type\n    }\n\n    /// Iterate over the constraints on sets of columns on this table.\n    fn backcompat_constraints_iter(&self) -> impl Iterator<Item = (ColList, Constraints)> + '_ {\n        self.constraints\n            .iter()\n            .map(|x| -> (ColList, Constraints) {\n                match &x.data {\n                    ConstraintData::Unique(unique) => (unique.columns.clone().into(), Constraints::unique()),\n                }\n            })\n            .chain(self.indexes.iter().map(|x| match &x.index_algorithm {\n                IndexAlgorithm::BTree(btree) => (btree.columns.clone(), Constraints::indexed()),\n                IndexAlgorithm::Hash(hash) => (hash.columns.clone(), Constraints::indexed()),\n                IndexAlgorithm::Direct(direct) => (direct.column.into(), Constraints::indexed()),\n            }))\n            .chain(\n                self.sequences\n                    .iter()\n                    .map(|x| (col_list![x.col_pos], Constraints::auto_inc())),\n            )\n            .chain(\n                self.primary_key\n                    .iter()\n                    .map(|x| (col_list![*x], Constraints::primary_key())),\n            )\n    }\n\n    /// Get backwards-compatible constraints for this table.\n    ///\n    /// This is closer to how `TableSchema` used to work.\n    pub fn backcompat_constraints(&self) -> BTreeMap<ColList, Constraints> {\n        combine_constraints(self.backcompat_constraints_iter())\n    }\n\n    /// Get backwards-compatible constraints for this table.\n    ///\n    /// Resolves the constraints per each column. If the column don't have one, auto-generate [Constraints::unset()].\n    /// This guarantee all columns can be queried for it constraints.\n    pub fn backcompat_column_constraints(&self) -> BTreeMap<ColList, Constraints> {\n        let mut result = self.backcompat_constraints();\n        for col in &self.columns {\n            result.entry(col_list![col.col_pos]).or_insert(Constraints::unset());\n        }\n        result\n    }\n\n    /// Get the column corresponding to the primary key, if any.\n    pub fn pk(&self) -> Option<&ColumnSchema> {\n        self.primary_key.and_then(|pk| self.get_column(pk.0 as usize))\n    }\n\n    /// Verify the definitions of this schema are valid:\n    /// - Check all names are not empty\n    /// - All columns exists\n    /// - Only 1 PK\n    /// - Only 1 sequence per column\n    /// - Only Btree Indexes\n    ///\n    /// Deprecated. This will eventually be replaced by the `schema` crate.\n    pub fn validated(self) -> Result<Self, Vec<SchemaError>> {\n        let mut errors = Vec::new();\n\n        let columns_not_found = self\n            .sequences\n            .iter()\n            .map(|x| (DefType::Sequence, x.sequence_name.clone(), ColList::new(x.col_pos)))\n            .chain(self.indexes.iter().map(|x| {\n                let cols = x.index_algorithm.columns().to_owned();\n                (DefType::Index, x.index_name.clone(), cols)\n            }))\n            .chain(self.constraints.iter().map(|x| {\n                (\n                    DefType::Constraint,\n                    x.constraint_name.clone(),\n                    match &x.data {\n                        ConstraintData::Unique(unique) => unique.columns.clone().into(),\n                    },\n                )\n            }))\n            .filter_map(|(ty, name, cols)| {\n                let mut not_found_iter = self\n                    .get_columns(&cols)\n                    .filter(|(_, x)| x.is_none())\n                    .map(|(col, _)| col)\n                    .peekable();\n\n                if not_found_iter.peek().is_none() {\n                    None\n                } else {\n                    Some(SchemaError::ColumnsNotFound {\n                        name,\n                        table: self.table_name.clone(),\n                        columns: not_found_iter.collect(),\n                        ty,\n                    })\n                }\n            });\n\n        errors.extend(columns_not_found);\n\n        errors.extend(self.columns.iter().filter_map(|x| {\n            if x.col_name.is_empty() {\n                Some(SchemaError::EmptyName {\n                    table: self.table_name.clone(),\n                    ty: DefType::Column,\n                    id: x.col_pos.0 as _,\n                })\n            } else {\n                None\n            }\n        }));\n\n        errors.extend(self.indexes.iter().filter_map(|x| {\n            if x.index_name.is_empty() {\n                Some(SchemaError::EmptyName {\n                    table: self.table_name.clone(),\n                    ty: DefType::Index,\n                    id: x.index_id.0,\n                })\n            } else {\n                None\n            }\n        }));\n        errors.extend(self.constraints.iter().filter_map(|x| {\n            if x.constraint_name.is_empty() {\n                Some(SchemaError::EmptyName {\n                    table: self.table_name.clone(),\n                    ty: DefType::Constraint,\n                    id: x.constraint_id.0,\n                })\n            } else {\n                None\n            }\n        }));\n\n        errors.extend(self.sequences.iter().filter_map(|x| {\n            if x.sequence_name.is_empty() {\n                Some(SchemaError::EmptyName {\n                    table: self.table_name.clone(),\n                    ty: DefType::Sequence,\n                    id: x.sequence_id.0,\n                })\n            } else {\n                None\n            }\n        }));\n\n        // Verify we don't have more than 1 auto_inc for the same column\n        if let Some(err) = self\n            .sequences\n            .iter()\n            .group_by(|&seq| seq.col_pos)\n            .into_iter()\n            .find_map(|(key, group)| {\n                let count = group.count();\n                if count > 1 {\n                    Some(SchemaError::OneAutoInc {\n                        table: self.table_name.clone(),\n                        field: self.columns[key.idx()].col_name.clone(),\n                    })\n                } else {\n                    None\n                }\n            })\n        {\n            errors.push(err);\n        }\n\n        if errors.is_empty() {\n            Ok(self)\n        } else {\n            Err(errors)\n        }\n    }\n\n    /// The C# and Rust SDKs are inconsistent about whether v8 column defs store resolved or unresolved algebraic types.\n    /// This method works around this problem by copying the column types from the module def into the table schema.\n    /// It can be removed once v8 is removed, since v9 will reject modules with an inconsistency like this.\n    pub fn janky_fix_column_defs(&mut self, module_def: &ModuleDef) {\n        let table_name = self.table_name.clone().into();\n        for col in &mut self.columns {\n            let def: &ColumnDef = module_def.lookup((&table_name, &col.col_name)).unwrap();\n            col.col_type = def.ty.clone();\n        }\n        let table_def: &TableDef = module_def.expect_lookup(&table_name);\n        self.row_type = module_def.typespace()[table_def.product_type_ref]\n            .as_product()\n            .unwrap()\n            .clone();\n    }\n\n    /// Normalize a `TableSchema`.\n    /// The result is semantically equivalent, but may have reordered indexes, constraints, or sequences.\n    /// Columns will not be reordered.\n    pub fn normalize(&mut self) {\n        self.indexes.sort_by(|a, b| a.index_name.cmp(&b.index_name));\n        self.constraints\n            .sort_by(|a, b| a.constraint_name.cmp(&b.constraint_name));\n        self.sequences.sort_by(|a, b| a.sequence_name.cmp(&b.sequence_name));\n    }\n}\n\n/// Removes and returns the first element satisfying `predicate` in `vec`.\nfn find_remove<T>(vec: &mut Vec<T>, predicate: impl Fn(&T) -> bool) -> Option<T> {\n    let pos = vec.iter().position(predicate)?;\n    Some(vec.remove(pos))\n}\n\n/// Like `assert_eq!` for `anyhow`, but `$msg` is just a string, not a format string.\nmacro_rules! ensure_eq {\n    ($a:expr, $b:expr, $msg:expr) => {\n        if $a != $b {\n            anyhow::bail!(\n                \"{0}: expected {1} == {2}:\\n   {1}: {3:?}\\n   {2}: {4:?}\",\n                $msg,\n                stringify!($a),\n                stringify!($b),\n                $a,\n                $b\n            );\n        }\n    };\n}\n\n/// Returns the list of [`ColumnSchema`]s for a certain list of [`ColumnDef`]s.\npub fn column_schemas_from_defs(module_def: &ModuleDef, columns: &[ColumnDef], table_id: TableId) -> Vec<ColumnSchema> {\n    columns\n        .iter()\n        .enumerate()\n        .map(|(col_pos, def)| ColumnSchema::from_module_def(module_def, def, (), (table_id, col_pos.into())))\n        .collect()\n}\n\nimpl TableSchema {\n    /// Generates a [`TableSchema`] for the purpose of client codegen.\n    ///\n    /// This is the schema defined in the module.\n    /// It does not have any internal columns like the schema for the datastore.\n    /// See [`Self::from_view_def_for_datastore`] for more details.\n    pub fn from_view_def_for_codegen(module_def: &ModuleDef, view_def: &ViewDef) -> Self {\n        module_def.expect_contains(view_def);\n\n        let ViewDef {\n            name,\n            is_public,\n            is_anonymous,\n            primary_key,\n            param_columns,\n            return_columns,\n            ..\n        } = view_def;\n\n        let columns = return_columns\n            .iter()\n            .map(|def| ColumnSchema::from_view_column_def(module_def, def))\n            .enumerate()\n            .map(|(i, schema)| (ColId::from(i), schema))\n            .map(|(col_pos, schema)| ColumnSchema { col_pos, ..schema })\n            .collect();\n        let view_primary_key = (module_def.raw_module_def_version() == RawModuleDefVersion::V10)\n            .then_some(*primary_key)\n            .flatten();\n\n        let table_access = if *is_public {\n            StAccess::Public\n        } else {\n            StAccess::Private\n        };\n\n        let view_info = ViewDefInfo {\n            view_id: ViewId::SENTINEL,\n            has_args: !param_columns.is_empty(),\n            is_anonymous: *is_anonymous,\n        };\n\n        TableSchema::new(\n            TableId::SENTINEL,\n            TableName::new(name.clone()),\n            Some(view_info),\n            columns,\n            vec![],\n            vec![],\n            vec![],\n            StTableType::User,\n            table_access,\n            None,\n            view_primary_key,\n            false,\n            None,\n        )\n    }\n\n    /// Generate a [`TableSchema`] for the purpose of materializing in the datastore.\n    ///\n    /// Note, every view is materialized by default. For example:\n    /// ```rust,ignore\n    /// #[table]\n    /// pub struct MyTable {\n    ///     a: u32,\n    ///     b: u32,\n    /// }\n    ///\n    /// #[view(accessor = my_view, public)]\n    /// fn my_view(ctx: &ViewContext, x: u32, y: u32) -> Vec<MyTable> { ... }\n    ///\n    /// #[view(accessor = my_anonymous_view, public)]\n    /// fn my_anonymous_view(ctx: &AnonymousViewContext, x: u32, y: u32) -> Vec<MyTable> { ... }\n    /// ```\n    ///\n    /// The above views are materialized with the following schema:\n    ///\n    /// my_view:\n    ///\n    /// | sender         | arg_id | a   | b   |\n    /// |----------------|--------|-----|-----|\n    /// | (some = 0x...) | u64    | u32 | u32 |\n    ///\n    /// my_anonymous_view:\n    ///\n    /// | sender      | arg_id | a   | b   |\n    /// |-------------|--------|-----|-----|\n    /// | (none = ()) | u64    | u32 | u32 |\n    ///\n    /// Note, `sender` and `arg_id` are internal columns not defined by the module,\n    /// where `arg_id` is a foreign key into `st_view_arg`.\n    pub fn from_view_def_for_datastore(module_def: &ModuleDef, view_def: &ViewDef) -> Self {\n        module_def.expect_contains(view_def);\n\n        let ViewDef {\n            name,\n            is_public,\n            is_anonymous,\n            primary_key,\n            param_columns,\n            return_columns,\n            accessor_name,\n            ..\n        } = view_def;\n\n        let n = return_columns.len() + 2;\n        let mut columns = Vec::with_capacity(n);\n        let mut meta_cols = 0;\n\n        let mut push_column = |name: &'static str, col_type| {\n            meta_cols += 1;\n            columns.push(ColumnSchema {\n                table_id: TableId::SENTINEL,\n                col_pos: columns.len().into(),\n                col_name: Identifier::new_assume_valid(name.into()),\n                col_type,\n                alias: None,\n            });\n        };\n\n        if !is_anonymous {\n            push_column(\"sender\", AlgebraicType::identity());\n        }\n\n        if !param_columns.is_empty() {\n            push_column(\"arg_id\", AlgebraicType::U64);\n        }\n\n        columns.extend(\n            return_columns\n                .iter()\n                .map(|def| ColumnSchema::from_view_column_def(module_def, def))\n                .enumerate()\n                .map(|(i, schema)| (ColId::from(meta_cols + i), schema))\n                .map(|(col_pos, schema)| ColumnSchema { col_pos, ..schema }),\n        );\n\n        let make_index_name = |col_list: &ColList| {\n            let cols_name = generate_cols_name(col_list, |col| columns.get(col.idx()).map(|col| &*col.col_name));\n            RawIdentifier::new(format!(\"{name}_{cols_name}_idx_btree\"))\n        };\n\n        let make_constraint_name = |col_list: &ColList| {\n            let cols_name = generate_cols_name(col_list, |col| columns.get(col.idx()).map(|col| &*col.col_name));\n            RawIdentifier::new(format!(\"{name}_{cols_name}_key\"))\n        };\n\n        let mut indexes = match meta_cols {\n            1 => vec![IndexSchema {\n                index_id: IndexId::SENTINEL,\n                table_id: TableId::SENTINEL,\n                index_name: make_index_name(&col_list![0]),\n                index_algorithm: IndexAlgorithm::BTree(col_list![0].into()),\n                alias: None,\n            }],\n            2 => vec![IndexSchema {\n                index_id: IndexId::SENTINEL,\n                table_id: TableId::SENTINEL,\n                index_name: make_index_name(&col_list![0, 1]),\n                index_algorithm: IndexAlgorithm::BTree(col_list![0, 1].into()),\n                alias: None,\n            }],\n            _ => vec![],\n        };\n\n        let mut constraints = vec![];\n        let view_primary_key = (module_def.raw_module_def_version() == RawModuleDefVersion::V10)\n            .then_some(primary_key.map(|pk| ColId::from(meta_cols + pk.idx())))\n            .flatten();\n\n        if *is_anonymous {\n            if let Some(pk_col) = view_primary_key {\n                let cols = col_list![pk_col];\n                constraints.push(ConstraintSchema {\n                    table_id: TableId::SENTINEL,\n                    constraint_id: ConstraintId::SENTINEL,\n                    constraint_name: make_constraint_name(&cols),\n                    data: ConstraintData::Unique(UniqueConstraintData {\n                        columns: ColSet::from(cols.clone()),\n                    }),\n                });\n                indexes.push(IndexSchema {\n                    index_id: IndexId::SENTINEL,\n                    table_id: TableId::SENTINEL,\n                    index_name: make_index_name(&cols),\n                    index_algorithm: IndexAlgorithm::BTree(cols.into()),\n                    alias: None,\n                });\n            }\n        } else if let Some(pk_col) = view_primary_key {\n            let cols = col_list![ColId(0), pk_col];\n            indexes.push(IndexSchema {\n                index_id: IndexId::SENTINEL,\n                table_id: TableId::SENTINEL,\n                index_name: make_index_name(&cols),\n                index_algorithm: IndexAlgorithm::BTree(cols.into()),\n                alias: None,\n            });\n        }\n\n        let table_access = if *is_public {\n            StAccess::Public\n        } else {\n            StAccess::Private\n        };\n\n        let view_info = ViewDefInfo {\n            view_id: ViewId::SENTINEL,\n            has_args: !param_columns.is_empty(),\n            is_anonymous: *is_anonymous,\n        };\n\n        TableSchema::new(\n            TableId::SENTINEL,\n            TableName::new(name.clone()),\n            Some(view_info),\n            columns,\n            indexes,\n            constraints,\n            vec![],\n            StTableType::User,\n            table_access,\n            None,\n            if *is_anonymous { view_primary_key } else { None },\n            false,\n            Some(accessor_name.clone()),\n        )\n    }\n}\n\nimpl Schema for TableSchema {\n    type Def = TableDef;\n    type Id = TableId;\n    type ParentId = ();\n\n    // N.B. This implementation gives all children ID 0 (the auto-inc sentinel value.)\n    fn from_module_def(\n        module_def: &ModuleDef,\n        def: &Self::Def,\n        _parent_id: Self::ParentId,\n        table_id: Self::Id,\n    ) -> Self {\n        module_def.expect_contains(def);\n\n        let TableDef {\n            name,\n            product_type_ref: _,\n            primary_key,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            schedule,\n            table_type,\n            table_access,\n            is_event,\n            accessor_name,\n            ..\n        } = def;\n\n        let columns = column_schemas_from_defs(module_def, columns, table_id);\n\n        // note: these Ids are fixed up somewhere else, so we can just use 0 here...\n        // but it would be nice to pass the correct values into this method.\n        let indexes = indexes\n            .values()\n            .map(|def| IndexSchema::from_module_def(module_def, def, table_id, IndexId::SENTINEL))\n            .collect();\n\n        let sequences = sequences\n            .values()\n            .map(|def| SequenceSchema::from_module_def(module_def, def, table_id, SequenceId::SENTINEL))\n            .collect();\n\n        let constraints = constraints\n            .values()\n            .map(|def| ConstraintSchema::from_module_def(module_def, def, table_id, ConstraintId::SENTINEL))\n            .collect();\n\n        let schedule = schedule\n            .as_ref()\n            .map(|schedule| ScheduleSchema::from_module_def(module_def, schedule, table_id, ScheduleId::SENTINEL));\n\n        TableSchema::new(\n            table_id,\n            TableName::new(name.clone()),\n            None,\n            columns,\n            indexes,\n            constraints,\n            sequences,\n            (*table_type).into(),\n            (*table_access).into(),\n            schedule,\n            *primary_key,\n            *is_event,\n            Some(accessor_name.clone()),\n        )\n    }\n\n    fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.table_name[..], &def.name[..], \"Table name mismatch\");\n        ensure_eq!(self.primary_key, def.primary_key, \"Primary key mismatch\");\n        let def_table_access: StAccess = (def.table_access).into();\n        ensure_eq!(self.table_access, def_table_access, \"Table access mismatch\");\n        let def_table_type: StTableType = (def.table_type).into();\n        ensure_eq!(self.table_type, def_table_type, \"Table type mismatch\");\n\n        for col in &self.columns {\n            let col_def = def\n                .columns\n                .get(col.col_pos.0 as usize)\n                .ok_or_else(|| anyhow::anyhow!(\"Column {} not found in definition\", col.col_pos.0))?;\n            col.check_compatible(module_def, col_def)?;\n        }\n        ensure_eq!(self.columns.len(), def.columns.len(), \"Column count mismatch\");\n\n        for index in &self.indexes {\n            let index_def = def\n                .indexes\n                .get(&index.index_name)\n                .ok_or_else(|| anyhow::anyhow!(\"Index {} not found in definition\", index.index_id.0))?;\n            index.check_compatible(module_def, index_def)?;\n        }\n        ensure_eq!(self.indexes.len(), def.indexes.len(), \"Index count mismatch\");\n\n        for constraint in &self.constraints {\n            let constraint_def = def\n                .constraints\n                .get(&constraint.constraint_name)\n                .ok_or_else(|| anyhow::anyhow!(\"Constraint {} not found in definition\", constraint.constraint_id.0))?;\n            constraint.check_compatible(module_def, constraint_def)?;\n        }\n        ensure_eq!(\n            self.constraints.len(),\n            def.constraints.len(),\n            \"Constraint count mismatch\"\n        );\n\n        for sequence in &self.sequences {\n            let sequence_def = def\n                .sequences\n                .get(&sequence.sequence_name)\n                .ok_or_else(|| anyhow::anyhow!(\"Sequence {} not found in definition\", sequence.sequence_id.0))?;\n            sequence.check_compatible(module_def, sequence_def)?;\n        }\n        ensure_eq!(self.sequences.len(), def.sequences.len(), \"Sequence count mismatch\");\n\n        if let Some(schedule) = &self.schedule {\n            let schedule_def = def\n                .schedule\n                .as_ref()\n                .ok_or_else(|| anyhow::anyhow!(\"Schedule not found in definition\"))?;\n            schedule.check_compatible(module_def, schedule_def)?;\n        }\n        ensure_eq!(\n            self.schedule.is_some(),\n            def.schedule.is_some(),\n            \"Schedule presence mismatch\"\n        );\n        Ok(())\n    }\n}\n\nimpl From<&TableSchema> for ProductType {\n    fn from(value: &TableSchema) -> Self {\n        value.row_type.clone()\n    }\n}\n\nimpl From<&TableSchema> for DbTable {\n    fn from(value: &TableSchema) -> Self {\n        DbTable::new(\n            Arc::new(value.into()),\n            value.table_id,\n            value.table_type,\n            value.table_access,\n        )\n    }\n}\n\nimpl From<&TableSchema> for Header {\n    fn from(value: &TableSchema) -> Self {\n        let fields = value\n            .columns\n            .iter()\n            .map(|x| Column::new(FieldName::new(value.table_id, x.col_pos), x.col_type.clone()))\n            .collect();\n\n        Header::new(\n            value.table_id,\n            value.table_name.clone(),\n            fields,\n            value.backcompat_constraints(),\n        )\n    }\n}\n\nimpl From<TableSchema> for Header {\n    fn from(schema: TableSchema) -> Self {\n        // TODO: optimize.\n        Header::from(&schema)\n    }\n}\n\n/// A struct representing the schema of a database column.\n#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]\npub struct ColumnSchema {\n    /// The ID of the table this column is attached to.\n    pub table_id: TableId,\n    /// The position of the column within the table.\n    pub col_pos: ColId,\n    /// The name of the column. Unique within the table.\n    pub col_name: Identifier,\n\n    pub alias: Option<Identifier>,\n    /// The type of the column. This will never contain any `AlgebraicTypeRef`s,\n    /// that is, it will be resolved.\n    pub col_type: AlgebraicType,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for ColumnSchema {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            table_id,\n            col_pos,\n            col_name,\n            col_type,\n            ..\n        } = self;\n        table_id.heap_usage() + col_pos.heap_usage() + col_name.heap_usage() + col_type.heap_usage()\n    }\n}\n\nimpl ColumnSchema {\n    #[cfg(any(test, feature = \"test\"))]\n    pub fn for_test(pos: impl Into<ColId>, name: impl AsRef<str>, ty: AlgebraicType) -> Self {\n        Self {\n            table_id: TableId::SENTINEL,\n            col_pos: pos.into(),\n            col_name: Identifier::for_test(name),\n            col_type: ty,\n            alias: None,\n        }\n    }\n\n    fn from_view_column_def(module_def: &ModuleDef, def: &ViewColumnDef) -> Self {\n        let col_type = WithTypespace::new(module_def.typespace(), &def.ty)\n            .resolve_refs()\n            .expect(\"validated module should have all types resolve\");\n        ColumnSchema {\n            table_id: TableId::SENTINEL,\n            col_pos: def.col_id,\n            col_name: def.name.clone(),\n            col_type,\n            alias: Some(def.accessor_name.clone()),\n        }\n    }\n}\n\nimpl Schema for ColumnSchema {\n    type Def = ColumnDef;\n    type ParentId = ();\n    // This is not like the other ID types: it's a tuple of the table ID and the column position.\n    // A `ColId` alone does NOT suffice to identify a column!\n    type Id = (TableId, ColId);\n\n    fn from_module_def(\n        module_def: &ModuleDef,\n        def: &ColumnDef,\n        _parent_id: (),\n        (table_id, col_pos): (TableId, ColId),\n    ) -> Self {\n        let col_type = WithTypespace::new(module_def.typespace(), &def.ty)\n            .resolve_refs()\n            .expect(\"validated module should have all types resolve\");\n        ColumnSchema {\n            table_id,\n            col_pos,\n            col_name: def.name.clone(),\n            col_type,\n            alias: Some(def.accessor_name.clone()),\n        }\n    }\n\n    fn check_compatible(&self, module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.col_name[..], &def.name[..], \"Column name mismatch\");\n        let resolved_def_ty = WithTypespace::new(module_def.typespace(), &def.ty).resolve_refs()?;\n        ensure_eq!(self.col_type, resolved_def_ty, \"Column type mismatch\");\n        ensure_eq!(self.col_pos, def.col_id, \"Column ID mismatch\");\n        Ok(())\n    }\n}\n\nimpl From<&ColumnSchema> for ProductTypeElement {\n    fn from(value: &ColumnSchema) -> Self {\n        Self {\n            name: Some(value.col_name.clone().into()),\n            algebraic_type: value.col_type.clone(),\n        }\n    }\n}\n\nimpl From<ColumnSchema> for Column {\n    fn from(schema: ColumnSchema) -> Self {\n        Column {\n            field: FieldName {\n                table: schema.table_id,\n                col: schema.col_pos,\n            },\n            algebraic_type: schema.col_type,\n        }\n    }\n}\n\n/// Contextualizes a reference to a [ColumnSchema] with the name of the table the column is attached to.\n#[derive(Debug, Clone)]\npub struct ColumnSchemaRef<'a> {\n    /// The column we are referring to.\n    pub column: &'a ColumnSchema,\n    /// The name of the table the column is attached to.\n    pub table_name: &'a RawIdentifier,\n}\n\nimpl From<ColumnSchemaRef<'_>> for ProductTypeElement {\n    fn from(value: ColumnSchemaRef) -> Self {\n        ProductTypeElement::new(\n            value.column.col_type.clone(),\n            Some(value.column.col_name.clone().into()),\n        )\n    }\n}\n\n/// Represents a schema definition for a database sequence.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct SequenceSchema {\n    /// The unique identifier for the sequence within a database.\n    pub sequence_id: SequenceId,\n    /// The name of the sequence.\n    /// Deprecated. In the future, sequences will be identified by col_pos.\n    pub sequence_name: RawIdentifier,\n    /// The ID of the table associated with the sequence.\n    pub table_id: TableId,\n    /// The position of the column associated with this sequence.\n    pub col_pos: ColId,\n    /// The increment value for the sequence.\n    pub increment: i128,\n    /// The initial value to be returned by this sequence.\n    pub start: i128,\n    /// The minimum value for the sequence.\n    pub min_value: i128,\n    /// The maximum value for the sequence.\n    pub max_value: i128,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for SequenceSchema {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            sequence_id,\n            sequence_name,\n            table_id,\n            col_pos,\n            increment,\n            start,\n            min_value,\n            max_value,\n        } = self;\n        sequence_id.heap_usage()\n            + sequence_name.heap_usage()\n            + table_id.heap_usage()\n            + col_pos.heap_usage()\n            + increment.heap_usage()\n            + start.heap_usage()\n            + min_value.heap_usage()\n            + max_value.heap_usage()\n    }\n}\n\nimpl Schema for SequenceSchema {\n    type Def = SequenceDef;\n    type Id = SequenceId;\n    type ParentId = TableId;\n\n    fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {\n        module_def.expect_contains(def);\n\n        SequenceSchema {\n            sequence_id: id,\n            sequence_name: def.name.clone(),\n            table_id: parent_id,\n            col_pos: def.column,\n            increment: def.increment,\n            start: def.start.unwrap_or(1),\n            min_value: def.min_value.unwrap_or(1),\n            max_value: def.max_value.unwrap_or(i128::MAX),\n            // allocated: 0, // TODO: information not available in the `Def`s anymore, which is correct, but this may need to be overridden later.\n        }\n    }\n\n    fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.sequence_name[..], &def.name[..], \"Sequence name mismatch\");\n        ensure_eq!(self.col_pos, def.column, \"Sequence column mismatch\");\n        ensure_eq!(self.increment, def.increment, \"Sequence increment mismatch\");\n        if let Some(start) = &def.start {\n            ensure_eq!(self.start, *start, \"Sequence start mismatch\");\n        }\n        if let Some(min_value) = &def.min_value {\n            ensure_eq!(self.min_value, *min_value, \"Sequence min_value mismatch\");\n        }\n        if let Some(max_value) = &def.max_value {\n            ensure_eq!(self.max_value, *max_value, \"Sequence max_value mismatch\");\n        }\n        Ok(())\n    }\n}\n\n/// Marks a table as a timer table for a scheduled reducer.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ScheduleSchema {\n    /// The identifier of the table.\n    pub table_id: TableId,\n\n    /// The identifier of the schedule.\n    pub schedule_id: ScheduleId,\n\n    /// The name of the schedule.\n    pub schedule_name: Identifier,\n\n    /// The name of the reducer or procedure to call.\n    pub function_name: Identifier,\n\n    /// The column containing the `ScheduleAt` enum.\n    pub at_column: ColId,\n}\n\nimpl ScheduleSchema {\n    #[cfg(any(test, feature = \"test\"))]\n    pub fn for_test(name: impl AsRef<str>, function: impl AsRef<str>, at: impl Into<ColId>) -> Self {\n        Self {\n            table_id: TableId::SENTINEL,\n            schedule_id: ScheduleId::SENTINEL,\n            schedule_name: Identifier::for_test(name.as_ref()),\n            function_name: Identifier::for_test(function.as_ref()),\n            at_column: at.into(),\n        }\n    }\n}\n\nimpl Schema for ScheduleSchema {\n    type Def = ScheduleDef;\n\n    type Id = ScheduleId;\n\n    type ParentId = TableId;\n\n    fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {\n        module_def.expect_contains(def);\n\n        ScheduleSchema {\n            table_id: parent_id,\n            schedule_id: id,\n            schedule_name: def.name.clone(),\n            function_name: def.function_name.clone(),\n            at_column: def.at_column,\n            // Ignore def.at_column and id_column. Those are recovered at runtime.\n        }\n    }\n\n    fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.schedule_name[..], &def.name[..], \"Schedule name mismatch\");\n        ensure_eq!(\n            &self.function_name[..],\n            &def.function_name[..],\n            \"Schedule function name mismatch\"\n        );\n        Ok(())\n    }\n}\n\n/// A struct representing the schema of a database index.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct IndexSchema {\n    /// The unique ID of the index within the schema.\n    pub index_id: IndexId,\n    /// The ID of the table associated with the index.\n    pub table_id: TableId,\n    /// The name of the index. This should not be assumed to follow any particular format.\n    /// Unique within the database.\n    pub index_name: RawIdentifier,\n\n    pub alias: Option<RawIdentifier>,\n    /// The data for the schema.\n    pub index_algorithm: IndexAlgorithm,\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for IndexSchema {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            index_id,\n            table_id,\n            index_name,\n            index_algorithm,\n            alias: _,\n        } = self;\n        index_id.heap_usage() + table_id.heap_usage() + index_name.heap_usage() + index_algorithm.heap_usage()\n    }\n}\n\nimpl IndexSchema {\n    pub fn for_test(name: impl AsRef<str>, algo: impl Into<IndexAlgorithm>) -> Self {\n        Self {\n            index_id: IndexId::SENTINEL,\n            table_id: TableId::SENTINEL,\n            index_name: RawIdentifier::new(name.as_ref()),\n            index_algorithm: algo.into(),\n            alias: None,\n        }\n    }\n}\n\nimpl Schema for IndexSchema {\n    type Def = IndexDef;\n    type Id = IndexId;\n    type ParentId = TableId;\n\n    fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {\n        module_def.expect_contains(def);\n\n        let index_algorithm = def.algorithm.clone();\n        IndexSchema {\n            index_id: id,\n            table_id: parent_id,\n            index_name: def.name.clone(),\n            index_algorithm,\n            alias: Some(def.source_name.clone()),\n        }\n    }\n\n    fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.index_name[..], &def.name[..], \"Index name mismatch\");\n        ensure_eq!(&self.index_algorithm, &def.algorithm, \"Index algorithm mismatch\");\n        Ok(())\n    }\n}\n\n/// A struct representing the schema of a database constraint.\n///\n/// This struct holds information about a database constraint, including its unique identifier,\n/// name, the table it belongs to, and the columns it is associated with.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ConstraintSchema {\n    /// The ID of the table the constraint applies to.\n    pub table_id: TableId,\n    /// The unique ID of the constraint within the database.\n    pub constraint_id: ConstraintId,\n    /// The name of the constraint.\n    pub constraint_name: RawIdentifier,\n    /// The data for the constraint.\n    pub data: ConstraintData, // this reuses the type from Def, which is fine, neither of `schema` nor `def` are ABI modules.\n}\n\nimpl spacetimedb_memory_usage::MemoryUsage for ConstraintSchema {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            table_id,\n            constraint_id,\n            constraint_name,\n            data,\n        } = self;\n        table_id.heap_usage() + constraint_id.heap_usage() + constraint_name.heap_usage() + data.heap_usage()\n    }\n}\n\nimpl ConstraintSchema {\n    pub fn unique_for_test(name: impl AsRef<str>, cols: impl Into<ColSet>) -> Self {\n        Self {\n            table_id: TableId::SENTINEL,\n            constraint_id: ConstraintId::SENTINEL,\n            constraint_name: RawIdentifier::new(name.as_ref()),\n            data: ConstraintData::Unique(UniqueConstraintData { columns: cols.into() }),\n        }\n    }\n\n    /// Constructs a `ConstraintSchema` from a given `ConstraintDef` and table identifier.\n    ///\n    /// # Parameters\n    ///\n    /// * `table_id`: Identifier of the table to which the constraint belongs.\n    /// * `constraint`: The `ConstraintDef` containing constraint information.\n    #[deprecated(note = \"Use TableSchema::from_module_def instead\")]\n    pub fn from_def(table_id: TableId, constraint: RawConstraintDefV8) -> Option<Self> {\n        if constraint.constraints.has_unique() {\n            Some(ConstraintSchema {\n                constraint_id: ConstraintId::SENTINEL, // Set to 0 as it may be assigned later.\n                constraint_name: RawIdentifier::new(constraint.constraint_name.trim()),\n                table_id,\n                data: ConstraintData::Unique(UniqueConstraintData {\n                    columns: constraint.columns.into(),\n                }),\n            })\n        } else {\n            None\n        }\n    }\n}\n\nimpl Schema for ConstraintSchema {\n    type Def = ConstraintDef;\n    type Id = ConstraintId;\n    type ParentId = TableId;\n\n    fn from_module_def(module_def: &ModuleDef, def: &Self::Def, parent_id: Self::ParentId, id: Self::Id) -> Self {\n        module_def.expect_contains(def);\n\n        ConstraintSchema {\n            constraint_id: id,\n            constraint_name: def.name.clone(),\n            table_id: parent_id,\n            data: def.data.clone(),\n        }\n    }\n\n    fn check_compatible(&self, _module_def: &ModuleDef, def: &Self::Def) -> Result<(), anyhow::Error> {\n        ensure_eq!(&self.constraint_name[..], &def.name[..], \"Constraint name mismatch\");\n        ensure_eq!(&self.data, &def.data, \"Constraint data mismatch\");\n        Ok(())\n    }\n}\n\n/// A struct representing the schema of a row-level security policy.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct RowLevelSecuritySchema {\n    pub table_id: TableId,\n    pub sql: RawSql,\n}\n"
  },
  {
    "path": "crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__empty_to_populated_migration.snap",
    "content": "---\nsource: crates/schema/src/auto_migrate.rs\nexpression: \"plan.pretty_print(PrettyPrintStyle::AnsiColor).expect(\\\"should pretty print\\\")\"\n---\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\u001b[0m\u001b[1m\u001b[34mDatabase Migration Plan\u001b[0m\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m user table: \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m (\u001b[0m\u001b[32mpublic\u001b[0m)\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • id: \u001b[0m\u001b[35mU64\u001b[0m\n        • name: \u001b[0m\u001b[35mString\u001b[0m\n        • count: \u001b[0m\u001b[35mU16\u001b[0m\n        • sum: \u001b[0m\u001b[35m(v1: U64)\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mUnique constraints:\u001b[0m\n        • Apples_id_key on [id]\n    \u001b[0m\u001b[1m\u001b[34mIndexes:\u001b[0m\n        • Apples_id_idx_btree on [id]\n        • Apples_id_name_idx_btree on [id, name]\n    \u001b[0m\u001b[1m\u001b[34mAuto-increment constraints:\u001b[0m\n        • Apples_id_seq on id\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m user table: \u001b[0m\u001b[1m\u001b[36mBananas\u001b[0m (\u001b[0m\u001b[32mpublic\u001b[0m)\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • id: \u001b[0m\u001b[35mU64\u001b[0m\n        • name: \u001b[0m\u001b[35mString\u001b[0m\n        • count: \u001b[0m\u001b[35mU16\u001b[0m\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m user table: \u001b[0m\u001b[1m\u001b[36mDeliveries\u001b[0m (\u001b[0m\u001b[32mpublic\u001b[0m)\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • scheduled_id: \u001b[0m\u001b[35mU64\u001b[0m\n        • scheduled_at: \u001b[0m\u001b[35m(Interval: (__time_duration_micros__: I64) | Time: (__timestamp_micros_since_unix_epoch__: I64))\u001b[0m\n        • sum: \u001b[0m\u001b[35mArray<(v1: U64)>\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mUnique constraints:\u001b[0m\n        • Deliveries_scheduled_id_key on [scheduled_id]\n    \u001b[0m\u001b[1m\u001b[34mIndexes:\u001b[0m\n        • Deliveries_scheduled_id_idx_btree on [scheduled_id]\n    \u001b[0m\u001b[1m\u001b[34mAuto-increment constraints:\u001b[0m\n        • Deliveries_scheduled_id_seq on scheduled_id\n    \u001b[0m\u001b[1m\u001b[34mSchedule:\u001b[0m\n        • Calls reducer: check_deliveries\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m user table: \u001b[0m\u001b[1m\u001b[36mInspections\u001b[0m (\u001b[0m\u001b[32mpublic\u001b[0m)\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • scheduled_id: \u001b[0m\u001b[35mU64\u001b[0m\n        • scheduled_at: \u001b[0m\u001b[35m(Interval: (__time_duration_micros__: I64) | Time: (__timestamp_micros_since_unix_epoch__: I64))\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mUnique constraints:\u001b[0m\n        • Inspections_scheduled_id_key on [scheduled_id]\n    \u001b[0m\u001b[1m\u001b[34mIndexes:\u001b[0m\n        • Inspections_scheduled_id_idx_btree on [scheduled_id]\n    \u001b[0m\u001b[1m\u001b[34mAuto-increment constraints:\u001b[0m\n        • Inspections_scheduled_id_seq on scheduled_id\n\n▸ ▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m anonymous view: \u001b[0m\u001b[1m\u001b[36mmy_view\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mParameters:\u001b[0m\n        • x: \u001b[0m\u001b[35mU32\u001b[0m\n        • y: \u001b[0m\u001b[35mU32\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • a: \u001b[0m\u001b[35mU64\u001b[0m\n        • b: \u001b[0m\u001b[35mU64\u001b[0m\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m row level security policy:\n    `\u001b[0m\u001b[34mSELECT * FROM Apples\u001b[0m`\n\u001b[0m\u001b[1m\u001b[41m!!! Warning: All clients will be disconnected due to breaking schema changes\u001b[0m\n"
  },
  {
    "path": "crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print no color.snap",
    "content": "---\nsource: crates/schema/src/auto_migrate.rs\nexpression: \"plan.pretty_print(PrettyPrintStyle::NoColor).expect(\\\"should pretty print\\\")\"\n---\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nDatabase Migration Plan\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n▸ Removed index Apples_id_name_idx_btree on [id, name] of table Apples\n▸ Removed unique constraint Apples_id_key on [id] of table Apples\n▸ Removed auto-increment constraint Apples_id_seq on column id of table Apples\n▸ Removed schedule for table Deliveries_sched calling reducer check_deliveries\n▸ ▸ Removed anonymous view: my_view\n    Parameters:\n        • x: U32\n        • y: U32\n    Columns:\n        • a: U64\n        • b: U64\n\n▸ Removed row level security policy:\n    `SELECT * FROM Apples`\n▸ Changed columns for table Apples\n▸ Changed columns for table Deliveries\n▸ Created column in table Bananas\n    + freshness: U32 (default: U32(\n    5,\n))\n▸ Created user table: Oranges (public)\n    Columns:\n        • id: U32\n    Unique constraints:\n        • Oranges_id_key on [id]\n    Indexes:\n        • Oranges_id_idx_btree on [id]\n    Auto-increment constraints:\n        • Oranges_id_seq on id\n\n▸ Created index Apples_id_count_idx_btree on [id, count] of table Apples\n▸ Created auto-increment constraint Bananas_id_seq on column id of table Bananas\n▸ Created schedule for table Inspections_sched calling reducer perform_inspection\n▸ ▸ Created anonymous view: my_view\n    Parameters:\n        • x: U32\n    Columns:\n        • a: U64\n\n▸ Created row level security policy:\n    `SELECT * FROM Bananas`\n▸ Changed access for table Bananas (public → private)\n!!! Warning: All clients will be disconnected due to breaking schema changes\n"
  },
  {
    "path": "crates/schema/src/snapshots/spacetimedb_schema__auto_migrate__tests__updated pretty print.snap",
    "content": "---\nsource: crates/schema/src/auto_migrate.rs\nexpression: \"plan.pretty_print(PrettyPrintStyle::AnsiColor).expect(\\\"should pretty print\\\")\"\n---\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\u001b[0m\u001b[1m\u001b[34mDatabase Migration Plan\u001b[0m\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m index Apples_id_name_idx_btree on [id, name] of table \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m unique constraint Apples_id_key on [id] of table \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m auto-increment constraint Apples_id_seq on column id of table \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m schedule for table \u001b[0m\u001b[1m\u001b[36mDeliveries_sched\u001b[0m calling reducer check_deliveries\n▸ ▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m anonymous view: \u001b[0m\u001b[1m\u001b[36mmy_view\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mParameters:\u001b[0m\n        • x: \u001b[0m\u001b[35mU32\u001b[0m\n        • y: \u001b[0m\u001b[35mU32\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • a: \u001b[0m\u001b[35mU64\u001b[0m\n        • b: \u001b[0m\u001b[35mU64\u001b[0m\n\n▸ \u001b[0m\u001b[1m\u001b[31mRemoved\u001b[0m row level security policy:\n    `\u001b[0m\u001b[34mSELECT * FROM Apples\u001b[0m`\n▸ \u001b[0m\u001b[1m\u001b[33mChanged\u001b[0m columns for table \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[33mChanged\u001b[0m columns for table \u001b[0m\u001b[1m\u001b[36mDeliveries\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m column in table \u001b[0m\u001b[1m\u001b[36mBananas\u001b[0m\n    + freshness: \u001b[0m\u001b[35mU32\u001b[0m (default: U32(\n    5,\n))\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m user table: \u001b[0m\u001b[1m\u001b[36mOranges\u001b[0m (\u001b[0m\u001b[32mpublic\u001b[0m)\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • id: \u001b[0m\u001b[35mU32\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mUnique constraints:\u001b[0m\n        • Oranges_id_key on [id]\n    \u001b[0m\u001b[1m\u001b[34mIndexes:\u001b[0m\n        • Oranges_id_idx_btree on [id]\n    \u001b[0m\u001b[1m\u001b[34mAuto-increment constraints:\u001b[0m\n        • Oranges_id_seq on id\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m index Apples_id_count_idx_btree on [id, count] of table \u001b[0m\u001b[1m\u001b[36mApples\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m auto-increment constraint Bananas_id_seq on column id of table \u001b[0m\u001b[1m\u001b[36mBananas\u001b[0m\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m schedule for table \u001b[0m\u001b[1m\u001b[36mInspections_sched\u001b[0m calling reducer perform_inspection\n▸ ▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m anonymous view: \u001b[0m\u001b[1m\u001b[36mmy_view\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mParameters:\u001b[0m\n        • x: \u001b[0m\u001b[35mU32\u001b[0m\n    \u001b[0m\u001b[1m\u001b[34mColumns:\u001b[0m\n        • a: \u001b[0m\u001b[35mU64\u001b[0m\n\n▸ \u001b[0m\u001b[1m\u001b[32mCreated\u001b[0m row level security policy:\n    `\u001b[0m\u001b[34mSELECT * FROM Bananas\u001b[0m`\n▸ \u001b[0m\u001b[1m\u001b[33mChanged\u001b[0m access for table \u001b[0m\u001b[1m\u001b[36mBananas\u001b[0m (\u001b[0m\u001b[32mpublic → private\u001b[0m)\n\u001b[0m\u001b[1m\u001b[41m!!! Warning: All clients will be disconnected due to breaking schema changes\u001b[0m\n"
  },
  {
    "path": "crates/schema/src/table_name.rs",
    "content": "use crate::identifier::Identifier;\nuse core::fmt;\nuse core::ops::Deref;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, raw_identifier::RawIdentifier};\n\n/// The name of a table.\n#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct TableName(Identifier);\n\nimpl_st!([] TableName, ts => Identifier::make_type(ts));\nimpl_serialize!([] TableName, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] TableName, de => Identifier::deserialize(de).map(Self));\n\nimpl TableName {\n    pub fn new(id: Identifier) -> Self {\n        Self(id)\n    }\n\n    #[cfg(any(test, feature = \"test\"))]\n    pub fn for_test(name: &str) -> Self {\n        Self(Identifier::for_test(name))\n    }\n}\n\nimpl Deref for TableName {\n    type Target = str;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl AsRef<str> for TableName {\n    fn as_ref(&self) -> &str {\n        &self.0\n    }\n}\n\nimpl From<TableName> for Identifier {\n    fn from(id: TableName) -> Self {\n        id.0\n    }\n}\n\nimpl From<TableName> for RawIdentifier {\n    fn from(id: TableName) -> Self {\n        Identifier::from(id).into()\n    }\n}\n\nimpl fmt::Debug for TableName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&self.0, f)\n    }\n}\n\nimpl fmt::Display for TableName {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.0, f)\n    }\n}\n"
  },
  {
    "path": "crates/schema/src/type_for_generate.rs",
    "content": "//! `AlgebraicType` extensions for generating client code.\n\nuse crate::{\n    error::{IdentifierError, PrettyAlgebraicType},\n    identifier::Identifier,\n};\nuse enum_as_inner::EnumAsInner;\nuse petgraph::{\n    algo::tarjan_scc,\n    visit::{GraphBase, IntoNeighbors, IntoNodeIdentifiers, NodeIndexable},\n};\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::{\n    error_stream::{CollectAllErrors, CombineErrors, ErrorStream},\n    map::{hash_set, HashMap, HashSet},\n};\nuse spacetimedb_lib::{AlgebraicType, ProductTypeElement};\nuse spacetimedb_sats::{\n    layout::PrimitiveType, raw_identifier::RawIdentifier, typespace::TypeRefError, AlgebraicTypeRef, ArrayType,\n    SumTypeVariant, Typespace,\n};\nuse std::{cell::RefCell, ops::Index, sync::Arc};\n\n/// Errors that can occur when rearranging types for client codegen.\n#[derive(thiserror::Error, Debug, PartialOrd, Ord, PartialEq, Eq)]\n#[non_exhaustive]\npub enum ClientCodegenError {\n    #[error(\n        \"internal codegen error: non-special product or sum type {ty} cannot be used to generate a client type use\"\n    )]\n    NonSpecialTypeNotAUse { ty: PrettyAlgebraicType },\n\n    #[error(\"internal codegen error: invalid AlgebraicTypeRef\")]\n    TypeRefError(#[from] TypeRefError),\n\n    #[error(\"internal codegen error: type ref {ref_} was not pre-declared as a definition\")]\n    NonDeclaredTypeDef { ref_: AlgebraicTypeRef },\n\n    #[error(\"internal codegen error: all type elements require names: {ty}\")]\n    NamelessTypeDefElement { ty: PrettyAlgebraicType },\n\n    #[error(\"internal codegen error: all reducer parameters require names\")]\n    NamelessReducerParam,\n\n    #[error(\"internal codegen error: type {ty} is not valid for generating a definition\")]\n    NotValidForDefinition { ty: PrettyAlgebraicType },\n\n    #[error(\"type {ty} contains identifier error {err}\")]\n    NotValidIdentifier {\n        ty: PrettyAlgebraicType,\n        err: IdentifierError,\n    },\n}\n\ntype Result<T> = std::result::Result<T, ErrorStream<ClientCodegenError>>;\n\n/// A typespace for generating client code.\n///\n/// The key difference is that this typespace stores only `AlgebraicTypeDef`s, not `AlgebraicType`s.\n/// We use the same `AlgebraicTypeRef`s from the original typespace.\n/// The difference is that `AlgebraicTypeRef`s ONLY point to `AlgebraicTypeDef`s.\n/// Chains of `AlgebraicTypeRef`s in the original `Typespace` are contracted to point to their ending `AlgebraicTypeDef`.\n///\n/// For example, the input:\n/// ```txt\n/// [\n///     0 -> AlgebraicType::Product { a: Ref(1) }\n///     1 -> AlgebraicType::Array(Ref(2))\n///     2 -> AlgebraicType::Product { b: U32 }\n/// ]\n/// ```\n/// Results in the output:\n/// ```txt\n/// [\n///     0 -> AlgebraicTypeDef::Product { a: Array(Ref(2)) }\n///     2 -> AlgebraicTypeDef::Product { b: U32 }\n/// ]\n/// ```\n///\n/// Cycles passing through a definition, such as:\n/// ```txt\n/// [\n///     0 -> Product { a: Ref(1) }\n///     1 -> Sum { a: U32, b: Ref(0) }\n/// ]\n/// ```\n/// are permitted.\n///\n/// Cycles NOT passing through a definition, such as:\n/// ```txt\n/// [\n///     0 -> Ref(1)\n///     1 -> Array(Ref(0))\n/// ]\n/// ```\n/// are forbidden. (Because most languages do not support anonymous recursive types.)\n#[derive(Debug, Clone)]\npub struct TypespaceForGenerate {\n    defs: HashMap<AlgebraicTypeRef, AlgebraicTypeDef>,\n}\n\nimpl TypespaceForGenerate {\n    /// Build a `TypespaceForGenerate`.\n    ///\n    /// We're required to declare known definitions up front.\n    /// This is required for distinguishing between a use of the unit type, and a reference to a type declaration of a product type with no elements.\n    pub fn builder(\n        typespace: &Typespace,\n        is_def: impl IntoIterator<Item = AlgebraicTypeRef>,\n    ) -> TypespaceForGenerateBuilder<'_> {\n        TypespaceForGenerateBuilder {\n            typespace,\n            result: TypespaceForGenerate {\n                defs: HashMap::default(),\n            },\n            is_def: is_def.into_iter().collect(),\n            uses: HashSet::default(),\n            known_uses: HashMap::default(),\n            currently_touching: HashSet::default(),\n        }\n    }\n\n    /// Get the definitions of the typespace.\n    pub fn defs(&self) -> &HashMap<AlgebraicTypeRef, AlgebraicTypeDef> {\n        &self.defs\n    }\n\n    /// Get a definition in the typespace.\n    pub fn get(&self, ref_: AlgebraicTypeRef) -> Option<&AlgebraicTypeDef> {\n        self.defs.get(&ref_)\n    }\n}\n\nimpl Index<AlgebraicTypeRef> for TypespaceForGenerate {\n    type Output = AlgebraicTypeDef;\n\n    fn index(&self, index: AlgebraicTypeRef) -> &Self::Output {\n        &self.defs[&index]\n    }\n}\nimpl Index<&'_ AlgebraicTypeRef> for TypespaceForGenerate {\n    type Output = AlgebraicTypeDef;\n\n    fn index(&self, index: &'_ AlgebraicTypeRef) -> &Self::Output {\n        &self.defs[index]\n    }\n}\n\n/// An algebraic type definition.\n#[derive(Debug, Clone, PartialEq, Eq, EnumAsInner)]\npub enum AlgebraicTypeDef {\n    /// A product type declaration.\n    Product(ProductTypeDef),\n    /// A sum type declaration.\n    Sum(SumTypeDef),\n    /// A plain enum definition.\n    PlainEnum(PlainEnumTypeDef),\n}\n\nthread_local! {\n    /// Used to efficiently extract refs from a def.\n    static EXTRACT_REFS_BUF: RefCell<HashSet<AlgebraicTypeRef>> = RefCell::new(HashSet::default());\n}\n\nimpl AlgebraicTypeDef {\n    /// Check if a def is recursive.\n    pub fn is_recursive(&self) -> bool {\n        match self {\n            AlgebraicTypeDef::Product(ProductTypeDef { recursive, .. }) => *recursive,\n            AlgebraicTypeDef::Sum(SumTypeDef { recursive, .. }) => *recursive,\n            AlgebraicTypeDef::PlainEnum(_) => false,\n        }\n    }\n\n    /// Extract all `AlgebraicTypeRef`s that are used in this type into a buffer.\n    /// The buffer may be in arbitrary order, but will not contain duplicates.\n    fn extract_refs(&self) -> SmallVec<[AlgebraicTypeRef; 16]> {\n        EXTRACT_REFS_BUF.with_borrow_mut(|buf| {\n            buf.clear();\n            match self {\n                AlgebraicTypeDef::Product(ProductTypeDef { elements, .. }) => {\n                    for (_, use_) in elements.iter() {\n                        use_.extract_refs(buf);\n                    }\n                }\n                AlgebraicTypeDef::Sum(SumTypeDef { variants, .. }) => {\n                    for (_, use_) in variants.iter() {\n                        use_.extract_refs(buf);\n                    }\n                }\n                AlgebraicTypeDef::PlainEnum(_) => {}\n            }\n            buf.drain().collect()\n        })\n    }\n\n    /// Mark a def recursive.\n    /// Panics if the def is a `PlainEnum`, because how would that be recursive?\n    fn mark_recursive(&mut self) {\n        match self {\n            AlgebraicTypeDef::Product(ProductTypeDef { recursive, .. }) => {\n                *recursive = true;\n            }\n            AlgebraicTypeDef::Sum(SumTypeDef { recursive, .. }) => {\n                *recursive = true;\n            }\n            AlgebraicTypeDef::PlainEnum(def) => {\n                panic!(\"mark_recursive called on a PlainEnumTypeDef: {def:?}\");\n            }\n        }\n    }\n}\n\n/// A product type definition.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct ProductTypeDef {\n    /// The elements of the product type, in order.\n    pub elements: Box<[(Identifier, AlgebraicTypeUse)]>,\n    /// If the type is recursive, that is, contains a use of itself.\n    pub recursive: bool,\n}\n\nimpl<'a> IntoIterator for &'a ProductTypeDef {\n    type Item = &'a (Identifier, AlgebraicTypeUse);\n    type IntoIter = std::slice::Iter<'a, (Identifier, AlgebraicTypeUse)>;\n    fn into_iter(self) -> Self::IntoIter {\n        self.elements.iter()\n    }\n}\n\nimpl ProductTypeDef {\n    pub fn element_types(&self) -> impl Iterator<Item = &AlgebraicTypeUse> {\n        self.elements.iter().map(|(_, ty)| ty)\n    }\n}\n\n/// A sum type definition.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct SumTypeDef {\n    /// The variants of the sum type, in order.\n    pub variants: Box<[(Identifier, AlgebraicTypeUse)]>,\n    /// If the type is recursive, that is, contains a use of itself.\n    pub recursive: bool,\n}\n\n/// A sum type, all of whose variants contain ().\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct PlainEnumTypeDef {\n    pub variants: Box<[Identifier]>,\n}\n\nimpl<'a> IntoIterator for &'a SumTypeDef {\n    type Item = &'a (Identifier, AlgebraicTypeUse);\n    type IntoIter = std::slice::Iter<'a, (Identifier, AlgebraicTypeUse)>;\n    fn into_iter(self) -> Self::IntoIter {\n        self.variants.iter()\n    }\n}\n\nimpl SumTypeDef {\n    pub fn variant_types(&self) -> impl Iterator<Item = &AlgebraicTypeUse> {\n        self.variants.iter().map(|(_, ty)| ty)\n    }\n}\n\n/// A use of an algebraic type.\n///\n/// This type uses `Arc`s to make cloning cheap.\n/// These `Arc`s are interned/hash-consed in the `TypespaceForGenerateBuilder`.\n/// They are not semantically meaningful and are guaranteed to be acyclic.\n#[derive(Debug, Clone, Eq, PartialEq, Hash)]\npub enum AlgebraicTypeUse {\n    /// A type where the definition is given by the typing context (`Typespace`).\n    /// In other words, this is defined by a pointer to another `AlgebraicType`.\n    /// An AlgebraicTypeUse must point to an `AlgebraicTypeDef`.\n    Ref(AlgebraicTypeRef),\n\n    /// The type of array values where elements are of a base type `elem_ty`.\n    /// Values [`AlgebraicValue::Array(array)`](crate::AlgebraicValue::Array) will have this type.\n    Array(Arc<AlgebraicTypeUse>),\n\n    /// A standard structural option type.\n    Option(Arc<AlgebraicTypeUse>),\n\n    /// A standard structural result type.\n    Result {\n        ok_ty: Arc<AlgebraicTypeUse>,\n        err_ty: Arc<AlgebraicTypeUse>,\n    },\n\n    /// The special `ScheduleAt` type.\n    ScheduleAt,\n\n    /// The special `Identity` type.\n    Identity,\n\n    /// The special `ConnectionId` type.\n    ConnectionId,\n\n    /// The special `Timestamp` type.\n    Timestamp,\n\n    /// The special `TimeDuration` type.\n    TimeDuration,\n\n    /// The special `Uuid` type.\n    Uuid,\n\n    /// The unit type (empty product).\n    /// This is *distinct* from a use of a definition of a product type with no elements.\n    Unit,\n\n    /// The never type (empty sum).\n    /// This is *distinct* from a use of a definition of a sum type with no variants.\n    Never,\n\n    /// The UTF-8 encoded `String` type.\n    String,\n\n    /// A primitive type.\n    Primitive(PrimitiveType),\n}\n\nimpl AlgebraicTypeUse {\n    /// Extract all `AlgebraicTypeRef`s that are used in this type and add them to `buf`.`\n    fn extract_refs(&self, buf: &mut HashSet<AlgebraicTypeRef>) {\n        self.for_each_ref(|r| {\n            buf.insert(r);\n        })\n    }\n\n    /// Recurse through this `AlgebraicTypeUse`, calling `f` on every type ref encountered.\n    pub fn for_each_ref(&self, mut f: impl FnMut(AlgebraicTypeRef)) {\n        self._for_each_ref(&mut f)\n    }\n\n    fn _for_each_ref(&self, f: &mut impl FnMut(AlgebraicTypeRef)) {\n        match self {\n            AlgebraicTypeUse::Ref(ref_) => f(*ref_),\n            AlgebraicTypeUse::Array(elem_ty) => elem_ty._for_each_ref(f),\n            AlgebraicTypeUse::Option(elem_ty) => elem_ty._for_each_ref(f),\n            AlgebraicTypeUse::Result { ok_ty, err_ty } => {\n                ok_ty._for_each_ref(f);\n                err_ty._for_each_ref(f);\n            }\n            _ => {}\n        }\n    }\n}\n\n/// A builder for a `TypespaceForGenerate`.\n///\n/// This is complicated by the fact that a typespace can store both *uses* and *definitions* of types.\npub struct TypespaceForGenerateBuilder<'a> {\n    /// The original typespace.\n    typespace: &'a Typespace,\n\n    /// The result we are building.\n    /// Invariant: all `Def`s in here have been fully processed and correctly marked cyclic.\n    /// Not all `Def`s may have been processed yet.\n    result: TypespaceForGenerate,\n\n    /// The AlgebraicTypeRefs that we know point to definitions. Must be declared at the start of building.\n    /// This is necessary to disambiguate between a use of the unit type, and a reference to a type declaration of a product type with no elements.\n    is_def: HashSet<AlgebraicTypeRef>,\n\n    /// Interning data structure, no semantic meaning.\n    /// We only intern AlgebraicTypes that are used inside other AlgebraicTypes.\n    uses: HashSet<Arc<AlgebraicTypeUse>>,\n\n    /// AlgebraicTypeRefs that point to uses.\n    known_uses: HashMap<AlgebraicTypeRef, AlgebraicTypeUse>,\n\n    /// Stores all `AlgebraicTypeRef`s that are currently being operated on.\n    currently_touching: HashSet<AlgebraicTypeRef>,\n}\n\nimpl TypespaceForGenerateBuilder<'_> {\n    /// Finish building the `TypespaceForGenerate`.\n    /// Panics if `add_definition` has not been called for all of `is_def`.\n    pub fn finish(mut self) -> TypespaceForGenerate {\n        // Finish validating any straggler uses that weren't already processed.\n        for type_ in self.is_def.iter() {\n            assert!(\n                self.result.defs.contains_key(type_),\n                \"internal codegen error: not all definitions were processed.\n                 Did you call `add_definition` for all types in `is_def`?\"\n            );\n        }\n\n        self.mark_allowed_cycles();\n\n        self.result\n    }\n\n    /// Use the `TypespaceForGenerateBuilder` to validate an `AlgebraicTypeUse`.\n    /// Does not actually add anything to the `TypespaceForGenerate`.\n    pub fn parse_use(&mut self, ty: &AlgebraicType) -> Result<AlgebraicTypeUse> {\n        if ty.is_connection_id() {\n            Ok(AlgebraicTypeUse::ConnectionId)\n        } else if ty.is_identity() {\n            Ok(AlgebraicTypeUse::Identity)\n        } else if ty.is_timestamp() {\n            Ok(AlgebraicTypeUse::Timestamp)\n        } else if ty.is_time_duration() {\n            Ok(AlgebraicTypeUse::TimeDuration)\n        } else if ty.is_uuid() {\n            Ok(AlgebraicTypeUse::Uuid)\n        } else if ty.is_unit() {\n            Ok(AlgebraicTypeUse::Unit)\n        } else if ty.is_never() {\n            Ok(AlgebraicTypeUse::Never)\n        } else if let Some(elem_ty) = ty.as_option() {\n            let elem_ty = self.parse_use(elem_ty)?;\n            let interned = self.intern_use(elem_ty);\n            Ok(AlgebraicTypeUse::Option(interned))\n        } else if let Some((ok_ty, err_ty)) = ty.as_result() {\n            let ok = self.parse_use(ok_ty)?;\n            let err = self.parse_use(err_ty)?;\n            let ok_ty = self.intern_use(ok);\n            let err_ty = self.intern_use(err);\n            Ok(AlgebraicTypeUse::Result { ok_ty, err_ty })\n        } else if ty.is_schedule_at() {\n            Ok(AlgebraicTypeUse::ScheduleAt)\n        } else {\n            match ty {\n                AlgebraicType::Ref(ref_) => {\n                    // Indirectly recurse.\n                    self.parse_ref(*ref_)\n                }\n                AlgebraicType::Array(ArrayType { elem_ty }) => {\n                    let elem_ty = self.parse_use(elem_ty)?;\n                    let interned = self.intern_use(elem_ty);\n                    Ok(AlgebraicTypeUse::Array(interned))\n                }\n                AlgebraicType::String => Ok(AlgebraicTypeUse::String),\n                AlgebraicType::Bool => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::Bool)),\n                AlgebraicType::I8 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I8)),\n                AlgebraicType::U8 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U8)),\n                AlgebraicType::I16 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I16)),\n                AlgebraicType::U16 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U16)),\n                AlgebraicType::I32 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I32)),\n                AlgebraicType::U32 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U32)),\n                AlgebraicType::I64 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I64)),\n                AlgebraicType::U64 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U64)),\n                AlgebraicType::I128 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I128)),\n                AlgebraicType::U128 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U128)),\n                AlgebraicType::I256 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::I256)),\n                AlgebraicType::U256 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::U256)),\n                AlgebraicType::F32 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::F32)),\n                AlgebraicType::F64 => Ok(AlgebraicTypeUse::Primitive(PrimitiveType::F64)),\n                ty @ (AlgebraicType::Product(_) | AlgebraicType::Sum(_)) => {\n                    Err(ErrorStream::from(ClientCodegenError::NonSpecialTypeNotAUse {\n                        ty: PrettyAlgebraicType(ty.clone()),\n                    }))\n                }\n            }\n        }\n    }\n\n    /// This is the only seriously complicated case of `parse_use`, which has to deal with cycle detection.\n    /// So it gets its own function.\n    /// Mutually recursive with `parse_use`.\n    fn parse_ref(&mut self, ref_: AlgebraicTypeRef) -> Result<AlgebraicTypeUse> {\n        if self.is_def.contains(&ref_) {\n            // We know this type is going to be a definition.\n            // So, we can just return a ref to it.\n            Ok(AlgebraicTypeUse::Ref(ref_))\n        } else if let Some(use_) = self.known_uses.get(&ref_) {\n            // The ref is to a use which we have already seen.\n            Ok(use_.clone())\n        } else {\n            // We haven't processed it yet. It's either a ref to a valid use, or invalid.\n            let def = self\n                .typespace\n                .get(ref_)\n                .ok_or(TypeRefError::InvalidTypeRef(ref_))\n                .and_then(|def| {\n                    if def == &AlgebraicType::Ref(ref_) {\n                        // Self-reference.\n                        Err(TypeRefError::RecursiveTypeRef(ref_))\n                    } else {\n                        Ok(def)\n                    }\n                })\n                .map_err(|e| ErrorStream::from(ClientCodegenError::TypeRefError(e)))?;\n\n            if self.currently_touching.contains(&ref_) {\n                return Err(ClientCodegenError::TypeRefError(TypeRefError::RecursiveTypeRef(ref_)).into());\n            }\n\n            // Mark this ref.\n            self.currently_touching.insert(ref_);\n            // Recurse.\n            let result = self.parse_use(def);\n            // Unmark this ref before dealing with possible errors.\n            self.currently_touching.remove(&ref_);\n\n            let use_ = result?;\n\n            self.known_uses.insert(ref_, use_.clone());\n\n            Ok(use_)\n        }\n    }\n\n    /// Add a definition.\n    /// Not mutually recursive with anything.\n    /// Does not detect cycles, those are left for `mark_allowed_cycles`, which is called after all definitions are processed.\n    ///\n    /// Why not invoke this for all definitions ourselves, since we know which refs are definitions?\n    /// It's so that the caller can wrap errors with better context information.\n    pub fn add_definition(&mut self, ref_: AlgebraicTypeRef) -> Result<()> {\n        assert!(\n            self.is_def.contains(&ref_),\n            \"internal codegen error: any AlgebraicTypeRef passed to `add_definition` must refer to a declared definition, {ref_} does not\"\n        );\n\n        let def = self\n            .typespace\n            .get(ref_)\n            .ok_or_else(|| ErrorStream::from(ClientCodegenError::TypeRefError(TypeRefError::InvalidTypeRef(ref_))))?;\n\n        match def {\n            AlgebraicType::Product(product) => product\n                .elements\n                .iter()\n                .map(|ProductTypeElement { name, algebraic_type }| self.process_element(def, name, algebraic_type))\n                .collect_all_errors()\n                .map(|elements| {\n                    // We have just processed all the elements, so we know if it's recursive.\n                    self.result.defs.insert(\n                        ref_,\n                        AlgebraicTypeDef::Product(ProductTypeDef {\n                            elements,\n                            recursive: false, // set in `mark_allowed_cycles`\n                        }),\n                    );\n                }),\n            AlgebraicType::Sum(sum) => sum\n                .variants\n                .iter()\n                .map(|SumTypeVariant { name, algebraic_type }| self.process_element(def, name, algebraic_type))\n                .collect_all_errors::<Vec<_>>()\n                .map(|variants| {\n                    if variants.iter().all(|(_, ty)| ty == &AlgebraicTypeUse::Unit) {\n                        // We have just processed all the elements, so we know if it's recursive.\n                        let variants = variants.into_iter().map(|(name, _)| name).collect();\n                        self.result\n                            .defs\n                            .insert(ref_, AlgebraicTypeDef::PlainEnum(PlainEnumTypeDef { variants }));\n                    } else {\n                        let variants = variants.into_boxed_slice();\n\n                        self.result.defs.insert(\n                            ref_,\n                            AlgebraicTypeDef::Sum(SumTypeDef {\n                                variants,\n                                recursive: false, // set in `mark_allowed_cycles`\n                            }),\n                        );\n                    }\n                }),\n            _ => Err(ClientCodegenError::NotValidForDefinition {\n                ty: PrettyAlgebraicType(def.clone()),\n            }\n            .into()),\n        }\n    }\n\n    /// Process an element/variant of a product/sum type.\n    ///\n    /// `def` is the *containing* type that corresponds to a `Def`,\n    /// `algebraic_type` is the type of the element/variant inside `def` and corresponds to a `Use`.\n    fn process_element(\n        &mut self,\n        def: &AlgebraicType,\n        element_name: &Option<RawIdentifier>,\n        element_type: &AlgebraicType,\n    ) -> Result<(Identifier, AlgebraicTypeUse)> {\n        let element_name = element_name\n            .as_ref()\n            .ok_or_else(|| ErrorStream::from(ClientCodegenError::NamelessTypeDefElement { ty: def.clone().into() }))\n            .and_then(|element_name| {\n                Identifier::new(element_name.clone()).map_err(|err| {\n                    ErrorStream::from(ClientCodegenError::NotValidIdentifier {\n                        ty: def.clone().into(),\n                        err,\n                    })\n                })\n            });\n        let ty = self.parse_use(element_type);\n        (element_name, ty).combine_errors()\n    }\n\n    // Intern a use.\n    // This is only used on types *inside* Map, Array, and Option types.\n    fn intern_use(&mut self, use_: AlgebraicTypeUse) -> Arc<AlgebraicTypeUse> {\n        if let Some(ty) = self.uses.get(&use_) {\n            return ty.clone();\n        }\n        let ty = Arc::new(use_);\n        self.uses.insert(ty.clone());\n        ty\n    }\n\n    /// Cycles passing through definitions are allowed.\n    /// This function is called after all definitions have been processed.\n    fn mark_allowed_cycles(&mut self) {\n        let strongly_connected_components: Vec<Vec<AlgebraicTypeRef>> = tarjan_scc(&*self);\n        for component in strongly_connected_components {\n            if component.len() == 1 {\n                // petgraph's implementation returns a vector for all nodes, not distinguishing between\n                // self referential and non-self-referential nodes. ignore this for now.\n                continue;\n            }\n            for ref_ in component {\n                self.result\n                    .defs\n                    .get_mut(&ref_)\n                    .expect(\"all defs should be processed by now\")\n                    .mark_recursive();\n            }\n        }\n\n        // Now, fix up directly self-referential nodes.\n        for (ref_, def_) in &mut self.result.defs {\n            let ref_ = *ref_;\n            if def_.is_recursive() {\n                continue;\n            }\n            let refs = def_.extract_refs();\n            if refs.contains(&ref_) {\n                def_.mark_recursive();\n            }\n        }\n    }\n}\n\n// We implement some `petgraph` traits for `TypespaceForGenerate` to allow using\n// petgraph's implementation of Tarjan's strongly-connected-components algorithm.\n// This is used in `mark_allowed_cycles`.\n// We don't implement all the traits, only the ones we need.\n// The traits are intended to be used *after* all defs have been processed.\n\nimpl GraphBase for TypespaceForGenerateBuilder<'_> {\n    /// Specifically, definition IDs.\n    type NodeId = AlgebraicTypeRef;\n\n    /// Definition `.0` uses definition `.1`.\n    type EdgeId = (AlgebraicTypeRef, AlgebraicTypeRef);\n}\nimpl NodeIndexable for TypespaceForGenerateBuilder<'_> {\n    fn node_bound(&self) -> usize {\n        self.typespace.types.len()\n    }\n\n    fn to_index(&self, a: Self::NodeId) -> usize {\n        a.idx()\n    }\n\n    fn from_index(&self, i: usize) -> Self::NodeId {\n        AlgebraicTypeRef(i as _)\n    }\n}\nimpl<'a> IntoNodeIdentifiers for &'a TypespaceForGenerateBuilder<'a> {\n    type NodeIdentifiers = std::iter::Cloned<hash_set::Iter<'a, spacetimedb_sats::AlgebraicTypeRef>>;\n\n    fn node_identifiers(self) -> Self::NodeIdentifiers {\n        self.is_def.iter().cloned()\n    }\n}\nimpl<'a> IntoNeighbors for &'a TypespaceForGenerateBuilder<'a> {\n    type Neighbors = <SmallVec<[AlgebraicTypeRef; 16]> as IntoIterator>::IntoIter;\n\n    fn neighbors(self, a: Self::NodeId) -> Self::Neighbors {\n        self.result\n            .defs\n            .get(&a)\n            .expect(\"all defs should have been processed by now\")\n            .extract_refs()\n            .into_iter()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use proptest::prelude::*;\n    use spacetimedb_data_structures::expect_error_matching;\n    use spacetimedb_lib::AlgebraicType;\n    use spacetimedb_sats::proptest::generate_typespace_valid_for_codegen;\n\n    fn is_def(typespace: &Typespace) -> HashSet<AlgebraicTypeRef> {\n        typespace\n            .refs_with_types()\n            .filter_map(|(ref_, ty)| {\n                if ty.is_valid_for_client_type_definition() {\n                    Some(ref_)\n                } else {\n                    None\n                }\n            })\n            .collect()\n    }\n\n    proptest! {\n        #[test]\n        fn test_valid_typespace(t in generate_typespace_valid_for_codegen(5)) {\n            let is_def = is_def(&t);\n            let mut builder = TypespaceForGenerate::builder(&t, is_def.clone());\n\n            for (ref_, ty) in t.refs_with_types() {\n                if is_def.contains(&ref_) {\n                    builder.add_definition(ref_).unwrap();\n                } else {\n                    builder.parse_use(ty).unwrap();\n                }\n            }\n        }\n    }\n\n    #[test]\n    fn test_collapses_chains() {\n        let mut t = Typespace::default();\n        let def = t.add(AlgebraicType::product([(\"a\", AlgebraicType::U32)]));\n        let ref0 = t.add(AlgebraicType::Ref(def));\n        let ref1 = t.add(AlgebraicType::array(AlgebraicType::Ref(def)));\n        let ref2 = t.add(AlgebraicType::option(AlgebraicType::Ref(ref1)));\n        let ref3 = t.add(AlgebraicType::Ref(ref2));\n        let ref4 = t.add(AlgebraicType::result(\n            AlgebraicType::Ref(ref3),\n            AlgebraicType::Ref(ref2),\n        ));\n\n        let expected_0 = AlgebraicTypeUse::Ref(def);\n        let expected_1 = AlgebraicTypeUse::Array(Arc::new(expected_0.clone()));\n        let expected_2 = AlgebraicTypeUse::Option(Arc::new(expected_1.clone()));\n        let expected_3 = expected_2.clone();\n        let expected_4 = AlgebraicTypeUse::Result {\n            ok_ty: Arc::new(expected_3.clone()),\n            err_ty: Arc::new(expected_2.clone()),\n        };\n\n        let mut for_generate_forward = TypespaceForGenerate::builder(&t, [def]);\n        for_generate_forward.add_definition(def).unwrap();\n        let use0 = for_generate_forward.parse_use(&ref0.into()).unwrap();\n        let use1 = for_generate_forward.parse_use(&ref1.into()).unwrap();\n        let use2 = for_generate_forward.parse_use(&ref2.into()).unwrap();\n        let use3 = for_generate_forward.parse_use(&ref3.into()).unwrap();\n        let use4 = for_generate_forward.parse_use(&ref4.into()).unwrap();\n\n        assert_eq!(use0, expected_0);\n        assert_eq!(use1, expected_1);\n        assert_eq!(use2, expected_2);\n        assert_eq!(use3, expected_3);\n        assert_eq!(use4, expected_4);\n\n        let mut for_generate_backward = TypespaceForGenerate::builder(&t, [def]);\n        let use4 = for_generate_backward.parse_use(&ref4.into()).unwrap();\n        let use3 = for_generate_forward.parse_use(&ref3.into()).unwrap();\n        let use2 = for_generate_forward.parse_use(&ref2.into()).unwrap();\n        let use1 = for_generate_forward.parse_use(&ref1.into()).unwrap();\n        let use0 = for_generate_backward.parse_use(&ref0.into()).unwrap();\n        for_generate_backward.add_definition(def).unwrap();\n\n        assert_eq!(use0, expected_0);\n        assert_eq!(use1, expected_1);\n        assert_eq!(use2, expected_2);\n        assert_eq!(use3, expected_3);\n        assert_eq!(use4, expected_4);\n    }\n\n    #[test]\n    fn test_detects_cycles_1() {\n        let cyclic_1 = Typespace::new(vec![AlgebraicType::Ref(AlgebraicTypeRef(0))]);\n        let mut for_generate = TypespaceForGenerate::builder(&cyclic_1, []);\n        let err1 = for_generate.parse_use(&AlgebraicType::Ref(AlgebraicTypeRef(0)));\n\n        expect_error_matching!(\n            err1,\n            ClientCodegenError::TypeRefError(TypeRefError::RecursiveTypeRef(AlgebraicTypeRef(0)))\n        );\n    }\n\n    #[test]\n    fn test_detects_cycles_2() {\n        let cyclic_2 = Typespace::new(vec![\n            AlgebraicType::Ref(AlgebraicTypeRef(1)),\n            AlgebraicType::Ref(AlgebraicTypeRef(0)),\n        ]);\n        let mut for_generate = TypespaceForGenerate::builder(&cyclic_2, []);\n        let err2 = for_generate.parse_use(&AlgebraicType::Ref(AlgebraicTypeRef(0)));\n\n        expect_error_matching!(\n            err2,\n            ClientCodegenError::TypeRefError(TypeRefError::RecursiveTypeRef(AlgebraicTypeRef(0)))\n        );\n    }\n\n    #[test]\n    fn test_detects_cycles_3() {\n        let cyclic_3 = Typespace::new(vec![\n            AlgebraicType::Ref(AlgebraicTypeRef(1)),\n            AlgebraicType::product([(\"field\", AlgebraicType::Ref(AlgebraicTypeRef(0)))]),\n        ]);\n        let mut for_generate = TypespaceForGenerate::builder(&cyclic_3, [AlgebraicTypeRef(1)]);\n        for_generate\n            .parse_use(&AlgebraicType::Ref(AlgebraicTypeRef(0)))\n            .expect(\"should be allowed\");\n        for_generate\n            .add_definition(AlgebraicTypeRef(1))\n            .expect(\"should be allowed\");\n        let result = for_generate.finish();\n        let table = result.defs().get(&AlgebraicTypeRef(1)).expect(\"should be defined\");\n\n        assert!(table.is_recursive(), \"recursion not detected? table: {table:?}\");\n    }\n\n    #[test]\n    fn test_detects_cycles_4() {\n        let cyclic_4 = Typespace::new(vec![\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(1).into())]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(2).into())]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(3).into())]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(0).into())]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(0).into())]),\n        ]);\n        let mut for_generate = TypespaceForGenerate::builder(\n            &cyclic_4,\n            [\n                AlgebraicTypeRef(0),\n                AlgebraicTypeRef(1),\n                AlgebraicTypeRef(2),\n                AlgebraicTypeRef(3),\n                AlgebraicTypeRef(4),\n            ],\n        );\n\n        for i in 0..5 {\n            for_generate\n                .parse_use(&AlgebraicType::Ref(AlgebraicTypeRef(i)))\n                .expect(\"should be allowed\");\n            for_generate\n                .add_definition(AlgebraicTypeRef(i))\n                .expect(\"should be allowed\");\n        }\n        let result = for_generate.finish();\n\n        for i in 0..4 {\n            assert!(result[AlgebraicTypeRef(i)].is_recursive(), \"recursion not detected\");\n        }\n        assert!(\n            !result[AlgebraicTypeRef(4)].is_recursive(),\n            \"recursion detected incorrectly\"\n        );\n    }\n\n    #[test]\n    fn test_detects_cycles_5() {\n        // Branching cycles.\n        let cyclic_5 = Typespace::new(vec![\n            // cyclic component.\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(1).into())]),\n            AlgebraicType::product([\n                (\"cyclic_1\", AlgebraicTypeRef(2).into()),\n                (\"cyclic_2\", AlgebraicTypeRef(3).into()),\n                (\"acyclic\", AlgebraicTypeRef(5).into()),\n            ]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(0).into())]),\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(0).into())]),\n            // points into cyclic component, but is not cyclic.\n            AlgebraicType::product([(\"field\", AlgebraicTypeRef(2).into())]),\n            // referred to by cyclic component, but is not cyclic.\n            AlgebraicType::product([(\"field\", AlgebraicType::U32)]),\n        ]);\n        let mut for_generate = TypespaceForGenerate::builder(\n            &cyclic_5,\n            [\n                AlgebraicTypeRef(0),\n                AlgebraicTypeRef(1),\n                AlgebraicTypeRef(2),\n                AlgebraicTypeRef(3),\n                AlgebraicTypeRef(4),\n                AlgebraicTypeRef(5),\n            ],\n        );\n\n        for i in 0..6 {\n            for_generate\n                .parse_use(&AlgebraicType::Ref(AlgebraicTypeRef(i)))\n                .expect(\"should be allowed\");\n            for_generate\n                .add_definition(AlgebraicTypeRef(i))\n                .expect(\"should be allowed\");\n        }\n        let result = for_generate.finish();\n\n        for i in 0..4 {\n            assert!(result[AlgebraicTypeRef(i)].is_recursive(), \"recursion not detected\");\n        }\n        for i in 4..6 {\n            assert!(\n                !result[AlgebraicTypeRef(i)].is_recursive(),\n                \"recursion detected incorrectly\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "crates/schema/tests/ensure_same_schema.rs",
    "content": "// Wrap these tests in a `mod` whose name contains `csharp`\n// so that we can run tests with `--skip csharp` in environments without dotnet installed.\nuse serial_test::serial;\nuse spacetimedb_schema::auto_migrate::{ponder_auto_migrate, AutoMigrateStep};\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_testing::modules::{CompilationMode, CompiledModule};\n\nfn get_normalized_schema(module_name: &str) -> ModuleDef {\n    let module = CompiledModule::compile(module_name, CompilationMode::Debug);\n    module.extract_schema_blocking()\n}\n\nfn assert_identical_modules(module_name_prefix: &str, lang_name: &str, suffix: &str) {\n    let rs = get_normalized_schema(module_name_prefix);\n    let cs = get_normalized_schema(&format!(\"{module_name_prefix}-{suffix}\"));\n    let mut diff = ponder_auto_migrate(&cs, &rs)\n        .unwrap_or_else(|e| panic!(\"could not compute a diff between Rust and {lang_name}: {e:?}\"))\n        .steps;\n\n    // In any migration plan, all `RowLevelSecurityDef`s are ALWAYS removed and\n    // re-added to ensure the core engine reinitializes the policies.\n    // This is slightly silly (and arguably should be hidden inside `core`),\n    // but for now, we just ignore these steps and manually compare the `RowLevelSecurityDef`s.\n    diff.retain(|step| {\n        !matches!(\n            step,\n            AutoMigrateStep::AddRowLevelSecurity(_) | AutoMigrateStep::RemoveRowLevelSecurity(_)\n        )\n    });\n\n    // TODO: Remove this once we have view bindings for C# and TypeScript\n    diff.retain(|step| {\n        !matches!(\n            step,\n            AutoMigrateStep::DisconnectAllUsers\n                | AutoMigrateStep::AddView(_)\n                | AutoMigrateStep::RemoveView(_)\n                | AutoMigrateStep::UpdateView(_)\n        )\n    });\n\n    assert!(\n        diff.is_empty(),\n        \"Rust and {lang_name} modules are not identical. Here are the steps to migrate from {lang_name} to Rust: {diff:#?}\"\n    );\n\n    let mut rls_rs = rs.row_level_security().collect::<Vec<_>>();\n    rls_rs.sort();\n    let mut rls_cs = cs.row_level_security().collect::<Vec<_>>();\n    rls_cs.sort();\n    assert_eq!(\n        rls_rs, rls_cs,\n        \"Rust and {lang_name} modules are not identical: different row level security policies\"\n    )\n}\n\nmacro_rules! declare_tests {\n        ($($name:ident => $path:literal,)*) => {\n            mod ensure_same_schema_rust_csharp {\n                use super::*;\n                $(\n                    #[test]\n                    #[serial]\n                    fn $name() {\n                        super::assert_identical_modules($path, \"C#\", \"cs\");\n                    }\n                )*\n            }\n            mod ensure_same_schema_rust_typescript {\n                use super::*;\n                $(\n                    #[test]\n                    #[serial]\n                    fn $name() {\n                        super::assert_identical_modules($path, \"typescript\", \"ts\");\n                    }\n                )*\n            }\n        }\n    }\n\ndeclare_tests! {\n    benchmarks => \"benchmarks\",\n    module_test => \"module-test\",\n    sdk_test_connect_disconnect => \"sdk-test-connect-disconnect\",\n    sdk_test => \"sdk-test\",\n}\n\n#[test]\n#[serial]\nfn ensure_same_schema_rust_csharp_benchmarks() {\n    assert_identical_modules(\"benchmarks\", \"C#\", \"cs\");\n}\n"
  },
  {
    "path": "crates/smoketests/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-smoketests\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\n\n[dependencies]\n# Test utilities (needed in lib for test helpers)\nspacetimedb-guard.workspace = true\ntempfile.workspace = true\nserde_json.workspace = true\ntoml.workspace = true\nregex.workspace = true\nanyhow.workspace = true\nwhich = \"8.0.0\"\n\n[dev-dependencies]\nspacetimedb-core.workspace = true\ncargo_metadata.workspace = true\nassert_cmd = \"2\"\npredicates = \"3\"\ntokio.workspace = true\ntokio-postgres.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/smoketests/DEVELOP.md",
    "content": "# Smoketests Development Guide\n\n## Running Tests\n\n### Recommended: cargo smoketest\n\n```bash\ncargo smoketest\n```\n\nThis command:\n1. Builds `spacetimedb-cli` and `spacetimedb-standalone` binaries\n2. Runs all smoketests in parallel using nextest (or cargo test if nextest isn't installed)\n\nTo run specific tests:\n```bash\ncargo smoketest test_sql_format\ncargo smoketest \"cli::\"  # Run all CLI tests\n```\n\n### WARNING: Stale Binary Risk\n\n**Smoketests use pre-built binaries and DO NOT automatically rebuild them.**\n\nIf you modify code in `spacetimedb-cli`, `spacetimedb-standalone`, or their dependencies,\nyou MUST rebuild before running tests:\n\n```bash\n# Option 1: Use cargo smoketest (always rebuilds first)\ncargo smoketest\n\n# Option 2: Manually rebuild, then run tests directly\ncargo build -p spacetimedb-cli -p spacetimedb-standalone --features spacetimedb-standalone/allow_loopback_http_for_tests\ncargo nextest run -p spacetimedb-smoketests\n```\n\n**If you run `cargo nextest run` or `cargo test` directly without rebuilding,\nyou may be testing against OLD binaries.** This can cause confusing test failures\nor, worse, tests that pass when they shouldn't.\n\nTo check which binary you're testing against:\n```bash\nls -la target/debug/spacetimedb-cli*  # Check modification time\n```\n\n### Why This Design?\n\nRunning `cargo build` from inside parallel tests causes race conditions on Windows\nwhere multiple processes try to replace running executables (\"Access denied\" errors).\nPre-building avoids this entirely.\n\n### Alternative: cargo test\n\nStandard `cargo test` also works, but you must rebuild first:\n\n```bash\ncargo build -p spacetimedb-cli -p spacetimedb-standalone --features spacetimedb-standalone/allow_loopback_http_for_tests\ncargo test -p spacetimedb-smoketests\n```\n\n## Test Performance\n\nEach test takes ~15-20s due to:\n- **WASM compilation** (~12s): Each test compiles a fresh Rust module to WASM\n- **Server spawn** (~2s): Each test starts its own SpacetimeDB server\n- **Module publish** (~2s): Server processes and initializes the WASM module\n\nWhen running tests in parallel, resource contention increases individual test times but reduces overall runtime.\n\n## Writing Tests\n\nSee existing tests for patterns. Key points:\n\n```rust\nuse spacetimedb_smoketests::Smoketest;\n\nconst MODULE_CODE: &str = r#\"\nuse spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = example, public)]\npub struct Example { value: u64 }\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, value: u64) {\n    ctx.db.example().insert(Example { value });\n}\n\"#;\n\n#[test]\nfn test_example() {\n    let test = Smoketest::builder()\n        .module_code(MODULE_CODE)\n        .build();\n\n    test.call(\"add\", &[\"42\"]).unwrap();\n    test.assert_sql(\"SELECT * FROM example\", \"value\\n-----\\n42\");\n}\n```\n"
  },
  {
    "path": "crates/smoketests/fixtures/README.md",
    "content": "# Smoketest Fixtures\n\n`upgrade_old_module_v1.wasm` is an old-format module fixture used by\n`crates/smoketests/tests/publish_upgrade_prompt.rs` to test the `1.0 -> 2.0`\nupgrade confirmation flow.\n\nIt was produced from a pre-`RawModuleDefV10` bindings snapshot and exports\n`__describe_module__` (not `__describe_module_v10__`).\n\n## Regenerate\n\n```bash\n# from repo root\nTMP=\"$(mktemp -d)\"\ngit archive --format=tar d3f59480e -o \"$TMP/old-repo.tar\"\nmkdir -p \"$TMP/old-repo\"\ntar -xf \"$TMP/old-repo.tar\" -C \"$TMP/old-repo\"\n\nmkdir -p \"$TMP/old-module/src\"\ncat > \"$TMP/old-module/Cargo.toml\" <<EOF\n[package]\nname = \"upgrade_old_module\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = { path = \"$TMP/old-repo/crates/bindings\", features = [\"unstable\"] }\nEOF\n\ncat > \"$TMP/old-module/src/lib.rs\" <<'EOF'\nuse spacetimedb::{reducer, ReducerContext};\n\n#[reducer]\npub fn noop(_ctx: &ReducerContext) {}\nEOF\n\nCARGO_NET_OFFLINE=true CARGO_TARGET_DIR=\"$TMP/target-old\" \\\n  cargo build --release --target wasm32-unknown-unknown \\\n  --manifest-path \"$TMP/old-module/Cargo.toml\"\n\ncp \"$TMP/target-old/wasm32-unknown-unknown/release/upgrade_old_module.wasm\" \\\n  crates/smoketests/fixtures/upgrade_old_module_v1.wasm\n```\n"
  },
  {
    "path": "crates/smoketests/modules/Cargo.toml",
    "content": "# Nested workspace for pre-compiled smoketest modules.\n# This workspace is excluded from the root workspace and built separately\n# during the smoketest warmup phase.\n#\n# All modules here are compiled to WASM once during warmup, then reused\n# by tests without per-test compilation overhead.\n\n[workspace]\nresolver = \"3\"\nmembers = [\n    # Filtering and query tests\n    \"filtering\",\n    \"dml\",\n\n    # Views tests\n    \"views-basic\",\n    # \"views-broken-namespace\" - intentionally broken, uses runtime compilation\n    # \"views-broken-return-type\" - intentionally broken, uses runtime compilation\n    \"views-sql\",\n    \"views-auto-migrate\",\n    \"views-auto-migrate-updated\",\n    \"views-drop-view\",\n    \"views-trapped\",\n    \"views-recovered\",\n    \"views-subscribe\",\n    \"views-query\",\n    \"views-callable\",\n    \"views-count\",\n\n    # Security and permissions\n    \"rls\",\n    \"rls-no-filter\",\n    \"rls-with-filter\",\n    \"permissions-private\",\n    \"permissions-lifecycle\",\n\n    # Call/procedure tests\n    \"call-reducer-procedure\",\n    \"call-empty\",\n\n    # SQL format tests\n    \"sql-format\",\n    \"pg-wire\",\n\n    # Scheduled reducer tests\n    \"schedule-cancel\",\n    \"schedule-subscribe\",\n    \"schedule-procedure\",\n    \"schedule-volatile\",\n\n    # Module lifecycle tests\n    \"describe\",\n    \"modules-basic\",\n    \"modules-breaking\",\n    \"modules-add-table\",\n    \"upload-module-2\",\n    \"hotswap-basic\",\n    \"hotswap-updated\",\n\n    # Index tests\n    \"add-remove-index\",\n    \"add-remove-index-indexed\",\n\n    # Panic/error handling\n    \"panic\",\n    \"panic-error\",\n\n    # Restart tests\n    \"restart-person\",\n    \"restart-connected-client\",\n\n    # Connection tests\n    \"connect-disconnect\",\n    \"confirmed-reads\",\n    \"delete-database\",\n    \"client-connection-reject\",\n    \"client-connection-disconnect-panic\",\n\n    # Log filtering tests\n    \"logs-level-filter\",\n\n    # Misc tests\n    \"namespaces\",\n    \"new-user-flow\",\n    \"module-nested-op\",\n    # \"fail-initial-publish-broken\" - intentionally broken, uses runtime compilation\n    \"fail-initial-publish-fixed\",\n\n    # Auto-increment tests (all 10 integer types in one module each)\n    \"autoinc-basic\",\n    \"autoinc-unique\",\n]\n\n[workspace.dependencies]\nspacetimedb = { path = \"../../../crates/bindings\", features = [\"unstable\"] }\nlog = \"0.4\"\n"
  },
  {
    "path": "crates/smoketests/modules/add-remove-index/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-add-remove-index\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/add-remove-index/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = t1)]\npub struct T1 {\n    id: u64,\n}\n\n#[spacetimedb::table(accessor = t2)]\npub struct T2 {\n    id: u64,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    for id in 0..1_000 {\n        ctx.db.t1().insert(T1 { id });\n        ctx.db.t2().insert(T2 { id });\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/modules/add-remove-index-indexed/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-add-remove-index-indexed\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/add-remove-index-indexed/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = t1)]\npub struct T1 {\n    #[index(btree)]\n    id: u64,\n}\n\n#[spacetimedb::table(accessor = t2)]\npub struct T2 {\n    #[index(btree)]\n    id: u64,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    for id in 0..1_000 {\n        ctx.db.t1().insert(T1 { id });\n        ctx.db.t2().insert(T2 { id });\n    }\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext) {\n    let id = 1_001;\n    ctx.db.t1().insert(T1 { id });\n    ctx.db.t2().insert(T2 { id });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/autoinc-basic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-autoinc-basic\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = { path = \"../../../../crates/bindings\" }\nlog = \"0.4\"\npaste = \"1.0\"\n"
  },
  {
    "path": "crates/smoketests/modules/autoinc-basic/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\nuse spacetimedb::{log, ReducerContext, Table};\n\nmacro_rules! autoinc_basic {\n    ($($ty:ident),*) => {\n        $(\n            paste::paste! {\n                #[spacetimedb::table(accessor = [<person_ $ty>])]\n                pub struct [<Person_ $ty>] {\n                    #[auto_inc]\n                    key_col: $ty,\n                    name: String,\n                }\n\n                #[spacetimedb::reducer]\n                pub fn [<add_ $ty>](ctx: &ReducerContext, name: String, expected_value: $ty) {\n                    let value = ctx.db.[<person_ $ty>]().insert([<Person_ $ty>] { key_col: 0, name });\n                    assert_eq!(value.key_col, expected_value);\n                }\n\n                #[spacetimedb::reducer]\n                pub fn [<say_hello_ $ty>](ctx: &ReducerContext) {\n                    for person in ctx.db.[<person_ $ty>]().iter() {\n                        log::info!(\"Hello, {}:{}!\", person.key_col, person.name);\n                    }\n                    log::info!(\"Hello, World!\");\n                }\n            }\n        )*\n    };\n}\n\nautoinc_basic!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);\n"
  },
  {
    "path": "crates/smoketests/modules/autoinc-unique/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-autoinc-unique\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = { path = \"../../../../crates/bindings\" }\nlog = \"0.4\"\npaste = \"1.0\"\n"
  },
  {
    "path": "crates/smoketests/modules/autoinc-unique/src/lib.rs",
    "content": "#![allow(non_camel_case_types)]\nuse spacetimedb::{log, ReducerContext, Table};\nuse std::error::Error;\n\nmacro_rules! autoinc_unique {\n    ($($ty:ident),*) => {\n        $(\n            paste::paste! {\n                #[spacetimedb::table(accessor = [<person_ $ty>])]\n                pub struct [<Person_ $ty>] {\n                    #[auto_inc]\n                    #[unique]\n                    key_col: $ty,\n                    #[unique]\n                    name: String,\n                }\n\n                #[spacetimedb::reducer]\n                pub fn [<add_new_ $ty>](ctx: &ReducerContext, name: String) -> Result<(), Box<dyn Error>> {\n                    let value = ctx.db.[<person_ $ty>]().try_insert([<Person_ $ty>] { key_col: 0, name })?;\n                    log::info!(\"Assigned Value: {} -> {}\", value.key_col, value.name);\n                    Ok(())\n                }\n\n                #[spacetimedb::reducer]\n                pub fn [<update_ $ty>](ctx: &ReducerContext, name: String, new_id: $ty) {\n                    ctx.db.[<person_ $ty>]().name().delete(&name);\n                    let _value = ctx.db.[<person_ $ty>]().insert([<Person_ $ty>] { key_col: new_id, name });\n                }\n\n                #[spacetimedb::reducer]\n                pub fn [<say_hello_ $ty>](ctx: &ReducerContext) {\n                    for person in ctx.db.[<person_ $ty>]().iter() {\n                        log::info!(\"Hello, {}:{}!\", person.key_col, person.name);\n                    }\n                    log::info!(\"Hello, World!\");\n                }\n            }\n        )*\n    };\n}\n\nautoinc_unique!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);\n"
  },
  {
    "path": "crates/smoketests/modules/call-empty/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-call-empty\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/call-empty/src/lib.rs",
    "content": "#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n}\n"
  },
  {
    "path": "crates/smoketests/modules/call-reducer-procedure/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-call-reducer-procedure\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/call-reducer-procedure/src/lib.rs",
    "content": "use spacetimedb::{log, ProcedureContext, ReducerContext};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(_ctx: &ReducerContext) {\n    log::info!(\"Hello, World!\");\n}\n\n#[spacetimedb::procedure]\npub fn return_person(_ctx: &mut ProcedureContext) -> Person {\n    return Person {\n        name: \"World\".to_owned(),\n    };\n}\n"
  },
  {
    "path": "crates/smoketests/modules/client-connection-disconnect-panic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-client-connection-disconnect-panic\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/client-connection-disconnect-panic/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = all_u8s, public)]\npub struct AllU8s {\n    number: u8,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    for i in u8::MIN..=u8::MAX {\n        ctx.db.all_u8s().insert(AllU8s { number: i });\n    }\n}\n\n#[spacetimedb::reducer(client_connected)]\npub fn identity_connected(_ctx: &ReducerContext) -> Result<(), String> {\n    Ok(())\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn identity_disconnected(_ctx: &ReducerContext) {\n    panic!(\"This should be called, but the `st_client` row should still be deleted\")\n}\n"
  },
  {
    "path": "crates/smoketests/modules/client-connection-reject/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-client-connection-reject\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/client-connection-reject/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = all_u8s, public)]\npub struct AllU8s {\n    number: u8,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    for i in u8::MIN..=u8::MAX {\n        ctx.db.all_u8s().insert(AllU8s { number: i });\n    }\n}\n\n#[spacetimedb::reducer(client_connected)]\npub fn identity_connected(_ctx: &ReducerContext) -> Result<(), String> {\n    Err(\"Rejecting connection from client\".to_string())\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn identity_disconnected(_ctx: &ReducerContext) {\n    panic!(\"This should never be called, since we reject all connections!\")\n}\n"
  },
  {
    "path": "crates/smoketests/modules/confirmed-reads/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-confirmed-reads\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/confirmed-reads/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/connect-disconnect/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-connect-disconnect\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/connect-disconnect/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext};\n\n#[spacetimedb::reducer(client_connected)]\npub fn connected(_ctx: &ReducerContext) {\n    log::info!(\"_connect called\");\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn disconnected(_ctx: &ReducerContext) {\n    log::info!(\"disconnect called\");\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(_ctx: &ReducerContext) {\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/delete-database/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-delete-database\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/delete-database/src/lib.rs",
    "content": "use spacetimedb::{duration, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = counter, public)]\npub struct Counter {\n    #[primary_key]\n    id: u64,\n    val: u64,\n}\n\n#[spacetimedb::table(accessor = scheduled_counter, public, scheduled(inc, at = sched_at))]\npub struct ScheduledCounter {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    sched_at: spacetimedb::ScheduleAt,\n}\n\n#[spacetimedb::reducer]\npub fn inc(ctx: &ReducerContext, arg: ScheduledCounter) {\n    if let Some(mut counter) = ctx.db.counter().id().find(arg.scheduled_id) {\n        counter.val += 1;\n        ctx.db.counter().id().update(counter);\n    } else {\n        ctx.db.counter().insert(Counter {\n            id: arg.scheduled_id,\n            val: 1,\n        });\n    }\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    ctx.db.scheduled_counter().insert(ScheduledCounter {\n        scheduled_id: 0,\n        sched_at: duration!(100ms).into(),\n    });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/describe/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-describe\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/describe/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/dml/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-dml\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/dml/src/lib.rs",
    "content": "#[spacetimedb::table(accessor = t, public)]\npub struct T {\n    name: String,\n}\n"
  },
  {
    "path": "crates/smoketests/modules/fail-initial-publish-broken/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-fail-initial-publish-broken\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/fail-initial-publish-broken/src/lib.rs",
    "content": "use spacetimedb::{client_visibility_filter, Filter};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[client_visibility_filter]\n// Bug: `Person` is the wrong table name, should be `person`.\nconst HIDE_PEOPLE_EXCEPT_ME: Filter = Filter::Sql(\"SELECT * FROM Person WHERE name = 'me'\");\n"
  },
  {
    "path": "crates/smoketests/modules/fail-initial-publish-fixed/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-fail-initial-publish-fixed\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/fail-initial-publish-fixed/src/lib.rs",
    "content": "use spacetimedb::{client_visibility_filter, Filter};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[client_visibility_filter]\nconst HIDE_PEOPLE_EXCEPT_ME: Filter = Filter::Sql(\"SELECT * FROM person WHERE name = 'me'\");\n"
  },
  {
    "path": "crates/smoketests/modules/filtering/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-filtering\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/filtering/src/lib.rs",
    "content": "use spacetimedb::{log, Identity, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    #[unique]\n    id: i32,\n\n    name: String,\n\n    #[unique]\n    nick: String,\n}\n\n#[spacetimedb::reducer]\npub fn insert_person(ctx: &ReducerContext, id: i32, name: String, nick: String) {\n    ctx.db.person().insert(Person { id, name, nick });\n}\n\n#[spacetimedb::reducer]\npub fn insert_person_twice(ctx: &ReducerContext, id: i32, name: String, nick: String) {\n    // We'd like to avoid an error due to a set-semantic error.\n    let name2 = format!(\"{name}2\");\n    ctx.db.person().insert(Person {\n        id,\n        name,\n        nick: nick.clone(),\n    });\n    match ctx.db.person().try_insert(Person {\n        id,\n        name: name2,\n        nick: nick.clone(),\n    }) {\n        Ok(_) => {}\n        Err(_) => {\n            log::info!(\"UNIQUE CONSTRAINT VIOLATION ERROR: id = {}, nick = {}\", id, nick)\n        }\n    }\n}\n\n#[spacetimedb::reducer]\npub fn delete_person(ctx: &ReducerContext, id: i32) {\n    ctx.db.person().id().delete(&id);\n}\n\n#[spacetimedb::reducer]\npub fn find_person(ctx: &ReducerContext, id: i32) {\n    match ctx.db.person().id().find(&id) {\n        Some(person) => log::info!(\"UNIQUE FOUND: id {}: {}\", id, person.name),\n        None => log::info!(\"UNIQUE NOT FOUND: id {}\", id),\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_person_read_only(ctx: &ReducerContext, id: i32) {\n    let ctx = ctx.as_read_only();\n    match ctx.db.person().id().find(&id) {\n        Some(person) => log::info!(\"UNIQUE FOUND: id {}: {}\", id, person.name),\n        None => log::info!(\"UNIQUE NOT FOUND: id {}\", id),\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_person_by_name(ctx: &ReducerContext, name: String) {\n    for person in ctx.db.person().iter().filter(|p| p.name == name) {\n        log::info!(\"UNIQUE FOUND: id {}: {} aka {}\", person.id, person.name, person.nick);\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_person_by_nick(ctx: &ReducerContext, nick: String) {\n    match ctx.db.person().nick().find(&nick) {\n        Some(person) => log::info!(\"UNIQUE FOUND: id {}: {}\", person.id, person.nick),\n        None => log::info!(\"UNIQUE NOT FOUND: nick {}\", nick),\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_person_by_nick_read_only(ctx: &ReducerContext, nick: String) {\n    let ctx = ctx.as_read_only();\n    match ctx.db.person().nick().find(&nick) {\n        Some(person) => log::info!(\"UNIQUE FOUND: id {}: {}\", person.id, person.nick),\n        None => log::info!(\"UNIQUE NOT FOUND: nick {}\", nick),\n    }\n}\n\n#[spacetimedb::table(accessor = nonunique_person)]\npub struct NonuniquePerson {\n    #[index(btree)]\n    id: i32,\n    name: String,\n    is_human: bool,\n}\n\n#[spacetimedb::reducer]\npub fn insert_nonunique_person(ctx: &ReducerContext, id: i32, name: String, is_human: bool) {\n    ctx.db.nonunique_person().insert(NonuniquePerson { id, name, is_human });\n}\n\n#[spacetimedb::reducer]\npub fn find_nonunique_person(ctx: &ReducerContext, id: i32) {\n    for person in ctx.db.nonunique_person().id().filter(&id) {\n        log::info!(\"NONUNIQUE FOUND: id {}: {}\", id, person.name)\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_nonunique_person_read_only(ctx: &ReducerContext, id: i32) {\n    let ctx = ctx.as_read_only();\n    for person in ctx.db.nonunique_person().id().filter(&id) {\n        log::info!(\"NONUNIQUE FOUND: id {}: {}\", id, person.name)\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_nonunique_humans(ctx: &ReducerContext) {\n    for person in ctx.db.nonunique_person().iter().filter(|p| p.is_human) {\n        log::info!(\"HUMAN FOUND: id {}: {}\", person.id, person.name);\n    }\n}\n\n#[spacetimedb::reducer]\npub fn find_nonunique_non_humans(ctx: &ReducerContext) {\n    for person in ctx.db.nonunique_person().iter().filter(|p| !p.is_human) {\n        log::info!(\"NON-HUMAN FOUND: id {}: {}\", person.id, person.name);\n    }\n}\n\n// Ensure that [Identity] is filterable and a legal unique column.\n#[spacetimedb::table(accessor = identified_person)]\nstruct IdentifiedPerson {\n    #[unique]\n    identity: Identity,\n    name: String,\n}\n\nfn identify(id_number: u64) -> Identity {\n    let mut bytes = [0u8; 32];\n    bytes[..8].clone_from_slice(&id_number.to_le_bytes());\n    Identity::from_byte_array(bytes)\n}\n\n#[spacetimedb::reducer]\nfn insert_identified_person(ctx: &ReducerContext, id_number: u64, name: String) {\n    let identity = identify(id_number);\n    ctx.db.identified_person().insert(IdentifiedPerson { identity, name });\n}\n\n#[spacetimedb::reducer]\nfn find_identified_person(ctx: &ReducerContext, id_number: u64) {\n    let identity = identify(id_number);\n    match ctx.db.identified_person().identity().find(&identity) {\n        Some(person) => log::info!(\"IDENTIFIED FOUND: {}\", person.name),\n        None => log::info!(\"IDENTIFIED NOT FOUND\"),\n    }\n}\n\n// Ensure that indices on non-unique columns behave as we expect.\n#[spacetimedb::table(accessor = indexed_person)]\nstruct IndexedPerson {\n    #[unique]\n    id: i32,\n    given_name: String,\n    #[index(btree)]\n    surname: String,\n}\n\n#[spacetimedb::reducer]\nfn insert_indexed_person(ctx: &ReducerContext, id: i32, given_name: String, surname: String) {\n    ctx.db.indexed_person().insert(IndexedPerson {\n        id,\n        given_name,\n        surname,\n    });\n}\n\n#[spacetimedb::reducer]\nfn delete_indexed_person(ctx: &ReducerContext, id: i32) {\n    ctx.db.indexed_person().id().delete(&id);\n}\n\n#[spacetimedb::reducer]\nfn find_indexed_people(ctx: &ReducerContext, surname: String) {\n    for person in ctx.db.indexed_person().surname().filter(&surname) {\n        log::info!(\n            \"INDEXED FOUND: id {}: {}, {}\",\n            person.id,\n            person.surname,\n            person.given_name\n        );\n    }\n}\n\n#[spacetimedb::reducer]\nfn find_indexed_people_read_only(ctx: &ReducerContext, surname: String) {\n    let ctx = ctx.as_read_only();\n    for person in ctx.db.indexed_person().surname().filter(&surname) {\n        log::info!(\n            \"INDEXED FOUND: id {}: {}, {}\",\n            person.id,\n            person.surname,\n            person.given_name\n        );\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/modules/hotswap-basic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-hotswap-basic\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/hotswap-basic/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { id: 0, name });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/hotswap-updated/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-hotswap-updated\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/hotswap-updated/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { id: 0, name });\n}\n\n#[spacetimedb::table(accessor = pet, public)]\npub struct Pet {\n    #[primary_key]\n    species: String,\n}\n\n#[spacetimedb::reducer]\npub fn add_pet(ctx: &ReducerContext, species: String) {\n    ctx.db.pet().insert(Pet { species });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/logs-level-filter/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-logs-level-filter\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/logs-level-filter/src/lib.rs",
    "content": "use spacetimedb::ReducerContext;\n\n#[spacetimedb::reducer]\npub fn log_all_levels(_ctx: &ReducerContext) {\n    log::trace!(\"msg-trace\");\n    log::debug!(\"msg-debug\");\n    log::info!(\"msg-info\");\n    log::warn!(\"msg-warn\");\n    log::error!(\"msg-error\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/module-nested-op/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-module-nested-op\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/module-nested-op/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = account)]\npub struct Account {\n    name: String,\n    #[unique]\n    id: i32,\n}\n\n#[spacetimedb::table(accessor = friends)]\npub struct Friends {\n    friend_1: i32,\n    friend_2: i32,\n}\n\n#[spacetimedb::reducer]\npub fn create_account(ctx: &ReducerContext, account_id: i32, name: String) {\n    ctx.db.account().insert(Account { id: account_id, name });\n}\n\n#[spacetimedb::reducer]\npub fn add_friend(ctx: &ReducerContext, my_id: i32, their_id: i32) {\n    // Make sure our friend exists\n    for account in ctx.db.account().iter() {\n        if account.id == their_id {\n            ctx.db.friends().insert(Friends {\n                friend_1: my_id,\n                friend_2: their_id,\n            });\n            return;\n        }\n    }\n}\n\n#[spacetimedb::reducer]\npub fn say_friends(ctx: &ReducerContext) {\n    for friendship in ctx.db.friends().iter() {\n        let friend1 = ctx.db.account().id().find(&friendship.friend_1).unwrap();\n        let friend2 = ctx.db.account().id().find(&friendship.friend_2).unwrap();\n        log::info!(\"{} is friends with {}\", friend1.name, friend2.name);\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/modules/modules-add-table/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-modules-add-table\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/modules-add-table/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    name: String,\n}\n\n#[spacetimedb::table(accessor = pets)]\npub struct Pet {\n    species: String,\n}\n\n#[spacetimedb::reducer]\npub fn are_we_updated_yet(_ctx: &ReducerContext) {\n    log::info!(\"MODULE UPDATED\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/modules-basic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-modules-basic\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/modules-basic/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { id: 0, name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/modules-breaking/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-modules-breaking\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/modules-breaking/src/lib.rs",
    "content": "#[spacetimedb::table(accessor = person)]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n    name: String,\n    age: u8,\n}\n"
  },
  {
    "path": "crates/smoketests/modules/namespaces/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-namespaces\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/namespaces/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(_ctx: &ReducerContext) {\n    // Called when the module is initially published\n}\n\n#[spacetimedb::reducer(client_connected)]\npub fn identity_connected(_ctx: &ReducerContext) {\n    // Called everytime a new client connects\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn identity_disconnected(_ctx: &ReducerContext) {\n    // Called everytime a client disconnects\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/new-user-flow/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-new-user-flow\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/new-user-flow/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/panic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-panic\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/panic/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext};\nuse std::cell::RefCell;\n\nthread_local! {\n    static X: RefCell<u32> = RefCell::new(0);\n}\n#[spacetimedb::reducer]\nfn first(_ctx: &ReducerContext) {\n    X.with(|x| {\n        let _x = x.borrow_mut();\n        panic!()\n    })\n}\n#[spacetimedb::reducer]\nfn second(_ctx: &ReducerContext) {\n    X.with(|x| *x.borrow_mut());\n    log::info!(\"Test Passed\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/panic-error/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-panic-error\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/panic-error/src/lib.rs",
    "content": "use spacetimedb::ReducerContext;\n\n#[spacetimedb::reducer]\nfn fail(_ctx: &ReducerContext) -> Result<(), String> {\n    Err(\"oopsie :(\".into())\n}\n"
  },
  {
    "path": "crates/smoketests/modules/permissions-lifecycle/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-permissions-lifecycle\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/permissions-lifecycle/src/lib.rs",
    "content": "#[spacetimedb::reducer(init)]\nfn lifecycle_init(_ctx: &spacetimedb::ReducerContext) {}\n\n#[spacetimedb::reducer(client_connected)]\nfn lifecycle_client_connected(_ctx: &spacetimedb::ReducerContext) {}\n\n#[spacetimedb::reducer(client_disconnected)]\nfn lifecycle_client_disconnected(_ctx: &spacetimedb::ReducerContext) {}\n"
  },
  {
    "path": "crates/smoketests/modules/permissions-private/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-permissions-private\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/permissions-private/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = secret, private)]\npub struct Secret {\n    answer: u8,\n}\n\n#[spacetimedb::table(accessor = common_knowledge, public)]\npub struct CommonKnowledge {\n    thing: String,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    ctx.db.secret().insert(Secret { answer: 42 });\n}\n\n#[spacetimedb::reducer]\npub fn do_thing(ctx: &ReducerContext, thing: String) {\n    ctx.db.secret().insert(Secret { answer: 20 });\n    ctx.db.common_knowledge().insert(CommonKnowledge { thing });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/pg-wire/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-pg-wire\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/pg-wire/src/lib.rs",
    "content": "use spacetimedb::sats::{i256, u256};\nuse spacetimedb::{ConnectionId, Identity, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, Uuid};\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = t_ints, public)]\npub struct TInts {\n    i8: i8,\n    i16: i16,\n    i32: i32,\n    i64: i64,\n    i128: i128,\n    i256: i256,\n}\n\n#[spacetimedb::table(accessor = t_ints_tuple, public)]\npub struct TIntsTuple {\n    tuple: TInts,\n}\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = t_uints, public)]\npub struct TUints {\n    u8: u8,\n    u16: u16,\n    u32: u32,\n    u64: u64,\n    u128: u128,\n    u256: u256,\n}\n\n#[spacetimedb::table(accessor = t_uints_tuple, public)]\npub struct TUintsTuple {\n    tuple: TUints,\n}\n\n#[derive(Clone)]\n#[spacetimedb::table(accessor = t_others, public)]\npub struct TOthers {\n    bool: bool,\n    f32: f32,\n    f64: f64,\n    str: String,\n    bytes: Vec<u8>,\n    identity: Identity,\n    connection_id: ConnectionId,\n    timestamp: Timestamp,\n    duration: TimeDuration,\n    uuid: Uuid,\n}\n\n#[spacetimedb::table(accessor = t_others_tuple, public)]\npub struct TOthersTuple {\n    tuple: TOthers,\n}\n\n#[derive(SpacetimeType, Debug, Clone, Copy)]\npub enum Action {\n    Inactive,\n    Active,\n}\n\n#[derive(SpacetimeType, Debug, Clone, Copy)]\npub enum Color {\n    Gray(u8),\n}\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = t_simple_enum, public)]\npub struct TSimpleEnum {\n    id: u32,\n    action: Action,\n}\n\n#[spacetimedb::table(accessor = t_enum, public)]\npub struct TEnum {\n    id: u32,\n    color: Color,\n}\n\n#[spacetimedb::table(accessor = t_nested, public)]\npub struct TNested {\n    en: TEnum,\n    se: TSimpleEnum,\n    ints: TInts,\n}\n\n#[derive(Clone)]\n#[spacetimedb::table(accessor = t_enums)]\npub struct TEnums {\n    bool_opt: Option<bool>,\n    bool_result: Result<bool, String>,\n    action: Action,\n}\n\n#[spacetimedb::table(accessor = t_enums_tuple)]\npub struct TEnumsTuple {\n    tuple: TEnums,\n}\n\n#[spacetimedb::reducer]\npub fn test(ctx: &ReducerContext) {\n    let tuple = TInts {\n        i8: -25,\n        i16: -3224,\n        i32: -23443,\n        i64: -2344353,\n        i128: -234434897853,\n        i256: (-234434897853i128).into(),\n    };\n    let ints = tuple;\n    ctx.db.t_ints().insert(tuple);\n    ctx.db.t_ints_tuple().insert(TIntsTuple { tuple });\n\n    let tuple = TUints {\n        u8: 105,\n        u16: 1050,\n        u32: 83892,\n        u64: 48937498,\n        u128: 4378528978889,\n        u256: 4378528978889u128.into(),\n    };\n    ctx.db.t_uints().insert(tuple);\n    ctx.db.t_uints_tuple().insert(TUintsTuple { tuple });\n\n    let tuple = TOthers {\n        bool: true,\n        f32: 594806.58906,\n        f64: -3454353.345389043278459,\n        str: \"This is spacetimedb\".to_string(),\n        bytes: vec![1, 2, 3, 4, 5, 6, 7],\n        identity: Identity::ONE,\n        connection_id: ConnectionId::ZERO,\n        timestamp: Timestamp::UNIX_EPOCH,\n        duration: TimeDuration::from_micros(1000 * 10000),\n        uuid: Uuid::NIL,\n    };\n    ctx.db.t_others().insert(tuple.clone());\n    ctx.db.t_others_tuple().insert(TOthersTuple { tuple });\n\n    ctx.db.t_simple_enum().insert(TSimpleEnum {\n        id: 1,\n        action: Action::Inactive,\n    });\n    ctx.db.t_simple_enum().insert(TSimpleEnum {\n        id: 2,\n        action: Action::Active,\n    });\n\n    ctx.db.t_enum().insert(TEnum {\n        id: 1,\n        color: Color::Gray(128),\n    });\n\n    ctx.db.t_nested().insert(TNested {\n        en: TEnum {\n            id: 1,\n            color: Color::Gray(128),\n        },\n        se: TSimpleEnum {\n            id: 2,\n            action: Action::Active,\n        },\n        ints,\n    });\n\n    let tuple = TEnums {\n        bool_opt: Some(true),\n        bool_result: Ok(false),\n        action: Action::Active,\n    };\n\n    ctx.db.t_enums().insert(tuple.clone());\n    ctx.db.t_enums_tuple().insert(TEnumsTuple { tuple });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/restart-connected-client/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-restart-connected-client\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/restart-connected-client/src/lib.rs",
    "content": "use log::info;\nuse spacetimedb::{ConnectionId, Identity, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = connected_client)]\npub struct ConnectedClient {\n    identity: Identity,\n    connection_id: ConnectionId,\n}\n\n#[spacetimedb::reducer(client_connected)]\nfn on_connect(ctx: &ReducerContext) {\n    ctx.db.connected_client().insert(ConnectedClient {\n        identity: ctx.sender(),\n        connection_id: ctx.connection_id().expect(\"sender connection id unset\"),\n    });\n}\n\n#[spacetimedb::reducer(client_disconnected)]\nfn on_disconnect(ctx: &ReducerContext) {\n    let sender_identity = &ctx.sender();\n    let connection_id = ctx.connection_id();\n    let sender_connection_id = connection_id.as_ref().expect(\"sender connection id unset\");\n    let match_client =\n        |row: &ConnectedClient| &row.identity == sender_identity && &row.connection_id == sender_connection_id;\n    if let Some(client) = ctx.db.connected_client().iter().find(match_client) {\n        ctx.db.connected_client().delete(client);\n    }\n}\n\n#[spacetimedb::reducer]\nfn print_num_connected(ctx: &ReducerContext) {\n    let n = ctx.db.connected_client().count();\n    info!(\"CONNECTED CLIENTS: {n}\")\n}\n"
  },
  {
    "path": "crates/smoketests/modules/restart-person/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-restart-person\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/restart-person/src/lib.rs",
    "content": "use spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person, index(accessor = name_idx, btree(columns = [name])))]\npub struct Person {\n    #[primary_key]\n    #[auto_inc]\n    id: u32,\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { id: 0, name });\n}\n\n#[spacetimedb::reducer]\npub fn say_hello(ctx: &ReducerContext) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"Hello, {}!\", person.name);\n    }\n    log::info!(\"Hello, World!\");\n}\n"
  },
  {
    "path": "crates/smoketests/modules/rls/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-rls\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/rls/src/lib.rs",
    "content": "use spacetimedb::{Identity, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = users, public)]\npub struct Users {\n    name: String,\n    identity: Identity,\n}\n\n#[spacetimedb::client_visibility_filter]\nconst USER_FILTER: spacetimedb::Filter = spacetimedb::Filter::Sql(\"SELECT * FROM users WHERE identity = :sender\");\n\n#[spacetimedb::reducer]\npub fn add_user(ctx: &ReducerContext, name: String) {\n    ctx.db.users().insert(Users {\n        name,\n        identity: ctx.sender(),\n    });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/rls-no-filter/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-rls-no-filter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/rls-no-filter/src/lib.rs",
    "content": "use spacetimedb::{Identity, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = users, public)]\npub struct Users {\n    name: String,\n    identity: Identity,\n}\n\n#[spacetimedb::reducer]\npub fn add_user(ctx: &ReducerContext, name: String) {\n    ctx.db.users().insert(Users {\n        name,\n        identity: ctx.sender(),\n    });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/rls-with-filter/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-rls-with-filter\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/rls-with-filter/src/lib.rs",
    "content": "use spacetimedb::{Identity, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = users, public)]\npub struct Users {\n    name: String,\n    identity: Identity,\n}\n\n#[spacetimedb::client_visibility_filter]\nconst USER_FILTER: spacetimedb::Filter = spacetimedb::Filter::Sql(\"SELECT * FROM users WHERE identity = :sender\");\n\n#[spacetimedb::reducer]\npub fn add_user(ctx: &ReducerContext, name: String) {\n    ctx.db.users().insert(Users {\n        name,\n        identity: ctx.sender(),\n    });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-cancel/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-schedule-cancel\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-cancel/src/lib.rs",
    "content": "use spacetimedb::{duration, log, ReducerContext, Table};\n\n#[spacetimedb::reducer(init)]\nfn init(ctx: &ReducerContext) {\n    let schedule = ctx.db.scheduled_reducer_args().insert(ScheduledReducerArgs {\n        num: 1,\n        scheduled_id: 0,\n        scheduled_at: duration!(100ms).into(),\n    });\n    ctx.db\n        .scheduled_reducer_args()\n        .scheduled_id()\n        .delete(&schedule.scheduled_id);\n\n    let schedule = ctx.db.scheduled_reducer_args().insert(ScheduledReducerArgs {\n        num: 2,\n        scheduled_id: 0,\n        scheduled_at: duration!(1000ms).into(),\n    });\n    do_cancel(ctx, schedule.scheduled_id);\n}\n\n#[spacetimedb::table(accessor = scheduled_reducer_args, public, scheduled(reducer))]\npub struct ScheduledReducerArgs {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    scheduled_at: spacetimedb::ScheduleAt,\n    num: i32,\n}\n\n#[spacetimedb::reducer]\nfn do_cancel(ctx: &ReducerContext, schedule_id: u64) {\n    ctx.db.scheduled_reducer_args().scheduled_id().delete(&schedule_id);\n}\n\n#[spacetimedb::reducer]\nfn reducer(_ctx: &ReducerContext, args: ScheduledReducerArgs) {\n    log::info!(\"the reducer ran: {}\", args.num);\n}\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-procedure/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-schedule-procedure\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-procedure/src/lib.rs",
    "content": "use spacetimedb::{duration, log, ProcedureContext, Query, ReducerContext, Table, Timestamp, ViewContext};\n\n#[spacetimedb::table(accessor = scheduled_table, public, scheduled(my_procedure, at = sched_at))]\npub struct ScheduledTable {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    sched_at: spacetimedb::ScheduleAt,\n    prev: Timestamp,\n}\n\n#[spacetimedb::view(accessor = scheduled_view, public)]\nfn scheduled_view(ctx: &ViewContext) -> impl Query<ScheduledTable> {\n    ctx.from.scheduled_table().build()\n}\n\n#[spacetimedb::reducer]\nfn schedule_procedure(ctx: &ReducerContext) {\n    ctx.db.scheduled_table().insert(ScheduledTable {\n        prev: Timestamp::from_micros_since_unix_epoch(0),\n        scheduled_id: 2,\n        sched_at: Timestamp::from_micros_since_unix_epoch(0).into(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn schedule_repeated_procedure(ctx: &ReducerContext) {\n    ctx.db.scheduled_table().insert(ScheduledTable {\n        prev: Timestamp::from_micros_since_unix_epoch(0),\n        scheduled_id: 1,\n        sched_at: duration!(100ms).into(),\n    });\n}\n\n#[spacetimedb::procedure]\npub fn my_procedure(ctx: &mut ProcedureContext, arg: ScheduledTable) {\n    log::info!(\n        \"Invoked: ts={:?}, delta={:?}\",\n        ctx.timestamp,\n        ctx.timestamp.duration_since(arg.prev)\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-subscribe/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-schedule-subscribe\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-subscribe/src/lib.rs",
    "content": "use spacetimedb::{duration, log, Identity, Query, ReducerContext, Table, Timestamp, ViewContext};\n\n#[spacetimedb::table(accessor = scheduled_table, public, scheduled(my_reducer, at = sched_at))]\npub struct ScheduledTable {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    sched_at: spacetimedb::ScheduleAt,\n    prev: Timestamp,\n}\n\n#[spacetimedb::table(accessor = failing_scheduled_table, public, scheduled(failing_reducer, at = sched_at))]\npub struct FailingScheduledTable {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    sched_at: spacetimedb::ScheduleAt,\n    prev: Timestamp,\n}\n\n#[spacetimedb::table(accessor = player_entity, public)]\npub struct PlayerEntity {\n    #[primary_key]\n    entity_id: u64,\n    owner: Identity,\n}\n\n#[spacetimedb::view(accessor = scheduled_view, public)]\nfn scheduled_view(ctx: &ViewContext) -> impl Query<ScheduledTable> {\n    ctx.from.scheduled_table().build()\n}\n\n#[spacetimedb::view(accessor = scheduled_sender_view, public)]\nfn scheduled_sender_view(ctx: &ViewContext) -> impl Query<ScheduledTable> {\n    ctx.from\n        .player_entity()\n        .r#where(|pe| pe.owner.eq(ctx.sender()))\n        .right_semijoin(ctx.from.scheduled_table(), |pe, st| pe.entity_id.eq(st.scheduled_id))\n        .build()\n}\n\n#[spacetimedb::view(accessor = failing_scheduled_sender_view, public)]\nfn failing_scheduled_sender_view(ctx: &ViewContext) -> impl Query<FailingScheduledTable> {\n    ctx.from\n        .player_entity()\n        .r#where(|pe| pe.owner.eq(ctx.sender()))\n        .right_semijoin(ctx.from.failing_scheduled_table(), |pe, st| {\n            pe.entity_id.eq(st.scheduled_id)\n        })\n        .build()\n}\n\n#[spacetimedb::reducer]\nfn schedule_reducer(ctx: &ReducerContext) {\n    ctx.db.scheduled_table().insert(ScheduledTable {\n        prev: Timestamp::from_micros_since_unix_epoch(0),\n        scheduled_id: 2,\n        sched_at: Timestamp::from_micros_since_unix_epoch(0).into(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn schedule_failing_reducer(ctx: &ReducerContext) {\n    ctx.db.failing_scheduled_table().insert(FailingScheduledTable {\n        prev: Timestamp::from_micros_since_unix_epoch(0),\n        scheduled_id: 3,\n        sched_at: Timestamp::from_micros_since_unix_epoch(0).into(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn schedule_repeated_reducer(ctx: &ReducerContext) {\n    ctx.db.scheduled_table().insert(ScheduledTable {\n        prev: Timestamp::from_micros_since_unix_epoch(0),\n        scheduled_id: 1,\n        sched_at: duration!(100ms).into(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn seed_player_entity(ctx: &ReducerContext, entity_id: u64) {\n    ctx.db.player_entity().entity_id().delete(&entity_id);\n    ctx.db.player_entity().insert(PlayerEntity {\n        entity_id,\n        owner: ctx.sender(),\n    });\n}\n\n#[spacetimedb::reducer]\npub fn my_reducer(ctx: &ReducerContext, arg: ScheduledTable) {\n    log::info!(\n        \"Invoked: ts={:?}, delta={:?}\",\n        ctx.timestamp,\n        ctx.timestamp.duration_since(arg.prev)\n    );\n}\n\n#[spacetimedb::reducer]\npub fn failing_reducer(_ctx: &ReducerContext, _arg: FailingScheduledTable) -> Result<(), String> {\n    Err(\"scheduled reducer failed\".into())\n}\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-volatile/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-schedule-volatile\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/schedule-volatile/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(accessor = my_table, public)]\npub struct MyTable {\n    x: String,\n}\n\n#[spacetimedb::reducer]\nfn do_schedule(_ctx: &ReducerContext) {\n    spacetimedb::volatile_nonatomic_schedule_immediate!(do_insert(\"hello\".to_owned()));\n}\n\n#[spacetimedb::reducer]\nfn do_insert(ctx: &ReducerContext, x: String) {\n    ctx.db.my_table().insert(MyTable { x });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/sql-format/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-sql-format\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/sql-format/src/lib.rs",
    "content": "use spacetimedb::sats::{i256, u256};\nuse spacetimedb::{\n    ConnectionId, Identity, Query, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, Uuid, ViewContext,\n};\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = t_ints)]\npub struct TInts {\n    i8: i8,\n    i16: i16,\n    i32: i32,\n    i64: i64,\n    i128: i128,\n    i256: i256,\n}\n\n#[spacetimedb::table(accessor = t_ints_tuple)]\npub struct TIntsTuple {\n    tuple: TInts,\n}\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = t_uints)]\npub struct TUints {\n    u8: u8,\n    u16: u16,\n    u32: u32,\n    u64: u64,\n    u128: u128,\n    u256: u256,\n}\n\n#[spacetimedb::table(accessor = t_uints_tuple)]\npub struct TUintsTuple {\n    tuple: TUints,\n}\n\n#[derive(Clone)]\n#[spacetimedb::table(accessor = t_others)]\npub struct TOthers {\n    bool: bool,\n    f32: f32,\n    f64: f64,\n    str: String,\n    bytes: Vec<u8>,\n    identity: Identity,\n    connection_id: ConnectionId,\n    timestamp: Timestamp,\n    duration: TimeDuration,\n    uuid: Uuid,\n}\n\n#[spacetimedb::table(accessor = t_others_tuple)]\npub struct TOthersTuple {\n    tuple: TOthers,\n}\n\n#[derive(SpacetimeType, Debug, Clone, Copy)]\npub enum Action {\n    Inactive,\n    Active,\n}\n\n#[derive(Clone)]\n#[spacetimedb::table(accessor = t_enums)]\npub struct TEnums {\n    bool_opt: Option<bool>,\n    bool_result: Result<bool, String>,\n    action: Action,\n}\n\n#[spacetimedb::table(accessor = t_enums_tuple)]\npub struct TEnumsTuple {\n    tuple: TEnums,\n}\n\n#[spacetimedb::reducer]\npub fn test(ctx: &ReducerContext) {\n    let tuple = TInts {\n        i8: -25,\n        i16: -3224,\n        i32: -23443,\n        i64: -2344353,\n        i128: -234434897853,\n        i256: (-234434897853i128).into(),\n    };\n    ctx.db.t_ints().insert(tuple);\n    ctx.db.t_ints_tuple().insert(TIntsTuple { tuple });\n\n    let tuple = TUints {\n        u8: 105,\n        u16: 1050,\n        u32: 83892,\n        u64: 48937498,\n        u128: 4378528978889,\n        u256: 4378528978889u128.into(),\n    };\n    ctx.db.t_uints().insert(tuple);\n    ctx.db.t_uints_tuple().insert(TUintsTuple { tuple });\n\n    let tuple = TOthers {\n        bool: true,\n        f32: 594806.58906,\n        f64: -3454353.345389043278459,\n        str: \"This is spacetimedb\".to_string(),\n        bytes: vec![1, 2, 3, 4, 5, 6, 7],\n        identity: Identity::ONE,\n        connection_id: ConnectionId::ZERO,\n        timestamp: Timestamp::UNIX_EPOCH,\n        duration: TimeDuration::ZERO,\n        uuid: Uuid::NIL,\n    };\n    ctx.db.t_others().insert(tuple.clone());\n    ctx.db.t_others_tuple().insert(TOthersTuple { tuple });\n\n    let tuple = TEnums {\n        bool_opt: Some(true),\n        bool_result: Ok(false),\n        action: Action::Active,\n    };\n\n    ctx.db.t_enums().insert(tuple.clone());\n    ctx.db.t_enums_tuple().insert(TEnumsTuple { tuple });\n}\n\n#[spacetimedb::table(accessor = accessor_table, name = \"canonical_table\", public)]\npub struct AccessorRow {\n    #[primary_key]\n    id: u32,\n    accessor_value1: u32,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n    ctx.db.accessor_table().insert(AccessorRow {\n        id: 1,\n        accessor_value1: 7,\n    });\n}\n\n#[spacetimedb::view(accessor = accessor_filtered, name = \"canonical_filtered\", public)]\nfn accessor_filtered(ctx: &ViewContext) -> impl Query<AccessorRow> {\n    ctx.from.accessor_table().r#where(|r| r.accessor_value1.eq(7))\n}\n"
  },
  {
    "path": "crates/smoketests/modules/upload-module-2/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-upload-module-2\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/upload-module-2/src/lib.rs",
    "content": "use spacetimedb::{duration, log, ReducerContext, Table, Timestamp};\n\n#[spacetimedb::table(accessor = scheduled_message, public, scheduled(my_repeating_reducer))]\npub struct ScheduledMessage {\n    #[primary_key]\n    #[auto_inc]\n    scheduled_id: u64,\n    scheduled_at: spacetimedb::ScheduleAt,\n    prev: Timestamp,\n}\n\n#[spacetimedb::reducer(init)]\nfn init(ctx: &ReducerContext) {\n    ctx.db.scheduled_message().insert(ScheduledMessage {\n        prev: ctx.timestamp,\n        scheduled_id: 0,\n        scheduled_at: duration!(100ms).into(),\n    });\n}\n\n#[spacetimedb::reducer]\npub fn my_repeating_reducer(ctx: &ReducerContext, arg: ScheduledMessage) {\n    log::info!(\n        \"Invoked: ts={:?}, delta={:?}\",\n        ctx.timestamp,\n        ctx.timestamp.duration_since(arg.prev)\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-auto-migrate/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-auto-migrate\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-auto-migrate/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(ctx: &ViewContext) -> Option<PlayerState> {\n    ctx.db.player_state().id().find(1u64)\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-auto-migrate-updated/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-auto-migrate-updated\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-auto-migrate-updated/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(ctx: &ViewContext) -> Option<PlayerState> {\n    ctx.db.player_state().id().find(2u64)\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-basic/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-basic\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-basic/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(ctx: &ViewContext) -> Option<PlayerState> {\n    ctx.db.player_state().id().find(0u64)\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-broken-namespace/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-broken-namespace\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-broken-namespace/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::view(accessor = person, public)]\npub fn person(ctx: &ViewContext) -> Option<Person> {\n    None\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-broken-return-type/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-broken-return-type\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-broken-return-type/src/lib.rs",
    "content": "use spacetimedb::{SpacetimeType, ViewContext};\n\n#[derive(SpacetimeType)]\npub enum ABC {\n    A,\n    B,\n    C,\n}\n\n#[spacetimedb::view(accessor = person, public)]\npub fn person(ctx: &ViewContext) -> Option<ABC> {\n    None\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-callable/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-callable\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-callable/src/lib.rs",
    "content": "use spacetimedb::{ReducerContext, Table, ViewContext};\n\n#[spacetimedb::table(accessor = items, public)]\npub struct Item {\n    value: u8,\n}\n\nmod foo {\n    use super::*;\n\n    #[spacetimedb::view(accessor = bar, public)]\n    pub(crate) fn bar(_ctx: &ViewContext) -> Option<Item> {\n        Some(Item { value: 7 })\n    }\n}\n\n#[spacetimedb::reducer]\npub fn baz(ctx: &ReducerContext) {\n    if let Some(item) = foo::bar(&ctx.as_read_only()) {\n        ctx.db.items().insert(item);\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-count/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-count\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-count/src/lib.rs",
    "content": "use spacetimedb::{reducer, table, view, AnonymousViewContext};\nuse spacetimedb::{ReducerContext, SpacetimeType, Table, ViewContext};\n\n#[table(accessor = item)]\npub struct Item {\n    #[primary_key]\n    id: u32,\n    value: u32,\n}\n\n#[derive(SpacetimeType)]\npub struct ItemCount {\n    count: u64,\n}\n\n#[view(accessor = sender_table_count, public)]\npub fn sender_table_count(ctx: &ViewContext) -> Option<ItemCount> {\n    Some(ItemCount {\n        count: ctx.db.item().count(),\n    })\n}\n\n#[view(accessor = anon_table_count, public)]\npub fn anon_table_count(ctx: &AnonymousViewContext) -> Option<ItemCount> {\n    Some(ItemCount {\n        count: ctx.db.item().count(),\n    })\n}\n\n#[reducer]\npub fn insert_item(ctx: &ReducerContext, id: u32, value: u32) {\n    ctx.db.item().insert(Item { id, value });\n}\n\n#[reducer]\npub fn replace_item(ctx: &ReducerContext, id: u32, value: u32) {\n    ctx.db.item().id().delete(&id);\n    ctx.db.item().insert(Item { id, value });\n}\n\n#[reducer]\npub fn delete_item(ctx: &ReducerContext, id: u32) {\n    ctx.db.item().id().delete(&id);\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-drop-view/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-drop-view\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-drop-view/src/lib.rs",
    "content": "#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-query/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-query\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-query/src/lib.rs",
    "content": "use spacetimedb::{AnonymousViewContext, Identity, Query, ReducerContext, Table, ViewContext};\n\n#[spacetimedb::table(accessor = user, public)]\npub struct User {\n    #[primary_key]\n    identity: u8,\n    name: String,\n    online: bool,\n}\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    #[primary_key]\n    identity: u8,\n    name: String,\n    #[index(btree)]\n    age: u8,\n}\n\n#[spacetimedb::table(accessor = pk_join_lhs, public)]\npub struct LeftPkJoinSource {\n    #[primary_key]\n    id: u8,\n    ok: bool,\n    #[index(btree)]\n    identity: Identity,\n}\n\n#[spacetimedb::table(accessor = pk_join_rhs, public)]\npub struct RightPkJoinSource {\n    #[primary_key]\n    id: u8,\n    ok: bool,\n    #[index(btree)]\n    identity: Identity,\n}\n\n#[spacetimedb::reducer(init)]\nfn init(ctx: &ReducerContext) {\n    ctx.db.user().insert(User {\n        identity: 1,\n        name: \"Alice\".to_string(),\n        online: true,\n    });\n\n    ctx.db.user().insert(User {\n        identity: 2,\n        name: \"BOB\".to_string(),\n        online: false,\n    });\n\n    ctx.db.user().insert(User {\n        identity: 3,\n        name: \"POP\".to_string(),\n        online: false,\n    });\n\n    ctx.db.person().insert(Person {\n        identity: 1,\n        name: \"Alice\".to_string(),\n        age: 30,\n    });\n\n    ctx.db.person().insert(Person {\n        identity: 2,\n        name: \"BOB\".to_string(),\n        age: 20,\n    });\n}\n\n#[spacetimedb::view(accessor = online_users, public)]\nfn online_users(ctx: &ViewContext) -> impl Query<User> {\n    ctx.from.user().r#where(|c| c.online)\n}\n\n#[spacetimedb::view(accessor = online_users_age, public)]\nfn online_users_age(ctx: &ViewContext) -> impl Query<Person> {\n    ctx.from\n        .user()\n        .r#where(|u| u.online)\n        .right_semijoin(ctx.from.person(), |u, p| u.identity.eq(p.identity))\n}\n\n#[spacetimedb::view(accessor = offline_user_20_years_old, public)]\nfn offline_user_in_twienties(ctx: &ViewContext) -> impl Query<User> {\n    ctx.from\n        .person()\n        .filter(|p| p.age.eq(20))\n        .right_semijoin(ctx.from.user(), |p, u| p.identity.eq(u.identity))\n        .filter(|u| u.online.eq(false))\n}\n\n#[spacetimedb::view(accessor = users_whos_age_is_known, public)]\nfn users_whos_age_is_known(ctx: &ViewContext) -> impl Query<User> {\n    ctx.from\n        .user()\n        .left_semijoin(ctx.from.person(), |p, u| p.identity.eq(u.identity))\n}\n\n#[spacetimedb::view(accessor = users_who_are_above_20_and_below_30, public)]\nfn users_who_are_above_20_and_below_30(ctx: &ViewContext) -> impl Query<Person> {\n    ctx.from.person().r#where(|p| p.age.gt(20).and(p.age.lt(30)))\n}\n\n#[spacetimedb::view(accessor = users_who_are_above_eq_20_and_below_eq_30, public)]\nfn users_who_are_above_eq_20_and_below_eq_30(ctx: &ViewContext) -> impl Query<Person> {\n    ctx.from.person().r#where(|p| p.age.gte(20).and(p.age.lte(30)))\n}\n\n#[spacetimedb::reducer]\nfn update_pk_join_lhs(ctx: &ReducerContext, id: u8, ok: bool) {\n    ctx.db.pk_join_lhs().id().delete(&id);\n    ctx.db.pk_join_lhs().insert(LeftPkJoinSource {\n        id,\n        ok,\n        identity: ctx.sender(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn delete_pk_join_lhs(ctx: &ReducerContext, id: u8) {\n    ctx.db.pk_join_lhs().id().delete(&id);\n}\n\n#[spacetimedb::reducer]\nfn update_pk_join_rhs(ctx: &ReducerContext, id: u8, ok: bool) {\n    ctx.db.pk_join_rhs().id().delete(&id);\n    ctx.db.pk_join_rhs().insert(RightPkJoinSource {\n        id,\n        ok,\n        identity: ctx.sender(),\n    });\n}\n\n#[spacetimedb::reducer]\nfn delete_pk_join_rhs(ctx: &ReducerContext, id: u8) {\n    ctx.db.pk_join_rhs().id().delete(&id);\n}\n\n#[spacetimedb::view(accessor = pk_join_lhs_view, public)]\nfn pk_join_lhs_view(ctx: &AnonymousViewContext) -> impl Query<LeftPkJoinSource> {\n    ctx.from.pk_join_lhs()\n}\n\n#[spacetimedb::view(accessor = pk_join_rhs_view, public)]\nfn pk_join_rhs_view(ctx: &AnonymousViewContext) -> impl Query<RightPkJoinSource> {\n    ctx.from.pk_join_rhs()\n}\n\n#[spacetimedb::view(accessor = pk_join_lhs_sender_view, public)]\nfn pk_join_lhs_sender_view(ctx: &ViewContext) -> impl Query<LeftPkJoinSource> {\n    ctx.from.pk_join_lhs().filter(|row| row.identity.eq(ctx.sender()))\n}\n\n#[spacetimedb::view(accessor = pk_join_rhs_sender_view, public)]\nfn pk_join_rhs_sender_view(ctx: &ViewContext) -> impl Query<RightPkJoinSource> {\n    ctx.from.pk_join_rhs().filter(|row| row.identity.eq(ctx.sender()))\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-recovered/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-recovered\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-recovered/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(ctx: &ViewContext) -> Option<PlayerState> {\n    ctx.db.player_state().id().find(2u64)\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-sql/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-sql\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\nlog.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-sql/src/lib.rs",
    "content": "use spacetimedb::{AnonymousViewContext, ReducerContext, Table, ViewContext};\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\n#[spacetimedb::table(accessor = player_level)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[derive(Clone)]\n#[spacetimedb::table(accessor = player_info, index(accessor=age_level_index, btree(columns = [age, level])))]\npub struct PlayerInfo {\n    #[primary_key]\n    id: u64,\n    age: u64,\n    level: u64,\n}\n\n#[spacetimedb::reducer]\npub fn add_player_level(ctx: &ReducerContext, id: u64, level: u64) {\n    ctx.db.player_level().insert(PlayerState { id, level });\n}\n\n#[spacetimedb::reducer]\npub fn set_player_state(ctx: &ReducerContext, id: u64, level: u64) {\n    if let Some(mut row) = ctx.db.player_state().id().find(id) {\n        row.level = level;\n        ctx.db.player_state().id().update(row);\n    } else {\n        ctx.db.player_state().insert(PlayerState { id, level });\n    }\n}\n\n#[spacetimedb::view(accessor = my_player_and_level, public)]\npub fn my_player_and_level(ctx: &AnonymousViewContext) -> Option<PlayerState> {\n    ctx.db.player_level().id().find(0)\n}\n\n#[spacetimedb::view(accessor = player_and_level, public)]\npub fn player_and_level(ctx: &AnonymousViewContext) -> Vec<PlayerState> {\n    ctx.db.player_level().level().filter(2u64).collect()\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(ctx: &ViewContext) -> Option<PlayerState> {\n    log::info!(\"player view called\");\n    ctx.db.player_state().id().find(42)\n}\n\n#[spacetimedb::view(accessor = player_none, public)]\npub fn player_none(_ctx: &ViewContext) -> Option<PlayerState> {\n    None\n}\n\n#[spacetimedb::view(accessor = player_vec, public)]\npub fn player_vec(ctx: &ViewContext) -> Vec<PlayerState> {\n    let first = ctx.db.player_state().id().find(42).unwrap();\n    let second = PlayerState { id: 7, level: 3 };\n    vec![first, second]\n}\n\n#[spacetimedb::view(accessor = player_info_multi_index, public)]\npub fn player_info_view(ctx: &ViewContext) -> Option<PlayerInfo> {\n    log::info!(\"player_info called\");\n    ctx.db.player_info().age_level_index().filter((25u64, 7u64)).next()\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-subscribe/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-subscribe\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-subscribe/src/lib.rs",
    "content": "use spacetimedb::{Identity, ProcedureContext, ReducerContext, Table, ViewContext};\n\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    identity: Identity,\n    #[unique]\n    name: String,\n}\n\n#[spacetimedb::view(accessor = my_player, public)]\npub fn my_player(ctx: &ViewContext) -> Option<PlayerState> {\n    ctx.db.player_state().identity().find(ctx.sender())\n}\n\n#[spacetimedb::reducer]\npub fn insert_player(ctx: &ReducerContext, name: String) {\n    ctx.db.player_state().insert(PlayerState {\n        name,\n        identity: ctx.sender(),\n    });\n}\n\n#[spacetimedb::procedure]\npub fn insert_player_proc(ctx: &mut ProcedureContext, name: String) {\n    let sender = ctx.sender();\n    ctx.with_tx(|tx| {\n        tx.db.player_state().insert(PlayerState {\n            name: name.clone(),\n            identity: sender,\n        });\n    });\n}\n"
  },
  {
    "path": "crates/smoketests/modules/views-trapped/Cargo.toml",
    "content": "[package]\nname = \"smoketest-module-views-trapped\"\nversion = \"0.1.0\"\nedition = \"2021\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb.workspace = true\n"
  },
  {
    "path": "crates/smoketests/modules/views-trapped/src/lib.rs",
    "content": "use spacetimedb::ViewContext;\n\n#[derive(Copy, Clone)]\n#[spacetimedb::table(accessor = player_state)]\npub struct PlayerState {\n    #[primary_key]\n    id: u64,\n    #[index(btree)]\n    level: u64,\n}\n\n#[spacetimedb::view(accessor = player, public)]\npub fn player(_ctx: &ViewContext) -> Option<PlayerState> {\n    panic!(\"This view is trapped\")\n}\n"
  },
  {
    "path": "crates/smoketests/src/csharp.rs",
    "content": "use anyhow::{anyhow, bail, Context, Result};\nuse serde_json::Value;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::sync::OnceLock;\n\nconst PACKAGE_PROJECTS: [(&str, &str); 2] = [\n    (\"BSATN.Runtime\", \"SpacetimeDB.BSATN.Runtime\"),\n    (\"Runtime\", \"SpacetimeDB.Runtime\"),\n];\n\nconst REQUIRED_RUNTIME_PACKAGES: [&str; 2] = [\"SpacetimeDB.BSATN.Runtime\", \"SpacetimeDB.Runtime\"];\n\n#[derive(Debug)]\nstruct CsharpBuildEnv {\n    local_feed_dir: PathBuf,\n}\n\nstatic CSHARP_WORKLOAD_READY: OnceLock<Result<(), anyhow::Error>> = OnceLock::new();\nstatic CSHARP_BUILD_ENV: OnceLock<Result<CsharpBuildEnv, anyhow::Error>> = OnceLock::new();\n\n/// Normalizes a filesystem path for string-based comparisons in NuGet artifacts.\n///\n/// NuGet and `project.assets.json` can emit paths with platform-specific separators\n/// and optional trailing slashes; this keeps comparisons stable across hosts.\nfn normalize_path(path: &Path) -> String {\n    path.display()\n        .to_string()\n        .replace('\\\\', \"/\")\n        .trim_end_matches('/')\n        .to_string()\n}\n\nfn package_cache_contains_version(cache_root: &Path, package_id: &str, version: &str) -> bool {\n    // NuGet usually stores package IDs lower-cased on disk.\n    let expected = cache_root.join(package_id.to_ascii_lowercase()).join(version);\n    if expected.exists() {\n        return true;\n    }\n    let Ok(entries) = fs::read_dir(cache_root) else {\n        return false;\n    };\n    entries.flatten().any(|entry| {\n        entry.file_type().map(|ty| ty.is_dir()).unwrap_or(false)\n            && entry.file_name().to_string_lossy().eq_ignore_ascii_case(package_id)\n            && entry.path().join(version).exists()\n    })\n}\n\n/// Runs `dotnet` in a given working directory with error context suitable for tests.\n///\n/// This wrapper centralizes command construction so callers consistently include\n/// command + cwd details in failures.\nfn run_dotnet(args: &[&str], cwd: &Path) -> Result<String> {\n    let mut cmd = Vec::with_capacity(args.len() + 1);\n    cmd.push(\"dotnet\");\n    cmd.extend_from_slice(args);\n    crate::run_cmd(&cmd, cwd).with_context(|| format!(\"dotnet {} failed in {}\", args.join(\" \"), cwd.display()))\n}\n\n/// Ensures the WASI workload required by C# module publishing is present.\n///\n/// We do a best-effort install first, then assert by reading `dotnet workload list`.\n/// Result is memoized for the process so repeated C# smoketests avoid redundant setup.\nfn ensure_wasi_workload() -> Result<()> {\n    let _ = CSHARP_WORKLOAD_READY\n        .get_or_init(|| {\n            let workspace = crate::workspace_root();\n            let modules_dir = workspace.join(\"modules\");\n            let _ = run_dotnet(\n                &[\n                    \"workload\",\n                    \"install\",\n                    \"wasi-experimental\",\n                    \"--skip-manifest-update\",\n                ],\n                &modules_dir,\n            );\n            let workloads = run_dotnet(&[\"workload\", \"list\"], &modules_dir)?;\n            if !workloads.contains(\"wasi-experimental\") {\n                bail!(\n                    \"dotnet wasi-experimental workload is required but not installed.\\n`dotnet workload list` output:\\n{}\",\n                    workloads\n                );\n            }\n            Ok(())\n        })\n        .as_ref()\n        .map_err(|err| anyhow!(\"{err:#}\"))?;\n    Ok(())\n}\n\n/// Builds (once per process) a local, source-built NuGet feed for runtime packages.\n///\n/// This is a guardrail against stale binaries. Tests consume packages packed from the\n/// current checkout rather than whatever may exist in machine-global caches.\nfn ensure_local_feed() -> Result<&'static CsharpBuildEnv> {\n    CSHARP_BUILD_ENV\n        .get_or_init(|| {\n            ensure_wasi_workload()?;\n\n            let workspace = crate::workspace_root();\n            let bindings = workspace.join(\"crates/bindings-csharp\");\n            let local_feed_dir = workspace.join(\"target/smoketests-csharp/local-feed\");\n            if local_feed_dir.exists() {\n                fs::remove_dir_all(&local_feed_dir)\n                    .with_context(|| format!(\"Failed to clear {}\", local_feed_dir.display()))?;\n            }\n            fs::create_dir_all(&local_feed_dir)\n                .with_context(|| format!(\"Failed to create {}\", local_feed_dir.display()))?;\n            let local_feed_dir_str = local_feed_dir\n                .to_str()\n                .context(\"Local C# NuGet feed path is not valid UTF-8\")?;\n\n            for (project_dir, _) in PACKAGE_PROJECTS {\n                run_dotnet(\n                    &[\"pack\", \"-c\", \"Release\", \"-o\", local_feed_dir_str],\n                    &bindings.join(project_dir),\n                )?;\n            }\n\n            let feed_files = fs::read_dir(&local_feed_dir)\n                .with_context(|| format!(\"Failed to inspect {}\", local_feed_dir.display()))?\n                .flatten()\n                .filter_map(|entry| entry.file_name().into_string().ok())\n                .collect::<Vec<_>>();\n\n            for (_, package_id) in PACKAGE_PROJECTS {\n                let package_prefix = format!(\"{package_id}.\");\n                if !feed_files\n                    .iter()\n                    .any(|name| name.starts_with(&package_prefix) && name.ends_with(\".nupkg\"))\n                {\n                    bail!(\n                        \"Local feed at {} is missing package {}. Found files: {:?}\",\n                        local_feed_dir.display(),\n                        package_id,\n                        feed_files\n                    );\n                }\n            }\n\n            Ok(CsharpBuildEnv { local_feed_dir })\n        })\n        .as_ref()\n        .map_err(|err| anyhow!(\"{err:#}\"))\n}\n\n/// Prepares a generated C# module directory for deterministic restore/publish.\n///\n/// It writes a module-local `nuget.config` that:\n/// - isolates global package cache to `<module>/.nuget/packages`\n/// - routes `SpacetimeDB.*` resolution to the source-built local feed\n/// - still allows all other dependencies from nuget.org\npub(crate) fn prepare_csharp_module(module_path: &Path) -> Result<()> {\n    let env = ensure_local_feed()?;\n\n    let package_cache_dir = module_path.join(\".nuget/packages\");\n    if package_cache_dir.exists() {\n        fs::remove_dir_all(&package_cache_dir)\n            .with_context(|| format!(\"Failed to clear {}\", package_cache_dir.display()))?;\n    }\n    fs::create_dir_all(&package_cache_dir)\n        .with_context(|| format!(\"Failed to create {}\", package_cache_dir.display()))?;\n\n    let nuget_config = format!(\n        r#\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <config>\n    <add key=\"globalPackagesFolder\" value=\"{}\" />\n  </config>\n  <packageSources>\n    <clear />\n    <add key=\"spacetimedb-local\" value=\"{}\" />\n    <add key=\"nuget.org\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n  <packageSourceMapping>\n    <packageSource key=\"spacetimedb-local\">\n      <package pattern=\"SpacetimeDB.*\" />\n    </packageSource>\n    <packageSource key=\"nuget.org\">\n      <package pattern=\"*\" />\n    </packageSource>\n  </packageSourceMapping>\n</configuration>\n\"#,\n        normalize_path(&package_cache_dir),\n        normalize_path(&env.local_feed_dir),\n    );\n\n    fs::write(module_path.join(\"nuget.config\"), nuget_config)\n        .with_context(|| format!(\"Failed to write {}\", module_path.join(\"nuget.config\").display()))?;\n    Ok(())\n}\n\n/// Verifies a C# module restore/publish used the intended local bindings.\n///\n/// We assert two invariants:\n/// - required `SpacetimeDB.*` runtime packages were resolved in `obj/project.assets.json`\n/// - those resolved package versions are present in the module-local package cache\n///\n/// Failing any of these means the smoketest may have used stale or external packages.\npub(crate) fn verify_csharp_module_restore(module_path: &Path) -> Result<()> {\n    let _ = ensure_local_feed()?;\n\n    let assets_path = module_path.join(\"obj\").join(\"project.assets.json\");\n    let assets_text =\n        fs::read_to_string(&assets_path).with_context(|| format!(\"Failed to read {}\", assets_path.display()))?;\n    let assets: Value =\n        serde_json::from_str(&assets_text).with_context(|| format!(\"Failed to parse {}\", assets_path.display()))?;\n\n    let libraries = assets\n        .get(\"libraries\")\n        .and_then(Value::as_object)\n        .context(\"project.assets.json missing libraries\")?;\n    let package_cache_dir = module_path.join(\".nuget/packages\");\n    for package_id in REQUIRED_RUNTIME_PACKAGES {\n        let package_key = libraries\n            .keys()\n            .find(|name| name.starts_with(&format!(\"{package_id}/\")))\n            .with_context(|| {\n                format!(\n                    \"project.assets.json did not resolve expected package `{package_id}`.\\nresolved SpacetimeDB packages: {:?}\",\n                    libraries\n                        .keys()\n                        .filter(|name| name.starts_with(\"SpacetimeDB.\"))\n                        .collect::<Vec<_>>()\n                )\n            })?;\n        let (_, version) = package_key\n            .split_once('/')\n            .with_context(|| format!(\"Unexpected package key format in project.assets.json: `{package_key}`\"))?;\n        if !package_cache_contains_version(&package_cache_dir, package_id, version) {\n            bail!(\n                \"Resolved package `{package_id}/{version}` was not found in module-local package cache {}\",\n                normalize_path(&package_cache_dir),\n            );\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/smoketests/src/lib.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n//! Rust smoketest infrastructure for SpacetimeDB.\n//!\n//! This crate provides utilities for writing end-to-end tests that compile and publish\n//! SpacetimeDB modules, then exercise them via CLI commands.\n//!\n//! # Pre-compiled Modules\n//!\n//! For better performance, modules can be pre-compiled during the warmup phase.\n//! Use `Smoketest::builder().precompiled_module(\"name\")` to use a pre-compiled module\n//! instead of `module_code()` which compiles at runtime.\n//!\n//! # Running Smoketests\n//!\n//! Always run smoketests using the xtask command to ensure binaries are pre-built:\n//!\n//! ```bash\n//! cargo smoketest                     # Run all smoketests\n//! cargo smoketest -- test_name        # Run specific tests\n//! cargo xtask smoketest -- --help     # See all options\n//! ```\n//!\n//! # Example\n//!\n//! ```ignore\n//! use spacetimedb_smoketests::Smoketest;\n//!\n//! const MODULE_CODE: &str = r#\"\n//! use spacetimedb::{table, reducer};\n//!\n//! #[spacetimedb::table(accessor = person, public)]\n//! pub struct Person {\n//!     name: String,\n//! }\n//!\n//! #[spacetimedb::reducer]\n//! pub fn add(ctx: &ReducerContext, name: String) {\n//!     ctx.db.person().insert(Person { name });\n//! }\n//! \"#;\n//!\n//! #[test]\n//! fn test_example() {\n//!     let mut test = Smoketest::builder()\n//!         .module_code(MODULE_CODE)\n//!         .build();\n//!\n//!     test.call(\"add\", &[\"Alice\"]).unwrap();\n//!     test.assert_sql(\"SELECT * FROM person\", \"name\\n-----\\nAlice\");\n//! }\n//! ```\n\nmod csharp;\npub mod modules;\n\nuse anyhow::{bail, Context, Result};\nuse regex::Regex;\nuse spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard};\nuse std::env;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Output, Stdio};\nuse std::sync::OnceLock;\nuse std::time::Instant;\nuse which::which;\n\n/// Returns the remote server URL if running against a remote server.\n///\n/// Set the `SPACETIME_REMOTE_SERVER` environment variable to run tests against\n/// a remote server instead of spawning local servers.\npub fn remote_server_url() -> Option<String> {\n    std::env::var(\"SPACETIME_REMOTE_SERVER\").ok()\n}\n\n/// Returns true if running against a remote server.\npub fn is_remote_server() -> bool {\n    remote_server_url().is_some()\n}\n\n/// Skip this test if running against a remote server.\n///\n/// Use this macro at the start of tests that require a local server,\n/// such as tests that call `restart_server()` or access local data directories.\n///\n/// # Example\n///\n/// ```ignore\n/// #[test]\n/// fn test_restart() {\n///     require_local_server!();\n///     let mut test = Smoketest::builder().build();\n///     test.restart_server();\n///     // ...\n/// }\n/// ```\n#[macro_export]\nmacro_rules! require_local_server {\n    () => {\n        if $crate::is_remote_server() {\n            #[allow(clippy::disallowed_macros)]\n            {\n                eprintln!(\"Skipping test: requires local server\");\n            }\n            return;\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! require_dotnet {\n    () => {\n        if !$crate::allow_dotnet() {\n            #[allow(clippy::disallowed_macros)]\n            {\n                eprintln!(\"Skipping dotnet test\");\n            }\n            return;\n        }\n        if !$crate::have_dotnet() {\n            panic!(\"dotnet 8.0+ not found\");\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! require_psql {\n    () => {\n        if !$crate::have_psql() {\n            panic!(\"psql not found\");\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! require_pnpm {\n    () => {\n        if $crate::pnpm_path().is_none() {\n            panic!(\"pnpm not found\");\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! require_emscripten {\n    () => {\n        if !$crate::have_emscripten() {\n            panic!(\"emcc (Emscripten) not found\");\n        }\n    };\n}\n\n/// Helper macro for timing operations and printing results\nmacro_rules! timed {\n    ($label:expr, $expr:expr) => {{\n        let start = Instant::now();\n        let result = $expr;\n        let elapsed = start.elapsed();\n        eprintln!(\"[TIMING] {}: {:?}\", $label, elapsed);\n        result\n    }};\n}\n\n/// Returns the workspace root directory.\npub fn workspace_root() -> PathBuf {\n    let manifest_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"));\n    manifest_dir\n        .parent()\n        .and_then(|p| p.parent())\n        .expect(\"Failed to find workspace root\")\n        .to_path_buf()\n}\n\n/// Rewrites `spacetimedb` dependency in `<module_dir>/Cargo.toml` to use local workspace bindings.\npub fn patch_module_cargo_to_local_bindings(module_dir: &Path) -> Result<()> {\n    let cargo_toml_path = module_dir.join(\"Cargo.toml\");\n    let cargo_toml = fs::read_to_string(&cargo_toml_path)\n        .with_context(|| format!(\"Failed to read {}\", cargo_toml_path.display()))?;\n\n    let bindings_path = workspace_root().join(\"crates/bindings\");\n    let bindings_path_str = bindings_path.display().to_string().replace('\\\\', \"/\");\n    let replacement = format!(r#\"spacetimedb = {{ path = \"{bindings_path_str}\", features = [\"unstable\"] }}\"#);\n\n    let patched = cargo_toml\n        .lines()\n        .map(|line| {\n            if line.trim_start().starts_with(\"spacetimedb = \") {\n                replacement.as_str()\n            } else {\n                line\n            }\n        })\n        .collect::<Vec<_>>()\n        .join(\"\\n\");\n\n    fs::write(&cargo_toml_path, format!(\"{patched}\\n\"))\n        .with_context(|| format!(\"Failed to write {}\", cargo_toml_path.display()))?;\n    Ok(())\n}\n\n/// Returns the shared target directory for smoketest module builds.\n///\n/// All tests share this directory to cache compiled dependencies. The warmup step\n/// pre-compiles dependencies, then each test only needs to compile its unique module.\n/// Cargo serializes builds due to directory locking, but this is still faster than\n/// each test compiling all dependencies from scratch.\nfn shared_target_dir() -> PathBuf {\n    static TARGET_DIR: OnceLock<PathBuf> = OnceLock::new();\n    TARGET_DIR\n        .get_or_init(|| {\n            let target_dir = workspace_root().join(\"target/smoketest-modules\");\n            fs::create_dir_all(&target_dir).expect(\"Failed to create shared module target directory\");\n            target_dir\n        })\n        .clone()\n}\n\n/// Generates a random lowercase alphabetic string suitable for database names.\npub fn random_string() -> String {\n    use std::time::{SystemTime, UNIX_EPOCH};\n    let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();\n    // Convert to base-26 using lowercase letters only (a-z)\n    let mut result = String::with_capacity(20);\n    let mut n = timestamp;\n    while n > 0 || result.len() < 10 {\n        let c = (b'a' + (n % 26) as u8) as char;\n        result.push(c);\n        n /= 26;\n    }\n    result\n}\n\n/// Returns true if dotnet 8.0+ is available on the system.\npub fn have_dotnet() -> bool {\n    static HAVE_DOTNET: OnceLock<bool> = OnceLock::new();\n    *HAVE_DOTNET.get_or_init(|| {\n        Command::new(\"dotnet\")\n            .args([\"--list-sdks\"])\n            .output()\n            .map(|output| {\n                if !output.status.success() {\n                    return false;\n                }\n                let stdout = String::from_utf8_lossy(&output.stdout);\n                // Check for dotnet 8.0 or higher\n                stdout\n                    .lines()\n                    .any(|line| line.starts_with(\"8.\") || line.starts_with(\"9.\") || line.starts_with(\"10.\"))\n            })\n            .unwrap_or(false)\n    })\n}\n\n/// Returns true if tests are configured to allow dotnet\npub fn allow_dotnet() -> bool {\n    let Ok(s) = std::env::var(\"SMOKETESTS_DOTNET\") else {\n        return true;\n    };\n    match s.as_str() {\n        \"\" | \"0\" => false,\n        s => s.to_lowercase() != \"false\",\n    }\n}\n\n/// Returns true if psql (PostgreSQL client) is available on the system.\npub fn have_psql() -> bool {\n    static HAVE_PSQL: OnceLock<bool> = OnceLock::new();\n    *HAVE_PSQL.get_or_init(|| {\n        Command::new(\"psql\")\n            .args([\"--version\"])\n            .output()\n            .map(|output| output.status.success())\n            .unwrap_or(false)\n    })\n}\n\n/// Returns true if pnpm is available on the system.\npub fn pnpm_path() -> Option<PathBuf> {\n    static PNPM_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();\n    PNPM_PATH.get_or_init(|| which(\"pnpm\").ok()).clone()\n}\n\n/// Runs a command and returns stdout as a string.\npub fn run_cmd(args: &[&str], cwd: &Path) -> Result<String> {\n    run_cmd_inner(args, cwd, None)\n}\n\n/// Runs a command with stdin input and returns stdout as a string.\npub fn run_cmd_with_stdin(args: &[&str], cwd: &Path, stdin_input: &str) -> Result<String> {\n    run_cmd_inner(args, cwd, Some(stdin_input))\n}\n\nfn run_cmd_inner(args: &[&str], cwd: &Path, stdin_input: Option<&str>) -> Result<String> {\n    let Some(program) = args.first() else {\n        bail!(\"run_cmd called with no program\");\n    };\n\n    let mut cmd = Command::new(program);\n    cmd.args(&args[1..])\n        .current_dir(cwd)\n        .stderr(Stdio::piped())\n        .stdout(Stdio::piped());\n\n    if stdin_input.is_some() {\n        cmd.stdin(Stdio::piped());\n    }\n\n    let mut child = cmd\n        .spawn()\n        .with_context(|| format!(\"Failed to spawn command: {args:?}\"))?;\n\n    if let Some(input) = stdin_input {\n        use std::io::Write;\n        if let Some(stdin) = child.stdin.as_mut() {\n            stdin.write_all(input.as_bytes())?;\n        }\n    }\n\n    let output = child.wait_with_output()?;\n\n    if !output.status.success() {\n        bail!(\n            \"command {:?} failed:\\nstdout: {}\\nstderr: {}\",\n            args,\n            String::from_utf8_lossy(&output.stdout),\n            String::from_utf8_lossy(&output.stderr)\n        );\n    }\n    Ok(String::from_utf8_lossy(&output.stdout).to_string())\n}\n\n/// Runs a `pnpm` command and returns stdout as a string.\npub fn pnpm(args: &[&str], cwd: &Path) -> Result<String> {\n    let pnpm_path = pnpm_path().context(\"Could not locate pnpm\")?;\n    let pnpm_path = pnpm_path.to_str().context(\"pnpm path is not valid UTF-8\")?;\n    let mut full_args = vec![pnpm_path];\n    full_args.extend(args);\n    run_cmd(&full_args, cwd)\n}\n\n/// Builds the local TypeScript bindings package.\npub fn build_typescript_sdk() -> Result<()> {\n    let workspace = workspace_root();\n    let ts_bindings = workspace.join(\"crates/bindings-typescript\");\n    pnpm(&[\"install\"], &ts_bindings)?;\n    pnpm(&[\"build\"], &ts_bindings)?;\n    Ok(())\n}\n\n/// Returns true if Emscripten (emcc) is available on the system.\npub fn have_emscripten() -> bool {\n    static HAVE_EMSCRIPTEN: OnceLock<bool> = OnceLock::new();\n    *HAVE_EMSCRIPTEN.get_or_init(|| which(\"emcc\").is_ok() || which(\"emcc.bat\").is_ok())\n}\n\n/// A smoketest instance that manages a SpacetimeDB server and module project.\npub struct Smoketest {\n    /// The SpacetimeDB server guard (stops server on drop).\n    /// None when running against a remote server.\n    pub guard: Option<SpacetimeDbGuard>,\n    /// Temporary directory containing the module project.\n    pub project_dir: tempfile::TempDir,\n    /// Additional features for the spacetimedb bindings dependency.\n    pub bindings_features: Vec<String>,\n    /// Additional dependencies to add to the module's Cargo.toml.\n    pub extra_deps: String,\n    /// Database identity after publishing (if any).\n    pub database_identity: Option<String>,\n    /// The server URL (e.g., \"http://127.0.0.1:3000\").\n    pub server_url: String,\n    /// Path to the test-specific CLI config file (isolates tests from user config).\n    pub config_path: std::path::PathBuf,\n    /// Unique module name for this test instance.\n    /// Used to avoid wasm output conflicts when tests run in parallel.\n    module_name: String,\n    /// Path to pre-compiled WASM file (if using precompiled_module).\n    precompiled_wasm_path: Option<PathBuf>,\n}\n\n/// Response from an HTTP API call.\npub struct ApiResponse {\n    /// HTTP status code.\n    pub status_code: u16,\n    /// Response body.\n    pub body: Vec<u8>,\n}\n\nimpl ApiResponse {\n    /// Returns the body as a string.\n    pub fn text(&self) -> Result<String> {\n        String::from_utf8(self.body.clone()).context(\"Response body is not valid UTF-8\")\n    }\n\n    /// Parses the body as JSON.\n    pub fn json(&self) -> Result<serde_json::Value> {\n        serde_json::from_slice(&self.body).context(\"Failed to parse response as JSON\")\n    }\n\n    /// Returns true if the status code indicates success (2xx).\n    pub fn is_success(&self) -> bool {\n        (200..300).contains(&self.status_code)\n    }\n}\n\n/// Builder for creating `Smoketest` instances.\npub struct SmoketestBuilder {\n    module_code: Option<String>,\n    precompiled_module: Option<String>,\n    bindings_features: Vec<String>,\n    extra_deps: String,\n    autopublish: bool,\n    pg_port: Option<u16>,\n}\n\nimpl Default for SmoketestBuilder {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl SmoketestBuilder {\n    /// Creates a new builder with default settings.\n    pub fn new() -> Self {\n        Self {\n            module_code: None,\n            precompiled_module: None,\n            bindings_features: vec![\"unstable\".to_string()],\n            extra_deps: String::new(),\n            autopublish: true,\n            pg_port: None,\n        }\n    }\n\n    /// Enables the PostgreSQL wire protocol on the specified port.\n    pub fn pg_port(mut self, port: u16) -> Self {\n        self.pg_port = Some(port);\n        self\n    }\n\n    /// Sets the module code to compile and publish.\n    pub fn module_code(mut self, code: &str) -> Self {\n        self.module_code = Some(code.to_string());\n        self\n    }\n\n    /// Uses a pre-compiled module instead of runtime compilation.\n    ///\n    /// Pre-compiled modules are built during the warmup phase and stored in\n    /// `crates/smoketests/modules/target/`. This eliminates per-test compilation\n    /// overhead for static modules.\n    ///\n    /// # Example\n    ///\n    /// ```ignore\n    /// let test = Smoketest::builder()\n    ///     .precompiled_module(\"filtering\")\n    ///     .build();\n    /// ```\n    ///\n    /// # Panics\n    ///\n    /// Panics if the module name is not found in the registry.\n    pub fn precompiled_module(mut self, name: &str) -> Self {\n        self.precompiled_module = Some(name.to_string());\n        self\n    }\n\n    /// Sets additional features for the spacetimedb bindings dependency.\n    pub fn bindings_features(mut self, features: &[&str]) -> Self {\n        self.bindings_features = features.iter().map(|s| s.to_string()).collect();\n        self\n    }\n\n    /// Adds extra dependencies to the module's Cargo.toml.\n    pub fn extra_deps(mut self, deps: &str) -> Self {\n        self.extra_deps = deps.to_string();\n        self\n    }\n\n    /// Sets whether to automatically publish the module on build.\n    /// Default is true.\n    pub fn autopublish(mut self, yes: bool) -> Self {\n        self.autopublish = yes;\n        self\n    }\n\n    /// Builds the `Smoketest` instance.\n    ///\n    /// This spawns a SpacetimeDB server (unless `SPACETIME_REMOTE_SERVER` is set),\n    /// creates a temporary project directory, writes the module code, and optionally\n    /// publishes the module.\n    ///\n    /// When `SPACETIME_REMOTE_SERVER` is set, tests run against the remote server\n    /// instead of spawning a local server. Tests that require local server control\n    /// (like restart tests) should use `skip_if_remote!()` at the start.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the CLI/standalone binaries haven't been built or are stale.\n    /// Run `cargo smoketest prepare` to build binaries before running tests.\n    pub fn build(self) -> Smoketest {\n        // Check binaries first - this will panic with a helpful message if missing/stale\n        let _ = ensure_binaries_built();\n        let build_start = Instant::now();\n\n        // Check if we're running against a remote server\n        let (guard, server_url) = if let Some(remote_url) = remote_server_url() {\n            eprintln!(\"[REMOTE] Using remote server: {}\", remote_url);\n            (None, remote_url)\n        } else {\n            let guard = timed!(\n                \"server spawn\",\n                SpacetimeDbGuard::spawn_in_temp_data_dir_with_pg_port(self.pg_port)\n            );\n            let url = guard.host_url.clone();\n            (Some(guard), url)\n        };\n\n        let project_dir = tempfile::tempdir().expect(\"Failed to create temp project directory\");\n\n        // Check if we're using a pre-compiled module\n        let precompiled_wasm_path = self.precompiled_module.as_ref().map(|name| {\n            let path = modules::precompiled_module(name);\n            if !path.exists() {\n                panic!(\n                    \"Pre-compiled module '{}' not found at {:?}. \\\n                    Run `cargo smoketest` to build pre-compiled modules during warmup.\",\n                    name, path\n                );\n            }\n            eprintln!(\"[PRECOMPILED] Using pre-compiled module: {}\", name);\n            path\n        });\n\n        let project_setup_start = Instant::now();\n\n        // Generate a unique module name to avoid wasm output conflicts in parallel tests.\n        // The format is smoketest_module_{random} which produces smoketest_module_{random}.wasm\n        let module_name = format!(\"smoketest_module_{}\", random_string());\n\n        let config_path = project_dir.path().join(\"config.toml\");\n        let mut smoketest = Smoketest {\n            guard,\n            project_dir,\n            database_identity: None,\n            server_url,\n            config_path,\n            module_name,\n            precompiled_wasm_path: precompiled_wasm_path.clone(),\n            bindings_features: self.bindings_features.clone(),\n            extra_deps: self.extra_deps.clone(),\n        };\n\n        // Only set up project structure if not using precompiled module\n        if precompiled_wasm_path.is_none() {\n            let module_code = self.module_code.unwrap_or_else(|| {\n                r#\"use spacetimedb::ReducerContext;\n\n#[spacetimedb::reducer]\npub fn noop(_ctx: &ReducerContext) {}\n\"#\n                .to_string()\n            });\n            smoketest.write_module_code(&module_code).unwrap();\n\n            eprintln!(\"[TIMING] project setup: {:?}\", project_setup_start.elapsed());\n        }\n\n        if self.autopublish {\n            smoketest.publish_module().expect(\"Failed to publish module\");\n        }\n\n        eprintln!(\"[TIMING] total build: {:?}\", build_start.elapsed());\n        smoketest\n    }\n}\n\nimpl Smoketest {\n    /// Creates a new builder for configuring a smoketest.\n    pub fn builder() -> SmoketestBuilder {\n        SmoketestBuilder::new()\n    }\n\n    /// Restart the SpacetimeDB server.\n    ///\n    /// This stops the current server process and starts a new one with the\n    /// same data directory. All data is preserved across the restart.\n    /// The server URL may change since a new ephemeral port is allocated.\n    ///\n    /// # Panics\n    ///\n    /// Panics if running against a remote server (no local server to restart).\n    /// Tests that call this method should use `skip_if_remote!()` at the start.\n    pub fn restart_server(&mut self) {\n        let guard = self.guard.as_mut().expect(\n            \"Cannot restart server: running against remote server. Use skip_if_remote!() at the start of this test.\",\n        );\n        guard.restart();\n        // Update server_url since the port may have changed\n        self.server_url = guard.host_url.clone();\n    }\n\n    /// Returns the server host (without protocol), e.g., \"127.0.0.1:3000\".\n    pub fn server_host(&self) -> &str {\n        self.server_url\n            .strip_prefix(\"http://\")\n            .or_else(|| self.server_url.strip_prefix(\"https://\"))\n            .unwrap_or(&self.server_url)\n    }\n\n    /// Returns the PostgreSQL wire protocol port, if enabled.\n    ///\n    /// Returns None if running against a remote server or if PostgreSQL\n    /// wire protocol wasn't enabled for the local server.\n    pub fn pg_port(&self) -> Option<u16> {\n        self.guard.as_ref().and_then(|g| g.pg_port)\n    }\n\n    /// Reads the authentication token from the config file.\n    pub fn read_token(&self) -> Result<String> {\n        let config_content = fs::read_to_string(&self.config_path).context(\"Failed to read config file\")?;\n\n        // Parse as TOML and extract spacetimedb_token\n        let config: toml::Value = config_content.parse().context(\"Failed to parse config as TOML\")?;\n\n        config\n            .get(\"spacetimedb_token\")\n            .and_then(|v| v.as_str())\n            .map(String::from)\n            .context(\"No spacetimedb_token found in config\")\n    }\n\n    /// Runs psql command against the PostgreSQL wire protocol server.\n    ///\n    /// Returns the output on success, or an error with stderr on failure.\n    pub fn psql(&self, database: &str, sql: &str) -> Result<String> {\n        let token = self.read_token()?;\n        self.psql_with_token(database, &token, sql)\n    }\n\n    pub fn psql_with_token(&self, database: &str, token: &str, sql: &str) -> Result<String> {\n        let pg_port = self.pg_port().context(\"PostgreSQL wire protocol not enabled\")?;\n\n        // Extract just the host part (without port)\n        let host = self.server_host().split(':').next().unwrap_or(\"127.0.0.1\");\n\n        let output = Command::new(\"psql\")\n            .args([\n                \"-h\",\n                host,\n                \"-p\",\n                &pg_port.to_string(),\n                \"-U\",\n                \"postgres\",\n                \"-d\",\n                database,\n                \"--quiet\",\n                \"-c\",\n                sql,\n            ])\n            .env(\"PGPASSWORD\", token)\n            .output()\n            .context(\"Failed to run psql\")?;\n\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        if !stderr.is_empty() && !output.status.success() {\n            bail!(\"{}\", stderr.trim());\n        }\n\n        Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())\n    }\n\n    /// Asserts that psql output matches the expected value.\n    pub fn assert_psql(&self, database: &str, sql: &str, expected: &str) {\n        let output = self.psql(database, sql).expect(\"psql failed\");\n        let output_normalized: String = output.lines().map(|l| l.trim_end()).collect::<Vec<_>>().join(\"\\n\");\n        let expected_normalized: String = expected.lines().map(|l| l.trim_end()).collect::<Vec<_>>().join(\"\\n\");\n        assert_eq!(\n            output_normalized, expected_normalized,\n            \"psql output mismatch for query: {}\\n\\nExpected:\\n{}\\n\\nActual:\\n{}\",\n            sql, expected_normalized, output_normalized\n        );\n    }\n\n    /// Runs a spacetime CLI command.\n    ///\n    /// Returns the command output. The command is run but not yet asserted.\n    /// Uses --config-path to isolate test config from user config.\n    /// Callers should pass `--server` explicitly when the command needs it.\n    pub fn spacetime_cmd(&self, args: &[&str]) -> Output {\n        let start = Instant::now();\n        let cli_path = ensure_binaries_built();\n        let output = Command::new(&cli_path)\n            .arg(\"--config-path\")\n            .arg(&self.config_path)\n            .args(args)\n            .current_dir(self.project_dir.path())\n            .output()\n            .expect(\"Failed to execute spacetime command\");\n\n        let cmd_name = args.first().unwrap_or(&\"unknown\");\n        eprintln!(\"[TIMING] spacetime {}: {:?}\", cmd_name, start.elapsed());\n        output\n    }\n\n    /// Runs a spacetime CLI command with stdin input.\n    ///\n    /// Returns the command output. The command is run but not yet asserted.\n    /// Uses --config-path to isolate test config from user config.\n    /// Callers should pass `--server` explicitly when the command needs it.\n    pub fn spacetime_cmd_with_stdin(&self, args: &[&str], stdin_input: &str) -> Output {\n        let start = Instant::now();\n        let cli_path = ensure_binaries_built();\n        let mut child = Command::new(&cli_path)\n            .arg(\"--config-path\")\n            .arg(&self.config_path)\n            .args(args)\n            .current_dir(self.project_dir.path())\n            .stdin(Stdio::piped())\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped())\n            .spawn()\n            .expect(\"Failed to spawn spacetime command\");\n\n        {\n            use std::io::Write;\n            let stdin = child.stdin.as_mut().expect(\"missing child stdin\");\n            stdin\n                .write_all(stdin_input.as_bytes())\n                .expect(\"Failed to write spacetime stdin\");\n        }\n\n        let output = child.wait_with_output().expect(\"Failed to wait for spacetime command\");\n\n        let cmd_name = args.first().unwrap_or(&\"unknown\");\n        eprintln!(\"[TIMING] spacetime {} (stdin): {:?}\", cmd_name, start.elapsed());\n        output\n    }\n\n    /// Runs a spacetime CLI command and returns stdout as a string.\n    ///\n    /// Panics if the command fails.\n    /// Callers should pass `--server` explicitly when the command needs it.\n    pub fn spacetime(&self, args: &[&str]) -> Result<String> {\n        let output = self.spacetime_cmd(args);\n        if !output.status.success() {\n            bail!(\n                \"spacetime {:?} failed:\\nstdout: {}\\nstderr: {}\",\n                args,\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n        Ok(String::from_utf8_lossy(&output.stdout).to_string())\n    }\n\n    /// Runs a spacetime CLI command with stdin and returns stdout as a string.\n    ///\n    /// Panics if the command fails.\n    /// Callers should pass `--server` explicitly when the command needs it.\n    pub fn spacetime_with_stdin(&self, args: &[&str], stdin_input: &str) -> Result<String> {\n        let output = self.spacetime_cmd_with_stdin(args, stdin_input);\n        if !output.status.success() {\n            bail!(\n                \"spacetime {:?} failed:\\nstdout: {}\\nstderr: {}\",\n                args,\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n        Ok(String::from_utf8_lossy(&output.stdout).to_string())\n    }\n\n    /// Initializes, writes, and publishes a TypeScript module from source.\n    ///\n    /// Will publish with the `--clear-database` flag.\n    ///\n    /// The module is initialized at `<test_project_dir>/<project_dir_name>/spacetimedb`.\n    /// On success this updates `self.database_identity`.\n    pub fn publish_typescript_module_source(\n        &mut self,\n        project_dir_name: &str,\n        module_name: &str,\n        module_source: &str,\n    ) -> Result<String> {\n        self.publish_typescript_module_source_clear(project_dir_name, module_name, module_source, true)\n    }\n\n    /// Initializes, writes, and publishes a TypeScript module from source.\n    ///\n    /// If `clear` is `true`, this will publish with the `--clear-database` flag.\n    ///\n    /// The module is initialized at `<test_project_dir>/<project_dir_name>/spacetimedb`.\n    /// On success this updates `self.database_identity`.\n    pub fn publish_typescript_module_source_clear(\n        &mut self,\n        project_dir_name: &str,\n        module_name: &str,\n        module_source: &str,\n        clear: bool,\n    ) -> Result<String> {\n        let module_root = self.project_dir.path().join(project_dir_name);\n        let module_root_str = module_root.to_str().context(\"Invalid TypeScript project path\")?;\n        self.spacetime(&[\n            \"init\",\n            \"--non-interactive\",\n            \"--lang\",\n            \"typescript\",\n            \"--project-path\",\n            module_root_str,\n            module_name,\n        ])?;\n\n        let module_path = module_root.join(\"spacetimedb\");\n        fs::write(module_path.join(\"src/index.ts\"), module_source).context(\"Failed to write TypeScript module code\")?;\n\n        build_typescript_sdk()?;\n        let _ = pnpm(&[\"uninstall\", \"spacetimedb\"], &module_path);\n\n        let ts_bindings = workspace_root().join(\"crates/bindings-typescript\");\n        let ts_bindings_path = ts_bindings.to_str().context(\"Invalid TypeScript bindings path\")?;\n        pnpm(&[\"install\", ts_bindings_path], &module_path)?;\n\n        let module_path_str = module_path.to_str().context(\"Invalid TypeScript module path\")?;\n        let mut publish_args = vec![\n            \"publish\",\n            \"--server\",\n            &self.server_url,\n            \"--module-path\",\n            module_path_str,\n            \"--yes\",\n        ];\n        if clear {\n            publish_args.push(\"--clear-database\");\n        }\n        publish_args.push(module_name);\n        let publish_output = self.spacetime(&publish_args)?;\n\n        let re = Regex::new(r\"identity: ([0-9a-fA-F]+)\").unwrap();\n        let identity = re\n            .captures(&publish_output)\n            .and_then(|caps| caps.get(1))\n            .map(|m| m.as_str().to_string())\n            .context(\"Failed to parse database identity from publish output\")?;\n        self.database_identity = Some(identity.clone());\n\n        Ok(identity)\n    }\n\n    /// Initializes, writes, and publishes a C# module from source.\n    ///\n    /// The module is initialized at `<test_project_dir>/<project_dir_name>/spacetimedb`.\n    /// On success this updates `self.database_identity`.\n    pub fn publish_csharp_module_source(\n        &mut self,\n        project_dir_name: &str,\n        module_name: &str,\n        module_source: &str,\n    ) -> Result<String> {\n        let module_root = self.project_dir.path().join(project_dir_name);\n        let module_root_str = module_root.to_str().context(\"Invalid C# project path\")?;\n        self.spacetime(&[\n            \"init\",\n            \"--non-interactive\",\n            \"--lang\",\n            \"csharp\",\n            \"--project-path\",\n            module_root_str,\n            module_name,\n        ])?;\n\n        let module_path = module_root.join(\"spacetimedb\");\n        fs::write(module_path.join(\"Lib.cs\"), module_source).context(\"Failed to write C# module code\")?;\n        csharp::prepare_csharp_module(&module_path)?;\n\n        let module_path_str = module_path.to_str().context(\"Invalid C# module path\")?;\n        let publish_output = self.spacetime(&[\n            \"publish\",\n            \"--server\",\n            &self.server_url,\n            \"--module-path\",\n            module_path_str,\n            \"--yes\",\n            \"--clear-database\",\n            module_name,\n        ])?;\n        csharp::verify_csharp_module_restore(&module_path)?;\n\n        let re = Regex::new(r\"identity: ([0-9a-fA-F]+)\").unwrap();\n        let identity = re\n            .captures(&publish_output)\n            .and_then(|caps| caps.get(1))\n            .map(|m| m.as_str().to_string())\n            .context(\"Failed to parse database identity from publish output\")?;\n        self.database_identity = Some(identity.clone());\n\n        Ok(identity)\n    }\n\n    /// Writes new module code to the project.\n    ///\n    /// This switches from precompiled mode to runtime compilation mode.\n    /// If the project structure doesn't exist (e.g., started with `precompiled_module()`),\n    /// it will be created on demand.\n    pub fn write_module_code(&mut self, code: &str) -> Result<()> {\n        // Clear precompiled module path so we use the source code instead\n        self.precompiled_wasm_path = None;\n\n        // Create project structure on demand if it doesn't exist\n        // (happens when test started with precompiled_module)\n        let src_dir = self.project_dir.path().join(\"src\");\n        if !src_dir.exists() {\n            fs::create_dir_all(&src_dir).context(\"Failed to create src directory\")?;\n\n            // Write Cargo.toml with default settings\n            let workspace_root = workspace_root();\n            let bindings_path = workspace_root.join(\"crates/bindings\");\n            let bindings_path_str = bindings_path.display().to_string().replace('\\\\', \"/\");\n            let features_str = format!(\"{:?}\", self.bindings_features);\n\n            let cargo_toml = format!(\n                r#\"[package]\nname = \"{}\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = {{ path = \"{}\", features = {} }}\nlog = \"0.4\"\n{}\n\"#,\n                self.module_name, bindings_path_str, features_str, self.extra_deps\n            );\n            fs::write(self.project_dir.path().join(\"Cargo.toml\"), cargo_toml).context(\"Failed to write Cargo.toml\")?;\n\n            // Copy rust-toolchain.toml\n            let toolchain_src = workspace_root.join(\"rust-toolchain.toml\");\n            if toolchain_src.exists() {\n                fs::copy(&toolchain_src, self.project_dir.path().join(\"rust-toolchain.toml\"))\n                    .context(\"Failed to copy rust-toolchain.toml\")?;\n            }\n        }\n\n        fs::write(self.project_dir.path().join(\"src/lib.rs\"), code).context(\"Failed to write module code\")?;\n        Ok(())\n    }\n\n    /// Switches to using a precompiled module.\n    ///\n    /// After calling this, subsequent `publish_module*` calls will use the\n    /// precompiled WASM file instead of building from source.\n    pub fn use_precompiled_module(&mut self, name: &str) {\n        let path = modules::precompiled_module(name);\n        if !path.exists() {\n            panic!(\n                \"Pre-compiled module '{}' not found at {:?}. \\\n                Run `cargo smoketest` to build pre-compiled modules during warmup.\",\n                name, path\n            );\n        }\n        eprintln!(\"[PRECOMPILED] Switching to pre-compiled module: {}\", name);\n        self.precompiled_wasm_path = Some(path);\n    }\n\n    /// Switches to using an explicit precompiled WASM path.\n    ///\n    /// After calling this, subsequent `publish_module*` calls will use this\n    /// WASM file instead of building from source.\n    pub fn use_precompiled_wasm_path(&mut self, path: impl AsRef<Path>) -> Result<()> {\n        let path = path.as_ref();\n        if !path.exists() {\n            bail!(\"Pre-compiled wasm not found at {}\", path.display());\n        }\n        eprintln!(\"[PRECOMPILED] Switching to explicit wasm path: {}\", path.display());\n        self.precompiled_wasm_path = Some(path.to_path_buf());\n        Ok(())\n    }\n\n    /// Runs `spacetime build` and returns the raw output.\n    ///\n    /// Use this when you need to check for build failures (e.g., wasm_bindgen detection).\n    pub fn spacetime_build(&self) -> Output {\n        let start = Instant::now();\n        let project_path = self.project_dir.path().to_str().unwrap();\n        let cli_path = ensure_binaries_built();\n\n        let mut cmd = Command::new(&cli_path);\n        cmd.args([\"build\", \"--module-path\", project_path])\n            .current_dir(self.project_dir.path())\n            .env(\"CARGO_TARGET_DIR\", shared_target_dir());\n\n        let output = cmd.output().expect(\"Failed to execute spacetime build\");\n        eprintln!(\"[TIMING] spacetime build: {:?}\", start.elapsed());\n        output\n    }\n\n    /// Publishes the module and stores the database identity.\n    pub fn publish_module(&mut self) -> Result<String> {\n        self.publish_module_opts(None, false)\n    }\n\n    /// Publishes the module with a specific name and optional clear flag.\n    ///\n    /// If `name` is provided, the database will be published with that name.\n    /// If `clear` is true, the database will be cleared before publishing.\n    pub fn publish_module_named(&mut self, name: &str, clear: bool) -> Result<String> {\n        self.publish_module_opts(Some(name), clear)\n    }\n\n    /// Re-publishes the module to the existing database identity with optional clear.\n    ///\n    /// This is useful for testing auto-migrations where you want to update\n    /// the module without clearing the database.\n    pub fn publish_module_clear(&mut self, clear: bool) -> Result<String> {\n        let identity = self\n            .database_identity\n            .as_ref()\n            .context(\"No database published yet\")?\n            .clone();\n        self.publish_module_opts(Some(&identity), clear)\n    }\n\n    /// Publishes the module with name, clear, and break_clients options.\n    pub fn publish_module_with_options(&mut self, name: &str, clear: bool, break_clients: bool) -> Result<String> {\n        self.publish_module_internal(Some(name), clear, break_clients, true, None)\n    }\n\n    /// Publishes the module and allows supplying stdin input to the CLI.\n    ///\n    /// Useful for interactive publish prompts which require typed acknowledgements.\n    /// Note: does NOT pass `--yes` so that interactive prompts are not suppressed.\n    pub fn publish_module_with_stdin(&mut self, name: &str, stdin_input: &str) -> Result<String> {\n        self.publish_module_internal(Some(name), false, false, false, Some(stdin_input))\n    }\n\n    /// Publishes the module without passing `--yes`, so interactive prompts are not suppressed.\n    pub fn publish_module_named_no_force(&mut self, name: &str) -> Result<String> {\n        self.publish_module_internal(Some(name), false, false, false, None)\n    }\n\n    /// Internal helper for publishing with options.\n    fn publish_module_opts(&mut self, name: Option<&str>, clear: bool) -> Result<String> {\n        self.publish_module_internal(name, clear, false, true, None)\n    }\n\n    /// Internal helper for publishing with all options.\n    fn publish_module_internal(\n        &mut self,\n        name: Option<&str>,\n        clear: bool,\n        break_clients: bool,\n        force: bool,\n        stdin_input: Option<&str>,\n    ) -> Result<String> {\n        let start = Instant::now();\n\n        // Determine the WASM path - either precompiled or build it\n        let wasm_path_str = if let Some(ref precompiled_path) = self.precompiled_wasm_path {\n            // Use pre-compiled WASM directly (no build needed)\n            eprintln!(\"[TIMING] spacetime build: skipped (using precompiled)\");\n            precompiled_path.to_str().unwrap().to_string()\n        } else {\n            // Build the WASM module from source\n            let project_path = self.project_dir.path().to_str().unwrap().to_string();\n            let build_start = Instant::now();\n            let cli_path = ensure_binaries_built();\n            let target_dir = shared_target_dir();\n\n            let mut build_cmd = Command::new(&cli_path);\n            build_cmd\n                .args([\"build\", \"--module-path\", &project_path])\n                .current_dir(self.project_dir.path())\n                .env(\"CARGO_TARGET_DIR\", &target_dir);\n\n            let build_output = build_cmd.output().expect(\"Failed to execute spacetime build\");\n            let build_elapsed = build_start.elapsed();\n            eprintln!(\"[TIMING] spacetime build: {:?}\", build_elapsed);\n\n            if !build_output.status.success() {\n                bail!(\n                    \"spacetime build failed:\\nstdout: {}\\nstderr: {}\",\n                    String::from_utf8_lossy(&build_output.stdout),\n                    String::from_utf8_lossy(&build_output.stderr)\n                );\n            }\n\n            // Construct the wasm path using the unique module name\n            let wasm_filename = format!(\"{}.wasm\", self.module_name);\n            let wasm_path = target_dir.join(\"wasm32-unknown-unknown/release\").join(&wasm_filename);\n            wasm_path.to_str().unwrap().to_string()\n        };\n\n        // Now publish with --bin-path to skip rebuild\n        let publish_start = Instant::now();\n        let mut args = vec![\"publish\", \"--server\", &self.server_url, \"--bin-path\", &wasm_path_str];\n\n        if force {\n            args.push(\"--yes\");\n        }\n\n        if clear {\n            args.push(\"--clear-database\");\n        }\n\n        if break_clients {\n            args.push(\"--break-clients\");\n        }\n\n        let name_owned;\n        if let Some(n) = name {\n            name_owned = n.to_string();\n            args.push(&name_owned);\n        }\n\n        let output = match stdin_input {\n            Some(stdin_input) => self.spacetime_with_stdin(&args, stdin_input)?,\n            None => self.spacetime(&args)?,\n        };\n        eprintln!(\n            \"[TIMING] spacetime publish (after build): {:?}\",\n            publish_start.elapsed()\n        );\n        eprintln!(\"[TIMING] publish_module total: {:?}\", start.elapsed());\n\n        // Parse the identity from output like \"identity: abc123...\"\n        let re = Regex::new(r\"identity: ([0-9a-fA-F]+)\").unwrap();\n        if let Some(caps) = re.captures(&output) {\n            let identity = caps.get(1).unwrap().as_str().to_string();\n            self.database_identity = Some(identity.clone());\n            Ok(identity)\n        } else {\n            bail!(\"Failed to parse database identity from publish output: {}\", output);\n        }\n    }\n\n    /// Calls a reducer or procedure with the given arguments.\n    ///\n    /// Arguments are passed directly to the CLI as strings.\n    pub fn call(&self, name: &str, args: &[&str]) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        let mut cmd_args = vec![\"call\", \"--server\", &self.server_url, \"--\", identity.as_str(), name];\n        cmd_args.extend(args);\n\n        self.spacetime(&cmd_args)\n    }\n\n    /// Calls a reducer/procedure and returns the full output including stderr.\n    pub fn call_output(&self, name: &str, args: &[&str]) -> Output {\n        let identity = self.database_identity.as_ref().expect(\"No database published\");\n\n        let mut cmd_args = vec![\"call\", \"--server\", &self.server_url, \"--\", identity.as_str(), name];\n        cmd_args.extend(args);\n\n        self.spacetime_cmd(&cmd_args)\n    }\n\n    /// Calls a reducer anonymously (without authentication).\n    pub fn call_anon(&self, name: &str, args: &[&str]) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        let mut cmd_args = vec![\n            \"call\",\n            \"--anonymous\",\n            \"--server\",\n            &self.server_url,\n            \"--\",\n            identity.as_str(),\n            name,\n        ];\n        cmd_args.extend(args);\n\n        self.spacetime(&cmd_args)\n    }\n\n    /// Describes the database schema.\n    pub fn describe(&self) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        self.spacetime(&[\"describe\", \"--server\", &self.server_url, identity.as_str()])\n    }\n\n    /// Describes the database schema anonymously (requires --json).\n    pub fn describe_anon(&self) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        self.spacetime(&[\n            \"describe\",\n            \"--anonymous\",\n            \"--json\",\n            \"--server\",\n            &self.server_url,\n            identity.as_str(),\n        ])\n    }\n\n    /// Executes a SQL query against the database.\n    pub fn sql(&self, query: &str) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        self.spacetime(&[\"sql\", \"--server\", &self.server_url, identity.as_str(), query])\n    }\n\n    /// Executes a SQL query with the --confirmed flag.\n    pub fn sql_confirmed(&self, query: &str) -> Result<String> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n\n        self.spacetime(&[\n            \"sql\",\n            \"--server\",\n            &self.server_url,\n            \"--confirmed\",\n            \"true\",\n            identity.as_str(),\n            query,\n        ])\n    }\n\n    /// Asserts that a SQL query produces the expected output.\n    ///\n    /// Both the actual output and expected string have trailing whitespace\n    /// trimmed from each line for comparison.\n    pub fn assert_sql(&self, query: &str, expected: &str) {\n        let actual = self.sql(query).expect(\"SQL query failed\");\n        let actual_normalized = normalize_whitespace(&actual);\n        let expected_normalized = normalize_whitespace(expected);\n\n        assert_eq!(\n            actual_normalized, expected_normalized,\n            \"SQL output mismatch for query: {}\\n\\nExpected:\\n{}\\n\\nActual:\\n{}\",\n            query, expected_normalized, actual_normalized\n        );\n    }\n\n    /// Fetches the last N log entries from the database.\n    pub fn logs(&self, n: usize) -> Result<Vec<String>> {\n        let records = self.log_records(n)?;\n        Ok(records\n            .into_iter()\n            .filter_map(|r| r.get(\"message\").and_then(|m| m.as_str()).map(String::from))\n            .collect())\n    }\n\n    /// Fetches the last N log records as JSON values.\n    pub fn log_records(&self, n: usize) -> Result<Vec<serde_json::Value>> {\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n        let n_str = n.to_string();\n\n        let output = self.spacetime(&[\n            \"logs\",\n            \"--server\",\n            &self.server_url,\n            \"--format=json\",\n            \"-n\",\n            &n_str,\n            \"--\",\n            identity,\n        ])?;\n\n        output\n            .lines()\n            .filter(|line| !line.trim().is_empty())\n            .map(|line| serde_json::from_str(line).context(\"Failed to parse log record\"))\n            .collect()\n    }\n\n    /// Creates a new identity by logging out and logging back in with a server-issued identity.\n    ///\n    /// This is useful for tests that need to test with multiple identities.\n    pub fn new_identity(&self) -> Result<()> {\n        let cli_path = ensure_binaries_built();\n        let config_path_str = self.config_path.to_str().unwrap();\n\n        // Logout first (ignore errors - may not be logged in)\n        let _ = Command::new(&cli_path)\n            .args([\"--config-path\", config_path_str, \"logout\"])\n            .output();\n\n        // Login with server-issued identity\n        // Format: login --server-issued-login <server>\n        let output = Command::new(&cli_path)\n            .args([\n                \"--config-path\",\n                config_path_str,\n                \"login\",\n                \"--server-issued-login\",\n                &self.server_url,\n            ])\n            .output()\n            .context(\"Failed to login with new identity\")?;\n\n        if !output.status.success() {\n            bail!(\n                \"Failed to create new identity:\\nstdout: {}\\nstderr: {}\",\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n\n        Ok(())\n    }\n\n    /// Makes an HTTP API call to the server.\n    ///\n    /// Returns the response body as bytes, or an error with the HTTP status code.\n    pub fn api_call(&self, method: &str, path: &str) -> Result<ApiResponse> {\n        self.api_call_with_body(method, path, None)\n    }\n\n    /// Makes an HTTP API call with an optional request body.\n    pub fn api_call_with_body(&self, method: &str, path: &str, body: Option<&[u8]>) -> Result<ApiResponse> {\n        self.api_call_internal(method, path, body, \"\")\n    }\n\n    /// Makes an HTTP API call with a JSON body.\n    pub fn api_call_json(&self, method: &str, path: &str, json_body: &str) -> Result<ApiResponse> {\n        self.api_call_internal(\n            method,\n            path,\n            Some(json_body.as_bytes()),\n            \"Content-Type: application/json\\r\\n\",\n        )\n    }\n\n    /// Internal HTTP API call implementation.\n    fn api_call_internal(\n        &self,\n        method: &str,\n        path: &str,\n        body: Option<&[u8]>,\n        extra_headers: &str,\n    ) -> Result<ApiResponse> {\n        use std::io::{Read, Write};\n        use std::net::TcpStream;\n\n        // Parse server URL to get host and port\n        let url = &self.server_url;\n        let host_port = url\n            .strip_prefix(\"http://\")\n            .or_else(|| url.strip_prefix(\"https://\"))\n            .unwrap_or(url);\n\n        let mut stream = TcpStream::connect(host_port).context(\"Failed to connect to server\")?;\n        stream.set_read_timeout(Some(std::time::Duration::from_secs(30))).ok();\n\n        // Get auth token\n        let token = self.read_token()?;\n\n        // Build HTTP request\n        let content_length = body.map(|b| b.len()).unwrap_or(0);\n        let request = format!(\n            \"{} {} HTTP/1.1\\r\\nHost: {}\\r\\nContent-Length: {}\\r\\nAuthorization: Bearer {}\\r\\n{}Connection: close\\r\\n\\r\\n\",\n            method, path, host_port, content_length, token, extra_headers\n        );\n\n        stream.write_all(request.as_bytes())?;\n        if let Some(body) = body {\n            stream.write_all(body)?;\n        }\n\n        // Read response\n        let mut response = Vec::new();\n        stream.read_to_end(&mut response)?;\n\n        // Parse HTTP response\n        let response_str = String::from_utf8_lossy(&response);\n        let mut lines = response_str.lines();\n\n        // Parse status line\n        let status_line = lines.next().context(\"Empty response\")?;\n        let status_code: u16 = status_line\n            .split_whitespace()\n            .nth(1)\n            .and_then(|s| s.parse().ok())\n            .context(\"Failed to parse status code\")?;\n\n        // Find body (after empty line)\n        let header_end = response_str.find(\"\\r\\n\\r\\n\").unwrap_or(response_str.len());\n        let body_start = header_end + 4;\n        let body = if body_start < response.len() {\n            response[body_start..].to_vec()\n        } else {\n            Vec::new()\n        };\n\n        Ok(ApiResponse { status_code, body })\n    }\n\n    /// Starts a subscription and waits for N updates (synchronous).\n    ///\n    /// Returns the updates as JSON values.\n    /// For tests that need to perform actions while subscribed, use `subscribe_background` instead.\n    pub fn subscribe(&self, queries: &[&str], n: usize) -> Result<Vec<serde_json::Value>> {\n        self.subscribe_opts(queries, n, None)\n    }\n\n    /// Starts a subscription with --confirmed flag and waits for N updates.\n    pub fn subscribe_confirmed(&self, queries: &[&str], n: usize) -> Result<Vec<serde_json::Value>> {\n        self.subscribe_opts(queries, n, Some(true))\n    }\n\n    /// Internal helper for subscribe with options.\n    fn subscribe_opts(&self, queries: &[&str], n: usize, confirmed: Option<bool>) -> Result<Vec<serde_json::Value>> {\n        let start = Instant::now();\n        let identity = self.database_identity.as_ref().context(\"No database published\")?;\n        let config_path_str = self.config_path.to_str().unwrap();\n\n        let cli_path = ensure_binaries_built();\n        let mut cmd = Command::new(&cli_path);\n        let mut args = vec![\n            \"--config-path\".to_string(),\n            config_path_str.to_string(),\n            \"subscribe\".to_string(),\n            \"--server\".to_string(),\n            self.server_url.to_string(),\n            identity.to_string(),\n            \"-t\".to_string(),\n            \"30\".to_string(),\n            \"-n\".to_string(),\n        ];\n        let n_str = n.to_string();\n        args.push(n_str);\n        args.push(\"--print-initial-update\".to_string());\n        if let Some(confirmed) = confirmed {\n            args.push(\"--confirmed\".to_string());\n            args.push(confirmed.to_string());\n        }\n        args.push(\"--\".to_string());\n        cmd.args(&args)\n            .args(queries)\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped());\n\n        let output = cmd.output().context(\"Failed to run subscribe command\")?;\n        eprintln!(\"[TIMING] subscribe (n={}): {:?}\", n, start.elapsed());\n\n        if !output.status.success() {\n            bail!(\"subscribe failed:\\nstderr: {}\", String::from_utf8_lossy(&output.stderr));\n        }\n\n        let stdout = String::from_utf8_lossy(&output.stdout);\n        stdout\n            .lines()\n            .filter(|line| !line.trim().is_empty())\n            .map(|line| serde_json::from_str(line).context(\"Failed to parse subscription update\"))\n            .collect()\n    }\n\n    /// Starts a subscription in the background and returns a handle.\n    ///\n    /// This matches Python's subscribe semantics - start subscription first,\n    /// perform actions, then call the handle to collect results.\n    pub fn subscribe_background(&self, queries: &[&str], n: usize) -> Result<SubscriptionHandle> {\n        self.subscribe_background_opts(queries, n, None)\n    }\n\n    /// Starts a subscription in the background with --confirmed flag.\n    pub fn subscribe_background_confirmed(&self, queries: &[&str], n: usize) -> Result<SubscriptionHandle> {\n        self.subscribe_background_opts(queries, n, Some(true))\n    }\n\n    /// Starts a subscription in the background with --confirmed flag.\n    pub fn subscribe_background_unconfirmed(&self, queries: &[&str], n: usize) -> Result<SubscriptionHandle> {\n        self.subscribe_background_opts(queries, n, Some(false))\n    }\n\n    /// Internal helper for background subscribe with options.\n    fn subscribe_background_opts(\n        &self,\n        queries: &[&str],\n        n: usize,\n        confirmed: Option<bool>,\n    ) -> Result<SubscriptionHandle> {\n        use std::io::{BufRead, BufReader};\n\n        let identity = self\n            .database_identity\n            .as_ref()\n            .context(\"No database published\")?\n            .clone();\n\n        let cli_path = ensure_binaries_built();\n        let mut cmd = Command::new(&cli_path);\n        // Use --print-initial-update so we know when subscription is established\n        let config_path_str = self.config_path.to_str().unwrap().to_string();\n        let mut args = vec![\n            \"--config-path\".to_string(),\n            config_path_str,\n            \"subscribe\".to_string(),\n            \"--server\".to_string(),\n            self.server_url.clone(),\n            identity,\n            \"-t\".to_string(),\n            \"30\".to_string(),\n            \"-n\".to_string(),\n            n.to_string(),\n            \"--print-initial-update\".to_string(),\n        ];\n        if let Some(confirmed) = confirmed {\n            args.push(\"--confirmed\".to_string());\n            args.push(confirmed.to_string());\n        }\n        args.push(\"--\".to_string());\n        cmd.args(&args)\n            .args(queries)\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped());\n\n        let mut child = cmd.spawn().context(\"Failed to spawn subscribe command\")?;\n        let stdout = child.stdout.take().context(\"No stdout from subscribe\")?;\n        let stderr = child.stderr.take().context(\"No stderr from subscribe\")?;\n        let mut reader = BufReader::new(stdout);\n\n        // Wait for initial update line - this blocks until subscription is established\n        let mut init_line = String::new();\n        reader\n            .read_line(&mut init_line)\n            .context(\"Failed to read initial update from subscribe\")?;\n        eprintln!(\"[SUBSCRIBE] initial update received: {}\", init_line.trim());\n\n        Ok(SubscriptionHandle {\n            child,\n            reader,\n            stderr,\n            n,\n            start: Instant::now(),\n        })\n    }\n}\n\n/// Handle for a background subscription.\npub struct SubscriptionHandle {\n    child: std::process::Child,\n    reader: std::io::BufReader<std::process::ChildStdout>,\n    stderr: std::process::ChildStderr,\n    n: usize,\n    start: Instant,\n}\n\nimpl SubscriptionHandle {\n    /// Wait for the subscription to complete and return the updates.\n    pub fn collect(mut self) -> Result<Vec<serde_json::Value>> {\n        use std::io::{BufRead, Read};\n\n        // Read remaining lines from stdout\n        let mut updates = Vec::new();\n        for line in self.reader.by_ref().lines() {\n            let line = line.context(\"Failed to read line from subscribe\")?;\n            if !line.trim().is_empty() {\n                let value: serde_json::Value =\n                    serde_json::from_str(&line).context(\"Failed to parse subscription update\")?;\n                updates.push(value);\n            }\n        }\n\n        // Wait for child to complete\n        let status = self.child.wait().context(\"Failed to wait for subscribe\")?;\n        eprintln!(\n            \"[TIMING] subscribe_background (n={}): {:?}\",\n            self.n,\n            self.start.elapsed()\n        );\n\n        if !status.success() {\n            let mut stderr_buf = String::new();\n            self.stderr.read_to_string(&mut stderr_buf).ok();\n            bail!(\"subscribe failed:\\nstderr: {}\", stderr_buf);\n        }\n\n        Ok(updates)\n    }\n}\n\n/// Normalizes whitespace by trimming trailing whitespace from each line.\nfn normalize_whitespace(s: &str) -> String {\n    s.lines().map(|line| line.trim_end()).collect::<Vec<_>>().join(\"\\n\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_normalize_whitespace() {\n        let input = \"hello   \\nworld  \\n  foo  \";\n        let expected = \"hello\\nworld\\n  foo\";\n        assert_eq!(normalize_whitespace(input), expected);\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/src/modules.rs",
    "content": "//! Registry for pre-compiled smoketest modules.\n//!\n//! This module provides access to WASM modules that are pre-compiled during the\n//! smoketest warmup phase, eliminating per-test compilation overhead.\n//!\n//! Modules are built from the nested workspace at `crates/smoketests/modules/`\n//! and their WASM outputs are stored in that workspace's target directory.\n//!\n//! Module names are automatically derived from WASM filenames:\n//! - `smoketest_module_foo_bar.wasm` → module name `foo-bar`\n\nuse std::collections::HashMap;\nuse std::path::PathBuf;\nuse std::sync::OnceLock;\n\nuse crate::workspace_root;\n\n/// Registry mapping module names to their pre-compiled WASM paths.\nstatic REGISTRY: OnceLock<HashMap<String, PathBuf>> = OnceLock::new();\n\n/// Returns the path to a pre-compiled module's WASM file.\n///\n/// # Panics\n///\n/// Panics if the module name is not found in the registry. This indicates\n/// either a typo in the module name or that the module hasn't been added\n/// to the nested workspace yet.\npub fn precompiled_module(name: &str) -> PathBuf {\n    let registry = REGISTRY.get_or_init(build_registry);\n    registry.get(name).cloned().unwrap_or_else(|| {\n        panic!(\n            \"Unknown precompiled module: '{}'. Available modules: {:?}\",\n            name,\n            registry.keys().collect::<Vec<_>>()\n        )\n    })\n}\n\n/// Returns the target directory where pre-compiled WASM modules are stored.\nfn modules_target_dir() -> PathBuf {\n    // Respect CARGO_TARGET_DIR if set (e.g., in CI), otherwise use the modules workspace's target dir\n    let base = std::env::var(\"CARGO_TARGET_DIR\")\n        .map(PathBuf::from)\n        .unwrap_or_else(|_| workspace_root().join(\"crates/smoketests/modules/target\"));\n    base.join(\"wasm32-unknown-unknown/release\")\n}\n\n/// Builds the registry by scanning the target directory for WASM files.\n///\n/// Module names are derived from filenames:\n/// - `smoketest_module_foo_bar.wasm` → `foo-bar`\nfn build_registry() -> HashMap<String, PathBuf> {\n    let target = modules_target_dir();\n    let mut reg = HashMap::new();\n\n    let Ok(entries) = std::fs::read_dir(&target) else {\n        return reg;\n    };\n\n    for entry in entries.filter_map(Result::ok) {\n        let path = entry.path();\n        if let Some(module_name) = wasm_to_module_name(path.clone()) {\n            reg.insert(module_name, path);\n        }\n    }\n\n    reg\n}\n\n/// Extract module name: smoketest_module_foo_bar.wasm -> foo-bar\nfn wasm_to_module_name(path: PathBuf) -> Option<String> {\n    let filename = path.file_name()?.to_str()?;\n    // Only process smoketest_module_*.wasm files\n    if !filename.starts_with(\"smoketest_module_\") || !filename.ends_with(\".wasm\") {\n        return None;\n    }\n    Some(\n        filename\n            .strip_prefix(\"smoketest_module_\")\n            .unwrap()\n            .strip_suffix(\".wasm\")\n            .unwrap()\n            .replace('_', \"-\"),\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_module_name_derivation() {\n        // Test the naming convention\n        let filename = \"smoketest_module_foo_bar.wasm\";\n        let expected = \"foo-bar\";\n        let actual = wasm_to_module_name(PathBuf::from(filename));\n        assert_eq!(actual, Some(expected.to_string()));\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/tests/integration.rs",
    "content": "// Single test binary entry point - includes all smoketests\n// We put the tests in a single submodule because if they are at the toplevel then\n// they all build and link independently, which takes a lot of linker time.\n// This has the unfortunate side effect of requiring that they are all listed in a mod.rs,\n// but what can you do ¯\\_(ツ)_/¯.\nmod smoketests;\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/add_remove_index.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\nconst JOIN_QUERY: &str = \"select t_1.* from t_1 join t_2 on t_1.id = t_2.id where t_2.id = 1001\";\n\n/// First publish without the indices,\n/// then add the indices, and publish,\n/// and finally remove the indices, and publish again.\n/// There should be no errors\n/// and the unindexed versions should reject subscriptions.\n#[test]\nfn test_add_then_remove_index() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"add-remove-index\")\n        .autopublish(false)\n        .build();\n\n    // TODO: Does the name do anything? Other tests just let the DB assign.\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Publish and attempt a subscribing to a join query.\n    // There are no indices, resulting in an unsupported unindexed join.\n    test.publish_module_named(&name, false).unwrap();\n    let result = test.subscribe(&[JOIN_QUERY], 0);\n    assert!(result.is_err(), \"Expected subscription to fail without indices\");\n\n    // Publish the indexed version.\n    // Now we have indices, so the query should be accepted.\n    test.use_precompiled_module(\"add-remove-index-indexed\");\n    test.publish_module_named(&name, false).unwrap();\n\n    // Subscribe and hold across the call, then collect results\n    let sub = test.subscribe_background(&[JOIN_QUERY], 1).unwrap();\n    test.call_anon(\"add\", &[]).unwrap();\n    let results = sub.collect().unwrap();\n    assert_eq!(results.len(), 1, \"Expected 1 update from subscription\");\n\n    // Publish the unindexed version again, removing the index.\n    // The initial subscription should be rejected again.\n    test.use_precompiled_module(\"add-remove-index\");\n    test.publish_module_named(&name, false).unwrap();\n    let result = test.subscribe(&[JOIN_QUERY], 0);\n    assert!(result.is_err(), \"Expected subscription to fail after removing indices\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/auto_inc.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\nstruct IntTy {\n    ty: &'static str,\n    name: &'static str,\n}\n\nconst INT_TYPES: &[IntTy] = &[\n    IntTy { ty: \"u8\", name: \"u_8\" },\n    IntTy {\n        ty: \"u16\",\n        name: \"u_16\",\n    },\n    IntTy {\n        ty: \"u32\",\n        name: \"u_32\",\n    },\n    IntTy {\n        ty: \"u64\",\n        name: \"u_64\",\n    },\n    IntTy {\n        ty: \"u128\",\n        name: \"u_128\",\n    },\n    IntTy { ty: \"i8\", name: \"i_8\" },\n    IntTy {\n        ty: \"i16\",\n        name: \"i_16\",\n    },\n    IntTy {\n        ty: \"i32\",\n        name: \"i_32\",\n    },\n    IntTy {\n        ty: \"i64\",\n        name: \"i_64\",\n    },\n    IntTy {\n        ty: \"i128\",\n        name: \"i_128\",\n    },\n];\n\n#[test]\nfn test_autoinc_basic() {\n    let test = Smoketest::builder().precompiled_module(\"autoinc-basic\").build();\n\n    for int in INT_TYPES {\n        test.call(&format!(\"add_{}\", int.name), &[r#\"\"Robert\"\"#, \"1\"]).unwrap();\n        test.call(&format!(\"add_{}\", int.name), &[r#\"\"Julie\"\"#, \"2\"]).unwrap();\n        test.call(&format!(\"add_{}\", int.name), &[r#\"\"Samantha\"\"#, \"3\"])\n            .unwrap();\n        test.call(&format!(\"say_hello_{}\", int.name), &[]).unwrap();\n\n        let logs = test.logs(4).unwrap();\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, 3:Samantha!\")),\n            \"[{}] Expected 'Hello, 3:Samantha!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, 2:Julie!\")),\n            \"[{}] Expected 'Hello, 2:Julie!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, 1:Robert!\")),\n            \"[{}] Expected 'Hello, 1:Robert!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, World!\")),\n            \"[{}] Expected 'Hello, World!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n    }\n}\n\n#[test]\nfn test_autoinc_unique() {\n    let test = Smoketest::builder().precompiled_module(\"autoinc-unique\").build();\n\n    for int in INT_TYPES {\n        test.call(&format!(\"update_{}\", int.name), &[r#\"\"Robert\"\"#, \"2\"])\n            .unwrap();\n        test.call(&format!(\"add_new_{}\", int.name), &[r#\"\"Success\"\"#]).unwrap();\n\n        let result = test.call(&format!(\"add_new_{}\", int.name), &[r#\"\"Failure\"\"#]);\n        assert!(\n            result.is_err(),\n            \"[{}] Expected add_new to fail due to unique constraint violation\",\n            int.ty\n        );\n\n        test.call(&format!(\"say_hello_{}\", int.name), &[]).unwrap();\n\n        let logs = test.logs(4).unwrap();\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, 2:Robert!\")),\n            \"[{}] Expected 'Hello, 2:Robert!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, 1:Success!\")),\n            \"[{}] Expected 'Hello, 1:Success!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n        assert!(\n            logs.iter().any(|msg| msg.contains(\"Hello, World!\")),\n            \"[{}] Expected 'Hello, World!' in logs, got: {:?}\",\n            int.ty,\n            logs\n        );\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/auto_migration.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\nconst MODULE_CODE_SIMPLE: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"{}: {}\", prefix, person.name);\n    }\n}\n\"#;\n\nconst MODULE_CODE_UPDATED_INCOMPATIBLE: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table};\n\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n    age: u128,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name, age: 70 });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"{}: {}\", prefix, person.name);\n    }\n}\n\"#;\n\n/// Tests that a module with invalid schema changes cannot be published without -c or a migration.\n#[test]\nfn test_reject_schema_changes() {\n    let mut test = Smoketest::builder().module_code(MODULE_CODE_SIMPLE).build();\n\n    // Try to update with incompatible schema (adding column without default)\n    test.write_module_code(MODULE_CODE_UPDATED_INCOMPATIBLE).unwrap();\n    let result = test.publish_module_clear(false);\n\n    assert!(\n        result.is_err(),\n        \"Expected publish to fail with incompatible schema change\"\n    );\n}\n\nconst MODULE_CODE_INIT: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table, SpacetimeType};\nuse PersonKind::*;\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n    kind: PersonKind,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String, kind: String) {\n    let kind = kind_from_string(kind);\n    ctx.db.person().insert(Person { name, kind });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        let kind = kind_to_string(person.kind);\n        log::info!(\"{prefix}: {} - {kind}\", person.name);\n    }\n}\n\n#[spacetimedb::table(accessor = point_mass)]\npub struct PointMass {\n    mass: f64,\n    position: Vector2,\n}\n\n#[derive(SpacetimeType, Clone, Copy)]\npub struct Vector2 {\n    x: f64,\n    y: f64,\n}\n\n#[spacetimedb::table(accessor = person_info)]\npub struct PersonInfo {\n    #[primary_key]\n    id: u64,\n}\n\n#[derive(SpacetimeType, Clone, Copy, PartialEq, Eq)]\npub enum PersonKind {\n    Student,\n}\n\nfn kind_from_string(_: String) -> PersonKind {\n    Student\n}\n\nfn kind_to_string(Student: PersonKind) -> &'static str {\n    \"Student\"\n}\n\"#;\n\nconst MODULE_CODE_UPDATED: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table, SpacetimeType};\nuse PersonKind::*;\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n    kind: PersonKind,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String, kind: String) {\n    let kind = kind_from_string(kind);\n    ctx.db.person().insert(Person { name, kind });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        let kind = kind_to_string(person.kind);\n        log::info!(\"{prefix}: {} - {kind}\", person.name);\n    }\n}\n\n#[spacetimedb::table(accessor = point_mass)]\npub struct PointMass {\n    mass: f64,\n    position: Vector2,\n}\n\n#[derive(SpacetimeType, Clone, Copy)]\npub struct Vector2 {\n    x: f64,\n    y: f64,\n}\n\n#[spacetimedb::table(accessor = person_info)]\npub struct PersonInfo {\n    #[primary_key]\n    #[auto_inc]\n    id: u64,\n}\n\n#[derive(SpacetimeType, Clone, Copy, PartialEq, Eq)]\npub enum PersonKind {\n    Student,\n    Professor,\n}\n\nfn kind_from_string(kind: String) -> PersonKind {\n    match &*kind {\n        \"Student\" => Student,\n        \"Professor\" => Professor,\n        _ => panic!(),\n    }\n}\n\nfn kind_to_string(kind: PersonKind) -> &'static str {\n    match kind {\n        Student => \"Student\",\n        Professor => \"Professor\",\n    }\n}\n\n#[spacetimedb::table(accessor = book, public)]\npub struct Book {\n    isbn: String,\n}\n\n#[spacetimedb::reducer]\npub fn add_book(ctx: &ReducerContext, isbn: String) {\n    ctx.db.book().insert(Book { isbn });\n}\n\n#[spacetimedb::reducer]\npub fn print_books(ctx: &ReducerContext, prefix: String) {\n    for book in ctx.db.book().iter() {\n        log::info!(\"{}: {}\", prefix, book.isbn);\n    }\n}\n\"#;\n\n/// Tests uploading a module with a schema change that should not require clearing the database.\n#[test]\nfn test_add_table_auto_migration() {\n    let mut test = Smoketest::builder().module_code(MODULE_CODE_INIT).build();\n\n    let sub = test.subscribe_background(&[\"select * from person\"], 4).unwrap();\n\n    // Add initial data\n    test.call(\"add_person\", &[\"Robert\", \"Student\"]).unwrap();\n    test.call(\"add_person\", &[\"Julie\", \"Student\"]).unwrap();\n    test.call(\"add_person\", &[\"Samantha\", \"Student\"]).unwrap();\n    test.call(\"print_persons\", &[\"BEFORE\"]).unwrap();\n\n    let logs = test.logs(100).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"BEFORE: Samantha - Student\")),\n        \"Expected Samantha in logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"BEFORE: Julie - Student\")),\n        \"Expected Julie in logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"BEFORE: Robert - Student\")),\n        \"Expected Robert in logs: {:?}\",\n        logs\n    );\n\n    // Update module without clearing database\n    test.write_module_code(MODULE_CODE_UPDATED).unwrap();\n    test.publish_module_clear(false).unwrap();\n\n    // Add new data with updated schema\n    test.call(\"add_person\", &[\"Husserl\", \"Student\"]).unwrap();\n\n    let sub_updates = sub.collect().unwrap();\n    assert_eq!(\n        sub_updates.len(),\n        4,\n        \"Expected 4 subscription updates, got {}: {:?}\",\n        sub_updates.len(),\n        sub_updates\n    );\n    test.call(\"add_person\", &[\"Husserl\", \"Professor\"]).unwrap();\n    test.call(\"add_book\", &[\"1234567890\"]).unwrap();\n    test.call(\"print_persons\", &[\"AFTER_PERSON\"]).unwrap();\n    test.call(\"print_books\", &[\"AFTER_BOOK\"]).unwrap();\n\n    let logs = test.logs(100).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"AFTER_PERSON: Samantha - Student\")),\n        \"Expected Samantha in AFTER logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"AFTER_PERSON: Julie - Student\")),\n        \"Expected Julie in AFTER logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"AFTER_PERSON: Robert - Student\")),\n        \"Expected Robert in AFTER logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"AFTER_PERSON: Husserl - Professor\")),\n        \"Expected Husserl Professor in AFTER logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"AFTER_BOOK: 1234567890\")),\n        \"Expected book ISBN in AFTER logs: {:?}\",\n        logs\n    );\n}\n\nconst MODULE_CODE_ADD_TABLE_COLUMNS_UPDATED: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table};\n\n#[derive(Debug)]\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    #[index(btree)]\n    name: String,\n    #[default(0)]\n    age: u16,\n    #[default(19)]\n    mass: u16,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name, age: 70, mass: 180 });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"{}: {:?}\", prefix, person);\n    }\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn identity_disconnected(_ctx: &ReducerContext) {\n    log::info!(\"FIRST_UPDATE: client disconnected\");\n}\n\"#;\n\nconst MODULE_CODE_ADD_TABLE_COLUMNS_UPDATED_AGAIN: &str = r#\"\nuse spacetimedb::{log, ReducerContext, Table};\n\n#[derive(Debug)]\n#[spacetimedb::table(accessor = person)]\npub struct Person {\n    name: String,\n    age: u16,\n    #[default(19)]\n    mass: u16,\n    #[default(160)]\n    height: u32,\n}\n\n#[spacetimedb::reducer]\npub fn add_person(ctx: &ReducerContext, name: String) {\n    ctx.db.person().insert(Person { name, age: 70, mass: 180, height: 72 });\n}\n\n#[spacetimedb::reducer]\npub fn print_persons(ctx: &ReducerContext, prefix: String) {\n    for person in ctx.db.person().iter() {\n        log::info!(\"{}: {:?}\", prefix, person);\n    }\n}\n\"#;\n\n/// Verify schema upgrades that add columns with defaults (twice).\n#[test]\nfn test_add_table_columns() {\n    const NUM_SUBSCRIBERS: usize = 20;\n\n    let mut test = Smoketest::builder().module_code(MODULE_CODE_SIMPLE).build();\n\n    // Subscribe to person table changes multiple times to simulate active clients\n    let mut subs = Vec::with_capacity(NUM_SUBSCRIBERS);\n    for _ in 0..NUM_SUBSCRIBERS {\n        // We need unconfirmed reads for the updates to arrive properly.\n        // Otherwise, there's a race between module teardown in publish, vs subscribers\n        // getting the row deletion they expect.\n        subs.push(\n            test.subscribe_background_unconfirmed(&[\"select * from person\"], 5)\n                .unwrap(),\n        );\n    }\n\n    // Insert under initial schema\n    test.call(\"add_person\", &[\"Robert\"]).unwrap();\n\n    // First upgrade: add age & mass columns\n    test.write_module_code(MODULE_CODE_ADD_TABLE_COLUMNS_UPDATED).unwrap();\n    let identity = test.database_identity.clone().unwrap();\n    test.publish_module_with_options(&identity, false, true).unwrap();\n    test.call(\"print_persons\", &[\"FIRST_UPDATE\"]).unwrap();\n\n    let logs1 = test.logs(100).unwrap();\n    assert!(\n        logs1.iter().any(|l| l.contains(\"Disconnecting all users\")),\n        \"Expected disconnect log in logs: {:?}\",\n        logs1\n    );\n    assert!(\n        logs1\n            .iter()\n            .any(|l| l.contains(\"FIRST_UPDATE: Person { name: \\\"Robert\\\", age: 0, mass: 19 }\")),\n        \"Expected migrated person with defaults in logs: {:?}\",\n        logs1\n    );\n\n    let disconnect_count = logs1\n        .iter()\n        .filter(|l| l.contains(\"FIRST_UPDATE: client disconnected\"))\n        .count();\n    assert_eq!(\n        disconnect_count,\n        NUM_SUBSCRIBERS + 1,\n        \"Unexpected disconnect counts: {disconnect_count}\"\n    );\n\n    // Insert new data under upgraded schema\n    test.call(\"add_person\", &[\"Robert2\"]).unwrap();\n\n    // Validate all subscribers were disconnected after first upgrade\n    for (i, sub) in subs.into_iter().enumerate() {\n        let rows = sub.collect().unwrap();\n        assert_eq!(rows.len(), 2, \"Subscriber {i} received unexpected rows: {rows:?}\");\n    }\n\n    // Second upgrade\n    test.write_module_code(MODULE_CODE_ADD_TABLE_COLUMNS_UPDATED_AGAIN)\n        .unwrap();\n    test.publish_module_with_options(&identity, false, true).unwrap();\n    test.call(\"print_persons\", &[\"UPDATE_2\"]).unwrap();\n\n    let logs2 = test.logs(100).unwrap();\n    assert!(\n        logs2\n            .iter()\n            .any(|l| { l.contains(\"UPDATE_2: Person { name: \\\"Robert2\\\", age: 70, mass: 180, height: 160 }\") }),\n        \"Expected updated schema with default height in logs: {:?}\",\n        logs2\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/call.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Check calling a reducer (no return) and procedure (return)\n#[test]\nfn test_call_reducer_procedure() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"call-reducer-procedure\")\n        .build();\n\n    // Reducer returns empty\n    let msg = test.call(\"say_hello\", &[]).unwrap();\n    assert_eq!(msg.trim(), \"\");\n\n    // Procedure returns a value\n    let msg = test.call(\"return_person\", &[]).unwrap();\n    assert_eq!(msg.trim(), r#\"[\"World\"]\"#);\n}\n\n/// Check calling a non-existent reducer/procedure raises error\n#[test]\nfn test_call_errors() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"call-reducer-procedure\")\n        .build();\n\n    let identity = test.database_identity.as_ref().unwrap();\n\n    // Non-existent reducer\n    let output = test.call_output(\"non_existent_reducer\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `non_existent_reducer` for database `{identity}` resolving to identity `{identity}`.\n\nHere are some existing reducers:\n- say_hello\n\nHere are some existing procedures:\n- return_person\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n\n    // Non-existent procedure\n    let output = test.call_output(\"non_existent_procedure\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `non_existent_procedure` for database `{identity}` resolving to identity `{identity}`.\n\nHere are some existing reducers:\n- say_hello\n\nHere are some existing procedures:\n- return_person\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n\n    // Similar name to reducer - should suggest similar\n    let output = test.call_output(\"say_hell\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `say_hell` for database `{identity}` resolving to identity `{identity}`.\n\nA reducer with a similar name exists: `say_hello`\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n\n    // Similar name to procedure - should suggest similar\n    let output = test.call_output(\"return_perso\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `return_perso` for database `{identity}` resolving to identity `{identity}`.\n\nA procedure with a similar name exists: `return_person`\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n}\n\n/// Check calling into a database with no reducers/procedures raises error\n#[test]\nfn test_call_empty_errors() {\n    let test = Smoketest::builder().precompiled_module(\"call-empty\").build();\n\n    let identity = test.database_identity.as_ref().unwrap();\n\n    let output = test.call_output(\"non_existent\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `non_existent` for database `{identity}` resolving to identity `{identity}`.\n\nThe database has no reducers.\n\nThe database has no procedures.\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n}\n\n/// Generate module code with many reducers and procedures\nfn generate_many_module_code() -> String {\n    let mut code = String::from(\n        r#\"\nuse spacetimedb::{log, ProcedureContext, ReducerContext};\n\"#,\n    );\n\n    for i in 0..11 {\n        code.push_str(&format!(\n            r#\"\n#[spacetimedb::reducer]\npub fn say_reducer_{i}(_ctx: &ReducerContext) {{\n    log::info!(\"Hello from reducer {i}!\");\n}}\n\n#[spacetimedb::procedure]\npub fn say_procedure_{i}(_ctx: &mut ProcedureContext) {{\n    log::info!(\"Hello from procedure {i}!\");\n}}\n\"#\n        ));\n    }\n\n    code\n}\n\n/// Check calling into a database with many reducers/procedures raises error with listing\n#[test]\nfn test_call_many_errors() {\n    let module_code = generate_many_module_code();\n    let test = Smoketest::builder().module_code(&module_code).build();\n\n    let identity = test.database_identity.as_ref().unwrap();\n\n    let output = test.call_output(\"non_existent\", &[]);\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n\n    let expected = format!(\n        \"WARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: No such reducer OR procedure `non_existent` for database `{identity}` resolving to identity `{identity}`.\n\nHere are some existing reducers:\n- say_reducer_0\n- say_reducer_1\n- say_reducer_2\n- say_reducer_3\n- say_reducer_4\n- say_reducer_5\n- say_reducer_6\n- say_reducer_7\n- say_reducer_8\n- say_reducer_9\n... (1 reducer not shown)\n\nHere are some existing procedures:\n- say_procedure_0\n- say_procedure_1\n- say_procedure_2\n- say_procedure_3\n- say_procedure_4\n- say_procedure_5\n- say_procedure_6\n- say_procedure_7\n- say_procedure_8\n- say_procedure_9\n... (1 procedure not shown)\"\n    );\n    assert!(\n        expected.contains(stderr.trim()),\n        \"Expected stderr to be contained in expected message.\\nExpected:\\n{}\\n\\nActual stderr:\\n{}\",\n        expected,\n        stderr.trim()\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/change_host_type.rs",
    "content": "use spacetimedb::messages::control_db::HostType;\nuse spacetimedb_smoketests::{require_local_server, require_pnpm, Smoketest};\n\nconst TS_MODULE_BASIC: &str = r#\"import { schema, t, table } from \"spacetimedb/server\";\n\nconst person = table(\n    { name: \"person\", public: true },\n    {\n        id: t.u64().primaryKey().autoInc(),\n        name: t.string()\n    }\n);\nconst spacetimedb = schema({ person });\nexport default spacetimedb;\n\nexport const add = spacetimedb.reducer({ name: t.string() }, (ctx, { name }) => {\n  ctx.db.person.insert({ id: 0n, name });\n});\n\"#;\n\n/// Tests that updating a module and also changing the host type works.\n///\n/// Note that this test restarts the server.\n#[test]\nfn test_update_with_different_host_type() {\n    require_pnpm!();\n    require_local_server!();\n\n    const PERSON_A: &str = \"Person A\";\n    const PERSON_B: &str = \"Person B\";\n    const PERSON_C: &str = \"Person C\";\n\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"modules-basic\")\n        .autopublish(false)\n        .build();\n\n    let database_identity = test.publish_module().unwrap();\n    add_person(&test, PERSON_A, \"initial\");\n\n    // Publish a TS module.\n    test.publish_typescript_module_source_clear(\"modules-basic-ts\", &database_identity, TS_MODULE_BASIC, false)\n        .unwrap();\n    add_person(&test, PERSON_B, \"post module update\");\n\n    // Restart and assert that the data is still there.\n    test.restart_server();\n    assert_has_rows(&test, &[PERSON_A, PERSON_B], \"post restart\");\n\n    // Change back to original module and assert that the data is still there.\n    test.publish_module_clear(false).unwrap();\n    add_person(&test, PERSON_C, \"post revert\");\n\n    // Restart once more and assert that the data is still there.\n    test.restart_server();\n    assert_has_rows(&test, &[PERSON_A, PERSON_B, PERSON_C], \"post restart 2\");\n}\n\nfn add_person(test: &Smoketest, name: &str, context: &str) {\n    test.call(\"add\", &[name]).unwrap();\n    assert_has_person(test, name, context);\n}\n\nfn assert_has_person(test: &Smoketest, name: &str, context: &str) {\n    let output = test\n        .sql_confirmed(&format!(\"select * from person where name = '{name}'\"))\n        .unwrap();\n    assert!(\n        output.contains(name),\n        \"{}: expected {} to be in result: {}\",\n        context,\n        name,\n        output\n    );\n}\n\nfn assert_has_rows(test: &Smoketest, names: &[&str], context: &str) {\n    let output = test.sql_confirmed(\"select * from person\").unwrap();\n    assert!(\n        output\n            .lines()\n            .skip(2)\n            .all(|row| names.iter().any(|name| row.contains(name))),\n        \"{context}: expected all of {names:?} to be in result: {output}\"\n    )\n}\n\n/// Tests that a legacy database that has a wrong host type in `st_module` is\n/// auto-repaired upon startup.\n///\n/// NOTE: The repair mechanism shall be removed eventually, and so shall this\n/// test (which will fail when the mechanism is sunset).\n///\n/// This test restarts the server.\n#[test]\nfn test_repair_host_type() {\n    require_pnpm!();\n    require_local_server!();\n\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    test.publish_typescript_module_source(\"modules-basic-ts\", \"basic-ts\", TS_MODULE_BASIC)\n        .unwrap();\n    assert_host_type(&test, HostType::Js);\n    // Set the program kind to the wrong value.\n    test.sql_confirmed(\"update st_module set program_kind=0\").unwrap();\n    assert_host_type(&test, HostType::Wasm);\n\n    // After restarting, the database both comes up and has the right host type.\n    test.restart_server();\n    assert_host_type(&test, HostType::Js);\n}\n\nfn assert_host_type(test: &Smoketest, host_type: HostType) {\n    let output = test.sql_confirmed(\"select program_kind from st_module\").unwrap();\n    let rows = output.lines().skip(2).map(|s| s.trim()).collect::<Vec<_>>();\n    match host_type {\n        HostType::Wasm => assert_eq!(&rows, &[\"0\"]),\n        HostType::Js => assert_eq!(&rows, &[\"1\"]),\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/auth.rs",
    "content": "//! CLI auth command tests (`login` / `logout`)\n\nuse spacetimedb_smoketests::{require_local_server, Smoketest};\nuse std::fs;\nuse std::process::Output;\nuse std::time::{Duration, Instant};\n\nfn output_stdout(output: &Output) -> String {\n    String::from_utf8_lossy(&output.stdout).to_string()\n}\n\nfn output_stderr(output: &Output) -> String {\n    String::from_utf8_lossy(&output.stderr).to_string()\n}\n\nfn assert_success(output: &Output, context: &str) {\n    assert!(\n        output.status.success(),\n        \"{context} failed:\\nstdout: {}\\nstderr: {}\",\n        output_stdout(output),\n        output_stderr(output),\n    );\n}\n\nfn read_config(test: &Smoketest) -> toml::Table {\n    let raw = fs::read_to_string(&test.config_path).expect(\"Failed to read config\");\n    raw.parse::<toml::Table>().expect(\"Failed to parse config\")\n}\n\nfn write_config(test: &Smoketest, config: &toml::Table) {\n    let raw = toml::to_string(config).expect(\"Failed to serialize config\");\n    fs::write(&test.config_path, raw).expect(\"Failed to write config\");\n}\n\n#[test]\nfn cli_logout_removes_cached_tokens() {\n    require_local_server!();\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let login = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&login, \"initial login\");\n\n    // Simulate a cached web session token; logout should clear both token fields.\n    let mut config = read_config(&test);\n    config.insert(\n        \"web_session_token\".to_string(),\n        toml::Value::String(\"fake-web-session-token\".to_string()),\n    );\n    write_config(&test, &config);\n\n    let logout = test.spacetime_cmd(&[\"logout\"]);\n    assert_success(&logout, \"logout\");\n    assert!(\n        output_stdout(&logout).contains(\"Logged out (identity \"),\n        \"logout stdout should include identity message:\\n{}\",\n        output_stdout(&logout),\n    );\n\n    let config_after = read_config(&test);\n    assert!(\n        config_after.get(\"spacetimedb_token\").is_none(),\n        \"spacetimedb_token should be removed after logout: {:?}\",\n        config_after.get(\"spacetimedb_token\")\n    );\n    assert!(\n        config_after.get(\"web_session_token\").is_none(),\n        \"web_session_token should be removed after logout: {:?}\",\n        config_after.get(\"web_session_token\")\n    );\n}\n\n#[test]\n// Even if there's no web session, logout still removes the SpacetimeDB token\nfn cli_logout_removes_cached_tokens_without_web_token() {\n    require_local_server!();\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let login = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&login, \"initial login\");\n\n    let logout = test.spacetime_cmd(&[\"logout\"]);\n    assert_success(&logout, \"logout\");\n    assert!(\n        output_stdout(&logout).contains(\"Logged out (identity \"),\n        \"logout stdout should include identity message:\\n{}\",\n        output_stdout(&logout),\n    );\n\n    let config_after = read_config(&test);\n    assert!(\n        config_after.get(\"spacetimedb_token\").is_none(),\n        \"spacetimedb_token should be removed after logout: {:?}\",\n        config_after.get(\"spacetimedb_token\")\n    );\n    assert!(\n        config_after.get(\"web_session_token\").is_none(),\n        \"web_session_token should be removed after logout: {:?}\",\n        config_after.get(\"web_session_token\")\n    );\n}\n\n#[test]\nfn cli_logout_is_idempotent() {\n    require_local_server!();\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let login = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&login, \"initial login\");\n\n    let first_logout = test.spacetime_cmd(&[\"logout\"]);\n    assert_success(&first_logout, \"first logout\");\n    assert!(\n        output_stdout(&first_logout).contains(\"Logged out \"),\n        \"first logout should report logged-out:\\n{}\",\n        output_stdout(&first_logout)\n    );\n\n    let second_logout = test.spacetime_cmd(&[\"logout\"]);\n    assert_success(&second_logout, \"second logout\");\n    assert!(\n        output_stdout(&second_logout).contains(\"You are not logged in.\"),\n        \"second logout should report not logged in:\\n{}\",\n        output_stdout(&second_logout)\n    );\n}\n\n#[test]\nfn cli_direct_login_works_and_shows_core_messages() {\n    require_local_server!();\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let login = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&login, \"direct login\");\n\n    let login_stdout = output_stdout(&login);\n    assert!(\n        login_stdout.contains(\"Logged in \"),\n        \"direct login stdout missing confirmation:\\n{}\",\n        login_stdout\n    );\n\n    let show = test.spacetime_cmd(&[\"login\", \"show\"]);\n    assert_success(&show, \"login show\");\n    assert!(\n        output_stdout(&show).contains(\"You are logged in as \"),\n        \"login show should report current identity:\\n{}\",\n        output_stdout(&show)\n    );\n}\n\n#[test]\nfn cli_logging_in_twice_works() {\n    require_local_server!();\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let first = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&first, \"first login\");\n\n    let second = test.spacetime_cmd(&[\"login\", \"--server-issued-login\", &test.server_url]);\n    assert_success(&second, \"second login\");\n\n    let second_stdout = output_stdout(&second);\n    assert!(\n        second_stdout.contains(\"Logged out (identity \"),\n        \"second login should log out previous identity first:\\n{}\",\n        second_stdout\n    );\n    assert!(\n        second_stdout.contains(\"Logged in with identity \"),\n        \"second login should complete with a new login:\\n{}\",\n        second_stdout\n    );\n}\n\nfn try_until_timeout<F: FnMut() -> Option<R>, R>(timeout: Duration, mut f: F) -> Option<R> {\n    let start = Instant::now();\n    loop {\n        match f() {\n            Some(result) => return Some(result),\n            None => {\n                if start.elapsed() > timeout {\n                    return None;\n                }\n                std::thread::sleep(std::time::Duration::from_millis(200));\n            }\n        }\n    }\n}\n\n/// Test that `spacetime login --token <token>` exits immediately after saving\n/// the token, without falling through to the interactive web login flow.\n///\n/// Without the fix in PR #4579, the command would fall through to the web\n/// login flow, which hangs waiting for a browser callback.\n#[test]\nfn cli_login_with_token() {\n    use std::io::Read;\n    use std::process::{Command, Stdio};\n\n    let test = Smoketest::builder().autopublish(false).build();\n    let cli_path = spacetimedb_guard::ensure_binaries_built();\n\n    let mut child = Command::new(&cli_path)\n        .arg(\"--config-path\")\n        .arg(&test.config_path)\n        .args([\"login\", \"--token\", \"test-dummy-token\", \"--no-browser\"])\n        .env_remove(\"BROWSER\")\n        .stdout(Stdio::piped())\n        .stderr(Stdio::piped())\n        .spawn()\n        .expect(\"Failed to spawn spacetime login\");\n\n    // Run with timeout in case something goes wrong and it tries to open the browser for login.\n    let timeout = Duration::from_secs(5);\n    let Some(status) = try_until_timeout(timeout, || child.try_wait().expect(\"Failed to poll child\")) else {\n        child.kill().ok();\n        panic!(\n            \"spacetime login --token hung for >{timeout:?} — \\\n            likely fell through to web login flow\"\n        );\n    };\n    let mut stdout = String::new();\n    child.stdout.take().unwrap().read_to_string(&mut stdout).unwrap();\n    assert!(\n        status.success(),\n        \"spacetime login --token failed (exit {status}):\\n{stdout}\"\n    );\n    assert!(\n        stdout.contains(\"Token saved.\"),\n        \"Expected 'Token saved.' in output, got: {stdout}\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/dev.rs",
    "content": "//! CLI dev command tests\n\nuse predicates::prelude::*;\nuse spacetimedb_guard::ensure_binaries_built;\nuse std::process::Command;\n\nfn cli_cmd() -> Command {\n    Command::new(ensure_binaries_built())\n}\n\n#[test]\nfn cli_dev_help_shows_template_option() {\n    let output = cli_cmd().args([\"dev\", \"--help\"]).output().expect(\"failed to execute\");\n\n    assert!(output.status.success());\n    let stdout = String::from_utf8_lossy(&output.stdout);\n    assert!(\n        predicate::str::contains(\"--template\").eval(&stdout),\n        \"stdout should contain --template\"\n    );\n    assert!(predicate::str::contains(\"-t\").eval(&stdout), \"stdout should contain -t\");\n}\n\n#[test]\nfn cli_dev_accepts_template_flag() {\n    // Running with an invalid server should fail, but not because of the template flag\n    let output = cli_cmd()\n        .args([\"dev\", \"--template\", \"react\", \"--server\", \"nonexistent-server-12345\"])\n        .output()\n        .expect(\"failed to execute\");\n\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    // The error should be about the server, not about an unrecognized --template flag\n    assert!(\n        !stderr.contains(\"unrecognized\") || !stderr.contains(\"template\"),\n        \"stderr should not complain about unrecognized template flag\"\n    );\n}\n\n#[test]\nfn cli_dev_accepts_short_template_flag() {\n    let output = cli_cmd()\n        .args([\"dev\", \"-t\", \"typescript\", \"--server\", \"nonexistent-server-12345\"])\n        .output()\n        .expect(\"failed to execute\");\n\n    assert!(!output.status.success());\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    // The error should be about the server, not about an unrecognized -t flag\n    assert!(\n        !stderr.contains(\"unrecognized\") || !stderr.contains(\"-t\"),\n        \"stderr should not complain about unrecognized -t flag\"\n    );\n}\n\n#[test]\nfn cli_init_with_template_creates_project() {\n    let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n\n    let output = cli_cmd()\n        .current_dir(temp_dir.path())\n        .args([\n            \"init\",\n            \"--template\",\n            \"basic-rs\",\n            \"--local\",\n            \"--non-interactive\",\n            \"test-project\",\n        ])\n        .output()\n        .expect(\"failed to execute\");\n\n    assert!(\n        output.status.success(),\n        \"init failed: {}\",\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    // Verify expected files were created\n    let project_dir = temp_dir.path().join(\"test-project\");\n    assert!(\n        project_dir.join(\"spacetimedb\").exists(),\n        \"spacetimedb directory should exist\"\n    );\n    assert!(project_dir.join(\"src\").exists(), \"src directory should exist\");\n}\n\n#[test]\nfn config_with_invalid_field_shows_error() {\n    // Test that using invalid field names shows a helpful error message\n    let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n\n    // Create a config with an invalid field name in dev\n    let config_content = r#\"{\n  \"dev\": {\n    \"run_command\": \"npm run dev\"\n  },\n  \"publish\": {\n    \"database\": \"test-db\"\n  }\n}\"#;\n    std::fs::write(temp_dir.path().join(\"spacetime.json\"), config_content).expect(\"failed to write config\");\n\n    // Create minimal spacetimedb module\n    std::fs::create_dir(temp_dir.path().join(\"spacetimedb\")).expect(\"failed to create spacetimedb dir\");\n    std::fs::create_dir(temp_dir.path().join(\"spacetimedb/src\")).expect(\"failed to create src dir\");\n    std::fs::write(\n        temp_dir.path().join(\"spacetimedb/Cargo.toml\"),\n        r#\"[package]\nname = \"test\"\nversion = \"0.1.0\"\n\n[dependencies]\nspacetimedb = \"1.0\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\"#,\n    )\n    .expect(\"failed to write Cargo.toml\");\n    std::fs::write(temp_dir.path().join(\"spacetimedb/src/lib.rs\"), \"\").expect(\"failed to write lib.rs\");\n\n    let output = cli_cmd()\n        .current_dir(temp_dir.path())\n        .args([\"dev\", \"test-db\"])\n        .output()\n        .expect(\"failed to execute\");\n\n    assert!(!output.status.success(), \"dev should fail with invalid config field\");\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    assert!(\n        stderr.contains(\"Failed to load spacetime.json\"),\n        \"stderr should mention Failed to load spacetime.json\"\n    );\n    assert!(\n        stderr.contains(\"unknown field `run_command`\"),\n        \"stderr should mention unknown field run_command\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/generate.rs",
    "content": "use predicates::prelude::*;\nuse spacetimedb_guard::ensure_binaries_built;\nuse spacetimedb_smoketests::patch_module_cargo_to_local_bindings;\nuse std::process::Command;\n\nfn cli_cmd() -> Command {\n    Command::new(ensure_binaries_built())\n}\n\n#[test]\nfn cli_generate_with_config_but_no_match_uses_cli_args() {\n    // Test that when config exists but doesn't match CLI args, we use CLI args\n    let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n\n    // Initialize a new project (creates <project-path>/spacetimedb/)\n    let output = cli_cmd()\n        .args([\n            \"init\",\n            \"--non-interactive\",\n            \"--lang\",\n            \"rust\",\n            \"--project-path\",\n            temp_dir.path().to_str().unwrap(),\n            \"test-project\",\n        ])\n        .current_dir(temp_dir.path())\n        .output()\n        .expect(\"failed to execute\");\n    assert!(\n        output.status.success(),\n        \"init failed: {}\",\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    let project_dir = temp_dir.path().to_path_buf();\n    let module_dir = project_dir.join(\"spacetimedb\");\n    patch_module_cargo_to_local_bindings(&module_dir).expect(\"failed to patch module Cargo.toml\");\n\n    // Create a config with a different module-path filter\n    let config_content = r#\"{\n  \"generate\": [\n    {\n      \"language\": \"typescript\",\n      \"out-dir\": \"./config-output\",\n      \"module-path\": \"config-module-path\"\n    }\n  ]\n}\"#;\n    std::fs::write(module_dir.join(\"spacetime.json\"), config_content).expect(\"failed to write config\");\n\n    // Build the module first\n    let output = cli_cmd()\n        .args([\"build\", \"--module-path\", module_dir.to_str().unwrap()])\n        .output()\n        .expect(\"failed to execute\");\n    assert!(\n        output.status.success(),\n        \"build failed: {}\",\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    let output_dir = module_dir.join(\"cli-output\");\n    std::fs::create_dir(&output_dir).expect(\"failed to create output dir\");\n\n    // Generate with different module-path from CLI - should use CLI args, not config\n    let output = cli_cmd()\n        .args([\n            \"generate\",\n            \"--lang\",\n            \"rust\",\n            \"--out-dir\",\n            output_dir.to_str().unwrap(),\n            \"--module-path\",\n            module_dir.to_str().unwrap(),\n        ])\n        .current_dir(&module_dir)\n        .output()\n        .expect(\"failed to execute\");\n    assert!(\n        output.status.success(),\n        \"generate failed: {}\",\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    // Verify files were generated in the CLI-specified output directory\n    assert!(\n        predicate::path::exists().eval(&output_dir.join(\"lib.rs\"))\n            || predicate::path::exists().eval(&output_dir.join(\"mod.rs\")),\n        \"Generated files should exist in CLI-specified output directory\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/mod.rs",
    "content": "pub mod auth;\npub mod dev;\npub mod generate;\npub mod publish;\npub mod server;\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/publish.rs",
    "content": "//! CLI publish command tests\n\nuse spacetimedb_smoketests::{patch_module_cargo_to_local_bindings, require_local_server, Smoketest};\n\n#[test]\nfn cli_can_publish_spacetimedb_on_disk() {\n    let test = Smoketest::builder().autopublish(false).build();\n\n    // Workspace root for `cargo run -p ...`\n    let workspace_dir = cargo_metadata::MetadataCommand::new().exec().unwrap().workspace_root;\n    // dir = <workspace_root>/templates/chat-console-rs/spacetimedb\n    let dir = workspace_dir\n        .join(\"templates\")\n        .join(\"chat-console-rs\")\n        .join(\"spacetimedb\");\n\n    let dir = dir.to_string();\n    let _ = test\n        .spacetime(&[\"publish\", \"--module-path\", &dir, \"--server\", &test.server_url, \"foobar\"])\n        .unwrap();\n\n    // Can republish without error to the same name\n    let _ = test\n        .spacetime(&[\"publish\", \"--module-path\", &dir, \"--server\", &test.server_url, \"foobar\"])\n        .unwrap();\n}\n\n// TODO: Somewhere we should test that data is actually deleted properly in all the expected cases,\n// e.g. when providing --delete-data, or when there's a conflict and --delete-data=on-conflict is provided.\n\nfn migration_test(module_name: &str, republish_args: &[&str], expect_success: bool) {\n    // This only requires a local server because the module names are static\n    require_local_server!();\n\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let workspace_dir = cargo_metadata::MetadataCommand::new().exec().unwrap().workspace_root;\n    let dir = workspace_dir.join(\"modules\").join(\"module-test\");\n\n    let dir = dir.to_string();\n    let _ = test\n        .spacetime(&[\n            \"publish\",\n            \"--module-path\",\n            &dir,\n            \"--server\",\n            &test.server_url,\n            module_name,\n        ])\n        .unwrap();\n\n    let dir = dir.to_string();\n    let mut args = vec![\n        \"publish\",\n        \"--module-path\",\n        &dir,\n        \"--server\",\n        &test.server_url,\n        module_name,\n    ];\n    args.extend(republish_args);\n    let output = test.spacetime_cmd(&args);\n\n    if expect_success {\n        assert!(\n            output.status.success(),\n            \"republish should have succeeded: {}\",\n            String::from_utf8_lossy(&output.stderr)\n        );\n    } else {\n        assert!(!output.status.success(), \"republish should have failed but succeeded\");\n    }\n}\n\n#[test]\nfn cli_can_publish_no_conflict_does_not_delete_data() {\n    migration_test(\n        \"no-conflict-test\",\n        &[\n            // NOTE: deleting data requires --yes,\n            // so not providing it here ensures that no data deletion is attempted.\n            \"--delete-data=on-conflict\",\n        ],\n        true,\n    );\n}\n\n#[test]\nfn cli_can_publish_no_conflict_with_delete_data_flag() {\n    migration_test(\"no-conflict-delete-data-test\", &[\"--delete-data\", \"--yes\"], true);\n}\n\n#[test]\nfn cli_can_publish_no_conflict_without_delete_data_flag() {\n    migration_test(\"no-conflict-test\", &[], true);\n}\n\n#[test]\nfn cli_can_publish_with_automigration_change() {\n    migration_test(\n        \"automigration-test\",\n        &[\"--build-options=--features test-add-column\", \"--break-clients\"],\n        true,\n    );\n}\n\n#[test]\nfn cli_cannot_publish_automigration_change_without_yes_break_clients() {\n    migration_test(\n        \"automigration-test-no-break-flag\",\n        &[\"--build-options=--features test-add-column\"],\n        false,\n    );\n}\n\n#[test]\nfn cli_can_publish_automigration_change_with_on_conflict_and_yes_break_clients() {\n    migration_test(\n        \"automigration-on-conflict-test\",\n        &[\n            \"--build-options=--features test-add-column\",\n            // NOTE: deleting data requires --yes,\n            // so not providing it here ensures that no data deletion is attempted.\n            \"--delete-data=on-conflict\",\n            \"--break-clients\",\n        ],\n        true,\n    );\n}\n\n#[test]\nfn cli_cannot_publish_automigration_change_with_on_conflict_without_yes_break_clients() {\n    migration_test(\n        \"automigration-on-conflict-no-break-flag-test\",\n        &[\n            \"--build-options=--features test-add-column\",\n            // NOTE: deleting data requires --yes,\n            // so not providing it here ensures that no data deletion is attempted.\n            \"--delete-data=on-conflict\",\n        ],\n        false,\n    );\n}\n\n#[test]\nfn cli_can_publish_automigration_change_with_delete_data_always_without_yes_break_clients() {\n    migration_test(\n        \"automigration-delete-data-test\",\n        &[\"--build-options=--features test-add-column\", \"--delete-data\", \"--yes\"],\n        true,\n    );\n}\n\n#[test]\nfn cli_can_publish_automigration_change_with_delete_data_always_and_yes_break_clients() {\n    migration_test(\n        \"automigration-delete-data-break-test\",\n        &[\n            \"--build-options=--features test-add-column\",\n            \"--delete-data\",\n            \"--yes\",\n            \"--break-clients\",\n        ],\n        true,\n    );\n}\n\n#[test]\nfn cli_cannot_publish_breaking_change_without_flag() {\n    migration_test(\n        \"breaking-change-test\",\n        &[\"--build-options=--features test-remove-table\"],\n        false,\n    );\n}\n\n#[test]\nfn cli_can_publish_breaking_change_with_delete_data_flag() {\n    migration_test(\n        \"breaking-change-delete-data-test\",\n        &[\"--build-options=--features test-remove-table\", \"--delete-data\", \"--yes\"],\n        true,\n    );\n}\n\n#[test]\nfn cli_can_publish_breaking_change_with_on_conflict_flag() {\n    migration_test(\n        \"breaking-change-on-conflict-test\",\n        &[\n            \"--build-options=--features test-remove-table\",\n            \"--delete-data=on-conflict\",\n            \"--yes\",\n        ],\n        true,\n    );\n}\n\n#[test]\nfn cli_publish_with_config_but_no_match_uses_cli_args() {\n    // Test that when config exists but doesn't match CLI args, we use CLI args\n    let test = Smoketest::builder().autopublish(false).build();\n    let temp_dir = tempfile::tempdir().expect(\"failed to create temp dir\");\n\n    // Initialize a new project (creates <project-path>/spacetimedb/)\n    test.spacetime(&[\n        \"init\",\n        \"--non-interactive\",\n        \"--lang\",\n        \"rust\",\n        \"--project-path\",\n        temp_dir.path().to_str().unwrap(),\n        \"test-project\",\n    ])\n    .unwrap();\n\n    let module_dir = temp_dir.path().join(\"spacetimedb\");\n    patch_module_cargo_to_local_bindings(&module_dir).expect(\"failed to patch module Cargo.toml\");\n\n    // Build the module first\n    test.spacetime(&[\"build\", \"--module-path\", module_dir.to_str().unwrap()])\n        .unwrap();\n\n    // Create a config with a different database name\n    let config_content = r#\"{\n  \"publish\": {\n    \"database\": \"config-db-name\"\n  }\n}\"#;\n    std::fs::write(module_dir.join(\"spacetime.json\"), config_content).expect(\"failed to write config\");\n\n    // Publish with a different database name from CLI - should use CLI args, not config\n    test.spacetime(&[\n        \"publish\",\n        \"--server\",\n        &test.server_url,\n        \"cli-db-name\",\n        \"--module-path\",\n        module_dir.to_str().unwrap(),\n    ])\n    .unwrap();\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/cli/server.rs",
    "content": "//! CLI server command tests\n\nuse spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard};\nuse std::process::Command;\n\nfn cli_cmd() -> Command {\n    Command::new(ensure_binaries_built())\n}\n\n#[test]\nfn cli_can_ping_spacetimedb_on_disk() {\n    let spacetime = SpacetimeDbGuard::spawn_in_temp_data_dir();\n    let output = cli_cmd()\n        .args([\"server\", \"ping\", &spacetime.host_url.to_string()])\n        .output()\n        .expect(\"failed to execute\");\n    assert!(\n        output.status.success(),\n        \"ping failed: {}\",\n        String::from_utf8_lossy(&output.stderr)\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/client_connection_errors.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Test that client_connected returning an error rejects the connection\n#[test]\nfn test_client_connected_error_rejects_connection() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"client-connection-reject\")\n        .build();\n\n    // Subscribe should fail because client_connected returns an error\n    let result = test.subscribe(&[\"SELECT * FROM all_u8s\"], 0);\n    assert!(\n        result.is_err(),\n        \"Expected subscribe to fail when client_connected returns error\"\n    );\n\n    let logs = test.logs(100).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"Rejecting connection from client\")),\n        \"Expected rejection message in logs: {:?}\",\n        logs\n    );\n    assert!(\n        !logs.iter().any(|l| l.contains(\"This should never be called\")),\n        \"client_disconnected should not have been called: {:?}\",\n        logs\n    );\n}\n\n/// Test that client_disconnected panicking still cleans up the st_client row\n#[test]\nfn test_client_disconnected_error_still_deletes_st_client() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"client-connection-disconnect-panic\")\n        .build();\n\n    // Subscribe should succeed (client_connected returns Ok)\n    let result = test.subscribe(&[\"SELECT * FROM all_u8s\"], 0);\n    assert!(result.is_ok(), \"Expected subscribe to succeed\");\n\n    let logs = test.logs(100).unwrap();\n    assert!(\n        logs.iter()\n            .any(|l| { l.contains(\"This should be called, but the `st_client` row should still be deleted\") }),\n        \"Expected disconnect panic message in logs: {:?}\",\n        logs\n    );\n\n    // Verify st_client table is empty (row was deleted despite the panic)\n    let sql_out = test.sql(\"SELECT * FROM st_client\").unwrap();\n    assert!(\n        sql_out.contains(\"identity | connection_id\") && !sql_out.contains(\"0x\"),\n        \"Expected st_client table to be empty, got: {}\",\n        sql_out\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/confirmed_reads.rs",
    "content": "//! TODO: We only test that we can pass a --confirmed flag and that things\n//! appear to work as if we hadn't. Without controlling the server, we can't\n//! test that there is any difference in behavior.\n\nuse spacetimedb_smoketests::Smoketest;\n\n/// Tests that subscribing with confirmed=true receives updates\n#[test]\nfn test_confirmed_reads_receive_updates() {\n    let test = Smoketest::builder().precompiled_module(\"confirmed-reads\").build();\n\n    // Start subscription in background with confirmed flag\n    let sub = test\n        .subscribe_background_confirmed(&[\"SELECT * FROM person\"], 2)\n        .unwrap();\n\n    // Insert via reducer\n    test.call(\"add\", &[\"Horst\"]).unwrap();\n\n    // Insert via SQL (use sql_confirmed to ensure durability before continuing,\n    // since the confirmed subscription won't send updates until durable)\n    test.sql_confirmed(\"INSERT INTO person (name) VALUES ('Egon')\").unwrap();\n\n    // Collect updates\n    let events = sub.collect().unwrap();\n\n    assert_eq!(\n        serde_json::json!(events),\n        serde_json::json!([{\n            \"person\": {\n                \"deletes\": [],\n                \"inserts\": [{\"name\": \"Horst\"}]\n            }\n        },\n        {\n            \"person\": {\n                \"deletes\": [],\n                \"inserts\": [{\"name\": \"Egon\"}]\n            }\n        }])\n    );\n}\n\n/// Tests that an SQL operation with confirmed=true returns a result\n#[test]\nfn test_sql_with_confirmed_reads_receives_result() {\n    let test = Smoketest::builder().precompiled_module(\"confirmed-reads\").build();\n\n    // Insert with confirmed\n    test.sql_confirmed(\"INSERT INTO person (name) VALUES ('Horst')\")\n        .unwrap();\n\n    // Query with confirmed\n    let result = test.sql_confirmed(\"SELECT * FROM person\").unwrap();\n\n    assert!(result.contains(\"Horst\"), \"Expected 'Horst' in result: {}\", result);\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/connect_disconnect_from_cli.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Ensure that the connect and disconnect functions are called when invoking a reducer from the CLI\n#[test]\nfn test_conn_disconn() {\n    let test = Smoketest::builder().precompiled_module(\"connect-disconnect\").build();\n\n    test.call(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(10).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"_connect called\")),\n        \"Expected '_connect called' in logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"disconnect called\")),\n        \"Expected 'disconnect called' in logs: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, World!\")),\n        \"Expected 'Hello, World!' in logs: {:?}\",\n        logs\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/create_project.rs",
    "content": "use spacetimedb_guard::ensure_binaries_built;\nuse std::process::Command;\nuse tempfile::tempdir;\n\n/// Ensure that the CLI is able to create a local project.\n/// This test does not depend on a running spacetimedb instance.\n#[test]\nfn test_create_project() {\n    let cli_path = ensure_binaries_built();\n    let tmpdir = tempdir().expect(\"Failed to create temp dir\");\n    let tmpdir_path = tmpdir.path().to_str().unwrap();\n\n    // Without --lang, init should fail\n    let output = Command::new(&cli_path)\n        .args([\"init\", \"--non-interactive\", \"test-project\"])\n        .current_dir(tmpdir_path)\n        .output()\n        .expect(\"Failed to run spacetime init\");\n    assert!(!output.status.success(), \"Expected init without --lang to fail\");\n\n    // Without --project-path to specify location, init should fail\n    let output = Command::new(&cli_path)\n        .args([\n            \"init\",\n            \"--non-interactive\",\n            \"--project-path\",\n            tmpdir_path,\n            \"test-project\",\n        ])\n        .output()\n        .expect(\"Failed to run spacetime init\");\n    assert!(\n        !output.status.success(),\n        \"Expected init without --lang to fail even with --project-path\"\n    );\n\n    // With all required args, init should succeed\n    let output = Command::new(&cli_path)\n        .args([\n            \"init\",\n            \"--non-interactive\",\n            \"--lang=rust\",\n            \"--project-path\",\n            tmpdir_path,\n            \"test-project\",\n        ])\n        .output()\n        .expect(\"Failed to run spacetime init\");\n    assert!(\n        output.status.success(),\n        \"Expected init to succeed:\\nstdout: {}\\nstderr: {}\",\n        String::from_utf8_lossy(&output.stdout),\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    // Running init again in the same directory should fail (already exists)\n    let output = Command::new(&cli_path)\n        .args([\n            \"init\",\n            \"--non-interactive\",\n            \"--lang=rust\",\n            \"--project-path\",\n            tmpdir_path,\n            \"test-project\",\n        ])\n        .output()\n        .expect(\"Failed to run spacetime init\");\n    assert!(\n        !output.status.success(),\n        \"Expected init to fail when project already exists\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/csharp_module.rs",
    "content": "#![allow(clippy::disallowed_macros)]\nuse spacetimedb_guard::ensure_binaries_built;\nuse spacetimedb_smoketests::{require_dotnet, workspace_root};\nuse std::fs;\nuse std::process::Command;\n\n/// Ensure that the CLI is able to create and compile a C# project.\n/// This test does not depend on a running SpacetimeDB instance.\n/// Skips if dotnet 8.0+ is not available.\n#[test]\nfn test_build_csharp_module() {\n    require_dotnet!();\n\n    let workspace = workspace_root();\n    let bindings = workspace.join(\"crates/bindings-csharp\");\n    // CLI is pre-built by artifact dependencies during compilation\n    let cli_path = ensure_binaries_built();\n\n    let status = Command::new(\"dotnet\")\n        .args([\"nuget\", \"locals\", \"all\", \"--clear\"])\n        .current_dir(&bindings)\n        .status()\n        .expect(\"Failed to clear nuget locals\");\n    assert!(status.success(), \"Failed to clear nuget locals\");\n\n    // Install wasi-experimental workload\n    let _status = Command::new(\"dotnet\")\n        .args([\"workload\", \"install\", \"wasi-experimental\", \"--skip-manifest-update\"])\n        .current_dir(workspace.join(\"modules\"))\n        .status()\n        .expect(\"Failed to install wasi workload\");\n    // This may fail if already installed, so we don't assert success\n\n    // Pack the bindings in Release configuration\n    let status = Command::new(\"dotnet\")\n        .args([\"pack\", \"-c\", \"Release\"])\n        .current_dir(&bindings)\n        .status()\n        .expect(\"Failed to pack bindings\");\n    assert!(status.success(), \"Failed to pack C# bindings\");\n\n    // Create temp directory for the project\n    let tmpdir = tempfile::tempdir().expect(\"Failed to create temp directory\");\n\n    // Initialize C# project\n    let output = Command::new(&cli_path)\n        .args([\n            \"init\",\n            \"--non-interactive\",\n            \"--lang=csharp\",\n            \"--project-path\",\n            tmpdir.path().to_str().unwrap(),\n            \"csharp-project\",\n        ])\n        .output()\n        .expect(\"Failed to run spacetime init\");\n    assert!(\n        output.status.success(),\n        \"spacetime init failed:\\nstdout: {}\\nstderr: {}\",\n        String::from_utf8_lossy(&output.stdout),\n        String::from_utf8_lossy(&output.stderr)\n    );\n\n    let server_path = tmpdir.path().join(\"spacetimedb\");\n\n    // Create nuget.config with local package sources\n    // Use <clear /> to avoid inheriting sources from machine/user config\n    let packed_projects = [\"BSATN.Runtime\", \"Runtime\"];\n    let mut sources =\n        String::from(\"    <clear />\\n    <add key=\\\"nuget.org\\\" value=\\\"https://api.nuget.org/v3/index.json\\\" />\\n\");\n    let mut mappings = String::new();\n\n    for project in &packed_projects {\n        let path = bindings.join(project).join(\"bin/Release\");\n        let package_name = format!(\"SpacetimeDB.{}\", project);\n        sources.push_str(&format!(\n            \"    <add key=\\\"{}\\\" value=\\\"{}\\\" />\\n\",\n            package_name,\n            path.display()\n        ));\n        mappings.push_str(&format!(\n            \"    <packageSource key=\\\"{}\\\">\\n      <package pattern=\\\"{}\\\" />\\n    </packageSource>\\n\",\n            package_name, package_name\n        ));\n    }\n    // Add fallback for other packages\n    mappings.push_str(\"    <packageSource key=\\\"nuget.org\\\">\\n      <package pattern=\\\"*\\\" />\\n    </packageSource>\\n\");\n\n    let nuget_config = format!(\n        r#\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n{}  </packageSources>\n  <packageSourceMapping>\n{}  </packageSourceMapping>\n</configuration>\n\"#,\n        sources, mappings\n    );\n\n    eprintln!(\"Writing nuget.config contents:\\n{}\", nuget_config);\n    fs::write(server_path.join(\"nuget.config\"), &nuget_config).expect(\"Failed to write nuget.config\");\n\n    // Run dotnet publish\n    let output = Command::new(\"dotnet\")\n        .args([\"publish\"])\n        .current_dir(&server_path)\n        .output()\n        .expect(\"Failed to run dotnet publish\");\n\n    assert!(\n        output.status.success(),\n        \"dotnet publish failed:\\nstdout: {}\\nstderr: {}\",\n        String::from_utf8_lossy(&output.stdout),\n        String::from_utf8_lossy(&output.stderr)\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/default_module_clippy.rs",
    "content": "//! These tests verify that the Rust module templates have no clippy warnings.\n\nuse std::path::PathBuf;\nuse std::process::Command;\n\nfn workspace_root() -> PathBuf {\n    PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_path_buf()\n}\n\n/// Run clippy on a template's spacetimedb module directory.\n/// Both templates use workspace dependencies, so they can be checked in place.\nfn check_template_clippy(template_name: &str) {\n    let template_module_dir = workspace_root().join(format!(\"templates/{}/spacetimedb\", template_name));\n\n    assert!(\n        template_module_dir.exists(),\n        \"Template module directory does not exist: {}\",\n        template_module_dir.display()\n    );\n\n    let output = Command::new(\"cargo\")\n        .args([\"clippy\", \"--\", \"-Dwarnings\"])\n        .current_dir(&template_module_dir)\n        .output()\n        .expect(\"Failed to run cargo clippy\");\n\n    assert!(\n        output.status.success(),\n        \"Template '{}' should have no clippy warnings:\\nstdout: {}\\nstderr: {}\",\n        template_name,\n        String::from_utf8_lossy(&output.stdout),\n        String::from_utf8_lossy(&output.stderr)\n    );\n}\n\n/// Ensure that the basic-rs template module has no clippy errors or warnings\n#[test]\nfn test_basic_rs_template_clippy() {\n    check_template_clippy(\"basic-rs\");\n}\n\n/// Ensure that the chat-console-rs template module has no clippy errors or warnings\n#[test]\nfn test_chat_console_rs_template_clippy() {\n    check_template_clippy(\"chat-console-rs\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/delete_database.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\nuse std::thread;\nuse std::time::Duration;\n\n/// Test that deleting a database stops the module.\n/// The module is considered stopped if its scheduled reducer stops\n/// producing update events.\n#[test]\nfn test_delete_database() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"delete-database\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n    test.publish_module_named(&name, false).unwrap();\n\n    // Start subscription in background to collect updates\n    // We request many updates but will stop early when we delete the db\n    let sub = test.subscribe_background(&[\"SELECT * FROM counter\"], 1000).unwrap();\n\n    // Let the scheduled reducer run for a bit\n    thread::sleep(Duration::from_secs(2));\n\n    // Delete the database\n    test.spacetime(&[\"delete\", \"--server\", &test.server_url, &name])\n        .unwrap();\n\n    // Collect whatever updates we got\n    let updates = sub.collect().unwrap();\n\n    // At a rate of 100ms, we shouldn't have more than 20 updates in 2secs.\n    // But let's say 50, in case the delete gets delayed for some reason.\n    assert!(\n        updates.len() <= 50,\n        \"Expected at most 50 updates, got {}. Database may not have stopped.\",\n        updates.len()\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/describe.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Check describing a module\n#[test]\nfn test_describe() {\n    let test = Smoketest::builder().precompiled_module(\"describe\").build();\n\n    let identity = test.database_identity.as_ref().unwrap();\n\n    // Describe the whole module\n    test.spacetime(&[\"describe\", \"--server\", &test.server_url, \"--json\", identity])\n        .unwrap();\n\n    // Describe a specific reducer\n    test.spacetime(&[\n        \"describe\",\n        \"--server\",\n        &test.server_url,\n        \"--json\",\n        identity,\n        \"reducer\",\n        \"say_hello\",\n    ])\n    .unwrap();\n\n    // Describe a specific table\n    test.spacetime(&[\n        \"describe\",\n        \"--server\",\n        &test.server_url,\n        \"--json\",\n        identity,\n        \"table\",\n        \"person\",\n    ])\n    .unwrap();\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/detect_wasm_bindgen.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Module code that uses wasm_bindgen (should be rejected)\nconst MODULE_CODE_WASM_BINDGEN: &str = r#\"\nuse spacetimedb::{log, ReducerContext};\n\n#[spacetimedb::reducer]\npub fn test(_ctx: &ReducerContext) {\n    log::info!(\"Hello! {}\", now());\n}\n\n#[wasm_bindgen::prelude::wasm_bindgen]\nextern \"C\" {\n    fn now() -> i32;\n}\n\"#;\n\n/// Module code that uses getrandom via rand (should be rejected)\nconst MODULE_CODE_GETRANDOM: &str = r#\"\nuse spacetimedb::{log, ReducerContext};\n\n#[spacetimedb::reducer]\npub fn test(_ctx: &ReducerContext) {\n    log::info!(\"Hello! {}\", rand::random::<u8>());\n}\n\"#;\n\n/// Ensure that spacetime build properly catches wasm_bindgen imports\n#[test]\nfn test_detect_wasm_bindgen() {\n    let test = Smoketest::builder()\n        .module_code(MODULE_CODE_WASM_BINDGEN)\n        .extra_deps(r#\"wasm-bindgen = \"0.2\"\"#)\n        .autopublish(false)\n        .build();\n\n    let output = test.spacetime_build();\n    assert!(!output.status.success(), \"Expected build to fail with wasm_bindgen\");\n\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    assert!(\n        stderr.contains(\"wasm-bindgen detected\"),\n        \"Expected 'wasm-bindgen detected' in stderr, got: {}\",\n        stderr\n    );\n}\n\n/// Ensure that spacetime build properly catches getrandom usage\n#[test]\nfn test_detect_getrandom() {\n    let test = Smoketest::builder()\n        .module_code(MODULE_CODE_GETRANDOM)\n        .extra_deps(r#\"rand = \"0.8\"\"#)\n        .autopublish(false)\n        .build();\n\n    let output = test.spacetime_build();\n    assert!(!output.status.success(), \"Expected build to fail with getrandom\");\n\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    assert!(\n        stderr.contains(\"getrandom usage detected\"),\n        \"Expected 'getrandom usage detected' in stderr, got: {}\",\n        stderr\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/dml.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Test that we receive subscription updates from DML\n#[test]\nfn test_subscribe() {\n    use std::thread;\n    use std::time::Duration;\n\n    let test = Smoketest::builder().precompiled_module(\"dml\").build();\n\n    // Start subscription FIRST (in background), matching Python semantics\n    let sub = test.subscribe_background(&[\"SELECT * FROM t\"], 2).unwrap();\n\n    // Small delay to ensure subscription is connected before inserts\n    thread::sleep(Duration::from_millis(500));\n\n    // Then do the SQL inserts while subscription is running\n    test.sql(\"INSERT INTO t (name) VALUES ('Alice')\").unwrap();\n    test.sql(\"INSERT INTO t (name) VALUES ('Bob')\").unwrap();\n\n    // Collect the subscription results\n    let updates = sub.collect().unwrap();\n\n    assert_eq!(\n        serde_json::json!(updates),\n        serde_json::json!([\n        {\"t\": {\"deletes\": [], \"inserts\": [{\"name\": \"Alice\"}]}},\n        {\"t\": {\"deletes\": [], \"inserts\": [{\"name\": \"Bob\"}]}},\n        ]),\n        \"Expected subscription updates for Alice and Bob inserts\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/domains.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Tests the functionality of the rename command\n#[test]\nfn test_set_name() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let orig_name = format!(\"test-db-{}\", std::process::id());\n    test.publish_module_named(&orig_name, false).unwrap();\n\n    let rand_name = format!(\"test-db-{}-renamed\", std::process::id());\n\n    // This should fail before there's a db with this name\n    let result = test.spacetime(&[\"logs\", \"--server\", &test.server_url, &rand_name]);\n    assert!(result.is_err(), \"Expected logs to fail for non-existent name\");\n\n    // Rename the database\n    let identity = test.database_identity.as_ref().unwrap();\n    test.spacetime(&[\"rename\", \"--server\", &test.server_url, \"--to\", &rand_name, identity])\n        .unwrap();\n\n    // Now logs should work with the new name\n    test.spacetime(&[\"logs\", \"--server\", &test.server_url, &rand_name])\n        .unwrap();\n\n    // Original name should no longer work\n    let result = test.spacetime(&[\"logs\", \"--server\", &test.server_url, &orig_name]);\n    assert!(result.is_err(), \"Expected logs to fail for original name after rename\");\n}\n\n/// Test how we treat the / character in published names\n#[test]\nfn test_subdomain_behavior() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let root_name = format!(\"test-db-{}\", std::process::id());\n    test.publish_module_named(&root_name, false).unwrap();\n\n    // Double slash should fail\n    let double_slash_name = format!(\"{}//test\", root_name);\n    let result = test.publish_module_named(&double_slash_name, false);\n    assert!(result.is_err(), \"Expected publish to fail with double slash in name\");\n\n    // Trailing slash should fail\n    let trailing_slash_name = format!(\"{}/test/\", root_name);\n    let result = test.publish_module_named(&trailing_slash_name, false);\n    assert!(result.is_err(), \"Expected publish to fail with trailing slash in name\");\n}\n\n/// Test that we can't rename to a name already in use\n#[test]\nfn test_set_to_existing_name() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    // Publish first database (no name)\n    test.publish_module().unwrap();\n    let id_to_rename = test.database_identity.clone().unwrap();\n\n    // Publish second database with a name\n    let rename_to = format!(\"test-db-{}-target\", std::process::id());\n    test.publish_module_named(&rename_to, false).unwrap();\n\n    // Try to rename first db to the name of the second - should fail\n    let result = test.spacetime(&[\n        \"rename\",\n        \"--server\",\n        &test.server_url,\n        \"--to\",\n        &rename_to,\n        &id_to_rename,\n    ]);\n    assert!(\n        result.is_err(),\n        \"Expected rename to fail when target name is already in use\"\n    );\n}\n\n/// Test that we can rename to a list of names via the API\n#[test]\nfn test_replace_names() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let orig_name = format!(\"test-db-{}\", std::process::id());\n    let alt_name1 = format!(\"test-db-{}-alt1\", std::process::id());\n    let alt_name2 = format!(\"test-db-{}-alt2\", std::process::id());\n    test.publish_module_named(&orig_name, false).unwrap();\n\n    // Use the API to replace names\n    let json_body = format!(r#\"[\"{}\",\"{}\"]\"#, alt_name1, alt_name2);\n    let response = test\n        .api_call_json(\"PUT\", &format!(\"/v1/database/{}/names\", orig_name), &json_body)\n        .unwrap();\n    assert!(\n        response.status_code == 200,\n        \"Expected 200 status, got {}: {}\",\n        response.status_code,\n        String::from_utf8_lossy(&response.body)\n    );\n\n    // Use logs to check that name resolution works\n    test.spacetime(&[\"logs\", \"--server\", &test.server_url, &alt_name1])\n        .unwrap();\n    test.spacetime(&[\"logs\", \"--server\", &test.server_url, &alt_name2])\n        .unwrap();\n\n    // Original name should no longer work\n    let result = test.spacetime(&[\"logs\", \"--server\", &test.server_url, &orig_name]);\n    assert!(result.is_err(), \"Expected logs to fail for original name after rename\");\n\n    // Restore orig name so the database gets deleted on cleanup\n    let json_body = format!(r#\"[\"{}\"]\"#, orig_name);\n    test.api_call_json(\"PUT\", &format!(\"/v1/database/{}/names\", alt_name1), &json_body)\n        .unwrap();\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/fail_initial_publish.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Module code with a bug: `Person` is the wrong table name, should be `person`\nconst MODULE_CODE_BROKEN: &str = r#\"\nuse spacetimedb::{client_visibility_filter, Filter};\n\n#[spacetimedb::table(accessor = person, public)]\npub struct Person {\n    name: String,\n}\n\n#[client_visibility_filter]\n// Bug: `Person` is the wrong table name, should be `person`.\nconst HIDE_PEOPLE_EXCEPT_ME: Filter = Filter::Sql(\"SELECT * FROM Person WHERE name = 'me'\");\n\"#;\n\nconst FIXED_QUERY: &str = r#\"\"sql\": \"SELECT * FROM person WHERE name = 'me'\"\"#;\n\n/// This tests that publishing an invalid module does not leave a broken entry in the control DB.\n#[test]\nfn test_fail_initial_publish() {\n    let mut test = Smoketest::builder()\n        .module_code(MODULE_CODE_BROKEN)\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // First publish should fail due to broken module\n    let result = test.publish_module_named(&name, false);\n    assert!(result.is_err(), \"Expected publish to fail with broken module\");\n\n    // Describe should fail because database doesn't exist\n    let describe_output = test.spacetime_cmd(&[\"describe\", \"--server\", &test.server_url, \"--json\", &name]);\n    assert!(\n        !describe_output.status.success(),\n        \"Expected describe to fail for non-existent database\"\n    );\n    let stderr = String::from_utf8_lossy(&describe_output.stderr);\n    assert!(\n        stderr.contains(\"No such database\"),\n        \"Expected 'No such database' in stderr, got: {}\",\n        stderr\n    );\n\n    // We can publish a fixed module under the same database name.\n    // This used to be broken; the failed initial publish would leave\n    // the control database in a bad state.\n    test.use_precompiled_module(\"fail-initial-publish-fixed\");\n    test.publish_module_named(&name, false).unwrap();\n\n    let describe_output = test\n        .spacetime(&[\"describe\", \"--server\", &test.server_url, \"--json\", &name])\n        .unwrap();\n    assert!(\n        describe_output.contains(FIXED_QUERY),\n        \"Expected describe output to contain fixed query.\\nGot: {}\",\n        describe_output\n    );\n\n    // Publishing the broken code again fails, but the database still exists afterwards,\n    // with the previous version of the module code.\n    test.write_module_code(MODULE_CODE_BROKEN).unwrap();\n    let result = test.publish_module_named(&name, false);\n    assert!(result.is_err(), \"Expected publish to fail with broken module\");\n\n    // Database should still exist with the fixed code\n    let describe_output = test\n        .spacetime(&[\"describe\", \"--server\", &test.server_url, \"--json\", &name])\n        .unwrap();\n    assert!(\n        describe_output.contains(FIXED_QUERY),\n        \"Expected describe output to still contain fixed query after failed update.\\nGot: {}\",\n        describe_output\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/filtering.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Test filtering reducers\n#[test]\nfn test_filtering() {\n    let test = Smoketest::builder().precompiled_module(\"filtering\").build();\n\n    test.call(\"insert_person\", &[\"23\", r#\"\"Alice\"\"#, r#\"\"al\"\"#]).unwrap();\n    test.call(\"insert_person\", &[\"42\", r#\"\"Bob\"\"#, r#\"\"bo\"\"#]).unwrap();\n    test.call(\"insert_person\", &[\"64\", r#\"\"Bob\"\"#, r#\"\"b2\"\"#]).unwrap();\n\n    // Find a person who is there.\n    test.call(\"find_person\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 23: Alice\")),\n        \"Expected 'UNIQUE FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n\n    // Find persons with the same name.\n    test.call(\"find_person_by_name\", &[r#\"\"Bob\"\"#]).unwrap();\n    let logs = test.logs(4).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 42: Bob aka bo\")),\n        \"Expected 'UNIQUE FOUND: id 42: Bob aka bo' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 64: Bob aka b2\")),\n        \"Expected 'UNIQUE FOUND: id 64: Bob aka b2' in logs, got: {:?}\",\n        logs\n    );\n\n    // Fail to find a person who is not there.\n    test.call(\"find_person\", &[\"43\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: id 43\")),\n        \"Expected 'UNIQUE NOT FOUND: id 43' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_person_read_only\", &[\"43\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: id 43\")),\n        \"Expected 'UNIQUE NOT FOUND: id 43' in logs, got: {:?}\",\n        logs\n    );\n\n    // Find a person by nickname.\n    test.call(\"find_person_by_nick\", &[r#\"\"al\"\"#]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 23: al\")),\n        \"Expected 'UNIQUE FOUND: id 23: al' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_person_by_nick_read_only\", &[r#\"\"al\"\"#]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 23: al\")),\n        \"Expected 'UNIQUE FOUND: id 23: al' in logs, got: {:?}\",\n        logs\n    );\n\n    // Remove a person, and then fail to find them.\n    test.call(\"delete_person\", &[\"23\"]).unwrap();\n    test.call(\"find_person\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: id 23\")),\n        \"Expected 'UNIQUE NOT FOUND: id 23' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_person_read_only\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: id 23\")),\n        \"Expected 'UNIQUE NOT FOUND: id 23' in logs, got: {:?}\",\n        logs\n    );\n    // Also fail by nickname\n    test.call(\"find_person_by_nick\", &[r#\"\"al\"\"#]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: nick al\")),\n        \"Expected 'UNIQUE NOT FOUND: nick al' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_person_by_nick_read_only\", &[r#\"\"al\"\"#]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE NOT FOUND: nick al\")),\n        \"Expected 'UNIQUE NOT FOUND: nick al' in logs, got: {:?}\",\n        logs\n    );\n\n    // Add some nonunique people.\n    test.call(\"insert_nonunique_person\", &[\"23\", r#\"\"Alice\"\"#, \"true\"])\n        .unwrap();\n    test.call(\"insert_nonunique_person\", &[\"42\", r#\"\"Bob\"\"#, \"true\"])\n        .unwrap();\n\n    // Find a nonunique person who is there.\n    test.call(\"find_nonunique_person\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Alice\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_nonunique_person_read_only\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Alice\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n\n    // Fail to find a nonunique person who is not there.\n    test.call(\"find_nonunique_person\", &[\"43\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        !logs.iter().any(|msg| msg.contains(\"NONUNIQUE NOT FOUND: id 43\")),\n        \"Expected no 'NONUNIQUE NOT FOUND: id 43' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_nonunique_person_read_only\", &[\"43\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        !logs.iter().any(|msg| msg.contains(\"NONUNIQUE NOT FOUND: id 43\")),\n        \"Expected no 'NONUNIQUE NOT FOUND: id 43' in logs, got: {:?}\",\n        logs\n    );\n\n    // Insert a non-human, then find humans, then find non-humans\n    test.call(\"insert_nonunique_person\", &[\"64\", r#\"\"Jibbitty\"\"#, \"false\"])\n        .unwrap();\n    test.call(\"find_nonunique_humans\", &[]).unwrap();\n    let logs = test.logs(4).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"HUMAN FOUND: id 23: Alice\")),\n        \"Expected 'HUMAN FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"HUMAN FOUND: id 42: Bob\")),\n        \"Expected 'HUMAN FOUND: id 42: Bob' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_nonunique_non_humans\", &[]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NON-HUMAN FOUND: id 64: Jibbitty\")),\n        \"Expected 'NON-HUMAN FOUND: id 64: Jibbitty' in logs, got: {:?}\",\n        logs\n    );\n\n    // Add another person with the same id, and find them both.\n    test.call(\"insert_nonunique_person\", &[\"23\", r#\"\"Claire\"\"#, \"true\"])\n        .unwrap();\n    test.call(\"find_nonunique_person\", &[\"23\"]).unwrap();\n    let logs = test.logs(4).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Alice\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Claire\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Claire' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_nonunique_person_read_only\", &[\"23\"]).unwrap();\n    let logs = test.logs(4).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Alice\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Alice' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"NONUNIQUE FOUND: id 23: Claire\")),\n        \"Expected 'NONUNIQUE FOUND: id 23: Claire' in logs, got: {:?}\",\n        logs\n    );\n\n    // Check for issues with things present in index but not DB\n    test.call(\"insert_person\", &[\"101\", r#\"\"Fee\"\"#, r#\"\"fee\"\"#]).unwrap();\n    test.call(\"insert_person\", &[\"102\", r#\"\"Fi\"\"#, r#\"\"fi\"\"#]).unwrap();\n    test.call(\"insert_person\", &[\"103\", r#\"\"Fo\"\"#, r#\"\"fo\"\"#]).unwrap();\n    test.call(\"insert_person\", &[\"104\", r#\"\"Fum\"\"#, r#\"\"fum\"\"#]).unwrap();\n    test.call(\"delete_person\", &[\"103\"]).unwrap();\n    test.call(\"find_person\", &[\"104\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 104: Fum\")),\n        \"Expected 'UNIQUE FOUND: id 104: Fum' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_person_read_only\", &[\"104\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"UNIQUE FOUND: id 104: Fum\")),\n        \"Expected 'UNIQUE FOUND: id 104: Fum' in logs, got: {:?}\",\n        logs\n    );\n\n    // As above, but for non-unique indices: check for consistency between index and DB\n    test.call(\"insert_indexed_person\", &[\"7\", r#\"\"James\"\"#, r#\"\"Bond\"\"#])\n        .unwrap();\n    test.call(\"insert_indexed_person\", &[\"79\", r#\"\"Gold\"\"#, r#\"\"Bond\"\"#])\n        .unwrap();\n    test.call(\"insert_indexed_person\", &[\"1\", r#\"\"Hydrogen\"\"#, r#\"\"Bond\"\"#])\n        .unwrap();\n    test.call(\"insert_indexed_person\", &[\"100\", r#\"\"Whiskey\"\"#, r#\"\"Bond\"\"#])\n        .unwrap();\n    test.call(\"delete_indexed_person\", &[\"100\"]).unwrap();\n    test.call(\"find_indexed_people\", &[r#\"\"Bond\"\"#]).unwrap();\n    let logs = test.logs(10).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"INDEXED FOUND: id 7: Bond, James\")),\n        \"Expected 'INDEXED FOUND: id 7: Bond, James' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"INDEXED FOUND: id 79: Bond, Gold\")),\n        \"Expected 'INDEXED FOUND: id 79: Bond, Gold' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter()\n            .any(|msg| msg.contains(\"INDEXED FOUND: id 1: Bond, Hydrogen\")),\n        \"Expected 'INDEXED FOUND: id 1: Bond, Hydrogen' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        !logs\n            .iter()\n            .any(|msg| msg.contains(\"INDEXED FOUND: id 100: Bond, Whiskey\")),\n        \"Expected no 'INDEXED FOUND: id 100: Bond, Whiskey' in logs, got: {:?}\",\n        logs\n    );\n    test.call(\"find_indexed_people_read_only\", &[r#\"\"Bond\"\"#]).unwrap();\n    let logs = test.logs(10).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"INDEXED FOUND: id 7: Bond, James\")),\n        \"Expected 'INDEXED FOUND: id 7: Bond, James' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"INDEXED FOUND: id 79: Bond, Gold\")),\n        \"Expected 'INDEXED FOUND: id 79: Bond, Gold' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        logs.iter()\n            .any(|msg| msg.contains(\"INDEXED FOUND: id 1: Bond, Hydrogen\")),\n        \"Expected 'INDEXED FOUND: id 1: Bond, Hydrogen' in logs, got: {:?}\",\n        logs\n    );\n    assert!(\n        !logs\n            .iter()\n            .any(|msg| msg.contains(\"INDEXED FOUND: id 100: Bond, Whiskey\")),\n        \"Expected no 'INDEXED FOUND: id 100: Bond, Whiskey' in logs, got: {:?}\",\n        logs\n    );\n\n    // Filter by Identity\n    test.call(\"insert_identified_person\", &[\"23\", r#\"\"Alice\"\"#]).unwrap();\n    test.call(\"find_identified_person\", &[\"23\"]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"IDENTIFIED FOUND: Alice\")),\n        \"Expected 'IDENTIFIED FOUND: Alice' in logs, got: {:?}\",\n        logs\n    );\n\n    // Inserting into a table with unique constraints fails\n    // when the second row has the same value in the constrained columns as the first row.\n    // In this case, the table has `#[unique] id` and `#[unique] nick` but not `#[unique] name`.\n    test.call(\"insert_person_twice\", &[\"23\", r#\"\"Alice\"\"#, r#\"\"al\"\"#])\n        .unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter()\n            .any(|msg| msg.contains(\"UNIQUE CONSTRAINT VIOLATION ERROR: id = 23, nick = al\")),\n        \"Expected 'UNIQUE CONSTRAINT VIOLATION ERROR: id = 23, nick = al' in logs, got: {:?}\",\n        logs\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/http_egress.rs",
    "content": "use std::io::Write;\nuse std::net::TcpListener;\nuse std::thread::JoinHandle;\nuse std::time::{Duration, Instant};\n\nuse spacetimedb_smoketests::Smoketest;\n\nfn module_code_http_disallowed_ip(addr: &str, port: u16) -> String {\n    format!(\n        r#\"\nuse spacetimedb::ProcedureContext;\n\n#[spacetimedb::procedure]\npub fn request_disallowed_ip(ctx: &mut ProcedureContext) -> Result<(), String> {{\n    match ctx.http.get(\"http://{addr}:{port}/\") {{\n        Ok(_) => Err(\"request unexpectedly succeeded\".to_owned()),\n        Err(err) => {{\n            let message = err.to_string();\n            if message.contains(\"refusing to connect to private or special-purpose addresses\") {{\n                Ok(())\n            }} else {{\n                Err(format!(\"unexpected error from http request: {{message}}\"))\n            }}\n        }}\n    }}\n}}\n\"#\n    )\n}\n\nfn spawn_redirect_server(location: &str) -> (u16, JoinHandle<std::io::Result<()>>) {\n    let listener = TcpListener::bind((\"127.0.0.1\", 0)).expect(\"failed to bind test redirect server\");\n    listener\n        .set_nonblocking(true)\n        .expect(\"failed to set test redirect server nonblocking mode\");\n    let port = listener\n        .local_addr()\n        .expect(\"failed to read test redirect server address\")\n        .port();\n    let location = location.to_owned();\n    let handle = std::thread::spawn(move || -> std::io::Result<()> {\n        let deadline = Instant::now() + Duration::from_secs(10);\n        let (mut stream, _) = loop {\n            match listener.accept() {\n                Ok(pair) => break pair,\n                Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {\n                    if Instant::now() >= deadline {\n                        return Err(std::io::Error::new(\n                            std::io::ErrorKind::TimedOut,\n                            \"redirect test server did not receive a request; rebuild standalone with allow_loopback_http_for_tests\",\n                        ));\n                    }\n                    std::thread::sleep(Duration::from_millis(10));\n                }\n                Err(err) => return Err(err),\n            }\n        };\n        let response =\n            format!(\"HTTP/1.1 302 Found\\r\\nLocation: {location}\\r\\nContent-Length: 0\\r\\nConnection: close\\r\\n\\r\\n\");\n        stream.write_all(response.as_bytes())?;\n        stream.flush()?;\n        Ok(())\n    });\n    (port, handle)\n}\n\n#[test]\nfn test_http_disallowed_ip_is_blocked() {\n    let module_code = module_code_http_disallowed_ip(\"10.0.0.1\", 80);\n    let test = Smoketest::builder().module_code(&module_code).build();\n\n    let output = test.call_output(\"request_disallowed_ip\", &[]);\n    let stdout = String::from_utf8_lossy(&output.stdout);\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    assert!(\n        output.status.success(),\n        \"Expected request_disallowed_ip to succeed after observing blocked egress error.\\nstdout:\\n{}\\nstderr:\\n{}\",\n        stdout,\n        stderr\n    );\n}\n\n#[test]\nfn test_http_redirect_to_disallowed_ip_is_blocked() {\n    let (port, redirect_server) = spawn_redirect_server(\"http://10.0.0.1:80/\");\n    let module_code = module_code_http_disallowed_ip(\"localhost\", port);\n    let test = Smoketest::builder().module_code(&module_code).build();\n\n    let output = test.call_output(\"request_disallowed_ip\", &[]);\n    let stdout = String::from_utf8_lossy(&output.stdout);\n    let stderr = String::from_utf8_lossy(&output.stderr);\n    assert!(\n        output.status.success(),\n        \"Expected request_disallowed_ip to succeed after observing blocked egress error.\\nstdout:\\n{}\\nstderr:\\n{}\",\n        stdout,\n        stderr\n    );\n\n    redirect_server\n        .join()\n        .expect(\"redirect test server thread panicked\")\n        .expect(\"redirect test server failed\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/logs_level_filter.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\nfn logs_filtered(test: &Smoketest, n: usize, extra_args: &[&str]) -> Vec<serde_json::Value> {\n    let identity = test.database_identity.as_ref().expect(\"No database published\");\n    let n_str = n.to_string();\n\n    let mut args = vec![\"logs\", \"--server\", &test.server_url, \"--format=json\", \"-n\", &n_str];\n    args.extend_from_slice(extra_args);\n    args.push(\"--\");\n    args.push(identity);\n\n    let output = test.spacetime(&args).expect(\"spacetime logs failed\");\n    output\n        .lines()\n        .filter(|line| !line.trim().is_empty())\n        .map(|line| serde_json::from_str(line).expect(\"Failed to parse log record\"))\n        .collect()\n}\n\nfn messages(records: &[serde_json::Value]) -> Vec<String> {\n    records\n        .iter()\n        .filter_map(|r| r.get(\"message\").and_then(|m| m.as_str()).map(String::from))\n        .collect()\n}\n\n/// Without --level, all log levels are returned.\n#[test]\nfn test_logs_no_filter() {\n    let test = Smoketest::builder().precompiled_module(\"logs-level-filter\").build();\n\n    test.call(\"log_all_levels\", &[]).unwrap();\n\n    let msgs = messages(&logs_filtered(&test, 100, &[]));\n    assert!(msgs.iter().any(|m| m == \"msg-trace\"), \"missing trace: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-debug\"), \"missing debug: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-info\"), \"missing info: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-warn\"), \"missing warn: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-error\"), \"missing error: {msgs:?}\");\n}\n\n/// --level filters to that level and above.\n#[test]\nfn test_logs_level_minimum() {\n    let test = Smoketest::builder().precompiled_module(\"logs-level-filter\").build();\n\n    test.call(\"log_all_levels\", &[]).unwrap();\n\n    // --level warn: only warn and error\n    let msgs = messages(&logs_filtered(&test, 100, &[\"--level\", \"warn\"]));\n    assert!(!msgs.iter().any(|m| m == \"msg-trace\"), \"unexpected trace: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-debug\"), \"unexpected debug: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-info\"), \"unexpected info: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-warn\"), \"missing warn: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-error\"), \"missing error: {msgs:?}\");\n\n    // --level error: only error\n    let msgs = messages(&logs_filtered(&test, 100, &[\"--level\", \"error\"]));\n    assert!(!msgs.iter().any(|m| m == \"msg-trace\"), \"unexpected trace: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-debug\"), \"unexpected debug: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-info\"), \"unexpected info: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-warn\"), \"unexpected warn: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-error\"), \"missing error: {msgs:?}\");\n}\n\n/// --level-exact shows only the specified level.\n#[test]\nfn test_logs_level_exact() {\n    let test = Smoketest::builder().precompiled_module(\"logs-level-filter\").build();\n\n    test.call(\"log_all_levels\", &[]).unwrap();\n\n    // --level info --level-exact: only info\n    let msgs = messages(&logs_filtered(&test, 100, &[\"--level\", \"info\", \"--level-exact\"]));\n    assert!(!msgs.iter().any(|m| m == \"msg-trace\"), \"unexpected trace: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-debug\"), \"unexpected debug: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-info\"), \"missing info: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-warn\"), \"unexpected warn: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-error\"), \"unexpected error: {msgs:?}\");\n\n    // --level debug --level-exact: only debug\n    let msgs = messages(&logs_filtered(&test, 100, &[\"--level\", \"debug\", \"--level-exact\"]));\n    assert!(!msgs.iter().any(|m| m == \"msg-trace\"), \"unexpected trace: {msgs:?}\");\n    assert!(msgs.iter().any(|m| m == \"msg-debug\"), \"missing debug: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-info\"), \"unexpected info: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-warn\"), \"unexpected warn: {msgs:?}\");\n    assert!(!msgs.iter().any(|m| m == \"msg-error\"), \"unexpected error: {msgs:?}\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/mod.rs",
    "content": "// All smoketest modules\nmod add_remove_index;\nmod auto_inc;\nmod auto_migration;\nmod call;\nmod change_host_type;\nmod cli;\nmod client_connection_errors;\nmod confirmed_reads;\nmod connect_disconnect_from_cli;\nmod create_project;\nmod csharp_module;\nmod default_module_clippy;\nmod delete_database;\nmod describe;\nmod detect_wasm_bindgen;\nmod dml;\nmod domains;\nmod fail_initial_publish;\nmod filtering;\nmod http_egress;\nmod logs_level_filter;\nmod module_nested_op;\nmod modules;\nmod namespaces;\nmod new_user_flow;\nmod panic;\nmod permissions;\nmod pg_wire;\nmod publish_upgrade_prompt;\nmod quickstart;\nmod restart;\nmod rls;\nmod schedule_reducer;\nmod servers;\nmod sql;\nmod templates;\nmod timestamp_route;\nmod views;\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/module_nested_op.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// This tests uploading a basic module and calling some functions and checking logs afterwards.\n#[test]\nfn test_module_nested_op() {\n    let test = Smoketest::builder().precompiled_module(\"module-nested-op\").build();\n\n    test.call(\"create_account\", &[\"1\", r#\"\"House\"\"#]).unwrap();\n    test.call(\"create_account\", &[\"2\", r#\"\"Wilson\"\"#]).unwrap();\n    test.call(\"add_friend\", &[\"1\", \"2\"]).unwrap();\n    test.call(\"say_friends\", &[]).unwrap();\n\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"House is friends with Wilson\")),\n        \"Expected 'House is friends with Wilson' in logs, got: {:?}\",\n        logs\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/modules.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Test publishing a module without the --delete-data option\n#[test]\nfn test_module_update() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"modules-basic\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Initial publish\n    test.publish_module_named(&name, false).unwrap();\n\n    test.call(\"add\", &[\"Robert\"]).unwrap();\n    test.call(\"add\", &[\"Julie\"]).unwrap();\n    test.call(\"add\", &[\"Samantha\"]).unwrap();\n    test.call(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(100).unwrap();\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Samantha!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Julie!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Robert!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, World!\")));\n\n    // Unchanged module is ok\n    test.publish_module_named(&name, false).unwrap();\n\n    // Changing an existing table isn't (adds age column to Person)\n    test.use_precompiled_module(\"modules-breaking\");\n    let result = test.publish_module_named(&name, false);\n    assert!(result.is_err(), \"Expected publish to fail with breaking change\");\n    let err = result.unwrap_err().to_string();\n    assert!(\n        err.contains(\"manual migration\") || err.contains(\"breaking\"),\n        \"Expected migration error, got: {}\",\n        err\n    );\n\n    // Check that the old module is still running by calling say_hello\n    test.call(\"say_hello\", &[]).unwrap();\n\n    // Adding a table is ok\n    test.use_precompiled_module(\"modules-add-table\");\n    test.publish_module_named(&name, false).unwrap();\n    test.call(\"are_we_updated_yet\", &[]).unwrap();\n\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"MODULE UPDATED\")),\n        \"Expected 'MODULE UPDATED' in logs, got: {:?}\",\n        logs\n    );\n}\n\n/// Test uploading a basic module and calling some functions and checking logs\n#[test]\nfn test_upload_module() {\n    let test = Smoketest::builder().precompiled_module(\"modules-basic\").build();\n\n    test.call(\"add\", &[\"Robert\"]).unwrap();\n    test.call(\"add\", &[\"Julie\"]).unwrap();\n    test.call(\"add\", &[\"Samantha\"]).unwrap();\n    test.call(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(100).unwrap();\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Samantha!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Julie!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, Robert!\")));\n    assert!(logs.iter().any(|l| l.contains(\"Hello, World!\")));\n}\n\n/// Test deploying a module with a repeating reducer and checking it runs\n#[test]\nfn test_upload_module_2() {\n    let test = Smoketest::builder().precompiled_module(\"upload-module-2\").build();\n\n    // Wait for the repeating reducer to run a few times\n    std::thread::sleep(std::time::Duration::from_secs(2));\n    let lines = test.logs(100).unwrap().iter().filter(|l| l.contains(\"Invoked\")).count();\n\n    // Wait more and check that count increased\n    std::thread::sleep(std::time::Duration::from_secs(4));\n    let new_lines = test.logs(100).unwrap().iter().filter(|l| l.contains(\"Invoked\")).count();\n\n    assert!(\n        lines < new_lines,\n        \"Expected more invocations after waiting, got {} then {}\",\n        lines,\n        new_lines\n    );\n}\n\n/// Test hotswapping modules while a subscription is active\n#[test]\nfn test_hotswap_module() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"hotswap-basic\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Publish initial module and subscribe to all\n    test.publish_module_named(&name, false).unwrap();\n    let sub = test.subscribe_background(&[\"SELECT * FROM *\"], 2).unwrap();\n\n    // Trigger event on the subscription\n    test.call(\"add_person\", &[\"Horst\"]).unwrap();\n\n    // Update the module (adds Pet table)\n    test.use_precompiled_module(\"hotswap-updated\");\n    test.publish_module_named(&name, false).unwrap();\n\n    // Assert that the module was updated\n    test.call(\"add_pet\", &[\"Turtle\"]).unwrap();\n    // And trigger another event on the subscription\n    test.call(\"add_person\", &[\"Cindy\"]).unwrap();\n\n    // Note that 'SELECT * FROM *' does NOT get refreshed to include the\n    // new table (this is a known limitation).\n    let updates = sub.collect().unwrap();\n\n    // Check that we got updates for both person inserts\n    assert_eq!(\n        serde_json::json!(updates),\n        serde_json::json!([\n            {\"person\": {\"deletes\": [], \"inserts\": [{\"id\": 1, \"name\": \"Horst\"}]}},\n            {\"person\": {\"deletes\": [], \"inserts\": [{\"id\": 2, \"name\": \"Cindy\"}]}}\n        ])\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/namespaces.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\nuse std::fs;\nuse std::path::{Path, PathBuf};\n\nfn workspace_root() -> PathBuf {\n    PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_path_buf()\n}\n\n/// Count occurrences of a needle string in all .cs files under a directory\nfn count_matches(dir: &Path, needle: &str) -> usize {\n    let mut count = 0;\n    if let Ok(entries) = fs::read_dir(dir) {\n        for entry in entries.flatten() {\n            let path = entry.path();\n            if path.is_dir() {\n                count += count_matches(&path, needle);\n            } else if path.extension().is_some_and(|ext| ext == \"cs\")\n                && let Ok(contents) = fs::read_to_string(&path)\n            {\n                count += contents.matches(needle).count();\n            }\n        }\n    }\n    count\n}\n\n/// Ensure that the default namespace is working properly\n#[test]\nfn test_spacetimedb_ns_csharp() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"namespaces\")\n        .autopublish(false)\n        .build();\n\n    let tmpdir = tempfile::tempdir().expect(\"Failed to create temp dir\");\n    let project_path = workspace_root().join(\"crates/smoketests/modules/namespaces\");\n\n    test.spacetime(&[\n        \"generate\",\n        \"--out-dir\",\n        tmpdir.path().to_str().unwrap(),\n        \"--lang=csharp\",\n        \"--module-path\",\n        project_path.to_str().unwrap(),\n    ])\n    .unwrap();\n\n    let namespace = \"SpacetimeDB.Types\";\n    assert_eq!(\n        count_matches(tmpdir.path(), &format!(\"namespace {}\", namespace)),\n        5,\n        \"Expected 5 occurrences of 'namespace {}'\",\n        namespace\n    );\n    assert_eq!(\n        count_matches(tmpdir.path(), \"using SpacetimeDB;\"),\n        0,\n        \"Expected 0 occurrences of 'using SpacetimeDB;'\"\n    );\n}\n\n/// Ensure that when a custom namespace is specified on the command line, it actually gets used in generation\n#[test]\nfn test_custom_ns_csharp() {\n    let test = Smoketest::builder()\n        .precompiled_module(\"namespaces\")\n        .autopublish(false)\n        .build();\n\n    let tmpdir = tempfile::tempdir().expect(\"Failed to create temp dir\");\n    let project_path = workspace_root().join(\"crates/smoketests/modules/namespaces\");\n\n    // Use a unique namespace name\n    let namespace = \"CustomTestNamespace\";\n\n    test.spacetime(&[\n        \"generate\",\n        \"--out-dir\",\n        tmpdir.path().to_str().unwrap(),\n        \"--lang=csharp\",\n        \"--namespace\",\n        namespace,\n        \"--module-path\",\n        project_path.to_str().unwrap(),\n    ])\n    .unwrap();\n\n    assert_eq!(\n        count_matches(tmpdir.path(), &format!(\"namespace {}\", namespace)),\n        5,\n        \"Expected 5 occurrences of 'namespace {}'\",\n        namespace\n    );\n    assert_eq!(\n        count_matches(tmpdir.path(), \"using SpacetimeDB;\"),\n        5,\n        \"Expected 5 occurrences of 'using SpacetimeDB;'\"\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/new_user_flow.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n// TODO: This test originally was testing to make sure that our tutorial isn't broken. Since our onboarding has changed we should probably update this test in the future.\n/// Test the entirety of the new user flow.\n#[test]\nfn test_new_user_flow() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"new-user-flow\")\n        .autopublish(false)\n        .build();\n\n    // Create a new identity and publish\n    test.new_identity().unwrap();\n    test.publish_module().unwrap();\n\n    // Calling our database\n    test.call(\"say_hello\", &[]).unwrap();\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, World!\")),\n        \"Expected 'Hello, World!' in logs: {:?}\",\n        logs\n    );\n\n    // Calling functions with arguments\n    test.call(\"add\", &[\"Tyler\"]).unwrap();\n    test.call(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(5).unwrap();\n    let hello_world_count = logs.iter().filter(|l| l.contains(\"Hello, World!\")).count();\n    let hello_tyler_count = logs.iter().filter(|l| l.contains(\"Hello, Tyler!\")).count();\n\n    assert_eq!(hello_world_count, 2, \"Expected 2 'Hello, World!' in logs\");\n    assert_eq!(hello_tyler_count, 1, \"Expected 1 'Hello, Tyler!' in logs\");\n\n    // Query via SQL\n    test.assert_sql(\n        \"SELECT * FROM person\",\n        r#\" name\n---------\n \"Tyler\"\"#,\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/panic.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Tests to check if a SpacetimeDB module can handle a panic without corrupting\n#[test]\nfn test_panic() {\n    let test = Smoketest::builder().precompiled_module(\"panic\").build();\n\n    // First reducer should panic/fail\n    let result = test.call(\"first\", &[]);\n    assert!(result.is_err(), \"Expected first reducer to fail due to panic\");\n\n    // Second reducer should succeed, proving state wasn't corrupted\n    test.call(\"second\", &[]).unwrap();\n\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"Test Passed\")),\n        \"Expected 'Test Passed' in logs, got: {:?}\",\n        logs\n    );\n}\n\n/// Tests to ensure an error message returned from a reducer gets printed to logs\n#[test]\nfn test_reducer_error_message() {\n    let test = Smoketest::builder().precompiled_module(\"panic-error\").build();\n\n    // Reducer should fail with error\n    let result = test.call(\"fail\", &[]);\n    assert!(result.is_err(), \"Expected fail reducer to return error\");\n\n    let logs = test.logs(2).unwrap();\n    assert!(\n        logs.iter().any(|msg| msg.contains(\"oopsie :(\")),\n        \"Expected 'oopsie :(' in logs, got: {:?}\",\n        logs\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/permissions.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\nuse std::path::PathBuf;\n\nfn workspace_root() -> PathBuf {\n    PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_path_buf()\n}\n\n/// Ensure that anyone has the permission to call any standard reducer\n#[test]\nfn test_call() {\n    let test = Smoketest::builder().precompiled_module(\"modules-basic\").build();\n\n    test.call_anon(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(10000).unwrap();\n    let world_count = logs.iter().filter(|l| l.contains(\"World\")).count();\n    assert_eq!(world_count, 1, \"Expected 1 'World' in logs, got {}\", world_count);\n}\n\n/// Ensure that anyone can describe any database\n#[test]\nfn test_describe() {\n    let test = Smoketest::builder().precompiled_module(\"modules-basic\").build();\n\n    // Should succeed with anonymous describe\n    test.describe_anon().unwrap();\n}\n\n/// Ensure that we are not able to view the logs of a module that we don't have permission to view\n#[test]\nfn test_logs() {\n    let test = Smoketest::builder().precompiled_module(\"modules-basic\").build();\n\n    // Call say_hello as owner\n    test.call(\"say_hello\", &[]).unwrap();\n\n    // Switch to a new identity\n    test.new_identity().unwrap();\n\n    // Call say_hello as new identity (should work - reducers are public)\n    test.call(\"say_hello\", &[]).unwrap();\n\n    // Switch to another new identity\n    test.new_identity().unwrap();\n\n    // Try to view logs - should fail as non-owner\n    let identity = test.database_identity.as_ref().unwrap();\n    let result = test.spacetime(&[\"logs\", \"--server\", &test.server_url, identity, \"-n\", \"10000\"]);\n    assert!(result.is_err(), \"Expected logs to fail for non-owner\");\n}\n\n/// Ensure that you cannot publish to an identity that you do not own\n#[test]\nfn test_publish() {\n    let test = Smoketest::builder().precompiled_module(\"modules-basic\").build();\n\n    let identity = test.database_identity.as_ref().unwrap().clone();\n\n    // Switch to a new identity\n    test.new_identity().unwrap();\n\n    // Try to publish with --delete-data - should fail\n    let project_path = workspace_root().join(\"crates/smoketests/modules/modules-basic\");\n    let result = test.spacetime(&[\n        \"publish\",\n        &identity,\n        \"--server\",\n        &test.server_url,\n        \"--module-path\",\n        project_path.to_str().unwrap(),\n        \"--delete-data\",\n        \"--yes\",\n    ]);\n    assert!(\n        result.is_err(),\n        \"Expected publish with --delete-data to fail for non-owner\"\n    );\n\n    // Try to publish without --delete-data - should also fail\n    let result = test.spacetime(&[\n        \"publish\",\n        &identity,\n        \"--server\",\n        &test.server_url,\n        \"--module-path\",\n        project_path.to_str().unwrap(),\n        \"--yes\",\n    ]);\n    assert!(result.is_err(), \"Expected publish to fail for non-owner\");\n}\n\n/// Test that you can't replace names of a database you don't own\n#[test]\nfn test_replace_names() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"modules-basic\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n    test.publish_module_named(&name, false).unwrap();\n\n    // Switch to a new identity\n    test.new_identity().unwrap();\n\n    // Try to replace names - should fail\n    let json_body = r#\"[\"post\", \"gres\"]\"#;\n    let response = test\n        .api_call_json(\"PUT\", &format!(\"/v1/database/{}/names\", name), json_body)\n        .unwrap();\n    assert!(\n        response.status_code != 200,\n        \"Expected replace names to fail for non-owner, got status {}\",\n        response.status_code\n    );\n}\n\n/// Ensure that a private table can only be queried by the database owner\n#[test]\nfn test_private_table() {\n    let test = Smoketest::builder().precompiled_module(\"permissions-private\").build();\n\n    // Owner can query private table\n    test.assert_sql(\n        \"SELECT * FROM secret\",\n        r#\" answer\n--------\n 42\"#,\n    );\n\n    // Switch to a new identity\n    test.new_identity().unwrap();\n\n    // Non-owner cannot query private table\n    let result = test.sql(\"SELECT * FROM secret\");\n    assert!(result.is_err(), \"Expected query on private table to fail for non-owner\");\n\n    // Subscribing to the private table fails\n    let result = test.subscribe(&[\"SELECT * FROM secret\"], 0);\n    assert!(\n        result.is_err(),\n        \"Expected subscribe to private table to fail for non-owner\"\n    );\n\n    // Subscribing to the public table works\n    let sub = test\n        .subscribe_background(&[\"SELECT * FROM common_knowledge\"], 1)\n        .unwrap();\n    test.call(\"do_thing\", &[\"godmorgon\"]).unwrap();\n    let events = sub.collect().unwrap();\n\n    assert_eq!(\n        serde_json::json!(events),\n        serde_json::json!([{\n            \"common_knowledge\": {\n                \"deletes\": [],\n                \"inserts\": [{\"thing\": \"godmorgon\"}]\n            }\n        }])\n    );\n\n    // Subscribing to both tables returns updates for the public one only\n    let sub = test.subscribe_background(&[\"SELECT * FROM *\"], 1).unwrap();\n    test.call(\"do_thing\", &[\"howdy\"]).unwrap();\n    let events = sub.collect().unwrap();\n\n    assert_eq!(\n        serde_json::json!(events),\n        serde_json::json!([{\n            \"common_knowledge\": {\n                \"deletes\": [],\n                \"inserts\": [{\"thing\": \"howdy\"}]\n            }\n        }])\n    );\n}\n\n/// Ensure that you cannot delete a database that you do not own\n#[test]\nfn test_cannot_delete_others_database() {\n    let test = Smoketest::builder().build();\n\n    let identity = test.database_identity.as_ref().unwrap().clone();\n\n    // Switch to a new identity\n    test.new_identity().unwrap();\n\n    // Try to delete the database - should fail\n    let result = test.spacetime(&[\"delete\", \"--server\", &test.server_url, &identity, \"--yes\"]);\n    assert!(result.is_err(), \"Expected delete to fail for non-owner\");\n}\n\n/// Ensure that lifecycle reducers (init, on_connect, etc) can't be called directly\n#[test]\nfn test_lifecycle_reducers_cant_be_called() {\n    let test = Smoketest::builder().precompiled_module(\"permissions-lifecycle\").build();\n\n    let lifecycle_kinds = [\"init\", \"client_connected\", \"client_disconnected\"];\n\n    for kind in lifecycle_kinds {\n        let reducer_name = format!(\"lifecycle_{}\", kind);\n        let result = test.call(&reducer_name, &[]);\n        assert!(\n            result.is_err(),\n            \"Expected call to lifecycle reducer '{}' to fail\",\n            reducer_name\n        );\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/pg_wire.rs",
    "content": "#![allow(clippy::disallowed_macros)]\nuse spacetimedb_smoketests::{require_local_server, require_psql, Smoketest};\n\n#[test]\nfn test_sql_format() {\n    require_psql!();\n    // This requires a local server because we don't have a clean way of providing\n    // the remote server's PG port.\n    require_local_server!();\n\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"pg-wire\")\n        .pg_port(5433) // Use non-standard port to avoid conflicts\n        .autopublish(false)\n        .build();\n\n    test.publish_module_named(\"quickstart\", true).unwrap();\n    test.call(\"test\", &[]).unwrap();\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_ints\",\n        r#\"i_8 | i_16  |  i_32  |   i_64   |     i_128     |     i_256\n-----+-------+--------+----------+---------------+---------------\n -25 | -3224 | -23443 | -2344353 | -234434897853 | -234434897853\n(1 row)\"#,\n    );\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_ints_tuple\",\n        r#\"tuple\n---------------------------------------------------------------------------------------------------------------\n {\"i_8\": -25, \"i_16\": -3224, \"i_32\": -23443, \"i_64\": -2344353, \"i_128\": -234434897853, \"i_256\": -234434897853}\n(1 row)\"#,\n    );\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_uints\",\n        r#\"u_8 | u_16 | u_32  |   u_64   |     u_128     |     u_256\n-----+------+-------+----------+---------------+---------------\n 105 | 1050 | 83892 | 48937498 | 4378528978889 | 4378528978889\n(1 row)\"#,\n    );\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_uints_tuple\",\n        r#\"tuple\n-------------------------------------------------------------------------------------------------------------\n {\"u_8\": 105, \"u_16\": 1050, \"u_32\": 83892, \"u_64\": 48937498, \"u_128\": 4378528978889, \"u_256\": 4378528978889}\n(1 row)\"#,\n    );\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_simple_enum\",\n        r#\"id |  action\n----+----------\n  1 | inactive\n  2 | active\n(2 rows)\"#,\n    );\n\n    test.assert_psql(\n        \"quickstart\",\n        \"SELECT * FROM t_enum\",\n        r#\"id |     color\n----+---------------\n  1 | {\"gray\": 128}\n(1 row)\"#,\n    );\n}\n\n/// Test connecting to the database using a PostgreSQL client.\n#[test]\nfn test_sql_conn() {\n    // This requires a local server because we don't have a clean way of providing\n    // the remote server's PG port.\n    require_local_server!();\n\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"pg-wire\")\n        .pg_port(5435) // Use different port from test_sql_format/test_failures\n        .autopublish(false)\n        .build();\n\n    test.publish_module_named(\"quickstart\", true).unwrap();\n    test.call(\"test\", &[]).unwrap();\n\n    let token = test.read_token().unwrap();\n    let pg_port = test.pg_port().expect(\"PostgreSQL wire protocol not enabled\");\n    let host = test.server_host().split(':').next().unwrap_or(\"127.0.0.1\");\n\n    let mut cfg = tokio_postgres::Config::new();\n    cfg.host(host);\n    cfg.port(pg_port);\n    cfg.user(\"postgres\");\n    cfg.password(token);\n    cfg.dbname(\"quickstart\");\n\n    let rt = tokio::runtime::Runtime::new().unwrap();\n    rt.block_on(async move {\n        let (client, connection) = cfg.connect(tokio_postgres::NoTls).await.unwrap();\n        tokio::spawn(async move {\n            let _ = connection.await;\n        });\n\n        let rows = client\n            .simple_query(\"select * from t_uints where u8 = 105 and u16 = 1050\")\n            .await\n            .unwrap();\n\n        let row = rows\n            .iter()\n            .find_map(|m| match m {\n                tokio_postgres::SimpleQueryMessage::Row(r) => Some(r),\n                _ => None,\n            })\n            .expect(\"Expected at least one row\");\n\n        assert_eq!(row.get(0), Some(\"105\"));\n        assert_eq!(row.get(1), Some(\"1050\"));\n        assert_eq!(row.get(2), Some(\"83892\"));\n        assert_eq!(row.get(3), Some(\"48937498\"));\n        assert_eq!(row.get(4), Some(\"4378528978889\"));\n        assert_eq!(row.get(5), Some(\"4378528978889\"));\n\n        // Check long-lived connection.\n        for _ in 0..10 {\n            let rows = client.simple_query(\"select count(*) as t from t_uints\").await.unwrap();\n\n            let row = rows\n                .iter()\n                .find_map(|m| match m {\n                    tokio_postgres::SimpleQueryMessage::Row(r) => Some(r),\n                    _ => None,\n                })\n                .expect(\"Expected count row\");\n\n            assert_eq!(row.get(0), Some(\"1\"));\n        }\n    });\n}\n\n/// Test failure cases\n#[test]\nfn test_failures() {\n    require_psql!();\n    // This requires a local server because we don't have a clean way of providing\n    // the remote server's PG port.\n    require_local_server!();\n\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"pg-wire\")\n        .pg_port(5434) // Use different port from test_sql_format\n        .autopublish(false)\n        .build();\n\n    test.publish_module_named(\"quickstart\", true).unwrap();\n\n    // Empty query returns empty result\n    let output = test.psql(\"quickstart\", \"\").unwrap();\n    assert!(\n        output.is_empty(),\n        \"Expected empty output for empty query, got: {}\",\n        output\n    );\n\n    let result = test.psql_with_token(\"quickstart\", \"invalid_token\", \"SELECT * FROM t_uints\");\n    assert!(result.is_err(), \"Expected error for invalid token\");\n    let err = result.unwrap_err().to_string();\n    assert!(\n        err.contains(\"Invalid token\"),\n        \"Expected 'Invalid token' in error message, got: {}\",\n        err\n    );\n\n    // Returns error for unsupported sql statements\n    let result = test.psql(\n        \"quickstart\",\n        \"SELECT CASE a WHEN 1 THEN 'one' ELSE 'other' END FROM t_uints\",\n    );\n    assert!(result.is_err(), \"Expected error for unsupported SQL\");\n    let err = result.unwrap_err().to_string();\n    assert!(\n        err.contains(\"Unsupported\") || err.contains(\"unsupported\"),\n        \"Expected 'Unsupported' in error message, got: {}\",\n        err\n    );\n\n    // And prepared statements\n    let result = test.psql(\"quickstart\", \"SELECT * FROM t_uints where u8 = $1\");\n    assert!(result.is_err(), \"Expected error for prepared statement\");\n    let err = result.unwrap_err().to_string();\n    assert!(\n        err.contains(\"Unsupported\") || err.contains(\"unsupported\"),\n        \"Expected 'Unsupported' in error message, got: {}\",\n        err\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/publish_upgrade_prompt.rs",
    "content": "use std::path::PathBuf;\n\nuse spacetimedb_smoketests::{random_string, workspace_root, Smoketest};\n\nconst MODULE_CODE: &str = r#\"\nuse spacetimedb::{reducer, ReducerContext};\n\n#[reducer]\npub fn noop(_ctx: &ReducerContext) {}\n\"#;\n\nfn old_fixture_wasm() -> PathBuf {\n    workspace_root()\n        .join(\"crates\")\n        .join(\"smoketests\")\n        .join(\"fixtures\")\n        .join(\"upgrade_old_module_v1.wasm\")\n}\n\n#[test]\nfn upgrade_prompt_on_publish() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let old_wasm = old_fixture_wasm();\n    assert!(old_wasm.exists(), \"expected old fixture wasm at {}\", old_wasm.display());\n\n    let db_name = format!(\"upgrade-smoke-{}\", random_string());\n\n    test.use_precompiled_wasm_path(&old_wasm).unwrap();\n    let initial_identity = test.publish_module_named(&db_name, false).unwrap();\n    assert_eq!(test.database_identity.as_deref(), Some(initial_identity.as_str()));\n\n    // Switch back to source-built module, which uses current bindings.\n    test.write_module_code(MODULE_CODE).unwrap();\n\n    let deny_err = test.publish_module_named_no_force(&db_name).unwrap_err().to_string();\n    assert!(deny_err.contains(\"major version upgrade from 1.0 to 2.0\"));\n    assert!(deny_err.contains(\"Please type 'upgrade' to accept this change:\"));\n\n    let accepted_identity = test.publish_module_with_stdin(&db_name, \"upgrade\\n\").unwrap();\n    assert_eq!(accepted_identity, initial_identity);\n}\n\n#[test]\nfn upgrade_prompt_suppressed_by_yes_flag() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let old_wasm = old_fixture_wasm();\n    assert!(old_wasm.exists(), \"expected old fixture wasm at {}\", old_wasm.display());\n\n    let db_name = format!(\"upgrade-smoke-yes-{}\", random_string());\n\n    test.use_precompiled_wasm_path(&old_wasm).unwrap();\n    let initial_identity = test.publish_module_named(&db_name, false).unwrap();\n    assert_eq!(test.database_identity.as_deref(), Some(initial_identity.as_str()));\n\n    // Switch back to source-built module, which uses current bindings.\n    test.write_module_code(MODULE_CODE).unwrap();\n\n    // With --yes, the upgrade prompt should be suppressed and publish should succeed.\n    let accepted_identity = test.publish_module_named(&db_name, false).unwrap();\n    assert_eq!(accepted_identity, initial_identity);\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/quickstart.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n//! This test validates that the quickstart documentation is correct by extracting\n//! code from markdown docs and running it.\n\nuse anyhow::{bail, Context, Result};\nuse regex::Regex;\nuse spacetimedb_smoketests::{\n    build_typescript_sdk, pnpm, require_dotnet, require_emscripten, require_pnpm, run_cmd, run_cmd_with_stdin,\n    workspace_root, Smoketest,\n};\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Stdio};\n\n/// Write content to a file, creating parent directories as needed.\nfn write_file(path: &Path, content: &str) -> Result<()> {\n    if let Some(parent) = path.parent() {\n        fs::create_dir_all(parent)?;\n    }\n    fs::write(path, content)?;\n    Ok(())\n}\n\n/// Append content to a file.\nfn append_to_file(path: &Path, content: &str) -> Result<()> {\n    use std::io::Write;\n    let mut file = fs::OpenOptions::new().append(true).open(path)?;\n    file.write_all(content.as_bytes())?;\n    Ok(())\n}\n\n/// Parse code blocks from quickstart markdown documentation.\n/// Extracts code blocks with the specified language tag.\n///\n/// - `language`: \"rust\", \"csharp\", or \"typescript\"\n/// - `module_name`: The name to replace \"quickstart-chat\" with\n/// - `server`: If true, look for server code blocks (e.g. \"rust server\"), else client blocks\nfn parse_quickstart(doc_content: &str, language: &str, module_name: &str, server: bool) -> String {\n    // Normalize line endings to Unix style (LF) for consistent regex matching\n    let doc_content = doc_content.replace(\"\\r\\n\", \"\\n\");\n\n    // Determine the codeblock language tag to search for\n    let codeblock_lang = if server {\n        if language == \"typescript\" {\n            \"ts server\".to_string()\n        } else {\n            format!(\"{} server\", language)\n        }\n    } else if language == \"typescript\" {\n        \"ts\".to_string()\n    } else {\n        language.to_string()\n    };\n\n    // Extract code blocks with the specified language\n    let pattern = format!(r\"```{}\\n([\\s\\S]*?)\\n```\", regex::escape(&codeblock_lang));\n    let re = Regex::new(&pattern).unwrap();\n    let mut blocks: Vec<String> = re\n        .captures_iter(&doc_content)\n        .map(|cap| cap.get(1).unwrap().as_str().to_string())\n        .collect();\n\n    let mut end = String::new();\n\n    // C# specific fixups\n    if language == \"csharp\" {\n        let mut found_on_connected = false;\n        let mut filtered_blocks = Vec::new();\n\n        for mut block in blocks {\n            // The doc first creates an empty class Module, so we need to fixup the closing brace\n            if block.contains(\"partial class Module\") {\n                block = block.replace(\"}\", \"\");\n                end = \"\\n}\".to_string();\n            }\n            // Remove the first `OnConnected` block, which body is later updated\n            if block.contains(\"OnConnected(DbConnection conn\") && !found_on_connected {\n                found_on_connected = true;\n                continue;\n            }\n            filtered_blocks.push(block);\n        }\n        blocks = filtered_blocks;\n    }\n\n    // Join blocks and replace module name\n    let result = blocks.join(\"\\n\").replace(\"quickstart-chat\", module_name);\n    result + &end\n}\n\nfn nuget_config_path(project_dir: &Path) -> PathBuf {\n    let p_upper = project_dir.join(\"NuGet.Config\");\n    if p_upper.exists() {\n        return p_upper;\n    }\n\n    let p_lower = project_dir.join(\"nuget.config\");\n    if p_lower.exists() {\n        return p_lower;\n    }\n\n    p_upper\n}\n\n/// Create a NuGet config.\nfn create_nuget_config(sources: &[(String, PathBuf)], mappings: &[(String, String)]) -> String {\n    let mut source_lines = String::new();\n    let mut mapping_lines = String::new();\n\n    for (key, path) in sources {\n        source_lines.push_str(&format!(\"    <add key=\\\"{}\\\" value=\\\"{}\\\" />\\n\", key, path.display()));\n    }\n\n    for (key, pattern) in mappings {\n        mapping_lines.push_str(&format!(\n            \"    <packageSource key=\\\"{}\\\">\\n      <package pattern=\\\"{}\\\" />\\n    </packageSource>\\n\",\n            key, pattern\n        ));\n    }\n\n    format!(\n        r#\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n{}  </packageSources>\n  <packageSourceMapping>\n{}  </packageSourceMapping>\n</configuration>\n\"#,\n        source_lines, mapping_lines\n    )\n}\n\n/// Override nuget config to use a local NuGet package on a .NET project.\nfn override_nuget_package(project_dir: &Path, package: &str, source_dir: &Path, build_subdir: &str) -> Result<()> {\n    println!(\"Override {package}: {project_dir:?} with {source_dir:?}\");\n\n    // Make sure the local package is built\n    let workspace = workspace_root();\n    let repo_nuget_config = workspace.join(\"NuGet.Config\");\n    if repo_nuget_config.exists() {\n        let output = Command::new(\"dotnet\")\n            .args([\"restore\", \"--configfile\", repo_nuget_config.to_str().unwrap()])\n            .current_dir(source_dir)\n            .output()\n            .context(\"Failed to run dotnet restore\")?;\n        if !output.status.success() {\n            bail!(\n                \"dotnet restore failed:\\nstdout: {}\\nstderr: {}\",\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n\n        let output = Command::new(\"dotnet\")\n            .args([\"pack\", \"-c\", \"Release\", \"--no-restore\"])\n            .current_dir(source_dir)\n            .output()\n            .context(\"Failed to run dotnet pack\")?;\n        if !output.status.success() {\n            bail!(\n                \"dotnet pack failed:\\nstdout: {}\\nstderr: {}\",\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n    } else {\n        let output = Command::new(\"dotnet\")\n            .args([\"pack\", \"-c\", \"Release\"])\n            .current_dir(source_dir)\n            .output()\n            .context(\"Failed to run dotnet pack\")?;\n        if !output.status.success() {\n            bail!(\n                \"dotnet pack failed:\\nstdout: {}\\nstderr: {}\",\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n    }\n\n    let nuget_config_path = nuget_config_path(project_dir);\n    let package_path = source_dir.join(build_subdir);\n\n    // Read existing config or create new one\n    let (mut sources, mut mappings) = if nuget_config_path.exists() {\n        // Parse existing config - simplified approach\n        let content = fs::read_to_string(&nuget_config_path)?;\n        parse_nuget_config(&content)\n    } else {\n        (Vec::new(), Vec::new())\n    };\n\n    // Add new source only if not already present (avoid duplicates)\n    if !sources.iter().any(|(k, _)| k == package) {\n        sources.push((package.to_string(), package_path));\n    }\n\n    // Add mapping for the package only if not already present\n    if !mappings.iter().any(|(k, _)| k == package) {\n        mappings.push((package.to_string(), package.to_string()));\n    }\n\n    // Ensure nuget.org fallback exists\n    if !sources.iter().any(|(k, _)| k == \"nuget.org\") {\n        sources.push((\n            \"nuget.org\".to_string(),\n            PathBuf::from(\"https://api.nuget.org/v3/index.json\"),\n        ));\n    }\n    if !mappings.iter().any(|(k, _)| k == \"nuget.org\") {\n        mappings.push((\"nuget.org\".to_string(), \"*\".to_string()));\n    }\n\n    // Write config\n    let config = create_nuget_config(&sources, &mappings);\n    fs::write(&nuget_config_path, config)?;\n\n    let _ = Command::new(\"dotnet\")\n        .args([\"nuget\", \"locals\", \"--clear\", \"all\"])\n        .stderr(Stdio::null())\n        .stdout(Stdio::null())\n        .status();\n\n    Ok(())\n}\n\n/// Parse an existing nuget.config file (simplified).\n#[allow(clippy::type_complexity)]\nfn parse_nuget_config(content: &str) -> (Vec<(String, PathBuf)>, Vec<(String, String)>) {\n    let mut sources = Vec::new();\n    let mut mappings = Vec::new();\n\n    // Simple regex-based parsing\n    let source_re = regex::Regex::new(r#\"<add key=\"([^\"]+)\" value=\"([^\"]+)\"\"#).unwrap();\n    for cap in source_re.captures_iter(content) {\n        sources.push((cap[1].to_string(), PathBuf::from(&cap[2])));\n    }\n\n    let mapping_re = regex::Regex::new(r#\"<packageSource key=\"([^\"]+)\">\\s*<package pattern=\"([^\"]+)\"\"#).unwrap();\n    for cap in mapping_re.captures_iter(content) {\n        mappings.push((cap[1].to_string(), cap[2].to_string()));\n    }\n\n    (sources, mappings)\n}\n\n/// Quickstart test configuration.\nstruct QuickstartConfig {\n    lang: &'static str,\n    client_lang: &'static str,\n    server_file: &'static str,\n    client_file: &'static str,\n    module_bindings: &'static str,\n    run_cmd: &'static [&'static str],\n    build_cmd: &'static [&'static str],\n    replacements: &'static [(&'static str, &'static str)],\n    extra_code: &'static str,\n    connected_str: &'static str,\n}\n\nimpl QuickstartConfig {\n    fn rust() -> Self {\n        Self {\n            lang: \"rust\",\n            client_lang: \"rust\",\n            server_file: \"src/lib.rs\",\n            client_file: \"src/main.rs\",\n            module_bindings: \"src/module_bindings\",\n            run_cmd: &[\"cargo\", \"run\"],\n            build_cmd: &[\"cargo\", \"build\"],\n            replacements: &[\n                // Replace the interactive user input to allow direct testing\n                (\"user_input_loop(&ctx)\", \"user_input_direct(&ctx)\"),\n                // Don't cache the token, because it will cause the test to fail if we run against a non-default server\n                (\".with_token(creds_store()\", \"//.with_token(creds_store()\"),\n            ],\n            extra_code: r#\"\nfn user_input_direct(ctx: &DbConnection) {\n    let mut line = String::new();\n    std::io::stdin().read_line(&mut line).expect(\"Failed to read from stdin.\");\n    if let Some(name) = line.strip_prefix(\"/name \") {\n        ctx.reducers.set_name(name.to_string()).unwrap();\n    } else {\n        ctx.reducers.send_message(line).unwrap();\n    }\n    std::thread::sleep(std::time::Duration::from_secs(1));\n    std::process::exit(0);\n}\n\"#,\n            connected_str: \"connected\",\n        }\n    }\n\n    fn csharp() -> Self {\n        Self {\n            lang: \"csharp\",\n            client_lang: \"csharp\",\n            server_file: \"Lib.cs\",\n            client_file: \"Program.cs\",\n            module_bindings: \"module_bindings\",\n            run_cmd: &[\"dotnet\", \"run\"],\n            build_cmd: &[\"dotnet\", \"build\"],\n            replacements: &[\n                // Replace the interactive user input to allow direct testing\n                (\"InputLoop();\", \"UserInputDirect();\"),\n                (\".OnConnect(OnConnected)\", \".OnConnect(OnConnectedSignal)\"),\n                (\n                    \".OnConnectError(OnConnectError)\",\n                    \".OnConnectError(OnConnectErrorSignal)\",\n                ),\n                // Don't cache the token\n                (\".WithToken(AuthToken.Token)\", \"//.WithToken(AuthToken.Token)\"),\n                // To put the main function at the end so it can see the new functions\n                (\"Main();\", \"\"),\n            ],\n            extra_code: r#\"\nvar connectedEvent = new ManualResetEventSlim(false);\nvar connectionFailed = new ManualResetEventSlim(false);\nvoid OnConnectErrorSignal(Exception e)\n{\n     OnConnectError(e);\n     connectionFailed.Set();\n}\nvoid OnConnectedSignal(DbConnection conn, Identity identity, string authToken)\n{\n    OnConnected(conn, identity, authToken);\n    connectedEvent.Set();\n}\n\nvoid UserInputDirect() {\n    string? line = Console.In.ReadToEnd()?.Trim();\n    if (line == null) Environment.Exit(0);\n\n    if (!WaitHandle.WaitAny(\n            new[] { connectedEvent.WaitHandle, connectionFailed.WaitHandle },\n            TimeSpan.FromSeconds(5)\n        ).Equals(0))\n    {\n        Console.WriteLine(\"Failed to connect to server within timeout.\");\n        Environment.Exit(1);\n    }\n\n    if (line.StartsWith(\"/name \")) {\n        input_queue.Enqueue((\"name\", line[6..]));\n    } else {\n        input_queue.Enqueue((\"message\", line));\n    }\n    Thread.Sleep(1000);\n}\nMain();\n\"#,\n            connected_str: \"Connected\",\n        }\n    }\n\n    fn typescript() -> Self {\n        // TypeScript server uses Rust client because the TypeScript client\n        // quickstart is a React app, which is difficult to smoketest.\n        Self {\n            lang: \"typescript\",\n            client_lang: \"rust\",\n            server_file: \"src/index.ts\",\n            // Client uses Rust config\n            client_file: \"src/main.rs\",\n            module_bindings: \"src/module_bindings\",\n            run_cmd: &[\"cargo\", \"run\"],\n            build_cmd: &[\"cargo\", \"build\"],\n            replacements: &[\n                (\"user_input_loop(&ctx)\", \"user_input_direct(&ctx)\"),\n                (\".with_token(creds_store()\", \"//.with_token(creds_store()\"),\n            ],\n            extra_code: r#\"\nfn user_input_direct(ctx: &DbConnection) {\n    let mut line = String::new();\n    std::io::stdin().read_line(&mut line).expect(\"Failed to read from stdin.\");\n    if let Some(name) = line.strip_prefix(\"/name \") {\n        ctx.reducers.set_name(name.to_string()).unwrap();\n    } else {\n        ctx.reducers.send_message(line).unwrap();\n    }\n    std::thread::sleep(std::time::Duration::from_secs(1));\n    std::process::exit(0);\n}\n\"#,\n            connected_str: \"connected\",\n        }\n    }\n\n    fn cpp() -> Self {\n        // C++ server uses Rust client (same as TypeScript pattern)\n        Self {\n            lang: \"cpp\",\n            client_lang: \"rust\",\n            server_file: \"src/lib.cpp\",\n            client_file: \"src/main.rs\",\n            module_bindings: \"src/module_bindings\",\n            run_cmd: &[\"cargo\", \"run\"],\n            build_cmd: &[\"cargo\", \"build\"],\n            replacements: &[\n                (\"user_input_loop(&ctx)\", \"user_input_direct(&ctx)\"),\n                (\".with_token(creds_store()\", \"//.with_token(creds_store()\"),\n            ],\n            extra_code: r#\"\nfn user_input_direct(ctx: &DbConnection) {\n    let mut line = String::new();\n    std::io::stdin().read_line(&mut line).expect(\"Failed to read from stdin.\");\n    if let Some(name) = line.strip_prefix(\"/name \") {\n        ctx.reducers.set_name(name.to_string()).unwrap();\n    } else {\n        ctx.reducers.send_message(line).unwrap();\n    }\n    std::thread::sleep(std::time::Duration::from_secs(1));\n    std::process::exit(0);\n}\n\"#,\n            connected_str: \"connected\",\n        }\n    }\n}\n\n/// Quickstart test runner.\nstruct QuickstartTest {\n    test: Smoketest,\n    config: QuickstartConfig,\n    project_path: PathBuf,\n    /// Temp directory for server/client - kept alive for duration of test\n    _temp_dir: Option<tempfile::TempDir>,\n}\n\nimpl QuickstartTest {\n    fn new(config: QuickstartConfig) -> Self {\n        let test = Smoketest::builder().autopublish(false).build();\n        Self {\n            test,\n            config,\n            project_path: PathBuf::new(),\n            _temp_dir: None,\n        }\n    }\n\n    fn module_name(&self) -> String {\n        format!(\"quickstart-chat-{}\", self.config.lang)\n    }\n\n    fn doc_path(&self) -> PathBuf {\n        workspace_root().join(\"docs/docs/00100-intro/00300-tutorials/00100-chat-app.md\")\n    }\n\n    /// Generate the server code from the quickstart documentation.\n    fn generate_server(&mut self, server_path: &Path) -> Result<PathBuf> {\n        let workspace = workspace_root();\n        eprintln!(\"Generating server code {}: {:?}...\", self.config.lang, server_path);\n\n        // Initialize the project (local operation, doesn't need server)\n        let output = self.test.spacetime(&[\n            \"init\",\n            \"--non-interactive\",\n            \"--lang\",\n            self.config.lang,\n            \"--project-path\",\n            server_path.to_str().unwrap(),\n            \"spacetimedb-project\",\n        ])?;\n        eprintln!(\"spacetime init output: {}\", output);\n\n        let project_path = server_path.join(\"spacetimedb\");\n        self.project_path = project_path.clone();\n\n        // Copy rust-toolchain.toml\n        let toolchain_src = workspace.join(\"rust-toolchain.toml\");\n        if toolchain_src.exists() {\n            fs::copy(&toolchain_src, project_path.join(\"rust-toolchain.toml\"))?;\n        }\n\n        // Read and parse the documentation\n        let doc_content = fs::read_to_string(self.doc_path())?;\n        let server_code = parse_quickstart(&doc_content, self.config.lang, &self.module_name(), true);\n\n        // Write server code\n        write_file(&project_path.join(self.config.server_file), &server_code)?;\n\n        // Language-specific server postprocessing\n        self.server_postprocess(&project_path)?;\n\n        // Build the server (local operation)\n        self.test\n            .spacetime(&[\"build\", \"-d\", \"-p\", project_path.to_str().unwrap()])?;\n\n        Ok(project_path)\n    }\n\n    /// Language-specific server postprocessing.\n    fn server_postprocess(&self, server_path: &Path) -> Result<()> {\n        let workspace = workspace_root();\n\n        match self.config.lang {\n            \"rust\" => {\n                // Write the Cargo.toml with local bindings path\n                let bindings_path = workspace.join(\"crates/bindings\");\n                let bindings_path_str = bindings_path.display().to_string().replace('\\\\', \"/\");\n\n                let cargo_toml = format!(\n                    r#\"[package]\nname = \"spacetimedb-quickstart-module\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacetimedb = {{ path = \"{}\", features = [\"unstable\"] }}\nlog = \"0.4\"\n\"#,\n                    bindings_path_str\n                );\n                fs::write(server_path.join(\"Cargo.toml\"), cargo_toml)?;\n            }\n            \"csharp\" => {\n                // Set up local NuGet packages\n                override_nuget_package(\n                    server_path,\n                    \"SpacetimeDB.Runtime\",\n                    &workspace.join(\"crates/bindings-csharp/Runtime\"),\n                    \"bin/Release\",\n                )?;\n                override_nuget_package(\n                    server_path,\n                    \"SpacetimeDB.BSATN.Runtime\",\n                    &workspace.join(\"crates/bindings-csharp/BSATN.Runtime\"),\n                    \"bin/Release\",\n                )?;\n            }\n            \"typescript\" => {\n                // Build and link the TypeScript SDK\n                build_typescript_sdk()?;\n\n                // Uninstall spacetimedb first to avoid pnpm issues\n                let _ = pnpm(&[\"uninstall\", \"spacetimedb\"], server_path);\n\n                // Install the local SDK\n                let ts_bindings = workspace.join(\"crates/bindings-typescript\");\n                pnpm(&[\"install\", ts_bindings.to_str().unwrap()], server_path)?;\n            }\n            _ => {}\n        }\n\n        Ok(())\n    }\n\n    /// Initialize the client project.\n    fn project_init(&self, client_path: &Path) -> Result<()> {\n        match self.config.client_lang {\n            \"rust\" => {\n                let parent = client_path.parent().unwrap();\n                run_cmd(\n                    &[\"cargo\", \"new\", \"--bin\", \"--name\", \"quickstart_chat_client\", \"client\"],\n                    parent,\n                )?;\n            }\n            \"csharp\" => {\n                run_cmd(\n                    &[\n                        \"dotnet\",\n                        \"new\",\n                        \"console\",\n                        \"--name\",\n                        \"QuickstartChatClient\",\n                        \"--output\",\n                        client_path.to_str().unwrap(),\n                    ],\n                    client_path.parent().unwrap(),\n                )?;\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    /// Set up the SDK for the client.\n    fn sdk_setup(&self, client_path: &Path) -> Result<()> {\n        let workspace = workspace_root();\n\n        match self.config.client_lang {\n            \"rust\" => {\n                let sdk_rust_path = workspace.join(\"sdks/rust\");\n                let sdk_rust_toml_escaped = sdk_rust_path.display().to_string().replace('\\\\', \"\\\\\\\\\\\\\\\\\"); // double escape for toml\n                let sdk_rust_toml = format!(\n                    \"spacetimedb-sdk = {{ path = \\\"{}\\\" }}\\nlog = \\\"0.4\\\"\\nhex = \\\"0.4\\\"\\n\",\n                    sdk_rust_toml_escaped\n                );\n                append_to_file(&client_path.join(\"Cargo.toml\"), &sdk_rust_toml)?;\n            }\n            \"csharp\" => {\n                // Set up NuGet packages for C# SDK\n                override_nuget_package(\n                    &workspace.join(\"sdks/csharp\"),\n                    \"SpacetimeDB.BSATN.Runtime\",\n                    &workspace.join(\"crates/bindings-csharp/BSATN.Runtime\"),\n                    \"bin/Release\",\n                )?;\n                override_nuget_package(\n                    &workspace.join(\"sdks/csharp\"),\n                    \"SpacetimeDB.Runtime\",\n                    &workspace.join(\"crates/bindings-csharp/Runtime\"),\n                    \"bin/Release\",\n                )?;\n                override_nuget_package(\n                    client_path,\n                    \"SpacetimeDB.BSATN.Runtime\",\n                    &workspace.join(\"crates/bindings-csharp/BSATN.Runtime\"),\n                    \"bin/Release\",\n                )?;\n                override_nuget_package(\n                    client_path,\n                    \"SpacetimeDB.ClientSDK\",\n                    &workspace.join(\"sdks/csharp\"),\n                    \"bin~/Release\",\n                )?;\n\n                run_cmd(&[\"dotnet\", \"add\", \"package\", \"SpacetimeDB.ClientSDK\"], client_path)?;\n            }\n            _ => {}\n        }\n        Ok(())\n    }\n\n    /// Run the client with input and check output.\n    fn check(&self, input: &str, client_path: &Path, contains: &str) -> Result<()> {\n        let output = run_cmd_with_stdin(self.config.run_cmd, client_path, input)?;\n        eprintln!(\"Output for {} client:\\n{}\", self.config.lang, output);\n\n        if !output.contains(contains) {\n            bail!(\"Expected output to contain '{}', but got:\\n{}\", contains, output);\n        }\n        Ok(())\n    }\n\n    /// Publish the module and return the client path.\n    fn publish(&mut self) -> Result<PathBuf> {\n        let temp_dir = tempfile::tempdir()?;\n        let base_path = temp_dir.path().to_path_buf();\n        self._temp_dir = Some(temp_dir);\n        let server_path = base_path.join(\"server\");\n\n        self.generate_server(&server_path)?;\n\n        // Publish the module\n        let project_path_str = self.project_path.to_str().unwrap().to_string();\n        let publish_output = self.test.spacetime(&[\n            \"publish\",\n            \"--server\",\n            &self.test.server_url,\n            \"--module-path\",\n            &project_path_str,\n            \"--yes\",\n            \"--clear-database\",\n            &self.module_name(),\n        ])?;\n\n        // Parse the identity from publish output\n        let re = regex::Regex::new(r\"identity: ([0-9a-fA-F]+)\").unwrap();\n        if let Some(caps) = re.captures(&publish_output) {\n            let identity = caps.get(1).unwrap().as_str().to_string();\n            self.test.database_identity = Some(identity);\n        } else {\n            bail!(\n                \"Failed to parse database identity from publish output: {}\",\n                publish_output\n            );\n        }\n\n        Ok(base_path.join(\"client\"))\n    }\n\n    /// Run the full quickstart test.\n    fn run_quickstart(&mut self) -> Result<()> {\n        let client_path = self.publish()?;\n\n        self.project_init(&client_path)?;\n        self.sdk_setup(&client_path)?;\n\n        // Build the client\n        run_cmd(self.config.build_cmd, &client_path)?;\n\n        // Generate bindings (local operation)\n        let bindings_path = client_path.join(self.config.module_bindings);\n        let project_path_str = self.project_path.to_str().unwrap().to_string();\n        self.test.spacetime(&[\n            \"generate\",\n            \"--lang\",\n            self.config.client_lang,\n            \"--out-dir\",\n            bindings_path.to_str().unwrap(),\n            \"--module-path\",\n            &project_path_str,\n        ])?;\n\n        // Read and parse client code from documentation\n        let doc_content = fs::read_to_string(self.doc_path())?;\n        let mut main_code = parse_quickstart(&doc_content, self.config.client_lang, &self.module_name(), false);\n\n        // Apply replacements\n        for (src, dst) in self.config.replacements {\n            main_code = main_code.replace(src, dst);\n        }\n\n        // Add extra code\n        main_code.push('\\n');\n        main_code.push_str(self.config.extra_code);\n\n        // Replace server address\n        let host = self.test.server_host();\n        let protocol = \"http\"; // The smoketest server uses http\n        main_code = main_code.replace(\"http://localhost:3000\", &format!(\"{}://{}\", protocol, host));\n\n        // Write the client code\n        write_file(&client_path.join(self.config.client_file), &main_code)?;\n\n        // Run the three test interactions\n        self.check(\"\", &client_path, self.config.connected_str)?;\n        self.check(\"/name Alice\", &client_path, \"Alice\")?;\n        self.check(\"Hello World\", &client_path, \"Hello World\")?;\n\n        Ok(())\n    }\n}\n\n/// Run the Rust quickstart guides for server and client.\n#[test]\nfn test_quickstart_rust() {\n    let mut qt = QuickstartTest::new(QuickstartConfig::rust());\n    qt.run_quickstart().expect(\"Rust quickstart test failed\");\n}\n\n/// Run the C# quickstart guides for server and client.\n#[test]\nfn test_quickstart_csharp() {\n    require_dotnet!();\n\n    let mut qt = QuickstartTest::new(QuickstartConfig::csharp());\n    qt.run_quickstart().expect(\"C# quickstart test failed\");\n}\n\n/// Run the TypeScript quickstart for server (with Rust client).\n#[test]\nfn test_quickstart_typescript() {\n    require_pnpm!();\n\n    let mut qt = QuickstartTest::new(QuickstartConfig::typescript());\n    qt.run_quickstart().expect(\"TypeScript quickstart test failed\");\n}\n\n/// Run the C++ quickstart for server (with Rust client).\n#[test]\nfn test_quickstart_cpp() {\n    require_emscripten!();\n\n    let mut qt = QuickstartTest::new(QuickstartConfig::cpp());\n    qt.run_quickstart().expect(\"C++ quickstart test failed\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/restart.rs",
    "content": "//! Tests for server restart behavior.\n//! Translated from smoketests/tests/zz_docker.py\n\nuse spacetimedb_smoketests::{require_local_server, Smoketest};\n\n/// Test data persistence across server restart.\n///\n/// This tests to see if SpacetimeDB can be queried after a restart.\n#[test]\nfn test_restart_module() {\n    require_local_server!();\n    let mut test = Smoketest::builder().precompiled_module(\"restart-person\").build();\n\n    test.call(\"add\", &[\"Robert\"]).unwrap();\n\n    // Wait for data to be durable before restarting.\n    // The --confirmed flag ensures we only see durable data.\n    let output = test\n        .sql_confirmed(\"SELECT * FROM person WHERE name = 'Robert'\")\n        .unwrap();\n    assert!(\n        output.contains(\"Robert\"),\n        \"Data not confirmed before restart: {}\",\n        output\n    );\n\n    test.restart_server();\n\n    test.call(\"add\", &[\"Julie\"]).unwrap();\n    test.call(\"add\", &[\"Samantha\"]).unwrap();\n    test.call(\"say_hello\", &[]).unwrap();\n\n    let logs = test.logs(100).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, Robert!\")),\n        \"Missing 'Hello, Robert!' in logs\"\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, Julie!\")),\n        \"Missing 'Hello, Julie!' in logs\"\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, Samantha!\")),\n        \"Missing 'Hello, Samantha!' in logs\"\n    );\n    assert!(\n        logs.iter().any(|l| l.contains(\"Hello, World!\")),\n        \"Missing 'Hello, World!' in logs\"\n    );\n}\n\n/// Test SQL queries work after restart.\n#[test]\nfn test_restart_sql() {\n    require_local_server!();\n    let mut test = Smoketest::builder().precompiled_module(\"restart-person\").build();\n\n    test.call(\"add\", &[\"Robert\"]).unwrap();\n    test.call(\"add\", &[\"Julie\"]).unwrap();\n    test.call(\"add\", &[\"Samantha\"]).unwrap();\n\n    // Wait for all data to be durable before restarting.\n    // Query the last inserted row to ensure all data is confirmed.\n    let output = test\n        .sql_confirmed(\"SELECT * FROM person WHERE name = 'Samantha'\")\n        .unwrap();\n    assert!(\n        output.contains(\"Samantha\"),\n        \"Data not confirmed before restart: {}\",\n        output\n    );\n\n    test.restart_server();\n\n    let output = test.sql(\"SELECT name FROM person WHERE id = 3\").unwrap();\n    assert!(\n        output.contains(\"Samantha\"),\n        \"Expected 'Samantha' in SQL output: {}\",\n        output\n    );\n}\n\n/// Test clients are auto-disconnected on restart.\n#[test]\nfn test_restart_auto_disconnect() {\n    require_local_server!();\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"restart-connected-client\")\n        .build();\n\n    // Start two subscribers in the background\n    let sub1 = test\n        .subscribe_background(&[\"SELECT * FROM connected_client\"], 2)\n        .unwrap();\n    let sub2 = test\n        .subscribe_background(&[\"SELECT * FROM connected_client\"], 2)\n        .unwrap();\n\n    // Call print_num_connected and check we have 3 clients (2 subscribers + the call)\n    test.call(\"print_num_connected\", &[]).unwrap();\n    let logs = test.logs(10).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"CONNECTED CLIENTS: 3\")),\n        \"Expected 3 connected clients before restart, logs: {:?}\",\n        logs\n    );\n\n    // Restart the server - this should disconnect all clients\n    test.restart_server();\n\n    // The subscriptions should fail/complete since the server restarted\n    // We don't wait for them, just drop the handles\n    drop(sub1);\n    drop(sub2);\n\n    // After restart, only the current call should be connected\n    test.call(\"print_num_connected\", &[]).unwrap();\n    let logs = test.logs(10).unwrap();\n    assert!(\n        logs.iter().any(|l| l.contains(\"CONNECTED CLIENTS: 1\")),\n        \"Expected 1 connected client after restart, logs: {:?}\",\n        logs\n    );\n}\n\nconst JOIN_QUERY: &str = \"select t1.* from t1 join t2 on t1.id = t2.id where t2.id = 1001\";\n\n/// Test autoinc sequences work correctly after restart.\n///\n/// This is the `AddRemoveIndex` test from add_remove_index.py,\n/// but restarts the server between each publish.\n///\n/// This detects a bug we once had where the system autoinc sequences\n/// were borked after restart, leading newly-created database objects\n/// to re-use IDs.\n#[test]\nfn test_add_remove_index_after_restart() {\n    require_local_server!();\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"add-remove-index\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Publish and attempt subscribing to a join query.\n    // There are no indices, resulting in an unsupported unindexed join.\n    test.publish_module_named(&name, false).unwrap();\n    let result = test.subscribe(&[JOIN_QUERY], 0);\n    assert!(result.is_err(), \"Expected subscription to fail without indices\");\n\n    // Restart before adding indices\n    test.restart_server();\n\n    // Publish the indexed version.\n    // Now we have indices, so the query should be accepted.\n    test.use_precompiled_module(\"add-remove-index-indexed\");\n    test.publish_module_named(&name, false).unwrap();\n\n    // Subscription should work now\n    let result = test.subscribe(&[JOIN_QUERY], 0);\n    assert!(\n        result.is_ok(),\n        \"Expected subscription to succeed with indices, got: {:?}\",\n        result.err()\n    );\n\n    // Verify call works too\n    let sub = test.subscribe_background(&[JOIN_QUERY], 1).unwrap();\n    test.call_anon(\"add\", &[]).unwrap();\n    let results = sub.collect().unwrap();\n    assert_eq!(results.len(), 1, \"Expected 1 update from subscription\");\n\n    // Restart before removing indices\n    test.restart_server();\n\n    // Publish the unindexed version again, removing the index.\n    // The initial subscription should be rejected again.\n    test.use_precompiled_module(\"add-remove-index\");\n    test.publish_module_named(&name, false).unwrap();\n    let result = test.subscribe(&[JOIN_QUERY], 0);\n    assert!(result.is_err(), \"Expected subscription to fail after removing indices\");\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/rls.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// Tests for querying tables with RLS rules\n#[test]\nfn test_rls_rules() {\n    let test = Smoketest::builder().precompiled_module(\"rls\").build();\n\n    // Insert a user for Alice (current identity)\n    test.call(\"add_user\", &[\"Alice\"]).unwrap();\n\n    // Create a new identity for Bob\n    test.new_identity().unwrap();\n    test.call(\"add_user\", &[\"Bob\"]).unwrap();\n\n    // Query the users table using Bob's identity - should only see Bob\n    test.assert_sql(\n        \"SELECT name FROM users\",\n        r#\" name\n-------\n \"Bob\"\"#,\n    );\n\n    // Create another new identity - should see no users\n    test.new_identity().unwrap();\n    test.assert_sql(\n        \"SELECT name FROM users\",\n        r#\" name\n------\"#,\n    );\n}\n\n/// Module code with RLS on a private table (intentionally broken)\nconst MODULE_CODE_BROKEN_RLS: &str = r#\"\nuse spacetimedb::{client_visibility_filter, Filter, Identity};\n\n#[spacetimedb::table(accessor = user)]\npub struct User {\n    identity: Identity,\n}\n\n#[client_visibility_filter]\nconst PERSON_FILTER: Filter = Filter::Sql(\"SELECT * FROM \\\"user\\\" WHERE identity = :sender\");\n\"#;\n\n/// Tests that publishing an RLS rule on a private table fails\n#[test]\nfn test_publish_fails_for_rls_on_private_table() {\n    let mut test = Smoketest::builder()\n        .module_code(MODULE_CODE_BROKEN_RLS)\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Publishing should fail because RLS is on a private table\n    let result = test.publish_module_named(&name, false);\n    assert!(result.is_err(), \"Expected publish to fail for RLS on private table\");\n}\n\n/// Tests that changing the RLS rules disconnects existing clients\n#[test]\nfn test_rls_disconnect_if_change() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"rls-no-filter\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Initial publish without RLS\n    test.publish_module_named(&name, false).unwrap();\n\n    // Now re-publish with RLS added (requires --break-clients)\n    test.use_precompiled_module(\"rls-with-filter\");\n    test.publish_module_with_options(&name, false, true).unwrap();\n\n    // Check the row-level SQL filter is added correctly\n    test.assert_sql(\n        \"SELECT sql FROM st_row_level_security\",\n        r#\" sql\n------------------------------------------------\n \"SELECT * FROM users WHERE identity = :sender\"\"#,\n    );\n\n    let logs = test.logs(100).unwrap();\n\n    // Validate disconnect + schema migration logs\n    assert!(\n        logs.iter().any(|l| l.contains(\"Disconnecting all users\")),\n        \"Expected 'Disconnecting all users' in logs: {:?}\",\n        logs\n    );\n}\n\n/// Tests that not changing the RLS rules does not disconnect existing clients\n#[test]\nfn test_rls_no_disconnect() {\n    let mut test = Smoketest::builder()\n        .precompiled_module(\"rls-with-filter\")\n        .autopublish(false)\n        .build();\n\n    let name = format!(\"test-db-{}\", std::process::id());\n\n    // Initial publish with RLS\n    test.publish_module_named(&name, false).unwrap();\n\n    // Re-publish the same module (no RLS change)\n    test.publish_module_named(&name, false).unwrap();\n\n    let logs = test.logs(100).unwrap();\n\n    // Validate no disconnect logs\n    assert!(\n        !logs.iter().any(|l| l.contains(\"Disconnecting all users\")),\n        \"Expected no 'Disconnecting all users' in logs: {:?}\",\n        logs\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/schedule_reducer.rs",
    "content": "use serde_json::json;\nuse serde_json::Value;\nuse spacetimedb_smoketests::Smoketest;\nuse std::thread;\nuse std::time::Duration;\n\nfn time_row_entry(scheduled_id: u64) -> Value {\n    json!({\n        \"prev\": {\"__timestamp_micros_since_unix_epoch__\": 0},\n        \"scheduled_id\": scheduled_id,\n        \"sched_at\": {\"Time\": {\"__timestamp_micros_since_unix_epoch__\": 0}},\n    })\n}\n\nfn interval_row_entry(scheduled_id: u64, duration_micros: u64) -> Value {\n    json!({\n        \"prev\": {\"__timestamp_micros_since_unix_epoch__\": 0},\n        \"scheduled_id\": scheduled_id,\n        \"sched_at\": {\"Interval\": {\"__time_duration_micros__\": duration_micros}},\n    })\n}\n\nfn collect_updates_after_call(test: &Smoketest, queries: &[&str], reducer_or_procedure: &str) -> Vec<Value> {\n    let sub = test.subscribe_background(queries, 2).unwrap();\n    test.call(reducer_or_procedure, &[]).unwrap();\n    sub.collect().unwrap()\n}\n\nfn assert_table_and_view_insert_delete_updates(\n    updates: Vec<Value>,\n    table_name: &str,\n    view_name: &str,\n    row_entry: Value,\n) {\n    assert_eq!(\n        serde_json::json!(updates),\n        serde_json::json!([\n            {\n                table_name: {\"deletes\": [], \"inserts\": [row_entry.clone()]},\n                view_name: {\"deletes\": [], \"inserts\": [row_entry.clone()]},\n            },\n            {\n                table_name: {\"deletes\": [row_entry.clone()], \"inserts\": []},\n                view_name: {\"deletes\": [row_entry], \"inserts\": []},\n            },\n        ])\n    );\n}\n\nfn assert_table_insert_only_updates(updates: Vec<Value>, table_name: &str, first_row: Value, second_row: Value) {\n    assert_eq!(\n        serde_json::json!(updates),\n        serde_json::json!([\n            {table_name: {\"deletes\": [], \"inserts\": [first_row]}},\n            {table_name: {\"deletes\": [], \"inserts\": [second_row]}},\n        ])\n    );\n}\n\n/// Ensure cancelling a reducer works\n#[test]\nfn test_cancel_reducer() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-cancel\").build();\n\n    // Wait for any scheduled reducers to potentially run\n    thread::sleep(Duration::from_secs(2));\n\n    let logs = test.logs(5).unwrap();\n    let logs_str = logs.join(\"\\n\");\n    assert!(\n        !logs_str.contains(\"the reducer ran\"),\n        \"Expected no 'the reducer ran' in logs, got: {:?}\",\n        logs\n    );\n}\n\n/// Test deploying a module with a scheduled reducer and check that the automatic cleanup\n/// transaction updates both the scheduled table and its dependent view together.\n#[test]\nfn test_scheduled_table_and_view_subscription() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-subscribe\").build();\n\n    let updates = collect_updates_after_call(\n        &test,\n        &[\"SELECT * FROM scheduled_table\", \"SELECT * FROM scheduled_view\"],\n        \"schedule_reducer\",\n    );\n\n    // The insert and delete should update the scheduled table and its view in the same\n    // subscription transactions.\n    assert_table_and_view_insert_delete_updates(updates, \"scheduled_table\", \"scheduled_view\", time_row_entry(2));\n}\n\n/// Test that repeated reducers run multiple times\n#[test]\nfn test_scheduled_table_subscription_repeated_reducer() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-subscribe\").build();\n\n    // Subscribe to empty scheduled_table.\n    let sub = test\n        .subscribe_background(&[\"SELECT * FROM scheduled_table\"], 2)\n        .unwrap();\n\n    // Call a reducer to schedule a repeated reducer\n    test.call(\"schedule_repeated_reducer\", &[]).unwrap();\n\n    // Wait for the scheduled reducer to run multiple times\n    thread::sleep(Duration::from_secs(2));\n\n    let logs = test.logs(100).unwrap();\n    let invoked_count = logs.iter().filter(|line| line.contains(\"Invoked:\")).count();\n    assert!(\n        invoked_count > 2,\n        \"Expected repeated reducer to run more than twice, but it ran {} times. Logs: {:?}\",\n        invoked_count,\n        logs\n    );\n\n    // Scheduling a one-shot reducer again just to get the 2nd subscription update.\n    test.call(\"schedule_reducer\", &[]).unwrap();\n\n    let updates = sub.collect().unwrap();\n\n    // subscription should have 2 updates and should not have any deletes\n    assert_table_insert_only_updates(\n        updates,\n        \"scheduled_table\",\n        interval_row_entry(1, 100_000),\n        time_row_entry(2),\n    );\n}\n\n/// Scheduled *procedure* subscription: expect insert + delete for both table and view.\n#[test]\nfn test_scheduled_procedure_table_and_view_subscription() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-procedure\").build();\n\n    let updates = collect_updates_after_call(\n        &test,\n        &[\"SELECT * FROM scheduled_table\", \"SELECT * FROM scheduled_view\"],\n        \"schedule_procedure\",\n    );\n\n    assert_table_and_view_insert_delete_updates(updates, \"scheduled_table\", \"scheduled_view\", time_row_entry(2));\n}\n\n/// Test that scheduled reducers refresh views for scheduled tables.\n#[test]\nfn test_view_refresh_for_scheduled_reducer() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-subscribe\").build();\n\n    test.call(\"seed_player_entity\", &[\"2\"]).unwrap();\n\n    let updates = collect_updates_after_call(\n        &test,\n        &[\"SELECT * FROM scheduled_table\", \"SELECT * FROM scheduled_sender_view\"],\n        \"schedule_reducer\",\n    );\n\n    assert_table_and_view_insert_delete_updates(updates, \"scheduled_table\", \"scheduled_sender_view\", time_row_entry(2));\n}\n\n/// Test that cleanup still refreshes a view when the scheduled reducer fails.\n#[test]\nfn test_view_refresh_on_failed_scheduled_reducer() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-subscribe\").build();\n\n    test.call(\"seed_player_entity\", &[\"3\"]).unwrap();\n\n    let updates = collect_updates_after_call(\n        &test,\n        &[\n            \"SELECT * FROM failing_scheduled_table\",\n            \"SELECT * FROM failing_scheduled_sender_view\",\n        ],\n        \"schedule_failing_reducer\",\n    );\n\n    assert_table_and_view_insert_delete_updates(\n        updates,\n        \"failing_scheduled_table\",\n        \"failing_scheduled_sender_view\",\n        time_row_entry(3),\n    );\n\n    let logs = test.logs(100).unwrap();\n    let logs_str = logs.join(\"\\n\");\n    assert!(\n        logs_str.contains(\"scheduled reducer failed\"),\n        \"Expected scheduled reducer failure to be logged, got: {:?}\",\n        logs\n    );\n}\n\n/// Repeated scheduled *procedure* subscription: expect inserts only (no deletes).\n#[test]\nfn test_scheduled_procedure_table_subscription_repeated_procedure() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-procedure\").build();\n\n    let sub = test\n        .subscribe_background(&[\"SELECT * FROM scheduled_table\"], 2)\n        .unwrap();\n\n    test.call(\"schedule_repeated_procedure\", &[]).unwrap();\n\n    thread::sleep(Duration::from_secs(2));\n\n    let logs = test.logs(100).unwrap();\n    let invoked_count = logs.iter().filter(|line| line.contains(\"Invoked:\")).count();\n    assert!(\n        invoked_count > 2,\n        \"Expected repeated procedure to run more than twice, but it ran {} times. Logs: {:?}\",\n        invoked_count,\n        logs\n    );\n\n    // Trigger another update so we get the expected 2 subscription updates.\n    test.call(\"schedule_procedure\", &[]).unwrap();\n\n    let updates = sub.collect().unwrap();\n\n    assert_table_insert_only_updates(\n        updates,\n        \"scheduled_table\",\n        interval_row_entry(1, 100_000),\n        time_row_entry(2),\n    );\n}\n\n/// Check that volatile_nonatomic_schedule_immediate works\n#[test]\nfn test_volatile_nonatomic_schedule_immediate() {\n    let test = Smoketest::builder().precompiled_module(\"schedule-volatile\").build();\n\n    let sub = test.subscribe_background(&[\"SELECT * FROM my_table\"], 2).unwrap();\n\n    // Insert directly first\n    test.call(\"do_insert\", &[r#\"\"yay!\"\"#]).unwrap();\n\n    // Schedule another insert\n    test.call(\"do_schedule\", &[]).unwrap();\n\n    let updates = sub.collect().unwrap();\n    assert_eq!(\n        serde_json::json!(updates),\n        serde_json::json!([\n            {\"my_table\": {\"deletes\": [], \"inserts\": [{\"x\": \"yay!\"}] }},\n            {\"my_table\": {\"deletes\": [], \"inserts\": [{\"x\": \"hello\"}] }},\n        ])\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/servers.rs",
    "content": "use regex::Regex;\nuse spacetimedb_smoketests::Smoketest;\n\n/// Verify that we can add and list server configurations\n#[test]\nfn test_servers() {\n    let test = Smoketest::builder().autopublish(false).build();\n\n    // Add a test server (local-only command, no --server flag needed)\n    let output = test\n        .spacetime(&[\n            \"server\",\n            \"add\",\n            \"--url\",\n            \"https://testnet.spacetimedb.com\",\n            \"testnet\",\n            \"--no-fingerprint\",\n        ])\n        .unwrap();\n\n    assert!(\n        Regex::new(r\"Host: testnet\\.spacetimedb\\.com\\n\")\n            .unwrap()\n            .is_match(&output),\n        \"Expected host in output: {}\",\n        output\n    );\n    assert!(\n        Regex::new(r\"Protocol: https\\n\").unwrap().is_match(&output),\n        \"Expected host in output: {}\",\n        output\n    );\n\n    // List servers (local-only command)\n    let servers = test.spacetime(&[\"server\", \"list\"]).unwrap();\n\n    let testnet_re = Regex::new(r\"(?m)^\\s*testnet\\.spacetimedb\\.com\\s+https\\s+testnet\\s*$\").unwrap();\n    assert!(\n        testnet_re.is_match(&servers),\n        \"Expected testnet in server list: {}\",\n        servers\n    );\n\n    // Add the local test server to the config so we can check its fingerprint\n    test.spacetime(&[\n        \"server\",\n        \"add\",\n        \"--url\",\n        &test.server_url,\n        \"test-local\",\n        \"--no-fingerprint\",\n    ])\n    .unwrap();\n\n    // Check fingerprint commands (local-only command)\n    let output = test.spacetime(&[\"server\", \"fingerprint\", \"test-local\", \"-y\"]).unwrap();\n    // The exact message may vary, just check it doesn't error\n    assert!(\n        output.contains(\"fingerprint\") || output.contains(\"Fingerprint\"),\n        \"Expected fingerprint message: {}\",\n        output\n    );\n}\n\n/// Verify that we can edit server configurations\n#[test]\nfn test_edit_server() {\n    let test = Smoketest::builder().autopublish(false).build();\n\n    // Add a server to edit (local-only command)\n    test.spacetime(&[\"server\", \"add\", \"--url\", \"https://foo.com\", \"foo\", \"--no-fingerprint\"])\n        .unwrap();\n\n    // Edit the server (local-only command)\n    test.spacetime(&[\n        \"server\",\n        \"edit\",\n        \"foo\",\n        \"--url\",\n        \"https://edited-testnet.spacetimedb.com\",\n        \"--new-name\",\n        \"edited-testnet\",\n        \"--no-fingerprint\",\n        \"--yes\",\n    ])\n    .unwrap();\n\n    // Verify the edit (local-only command)\n    let servers = test.spacetime(&[\"server\", \"list\"]).unwrap();\n    let edited_re = Regex::new(r\"(?m)^\\s*edited-testnet\\.spacetimedb\\.com\\s+https\\s+edited-testnet\\s*$\").unwrap();\n    assert!(\n        edited_re.is_match(&servers),\n        \"Expected edited server in list: {}\",\n        servers\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/sql.rs",
    "content": "use spacetimedb_smoketests::Smoketest;\n\n/// This test is designed to test the format of the output of sql queries\n#[test]\nfn test_sql_format() {\n    let test = Smoketest::builder().precompiled_module(\"sql-format\").build();\n\n    test.call(\"test\", &[]).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM t_ints\",\n        r#\" i_8 | i_16  | i_32   | i_64     | i_128         | i_256\n-----+-------+--------+----------+---------------+---------------\n -25 | -3224 | -23443 | -2344353 | -234434897853 | -234434897853\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_ints_tuple\",\n        r#\" tuple\n---------------------------------------------------------------------------------------------------------\n (i_8 = -25, i_16 = -3224, i_32 = -23443, i_64 = -2344353, i_128 = -234434897853, i_256 = -234434897853)\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_uints\",\n        r#\" u_8 | u_16 | u_32  | u_64     | u_128         | u_256\n-----+------+-------+----------+---------------+---------------\n 105 | 1050 | 83892 | 48937498 | 4378528978889 | 4378528978889\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_uints_tuple\",\n        r#\" tuple\n-------------------------------------------------------------------------------------------------------\n (u_8 = 105, u_16 = 1050, u_32 = 83892, u_64 = 48937498, u_128 = 4378528978889, u_256 = 4378528978889)\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_others\",\n        r#\" bool | f_32      | f_64               | str                   | bytes            | identity                                                           | connection_id                      | timestamp                 | duration  | uuid\n------+-----------+--------------------+-----------------------+------------------+--------------------------------------------------------------------+------------------------------------+---------------------------+-----------+----------------------------------------\n true | 594806.56 | -3454353.345389043 | \"This is spacetimedb\" | 0x01020304050607 | 0x0000000000000000000000000000000000000000000000000000000000000001 | 0x00000000000000000000000000000000 | 1970-01-01T00:00:00+00:00 | +0.000000 | \"00000000-0000-0000-0000-000000000000\"\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_others_tuple\",\n        r#\" tuple\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n (bool = true, f_32 = 594806.56, f_64 = -3454353.345389043, str = \"This is spacetimedb\", bytes = 0x01020304050607, identity = 0x0000000000000000000000000000000000000000000000000000000000000001, connection_id = 0x00000000000000000000000000000000, timestamp = 1970-01-01T00:00:00+00:00, duration = +0.000000, uuid = \"00000000-0000-0000-0000-000000000000\")\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_enums\",\n        r#\" bool_opt      | bool_result  | action\n---------------+--------------+---------------\n (some = true) | (ok = false) | (active = ())\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM t_enums_tuple\",\n        r#\" tuple\n--------------------------------------------------------------------------------\n (bool_opt = (some = true), bool_result = (ok = false), action = (active = ()))\"#,\n    );\n}\n\n#[test]\nfn test_sql_resolves_accessor_and_canonical_names_for_table() {\n    let test = Smoketest::builder().precompiled_module(\"sql-format\").build();\n\n    test.assert_sql(\n        \"SELECT * FROM accessor_table\",\n        r#\" id | accessor_value_1\n----+------------------\n 1  | 7\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM canonical_table\",\n        r#\" id | accessor_value_1\n----+------------------\n 1  | 7\"#,\n    );\n}\n\n#[test]\nfn test_sql_resolves_accessor_and_canonical_names_for_view() {\n    let test = Smoketest::builder().precompiled_module(\"sql-format\").build();\n\n    test.assert_sql(\n        \"SELECT * FROM accessor_filtered\",\n        r#\" id | accessor_value_1\n----+------------------\n 1  | 7\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM canonical_filtered\",\n        r#\" id | accessor_value_1\n----+------------------\n 1  | 7\"#,\n    );\n}\n\n#[test]\nfn test_sql_resolves_accessor_and_canonical_names_for_column() {\n    let test = Smoketest::builder().precompiled_module(\"sql-format\").build();\n\n    test.assert_sql(\n        \"SELECT accessor_value_1 FROM accessor_table\",\n        r#\" accessor_value_1\n------------------\n 7\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT accessor_value1 FROM accessor_table\",\n        r#\" accessor_value1\n-----------------\n 7\"#,\n    );\n}\n\n#[test]\nfn test_query_builder_resolves_accessor_and_canonical_names() {\n    let test = Smoketest::builder().precompiled_module(\"sql-format\").build();\n\n    test.assert_sql(\n        \"SELECT * FROM accessor_filtered\",\n        r#\" id | accessor_value_1\n----+------------------\n 1  | 7\"#,\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/templates.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n//! Template tests - ensure all project templates can be initialized, built, and published.\n//!\n//! These tests verify that:\n//! 1. `spacetime init --template <id>` successfully creates a project\n//! 2. The server-side module can be published to a SpacetimeDB instance\n//! 3. The client-side code compiles/type-checks successfully\n//!\n//! Templates are discovered dynamically by scanning the `templates/` directory for\n//! subdirectories that contain a `.template.json` metadata file.\n\nuse anyhow::{bail, Context, Result};\nuse regex::Regex;\nuse serde_json::Value;\nuse spacetimedb_smoketests::{pnpm_path, random_string, workspace_root, Smoketest};\nuse std::env;\nuse std::fs;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse tempfile::TempDir;\n\n// ============================================================================\n// Template metadata\n// ============================================================================\n\n#[derive(Debug, Clone)]\nstruct Template {\n    /// Directory name under `templates/`, used as the template id.\n    id: String,\n    /// The server language (e.g. \"rust\", \"typescript\", \"csharp\").\n    server_lang: Option<String>,\n    /// The client language (e.g. \"rust\", \"typescript\", \"csharp\").\n    client_lang: Option<String>,\n}\n\n/// Discovers all templates by scanning `<workspace_root>/templates/`.\n///\n/// A directory is treated as a template if it contains a `.template.json` file\n/// with (at minimum) a `server_lang` field - matching what `spacetime init\n/// --template` expects.\nfn get_templates() -> Vec<Template> {\n    let templates_dir = workspace_root().join(\"templates\");\n    let mut templates = Vec::new();\n\n    let entries = fs::read_dir(&templates_dir)\n        .unwrap_or_else(|e| panic!(\"Failed to read templates directory {:?}: {}\", templates_dir, e));\n\n    for entry in entries.flatten() {\n        if !entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {\n            continue;\n        }\n\n        let template_json = entry.path().join(\".template.json\");\n        if !template_json.exists() {\n            continue;\n        }\n\n        let content =\n            fs::read_to_string(&template_json).unwrap_or_else(|e| panic!(\"Failed to read {:?}: {}\", template_json, e));\n        let meta: Value =\n            serde_json::from_str(&content).unwrap_or_else(|e| panic!(\"Failed to parse {:?}: {}\", template_json, e));\n\n        templates.push(Template {\n            id: entry.file_name().to_string_lossy().into_owned(),\n            server_lang: meta[\"server_lang\"].as_str().map(String::from),\n            client_lang: meta[\"client_lang\"].as_str().map(String::from),\n        });\n    }\n\n    templates.sort_by(|a, b| a.id.cmp(&b.id));\n    templates\n}\n\n/// Converts a filesystem path into a dependency path string suitable for\n/// Cargo.toml/package.json entries.\n///\n/// On Windows, `canonicalize()` can return verbatim paths like `\\\\?\\D:\\...`.\n/// Cargo path dependencies reject that as `//?/D:/...`, so strip `\\\\?\\` first.\nfn normalize_dependency_path(local_path: &Path) -> String {\n    let abs_path = local_path.canonicalize().unwrap_or_else(|_| local_path.to_path_buf());\n    let mut path_str = abs_path.to_string_lossy().into_owned();\n\n    if let Some(stripped) = path_str.strip_prefix(r\"\\\\?\\\") {\n        path_str = stripped.to_string();\n    }\n\n    path_str.replace('\\\\', \"/\")\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/// Runs `spacetime init --template <id>` into a fresh temp directory.\n/// Returns `(tmpdir, project_path)` - caller must keep `tmpdir` alive.\nfn init_template(test: &Smoketest, template_id: &str) -> Result<(TempDir, PathBuf)> {\n    let tmpdir = tempfile::tempdir().context(\"Failed to create temp dir\")?;\n    let project_name = format!(\"test-{}\", template_id);\n    let project_path = tmpdir.path().join(&project_name);\n\n    test.spacetime(&[\n        \"init\",\n        \"--template\",\n        template_id,\n        \"--project-path\",\n        project_path.to_str().unwrap(),\n        \"--non-interactive\",\n        &project_name,\n    ])\n    .with_context(|| format!(\"spacetime init --template {} failed\", template_id))?;\n\n    if !project_path.exists() {\n        bail!(\"Project directory not created for template {}\", template_id);\n    }\n\n    Ok((tmpdir, project_path))\n}\n\n/// Updates a `[dependencies]` entry in a `Cargo.toml` to use a local path.\nfn update_cargo_toml_dependency(cargo_toml_path: &Path, package_name: &str, local_path: &Path) -> Result<()> {\n    if !cargo_toml_path.exists() {\n        return Ok(());\n    }\n    let content =\n        fs::read_to_string(cargo_toml_path).with_context(|| format!(\"Failed to read {:?}\", cargo_toml_path))?;\n    let mut cargo_data: toml::Value = content\n        .parse()\n        .with_context(|| format!(\"Failed to parse {:?}\", cargo_toml_path))?;\n\n    let deps = match cargo_data.get_mut(\"dependencies\") {\n        Some(d) => d,\n        None => return Ok(()),\n    };\n\n    if deps.get(package_name).is_none() {\n        return Ok(());\n    }\n\n    // Use a normalized path string that is accepted by Cargo on all platforms.\n    let path_str = normalize_dependency_path(local_path);\n\n    let mut table = toml::value::Table::new();\n    table.insert(\"path\".to_string(), toml::Value::String(path_str));\n    deps[package_name] = toml::Value::Table(table);\n\n    let new_content =\n        toml::to_string_pretty(&cargo_data).with_context(|| format!(\"Failed to serialize {:?}\", cargo_toml_path))?;\n    fs::write(cargo_toml_path, new_content).with_context(|| format!(\"Failed to write {:?}\", cargo_toml_path))?;\n\n    Ok(())\n}\n\n/// Updates a `dependencies` entry in a `package.json` to point to a local path.\nfn update_package_json_dependency(package_json_path: &Path, package_name: &str, local_path: &Path) -> Result<()> {\n    let content =\n        fs::read_to_string(package_json_path).with_context(|| format!(\"Failed to read {:?}\", package_json_path))?;\n    let mut data: Value =\n        serde_json::from_str(&content).with_context(|| format!(\"Failed to parse {:?}\", package_json_path))?;\n\n    let path_str = normalize_dependency_path(local_path);\n\n    if let Some(deps) = data.get_mut(\"dependencies\")\n        && deps.get(package_name).is_some()\n    {\n        deps[package_name] = Value::String(path_str);\n    }\n\n    let new_content =\n        serde_json::to_string_pretty(&data).with_context(|| format!(\"Failed to serialize {:?}\", package_json_path))?;\n    fs::write(package_json_path, new_content).with_context(|| format!(\"Failed to write {:?}\", package_json_path))?;\n\n    Ok(())\n}\n\n/// Runs pnpm with the given arguments in the given working directory.\nfn run_pnpm(args: &[&str], cwd: &Path) -> Result<()> {\n    let pnpm = pnpm_path().context(\"pnpm not found\")?;\n    let output = Command::new(&pnpm)\n        .args(args)\n        .current_dir(cwd)\n        .output()\n        .with_context(|| format!(\"Failed to spawn pnpm {}\", args.join(\" \")))?;\n    if !output.status.success() {\n        bail!(\n            \"pnpm {} (in {:?}) failed:\\nstdout: {}\\nstderr: {}\",\n            args.join(\" \"),\n            cwd,\n            String::from_utf8_lossy(&output.stdout),\n            String::from_utf8_lossy(&output.stderr)\n        );\n    }\n    Ok(())\n}\n\n/// Runs dotnet with the given arguments in the given working directory.\nfn run_dotnet(args: &[&str], cwd: &Path) -> Result<()> {\n    let output = Command::new(\"dotnet\")\n        .args(args)\n        .current_dir(cwd)\n        .output()\n        .with_context(|| format!(\"Failed to spawn dotnet {}\", args.join(\" \")))?;\n    if !output.status.success() {\n        bail!(\n            \"dotnet {} (in {:?}) failed:\\nstdout: {}\\nstderr: {}\",\n            args.join(\" \"),\n            cwd,\n            String::from_utf8_lossy(&output.stdout),\n            String::from_utf8_lossy(&output.stderr)\n        );\n    }\n    Ok(())\n}\n\n/// Best-effort debug logging for C# package resolution.\n///\n/// Prints:\n/// - `dotnet restore` output for the server project (so assets are present)\n/// - `dotnet list package --include-transitive` output for the server project\n/// - NuGet restore sources from `obj/project.assets.json`\n/// - Resolved `SpacetimeDB.*` libraries from `obj/project.assets.json`\nfn debug_log_csharp_packages(server_path: &Path) {\n    let csproj = fs::read_dir(server_path).ok().and_then(|entries| {\n        entries\n            .flatten()\n            .map(|e| e.path())\n            .find(|p| p.extension().is_some_and(|ext| ext == \"csproj\"))\n    });\n\n    if let Some(csproj) = csproj {\n        let restore_args = vec![\"restore\", csproj.to_str().unwrap_or_default()];\n        match Command::new(\"dotnet\")\n            .args(&restore_args)\n            .current_dir(server_path)\n            .output()\n        {\n            Ok(output) => {\n                eprintln!(\n                    \"[TEMPLATES][C#] dotnet {} ({:?})\\nstatus: {}\\nstdout:\\n{}\\nstderr:\\n{}\",\n                    restore_args.join(\" \"),\n                    csproj,\n                    output.status,\n                    String::from_utf8_lossy(&output.stdout),\n                    String::from_utf8_lossy(&output.stderr)\n                );\n            }\n            Err(err) => {\n                eprintln!(\n                    \"[TEMPLATES][C#] failed to run `dotnet restore` in {:?}: {}\",\n                    server_path, err\n                );\n            }\n        }\n\n        match Command::new(\"dotnet\")\n            .args([\n                \"list\",\n                csproj.to_str().unwrap_or_default(),\n                \"package\",\n                \"--include-transitive\",\n            ])\n            .current_dir(server_path)\n            .output()\n        {\n            Ok(output) => {\n                eprintln!(\n                    \"[TEMPLATES][C#] dotnet list package ({:?})\\nstatus: {}\\nstdout:\\n{}\\nstderr:\\n{}\",\n                    csproj,\n                    output.status,\n                    String::from_utf8_lossy(&output.stdout),\n                    String::from_utf8_lossy(&output.stderr)\n                );\n            }\n            Err(err) => {\n                eprintln!(\n                    \"[TEMPLATES][C#] failed to run `dotnet list package` in {:?}: {}\",\n                    server_path, err\n                );\n            }\n        }\n    } else {\n        eprintln!(\n            \"[TEMPLATES][C#] no .csproj found in {:?}; skipping package debug\",\n            server_path\n        );\n    }\n\n    let assets_path = server_path.join(\"obj\").join(\"project.assets.json\");\n    let assets = fs::read_to_string(&assets_path)\n        .ok()\n        .and_then(|text| serde_json::from_str::<Value>(&text).ok());\n\n    if let Some(assets) = assets {\n        let package_folders = assets\n            .get(\"packageFolders\")\n            .and_then(|p| p.as_object())\n            .map(|obj| obj.keys().cloned().collect::<Vec<_>>())\n            .unwrap_or_default();\n        eprintln!(\"[TEMPLATES][C#] packageFolders from {:?}:\", assets_path);\n        for folder in package_folders {\n            eprintln!(\"  - {}\", folder);\n        }\n\n        let sources = assets\n            .get(\"project\")\n            .and_then(|p| p.get(\"restore\"))\n            .and_then(|r| r.get(\"sources\"))\n            .and_then(|s| s.as_object())\n            .map(|obj| obj.keys().cloned().collect::<Vec<_>>())\n            .unwrap_or_default();\n        eprintln!(\"[TEMPLATES][C#] restore sources from {:?}:\", assets_path);\n        for src in sources {\n            eprintln!(\"  - {}\", src);\n        }\n\n        let libs = assets\n            .get(\"libraries\")\n            .and_then(|l| l.as_object())\n            .map(|obj| {\n                obj.keys()\n                    .filter(|k| k.starts_with(\"SpacetimeDB.\"))\n                    .cloned()\n                    .collect::<Vec<_>>()\n            })\n            .unwrap_or_default();\n        eprintln!(\n            \"[TEMPLATES][C#] resolved SpacetimeDB.* libraries from {:?}:\",\n            assets_path\n        );\n        for lib in libs {\n            eprintln!(\"  - {}\", lib);\n        }\n    } else {\n        eprintln!(\n            \"[TEMPLATES][C#] no readable/parseable {:?}; skipping assets debug\",\n            assets_path\n        );\n    }\n}\n\n/// Clears a package id from the global NuGet package cache to avoid stale\n/// versions with identical semantic version numbers shadowing local packed\n/// packages.\nfn clear_cached_nuget_package(package_id: &str) -> Result<()> {\n    let package_id = package_id.to_lowercase();\n\n    let mut candidate_roots = Vec::new();\n    if let Some(path) = env::var_os(\"NUGET_PACKAGES\") {\n        candidate_roots.push(PathBuf::from(path));\n    }\n    if let Some(home) = env::var_os(\"HOME\") {\n        candidate_roots.push(PathBuf::from(home).join(\".nuget\").join(\"packages\"));\n    }\n    if let Some(userprofile) = env::var_os(\"USERPROFILE\") {\n        candidate_roots.push(PathBuf::from(userprofile).join(\".nuget\").join(\"packages\"));\n    }\n\n    for root in candidate_roots {\n        let package_dir = root.join(&package_id);\n        if package_dir.exists() {\n            eprintln!(\n                \"[TEMPLATES] Clearing NuGet cache for {} at {:?}\",\n                package_id, package_dir\n            );\n            fs::remove_dir_all(&package_dir)\n                .with_context(|| format!(\"Failed to remove NuGet cache directory {:?}\", package_dir))?;\n        }\n    }\n\n    Ok(())\n}\n\n/// Reads `<Version>...</Version>` from a .csproj file.\nfn read_csproj_version(csproj_path: &Path) -> Result<String> {\n    let content = fs::read_to_string(csproj_path).with_context(|| format!(\"Failed to read {:?}\", csproj_path))?;\n    let version_re = Regex::new(r\"(?s)<Version>\\s*([^<\\s]+)\\s*</Version>\").unwrap();\n    let caps = version_re\n        .captures(&content)\n        .with_context(|| format!(\"No <Version> found in {:?}\", csproj_path))?;\n    Ok(caps.get(1).unwrap().as_str().to_string())\n}\n\n/// Pins `SpacetimeDB.Runtime` in the server project to the exact local runtime\n/// package version, avoiding wildcard resolution drift to older published\n/// versions.\nfn pin_csharp_server_runtime_package_version(server_path: &Path) -> Result<()> {\n    let runtime_csproj = workspace_root().join(\"crates/bindings-csharp/Runtime/Runtime.csproj\");\n    let runtime_version = read_csproj_version(&runtime_csproj)?;\n\n    let csproj = fs::read_dir(server_path)\n        .with_context(|| format!(\"Failed to read {:?}\", server_path))?\n        .flatten()\n        .map(|e| e.path())\n        .find(|p| p.extension().is_some_and(|ext| ext == \"csproj\"))\n        .with_context(|| format!(\"No .csproj found in {:?}\", server_path))?;\n\n    let content = fs::read_to_string(&csproj).with_context(|| format!(\"Failed to read {:?}\", csproj))?;\n    // Match PackageReference with Include+Version in either attribute order.\n    let runtime_ref_re_a =\n        Regex::new(r#\"<PackageReference\\b[^>]*\\bInclude=\"SpacetimeDB\\.Runtime\"[^>]*\\bVersion=\"([^\"]+)\"[^>]*/?>\"#)\n            .unwrap();\n    let runtime_ref_re_b =\n        Regex::new(r#\"<PackageReference\\b[^>]*\\bVersion=\"([^\"]+)\"[^>]*\\bInclude=\"SpacetimeDB\\.Runtime\"[^>]*/?>\"#)\n            .unwrap();\n\n    if runtime_ref_re_a.is_match(&content) || runtime_ref_re_b.is_match(&content) {\n        let updated = runtime_ref_re_a\n            .replace_all(&content, |caps: &regex::Captures| {\n                caps[0].replace(\n                    &format!(r#\"Version=\"{}\"\"#, &caps[1]),\n                    &format!(r#\"Version=\"{}\"\"#, runtime_version),\n                )\n            })\n            .to_string();\n        let new_content = runtime_ref_re_b\n            .replace_all(&updated, |caps: &regex::Captures| {\n                caps[0].replace(\n                    &format!(r#\"Version=\"{}\"\"#, &caps[1]),\n                    &format!(r#\"Version=\"{}\"\"#, runtime_version),\n                )\n            })\n            .to_string();\n        fs::write(&csproj, new_content).with_context(|| format!(\"Failed to write {:?}\", csproj))?;\n        eprintln!(\n            \"[TEMPLATES][C#] pinned SpacetimeDB.Runtime version in {:?} to {}\",\n            csproj, runtime_version\n        );\n    } else {\n        eprintln!(\n            \"[TEMPLATES][C#] no SpacetimeDB.Runtime PackageReference found in {:?}; skipping runtime pin\",\n            csproj\n        );\n    }\n\n    Ok(())\n}\n\n/// Pins/rewires the C# client SDK reference to the current local SDK.\nfn pin_csharp_client_sdk_package_version(project_path: &Path) -> Result<()> {\n    let client_csproj = project_path.join(\"client.csproj\");\n    if !client_csproj.exists() {\n        return Ok(());\n    }\n\n    let client_sdk_csproj = workspace_root().join(\"sdks/csharp/SpacetimeDB.ClientSDK.csproj\");\n    let client_sdk_version = read_csproj_version(&client_sdk_csproj)?;\n    let content = fs::read_to_string(&client_csproj).with_context(|| format!(\"Failed to read {:?}\", client_csproj))?;\n\n    let package_ref_re_a =\n        Regex::new(r#\"<PackageReference\\b[^>]*\\bInclude=\"SpacetimeDB\\.ClientSDK\"[^>]*\\bVersion=\"([^\"]+)\"[^>]*/?>\"#)\n            .unwrap();\n    let package_ref_re_b =\n        Regex::new(r#\"<PackageReference\\b[^>]*\\bVersion=\"([^\"]+)\"[^>]*\\bInclude=\"SpacetimeDB\\.ClientSDK\"[^>]*/?>\"#)\n            .unwrap();\n    let project_ref_re =\n        Regex::new(r#\"<ProjectReference\\b[^>]*\\bInclude=\"[^\"]*SpacetimeDB\\.ClientSDK\\.csproj\"[^>]*/?>\"#).unwrap();\n\n    let mut changed = false;\n    let mut new_content = content.clone();\n\n    if package_ref_re_a.is_match(&new_content) || package_ref_re_b.is_match(&new_content) {\n        new_content = package_ref_re_a\n            .replace_all(&new_content, |caps: &regex::Captures| {\n                changed = true;\n                caps[0].replace(\n                    &format!(r#\"Version=\"{}\"\"#, &caps[1]),\n                    &format!(r#\"Version=\"{}\"\"#, client_sdk_version),\n                )\n            })\n            .to_string();\n        new_content = package_ref_re_b\n            .replace_all(&new_content, |caps: &regex::Captures| {\n                changed = true;\n                caps[0].replace(\n                    &format!(r#\"Version=\"{}\"\"#, &caps[1]),\n                    &format!(r#\"Version=\"{}\"\"#, client_sdk_version),\n                )\n            })\n            .to_string();\n    }\n\n    if project_ref_re.is_match(&new_content) {\n        let project_ref = format!(\n            r#\"<ProjectReference Include=\"{}\" />\"#,\n            normalize_dependency_path(&client_sdk_csproj)\n        );\n        new_content = project_ref_re.replace_all(&new_content, project_ref).to_string();\n        changed = true;\n    }\n\n    if changed {\n        fs::write(&client_csproj, new_content).with_context(|| format!(\"Failed to write {:?}\", client_csproj))?;\n        eprintln!(\n            \"[TEMPLATES][C#] pinned client SDK reference in {:?} to version {}\",\n            client_csproj, client_sdk_version\n        );\n    } else {\n        eprintln!(\n            \"[TEMPLATES][C#] no SpacetimeDB.ClientSDK reference found in {:?}; skipping client pin\",\n            client_csproj\n        );\n    }\n\n    Ok(())\n}\n\n/// Builds the TypeScript SDK (`crates/bindings-typescript`).\n///\n/// Should be called once before testing any TypeScript templates.\nfn build_typescript_sdk() -> Result<()> {\n    let sdk_path = workspace_root().join(\"crates/bindings-typescript\");\n    eprintln!(\"[TEMPLATES] Building TypeScript SDK at {:?}\", sdk_path);\n    run_pnpm(&[\"install\"], &sdk_path)?;\n    run_pnpm(&[\"build\"], &sdk_path)?;\n    Ok(())\n}\n\n/// Points the `spacetimedb` entry in `package.json` at the local TypeScript\n/// SDK and removes the template's lockfile so pnpm re-resolves dependencies.\nfn setup_typescript_sdk_in_package_json(package_json_path: &Path) -> Result<()> {\n    let sdk_path = workspace_root().join(\"crates/bindings-typescript\");\n    update_package_json_dependency(package_json_path, \"spacetimedb\", &sdk_path)?;\n\n    // Remove the template's lockfile; the dependency changed.\n    let lockfile = package_json_path.parent().unwrap().join(\"pnpm-lock.yaml\");\n    if lockfile.exists() {\n        fs::remove_file(&lockfile).context(\"Failed to remove pnpm-lock.yaml\")?;\n    }\n    Ok(())\n}\n\n/// Rewires the Rust server module's `spacetimedb` dependency to the local\n/// `crates/bindings` path.\n///\n/// Templates contain a relative path (`../../../crates/bindings`) that is only\n/// valid inside the repo's `templates/` tree.  After `spacetime init` copies\n/// the template to a temp directory the relative path is wrong, so we replace\n/// it with an absolute path.\nfn setup_rust_server_sdk(server_path: &Path) -> Result<()> {\n    let bindings_path = workspace_root().join(\"crates/bindings\");\n    update_cargo_toml_dependency(&server_path.join(\"Cargo.toml\"), \"spacetimedb\", &bindings_path)\n}\n\n/// Rewires the Rust client's `spacetimedb-sdk` dependency to the local\n/// `sdks/rust` path.\nfn setup_rust_client_sdk(project_path: &Path) -> Result<()> {\n    let sdk_path = workspace_root().join(\"sdks/rust\");\n    update_cargo_toml_dependency(&project_path.join(\"Cargo.toml\"), \"spacetimedb-sdk\", &sdk_path)\n}\n\n/// Creates a local `nuget.config`, packs all required SpacetimeDB C# packages\n/// from source, and registers them as local NuGet sources.\nfn setup_csharp_nuget(project_path: &Path) -> Result<PathBuf> {\n    eprintln!(\"[TEMPLATES] Setting up C# NuGet sources at {:?}\", project_path);\n\n    // NuGet can reuse stale packages from global cache even if we add local\n    // package sources below. Remove the relevant package IDs so restore/publish\n    // uses freshly packed local artifacts.\n    for package in &[\n        \"SpacetimeDB.Runtime\",\n        \"SpacetimeDB.BSATN.Runtime\",\n        \"SpacetimeDB.Codegen\",\n        \"SpacetimeDB.BSATN.Codegen\",\n        \"SpacetimeDB.ClientSDK\",\n    ] {\n        clear_cached_nuget_package(package)?;\n    }\n\n    let nuget_config = project_path.join(\"nuget.config\");\n    if !nuget_config.exists() {\n        fs::write(\n            &nuget_config,\n            r#\"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <packageSources>\n    <clear />\n    <add key=\"nuget.org\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n</configuration>\n\"#,\n        )\n        .context(\"Failed to write nuget.config\")?;\n    }\n\n    let bindings = workspace_root().join(\"crates/bindings-csharp\");\n    for pkg in &[\"BSATN.Runtime\", \"Runtime\", \"BSATN.Codegen\", \"Codegen\"] {\n        run_dotnet(&[\"pack\", \"-c\", \"Release\"], &bindings.join(pkg))?;\n        let pkg_output = bindings.join(pkg).join(\"bin\").join(\"Release\");\n        run_dotnet(\n            &[\n                \"nuget\",\n                \"add\",\n                \"source\",\n                pkg_output.to_str().unwrap(),\n                \"-n\",\n                &format!(\"SpacetimeDB.{}\", pkg),\n                \"--configfile\",\n                nuget_config.to_str().unwrap(),\n            ],\n            project_path,\n        )?;\n    }\n\n    // Pack and register the client SDK (needed by client templates).\n    let client_sdk = workspace_root().join(\"sdks/csharp\");\n    let client_sdk_proj = client_sdk.join(\"SpacetimeDB.ClientSDK.csproj\");\n    run_dotnet(\n        &[\n            \"pack\",\n            client_sdk_proj.to_str().unwrap(),\n            \"-c\",\n            \"Release\",\n            \"--configfile\",\n            nuget_config.to_str().unwrap(),\n        ],\n        project_path,\n    )?;\n    let client_sdk_output = client_sdk.join(\"bin~\").join(\"Release\");\n    run_dotnet(\n        &[\n            \"nuget\",\n            \"add\",\n            \"source\",\n            client_sdk_output.to_str().unwrap(),\n            \"-n\",\n            \"SpacetimeDB.ClientSDK\",\n            \"--configfile\",\n            nuget_config.to_str().unwrap(),\n        ],\n        project_path,\n    )?;\n\n    Ok(nuget_config)\n}\n\n// ============================================================================\n// Per-language publish + client-test helpers\n// ============================================================================\n\n/// Publishes a Rust server module and verifies the Rust client builds.\nfn test_rust_template(test: &Smoketest, template: &Template, project_path: &Path) -> Result<()> {\n    let server_path = project_path.join(\"spacetimedb\");\n    setup_rust_server_sdk(&server_path)?;\n\n    let domain = format!(\"test-{}-{}\", template.id, random_string());\n    test.spacetime(&[\n        \"publish\",\n        \"--server\",\n        &test.server_url,\n        \"--yes\",\n        \"--module-path\",\n        server_path.to_str().unwrap(),\n        &domain,\n    ])\n    .with_context(|| format!(\"spacetime publish failed for Rust server in template {}\", template.id))?;\n    // Best-effort cleanup.\n    let _ = test.spacetime(&[\"delete\", \"--server\", &test.server_url, \"--yes\", &domain]);\n\n    if template.client_lang.as_deref() == Some(\"rust\") {\n        setup_rust_client_sdk(project_path)?;\n        let output = Command::new(\"cargo\")\n            .args([\"build\"])\n            .current_dir(project_path)\n            .output()\n            .context(\"Failed to run cargo build\")?;\n        if !output.status.success() {\n            bail!(\n                \"cargo build for {} client failed:\\nstdout: {}\\nstderr: {}\",\n                template.id,\n                String::from_utf8_lossy(&output.stdout),\n                String::from_utf8_lossy(&output.stderr)\n            );\n        }\n    }\n    Ok(())\n}\n\n/// Publishes a TypeScript server module and verifies the TypeScript client type-checks.\nfn test_typescript_template(test: &Smoketest, template: &Template, project_path: &Path) -> Result<()> {\n    // Server\n    let server_path = project_path.join(\"spacetimedb\");\n    setup_typescript_sdk_in_package_json(&server_path.join(\"package.json\"))?;\n    run_pnpm(&[\"install\"], &server_path)?;\n\n    let domain = format!(\"test-{}-{}\", template.id, random_string());\n    test.spacetime(&[\n        \"publish\",\n        \"--server\",\n        &test.server_url,\n        \"--yes\",\n        \"--module-path\",\n        server_path.to_str().unwrap(),\n        &domain,\n    ])\n    .with_context(|| {\n        format!(\n            \"spacetime publish failed for TypeScript server in template {}\",\n            template.id\n        )\n    })?;\n    let _ = test.spacetime(&[\"delete\", \"--server\", &test.server_url, \"--yes\", &domain]);\n\n    // Client type-check (only if there's a client package.json in the project root)\n    let client_package_json = project_path.join(\"package.json\");\n    if client_package_json.exists() {\n        setup_typescript_sdk_in_package_json(&client_package_json)?;\n        run_pnpm(&[\"install\"], project_path)?;\n\n        // TODO: some templates don't pass tsc yet, re-enable once they're fixed.\n        // run_pnpm(&[\"exec\", \"tsc\", \"--noEmit\"], project_path)?;\n    }\n    Ok(())\n}\n\n/// Publishes a C# server module and verifies the C# client builds.\nfn test_csharp_template(test: &Smoketest, template: &Template, project_path: &Path) -> Result<()> {\n    // Use one nuget.config at the project root, shared between server and client.\n    setup_csharp_nuget(project_path)?;\n\n    let server_path = project_path.join(\"spacetimedb\");\n    pin_csharp_server_runtime_package_version(&server_path)?;\n    // Copy nuget.config into the server directory so `spacetime publish` (which runs\n    // `dotnet publish` from the server dir) can find the local package sources.\n    let root_nuget = project_path.join(\"nuget.config\");\n    let server_nuget = server_path.join(\"nuget.config\");\n    if root_nuget.exists() && !server_nuget.exists() {\n        fs::copy(&root_nuget, &server_nuget).context(\"Failed to copy nuget.config to server dir\")?;\n    }\n\n    // Debug package resolution to diagnose CI/local NuGet source/version drift.\n    debug_log_csharp_packages(&server_path);\n\n    let domain = format!(\"test-{}-{}\", template.id, random_string());\n    test.spacetime(&[\n        \"publish\",\n        \"--server\",\n        &test.server_url,\n        \"--yes\",\n        \"--module-path\",\n        server_path.to_str().unwrap(),\n        &domain,\n    ])\n    .with_context(|| format!(\"spacetime publish failed for C# server in template {}\", template.id))?;\n    let _ = test.spacetime(&[\"delete\", \"--server\", &test.server_url, \"--yes\", &domain]);\n\n    if template.client_lang.as_deref() == Some(\"csharp\") {\n        pin_csharp_client_sdk_package_version(project_path)?;\n        run_dotnet(&[\"build\"], project_path)?;\n    }\n    Ok(())\n}\n\n/// Runs the full init + publish + client-test cycle for a single template.\nfn test_template(test: &Smoketest, template: &Template) -> Result<()> {\n    eprintln!(\"[TEMPLATES] Testing template: {}\", template.id);\n\n    let (_tmpdir, project_path) = init_template(test, &template.id)?;\n\n    match template.server_lang.as_deref() {\n        Some(\"rust\") => test_rust_template(test, template, &project_path)?,\n        Some(\"typescript\") => test_typescript_template(test, template, &project_path)?,\n        Some(\"csharp\") => test_csharp_template(test, template, &project_path)?,\n        Some(other) => {\n            eprintln!(\n                \"[TEMPLATES] Skipping template {} with unsupported server language: {}\",\n                template.id, other\n            );\n        }\n        None => {\n            eprintln!(\"[TEMPLATES] Skipping template {} with no server language\", template.id);\n        }\n    }\n\n    Ok(())\n}\n\n// ============================================================================\n// Test entry point\n// ============================================================================\n\n/// Tests all templates discovered in the `templates/` directory.\n///\n/// For each template the test:\n/// 1. Runs `spacetime init --template <id>` into a temp directory\n/// 2. Wires local SDK dependencies so the template builds against the current source\n/// 3. Publishes the server module and verifies it succeeds\n/// 4. Type-checks / builds the client code\n///\n/// All templates are exercised even if some fail; a summary is printed at the\n/// end and the test fails if any template did.\n#[test]\nfn test_all_templates() {\n    let templates = get_templates();\n    assert!(!templates.is_empty(), \"No templates found in templates/\");\n\n    let has_typescript = templates.iter().any(|t| t.server_lang.as_deref() == Some(\"typescript\"));\n    let has_csharp = templates.iter().any(|t| t.server_lang.as_deref() == Some(\"csharp\"));\n\n    // Guard checks - verify required tools are available before starting.\n    if has_typescript {\n        spacetimedb_smoketests::require_pnpm!();\n    }\n    if has_csharp {\n        spacetimedb_smoketests::require_dotnet!();\n    }\n\n    // Build the TypeScript SDK once up-front if any TypeScript templates exist.\n    if has_typescript {\n        build_typescript_sdk().expect(\"Failed to build TypeScript SDK\");\n    }\n\n    // One shared server for all templates.\n    let test = Smoketest::builder().autopublish(false).build();\n\n    let mut results: Vec<(String, Result<()>)> = Vec::new();\n\n    for template in &templates {\n        let result = test_template(&test, template);\n        let passed = result.is_ok();\n        eprintln!(\n            \"[TEMPLATES] {} {}\",\n            if passed { \"[PASS]\" } else { \"[FAIL]\" },\n            template.id\n        );\n        results.push((template.id.clone(), result));\n    }\n\n    // Print summary.\n    eprintln!(\"\\n{}\", \"=\".repeat(60));\n    eprintln!(\"TEMPLATE TEST SUMMARY\");\n    eprintln!(\"{}\", \"=\".repeat(60));\n    for (id, result) in &results {\n        eprintln!(\n            \"{:40} {}\",\n            id,\n            if result.is_ok() {\n                \"[PASS]\".to_string()\n            } else {\n                format!(\"[FAIL]: {:#}\", result.as_ref().unwrap_err())\n            }\n        );\n    }\n    let passed = results.iter().filter(|(_, r)| r.is_ok()).count();\n    let total = results.len();\n    eprintln!(\"{}\", \"=\".repeat(60));\n    eprintln!(\"TOTAL: {}/{} passed\", passed, total);\n\n    // Fail if any template failed.\n    let failures: Vec<_> = results\n        .iter()\n        .filter(|(_, r)| r.is_err())\n        .map(|(id, r)| format!(\"  {}: {:#}\", id, r.as_ref().unwrap_err()))\n        .collect();\n\n    if !failures.is_empty() {\n        panic!(\n            \"{}/{} template(s) failed:\\n{}\",\n            failures.len(),\n            total,\n            failures.join(\"\\n\")\n        );\n    }\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/timestamp_route.rs",
    "content": "use spacetimedb_smoketests::{random_string, Smoketest};\n\nconst TIMESTAMP_TAG: &str = \"__timestamp_micros_since_unix_epoch__\";\n\n/// Test the /v1/database/{name}/unstable/timestamp endpoint\n#[test]\nfn test_timestamp_route() {\n    let mut test = Smoketest::builder().autopublish(false).build();\n\n    let name = random_string();\n\n    // Since we didn't publish, we're not logged in yet, so `api_call` will fail to get a token.\n    test.new_identity().unwrap();\n\n    // A request for the timestamp at a non-existent database is an error with code 404\n    let resp = test\n        .api_call(\"GET\", &format!(\"/v1/database/{}/unstable/timestamp\", name))\n        .unwrap();\n    assert_eq!(\n        resp.status_code, 404,\n        \"Expected 404 for non-existent database, got {}\",\n        resp.status_code\n    );\n\n    // Publish a module with the random name\n    test.publish_module_named(&name, false).unwrap();\n\n    // A request for the timestamp at an extant database is a success\n    let resp = test\n        .api_call(\"GET\", &format!(\"/v1/database/{}/unstable/timestamp\", name))\n        .unwrap();\n    assert!(\n        resp.is_success(),\n        \"Expected success for existing database, got {}\",\n        resp.status_code\n    );\n\n    // The response body is a SATS-JSON encoded `Timestamp`\n    let timestamp = resp.json().unwrap();\n    assert!(\n        timestamp.is_object(),\n        \"Expected timestamp to be an object, got {:?}\",\n        timestamp\n    );\n    assert!(\n        timestamp.get(TIMESTAMP_TAG).is_some(),\n        \"Expected timestamp to have '{}' field, got {:?}\",\n        TIMESTAMP_TAG,\n        timestamp\n    );\n    assert!(\n        timestamp[TIMESTAMP_TAG].is_i64() || timestamp[TIMESTAMP_TAG].is_u64(),\n        \"Expected timestamp value to be an integer, got {:?}\",\n        timestamp[TIMESTAMP_TAG]\n    );\n}\n"
  },
  {
    "path": "crates/smoketests/tests/smoketests/views.rs",
    "content": "use serde_json::{json, Value};\nuse spacetimedb_smoketests::{require_dotnet, require_pnpm, Smoketest};\n\nconst TS_VIEWS_SUBSCRIBE_MODULE: &str = r#\"import { schema, t, table } from \"spacetimedb/server\";\n\nconst playerState = table(\n  { name: \"player_state\" },\n  {\n    identity: t.identity().primaryKey(),\n    name: t.string().unique(),\n    online: t.bool(),\n  }\n);\n\nconst spacetimedb = schema({ playerState });\nexport default spacetimedb;\n\nexport const my_player = spacetimedb.view(\n  { public: true },\n  t.option(playerState.rowType),\n  ctx => ctx.db.playerState.identity.find(ctx.sender) ?? undefined\n);\n\nexport const all_players = spacetimedb.anonymousView(\n  { public: true },\n  t.array(playerState.rowType),\n  ctx => ctx.from.playerState\n);\n\nexport const online_players = spacetimedb.anonymousView(\n  { public: true },\n  t.array(playerState.rowType),\n  ctx => ctx.from.playerState.where(row => row.online)\n);\n\nexport const insert_player_proc = spacetimedb.procedure(\n  { name: t.string() },\n  t.unit(),\n  (ctx, { name }) => {\n    const sender = ctx.sender;\n    ctx.withTx(tx => {\n      tx.db.playerState.insert({ name, identity: sender, online: true });\n    });\n    return {};\n  }\n);\n\"#;\n\nconst CS_VIEWS_QUERY_BUILDER_MODULE: &str = r#\"using SpacetimeDB;\n\npublic static partial class Module\n{\n    [Table(Accessor = \"Table\", Public = true)]\n    public partial struct Table\n    {\n        public uint Value;\n        public bool Alive;\n    }\n\n    [Reducer]\n    public static void InsertValue(ReducerContext ctx, uint value, bool alive)\n    {\n        ctx.Db.Table.Insert(new Table { Value = value, Alive = alive });\n    }\n\n    [View(Accessor = \"all\", Public = true)]\n    public static IQuery<Table> All(ViewContext ctx)\n    {\n        return ctx.From.Table();\n    }\n\n    [View(Accessor = \"some\", Public = true)]\n    public static IQuery<Table> Some(ViewContext ctx)\n    {\n        return ctx.From.Table().Where(Row => Row.Alive);\n    }\n}\n\"#;\n\nconst CS_COUNT_VIEW_MODULE: &str = r#\"using SpacetimeDB;\n\n[SpacetimeDB.Type]\npublic partial struct ItemCount\n{\n    public ulong count;\n}\n\npublic static partial class Module\n{\n    [Table(Accessor = \"item\", Public = true)]\n    public partial struct Item\n    {\n        [PrimaryKey]\n        public uint id;\n        public uint value;\n    }\n\n    [View(Accessor = \"sender_table_count\", Public = true)]\n    public static ItemCount? sender_table_count(ViewContext ctx)\n    {\n        return new ItemCount { count = ctx.Db.item.Count };\n    }\n\n    [View(Accessor = \"anon_table_count\", Public = true)]\n    public static ItemCount? anon_table_count(AnonymousViewContext ctx)\n    {\n        return new ItemCount { count = ctx.Db.item.Count };\n    }\n\n    [Reducer]\n    public static void insert_item(ReducerContext ctx, uint id, uint value)\n    {\n        ctx.Db.item.Insert(new Item { id = id, value = value });\n    }\n\n    [Reducer]\n    public static void replace_item(ReducerContext ctx, uint id, uint value)\n    {\n        ctx.Db.item.id.Delete(id);\n        ctx.Db.item.Insert(new Item { id = id, value = value });\n    }\n\n    [Reducer]\n    public static void delete_item(ReducerContext ctx, uint id)\n    {\n        ctx.Db.item.id.Delete(id);\n    }\n}\n\"#;\n\nconst TS_COUNT_VIEW_MODULE: &str = r#\"import { schema, t, table } from \"spacetimedb/server\";\n\nconst item = table(\n  { name: \"item\" },\n  {\n    id: t.u32().primaryKey(),\n    value: t.u32(),\n  }\n);\n\nconst itemCount = t.object(\"ItemCountRow\", {\n  count: t.u64(),\n});\n\nconst spacetimedb = schema({ item });\nexport default spacetimedb;\n\nexport const sender_table_count = spacetimedb.view(\n  { public: true },\n  t.option(itemCount),\n  ctx => ({ count: ctx.db.item.count() })\n);\n\nexport const anon_table_count = spacetimedb.anonymousView(\n  { public: true },\n  t.option(itemCount),\n  ctx => ({ count: ctx.db.item.count() })\n);\n\nexport const insert_item = spacetimedb.reducer(\n  { id: t.u32(), value: t.u32() },\n  (ctx, { id, value }) => {\n    ctx.db.item.insert({ id, value });\n  }\n);\n\nexport const replace_item = spacetimedb.reducer(\n  { id: t.u32(), value: t.u32() },\n  (ctx, { id, value }) => {\n    ctx.db.item.id.delete(id);\n    ctx.db.item.insert({ id, value });\n  }\n);\n\nexport const delete_item = spacetimedb.reducer(\n  { id: t.u32() },\n  (ctx, { id }) => {\n    ctx.db.item.id.delete(id);\n  }\n);\n\"#;\n\nfn project_fields(events: Vec<Value>, view_name: &str, projected_fields: &[&str]) -> Vec<Value> {\n    let project_row = |row: &Value| {\n        if projected_fields.is_empty() {\n            row.clone()\n        } else {\n            let mut projected = serde_json::Map::new();\n            for field in projected_fields {\n                if let Some(value) = row.get(*field) {\n                    projected.insert((*field).to_string(), value.clone());\n                }\n            }\n            Value::Object(projected)\n        }\n    };\n\n    events\n        .into_iter()\n        .map(|event| {\n            json!({\n                view_name: {\n                    \"deletes\": event[view_name][\"deletes\"]\n                        .as_array()\n                        .unwrap()\n                        .iter()\n                        .map(&project_row)\n                        .collect::<Vec<_>>(),\n                    \"inserts\": event[view_name][\"inserts\"]\n                        .as_array()\n                        .unwrap()\n                        .iter()\n                        .map(&project_row)\n                        .collect::<Vec<_>>()\n                }\n            })\n        })\n        .collect()\n}\n\nfn assert_count_view_refresh_behavior(test: &Smoketest, view_name: &str, id: &str, value: &str, updated_value: &str) {\n    let query = format!(\"select * from {view_name}\");\n    let sub = test.subscribe_background(&[&query], 2).unwrap();\n\n    test.call(\"insert_item\", &[id, value]).unwrap();\n    test.call(\"replace_item\", &[id, updated_value]).unwrap();\n    test.call(\"delete_item\", &[id]).unwrap();\n\n    let events = sub.collect().unwrap();\n    let projection = project_fields(events, view_name, &[\"count\"]);\n    assert_eq!(\n        serde_json::json!(projection),\n        serde_json::json!([\n            {view_name: {\"deletes\": [{\"count\": 0}], \"inserts\": [{\"count\": 1}]}},\n            {view_name: {\"deletes\": [{\"count\": 1}], \"inserts\": [{\"count\": 0}]}}\n        ])\n    );\n}\n\nfn assert_all_count_view_refreshes(test: &Smoketest) {\n    assert_count_view_refresh_behavior(test, \"sender_table_count\", \"1\", \"10\", \"11\");\n    assert_count_view_refresh_behavior(test, \"anon_table_count\", \"2\", \"20\", \"21\");\n}\n\n/// Tests that views populate the st_view_* system tables\n#[test]\nfn test_st_view_tables() {\n    let test = Smoketest::builder().precompiled_module(\"views-basic\").build();\n\n    test.assert_sql(\n        \"SELECT * FROM st_view\",\n        r#\" view_id | view_name | table_id      | is_public | is_anonymous\n---------+-----------+---------------+-----------+--------------\n 4096    | \"player\"  | (some = 4097) | true      | false\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM st_view_column\",\n        r#\" view_id | col_pos | col_name | col_type\n---------+---------+----------+----------\n 4096    | 0       | \"id\"     | 0x0d\n 4096    | 1       | \"level\"  | 0x0d\"#,\n    );\n}\n\n/// Publishing a module should fail if a table and view have the same name\n#[test]\nfn test_fail_publish_namespace_collision() {\n    let mut test = Smoketest::builder()\n        // Can't be precompiled because the code is intentionally broken\n        .module_code(include_str!(\"../../modules/views-broken-namespace/src/lib.rs\"))\n        .autopublish(false)\n        .build();\n\n    let result = test.publish_module();\n    assert!(\n        result.is_err(),\n        \"Expected publish to fail when table and view have same name\"\n    );\n}\n\n/// Publishing a module should fail if the inner return type is not a product type\n#[test]\nfn test_fail_publish_wrong_return_type() {\n    let mut test = Smoketest::builder()\n        // Can't be precompiled because the code is intentionally broken\n        .module_code(include_str!(\"../../modules/views-broken-return-type/src/lib.rs\"))\n        .autopublish(false)\n        .build();\n\n    let result = test.publish_module();\n    assert!(\n        result.is_err(),\n        \"Expected publish to fail when view return type is not a product type\"\n    );\n}\n\n/// Tests that views can be queried over HTTP SQL\n#[test]\nfn test_http_sql_views() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    // Insert initial data\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (42, 7)\").unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 42 | 7\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM player_none\",\n        r#\" id | level\n----+-------\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM player_vec\",\n        r#\" id | level\n----+-------\n 42 | 7\n 7  | 3\"#,\n    );\n}\n\n#[test]\nfn test_view_materialization() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    let player_called_log = \"player view called\";\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 1, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (42, 7);\")\n        .unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 42 | 7\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 2, \"Unexpected logs: {logs:?}\");\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 42 | 7\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 2, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (22, 8);\")\n        .unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 42 | 7\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 2, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"UPDATE player_state SET level = 9 WHERE id = 42;\").unwrap();\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 3, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"UPDATE player_state SET level = 7 WHERE id = 42;\").unwrap();\n}\n\n#[test]\nfn test_view_multi_index_materialization() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    let player_called_log = \"player_info called\";\n\n    test.assert_sql(\n        \"SELECT * FROM player_info_multi_index\",\n        r#\" id | age | level\n----+-----+-------\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 1, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"INSERT INTO player_info (id, age, level) VALUES (1, 25, 7);\")\n        .unwrap();\n    test.assert_sql(\n        \"SELECT * FROM player_info_multi_index\",\n        r#\" id | age | level\n----+-----+-------\n 1  | 25  | 7\"#,\n    );\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 2, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"INSERT INTO player_info (id, age, level) VALUES (2, 25, 8);\")\n        .unwrap();\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 2, \"Unexpected logs: {logs:?}\");\n\n    test.sql(\"UPDATE player_info SET age = 26 WHERE id = 1;\").unwrap();\n    let logs = test.logs(100).unwrap();\n    let count = logs.iter().filter(|l| l.contains(player_called_log)).count();\n    assert_eq!(count, 3, \"Unexpected logs: {logs:?}\");\n\n    test.assert_sql(\n        \"SELECT * FROM player_info_multi_index\",\n        r#\" id | age | level\n----+-----+-------\"#,\n    );\n}\n\n/// Tests that anonymous views are updated for reducers\n#[test]\nfn test_query_anonymous_view_reducer() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    test.call(\"add_player_level\", &[\"0\", \"1\"]).unwrap();\n    test.call(\"add_player_level\", &[\"1\", \"2\"]).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM my_player_and_level\",\n        r#\" id | level\n----+-------\n 0  | 1\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM player_and_level\",\n        r#\" id | level\n----+-------\n 1  | 2\"#,\n    );\n\n    test.call(\"add_player_level\", &[\"2\", \"2\"]).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player_and_level\",\n        r#\" id | level\n----+-------\n 1  | 2\n 2  | 2\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM player_and_level WHERE id = 2\",\n        r#\" id | level\n----+-------\n 2  | 2\"#,\n    );\n}\n\n#[test]\nfn test_views_auto_migration() {\n    let mut test = Smoketest::builder().precompiled_module(\"views-auto-migrate\").build();\n\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (1, 1);\").unwrap();\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (2, 2);\").unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 1  | 1\"#,\n    );\n\n    test.use_precompiled_module(\"views-auto-migrate-updated\");\n    test.publish_module_clear(false).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 2  | 2\"#,\n    );\n}\n\n#[test]\nfn test_auto_migration_drop_view() {\n    let mut test = Smoketest::builder().precompiled_module(\"views-auto-migrate\").build();\n    test.use_precompiled_module(\"views-drop-view\");\n    test.publish_module_clear(false).unwrap();\n}\n\n#[test]\nfn test_auto_migration_add_view() {\n    let mut test = Smoketest::builder().precompiled_module(\"views-drop-view\").build();\n    test.use_precompiled_module(\"views-auto-migrate\");\n    test.publish_module_clear(false).unwrap();\n}\n\n#[test]\nfn test_view_accessibility() {\n    let test = Smoketest::builder().precompiled_module(\"views-callable\").build();\n\n    test.new_identity().unwrap();\n    test.call(\"baz\", &[]).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM items\",\n        r#\" value\n-------\n 7\"#,\n    );\n}\n\n#[test]\nfn test_recovery_from_trapped_views_auto_migration() {\n    let mut test = Smoketest::builder().precompiled_module(\"views-auto-migrate\").build();\n\n    test.sql(\"INSERT INTO player_state (id, level) VALUES (1, 1);\").unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 1  | 1\"#,\n    );\n\n    test.use_precompiled_module(\"views-trapped\");\n    let result = test.publish_module_clear(false);\n    assert!(result.is_err(), \"Expected trapped publish to fail\");\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\n 1  | 1\"#,\n    );\n\n    test.use_precompiled_module(\"views-recovered\");\n    test.publish_module_clear(false).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM player\",\n        r#\" id | level\n----+-------\"#,\n    );\n}\n\n#[test]\nfn test_subscribing_with_different_identities() {\n    let test = Smoketest::builder().precompiled_module(\"views-subscribe\").build();\n\n    test.call(\"insert_player\", &[\"Alice\"]).unwrap();\n\n    test.new_identity().unwrap();\n\n    let sub = test.subscribe_background(&[\"select * from my_player\"], 2).unwrap();\n    test.call(\"insert_player\", &[\"Bob\"]).unwrap();\n    let events = sub.collect().unwrap();\n\n    let projection = project_fields(events, \"my_player\", &[\"name\"]);\n    assert_eq!(\n        serde_json::json!(projection),\n        serde_json::json!([\n            {\"my_player\": {\"deletes\": [], \"inserts\": [{\"name\": \"Bob\"}]}}\n        ])\n    );\n}\n\n#[test]\nfn test_query_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n    test.assert_sql(\n        \"SELECT * FROM online_users\",\n        r#\" identity | name    | online\n----------+---------+--------\n 1        | \"Alice\" | true\"#,\n    );\n}\n\n#[test]\nfn test_query_right_semijoin_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n    test.assert_sql(\n        \"SELECT * FROM online_users_age\",\n        r#\" identity | name    | age\n----------+---------+-----\n 1        | \"Alice\" | 30\"#,\n    );\n}\n\n#[test]\nfn test_query_left_semijoin_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n    test.assert_sql(\n        \"SELECT * FROM users_whos_age_is_known\",\n        r#\" identity | name    | online\n----------+---------+--------\n 1        | \"Alice\" | true\n 2        | \"BOB\"   | false\"#,\n    );\n}\n\n#[test]\nfn test_query_complex_right_semijoin_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n    test.assert_sql(\n        \"SELECT * FROM offline_user_20_years_old\",\n        r#\" identity | name  | online\n----------+-------+--------\n 2        | \"BOB\" | false\"#,\n    );\n}\n\n#[test]\nfn test_where_expr_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n    test.assert_sql(\n        \"SELECT * FROM users_who_are_above_20_and_below_30\",\n        r#\" identity | name | age\n----------+------+-----\"#,\n    );\n\n    test.assert_sql(\n        \"SELECT * FROM users_who_are_above_eq_20_and_below_eq_30\",\n        r#\" identity | name    | age\n----------+---------+-----\n 1        | \"Alice\" | 30\n 2        | \"BOB\"   | 20\"#,\n    );\n}\n\n#[test]\nfn test_procedure_triggers_subscription_updates() {\n    let test = Smoketest::builder().precompiled_module(\"views-subscribe\").build();\n    let sub = test.subscribe_background(&[\"select * from my_player\"], 1).unwrap();\n    test.call(\"insert_player_proc\", &[\"Alice\"]).unwrap();\n    let events = sub.collect().unwrap();\n    let projection = project_fields(events, \"my_player\", &[\"name\"]);\n    assert_eq!(\n        serde_json::json!(projection),\n        serde_json::json!([\n            {\"my_player\": {\"deletes\": [], \"inserts\": [{\"name\": \"Alice\"}]}}\n        ])\n    );\n}\n\n#[test]\nfn test_typescript_procedure_triggers_subscription_updates() {\n    require_pnpm!();\n    let mut test = Smoketest::builder().autopublish(false).build();\n    test.publish_typescript_module_source(\n        \"views-subscribe-typescript\",\n        \"views-subscribe-typescript\",\n        TS_VIEWS_SUBSCRIBE_MODULE,\n    )\n    .unwrap();\n\n    let sub = test.subscribe_background(&[\"select * from my_player\"], 1).unwrap();\n    test.call(\"insert_player_proc\", &[\"Alice\"]).unwrap();\n    let events = sub.collect().unwrap();\n\n    let projection = project_fields(events, \"my_player\", &[\"name\"]);\n    assert_eq!(\n        serde_json::json!(projection),\n        serde_json::json!([\n            {\"my_player\": {\"deletes\": [], \"inserts\": [{\"name\": \"Alice\"}]}}\n        ])\n    );\n}\n\n#[test]\nfn test_rust_count_view_subscription_refreshes() {\n    let test = Smoketest::builder().precompiled_module(\"views-count\").build();\n    assert_all_count_view_refreshes(&test);\n}\n\n#[test]\nfn test_csharp_count_view_subscription_refreshes() {\n    require_dotnet!();\n\n    let mut test = Smoketest::builder().autopublish(false).build();\n    test.publish_csharp_module_source(\"views-count-csharp\", \"views-count-csharp\", CS_COUNT_VIEW_MODULE)\n        .unwrap();\n\n    assert_all_count_view_refreshes(&test);\n}\n\n#[test]\nfn test_typescript_count_view_subscription_refreshes() {\n    require_pnpm!();\n\n    let mut test = Smoketest::builder().autopublish(false).build();\n    test.publish_typescript_module_source(\"views-count-typescript\", \"views-count-typescript\", TS_COUNT_VIEW_MODULE)\n        .unwrap();\n\n    assert_all_count_view_refreshes(&test);\n}\n\n#[test]\nfn test_disconnect_does_not_break_sender_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    test.call(\"set_player_state\", &[\"42\", \"1\"]).unwrap();\n\n    // Two connections subscribe to the same view.\n    let sub_keep = test.subscribe_background(&[\"SELECT * FROM player\"], 2).unwrap();\n    let sub_drop = test.subscribe_background(&[\"SELECT * FROM player\"], 1).unwrap();\n\n    // Both connections should receive the first update.\n    // After one connection disconnects, the other should still receive updates.\n    test.call(\"set_player_state\", &[\"42\", \"2\"]).unwrap();\n    let _ = sub_drop.collect().unwrap();\n    test.call(\"set_player_state\", &[\"42\", \"3\"]).unwrap();\n\n    let events = sub_keep.collect().unwrap();\n\n    assert_eq!(events.len(), 2, \"Expected two updates for player, got: {events:?}\");\n    let inserts = events[1][\"player\"][\"inserts\"]\n        .as_array()\n        .expect(\"Expected inserts array on player update\");\n    assert!(\n        inserts\n            .iter()\n            .any(|row| row[\"id\"] == json!(42) && row[\"level\"] == json!(3)),\n        \"Expected player id=42 level=3 insert after disconnect, got: {events:?}\"\n    );\n}\n\n#[test]\nfn test_disconnect_does_not_break_anonymous_view() {\n    let test = Smoketest::builder().precompiled_module(\"views-sql\").build();\n\n    // Seed a row in the anonymous-view source table.\n    test.call(\"add_player_level\", &[\"0\", \"2\"]).unwrap();\n\n    // Two connections subscribe to the same anonymous view.\n    let sub_keep = test\n        .subscribe_background(&[\"SELECT * FROM player_and_level\"], 2)\n        .unwrap();\n    let sub_drop = test\n        .subscribe_background(&[\"SELECT * FROM player_and_level\"], 1)\n        .unwrap();\n\n    // Both connections should receive the first update.\n    // After one connection disconnects, the other should still receive updates.\n    test.call(\"add_player_level\", &[\"1\", \"2\"]).unwrap();\n    let _ = sub_drop.collect().unwrap();\n    test.call(\"add_player_level\", &[\"2\", \"2\"]).unwrap();\n\n    let events = sub_keep.collect().unwrap();\n\n    assert_eq!(\n        events.len(),\n        2,\n        \"Expected two updates for player_and_level, got: {events:?}\"\n    );\n    let inserts = events[1][\"player_and_level\"][\"inserts\"]\n        .as_array()\n        .expect(\"Expected inserts array on player_and_level update\");\n    assert!(\n        inserts\n            .iter()\n            .any(|row| row[\"id\"] == json!(2) && row[\"level\"] == json!(2)),\n        \"Expected player id=2 level=2 insert after disconnect, got: {events:?}\"\n    );\n}\n\n#[test]\nfn test_typescript_query_builder_view_query() {\n    require_pnpm!();\n    let mut test = Smoketest::builder().autopublish(false).build();\n    test.publish_typescript_module_source(\n        \"views-subscribe-typescript\",\n        \"views-subscribe-typescript\",\n        TS_VIEWS_SUBSCRIBE_MODULE,\n    )\n    .unwrap();\n\n    test.call(\"insert_player_proc\", &[\"Alice\"]).unwrap();\n\n    test.assert_sql(\n        \"SELECT name FROM online_players\",\n        r#\" name\n---------\n \"Alice\"\"#,\n    );\n}\n\n#[test]\nfn test_csharp_query_builder_view_query() {\n    require_dotnet!();\n    let mut test = Smoketest::builder().autopublish(false).build();\n    test.publish_csharp_module_source(\"views-csharp\", \"views-csharp\", CS_VIEWS_QUERY_BUILDER_MODULE)\n        .unwrap();\n\n    test.call(\"insert_value\", &[\"0\", \"false\"]).unwrap();\n    test.call(\"insert_value\", &[\"1\", \"true\"]).unwrap();\n    test.call(\"insert_value\", &[\"2\", \"false\"]).unwrap();\n\n    test.assert_sql(\n        \"SELECT * FROM some\",\n        r#\" value | alive\n-------+-------\n 1     | true\"#,\n    );\n}\n\nenum PkJoinMutation {\n    UpdateLhs { id: u64, ok: bool },\n    UpdateRhs { id: u64, ok: bool },\n    DeleteLhs { id: u64 },\n}\n\nfn apply_pk_join_mutation(test: &Smoketest, mutation: &PkJoinMutation) {\n    match mutation {\n        PkJoinMutation::UpdateLhs { id, ok } => {\n            let id = id.to_string();\n            let ok = if *ok { \"true\" } else { \"false\" };\n            test.call(\"update_pk_join_lhs\", &[id.as_str(), ok]).unwrap();\n        }\n        PkJoinMutation::UpdateRhs { id, ok } => {\n            let id = id.to_string();\n            let ok = if *ok { \"true\" } else { \"false\" };\n            test.call(\"update_pk_join_rhs\", &[id.as_str(), ok]).unwrap();\n        }\n        PkJoinMutation::DeleteLhs { id } => {\n            let id = id.to_string();\n            test.call(\"delete_pk_join_lhs\", &[id.as_str()]).unwrap();\n        }\n    }\n}\n\nfn expected_pk_join_projection(view_name: &str) -> Value {\n    let mk_event = |deletes: Vec<Value>, inserts: Vec<Value>| {\n        let mut event_payload = serde_json::Map::new();\n        event_payload.insert(\"deletes\".to_string(), Value::Array(deletes));\n        event_payload.insert(\"inserts\".to_string(), Value::Array(inserts));\n\n        let mut event = serde_json::Map::new();\n        event.insert(view_name.to_string(), Value::Object(event_payload));\n        Value::Object(event)\n    };\n\n    Value::Array(vec![\n        mk_event(vec![], vec![json!({\"id\": 1, \"ok\": true})]),\n        mk_event(vec![json!({\"id\": 1, \"ok\": true})], vec![]),\n        mk_event(vec![], vec![json!({\"id\": 2, \"ok\": true})]),\n        mk_event(vec![json!({\"id\": 2, \"ok\": true})], vec![]),\n    ])\n}\n\nfn run_pk_join_subscription_test(query: &str, projected_view_name: &str, mutations: &[PkJoinMutation]) {\n    let test = Smoketest::builder().precompiled_module(\"views-query\").build();\n\n    // Seed rows for identity A in both underlying tables.\n    test.call(\"update_pk_join_lhs\", &[\"200\", \"true\"]).unwrap();\n    test.call(\"update_pk_join_rhs\", &[\"200\", \"true\"]).unwrap();\n\n    // Switch to identity B so each underlying table has rows for 2 identities.\n    test.new_identity().unwrap();\n\n    let sub = test.subscribe_background(&[query], 4).unwrap();\n\n    for mutation in mutations {\n        apply_pk_join_mutation(&test, mutation);\n    }\n\n    let update_events = sub.collect().unwrap();\n    assert_eq!(\n        serde_json::json!(project_fields(update_events, projected_view_name, &[\"id\", \"ok\"])),\n        expected_pk_join_projection(projected_view_name)\n    );\n}\n\n#[test]\nfn test_subscribe_join_pk_views_with_filters_on_both_sides() {\n    let query = \"SELECT pk_join_lhs_sender_view.* \\\n                 FROM pk_join_lhs_sender_view \\\n                 JOIN pk_join_rhs_sender_view \\\n                 ON pk_join_lhs_sender_view.id = pk_join_rhs_sender_view.id \\\n                 WHERE pk_join_lhs_sender_view.ok = true \\\n                 AND pk_join_rhs_sender_view.ok = true\";\n\n    run_pk_join_subscription_test(\n        query,\n        \"pk_join_lhs_sender_view\",\n        &[\n            PkJoinMutation::UpdateLhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: false },\n            PkJoinMutation::UpdateRhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: false },\n        ],\n    );\n}\n\n#[test]\nfn test_subscribe_join_anon_pk_views_with_filters_on_both_sides() {\n    let query = \"SELECT pk_join_lhs_view.* \\\n                 FROM pk_join_lhs_view \\\n                 JOIN pk_join_rhs_view \\\n                 ON pk_join_lhs_view.id = pk_join_rhs_view.id \\\n                 WHERE pk_join_lhs_view.ok = true \\\n                 AND pk_join_rhs_view.ok = true\";\n\n    run_pk_join_subscription_test(\n        query,\n        \"pk_join_lhs_view\",\n        &[\n            PkJoinMutation::UpdateLhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: false },\n            PkJoinMutation::UpdateRhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: false },\n        ],\n    );\n}\n\n#[test]\nfn test_subscribe_join_anon_pk_view_with_table_and_filter() {\n    let query = \"SELECT pk_join_lhs_view.* \\\n                 FROM pk_join_lhs_view \\\n                 JOIN pk_join_rhs \\\n                 ON pk_join_lhs_view.id = pk_join_rhs.id \\\n                 WHERE pk_join_lhs_view.ok = true \\\n                 AND pk_join_rhs.identity = :sender\";\n\n    run_pk_join_subscription_test(\n        query,\n        \"pk_join_lhs_view\",\n        &[\n            PkJoinMutation::UpdateLhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: true },\n            PkJoinMutation::UpdateLhs { id: 1, ok: false },\n            PkJoinMutation::UpdateRhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: false },\n        ],\n    );\n}\n\n#[test]\nfn test_subscribe_join_anon_pk_view_with_table() {\n    let query = \"SELECT pk_join_lhs_view.* \\\n                 FROM pk_join_lhs_view \\\n                 JOIN pk_join_rhs \\\n                 ON pk_join_lhs_view.id = pk_join_rhs.id \\\n                 WHERE pk_join_rhs.identity = :sender\";\n\n    run_pk_join_subscription_test(\n        query,\n        \"pk_join_lhs_view\",\n        &[\n            PkJoinMutation::UpdateLhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: true },\n            PkJoinMutation::DeleteLhs { id: 1 },\n            PkJoinMutation::UpdateRhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: true },\n            PkJoinMutation::DeleteLhs { id: 2 },\n        ],\n    );\n}\n\n#[test]\nfn test_subscribe_join_pk_view_with_table() {\n    let query = \"SELECT pk_join_lhs_sender_view.* \\\n                 FROM pk_join_lhs_sender_view \\\n                 JOIN pk_join_rhs \\\n                 ON pk_join_lhs_sender_view.id = pk_join_rhs.id\";\n\n    run_pk_join_subscription_test(\n        query,\n        \"pk_join_lhs_sender_view\",\n        &[\n            PkJoinMutation::UpdateLhs { id: 1, ok: true },\n            PkJoinMutation::UpdateRhs { id: 1, ok: true },\n            PkJoinMutation::DeleteLhs { id: 1 },\n            PkJoinMutation::UpdateRhs { id: 2, ok: true },\n            PkJoinMutation::UpdateLhs { id: 2, ok: true },\n            PkJoinMutation::DeleteLhs { id: 2 },\n        ],\n    );\n}\n"
  },
  {
    "path": "crates/snapshot/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-snapshot\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"Low-level interfaces for capturing and restoring snapshots of database states\"\n\n[dependencies]\nspacetimedb-table.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-durability.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-sats = { workspace = true, features = [\"blake3\"] }\nspacetimedb-primitives.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-fs-utils.workspace = true\n\nblake3.workspace = true\nbytes.workspace = true\ncrossbeam-queue.workspace = true\nfutures.workspace = true\nhex.workspace = true\nlog.workspace = true\nscopeguard.workspace = true\ntempfile.workspace = true\nthiserror.workspace = true\ntokio = { workspace = true, features = [\"io-util\"] }\ntokio-stream.workspace = true\ntokio-util = { workspace = true, features = [\"io\"] }\nzstd-framed.workspace = true\n\n[dev-dependencies]\nspacetimedb-core = { path = \"../core\", features = [\"test\"] }\nspacetimedb-schema = { path = \"../schema\" }\nspacetimedb-datastore = { path = \"../datastore\", features = [\"test\"] }\nspacetimedb-durability = { workspace = true, features = [\"test\"] }\n\nanyhow.workspace = true\nenv_logger.workspace = true\npretty_assertions = { workspace = true, features = [\"unstable\"] }\nrand.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/snapshot/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/snapshot/src/lib.rs",
    "content": "//! This crate implements capturing and restoring snapshots in SpacetimeDB.\n//!\n//! A snapshot is an on-disk view of the committed state of a database at a particular transaction offset.\n//! Snapshots exist as an optimization over replaying the commitlog;\n//! when restoring to the most recent transaction, rather than replaying the commitlog from 0,\n//! we can reload the most recent snapshot, then replay only the suffix of the commitlog.\n//!\n//! This crate is responsible for:\n//! - The on-disk format of snapshots.\n//! - A [`SnapshotRepository`] which contains multiple snapshots of a DB and can create and retrieve them.\n//! - Creating a snapshot given a view of a DB's committed state in [`SnapshotRepository::create_snapshot`].\n//! - Reading an on-disk snapshot into memory as a [`ReconstructedSnapshot`] in [`SnapshotRepository::read_snapshot`].\n//!   The [`ReconstructedSnapshot`] can then be installed into a datastore.\n//! - Locating the most-recent snapshot of a DB, or the most recent snapshot not newer than a given tx offset,\n//!   in [`SnapshotRepository::latest_snapshot`] and [`SnapshotRepository::latest_snapshot_older_than`].\n//!\n//! This crate *is not* responsible for:\n//! - Determining when to capture snapshots.\n//! - Deciding which snapshot to restore from after a restart.\n//! - Replaying the suffix of the commitlog after restoring a snapshot.\n//! - Transforming a [`ReconstructedSnapshot`] into a live Spacetime datastore.\n// TODO(docs): consider making the snapshot proposal public and either linking or pasting it here.\n\n#![allow(clippy::result_large_err)]\n\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashMap};\nuse spacetimedb_durability::TxOffset;\nuse spacetimedb_fs_utils::compression::{\n    compress_with_zstd, CompressCount, CompressReader, CompressType, CompressionAlgorithm,\n};\nuse spacetimedb_fs_utils::{\n    dir_trie::{o_excl, o_rdonly, CountCreated, DirTrie},\n    lockfile::{Lockfile, LockfileError},\n};\nuse spacetimedb_lib::Identity;\nuse spacetimedb_paths::server::{ArchivedSnapshotDirPath, SnapshotDirPath, SnapshotFilePath, SnapshotsPath};\nuse spacetimedb_paths::FromPathUnchecked;\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::{bsatn, de::Deserialize, ser::Serialize};\nuse spacetimedb_table::{\n    blob_store::{BlobHash, BlobStore, HashMapBlobStore},\n    page::Page,\n    page_pool::PagePool,\n    table::Table,\n};\nuse std::fs;\nuse std::ops::RangeBounds;\nuse std::time::{Duration, Instant};\nuse std::{\n    collections::BTreeMap,\n    ffi::OsStr,\n    fmt,\n    io::{BufWriter, Read, Write},\n    ops::{Add, AddAssign},\n    path::PathBuf,\n};\nuse tokio::task::spawn_blocking;\n\npub mod remote;\nuse remote::verify_snapshot;\n\n#[derive(Debug, Copy, Clone)]\n/// An object which may be associated with an error during snapshotting.\npub enum ObjectType {\n    Blob(BlobHash),\n    Page(blake3::Hash),\n    Snapshot,\n}\n\nimpl std::fmt::Display for ObjectType {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        match *self {\n            ObjectType::Blob(hash) => write!(f, \"blob {hash:x?}\"),\n            ObjectType::Page(hash) => write!(f, \"page {hash:x?}\"),\n            ObjectType::Snapshot => write!(f, \"snapshot\"),\n        }\n    }\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum SnapshotError {\n    #[error(\"Cannot open SnapshotRepo {0}: not an accessible directory\")]\n    Open(PathBuf),\n    #[error(\"Failed to write {ty} to {dest_repo:?}, attempting to hardlink link from {source_repo:?}: {cause}\")]\n    WriteObject {\n        ty: ObjectType,\n        dest_repo: PathBuf,\n        source_repo: Option<PathBuf>,\n        #[source]\n        cause: std::io::Error,\n    },\n    #[error(\"Failed to read {ty} from {source_repo:?}: {cause}\")]\n    ReadObject {\n        ty: ObjectType,\n        source_repo: PathBuf,\n        #[source]\n        cause: std::io::Error,\n    },\n    #[error(\"Encountered corrupted {ty} in {source_repo:?}: expected hash {expected:x?}, but computed {computed:x?}\")]\n    HashMismatch {\n        ty: ObjectType,\n        expected: [u8; 32],\n        computed: [u8; 32],\n        source_repo: PathBuf,\n    },\n    #[error(\"Failed to BSATN serialize {ty}: {cause}\")]\n    Serialize {\n        ty: ObjectType,\n        #[source]\n        cause: bsatn::ser::BsatnError,\n    },\n    #[error(\"Failed to BSATN deserialize {ty} from {source_repo:?}: {cause}\")]\n    Deserialize {\n        ty: ObjectType,\n        source_repo: PathBuf,\n        cause: bsatn::DecodeError,\n    },\n    #[error(\"Refusing to reconstruct incomplete snapshot {tx_offset}: lockfile {lockfile:?} exists\")]\n    Incomplete { tx_offset: TxOffset, lockfile: PathBuf },\n    #[error(\"Refusing to reconstruct snapshot {tx_offset} with bad magic number {magic:x?}\")]\n    BadMagic { tx_offset: TxOffset, magic: [u8; 4] },\n    #[error(\"Refusing to reconstruct snapshot {tx_offset} with unsupported version {version}\")]\n    BadVersion { tx_offset: TxOffset, version: u8 },\n    #[error(\"Cannot open snapshot repository in non-directory {root:?}\")]\n    NotDirectory { root: SnapshotsPath },\n    #[error(transparent)]\n    Lockfile(#[from] LockfileError),\n    #[error(transparent)]\n    Io(#[from] std::io::Error),\n}\n\nimpl SnapshotError {\n    pub fn is_already_exists(&self) -> bool {\n        matches!(self, Self::Io(e) if e.kind() == std::io::ErrorKind::AlreadyExists)\n    }\n}\n\n/// Magic number for snapshot files: a point in spacetime.\n///\n/// Chosen because the commitlog magic number is a spacetime interval,\n/// so a snapshot should be a point to which an interval can be applied.\npub const MAGIC: [u8; 4] = *b\"txyz\";\n\n/// Snapshot format version number.\npub const CURRENT_SNAPSHOT_VERSION: u8 = 0;\n\n/// ABI version of the module from which this snapshot was created, as [MAJOR, MINOR].\npub const CURRENT_MODULE_ABI_VERSION: [u16; 2] = [7, 0];\n\n/// File extension of snapshot directories.\npub const SNAPSHOT_DIR_EXT: &str = \"snapshot_dir\";\n\n/// File extension of snapshot files, which contain BSATN-encoded [`Snapshot`]s preceded by [`blake3::Hash`]es.\npub const SNAPSHOT_FILE_EXT: &str = \"snapshot_bsatn\";\n\n/// File extension of snapshots which have been marked invalid by [`SnapshotRepository::invalidate_newer_snapshots`].\npub const INVALID_SNAPSHOT_DIR_EXT: &str = \"invalid_snapshot\";\n\n/// File extension of snapshots which have been archived\npub const ARCHIVED_SNAPSHOT_EXT: &str = \"archived_snapshot\";\n\n#[derive(Clone, Serialize, Deserialize)]\n/// The hash and refcount of a single blob in the blob store.\nstruct BlobEntry {\n    hash: BlobHash,\n    uses: u32,\n}\n\n#[derive(Clone, Serialize, Deserialize)]\n/// A snapshot of a single table, containing the hashes of all its resident pages.\nstruct TableEntry {\n    table_id: TableId,\n    pages: Vec<blake3::Hash>,\n}\n\n#[derive(Clone, Serialize, Deserialize)]\npub struct Snapshot {\n    /// A magic number: must be equal to [`MAGIC`].\n    magic: [u8; 4],\n\n    /// The snapshot version number. Must be equal to [`CURRENT_SNAPSHOT_VERSION`].\n    version: u8,\n\n    /// The identity of the snapshotted database.\n    pub database_identity: Identity,\n    /// The instance ID of the snapshotted database.\n    pub replica_id: u64,\n\n    /// ABI version of the module from which this snapshot was created, as [MAJOR, MINOR].\n    ///\n    /// As of this proposal, [7, 0].\n    module_abi_version: [u16; 2],\n\n    /// The transaction offset of the state this snapshot reflects.\n    pub tx_offset: TxOffset,\n\n    /// The hashes and reference counts of all objects in the blob store.\n    blobs: Vec<BlobEntry>,\n\n    /// For each table, its table ID followed by the hashes of all resident pages.\n    ///\n    /// It's necessary to store the table ID rather than relying on order\n    /// because table IDs may be sparse (and will be, once we reserve a bunch of system table ids).\n    tables: Vec<TableEntry>,\n}\n\nimpl Snapshot {\n    /// Insert a single large blob from a [`BlobStore`]\n    /// into the in-memory snapshot `self`\n    /// and the on-disk object repository `object_repo`.\n    ///\n    /// If the `prev_snapshot` is supplied, this method will attempt to hardlink the blob's on-disk object\n    /// from that previous snapshot into `object_repo` rather than creating a fresh object.\n    fn write_blob(\n        &mut self,\n        object_repo: &DirTrie,\n        hash: &BlobHash,\n        uses: usize,\n        blob: &[u8],\n        prev_snapshot: Option<&DirTrie>,\n        counter: &mut CountCreated,\n    ) -> Result<(), SnapshotError> {\n        object_repo\n            .hardlink_or_write(prev_snapshot, &hash.data, || blob, counter)\n            .map_err(|cause| SnapshotError::WriteObject {\n                ty: ObjectType::Blob(*hash),\n                dest_repo: object_repo.root().to_path_buf(),\n                source_repo: prev_snapshot.map(|dest_repo| dest_repo.root().to_path_buf()),\n                cause,\n            })?;\n        self.blobs.push(BlobEntry {\n            hash: *hash,\n            uses: uses as u32,\n        });\n        Ok(())\n    }\n\n    /// Populate the in-memory snapshot `self`,\n    /// and the on-disk object repository `object_repo`,\n    /// with all large blobs from `blobs`.\n    fn write_all_blobs(\n        &mut self,\n        object_repo: &DirTrie,\n        blobs: &dyn BlobStore,\n        prev_snapshot: Option<&DirTrie>,\n        counter: &mut CountCreated,\n    ) -> Result<(), SnapshotError> {\n        for (hash, uses, blob) in blobs.iter_blobs() {\n            self.write_blob(object_repo, hash, uses, blob, prev_snapshot, counter)?;\n        }\n        Ok(())\n    }\n\n    /// Write a single `page` into the on-disk object repository `object_repo`.\n    ///\n    /// `hash` must be the content hash of `page`, and must be stored in `page.unmodified_hash()`.\n    ///\n    /// Returns the `hash` for convenient use with [`Iter::map`] in [`Self::write_table`].\n    ///\n    /// If the `prev_snapshot` is supplied, this function will attempt to hardlink the page's on-disk object\n    /// from that previous snapshot into `object_repo` rather than creating a fresh object.\n    fn write_page(\n        object_repo: &DirTrie,\n        page: &Page,\n        hash: blake3::Hash,\n        prev_snapshot: Option<&DirTrie>,\n        counter: &mut CountCreated,\n    ) -> Result<blake3::Hash, SnapshotError> {\n        debug_assert!(page.unmodified_hash().copied() == Some(hash));\n\n        object_repo\n            .hardlink_or_write(prev_snapshot, hash.as_bytes(), || bsatn::to_vec(page).unwrap(), counter)\n            .map_err(|cause| SnapshotError::WriteObject {\n                ty: ObjectType::Page(hash),\n                dest_repo: object_repo.root().to_path_buf(),\n                source_repo: prev_snapshot.map(|source_repo| source_repo.root().to_path_buf()),\n                cause,\n            })?;\n\n        Ok(hash)\n    }\n\n    /// Populate the in-memory snapshot `self`,\n    /// and the on-disk object repository `object_repo`,\n    /// with all pages from `table`.\n    fn write_table(\n        &mut self,\n        object_repo: &DirTrie,\n        table: &mut Table,\n        prev_snapshot: Option<&DirTrie>,\n        counter: &mut CountCreated,\n    ) -> Result<(), SnapshotError> {\n        let pages = table\n            .iter_pages_with_hashes()\n            .map(|(hash, page)| Self::write_page(object_repo, page, hash, prev_snapshot, counter))\n            .collect::<Result<Vec<blake3::Hash>, SnapshotError>>()?;\n\n        self.tables.push(TableEntry {\n            table_id: table.schema.table_id,\n            pages,\n        });\n        Ok(())\n    }\n\n    /// Populate the in-memory snapshot `self`,\n    /// and the on-disk object repository `object_repo`,\n    /// with all pages from all tables in `tables`.\n    fn write_all_tables<'db>(\n        &mut self,\n        object_repo: &DirTrie,\n        tables: impl Iterator<Item = &'db mut Table>,\n        prev_snapshot: Option<&DirTrie>,\n        counter: &mut CountCreated,\n    ) -> Result<(), SnapshotError> {\n        for table in tables {\n            self.write_table(object_repo, table, prev_snapshot, counter)?;\n        }\n        Ok(())\n    }\n\n    /// Read a [`Snapshot`] from the file at `path`, verify its hash, and return it.\n    ///\n    /// **NOTE**: It detects if the file was compressed or not.\n    ///\n    /// Fails if:\n    /// - `path` does not refer to a readable file.\n    /// - Fails to check if is compressed or not.\n    /// - The file at `path` is corrupted,\n    ///   as detected by comparing the hash of its bytes to a hash recorded in the file.\n    pub fn read_from_file(path: &SnapshotFilePath) -> Result<(Self, CompressType), SnapshotError> {\n        let err_read_object = |cause| SnapshotError::ReadObject {\n            ty: ObjectType::Snapshot,\n            source_repo: path.0.clone(),\n            cause,\n        };\n        let snapshot_file = path.open_file(&o_rdonly()).map_err(err_read_object)?;\n        let mut snapshot_file = CompressReader::new(snapshot_file)?;\n\n        // The snapshot file is prefixed with the hash of the `Snapshot`'s BSATN.\n        // Read that hash.\n        let mut hash = [0; blake3::OUT_LEN];\n        snapshot_file.read_exact(&mut hash).map_err(err_read_object)?;\n        let hash = blake3::Hash::from_bytes(hash);\n\n        // Read the `Snapshot`'s BSATN and compute its hash.\n        let mut snapshot_bsatn = vec![];\n        snapshot_file\n            .read_to_end(&mut snapshot_bsatn)\n            .map_err(err_read_object)?;\n        let computed_hash = blake3::hash(&snapshot_bsatn);\n\n        // Compare the saved and computed hashes, and fail if they do not match.\n        if hash != computed_hash {\n            return Err(SnapshotError::HashMismatch {\n                ty: ObjectType::Snapshot,\n                expected: *hash.as_bytes(),\n                computed: *computed_hash.as_bytes(),\n                source_repo: path.0.clone(),\n            });\n        }\n\n        let snapshot = bsatn::from_slice::<Snapshot>(&snapshot_bsatn).map_err(|cause| SnapshotError::Deserialize {\n            ty: ObjectType::Snapshot,\n            source_repo: path.0.clone(),\n            cause,\n        })?;\n\n        Ok((snapshot, snapshot_file.compress_type()))\n    }\n\n    /// Construct a [`HashMapBlobStore`] containing all the blobs referenced in `self`,\n    /// reading their data from files in the `object_repo`.\n    ///\n    /// Fails if any of the object files is missing or corrupted,\n    /// as detected by comparing the hash of its bytes to the hash recorded in `self`.\n    fn reconstruct_blob_store(&self, object_repo: &DirTrie) -> Result<HashMapBlobStore, SnapshotError> {\n        let mut blob_store = HashMapBlobStore::default();\n\n        for BlobEntry { hash, uses } in &self.blobs {\n            // Read the bytes of the blob object.\n            let buf = object_repo\n                .read_entry(&hash.data)\n                .map_err(|cause| SnapshotError::ReadObject {\n                    ty: ObjectType::Blob(*hash),\n                    source_repo: object_repo.root().to_path_buf(),\n                    cause,\n                })?;\n\n            // Compute the blob's hash.\n            let computed_hash = BlobHash::hash_from_bytes(&buf);\n\n            // Compare the computed hash to the one recorded in the `Snapshot`,\n            // and fail if they do not match.\n            if *hash != computed_hash {\n                return Err(SnapshotError::HashMismatch {\n                    ty: ObjectType::Blob(*hash),\n                    expected: hash.data,\n                    computed: computed_hash.data,\n                    source_repo: object_repo.root().to_path_buf(),\n                });\n            }\n\n            blob_store.insert_with_uses(hash, *uses as usize, buf.into_boxed_slice());\n        }\n\n        Ok(blob_store)\n    }\n\n    /// Read all the pages referenced by `pages` from the `object_repo`.\n    ///\n    /// Fails if any of the pages files is missing or corrupted,\n    /// as detected by comparing the hash of its bytes to the hash listed in `pages`.\n    fn reconstruct_one_table_pages(\n        object_repo: &DirTrie,\n        pages: &[blake3::Hash],\n        page_pool: &PagePool,\n    ) -> Result<Vec<Box<Page>>, SnapshotError> {\n        pages\n            .iter()\n            .map(|hash| {\n                // Read the BSATN bytes of the on-disk page object.\n                let buf = object_repo\n                    .read_entry(hash.as_bytes())\n                    .map_err(|cause| SnapshotError::ReadObject {\n                        ty: ObjectType::Page(*hash),\n                        source_repo: object_repo.root().to_path_buf(),\n                        cause,\n                    })?;\n\n                // Deserialize the bytes into a `Page`.\n                let page = page_pool.take_deserialize_from(&buf);\n                let page = page.map_err(|cause| SnapshotError::Deserialize {\n                    ty: ObjectType::Page(*hash),\n                    source_repo: object_repo.root().to_path_buf(),\n                    cause,\n                })?;\n\n                // Compute the hash of the page.\n                let computed_hash = page.content_hash();\n\n                // Compare the computed hash to the one recorded in the `Snapshot`,\n                // and fail if they do not match.\n                if *hash != computed_hash {\n                    return Err(SnapshotError::HashMismatch {\n                        ty: ObjectType::Page(*hash),\n                        expected: *hash.as_bytes(),\n                        computed: *computed_hash.as_bytes(),\n                        source_repo: object_repo.root().to_path_buf(),\n                    });\n                }\n\n                Ok::<Box<Page>, SnapshotError>(page)\n            })\n            .collect()\n    }\n\n    fn reconstruct_one_table(\n        object_repo: &DirTrie,\n        TableEntry { table_id, pages }: &TableEntry,\n        page_pool: &PagePool,\n    ) -> Result<(TableId, Vec<Box<Page>>), SnapshotError> {\n        Ok((\n            *table_id,\n            Self::reconstruct_one_table_pages(object_repo, pages, page_pool)?,\n        ))\n    }\n\n    /// Reconstruct all the table data from `self`,\n    /// reading pages from files in the `object_repo`.\n    ///\n    /// This method cannot construct [`Table`] objects\n    /// because doing so requires knowledge of the system tables' schemas\n    /// to compute the schemas of the user-defined tables\n    ///\n    /// Fails if any object file referenced in `self` (as a page or large blob)\n    /// is missing or corrupted,\n    /// as detected by comparing the hash of its bytes to the hash recorded in `self`.\n    fn reconstruct_tables(\n        &self,\n        object_repo: &DirTrie,\n        page_pool: &PagePool,\n    ) -> Result<BTreeMap<TableId, Vec<Box<Page>>>, SnapshotError> {\n        self.tables\n            .iter()\n            .map(|tbl| Self::reconstruct_one_table(object_repo, tbl, page_pool))\n            .collect()\n    }\n\n    /// The number of objects in this snapshot, both blobs and pages.\n    pub fn total_objects(&self) -> usize {\n        self.blobs.len() + self.tables.iter().map(|table| table.pages.len()).sum::<usize>()\n    }\n\n    /// Obtain an iterator over the [`blake3::Hash`]es of all objects\n    /// this snapshot is referring to.\n    pub fn objects(&self) -> impl Iterator<Item = blake3::Hash> + '_ {\n        self.blobs\n            .iter()\n            .map(|b| blake3::Hash::from_bytes(b.hash.data))\n            .chain(self.tables.iter().flat_map(|t| t.pages.iter().copied()))\n    }\n\n    /// Obtain an iterator over the [`PathBuf`]s of all objects\n    pub fn files<'a>(&'a self, src_repo: &'a DirTrie) -> impl Iterator<Item = (blake3::Hash, PathBuf)> + 'a {\n        self.objects().map(move |hash| {\n            let path = src_repo.file_path(hash.as_bytes());\n            (hash, path)\n        })\n    }\n}\n\n/// Collect the size of the snapshot and the number of objects in it.\n#[derive(Clone, Default)]\npub struct SnapshotSize {\n    /// How many snapshots are in the snapshot directory, and what `CompressType` they are.\n    pub snapshot: CompressCount,\n    /// The size of the snapshot file in `bytes`.\n    pub file_size: u64,\n    /// The size of the snapshot's objects in `bytes`.\n    pub object_size: u64,\n    /// The number of objects in the snapshot.\n    pub object_count: u64,\n    /// Total size of the snapshot in `bytes`, `file_size + object_size`.\n    pub total_size: u64,\n}\n\nimpl Add for SnapshotSize {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        Self {\n            snapshot: CompressCount {\n                none: self.snapshot.none + rhs.snapshot.none,\n                zstd: self.snapshot.zstd + rhs.snapshot.zstd,\n            },\n            file_size: self.file_size + rhs.file_size,\n            object_size: self.object_size + rhs.object_size,\n            object_count: self.object_count + rhs.object_count,\n            total_size: self.total_size + rhs.total_size,\n        }\n    }\n}\n\nimpl AddAssign for SnapshotSize {\n    fn add_assign(&mut self, rhs: Self) {\n        *self = self.clone() + rhs;\n    }\n}\n\nimpl fmt::Debug for SnapshotSize {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"SnapshotSize\")\n            .field(\"snapshot       \", &self.snapshot)\n            .field(\"object_count   \", &self.object_count)\n            .field(\"file_size      \", &format_args!(\"{:>8} bytes\", self.file_size))\n            .field(\"object_size    \", &format_args!(\"{:>8} bytes\", self.object_size))\n            .field(\"total_size     \", &format_args!(\"{:>8} bytes\", self.total_size))\n            .finish()\n    }\n}\n\n/// Number of objects compressed or hardlinked.\n#[derive(Clone, Copy, Debug, Default)]\npub struct ObjectCompressionStats {\n    /// Number of objects freshly compressed.\n    pub compressed: usize,\n    /// Number of objects hardlinked from a parent repository.\n    pub hardlinked: usize,\n}\n\nimpl ObjectCompressionStats {\n    fn is_zero(&self) -> bool {\n        self.compressed == 0 && self.hardlinked == 0\n    }\n\n    pub fn reset(&mut self) {\n        *self = Self::default();\n    }\n}\n\nimpl AddAssign for ObjectCompressionStats {\n    fn add_assign(&mut self, Self { compressed, hardlinked }: Self) {\n        self.compressed += compressed;\n        self.hardlinked += hardlinked;\n    }\n}\n\n/// Information about the progress of [compress_snapshots].\n///\n/// [compress_snapshots]: SnapshotRepository::compress_snapshots\n#[derive(Default)]\npub struct CompressionStats {\n    /// Incremented for each snapshot in the supplied range that\n    /// is found to be already compressed.\n    pub skipped: usize,\n    /// Times to compress individual snapshots.\n    ///\n    /// Timings are only recorded for snapshots that were actually compressed\n    /// during the [compress_snapshots] pass, not the `skipped` ones.\n    ///\n    /// That is, `compressed.len() + skipped` is the total number of visited\n    /// snapshots during the pass.\n    ///\n    /// [compress_snapshots]: SnapshotRepository::compress_snapshots\n    pub compression_timings: Vec<Duration>,\n    /// The cumulative [ObjectCompressionStats] for all snapshots visited.\n    pub objects: ObjectCompressionStats,\n    /// The offset of the latest snapshot in the supplied range found to be\n    /// compressed.\n    ///\n    /// Note that the snapshot may have been compressed already, or was\n    /// compressed during the current [compress_snapshots] pass.\n    ///\n    /// If no snapshot was visited during the run, the value is left unchanged.\n    ///\n    /// [compress_snapshots]: SnapshotRepository::compress_snapshots\n    pub last_compressed: Option<TxOffset>,\n}\n\nimpl CompressionStats {\n    /// Number of snapshots that were compressed (as opposed to `skipped`).\n    pub fn compressed(&self) -> usize {\n        self.compression_timings.len()\n    }\n}\n\n/// A repository of snapshots of a particular database instance.\n#[derive(Clone)]\npub struct SnapshotRepository {\n    /// The directory which contains all the snapshots.\n    root: SnapshotsPath,\n\n    /// The database identity of the database instance for which this repository stores snapshots.\n    database_identity: Identity,\n\n    /// The database instance ID of the database instance for which this repository stores snapshots.\n    replica_id: u64,\n    // TODO(deduplication): track the most recent successful snapshot\n    // (possibly in a file)\n    // and hardlink its objects into the next snapshot for deduplication.\n}\n\nimpl SnapshotRepository {\n    /// Returns the [`Identity`] of the database this [`SnapshotRepository`] is configured to snapshot.\n    pub fn database_identity(&self) -> Identity {\n        self.database_identity\n    }\n\n    /// Capture a snapshot of the state of the database at `tx_offset`,\n    /// where `tables` is the committed state of all the tables in the database,\n    /// and `blobs` is the committed state's blob store.\n    ///\n    /// Returns the path of the newly-created snapshot directory.\n    ///\n    /// **NOTE**: The current snapshot is uncompressed to avoid the potential slowdown.\n    pub fn create_snapshot<'db>(\n        &self,\n        tables: impl Iterator<Item = &'db mut Table>,\n        blobs: &'db dyn BlobStore,\n        tx_offset: TxOffset,\n    ) -> Result<SnapshotDirPath, SnapshotError> {\n        // Invalidate equal to or newer than `tx_offset`.\n        //\n        // This is because snapshots don't currently track the epoch in which\n        // they were created:\n        //\n        // Say, for example, a snapshot was created at offset 10, then a leader\n        // failover causes the commitlog to be reset to offset 9. The next\n        // transaction (also offset 10) will trigger snapshot creation, but we'd\n        // mistake the existing snapshot (now invalid) as the previous snapshot.\n        self.invalidate_newer_snapshots(tx_offset.saturating_sub(1))?;\n\n        // If a previous snapshot exists in this snapshot repo,\n        // get a handle on its object repo in order to hardlink shared objects into the new snapshot.\n        let prev_snapshot = self.latest_snapshot()?.map(|offset| self.snapshot_dir_path(offset));\n\n        let prev_snapshot = if let Some(prev_snapshot) = prev_snapshot {\n            assert!(\n                prev_snapshot.0.is_dir(),\n                \"prev_snapshot {prev_snapshot:?} is not a directory\"\n            );\n            let object_repo = Self::object_repo(&prev_snapshot)?;\n            Some(object_repo)\n        } else {\n            None\n        };\n\n        let mut counter = CountCreated::default();\n\n        let snapshot_dir = self.snapshot_dir_path(tx_offset);\n\n        // Before performing any observable operations,\n        // acquire a lockfile on the snapshot you want to create.\n        // Because we could be compressing the snapshot.\n        let _lock = Lockfile::for_file(&snapshot_dir)?;\n\n        // Create the snapshot directory.\n        snapshot_dir.create()?;\n\n        // Create a new `DirTrie` to hold all the content-addressed objects in the snapshot.\n        let object_repo = Self::object_repo(&snapshot_dir)?;\n\n        // Build the in-memory `Snapshot` object.\n        let mut snapshot = self.empty_snapshot(tx_offset);\n\n        // Populate both the in-memory `Snapshot` object and the on-disk object repository\n        // with all the blobs and pages.\n        snapshot.write_all_blobs(&object_repo, blobs, prev_snapshot.as_ref(), &mut counter)?;\n        snapshot.write_all_tables(&object_repo, tables, prev_snapshot.as_ref(), &mut counter)?;\n\n        self.write_snapshot_file(&snapshot_dir, snapshot)?;\n\n        log::info!(\n            \"[{}] SNAPSHOT {:0>20}: Hardlinked {} objects and wrote {} objects\",\n            self.database_identity,\n            tx_offset,\n            counter.objects_hardlinked,\n            counter.objects_written,\n        );\n\n        // Success! return the directory of the newly-created snapshot.\n        // The lockfile will be dropped here.\n        Ok(snapshot_dir)\n    }\n\n    /// Write the on-disk snapshot file containing the BSATN-encoded `snapshot`\n    /// into `snapshot_dir`.\n    ///\n    /// It is not recommanded to call this method directly; prefer using `create_snapshot`.\n    /// It is exposed publicly to be used in very specific scenarios, like modifying an existing\n    /// snapshot.\n    pub fn write_snapshot_file(&self, snapshot_dir: &SnapshotDirPath, snapshot: Snapshot) -> Result<(), SnapshotError> {\n        // Serialize and hash the in-memory `Snapshot` object.\n        let snapshot_bsatn = bsatn::to_vec(&snapshot).map_err(|cause| SnapshotError::Serialize {\n            ty: ObjectType::Snapshot,\n            cause,\n        })?;\n        let hash = blake3::hash(&snapshot_bsatn);\n\n        // Create the snapshot file, containing first the hash, then the `Snapshot`.\n        {\n            let mut snapshot_file =\n                BufWriter::new(snapshot_dir.snapshot_file(snapshot.tx_offset).open_file(&o_excl())?);\n            snapshot_file.write_all(hash.as_bytes())?;\n            snapshot_file.write_all(&snapshot_bsatn)?;\n            snapshot_file.flush()?;\n        }\n\n        Ok(())\n    }\n\n    fn empty_snapshot(&self, tx_offset: TxOffset) -> Snapshot {\n        Snapshot {\n            magic: MAGIC,\n            version: CURRENT_SNAPSHOT_VERSION,\n            database_identity: self.database_identity,\n            replica_id: self.replica_id,\n            module_abi_version: CURRENT_MODULE_ABI_VERSION,\n            tx_offset,\n            blobs: vec![],\n            tables: vec![],\n        }\n    }\n\n    /// Get the path to the directory which would contain the snapshot of transaction `tx_offset`.\n    ///\n    /// The directory may not exist if no snapshot has been taken of `tx_offset`.\n    ///\n    /// The directory may exist but be locked or incomplete\n    /// if a file with the same name and the extension `.lock` exists.\n    /// In this case, callers should treat the snapshot as if it did not exist.\n    ///\n    /// Use `[Self::all_snapshots]` to get `tx_offsets` which will return valid extant paths.\n    /// `[Self::all_snapshots]` will never return a `tx_offset` for a locked or incomplete snapshot.\n    /// `[Self::all_snapshots]` does not validate the contents of snapshots,\n    /// so it may return a `tx_offset` whose snapshot is corrupted.\n    ///\n    /// Any mutations to any files contained in the returned directory\n    /// will likely corrupt the snapshot,\n    /// causing attempts to reconstruct it to fail.\n    pub fn snapshot_dir_path(&self, tx_offset: TxOffset) -> SnapshotDirPath {\n        self.root.snapshot_dir(tx_offset)\n    }\n\n    /// Given `snapshot_dir` as the result of [`SnapshotRepository::snapshot_dir_path`],\n    /// get the [`DirTrie`] which contains serialized objects (pages and large blobs)\n    /// referenced by the [`Snapshot`] contained in the [`SnapshotDirPath`].\n    ///\n    /// Consequences are unspecified if this method is called from outside this crate\n    /// on a non-existent, locked or incomplete `snapshot_dir`.\n    ///\n    /// Any mutations to the returned [`DirTrie`] or its contents\n    /// will likely render the snapshot corrupted,\n    /// causing future attempts to reconstruct it to fail.\n    pub fn object_repo(snapshot_dir: &SnapshotDirPath) -> Result<DirTrie, std::io::Error> {\n        DirTrie::open(snapshot_dir.objects().0)\n    }\n\n    /// Read a snapshot contained in self referring to `tx_offset`,\n    /// verify its hashes,\n    /// and parse it into an in-memory structure [`ReconstructedSnapshot`]\n    /// which can be used to build a `CommittedState`.\n    ///\n    /// This method cannot construct [`Table`] objects\n    /// because doing so requires knowledge of the system tables' schemas\n    /// to compute the schemas of the user-defined tables.\n    ///\n    /// Fails if:\n    /// - No snapshot exists in `self` for `tx_offset`.\n    /// - The snapshot is incomplete, as detected by its lockfile still existing.\n    /// - Any object file (page or large blob) referenced by the snapshot file\n    ///   is missing or corrupted,\n    ///   as detected by comparing the hash of its bytes to the hash recorded in the snapshot file.\n    /// - The snapshot file's magic number does not match [`MAGIC`].\n    /// - The snapshot file's version does not match [`CURRENT_SNAPSHOT_VERSION`].\n    ///\n    /// The following conditions are not detected or considered as errors:\n    /// - The snapshot file's database identity or instance ID do not match those in `self`.\n    /// - The snapshot file's module ABI version does not match [`CURRENT_MODULE_ABI_VERSION`].\n    /// - The snapshot file's recorded transaction offset does not match `tx_offset`.\n    ///\n    /// This means that callers must inspect the returned [`ReconstructedSnapshot`]\n    /// and verify that they can handle its contained database identity, instance ID, module ABI version and transaction offset.\n    pub fn read_snapshot(\n        &self,\n        tx_offset: TxOffset,\n        page_pool: &PagePool,\n    ) -> Result<ReconstructedSnapshot, SnapshotError> {\n        let snapshot_dir = self.snapshot_dir_path(tx_offset);\n        let lockfile = Lockfile::lock_path(&snapshot_dir);\n        if lockfile.try_exists()? {\n            return Err(SnapshotError::Incomplete { tx_offset, lockfile });\n        }\n\n        let snapshot_file_path = snapshot_dir.snapshot_file(tx_offset);\n        let (snapshot, compress_type) = Snapshot::read_from_file(&snapshot_file_path)?;\n\n        if snapshot.magic != MAGIC {\n            return Err(SnapshotError::BadMagic {\n                tx_offset,\n                magic: snapshot.magic,\n            });\n        }\n\n        if snapshot.version != CURRENT_SNAPSHOT_VERSION {\n            return Err(SnapshotError::BadVersion {\n                tx_offset,\n                version: snapshot.version,\n            });\n        }\n\n        let snapshot_dir = self.snapshot_dir_path(tx_offset);\n        let object_repo = Self::object_repo(&snapshot_dir)?;\n\n        let blob_store = snapshot.reconstruct_blob_store(&object_repo)?;\n\n        let tables = snapshot.reconstruct_tables(&object_repo, page_pool)?;\n\n        Ok(ReconstructedSnapshot {\n            database_identity: snapshot.database_identity,\n            replica_id: snapshot.replica_id,\n            tx_offset: snapshot.tx_offset,\n            module_abi_version: snapshot.module_abi_version,\n            blob_store,\n            tables,\n            compress_type,\n        })\n    }\n\n    /// Read the [`Snapshot`] metadata at `tx_offset` and verify the integrity\n    /// of all objects it refers to.\n    ///\n    /// Fails if:\n    ///\n    /// - No snapshot exists in `self` for `tx_offset`\n    /// - The snapshot is incomplete, as detected by its lockfile still existing.\n    /// - The snapshot file's magic number does not match [`MAGIC`].\n    /// - Any object file (page or large blob) referenced by the snapshot file\n    ///   is missing or corrupted.\n    ///\n    /// The following conditions are not detected or considered as errors:\n    ///\n    /// - The snapshot file's version does not match [`CURRENT_SNAPSHOT_VERSION`].\n    /// - The snapshot file's database identity or instance ID do not match\n    ///   those in `self`.\n    /// - The snapshot file's module ABI version does not match\n    ///   [`CURRENT_MODULE_ABI_VERSION`].\n    /// - The snapshot file's recorded transaction offset does not match\n    ///   `tx_offset`.\n    ///\n    /// Callers may want to inspect the returned [`Snapshot`] and ensure its\n    /// contents match their expectations.\n    pub async fn verify_snapshot(&self, tx_offset: TxOffset) -> Result<Snapshot, SnapshotError> {\n        let snapshot_dir = self.snapshot_dir_path(tx_offset);\n        let snapshot = spawn_blocking({\n            let snapshot_dir = snapshot_dir.clone();\n            move || {\n                let lockfile = Lockfile::lock_path(&snapshot_dir);\n                if lockfile.try_exists()? {\n                    return Err(SnapshotError::Incomplete { tx_offset, lockfile });\n                }\n\n                let snapshot_file_path = snapshot_dir.snapshot_file(tx_offset);\n                let (snapshot, _compress_type) = Snapshot::read_from_file(&snapshot_file_path)?;\n\n                if snapshot.magic != MAGIC {\n                    return Err(SnapshotError::BadMagic {\n                        tx_offset,\n                        magic: snapshot.magic,\n                    });\n                }\n                Ok(snapshot)\n            }\n        })\n        .await\n        .unwrap()?;\n        let object_repo = Self::object_repo(&snapshot_dir)?;\n        verify_snapshot(object_repo, self.root.clone(), snapshot.clone())\n            .await\n            .map(drop)?;\n        Ok(snapshot)\n    }\n\n    /// Open a repository at `root`, failing if the `root` doesn't exist or isn't a directory.\n    ///\n    /// Calls [`SnapshotsPath::is_dir`] and requires that the result is `true`.\n    /// See that method for more detailed preconditions on this function.\n    pub fn open(root: SnapshotsPath, database_identity: Identity, replica_id: u64) -> Result<Self, SnapshotError> {\n        if !root.is_dir() {\n            return Err(SnapshotError::NotDirectory { root });\n        }\n        Ok(Self {\n            root,\n            database_identity,\n            replica_id,\n        })\n    }\n\n    /// Return the `TxOffset` of the highest-offset complete snapshot in the repository\n    /// lower than or equal to `upper_bound`.\n    ///\n    /// When searching for a snapshot to restore,\n    /// we will pass the [`spacetimedb_durability::Durability::durable_tx_offset`]\n    /// as the `upper_bound` to ensure we don't lose TXes.\n    ///\n    /// Does not verify that the snapshot of the returned `TxOffset` is valid and uncorrupted,\n    /// so a subsequent [`Self::read_snapshot`] may fail.\n    pub fn latest_snapshot_older_than(&self, upper_bound: TxOffset) -> Result<Option<TxOffset>, SnapshotError> {\n        Ok(self\n            .all_snapshots()?\n            // Ignore `tx_offset`s greater than the current upper bound.\n            .filter(|tx_offset| *tx_offset <= upper_bound)\n            // Select the largest TxOffset.\n            .max())\n    }\n\n    pub fn all_snapshots(&self) -> Result<impl Iterator<Item = TxOffset> + use<>, SnapshotError> {\n        Ok(self\n            .root\n            // Item = Result<DirEntry>\n            .read_dir()?\n            // Item = DirEntry\n            .filter_map(Result::ok)\n            // Item = PathBuf\n            .map(|dirent| dirent.path())\n            // Ignore entries not shaped like snapshot directories.\n            .filter(|path| path.extension() == Some(OsStr::new(SNAPSHOT_DIR_EXT)))\n            // Ignore entries whose lockfile still exists.\n            .filter(|path| !Lockfile::lock_path(path).exists())\n            // Parse each entry's TxOffset from the file name; ignore unparsable.\n            // Also ignore if the snapshot file doesn't exists.\n            // This can happen on incomplete transfers, or if something went\n            // wrong during creation.\n            // Item = TxOffset\n            .filter_map(|path| {\n                let offset = TxOffset::from_str_radix(path.file_stem()?.to_str()?, 10).ok()?;\n                let snapshot_file = SnapshotDirPath::from_path_unchecked(path).snapshot_file(offset);\n                if !snapshot_file.0.exists() {\n                    None\n                } else {\n                    Some(offset)\n                }\n            }))\n    }\n\n    /// Return an interator of [`ArchivedSnapshotDirPath`] for all the archived snapshot directories on disk\n    pub fn all_archived_snapshots(\n        &self,\n    ) -> Result<impl Iterator<Item = ArchivedSnapshotDirPath> + use<>, SnapshotError> {\n        Ok(self\n            .root\n            // Item = Result<DirEntry>\n            .read_dir()?\n            // Item = DirEntry\n            .filter_map(Result::ok)\n            // Item = PathBuf\n            .map(|dirent| dirent.path())\n            // Ignore entries not shaped like snapshot directories.\n            .filter(|path| path.extension() == Some(OsStr::new(ARCHIVED_SNAPSHOT_EXT)))\n            // Item = ArchivedSnapshotDirPath\n            .map(ArchivedSnapshotDirPath::from_path_unchecked))\n    }\n\n    /// Delete an archived snapshot from disk\n    pub fn remove_archived_snapshot(path: &ArchivedSnapshotDirPath) -> Result<(), SnapshotError> {\n        fs::remove_dir_all(path).map_err(SnapshotError::Io)\n    }\n\n    /// Return the `TxOffset` of the highest-offset complete snapshot in the repository.\n    ///\n    /// Does not verify that the snapshot of the returned `TxOffset` is valid and uncorrupted,\n    /// so a subsequent [`Self::read_snapshot`] may fail.\n    pub fn latest_snapshot(&self) -> Result<Option<TxOffset>, SnapshotError> {\n        self.latest_snapshot_older_than(TxOffset::MAX)\n    }\n\n    /// Rename any snapshot newer than `upper_bound` with [`INVALID_SNAPSHOT_DIR_EXT`].\n    ///\n    /// When rebuilding a database, we will call this method\n    /// with the [`spacetimedb_durability::Durability::durable_tx_offset`] as the `upper_bound`\n    /// in order to prevent us from retaining snapshots which will be superseded by the new diverging history.\n    ///\n    /// It is also called when creating a new snapshot via [`Self::create_snapshot`]\n    /// in order to prevent a diverging snapshot from being used as its own parent.\n    ///\n    /// Does not invalidate snapshots which are locked.\n    ///\n    /// This may overwrite previously-invalidated snapshots.\n    ///\n    /// If this method returns an error, some snapshots may have been invalidated, but not all will have been.\n    pub fn invalidate_newer_snapshots(&self, upper_bound: TxOffset) -> Result<(), SnapshotError> {\n        let newer_snapshots = self\n            .all_snapshots()?\n            .filter(|tx_offset| *tx_offset > upper_bound)\n            // Collect to a vec to avoid iterator invalidation,\n            // as the subsequent `for` loop will mutate the directory.\n            .collect::<Vec<TxOffset>>();\n\n        for newer_snapshot in newer_snapshots {\n            let path = self.snapshot_dir_path(newer_snapshot);\n            log::info!(\"Renaming snapshot newer than {upper_bound} from {path:?} to {path:?}\");\n            path.rename_invalid()?;\n        }\n        Ok(())\n    }\n\n    /// Compress the `current` snapshot, unless it is already compressed.\n    ///\n    /// If a `parent` snapshot is given, its object repo will be used to\n    /// hardlink common objects and avoid re-compressing them:\n    ///\n    /// If an object in `current` is uncompressed, but exists in `parent` and\n    /// is compressed, a hardlink is created in `current`. Otherwise, the object\n    /// in `current` is compressed in place.\n    ///\n    /// The `parent`'s object repo is never modified.\n    ///\n    /// Returns [ObjectCompressionStats] with information about how many objects\n    /// were compressed and hardlinked, respectively.\n    fn compress_snapshot(\n        parent: Option<&(TxOffset, SnapshotDirPath)>,\n        current: &(TxOffset, SnapshotDirPath),\n    ) -> Result<ObjectCompressionStats, SnapshotError> {\n        let (tx_offset, snapshot_dir) = current;\n        let tx_offset = *tx_offset;\n        let snapshot_file = snapshot_dir.snapshot_file(tx_offset);\n        let (snapshot, compress_type) = Snapshot::read_from_file(&snapshot_file)?;\n\n        let mut stats = ObjectCompressionStats::default();\n        if let Some(algo) = compress_type.algorithm() {\n            log::debug!(\n                \"Snapshot {snapshot_dir:?} of replica {} is already compressed: {algo:?}\",\n                snapshot.replica_id\n            );\n            return Ok(stats);\n        }\n\n        let old = if let Some((tx_offset, snapshot_dir)) = parent {\n            let snapshot_file = snapshot_dir.snapshot_file(*tx_offset);\n            let (snapshot, _) = Snapshot::read_from_file(&snapshot_file)?;\n            let dir = SnapshotRepository::object_repo(snapshot_dir)?;\n            snapshot.files(&dir).collect()\n        } else {\n            HashMap::new()\n        };\n\n        // Replace the original file with the compressed one.\n        fn compress(\n            old: &HashMap<blake3::Hash, PathBuf>,\n            src: &PathBuf,\n            hash: Option<blake3::Hash>,\n            stats: Option<&mut ObjectCompressionStats>,\n        ) -> Result<(), SnapshotError> {\n            let read = CompressReader::new(o_rdonly().open(src)?)?;\n            if read.is_compressed() {\n                return Ok(()); // Already compressed\n            }\n            if let Some(hash) = hash\n                && let Some(old_path) = old.get(&hash)\n            {\n                let old_file = CompressReader::new(o_rdonly().open(old_path)?)?;\n                if old_file.is_compressed() {\n                    std::fs::hard_link(old_path, src.with_extension(\"_tmp\"))?;\n                    std::fs::rename(src.with_extension(\"_tmp\"), src)?;\n                    if let Some(stats) = stats {\n                        stats.hardlinked += 1;\n                    }\n                    return Ok(());\n                }\n            }\n\n            let dst = src.with_extension(\"_tmp\");\n            let mut write = BufWriter::new(o_excl().open(&dst)?);\n            // The default frame size compress better.\n            compress_with_zstd(read, &mut write, None)?;\n            std::fs::rename(dst, src)?;\n            if let Some(stats) = stats {\n                stats.compressed += 1;\n            }\n\n            Ok(())\n        }\n\n        let _lock = Lockfile::for_file(snapshot_dir)?;\n\n        log::info!(\n            \"Compressing snapshot {snapshot_dir:?} of replica {}\",\n            snapshot.replica_id\n        );\n\n        let dir = SnapshotRepository::object_repo(snapshot_dir)?;\n        for (hash, path) in snapshot.files(&dir) {\n            compress(&old, &path, Some(hash), Some(&mut stats)).inspect_err(|err| {\n                log::error!(\"Failed to compress object file {path:?}: {err}\");\n            })?;\n        }\n\n        // Compress the snapshot file last,\n        // which marks the whole snapshot as compressed.\n        //\n        // Don't update the stats for the snapshot file.\n        compress(&old, &snapshot_file.0, None, None).inspect_err(|err| {\n            log::error!(\"Failed to compress snapshot file {snapshot_file:?}: {err}\");\n        })?;\n\n        log::info!(\n            \"Compressed snapshot {snapshot_dir:?} of replica {}: {compress_type:?}\",\n            snapshot.replica_id\n        );\n        Ok(stats)\n    }\n\n    /// Attempt to compress all snapshots that fall into `range`, and record\n    /// the outcome in `stats`.\n    ///\n    /// The snapshots in `range` are traversed in ascending order.\n    /// If an error occurs, processing stops and the error is returned.\n    ///\n    /// See [CompressionStats] for how to interpret the results.\n    pub fn compress_snapshots(\n        &self,\n        stats: &mut CompressionStats,\n        range: impl RangeBounds<TxOffset>,\n    ) -> Result<(), SnapshotError> {\n        let mut snapshots = self\n            .all_snapshots()?\n            .filter(|offset| range.contains(offset))\n            .map(|offset| (offset, self.snapshot_dir_path(offset)))\n            .collect::<Vec<_>>();\n        snapshots.sort_by_key(|&(offset, _)| offset);\n\n        let mut previous = None;\n        for current in &snapshots {\n            let start = Instant::now();\n            let object_stats = Self::compress_snapshot(previous, current)?;\n            if object_stats.is_zero() {\n                stats.skipped += 1;\n            } else {\n                stats.compression_timings.push(start.elapsed());\n            }\n            stats.objects += object_stats;\n            stats.last_compressed = Some(current.0);\n            previous = Some(current);\n        }\n\n        Ok(())\n    }\n\n    /// Calculate the size of the snapshot repository in bytes.\n    pub fn size_on_disk(&self) -> Result<SnapshotSize, SnapshotError> {\n        let mut size = SnapshotSize::default();\n\n        for snapshot in self.all_snapshots()? {\n            size += self.size_on_disk_snapshot(snapshot)?;\n        }\n        Ok(size)\n    }\n\n    pub fn size_on_disk_snapshot(&self, offset: TxOffset) -> Result<SnapshotSize, SnapshotError> {\n        let mut size = SnapshotSize::default();\n\n        let snapshot_dir = self.snapshot_dir_path(offset);\n        let snapshot_file = snapshot_dir.snapshot_file(offset);\n        let snapshot_file_size = snapshot_file.metadata()?.len();\n\n        let (snapshot, compress_type) = Snapshot::read_from_file(&snapshot_file)?;\n\n        size.snapshot = match compress_type {\n            CompressType::None => CompressCount { none: 1, zstd: 0 },\n            CompressType::Algorithm(CompressionAlgorithm::Zstd) => CompressCount { none: 0, zstd: 1 },\n        };\n\n        size.file_size += snapshot_file_size;\n        size.total_size += snapshot_file_size;\n        let repo = Self::object_repo(&snapshot_dir)?;\n        for (_, f) in snapshot.files(&repo) {\n            let file_size = f.metadata()?.len();\n            size.object_size += file_size;\n            size.total_size += file_size;\n            size.object_count += 1;\n        }\n\n        Ok(size)\n    }\n}\n\npub struct ReconstructedSnapshot {\n    /// The identity of the snapshotted database.\n    pub database_identity: Identity,\n    /// The instance ID of the snapshotted database.\n    pub replica_id: u64,\n    /// The transaction offset of the state this snapshot reflects.\n    pub tx_offset: TxOffset,\n    /// ABI version of the module from which this snapshot was created, as [MAJOR, MINOR].\n    pub module_abi_version: [u16; 2],\n\n    /// The blob store of the snapshotted state.\n    pub blob_store: HashMapBlobStore,\n\n    /// All the tables from the snapshotted state, sans schema information and indexes.\n    ///\n    /// This includes the system tables,\n    /// so the schema of user-defined tables can be recovered\n    /// given knowledge of the schema of `st_table` and `st_column`.\n    pub tables: BTreeMap<TableId, Vec<Box<Page>>>,\n    /// If the snapshot was compressed or not.\n    pub compress_type: CompressType,\n}\n\n#[cfg(test)]\nmod tests {\n    use std::fs::OpenOptions;\n\n    use tempfile::tempdir;\n\n    use super::*;\n\n    #[test]\n    fn listing_ignores_if_snapshot_file_is_missing() -> anyhow::Result<()> {\n        let tmp = tempdir()?;\n\n        let root = SnapshotsPath::from_path_unchecked(tmp.path());\n        let repo = SnapshotRepository::open(root, Identity::ZERO, 42)?;\n        for i in 0..10 {\n            repo.snapshot_dir_path(i).create()?;\n        }\n        repo.snapshot_dir_path(5)\n            .snapshot_file(5)\n            .open_file(OpenOptions::new().write(true).create_new(true))\n            .map(drop)?;\n\n        assert_eq!(vec![5], repo.all_snapshots()?.collect::<Vec<_>>());\n\n        Ok(())\n    }\n\n    #[test]\n    fn listing_ignores_if_lockfile_exists() -> anyhow::Result<()> {\n        let tmp = tempdir()?;\n\n        let root = SnapshotsPath::from_path_unchecked(tmp.path());\n        let repo = SnapshotRepository::open(root, Identity::ZERO, 42)?;\n        for i in 0..10 {\n            let snapshot_dir = repo.snapshot_dir_path(i);\n            snapshot_dir.create()?;\n            snapshot_dir\n                .snapshot_file(i)\n                .open_file(OpenOptions::new().write(true).create_new(true))\n                .map(drop)?;\n        }\n        let _lock = Lockfile::for_file(repo.snapshot_dir_path(5))?;\n\n        let mut snapshots = repo.all_snapshots()?.collect::<Vec<_>>();\n        snapshots.sort();\n        assert_eq!(vec![0, 1, 2, 3, 4, 6, 7, 8, 9], snapshots);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/snapshot/src/remote.rs",
    "content": "use std::{\n    future::Future,\n    io, mem,\n    path::PathBuf,\n    pin::Pin,\n    sync::{\n        atomic::{AtomicU64, Ordering},\n        Arc,\n    },\n    task::{Context, Poll},\n};\n\nuse bytes::{Bytes, BytesMut};\nuse crossbeam_queue::ArrayQueue;\nuse futures::{stream, StreamExt as _, TryStreamExt as _};\nuse scopeguard::ScopeGuard;\nuse spacetimedb_fs_utils::{compression::ZSTD_MAGIC_BYTES, dir_trie::DirTrie, lockfile::Lockfile};\nuse spacetimedb_lib::bsatn;\nuse spacetimedb_paths::server::{SnapshotDirPath, SnapshotsPath};\nuse spacetimedb_sats::buffer::BufWriter as SatsWriter;\nuse spacetimedb_table::{blob_store::BlobHash, page::Page, page_pool::PagePool};\nuse tempfile::NamedTempFile;\nuse tokio::{\n    fs,\n    io::{AsyncBufRead, AsyncBufReadExt as _, AsyncWrite, AsyncWriteExt as _, BufReader, BufWriter},\n    sync::mpsc,\n    task::spawn_blocking,\n};\nuse tokio_stream::wrappers::UnboundedReceiverStream;\nuse tokio_util::io::{InspectReader, InspectWriter, StreamReader};\nuse zstd_framed::AsyncZstdReader;\n\nuse crate::{ObjectType, Snapshot, SnapshotError, SnapshotRepository};\n\npub type Result<T> = std::result::Result<T, SnapshotError>;\n\n/// A source of snapshot objects that can be obtained by `hash`.\npub trait BlobProvider: Send {\n    fn blob_reader(\n        &self,\n        hash: blake3::Hash,\n    ) -> impl Future<Output = io::Result<impl AsyncBufRead + Send + Unpin>> + Send;\n}\n\nimpl BlobProvider for DirTrie {\n    async fn blob_reader(&self, hash: blake3::Hash) -> io::Result<impl AsyncBufRead + Send + Unpin> {\n        fs::File::open(self.file_path(hash.as_bytes()))\n            .await\n            .map(BufReader::new)\n    }\n}\n\nimpl<F, Fut, R> BlobProvider for F\nwhere\n    F: Fn(blake3::Hash) -> Fut + Send + Sync,\n    Fut: Future<Output = io::Result<R>> + Send,\n    R: AsyncBufRead + Send + Unpin,\n{\n    fn blob_reader(\n        &self,\n        hash: blake3::Hash,\n    ) -> impl Future<Output = io::Result<impl AsyncBufRead + Send + Unpin>> + Send {\n        (self)(hash)\n    }\n}\n\nimpl<T: BlobProvider + Send + Sync> BlobProvider for Arc<T> {\n    fn blob_reader(\n        &self,\n        hash: blake3::Hash,\n    ) -> impl Future<Output = io::Result<impl AsyncBufRead + Send + Unpin>> + Send {\n        (**self).blob_reader(hash)\n    }\n}\n\n/// Counters tracking how [`synchronize_snapshot`] handled objects.\n#[derive(Clone, Copy, Default, Debug)]\npub struct Stats {\n    /// Number of new objects written to disk.\n    pub objects_written: u64,\n    /// Number of objects hardlinked to the previous snapshot.\n    pub objects_hardlinked: u64,\n    /// Number of objects skipped due to them already existing in the snapshot's\n    /// object repository.\n    pub objects_skipped: u64,\n}\n\nimpl From<StatsInner> for Stats {\n    fn from(inner: StatsInner) -> Self {\n        Self {\n            objects_written: inner.objects_written.load(Ordering::Relaxed),\n            objects_hardlinked: inner.objects_hardlinked.load(Ordering::Relaxed),\n            objects_skipped: inner.objects_skipped.load(Ordering::Relaxed),\n        }\n    }\n}\n\n/// Given [`Snapshot`] metadata, obtained separately, fetches all objects\n/// referenced from it from a remote source `provider`, and stores them in the\n/// local `snapshots_dir`.\n///\n/// The function tries to avoid work where possible. Namely:\n///\n/// - If there is a parent snapshot locally and an object can be found in its\n///   object store, the object is hardlinked to the existing object instead of\n///   being fetched.\n/// - If an object referenced from `snapshot` can be found in the object store\n///   of the corresponding local snapshot, the object is not fetched.\n///   **NOTE** that the hash of the existing local object is not verified.\n///\n/// It will, however, proceed if the snapshot file already exists at the target\n/// path and hashes to the same value as the given `snapshot`. This can be\n/// useful to \"repair\" snapshots transferred by other methods.\n///\n/// Fetched objects are verified against the hashes from the `snapshot`, before\n/// being moved into place in the object store.\n///\n/// If successful, the `snapshot` is written to the designated location in the\n/// `snapshots_dir`.\n///\n/// # Cancellation\n///\n/// The function is **not** cancel safe in the same way as [`spawn_blocking`]\n/// (which it makes use of internally) is not cancel safe.\n///\n/// It is, however, safe to retry a failed [`synchronize_snapshot`] run.\npub async fn synchronize_snapshot(\n    provider: impl BlobProvider + 'static,\n    snapshots_dir: SnapshotsPath,\n    snapshot: Snapshot,\n) -> Result<Stats> {\n    run_fetcher(provider, snapshots_dir, snapshot, false).await\n}\n\n/// Verifies the integrity of the objects referenced from [`Snapshot`],\n/// in constant memory.\n///\n/// Like [`synchronize_snapshot`], but doesn't modify the local storage.\n/// Usually, a local [`BlobProvider`] like [`DirTrie`] should be provided.\npub async fn verify_snapshot(\n    provider: impl BlobProvider + 'static,\n    snapshots_dir: SnapshotsPath,\n    snapshot: Snapshot,\n) -> Result<()> {\n    run_fetcher(provider, snapshots_dir, snapshot, true).await.map(drop)\n}\n\nasync fn run_fetcher(\n    provider: impl BlobProvider + 'static,\n    snapshots_dir: SnapshotsPath,\n    snapshot: Snapshot,\n    dry_run: bool,\n) -> Result<Stats> {\n    spawn_blocking(|| {\n        SnapshotFetcher::create(\n            provider,\n            snapshots_dir,\n            snapshot,\n            PagePool::new(Some(PAGE_POOL_SIZE)),\n            BufPool::new(BUF_POOL_SIZE),\n        )\n    })\n    .await\n    .unwrap()?\n    .run(dry_run)\n    .await\n}\n\n#[derive(Default)]\nstruct StatsInner {\n    objects_written: AtomicU64,\n    objects_hardlinked: AtomicU64,\n    objects_skipped: AtomicU64,\n}\n\nimpl StatsInner {\n    fn wrote_object(&self) {\n        Self::inc(&self.objects_written);\n    }\n\n    fn hardlinked_object(&self) {\n        Self::inc(&self.objects_hardlinked);\n    }\n\n    fn skipped_object(&self) {\n        Self::inc(&self.objects_skipped)\n    }\n\n    fn inc(counter: &AtomicU64) {\n        counter.fetch_add(1, Ordering::Relaxed);\n    }\n}\n\n/// Limits the number of futures that concurrently fetch and process objects.\n///\n/// Note that this applies to blobs and pages separately, so the total\n/// concurrency limit is `2*FETCH_CONCURRENCY`.\nconst FETCH_CONCURRENCY: usize = 8;\n/// Size of a [`Page`], in bytes.\nconst PAGE_SIZE: usize = size_of::<Page>(); // 64 KiB\n/// Max size of the [`PagePool`], in bytes.\n///\n/// We only ever retain at most `FETCH_CONCURRENCY` pages in memory at the same\n/// time, thus the required size of the pool is `FETCH_CONCURRENCY * PAGE_SIZE`.\nconst PAGE_POOL_SIZE: usize = FETCH_CONCURRENCY * PAGE_SIZE;\n/// Max size of the [`BufPool`], in number of buffers.\n///\n/// We use the pooled buffers to:\n///\n/// - hold raw, decompressed page data\n/// - \"tee\" compressed blob data to a hasher task\n/// - \"tee\" compressed page data to a decompressor task\n///\n/// Therefore, the maximum size we need is `3 * FETCH_CONCURRENCY`.\nconst BUF_POOL_SIZE: usize = 3 * FETCH_CONCURRENCY;\n\n/// Creates a [`PagePool`] suitable for a one-off [`SnapshotFetcher::run`].\n///\n/// When many fetchers are active in parallel, sharing a larger pool between\n/// them is likely beneficial.\npub fn default_page_pool() -> PagePool {\n    PagePool::new(Some(PAGE_POOL_SIZE))\n}\n\npub struct SnapshotFetcher<P> {\n    snapshot: Snapshot,\n    dir: SnapshotDirPath,\n    object_repo: Arc<DirTrie>,\n    parent_repo: Option<Arc<DirTrie>>,\n    provider: P,\n\n    /// Re-usable memory for deserialized pages.\n    page_pool: PagePool,\n    /// Re-usable memory for raw (un-deserialized) pages.\n    buf_pool: BufPool,\n\n    dry_run: bool,\n    stats: StatsInner,\n\n    // NOTE: This should remain the last declared field,\n    // so that the lock file is dropped last when `self` is dropped.\n    #[allow(unused)]\n    lock: Lockfile,\n}\n\nimpl<P: BlobProvider> SnapshotFetcher<P> {\n    pub fn create(\n        provider: P,\n        snapshots_dir: SnapshotsPath,\n        snapshot: Snapshot,\n        page_pool: PagePool,\n        buf_pool: BufPool,\n    ) -> Result<Self> {\n        let snapshot_repo = SnapshotRepository::open(snapshots_dir, snapshot.database_identity, snapshot.replica_id)?;\n        let snapshot_dir = snapshot_repo.snapshot_dir_path(snapshot.tx_offset);\n        let lock = Lockfile::for_file(&snapshot_dir)?;\n        std::fs::create_dir_all(&snapshot_dir)?;\n\n        let object_repo = SnapshotRepository::object_repo(&snapshot_dir)?;\n        let parent_offset = snapshot_repo.latest_snapshot_older_than(snapshot.tx_offset)?;\n        // The parent offset must always be smaller than `snapshot`'s offset,\n        // because we locked `snapshot_dir`, so this snapshot is not selected.\n        debug_assert!(\n            parent_offset.is_none() || parent_offset.is_some_and(|offset| offset < snapshot.tx_offset),\n            \"invalid parent offset\"\n        );\n        let parent_repo = parent_offset\n            .map(|offset| {\n                let path = snapshot_repo.snapshot_dir_path(offset);\n                SnapshotRepository::object_repo(&path)\n            })\n            .transpose()?;\n\n        Ok(Self {\n            snapshot,\n            dir: snapshot_dir,\n            object_repo: Arc::new(object_repo),\n            parent_repo: parent_repo.map(Arc::new),\n            provider,\n            page_pool,\n            buf_pool,\n            dry_run: false,\n            stats: <_>::default(),\n            lock,\n        })\n    }\n\n    /// Run the snapshot fetcher, returning [`Stats`] of what it did.\n    ///\n    /// If `dry_run` is `true`, no modifications will be made to the object\n    /// repository. This is useful for verifying the integrity of a snapshot.\n    pub async fn run(&mut self, dry_run: bool) -> Result<Stats> {\n        self.dry_run = dry_run;\n\n        let snapshot_bsatn = {\n            let mut buf = Vec::new();\n            serialize_snapshot(&mut buf, &self.snapshot)?;\n            buf\n        };\n        let snapshot_hash = blake3::hash(&snapshot_bsatn);\n        let snapshot_file_path = self.dir.snapshot_file(self.snapshot.tx_offset);\n        // If the snapshot file already exists at the target path,\n        // check that it is valid and that it hashes to `snapshot_hash`.\n        if fs::try_exists(&snapshot_file_path).await? {\n            let (existing, _) = spawn_blocking({\n                let snapshot_file_path = snapshot_file_path.clone();\n                move || Snapshot::read_from_file(&snapshot_file_path)\n            })\n            .await\n            .unwrap()?;\n            let existing_hash = {\n                let mut hasher = Hasher::default();\n                serialize_snapshot(&mut hasher, &existing)?;\n                hasher.hash()\n            };\n\n            if existing_hash != snapshot_hash {\n                return Err(SnapshotError::HashMismatch {\n                    ty: ObjectType::Snapshot,\n                    expected: *snapshot_hash.as_bytes(),\n                    computed: *existing_hash.as_bytes(),\n                    source_repo: snapshot_file_path.0.clone(),\n                });\n            }\n        }\n\n        // Get all the objects.\n        tokio::try_join!(self.fetch_blobs(), self.fetch_pages())?;\n\n        // Success. Write out the snapshot file.\n        atomically((!self.dry_run).then_some(snapshot_file_path.0), |out| async {\n            let mut out = BufWriter::new(out);\n            out.write_all(snapshot_hash.as_bytes()).await?;\n            out.write_all(&snapshot_bsatn).await?;\n            out.flush().await?;\n\n            Ok(out.into_inner())\n        })\n        .await?;\n\n        Ok(mem::take(&mut self.stats).into())\n    }\n\n    async fn fetch_blobs(&self) -> Result<()> {\n        let tasks = self\n            .snapshot\n            .blobs\n            .iter()\n            .map(|entry| {\n                let hash = blake3::Hash::from_bytes(entry.hash.data);\n                self.fetch_blob(hash)\n            })\n            .collect::<Box<[_]>>();\n        stream::iter(tasks)\n            .map(Ok)\n            .try_for_each_concurrent(FETCH_CONCURRENCY, |task| task)\n            .await\n    }\n\n    async fn fetch_pages(&self) -> Result<()> {\n        let tasks = self\n            .snapshot\n            .tables\n            .iter()\n            .flat_map(|entry| entry.pages.iter().copied().map(|hash| self.fetch_page(hash)))\n            .collect::<Box<[_]>>();\n        stream::iter(tasks)\n            .map(Ok)\n            .try_for_each_concurrent(FETCH_CONCURRENCY, |task| task)\n            .await\n    }\n\n    async fn fetch_blob(&self, hash: blake3::Hash) -> Result<()> {\n        let Some(dst_path) = self\n            .object_file_path(ObjectType::Blob(BlobHash { data: *hash.as_bytes() }))\n            .await?\n        else {\n            return Ok(());\n        };\n        atomically((!self.dry_run).then_some(dst_path), |out| async move {\n            let mut out = BufWriter::new(out);\n            let mut src = self.provider.blob_reader(hash).await?;\n            let compressed = src.fill_buf().await?.starts_with(&ZSTD_MAGIC_BYTES);\n\n            // Consume the blob reader,\n            // write its contents to `out`,\n            // and compute the content hash on the fly.\n            let mut hasher = Hasher::default();\n            let computed_hash = if !compressed {\n                // If the input is uncompressed, just update the hasher as we go.\n                let mut writer = InspectWriter::new(&mut out, |chunk| {\n                    hasher.update(chunk);\n                });\n                tokio::io::copy_buf(&mut src, &mut writer).await?;\n                writer.flush().await?;\n\n                hasher.hash()\n            } else {\n                // If the input is compressed, send a copy of all received\n                // chunks to a separate task that decompresses the stream and\n                // computes the hash from the decompressed bytes.\n                let (mut zstd, tx) = zstd_reader()?;\n                let decompressor = tokio::spawn(async move {\n                    tokio::io::copy_buf(&mut zstd, &mut hasher).await?;\n                    Ok::<_, io::Error>(hasher.hash())\n                });\n\n                let mut buf = self.buf_pool.get();\n                let mut src = InspectReader::new(src, |chunk| {\n                    buf.extend_from_slice(chunk);\n                    tx.send(buf.split().freeze()).ok();\n                });\n                tokio::io::copy(&mut src, &mut out).await?;\n                out.flush().await?;\n\n                drop(tx);\n                decompressor.await.unwrap()?\n            };\n            if computed_hash != hash {\n                return Err(SnapshotError::HashMismatch {\n                    ty: ObjectType::Blob(BlobHash { data: *hash.as_bytes() }),\n                    expected: *hash.as_bytes(),\n                    computed: *computed_hash.as_bytes(),\n                    source_repo: self.dir.0.clone(),\n                });\n            }\n\n            Ok(out.into_inner())\n        })\n        .await\n        .inspect(|()| {\n            self.stats.wrote_object();\n        })\n    }\n\n    async fn fetch_page(&self, hash: blake3::Hash) -> Result<()> {\n        let Some(dst_path) = self.object_file_path(ObjectType::Page(hash)).await? else {\n            return Ok(());\n        };\n        atomically((!self.dry_run).then_some(dst_path), |out| async {\n            let mut out = BufWriter::new(out);\n            let mut src = self.provider.blob_reader(hash).await?;\n            let compressed = src.fill_buf().await?.starts_with(&ZSTD_MAGIC_BYTES);\n\n            // To compute the page hash, we need to bsatn deserialize it.\n            // As bsatn doesn't support streaming deserialization yet,\n            // we need to keep a copy of the input bytes,\n            // while also writing them to `out`.\n            let page_bytes = if !compressed {\n                // If the input is uncompressed, just copy all bytes to a buffer.\n                let mut page_buf = self.buf_pool.get();\n                let mut writer = InspectWriter::new(&mut out, |chunk| {\n                    page_buf.extend_from_slice(chunk);\n                });\n                tokio::io::copy_buf(&mut src, &mut writer).await?;\n                writer.flush().await?;\n\n                page_buf.split().freeze()\n            } else {\n                // If the input is compressed, send all received chunks to a\n                // separate task that decompresses the stream and returns\n                // the uncompressed bytes.\n                let (mut zstd, tx) = zstd_reader()?;\n                let buf_pool = self.buf_pool.clone();\n                let decompressor = tokio::spawn(async move {\n                    let mut page_buf = buf_pool.get();\n                    tokio::io::copy_buf(&mut zstd, &mut AsyncBufWriter(&mut page_buf)).await?;\n                    Ok::<_, io::Error>(page_buf.split().freeze())\n                });\n\n                let mut buf = self.buf_pool.get();\n                let mut writer = InspectWriter::new(&mut out, |chunk| {\n                    buf.extend_from_slice(chunk);\n                    tx.send(buf.split().freeze()).ok();\n                });\n                tokio::io::copy_buf(&mut src, &mut writer).await?;\n                writer.flush().await?;\n\n                drop(tx);\n                decompressor.await.unwrap()?\n            };\n\n            self.verify_page(hash, &page_bytes)?;\n\n            Ok(out.into_inner())\n        })\n        .await\n        .inspect(|()| {\n            self.stats.wrote_object();\n        })\n    }\n\n    /// Get the path of object `hash` in the target object repo.\n    ///\n    /// Returns `None` if the file already exists, or\n    /// we have a parent repo, and the object exists there.\n    ///\n    /// In the latter case, a hardlink will be created.\n    /// `self.stats` is updated in either case.\n    ///\n    /// In dry-run mode, `Some(path)` is returned\n    /// if the file exists in either the target or the parent repo,\n    /// in order to force hash verification.\n    /// If it does not exist, an error is returned.\n    async fn object_file_path(&self, ty: ObjectType) -> Result<Option<PathBuf>> {\n        let hash = match ty {\n            ObjectType::Blob(hash) => blake3::Hash::from_bytes(hash.data),\n            ObjectType::Page(hash) => hash,\n            ObjectType::Snapshot => unreachable!(\"invalid argument\"),\n        };\n        let path = self.object_repo.file_path(hash.as_bytes());\n        if fs::try_exists(&path).await? {\n            if self.dry_run {\n                return Ok(Some(path));\n            }\n\n            self.stats.skipped_object();\n            return Ok(None);\n        }\n\n        if self.try_hardlink(hash).await? {\n            if self.dry_run {\n                return Ok(Some(path));\n            }\n\n            self.stats.hardlinked_object();\n            return Ok(None);\n        }\n\n        if self.dry_run {\n            return Err(SnapshotError::ReadObject {\n                ty,\n                source_repo: self.object_repo.root().to_owned(),\n                cause: io::Error::new(io::ErrorKind::NotFound, format!(\"missing object {}\", path.display())),\n            });\n        }\n\n        Ok(Some(path))\n    }\n\n    async fn try_hardlink(&self, hash: blake3::Hash) -> Result<bool> {\n        let Some(parent) = self.parent_repo.as_ref() else {\n            return Ok(false);\n        };\n\n        let object_repo = Arc::clone(&self.object_repo);\n        let parent_repo = Arc::clone(parent);\n        if !self.dry_run {\n            spawn_blocking(move || object_repo.try_hardlink_from(&parent_repo, hash.as_bytes()))\n                .await\n                .unwrap()\n                .map_err(Into::into)\n        } else {\n            let src_file = parent_repo.file_path(hash.as_bytes());\n            let meta = tokio::fs::metadata(src_file).await?;\n            Ok(meta.is_file())\n        }\n    }\n\n    fn verify_page(&self, expected_hash: blake3::Hash, buf: &[u8]) -> Result<()> {\n        let page = self\n            .page_pool\n            .take_deserialize_from(buf)\n            .map_err(|cause| SnapshotError::Deserialize {\n                ty: ObjectType::Page(expected_hash),\n                source_repo: self.dir.0.clone(),\n                cause,\n            })?;\n        let computed_hash = page.content_hash();\n        self.page_pool.put(page);\n\n        if computed_hash != expected_hash {\n            return Err(SnapshotError::HashMismatch {\n                ty: ObjectType::Blob(BlobHash {\n                    data: *expected_hash.as_bytes(),\n                }),\n                expected: *expected_hash.as_bytes(),\n                computed: *computed_hash.as_bytes(),\n                source_repo: self.dir.0.clone(),\n            });\n        }\n\n        Ok(())\n    }\n}\n\n/// Create an [`AsyncZstdReader`] that incrementally decompresses\n/// the data fed to it via the returned [`mpsc::UnboundedSender`].\n///\n/// The reader implements [`tokio::io::AsyncRead`] and will indicate EOF\n/// once the sender is dropped and all remaining data in the channel has been\n/// consumed.\nfn zstd_reader() -> io::Result<(\n    AsyncZstdReader<'static, impl AsyncBufRead>,\n    mpsc::UnboundedSender<Bytes>,\n)> {\n    let (tx, rx) = mpsc::unbounded_channel::<Bytes>();\n    let reader = StreamReader::new(UnboundedReceiverStream::new(rx).map(Ok::<_, io::Error>));\n    let zstd = AsyncZstdReader::builder_tokio(reader).build()?;\n\n    Ok((zstd, tx))\n}\n\n/// Newtype around [`blake3::Hasher`]\n/// that implements [`AsyncWrite`] and [`SatsWriter`].\n#[derive(Default)]\nstruct Hasher {\n    inner: blake3::Hasher,\n}\n\nimpl Hasher {\n    pub fn hash(&self) -> blake3::Hash {\n        self.inner.finalize()\n    }\n\n    pub fn update(&mut self, input: &[u8]) {\n        self.inner.update(input);\n    }\n}\n\nimpl AsyncWrite for Hasher {\n    fn poll_write(self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {\n        self.get_mut().update(buf);\n        Poll::Ready(Ok(buf.len()))\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n}\n\nimpl SatsWriter for Hasher {\n    fn put_slice(&mut self, slice: &[u8]) {\n        self.update(slice);\n    }\n}\n\n/// The [`AsyncWrite`] created by [`atomically`].\n///\n/// Either a temporary file that is being renamed atomically if and when the\n/// closure returns successfully,\n/// or a [`tokio::io::Sink`] that discards all data written to it (used for\n/// [`verify_snapshot`]).\nenum AtomicWriter {\n    File(fs::File),\n    Null(tokio::io::Sink),\n}\n\nimpl AtomicWriter {\n    async fn sync_all(&self) -> io::Result<()> {\n        if let Self::File(file) = self {\n            file.sync_all().await?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl AsyncWrite for AtomicWriter {\n    fn poll_write(\n        self: Pin<&mut Self>,\n        cx: &mut Context<'_>,\n        buf: &[u8],\n    ) -> Poll<std::result::Result<usize, io::Error>> {\n        match self.get_mut() {\n            Self::File(file) => Pin::new(file).poll_write(cx, buf),\n            Self::Null(sink) => Pin::new(sink).poll_write(cx, buf),\n        }\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::result::Result<(), io::Error>> {\n        match self.get_mut() {\n            Self::File(file) => Pin::new(file).poll_flush(cx),\n            Self::Null(sink) => Pin::new(sink).poll_flush(cx),\n        }\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::result::Result<(), io::Error>> {\n        match self.get_mut() {\n            Self::File(file) => Pin::new(file).poll_shutdown(cx),\n            Self::Null(sink) => Pin::new(sink).poll_shutdown(cx),\n        }\n    }\n}\n\nasync fn atomically<F, Fut>(file_path: Option<PathBuf>, f: F) -> Result<()>\nwhere\n    F: FnOnce(AtomicWriter) -> Fut,\n    Fut: Future<Output = Result<AtomicWriter>>,\n{\n    match file_path {\n        Some(file_path) => {\n            let dir = file_path.parent().expect(\"file not in a directory\").to_owned();\n            fs::create_dir_all(&dir).await?;\n            let (tmp_file, tmp_out) = spawn_blocking(move || {\n                let tmp = NamedTempFile::new_in(dir)?;\n                let out = tmp.reopen()?;\n                Ok::<_, io::Error>((tmp, out))\n            })\n            .await\n            .unwrap()?;\n\n            let mut file = AtomicWriter::File(fs::File::from_std(tmp_out));\n            file = f(file).await?;\n            file.sync_all().await?;\n\n            spawn_blocking(|| tmp_file.persist(file_path))\n                .await\n                .unwrap()\n                .map_err(|e| e.error)?;\n        }\n\n        None => {\n            f(AtomicWriter::Null(tokio::io::sink())).await?;\n        }\n    }\n\n    Ok(())\n}\n\nfn serialize_snapshot(w: &mut impl SatsWriter, value: &Snapshot) -> Result<()> {\n    bsatn::to_writer(w, value).map_err(|cause| SnapshotError::Serialize {\n        ty: ObjectType::Snapshot,\n        cause,\n    })\n}\n\n/// Pool of [`BytesMut`] buffers, each with page-sized capacity.\n///\n/// The [`Default`] impl creates a pool suitable a one-off [`SnapshotFetcher::run`].\n/// When many fetchers are active in parallel, sharing a larger pool between\n/// them is likely beneficial.\n#[derive(Clone)]\npub struct BufPool {\n    inner: Arc<ArrayQueue<BytesMut>>,\n}\n\nimpl Default for BufPool {\n    fn default() -> Self {\n        Self::new(BUF_POOL_SIZE)\n    }\n}\n\nimpl BufPool {\n    /// Creates a new pool capable of holding a maximum of `cap` buffers.\n    pub fn new(cap: usize) -> Self {\n        Self {\n            inner: Arc::new(ArrayQueue::new(cap)),\n        }\n    }\n\n    /// Get a buffer from the pool, or create a new one.\n    ///\n    /// The buffer is returned to the pool when the returned [`ScopeGuard`]\n    /// goes out of scope.\n    pub fn get(&self) -> ScopeGuard<BytesMut, impl FnOnce(BytesMut) + use<>> {\n        let this = self.clone();\n        scopeguard::guard(\n            this.inner.pop().unwrap_or_else(|| BytesMut::with_capacity(PAGE_SIZE)),\n            move |buf| this.put(buf),\n        )\n    }\n\n    /// Put `buf` back into the pool, or drop it if the pool is full.\n    pub fn put(&self, buf: BytesMut) {\n        let _ = self.inner.push(buf);\n    }\n}\n\n/// Makes a [`BytesMut`] [`AsyncWrite`].\nstruct AsyncBufWriter<'a>(&'a mut BytesMut);\n\nimpl AsyncWrite for AsyncBufWriter<'_> {\n    fn poll_write(self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {\n        self.get_mut().0.extend_from_slice(buf);\n        Poll::Ready(Ok(buf.len()))\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {\n        Poll::Ready(Ok(()))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{default_page_pool, BlobProvider, BufPool, SnapshotFetcher};\n    use crate::{Snapshot, SnapshotError, CURRENT_MODULE_ABI_VERSION, CURRENT_SNAPSHOT_VERSION, MAGIC};\n    use pretty_assertions::assert_matches;\n    use spacetimedb_lib::Identity;\n    use spacetimedb_paths::{server::SnapshotsPath, FromPathUnchecked};\n    use spacetimedb_sats::bsatn;\n    use spacetimedb_sats::layout::{row_size_for_type, Size};\n    use spacetimedb_table::{\n        blob_store::NullBlobStore, indexes::PageOffset, page::Page, var_len::AlignedVarLenOffsets,\n    };\n    use std::io::Cursor;\n    use tempfile::tempdir;\n    use zstd_framed::AsyncZstdWriter;\n\n    const ZEROES: &[u8] = &[0; 32];\n    const DUMMY_SNAPSHOT: Snapshot = Snapshot {\n        magic: MAGIC,\n        version: CURRENT_SNAPSHOT_VERSION,\n        database_identity: Identity::ZERO,\n        replica_id: 1,\n        module_abi_version: CURRENT_MODULE_ABI_VERSION,\n        tx_offset: 1_000_001,\n        blobs: vec![],\n        tables: vec![],\n    };\n\n    /// [`BlobProvider`] that serves only zeroes.\n    fn zeroes_provider() -> impl BlobProvider {\n        |_hash| async { Ok(Box::new(ZEROES)) }\n    }\n\n    /// [`BlobProvider`] that serves only the given `data`.\n    fn const_provider(data: Vec<u8>) -> impl BlobProvider {\n        move |_hash| {\n            let data = Cursor::new(data.clone());\n            async move { Ok(data) }\n        }\n    }\n\n    #[tokio::test]\n    async fn verifies_hash_of_uncompressed_blob() {\n        let tmp = tempdir().unwrap();\n        let dir = SnapshotsPath::from_path_unchecked(tmp.path());\n\n        let blob_hash = blake3::hash(&[1; 32]);\n        let sf = SnapshotFetcher::create(\n            zeroes_provider(),\n            dir,\n            DUMMY_SNAPSHOT,\n            default_page_pool(),\n            BufPool::default(),\n        )\n        .unwrap();\n\n        sf.fetch_blob(blake3::hash(ZEROES)).await.unwrap();\n        assert_matches!(sf.fetch_blob(blob_hash).await, Err(SnapshotError::HashMismatch { .. }));\n    }\n\n    #[tokio::test]\n    async fn verifies_hash_of_compressed_blob() {\n        let tmp = tempdir().unwrap();\n        let dir = SnapshotsPath::from_path_unchecked(tmp.path());\n\n        let blob_data = [1; 1024];\n        let blob_hash = blake3::hash(&blob_data);\n        let mut blob_zstd = Vec::new();\n        compress(&mut blob_data.as_slice(), &mut blob_zstd).await;\n\n        let sf = SnapshotFetcher::create(\n            const_provider(blob_zstd),\n            dir,\n            DUMMY_SNAPSHOT,\n            default_page_pool(),\n            BufPool::default(),\n        )\n        .unwrap();\n\n        sf.fetch_blob(blob_hash).await.unwrap();\n        assert_matches!(\n            sf.fetch_blob(blake3::hash(ZEROES)).await,\n            Err(SnapshotError::HashMismatch { .. })\n        );\n    }\n\n    #[tokio::test]\n    async fn verifies_hash_of_uncompressed_page() {\n        let tmp = tempdir().unwrap();\n        let dir = SnapshotsPath::from_path_unchecked(tmp.path());\n\n        let mut page = Page::new(u64_row_size());\n        for val in 0..64 {\n            insert_u64(&mut page, val);\n        }\n        let page_hash = page_hash_save_get(&mut page);\n        let page_blob = bsatn::to_vec(&page).unwrap();\n\n        let sf = SnapshotFetcher::create(\n            const_provider(page_blob),\n            dir,\n            DUMMY_SNAPSHOT,\n            default_page_pool(),\n            BufPool::default(),\n        )\n        .unwrap();\n\n        sf.fetch_page(page_hash).await.unwrap();\n        assert_matches!(\n            sf.fetch_page(blake3::hash(ZEROES)).await,\n            Err(SnapshotError::HashMismatch { .. })\n        );\n    }\n\n    #[tokio::test]\n    async fn verifies_hash_of_compressed_page() {\n        let tmp = tempdir().unwrap();\n        let dir = SnapshotsPath::from_path_unchecked(tmp.path());\n\n        let mut page = Page::new(u64_row_size());\n        for val in 0..64 {\n            insert_u64(&mut page, val);\n        }\n        let page_hash = page_hash_save_get(&mut page);\n        let page_blob = bsatn::to_vec(&page).unwrap();\n        let mut page_zstd = Vec::new();\n        compress(&mut page_blob.as_slice(), &mut page_zstd).await;\n\n        let sf = SnapshotFetcher::create(\n            const_provider(page_zstd),\n            dir,\n            DUMMY_SNAPSHOT,\n            default_page_pool(),\n            BufPool::default(),\n        )\n        .unwrap();\n\n        sf.fetch_page(page_hash).await.unwrap();\n        pretty_assertions::assert_matches!(\n            sf.fetch_page(blake3::hash(ZEROES)).await,\n            Err(SnapshotError::HashMismatch { .. })\n        );\n    }\n\n    async fn compress(input: &mut &[u8], output: &mut Vec<u8>) {\n        let mut zstd = AsyncZstdWriter::builder(output).with_seek_table(256).build().unwrap();\n        tokio::io::copy(input, &mut zstd).await.unwrap();\n    }\n\n    fn u64_row_size() -> Size {\n        let fixed_row_size = row_size_for_type::<u64>();\n        assert_eq!(fixed_row_size.len(), 8);\n        fixed_row_size\n    }\n\n    fn insert_u64(page: &mut Page, val: u64) -> PageOffset {\n        let val_slice = val.to_le_bytes();\n        unsafe { page.insert_row(&val_slice, &[] as &[&[u8]], u64_var_len_visitor(), &mut NullBlobStore) }\n            .expect(\"Failed to insert first row\")\n    }\n\n    const U64_VL_VISITOR: AlignedVarLenOffsets<'_> = AlignedVarLenOffsets::from_offsets(&[]);\n    fn u64_var_len_visitor() -> &'static AlignedVarLenOffsets<'static> {\n        &U64_VL_VISITOR\n    }\n\n    fn page_hash_save_get(page: &mut Page) -> blake3::Hash {\n        page.save_content_hash();\n        page.content_hash()\n    }\n}\n"
  },
  {
    "path": "crates/snapshot/tests/remote.rs",
    "content": "use std::{io, sync::Arc, time::Instant};\n\nuse env_logger::Env;\nuse log::info;\nuse pretty_assertions::assert_matches;\nuse rand::seq::IndexedRandom as _;\nuse spacetimedb::{\n    db::{\n        relational_db::{tests_utils::TestDB, Persistence, SNAPSHOT_FREQUENCY},\n        snapshot::{self, SnapshotWorker},\n    },\n    error::DBError,\n    Identity,\n};\nuse spacetimedb_datastore::execution_context::Workload;\nuse spacetimedb_datastore::locking_tx_datastore::datastore::Locking;\nuse spacetimedb_durability::{EmptyHistory, NoDurability, TxOffset};\nuse spacetimedb_fs_utils::dir_trie::DirTrie;\nuse spacetimedb_lib::{\n    bsatn,\n    db::raw_def::v9::{RawModuleDefV9Builder, RawTableDefBuilder},\n    AlgebraicType, ProductType,\n};\nuse spacetimedb_paths::{server::SnapshotsPath, FromPathUnchecked};\nuse spacetimedb_primitives::TableId;\nuse spacetimedb_sats::{product, raw_identifier::RawIdentifier};\nuse spacetimedb_schema::{\n    def::ModuleDef,\n    schema::{Schema as _, TableSchema},\n};\nuse spacetimedb_snapshot::{\n    remote::{synchronize_snapshot, verify_snapshot},\n    Snapshot, SnapshotError, SnapshotRepository,\n};\nuse spacetimedb_table::page_pool::PagePool;\nuse tempfile::{tempdir, TempDir};\nuse tokio::{sync::OnceCell, task::spawn_blocking};\n\n// TODO: Happy path for compressed snapshot, pending #2034\n#[tokio::test]\nasync fn can_sync_a_snapshot() -> anyhow::Result<()> {\n    enable_logging();\n    let tmp = tempdir()?;\n    let src = SourceSnapshot::get_or_create().await?;\n\n    let dst_path = SnapshotsPath::from_path_unchecked(tmp.path());\n    dst_path.create()?;\n\n    let dst_repo = SnapshotRepository::open(dst_path.clone(), Identity::ZERO, 0).map(Arc::new)?;\n\n    let mut src_snapshot = src.meta.clone();\n    let total_objects = src_snapshot.total_objects() as u64;\n\n    let blob_provider = src.objects.clone();\n\n    // This is the first snapshot in `dst_repo`, so all objects should be written.\n    let stats = synchronize_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n    assert_eq!(stats.objects_written, total_objects);\n\n    // Assert that the copied snapshot is valid.\n    let pool = PagePool::new_for_test();\n    let dst_snapshot_full = dst_repo.read_snapshot(src.offset, &pool)?;\n    Locking::restore_from_snapshot(dst_snapshot_full, pool)?;\n\n    // Let's also check that running `synchronize_snapshot` again does nothing.\n    let stats = synchronize_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n    assert_eq!(stats.objects_skipped, total_objects);\n\n    // Lastly, pretend the next snapshot has the same objects and\n    // assert that they all get hardlinked.\n    src_snapshot.tx_offset += SNAPSHOT_FREQUENCY;\n    let stats = synchronize_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n    assert_eq!(stats.objects_hardlinked, total_objects);\n\n    // Try again to ensure we skip all objects previously hardlinked.\n    let stats = synchronize_snapshot(blob_provider, dst_path, src_snapshot).await?;\n    assert_eq!(stats.objects_skipped, total_objects);\n\n    Ok(())\n}\n\n#[tokio::test]\nasync fn rejects_overwrite() -> anyhow::Result<()> {\n    enable_logging();\n    let tmp = tempdir()?;\n    let src = SourceSnapshot::get_or_create().await?;\n\n    let dst_path = SnapshotsPath::from_path_unchecked(tmp.path());\n    dst_path.create()?;\n\n    let src_snapshot = src.meta.clone();\n    let blob_provider = src.objects.clone();\n\n    synchronize_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n\n    // Try to overwrite with the previous snapshot.\n    // A previous snapshot exists because one is created immediately after\n    // database initialization.\n    let prev_offset = src.repo.latest_snapshot_older_than(src.offset - 1)?.unwrap();\n    let src_snapshot_path = src.repo.snapshot_dir_path(prev_offset);\n    let (mut src_snapshot, _) = Snapshot::read_from_file(&src_snapshot_path.snapshot_file(prev_offset))?;\n    // Pretend it's the current snapshot, thereby altering the preimage.\n    src_snapshot.tx_offset = src.offset;\n\n    let res = synchronize_snapshot(blob_provider, dst_path, src_snapshot).await;\n    assert_matches!(res, Err(SnapshotError::HashMismatch { .. }));\n\n    Ok(())\n}\n\n#[tokio::test]\nasync fn verifies_objects() -> anyhow::Result<()> {\n    enable_logging();\n    let tmp = tempdir()?;\n    let src = SourceSnapshot::get_or_create().await?;\n\n    let dst_path = SnapshotsPath::from_path_unchecked(tmp.path());\n    dst_path.create()?;\n\n    let src_snapshot = src.meta.clone();\n\n    synchronize_snapshot(src.objects.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n\n    // Read objects for verification from the destination repo.\n    let blob_provider = spawn_blocking({\n        let dst_path = dst_path.clone();\n        let snapshot_offset = src_snapshot.tx_offset;\n        move || {\n            let repo = SnapshotRepository::open(dst_path, Identity::ZERO, 0)?;\n            let objects = SnapshotRepository::object_repo(&repo.snapshot_dir_path(snapshot_offset))?;\n            anyhow::Ok(Arc::new(objects))\n        }\n    })\n    .await\n    .unwrap()?;\n    // Initially, all should be good.\n    verify_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone()).await?;\n\n    // Pick a random object to mess with.\n    let random_object_path = {\n        let all_objects = src_snapshot.objects().collect::<Box<[_]>>();\n        let random_object = all_objects.choose(&mut rand::rng()).copied().unwrap();\n        blob_provider.file_path(random_object.as_bytes())\n    };\n\n    // Truncate the object file and assert that verification fails.\n    tokio::fs::File::options()\n        .write(true)\n        .open(&random_object_path)\n        .await?\n        .set_len(1)\n        .await?;\n    info!(\"truncated object file {}\", random_object_path.display());\n    let err = verify_snapshot(blob_provider.clone(), dst_path.clone(), src_snapshot.clone())\n        .await\n        .unwrap_err();\n    assert_matches!(\n        err,\n        // If the object is a page, we'll get `Deserialize`,\n        // otherwise `HashMismatch`.\n        SnapshotError::HashMismatch { .. } | SnapshotError::Deserialize { .. }\n    );\n\n    // Delete the object file and assert that verification fails.\n    tokio::fs::remove_file(&random_object_path).await?;\n    info!(\"deleted object file {}\", random_object_path.display());\n    let err = verify_snapshot(blob_provider, dst_path, src_snapshot)\n        .await\n        .unwrap_err();\n    assert_matches!(err, SnapshotError::ReadObject { cause, ..} if cause.kind() == io::ErrorKind::NotFound);\n\n    Ok(())\n}\n\n/// Creating a snapshot takes a long time, because we need to commit\n/// `SNAPSHOT_FREQUENCY` transactions to trigger one.\n///\n/// Until the snapshot frequency becomes configurable,\n/// avoid creating the source snapshot repeatedly.\nstruct SourceSnapshot {\n    offset: TxOffset,\n    meta: Snapshot,\n    objects: Arc<DirTrie>,\n    repo: Arc<SnapshotRepository>,\n\n    #[allow(unused)]\n    tmp: TempDir,\n}\n\nimpl SourceSnapshot {\n    async fn get_or_create() -> anyhow::Result<&'static Self> {\n        static SOURCE_SNAPSHOT: OnceCell<SourceSnapshot> = OnceCell::const_new();\n        SOURCE_SNAPSHOT.get_or_try_init(Self::try_init).await\n    }\n\n    async fn try_init() -> anyhow::Result<Self> {\n        let tmp = tempdir()?;\n\n        let repo_path = SnapshotsPath::from_path_unchecked(tmp.path());\n        let repo = spawn_blocking(move || {\n            repo_path.create()?;\n            SnapshotRepository::open(repo_path, Identity::ZERO, 0).map(Arc::new)\n        })\n        .await\n        .unwrap()?;\n        let offset = create_snapshot(repo.clone()).await?;\n\n        let dir_path = repo.snapshot_dir_path(offset);\n        let (meta, objects) = spawn_blocking(move || {\n            let meta = Snapshot::read_from_file(&dir_path.snapshot_file(offset)).map(|(file, _)| file)?;\n            let objects = SnapshotRepository::object_repo(&dir_path).map(Arc::new)?;\n\n            Ok::<_, SnapshotError>((meta, objects))\n        })\n        .await\n        .unwrap()?;\n\n        Ok(SourceSnapshot {\n            offset,\n            meta,\n            objects,\n            repo,\n            tmp,\n        })\n    }\n}\n\nasync fn create_snapshot(repo: Arc<SnapshotRepository>) -> anyhow::Result<TxOffset> {\n    let start = Instant::now();\n    let rt = tokio::runtime::Handle::current();\n    // NOTE: `_db` needs to stay alive until the snapshot is taken,\n    // because the snapshot worker holds only a weak reference.\n    let (mut watch, _db) = spawn_blocking(|| {\n        let persistence = Persistence {\n            durability: Arc::new(NoDurability::default()),\n            disk_size: Arc::new(|| Ok(<_>::default())),\n            snapshots: Some(SnapshotWorker::new(repo, snapshot::Compression::Disabled)),\n            runtime: rt,\n        };\n        let db = TestDB::open_db(EmptyHistory::new(), Some(persistence), None, 0)?;\n        let watch = db.subscribe_to_snapshots().unwrap();\n\n        let table_id = db.with_auto_commit(Workload::Internal, |tx| {\n            db.create_table(\n                tx,\n                table(\"a\", ProductType::from([(\"x\", AlgebraicType::U64)]), |builder| builder),\n            )\n        })?;\n\n        for i in 0..SNAPSHOT_FREQUENCY {\n            db.with_auto_commit(Workload::Internal, |tx| {\n                db.insert(tx, table_id, &bsatn::to_vec(&product![i]).unwrap()).map(drop)\n            })?;\n        }\n\n        Ok::<_, DBError>((watch, db))\n    })\n    .await\n    .unwrap()?;\n\n    let mut snapshot_offset = *watch.borrow();\n    while snapshot_offset < SNAPSHOT_FREQUENCY && watch.changed().await.is_ok() {\n        snapshot_offset = *watch.borrow_and_update();\n    }\n    assert!(snapshot_offset >= SNAPSHOT_FREQUENCY);\n    info!(\n        \"snapshot creation took {}s\",\n        Instant::now().duration_since(start).as_secs_f32()\n    );\n\n    Ok(snapshot_offset)\n}\n\nfn table(\n    name: &str,\n    columns: ProductType,\n    f: impl FnOnce(RawTableDefBuilder<'_>) -> RawTableDefBuilder,\n) -> TableSchema {\n    let mut builder = RawModuleDefV9Builder::new();\n    f(builder.build_table_with_new_type(RawIdentifier::new(name), columns, true));\n    let raw = builder.finish();\n    let def: ModuleDef = raw.try_into().expect(\"table validation failed\");\n    let table = def.table(name).expect(\"table not found\");\n    TableSchema::from_module_def(&def, table, (), TableId::SENTINEL)\n}\n\nfn enable_logging() {\n    let _ = env_logger::Builder::from_env(Env::default().default_filter_or(\"info\"))\n        .format_timestamp(None)\n        .is_test(true)\n        .try_init();\n}\n"
  },
  {
    "path": "crates/sql-parser/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-sql-parser\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The SpacetimeDB SQL AST and Parser\"\n\n[dependencies]\nderive_more.workspace = true\nsqlparser.workspace = true\nthiserror.workspace = true\nspacetimedb-lib.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/sql-parser/src/ast/mod.rs",
    "content": "use spacetimedb_lib::sats::raw_identifier::RawIdentifier;\nuse spacetimedb_lib::Identity;\nuse sqlparser::ast::Ident;\nuse std::fmt::{Display, Formatter};\n\npub mod sql;\npub mod sub;\n\n/// The FROM clause is either a relvar or a JOIN\n#[derive(Debug)]\npub enum SqlFrom {\n    Expr(SqlIdent, SqlIdent),\n    Join(SqlIdent, SqlIdent, Vec<SqlJoin>),\n}\n\nimpl SqlFrom {\n    pub fn has_unqualified_vars(&self) -> bool {\n        match self {\n            Self::Join(_, _, joins) => joins.iter().any(|join| join.has_unqualified_vars()),\n            _ => false,\n        }\n    }\n}\n\n/// An inner join in a FROM clause\n#[derive(Debug)]\npub struct SqlJoin {\n    pub var: SqlIdent,\n    pub alias: SqlIdent,\n    pub on: Option<SqlExpr>,\n}\n\nimpl SqlJoin {\n    pub fn has_unqualified_vars(&self) -> bool {\n        self.on.as_ref().is_some_and(|expr| expr.has_unqualified_vars())\n    }\n}\n\n/// A projection expression in a SELECT clause\n#[derive(Debug)]\npub struct ProjectElem(pub ProjectExpr, pub SqlIdent);\n\nimpl ProjectElem {\n    pub fn qualify_vars(self, with: SqlIdent) -> Self {\n        let Self(expr, alias) = self;\n        Self(expr.qualify_vars(with), alias)\n    }\n}\n\n/// A column projection in a SELECT clause\n#[derive(Debug)]\npub enum ProjectExpr {\n    Var(SqlIdent),\n    Field(SqlIdent, SqlIdent),\n}\n\nimpl From<ProjectExpr> for SqlExpr {\n    fn from(value: ProjectExpr) -> Self {\n        match value {\n            ProjectExpr::Var(name) => Self::Var(name),\n            ProjectExpr::Field(table, field) => Self::Field(table, field),\n        }\n    }\n}\n\nimpl ProjectExpr {\n    pub fn qualify_vars(self, with: SqlIdent) -> Self {\n        match self {\n            Self::Var(name) => Self::Field(with, name),\n            Self::Field(_, _) => self,\n        }\n    }\n}\n\n/// A SQL SELECT clause\n#[derive(Debug)]\npub enum Project {\n    /// SELECT *\n    /// SELECT a.*\n    Star(Option<SqlIdent>),\n    /// SELECT a, b\n    Exprs(Vec<ProjectElem>),\n    /// SELECT COUNT(*)\n    Count(SqlIdent),\n}\n\nimpl Project {\n    pub fn qualify_vars(self, with: SqlIdent) -> Self {\n        match self {\n            Self::Star(..) | Self::Count(..) => self,\n            Self::Exprs(elems) => Self::Exprs(elems.into_iter().map(|elem| elem.qualify_vars(with.clone())).collect()),\n        }\n    }\n\n    pub fn has_unqualified_vars(&self) -> bool {\n        match self {\n            Self::Exprs(exprs) => exprs\n                .iter()\n                .any(|ProjectElem(expr, _)| matches!(expr, ProjectExpr::Var(_))),\n            _ => false,\n        }\n    }\n}\n\n/// A scalar SQL expression\n#[derive(Debug)]\npub enum SqlExpr {\n    /// A constant expression\n    Lit(SqlLiteral),\n    /// Unqualified column ref\n    Var(SqlIdent),\n    /// A parameter prefixed with `:`\n    Param(Parameter),\n    /// Qualified column ref\n    Field(SqlIdent, SqlIdent),\n    /// A binary infix expression\n    Bin(Box<SqlExpr>, Box<SqlExpr>, BinOp),\n    /// A binary logic expression\n    Log(Box<SqlExpr>, Box<SqlExpr>, LogOp),\n}\n\nimpl SqlExpr {\n    pub fn qualify_vars(self, with: SqlIdent) -> Self {\n        match self {\n            Self::Var(name) => Self::Field(with, name),\n            Self::Lit(..) | Self::Field(..) | Self::Param(..) => self,\n            Self::Bin(a, b, op) => Self::Bin(\n                Box::new(a.qualify_vars(with.clone())),\n                Box::new(b.qualify_vars(with)),\n                op,\n            ),\n            Self::Log(a, b, op) => Self::Log(\n                Box::new(a.qualify_vars(with.clone())),\n                Box::new(b.qualify_vars(with)),\n                op,\n            ),\n        }\n    }\n\n    pub fn has_unqualified_vars(&self) -> bool {\n        match self {\n            Self::Var(_) => true,\n            Self::Bin(a, b, _) | Self::Log(a, b, _) => a.has_unqualified_vars() || b.has_unqualified_vars(),\n            _ => false,\n        }\n    }\n\n    /// Is this AST parameterized?\n    /// We need to know in order to hash subscription queries correctly.\n    pub fn has_parameter(&self) -> bool {\n        match self {\n            Self::Lit(_) | Self::Var(_) | Self::Field(..) => false,\n            Self::Param(Parameter::Sender) => true,\n            Self::Bin(a, b, _) | Self::Log(a, b, _) => a.has_parameter() || b.has_parameter(),\n        }\n    }\n\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    pub fn resolve_sender(self, sender_identity: Identity) -> Self {\n        match self {\n            Self::Lit(_) | Self::Var(_) | Self::Field(..) => self,\n            Self::Param(Parameter::Sender) => {\n                Self::Lit(SqlLiteral::Hex(String::from(sender_identity.to_hex()).into_boxed_str()))\n            }\n\n            Self::Bin(a, b, op) => Self::Bin(\n                Box::new(a.resolve_sender(sender_identity)),\n                Box::new(b.resolve_sender(sender_identity)),\n                op,\n            ),\n            Self::Log(a, b, op) => Self::Log(\n                Box::new(a.resolve_sender(sender_identity)),\n                Box::new(b.resolve_sender(sender_identity)),\n                op,\n            ),\n        }\n    }\n}\n\n/// A named parameter prefixed with `:`\n#[derive(Debug)]\npub enum Parameter {\n    /// :sender\n    Sender,\n}\n\n/// A SQL identifier or named reference.\n/// Currently case sensitive.\n#[derive(Debug, Clone)]\npub struct SqlIdent(pub RawIdentifier);\n\n/// Case insensitivity should be implemented here if at all\nimpl From<Ident> for SqlIdent {\n    fn from(Ident { value, .. }: Ident) -> Self {\n        SqlIdent(RawIdentifier::new(value))\n    }\n}\n\n/// A SQL constant expression\n#[derive(Debug)]\npub enum SqlLiteral {\n    /// A boolean constant\n    Bool(bool),\n    /// A hex value like 0xFF or x'FF'\n    Hex(Box<str>),\n    /// An integer or float value\n    Num(Box<str>),\n    /// A string value\n    Str(Box<str>),\n}\n\n/// Binary infix operators\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum BinOp {\n    Eq,\n    Ne,\n    Lt,\n    Gt,\n    Lte,\n    Gte,\n}\n\nimpl Display for BinOp {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Eq => write!(f, \"=\"),\n            Self::Ne => write!(f, \"<>\"),\n            Self::Lt => write!(f, \"<\"),\n            Self::Gt => write!(f, \">\"),\n            Self::Lte => write!(f, \"<=\"),\n            Self::Gte => write!(f, \">=\"),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum LogOp {\n    And,\n    Or,\n}\n\nimpl Display for LogOp {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::And => write!(f, \"AND\"),\n            Self::Or => write!(f, \"OR\"),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sql-parser/src/ast/sql.rs",
    "content": "use spacetimedb_lib::Identity;\n\nuse crate::parser::{errors::SqlUnsupported, SqlParseResult};\n\nuse super::{Project, SqlExpr, SqlFrom, SqlIdent, SqlLiteral};\n\n/// The AST for the SQL DML and query language\n#[derive(Debug)]\npub enum SqlAst {\n    /// SELECT ...\n    Select(SqlSelect),\n    /// INSERT INTO ...\n    Insert(SqlInsert),\n    /// UPDATE ...\n    Update(SqlUpdate),\n    /// DELETE FROM ...\n    Delete(SqlDelete),\n    /// SET var TO ...\n    Set(SqlSet),\n    /// SHOW var\n    Show(SqlShow),\n}\n\nimpl SqlAst {\n    pub fn qualify_vars(self) -> Self {\n        match self {\n            Self::Select(select) => Self::Select(select.qualify_vars()),\n            Self::Update(SqlUpdate {\n                table: with,\n                assignments,\n                filter,\n            }) => Self::Update(SqlUpdate {\n                table: with.clone(),\n                filter: filter.map(|expr| expr.qualify_vars(with)),\n                assignments,\n            }),\n            Self::Delete(SqlDelete { table: with, filter }) => Self::Delete(SqlDelete {\n                table: with.clone(),\n                filter: filter.map(|expr| expr.qualify_vars(with)),\n            }),\n            _ => self,\n        }\n    }\n\n    pub fn find_unqualified_vars(self) -> SqlParseResult<Self> {\n        match self {\n            Self::Select(select) => select.find_unqualified_vars().map(Self::Select),\n            _ => Ok(self),\n        }\n    }\n\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    pub fn resolve_sender(self, sender_identity: Identity) -> Self {\n        match self {\n            Self::Select(select) => Self::Select(select.resolve_sender(sender_identity)),\n            Self::Update(update) => Self::Update(update.resolve_sender(sender_identity)),\n            Self::Delete(delete) => Self::Delete(delete.resolve_sender(sender_identity)),\n            _ => self,\n        }\n    }\n}\n\n/// A SELECT statement in the SQL query language\n#[derive(Debug)]\npub struct SqlSelect {\n    pub project: Project,\n    pub from: SqlFrom,\n    pub filter: Option<SqlExpr>,\n    pub limit: Option<Box<str>>,\n}\n\nimpl SqlSelect {\n    pub fn qualify_vars(self) -> Self {\n        match &self.from {\n            SqlFrom::Expr(_, alias) => Self {\n                project: self.project.qualify_vars(alias.clone()),\n                filter: self.filter.map(|expr| expr.qualify_vars(alias.clone())),\n                ..self\n            },\n            SqlFrom::Join(..) => self,\n        }\n    }\n\n    pub fn find_unqualified_vars(self) -> SqlParseResult<Self> {\n        if self.from.has_unqualified_vars() {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        if self.project.has_unqualified_vars() {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        if let Some(expr) = &self.filter\n            && expr.has_unqualified_vars()\n        {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        Ok(self)\n    }\n\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    pub fn resolve_sender(self, sender_identity: Identity) -> Self {\n        Self {\n            filter: self.filter.map(|expr| expr.resolve_sender(sender_identity)),\n            ..self\n        }\n    }\n}\n\n/// INSERT INTO table cols VALUES literals\n#[derive(Debug)]\npub struct SqlInsert {\n    pub table: SqlIdent,\n    pub fields: Vec<SqlIdent>,\n    pub values: SqlValues,\n}\n\n/// VALUES literals\n#[derive(Debug)]\npub struct SqlValues(pub Vec<Vec<SqlLiteral>>);\n\n/// UPDATE table SET cols [ WHERE predicate ]\n#[derive(Debug)]\npub struct SqlUpdate {\n    pub table: SqlIdent,\n    pub assignments: Vec<SqlSet>,\n    pub filter: Option<SqlExpr>,\n}\n\nimpl SqlUpdate {\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    fn resolve_sender(self, sender_identity: Identity) -> Self {\n        Self {\n            filter: self.filter.map(|expr| expr.resolve_sender(sender_identity)),\n            ..self\n        }\n    }\n}\n\n/// DELETE FROM table [ WHERE predicate ]\n#[derive(Debug)]\npub struct SqlDelete {\n    pub table: SqlIdent,\n    pub filter: Option<SqlExpr>,\n}\n\nimpl SqlDelete {\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    fn resolve_sender(self, sender_identity: Identity) -> Self {\n        Self {\n            filter: self.filter.map(|expr| expr.resolve_sender(sender_identity)),\n            ..self\n        }\n    }\n}\n\n/// SET var '=' literal\n#[derive(Debug)]\npub struct SqlSet(pub SqlIdent, pub SqlLiteral);\n\n/// SHOW var\n#[derive(Debug)]\npub struct SqlShow(pub SqlIdent);\n"
  },
  {
    "path": "crates/sql-parser/src/ast/sub.rs",
    "content": "use spacetimedb_lib::Identity;\n\nuse crate::parser::{errors::SqlUnsupported, SqlParseResult};\n\nuse super::{Project, SqlExpr, SqlFrom};\n\n/// A SELECT statement in the SQL subscription language\n#[derive(Debug)]\npub struct SqlSelect {\n    pub project: Project,\n    pub from: SqlFrom,\n    pub filter: Option<SqlExpr>,\n}\n\nimpl SqlSelect {\n    pub fn qualify_vars(self) -> Self {\n        match &self.from {\n            SqlFrom::Expr(_, alias) => Self {\n                project: self.project.qualify_vars(alias.clone()),\n                filter: self.filter.map(|expr| expr.qualify_vars(alias.clone())),\n                from: self.from,\n            },\n            SqlFrom::Join(..) => self,\n        }\n    }\n\n    pub fn find_unqualified_vars(self) -> SqlParseResult<Self> {\n        if self.from.has_unqualified_vars() {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        if self.project.has_unqualified_vars() {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        if let Some(expr) = &self.filter\n            && expr.has_unqualified_vars()\n        {\n            return Err(SqlUnsupported::UnqualifiedNames.into());\n        }\n        Ok(self)\n    }\n\n    /// Is this AST parameterized?\n    /// We need to know in order to hash subscription queries correctly.\n    pub fn has_parameter(&self) -> bool {\n        self.filter.as_ref().is_some_and(|expr| expr.has_parameter())\n    }\n\n    /// Replace the `:sender` parameter with the [Identity] it represents\n    pub fn resolve_sender(self, sender_identity: Identity) -> Self {\n        Self {\n            filter: self.filter.map(|expr| expr.resolve_sender(sender_identity)),\n            ..self\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sql-parser/src/lib.rs",
    "content": "pub mod ast;\npub mod parser;\n"
  },
  {
    "path": "crates/sql-parser/src/parser/errors.rs",
    "content": "use std::fmt::Display;\n\nuse sqlparser::{\n    ast::{\n        BinaryOperator, Expr, Function, ObjectName, Query, Select, SelectItem, SetExpr, TableFactor, TableWithJoins,\n        Value,\n    },\n    parser::ParserError,\n};\nuse thiserror::Error;\n\n// FIXME: reduce type size\n#[expect(clippy::large_enum_variant)]\n#[derive(Error, Debug)]\npub enum SubscriptionUnsupported {\n    #[error(\"Unsupported SELECT: {0}\")]\n    Select(Select),\n    #[error(\"Unsupported: {0}\")]\n    Feature(String),\n    #[error(\"Unsupported: Non-SELECT queries\")]\n    Dml,\n}\n\nimpl SubscriptionUnsupported {\n    pub(crate) fn feature(expr: impl Display) -> Self {\n        Self::Feature(format!(\"{expr}\"))\n    }\n}\n\n// FIXME: reduce type size\n#[expect(clippy::large_enum_variant)]\n#[derive(Error, Debug)]\npub enum SqlUnsupported {\n    #[error(\"Unsupported literal expression: {0}\")]\n    Literal(Value),\n    #[error(\"Unsupported LIMIT expression: {0}\")]\n    Limit(Expr),\n    #[error(\"Unsupported expression: {0}\")]\n    Expr(Expr),\n    #[error(\"Unsupported binary operator: {0}\")]\n    BinOp(BinaryOperator),\n    #[error(\"Unsupported projection: {0}\")]\n    Projection(SelectItem),\n    #[error(\"Unsupported projection expression: {0}\")]\n    ProjectionExpr(Expr),\n    #[error(\"Unsupported aggregate function: {0}\")]\n    Aggregate(Function),\n    #[error(\"Aggregate expressions must have column aliases\")]\n    AggregateWithoutAlias,\n    #[error(\"Unsupported FROM expression: {0}\")]\n    From(TableFactor),\n    #[error(\"Unsupported set operation: {0}\")]\n    SetOp(Box<SetExpr>),\n    #[error(\"Unsupported INSERT expression: {0}\")]\n    Insert(Query),\n    #[error(\"Unsupported INSERT value: {0}\")]\n    InsertValue(Expr),\n    #[error(\"Unsupported table expression in DELETE: {0}\")]\n    DeleteTable(TableWithJoins),\n    #[error(\"Unsupported column/variable assignment expression: {0}\")]\n    Assignment(Expr),\n    #[error(\"Multi-part names are not supported: {0}\")]\n    MultiPartName(ObjectName),\n    #[error(\"Unsupported: {0}\")]\n    Feature(String),\n    #[error(\"Non-inner joins are not supported\")]\n    JoinType,\n    #[error(\"Implicit joins are not supported\")]\n    ImplicitJoins,\n    #[error(\"Mixed wildcard projections are not supported\")]\n    MixedWildcardProject,\n    #[error(\"Multiple SQL statements are not supported\")]\n    MultiStatement,\n    #[error(\"Multi-table DELETE is not supported\")]\n    MultiTableDelete,\n    #[error(\"Empty SQL query\")]\n    Empty,\n    #[error(\"Names must be qualified when using joins\")]\n    UnqualifiedNames,\n}\n\nimpl SqlUnsupported {\n    pub(crate) fn feature(expr: impl Display) -> Self {\n        Self::Feature(format!(\"{expr}\"))\n    }\n}\n\n#[derive(Error, Debug)]\npub enum SqlRequired {\n    #[error(\"A FROM clause is required\")]\n    From,\n    #[error(\"Aliases are required for JOIN\")]\n    JoinAlias,\n}\n\n#[derive(Error, Debug)]\n#[error(\"Recursion limit exceeded, `{source_}`\")]\npub struct RecursionError {\n    pub(crate) source_: &'static str,\n}\n\n#[derive(Error, Debug)]\npub enum SqlParseError {\n    #[error(transparent)]\n    SqlUnsupported(#[from] Box<SqlUnsupported>),\n    #[error(transparent)]\n    SubscriptionUnsupported(#[from] Box<SubscriptionUnsupported>),\n    #[error(transparent)]\n    SqlRequired(#[from] SqlRequired),\n    #[error(transparent)]\n    ParserError(#[from] ParserError),\n    #[error(transparent)]\n    Recursion(#[from] RecursionError),\n}\n\nimpl From<SubscriptionUnsupported> for SqlParseError {\n    fn from(value: SubscriptionUnsupported) -> Self {\n        SqlParseError::SubscriptionUnsupported(Box::new(value))\n    }\n}\n\nimpl From<SqlUnsupported> for SqlParseError {\n    fn from(value: SqlUnsupported) -> Self {\n        SqlParseError::SqlUnsupported(Box::new(value))\n    }\n}\n"
  },
  {
    "path": "crates/sql-parser/src/parser/mod.rs",
    "content": "use errors::{SqlParseError, SqlRequired, SqlUnsupported};\nuse sqlparser::ast::{\n    BinaryOperator, Expr, Function, FunctionArg, FunctionArgExpr, Ident, Join, JoinConstraint, JoinOperator,\n    ObjectName, Query, SelectItem, TableAlias, TableFactor, TableWithJoins, UnaryOperator, Value,\n    WildcardAdditionalOptions,\n};\n\nuse crate::ast::{\n    BinOp, LogOp, Parameter, Project, ProjectElem, ProjectExpr, SqlExpr, SqlFrom, SqlIdent, SqlJoin, SqlLiteral,\n};\n\npub mod errors;\npub mod recursion;\npub mod sql;\npub mod sub;\n\npub type SqlParseResult<T> = core::result::Result<T, SqlParseError>;\n\n/// Methods for parsing a relation expression.\n/// Note we abstract over the type of the relation expression,\n/// as each language has a different definition for it.\ntrait RelParser {\n    type Ast;\n\n    /// Parse a top level relation expression\n    fn parse_query(query: Query) -> SqlParseResult<Self::Ast>;\n\n    /// Parse a FROM clause\n    fn parse_from(mut tables: Vec<TableWithJoins>) -> SqlParseResult<SqlFrom> {\n        if tables.is_empty() {\n            return Err(SqlRequired::From.into());\n        }\n        if tables.len() > 1 {\n            return Err(SqlUnsupported::ImplicitJoins.into());\n        }\n        let TableWithJoins { relation, joins } = tables.swap_remove(0);\n        let (name, alias) = Self::parse_relvar(relation)?;\n        if joins.is_empty() {\n            return Ok(SqlFrom::Expr(name, alias));\n        }\n        Ok(SqlFrom::Join(name, alias, Self::parse_joins(joins)?))\n    }\n\n    /// Parse a sequence of JOIN clauses\n    fn parse_joins(joins: Vec<Join>) -> SqlParseResult<Vec<SqlJoin>> {\n        joins.into_iter().map(Self::parse_join).collect()\n    }\n\n    /// Parse a single JOIN clause\n    fn parse_join(join: Join) -> SqlParseResult<SqlJoin> {\n        let (var, alias) = Self::parse_relvar(join.relation)?;\n        match join.join_operator {\n            JoinOperator::CrossJoin => Ok(SqlJoin { var, alias, on: None }),\n            JoinOperator::Inner(JoinConstraint::None) => Ok(SqlJoin { var, alias, on: None }),\n            JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {\n                left,\n                op: BinaryOperator::Eq,\n                right,\n            })) if matches!(*left, Expr::Identifier(..) | Expr::CompoundIdentifier(..))\n                && matches!(*right, Expr::Identifier(..) | Expr::CompoundIdentifier(..)) =>\n            {\n                Ok(SqlJoin {\n                    var,\n                    alias,\n                    on: Some(parse_expr(\n                        Expr::BinaryOp {\n                            left,\n                            op: BinaryOperator::Eq,\n                            right,\n                        },\n                        0,\n                    )?),\n                })\n            }\n            _ => Err(SqlUnsupported::JoinType.into()),\n        }\n    }\n\n    /// Parse a table reference in a FROM clause\n    fn parse_relvar(expr: TableFactor) -> SqlParseResult<(SqlIdent, SqlIdent)> {\n        match expr {\n            // Relvar no alias\n            TableFactor::Table {\n                name,\n                alias: None,\n                args: None,\n                with_hints,\n                version: None,\n                partitions,\n            } if with_hints.is_empty() && partitions.is_empty() => {\n                let name = parse_ident(name)?;\n                let alias = name.clone();\n                Ok((name, alias))\n            }\n            // Relvar with alias\n            TableFactor::Table {\n                name,\n                alias: Some(TableAlias { name: alias, columns }),\n                args: None,\n                with_hints,\n                version: None,\n                partitions,\n            } if with_hints.is_empty() && partitions.is_empty() && columns.is_empty() => {\n                Ok((parse_ident(name)?, alias.into()))\n            }\n            _ => Err(SqlUnsupported::From(expr).into()),\n        }\n    }\n}\n\n/// Parse the items of a SELECT clause\npub(crate) fn parse_projection(mut items: Vec<SelectItem>) -> SqlParseResult<Project> {\n    if items.len() == 1 {\n        return parse_project_or_agg(items.swap_remove(0));\n    }\n    Ok(Project::Exprs(\n        items\n            .into_iter()\n            .map(parse_project_elem)\n            .collect::<SqlParseResult<_>>()?,\n    ))\n}\n\n/// Parse a SELECT clause with only a single item\npub(crate) fn parse_project_or_agg(item: SelectItem) -> SqlParseResult<Project> {\n    match item {\n        SelectItem::Wildcard(WildcardAdditionalOptions {\n            opt_exclude: None,\n            opt_except: None,\n            opt_rename: None,\n            opt_replace: None,\n        }) => Ok(Project::Star(None)),\n        SelectItem::QualifiedWildcard(\n            table_name,\n            WildcardAdditionalOptions {\n                opt_exclude: None,\n                opt_except: None,\n                opt_rename: None,\n                opt_replace: None,\n            },\n        ) => Ok(Project::Star(Some(parse_ident(table_name)?))),\n        SelectItem::UnnamedExpr(Expr::Function(_)) => Err(SqlUnsupported::AggregateWithoutAlias.into()),\n        SelectItem::ExprWithAlias {\n            expr: Expr::Function(agg_fn),\n            alias,\n        } => parse_agg_fn(agg_fn, alias.into()),\n        SelectItem::UnnamedExpr(_) | SelectItem::ExprWithAlias { .. } => {\n            Ok(Project::Exprs(vec![parse_project_elem(item)?]))\n        }\n        item => Err(SqlUnsupported::Projection(item).into()),\n    }\n}\n\n/// Parse an aggregate function in a select list\nfn parse_agg_fn(agg_fn: Function, alias: SqlIdent) -> SqlParseResult<Project> {\n    fn is_count(name: &ObjectName) -> bool {\n        name.0.len() == 1\n            && name\n                .0\n                .first()\n                .is_some_and(|Ident { value, .. }| value.to_lowercase() == \"count\")\n    }\n    match agg_fn {\n        Function {\n            name,\n            args,\n            over: None,\n            distinct: false,\n            special: false,\n            order_by,\n        } if is_count(&name)\n            && order_by.is_empty()\n            && args.len() == 1\n            && args\n                .first()\n                .is_some_and(|arg| matches!(arg, FunctionArg::Unnamed(FunctionArgExpr::Wildcard))) =>\n        {\n            Ok(Project::Count(alias))\n        }\n        agg_fn => Err(SqlUnsupported::Aggregate(agg_fn).into()),\n    }\n}\n\n/// Parse an item in a SELECT clause\npub(crate) fn parse_project_elem(item: SelectItem) -> SqlParseResult<ProjectElem> {\n    match item {\n        SelectItem::Wildcard(_) => Err(SqlUnsupported::MixedWildcardProject.into()),\n        SelectItem::QualifiedWildcard(..) => Err(SqlUnsupported::MixedWildcardProject.into()),\n        SelectItem::UnnamedExpr(expr) => match parse_proj(expr)? {\n            ProjectExpr::Var(name) => Ok(ProjectElem(ProjectExpr::Var(name.clone()), name)),\n            ProjectExpr::Field(name, field) => Ok(ProjectElem(ProjectExpr::Field(name, field.clone()), field)),\n        },\n        SelectItem::ExprWithAlias { expr, alias } => Ok(ProjectElem(parse_proj(expr)?, alias.into())),\n    }\n}\n\n/// Parse a column projection\npub(crate) fn parse_proj(expr: Expr) -> SqlParseResult<ProjectExpr> {\n    match expr {\n        Expr::Identifier(ident) => Ok(ProjectExpr::Var(ident.into())),\n        Expr::CompoundIdentifier(mut idents) if idents.len() == 2 => {\n            let table = idents.swap_remove(0).into();\n            let field = idents.swap_remove(0).into();\n            Ok(ProjectExpr::Field(table, field))\n        }\n        _ => Err(SqlUnsupported::ProjectionExpr(expr).into()),\n    }\n}\n\n// These types determine the size of [`parse_expr`]'s stack frame.\n// Changing their sizes will require updating the recursion limit to avoid stack overflows.\nconst _: () = assert!(size_of::<Expr>() == 168);\nconst _: () = assert!(size_of::<SqlParseResult<SqlExpr>>() == 40);\n\n/// Parse a scalar expression\nfn parse_expr(expr: Expr, depth: usize) -> SqlParseResult<SqlExpr> {\n    fn signed_num(sign: impl Into<String>, expr: Expr) -> Result<SqlExpr, Box<SqlUnsupported>> {\n        match expr {\n            Expr::Value(Value::Number(n, _)) => Ok(SqlExpr::Lit(SqlLiteral::Num((sign.into() + &n).into_boxed_str()))),\n            expr => Err(SqlUnsupported::Expr(expr).into()),\n        }\n    }\n    recursion::guard(depth, recursion::MAX_RECURSION_EXPR, \"sql-parser::parse_expr\")?;\n    match expr {\n        Expr::Nested(expr) => parse_expr(*expr, depth + 1),\n        Expr::Value(Value::Placeholder(param)) if &param == \":sender\" => Ok(SqlExpr::Param(Parameter::Sender)),\n        Expr::Value(v) => Ok(SqlExpr::Lit(parse_literal(v)?)),\n        Expr::UnaryOp {\n            op: UnaryOperator::Plus,\n            expr,\n        } if matches!(&*expr, Expr::Value(Value::Number(..))) => {\n            signed_num(\"+\", *expr).map_err(SqlParseError::SqlUnsupported)\n        }\n        Expr::UnaryOp {\n            op: UnaryOperator::Minus,\n            expr,\n        } if matches!(&*expr, Expr::Value(Value::Number(..))) => {\n            signed_num(\"-\", *expr).map_err(SqlParseError::SqlUnsupported)\n        }\n        Expr::Identifier(ident) => Ok(SqlExpr::Var(ident.into())),\n        Expr::CompoundIdentifier(mut idents) if idents.len() == 2 => {\n            let table = idents.swap_remove(0).into();\n            let field = idents.swap_remove(0).into();\n            Ok(SqlExpr::Field(table, field))\n        }\n        Expr::BinaryOp {\n            left,\n            op: BinaryOperator::And,\n            right,\n        } => {\n            let l = parse_expr(*left, depth + 1)?;\n            let r = parse_expr(*right, depth + 1)?;\n            Ok(SqlExpr::Log(Box::new(l), Box::new(r), LogOp::And))\n        }\n        Expr::BinaryOp {\n            left,\n            op: BinaryOperator::Or,\n            right,\n        } => {\n            let l = parse_expr(*left, depth + 1)?;\n            let r = parse_expr(*right, depth + 1)?;\n            Ok(SqlExpr::Log(Box::new(l), Box::new(r), LogOp::Or))\n        }\n        Expr::BinaryOp { left, op, right } => {\n            let l = parse_expr(*left, depth + 1)?;\n            let r = parse_expr(*right, depth + 1)?;\n            Ok(SqlExpr::Bin(Box::new(l), Box::new(r), parse_binop(op)?))\n        }\n        _ => Err(SqlUnsupported::Expr(expr).into()),\n    }\n}\n\n/// Parse an optional scalar expression\npub(crate) fn parse_expr_opt(opt: Option<Expr>) -> SqlParseResult<Option<SqlExpr>> {\n    opt.map(|expr| parse_expr(expr, 0)).transpose()\n}\n\n/// Parse a scalar binary operator\npub(crate) fn parse_binop(op: BinaryOperator) -> SqlParseResult<BinOp> {\n    match op {\n        BinaryOperator::Eq => Ok(BinOp::Eq),\n        BinaryOperator::NotEq => Ok(BinOp::Ne),\n        BinaryOperator::Lt => Ok(BinOp::Lt),\n        BinaryOperator::LtEq => Ok(BinOp::Lte),\n        BinaryOperator::Gt => Ok(BinOp::Gt),\n        BinaryOperator::GtEq => Ok(BinOp::Gte),\n        _ => Err(SqlUnsupported::BinOp(op).into()),\n    }\n}\n\n/// Parse a literal expression\npub(crate) fn parse_literal(value: Value) -> SqlParseResult<SqlLiteral> {\n    match value {\n        Value::Boolean(v) => Ok(SqlLiteral::Bool(v)),\n        Value::Number(v, _) => Ok(SqlLiteral::Num(v.into_boxed_str())),\n        Value::SingleQuotedString(s) => Ok(SqlLiteral::Str(s.into_boxed_str())),\n        Value::HexStringLiteral(s) => Ok(SqlLiteral::Hex(s.into_boxed_str())),\n        _ => Err(SqlUnsupported::Literal(value).into()),\n    }\n}\n\n/// Parse an identifier\npub(crate) fn parse_ident(ObjectName(parts): ObjectName) -> SqlParseResult<SqlIdent> {\n    parse_parts(parts)\n}\n\n/// Parse an identifier\npub(crate) fn parse_parts(mut parts: Vec<Ident>) -> SqlParseResult<SqlIdent> {\n    if parts.len() == 1 {\n        return Ok(parts.swap_remove(0).into());\n    }\n    Err(SqlUnsupported::MultiPartName(ObjectName(parts)).into())\n}\n"
  },
  {
    "path": "crates/sql-parser/src/parser/recursion.rs",
    "content": "//! A utility for guarding against stack overflows in the SQL parser.\n//!\n//! Different parts of the parser may have different recursion limits, based in the size of the structures they parse.\n\nuse crate::parser::errors::{RecursionError, SqlParseError};\n\n/// A conservative limit for recursion depth on `parse_expr`.\npub const MAX_RECURSION_EXPR: usize = 1_600;\n/// A conservative limit for recursion depth on `type_expr`.\npub const MAX_RECURSION_TYP_EXPR: usize = 2_500;\n\n/// A utility for guarding against stack overflows in the SQL parser.\n///\n/// **Usage:**\n/// ```\n/// use spacetimedb_sql_parser::parser::recursion;\n/// let mut depth = 0;\n/// assert!(recursion::guard(depth, 10, \"test\").is_ok());\n/// ```\npub fn guard(depth: usize, limit: usize, source: &'static str) -> Result<(), SqlParseError> {\n    if depth > limit {\n        Err(RecursionError { source_: source }.into())\n    } else {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/sql-parser/src/parser/sql.rs",
    "content": "//! The SpacetimeDB SQL grammar\n//!\n//! ```ebnf\n//! statement\n//!     = select\n//!     | insert\n//!     | delete\n//!     | update\n//!     | set\n//!     | show\n//!     ;\n//!\n//! insert\n//!     = INSERT INTO table [ '(' column { ',' column } ')' ] VALUES '(' literal { ',' literal } ')'\n//!     ;\n//!\n//! delete\n//!     = DELETE FROM table [ WHERE predicate ]\n//!     ;\n//!\n//! update\n//!     = UPDATE table SET [ '(' assignment { ',' assignment } ')' ] [ WHERE predicate ]\n//!     ;\n//!\n//! assignment\n//!     = column '=' expr\n//!     ;\n//!\n//! set\n//!     = SET var ( TO | '=' ) literal\n//!     ;\n//!\n//! show\n//!     = SHOW var\n//!     ;\n//!\n//! var\n//!     = ident\n//!     ;\n//!\n//! select\n//!     = SELECT [ DISTINCT ] projection FROM relation [ [ WHERE predicate ] [ ORDER BY order ] [ LIMIT limit ] ]\n//!     ;\n//!\n//! projection\n//!     = listExpr\n//!     | projExpr { ',' projExpr }\n//!     | aggrExpr { ',' aggrExpr }\n//!     ;\n//!\n//! listExpr\n//!     = STAR\n//!     | ident '.' STAR\n//!     ;\n//!\n//! projExpr\n//!     = columnExpr [ [ AS ] ident ]\n//!     ;\n//!\n//! columnExpr\n//!     = column\n//!     | field\n//!     ;\n//!\n//! aggrExpr\n//!     = COUNT '(' STAR ')' AS ident\n//!     | COUNT '(' DISTINCT columnExpr ')' AS ident\n//!     | SUM   '(' columnExpr ')' AS ident\n//!     ;\n//!\n//! relation\n//!     = table\n//!     | '(' query ')'\n//!     | relation [ [AS] ident ] { [INNER] JOIN relation [ [AS] ident ] ON predicate }\n//!     ;\n//!\n//! predicate\n//!     = expr\n//!     | predicate AND predicate\n//!     | predicate OR  predicate\n//!     ;\n//!\n//! expr\n//!     = literal\n//!     | ident\n//!     | field\n//!     | expr op expr\n//!     ;\n//!\n//! field\n//!     = ident '.' ident\n//!     ;\n//!\n//! op\n//!     = '='\n//!     | '<'\n//!     | '>'\n//!     | '<' '='\n//!     | '>' '='\n//!     | '!' '='\n//!     | '<' '>'\n//!     ;\n//!\n//! order\n//!     = columnExpr [ ASC | DESC ] { ',' columnExpr [ ASC | DESC ] }\n//!     ;\n//!\n//! limit\n//!     = INTEGER\n//!     ;\n//!\n//! table\n//!     = ident\n//!     ;\n//!\n//! column\n//!     = ident\n//!     ;\n//!\n//! literal\n//!     = INTEGER\n//!     | FLOAT\n//!     | STRING\n//!     | HEX\n//!     | TRUE\n//!     | FALSE\n//!     ;\n//! ```\n\nuse sqlparser::{\n    ast::{\n        Assignment, Expr, GroupByExpr, ObjectName, Query, Select, SetExpr, Statement, TableFactor, TableWithJoins,\n        Value, Values,\n    },\n    dialect::PostgreSqlDialect,\n    parser::Parser,\n};\n\nuse crate::ast::{\n    sql::{SqlAst, SqlDelete, SqlInsert, SqlSelect, SqlSet, SqlShow, SqlUpdate, SqlValues},\n    SqlIdent,\n};\n\nuse super::{\n    errors::SqlUnsupported, parse_expr_opt, parse_ident, parse_literal, parse_parts, parse_projection, RelParser,\n    SqlParseResult,\n};\n\n/// Parse a SQL string\npub fn parse_sql(sql: &str) -> SqlParseResult<SqlAst> {\n    let mut stmts = Parser::parse_sql(&PostgreSqlDialect {}, sql)?;\n    if stmts.len() > 1 {\n        return Err(SqlUnsupported::MultiStatement.into());\n    }\n    if stmts.is_empty() {\n        return Err(SqlUnsupported::Empty.into());\n    }\n    parse_statement(stmts.swap_remove(0))\n        .map(|ast| ast.qualify_vars())\n        .and_then(|ast| ast.find_unqualified_vars())\n}\n\n/// Parse a SQL statement\nfn parse_statement(stmt: Statement) -> SqlParseResult<SqlAst> {\n    match stmt {\n        Statement::Query(query) => Ok(SqlAst::Select(SqlParser::parse_query(*query)?)),\n        Statement::Insert {\n            or: None,\n            table_name,\n            columns,\n            overwrite: false,\n            source,\n            partitioned: None,\n            after_columns,\n            table: false,\n            on: None,\n            returning: None,\n            ..\n        } if after_columns.is_empty() => Ok(SqlAst::Insert(SqlInsert {\n            table: parse_ident(table_name)?,\n            fields: columns.into_iter().map(SqlIdent::from).collect(),\n            values: parse_values(*source)?,\n        })),\n        Statement::Update {\n            table:\n                TableWithJoins {\n                    relation:\n                        TableFactor::Table {\n                            name,\n                            alias: None,\n                            args: None,\n                            with_hints,\n                            version: None,\n                            partitions,\n                        },\n                    joins,\n                },\n            assignments,\n            from: None,\n            selection,\n            returning: None,\n        } if joins.is_empty() && with_hints.is_empty() && partitions.is_empty() => Ok(SqlAst::Update(SqlUpdate {\n            table: parse_ident(name)?,\n            assignments: parse_assignments(assignments)?,\n            filter: parse_expr_opt(selection)?,\n        })),\n        Statement::Delete {\n            tables,\n            from,\n            using: None,\n            selection,\n            returning: None,\n        } if tables.is_empty() => Ok(SqlAst::Delete(parse_delete(from, selection)?)),\n        Statement::SetVariable {\n            local: false,\n            hivevar: false,\n            variable,\n            value,\n        } => Ok(SqlAst::Set(parse_set_var(variable, value)?)),\n        Statement::ShowVariable { variable } => Ok(SqlAst::Show(SqlShow(parse_parts(variable)?))),\n        _ => Err(SqlUnsupported::feature(stmt).into()),\n    }\n}\n\n/// Parse a VALUES expression\nfn parse_values(values: Query) -> SqlParseResult<SqlValues> {\n    match values {\n        Query {\n            with: None,\n            body,\n            order_by,\n            limit: None,\n            offset: None,\n            fetch: None,\n            locks,\n        } if order_by.is_empty() && locks.is_empty() => match *body {\n            SetExpr::Values(Values {\n                explicit_row: false,\n                rows,\n            }) => {\n                let mut row_literals = Vec::new();\n                for row in rows {\n                    let mut literals = Vec::new();\n                    for expr in row {\n                        if let Expr::Value(value) = expr {\n                            literals.push(parse_literal(value)?);\n                        } else {\n                            return Err(SqlUnsupported::InsertValue(expr).into());\n                        }\n                    }\n                    row_literals.push(literals);\n                }\n                Ok(SqlValues(row_literals))\n            }\n            _ => Err(SqlUnsupported::Insert(Query {\n                with: None,\n                body,\n                order_by,\n                limit: None,\n                offset: None,\n                fetch: None,\n                locks,\n            })\n            .into()),\n        },\n        _ => Err(SqlUnsupported::Insert(values).into()),\n    }\n}\n\n/// Parse column/variable assignments in an UPDATE or SET statement\nfn parse_assignments(assignments: Vec<Assignment>) -> SqlParseResult<Vec<SqlSet>> {\n    assignments.into_iter().map(parse_assignment).collect()\n}\n\n/// Parse a column/variable assignment in an UPDATE or SET statement\nfn parse_assignment(Assignment { id, value }: Assignment) -> SqlParseResult<SqlSet> {\n    match value {\n        Expr::Value(value) => Ok(SqlSet(parse_parts(id)?, parse_literal(value)?)),\n        _ => Err(SqlUnsupported::Assignment(value).into()),\n    }\n}\n\n/// Parse a DELETE statement\nfn parse_delete(mut from: Vec<TableWithJoins>, selection: Option<Expr>) -> SqlParseResult<SqlDelete> {\n    if from.len() == 1 {\n        match from.swap_remove(0) {\n            TableWithJoins {\n                relation:\n                    TableFactor::Table {\n                        name,\n                        alias: None,\n                        args: None,\n                        with_hints,\n                        version: None,\n                        partitions,\n                    },\n                joins,\n            } if joins.is_empty() && with_hints.is_empty() && partitions.is_empty() => Ok(SqlDelete {\n                table: parse_ident(name)?,\n                filter: parse_expr_opt(selection)?,\n            }),\n            t => Err(SqlUnsupported::DeleteTable(t).into()),\n        }\n    } else {\n        Err(SqlUnsupported::MultiTableDelete.into())\n    }\n}\n\n/// Parse a SET variable statement\nfn parse_set_var(variable: ObjectName, mut value: Vec<Expr>) -> SqlParseResult<SqlSet> {\n    if value.len() == 1 {\n        Ok(SqlSet(\n            parse_ident(variable)?,\n            match value.swap_remove(0) {\n                Expr::Value(value) => parse_literal(value)?,\n                expr => {\n                    return Err(SqlUnsupported::Assignment(expr).into());\n                }\n            },\n        ))\n    } else {\n        Err(SqlUnsupported::feature(Statement::SetVariable {\n            local: false,\n            hivevar: false,\n            variable,\n            value,\n        })\n        .into())\n    }\n}\n\nstruct SqlParser;\n\nimpl RelParser for SqlParser {\n    type Ast = SqlSelect;\n\n    fn parse_query(query: Query) -> SqlParseResult<Self::Ast> {\n        match query {\n            Query {\n                with: None,\n                body,\n                order_by,\n                limit: None,\n                offset: None,\n                fetch: None,\n                locks,\n            } if order_by.is_empty() && locks.is_empty() => parse_set_op(*body, None),\n            Query {\n                with: None,\n                body,\n                order_by,\n                limit: Some(Expr::Value(Value::Number(n, _))),\n                offset: None,\n                fetch: None,\n                locks,\n            } if order_by.is_empty() && locks.is_empty() => parse_set_op(*body, Some(n.into_boxed_str())),\n            _ => Err(SqlUnsupported::feature(query).into()),\n        }\n    }\n}\n\n/// Parse a set operation\nfn parse_set_op(expr: SetExpr, limit: Option<Box<str>>) -> SqlParseResult<SqlSelect> {\n    match expr {\n        SetExpr::Select(select) => parse_select(*select, limit).map(SqlSelect::qualify_vars),\n        _ => Err(SqlUnsupported::feature(expr).into()),\n    }\n}\n\n/// Parse a SELECT statement\nfn parse_select(select: Select, limit: Option<Box<str>>) -> SqlParseResult<SqlSelect> {\n    match select {\n        Select {\n            distinct: None,\n            top: None,\n            projection,\n            into: None,\n            from,\n            lateral_views,\n            selection,\n            group_by: GroupByExpr::Expressions(exprs),\n            cluster_by,\n            distribute_by,\n            sort_by,\n            having: None,\n            named_window,\n            qualify: None,\n        } if lateral_views.is_empty()\n            && exprs.is_empty()\n            && cluster_by.is_empty()\n            && distribute_by.is_empty()\n            && sort_by.is_empty()\n            && named_window.is_empty() =>\n        {\n            Ok(SqlSelect {\n                project: parse_projection(projection)?,\n                from: SqlParser::parse_from(from)?,\n                filter: parse_expr_opt(selection)?,\n                limit,\n            })\n        }\n        _ => Err(SqlUnsupported::feature(select).into()),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::parser::sql::parse_sql;\n\n    #[test]\n    fn unsupported() {\n        for sql in [\n            // FROM is required\n            \"select 1\",\n            // Multi-part table names\n            \"select a from s.t\",\n            // Bit-string literals\n            \"select * from t where a = B'1010'\",\n            // Wildcard with non-wildcard projections\n            \"select a.*, b, c from t\",\n            // Limit expression\n            \"select * from t order by a limit b\",\n            // GROUP BY\n            \"select a, count(*) from t group by a\",\n            // Join updates\n            \"update t as a join s as b on a.id = b.id set c = 1\",\n            // Join updates\n            \"update t set a = 1 from s where t.id = s.id and s.b = 2\",\n            // Implicit joins\n            \"select a.* from t as a, s as b where a.id = b.id and b.c = 1\",\n            // Joins require qualified vars\n            \"select t.* from t join s on int = u32\",\n        ] {\n            assert!(parse_sql(sql).is_err());\n        }\n    }\n\n    #[test]\n    fn supported() {\n        for sql in [\n            \"select a from t\",\n            \"select a from t where x = :sender\",\n            \"select count(*) as n from t\",\n            \"select count(*) as n from t join s on t.id = s.id where s.x = 1\",\n            \"insert into t values (1, 2)\",\n            \"delete from t\",\n            \"delete from t where a = 1\",\n            \"delete from t where x = :sender\",\n            \"update t set a = 1, b = 2\",\n            \"update t set a = 1, b = 2 where c = 3\",\n            \"update t set a = 1, b = 2 where x = :sender\",\n        ] {\n            assert!(parse_sql(sql).is_ok());\n        }\n    }\n\n    #[test]\n    fn invalid() {\n        for sql in [\n            // Empty SELECT\n            \"select from t\",\n            // Empty FROM\n            \"select a from where b = 1\",\n            // Empty WHERE\n            \"select a from t where\",\n            // Empty GROUP BY\n            \"select a, count(*) from t group by\",\n            // Aggregate without alias\n            \"select count(*) from t\",\n            // Empty statement\n            \"\",\n            \" \",\n        ] {\n            assert!(parse_sql(sql).is_err());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sql-parser/src/parser/sub.rs",
    "content": "//! The SpacetimeDB SQL subscription grammar\n//!\n//! ```ebnf\n//! query\n//!     = SELECT projection FROM relation [ WHERE predicate ]\n//!     ;\n//!\n//! projection\n//!     = STAR\n//!     | ident '.' STAR\n//!     ;\n//!\n//! relation\n//!     = table\n//!     | '(' query ')'\n//!     | relation [ [AS] ident ] { [INNER] JOIN relation [ [AS] ident ] ON predicate }\n//!     ;\n//!\n//! predicate\n//!     = expr\n//!     | predicate AND predicate\n//!     | predicate OR  predicate\n//!     ;\n//!\n//! expr\n//!     = literal\n//!     | ident\n//!     | field\n//!     | expr op expr\n//!     ;\n//!\n//! field\n//!     = ident '.' ident\n//!     ;\n//!\n//! op\n//!     = '='\n//!     | '<'\n//!     | '>'\n//!     | '<' '='\n//!     | '>' '='\n//!     | '!' '='\n//!     | '<' '>'\n//!     ;\n//!\n//! literal\n//!     = INTEGER\n//!     | FLOAT\n//!     | STRING\n//!     | HEX\n//!     | TRUE\n//!     | FALSE\n//!     ;\n//! ```\n\nuse sqlparser::{\n    ast::{GroupByExpr, Query, Select, SetExpr, Statement},\n    dialect::PostgreSqlDialect,\n    parser::Parser,\n};\n\nuse crate::ast::sub::SqlSelect;\n\nuse super::{\n    errors::{SqlUnsupported, SubscriptionUnsupported},\n    parse_expr_opt, parse_projection, RelParser, SqlParseResult,\n};\n\n/// Parse a SQL string\npub fn parse_subscription(sql: &str) -> SqlParseResult<SqlSelect> {\n    let mut stmts = Parser::parse_sql(&PostgreSqlDialect {}, sql)?;\n    match stmts.len() {\n        0 => Err(SqlUnsupported::Empty.into()),\n        1 => parse_statement(stmts.swap_remove(0))\n            .map(|ast| ast.qualify_vars())\n            .and_then(|ast| ast.find_unqualified_vars()),\n        _ => Err(SqlUnsupported::MultiStatement.into()),\n    }\n}\n\n/// Parse a SQL query\nfn parse_statement(stmt: Statement) -> SqlParseResult<SqlSelect> {\n    match stmt {\n        Statement::Query(query) => SubParser::parse_query(*query),\n        _ => Err(SubscriptionUnsupported::Dml.into()),\n    }\n}\n\nstruct SubParser;\n\nimpl RelParser for SubParser {\n    type Ast = SqlSelect;\n\n    fn parse_query(query: Query) -> SqlParseResult<Self::Ast> {\n        match query {\n            Query {\n                with: None,\n                body,\n                order_by,\n                limit: None,\n                offset: None,\n                fetch: None,\n                locks,\n            } if order_by.is_empty() && locks.is_empty() => parse_set_op(*body),\n            _ => Err(SubscriptionUnsupported::feature(query).into()),\n        }\n    }\n}\n\n/// Parse a set operation\nfn parse_set_op(expr: SetExpr) -> SqlParseResult<SqlSelect> {\n    match expr {\n        SetExpr::Select(select) => parse_select(*select).map(SqlSelect::qualify_vars),\n        _ => Err(SqlUnsupported::SetOp(Box::new(expr)).into()),\n    }\n}\n\n// Parse a SELECT statement\nfn parse_select(select: Select) -> SqlParseResult<SqlSelect> {\n    match select {\n        Select {\n            distinct: None,\n            top: None,\n            projection,\n            into: None,\n            from,\n            lateral_views,\n            selection,\n            group_by: GroupByExpr::Expressions(exprs),\n            cluster_by,\n            distribute_by,\n            sort_by,\n            having: None,\n            named_window,\n            qualify: None,\n        } if lateral_views.is_empty()\n            && exprs.is_empty()\n            && cluster_by.is_empty()\n            && distribute_by.is_empty()\n            && sort_by.is_empty()\n            && named_window.is_empty() =>\n        {\n            Ok(SqlSelect {\n                from: SubParser::parse_from(from)?,\n                filter: parse_expr_opt(selection)?,\n                project: parse_projection(projection)?,\n            })\n        }\n        _ => Err(SubscriptionUnsupported::Select(select).into()),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::parser::sub::parse_subscription;\n\n    #[test]\n    fn unsupported() {\n        for sql in [\n            \"delete from t\",\n            \" \",\n            \"\",\n            \"select distinct a from t\",\n            \"select * from (select * from t) join (select * from s) on a = b\",\n        ] {\n            assert!(parse_subscription(sql).is_err());\n        }\n    }\n\n    #[test]\n    fn supported() {\n        for sql in [\n            \"select * from t\",\n            \"select * from t where a = 1\",\n            \"select * from t where a <> 1\",\n            \"select * from t where a = 1 or a = 2\",\n            \"select t.* from t join s\",\n            \"select t.* from t join s on t.c = s.d\",\n            \"select a.* from t as a join s as b on a.c = b.d\",\n            \"select * from t where x = :sender\",\n        ] {\n            assert!(parse_subscription(sql).is_ok());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sqltest/Cargo.toml",
    "content": "[package]\nname = \"sqltest\"\nversion.workspace = true\nedition.workspace = true\ndescription = \"Sql integration test for SpacetimeDB\"\nlicense-file = \"LICENSE\"\npublish = false\n\n[dependencies]\nspacetimedb-lib.workspace = true\nspacetimedb-core = { workspace = true,  features = [\"test\"] }\nspacetimedb-sats.workspace = true\n\nanyhow.workspace = true\nasync-trait.workspace = true\nchrono.workspace = true\nclap.workspace = true\nconsole.workspace = true\nderive_more.workspace = true\nfs-err.workspace = true\nfutures.workspace = true\nglob.workspace = true\nitertools.workspace = true\nparking_lot.workspace = true\npostgres-types.workspace = true\nquick-junit.workspace = true\nrusqlite.workspace = true\nrust_decimal.workspace = true\nsqllogictest-engines.workspace = true\nsqllogictest.workspace = true\ntempfile.workspace = true\ntokio.workspace = true\ntokio-postgres.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/sqltest/README.md",
    "content": "This crate check the conformance of SpacetimeDB to SQL:2016\n\n> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n\n# Setup PG\n\nCreate a database called `TestSpace`\n\n# Issues\n\nList of issues in the execution of the conformance test\n\n## UNSUPPORTED: issue 1\n\n`CHARACTERS/OCTETS` is not implemented by neither PG nor Sqlite\n```sql\nCREATE TABLE TABLE_E021_01_01_02 ( A CHAR ( 8 CHARACTERS ) )\nCREATE TABLE TABLE_E021_01_01_02 ( A CHAR ( 8 OCTETS ) )\n```\n## UNSUPPORTED: issue 2\n\n`CHAR VARING` is not implemented by neither PG nor Sqlite\n\n```sql\nCREATE TABLE TABLE_E021_02_01_02 ( A CHAR VARING ( 8 CHARACTERS ) )\n```\n\n## UNSUPPORTED: issue 3\n\n` AS ( C , D )` is a syntax error on PG and Sqlite\n\n```sql\nSELECT * AS ( C , D ) FROM TABLE_E051_07_01_01\n```\n## REPLACED: issue 4\n\n`CURRENT_TIME` and `timetz` type is marked as \"Don't use EVER\" by PG:\nhttps://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timetz\n\n```sql\nSELECT CURRENT_TIME\n```\n\n## UNSUPPORTED: issue 5\n\n`CASE 0 WHEN 2 , 2` is a syntax error on PG and Sqlite\n\n```sql\nSELECT CASE 0 WHEN 2 , 2 THEN 1 END\n```\n\n## WRONG: issue 6\n\nThis expression fail in PG by the lack of a timezone and Sqlite spit out a nonsensical value (`1`)\n```sql\nSELECT CAST ( CAST ( '01:02:03' AS TIME ) AS TIMESTAMP )\n```"
  },
  {
    "path": "crates/sqltest/build_standard.py",
    "content": "import glob\nimport os\nimport yaml\nfrom dataclasses import dataclass\nimport shutil\n\nbase = os.path.dirname(__file__)\nstandard = os.path.join(base, 'standards', '2016')\ntests_paths = glob.glob(os.path.join(standard, '**', '*.tests.yml'), recursive=True)\noutput_path = os.path.join(base, 'test', 'sql_2016')\n\nshutil.rmtree(output_path)\nos.mkdir(output_path)\n\nmandatory = {}\nwith open(os.path.join(standard, 'features.yml'), 'r') as file:\n    document = next(yaml.safe_load_all(file))\n\n    mandatory = document['mandatory']\n\n\n# print(mandatory)\n# print(optional)\n\n\n@dataclass\nclass Test:\n    feature: str\n    id: str\n    sql: str\n    desc: str\n\n\ndef comment_sql(sql: str):\n    new = \"\"\n    for x in sql.split(\"\\n\"):\n        new = new + \"# \" + x + \"\\n\"\n    return new\n\n# Patch the cases that are not supported or need some adjustments to run\ndef fix_sql(old_sql: str):\n    sql = old_sql\n    sql_lower = sql.lower()\n    if sql_lower.startswith(\"select\"):\n        header = \"query I\"\n        footer = \"----\\n\"\n    else:\n        header = \"statement ok\"\n        footer = \"\"\n\n    if 'octets' in sql_lower:\n        header = \"# (UNSUPPORTED: issue 1) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if 'characters' in sql_lower:\n        header = \"# (UNSUPPORTED: issue 1) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if 'char varing' in sql_lower:\n        header = \"# (UNSUPPORTED: issue 2) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if 'as ( c , d )' in sql_lower:\n        header = \"# (UNSUPPORTED: issue 3) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if 'current_time' in sql_lower and not('current_timestamp' in sql_lower):\n        header = \"# (REPLACED: issue 4)\\n\" + header\n        sql = sql.replace(\"CURRENT_TIME\", \"CURRENT_TIMESTAMP\") \n    if 'when 2 , 2' in sql_lower:\n        header = \"# (UNSUPPORTED: issue 5) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if \"( cast ( '01:02:03' as time ) as timestamp )\" in sql_lower:\n        header = \"# (WRONG: issue 6) \" + header\n        sql = comment_sql(sql)\n        footer = \"\"\n    if 'default current_path' in sql_lower:\n         header = \"skipif Postgres\\n\" + header\n         sql = sql + \" --NOT_REWRITE\"\n    if 'default system_user' in sql_lower:\n         header = \"skipif Postgres\\n\" + header\n         sql = sql + \" --NOT_REWRITE\"\n    if 'schema' in sql_lower:\n         header = \"onlyif Postgres\\n\" + header\n    if 'cursor' in sql_lower:\n        header = \"onlyif Postgres\\n\" + header\n    if 'type' in sql_lower:\n        header = \"onlyif Postgres\\n\" + header\n    if 'open cur' in sql_lower:\n        header = \"onlyif Postgres\\n\" + header\n    if 'close cur' in sql_lower:\n        header = \"onlyif Postgres\\n\" + header\n    if 'role' in sql_lower:\n        header = \"onlyif Postgres\\n\" + header\n    if \"select\" in sql_lower:\n        if 'current_time' in sql_lower:\n            sql += \" = CURRENT_TIMESTAMP\" \n        if 'current_date' in sql_lower:\n            sql += \" = CURRENT_DATE\" \n\n    return \"\\n%s\\n%s\\n%s\" % (header, sql, footer)\n\ndef write_sql(file: file, data: Test):\n    if not (isinstance(data.sql, list)):\n        data.sql = [data.sql]\n\n    sql = \";\\n\".join(data.sql)\n    file.write(fix_sql(sql))\n\ndef generate(data: Test):\n    file_name = data.feature.replace(\"-\", \"_\") + \".slt\"\n    file_name = os.path.join(output_path, file_name)\n    print(file_name)\n    if os.path.exists(file_name):\n        with open(file_name, 'a') as file:\n            write_sql(file, data)\n    else:\n        with open(file_name, 'w') as file:\n            file.write(\"# %s: %s\\n\" % (data.feature, data.desc))\n            write_sql(file, data)\n\n\ntotal = 0\nfor file_path in tests_paths:\n    print(file_path)\n    with open(file_path, 'r') as file:\n        documents = yaml.safe_load_all(file)\n        for row in documents:\n            # print(row)\n            # print(\"\\n\")\n            feature = row['feature']\n            if feature in mandatory:\n                desc = mandatory[feature]\n            else:\n                continue\n\n            t = Test(feature=feature, id=row['id'], sql=row['sql'],  desc=desc)\n            generate(t)\n            total += 1\nprint(total)\n"
  },
  {
    "path": "crates/sqltest/clippy.toml",
    "content": "# Overwrite the root's disallowed-macros,\n# as sqltest is allowed to use println and friends.\ndisallowed-macros = []\n"
  },
  {
    "path": "crates/sqltest/override_with_output.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [ \"$#\" -lt \"2\" ] ; then\n  echo \"Usage: $0 <glob> <engine>\"\n  exit 1\nfi\n\ncd \"$(dirname \"$0\")\"\n\n#Override the files with the output generated by the engine\ncargo run --color=always $1 --engine $2 --override"
  },
  {
    "path": "crates/sqltest/reformat.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [ \"$#\" -lt \"1\" ] ; then\n  echo \"Usage: $0 <glob>\"\n  exit 1\nfi\n\ncd \"$(dirname \"$0\")\"\n\ncargo run --color=always $1 --format"
  },
  {
    "path": "crates/sqltest/run_all_sequential.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [ \"$#\" -lt \"1\" ] ; then\n  echo \"Usage: $0 <engine>\"\n  exit 1\nfi\n\ncd \"$(dirname \"$0\")\"\n\n#Run in sequence\ncargo run --color=always './test/**/*.slt' --engine $1"
  },
  {
    "path": "crates/sqltest/src/db.rs",
    "content": "use crate::pg::Pg;\nuse crate::space::{Kind, SpaceDb};\nuse crate::sqlite::Sqlite;\nuse async_trait::async_trait;\nuse spacetimedb::error::DBError;\nuse sqllogictest::{AsyncDB, DBOutput};\n\npub enum DBRunner {\n    Sqlite(Sqlite),\n    Space(SpaceDb),\n    Pg(Pg),\n}\n\n#[async_trait]\nimpl AsyncDB for DBRunner {\n    type Error = DBError;\n    type ColumnType = Kind;\n\n    async fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {\n        let mut last = None;\n        for x in sql.split('\\n') {\n            last = Some(match self {\n                DBRunner::Space(db) => db.run(x).await?,\n                DBRunner::Sqlite(db) => db.run(x).await.map_err(|err| DBError::Other(err.into()))?,\n                DBRunner::Pg(db) => db.run(x).await.map_err(|err| DBError::Other(err.into()))?,\n            })\n        }\n\n        Ok(last.unwrap())\n    }\n}\n"
  },
  {
    "path": "crates/sqltest/src/main.rs",
    "content": "// Forked from https://github.com/risinglightdb/sqllogictest-rs/tree/main/sqllogictest-bin/src\nmod db;\nmod pg;\nmod space;\nmod sqlite;\n\nuse std::fs::{File, OpenOptions};\nuse std::io::Write;\nuse std::io::{Read, Seek, SeekFrom};\nuse std::path::{Path, PathBuf};\nuse std::time::{Duration, Instant};\n\nuse crate::db::DBRunner;\nuse crate::pg::Pg;\nuse crate::space::SpaceDb;\nuse crate::sqlite::Sqlite;\nuse anyhow::{anyhow, bail, Context};\nuse chrono::Local;\nuse clap::{Parser, ValueEnum};\nuse console::style;\nuse itertools::Itertools;\nuse quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite};\nuse spacetimedb::error::DBError;\nuse sqllogictest::{\n    default_validator, strict_column_validator, update_record_with_output, AsyncDB, Injected, MakeConnection, Record,\n    Runner,\n};\n\n#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]\n#[clap(rename_all = \"PascalCase\")]\nenum DbType {\n    #[default]\n    SpacetimeDB,\n    Sqlite,\n    Postgres,\n}\n\n#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]\n#[must_use]\npub enum Color {\n    #[default]\n    Auto,\n    Always,\n    Never,\n}\n\n#[derive(Parser, Debug, Clone)]\n#[clap(about, version, author)]\nstruct Args {\n    /// Glob(s) of a set of test files.\n    /// Example: `./test/**/*.slt`\n    #[clap(required = true, num_args = 1..)]\n    files: Vec<String>,\n\n    /// The database engine name.\n    #[clap(short, long, value_enum, default_value = \"SpacetimeDB\")]\n    engine: DbType,\n\n    /// Whether to enable colors in the output.\n    #[clap(long, value_enum, default_value_t, value_name = \"WHEN\")]\n    color: Color,\n\n    /// Whether to enable parallel test. One database will be created for each test file.\n    #[clap(long, short)]\n    jobs: Option<usize>,\n\n    /// Overrides the test files with the actual output of the database.\n    #[clap(long)]\n    r#override: bool,\n    /// Reformat the test files.\n    #[clap(long)]\n    format: bool,\n}\n\nconst TEST_NAME: &str = \"sqllogictest\";\n\nasync fn flush(out: &mut impl std::io::Write) -> std::io::Result<()> {\n    tokio::task::block_in_place(|| out.flush())\n}\n\n#[tokio::main]\nasync fn main() -> anyhow::Result<()> {\n    let args = Args::parse();\n\n    match args.color {\n        Color::Always => {\n            console::set_colors_enabled(true);\n            console::set_colors_enabled_stderr(true);\n        }\n        Color::Never => {\n            console::set_colors_enabled(false);\n            console::set_colors_enabled_stderr(false);\n        }\n        Color::Auto => {}\n    }\n\n    let glob_patterns = args.files;\n    let mut files: Vec<PathBuf> = Vec::new();\n    for glob_pattern in glob_patterns.into_iter() {\n        let pathbufs = glob::glob(&glob_pattern).context(\"failed to read glob pattern\")?;\n        for pathbuf in pathbufs.filter_map(Result::ok) {\n            files.push(pathbuf)\n        }\n    }\n\n    if files.is_empty() {\n        bail!(\"no test case found\");\n    }\n    if args.r#override || args.format {\n        return update_test_files(files, args.engine, args.format).await;\n    }\n\n    let mut report = Report::new(TEST_NAME.to_string());\n    report.set_timestamp(Local::now());\n\n    let mut test_suite = TestSuite::new(TEST_NAME);\n    test_suite.set_timestamp(Local::now());\n\n    let result = run_serial(&mut test_suite, files, args.engine).await;\n\n    report.add_test_suite(test_suite);\n\n    result\n}\n\nasync fn open_db(engine: DbType) -> Result<DBRunner, DBError> {\n    Ok(match engine {\n        DbType::SpacetimeDB => SpaceDb::new()?.into_db(),\n        DbType::Sqlite => Sqlite::new().map_err(DBError::Other)?.into_db(),\n        DbType::Postgres => Pg::new().await.map_err(DBError::Other)?.into_db(),\n    })\n}\n\n/// Run test one by one\nasync fn run_serial(test_suite: &mut TestSuite, files: Vec<PathBuf>, engine: DbType) -> anyhow::Result<()> {\n    let mut failed_case = vec![];\n    let start = Instant::now();\n\n    for file in files {\n        let db_name = format!(\"{engine:?}\");\n        let runner = Runner::new(|| open_db(engine));\n        let filename = file.to_string_lossy().to_string();\n        let test_case_name = filename.replace(['/', ' ', '.', '-'], \"_\");\n        let case = match run_test_file(&mut std::io::stdout(), runner, &file, &db_name).await {\n            Ok((skipped, duration)) => {\n                let status = if skipped {\n                    TestCaseStatus::skipped()\n                } else {\n                    TestCaseStatus::success()\n                };\n\n                let mut case = TestCase::new(test_case_name, status);\n\n                case.set_time(duration);\n                case.set_timestamp(Local::now());\n                case.set_classname(TEST_NAME);\n                case\n            }\n            Err(e) => {\n                println!(\"{}\\n\\n{:?}\", style(\"[FAILED]\").red().bold(), e);\n                println!();\n                failed_case.push(filename.clone());\n                let mut status = TestCaseStatus::non_success(NonSuccessKind::Failure);\n                status.set_type(\"test failure\");\n                let mut case = TestCase::new(test_case_name, status);\n                case.set_timestamp(Local::now());\n                case.set_classname(TEST_NAME);\n                case.set_system_err(e.to_string());\n                case.set_time(Duration::from_millis(0));\n                case.set_system_out(\"\");\n                case\n            }\n        };\n        test_suite.add_test_case(case);\n    }\n\n    if !failed_case.is_empty() {\n        println!(\"some test case failed:\\n{failed_case:#?}\");\n    }\n    println!();\n\n    let total = test_suite.tests - test_suite.disabled;\n    let failed = test_suite.failures + test_suite.errors;\n    let ok = total - failed;\n    println!(\"{}: {}\", style(\"[FAILED]\").red().bold(), failed);\n    println!(\"{}: {}\", style(\"[SKIP  ]\").yellow().bold(), test_suite.disabled);\n    println!(\"{}: {}\", style(\"[OK    ]\").green().bold(), ok);\n    println!(\"{}: {}%\", style(\"[PASS  ]\").blue().bold(), (ok * 100) / total);\n    println!(\"{}: {:?}\", style(\"[Elapsed]\").reverse().bold(), start.elapsed());\n\n    Ok(())\n}\n\n/// Different from [`Runner::run_file_async`], we re-implement it here to print some progress\n/// information.\nasync fn run_test_file<T: std::io::Write, D: AsyncDB, M: MakeConnection<Conn = D>>(\n    out: &mut T,\n    mut runner: Runner<D, M>,\n    filename: impl AsRef<Path>,\n    db_name: &str,\n) -> anyhow::Result<(bool, Duration)> {\n    let filename = filename.as_ref();\n    let records = sqllogictest::parse_file(filename).map_err(|e| anyhow!(\"{e:?}\"))?;\n\n    let mut begin_times = vec![];\n    let mut did_pop = false;\n    let mut skipped = 0;\n    let mut total = 0;\n\n    writeln!(out, \"{: <60} .. \", filename.to_string_lossy())?;\n    flush(out).await?;\n\n    begin_times.push(Instant::now());\n\n    for record in records {\n        match &record {\n            Record::Injected(Injected::BeginInclude(file)) => {\n                begin_times.push(Instant::now());\n                if !did_pop {\n                    writeln!(out, \"{}\", style(\"[BEGIN]\").blue().bold())?;\n                } else {\n                    writeln!(out)?;\n                }\n                did_pop = false;\n                write!(out, \"{}{: <60} .. \", \"| \".repeat(begin_times.len() - 1), file)?;\n                flush(out).await?;\n            }\n            Record::Injected(Injected::EndInclude(file)) => {\n                finish_test_file(out, &mut begin_times, &mut did_pop, file, total - skipped == 0).await?;\n            }\n            _ => {}\n        }\n\n        let will_skip = match &record {\n            Record::Statement { conditions, .. } | Record::Query { conditions, .. } => {\n                total += 1;\n                conditions.iter().any(|c| should_skip(c, [db_name]))\n            }\n            _ => false,\n        };\n\n        if will_skip {\n            skipped += 1;\n        }\n\n        runner\n            .run_async(record)\n            .await\n            .map_err(|e| anyhow!(\"{}\", e.display(console::colors_enabled())))\n            .context(format!(\"failed to run `{}`\", style(filename.to_string_lossy()).bold()))?;\n    }\n\n    let duration = begin_times[0].elapsed();\n\n    let skipped = total - skipped == 0;\n\n    finish_test_file(\n        out,\n        &mut begin_times,\n        &mut did_pop,\n        &filename.to_string_lossy(),\n        skipped,\n    )\n    .await?;\n\n    writeln!(out)?;\n\n    Ok((skipped, duration))\n}\n\n// sqllogictest::Condition::should_skip\nfn should_skip<'a>(c: &'a sqllogictest::Condition, labels: impl IntoIterator<Item = &'a str>) -> bool {\n    match c {\n        sqllogictest::Condition::OnlyIf { label } => !labels.into_iter().contains(&label.as_str()),\n        sqllogictest::Condition::SkipIf { label } => labels.into_iter().contains(&label.as_str()),\n    }\n}\n\nasync fn finish_test_file<T: std::io::Write>(\n    out: &mut T,\n    time_stack: &mut Vec<Instant>,\n    did_pop: &mut bool,\n    file: &str,\n    skipped: bool,\n) -> anyhow::Result<()> {\n    let begin_time = time_stack.pop().unwrap();\n    let result = if skipped {\n        style(\"[SKIP]\").yellow().strikethrough().bold()\n    } else {\n        style(\"[OK]\").green().bold()\n    };\n\n    if *did_pop {\n        // start a new line if the result is not immediately after the item\n        write!(\n            out,\n            \"\\n{}{} {: <54} .. {} in {} ms\",\n            \"| \".repeat(time_stack.len()),\n            style(\"[END]\").blue().bold(),\n            file,\n            result,\n            begin_time.elapsed().as_millis()\n        )?;\n    } else {\n        // otherwise, append time to the previous line\n        write!(out, \"{} in {} ms\", result, begin_time.elapsed().as_millis())?;\n    }\n\n    *did_pop = true;\n\n    Ok::<_, anyhow::Error>(())\n}\n\n/// * `format` - If true, will not run sqls, only formats the file.\nasync fn update_test_files(files: Vec<PathBuf>, engine: DbType, format: bool) -> anyhow::Result<()> {\n    for file in files {\n        let runner = Runner::new(|| open_db(engine));\n\n        if let Err(e) = update_test_file(&mut std::io::stdout(), runner, &file, format).await {\n            {\n                println!(\"{}\\n\\n{:?}\", style(\"[FAILED]\").red().bold(), e);\n                println!();\n            }\n        };\n    }\n\n    Ok(())\n}\n\n/// Different from [`sqllogictest::update_test_file`], we re-implement it here to print some\n/// progress information.\nasync fn update_test_file<T: std::io::Write, D: AsyncDB, M: MakeConnection<Conn = D>>(\n    out: &mut T,\n    mut runner: Runner<D, M>,\n    filename: impl AsRef<Path>,\n    format: bool,\n) -> anyhow::Result<()> {\n    let filename = filename.as_ref();\n    let records = tokio::task::block_in_place(|| sqllogictest::parse_file(filename).map_err(|e| anyhow!(\"{e:?}\")))\n        .context(\"failed to parse sqllogictest file\")?;\n\n    let mut begin_times = vec![];\n    let mut did_pop = false;\n\n    write!(out, \"{: <60} .. \", filename.to_string_lossy())?;\n    flush(out).await?;\n\n    begin_times.push(Instant::now());\n\n    fn create_outfile(filename: impl AsRef<Path>) -> std::io::Result<(PathBuf, File)> {\n        let filename = filename.as_ref();\n        let outfilename = filename.file_name().unwrap().to_str().unwrap().to_owned() + \".temp\";\n        let outfilename = filename.parent().unwrap().join(outfilename);\n        // create a temp file in read-write mode\n        let outfile = OpenOptions::new()\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .read(true)\n            .open(&outfilename)?;\n        Ok((outfilename, outfile))\n    }\n\n    fn override_with_outfile(filename: &String, outfilename: &PathBuf, outfile: &mut File) -> std::io::Result<()> {\n        // check whether outfile ends with multiple newlines, which happens if\n        // - the last record is statement/query\n        // - the original file ends with multiple newlines\n\n        const N: usize = 8;\n        let mut buf = [0u8; N];\n        loop {\n            outfile.seek(SeekFrom::End(-(N as i64))).unwrap();\n            outfile.read_exact(&mut buf).unwrap();\n            let num_newlines = buf.iter().rev().take_while(|&&b| b == b'\\n').count();\n            assert!(num_newlines > 0);\n\n            if num_newlines > 1 {\n                // if so, remove the last ones\n                outfile\n                    .set_len(outfile.metadata().unwrap().len() - num_newlines as u64 + 1)\n                    .unwrap();\n            }\n\n            if num_newlines == 1 || num_newlines < N {\n                break;\n            }\n        }\n\n        fs_err::rename(outfilename, filename)?;\n\n        Ok(())\n    }\n\n    struct Item {\n        filename: String,\n        outfilename: PathBuf,\n        outfile: File,\n        halt: bool,\n    }\n    let (outfilename, outfile) = create_outfile(filename)?;\n    let mut stack = vec![Item {\n        filename: filename.to_string_lossy().to_string(),\n        outfilename,\n        outfile,\n        halt: false,\n    }];\n\n    for record in records {\n        let Item {\n            filename,\n            outfilename,\n            outfile,\n            halt,\n        } = stack.last_mut().unwrap();\n\n        match &record {\n            Record::Injected(Injected::BeginInclude(filename)) => {\n                let (outfilename, outfile) = create_outfile(filename)?;\n                stack.push(Item {\n                    filename: filename.clone(),\n                    outfilename,\n                    outfile,\n                    halt: false,\n                });\n\n                begin_times.push(Instant::now());\n                if !did_pop {\n                    writeln!(out, \"{}\", style(\"[BEGIN]\").blue().bold())?;\n                } else {\n                    writeln!(out)?;\n                }\n                did_pop = false;\n                write!(out, \"{}{: <60} .. \", \"| \".repeat(begin_times.len() - 1), filename)?;\n                flush(out).await?;\n            }\n            Record::Injected(Injected::EndInclude(file)) => {\n                override_with_outfile(filename, outfilename, outfile)?;\n                stack.pop();\n                finish_test_file(out, &mut begin_times, &mut did_pop, file, false).await?;\n            }\n            _ => {\n                if *halt {\n                    writeln!(outfile, \"{record}\")?;\n                    continue;\n                }\n                if matches!(record, Record::Halt { .. }) {\n                    *halt = true;\n                    writeln!(outfile, \"{record}\")?;\n                    continue;\n                }\n                match &record {\n                    Record::Statement { sql, .. } => {\n                        if sql.contains(\"NOT_REWRITE\") {\n                            continue;\n                        }\n                    }\n                    Record::Query { sql, .. } => {\n                        if sql.contains(\"NOT_REWRITE\") {\n                            continue;\n                        }\n                    }\n                    _ => (),\n                }\n                update_record(outfile, &mut runner, record, format)\n                    .await\n                    .context(format!(\"failed to run `{}`\", style(filename).bold()))?;\n            }\n        }\n    }\n\n    finish_test_file(out, &mut begin_times, &mut did_pop, &filename.to_string_lossy(), false).await?;\n\n    let Item {\n        filename,\n        outfilename,\n        outfile,\n        halt: _,\n    } = stack.last_mut().unwrap();\n    override_with_outfile(filename, outfilename, outfile)?;\n\n    Ok(())\n}\n\nasync fn update_record<D: AsyncDB, M: MakeConnection<Conn = D>>(\n    outfile: &mut File,\n    runner: &mut Runner<D, M>,\n    record: Record<D::ColumnType>,\n    format: bool,\n) -> anyhow::Result<()> {\n    assert!(!matches!(record, Record::Injected(_)));\n\n    if format {\n        writeln!(outfile, \"{record}\")?;\n        return Ok(());\n    }\n\n    let record_output = runner.apply_record(record.clone()).await;\n    match update_record_with_output(\n        &record,\n        &record_output,\n        \"\\t\",\n        default_validator,\n        strict_column_validator,\n    ) {\n        Some(new_record) => {\n            writeln!(outfile, \"{new_record}\")?;\n        }\n        None => {\n            writeln!(outfile, \"{record}\")?;\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/sqltest/src/pg.rs",
    "content": "use crate::db::DBRunner;\nuse crate::space::Kind;\nuse async_trait::async_trait;\nuse chrono::Local;\nuse derive_more::From;\nuse postgres_types::{FromSql, Type};\nuse rust_decimal::Decimal;\nuse spacetimedb_sats::AlgebraicType;\nuse sqllogictest::{AsyncDB, DBOutput};\nuse std::time::Duration;\nuse tokio_postgres::{Client, Column, NoTls};\n\nfn columns(cols: &[Column]) -> Vec<(String, AlgebraicType)> {\n    cols.iter()\n        .map(|col| {\n            let kind = kind(col.type_());\n\n            (col.name().to_string(), kind)\n        })\n        .collect::<Vec<_>>()\n}\n\nfn kind(ty: &Type) -> AlgebraicType {\n    match ty {\n        &Type::VARCHAR | &Type::TEXT | &Type::BPCHAR | &Type::NAME | &Type::UNKNOWN => AlgebraicType::String,\n        &Type::INT2 => AlgebraicType::I16,\n        &Type::INT4 => AlgebraicType::I32,\n        &Type::INT8 => AlgebraicType::I64,\n        &Type::FLOAT4 => AlgebraicType::F32,\n        &Type::FLOAT8 => AlgebraicType::F64,\n        &Type::BOOL => AlgebraicType::Bool,\n        &Type::NUMERIC => AlgebraicType::F64,\n        &Type::DATE => AlgebraicType::String,\n        &Type::TIME => AlgebraicType::String,\n        &Type::TIMESTAMP => AlgebraicType::String,\n        &Type::TIMESTAMPTZ => AlgebraicType::String,\n        _ => unimplemented!(\"{}\", ty),\n    }\n}\n\nconst SQL_DROP: &str = \"\\\nDO\n$function$\nDECLARE\n  _schema VARCHAR;\nBEGIN\n    FOR _schema IN\n        SELECT schema_name\n        FROM information_schema.schemata\n        WHERE schema_name NOT LIKE 'pg_%' AND schema_name <> 'information_schema'\n    LOOP\n        RAISE NOTICE 'SCHEMA.. :%', _schema;\n        EXECUTE 'DROP SCHEMA IF EXISTS ' || _schema || ' CASCADE';\n    END LOOP;\n    CREATE SCHEMA public;\n\n\n    FOR _schema IN\n        SELECT rolname\n        FROM pg_roles\n        WHERE rolname LIKE 'role_%'\n    LOOP\n        RAISE NOTICE 'ROLE.. :%', _schema;\n        EXECUTE 'DROP ROLE IF EXISTS ' || _schema;\n    END LOOP;\nEND\n$function$;\n\";\n\npub struct Pg {\n    pub(crate) client: Client,\n}\n\nimpl Pg {\n    pub async fn new() -> anyhow::Result<Self> {\n        let (client, conn) = tokio_postgres::connect(\"postgresql://postgres@localhost/TestSpace\", NoTls).await?;\n        tokio::spawn(async move {\n            if let Err(e) = conn.await {\n                eprintln!(\"connection error: {e}\");\n            }\n        });\n        client.batch_execute(SQL_DROP).await?;\n        Ok(Self { client })\n    }\n\n    pub fn into_db(self) -> DBRunner {\n        DBRunner::Pg(self)\n    }\n}\n\n#[async_trait]\nimpl AsyncDB for Pg {\n    type Error = tokio_postgres::Error;\n    type ColumnType = Kind;\n\n    async fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {\n        let stmt = self.client.prepare(sql).await?;\n\n        let is_query_sql = {\n            let lower_sql = sql.trim_start().to_ascii_lowercase();\n            lower_sql.starts_with(\"select\")\n                || (lower_sql.starts_with(\"insert\")\n                    || lower_sql.starts_with(\"update\")\n                    || lower_sql.starts_with(\"delete\"))\n        };\n        if !is_query_sql {\n            self.client.execute(&stmt, &[]).await?;\n            return Ok(DBOutput::StatementComplete(0));\n        }\n\n        let cols = columns(stmt.columns());\n        let rows = self.client.query(&stmt, &[]).await?;\n        let mut data = Vec::new();\n\n        for row in rows {\n            let mut new = Vec::with_capacity(row.columns().len());\n\n            for col in row.columns() {\n                let value = row.get::<&str, Scalar>(col.name());\n\n                new.push(value.0);\n            }\n\n            data.push(new);\n        }\n\n        Ok(DBOutput::Rows {\n            types: cols.into_iter().map(|(_, ty)| Kind(ty)).collect(),\n            rows: data,\n        })\n    }\n\n    fn engine_name(&self) -> &str {\n        \"Postgres\"\n    }\n\n    async fn sleep(dur: Duration) {\n        tokio::time::sleep(dur).await\n    }\n}\n\n#[derive(From)]\npub struct Scalar(String);\n\nimpl FromSql<'_> for Scalar {\n    fn from_sql(ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {\n        let value = match ty {\n            &Type::VARCHAR | &Type::TEXT | &Type::BPCHAR | &Type::NAME | &Type::UNKNOWN => {\n                let x: Option<String> = FromSql::from_sql(ty, raw)?;\n                if let Some(x) = x {\n                    format!(\"'{x}'\").into()\n                } else {\n                    \"null\".to_string().into()\n                }\n            }\n\n            &Type::INT2 | &Type::INT4 => {\n                let x: i32 = FromSql::from_sql(ty, raw)?;\n                x.to_string().into()\n            }\n            &Type::INT8 => {\n                let x: i64 = FromSql::from_sql(ty, raw)?;\n                x.to_string().into()\n            }\n            &Type::FLOAT4 => {\n                let x: f32 = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            &Type::FLOAT8 => {\n                let x: f32 = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            &Type::NUMERIC => {\n                let x: Decimal = FromSql::from_sql(ty, raw)?;\n                let txt = x.to_string();\n                if txt.contains('.') {\n                    txt.into()\n                } else {\n                    format!(\"{txt}.0\").into()\n                }\n            }\n            &Type::BOOL => {\n                let x: bool = FromSql::from_sql(ty, raw)?;\n                //for compat with sqlite...\n                let x = if x { \"1\" } else { \"0\" };\n                x.to_string().into()\n            }\n            &Type::DATE => {\n                let x: chrono::NaiveDate = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            &Type::TIME => {\n                let x: chrono::NaiveTime = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            &Type::TIMESTAMP => {\n                let x: chrono::DateTime<Local> = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            &Type::TIMESTAMPTZ => {\n                let x: chrono::DateTime<Local> = FromSql::from_sql(ty, raw)?;\n                format!(\"{x:?}\").into()\n            }\n            _ => unimplemented!(\"{}\", ty),\n        };\n        Ok(value)\n    }\n\n    fn from_sql_null(_ty: &Type) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {\n        Ok(\"null\".to_string().into())\n    }\n\n    fn accepts(ty: &Type) -> bool {\n        match ty {\n            &Type::VARCHAR\n            | &Type::TEXT\n            | &Type::BPCHAR\n            | &Type::NAME\n            | &Type::UNKNOWN\n            | &Type::BOOL\n            | &Type::INT2\n            | &Type::INT4\n            | &Type::INT8\n            | &Type::FLOAT4\n            | &Type::FLOAT8\n            | &Type::NUMERIC\n            | &Type::DATE\n            | &Type::TIME\n            | &Type::TIMESTAMP\n            | &Type::TIMESTAMPTZ => true,\n            ty if ty.name() == \"citext\" => true,\n            _ => false,\n        }\n    }\n}\n"
  },
  {
    "path": "crates/sqltest/src/space.rs",
    "content": "use crate::db::DBRunner;\nuse async_trait::async_trait;\nuse spacetimedb::db::relational_db::tests_utils::TestDB;\nuse spacetimedb::error::DBError;\nuse spacetimedb::sql::execute::{run, SqlResult};\nuse spacetimedb::subscription::module_subscription_actor::ModuleSubscriptions;\nuse spacetimedb_lib::identity::AuthCtx;\nuse spacetimedb_sats::algebraic_value::Packed;\nuse spacetimedb_sats::meta_type::MetaType;\nuse spacetimedb_sats::satn::Satn;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductValue};\nuse sqllogictest::{AsyncDB, ColumnType, DBOutput};\nuse std::fs;\nuse std::io::Write;\nuse std::sync::Arc;\n\n#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct Kind(pub(crate) AlgebraicType);\n\nimpl ColumnType for Kind {\n    fn from_char(value: char) -> Option<Self> {\n        match value {\n            'B' => Some(Kind(AlgebraicType::Bool)),\n            'T' => Some(Kind(AlgebraicType::String)),\n            'I' => Some(Kind(AlgebraicType::I64)),\n            'R' => Some(Kind(AlgebraicType::F32)),\n            _ => Some(Kind(AlgebraicType::meta_type())),\n        }\n    }\n\n    fn to_char(&self) -> char {\n        match &self.0 {\n            AlgebraicType::Array(_) => '?',\n            ty if ty.is_integer() => 'I',\n            ty if ty.is_float() => 'R',\n            AlgebraicType::String => 'T',\n            AlgebraicType::Bool => 'B',\n            AlgebraicType::Ref(_) | AlgebraicType::Sum(_) | AlgebraicType::Product(_) => '!',\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[allow(dead_code)]\nfn append_file(to: &std::path::Path, content: &str) -> anyhow::Result<()> {\n    let mut f = fs::OpenOptions::new().create(true).append(true).open(to)?;\n\n    f.write_all(format!(\"{content}\\n\").as_bytes())?;\n\n    Ok(())\n}\n\npub struct SpaceDb {\n    pub(crate) conn: TestDB,\n    auth: AuthCtx,\n}\n\nimpl SpaceDb {\n    pub fn new() -> anyhow::Result<Self> {\n        let conn = TestDB::durable()?;\n        Ok(Self {\n            conn,\n            auth: AuthCtx::for_testing(),\n        })\n    }\n\n    pub(crate) fn run_sql(&self, sql: &str) -> anyhow::Result<(Vec<Kind>, Vec<ProductValue>)> {\n        let (subs, runtime) = ModuleSubscriptions::for_test_new_runtime(Arc::clone(&self.conn.db));\n        let mut head = Vec::new();\n        let SqlResult { rows, .. } = runtime.block_on(run(\n            Arc::clone(&self.conn.db),\n            sql.to_string(),\n            self.auth.clone(),\n            Some(subs),\n            None,\n            &mut head,\n        ))?;\n\n        let header = head.into_iter().map(|(_, ty)| Kind(ty)).collect();\n\n        // Remove comments to see which SQL worked. Can't collect it outside from lack of a hook in\n        // the external `sqllogictest` crate. :(\n        // append_file(&std::path::PathBuf::from(\".ok.sql\"), sql)?;\n        Ok((header, rows))\n    }\n\n    pub fn into_db(self) -> DBRunner {\n        DBRunner::Space(self)\n    }\n}\n\n#[async_trait]\nimpl AsyncDB for SpaceDb {\n    type Error = DBError;\n    type ColumnType = Kind;\n\n    async fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {\n        let (header, rows) = self.run_sql(sql)?;\n        if header.is_empty() {\n            return Ok(DBOutput::StatementComplete(0));\n        }\n\n        let output: Vec<Vec<_>> = rows\n            .into_iter()\n            .map(|row| {\n                row.into_iter()\n                    .map(|value| match value {\n                        AlgebraicValue::Bool(x) => if x { \"1\" } else { \"0\" }.to_string(),\n                        // ^-- For compat with sqlite.\n                        AlgebraicValue::I8(x) => x.to_string(),\n                        AlgebraicValue::U8(x) => x.to_string(),\n                        AlgebraicValue::I16(x) => x.to_string(),\n                        AlgebraicValue::U16(x) => x.to_string(),\n                        AlgebraicValue::I32(x) => x.to_string(),\n                        AlgebraicValue::U32(x) => x.to_string(),\n                        AlgebraicValue::I64(x) => x.to_string(),\n                        AlgebraicValue::U64(x) => x.to_string(),\n                        AlgebraicValue::I128(Packed(x)) => x.to_string(),\n                        AlgebraicValue::U128(Packed(x)) => x.to_string(),\n                        AlgebraicValue::I256(x) => x.to_string(),\n                        AlgebraicValue::U256(x) => x.to_string(),\n                        AlgebraicValue::F32(x) => format!(\"{:?}\", x.as_ref()),\n                        AlgebraicValue::F64(x) => format!(\"{:?}\", x.as_ref()),\n                        AlgebraicValue::String(x) => format!(\"'{x}'\"),\n                        x => x.to_satn(),\n                    })\n                    .collect()\n            })\n            .collect();\n\n        Ok(DBOutput::Rows {\n            types: header,\n            rows: output,\n        })\n    }\n\n    fn engine_name(&self) -> &str {\n        \"SpacetimeDB\"\n    }\n}\n"
  },
  {
    "path": "crates/sqltest/src/sqlite.rs",
    "content": "use crate::db::DBRunner;\nuse crate::space::Kind;\nuse async_trait::async_trait;\nuse rusqlite::types::Value;\nuse spacetimedb_sats::{meta_type::MetaType, AlgebraicType};\nuse sqllogictest::{AsyncDB, DBOutput};\nuse std::path::PathBuf;\nuse tempfile::TempDir;\n\nfn kind(of: &str) -> AlgebraicType {\n    let of = of.to_uppercase();\n    match of.as_str() {\n        \"INT\" => AlgebraicType::I32,\n        \"INTEGER\" => AlgebraicType::I32,\n        \"BIGINT\" => AlgebraicType::I64,\n        \"BOOLEAN\" => AlgebraicType::Bool,\n        \"VARCHAR\" => AlgebraicType::String,\n        \"TEXT\" => AlgebraicType::String,\n        \"REAL\" => AlgebraicType::F32,\n        x => {\n            if of.starts_with(\"VARCHAR\") {\n                AlgebraicType::String\n            } else {\n                unimplemented!(\"sqlite kind {}\", x)\n            }\n        }\n    }\n}\n\nfn columns(stmt: &mut rusqlite::Statement) -> Vec<(String, AlgebraicType)> {\n    stmt.columns()\n        .iter()\n        .map(|col| {\n            let kind = col.decl_type().map(kind).unwrap_or_else(AlgebraicType::meta_type);\n\n            (col.name().to_string(), kind)\n        })\n        .collect::<Vec<_>>()\n}\n\npub struct Sqlite {\n    pub(crate) conn: rusqlite::Connection,\n    #[allow(dead_code)]\n    pub(crate) tmp_dir: TempDir,\n}\n\nimpl Sqlite {\n    pub fn new() -> anyhow::Result<Self> {\n        let tmp_dir = TempDir::with_prefix(\"sqlite_test\")?;\n        let mut file = PathBuf::from(tmp_dir.path());\n        file.push(\"db.db\");\n\n        let conn = rusqlite::Connection::open(file)?;\n\n        Ok(Self { conn, tmp_dir })\n    }\n\n    pub fn into_db(self) -> DBRunner {\n        DBRunner::Sqlite(self)\n    }\n}\n\n#[async_trait]\nimpl AsyncDB for Sqlite {\n    type Error = rusqlite::Error;\n    type ColumnType = Kind;\n\n    async fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {\n        let mut stmt = self.conn.prepare(sql)?;\n\n        let lower_sql = sql.trim_start().to_ascii_lowercase();\n        let is_query_sql = {\n            lower_sql.starts_with(\"select\")\n                || (lower_sql.starts_with(\"insert\")\n                    || lower_sql.starts_with(\"update\")\n                    || lower_sql.starts_with(\"delete\"))\n        };\n        if !is_query_sql {\n            stmt.execute([])?;\n            return Ok(DBOutput::StatementComplete(0));\n        }\n\n        let mut columns = columns(&mut stmt);\n        let mut rows = stmt.query([])?;\n        let mut data = Vec::new();\n        let mut meta = AlgebraicType::meta_type();\n\n        while let Some(row) = rows.next()? {\n            let mut new = Vec::with_capacity(columns.len());\n\n            for (name, dectype) in &mut columns {\n                let value = row.get::<_, Value>(name.as_str())?;\n                let (value, kind) = match value {\n                    Value::Null => (\"null\".into(), AlgebraicType::never()),\n                    Value::Integer(x) => (x.to_string(), AlgebraicType::I64),\n                    Value::Real(x) => (format!(\"{x:?}\"), AlgebraicType::F64),\n                    Value::Text(x) => (format!(\"'{x}'\"), AlgebraicType::String),\n                    _ => unimplemented!(\"Sqlite from\"),\n                };\n                if dectype == &mut meta {\n                    *dectype = kind;\n                }\n                new.push(value);\n            }\n\n            data.push(new);\n        }\n\n        Ok(DBOutput::Rows {\n            types: columns.into_iter().map(|(_, ty)| Kind(ty)).collect(),\n            rows: data,\n        })\n    }\n\n    fn engine_name(&self) -> &str {\n        \"Sqlite\"\n    }\n}\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-01.tests.yml",
    "content": "feature: E011-01\nid: e011_01_01_01\nsql: CREATE TABLE TABLE_E011_01_01_01 ( A BIGINT )\n---\nfeature: E011-01\nid: e011_01_01_02\nsql: CREATE TABLE TABLE_E011_01_01_02 ( A INT )\n---\nfeature: E011-01\nid: e011_01_01_03\nsql: CREATE TABLE TABLE_E011_01_01_03 ( A INTEGER )\n---\nfeature: E011-01\nid: e011_01_01_04\nsql: CREATE TABLE TABLE_E011_01_01_04 ( A SMALLINT )\n---\nfeature: E011-01\nid: e011_01_02_01\nsql: SELECT +5\n---\nfeature: E011-01\nid: e011_01_02_02\nsql: SELECT -5\n---\nfeature: E011-01\nid: e011_01_02_03\nsql: SELECT 5\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-02.tests.yml",
    "content": "feature: E011-02\nid: e011_02_01_01\nsql: CREATE TABLE TABLE_E011_02_01_01 ( A DOUBLE PRECISION )\n---\nfeature: E011-02\nid: e011_02_01_02\nsql: CREATE TABLE TABLE_E011_02_01_02 ( A FLOAT ( 2 ) )\n---\nfeature: E011-02\nid: e011_02_01_03\nsql: CREATE TABLE TABLE_E011_02_01_03 ( A FLOAT )\n---\nfeature: E011-02\nid: e011_02_01_04\nsql: CREATE TABLE TABLE_E011_02_01_04 ( A REAL )\n---\nfeature: E011-02\nid: e011_02_02_01\nsql: SELECT +7.8\n---\nfeature: E011-02\nid: e011_02_02_02\nsql: SELECT -7.8\n---\nfeature: E011-02\nid: e011_02_03_01\nsql: SELECT +.2\n---\nfeature: E011-02\nid: e011_02_03_02\nsql: SELECT +.2E+2\n---\nfeature: E011-02\nid: e011_02_03_03\nsql: SELECT +.2E-2\n---\nfeature: E011-02\nid: e011_02_03_04\nsql: SELECT +.2E2\n---\nfeature: E011-02\nid: e011_02_03_05\nsql: SELECT +2\n---\nfeature: E011-02\nid: e011_02_03_06\nsql: SELECT +2.\n---\nfeature: E011-02\nid: e011_02_03_07\nsql: SELECT +2.2\n---\nfeature: E011-02\nid: e011_02_03_08\nsql: SELECT +2.2E+2\n---\nfeature: E011-02\nid: e011_02_03_09\nsql: SELECT +2.2E-2\n---\nfeature: E011-02\nid: e011_02_03_10\nsql: SELECT +2.2E2\n---\nfeature: E011-02\nid: e011_02_03_11\nsql: SELECT +2.E+2\n---\nfeature: E011-02\nid: e011_02_03_12\nsql: SELECT +2.E-2\n---\nfeature: E011-02\nid: e011_02_03_13\nsql: SELECT +2.E2\n---\nfeature: E011-02\nid: e011_02_03_14\nsql: SELECT +2E+2\n---\nfeature: E011-02\nid: e011_02_03_15\nsql: SELECT +2E-2\n---\nfeature: E011-02\nid: e011_02_03_16\nsql: SELECT +2E2\n---\nfeature: E011-02\nid: e011_02_03_17\nsql: SELECT -.2\n---\nfeature: E011-02\nid: e011_02_03_18\nsql: SELECT -.2E+2\n---\nfeature: E011-02\nid: e011_02_03_19\nsql: SELECT -.2E-2\n---\nfeature: E011-02\nid: e011_02_03_20\nsql: SELECT -.2E2\n---\nfeature: E011-02\nid: e011_02_03_21\nsql: SELECT -2\n---\nfeature: E011-02\nid: e011_02_03_22\nsql: SELECT -2.\n---\nfeature: E011-02\nid: e011_02_03_23\nsql: SELECT -2.2\n---\nfeature: E011-02\nid: e011_02_03_24\nsql: SELECT -2.2E+2\n---\nfeature: E011-02\nid: e011_02_03_25\nsql: SELECT -2.2E-2\n---\nfeature: E011-02\nid: e011_02_03_26\nsql: SELECT -2.2E2\n---\nfeature: E011-02\nid: e011_02_03_27\nsql: SELECT -2.E+2\n---\nfeature: E011-02\nid: e011_02_03_28\nsql: SELECT -2.E-2\n---\nfeature: E011-02\nid: e011_02_03_29\nsql: SELECT -2.E2\n---\nfeature: E011-02\nid: e011_02_03_30\nsql: SELECT -2E+2\n---\nfeature: E011-02\nid: e011_02_03_31\nsql: SELECT -2E-2\n---\nfeature: E011-02\nid: e011_02_03_32\nsql: SELECT -2E2\n---\nfeature: E011-02\nid: e011_02_03_33\nsql: SELECT .2\n---\nfeature: E011-02\nid: e011_02_03_34\nsql: SELECT .2E+2\n---\nfeature: E011-02\nid: e011_02_03_35\nsql: SELECT .2E-2\n---\nfeature: E011-02\nid: e011_02_03_36\nsql: SELECT .2E2\n---\nfeature: E011-02\nid: e011_02_03_37\nsql: SELECT 2\n---\nfeature: E011-02\nid: e011_02_03_38\nsql: SELECT 2.\n---\nfeature: E011-02\nid: e011_02_03_39\nsql: SELECT 2.2\n---\nfeature: E011-02\nid: e011_02_03_40\nsql: SELECT 2.2E+2\n---\nfeature: E011-02\nid: e011_02_03_41\nsql: SELECT 2.2E-2\n---\nfeature: E011-02\nid: e011_02_03_42\nsql: SELECT 2.2E2\n---\nfeature: E011-02\nid: e011_02_03_43\nsql: SELECT 2.E+2\n---\nfeature: E011-02\nid: e011_02_03_44\nsql: SELECT 2.E-2\n---\nfeature: E011-02\nid: e011_02_03_45\nsql: SELECT 2.E2\n---\nfeature: E011-02\nid: e011_02_03_46\nsql: SELECT 2E+2\n---\nfeature: E011-02\nid: e011_02_03_47\nsql: SELECT 2E-2\n---\nfeature: E011-02\nid: e011_02_03_48\nsql: SELECT 2E2\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-03.tests.yml",
    "content": "feature: E011-03\nid: e011_03_01_01\nsql: CREATE TABLE TABLE_E011_03_01_01 ( A DEC ( 6 ) )\n---\nfeature: E011-03\nid: e011_03_01_02\nsql: CREATE TABLE TABLE_E011_03_01_02 ( A DEC ( 6 , 3 ) )\n---\nfeature: E011-03\nid: e011_03_01_03\nsql: CREATE TABLE TABLE_E011_03_01_03 ( A DEC )\n---\nfeature: E011-03\nid: e011_03_01_04\nsql: CREATE TABLE TABLE_E011_03_01_04 ( A DECIMAL ( 6 ) )\n---\nfeature: E011-03\nid: e011_03_01_05\nsql: CREATE TABLE TABLE_E011_03_01_05 ( A DECIMAL ( 6 , 3 ) )\n---\nfeature: E011-03\nid: e011_03_01_06\nsql: CREATE TABLE TABLE_E011_03_01_06 ( A DECIMAL )\n---\nfeature: E011-03\nid: e011_03_01_07\nsql: CREATE TABLE TABLE_E011_03_01_07 ( A NUMERIC ( 6 ) )\n---\nfeature: E011-03\nid: e011_03_01_08\nsql: CREATE TABLE TABLE_E011_03_01_08 ( A NUMERIC ( 6 , 3 ) )\n---\nfeature: E011-03\nid: e011_03_01_09\nsql: CREATE TABLE TABLE_E011_03_01_09 ( A NUMERIC )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-04.tests.yml",
    "content": "feature: E011-04\nid: e011_04_01_01\nsql: SELECT 5 +3\n---\nfeature: E011-04\nid: e011_04_01_02\nsql: SELECT 5 -3\n---\nfeature: E011-04\nid: e011_04_02_01\nsql: SELECT 3.4 + 1.2\n---\nfeature: E011-04\nid: e011_04_02_02\nsql: SELECT 3.4 - 1.2\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-05.tests.yml",
    "content": "feature: E011-05\nid: e011_05_01_01\nsql: SELECT 3 < 5\n---\nfeature: E011-05\nid: e011_05_01_02\nsql: SELECT 3 <= 5\n---\nfeature: E011-05\nid: e011_05_01_03\nsql: SELECT 3 <> 5\n---\nfeature: E011-05\nid: e011_05_01_04\nsql: SELECT 3 = 5\n---\nfeature: E011-05\nid: e011_05_01_05\nsql: SELECT 3 > 5\n---\nfeature: E011-05\nid: e011_05_01_06\nsql: SELECT 3 >= 5\n---\nfeature: E011-05\nid: e011_05_02_01\nsql: SELECT 3.7 < 5.2\n---\nfeature: E011-05\nid: e011_05_02_02\nsql: SELECT 3.7 <= 5.2\n---\nfeature: E011-05\nid: e011_05_02_03\nsql: SELECT 3.7 <> 5.2\n---\nfeature: E011-05\nid: e011_05_02_04\nsql: SELECT 3.7 = 5.2\n---\nfeature: E011-05\nid: e011_05_02_05\nsql: SELECT 3.7 > 5.2\n---\nfeature: E011-05\nid: e011_05_02_06\nsql: SELECT 3.7 >= 5.2\n---\nfeature: E011-05\nid: e011_05_03_01\nsql: SELECT ( 1 )\n---\nfeature: E011-05\nid: e011_05_04_01\nsql: SELECT ( 5.5 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E011-06.tests.yml",
    "content": "feature: E011-06\nid: e011_06_01_01\nsql: SELECT 3 < 1.2\n---\nfeature: E011-06\nid: e011_06_01_02\nsql: SELECT 3 < 5\n---\nfeature: E011-06\nid: e011_06_01_03\nsql: SELECT 3 <= 1.2\n---\nfeature: E011-06\nid: e011_06_01_04\nsql: SELECT 3 <= 5\n---\nfeature: E011-06\nid: e011_06_01_05\nsql: SELECT 3 <> 1.2\n---\nfeature: E011-06\nid: e011_06_01_06\nsql: SELECT 3 <> 5\n---\nfeature: E011-06\nid: e011_06_01_07\nsql: SELECT 3 = 1.2\n---\nfeature: E011-06\nid: e011_06_01_08\nsql: SELECT 3 = 5\n---\nfeature: E011-06\nid: e011_06_01_09\nsql: SELECT 3 > 1.2\n---\nfeature: E011-06\nid: e011_06_01_10\nsql: SELECT 3 > 5\n---\nfeature: E011-06\nid: e011_06_01_11\nsql: SELECT 3 >= 1.2\n---\nfeature: E011-06\nid: e011_06_01_12\nsql: SELECT 3 >= 5\n---\nfeature: E011-06\nid: e011_06_01_13\nsql: SELECT 3.7 < 1.2\n---\nfeature: E011-06\nid: e011_06_01_14\nsql: SELECT 3.7 < 5\n---\nfeature: E011-06\nid: e011_06_01_15\nsql: SELECT 3.7 <= 1.2\n---\nfeature: E011-06\nid: e011_06_01_16\nsql: SELECT 3.7 <= 5\n---\nfeature: E011-06\nid: e011_06_01_17\nsql: SELECT 3.7 <> 1.2\n---\nfeature: E011-06\nid: e011_06_01_18\nsql: SELECT 3.7 <> 5\n---\nfeature: E011-06\nid: e011_06_01_19\nsql: SELECT 3.7 = 1.2\n---\nfeature: E011-06\nid: e011_06_01_20\nsql: SELECT 3.7 = 5\n---\nfeature: E011-06\nid: e011_06_01_21\nsql: SELECT 3.7 > 1.2\n---\nfeature: E011-06\nid: e011_06_01_22\nsql: SELECT 3.7 > 5\n---\nfeature: E011-06\nid: e011_06_01_23\nsql: SELECT 3.7 >= 1.2\n---\nfeature: E011-06\nid: e011_06_01_24\nsql: SELECT 3.7 >= 5\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-01.tests.yml",
    "content": "feature: E021-01\nid: e021_01_01_01\nsql: CREATE TABLE TABLE_E021_01_01_01 ( A CHAR ( 8 ) )\n---\nfeature: E021-01\nid: e021_01_01_02\nsql: CREATE TABLE TABLE_E021_01_01_02 ( A CHAR ( 8 CHARACTERS ) )\n---\nfeature: E021-01\nid: e021_01_01_03\nsql: CREATE TABLE TABLE_E021_01_01_03 ( A CHAR ( 8 OCTETS ) )\n---\nfeature: E021-01\nid: e021_01_01_04\nsql: CREATE TABLE TABLE_E021_01_01_04 ( A CHAR )\n---\nfeature: E021-01\nid: e021_01_01_05\nsql: CREATE TABLE TABLE_E021_01_01_05 ( A CHARACTER ( 8 ) )\n---\nfeature: E021-01\nid: e021_01_01_06\nsql: CREATE TABLE TABLE_E021_01_01_06 ( A CHARACTER ( 8 CHARACTERS ) )\n---\nfeature: E021-01\nid: e021_01_01_07\nsql: CREATE TABLE TABLE_E021_01_01_07 ( A CHARACTER ( 8 OCTETS ) )\n---\nfeature: E021-01\nid: e021_01_01_08\nsql: CREATE TABLE TABLE_E021_01_01_08 ( A CHARACTER )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-02.tests.yml",
    "content": "feature: E021-02\nid: e021_02_01_01\nsql: CREATE TABLE TABLE_E021_02_01_01 ( A CHAR VARING ( 8 ) )\n---\nfeature: E021-02\nid: e021_02_01_02\nsql: CREATE TABLE TABLE_E021_02_01_02 ( A CHAR VARING ( 8 CHARACTERS ) )\n---\nfeature: E021-02\nid: e021_02_01_03\nsql: CREATE TABLE TABLE_E021_02_01_03 ( A CHAR VARING ( 8 OCTETS ) )\n---\nfeature: E021-02\nid: e021_02_01_04\nsql: CREATE TABLE TABLE_E021_02_01_04 ( A CHAR VARING )\n---\nfeature: E021-02\nid: e021_02_01_05\nsql: CREATE TABLE TABLE_E021_02_01_05 ( A CHARACTER VARYING ( 8 ) )\n---\nfeature: E021-02\nid: e021_02_01_06\nsql: CREATE TABLE TABLE_E021_02_01_06 ( A CHARACTER VARYING ( 8 CHARACTERS ) )\n---\nfeature: E021-02\nid: e021_02_01_07\nsql: CREATE TABLE TABLE_E021_02_01_07 ( A CHARACTER VARYING ( 8 OCTETS ) )\n---\nfeature: E021-02\nid: e021_02_01_08\nsql: CREATE TABLE TABLE_E021_02_01_08 ( A CHARACTER VARYING )\n---\nfeature: E021-02\nid: e021_02_01_09\nsql: CREATE TABLE TABLE_E021_02_01_09 ( A VARCHAR ( 8 ) )\n---\nfeature: E021-02\nid: e021_02_01_10\nsql: CREATE TABLE TABLE_E021_02_01_10 ( A VARCHAR ( 8 CHARACTERS ) )\n---\nfeature: E021-02\nid: e021_02_01_11\nsql: CREATE TABLE TABLE_E021_02_01_11 ( A VARCHAR ( 8 OCTETS ) )\n---\nfeature: E021-02\nid: e021_02_01_12\nsql: CREATE TABLE TABLE_E021_02_01_12 ( A VARCHAR )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-03.tests.yml",
    "content": "feature: E021-03\nid: e021_03_01_01\nsql: SELECT ''\n---\nfeature: E021-03\nid: e021_03_01_02\nsql: SELECT 'a'\n---\nfeature: E021-03\nid: e021_03_01_03\nsql: SELECT 'abc'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-04.tests.yml",
    "content": "feature: E021-04\nid: e021_04_01_01\nsql: SELECT CHARACTER_LENGTH ( 'foo' )\n---\nfeature: E021-04\nid: e021_04_01_02\nsql: SELECT CHARACTER_LENGTH ( 'foo' USING CHARACTERS )\n---\nfeature: E021-04\nid: e021_04_01_03\nsql: SELECT CHARACTER_LENGTH ( 'foo' USING OCTETS )\n---\nfeature: E021-04\nid: e021_04_01_04\nsql: SELECT CHAR_LENGTH ( 'foo' )\n---\nfeature: E021-04\nid: e021_04_01_05\nsql: SELECT CHAR_LENGTH ( 'foo' USING CHARACTERS )\n---\nfeature: E021-04\nid: e021_04_01_06\nsql: SELECT CHAR_LENGTH ( 'foo' USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-05.tests.yml",
    "content": "feature: E021-05\nid: e021_05_01_01\nsql: SELECT OCTET_LENGTH ( 'foo' )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-06.tests.yml",
    "content": "feature: E021-06\nid: e021_06_01_01\nsql: SELECT SUBSTRING ( 'foo' FROM 1 )\n---\nfeature: E021-06\nid: e021_06_01_02\nsql: SELECT SUBSTRING ( 'foo' FROM 1 FOR 2 )\n---\nfeature: E021-06\nid: e021_06_01_03\nsql: SELECT SUBSTRING ( 'foo' FROM 1 FOR 2 USING CHARACTERS )\n---\nfeature: E021-06\nid: e021_06_01_04\nsql: SELECT SUBSTRING ( 'foo' FROM 1 FOR 2 USING OCTETS )\n---\nfeature: E021-06\nid: e021_06_01_05\nsql: SELECT SUBSTRING ( 'foo' FROM 1 USING CHARACTERS )\n---\nfeature: E021-06\nid: e021_06_01_06\nsql: SELECT SUBSTRING ( 'foo' FROM 1 USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-07.tests.yml",
    "content": "feature: E021-07\nid: e021_07_01_01\nsql: SELECT 'foo' || 'bar'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-08.tests.yml",
    "content": "feature: E021-08\nid: e021_08_01_01\nsql: SELECT LOWER ( 'foo' )\n---\nfeature: E021-08\nid: e021_08_01_02\nsql: SELECT UPPER ( 'foo' )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-09.tests.yml",
    "content": "feature: E021-09\nid: e021_09_01_01\nsql: SELECT TRIM ( 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_02\nsql: SELECT TRIM ( 'foo' FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_03\nsql: SELECT TRIM ( BOTH 'foo' FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_04\nsql: SELECT TRIM ( BOTH FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_05\nsql: SELECT TRIM ( FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_06\nsql: SELECT TRIM ( LEADING 'foo' FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_07\nsql: SELECT TRIM ( LEADING FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_08\nsql: SELECT TRIM ( TRAILING 'foo' FROM 'foo' )\n---\nfeature: E021-09\nid: e021_09_01_09\nsql: SELECT TRIM ( TRAILING FROM 'foo' )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-10.tests.yml",
    "content": "feature: E021-10\nid: e021_10_01_01\nsql:\n- CREATE TABLE TABLE_E021_10_01_01 ( A CHARACTER ( 10 ) , B CHARACTER VARYING ( 15\n  ) )\n- INSERT INTO TABLE_E021_10_01_01 ( A, B ) VALUES ( 'foo' , 'bar' )\n- SELECT A = B FROM TABLE_E021_10_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-11.tests.yml",
    "content": "feature: E021-11\nid: e021_11_01_01\nsql: SELECT POSITION ( 'foo' IN 'bar' )\n---\nfeature: E021-11\nid: e021_11_01_02\nsql: SELECT POSITION ( 'foo' IN 'bar' USING CHARACTERS )\n---\nfeature: E021-11\nid: e021_11_01_03\nsql: SELECT POSITION ( 'foo' IN 'bar' USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E021-12.tests.yml",
    "content": "feature: E021-12\nid: e021_12_01_01\nsql: SELECT 'foo' < 'bar'\n---\nfeature: E021-12\nid: e021_12_01_02\nsql: SELECT 'foo' <= 'bar'\n---\nfeature: E021-12\nid: e021_12_01_03\nsql: SELECT 'foo' <> 'bar'\n---\nfeature: E021-12\nid: e021_12_01_04\nsql: SELECT 'foo' = 'bar'\n---\nfeature: E021-12\nid: e021_12_01_05\nsql: SELECT 'foo' > 'bar'\n---\nfeature: E021-12\nid: e021_12_01_06\nsql: SELECT 'foo' >= 'bar'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E031-01.tests.yml",
    "content": "feature: E031-01\nid: e031_01_01_01\nsql:\n- CREATE TABLE TABLE_E031_01_01_01 ( \"A\" INT )\n- SELECT \"A\" FROM TABLE_E031_01_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E031-02.tests.yml",
    "content": "feature: E031-02\nid: e031_02_01_01\nsql:\n- CREATE TABLE TABLE_E031_02_01_01 ( A INT )\n- SELECT A FROM TABLE_E031_02_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E031-03.tests.yml",
    "content": "feature: E031-03\nid: e031_03_01_01\nsql:\n- CREATE TABLE TABLE_E031_03_01_01 ( A_ INT )\n- SELECT A_ FROM TABLE_E031_03_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-01.tests.yml",
    "content": "feature: E051-01\nid: e051_01_01_01\nsql:\n- CREATE TABLE TABLE_E051_01_01_01 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E051_01_01_01\n---\nfeature: E051-01\nid: e051_01_01_02\nsql:\n- CREATE TABLE TABLE_E051_01_01_02 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E051_01_01_02 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-02.tests.yml",
    "content": "feature: E051-02\nid: e051_02_01_01\nsql:\n- CREATE TABLE TABLE_E051_02_01_01 ( A INT, B INT )\n- SELECT A, B FROM TABLE_E051_02_01_01 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_01_02\nsql:\n- CREATE TABLE TABLE_E051_02_01_02 ( A INT, B INT )\n- SELECT A, B FROM TABLE_E051_02_01_02 WHERE A = 1 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_01_03\nsql:\n- CREATE TABLE TABLE_E051_02_01_03 ( A INT, B INT )\n- SELECT ALL A, B FROM TABLE_E051_02_01_03 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_01_04\nsql:\n- CREATE TABLE TABLE_E051_02_01_04 ( A INT, B INT )\n- SELECT ALL A, B FROM TABLE_E051_02_01_04 WHERE A = 1 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_01_05\nsql:\n- CREATE TABLE TABLE_E051_02_01_05 ( A INT, B INT )\n- SELECT DISTINCT A, B FROM TABLE_E051_02_01_05 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_01_06\nsql:\n- CREATE TABLE TABLE_E051_02_01_06 ( A INT, B INT )\n- SELECT DISTINCT A, B FROM TABLE_E051_02_01_06 WHERE A = 1 GROUP BY A, B\n---\nfeature: E051-02\nid: e051_02_02_01\nsql:\n- CREATE TABLE TABLE_E051_02_02_01 ( A INT, B INT )\n- SELECT A FROM TABLE_E051_02_02_01 GROUP BY A\n---\nfeature: E051-02\nid: e051_02_02_02\nsql:\n- CREATE TABLE TABLE_E051_02_02_02 ( A INT, B INT )\n- SELECT A FROM TABLE_E051_02_02_02 WHERE A = 1 GROUP BY A\n---\nfeature: E051-02\nid: e051_02_02_03\nsql:\n- CREATE TABLE TABLE_E051_02_02_03 ( A INT, B INT )\n- SELECT ALL A FROM TABLE_E051_02_02_03 GROUP BY A\n---\nfeature: E051-02\nid: e051_02_02_04\nsql:\n- CREATE TABLE TABLE_E051_02_02_04 ( A INT, B INT )\n- SELECT ALL A FROM TABLE_E051_02_02_04 WHERE A = 1 GROUP BY A\n---\nfeature: E051-02\nid: e051_02_02_05\nsql:\n- CREATE TABLE TABLE_E051_02_02_05 ( A INT, B INT )\n- SELECT DISTINCT A FROM TABLE_E051_02_02_05 GROUP BY A\n---\nfeature: E051-02\nid: e051_02_02_06\nsql:\n- CREATE TABLE TABLE_E051_02_02_06 ( A INT, B INT )\n- SELECT DISTINCT A FROM TABLE_E051_02_02_06 WHERE A = 1 GROUP BY A\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-04.tests.yml",
    "content": "feature: E051-04\nid: e051_04_01_01\nsql:\n- CREATE TABLE TABLE_E051_04_01_01 ( A INT, B INT )\n- SELECT ALL B FROM TABLE_E051_04_01_01 GROUP BY B\n---\nfeature: E051-04\nid: e051_04_01_02\nsql:\n- CREATE TABLE TABLE_E051_04_01_02 ( A INT, B INT )\n- SELECT ALL B FROM TABLE_E051_04_01_02 WHERE A = 1 GROUP BY B\n---\nfeature: E051-04\nid: e051_04_01_03\nsql:\n- CREATE TABLE TABLE_E051_04_01_03 ( A INT, B INT )\n- SELECT B FROM TABLE_E051_04_01_03 GROUP BY B\n---\nfeature: E051-04\nid: e051_04_01_04\nsql:\n- CREATE TABLE TABLE_E051_04_01_04 ( A INT, B INT )\n- SELECT B FROM TABLE_E051_04_01_04 WHERE A = 1 GROUP BY B\n---\nfeature: E051-04\nid: e051_04_01_05\nsql:\n- CREATE TABLE TABLE_E051_04_01_05 ( A INT, B INT )\n- SELECT DISTINCT B FROM TABLE_E051_04_01_05 GROUP BY B\n---\nfeature: E051-04\nid: e051_04_01_06\nsql:\n- CREATE TABLE TABLE_E051_04_01_06 ( A INT, B INT )\n- SELECT DISTINCT B FROM TABLE_E051_04_01_06 WHERE A = 1 GROUP BY B\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-05.tests.yml",
    "content": "feature: E051-05\nid: e051_05_01_01\nsql:\n- CREATE TABLE TABLE_E051_05_01_01 ( A INT )\n- SELECT A AS RENAMED FROM TABLE_E051_05_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-06.tests.yml",
    "content": "feature: E051-06\nid: e051_06_01_01\nsql:\n- CREATE TABLE TABLE_E051_06_01_01 ( A INT, B INT )\n- SELECT A FROM TABLE_E051_06_01_01 GROUP BY A HAVING A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-07.tests.yml",
    "content": "feature: E051-07\nid: e051_07_01_01\nsql:\n- CREATE TABLE TABLE_E051_07_01_01 ( A INT, B INT )\n- SELECT * AS ( C , D ) FROM TABLE_E051_07_01_01\n---\nfeature: E051-07\nid: e051_07_01_02\nsql:\n- CREATE TABLE TABLE_E051_07_01_02 ( A INT, B INT )\n- SELECT * FROM TABLE_E051_07_01_02\n---\nfeature: E051-07\nid: e051_07_01_03\nsql:\n- CREATE TABLE TABLE_E051_07_01_03 ( A INT, B INT )\n- SELECT ALL * AS ( C , D ) FROM TABLE_E051_07_01_03\n---\nfeature: E051-07\nid: e051_07_01_04\nsql:\n- CREATE TABLE TABLE_E051_07_01_04 ( A INT, B INT )\n- SELECT ALL * FROM TABLE_E051_07_01_04\n---\nfeature: E051-07\nid: e051_07_01_05\nsql:\n- CREATE TABLE TABLE_E051_07_01_05 ( A INT, B INT )\n- SELECT ALL TABLE_E051_07_01_05 . * AS ( C , D ) FROM TABLE_E051_07_01_05\n---\nfeature: E051-07\nid: e051_07_01_06\nsql:\n- CREATE TABLE TABLE_E051_07_01_06 ( A INT, B INT )\n- SELECT ALL TABLE_E051_07_01_06 . * FROM TABLE_E051_07_01_06\n---\nfeature: E051-07\nid: e051_07_01_07\nsql:\n- CREATE TABLE TABLE_E051_07_01_07 ( A INT, B INT )\n- SELECT DISTINCT * AS ( C , D ) FROM TABLE_E051_07_01_07\n---\nfeature: E051-07\nid: e051_07_01_08\nsql:\n- CREATE TABLE TABLE_E051_07_01_08 ( A INT, B INT )\n- SELECT DISTINCT * FROM TABLE_E051_07_01_08\n---\nfeature: E051-07\nid: e051_07_01_09\nsql:\n- CREATE TABLE TABLE_E051_07_01_09 ( A INT, B INT )\n- SELECT DISTINCT TABLE_E051_07_01_09 . * AS ( C , D ) FROM TABLE_E051_07_01_09\n---\nfeature: E051-07\nid: e051_07_01_10\nsql:\n- CREATE TABLE TABLE_E051_07_01_10 ( A INT, B INT )\n- SELECT DISTINCT TABLE_E051_07_01_10 . * FROM TABLE_E051_07_01_10\n---\nfeature: E051-07\nid: e051_07_01_11\nsql:\n- CREATE TABLE TABLE_E051_07_01_11 ( A INT, B INT )\n- SELECT TABLE_E051_07_01_11 . * AS ( C , D ) FROM TABLE_E051_07_01_11\n---\nfeature: E051-07\nid: e051_07_01_12\nsql:\n- CREATE TABLE TABLE_E051_07_01_12 ( A INT, B INT )\n- SELECT TABLE_E051_07_01_12 . * FROM TABLE_E051_07_01_12\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-08.tests.yml",
    "content": "feature: E051-08\nid: e051_08_01_01\nsql:\n- CREATE TABLE TABLE_E051_08_01_01 ( A INT, B INT )\n- SELECT * AS ( C , D ) FROM TABLE_E051_08_01_01 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_02\nsql:\n- CREATE TABLE TABLE_E051_08_01_02 ( A INT, B INT )\n- SELECT * FROM TABLE_E051_08_01_02 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_03\nsql:\n- CREATE TABLE TABLE_E051_08_01_03 ( A INT, B INT )\n- SELECT ALL * AS ( C , D ) FROM TABLE_E051_08_01_03 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_04\nsql:\n- CREATE TABLE TABLE_E051_08_01_04 ( A INT, B INT )\n- SELECT ALL * FROM TABLE_E051_08_01_04 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_05\nsql:\n- CREATE TABLE TABLE_E051_08_01_05 ( A INT, B INT )\n- SELECT ALL MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_05 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_06\nsql:\n- CREATE TABLE TABLE_E051_08_01_06 ( A INT, B INT )\n- SELECT ALL MYTEMP . * FROM TABLE_E051_08_01_06 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_07\nsql:\n- CREATE TABLE TABLE_E051_08_01_07 ( A INT, B INT )\n- SELECT DISTINCT * AS ( C , D ) FROM TABLE_E051_08_01_07 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_08\nsql:\n- CREATE TABLE TABLE_E051_08_01_08 ( A INT, B INT )\n- SELECT DISTINCT * FROM TABLE_E051_08_01_08 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_09\nsql:\n- CREATE TABLE TABLE_E051_08_01_09 ( A INT, B INT )\n- SELECT DISTINCT MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_09 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_10\nsql:\n- CREATE TABLE TABLE_E051_08_01_10 ( A INT, B INT )\n- SELECT DISTINCT MYTEMP . * FROM TABLE_E051_08_01_10 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_11\nsql:\n- CREATE TABLE TABLE_E051_08_01_11 ( A INT, B INT )\n- SELECT MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_11 AS MYTEMP\n---\nfeature: E051-08\nid: e051_08_01_12\nsql:\n- CREATE TABLE TABLE_E051_08_01_12 ( A INT, B INT )\n- SELECT MYTEMP . * FROM TABLE_E051_08_01_12 AS MYTEMP\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051-09.tests.yml",
    "content": "feature: E051-09\nid: e051_09_01_01\nsql:\n- CREATE TABLE TABLE_E051_09_01_01 ( A INT, B INT )\n- SELECT ALL MYTEMP . X , Y FROM TABLE_E051_09_01_01 AS MYTEMP ( X, Y )\n---\nfeature: E051-09\nid: e051_09_01_02\nsql:\n- CREATE TABLE TABLE_E051_09_01_02 ( A INT, B INT )\n- SELECT DISTINCT MYTEMP . X , Y FROM TABLE_E051_09_01_02 AS MYTEMP ( X, Y )\n---\nfeature: E051-09\nid: e051_09_01_03\nsql:\n- CREATE TABLE TABLE_E051_09_01_03 ( A INT, B INT )\n- SELECT MYTEMP . X , Y FROM TABLE_E051_09_01_03 AS MYTEMP ( X, Y )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E051.tests.yml",
    "content": "feature: E051\nid: e051_01_01\nsql:\n- CREATE TABLE TABLE_E051_01_01 ( A INT )\n- SELECT A FROM TABLE_E051_01_01\n---\nfeature: E051\nid: e051_01_02\nsql:\n- CREATE TABLE TABLE_E051_01_02 ( A INT )\n- SELECT A FROM TABLE_E051_01_02 WHERE A = 1\n---\nfeature: E051\nid: e051_01_03\nsql:\n- CREATE TABLE TABLE_E051_01_03 ( A INT )\n- SELECT ALL A FROM TABLE_E051_01_03\n---\nfeature: E051\nid: e051_01_04\nsql:\n- CREATE TABLE TABLE_E051_01_04 ( A INT )\n- SELECT ALL A FROM TABLE_E051_01_04 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-01.tests.yml",
    "content": "feature: E061-01\nid: e061_01_01_01\nsql:\n- CREATE TABLE TABLE_E061_01_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_01 WHERE A < 1\n---\nfeature: E061-01\nid: e061_01_01_02\nsql:\n- CREATE TABLE TABLE_E061_01_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_02 WHERE A <= 1\n---\nfeature: E061-01\nid: e061_01_01_03\nsql:\n- CREATE TABLE TABLE_E061_01_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_03 WHERE A <> 1\n---\nfeature: E061-01\nid: e061_01_01_04\nsql:\n- CREATE TABLE TABLE_E061_01_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_04 WHERE A = 1\n---\nfeature: E061-01\nid: e061_01_01_05\nsql:\n- CREATE TABLE TABLE_E061_01_01_05 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_05 WHERE A > 1\n---\nfeature: E061-01\nid: e061_01_01_06\nsql:\n- CREATE TABLE TABLE_E061_01_01_06 ( A INT )\n- SELECT A FROM TABLE_E061_01_01_06 WHERE A >= 1\n---\nfeature: E061-01\nid: e061_01_01_07\nsql:\n- CREATE TABLE TABLE_E061_01_01_07 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_07 WHERE A < 1\n---\nfeature: E061-01\nid: e061_01_01_08\nsql:\n- CREATE TABLE TABLE_E061_01_01_08 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_08 WHERE A <= 1\n---\nfeature: E061-01\nid: e061_01_01_09\nsql:\n- CREATE TABLE TABLE_E061_01_01_09 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_09 WHERE A <> 1\n---\nfeature: E061-01\nid: e061_01_01_10\nsql:\n- CREATE TABLE TABLE_E061_01_01_10 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_10 WHERE A = 1\n---\nfeature: E061-01\nid: e061_01_01_11\nsql:\n- CREATE TABLE TABLE_E061_01_01_11 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_11 WHERE A > 1\n---\nfeature: E061-01\nid: e061_01_01_12\nsql:\n- CREATE TABLE TABLE_E061_01_01_12 ( A INT )\n- SELECT ALL A FROM TABLE_E061_01_01_12 WHERE A >= 1\n---\nfeature: E061-01\nid: e061_01_01_13\nsql:\n- CREATE TABLE TABLE_E061_01_01_13 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_13 WHERE A < 1\n---\nfeature: E061-01\nid: e061_01_01_14\nsql:\n- CREATE TABLE TABLE_E061_01_01_14 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_14 WHERE A <= 1\n---\nfeature: E061-01\nid: e061_01_01_15\nsql:\n- CREATE TABLE TABLE_E061_01_01_15 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_15 WHERE A <> 1\n---\nfeature: E061-01\nid: e061_01_01_16\nsql:\n- CREATE TABLE TABLE_E061_01_01_16 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_16 WHERE A = 1\n---\nfeature: E061-01\nid: e061_01_01_17\nsql:\n- CREATE TABLE TABLE_E061_01_01_17 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_17 WHERE A > 1\n---\nfeature: E061-01\nid: e061_01_01_18\nsql:\n- CREATE TABLE TABLE_E061_01_01_18 ( A INT )\n- SELECT DISTINCT A FROM TABLE_E061_01_01_18 WHERE A >= 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-02.tests.yml",
    "content": "feature: E061-02\nid: e061_02_01_01\nsql:\n- CREATE TABLE TABLE_E061_02_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_01 WHERE A BETWEEN 1 AND 1\n---\nfeature: E061-02\nid: e061_02_01_02\nsql:\n- CREATE TABLE TABLE_E061_02_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_02 WHERE A BETWEEN ASYMMETRIC 1 AND 1\n---\nfeature: E061-02\nid: e061_02_01_03\nsql:\n- CREATE TABLE TABLE_E061_02_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_03 WHERE A BETWEEN SYMMETRIC 1 AND 1\n---\nfeature: E061-02\nid: e061_02_01_04\nsql:\n- CREATE TABLE TABLE_E061_02_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_04 WHERE A NOT BETWEEN 1 AND 1\n---\nfeature: E061-02\nid: e061_02_01_05\nsql:\n- CREATE TABLE TABLE_E061_02_01_05 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_05 WHERE A NOT BETWEEN ASYMMETRIC 1 AND 1\n---\nfeature: E061-02\nid: e061_02_01_06\nsql:\n- CREATE TABLE TABLE_E061_02_01_06 ( A INT )\n- SELECT A FROM TABLE_E061_02_01_06 WHERE A NOT BETWEEN SYMMETRIC 1 AND 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-03.tests.yml",
    "content": "feature: E061-03\nid: e061_03_01_01\nsql:\n- CREATE TABLE TABLE_E061_03_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_03_01_01 WHERE A IN ( 1 )\n---\nfeature: E061-03\nid: e061_03_01_02\nsql:\n- CREATE TABLE TABLE_E061_03_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_03_01_02 WHERE A IN ( 1, 2 )\n---\nfeature: E061-03\nid: e061_03_01_03\nsql:\n- CREATE TABLE TABLE_E061_03_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_03_01_03 WHERE A NOT IN ( 1 )\n---\nfeature: E061-03\nid: e061_03_01_04\nsql:\n- CREATE TABLE TABLE_E061_03_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_03_01_04 WHERE A NOT IN ( 1, 2 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-04.tests.yml",
    "content": "feature: E061-04\nid: e061_04_01_01\nsql:\n- CREATE TABLE TABLE_E061_04_01_01 ( A VARCHAR ( 255 ) )\n- SELECT A FROM TABLE_E061_04_01_01 WHERE A LIKE 'foo'\n---\nfeature: E061-04\nid: e061_04_01_02\nsql:\n- CREATE TABLE TABLE_E061_04_01_02 ( A VARCHAR ( 255 ) )\n- SELECT A FROM TABLE_E061_04_01_02 WHERE A NOT LIKE 'foo'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-05.tests.yml",
    "content": "feature: E061-05\nid: e061_05_01_01\nsql:\n- CREATE TABLE TABLE_E061_05_01_01 ( A VARCHAR ( 255 ) )\n- SELECT A FROM TABLE_E061_05_01_01 WHERE A LIKE 'foo' ESCAPE 'f'\n---\nfeature: E061-05\nid: e061_05_01_02\nsql:\n- CREATE TABLE TABLE_E061_05_01_02 ( A VARCHAR ( 255 ) )\n- SELECT A FROM TABLE_E061_05_01_02 WHERE A NOT LIKE 'foo' ESCAPE 'f'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-06.tests.yml",
    "content": "feature: E061-06\nid: e061_06_01_01\nsql:\n- CREATE TABLE TABLE_E061_06_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_06_01_01 WHERE A IS NOT NULL\n---\nfeature: E061-06\nid: e061_06_01_02\nsql:\n- CREATE TABLE TABLE_E061_06_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_06_01_02 WHERE A IS NULL\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-07.tests.yml",
    "content": "feature: E061-07\nid: e061_07_01_01\nsql:\n- CREATE TABLE TABLE_E061_07_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_01 WHERE A < ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_02\nsql:\n- CREATE TABLE TABLE_E061_07_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_02 WHERE A < ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_03\nsql:\n- CREATE TABLE TABLE_E061_07_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_03 WHERE A < SOME ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_04\nsql:\n- CREATE TABLE TABLE_E061_07_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_04 WHERE A <= ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_05\nsql:\n- CREATE TABLE TABLE_E061_07_01_05 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_05 WHERE A <= ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_06\nsql:\n- CREATE TABLE TABLE_E061_07_01_06 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_06 WHERE A <= SOME ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_07\nsql:\n- CREATE TABLE TABLE_E061_07_01_07 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_07 WHERE A <> ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_08\nsql:\n- CREATE TABLE TABLE_E061_07_01_08 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_08 WHERE A <> ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_09\nsql:\n- CREATE TABLE TABLE_E061_07_01_09 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_09 WHERE A <> SOME ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_10\nsql:\n- CREATE TABLE TABLE_E061_07_01_10 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_10 WHERE A = ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_11\nsql:\n- CREATE TABLE TABLE_E061_07_01_11 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_11 WHERE A = ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_12\nsql:\n- CREATE TABLE TABLE_E061_07_01_12 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_12 WHERE A = SOME ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_13\nsql:\n- CREATE TABLE TABLE_E061_07_01_13 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_13 WHERE A > ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_14\nsql:\n- CREATE TABLE TABLE_E061_07_01_14 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_14 WHERE A > ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_15\nsql:\n- CREATE TABLE TABLE_E061_07_01_15 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_15 WHERE A > SOME ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_16\nsql:\n- CREATE TABLE TABLE_E061_07_01_16 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_16 WHERE A >= ALL ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_17\nsql:\n- CREATE TABLE TABLE_E061_07_01_17 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_17 WHERE A >= ANY ( SELECT 1 )\n---\nfeature: E061-07\nid: e061_07_01_18\nsql:\n- CREATE TABLE TABLE_E061_07_01_18 ( A INT )\n- SELECT A FROM TABLE_E061_07_01_18 WHERE A >= SOME ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-08.tests.yml",
    "content": "feature: E061-08\nid: e061_08_01_01\nsql:\n- CREATE TABLE TABLE_E061_08_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_08_01_01 WHERE EXISTS ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-09.tests.yml",
    "content": "feature: E061-09\nid: e061_09_01_01\nsql:\n- CREATE TABLE TABLE_E061_09_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_01 WHERE A < ( SELECT 1 )\n---\nfeature: E061-09\nid: e061_09_01_02\nsql:\n- CREATE TABLE TABLE_E061_09_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_02 WHERE A <= ( SELECT 1 )\n---\nfeature: E061-09\nid: e061_09_01_03\nsql:\n- CREATE TABLE TABLE_E061_09_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_03 WHERE A <> ( SELECT 1 )\n---\nfeature: E061-09\nid: e061_09_01_04\nsql:\n- CREATE TABLE TABLE_E061_09_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_04 WHERE A = ( SELECT 1 )\n---\nfeature: E061-09\nid: e061_09_01_05\nsql:\n- CREATE TABLE TABLE_E061_09_01_05 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_05 WHERE A > ( SELECT 1 )\n---\nfeature: E061-09\nid: e061_09_01_06\nsql:\n- CREATE TABLE TABLE_E061_09_01_06 ( A INT )\n- SELECT A FROM TABLE_E061_09_01_06 WHERE A >= ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-11.tests.yml",
    "content": "feature: E061-11\nid: e061_11_01_01\nsql:\n- CREATE TABLE TABLE_E061_11_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_11_01_01 WHERE A IN ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-12.tests.yml",
    "content": "feature: E061-12\nid: e061_12_01_01\nsql:\n- CREATE TABLE TABLE_E061_12_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_01 WHERE A < ALL ( SELECT A FROM TABLE_E061_12_01_01\n  )\n---\nfeature: E061-12\nid: e061_12_01_02\nsql:\n- CREATE TABLE TABLE_E061_12_01_02 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_02 WHERE A < ANY ( SELECT A FROM TABLE_E061_12_01_02\n  )\n---\nfeature: E061-12\nid: e061_12_01_03\nsql:\n- CREATE TABLE TABLE_E061_12_01_03 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_03 WHERE A < SOME ( SELECT A FROM TABLE_E061_12_01_03\n  )\n---\nfeature: E061-12\nid: e061_12_01_04\nsql:\n- CREATE TABLE TABLE_E061_12_01_04 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_04 WHERE A <= ALL ( SELECT A FROM TABLE_E061_12_01_04\n  )\n---\nfeature: E061-12\nid: e061_12_01_05\nsql:\n- CREATE TABLE TABLE_E061_12_01_05 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_05 WHERE A <= ANY ( SELECT A FROM TABLE_E061_12_01_05\n  )\n---\nfeature: E061-12\nid: e061_12_01_06\nsql:\n- CREATE TABLE TABLE_E061_12_01_06 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_06 WHERE A <= SOME ( SELECT A FROM TABLE_E061_12_01_06\n  )\n---\nfeature: E061-12\nid: e061_12_01_07\nsql:\n- CREATE TABLE TABLE_E061_12_01_07 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_07 WHERE A <> ALL ( SELECT A FROM TABLE_E061_12_01_07\n  )\n---\nfeature: E061-12\nid: e061_12_01_08\nsql:\n- CREATE TABLE TABLE_E061_12_01_08 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_08 WHERE A <> ANY ( SELECT A FROM TABLE_E061_12_01_08\n  )\n---\nfeature: E061-12\nid: e061_12_01_09\nsql:\n- CREATE TABLE TABLE_E061_12_01_09 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_09 WHERE A <> SOME ( SELECT A FROM TABLE_E061_12_01_09\n  )\n---\nfeature: E061-12\nid: e061_12_01_10\nsql:\n- CREATE TABLE TABLE_E061_12_01_10 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_10 WHERE A = ALL ( SELECT A FROM TABLE_E061_12_01_10\n  )\n---\nfeature: E061-12\nid: e061_12_01_11\nsql:\n- CREATE TABLE TABLE_E061_12_01_11 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_11 WHERE A = ANY ( SELECT A FROM TABLE_E061_12_01_11\n  )\n---\nfeature: E061-12\nid: e061_12_01_12\nsql:\n- CREATE TABLE TABLE_E061_12_01_12 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_12 WHERE A = SOME ( SELECT A FROM TABLE_E061_12_01_12\n  )\n---\nfeature: E061-12\nid: e061_12_01_13\nsql:\n- CREATE TABLE TABLE_E061_12_01_13 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_13 WHERE A > ALL ( SELECT A FROM TABLE_E061_12_01_13\n  )\n---\nfeature: E061-12\nid: e061_12_01_14\nsql:\n- CREATE TABLE TABLE_E061_12_01_14 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_14 WHERE A > ANY ( SELECT A FROM TABLE_E061_12_01_14\n  )\n---\nfeature: E061-12\nid: e061_12_01_15\nsql:\n- CREATE TABLE TABLE_E061_12_01_15 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_15 WHERE A > SOME ( SELECT A FROM TABLE_E061_12_01_15\n  )\n---\nfeature: E061-12\nid: e061_12_01_16\nsql:\n- CREATE TABLE TABLE_E061_12_01_16 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_16 WHERE A >= ALL ( SELECT A FROM TABLE_E061_12_01_16\n  )\n---\nfeature: E061-12\nid: e061_12_01_17\nsql:\n- CREATE TABLE TABLE_E061_12_01_17 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_17 WHERE A >= ANY ( SELECT A FROM TABLE_E061_12_01_17\n  )\n---\nfeature: E061-12\nid: e061_12_01_18\nsql:\n- CREATE TABLE TABLE_E061_12_01_18 ( A INT )\n- SELECT A FROM TABLE_E061_12_01_18 WHERE A >= SOME ( SELECT A FROM TABLE_E061_12_01_18\n  )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-13.tests.yml",
    "content": "feature: E061-13\nid: e061_13_01_01\nsql:\n- CREATE TABLE TABLE_E061_13_01_011 ( A INT )\n- CREATE TABLE TABLE_E061_13_01_012 ( B INT )\n- SELECT A FROM TABLE_E061_13_01_011 WHERE A IN ( SELECT B FROM TABLE_E061_13_01_012\n  WHERE B = A )\n---\nfeature: E061-13\nid: e061_13_01_02\nsql:\n- CREATE TABLE TABLE_E061_13_01_021 ( A INT )\n- CREATE TABLE TABLE_E061_13_01_022 ( B INT )\n- SELECT A FROM TABLE_E061_13_01_021 WHERE A NOT IN ( SELECT B FROM TABLE_E061_13_01_022\n  WHERE B = A )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E061-14.tests.yml",
    "content": "feature: E061-14\nid: e061_14_01_01\nsql:\n- CREATE TABLE TABLE_E061_14_01_01 ( A INT )\n- SELECT A FROM TABLE_E061_14_01_01 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E071-01.tests.yml",
    "content": "feature: E071-01\nid: e071_01_01_01\nsql:\n- CREATE TABLE TABLE_E071_01_01_011 ( A INT )\n- CREATE TABLE TABLE_E071_01_01_012 ( B INT )\n- SELECT A FROM TABLE_E071_01_01_011 UNION DISTINCT SELECT B FROM TABLE_E071_01_01_012\n---\nfeature: E071-01\nid: e071_01_01_02\nsql:\n- CREATE TABLE TABLE_E071_01_01_021 ( A INT )\n- CREATE TABLE TABLE_E071_01_01_022 ( B INT )\n- SELECT A FROM TABLE_E071_01_01_021 UNION SELECT B FROM TABLE_E071_01_01_022\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E071-02.tests.yml",
    "content": "feature: E071-02\nid: e071_02_01_01\nsql:\n- CREATE TABLE TABLE_E071_02_01_011 ( A INT )\n- CREATE TABLE TABLE_E071_02_01_012 ( B INT )\n- SELECT A FROM TABLE_E071_02_01_011 UNION ALL SELECT B FROM TABLE_E071_02_01_012\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E071-03.tests.yml",
    "content": "feature: E071-03\nid: e071_03_01_01\nsql:\n- CREATE TABLE TABLE_E071_03_01_011 ( A INT )\n- CREATE TABLE TABLE_E071_03_01_012 ( B INT )\n- SELECT A FROM TABLE_E071_03_01_011 EXCEPT DISTINCT SELECT B FROM TABLE_E071_03_01_012\n---\nfeature: E071-03\nid: e071_03_01_02\nsql:\n- CREATE TABLE TABLE_E071_03_01_021 ( A INT )\n- CREATE TABLE TABLE_E071_03_01_022 ( B INT )\n- SELECT A FROM TABLE_E071_03_01_021 EXCEPT SELECT B FROM TABLE_E071_03_01_022\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E071-05.tests.yml",
    "content": "feature: E071-05\nid: e071_05_01_01\nsql:\n- CREATE TABLE TABLE_E071_05_01_011 ( A INT )\n- CREATE TABLE TABLE_E071_05_01_012 ( B FLOAT )\n- SELECT A FROM TABLE_E071_05_01_011 UNION ALL SELECT B FROM TABLE_E071_05_01_012\n---\nfeature: E071-05\nid: e071_05_01_02\nsql:\n- CREATE TABLE TABLE_E071_05_01_021 ( A INT )\n- CREATE TABLE TABLE_E071_05_01_022 ( B FLOAT )\n- SELECT A FROM TABLE_E071_05_01_021 UNION DISTINCT SELECT B FROM TABLE_E071_05_01_022\n---\nfeature: E071-05\nid: e071_05_01_03\nsql:\n- CREATE TABLE TABLE_E071_05_01_031 ( A INT )\n- CREATE TABLE TABLE_E071_05_01_032 ( B FLOAT )\n- SELECT A FROM TABLE_E071_05_01_031 UNION SELECT B FROM TABLE_E071_05_01_032\n---\nfeature: E071-05\nid: e071_05_02_01\nsql:\n- CREATE TABLE TABLE_E071_05_02_011 ( A INT )\n- CREATE TABLE TABLE_E071_05_02_012 ( B FLOAT )\n- SELECT A FROM TABLE_E071_05_02_011 EXCEPT DISTINCT SELECT B FROM TABLE_E071_05_02_012\n---\nfeature: E071-05\nid: e071_05_02_02\nsql:\n- CREATE TABLE TABLE_E071_05_02_021 ( A INT )\n- CREATE TABLE TABLE_E071_05_02_022 ( B FLOAT )\n- SELECT A FROM TABLE_E071_05_02_021 EXCEPT SELECT B FROM TABLE_E071_05_02_022\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E071-06.tests.yml",
    "content": "feature: E071-06\nid: e071_06_01_01\nsql:\n- CREATE TABLE TABLE_E071_06_01_011 ( A INT )\n- CREATE TABLE TABLE_E071_06_01_012 ( B FLOAT )\n- SELECT A FROM TABLE_E071_06_01_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_011\n  UNION ALL SELECT B FROM TABLE_E071_06_01_012 )\n---\nfeature: E071-06\nid: e071_06_01_02\nsql:\n- CREATE TABLE TABLE_E071_06_01_021 ( A INT )\n- CREATE TABLE TABLE_E071_06_01_022 ( B FLOAT )\n- SELECT A FROM TABLE_E071_06_01_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_021\n  UNION DISTINCT SELECT B FROM TABLE_E071_06_01_022 )\n---\nfeature: E071-06\nid: e071_06_01_03\nsql:\n- CREATE TABLE TABLE_E071_06_01_031 ( A INT )\n- CREATE TABLE TABLE_E071_06_01_032 ( B FLOAT )\n- SELECT A FROM TABLE_E071_06_01_031 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_031\n  UNION SELECT B FROM TABLE_E071_06_01_032 )\n---\nfeature: E071-06\nid: e071_06_02_01\nsql:\n- CREATE TABLE TABLE_E071_06_02_011 ( A INT )\n- CREATE TABLE TABLE_E071_06_02_012 ( B FLOAT )\n- SELECT A FROM TABLE_E071_06_02_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_02_011\n  EXCEPT DISTINCT SELECT B FROM TABLE_E071_06_02_012 )\n---\nfeature: E071-06\nid: e071_06_02_02\nsql:\n- CREATE TABLE TABLE_E071_06_02_021 ( A INT )\n- CREATE TABLE TABLE_E071_06_02_022 ( B FLOAT )\n- SELECT A FROM TABLE_E071_06_02_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_02_021\n  EXCEPT SELECT B FROM TABLE_E071_06_02_022 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-01.tests.yml",
    "content": "feature: E081-01\nid: e081_01_01_01\nsql:\n- CREATE TABLE TABLE_E081_01_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_01_01_01\n- GRANT SELECT ON TABLE TABLE_E081_01_01_011 TO ROLE_E081_01_01_01\n---\nfeature: E081-01\nid: e081_01_01_02\nsql:\n- CREATE TABLE TABLE_E081_01_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_01_01_02\n- GRANT SELECT ON TABLE_E081_01_01_021 TO ROLE_E081_01_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-02.tests.yml",
    "content": "feature: E081-02\nid: e081_02_01_01\nsql:\n- CREATE TABLE TABLE_E081_02_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_02_01_01\n- GRANT DELETE ON TABLE TABLE_E081_02_01_011 TO ROLE_E081_02_01_01\n---\nfeature: E081-02\nid: e081_02_01_02\nsql:\n- CREATE TABLE TABLE_E081_02_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_02_01_02\n- GRANT DELETE ON TABLE_E081_02_01_021 TO ROLE_E081_02_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-03.tests.yml",
    "content": "feature: E081-03\nid: e081_03_01_01\nsql:\n- CREATE TABLE TABLE_E081_03_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_03_01_01\n- GRANT INSERT ON TABLE TABLE_E081_03_01_011 TO ROLE_E081_03_01_01\n---\nfeature: E081-03\nid: e081_03_01_02\nsql:\n- CREATE TABLE TABLE_E081_03_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_03_01_02\n- GRANT INSERT ON TABLE_E081_03_01_021 TO ROLE_E081_03_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-04.tests.yml",
    "content": "feature: E081-04\nid: e081_04_01_01\nsql:\n- CREATE TABLE TABLE_E081_04_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_04_01_01\n- GRANT UPDATE ON TABLE TABLE_E081_04_01_011 TO ROLE_E081_04_01_01\n---\nfeature: E081-04\nid: e081_04_01_02\nsql:\n- CREATE TABLE TABLE_E081_04_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_04_01_02\n- GRANT UPDATE ON TABLE_E081_04_01_021 TO ROLE_E081_04_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-05.tests.yml",
    "content": "feature: E081-05\nid: e081_05_01_01\nsql:\n- CREATE TABLE TABLE_E081_05_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_05_01_01\n- GRANT UPDATE ( A ) ON TABLE TABLE_E081_05_01_011 TO ROLE_E081_05_01_01\n---\nfeature: E081-05\nid: e081_05_01_02\nsql:\n- CREATE TABLE TABLE_E081_05_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_05_01_02\n- GRANT UPDATE ( A ) ON TABLE_E081_05_01_021 TO ROLE_E081_05_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-06.tests.yml",
    "content": "feature: E081-06\nid: e081_06_01_01\nsql:\n- CREATE TABLE TABLE_E081_06_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_06_01_01\n- GRANT REFERENCES ON TABLE TABLE_E081_06_01_011 TO ROLE_E081_06_01_01\n---\nfeature: E081-06\nid: e081_06_01_02\nsql:\n- CREATE TABLE TABLE_E081_06_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_06_01_02\n- GRANT REFERENCES ON TABLE_E081_06_01_021 TO ROLE_E081_06_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-07.tests.yml",
    "content": "feature: E081-07\nid: e081_07_01_01\nsql:\n- CREATE TABLE TABLE_E081_07_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_07_01_01\n- GRANT REFERENCES ( A ) ON TABLE TABLE_E081_07_01_011 TO ROLE_E081_07_01_01\n---\nfeature: E081-07\nid: e081_07_01_02\nsql:\n- CREATE TABLE TABLE_E081_07_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_07_01_02\n- GRANT REFERENCES ( A ) ON TABLE_E081_07_01_021 TO ROLE_E081_07_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-08.tests.yml",
    "content": "feature: E081-08\nid: e081_08_01_01\nsql:\n- CREATE TABLE TABLE_E081_08_01_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_01_01\n- GRANT SELECT ON TABLE TABLE_E081_08_01_011 TO ROLE_E081_08_01_01 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_01_02\nsql:\n- CREATE TABLE TABLE_E081_08_01_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_01_02\n- GRANT SELECT ON TABLE_E081_08_01_021 TO ROLE_E081_08_01_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_02_01\nsql:\n- CREATE TABLE TABLE_E081_08_02_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_02_01\n- GRANT DELETE ON TABLE TABLE_E081_08_02_011 TO ROLE_E081_08_02_01 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_02_02\nsql:\n- CREATE TABLE TABLE_E081_08_02_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_02_02\n- GRANT DELETE ON TABLE_E081_08_02_021 TO ROLE_E081_08_02_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_03_01\nsql:\n- CREATE TABLE TABLE_E081_08_03_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_03_01\n- GRANT INSERT ON TABLE TABLE_E081_08_03_011 TO ROLE_E081_08_03_01 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_03_02\nsql:\n- CREATE TABLE TABLE_E081_08_03_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_03_02\n- GRANT INSERT ON TABLE_E081_08_03_021 TO ROLE_E081_08_03_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_04_01\nsql:\n- CREATE TABLE TABLE_E081_08_04_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_04_01\n- GRANT UPDATE ON TABLE TABLE_E081_08_04_011 TO ROLE_E081_08_04_01 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_04_02\nsql:\n- CREATE TABLE TABLE_E081_08_04_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_04_02\n- GRANT UPDATE ON TABLE_E081_08_04_021 TO ROLE_E081_08_04_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_05_01\nsql:\n- CREATE TABLE TABLE_E081_08_05_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_05_01\n- GRANT UPDATE ( A ) ON TABLE TABLE_E081_08_05_011 TO ROLE_E081_08_05_01 WITH GRANT\n  OPTION\n---\nfeature: E081-08\nid: e081_08_05_02\nsql:\n- CREATE TABLE TABLE_E081_08_05_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_05_02\n- GRANT UPDATE ( A ) ON TABLE_E081_08_05_021 TO ROLE_E081_08_05_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_06_01\nsql:\n- CREATE TABLE TABLE_E081_08_06_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_06_01\n- GRANT REFERENCES ON TABLE TABLE_E081_08_06_011 TO ROLE_E081_08_06_01 WITH GRANT\n  OPTION\n---\nfeature: E081-08\nid: e081_08_06_02\nsql:\n- CREATE TABLE TABLE_E081_08_06_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_06_02\n- GRANT REFERENCES ON TABLE_E081_08_06_021 TO ROLE_E081_08_06_02 WITH GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_07_01\nsql:\n- CREATE TABLE TABLE_E081_08_07_011 ( A INT )\n- CREATE ROLE ROLE_E081_08_07_01\n- GRANT REFERENCES ( A ) ON TABLE TABLE_E081_08_07_011 TO ROLE_E081_08_07_01 WITH\n  GRANT OPTION\n---\nfeature: E081-08\nid: e081_08_07_02\nsql:\n- CREATE TABLE TABLE_E081_08_07_021 ( A INT )\n- CREATE ROLE ROLE_E081_08_07_02\n- GRANT REFERENCES ( A ) ON TABLE_E081_08_07_021 TO ROLE_E081_08_07_02 WITH GRANT\n  OPTION\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-09.tests.yml",
    "content": "feature: E081-09\nid: e081_09_01_01\nsql:\n- CREATE SCHEMA TABLE_E081_09_01_011\n- CREATE ROLE ROLE_E081_09_01_01\n- GRANT USAGE ON TABLE TABLE_E081_09_01_011 TO ROLE_E081_09_01_01\n---\nfeature: E081-09\nid: e081_09_01_02\nsql:\n- CREATE SCHEMA TABLE_E081_09_01_021\n- CREATE ROLE ROLE_E081_09_01_02\n- GRANT USAGE ON TABLE_E081_09_01_021 TO ROLE_E081_09_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E081-10.tests.yml",
    "content": "feature: E081-10\nid: e081_10_01_01\nsql:\n- CREATE SCHEMA TABLE_E081_10_01_011\n- CREATE ROLE ROLE_E081_10_01_01\n- GRANT EXECUTE ON TABLE TABLE_E081_10_01_011 TO ROLE_E081_10_01_01\n---\nfeature: E081-10\nid: e081_10_01_02\nsql:\n- CREATE SCHEMA TABLE_E081_10_01_021\n- CREATE ROLE ROLE_E081_10_01_02\n- GRANT EXECUTE ON TABLE_E081_10_01_021 TO ROLE_E081_10_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-01.tests.yml",
    "content": "feature: E091-01\nid: e091_01_01_01\nsql:\n- CREATE TABLE TABLE_E091_01_01_01 ( A FLOAT )\n- SELECT AVG ( A ) FROM TABLE_E091_01_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-02.tests.yml",
    "content": "feature: E091-02\nid: e091_02_01_01\nsql:\n- CREATE TABLE TABLE_E091_02_01_01 ( A FLOAT )\n- SELECT COUNT ( * ) FROM TABLE_E091_02_01_01\n---\nfeature: E091-02\nid: e091_02_01_02\nsql:\n- CREATE TABLE TABLE_E091_02_01_02 ( A FLOAT )\n- SELECT COUNT ( A ) FROM TABLE_E091_02_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-03.tests.yml",
    "content": "feature: E091-03\nid: e091_03_01_01\nsql:\n- CREATE TABLE TABLE_E091_03_01_01 ( A FLOAT )\n- SELECT MAX ( A ) FROM TABLE_E091_03_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-04.tests.yml",
    "content": "feature: E091-04\nid: e091_04_01_01\nsql:\n- CREATE TABLE TABLE_E091_04_01_01 ( A FLOAT )\n- SELECT MIN ( A ) FROM TABLE_E091_04_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-05.tests.yml",
    "content": "feature: E091-05\nid: e091_05_01_01\nsql:\n- CREATE TABLE TABLE_E091_05_01_01 ( A FLOAT )\n- SELECT SUM ( A ) FROM TABLE_E091_05_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-06.tests.yml",
    "content": "feature: E091-06\nid: e091_06_01_01\nsql:\n- CREATE TABLE TABLE_E091_06_01_01 ( A FLOAT )\n- SELECT AVG ( ALL A ) FROM TABLE_E091_06_01_01\n---\nfeature: E091-06\nid: e091_06_02_01\nsql:\n- CREATE TABLE TABLE_E091_06_02_01 ( A FLOAT )\n- SELECT COUNT ( ALL A ) FROM TABLE_E091_06_02_01\n---\nfeature: E091-06\nid: e091_06_03_01\nsql:\n- CREATE TABLE TABLE_E091_06_03_01 ( A FLOAT )\n- SELECT MAX ( ALL A ) FROM TABLE_E091_06_03_01\n---\nfeature: E091-06\nid: e091_06_04_01\nsql:\n- CREATE TABLE TABLE_E091_06_04_01 ( A FLOAT )\n- SELECT MIN ( ALL A ) FROM TABLE_E091_06_04_01\n---\nfeature: E091-06\nid: e091_06_05_01\nsql:\n- CREATE TABLE TABLE_E091_06_05_01 ( A FLOAT )\n- SELECT SUM ( ALL A ) FROM TABLE_E091_06_05_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E091-07.tests.yml",
    "content": "feature: E091-07\nid: e091_07_01_01\nsql:\n- CREATE TABLE TABLE_E091_07_01_01 ( A FLOAT )\n- SELECT AVG ( DISTINCT A ) FROM TABLE_E091_07_01_01\n---\nfeature: E091-07\nid: e091_07_01_02\nsql:\n- CREATE TABLE TABLE_E091_07_01_02 ( A FLOAT )\n- SELECT COUNT ( DISTINCT A ) FROM TABLE_E091_07_01_02\n---\nfeature: E091-07\nid: e091_07_01_03\nsql:\n- CREATE TABLE TABLE_E091_07_01_03 ( A FLOAT )\n- SELECT MAX ( DISTINCT A ) FROM TABLE_E091_07_01_03\n---\nfeature: E091-07\nid: e091_07_01_04\nsql:\n- CREATE TABLE TABLE_E091_07_01_04 ( A FLOAT )\n- SELECT MIN ( DISTINCT A ) FROM TABLE_E091_07_01_04\n---\nfeature: E091-07\nid: e091_07_01_05\nsql:\n- CREATE TABLE TABLE_E091_07_01_05 ( A FLOAT )\n- SELECT SUM ( DISTINCT A ) FROM TABLE_E091_07_01_05\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E101-01.tests.yml",
    "content": "feature: E101-01\nid: e101_01_01_01\nsql:\n- CREATE TABLE TABLE_E101_01_01_01 ( A INT, B INT )\n- INSERT INTO TABLE_E101_01_01_01 VALUES ( 1, 2 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E101-03.tests.yml",
    "content": "feature: E101-03\nid: e101_03_01_01\nsql:\n- CREATE TABLE TABLE_E101_03_01_01 ( A INT, B INT )\n- INSERT INTO TABLE_E101_03_01_01 VALUES ( 1, 2 )\n- UPDATE TABLE_E101_03_01_01 SET A = 3, B = 4\n---\nfeature: E101-03\nid: e101_03_01_02\nsql:\n- CREATE TABLE TABLE_E101_03_01_02 ( A INT, B INT )\n- INSERT INTO TABLE_E101_03_01_02 VALUES ( 1, 2 )\n- UPDATE TABLE_E101_03_01_02 SET A = 3, B = 4 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E101-04.tests.yml",
    "content": "feature: E101-04\nid: e101_04_01_01\nsql:\n- CREATE TABLE TABLE_E101_04_01_01 ( A INT, B INT )\n- INSERT INTO TABLE_E101_04_01_01 VALUES ( 1, 2 )\n- DELETE FROM TABLE_E101_04_01_01\n---\nfeature: E101-04\nid: e101_04_01_02\nsql:\n- CREATE TABLE TABLE_E101_04_01_02 ( A INT, B INT )\n- INSERT INTO TABLE_E101_04_01_02 VALUES ( 1, 2 )\n- DELETE FROM TABLE_E101_04_01_02 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E111.tests.yml",
    "content": "feature: E111\nid: e111_01_01\nsql:\n- CREATE TABLE TABLE_E111_01_011 ( A INT )\n- CREATE TABLE TABLE_E111_01_012 ( A INT )\n- SELECT A INTO TABLE_E111_01_013 FROM TABLE_E111_01_011\n---\nfeature: E111\nid: e111_01_02\nsql:\n- CREATE TABLE TABLE_E111_01_021 ( A INT )\n- CREATE TABLE TABLE_E111_01_022 ( A INT )\n- SELECT A INTO TABLE_E111_01_023 FROM TABLE_E111_01_021 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-01.tests.yml",
    "content": "feature: E121-01\nid: e121_01_01_01\nsql:\n- CREATE TABLE TABLE_E121_01_01_01 ( A INT )\n- DECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_01\n---\nfeature: E121-01\nid: e121_01_01_02\nsql:\n- CREATE TABLE TABLE_E121_01_01_02 ( A INT )\n- DECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_02 FOR READ ONLY\n---\nfeature: E121-01\nid: e121_01_01_03\nsql:\n- CREATE TABLE TABLE_E121_01_01_03 ( A INT )\n- DECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_03 FOR UPDATE\n---\nfeature: E121-01\nid: e121_01_01_04\nsql:\n- CREATE TABLE TABLE_E121_01_01_04 ( A INT )\n- DECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_04 FOR UPDATE OF TABLE_E121_01_01_04\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-02.tests.yml",
    "content": "feature: E121-02\nid: e121_02_01_01\nsql:\n- CREATE TABLE TABLE_E121_02_01_01 ( A INT, B INT )\n- DECLARE CUR_E121_02_01_01 CURSOR FOR SELECT A FROM TABLE_E121_02_01_01 ORDER BY\n  B\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-03.tests.yml",
    "content": "feature: E121-03\nid: e121_03_01_01\nsql:\n- CREATE TABLE TABLE_E121_03_01_01 ( A INT )\n- DECLARE CUR_E121_03_01_01 CURSOR FOR SELECT A FROM TABLE_E121_03_01_01 ORDER BY\n  A +5\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-04.tests.yml",
    "content": "feature: E121-04\nid: e121_04_01_01\nsql:\n- CREATE TABLE TABLE_E121_04_01_01 ( A INT )\n- DECLARE CUR_E121_04_01_01 CURSOR FOR SELECT A FROM TABLE_E121_04_01_01\n- OPEN CUR_E121_04_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-06.tests.yml",
    "content": "feature: E121-06\nid: e121_06_01_01\nsql:\n- CREATE TABLE TABLE_E121_06_01_01 ( A INT )\n- DECLARE CUR_E121_06_01_01 CURSOR FOR SELECT A FROM TABLE_E121_06_01_01\n- UPDATE TABLE_E121_06_01_01 SET A = 1 WHERE CURRENT OF CUR_E121_06_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-07.tests.yml",
    "content": "feature: E121-07\nid: e121_07_01_01\nsql:\n- CREATE TABLE TABLE_E121_07_01_01 ( A INT )\n- DECLARE CUR_E121_07_01_01 CURSOR FOR SELECT A FROM TABLE_E121_07_01_01\n- DELETE FROM ONLY ( TABLE_E121_07_01_01 ) WHERE CURRENT OF CUR_E121_07_01_01\n---\nfeature: E121-07\nid: e121_07_01_02\nsql:\n- CREATE TABLE TABLE_E121_07_01_02 ( A INT )\n- DECLARE CUR_E121_07_01_02 CURSOR FOR SELECT A FROM TABLE_E121_07_01_02\n- DELETE FROM TABLE_E121_07_01_02 WHERE CURRENT OF CUR_E121_07_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-08.tests.yml",
    "content": "feature: E121-08\nid: e121_08_01_01\nsql:\n- CREATE TABLE TABLE_E121_08_01_01 ( A INT )\n- DECLARE CUR_E121_08_01_01 CURSOR FOR SELECT A FROM TABLE_E121_08_01_01\n- OPEN CUR_E121_08_01_01\n- CLOSE CUR_E121_08_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-10.tests.yml",
    "content": "feature: E121-10\nid: e121_10_01_01\nsql:\n- CREATE TABLE TABLE_E121_10_01_011 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_012 ( A INT )\n- DECLARE CUR_E121_10_01_01 CURSOR FOR SELECT A FROM TABLE_E121_10_01_011\n- OPEN CUR_E121_10_01_01\n- FETCH ABSOLUTE 2 FROM CUR_E121_10_01_01 INTO TABLE_E121_10_01_012\n---\nfeature: E121-10\nid: e121_10_01_02\nsql:\n- CREATE TABLE TABLE_E121_10_01_021 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_022 ( A INT )\n- DECLARE CUR_E121_10_01_02 CURSOR FOR SELECT A FROM TABLE_E121_10_01_021\n- OPEN CUR_E121_10_01_02\n- FETCH CUR_E121_10_01_02 INTO TABLE_E121_10_01_022\n---\nfeature: E121-10\nid: e121_10_01_03\nsql:\n- CREATE TABLE TABLE_E121_10_01_031 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_032 ( A INT )\n- DECLARE CUR_E121_10_01_03 CURSOR FOR SELECT A FROM TABLE_E121_10_01_031\n- OPEN CUR_E121_10_01_03\n- FETCH FIRST FROM CUR_E121_10_01_03 INTO TABLE_E121_10_01_032\n---\nfeature: E121-10\nid: e121_10_01_04\nsql:\n- CREATE TABLE TABLE_E121_10_01_041 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_042 ( A INT )\n- DECLARE CUR_E121_10_01_04 CURSOR FOR SELECT A FROM TABLE_E121_10_01_041\n- OPEN CUR_E121_10_01_04\n- FETCH FROM CUR_E121_10_01_04 INTO TABLE_E121_10_01_042\n---\nfeature: E121-10\nid: e121_10_01_05\nsql:\n- CREATE TABLE TABLE_E121_10_01_051 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_052 ( A INT )\n- DECLARE CUR_E121_10_01_05 CURSOR FOR SELECT A FROM TABLE_E121_10_01_051\n- OPEN CUR_E121_10_01_05\n- FETCH LAST FROM CUR_E121_10_01_05 INTO TABLE_E121_10_01_052\n---\nfeature: E121-10\nid: e121_10_01_06\nsql:\n- CREATE TABLE TABLE_E121_10_01_061 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_062 ( A INT )\n- DECLARE CUR_E121_10_01_06 CURSOR FOR SELECT A FROM TABLE_E121_10_01_061\n- OPEN CUR_E121_10_01_06\n- FETCH NEXT FROM CUR_E121_10_01_06 INTO TABLE_E121_10_01_062\n---\nfeature: E121-10\nid: e121_10_01_07\nsql:\n- CREATE TABLE TABLE_E121_10_01_071 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_072 ( A INT )\n- DECLARE CUR_E121_10_01_07 CURSOR FOR SELECT A FROM TABLE_E121_10_01_071\n- OPEN CUR_E121_10_01_07\n- FETCH PRIOR FROM CUR_E121_10_01_07 INTO TABLE_E121_10_01_072\n---\nfeature: E121-10\nid: e121_10_01_08\nsql:\n- CREATE TABLE TABLE_E121_10_01_081 ( A INT )\n- CREATE TABLE TABLE_E121_10_01_082 ( A INT )\n- DECLARE CUR_E121_10_01_08 CURSOR FOR SELECT A FROM TABLE_E121_10_01_081\n- OPEN CUR_E121_10_01_08\n- FETCH RELATIVE 2 FROM CUR_E121_10_01_08 INTO TABLE_E121_10_01_082\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E121-17.tests.yml",
    "content": "feature: E121-17\nid: e121_17_01_01\nsql:\n- CREATE TABLE TABLE_E121_17_01_01 ( A INT )\n- DECLARE CUR_E121_17_01_01 CURSOR WITH HOLD FOR SELECT A FROM TABLE_E121_17_01_01\n---\nfeature: E121-17\nid: e121_17_01_02\nsql:\n- CREATE TABLE TABLE_E121_17_01_02 ( A INT )\n- DECLARE CUR_E121_17_01_02 CURSOR WITHOUT HOLD FOR SELECT A FROM TABLE_E121_17_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E131.tests.yml",
    "content": "feature: E131\nid: e131_01_01\nsql: SELECT NULL\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-01.tests.yml",
    "content": "feature: E141-01\nid: e141_01_01_01\nsql: CREATE TABLE TABLE_E141_01_01_01 ( A INT CONSTRAINT CONST_E141_01_01_01 NOT NULL\n  )\n---\nfeature: E141-01\nid: e141_01_01_02\nsql: CREATE TABLE TABLE_E141_01_01_02 ( A INT NOT NULL )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-02.tests.yml",
    "content": "feature: E141-02\nid: e141_02_01_01\nsql: CREATE TABLE TABLE_E141_02_01_01 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT\n  CONST_E141_02_01_01 UNIQUE ( A ) )\n---\nfeature: E141-02\nid: e141_02_01_02\nsql: CREATE TABLE TABLE_E141_02_01_02 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT\n  CONST_E141_02_01_02 UNIQUE ( A , B ) )\n---\nfeature: E141-02\nid: e141_02_01_03\nsql: CREATE TABLE TABLE_E141_02_01_03 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A\n  ) )\n---\nfeature: E141-02\nid: e141_02_01_04\nsql: CREATE TABLE TABLE_E141_02_01_04 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A\n  , B ) )\n---\nfeature: E141-02\nid: e141_02_02_01\nsql: CREATE TABLE TABLE_E141_02_02_01 ( A INT NOT NULL CONSTRAINT CONST_E141_02_02_01\n  UNIQUE )\n---\nfeature: E141-02\nid: e141_02_02_02\nsql: CREATE TABLE TABLE_E141_02_02_02 ( A INT NOT NULL UNIQUE )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-03.tests.yml",
    "content": "feature: E141-03\nid: e141_03_01_01\nsql: CREATE TABLE TABLE_E141_03_01_01 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT\n  CONST_E141_03_01_01 PRIMARY KEY ( A ) )\n---\nfeature: E141-03\nid: e141_03_01_02\nsql: CREATE TABLE TABLE_E141_03_01_02 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT\n  CONST_E141_03_01_02 PRIMARY KEY ( A , B ) )\n---\nfeature: E141-03\nid: e141_03_01_03\nsql: CREATE TABLE TABLE_E141_03_01_03 ( A INT NOT NULL, B INT NOT NULL, PRIMARY KEY\n  ( A ) )\n---\nfeature: E141-03\nid: e141_03_01_04\nsql: CREATE TABLE TABLE_E141_03_01_04 ( A INT NOT NULL, B INT NOT NULL, PRIMARY KEY\n  ( A , B ) )\n---\nfeature: E141-03\nid: e141_03_02_01\nsql: CREATE TABLE TABLE_E141_03_02_01 ( A INT NOT NULL CONSTRAINT CONST_E141_03_02_01\n  PRIMARY KEY )\n---\nfeature: E141-03\nid: e141_03_02_02\nsql: CREATE TABLE TABLE_E141_03_02_02 ( A INT NOT NULL PRIMARY KEY )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-04.tests.yml",
    "content": "feature: E141-04\nid: e141_04_01_01\nsql:\n- CREATE TABLE TABLE_E141_04_01_011 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_012 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_01\n  REFERENCES TABLE_E141_04_01_011 ( A ) )\n---\nfeature: E141-04\nid: e141_04_01_02\nsql:\n- CREATE TABLE TABLE_E141_04_01_021 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_022 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_02\n  REFERENCES TABLE_E141_04_01_021 ( A ) ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_01_03\nsql:\n- CREATE TABLE TABLE_E141_04_01_031 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_032 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_03\n  REFERENCES TABLE_E141_04_01_031 ( A ) ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_01_04\nsql:\n- CREATE TABLE TABLE_E141_04_01_041 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_042 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_04\n  REFERENCES TABLE_E141_04_01_041 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_01_05\nsql:\n- CREATE TABLE TABLE_E141_04_01_051 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_052 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_051\n  ( A ) )\n---\nfeature: E141-04\nid: e141_04_01_06\nsql:\n- CREATE TABLE TABLE_E141_04_01_061 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_062 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_061\n  ( A ) ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_01_07\nsql:\n- CREATE TABLE TABLE_E141_04_01_071 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_072 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_071\n  ( A ) ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_01_08\nsql:\n- CREATE TABLE TABLE_E141_04_01_081 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_01_082 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_081\n  ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_02_01\nsql:\n- CREATE TABLE TABLE_E141_04_02_011 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_02_012 ( B INT NOT NULL CONSTRAINT CONST_E141_04_02_01\n  REFERENCES TABLE_E141_04_02_011 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_02_02\nsql:\n- CREATE TABLE TABLE_E141_04_02_021 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_02_022 ( B INT NOT NULL REFERENCES TABLE_E141_04_02_021\n  ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_01\nsql:\n- CREATE TABLE TABLE_E141_04_03_011 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_012 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_01\n  FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_011 ( A ) )\n---\nfeature: E141-04\nid: e141_04_03_02\nsql:\n- CREATE TABLE TABLE_E141_04_03_021 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_022 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_02\n  FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_021 ( A ) ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_03\nsql:\n- CREATE TABLE TABLE_E141_04_03_031 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_032 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_03\n  FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_031 ( A ) ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_04\nsql:\n- CREATE TABLE TABLE_E141_04_03_041 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_042 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_04\n  FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_041 ( A ) ON UPDATE NO ACTION ON DELETE\n  NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_05\nsql:\n- CREATE TABLE TABLE_E141_04_03_051 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_052 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES\n  TABLE_E141_04_03_051 ( A ) )\n---\nfeature: E141-04\nid: e141_04_03_06\nsql:\n- CREATE TABLE TABLE_E141_04_03_061 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_062 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES\n  TABLE_E141_04_03_061 ( A ) ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_07\nsql:\n- CREATE TABLE TABLE_E141_04_03_071 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_072 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES\n  TABLE_E141_04_03_071 ( A ) ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_03_08\nsql:\n- CREATE TABLE TABLE_E141_04_03_081 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_03_082 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES\n  TABLE_E141_04_03_081 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_04_01\nsql:\n- CREATE TABLE TABLE_E141_04_04_011 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_04_012 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_04_01\n  FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_04_011 ( A ) ON DELETE NO ACTION ON UPDATE\n  NO ACTION )\n---\nfeature: E141-04\nid: e141_04_04_02\nsql:\n- CREATE TABLE TABLE_E141_04_04_021 ( A INT NOT NULL, UNIQUE ( A ) )\n- CREATE TABLE TABLE_E141_04_04_022 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES\n  TABLE_E141_04_04_021 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_05_01\nsql:\n- CREATE TABLE TABLE_E141_04_05_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_01\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_011 ( A, B ) )\n---\nfeature: E141-04\nid: e141_04_05_02\nsql:\n- CREATE TABLE TABLE_E141_04_05_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_022 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_02\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_021 ( A, B ) ON DELETE NO ACTION\n  )\n---\nfeature: E141-04\nid: e141_04_05_03\nsql:\n- CREATE TABLE TABLE_E141_04_05_031 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_032 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_03\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_031 ( A, B ) ON UPDATE NO ACTION\n  )\n---\nfeature: E141-04\nid: e141_04_05_04\nsql:\n- CREATE TABLE TABLE_E141_04_05_041 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_042 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_04\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_041 ( A, B ) ON UPDATE NO ACTION\n  ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_05_05\nsql:\n- CREATE TABLE TABLE_E141_04_05_051 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_052 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_04_05_051 ( A, B ) )\n---\nfeature: E141-04\nid: e141_04_05_06\nsql:\n- CREATE TABLE TABLE_E141_04_05_061 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_062 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_04_05_061 ( A, B ) ON DELETE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_05_07\nsql:\n- CREATE TABLE TABLE_E141_04_05_071 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_072 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_04_05_071 ( A, B ) ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_05_08\nsql:\n- CREATE TABLE TABLE_E141_04_05_081 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_05_082 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_04_05_081 ( A, B ) ON UPDATE NO ACTION ON DELETE\n  NO ACTION )\n---\nfeature: E141-04\nid: e141_04_06_01\nsql:\n- CREATE TABLE TABLE_E141_04_06_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_06_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_06_01\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_06_011 ( A, B ) ON DELETE NO ACTION\n  ON UPDATE NO ACTION )\n---\nfeature: E141-04\nid: e141_04_06_02\nsql:\n- CREATE TABLE TABLE_E141_04_06_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_04_06_022 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_04_06_021 ( A, B ) ON DELETE NO ACTION ON UPDATE\n  NO ACTION )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-06.tests.yml",
    "content": "feature: E141-06\nid: e141_06_01_01\nsql: CREATE TABLE TABLE_E141_06_01_011 ( A INT CHECK ( A = 1 ) )\n---\nfeature: E141-06\nid: e141_06_01_02\nsql: CREATE TABLE TABLE_E141_06_01_021 ( A INT CONSTRAINT CONST_E141_06_01_02 CHECK\n  ( A = 1 ) )\n---\nfeature: E141-06\nid: e141_06_02_01\nsql: CREATE TABLE TABLE_E141_06_02_011 ( A INT, CHECK ( A = 1 ) )\n---\nfeature: E141-06\nid: e141_06_02_02\nsql: CREATE TABLE TABLE_E141_06_02_021 ( A INT, CONSTRAINT CONST_E141_06_02_02 CHECK\n  ( A = 1 ) )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-07.tests.yml",
    "content": "feature: E141-07\nid: e141_07_01_01\nsql: CREATE TABLE TABLE_E141_07_01_01 ( A NAME DEFAULT CURRENT_CATALOG )\n---\nfeature: E141-07\nid: e141_07_02_01\nsql: CREATE TABLE TABLE_E141_07_02_01 ( A DATE DEFAULT CURRENT_DATE )\n---\nfeature: E141-07\nid: e141_07_03_01\nsql: CREATE TABLE TABLE_E141_07_03_01 ( A INT DEFAULT CURRENT_PATH )\n---\nfeature: E141-07\nid: e141_07_04_01\nsql: CREATE TABLE TABLE_E141_07_04_01 ( A NAME DEFAULT CURRENT_ROLE )\n---\nfeature: E141-07\nid: e141_07_05_01\nsql: CREATE TABLE TABLE_E141_07_05_01 ( A NAME DEFAULT CURRENT_SCHEMA )\n---\nfeature: E141-07\nid: e141_07_06_01\nsql: CREATE TABLE TABLE_E141_07_06_01 ( A NAME DEFAULT CURRENT_USER )\n---\nfeature: E141-07\nid: e141_07_07_01\nsql: CREATE TABLE TABLE_E141_07_07_01 ( A NAME DEFAULT SESSION_USER )\n---\nfeature: E141-07\nid: e141_07_08_01\nsql: CREATE TABLE TABLE_E141_07_08_01 ( A INT DEFAULT SYSTEM_USER )\n---\nfeature: E141-07\nid: e141_07_09_01\nsql: CREATE TABLE TABLE_E141_07_09_01 ( A NAME DEFAULT USER )\n---\nfeature: E141-07\nid: e141_07_10_01\nsql: CREATE TABLE TABLE_E141_07_10_01 ( A TIME WITH TIME ZONE DEFAULT CURRENT_TIME\n  )\n---\nfeature: E141-07\nid: e141_07_11_01\nsql: CREATE TABLE TABLE_E141_07_11_01 ( A TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP\n  )\n---\nfeature: E141-07\nid: e141_07_12_01\nsql: CREATE TABLE TABLE_E141_07_12_01 ( A TIME WITH TIME ZONE DEFAULT LOCALTIME )\n---\nfeature: E141-07\nid: e141_07_13_01\nsql: CREATE TABLE TABLE_E141_07_13_01 ( A TIMESTAMP WITH TIME ZONE DEFAULT LOCALTIMESTAMP\n  )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-08.tests.yml",
    "content": "feature: E141-08\nid: e141_08_01_01\nsql: CREATE TABLE TABLE_E141_08_01_01 ( A INT, B INT, CONSTRAINT CONST_E141_08_01_01\n  UNIQUE ( A ) )\n---\nfeature: E141-08\nid: e141_08_01_02\nsql: CREATE TABLE TABLE_E141_08_01_02 ( A INT, B INT, CONSTRAINT CONST_E141_08_01_02\n  UNIQUE ( A , B ) )\n---\nfeature: E141-08\nid: e141_08_01_03\nsql: CREATE TABLE TABLE_E141_08_01_03 ( A INT, B INT, UNIQUE ( A ) )\n---\nfeature: E141-08\nid: e141_08_01_04\nsql: CREATE TABLE TABLE_E141_08_01_04 ( A INT, B INT, UNIQUE ( A , B ) )\n---\nfeature: E141-08\nid: e141_08_02_01\nsql: CREATE TABLE TABLE_E141_08_02_01 ( A INT CONSTRAINT CONST_E141_08_02_01 UNIQUE\n  )\n---\nfeature: E141-08\nid: e141_08_02_02\nsql: CREATE TABLE TABLE_E141_08_02_02 ( A INT UNIQUE )\n---\nfeature: E141-08\nid: e141_08_03_01\nsql: CREATE TABLE TABLE_E141_08_03_01 ( A INT, B INT, CONSTRAINT CONST_E141_08_03_01\n  PRIMARY KEY ( A ) )\n---\nfeature: E141-08\nid: e141_08_03_02\nsql: CREATE TABLE TABLE_E141_08_03_02 ( A INT, B INT, CONSTRAINT CONST_E141_08_03_02\n  PRIMARY KEY ( A , B ) )\n---\nfeature: E141-08\nid: e141_08_03_03\nsql: CREATE TABLE TABLE_E141_08_03_03 ( A INT, B INT, PRIMARY KEY ( A ) )\n---\nfeature: E141-08\nid: e141_08_03_04\nsql: CREATE TABLE TABLE_E141_08_03_04 ( A INT, B INT, PRIMARY KEY ( A , B ) )\n---\nfeature: E141-08\nid: e141_08_04_01\nsql: CREATE TABLE TABLE_E141_08_04_01 ( A INT CONSTRAINT CONST_E141_08_04_01 PRIMARY\n  KEY )\n---\nfeature: E141-08\nid: e141_08_04_02\nsql: CREATE TABLE TABLE_E141_08_04_02 ( A INT PRIMARY KEY )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E141-10.tests.yml",
    "content": "feature: E141-10\nid: e141_10_01_01\nsql:\n- CREATE TABLE TABLE_E141_10_01_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_01\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_011 ( B, A ) )\n---\nfeature: E141-10\nid: e141_10_01_02\nsql:\n- CREATE TABLE TABLE_E141_10_01_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_022 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_02\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_021 ( B, A ) ON DELETE NO ACTION\n  )\n---\nfeature: E141-10\nid: e141_10_01_03\nsql:\n- CREATE TABLE TABLE_E141_10_01_031 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_032 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_03\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_031 ( B, A ) ON UPDATE NO ACTION\n  )\n---\nfeature: E141-10\nid: e141_10_01_04\nsql:\n- CREATE TABLE TABLE_E141_10_01_041 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_042 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_04\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_041 ( B, A ) ON UPDATE NO ACTION\n  ON DELETE NO ACTION )\n---\nfeature: E141-10\nid: e141_10_01_05\nsql:\n- CREATE TABLE TABLE_E141_10_01_051 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_052 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_10_01_051 ( B, A ) )\n---\nfeature: E141-10\nid: e141_10_01_06\nsql:\n- CREATE TABLE TABLE_E141_10_01_061 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_062 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_10_01_061 ( B, A ) ON DELETE NO ACTION )\n---\nfeature: E141-10\nid: e141_10_01_07\nsql:\n- CREATE TABLE TABLE_E141_10_01_071 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_072 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_10_01_071 ( B, A ) ON UPDATE NO ACTION )\n---\nfeature: E141-10\nid: e141_10_01_08\nsql:\n- CREATE TABLE TABLE_E141_10_01_081 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_01_082 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_10_01_081 ( B, A ) ON UPDATE NO ACTION ON DELETE\n  NO ACTION )\n---\nfeature: E141-10\nid: e141_10_02_01\nsql:\n- CREATE TABLE TABLE_E141_10_02_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_02_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_02_01\n  FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_02_011 ( B, A ) ON DELETE NO ACTION\n  ON UPDATE NO ACTION )\n---\nfeature: E141-10\nid: e141_10_02_02\nsql:\n- CREATE TABLE TABLE_E141_10_02_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A,\n  B ) )\n- CREATE TABLE TABLE_E141_10_02_022 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY\n  ( C, D ) REFERENCES TABLE_E141_10_02_021 ( B, A ) ON DELETE NO ACTION ON UPDATE\n  NO ACTION )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E151-01.tests.yml",
    "content": "feature: E151-01\nid: e151_01_01_01\nsql:\n- START TRANSACTION\n- COMMIT\n---\nfeature: E151-01\nid: e151_01_01_02\nsql:\n- START TRANSACTION\n- COMMIT WORK\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E151-02.tests.yml",
    "content": "feature: E151-02\nid: e151_02_01_01\nsql:\n- START TRANSACTION\n- ROLLBACK\n---\nfeature: E151-02\nid: e151_02_01_02\nsql:\n- START TRANSACTION\n- ROLLBACK WORK\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E152-01.tests.yml",
    "content": "feature: E152-01\nid: e152_01_01_01\nsql:\n- START TRANSACTION\n- SET LOCAL TRANSACTION ISOLATION LEVEL SERIALIZABLE\n---\nfeature: E152-01\nid: e152_01_01_02\nsql:\n- START TRANSACTION\n- SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E152-02.tests.yml",
    "content": "feature: E152-02\nid: e152_02_01_01\nsql:\n- START TRANSACTION\n- SET LOCAL TRANSACTION READ ONLY\n---\nfeature: E152-02\nid: e152_02_01_02\nsql:\n- START TRANSACTION\n- SET LOCAL TRANSACTION READ WRITE\n---\nfeature: E152-02\nid: e152_02_01_03\nsql:\n- START TRANSACTION\n- SET TRANSACTION READ ONLY\n---\nfeature: E152-02\nid: e152_02_01_04\nsql:\n- START TRANSACTION\n- SET TRANSACTION READ WRITE\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E153.tests.yml",
    "content": "feature: E153\nid: e153_01_01\nsql:\n- CREATE TABLE TABLE_E153_01_01 ( A INT, B INT )\n- INSERT INTO TABLE_E153_01_01 VALUES ( 1, 2 )\n- UPDATE TABLE_E153_01_01 SET A = 3, B = 4 WHERE A = ( SELECT 1 )\n---\nfeature: E153\nid: e153_02_01\nsql:\n- CREATE TABLE TABLE_E153_02_01 ( A INT, B INT )\n- INSERT INTO TABLE_E153_02_01 VALUES ( 1, 2 )\n- DELETE FROM TABLE_E153_02_01 WHERE A = ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/E/E161.tests.yml",
    "content": "feature: E161\nid: e161_01_01\nsql: SELECT 1 -- hello\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-01.tests.yml",
    "content": "feature: F031-01\nid: f031_01_01_01\nsql: CREATE TABLE TABLE_F031_01_01_01 ( A INTEGER )\n---\nfeature: F031-01\nid: f031_01_01_02\nsql: CREATE TABLE TABLE_F031_01_01_02 ( A INTEGER , B INTEGER )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-02.tests.yml",
    "content": "feature: F031-02\nid: f031_02_01_01\nsql:\n- CREATE TABLE TABLE_F031_02_01_01 ( A INTEGER )\n- CREATE VIEW VIEW_F031_02_01_01 AS SELECT A FROM TABLE_F031_02_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-03.tests.yml",
    "content": "feature: F031-03\nid: f031_03_01_01\nsql:\n- CREATE TABLE TABLE_F031_03_01_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_01\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_01 TO ROLE_F031_03_01_01\n---\nfeature: F031-03\nid: f031_03_01_02\nsql:\n- CREATE TABLE TABLE_F031_03_01_02 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_02\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_02 TO ROLE_F031_03_01_02 GRANTED BY CURRENT_ROLE\n---\nfeature: F031-03\nid: f031_03_01_03\nsql:\n- CREATE TABLE TABLE_F031_03_01_03 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_03\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_03 TO ROLE_F031_03_01_03 GRANTED BY CURRENT_USER\n---\nfeature: F031-03\nid: f031_03_01_04\nsql:\n- CREATE TABLE TABLE_F031_03_01_04 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_04\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_04 TO ROLE_F031_03_01_04 WITH GRANT OPTION\n---\nfeature: F031-03\nid: f031_03_01_05\nsql:\n- CREATE TABLE TABLE_F031_03_01_05 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_05\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_05 TO ROLE_F031_03_01_05 WITH GRANT OPTION\n  GRANTED BY CURRENT_ROLE\n---\nfeature: F031-03\nid: f031_03_01_06\nsql:\n- CREATE TABLE TABLE_F031_03_01_06 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_01_06\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_01_06 TO ROLE_F031_03_01_06 WITH GRANT OPTION\n  GRANTED BY CURRENT_USER\n---\nfeature: F031-03\nid: f031_03_02_01\nsql:\n- CREATE TABLE TABLE_F031_03_02_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_02_01\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_02_01 TO ROLE_F031_03_02_01 , ROLE_F031_03_02_01\n---\nfeature: F031-03\nid: f031_03_03_01\nsql:\n- CREATE TABLE TABLE_F031_03_03_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_01\n- GRANT DELETE ON TABLE_F031_03_03_01 TO ROLE_F031_03_03_01\n---\nfeature: F031-03\nid: f031_03_03_02\nsql:\n- CREATE TABLE TABLE_F031_03_03_02 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_02\n- GRANT INSERT ( A ) ON TABLE_F031_03_03_02 TO ROLE_F031_03_03_02\n---\nfeature: F031-03\nid: f031_03_03_03\nsql:\n- CREATE TABLE TABLE_F031_03_03_03 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_03\n- GRANT INSERT ON TABLE_F031_03_03_03 TO ROLE_F031_03_03_03\n---\nfeature: F031-03\nid: f031_03_03_04\nsql:\n- CREATE TABLE TABLE_F031_03_03_04 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_04\n- GRANT REFERENCES ( A ) ON TABLE_F031_03_03_04 TO ROLE_F031_03_03_04\n---\nfeature: F031-03\nid: f031_03_03_05\nsql:\n- CREATE TABLE TABLE_F031_03_03_05 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_05\n- GRANT REFERENCES ON TABLE_F031_03_03_05 TO ROLE_F031_03_03_05\n---\nfeature: F031-03\nid: f031_03_03_06\nsql:\n- CREATE TABLE TABLE_F031_03_03_06 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_06\n- GRANT SELECT ( A ) ON TABLE_F031_03_03_06 TO ROLE_F031_03_03_06\n---\nfeature: F031-03\nid: f031_03_03_07\nsql:\n- CREATE TABLE TABLE_F031_03_03_07 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_07\n- GRANT SELECT ON TABLE_F031_03_03_07 TO ROLE_F031_03_03_07\n---\nfeature: F031-03\nid: f031_03_03_08\nsql:\n- CREATE TABLE TABLE_F031_03_03_08 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_08\n- GRANT TRIGGER ON TABLE_F031_03_03_08 TO ROLE_F031_03_03_08\n---\nfeature: F031-03\nid: f031_03_03_09\nsql:\n- CREATE TABLE TABLE_F031_03_03_09 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_09\n- GRANT UNDER ON TABLE_F031_03_03_09 TO ROLE_F031_03_03_09\n---\nfeature: F031-03\nid: f031_03_03_10\nsql:\n- CREATE TABLE TABLE_F031_03_03_10 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_10\n- GRANT UPDATE ( A ) ON TABLE_F031_03_03_10 TO ROLE_F031_03_03_10\n---\nfeature: F031-03\nid: f031_03_03_11\nsql:\n- CREATE TABLE TABLE_F031_03_03_11 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_03_11\n- GRANT UPDATE ON TABLE_F031_03_03_11 TO ROLE_F031_03_03_11\n---\nfeature: F031-03\nid: f031_03_04_01\nsql:\n- CREATE SCHEMA TABLE_F031_03_04_01\n- CREATE ROLE ROLE_F031_03_04_01\n- GRANT EXECUTE ON TABLE_F031_03_04_01 TO ROLE_F031_03_04_01\n---\nfeature: F031-03\nid: f031_03_04_02\nsql:\n- CREATE SCHEMA TABLE_F031_03_04_02\n- CREATE ROLE ROLE_F031_03_04_02\n- GRANT USAGE ON TABLE_F031_03_04_02 TO ROLE_F031_03_04_02\n---\nfeature: F031-03\nid: f031_03_05_01\nsql:\n- CREATE TABLE TABLE_F031_03_05_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_05_01\n- GRANT ALL PRIVILEGES ON TABLE TABLE_F031_03_05_01 TO ROLE_F031_03_05_01\n---\nfeature: F031-03\nid: f031_03_05_02\nsql:\n- CREATE TABLE TABLE_F031_03_05_02 ( A INTEGER )\n- CREATE ROLE ROLE_F031_03_05_02\n- GRANT ALL PRIVILEGES ON TABLE_F031_03_05_02 TO ROLE_F031_03_05_02\n---\nfeature: F031-03\nid: f031_03_06_01\nsql:\n- CREATE DOMAIN DOMAIN1 AS INT\n- CREATE ROLE ROLE_F031_03_06_01\n- GRANT ALL PRIVILEGES ON DOMAIN DOMAIN1 TO ROLE_F031_03_06_01\n---\nfeature: F031-03\nid: f031_03_07_01\nsql:\n- CREATE COLLATION COLLATION1 FROM 'de_DE'\n- CREATE ROLE ROLE_F031_03_07_01\n- GRANT ALL PRIVILEGES ON COLLATION COLLATION1 TO ROLE_F031_03_07_01\n---\nfeature: F031-03\nid: f031_03_08_01\nsql:\n- CREATE CHARACTER SET CHARACTERSET1\n- CREATE ROLE ROLE_F031_03_08_01\n- GRANT ALL PRIVILEGES ON CHARACTER SET CHARACTERSET1 TO ROLE_F031_03_08_01\n---\nfeature: F031-03\nid: f031_03_09_01\nsql:\n- CREATE TRANSLATION TRANSLATION1\n- CREATE ROLE ROLE_F031_03_09_01\n- GRANT ALL PRIVILEGES ON TRANSLATION TRANSLATION1 TO ROLE_F031_03_09_01\n---\nfeature: F031-03\nid: f031_03_10_01\nsql:\n- CREATE TYPE TYPE1\n- CREATE ROLE ROLE_F031_03_10_01\n- GRANT ALL PRIVILEGES ON TYPE TYPE1 TO ROLE_F031_03_10_01\n---\nfeature: F031-03\nid: f031_03_11_01\nsql:\n- CREATE SEQUENCE SEQUENCE1\n- CREATE ROLE ROLE_F031_03_11_01\n- GRANT ALL PRIVILEGES ON SEQUENCE SEQUENCE1 TO ROLE_F031_03_11_01\n---\nfeature: F031-03\nid: f031_03_12_01\nsql:\n- CREATE ROLE ROLE_F031_03_12_01\n- GRANT ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FOR BAZ TO ROLE_F031_03_12_01\n---\nfeature: F031-03\nid: f031_03_12_02\nsql:\n- CREATE ROLE ROLE_F031_03_12_02\n- GRANT ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR TO ROLE_F031_03_12_02\n---\nfeature: F031-03\nid: f031_03_12_03\nsql:\n- CREATE ROLE ROLE_F031_03_12_03\n- GRANT ALL PRIVILEGES ON FUNCTION BAR FOR BAZ TO ROLE_F031_03_12_03\n---\nfeature: F031-03\nid: f031_03_12_04\nsql:\n- CREATE ROLE ROLE_F031_03_12_04\n- GRANT ALL PRIVILEGES ON FUNCTION BAR TO ROLE_F031_03_12_04\n---\nfeature: F031-03\nid: f031_03_12_05\nsql:\n- CREATE ROLE ROLE_F031_03_12_05\n- GRANT ALL PRIVILEGES ON INSTANCE METHOD BAR FOR BAZ TO ROLE_F031_03_12_05\n---\nfeature: F031-03\nid: f031_03_12_06\nsql:\n- CREATE ROLE ROLE_F031_03_12_06\n- GRANT ALL PRIVILEGES ON INSTANCE METHOD BAR TO ROLE_F031_03_12_06\n---\nfeature: F031-03\nid: f031_03_12_07\nsql:\n- CREATE ROLE ROLE_F031_03_12_07\n- GRANT ALL PRIVILEGES ON METHOD BAR FOR BAZ TO ROLE_F031_03_12_07\n---\nfeature: F031-03\nid: f031_03_12_08\nsql:\n- CREATE ROLE ROLE_F031_03_12_08\n- GRANT ALL PRIVILEGES ON METHOD BAR TO ROLE_F031_03_12_08\n---\nfeature: F031-03\nid: f031_03_12_09\nsql:\n- CREATE ROLE ROLE_F031_03_12_09\n- GRANT ALL PRIVILEGES ON PROCEDURE BAR FOR BAZ TO ROLE_F031_03_12_09\n---\nfeature: F031-03\nid: f031_03_12_10\nsql:\n- CREATE ROLE ROLE_F031_03_12_10\n- GRANT ALL PRIVILEGES ON PROCEDURE BAR TO ROLE_F031_03_12_10\n---\nfeature: F031-03\nid: f031_03_12_11\nsql:\n- CREATE ROLE ROLE_F031_03_12_11\n- GRANT ALL PRIVILEGES ON ROUTINE BAR FOR BAZ TO ROLE_F031_03_12_11\n---\nfeature: F031-03\nid: f031_03_12_12\nsql:\n- CREATE ROLE ROLE_F031_03_12_12\n- GRANT ALL PRIVILEGES ON ROUTINE BAR TO ROLE_F031_03_12_12\n---\nfeature: F031-03\nid: f031_03_12_13\nsql:\n- CREATE ROLE ROLE_F031_03_12_13\n- GRANT ALL PRIVILEGES ON SPECIFIC CONSTRUCTOR METHOD FOO TO ROLE_F031_03_12_13\n---\nfeature: F031-03\nid: f031_03_12_14\nsql:\n- CREATE ROLE ROLE_F031_03_12_14\n- GRANT ALL PRIVILEGES ON SPECIFIC FUNCTION FOO TO ROLE_F031_03_12_14\n---\nfeature: F031-03\nid: f031_03_12_15\nsql:\n- CREATE ROLE ROLE_F031_03_12_15\n- GRANT ALL PRIVILEGES ON SPECIFIC INSTANCE METHOD FOO TO ROLE_F031_03_12_15\n---\nfeature: F031-03\nid: f031_03_12_16\nsql:\n- CREATE ROLE ROLE_F031_03_12_16\n- GRANT ALL PRIVILEGES ON SPECIFIC METHOD FOO TO ROLE_F031_03_12_16\n---\nfeature: F031-03\nid: f031_03_12_17\nsql:\n- CREATE ROLE ROLE_F031_03_12_17\n- GRANT ALL PRIVILEGES ON SPECIFIC PROCEDURE FOO TO ROLE_F031_03_12_17\n---\nfeature: F031-03\nid: f031_03_12_18\nsql:\n- CREATE ROLE ROLE_F031_03_12_18\n- GRANT ALL PRIVILEGES ON SPECIFIC ROUTINE FOO TO ROLE_F031_03_12_18\n---\nfeature: F031-03\nid: f031_03_12_19\nsql:\n- CREATE ROLE ROLE_F031_03_12_19\n- GRANT ALL PRIVILEGES ON SPECIFIC STATIC METHOD FOO TO ROLE_F031_03_12_19\n---\nfeature: F031-03\nid: f031_03_12_20\nsql:\n- CREATE ROLE ROLE_F031_03_12_20\n- GRANT ALL PRIVILEGES ON STATIC METHOD BAR FOR BAZ TO ROLE_F031_03_12_20\n---\nfeature: F031-03\nid: f031_03_12_21\nsql:\n- CREATE ROLE ROLE_F031_03_12_21\n- GRANT ALL PRIVILEGES ON STATIC METHOD BAR TO ROLE_F031_03_12_21\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-04.tests.yml",
    "content": "feature: F031-04\nid: f031_04_01_01\nsql:\n- CREATE TABLE TABLE_F031_04_01_01 ( A INTEGER )\n- ALTER TABLE TABLE_F031_04_01_01 ADD B INT\n---\nfeature: F031-04\nid: f031_04_01_02\nsql:\n- CREATE TABLE TABLE_F031_04_01_02 ( A INTEGER )\n- ALTER TABLE TABLE_F031_04_01_02 ADD COLUMN B INT\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-13.tests.yml",
    "content": "feature: F031-13\nid: f031_13_01_01\nsql:\n- CREATE TABLE TABLE_F031_13_01_01 ( A INTEGER )\n- 'DROP TABLE TABLE_F031_13_01_01 '\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-16.tests.yml",
    "content": "feature: F031-16\nid: f031_16_01_01\nsql:\n- CREATE TABLE TABLE_F031_16_01_01 ( A INTEGER )\n- CREATE VIEW VIEW_F031_16_01_01 AS SELECT A FROM TABLE_F031_16_01_01\n- 'DROP VIEW VIEW_F031_16_01_01 '\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F031-19.tests.yml",
    "content": "feature: F031-19\nid: f031_19_01_01\nsql:\n- CREATE TABLE TABLE_F031_19_01_01 ( A INT )\n- CREATE ROLE ROLE_F031_19_01_01\n- 'REVOKE SELECT ON TABLE_F031_19_01_01 FROM ROLE_F031_19_01_01 '\n---\nfeature: F031-19\nid: f031_19_01_02\nsql:\n- CREATE TABLE TABLE_F031_19_01_02 ( A INT )\n- CREATE ROLE ROLE_F031_19_01_02\n- 'REVOKE SELECT ON TABLE_F031_19_01_02 FROM ROLE_F031_19_01_02 , ROLE_F031_19_01_02 '\n---\nfeature: F031-19\nid: f031_19_01_03\nsql:\n- CREATE TABLE TABLE_F031_19_01_03 ( A INT )\n- CREATE ROLE ROLE_F031_19_01_03\n- 'REVOKE SELECT ON TABLE_F031_19_01_03 FROM ROLE_F031_19_01_03 , ROLE_F031_19_01_03\n  GRANTED BY ROLE_F031_19_01_03 '\n---\nfeature: F031-19\nid: f031_19_01_04\nsql:\n- CREATE TABLE TABLE_F031_19_01_04 ( A INT )\n- CREATE ROLE ROLE_F031_19_01_04\n- 'REVOKE SELECT ON TABLE_F031_19_01_04 FROM ROLE_F031_19_01_04 GRANTED BY ROLE_F031_19_01_04 '\n---\nfeature: F031-19\nid: f031_19_02_01\nsql:\n- CREATE TABLE TABLE_F031_19_02_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_01\n- REVOKE DELETE ON TABLE_F031_19_02_01 FROM ROLE_F031_19_02_01\n---\nfeature: F031-19\nid: f031_19_02_02\nsql:\n- CREATE TABLE TABLE_F031_19_02_02 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_02\n- REVOKE EXECUTE ON TABLE_F031_19_02_02 FROM ROLE_F031_19_02_02\n---\nfeature: F031-19\nid: f031_19_02_03\nsql:\n- CREATE TABLE TABLE_F031_19_02_03 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_03\n- REVOKE INSERT ( A ) ON TABLE_F031_19_02_03 FROM ROLE_F031_19_02_03\n---\nfeature: F031-19\nid: f031_19_02_04\nsql:\n- CREATE TABLE TABLE_F031_19_02_04 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_04\n- REVOKE INSERT ON TABLE_F031_19_02_04 FROM ROLE_F031_19_02_04\n---\nfeature: F031-19\nid: f031_19_02_05\nsql:\n- CREATE TABLE TABLE_F031_19_02_05 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_05\n- REVOKE REFERENCES ( A ) ON TABLE_F031_19_02_05 FROM ROLE_F031_19_02_05\n---\nfeature: F031-19\nid: f031_19_02_06\nsql:\n- CREATE TABLE TABLE_F031_19_02_06 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_06\n- REVOKE REFERENCES ON TABLE_F031_19_02_06 FROM ROLE_F031_19_02_06\n---\nfeature: F031-19\nid: f031_19_02_07\nsql:\n- CREATE TABLE TABLE_F031_19_02_07 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_07\n- REVOKE SELECT ( A ) ON TABLE_F031_19_02_07 FROM ROLE_F031_19_02_07\n---\nfeature: F031-19\nid: f031_19_02_08\nsql:\n- CREATE TABLE TABLE_F031_19_02_08 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_08\n- REVOKE SELECT ON TABLE_F031_19_02_08 FROM ROLE_F031_19_02_08\n---\nfeature: F031-19\nid: f031_19_02_09\nsql:\n- CREATE TABLE TABLE_F031_19_02_09 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_09\n- REVOKE TRIGGER ON TABLE_F031_19_02_09 FROM ROLE_F031_19_02_09\n---\nfeature: F031-19\nid: f031_19_02_10\nsql:\n- CREATE TABLE TABLE_F031_19_02_10 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_10\n- REVOKE UNDER ON TABLE_F031_19_02_10 FROM ROLE_F031_19_02_10\n---\nfeature: F031-19\nid: f031_19_02_11\nsql:\n- CREATE TABLE TABLE_F031_19_02_11 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_11\n- REVOKE UPDATE ( A ) ON TABLE_F031_19_02_11 FROM ROLE_F031_19_02_11\n---\nfeature: F031-19\nid: f031_19_02_12\nsql:\n- CREATE TABLE TABLE_F031_19_02_12 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_12\n- REVOKE UPDATE ON TABLE_F031_19_02_12 FROM ROLE_F031_19_02_12\n---\nfeature: F031-19\nid: f031_19_02_13\nsql:\n- CREATE TABLE TABLE_F031_19_02_13 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_02_13\n- REVOKE USAGE ON TABLE_F031_19_02_13 FROM ROLE_F031_19_02_13\n---\nfeature: F031-19\nid: f031_19_03_01\nsql:\n- CREATE TABLE TABLE_F031_19_03_01 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_03_01\n- REVOKE ALL PRIVILEGES ON TABLE TABLE_F031_19_03_01 FROM ROLE_F031_19_03_01\n---\nfeature: F031-19\nid: f031_19_03_02\nsql:\n- CREATE TABLE TABLE_F031_19_03_02 ( A INTEGER )\n- CREATE ROLE ROLE_F031_19_03_02\n- REVOKE ALL PRIVILEGES ON TABLE_F031_19_03_02 FROM ROLE_F031_19_03_02\n---\nfeature: F031-19\nid: f031_19_04_01\nsql:\n- CREATE DOMAIN DOMAIN1 AS INT\n- CREATE ROLE ROLE_F031_19_04_01\n- REVOKE ALL PRIVILEGES ON DOMAIN DOMAIN1 FROM ROLE_F031_19_04_01\n---\nfeature: F031-19\nid: f031_19_05_01\nsql:\n- CREATE COLLATION COLLATION1 FROM 'de_DE'\n- CREATE ROLE ROLE_F031_19_05_01\n- REVOKE ALL PRIVILEGES ON COLLATION COLLATION1 FROM ROLE_F031_19_05_01\n---\nfeature: F031-19\nid: f031_19_06_01\nsql:\n- CREATE CHARACTER SET CHARACTERSET1\n- CREATE ROLE ROLE_F031_19_06_01\n- REVOKE ALL PRIVILEGES ON CHARACTER SET CHARACTERSET1 FROM ROLE_F031_19_06_01\n---\nfeature: F031-19\nid: f031_19_07_01\nsql:\n- CREATE TRANSLATION TRANSLATION1\n- CREATE ROLE ROLE_F031_19_07_01\n- REVOKE ALL PRIVILEGES ON TRANSLATION TRANSLATION1 FROM ROLE_F031_19_07_01\n---\nfeature: F031-19\nid: f031_19_08_01\nsql:\n- CREATE TYPE TYPE1\n- CREATE ROLE ROLE_F031_19_08_01\n- REVOKE ALL PRIVILEGES ON TYPE TYPE1 FROM ROLE_F031_19_08_01\n---\nfeature: F031-19\nid: f031_19_09_01\nsql:\n- CREATE SEQUENCE SEQUENCE1\n- CREATE ROLE ROLE_F031_19_09_01\n- REVOKE ALL PRIVILEGES ON SEQUENCE SEQUENCE1 FROM ROLE_F031_19_09_01\n---\nfeature: F031-19\nid: f031_19_10_01\nsql:\n- CREATE ROLE ROLE_F031_19_10_01\n- REVOKE ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FOR BAZ FROM ROLE_F031_19_10_01\n---\nfeature: F031-19\nid: f031_19_10_02\nsql:\n- CREATE ROLE ROLE_F031_19_10_02\n- REVOKE ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FROM ROLE_F031_19_10_02\n---\nfeature: F031-19\nid: f031_19_10_03\nsql:\n- CREATE ROLE ROLE_F031_19_10_03\n- REVOKE ALL PRIVILEGES ON FUNCTION BAR FOR BAZ FROM ROLE_F031_19_10_03\n---\nfeature: F031-19\nid: f031_19_10_04\nsql:\n- CREATE ROLE ROLE_F031_19_10_04\n- REVOKE ALL PRIVILEGES ON FUNCTION BAR FROM ROLE_F031_19_10_04\n---\nfeature: F031-19\nid: f031_19_10_05\nsql:\n- CREATE ROLE ROLE_F031_19_10_05\n- REVOKE ALL PRIVILEGES ON INSTANCE METHOD BAR FOR BAZ FROM ROLE_F031_19_10_05\n---\nfeature: F031-19\nid: f031_19_10_06\nsql:\n- CREATE ROLE ROLE_F031_19_10_06\n- REVOKE ALL PRIVILEGES ON INSTANCE METHOD BAR FROM ROLE_F031_19_10_06\n---\nfeature: F031-19\nid: f031_19_10_07\nsql:\n- CREATE ROLE ROLE_F031_19_10_07\n- REVOKE ALL PRIVILEGES ON METHOD BAR FOR BAZ FROM ROLE_F031_19_10_07\n---\nfeature: F031-19\nid: f031_19_10_08\nsql:\n- CREATE ROLE ROLE_F031_19_10_08\n- REVOKE ALL PRIVILEGES ON METHOD BAR FROM ROLE_F031_19_10_08\n---\nfeature: F031-19\nid: f031_19_10_09\nsql:\n- CREATE ROLE ROLE_F031_19_10_09\n- REVOKE ALL PRIVILEGES ON PROCEDURE BAR FOR BAZ FROM ROLE_F031_19_10_09\n---\nfeature: F031-19\nid: f031_19_10_10\nsql:\n- CREATE ROLE ROLE_F031_19_10_10\n- REVOKE ALL PRIVILEGES ON PROCEDURE BAR FROM ROLE_F031_19_10_10\n---\nfeature: F031-19\nid: f031_19_10_11\nsql:\n- CREATE ROLE ROLE_F031_19_10_11\n- REVOKE ALL PRIVILEGES ON ROUTINE BAR FOR BAZ FROM ROLE_F031_19_10_11\n---\nfeature: F031-19\nid: f031_19_10_12\nsql:\n- CREATE ROLE ROLE_F031_19_10_12\n- REVOKE ALL PRIVILEGES ON ROUTINE BAR FROM ROLE_F031_19_10_12\n---\nfeature: F031-19\nid: f031_19_10_13\nsql:\n- CREATE ROLE ROLE_F031_19_10_13\n- REVOKE ALL PRIVILEGES ON SPECIFIC CONSTRUCTOR METHOD FOO FROM ROLE_F031_19_10_13\n---\nfeature: F031-19\nid: f031_19_10_14\nsql:\n- CREATE ROLE ROLE_F031_19_10_14\n- REVOKE ALL PRIVILEGES ON SPECIFIC FUNCTION FOO FROM ROLE_F031_19_10_14\n---\nfeature: F031-19\nid: f031_19_10_15\nsql:\n- CREATE ROLE ROLE_F031_19_10_15\n- REVOKE ALL PRIVILEGES ON SPECIFIC INSTANCE METHOD FOO FROM ROLE_F031_19_10_15\n---\nfeature: F031-19\nid: f031_19_10_16\nsql:\n- CREATE ROLE ROLE_F031_19_10_16\n- REVOKE ALL PRIVILEGES ON SPECIFIC METHOD FOO FROM ROLE_F031_19_10_16\n---\nfeature: F031-19\nid: f031_19_10_17\nsql:\n- CREATE ROLE ROLE_F031_19_10_17\n- REVOKE ALL PRIVILEGES ON SPECIFIC PROCEDURE FOO FROM ROLE_F031_19_10_17\n---\nfeature: F031-19\nid: f031_19_10_18\nsql:\n- CREATE ROLE ROLE_F031_19_10_18\n- REVOKE ALL PRIVILEGES ON SPECIFIC ROUTINE FOO FROM ROLE_F031_19_10_18\n---\nfeature: F031-19\nid: f031_19_10_19\nsql:\n- CREATE ROLE ROLE_F031_19_10_19\n- REVOKE ALL PRIVILEGES ON SPECIFIC STATIC METHOD FOO FROM ROLE_F031_19_10_19\n---\nfeature: F031-19\nid: f031_19_10_20\nsql:\n- CREATE ROLE ROLE_F031_19_10_20\n- REVOKE ALL PRIVILEGES ON STATIC METHOD BAR FOR BAZ FROM ROLE_F031_19_10_20\n---\nfeature: F031-19\nid: f031_19_10_21\nsql:\n- CREATE ROLE ROLE_F031_19_10_21\n- REVOKE ALL PRIVILEGES ON STATIC METHOD BAR FROM ROLE_F031_19_10_21\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-01.tests.yml",
    "content": "feature: F041-01\nid: f041_01_01_01\nsql:\n- CREATE TABLE TABLE_F041_01_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_01_01_012 ( A INTEGER )\n- SELECT TABLE_F041_01_01_011.A, TABLE_F041_01_01_012.A FROM TABLE_F041_01_01_011\n  JOIN TABLE_F041_01_01_012 ON TABLE_F041_01_01_011.A = TABLE_F041_01_01_012.A\n---\nfeature: F041-01\nid: f041_01_01_02\nsql:\n- CREATE TABLE TABLE_F041_01_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F041_01_01_022 ( A INTEGER )\n- SELECT TABLE_F041_01_01_021.A, TABLE_F041_01_01_022.A FROM TABLE_F041_01_01_021\n  JOIN TABLE_F041_01_01_022 USING ( A )\n---\nfeature: F041-01\nid: f041_01_01_03\nsql:\n- CREATE TABLE TABLE_F041_01_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F041_01_01_032 ( A INTEGER )\n- SELECT TABLE_F041_01_01_031.A, TABLE_F041_01_01_032.A FROM TABLE_F041_01_01_031\n  JOIN TABLE_F041_01_01_032 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-02.tests.yml",
    "content": "feature: F041-02\nid: f041_02_01_01\nsql:\n- CREATE TABLE TABLE_F041_02_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_02_01_012 ( A INTEGER )\n- SELECT TABLE_F041_02_01_011.A, TABLE_F041_02_01_012.A FROM TABLE_F041_02_01_011\n  INNER JOIN TABLE_F041_02_01_012 ON TABLE_F041_02_01_011.A = TABLE_F041_02_01_012.A\n---\nfeature: F041-02\nid: f041_02_01_02\nsql:\n- CREATE TABLE TABLE_F041_02_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F041_02_01_022 ( A INTEGER )\n- SELECT TABLE_F041_02_01_021.A, TABLE_F041_02_01_022.A FROM TABLE_F041_02_01_021\n  INNER JOIN TABLE_F041_02_01_022 USING ( A )\n---\nfeature: F041-02\nid: f041_02_01_03\nsql:\n- CREATE TABLE TABLE_F041_02_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F041_02_01_032 ( A INTEGER )\n- SELECT TABLE_F041_02_01_031.A, TABLE_F041_02_01_032.A FROM TABLE_F041_02_01_031\n  INNER JOIN TABLE_F041_02_01_032 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-03.tests.yml",
    "content": "feature: F041-03\nid: f041_03_01_01\nsql:\n- CREATE TABLE TABLE_F041_03_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_012 ( A INTEGER )\n- SELECT TABLE_F041_03_01_011.A, TABLE_F041_03_01_012.A FROM TABLE_F041_03_01_011\n  LEFT JOIN TABLE_F041_03_01_012 ON TABLE_F041_03_01_011.A = TABLE_F041_03_01_012.A\n---\nfeature: F041-03\nid: f041_03_01_02\nsql:\n- CREATE TABLE TABLE_F041_03_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_022 ( A INTEGER )\n- SELECT TABLE_F041_03_01_021.A, TABLE_F041_03_01_022.A FROM TABLE_F041_03_01_021\n  LEFT JOIN TABLE_F041_03_01_022 USING ( A )\n---\nfeature: F041-03\nid: f041_03_01_03\nsql:\n- CREATE TABLE TABLE_F041_03_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_032 ( A INTEGER )\n- SELECT TABLE_F041_03_01_031.A, TABLE_F041_03_01_032.A FROM TABLE_F041_03_01_031\n  LEFT JOIN TABLE_F041_03_01_032 USING ( A ) AS FOO\n---\nfeature: F041-03\nid: f041_03_01_04\nsql:\n- CREATE TABLE TABLE_F041_03_01_041 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_042 ( A INTEGER )\n- SELECT TABLE_F041_03_01_041.A, TABLE_F041_03_01_042.A FROM TABLE_F041_03_01_041\n  LEFT OUTER JOIN TABLE_F041_03_01_042 ON TABLE_F041_03_01_041.A = TABLE_F041_03_01_042.A\n---\nfeature: F041-03\nid: f041_03_01_05\nsql:\n- CREATE TABLE TABLE_F041_03_01_051 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_052 ( A INTEGER )\n- SELECT TABLE_F041_03_01_051.A, TABLE_F041_03_01_052.A FROM TABLE_F041_03_01_051\n  LEFT OUTER JOIN TABLE_F041_03_01_052 USING ( A )\n---\nfeature: F041-03\nid: f041_03_01_06\nsql:\n- CREATE TABLE TABLE_F041_03_01_061 ( A INTEGER )\n- CREATE TABLE TABLE_F041_03_01_062 ( A INTEGER )\n- SELECT TABLE_F041_03_01_061.A, TABLE_F041_03_01_062.A FROM TABLE_F041_03_01_061\n  LEFT OUTER JOIN TABLE_F041_03_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-04.tests.yml",
    "content": "feature: F041-04\nid: f041_04_01_01\nsql:\n- CREATE TABLE TABLE_F041_04_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_012 ( A INTEGER )\n- SELECT TABLE_F041_04_01_011.A, TABLE_F041_04_01_012.A FROM TABLE_F041_04_01_011\n  RIGHT JOIN TABLE_F041_04_01_012 ON TABLE_F041_04_01_011.A = TABLE_F041_04_01_012.A\n---\nfeature: F041-04\nid: f041_04_01_02\nsql:\n- CREATE TABLE TABLE_F041_04_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_022 ( A INTEGER )\n- SELECT TABLE_F041_04_01_021.A, TABLE_F041_04_01_022.A FROM TABLE_F041_04_01_021\n  RIGHT JOIN TABLE_F041_04_01_022 USING ( A )\n---\nfeature: F041-04\nid: f041_04_01_03\nsql:\n- CREATE TABLE TABLE_F041_04_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_032 ( A INTEGER )\n- SELECT TABLE_F041_04_01_031.A, TABLE_F041_04_01_032.A FROM TABLE_F041_04_01_031\n  RIGHT JOIN TABLE_F041_04_01_032 USING ( A ) AS FOO\n---\nfeature: F041-04\nid: f041_04_01_04\nsql:\n- CREATE TABLE TABLE_F041_04_01_041 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_042 ( A INTEGER )\n- SELECT TABLE_F041_04_01_041.A, TABLE_F041_04_01_042.A FROM TABLE_F041_04_01_041\n  RIGHT OUTER JOIN TABLE_F041_04_01_042 ON TABLE_F041_04_01_041.A = TABLE_F041_04_01_042.A\n---\nfeature: F041-04\nid: f041_04_01_05\nsql:\n- CREATE TABLE TABLE_F041_04_01_051 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_052 ( A INTEGER )\n- SELECT TABLE_F041_04_01_051.A, TABLE_F041_04_01_052.A FROM TABLE_F041_04_01_051\n  RIGHT OUTER JOIN TABLE_F041_04_01_052 USING ( A )\n---\nfeature: F041-04\nid: f041_04_01_06\nsql:\n- CREATE TABLE TABLE_F041_04_01_061 ( A INTEGER )\n- CREATE TABLE TABLE_F041_04_01_062 ( A INTEGER )\n- SELECT TABLE_F041_04_01_061.A, TABLE_F041_04_01_062.A FROM TABLE_F041_04_01_061\n  RIGHT OUTER JOIN TABLE_F041_04_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-05.tests.yml",
    "content": "feature: F041-05\nid: f041_05_01_01\nsql:\n- CREATE TABLE TABLE_F041_05_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_05_01_012 ( A INTEGER )\n- CREATE TABLE TABLE_F041_05_01_013 ( A INTEGER )\n- SELECT TABLE_F041_05_01_011.A FROM TABLE_F041_05_01_011 LEFT JOIN TABLE_F041_05_01_012\n  ON TABLE_F041_05_01_011.A = TABLE_F041_05_01_012.A LEFT JOIN TABLE_F041_05_01_013\n  ON TABLE_F041_05_01_012.A = TABLE_F041_05_01_013.A\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-07.tests.yml",
    "content": "feature: F041-07\nid: f041_07_01_01\nsql:\n- CREATE TABLE TABLE_F041_07_01_011 ( A INTEGER )\n- SELECT TABLE_F041_07_01_011.A FROM TABLE_F041_07_01_011 INNER JOIN TABLE_F041_07_01_011\n  AS TABLE_F041_07_01_012 ON TABLE_F041_07_01_011.A = TABLE_F041_07_01_012.A\n---\nfeature: F041-07\nid: f041_07_01_02\nsql:\n- CREATE TABLE TABLE_F041_07_01_021 ( A INTEGER )\n- SELECT TABLE_F041_07_01_021.A FROM TABLE_F041_07_01_021 INNER JOIN TABLE_F041_07_01_021\n  AS TABLE_F041_07_01_022 USING ( A )\n---\nfeature: F041-07\nid: f041_07_01_03\nsql:\n- CREATE TABLE TABLE_F041_07_01_031 ( A INTEGER )\n- SELECT TABLE_F041_07_01_031.A FROM TABLE_F041_07_01_031 INNER JOIN TABLE_F041_07_01_031\n  AS TABLE_F041_07_01_032 USING ( A ) AS FOO\n---\nfeature: F041-07\nid: f041_07_01_04\nsql:\n- CREATE TABLE TABLE_F041_07_01_041 ( A INTEGER )\n- SELECT TABLE_F041_07_01_041.A FROM TABLE_F041_07_01_041 JOIN TABLE_F041_07_01_041\n  AS TABLE_F041_07_01_042 ON TABLE_F041_07_01_041.A = TABLE_F041_07_01_042.A\n---\nfeature: F041-07\nid: f041_07_01_05\nsql:\n- CREATE TABLE TABLE_F041_07_01_051 ( A INTEGER )\n- SELECT TABLE_F041_07_01_051.A FROM TABLE_F041_07_01_051 JOIN TABLE_F041_07_01_051\n  AS TABLE_F041_07_01_052 USING ( A )\n---\nfeature: F041-07\nid: f041_07_01_06\nsql:\n- CREATE TABLE TABLE_F041_07_01_061 ( A INTEGER )\n- SELECT TABLE_F041_07_01_061.A FROM TABLE_F041_07_01_061 JOIN TABLE_F041_07_01_061\n  AS TABLE_F041_07_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F041-08.tests.yml",
    "content": "feature: F041-08\nid: f041_08_01_01\nsql:\n- CREATE TABLE TABLE_F041_08_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_012 ( B INTEGER )\n- SELECT TABLE_F041_08_01_011.A, TABLE_F041_08_01_012.B FROM TABLE_F041_08_01_011\n  JOIN TABLE_F041_08_01_012 ON TABLE_F041_08_01_011.A < TABLE_F041_08_01_012.B\n---\nfeature: F041-08\nid: f041_08_01_02\nsql:\n- CREATE TABLE TABLE_F041_08_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_022 ( B INTEGER )\n- SELECT TABLE_F041_08_01_021.A, TABLE_F041_08_01_022.B FROM TABLE_F041_08_01_021\n  JOIN TABLE_F041_08_01_022 ON TABLE_F041_08_01_021.A <= TABLE_F041_08_01_022.B\n---\nfeature: F041-08\nid: f041_08_01_03\nsql:\n- CREATE TABLE TABLE_F041_08_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_032 ( B INTEGER )\n- SELECT TABLE_F041_08_01_031.A, TABLE_F041_08_01_032.B FROM TABLE_F041_08_01_031\n  JOIN TABLE_F041_08_01_032 ON TABLE_F041_08_01_031.A <> TABLE_F041_08_01_032.B\n---\nfeature: F041-08\nid: f041_08_01_04\nsql:\n- CREATE TABLE TABLE_F041_08_01_041 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_042 ( B INTEGER )\n- SELECT TABLE_F041_08_01_041.A, TABLE_F041_08_01_042.B FROM TABLE_F041_08_01_041\n  JOIN TABLE_F041_08_01_042 ON TABLE_F041_08_01_041.A = TABLE_F041_08_01_042.B\n---\nfeature: F041-08\nid: f041_08_01_05\nsql:\n- CREATE TABLE TABLE_F041_08_01_051 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_052 ( B INTEGER )\n- SELECT TABLE_F041_08_01_051.A, TABLE_F041_08_01_052.B FROM TABLE_F041_08_01_051\n  JOIN TABLE_F041_08_01_052 ON TABLE_F041_08_01_051.A > TABLE_F041_08_01_052.B\n---\nfeature: F041-08\nid: f041_08_01_06\nsql:\n- CREATE TABLE TABLE_F041_08_01_061 ( A INTEGER )\n- CREATE TABLE TABLE_F041_08_01_062 ( B INTEGER )\n- SELECT TABLE_F041_08_01_061.A, TABLE_F041_08_01_062.B FROM TABLE_F041_08_01_061\n  JOIN TABLE_F041_08_01_062 ON TABLE_F041_08_01_061.A >= TABLE_F041_08_01_062.B\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-01.tests.yml",
    "content": "feature: F051-01\nid: f051_01_01_01\nsql: CREATE TABLE TABLE_F051_01_01_011 ( A DATE )\n---\nfeature: F051-01\nid: f051_01_02_01\nsql: SELECT DATE '2016-03-26'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-02.tests.yml",
    "content": "feature: F051-02\nid: f051_02_01_01\nsql: CREATE TABLE TABLE_F051_02_01_011 ( A TIME )\n---\nfeature: F051-02\nid: f051_02_02_01\nsql: SELECT TIME '01:02:03'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-03.tests.yml",
    "content": "feature: F051-03\nid: f051_03_01_01\nsql: CREATE TABLE TABLE_F051_03_01_011 ( A TIMESTAMP )\n---\nfeature: F051-03\nid: f051_03_02_01\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-04.tests.yml",
    "content": "feature: F051-04\nid: f051_04_01_01\nsql: SELECT DATE '2016-03-26' < DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_01_02\nsql: SELECT DATE '2016-03-26' <= DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_01_03\nsql: SELECT DATE '2016-03-26' <> DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_01_04\nsql: SELECT DATE '2016-03-26' = DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_01_05\nsql: SELECT DATE '2016-03-26' > DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_01_06\nsql: SELECT DATE '2016-03-26' >= DATE '2016-03-26'\n---\nfeature: F051-04\nid: f051_04_02_01\nsql: SELECT TIME '01:02:03' < TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_02_02\nsql: SELECT TIME '01:02:03' <= TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_02_03\nsql: SELECT TIME '01:02:03' <> TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_02_04\nsql: SELECT TIME '01:02:03' = TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_02_05\nsql: SELECT TIME '01:02:03' > TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_02_06\nsql: SELECT TIME '01:02:03' >= TIME '01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_01\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' < TIMESTAMP '2016-03-26 01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_02\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' <= TIMESTAMP '2016-03-26 01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_03\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' <> TIMESTAMP '2016-03-26 01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_04\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' = TIMESTAMP '2016-03-26 01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_05\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' > TIMESTAMP '2016-03-26 01:02:03'\n---\nfeature: F051-04\nid: f051_04_03_06\nsql: SELECT TIMESTAMP '2016-03-26 01:02:03' >= TIMESTAMP '2016-03-26 01:02:03'\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-05.tests.yml",
    "content": "feature: F051-05\nid: f051_05_01_01\nsql: SELECT CAST ( '2016-03-26' AS DATE )\n---\nfeature: F051-05\nid: f051_05_02_01\nsql: SELECT CAST ( '01:02:03' AS TIME )\n---\nfeature: F051-05\nid: f051_05_03_01\nsql: SELECT CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE )\n---\nfeature: F051-05\nid: f051_05_04_01\nsql: SELECT CAST ( CAST ( '2016-03-26' AS DATE ) AS VARCHAR )\n---\nfeature: F051-05\nid: f051_05_05_01\nsql: SELECT CAST ( CAST ( '01:02:03' AS TIME ) AS VARCHAR )\n---\nfeature: F051-05\nid: f051_05_06_01\nsql: SELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS\n  VARCHAR )\n---\nfeature: F051-05\nid: f051_05_07_01\nsql: SELECT CAST ( CAST ( '01:02:03' AS TIME ) AS TIME )\n---\nfeature: F051-05\nid: f051_05_07_02\nsql: SELECT CAST ( CAST ( '01:02:03' AS TIME ) AS TIMESTAMP )\n---\nfeature: F051-05\nid: f051_05_07_03\nsql: SELECT CAST ( CAST ( '01:02:03' AS TIME ) AS VARCHAR )\n---\nfeature: F051-05\nid: f051_05_08_01\nsql: SELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS\n  DATE )\n---\nfeature: F051-05\nid: f051_05_08_02\nsql: SELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS\n  TIME )\n---\nfeature: F051-05\nid: f051_05_08_03\nsql: SELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS\n  TIMESTAMP )\n---\nfeature: F051-05\nid: f051_05_08_04\nsql: SELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS\n  VARCHAR )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-06.tests.yml",
    "content": "feature: F051-06\nid: f051_06_01_01\nsql: SELECT CURRENT_DATE\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-07.tests.yml",
    "content": "feature: F051-07\nid: f051_07_01_01\nsql: SELECT CURRENT_TIME\n---\nfeature: F051-07\nid: f051_07_01_02\nsql: SELECT CURRENT_TIME ( 0 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F051-08.tests.yml",
    "content": "feature: F051-08\nid: f051_08_01_01\nsql: SELECT CURRENT_TIMESTAMP\n---\nfeature: F051-08\nid: f051_08_01_02\nsql: SELECT CURRENT_TIMESTAMP ( 0 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F081.tests.yml",
    "content": "feature: F081\nid: f081_01_01\nsql:\n- CREATE TABLE TABLE_F081_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F081_01_012 ( A INTEGER )\n- CREATE VIEW VIEW_F081_01_01 AS SELECT A FROM TABLE_F081_01_011 EXCEPT SELECT A FROM\n  TABLE_F081_01_012\n---\nfeature: F081\nid: f081_01_02\nsql:\n- CREATE TABLE TABLE_F081_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F081_01_022 ( A INTEGER )\n- CREATE VIEW VIEW_F081_01_02 AS SELECT A FROM TABLE_F081_01_021 UNION ALL SELECT\n  A FROM TABLE_F081_01_022\n---\nfeature: F081\nid: f081_01_03\nsql:\n- CREATE TABLE TABLE_F081_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F081_01_032 ( A INTEGER )\n- CREATE VIEW VIEW_F081_01_03 AS SELECT A FROM TABLE_F081_01_031 UNION SELECT A FROM\n  TABLE_F081_01_032\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F131-01.tests.yml",
    "content": "feature: F131-01\nid: f131_01_01_01\nsql:\n- CREATE TABLE TABLE_F131_01_01_01 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_01 AS SELECT A FROM TABLE_F131_01_01_01 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_01\n---\nfeature: F131-01\nid: f131_01_01_02\nsql:\n- CREATE TABLE TABLE_F131_01_01_02 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_02 AS SELECT A FROM TABLE_F131_01_01_02 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_02 GROUP BY A\n---\nfeature: F131-01\nid: f131_01_01_03\nsql:\n- CREATE TABLE TABLE_F131_01_01_03 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_03 AS SELECT A FROM TABLE_F131_01_01_03 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_03 GROUP BY A HAVING A = 2\n---\nfeature: F131-01\nid: f131_01_01_04\nsql:\n- CREATE TABLE TABLE_F131_01_01_04 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_04 AS SELECT A FROM TABLE_F131_01_01_04 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_04 WHERE A = 1\n---\nfeature: F131-01\nid: f131_01_01_05\nsql:\n- CREATE TABLE TABLE_F131_01_01_05 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_05 AS SELECT A FROM TABLE_F131_01_01_05 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_05 WHERE A = 1 GROUP BY A\n---\nfeature: F131-01\nid: f131_01_01_06\nsql:\n- CREATE TABLE TABLE_F131_01_01_06 ( A INTEGER )\n- CREATE VIEW VIEW_F131_01_01_06 AS SELECT A FROM TABLE_F131_01_01_06 GROUP BY A\n- SELECT A FROM VIEW_F131_01_01_06 WHERE A = 1 GROUP BY A HAVING A = 2\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F131-02.tests.yml",
    "content": "feature: F131-02\nid: f131_02_01_01\nsql:\n- CREATE TABLE TABLE_F131_02_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F131_02_01_012 ( A INTEGER )\n- CREATE VIEW VIEW_F131_02_01_01 AS SELECT A FROM TABLE_F131_02_01_011 GROUP BY A\n- SELECT A FROM VIEW_F131_02_01_01 JOIN TABLE_F131_02_01_012 USING ( A )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F131-03.tests.yml",
    "content": "feature: F131-03\nid: f131_03_01_01\nsql:\n- CREATE TABLE TABLE_F131_03_01_011 ( A INTEGER, B INTEGER )\n- CREATE VIEW VIEW_F131_03_01_01 AS SELECT A, MIN ( B ) AS C FROM TABLE_F131_03_01_011\n  GROUP BY A\n- SELECT SUM ( C ) FROM VIEW_F131_03_01_01\n---\nfeature: F131-03\nid: f131_03_01_02\nsql:\n- CREATE TABLE TABLE_F131_03_01_021 ( A INTEGER, B INTEGER )\n- CREATE VIEW VIEW_F131_03_01_02 AS SELECT A, MIN ( B ) AS C FROM TABLE_F131_03_01_021\n  GROUP BY A\n- SELECT SUM ( C ) FROM VIEW_F131_03_01_02 GROUP BY A\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F131-04.tests.yml",
    "content": "feature: F131-04\nid: f131_04_01_01\nsql:\n- CREATE TABLE TABLE_F131_04_01_011 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_012 ( B INTEGER )\n- SELECT B < ( SELECT MAX ( A ) FROM TABLE_F131_04_01_011 GROUP BY A ) FROM TABLE_F131_04_01_012\n---\nfeature: F131-04\nid: f131_04_01_02\nsql:\n- CREATE TABLE TABLE_F131_04_01_021 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_022 ( B INTEGER )\n- SELECT B < ( SELECT MAX ( A ) FROM TABLE_F131_04_01_021 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_022\n---\nfeature: F131-04\nid: f131_04_01_03\nsql:\n- CREATE TABLE TABLE_F131_04_01_031 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_032 ( B INTEGER )\n- SELECT B <= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_031 GROUP BY A ) FROM TABLE_F131_04_01_032\n---\nfeature: F131-04\nid: f131_04_01_04\nsql:\n- CREATE TABLE TABLE_F131_04_01_041 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_042 ( B INTEGER )\n- SELECT B <= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_041 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_042\n---\nfeature: F131-04\nid: f131_04_01_05\nsql:\n- CREATE TABLE TABLE_F131_04_01_051 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_052 ( B INTEGER )\n- SELECT B <> ( SELECT MAX ( A ) FROM TABLE_F131_04_01_051 GROUP BY A ) FROM TABLE_F131_04_01_052\n---\nfeature: F131-04\nid: f131_04_01_06\nsql:\n- CREATE TABLE TABLE_F131_04_01_061 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_062 ( B INTEGER )\n- SELECT B <> ( SELECT MAX ( A ) FROM TABLE_F131_04_01_061 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_062\n---\nfeature: F131-04\nid: f131_04_01_07\nsql:\n- CREATE TABLE TABLE_F131_04_01_071 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_072 ( B INTEGER )\n- SELECT B = ( SELECT MAX ( A ) FROM TABLE_F131_04_01_071 GROUP BY A ) FROM TABLE_F131_04_01_072\n---\nfeature: F131-04\nid: f131_04_01_08\nsql:\n- CREATE TABLE TABLE_F131_04_01_081 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_082 ( B INTEGER )\n- SELECT B = ( SELECT MAX ( A ) FROM TABLE_F131_04_01_081 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_082\n---\nfeature: F131-04\nid: f131_04_01_09\nsql:\n- CREATE TABLE TABLE_F131_04_01_091 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_092 ( B INTEGER )\n- SELECT B > ( SELECT MAX ( A ) FROM TABLE_F131_04_01_091 GROUP BY A ) FROM TABLE_F131_04_01_092\n---\nfeature: F131-04\nid: f131_04_01_10\nsql:\n- CREATE TABLE TABLE_F131_04_01_101 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_102 ( B INTEGER )\n- SELECT B > ( SELECT MAX ( A ) FROM TABLE_F131_04_01_101 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_102\n---\nfeature: F131-04\nid: f131_04_01_11\nsql:\n- CREATE TABLE TABLE_F131_04_01_111 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_112 ( B INTEGER )\n- SELECT B >= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_111 GROUP BY A ) FROM TABLE_F131_04_01_112\n---\nfeature: F131-04\nid: f131_04_01_12\nsql:\n- CREATE TABLE TABLE_F131_04_01_121 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_01_122 ( B INTEGER )\n- SELECT B >= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_121 GROUP BY A HAVING A = 5\n  ) FROM TABLE_F131_04_01_122\n---\nfeature: F131-04\nid: f131_04_02_01\nsql:\n- CREATE TABLE TABLE_F131_04_02_011 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_012 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_01 AS SELECT A FROM TABLE_F131_04_02_011 GROUP BY A\n- SELECT B < ( SELECT MAX ( A ) FROM VIEW_F131_04_02_01 ) FROM TABLE_F131_04_02_012\n---\nfeature: F131-04\nid: f131_04_02_02\nsql:\n- CREATE TABLE TABLE_F131_04_02_021 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_022 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_02 AS SELECT A FROM TABLE_F131_04_02_021 GROUP BY A\n- SELECT B <= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_02 ) FROM TABLE_F131_04_02_022\n---\nfeature: F131-04\nid: f131_04_02_03\nsql:\n- CREATE TABLE TABLE_F131_04_02_031 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_032 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_03 AS SELECT A FROM TABLE_F131_04_02_031 GROUP BY A\n- SELECT B <> ( SELECT MAX ( A ) FROM VIEW_F131_04_02_03 ) FROM TABLE_F131_04_02_032\n---\nfeature: F131-04\nid: f131_04_02_04\nsql:\n- CREATE TABLE TABLE_F131_04_02_041 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_042 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_04 AS SELECT A FROM TABLE_F131_04_02_041 GROUP BY A\n- SELECT B = ( SELECT MAX ( A ) FROM VIEW_F131_04_02_04 ) FROM TABLE_F131_04_02_042\n---\nfeature: F131-04\nid: f131_04_02_05\nsql:\n- CREATE TABLE TABLE_F131_04_02_051 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_052 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_05 AS SELECT A FROM TABLE_F131_04_02_051 GROUP BY A\n- SELECT B > ( SELECT MAX ( A ) FROM VIEW_F131_04_02_05 ) FROM TABLE_F131_04_02_052\n---\nfeature: F131-04\nid: f131_04_02_06\nsql:\n- CREATE TABLE TABLE_F131_04_02_061 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_062 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_06 AS SELECT A FROM TABLE_F131_04_02_061 GROUP BY A\n- SELECT B >= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_06 ) FROM TABLE_F131_04_02_062\n---\nfeature: F131-04\nid: f131_04_02_07\nsql:\n- CREATE TABLE TABLE_F131_04_02_071 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_072 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_07 AS SELECT A FROM TABLE_F131_04_02_071 GROUP BY A\n  HAVING A = 5\n- SELECT B < ( SELECT MAX ( A ) FROM VIEW_F131_04_02_07 ) FROM TABLE_F131_04_02_072\n---\nfeature: F131-04\nid: f131_04_02_08\nsql:\n- CREATE TABLE TABLE_F131_04_02_081 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_082 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_08 AS SELECT A FROM TABLE_F131_04_02_081 GROUP BY A\n  HAVING A = 5\n- SELECT B <= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_08 ) FROM TABLE_F131_04_02_082\n---\nfeature: F131-04\nid: f131_04_02_09\nsql:\n- CREATE TABLE TABLE_F131_04_02_091 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_092 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_09 AS SELECT A FROM TABLE_F131_04_02_091 GROUP BY A\n  HAVING A = 5\n- SELECT B <> ( SELECT MAX ( A ) FROM VIEW_F131_04_02_09 ) FROM TABLE_F131_04_02_092\n---\nfeature: F131-04\nid: f131_04_02_10\nsql:\n- CREATE TABLE TABLE_F131_04_02_101 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_102 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_10 AS SELECT A FROM TABLE_F131_04_02_101 GROUP BY A\n  HAVING A = 5\n- SELECT B = ( SELECT MAX ( A ) FROM VIEW_F131_04_02_10 ) FROM TABLE_F131_04_02_102\n---\nfeature: F131-04\nid: f131_04_02_11\nsql:\n- CREATE TABLE TABLE_F131_04_02_111 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_112 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_11 AS SELECT A FROM TABLE_F131_04_02_111 GROUP BY A\n  HAVING A = 5\n- SELECT B > ( SELECT MAX ( A ) FROM VIEW_F131_04_02_11 ) FROM TABLE_F131_04_02_112\n---\nfeature: F131-04\nid: f131_04_02_12\nsql:\n- CREATE TABLE TABLE_F131_04_02_121 ( A INTEGER )\n- CREATE TABLE TABLE_F131_04_02_122 ( B INTEGER )\n- CREATE VIEW VIEW_F131_04_02_12 AS SELECT A FROM TABLE_F131_04_02_121 GROUP BY A\n  HAVING A = 5\n- SELECT B >= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_12 ) FROM TABLE_F131_04_02_122\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F221.tests.yml",
    "content": "feature: F221\nid: f221_01_01\nsql:\n- CREATE TABLE TABLE_F221_01_011 ( A INTEGER DEFAULT 123 )\n- INSERT INTO TABLE_F221_01_011 ( A ) VALUES ( DEFAULT )\n---\nfeature: F221\nid: f221_02_01\nsql:\n- CREATE TABLE TABLE_F221_02_011 ( A INTEGER DEFAULT 123 )\n- UPDATE TABLE_F221_02_011 SET A = DEFAULT\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F261-01.tests.yml",
    "content": "feature: F261-01\nid: f261_01_01_01\nsql: SELECT CASE 0 WHEN 2 , 2 THEN 1 ELSE 1 END\n---\nfeature: F261-01\nid: f261_01_01_02\nsql: SELECT CASE 0 WHEN 2 , 2 THEN 1 ELSE NULL END\n---\nfeature: F261-01\nid: f261_01_01_03\nsql: SELECT CASE 0 WHEN 2 , 2 THEN 1 END\n---\nfeature: F261-01\nid: f261_01_01_04\nsql: SELECT CASE 0 WHEN 2 , 2 THEN NULL ELSE 1 END\n---\nfeature: F261-01\nid: f261_01_01_05\nsql: SELECT CASE 0 WHEN 2 , 2 THEN NULL ELSE NULL END\n---\nfeature: F261-01\nid: f261_01_01_06\nsql: SELECT CASE 0 WHEN 2 , 2 THEN NULL END\n---\nfeature: F261-01\nid: f261_01_01_07\nsql: SELECT CASE 0 WHEN 2 THEN 1 ELSE 1 END\n---\nfeature: F261-01\nid: f261_01_01_08\nsql: SELECT CASE 0 WHEN 2 THEN 1 ELSE NULL END\n---\nfeature: F261-01\nid: f261_01_01_09\nsql: SELECT CASE 0 WHEN 2 THEN 1 END\n---\nfeature: F261-01\nid: f261_01_01_10\nsql: SELECT CASE 0 WHEN 2 THEN NULL ELSE 1 END\n---\nfeature: F261-01\nid: f261_01_01_11\nsql: SELECT CASE 0 WHEN 2 THEN NULL ELSE NULL END\n---\nfeature: F261-01\nid: f261_01_01_12\nsql: SELECT CASE 0 WHEN 2 THEN NULL END\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F261-02.tests.yml",
    "content": "feature: F261-02\nid: f261_02_01_01\nsql: SELECT CASE WHEN 0 = 1 THEN 1 ELSE 1 END\n---\nfeature: F261-02\nid: f261_02_01_02\nsql: SELECT CASE WHEN 0 = 1 THEN 1 ELSE NULL END\n---\nfeature: F261-02\nid: f261_02_01_03\nsql: SELECT CASE WHEN 0 = 1 THEN 1 END\n---\nfeature: F261-02\nid: f261_02_01_04\nsql: SELECT CASE WHEN 0 = 1 THEN NULL ELSE 1 END\n---\nfeature: F261-02\nid: f261_02_01_05\nsql: SELECT CASE WHEN 0 = 1 THEN NULL ELSE NULL END\n---\nfeature: F261-02\nid: f261_02_01_06\nsql: SELECT CASE WHEN 0 = 1 THEN NULL END\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F261-03.tests.yml",
    "content": "feature: F261-03\nid: f261_03_01_01\nsql: SELECT NULLIF ( 1 , 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F261-04.tests.yml",
    "content": "feature: F261-04\nid: f261_04_01_01\nsql: SELECT COALESCE ( 1 , 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F311-01.tests.yml",
    "content": "feature: F311-01\nid: f311_01_01_01\nsql: CREATE SCHEMA TABLE_F311_01_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F311-02.tests.yml",
    "content": "feature: F311-02\nid: f311_02_01_01\nsql: CREATE SCHEMA TABLE_F311_02_01_01 CREATE TABLE TABLE_F311_02_01_012 ( A INT )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F311-03.tests.yml",
    "content": "feature: F311-03\nid: f311_03_01_01\nsql:\n- CREATE TABLE TABLE_F311_03_01_01 ( A INTEGER )\n- CREATE SCHEMA TABLE_F311_03_01_012 CREATE VIEW TABLE_F311_03_01_013 AS SELECT A\n  FROM TABLE_F311_03_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F311-04.tests.yml",
    "content": "feature: F311-04\nid: f311_04_01_01\nsql:\n- CREATE TABLE TABLE_F311_04_01_01 ( A INTEGER )\n- CREATE SCHEMA TABLE_F311_04_01_012 CREATE VIEW TABLE_F311_04_01_013 AS SELECT A\n  FROM TABLE_F311_04_01_01 WITH CASCADED CHECK OPTION\n---\nfeature: F311-04\nid: f311_04_01_02\nsql:\n- CREATE TABLE TABLE_F311_04_01_02 ( A INTEGER )\n- CREATE SCHEMA TABLE_F311_04_01_022 CREATE VIEW TABLE_F311_04_01_023 AS SELECT A\n  FROM TABLE_F311_04_01_02 WITH CHECK OPTION\n---\nfeature: F311-04\nid: f311_04_01_03\nsql:\n- CREATE TABLE TABLE_F311_04_01_03 ( A INTEGER )\n- CREATE SCHEMA TABLE_F311_04_01_032 CREATE VIEW TABLE_F311_04_01_033 AS SELECT A\n  FROM TABLE_F311_04_01_03 WITH LOCAL CHECK OPTION\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F311-05.tests.yml",
    "content": "feature: F311-05\nid: f311_05_01_01\nsql:\n- CREATE TABLE TABLE_F311_05_01_01 ( A INTEGER )\n- CREATE ROLE ROLE_F311_05_01_01\n- CREATE SCHEMA TABLE_F311_05_01_012 GRANT SELECT ON TABLE TABLE_F311_05_01_01 TO\n  ROLE_F311_05_01_01\n---\nfeature: F311-05\nid: f311_05_01_02\nsql:\n- CREATE TABLE TABLE_F311_05_01_02 ( A INTEGER )\n- CREATE ROLE ROLE_F311_05_01_02\n- CREATE SCHEMA TABLE_F311_05_01_022 GRANT SELECT ON TABLE_F311_05_01_02 TO ROLE_F311_05_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F471.tests.yml",
    "content": "feature: F471\nid: f471_01_01\nsql:\n- CREATE TABLE TABLE_F471_01_01 ( A INTEGER )\n- INSERT INTO TABLE_F471_01_01 ( A ) VALUES ( 1 )\n- SELECT ( SELECT A FROM TABLE_F471_01_01 ) FROM TABLE_F471_01_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/F/F481.tests.yml",
    "content": "feature: F481\nid: f481_01_01\nsql:\n- CREATE TABLE TABLE_F481_01_01 ( A INTEGER )\n- SELECT A + A IS NOT NULL FROM TABLE_F481_01_01\n---\nfeature: F481\nid: f481_01_02\nsql:\n- CREATE TABLE TABLE_F481_01_02 ( A INTEGER )\n- SELECT A + A IS NULL FROM TABLE_F481_01_02\n"
  },
  {
    "path": "crates/sqltest/standards/2016/S/S011.tests.yml",
    "content": "feature: S011\nid: s011_01_01\nsql: CREATE TYPE TABLE_S011_01_01\n---\nfeature: S011\nid: s011_01_02\nsql: CREATE TYPE TABLE_S011_01_02 AS INT\n---\nfeature: S011\nid: s011_02_01\nsql:\n- CREATE TYPE TABLE_S011_02_01 AS INT\n- DROP TYPE TABLE_S011_02_01\n"
  },
  {
    "path": "crates/sqltest/standards/2016/T/T321.tests.yml",
    "content": ""
  },
  {
    "path": "crates/sqltest/standards/2016/T/T631.tests.yml",
    "content": "feature: T631\nid: t631_01_01\nsql:\n- CREATE TABLE TABLE_T631_01_01 ( A INTEGER )\n- INSERT INTO TABLE_T631_01_01 ( A ) VALUES ( 1 )\n- SELECT A FROM TABLE_T631_01_01 WHERE A IN ( 1 )\n"
  },
  {
    "path": "crates/sqltest/standards/2016/features.yml",
    "content": "# These have been derived from:\n# Annex F: SQL feature taxonomy\n# Page 1635\nmandatory:\n  E011: Numeric data types\n  E011-01: INTEGER and SMALLINT data types (including all spellings)\n  E011-02: REAL, DOUBLE PRECISION, and FLOAT data types\n  E011-03: DECIMAL and NUMERIC data types\n  E011-04: Arithmetic operators\n  E011-05: Numeric comparison\n  E011-06: Implicit casting among the numeric data types\n  E021: Character string types\n  E021-01: CHARACTER data type (including all its spellings)\n  E021-02: CHARACTER VARYING data type (including all its spellings)\n  E021-03: Character literals\n  E021-04: CHARACTER_LENGTH function\n  E021-05: OCTET_LENGTH function\n  E021-06: SUBSTRING function\n  E021-07: Character concatenation\n  E021-08: UPPER and LOWER functions\n  E021-09: TRIM function\n  E021-10: Implicit casting among the fixed-length and variable-length character string types\n  E021-11: POSITION function\n  E021-12: Character comparison\n  E031: Identifiers\n  E031-01: Delimited identifiers\n  E031-02: Lower case identifiers\n  E031-03: Trailing underscore\n  E051: Basic query specification\n  E051-01: SELECT DISTINCT\n  E051-02: GROUP BY clause\n  E051-04: GROUP BY can contain columns not in <select list>\n  E051-05: Select list items can be renamed\n  E051-06: HAVING clause\n  E051-07: Qualified * in select list\n  E051-08: Correlation names in the FROM clause\n  E051-09: Rename columns in the FROM clause\n  E061: Basic predicates and search conditions\n  E061-01: Comparison predicate\n  E061-02: BETWEEN predicate\n  E061-03: IN predicate with list of values\n  E061-04: LIKE predicate\n  E061-05: \"LIKE predicate: ESCAPE clause\"\n  E061-06: NULL predicate\n  E061-07: Quantified comparison predicate\n  E061-08: EXISTS predicate\n  E061-09: Subqueries in comparison predicate\n  E061-11: Subqueries in IN predicate\n  E061-12: Subqueries in quantified comparison predicate\n  E061-13: Correlated subqueries\n  E061-14: Search condition\n  E071: Basic query expressions\n  E071-01: UNION DISTINCT table operator\n  E071-02: UNION ALL table operator\n  E071-03: EXCEPT DISTINCT table operator\n  E071-05: Columns combined via table operators need not have exactly the same data type\n  E071-06: Table operators in subqueries\n  E081: Basic Privileges\n  E081-01: SELECT privilege at the table level\n  E081-02: DELETE privilege\n  E081-03: INSERT privilege at the table level\n  E081-04: UPDATE privilege at the table level\n  E081-05: UPDATE privilege at the column level\n  E081-06: REFERENCES privilege at the table level\n  E081-07: REFERENCES privilege at the column level\n  E081-08: WITH GRANT OPTION\n  E081-09: USAGE privilege\n  E081-10: EXECUTE privilege\n  E091: Set functions\n  E091-01: AVG\n  E091-02: COUNT\n  E091-03: MAX\n  E091-04: MIN\n  E091-05: SUM\n  E091-06: ALL quantifier\n  E091-07: DISTINCT quantifier\n  E101: Basic data manipulation\n  E101-01: INSERT statement\n  E101-03: Searched UPDATE statement\n  E101-04: Searched DELETE statement\n  E111: Single row SELECT statement\n  E121: Basic cursor support\n  E121-01: DECLARE CURSOR\n  E121-02: ORDER BY columns need not be in select list\n  E121-03: Value expressions in ORDER BY clause\n  E121-04: OPEN statement\n  E121-06: Positioned UPDATE statement\n  E121-07: Positioned DELETE statement\n  E121-08: CLOSE statement\n  E121-10: \"FETCH statement: implicit NEXT\"\n  E121-17: WITH HOLD cursors\n  E131: Null value support (nulls in lieu of values)\n  E141: Basic integrity constraints\n  E141-01: NOT NULL constraints\n  E141-02: UNIQUE constraints of NOT NULL columns\n  E141-03: PRIMARY KEY constraints\n  E141-04: Basic FOREIGN KEY constraint with the NO ACTION default for both referential delete action and referential update action\n  E141-06: CHECK constraints\n  E141-07: Column defaults\n  E141-08: NOT NULL inferred on PRIMARY KEY\n  E141-10: Names in a foreign key can be specified in any order\n  E151: Transaction support\n  E151-01: COMMIT statement\n  E151-02: ROLLBACK statement\n  E152: Basic SET TRANSACTION statement\n  E152-01: \"SET TRANSACTION statement: ISOLATION LEVEL SERIALIZABLE clause\"\n  E152-02: \"SET TRANSACTION statement: READ ONLY and READ WRITE clauses\"\n  E153: Updatable queries with subqueries\n  E161: SQL comments using leading double minus\n  E171: SQLSTATE support\n  E182: Host language binding\n  F031: Basic schema manipulation\n  F031-01: CREATE TABLE statement to create persistent base tables\n  F031-02: CREATE VIEW statement\n  F031-03: GRANT statement\n  F031-04: \"ALTER TABLE statement: ADD COLUMN clause\"\n  F031-13: \"DROP TABLE statement: RESTRICT clause\"\n  F031-16: \"DROP VIEW statement: RESTRICT clause\"\n  F031-19: \"REVOKE statement: RESTRICT clause\"\n  F041: Basic joined table\n  F041-01: Inner join (but not necessarily the INNER keyword)\n  F041-02: INNER keyword\n  F041-03: LEFT OUTER JOIN\n  F041-04: RIGHT OUTER JOIN\n  F041-05: Outer joins can be nested\n  F041-07: The inner table in a left or right outer join can also be used in an inner join\n  F041-08: All comparison operators are supported (rather than just =)\n  F051: Basic date and time\n  F051-01: DATE data type (including support of DATE literal)\n  F051-02: TIME data type (including support of TIME literal) with fractional seconds precision of at least 0\n  F051-03: TIMESTAMP data type (including support of TIMESTAMP literal) with fractional seconds precision of at least 0 and 6\n  F051-04: Comparison predicate on DATE, TIME, and TIMESTAMP data types\n  F051-05: Explicit CAST between datetime types and character string types\n  F051-06: CURRENT_DATE\n  F051-07: LOCALTIME\n  F051-08: LOCALTIMESTAMP\n  F081: UNION and EXCEPT in views\n  F131: Grouped operations\n  F131-01: WHERE, GROUP BY, and HAVING clauses supported in queries with grouped views\n  F131-02: Multiple tables supported in queries with grouped views\n  F131-03: Set functions supported in queries with grouped views\n  F131-04: Subqueries with GROUP BY and HAVING clauses and grouped views\n  F131-05: Single row SELECT with GROUP BY and HAVING clauses and grouped views\n  F181: Multiple module support \n  F201: CAST function \n  F221: Explicit defaults\n  F261: CASE expression\n  F261-01: Simple CASE\n  F261-02: Searched CASE\n  F261-03: NULLIF\n  F261-04: COALESCE\n  F311: Schema definition statement\n  F311-01: CREATE SCHEMA\n  F311-02: CREATE TABLE for persistent base tables\n  F311-03: CREATE VIEW\n  F311-04: \"CREATE VIEW: WITH CHECK OPTION\"\n  F311-05: GRANT statement\n  F471: Scalar subquery values\n  F481: Expanded NULL predicate\n  F812: Basic flagging\n  S011: Distinct data types\n  T321: Basic SQL-invoked routines\n  T321-01: User-defined functions with no overloading\n  T321-02: User-defined stored procedures with no overloading\n  T321-03: Function invocation\n  T321-04: CALL statement\n  T321-05: RETURN statement\n  T631: IN predicate with one list element\noptional:\n  B0111: Embedded Ada\n  B0121: Embedded C\n  B0131: Embedded COBOL\n  B0141: Embedded Fortran\n  B0151: Embedded MUMPS\n  B0161: Embedded Pascal\n  B0171: Embedded PL/I\n  B021: Direct SQL\n  B031: Basic dynamic SQL\n  B032: Extended dynamic SQL\n  B032-01: <describe input statement>\n  B033: Untyped SQL-invoked function arguments\n  B034: Dynamic specification of cursor attributes\n  B035: Non-extended descriptor names\n  B041: Extensions to embedded SQL exception declarations\n  B051: Enhanced execution rights\n  B1111: Module language Ada\n  B1121: Module language C\n  B1131: Module language COBOL\n  B1141: Module language Fortran\n  B1151: Module language MUMPS\n  B1161: Module language Pascal\n  B1171: Module language PL/I\n  B121: Routine language Ada\n  B122: Routine language C\n  B123: Routine language COBOL\n  B124: Routine language Fortran\n  B125: Routine language MUMPS\n  B126: Routine language Pascal\n  B127: Routine language PL/I\n  B128: Routine language SQL\n  B200: Polymorphic table functions\n  B201: More than one PTF generic table parameter\n  B202: PTF Copartitioning\n  B203: More than one copartition specification\n  B204: PRUNE WHEN EMPTY\n  B205: Pass-through columns\n  B206: PTF descriptor parameters\n  B207: Cross products of partitionings\n  B208: PTF component procedure interface\n  B209: PTF extended names\n  B211: \"Module language Ada: VARCHAR and NUMERIC support\"\n  B221: \"Routine language Ada: VARCHAR and NUMERIC support\"\n  F032: CASCADE drop behavior\n  F033: \"ALTER TABLE statement: DROP COLUMN clause\"\n  F034: Extended REVOKE statement\n  F034-01: REVOKE statement performed by other than the owner of a schema object\n  F034-02: \"REVOKE statement: GRANT OPTION FOR clause\"\n  F034-03: REVOKE statement to revoke a privilege that the grantee has WITH GRANT OPTION\n  F052: Intervals and datetime arithmetic\n  F053: OVERLAPS predicate\n  F054: TIMESTAMP in DATE type precedence list\n  F111: Isolation levels other than SERIALIZABLE\n  F111-01: READ UNCOMMITTED isolation level\n  F111-02: READ COMMITTED isolation level\n  F111-03: REPEATABLE READ isolation level\n  F121: Basic diagnostics management\n  F121-01: GET DIAGNOSTICS statement\n  F121-02: \"SET TRANSACTION statement: DIAGNOSTICS SIZE clause\"\n  F122: Enhanced diagnostics management\n  F123: All diagnostics\n  F171: Multiple schemas per user\n  F191: Referential delete actions\n  F200: TRUNCATE TABLE statement\n  F202: \"TRUNCATE TABLE: identity column restart option\"\n  F222: \"INSERT statement: DEFAULT VALUES clause\"\n  F251: Domain support\n  F262: Extended CASE expression\n  F263: Comma-separated predicates in simple CASE expression\n  F271: Compound character literals\n  F281: LIKE enhancements\n  F291: UNIQUE predicate\n  F301: CORRESPONDING in query expressions\n  F302: INTERSECT table operator\n  F302-01: INTERSECT DISTINCT table operator\n  F302-02: INTERSECT ALL table operator\n  F304: EXCEPT ALL table operator\n  F312: MERGE statement\n  F313: Enhanced MERGE statement\n  F314: MERGE statement with DELETE branch\n  F321: User authorization\n  F361: Subprogram support\n  F381: Extended schema manipulation\n  F381-01: \"ALTER TABLE statement: ALTER COLUMN clause\"\n  F381-02: \"ALTER TABLE statement: ADD CONSTRAINT clause\"\n  F381-03: \"ALTER TABLE statement: DROP CONSTRAINT clause\"\n  F382: Alter column data type\n  F383: Set column not null clause\n  F384: Drop identity property clause\n  F385: Drop column generation expression clause\n  F386: Set identity column generation clause\n  F391: Long identifiers\n  F392: Unicode escapes in identifiers\n  F393: Unicode escapes in literals\n  F394: Optional normal form specification\n  F401: Extended joined table\n  F401-01: NATURAL JOIN\n  F401-02: FULL OUTER JOIN\n  F401-04: CROSS JOIN\n  F402: \"Named column joins for LOBs, arrays, and multisets\"\n  F403: Partitioned join tables\n  F404: Range variable for common column names\n  F411: Time zone specification\n  F421: National character\n  F431: Read-only scROLEable cursors\n  F431-01: FETCH with explicit NEXT\n  F431-02: FETCH FIRST\n  F431-03: FETCH LAST\n  F431-04: FETCH PRIOR\n  F431-05: FETCH ABSOLUTE\n  F431-06: FETCH RELATIVE\n  F441: Extended set function support\n  F442: Mixed column references in set functions\n  F451: Character set definition\n  F461: Named character sets\n  F491: Constraint management\n  F492: Optional table constraint enforcement\n  F521: Assertions\n  F531: Temporary tables\n  F555: Enhanced seconds precision\n  F561: Full value expressions\n  F571: Truth value tests\n  F591: Derived tables\n  F611: Indicator data types\n  F641: Row and table constructors\n  F651: Catalog name qualifiers\n  F661: Simple tables\n  F671: Subqueries in CHECK constraints\n  F672: Retrospective check constraints\n  F673: Reads SQL-data routine invocations in CHECK constraints\n  F690: Collation support\n  F692: Enhanced collation support\n  F693: SQL-session and client module collations\n  F695: Translation support\n  F701: Referential update actions\n  F711: ALTER domain\n  F721: Deferrable constraints\n  F731: INSERT column privileges\n  F741: Referential MATCH types\n  F751: View CHECK enhancements\n  F761: Session management\n  F762: CURRENT_CATALOG\n  F763: CURRENT_SCHEMA\n  F771: Connection management\n  F781: Self-referencing operations\n  F791: Insensitive cursors\n  F801: Full set function\n  F813: Extended flagging\n  F821: Local table references\n  F831: Full cursor update\n  F831-01: Updateable scROLEable cursors\n  F831-02: Updateable ordered cursors\n  F841: LIKE_REGEX predicate\n  F842: OCCURRENCES_REGEX function\n  F843: POSITION_REGEX function\n  F844: SUBSTRING_REGEX\n  F845: TRANSLATE_REGEX\n  F846: Octet support in regular expression operators\n  F847: Nonconstant regular expressions\n  F850: Top-level <order by clause> in <query expression>\n  F851: <order by clause> in subqueries\n  F852: Top-level <order by clause> in views\n  F855: Nested <order by clause> in <query expression>\n  F856: Nested <fetch first clause> in <query expression>\n  F857: Top-level <fetch first clause> in <query expression>\n  F858: <fetch first clause> in subqueries\n  F859: Top-level <fetch first clause> in views\n  F860: dynamic <fetch first row count> in <fetch first clause>\n  F861: Top-level <result offset clause> in <query expression>\n  F862: <result offset clause> in subqueries\n  F863: Nested <result offset clause> in <query expression>\n  F864: Top-level <result offset clause> in views\n  F865: dynamic <offset row count> in <result offset clause>\n  F866: \"FETCH FIRST clause: PERCENT option\"\n  F867: \"FETCH FIRST clause: WITH TIES option\"\n  R010: \"Row pattern recognition: FROM clause\"\n  R020: \"Row pattern recognition: WINDOW clause\"\n  R030: \"Row pattern recognition: full aggregate support\"\n  S023: Basic structured types\n  S024: Enhanced structured types\n  S025: Final structured types\n  S026: Self-referencing structured types\n  S027: Create method by specific method name\n  S028: Permutable UDT options list\n  S041: Basic reference types\n  S043: Enhanced reference types\n  S051: Create table of type\n  S071: SQL paths in function and type name resolution\n  S081: Subtables\n  S091: Basic array support\n  S091-01: Arrays of built-in data types\n  S091-02: Arrays of distinct types\n  S091-03: Array expressions\n  S092: Arrays of user-defined types\n  S094: Arrays of reference types\n  S095: Array constructors by query\n  S096: Optional array bounds\n  S097: Array element assignment\n  S098: ARRAY_AGG\n  S111: ONLY in query expressions\n  S151: Type predicate\n  S161: Subtype treatment\n  S162: Subtype treatment for references\n  S201: SQL-invoked routines on arrays\n  S201-01: Array parameters\n  S201-02: Array as result type of functions\n  S202: SQL-invoked routines on multisets\n  S211: User-defined cast functions\n  S231: Structured type locators\n  S232: Array locators\n  S233: Multiset locators\n  S241: Transform functions\n  S242: Alter transform statement\n  S251: User-defined orderings\n  S261: Specific type method\n  S271: Basic multiset support\n  S272: Multisets of user-defined types\n  S274: Multisets of reference types\n  S275: Advanced multiset support\n  S281: Nested collection types\n  S291: Unique constraint on entire row\n  S301: Enhanced UNNEST\n  S401: Distinct types based on array types\n  S402: Distinct types based on multiset types\n  S403: ARRAY_MAX_CARDINALITY\n  S404: TRIM_ARRAY\n  T021: BINARY and VARBINARY data types\n  T022: Advanced support for BINARY and VARBINARY data types\n  T023: Compound binary literals\n  T024: Spaces in binary literals\n  T031: BOOLEAN data type\n  T041: Basic LOB data type support\n  T041-01: BLOB data type \n  T041-02: CLOB data type \n  T041-03: POSITION, LENGTH, LOWER, TRIM, UPPER, and SUBSTRING functions for LOB data types \n  T041-04: Concatenation of LOB data types \n  T041-05: \"LOB locator: non-holdable\"\n  T042: Extended LOB data type support\n  T043: Multiplier T\n  T044: Multiplier P\n  T051: Row types\n  T053: Explicit aliases for all-fields reference\n  T061: UCS support\n  T071: BIGINT data type\n  T076: DECFLOAT data type\n  T101: Enhanced nullability determination\n  T111: Updatable joins, unions, and columns\n  T121: WITH (excluding RECURSIVE) in query expression\n  T122: WITH (excluding RECURSIVE) in subquery\n  T131: Recursive query\n  T132: Recursive query in subquery\n  T141: SIMILAR predicate\n  T151: DISTINCT predicate\n  T152: DISTINCT predicate with negation\n  T171: LIKE clause in table definition\n  T172: AS subquery clause in table definition\n  T173: Extended LIKE clause in table definition\n  T174: Identity columns\n  T175: Generated columns\n  T176: Sequence generator support\n  T177: \"Sequence generator support: simple restart option\"\n  T178: \"Identity columns: simple restart option\"\n  T180: System-versioned tables\n  T181: Application-time period tables\n  T191: Referential action RESTRICT\n  T201: Comparable data types for referential constraints\n  T211: Basic trigger capability\n  T211-01: Triggers activated on UPDATE, INSERT, or DELETE of one base table\n  T211-02: BEFORE triggers\n  T211-03: AFTER triggers\n  T211-04: FOR EACH ROW triggers\n  T211-05: Ability to specify a search condition that shall be True before the trigger is invoked\n  T211-06: Support for run-time rules for the interaction of triggers and constraints\n  T211-07: TRIGGER privilege\n  T211-08: Multiple triggers for the same event are executed in the order in which they were created in the catalog\n  T212: Enhanced trigger capability\n  T213: INSTEAD OF triggers\n  T231: Sensitive cursors\n  T241: START TRANSACTION statement\n  T251: \"SET TRANSACTION statement: LOCAL option\"\n  T261: Chained transactions\n  T271: Savepoints\n  T272: Enhanced savepoint management\n  T281: SELECT privilege with column granularity\n  T285: Enhanced derived column names\n  T301: Functional dependencies\n  T312: OVERLAY function\n  T323: Explicit security for external routines\n  T324: Explicit security for SQL routines\n  T325: Qualified SQL parameter references\n  T326: Table functions\n  T331: Basic roles\n  T332: Extended roles\n  T341: Overloading of SQL-invoked functions and SQL-invoked procedures\n  T351: Bracketed comments\n  T431: Extended grouping capabilities\n  T432: Nested and concatenated GROUPING SETS\n  T433: Multiargument GROUPING function\n  T434: GROUP BY DISINCT\n  T441: ABS and MOD functions\n  T461: Symmetric BETWEEN predicate\n  T471: Result sets return value\n  T472: DESCRIBE CURSOR\n  T491: LATERAL derived table\n  T495: Combined data change and retrieval\n  T501: Enhanced EXISTS predicate\n  T502: Period predicates\n  T511: Transaction counts\n  T521: Named arguments in CALL statement\n  T522: Default values for IN parameters of SQL-invoked procedures\n  T523: Default values for INOUT parameters of SQL-invoked procedures\n  T524: Named arguments in routine invocations other than a CALL statement\n  T525: Default values for parameters of SQL-invoked functions\n  T551: Optional key words for default syntax\n  T561: Holdable locators\n  T571: Array-returning external SQL-invoked functions\n  T572: Multiset-returning external SQL-invoked functions\n  T581: Regular expression substring function\n  T591: UNIQUE constraints of possibly null columns\n  T601: Local cursor references\n  T611: Elementary OLAP operations\n  T612: Advanced OLAP operations\n  T613: Sampling\n  T614: NTILE function\n  T615: LEAD and LAG functions\n  T616: Null treatment option for LEAD and LAG functions\n  T617: FIRST_VALUE and LAST_VALUE functions\n  T618: NTH_VALUE function\n  T619: Nested window functions\n  T620: \"WINDOW clause: GROUPS option\"\n  T621: Enhanced numeric functions\n  T622: Trigonometric functions\n  T623: General logarithm functions\n  T624: Common logarithm functions\n  T625: LISTAGG\n  T641: Multiple column assignment\n  T651: SQL-schema statements in SQL routines\n  T652: SQL-dynamic statements in SQL routines\n  T653: SQL-schema statements in external routines\n  T654: SQL-dynamic statements in external routines\n  T655: Cyclically dependent routines\n  T811: Basic SQL/JSON constructor functions\n  T812: \"SQL/JSON: JSON_OBJECTAGG\"\n  T813: \"SQL/JSON: JSON_ARRAYAGG with ORDER BY\"\n  T814: Colon in JSON_OBJECT or JSON_OBJECTAGG\n  T821: Basic SQL/JSON query operators\n  T822: \"SQL/JSON: IS JSON WITH UNIQUE KEYS predicate\"\n  T823: \"SQL/JSON: PASSING clause\"\n  T824: \"JSON_TABLE: specific PLAN clause\"\n  T825: \"SQL/JSON: ON EMPTY and ON ERROR clauses\"\n  T826: General value expression in ON ERROR or ON EMPTY clauses\n  T827: \"JSON_TABLE: sibling NESTED COLUMNS clauses\"\n  T828: JSON_QUERY\n  T829: \"JSON_QUERY: array wrapper options\"\n  T830: Enforcing unique keys in SQL/JSON constructor functions\n  T831: \"SQL/JSON path language: strict mode\"\n  T832: \"SQL/JSON path language: item method\"\n  T833: \"SQL/JSON path language: multiple subscripts\"\n  T834: \"SQL/JSON path language: wildcard member accessor\"\n  T835: \"SQL/JSON path language: filter expressions\"\n  T836: \"SQL/JSON path language: starts with predicate\"\n  T837: \"SQL/JSON path language: regex_like predicate\"\n  T838: \"JSON_TABLE: PLAN DEFAULT clause\"\n  T839: Formatted cast of datetimes to/from character strings\n"
  },
  {
    "path": "crates/sqltest/standards/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Elliot Chance\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "crates/sqltest/standards/README.md",
    "content": "Test generated from (https://elliotchance.github.io/sqltest/)"
  },
  {
    "path": "crates/sqltest/test/basic/delete.slt",
    "content": "include ./test_data.slt\n\nquery IT rowsort\nselect * from inventory\n----\n1\t'health1'\n2\t'health2'\n3\t'health3'\n\nstatement ok\nDELETE FROM inventory WHERE inventory_id = 1\n\nquery IT rowsort\nselect * from inventory\n----\n2\t'health2'\n3\t'health3'\n\nstatement ok\nDELETE FROM inventory\n\nquery IT\nselect * from inventory\n----\n"
  },
  {
    "path": "crates/sqltest/test/basic/insert.slt",
    "content": "statement ok\nCREATE TABLE inventory (inventory_id INTEGER, name TEXT)\n\nstatement ok\nINSERT INTO inventory (inventory_id, name) VALUES (1, 'health1');\n\nquery IT\nselect * from inventory\n----\n1\t'health1'\n\n#TODO: It fails with many separate inserts\n"
  },
  {
    "path": "crates/sqltest/test/basic/joins.slt",
    "content": "include ./test_data_join.slt\n\nquery IT rowsort\nselect * from inventory\n----\n1\t'health1'\n2\t'health2'\n3\t'health3'\n\n\nquery II rowsort\nselect * from player\n----\n100\t1\n200\t1\n300\t1\n\n\nquery IRR rowsort\nSELECT * FROM location\n----\n100\t0.0\t32.0\n100\t1.0\t31.0\n\nquery II\nSELECT player.*  FROM player JOIN location\tON location.entity_id = player.entity_id WHERE x > 0 AND x <= 32 AND z > 0 AND z <= 32\n----\n100\t1\n\nquery IT\nSELECT\tinventory.*  FROM inventory\tJOIN player\tON inventory.inventory_id = player.inventory_id\tJOIN location ON player.entity_id = location.entity_id\tWHERE x > 0 AND x <= 32 AND z > 0 AND z <= 32\n----\n1\t'health1'\n"
  },
  {
    "path": "crates/sqltest/test/basic/select.slt",
    "content": "include ./test_data.slt\n\nquery IT rowsort\nselect * from inventory\n----\n1\t'health1'\n2\t'health2'\n3\t'health3'\n\nquery IT rowsort\nselect inventory.* from inventory\n----\n1\t'health1'\n2\t'health2'\n3\t'health3'\n\n\nquery I rowsort\nselect 1 from inventory\n----\n1\n1\n1\n\nquery IT rowsort\nselect * from inventory WHERE inventory_id = 1\n----\n1\t'health1'\n\n#Checking using table identifiers\nquery TI\nselect name, inventory.inventory_id from inventory WHERE inventory_id = 1\n----\n'health1'\t1\n\nquery I\nSELECT inventory.inventory_id FROM inventory WHERE inventory.inventory_id = 1\n----\n1\n\nquery TI\nselect name, inventory_id from inventory WHERE inventory_id = 1\n----\n'health1'\t1\n\n\nquery TI\nselect (name), (inventory_id) FROM inventory WHERE inventory_id = 1\n----\n'health1'\t1\n\n# TODO: Using expressions like inventory_id + 1\n"
  },
  {
    "path": "crates/sqltest/test/basic/test_data.slt",
    "content": "statement ok\nCREATE TABLE inventory (inventory_id INTEGER, name TEXT)\n\nstatement ok\nINSERT INTO inventory (inventory_id, name) VALUES (1, 'health1'),  (2, 'health2'), (3, 'health3')\n"
  },
  {
    "path": "crates/sqltest/test/basic/test_data_join.slt",
    "content": "statement ok\nCREATE TABLE inventory (inventory_id INTEGER, name TEXT);\nINSERT INTO inventory (inventory_id, name) VALUES (1, 'health1');\nINSERT INTO inventory (inventory_id, name) VALUES (2, 'health2');\nINSERT INTO inventory (inventory_id, name) VALUES (3, 'health3');\n\nstatement ok\nCREATE TABLE player (entity_id INTEGER, inventory_id INTEGER);\nINSERT INTO player (entity_id, inventory_id) VALUES (100,1), (200,1), (300,1);\n\nstatement ok\nCREATE TABLE location (entity_id INTEGER, x REAL,  z REAL);\nINSERT INTO location (entity_id, x, z) VALUES (100, 0.0, 32.0), (100, 1.0, 31.0)\n"
  },
  {
    "path": "crates/sqltest/test/basic/where.slt",
    "content": "include ./test_data.slt\n\nquery IT\nSELECT * FROM inventory WHERE inventory_id = 1\n----\n1\t'health1'\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id = 1 OR inventory_id=2\n----\n1\t'health1'\n2\t'health2'\n\nquery IT\nSELECT * FROM inventory WHERE inventory_id = 1 AND (1=1)\n----\n1\t'health1'\n\n\nquery IT\nSELECT * FROM inventory WHERE inventory.inventory_id = 1\n----\n1\t'health1'\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id > 1\n----\n2\t'health2'\n3\t'health3'\n\n\nquery IT\nSELECT * FROM inventory WHERE inventory_id < 2\n----\n1\t'health1'\n\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id <> 2\n----\n1\t'health1'\n3\t'health3'\n\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id <= 2\n----\n1\t'health1'\n2\t'health2'\n\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id >= 2\n----\n2\t'health2'\n3\t'health3'\n\n\n\nquery IT rowsort\nSELECT * FROM inventory WHERE inventory_id >= 2\n----\n2\t'health2'\n3\t'health3'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_01.slt",
    "content": "# E011-01: INTEGER and SMALLINT data types (including all spellings)\n\nstatement ok\nCREATE TABLE TABLE_E011_01_01_01 ( A BIGINT )\n\nstatement ok\nCREATE TABLE TABLE_E011_01_01_02 ( A INT )\n\nstatement ok\nCREATE TABLE TABLE_E011_01_01_03 ( A INTEGER )\n\nstatement ok\nCREATE TABLE TABLE_E011_01_01_04 ( A SMALLINT )\n\nquery I\nSELECT +5\n----\n5\n\nquery I\nSELECT -5\n----\n-5\n\nquery I\nSELECT 5\n----\n5\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_02.slt",
    "content": "# E011-02: REAL, DOUBLE PRECISION, and FLOAT data types\n\nstatement ok\nCREATE TABLE TABLE_E011_02_01_01 ( A DOUBLE PRECISION )\n\nstatement ok\nCREATE TABLE TABLE_E011_02_01_02 ( A FLOAT ( 2 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_02_01_03 ( A FLOAT )\n\nstatement ok\nCREATE TABLE TABLE_E011_02_01_04 ( A REAL )\n\nquery R\nSELECT +7.8\n----\n7.8\n\nquery R\nSELECT -7.8\n----\n-7.8\n\nquery R\nSELECT +.2\n----\n0.2\n\nquery R\nSELECT +.2E+2\n----\n20.0\n\nquery R\nSELECT +.2E-2\n----\n0.002\n\nquery R\nSELECT +.2E2\n----\n20.0\n\nquery I\nSELECT +2\n----\n2\n\nquery R\nSELECT +2.\n----\n2.0\n\nquery R\nSELECT +2.2\n----\n2.2\n\nquery R\nSELECT +2.2E+2\n----\n220.0\n\nquery R\nSELECT +2.2E-2\n----\n0.022\n\nquery R\nSELECT +2.2E2\n----\n220.0\n\nquery R\nSELECT +2.E+2\n----\n200.0\n\nquery R\nSELECT +2.E-2\n----\n0.02\n\nquery R\nSELECT +2.E2\n----\n200.0\n\nquery R\nSELECT +2E+2\n----\n200.0\n\nquery R\nSELECT +2E-2\n----\n0.02\n\nquery R\nSELECT +2E2\n----\n200.0\n\nquery R\nSELECT -.2\n----\n-0.2\n\nquery R\nSELECT -.2E+2\n----\n-20.0\n\nquery R\nSELECT -.2E-2\n----\n-0.002\n\nquery R\nSELECT -.2E2\n----\n-20.0\n\nquery I\nSELECT -2\n----\n-2\n\nquery R\nSELECT -2.\n----\n-2.0\n\nquery R\nSELECT -2.2\n----\n-2.2\n\nquery R\nSELECT -2.2E+2\n----\n-220.0\n\nquery R\nSELECT -2.2E-2\n----\n-0.022\n\nquery R\nSELECT -2.2E2\n----\n-220.0\n\nquery R\nSELECT -2.E+2\n----\n-200.0\n\nquery R\nSELECT -2.E-2\n----\n-0.02\n\nquery R\nSELECT -2.E2\n----\n-200.0\n\nquery R\nSELECT -2E+2\n----\n-200.0\n\nquery R\nSELECT -2E-2\n----\n-0.02\n\nquery R\nSELECT -2E2\n----\n-200.0\n\nquery R\nSELECT .2\n----\n0.2\n\nquery R\nSELECT .2E+2\n----\n20.0\n\nquery R\nSELECT .2E-2\n----\n0.002\n\nquery R\nSELECT .2E2\n----\n20.0\n\nquery I\nSELECT 2\n----\n2\n\nquery R\nSELECT 2.\n----\n2.0\n\nquery R\nSELECT 2.2\n----\n2.2\n\nquery R\nSELECT 2.2E+2\n----\n220.0\n\nquery R\nSELECT 2.2E-2\n----\n0.022\n\nquery R\nSELECT 2.2E2\n----\n220.0\n\nquery R\nSELECT 2.E+2\n----\n200.0\n\nquery R\nSELECT 2.E-2\n----\n0.02\n\nquery R\nSELECT 2.E2\n----\n200.0\n\nquery R\nSELECT 2E+2\n----\n200.0\n\nquery R\nSELECT 2E-2\n----\n0.02\n\nquery R\nSELECT 2E2\n----\n200.0\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_03.slt",
    "content": "# E011-03: DECIMAL and NUMERIC data types\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_01 ( A DEC ( 6 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_02 ( A DEC ( 6 , 3 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_03 ( A DEC )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_04 ( A DECIMAL ( 6 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_05 ( A DECIMAL ( 6 , 3 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_06 ( A DECIMAL )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_07 ( A NUMERIC ( 6 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_08 ( A NUMERIC ( 6 , 3 ) )\n\nstatement ok\nCREATE TABLE TABLE_E011_03_01_09 ( A NUMERIC )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_04.slt",
    "content": "# E011-04: Arithmetic operators\n\nquery I\nSELECT 5 +3\n----\n8\n\nquery I\nSELECT 5 -3\n----\n2\n\nquery R\nSELECT 3.4 + 1.2\n----\n4.6\n\nquery R\nSELECT 3.4 - 1.2\n----\n2.2\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_05.slt",
    "content": "# E011-05: Numeric comparison\n\nquery B\nSELECT 3 < 5\n----\n1\n\nquery B\nSELECT 3 <= 5\n----\n1\n\nquery B\nSELECT 3 <> 5\n----\n1\n\nquery B\nSELECT 3 = 5\n----\n0\n\nquery B\nSELECT 3 > 5\n----\n0\n\nquery B\nSELECT 3 >= 5\n----\n0\n\nquery B\nSELECT 3.7 < 5.2\n----\n1\n\nquery B\nSELECT 3.7 <= 5.2\n----\n1\n\nquery B\nSELECT 3.7 <> 5.2\n----\n1\n\nquery B\nSELECT 3.7 = 5.2\n----\n0\n\nquery B\nSELECT 3.7 > 5.2\n----\n0\n\nquery B\nSELECT 3.7 >= 5.2\n----\n0\n\nquery I\nSELECT ( 1 )\n----\n1\n\nquery R\nSELECT ( 5.5 )\n----\n5.5\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E011_06.slt",
    "content": "# E011-06: Implicit casting among the numeric data types\n\nquery B\nSELECT 3 < 1.2\n----\n0\n\nquery B\nSELECT 3 < 5\n----\n1\n\nquery B\nSELECT 3 <= 1.2\n----\n0\n\nquery B\nSELECT 3 <= 5\n----\n1\n\nquery B\nSELECT 3 <> 1.2\n----\n1\n\nquery B\nSELECT 3 <> 5\n----\n1\n\nquery B\nSELECT 3 = 1.2\n----\n0\n\nquery B\nSELECT 3 = 5\n----\n0\n\nquery B\nSELECT 3 > 1.2\n----\n1\n\nquery B\nSELECT 3 > 5\n----\n0\n\nquery B\nSELECT 3 >= 1.2\n----\n1\n\nquery B\nSELECT 3 >= 5\n----\n0\n\nquery B\nSELECT 3.7 < 1.2\n----\n0\n\nquery B\nSELECT 3.7 < 5\n----\n1\n\nquery B\nSELECT 3.7 <= 1.2\n----\n0\n\nquery B\nSELECT 3.7 <= 5\n----\n1\n\nquery B\nSELECT 3.7 <> 1.2\n----\n1\n\nquery B\nSELECT 3.7 <> 5\n----\n1\n\nquery B\nSELECT 3.7 = 1.2\n----\n0\n\nquery B\nSELECT 3.7 = 5\n----\n0\n\nquery B\nSELECT 3.7 > 1.2\n----\n1\n\nquery B\nSELECT 3.7 > 5\n----\n0\n\nquery B\nSELECT 3.7 >= 1.2\n----\n1\n\nquery B\nSELECT 3.7 >= 5\n----\n0\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_01.slt",
    "content": "# E021-01: CHARACTER data type (including all its spellings)\n\nstatement ok\nCREATE TABLE TABLE_E021_01_01_01 ( A CHAR ( 8 ) )\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_01_01_02 ( A CHAR ( 8 CHARACTERS ) )\n\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_01_01_03 ( A CHAR ( 8 OCTETS ) )\n\n\nstatement ok\nCREATE TABLE TABLE_E021_01_01_04 ( A CHAR )\n\nstatement ok\nCREATE TABLE TABLE_E021_01_01_05 ( A CHARACTER ( 8 ) )\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_01_01_06 ( A CHARACTER ( 8 CHARACTERS ) )\n\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_01_01_07 ( A CHARACTER ( 8 OCTETS ) )\n\n\nstatement ok\nCREATE TABLE TABLE_E021_01_01_08 ( A CHARACTER )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_02.slt",
    "content": "# E021-02: CHARACTER VARYING data type (including all its spellings)\n\n# (UNSUPPORTED: issue 2) statement ok\n# CREATE TABLE TABLE_E021_02_01_01 ( A CHAR VARING ( 8 ) )\n\n\n# (UNSUPPORTED: issue 2) # (UNSUPPORTED: issue 1) statement ok\n# # CREATE TABLE TABLE_E021_02_01_02 ( A CHAR VARING ( 8 CHARACTERS ) )\n#\n\n\n# (UNSUPPORTED: issue 2) # (UNSUPPORTED: issue 1) statement ok\n# # CREATE TABLE TABLE_E021_02_01_03 ( A CHAR VARING ( 8 OCTETS ) )\n#\n\n\n# (UNSUPPORTED: issue 2) statement ok\n# CREATE TABLE TABLE_E021_02_01_04 ( A CHAR VARING )\n\n\nstatement ok\nCREATE TABLE TABLE_E021_02_01_05 ( A CHARACTER VARYING ( 8 ) )\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_02_01_06 ( A CHARACTER VARYING ( 8 CHARACTERS ) )\n\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_02_01_07 ( A CHARACTER VARYING ( 8 OCTETS ) )\n\n\nstatement ok\nCREATE TABLE TABLE_E021_02_01_08 ( A CHARACTER VARYING )\n\nstatement ok\nCREATE TABLE TABLE_E021_02_01_09 ( A VARCHAR ( 8 ) )\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_02_01_10 ( A VARCHAR ( 8 CHARACTERS ) )\n\n\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE TABLE TABLE_E021_02_01_11 ( A VARCHAR ( 8 OCTETS ) )\n\n\nstatement ok\nCREATE TABLE TABLE_E021_02_01_12 ( A VARCHAR )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_03.slt",
    "content": "# E021-03: Character literals\n\nquery T\nSELECT ''\n----\n''\n\nquery T\nSELECT 'a'\n----\n'a'\n\nquery T\nSELECT 'abc'\n----\n'abc'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_04.slt",
    "content": "# E021-04: CHARACTER_LENGTH function\n\nquery I\nSELECT CHARACTER_LENGTH ( 'foo' )\n----\n3\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT CHARACTER_LENGTH ( 'foo' USING CHARACTERS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT CHARACTER_LENGTH ( 'foo' USING OCTETS )\n\n\nquery I\nSELECT CHAR_LENGTH ( 'foo' )\n----\n3\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT CHAR_LENGTH ( 'foo' USING CHARACTERS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT CHAR_LENGTH ( 'foo' USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_05.slt",
    "content": "# E021-05: OCTET_LENGTH function\n\nquery I\nSELECT OCTET_LENGTH ( 'foo' )\n----\n3\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_06.slt",
    "content": "# E021-06: SUBSTRING function\n\nquery T\nSELECT SUBSTRING ( 'foo' FROM 1 )\n----\n'foo'\n\nquery T\nSELECT SUBSTRING ( 'foo' FROM 1 FOR 2 )\n----\n'fo'\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT SUBSTRING ( 'foo' FROM 1 FOR 2 USING CHARACTERS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT SUBSTRING ( 'foo' FROM 1 FOR 2 USING OCTETS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT SUBSTRING ( 'foo' FROM 1 USING CHARACTERS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT SUBSTRING ( 'foo' FROM 1 USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_07.slt",
    "content": "# E021-07: Character concatenation\n\nquery T\nSELECT 'foo' || 'bar'\n----\n'foobar'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_08.slt",
    "content": "# E021-08: UPPER and LOWER functions\n\nquery T\nSELECT LOWER ( 'foo' )\n----\n'foo'\n\nquery T\nSELECT UPPER ( 'foo' )\n----\n'FOO'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_09.slt",
    "content": "# E021-09: TRIM function\n\nquery T\nSELECT TRIM ( 'foo' )\n----\n'foo'\n\nquery T\nSELECT TRIM ( 'foo' FROM 'foo' )\n----\n''\n\nquery T\nSELECT TRIM ( BOTH 'foo' FROM 'foo' )\n----\n''\n\nquery T\nSELECT TRIM ( BOTH FROM 'foo' )\n----\n'foo'\n\nquery T\nSELECT TRIM ( FROM 'foo' )\n----\n'foo'\n\nquery T\nSELECT TRIM ( LEADING 'foo' FROM 'foo' )\n----\n''\n\nquery T\nSELECT TRIM ( LEADING FROM 'foo' )\n----\n'foo'\n\nquery T\nSELECT TRIM ( TRAILING 'foo' FROM 'foo' )\n----\n''\n\nquery T\nSELECT TRIM ( TRAILING FROM 'foo' )\n----\n'foo'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_10.slt",
    "content": "# E021-10: Implicit casting among the fixed-length and variable-length character string types\n\nstatement ok\nCREATE TABLE TABLE_E021_10_01_01 ( A CHARACTER ( 10 ) , B CHARACTER VARYING ( 15 ) );\nINSERT INTO TABLE_E021_10_01_01 ( A, B ) VALUES ( 'foo' , 'bar' );\nSELECT A = B FROM TABLE_E021_10_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_11.slt",
    "content": "# E021-11: POSITION function\n\nquery I\nSELECT POSITION ( 'foo' IN 'bar' )\n----\n0\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT POSITION ( 'foo' IN 'bar' USING CHARACTERS )\n\n\n# (UNSUPPORTED: issue 1) query I\n# SELECT POSITION ( 'foo' IN 'bar' USING OCTETS )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E021_12.slt",
    "content": "# E021-12: Character comparison\n\nquery B\nSELECT 'foo' < 'bar'\n----\n0\n\nquery B\nSELECT 'foo' <= 'bar'\n----\n0\n\nquery B\nSELECT 'foo' <> 'bar'\n----\n1\n\nquery B\nSELECT 'foo' = 'bar'\n----\n0\n\nquery B\nSELECT 'foo' > 'bar'\n----\n1\n\nquery B\nSELECT 'foo' >= 'bar'\n----\n1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E031_01.slt",
    "content": "# E031-01: Delimited identifiers\n\nstatement ok\nCREATE TABLE TABLE_E031_01_01_01 ( \"A\" INT );\nSELECT \"A\" FROM TABLE_E031_01_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E031_02.slt",
    "content": "# E031-02: Lower case identifiers\n\nstatement ok\nCREATE TABLE TABLE_E031_02_01_01 ( A INT );\nSELECT A FROM TABLE_E031_02_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E031_03.slt",
    "content": "# E031-03: Trailing underscore\n\nstatement ok\nCREATE TABLE TABLE_E031_03_01_01 ( A_ INT );\nSELECT A_ FROM TABLE_E031_03_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051.slt",
    "content": "# E051: Basic query specification\n\nstatement ok\nCREATE TABLE TABLE_E051_01_01 ( A INT );\nSELECT A FROM TABLE_E051_01_01\n\nstatement ok\nCREATE TABLE TABLE_E051_01_02 ( A INT );\nSELECT A FROM TABLE_E051_01_02 WHERE A = 1\n\nstatement ok\nCREATE TABLE TABLE_E051_01_03 ( A INT );\nSELECT ALL A FROM TABLE_E051_01_03\n\nstatement ok\nCREATE TABLE TABLE_E051_01_04 ( A INT );\nSELECT ALL A FROM TABLE_E051_01_04 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_01.slt",
    "content": "# E051-01: SELECT DISTINCT\n\nstatement ok\nCREATE TABLE TABLE_E051_01_01_01 ( A INT );\nSELECT DISTINCT A FROM TABLE_E051_01_01_01\n\nstatement ok\nCREATE TABLE TABLE_E051_01_01_02 ( A INT );\nSELECT DISTINCT A FROM TABLE_E051_01_01_02 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_02.slt",
    "content": "# E051-02: GROUP BY clause\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_01 ( A INT, B INT );\nSELECT A, B FROM TABLE_E051_02_01_01 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_02 ( A INT, B INT );\nSELECT A, B FROM TABLE_E051_02_01_02 WHERE A = 1 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_03 ( A INT, B INT );\nSELECT ALL A, B FROM TABLE_E051_02_01_03 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_04 ( A INT, B INT );\nSELECT ALL A, B FROM TABLE_E051_02_01_04 WHERE A = 1 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_05 ( A INT, B INT );\nSELECT DISTINCT A, B FROM TABLE_E051_02_01_05 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_01_06 ( A INT, B INT );\nSELECT DISTINCT A, B FROM TABLE_E051_02_01_06 WHERE A = 1 GROUP BY A, B\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_01 ( A INT, B INT );\nSELECT A FROM TABLE_E051_02_02_01 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_02 ( A INT, B INT );\nSELECT A FROM TABLE_E051_02_02_02 WHERE A = 1 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_03 ( A INT, B INT );\nSELECT ALL A FROM TABLE_E051_02_02_03 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_04 ( A INT, B INT );\nSELECT ALL A FROM TABLE_E051_02_02_04 WHERE A = 1 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_05 ( A INT, B INT );\nSELECT DISTINCT A FROM TABLE_E051_02_02_05 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_E051_02_02_06 ( A INT, B INT );\nSELECT DISTINCT A FROM TABLE_E051_02_02_06 WHERE A = 1 GROUP BY A\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_04.slt",
    "content": "# E051-04: GROUP BY can contain columns not in <select list>\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_01 ( A INT, B INT );\nSELECT ALL B FROM TABLE_E051_04_01_01 GROUP BY B\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_02 ( A INT, B INT );\nSELECT ALL B FROM TABLE_E051_04_01_02 WHERE A = 1 GROUP BY B\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_03 ( A INT, B INT );\nSELECT B FROM TABLE_E051_04_01_03 GROUP BY B\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_04 ( A INT, B INT );\nSELECT B FROM TABLE_E051_04_01_04 WHERE A = 1 GROUP BY B\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_05 ( A INT, B INT );\nSELECT DISTINCT B FROM TABLE_E051_04_01_05 GROUP BY B\n\nstatement ok\nCREATE TABLE TABLE_E051_04_01_06 ( A INT, B INT );\nSELECT DISTINCT B FROM TABLE_E051_04_01_06 WHERE A = 1 GROUP BY B\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_05.slt",
    "content": "# E051-05: Select list items can be renamed\n\nstatement ok\nCREATE TABLE TABLE_E051_05_01_01 ( A INT );\nSELECT A AS RENAMED FROM TABLE_E051_05_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_06.slt",
    "content": "# E051-06: HAVING clause\n\nstatement ok\nCREATE TABLE TABLE_E051_06_01_01 ( A INT, B INT );\nSELECT A FROM TABLE_E051_06_01_01 GROUP BY A HAVING A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_07.slt",
    "content": "# E051-07: Qualified * in select list\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_01 ( A INT, B INT );\n# SELECT * AS ( C , D ) FROM TABLE_E051_07_01_01\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_02 ( A INT, B INT );\nSELECT * FROM TABLE_E051_07_01_02\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_03 ( A INT, B INT );\n# SELECT ALL * AS ( C , D ) FROM TABLE_E051_07_01_03\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_04 ( A INT, B INT );\nSELECT ALL * FROM TABLE_E051_07_01_04\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_05 ( A INT, B INT );\n# SELECT ALL TABLE_E051_07_01_05 . * AS ( C , D ) FROM TABLE_E051_07_01_05\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_06 ( A INT, B INT );\nSELECT ALL TABLE_E051_07_01_06 . * FROM TABLE_E051_07_01_06\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_07 ( A INT, B INT );\n# SELECT DISTINCT * AS ( C , D ) FROM TABLE_E051_07_01_07\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_08 ( A INT, B INT );\nSELECT DISTINCT * FROM TABLE_E051_07_01_08\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_09 ( A INT, B INT );\n# SELECT DISTINCT TABLE_E051_07_01_09 . * AS ( C , D ) FROM TABLE_E051_07_01_09\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_10 ( A INT, B INT );\nSELECT DISTINCT TABLE_E051_07_01_10 . * FROM TABLE_E051_07_01_10\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_07_01_11 ( A INT, B INT );\n# SELECT TABLE_E051_07_01_11 . * AS ( C , D ) FROM TABLE_E051_07_01_11\n\n\nstatement ok\nCREATE TABLE TABLE_E051_07_01_12 ( A INT, B INT );\nSELECT TABLE_E051_07_01_12 . * FROM TABLE_E051_07_01_12\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_08.slt",
    "content": "# E051-08: Correlation names in the FROM clause\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_01 ( A INT, B INT );\n# SELECT * AS ( C , D ) FROM TABLE_E051_08_01_01 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_02 ( A INT, B INT );\nSELECT * FROM TABLE_E051_08_01_02 AS MYTEMP\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_03 ( A INT, B INT );\n# SELECT ALL * AS ( C , D ) FROM TABLE_E051_08_01_03 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_04 ( A INT, B INT );\nSELECT ALL * FROM TABLE_E051_08_01_04 AS MYTEMP\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_05 ( A INT, B INT );\n# SELECT ALL MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_05 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_06 ( A INT, B INT );\nSELECT ALL MYTEMP . * FROM TABLE_E051_08_01_06 AS MYTEMP\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_07 ( A INT, B INT );\n# SELECT DISTINCT * AS ( C , D ) FROM TABLE_E051_08_01_07 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_08 ( A INT, B INT );\nSELECT DISTINCT * FROM TABLE_E051_08_01_08 AS MYTEMP\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_09 ( A INT, B INT );\n# SELECT DISTINCT MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_09 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_10 ( A INT, B INT );\nSELECT DISTINCT MYTEMP . * FROM TABLE_E051_08_01_10 AS MYTEMP\n\n# (UNSUPPORTED: issue 3) statement ok\n# CREATE TABLE TABLE_E051_08_01_11 ( A INT, B INT );\n# SELECT MYTEMP . * AS ( C , D ) FROM TABLE_E051_08_01_11 AS MYTEMP\n\n\nstatement ok\nCREATE TABLE TABLE_E051_08_01_12 ( A INT, B INT );\nSELECT MYTEMP . * FROM TABLE_E051_08_01_12 AS MYTEMP\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E051_09.slt",
    "content": "# E051-09: Rename columns in the FROM clause\n\nstatement ok\nCREATE TABLE TABLE_E051_09_01_01 ( A INT, B INT );\nSELECT ALL MYTEMP . X , Y FROM TABLE_E051_09_01_01 AS MYTEMP ( X, Y )\n\nstatement ok\nCREATE TABLE TABLE_E051_09_01_02 ( A INT, B INT );\nSELECT DISTINCT MYTEMP . X , Y FROM TABLE_E051_09_01_02 AS MYTEMP ( X, Y )\n\nstatement ok\nCREATE TABLE TABLE_E051_09_01_03 ( A INT, B INT );\nSELECT MYTEMP . X , Y FROM TABLE_E051_09_01_03 AS MYTEMP ( X, Y )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_01.slt",
    "content": "# E061-01: Comparison predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_01 ( A INT );\nSELECT A FROM TABLE_E061_01_01_01 WHERE A < 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_02 ( A INT );\nSELECT A FROM TABLE_E061_01_01_02 WHERE A <= 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_03 ( A INT );\nSELECT A FROM TABLE_E061_01_01_03 WHERE A <> 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_04 ( A INT );\nSELECT A FROM TABLE_E061_01_01_04 WHERE A = 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_05 ( A INT );\nSELECT A FROM TABLE_E061_01_01_05 WHERE A > 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_06 ( A INT );\nSELECT A FROM TABLE_E061_01_01_06 WHERE A >= 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_07 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_07 WHERE A < 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_08 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_08 WHERE A <= 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_09 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_09 WHERE A <> 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_10 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_10 WHERE A = 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_11 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_11 WHERE A > 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_12 ( A INT );\nSELECT ALL A FROM TABLE_E061_01_01_12 WHERE A >= 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_13 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_13 WHERE A < 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_14 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_14 WHERE A <= 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_15 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_15 WHERE A <> 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_16 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_16 WHERE A = 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_17 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_17 WHERE A > 1\n\nstatement ok\nCREATE TABLE TABLE_E061_01_01_18 ( A INT );\nSELECT DISTINCT A FROM TABLE_E061_01_01_18 WHERE A >= 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_02.slt",
    "content": "# E061-02: BETWEEN predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_01 ( A INT );\nSELECT A FROM TABLE_E061_02_01_01 WHERE A BETWEEN 1 AND 1\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_02 ( A INT );\nSELECT A FROM TABLE_E061_02_01_02 WHERE A BETWEEN ASYMMETRIC 1 AND 1\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_03 ( A INT );\nSELECT A FROM TABLE_E061_02_01_03 WHERE A BETWEEN SYMMETRIC 1 AND 1\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_04 ( A INT );\nSELECT A FROM TABLE_E061_02_01_04 WHERE A NOT BETWEEN 1 AND 1\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_05 ( A INT );\nSELECT A FROM TABLE_E061_02_01_05 WHERE A NOT BETWEEN ASYMMETRIC 1 AND 1\n\nstatement ok\nCREATE TABLE TABLE_E061_02_01_06 ( A INT );\nSELECT A FROM TABLE_E061_02_01_06 WHERE A NOT BETWEEN SYMMETRIC 1 AND 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_03.slt",
    "content": "# E061-03: IN predicate with list of values\n\nstatement ok\nCREATE TABLE TABLE_E061_03_01_01 ( A INT );\nSELECT A FROM TABLE_E061_03_01_01 WHERE A IN ( 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_03_01_02 ( A INT );\nSELECT A FROM TABLE_E061_03_01_02 WHERE A IN ( 1, 2 )\n\nstatement ok\nCREATE TABLE TABLE_E061_03_01_03 ( A INT );\nSELECT A FROM TABLE_E061_03_01_03 WHERE A NOT IN ( 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_03_01_04 ( A INT );\nSELECT A FROM TABLE_E061_03_01_04 WHERE A NOT IN ( 1, 2 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_04.slt",
    "content": "# E061-04: LIKE predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_04_01_01 ( A VARCHAR ( 255 ) );\nSELECT A FROM TABLE_E061_04_01_01 WHERE A LIKE 'foo'\n\nstatement ok\nCREATE TABLE TABLE_E061_04_01_02 ( A VARCHAR ( 255 ) );\nSELECT A FROM TABLE_E061_04_01_02 WHERE A NOT LIKE 'foo'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_05.slt",
    "content": "# E061-05: LIKE predicate: ESCAPE clause\n\nstatement ok\nCREATE TABLE TABLE_E061_05_01_01 ( A VARCHAR ( 255 ) );\nSELECT A FROM TABLE_E061_05_01_01 WHERE A LIKE 'foo' ESCAPE 'f'\n\nstatement ok\nCREATE TABLE TABLE_E061_05_01_02 ( A VARCHAR ( 255 ) );\nSELECT A FROM TABLE_E061_05_01_02 WHERE A NOT LIKE 'foo' ESCAPE 'f'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_06.slt",
    "content": "# E061-06: NULL predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_06_01_01 ( A INT );\nSELECT A FROM TABLE_E061_06_01_01 WHERE A IS NOT NULL\n\nstatement ok\nCREATE TABLE TABLE_E061_06_01_02 ( A INT );\nSELECT A FROM TABLE_E061_06_01_02 WHERE A IS NULL\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_07.slt",
    "content": "# E061-07: Quantified comparison predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_01 ( A INT );\nSELECT A FROM TABLE_E061_07_01_01 WHERE A < ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_02 ( A INT );\nSELECT A FROM TABLE_E061_07_01_02 WHERE A < ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_03 ( A INT );\nSELECT A FROM TABLE_E061_07_01_03 WHERE A < SOME ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_04 ( A INT );\nSELECT A FROM TABLE_E061_07_01_04 WHERE A <= ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_05 ( A INT );\nSELECT A FROM TABLE_E061_07_01_05 WHERE A <= ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_06 ( A INT );\nSELECT A FROM TABLE_E061_07_01_06 WHERE A <= SOME ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_07 ( A INT );\nSELECT A FROM TABLE_E061_07_01_07 WHERE A <> ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_08 ( A INT );\nSELECT A FROM TABLE_E061_07_01_08 WHERE A <> ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_09 ( A INT );\nSELECT A FROM TABLE_E061_07_01_09 WHERE A <> SOME ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_10 ( A INT );\nSELECT A FROM TABLE_E061_07_01_10 WHERE A = ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_11 ( A INT );\nSELECT A FROM TABLE_E061_07_01_11 WHERE A = ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_12 ( A INT );\nSELECT A FROM TABLE_E061_07_01_12 WHERE A = SOME ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_13 ( A INT );\nSELECT A FROM TABLE_E061_07_01_13 WHERE A > ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_14 ( A INT );\nSELECT A FROM TABLE_E061_07_01_14 WHERE A > ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_15 ( A INT );\nSELECT A FROM TABLE_E061_07_01_15 WHERE A > SOME ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_16 ( A INT );\nSELECT A FROM TABLE_E061_07_01_16 WHERE A >= ALL ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_17 ( A INT );\nSELECT A FROM TABLE_E061_07_01_17 WHERE A >= ANY ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_07_01_18 ( A INT );\nSELECT A FROM TABLE_E061_07_01_18 WHERE A >= SOME ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_08.slt",
    "content": "# E061-08: EXISTS predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_08_01_01 ( A INT );\nSELECT A FROM TABLE_E061_08_01_01 WHERE EXISTS ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_09.slt",
    "content": "# E061-09: Subqueries in comparison predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_01 ( A INT );\nSELECT A FROM TABLE_E061_09_01_01 WHERE A < ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_02 ( A INT );\nSELECT A FROM TABLE_E061_09_01_02 WHERE A <= ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_03 ( A INT );\nSELECT A FROM TABLE_E061_09_01_03 WHERE A <> ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_04 ( A INT );\nSELECT A FROM TABLE_E061_09_01_04 WHERE A = ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_05 ( A INT );\nSELECT A FROM TABLE_E061_09_01_05 WHERE A > ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E061_09_01_06 ( A INT );\nSELECT A FROM TABLE_E061_09_01_06 WHERE A >= ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_11.slt",
    "content": "# E061-11: Subqueries in IN predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_11_01_01 ( A INT );\nSELECT A FROM TABLE_E061_11_01_01 WHERE A IN ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_12.slt",
    "content": "# E061-12: Subqueries in quantified comparison predicate\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_01 ( A INT );\nSELECT A FROM TABLE_E061_12_01_01 WHERE A < ALL ( SELECT A FROM TABLE_E061_12_01_01 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_02 ( A INT );\nSELECT A FROM TABLE_E061_12_01_02 WHERE A < ANY ( SELECT A FROM TABLE_E061_12_01_02 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_03 ( A INT );\nSELECT A FROM TABLE_E061_12_01_03 WHERE A < SOME ( SELECT A FROM TABLE_E061_12_01_03 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_04 ( A INT );\nSELECT A FROM TABLE_E061_12_01_04 WHERE A <= ALL ( SELECT A FROM TABLE_E061_12_01_04 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_05 ( A INT );\nSELECT A FROM TABLE_E061_12_01_05 WHERE A <= ANY ( SELECT A FROM TABLE_E061_12_01_05 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_06 ( A INT );\nSELECT A FROM TABLE_E061_12_01_06 WHERE A <= SOME ( SELECT A FROM TABLE_E061_12_01_06 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_07 ( A INT );\nSELECT A FROM TABLE_E061_12_01_07 WHERE A <> ALL ( SELECT A FROM TABLE_E061_12_01_07 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_08 ( A INT );\nSELECT A FROM TABLE_E061_12_01_08 WHERE A <> ANY ( SELECT A FROM TABLE_E061_12_01_08 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_09 ( A INT );\nSELECT A FROM TABLE_E061_12_01_09 WHERE A <> SOME ( SELECT A FROM TABLE_E061_12_01_09 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_10 ( A INT );\nSELECT A FROM TABLE_E061_12_01_10 WHERE A = ALL ( SELECT A FROM TABLE_E061_12_01_10 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_11 ( A INT );\nSELECT A FROM TABLE_E061_12_01_11 WHERE A = ANY ( SELECT A FROM TABLE_E061_12_01_11 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_12 ( A INT );\nSELECT A FROM TABLE_E061_12_01_12 WHERE A = SOME ( SELECT A FROM TABLE_E061_12_01_12 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_13 ( A INT );\nSELECT A FROM TABLE_E061_12_01_13 WHERE A > ALL ( SELECT A FROM TABLE_E061_12_01_13 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_14 ( A INT );\nSELECT A FROM TABLE_E061_12_01_14 WHERE A > ANY ( SELECT A FROM TABLE_E061_12_01_14 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_15 ( A INT );\nSELECT A FROM TABLE_E061_12_01_15 WHERE A > SOME ( SELECT A FROM TABLE_E061_12_01_15 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_16 ( A INT );\nSELECT A FROM TABLE_E061_12_01_16 WHERE A >= ALL ( SELECT A FROM TABLE_E061_12_01_16 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_17 ( A INT );\nSELECT A FROM TABLE_E061_12_01_17 WHERE A >= ANY ( SELECT A FROM TABLE_E061_12_01_17 )\n\nstatement ok\nCREATE TABLE TABLE_E061_12_01_18 ( A INT );\nSELECT A FROM TABLE_E061_12_01_18 WHERE A >= SOME ( SELECT A FROM TABLE_E061_12_01_18 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_13.slt",
    "content": "# E061-13: Correlated subqueries\n\nstatement ok\nCREATE TABLE TABLE_E061_13_01_011 ( A INT );\nCREATE TABLE TABLE_E061_13_01_012 ( B INT );\nSELECT A FROM TABLE_E061_13_01_011 WHERE A IN ( SELECT B FROM TABLE_E061_13_01_012 WHERE B = A )\n\nstatement ok\nCREATE TABLE TABLE_E061_13_01_021 ( A INT );\nCREATE TABLE TABLE_E061_13_01_022 ( B INT );\nSELECT A FROM TABLE_E061_13_01_021 WHERE A NOT IN ( SELECT B FROM TABLE_E061_13_01_022 WHERE B = A )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E061_14.slt",
    "content": "# E061-14: Search condition\n\nstatement ok\nCREATE TABLE TABLE_E061_14_01_01 ( A INT );\nSELECT A FROM TABLE_E061_14_01_01 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E071_01.slt",
    "content": "# E071-01: UNION DISTINCT table operator\n\nstatement ok\nCREATE TABLE TABLE_E071_01_01_011 ( A INT );\nCREATE TABLE TABLE_E071_01_01_012 ( B INT );\nSELECT A FROM TABLE_E071_01_01_011 UNION DISTINCT SELECT B FROM TABLE_E071_01_01_012\n\nstatement ok\nCREATE TABLE TABLE_E071_01_01_021 ( A INT );\nCREATE TABLE TABLE_E071_01_01_022 ( B INT );\nSELECT A FROM TABLE_E071_01_01_021 UNION SELECT B FROM TABLE_E071_01_01_022\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E071_02.slt",
    "content": "# E071-02: UNION ALL table operator\n\nstatement ok\nCREATE TABLE TABLE_E071_02_01_011 ( A INT );\nCREATE TABLE TABLE_E071_02_01_012 ( B INT );\nSELECT A FROM TABLE_E071_02_01_011 UNION ALL SELECT B FROM TABLE_E071_02_01_012\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E071_03.slt",
    "content": "# E071-03: EXCEPT DISTINCT table operator\n\nstatement ok\nCREATE TABLE TABLE_E071_03_01_011 ( A INT );\nCREATE TABLE TABLE_E071_03_01_012 ( B INT );\nSELECT A FROM TABLE_E071_03_01_011 EXCEPT DISTINCT SELECT B FROM TABLE_E071_03_01_012\n\nstatement ok\nCREATE TABLE TABLE_E071_03_01_021 ( A INT );\nCREATE TABLE TABLE_E071_03_01_022 ( B INT );\nSELECT A FROM TABLE_E071_03_01_021 EXCEPT SELECT B FROM TABLE_E071_03_01_022\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E071_05.slt",
    "content": "# E071-05: Columns combined via table operators need not have exactly the same data type\n\nstatement ok\nCREATE TABLE TABLE_E071_05_01_011 ( A INT );\nCREATE TABLE TABLE_E071_05_01_012 ( B FLOAT );\nSELECT A FROM TABLE_E071_05_01_011 UNION ALL SELECT B FROM TABLE_E071_05_01_012\n\nstatement ok\nCREATE TABLE TABLE_E071_05_01_021 ( A INT );\nCREATE TABLE TABLE_E071_05_01_022 ( B FLOAT );\nSELECT A FROM TABLE_E071_05_01_021 UNION DISTINCT SELECT B FROM TABLE_E071_05_01_022\n\nstatement ok\nCREATE TABLE TABLE_E071_05_01_031 ( A INT );\nCREATE TABLE TABLE_E071_05_01_032 ( B FLOAT );\nSELECT A FROM TABLE_E071_05_01_031 UNION SELECT B FROM TABLE_E071_05_01_032\n\nstatement ok\nCREATE TABLE TABLE_E071_05_02_011 ( A INT );\nCREATE TABLE TABLE_E071_05_02_012 ( B FLOAT );\nSELECT A FROM TABLE_E071_05_02_011 EXCEPT DISTINCT SELECT B FROM TABLE_E071_05_02_012\n\nstatement ok\nCREATE TABLE TABLE_E071_05_02_021 ( A INT );\nCREATE TABLE TABLE_E071_05_02_022 ( B FLOAT );\nSELECT A FROM TABLE_E071_05_02_021 EXCEPT SELECT B FROM TABLE_E071_05_02_022\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E071_06.slt",
    "content": "# E071-06: Table operators in subqueries\n\nstatement ok\nCREATE TABLE TABLE_E071_06_01_011 ( A INT );\nCREATE TABLE TABLE_E071_06_01_012 ( B FLOAT );\nSELECT A FROM TABLE_E071_06_01_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_011 UNION ALL SELECT B FROM TABLE_E071_06_01_012 )\n\nstatement ok\nCREATE TABLE TABLE_E071_06_01_021 ( A INT );\nCREATE TABLE TABLE_E071_06_01_022 ( B FLOAT );\nSELECT A FROM TABLE_E071_06_01_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_021 UNION DISTINCT SELECT B FROM TABLE_E071_06_01_022 )\n\nstatement ok\nCREATE TABLE TABLE_E071_06_01_031 ( A INT );\nCREATE TABLE TABLE_E071_06_01_032 ( B FLOAT );\nSELECT A FROM TABLE_E071_06_01_031 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_01_031 UNION SELECT B FROM TABLE_E071_06_01_032 )\n\nstatement ok\nCREATE TABLE TABLE_E071_06_02_011 ( A INT );\nCREATE TABLE TABLE_E071_06_02_012 ( B FLOAT );\nSELECT A FROM TABLE_E071_06_02_011 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_02_011 EXCEPT DISTINCT SELECT B FROM TABLE_E071_06_02_012 )\n\nstatement ok\nCREATE TABLE TABLE_E071_06_02_021 ( A INT );\nCREATE TABLE TABLE_E071_06_02_022 ( B FLOAT );\nSELECT A FROM TABLE_E071_06_02_021 WHERE EXISTS ( SELECT A FROM TABLE_E071_06_02_021 EXCEPT SELECT B FROM TABLE_E071_06_02_022 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_01.slt",
    "content": "# E081-01: SELECT privilege at the table level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_01_01_011 ( A INT );\nCREATE ROLE ROLE_E081_01_01_01;\nGRANT SELECT ON TABLE TABLE_E081_01_01_011 TO ROLE_E081_01_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_01_01_021 ( A INT );\nCREATE ROLE ROLE_E081_01_01_02;\nGRANT SELECT ON TABLE_E081_01_01_021 TO ROLE_E081_01_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_02.slt",
    "content": "# E081-02: DELETE privilege\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_02_01_011 ( A INT );\nCREATE ROLE ROLE_E081_02_01_01;\nGRANT DELETE ON TABLE TABLE_E081_02_01_011 TO ROLE_E081_02_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_02_01_021 ( A INT );\nCREATE ROLE ROLE_E081_02_01_02;\nGRANT DELETE ON TABLE_E081_02_01_021 TO ROLE_E081_02_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_03.slt",
    "content": "# E081-03: INSERT privilege at the table level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_03_01_011 ( A INT );\nCREATE ROLE ROLE_E081_03_01_01;\nGRANT INSERT ON TABLE TABLE_E081_03_01_011 TO ROLE_E081_03_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_03_01_021 ( A INT );\nCREATE ROLE ROLE_E081_03_01_02;\nGRANT INSERT ON TABLE_E081_03_01_021 TO ROLE_E081_03_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_04.slt",
    "content": "# E081-04: UPDATE privilege at the table level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_04_01_011 ( A INT );\nCREATE ROLE ROLE_E081_04_01_01;\nGRANT UPDATE ON TABLE TABLE_E081_04_01_011 TO ROLE_E081_04_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_04_01_021 ( A INT );\nCREATE ROLE ROLE_E081_04_01_02;\nGRANT UPDATE ON TABLE_E081_04_01_021 TO ROLE_E081_04_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_05.slt",
    "content": "# E081-05: UPDATE privilege at the column level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_05_01_011 ( A INT );\nCREATE ROLE ROLE_E081_05_01_01;\nGRANT UPDATE ( A ) ON TABLE TABLE_E081_05_01_011 TO ROLE_E081_05_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_05_01_021 ( A INT );\nCREATE ROLE ROLE_E081_05_01_02;\nGRANT UPDATE ( A ) ON TABLE_E081_05_01_021 TO ROLE_E081_05_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_06.slt",
    "content": "# E081-06: REFERENCES privilege at the table level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_06_01_011 ( A INT );\nCREATE ROLE ROLE_E081_06_01_01;\nGRANT REFERENCES ON TABLE TABLE_E081_06_01_011 TO ROLE_E081_06_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_06_01_021 ( A INT );\nCREATE ROLE ROLE_E081_06_01_02;\nGRANT REFERENCES ON TABLE_E081_06_01_021 TO ROLE_E081_06_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_07.slt",
    "content": "# E081-07: REFERENCES privilege at the column level\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_07_01_011 ( A INT );\nCREATE ROLE ROLE_E081_07_01_01;\nGRANT REFERENCES ( A ) ON TABLE TABLE_E081_07_01_011 TO ROLE_E081_07_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_07_01_021 ( A INT );\nCREATE ROLE ROLE_E081_07_01_02;\nGRANT REFERENCES ( A ) ON TABLE_E081_07_01_021 TO ROLE_E081_07_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_08.slt",
    "content": "# E081-08: WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_01_011 ( A INT );\nCREATE ROLE ROLE_E081_08_01_01;\nGRANT SELECT ON TABLE TABLE_E081_08_01_011 TO ROLE_E081_08_01_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_01_021 ( A INT );\nCREATE ROLE ROLE_E081_08_01_02;\nGRANT SELECT ON TABLE_E081_08_01_021 TO ROLE_E081_08_01_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_02_011 ( A INT );\nCREATE ROLE ROLE_E081_08_02_01;\nGRANT DELETE ON TABLE TABLE_E081_08_02_011 TO ROLE_E081_08_02_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_02_021 ( A INT );\nCREATE ROLE ROLE_E081_08_02_02;\nGRANT DELETE ON TABLE_E081_08_02_021 TO ROLE_E081_08_02_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_03_011 ( A INT );\nCREATE ROLE ROLE_E081_08_03_01;\nGRANT INSERT ON TABLE TABLE_E081_08_03_011 TO ROLE_E081_08_03_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_03_021 ( A INT );\nCREATE ROLE ROLE_E081_08_03_02;\nGRANT INSERT ON TABLE_E081_08_03_021 TO ROLE_E081_08_03_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_04_011 ( A INT );\nCREATE ROLE ROLE_E081_08_04_01;\nGRANT UPDATE ON TABLE TABLE_E081_08_04_011 TO ROLE_E081_08_04_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_04_021 ( A INT );\nCREATE ROLE ROLE_E081_08_04_02;\nGRANT UPDATE ON TABLE_E081_08_04_021 TO ROLE_E081_08_04_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_05_011 ( A INT );\nCREATE ROLE ROLE_E081_08_05_01;\nGRANT UPDATE ( A ) ON TABLE TABLE_E081_08_05_011 TO ROLE_E081_08_05_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_05_021 ( A INT );\nCREATE ROLE ROLE_E081_08_05_02;\nGRANT UPDATE ( A ) ON TABLE_E081_08_05_021 TO ROLE_E081_08_05_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_06_011 ( A INT );\nCREATE ROLE ROLE_E081_08_06_01;\nGRANT REFERENCES ON TABLE TABLE_E081_08_06_011 TO ROLE_E081_08_06_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_06_021 ( A INT );\nCREATE ROLE ROLE_E081_08_06_02;\nGRANT REFERENCES ON TABLE_E081_08_06_021 TO ROLE_E081_08_06_02 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_07_011 ( A INT );\nCREATE ROLE ROLE_E081_08_07_01;\nGRANT REFERENCES ( A ) ON TABLE TABLE_E081_08_07_011 TO ROLE_E081_08_07_01 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E081_08_07_021 ( A INT );\nCREATE ROLE ROLE_E081_08_07_02;\nGRANT REFERENCES ( A ) ON TABLE_E081_08_07_021 TO ROLE_E081_08_07_02 WITH GRANT OPTION\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_09.slt",
    "content": "# E081-09: USAGE privilege\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_E081_09_01_011;\nCREATE ROLE ROLE_E081_09_01_01;\nGRANT USAGE ON TABLE TABLE_E081_09_01_011 TO ROLE_E081_09_01_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_E081_09_01_021;\nCREATE ROLE ROLE_E081_09_01_02;\nGRANT USAGE ON TABLE_E081_09_01_021 TO ROLE_E081_09_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E081_10.slt",
    "content": "# E081-10: EXECUTE privilege\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_E081_10_01_011;\nCREATE ROLE ROLE_E081_10_01_01;\nGRANT EXECUTE ON TABLE TABLE_E081_10_01_011 TO ROLE_E081_10_01_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_E081_10_01_021;\nCREATE ROLE ROLE_E081_10_01_02;\nGRANT EXECUTE ON TABLE_E081_10_01_021 TO ROLE_E081_10_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_01.slt",
    "content": "# E091-01: AVG\n\nstatement ok\nCREATE TABLE TABLE_E091_01_01_01 ( A FLOAT );\nSELECT AVG ( A ) FROM TABLE_E091_01_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_02.slt",
    "content": "# E091-02: COUNT\n\nstatement ok\nCREATE TABLE TABLE_E091_02_01_01 ( A FLOAT );\nSELECT COUNT ( * ) FROM TABLE_E091_02_01_01\n\nstatement ok\nCREATE TABLE TABLE_E091_02_01_02 ( A FLOAT );\nSELECT COUNT ( A ) FROM TABLE_E091_02_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_03.slt",
    "content": "# E091-03: MAX\n\nstatement ok\nCREATE TABLE TABLE_E091_03_01_01 ( A FLOAT );\nSELECT MAX ( A ) FROM TABLE_E091_03_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_04.slt",
    "content": "# E091-04: MIN\n\nstatement ok\nCREATE TABLE TABLE_E091_04_01_01 ( A FLOAT );\nSELECT MIN ( A ) FROM TABLE_E091_04_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_05.slt",
    "content": "# E091-05: SUM\n\nstatement ok\nCREATE TABLE TABLE_E091_05_01_01 ( A FLOAT );\nSELECT SUM ( A ) FROM TABLE_E091_05_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_06.slt",
    "content": "# E091-06: ALL quantifier\n\nstatement ok\nCREATE TABLE TABLE_E091_06_01_01 ( A FLOAT );\nSELECT AVG ( ALL A ) FROM TABLE_E091_06_01_01\n\nstatement ok\nCREATE TABLE TABLE_E091_06_02_01 ( A FLOAT );\nSELECT COUNT ( ALL A ) FROM TABLE_E091_06_02_01\n\nstatement ok\nCREATE TABLE TABLE_E091_06_03_01 ( A FLOAT );\nSELECT MAX ( ALL A ) FROM TABLE_E091_06_03_01\n\nstatement ok\nCREATE TABLE TABLE_E091_06_04_01 ( A FLOAT );\nSELECT MIN ( ALL A ) FROM TABLE_E091_06_04_01\n\nstatement ok\nCREATE TABLE TABLE_E091_06_05_01 ( A FLOAT );\nSELECT SUM ( ALL A ) FROM TABLE_E091_06_05_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E091_07.slt",
    "content": "# E091-07: DISTINCT quantifier\n\nstatement ok\nCREATE TABLE TABLE_E091_07_01_01 ( A FLOAT );\nSELECT AVG ( DISTINCT A ) FROM TABLE_E091_07_01_01\n\nstatement ok\nCREATE TABLE TABLE_E091_07_01_02 ( A FLOAT );\nSELECT COUNT ( DISTINCT A ) FROM TABLE_E091_07_01_02\n\nstatement ok\nCREATE TABLE TABLE_E091_07_01_03 ( A FLOAT );\nSELECT MAX ( DISTINCT A ) FROM TABLE_E091_07_01_03\n\nstatement ok\nCREATE TABLE TABLE_E091_07_01_04 ( A FLOAT );\nSELECT MIN ( DISTINCT A ) FROM TABLE_E091_07_01_04\n\nstatement ok\nCREATE TABLE TABLE_E091_07_01_05 ( A FLOAT );\nSELECT SUM ( DISTINCT A ) FROM TABLE_E091_07_01_05\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E101_01.slt",
    "content": "# E101-01: INSERT statement\n\nstatement ok\nCREATE TABLE TABLE_E101_01_01_01 ( A INT, B INT );\nINSERT INTO TABLE_E101_01_01_01 VALUES ( 1, 2 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E101_03.slt",
    "content": "# E101-03: Searched UPDATE statement\n\nstatement ok\nCREATE TABLE TABLE_E101_03_01_01 ( A INT, B INT );\nINSERT INTO TABLE_E101_03_01_01 VALUES ( 1, 2 );\nUPDATE TABLE_E101_03_01_01 SET A = 3, B = 4\n\n# Cause panic for Space\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E101_03_01_02 ( A INT, B INT );\nINSERT INTO TABLE_E101_03_01_02 VALUES ( 1, 2 );\nUPDATE TABLE_E101_03_01_02 SET A = 3, B = 4 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E101_04.slt",
    "content": "# E101-04: Searched DELETE statement\n\nstatement ok\nCREATE TABLE TABLE_E101_04_01_01 ( A INT, B INT );\nINSERT INTO TABLE_E101_04_01_01 VALUES ( 1, 2 );\nDELETE FROM TABLE_E101_04_01_01\n\nstatement ok\nCREATE TABLE TABLE_E101_04_01_02 ( A INT, B INT );\nINSERT INTO TABLE_E101_04_01_02 VALUES ( 1, 2 );\nDELETE FROM TABLE_E101_04_01_02 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E111.slt",
    "content": "# E111: Single row SELECT statement\n\nstatement ok\nCREATE TABLE TABLE_E111_01_011 ( A INT );\nCREATE TABLE TABLE_E111_01_012 ( A INT );\nSELECT A INTO TABLE_E111_01_013 FROM TABLE_E111_01_011\n\nstatement ok\nCREATE TABLE TABLE_E111_01_021 ( A INT );\nCREATE TABLE TABLE_E111_01_022 ( A INT );\nSELECT A INTO TABLE_E111_01_023 FROM TABLE_E111_01_021 WHERE A = 1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_01.slt",
    "content": "# E121-01: DECLARE CURSOR\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_01_01_01 ( A INT );\nDECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_01_01_02 ( A INT );\nDECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_02 FOR READ ONLY\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_01_01_03 ( A INT );\nDECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_03 FOR UPDATE\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_01_01_04 ( A INT );\nDECLARE MYCURSOR CURSOR FOR SELECT A FROM TABLE_E121_01_01_04 FOR UPDATE OF TABLE_E121_01_01_04\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_02.slt",
    "content": "# E121-02: ORDER BY columns need not be in select list\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_02_01_01 ( A INT, B INT );\nDECLARE CUR_E121_02_01_01 CURSOR FOR SELECT A FROM TABLE_E121_02_01_01 ORDER BY B\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_03.slt",
    "content": "# E121-03: Value expressions in ORDER BY clause\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_03_01_01 ( A INT );\nDECLARE CUR_E121_03_01_01 CURSOR FOR SELECT A FROM TABLE_E121_03_01_01 ORDER BY A +5\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_04.slt",
    "content": "# E121-04: OPEN statement\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_04_01_01 ( A INT );\nDECLARE CUR_E121_04_01_01 CURSOR FOR SELECT A FROM TABLE_E121_04_01_01;\nOPEN CUR_E121_04_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_06.slt",
    "content": "# E121-06: Positioned UPDATE statement\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_06_01_01 ( A INT );\nDECLARE CUR_E121_06_01_01 CURSOR FOR SELECT A FROM TABLE_E121_06_01_01;\nUPDATE TABLE_E121_06_01_01 SET A = 1 WHERE CURRENT OF CUR_E121_06_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_07.slt",
    "content": "# E121-07: Positioned DELETE statement\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_07_01_01 ( A INT );\nDECLARE CUR_E121_07_01_01 CURSOR FOR SELECT A FROM TABLE_E121_07_01_01;\nDELETE FROM ONLY ( TABLE_E121_07_01_01 ) WHERE CURRENT OF CUR_E121_07_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_07_01_02 ( A INT );\nDECLARE CUR_E121_07_01_02 CURSOR FOR SELECT A FROM TABLE_E121_07_01_02;\nDELETE FROM TABLE_E121_07_01_02 WHERE CURRENT OF CUR_E121_07_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_08.slt",
    "content": "# E121-08: CLOSE statement\n\nonlyif Postgres\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_08_01_01 ( A INT );\nDECLARE CUR_E121_08_01_01 CURSOR FOR SELECT A FROM TABLE_E121_08_01_01;\nOPEN CUR_E121_08_01_01;\nCLOSE CUR_E121_08_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_10.slt",
    "content": "# E121-10: FETCH statement: implicit NEXT\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_011 ( A INT );\nCREATE TABLE TABLE_E121_10_01_012 ( A INT );\nDECLARE CUR_E121_10_01_01 CURSOR FOR SELECT A FROM TABLE_E121_10_01_011;\nOPEN CUR_E121_10_01_01;\nFETCH ABSOLUTE 2 FROM CUR_E121_10_01_01 INTO TABLE_E121_10_01_012\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_021 ( A INT );\nCREATE TABLE TABLE_E121_10_01_022 ( A INT );\nDECLARE CUR_E121_10_01_02 CURSOR FOR SELECT A FROM TABLE_E121_10_01_021;\nOPEN CUR_E121_10_01_02;\nFETCH CUR_E121_10_01_02 INTO TABLE_E121_10_01_022\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_031 ( A INT );\nCREATE TABLE TABLE_E121_10_01_032 ( A INT );\nDECLARE CUR_E121_10_01_03 CURSOR FOR SELECT A FROM TABLE_E121_10_01_031;\nOPEN CUR_E121_10_01_03;\nFETCH FIRST FROM CUR_E121_10_01_03 INTO TABLE_E121_10_01_032\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_041 ( A INT );\nCREATE TABLE TABLE_E121_10_01_042 ( A INT );\nDECLARE CUR_E121_10_01_04 CURSOR FOR SELECT A FROM TABLE_E121_10_01_041;\nOPEN CUR_E121_10_01_04;\nFETCH FROM CUR_E121_10_01_04 INTO TABLE_E121_10_01_042\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_051 ( A INT );\nCREATE TABLE TABLE_E121_10_01_052 ( A INT );\nDECLARE CUR_E121_10_01_05 CURSOR FOR SELECT A FROM TABLE_E121_10_01_051;\nOPEN CUR_E121_10_01_05;\nFETCH LAST FROM CUR_E121_10_01_05 INTO TABLE_E121_10_01_052\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_061 ( A INT );\nCREATE TABLE TABLE_E121_10_01_062 ( A INT );\nDECLARE CUR_E121_10_01_06 CURSOR FOR SELECT A FROM TABLE_E121_10_01_061;\nOPEN CUR_E121_10_01_06;\nFETCH NEXT FROM CUR_E121_10_01_06 INTO TABLE_E121_10_01_062\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_071 ( A INT );\nCREATE TABLE TABLE_E121_10_01_072 ( A INT );\nDECLARE CUR_E121_10_01_07 CURSOR FOR SELECT A FROM TABLE_E121_10_01_071;\nOPEN CUR_E121_10_01_07;\nFETCH PRIOR FROM CUR_E121_10_01_07 INTO TABLE_E121_10_01_072\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_10_01_081 ( A INT );\nCREATE TABLE TABLE_E121_10_01_082 ( A INT );\nDECLARE CUR_E121_10_01_08 CURSOR FOR SELECT A FROM TABLE_E121_10_01_081;\nOPEN CUR_E121_10_01_08;\nFETCH RELATIVE 2 FROM CUR_E121_10_01_08 INTO TABLE_E121_10_01_082\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E121_17.slt",
    "content": "# E121-17: WITH HOLD cursors\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_17_01_01 ( A INT );\nDECLARE CUR_E121_17_01_01 CURSOR WITH HOLD FOR SELECT A FROM TABLE_E121_17_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E121_17_01_02 ( A INT );\nDECLARE CUR_E121_17_01_02 CURSOR WITHOUT HOLD FOR SELECT A FROM TABLE_E121_17_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E131.slt",
    "content": "# E131: Null value support (nulls in lieu of values)\n\nquery T\nSELECT NULL\n----\nnull\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_01.slt",
    "content": "# E141-01: NOT NULL constraints\n\nstatement ok\nCREATE TABLE TABLE_E141_01_01_01 ( A INT CONSTRAINT CONST_E141_01_01_01 NOT NULL )\n\nstatement ok\nCREATE TABLE TABLE_E141_01_01_02 ( A INT NOT NULL )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_02.slt",
    "content": "# E141-02: UNIQUE constraints of NOT NULL columns\n\nstatement ok\nCREATE TABLE TABLE_E141_02_01_01 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT CONST_E141_02_01_01 UNIQUE ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_02_01_02 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT CONST_E141_02_01_02 UNIQUE ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_02_01_03 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_02_01_04 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_02_02_01 ( A INT NOT NULL CONSTRAINT CONST_E141_02_02_01 UNIQUE )\n\nstatement ok\nCREATE TABLE TABLE_E141_02_02_02 ( A INT NOT NULL UNIQUE )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_03.slt",
    "content": "# E141-03: PRIMARY KEY constraints\n\nstatement ok\nCREATE TABLE TABLE_E141_03_01_01 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT CONST_E141_03_01_01 PRIMARY KEY ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_03_01_02 ( A INT NOT NULL, B INT NOT NULL, CONSTRAINT CONST_E141_03_01_02 PRIMARY KEY ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_03_01_03 ( A INT NOT NULL, B INT NOT NULL, PRIMARY KEY ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_03_01_04 ( A INT NOT NULL, B INT NOT NULL, PRIMARY KEY ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_03_02_01 ( A INT NOT NULL CONSTRAINT CONST_E141_03_02_01 PRIMARY KEY )\n\nstatement ok\nCREATE TABLE TABLE_E141_03_02_02 ( A INT NOT NULL PRIMARY KEY )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_04.slt",
    "content": "# E141-04: Basic FOREIGN KEY constraint with the NO ACTION default for both referential delete action and referential update action\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_011 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_012 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_01 REFERENCES TABLE_E141_04_01_011 ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_021 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_022 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_02 REFERENCES TABLE_E141_04_01_021 ( A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_031 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_032 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_03 REFERENCES TABLE_E141_04_01_031 ( A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_041 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_042 ( B INT NOT NULL CONSTRAINT CONST_E141_04_01_04 REFERENCES TABLE_E141_04_01_041 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_051 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_052 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_051 ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_061 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_062 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_061 ( A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_071 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_072 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_071 ( A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_01_081 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_01_082 ( B INT NOT NULL REFERENCES TABLE_E141_04_01_081 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_02_011 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_02_012 ( B INT NOT NULL CONSTRAINT CONST_E141_04_02_01 REFERENCES TABLE_E141_04_02_011 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_02_021 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_02_022 ( B INT NOT NULL REFERENCES TABLE_E141_04_02_021 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_011 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_012 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_01 FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_011 ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_021 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_022 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_02 FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_021 ( A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_031 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_032 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_03 FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_031 ( A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_041 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_042 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_03_04 FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_041 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_051 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_052 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_051 ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_061 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_062 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_061 ( A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_071 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_072 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_071 ( A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_03_081 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_03_082 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_03_081 ( A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_04_011 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_04_012 ( B INT NOT NULL, CONSTRAINT CONST_E141_04_04_01 FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_04_011 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_04_021 ( A INT NOT NULL, UNIQUE ( A ) );\nCREATE TABLE TABLE_E141_04_04_022 ( B INT NOT NULL, FOREIGN KEY ( B ) REFERENCES TABLE_E141_04_04_021 ( A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_01 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_011 ( A, B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_022 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_02 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_021 ( A, B ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_031 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_032 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_03 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_031 ( A, B ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_041 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_042 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_05_04 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_041 ( A, B ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_051 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_052 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_051 ( A, B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_061 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_062 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_061 ( A, B ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_071 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_072 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_071 ( A, B ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_05_081 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_05_082 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_05_081 ( A, B ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_06_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_06_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_04_06_01 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_06_011 ( A, B ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_04_06_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_04_06_022 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_04_06_021 ( A, B ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_06.slt",
    "content": "# E141-06: CHECK constraints\n\nstatement ok\nCREATE TABLE TABLE_E141_06_01_011 ( A INT CHECK ( A = 1 ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_06_01_021 ( A INT CONSTRAINT CONST_E141_06_01_02 CHECK ( A = 1 ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_06_02_011 ( A INT, CHECK ( A = 1 ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_06_02_021 ( A INT, CONSTRAINT CONST_E141_06_02_02 CHECK ( A = 1 ) )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_07.slt",
    "content": "# E141-07: Column defaults\n\nstatement ok\nCREATE TABLE TABLE_E141_07_01_01 ( A NAME DEFAULT CURRENT_CATALOG )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_02_01 ( A DATE DEFAULT CURRENT_DATE )\n\nskipif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E141_07_04_01 ( A NAME DEFAULT CURRENT_ROLE )\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_E141_07_05_01 ( A NAME DEFAULT CURRENT_SCHEMA )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_06_01 ( A NAME DEFAULT CURRENT_USER )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_07_01 ( A NAME DEFAULT SESSION_USER )\n\nskipif Postgres\nstatement ok\nCREATE TABLE TABLE_E141_07_09_01 ( A NAME DEFAULT USER )\n\n# (REPLACED: issue 4)\nstatement ok\nCREATE TABLE TABLE_E141_07_10_01 ( A TIME WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_11_01 ( A TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_12_01 ( A TIME WITH TIME ZONE DEFAULT LOCALTIME )\n\nstatement ok\nCREATE TABLE TABLE_E141_07_13_01 ( A TIMESTAMP WITH TIME ZONE DEFAULT LOCALTIMESTAMP )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_08.slt",
    "content": "# E141-08: NOT NULL inferred on PRIMARY KEY\n\nstatement ok\nCREATE TABLE TABLE_E141_08_01_01 ( A INT, B INT, CONSTRAINT CONST_E141_08_01_01 UNIQUE ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_01_02 ( A INT, B INT, CONSTRAINT CONST_E141_08_01_02 UNIQUE ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_01_03 ( A INT, B INT, UNIQUE ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_01_04 ( A INT, B INT, UNIQUE ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_02_01 ( A INT CONSTRAINT CONST_E141_08_02_01 UNIQUE )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_02_02 ( A INT UNIQUE )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_03_01 ( A INT, B INT, CONSTRAINT CONST_E141_08_03_01 PRIMARY KEY ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_03_02 ( A INT, B INT, CONSTRAINT CONST_E141_08_03_02 PRIMARY KEY ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_03_03 ( A INT, B INT, PRIMARY KEY ( A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_03_04 ( A INT, B INT, PRIMARY KEY ( A , B ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_04_01 ( A INT CONSTRAINT CONST_E141_08_04_01 PRIMARY KEY )\n\nstatement ok\nCREATE TABLE TABLE_E141_08_04_02 ( A INT PRIMARY KEY )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E141_10.slt",
    "content": "# E141-10: Names in a foreign key can be specified in any order\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_01 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_011 ( B, A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_022 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_02 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_021 ( B, A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_031 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_032 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_03 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_031 ( B, A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_041 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_042 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_01_04 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_041 ( B, A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_051 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_052 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_051 ( B, A ) )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_061 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_062 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_061 ( B, A ) ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_071 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_072 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_071 ( B, A ) ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_01_081 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_01_082 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_01_081 ( B, A ) ON UPDATE NO ACTION ON DELETE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_02_011 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_02_012 ( C INT NOT NULL, D INT NOT NULL, CONSTRAINT CONST_E141_10_02_01 FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_02_011 ( B, A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n\nstatement ok\nCREATE TABLE TABLE_E141_10_02_021 ( A INT NOT NULL, B INT NOT NULL, UNIQUE ( A, B ) );\nCREATE TABLE TABLE_E141_10_02_022 ( C INT NOT NULL, D INT NOT NULL, FOREIGN KEY ( C, D ) REFERENCES TABLE_E141_10_02_021 ( B, A ) ON DELETE NO ACTION ON UPDATE NO ACTION )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E151_01.slt",
    "content": "# E151-01: COMMIT statement\n\nstatement ok\nSTART TRANSACTION;\nCOMMIT\n\nstatement ok\nSTART TRANSACTION;\nCOMMIT WORK\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E151_02.slt",
    "content": "# E151-02: ROLLBACK statement\n\nstatement ok\nSTART TRANSACTION;\nROLLBACK\n\nstatement ok\nSTART TRANSACTION;\nROLLBACK WORK\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E152_01.slt",
    "content": "# E152-01: SET TRANSACTION statement: ISOLATION LEVEL SERIALIZABLE clause\n\nstatement ok\nSTART TRANSACTION;\nSET LOCAL TRANSACTION ISOLATION LEVEL SERIALIZABLE\n\nstatement ok\nSTART TRANSACTION;\nSET TRANSACTION ISOLATION LEVEL SERIALIZABLE\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E152_02.slt",
    "content": "# E152-02: SET TRANSACTION statement: READ ONLY and READ WRITE clauses\n\nstatement ok\nSTART TRANSACTION;\nSET LOCAL TRANSACTION READ ONLY\n\nstatement ok\nSTART TRANSACTION;\nSET LOCAL TRANSACTION READ WRITE\n\nstatement ok\nSTART TRANSACTION;\nSET TRANSACTION READ ONLY\n\nstatement ok\nSTART TRANSACTION;\nSET TRANSACTION READ WRITE\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E153.slt",
    "content": "# E153: Updatable queries with subqueries\n\nstatement ok\nCREATE TABLE TABLE_E153_01_01 ( A INT, B INT );\nINSERT INTO TABLE_E153_01_01 VALUES ( 1, 2 );\nUPDATE TABLE_E153_01_01 SET A = 3, B = 4 WHERE A = ( SELECT 1 )\n\nstatement ok\nCREATE TABLE TABLE_E153_02_01 ( A INT, B INT );\nINSERT INTO TABLE_E153_02_01 VALUES ( 1, 2 );\nDELETE FROM TABLE_E153_02_01 WHERE A = ( SELECT 1 )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/E161.slt",
    "content": "# E161: SQL comments using leading double minus\n\nquery I\nSELECT 1 -- hello\n----\n1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_01.slt",
    "content": "# F031-01: CREATE TABLE statement to create persistent base tables\n\nstatement ok\nCREATE TABLE TABLE_F031_01_01_01 ( A INTEGER )\n\nstatement ok\nCREATE TABLE TABLE_F031_01_01_02 ( A INTEGER , B INTEGER )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_02.slt",
    "content": "# F031-02: CREATE VIEW statement\n\nstatement ok\nCREATE TABLE TABLE_F031_02_01_01 ( A INTEGER );\nCREATE VIEW VIEW_F031_02_01_01 AS SELECT A FROM TABLE_F031_02_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_03.slt",
    "content": "# F031-03: GRANT statement\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_01;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_01 TO ROLE_F031_03_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_02 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_02;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_02 TO ROLE_F031_03_01_02 GRANTED BY CURRENT_ROLE\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_03 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_03;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_03 TO ROLE_F031_03_01_03 GRANTED BY CURRENT_USER\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_04 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_04;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_04 TO ROLE_F031_03_01_04 WITH GRANT OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_05 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_05;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_05 TO ROLE_F031_03_01_05 WITH GRANT OPTION GRANTED BY CURRENT_ROLE\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_01_06 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_01_06;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_01_06 TO ROLE_F031_03_01_06 WITH GRANT OPTION GRANTED BY CURRENT_USER\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_02_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_02_01;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_02_01 TO ROLE_F031_03_02_01 , ROLE_F031_03_02_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_01;\nGRANT DELETE ON TABLE_F031_03_03_01 TO ROLE_F031_03_03_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_02 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_02;\nGRANT INSERT ( A ) ON TABLE_F031_03_03_02 TO ROLE_F031_03_03_02\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_03 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_03;\nGRANT INSERT ON TABLE_F031_03_03_03 TO ROLE_F031_03_03_03\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_04 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_04;\nGRANT REFERENCES ( A ) ON TABLE_F031_03_03_04 TO ROLE_F031_03_03_04\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_05 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_05;\nGRANT REFERENCES ON TABLE_F031_03_03_05 TO ROLE_F031_03_03_05\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_06 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_06;\nGRANT SELECT ( A ) ON TABLE_F031_03_03_06 TO ROLE_F031_03_03_06\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_07 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_07;\nGRANT SELECT ON TABLE_F031_03_03_07 TO ROLE_F031_03_03_07\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_08 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_08;\nGRANT TRIGGER ON TABLE_F031_03_03_08 TO ROLE_F031_03_03_08\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_09 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_09;\nGRANT UNDER ON TABLE_F031_03_03_09 TO ROLE_F031_03_03_09\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_10 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_10;\nGRANT UPDATE ( A ) ON TABLE_F031_03_03_10 TO ROLE_F031_03_03_10\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_03_11 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_03_11;\nGRANT UPDATE ON TABLE_F031_03_03_11 TO ROLE_F031_03_03_11\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_F031_03_04_01;\nCREATE ROLE ROLE_F031_03_04_01;\nGRANT EXECUTE ON TABLE_F031_03_04_01 TO ROLE_F031_03_04_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_F031_03_04_02;\nCREATE ROLE ROLE_F031_03_04_02;\nGRANT USAGE ON TABLE_F031_03_04_02 TO ROLE_F031_03_04_02\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_05_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_05_01;\nGRANT ALL PRIVILEGES ON TABLE TABLE_F031_03_05_01 TO ROLE_F031_03_05_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_03_05_02 ( A INTEGER );\nCREATE ROLE ROLE_F031_03_05_02;\nGRANT ALL PRIVILEGES ON TABLE_F031_03_05_02 TO ROLE_F031_03_05_02\n\nonlyif Postgres\nstatement ok\nCREATE DOMAIN DOMAIN1 AS INT;\nCREATE ROLE ROLE_F031_03_06_01;\nGRANT ALL PRIVILEGES ON DOMAIN DOMAIN1 TO ROLE_F031_03_06_01\n\nonlyif Postgres\nstatement ok\nCREATE COLLATION COLLATION1 FROM 'de_DE';\nCREATE ROLE ROLE_F031_03_07_01;\nGRANT ALL PRIVILEGES ON COLLATION COLLATION1 TO ROLE_F031_03_07_01\n\nonlyif Postgres\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE CHARACTER SET CHARACTERSET1;\n# CREATE ROLE ROLE_F031_03_08_01;\n# GRANT ALL PRIVILEGES ON CHARACTER SET CHARACTERSET1 TO ROLE_F031_03_08_01\n\n\nonlyif Postgres\nstatement ok\nCREATE TRANSLATION TRANSLATION1;\nCREATE ROLE ROLE_F031_03_09_01;\nGRANT ALL PRIVILEGES ON TRANSLATION TRANSLATION1 TO ROLE_F031_03_09_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TYPE TYPE1;\nCREATE ROLE ROLE_F031_03_10_01;\nGRANT ALL PRIVILEGES ON TYPE TYPE1 TO ROLE_F031_03_10_01\n\nonlyif Postgres\nstatement ok\nCREATE SEQUENCE SEQUENCE1;\nCREATE ROLE ROLE_F031_03_11_01;\nGRANT ALL PRIVILEGES ON SEQUENCE SEQUENCE1 TO ROLE_F031_03_11_01\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_01;\nGRANT ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FOR BAZ TO ROLE_F031_03_12_01\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_02;\nGRANT ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR TO ROLE_F031_03_12_02\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_03;\nGRANT ALL PRIVILEGES ON FUNCTION BAR FOR BAZ TO ROLE_F031_03_12_03\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_04;\nGRANT ALL PRIVILEGES ON FUNCTION BAR TO ROLE_F031_03_12_04\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_05;\nGRANT ALL PRIVILEGES ON INSTANCE METHOD BAR FOR BAZ TO ROLE_F031_03_12_05\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_06;\nGRANT ALL PRIVILEGES ON INSTANCE METHOD BAR TO ROLE_F031_03_12_06\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_07;\nGRANT ALL PRIVILEGES ON METHOD BAR FOR BAZ TO ROLE_F031_03_12_07\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_08;\nGRANT ALL PRIVILEGES ON METHOD BAR TO ROLE_F031_03_12_08\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_09;\nGRANT ALL PRIVILEGES ON PROCEDURE BAR FOR BAZ TO ROLE_F031_03_12_09\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_10;\nGRANT ALL PRIVILEGES ON PROCEDURE BAR TO ROLE_F031_03_12_10\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_11;\nGRANT ALL PRIVILEGES ON ROUTINE BAR FOR BAZ TO ROLE_F031_03_12_11\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_12;\nGRANT ALL PRIVILEGES ON ROUTINE BAR TO ROLE_F031_03_12_12\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_13;\nGRANT ALL PRIVILEGES ON SPECIFIC CONSTRUCTOR METHOD FOO TO ROLE_F031_03_12_13\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_14;\nGRANT ALL PRIVILEGES ON SPECIFIC FUNCTION FOO TO ROLE_F031_03_12_14\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_15;\nGRANT ALL PRIVILEGES ON SPECIFIC INSTANCE METHOD FOO TO ROLE_F031_03_12_15\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_16;\nGRANT ALL PRIVILEGES ON SPECIFIC METHOD FOO TO ROLE_F031_03_12_16\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_17;\nGRANT ALL PRIVILEGES ON SPECIFIC PROCEDURE FOO TO ROLE_F031_03_12_17\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_18;\nGRANT ALL PRIVILEGES ON SPECIFIC ROUTINE FOO TO ROLE_F031_03_12_18\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_19;\nGRANT ALL PRIVILEGES ON SPECIFIC STATIC METHOD FOO TO ROLE_F031_03_12_19\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_20;\nGRANT ALL PRIVILEGES ON STATIC METHOD BAR FOR BAZ TO ROLE_F031_03_12_20\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_03_12_21;\nGRANT ALL PRIVILEGES ON STATIC METHOD BAR TO ROLE_F031_03_12_21\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_04.slt",
    "content": "# F031-04: ALTER TABLE statement: ADD COLUMN clause\n\nstatement ok\nCREATE TABLE TABLE_F031_04_01_01 ( A INTEGER );\nALTER TABLE TABLE_F031_04_01_01 ADD B INT\n\nstatement ok\nCREATE TABLE TABLE_F031_04_01_02 ( A INTEGER );\nALTER TABLE TABLE_F031_04_01_02 ADD COLUMN B INT\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_13.slt",
    "content": "# F031-13: DROP TABLE statement: RESTRICT clause\n\nstatement ok\nCREATE TABLE TABLE_F031_13_01_01 ( A INTEGER );\nDROP TABLE TABLE_F031_13_01_01 \n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_16.slt",
    "content": "# F031-16: DROP VIEW statement: RESTRICT clause\n\nstatement ok\nCREATE TABLE TABLE_F031_16_01_01 ( A INTEGER );\nCREATE VIEW VIEW_F031_16_01_01 AS SELECT A FROM TABLE_F031_16_01_01;\nDROP VIEW VIEW_F031_16_01_01 \n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F031_19.slt",
    "content": "# F031-19: REVOKE statement: RESTRICT clause\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_01_01 ( A INT );\nCREATE ROLE ROLE_F031_19_01_01;\nREVOKE SELECT ON TABLE_F031_19_01_01 FROM ROLE_F031_19_01_01 \n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_01_02 ( A INT );\nCREATE ROLE ROLE_F031_19_01_02;\nREVOKE SELECT ON TABLE_F031_19_01_02 FROM ROLE_F031_19_01_02 , ROLE_F031_19_01_02 \n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_01_03 ( A INT );\nCREATE ROLE ROLE_F031_19_01_03;\nREVOKE SELECT ON TABLE_F031_19_01_03 FROM ROLE_F031_19_01_03 , ROLE_F031_19_01_03 GRANTED BY ROLE_F031_19_01_03 \n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_01_04 ( A INT );\nCREATE ROLE ROLE_F031_19_01_04;\nREVOKE SELECT ON TABLE_F031_19_01_04 FROM ROLE_F031_19_01_04 GRANTED BY ROLE_F031_19_01_04 \n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_01;\nREVOKE DELETE ON TABLE_F031_19_02_01 FROM ROLE_F031_19_02_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_02 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_02;\nREVOKE EXECUTE ON TABLE_F031_19_02_02 FROM ROLE_F031_19_02_02\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_03 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_03;\nREVOKE INSERT ( A ) ON TABLE_F031_19_02_03 FROM ROLE_F031_19_02_03\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_04 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_04;\nREVOKE INSERT ON TABLE_F031_19_02_04 FROM ROLE_F031_19_02_04\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_05 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_05;\nREVOKE REFERENCES ( A ) ON TABLE_F031_19_02_05 FROM ROLE_F031_19_02_05\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_06 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_06;\nREVOKE REFERENCES ON TABLE_F031_19_02_06 FROM ROLE_F031_19_02_06\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_07 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_07;\nREVOKE SELECT ( A ) ON TABLE_F031_19_02_07 FROM ROLE_F031_19_02_07\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_08 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_08;\nREVOKE SELECT ON TABLE_F031_19_02_08 FROM ROLE_F031_19_02_08\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_09 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_09;\nREVOKE TRIGGER ON TABLE_F031_19_02_09 FROM ROLE_F031_19_02_09\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_10 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_10;\nREVOKE UNDER ON TABLE_F031_19_02_10 FROM ROLE_F031_19_02_10\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_11 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_11;\nREVOKE UPDATE ( A ) ON TABLE_F031_19_02_11 FROM ROLE_F031_19_02_11\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_12 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_12;\nREVOKE UPDATE ON TABLE_F031_19_02_12 FROM ROLE_F031_19_02_12\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_02_13 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_02_13;\nREVOKE USAGE ON TABLE_F031_19_02_13 FROM ROLE_F031_19_02_13\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_03_01 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_03_01;\nREVOKE ALL PRIVILEGES ON TABLE TABLE_F031_19_03_01 FROM ROLE_F031_19_03_01\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F031_19_03_02 ( A INTEGER );\nCREATE ROLE ROLE_F031_19_03_02;\nREVOKE ALL PRIVILEGES ON TABLE_F031_19_03_02 FROM ROLE_F031_19_03_02\n\nonlyif Postgres\nstatement ok\nCREATE DOMAIN DOMAIN1 AS INT;\nCREATE ROLE ROLE_F031_19_04_01;\nREVOKE ALL PRIVILEGES ON DOMAIN DOMAIN1 FROM ROLE_F031_19_04_01\n\nonlyif Postgres\nstatement ok\nCREATE COLLATION COLLATION1 FROM 'de_DE';\nCREATE ROLE ROLE_F031_19_05_01;\nREVOKE ALL PRIVILEGES ON COLLATION COLLATION1 FROM ROLE_F031_19_05_01\n\nonlyif Postgres\n# (UNSUPPORTED: issue 1) statement ok\n# CREATE CHARACTER SET CHARACTERSET1;\n# CREATE ROLE ROLE_F031_19_06_01;\n# REVOKE ALL PRIVILEGES ON CHARACTER SET CHARACTERSET1 FROM ROLE_F031_19_06_01\n\n\nonlyif Postgres\nstatement ok\nCREATE TRANSLATION TRANSLATION1;\nCREATE ROLE ROLE_F031_19_07_01;\nREVOKE ALL PRIVILEGES ON TRANSLATION TRANSLATION1 FROM ROLE_F031_19_07_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TYPE TYPE1;\nCREATE ROLE ROLE_F031_19_08_01;\nREVOKE ALL PRIVILEGES ON TYPE TYPE1 FROM ROLE_F031_19_08_01\n\nonlyif Postgres\nstatement ok\nCREATE SEQUENCE SEQUENCE1;\nCREATE ROLE ROLE_F031_19_09_01;\nREVOKE ALL PRIVILEGES ON SEQUENCE SEQUENCE1 FROM ROLE_F031_19_09_01\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_01;\nREVOKE ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FOR BAZ FROM ROLE_F031_19_10_01\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_02;\nREVOKE ALL PRIVILEGES ON CONSTRUCTOR METHOD BAR FROM ROLE_F031_19_10_02\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_03;\nREVOKE ALL PRIVILEGES ON FUNCTION BAR FOR BAZ FROM ROLE_F031_19_10_03\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_04;\nREVOKE ALL PRIVILEGES ON FUNCTION BAR FROM ROLE_F031_19_10_04\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_05;\nREVOKE ALL PRIVILEGES ON INSTANCE METHOD BAR FOR BAZ FROM ROLE_F031_19_10_05\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_06;\nREVOKE ALL PRIVILEGES ON INSTANCE METHOD BAR FROM ROLE_F031_19_10_06\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_07;\nREVOKE ALL PRIVILEGES ON METHOD BAR FOR BAZ FROM ROLE_F031_19_10_07\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_08;\nREVOKE ALL PRIVILEGES ON METHOD BAR FROM ROLE_F031_19_10_08\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_09;\nREVOKE ALL PRIVILEGES ON PROCEDURE BAR FOR BAZ FROM ROLE_F031_19_10_09\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_10;\nREVOKE ALL PRIVILEGES ON PROCEDURE BAR FROM ROLE_F031_19_10_10\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_11;\nREVOKE ALL PRIVILEGES ON ROUTINE BAR FOR BAZ FROM ROLE_F031_19_10_11\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_12;\nREVOKE ALL PRIVILEGES ON ROUTINE BAR FROM ROLE_F031_19_10_12\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_13;\nREVOKE ALL PRIVILEGES ON SPECIFIC CONSTRUCTOR METHOD FOO FROM ROLE_F031_19_10_13\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_14;\nREVOKE ALL PRIVILEGES ON SPECIFIC FUNCTION FOO FROM ROLE_F031_19_10_14\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_15;\nREVOKE ALL PRIVILEGES ON SPECIFIC INSTANCE METHOD FOO FROM ROLE_F031_19_10_15\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_16;\nREVOKE ALL PRIVILEGES ON SPECIFIC METHOD FOO FROM ROLE_F031_19_10_16\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_17;\nREVOKE ALL PRIVILEGES ON SPECIFIC PROCEDURE FOO FROM ROLE_F031_19_10_17\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_18;\nREVOKE ALL PRIVILEGES ON SPECIFIC ROUTINE FOO FROM ROLE_F031_19_10_18\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_19;\nREVOKE ALL PRIVILEGES ON SPECIFIC STATIC METHOD FOO FROM ROLE_F031_19_10_19\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_20;\nREVOKE ALL PRIVILEGES ON STATIC METHOD BAR FOR BAZ FROM ROLE_F031_19_10_20\n\nonlyif Postgres\nstatement ok\nCREATE ROLE ROLE_F031_19_10_21;\nREVOKE ALL PRIVILEGES ON STATIC METHOD BAR FROM ROLE_F031_19_10_21\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_01.slt",
    "content": "# F041-01: Inner join (but not necessarily the INNER keyword)\n\nstatement ok\nCREATE TABLE TABLE_F041_01_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_01_01_012 ( A INTEGER );\nSELECT TABLE_F041_01_01_011.A, TABLE_F041_01_01_012.A FROM TABLE_F041_01_01_011 JOIN TABLE_F041_01_01_012 ON TABLE_F041_01_01_011.A = TABLE_F041_01_01_012.A\n\nstatement ok\nCREATE TABLE TABLE_F041_01_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F041_01_01_022 ( A INTEGER );\nSELECT TABLE_F041_01_01_021.A, TABLE_F041_01_01_022.A FROM TABLE_F041_01_01_021 JOIN TABLE_F041_01_01_022 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_01_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F041_01_01_032 ( A INTEGER );\nSELECT TABLE_F041_01_01_031.A, TABLE_F041_01_01_032.A FROM TABLE_F041_01_01_031 JOIN TABLE_F041_01_01_032 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_02.slt",
    "content": "# F041-02: INNER keyword\n\nstatement ok\nCREATE TABLE TABLE_F041_02_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_02_01_012 ( A INTEGER );\nSELECT TABLE_F041_02_01_011.A, TABLE_F041_02_01_012.A FROM TABLE_F041_02_01_011 INNER JOIN TABLE_F041_02_01_012 ON TABLE_F041_02_01_011.A = TABLE_F041_02_01_012.A\n\nstatement ok\nCREATE TABLE TABLE_F041_02_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F041_02_01_022 ( A INTEGER );\nSELECT TABLE_F041_02_01_021.A, TABLE_F041_02_01_022.A FROM TABLE_F041_02_01_021 INNER JOIN TABLE_F041_02_01_022 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_02_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F041_02_01_032 ( A INTEGER );\nSELECT TABLE_F041_02_01_031.A, TABLE_F041_02_01_032.A FROM TABLE_F041_02_01_031 INNER JOIN TABLE_F041_02_01_032 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_03.slt",
    "content": "# F041-03: LEFT OUTER JOIN\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_012 ( A INTEGER );\nSELECT TABLE_F041_03_01_011.A, TABLE_F041_03_01_012.A FROM TABLE_F041_03_01_011 LEFT JOIN TABLE_F041_03_01_012 ON TABLE_F041_03_01_011.A = TABLE_F041_03_01_012.A\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_022 ( A INTEGER );\nSELECT TABLE_F041_03_01_021.A, TABLE_F041_03_01_022.A FROM TABLE_F041_03_01_021 LEFT JOIN TABLE_F041_03_01_022 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_032 ( A INTEGER );\nSELECT TABLE_F041_03_01_031.A, TABLE_F041_03_01_032.A FROM TABLE_F041_03_01_031 LEFT JOIN TABLE_F041_03_01_032 USING ( A ) AS FOO\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_041 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_042 ( A INTEGER );\nSELECT TABLE_F041_03_01_041.A, TABLE_F041_03_01_042.A FROM TABLE_F041_03_01_041 LEFT OUTER JOIN TABLE_F041_03_01_042 ON TABLE_F041_03_01_041.A = TABLE_F041_03_01_042.A\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_051 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_052 ( A INTEGER );\nSELECT TABLE_F041_03_01_051.A, TABLE_F041_03_01_052.A FROM TABLE_F041_03_01_051 LEFT OUTER JOIN TABLE_F041_03_01_052 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_03_01_061 ( A INTEGER );\nCREATE TABLE TABLE_F041_03_01_062 ( A INTEGER );\nSELECT TABLE_F041_03_01_061.A, TABLE_F041_03_01_062.A FROM TABLE_F041_03_01_061 LEFT OUTER JOIN TABLE_F041_03_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_04.slt",
    "content": "# F041-04: RIGHT OUTER JOIN\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_012 ( A INTEGER );\nSELECT TABLE_F041_04_01_011.A, TABLE_F041_04_01_012.A FROM TABLE_F041_04_01_011 RIGHT JOIN TABLE_F041_04_01_012 ON TABLE_F041_04_01_011.A = TABLE_F041_04_01_012.A\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_022 ( A INTEGER );\nSELECT TABLE_F041_04_01_021.A, TABLE_F041_04_01_022.A FROM TABLE_F041_04_01_021 RIGHT JOIN TABLE_F041_04_01_022 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_032 ( A INTEGER );\nSELECT TABLE_F041_04_01_031.A, TABLE_F041_04_01_032.A FROM TABLE_F041_04_01_031 RIGHT JOIN TABLE_F041_04_01_032 USING ( A ) AS FOO\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_041 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_042 ( A INTEGER );\nSELECT TABLE_F041_04_01_041.A, TABLE_F041_04_01_042.A FROM TABLE_F041_04_01_041 RIGHT OUTER JOIN TABLE_F041_04_01_042 ON TABLE_F041_04_01_041.A = TABLE_F041_04_01_042.A\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_051 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_052 ( A INTEGER );\nSELECT TABLE_F041_04_01_051.A, TABLE_F041_04_01_052.A FROM TABLE_F041_04_01_051 RIGHT OUTER JOIN TABLE_F041_04_01_052 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_04_01_061 ( A INTEGER );\nCREATE TABLE TABLE_F041_04_01_062 ( A INTEGER );\nSELECT TABLE_F041_04_01_061.A, TABLE_F041_04_01_062.A FROM TABLE_F041_04_01_061 RIGHT OUTER JOIN TABLE_F041_04_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_05.slt",
    "content": "# F041-05: Outer joins can be nested\n\nstatement ok\nCREATE TABLE TABLE_F041_05_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_05_01_012 ( A INTEGER );\nCREATE TABLE TABLE_F041_05_01_013 ( A INTEGER );\nSELECT TABLE_F041_05_01_011.A FROM TABLE_F041_05_01_011 LEFT JOIN TABLE_F041_05_01_012 ON TABLE_F041_05_01_011.A = TABLE_F041_05_01_012.A LEFT JOIN TABLE_F041_05_01_013 ON TABLE_F041_05_01_012.A = TABLE_F041_05_01_013.A\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_07.slt",
    "content": "# F041-07: The inner table in a left or right outer join can also be used in an inner join\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_011 ( A INTEGER );\nSELECT TABLE_F041_07_01_011.A FROM TABLE_F041_07_01_011 INNER JOIN TABLE_F041_07_01_011 AS TABLE_F041_07_01_012 ON TABLE_F041_07_01_011.A = TABLE_F041_07_01_012.A\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_021 ( A INTEGER );\nSELECT TABLE_F041_07_01_021.A FROM TABLE_F041_07_01_021 INNER JOIN TABLE_F041_07_01_021 AS TABLE_F041_07_01_022 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_031 ( A INTEGER );\nSELECT TABLE_F041_07_01_031.A FROM TABLE_F041_07_01_031 INNER JOIN TABLE_F041_07_01_031 AS TABLE_F041_07_01_032 USING ( A ) AS FOO\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_041 ( A INTEGER );\nSELECT TABLE_F041_07_01_041.A FROM TABLE_F041_07_01_041 JOIN TABLE_F041_07_01_041 AS TABLE_F041_07_01_042 ON TABLE_F041_07_01_041.A = TABLE_F041_07_01_042.A\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_051 ( A INTEGER );\nSELECT TABLE_F041_07_01_051.A FROM TABLE_F041_07_01_051 JOIN TABLE_F041_07_01_051 AS TABLE_F041_07_01_052 USING ( A )\n\nstatement ok\nCREATE TABLE TABLE_F041_07_01_061 ( A INTEGER );\nSELECT TABLE_F041_07_01_061.A FROM TABLE_F041_07_01_061 JOIN TABLE_F041_07_01_061 AS TABLE_F041_07_01_062 USING ( A ) AS FOO\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F041_08.slt",
    "content": "# F041-08: All comparison operators are supported (rather than just =)\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_012 ( B INTEGER );\nSELECT TABLE_F041_08_01_011.A, TABLE_F041_08_01_012.B FROM TABLE_F041_08_01_011 JOIN TABLE_F041_08_01_012 ON TABLE_F041_08_01_011.A < TABLE_F041_08_01_012.B\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_022 ( B INTEGER );\nSELECT TABLE_F041_08_01_021.A, TABLE_F041_08_01_022.B FROM TABLE_F041_08_01_021 JOIN TABLE_F041_08_01_022 ON TABLE_F041_08_01_021.A <= TABLE_F041_08_01_022.B\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_032 ( B INTEGER );\nSELECT TABLE_F041_08_01_031.A, TABLE_F041_08_01_032.B FROM TABLE_F041_08_01_031 JOIN TABLE_F041_08_01_032 ON TABLE_F041_08_01_031.A <> TABLE_F041_08_01_032.B\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_041 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_042 ( B INTEGER );\nSELECT TABLE_F041_08_01_041.A, TABLE_F041_08_01_042.B FROM TABLE_F041_08_01_041 JOIN TABLE_F041_08_01_042 ON TABLE_F041_08_01_041.A = TABLE_F041_08_01_042.B\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_051 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_052 ( B INTEGER );\nSELECT TABLE_F041_08_01_051.A, TABLE_F041_08_01_052.B FROM TABLE_F041_08_01_051 JOIN TABLE_F041_08_01_052 ON TABLE_F041_08_01_051.A > TABLE_F041_08_01_052.B\n\nstatement ok\nCREATE TABLE TABLE_F041_08_01_061 ( A INTEGER );\nCREATE TABLE TABLE_F041_08_01_062 ( B INTEGER );\nSELECT TABLE_F041_08_01_061.A, TABLE_F041_08_01_062.B FROM TABLE_F041_08_01_061 JOIN TABLE_F041_08_01_062 ON TABLE_F041_08_01_061.A >= TABLE_F041_08_01_062.B\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_01.slt",
    "content": "# F051-01: DATE data type (including support of DATE literal)\n\nstatement ok\nCREATE TABLE TABLE_F051_01_01_011 ( A DATE )\n\nquery T\nSELECT DATE '2016-03-26'\n----\n2016-03-26\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_02.slt",
    "content": "# F051-02: TIME data type (including support of TIME literal) with fractional seconds precision of at least 0\n\nstatement ok\nCREATE TABLE TABLE_F051_02_01_011 ( A TIME )\n\nquery T\nSELECT TIME '01:02:03'\n----\n01:02:03\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_03.slt",
    "content": "# F051-03: TIMESTAMP data type (including support of TIMESTAMP literal) with fractional seconds precision of at least 0 and 6\n\nstatement ok\nCREATE TABLE TABLE_F051_03_01_011 ( A TIMESTAMP )\n\nquery T\nSELECT TIMESTAMP '2016-03-26 01:02:03'\n----\n2016-03-25T20:02:03-05:00\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_04.slt",
    "content": "# F051-04: Comparison predicate on DATE, TIME, and TIMESTAMP data types\n\nquery B\nSELECT DATE '2016-03-26' < DATE '2016-03-26'\n----\n0\n\nquery B\nSELECT DATE '2016-03-26' <= DATE '2016-03-26'\n----\n1\n\nquery B\nSELECT DATE '2016-03-26' <> DATE '2016-03-26'\n----\n0\n\nquery B\nSELECT DATE '2016-03-26' = DATE '2016-03-26'\n----\n1\n\nquery B\nSELECT DATE '2016-03-26' > DATE '2016-03-26'\n----\n0\n\nquery B\nSELECT DATE '2016-03-26' >= DATE '2016-03-26'\n----\n1\n\nquery B\nSELECT TIME '01:02:03' < TIME '01:02:03'\n----\n0\n\nquery B\nSELECT TIME '01:02:03' <= TIME '01:02:03'\n----\n1\n\nquery B\nSELECT TIME '01:02:03' <> TIME '01:02:03'\n----\n0\n\nquery B\nSELECT TIME '01:02:03' = TIME '01:02:03'\n----\n1\n\nquery B\nSELECT TIME '01:02:03' > TIME '01:02:03'\n----\n0\n\nquery B\nSELECT TIME '01:02:03' >= TIME '01:02:03'\n----\n1\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' < TIMESTAMP '2016-03-26 01:02:03'\n----\n0\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' <= TIMESTAMP '2016-03-26 01:02:03'\n----\n1\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' <> TIMESTAMP '2016-03-26 01:02:03'\n----\n0\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' = TIMESTAMP '2016-03-26 01:02:03'\n----\n1\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' > TIMESTAMP '2016-03-26 01:02:03'\n----\n0\n\nquery B\nSELECT TIMESTAMP '2016-03-26 01:02:03' >= TIMESTAMP '2016-03-26 01:02:03'\n----\n1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_05.slt",
    "content": "# F051-05: Explicit CAST between datetime types and character string types\n\nquery T\nSELECT CAST ( '2016-03-26' AS DATE )\n----\n2016-03-26\n\nquery T\nSELECT CAST ( '01:02:03' AS TIME )\n----\n01:02:03\n\nquery T\nSELECT CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE )\n----\n2016-03-25T20:02:03-05:00\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26' AS DATE ) AS VARCHAR )\n----\n'2016-03-26'\n\nquery T\nSELECT CAST ( CAST ( '01:02:03' AS TIME ) AS VARCHAR )\n----\n'01:02:03'\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS VARCHAR )\n----\n'2016-03-26 01:02:03'\n\nquery T\nSELECT CAST ( CAST ( '01:02:03' AS TIME ) AS TIME )\n----\n01:02:03\n\n# (WRONG: issue 6) query I\n# SELECT CAST ( CAST ( '01:02:03' AS TIME ) AS TIMESTAMP )\n\n\nquery T\nSELECT CAST ( CAST ( '01:02:03' AS TIME ) AS VARCHAR )\n----\n'01:02:03'\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS DATE )\n----\n2016-03-26\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS TIME )\n----\n01:02:03\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS TIMESTAMP )\n----\n2016-03-25T20:02:03-05:00\n\nquery T\nSELECT CAST ( CAST ( '2016-03-26 01:02:03' AS TIMESTAMP WITHOUT TIME ZONE ) AS VARCHAR )\n----\n'2016-03-26 01:02:03'\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_06.slt",
    "content": "# F051-06: CURRENT_DATE\n\nquery B\nSELECT CURRENT_DATE = CURRENT_DATE\n----\n1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_07.slt",
    "content": "# F051-07: LOCALTIME\n\n# (REPLACED: issue 4)\nquery B\nSELECT CURRENT_TIMESTAMP = CURRENT_TIMESTAMP\n----\n1\n\n# (REPLACED: issue 4)\nquery B\nSELECT CURRENT_TIMESTAMP ( 0 ) = CURRENT_TIMESTAMP\n----\n0\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F051_08.slt",
    "content": "# F051-08: LOCALTIMESTAMP\n\nquery B\nSELECT CURRENT_TIMESTAMP = CURRENT_TIMESTAMP\n----\n1\n\nquery B\nSELECT CURRENT_TIMESTAMP ( 0 ) = CURRENT_TIMESTAMP\n----\n0\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F081.slt",
    "content": "# F081: UNION and EXCEPT in views\n\nstatement ok\nCREATE TABLE TABLE_F081_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F081_01_012 ( A INTEGER );\nCREATE VIEW VIEW_F081_01_01 AS SELECT A FROM TABLE_F081_01_011 EXCEPT SELECT A FROM TABLE_F081_01_012\n\nstatement ok\nCREATE TABLE TABLE_F081_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F081_01_022 ( A INTEGER );\nCREATE VIEW VIEW_F081_01_02 AS SELECT A FROM TABLE_F081_01_021 UNION ALL SELECT A FROM TABLE_F081_01_022\n\nstatement ok\nCREATE TABLE TABLE_F081_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F081_01_032 ( A INTEGER );\nCREATE VIEW VIEW_F081_01_03 AS SELECT A FROM TABLE_F081_01_031 UNION SELECT A FROM TABLE_F081_01_032\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F131_01.slt",
    "content": "# F131-01: WHERE, GROUP BY, and HAVING clauses supported in queries with grouped views\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_01 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_01 AS SELECT A FROM TABLE_F131_01_01_01 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_01\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_02 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_02 AS SELECT A FROM TABLE_F131_01_01_02 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_02 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_03 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_03 AS SELECT A FROM TABLE_F131_01_01_03 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_03 GROUP BY A HAVING A = 2\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_04 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_04 AS SELECT A FROM TABLE_F131_01_01_04 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_04 WHERE A = 1\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_05 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_05 AS SELECT A FROM TABLE_F131_01_01_05 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_05 WHERE A = 1 GROUP BY A\n\nstatement ok\nCREATE TABLE TABLE_F131_01_01_06 ( A INTEGER );\nCREATE VIEW VIEW_F131_01_01_06 AS SELECT A FROM TABLE_F131_01_01_06 GROUP BY A;\nSELECT A FROM VIEW_F131_01_01_06 WHERE A = 1 GROUP BY A HAVING A = 2\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F131_02.slt",
    "content": "# F131-02: Multiple tables supported in queries with grouped views\n\nstatement ok\nCREATE TABLE TABLE_F131_02_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F131_02_01_012 ( A INTEGER );\nCREATE VIEW VIEW_F131_02_01_01 AS SELECT A FROM TABLE_F131_02_01_011 GROUP BY A;\nSELECT A FROM VIEW_F131_02_01_01 JOIN TABLE_F131_02_01_012 USING ( A )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F131_03.slt",
    "content": "# F131-03: Set functions supported in queries with grouped views\n\nstatement ok\nCREATE TABLE TABLE_F131_03_01_011 ( A INTEGER, B INTEGER );\nCREATE VIEW VIEW_F131_03_01_01 AS SELECT A, MIN ( B ) AS C FROM TABLE_F131_03_01_011 GROUP BY A;\nSELECT SUM ( C ) FROM VIEW_F131_03_01_01\n\nstatement ok\nCREATE TABLE TABLE_F131_03_01_021 ( A INTEGER, B INTEGER );\nCREATE VIEW VIEW_F131_03_01_02 AS SELECT A, MIN ( B ) AS C FROM TABLE_F131_03_01_021 GROUP BY A;\nSELECT SUM ( C ) FROM VIEW_F131_03_01_02 GROUP BY A\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F131_04.slt",
    "content": "# F131-04: Subqueries with GROUP BY and HAVING clauses and grouped views\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_011 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_012 ( B INTEGER );\nSELECT B < ( SELECT MAX ( A ) FROM TABLE_F131_04_01_011 GROUP BY A ) FROM TABLE_F131_04_01_012\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_021 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_022 ( B INTEGER );\nSELECT B < ( SELECT MAX ( A ) FROM TABLE_F131_04_01_021 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_022\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_031 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_032 ( B INTEGER );\nSELECT B <= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_031 GROUP BY A ) FROM TABLE_F131_04_01_032\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_041 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_042 ( B INTEGER );\nSELECT B <= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_041 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_042\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_051 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_052 ( B INTEGER );\nSELECT B <> ( SELECT MAX ( A ) FROM TABLE_F131_04_01_051 GROUP BY A ) FROM TABLE_F131_04_01_052\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_061 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_062 ( B INTEGER );\nSELECT B <> ( SELECT MAX ( A ) FROM TABLE_F131_04_01_061 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_062\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_071 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_072 ( B INTEGER );\nSELECT B = ( SELECT MAX ( A ) FROM TABLE_F131_04_01_071 GROUP BY A ) FROM TABLE_F131_04_01_072\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_081 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_082 ( B INTEGER );\nSELECT B = ( SELECT MAX ( A ) FROM TABLE_F131_04_01_081 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_082\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_091 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_092 ( B INTEGER );\nSELECT B > ( SELECT MAX ( A ) FROM TABLE_F131_04_01_091 GROUP BY A ) FROM TABLE_F131_04_01_092\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_101 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_102 ( B INTEGER );\nSELECT B > ( SELECT MAX ( A ) FROM TABLE_F131_04_01_101 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_102\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_111 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_112 ( B INTEGER );\nSELECT B >= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_111 GROUP BY A ) FROM TABLE_F131_04_01_112\n\nstatement ok\nCREATE TABLE TABLE_F131_04_01_121 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_01_122 ( B INTEGER );\nSELECT B >= ( SELECT MAX ( A ) FROM TABLE_F131_04_01_121 GROUP BY A HAVING A = 5 ) FROM TABLE_F131_04_01_122\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_011 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_012 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_01 AS SELECT A FROM TABLE_F131_04_02_011 GROUP BY A;\nSELECT B < ( SELECT MAX ( A ) FROM VIEW_F131_04_02_01 ) FROM TABLE_F131_04_02_012\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_021 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_022 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_02 AS SELECT A FROM TABLE_F131_04_02_021 GROUP BY A;\nSELECT B <= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_02 ) FROM TABLE_F131_04_02_022\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_031 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_032 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_03 AS SELECT A FROM TABLE_F131_04_02_031 GROUP BY A;\nSELECT B <> ( SELECT MAX ( A ) FROM VIEW_F131_04_02_03 ) FROM TABLE_F131_04_02_032\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_041 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_042 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_04 AS SELECT A FROM TABLE_F131_04_02_041 GROUP BY A;\nSELECT B = ( SELECT MAX ( A ) FROM VIEW_F131_04_02_04 ) FROM TABLE_F131_04_02_042\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_051 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_052 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_05 AS SELECT A FROM TABLE_F131_04_02_051 GROUP BY A;\nSELECT B > ( SELECT MAX ( A ) FROM VIEW_F131_04_02_05 ) FROM TABLE_F131_04_02_052\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_061 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_062 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_06 AS SELECT A FROM TABLE_F131_04_02_061 GROUP BY A;\nSELECT B >= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_06 ) FROM TABLE_F131_04_02_062\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_071 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_072 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_07 AS SELECT A FROM TABLE_F131_04_02_071 GROUP BY A HAVING A = 5;\nSELECT B < ( SELECT MAX ( A ) FROM VIEW_F131_04_02_07 ) FROM TABLE_F131_04_02_072\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_081 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_082 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_08 AS SELECT A FROM TABLE_F131_04_02_081 GROUP BY A HAVING A = 5;\nSELECT B <= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_08 ) FROM TABLE_F131_04_02_082\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_091 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_092 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_09 AS SELECT A FROM TABLE_F131_04_02_091 GROUP BY A HAVING A = 5;\nSELECT B <> ( SELECT MAX ( A ) FROM VIEW_F131_04_02_09 ) FROM TABLE_F131_04_02_092\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_101 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_102 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_10 AS SELECT A FROM TABLE_F131_04_02_101 GROUP BY A HAVING A = 5;\nSELECT B = ( SELECT MAX ( A ) FROM VIEW_F131_04_02_10 ) FROM TABLE_F131_04_02_102\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_111 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_112 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_11 AS SELECT A FROM TABLE_F131_04_02_111 GROUP BY A HAVING A = 5;\nSELECT B > ( SELECT MAX ( A ) FROM VIEW_F131_04_02_11 ) FROM TABLE_F131_04_02_112\n\nstatement ok\nCREATE TABLE TABLE_F131_04_02_121 ( A INTEGER );\nCREATE TABLE TABLE_F131_04_02_122 ( B INTEGER );\nCREATE VIEW VIEW_F131_04_02_12 AS SELECT A FROM TABLE_F131_04_02_121 GROUP BY A HAVING A = 5;\nSELECT B >= ( SELECT MAX ( A ) FROM VIEW_F131_04_02_12 ) FROM TABLE_F131_04_02_122\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F221.slt",
    "content": "# F221: Explicit defaults\n\nstatement ok\nCREATE TABLE TABLE_F221_01_011 ( A INTEGER DEFAULT 123 );\nINSERT INTO TABLE_F221_01_011 ( A ) VALUES ( DEFAULT )\n\nstatement ok\nCREATE TABLE TABLE_F221_02_011 ( A INTEGER DEFAULT 123 );\nUPDATE TABLE_F221_02_011 SET A = DEFAULT\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F261_01.slt",
    "content": "# F261-01: Simple CASE\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN 1 ELSE 1 END\n\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN 1 ELSE NULL END\n\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN 1 END\n\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN NULL ELSE 1 END\n\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN NULL ELSE NULL END\n\n\n# (UNSUPPORTED: issue 5) query I\n# SELECT CASE 0 WHEN 2 , 2 THEN NULL END\n\n\nquery I\nSELECT CASE 0 WHEN 2 THEN 1 ELSE 1 END\n----\n1\n\nquery I\nSELECT CASE 0 WHEN 2 THEN 1 ELSE NULL END\n----\nnull\n\nquery I\nSELECT CASE 0 WHEN 2 THEN 1 END\n----\nnull\n\nquery I\nSELECT CASE 0 WHEN 2 THEN NULL ELSE 1 END\n----\n1\n\nquery T\nSELECT CASE 0 WHEN 2 THEN NULL ELSE NULL END\n----\nnull\n\nquery T\nSELECT CASE 0 WHEN 2 THEN NULL END\n----\nnull\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F261_02.slt",
    "content": "# F261-02: Searched CASE\n\nquery I\nSELECT CASE WHEN 0 = 1 THEN 1 ELSE 1 END\n----\n1\n\nquery I\nSELECT CASE WHEN 0 = 1 THEN 1 ELSE NULL END\n----\nnull\n\nquery I\nSELECT CASE WHEN 0 = 1 THEN 1 END\n----\nnull\n\nquery I\nSELECT CASE WHEN 0 = 1 THEN NULL ELSE 1 END\n----\n1\n\nquery T\nSELECT CASE WHEN 0 = 1 THEN NULL ELSE NULL END\n----\nnull\n\nquery T\nSELECT CASE WHEN 0 = 1 THEN NULL END\n----\nnull\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F261_03.slt",
    "content": "# F261-03: NULLIF\n\nquery I\nSELECT NULLIF ( 1 , 1 )\n----\nnull\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F261_04.slt",
    "content": "# F261-04: COALESCE\n\nquery I\nSELECT COALESCE ( 1 , 1 )\n----\n1\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F311_01.slt",
    "content": "# F311-01: CREATE SCHEMA\n\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_F311_01_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F311_02.slt",
    "content": "# F311-02: CREATE TABLE for persistent base tables\n\nonlyif Postgres\nstatement ok\nCREATE SCHEMA TABLE_F311_02_01_01 CREATE TABLE TABLE_F311_02_01_012 ( A INT )\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F311_03.slt",
    "content": "# F311-03: CREATE VIEW\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_03_01_01 ( A INTEGER );\nCREATE SCHEMA TABLE_F311_03_01_012 CREATE VIEW TABLE_F311_03_01_013 AS SELECT A FROM TABLE_F311_03_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F311_04.slt",
    "content": "# F311-04: CREATE VIEW: WITH CHECK OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_04_01_01 ( A INTEGER );\nCREATE SCHEMA TABLE_F311_04_01_012 CREATE VIEW TABLE_F311_04_01_013 AS SELECT A FROM TABLE_F311_04_01_01 WITH CASCADED CHECK OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_04_01_02 ( A INTEGER );\nCREATE SCHEMA TABLE_F311_04_01_022 CREATE VIEW TABLE_F311_04_01_023 AS SELECT A FROM TABLE_F311_04_01_02 WITH CHECK OPTION\n\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_04_01_03 ( A INTEGER );\nCREATE SCHEMA TABLE_F311_04_01_032 CREATE VIEW TABLE_F311_04_01_033 AS SELECT A FROM TABLE_F311_04_01_03 WITH LOCAL CHECK OPTION\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F311_05.slt",
    "content": "# F311-05: GRANT statement\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_05_01_01 ( A INTEGER );\nCREATE ROLE ROLE_F311_05_01_01;\nCREATE SCHEMA TABLE_F311_05_01_012 GRANT SELECT ON TABLE TABLE_F311_05_01_01 TO ROLE_F311_05_01_01\n\nonlyif Postgres\nonlyif Postgres\nstatement ok\nCREATE TABLE TABLE_F311_05_01_02 ( A INTEGER );\nCREATE ROLE ROLE_F311_05_01_02;\nCREATE SCHEMA TABLE_F311_05_01_022 GRANT SELECT ON TABLE_F311_05_01_02 TO ROLE_F311_05_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F471.slt",
    "content": "# F471: Scalar subquery values\n\nstatement ok\nCREATE TABLE TABLE_F471_01_01 ( A INTEGER );\nINSERT INTO TABLE_F471_01_01 ( A ) VALUES ( 1 );\nSELECT ( SELECT A FROM TABLE_F471_01_01 ) FROM TABLE_F471_01_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/F481.slt",
    "content": "# F481: Expanded NULL predicate\n\nstatement ok\nCREATE TABLE TABLE_F481_01_01 ( A INTEGER );\nSELECT A + A IS NOT NULL FROM TABLE_F481_01_01\n\nstatement ok\nCREATE TABLE TABLE_F481_01_02 ( A INTEGER );\nSELECT A + A IS NULL FROM TABLE_F481_01_02\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/S011.slt",
    "content": "# S011: Distinct data types\n\nonlyif Postgres\nstatement ok\nCREATE TYPE TABLE_S011_01_01\n\nonlyif Postgres\nstatement ok\nCREATE TYPE TABLE_S011_01_02 AS INT\n\nonlyif Postgres\nstatement ok\nCREATE TYPE TABLE_S011_02_01 AS INT;\nDROP TYPE TABLE_S011_02_01\n"
  },
  {
    "path": "crates/sqltest/test/sql_2016/T631.slt",
    "content": "# T631: IN predicate with one list element\n\nstatement ok\nCREATE TABLE TABLE_T631_01_01 ( A INTEGER );\nINSERT INTO TABLE_T631_01_01 ( A ) VALUES ( 1 );\nSELECT A FROM TABLE_T631_01_01 WHERE A IN ( 1 )\n"
  },
  {
    "path": "crates/sqltest/test/tutorial.slt",
    "content": "# Original doc of the dialect at https://www.sqlite.org/sqllogictest/doc/trunk/about.wiki\n\n# The purpose of this test suite is to validate the logic behind the evaluation of SQL statements,\n# not the ability to handle extreme values, check performance, etc.\n# Use a small range of values: small integers, short strings, and floating point numbers that use only the most significant bits of an a 32-bit IEEE float.\n\n# Typically you start creating the schemas.\n\n# A statetment that must execute end in `ok`.\nstatement ok\ncreate table t(v1 int not null, v2 int not null, v3 int not null)\n\n\nstatement ok\ninsert into t values(1,4,2), (2,3,3), (3,4,4), (4,3,5)\n\n# A statetment that must fail end in `error`\nstatement error\ninsert into failure\n\n# Ensure that the statement errors and that the error\n# message contains 'Multiple object drop not supported'\nonlyif sqlite\nstatement error DROP VIEW foo, bar\nDROP VIEW foo, bar;\n\n# Ensure that the statement errors and that the error\n# message contains 'DROP VIEW foo, bar'\nonlyif sqlite\nstatement error DROP VIEW foo, bar\nDROP VIEW foo, bar;\n\n\n# Then you run queries. Use `----` to separate the output\n\n# `III` means `3 columns of INTEGER`. So `I` is the datatype.\n# The options are\n#  T: Text\n#  I: I32\n#  R: F32\n# `rowsort` do sorting in the test suite\n# to account for the fact SQL have not intrinsic ordering\nquery III rowsort\nselect * from t\n----\n1\t4\t2\n2\t3\t3\n3\t4\t4\n4\t3\t5\n\n# You can implement execution that is conditional to the DB engine (sqlite, spacetime) using:\n\n# .. execute in all engines except\nskipif postgresql\nquery I\nselect v1 from t WHERE v1 = 1\n----\n1\n\n# .. execute only on the specified engine\nonlyif sqlite\nquery I\nselect v1 from t WHERE v1 = 1\n----\n    1\n\nstatement ok\ndrop table t\n"
  },
  {
    "path": "crates/standalone/.gitignore",
    "content": "flamegraphs\n"
  },
  {
    "path": "crates/standalone/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-standalone\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"An executable for running a single SpacetimeDB standalone instance\"\nrust-version.workspace = true\n\n[[bin]]\nname = \"spacetimedb-standalone\"   # The name of the target.\npath = \"src/main.rs\"   # The source file of the target.\ntest = true            # Is tested by default.\nbench = false          # Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options\ndoc = false            # Is documented by default.\nproc-macro = false     # Set to `true` for a proc-macro library.\nharness = true         # Use libtest harness.\nrequired-features = [] # Features required to build this target (N/A for lib)\n\n[features]\nunstable = [\"spacetimedb-client-api/unstable\"]\nallow_loopback_http_for_tests = [\"spacetimedb-core/allow_loopback_http_for_tests\"]\n# Perfmaps for profiling modules\nperfmap = [\"spacetimedb-core/perfmap\"]\n# Disables core pinning\nno-core-pinning = [\"spacetimedb-core/no-core-pinning\"]\nno-job-core-pinning = [\"spacetimedb-core/no-job-core-pinning\"]\n\n[dependencies]\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-client-api.workspace = true\nspacetimedb-core.workspace = true\nspacetimedb-datastore.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-pg.workspace = true\nspacetimedb-table.workspace = true\nspacetimedb-schema.workspace = true\n\nanyhow.workspace = true\nasync-trait.workspace = true\naxum.workspace = true\nclap = { workspace = true, features = [\"derive\", \"string\"] }\ndirs.workspace = true\nfutures.workspace = true\nhostname.workspace = true\nhttp.workspace = true\nlog.workspace = true\nnetstat2.workspace = true\nopenssl.workspace = true\nparse-size.workspace = true\nprometheus.workspace = true\nscopeguard.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nsled.workspace = true\nsocket2.workspace = true\nthiserror.workspace = true\ntokio.workspace = true\ntower-http.workspace = true\ntoml.workspace = true\ntracing = { workspace = true, features = [\"release_max_level_debug\"] }\n\n[target.'cfg(not(target_env = \"msvc\"))'.dependencies]\ntikv-jemallocator = {workspace = true}\ntikv-jemalloc-ctl = {workspace = true}\n\n[dev-dependencies]\nonce_cell.workspace = true\ntempfile.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/standalone/Dockerfile",
    "content": "ARG CARGO_PROFILE=release\n\n\nFROM rust:1.93.0 AS chef\nRUN rust_target=$(rustc -vV | awk '/^host:/{ print $2 }') && \\\n  curl https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-$rust_target.tgz -fL | tar xz -C $CARGO_HOME/bin\nRUN cargo binstall -y cargo-chef@0.1.70\nWORKDIR /usr/src/app\n\nFROM chef AS planner\nCOPY . .\nRUN cargo chef prepare --recipe-path recipe.json\n\nFROM chef AS builder\n\nRUN cargo binstall -y cargo-watch@8.4.0\nRUN cargo binstall -y flamegraph@0.6.2\n\nCOPY --from=planner /usr/src/app/recipe.json .\n\nENV CARGO_INCREMENTAL=0\n\nARG CARGO_PROFILE=release\n\nRUN cargo chef cook -p spacetimedb-standalone --profile=${CARGO_PROFILE}\n\nCOPY . .\nRUN cargo build -p spacetimedb-standalone --profile=${CARGO_PROFILE} --locked\n\nFROM builder as env-dev\nRUN mkdir -p /stdb/data && ln -s /usr/src/app/crates/standalone/config.toml /stdb/data/config.toml\nENV PATH=\"/usr/src/app/target/debug:${PATH}\"\n\nFROM debian as env-release\nRUN apt-get update && \\\n    apt-get install -y ca-certificates libssl-dev && \\\n    rm -rf /var/lib/apt/lists/*\nCOPY --from=builder /usr/src/app/target/release/spacetimedb-standalone /usr/local/bin/\nCOPY --from=builder /usr/src/app/crates/standalone/config.toml /stdb/data/config.toml\n\nFROM env-${CARGO_PROFILE}\n\nEXPOSE 3000\n\nENV RUST_BACKTRACE=1\nENTRYPOINT [\"spacetimedb-standalone\"]\nCMD [\"start\", \"--data-dir=/stdb/data\", \"--jwt-pub-key-path=/etc/spacetimedb/id_ecdsa.pub\", \"--jwt-priv-key-path=/etc/spacetimedb/id_ecdsa\"]\n"
  },
  {
    "path": "crates/standalone/README.md",
    "content": "> ⚠️ **Unstable Crate** ⚠️\n>\n> The interface of this crate is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/standalone/config.toml",
    "content": "# [certificate-authority]\n# jwt-priv-key-path = \"~/.config/spacetime/id_ecdsa\"\n# jwt-pub-key-path = \"~/.config/spacetime/id_ecdsa.pub\"\n\n[logs]\n# The default level filter for logging\n# level = \"ERROR\"\n\n# directives for logging, see link for syntax:\n# https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html#directives\ndirectives = [\n    \"spacetimedb=debug\",\n    \"spacetimedb_client_api=debug\",\n    \"spacetimedb_lib=debug\",\n    \"spacetimedb_standalone=debug\",\n    \"spacetimedb_commitlog=info\",\n    \"spacetimedb_durability=info\",\n    \"axum::rejection=trace\",\n]\n\n# vim: set nowritebackup: << otherwise triggers cargo-watch\n"
  },
  {
    "path": "crates/standalone/src/control_db/tests.rs",
    "content": "use std::str::FromStr;\n\nuse once_cell::sync::Lazy;\nuse spacetimedb::messages::control_db::HostType;\nuse spacetimedb_client_api::auth::LOCALHOST;\nuse spacetimedb_lib::error::ResultTest;\nuse spacetimedb_lib::Hash;\nuse tempfile::TempDir;\n\nuse super::*;\n\nstatic ALICE: Lazy<Identity> = Lazy::new(|| Identity::from_claims(LOCALHOST, \"alice\"));\nstatic BOB: Lazy<Identity> = Lazy::new(|| Identity::from_claims(LOCALHOST, \"bob\"));\n\n#[test]\nfn test_register_tld() -> anyhow::Result<()> {\n    let tmp = TempDir::with_prefix(\"register-tld\")?;\n\n    let domain: DomainName = \"amaze\".parse()?;\n    let cdb = ControlDb::at(tmp.path())?;\n\n    cdb.spacetime_register_tld(domain.to_tld(), *ALICE)?;\n    let owner = cdb.spacetime_lookup_tld(domain.tld())?;\n    assert_eq!(owner, Some(*ALICE));\n\n    let unauthorized = cdb.spacetime_register_tld(domain.to_tld(), *BOB)?;\n    assert!(matches!(unauthorized, RegisterTldResult::Unauthorized { .. }));\n    let already_registered = cdb.spacetime_register_tld(domain.to_tld(), *ALICE)?;\n    assert!(matches!(\n        already_registered,\n        RegisterTldResult::AlreadyRegistered { .. }\n    ));\n    let domain = DomainName::from_str(\"amAZe\")?;\n    let already_registered = cdb.spacetime_register_tld(domain.to_tld(), *ALICE)?;\n    assert!(matches!(\n        already_registered,\n        RegisterTldResult::AlreadyRegistered { .. }\n    ));\n    let _ = tmp.close().ok(); // force tmp to not be dropped until here\n\n    Ok(())\n}\n\n#[test]\nfn test_domain() -> anyhow::Result<()> {\n    let tmp = TempDir::with_prefix(\"insert-domain\")?;\n    let domain: DomainName = \"this/hASmiXed/case\".parse()?;\n    let domain_lower: DomainName = domain.to_lowercase().parse()?;\n\n    let cdb = ControlDb::at(tmp.path())?;\n\n    let addr = Identity::ZERO;\n    let res = cdb.spacetime_insert_domain(&addr, domain.clone(), *ALICE, true)?;\n    assert!(matches!(res, InsertDomainResult::Success { .. }));\n\n    // Check Alice owns TLD\n    let unauthorized = cdb\n        .spacetime_insert_domain(&addr, \"this/is/bob\".parse()?, *BOB, true)\n        .unwrap();\n    assert!(matches!(unauthorized, InsertDomainResult::PermissionDenied { .. }));\n\n    let already_registered = cdb.spacetime_insert_domain(&addr, domain.clone(), *ALICE, true);\n    assert!(matches!(already_registered, Err(Error::RecordAlreadyExists(_))));\n    // Cannot register lowercase\n    let already_registered = cdb.spacetime_insert_domain(&addr, domain_lower.clone(), *ALICE, true);\n    assert!(matches!(already_registered, Err(Error::RecordAlreadyExists(_))));\n\n    let tld_owner = cdb.spacetime_lookup_tld(domain.tld())?;\n    assert_eq!(tld_owner, Some(*ALICE));\n\n    let registered_addr = cdb.spacetime_dns(domain.as_ref())?;\n    assert_eq!(registered_addr, Some(addr));\n\n    // Try lowercase, too\n    let registered_addr = cdb.spacetime_dns(domain_lower.as_ref())?;\n    assert_eq!(registered_addr, Some(addr));\n\n    // Reverse should yield the original domain (in mixed-case)\n    let reverse_lookup = cdb.spacetime_reverse_dns(&addr)?;\n    assert_eq!(\n        reverse_lookup.first().map(ToString::to_string),\n        Some(domain.to_string())\n    );\n    assert_eq!(reverse_lookup, vec![domain.clone()]);\n\n    // We can remove the domain records for Alice's database\n    let deleted = cdb.spacetime_replace_domains(&addr, &ALICE, &[]);\n    assert!(matches!(deleted, Ok(SetDomainsResult::Success)));\n\n    // The domain records are gone\n    let registered_addr = cdb.spacetime_dns(domain.as_ref())?;\n    assert_eq!(registered_addr, None);\n\n    // Reverse DNS should yield empty\n    let reverse_lookup = cdb.spacetime_reverse_dns(&addr)?;\n    assert_eq!(reverse_lookup, vec![]);\n\n    // Bob cannot register the TLD\n    let unauthorized = cdb\n        .spacetime_insert_domain(&addr, \"this/is/bob\".parse()?, *BOB, true)\n        .unwrap();\n    assert!(matches!(unauthorized, InsertDomainResult::PermissionDenied { .. }));\n\n    // Alice can add the domain back\n    let addr = Identity::ZERO;\n    let res = cdb.spacetime_insert_domain(&addr, domain.clone(), *ALICE, true)?;\n    assert!(matches!(res, InsertDomainResult::Success { .. }));\n\n    let _ = tmp.close().ok(); // force tmp to not be dropped until here\n\n    Ok(())\n}\n\n#[test]\nfn test_decode() -> ResultTest<()> {\n    let path = TempDir::with_prefix(\"decode\")?;\n\n    let cdb = ControlDb::at(path)?;\n\n    // TODO: Use a random identity.\n    let id = Identity::ZERO;\n\n    let db = Database {\n        id: 0,\n        database_identity: Default::default(),\n        owner_identity: id,\n        host_type: HostType::Wasm,\n        initial_program: Hash::ZERO,\n    };\n\n    cdb.insert_database(db.clone())?;\n\n    let dbs = cdb.get_databases()?;\n\n    assert_eq!(dbs.len(), 1);\n    assert_eq!(dbs[0].owner_identity, id);\n\n    let new_replica = Replica {\n        id: 0,\n        database_id: 1,\n        node_id: 0,\n        leader: true,\n    };\n\n    let id = cdb.insert_replica(new_replica)?;\n\n    let dbs = cdb.get_replicas()?;\n\n    assert_eq!(dbs.len(), 1);\n    assert_eq!(dbs[0].id, id);\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/standalone/src/control_db.rs",
    "content": "use anyhow::Context;\nuse sled::transaction::{\n    self, ConflictableTransactionError, ConflictableTransactionResult, TransactionError, TransactionResult,\n    Transactional, TransactionalTree,\n};\nuse spacetimedb::energy;\nuse spacetimedb::identity::Identity;\nuse spacetimedb::messages::control_db::{Database, EnergyBalance, Node, Replica};\n\nuse spacetimedb_client_api_messages::name::{\n    DomainName, DomainParsingError, InsertDomainResult, RegisterTldResult, SetDomainsResult, Tld, TldRef,\n};\nuse spacetimedb_lib::bsatn;\nuse spacetimedb_paths::standalone::ControlDbDir;\n\n#[cfg(test)]\nmod tests;\n\n/// A control database when SpacetimeDB is running standalone.\n///\n/// Important note: The `ConnectionId`s and `Identity`s stored in this database\n/// are stored as *LITTLE-ENDIAN* byte arrays. This means that printing such an array\n/// in hexadecimal will result in the REVERSE of the standard way to print `ConnectionId`s and `Identity`s.\n#[derive(Clone)]\npub struct ControlDb {\n    db: sled::Db,\n}\n\npub type Result<T> = core::result::Result<T, Error>;\n\n#[derive(thiserror::Error, Debug)]\npub enum Error {\n    #[error(\"collection not found: {0}\")]\n    CollectionNotFound(sled::Error),\n    #[error(\"database error: {0}\")]\n    Database(sled::Error),\n    #[error(\"record with the name {0} already exists\")]\n    RecordAlreadyExists(DomainName),\n    #[error(\"database with identity {0} already exists\")]\n    DatabaseAlreadyExists(Identity),\n    #[error(\"database with identity {0} does not exist\")]\n    DatabaseNotFound(Identity),\n    #[error(\"failed to register {0} domain\")]\n    DomainRegistrationFailure(DomainName),\n    #[error(\"failed to decode data\")]\n    Decoding(#[from] bsatn::DecodeError),\n    #[error(\"failed to encode data\")]\n    Encoding(#[from] bsatn::EncodeError),\n    #[error(transparent)]\n    DomainParsing(#[from] DomainParsingError),\n    #[error(transparent)]\n    Json(#[from] serde_json::Error),\n    #[error(transparent)]\n    Task(#[from] tokio::task::JoinError),\n    #[error(transparent)]\n    Other(#[from] anyhow::Error),\n}\n\nimpl From<sled::Error> for Error {\n    fn from(err: sled::Error) -> Self {\n        match err {\n            sled::Error::CollectionNotFound(_) => Error::CollectionNotFound(err),\n            err => Error::Database(err),\n        }\n    }\n}\n\nimpl ControlDb {\n    pub fn new(path: &ControlDbDir) -> Result<Self> {\n        let config = sled::Config::default()\n            .path(path)\n            .flush_every_ms(Some(50))\n            .mode(sled::Mode::HighThroughput);\n        let db = config.open()?;\n        Ok(Self { db })\n    }\n\n    #[cfg(test)]\n    pub fn at(path: impl AsRef<std::path::Path>) -> Result<Self> {\n        let config = sled::Config::default()\n            .path(path.as_ref())\n            .flush_every_ms(Some(50))\n            .mode(sled::Mode::HighThroughput);\n        let db = config.open()?;\n        Ok(Self { db })\n    }\n}\n\n/// A helper to convert a `sled::IVec` into an `Identity`.\n/// This expects the identity to be in LITTLE_ENDIAN format.\n/// This fails if the `sled::IVec` is not 32 bytes long.\nfn identity_from_le_ivec(ivec: &sled::IVec) -> Result<Identity> {\n    let identity_bytes: [u8; 32] = ivec\n        .as_ref()\n        .try_into()\n        .map_err(|_| anyhow::anyhow!(\"invalid size for identity: {}\", ivec.len()))?;\n    Ok(Identity::from_byte_array(identity_bytes))\n}\n\nimpl ControlDb {\n    pub fn spacetime_dns(&self, domain: &str) -> Result<Option<Identity>> {\n        let tree = self.db.open_tree(\"dns\")?;\n        let value = tree.get(domain.to_lowercase().as_bytes())?;\n        if let Some(value) = value {\n            return Ok(Some(identity_from_le_ivec(&value)?));\n        }\n        Ok(None)\n    }\n\n    pub fn spacetime_reverse_dns(&self, database_identity: &Identity) -> Result<Vec<DomainName>> {\n        let tree = self.db.open_tree(\"reverse_dns\")?;\n        let value = tree.get(database_identity.to_byte_array())?;\n        if let Some(value) = value {\n            let vec: Vec<DomainName> = serde_json::from_slice(&value[..])?;\n            return Ok(vec);\n        }\n        Ok(vec![])\n    }\n\n    /// Creates a new domain which points to the database identity. For example:\n    ///  * `my_domain/my_database`\n    ///  * `my_company/my_team/my_product`\n    ///\n    /// A TLD itself is also a fully qualified database name:\n    ///  * `clockworklabs`\n    ///  * `bitcraft`\n    ///  * `...`\n    ///\n    /// # Arguments\n    ///  * `database_identity` - The identity the database name should point to\n    ///  * `database_name` - The database name to register\n    ///  * `owner_identity` - The identity that is publishing the database name\n    pub fn spacetime_insert_domain(\n        &self,\n        database_identity: &Identity,\n        domain: DomainName,\n        owner_identity: Identity,\n        try_register_tld: bool,\n    ) -> Result<InsertDomainResult> {\n        let database_identity = *database_identity;\n        if self.spacetime_dns(domain.as_ref())?.is_some() {\n            return Err(Error::RecordAlreadyExists(domain));\n        }\n        let tld = domain.tld();\n        match self.spacetime_lookup_tld(tld)? {\n            Some(owner) => {\n                if owner != owner_identity {\n                    return Ok(InsertDomainResult::PermissionDenied { domain });\n                }\n            }\n            None => {\n                if try_register_tld {\n                    // Let's try to automatically register this TLD for the identity\n                    let result = self.spacetime_register_tld(tld.to_owned(), owner_identity)?;\n                    if let RegisterTldResult::Success { .. } = result {\n                        // This identity now owns this TLD\n                    } else {\n                        // This is technically possibly due to race conditions\n                        return Err(Error::DomainRegistrationFailure(domain));\n                    }\n                } else {\n                    return Ok(InsertDomainResult::TldNotRegistered { domain });\n                }\n            }\n        }\n\n        let identity_bytes = database_identity.to_byte_array();\n        let tree = self.db.open_tree(\"dns\")?;\n        tree.insert(domain.to_lowercase(), &identity_bytes)?;\n\n        let tree = self.db.open_tree(\"reverse_dns\")?;\n        match tree.get(identity_bytes)? {\n            Some(value) => {\n                let mut vec: Vec<DomainName> = serde_json::from_slice(&value[..])?;\n                vec.push(domain.clone());\n                tree.insert(identity_bytes, serde_json::to_string(&vec)?.as_bytes())?;\n            }\n            None => {\n                tree.insert(identity_bytes, serde_json::to_string(&vec![&domain])?.as_bytes())?;\n            }\n        }\n\n        Ok(InsertDomainResult::Success {\n            domain,\n            database_identity,\n        })\n    }\n\n    /// Replace all domains pointing to `database_identity` with `domain_names`.\n    ///\n    /// That is, delete all existing names pointing to `database_identity`, then\n    /// create all `domain_names`, pointing to `database_identity`.\n    ///\n    /// All existing names in the database and in `domain_names` must be\n    /// owned by `owner_identity`, i.e. their TLD must belong to `owner_identity`.\n    ///\n    /// The `owner_identity` is typically also the owner of the database.\n    ///\n    /// The operation is atomic -- either all `domain_names` are created and\n    /// existing ones deleted, or none.\n    pub fn spacetime_replace_domains(\n        &self,\n        database_identity: &Identity,\n        owner_identity: &Identity,\n        domain_names: &[DomainName],\n    ) -> Result<SetDomainsResult> {\n        let database_identity_bytes = database_identity.to_byte_array();\n\n        let dns_tree = self.db.open_tree(\"dns\")?;\n        let rev_tree = self.db.open_tree(\"reverse_dns\")?;\n        let tld_tree = self.db.open_tree(\"top_level_domains\")?;\n\n        /// Abort transaction with a user error.\n        #[derive(Debug)]\n        enum AbortWith {\n            Domain(SetDomainsResult),\n            Database(Error),\n        }\n\n        /// Decode the slice into a `Vec<DomainName>`.\n        /// Returns a transaction abort if decoding fails.\n        fn decode_domain_names(ivec: &[u8]) -> ConflictableTransactionResult<Vec<DomainName>, AbortWith> {\n            serde_json::from_slice(ivec).map_err(|e| {\n                log::error!(\"Control database corruption: invalid domain set in `reverse_dns` tree: {e}\");\n                ConflictableTransactionError::Abort(AbortWith::Database(e.into()))\n            })\n        }\n\n        /// Find the owner of the `domain`'s TLD, if there is one.\n        /// Returns a transaction abort if the owner could not be decoded into\n        /// an [`Identity`].\n        fn domain_owner(\n            tlds: &TransactionalTree,\n            domain: &DomainName,\n        ) -> ConflictableTransactionResult<Option<Identity>, AbortWith> {\n            tlds.get(domain.tld().to_lowercase().as_bytes())?\n                .as_ref()\n                .map(identity_from_le_ivec)\n                .transpose()\n                .map_err(|e| ConflictableTransactionError::Abort(AbortWith::Database(e)))\n        }\n\n        let trees = (&dns_tree, &rev_tree, &tld_tree);\n        let result: TransactionResult<(), AbortWith> =\n            Transactional::transaction(&trees, |(dns_tx, rev_tx, tld_tx)| {\n                // Remove all existing names.\n                if let Some(value) = rev_tx.get(database_identity_bytes)? {\n                    for domain in decode_domain_names(&value)? {\n                        if let Some(ref owner) = domain_owner(tld_tx, &domain)?\n                            && owner != owner_identity\n                        {\n                            transaction::abort(AbortWith::Domain(SetDomainsResult::PermissionDenied {\n                                domain: domain.clone(),\n                            }))?;\n                        }\n                        dns_tx.remove(domain.to_lowercase().as_bytes())?;\n                    }\n                    rev_tx.remove(&database_identity_bytes)?;\n                }\n\n                // Insert the new names.\n                for domain in domain_names {\n                    if let Some(ref owner) = domain_owner(tld_tx, domain)?\n                        && owner != owner_identity\n                    {\n                        transaction::abort(AbortWith::Domain(SetDomainsResult::PermissionDenied {\n                            domain: domain.clone(),\n                        }))?;\n                    }\n                    tld_tx.insert(domain.tld().to_lowercase().as_bytes(), &owner_identity.to_byte_array())?;\n                    dns_tx.insert(domain.to_lowercase().as_bytes(), &database_identity_bytes)?;\n                }\n                rev_tx.insert(&database_identity_bytes, serde_json::to_vec(domain_names).unwrap())?;\n\n                Ok::<_, ConflictableTransactionError<AbortWith>>(())\n            });\n\n        match result {\n            Ok(()) => Ok(SetDomainsResult::Success),\n            Err(e) => match e {\n                TransactionError::Storage(e) => Err(Error::Database(e)),\n                TransactionError::Abort(abort) => match abort {\n                    AbortWith::Database(e) => Err(e),\n                    AbortWith::Domain(res) => Ok(res),\n                },\n            },\n        }\n    }\n\n    /// Inserts a top level domain that will be owned by `owner_identity`.\n    ///\n    /// # Arguments\n    ///\n    /// * `domain` - The domain name to register\n    /// * `owner_identity` - The identity that should own this domain name.\n    pub fn spacetime_register_tld(&self, tld: Tld, owner_identity: Identity) -> Result<RegisterTldResult> {\n        let tree = self.db.open_tree(\"top_level_domains\")?;\n        let key = tld.to_lowercase();\n        let current_owner = tree.get(&key)?;\n        match current_owner {\n            Some(owner) => {\n                let current_owner =\n                    identity_from_le_ivec(&owner).context(\"Invalid current owner in top_level_domains\")?;\n                if current_owner == owner_identity {\n                    Ok(RegisterTldResult::AlreadyRegistered { domain: tld })\n                } else {\n                    Ok(RegisterTldResult::Unauthorized { domain: tld })\n                }\n            }\n            None => {\n                tree.insert(key, &owner_identity.to_byte_array())?;\n                Ok(RegisterTldResult::Success { domain: tld })\n            }\n        }\n    }\n\n    /// Returns the owner (or `None` if there is no owner) of the domain.\n    ///\n    /// # Arguments\n    ///  * `domain` - The domain to lookup\n    pub fn spacetime_lookup_tld(&self, domain: impl AsRef<TldRef>) -> Result<Option<Identity>> {\n        let tree = self.db.open_tree(\"top_level_domains\")?;\n        match tree.get(domain.as_ref().to_lowercase().as_bytes())? {\n            Some(owner) => Ok(Some(identity_from_le_ivec(&owner)?)),\n            None => Ok(None),\n        }\n    }\n\n    pub fn get_databases(&self) -> Result<Vec<Database>> {\n        let tree = self.db.open_tree(\"database\")?;\n        let mut databases = Vec::new();\n        let scan_key: &[u8] = b\"\";\n        for result in tree.range(scan_key..) {\n            let (_key, value) = result?;\n            let database = compat::Database::from_slice(&value)?.into();\n            databases.push(database);\n        }\n        Ok(databases)\n    }\n\n    pub fn get_database_by_id(&self, id: u64) -> Result<Option<Database>> {\n        for database in self.get_databases()? {\n            if database.id == id {\n                return Ok(Some(database));\n            }\n        }\n        Ok(None)\n    }\n\n    pub fn get_database_by_identity(&self, identity: &Identity) -> Result<Option<Database>> {\n        let tree = self.db.open_tree(\"database_by_identity\")?;\n        let key = identity.to_be_byte_array();\n        let value = tree.get(&key[..])?;\n        if let Some(value) = value {\n            let database = compat::Database::from_slice(&value[..])?.into();\n            return Ok(Some(database));\n        }\n        Ok(None)\n    }\n\n    pub fn insert_database(&self, mut database: Database) -> Result<u64> {\n        let id = self.db.generate_id()?;\n        let tree = self.db.open_tree(\"database_by_identity\")?;\n\n        let key = database.database_identity.to_be_byte_array();\n        if tree.contains_key(key)? {\n            return Err(Error::DatabaseAlreadyExists(database.database_identity));\n        }\n\n        database.id = id;\n\n        let buf = sled::IVec::from(compat::Database::from(database).to_vec()?);\n\n        tree.insert(key, buf.clone())?;\n\n        let tree = self.db.open_tree(\"database\")?;\n        tree.insert(id.to_be_bytes(), buf)?;\n\n        Ok(id)\n    }\n\n    pub(crate) fn update_database(&self, database: Database) -> Result<()> {\n        let Some(stored_database) = self.get_database_by_identity(&database.database_identity)? else {\n            return Err(Error::DatabaseNotFound(database.database_identity));\n        };\n\n        let tree = self.db.open_tree(\"database_by_identity\")?;\n        let buf = sled::IVec::from(compat::Database::from(database).to_vec()?);\n        tree.insert(stored_database.database_identity.to_be_byte_array(), buf.clone())?;\n\n        let tree = self.db.open_tree(\"database\")?;\n        tree.insert(stored_database.id.to_be_bytes(), buf)?;\n\n        Ok(())\n    }\n\n    pub fn delete_database(&self, id: u64) -> Result<Option<u64>> {\n        let tree = self.db.open_tree(\"database\")?;\n        let tree_by_identity = self.db.open_tree(\"database_by_identity\")?;\n\n        if let Some(old_value) = tree.get(id.to_be_bytes())? {\n            let database = compat::Database::from_slice(&old_value[..])?;\n            let key = database.database_identity().to_be_byte_array();\n\n            tree_by_identity.remove(&key[..])?;\n            tree.remove(id.to_be_bytes())?;\n            return Ok(Some(id));\n        }\n\n        Ok(None)\n    }\n\n    pub fn get_replicas(&self) -> Result<Vec<Replica>> {\n        let tree = self.db.open_tree(\"replica\")?;\n        let mut replicas = Vec::new();\n        let scan_key: &[u8] = b\"\";\n        for result in tree.range(scan_key..) {\n            let (_key, value) = result?;\n            let replica = bsatn::from_slice(&value[..])?;\n            replicas.push(replica);\n        }\n        Ok(replicas)\n    }\n\n    pub fn get_replica_by_id(&self, replica_id: u64) -> Result<Option<Replica>> {\n        for di in self.get_replicas()? {\n            if di.id == replica_id {\n                return Ok(Some(di));\n            }\n        }\n        Ok(None)\n    }\n\n    pub fn get_leader_replica_by_database(&self, database_id: u64) -> Option<Replica> {\n        self.get_replicas()\n            .unwrap()\n            .into_iter()\n            .find(|instance| instance.database_id == database_id && instance.leader)\n    }\n\n    pub fn get_replicas_by_database(&self, database_id: u64) -> Result<Vec<Replica>> {\n        // TODO: because we don't have foreign key constraints it's actually possible to have\n        // instances in here with no database. Although we'd be in a bit of a corrupted state\n        // in that case\n        //\n        // let tree = self.db.open_tree(\"database\")?;\n        // if !tree.contains_key(database_id.to_be_bytes())? {\n        //     return Err(anyhow::anyhow!(\"No such database.\"));\n        // }\n        //\n        let replicas = self\n            .get_replicas()?\n            .iter()\n            .filter(|instance| instance.database_id == database_id)\n            .cloned()\n            .collect::<Vec<_>>();\n        Ok(replicas)\n    }\n\n    pub fn insert_replica(&self, mut replica: Replica) -> Result<u64> {\n        let tree = self.db.open_tree(\"replica\")?;\n\n        let id = self.db.generate_id()?;\n\n        replica.id = id;\n        let buf = bsatn::to_vec(&replica).unwrap();\n\n        tree.insert(id.to_be_bytes(), buf)?;\n\n        Ok(id)\n    }\n\n    pub fn delete_replica(&self, id: u64) -> Result<()> {\n        let tree = self.db.open_tree(\"replica\")?;\n        tree.remove(id.to_be_bytes())?;\n        Ok(())\n    }\n\n    pub fn _get_nodes(&self) -> Result<Vec<Node>> {\n        let tree = self.db.open_tree(\"node\")?;\n        let mut nodes = Vec::new();\n        let scan_key: &[u8] = b\"\";\n        for result in tree.range(scan_key..) {\n            let (_key, value) = result?;\n            let node = bsatn::from_slice(&value[..]).unwrap();\n            nodes.push(node);\n        }\n        Ok(nodes)\n    }\n\n    pub fn _get_node(&self, id: u64) -> Result<Option<Node>> {\n        let tree = self.db.open_tree(\"node\")?;\n\n        let value = tree.get(id.to_be_bytes())?;\n        if let Some(value) = value {\n            let node = bsatn::from_slice(&value[..])?;\n            Ok(Some(node))\n        } else {\n            Ok(None)\n        }\n    }\n\n    pub fn _insert_node(&self, mut node: Node) -> Result<u64> {\n        let tree = self.db.open_tree(\"node\")?;\n\n        let id = self.db.generate_id()?;\n\n        node.id = id;\n        let buf = bsatn::to_vec(&node).unwrap();\n\n        tree.insert(id.to_be_bytes(), buf)?;\n\n        Ok(id)\n    }\n\n    pub fn _update_node(&self, node: Node) -> Result<()> {\n        let tree = self.db.open_tree(\"node\")?;\n\n        let buf = bsatn::to_vec(&node)?;\n\n        tree.insert(node.id.to_be_bytes(), buf)?;\n        Ok(())\n    }\n\n    pub fn _delete_node(&self, id: u64) -> Result<()> {\n        let tree = self.db.open_tree(\"node\")?;\n        tree.remove(id.to_be_bytes())?;\n        Ok(())\n    }\n\n    /// Return the current budget for all identities as stored in the db.\n    /// Note: this function is for the stored budget only and should *only* be called by functions in\n    /// `control_budget`, where a cached copy is stored along with business logic for managing it.\n    pub fn _get_energy_balances(&self) -> Result<Vec<EnergyBalance>> {\n        let mut balances = vec![];\n        let tree = self.db.open_tree(\"energy_budget\")?;\n        for balance_entry in tree.iter() {\n            let balance_entry = match balance_entry {\n                Ok(e) => e,\n                Err(e) => {\n                    log::error!(\"Invalid iteration in energy_budget control_db tree: {e}\");\n                    continue;\n                }\n            };\n            let arr = <[u8; 16]>::try_from(balance_entry.1.as_ref()).map_err(|_| bsatn::DecodeError::BufferLength {\n                for_type: \"balance_entry\",\n                expected: 16,\n                given: balance_entry.1.len(),\n            })?;\n            let balance = i128::from_be_bytes(arr);\n            let identity = identity_from_le_ivec(&balance_entry.0).context(\"invalid identity in energy_budget\")?;\n            let energy_balance = EnergyBalance { identity, balance };\n            balances.push(energy_balance);\n        }\n        Ok(balances)\n    }\n\n    /// Return the current budget for a given identity as stored in the db.\n    /// Note: this function is for the stored budget only and should *only* be called by functions in\n    /// `control_budget`, where a cached copy is stored along with business logic for managing it.\n    pub fn get_energy_balance(&self, identity: &Identity) -> Result<Option<energy::EnergyBalance>> {\n        let tree = self.db.open_tree(\"energy_budget\")?;\n        let value = tree.get(identity.to_byte_array())?;\n        if let Some(value) = value {\n            let arr = <[u8; 16]>::try_from(value.as_ref()).map_err(|_| bsatn::DecodeError::BufferLength {\n                for_type: \"Identity\",\n                expected: 16,\n                given: value.as_ref().len(),\n            })?;\n            let balance = i128::from_be_bytes(arr);\n            Ok(Some(energy::EnergyBalance::new(balance)))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Update the stored current budget for a identity.\n    /// Note: this function is for the stored budget only and should *only* be called by functions in\n    /// `control_budget`, where a cached copy is stored along with business logic for managing it.\n    pub fn set_energy_balance(&self, identity: Identity, energy_balance: energy::EnergyBalance) -> Result<()> {\n        let tree = self.db.open_tree(\"energy_budget\")?;\n        tree.insert(identity.to_byte_array(), &energy_balance.get().to_be_bytes())?;\n\n        Ok(())\n    }\n}\n\nmod compat {\n    use spacetimedb::hash::Hash;\n    use spacetimedb::messages::control_db::{Database as CanonicalDatabase, HostType};\n    use spacetimedb::Identity;\n    use spacetimedb_lib::bsatn::ser::BsatnError;\n    use spacetimedb_lib::bsatn::{self, DecodeError};\n    use spacetimedb_lib::{de::Deserialize, ser::Serialize};\n\n    /// Serialized form of a [`spacetimedb::messages::control_db::Database`].\n    ///\n    /// To maintain compatibility.\n    #[derive(Serialize, Deserialize)]\n    pub(super) struct Database {\n        id: u64,\n        database_identity: Identity,\n        owner_identity: Identity,\n        host_type: HostType,\n        initial_program: Hash,\n    }\n\n    impl Database {\n        pub fn database_identity(&self) -> Identity {\n            self.database_identity\n        }\n\n        #[inline]\n        pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {\n            bsatn::from_slice(s)\n        }\n\n        #[inline]\n        pub fn to_vec(&self) -> Result<Vec<u8>, BsatnError> {\n            bsatn::to_vec(self)\n        }\n    }\n\n    impl From<Database> for CanonicalDatabase {\n        fn from(\n            Database {\n                id,\n                database_identity,\n                owner_identity,\n                host_type,\n                initial_program,\n            }: Database,\n        ) -> Self {\n            Self {\n                id,\n                database_identity,\n                owner_identity,\n                host_type,\n                initial_program,\n            }\n        }\n    }\n\n    impl From<CanonicalDatabase> for Database {\n        fn from(\n            CanonicalDatabase {\n                id,\n                database_identity,\n                owner_identity,\n                host_type,\n                initial_program,\n            }: CanonicalDatabase,\n        ) -> Self {\n            Self {\n                id,\n                database_identity,\n                owner_identity,\n                host_type,\n                initial_program,\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/standalone/src/lib.rs",
    "content": "mod control_db;\npub mod subcommands;\npub mod util;\npub mod version;\n\nuse crate::control_db::ControlDb;\nuse crate::subcommands::{extract_schema, start};\nuse anyhow::Context as _;\nuse async_trait::async_trait;\nuse clap::{ArgMatches, Command};\nuse http::StatusCode;\nuse spacetimedb::client::ClientActorIndex;\nuse spacetimedb::config::{CertificateAuthority, MetadataFile};\nuse spacetimedb::db;\nuse spacetimedb::db::persistence::LocalPersistenceProvider;\nuse spacetimedb::energy::{EnergyBalance, EnergyQuanta, NullEnergyMonitor};\nuse spacetimedb::host::{DiskStorage, HostController, MigratePlanResult, UpdateDatabaseResult};\nuse spacetimedb::identity::{AuthCtx, Identity};\nuse spacetimedb::messages::control_db::{Database, Node, Replica};\nuse spacetimedb::subscription::row_list_builder_pool::BsatnRowListBuilderPool;\nuse spacetimedb::util::jobs::JobCores;\nuse spacetimedb::worker_metrics::WORKER_METRICS;\nuse spacetimedb_client_api::auth::{self, LOCALHOST};\nuse spacetimedb_client_api::routes::subscribe::{HasWebSocketOptions, WebSocketOptions};\nuse spacetimedb_client_api::{ControlStateReadAccess, DatabaseResetDef, Host, NodeDelegate};\nuse spacetimedb_client_api_messages::name::{\n    DatabaseName, DomainName, InsertDomainResult, RegisterTldResult, SetDomainsResult, Tld,\n};\nuse spacetimedb_datastore::db_metrics::data_size::DATA_SIZE_METRICS;\nuse spacetimedb_datastore::db_metrics::DB_METRICS;\nuse spacetimedb_datastore::traits::Program;\nuse spacetimedb_paths::server::{ModuleLogsDir, PidFile, ServerDataDir};\nuse spacetimedb_paths::standalone::StandaloneDataDirExt;\nuse spacetimedb_schema::auto_migrate::{MigrationPolicy, PrettyPrintStyle};\nuse spacetimedb_table::page_pool::PagePool;\nuse std::sync::Arc;\nuse std::time::Duration;\n\npub use spacetimedb_client_api::routes::subscribe::{BIN_PROTOCOL, TEXT_PROTOCOL};\n\n#[derive(Clone, Copy)]\npub struct StandaloneOptions {\n    pub db_config: db::Config,\n    pub websocket: WebSocketOptions,\n}\n\npub struct StandaloneEnv {\n    control_db: ControlDb,\n    program_store: Arc<DiskStorage>,\n    host_controller: HostController,\n    client_actor_index: ClientActorIndex,\n    metrics_registry: prometheus::Registry,\n    _pid_file: PidFile,\n    auth_provider: auth::DefaultJwtAuthProvider,\n    websocket_options: WebSocketOptions,\n}\n\nimpl StandaloneEnv {\n    pub async fn init(\n        config: StandaloneOptions,\n        certs: &CertificateAuthority,\n        data_dir: Arc<ServerDataDir>,\n        db_cores: JobCores,\n    ) -> anyhow::Result<Arc<Self>> {\n        let _pid_file = data_dir.pid_file()?;\n        let meta_path = data_dir.metadata_toml();\n        let mut meta = MetadataFile::new(\"standalone\");\n        if let Some(existing_meta) = MetadataFile::read(&meta_path).context(\"failed reading metadata.toml\")? {\n            meta = existing_meta.check_compatibility_and_update(meta)?;\n        }\n        meta.write(&meta_path).context(\"failed writing metadata.toml\")?;\n\n        let control_db = ControlDb::new(&data_dir.control_db()).context(\"failed to initialize control db\")?;\n        let energy_monitor = Arc::new(NullEnergyMonitor);\n        let program_store = Arc::new(DiskStorage::new(data_dir.program_bytes().0).await?);\n\n        let persistence_provider = Arc::new(LocalPersistenceProvider::new(data_dir.clone()));\n        let host_controller = HostController::new(\n            data_dir,\n            config.db_config,\n            program_store.clone(),\n            energy_monitor,\n            persistence_provider,\n            db_cores,\n        );\n        let client_actor_index = ClientActorIndex::new();\n        let jwt_keys = certs.get_or_create_keys()?;\n\n        let auth_env = auth::default_auth_environment(jwt_keys, LOCALHOST.into());\n\n        let metrics_registry = prometheus::Registry::new();\n        metrics_registry.register(Box::new(&*WORKER_METRICS)).unwrap();\n        metrics_registry.register(Box::new(&*DB_METRICS)).unwrap();\n        metrics_registry.register(Box::new(&*DATA_SIZE_METRICS)).unwrap();\n\n        Ok(Arc::new(Self {\n            control_db,\n            program_store,\n            host_controller,\n            client_actor_index,\n            metrics_registry,\n            _pid_file,\n            auth_provider: auth_env,\n            websocket_options: config.websocket,\n        }))\n    }\n\n    pub fn data_dir(&self) -> &Arc<ServerDataDir> {\n        &self.host_controller.data_dir\n    }\n\n    pub fn page_pool(&self) -> &PagePool {\n        &self.host_controller.page_pool\n    }\n\n    pub fn bsatn_rlb_pool(&self) -> &BsatnRowListBuilderPool {\n        &self.host_controller.bsatn_rlb_pool\n    }\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum GetLeaderHostError {\n    #[error(\"database does not exist\")]\n    NoSuchDatabase,\n    #[error(\"replica does not exist\")]\n    NoSuchReplica,\n    #[error(\"error starting database: {source:#}\")]\n    LaunchError { source: anyhow::Error },\n    #[error(\"error accessing controldb: {0:#}\")]\n    Control(#[from] control_db::Error),\n}\n\nimpl spacetimedb_client_api::MaybeMisdirected for GetLeaderHostError {\n    fn is_misdirected(&self) -> bool {\n        matches!(self, Self::NoSuchDatabase | Self::NoSuchReplica)\n    }\n}\n\nimpl From<GetLeaderHostError> for axum::response::ErrorResponse {\n    fn from(e: GetLeaderHostError) -> Self {\n        let status = match e {\n            GetLeaderHostError::NoSuchDatabase | GetLeaderHostError::NoSuchReplica => StatusCode::NOT_FOUND,\n            GetLeaderHostError::LaunchError { .. } | GetLeaderHostError::Control { .. } => {\n                StatusCode::INTERNAL_SERVER_ERROR\n            }\n        };\n\n        Self::from((status, e.to_string()))\n    }\n}\n\n#[async_trait]\nimpl NodeDelegate for StandaloneEnv {\n    type JwtAuthProviderT = auth::DefaultJwtAuthProvider;\n    type GetLeaderHostError = GetLeaderHostError;\n\n    fn gather_metrics(&self) -> Vec<prometheus::proto::MetricFamily> {\n        self.metrics_registry.gather()\n    }\n\n    fn client_actor_index(&self) -> &ClientActorIndex {\n        &self.client_actor_index\n    }\n\n    fn jwt_auth_provider(&self) -> &Self::JwtAuthProviderT {\n        &self.auth_provider\n    }\n\n    async fn leader(&self, database_id: u64) -> Result<Host, Self::GetLeaderHostError> {\n        let Some(leader) = self.control_db.get_leader_replica_by_database(database_id) else {\n            return Err(GetLeaderHostError::NoSuchReplica);\n        };\n\n        let Some(database) = self.control_db.get_database_by_id(database_id)? else {\n            return Err(GetLeaderHostError::NoSuchDatabase);\n        };\n\n        self.host_controller\n            .get_or_launch_module_host(database, leader.id)\n            .await\n            .map_err(|source| GetLeaderHostError::LaunchError { source })?;\n\n        Ok(Host::new(leader.id, self.host_controller.clone()))\n    }\n\n    fn module_logs_dir(&self, replica_id: u64) -> ModuleLogsDir {\n        self.data_dir().replica(replica_id).module_logs()\n    }\n}\n\n#[async_trait]\nimpl spacetimedb_client_api::ControlStateReadAccess for StandaloneEnv {\n    // Nodes\n    async fn get_node_id(&self) -> Option<u64> {\n        Some(0)\n    }\n\n    async fn get_node_by_id(&self, node_id: u64) -> anyhow::Result<Option<Node>> {\n        if node_id == 0 {\n            return Ok(Some(Node {\n                id: 0,\n                unschedulable: false,\n                advertise_addr: Some(\"node:80\".to_owned()),\n                pg_addr: Some(\"node:5432\".to_owned()),\n            }));\n        }\n        Ok(None)\n    }\n\n    async fn get_nodes(&self) -> anyhow::Result<Vec<Node>> {\n        Ok(vec![self.get_node_by_id(0).await?.unwrap()])\n    }\n\n    // Databases\n    async fn get_database_by_id(&self, id: u64) -> anyhow::Result<Option<Database>> {\n        Ok(self.control_db.get_database_by_id(id)?)\n    }\n\n    async fn get_database_by_identity(&self, database_identity: &Identity) -> anyhow::Result<Option<Database>> {\n        Ok(self.control_db.get_database_by_identity(database_identity)?)\n    }\n\n    async fn get_databases(&self) -> anyhow::Result<Vec<Database>> {\n        Ok(self.control_db.get_databases()?)\n    }\n\n    // Replicas\n    async fn get_replica_by_id(&self, id: u64) -> anyhow::Result<Option<Replica>> {\n        Ok(self.control_db.get_replica_by_id(id)?)\n    }\n\n    async fn get_replicas(&self) -> anyhow::Result<Vec<Replica>> {\n        Ok(self.control_db.get_replicas()?)\n    }\n\n    async fn get_leader_replica_by_database(&self, database_id: u64) -> Option<Replica> {\n        self.control_db.get_leader_replica_by_database(database_id)\n    }\n    // Energy\n    async fn get_energy_balance(&self, identity: &Identity) -> anyhow::Result<Option<EnergyBalance>> {\n        Ok(self.control_db.get_energy_balance(identity)?)\n    }\n\n    // DNS\n    async fn lookup_database_identity(&self, domain: &str) -> anyhow::Result<Option<Identity>> {\n        Ok(self.control_db.spacetime_dns(domain)?)\n    }\n\n    async fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result<Vec<DomainName>> {\n        Ok(self.control_db.spacetime_reverse_dns(database_identity)?)\n    }\n\n    async fn lookup_namespace_owner(&self, name: &str) -> anyhow::Result<Option<Identity>> {\n        let name: DatabaseName = name.parse()?;\n        Ok(self.control_db.spacetime_lookup_tld(Tld::from(name))?)\n    }\n}\n\n#[async_trait]\nimpl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv {\n    async fn publish_database(\n        &self,\n        publisher: &Identity,\n        spec: spacetimedb_client_api::DatabaseDef,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<Option<UpdateDatabaseResult>> {\n        let existing_db = self.control_db.get_database_by_identity(&spec.database_identity)?;\n\n        // standalone does not support replication.\n        let num_replicas = 1;\n\n        match existing_db {\n            // The database does not already exist, so we'll create it.\n            None => {\n                let program = Program::from_bytes(spec.host_type.into(), &spec.program_bytes[..]);\n\n                let database = Database {\n                    id: 0,\n                    database_identity: spec.database_identity,\n                    owner_identity: *publisher,\n                    host_type: spec.host_type,\n                    initial_program: program.hash,\n                };\n\n                let _hash_for_assert = program.hash;\n\n                // Instantiate a temporary database in order to check that the module is valid.\n                // This will e.g. typecheck RLS filters.\n                self.host_controller\n                    .check_module_validity(database.clone(), program)\n                    .await?;\n\n                let program_hash = self.program_store.put(&spec.program_bytes).await?;\n\n                debug_assert_eq!(_hash_for_assert, program_hash);\n\n                let database_id = self.control_db.insert_database(database)?;\n\n                self.schedule_replicas(database_id, num_replicas).await?;\n\n                Ok(None)\n            }\n            // The database already exists, so we'll try to update it.\n            // If that fails, we'll keep the old one.\n            Some(database) => {\n                let database_id = database.id;\n                let database_identity = database.database_identity;\n\n                let leader = self.leader(database_id).await?;\n                let update_result = leader\n                    .update(database, spec.host_type, spec.program_bytes.to_vec().into(), policy)\n                    .await?;\n                if update_result.was_successful() {\n                    let replicas = self.control_db.get_replicas_by_database(database_id)?;\n                    let desired_replicas = num_replicas as usize;\n                    if desired_replicas == 0 {\n                        log::info!(\"Decommissioning all replicas of database {database_identity}\");\n                        for instance in replicas {\n                            self.delete_replica(instance.id).await?;\n                        }\n                    } else if desired_replicas > replicas.len() {\n                        let n = desired_replicas - replicas.len();\n                        log::info!(\n                            \"Scaling up database {} from {} to {} replicas\",\n                            database_identity,\n                            replicas.len(),\n                            n\n                        );\n                        for _ in 0..n {\n                            self.insert_replica(Replica {\n                                id: 0,\n                                database_id,\n                                node_id: 0,\n                                leader: false,\n                            })\n                            .await?;\n                        }\n                    } else if desired_replicas < replicas.len() {\n                        let n = replicas.len() - desired_replicas;\n                        log::info!(\n                            \"Scaling down database {} from {} to {} replicas\",\n                            database_identity,\n                            replicas.len(),\n                            n\n                        );\n                        for instance in replicas.into_iter().filter(|instance| !instance.leader).take(n) {\n                            self.delete_replica(instance.id).await?;\n                        }\n                    } else {\n                        log::debug!(\n                            \"Desired replica count {desired_replicas} for database {database_identity} already satisfied\"\n                        );\n                    }\n                }\n\n                anyhow::Ok(Some(update_result))\n            }\n        }\n    }\n\n    async fn migrate_plan(\n        &self,\n        spec: spacetimedb_client_api::DatabaseDef,\n        style: PrettyPrintStyle,\n    ) -> anyhow::Result<MigratePlanResult> {\n        let existing_db = self.control_db.get_database_by_identity(&spec.database_identity)?;\n\n        match existing_db {\n            Some(db) => {\n                let host = self.leader(db.id).await?;\n                self.host_controller\n                    .migrate_plan(\n                        db,\n                        spec.host_type,\n                        host.replica_id,\n                        spec.program_bytes.to_vec().into(),\n                        style,\n                    )\n                    .await\n            }\n            None => anyhow::bail!(\n                \"Database `{}` does not exist\",\n                spec.database_identity.to_abbreviated_hex()\n            ),\n        }\n    }\n\n    async fn delete_database(&self, _caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()> {\n        let Some(database) = self.control_db.get_database_by_identity(database_identity)? else {\n            return Ok(());\n        };\n        self.control_db.delete_database(database.id)?;\n\n        for instance in self.control_db.get_replicas_by_database(database.id)? {\n            self.delete_replica(instance.id).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn reset_database(&self, _caller_identity: &Identity, spec: DatabaseResetDef) -> anyhow::Result<()> {\n        let mut database = self\n            .control_db\n            .get_database_by_identity(&spec.database_identity)?\n            .with_context(|| format!(\"Database `{}` does not exist\", spec.database_identity))?;\n        let database_id = database.id;\n\n        if let Some(program) = spec.program_bytes {\n            if let Some(host_type) = spec.host_type {\n                database.host_type = host_type;\n            }\n            let program_bytes = &program[..];\n            let program = Program::from_bytes(database.host_type.into(), program_bytes);\n            let _hash_for_assert = program.hash;\n\n            database.initial_program = program.hash;\n\n            self.host_controller\n                .check_module_validity(database.clone(), program)\n                .await?;\n            let _stored_hash_for_assert = self.program_store.put(program_bytes).await?;\n            debug_assert_eq!(_hash_for_assert, _stored_hash_for_assert);\n\n            self.control_db.update_database(database)?;\n        }\n\n        for instance in self.control_db.get_replicas_by_database(database_id)? {\n            self.delete_replica(instance.id).await?;\n        }\n        // Standalone only support a single replica.\n        let num_replicas = 1;\n        self.schedule_replicas(database_id, num_replicas).await?;\n\n        Ok(())\n    }\n\n    async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()> {\n        let balance = self\n            .control_db\n            .get_energy_balance(identity)?\n            .unwrap_or(EnergyBalance::ZERO);\n\n        let balance = balance.saturating_add_energy(amount);\n\n        self.control_db.set_energy_balance(*identity, balance)?;\n        Ok(())\n    }\n    async fn withdraw_energy(&self, _identity: &Identity, _amount: EnergyQuanta) -> anyhow::Result<()> {\n        // The energy balance code is obsolete.\n        Ok(())\n    }\n\n    async fn register_tld(&self, identity: &Identity, tld: Tld) -> anyhow::Result<RegisterTldResult> {\n        Ok(self.control_db.spacetime_register_tld(tld, *identity)?)\n    }\n\n    async fn create_dns_record(\n        &self,\n        owner_identity: &Identity,\n        domain: &DomainName,\n        database_identity: &Identity,\n    ) -> anyhow::Result<InsertDomainResult> {\n        Ok(self\n            .control_db\n            .spacetime_insert_domain(database_identity, domain.clone(), *owner_identity, true)?)\n    }\n\n    async fn replace_dns_records(\n        &self,\n        database_identity: &Identity,\n        owner_identity: &Identity,\n        domain_names: &[DomainName],\n    ) -> anyhow::Result<SetDomainsResult> {\n        Ok(self\n            .control_db\n            .spacetime_replace_domains(database_identity, owner_identity, domain_names)?)\n    }\n}\n\nimpl spacetimedb_client_api::Authorization for StandaloneEnv {\n    async fn authorize_action(\n        &self,\n        subject: Identity,\n        database: Identity,\n        action: spacetimedb_client_api::Action,\n    ) -> Result<(), spacetimedb_client_api::Unauthorized> {\n        // Creating a database is always allowed.\n        if let spacetimedb_client_api::Action::CreateDatabase { .. } = action {\n            return Ok(());\n        }\n\n        // Otherwise, the database must already exist,\n        // and the `subject` equal to `database.owner_identity`.\n        let database = self\n            .get_database_by_identity(&database)\n            .await?\n            .with_context(|| format!(\"database {database} not found\"))\n            .with_context(|| format!(\"Unable to authorize {subject} to perform {action:?})\"))?;\n        if subject == database.owner_identity {\n            return Ok(());\n        }\n\n        Err(spacetimedb_client_api::Unauthorized::Unauthorized {\n            subject,\n            action,\n            database: database.database_identity.into(),\n            source: None,\n        })\n    }\n\n    async fn authorize_sql(\n        &self,\n        subject: Identity,\n        database: Identity,\n    ) -> Result<AuthCtx, spacetimedb_client_api::Unauthorized> {\n        let database = self\n            .get_database_by_identity(&database)\n            .await?\n            .with_context(|| format!(\"database {database} not found\"))\n            .with_context(|| format!(\"Unable to authorize {subject} for SQL\"))?;\n\n        Ok(AuthCtx::new(database.owner_identity, subject))\n    }\n}\n\nimpl StandaloneEnv {\n    async fn insert_replica(&self, replica: Replica) -> Result<(), anyhow::Error> {\n        let mut new_replica = replica.clone();\n        let id = self.control_db.insert_replica(replica)?;\n        new_replica.id = id;\n\n        self.on_insert_replica(&new_replica).await?;\n\n        Ok(())\n    }\n\n    async fn delete_replica(&self, replica_id: u64) -> Result<(), anyhow::Error> {\n        self.control_db.delete_replica(replica_id)?;\n        self.on_delete_replica(replica_id).await?;\n\n        Ok(())\n    }\n\n    async fn schedule_replicas(&self, database_id: u64, num_replicas: u8) -> Result<(), anyhow::Error> {\n        // Just scheduling a bunch of replicas to the only machine\n        for i in 0..num_replicas {\n            let replica = Replica {\n                id: 0,\n                database_id,\n                node_id: 0,\n                leader: i == 0,\n            };\n            self.insert_replica(replica).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn on_insert_replica(&self, instance: &Replica) -> Result<(), anyhow::Error> {\n        if instance.leader {\n            let database = self\n                .control_db\n                .get_database_by_id(instance.database_id)?\n                .with_context(|| {\n                    format!(\n                        \"unknown database: id: {}, instance: {}\",\n                        instance.database_id, instance.id\n                    )\n                })?;\n            self.leader(database.id).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn on_delete_replica(&self, replica_id: u64) -> anyhow::Result<()> {\n        // TODO(cloutiertyler): We should think about how to clean up\n        // replicas which have been deleted. This will just drop\n        // them from memory, but will not remove them from disk.  We need\n        // some kind of database lifecycle manager long term.\n        self.host_controller\n            .exit_module_host(replica_id, Duration::from_secs(30))\n            .await?;\n\n        Ok(())\n    }\n}\n\nimpl HasWebSocketOptions for StandaloneEnv {\n    fn websocket_options(&self) -> WebSocketOptions {\n        self.websocket_options\n    }\n}\n\npub async fn exec_subcommand(cmd: &str, args: &ArgMatches, db_cores: JobCores) -> Result<(), anyhow::Error> {\n    match cmd {\n        \"start\" => start::exec(args, db_cores).await,\n        \"extract-schema\" => extract_schema::exec(args).await,\n        unknown => Err(anyhow::anyhow!(\"Invalid subcommand: {unknown}\")),\n    }\n}\n\npub fn get_subcommands() -> Vec<Command> {\n    vec![start::cli(), extract_schema::cli()]\n}\n\npub async fn start_server(data_dir: &ServerDataDir, cert_dir: Option<&std::path::Path>) -> anyhow::Result<()> {\n    let mut args: Vec<&std::ffi::OsStr> = vec![\"start\".as_ref(), \"--data-dir\".as_ref(), data_dir.0.as_os_str()];\n    if let Some(cert_dir) = &cert_dir {\n        args.extend([\"--jwt-key-dir\".as_ref(), cert_dir.as_os_str()])\n    }\n    let args = start::cli().try_get_matches_from(args)?;\n    start::exec(&args, JobCores::without_pinned_cores()).await\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use anyhow::Result;\n    use spacetimedb::db::Storage;\n    use spacetimedb_paths::{cli::*, FromPathUnchecked};\n    use std::fs;\n    use tempfile::TempDir;\n\n    #[tokio::test]\n    async fn ensure_init_grabs_lock() -> Result<()> {\n        let tempdir = TempDir::new()?;\n        // Use one subdir for keys and another for the data dir.\n        let keys = tempdir.path().join(\"keys\");\n        let root = tempdir.path().join(\"data\");\n        let data_dir = Arc::new(ServerDataDir::from_path_unchecked(root));\n\n        fs::create_dir(&keys)?;\n        data_dir.create()?;\n\n        let pub_key = PubKeyPath(keys.join(\"public\"));\n        let priv_key = PrivKeyPath(keys.join(\"private\"));\n        let ca = CertificateAuthority {\n            jwt_pub_key_path: pub_key,\n            jwt_priv_key_path: priv_key,\n        };\n\n        // Create the keys.\n        ca.get_or_create_keys()?;\n        let config = StandaloneOptions {\n            db_config: db::Config {\n                storage: Storage::Memory,\n                page_pool_max_size: None,\n            },\n            websocket: WebSocketOptions::default(),\n        };\n\n        let _env = StandaloneEnv::init(config, &ca, data_dir.clone(), JobCores::without_pinned_cores()).await?;\n        // Ensure that we have a lock.\n        assert!(\n            StandaloneEnv::init(config, &ca, data_dir.clone(), JobCores::without_pinned_cores())\n                .await\n                .is_err()\n        );\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/standalone/src/main.rs",
    "content": "use clap::Command;\n\nuse spacetimedb::startup;\nuse spacetimedb::util::jobs::JobCores;\nuse tokio::runtime::Builder;\n\nuse spacetimedb_standalone::*;\nuse std::panic;\nuse std::process;\n\nasync fn async_main(db_cores: JobCores) -> anyhow::Result<()> {\n    let matches = get_command().get_matches();\n    let (cmd, subcommand_args) = matches.subcommand().unwrap();\n    exec_subcommand(cmd, subcommand_args, db_cores).await?;\n    Ok(())\n}\n\nfn get_command() -> Command {\n    Command::new(\"spacetimedb\")\n        .args_conflicts_with_subcommands(true)\n        .arg_required_else_help(true)\n        .version(version::CLI_VERSION)\n        .long_version(version::long_version())\n        .subcommand_required(true)\n        .subcommands(get_subcommands())\n        .help_expected(true)\n        .help_template(\n            r#\"\n┌──────────────────────────────────────────────────────────┐\n│ spacetimedb                                              │\n│ Run a standalone SpacetimeDB instance                    │\n│                                                          │\n│ Please give us feedback at:                              │\n│ https://github.com/clockworklabs/SpacetimeDB/issues      │\n└──────────────────────────────────────────────────────────┘\nExample usage:\n┌──────────────────────────────────────────────────────────┐\n│ machine# spacetimedb start                               │\n└──────────────────────────────────────────────────────────┘\n\"#,\n        )\n}\n\n#[cfg(not(target_env = \"msvc\"))]\nuse tikv_jemallocator::Jemalloc;\n\n#[cfg(not(target_env = \"msvc\"))]\n#[global_allocator]\nstatic GLOBAL: Jemalloc = Jemalloc;\n\n// Defaults our jemalloc configuration to allow for profiling, but have it disabled initially.\n// It can be enabled with an internal endpoint.\n// When enabled, it will sample once per 2^19 bytes (512 KB) of memory allocated.\n\n// This can be overridden by setting the `_RJEM_MALLOC_CONF` environment variable.\n// We export this symbol so that the jemalloc library can find it.\n// See https://github.com/polarsignals/rust-jemalloc-pprof?tab=readme-ov-file#usage\n#[allow(non_upper_case_globals)]\n#[unsafe(export_name = \"_rjem_malloc_conf\")]\npub static _rjem_malloc_conf: &[u8] = b\"prof:true,prof_active:false,lg_prof_sample:19\\0\";\n\nfn main() -> anyhow::Result<()> {\n    // take_hook() returns the default hook in case when a custom one is not set\n    let orig_hook = panic::take_hook();\n    panic::set_hook(Box::new(move |panic_info| {\n        // invoke the default handler and exit the process\n        orig_hook(panic_info);\n        process::exit(1);\n    }));\n\n    let cores = startup::pin_threads();\n\n    // Create a multi-threaded run loop\n    let mut builder = Builder::new_multi_thread();\n    builder.enable_all();\n    cores.tokio.configure(&mut builder);\n    let rt = builder.build().unwrap();\n    cores.rayon.configure(rt.handle());\n    let database_cores = cores.databases.make_database_runners();\n\n    // Keep a handle on the `database_cores` alive outside of `async_main`\n    // and explicitly drop it to avoid dropping it from an `async` context -\n    // Tokio gets angry when you drop a runtime within another runtime.\n    let res = rt.block_on(async_main(database_cores.clone()));\n    drop(database_cores);\n\n    res\n}\n"
  },
  {
    "path": "crates/standalone/src/subcommands/extract_schema.rs",
    "content": "use std::path::PathBuf;\n\nuse anyhow::Context;\nuse clap::{ArgMatches, CommandFactory, FromArgMatches};\nuse spacetimedb::host::extract_schema;\nuse spacetimedb::messages::control_db;\nuse spacetimedb_lib::{db::raw_def::v10::RawModuleDefV10, sats, RawModuleDef};\n\n/// Extracts the module schema from a local module file.\n/// WARNING: This command is UNSTABLE and subject to breaking changes.\n#[derive(clap::Parser)]\n#[command(name = \"extract-schema\")]\nstruct Args {\n    /// The module file\n    module: PathBuf,\n\n    /// The type of module\n    #[arg(long)]\n    host_type: Option<HostType>,\n}\n\n#[derive(clap::ValueEnum, Copy, Clone)]\nenum HostType {\n    Wasm,\n    Js,\n}\n\nimpl From<HostType> for control_db::HostType {\n    fn from(x: HostType) -> Self {\n        match x {\n            HostType::Wasm => control_db::HostType::Wasm,\n            HostType::Js => control_db::HostType::Js,\n        }\n    }\n}\n\nimpl HostType {\n    fn from_extension(ext: &str) -> Option<Self> {\n        match ext {\n            \"wasm\" => Some(Self::Wasm),\n            \"js\" => Some(Self::Js),\n            _ => None,\n        }\n    }\n}\n\npub fn cli() -> clap::Command {\n    Args::command()\n}\n\npub async fn exec(args: &ArgMatches) -> anyhow::Result<()> {\n    let args = Args::from_arg_matches(args)?;\n\n    let host_type = match args.host_type {\n        Some(x) => x,\n        None => args\n            .module\n            .extension()\n            .and_then(|x| x.to_str())\n            .and_then(HostType::from_extension)\n            .context(\"--host-type not provided but cannot deduce from file extension\")?,\n    };\n\n    let program_bytes = tokio::fs::read(&args.module)\n        .await\n        .with_context(|| format!(\"could not read module file {}\", args.module.display()))?;\n\n    let module_def = extract_schema(program_bytes.into(), host_type.into()).await?;\n\n    let raw_def = RawModuleDef::V10(RawModuleDefV10::from(module_def));\n\n    serde_json::to_writer(std::io::stdout().lock(), &sats::serde::SerdeWrapper(raw_def))?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/standalone/src/subcommands/mod.rs",
    "content": "// CLI commands are allowed to use println and friends.\n#![allow(clippy::disallowed_macros)]\n\npub mod extract_schema;\npub mod start;\n"
  },
  {
    "path": "crates/standalone/src/subcommands/start.rs",
    "content": "use netstat2::{get_sockets_info, AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, TcpState};\nuse spacetimedb_client_api::routes::identity::IdentityRoutes;\nuse spacetimedb_pg::pg_server;\nuse std::io::{self, Write};\nuse std::net::IpAddr;\nuse std::sync::Arc;\n\nuse crate::{StandaloneEnv, StandaloneOptions};\nuse anyhow::Context;\nuse axum::extract::DefaultBodyLimit;\nuse clap::ArgAction::SetTrue;\nuse clap::{Arg, ArgMatches};\nuse spacetimedb::config::{parse_config, CertificateAuthority};\nuse spacetimedb::db::{self, Storage};\nuse spacetimedb::startup::{self, TracingOptions};\nuse spacetimedb::util::jobs::JobCores;\nuse spacetimedb::worker_metrics;\nuse spacetimedb_client_api::routes::database::DatabaseRoutes;\nuse spacetimedb_client_api::routes::router;\nuse spacetimedb_client_api::routes::subscribe::WebSocketOptions;\nuse spacetimedb_paths::cli::{PrivKeyPath, PubKeyPath};\nuse spacetimedb_paths::server::{ConfigToml, ServerDataDir};\nuse tokio::net::TcpListener;\n\npub fn cli() -> clap::Command {\n    clap::Command::new(\"start\")\n        .about(\"Starts a standalone SpacetimeDB instance\")\n        .args_override_self(true)\n        .override_usage(\"spacetime start [OPTIONS]\")\n        .arg(\n            Arg::new(\"listen_addr\")\n                .long(\"listen-addr\")\n                .short('l')\n                .default_value(\"0.0.0.0:3000\")\n                .help(\n                    \"The address and port where SpacetimeDB should listen for connections. \\\n                     This defaults to to listen on all IP addresses on port 80.\",\n                ),\n        )\n        .arg(\n            Arg::new(\"data_dir\")\n                .long(\"data-dir\")\n                .help(\"The path to the data directory for the database\")\n                .required(true)\n                .value_parser(clap::value_parser!(ServerDataDir)),\n        )\n        .arg(\n            Arg::new(\"enable_tracy\")\n                .long(\"enable-tracy\")\n                .action(SetTrue)\n                .help(\"Enable Tracy profiling\"),\n        )\n        .arg(\n            Arg::new(\"jwt_key_dir\")\n                .hide(true)\n                .long(\"jwt-key-dir\")\n                .help(\"The directory with id_ecdsa and id_ecdsa.pub\")\n                .value_parser(clap::value_parser!(spacetimedb_paths::cli::ConfigDir)),\n        )\n        .arg(\n            Arg::new(\"jwt_pub_key_path\")\n                .long(\"jwt-pub-key-path\")\n                .requires(\"jwt_priv_key_path\")\n                .help(\"The path to the public jwt key for verifying identities\")\n                .value_parser(clap::value_parser!(PubKeyPath)),\n        )\n        .arg(\n            Arg::new(\"jwt_priv_key_path\")\n                .long(\"jwt-priv-key-path\")\n                .requires(\"jwt_pub_key_path\")\n                .help(\"The path to the private jwt key for issuing identities\")\n                .value_parser(clap::value_parser!(PrivKeyPath)),\n        )\n        .arg(Arg::new(\"in_memory\").long(\"in-memory\").action(SetTrue).help(\n            \"If specified the database will run entirely in memory. After the process exits all data will be lost.\",\n        ))\n        .arg(\n            Arg::new(\"page_pool_max_size\").long(\"page_pool_max_size\").help(\n                \"The maximum size of the page pool in bytes. Should be a multiple of 64KiB. The default is 8GiB.\",\n            ),\n        )\n        .arg(\n            Arg::new(\"pg_port\")\n                .long(\"pg-port\")\n                .help(\"If specified, enables the built-in PostgreSQL wire protocol server on the given port.\")\n                .value_parser(clap::value_parser!(u16).range(1024..65535)),\n        )\n        .arg(\n            Arg::new(\"non_interactive\")\n                .long(\"non-interactive\")\n                .action(SetTrue)\n                .help(\"Run in non-interactive mode (fail immediately if port is in use)\"),\n        )\n    // .after_help(\"Run `spacetime help start` for more detailed information.\")\n}\n\n#[derive(Default, serde::Deserialize)]\nstruct ConfigFile {\n    #[serde(flatten)]\n    common: spacetimedb::config::ConfigFile,\n    #[serde(default)]\n    websocket: WebSocketOptions,\n}\n\nimpl ConfigFile {\n    fn read(path: &ConfigToml) -> anyhow::Result<Option<Self>> {\n        parse_config(path.as_ref())\n    }\n}\n\npub async fn exec(args: &ArgMatches, db_cores: JobCores) -> anyhow::Result<()> {\n    let listen_addr = args.get_one::<String>(\"listen_addr\").unwrap();\n    let pg_port = args.get_one::<u16>(\"pg_port\");\n    let non_interactive = args.get_flag(\"non_interactive\");\n    let cert_dir = args.get_one::<spacetimedb_paths::cli::ConfigDir>(\"jwt_key_dir\");\n    let certs = Option::zip(\n        args.get_one::<PubKeyPath>(\"jwt_pub_key_path\").cloned(),\n        args.get_one::<PrivKeyPath>(\"jwt_priv_key_path\").cloned(),\n    )\n    .map(|(jwt_pub_key_path, jwt_priv_key_path)| CertificateAuthority {\n        jwt_pub_key_path,\n        jwt_priv_key_path,\n    });\n    let data_dir = args.get_one::<ServerDataDir>(\"data_dir\").unwrap();\n    let enable_tracy = args.get_flag(\"enable_tracy\") || std::env::var_os(\"SPACETIMEDB_TRACY\").is_some();\n    let storage = if args.get_flag(\"in_memory\") {\n        Storage::Memory\n    } else {\n        Storage::Disk\n    };\n    let page_pool_max_size = args\n        .get_one::<String>(\"page_pool_max_size\")\n        .map(|size| parse_size::Config::new().with_binary().parse_size(size))\n        .transpose()\n        .context(\"unrecognized format in `page_pool_max_size`\")?\n        .map(|size| size as usize);\n    let db_config = db::Config {\n        storage,\n        page_pool_max_size,\n    };\n\n    banner();\n    let exe_name = std::env::current_exe()?;\n    let exe_name = exe_name.file_name().unwrap().to_str().unwrap();\n    println!(\"{} version: {}\", exe_name, env!(\"CARGO_PKG_VERSION\"));\n    println!(\"{} path: {}\", exe_name, std::env::current_exe()?.display());\n    println!(\"database running in data directory {}\", data_dir.display());\n\n    let config_path = data_dir.config_toml();\n    let config = match ConfigFile::read(&data_dir.config_toml())? {\n        Some(config) => config,\n        None => {\n            let default_config = include_str!(\"../../config.toml\");\n            data_dir.create()?;\n            config_path.write(default_config)?;\n            toml::from_str(default_config).unwrap()\n        }\n    };\n\n    startup::configure_tracing(TracingOptions {\n        config: config.common.logs,\n        reload_config: cfg!(debug_assertions).then_some(config_path),\n        disk_logging: std::env::var_os(\"SPACETIMEDB_DISABLE_DISK_LOGGING\")\n            .is_none()\n            .then(|| data_dir.logs()),\n        edition: \"standalone\".to_owned(),\n        tracy: enable_tracy || std::env::var_os(\"SPACETIMEDB_TRACY\").is_some(),\n        flamegraph: std::env::var_os(\"SPACETIMEDB_FLAMEGRAPH\").map(|_| {\n            std::env::var_os(\"SPACETIMEDB_FLAMEGRAPH_PATH\")\n                .unwrap_or(\"/var/log/flamegraph.folded\".into())\n                .into()\n        }),\n    });\n\n    let certs = certs\n        .or(config.common.certificate_authority)\n        .or_else(|| cert_dir.map(CertificateAuthority::in_cli_config_dir))\n        .context(\"cannot omit --jwt-{pub,priv}-key-path when those options are not specified in config.toml\")?;\n\n    let data_dir = Arc::new(data_dir.clone());\n    let ctx = StandaloneEnv::init(\n        StandaloneOptions {\n            db_config,\n            websocket: config.websocket,\n        },\n        &certs,\n        data_dir,\n        db_cores,\n    )\n    .await?;\n    worker_metrics::spawn_jemalloc_stats(listen_addr.clone());\n    worker_metrics::spawn_tokio_stats(listen_addr.clone());\n    worker_metrics::spawn_page_pool_stats(listen_addr.clone(), ctx.page_pool().clone());\n    worker_metrics::spawn_bsatn_rlb_pool_stats(listen_addr.clone(), ctx.bsatn_rlb_pool().clone());\n    let mut db_routes = DatabaseRoutes::default();\n    db_routes.root_post = db_routes.root_post.layer(DefaultBodyLimit::disable());\n    db_routes.db_put = db_routes.db_put.layer(DefaultBodyLimit::disable());\n    db_routes.pre_publish = db_routes.pre_publish.layer(DefaultBodyLimit::disable());\n    let extra = axum::Router::new().nest(\"/health\", spacetimedb_client_api::routes::health::router());\n    let service = router(&ctx, db_routes, IdentityRoutes::default(), extra).with_state(ctx.clone());\n\n    // Check if the requested port is available on both IPv4 and IPv6.\n    // If not, offer to find an available port by incrementing (unless non-interactive).\n    let listen_addr = if let Some((host, port_str)) = listen_addr.rsplit_once(':') {\n        if let Ok(requested_port) = port_str.parse::<u16>() {\n            if !is_port_available(host, requested_port) {\n                if non_interactive {\n                    anyhow::bail!(\n                        \"Port {} is already in use. Please free up the port or specify a different port with --listen-addr.\",\n                        requested_port\n                    );\n                }\n                // Port is in use, try to find an alternative\n                match find_available_port(host, requested_port.saturating_add(1), 100) {\n                    Some(available_port) => {\n                        let question = format!(\n                            \"Port {} is already in use. Would you like to use port {} instead?\",\n                            requested_port, available_port\n                        );\n                        if prompt_yes_no(&question) {\n                            format!(\"{}:{}\", host, available_port)\n                        } else {\n                            anyhow::bail!(\n                                \"Port {} is already in use. Please free up the port or specify a different port with --listen-addr.\",\n                                requested_port\n                            );\n                        }\n                    }\n                    None => {\n                        anyhow::bail!(\n                            \"Port {} is already in use and could not find an available port nearby. \\\n                             Please free up the port or specify a different port with --listen-addr.\",\n                            requested_port\n                        );\n                    }\n                }\n            } else {\n                listen_addr.to_string()\n            }\n        } else {\n            listen_addr.to_string()\n        }\n    } else {\n        listen_addr.to_string()\n    };\n\n    let tcp = TcpListener::bind(&listen_addr).await.context(format!(\n        \"failed to bind the SpacetimeDB server to '{listen_addr}', please check that the address is valid and not already in use\"\n    ))?;\n    socket2::SockRef::from(&tcp).set_nodelay(true)?;\n    log::info!(\"Starting SpacetimeDB listening on {}\", tcp.local_addr()?);\n\n    if let Some(pg_port) = pg_port {\n        let server_addr = listen_addr.split(':').next().unwrap();\n        let tcp_pg = TcpListener::bind(format!(\"{server_addr}:{pg_port}\")).await.context(format!(\n            \"failed to bind the SpacetimeDB PostgreSQL wire protocol server to {server_addr}:{pg_port}, please check that the port is valid and not already in use\"\n        ))?;\n\n        let notify = Arc::new(tokio::sync::Notify::new());\n        let shutdown_notify = notify.clone();\n        tokio::select! {\n            _ = pg_server::start_pg(notify.clone(), ctx, tcp_pg) => {},\n            _ = axum::serve(tcp, service).with_graceful_shutdown(async move  {\n                shutdown_notify.notified().await;\n            }) => {},\n            _ = tokio::signal::ctrl_c() => {\n                println!(\"Shutting down servers...\");\n                notify.notify_waiters(); // Notify all tasks\n            }\n        }\n    } else {\n        log::warn!(\"PostgreSQL wire protocol server disabled\");\n        axum::serve(tcp, service)\n            .with_graceful_shutdown(async {\n                tokio::signal::ctrl_c().await.expect(\"failed to install Ctrl+C handler\");\n                log::info!(\"Shutting down server...\");\n            })\n            .await?;\n    }\n\n    Ok(())\n}\n\n/// Check if a port is available on the requested host for both IPv4 and IPv6.\n///\n/// On macOS (and some other systems), `localhost` can resolve to both IPv4 (127.0.0.1)\n/// and IPv6 (::1). If SpacetimeDB binds only to IPv4 but another service is using the\n/// same port on IPv6, browsers may connect to the wrong service depending on which\n/// address they try first.\n///\n/// This function checks both the requested IPv4 address and its IPv6 equivalent:\n/// - 127.0.0.1 -> also checks ::1\n/// - 0.0.0.0 -> also checks ::\n/// - 10.1.1.1 -> also checks ::ffff:10.1.1.1 (IPv4-mapped IPv6)\n///\n/// Note: There is a small race condition between this check and the actual bind -\n/// another process could grab the port in between. This is unlikely in practice\n/// and the actual bind will fail with a clear error if it happens.\npub fn is_port_available(host: &str, port: u16) -> bool {\n    let requested = match parse_host(host) {\n        Some(r) => r,\n        None => return false, // invalid host string => treat as not available\n    };\n\n    let sockets = match get_sockets_info(AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6, ProtocolFlags::TCP) {\n        Ok(s) => s,\n        Err(_) => return false, // if we can't inspect sockets, fail closed\n    };\n\n    for si in sockets {\n        let tcp = match si.protocol_socket_info {\n            ProtocolSocketInfo::Tcp(tcp_si) => tcp_si,\n            _ => continue,\n        };\n\n        if tcp.state != TcpState::Listen {\n            continue;\n        }\n\n        if tcp.local_port != port {\n            continue;\n        }\n\n        if conflicts(requested, tcp.local_addr) {\n            return false;\n        }\n    }\n\n    true\n}\n\n#[derive(Debug, Clone, Copy)]\nenum RequestedHost {\n    Localhost,\n    Ip(IpAddr),\n}\n\nfn parse_host(host: &str) -> Option<RequestedHost> {\n    let host = host.trim();\n\n    // Allow common bracketed IPv6 formats like \"[::1]\"\n    let host = host.strip_prefix('[').and_then(|s| s.strip_suffix(']')).unwrap_or(host);\n\n    if host.eq_ignore_ascii_case(\"localhost\") {\n        return Some(RequestedHost::Localhost);\n    }\n\n    host.parse::<IpAddr>().ok().map(RequestedHost::Ip)\n}\n\nfn conflicts(requested: RequestedHost, listener_addr: IpAddr) -> bool {\n    match requested {\n        RequestedHost::Localhost => match listener_addr {\n            // localhost should conflict with loopback and wildcards in each family\n            IpAddr::V4(v4) => v4.is_loopback() || v4.is_unspecified(),\n            IpAddr::V6(v6) => v6.is_loopback() || v6.is_unspecified(),\n        },\n\n        RequestedHost::Ip(IpAddr::V4(req_v4)) => match listener_addr {\n            IpAddr::V4(l_v4) => {\n                if req_v4.is_unspecified() {\n                    // 0.0.0.0 conflicts with any IPv4 listener\n                    true\n                } else if req_v4.is_loopback() {\n                    // 127.0.0.1 conflicts with 127.0.0.1 and 0.0.0.0\n                    l_v4 == req_v4 || l_v4.is_unspecified()\n                } else {\n                    // specific IPv4 conflicts with that IPv4 and 0.0.0.0\n                    l_v4 == req_v4 || l_v4.is_unspecified()\n                }\n            }\n            IpAddr::V6(l_v6) => {\n                if req_v4.is_unspecified() {\n                    // special case: 0.0.0.0 conflicts with :: (and vice versa)\n                    l_v6.is_unspecified()\n                } else if req_v4.is_loopback() {\n                    // special case: 127.0.0.1 conflicts with ::1 (and vice versa)\n                    l_v6.is_loopback()\n                        // and treat IPv6 wildcard as conflicting with IPv4 loopback per your table\n                        || l_v6.is_unspecified()\n                        // also consider rare IPv4-mapped IPv6 listeners\n                        || l_v6.to_ipv4_mapped() == Some(req_v4)\n                } else {\n                    // specific IPv4 should conflict with IPv6 wildcard (::) per your table\n                    l_v6.is_unspecified() || l_v6.to_ipv4_mapped() == Some(req_v4)\n                }\n            }\n        },\n\n        RequestedHost::Ip(IpAddr::V6(req_v6)) => match listener_addr {\n            IpAddr::V6(l_v6) => {\n                if req_v6.is_unspecified() {\n                    // :: conflicts with any IPv6 listener\n                    true\n                } else if req_v6.is_loopback() {\n                    // ::1 conflicts with ::1 and :: (and also with 127.0.0.1 via IPv4 branch below)\n                    l_v6 == req_v6 || l_v6.is_unspecified()\n                } else {\n                    // specific IPv6 conflicts with itself and ::\n                    l_v6 == req_v6 || l_v6.is_unspecified()\n                }\n            }\n            IpAddr::V4(l_v4) => {\n                if req_v6.is_unspecified() {\n                    // :: conflicts with any IPv4 listener (matches your table)\n                    true\n                } else if req_v6.is_loopback() {\n                    // special case: ::1 conflicts with 127.0.0.1 (and vice versa)\n                    l_v4.is_loopback()\n                } else {\n                    // Not required by your rules: specific IPv6 does NOT conflict with IPv4 listeners.\n                    false\n                }\n            }\n        },\n    }\n}\n\n/// Find an available port starting from the requested port.\n/// Returns the first port that is available on both IPv4 and IPv6.\nfn find_available_port(host: &str, requested_port: u16, max_attempts: u16) -> Option<u16> {\n    for offset in 0..max_attempts {\n        let port = requested_port.saturating_add(offset);\n        if port == 0 || port == u16::MAX {\n            break;\n        }\n        if is_port_available(host, port) {\n            return Some(port);\n        }\n    }\n    None\n}\n\n/// Prompt the user with a yes/no question. Returns true if they answer yes.\nfn prompt_yes_no(question: &str) -> bool {\n    print!(\"{} [y/N] \", question);\n    io::stdout().flush().ok();\n\n    let mut input = String::new();\n    if io::stdin().read_line(&mut input).is_err() {\n        return false;\n    }\n\n    matches!(input.trim().to_lowercase().as_str(), \"y\" | \"yes\")\n}\n\nfn banner() {\n    println!(\n        r#\"\n┌───────────────────────────────────────────────────────────────────────────────────────────────────────┐\n│                                                                                                       │\n│                                                                                                       │\n│                                                                              ⢀⠔⠁                      │\n│                                                                            ⣠⡞⠁                        │\n│                                              ⣀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣀⣀⣀⣀⣀⣀⣀⣤⣤⡴⠒    ⢀⣠⡾⠋                          │\n│                                         ⢀⣤⣶⣾88888888888888888888⠿⠋    ⢀⣴8⡟⠁                           │\n│                                      ⢀⣤⣾88888⡿⠿⠛⠛⠛⠛⠛⠛⠛⠛⠻⠿88888⠟⠁    ⣠⣾88⡟                             │\n│                                    ⢀⣴88888⠟⠋⠁ ⣀⣤⠤⠶⠶⠶⠶⠶⠤⣤⣀ ⠉⠉⠉    ⢀⣴⣾888⡟                              │\n│                                   ⣠88888⠋  ⣠⠶⠋⠉         ⠉⠙⠶⣄   ⢀⣴888888⠃                              │\n│                                  ⣰8888⡟⠁ ⣰⠟⠁               ⠈⠻⣆ ⠈⢿888888                               │\n│                                 ⢠8888⡟  ⡼⠁                   ⠈⢧ ⠈⢿8888⡿                               │\n│                                 ⣼8888⠁ ⢸⠇                     ⠸⡇ ⠘8888⣷                               │\n│                                 88888  8                       8  88888                               │\n│                                 ⢿8888⡄ ⢸⡆                     ⢰⡇ ⢀8888⡟                               │\n│                                 ⣾8888⣷⡀ ⢳⡀                   ⢀⡞  ⣼8888⠃                               │\n│                                 888888⣷⡀ ⠹⣦⡀               ⢀⣴⠏ ⢀⣼8888⠏                                │\n│                                ⢠888888⠟⠁   ⠙⠶⣄⣀         ⣀⣠⠶⠋  ⣠88888⠋                                 │\n│                                ⣼888⡿⠟⠁    ⣀⣀⣀ ⠉⠛⠒⠶⠶⠶⠶⠶⠒⠛⠉ ⢀⣠⣴88888⠟⠁                                  │\n│                               ⣼88⡿⠋    ⢀⣴88888⣶⣦⣤⣤⣤⣤⣤⣤⣤⣤⣶⣾88888⡿⠛⠁                                    │\n│                             ⢀⣼8⠟⠁    ⣠⣶88888888888888888888⡿⠿⠛⠁                                       │\n│                            ⣠⡾⠋⠁    ⠤⠞⠛⠛⠉⠉⠉⠉⠉⠉⠉⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠉⠉                                            │\n│                          ⢀⡼⠋                                                                          │\n│                        ⢀⠔⠁                                                                            │\n│                                                                                                       │\n│                                                                                                       │\n│  .d8888b.                                     888    d8b                        8888888b.  888888b.   │\n│ d88P  Y88b                                    888    Y8P                        888  \"Y88b 888  \"88b  │\n│ Y88b.                                         888                               888    888 888  .88P  │\n│  \"Y888b.   88888b.   8888b.   .d8888b .d88b.  888888 888 88888b.d88b.   .d88b.  888    888 8888888K.  │\n│     \"Y88b. 888 \"88b     \"88b d88P\"   d8P  Y8b 888    888 888 \"888 \"88b d8P  Y8b 888    888 888  \"Y88b │\n│       \"888 888  888 .d888888 888     88888888 888    888 888  888  888 88888888 888    888 888    888 │\n│ Y88b  d88P 888 d88P 888  888 Y88b.   Y8b.     Y88b.  888 888  888  888 Y8b.     888  .d88P 888   d88P │\n│  \"Y8888P\"  88888P\"  \"Y888888  \"Y8888P \"Y8888   \"Y888 888 888  888  888  \"Y8888  8888888P\"  8888888P\"  │\n│            888                                                                                        │\n│            888                                                                                        │\n│            888                                                                                        │\n│                                  \"Multiplayer at the speed of light\"                                  │\n└───────────────────────────────────────────────────────────────────────────────────────────────────────┘\n    \"#\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::time::Duration;\n\n    #[test]\n    fn options_from_partial_toml() {\n        let toml = r#\"\n            [logs]\n            directives = [\n                \"banana_shake=strawberry\",\n            ]\n\n            [websocket]\n            idle-timeout = \"1min\"\n            close-handshake-timeout = \"500ms\"\n\"#;\n\n        let config: ConfigFile = toml::from_str(toml).unwrap();\n\n        // `spacetimedb::config::ConfigFile` doesn't implement `PartialEq`,\n        // so check `common` in a pedestrian way.\n        assert_eq!(&config.common.logs.directives, &[\"banana_shake=strawberry\"]);\n        assert!(config.common.certificate_authority.is_none());\n\n        assert_eq!(\n            config.websocket,\n            WebSocketOptions {\n                idle_timeout: Duration::from_secs(60),\n                close_handshake_timeout: Duration::from_millis(500),\n                ..<_>::default()\n            }\n        );\n    }\n}\n"
  },
  {
    "path": "crates/standalone/src/util.rs",
    "content": "use std::env;\nuse std::path::Path;\n\n/// Returns the name of the current executable without the tail extension and the path.\npub fn get_exe_name() -> String {\n    let exe_path = env::current_exe().expect(\"Failed to get executable path\");\n    let executable_name = Path::new(&exe_path)\n        .file_stem()\n        .and_then(|n| n.to_str())\n        .unwrap_or(\"unknown\");\n    executable_name.to_string()\n}\n"
  },
  {
    "path": "crates/standalone/src/version.rs",
    "content": "pub const CLI_VERSION: &str = env!(\"CARGO_PKG_VERSION\");\n\npub fn long_version() -> String {\n    format!(\n        \"spacetimedb tool version {CLI_VERSION}; spacetimedb-lib version {};\",\n        spacetimedb_lib::version::spacetimedb_lib_version()\n    )\n}\n"
  },
  {
    "path": "crates/subscription/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-subscription\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"The SpacetimeDB subscription compiler\"\n\n[dependencies]\nanyhow.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-execution.workspace = true\nspacetimedb-expr.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-primitives.workspace = true\nspacetimedb-physical-plan.workspace = true\nspacetimedb-query.workspace = true\nspacetimedb-schema.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/subscription/README.md",
    "content": "> ⚠️ **Unstable Crate** ⚠️\n>\n> The interface of this crate is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/subscription/src/lib.rs",
    "content": "use anyhow::{bail, Result};\nuse spacetimedb_data_structures::map::{HashCollectionExt as _, HashSet};\nuse spacetimedb_execution::{\n    pipelined::{\n        PipelinedExecutor, PipelinedIxDeltaJoin, PipelinedIxDeltaScanEq, PipelinedIxDeltaScanRange, PipelinedIxJoin,\n        PipelinedIxScanEq, PipelinedIxScanRange, PipelinedProject,\n    },\n    Datastore, DeltaStore, Row,\n};\nuse spacetimedb_expr::{check::SchemaView, expr::CollectViews};\nuse spacetimedb_lib::{identity::AuthCtx, metrics::ExecutionMetrics, query::Delta, AlgebraicValue};\nuse spacetimedb_physical_plan::plan::{IxJoin, IxScan, Label, PhysicalPlan, ProjectPlan, Sarg, TableScan, TupleField};\nuse spacetimedb_primitives::{ColId, ColList, IndexId, TableId, ViewId};\nuse spacetimedb_query::compile_subscription;\nuse spacetimedb_schema::table_name::TableName;\nuse std::ops::RangeBounds;\n\n/// A subscription is a view over a particular table.\n/// How do we incrementally maintain that view?\n/// These are the query fragments that are required.\n/// See [Self::compile_from_plan] for how to generate them.\n#[derive(Debug)]\nstruct Fragments {\n    /// Plan fragments that return rows to insert.\n    /// For joins there will be 4 fragments,\n    /// but for selects only one.\n    insert_plans: Vec<PipelinedProject>,\n    /// Plan fragments that return rows to delete.\n    /// For joins there will be 4 fragments,\n    /// but for selects only one.\n    delete_plans: Vec<PipelinedProject>,\n}\n\nimpl Fragments {\n    /// Returns the index ids from which this fragment reads.\n    fn index_ids(&self) -> impl Iterator<Item = (TableId, IndexId)> + use<> {\n        let mut index_ids = HashSet::new();\n        for plan in self.insert_plans.iter().chain(self.delete_plans.iter()) {\n            plan.visit(&mut |plan| match plan {\n                PipelinedExecutor::IxScanEq(PipelinedIxScanEq { table_id, index_id, .. })\n                | PipelinedExecutor::IxScanRange(PipelinedIxScanRange { table_id, index_id, .. })\n                | PipelinedExecutor::IxDeltaScanEq(PipelinedIxDeltaScanEq { table_id, index_id, .. })\n                | PipelinedExecutor::IxDeltaScanRange(PipelinedIxDeltaScanRange { table_id, index_id, .. })\n                | PipelinedExecutor::IxJoin(PipelinedIxJoin {\n                    rhs_table: table_id,\n                    rhs_index: index_id,\n                    ..\n                })\n                | PipelinedExecutor::IxDeltaJoin(PipelinedIxDeltaJoin {\n                    rhs_table: table_id,\n                    rhs_index: index_id,\n                    ..\n                }) => {\n                    index_ids.insert((*table_id, *index_id));\n                }\n                _ => {}\n            });\n        }\n        index_ids.into_iter()\n    }\n\n    /// A subscription is just a view of a particular table.\n    /// Here we compute the rows that are to be inserted into that view,\n    /// and evaluate a closure over each one.\n    fn for_each_insert<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Row<'a>) -> Result<()>,\n    ) -> Result<()> {\n        for plan in &self.insert_plans {\n            if !plan.is_empty(tx) {\n                plan.execute(tx, metrics, f)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// A subscription is just a view of a particular table.\n    /// Here we compute the rows that are to be removed from that view,\n    /// and evaluate a closure over each one.\n    fn for_each_delete<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Row<'a>) -> Result<()>,\n    ) -> Result<()> {\n        for plan in &self.delete_plans {\n            if !plan.is_empty(tx) {\n                plan.execute(tx, metrics, f)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Which fragments are required for incrementally updating a subscription?\n    /// This is most interesting in the case of a join.\n    ///\n    /// Let `V`  denote the join between tables `R` and `S` at time `t`.\n    /// Let `V'` denote the same join at time `t+1`.\n    ///\n    /// We then have the following equality\n    ///\n    /// ```text\n    /// V' = V U dv\n    /// ```\n    ///\n    /// where `dv` is called the delta of `V`.\n    ///\n    /// So how do we compute `dv` incrementally?\n    /// That is, without evaluating `R' x S'`.\n    /// and without access to the state at time `t`.\n    ///\n    /// Given the following notation:\n    ///\n    /// ```text\n    /// x: The relational join operator\n    /// U: union\n    /// -: difference\n    ///\n    /// dv: The difference or delta between V and V'\n    ///\n    /// dv(+): Rows in V' that are not in V\n    /// dv(-): Rows in V  that are not in V'\n    /// ```\n    ///\n    /// we derive the following equations\n    ///\n    /// ```text\n    /// V  = R x S\n    ///    = RS\n    ///\n    /// V' = V  U dv\n    ///    = RS U dv\n    ///\n    /// V' = R' x S'\n    ///    = (R U dr) x (S U ds)\n    ///    = RS U Rds U drS U drds\n    ///\n    /// dv = Rds U drS U drds\n    ///    = (R' - dr)ds U dr(S' - ds) U drds\n    ///    = R'ds - drds U drS' - drds U drds\n    ///    = R'ds U drS' - drds\n    ///    = R'(ds(+) - ds(-)) U (dr(+) - dr(-))S' - (dr(+) - dr(-))(ds(+) - ds(-))\n    ///    = R'ds(+)\n    ///         - R'ds(-)\n    ///         U dr(+)S'\n    ///         - dr(-)S'\n    ///         - dr(+)ds(+)\n    ///         U dr(+)ds(-)\n    ///         U dr(-)ds(+)\n    ///         - dr(-)ds(-)\n    ///    = R'ds(+)\n    ///         U dr(+)S'\n    ///         U dr(+)ds(-)\n    ///         U dr(-)ds(+)\n    ///         - R'ds(-)\n    ///         - dr(-)S'\n    ///         - dr(+)ds(+)\n    ///         - dr(-)ds(-)\n    ///\n    /// dv(+) = R'ds(+) U dr(+)S' U dr(+)ds(-) U dr(-)ds(+)\n    /// dv(-) = R'ds(-) U dr(-)S' U dr(+)ds(+) U dr(-)ds(-)\n    /// ```\n    fn compile_from_plan(plan: &ProjectPlan, tables: &[Label], auth: &AuthCtx) -> Result<Self> {\n        /// Mutate a query plan by turning a table scan into a delta scan\n        fn mut_plan(plan: &mut ProjectPlan, relvar: Label, delta: Delta) {\n            plan.visit_mut(&mut |plan| match plan {\n                PhysicalPlan::TableScan(\n                    scan @ TableScan {\n                        limit: None,\n                        delta: None,\n                        ..\n                    },\n                    alias,\n                ) if alias == &relvar => {\n                    scan.delta = Some(delta);\n                }\n                _ => {}\n            });\n        }\n\n        /// Return a new plan with delta scans for the given tables\n        fn new_plan(plan: &ProjectPlan, tables: &[(Label, Delta)], auth: &AuthCtx) -> Result<PipelinedProject> {\n            let mut plan = plan.clone();\n            for (alias, delta) in tables {\n                mut_plan(&mut plan, *alias, *delta);\n            }\n            plan.optimize(auth).map(PipelinedProject::from)\n        }\n\n        match tables {\n            [dr] => Ok(Fragments {\n                insert_plans: vec![new_plan(plan, &[(*dr, Delta::Inserts)], auth)?],\n                delete_plans: vec![new_plan(plan, &[(*dr, Delta::Deletes)], auth)?],\n            }),\n            [dr, ds] => Ok(Fragments {\n                insert_plans: vec![\n                    new_plan(\n                        // dr(+)S'\n                        plan,\n                        &[(*dr, Delta::Inserts)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // R'ds(+)\n                        plan,\n                        &[(*ds, Delta::Inserts)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // dr(+)ds(-)\n                        plan,\n                        &[(*dr, Delta::Inserts), (*ds, Delta::Deletes)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // dr(-)ds(+)\n                        plan,\n                        &[(*dr, Delta::Deletes), (*ds, Delta::Inserts)],\n                        auth,\n                    )?,\n                ],\n                delete_plans: vec![\n                    new_plan(\n                        // dr(-)S'\n                        plan,\n                        &[(*dr, Delta::Deletes)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // R'ds(-)\n                        plan,\n                        &[(*ds, Delta::Deletes)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // dr(+)ds(+)\n                        plan,\n                        &[(*dr, Delta::Inserts), (*ds, Delta::Inserts)],\n                        auth,\n                    )?,\n                    new_plan(\n                        // dr(-)ds(-)\n                        plan,\n                        &[(*dr, Delta::Deletes), (*ds, Delta::Deletes)],\n                        auth,\n                    )?,\n                ],\n            }),\n            _ => bail!(\"Invalid number of tables in subscription: {}\", tables.len()),\n        }\n    }\n}\n\n/// A join edge is used for pruning queries when evaluating subscription updates.\n///\n/// If we have the following subscriptions:\n/// ```sql\n/// SELECT a.* FROM a JOIN b ON a.id = b.id WHERE b.x = 1\n/// SELECT a.* FROM a JOIN b ON a.id = b.id WHERE b.x = 2\n/// ...\n/// SELECT a.* FROM a JOIN b ON a.id = b.id WHERE b.x = n\n/// ```\n///\n/// Whenever `a` is updated, only the relevant queries are evaluated.\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]\npub struct JoinEdge {\n    /// The [`TableId`] for `a`\n    pub lhs_table: TableId,\n    /// The [`TableId`] for `b`\n    pub rhs_table: TableId,\n    /// The [`ColId`] for `a.id`\n    pub lhs_join_col: ColId,\n    /// The [`ColId`] for `b.id`\n    pub rhs_join_col: ColId,\n    /// The [`ColId`] for `b.x`\n    pub rhs_col: ColId,\n}\n\nimpl JoinEdge {\n    /// A helper method for finding a range of join edges for a particular table in a sorted set.\n    fn min_for_table(lhs_table: TableId) -> Self {\n        Self {\n            lhs_table,\n            rhs_table: TableId(u32::MIN),\n            lhs_join_col: ColId(u16::MIN),\n            rhs_join_col: ColId(u16::MIN),\n            rhs_col: ColId(u16::MIN),\n        }\n    }\n\n    /// A helper method for finding a range of join edges for a particular table in a sorted set.\n    fn max_for_table(lhs_table: TableId) -> Self {\n        Self {\n            lhs_table,\n            rhs_table: TableId(u32::MAX),\n            lhs_join_col: ColId(u16::MAX),\n            rhs_join_col: ColId(u16::MAX),\n            rhs_col: ColId(u16::MAX),\n        }\n    }\n\n    /// A helper method for finding a range of join edges for a particular table in a sorted set.\n    pub fn range_for_table(lhs_table: TableId) -> impl RangeBounds<Self> {\n        Self::min_for_table(lhs_table)..=Self::max_for_table(lhs_table)\n    }\n}\n\n/// A subscription defines a view over a table\n#[derive(Debug)]\npub struct SubscriptionPlan {\n    /// To which table are we subscribed?\n    return_id: TableId,\n    /// To which table are we subscribed?\n    return_name: TableName,\n    /// A subscription can read from multiple tables.\n    /// From which tables do we read?\n    table_ids: Vec<TableId>,\n    /// The plan fragments for updating the view\n    fragments: Fragments,\n    /// The optimized plan without any delta scans\n    plan_opt: ProjectPlan,\n}\n\nimpl CollectViews for SubscriptionPlan {\n    fn collect_views(&self, views: &mut HashSet<ViewId>) {\n        self.plan_opt.collect_views(views);\n    }\n}\n\nimpl SubscriptionPlan {\n    /// Is this a plan for a join?\n    pub fn is_join(&self) -> bool {\n        self.fragments.insert_plans.len() > 1 && self.fragments.delete_plans.len() > 1\n    }\n\n    /// Does this plan return rows from a view?\n    pub fn is_view(&self) -> bool {\n        self.plan_opt.returns_view_table()\n    }\n\n    /// Does this plan return rows from an event table?\n    pub fn returns_event_table(&self) -> bool {\n        self.plan_opt.return_table().is_some_and(|schema| schema.is_event)\n    }\n\n    /// The number of columns returned.\n    /// Only relevant if [`Self::is_view`] is true.\n    pub fn num_cols(&self) -> usize {\n        self.plan_opt\n            .return_table()\n            .map(|schema| schema.num_cols())\n            .unwrap_or_default()\n    }\n\n    /// The number of private columns returned.\n    /// Only relevant if [`Self::is_view`] is true.\n    pub fn num_private_cols(&self) -> usize {\n        self.plan_opt\n            .return_table()\n            .map(|schema| schema.num_private_cols())\n            .unwrap_or_default()\n    }\n\n    /// To which table does this plan subscribe?\n    pub fn subscribed_table_id(&self) -> TableId {\n        self.return_id\n    }\n\n    /// To which table does this plan subscribe?\n    pub fn subscribed_table_name(&self) -> &TableName {\n        &self.return_name\n    }\n\n    /// From which tables does this plan read?\n    pub fn table_ids(&self) -> impl Iterator<Item = TableId> + '_ {\n        self.table_ids.iter().copied()\n    }\n\n    /// The optimized plan without any delta scans\n    pub fn optimized_physical_plan(&self) -> &ProjectPlan {\n        &self.plan_opt\n    }\n\n    /// From which indexes does this plan read?\n    pub fn index_ids(&self) -> impl Iterator<Item = (TableId, IndexId)> + use<> {\n        self.fragments.index_ids()\n    }\n\n    /// A subscription is just a view of a particular table.\n    /// Here we compute the rows that are to be inserted into that view,\n    /// and evaluate a closure over each one.\n    pub fn for_each_insert<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Row<'a>) -> Result<()>,\n    ) -> Result<()> {\n        self.fragments.for_each_insert(tx, metrics, f)\n    }\n\n    /// A subscription is just a view of a particular table.\n    /// Here we compute the rows that are to be removed from that view,\n    /// and evaluate a closure over each one.\n    pub fn for_each_delete<'a, Tx: Datastore + DeltaStore>(\n        &self,\n        tx: &'a Tx,\n        metrics: &mut ExecutionMetrics,\n        f: &mut dyn FnMut(Row<'a>) -> Result<()>,\n    ) -> Result<()> {\n        self.fragments.for_each_delete(tx, metrics, f)\n    }\n\n    /// Returns a join edge for this query if it has one.\n    ///\n    /// Requirements include:\n    /// 1. Unique join index\n    /// 2. Single column index lookup on the rhs table\n    /// 3. No self joins\n    pub fn join_edge(&self) -> Option<(JoinEdge, AlgebraicValue)> {\n        if !self.is_join() {\n            return None;\n        }\n        let mut join_edge = None;\n        self.plan_opt.visit(&mut |op| match op {\n            PhysicalPlan::IxJoin(\n                IxJoin {\n                    lhs,\n                    rhs,\n                    rhs_field: lhs_join_col,\n                    lhs_field:\n                        TupleField {\n                            field_pos: rhs_join_col,\n                            ..\n                        },\n                    ..\n                },\n                _,\n            ) if rhs.table_id == self.return_id => match &**lhs {\n                PhysicalPlan::IxScan(\n                    IxScan {\n                        schema,\n                        prefix,\n                        arg: Sarg::Eq(rhs_col, rhs_val),\n                        ..\n                    },\n                    _,\n                ) if schema.table_id != self.return_id\n                    && prefix.is_empty()\n                    && schema.is_unique(&ColList::new((*rhs_join_col).into())) =>\n                {\n                    let lhs_table = self.return_id;\n                    let rhs_table = schema.table_id;\n                    let rhs_col = *rhs_col;\n                    let rhs_val = rhs_val.clone();\n                    let lhs_join_col = *lhs_join_col;\n                    let rhs_join_col = (*rhs_join_col).into();\n                    let edge = JoinEdge {\n                        lhs_table,\n                        rhs_table,\n                        lhs_join_col,\n                        rhs_join_col,\n                        rhs_col,\n                    };\n                    join_edge = Some((edge, rhs_val));\n                }\n                _ => {}\n            },\n            _ => {}\n        });\n        join_edge\n    }\n\n    /// Generate a plan for incrementally maintaining a subscription\n    pub fn compile(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> Result<(Vec<Self>, bool)> {\n        let (plans, return_id, return_name, has_param) = compile_subscription(sql, tx, auth)?;\n\n        /// Does this plan have any non-index joins?\n        fn has_non_index_join(plan: &PhysicalPlan) -> bool {\n            plan.any(&|op| matches!(op, PhysicalPlan::HashJoin(..) | PhysicalPlan::NLJoin(..)))\n        }\n\n        /// What tables are involved in this plan?\n        fn table_ids_for_plan(plan: &PhysicalPlan) -> (Vec<TableId>, Vec<Label>) {\n            let mut table_aliases = vec![];\n            let mut table_ids = vec![];\n            plan.visit(&mut |plan| match plan {\n                PhysicalPlan::TableScan(\n                    TableScan {\n                        // What table are we reading?\n                        schema,\n                        ..\n                    },\n                    alias,\n                )\n                | PhysicalPlan::IxScan(\n                    IxScan {\n                        // What table are we reading?\n                        schema,\n                        ..\n                    },\n                    alias,\n                ) => {\n                    table_aliases.push(*alias);\n                    table_ids.push(schema.table_id);\n                }\n                _ => {}\n            });\n            (table_ids, table_aliases)\n        }\n\n        let mut subscriptions = vec![];\n\n        for plan in plans {\n            let plan_opt = plan.clone().optimize(auth)?;\n\n            if has_non_index_join(&plan_opt) {\n                bail!(\"Subscriptions require indexes on join columns\")\n            }\n\n            if plan_opt.reads_from_event_table() {\n                bail!(\"Event tables cannot be used as the lookup table in subscription joins\")\n            }\n\n            let (table_ids, table_aliases) = table_ids_for_plan(&plan);\n\n            let fragments = Fragments::compile_from_plan(&plan, &table_aliases, auth)?;\n\n            subscriptions.push(Self {\n                return_id,\n                return_name: return_name.clone(),\n                table_ids,\n                plan_opt,\n                fragments,\n            });\n        }\n\n        Ok((subscriptions, has_param))\n    }\n}\n"
  },
  {
    "path": "crates/table/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-table\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\ndescription = \"A database Table implementation and friends\"\nrust-version.workspace = true\n\n[[bench]]\nname = \"pointer_map\"\nharness = false\n\n[[bench]]\nname = \"page\"\nharness = false\n\n[[bench]]\nname = \"var_len_visitor\"\nharness = false\n\n[[bench]]\nname = \"page_manager\"\nharness = false\n\n[features]\n# Allows using `Arbitrary` impls defined in this crate.\nproptest = [\"dep:proptest\", \"dep:proptest-derive\", \"spacetimedb-sats/proptest\"]\n\n# Needed for miri\nblake3_pure = [\"blake3/pure\"]\n\n[dependencies]\nspacetimedb-data-structures = { workspace = true, features = [\"memory-usage\"] }\nspacetimedb-memory-usage = { workspace = true, features = [\"hash_map\", \"ethnum\", \"smallvec\"] }\nspacetimedb-primitives.workspace = true\nspacetimedb-sats = { workspace = true, features = [\"blake3\"] }\nspacetimedb-lib = { workspace = true, features = [\"memory-usage\"] }\nspacetimedb-schema.workspace = true\n\nahash.workspace = true\nblake3.workspace = true\nbytemuck = { workspace = true, features = [\"derive\"] }\ndecorum.workspace = true\nderive_more.workspace = true\nenum-as-inner.workspace = true\nfoldhash.workspace = true\nhashbrown.workspace = true\nitertools.workspace = true\nsmallvec.workspace = true\nthiserror.workspace = true\ncrossbeam-queue.workspace = true\n\n# For the 'proptest' feature.\nproptest = { workspace = true, optional = true }\nproptest-derive = { workspace = true, optional = true }\n\n[dev-dependencies]\nspacetimedb-schema = { path = \"../schema\", features = [\"test\"] }\nspacetimedb-sats = { path = \"../sats\", features = [\"proptest\"] }\ncriterion.workspace = true\nproptest.workspace = true\nproptest-derive.workspace = true\nrand.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/table/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/table/benches/page.rs",
    "content": "use core::hash::BuildHasher;\nuse core::mem;\nuse core::time::Duration;\nuse criterion::measurement::{Measurement, WallTime};\nuse criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion, Throughput};\nuse rand::rngs::StdRng;\nuse rand::seq::SliceRandom;\nuse rand::{Rng, SeedableRng};\nuse spacetimedb_sats::algebraic_value::ser::ValueSerializer;\nuse spacetimedb_sats::layout::{row_size_for_type, RowTypeLayout};\nuse spacetimedb_sats::{product, AlgebraicType, AlgebraicValue, ArrayValue, ProductType, ProductValue};\nuse spacetimedb_table::bflatn_from::serialize_row_from_page;\nuse spacetimedb_table::bflatn_to::write_row_to_page;\nuse spacetimedb_table::blob_store::NullBlobStore;\nuse spacetimedb_table::eq::eq_row_in_page;\nuse spacetimedb_table::indexes::{Byte, Bytes, PageOffset, RowHash};\nuse spacetimedb_table::page::Page;\nuse spacetimedb_table::row_hash::hash_row_in_page;\nuse spacetimedb_table::row_type_visitor::{row_type_visitor, VarLenVisitorProgram};\nuse spacetimedb_table::static_layout::StaticLayout;\nuse spacetimedb_table::var_len::{AlignedVarLenOffsets, NullVarLenVisitor, VarLenGranule, VarLenMembers, VarLenRef};\n\nfn time<R>(acc: &mut Duration, body: impl FnOnce() -> R) -> R {\n    let start = WallTime.start();\n    let ret = body();\n    let end = WallTime.end(start);\n    *acc = WallTime.add(acc, &end);\n    black_box(ret)\n}\n\nfn iter_time_with_page<P, B>(\n    b: &mut Bencher,\n    page: &mut Page,\n    mut pre: impl FnMut(&mut Page) -> P,\n    mut body: impl FnMut(P, u64, &mut Page) -> B,\n) {\n    b.iter_custom(|num_iters| {\n        let mut elapsed = WallTime.zero();\n        for i in 0..num_iters {\n            let p = pre(page);\n            black_box(&page);\n            time(&mut elapsed, || body(p, i, page));\n            black_box(&page);\n        }\n        elapsed\n    })\n}\n\nfn clear_zero(page: &mut Page) {\n    page.clear();\n    unsafe { page.zero_data() };\n}\n\n// Strictly this would be unsafe,\n// since it causes UB when applied to types that contain padding/`poison`,\n// but it's a benchmark so who cares.\nfn as_bytes<T>(t: &T) -> &Bytes {\n    let ptr = (t as *const T).cast::<Byte>();\n    unsafe { std::slice::from_raw_parts(ptr, mem::size_of::<T>()) }\n}\n\n#[allow(clippy::missing_safety_doc)] // It's a benchmark, clippy. Who cares.\nunsafe trait Row {\n    fn row_type() -> ProductType;\n\n    fn var_len_visitor() -> VarLenVisitorProgram {\n        row_type_visitor(&Self::row_type().into())\n    }\n}\n\n#[allow(clippy::missing_safety_doc)] // It's a benchmark, clippy. Who cares.\n/// Apply only to types which:\n/// - Contain no padding bytes.\n/// - Contain no members which are stored BFLATN as var-len.\nunsafe trait FixedLenRow: Row + Sized {\n    fn as_bytes(&self) -> &Bytes {\n        as_bytes(self)\n    }\n\n    unsafe fn from_bytes(bytes: &Bytes) -> &Self {\n        let ptr = bytes.as_ptr();\n        debug_assert_eq!(ptr as usize % mem::align_of::<Self>(), 0);\n        debug_assert_eq!(bytes.len(), mem::size_of::<Self>());\n        unsafe { &*ptr.cast::<Self>() }\n    }\n\n    fn from_u64(u: u64) -> Self;\n}\n\nfn read_fixed_len<Row: FixedLenRow>(page: &Page, offset: PageOffset) -> &Row {\n    let row = black_box(page).get_row_data(offset, row_size_for_type::<Row>());\n    unsafe { Row::from_bytes(row) }\n}\n\nunsafe impl Row for u64 {\n    fn row_type() -> ProductType {\n        [AlgebraicType::U64].into()\n    }\n}\n\nunsafe impl FixedLenRow for u64 {\n    fn from_u64(u: u64) -> Self {\n        u\n    }\n}\n\n#[repr(C)]\nstruct U32x8 {\n    vals: [u32; 8],\n}\n\nunsafe impl Row for U32x8 {\n    fn row_type() -> ProductType {\n        [const { AlgebraicType::U32 }; 8].into()\n    }\n}\n\nunsafe impl FixedLenRow for U32x8 {\n    fn from_u64(u: u64) -> Self {\n        Self { vals: [u as u32; 8] }\n    }\n}\n\n#[repr(C)]\nstruct U32x64 {\n    vals: [u32; 64],\n}\n\nunsafe impl Row for U32x64 {\n    fn row_type() -> ProductType {\n        [const { AlgebraicType::U32 }; 64].into()\n    }\n}\n\nunsafe impl FixedLenRow for U32x64 {\n    fn from_u64(u: u64) -> Self {\n        Self { vals: [u as u32; 64] }\n    }\n}\n\nunsafe impl Row for VarLenRef {\n    fn row_type() -> ProductType {\n        [AlgebraicType::String].into()\n    }\n}\n\nconst fn rows_per_page<Row: FixedLenRow>() -> usize {\n    PageOffset::PAGE_END.idx() / row_size_for_type::<Row>().len()\n}\n\nfn var_len_rows_per_page(data_size_in_bytes: usize) -> usize {\n    let var_object_size = row_size_for_type::<VarLenRef>().len()\n        + VarLenGranule::bytes_to_granules(data_size_in_bytes).0 * VarLenGranule::SIZE.len();\n    PageOffset::PAGE_END.idx() / var_object_size\n}\n\nfn fill_with_fixed_len<Row: FixedLenRow>(page: &mut Page, val: Row, visitor: &impl VarLenMembers) {\n    while black_box(&*page).has_space_for_row(row_size_for_type::<Row>(), 0) {\n        let _ = black_box(unsafe {\n            black_box(&mut *page).insert_row(val.as_bytes(), &[] as &[&[u8]], visitor, &mut NullBlobStore)\n        });\n    }\n}\n\nfn insert_fixed_len_with_visitor<Row: FixedLenRow, V: VarLenMembers>(\n    c: &mut Criterion,\n    visitor: &V,\n    visitor_name: &str,\n) {\n    let group_name = format!(\"insert_fixed_len/{}/{}_bytes\", visitor_name, mem::size_of::<Row>());\n\n    let mut group = c.benchmark_group(&group_name);\n    group.throughput(Throughput::Bytes(\n        (rows_per_page::<Row>() * mem::size_of::<Row>()) as u64,\n    ));\n\n    group.bench_function(\"clean_page\", |b| {\n        let mut page = Page::new(row_size_for_type::<Row>());\n        unsafe { page.zero_data() };\n        iter_time_with_page(b, &mut page, clear_zero, |_, i, page| {\n            fill_with_fixed_len(page, Row::from_u64(i), visitor)\n        });\n    });\n\n    group.bench_function(\"dirty_page\", |b| {\n        // Setup: alloc and fill a page, so it's dirty.\n        let mut page = Page::new(row_size_for_type::<Row>());\n        let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n        fill_with_fixed_len(&mut page, Row::from_u64(0), visitor);\n        let pre = |page: &mut _| delete_all_shuffled::<Row>(page, &mut rng);\n        iter_time_with_page(b, &mut page, pre, |_, i, page| {\n            fill_with_fixed_len(page, Row::from_u64(i), visitor)\n        });\n    });\n}\n\nfn insert_fixed_len<Row: FixedLenRow>(c: &mut Criterion) {\n    insert_fixed_len_with_visitor::<Row, _>(c, &NullVarLenVisitor, \"NullVarLenVisitor\");\n    insert_fixed_len_with_visitor::<Row, _>(c, &AlignedVarLenOffsets::from_offsets(&[]), \"AlignedVarLenOffsets\");\n\n    let visitor = Row::var_len_visitor();\n    insert_fixed_len_with_visitor::<Row, _>(c, &visitor, \"VarLenVisitorProgram\");\n}\n\nfn delete_all_shuffled<R: Row>(page: &mut Page, rng: &mut impl Rng) {\n    let mut row_offsets = page.iter_fixed_len(row_size_for_type::<R>()).collect::<Vec<_>>();\n    // Asserts in here are fine; we don't time this function.\n    assert_eq!(row_offsets.len(), page.num_rows());\n    // Delete in a random order, so the freelist isn't in (reverse) order.\n    row_offsets.shuffle(rng);\n\n    let visitor = R::var_len_visitor();\n    for row in row_offsets {\n        unsafe { page.delete_row(row, row_size_for_type::<R>(), &visitor, &mut NullBlobStore) };\n    }\n    assert_eq!(page.num_rows(), 0);\n}\n\nfn fill_with_var_len(page: &mut Page, var_len: &[u8], visitor: &impl VarLenMembers) {\n    while black_box(&*page).has_space_for_row_with_objects(row_size_for_type::<VarLenRef>(), &[var_len]) {\n        let _ = black_box(unsafe {\n            black_box(&mut *page).insert_row(as_bytes(&VarLenRef::NULL), &[var_len], visitor, &mut NullBlobStore)\n        });\n    }\n}\n\nconst VL_SIZES: [usize; 6] = [\n    1,\n    VarLenGranule::DATA_SIZE,\n    VarLenGranule::DATA_SIZE * 2,\n    VarLenGranule::DATA_SIZE * 4,\n    VarLenGranule::DATA_SIZE * 8,\n    VarLenGranule::DATA_SIZE * 16,\n];\n\nfn insert_var_len_clean_page(c: &mut Criterion, visitor: &impl VarLenMembers, visitor_name: &str) {\n    let mut group = c.benchmark_group(format!(\"insert_var_len/{visitor_name}/clean_page\"));\n\n    for len_in_bytes in VL_SIZES {\n        group.throughput(Throughput::Bytes(\n            (var_len_rows_per_page(len_in_bytes) * len_in_bytes) as u64,\n        ));\n\n        group.bench_with_input(\n            BenchmarkId::new(\"fill_with_objs_of_size\", len_in_bytes),\n            &len_in_bytes,\n            |b, &len_in_bytes| {\n                let mut page = Page::new(row_size_for_type::<VarLenRef>());\n                unsafe { page.zero_data() };\n                // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n                let data = [0xa5u8].repeat(len_in_bytes);\n                iter_time_with_page(b, &mut page, clear_zero, |_, _, page| {\n                    fill_with_var_len(page, &data, visitor)\n                });\n            },\n        );\n    }\n}\n\nfn insert_var_len_dirty_page(c: &mut Criterion, visitor: &impl VarLenMembers, visitor_name: &str) {\n    let mut group = c.benchmark_group(format!(\"insert_var_len/{visitor_name}/dirty_page\"));\n\n    for len_in_bytes in VL_SIZES {\n        group.throughput(Throughput::Bytes(\n            (var_len_rows_per_page(len_in_bytes) * len_in_bytes) as u64,\n        ));\n\n        group.bench_with_input(\n            BenchmarkId::new(\"fill_with_objs_of_size\", len_in_bytes),\n            &len_in_bytes,\n            |b, &len_in_bytes| {\n                let mut page = Page::new(row_size_for_type::<VarLenRef>());\n                // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n                let data = [0xa5u8].repeat(len_in_bytes);\n                fill_with_var_len(&mut page, &data, visitor);\n\n                let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n\n                let pre = |page: &mut _| delete_all_shuffled::<VarLenRef>(page, &mut rng);\n                iter_time_with_page(b, &mut page, pre, |_, _, page| fill_with_var_len(page, &data, visitor));\n            },\n        );\n    }\n}\n\nfn insert_var_len(c: &mut Criterion) {\n    let offset_visitor = AlignedVarLenOffsets::from_offsets(&[0]);\n    insert_var_len_clean_page(c, &offset_visitor, \"AlignedVarLenOffsets\");\n    insert_var_len_dirty_page(c, &offset_visitor, \"AlignedVarLenOffsets\");\n\n    let program_visitor = VarLenRef::var_len_visitor();\n    insert_var_len_clean_page(c, &program_visitor, \"VarLenVisitorProgram\");\n    insert_var_len_dirty_page(c, &program_visitor, \"VarLenVisitorProgram\");\n}\n\nfn insert_opt_str(c: &mut Criterion) {\n    let typ: ProductType = [AlgebraicType::sum([AlgebraicType::String, AlgebraicType::unit()])].into();\n    let typ: RowTypeLayout = typ.into();\n\n    let visitor = row_type_visitor(&typ);\n    let fixed_row_size = typ.size();\n    assert!(fixed_row_size.len() == 6);\n    let mut clean_page_group = c.benchmark_group(\"insert_optional_str/clean_page\");\n\n    // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n    let mut variant_none = [0xa5u8; 6];\n    variant_none[0] = 1;\n\n    // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n    let mut variant_some = [0xa5u8; 6];\n    variant_some[0] = 0;\n\n    for some_ratio in [0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0] {\n        for &data_length_in_bytes in if some_ratio == 0.0 { &[0][..] } else { &VL_SIZES } {\n            let input = (some_ratio, data_length_in_bytes);\n            let avg_row_useful_size = 1.0 + data_length_in_bytes as f64 * some_ratio;\n            let granules_per_row = VarLenGranule::bytes_to_granules(data_length_in_bytes).0;\n            let avg_row_stored_size =\n                fixed_row_size.len() as f64 + (granules_per_row * VarLenGranule::SIZE.len()) as f64 * some_ratio;\n            let rows_per_page = PageOffset::PAGE_END.idx() as f64 / avg_row_stored_size;\n\n            clean_page_group.throughput(Throughput::Bytes((rows_per_page * avg_row_useful_size) as u64));\n\n            // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n            let var_len_data = [0xa5].repeat(data_length_in_bytes);\n            clean_page_group.bench_with_input(\n                BenchmarkId::new(\n                    \"(some_ratio, length_in_bytes)\",\n                    format!(\"({some_ratio}, {data_length_in_bytes})\"),\n                ),\n                &input,\n                |b, &(some_ratio, _)| {\n                    let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n                    let mut page = Page::new(fixed_row_size);\n                    unsafe { page.zero_data() };\n\n                    let body = |_, _, page: &mut Page| loop {\n                        let insert_none = rng.random_bool(some_ratio);\n                        if !insert_none {\n                            if !page.has_space_for_row(fixed_row_size, 0) {\n                                break;\n                            }\n                            let _ = unsafe {\n                                page.insert_row(&variant_none, &[] as &[&[u8]], &visitor, &mut NullBlobStore)\n                            };\n                        } else {\n                            if !page.has_space_for_row_with_objects(fixed_row_size, &[&var_len_data]) {\n                                break;\n                            }\n                            let _ = unsafe {\n                                page.insert_row(&variant_some, &[&var_len_data], &visitor, &mut NullBlobStore)\n                            };\n                        }\n                    };\n                    iter_time_with_page(b, &mut page, clear_zero, body);\n                },\n            );\n        }\n    }\n}\n\ncriterion_group!(\n    insert,\n    insert_fixed_len::<u64>,\n    insert_fixed_len::<U32x8>,\n    insert_fixed_len::<U32x64>,\n    insert_var_len,\n    insert_opt_str,\n);\n\nfn delete_to_approx_fullness_ratio<Row: FixedLenRow>(page: &mut Page, fullness: f64) {\n    assert!(fullness > 0.0 && fullness < 1.0);\n    let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n    let row_offsets = page.iter_fixed_len(row_size_for_type::<Row>()).collect::<Vec<_>>();\n    let visitor = Row::var_len_visitor();\n    for row in row_offsets.into_iter() {\n        let should_keep = rng.random_bool(fullness);\n        if !should_keep {\n            unsafe { page.delete_row(row, row_size_for_type::<Row>(), &visitor, &mut NullBlobStore) };\n        }\n    }\n}\n\nfn iter_read_fixed_len_from_page<Row: FixedLenRow>(b: &mut Bencher, page: &Page) {\n    b.iter(|| {\n        for row_offset in black_box(page).iter_fixed_len(row_size_for_type::<Row>()) {\n            black_box(read_fixed_len::<Row>(black_box(page), row_offset));\n        }\n    });\n}\n\n/// For various `fullness_ratio`s,\n/// construct a page of `u64`s\n/// which is `fullness_ratio` full,\n/// then benchmark iterating over it and reading each row.\nfn iter_read_fixed_len<Row: FixedLenRow>(c: &mut Criterion) {\n    let mut group = c.benchmark_group(format!(\"iter_read_fixed_len {}_byte\", mem::size_of::<Row>()));\n\n    let visitor = Row::var_len_visitor();\n\n    for fullness_ratio in [0.1, 0.25, 0.5, 0.75, 0.9] {\n        // Construct a page which is approximately `fullness_ratio` full,\n        // i.e. contains approximately `fullness_ratio * U64S_PER_PAGE` rows.\n        let mut partial_page = Page::new(row_size_for_type::<Row>());\n        // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n        fill_with_fixed_len::<Row>(&mut partial_page, Row::from_u64(0xa5a5a5a5_a5a5a5a5), &visitor);\n        // `delete_u64s_to_approx_fullness_ratio` uses a seeded `StdRng`,\n        // so this should be consistent-ish.\n        // It is liable to change on different machines or when updating `rand`.\n        // TODO: use `rand_chacha`?\n        delete_to_approx_fullness_ratio::<Row>(&mut partial_page, fullness_ratio);\n\n        // Throughput in rows visited per sample,\n        // i.e. the number of rows in the page.\n        group.throughput(Throughput::Bytes(\n            (partial_page.num_rows() * mem::size_of::<Row>()) as u64,\n        ));\n        group.bench_with_input(\n            BenchmarkId::new(\n                format!(\"iter_read_fixed_len {}_byte\", mem::size_of::<Row>()),\n                fullness_ratio,\n            ),\n            &*partial_page,\n            iter_read_fixed_len_from_page::<Row>,\n        );\n    }\n\n    let mut full_page = Page::new(row_size_for_type::<Row>());\n    // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n    fill_with_fixed_len(&mut full_page, Row::from_u64(0xa5a5a5a5_a5a5a5a5), &visitor);\n    group.throughput(Throughput::Bytes((full_page.num_rows() * mem::size_of::<Row>()) as u64));\n    group.bench_with_input(\n        BenchmarkId::new(format!(\"iter_read_fixed_len {}_byte\", mem::size_of::<Row>()), 1.0),\n        &*full_page,\n        iter_read_fixed_len_from_page::<Row>,\n    );\n}\n\ncriterion_group!(\n    iter,\n    iter_read_fixed_len::<u64>,\n    iter_read_fixed_len::<U32x8>,\n    iter_read_fixed_len::<U32x64>,\n);\n\nfn copy_filter_into_fixed_len_keep_ratio<Row: FixedLenRow>(b: &mut Bencher, keep_ratio: &f64) {\n    let visitor = Row::var_len_visitor();\n\n    let mut target_page = Page::new(row_size_for_type::<Row>());\n\n    let mut src_page = Page::new(row_size_for_type::<Row>());\n    // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n    fill_with_fixed_len::<Row>(&mut src_page, Row::from_u64(0xa5a5a5a5_a5a5a5a5), &visitor);\n\n    let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n\n    iter_time_with_page(b, &mut target_page, clear_zero, |_, _, target_page| {\n        let _ = unsafe {\n            black_box(&src_page).copy_filter_into(\n                PageOffset(0),\n                target_page,\n                row_size_for_type::<Row>(),\n                &visitor,\n                None::<&mut Box<dyn FnMut(_)>>,\n                |_page, _row| rng.random_bool(*keep_ratio),\n            )\n        };\n    });\n}\n\nfn copy_filter_into_fixed_len<Row: FixedLenRow>(c: &mut Criterion) {\n    let mut group = c.benchmark_group(format!(\"copy_filter_into_fixed_len {}_byte\", mem::size_of::<Row>()));\n\n    for keep_ratio in [0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0] {\n        group.throughput(if keep_ratio == 0.0 {\n            Throughput::Elements(1)\n        } else {\n            Throughput::Bytes(((rows_per_page::<Row>() as f64) * keep_ratio) as u64 * mem::size_of::<Row>() as u64)\n        });\n        group.bench_with_input(\n            BenchmarkId::new(\n                format!(\"copy_filter_into_fixed_len {}_byte\", mem::size_of::<Row>()),\n                keep_ratio,\n            ),\n            &keep_ratio,\n            copy_filter_into_fixed_len_keep_ratio::<Row>,\n        );\n    }\n}\n\ncriterion_group!(\n    copy_filter_into,\n    copy_filter_into_fixed_len::<u64>,\n    copy_filter_into_fixed_len::<U32x8>,\n    copy_filter_into_fixed_len::<U32x64>,\n);\n\nfn u32x2_type() -> ProductType {\n    [const { AlgebraicType::U32 }; 2].into()\n}\n\nfn u32x4_type() -> ProductType {\n    [const { AlgebraicType::U32 }; 4].into()\n}\n\nfn string_row_type() -> ProductType {\n    [AlgebraicType::String].into()\n}\n\nfn u32_array_row_type() -> ProductType {\n    [AlgebraicType::array(AlgebraicType::U32)].into()\n}\n\nfn product_types<const N: usize>(types: [ProductType; N]) -> ProductType {\n    types.map(AlgebraicType::from).into()\n}\n\nfn u32_array_value<const N: usize>(arr: [u32; N]) -> ProductValue {\n    AlgebraicValue::Array(ArrayValue::U32(arr.into())).into()\n}\n\nfn u32_matrix_value<const N: usize, const M: usize>(matrix: [[u32; N]; M]) -> ProductValue {\n    let elements = matrix\n        .map(|inner| AlgebraicValue::product(inner.map(AlgebraicValue::U32)))\n        .into();\n    ProductValue { elements }\n}\n\nfn product_value_test_cases() -> impl Iterator<\n    Item = (\n        &'static str,\n        ProductType,\n        ProductValue,\n        Option<NullVarLenVisitor>,\n        Option<AlignedVarLenOffsets<'static>>,\n    ),\n> {\n    [\n        (\n            \"U32\",\n            [AlgebraicType::U32].into(),\n            // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n            product![0xa5a5_a5a5u32],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"Option<U32>/None\",\n            [AlgebraicType::option(AlgebraicType::U32)].into(),\n            product![AlgebraicValue::OptionNone()],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"Option<U32>/Some\",\n            [AlgebraicType::option(AlgebraicType::U32)].into(),\n            // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n            product![AlgebraicValue::OptionSome(AlgebraicValue::U32(0xa5a5_a5a5))],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"U32x2\",\n            u32x2_type(),\n            product![0u32, 1u32],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"U32x4\",\n            u32x4_type(),\n            product![0u32, 1u32, 2u32, 3u32],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"U32x8\",\n            [const { AlgebraicType::U32 }; 8].into(),\n            product![0u32, 1u32, 2u32, 3u32, 4u32, 5u32, 6u32, 7u32],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"String/0\",\n            string_row_type(),\n            product![\"\"],\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"String/16\",\n            string_row_type(),\n            product![\"0123456789abcdef\"],\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"String/128\",\n            string_row_type(),\n            product![\"0123456789abcdef\".repeat(8).into_boxed_str()],\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"String/512\",\n            string_row_type(),\n            product![\"0123456789abcdef\".repeat(32).into_boxed_str()],\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"Array<U32>/0\",\n            u32_array_row_type(),\n            u32_array_value([]),\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"Array<U32>/1\",\n            u32_array_row_type(),\n            u32_array_value([0]),\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"Array<U32>/2\",\n            u32_array_row_type(),\n            u32_array_value([0, 1]),\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"Array<U32>/4\",\n            u32_array_row_type(),\n            u32_array_value([0, 1, 2, 3]),\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"Array<U32>/8\",\n            u32_array_row_type(),\n            u32_array_value([0, 1, 2, 3, 4, 5, 6, 7]),\n            None,\n            Some(AlignedVarLenOffsets::from_offsets(&[0])),\n        ),\n        (\n            \"U32x2x2\",\n            product_types([u32x2_type(), u32x2_type()]),\n            u32_matrix_value([[0, 1], [2, 3]]),\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"U32x4x2\",\n            product_types([u32x4_type(), u32x4_type()]),\n            u32_matrix_value([[0, 1, 2, 3], [4, 5, 6, 7]]),\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"U32x2x4\",\n            product_types([u32x2_type(), u32x2_type(), u32x2_type(), u32x2_type()]),\n            u32_matrix_value([[0, 1], [2, 3], [4, 5], [6, 7]]),\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"Option<U32x2>/None\",\n            [AlgebraicType::option(u32x2_type().into())].into(),\n            product![AlgebraicValue::OptionNone()],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n        (\n            \"Option<U32x2>/Some\",\n            [AlgebraicType::option(u32x2_type().into())].into(),\n            product![AlgebraicValue::OptionSome(product![0u32, 1u32].into())],\n            Some(NullVarLenVisitor),\n            Some(AlignedVarLenOffsets::from_offsets(&[])),\n        ),\n    ]\n    .into_iter()\n}\n\nfn time_insert_one(\n    b: &mut Bencher,\n    page: &mut Page,\n    ty: &RowTypeLayout,\n    value: &ProductValue,\n    visitor: &impl VarLenMembers,\n) {\n    let pre = |page: &mut _| {\n        clear_zero(page);\n        value.clone()\n    };\n    iter_time_with_page(b, page, pre, |val, _, page| unsafe {\n        let _ = black_box(write_row_to_page(page, &mut NullBlobStore, visitor, ty, &val));\n    });\n}\n\nfn ty_page_visitor(ty: ProductType) -> (RowTypeLayout, Box<Page>, VarLenVisitorProgram) {\n    let ty: RowTypeLayout = ty.into();\n    let page = Page::new(ty.size());\n    let vis = row_type_visitor(&ty);\n    (ty, page, vis)\n}\n\n#[inline(never)]\nfn insert_product_value_into_page(c: &mut Criterion) {\n    for (name, ty, value, null_visitor, aligned_offsets_visitor) in product_value_test_cases() {\n        let mut group = c.benchmark_group(format!(\"insert_product_value/{name}\"));\n        let (ty, mut page, program_visitor) = ty_page_visitor(ty);\n\n        if let Some(null_visitor) = null_visitor {\n            group.bench_function(\"NullVarLenVisitor\", |b| {\n                time_insert_one(b, &mut page, &ty, &value, &null_visitor);\n            });\n        }\n\n        if let Some(aligned_offsets_visitor) = aligned_offsets_visitor {\n            group.bench_function(\"AlignedVarLenOffsets\", |b| {\n                time_insert_one(b, &mut page, &ty, &value, &aligned_offsets_visitor);\n            });\n        }\n\n        group.bench_function(\"VarLenVisitorProgram\", |b| {\n            time_insert_one(b, &mut page, &ty, &value, &program_visitor);\n        });\n    }\n}\n\nfn time_extract_one(b: &mut Bencher, page: &mut Page, offset: PageOffset, ty: &RowTypeLayout) {\n    let body =\n        |_, _, page: &mut _| unsafe { serialize_row_from_page(ValueSerializer, page, &NullBlobStore, offset, ty) };\n    iter_time_with_page(b, page, |_| (), body);\n}\n\n#[inline(never)]\nfn extract_product_value_from_page(c: &mut Criterion) {\n    for (name, ty, value, _null_visitor, _aligned_offsets_visitor) in product_value_test_cases() {\n        let mut group = c.benchmark_group(\"extract_product_value\");\n        let (ty, mut page, visitor) = ty_page_visitor(ty);\n        let (offset, _) = unsafe { write_row_to_page(&mut page, &mut NullBlobStore, &visitor, &ty, &value) }.unwrap();\n        group.bench_function(name, |b| time_extract_one(b, &mut page, offset, &ty));\n    }\n}\n\n/// Puts two rows into a page which are equal,\n/// then times how long it takes to use `eq_row_in_page`\n/// to compare them.\n/// One iteration = one comparison between two rows.\nfn eq_in_page_same(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"eq_in_page\");\n    for (name, ty, value, _null_visitor, _aligned_offsets_visitor) in product_value_test_cases() {\n        let (ty, mut page, visitor) = ty_page_visitor(ty);\n        let static_bsatn_layout = StaticLayout::for_row_type(&ty);\n\n        let (offset_0, _) = unsafe { write_row_to_page(&mut page, &mut NullBlobStore, &visitor, &ty, &value) }.unwrap();\n        let (offset_1, _) = unsafe { write_row_to_page(&mut page, &mut NullBlobStore, &visitor, &ty, &value) }.unwrap();\n\n        group.bench_function(name, |b| {\n            b.iter(|| {\n                black_box(unsafe {\n                    eq_row_in_page(\n                        black_box(&page),\n                        black_box(&page),\n                        black_box(offset_0),\n                        black_box(offset_1),\n                        black_box(&ty),\n                        black_box(static_bsatn_layout.as_ref()),\n                    )\n                })\n            });\n        });\n    }\n}\n\n/// Puts a row into a page,\n/// then times how long it takes to use `hash_row_in_page`\n/// to compute its hash.\n/// One iteration = one row hashed.\nfn hash_in_page(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"hash_in_page\");\n    for (name, ty, value, _null_visitor, _aligned_offsets_visitor) in product_value_test_cases() {\n        let (ty, mut page, visitor) = ty_page_visitor(ty);\n\n        let (offset, _) = unsafe { write_row_to_page(&mut page, &mut NullBlobStore, &visitor, &ty, &value) }.unwrap();\n        group.bench_function(name, |b| {\n            let mut hasher = RowHash::hasher_builder().build_hasher();\n            b.iter(|| {\n                unsafe {\n                    hash_row_in_page(\n                        &mut hasher,\n                        black_box(&page),\n                        black_box(&NullBlobStore),\n                        black_box(offset),\n                        black_box(&ty),\n                    )\n                };\n                black_box(&mut hasher);\n            });\n        });\n    }\n}\n\ncriterion_group!(\n    bflatn,\n    insert_product_value_into_page,\n    extract_product_value_from_page,\n    eq_in_page_same,\n    hash_in_page,\n);\n\ncriterion_main!(insert, iter, copy_filter_into, bflatn);\n"
  },
  {
    "path": "crates/table/benches/page_manager.rs",
    "content": "use core::iter;\nuse core::mem;\nuse core::time::Duration;\nuse criterion::measurement::{Measurement, WallTime};\nuse criterion::{\n    black_box, criterion_group, criterion_main, Bencher, BenchmarkGroup, BenchmarkId, Criterion, Throughput,\n};\nuse rand::rngs::StdRng;\nuse rand::{Rng, SeedableRng};\nuse spacetimedb_lib::db::raw_def::v9::RawIndexAlgorithm;\nuse spacetimedb_lib::db::raw_def::v9::RawModuleDefV9Builder;\nuse spacetimedb_primitives::{ColList, IndexId, TableId};\nuse spacetimedb_sats::layout::{row_size_for_bytes, row_size_for_type, Size};\nuse spacetimedb_sats::raw_identifier::RawIdentifier;\nuse spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductValue};\nuse spacetimedb_schema::def::BTreeAlgorithm;\nuse spacetimedb_schema::def::ModuleDef;\nuse spacetimedb_schema::schema::TableSchema;\nuse spacetimedb_schema::table_name::TableName;\nuse spacetimedb_table::blob_store::NullBlobStore;\nuse spacetimedb_table::indexes::{Byte, Bytes, PageOffset, RowPointer, SquashedOffset, PAGE_DATA_SIZE};\nuse spacetimedb_table::page_pool::PagePool;\nuse spacetimedb_table::pages::Pages;\nuse spacetimedb_table::row_type_visitor::{row_type_visitor, VarLenVisitorProgram};\nuse spacetimedb_table::table::Table;\nuse spacetimedb_table::var_len::{NullVarLenVisitor, VarLenGranule, VarLenMembers, VarLenRef};\n\nfn time<R>(acc: &mut Duration, body: impl FnOnce() -> R) -> R {\n    let start = WallTime.start();\n    let ret = body();\n    let end = WallTime.end(start);\n    *acc = WallTime.add(acc, &end);\n    black_box(ret)\n}\n\nfn iter_time_with<P, B, X>(\n    b: &mut Bencher,\n    x: &mut X,\n    mut pre: impl FnMut(u64, &mut X) -> P,\n    mut body: impl FnMut(P, u64, &mut X) -> B,\n) {\n    b.iter_custom(|num_iters| {\n        let mut elapsed = WallTime.zero();\n        for i in 0..num_iters {\n            let p = black_box(pre(i, x));\n            black_box(&x);\n            time(&mut elapsed, || body(p, i, x));\n            black_box(&x);\n        }\n        elapsed\n    })\n}\n\n// Strictly this would be unsafe,\n// since it causes UB when applied to types that contain padding/`poison`,\n// but it's a benchmark so who cares.\nfn as_bytes<T>(t: &T) -> &Bytes {\n    let ptr = (t as *const T).cast::<Byte>();\n    unsafe { std::slice::from_raw_parts(ptr, mem::size_of::<T>()) }\n}\n\n#[allow(clippy::missing_safety_doc)] // It's a benchmark, clippy. Who cares.\nunsafe trait Row {\n    fn row_type() -> ProductType;\n\n    fn row_type_for_schema() -> ProductType {\n        let mut ty = Self::row_type();\n        // Ensure that we have names for every column,\n        // so that its accepted when used in a `ModuleDef` as a row type.\n        for (idx, elem) in ty.elements.iter_mut().enumerate() {\n            if elem.name.is_none() {\n                elem.name = Some(RawIdentifier::new(format!(\"col_{idx}\")));\n            }\n        }\n        ty\n    }\n\n    fn var_len_visitor() -> VarLenVisitorProgram {\n        row_type_visitor(&Self::row_type().into())\n    }\n\n    fn to_product(self) -> ProductValue;\n}\n\n#[allow(clippy::missing_safety_doc)] // It's a benchmark, clippy. Who cares.\n/// Apply only to types which:\n/// - Contain no padding bytes.\n/// - Contain no members which are stored BFLATN as var-len.\nunsafe trait FixedLenRow: Row + Sized {\n    fn as_bytes(&self) -> &Bytes {\n        as_bytes(self)\n    }\n\n    fn from_u64(u: u64) -> Self;\n}\n\nunsafe impl Row for u64 {\n    fn row_type() -> ProductType {\n        [AlgebraicType::U64].into()\n    }\n\n    fn to_product(self) -> ProductValue {\n        AlgebraicValue::U64(self).into()\n    }\n}\n\nunsafe impl FixedLenRow for u64 {\n    fn from_u64(u: u64) -> Self {\n        u\n    }\n}\n\n#[repr(C)]\nstruct U32x8 {\n    vals: [u32; 8],\n}\n\n// TODO: get rid of this once we change to >= Rust 1.79.\nconst TY_U32: AlgebraicType = AlgebraicType::U32;\n\nunsafe impl Row for U32x8 {\n    fn row_type() -> ProductType {\n        [TY_U32; 8].into()\n    }\n\n    fn to_product(self) -> ProductValue {\n        self.vals.map(AlgebraicValue::U32).into()\n    }\n}\n\nunsafe impl FixedLenRow for U32x8 {\n    fn from_u64(u: u64) -> Self {\n        Self { vals: [u as u32; 8] }\n    }\n}\n\n#[repr(C)]\nstruct U32x64 {\n    vals: [u32; 64],\n}\n\nunsafe impl Row for U32x64 {\n    fn row_type() -> ProductType {\n        [TY_U32; 64].into()\n    }\n\n    fn to_product(self) -> ProductValue {\n        self.vals.map(AlgebraicValue::U32).into()\n    }\n}\n\nunsafe impl FixedLenRow for U32x64 {\n    fn from_u64(u: u64) -> Self {\n        Self { vals: [u as u32; 64] }\n    }\n}\n\nunsafe impl Row for Box<str> {\n    fn row_type() -> ProductType {\n        [AlgebraicType::String].into()\n    }\n\n    fn to_product(self) -> ProductValue {\n        AlgebraicValue::String(self).into()\n    }\n}\n\nconst fn rows_per_page<Row: FixedLenRow>() -> usize {\n    PageOffset::PAGE_END.idx() / row_size_for_type::<Row>().len()\n}\n\n#[allow(unused)]\nfn var_len_rows_per_page(data_size_in_bytes: usize) -> usize {\n    let var_object_size = row_size_for_type::<VarLenRef>().len()\n        + VarLenGranule::bytes_to_granules(data_size_in_bytes).0 * VarLenGranule::SIZE.len();\n    PageOffset::PAGE_END.idx() / var_object_size\n}\n\nfn reserve_empty_page(c: &mut Criterion) {\n    const RESERVE_SIZE: Size = row_size_for_bytes(8);\n\n    let mut group = c.benchmark_group(\"reserve_empty_page\");\n    group.throughput(Throughput::Bytes(PAGE_DATA_SIZE as _));\n    group.bench_function(\"leave_uninit\", |b| {\n        let pool = PagePool::new_for_test();\n        let mut pages = Pages::default();\n        b.iter(|| {\n            let _ = black_box(pages.reserve_empty_page(&pool, RESERVE_SIZE));\n        });\n    });\n\n    let fill_with_zeros = |_, _, pages: &mut Pages| {\n        let pool = PagePool::new_for_test();\n        let page = pages.reserve_empty_page(&pool, RESERVE_SIZE).unwrap();\n        let page = pages.get_page_mut(page);\n        unsafe { page.zero_data() };\n    };\n    group.bench_function(\"fill_with_zeros\", |b| {\n        iter_time_with(b, &mut Pages::default(), |_, _| (), fill_with_zeros)\n    });\n}\n\nfn insert_one_page_worth_fixed_len<R: FixedLenRow>(\n    pool: &PagePool,\n    pages: &mut Pages,\n    visitor: &impl VarLenMembers,\n    val: &R,\n) {\n    let size = row_size_for_type::<R>();\n    for _ in 0..rows_per_page::<R>() {\n        let _ = black_box(unsafe {\n            black_box(&mut *pages).insert_row(pool, visitor, size, val.as_bytes(), &[], &mut NullBlobStore)\n        });\n    }\n}\n\ntype Group<'a, 'b> = &'a mut BenchmarkGroup<'b, WallTime>;\n\n// time to insert a whole bunch of rows\nfn insert_one_page_fixed_len(c: &mut Criterion) {\n    fn bench_insert_one_page_fixed_len<R: FixedLenRow>(group: Group<'_, '_>, visitor: &impl VarLenMembers, name: &str) {\n        group.throughput(Throughput::Bytes(\n            rows_per_page::<R>() as u64 * mem::size_of::<R>() as u64,\n        ));\n        group.bench_function(name, |b| {\n            let pool = PagePool::new_for_test();\n            let mut pages = Pages::default();\n            // `0xa5` is the alternating bit pattern, which makes incorrect accesses obvious.\n            insert_one_page_worth_fixed_len(&pool, &mut pages, visitor, &R::from_u64(0xa5a5a5a5_a5a5a5a5));\n            let pre = |_, pages: &mut Pages| pages.clear();\n            iter_time_with(b, &mut pages, pre, |_, _, pages| {\n                insert_one_page_worth_fixed_len(&pool, pages, visitor, &R::from_u64(0xdeadbeef_0badbeef))\n            });\n        });\n    }\n\n    let mut group = c.benchmark_group(\"insert_one_page_fixed_len\");\n    bench_insert_one_page_fixed_len::<u64>(&mut group, &NullVarLenVisitor, \"u64/NullVarLenVisitor\");\n    bench_insert_one_page_fixed_len::<u64>(&mut group, &u64::var_len_visitor(), \"u64/VarLenVisitorProgram\");\n\n    bench_insert_one_page_fixed_len::<U32x8>(&mut group, &NullVarLenVisitor, \"U32x8/NullVarLenVisitor\");\n    bench_insert_one_page_fixed_len::<U32x8>(&mut group, &U32x8::var_len_visitor(), \"U32x8/VarLenVisitorProgram\");\n\n    bench_insert_one_page_fixed_len::<U32x64>(&mut group, &NullVarLenVisitor, \"U32x64/NullVarLenVisitor\");\n    bench_insert_one_page_fixed_len::<U32x64>(&mut group, &U32x64::var_len_visitor(), \"U32x64/VarLenVisitorProgram\");\n}\n\nfn fill_page_with_fixed_len_collect_row_pointers<R: FixedLenRow>(\n    pool: &PagePool,\n    pages: &mut Pages,\n    visitor: &impl VarLenMembers,\n    val: &R,\n) -> Vec<RowPointer> {\n    let mut ptrs = Vec::with_capacity(rows_per_page::<R>());\n    for _ in 0..rows_per_page::<R>() {\n        let (page, offset) = unsafe {\n            pages.insert_row(\n                pool,\n                visitor,\n                row_size_for_type::<R>(),\n                val.as_bytes(),\n                &[],\n                &mut NullBlobStore,\n            )\n        }\n        .unwrap();\n        let ptr = RowPointer::new(false, page, offset, SquashedOffset::COMMITTED_STATE);\n        ptrs.push(ptr);\n    }\n    ptrs\n}\n\n// insert a whole bunch of rows, then time to delete them all\nfn delete_one_page_fixed_len(c: &mut Criterion) {\n    fn bench_delete_one_page_fixed_len<R: FixedLenRow>(group: Group<'_, '_>, visitor: &impl VarLenMembers, name: &str) {\n        let rows_per_page = rows_per_page::<R>();\n\n        group.throughput(Throughput::Bytes(rows_per_page as u64 * mem::size_of::<R>() as u64));\n\n        group.bench_function(name, |b| {\n            let pre = |i, (pages, pool): &mut _| {\n                let val = R::from_u64(i);\n                fill_page_with_fixed_len_collect_row_pointers::<R>(pool, pages, visitor, &val)\n            };\n            iter_time_with(\n                b,\n                &mut (Pages::default(), PagePool::new_for_test()),\n                pre,\n                |ptrs, _, (pages, _)| {\n                    for ptr in ptrs {\n                        unsafe {\n                            pages.delete_row(visitor, row_size_for_type::<R>(), black_box(ptr), &mut NullBlobStore)\n                        };\n                    }\n                },\n            );\n        });\n    }\n\n    let mut group = c.benchmark_group(\"delete_one_page_fixed_len\");\n\n    bench_delete_one_page_fixed_len::<u64>(&mut group, &NullVarLenVisitor, \"u64/NullVarLenVisitor\");\n    bench_delete_one_page_fixed_len::<u64>(&mut group, &u64::var_len_visitor(), \"u64/VarLenVisitorProgram\");\n\n    bench_delete_one_page_fixed_len::<U32x8>(&mut group, &NullVarLenVisitor, \"U32x8/NullVarLenVisitor\");\n    bench_delete_one_page_fixed_len::<U32x8>(&mut group, &U32x8::var_len_visitor(), \"U32x8/VarLenVisitorProgram\");\n\n    bench_delete_one_page_fixed_len::<U32x64>(&mut group, &NullVarLenVisitor, \"U32x64/NullVarLenVisitor\");\n    bench_delete_one_page_fixed_len::<U32x64>(&mut group, &U32x64::var_len_visitor(), \"U32x64/VarLenVisitorProgram\");\n}\n\n// insert a whole bunch of rows, then time to access them\nfn retrieve_one_page_fixed_len(c: &mut Criterion) {\n    fn bench_retrieve_one_page<R: FixedLenRow>(group: Group<'_, '_>, visitor: &impl VarLenMembers, name: &str) {\n        let rows_per_page = rows_per_page::<R>();\n        group.throughput(Throughput::Bytes(rows_per_page as u64 * mem::size_of::<R>() as u64));\n\n        group.bench_function(name, |b| {\n            let pool = PagePool::new_for_test();\n            let mut pages = Pages::default();\n\n            let ptrs = fill_page_with_fixed_len_collect_row_pointers(\n                &pool,\n                &mut pages,\n                visitor,\n                &R::from_u64(0xdeadbeef_0badbeef),\n            );\n\n            b.iter(|| {\n                for &ptr in &ptrs {\n                    let bytes = black_box(&pages).get_fixed_len_row(ptr, row_size_for_type::<R>());\n                    let val: *const R = bytes.as_ptr().cast();\n                    let val: R = unsafe { std::ptr::read(val) };\n                    black_box(val);\n                }\n            });\n        });\n    }\n\n    let mut group = c.benchmark_group(\"retrieve_one_page_fixed_len\");\n\n    bench_retrieve_one_page::<u64>(&mut group, &NullVarLenVisitor, \"u64/NullVarLenVisitor\");\n    bench_retrieve_one_page::<u64>(&mut group, &u64::var_len_visitor(), \"u64/VarLenVisitorProgram\");\n\n    bench_retrieve_one_page::<U32x8>(&mut group, &NullVarLenVisitor, \"U32x8/NullVarLenVisitor\");\n    bench_retrieve_one_page::<U32x8>(&mut group, &U32x8::var_len_visitor(), \"U32x8/VarLenVisitorProgram\");\n\n    bench_retrieve_one_page::<U32x64>(&mut group, &NullVarLenVisitor, \"U32x64/NullVarLenVisitor\");\n    bench_retrieve_one_page::<U32x64>(&mut group, &U32x64::var_len_visitor(), \"U32x64/VarLenVisitorProgram\");\n}\n\n// insert a bunch of rows,\n// delete some fraction of them to create holes in multiple pages,\n// then time to insert into those holes\nfn insert_with_holes_fixed_len(c: &mut Criterion) {\n    fn bench_insert_with_holes<R: FixedLenRow>(c: &mut Criterion, var_len_visitor: &impl VarLenMembers, name: &str) {\n        let mut group = c.benchmark_group(format!(\"insert_with_holes_fixed_len/{name}\"));\n        let val = R::from_u64(0xdeadbeef_0badbeef);\n        for delete_ratio in [0.1f64, 0.25, 0.5, 0.75, 0.9, 1.0] {\n            let num_pages = 16;\n            let total_num_rows = rows_per_page::<R>() * num_pages;\n            let num_to_delete = (total_num_rows as f64 * delete_ratio) as usize;\n\n            let num_to_delete_in_bytes = num_to_delete * mem::size_of::<R>();\n\n            let row_size = row_size_for_type::<R>();\n\n            group.throughput(Throughput::Bytes(num_to_delete_in_bytes as u64));\n\n            group.bench_function(delete_ratio.to_string(), |b| {\n                let pool = PagePool::new_for_test();\n                let mut pages = Pages::default();\n\n                let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n\n                for _ in 0..num_pages {\n                    let page = pages.reserve_empty_page(&pool, row_size).unwrap();\n                    let page = pages.get_page_mut(page);\n\n                    unsafe { page.zero_data() };\n                }\n\n                let pre = |_, (pages, pool): &mut (Pages, PagePool)| {\n                    pages.clear();\n                    let mut ptrs_to_delete = Vec::with_capacity(num_to_delete);\n                    for _ in 0..total_num_rows {\n                        let (page_idx, offset) = unsafe {\n                            pages.insert_row(pool, var_len_visitor, row_size, val.as_bytes(), &[], &mut NullBlobStore)\n                        }\n                        .unwrap();\n\n                        if rng.random_bool(delete_ratio) {\n                            ptrs_to_delete.push(RowPointer::new(\n                                false,\n                                page_idx,\n                                offset,\n                                SquashedOffset::COMMITTED_STATE,\n                            ));\n                        }\n                    }\n                    let actual_num_deleted = ptrs_to_delete.len();\n                    for ptr in ptrs_to_delete {\n                        unsafe {\n                            pages.delete_row(var_len_visitor, row_size, ptr, &mut NullBlobStore);\n                        }\n                    }\n                    actual_num_deleted\n                };\n                let body = |actual_num_deleted, _, (pages, pool): &mut (Pages, PagePool)| {\n                    for _ in 0..actual_num_deleted {\n                        let _ = black_box(unsafe {\n                            pages.insert_row(pool, var_len_visitor, row_size, val.as_bytes(), &[], &mut NullBlobStore)\n                        });\n                    }\n                };\n                iter_time_with(b, &mut (pages, pool), pre, body);\n            });\n        }\n    }\n\n    bench_insert_with_holes::<u64>(c, &NullVarLenVisitor, \"u64/NullVarLenVisitor\");\n    bench_insert_with_holes::<u64>(c, &u64::var_len_visitor(), \"u64/VarLenVisitorProgram\");\n\n    bench_insert_with_holes::<U32x8>(c, &NullVarLenVisitor, \"U32x8/NullVarLenVisitor\");\n    bench_insert_with_holes::<U32x8>(c, &U32x8::var_len_visitor(), \"U32x8/VarLenVisitorProgram\");\n\n    bench_insert_with_holes::<U32x64>(c, &NullVarLenVisitor, \"U32x64/NullVarLenVisitor\");\n    bench_insert_with_holes::<U32x64>(c, &U32x64::var_len_visitor(), \"U32x64/VarLenVisitorProgram\");\n}\n\n// insert a whole bunch of rows, then time to copy_filter materialize a view\n\nfn copy_filter_fixed_len(c: &mut Criterion) {\n    fn bench_copy_filter<R: FixedLenRow>(c: &mut Criterion, name: &str) {\n        let mut group = c.benchmark_group(format!(\"copy_filter_fixed_len/{name}\"));\n        let row_size = black_box(row_size_for_type::<R>());\n\n        let val = R::from_u64(0xdeadbeef_0badbeef);\n        for keep_ratio in [0.1, 0.25, 0.5, 0.75, 0.9, 1.0] {\n            let visitor = &NullVarLenVisitor;\n            let pool = PagePool::new_for_test();\n            let mut pages = Pages::default();\n\n            let num_pages = 16;\n            let total_num_rows = rows_per_page::<R>() * num_pages;\n\n            for _ in 0..total_num_rows {\n                unsafe { pages.insert_row(&pool, visitor, row_size, val.as_bytes(), &[], &mut NullBlobStore) }.unwrap();\n            }\n\n            let num_to_keep = (total_num_rows as f64 * keep_ratio) as usize;\n            let num_to_keep_bytes = num_to_keep * mem::size_of::<R>();\n\n            group.throughput(Throughput::Bytes(num_to_keep_bytes as u64));\n\n            let mut rng = StdRng::seed_from_u64(0xa5a5a5a5_a5a5a5a5);\n\n            // To avoid advancing RNG in the benchmark,\n            // precompute a big vec of bools, with one bool for each value that we may or may not keep.\n            let keep_seq: Vec<bool> = (0..total_num_rows).map(|_| rng.random_bool(keep_ratio)).collect();\n\n            group.bench_function(keep_ratio.to_string(), |b| {\n                b.iter_with_large_drop(|| unsafe {\n                    let mut keep_iter = keep_seq.iter().copied();\n                    black_box(&pages).copy_filter(visitor, row_size, None::<&mut Box<dyn FnMut(_)>>, |_, _| {\n                        black_box(keep_iter.next().unwrap_or_default())\n                    })\n                });\n            });\n        }\n    }\n\n    bench_copy_filter::<u64>(c, \"u64\");\n    bench_copy_filter::<U32x8>(c, \"U32x8\");\n    bench_copy_filter::<U32x64>(c, \"U32x64\");\n}\n\n// TODO(bench):\n// - Duplicate above benchmarks with var-len rows of various sizes\n//   - In the insert-with-holes benchmark, randomize size of each row to simulate fragmentation.\n// - Extend above benchmarks to go through `Table` with `AlgebraicValue`.\n\ncriterion_group!(\n    pages,\n    reserve_empty_page,\n    insert_one_page_fixed_len,\n    delete_one_page_fixed_len,\n    retrieve_one_page_fixed_len,\n    insert_with_holes_fixed_len,\n    copy_filter_fixed_len,\n);\n\nfn schema_from_ty(ty: ProductType, name: &str) -> TableSchema {\n    let mut result = TableSchema::from_product_type(ty);\n    result.table_name = TableName::for_test(name);\n    result\n}\n\nfn make_table(c: &mut Criterion) {\n    fn bench_make_table<R: Row>(group: Group<'_, '_>, name: &str) {\n        let ty = R::row_type_for_schema();\n        let schema = schema_from_ty(ty.clone(), name);\n        group.bench_function(name, |b| {\n            b.iter_custom(|num_iters| {\n                let schemas = vec![schema.clone(); num_iters as usize];\n                let mut tables = Vec::with_capacity(num_iters as usize);\n                let start = WallTime.start();\n                for schema in schemas {\n                    tables.push(Table::new(schema.into(), SquashedOffset::COMMITTED_STATE));\n                }\n                let elapsed = WallTime.end(start);\n                black_box(tables);\n                elapsed\n            });\n        });\n    }\n\n    let mut group = c.benchmark_group(\"make_table\");\n\n    bench_make_table::<u64>(&mut group, \"u64\");\n    bench_make_table::<U32x8>(&mut group, \"U32x8\");\n    bench_make_table::<U32x64>(&mut group, \"U32x64\");\n}\n\nfn make_table_for_row_type<R: Row>(name: &str) -> Table {\n    let ty = R::row_type_for_schema();\n    let schema = schema_from_ty(ty.clone(), name);\n    Table::new(schema.into(), SquashedOffset::COMMITTED_STATE)\n}\n\nfn use_type_throughput<T>(group: &mut BenchmarkGroup<'_, impl Measurement>) {\n    group.throughput(Throughput::Bytes(mem::size_of::<T>() as u64));\n}\n\nfn table_insert_one_row(c: &mut Criterion) {\n    fn bench_insert_row<R: Row>(group: Group<'_, '_>, val: R, name: &str) {\n        let table = make_table_for_row_type::<R>(name);\n        let val = black_box(val.to_product());\n\n        // Insert before benching to alloc and fault in a page.\n        let pool = PagePool::new_for_test();\n        let mut ctx = (table, NullBlobStore);\n        let ptr = ctx.0.insert(&pool, &mut ctx.1, &val).unwrap().1.pointer();\n        let pre = |_, (table, bs): &mut (Table, NullBlobStore)| {\n            table.delete(bs, ptr, |_| ()).unwrap();\n        };\n        group.bench_function(name, |b| {\n            iter_time_with(b, &mut ctx, pre, |_, _, (table, bs)| {\n                table.insert(&pool, bs, &val).map(|r| r.1.pointer())\n            });\n        });\n    }\n\n    {\n        let mut group = c.benchmark_group(\"table_insert_one_row/fixed_len\");\n\n        use_type_throughput::<u64>(&mut group);\n        bench_insert_row(&mut group, 0xdeadbeef_0badbabeu64, \"u64\");\n\n        use_type_throughput::<U32x8>(&mut group);\n        bench_insert_row(&mut group, U32x8::from_u64(0xdeadbeef_0badbabe), \"U32x8\");\n\n        use_type_throughput::<U32x64>(&mut group);\n        bench_insert_row(&mut group, U32x64::from_u64(0xdeadbeef_0badbabe), \"U32x64\");\n    }\n\n    let mut group = c.benchmark_group(\"table_insert_one_row/String\");\n\n    group.throughput(Throughput::Elements(1));\n    bench_insert_row(&mut group, Box::from(\"\"), \"0\");\n\n    group.throughput(Throughput::Bytes(1));\n    bench_insert_row(&mut group, Box::from(\"a\"), \"1\");\n\n    for num_granules in [1, 2, 4, 8, 16] {\n        let num_bytes = VarLenGranule::DATA_SIZE * num_granules;\n        group.throughput(Throughput::Bytes(num_bytes as u64));\n        bench_insert_row(\n            &mut group,\n            \"a\".repeat(num_bytes).into_boxed_str(),\n            &num_bytes.to_string(),\n        );\n    }\n}\n\nfn table_delete_one_row(c: &mut Criterion) {\n    fn bench_delete_row<R: Row>(group: Group<'_, '_>, val: R, name: &str) {\n        let table = make_table_for_row_type::<R>(name);\n        let val = val.to_product();\n\n        // Insert before benching to alloc and fault in a page.\n        let mut ctx = (table, NullBlobStore, PagePool::new_for_test());\n        let insert = |_: u64, (table, bs, pool): &mut (Table, NullBlobStore, PagePool)| {\n            table.insert(pool, bs, &val).unwrap().1.pointer()\n        };\n\n        group.bench_function(name, |b| {\n            iter_time_with(b, &mut ctx, insert, |row, _, (table, bs, _)| {\n                table.delete(bs, row, |_| ())\n            });\n        });\n    }\n\n    {\n        let mut group = c.benchmark_group(\"table_delete_one_row/fixed_len\");\n\n        use_type_throughput::<u64>(&mut group);\n        bench_delete_row(&mut group, 0xdeadbeef_0badbabeu64, \"u64\");\n\n        use_type_throughput::<U32x8>(&mut group);\n        bench_delete_row(&mut group, U32x8::from_u64(0xdeadbeef_0badbabe), \"U32x8\");\n\n        use_type_throughput::<U32x64>(&mut group);\n        bench_delete_row(&mut group, U32x64::from_u64(0xdeadbeef_0badbabe), \"U32x64\");\n    }\n\n    let mut group = c.benchmark_group(\"table_delete_one_row/String\");\n\n    group.throughput(Throughput::Elements(1));\n    bench_delete_row(&mut group, Box::from(\"\"), \"0\");\n\n    group.throughput(Throughput::Bytes(1));\n    bench_delete_row(&mut group, Box::from(\"a\"), \"1\");\n\n    for num_granules in [1, 2, 4, 8, 16] {\n        let num_bytes = VarLenGranule::DATA_SIZE * num_granules;\n        group.throughput(Throughput::Bytes(num_bytes as u64));\n        bench_delete_row(\n            &mut group,\n            \"a\".repeat(num_bytes).into_boxed_str(),\n            &num_bytes.to_string(),\n        );\n    }\n}\n\nfn table_extract_one_row(c: &mut Criterion) {\n    fn bench_extract_row<R: Row>(group: Group<'_, '_>, val: R, name: &str) {\n        let mut table = make_table_for_row_type::<R>(name);\n        let val = val.to_product();\n\n        let pool = PagePool::new_for_test();\n        let mut blob_store = NullBlobStore;\n        let row = black_box(table.insert(&pool, &mut blob_store, &val).unwrap().1);\n        group.bench_function(name, |b| {\n            b.iter_with_large_drop(|| black_box(row.to_product_value()));\n        });\n    }\n\n    {\n        let mut group = c.benchmark_group(\"table_extract_one_row/fixed_len\");\n\n        use_type_throughput::<u64>(&mut group);\n        bench_extract_row(&mut group, 0xdeadbeef_0badbabeu64, \"u64\");\n\n        use_type_throughput::<U32x8>(&mut group);\n        bench_extract_row(&mut group, U32x8::from_u64(0xdeadbeef_0badbabe), \"U32x8\");\n\n        use_type_throughput::<U32x64>(&mut group);\n        bench_extract_row(&mut group, U32x64::from_u64(0xdeadbeef_0badbabe), \"U32x64\");\n    }\n\n    let mut group = c.benchmark_group(\"table_extract_one_row/String\");\n\n    group.throughput(Throughput::Elements(1));\n    bench_extract_row(&mut group, Box::from(\"\"), \"0\");\n\n    group.throughput(Throughput::Bytes(1));\n    bench_extract_row(&mut group, Box::from(\"a\"), \"1\");\n\n    for num_granules in [1, 2, 4, 8, 16] {\n        let num_bytes = VarLenGranule::DATA_SIZE * num_granules;\n        group.throughput(Throughput::Bytes(num_bytes as u64));\n        bench_extract_row(\n            &mut group,\n            \"a\".repeat(num_bytes).into_boxed_str(),\n            &num_bytes.to_string(),\n        );\n    }\n}\n\n// TODO(bench):\n// - table insert_with_holes benchmark\n// - table copy_filter benchmark\n// - index benchmarks: for a variety of table sizes,\n//  - insert a row into a table with an index\n//  - delete a row from a table with an index\n//  - seek a row in an index\n\ncriterion_group!(\n    table,\n    make_table,\n    table_insert_one_row,\n    table_delete_one_row,\n    table_extract_one_row,\n);\n\ntrait IndexedRow: Row + Sized {\n    fn indexed_columns() -> ColList {\n        0.into()\n    }\n    fn table_name() -> String {\n        std::any::type_name::<Self>()\n            .chars()\n            .filter(|c| c.is_alphabetic())\n            .collect()\n    }\n    /// Don't call this in a loop, it runs validation code.\n    fn make_schema() -> TableSchema {\n        let name = RawIdentifier::new(Self::table_name());\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(name.clone(), Self::row_type_for_schema(), true)\n            .with_index(\n                RawIndexAlgorithm::BTree {\n                    columns: Self::indexed_columns(),\n                },\n                \"accessor_name_doesnt_matter\",\n            );\n        let def: ModuleDef = builder.finish().try_into().expect(\"failed to build table schema\");\n        def.table_schema(&*name, TableId::SENTINEL).unwrap()\n    }\n    fn throughput() -> Throughput {\n        Throughput::Bytes(mem::size_of::<Self>() as u64)\n    }\n    fn column_value_from_u64(u: u64) -> AlgebraicValue;\n}\n\nimpl IndexedRow for u64 {\n    fn column_value_from_u64(u: u64) -> AlgebraicValue {\n        AlgebraicValue::U64(u)\n    }\n}\n\nimpl IndexedRow for U32x8 {\n    fn column_value_from_u64(u: u64) -> AlgebraicValue {\n        AlgebraicValue::U32(u as _)\n    }\n}\n\nimpl IndexedRow for U32x64 {\n    fn column_value_from_u64(u: u64) -> AlgebraicValue {\n        AlgebraicValue::U32(u as _)\n    }\n}\n\nimpl IndexedRow for Box<str> {\n    fn column_value_from_u64(u: u64) -> AlgebraicValue {\n        AlgebraicValue::String(u.to_string().into())\n    }\n    fn throughput() -> Throughput {\n        // I'm too lazy to come up with an interface that computes the length of a string\n        // and passes it to throughput.\n        Throughput::Elements(1)\n    }\n}\n\nfn make_table_with_index<R: IndexedRow>(unique: bool) -> (Table, IndexId) {\n    let schema = R::make_schema();\n    let mut tbl = Table::new(schema.into(), SquashedOffset::COMMITTED_STATE);\n\n    let cols = R::indexed_columns();\n    let index_id = IndexId::SENTINEL;\n    let algo = BTreeAlgorithm { columns: cols }.into();\n    let idx = tbl.new_index(&algo, unique).unwrap();\n    // SAFETY: index was derived from the table.\n    unsafe { tbl.insert_index(&NullBlobStore, index_id, idx) };\n\n    (tbl, index_id)\n}\n\n#[cfg(not(debug_assertions))]\nconst TABLE_SIZE_POWERS: [u64; 7] = [0, 3, 5, 8, 11, 14, 17];\n#[cfg(debug_assertions)]\nconst TABLE_SIZE_POWERS: [u64; 6] = [0, 3, 5, 8, 11, 14];\n\nfn powers<const N: usize>(ps: [u64; N]) -> [u64; N] {\n    ps.map(|n| 1 << n)\n}\n\nfn insert_num_same<R: IndexedRow>(\n    pool: &PagePool,\n    tbl: &mut Table,\n    mut make_row: impl FnMut() -> R,\n    num_same: usize,\n) -> Option<RowPointer> {\n    iter::repeat_n(make_row().to_product(), num_same)\n        .zip(0u32..)\n        .map(|(mut row, n)| {\n            if let Some(slot) = row.elements.get_mut(1) {\n                *slot = n.into();\n            }\n            tbl.insert(pool, &mut NullBlobStore, &row)\n                .map(|(_, row)| row.pointer())\n                .ok()\n        })\n        .last()\n        .flatten()\n}\n\nfn clear_all_same<R: IndexedRow>(tbl: &mut Table, index_id: IndexId, val_same: u64) {\n    let ptrs = tbl\n        .get_index_by_id(index_id)\n        .unwrap()\n        .seek_point(&R::column_value_from_u64(val_same))\n        .collect::<Vec<_>>();\n    for ptr in ptrs {\n        tbl.delete(&mut NullBlobStore, ptr, |_| ()).unwrap();\n    }\n}\n\nfn bench_id_for_index(name: &str, num_rows: u64, same_ratio: f64, num_same: usize, unique: bool) -> BenchmarkId {\n    BenchmarkId::new(\n        name,\n        format_args!(\"(rows = {num_rows}, sratio = {same_ratio}, snum = {num_same}, unique = {unique})\"),\n    )\n}\n\nfn make_table_with_same_ratio<R: IndexedRow>(\n    pool: &PagePool,\n    mut make_row: impl FnMut(u64) -> R,\n    num_rows: u64,\n    same_ratio: f64,\n    unique: bool,\n) -> (Table, IndexId, usize, u64) {\n    let (mut tbl, index_id) = make_table_with_index::<R>(unique);\n\n    let num_same = if unique {\n        1\n    } else {\n        let num_same = (num_rows as f64 * same_ratio) as usize;\n        num_same.max(1)\n    };\n    let num_diff = num_rows / num_same as u64;\n\n    for i in 0..num_diff {\n        insert_num_same(pool, &mut tbl, || make_row(i), num_same);\n    }\n\n    (tbl, index_id, num_same, num_diff)\n}\n\nfn index_insert(c: &mut Criterion) {\n    fn bench_index_insert<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        num_rows: u64,\n        same_ratio: f64,\n    ) {\n        let make_row_move = &mut make_row;\n        let pool = PagePool::new_for_test();\n        let (tbl, index_id, num_same, _) =\n            make_table_with_same_ratio::<R>(&pool, make_row_move, num_rows, same_ratio, false);\n        let mut ctx = (tbl, NullBlobStore, pool);\n\n        group.bench_with_input(\n            bench_id_for_index(name, num_rows, same_ratio, num_same, false),\n            &num_rows,\n            |b, &num_rows| {\n                let pre = |_, (tbl, _, pool): &mut (Table, NullBlobStore, PagePool)| {\n                    clear_all_same::<R>(tbl, index_id, num_rows);\n                    insert_num_same(pool, tbl, || make_row(num_rows), num_same - 1);\n                    make_row(num_rows).to_product()\n                };\n                iter_time_with(b, &mut ctx, pre, |row, _, (tbl, bs, pool)| {\n                    tbl.insert(pool, bs, &row).map(|r| r.1.pointer())\n                });\n            },\n        );\n    }\n    fn bench_many_table_sizes<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        same_ratio: f64,\n    ) {\n        group.throughput(R::throughput());\n        for num_rows in powers(TABLE_SIZE_POWERS) {\n            bench_index_insert(&mut make_row, group, name, num_rows, same_ratio);\n        }\n    }\n\n    let mut group = c.benchmark_group(\"index_insert\");\n\n    bench_many_table_sizes::<u64>(FixedLenRow::from_u64, &mut group, \"u64\", 0.0);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.00);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.01);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.05);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.10);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.25);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.50);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 1.00);\n    bench_many_table_sizes::<U32x64>(FixedLenRow::from_u64, &mut group, \"U32x64\", 0.0);\n    bench_many_table_sizes::<Box<str>>(|i| i.to_string().into(), &mut group, \"String\", 0.0);\n}\n\nfn index_seek(c: &mut Criterion) {\n    fn bench_index_seek<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        num_rows: u64,\n        same_ratio: f64,\n        unique: bool,\n    ) {\n        let make_row_move = &mut make_row;\n        let (tbl, index_id, num_same, num_diff) =\n            make_table_with_same_ratio::<R>(&PagePool::new_for_test(), make_row_move, num_rows, same_ratio, unique);\n\n        group.bench_with_input(\n            bench_id_for_index(name, num_rows, same_ratio, num_same, unique),\n            &num_diff,\n            |b, &num_diff| {\n                let col_to_seek = black_box(R::column_value_from_u64(num_diff / 2));\n                let index = black_box(&tbl)\n                    .get_index_by_id_with_table(&NullBlobStore, index_id)\n                    .unwrap();\n                b.iter_custom(|num_iters| {\n                    let mut elapsed = WallTime.zero();\n                    for _ in 0..num_iters {\n                        let (row, none) = time(&mut elapsed, || {\n                            let mut iter = index.seek_range(&col_to_seek).unwrap();\n                            (iter.next(), iter.next())\n                        });\n                        assert!(\n                            num_same > 1 || none.is_none(),\n                            \"Found a second row at {:?}: {:?} (first row is {:?})\",\n                            none,\n                            none.unwrap().to_product_value(),\n                            row.unwrap().to_product_value(),\n                        );\n                    }\n                    elapsed\n                });\n            },\n        );\n    }\n    fn bench_many_table_sizes<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        same_ratio: f64,\n        unique: bool,\n    ) {\n        group.throughput(Throughput::Elements(1));\n        for num_rows in powers(TABLE_SIZE_POWERS) {\n            bench_index_seek(&mut make_row, group, name, num_rows, same_ratio, unique);\n        }\n    }\n\n    let mut group = c.benchmark_group(\"index_seek\");\n\n    bench_many_table_sizes::<u64>(FixedLenRow::from_u64, &mut group, \"u64\", 0.0, false);\n    bench_many_table_sizes::<u64>(FixedLenRow::from_u64, &mut group, \"u64\", 0.0, true);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.00, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.01, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.05, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.10, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.25, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.50, false);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 1.00, false);\n    bench_many_table_sizes::<U32x64>(FixedLenRow::from_u64, &mut group, \"U32x64\", 0.0, false);\n    bench_many_table_sizes::<Box<str>>(|i| i.to_string().into(), &mut group, \"String\", 0.0, false);\n}\n\nfn index_delete(c: &mut Criterion) {\n    fn bench_index_delete<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        num_rows: u64,\n        same_ratio: f64,\n    ) {\n        let make_row_move = &mut make_row;\n        let pool = PagePool::new_for_test();\n        let (mut tbl, index_id, num_same, _) =\n            make_table_with_same_ratio::<R>(&pool, make_row_move, num_rows, same_ratio, false);\n\n        group.bench_with_input(\n            bench_id_for_index(name, num_rows, same_ratio, num_same, false),\n            &num_rows,\n            |b, &num_rows| {\n                let pre = |_, tbl: &mut Table| {\n                    clear_all_same::<R>(tbl, index_id, num_rows);\n                    insert_num_same(&pool, tbl, || make_row(num_rows), num_same).unwrap()\n                };\n                iter_time_with(b, &mut tbl, pre, |ptr, _, tbl| {\n                    tbl.delete(&mut NullBlobStore, ptr, |_| ())\n                });\n            },\n        );\n    }\n    fn bench_many_table_sizes<R: IndexedRow>(\n        mut make_row: impl FnMut(u64) -> R,\n        group: Group<'_, '_>,\n        name: &str,\n        same_ratio: f64,\n    ) {\n        group.throughput(R::throughput());\n        for num_rows in powers(TABLE_SIZE_POWERS) {\n            bench_index_delete(&mut make_row, group, name, num_rows, same_ratio);\n        }\n    }\n\n    let mut group = c.benchmark_group(\"index_delete\");\n\n    bench_many_table_sizes::<u64>(FixedLenRow::from_u64, &mut group, \"u64\", 0.0);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.0);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.01);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.05);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.10);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.25);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 0.50);\n    bench_many_table_sizes::<U32x8>(FixedLenRow::from_u64, &mut group, \"U32x8\", 1.00);\n    bench_many_table_sizes::<U32x64>(FixedLenRow::from_u64, &mut group, \"U32x64\", 0.0);\n    bench_many_table_sizes::<Box<str>>(|i| i.to_string().into(), &mut group, \"String\", 0.0);\n}\n\ncriterion_group!(index, index_insert, index_seek, index_delete);\n\ncriterion_main!(pages, table, index);\n"
  },
  {
    "path": "crates/table/benches/pointer_map.rs",
    "content": "//! Benchmarks for the `PointerMap`.\n//!\n//! The [`intmap`](https://crates.io/crates/intmap) crate was evaluated\n//! and showed significant (20-400%) regressions except for the 100% collisions case\n//! which is statistically impossible.\n//! This isn't entirely surprising, as the `nohash_hasher`\n//! does no work to hash the value as it is already a hash.\n\nuse criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion, Throughput};\nuse rand::rngs::ThreadRng;\nuse rand::seq::IndexedRandom;\nuse rand::Rng;\nuse spacetimedb_table::indexes::{PageIndex, PageOffset, RowHash, RowPointer, SquashedOffset};\nuse spacetimedb_table::pointer_map::PointerMap;\nuse std::time::{Duration, Instant};\n\ntype RngMut<'r> = &'r mut ThreadRng;\n\nfn gen_ptr(rng: RngMut<'_>) -> RowPointer {\n    let page = PageIndex(rng.random::<u64>());\n    let page_offset = PageOffset(rng.random::<u16>());\n    RowPointer::new(false, page, page_offset, SquashedOffset::TX_STATE)\n}\n\nfn gen_row_hash(rng: RngMut<'_>, max_range: u64) -> RowHash {\n    RowHash(rng.random_range(0..max_range))\n}\n\nfn gen_hash_and_ptrs(rng: RngMut<'_>, max: u64, count: usize) -> impl '_ + Iterator<Item = (RowHash, RowPointer)> {\n    (0..count).map(move |_| (gen_row_hash(rng, max), gen_ptr(rng)))\n}\n\nfn max_range(n: usize, collision_ratio: f64) -> u64 {\n    let n = n as f64;\n    let max_range = -1.0 / (-1.0 + f64::powf(1.0 - collision_ratio, 1.0 / (-1.0 + n)));\n    if max_range.is_finite() {\n        max_range as u64\n    } else {\n        u64::MAX\n    }\n}\n\nfn time(body: impl FnOnce()) -> Duration {\n    let start = Instant::now();\n    body();\n    start.elapsed()\n}\n\nfn bench_insert(c: &mut Criterion) {\n    const NUM_INSERTS_PER_MAP: usize = 1000;\n    let bench_insert_inner = |bench: &mut Bencher<'_, _>, collision_ratio: &f64| {\n        let preload_amt = 10_000;\n        let max_range = max_range(preload_amt + NUM_INSERTS_PER_MAP, *collision_ratio);\n\n        let mut rng = rand::rng();\n        let map = gen_hash_and_ptrs(&mut rng, max_range, preload_amt).collect::<PointerMap>();\n        let to_insert = gen_hash_and_ptrs(&mut rng, max_range, NUM_INSERTS_PER_MAP).collect::<Vec<_>>();\n\n        bench.iter_custom(|iters| {\n            let mut total_duration = Duration::from_secs(0);\n            let mut num_iters = 0;\n            while num_iters < iters {\n                let mut map = map.clone();\n                for (hash, ptr) in to_insert.iter().copied() {\n                    // Compute duration of map insertion.\n                    total_duration += time(|| {\n                        black_box(map.insert(black_box(hash), black_box(ptr)));\n                    });\n\n                    num_iters += 1;\n                    if num_iters >= iters {\n                        break;\n                    }\n                }\n                drop(map);\n            }\n            total_duration\n        });\n    };\n    let mut bench_group = c.benchmark_group(\"insert\");\n    bench_group.throughput(Throughput::Elements(1));\n    let mut bench = |percent, prob: f64| {\n        bench_group.bench_with_input(\n            BenchmarkId::new(\"load/10_000/insert/1000/collisions\", percent),\n            &prob,\n            bench_insert_inner,\n        );\n    };\n    bench(\"0%\", 0.00);\n    bench(\"1%\", 0.01);\n    bench(\"10%\", 0.10);\n    bench(\"50%\", 0.50);\n    bench(\"100%\", 1.0);\n    bench_group.finish();\n}\n\nfn bench_pointers_for(c: &mut Criterion) {\n    const NUM_GETS_PER_MAP: usize = 1000;\n    let bench_insert_inner = |bench: &mut Bencher<'_, _>, collision_ratio: &f64| {\n        let preload_amt = 10_000;\n        let max_range = max_range(preload_amt, *collision_ratio);\n\n        let mut rng = rand::rng();\n        let mut map = PointerMap::default();\n        let preloaded = gen_hash_and_ptrs(&mut rng, max_range, preload_amt).collect::<Vec<_>>();\n        let queries = preloaded\n            .choose_multiple(&mut rng, NUM_GETS_PER_MAP)\n            .collect::<Vec<_>>();\n\n        for (row_hash, ptr) in &preloaded {\n            map.insert(*row_hash, *ptr);\n        }\n\n        bench.iter_custom(|iters| {\n            let mut total_duration = Duration::from_secs(0);\n            let mut num_iters = 0;\n            while num_iters < iters {\n                for &&(hash, _) in &queries {\n                    // Compute duration of map insertion.\n                    total_duration += time(|| {\n                        black_box(map.pointers_for(black_box(hash)));\n                    });\n\n                    num_iters += 1;\n                    if num_iters >= iters {\n                        break;\n                    }\n                }\n            }\n            total_duration\n        });\n    };\n    let mut bench_group = c.benchmark_group(\"pointers_for\");\n    bench_group.throughput(Throughput::Elements(1));\n    let mut bench = |percent, prob: f64| {\n        bench_group.bench_with_input(\n            BenchmarkId::new(\"load/10_000/get/1000/collisions\", percent),\n            &prob,\n            bench_insert_inner,\n        );\n    };\n    bench(\"0%\", 0.00);\n    bench(\"1%\", 0.01);\n    bench(\"10%\", 0.10);\n    bench(\"50%\", 0.50);\n    bench(\"100%\", 1.0);\n\n    bench_group.finish();\n}\n\ncriterion_group!(benches, bench_insert, bench_pointers_for);\ncriterion_main!(benches);\n"
  },
  {
    "path": "crates/table/benches/var_len_visitor.rs",
    "content": "use core::slice;\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\nuse spacetimedb_sats::{AlgebraicType, ProductType};\nuse spacetimedb_table::indexes::{Byte, Bytes};\nuse spacetimedb_table::row_type_visitor::{dump_visitor_program, row_type_visitor, VarLenVisitorProgram};\nuse spacetimedb_table::var_len::{AlignedVarLenOffsets, NullVarLenVisitor, VarLenMembers, VarLenRef};\nuse std::mem;\n\nfn visit_count(row: &Bytes, visitor: &impl VarLenMembers) {\n    black_box(unsafe { visitor.visit_var_len(row) }.count());\n}\n\n#[allow(clippy::disallowed_macros)]\nfn visitor_program(row_ty: impl Into<ProductType>) -> VarLenVisitorProgram {\n    let row_ty: ProductType = row_ty.into();\n    let visitor = row_type_visitor(&row_ty.into());\n\n    eprintln!(\"Using visitor program:\");\n    dump_visitor_program(&visitor);\n    eprintln!();\n\n    visitor\n}\n\nfn visit_fixed_len(c: &mut C) {\n    let row = &[0xa5a5_a5a5u32; 1];\n    let row = row.as_ptr().cast::<Byte>();\n    let row = unsafe { slice::from_raw_parts(row, mem::size_of::<u32>()) };\n\n    let mut group = c.benchmark_group(\"visit_fixed_len/u64\");\n\n    let null_visitor = &NullVarLenVisitor;\n\n    group.bench_function(\"NullVarLenVisitor\", |b| {\n        b.iter(|| visit_count(row, null_visitor));\n    });\n\n    let offsets_visitor = &AlignedVarLenOffsets::from_offsets(&[]);\n\n    group.bench_function(\"AlignedVarLenOffsets\", |b| {\n        b.iter(|| visit_count(row, offsets_visitor));\n    });\n\n    let visitor = &visitor_program([AlgebraicType::U32]);\n\n    group.bench_function(\"VarLenVisitorProgram\", |b| {\n        b.iter(|| visit_count(row, visitor));\n    });\n}\n\nfn visit_var_len_product(c: &mut C) {\n    let row = &[VarLenRef::NULL; 1];\n    let row = row.as_ptr().cast::<Byte>();\n    let row = unsafe { slice::from_raw_parts(row, mem::size_of::<VarLenRef>()) };\n\n    let mut group = c.benchmark_group(\"visit_var_len_product/VarLenRef\");\n\n    let offsets_visitor = &AlignedVarLenOffsets::from_offsets(&[0]);\n\n    group.bench_function(\"AlignedVarLenOffsets\", |b| {\n        b.iter(|| visit_count(row, offsets_visitor));\n    });\n\n    let visitor = &visitor_program([AlgebraicType::String]);\n\n    group.bench_function(\"VarLenVisitorProgram\", |b| {\n        b.iter(|| visit_count(row, visitor));\n    });\n}\n\nfn visit_var_len_sum(c: &mut C) {\n    let mut group = c.benchmark_group(\"visit_var_len_sum/opt_str\");\n\n    let visitor = &visitor_program([AlgebraicType::sum([AlgebraicType::String, AlgebraicType::unit()])]);\n\n    let row = &mut [0xa5a5u16; 3];\n    let row = row.as_mut_ptr().cast::<Byte>();\n    let row = unsafe { slice::from_raw_parts_mut(row, 6) };\n\n    group.bench_function(\"none/VarLenVisitorProgram\", |b| {\n        // None\n        row[0] = 1;\n\n        b.iter(|| visit_count(row, visitor));\n    });\n\n    group.bench_function(\"some/VarLenVisitorProgram\", |b| {\n        // Some\n        row[0] = 0;\n\n        b.iter(|| visit_count(row, visitor));\n    });\n}\n\n// TODO: bring back perfcnt once cargo allows per-target-OS dev dependencies (it broke on mac)\nmod measurement {\n    use criterion::measurement::WallTime;\n    pub type Measurement = WallTime;\n    pub fn get() -> Measurement {\n        WallTime\n    }\n}\n\ntype C = Criterion<measurement::Measurement>;\nfn config() -> C {\n    Criterion::default().with_measurement(measurement::get())\n}\n\ncriterion_group!(\n    name = var_len_visitors;\n    config = config();\n    targets =\n        visit_fixed_len,\n        visit_var_len_product,\n        visit_var_len_sum\n);\n\ncriterion_main!(var_len_visitors);\n"
  },
  {
    "path": "crates/table/proptest-regressions/bflatn_to.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 88a42f843e12c460bd3b03b90c3ca02a96a9a632ab8a3e9ffff462f50282ee99 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(Bool) }, ProductTypeElement { name: None, algebraic_type: Sum(SumType { variants: [SumTypeVariant { name: None, algebraic_type: Builtin(I32) }, SumTypeVariant { name: None, algebraic_type: Builtin(I8) }] }) }] }, ProductValue { elements: [Bool(false), Sum(SumValue { tag: 0, value: I32(0) })] })\n"
  },
  {
    "path": "crates/table/proptest-regressions/btree_index.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 6d64f4cbc7b9419bb398ea63a792a77e265650c592e74729d247b16de2f7f17b # shrinks to ((cols, pv), ptr) = (([ColId(0)], ProductValue { elements: [Array([]), Array([]), I128(0), Bool(false), Sum(SumValue { tag: 0, value: F64(Total(0.0)) }), Product(ProductValue { elements: [Bool(false), U8(0), I128(0), U32(0), Sum(SumValue { tag: 0, value: U8(0) }), U8(0), U32(215254127), Product(ProductValue { elements: [] })] }), I128(39831157966956966676235972844869827525), String(\"$ኽ'{cCᠸ🛵`=𑍌:.1¥N𑌜Iﹳ\\\"9.zලC'נּ`\"), Array([7152110280299272705, 11681398356968567052, 1089317074085656246]), I16(-26517), Map({}), Map({I8(-125): U64(8656771766735343889), I8(-123): U64(11354150614744344657), I8(-109): U64(6824489582432753730), I8(-76): U64(13794281125277408796), I8(-73): U64(15475706408731022087), I8(-64): U64(14535941231843587119), I8(-63): U64(14230060952189702592), I8(-44): U64(7088265520780061145), I8(-1): U64(7685767798006742171), I8(24): U64(15400215746525807156), I8(28): U64(1665061385205215140), I8(59): U64(1530056886806704170), I8(67): U64(11242987644565198574), I8(92): U64(5588764734416174352)})] }), RowPointer(r: 0, pi: 338095043233, po: 58264, so: 0))\n"
  },
  {
    "path": "crates/table/proptest-regressions/pointer_map.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 14fb129e6672970b5e734edccc7ded859b78f3d46f5867ee998df9333cc69cef # shrinks to (hash, ptr) = (RowHash(0), RowPointer(0))\n"
  },
  {
    "path": "crates/table/proptest-regressions/read_column.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 9a8ba470c98263e7b6871ca4079acd30e347b2d4532f5a481aae7eb78e6f3994 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(U32) }, ProductTypeElement { name: None, algebraic_type: Builtin(U16) }, ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(U128) }, ProductTypeElement { name: None, algebraic_type: Builtin(U16) }, ProductTypeElement { name: None, algebraic_type: Builtin(I8) }, ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(F32) }, ProductTypeElement { name: None, algebraic_type: Builtin(String) }] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(U8) }, ProductTypeElement { name: None, algebraic_type: Builtin(Array(ArrayType { elem_ty: Builtin(U32) })) }, ProductTypeElement { name: None, algebraic_type: Builtin(Array(ArrayType { elem_ty: Builtin(String) })) }, ProductTypeElement { name: None, algebraic_type: Builtin(U32) }] }, ProductValue { elements: [Product(ProductValue { elements: [Product(ProductValue { elements: [] }), U32(0), U16(0), Product(ProductValue { elements: [] }), U128(13840125084752990830451071514702880), U16(45599), I8(52), Product(ProductValue { elements: [] }), F32(Total(2.022854e37)), String(\"֎𞹹PȺM?𞅏.{<B.🕴'.ȺwIﬖn<\\\"{🮪🕴``<�𝒞%ⁱ\")] }), U8(110), Array([2056746203, 181477477, 526438060, 4180078969, 974951910, 3532798473, 1277693402, 1404482362, 1853186792, 342656873]), Array([\"?\\\\%^ଢ଼\", \"ຄ\", \"מּ\"]), U32(247955447)] })\n"
  },
  {
    "path": "crates/table/proptest-regressions/row_hash.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 3e55b94365a0ae7698bb9e89259f3f5b84227b1c5ba2f0737be0360f55256c26 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Sum(SumType { variants: [SumTypeVariant { name: None, algebraic_type: Builtin(Bool) }] }) }] }, ProductValue { elements: [Sum(SumValue { tag: 0, value: Bool(false) })] })\ncc aedcfc0fa45005cb11fa8b47f668a8b68c99adadfb50fdc6219840aa8ffd83f6 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Sum(SumType { variants: [SumTypeVariant { name: None, algebraic_type: Builtin(I32) }, SumTypeVariant { name: None, algebraic_type: Builtin(F64) }, SumTypeVariant { name: None, algebraic_type: Builtin(String) }, SumTypeVariant { name: None, algebraic_type: Builtin(U16) }, SumTypeVariant { name: None, algebraic_type: Builtin(U16) }, SumTypeVariant { name: None, algebraic_type: Builtin(String) }, SumTypeVariant { name: None, algebraic_type: Builtin(Bool) }, SumTypeVariant { name: None, algebraic_type: Builtin(I16) }, SumTypeVariant { name: None, algebraic_type: Builtin(I16) }, SumTypeVariant { name: None, algebraic_type: Builtin(I64) }, SumTypeVariant { name: None, algebraic_type: Builtin(I128) }, SumTypeVariant { name: None, algebraic_type: Builtin(U64) }] }) }] }, ProductValue { elements: [Sum(SumValue { tag: 2, value: String(\"יּ/Ⱥ🂠છrÔ\") })] })\n"
  },
  {
    "path": "crates/table/proptest-regressions/ser.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 224fbf0d3996aab96ec3dd62774790a26dc81d5526dab88c0841d71caf3a7589 # shrinks to (ty, val) = (Builtin(Map(MapType { key_ty: Builtin(Bool), ty: Builtin(Bool) })), Map({}))\ncc 974a8221dc45760d5e44e017adab162e3bd574f376a06168d91bd01471600ef1 # shrinks to (ty, val) = (Builtin(I32), I32(-2))\ncc c1927b3257b3ff27076d627096e825fe0c18d2d1a066908518c3286257878118 # shrinks to (ty, val) = (Product(ProductType { elements: [] }), Product(ProductValue { elements: [] }))\ncc 1ac110bc5cbbac678326767f5c6d77f9d0ff5c5dd6ae456191160289ced4b404 # shrinks to (ty, val) = (Builtin(Map(MapType { key_ty: Builtin(U8), ty: Builtin(Map(MapType { key_ty: Builtin(I8), ty: Builtin(I128) })) })), Map({U8(0): Map({I8(-4): I128(0), I8(-3): I128(0), I8(-2): I128(0), I8(-1): I128(0), I8(0): I128(0), I8(1): I128(0), I8(2): I128(0), I8(3): I128(0), I8(4): I128(0), I8(5): I128(0)}), U8(1): Map({I8(-8): I128(0), I8(-7): I128(0), I8(-5): I128(0), I8(-4): I128(0), I8(-3): I128(0), I8(-2): I128(0), I8(-1): I128(0), I8(0): I128(0), I8(1): I128(0), I8(2): I128(0), I8(3): I128(0)}), U8(2): Map({I8(-5): I128(0), I8(-4): I128(0), I8(-3): I128(0), I8(-2): I128(0), I8(-1): I128(0), I8(0): I128(0), I8(1): I128(0), I8(2): I128(0), I8(3): I128(0), I8(4): I128(0), I8(5): I128(0), I8(6): I128(0), I8(7): I128(0), I8(8): I128(0), I8(46): I128(0)}), U8(3): Map({I8(-5): I128(0), I8(-4): I128(0), I8(-3): I128(0), I8(-2): I128(0), I8(-1): I128(0), I8(0): I128(0), I8(1): I128(0), I8(2): I128(0), I8(3): I128(0), I8(4): I128(0)}), U8(4): Map({I8(-16): I128(0), I8(-4): I128(0), I8(-3): I128(0), I8(-2): I128(0), I8(-1): I128(0), I8(0): I128(0), I8(1): I128(0), I8(2): I128(0), I8(3): I128(0), I8(4): I128(0), I8(5): I128(0)})}))\n"
  },
  {
    "path": "crates/table/proptest-regressions/static_bsatn_validator.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc a6756c136abdcebcb9502aafb473f334ddff07a00500f58527f1d0f9469b3dbf # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Sum(SumType { variants: [SumTypeVariant { name: Some(\"variant_0\"), algebraic_type: U8 }] }) }, ProductTypeElement { name: None, algebraic_type: Bool }] }, ProductValue { elements: [Sum(SumValue { tag: 0, value: U8(2) }), Bool(false)] })\n"
  },
  {
    "path": "crates/table/proptest-regressions/table.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc dfe7d6439b107313eb2d5810fd99d8e5002c0911b27526d84f1136473405b8ce # shrinks to (ty, val) = (ProductType { elements: [] }, ProductValue { elements: [] })\ncc 506db914ca1cdfa0f788f728d3f190402b24f6b158bfbf02b7128ec2a2c97062 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(I8) }, ProductTypeElement { name: None, algebraic_type: Builtin(U8) }] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(I8) }] }, ProductValue { elements: [Product(ProductValue { elements: [I8(0), U8(0)] }), I8(0)] })\ncc 62a433be11cbcb6a7d87e484a4fe50fcc83159571378777ee6ac6ff19d92f740 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(String) }] }, ProductValue { elements: [String(\" \")] })\ncc 7db525c6fd4b0404947db0964859afdf1f5b6a42a353ec2cb3b1185b7f7456ec # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(F32) }, ProductTypeElement { name: None, algebraic_type: Builtin(String) }] }, ProductValue { elements: [F32(Total(0.0)), String(\"a\")] })\ncc 1f295db61a02ac3378f5ffcceb084637d2391bcc1758af6fb2df8355a713e998 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(U8) }, ProductTypeElement { name: None, algebraic_type: Builtin(U32) }, ProductTypeElement { name: None, algebraic_type: Builtin(U8) }, ProductTypeElement { name: None, algebraic_type: Builtin(I32) }, ProductTypeElement { name: None, algebraic_type: Builtin(F64) }, ProductTypeElement { name: None, algebraic_type: Builtin(I32) }, ProductTypeElement { name: None, algebraic_type: Builtin(I8) }, ProductTypeElement { name: None, algebraic_type: Builtin(Bool) }, ProductTypeElement { name: None, algebraic_type: Builtin(U128) }, ProductTypeElement { name: None, algebraic_type: Builtin(I64) }, ProductTypeElement { name: None, algebraic_type: Builtin(I32) }, ProductTypeElement { name: None, algebraic_type: Builtin(I64) }] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(Array(ArrayType { elem_ty: Product(ProductType { elements: [] }) })) }, ProductTypeElement { name: None, algebraic_type: Builtin(I128) }, ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(U8) }, ProductTypeElement { name: None, algebraic_type: Builtin(I32) }, ProductTypeElement { name: None, algebraic_type: Builtin(U32) }, ProductTypeElement { name: None, algebraic_type: Builtin(F64) }, ProductTypeElement { name: None, algebraic_type: Builtin(I32) }, ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(U128) }, ProductTypeElement { name: None, algebraic_type: Builtin(I128) }, ProductTypeElement { name: None, algebraic_type: Builtin(F32) }] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(Array(ArrayType { elem_ty: Builtin(Bool) })) }, ProductTypeElement { name: None, algebraic_type: Builtin(Map(MapType { key_ty: Builtin(I32), ty: Builtin(F64) })) }, ProductTypeElement { name: None, algebraic_type: Builtin(Map(MapType { key_ty: Builtin(String), ty: Product(ProductType { elements: [] }) })) }, ProductTypeElement { name: None, algebraic_type: Product(ProductType { elements: [] }) }, ProductTypeElement { name: None, algebraic_type: Builtin(String) }, ProductTypeElement { name: None, algebraic_type: Builtin(Map(MapType { key_ty: Builtin(I64), ty: Builtin(I8) })) }] }, ProductValue { elements: [Product(ProductValue { elements: [U8(11), U32(2704794471), U8(8), I32(560143777), F64(Total(-9.491689542097868e133)), I32(985486457), I8(-29), Bool(true), U128(86863927427475495075905443157762268537), I64(-8445479071889615496), I32(-1170195986), I64(6816263725212890698)] }), Array([ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }, ProductValue { elements: [] }]), I128(-128354251946811180649547031748393312830), Product(ProductValue { elements: [U8(15), I32(-368395546), U32(2838077641), F64(Total(1.2470445399331832e247)), I32(2008554396), Product(ProductValue { elements: [] }), U128(61997091902348291924599987290041086259), I128(-133941015622227481390314668001705570040), F32(Total(0.15960509))] }), Array([false]), Map({I32(-2059030289): F64(Total(-1.6078720198488e-228)), I32(-1525181441): F64(Total(1.710242945029728e-144)), I32(-1052673626): F64(Total(-3.440596620446167e-309)), I32(-1003357692): F64(Total(2.6586000314438054e194)), I32(-486984568): F64(Total(-5.102821862861034e-213)), I32(-301908528): F64(Total(-5.4166271500898735e240)), I32(-167581317): F64(Total(-3.8367297211220085e287)), I32(21583530): F64(Total(-0.0)), I32(405655173): F64(Total(9.047781752663224e-285)), I32(729921068): F64(Total(-1.8324261076055166e-308)), I32(1027660437): F64(Total(-2.1714048524014455e-308)), I32(1446645815): F64(Total(-2.6032637839486984e-299))}), Map({String(\"\"): Product(ProductValue { elements: [] }), String(\"&?:Pථ-aé𖾕স¥:𐽴𑰇r:ῲxS�~\"): Product(ProductValue { elements: [] }), String(\"*𖿰<ᝈ\"): Product(ProductValue { elements: [] }), String(\"<7=ઞ*y𐋧=`TȺ\\u{11a09}v6&'\\\\<@`$আ:/W'.\"): Product(ProductValue { elements: [] }), String(\"?\\u{b4d}𖭭𞸕ᥔ¾\\\"᪗F\\\"/.Ⱥ໑𝕆\\\"சਇ¥Z&U69~\"): Product(ProductValue { elements: [] }), String(\"L<%/{🛤.<¥𞹉ᾶ\"): Product(ProductValue { elements: [] }), String(\"u𐡿BѨ{𑈎𞱻؏ං/6j⺓.K𐤗𐏔O<<r\"): Product(ProductValue { elements: [] }), String(\"Ⱥ🕴X𚿽&%a&𓑅🉅Åk`𞓖DSMx'𐧅*¥𑊈\\\"🔮𞅎\"): Product(ProductValue { elements: [] }), String(\"𐲦`=q<Ѩ🛵{=:&]K7&?𑊍כּ'h.\\\"$$\"): Product(ProductValue { elements: [] }), String(\"𞹏𑧇�=í𑖪i.︗:𐤲%\\\"@'Bh<𐖐𐋴5HȺ`$Ѩ\\\\*&\"): Product(ProductValue { elements: [] }), String(\"𱶴ꩊb\"): Product(ProductValue { elements: [] })}), Product(ProductValue { elements: [] }), String(\"'ₜ𐠈𱷶*!𐫛dⷘ\\u{e0169}𖾗\"), Map({I64(-9030290742665700267): I8(-13), I64(-6470697391802226383): I8(-127), I64(-2634204048655862644): I8(40), I64(-2107696645757475826): I8(85), I64(-732265304332738185): I8(45), I64(1147016759885247030): I8(111), I64(3599640790078676137): I8(-63), I64(4789426107927977142): I8(-71), I64(6297834554178975959): I8(-8), I64(7600031141858237684): I8(-61)})] })\ncc 776d142680b35d7dad5b558fea7071b095f7e6a23c8549e9b32b452d5eebf92b # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(String) }] }, ProductValue { elements: [String(\"\\u{16af0}a®ਲ𒒀A 𑌅 ಎ꒐𑍇A A𐫫Aⷀ𑌵ૠ\\u{b55} aㄱ \\u{f99}a \")] })\ncc 66d99531b8e513d0fd558f492f708d110e1e117dfc7f3f42188bcc57c23bb89e # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(Map(MapType { key_ty: Builtin(U8), ty: Builtin(Map(MapType { key_ty: Builtin(I32), ty: Builtin(F32) })) })) }] }, ProductValue { elements: [Map({U8(0): Map({I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(-0.0)), I32(-1): F32(Total(-0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0))}), U8(1): Map({I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(-0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0)), I32(4): F32(Total(0.0)), I32(5): F32(Total(0.0)), I32(6): F32(Total(0.0)), I32(7): F32(Total(0.0))}), U8(2): Map({I32(-3): F32(Total(-0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(-0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0))}), U8(3): Map({I32(-10): F32(Total(0.0)), I32(-9): F32(Total(0.0)), I32(-8): F32(Total(-0.0)), I32(-7): F32(Total(0.0)), I32(-6): F32(Total(0.0)), I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0))}), U8(4): Map({I32(-7): F32(Total(0.0)), I32(-6): F32(Total(0.0)), I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0))}), U8(5): Map({I32(-9): F32(Total(0.0)), I32(-8): F32(Total(0.0)), I32(-7): F32(Total(0.0)), I32(-6): F32(Total(0.0)), I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0)), I32(4): F32(Total(0.0)), I32(5): F32(Total(0.0))}), U8(6): Map({I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0))}), U8(7): Map({I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(-0.0)), I32(3): F32(Total(0.0))}), U8(8): Map({I32(-7): F32(Total(0.0)), I32(-6): F32(Total(-0.0)), I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0)), I32(4): F32(Total(0.0)), I32(5): F32(Total(0.0)), I32(6): F32(Total(-0.0)), I32(7): F32(Total(0.0))}), U8(9): Map({I32(-1349171619): F32(Total(418648100.0)), I32(-665792478): F32(Total(-5.3081414e23)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0)), I32(5): F32(Total(-0.0)), I32(906732021): F32(Total(1.952517e16)), I32(1965197035): F32(Total(1020.84216))}), U8(11): Map({I32(-7): F32(Total(0.0)), I32(-6): F32(Total(0.0)), I32(-5): F32(Total(0.0)), I32(-4): F32(Total(0.0)), I32(-3): F32(Total(0.0)), I32(-2): F32(Total(0.0)), I32(-1): F32(Total(0.0)), I32(0): F32(Total(0.0)), I32(1): F32(Total(0.0)), I32(2): F32(Total(0.0)), I32(3): F32(Total(0.0)), I32(4): F32(Total(0.0)), I32(5): F32(Total(0.0)), I32(6): F32(Total(0.0))})})] })\ncc 7f478c4dd0f24e715a74949c6d06af8ca2b4c8b82fae4f53c953a2b323cff851 # shrinks to (ty, val) = (ProductType { elements: [ProductTypeElement { name: None, algebraic_type: Builtin(Array(ArrayType { elem_ty: Builtin(Map(MapType { key_ty: Builtin(U64), ty: Builtin(Bool) })) })) }] }, ProductValue { elements: [Array([{U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false), U64(10): Bool(false), U64(11): Bool(false), U64(12): Bool(false), U64(13): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false), U64(10): Bool(false), U64(11): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false), U64(10): Bool(false), U64(11): Bool(false), U64(12): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false), U64(10): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false), U64(6): Bool(false), U64(7): Bool(false), U64(8): Bool(false), U64(9): Bool(false), U64(10): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false), U64(2): Bool(false), U64(3): Bool(false), U64(4): Bool(false), U64(5): Bool(false)}, {U64(0): Bool(false), U64(1): Bool(false)}])] })\n"
  },
  {
    "path": "crates/table/proptest-regressions/table_index/mod.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 3276d3db4a1a70d78db9a6a01eaa3bba810a2317e9c67e4d5d8d93cbba472c99 # shrinks to ((ty, cols, pv), is_unique) = ((ProductType {None: Bool}, [ColId(0)], ProductValue { elements: [Bool(false)] }), false)\ncc bc80b80ac2390452c0a152d2c6e2abc29ce146642f3cd0fe136ffe6173cf4c8c # shrinks to (ty, cols, pv) = (ProductType {None: I64}, [ColId(0)], ProductValue { elements: [I64(0)] }), kind = Direct\ncc c1e4c959a32f6ab8ef9c4e29d39a24ec47cb03524584606a7f1fa4563f0f8cca # shrinks to (ty, cols, pv) = (ProductType {None: Sum(SumType {\"variant_0\": Product(ProductType {})})}, [ColId(0)], ProductValue { elements: [Sum(SumValue { tag: 0, value: Product(ProductValue { elements: [] }) })] }), kind = Direct\n"
  },
  {
    "path": "crates/table/proptest-regressions/table_index/unique_directer_index.txt",
    "content": "# Seeds for failure cases proptest has generated in the past. It is\n# automatically read and these particular cases re-run before any\n# novel cases are generated.\n#\n# It is recommended to check this file in to source control so that\n# everyone who runs the test benefits from these saved cases.\ncc 471733dcdf80f2858344c15b6297070a312d2051ecf0eb56eaf9bf38cd51aab4 # shrinks to start = 38, end = 0\ncc 98241ddfa4ab59c91a264cbbd364619c1d02a912406c4402cd01fb0c10a72e10 # shrinks to start = 75, end = 0\ncc b058bb0beeaf3bb4f97854dff2bf835e2ea4eaffb1eb3c13c78652018b02b957 # shrinks to start = 130, end = 0, key = 0\ncc dc8757b81c7b7cffe9e4a9d43b75cd5814d5fb0667a0c1f401fd5f402fde3d97 # shrinks to start = 37, end = 37, key = 0\n"
  },
  {
    "path": "crates/table/src/bflatn_from.rs",
    "content": "//! Provides the function [`read_row_from_page(ser, page, fixed_offset, ty)`]\n//! which serializes `value = page.get_row_data(fixed_offset, fixed_row_size)` typed at `ty`\n//! and associated var len objects in `value` into the serializer `ser`.\n\nuse super::{\n    blob_store::BlobStore,\n    indexes::{Bytes, PageOffset},\n    page::Page,\n    row_hash,\n    var_len::VarLenRef,\n};\nuse core::cell::Cell;\nuse core::str;\nuse spacetimedb_sats::{\n    i256, impl_serialize,\n    layout::{\n        align_to, AlgebraicTypeLayout, HasLayout as _, ProductTypeLayoutView, RowTypeLayout, SumTypeLayout, VarLenType,\n    },\n    ser::{SerializeNamedProduct, Serializer},\n    u256, ArrayType,\n};\n\n/// Serializes the row in `page` where the fixed part starts at `fixed_offset`\n/// and lasts `ty.size()` bytes. This region is typed at `ty`.\n///\n/// # Safety\n///\n/// 1. the `fixed_offset` must point at a row in `page` lasting `ty.size()` byte.\n/// 2. the row must be a valid `ty`.\n/// 3. for any `vlr: VarLenRef` stored in the row,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\npub unsafe fn serialize_row_from_page<S: Serializer>(\n    ser: S,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    fixed_offset: PageOffset,\n    ty: &RowTypeLayout,\n) -> Result<S::Ok, S::Error> {\n    let fixed_bytes = page.get_row_data(fixed_offset, ty.size());\n    // SAFETY:\n    // - Per 1. and 2., `fixed_bytes` points at a row in `page` valid for `ty`.\n    // - Per 3., for any `vlr: VarLenRef` stored in `fixed_bytes`,\n    //   `vlr.first_offset` is either `NULL` or points to a valid granule in `page`.\n    unsafe { serialize_product(ser, fixed_bytes, page, blob_store, &Cell::new(0), ty.product()) }\n}\n\n/// This has to be a `Cell<_>` here as we only get `&Value` in `Serialize`.\ntype CurrOffset<'a> = &'a Cell<usize>;\n\n/// Updates `curr_offset` by running `with` on a copy of its current value.\nfn update<R>(curr_offset: CurrOffset<'_>, with: impl FnOnce(&mut usize) -> R) -> R {\n    let mut tmp = curr_offset.get();\n    let ret = with(&mut tmp);\n    curr_offset.set(tmp);\n    ret\n}\n\n/// Serializes every product field in `value = &bytes[range_move(0..ty.size(), *curr_offset)]`,\n/// which is typed at `ty`, into `ser`.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\nunsafe fn serialize_product<S: Serializer>(\n    ser: S,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: CurrOffset<'_>,\n    ty: ProductTypeLayoutView<'_>,\n) -> Result<S::Ok, S::Error> {\n    let elems = &ty.elements;\n    let mut ser = ser.serialize_named_product(elems.len())?;\n\n    let my_offset = curr_offset.get();\n\n    for elem_ty in elems.iter() {\n        curr_offset.set(my_offset + elem_ty.offset as usize);\n        // SAFETY: By 1., `value` is valid at `ty`,\n        // so it follows that valid and properly aligned sub-`value`s\n        // are valid `elem_ty.ty`s.\n        // By 2., and the above, it follows that sub-`value`s won't have dangling `VarLenRef`s.\n        let value = Value {\n            bytes,\n            page,\n            blob_store,\n            curr_offset,\n            ty: &elem_ty.ty,\n        };\n        ser.serialize_element(elem_ty.name.as_deref(), &value)?;\n    }\n\n    ser.end()\n}\n\n/// Serializes the sum value in `value = &bytes[range_move(0..ty.size(), *curr_offset)]`,\n/// which is typed at `ty`, into `ser`.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\nunsafe fn serialize_sum<S: Serializer>(\n    ser: S,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: CurrOffset<'_>,\n    ty: &SumTypeLayout,\n) -> Result<S::Ok, S::Error> {\n    // Read the tag of the sum value.\n    let (tag, data_ty) = read_tag(bytes, ty, curr_offset.get());\n\n    // Serialize the variant data value.\n    let data_offset = &Cell::new(curr_offset.get() + ty.offset_of_variant_data(tag));\n    // SAFETY: `value` is valid at `ty` so given `tag`,\n    // we know `data_value = &bytes[range_move(0..data_ty.size(), data_offset))`\n    // is valid at `data_ty`.\n    // By 2., and the above, we also know that `data_value` won't have dangling `VarLenRef`s.\n    let data_value = Value {\n        bytes,\n        page,\n        blob_store,\n        curr_offset: data_offset,\n        ty: data_ty,\n    };\n    let ret = ser.serialize_variant(tag, None, &data_value);\n\n    update(curr_offset, |co| *co += ty.size());\n    ret\n}\n\n/// Reads the tag of the sum value and selects the data variant type.\npub fn read_tag<'ty>(bytes: &Bytes, ty: &'ty SumTypeLayout, curr_offset: usize) -> (u8, &'ty AlgebraicTypeLayout) {\n    let tag_offset = ty.offset_of_tag();\n    let tag = bytes[curr_offset + tag_offset];\n\n    // Extract the variant data type depending on the tag.\n    let data_ty = &ty.variants[tag as usize].ty;\n\n    (tag, data_ty)\n}\n\n/// A `Serialize` version of `serialize_value`.\n///\n/// SAFETY: Constructing a value of this type\n/// has the same safety requirements as calling `serialize_value`.\nstruct Value<'a> {\n    bytes: &'a Bytes,\n    page: &'a Page,\n    blob_store: &'a dyn BlobStore,\n    curr_offset: CurrOffset<'a>,\n    ty: &'a AlgebraicTypeLayout,\n}\n\nimpl_serialize!(['a] Value<'a>, (self, ser) => {\n    unsafe { serialize_value(ser, self.bytes, self.page, self.blob_store, self.curr_offset, self.ty) }\n});\n\n/// Serialize `value = &bytes[range_move(0..ty.size(), *curr_offset)]` into a `ser`,\n/// using `blob_store` to retrieve the bytes of any large blob object\n/// and `curr_offset`, advanced as serialization progresses, to decide where to start reading.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\n/// 3. `align_to(curr_offset.get(), ty.align())` must be the offset of a field typed at `ty`.\npub(crate) unsafe fn serialize_value<S: Serializer>(\n    ser: S,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: CurrOffset<'_>,\n    ty: &AlgebraicTypeLayout,\n) -> Result<S::Ok, S::Error> {\n    debug_assert_eq!(\n        curr_offset.get(),\n        align_to(curr_offset.get(), ty.align()),\n        \"curr_offset {curr_offset:?} insufficiently aligned for type {ty:#?}\",\n    );\n\n    match ty {\n        AlgebraicTypeLayout::Sum(ty) => {\n            // SAFETY: `value` was valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { serialize_sum(ser, bytes, page, blob_store, curr_offset, ty) }\n        }\n        AlgebraicTypeLayout::Product(ty) => {\n            // SAFETY: `value` was valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { serialize_product(ser, bytes, page, blob_store, curr_offset, ty.view()) }\n        }\n        // The primitive types:\n        //\n        // SAFETY (applies to app primitive types):\n        // Per caller requirement, know `value` points to a valid `ty`.\n        // Thus `&bytes[range_move(0..ty.size(), *curr_offset)]` points to init bytes\n        // and `ty.size()` corresponds exactly to `N = 1, 1, 1, 2, 2, 4, 4, 8, 8, 16, 16, 32, 32, 4, 8`.\n        &AlgebraicTypeLayout::Bool => ser.serialize_bool(unsafe { read_from_bytes::<u8>(bytes, curr_offset) } != 0),\n        &AlgebraicTypeLayout::I8 => ser.serialize_i8(unsafe { read_from_bytes(bytes, curr_offset) }),\n        &AlgebraicTypeLayout::U8 => ser.serialize_u8(unsafe { read_from_bytes(bytes, curr_offset) }),\n        &AlgebraicTypeLayout::I16 => {\n            ser.serialize_i16(i16::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::U16 => {\n            ser.serialize_u16(u16::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::I32 => {\n            ser.serialize_i32(i32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::U32 => {\n            ser.serialize_u32(u32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::I64 => {\n            ser.serialize_i64(i64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::U64 => {\n            ser.serialize_u64(u64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::I128 => {\n            ser.serialize_i128(i128::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::U128 => {\n            ser.serialize_u128(u128::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::I256 => {\n            ser.serialize_i256(i256::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::U256 => {\n            ser.serialize_u256(u256::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::F32 => {\n            ser.serialize_f32(f32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n        &AlgebraicTypeLayout::F64 => {\n            ser.serialize_f64(f64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }))\n        }\n\n        // The var-len cases.\n        &AlgebraicTypeLayout::String => {\n            // SAFETY: `value` was valid at `::String` and `VarLenRef`s won't be dangling.\n            unsafe { serialize_string(ser, bytes, page, blob_store, curr_offset) }\n        }\n        AlgebraicTypeLayout::VarLen(VarLenType::Array(ty)) => {\n            // SAFETY: `value` was valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { serialize_array(ser, bytes, page, blob_store, curr_offset, ty) }\n        }\n    }\n}\n\n/// Serialize `value = &bytes[range_move(0..::String.size(), *curr_offset)]` into a `ser`,\n/// using `blob_store` to retrieve the bytes of any large blob object\n/// and `curr_offset`, advanced as serialization progresses, to decide where to start reading.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `::String` and properly aligned for `::String``.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\nunsafe fn serialize_string<S: Serializer>(\n    ser: S,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: CurrOffset<'_>,\n) -> Result<S::Ok, S::Error> {\n    // SAFETY: `value` was valid at and aligned for `::String`\n    // which stores a `vlr: VarLenRef` as its fixed value.\n    // The range thus is valid and properly aligned for `VarLenRef`.\n    let vlr = unsafe { read_from_bytes::<VarLenRef>(bytes, curr_offset) };\n\n    if vlr.is_large_blob() {\n        // SAFETY: As `vlr` a blob, `vlr.first_granule` always points to a valid granule.\n        let blob = unsafe { vlr_blob_bytes(page, blob_store, vlr) };\n        // SAFETY: For `::String`, the blob will always be valid UTF-8.\n        let str = unsafe { str::from_utf8_unchecked(blob) };\n        ser.serialize_str(str)\n    } else {\n        // SAFETY: `vlr.first_granule` is either NULL or points to a valid granule.\n        let var_iter = unsafe { page.iter_vlo_data(vlr.first_granule) };\n        let total_len = vlr.length_in_bytes as usize;\n        // SAFETY:\n        // - `total_len <= isize::MAX` is the total length of the granules concatenated.\n        // - The aforementioned concatenation is valid UTF-8.\n        unsafe { ser.serialize_str_in_chunks(total_len, var_iter) }\n    }\n}\n\nunsafe fn serialize_array<S: Serializer>(\n    ser: S,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: CurrOffset<'_>,\n    ty: &ArrayType,\n) -> Result<S::Ok, S::Error> {\n    // SAFETY: `value` was valid at and aligned for `ty`.\n    // These `ty` store a `vlr: VarLenRef` as their fixed value.\n    // The range thus is valid and properly aligned for `VarLenRef`.\n    let vlr = unsafe { read_from_bytes::<VarLenRef>(bytes, curr_offset) };\n\n    if vlr.is_large_blob() {\n        // SAFETY: As `vlr` is a blob, `vlr.first_granule` always points to a valid granule.\n        let blob = unsafe { vlr_blob_bytes(page, blob_store, vlr) };\n        // SAFETY: The BSATN in `blob` is encoded from an `AlgebraicValue`.\n        unsafe { ser.serialize_bsatn(ty, blob) }\n    } else {\n        // SAFETY: `vlr.first_granule` is either NULL or points to a valid granule.\n        let var_iter = unsafe { page.iter_vlo_data(vlr.first_granule) };\n        let total_len = vlr.length_in_bytes as usize;\n        // SAFETY:\n        // - `total_len <= isize::MAX` is the total length of the granules concatenated.\n        // - The BSATN in `blob` is encoded from an `AlgebraicValue`.\n        unsafe { ser.serialize_bsatn_in_chunks(ty, total_len, var_iter) }\n    }\n}\n\n/// Get the large blob object that `vlr.first_granule` points to.\n///\n/// SAFETY:\n/// - `vlr.first_granule` must point to a valid granule in `page`.\n#[cold]\n#[inline(never)]\npub(crate) unsafe fn vlr_blob_bytes<'b>(page: &Page, blob_store: &'b dyn BlobStore, vlr: VarLenRef) -> &'b [u8] {\n    // Read the blob hash.\n    // SAFETY: `vlr.first_granule` points to a valid granule.\n    let mut var_iter = unsafe { page.iter_var_len_object(vlr.first_granule) };\n    let granule = var_iter.next();\n    // SAFETY: As it pointed to a valid granule and not null,\n    // the iterator will never yield `None` on the first call.\n    let granule = unsafe { granule.unwrap_unchecked() };\n    let hash = granule.blob_hash();\n\n    // Find the blob.\n    blob_store.retrieve_blob(&hash).unwrap()\n}\n\n/// Read a `T` from `bytes` at the `curr_offset` and advance by `size` bytes.\n///\n/// # Safety\n///\n/// Let `value = &bytes[range_move(0..size_of::<T>(), *curr_offset)]`.\n/// Then `value` must point to a valid `T` and must be properly aligned for `T`.\npub unsafe fn read_from_bytes<T: Copy>(bytes: &Bytes, curr_offset: CurrOffset<'_>) -> T {\n    // SAFETY: forward caller requirements.\n    update(curr_offset, |co| unsafe { row_hash::read_from_bytes(bytes, co) })\n}\n"
  },
  {
    "path": "crates/table/src/bflatn_to.rs",
    "content": "//! Provides the functions [`write_row_to_pages(pages, blob_store, ty, val)`]\n//! and [`write_row_to_page(page, blob_store, visitor, ty, val)`]\n//! which write `val: ProductValue` typed at `ty` to `page` and `pages` respectively.\n\nuse super::{\n    blob_store::BlobStore,\n    indexes::{Bytes, PageOffset, RowPointer, SquashedOffset},\n    page::{GranuleOffsetIter, Page, VarView},\n    page_pool::PagePool,\n    pages::Pages,\n    table::BlobNumBytes,\n    util::range_move,\n    var_len::{VarLenGranule, VarLenMembers, VarLenRef},\n};\nuse spacetimedb_sats::{\n    bsatn::{self, to_writer, DecodeError},\n    buffer::BufWriter,\n    de::DeserializeSeed as _,\n    i256,\n    layout::{\n        align_to, AlgebraicTypeLayout, HasLayout, ProductTypeLayoutView, RowTypeLayout, SumTypeLayout, VarLenType,\n    },\n    u256, AlgebraicType, AlgebraicValue, ProductValue, SumValue,\n};\nuse thiserror::Error;\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum Error {\n    #[error(transparent)]\n    Decode(#[from] DecodeError),\n    #[error(\"Expected a value of type {0:?}, but found {1:?}\")]\n    WrongType(AlgebraicType, AlgebraicValue),\n    #[error(transparent)]\n    PageError(#[from] super::page::Error),\n    #[error(transparent)]\n    PagesError(#[from] super::pages::Error),\n}\n\n/// Writes `row` typed at `ty` to `pages`\n/// using `blob_store` as needed to write large blobs.\n///\n/// Panics if `val` is not of type `ty`.\n///\n/// # Safety\n///\n/// `pages` must be specialized to store rows of `ty`.\n/// This includes that its `visitor` must be prepared to visit var-len members within `ty`,\n/// and must do so in the same order as a `VarLenVisitorProgram` for `ty` would,\n/// i.e. by monotonically increasing offsets.\npub unsafe fn write_row_to_pages_bsatn(\n    pool: &PagePool,\n    pages: &mut Pages,\n    visitor: &impl VarLenMembers,\n    blob_store: &mut dyn BlobStore,\n    ty: &RowTypeLayout,\n    mut bytes: &[u8],\n    squashed_offset: SquashedOffset,\n) -> Result<(RowPointer, BlobNumBytes), Error> {\n    let val = ty.product().deserialize(bsatn::Deserializer::new(&mut bytes))?;\n    unsafe { write_row_to_pages(pool, pages, visitor, blob_store, ty, &val, squashed_offset) }\n}\n\n/// Writes `row` typed at `ty` to `pages`\n/// using `blob_store` as needed to write large blobs.\n///\n/// Panics if `val` is not of type `ty`.\n///\n/// # Safety\n///\n/// `pages` must be specialized to store rows of `ty`.\n/// This includes that its `visitor` must be prepared to visit var-len members within `ty`,\n/// and must do so in the same order as a `VarLenVisitorProgram` for `ty` would,\n/// i.e. by monotonically increasing offsets.\npub unsafe fn write_row_to_pages(\n    pool: &PagePool,\n    pages: &mut Pages,\n    visitor: &impl VarLenMembers,\n    blob_store: &mut dyn BlobStore,\n    ty: &RowTypeLayout,\n    val: &ProductValue,\n    squashed_offset: SquashedOffset,\n) -> Result<(RowPointer, BlobNumBytes), Error> {\n    let num_granules = if ty.layout().fixed {\n        // Fast-path: The row type doesn't contain var-len members,\n        // so 0 granules are needed.\n        0\n    } else {\n        required_var_len_granules_for_row(val)\n    };\n\n    match pages.with_page_to_insert_row(pool, ty.size(), num_granules, |page| {\n        // SAFETY:\n        // - Caller promised that `pages` is suitable for storing instances of `ty`\n        //   so `page` is also suitable.\n        // - Caller promised that `visitor` is prepared to visit for `ty`\n        //   and in the same order as a `VarLenVisitorProgram` for `ty` would.\n        // - `visitor` came from `pages` which we can trust to visit in the right order.\n        unsafe { write_row_to_page(page, blob_store, visitor, ty, val) }\n    })? {\n        (page, Ok((offset, blob_inserted))) => {\n            Ok((RowPointer::new(false, page, offset, squashed_offset), blob_inserted))\n        }\n        (_, Err(e)) => Err(e),\n    }\n}\n\n/// Writes `row` typed at `ty` to `page`\n/// using `blob_store` as needed to write large blobs\n/// and `visitor` to fixup var-len pointers in the fixed-len row part.\n///\n/// Panics if `val` is not of type `ty`.\n///\n/// # Safety\n///\n/// - `page` must be prepared to store instances of `ty`.\n///\n/// - `visitor` must be prepared to visit var-len members within `ty`,\n///   and must do so in the same order as a `VarLenVisitorProgram` for `ty` would,\n///   i.e. by monotonically increasing offsets.\n///\n/// - `page` must use a var-len visitor which visits the same var-len members in the same order.\npub unsafe fn write_row_to_page(\n    page: &mut Page,\n    blob_store: &mut dyn BlobStore,\n    visitor: &impl VarLenMembers,\n    ty: &RowTypeLayout,\n    val: &ProductValue,\n) -> Result<(PageOffset, BlobNumBytes), Error> {\n    let fixed_row_size = ty.size();\n    // SAFETY: We've used the right `row_size` and we trust that others have too.\n    // `RowTypeLayout` also ensures that we satisfy the minimum row size.\n    let fixed_offset = unsafe { page.alloc_fixed_len(fixed_row_size)? };\n\n    // Create the context for writing to `page`.\n    let (mut fixed, var_view) = page.split_fixed_var_mut();\n    let mut serialized = BflatnSerializedRowBuffer {\n        fixed_buf: fixed.get_row_mut(fixed_offset, fixed_row_size),\n        curr_offset: 0,\n        var_view,\n        last_allocated_var_len_index: 0,\n        large_blob_insertions: Vec::new(),\n    };\n\n    // Write the row to the page. Roll back on any failure.\n    if let Err(e) = serialized.write_product(ty.product(), val) {\n        // SAFETY: The `visitor` is proper for the row type per caller requirements.\n        unsafe { serialized.roll_back_var_len_allocations(visitor) };\n        // SAFETY:\n        // - `fixed_offset` came from `alloc_fixed_len` so it is in bounds of `page`.\n        // - `RowTypeLayout::size()` ensures `fixed_offset` is properly aligned for `FreeCellRef`.\n        unsafe { fixed.free(fixed_offset, fixed_row_size) };\n        return Err(e);\n    }\n\n    let blob_store_inserted_bytes = if ty.layout.fixed {\n        // The layout is fixed, so there are no large blobs to write.\n        <_>::default()\n    } else {\n        // Haven't stored large blobs or init those granules with blob hashes yet,\n        // so do it now.\n        serialized.write_large_blobs(blob_store)\n    };\n\n    Ok((fixed_offset, blob_store_inserted_bytes))\n}\n\n/// The writing / serialization context used by the function [`write_row_to_page`].\nstruct BflatnSerializedRowBuffer<'page> {\n    /// The work-in-progress fixed part of the row,\n    /// allocated inside the page.\n    fixed_buf: &'page mut Bytes,\n\n    /// The current offset into `fixed_buf` at which we are writing.\n    ///\n    /// The various writing methods will advance `curr_offset`.\n    curr_offset: usize,\n\n    /// The number of inserted var-len objects to the page.\n    last_allocated_var_len_index: usize,\n\n    /// The deferred large-blob insertions\n    /// with `Vec<u8>` being the blob bytes to insert to the blob store\n    /// and the `VarLenRef` being the destination to write the blob hash.\n    large_blob_insertions: Vec<(VarLenRef, Vec<u8>)>,\n\n    /// The mutable view of the variable section of the page.\n    var_view: VarView<'page>,\n}\n\nimpl BflatnSerializedRowBuffer<'_> {\n    /// Rolls back all the var-len allocations made when writing the row.\n    ///\n    /// # Safety\n    ///\n    /// The `visitor` must be proper for the row type.\n    unsafe fn roll_back_var_len_allocations(&mut self, visitor: &impl VarLenMembers) {\n        // SAFETY:\n        // - `fixed_buf` is properly aligned for the row type\n        //    and `fixed_buf.len()` matches exactly the size of the row type.\n        // - `fixed_buf`'s `VarLenRef`s are initialized up to `last_allocated_var_len_index`.\n        // - `visitor` is proper for the row type.\n        let visitor_iter = unsafe { visitor.visit_var_len(self.fixed_buf) };\n        for vlr in visitor_iter.take(self.last_allocated_var_len_index) {\n            // SAFETY: The `vlr` came from the allocation in `write_var_len_obj`\n            // which wrote it to the fixed part using `write_var_len_ref`.\n            // Thus, it points to a valid `VarLenGranule`.\n            unsafe { self.var_view.free_object_ignore_blob(*vlr) };\n        }\n    }\n\n    /// Insert all large blobs into `blob_store` and their hashes to their granules.\n    #[cold]\n    #[inline(never)]\n    fn write_large_blobs(mut self, blob_store: &mut dyn BlobStore) -> BlobNumBytes {\n        let mut blob_store_inserted_bytes = BlobNumBytes::default();\n        for (vlr, value) in self.large_blob_insertions {\n            // SAFETY: `vlr` was given to us by `alloc_for_slice`\n            // so it is properly aligned for a `VarLenGranule` and in bounds of the page.\n            // However, as it was added to `self.large_blob_insertions`,\n            // we have not yet written the hash to that granule.\n            unsafe {\n                blob_store_inserted_bytes += self.var_view.write_large_blob_hash_to_granule(blob_store, &value, vlr);\n            }\n        }\n        blob_store_inserted_bytes\n    }\n\n    /// Write an `val`, an [`AlgebraicValue`], typed at `ty`, to the buffer.\n    fn write_value(&mut self, ty: &AlgebraicTypeLayout, val: &AlgebraicValue) -> Result<(), Error> {\n        debug_assert_eq!(\n            self.curr_offset,\n            align_to(self.curr_offset, ty.align()),\n            \"curr_offset {} insufficiently aligned for type {:#?}\",\n            self.curr_offset,\n            val,\n        );\n\n        match (ty, val) {\n            // For sums, select the type based on the sum tag,\n            // write the variant data given the variant type,\n            // and finally write the tag.\n            (AlgebraicTypeLayout::Sum(ty), AlgebraicValue::Sum(val)) => self.write_sum(ty, val)?,\n            // For products, write every element in order.\n            (AlgebraicTypeLayout::Product(ty), AlgebraicValue::Product(val)) => self.write_product(ty.view(), val)?,\n\n            // For primitive types, write their contents by LE-encoding.\n            (&AlgebraicTypeLayout::Bool, AlgebraicValue::Bool(val)) => self.write_bool(*val),\n            // Integer types:\n            (&AlgebraicTypeLayout::I8, AlgebraicValue::I8(val)) => self.write_i8(*val),\n            (&AlgebraicTypeLayout::U8, AlgebraicValue::U8(val)) => self.write_u8(*val),\n            (&AlgebraicTypeLayout::I16, AlgebraicValue::I16(val)) => self.write_i16(*val),\n            (&AlgebraicTypeLayout::U16, AlgebraicValue::U16(val)) => self.write_u16(*val),\n            (&AlgebraicTypeLayout::I32, AlgebraicValue::I32(val)) => self.write_i32(*val),\n            (&AlgebraicTypeLayout::U32, AlgebraicValue::U32(val)) => self.write_u32(*val),\n            (&AlgebraicTypeLayout::I64, AlgebraicValue::I64(val)) => self.write_i64(*val),\n            (&AlgebraicTypeLayout::U64, AlgebraicValue::U64(val)) => self.write_u64(*val),\n            (&AlgebraicTypeLayout::I128, AlgebraicValue::I128(val)) => self.write_i128(val.0),\n            (&AlgebraicTypeLayout::U128, AlgebraicValue::U128(val)) => self.write_u128(val.0),\n            (&AlgebraicTypeLayout::I256, AlgebraicValue::I256(val)) => self.write_i256(**val),\n            (&AlgebraicTypeLayout::U256, AlgebraicValue::U256(val)) => self.write_u256(**val),\n            // Float types:\n            (&AlgebraicTypeLayout::F32, AlgebraicValue::F32(val)) => self.write_f32((*val).into()),\n            (&AlgebraicTypeLayout::F64, AlgebraicValue::F64(val)) => self.write_f64((*val).into()),\n\n            // For strings, we reserve space for a `VarLenRef`\n            // and push the bytes as a var-len object.\n            (&AlgebraicTypeLayout::String, AlgebraicValue::String(val)) => self.write_string(val)?,\n\n            // For array and maps, we reserve space for a `VarLenRef`\n            // and push the bytes, after BSATN encoding, as a var-len object.\n            (AlgebraicTypeLayout::VarLen(VarLenType::Array(_)), val @ AlgebraicValue::Array(_)) => {\n                self.write_av_bsatn(val)?\n            }\n\n            // If the type doesn't match the value, return an error.\n            (ty, val) => Err(Error::WrongType(ty.algebraic_type(), val.clone()))?,\n        }\n\n        Ok(())\n    }\n\n    /// Write a `val`, a [`SumValue`], typed at `ty`, to the buffer.\n    fn write_sum(&mut self, ty: &SumTypeLayout, val: &SumValue) -> Result<(), Error> {\n        // Extract sum value components and variant type, and offsets.\n        let SumValue { tag, ref value } = *val;\n        let variant_ty = &ty.variants[tag as usize];\n        let variant_offset = self.curr_offset + ty.offset_of_variant_data(tag);\n        let tag_offset = self.curr_offset + ty.offset_of_tag();\n\n        // Write the variant value at `variant_offset`.\n        self.curr_offset = variant_offset;\n        self.write_value(&variant_ty.ty, value)?;\n\n        // Write the variant value at `tag_offset`.\n        self.curr_offset = tag_offset;\n        self.write_u8(tag);\n\n        Ok(())\n    }\n\n    /// Write an `val`, a [`ProductValue`], typed at `ty`, to the buffer.\n    fn write_product(&mut self, ty: ProductTypeLayoutView<'_>, val: &ProductValue) -> Result<(), Error> {\n        // `Iterator::zip` silently drops elements if the two iterators have different lengths,\n        // so we need to check that our `ProductValue` has the same number of elements\n        // as our `ProductTypeLayout` to be sure it's typed correctly.\n        // Otherwise, if the value is too long, we'll discard its fields (whatever),\n        // or if it's too long, we'll leave some fields in the page \"uninit\"\n        // (actually valid-unconstrained) (very bad).\n        if ty.elements.len() != val.elements.len() {\n            return Err(Error::WrongType(\n                ty.algebraic_type(),\n                AlgebraicValue::Product(val.clone()),\n            ));\n        }\n\n        let base_offset = self.curr_offset;\n\n        for (elt_ty, elt) in ty.elements.iter().zip(val.elements.iter()) {\n            self.curr_offset = base_offset + elt_ty.offset as usize;\n            self.write_value(&elt_ty.ty, elt)?;\n        }\n        Ok(())\n    }\n\n    /// Write the string `str` to the var-len section\n    /// and a `VarLenRef` to the fixed buffer and advance the `curr_offset`.\n    fn write_string(&mut self, val: &str) -> Result<(), Error> {\n        let val = val.as_bytes();\n\n        // Write `val` to the page. The handle is `vlr`.\n        let (vlr, in_blob) = self.var_view.alloc_for_slice(val)?;\n        if in_blob {\n            self.defer_insert_large_blob(vlr, val.to_vec());\n        }\n\n        // Write `vlr` to the fixed part.\n        self.write_var_len_ref(vlr);\n        Ok(())\n    }\n\n    /// Write `val` BSATN-encoded to var-len section\n    /// and a `VarLenRef` to the fixed buffer and advance the `curr_offset`.\n    fn write_av_bsatn(&mut self, val: &AlgebraicValue) -> Result<(), Error> {\n        // Allocate space.\n        let len_in_bytes = bsatn_len(val);\n        let (vlr, in_blob) = self.var_view.alloc_for_len(len_in_bytes)?;\n\n        // Write `vlr` to the fixed part.\n        self.write_var_len_ref(vlr);\n\n        if in_blob {\n            // We won't be storing the large blob in the page,\n            // so no point in writing the blob directly to the page.\n            let mut bytes = Vec::with_capacity(len_in_bytes);\n            val.encode(&mut bytes);\n            self.defer_insert_large_blob(vlr, bytes);\n        } else {\n            // Write directly to the page.\n            // SAFETY: `vlr.first_granule` points to a granule\n            // even though the granule's data is not initialized as of yet.\n            // Note that the granule stores valid-unconstrained bytes (i.e. they are not uninit),\n            // but they may be leftovers from a previous allocation.\n            let iter = unsafe { self.var_view.granule_offset_iter(vlr.first_granule) };\n            let mut writer = GranuleBufWriter { buf: None, iter };\n            to_writer(&mut writer, val).unwrap();\n        }\n\n        /// A `BufWriter` that writes directly to a page.\n        struct GranuleBufWriter<'vv, 'page> {\n            /// The offset to the granule being written to\n            /// and how much has been written to it already.\n            buf: Option<(PageOffset, usize)>,\n            /// The iterator for the offsets to all the granule we'll write to.\n            iter: GranuleOffsetIter<'page, 'vv>,\n        }\n        impl BufWriter for GranuleBufWriter<'_, '_> {\n            fn put_slice(&mut self, mut slice: &[u8]) {\n                while !slice.is_empty() {\n                    let (offset, start) = match self.buf.take() {\n                        // Still have some to write to this granule.\n                        Some(buf @ (_, start)) if start < VarLenGranule::DATA_SIZE => buf,\n                        // First granule or the current one is full.\n                        _ => {\n                            let next = self.iter.next();\n                            debug_assert!(next.is_some());\n                            // SAFETY: The iterator length is exactly such that\n                            // `next.is_none() == slice.is_empty()`.\n                            let next = unsafe { next.unwrap_unchecked() };\n                            (next, 0)\n                        }\n                    };\n\n                    // Derive how much we can add to this granule\n                    // and only take that much from `slice`.\n                    let capacity_remains = VarLenGranule::DATA_SIZE - start;\n                    debug_assert!(capacity_remains > 0);\n                    let extend_len = capacity_remains.min(slice.len());\n                    let (extend_with, rest) = slice.split_at(extend_len);\n                    // The section of the granule data to write to.\n                    // SAFETY:\n                    // - `offset` came from `self.iter`, which only yields valid offsets.\n                    // - `start < VarLenGranule::DATA_SIZE` was ensured above.\n                    let write_to = unsafe { self.iter.get_mut_data(offset, start) };\n\n                    // Write to the granule.\n                    for (to, byte) in write_to.iter_mut().zip(extend_with) {\n                        *to = *byte;\n                    }\n\n                    slice = rest;\n                    self.buf = Some((offset, start + extend_len));\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Write a `VarLenRef` to the fixed buffer and advance the `curr_offset`.\n    fn write_var_len_ref(&mut self, val: VarLenRef) {\n        self.write_u16(val.length_in_bytes);\n        self.write_u16(val.first_granule.0);\n\n        // Keep track of how many var len objects we've added so far\n        // so that we can free them on failure.\n        self.last_allocated_var_len_index += 1;\n    }\n\n    /// Defers the insertion of a large blob to the blob store as well as writing the hash to the granule.\n    fn defer_insert_large_blob(&mut self, vlr: VarLenRef, obj_bytes: Vec<u8>) {\n        self.large_blob_insertions.push((vlr, obj_bytes));\n    }\n\n    /// Write `bytes: &[u8; N]` starting at the current offset\n    /// and advance the offset by `N`.\n    fn write_bytes<const N: usize>(&mut self, bytes: &[u8; N]) {\n        self.fixed_buf[range_move(0..N, self.curr_offset)].copy_from_slice(bytes);\n        self.curr_offset += N;\n    }\n\n    /// Write a `u8` to the fixed buffer and advance the `curr_offset`.\n    fn write_u8(&mut self, val: u8) {\n        self.write_bytes(&[val]);\n    }\n\n    /// Write an `i8` to the fixed buffer and advance the `curr_offset`.\n    fn write_i8(&mut self, val: i8) {\n        self.write_u8(val as u8);\n    }\n\n    /// Write a `bool` to the fixed buffer and advance the `curr_offset`.\n    fn write_bool(&mut self, val: bool) {\n        self.write_u8(val as u8);\n    }\n\n    /// Write a `u16` to the fixed buffer and advance the `curr_offset`.\n    fn write_u16(&mut self, val: u16) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write an `i16` to the fixed buffer and advance the `curr_offset`.\n    fn write_i16(&mut self, val: i16) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `u32` to the fixed buffer and advance the `curr_offset`.\n    fn write_u32(&mut self, val: u32) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write an `i32` to the fixed buffer and advance the `curr_offset`.\n    fn write_i32(&mut self, val: i32) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `u64` to the fixed buffer and advance the `curr_offset`.\n    fn write_u64(&mut self, val: u64) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write an `i64` to the fixed buffer and advance the `curr_offset`.\n    fn write_i64(&mut self, val: i64) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `u128` to the fixed buffer and advance the `curr_offset`.\n    fn write_u128(&mut self, val: u128) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write an `i128` to the fixed buffer and advance the `curr_offset`.\n    fn write_i128(&mut self, val: i128) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `u256` to the fixed buffer and advance the `curr_offset`.\n    fn write_u256(&mut self, val: u256) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write an `i256` to the fixed buffer and advance the `curr_offset`.\n    fn write_i256(&mut self, val: i256) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `f32` to the fixed buffer and advance the `curr_offset`.\n    fn write_f32(&mut self, val: f32) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n\n    /// Write a `f64` to the fixed buffer and advance the `curr_offset`.\n    fn write_f64(&mut self, val: f64) {\n        self.write_bytes(&val.to_le_bytes());\n    }\n}\n\n/// Counts the number of [`VarLenGranule`] allocations required to store `val` in a page.\nfn required_var_len_granules_for_row(val: &ProductValue) -> usize {\n    fn traverse_av(val: &AlgebraicValue, count: &mut usize) {\n        match val {\n            AlgebraicValue::Product(val) => traverse_product(val, count),\n            AlgebraicValue::Sum(val) => traverse_av(&val.value, count),\n            AlgebraicValue::Array(_) => add_for_bytestring(bsatn_len(val), count),\n            AlgebraicValue::String(val) => add_for_bytestring(val.len(), count),\n            _ => (),\n        }\n    }\n\n    fn traverse_product(val: &ProductValue, count: &mut usize) {\n        for elt in val {\n            traverse_av(elt, count);\n        }\n    }\n\n    fn add_for_bytestring(len_in_bytes: usize, count: &mut usize) {\n        *count += VarLenGranule::bytes_to_granules(len_in_bytes).0;\n    }\n\n    let mut required_granules: usize = 0;\n    traverse_product(val, &mut required_granules);\n    required_granules\n}\n\n/// Computes the size of `val` when BSATN encoding without actually encoding.\nfn bsatn_len(val: &AlgebraicValue) -> usize {\n    // We store arrays and maps BSATN-encoded,\n    // so we need to go through BSATN encoding to determine the size of the resulting byte blob,\n    // but we don't actually need that byte blob in this calculation,\n    // instead, we can just count them as a serialization format.\n    bsatn::to_len(val).unwrap()\n}\n\n#[cfg(test)]\npub mod test {\n    use super::*;\n    use crate::{\n        bflatn_from::serialize_row_from_page, blob_store::HashMapBlobStore, page::tests::hash_unmodified_save_get,\n        row_type_visitor::row_type_visitor,\n    };\n    use proptest::{prelude::*, prop_assert_eq, proptest};\n    use spacetimedb_sats::algebraic_value::ser::ValueSerializer;\n    use spacetimedb_sats::proptest::generate_typed_row;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n        #[test]\n        fn av_serde_round_trip_through_page((ty, val) in generate_typed_row()) {\n            let ty: RowTypeLayout = ty.into();\n            let mut page = Page::new(ty.size());\n            let visitor = row_type_visitor(&ty);\n            let blob_store = &mut HashMapBlobStore::default();\n\n            let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n            let (offset, _) = unsafe { write_row_to_page(&mut page, blob_store, &visitor, &ty, &val).unwrap() };\n\n            let hash_pre_ser = hash_unmodified_save_get(&mut page);\n            assert_ne!(hash_pre_ins, hash_pre_ser);\n\n            let read_val = unsafe { serialize_row_from_page(ValueSerializer, &page, blob_store, offset, &ty) }\n                .unwrap().into_product().unwrap();\n\n            prop_assert_eq!(val, read_val);\n            assert_eq!(hash_pre_ser, *page.unmodified_hash().unwrap());\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/blob_store.rs",
    "content": "//! Provides the interface [`BlobStore`] that tables use to talk to\n//! a blob store engine for large var-len objects.\n//!\n//! These blob objects are referred to by their [`BlobHash`],\n//! which is currently defined through BLAKE3 on the bytes of the blob object.\n//!\n//! Two simple implementations are provided,\n//! primarily for tests and benchmarking.\n//! - [`NullBlobStore`], a blob store that always panics.\n//!   Used when ensuring that the blob store is unreachable in a scenario.\n//! - [`HashMapBlobStore`], a blob store backed by a `HashMap` that refcounts blob objects.\n//!   It is not optimize and is mainly intended for testing purposes.\n\nuse blake3::hash;\nuse core::mem;\nuse spacetimedb_data_structures::map::{hash_map::Entry, HashMap};\nuse spacetimedb_lib::{de::Deserialize, ser::Serialize};\nuse spacetimedb_memory_usage::MemoryUsage;\n\n/// The content address of a blob-stored object.\n#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Copy, Hash, Debug, Serialize, Deserialize)]\npub struct BlobHash {\n    /// The hash of the blob-stored object.\n    ///\n    /// Uses BLAKE3 which fits in 32 bytes.\n    pub data: [u8; BlobHash::SIZE],\n}\n\nimpl MemoryUsage for BlobHash {}\n\nimpl BlobHash {\n    /// The size of the hash function's output in bytes.\n    pub const SIZE: usize = 32;\n\n    /// Returns the blob hash for `bytes`.\n    pub fn hash_from_bytes(bytes: &[u8]) -> Self {\n        let data = hash(bytes).into();\n        Self { data }\n    }\n}\n\nimpl TryFrom<&[u8]> for BlobHash {\n    type Error = ();\n\n    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {\n        let data: [u8; Self::SIZE] = data.try_into().map_err(drop)?;\n        Ok(Self { data })\n    }\n}\n\n/// An error that signifies that a [`BlobHash`] wasn't associated with a large blob object.\n#[derive(Debug)]\npub struct NoSuchBlobError;\n\n/// Iterator returned by [`BlobStore::iter_blobs`].\n///\n/// Each element is a tuple `(hash, uses, data)`,\n/// where `hash` is a blob's content-addressed [`BlobHash`],\n/// `uses` is the number of references to that blob,\n/// and `data` is the data itself.\npub type BlobsIter<'a> = Box<dyn Iterator<Item = (&'a BlobHash, usize, &'a [u8])> + 'a>;\n\n/// The interface that tables use to talk to the blob store engine for large var-len objects.\n///\n/// These blob objects are referred to by their [`BlobHash`],\n/// which is currently defined through BLAKE3 on the bytes of the blob object.\npub trait BlobStore: Sync {\n    /// Mark the `hash` as used.\n    ///\n    /// This is a more efficient way of doing:\n    /// ```ignore\n    /// let bytes = self.retrieve_blob(&hash);\n    /// let _ = self.insert_blob(&bytes);\n    /// ```\n    fn clone_blob(&mut self, hash: &BlobHash) -> Result<(), NoSuchBlobError>;\n\n    /// Insert `bytes` into the blob store.\n    ///\n    /// Returns the content address of `bytes` a `BlobHash`\n    /// which can be used in [`retrieve_blob`] to fetch it.\n    fn insert_blob(&mut self, bytes: &[u8]) -> BlobHash;\n\n    /// Insert `hash` referring to `bytes` and mark its refcount as `uses`.\n    ///\n    /// Used when restoring from a snapshot.\n    fn insert_with_uses(&mut self, hash: &BlobHash, uses: usize, bytes: Box<[u8]>);\n\n    /// Returns the bytes stored at the content address `hash`.\n    fn retrieve_blob(&self, hash: &BlobHash) -> Result<&[u8], NoSuchBlobError>;\n\n    /// Marks the `hash` as unused.\n    ///\n    /// Depending on the strategy employed by the blob store,\n    /// this might not actually free the data,\n    /// but rather just decrement a reference count.\n    fn free_blob(&mut self, hash: &BlobHash) -> Result<(), NoSuchBlobError>;\n\n    /// Iterate over all blobs present in the blob store.\n    ///\n    /// Each element is a tuple `(hash, uses, data)`,\n    /// where `hash` is a blob's content-addressed [`BlobHash`],\n    /// `uses` is the number of references to that blob,\n    /// and `data` is the data itself.\n    ///\n    /// Used when capturing a snapshot.\n    fn iter_blobs(&self) -> BlobsIter<'_>;\n\n    /// Returns the amount of memory in bytes used by blobs in this `BlobStore`.\n    ///\n    /// Duplicate blobs are counted a number of times equal to their refcount.\n    /// This is in order to preserve the property that inserting a large blob\n    /// causes this quantity to increase by that blob's size,\n    /// and deleting a large blob causes it to decrease the same amount.\n    fn bytes_used_by_blobs(&self) -> u64 {\n        self.iter_blobs()\n            .map(|(_, uses, data)| data.len() as u64 * uses as u64)\n            .sum()\n    }\n\n    /// Returns the number of blobs, or more precisely, blob-usages, recorded in this `BlobStore`.\n    ///\n    /// Duplicate blobs are counted a number of times equal to their refcount.\n    /// This is in order to preserve the property that inserting a large blob\n    /// causes this quantity to increase by 1, and deleting a large blob causes it to decrease by 1.\n    fn num_blobs(&self) -> u64 {\n        self.iter_blobs().map(|(_, uses, _)| uses as u64).sum()\n    }\n}\n\n/// A blob store that panics on all operations.\n/// Used for tests when you want to ensure that the blob store isn't used.\n#[derive(Default)]\npub struct NullBlobStore;\n\nimpl BlobStore for NullBlobStore {\n    fn clone_blob(&mut self, _hash: &BlobHash) -> Result<(), NoSuchBlobError> {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n\n    fn insert_blob(&mut self, _bytes: &[u8]) -> BlobHash {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n\n    fn insert_with_uses(&mut self, _hash: &BlobHash, _uses: usize, _bytes: Box<[u8]>) {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n\n    fn retrieve_blob(&self, _hash: &BlobHash) -> Result<&[u8], NoSuchBlobError> {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n\n    fn free_blob(&mut self, _hash: &BlobHash) -> Result<(), NoSuchBlobError> {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n\n    fn iter_blobs(&self) -> BlobsIter<'_> {\n        unimplemented!(\"NullBlobStore doesn't do anything\")\n    }\n}\n\n/// A blob store that is backed by a hash map with a reference counted value.\n/// Used for tests when you need an actual blob store.\n#[derive(Default, PartialEq, Eq, Debug)]\npub struct HashMapBlobStore {\n    /// For testing, we use a hash map with a reference count\n    /// to handle freeing and cloning correctly.\n    map: HashMap<BlobHash, BlobObject>,\n}\n\nimpl MemoryUsage for HashMapBlobStore {\n    fn heap_usage(&self) -> usize {\n        let Self { map } = self;\n        map.heap_usage()\n    }\n}\n\n/// A blob object including a reference count and the data.\n#[derive(PartialEq, Eq, Debug)]\nstruct BlobObject {\n    /// Reference count of the blob.\n    uses: usize,\n    /// The blob data.\n    blob: Box<[u8]>,\n}\n\nimpl MemoryUsage for BlobObject {\n    fn heap_usage(&self) -> usize {\n        let Self { uses, blob } = self;\n        uses.heap_usage() + blob.heap_usage()\n    }\n}\n\nimpl BlobStore for HashMapBlobStore {\n    fn clone_blob(&mut self, hash: &BlobHash) -> Result<(), NoSuchBlobError> {\n        self.map.get_mut(hash).ok_or(NoSuchBlobError)?.uses += 1;\n        Ok(())\n    }\n\n    fn insert_blob(&mut self, bytes: &[u8]) -> BlobHash {\n        let hash = BlobHash::hash_from_bytes(bytes);\n        self.map\n            .entry(hash)\n            .and_modify(|v| v.uses += 1)\n            .or_insert_with(|| BlobObject {\n                blob: bytes.into(),\n                uses: 1,\n            });\n        hash\n    }\n\n    fn insert_with_uses(&mut self, hash: &BlobHash, uses: usize, bytes: Box<[u8]>) {\n        debug_assert_eq!(hash, &BlobHash::hash_from_bytes(&bytes));\n        self.map\n            .entry(*hash)\n            .and_modify(|v| v.uses += uses)\n            .or_insert_with(|| BlobObject { blob: bytes, uses });\n    }\n\n    fn retrieve_blob(&self, hash: &BlobHash) -> Result<&[u8], NoSuchBlobError> {\n        self.map.get(hash).map(|obj| &*obj.blob).ok_or(NoSuchBlobError)\n    }\n\n    fn free_blob(&mut self, hash: &BlobHash) -> Result<(), NoSuchBlobError> {\n        match self.map.entry(*hash) {\n            Entry::Vacant(_) => return Err(NoSuchBlobError),\n            Entry::Occupied(entry) if entry.get().uses == 1 => drop(entry.remove()),\n            Entry::Occupied(mut entry) => entry.get_mut().uses -= 1,\n        }\n        Ok(())\n    }\n\n    fn iter_blobs(&self) -> BlobsIter<'_> {\n        Box::new(self.map.iter().map(|(hash, obj)| (hash, obj.uses, &obj.blob[..])))\n    }\n}\n\nimpl HashMapBlobStore {\n    /// Merge `src_bs` into `self`.\n    pub fn merge_from(&mut self, src_bs: Self) {\n        for (hash, mut obj) in src_bs.map {\n            let uses = mem::take(&mut obj.uses);\n            self.map.entry(hash).or_insert(obj).uses += uses;\n        }\n    }\n}\n\n#[cfg(test)]\nimpl HashMapBlobStore {\n    /// Returns a map relating blob hashes to the usage count in this blob store.\n    pub fn usage_counter(&self) -> HashMap<BlobHash, usize> {\n        self.iter_blobs().map(|(hash, uses, _)| (*hash, uses)).collect()\n    }\n}\n"
  },
  {
    "path": "crates/table/src/eq.rs",
    "content": "//! Provides the function [`eq_row_in_page(page_a, page_b, offset_a, offset_b, ty)`]\n//! which, for `value_a/b = page_a/b.get_row_data(offset_a/b, fixed_row_size)` typed at `ty`,\n//! compares `value_a` and `value_b` for equality.\n\nuse super::{\n    bflatn_from::read_tag,\n    indexes::{Bytes, PageOffset},\n    page::Page,\n    row_hash::read_from_bytes,\n    static_layout::StaticLayout,\n    util::range_move,\n    var_len::VarLenRef,\n};\nuse spacetimedb_sats::layout::{align_to, AlgebraicTypeLayout, HasLayout, ProductTypeLayoutView, RowTypeLayout};\n\n/// Equates row `a` in `page_a` with its fixed part starting at `fixed_offset_a`\n/// to row `b` in `page_b` with its fixed part starting at `fixed_offset_b`.\n/// Both rows last for `ty.size()` bytes\n/// and are assumed to be typed at `ty` and must be valid for `ty`.\n///\n/// Returns whether row `a` is equal to row `b` including their var-len objects.\n///\n/// # Safety\n///\n/// 1. `fixed_offset_a/b` are valid offsets for rows typed at `ty` in `page_a/b`.\n/// 2. for any `vlr_a/b: VarLenRef` in the fixed parts of row `a` and `b`,\n///    `vlr_a/b.first_offset` must either be `NULL` or point to a valid granule in `page_a/b`.\n/// 3. the `static_bsatn_layout` must be derived from `ty`.\npub unsafe fn eq_row_in_page(\n    page_a: &Page,\n    page_b: &Page,\n    fixed_offset_a: PageOffset,\n    fixed_offset_b: PageOffset,\n    ty: &RowTypeLayout,\n    static_layout: Option<&StaticLayout>,\n) -> bool {\n    // Contexts for rows `a` and `b`.\n    let a = BytesPage::new(page_a, fixed_offset_a, ty);\n    let b = BytesPage::new(page_b, fixed_offset_b, ty);\n\n    // If there are only fixed parts in the layout,\n    // there are no pointers to anywhere,\n    // So it is sound to simply check for byte-wise equality while ignoring padding.\n    match static_layout {\n        None => {\n            // Context for the whole comparison.\n            let mut ctx = EqCtx { a, b, curr_offset: 0 };\n\n            // Test for equality!\n            // SAFETY:\n            // 1. Per requirement 1., rows `a/b` are valid at type `ty` and properly aligned for `ty`.\n            //    Their fixed parts are defined as:\n            //    `value_a/b = ctx.a/b.bytes[range_move(0..fixed_row_size, fixed_offset_a/b)]`\n            //    as needed.\n            // 2. for any `vlr_a/b: VarLenRef` stored in `value_a/b`,\n            //   `vlr_a/b.first_offset` must either be `NULL` or point to a valid granule in `page_a/b`.\n            unsafe { eq_product(&mut ctx, ty.product()) }\n        }\n        Some(static_bsatn_layout) => {\n            // SAFETY: caller promised that `a/b` are valid BFLATN representations matching `ty`\n            // and as `static_bsatn_layout` was promised to be derived from `ty`,\n            // so too are `a/b` valid for `static_bsatn_layout`.\n            unsafe { static_bsatn_layout.eq(a.bytes, b.bytes) }\n        }\n    }\n}\n\n/// A view into the fixed part of a row combined with the page it belongs to.\n#[derive(Clone, Copy)]\npub(crate) struct BytesPage<'page> {\n    /// The `Bytes` of the fixed part of a row in `page`.\n    pub(crate) bytes: &'page Bytes,\n    /// The `Page` which has the fixed part `bytes` and associated var-len objects.\n    pub(crate) page: &'page Page,\n}\n\nimpl<'page> BytesPage<'page> {\n    /// Returns a view into the bytes of the row at `offset` in `page` typed at `ty`.\n    pub(crate) fn new(page: &'page Page, offset: PageOffset, ty: &RowTypeLayout) -> Self {\n        let bytes = page.get_row_data(offset, ty.size());\n        Self { page, bytes }\n    }\n}\n\n/// Comparison context used in the functions below.\n#[derive(Clone, Copy)]\nstruct EqCtx<'page_a, 'page_b> {\n    /// The view into the fixed part of row `a` in page `A` to compare against `b`.\n    a: BytesPage<'page_a>,\n    /// The view into the fixed part of row `b` in page `B` to compare against `a`.\n    b: BytesPage<'page_b>,\n    /// The current offset at which some sub-object of both `a` and `b` exist.\n    curr_offset: usize,\n}\n\n/// For every product field in `value_a/b = &ctx.a/b.bytes[range_move(0..ty.size(), *ctx.curr_offset)]`,\n/// which is typed at `ty`,\n/// equates `value_a/value_b`, including any var-len object,\n/// and advance the `ctx.curr_offset`.\n///\n/// SAFETY:\n/// 1. `value_a/b` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr_a/b: VarLenRef` stored in `value_a/b`,\n///    `vlr_a/b.first_offset` must either be `NULL` or point to a valid granule in `page_a/b`.\nunsafe fn eq_product(ctx: &mut EqCtx<'_, '_>, ty: ProductTypeLayoutView<'_>) -> bool {\n    let base_offset = ctx.curr_offset;\n    ty.elements.iter().all(|elem_ty| {\n        ctx.curr_offset = base_offset + elem_ty.offset as usize;\n\n        // SAFETY: By 1., `value_a/b` are valid at `ty`,\n        // so it follows that valid and properly aligned sub-`value_a/b`s\n        // are valid `elem_ty.ty`s.\n        // By 2., and the above, it follows that sub-`value_a/b`s won't have dangling `VarLenRef`s.\n        unsafe { eq_value(ctx, &elem_ty.ty) }\n    })\n}\n\n/// For `value_a/b = &ctx.a/b.bytes[range_move(0..ty.size(), *ctx.curr_offset)]` typed at `ty`,\n/// equates `value_a == value_b`, including any var-len objects,\n/// and advances the `ctx.curr_offset`.\n///\n/// SAFETY:\n/// 1. `value_a/b` must both be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr_a/b: VarLenRef` stored in `value_a/b`,\n///    `vlr_a/b.first_offset` must either be `NULL` or point to a valid granule in `page_a/b`.\nunsafe fn eq_value(ctx: &mut EqCtx<'_, '_>, ty: &AlgebraicTypeLayout) -> bool {\n    debug_assert_eq!(\n        ctx.curr_offset,\n        align_to(ctx.curr_offset, ty.align()),\n        \"curr_offset {} insufficiently aligned for type {:?}\",\n        ctx.curr_offset,\n        ty\n    );\n\n    match ty {\n        AlgebraicTypeLayout::Sum(ty) => {\n            // Read the tags of the sum values.\n            let (tag_a, data_ty) = read_tag(ctx.a.bytes, ty, ctx.curr_offset);\n            let (tag_b, _) = read_tag(ctx.b.bytes, ty, ctx.curr_offset);\n\n            // The tags must match!\n            if tag_a != tag_b {\n                return false;\n            }\n\n            // Equate the variant data values.\n            let curr_offset = ctx.curr_offset + ty.offset_of_variant_data(tag_a);\n            ctx.curr_offset += ty.size();\n            let mut ctx = EqCtx { curr_offset, ..*ctx };\n            // SAFETY: `value_a/b` are valid at `ty` so given `tag`,\n            // we know `data_value_a/b = &ctx.a/b.bytes[range_move(0..data_ty.size(), data_offset))`\n            // are valid at `data_ty`.\n            // By 2., and the above, we also know that `data_value_a/b` won't have dangling `VarLenRef`s.\n            unsafe { eq_value(&mut ctx, data_ty) }\n        }\n        AlgebraicTypeLayout::Product(ty) => {\n            // SAFETY: `value_a/b` are valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { eq_product(ctx, ty.view()) }\n        }\n\n        // The primitive types:\n        &AlgebraicTypeLayout::Bool\n        | &AlgebraicTypeLayout::I8\n        | &AlgebraicTypeLayout::U8\n        | &AlgebraicTypeLayout::I16\n        | &AlgebraicTypeLayout::U16\n        | &AlgebraicTypeLayout::I32\n        | &AlgebraicTypeLayout::U32\n        | &AlgebraicTypeLayout::I64\n        | &AlgebraicTypeLayout::U64\n        | &AlgebraicTypeLayout::I128\n        | &AlgebraicTypeLayout::U128\n        | &AlgebraicTypeLayout::I256\n        | &AlgebraicTypeLayout::U256\n        | &AlgebraicTypeLayout::F32\n        | &AlgebraicTypeLayout::F64 => eq_byte_array(ctx, ty.size()),\n\n        // The var-len cases.\n        &AlgebraicTypeLayout::String | AlgebraicTypeLayout::VarLen(_) => {\n            // SAFETY: `value_a/b` were valid at and aligned for `ty`.\n            // These `ty` each store a `vlr_a/vlr_b: VarLenRef` as their value,\n            // so the range is valid and properly aligned for `VarLenRef`.\n            // Moreover, `vlr_a/vlr_b.first_granule` were promised by the caller\n            // to either be `NULL` or point to a valid granule in `page_a/page_b`.\n            unsafe { eq_vlo(ctx) }\n        }\n    }\n}\n\n/// Equates the bytes of two var-len objects\n/// referred to at by the var-len references in `ctx.a/b.bytes` at `ctx.curr_offset`\n/// which is then advanced.\n///\n/// The function does not care about large-blob-ness.\n/// Rather, the blob hash is implicitly tested for equality.\n///\n/// SAFETY: `data_a/b = ctx.a/b.bytes[range_move(0..size_of::<VarLenRef>(), *ctx.curr_offset)]`\n/// must be valid `vlr_a/b = VarLenRef` and `&data_a/b` must be properly aligned for a `VarLenRef`.\n/// The `vlr_a/b.first_granule`s must be `NULL` or must point to a valid granule in `ctx.a/b.page`.\nunsafe fn eq_vlo(ctx: &mut EqCtx<'_, '_>) -> bool {\n    // SAFETY: We have a valid `VarLenRef` at `&data_a`.\n    let vlr_a = unsafe { read_from_bytes::<VarLenRef>(ctx.a.bytes, &mut { ctx.curr_offset }) };\n    // SAFETY: We have a valid `VarLenRef` at `&data_b`.\n    let vlr_b = unsafe { read_from_bytes::<VarLenRef>(ctx.b.bytes, &mut ctx.curr_offset) };\n\n    // Lengths have to match or they cannot be equal.\n    // This also implicitly checks that both sides are blobs or neither are.\n    if vlr_a.length_in_bytes != vlr_b.length_in_bytes {\n        return false;\n    }\n\n    // SAFETY: ^-- got valid `VarLenRef` where `vlr_a.first_granule` was `NULL`\n    // or a pointer to a valid starting granule, as required.\n    let var_iter_a = unsafe { ctx.a.page.iter_vlo_data(vlr_a.first_granule) };\n    // SAFETY: ^-- got valid `VarLenRef` where `vlr_b.first_granule` was `NULL`\n    // or a pointer to a valid starting granule, as required.\n    let var_iter_b = unsafe { ctx.b.page.iter_vlo_data(vlr_b.first_granule) };\n    var_iter_a.zip(var_iter_b).all(|(da, db)| da == db)\n}\n\n/// Equates the byte arrays `data_a/data_b = ctx.a/b.bytes[range_move(0..len, ctx.curr_offset)]`\n/// and advances the offset.\nfn eq_byte_array(ctx: &mut EqCtx<'_, '_>, len: usize) -> bool {\n    let data_a = &ctx.a.bytes[range_move(0..len, ctx.curr_offset)];\n    let data_b = &ctx.b.bytes[range_move(0..len, ctx.curr_offset)];\n    ctx.curr_offset += len;\n    data_a == data_b\n}\n\n#[cfg(test)]\nmod test {\n    use crate::{blob_store::NullBlobStore, page_pool::PagePool};\n    use spacetimedb_sats::{product, AlgebraicType, AlgebraicValue, ProductType};\n\n    #[test]\n    fn sum_with_variant_with_distinct_layout() {\n        // This is a type where the layout of the sum variants differ,\n        // with the latter having some padding bytes due to alignment.\n        let ty = ProductType::from([AlgebraicType::sum([\n            AlgebraicType::U64,                                              // xxxxxxxx\n            AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U32]), // xpppxxxx\n        ])]);\n\n        let pool = PagePool::new_for_test();\n        let bs = &mut NullBlobStore;\n        let mut table_a = crate::table::test::table(ty.clone());\n        let mut table_b = crate::table::test::table(ty);\n\n        // Insert u64::MAX with tag 0 and then delete it.\n        let a0 = product![AlgebraicValue::sum(0, u64::MAX.into())];\n        let (_, a0_rr) = table_a.insert(&pool, bs, &a0).unwrap();\n        let a0_ptr = a0_rr.pointer();\n        assert!(table_a.delete(bs, a0_ptr, |_| {}).is_some());\n\n        // Insert u64::ALTERNATING_BIT_PATTERN with tag 0 and then delete it.\n        let b0 = 0b01010101_01010101_01010101_01010101_01010101_01010101_01010101_01010101u64;\n        let b0 = product![AlgebraicValue::sum(0, b0.into())];\n        let (_, b0_rr) = table_b.insert(&pool, bs, &b0).unwrap();\n        let b0_ptr = b0_rr.pointer();\n        assert!(table_b.delete(bs, b0_ptr, |_| {}).is_some());\n\n        // Insert two identical rows `a1` and `b2` into the tables.\n        // They should occupy the spaces of the previous rows.\n        let v1 = product![AlgebraicValue::sum(1, product![0u8, 0u32].into())];\n        let (_, a1_rr) = table_a.insert(&pool, bs, &v1).unwrap();\n        let bs = &mut NullBlobStore;\n        let (_, b1_rr) = table_b.insert(&pool, bs, &v1).unwrap();\n        assert_eq!(a0_ptr, a1_rr.pointer());\n        assert_eq!(b0_ptr, b1_rr.pointer());\n\n        // Check that the rows are considered equal\n        // and that padding does not mess this up.\n        assert_eq!(a1_rr, b1_rr);\n    }\n}\n"
  },
  {
    "path": "crates/table/src/eq_to_pv.rs",
    "content": "//! Provides the function [`eq_row_in_page_to_pv(page, offset, pv, ty)`]\n//! which, for `value = page/b.get_row_data(offset, fixed_row_size)` typed at `ty`,\n//! and `pv`, a product value typed at `ty`,\n//! compares `value` and `pv` for equality.\n\nuse crate::{\n    bflatn_from::{read_tag, vlr_blob_bytes},\n    blob_store::BlobStore,\n    eq::BytesPage,\n    indexes::PageOffset,\n    page::Page,\n    row_hash::{read_from_bytes, run_vlo_bytes},\n    var_len::{VarLenGranule, VarLenRef},\n};\nuse core::str;\nuse spacetimedb_sats::bsatn::{eq::eq_bsatn, Deserializer};\nuse spacetimedb_sats::layout::{align_to, AlgebraicTypeLayout, HasLayout as _, ProductTypeLayoutView, RowTypeLayout};\nuse spacetimedb_sats::{AlgebraicValue, ProductValue};\n\n/// Equates row `lhs` in `page` with its fixed part starting at `fixed_offset` to `rhs`.\n/// It is required for safety that `lhs` be typed at `ty`.\n/// That is, row `lhs` has length `ty.size()` bytes,\n/// is assumed to be typed at `ty` and must be valid for `ty`.\n/// `rhs` should also be typed at `ty`, but this is only a logical requirement,\n/// not a safety requirement.\n///\n/// Returns whether row `lhs` is equal to `rhs`.\n///\n/// # Safety\n///\n/// 1. `fixed_offset` is a valid offset for row `lhs` typed at `ty` in `page`.\n/// 2. for any `vlr: VarLenRef` in the fixed parts of row `lhs`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\npub unsafe fn eq_row_in_page_to_pv(\n    blob_store: &dyn BlobStore,\n    page: &Page,\n    fixed_offset: PageOffset,\n    rhs: &ProductValue,\n    ty: &RowTypeLayout,\n) -> bool {\n    // Context for the whole comparison.\n    let mut ctx = EqCtx {\n        lhs: BytesPage::new(page, fixed_offset, ty),\n        blob_store,\n        curr_offset: 0,\n    };\n    // Test for equality!\n    // SAFETY:\n    // 1. Per requirement 1., row `lhs` is valid at type `ty` and properly aligned for `ty`.\n    //    Their fixed parts are defined as:\n    //    `lhs = ctx.a/b.bytes[range_move(0..ty.size(), fixed_offset)]`\n    //    as needed.\n    // 2. for any `vlr: VarLenRef` stored in `lhs`,\n    //   `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\n    unsafe { eq_product(&mut ctx, ty.product(), rhs) }\n}\n\n/// Comparison context used in the functions below.\n#[derive(Clone, Copy)]\nstruct EqCtx<'page> {\n    /// The view into the fixed part of row `lhs` in its page.\n    lhs: BytesPage<'page>,\n    /// The blob store that `lhs.page` uses for its large blob VLOs.\n    blob_store: &'page dyn BlobStore,\n    /// The current offset at which some sub-object of `lhs` exists.\n    curr_offset: usize,\n}\n\n/// For every product field in `lhs = &ctx.lhs.bytes[range_move(0..ty.size(), *ctx.curr_offset)]`,\n/// which is typed at `ty`,\n/// equates `lhs`, including any var-len object, to the corresponding field in `rhs`\n/// and advances the `ctx.curr_offset`.\n///\n/// SAFETY:\n/// 1. `lhs` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `lhs`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `ctx.lhs.page`.\nunsafe fn eq_product(ctx: &mut EqCtx<'_>, ty: ProductTypeLayoutView<'_>, rhs: &ProductValue) -> bool {\n    let base_offset = ctx.curr_offset;\n    ty.elements.len() == rhs.elements.len()\n        && ty.elements.iter().zip(&*rhs.elements).all(|(elem_ty, rhs)| {\n            ctx.curr_offset = base_offset + elem_ty.offset as usize;\n\n            // SAFETY: By 1., `lhs` is valid at `ty`,\n            // so it follows that valid and properly aligned sub-`lhs`s\n            // are valid `elem_ty.ty`s.\n            // By 2., and the above, it follows that sub-`lhs`s won't have dangling `VarLenRef`s.\n            unsafe { eq_value(ctx, &elem_ty.ty, rhs) }\n        })\n}\n\n/// For `lhs = &ctx.lhs.bytes[range_move(0..ty.size(), *ctx.curr_offset)]` typed at `ty`,\n/// equates `lhs == rhs`, including any var-len objects,\n/// and advances the `ctx.curr_offset`.\n///\n/// SAFETY:\n/// 1. `lhs` must both be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `lhs`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `ctx.lhs.page`.\nunsafe fn eq_value(ctx: &mut EqCtx<'_>, ty: &AlgebraicTypeLayout, rhs: &AlgebraicValue) -> bool {\n    debug_assert_eq!(\n        ctx.curr_offset,\n        align_to(ctx.curr_offset, ty.align()),\n        \"curr_offset {} insufficiently aligned for type {:?}\",\n        ctx.curr_offset,\n        ty\n    );\n\n    match (ty, rhs) {\n        (AlgebraicTypeLayout::Sum(ty), AlgebraicValue::Sum(rhs)) => {\n            // Read the tag of the sum value of `lhs`.\n            let (tag_lhs, data_ty) = read_tag(ctx.lhs.bytes, ty, ctx.curr_offset);\n\n            // The tags must match!\n            if tag_lhs != rhs.tag {\n                return false;\n            }\n\n            // Equate the variant data values.\n            let curr_offset = ctx.curr_offset + ty.offset_of_variant_data(tag_lhs);\n            ctx.curr_offset += ty.size();\n            let mut ctx = EqCtx { curr_offset, ..*ctx };\n            // SAFETY: `lhs` are valid at `ty` so given `tag_lhs`,\n            // we know `data_lhs = &ctx.lhs.bytes[range_move(0..data_ty.size(), curr_offset))`\n            // are valid at `data_ty`.\n            // By 2., and the above, we also know that `data_lhs` won't have dangling `VarLenRef`s.\n            unsafe { eq_value(&mut ctx, data_ty, &rhs.value) }\n        }\n        (AlgebraicTypeLayout::Product(ty), AlgebraicValue::Product(rhs)) => {\n            // SAFETY: `lhs` is valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { eq_product(ctx, ty.view(), rhs) }\n        }\n\n        // The primitive types:\n        // SAFETY(for all of the below): `lhs` is valid at `ty = T`.\n        (&AlgebraicTypeLayout::Bool, AlgebraicValue::Bool(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I8, AlgebraicValue::I8(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::U8, AlgebraicValue::U8(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I16, AlgebraicValue::I16(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::U16, AlgebraicValue::U16(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I32, AlgebraicValue::I32(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::U32, AlgebraicValue::U32(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I64, AlgebraicValue::I64(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::U64, AlgebraicValue::U64(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I128, AlgebraicValue::I128(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::U128, AlgebraicValue::U128(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::I256, AlgebraicValue::I256(rhs)) => unsafe { eq_at(ctx, &**rhs) },\n        (&AlgebraicTypeLayout::U256, AlgebraicValue::U256(rhs)) => unsafe { eq_at(ctx, &**rhs) },\n        (&AlgebraicTypeLayout::F32, AlgebraicValue::F32(rhs)) => unsafe { eq_at(ctx, rhs) },\n        (&AlgebraicTypeLayout::F64, AlgebraicValue::F64(rhs)) => unsafe { eq_at(ctx, rhs) },\n\n        // The var-len cases.\n        (&AlgebraicTypeLayout::String, AlgebraicValue::String(rhs)) => {\n            // SAFETY: `lhs` was valid at and aligned for `ty` (= `String`, as required).\n            // These `ty` store a `vlr: VarLenRef` as their value,\n            // so the range is valid and properly aligned for `VarLenRef`.\n            // Moreover, `vlr.first_granule` was promised by the caller\n            // to either be `NULL` or point to a valid granule in `ctx.lhs.page`.\n            unsafe { eq_str(ctx, rhs) }\n        }\n        (AlgebraicTypeLayout::VarLen(_), AlgebraicValue::Array(_)) => {\n            // SAFETY: `lhs` was valid at and aligned for `ty`.\n            // This kind of `ty` stores a `vlr: VarLenRef` as its value,\n            // so the range is valid and properly aligned for `VarLenRef`.\n            // Moreover, `vlr.first_granule` were promised by the caller\n            // to either be `NULL` or point to a valid granule in `ctx.lhs.page`.\n            unsafe {\n                run_vlo_bytes(\n                    ctx.lhs.page,\n                    ctx.lhs.bytes,\n                    ctx.blob_store,\n                    &mut ctx.curr_offset,\n                    |mut bsatn| {\n                        let lhs = Deserializer::new(&mut bsatn);\n                        eq_bsatn(rhs, lhs)\n                    },\n                )\n            }\n        }\n        _ => false,\n    }\n}\n\n/// Equates `ctx.lhs`, known to store a string at `ctx.current_offset`, to `rhs`,\n/// and advances `ctx.current_offset`.\n///\n/// SAFETY: `lhs = ctx.lhs.bytes[range_move(0..size_of::<VarLenRef>(), *curr_offset)]`\n/// must be a valid `vlr = VarLenRef` and `&data` must be properly aligned for a `VarLenRef`.\n/// The `vlr.first_granule` must be `NULL` or must point to a valid granule in `page`.\n/// Moreover, `lhs` must be typed at `AlgebraicTypeLayout::String`.\nunsafe fn eq_str(ctx: &mut EqCtx<'_>, rhs: &str) -> bool {\n    // SAFETY: `value` was valid at and aligned for `ty = String`.\n    // These `ty` store a `vlr: VarLenRef` as their fixed value.\n    // The range thus is valid and properly aligned for `VarLenRef`.\n    let vlr = unsafe { read_from_bytes::<VarLenRef>(ctx.lhs.bytes, &mut ctx.curr_offset) };\n\n    if vlr.is_large_blob() {\n        // SAFETY: As `vlr` is a blob, `vlr.first_granule` always points to a valid granule.\n        let bytes = unsafe { vlr_blob_bytes(ctx.lhs.page, ctx.blob_store, vlr) };\n        // SAFETY: For `ty = String`, the blob will always be valid UTF-8.\n        rhs == unsafe { str::from_utf8_unchecked(bytes) }\n    } else {\n        // SAFETY: `vlr.first_granule` is either NULL or points to a valid granule.\n        let lhs_chunks = unsafe { ctx.lhs.page.iter_vlo_data(vlr.first_granule) };\n        let total_len = vlr.length_in_bytes as usize;\n\n        // Don't bother checking the data if the lengths don't match.\n        if total_len != rhs.len() {\n            return false;\n        }\n\n        // Check that the chunks of `lhs` is equal to the granule-sized chunks of `rhs`.\n        lhs_chunks\n            .zip(rhs.as_bytes().chunks(VarLenGranule::DATA_SIZE))\n            .all(|(l, r)| l == r)\n    }\n}\n\n/// Equates `lhs`, assumed to be typed at `T`, to `rhs`.\n///\n/// SAFETY: Let `lhs = &ctx.lhs.bytes[range_move(0..size_of::<T>(), ctx.curr_offset)]`.\n/// Then `lhs` must point to a valid `T` and must be properly aligned for `T`.\nunsafe fn eq_at<T: Copy + Eq>(ctx: &mut EqCtx<'_>, rhs: &T) -> bool {\n    &unsafe { read_from_bytes::<T>(ctx.lhs.bytes, &mut ctx.curr_offset) } == rhs\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{blob_store::HashMapBlobStore, page_pool::PagePool};\n    use proptest::prelude::*;\n    use spacetimedb_sats::proptest::generate_typed_row;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n        #[test]\n        fn pv_row_ref_eq((ty, val) in generate_typed_row()) {\n            // Turn `val` into a `RowRef`.\n            let mut table = crate::table::test::table(ty);\n            let blob_store = &mut HashMapBlobStore::default();\n            let (_, row) = table.insert(&PagePool::new_for_test(), blob_store, &val).unwrap();\n\n            // Check eq algo.\n            prop_assert_eq!(row, val);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/fixed_bit_set.rs",
    "content": "use core::{\n    cmp, fmt,\n    ops::{BitAnd, BitAndAssign, BitOr, Not, Shl},\n    slice::Iter,\n};\npub use internal_unsafe::FixedBitSet;\nuse internal_unsafe::Len;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n/// A type used to represent blocks in a bit set.\n/// A smaller type, compared to usize,\n/// means taking less advantage of native operations.\n/// A larger type means we might over-allocate more.\npub trait BitBlock:\n    Copy\n    + Eq\n    + Not<Output = Self>\n    + BitAnd<Self, Output = Self>\n    + BitAndAssign\n    + BitOr<Self, Output = Self>\n    + Shl<usize, Output = Self>\n{\n    /// The number of bits that [`Self`] can represent.\n    const BITS: u32;\n\n    /// The first bit is set.\n    const ONE: Self;\n\n    /// No bits are set.\n    const ZERO: Self;\n\n    fn wrapping_sub(self, rhs: Self) -> Self;\n    fn trailing_zeros(self) -> u32;\n}\n\ntype DefaultBitBlock = u64;\n\nimpl BitBlock for DefaultBitBlock {\n    const BITS: u32 = Self::BITS;\n    const ONE: Self = 1;\n    const ZERO: Self = 0;\n\n    #[inline]\n    fn wrapping_sub(self, rhs: Self) -> Self {\n        self.wrapping_sub(rhs)\n    }\n\n    #[inline]\n    fn trailing_zeros(self) -> u32 {\n        self.trailing_zeros()\n    }\n}\n\n/// The internals of `FixedBitSet`.\n/// Separated from the higher level APIs to contain the safety boundary.\nmod internal_unsafe {\n    use spacetimedb_lib::{de::Deserialize, ser::Serialize};\n    use spacetimedb_sats::{impl_deserialize, impl_serialize};\n\n    use super::{BitBlock, DefaultBitBlock};\n    use crate::{static_assert_align, static_assert_size};\n    use core::{\n        mem,\n        ptr::NonNull,\n        slice::{from_raw_parts, from_raw_parts_mut},\n    };\n\n    /// The type used to represent the number of bits the set can hold.\n    ///\n    /// Currently `u16` to keep `mem::size_of::<FixedBitSet>()` small.\n    pub(super) type Len = u16;\n\n    /// A bit set that, once created, has a fixed size over time.\n    ///\n    /// The set can store at most `u16::MAX` number of bits.\n    #[repr(C, packed)]\n    pub struct FixedBitSet<B = DefaultBitBlock> {\n        /// The size of the heap allocation in number of elements.\n        len: Len,\n        /// A pointer to a heap allocation of `[B]` of `self.len`.\n        ptr: NonNull<B>,\n    }\n\n    static_assert_align!(FixedBitSet, 1);\n    static_assert_size!(FixedBitSet, mem::size_of::<usize>() + mem::size_of::<Len>());\n\n    // SAFETY: `FixedBitSet` owns its data.\n    unsafe impl<B> Send for FixedBitSet<B> {}\n    // SAFETY: `FixedBitSet` owns its data.\n    unsafe impl<B> Sync for FixedBitSet<B> {}\n\n    impl<B> Drop for FixedBitSet<B> {\n        fn drop(&mut self) {\n            let blocks = self.storage_mut();\n            // SAFETY: We own the memory region pointed to by `blocks`,\n            // and as we have `&'0 mut self`, we also have exclusive access to it.\n            // So, and since we are in `drop`,\n            // we can deallocate the memory as we are the last referent to it.\n            // Moreover, the memory was allocated in `Self::new(..)` using `vec![..]`,\n            // which will allocate using `Global`, so we can convert it back to a `Box`.\n            let _ = unsafe { Box::from_raw(blocks) };\n        }\n    }\n\n    // We need to be able to serialize and deserialize `FixedBitSet` because they appear in the `PageHeader`.\n    impl_serialize!([B: BitBlock + Serialize] FixedBitSet<B>, (self, ser) => self.storage().serialize(ser));\n    impl_deserialize!([B: BitBlock + Deserialize<'de>] FixedBitSet<B>, de => {\n        let storage = Box::<[B]>::deserialize(de)?;\n        Ok(Self::from_boxed_slice(storage))\n    });\n\n    impl<B: BitBlock> FixedBitSet<B> {\n        pub(super) fn from_boxed_slice(storage: Box<[B]>) -> Self {\n            // SAFETY: required for the soundness of `Drop` as\n            // `dealloc` must receive the same layout as it was `alloc`ated with.\n            assert!(storage.len() <= Len::MAX as usize);\n            let len = storage.len() as Len;\n            let ptr = NonNull::from(Box::leak(storage)).cast();\n            Self { ptr, len }\n        }\n    }\n\n    impl<B> FixedBitSet<B> {\n        /// Returns the capacity of the bitset.\n        #[inline]\n        pub(super) const fn blocks(&self) -> usize {\n            self.len as usize\n        }\n\n        /// Returns the backing `[B]` slice for shared access.\n        pub(crate) const fn storage(&self) -> &[B] {\n            let ptr = self.ptr.as_ptr();\n            let len = self.blocks();\n            // SAFETY:\n            // - `self.ptr` is a `NonNull` so `ptr` cannot be null.\n            // - `self.ptr` is properly aligned for `BitBlock`s.\n            // - `self.ptr` is valid for reads as we have `&self` and we own the memory\n            //   which we know is `blocks` elements long.\n            // - As we have `&'0 self`, elsewhere cannot mutate the memory during `'0`\n            //   except through an `UnsafeCell`.\n            unsafe { from_raw_parts(ptr, len) }\n        }\n\n        /// Returns the backing `[B]` slice for mutation.\n        pub(super) fn storage_mut(&mut self) -> &mut [B] {\n            let ptr = self.ptr.as_ptr();\n            let len = self.blocks();\n            // SAFETY:\n            // - `self.ptr` is a `NonNull` so `ptr` cannot be null.\n            // - `self.ptr` is properly aligned for `BitBlock`s.\n            // - `self.ptr` is valid for reads and writes as we have `&mut self` and we own the memory\n            //   which we know is `blocks` elements long.\n            // - As we have `&'0 mut self`, we have exclusive access for `'0`\n            //   so the memory cannot be accessed elsewhere during `'0`.\n            unsafe { from_raw_parts_mut(ptr, len) }\n        }\n    }\n}\n\nimpl<B: BitBlock> cmp::Eq for FixedBitSet<B> {}\nimpl<B: BitBlock> cmp::PartialEq for FixedBitSet<B> {\n    fn eq(&self, other: &Self) -> bool {\n        self.storage() == other.storage()\n    }\n}\n\n/// Computes how many blocks are needed to store that many bits.\nfn blocks_for_bits<B: BitBlock>(bits: usize) -> usize {\n    // Must round e.g., 31 / 32 to 1 and 32 / 32 to 1 as well.\n    bits.div_ceil(B::BITS as usize)\n}\n\nimpl<B: BitBlock> FixedBitSet<B> {\n    /// Allocates a new bit set capable of holding `bits` number of bits.\n    pub fn new(bits: usize) -> Self {\n        Self::new_for_blocks(blocks_for_bits::<B>(bits))\n    }\n\n    /// Allocates a new bit set that will have a capacity of `nblocks` blocks.\n    #[inline]\n    fn new_for_blocks(nblocks: usize) -> Self {\n        // Allocate the blocks and extract the pointer to the heap region.\n        let blocks: Box<[B]> = vec![B::ZERO; nblocks].into_boxed_slice();\n\n        Self::from_boxed_slice(blocks)\n    }\n\n    /// Converts `idx` to its block index and the index within the block.\n    const fn idx_to_pos(idx: usize) -> (usize, usize) {\n        let bits = B::BITS as usize;\n        (idx / bits, idx % bits)\n    }\n\n    /// Returns whether `idx` is set or not.\n    pub fn get(&self, idx: usize) -> bool {\n        let (block_idx, pos_in_block) = Self::idx_to_pos(idx);\n        let block = self.storage()[block_idx];\n        (block & (B::ONE << pos_in_block)) != B::ZERO\n    }\n\n    /// Sets bit at position `idx` to `val`.\n    pub fn set(&mut self, idx: usize, val: bool) {\n        let (block_idx, pos_in_block) = Self::idx_to_pos(idx);\n        let block = &mut self.storage_mut()[block_idx];\n\n        // Update the block.\n        let flag = B::ONE << pos_in_block;\n        *block = if val { *block | flag } else { *block & !flag };\n    }\n\n    /// Clears every bit in the vec.\n    pub fn clear(&mut self) {\n        self.storage_mut().fill(B::ZERO);\n    }\n\n    /// Resets the bit set so that it's capable of holding `bits` number of bits.\n    ///\n    /// Every bit in the set will be zero after this.\n    pub fn reset_for(&mut self, bits: usize) {\n        // Compute the number of blocks needed.\n        let nblocks = blocks_for_bits::<B>(bits);\n\n        // Either clear the existing set, reusing it, or make a new one.\n        if nblocks == self.blocks() {\n            self.clear();\n        } else {\n            *self = Self::new_for_blocks(nblocks)\n        }\n    }\n\n    /// Returns the capacity of the bitset in bits.\n    #[inline]\n    pub(crate) const fn bits(&self) -> usize {\n        self.blocks() * B::BITS as usize\n    }\n\n    /// Returns all the set indices.\n    pub fn iter_set(&self) -> IterSet<'_, B> {\n        let mut inner = self.storage().iter();\n\n        // Fetch the first block; if it isn't there, use an all-zero one.\n        // This will cause the iterator to terminate immediately.\n        let curr = inner.next().copied().unwrap_or(B::ZERO);\n\n        IterSet {\n            inner,\n            curr,\n            block_idx: 0,\n        }\n    }\n\n    /// Returns all the set indices from `start_idx` inclusive.\n    pub fn iter_set_from(&self, start_idx: usize) -> IterSet<'_, B> {\n        // Translate the index to its block and position within it.\n        let (block_idx, pos_in_block) = Self::idx_to_pos(start_idx);\n\n        // We want our iteration to start from the block that includes `start_idx`.\n        let mut inner = self.storage()[block_idx..].iter();\n\n        // Fetch the first block; if it isn't there, use an all-zero one.\n        // This will cause the iterator to terminate immediately.\n        let curr = inner.next().copied().unwrap_or(B::ZERO);\n\n        // Our `start_idx` might be in the middle of the `curr` block.\n        // To resolve this, we must zero out any preceding bits.\n        // So e.g., for `B = u8`,\n        // we must transform `0000_1011` to `0000_1000` for `start_idx = 3`.\n        let zero_preceding_mask = B::ZERO.wrapping_sub(B::ONE << pos_in_block);\n        let curr = curr & zero_preceding_mask;\n\n        IterSet {\n            inner,\n            curr,\n            block_idx: block_idx as Len,\n        }\n    }\n}\n\nimpl<B> MemoryUsage for FixedBitSet<B> {\n    fn heap_usage(&self) -> usize {\n        std::mem::size_of_val(self.storage())\n    }\n}\n\nimpl<B: BitBlock> fmt::Debug for FixedBitSet<B> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_set().entries(self.iter_set()).finish()\n    }\n}\n\n/// An iterator that yields the set indices of a [`FixedBitSet`].\npub struct IterSet<'a, B = DefaultBitBlock> {\n    /// The block iterator.\n    inner: Iter<'a, B>,\n    /// The current block being processed, taken from `self.inner`.\n    curr: B,\n    /// What the index of `self.curr` is.\n    block_idx: Len,\n}\n\nimpl<B: BitBlock> Iterator for IterSet<'_, B> {\n    type Item = usize;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            let tz = self.curr.trailing_zeros();\n            if tz < B::BITS {\n                // Some bit was set; so yield the index of that\n                // and zero the bit out so we don't yield it again.\n                self.curr &= self.curr.wrapping_sub(B::ONE);\n                let idx = self.block_idx as u32 * B::BITS + tz;\n                return Some(idx as usize);\n            } else {\n                // No bit is set; advance to the next block, or quit if none left.\n                self.curr = *self.inner.next()?;\n                self.block_idx += 1;\n            }\n        }\n    }\n}\n\n#[cfg(test)]\npub(crate) mod test {\n    use super::*;\n    use proptest::bits::bitset::between;\n    use proptest::prelude::*;\n    use spacetimedb_data_structures::map::HashSet;\n\n    #[test]\n    #[should_panic]\n    fn zero_sized_is_ok() {\n        let mut set = FixedBitSet::<DefaultBitBlock>::new(0);\n        set.clear();\n        set.iter_set_from(0).count();\n        set.iter_set().count();\n        set.get(0);\n    }\n\n    const MAX_NBITS: usize = 1000;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n\n        #[test]\n        fn after_new_there_are_no_bits_set(nbits in 0..MAX_NBITS) {\n            let set = FixedBitSet::<DefaultBitBlock>::new(nbits);\n            for idx in 0..nbits {\n                prop_assert!(!set.get(idx));\n            }\n        }\n\n        #[test]\n        fn after_clear_there_are_no_bits_set(choices in between(0, MAX_NBITS)) {\n            let nbits = choices.get_ref().len();\n\n            let mut set = FixedBitSet::<DefaultBitBlock>::new(nbits);\n\n            // Set all the bits chosen.\n            for idx in &choices {\n                prop_assert!(!set.get(idx));\n                set.set(idx, true);\n                prop_assert!(set.get(idx));\n            }\n\n            // Clear!\n            set.clear();\n\n            // After clearing, all bits should be unset.\n            for idx in 0..nbits {\n                prop_assert!(!set.get(idx));\n            }\n        }\n\n        #[test]\n        fn get_set_consistency(choices in between(0, MAX_NBITS)) {\n            let nbits = choices.get_ref().len();\n            let mut set = FixedBitSet::<DefaultBitBlock>::new(nbits);\n\n            // Set all the bits chosen.\n            for idx in &choices {\n                prop_assert!(!set.get(idx));\n\n                // After setting, it's true.\n                set.set(idx, true);\n                prop_assert!(set.get(idx));\n                // And this is idempotent.\n                set.set(idx, true);\n                prop_assert!(set.get(idx));\n            }\n\n            // Build the \"complement\" of `choices`.\n            let choices: HashSet<_> = choices.into_iter().collect();\n            let universe: HashSet<_> = (0..nbits).collect();\n            for idx in universe.difference(&choices) {\n                prop_assert!(!set.get(*idx));\n            }\n\n            // Unset all the bits chosen.\n            for idx in &choices {\n                // After unsetting, it's false.\n                set.set(*idx, false);\n                prop_assert!(!set.get(*idx));\n                // And this is idempotent.\n                set.set(*idx, false);\n                prop_assert!(!set.get(*idx));\n            }\n        }\n\n        #[test]\n        fn iter_set_preserves_order_of_original_choices(choices in between(0, MAX_NBITS)) {\n            let nbits = choices.get_ref().len();\n\n            // Set all the bits chosen.\n            let mut set = FixedBitSet::<DefaultBitBlock>::new(nbits);\n            for idx in &choices {\n                set.set(idx, true);\n            }\n\n            // `iter_set` produces the same list `choices`.\n            let collected = set.iter_set().collect::<Vec<_>>();\n            let original = choices.iter().collect::<Vec<_>>();\n            prop_assert_eq!(&original, &collected);\n\n            if let [_, second, ..] = &*original {\n                // Starting from the second yields the same list as `choices[1..]`.\n                let collected = set.iter_set_from(*second).collect::<Vec<_>>();\n                prop_assert_eq!(&original[1..], &collected);\n            }\n\n            // `iter_set_from` and `iter_set` produce the same list.\n            prop_assert_eq!(collected, set.iter_set_from(0).collect::<Vec<_>>());\n        }\n\n        #[test]\n        fn serde_round_trip(choices in between(0, MAX_NBITS)) {\n            let nbits = choices.get_ref().len();\n\n            // Set all the bits chosen.\n            let mut set = FixedBitSet::<DefaultBitBlock>::new(nbits);\n            for idx in &choices {\n                set.set(idx, true);\n            }\n\n            let ser = spacetimedb_lib::bsatn::to_vec(&set)?;\n            let de = spacetimedb_lib::bsatn::from_slice::<FixedBitSet<DefaultBitBlock>>(&ser)?;\n\n            assert!(set == de);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/indexes.rs",
    "content": "//! Provides primitive types and definitions around\n//! bytes, row hashes, (page) sizes, offsets, and indices.\n\nuse super::util::range_move;\nuse crate::static_assert_size;\nuse ahash::RandomState;\nuse core::fmt;\nuse core::ops::{AddAssign, Div, Range, SubAssign};\nuse derive_more::{Add, Sub};\nuse spacetimedb_data_structures::map::ValidAsIdentityHash;\nuse spacetimedb_sats::layout::Size;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_sats::{impl_deserialize, impl_serialize};\n\n/// A byte is a `u8`.\n///\n/// Previous implementations used `MaybeUninit<u8>` here,\n/// but it became necessary to serialize pages to enable snapshotting,\n/// so we require that all bytes in a page be valid `u8`s, never uninit.\npub type Byte = u8;\n\n/// A slice of [`Byte`]s.\npub type Bytes = [Byte];\n\n/// Total size of a page, incl. header.\n///\n/// Defined as 64 KiB.\npub(crate) const PAGE_SIZE: usize = u16::MAX as usize + 1;\n\n/// Total size of a page header.\n///\n/// 64 as the header is aligned to 64 bytes.\npub(crate) const PAGE_HEADER_SIZE: usize = 64;\n\n/// The size of the data segment of a [`Page`](super::page::Page).\n///\n/// Currently 64KiB - 64 bytes.\n/// The 64 bytes are used for the header of the `Page`.\n// pub for benchmarks\npub const PAGE_DATA_SIZE: usize = PAGE_SIZE - PAGE_HEADER_SIZE;\n\n/// The content hash of a row.\n///\n/// Notes:\n/// - The hash is not cryptographically secure.\n///\n/// - The hash is valid only for the lifetime of a `Table`.\n///   This entails that it should not be persisted to disk\n///   or used as a stable identifier over the network.\n///   For example, the hashing algorithm could be different\n///   on different machines based on availability of hardware instructions.\n///   Moreover, due to random seeds, when restarting from disk,\n///   the hashes may be different for the same rows.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\n#[cfg_attr(any(test, feature = \"proptest\"), derive(proptest_derive::Arbitrary))]\npub struct RowHash(pub u64);\n\nimpl MemoryUsage for RowHash {}\n\nstatic_assert_size!(RowHash, 8);\n\n/// `RowHash` is already a hash, so no need to hash again.\nimpl ValidAsIdentityHash for RowHash {}\n\nimpl RowHash {\n    /// Returns a `Hasher` builder that yields the type of hashes that `RowHash` stores.\n    pub fn hasher_builder() -> RandomState {\n        // For equal `row`s, all calls within the same process will yield the same hash.\n        RandomState::with_seed(0x42)\n    }\n}\n\n/// An offset into a [`Page`].\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Add, Sub, bytemuck::NoUninit)]\n#[repr(transparent)]\n#[cfg_attr(any(test, feature = \"proptest\"), derive(proptest_derive::Arbitrary))]\npub struct PageOffset(\n    #[cfg_attr(any(test, feature = \"proptest\"), proptest(strategy = \"0..PageOffset::PAGE_END.0\"))] pub u16,\n);\n\nimpl MemoryUsage for PageOffset {}\n\nstatic_assert_size!(PageOffset, 2);\n\n// We need to ser/de `PageOffset`s because they appear within the `PageHeader`.\nimpl_serialize!([] PageOffset, (self, ser) => self.0.serialize(ser));\nimpl_deserialize!([] PageOffset, de => u16::deserialize(de).map(PageOffset));\n\nimpl PageOffset {\n    /// Returns the offset as a `usize` index.\n    #[inline]\n    pub const fn idx(self) -> usize {\n        self.0 as usize\n    }\n\n    /// Is this offset the null offset, which refers to an empty var-len object?\n    ///\n    /// The null `PageOffset` is used as a sentinel in `VarLenRef`s and `VarLenGranule`s\n    /// as the empty list.\n    /// `VAR_LEN_NULL` can never refer to an allocated `VarLenGranule`,\n    /// because the existence of a `VarLenGranule` implies the existence of at least one fixed-len row,\n    /// which means the fixed-len high water mark is strictly greater than zero.\n    ///\n    /// Note that the null `PageOffset` refers to a valid fixed-len row slot.\n    /// It may only be used as a sentinel value when referring to var-len allocations.\n    #[inline]\n    pub const fn is_var_len_null(self) -> bool {\n        self.0 == Self::VAR_LEN_NULL.0\n    }\n\n    /// The null offset, pointing to the beginning of a page.\n    ///\n    /// The null `PageOffset` is used as a sentinel in `VarLenRef`s and `VarLenGranule`s\n    /// as the empty list.\n    /// `VAR_LEN_NULL` can never refer to an allocated `VarLenGranule`,\n    /// because the existence of a `VarLenGranule` implies the existence of at least one fixed-len row,\n    /// which means the fixed-len high water mark is strictly greater than zero.\n    ///\n    /// Note that the null `PageOffset` refers to a valid fixed-len row slot.\n    /// It may only be used as a sentinel value when referring to var-len allocations.\n    pub const VAR_LEN_NULL: Self = Self(0);\n\n    /// Is this offset at the [`PageOffset::PAGE_END`]?\n    #[inline]\n    pub const fn is_at_end(self) -> bool {\n        self.0 == Self::PAGE_END.0\n    }\n\n    /// The offset one past the end of the page.\n    /// That is, for `row_data: [Byte; PAGE_DATA_SIZE]`, this is `row_data.len()`.\n    ///\n    /// This also means that `PAGE_END` is **not** a page offset\n    /// that can be used for indexing, but it can be used as a sentinel.\n    pub const PAGE_END: Self = Self(PAGE_DATA_SIZE as u16);\n\n    /// Returns a range from this offset lasting `size` bytes.\n    #[inline]\n    pub const fn range(self, size: Size) -> Range<usize> {\n        range_move(0..size.len(), self.idx())\n    }\n}\n\nimpl PartialEq<Size> for PageOffset {\n    #[inline]\n    fn eq(&self, other: &Size) -> bool {\n        self.0 == other.0\n    }\n}\n\nimpl AddAssign<Size> for PageOffset {\n    #[inline]\n    fn add_assign(&mut self, rhs: Size) {\n        self.0 += rhs.0;\n    }\n}\n\nimpl SubAssign<Size> for PageOffset {\n    #[inline]\n    fn sub_assign(&mut self, rhs: Size) {\n        self.0 -= rhs.0;\n    }\n}\n\nimpl Div<Size> for PageOffset {\n    type Output = usize;\n\n    #[inline]\n    fn div(self, size: Size) -> Self::Output {\n        self.idx() / size.len()\n    }\n}\n\nimpl fmt::LowerHex for PageOffset {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::LowerHex::fmt(&self.0, f)\n    }\n}\n\n/// Returns the maximum number of rows with `fixed_row_size` that a page can hold.\n#[inline]\npub fn max_rows_in_page(fixed_row_size: Size) -> usize {\n    PageOffset::PAGE_END.idx().div_ceil(fixed_row_size.len())\n}\n\n/// The index of a [`Page`] within a [`Pages`].\n#[cfg_attr(any(test, feature = \"proptest\"), derive(proptest_derive::Arbitrary))]\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]\npub struct PageIndex(#[cfg_attr(any(test, feature = \"proptest\"), proptest(strategy = \"0..MASK_PI\"))] pub u64);\n\nimpl MemoryUsage for PageIndex {}\n\nstatic_assert_size!(PageIndex, 8);\n\nimpl PageIndex {\n    /// The maximum page index, currently `(1 << 39) - 1`.\n    ///\n    /// Limiting `PageIndex` to 39 bits allows it to be packed into `RowPointer`'s 64 bits\n    /// alongside a `PageOffset`, a `SquashedOffset` and a reserved bit.\n    pub const MAX: Self = Self(MASK_PI);\n\n    /// Returns this index as a `usize`.\n    #[inline]\n    pub const fn idx(self) -> usize {\n        self.0 as usize\n    }\n}\n\n/// Indicates which version of a `Table` is referred to by a `RowPointer`.\n///\n/// Currently, `SquashedOffset` has two meaningful values,\n/// [`SquashedOffset::TX_STATE`] and [`SquashedOffset::COMMITTED_STATE`],\n/// which refer to the TX scratchpad and the committed state respectively.\n///\n/// In the future, `SquashedOffset` will be extended to capture\n/// which savepoint within a transaction the pointer refers to,\n/// or which committed-unsquashed preceding transaction.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(any(test, feature = \"proptest\"), derive(proptest_derive::Arbitrary))]\npub struct SquashedOffset(pub u8);\n\nimpl MemoryUsage for SquashedOffset {}\n\nstatic_assert_size!(SquashedOffset, 1);\n\nimpl SquashedOffset {\n    /// Does this `SquahsedOffset` refer to the TX scratchpad?\n    #[inline]\n    pub const fn is_tx_state(self) -> bool {\n        self.0 == Self::TX_STATE.0\n    }\n\n    /// The `SquashedOffset` for the TX scratchpad.\n    pub const TX_STATE: Self = Self(0);\n\n    /// Does this `SquashedOffset` refer to the committed (squashed) state?\n    #[inline]\n    pub const fn is_committed_state(self) -> bool {\n        self.0 == Self::COMMITTED_STATE.0\n    }\n\n    /// The `SquashedOffset` for the committed (squashed) state.\n    pub const COMMITTED_STATE: Self = Self(1);\n}\n\n/// Offset to a buffer inside `Pages` referring\n/// to the index of a specific page\n/// and the offset within the page.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct RowPointer(pub u64);\n\nimpl MemoryUsage for RowPointer {}\n\nstatic_assert_size!(RowPointer, 8);\n\n// Offsets and bits for the various components of `RowPointer`.\nconst OFFSET_RB: u64 = 0;\nconst BITS_RB: u64 = 1;\n// ^-- Bit 1 is reserved so it can be used in `OffsetOrCollider` (\"reserved bit\").\nconst OFFSET_PI: u64 = OFFSET_RB + BITS_RB;\nconst BITS_PI: u64 = 39;\nconst OFFSET_PO: u64 = OFFSET_PI + BITS_PI;\nconst BITS_PO: u64 = 16;\nconst OFFSET_SQ: u64 = OFFSET_PO + BITS_PO;\nconst BITS_SQ: u64 = 8;\n\n// Extracting masks for the various components of `RowPointer`.\nconst MASK_RB: u64 = (1 << BITS_RB) - 1;\nconst MASK_PI: u64 = (1 << BITS_PI) - 1;\nconst MASK_PO: u64 = (1 << BITS_PO) - 1;\nconst MASK_SQ: u64 = (1 << BITS_SQ) - 1;\n\n// Zeroing masks for the various components of `RowPointer`.\nconst MASK_ZERO_RB: u64 = !(MASK_RB << OFFSET_RB);\nconst MASK_ZERO_PI: u64 = !(MASK_PI << OFFSET_PI);\nconst MASK_ZERO_PO: u64 = !(MASK_PO << OFFSET_PO);\nconst MASK_ZERO_SQ: u64 = !(MASK_SQ << OFFSET_SQ);\n\nimpl RowPointer {\n    /// Returns a row pointer that is at the given `page_offset`,\n    /// in the page with `page_index`,\n    /// and with the `squashed_offset` (savepoint offset).\n    #[inline(always)]\n    pub const fn new(\n        reserved_bit: bool,\n        page_index: PageIndex,\n        page_offset: PageOffset,\n        squashed_offset: SquashedOffset,\n    ) -> Self {\n        Self(0)\n            .with_reserved_bit(reserved_bit)\n            .with_squashed_offset(squashed_offset)\n            .with_page_index(page_index)\n            .with_page_offset(page_offset)\n    }\n\n    /// Returns the reserved bit.\n    #[inline(always)]\n    pub const fn reserved_bit(self) -> bool {\n        ((self.0 >> OFFSET_RB) & MASK_RB) != 0\n    }\n\n    /// Returns the index of the page.\n    #[inline(always)]\n    pub const fn page_index(self) -> PageIndex {\n        PageIndex((self.0 >> OFFSET_PI) & MASK_PI)\n    }\n\n    /// Returns the offset within the page.\n    #[inline(always)]\n    pub const fn page_offset(self) -> PageOffset {\n        PageOffset(((self.0 >> OFFSET_PO) & MASK_PO) as u16)\n    }\n\n    /// Returns the squashed offset, i.e., the savepoint offset.\n    #[inline(always)]\n    pub const fn squashed_offset(self) -> SquashedOffset {\n        SquashedOffset(((self.0 >> OFFSET_SQ) & MASK_SQ) as u8)\n    }\n\n    /// Returns a new row pointer\n    /// with its reserved bit changed to `reserved_bit`.\n    #[inline(always)]\n    pub const fn with_reserved_bit(self, reserved_bit: bool) -> Self {\n        Self::with(self, reserved_bit as u64, MASK_RB, OFFSET_RB, MASK_ZERO_RB)\n    }\n\n    /// Returns a new row pointer\n    /// with its `PageIndex` changed to `page_index`.\n    #[inline(always)]\n    pub const fn with_page_index(self, page_index: PageIndex) -> Self {\n        Self::with(self, page_index.0, MASK_PI, OFFSET_PI, MASK_ZERO_PI)\n    }\n\n    /// Returns a new row pointer\n    /// with its `PageOffset` changed to `page_offset`.\n    #[inline(always)]\n    pub const fn with_page_offset(self, page_offset: PageOffset) -> Self {\n        Self::with(self, page_offset.0 as u64, MASK_PO, OFFSET_PO, MASK_ZERO_PO)\n    }\n\n    /// Returns a new row pointer\n    /// with its `SquashedOffset` changed to `squashed_offset`.\n    #[inline(always)]\n    pub const fn with_squashed_offset(self, squashed_offset: SquashedOffset) -> Self {\n        Self::with(self, squashed_offset.0 as u64, MASK_SQ, OFFSET_SQ, MASK_ZERO_SQ)\n    }\n\n    #[inline(always)]\n    const fn with(self, v: u64, mask: u64, offset: u64, zero: u64) -> Self {\n        let vmoved = (v & mask) << offset;\n        Self((self.0 & zero) | vmoved)\n    }\n}\n\nimpl fmt::Debug for RowPointer {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(\n            f,\n            \"RowPointer(r: {:?}, pi: {:?}, po: {:?}, so: {:?})\",\n            self.reserved_bit() as u8,\n            self.page_index().idx(),\n            self.page_offset().idx(),\n            self.squashed_offset().0,\n        )\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use proptest::prelude::*;\n\n    proptest! {\n        #[test]\n        fn row_pointer_ops_work(\n            ((rb1, pi1, po1, so1), (rb2, pi2, po2, so2)) in (\n                (any::<bool>(), any::<PageIndex>(), any::<PageOffset>(), any::<SquashedOffset>()),\n                (any::<bool>(), any::<PageIndex>(), any::<PageOffset>(), any::<SquashedOffset>()),\n        )) {\n            let check = |rb, pi, po, so, ptr: RowPointer| {\n                prop_assert_eq!(rb, ptr.reserved_bit());\n                prop_assert_eq!(pi, ptr.page_index());\n                prop_assert_eq!(po, ptr.page_offset());\n                prop_assert_eq!(so, ptr.squashed_offset());\n                Ok(())\n            };\n            let ptr = RowPointer::new(rb1, pi1, po1, so1);\n            check(rb1, pi1, po1, so1, ptr)?;\n            check(rb2, pi1, po1, so1, ptr.with_reserved_bit(rb2))?;\n            check(rb1, pi2, po1, so1, ptr.with_page_index(pi2))?;\n            check(rb1, pi1, po2, so1, ptr.with_page_offset(po2))?;\n            check(rb1, pi1, po1, so2, ptr.with_squashed_offset(so2))?;\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/lib.rs",
    "content": "#![forbid(unsafe_op_in_unsafe_fn)]\n\n//! The `spacetimedb_table` crate provides a `Table` implementation\n//! and various ways to interact with a table.\n\n// For now, all of these are public.\n// We'll make as much as possible private when mem-arch has merged fully.\n\npub mod bflatn_from;\npub mod bflatn_to;\npub mod blob_store;\npub mod eq;\nmod eq_to_pv;\npub mod fixed_bit_set;\npub mod indexes;\npub mod page;\npub mod page_pool;\npub mod pages;\npub mod pointer_map;\npub mod read_column;\npub mod row_hash;\npub mod row_type_visitor;\npub mod static_bsatn_validator;\npub mod static_layout;\npub mod table;\npub mod table_index;\npub mod var_len;\n\n#[doc(hidden)] // Used in tests and benchmarks.\npub mod util;\n"
  },
  {
    "path": "crates/table/src/page.rs",
    "content": "//! Provides a [`Page`] abstraction that stores rows\n//! and an associated header necessary for the page to work.\n//! Consult the documentation of this type for a list of operations\n//! and a description of how page work.\n//!\n//! A page can provide a split mutable view of its fixed section and its variable section.\n//! This is provided through [`Page::split_fixed_var_mut`] with view operations\n//! defined on [`FixedView`] and [`VarView`].\n//!\n//! [ralfj_safe_valid]: https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html\n//!\n//! Technical terms:\n//!\n//! - `valid` refers to, when referring to a type, granule, or row,\n//!   depending on the context, a memory location that holds a *safe* object.\n//!   When \"valid for writes\" is used, the location must be properly aligned\n//!   and none of its bytes may be uninit,\n//!   but the value need not be valid at the type in question.\n//!   \"Valid for writes\" is equivalent to valid-unconstrained.\n//!\n//! - `valid-unconstrained`, when referring to a memory location with a given type,\n//!   that the location stores a byte pattern which Rust/LLVM's memory model recognizes as valid,\n//!   and therefore must not contain any uninit,\n//!   but the value is not required to be logically meaningful,\n//!   and no code may depend on the data within it to uphold any invariants.\n//!   E.g. an unallocated [`VarLenGranule`] within a page stores valid-unconstrained bytes,\n//!   because the bytes are either 0 from the initial [`alloc_zeroed`] of the page,\n//!   or contain stale data from a previously freed [`VarLenGranule`].\n//!\n//! - `unused` means that it is safe to overwrite a block of memory without cleaning up its previous value.\n//!\n//!   See the post [Two Kinds of Invariants: Safety and Validity][ralf_safe_valid]\n//!   for a discussion on safety and validity invariants.\n\nuse super::{\n    blob_store::{BlobHash, BlobStore},\n    fixed_bit_set::FixedBitSet,\n    fixed_bit_set::IterSet,\n    indexes::{max_rows_in_page, Byte, Bytes, PageOffset, PAGE_HEADER_SIZE, PAGE_SIZE},\n    static_assert_size,\n    table::BlobNumBytes,\n    var_len::{is_granule_offset_aligned, VarLenGranule, VarLenGranuleHeader, VarLenMembers, VarLenRef},\n};\nuse core::{mem, ops::ControlFlow};\nuse spacetimedb_sats::layout::{Size, MIN_ROW_SIZE};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_sats::{de::Deserialize, ser::Serialize};\nuse thiserror::Error;\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum Error {\n    #[error(\"Want to allocate a var-len object of {need} granules, but have only {have} granules available\")]\n    InsufficientVarLenSpace { need: u16, have: u16 },\n    #[error(\"Want to allocate a fixed-len row of {} bytes, but the page is full\", need.len())]\n    InsufficientFixedLenSpace { need: Size },\n}\n\n/// A cons-cell in a freelist either\n/// for an unused fixed-len cell or a variable-length granule.\n#[repr(C)] // Required for a stable ABI.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, bytemuck::NoUninit, Serialize, Deserialize)]\nstruct FreeCellRef {\n    /// The address of the next free cell in a freelist.\n    ///\n    /// The `PageOffset::PAGE_END` is used as a sentinel to signal \"`None`\".\n    next: PageOffset,\n}\n\nimpl MemoryUsage for FreeCellRef {\n    fn heap_usage(&self) -> usize {\n        let Self { next } = self;\n        next.heap_usage()\n    }\n}\n\nimpl FreeCellRef {\n    /// The sentinel for NULL cell references.\n    const NIL: Self = Self {\n        next: PageOffset::PAGE_END,\n    };\n\n    /// Replaces the cell reference with `offset`, returning the existing one.\n    #[inline]\n    fn replace(&mut self, offset: PageOffset) -> FreeCellRef {\n        let next = mem::replace(&mut self.next, offset);\n        Self { next }\n    }\n\n    /// Returns whether the cell reference is non-empty.\n    #[inline]\n    const fn has(&self) -> bool {\n        !self.next.is_at_end()\n    }\n\n    /// Take the first free cell in the freelist starting with `self`, if any,\n    /// and promote the second free cell as the freelist head.\n    ///\n    /// # Safety\n    ///\n    /// When `self.has()`, it must point to a valid `FreeCellRef`.\n    #[inline]\n    unsafe fn take_freelist_head(\n        self: &mut FreeCellRef,\n        row_data: &Bytes,\n        adjust_free: impl FnOnce(PageOffset) -> PageOffset,\n    ) -> Option<PageOffset> {\n        self.has().then(|| {\n            let head = adjust_free(self.next);\n            // SAFETY: `self.next` so `head` points to a valid `FreeCellRef`.\n            let next = unsafe { get_ref(row_data, head) };\n            self.replace(*next).next\n        })\n    }\n\n    /// Prepend `new_head` to the freelist starting with `self`.\n    ///\n    /// SAFETY: `new_head`, after adjustment, must be in bounds of `row_data`.\n    /// Moreover, it must be valid for writing a `FreeCellRef` to it,\n    /// which includes being properly aligned with respect to `row_data` for a `FreeCellRef`.\n    /// Additionally, `self` must contain a valid `FreeCellRef`.\n    #[inline]\n    unsafe fn prepend_freelist(\n        self: &mut FreeCellRef,\n        row_data: &mut Bytes,\n        new_head: PageOffset,\n        adjust_free: impl FnOnce(PageOffset) -> PageOffset,\n    ) {\n        let next = self.replace(new_head);\n        let new_head = adjust_free(new_head);\n        // SAFETY: Per caller contract, `new_head` is in bounds of `row_data`.\n        // Moreover, `new_head` points to an unused `FreeCellRef`, so we can write to it.\n        let next_slot: &mut FreeCellRef = unsafe { get_mut(row_data, new_head) };\n        *next_slot = next;\n    }\n}\n\n/// All the fixed size header information.\n#[repr(C)] // Required for a stable ABI.\n#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] // So we can dump and restore pages during snapshotting.\nstruct FixedHeader {\n    /// A pointer to the head of the freelist which stores\n    /// all the unused (freed) fixed row cells.\n    /// These cells can be reused when inserting a new row.\n    next_free: FreeCellRef,\n\n    /// High water mark (HWM) for fixed-length rows.\n    /// Points one past the last-allocated (highest-indexed) fixed-length row,\n    /// so to allocate a fixed-length row from the gap,\n    /// post-increment this index.\n    // TODO(perf,future-work): determine how to lower the high water mark when freeing the topmost row.\n    last: PageOffset,\n\n    /// The number of rows currently in the page.\n    ///\n    /// N.B. this is not the same as `self.present_rows.len()`\n    /// as that counts both zero and one bits.\n    num_rows: u16,\n\n    // TODO(stable-module-abi): should this be inlined into the page?\n    /// For each fixed-length row slot, true if a row is stored there,\n    /// false if the slot is unallocated.\n    ///\n    /// Unallocated row slots store valid-unconstrained bytes, i.e. are never uninit.\n    present_rows: FixedBitSet,\n}\n\nimpl MemoryUsage for FixedHeader {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            next_free,\n            last,\n            num_rows,\n            present_rows,\n        } = self;\n        next_free.heap_usage() + last.heap_usage() + num_rows.heap_usage() + present_rows.heap_usage()\n    }\n}\n\nstatic_assert_size!(FixedHeader, 16);\n\nimpl FixedHeader {\n    /// Returns a new `FixedHeader`\n    /// using the provided `max_rows_in_page` to decide how many rows `present_rows` can represent.\n    #[inline]\n    fn new(max_rows_in_page: usize) -> Self {\n        Self {\n            next_free: FreeCellRef::NIL,\n            // Points one after the last allocated fixed-length row, or `NULL` for an empty page.\n            last: PageOffset::VAR_LEN_NULL,\n            num_rows: 0,\n            present_rows: FixedBitSet::new(max_rows_in_page),\n        }\n    }\n\n    /// Set the (fixed) row starting at `offset`\n    /// and lasting `fixed_row_size` as `present`.\n    #[inline]\n    fn set_row_present(&mut self, offset: PageOffset, fixed_row_size: Size) {\n        self.set_row_presence(offset, fixed_row_size, true);\n        self.num_rows += 1;\n    }\n\n    /// Sets whether the (fixed) row starting at `offset`\n    /// and lasting `fixed_row_size` is `present` or not.\n    #[inline]\n    fn set_row_presence(&mut self, offset: PageOffset, fixed_row_size: Size, present: bool) {\n        self.present_rows.set(offset / fixed_row_size, present);\n    }\n\n    /// Returns whether the (fixed) row starting at `offset`\n    /// and lasting `fixed_row_size` is present or not.\n    #[inline]\n    fn is_row_present(&self, offset: PageOffset, fixed_row_size: Size) -> bool {\n        self.present_rows.get(offset / fixed_row_size)\n    }\n\n    /// Resets the header information to its state\n    /// when it was first created in [`FixedHeader::new`]\n    /// but with `max_rows_in_page` instead of the value passed on creation.\n    fn reset_for(&mut self, max_rows_in_page: usize) {\n        self.next_free = FreeCellRef::NIL;\n        self.last = PageOffset::VAR_LEN_NULL;\n        self.num_rows = 0;\n        self.present_rows.reset_for(max_rows_in_page);\n    }\n\n    /// Resets the header information to its state\n    /// when it was first created in [`FixedHeader::new`].\n    ///\n    /// The header is only good for the original row size.\n    #[inline]\n    fn clear(&mut self) {\n        self.next_free = FreeCellRef::NIL;\n        self.last = PageOffset::VAR_LEN_NULL;\n        self.num_rows = 0;\n        self.present_rows.clear();\n    }\n}\n\n/// All the var-len header information.\n#[repr(C)] // Required for a stable ABI.\n#[derive(bytemuck::NoUninit, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]\nstruct VarHeader {\n    /// A pointer to the head of the freelist which stores\n    /// all the unused (freed) var-len granules.\n    /// These cells can be reused when inserting a new row.\n    next_free: FreeCellRef,\n\n    /// The length of the freelist with its head referred to by `next_free`.\n    /// Stored in units of var-len nodes.\n    ///\n    /// This field is redundant,\n    /// as it can be recovered by traversing `next_free`.\n    /// However, traversing this linked-list is not cache friendly,\n    /// so we memoize the computation here.\n    freelist_len: u16,\n\n    /// High water mark (HWM) for var-len granules.\n    /// Points to the last-allocated (lowest-indexed) var-len granule,\n    /// so to allocate a var-len granule from the gap,\n    /// pre-decrement this index.\n    // TODO(perf,future-work): determine how to \"lower\" the high water mark when freeing the \"top\"-most granule.\n    first: PageOffset,\n\n    /// The number of granules currently used by rows within this page.\n    ///\n    /// [`Page::bytes_used_by_rows`] needs this information.\n    /// Stored here because otherwise counting it would require traversing all the present rows.\n    num_granules: u16,\n}\n\nimpl MemoryUsage for VarHeader {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            next_free,\n            freelist_len,\n            first,\n            num_granules,\n        } = self;\n        next_free.heap_usage() + freelist_len.heap_usage() + first.heap_usage() + num_granules.heap_usage()\n    }\n}\n\nstatic_assert_size!(VarHeader, 8);\n\nimpl Default for VarHeader {\n    fn default() -> Self {\n        Self {\n            next_free: FreeCellRef::NIL,\n            freelist_len: 0,\n            first: PageOffset::PAGE_END,\n            num_granules: 0,\n        }\n    }\n}\n\nimpl VarHeader {\n    /// Resets the header information to its state\n    /// when it was first created in [`VarHeader::default`].\n    fn clear(&mut self) {\n        *self = Self::default();\n    }\n}\n\n/// The metadata / header of a page that is necessary\n/// for modifying and interpreting the `row_data`.\n///\n/// This header info is split into a header for the fixed part\n/// and one for the variable part.\n/// The header is stored in the same heap allocation as the `row_data`\n/// as the whole [`Page`] is `Box`ed.\n#[repr(C)] // Required for a stable ABI.\n#[repr(align(64))] // Alignment must be same as `VarLenGranule::SIZE`.\n#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] // So we can dump and restore pages during snapshotting.\npub(super) struct PageHeader {\n    /// The header data relating to the fixed component of a row.\n    fixed: FixedHeader,\n    /// The header data relating to the var-len component of a row.\n    var: VarHeader,\n    /// The content-addressed hash of the page on disk,\n    /// if unmodified since the last snapshot,\n    /// and `None` otherwise.\n    ///\n    /// This means that modifications to the page always sets this field to `None`.\n    unmodified_hash: Option<blake3::Hash>,\n}\n\nimpl MemoryUsage for PageHeader {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            fixed,\n            var,\n            // MEMUSE: no allocation, ok to ignore\n            unmodified_hash: _,\n        } = self;\n        fixed.heap_usage() + var.heap_usage()\n    }\n}\n\nstatic_assert_size!(PageHeader, PAGE_HEADER_SIZE);\n\nimpl PageHeader {\n    /// Returns a new `PageHeader` proper a [`Page`] for holding at most `max_rows_in_page` rows.\n    fn new(max_rows_in_page: usize) -> Self {\n        Self {\n            fixed: FixedHeader::new(max_rows_in_page),\n            var: VarHeader::default(),\n            unmodified_hash: None,\n        }\n    }\n\n    /// Resets the header information to its state\n    /// when it was first created in [`PageHeader::new`]\n    /// but with `max_rows_in_page` instead of the value passed on creation.\n    fn reset_for(&mut self, max_rows_in_page: usize) {\n        self.fixed.reset_for(max_rows_in_page);\n        self.var.clear();\n        self.unmodified_hash = None;\n    }\n\n    /// Resets the header information to its state\n    /// when it was first created in [`PageHeader::new`].\n    ///\n    /// The header is only good for the original row size.\n    fn clear(&mut self) {\n        self.fixed.clear();\n        self.var.clear();\n        self.unmodified_hash = None;\n    }\n\n    /// Returns the maximum number of rows the page can hold.\n    ///\n    /// Note that this number can be bigger\n    /// than the value provided in [`Self::new`] due to rounding up.\n    pub(super) fn max_rows_in_page(&self) -> usize {\n        self.fixed.present_rows.bits()\n    }\n\n    /// Returns a pointer to the `present_rows` bitset.\n    /// This is exposed for testing only.\n    #[cfg(test)]\n    pub(super) fn present_rows_storage_ptr_for_test(&self) -> *const () {\n        self.fixed.present_rows.storage().as_ptr().cast()\n    }\n}\n\n/// Fixed-length row portions must be at least large enough to store a `FreeCellRef`.\nconst _MIN_ROW_SIZE_CAN_STORE_FCR: () = assert!(MIN_ROW_SIZE.len() >= mem::size_of::<FreeCellRef>());\n\n/// [`VarLenGranule`]s must be at least large enough to store a [`FreeCellRef`].\nconst _VLG_CAN_STORE_FCR: () = assert!(VarLenGranule::SIZE.len() >= MIN_ROW_SIZE.len());\n\n/// Pointers properly aligned for a [`VarLenGranule`] must be properly aligned for [`FreeCellRef`].\n/// This is the case as the former's alignment is a multiple of the latter's alignment.\nconst _VLG_ALIGN_MULTIPLE_OF_FCR: () =\n    assert!(mem::align_of::<VarLenGranule>().is_multiple_of(mem::align_of::<FreeCellRef>()));\n\n/// The actual row data of a [`Page`].\ntype RowData = [Byte; PageOffset::PAGE_END.idx()];\n\n/// A page of row data with an associated `header` and the raw `row_data` itself.\n///\n/// As a rough summary, the strategy employed by this page is:\n///\n/// - The fixed-len parts of rows grows left-to-right\n///   and starts from the beginning of the `row_data`\n///   until its high water mark (fixed HWM), i.e., `self.header.fixed.last`.\n///\n/// - The var-len parts of rows grows right-to-left\n///   and starts from the end of the `row_data`\n///   until its high water mark (variable HWM), i.e., `self.header.var.first`.\n///\n///   Each var-len object is stored in terms of a linked-list of chunks.\n///   Each chunk in this case is a [`VarLenGranule`] taking up 64 bytes where:\n///   - 6 bits = length, 10 bits = next-cell-pointer\n///   - 62 bytes = the bytes of the object\n///\n/// - As new rows are added, the HWMs move appropriately.\n///   When the fixed and variable HWMs meet, the page is full.\n///\n/// - When rows are freed, a freelist strategy is used both for\n///   the fixed parts and each `VarLenGranule`.\n///   These freelists are then used first before using space from the gap.\n///   The head of these freelists are stored in `next_free`\n///   in the fixed and variable headers respectively.\n///\n/// - As the fixed parts of rows may store pointers into the var-length section,\n///   to ensure that these pointers aren't dangling,\n///   the page uses pointer fixups when adding to, deleting from, and copying the page.\n///   These fixups are handled by having callers provide `VarLenMembers`\n///   to find the var-len reference slots in the fixed parts.\n#[repr(C)]\n// ^-- Required for a stable ABI.\n#[repr(align(64))]\n// ^-- Must have align at least that of `VarLenGranule`,\n// so that `row_data[PageOffset::PAGE_END - VarLenGranule::SIZE]` is an aligned pointer to `VarLenGranule`.\n// TODO(bikeshedding): consider raising the alignment. We may want this to be OS page (4096) aligned.\n#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] // So we can dump and restore pages during snapshotting.\npub struct Page {\n    /// The header containing metadata on how to interpret and modify the `row_data`.\n    header: PageHeader,\n    /// The actual bytes stored in the page.\n    /// This contains row data, fixed and variable, and freelists.\n    row_data: RowData,\n}\n\nimpl MemoryUsage for Page {\n    fn heap_usage(&self) -> usize {\n        self.header.heap_usage()\n    }\n}\n\nstatic_assert_size!(Page, PAGE_SIZE);\n\n/// A mutable view of the fixed-len section of a [`Page`].\npub struct FixedView<'page> {\n    /// A mutable view of the fixed-len bytes.\n    fixed_row_data: &'page mut Bytes,\n    /// A mutable view of the fixed header.\n    header: &'page mut FixedHeader,\n}\n\nimpl FixedView<'_> {\n    /// Returns a mutable view of the row from `start` lasting `fixed_row_size` number of bytes.\n    ///\n    /// This method is safe, but callers should take care that `start` and `fixed_row_size`\n    /// are correct for this page, and that `start` is aligned.\n    /// Callers should further ensure that mutations to the row leave the row bytes\n    /// in an expected state, i.e. initialized where required by the row type,\n    /// and with `VarLenRef`s that point to valid granules and with correct lengths.\n    pub fn get_row_mut(&mut self, start: PageOffset, fixed_row_size: Size) -> &mut Bytes {\n        &mut self.fixed_row_data[start.range(fixed_row_size)]\n    }\n\n    /// Returns a shared view of the row from `start` lasting `fixed_row_size` number of bytes.\n    fn get_row(&mut self, start: PageOffset, fixed_row_size: Size) -> &Bytes {\n        &self.fixed_row_data[start.range(fixed_row_size)]\n    }\n\n    /// Frees the row starting at `row_offset` and lasting `fixed_row_size` bytes.\n    ///\n    /// # Safety\n    ///\n    /// `range_move(0..fixed_row_size, row_offset)` must be in bounds of `row_data`.\n    /// Moreover, it must be valid for writing a `FreeCellRef` to it,\n    /// which includes being properly aligned with respect to `row_data` for a `FreeCellRef`.\n    pub unsafe fn free(&mut self, row_offset: PageOffset, fixed_row_size: Size) {\n        // TODO(perf,future-work): if `row` is at the HWM, return it to the gap.\n\n        // SAFETY: Per caller contract, `row_offset` must be in bounds of `row_data`.\n        // Moreover, it must be valid for writing a `FreeCellRef` to it,\n        // which includes being properly aligned with respect to `row_data` for a `FreeCellRef`.\n        // We also know that `self.header.next_free` contains a valid `FreeCellRef`.\n        unsafe {\n            self.header\n                .next_free\n                .prepend_freelist(self.fixed_row_data, row_offset, |x| x)\n        };\n        self.header.num_rows -= 1;\n        self.header.set_row_presence(row_offset, fixed_row_size, false);\n    }\n}\n\n/// A mutable view of the var-len section of a [`Page`].\npub struct VarView<'page> {\n    /// A mutable view of the var-len bytes.\n    var_row_data: &'page mut Bytes,\n    /// A mutable view of the var-len header.\n    header: &'page mut VarHeader,\n    /// One past the end of the fixed-len section of the page.\n    last_fixed: PageOffset,\n}\n\nimpl<'page> VarView<'page> {\n    /// Returns the number of granules required to store the data,\n    /// whether the page has enough space,\n    /// and whether the object needs to go in the blob store.\n    ///\n    /// If the third value is `true`, i.e., the object will go in the blob store,\n    /// the first value will always be `1`.\n    fn has_enough_space_for(&self, len_in_bytes: usize) -> (usize, bool, bool) {\n        let (num_granules_req, in_blob) = VarLenGranule::bytes_to_granules(len_in_bytes);\n        let enough_space = num_granules_req <= self.num_granules_available();\n        (num_granules_req, enough_space, in_blob)\n    }\n\n    /// Returns the number of granules available for allocation.\n    fn num_granules_available(&self) -> usize {\n        self.header.freelist_len as usize\n            + VarLenGranule::space_to_granules(gap_remaining_size(self.header.first, self.last_fixed))\n    }\n\n    /// Provides an adjuster of offset in terms of `Page::row_data`\n    /// to work in terms of `VarView::var_row_data`.\n    ///\n    /// This has to be done due to `page.row_data.split_at_mut(last_fixed)`.\n    #[inline(always)]\n    fn adjuster(&self) -> impl FnOnce(PageOffset) -> PageOffset + use<> {\n        let lf = self.last_fixed;\n        move |offset| offset - lf\n    }\n\n    /// Allocates a linked-list of granules, in the var-len storage of the page,\n    /// for a var-len object of `obj_len` bytes.\n    ///\n    /// Returns a [`VarLenRef`] pointing to the head of that list,\n    /// and a boolean `in_blob` for whether the allocation is a `BlobHash`\n    /// and the object must be inserted into the large-blob store.\n    ///\n    /// The length of each granule is set, but no data is written to any granule.\n    /// Thus, the caller must proceed to write data to each granule for the claimed lengths.\n    ///\n    /// # Safety post-requirements\n    ///\n    /// The following are the safety *post-requirements* of calling this method.\n    /// That is, this method is safe to call,\n    /// but may leave the page in an inconsistent state\n    /// which must be rectified before other **unsafe methods** may be called.\n    ///\n    /// 1. When the returned `in_blob` holds, caller must ensure that,\n    ///    before the granule's data is read from / assumed to be initialized,\n    ///    the granule pointed to by the returned `vlr.first_granule`\n    ///    has an initialized header and a data section initialized to at least\n    ///    as many bytes as claimed by the header.\n    ///\n    /// 2. The caller must initialize each granule with data for the claimed length\n    ///    of the granule's data.\n    pub fn alloc_for_len(&mut self, obj_len: usize) -> Result<(VarLenRef, bool), Error> {\n        // Safety post-requirements of `alloc_for_obj_common`:\n        // 1. caller promised they will be satisfied.\n        // 2a. already satisfied as the closure below returns all the summands of `obj_len`.\n        // 2b. caller promised in 2. that they will satisfy this.\n        self.alloc_for_obj_common(obj_len, |req_granules| {\n            let rem = obj_len % VarLenGranule::DATA_SIZE;\n            (0..req_granules).map(move |rev_idx| {\n                let len = if rev_idx == 0 && rem != 0 {\n                    // The first allocated granule will be the last in the list.\n                    // Thus, `rev_idx == 0` is the last element and might not take up a full granule.\n                    rem\n                } else {\n                    VarLenGranule::DATA_SIZE\n                };\n                // Caller will initialize the granule's data for `len` bytes.\n                (<&[u8]>::default(), len)\n            })\n        })\n    }\n\n    /// Returns an iterator over all offsets of the `VarLenGranule`s of the var-len object\n    /// that has its first granule at offset `first_granule`.\n    /// An empty iterator will be returned when `first_granule` is `NULL`.\n    ///\n    /// # Safety\n    ///\n    /// `first_granule` must be an offset to a granule or `NULL`.\n    /// The data of the granule need not be initialized.\n    pub unsafe fn granule_offset_iter(&mut self, first_granule: PageOffset) -> GranuleOffsetIter<'_, 'page> {\n        GranuleOffsetIter {\n            next_granule: first_granule,\n            var_view: self,\n        }\n    }\n\n    /// Allocates and stores `slice` as a linked-list of granules\n    /// in the var-len storage of the page.\n    ///\n    /// Returns a [`VarLenRef`] pointing to the head of that list,\n    /// and a boolean `in_blob` for whether the allocation is a `BlobHash`\n    /// and the `slice` must be inserted into the large-blob store.\n    ///\n    /// # Safety post-requirements\n    ///\n    /// The following are the safety *post-requirements* of calling this method.\n    /// That is, this method is safe to call,\n    /// but may leave the page in an inconsistent state\n    /// which must be rectified before other **unsafe methods** may be called.\n    ///\n    /// 1. When the returned `in_blob` holds, caller must ensure that,\n    ///    before the granule's data is read from / assumed to be initialized,\n    ///    the granule pointed to by the returned `vlr.first_granule`\n    ///    has an initialized header and a data section initialized to at least\n    ///    as many bytes as claimed by the header.\n    pub fn alloc_for_slice(&mut self, slice: &[u8]) -> Result<(VarLenRef, bool), Error> {\n        let obj_len = slice.len();\n        // Safety post-requirement 2. of `alloc_for_obj_common` is already satisfied\n        // as `chunks(slice)` will return sub-slices where the sum is `obj_len`.\n        // Moreover, we initialize each granule already with the right data and length.\n        // The requirement 1. is forwarded to the caller.\n        let chunks = |_| VarLenGranule::chunks(slice).rev().map(|c| (c, c.len()));\n        self.alloc_for_obj_common(obj_len, chunks)\n    }\n\n    /// Allocates for `obj_len` bytes as a linked-list of granules\n    /// in the var-len storage of the page.\n    ///\n    /// For every granule in the aforementioned linked-list,\n    /// the caller must provide an element in the *reversed* iterator `chunks`,\n    /// and of pairs `(chunk, len)`.\n    /// To each granule `chunk` will be written and the granule will be of length `len`.\n    /// The caller can opt to provide `chunk` that is not of `len`.\n    ///\n    /// Returns a [`VarLenRef`] pointing to the head of that list,\n    /// and a boolean `in_blob` for whether the allocation is a `BlobHash`\n    /// and the `slice` must be inserted into the large-blob store.\n    ///\n    /// # Safety post-requirements\n    ///\n    /// The following are the safety *post-requirements* of calling this method.\n    /// That is, this method is safe to call,\n    /// but may leave the page in an inconsistent state\n    /// which must be rectified before other **unsafe methods** may be called.\n    ///\n    /// 1. When the returned `in_blob` holds, caller must ensure that,\n    ///    before the granule's data is read from / assumed to be initialized,\n    ///    the granule pointed to by the returned `vlr.first_granule`\n    ///    has an initialized header and a data section initialized to at least\n    ///    as many bytes as claimed by the header.\n    ///\n    /// 2. Otherwise, when `in_blob` doesn't hold the safety post-requirements are:\n    ///\n    ///    a. Let `cs = chunks(req_granules)` for the `req_granules` derived from `obj_len`.\n    ///       Then, `obj_len == cs.map(|(_, len)| len).sum()`.\n    ///\n    ///    b. For each `(_, len) ∈ cs`, caller must ensure that\n    ///       the relevant granule is initialized with data for at least `len`\n    ///       before the granule's data is read from / assumed to be initialized.\n    #[expect(clippy::doc_overindented_list_items)]\n    fn alloc_for_obj_common<'chunk, Cs: Iterator<Item = (&'chunk [u8], usize)>>(\n        &mut self,\n        obj_len: usize,\n        chunks: impl Copy + FnOnce(usize) -> Cs,\n    ) -> Result<(VarLenRef, bool), Error> {\n        // Check that we have sufficient space to allocate `obj_len` bytes in var-len data.\n        let (req_granules, enough_space, in_blob) = self.has_enough_space_for(obj_len);\n        if !enough_space {\n            return Err(Error::InsufficientVarLenSpace {\n                need: req_granules.try_into().unwrap_or(u16::MAX),\n                have: self.num_granules_available().try_into().unwrap_or(u16::MAX),\n            });\n        }\n\n        // For large blob objects, only reserve a granule.\n        // The caller promised that they will initialize it with a blob hash.\n        if in_blob {\n            let vlr = self.alloc_blob_hash()?;\n            return Ok((vlr, true));\n        };\n\n        // Write each `chunk` to var-len storage.\n        // To do this, we allocate granules for and store the chunks in reverse,\n        // starting with the end first.\n        // The offset to the previous granule in the iteration is kept to\n        // link it in as the next pointer in the current iteration.\n        let mut next = PageOffset::VAR_LEN_NULL;\n        debug_assert_eq!(obj_len, chunks(req_granules).map(|(_, len)| len).sum::<usize>());\n        for (chunk, len) in chunks(req_granules) {\n            // This should never error, since we already checked for available space.\n            let granule = self.alloc_granule()?;\n            // SAFETY:\n            // 1. `granule` is properly aligned as it came from `alloc_granule`\n            //    and so is `next` as it's either NULL or was the previous `granule`.\n            //    This also ensures that both are in bounds\n            //    of the page for `granule + granule + VarLenGranule::SIZE`.\n            //\n            // 2. `next` is either NULL or was initialized in the previous loop iteration.\n            //\n            // 3. `granule` points to an unused slot as the space was just allocated.\n            unsafe { self.write_chunk_to_granule(chunk, len, granule, next) };\n            next = granule;\n        }\n\n        Ok((\n            VarLenRef {\n                first_granule: next,\n                length_in_bytes: obj_len as u16,\n            },\n            false,\n        ))\n    }\n\n    /// Allocates a granule for a large blob object\n    /// and returns a [`VarLenRef`] pointing to that granule.\n    ///\n    /// The granule is not initialized by this method, and contains valid-unconstrained bytes.\n    /// It is the caller's responsibility to initialize it with a [`BlobHash`](super::blob_hash::BlobHash).\n    #[cold]\n    fn alloc_blob_hash(&mut self) -> Result<VarLenRef, Error> {\n        // Var-len hashes are 32 bytes, which fits within a single granule.\n        self.alloc_granule().map(VarLenRef::large_blob)\n    }\n\n    /// Inserts `var_len_obj` into `blob_store`\n    /// and stores the blob hash in the granule pointed to by `vlr.first_granule`.\n    ///\n    /// This insertion will never fail.\n    ///\n    /// # Safety\n    ///\n    /// `vlr.first_granule` must point to an unused `VarLenGranule` in bounds of this page,\n    /// which must be valid for writes.\n    pub unsafe fn write_large_blob_hash_to_granule(\n        &mut self,\n        blob_store: &mut dyn BlobStore,\n        var_len_obj: &impl AsRef<[u8]>,\n        vlr: VarLenRef,\n    ) -> BlobNumBytes {\n        let hash = blob_store.insert_blob(var_len_obj.as_ref());\n\n        let granule = vlr.first_granule;\n        // SAFETY:\n        // 1. `granule` is properly aligned for `VarLenGranule` and is in bounds of the page.\n        // 2. The null granule is trivially initialized.\n        // 3. The caller promised that `granule` is safe to overwrite.\n        unsafe { self.write_chunk_to_granule(&hash.data, hash.data.len(), granule, PageOffset::VAR_LEN_NULL) };\n        var_len_obj.as_ref().len().into()\n    }\n\n    /// Write the `chunk` (data) to the [`VarLenGranule`] pointed to by `granule`,\n    /// set the granule's length to be `len`,\n    /// and set the next granule in the list to `next`.\n    ///\n    /// SAFETY:\n    ///\n    /// 1. Both `granule` and `next` must be properly aligned pointers to [`VarLenGranule`]s\n    ///    and they must be in bounds of the page. However, neither need to point to init data.\n    ///\n    /// 2. The caller must initialize the granule pointed to by `next`\n    ///    before the granule-list is read from (e.g., iterated on).\n    ///    The null granule is considered trivially initialized.\n    ///\n    /// 3. The space pointed to by `granule` must be unused and valid for writes,\n    ///    and will be overwritten here.\n    unsafe fn write_chunk_to_granule(&mut self, chunk: &[u8], len: usize, granule: PageOffset, next: PageOffset) {\n        let granule = self.adjuster()(granule);\n        // SAFETY: A `PageOffset` is always in bounds of the page.\n        let ptr: *mut VarLenGranule = unsafe { offset_to_ptr_mut(self.var_row_data, granule).cast() };\n\n        // TODO(centril,bikeshedding): check if creating the `VarLenGranule` first on stack\n        // and then writing to `ptr` would have any impact on perf.\n        // This would be nicer as it requires less `unsafe`.\n\n        // We need to initialize `Page::header`\n        // without materializing a `&mut` as that is instant UB.\n        // SAFETY: `ptr` isn't NULL as `&mut self.row_data` itself is a non-null pointer.\n        let header = unsafe { &raw mut (*ptr).header };\n\n        // SAFETY: `header` is valid for writes as only we have exclusive access.\n        //          (1) The `ptr` was also promised as aligned\n        //          and `granule + (granule + 64 bytes)` is in bounds of the page per caller contract.\n        //          (2) Moreover, `next` will be an initialized granule per caller contract,\n        //          so we can link it into the list without causing UB elsewhere.\n        //          (3) It's also OK to write to `granule` as it's unused.\n        unsafe {\n            header.write(VarLenGranuleHeader::new(len as u8, next));\n        }\n\n        // SAFETY: We can treat any part of `row_data` as `.data`. Also (1) and (2).\n        let data = unsafe { &mut (*ptr).data };\n\n        // Copy the data into the granule.\n        data[0..chunk.len()].copy_from_slice(chunk);\n    }\n\n    /// Allocate a [`VarLenGranule`] at the returned [`PageOffset`].\n    ///\n    /// The allocated storage is not initialized by this method,\n    /// and will be valid-unconstrained at [`VarLenGranule`].\n    ///\n    /// This offset will be properly aligned for `VarLenGranule` when converted to a pointer.\n    ///\n    /// Returns an error when there are neither free granules nor space in the gap left.\n    fn alloc_granule(&mut self) -> Result<PageOffset, Error> {\n        let granule = self\n            .alloc_from_freelist()\n            .or_else(|| self.alloc_from_gap())\n            .ok_or(Error::InsufficientVarLenSpace { need: 1, have: 0 })?;\n\n        debug_assert!(\n            is_granule_offset_aligned(granule),\n            \"Allocated an unaligned var-len granule: {granule:x}\",\n        );\n\n        self.header.num_granules += 1;\n\n        Ok(granule)\n    }\n\n    /// Allocate a [`VarLenGranule`] at the returned [`PageOffset`]\n    /// taken from the freelist, if any.\n    #[inline]\n    fn alloc_from_freelist(&mut self) -> Option<PageOffset> {\n        // SAFETY: `header.next_free` points to a `c: FreeCellRef` when the former `.has()`.\n        let free = unsafe {\n            self.header\n                .next_free\n                .take_freelist_head(self.var_row_data, |o| o - self.last_fixed)\n        }?;\n        self.header.freelist_len -= 1;\n        Some(free)\n    }\n\n    /// Allocate a [`VarLenGranule`] at the returned [`PageOffset`]\n    /// taken from the gap, if there is space left, or `None` if there is insufficient space.\n    #[inline]\n    fn alloc_from_gap(&mut self) -> Option<PageOffset> {\n        if gap_enough_size_for_row(self.header.first, self.last_fixed, VarLenGranule::SIZE) {\n            // `var.first` points *at* the lowest-indexed var-len granule,\n            // *not* before it, so pre-decrement.\n            self.header.first -= VarLenGranule::SIZE;\n            Some(self.header.first)\n        } else {\n            None\n        }\n    }\n\n    /// Free a single var-len granule pointed to at by `offset`.\n    ///\n    /// SAFETY: `offset` must point to a valid [`VarLenGranule`].\n    #[inline]\n    unsafe fn free_granule(&mut self, offset: PageOffset) {\n        // TODO(perf,future-work): if `chunk` is at the HWM, return it to the gap.\n        //       Returning a single chunk to the gap is easy,\n        //       but we want to return a whole \"run\" of sequential freed chunks,\n        //       which requires some bookkeeping (or an O(> n) linked list traversal).\n        self.header.freelist_len += 1;\n        self.header.num_granules -= 1;\n        let adjuster = self.adjuster();\n\n        // SAFETY: Per caller contract, `offset` is a valid `VarLenGranule`,\n        // and is therefore in bounds of the page row data.\n        // By `_VLG_CAN_STORE_FCR`, and as we won't be reading from the granule anymore,\n        // we know that this makes it valid for writing a `FreeCellRef` to it.\n        // Moreover, by `_VLG_ALIGN_MULTIPLE_OF_FCR`,\n        // the derived pointer is properly aligned (64) for a granule\n        // and as `64 % 2 == 0` the alignment of a granule works for a `FreeCellRef`.\n        // Finally, `self.header.next_free` contains a valid `FreeCellRef`.\n        unsafe {\n            self.header\n                .next_free\n                .prepend_freelist(self.var_row_data, offset, adjuster)\n        };\n    }\n\n    /// Returns a reference to the granule at `offset`.\n    ///\n    /// SAFETY: `offset` must point to a valid [`VarLenGranule`].\n    unsafe fn get_granule_ref(&self, offset: PageOffset) -> &VarLenGranule {\n        unsafe { get_ref(self.var_row_data, self.adjuster()(offset)) }\n    }\n\n    /// Frees the blob pointed to by the [`BlobHash`] stored in the granule at `offset`.\n    ///\n    /// Panics when `offset` is NULL.\n    ///\n    /// SAFETY: `offset` must point to a valid [`VarLenGranule`] or be NULL.\n    #[cold]\n    #[inline(never)]\n    unsafe fn free_blob(&self, offset: PageOffset, blob_store: &mut dyn BlobStore) -> BlobNumBytes {\n        assert!(!offset.is_var_len_null());\n\n        // SAFETY: Per caller contract + the assertion above,\n        // we know `offset` refers to a valid `VarLenGranule`.\n        let granule = unsafe { self.get_granule_ref(offset) };\n\n        // Actually free the blob.\n        let hash = granule.blob_hash();\n\n        // The size of `deleted_bytes` is calculated here instead of requesting it from `blob_store`.\n        // This is because the actual number of bytes deleted depends on the `blob_store`'s logic.\n        // We prefer to measure it from the datastore's point of view.\n        let blob_store_deleted_bytes = blob_store\n            .retrieve_blob(&hash)\n            .expect(\"failed to free var-len blob\")\n            .len()\n            .into();\n\n        // Actually free the blob.\n        blob_store.free_blob(&hash).expect(\"failed to free var-len blob\");\n\n        blob_store_deleted_bytes\n    }\n\n    /// Frees an entire var-len linked-list object.\n    ///\n    /// If the `var_len_obj` is a large blob,\n    /// the `VarLenGranule` which stores its blob hash will be freed from the page,\n    /// but the blob itself will not be freed from the blob store.\n    /// If used incorrectly, this may leak large blobs.\n    ///\n    /// This behavior is used to roll-back on failure in `[crate::bflatn::ser::write_av_to_page]`,\n    /// where inserting large blobs is deferred until all allocations succeed.\n    /// Freeing a fully-inserted object should instead use [`Self::free_object`].\n    ///\n    /// # Safety\n    ///\n    /// `var_len_obj.first_granule` must point to a valid [`VarLenGranule`] or be NULL.\n    pub unsafe fn free_object_ignore_blob(&mut self, var_len_obj: VarLenRef) {\n        let mut next_granule = var_len_obj.first_granule;\n\n        while !next_granule.is_var_len_null() {\n            // SAFETY: Per caller contract, `first_granule` points to a valid granule or is NULL.\n            // We know however at this point that it isn't NULL so it is valid.\n            // Thus the successor is too a valid granule or NULL.\n            // However, again, at this point we know that the successor isn't NULL.\n            // It follows then by induction that any `next_granule` at this point is valid.\n            // Thus we have fulfilled the requirement that `next_granule` points to a valid granule.\n            let header = unsafe { self.get_granule_ref(next_granule) }.header;\n            // SAFETY: `next_granule` still points to a valid granule per above.\n            unsafe {\n                self.free_granule(next_granule);\n            }\n            next_granule = header.next();\n        }\n    }\n\n    /// Frees an entire var-len linked-list object.\n    ///\n    /// SAFETY: `var_len_obj.first_granule` must point to a valid [`VarLenGranule`] or be NULL.\n    unsafe fn free_object(&mut self, var_len_obj: VarLenRef, blob_store: &mut dyn BlobStore) -> BlobNumBytes {\n        let mut blob_store_deleted_bytes = BlobNumBytes::default();\n        // For large blob objects, extract the hash and tell `blob_store` to discard it.\n        if var_len_obj.is_large_blob() {\n            // SAFETY: `var_len_obj.first_granule` was promised to\n            // point to a valid [`VarLenGranule`] or be NULL, as required.\n            unsafe {\n                blob_store_deleted_bytes = self.free_blob(var_len_obj.first_granule, blob_store);\n            }\n        }\n\n        // SAFETY: `free_object_ignore_blob` has the same safety contract as this method.\n        unsafe {\n            self.free_object_ignore_blob(var_len_obj);\n        }\n\n        blob_store_deleted_bytes\n    }\n}\n\n/// An iterator yielding the offsets to the granules of a var-len object.\npub struct GranuleOffsetIter<'vv, 'page> {\n    /// Our mutable view of the page.\n    var_view: &'vv mut VarView<'page>,\n    /// The offset, that will be yielded next, pointing to next granule.\n    next_granule: PageOffset,\n}\n\nimpl GranuleOffsetIter<'_, '_> {\n    /// Returns a mutable view of, for the `granule` at `offset`, `granule.data[start..]`.\n    ///\n    /// # Safety\n    ///\n    /// - `offset` must point to a valid granule\n    /// - `start < VarLenGranule::DATA_SIZE`\n    pub unsafe fn get_mut_data(&mut self, offset: PageOffset, start: usize) -> &mut Bytes {\n        // SAFETY: Caller promised that `offset` points o a valid granule.\n        let granule: &mut VarLenGranule = unsafe { get_mut(self.var_view.var_row_data, offset) };\n        // SAFETY: Caller promised `start < granule.data.len()`.\n        unsafe { granule.data.as_mut_slice().get_unchecked_mut(start..) }\n    }\n}\n\nimpl Iterator for GranuleOffsetIter<'_, '_> {\n    type Item = PageOffset;\n    fn next(&mut self) -> Option<Self::Item> {\n        let adjust = self.var_view.adjuster();\n\n        if self.next_granule.is_var_len_null() {\n            return None;\n        }\n        let ret = adjust(self.next_granule);\n        // SAFETY: By construction,\n        // the initial `next_granule` was promised to either be `NULL` or point to a valid granule.\n        // For a given granule, the same applies to its `.next()` granule.\n        // At this point, we've excluded `NULL`,\n        // so we know inductively that `next_granule` points to a valid granule, as required.\n        let granule: &VarLenGranule = unsafe { get_ref(self.var_view.var_row_data, ret) };\n        self.next_granule = granule.header.next();\n\n        Some(ret)\n    }\n}\n\n/// Assert that `ptr` is sufficiently aligned to reference a value of `T`.\n///\n/// In release mode, this is a no-op.\nfn assert_alignment<T>(ptr: *const Byte) {\n    debug_assert_eq!(\n        ptr as usize % mem::align_of::<T>(),\n        0,\n        \"Wanted a PageOffset with align 0x{:x} (for {}) but found 0x{:x}\",\n        mem::align_of::<T>(),\n        std::any::type_name::<T>(),\n        ptr as usize,\n    );\n}\n\n/// Returns a reference to the [`T`] pointed to at by `offset`.\n///\n/// # Safety\n///\n/// `offset` must point to a valid `T` in `row_data`.\n#[inline]\npub unsafe fn get_ref<T>(row_data: &Bytes, offset: PageOffset) -> &T {\n    // SAFETY: Caller promised that `offset` is in bounds of `row_data`.\n    let ptr = unsafe { offset_to_ptr(row_data, offset) };\n    assert_alignment::<T>(ptr);\n    let ptr = ptr.cast::<T>();\n    // SAFETY: Caller promised that `offset` points to a `T` in `row_data`.\n    unsafe { &*ptr }\n}\n\n/// Returns a mutable reference to the [`T`] pointed to at by `offset`.\n///\n/// # Safety\n///\n/// `offset` must point to a valid `T` in `row_data`.\n#[inline]\nunsafe fn get_mut<T>(row_data: &mut Bytes, offset: PageOffset) -> &mut T {\n    // SAFETY: Caller promised that `offset` is in bounds of `row_data`.\n    let ptr = unsafe { offset_to_ptr_mut(row_data, offset) };\n    assert_alignment::<T>(ptr as *const Byte);\n    let ptr = ptr.cast::<T>();\n    // SAFETY: Caller promised that `offset` points to a `T` in `row_data`.\n    unsafe { &mut *ptr }\n}\n\n/// Returns a raw const pointer into the `row_data` at `offset` bytes.\n///\n/// # Safety\n///\n/// `offset` must be in bounds or one past end of `row_data`.\n#[inline]\nunsafe fn offset_to_ptr(row_data: &Bytes, offset: PageOffset) -> *const Byte {\n    debug_assert!(offset.idx() <= row_data.len());\n\n    // SAFETY: per caller contract, `offset` is in bounds or one past end of `row_data`.\n    unsafe { row_data.as_ptr().add(offset.idx()) }\n}\n\n/// Returns a raw mutable pointer into the `row_data` at `offset` bytes.\n///\n/// SAFETY: `offset` must be in bounds or one past end of `row_data`.\n#[inline]\nunsafe fn offset_to_ptr_mut(row_data: &mut Bytes, offset: PageOffset) -> *mut Byte {\n    debug_assert!(offset.idx() <= row_data.len());\n\n    // SAFETY: per caller contract, `offset` is in bounds or one past end of `row_data`.\n    unsafe { row_data.as_mut_ptr().add(offset.idx()) }\n}\n\n/// Returns the size of the gap,\n/// assuming `first_var` is the high water mark (HWM) of the var-len section,\n/// pointing *at* the granule with the lowest offset,\n/// and `last_fixed` is the HWM of the fixed-len section,\n/// pointing *one past the end* of the last fixed row.\n#[inline]\nfn gap_remaining_size(first_var: PageOffset, last_fixed: PageOffset) -> Size {\n    // For illustration, suppose `row_data` is 10 bytes, i.e., `[Byte; 10]`.\n    // Let's assume the following setup with a full page,\n    // where capital letters are fixed rows and lower case are variable.\n    //\n    // [ A, B, C, D, E, f, g, h, i, j ]\n    //                  ^\n    //               first_var\n    //                  ^\n    //               last_fixed\n    //\n    // The high water mark `first_var` points *at* the granule with the lowest offset (`f`).\n    // Whereas `last_fixed` points *one past the end* (`f`) of the last fixed row (`E`)\n    //\n    // This is the case we have to consider in terms of possible underflow.\n    // As both HWMs would point at the same place,\n    // the result would be `0`, and no underflow occurs.\n    Size((first_var - last_fixed).0)\n}\n\n/// Returns whether the remaining gap is large enough to host an object `fixed_row_size` large,\n/// assuming `first_var` is the high water mark (HWM) of the var-len section,\n/// pointing *at* the granule with the lowest offset,\n/// and `last_fixed` is the HWM of the fixed-len section,\n/// pointing *one past the end* of the last fixed row.\n#[inline]\nfn gap_enough_size_for_row(first_var: PageOffset, last_fixed: PageOffset, fixed_row_size: Size) -> bool {\n    gap_remaining_size(first_var, last_fixed) >= fixed_row_size\n}\n\nimpl Page {\n    /// Returns a new page allocated on the heap.\n    ///\n    /// The new page supports a rows with `fixed_row_size`.\n    pub fn new(fixed_row_size: Size) -> Box<Self> {\n        Self::new_with_max_row_count(max_rows_in_page(fixed_row_size))\n    }\n\n    /// Returns a new page allocated on the heap.\n    ///\n    /// The new page supports `max_rows_in_page` at most.\n    pub fn new_with_max_row_count(max_rows_in_page: usize) -> Box<Self> {\n        // TODO(perf): mmap? allocator may do so already.\n        // mmap may be more efficient as we save allocator metadata.\n        use std::alloc::{alloc_zeroed, handle_alloc_error, Layout};\n\n        let layout = Layout::new::<Page>();\n\n        // Allocate with `alloc_zeroed` so that the bytes are initially 0, rather than uninit.\n        // We will never write an uninit byte into the page except in the `PageHeader`,\n        // so it is safe for `row_data` to have type `[u8; _]` rather than `[MaybeUninit<u8>; _]`.\n        // `alloc_zeroed` may be more efficient than `alloc` + `memset`;\n        // in particular, it may `mmap` pages directly from the OS, which are always zeroed for security reasons.\n        // TODO: use Box::new_zeroed() once stabilized.\n        // SAFETY: The layout's size is non-zero.\n        let raw: *mut Page = unsafe { alloc_zeroed(layout) }.cast();\n\n        if raw.is_null() {\n            handle_alloc_error(layout);\n        }\n\n        // We need to initialize `Page::header`\n        // without materializing a `&mut` as that is instant UB.\n        // SAFETY: `raw` isn't NULL.\n        let header = unsafe { &raw mut (*raw).header };\n\n        // SAFETY: `header` is valid for writes as only we have exclusive access.\n        //          The pointer is also aligned.\n        unsafe { header.write(PageHeader::new(max_rows_in_page)) };\n\n        // SAFETY: We used the global allocator with a layout for `Page`.\n        //         We have initialized the `header`,\n        //         and the `row_bytes` are initially 0 by `alloc_zeroed`,\n        //         making the pointee a `Page` valid for reads and writes.\n        unsafe { Box::from_raw(raw) }\n    }\n\n    /// Returns the number of rows stored in this page.\n    ///\n    /// This method runs in constant time.\n    pub fn num_rows(&self) -> usize {\n        self.header.fixed.num_rows as usize\n    }\n\n    #[cfg(test)]\n    /// Use this page's present rows bitvec to compute the number of present rows.\n    ///\n    /// This can be compared with [`Self::num_rows`] as a consistency check during tests.\n    pub fn reconstruct_num_rows(&self) -> usize {\n        // If we cared, we could rewrite this to `u64::count_ones` on each block of the bitset.\n        // We do not care. This method is slow.\n        self.header.fixed.present_rows.iter_set().count()\n    }\n\n    /// Returns the number of var-len granules allocated in this page.\n    ///\n    /// This method runs in constant time.\n    pub fn num_var_len_granules(&self) -> usize {\n        self.header.var.num_granules as usize\n    }\n\n    #[cfg(test)]\n    /// # Safety\n    ///\n    /// - `var_len_visitor` must be a valid [`VarLenMembers`] visitor\n    ///   specialized to the type and layout of rows within this [`Page`].\n    /// - `fixed_row_size` must be exactly the length in bytes of fixed rows in this page,\n    ///   which must further be the length of rows expected by the `var_len_visitor`.\n    pub unsafe fn reconstruct_num_var_len_granules(\n        &self,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n    ) -> usize {\n        self.iter_fixed_len(fixed_row_size)\n            .flat_map(|row| unsafe {\n                // Safety: `row` came out of `iter_fixed_len`,\n                // which, due to caller requirements on `fixed_row_size`,\n                // is giving us valid, aligned, initialized rows of the row type.\n                var_len_visitor.visit_var_len(self.get_row_data(row, fixed_row_size))\n            })\n            .flat_map(|var_len_obj| unsafe {\n                // Safety: We believe `row` to be valid\n                // and `var_len_visitor` to be correctly visiting its var-len members.\n                // Therefore, `var_len_obj` is a valid var-len object.\n                self.iter_var_len_object(var_len_obj.first_granule)\n            })\n            .count()\n    }\n\n    /// Returns the number of bytes used by rows stored in this page.\n    ///\n    /// This is necessarily an overestimate of live data bytes, as it includes:\n    /// - Padding bytes within the fixed-length portion of the rows.\n    /// - [`VarLenRef`] pointer-like portions of rows.\n    /// - Unused trailing parts of partially-filled [`VarLenGranule`]s.\n    /// - [`VarLenGranule`]s used to store [`BlobHash`]es.\n    ///\n    /// Note that large blobs themselves are not counted.\n    /// The caller should obtain a count of the bytes used by large blobs\n    /// from the [`super::blob_store::BlobStore`].\n    ///\n    /// This method runs in constant time.\n    pub fn bytes_used_by_rows(&self, fixed_row_size: Size) -> usize {\n        let fixed_row_bytes = self.num_rows() * fixed_row_size.len();\n        let var_len_bytes = self.num_var_len_granules() * VarLenGranule::SIZE.len();\n        fixed_row_bytes + var_len_bytes\n    }\n\n    #[cfg(test)]\n    /// # Safety\n    ///\n    /// - `var_len_visitor` must be a valid [`VarLenMembers`] visitor\n    ///   specialized to the type and layout of rows within this [`Page`].\n    /// - `fixed_row_size` must be exactly the length in bytes of fixed rows in this page,\n    ///   which must further be the length of rows expected by the `var_len_visitor`.\n    pub unsafe fn reconstruct_bytes_used_by_rows(\n        &self,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n    ) -> usize {\n        let fixed_row_bytes = self.reconstruct_num_rows() * fixed_row_size.len();\n        let var_len_bytes = unsafe { self.reconstruct_num_var_len_granules(fixed_row_size, var_len_visitor) }\n            * VarLenGranule::SIZE.len();\n        fixed_row_bytes + var_len_bytes\n    }\n\n    /// Returns the range of row data starting at `offset` and lasting `size` bytes.\n    pub fn get_row_data(&self, row: PageOffset, size: Size) -> &Bytes {\n        &self.row_data[row.range(size)]\n    }\n\n    /// Returns whether the row at `offset` is present or not.\n    pub fn has_row_offset(&self, fixed_row_size: Size, offset: PageOffset) -> bool {\n        // Check that the `offset` is properly aligned for a row of size `fixed_row_size`.\n        // This cannot be `debug_assert!` as the caller could rely on this\n        // reporting properly whether `offset` is at a row boundary or not.\n        assert_eq!(offset.idx() % fixed_row_size.len(), 0);\n\n        self.header.fixed.is_row_present(offset, fixed_row_size)\n    }\n\n    /// Returns split mutable views of this page over the fixed and variable sections.\n    pub fn split_fixed_var_mut(&mut self) -> (FixedView<'_>, VarView<'_>) {\n        // The fixed HWM (`fixed.last`) points *one past the end* of the fixed section\n        // which is exactly what we want for `split_at_mut`.\n        let last_fixed = self.header.fixed.last;\n        let (fixed_row_data, var_row_data) = self.row_data.split_at_mut(last_fixed.idx());\n\n        // Construct the fixed-len view.\n        let fixed = FixedView {\n            fixed_row_data,\n            header: &mut self.header.fixed,\n        };\n\n        // Construct the var-len view.\n        let var = VarView {\n            var_row_data,\n            header: &mut self.header.var,\n            last_fixed,\n        };\n\n        (fixed, var)\n    }\n\n    /// Returns a mutable view of the row from `start` lasting `fixed_row_size` number of bytes.\n    ///\n    /// This method is safe, but callers should take care that `start` and `fixed_row_size`\n    /// are correct for this page, and that `start` is aligned.\n    /// Callers should further ensure that mutations to the row leave the row bytes\n    /// in an expected state, i.e. initialized where required by the row type,\n    /// and with `VarLenRef`s that point to valid granules and with correct lengths.\n    ///\n    /// This call will clear the unmodified hash\n    /// as it is expected that the caller will alter the the page.\n    pub fn get_fixed_row_data_mut(&mut self, start: PageOffset, fixed_row_size: Size) -> &mut Bytes {\n        self.header.unmodified_hash = None;\n        &mut self.row_data[start.range(fixed_row_size)]\n    }\n\n    /// Return the total required var-len granules to store `objects`.\n    pub fn total_granules_required_for_objects(objects: &[impl AsRef<[u8]>]) -> usize {\n        objects\n            .iter()\n            .map(|obj| VarLenGranule::bytes_to_granules(obj.as_ref().len()).0)\n            .sum()\n    }\n\n    /// Does the page have space to store a row,\n    /// where the fixed size part is `fixed_row_size` bytes large,\n    /// and the row has the given `var_len_objects`?\n    pub fn has_space_for_row_with_objects(&self, fixed_row_size: Size, var_len_objects: &[impl AsRef<[u8]>]) -> bool {\n        let num_granules_required = Self::total_granules_required_for_objects(var_len_objects);\n        self.has_space_for_row(fixed_row_size, num_granules_required)\n    }\n\n    /// Does the page have space to store a row,\n    /// where the fixed size part is `fixed_row_size` bytes large,\n    /// and the variable part requires `num_granules`.\n    pub fn has_space_for_row(&self, fixed_row_size: Size, num_granules: usize) -> bool {\n        let has_fixed_free = self.header.fixed.next_free.has();\n        let var_first = self.header.var.first;\n        let fixed_last = self.header.fixed.last;\n\n        if num_granules == 0 {\n            // No granules needed. Just verify that there's space for the fixed part.\n            return has_fixed_free || gap_enough_size_for_row(var_first, fixed_last, fixed_row_size);\n        }\n\n        // Determine the gap remaining after allocating for the fixed part.\n        let gap_remaining = gap_remaining_size(var_first, fixed_last);\n        let gap_avail_for_granules = if has_fixed_free {\n            // If we have a free fixed length block, then we can use the whole gap for var-len granules.\n            gap_remaining\n        } else {\n            // If we need to grow the fixed-length store into the gap,\n            if gap_remaining < fixed_row_size {\n                // If the gap is too small for fixed-length row, fail.\n                return false;\n            }\n\n            // Otherwise, the space available in the gap for var-len granules\n            // is the current gap size less the fixed-len row size.\n            gap_remaining - fixed_row_size\n        };\n\n        // Convert the gap size to granules.\n        let gap_in_granules = VarLenGranule::space_to_granules(gap_avail_for_granules);\n        // Account for granules available in the freelist.\n        let needed_granules_after_freelist = num_granules.saturating_sub(self.header.var.freelist_len as usize);\n\n        gap_in_granules >= needed_granules_after_freelist\n    }\n\n    /// Returns whether the row is full with respect to storing a fixed row with `fixed_row_size`\n    /// and no variable component.\n    pub fn is_full(&self, fixed_row_size: Size) -> bool {\n        !self.has_space_for_row(fixed_row_size, 0)\n    }\n\n    /// Will leave partially-allocated chunks if fails prematurely,\n    /// so always check `Self::has_space_for_row` before calling.\n    ///\n    /// This method is provided for testing the page store directly;\n    /// higher-level codepaths are expected to use [`crate::bflatn::ser::write_av_to_page`],\n    /// which performs similar operations to this method,\n    /// but handles rollback on failure appropriately.\n    ///\n    /// This function will never fail if `Self::has_space_for_row` has returned true.\n    ///\n    /// # Safety\n    ///\n    /// - `var_len_visitor` is suitable for visiting var-len refs in `fixed_row`.\n    ///\n    /// - `fixed_row.len()` must be consistent with `var_len_visitor` and `self`.\n    ///   That is, `VarLenMembers` must be specialized for a row type with that length,\n    ///   and all past, present, and future fixed-length rows stored in this `Page`\n    ///   must also be of that length.\n    pub unsafe fn insert_row(\n        &mut self,\n        fixed_row: &Bytes,\n        var_len_objects: &[impl AsRef<[u8]>],\n        var_len_visitor: &impl VarLenMembers,\n        blob_store: &mut dyn BlobStore,\n    ) -> Result<PageOffset, Error> {\n        // Allocate the fixed-len row.\n        let fixed_row_size = Size(fixed_row.len() as u16);\n\n        // SAFETY: Caller promised that `fixed_row.len()` uses the right `fixed_row_size`\n        // and we trust that others have too.\n        let fixed_len_offset = unsafe { self.alloc_fixed_len(fixed_row_size)? };\n\n        // Store the fixed-len row.\n        let (mut fixed, mut var) = self.split_fixed_var_mut();\n        let row = fixed.get_row_mut(fixed_len_offset, fixed_row_size);\n        row.copy_from_slice(fixed_row);\n\n        // Store all var-len refs into their appropriate slots in the fixed-len row.\n        // SAFETY:\n        // - The `fixed_len_offset` given by `alloc_fixed_len` results in `row`\n        //   being properly aligned for the row type.\n        // - Caller promised that `fixed_row.len()` matches the row type size exactly.\n        // - `var_len_visitor` is suitable for `fixed_row`.\n        let vlr_slot_iter = unsafe { var_len_visitor.visit_var_len_mut(row) };\n        for (var_len_ref_slot, var_len_obj) in vlr_slot_iter.zip(var_len_objects) {\n            let (var_len_ref, in_blob) = var.alloc_for_slice(var_len_obj.as_ref())?;\n            if in_blob {\n                // The blob store insertion will never fail.\n                // SAFETY: `alloc_for_slice` always returns a pointer\n                // to a `VarLenGranule` in bounds of this page.\n                // As `in_blob` holds, it is also unused, as required.\n                // We'll now make that granule valid.\n                unsafe {\n                    var.write_large_blob_hash_to_granule(blob_store, var_len_obj, var_len_ref);\n                }\n            }\n            *var_len_ref_slot = var_len_ref;\n        }\n\n        Ok(fixed_len_offset)\n    }\n\n    /// Allocates space for a fixed size row of `fixed_row_size` bytes.\n    ///\n    /// # Safety\n    ///\n    /// `fixed_row_size` must be equal to the value passed\n    /// to all other methods ever invoked on `self`.\n    pub unsafe fn alloc_fixed_len(&mut self, fixed_row_size: Size) -> Result<PageOffset, Error> {\n        self.alloc_fixed_len_from_freelist(fixed_row_size)\n            .or_else(|| self.alloc_fixed_len_from_gap(fixed_row_size))\n            .ok_or(Error::InsufficientFixedLenSpace { need: fixed_row_size })\n    }\n\n    /// Allocates a space for a fixed size row of `fixed_row_size` in the freelist, if possible.\n    ///\n    /// This call will clear the unmodified hash.\n    #[inline]\n    fn alloc_fixed_len_from_freelist(&mut self, fixed_row_size: Size) -> Option<PageOffset> {\n        let header = &mut self.header.fixed;\n        // SAFETY: `header.next_free` points to a `FreeCellRef` when the former `.has()`.\n        let free = unsafe { header.next_free.take_freelist_head(&self.row_data, |x| x) }?;\n        header.set_row_present(free, fixed_row_size);\n\n        // We are and have modified the page, so clear the unmodified hash.\n        self.header.unmodified_hash = None;\n\n        Some(free)\n    }\n\n    /// Allocates a space for a fixed size row of `fixed_row_size` in the freelist, if possible.\n    ///\n    /// This call will clear the unmodified hash.\n    #[inline]\n    fn alloc_fixed_len_from_gap(&mut self, fixed_row_size: Size) -> Option<PageOffset> {\n        if gap_enough_size_for_row(self.header.var.first, self.header.fixed.last, fixed_row_size) {\n            // We're modifying the page, so clear the unmodified hash.\n            self.header.unmodified_hash = None;\n\n            // Enough space in the gap; move the high water mark and return the old HWM.\n            // `fixed.last` points *after* the highest-indexed fixed-len row,\n            // so post-increment.\n            let ptr = self.header.fixed.last;\n            self.header.fixed.last += fixed_row_size;\n            self.header.fixed.set_row_present(ptr, fixed_row_size);\n            Some(ptr)\n        } else {\n            // Not enough space in the gap for another row!\n            None\n        }\n    }\n\n    /// Returns an iterator over all the [`PageOffset`]s of the fixed rows in this page\n    /// beginning with `starting_from`.\n    ///\n    /// The rows are assumed to be `fixed_row_size` bytes long\n    /// and `starting_from` is assumed to be at a valid starting `PageOffset` for a fixed row.\n    ///\n    /// NOTE: This method is not `unsafe` as it cannot trigger UB.\n    /// However, when provided with garbage input, it will return garbage back.\n    /// It is the caller's responsibility to ensure that `PageOffset`s derived from\n    /// this iterator are valid when used to do anything `unsafe`.\n    fn iter_fixed_len_from(&self, fixed_row_size: Size, starting_from: PageOffset) -> FixedLenRowsIter<'_> {\n        let idx = starting_from / fixed_row_size;\n        FixedLenRowsIter {\n            idx_iter: self.header.fixed.present_rows.iter_set_from(idx),\n            fixed_row_size,\n        }\n    }\n\n    /// Returns an iterator over all the [`PageOffset`]s of the fixed rows in this page.\n    ///\n    /// The rows are assumed to be `fixed_row_size` bytes long.\n    ///\n    /// NOTE: This method is not `unsafe` as it cannot trigger UB.\n    /// However, when provided with garbage input, it will return garbage back.\n    /// It is the caller's responsibility to ensure that `PageOffset`s derived from\n    /// this iterator are valid when used to do anything `unsafe`.\n    pub fn iter_fixed_len(&self, fixed_row_size: Size) -> FixedLenRowsIter<'_> {\n        FixedLenRowsIter {\n            idx_iter: self.header.fixed.present_rows.iter_set(),\n            fixed_row_size,\n        }\n    }\n\n    /// Returns an iterator over all the `VarLenGranule`s of the var-len object\n    /// that has its first granule at offset `first_granule`.\n    /// An empty iterator will be returned when `first_granule` is `NULL`.\n    ///\n    /// # Safety\n    ///\n    /// `first_granule` must be an offset to a valid granule or `NULL`.\n    pub unsafe fn iter_var_len_object(\n        &self,\n        first_granule: PageOffset,\n    ) -> impl Clone + Iterator<Item = &VarLenGranule> {\n        VarLenGranulesIter {\n            page: self,\n            next_granule: first_granule,\n        }\n    }\n\n    /// Returns an iterator over the data of all the `VarLenGranule`s of the var-len object\n    /// that has its first granule at offset `first_granule`.\n    /// An empty iterator will be returned when `first_granule` is `NULL`.\n    ///\n    /// # Safety\n    ///\n    /// `first_granule` must be an offset to a valid granule or `NULL`.\n    pub unsafe fn iter_vlo_data(&self, first_granule: PageOffset) -> impl '_ + Clone + Iterator<Item = &[u8]> {\n        // SAFETY: Caller and callee have the exact same safety requirements.\n        unsafe { self.iter_var_len_object(first_granule) }.map(|g| g.data())\n    }\n\n    /// Free a row, marking its fixed-len and var-len storage granules as available for re-use.\n    ///\n    /// This call will clear the unmodified hash.\n    ///\n    /// # Safety\n    ///\n    /// - `fixed_row` must point to a valid row in this page.\n    ///\n    /// - `fixed_row_size` must be the size in bytes of the fixed part\n    ///   of all past, present, and future rows in this page and future rows in this page.\n    ///\n    /// - The `var_len_visitor` must visit the same set of `VarLenRef`s in the row\n    ///   as the visitor provided to `insert_row`.\n    pub unsafe fn delete_row(\n        &mut self,\n        fixed_row: PageOffset,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n        blob_store: &mut dyn BlobStore,\n    ) -> BlobNumBytes {\n        // We're modifying the page, so clear the unmodified hash.\n        self.header.unmodified_hash = None;\n\n        let (mut fixed, mut var) = self.split_fixed_var_mut();\n\n        let mut blob_store_deleted_bytes = BlobNumBytes::default();\n\n        // Visit the var-len members of the fixed row and free them.\n        let row = fixed.get_row(fixed_row, fixed_row_size);\n        // SAFETY: `row` is derived from `fixed_row`, which is known by caller requirements to be valid.\n        let var_len_refs = unsafe { var_len_visitor.visit_var_len(row) };\n        for var_len_ref in var_len_refs {\n            // SAFETY: A sound call to `visit_var_len` on a fully initialized valid row,\n            // which we've justified that the above is,\n            // returns an iterator, that will only yield `var_len_ref`s,\n            // where `var_len_ref.first_granule` points to a valid `VarLenGranule` or is NULL.\n            blob_store_deleted_bytes += unsafe { var.free_object(*var_len_ref, blob_store) }\n        }\n\n        // SAFETY: Caller promised that `fixed_row` points to a valid row in the page.\n        // Thus, `range_move(0..fixed_row_size, fixed_row)` is in bounds of `row_data`.\n        // Moreover, this entails that it is valid for writing a `FreeCellRef`\n        // to the beginning or entire range, as any row can at least hold a `FreeCellRef`\n        // and will be properly aligned for it as well.\n        unsafe {\n            fixed.free(fixed_row, fixed_row_size);\n        }\n\n        blob_store_deleted_bytes\n    }\n\n    /// Returns the total number of granules used by the fixed row at `fixed_row_offset`\n    /// and lasting `fixed_row_size` bytes where `var_len_visitor` is used to find\n    /// the [`VarLenRef`]s in the fixed row.\n    ///\n    /// # Safety\n    ///\n    /// - `fixed_row_offset` must refer to a previously-allocated and initialized row in `self`,\n    ///   and must not have been de-allocated. In other words, the fixed row must be *valid*.\n    ///\n    /// - `fixed_row_size` and `var_len_visitor` must be consistent with each other\n    ///   and with all other calls to any methods on `self`.\n    pub unsafe fn row_total_granules(\n        &self,\n        fixed_row_offset: PageOffset,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n    ) -> usize {\n        let fixed_row = self.get_row_data(fixed_row_offset, fixed_row_size);\n        // SAFETY:\n        // - Caller promised that `fixed_row_offset` is a valid row.\n        // - Caller promised consistency of `var_len_visitor` wrt. `fixed_row_size` and this page.\n        let vlr_iter = unsafe { var_len_visitor.visit_var_len(fixed_row) };\n        vlr_iter.copied().map(|slot| slot.granules_used()).sum()\n    }\n\n    /// Copy as many rows from `self` for which `filter` returns `true` into `dst` as will fit,\n    /// starting from `starting_from`.\n    ///\n    /// If less than the entirety of `self` could be processed, return `Continue(resume_point)`,\n    /// where `resume_point` is the `starting_from` argument of a subsequent call to `copy_filter_into`\n    /// that will complete the iteration.\n    /// `dst` should be assumed to be full in this case,\n    /// as it does not contain enough free space to store the row of `self` at `resume_point`.\n    ///\n    /// If the entirety of `self` is processed, return `Break`.\n    /// `dst` may or may not be full in this case, but is likely not full.\n    ///\n    /// # Safety\n    ///\n    /// The `var_len_visitor` must visit the same set of `VarLenRef`s in the row\n    /// as the visitor provided to all other methods on `self` and `dst`.\n    ///\n    /// The `fixed_row_size` must be consistent with the `var_len_visitor`,\n    /// and be equal to the value provided to all other methods on `self` and `dst`.\n    ///\n    /// The `starting_from` offset must point to a valid starting offset\n    /// consistent with `fixed_row_size`.\n    /// That is, it must not point into the middle of a row.\n    pub unsafe fn copy_filter_into(\n        &self,\n        starting_from: PageOffset,\n        dst: &mut Page,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n        mut blob_policy: Option<&mut impl FnMut(BlobHash)>,\n        mut filter: impl FnMut(&Page, PageOffset) -> bool,\n    ) -> ControlFlow<(), PageOffset> {\n        for row_offset in self\n            .iter_fixed_len_from(fixed_row_size, starting_from)\n            // Only copy rows satisfying the predicate `filter`.\n            .filter(|o| filter(self, *o))\n        {\n            let blob_policy = blob_policy.as_mut();\n            // SAFETY:\n            // - `starting_from` points to a valid row and thus `row_offset` also does.\n            // - `var_len_visitor` will visit the right `VarLenRef`s and is consistent with other calls.\n            // - `fixed_row_size` is consistent with `var_len_visitor` and `self`.\n            if !unsafe { self.copy_row_into(row_offset, dst, fixed_row_size, var_len_visitor, blob_policy) } {\n                // Target doesn't have enough space for row;\n                // stop here and return the offset of the uncopied row\n                // so a later call to `copy_filter_into` can start there.\n                return ControlFlow::Continue(row_offset);\n            }\n        }\n\n        // The `for` loop completed.\n        // We successfully copied the entire page of `self` into `target`.\n        // The caller doesn't need to resume from this offset.\n        ControlFlow::Break(())\n    }\n\n    /// Copies the row at `row_offset` from `self` into `dst`\n    /// or returns `false` otherwise if `dst` has no space for the row.\n    ///\n    /// # Safety\n    ///\n    /// - `row_offset` offset must point to a valid row.\n    ///\n    /// - `var_len_visitor` must visit the same set of `VarLenRef`s in the row\n    ///   as the visitor provided to all other methods on `self` and `dst`.\n    ///\n    /// - `fixed_row_size` must be consistent with the `var_len_visitor`,\n    ///   and be equal to the value provided to all other methods on `self` and `dst`.\n    unsafe fn copy_row_into(\n        &self,\n        row_offset: PageOffset,\n        dst: &mut Page,\n        fixed_row_size: Size,\n        var_len_visitor: &impl VarLenMembers,\n        mut blob_policy: Option<&mut impl FnMut(BlobHash)>,\n    ) -> bool {\n        // SAFETY: Caller promised that `starting_from` points to a valid row\n        // consistent with `fixed_row_size` which was also\n        // claimed to be consistent with `var_len_visitor` and `self`.\n        let required_granules = unsafe { self.row_total_granules(row_offset, fixed_row_size, var_len_visitor) };\n        if !dst.has_space_for_row(fixed_row_size, required_granules) {\n            // Target doesn't have enough space for row.\n            return false;\n        };\n\n        let src_row = self.get_row_data(row_offset, fixed_row_size);\n\n        // Allocate for the fixed-len data.\n        // SAFETY: forward our requirement on `fixed_row_size` to `alloc_fixed_len`.\n        let inserted_offset = unsafe { dst.alloc_fixed_len(fixed_row_size) }\n            .expect(\"Failed to allocate fixed-len row in dst page after checking for available space\");\n\n        // Copy all fixed-len data. We'll overwrite the var-len parts next.\n        let (mut dst_fixed, mut dst_var) = dst.split_fixed_var_mut();\n        let dst_row = dst_fixed.get_row_mut(inserted_offset, fixed_row_size);\n        dst_row.copy_from_slice(src_row);\n\n        // Copy var-len members into target.\n        // Fixup `VarLenRef`s in `dst_row` to point to the copied var-len objects.\n        //\n        // SAFETY: `src_row` is valid because it came from `self.iter_fixed_len_from`.\n        //\n        //         Forward our safety requirements re: `var_len_visitor` to `visit_var_len`.\n        let src_vlr_iter = unsafe { var_len_visitor.visit_var_len(src_row) };\n        // SAFETY: forward our requirement on `var_len_visitor` to `visit_var_len_mut`.\n        let target_vlr_iter = unsafe { var_len_visitor.visit_var_len_mut(dst_row) };\n        for (src_vlr, target_vlr_slot) in src_vlr_iter.zip(target_vlr_iter) {\n            let blob_policy = blob_policy.as_mut();\n            // SAFETY:\n            //\n            // - requirements of `visit_var_len_assume_init` were met,\n            //   so we can assume that `src_vlr.first_granule` points to a valid granule or is NULL.\n            //\n            // - the call to `dst.has_space_for_row` above ensures\n            //   that the allocation will not fail part-way through.\n            let target_vlr_fixup = unsafe { self.copy_var_len_into(*src_vlr, &mut dst_var, blob_policy) }\n                .expect(\"Failed to allocate var-len object in dst page after checking for available space\");\n\n            *target_vlr_slot = target_vlr_fixup;\n        }\n\n        true\n    }\n\n    /// Copy a var-len object `src_vlr` from `self` into `dst_var`,\n    /// and return the `VarLenRef` to the copy in `dst_var`.\n    ///\n    /// If the `src_vlr` is empty,\n    /// i.e., has `first_granule.is_null()` and `length_in_bytes == 0`,\n    /// this will return `VarLenRef::NULL`.\n    ///\n    /// # SAFETY:\n    ///\n    /// - `src_vlr.first_granule` must point to a valid granule or be NULL.\n    ///\n    /// - To avoid leaving dangling uninitialized allocations in `dst_var`,\n    ///   `dst_var` must already be checked to have enough size to store `src_vlr`\n    ///   using `Self::has_space_for_row`.\n    unsafe fn copy_var_len_into(\n        &self,\n        src_vlr: VarLenRef,\n        dst_var: &mut VarView<'_>,\n        blob_policy: Option<&mut impl FnMut(BlobHash)>,\n    ) -> Result<VarLenRef, Error> {\n        // SAFETY: Caller promised that `src_vlr.first_granule` points to a valid granule is be NULL.\n        let mut iter = unsafe { self.iter_var_len_object(src_vlr.first_granule) };\n\n        // If the `src_vlr` is empty, don't copy anything, and return null.\n        let Some(mut src_chunk) = iter.next() else {\n            debug_assert!(src_vlr.length_in_bytes == 0);\n            return Ok(VarLenRef::NULL);\n        };\n        let mut dst_chunk = dst_var.alloc_granule()?;\n\n        let copied_head = dst_chunk;\n\n        // Weird-looking iterator so we can put the next-pointer into `copied_chunk`.\n        for next_src_chunk in iter {\n            // Allocate space for the next granule so we can initialize it in the next iteration.\n            let next_dst_chunk = dst_var.alloc_granule()?;\n            let data = src_chunk.data();\n            // Initialize `dst_chunk` with data and next-pointer.\n            //\n            // SAFETY:\n            // 1. `dst_chunk` is properly aligned as it came from `alloc_granule` either\n            //    before the loop or in the previous iteration.\n            //    This also ensures that both are in bounds\n            //    of the page for `granule + granule + VarLenGranule::SIZE`.\n            //\n            // 2. `next_dst_chunk` will be initialized\n            //    either in the next iteration or after the loop ends.\n            //\n            // 3. `dst_chunk` points to unused data as the space was allocated before the loop\n            //    or was `next_dst_chunk` in the previous iteration and hasn't been written to yet.\n            unsafe { dst_var.write_chunk_to_granule(data, data.len(), dst_chunk, next_dst_chunk) };\n            dst_chunk = next_dst_chunk;\n            src_chunk = next_src_chunk;\n        }\n\n        let data = src_chunk.data();\n        // The last granule has null as next-pointer.\n        //\n        // SAFETY:\n        // 1. `dst_chunk` is properly aligned as it came from `alloc_granule` either\n        //    before the loop or in the previous iteration.\n        //    This also ensures that both are in bounds\n        //    of the page for `granule + granule + VarLenGranule::SIZE`.\n        //\n        // 2. `next` is NULL which is trivially init.\n        //\n        // 3. `dst_chunk` points to unused data as the space was allocated before the loop\n        //    or was `next_dst_chunk` in the previous iteration and hasn't been written to yet.\n        unsafe { dst_var.write_chunk_to_granule(data, data.len(), dst_chunk, PageOffset::VAR_LEN_NULL) };\n\n        // For a large blob object, run the blob policy, if we have one.\n        // This could, for example:\n        // - increment the refcount in the blob store\n        // - clone the blob object and the bytes to a new blob store.\n        if let Some(blob_policy) = blob_policy.filter(|_| src_vlr.is_large_blob()) {\n            blob_policy(src_chunk.blob_hash());\n        }\n\n        Ok(VarLenRef {\n            first_granule: copied_head,\n            length_in_bytes: src_vlr.length_in_bytes,\n        })\n    }\n\n    /// Make `self` empty, removing all rows from it and resetting the high water marks to zero.\n    ///\n    /// This also clears the `unmodified_hash`.\n    pub fn clear(&mut self) {\n        self.header.clear();\n    }\n\n    /// Zeroes every byte of row data in this page.\n    ///\n    /// # Safety\n    ///\n    /// Causes the page header to no longer match the contents, invalidating many assumptions.\n    /// Should be called in conjunction with [`Self::clear`].\n    pub unsafe fn zero_data(&mut self) {\n        self.row_data.fill(0);\n    }\n\n    /// Resets this page for reuse of its allocation.\n    ///\n    /// The reset page supports `max_rows_in_page` at most.\n    pub fn reset_for(&mut self, max_rows_in_page: usize) {\n        self.header.reset_for(max_rows_in_page);\n\n        // NOTE(centril): We previously zeroed pages when resetting.\n        // This had an adverse performance impact.\n        // The reason why we previously zeroed was for security under a multi-tenant setup\n        // when exposing a module ABI that allows modules to memcpy whole pages over.\n        // However, we have no such ABI for the time being, so we can soundly avoid zeroing.\n        // If we ever decide to add such an ABI, we must start zeroing again.\n        //\n        // // SAFETY: We just reset the page header.\n        // unsafe { self.zero_data() };\n    }\n\n    /// Sets the header and the row data.\n    ///\n    /// # Safety\n    ///\n    /// The `header` and `row_data` must be consistent with each other.\n    pub(super) unsafe fn set_raw(&mut self, header: PageHeader, row_data: RowData) {\n        self.header = header;\n        self.row_data = row_data;\n    }\n\n    /// Returns the page header, for testing.\n    #[cfg(test)]\n    pub(super) fn page_header_for_test(&self) -> &PageHeader {\n        &self.header\n    }\n\n    /// Computes the content hash of this page.\n    pub fn content_hash(&self) -> blake3::Hash {\n        let mut hasher = blake3::Hasher::new();\n\n        // Hash the page contents.\n        hasher.update(&self.row_data);\n\n        // Hash the `FixedHeader`, first copy out the fixed part save for the bitset into an array.\n        let fixed = &self.header.fixed;\n        let mut fixed_bytes = [0u8; 6];\n        fixed_bytes[0..2].copy_from_slice(&fixed.next_free.next.0.to_le_bytes());\n        fixed_bytes[2..4].copy_from_slice(&fixed.last.0.to_le_bytes());\n        fixed_bytes[4..6].copy_from_slice(&fixed.num_rows.to_le_bytes());\n        hasher.update(&fixed_bytes);\n\n        // Hash the fixed bit set.\n        hasher.update(bytemuck::must_cast_slice(fixed.present_rows.storage()));\n\n        // Hash the `VarHeader`.\n        hasher.update(bytemuck::bytes_of(&self.header.var));\n\n        // We're done.\n        // Note that `unmodified_hash` itself must not be hashed to avoid a recursive dependency.\n        hasher.finalize()\n    }\n\n    /// Computes the content hash of this page and saves it to [`PageHeader::unmodified_hash`].\n    pub fn save_content_hash(&mut self) {\n        let hash = self.content_hash();\n        self.header.unmodified_hash = Some(hash);\n    }\n\n    /// Return the page's content hash, computing and saving it if it is not already stored.\n    pub fn save_or_get_content_hash(&mut self) -> blake3::Hash {\n        self.unmodified_hash().copied().unwrap_or_else(|| {\n            self.save_content_hash();\n            self.header.unmodified_hash.unwrap()\n        })\n    }\n\n    /// Returns the stored unmodified hash, if any.\n    pub fn unmodified_hash(&self) -> Option<&blake3::Hash> {\n        self.header.unmodified_hash.as_ref()\n    }\n}\n\n/// An iterator over the `PageOffset`s of all present fixed-length rows in a [`Page`].\npub struct FixedLenRowsIter<'page> {\n    /// The fixed header of the page,\n    /// used to determine where the last fixed row is\n    /// and whether the fixed row slot is actually a fixed row.\n    idx_iter: IterSet<'page>,\n    /// The size of a row in bytes.\n    fixed_row_size: Size,\n}\n\nimpl Iterator for FixedLenRowsIter<'_> {\n    type Item = PageOffset;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.idx_iter\n            .next()\n            .map(|idx| PageOffset(idx as u16 * self.fixed_row_size.0))\n    }\n}\n\n/// An iterator over the [`VarLenGranule`]s in a particular [`VarLenRef`] in `page`.\n///\n/// Constructing a `VarLenGranulesIter` should be considered unsafe\n/// because the initial `next_granule` must either be `NULL` or point to a valid [`VarLenGranule`].\n///\n/// Iterating over [`VarLenRef::NULL`] is safe and will immediately return `None`.\n#[derive(Clone, Copy)]\nstruct VarLenGranulesIter<'page> {\n    /// The page to yield granules from.\n    page: &'page Page,\n    /// Location of the next granule in `page`.\n    /// Must either be `NULL` or point to a valid granule.\n    next_granule: PageOffset,\n    // TODO(perf,bikeshedding): store length and implement `Iterator::size_hint`?\n}\n\nimpl<'page> Iterator for VarLenGranulesIter<'page> {\n    type Item = &'page VarLenGranule;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.next_granule.is_var_len_null() {\n            return None;\n        }\n\n        // SAFETY: By construction,\n        // the initial `next_granule` was promised to either be `NULL` or point to a valid granule.\n        // For a given granule, the same applies to its `.next()` granule.\n        // At this point, we've excluded `NULL`,\n        // so we know inductively that `next_granule` points to a valid granule, as required.\n        let granule: &VarLenGranule = unsafe { get_ref(&self.page.row_data, self.next_granule) };\n        self.next_granule = granule.header.next();\n\n        Some(granule)\n    }\n}\n\n#[cfg(test)]\npub(crate) mod tests {\n    use super::*;\n    use crate::{blob_store::NullBlobStore, page_pool::PagePool, var_len::AlignedVarLenOffsets};\n    use proptest::{collection::vec, prelude::*};\n    use spacetimedb_sats::bsatn;\n    use spacetimedb_sats::layout::row_size_for_type;\n\n    fn u64_row_size() -> Size {\n        let fixed_row_size = row_size_for_type::<u64>();\n        assert_eq!(fixed_row_size.len(), 8);\n        fixed_row_size\n    }\n\n    const U64_VL_VISITOR: AlignedVarLenOffsets<'_> = AlignedVarLenOffsets::from_offsets(&[]);\n    fn u64_var_len_visitor() -> &'static AlignedVarLenOffsets<'static> {\n        &U64_VL_VISITOR\n    }\n\n    fn insert_u64(page: &mut Page, val: u64) -> PageOffset {\n        let val_slice = val.to_le_bytes();\n        unsafe { page.insert_row(&val_slice, &[] as &[&[u8]], u64_var_len_visitor(), &mut NullBlobStore) }\n            .expect(\"Failed to insert first row\")\n    }\n\n    fn insert_u64_drop(page: &mut Page, val: u64) {\n        insert_u64(page, val);\n    }\n\n    fn read_u64(page: &Page, offset: PageOffset) -> u64 {\n        let row = page.get_row_data(offset, u64_row_size());\n        u64::from_le_bytes(row.try_into().unwrap())\n    }\n\n    fn data_sub_n_vlg(n: usize) -> usize {\n        PageOffset::PAGE_END.idx() - (VarLenGranule::SIZE * n).len()\n    }\n\n    pub(crate) fn hash_unmodified_save_get(page: &mut Page) -> blake3::Hash {\n        assert_eq!(page.header.unmodified_hash, None);\n        page.save_content_hash();\n        page.header.unmodified_hash.unwrap()\n    }\n\n    #[test]\n    fn insert_one_u64() {\n        let mut page = Page::new(u64_row_size());\n\n        // First the hash is not saved, so compute it.\n        let hash = hash_unmodified_save_get(&mut page);\n\n        let val: u64 = 0xa5a5_a5a5_a5a5_a5a5;\n\n        let offset = insert_u64(&mut page, val);\n\n        assert_eq!(offset.idx(), 0);\n\n        let row_val = read_u64(&page, offset);\n\n        assert_eq!(row_val, val);\n\n        // The hash should have been cleared.\n        assert_ne!(hash, hash_unmodified_save_get(&mut page));\n    }\n\n    fn insert_while(\n        page: &mut Page,\n        mut next_val: u64,\n        fixed_row_size: Size,\n        vl_num: usize,\n        mut insert: impl FnMut(&mut Page, u64),\n    ) -> u64 {\n        while page.has_space_for_row(fixed_row_size, vl_num) {\n            insert(page, next_val);\n            next_val += 1;\n        }\n        next_val\n    }\n\n    #[test]\n    fn fill_then_iter_fixed_len_u64() {\n        let mut page = Page::new(u64_row_size());\n\n        let last_val = insert_while(&mut page, 0, u64_row_size(), 0, insert_u64_drop);\n        assert_eq!(last_val, (PageOffset::PAGE_END / u64_row_size()) as u64);\n\n        for (row_idx, expected_val) in page.iter_fixed_len(u64_row_size()).zip(0..last_val) {\n            let row_val = read_u64(&page, row_idx);\n            assert_eq!(\n                row_val, expected_val,\n                \"row_val {row_val:x} /= expected_val {expected_val:x}\"\n            );\n        }\n    }\n\n    #[test]\n    fn fill_delete_iter_fixed_len_u64() {\n        let mut page = Page::new(u64_row_size());\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert rows.\n        let mut odds: Vec<PageOffset> = Vec::new();\n        let last_val = insert_while(&mut page, 2, u64_row_size(), 0, |page, val| {\n            let offset = insert_u64(page, val);\n            if val % 2 == 1 {\n                odds.push(offset);\n            }\n        });\n\n        // The hash should have been cleared.\n        let hash_pre_del = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_del);\n\n        // Delete rows.\n        for row_offset in odds {\n            unsafe { page.delete_row(row_offset, u64_row_size(), u64_var_len_visitor(), &mut NullBlobStore) };\n        }\n\n        // The hash should have been cleared.\n        let hash_pre_iter = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_iter);\n        assert_ne!(hash_pre_del, hash_pre_iter);\n\n        // Iterate the rows.\n        for (row_offset, expected_val) in page.iter_fixed_len(u64_row_size()).zip((2..last_val).step_by(2)) {\n            let found_val = read_u64(&page, row_offset);\n            assert_eq!(found_val, expected_val);\n        }\n\n        // Hash is unchanged.\n        assert_eq!(page.header.unmodified_hash, Some(hash_pre_iter));\n    }\n\n    #[test]\n    /// After deleting a fixed-length row and then inserting a new fixed-length row,\n    /// the fixed-length high water mark must not change,\n    /// i.e. we must re-use memory from the deleted row to store the new insertion.\n    fn reuse_fixed_len_space() {\n        let mut page = Page::new(u64_row_size());\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert two rows.\n        let offset_0 = insert_u64(&mut page, 0xa5a5a5a5_a5a5a5a5);\n        assert_eq!(offset_0.idx(), 0);\n        let offset_1 = insert_u64(&mut page, 0xbeefbeef_beefbeef);\n        assert_eq!(offset_1, u64_row_size());\n\n        assert_eq!(page.header.fixed.last, u64_row_size() * 2);\n\n        // Hash has been cleared after inserts.\n        let hash_pre_del = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_del);\n\n        // Delete first row.\n        unsafe { page.delete_row(offset_0, u64_row_size(), u64_var_len_visitor(), &mut NullBlobStore) };\n\n        assert_eq!(page.header.fixed.last, u64_row_size() * 2);\n\n        // Hash has been cleared after deletes.\n        let hash_pre_ins2 = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_ins2);\n        assert_ne!(hash_pre_del, hash_pre_ins2);\n\n        // Insert first row again, re-using memory.\n        let offset_0_again = insert_u64(&mut page, 0xffffffff_ffffffff);\n\n        assert_eq!(offset_0_again.idx(), 0);\n        assert_eq!(offset_0.idx(), offset_0_again.idx());\n\n        assert_eq!(page.header.fixed.last, u64_row_size() * 2);\n\n        // Hash has been cleared after last insert, despite re-using memory.\n        let hash_post_ins2 = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_post_ins2);\n        assert_ne!(hash_pre_del, hash_post_ins2);\n        assert_ne!(hash_pre_ins2, hash_post_ins2);\n    }\n\n    const STR_ROW_SIZE: Size = row_size_for_type::<VarLenRef>();\n\n    const _: () = assert!(STR_ROW_SIZE.len() == mem::size_of::<VarLenRef>());\n\n    const STR_VL_VISITOR: AlignedVarLenOffsets<'_> = AlignedVarLenOffsets::from_offsets(&[0]);\n    fn str_var_len_visitor() -> &'static AlignedVarLenOffsets<'static> {\n        &STR_VL_VISITOR\n    }\n\n    fn insert_str(page: &mut Page, data: &[u8]) -> PageOffset {\n        let fixed_len_data = [0u8; STR_ROW_SIZE.len()];\n        unsafe { page.insert_row(&fixed_len_data, &[data], str_var_len_visitor(), &mut NullBlobStore) }\n            .expect(\"Failed to insert row\")\n    }\n\n    fn read_str_ref(page: &Page, offset: PageOffset) -> VarLenRef {\n        *unsafe { get_ref(&page.row_data, offset) }\n    }\n\n    #[test]\n    fn insert_empty_str() {\n        let mut page = Page::new(STR_ROW_SIZE);\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert the empty string.\n        let offset = insert_str(&mut page, &[]);\n\n        // No granules were used.\n        let extracted = read_str_ref(&page, offset);\n        let mut granules_iter = unsafe { page.iter_var_len_object(extracted.first_granule) };\n        assert!(granules_iter.next().is_none());\n        drop(granules_iter);\n\n        // Hash is cleared even though the string was empty.\n        assert_ne!(hash_pre_ins, hash_unmodified_save_get(&mut page));\n    }\n\n    proptest! {\n        #[test]\n        fn insert_one_short_str(data in vec(any::<u8>(), 1..VarLenGranule::DATA_SIZE)) {\n            let mut page = Page::new(STR_ROW_SIZE);\n\n            // First the hash is not saved, so compute it.\n            let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n            // Insert the row.\n            let offset = insert_str(&mut page, &data);\n\n            // Hash was cleared by the insert.\n            let hash_pre_iter = hash_unmodified_save_get(&mut page);\n            assert_ne!(hash_pre_ins, hash_pre_iter);\n\n            // Confirm we inserted correctly.\n            let extracted = read_str_ref(&page, offset);\n            let mut data_iter = unsafe { page.iter_vlo_data(extracted.first_granule) };\n            let (first, next) = (data_iter.next(), data_iter.next());\n            assert_eq!(first, Some(&*data));\n            assert_eq!(next, None);\n\n            // Iteration and reading did not change the hash.\n            assert_eq!(hash_pre_iter, page.header.unmodified_hash.unwrap());\n        }\n\n        #[test]\n        fn insert_one_long_str(data in vec(any::<u8>(), (VarLenGranule::OBJECT_SIZE_BLOB_THRESHOLD / 2)..VarLenGranule::OBJECT_SIZE_BLOB_THRESHOLD)) {\n            let mut page = Page::new(STR_ROW_SIZE);\n\n            // First the hash is not saved, so compute it.\n            let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n            // Insert the long string.\n            let offset = insert_str(&mut page, &data);\n\n            // The hash was cleared, and the new one is different.\n            let hash_post_ins = hash_unmodified_save_get(&mut page);\n            assert_ne!(hash_pre_ins, hash_post_ins);\n\n            // Check that we inserted correctly.\n            let extracted = read_str_ref(&page, offset);\n\n            let mut data_iter = unsafe { page.iter_vlo_data(extracted.first_granule) };\n            let mut chunks_iter = data.chunks(VarLenGranule::DATA_SIZE);\n\n            for (i, (data, chunk)) in (&mut data_iter).zip(&mut chunks_iter).enumerate() {\n                assert_eq!(\n                    data,\n                    chunk,\n                    \"Chunk {i} does not match. Left is found, right is expected.\",\n                );\n            }\n\n            // Both iterators must be finished, i.e. they must have the same length.\n            assert!(data_iter.next().is_none());\n            assert!(chunks_iter.next().is_none());\n\n            // Reading did not alter the hash.\n            assert_eq!(hash_post_ins, page.header.unmodified_hash.unwrap());\n        }\n    }\n\n    #[test]\n    fn reuse_var_len_space_no_fragmentation_concerns() {\n        let data_0 = b\"Hello, world!\";\n        let data_1 = b\"How goes life?\";\n        let data_2 = b\"Glad to hear it.\";\n\n        let mut page = Page::new(STR_ROW_SIZE);\n        let offset_0 = insert_str(&mut page, data_0);\n        let offset_1 = insert_str(&mut page, data_1);\n\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(2));\n\n        assert_ne!(offset_0.idx(), offset_1.idx());\n\n        let var_len_0 = read_str_ref(&page, offset_0);\n\n        assert_eq!(var_len_0.length_in_bytes as usize, data_0.len());\n        assert_eq!(var_len_0.first_granule.idx(), data_sub_n_vlg(1));\n\n        let var_len_1 = read_str_ref(&page, offset_1);\n\n        assert_eq!(var_len_1.length_in_bytes as usize, data_1.len());\n        assert_eq!(var_len_1.first_granule.idx(), data_sub_n_vlg(2));\n\n        let hash_pre_del = hash_unmodified_save_get(&mut page);\n\n        unsafe { page.delete_row(offset_0, STR_ROW_SIZE, str_var_len_visitor(), &mut NullBlobStore) };\n\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        let offset_2 = insert_str(&mut page, data_2);\n\n        let hash_post_ins = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_del, hash_pre_ins);\n        assert_ne!(hash_pre_del, hash_post_ins);\n        assert_ne!(hash_pre_ins, hash_post_ins);\n\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(2));\n\n        assert_eq!(offset_0.idx(), offset_2.idx());\n\n        let var_len_2 = read_str_ref(&page, offset_2);\n\n        assert_eq!(var_len_2.length_in_bytes as usize, data_2.len());\n        assert_eq!(var_len_2.first_granule.idx(), var_len_0.first_granule.idx());\n    }\n\n    #[test]\n    fn free_var_len_obj_multiple_granules() {\n        let mut page = Page::new(STR_ROW_SIZE);\n\n        // Allocate a 4-granule var-len object.\n        let data_0 = [0xa5u8].repeat(VarLenGranule::DATA_SIZE * 4);\n        let offset_0 = insert_str(&mut page, &data_0);\n\n        let var_len_0 = read_str_ref(&page, offset_0);\n\n        // Read the addresses of its var-len granules.\n        let granules_0 = unsafe { page.iter_var_len_object(var_len_0.first_granule) }\n            .map(|granule| granule as *const VarLenGranule as usize)\n            .collect::<Vec<_>>();\n\n        // Sanity checks: we have allocated 4 granules.\n        assert_eq!(granules_0.len(), 4);\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(4));\n\n        // Delete the row.\n        unsafe { page.delete_row(offset_0, STR_ROW_SIZE, str_var_len_visitor(), &mut NullBlobStore) };\n\n        // Allocate a new 4-granule var-len object.\n        // This should use the same storage as the original row.\n        let data_1 = [0xffu8].repeat(VarLenGranule::DATA_SIZE * 4);\n        let offset_1 = insert_str(&mut page, &data_1);\n\n        let var_len_1 = read_str_ref(&page, offset_1);\n\n        // Read the addresses of the new allocation's var-len granules.\n        let granules_1 = unsafe { page.iter_var_len_object(var_len_1.first_granule) }\n            .map(|granule| granule as *const VarLenGranule as usize)\n            .collect::<Vec<_>>();\n\n        // Sanity check: the new allocation is also 4 granules.\n        assert_eq!(granules_1.len(), 4);\n\n        for granule in granules_1.iter().copied() {\n            // The new var-len allocation must contain all the same granules by address\n            // as the old var-len allocation.\n            assert!(granules_0.iter().copied().any(|other_granule| other_granule == granule));\n        }\n\n        // The var-len high water mark must not have moved.\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(4));\n    }\n\n    #[test]\n    fn reuse_var_len_space_avoid_fragmentation() {\n        let data_0 = &[0xa5u8];\n        let data_1 = &[0xffu8];\n        let data_2 = [0x11u8].repeat(VarLenGranule::DATA_SIZE + 1);\n        let data_2 = data_2.as_ref();\n\n        let mut page = Page::new(STR_ROW_SIZE);\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert two string rows.\n        let offset_0 = insert_str(&mut page, data_0);\n        let _offset_1 = insert_str(&mut page, data_1);\n\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(2));\n\n        // Hash is cleared by inserting and the new one is different.\n        let hash_pre_del = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_del);\n\n        // Delete the first row.\n        unsafe { page.delete_row(offset_0, STR_ROW_SIZE, str_var_len_visitor(), &mut NullBlobStore) };\n\n        // Hash is cleared by deleting.\n        let hash_post_del = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_post_del);\n        assert_ne!(hash_pre_del, hash_post_del);\n\n        // Insert again, re-using memory.\n        let offset_2 = insert_str(&mut page, data_2);\n\n        assert_eq!(page.header.var.first.idx(), data_sub_n_vlg(3));\n\n        // Hash is cleared by inserting again, even though we re-used memory.\n        let hash_post_ins2 = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_post_ins2);\n        assert_ne!(hash_pre_del, hash_post_ins2);\n        assert_ne!(hash_post_del, hash_post_ins2);\n\n        // Check that we inserted correctly.\n        let var_len_2 = read_str_ref(&page, offset_2);\n\n        let mut data_iter = unsafe { page.iter_vlo_data(var_len_2.first_granule) };\n        let mut chunks_iter = data_2.chunks(VarLenGranule::DATA_SIZE);\n\n        for (i, (data, chunk)) in (&mut data_iter).zip(&mut chunks_iter).enumerate() {\n            assert_eq!(\n                data, chunk,\n                \"Chunk {i} does not match. Left is found, right is expected.\",\n            );\n        }\n\n        // Both iterators must be finished, i.e. they must have the same length.\n        assert!(data_iter.next().is_none());\n        assert!(chunks_iter.next().is_none());\n    }\n\n    fn check_u64_in_str(page: &Page, row_idx: PageOffset, expected_val: u64) {\n        let vlr = read_str_ref(page, row_idx);\n\n        let mut var_len_iter = unsafe { page.iter_vlo_data(vlr.first_granule) };\n        let data = var_len_iter.next().unwrap();\n        assert!(var_len_iter.next().is_none());\n        assert_eq!(data.len(), mem::size_of::<u64>());\n\n        let val = u64::from_le_bytes(data.try_into().unwrap());\n        assert_eq!(val, expected_val);\n    }\n\n    #[test]\n    fn fill_then_iter_var_len_str() {\n        let mut page = Page::new(STR_ROW_SIZE);\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert the strings.\n        let last_val = insert_while(&mut page, 0, STR_ROW_SIZE, 1, |page, val| {\n            insert_str(page, &val.to_le_bytes());\n        });\n\n        // Hash is cleared by inserting and the new one is different.\n        let hash_pre_iter = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_iter);\n\n        // Check that we inserted correctly.\n        let size_per_row = STR_ROW_SIZE + VarLenGranule::SIZE;\n\n        assert_eq!(last_val, (PageOffset::PAGE_END / size_per_row) as u64);\n\n        for (row_idx, expected_val) in page.iter_fixed_len(STR_ROW_SIZE).zip(0..last_val) {\n            check_u64_in_str(&page, row_idx, expected_val);\n        }\n\n        // Reading does not alter the hash.\n        assert_eq!(hash_pre_iter, page.header.unmodified_hash.unwrap());\n    }\n\n    #[test]\n    fn fill_delete_iter_var_len_str() {\n        let mut page = Page::new(STR_ROW_SIZE);\n\n        // First the hash is not saved, so compute it.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n\n        // Insert the string rows.\n        let mut odds = Vec::new();\n        let last_val = insert_while(&mut page, 0, STR_ROW_SIZE, 1, |page, val| {\n            let offset = insert_str(page, &val.to_le_bytes());\n            if val % 2 == 1 {\n                odds.push(offset);\n            }\n        });\n\n        let size_per_row = STR_ROW_SIZE + VarLenGranule::SIZE;\n        let num_rows_inserted = (PageOffset::PAGE_END / size_per_row) as u64;\n        assert_eq!(last_val, num_rows_inserted);\n\n        // Hash was cleared by inserting and is different now.\n        let hash_pre_del = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_del);\n\n        // Delete the rows.\n        for row_offset in odds {\n            unsafe { page.delete_row(row_offset, STR_ROW_SIZE, str_var_len_visitor(), &mut NullBlobStore) };\n        }\n\n        // Hash was cleared by deleting and is different now.\n        let hash_pre_iter = hash_unmodified_save_get(&mut page);\n        assert_ne!(hash_pre_ins, hash_pre_iter);\n        assert_ne!(hash_pre_del, hash_pre_iter);\n\n        // Check that we deleted correctly.\n        let num_rows_retained = num_rows_inserted.div_ceil(2);\n        let num_rows_removed = num_rows_inserted / 2;\n\n        assert_eq!(page.header.fixed.num_rows as u64, num_rows_retained);\n\n        assert_eq!(page.header.var.freelist_len as u64, num_rows_removed);\n\n        for (row_idx, expected_val) in page.iter_fixed_len(STR_ROW_SIZE).zip((0..last_val).step_by(2)) {\n            check_u64_in_str(&page, row_idx, expected_val);\n        }\n\n        // Reading did not alter the hash.\n        assert_eq!(hash_pre_iter, page.header.unmodified_hash.unwrap());\n    }\n\n    #[test]\n    fn serde_round_trip_whole_page() {\n        let pool = PagePool::new_for_test();\n        let mut page = Page::new(u64_row_size());\n\n        // Construct an empty page, ser/de it, and assert that it's still empty.\n        let hash_pre_ins = hash_unmodified_save_get(&mut page);\n        let ser_pre_ins = bsatn::to_vec(&page).unwrap();\n        let de_pre_ins = pool.take_deserialize_from(&ser_pre_ins).unwrap();\n        assert_eq!(de_pre_ins.content_hash(), hash_pre_ins);\n        assert_eq!(de_pre_ins.header.fixed.num_rows, 0);\n        assert!(de_pre_ins.header.fixed.present_rows == page.header.fixed.present_rows);\n\n        // Insert some rows into the page.\n        let offsets = (0..64)\n            .map(|val| insert_u64(&mut page, val))\n            .collect::<Vec<PageOffset>>();\n\n        let hash_ins = hash_unmodified_save_get(&mut page);\n\n        // Ser/de the page and assert that it contains the same rows.\n        let ser_ins = bsatn::to_vec(&page).unwrap();\n        let de_ins = pool.take_deserialize_from(&ser_ins).unwrap();\n        assert_eq!(de_ins.content_hash(), hash_ins);\n        assert_eq!(de_ins.header.fixed.num_rows, 64);\n        assert!(de_ins.header.fixed.present_rows == page.header.fixed.present_rows);\n        assert_eq!(\n            de_ins.iter_fixed_len(u64_row_size()).collect::<Vec<PageOffset>>(),\n            offsets\n        );\n\n        // Delete the even-numbered rows, leaving the odds.\n        let offsets = offsets\n            .into_iter()\n            .enumerate()\n            .filter_map(|(i, offset)| {\n                if i % 2 == 0 {\n                    unsafe { page.delete_row(offset, u64_row_size(), u64_var_len_visitor(), &mut NullBlobStore) };\n                    None\n                } else {\n                    Some(offset)\n                }\n            })\n            .collect::<Vec<PageOffset>>();\n\n        // Ser/de the page again and assert that it contains only the odd-numbered rows.\n        let hash_del = hash_unmodified_save_get(&mut page);\n        let ser_del = bsatn::to_vec(&page).unwrap();\n        let de_del = pool.take_deserialize_from(&ser_del).unwrap();\n        assert_eq!(de_del.content_hash(), hash_del);\n        assert_eq!(de_del.header.fixed.num_rows, 32);\n        assert!(de_del.header.fixed.present_rows == page.header.fixed.present_rows);\n        assert_eq!(\n            de_del.iter_fixed_len(u64_row_size()).collect::<Vec<PageOffset>>(),\n            offsets\n        );\n    }\n}\n"
  },
  {
    "path": "crates/table/src/page_pool.rs",
    "content": "use super::{\n    indexes::max_rows_in_page,\n    page::{Page, PageHeader},\n};\nuse derive_more::Deref;\nuse spacetimedb_data_structures::object_pool::{Pool, PooledObject};\nuse spacetimedb_sats::bsatn::{self, DecodeError};\nuse spacetimedb_sats::de::{\n    DeserializeSeed, Deserializer, Error, NamedProductAccess, ProductVisitor, SeqProductAccess,\n};\nuse spacetimedb_sats::layout::Size;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\nimpl PooledObject for Box<Page> {\n    type ResidentBytesStorage = ();\n    fn resident_object_bytes(_: &Self::ResidentBytesStorage, num_objects: usize) -> usize {\n        // Each page takes up a fixed amount.\n        num_objects * size_of::<Page>()\n    }\n    fn add_to_resident_object_bytes(_: &Self::ResidentBytesStorage, _: usize) {}\n    fn sub_from_resident_object_bytes(_: &Self::ResidentBytesStorage, _: usize) {}\n}\n\n/// A page pool of currently unused pages available for use in [`Pages`](super::pages::Pages).\n#[derive(Clone, Deref)]\npub struct PagePool {\n    pool: Pool<Box<Page>>,\n}\n\nimpl MemoryUsage for PagePool {\n    fn heap_usage(&self) -> usize {\n        self.pool.heap_usage()\n    }\n}\n\nimpl PagePool {\n    pub fn new_for_test() -> Self {\n        Self::new(Some(100 * size_of::<Page>()))\n    }\n\n    /// Returns a new page pool with `max_size` bytes rounded down to the nearest multiple of 64 KiB.\n    ///\n    /// if no size is provided, a default of 1 page is used.\n    pub fn new(max_size: Option<usize>) -> Self {\n        const PAGE_SIZE: usize = size_of::<Page>();\n        // TODO(centril): Currently, we have a test `test_index_scans`.\n        // The test sets up a `Location` table, like in BitCraft, with a `chunk` field,\n        // and populates it with 1000 different chunks with 1200 rows each.\n        // Then it asserts that the cold latency of an index scan on `chunk` takes < 1 ms.\n        // However, for reasons currently unknown to us,\n        // a large page pool, with capacity `1 << 26` bytes, on i7-7700K, 64GB RAM,\n        // will turn the latency into 30-40 ms.\n        // As a precaution, we use a smaller page pool by default.\n        const DEFAULT_MAX_SIZE: usize = 128 * PAGE_SIZE; // 128 pages\n\n        let queue_size = max_size.unwrap_or(DEFAULT_MAX_SIZE) / PAGE_SIZE;\n        let pool = Pool::new(queue_size);\n        Self { pool }\n    }\n\n    /// Takes a [`Page`] from the pool or creates a new one.\n    ///\n    /// The returned page supports fixed rows of size `fixed_row_size`.\n    pub fn take_with_fixed_row_size(&self, fixed_row_size: Size) -> Box<Page> {\n        self.take_with_max_row_count(max_rows_in_page(fixed_row_size))\n    }\n\n    /// Takes a [`Page`] from the pool or creates a new one.\n    ///\n    /// The returned page supports a maximum of `max_rows_in_page` rows.\n    fn take_with_max_row_count(&self, max_rows_in_page: usize) -> Box<Page> {\n        self.pool.take(\n            |page| page.reset_for(max_rows_in_page),\n            || Page::new_with_max_row_count(max_rows_in_page),\n        )\n    }\n\n    /// Deserialize a page from `buf` but reuse the allocations in the pool.\n    pub fn take_deserialize_from(&self, buf: &[u8]) -> Result<Box<Page>, DecodeError> {\n        self.deserialize(bsatn::Deserializer::new(&mut &*buf))\n    }\n}\n\nimpl<'de> DeserializeSeed<'de> for &PagePool {\n    type Output = Box<Page>;\n\n    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {\n        de.deserialize_product(self)\n    }\n}\n\nimpl<'de> ProductVisitor<'de> for &PagePool {\n    type Output = Box<Page>;\n\n    fn product_name(&self) -> Option<&str> {\n        Some(\"Page\")\n    }\n\n    fn product_len(&self) -> usize {\n        2\n    }\n\n    fn visit_seq_product<A: SeqProductAccess<'de>>(self, mut prod: A) -> Result<Self::Output, A::Error> {\n        let header = prod\n            .next_element::<PageHeader>()?\n            .ok_or_else(|| A::Error::invalid_product_length(2, &self))?;\n        let row_data = prod\n            .next_element()?\n            .ok_or_else(|| A::Error::invalid_product_length(2, &self))?;\n\n        // TODO(perf, centril): reuse the allocation of `present_rows` in `page`.\n        let mut page = self.take_with_max_row_count(header.max_rows_in_page());\n        // SAFETY: `header` and `row_data` are consistent with each other.\n        unsafe { page.set_raw(header, row_data) };\n\n        Ok(page)\n    }\n\n    fn visit_named_product<A: NamedProductAccess<'de>>(self, _: A) -> Result<Self::Output, A::Error> {\n        Err(A::Error::named_products_not_supported())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use core::ptr::addr_eq;\n\n    fn present_rows_ptr(page: &Page) -> *const () {\n        page.page_header_for_test().present_rows_storage_ptr_for_test()\n    }\n\n    #[test]\n    fn page_pool_bitset_reuse() {\n        let pool = PagePool::new_for_test();\n        // Create a page and put it back.\n        let page1 = pool.take_with_max_row_count(10);\n        let page1_pr_ptr = present_rows_ptr(&page1);\n        pool.put(page1);\n\n        // Extract another page again, but use a different max row count (64).\n        // The bitset should be the same, as `10.div_ceil(64) == 64`.\n        let page2 = pool.take_with_max_row_count(64);\n        assert!(addr_eq(page1_pr_ptr, present_rows_ptr(&page2)));\n        pool.put(page2);\n\n        // Extract a page again, but this time, go beyond the first bitset block.\n        let page3 = pool.take_with_max_row_count(64 + 1);\n        // The bitset should not be the same, as `65.div_ceil(64) == 2`.\n        assert!(!addr_eq(page1_pr_ptr, present_rows_ptr(&page3)));\n    }\n}\n"
  },
  {
    "path": "crates/table/src/pages.rs",
    "content": "//! Provides [`Pages`], a page manager dealing with [`Page`]s as a collection.\n\nuse super::blob_store::{BlobHash, BlobStore};\nuse super::indexes::{Bytes, PageIndex, PageOffset, RowPointer};\nuse super::page::Page;\nuse super::page_pool::PagePool;\nuse super::table::BlobNumBytes;\nuse super::var_len::VarLenMembers;\nuse core::ops::{ControlFlow, Deref, Index, IndexMut};\nuse spacetimedb_sats::layout::Size;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse std::ops::DerefMut;\nuse thiserror::Error;\n\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum Error {\n    #[error(\"Attempt to allocate more than {} pages.\", PageIndex::MAX.idx())]\n    TooManyPages,\n    #[error(transparent)]\n    Page(#[from] super::page::Error),\n}\n\nimpl Index<PageIndex> for Pages {\n    type Output = Page;\n\n    fn index(&self, pi: PageIndex) -> &Self::Output {\n        &self.pages[pi.idx()]\n    }\n}\n\nimpl IndexMut<PageIndex> for Pages {\n    fn index_mut(&mut self, pi: PageIndex) -> &mut Self::Output {\n        &mut self.pages[pi.idx()]\n    }\n}\n\n/// A manager of [`Page`]s.\n#[derive(Default, Debug, PartialEq, Eq)]\npub struct Pages {\n    /// The collection of pages under management.\n    pages: Vec<Box<Page>>,\n    /// The set of pages that aren't yet full.\n    non_full_pages: Vec<PageIndex>,\n}\n\nimpl MemoryUsage for Pages {\n    fn heap_usage(&self) -> usize {\n        let Self { pages, non_full_pages } = self;\n        pages.heap_usage() + non_full_pages.heap_usage()\n    }\n}\n\nimpl Pages {\n    /// Is there space to allocate another page?\n    pub fn can_allocate_new_page(&self) -> Result<PageIndex, Error> {\n        let new_idx = self.len();\n        if new_idx <= PageIndex::MAX.idx() {\n            Ok(PageIndex(new_idx as _))\n        } else {\n            Err(Error::TooManyPages)\n        }\n    }\n\n    /// Get a mutable reference to a `Page`.\n    ///\n    /// Used in benchmarks. Internal operators will prefer directly indexing into `self.pages`,\n    /// as that allows split borrows.\n    pub fn get_page_mut(&mut self, page: PageIndex) -> &mut Page {\n        &mut self.pages[page.idx()]\n    }\n\n    /// Make all pages within `self` clear,\n    /// deleting all rows.\n    #[doc(hidden)] // Used in benchmarks.\n    pub fn clear(&mut self) {\n        // Clear every page.\n        for page in &mut self.pages {\n            page.clear();\n        }\n        // Mark every page non-full.\n        self.non_full_pages = (0..self.pages.len()).map(|idx| PageIndex(idx as u64)).collect();\n    }\n\n    /// Get a reference to fixed-len row data.\n    ///\n    /// Used in benchmarks.\n    /// Higher-level code paths are expected to go through [`super::de::read_row_from_pages`].\n    #[doc(hidden)] // Used in benchmarks.\n    pub fn get_fixed_len_row(&self, row: RowPointer, fixed_row_size: Size) -> &Bytes {\n        self[row.page_index()].get_row_data(row.page_offset(), fixed_row_size)\n    }\n\n    /// Allocates one additional page,\n    /// returning an error if the new number of pages would overflow `PageIndex::MAX`.\n    ///\n    /// The new page is initially empty, but is not added to the non-full set.\n    /// Callers should call [`Pages::maybe_mark_page_non_full`] after operating on the new page.\n    fn allocate_new_page(&mut self, pool: &PagePool, fixed_row_size: Size) -> Result<PageIndex, Error> {\n        let new_idx = self.can_allocate_new_page()?;\n\n        let page = pool.take_with_fixed_row_size(fixed_row_size);\n        self.pages.push(page);\n\n        Ok(new_idx)\n    }\n\n    /// Reserve a new, initially empty page.\n    pub fn reserve_empty_page(&mut self, pool: &PagePool, fixed_row_size: Size) -> Result<PageIndex, Error> {\n        let idx = self.allocate_new_page(pool, fixed_row_size)?;\n        self.mark_page_non_full(idx);\n        Ok(idx)\n    }\n\n    /// Mark the page at `idx` as non-full.\n    pub fn mark_page_non_full(&mut self, idx: PageIndex) {\n        self.non_full_pages.push(idx);\n    }\n\n    /// If the page at `page_index` is not full,\n    /// add it to the non-full set so that later insertions can access it.\n    pub fn maybe_mark_page_non_full(&mut self, page_index: PageIndex, fixed_row_size: Size) {\n        if !self[page_index].is_full(fixed_row_size) {\n            self.non_full_pages.push(page_index);\n        }\n    }\n\n    /// Call `f` with a reference to a page which satisfies\n    /// `page.has_space_for_row(fixed_row_size, num_var_len_granules)`.\n    pub fn with_page_to_insert_row<Res>(\n        &mut self,\n        pool: &PagePool,\n        fixed_row_size: Size,\n        num_var_len_granules: usize,\n        f: impl FnOnce(&mut Page) -> Res,\n    ) -> Result<(PageIndex, Res), Error> {\n        let page_index = self.find_page_with_space_for_row(pool, fixed_row_size, num_var_len_granules)?;\n        let res = f(&mut self[page_index]);\n        self.maybe_mark_page_non_full(page_index, fixed_row_size);\n        Ok((page_index, res))\n    }\n\n    /// Find a page with sufficient available space to store a row of size `fixed_row_size`\n    /// containing `num_var_len_granules` granules of var-len data.\n    ///\n    /// Retrieving a page in this way will remove it from the non-full set.\n    /// After performing an insertion, the caller should use [`Pages::maybe_mark_page_non_full`]\n    /// to restore the page to the non-full set.\n    fn find_page_with_space_for_row(\n        &mut self,\n        pool: &PagePool,\n        fixed_row_size: Size,\n        num_var_len_granules: usize,\n    ) -> Result<PageIndex, Error> {\n        if let Some((page_idx_idx, page_idx)) = self\n            .non_full_pages\n            .iter()\n            .copied()\n            .enumerate()\n            .find(|(_, page_idx)| self[*page_idx].has_space_for_row(fixed_row_size, num_var_len_granules))\n        {\n            self.non_full_pages.swap_remove(page_idx_idx);\n            return Ok(page_idx);\n        }\n\n        self.allocate_new_page(pool, fixed_row_size)\n    }\n\n    /// Superseded by `write_av_to_pages`, but exposed for benchmarking\n    /// when we want to avoid the overhead of traversing `AlgebraicType`.\n    ///\n    /// Inserts a row with fixed parts in `fixed_len` and variable parts in `var_len`.\n    /// The `fixed_len.len()` is equal to `fixed_row_size`.\n    ///\n    /// # Safety\n    ///\n    /// - `var_len_visitor` must be suitable for visiting var-len refs in `fixed_row`.\n    /// - `fixed_row.len()` matches the row type size exactly.\n    /// - `fixed_row.len()` is consistent\n    ///   with what has been passed to the manager in all other ops\n    ///   and must be consistent with the `var_len_visitor` the manager was made with.\n    // TODO(bikeshedding): rename to make purpose as bench interface clear?\n    pub unsafe fn insert_row(\n        &mut self,\n        pool: &PagePool,\n        var_len_visitor: &impl VarLenMembers,\n        fixed_row_size: Size,\n        fixed_len: &Bytes,\n        var_len: &[&[u8]],\n        blob_store: &mut dyn BlobStore,\n    ) -> Result<(PageIndex, PageOffset), Error> {\n        debug_assert!(fixed_len.len() == fixed_row_size.len());\n\n        match self.with_page_to_insert_row(\n            pool,\n            fixed_row_size,\n            Page::total_granules_required_for_objects(var_len),\n            |page| {\n                // This insertion can never fail, as we know that the page has sufficient space from `find_page_with_space_for_row`.\n                //\n                // SAFETY:\n                // - Caller promised that `var_len_visitor`\n                //   is suitable for visiting var-len refs in `fixed_row`\n                //   and that `fixed_row.len()` matches the row type size exactly.\n                //\n                // - Caller promised that `fixed_row.len()` is consistent\n                //   with what has been passed to the manager in all other ops.\n                //   This entails that `fixed_row.len()` is consistent with `page`.\n                unsafe { page.insert_row(fixed_len, var_len, var_len_visitor, blob_store) }\n            },\n        )? {\n            (page, Ok(offset)) => Ok((page, offset)),\n            (_, Err(e)) => Err(e.into()),\n        }\n    }\n\n    /// Free the row that is pointed to by `row_ptr`,\n    /// marking its fixed-len storage\n    /// and var-len storage granules as available for re-use.\n    ///\n    /// # Safety\n    ///\n    /// The `row_ptr` must point to a valid row in this page manager,\n    /// of `fixed_row_size` bytes for the fixed part.\n    ///\n    /// The `fixed_row_size` must be consistent\n    /// with what has been passed to the manager in all other operations\n    /// and must be consistent with the `var_len_visitor` the manager was made with.\n    pub unsafe fn delete_row(\n        &mut self,\n        var_len_visitor: &impl VarLenMembers,\n        fixed_row_size: Size,\n        row_ptr: RowPointer,\n        blob_store: &mut dyn BlobStore,\n    ) -> BlobNumBytes {\n        let page = &mut self[row_ptr.page_index()];\n        let full_before = page.is_full(fixed_row_size);\n        // SAFETY:\n        // - `row_ptr.page_offset()` does point to a valid row in this page\n        //   as the caller promised that `row_ptr` points to a valid row in `self`.\n        //\n        // - `fixed_row_size` is consistent with the size in bytes of the fixed part of the row.\n        //   The size is also conistent with `var_len_visitor`.\n        let blob_store_deleted_bytes =\n            unsafe { page.delete_row(row_ptr.page_offset(), fixed_row_size, var_len_visitor, blob_store) };\n\n        // If the page was previously full, mark it as non-full now,\n        // since we just opened a space in it.\n        if full_before {\n            self.mark_page_non_full(row_ptr.page_index());\n        }\n        blob_store_deleted_bytes\n    }\n\n    /// Materialize a view of rows in `self` for which the  `filter` returns `true`.\n    ///\n    /// # Safety\n    ///\n    /// - The `var_len_visitor` will visit the same set of `VarLenRef`s in the row\n    ///   as the visitor provided to all other methods on `self`.\n    ///\n    /// - The `fixed_row_size` is consistent with the `var_len_visitor`\n    ///   and is equal to the value provided to all other methods on `self`.\n    pub unsafe fn copy_filter(\n        &self,\n        var_len_visitor: &impl VarLenMembers,\n        fixed_row_size: Size,\n        mut blob_policy: Option<&mut impl FnMut(BlobHash)>,\n        mut filter: impl FnMut(&Page, PageOffset) -> bool,\n    ) -> Self {\n        // Build a new container to hold the materialized view.\n        // Push pages into it later.\n        let mut partial_copied_pages = Self::default();\n\n        // A destination page that was not filled entirely,\n        // or `None` if it's time to allocate a new destination page.\n        let mut partial_page = None;\n\n        // Copy each page.\n        for from_page in &self.pages {\n            // You may require multiple calls to `Page::copy_starting_from`\n            // if `partial_page` fills up;\n            // the first call starts from 0.\n            let mut copy_starting_from = Some(PageOffset(0));\n\n            // While there are unprocessed rows in `from_page`,\n            while let Some(next_offset) = copy_starting_from.take() {\n                // Grab the `partial_page` or allocate a new one.\n                let mut to_page = partial_page.take().unwrap_or_else(|| Page::new(fixed_row_size));\n\n                // Copy as many rows as will fit in `to_page`.\n                //\n                // SAFETY:\n                //\n                // - The `var_len_visitor` will visit the same set of `VarLenRef`s in the row\n                //   as the visitor provided to all other methods on `self`.\n                //   The `to_page` uses the same visitor as the `from_page`.\n                //\n                // - The `fixed_row_size` is consistent with the `var_len_visitor`\n                //   and is equal to the value provided to all other methods on `self`,\n                //   as promised by the caller.\n                //   The newly made `to_page` uses the same `fixed_row_size` as the `from_page`.\n                //\n                // - The `next_offset` is either 0,\n                //   which is always a valid starting offset for any row size,\n                //   or it came from `copy_filter_into` in a previous iteration,\n                //   which, given that `fixed_row_size` was valid,\n                //   always returns a valid starting offset in case of `Continue(_)`.\n                let cfi_ret = unsafe {\n                    from_page.copy_filter_into(\n                        next_offset,\n                        &mut to_page,\n                        fixed_row_size,\n                        var_len_visitor,\n                        blob_policy.as_mut(),\n                        &mut filter,\n                    )\n                };\n                copy_starting_from = if let ControlFlow::Continue(continue_point) = cfi_ret {\n                    // If `to_page` couldn't fit all of `from_page`,\n                    // repeat the `while_let` loop to copy the rest.\n                    Some(continue_point)\n                } else {\n                    // If `to_page` fit all of `from_page`, we can move on.\n                    None\n                };\n\n                // If `from_page` finished copying into `to_page`, then `to_page` may have extra room.\n                //\n                // If `copy_filtered_into` returns `Some`,\n                // that means at least one row didn't have space in `to_page`,\n                // so we must consider `to_page` full.\n                //\n                // Note that this is distinct from `Page::is_full`,\n                // as that method considers the optimistic case of a row with no var-len members.\n                if copy_starting_from.is_none() {\n                    partial_page = Some(to_page);\n                } else {\n                    partial_copied_pages.pages.push(to_page);\n                }\n            }\n        }\n\n        partial_copied_pages\n    }\n\n    /// Set this [`Pages`]' contents to be the `pages`.\n    ///\n    /// Used when restoring from a snapshot.\n    ///\n    /// Each page in the `pages` must be consistent with the schema for this [`Pages`],\n    /// i.e. the schema for the [`crate::table::Table`] which contains `self`.\n    ///\n    /// Should only ever be called when `self.is_empty()`.\n    ///\n    /// Also populates `self.non_full_pages`.\n    pub fn set_contents(&mut self, pages: Vec<Box<Page>>, fixed_row_size: Size) {\n        debug_assert!(self.is_empty());\n        self.non_full_pages = pages\n            .iter()\n            .enumerate()\n            .filter_map(|(idx, page)| (!page.is_full(fixed_row_size)).then_some(PageIndex(idx as _)))\n            .collect();\n        self.pages = pages;\n    }\n\n    /// Consumes the page manager, returning all the pages it held.\n    pub fn into_page_iter(self) -> impl Iterator<Item = Box<Page>> {\n        self.pages.into_iter()\n    }\n}\n\nimpl Deref for Pages {\n    type Target = [Box<Page>];\n\n    fn deref(&self) -> &Self::Target {\n        &self.pages\n    }\n}\n\nimpl DerefMut for Pages {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.pages\n    }\n}\n"
  },
  {
    "path": "crates/table/src/pointer_map.rs",
    "content": "//! Provides [`PointerMap`] that deals with the\n//! association of a [`RowHash`] to a [`RowPointer`]\n//! through operations [`insert`](self::PointerMap::insert)\n//! and [`delete`](PointerMap::delete).\n//!\n//! These associations can then be queried through\n//! `map.pointers_for(hash)` and `map.pointers_for_mut(hash)`.\n//! In most cases, this will result in a `1:1` mapping\n//! and so a direct hit in a hash map.\n//! If however multiple pointers collide to a single hash,\n//! all of these pointers will be returned, in an arbitrary unstable order.\n//! Pointers are returned as a slice, which does not require an allocation.\n//! In this highly unlikely event of a collision,\n//! retrieval is probably no more than 100% slower.\n\nuse super::indexes::{PageIndex, PageOffset, RowHash, RowPointer, SquashedOffset};\nuse crate::static_assert_size;\nuse core::{hint, slice};\nuse spacetimedb_data_structures::map::{\n    hash_map::Entry,\n    IntMap, // No need to hash a hash.\n};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n/// An index to the outer layer of `colliders` in `PointerMap`.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\nstruct ColliderSlotIndex(u32);\n\nimpl MemoryUsage for ColliderSlotIndex {}\n\nimpl ColliderSlotIndex {\n    /// Returns a new slot index based on `idx`.\n    fn new(idx: usize) -> Self {\n        Self(idx as u32)\n    }\n\n    /// Returns the index as a `usize`.\n    fn idx(self) -> usize {\n        self.0 as usize\n    }\n}\n\n/// A pointer into the `pages` of a table\n/// or, for any `RowHash` collisions in `map`,\n/// the index in `colliders` to a list of `RowPointer`s.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]\nstruct PtrOrCollider(RowPointer);\n\nimpl MemoryUsage for PtrOrCollider {}\n\n/// An unpacked representation of [`&mut PtrOrCollider`](PtrOrCollider).\nenum MapSlotRef<'map> {\n    /// The hash has no collision and is associated to a single row pointer.\n    Pointer(&'map RowPointer),\n    /// The hash has collisions\n    /// and all of the associated row pointers can be found at `map.colliders[idx]`.\n    Collider(ColliderSlotIndex),\n}\n\n/// An unpacked representation of [`&PtrOrCollider`](PtrOrCollider).\nenum MapSlotMut<'map> {\n    /// The hash has no collision and is associated to a single row pointer.\n    Pointer(&'map mut RowPointer),\n    /// The hash has collisions\n    /// and all of the associated row pointers can be found at `map.colliders[idx]`.\n    Collider(ColliderSlotIndex),\n}\n\n/// Ensures `rp` is treated as a `RowPointer` by the map, and not as a collider.\n/// This is achieved by setting the reserved bit,\n/// used by [`PtrOrCollider::is_ptr`], to `false`.\n#[inline]\nconst fn ensure_ptr(rp: RowPointer) -> RowPointer {\n    rp.with_reserved_bit(false)\n}\n\nimpl PtrOrCollider {\n    /// Returns a pointer.\n    const fn ptr(rp: RowPointer) -> Self {\n        Self(ensure_ptr(rp))\n    }\n\n    /// Returns a collider.\n    const fn collider(c: ColliderSlotIndex) -> Self {\n        // Pack the `ColliderSlotIndex` into the page index bits.\n        let pi = PageIndex(c.0 as u64);\n        Self(RowPointer::new(\n            true,\n            pi,\n            PageOffset::VAR_LEN_NULL,\n            SquashedOffset::COMMITTED_STATE,\n        ))\n    }\n\n    /// Returns whether this is a pointer or not.\n    const fn is_ptr(&self) -> bool {\n        !self.0.reserved_bit()\n    }\n\n    /// Assumes that `self` is a `ColliderSlotIndex` and returns it as such.\n    const fn as_collider(&self) -> ColliderSlotIndex {\n        ColliderSlotIndex(self.0.page_index().0 as u32)\n    }\n\n    /// Convert the packed representation into an unpacked one.\n    const fn unpack(&self) -> MapSlotRef<'_> {\n        if self.is_ptr() {\n            MapSlotRef::Pointer(&self.0)\n        } else {\n            MapSlotRef::Collider(self.as_collider())\n        }\n    }\n\n    /// Convert the packed representation into an unpacked one.\n    fn unpack_mut(&mut self) -> MapSlotMut<'_> {\n        if self.is_ptr() {\n            MapSlotMut::Pointer(&mut self.0)\n        } else {\n            MapSlotMut::Collider(self.as_collider())\n        }\n    }\n}\n\nimpl From<ColliderSlotIndex> for PtrOrCollider {\n    fn from(index: ColliderSlotIndex) -> Self {\n        Self::collider(index)\n    }\n}\n\n/// An pointer map `RowHash -> [RowPointer]`.\n#[derive(Default, Clone, PartialEq, Eq, Debug)]\npub struct PointerMap {\n    /// The pointer map from row hashes to row pointer(s).\n    ///\n    /// Invariant: `self.maintains_map_invariant()`.\n    map: IntMap<RowHash, PtrOrCollider>,\n    /// The inner vector is a list (\"slot\") of row pointers that share a row hash.\n    /// The outer is indexed by [`ColliderSlotIndex`].\n    ///\n    /// This indirect approach is used,\n    /// rather than storing a list of [`RowPointer`]s,\n    /// to reduce the cost for the more common case (fewer collisions).\n    ///\n    /// This list is append-only as `ColliderSlotIndex` have to be stable.\n    /// When removing a row pointer causes a slot to become empty,\n    /// the index is added to `emptied_collider_slots` and it can be reused.\n    /// This is done to avoid a linear scan of `colliders` for the first empty slot.\n    ///\n    /// Invariant: `self.maintains_colliders_invariant()`.\n    // TODO(centril,perf): Use a `SatsBuffer<T>` with `len/capacity: u32` to reduce size.\n    colliders: Vec<Vec<RowPointer>>,\n    /// Stack of emptied collider slots.\n    // TODO(centril,perf): Use a `SatsBuffer<T>` with `len/capacity: u32` to reduce size.\n    emptied_collider_slots: Vec<ColliderSlotIndex>,\n}\n\nimpl MemoryUsage for PointerMap {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            map,\n            colliders,\n            emptied_collider_slots,\n        } = self;\n        map.heap_usage() + colliders.heap_usage() + emptied_collider_slots.heap_usage()\n    }\n}\n\nstatic_assert_size!(PointerMap, 80);\n\n// Provides the public API.\nimpl PointerMap {\n    /// The number of colliding hashes in the map.\n    ///\n    /// If two hashes collide then this counts as 2.\n    pub fn num_collisions(&self) -> usize {\n        self.colliders.iter().map(|a| a.len()).sum()\n    }\n\n    /// The number hashes that do not collide.\n    pub fn num_non_collisions(&self) -> usize {\n        self.map.len() - (self.colliders.len() - self.emptied_collider_slots.len())\n    }\n\n    /// The number of pointers in the map. This is equal to the number of non-colliding hashes\n    /// plus the number of colliding hashes.\n    pub fn len(&self) -> usize {\n        self.num_collisions() + self.num_non_collisions()\n    }\n\n    /// Returns true if there are no pointers in the map.\n    pub fn is_empty(&self) -> bool {\n        self.map.is_empty()\n    }\n\n    /// Returns the row pointers associated with the given row `hash`.\n    pub fn pointers_for(&self, hash: RowHash) -> &[RowPointer] {\n        self.map.get(&hash).map_or(&[], |poc| self.poc_pointers(poc))\n    }\n\n    /// Returns the row pointers for `poc`.\n    fn poc_pointers<'a>(&'a self, poc: &'a PtrOrCollider) -> &'a [RowPointer] {\n        match poc.unpack() {\n            MapSlotRef::Pointer(ro) => slice::from_ref(ro),\n            MapSlotRef::Collider(ci) => &self.colliders[ci.idx()],\n        }\n    }\n\n    /// Returns the row pointers associated with the given row `hash`.\n    ///\n    /// Take care not to change the reserved bit of any row pointer\n    /// or this will mess up the internal state of the [`PointerMap`].\n    pub fn pointers_for_mut(&mut self, hash: RowHash) -> &mut [RowPointer] {\n        self.map.get_mut(&hash).map_or(&mut [], |poc| match poc.unpack_mut() {\n            MapSlotMut::Pointer(ro) => slice::from_mut(ro),\n            MapSlotMut::Collider(ci) => &mut self.colliders[ci.idx()],\n        })\n    }\n\n    /// Associates row `hash` with row `ptr`.\n    /// Returns whether `hash` was already associated with `ptr`\n    ///\n    /// Handles any hash conflicts for `hash`.\n    pub fn insert(&mut self, hash: RowHash, ptr: RowPointer) -> bool {\n        let mut was_in_map = false;\n\n        self.map\n            .entry(hash)\n            .and_modify(|v| match v.unpack() {\n                // Already in map; bail for idempotence.\n                MapSlotRef::Pointer(existing) if *existing == ptr => was_in_map = true,\n                // Stored inline => colliders list.\n                MapSlotRef::Pointer(existing) => {\n                    let ptrs = [*existing, ptr].map(ensure_ptr);\n                    let ci = match self.emptied_collider_slots.pop() {\n                        // Allocate a new colliders slot.\n                        None => {\n                            let ci = ColliderSlotIndex::new(self.colliders.len());\n                            self.colliders.push(ptrs.into());\n                            ci\n                        }\n                        // Reuse an empty slot.\n                        Some(ci) => {\n                            self.colliders[ci.idx()].extend(ptrs);\n                            ci\n                        }\n                    };\n                    *v = PtrOrCollider::collider(ci);\n                }\n                // Already using a list; add to it.\n                MapSlotRef::Collider(ci) => {\n                    let ptr = ensure_ptr(ptr);\n                    let colliders = &mut self.colliders[ci.idx()];\n                    if colliders.contains(&ptr) {\n                        // Already in map; bail for idempotence.\n                        //\n                        // O(n) check, but that's OK,\n                        // as we only regress perf in case we have > 5_000\n                        // collisions for this `hash`.\n                        //\n                        // Let `n` be the number of bits (`64`)\n                        // and `k` be the number of hashes.\n                        // The average number of collisions, `avg`,\n                        // according to the birthday problem is:\n                        // `avg = 2^(-n) * combinations(k, 2)`.\n                        // (Caveat: our hash function is not truly random.)\n                        //\n                        // Solving for `avg = 5000`, we get `k ≈ 5 * 10^11`.\n                        // That is, we need around half a trillion hashes before,\n                        // on average, getting 5_000 collisions.\n                        // So we can safely ignore this in terms of perf.\n                        return was_in_map = true;\n                    }\n                    colliders.push(ptr);\n                }\n            })\n            // 0 hashes so far.\n            .or_insert(PtrOrCollider::ptr(ptr));\n\n        was_in_map\n    }\n\n    /// Removes the association `hash -> ptr`.\n    ///\n    /// Returns whether the association was deleted.\n    pub fn remove(&mut self, hash: RowHash, ptr: RowPointer) -> bool {\n        'fun: {\n            let Entry::Occupied(mut entry) = self.map.entry(hash) else {\n                break 'fun false;\n            };\n\n            match entry.get().unpack() {\n                // Remove entry on `hash -> [ptr]`.\n                MapSlotRef::Pointer(o) if *o == ptr => drop(entry.remove()),\n                MapSlotRef::Pointer(_) => break 'fun false,\n                MapSlotRef::Collider(ci) => {\n                    // Find `ptr` in slot and remove.\n                    let slot = &mut self.colliders[ci.idx()];\n                    let Some(idx) = slot.iter().position(|o| *o == ptr) else {\n                        break 'fun false;\n                    };\n                    slot.swap_remove(idx);\n\n                    match slot.len() {\n                        // SAFETY: This never happens per `self.maintains_collider_invariant()`.\n                        0 => unsafe { hint::unreachable_unchecked() },\n                        // Simplify; don't use collider list since `hash -> [a_ptr]`.\n                        1 => *entry.get_mut() = PtrOrCollider::ptr(slot.pop().unwrap()),\n                        _ => break 'fun true,\n                    }\n\n                    // Slot is now empty; reuse later.\n                    self.emptied_collider_slots.push(ci);\n                }\n            }\n\n            true\n        }\n    }\n\n    /// Returns an iterator over all row hash x row pointer pairs.\n    pub fn iter(&self) -> impl '_ + Iterator<Item = (RowHash, RowPointer)> {\n        self.map\n            .iter()\n            .flat_map(|(hash, poc)| self.poc_pointers(poc).iter().map(|&ptr| (*hash, ptr)))\n    }\n\n    /// Merges `translate(src_pm)` into `self`.\n    ///\n    /// For every `(hash, ptr)` in `src_pm`,\n    /// inserts `(hash, translate(ptr))` into `self`.\n    pub fn merge_from(&mut self, src_pm: PointerMap, translate: impl Fn(RowPointer) -> RowPointer) {\n        for (hash, ptr) in src_pm.iter() {\n            self.insert(hash, translate(ptr));\n        }\n    }\n}\n\nimpl FromIterator<(RowHash, RowPointer)> for PointerMap {\n    fn from_iter<T: IntoIterator<Item = (RowHash, RowPointer)>>(iter: T) -> Self {\n        let mut map = PointerMap::default();\n        for (h, o) in iter {\n            let _ = map.insert(h, o);\n        }\n        map\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use core::hash::Hash;\n    use core::mem;\n    use itertools::Itertools;\n    use proptest::collection::vec;\n    use proptest::prelude::*;\n\n    type R = Result<(), TestCaseError>;\n\n    fn gen_row_pointer() -> impl Strategy<Value = RowPointer> {\n        (any::<PageOffset>(), any::<PageIndex>()).prop_map(|(po, pi)| RowPointer::new(false, pi, po, SquashedOffset(0)))\n    }\n\n    fn collect_entries(map: &PointerMap) -> Vec<(RowHash, PtrOrCollider)> {\n        map.map.iter().map(|(h, o)| (*h, *o)).collect::<Vec<_>>()\n    }\n\n    fn entry(hash: RowHash, ptr: RowPointer) -> (RowHash, PtrOrCollider) {\n        (hash, PtrOrCollider(ptr))\n    }\n\n    fn sorted<T: Ord + Copy>(xs: &[T]) -> Vec<T> {\n        xs.iter().copied().sorted().collect()\n    }\n\n    fn assert_ptrs_are(map: &mut PointerMap, hash: RowHash, ptrs: &[RowPointer]) -> R {\n        let ptrs = sorted(ptrs);\n        prop_assert_eq!(sorted(map.pointers_for(hash)), &*ptrs);\n        prop_assert_eq!(sorted(map.pointers_for_mut(hash)), ptrs);\n        Ok(())\n    }\n\n    fn assert_ptrs_and_len(map: &mut PointerMap, hash: RowHash, ptrs: &[RowPointer]) -> R {\n        assert_ptrs_are(map, hash, ptrs)?;\n        prop_assert_eq!(map.len(), ptrs.len());\n        prop_assert_eq!(map.is_empty(), ptrs.is_empty());\n        Ok(())\n    }\n\n    fn assert_collisions(map: &PointerMap, num_collisions: usize, num_not: usize) -> R {\n        prop_assert_eq!(map.num_collisions(), num_collisions);\n        prop_assert_eq!(map.num_non_collisions(), num_not);\n        Ok(())\n    }\n\n    fn ensure_unique<T: Eq + Hash>(xs: &[T]) -> R {\n        if !xs.iter().all_unique() {\n            return Err(TestCaseError::reject(\"all elements must be unique\"));\n        }\n        Ok(())\n    }\n\n    proptest! {\n        #[test]\n        fn insert_same_twice_idempotence(\n            (hash, ptrs) in (\n                any::<RowHash>(),\n                vec(gen_row_pointer(), 3..10)\n            )\n         ) {\n            ensure_unique(&ptrs)?;\n\n            let mut map = PointerMap::default();\n\n            // Test the inline case.\n            let ptr = ptrs[0];\n            prop_assert_eq!(map.insert(hash, ptr), false);\n            let old_map = map.clone(); // Savepoint\n            prop_assert_eq!(map.insert(hash, ptr), true); // Insert again.\n            prop_assert_eq!(&map, &old_map); // No change!\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &[ptr])?;\n            assert_collisions(&map, 0, 1)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [entry(hash, ptr)]);\n            prop_assert!(map.colliders.is_empty());\n            prop_assert!(map.emptied_collider_slots.is_empty());\n\n            // Test the colliders case.\n            // First insert the rest of the `ptrs`.\n            for ptr in &ptrs[1..] {\n                prop_assert_eq!(map.insert(hash, *ptr), false);\n            }\n            assert_ptrs_and_len(&mut map, hash, &ptrs)?;\n            assert_collisions(&map, ptrs.len(), 0)?;\n            // Now try inserting `ptr` again.\n            let old_map = map.clone(); // Savepoint\n            prop_assert_eq!(map.insert(hash, ptr), true); // Insert again.\n            prop_assert_eq!(&map, &old_map); // No change!\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &ptrs)?;\n            assert_collisions(&map, ptrs.len(), 0)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [(hash, ColliderSlotIndex::new(0).into())]);\n            prop_assert_eq!(map.colliders, [ptrs.to_owned()]);\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn insert_same_ptr_under_diff_hash(\n            (hashes, ptr) in (vec(any::<RowHash>(), 2..10), gen_row_pointer())\n        ) {\n            ensure_unique(&hashes)?;\n\n            // Insert `ptr` under all `hashes`.\n            let mut map = PointerMap::default();\n            for hash in &hashes {\n                prop_assert_eq!(map.insert(*hash, ptr), false);\n            }\n            // Check API state:\n            for hash in &hashes {\n                assert_ptrs_are(&mut map, *hash, &[ptr])?;\n            }\n            prop_assert_eq!(map.len(), hashes.len());\n            prop_assert_eq!(map.is_empty(), false);\n            assert_collisions(&map, 0, hashes.len())?;\n            // Check internal state.\n            let mut entries = collect_entries(&map);\n            entries.sort();\n            prop_assert_eq!(\n                entries,\n                hashes.iter().copied().sorted().map(|hash| entry(hash, ptr)).collect::<Vec<_>>()\n            );\n            prop_assert!(map.colliders.is_empty());\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn insert_different_for_same_hash_handles_collision(\n            (hash, ptrs) in (any::<RowHash>(), vec(gen_row_pointer(), 3..10))\n        ) {\n            ensure_unique(&ptrs)?;\n\n            let mut map = PointerMap::default();\n\n            // Insert `0` -> no collision.\n            prop_assert_eq!(map.insert(hash, ptrs[0]), false);\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &ptrs[..1])?;\n            assert_collisions(&map, 0, 1)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [entry(hash, ptrs[0])]);\n            prop_assert!(map.colliders.is_empty());\n            prop_assert!(map.emptied_collider_slots.is_empty());\n\n            // Insert `1` => `0` and `1` collide.\n            // This exercises \"make new collider slot\".\n            prop_assert_eq!(map.insert(hash, ptrs[1]), false);\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &ptrs[..2])?;\n            assert_collisions(&map, 2, 0)?;\n            // Check internal state.\n            let first_collider_idx = ColliderSlotIndex::new(0);\n            let one_collider_entry = [(hash, first_collider_idx.into())];\n            prop_assert_eq!(collect_entries(&map), one_collider_entry);\n            prop_assert_eq!(&*map.colliders, [ptrs[..2].to_owned()]);\n            prop_assert!(map.emptied_collider_slots.is_empty());\n\n            // This exercises \"reuse collider slot\".\n            for (ptr, i) in ptrs[2..].iter().copied().zip(2..) {\n                // Insert `i = 2..`\n                prop_assert_eq!(map.insert(hash, ptr), false);\n                // Check API state:\n                assert_ptrs_and_len(&mut map, hash, &ptrs[..=i])?;\n                assert_collisions(&map, i + 1, 0)?;\n                // Check internal state.\n                prop_assert_eq!(collect_entries(&map), one_collider_entry);\n                prop_assert_eq!(&*map.colliders, [ptrs[..=i].to_owned()]);\n                prop_assert!(map.emptied_collider_slots.is_empty());\n            }\n\n            // Remove all but the last one.\n            let last = ptrs.len() - 1;\n            for ptr in &ptrs[..last] {\n                prop_assert!(map.remove(hash, *ptr));\n            }\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &ptrs[last..])?;\n            assert_collisions(&map, 0, 1)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [entry(hash, ptrs[last])]);\n            prop_assert_eq!(&*map.colliders, [vec![]]);\n            prop_assert_eq!(&*map.emptied_collider_slots, [first_collider_idx]);\n\n            // Insert `pennultimate` => `last` and `pennultimate` collide.\n            // This exercises \"reuse collider slot\".\n            let penultimate = last - 1;\n            prop_assert_eq!(map.insert(hash, ptrs[penultimate]), false);\n            // Check API state:\n            let pointers = ptrs[penultimate..].iter().copied().rev().collect::<Vec<_>>();\n            assert_ptrs_and_len(&mut map, hash, &pointers)?;\n            assert_collisions(&map, 2, 0)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), one_collider_entry);\n            prop_assert_eq!(&*map.colliders, [pointers]);\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn remove_non_existing_fails((hash, ptr) in (any::<RowHash>(), gen_row_pointer())) {\n            let mut map = PointerMap::default();\n            prop_assert_eq!(map.remove(hash, ptr), false);\n        }\n\n        #[test]\n        fn remove_uncollided_hash_works((hash, ptr) in (any::<RowHash>(), gen_row_pointer())) {\n            let mut map = PointerMap::default();\n\n            // Insert and then remove.\n            prop_assert_eq!(map.insert(hash, ptr), false);\n            prop_assert_eq!(map.remove(hash, ptr), true);\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &[])?;\n            assert_collisions(&map, 0, 0)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), []);\n            prop_assert!(map.colliders.is_empty());\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn remove_same_hash_wrong_ptr_fails(\n            (hash, ptr_a, ptr_b) in (\n                any::<RowHash>(),\n                gen_row_pointer(),\n                gen_row_pointer(),\n            )\n        ) {\n            ensure_unique(&[ptr_a, ptr_b])?;\n\n            let mut map = PointerMap::default();\n\n            // Insert `ptr_a` and then remove `ptr_b`.\n            prop_assert_eq!(map.insert(hash, ptr_a), false);\n            prop_assert_eq!(map.remove(hash, ptr_b), false);\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, &[ptr_a])?;\n            assert_collisions(&map, 0, 1)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [entry(hash, ptr_a)]);\n            prop_assert!(map.colliders.is_empty());\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn remove_collided_hash_wrong_ptr_fails(\n            (hash, ptrs) in (any::<RowHash>(), vec(gen_row_pointer(), 3..10))\n        ) {\n            ensure_unique(&ptrs)?;\n\n            let mut map = PointerMap::default();\n\n            // Insert `ptrs[0..last]` and then remove `ptrs[last]`.\n            let last = ptrs.len() - 1;\n            let but_last = &ptrs[0..last];\n            for ptr in but_last {\n                prop_assert_eq!(map.insert(hash, *ptr), false);\n            }\n            prop_assert_eq!(map.remove(hash, ptrs[last]), false);\n            // Check API state:\n            assert_ptrs_and_len(&mut map, hash, but_last)?;\n            assert_collisions(&map, but_last.len(), 0)?;\n            // Check internal state.\n            prop_assert_eq!(collect_entries(&map), [(hash, ColliderSlotIndex::new(0).into())]);\n            prop_assert_eq!(&*map.colliders, [but_last.to_owned()]);\n            prop_assert!(map.emptied_collider_slots.is_empty());\n        }\n\n        #[test]\n        fn remove_collided_hash_reduction_works(\n            (hash, mut ptr_a, mut ptr_b, pick_b) in (\n                any::<RowHash>(),\n                gen_row_pointer(),\n                gen_row_pointer(),\n                any::<bool>(),\n            )\n        ) {\n            ensure_unique(&[ptr_a, ptr_b])?;\n\n            // Insert `ptr_a` and `ptr_b`.\n            let mut map = PointerMap::default();\n            prop_assert_eq!(map.insert(hash, ptr_a), false);\n            prop_assert_eq!(map.insert(hash, ptr_b), false);\n            assert_collisions(&map, 2, 0)?;\n\n            // Now remove `ptr_a` or `ptr_b`.\n            if pick_b {\n                mem::swap(&mut ptr_a, &mut ptr_b);\n            }\n            prop_assert_eq!(map.remove(hash, ptr_b), true);\n            assert_ptrs_and_len(&mut map, hash, &[ptr_a])?;\n            assert_collisions(&map, 0, 1)?;\n            prop_assert_eq!(map.emptied_collider_slots, [ColliderSlotIndex(0)]);\n        }\n\n        #[test]\n        fn remove_collided_hash_works(\n            (hash, mut ptrs, pick_remove_idx) in (\n                any::<RowHash>(),\n                vec(gen_row_pointer(), 3..10),\n                0..10usize,\n            )\n        ) {\n            ensure_unique(&ptrs)?;\n\n            let pick_remove_idx = pick_remove_idx.min(ptrs.len() - 1);\n\n            // Insert all in `ptrs`.\n            let mut map = PointerMap::default();\n            for ptr in &ptrs {\n                prop_assert_eq!(map.insert(hash, *ptr), false);\n            }\n            assert_collisions(&map, ptrs.len(), 0)?;\n\n            // Now remove `ptrs[pick_remove_idx]`.\n            let ptr_to_remove = ptrs.remove(pick_remove_idx);\n            prop_assert_eq!(map.remove(hash, ptr_to_remove), true);\n            assert_ptrs_and_len(&mut map, hash, &ptrs)?;\n            assert_collisions(&map, ptrs.len(), 0)?;\n            prop_assert_eq!(sorted(&map.colliders[0]), sorted(&ptrs));\n            prop_assert_eq!(map.emptied_collider_slots, []);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/read_column.rs",
    "content": "//! Provides a trait [`ReadColumn`] for extracting a single column from a [`crate::table::RowRef`].\n//! This is desirable as frequently, e.g. when evaluating filtered queries,\n//! we are interested in only a single column (or a small set of columns),\n//! and would like to avoid the allocation required by a `ProductValue`.\n\nuse crate::{bflatn_from, indexes::PageOffset, table::RowRef};\nuse spacetimedb_sats::layout::{AlgebraicTypeLayout, PrimitiveType, ProductTypeElementLayout, Size, VarLenType};\nuse spacetimedb_sats::{\n    algebraic_value::{ser::ValueSerializer, Packed},\n    i256,\n    sum_value::SumTag,\n    u256, AlgebraicType, AlgebraicValue, ArrayValue, ProductType, ProductValue, SumValue, F32, F64,\n};\nuse std::{cell::Cell, mem};\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum TypeError {\n    #[error(\n        \"Attempt to read column {} of a product with only {} columns of type {:?}\",\n        desired,\n        found.elements.len(),\n        found,\n    )]\n    IndexOutOfBounds { desired: usize, found: ProductType },\n    #[error(\"Attempt to read a column at type `{desired}`, but the column's type is {found:?}\")]\n    WrongType {\n        desired: &'static str,\n        found: AlgebraicType,\n    },\n}\n\n/// Types which can be stored in a column of a row,\n/// and can be extracted directly from a row.\n///\n/// # Safety\n///\n/// The implementor must define `is_compatible_type` to return `true` only for `AlgebraicTypeLayout`s\n/// for which `unchecked_read_column` is safe.\n/// The provided `read_column` method uses `is_compatible_type` to detect type errors,\n/// and calls `unchecked_read_column` if `is_compatible_type` returns true.\npub unsafe trait ReadColumn: Sized {\n    /// Is `ty` compatible with `Self`?\n    ///\n    /// The definition of \"compatible\" here is left to the implementor,\n    /// to be defined by `Self::is_compatible_type`.\n    ///\n    /// For most types,\"compatibility\" will mean that each Rust type which implements `ReadColumn`\n    /// has exactly one corresponding [`AlgebraicTypeLayout`] which represents it,\n    /// and the column in `table.row_layout` must be of that type.\n    ///\n    /// Notable exceptions are [`AlgebraicValue`], [`ProductValue`] and [`SumValue`].\n    /// Any `ProductTypeLayout` is compatible with `ProductValue`,\n    /// any `SumTypeLayout` is compatible with `SumValue`,\n    /// and any `AlgebraicTypeLayout` at all is compatible with `AlgebraicValue`.\n    fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool;\n\n    /// Extract a value of type `Self` from the row pointed to by `row_ref`\n    /// which is stored in the column defined by `layout`.\n    ///\n    /// # Safety\n    ///\n    /// `layout` must appear as a column in the `table.row_layout.product().elements`,\n    /// *not* to a nested field of a column which is a product or sum value.\n    /// That column must have the same layout as `layout`.\n    /// This restriction may be loosened in the future.\n    ///\n    /// Assuming that the `row_ref` refers to a properly-aligned row,\n    /// adding the `layout.offset` must result in a properly-aligned value of that compatible type.\n    ///\n    /// `layout.ty` must be compatible with `Self`.\n    /// The definition of \"compatible\" here is left to the implementor,\n    /// to be defined by `Self::is_compatible_type`.\n    ///\n    /// For most types,\"compatibility\" will mean that each Rust type which implements `ReadColumn`\n    /// has exactly one corresponding [`AlgebraicTypeLayout`] which represents it,\n    /// and the column in `table.row_layout` must be of that type.\n    ///\n    /// Notable exceptions are [`AlgebraicValue`], [`ProductValue`] and [`SumValue`].\n    /// Any `ProductTypeLayout` is compatible with `ProductValue`,\n    /// any `SumTypeLayout` is compatible with `SumValue`,\n    /// and any `AlgebraicTypeLayout` at all is compatible with `AlgebraicValue`.\n    ///\n    /// # Notes for implementors\n    ///\n    /// Implementors may depend on all of the above safety requirements,\n    /// and on the validity of the `row_ref`.\n    /// Assuming all of the above safety requirements are met and the `row_ref` refers to a valid row,\n    /// this method *must never* invoke Undefined Behavior.\n    ///\n    /// Implementors should carefully study the BFLATN format.\n    /// Currently BFLATN lacks a normative specification,\n    /// so implementors should read the definitions in [`layout.rs`], [`bflatn_to.rs`] and [`bflatn_from.rs`].\n    /// A few highlights are included here:\n    ///\n    /// - Variable-length columns, i.e. `AlgebraicType::String`, `AlgebraicType::Array` and `AlgebraicType::Map`\n    ///   are stored within the row as [`crate::var_len::VarLenRef`s],\n    ///   which refer to an intrusive linked list of 62-byte \"granules\",\n    ///   allocated separately in a space starting from the end of the page.\n    ///   Strings are stored as UTF-8 bytes; all other var-len types are stored as BSATN-encoded bytes.\n    ///\n    /// - Fixed-length columns, i.e. all types not listed above as variable-length,\n    ///   are stored inline at a known offset.\n    ///   Their layout generally matches the C ABI on an x86_64 Linux machine,\n    ///   with the notable exception of sum types, since the C ABI doesn't define a layout for sums.\n    ///\n    /// - Fixed-length columns are stored in order, with padding between to ensure proper alignment.\n    ///\n    /// - Primitive (non-compound) fixed-length types, i.e. integers, floats and booleans,\n    ///   have alignment equal to their size.\n    ///\n    /// - Integers are stored little-endian.\n    ///\n    /// - Floats are stored by bitwise converting to integers as per IEEE-754,\n    ///   then storing those integers little-endian.\n    ///\n    /// - Booleans are stored as `u8`, i.e. bytes, restricted to the values `0` and `1`.\n    ///\n    /// - Products store their elements in order, with padding between to ensure proper alignment.\n    ///\n    /// - The first element of a product has offset 0.\n    ///\n    /// - The alignment of a product is the maximum alignment of its elements,\n    ///   or 1 for the empty product.\n    ///\n    /// - The size of a product is the number of bytes required to store its elements, including padding,\n    ///   plus trailing padding bytes so that the size is a multiple of the alignment.\n    ///\n    /// - Sums store their payload at offset 0, followed by a 1-byte tag.\n    ///\n    /// - The alignment of a sum is the maximum alignment of its variants' payloads.\n    ///\n    /// - The size of a sum is the maximum size of its variants' payloads, plus 1 (the tag),\n    ///   plus trailing padding bytes so that the size is a multiple of the alignment.\n    ///\n    /// - The offset of a sum's tag bit is the maximum size of its variants' payloads.\n    unsafe fn unchecked_read_column(row_ref: RowRef<'_>, layout: &ProductTypeElementLayout) -> Self;\n\n    /// Check that the `idx`th column of the row type stored by `row_ref` is compatible with `Self`,\n    /// and read the value of that column from `row_ref`.\n    fn read_column(row_ref: RowRef<'_>, idx: usize) -> Result<Self, TypeError> {\n        let layout = row_ref.row_layout().product();\n\n        // Look up the `ProductTypeElementLayout` of the requested column,\n        // or return an error on an out-of-bounds index.\n        let col = layout.elements.get(idx).ok_or_else(|| TypeError::IndexOutOfBounds {\n            desired: idx,\n            found: layout.product_type(),\n        })?;\n\n        // Check that the requested column is of the expected type.\n        if !Self::is_compatible_type(&col.ty) {\n            return Err(TypeError::WrongType {\n                desired: std::any::type_name::<Self>(),\n                found: col.ty.algebraic_type(),\n            });\n        }\n\n        Ok(unsafe {\n            // SAFETY:\n            // - We trust that the `row_ref.table` knows its own layout,\n            //   and we've derived our type and layout info from it,\n            //   so they are correct.\n            // - We trust `Self::is_compatible_type`, and it returned `true`,\n            //   so the column must be of appropriate type.\n            Self::unchecked_read_column(row_ref, col)\n        })\n    }\n}\n\nunsafe impl ReadColumn for bool {\n    fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool {\n        matches!(ty, AlgebraicTypeLayout::Primitive(PrimitiveType::Bool))\n    }\n\n    unsafe fn unchecked_read_column(row_ref: RowRef<'_>, layout: &ProductTypeElementLayout) -> Self {\n        debug_assert!(Self::is_compatible_type(&layout.ty));\n\n        let (page, offset) = row_ref.page_and_offset();\n        let col_offset = offset + PageOffset(layout.offset);\n\n        let data = page.get_row_data(col_offset, Size(mem::size_of::<Self>() as u16));\n        let data: *const bool = data.as_ptr().cast();\n        // SAFETY: We trust that the `row_ref` refers to a valid, initialized row,\n        // and that the `offset_in_bytes` refers to a column of type `Bool` within that row.\n        // A valid row can never have a column of an invalid value,\n        // and no byte in `Page.row_data` is ever uninit,\n        // so `data` must be initialized as either 0 or 1.\n        unsafe { *data }\n    }\n}\n\nmacro_rules! impl_read_column_number {\n    ($primitive_type:ident => $native_type:ty) => {\n        unsafe impl ReadColumn for $native_type {\n            fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool {\n                matches!(ty, AlgebraicTypeLayout::Primitive(PrimitiveType::$primitive_type))\n            }\n\n            unsafe fn unchecked_read_column(\n                row_ref: RowRef<'_>,\n                layout: &ProductTypeElementLayout,\n            ) -> Self {\n                debug_assert!(Self::is_compatible_type(&layout.ty));\n\n                let (page, offset) = row_ref.page_and_offset();\n                let col_offset = offset + PageOffset(layout.offset);\n\n                let data = page.get_row_data(col_offset, Size(mem::size_of::<Self>() as u16));\n                let data: Result<[u8; mem::size_of::<Self>()], _> = data.try_into();\n                // SAFETY: `<[u8; N] as TryFrom<&[u8]>` succeeds if and only if the slice's length is `N`.\n                // We used `mem::size_of::<Self>()` as both the length of the slice and the array,\n                // so we know them to be equal.\n                let data = unsafe { data.unwrap_unchecked() };\n\n                Self::from_le_bytes(data)\n            }\n        }\n    };\n\n    ($($primitive_type:ident => $native_type:ty);* $(;)*) => {\n        $(impl_read_column_number!($primitive_type => $native_type);)*\n    };\n}\n\nimpl_read_column_number! {\n    I8 => i8;\n    U8 => u8;\n    I16 => i16;\n    U16 => u16;\n    I32 => i32;\n    U32 => u32;\n    I64 => i64;\n    U64 => u64;\n    I128 => i128;\n    U128 => u128;\n    I256 => i256;\n    U256 => u256;\n    F32 => f32;\n    F64 => f64;\n}\n\nunsafe impl ReadColumn for AlgebraicValue {\n    fn is_compatible_type(_ty: &AlgebraicTypeLayout) -> bool {\n        true\n    }\n    unsafe fn unchecked_read_column(row_ref: RowRef<'_>, layout: &ProductTypeElementLayout) -> Self {\n        let curr_offset = Cell::new(layout.offset as usize);\n        let blob_store = row_ref.blob_store();\n        let (page, page_offset) = row_ref.page_and_offset();\n        let fixed_bytes = page.get_row_data(page_offset, row_ref.row_layout().size());\n\n        // SAFETY:\n        // 1. Our requirements on `row_ref` and `layout` mean that the column is valid at `layout`.\n        // 2. As a result of the above, all `VarLenRef`s in the column are valid.\n        // 3. Our requirements on `offset_in_bytes` mean that our `curr_offset` is valid.\n        let res = unsafe {\n            bflatn_from::serialize_value(ValueSerializer, fixed_bytes, page, blob_store, &curr_offset, &layout.ty)\n        };\n\n        debug_assert!(res.is_ok());\n\n        // SAFETY: `ValueSerializer` is infallible.\n        unsafe { res.unwrap_unchecked() }\n    }\n}\n\nmacro_rules! impl_read_column_via_av {\n    ($av_pattern:pat => $into_method:ident => $native_type:ty) => {\n        unsafe impl ReadColumn for $native_type {\n            fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool {\n                matches!(ty, $av_pattern)\n            }\n\n            unsafe fn unchecked_read_column(\n                row_ref: RowRef<'_>,\n                layout: &ProductTypeElementLayout,\n            ) -> Self {\n                debug_assert!(Self::is_compatible_type(&layout.ty));\n\n                // SAFETY:\n                // - Any layout is valid for `AlgebraicValue`, including our `layout`.\n                // - Forward requirements on `offset_in_bytes`.\n                let av = unsafe { AlgebraicValue::unchecked_read_column(row_ref, layout) };\n\n                let res = av.$into_method();\n\n                debug_assert!(res.is_ok());\n\n                // SAFETY: We trust that the value `row_ref + offset_in_bytes` is of type `layout`,\n                // and that `layout` is the layout for `Self`,\n                // so the `av` above must be a `Self`.\n                unsafe { res.unwrap_unchecked() }\n            }\n        }\n    };\n\n    ($($av_pattern:pat => $into_method:ident => $native_type:ty);* $(;)*) => {\n        $(impl_read_column_via_av!($av_pattern => $into_method => $native_type);)*\n    };\n}\n\nimpl_read_column_via_av! {\n    AlgebraicTypeLayout::VarLen(VarLenType::String) => into_string => Box<str>;\n    AlgebraicTypeLayout::VarLen(VarLenType::Array(_)) => into_array => ArrayValue;\n    AlgebraicTypeLayout::Sum(_) => into_sum => SumValue;\n    AlgebraicTypeLayout::Product(_) => into_product => ProductValue;\n}\n\nmacro_rules! impl_read_column_via_from {\n    ($($base:ty => $target:ty);* $(;)*) => {\n        $(\n            unsafe impl ReadColumn for $target {\n                fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool {\n                    <$base>::is_compatible_type(ty)\n                }\n\n                unsafe fn unchecked_read_column(row_ref: RowRef<'_>, layout: &ProductTypeElementLayout) -> Self {\n                    // SAFETY: We use `$base`'s notion of compatible types, so we can forward promises.\n                    <$target>::from(unsafe { <$base>::unchecked_read_column(row_ref, layout) })\n                }\n            }\n        )*\n    };\n}\n\nimpl_read_column_via_from! {\n    u64 => spacetimedb_primitives::ArgId;\n    u16 => spacetimedb_primitives::ColId;\n    u32 => spacetimedb_primitives::ViewId;\n    u32 => spacetimedb_primitives::TableId;\n    u32 => spacetimedb_primitives::IndexId;\n    u32 => spacetimedb_primitives::ConstraintId;\n    u32 => spacetimedb_primitives::SequenceId;\n    u32 => spacetimedb_primitives::ScheduleId;\n    u128 => Packed<u128>;\n    i128 => Packed<i128>;\n    u256 => Box<u256>;\n    i256 => Box<i256>;\n    f32 => F32;\n    f64 => F64;\n}\n\n/// SAFETY: `is_compatible_type` only returns true for sum types,\n/// and any sum value stores the tag first in BFLATN.\nunsafe impl ReadColumn for SumTag {\n    fn is_compatible_type(ty: &AlgebraicTypeLayout) -> bool {\n        matches!(ty, AlgebraicTypeLayout::Sum(_))\n    }\n\n    unsafe fn unchecked_read_column(row_ref: RowRef<'_>, layout: &ProductTypeElementLayout) -> Self {\n        debug_assert!(Self::is_compatible_type(&layout.ty));\n\n        let (page, offset) = row_ref.page_and_offset();\n        let col_offset = offset + PageOffset(layout.offset);\n\n        let data = page.get_row_data(col_offset, Size(1));\n        let data: Result<[u8; 1], _> = data.try_into();\n        // SAFETY: `<[u8; 1] as TryFrom<&[u8]>` succeeds if and only if the slice's length is `1`.\n        // We used `1` as both the length of the slice and the array, so we know them to be equal.\n        let [data] = unsafe { data.unwrap_unchecked() };\n\n        Self(data)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::table::test::table;\n    use crate::{blob_store::HashMapBlobStore, page_pool::PagePool};\n    use proptest::{prelude::*, prop_assert_eq, proptest, test_runner::TestCaseResult};\n    use spacetimedb_sats::{product, proptest::generate_typed_row};\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n\n        #[test]\n        /// Test that `AlgebraicValue::read_column` returns expected values.\n        ///\n        /// That is, test that, for any row type and any row value,\n        /// inserting the row, then doing `AlgebraicValue::read_column` on each column of the row\n        /// returns the expected value.\n        fn read_column_same_value((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty);\n\n            let (_, row_ref) = table.insert(&pool, &mut blob_store, &val).unwrap();\n\n            for (idx, orig_col_value) in val.into_iter().enumerate() {\n                let read_col_value = row_ref.read_col::<AlgebraicValue>(idx).unwrap();\n                prop_assert_eq!(orig_col_value, read_col_value);\n            }\n        }\n\n        #[test]\n        /// Test that trying to read a column at a type more specific than `AlgebraicValue`\n        /// which does not match the actual column type\n        /// returns an appropriate error.\n        fn read_column_wrong_type((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty.clone());\n\n            let (_, row_ref) = table.insert(&pool, &mut blob_store, &val).unwrap();\n\n            for (idx, col_ty) in ty.elements.iter().enumerate() {\n                assert_wrong_type_error::<u8>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U8)?;\n                assert_wrong_type_error::<i8>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I8)?;\n                assert_wrong_type_error::<u16>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U16)?;\n                assert_wrong_type_error::<i16>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I16)?;\n                assert_wrong_type_error::<u32>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U32)?;\n                assert_wrong_type_error::<i32>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I32)?;\n                assert_wrong_type_error::<u64>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U64)?;\n                assert_wrong_type_error::<i64>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I64)?;\n                assert_wrong_type_error::<u128>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U128)?;\n                assert_wrong_type_error::<i128>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I128)?;\n                assert_wrong_type_error::<u256>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::U256)?;\n                assert_wrong_type_error::<i256>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::I256)?;\n                assert_wrong_type_error::<f32>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::F32)?;\n                assert_wrong_type_error::<f64>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::F64)?;\n                assert_wrong_type_error::<bool>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::Bool)?;\n                assert_wrong_type_error::<Box<str>>(row_ref, idx, &col_ty.algebraic_type, AlgebraicType::String)?;\n            }\n        }\n\n        #[test]\n        /// Test that trying to read a column which does not exist,\n        /// i.e. with an out-of-bounds index,\n        /// returns an appropriate error.\n        fn read_column_out_of_bounds((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty.clone());\n\n            let (_, row_ref) = table.insert(&pool, &mut blob_store, &val).unwrap();\n\n            let oob = ty.elements.len();\n\n            match row_ref.read_col::<AlgebraicValue>(oob) {\n                Err(TypeError::IndexOutOfBounds { desired, found }) => {\n                    prop_assert_eq!(desired, oob);\n                    // Constructing a table changes the `ProductType` by adding column names\n                    // if the type has `None` for its element names,\n                    // so we can't blindly `prop_assert_eq!(found, ty)`.\n                    // Instead, check that they have the same number of elements\n                    // and that each element has the same type.\n                    prop_assert_eq!(found.elements.len(), ty.elements.len());\n                    for (found_col, ty_col) in found.elements.iter().zip(ty.elements.iter()) {\n                        prop_assert_eq!(&found_col.algebraic_type, &ty_col.algebraic_type);\n                    }\n                }\n                Err(e) => panic!(\"Expected TypeError::IndexOutOfBounds but found {e:?}\"),\n                Ok(val) => panic!(\"Expected error but found Ok({val:?})\"),\n            }\n        }\n    }\n\n    /// Assert, if and only if `col_ty` is not `correct_col_ty`,\n    /// that `row_ref.read_col::<Col>(col_idx)` returns a `TypeError::WrongType`.\n    ///\n    /// If `col_ty == correct_col_ty`, do nothing.\n    fn assert_wrong_type_error<Col: ReadColumn + PartialEq + std::fmt::Debug>(\n        row_ref: RowRef<'_>,\n        col_idx: usize,\n        col_ty: &AlgebraicType,\n        correct_col_ty: AlgebraicType,\n    ) -> TestCaseResult {\n        if col_ty != &correct_col_ty {\n            match row_ref.read_col::<Col>(col_idx) {\n                Err(TypeError::WrongType { desired, found }) => {\n                    prop_assert_eq!(desired, std::any::type_name::<Col>());\n                    prop_assert_eq!(&found, col_ty);\n                }\n                Err(e) => panic!(\"Expected TypeError::WrongType but found {e:?}\"),\n                Ok(val) => panic!(\"Expected error but found Ok({val:?})\"),\n            }\n        }\n        Ok(())\n    }\n\n    /// Define a test or tests which constructs a row containing a known value of a known type,\n    /// then uses `ReadColumn::read_column` to extract that type as a native type,\n    /// e.g. a Rust integer,\n    /// and asserts that the extracted value is as expected.\n    macro_rules! test_read_column_primitive {\n        ($name:ident { $algebraic_type:expr => $rust_type:ty = $val:expr }) => {\n            #[test]\n            fn $name() {\n                let pool = PagePool::new_for_test();\n                let mut blob_store = HashMapBlobStore::default();\n                let mut table = table(ProductType::from_iter([$algebraic_type]));\n\n                let val: $rust_type = $val;\n                let (_, row_ref) = table.insert(&pool, &mut blob_store, &product![val.clone()]).unwrap();\n\n                assert_eq!(val, row_ref.read_col::<$rust_type>(0).unwrap());\n            }\n        };\n\n\n        ($($name:ident { $algebraic_type:expr => $rust_type:ty = $val:expr };)*) => {\n            $(test_read_column_primitive! {\n                $name { $algebraic_type => $rust_type = $val }\n            })*\n        }\n    }\n\n    test_read_column_primitive! {\n        read_column_i8 { AlgebraicType::I8 => i8 = i8::MAX };\n        read_column_u8 { AlgebraicType::U8 => u8 = 0xa5 };\n        read_column_i16 { AlgebraicType::I16 => i16 = i16::MAX };\n        read_column_u16 { AlgebraicType::U16 => u16 = 0xa5a5 };\n        read_column_i32 { AlgebraicType::I32 => i32 = i32::MAX };\n        read_column_u32 { AlgebraicType::U32 => u32 = 0xa5a5a5a5 };\n        read_column_i64 { AlgebraicType::I64 => i64 = i64::MAX };\n        read_column_u64 { AlgebraicType::U64 => u64 = 0xa5a5a5a5_a5a5a5a5 };\n        read_column_i128 { AlgebraicType::I128 => i128 = i128::MAX };\n        read_column_u128 { AlgebraicType::U128 => u128 = 0xa5a5a5a5_a5a5a5a5_a5a5a5a5_a5a5a5a5 };\n        read_column_i256 { AlgebraicType::I256 => i256 = i256::MAX };\n        read_column_u256 { AlgebraicType::U256 => u256 =\n            u256::from_words(\n                0xa5a5a5a5_a5a5a5a5_a5a5a5a5_a5a5a5a5,\n                0xa5a5a5a5_a5a5a5a5_a5a5a5a5_a5a5a5a5\n            )\n        };\n\n        read_column_f32 { AlgebraicType::F32 => f32 = 1.0 };\n        read_column_f64 { AlgebraicType::F64 => f64 = 1.0 };\n\n        read_column_bool { AlgebraicType::Bool => bool = true };\n\n        read_column_empty_string { AlgebraicType::String => Box<str> = \"\".into() };\n\n        // Use a short string which fits in a single granule.\n        read_column_short_string { AlgebraicType::String => Box<str> = \"short string\".into() };\n\n        // Use a medium-sized string which takes multiple granules.\n        read_column_medium_string { AlgebraicType::String => Box<str> = \"medium string.\".repeat(16).into() };\n\n        // Use a long string which will hit the blob store.\n        read_column_long_string { AlgebraicType::String => Box<str> = \"long string. \".repeat(2048).into() };\n\n        read_sum_value_plain { AlgebraicType::simple_enum([\"a\", \"b\"].into_iter()) => SumValue = SumValue::new_simple(1) };\n        read_sum_tag_plain { AlgebraicType::simple_enum([\"a\", \"b\"].into_iter()) => SumTag = SumTag(1) };\n    }\n\n    #[test]\n    fn read_sum_tag_from_sum_with_payload() {\n        let algebraic_type = AlgebraicType::sum([(\"a\", AlgebraicType::U8), (\"b\", AlgebraicType::U16)]);\n\n        let pool = PagePool::new_for_test();\n        let mut blob_store = HashMapBlobStore::default();\n        let mut table = table(ProductType::from([algebraic_type]));\n\n        let val = SumValue::new(1, 42u16);\n        let (_, row_ref) = table.insert(&pool, &mut blob_store, &product![val.clone()]).unwrap();\n\n        assert_eq!(val.tag, row_ref.read_col::<SumTag>(0).unwrap().0);\n    }\n}\n"
  },
  {
    "path": "crates/table/src/row_hash.rs",
    "content": "//! Provides the function [`hash_row_in_page(hasher, page, fixed_offset, ty)`]\n//! which hashes `value = page.get_row_data(fixed_offset, fixed_row_size)` typed at `ty`\n//! and associated var len objects in `value` into `hasher`.\n\nuse super::{\n    bflatn_from::read_tag,\n    indexes::{Bytes, PageOffset},\n    page::Page,\n    var_len::VarLenRef,\n};\nuse crate::{bflatn_from::vlr_blob_bytes, blob_store::BlobStore};\nuse core::hash::{Hash as _, Hasher};\nuse core::mem;\nuse core::str;\nuse spacetimedb_sats::layout::{\n    align_to, AlgebraicTypeLayout, HasLayout, ProductTypeLayoutView, RowTypeLayout, VarLenType,\n};\nuse spacetimedb_sats::{algebraic_value::ser::concat_byte_chunks_buf, bsatn::Deserializer, i256, u256, F32, F64};\n\n/// Hashes the row in `page` where the fixed part starts at `fixed_offset`\n/// and lasts `ty.size()` bytes. This region is typed at `ty`.\n///\n/// Note that the hash of an in-page row might not be the same as\n/// hashing the row as its equivalent `ProductValue`.\n///\n/// # Safety\n///\n/// 1. the `fixed_offset` must point at a row in `page` lasting `ty.size()` bytes.\n/// 2. the row must be a valid `ty`.\n/// 3. for any `vlr: VarLenRef` stored in the row,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\npub unsafe fn hash_row_in_page(\n    hasher: &mut impl Hasher,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    fixed_offset: PageOffset,\n    ty: &RowTypeLayout,\n) {\n    let fixed_bytes = page.get_row_data(fixed_offset, ty.size());\n\n    // SAFETY:\n    // - Per 1. and 2., `fixed_bytes` points at a row in `page` valid for `ty`.\n    // - Per 3., for any `vlr: VarLenRef` stored in `fixed_bytes`,\n    //   `vlr.first_offset` is either `NULL` or points to a valid granule in `page`.\n    unsafe { hash_product(hasher, fixed_bytes, page, blob_store, &mut 0, ty.product()) };\n}\n\n/// Hashes every product field in `value = &bytes[range_move(0..ty.size(), *curr_offset)]`\n/// which is typed at `ty`.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\nunsafe fn hash_product(\n    hasher: &mut impl Hasher,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: &mut usize,\n    ty: ProductTypeLayoutView<'_>,\n) {\n    let base_offset = *curr_offset;\n    for elem_ty in ty.elements {\n        *curr_offset = base_offset + elem_ty.offset as usize;\n\n        // SAFETY: By 1., `value` is valid at `ty`,\n        // so it follows that valid and properly aligned sub-`value`s\n        // are valid `elem_ty.ty`s.\n        // By 2., and the above, it follows that sub-`value`s won't have dangling `VarLenRef`s.\n        unsafe {\n            hash_value(hasher, bytes, page, blob_store, curr_offset, &elem_ty.ty);\n        }\n    }\n}\n\n/// Hashes `value = &bytes[range_move(0..ty.size(), *curr_offset)]` typed at `ty`\n/// and advances the `curr_offset`.\n///\n/// SAFETY:\n/// 1. the `value` must be valid at type `ty` and properly aligned for `ty`.\n/// 2. for any `vlr: VarLenRef` stored in `value`,\n///    `vlr.first_offset` must either be `NULL` or point to a valid granule in `page`.\nunsafe fn hash_value(\n    hasher: &mut impl Hasher,\n    bytes: &Bytes,\n    page: &Page,\n    blob_store: &dyn BlobStore,\n    curr_offset: &mut usize,\n    ty: &AlgebraicTypeLayout,\n) {\n    debug_assert_eq!(\n        *curr_offset,\n        align_to(*curr_offset, ty.align()),\n        \"curr_offset {} insufficiently aligned for type {:?}\",\n        *curr_offset,\n        ty\n    );\n\n    match ty {\n        AlgebraicTypeLayout::Sum(ty) => {\n            // Read and hash the tag of the sum value.\n            let (tag, data_ty) = read_tag(bytes, ty, *curr_offset);\n            tag.hash(hasher);\n\n            // Hash the variant data value.\n            let mut data_offset = *curr_offset + ty.offset_of_variant_data(tag);\n            // SAFETY: `value` is valid at `ty` so given `tag`,\n            // we know `data_value = &bytes[range_move(0..data_ty.size(), data_offset))`\n            // is valid at `data_ty`.\n            // By 2., and the above, we also know that `data_value` won't have dangling `VarLenRef`s.\n            unsafe { hash_value(hasher, bytes, page, blob_store, &mut data_offset, data_ty) };\n            *curr_offset += ty.size();\n        }\n        AlgebraicTypeLayout::Product(ty) => {\n            // SAFETY: `value` was valid at `ty` and `VarLenRef`s won't be dangling.\n            unsafe { hash_product(hasher, bytes, page, blob_store, curr_offset, ty.view()) }\n        }\n\n        // The primitive types:\n        //\n        // SAFETY (applies to app primitive types):\n        // Per caller requirement, know `value` points to a valid `ty`.\n        // Thus `&bytes[range_move(0..ty.size(), *curr_offset)]` points to init bytes\n        // and `ty.size()` corresponds exactly to `N = 1, 1, 1, 2, 2, 4, 4, 8, 8, 16, 16, 32, 32, 4, 8`.\n        &AlgebraicTypeLayout::Bool | &AlgebraicTypeLayout::U8 => {\n            u8::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher)\n        }\n        &AlgebraicTypeLayout::I8 => i8::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::I16 => i16::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::U16 => u16::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::I32 => i32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::U32 => u32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::I64 => i64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::U64 => u64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::I128 => i128::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::U128 => u128::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::I256 => i256::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::U256 => u256::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) }).hash(hasher),\n        &AlgebraicTypeLayout::F32 => {\n            F32::from(f32::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) })).hash(hasher)\n        }\n        &AlgebraicTypeLayout::F64 => {\n            F64::from(f64::from_le_bytes(unsafe { read_from_bytes(bytes, curr_offset) })).hash(hasher)\n        }\n\n        // The var-len cases.\n        &AlgebraicTypeLayout::String => {\n            // SAFETY: `value` was valid at and aligned for `ty`.\n            // These `ty` store a `vlr: VarLenRef` as their value,\n            // so the range is valid and properly aligned for `VarLenRef`.\n            // Moreover, `vlr.first_granule` was promised by the caller\n            // to either be `NULL` or point to a valid granule in `page`.\n            unsafe {\n                run_vlo_bytes(page, bytes, blob_store, curr_offset, |bytes| {\n                    // SAFETY: For `::String`, the blob will always be valid UTF-8.\n                    let string = str::from_utf8_unchecked(bytes);\n                    string.hash(hasher)\n                });\n            }\n        }\n        AlgebraicTypeLayout::VarLen(VarLenType::Array(ty)) => {\n            let ty = &ty.elem_ty;\n\n            // SAFETY: `value` was valid at and aligned for `ty`.\n            // These `ty` store a `vlr: VarLenRef` as their value,\n            // so the range is valid and properly aligned for `VarLenRef`.\n            // Moreover, `vlr.first_granule` was promised by the caller\n            // to either be `NULL` or point to a valid granule in `page`.\n            unsafe {\n                run_vlo_bytes(page, bytes, blob_store, curr_offset, |mut bsatn| {\n                    let de = Deserializer::new(&mut bsatn);\n                    spacetimedb_sats::hash_bsatn_array(hasher, ty, de).unwrap();\n                });\n            }\n        }\n    }\n}\n\n/// Runs the function `run` on the concatenated bytes of a var-len object,\n/// referred to at by the var-len reference at `curr_offset`\n/// which is then advanced.\n///\n/// SAFETY: `data = bytes[range_move(0..size_of::<VarLenRef>(), *curr_offset)]`\n/// must be a valid `vlr = VarLenRef` and `&data` must be properly aligned for a `VarLenRef`.\n/// The `vlr.first_granule` must be `NULL` or must point to a valid granule in `page`.\npub(crate) unsafe fn run_vlo_bytes<R>(\n    page: &Page,\n    bytes: &Bytes,\n    blob_store: &dyn BlobStore,\n    curr_offset: &mut usize,\n    run: impl FnOnce(&[u8]) -> R,\n) -> R {\n    // SAFETY: `value` was valid at and aligned for `ty`.\n    // These `ty` store a `vlr: VarLenRef` as their fixed value.\n    // The range thus is valid and properly aligned for `VarLenRef`.\n    let vlr = unsafe { read_from_bytes::<VarLenRef>(bytes, curr_offset) };\n\n    if vlr.is_large_blob() {\n        // SAFETY: As `vlr` is a blob, `vlr.first_granule` always points to a valid granule.\n        let bytes = unsafe { vlr_blob_bytes(page, blob_store, vlr) };\n        run(bytes)\n    } else {\n        // SAFETY: `vlr.first_granule` is either NULL or points to a valid granule.\n        let var_iter = unsafe { page.iter_vlo_data(vlr.first_granule) };\n        let total_len = vlr.length_in_bytes as usize;\n\n        // SAFETY: `total_len == var_iter.map(|c| c.len()).sum()`.\n        unsafe { concat_byte_chunks_buf(total_len, var_iter, run) }\n    }\n}\n\n/// Read a `T` from `bytes` at the `curr_offset` and advance by `size` bytes.\n///\n/// # Safety\n///\n/// Let `value = &bytes[range_move(0..size_of::<T>(), *curr_offset)]`.\n/// Then `value` must point to a valid `T` and must be properly aligned for `T`.\npub unsafe fn read_from_bytes<T: Copy>(bytes: &Bytes, curr_offset: &mut usize) -> T {\n    let bytes = &bytes[*curr_offset..];\n    *curr_offset += mem::size_of::<T>();\n    // TODO: Endianness concerns? Do we need to explicitly read as little-endian here?\n    let ptr: *const T = bytes.as_ptr().cast();\n    // SAFETY: Caller promised that `ptr` points to a `T`.\n    // Moreover, `ptr` is derived from a shared reference with permission to read this range.\n    unsafe { *ptr }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::{blob_store::HashMapBlobStore, page_pool::PagePool};\n    use core::hash::BuildHasher;\n    use proptest::prelude::*;\n    use spacetimedb_sats::proptest::generate_typed_row;\n\n    proptest! {\n        #![proptest_config(ProptestConfig::with_cases(if cfg!(miri) { 8 } else { 2048 }))]\n        #[test]\n        fn pv_row_ref_hash_same_std_random_state((ty, val) in generate_typed_row()) {\n            // Turn `val` into a `RowRef`.\n            let mut table = crate::table::test::table(ty);\n            let pool = &PagePool::new_for_test();\n            let blob_store = &mut HashMapBlobStore::default();\n            let (_, row) = table.insert(pool, blob_store, &val).unwrap();\n\n            // Check hashing algos.\n            let rs = std::hash::RandomState::new();\n            prop_assert_eq!(rs.hash_one(&val), rs.hash_one(row));\n        }\n\n        #[test]\n        fn pv_row_ref_hash_same_ahash((ty, val) in generate_typed_row()) {\n            // Turn `val` into a `RowRef`.\n            let pool = &PagePool::new_for_test();\n            let blob_store = &mut HashMapBlobStore::default();\n            let mut table = crate::table::test::table(ty);\n            let (_, row) = table.insert(pool, blob_store, &val).unwrap();\n\n            // Check hashing algos.\n            let rs = std::hash::RandomState::new();\n            prop_assert_eq!(rs.hash_one(&val), rs.hash_one(row));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/row_type_visitor.rs",
    "content": "//! A [`VarLenMembers`] visitor for [`AlgebraicType`],\n//! supporting any non-recursive `AlgebraicType`,\n//! including sums and products.\n//!\n//! The general architecture is:\n//! first, we walk the `AlgebraicType` to construct a [`VarLenRoseTree`],\n//! a [Rose Tree](https://en.wikipedia.org/wiki/Rose_tree)\n//! with a structure that matches the original `AlgebraicType`,\n//! but with non-var-len members stripped out\n//! and var-len members resolved to their offsets within a row.\n//!\n//! We then flatten the `VarLenRoseTree` into a [`VarLenVisitorProgram`],\n//! a simple bytecode language which can be interpreted relatively efficiently\n//! to visit the var-len members in a row.\n//!\n//! A `VarLenVisitorProgram` for a fixed-length-only row will be empty.\n//!\n//! A `VarLenVisitorProgram` for a (potentially nested) product type\n//! will be a sequence of `VisitOffset` instructions,\n//! with no control flow.\n//!\n//! [`SumType`](spacetimedb_sats::SumType)s which contain var-len refs\n//! will introduce control-flow to the `VarLenVisitorProgram`\n//! using `SwitchOnTag` to visit the appropriate variant,\n//! and `Goto` to return to the part of the var-len object after the sum.\n//!\n//! The `VarLenMembers` impl for `VarLenVisitorProgram`\n//! implements a simple interpreter loop for the var-len visitor bytecode.\n\nuse super::{\n    indexes::{Byte, Bytes, PageOffset},\n    page::get_ref,\n    var_len::{VarLenMembers, VarLenRef},\n};\nuse core::fmt;\nuse core::marker::PhantomData;\nuse itertools::Itertools;\nuse spacetimedb_sats::layout::{\n    align_to, AlgebraicTypeLayout, HasLayout, ProductTypeLayoutView, RowTypeLayout, SumTypeLayout,\n};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse std::sync::Arc;\n\n/// Construct an implementor of `VarLenMembers`,\n/// which visits the var-len members in a row of `ty`.\n///\n/// This is a potentially expensive operation,\n/// so the resulting `VarLenVisitorProgram` should be stored and re-used.\npub fn row_type_visitor(ty: &RowTypeLayout) -> VarLenVisitorProgram {\n    if ty.layout().fixed {\n        // Fast-path: The row type doesn't contain var-len members, so quit early.\n        return VarLenVisitorProgram { insns: [].into() };\n    }\n\n    let rose_tree = product_type_to_rose_tree(ty.product(), &mut 0);\n\n    rose_tree_to_visitor_program(&rose_tree)\n}\n\n/// Construct a `VarLenRoseTree` from `ty`.\n///\n/// See [`algebraic_type_to_rose_tree`] for more details.\nfn product_type_to_rose_tree(ty: ProductTypeLayoutView<'_>, current_offset: &mut usize) -> VarLenRoseTree {\n    // Loop over all the product elements,\n    // which we store in-order,\n    // and collect them into a subtree.\n\n    // Better to over-allocate than under-allocate (maybe).\n    let mut contents = Vec::with_capacity(ty.elements.len());\n\n    for elt in ty.elements {\n        match algebraic_type_to_rose_tree(&elt.ty, current_offset) {\n            // No need to collect empty subtrees.\n            VarLenRoseTree::Empty => {}\n            // Nested products can be flattened.\n            VarLenRoseTree::Product(children) => contents.extend(children),\n            // `Offset` and `Sum` must be stored as a child of the product.\n            child => contents.push(child),\n        }\n    }\n\n    // After visiting the elements,\n    // make `current_offset` aligned for this member,\n    // to store the member's trailing padding.\n    *current_offset = align_to(*current_offset, ty.align());\n\n    match contents.len() {\n        // A product with no var-len members is `Empty`.\n        0 => VarLenRoseTree::Empty,\n        // A product with a single subtree behaves like that subtree,\n        // so prune the intermediate node.\n        1 => contents.pop().unwrap(),\n        // For a product with multiple children, return a node with those children.\n        _ => VarLenRoseTree::Product(contents),\n    }\n}\n\n/// Construct a `VarLenRoseTree` from the sum type `ty`.\n/// The sum types are the main reason we go through the complication of a rose tree\n/// and why we cannot end up with a simple `[u16]`\n/// (i.e., [`AlignedVarLenOffsets`](super::var_len::AlignedVarLenOffsets))\n/// as our `VarlenMembers` type.\n///\n/// See [`algebraic_type_to_rose_tree`] for more details.\nfn sum_type_to_rose_tree(ty: &SumTypeLayout, current_offset: &mut usize) -> VarLenRoseTree {\n    // The tag goes before the variant data.\n    // Currently, this is the same as `*current_offset`.\n    let tag_offset = *current_offset + ty.offset_of_tag();\n\n    // For each variant, collect that variant's sub-tree.\n    let mut variants = ty\n        .variants\n        .iter()\n        .enumerate()\n        .map(|(tag, variant)| {\n            let var_ty = &variant.ty;\n\n            // All variants are stored overlapping at the offset of the sum.\n            // Don't let them mutate `current_offset`.\n            // Note that we store sums with tag first,\n            // followed by data/payload.\n            //\n            // `offset_of_variant_data` is defined as 0,\n            // but included for future-proofing.\n            let mut child_offset = *current_offset + ty.offset_of_variant_data(tag as u8);\n            algebraic_type_to_rose_tree(var_ty, &mut child_offset)\n        })\n        .collect::<Vec<_>>();\n\n    // Store the new offset after the sum.\n    *current_offset += ty.size();\n\n    if variants.iter().all(|var| matches!(var, VarLenRoseTree::Empty)) {\n        // Sums with no var-len members are `Empty`.\n        VarLenRoseTree::Empty\n    } else if variants.iter().all_equal() {\n        // When all variants have the same set of var-len refs,\n        // there's no need to switch on the tag, so prune the intermediate node.\n        // A special case of this is single-variant sums.\n        variants.pop().unwrap()\n    } else {\n        // For `Sum`s with multiple variants,\n        // at least one of which contains a var-len obj,\n        // return a `Sum` node.\n        VarLenRoseTree::Sum {\n            tag_offset: tag_offset as u16,\n            tag_data_processors: variants,\n        }\n    }\n}\n\n/// Construct a `VarLenRoseTree` from `ty`.\n///\n/// `current_offset` should be passed as `&mut 0` upon entry to the row-type,\n/// and will be incremented as appropriate during recursive traversal\n/// to track the offset in bytes of the member currently being visited.\nfn algebraic_type_to_rose_tree(ty: &AlgebraicTypeLayout, current_offset: &mut usize) -> VarLenRoseTree {\n    // Align the `current_offset` for `ty`.\n    // This accounts for any padding before `ty`.\n    *current_offset = align_to(*current_offset, ty.align());\n\n    match ty {\n        AlgebraicTypeLayout::VarLen(_) => {\n            // Strings, arrays, and maps are stored as `VarLenRef`s.\n            // These are the offsets we want to find.\n\n            // Post-increment the size of `VarLenRef` into `current_offset`,\n            // so it stores the offset after this member.\n            let member = *current_offset as u16;\n            *current_offset += ty.size();\n            VarLenRoseTree::Offset(member)\n        }\n        AlgebraicTypeLayout::Primitive(primitive_type) => {\n            // For primitive types, increment `current_offset` past this member,\n            // then return the empty tree.\n            *current_offset += primitive_type.size();\n            VarLenRoseTree::Empty\n        }\n        AlgebraicTypeLayout::Product(ty) => product_type_to_rose_tree(ty.view(), current_offset),\n        AlgebraicTypeLayout::Sum(ty) => sum_type_to_rose_tree(ty, current_offset),\n    }\n}\n\n/// A [Rose Tree](https://en.wikipedia.org/wiki/Rose_tree)\n/// containing information about the var-len members in an `AlgebraicType`.\n#[derive(PartialEq, Eq)]\nenum VarLenRoseTree {\n    /// No var-len members.\n    Empty,\n\n    /// A var-len member at `row + N` bytes.\n    Offset(u16),\n\n    /// A product type which contains multiple var-len members.\n    Product(Vec<VarLenRoseTree>),\n\n    /// A sum type which contains at least one variant with at least one var-len member.\n    Sum {\n        /// The sum's tag is at `row + tag_offset` bytes.\n        tag_offset: u16,\n        /// The var-len members of variant `N` are described in `tag_data_processors[N]`.\n        tag_data_processors: Vec<VarLenRoseTree>,\n    },\n}\n\n/// Compile the [`VarLenRoseTree`] to [`VarLenVisitorProgram`].\nfn rose_tree_to_visitor_program(tree: &VarLenRoseTree) -> VarLenVisitorProgram {\n    let mut program = Vec::new();\n\n    fn compile_tree(tree: &VarLenRoseTree, into: &mut Vec<Insn>) {\n        match tree {\n            VarLenRoseTree::Empty => {}\n            VarLenRoseTree::Offset(offset) => into.push(Insn::VisitOffset(*offset)),\n            VarLenRoseTree::Product(elts) => {\n                for elt in elts {\n                    compile_tree(elt, into);\n                }\n            }\n            VarLenRoseTree::Sum {\n                tag_offset,\n                tag_data_processors,\n            } => {\n                let dummy_insn = into.len();\n                // We'll replace this later with a `SwitchOnTag`.\n                into.push(Insn::FIXUP);\n\n                let mut goto_fixup_locations = Vec::with_capacity(tag_data_processors.len());\n                let mut jump_targets = Vec::with_capacity(tag_data_processors.len());\n\n                for branch in tag_data_processors {\n                    jump_targets.push(into.len() as u16);\n                    compile_tree(branch, into);\n                    goto_fixup_locations.push(into.len());\n\n                    // We'll rewrite this to store the after-sum address later.\n                    into.push(Insn::FIXUP);\n                }\n\n                let goto_addr = into.len();\n                for idx in goto_fixup_locations {\n                    into[idx] = Insn::Goto(goto_addr as u16);\n                }\n\n                into[dummy_insn] = Insn::SwitchOnTag {\n                    tag_offset: *tag_offset,\n                    jump_targets: jump_targets.into(),\n                };\n            }\n        }\n    }\n\n    compile_tree(tree, &mut program);\n\n    remove_trailing_gotos(&mut program);\n\n    VarLenVisitorProgram { insns: program.into() }\n}\n\n/// Remove any trailing gotos.\n///\n/// They are not needed as they will only go towards the end,\n/// so we can just cut them out.\nfn remove_trailing_gotos(program: &mut Vec<Insn>) -> bool {\n    let mut changed = false;\n    for idx in (0..program.len()).rev() {\n        match program[idx] {\n            Insn::Goto(_) => {\n                program.pop();\n                changed = true;\n            }\n            _ => break,\n        }\n    }\n    changed\n}\n\n/// The instruction set of a [`VarLenVisitorProgram`].\n#[derive(Debug, Clone, PartialEq, Eq)]\nenum Insn {\n    // TODO(perf): consider boxing this variant (or making it a variable-width instruction)\n    //             to minimize sizeof(insn),\n    //             This could be valuable, assuming that sum types in tables are rare.\n    //             E.g. a `SwitchOnTag` could be replaced with:\n    //       ```ignore\n    //       +0: ReadTagRelativeBranch(tag_offset) // read tag, add it to instruction pointer\n    //       +1: Goto(target 0)\n    //       +2: Goto(target 1)\n    //       +n: Goto(target n - 1)\n    //       target 0: code to visit variant 0\n    //       target 1: code to visit variant 1\n    //       ```\n    /// Read a byte `tag` from `row + tag_offset`,\n    /// and branch to `jump_targets[tag]`.\n    ///\n    /// Entering a sum will `SwitchOnTag` to visit the appropriate variant.\n    SwitchOnTag {\n        /// The tag to dispatch on is stored at `row+tag_offset`.\n        tag_offset: u16,\n        /// Indexes within the vec of insns.\n        /// Invariant: `∀ jt ∈ jump_targets. jt > instr_ptr`.\n        jump_targets: Box<[u16]>,\n    },\n\n    /// Visit the `VarLenRef` at `row + N` bytes.\n    VisitOffset(\n        /// The offset relative to the `row` at which the `VarLenRef` slot resides.\n        u16,\n    ),\n\n    /// Unconditionally branch to the instruction at `program[N]`\n    /// where `N > instruction pointer`.\n    ///\n    /// After visiting a sum variant, the variant will `Goto` to skip all subsequent sum variants.\n    Goto(\n        /// The new instruction pointer.\n        u16,\n    ),\n}\n\nimpl Insn {\n    const FIXUP: Self = Self::Goto(u16::MAX);\n}\n\nimpl MemoryUsage for Insn {}\n\n#[allow(clippy::disallowed_macros)] // This is for test code.\npub fn dump_visitor_program(program: &VarLenVisitorProgram) {\n    for (idx, insn) in program.insns.iter().enumerate() {\n        eprintln!(\"{idx:2}: {insn}\");\n    }\n}\n\nimpl fmt::Display for Insn {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::VisitOffset(offset) => write!(f, \"visit {offset}\"),\n            Self::Goto(x) => write!(f, \"goto -> {x}\"),\n            Self::SwitchOnTag {\n                tag_offset,\n                jump_targets,\n            } => write!(f, \"switch_on_tag at {tag_offset} {jump_targets:?}\"),\n        }\n    }\n}\n\n/// A var-len visitor `program` constructed for a particular row type `ty`,\n/// a [`RowTypeLayout`], using [`row_type_visitor`],\n/// which is the only way a program can be produced.\n///\n/// Users of the `program` must ensure,\n/// when calling [`VarLenMembers::visit_var_len`] or [`VarLenMembers::visit_var_len_mut`],\n/// that is only ever used on a `row` where `row: ty`.\n///\n/// A program consists of a list of simple byte-code instructions\n/// which can be interpreted to visit the var-len members in a row.\n/// Forward progress, and thus termination,\n/// during interpretation is guaranteed when evaluating a program,\n/// as all jumps (`SwitchOnTag` and `Goto`) will set `new_instr_ptr > old_instr_ptr`.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct VarLenVisitorProgram {\n    /// The list of instructions that make up this program.\n    insns: Arc<[Insn]>,\n}\n\nimpl MemoryUsage for VarLenVisitorProgram {\n    fn heap_usage(&self) -> usize {\n        let Self { insns } = self;\n        insns.heap_usage()\n    }\n}\n\n/// Evaluates the `program`,\n/// provided the `instr_ptr` as its program counter / instruction pointer,\n/// and a callback `read_tag` to extract a tag at the given offset,\n/// until `Some(offset)` is reached,\n/// or the program halts.\n///\n/// SAFETY: This function is safe,\n/// as it is the responsibility of `read_tag` to ensure that `read_tag(tag_offset)`\n/// for the tag offsets stored in `program` is valid.\n#[inline]\nfn next_vlr_offset(program: &[Insn], instr_ptr: &mut u16, read_tag: impl Fn(u16) -> u8) -> Option<PageOffset> {\n    loop {\n        match program.get(*instr_ptr as usize)? {\n            Insn::VisitOffset(offset) => {\n                *instr_ptr += 1;\n                return Some(PageOffset(*offset));\n            }\n            Insn::Goto(next) => *instr_ptr = *next,\n            Insn::SwitchOnTag {\n                tag_offset,\n                jump_targets,\n            } => {\n                let tag = read_tag(*tag_offset);\n                let go_to = jump_targets[tag as usize];\n                *instr_ptr = go_to;\n            }\n        }\n    }\n}\n\n// Reads the `tag: u8` at `offset` in `row`.\n//\n// SAFETY: `offset` mut be in bounds of `row`\n// and `row[offset]` must refer to a valid `u8`.\n#[inline]\nunsafe fn read_tag(row: *const Byte, offset: u16) -> u8 {\n    // SAFETY: Caller promised that `offset` will be in bounds of `self.row`.\n    let byte_ptr = unsafe { row.add(offset as usize) }.cast();\n    // SAFETY: Caller promised that `offset`, so `byte_ptr`, points to a valid `u8`.\n    unsafe { *byte_ptr }\n}\n\n// SAFETY: Both methods visit the same permutation\n// as both use the same underlying mechanism `next_vlr_offset` on the same `&[Insn]`.\nunsafe impl VarLenMembers for VarLenVisitorProgram {\n    type Iter<'this, 'row> = VarLenVisitorProgramIter<'this, 'row>;\n    type IterMut<'this, 'row> = VarLenVisitorProgramIterMut<'this, 'row>;\n\n    unsafe fn visit_var_len<'this, 'row>(&'this self, row: &'row Bytes) -> Self::Iter<'this, 'row> {\n        // SAFETY:\n        //\n        // - Caller promised that `row` is properly aligned for the row type\n        //   so based on this assumption, our program will yield references that are properly\n        //   aligned for `VarLenGranule`s.\n        //\n        // - Caller promised that `row.len() == row_type.size()`.\n        //   This ensures that our program will yield references that are in bounds of `row`.\n        Self::Iter {\n            instr_ptr: 0,\n            row,\n            program: &self.insns,\n        }\n    }\n\n    unsafe fn visit_var_len_mut<'this, 'row>(&'this self, row: &'row mut Bytes) -> Self::IterMut<'this, 'row> {\n        // SAFETY: Same as in `visit_var_len` above.\n        Self::IterMut {\n            instr_ptr: 0,\n            _row_lifetime: PhantomData,\n            row: row.as_mut_ptr(),\n            program: &self.insns,\n        }\n    }\n}\n\n/// The iterator type for `VarLenVisitorProgram::visit_var_len`.\npub struct VarLenVisitorProgramIter<'visitor, 'row> {\n    /// The program used to find the var-len ref offsets.\n    program: &'visitor [Insn],\n    /// The row to find the var-len refs in.\n    row: &'row Bytes,\n    /// The program counter / instruction pointer.\n    instr_ptr: u16,\n}\n\nimpl<'row> Iterator for VarLenVisitorProgramIter<'_, 'row> {\n    type Item = &'row VarLenRef;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // Reads the `tag: u8` at `offset`.\n        // SAFETY: Constructing the iterator is a promise that `self.row[offset]` refers to a valid `u8`.\n        let read_tag = |offset| unsafe { read_tag(self.row.as_ptr().cast(), offset) };\n\n        let offset = next_vlr_offset(self.program, &mut self.instr_ptr, read_tag)?;\n        // SAFETY: Constructing the iterator is a promise that\n        // `offset`s produced by the program will be in bounds of `self.row`\n        // and that the derived pointer must be properly aligned for a `VarLenRef`.\n        //\n        // Moreover, `self.row` is non-null, so adding the offset to it results in a non-null pointer.\n        // By having `self.row: &'row Bytes` we also know that the pointer is valid for reads\n        // and that it will be for `'row` which is tied to the lifetime of `Self::Item`.\n        Some(unsafe { get_ref::<VarLenRef>(self.row, offset) })\n    }\n}\n\n/// The iterator type for `VarLenVisitorProgram::visit_var_len_mut`.\npub struct VarLenVisitorProgramIterMut<'visitor, 'row> {\n    /// The program used to find the var-len ref offsets.\n    program: &'visitor [Insn],\n    _row_lifetime: PhantomData<&'row mut Bytes>,\n    /// Pointer to the row to find the var-len refs in.\n    row: *mut Byte,\n    /// The program counter / instruction pointer.\n    instr_ptr: u16,\n}\n\nimpl<'row> Iterator for VarLenVisitorProgramIterMut<'_, 'row> {\n    type Item = &'row mut VarLenRef;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // Reads the `tag: u8` at `offset`.\n        // SAFETY: Constructing the iterator is a promise that `self.row[offset]` refers to a valid `u8`.\n        let read_tag = |offset| unsafe { read_tag(self.row, offset) };\n\n        let offset = next_vlr_offset(self.program, &mut self.instr_ptr, read_tag)?;\n        // SAFETY: Constructing the iterator is a promise that\n        // `offset`s produced by the program will be in bounds of `self.row`.\n        let vlr_ptr: *mut VarLenRef = unsafe { self.row.add(offset.idx()).cast() };\n        // SAFETY: Constructing the iterator is a promise that\n        // The derived pointer must be properly aligned for a `VarLenRef`.\n        //\n        // Moreover, `self.row` is non-null, so adding the offset to it results in a non-null pointer.\n        // By having `self.row: &'row mut Bytes` we also know that the pointer is valid for reads and writes\n        // and that it will be for `'row` which is tied to the lifetime of `Self::Item`.\n        Some(unsafe { &mut *vlr_ptr })\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use spacetimedb_sats::layout::Size;\n    use spacetimedb_sats::{AlgebraicType, ProductType};\n\n    fn row_type<T: Into<ProductType>>(row_ty: T) -> RowTypeLayout {\n        let ty: ProductType = row_ty.into();\n        ty.into()\n    }\n\n    fn addr<T>(x: &T) -> usize {\n        x as *const _ as usize\n    }\n\n    fn check_addrs<const N: usize>(prog: &VarLenVisitorProgram, row: &Bytes, expected: [usize; N]) {\n        let mut visitor = unsafe { prog.visit_var_len(row) };\n        for expected in expected.map(|i| &row[i]) {\n            assert_eq!(addr(visitor.next().unwrap()), addr(expected));\n        }\n        assert!(visitor.next().is_none());\n    }\n\n    #[test]\n    fn visit_var_len_in_u32_string_u8() {\n        let ty = row_type([AlgebraicType::U32, AlgebraicType::String, AlgebraicType::U8]);\n        assert_eq!(ty.size(), Size(12));\n\n        // alloc an array of u32 to ensure 4-byte alignment.\n        let row = &[0xa5a5_a5a5u32; 3];\n        let row = row.as_ptr().cast::<[Byte; 12]>();\n        let row = unsafe { &*row };\n\n        check_addrs(&row_type_visitor(&ty), row, [4]);\n    }\n\n    #[test]\n    fn visit_var_len_nested_product() {\n        let ty = row_type([\n            AlgebraicType::U32,\n            AlgebraicType::String,\n            AlgebraicType::U8,\n            AlgebraicType::product([AlgebraicType::U32, AlgebraicType::String]),\n            AlgebraicType::U8,\n        ]);\n        assert_eq!(ty.size(), Size(24));\n\n        // Alloc an array of u32 to ensure 4-byte alignment.\n        let row = &[0xa5a5_a5a5u32; 6];\n        let row = row.as_ptr().cast::<[Byte; 24]>();\n        let row = unsafe { &*row };\n\n        check_addrs(&row_type_visitor(&ty), row, [4, 16]);\n    }\n\n    #[test]\n    fn visit_var_len_bare_enum() {\n        let ty = row_type([AlgebraicType::option(\n            AlgebraicType::String, // tag 0, size 4, align 2\n        )]);\n        assert_eq!(ty.size(), Size(6));\n        assert_eq!(ty.align(), 2);\n        let outer_sum = &ty[0].as_sum().unwrap();\n        let outer_tag = outer_sum.offset_of_tag();\n\n        let row = &mut [0xa5a5u16; 3];\n        let row_ptr = row.as_mut_ptr().cast::<[Byte; 6]>();\n        let row = unsafe { &mut *row_ptr };\n\n        let program = row_type_visitor(&ty);\n        // Variant 1 (String) is live\n        row[outer_tag] = 0;\n        check_addrs(&program, row, [2]);\n\n        // Variant 1 (none) is live\n        row[outer_tag] = 1;\n        check_addrs(&program, row, []);\n    }\n\n    #[test]\n    fn visit_var_len_nested_enum() {\n        // Tables are always product types.\n        let ty = row_type([AlgebraicType::sum([\n            AlgebraicType::U32,                                                  // tag 0, size 4, align 4\n            AlgebraicType::String,                                               // tag 1, size 4, align 2\n            AlgebraicType::product([AlgebraicType::U32, AlgebraicType::String]), // tag 2, size 8, align 4\n            AlgebraicType::sum(\n                // tag 3, size 8, align 4\n                [\n                    AlgebraicType::U32,    // tag (3, 0), size 4, align 4\n                    AlgebraicType::String, // tag (3, 1), size 4, align 2\n                ],\n            ),\n        ])]);\n        assert_eq!(ty.size(), Size(12));\n        assert_eq!(ty.align(), 4);\n        let outer_sum = &ty[0].as_sum().unwrap();\n        let outer_tag = outer_sum.offset_of_tag();\n        assert_eq!(outer_tag, 0);\n        let inner_sum = outer_sum.variants[3].ty.as_sum().unwrap();\n        let inner_tag = outer_sum.offset_of_variant_data(3) + inner_sum.offset_of_tag();\n        assert_eq!(inner_tag, 4);\n\n        let row = &mut [0xa5a5_a5a5u32; 3];\n        let row_ptr = row.as_mut_ptr().cast::<[Byte; 12]>();\n        let row = unsafe { &mut *row_ptr };\n\n        let program = row_type_visitor(&ty);\n\n        // Variant 0 (U32) is live\n        row[outer_tag] = 0;\n        check_addrs(&program, row, []);\n\n        // Variant 1 (String) is live\n        row[outer_tag] = 1;\n        check_addrs(&program, row, [4]);\n\n        // Variant 2 (Product) is live\n        row[outer_tag] = 2;\n        check_addrs(&program, row, [8]);\n\n        // Variant 3 (Sum) is live but its tag is not valid yet.\n        row[outer_tag] = 3;\n\n        // Variant 3, 0 (Sum, U32) is live.\n        row[inner_tag] = 0;\n        check_addrs(&program, row, []);\n\n        // Variant 3, 1 (Sum, String) is live.\n        row[inner_tag] = 1;\n        check_addrs(&program, row, [8]);\n    }\n}\n"
  },
  {
    "path": "crates/table/src/static_bsatn_validator.rs",
    "content": "//! To efficiently implement a fast-path BSATN -> BFLATN,\n//! we use a `StaticLayout` but in reverse of the read path.\n//! This however leaves us with no way to validate\n//! that the BSATN satisfies the row type of a given table.\n//!\n//! More specifically, we must validate that:\n//! 1. The length of the BSATN-encoded row matches the expected length.\n//! 2. All `bool`s in the row type only receive the values 0 or 1.\n//! 3. All sum tags are valid.\n//! 4. a sum's payload follows 2-3 recursively.\n//!\n//! That is where this module comes in,\n//! which provides two functions:\n//! - [`static_bsatn_validator`], which compiles a validator program given the table's row type.\n//! - [`validate_bsatn`], which executes the validator program against a row encoded in BSATN.\n//!\n//! The compilation uses the same strategy as for row type visitors,\n//! first simplifying to a rose-tree and then flattening that to\n//! a simple forward-progress-only byte code instruction set.\n\n#![allow(unused)]\n\nuse super::static_layout::StaticLayout;\nuse itertools::{repeat_n, Itertools as _};\nuse spacetimedb_sats::bsatn::DecodeError;\nuse spacetimedb_sats::layout::{\n    AlgebraicTypeLayout, HasLayout as _, PrimitiveType, ProductTypeLayout, ProductTypeLayoutView, RowTypeLayout,\n};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse std::sync::Arc;\n\n/// Constructs a validator for a row encoded in BSATN\n/// that checks that the row satisfies the type `ty`\n/// when `ty` has `StaticLayout`.\n///\n/// This is a potentially expensive operation,\n/// so the resulting `StaticBsatnValidator` should be stored and re-used.\npub(crate) fn static_bsatn_validator(ty: &RowTypeLayout) -> StaticBsatnValidator {\n    let tree = row_type_to_tree(ty.product());\n    let insns = tree_to_insns(&tree).into();\n    StaticBsatnValidator { insns }\n}\n\n/// Construct a `Tree` from `ty`.\n///\n/// See [`extend_trees_for_algebraic_type`] for more details.\nfn row_type_to_tree(ty: ProductTypeLayoutView<'_>) -> Tree {\n    let mut sub_trees = Vec::new();\n    extend_trees_for_product_type(ty, &mut 0, &mut sub_trees);\n    sub_trees_to_tree(sub_trees)\n}\n\n/// Convert a list of `sub_trees` to one tree.\nfn sub_trees_to_tree(mut sub_trees: Vec<Tree>) -> Tree {\n    match sub_trees.len() {\n        // No trees is `Empty`.\n        0 => Tree::Empty,\n        // A single subtree can be collapsed.\n        // so prune the intermediate node.\n        1 => sub_trees.pop().unwrap(),\n        // For more than one children, sequence them doing one after the other.\n        _ => Tree::Sequence { sub_trees },\n    }\n}\n\n/// Extend `sub_trees` with checks for `ty`.\n///\n/// See [`extend_trees_for_algebraic_type`] for more details.\nfn extend_trees_for_product_type(ty: ProductTypeLayoutView<'_>, current_offset: &mut usize, sub_trees: &mut Vec<Tree>) {\n    for elem in ty.elements {\n        extend_trees_for_algebraic_type(&elem.ty, current_offset, sub_trees);\n    }\n}\n\n/// Extend `sub_trees` with checks for `ty`.\n///\n/// `current_offset` should be passed as `&mut 0` upon entry to the row-type,\n/// and will be incremented as appropriate during recursive traversal\n/// to track the offset in bytes of the member currently being visited.\nfn extend_trees_for_algebraic_type(ty: &AlgebraicTypeLayout, current_offset: &mut usize, sub_trees: &mut Vec<Tree>) {\n    match ty {\n        AlgebraicTypeLayout::Primitive(PrimitiveType::Bool) => {\n            // The `Bool` type is special, as it only allows a BSATN byte to be 0 or 1.\n            let offset = *current_offset as u16;\n            *current_offset += 1;\n            sub_trees.push(Tree::CheckBool { offset });\n        }\n        AlgebraicTypeLayout::Primitive(prim_ty) => {\n            // For primitive types, increment `current_offset` past this member.\n            // Primitive types have no padding, so we can use `prim_ty.size()` for bsatn.\n            *current_offset += prim_ty.size();\n        }\n        AlgebraicTypeLayout::Product(prod_ty) => {\n            extend_trees_for_product_type(prod_ty.view(), current_offset, sub_trees)\n        }\n        AlgebraicTypeLayout::Sum(sum_ty) => {\n            // Record the tag's offset and the number of variants.\n            let num_variants = sum_ty.variants.len() as u8;\n            let tag_offset = *current_offset as u16;\n            *current_offset += 1;\n\n            // For each variant, collect that variant's sub-tree.\n            // All variants are stored overlapping at the offset of the sum\n            // so we must reset `current_offset` each time to the before-variant value.\n            // We also need to create a fresh `sub_tree` context.\n            // Note that BSATN stores sums with tag first,\n            // followed by data/payload.\n            let mut child_offset = *current_offset;\n            let mut variants = sum_ty\n                .variants\n                .iter()\n                .map(|variant| {\n                    let var_ty = &variant.ty;\n                    let mut sub_trees = Vec::new();\n                    child_offset = *current_offset;\n                    extend_trees_for_algebraic_type(var_ty, &mut child_offset, &mut sub_trees);\n                    sub_trees_to_tree(sub_trees)\n                })\n                .collect::<Vec<_>>();\n            // Having dealt with all variants,\n            // we must now move `current_offset` forward to the size of the payload\n            // which we know to be same for all variants.\n            *current_offset = child_offset;\n\n            if variants.iter().all_equal() {\n                // When all variants have the same set checks,\n                // there's no need to switch on the tag, so prune the intermediate node.\n                // A special case of this is single-variant sums.\n                sub_trees.push(Tree::CheckTag {\n                    tag_offset,\n                    num_variants,\n                });\n                if let Some(tree) = variants.pop() {\n                    sub_trees.push(tree);\n                }\n            } else {\n                sub_trees.push(Tree::Sum {\n                    tag_offset,\n                    tag_data_processors: variants,\n                });\n            }\n        }\n\n        // There are no var-len members when there's a static fixed bsatn length.\n        AlgebraicTypeLayout::VarLen(_) => unreachable!(),\n    }\n}\n\n/// A [Rose Tree](https://en.wikipedia.org/wiki/Rose_tree)\n/// containing information about validation steps for\n/// decoding BSATN for statically known fixed size `AlgebraicType`s.\n#[derive(Debug, PartialEq, Eq)]\nenum Tree {\n    /// Nothing to check.\n    Empty,\n\n    /// Do each sub-tree after each other.\n    Sequence { sub_trees: Vec<Tree> },\n\n    /// Check a byte at `start + N` bytes to be a valid `bool`.\n    CheckBool { offset: u16 },\n\n    /// Check a byte at `start + N` bytes to be `< num_variants`.\n    CheckTag {\n        /// The sum's tag is at `row + tag_offset` bytes.\n        tag_offset: u16,\n        /// The number of variants there are.\n        /// The read tag must be `< num_variants`.\n        num_variants: u8,\n    },\n\n    /// A choice between several variants.\n    Sum {\n        /// The sum's tag is at `row + tag_offset` bytes.\n        tag_offset: u16,\n        /// The checks for variant `N` are described in `tag_data_processors[N]`.\n        tag_data_processors: Vec<Tree>,\n    },\n}\n\n/// Compile the [`Tree`] to a list of [`Insn`].\nfn tree_to_insns(tree: &Tree) -> Vec<Insn> {\n    let mut program = Vec::new();\n\n    fn compile_tree(tree: &Tree, into: &mut Vec<Insn>) {\n        match tree {\n            Tree::Empty => {}\n            &Tree::CheckBool { offset } => into.push(Insn::CheckBool(offset)),\n            Tree::Sequence { sub_trees } => {\n                for tree in &**sub_trees {\n                    compile_tree(tree, into);\n                }\n            }\n            &Tree::CheckTag {\n                tag_offset,\n                num_variants,\n            } => into.push(Insn::CheckTag(CheckTag {\n                tag_offset,\n                num_variants,\n            })),\n            Tree::Sum {\n                tag_offset,\n                tag_data_processors,\n            } => {\n                // Add the branching instruction itself.\n                let num_variants = tag_data_processors.len();\n                into.push(Insn::CheckReadTagRelBranch(CheckTag {\n                    tag_offset: *tag_offset,\n                    num_variants: num_variants as u8,\n                }));\n                // Add N slots for \"to variant goto\"s.\n                let to_branches = into.len();\n                into.extend(repeat_n(Insn::FIXUP, num_variants));\n                // Compile the branches.\n                let mut from_variant_gotos = Vec::with_capacity(num_variants);\n                for (tag, branch) in tag_data_processors.iter().enumerate() {\n                    // Fixup the to-variant jump address.\n                    into[to_branches + tag] = Insn::Goto(into.len() as u16);\n                    // Compile the branch.\n                    compile_tree(branch, into);\n                    // Add jump-out gotos that we'll fixup later to store the after-sum address.\n                    from_variant_gotos.push(into.len());\n                    into.push(Insn::FIXUP);\n                }\n                // Fixup the jump-out-from-variant addresses.\n                let goto_addr = into.len();\n                for idx in from_variant_gotos {\n                    into[idx] = Insn::Goto(goto_addr as u16);\n                }\n            }\n        }\n    }\n\n    compile_tree(tree, &mut program);\n    remove_trailing_gotos(&mut program);\n    program\n}\n\n/// Remove any trailing gotos.\n///\n/// They are not needed as they will only go towards the end,\n/// so we can just cut them out.\nfn remove_trailing_gotos(program: &mut Vec<Insn>) {\n    for idx in (0..program.len()).rev() {\n        match program[idx] {\n            Insn::Goto(_) => program.pop(),\n            _ => break,\n        };\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct CheckTag {\n    /// The tag to check is stored at `start + tag_offset`.\n    tag_offset: u16,\n    /// The number of variants there are.\n    /// The read tag must be `< num_variants`.\n    num_variants: u8,\n}\n\n/// The instruction set of a [`StaticBsatnValidator`].\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum Insn {\n    /// Visit the byte at offset `start + N`\n    /// and assert that it is 0 or 1, i.e., a valid `bool`.\n    CheckBool(u16),\n\n    /// Read the `tag` at `start + tag_offset`\n    /// and validate that `tag < num_variants`.\n    CheckTag(CheckTag),\n\n    /// Read the `tag` at `start + tag_offset`\n    /// and validate that `tag < num_variants`.\n    /// Then move the instruction pointer forward by `tag + 1`.\n    /// The branch logic for the variant payload continues there.\n    CheckReadTagRelBranch(CheckTag),\n\n    /// Unconditionally branch to the instruction at `program[N]`\n    /// where `N > instruction pointer`.\n    Goto(u16),\n}\n\nimpl Insn {\n    const FIXUP: Self = Self::Goto(u16::MAX);\n}\n\nimpl MemoryUsage for Insn {}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct StaticBsatnValidator {\n    /// The list of instructions that make up this program.\n    insns: Arc<[Insn]>,\n}\n\nimpl MemoryUsage for StaticBsatnValidator {\n    fn heap_usage(&self) -> usize {\n        let Self { insns } = self;\n        insns.heap_usage()\n    }\n}\n\n/// Check that `bytes[tag_offset] < num_variants`.\n///\n/// SAFETY: `tag_offset < bytes.len()`.\nunsafe fn check_tag(bytes: &[u8], check: CheckTag) -> Result<u8, DecodeError> {\n    // SAFETY: the caller has guaranteed that `tag_offset < bytes.len()`.\n    let tag = *unsafe { bytes.get_unchecked(check.tag_offset as usize) };\n    if tag < check.num_variants {\n        Ok(tag)\n    } else {\n        Err(DecodeError::InvalidTag { tag, sum_name: None })\n    }\n}\n\n/// Validates that `bytes`, encoded in BSATN,\n/// is valid according to the validation `program`\n/// and a corresponding `static_layout`,\n///\n/// # Safety\n///\n/// The caller must guarantee that\n/// all offsets in `program` are `< static_layout.bsatn_length`.\npub(crate) unsafe fn validate_bsatn(\n    program: &StaticBsatnValidator,\n    static_layout: &StaticLayout,\n    bytes: &[u8],\n) -> Result<(), DecodeError> {\n    // Validate length of BSATN `bytes` against the expected length.\n    let expected = static_layout.bsatn_length as usize;\n    let given = bytes.len();\n    if expected != given {\n        return Err(DecodeError::InvalidLen { expected, given });\n    }\n\n    let program = &*program.insns;\n    let mut instr_ptr = 0;\n    loop {\n        match program.get(instr_ptr as usize).copied() {\n            None => break,\n            Some(Insn::CheckBool(offset)) => {\n                instr_ptr += 1;\n                // SAFETY: the caller has guaranteed\n                // that all offsets in `program` are `< expected`\n                // which we by now know is `= bytes.len()`.\n                let byte = *unsafe { bytes.get_unchecked(offset as usize) };\n                if byte > 1 {\n                    return Err(DecodeError::InvalidBool(byte));\n                }\n            }\n            Some(Insn::Goto(new_insn)) => instr_ptr = new_insn,\n            Some(Insn::CheckTag(check)) => {\n                // SAFETY: the caller has guaranteed\n                // that all offsets in `program` are `< expected`\n                // which we by now know is `= bytes.len()`.\n                unsafe { check_tag(bytes, check) }?;\n                instr_ptr += 1;\n            }\n            Some(Insn::CheckReadTagRelBranch(check)) => {\n                // SAFETY: the caller has guaranteed\n                // that all offsets in `program` are `< expected`\n                // which we by now know is `= bytes.len()`.\n                let tag = unsafe { check_tag(bytes, check) }?;\n                instr_ptr += tag as u16 + 1;\n            }\n        }\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\npub mod test {\n    use super::*;\n    use crate::{\n        bflatn_to::write_row_to_page, blob_store::HashMapBlobStore, page::Page, row_type_visitor::row_type_visitor,\n    };\n    use proptest::{prelude::*, prop_assert_eq, proptest};\n    use spacetimedb_sats::bsatn::to_vec;\n    use spacetimedb_sats::proptest::generate_typed_row;\n    use spacetimedb_sats::{AlgebraicType, ProductType};\n\n    proptest! {\n        // This test checks that `validate_bsatn(...).is_ok() == write_row_to_page(..).is_ok()`.\n        #![proptest_config(ProptestConfig {\n            max_global_rejects: 65536,\n            cases: if cfg!(miri) { 8 } else { 2048 },\n            ..<_>::default()\n        })]\n        #[test]\n        fn validation_same_as_write_row_to_pages((ty, val) in generate_typed_row()) {\n            let ty: RowTypeLayout = ty.into();\n            let Some(static_layout) = StaticLayout::for_row_type(&ty) else {\n                // `ty` has a var-len member or a sum with different payload lengths,\n                // so the fast path doesn't apply.\n                return Err(TestCaseError::reject(\"Var-length type\"));\n            };\n            let validator = static_bsatn_validator(&ty);\n            let bsatn = to_vec(&val).unwrap();\n            let res_validate = unsafe { validate_bsatn(&validator, &static_layout, &bsatn) };\n\n            let mut page = Page::new(ty.size());\n            let visitor = row_type_visitor(&ty);\n            let blob_store = &mut HashMapBlobStore::default();\n            let res_write = unsafe { write_row_to_page(&mut page, blob_store, &visitor, &ty, &val) };\n\n            prop_assert_eq!(res_validate.is_ok(), res_write.is_ok());\n        }\n\n        #[test]\n        fn bad_bool_validates_to_error(byte in 2u8..) {\n            let ty: RowTypeLayout = ProductType::from([AlgebraicType::Bool]).into();\n            let static_layout = StaticLayout::for_row_type(&ty).unwrap();\n            let validator = static_bsatn_validator(&ty);\n\n            let bsatn = [byte];\n            let res_validate = unsafe { validate_bsatn(&validator, &static_layout, &bsatn) };\n            prop_assert_eq!(res_validate, Err(DecodeError::InvalidBool(byte)));\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/static_layout.rs",
    "content": "//! This module implements a fast path for converting certain row types between BFLATN <-> BSATN.\n//!\n//! The key insight is that a majority of row types will have a known fixed length,\n//! with no variable-length members.\n//! BFLATN is designed with this in mind, storing fixed-length portions of rows inline,\n//! at the expense of an indirection to reach var-length columns like strings.\n//! A majority of these types will also have a fixed BSATN length,\n//! but note that BSATN stores sum values (enums) without padding,\n//! so row types which contain sums may not have a fixed BSATN length\n//! if the sum's variants have different \"live\" unpadded lengths.\n//!\n//! For row types with fixed BSATN lengths, we can reduce the BFLATN <-> BSATN conversions\n//! to a series of `memcpy`s, skipping over padding sequences.\n//! This is potentially much faster than the more general\n//! [`crate::bflatn_from::serialize_row_from_page`] and [`crate::bflatn_to::write_row_to_page`] ,\n//! which both traverse a [`RowTypeLayout`] and dispatch on the type of each column.\n//!\n//! For example, to serialize a row of type `(u64, u64, u32, u64)`,\n//! [`bflatn_from`] will do four dispatches, three calls to `serialize_u64` and one to `serialize_u32`.\n//! This module will make 2 `memcpy`s (or actually, `<[u8]>::copy_from_slice`s):\n//! one of 20 bytes to copy the leading `(u64, u64, u32)`, which contains no padding,\n//! and then one of 8 bytes to copy the trailing `u64`, skipping over 4 bytes of padding in between.\n\nuse super::{\n    indexes::{Byte, Bytes},\n    util::range_move,\n};\nuse core::mem::MaybeUninit;\nuse core::ptr;\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::slim_slice::SlimSmallSliceBox;\nuse spacetimedb_sats::bsatn::BufReservedFill;\nuse spacetimedb_sats::layout::{\n    AlgebraicTypeLayout, HasLayout, PrimitiveType, ProductTypeElementLayout, ProductTypeLayoutView, RowTypeLayout,\n    SumTypeLayout, SumTypeVariantLayout,\n};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n/// A precomputed layout for a type whose encoded BSATN and BFLATN lengths are both known constants,\n/// enabling fast BFLATN <-> BSATN conversions.\n#[derive(PartialEq, Eq, Debug, Clone)]\n#[repr(align(8))]\npub struct StaticLayout {\n    /// The length of the encoded BSATN representation of a row of this type,\n    /// in bytes.\n    ///\n    /// Storing this allows us to pre-allocate correctly-sized buffers,\n    /// avoiding potentially-expensive `realloc`s.\n    pub(crate) bsatn_length: u16,\n\n    /// A series of `memcpy` invocations from a BFLATN src/dst <-> a BSATN src/dst\n    /// which are sufficient to convert BSATN to BFLATN and vice versa.\n    fields: SlimSmallSliceBox<MemcpyField, 3>,\n}\n\nimpl MemoryUsage for StaticLayout {\n    fn heap_usage(&self) -> usize {\n        let Self { bsatn_length, fields } = self;\n        bsatn_length.heap_usage() + fields.heap_usage()\n    }\n}\n\nimpl StaticLayout {\n    /// Serialize `row` from BFLATN to BSATN into `buf`.\n    ///\n    /// # Safety\n    ///\n    /// - `buf` must be at least `self.bsatn_length` long.\n    /// - `row` must store a valid, initialized instance of the BFLATN row type\n    ///   for which `self` was computed.\n    ///   As a consequence of this, for every `field` in `self.fields`,\n    ///   `row[field.bflatn_offset .. field.bflatn_offset + length]` will be initialized.\n    unsafe fn serialize_row_into(&self, buf: &mut [MaybeUninit<Byte>], row: &Bytes) {\n        debug_assert!(buf.len() >= self.bsatn_length as usize);\n        for field in &*self.fields {\n            // SAFETY: forward caller requirements.\n            unsafe { field.copy_bflatn_to_bsatn(row, buf) };\n        }\n    }\n\n    /// Serialize `row` from BFLATN to BSATN into a `Vec<u8>`.\n    ///\n    /// # Safety\n    ///\n    /// - `row` must store a valid, initialized instance of the BFLATN row type\n    ///   for which `self` was computed.\n    ///   As a consequence of this, for every `field` in `self.fields`,\n    ///   `row[field.bflatn_offset .. field.bflatn_offset + length]` will be initialized.\n    pub(crate) unsafe fn serialize_row_into_vec(&self, row: &Bytes) -> Vec<u8> {\n        let mut buf = Vec::new();\n\n        // SAFETY: Forward caller requirements.\n        unsafe { self.serialize_row_extend(&mut buf, row) };\n\n        buf\n    }\n\n    /// Serialize `row` from BFLATN to BSATN, appending the BSATN to `buf`.\n    ///\n    /// # Safety\n    ///\n    /// - `row` must store a valid, initialized instance of the BFLATN row type\n    ///   for which `self` was computed.\n    ///   As a consequence of this, for every `field` in `self.fields`,\n    ///   `row[field.bflatn_offset .. field.bflatn_offset + length]` will be initialized.\n    pub(crate) unsafe fn serialize_row_extend(&self, buf: &mut impl BufReservedFill, row: &Bytes) {\n        let len = self.bsatn_length as usize;\n        // Writes the row into the slice using a series of `memcpy`s.\n        // SAFETY:\n        // - Caller promised that `row` is valid for `self`.\n        // - `sink` was constructed with exactly the correct length above.\n        let filler = |sink: &mut _| unsafe {\n            self.serialize_row_into(sink, row);\n        };\n        // SAFETY:\n        // The closure `filler` will write exactly `len` bytes.\n        unsafe { buf.reserve_and_fill(len, filler) };\n    }\n\n    #[allow(unused)]\n    /// Deserializes the BSATN-encoded `row` into the BFLATN-encoded `buf`.\n    ///\n    /// - `row` must be at least `self.bsatn_length` long.\n    /// - `buf` must be ready to store an instance of the BFLATN row type\n    ///   for which `self` was computed.\n    ///   As a consequence of this, for every `field` in `self.fields`,\n    ///   `field.bflatn_offset .. field.bflatn_offset + length` must be in-bounds of `buf`.\n    pub(crate) unsafe fn deserialize_row_into(&self, buf: &mut Bytes, row: &[u8]) {\n        for field in &*self.fields {\n            // SAFETY: forward caller requirements.\n            unsafe { field.copy_bsatn_to_bflatn(row, buf) };\n        }\n    }\n\n    /// Compares `row_a` for equality against `row_b`.\n    ///\n    /// # Safety\n    ///\n    /// - `row` must store a valid, initialized instance of the BFLATN row type\n    ///   for which `self` was computed.\n    ///   As a consequence of this, for every `field` in `self.fields`,\n    ///   `row[field.bflatn_offset .. field.bflatn_offset + field.length]` will be initialized.\n    pub(crate) unsafe fn eq(&self, row_a: &Bytes, row_b: &Bytes) -> bool {\n        // No need to check the lengths.\n        // We assume they are of the same length.\n        self.fields.iter().all(|field| {\n            // SAFETY: The consequence of what the caller promised is that\n            // `row_(a/b).len() >= field.bflatn_offset + field.length >= field.bflatn_offset`.\n            unsafe { field.eq(row_a, row_b) }\n        })\n    }\n\n    /// Construct a `StaticLayout` for converting BFLATN rows of `row_type` <-> BSATN.\n    ///\n    /// Returns `None` if `row_type` contains a column which does not have a constant length in BSATN,\n    /// either a [`VarLenType`]\n    /// or a [`SumTypeLayout`] whose variants do not have the same \"live\" unpadded length.\n    pub fn for_row_type(row_type: &RowTypeLayout) -> Option<Self> {\n        if !row_type.layout().fixed {\n            // Don't bother computing the static layout if there are variable components.\n            return None;\n        }\n\n        let mut builder = LayoutBuilder::new_builder();\n        builder.visit_product(row_type.product())?;\n        Some(builder.build())\n    }\n}\n\n/// An identifier for a series of bytes within a BFLATN row\n/// which can be directly copied into an output BSATN buffer\n/// with a known length and offset or vice versa.\n///\n/// Within the row type's BFLATN layout, `row[bflatn_offset .. (bflatn_offset + length)]`\n/// must not contain any padding bytes,\n/// i.e. all of those bytes must be fully initialized if the row is initialized.\n#[derive(PartialEq, Eq, Debug, Copy, Clone)]\nstruct MemcpyField {\n    /// Offset in the BFLATN row from which to begin `memcpy`ing, in bytes.\n    bflatn_offset: u16,\n\n    /// Offset in the BSATN buffer to which to begin `memcpy`ing, in bytes.\n    // TODO(perf): Could be a running counter, but this way we just have all the `memcpy` args in one place.\n    // Should bench; I (pgoldman 2024-03-25) suspect this allows more insn parallelism and is therefore better.\n    bsatn_offset: u16,\n\n    /// Length to `memcpy`, in bytes.\n    length: u16,\n}\n\nimpl MemoryUsage for MemcpyField {}\n\nimpl MemcpyField {\n    /// Copies the bytes at `src[self.bflatn_offset .. self.bflatn_offset + self.length]`\n    /// into `dst[self.bsatn_offset .. self.bsatn_offset + self.length]`.\n    ///\n    /// # Safety\n    ///\n    /// 1. `src.len() >= self.bflatn_offset + self.length`.\n    /// 2. `dst.len() >= self.bsatn_offset + self.length`\n    unsafe fn copy_bflatn_to_bsatn(&self, src: &Bytes, dst: &mut [MaybeUninit<Byte>]) {\n        let src_offset = self.bflatn_offset as usize;\n        let dst_offset = self.bsatn_offset as usize;\n\n        let len = self.length as usize;\n        let src = src.as_ptr();\n        let dst = dst.as_mut_ptr();\n        // SAFETY: per 1., it follows that `src_offset` is in bounds of `src`.\n        let src = unsafe { src.add(src_offset) };\n        // SAFETY: per 2., it follows that `dst_offset` is in bounds of `dst`.\n        let dst = unsafe { dst.add(dst_offset) };\n        let dst = dst.cast();\n\n        // SAFETY:\n        // 1. `src` is valid for reads for `len` bytes per caller requirement 1.\n        //    and because `src` was derived from a shared slice.\n        // 2. `dst` is valid for writes for `len` bytes per caller requirement 2.\n        //    and because `dst` was derived from an exclusive slice.\n        // 3. Alignment for `u8` is trivially satisfied for any pointer.\n        // 4. As `src` and `dst` were derived from shared and exclusive slices, they cannot overlap.\n        unsafe { ptr::copy_nonoverlapping(src, dst, len) }\n    }\n\n    /// Copies the bytes at `src[self.bsatn_offset .. self.bsatn_offset + self.length]`\n    /// into `dst[self.bflatn_offset .. self.bflatn_offset + self.length]`.\n    ///\n    /// # Safety\n    ///\n    /// 1. `src.len() >= self.bsatn_offset + self.length`.\n    /// 2. `dst.len() >= self.bflatn_offset + self.length`\n    unsafe fn copy_bsatn_to_bflatn(&self, src: &Bytes, dst: &mut Bytes) {\n        let src_offset = self.bsatn_offset as usize;\n        let dst_offset = self.bflatn_offset as usize;\n\n        let len = self.length as usize;\n        let src = src.as_ptr();\n        let dst = dst.as_mut_ptr();\n        // SAFETY: per 1., it follows that `src_offset` is in bounds of `src`.\n        let src = unsafe { src.add(src_offset) };\n        // SAFETY: per 2., it follows that `dst_offset` is in bounds of `dst`.\n        let dst = unsafe { dst.add(dst_offset) };\n\n        // SAFETY:\n        // 1. `src` is valid for reads for `len` bytes per caller requirement 1.\n        //    and because `src` was derived from a shared slice.\n        // 2. `dst` is valid for writes for `len` bytes per caller requirement 2.\n        //    and because `dst` was derived from an exclusive slice.\n        // 3. Alignment for `u8` is trivially satisfied for any pointer.\n        // 4. As `src` and `dst` were derived from shared and exclusive slices, they cannot overlap.\n        unsafe { ptr::copy_nonoverlapping(src, dst, len) }\n    }\n\n    /// Compares `row_a` and `row_b` for equality in this field.\n    ///\n    /// # Safety\n    ///\n    /// - `row_a.len() >= self.bflatn_offset + self.length`\n    /// - `row_b.len() >= self.bflatn_offset + self.length`\n    unsafe fn eq(&self, row_a: &Bytes, row_b: &Bytes) -> bool {\n        let range = range_move(0..self.length as usize, self.bflatn_offset as usize);\n        let range2 = range.clone();\n        // SAFETY: The `range` is in bounds as\n        // `row_a.len() >= self.bflatn_offset + self.length >= self.bflatn_offset`.\n        let row_a_field = unsafe { row_a.get_unchecked(range) };\n        // SAFETY: The `range` is in bounds as\n        // `row_b.len() >= self.bflatn_offset + self.length >= self.bflatn_offset`.\n        let row_b_field = unsafe { row_b.get_unchecked(range2) };\n        row_a_field == row_b_field\n    }\n\n    fn is_empty(&self) -> bool {\n        self.length == 0\n    }\n}\n\n/// A builder for a [`StaticLayout`].\nstruct LayoutBuilder {\n    /// Always at least one element.\n    fields: Vec<MemcpyField>,\n}\n\nimpl LayoutBuilder {\n    fn new_builder() -> Self {\n        Self {\n            fields: vec![MemcpyField {\n                bflatn_offset: 0,\n                bsatn_offset: 0,\n                length: 0,\n            }],\n        }\n    }\n\n    fn build(self) -> StaticLayout {\n        let LayoutBuilder { fields } = self;\n        let fields: SmallVec<[_; 3]> = fields.into_iter().filter(|field| !field.is_empty()).collect();\n        let fields: SlimSmallSliceBox<MemcpyField, 3> = fields.into();\n        let bsatn_length = fields.last().map(|last| last.bsatn_offset + last.length).unwrap_or(0);\n\n        StaticLayout { bsatn_length, fields }\n    }\n\n    fn current_field(&self) -> &MemcpyField {\n        self.fields.last().unwrap()\n    }\n\n    fn current_field_mut(&mut self) -> &mut MemcpyField {\n        self.fields.last_mut().unwrap()\n    }\n\n    fn next_bflatn_offset(&self) -> u16 {\n        let last = self.current_field();\n        last.bflatn_offset + last.length\n    }\n\n    fn next_bsatn_offset(&self) -> u16 {\n        let last = self.current_field();\n        last.bsatn_offset + last.length\n    }\n\n    fn visit_product(&mut self, product: ProductTypeLayoutView) -> Option<()> {\n        let base_bflatn_offset = self.next_bflatn_offset();\n        for elt in product.elements.iter() {\n            self.visit_product_element(elt, base_bflatn_offset)?;\n        }\n        Some(())\n    }\n\n    fn visit_product_element(&mut self, elt: &ProductTypeElementLayout, product_base_offset: u16) -> Option<()> {\n        let elt_offset = product_base_offset + elt.offset;\n        let next_bflatn_offset = self.next_bflatn_offset();\n        if next_bflatn_offset != elt_offset {\n            // Padding between previous element and this element,\n            // so start a new field.\n            //\n            // Note that this is the only place we have to reason about alignment and padding\n            // because the enclosing `ProductTypeLayout` has already computed valid aligned offsets\n            // for the elements.\n\n            let bsatn_offset = self.next_bsatn_offset();\n            self.fields.push(MemcpyField {\n                bsatn_offset,\n                bflatn_offset: elt_offset,\n                length: 0,\n            });\n        }\n        self.visit_value(&elt.ty)\n    }\n\n    fn visit_value(&mut self, val: &AlgebraicTypeLayout) -> Option<()> {\n        match val {\n            AlgebraicTypeLayout::Sum(sum) => self.visit_sum(sum),\n            AlgebraicTypeLayout::Product(prod) => self.visit_product(prod.view()),\n            AlgebraicTypeLayout::Primitive(prim) => {\n                self.visit_primitive(prim);\n                Some(())\n            }\n\n            // Var-len types (obviously) don't have a known BSATN length,\n            // so fail.\n            AlgebraicTypeLayout::VarLen(_) => None,\n        }\n    }\n\n    fn visit_sum(&mut self, sum: &SumTypeLayout) -> Option<()> {\n        // If the sum has no variants, it's the never type, so there's no point in computing a layout.\n        let first_variant = sum.variants.first()?;\n\n        let variant_layout = |variant: &SumTypeVariantLayout| {\n            let mut builder = LayoutBuilder::new_builder();\n            builder.visit_value(&variant.ty)?;\n            Some(builder.build())\n        };\n\n        // Check that the variants all have the same `StaticLayout`.\n        // If they don't, bail.\n        let first_variant_layout = variant_layout(first_variant)?;\n        for later_variant in &sum.variants[1..] {\n            let later_variant_layout = variant_layout(later_variant)?;\n            if later_variant_layout != first_variant_layout {\n                return None;\n            }\n        }\n\n        if first_variant_layout.bsatn_length == 0 {\n            // For C-style enums (those without payloads),\n            // simply serialize the tag and move on.\n            self.current_field_mut().length += 1;\n            return Some(());\n        }\n\n        // Now that we've reached this point, we know that `first_variant_layout`\n        // applies to the values of all the variants.\n\n        let tag_bflatn_offset = self.next_bflatn_offset();\n        let payload_bflatn_offset = tag_bflatn_offset + sum.payload_offset;\n\n        let tag_bsatn_offset = self.next_bsatn_offset();\n        let payload_bsatn_offset = tag_bsatn_offset + 1;\n\n        // Serialize the tag, consolidating into the previous memcpy if possible.\n        self.visit_primitive(&PrimitiveType::U8);\n\n        if sum.payload_offset > 1 {\n            // Add an empty marker field to keep track of padding.\n            self.fields.push(MemcpyField {\n                bflatn_offset: payload_bflatn_offset,\n                bsatn_offset: payload_bsatn_offset,\n                length: 0,\n            });\n        } // Otherwise, nothing to do.\n\n        // Lay out the variants.\n        // Since all variants have the same layout, we just use the first one.\n        self.visit_value(&first_variant.ty)?;\n\n        Some(())\n    }\n\n    fn visit_primitive(&mut self, prim: &PrimitiveType) {\n        self.current_field_mut().length += prim.size() as u16\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::{blob_store::HashMapBlobStore, page_pool::PagePool};\n    use proptest::prelude::*;\n    use spacetimedb_sats::{bsatn, proptest::generate_typed_row, AlgebraicType, ProductType};\n\n    fn assert_expected_layout(ty: ProductType, bsatn_length: u16, fields: &[(u16, u16, u16)]) {\n        let expected_layout = StaticLayout {\n            bsatn_length,\n            fields: fields\n                .iter()\n                .copied()\n                .map(|(bflatn_offset, bsatn_offset, length)| MemcpyField {\n                    bflatn_offset,\n                    bsatn_offset,\n                    length,\n                })\n                .collect::<SmallVec<_>>()\n                .into(),\n        };\n        let row_type = RowTypeLayout::from(ty.clone());\n        let Some(computed_layout) = StaticLayout::for_row_type(&row_type) else {\n            panic!(\"assert_expected_layout: Computed `None` for row {row_type:#?}\\nExpected:{expected_layout:#?}\");\n        };\n        assert_eq!(\n            computed_layout, expected_layout,\n            \"assert_expected_layout: Computed layout (left) doesn't match expected (right) for {ty:?}\",\n        );\n    }\n\n    #[test]\n    fn known_types_expected_layout_plain() {\n        for prim in [\n            AlgebraicType::Bool,\n            AlgebraicType::U8,\n            AlgebraicType::I8,\n            AlgebraicType::U16,\n            AlgebraicType::I16,\n            AlgebraicType::U32,\n            AlgebraicType::I32,\n            AlgebraicType::U64,\n            AlgebraicType::I64,\n            AlgebraicType::U128,\n            AlgebraicType::I128,\n            AlgebraicType::U256,\n            AlgebraicType::I256,\n        ] {\n            let size = AlgebraicTypeLayout::from(prim.clone()).size() as u16;\n            assert_expected_layout(ProductType::from([prim]), size, &[(0, 0, size)]);\n        }\n    }\n\n    #[test]\n    fn known_types_expected_layout_complex() {\n        for (ty, bsatn_length, fields) in [\n            (ProductType::new([].into()), 0, &[][..]),\n            (\n                ProductType::from([AlgebraicType::sum([\n                    AlgebraicType::U8,\n                    AlgebraicType::I8,\n                    AlgebraicType::Bool,\n                ])]),\n                2,\n                // In BFLATN, sums have padding after the tag to the max alignment of any variant payload.\n                // In this case, 0 bytes of padding, because all payloads are aligned to 1.\n                // Since there's no padding, the memcpys can be consolidated.\n                &[(0, 0, 2)][..],\n            ),\n            (\n                ProductType::from([AlgebraicType::sum([\n                    AlgebraicType::product([\n                        AlgebraicType::U8,\n                        AlgebraicType::U8,\n                        AlgebraicType::U8,\n                        AlgebraicType::U8,\n                    ]),\n                    AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U16]),\n                    AlgebraicType::U32,\n                ])]),\n                5,\n                // In BFLATN, sums have padding after the tag to the max alignment of any variant payload.\n                // In this case, 3 bytes of padding.\n                &[(0, 0, 1), (4, 1, 4)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::I128]),\n                    AlgebraicType::U32,\n                ]),\n                21,\n                // In BFLATN, sums have padding after the tag to the max alignment of any variant payload.\n                // In this case, 15 bytes of padding.\n                &[(0, 0, 1), (16, 1, 20)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::sum([AlgebraicType::U256, AlgebraicType::I256]),\n                    AlgebraicType::U32,\n                ]),\n                37,\n                // In BFLATN, sums have padding after the tag to the max alignment of any variant payload.\n                // In this case, 15 bytes of padding.\n                &[(0, 0, 1), (32, 1, 36)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::U256,\n                    AlgebraicType::U128,\n                    AlgebraicType::U64,\n                    AlgebraicType::U32,\n                    AlgebraicType::U16,\n                    AlgebraicType::U8,\n                ]),\n                63,\n                &[(0, 0, 63)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::U8,\n                    AlgebraicType::U16,\n                    AlgebraicType::U32,\n                    AlgebraicType::U64,\n                    AlgebraicType::U128,\n                ]),\n                31,\n                &[(0, 0, 1), (2, 1, 30)][..],\n            ),\n            // Make sure sums with no variant data are handled correctly.\n            (\n                ProductType::from([AlgebraicType::sum([AlgebraicType::product::<[AlgebraicType; 0]>([])])]),\n                1,\n                &[(0, 0, 1)][..],\n            ),\n            (\n                ProductType::from([AlgebraicType::sum([\n                    AlgebraicType::product::<[AlgebraicType; 0]>([]),\n                    AlgebraicType::product::<[AlgebraicType; 0]>([]),\n                ])]),\n                1,\n                &[(0, 0, 1)][..],\n            ),\n            // Various experiments with 1-byte-aligned payloads.\n            // These are particularly nice for memcpy consolidation as there's no padding.\n            (\n                ProductType::from([AlgebraicType::sum([\n                    AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8]),\n                    AlgebraicType::product([AlgebraicType::Bool, AlgebraicType::Bool]),\n                ])]),\n                3,\n                &[(0, 0, 3)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::sum([AlgebraicType::Bool, AlgebraicType::U8]),\n                    AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::Bool]),\n                ]),\n                4,\n                &[(0, 0, 4)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::U16,\n                    AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::Bool]),\n                    AlgebraicType::U16,\n                ]),\n                6,\n                &[(0, 0, 6)][..],\n            ),\n            (\n                ProductType::from([\n                    AlgebraicType::U32,\n                    AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::I16]),\n                    AlgebraicType::U32,\n                ]),\n                11,\n                &[(0, 0, 5), (6, 5, 6)][..],\n            ),\n        ] {\n            assert_expected_layout(ty, bsatn_length, fields);\n        }\n    }\n\n    #[test]\n    fn known_types_not_applicable() {\n        for ty in [\n            AlgebraicType::String,\n            AlgebraicType::bytes(),\n            AlgebraicType::never(),\n            AlgebraicType::array(AlgebraicType::U16),\n            AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U16]),\n        ] {\n            let layout = RowTypeLayout::from(ProductType::from([ty]));\n            if let Some(computed) = StaticLayout::for_row_type(&layout) {\n                panic!(\"Expected row type not to have a constant BSATN layout!\\nRow type: {layout:#?}\\nBSATN layout: {computed:#?}\");\n            }\n        }\n    }\n\n    proptest! {\n        // The tests `known_bsatn_same_as_bflatn_from`\n        // and `known_bflatn_same_as_pv_from` generate a lot of rejects,\n        // as a vast majority of the space of `ProductType` does not have a fixed BSATN length.\n        // Writing a proptest generator which produces only types that have a fixed BSATN length\n        // seems hard, because we'd have to generate sums with known matching layouts,\n        // so we just bump the `max_global_rejects` up as high as it'll go and move on with our lives.\n        //\n        // Note that I (pgoldman 2024-03-21) tried modifying `generate_typed_row`\n        // to not emit `String`, `Array` or `Map` types (the trivially var-len types),\n        // but did not see a meaningful decrease in the number of rejects.\n        // This is because a majority of the var-len BSATN types in the `generate_typed_row` space\n        // are due to sums with inconsistent payload layouts.\n        //\n        // We still include the test `known_bsatn_same_as_bsatn_from`\n        // because it tests row types not covered in `known_types_expected_layout`,\n        // especially larger types with unusual sequences of aligned fields.\n        #![proptest_config(ProptestConfig { max_global_rejects: 65536, ..Default::default()})]\n\n        #[test]\n        fn known_bsatn_same_as_bflatn_from((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = crate::table::test::table(ty);\n            let Some(static_layout) = table.static_layout().cloned() else {\n                // `ty` has a var-len member or a sum with different payload lengths,\n                // so the fast path doesn't apply.\n                return Err(TestCaseError::reject(\"Var-length type\"));\n            };\n\n            let (_, row_ref) = table.insert(&pool, &mut blob_store, &val).unwrap();\n            let bytes = row_ref.get_row_data();\n\n            let slow_path = bsatn::to_vec(&row_ref).unwrap();\n\n            let fast_path = unsafe {\n                static_layout.serialize_row_into_vec(bytes)\n            };\n\n            let mut fast_path2 = Vec::new();\n            unsafe {\n                static_layout.serialize_row_extend(&mut fast_path2, bytes)\n            };\n\n            assert_eq!(slow_path, fast_path);\n            assert_eq!(slow_path, fast_path2);\n        }\n\n        #[test]\n        fn known_bflatn_same_as_pv_from((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = crate::table::test::table(ty);\n            let Some(static_layout) = table.static_layout().cloned() else {\n                // `ty` has a var-len member or a sum with different payload lengths,\n                // so the fast path doesn't apply.\n                return Err(TestCaseError::reject(\"Var-length type\"));\n            };\n            let bsatn = bsatn::to_vec(&val).unwrap();\n\n            let (_, row_ref) = table.insert(&pool, &mut blob_store, &val).unwrap();\n            let slow_path = row_ref.get_row_data();\n\n            let mut fast_path = vec![0u8; slow_path.len()];\n            unsafe {\n                static_layout.deserialize_row_into(&mut fast_path, &bsatn);\n            };\n\n            assert_eq!(slow_path, fast_path);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table.rs",
    "content": "use crate::{\n    blob_store::NullBlobStore,\n    table_index::{IndexCannotSeekRange, IndexKind},\n};\n\nuse super::{\n    bflatn_from::serialize_row_from_page,\n    bflatn_to::{write_row_to_pages, write_row_to_pages_bsatn, Error},\n    blob_store::BlobStore,\n    eq::eq_row_in_page,\n    eq_to_pv::eq_row_in_page_to_pv,\n    indexes::{Bytes, PageIndex, PageOffset, RowHash, RowPointer, SquashedOffset, PAGE_DATA_SIZE},\n    page::{FixedLenRowsIter, Page},\n    page_pool::PagePool,\n    pages::Pages,\n    pointer_map::PointerMap,\n    read_column::{ReadColumn, TypeError},\n    row_hash::hash_row_in_page,\n    row_type_visitor::{row_type_visitor, VarLenVisitorProgram},\n    static_assert_size,\n    static_bsatn_validator::{static_bsatn_validator, validate_bsatn, StaticBsatnValidator},\n    static_layout::StaticLayout,\n    table_index::{TableIndex, TableIndexPointIter, TableIndexRangeIter},\n    var_len::VarLenMembers,\n};\nuse core::{fmt, ptr};\nuse core::{\n    hash::{Hash, Hasher},\n    hint::unreachable_unchecked,\n};\nuse core::{mem, ops::RangeBounds};\nuse derive_more::{Add, AddAssign, From, Sub, SubAssign};\nuse enum_as_inner::EnumAsInner;\nuse smallvec::SmallVec;\nuse spacetimedb_primitives::{ColId, ColList, IndexId, SequenceId, TableId};\nuse spacetimedb_sats::{\n    algebraic_value::ser::ValueSerializer,\n    bsatn::{self, ser::BsatnError, BufReservedFill, DecodeError, ToBsatn},\n    buffer::BufWriter,\n    de::DeserializeOwned,\n    i256,\n    product_value::InvalidFieldError,\n    satn::Satn,\n    ser::{Serialize, Serializer},\n    u256, AlgebraicValue, ProductType, ProductValue,\n};\nuse spacetimedb_sats::{\n    layout::{AlgebraicTypeLayout, IncompatibleTypeLayoutError, PrimitiveType, RowTypeLayout, Size},\n    Typespace,\n};\nuse spacetimedb_sats::{memory_usage::MemoryUsage, raw_identifier::RawIdentifier};\nuse spacetimedb_schema::{\n    def::{BTreeAlgorithm, IndexAlgorithm},\n    identifier::Identifier,\n    schema::{columns_to_row_type, ColumnSchema, IndexSchema, TableSchema},\n    table_name::TableName,\n};\nuse std::{\n    collections::{btree_map, BTreeMap},\n    sync::Arc,\n};\nuse thiserror::Error;\n\n/// The number of bytes used by, added to, or removed from a [`Table`]'s share of a [`BlobStore`].\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Default, From, Add, Sub, AddAssign, SubAssign)]\npub struct BlobNumBytes(usize);\n\nimpl MemoryUsage for BlobNumBytes {}\n\npub type SeqIdList = SmallVec<[SequenceId; 4]>;\nstatic_assert_size!(SeqIdList, 24);\n\n/// A database table containing the row schema, the rows, and indices.\n///\n/// The table stores the rows into a page manager\n/// and uses an internal map to ensure that no identical row is stored more than once.\n#[derive(Debug, PartialEq, Eq)]\npub struct Table {\n    /// Page manager and row layout grouped together, for `RowRef` purposes.\n    inner: TableInner,\n    /// Maps `RowHash -> [RowPointer]` where a [`RowPointer`] points into `pages`.\n    /// A [`PointerMap`] is effectively a specialized unique index on all the columns.\n    ///\n    /// In tables without any other unique constraints,\n    /// the pointer map is used to enforce set semantics,\n    /// i.e. to prevent duplicate rows.\n    /// If `self.indexes` contains at least one unique index,\n    /// duplicate rows are impossible regardless, so this will be `None`.\n    pointer_map: Option<PointerMap>,\n    /// The indices associated with a set of columns of the table.\n    pub indexes: BTreeMap<IndexId, TableIndex>,\n    /// The schema of the table, from which the type, and other details are derived.\n    pub schema: Arc<TableSchema>,\n    /// `SquashedOffset::TX_STATE` or `SquashedOffset::COMMITTED_STATE`\n    /// depending on whether this is a tx scratchpad table\n    /// or a committed table.\n    squashed_offset: SquashedOffset,\n    /// Stores number of rows present in table.\n    pub row_count: u64,\n    /// Stores the sum total number of bytes that each blob object in the table occupies.\n    ///\n    /// Note that the [`HashMapBlobStore`] does ref-counting and de-duplication,\n    /// but this sum will count an object each time its hash is mentioned, rather than just once.\n    blob_store_bytes: BlobNumBytes,\n    /// Indicates whether this is a scheduler table or not.\n    ///\n    /// This is an optimization to avoid checking the schema in e.g., `InstanceEnv::{insert, update}`.\n    is_scheduler: bool,\n}\n\ntype StaticLayoutInTable = Option<(StaticLayout, StaticBsatnValidator)>;\n\n/// The part of a `Table` concerned only with storing rows.\n///\n/// Separated from the \"outer\" parts of `Table`, especially the `indexes`,\n/// so that `RowRef` can borrow only the `TableInner`,\n/// while other mutable references to the `indexes` exist.\n/// This is necessary because index insertions and deletions take a `RowRef` as an argument,\n/// from which they [`ReadColumn::read_column`] their keys.\n#[derive(Debug, PartialEq, Eq)]\npub(crate) struct TableInner {\n    /// The type of rows this table stores, with layout information included.\n    row_layout: RowTypeLayout,\n    /// A [`StaticLayout`] for fast BFLATN <-> BSATN conversion,\n    /// if the [`RowTypeLayout`] has a static BSATN length and layout.\n    ///\n    /// A [`StaticBsatnValidator`] is also included.\n    /// It's used to validate BSATN-encoded rows before converting to BFLATN.\n    static_layout: StaticLayoutInTable,\n    /// The visitor program for `row_layout`.\n    ///\n    /// Must be in the `TableInner` so that [`RowRef::blob_store_bytes`] can use it.\n    visitor_prog: VarLenVisitorProgram,\n    /// The page manager that holds rows\n    /// including both their fixed and variable components.\n    pages: Pages,\n}\n\nimpl TableInner {\n    /// Assumes `ptr` is a present row in `self` and returns a [`RowRef`] to it.\n    ///\n    /// # Safety\n    ///\n    /// The requirement is that `table.is_row_present(ptr)` must hold,\n    /// where `table` is the `Table` which contains this `TableInner`.\n    /// That is, `ptr` must refer to a row within `self`\n    /// which was previously inserted and has not been deleted since.\n    ///\n    /// This means:\n    /// - The `PageIndex` of `ptr` must be in-bounds for `self.pages`.\n    /// - The `PageOffset` of `ptr` must be properly aligned for the row type of `self`,\n    ///   and must refer to a valid, live row in that page.\n    /// - The `SquashedOffset` of `ptr` must match the enclosing table's `table.squashed_offset`.\n    ///\n    /// Showing that `ptr` was the result of a call to [`Table::insert(table, ..)`]\n    /// and has not been passed to [`Table::delete_internal_skip_pointer_map(table, ..)`]\n    /// is sufficient to demonstrate all of these properties.\n    unsafe fn get_row_ref_unchecked<'a>(\n        &'a self,\n        blob_store: &'a dyn BlobStore,\n        squashed_offset: SquashedOffset,\n        ptr: RowPointer,\n    ) -> RowRef<'a> {\n        // SAFETY: Forward caller requirements.\n        unsafe { RowRef::new(self, blob_store, squashed_offset, ptr) }\n    }\n\n    /// Returns whether the row at `ptr` is present or not.\n    // TODO: Remove all uses of this method,\n    //       or more likely, gate them behind `debug_assert!`\n    //       so they don't have semantic meaning.\n    //\n    //       Unlike the previous `locking_tx_datastore::Table`'s `RowId`,\n    //       `RowPointer` is not content-addressed.\n    //       This means it is possible to:\n    //       - have a `RowPointer` A* to row A,\n    //       - Delete row A,\n    //       - Insert row B into the same storage as freed from A,\n    //       - Test `is_row_present(A*)`, which falsely reports that row A is still present.\n    //\n    //       In the final interface, this method is superfluous anyways,\n    //       as `RowPointer` is not part of our public interface.\n    //       Instead, we will always discover a known-present `RowPointer`\n    //       during a table scan or index seek.\n    //       As such, our `delete` and `insert` methods can be `unsafe`\n    //       and trust that the `RowPointer` is valid.\n    fn is_row_present(&self, _squashed_offset: SquashedOffset, ptr: RowPointer) -> bool {\n        if _squashed_offset != ptr.squashed_offset() {\n            return false;\n        }\n        let Some((page, offset)) = self.try_page_and_offset(ptr) else {\n            return false;\n        };\n        page.has_row_offset(self.row_layout.size(), offset)\n    }\n\n    fn try_page_and_offset(&self, ptr: RowPointer) -> Option<(&Page, PageOffset)> {\n        (ptr.page_index().idx() < self.pages.len()).then(|| (&self.pages[ptr.page_index()], ptr.page_offset()))\n    }\n\n    /// Returns the page and page offset that `ptr` points to.\n    fn page_and_offset(&self, ptr: RowPointer) -> (&Page, PageOffset) {\n        self.try_page_and_offset(ptr).unwrap()\n    }\n}\n\nstatic_assert_size!(Table, 264);\n\nimpl MemoryUsage for Table {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            inner,\n            pointer_map,\n            indexes,\n            // MEMUSE: intentionally ignoring schema\n            schema: _,\n            squashed_offset,\n            row_count,\n            blob_store_bytes,\n            is_scheduler,\n        } = self;\n        inner.heap_usage()\n            + pointer_map.heap_usage()\n            + indexes.heap_usage()\n            + squashed_offset.heap_usage()\n            + row_count.heap_usage()\n            + blob_store_bytes.heap_usage()\n            + is_scheduler.heap_usage()\n    }\n}\n\nimpl MemoryUsage for TableInner {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            row_layout,\n            static_layout,\n            visitor_prog,\n            pages,\n        } = self;\n        row_layout.heap_usage() + static_layout.heap_usage() + visitor_prog.heap_usage() + pages.heap_usage()\n    }\n}\n\n/// There was already a row with the same value.\n#[derive(Error, Debug, PartialEq, Eq)]\n#[error(\"Duplicate insertion of row {0:?} violates set semantics\")]\npub struct DuplicateError(pub RowPointer);\n\n/// Various error that can happen on table insertion.\n#[derive(Error, Debug, PartialEq, Eq, EnumAsInner)]\npub enum InsertError {\n    /// There was already a row with the same value.\n    #[error(transparent)]\n    Duplicate(#[from] DuplicateError),\n\n    /// Couldn't write the row to the page manager.\n    #[error(transparent)]\n    Bflatn(#[from] super::bflatn_to::Error),\n\n    /// Some index related error occurred.\n    #[error(transparent)]\n    IndexError(#[from] UniqueConstraintViolation),\n}\n\n/// Errors that can occur while trying to read a value via bsatn.\n#[derive(Error, Debug)]\npub enum ReadViaBsatnError {\n    #[error(transparent)]\n    BSatnError(#[from] BsatnError),\n\n    #[error(transparent)]\n    DecodeError(#[from] DecodeError),\n}\n\n/// Error that can occur when attempting to [`Table::change_columns_to`] a different row type.\n///\n/// This will usually be [`Box`]ed, as it's large enough to trigger\n/// [Clippy's `result_large_err` lint](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err).\n///\n/// Seeing this error represents a bug in SpacetimeDB, as incompatible column-type-changing migrations\n/// are supposed to be detected by the checks in [`spacetimedb_schema::auto_migrate`].\n#[derive(Error, Debug)]\n#[error(\"Cannot change the columns of table `{table_name}` with id {table_id} from `{old:?}` to `{new:?}`: {reason}\")]\npub struct ChangeColumnsError {\n    table_id: TableId,\n    table_name: TableName,\n    old: Vec<ColumnSchema>,\n    new: Vec<ColumnSchema>,\n    reason: ChangeColumnsErrorReason,\n}\n\n/// More specific reason that a [`Table::change_columns_to`] resulted in a [`ChangeColumnsError`],\n/// contained in that error alongside the table metadata and new and old schemas.\n#[derive(Error, Debug)]\npub enum ChangeColumnsErrorReason {\n    #[error(\"Layout of schedule table's at column changed from {old:?} to {new:?}\")]\n    ScheduleAtColumnChanged {\n        index: usize,\n        old: AlgebraicTypeLayout,\n        new: AlgebraicTypeLayout,\n    },\n    #[error(\"Layout of schedule table's id column changed from {old:?} to {new:?}\")]\n    ScheduleIdColumnChanged {\n        index: usize,\n        old: AlgebraicTypeLayout,\n        new: AlgebraicTypeLayout,\n    },\n    #[error(transparent)]\n    IncompatibleRowLayout(#[from] IncompatibleTypeLayoutError),\n}\n\n/// Error that can occur when attempting to [`Table::validate_add_columns_schema`].\n///\n/// Like [`ChangeColumnsError`], this should never normally be seen at runtime.\n/// Any such error indicates a **bug in SpacetimeDB**, since incompatible\n/// \"add column\" migrations should be caught earlier by the checks in\n/// [`spacetimedb_schema::auto_migrate`].\n#[derive(Error, Debug)]\n#[error(\"Cannot change the columns of table `{table_name}` with id {table_id} from `{old:?}` to `{new:?}`: {reason}\")]\npub struct AddColumnsError {\n    table_id: TableId,\n    table_name: TableName,\n    old: Vec<ColumnSchema>,\n    new: Vec<ColumnSchema>,\n    default_values: Vec<AlgebraicValue>,\n    reason: AddColumnsErrorReason,\n}\n\n#[derive(Error, Debug)]\npub enum AddColumnsErrorReason {\n    #[error(\"Default value type does not match column type for column {0}\")]\n    DefaultValueTypeMismatch(ColId),\n    #[error(\"Missing default value for column {0}\")]\n    DefaultValueMissing(ColId),\n    #[error(\"New column schema missing existing columns\")]\n    MissingExistingColumns,\n    #[error(transparent)]\n    ExistingColumnsTypeMismatched(#[from] ChangeColumnsErrorReason),\n}\n\n/// Computes the parts of a table definition, that are row type dependent, from the row type.\nfn table_row_type_dependents(row_type: ProductType) -> (RowTypeLayout, StaticLayoutInTable, VarLenVisitorProgram) {\n    let row_layout: RowTypeLayout = row_type.into();\n    let static_layout = StaticLayout::for_row_type(&row_layout).map(|sl| (sl, static_bsatn_validator(&row_layout)));\n    let visitor_prog = row_type_visitor(&row_layout);\n\n    (row_layout, static_layout, visitor_prog)\n}\n\n// Public API:\nimpl Table {\n    /// Creates a new empty table with the given `schema` and `squashed_offset`.\n    pub fn new(schema: Arc<TableSchema>, squashed_offset: SquashedOffset) -> Self {\n        let (row_layout, static_layout, visitor_prog) = table_row_type_dependents(schema.get_row_type().clone());\n\n        // By default, we start off with an empty pointer map,\n        // which is removed when the first unique index is added.\n        let pm = Some(PointerMap::default());\n        Self::new_raw(schema, row_layout, static_layout, visitor_prog, squashed_offset, pm)\n    }\n\n    /// Change the columns of `self` to those in `column_schemas`\n    /// and returns the old column schemas.\n    ///\n    /// Returns an error if the new list of column is incompatible with the old.\n    pub fn change_columns_to(\n        &mut self,\n        column_schemas: Vec<ColumnSchema>,\n    ) -> Result<Vec<ColumnSchema>, Box<ChangeColumnsError>> {\n        unsafe { self.change_columns_to_unchecked(column_schemas, Self::validate_row_type_layout) }\n    }\n\n    /// Validate that the old row type layout can be changed to the new.\n    // Intentionally fail fast rather than combining errors with [`spacetimedb_data_structures::error_stream`]\n    // because we've (at least theoretically) already passed through\n    // `spacetimedb_schema::auto_migrate::ensure_old_ty_upgradable_to_new` to get here,\n    // and that method has proper pretty error reporting with `ErrorStream`.\n    // The error here is for internal debugging.\n    fn validate_row_type_layout(\n        &self,\n        new_row_layout: &RowTypeLayout,\n        column_schemas: &[ColumnSchema],\n    ) -> Result<(), Box<ChangeColumnsError>> {\n        let schema = self.get_schema();\n        let row_layout = self.row_layout();\n\n        let make_err = |reason| {\n            Box::new(ChangeColumnsError {\n                table_id: schema.table_id,\n                table_name: schema.table_name.clone(),\n                old: schema.columns().to_vec(),\n                new: column_schemas.to_vec(),\n                reason,\n            })\n        };\n\n        // Require that a scheduler table doesn't change the `id` and `at` fields.\n        if let Some((schedule, pk)) = schema.schedule.as_ref().zip(schema.pk()) {\n            let at_col = schedule.at_column.idx();\n            if row_layout[at_col] != new_row_layout[at_col] {\n                return Err(make_err(ChangeColumnsErrorReason::ScheduleAtColumnChanged {\n                    index: at_col,\n                    old: row_layout[at_col].clone(),\n                    new: new_row_layout[at_col].clone(),\n                }));\n            }\n\n            let id_col = pk.col_pos.idx();\n            if row_layout[id_col] != new_row_layout[id_col] {\n                return Err(make_err(ChangeColumnsErrorReason::ScheduleIdColumnChanged {\n                    index: id_col,\n                    old: row_layout[id_col].clone(),\n                    new: new_row_layout[id_col].clone(),\n                }));\n            }\n        }\n\n        // The `row_layout` must also be compatible with the new.\n        if let Err(reason) = row_layout.ensure_compatible_with(new_row_layout) {\n            return Err(make_err((*reason).into()));\n        }\n\n        Ok(())\n    }\n\n    /// Validates that the proposed `new_columns` schema is compatible with the\n    /// existing table schema and that all newly added columns are initialized\n    /// with default values.\n    /// - `new_columns`: columns schema after adding new columns ordered by `ColId`s.\n    /// - `default_values`: default values for newly added columns ordered by `ColId`s.\n    pub fn validate_add_columns_schema(\n        &self,\n        new_columns: &[ColumnSchema],\n        default_values: &[AlgebraicValue],\n    ) -> Result<(), Box<AddColumnsError>> {\n        let schema = self.get_schema();\n        let existing_columns = &schema.columns;\n\n        let make_err = |reason| {\n            Box::new(AddColumnsError {\n                table_id: schema.table_id,\n                table_name: schema.table_name.clone(),\n                old: schema.columns().to_vec(),\n                new: new_columns.to_vec(),\n                default_values: default_values.to_vec(),\n                reason,\n            })\n        };\n\n        // Ensure we have at least as many (prefix) as the existing columns.\n        let Some(old_cols_in_new_schema) = &new_columns.get(..existing_columns.len()) else {\n            return Err(make_err(AddColumnsErrorReason::MissingExistingColumns));\n        };\n\n        // Ensure that the existing prefix is compatible with the new prefix.\n        let new_row_layout: RowTypeLayout = columns_to_row_type(old_cols_in_new_schema).into();\n        self.validate_row_type_layout(&new_row_layout, new_columns)\n            .map_err(|e| make_err(e.reason.into()))?;\n\n        // Validate that all new columns have default values and their types match.\n        for (idx, new_col) in new_columns.iter().skip(existing_columns.len()).enumerate() {\n            let default_value = default_values\n                .get(idx)\n                .ok_or_else(|| make_err(AddColumnsErrorReason::DefaultValueMissing(new_col.col_pos)))?;\n            if !new_col.col_type.type_check(default_value, Typespace::EMPTY) {\n                return Err(make_err(AddColumnsErrorReason::DefaultValueTypeMismatch(\n                    new_col.col_pos,\n                )));\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Change the columns of `self` to those in `column_schemas`\n    /// and returns the old column schemas.\n    ///\n    /// Returns an error if the new list of column is incompatible with the old.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure, using `validate`,\n    /// that `new_row_layout` is compatible with the rows existing in `self`.\n    pub unsafe fn change_columns_to_unchecked<E>(\n        &mut self,\n        column_schemas: Vec<ColumnSchema>,\n        validate: impl FnOnce(&Self, &RowTypeLayout, &[ColumnSchema]) -> Result<(), E>,\n    ) -> Result<Vec<ColumnSchema>, E> {\n        // Compute the new row type, layout, and related stuff.\n        let new_row_type: ProductType = columns_to_row_type(&column_schemas);\n        let (new_row_layout, new_static_layout, new_visitor_prog) = table_row_type_dependents(new_row_type.clone());\n\n        validate(self, &new_row_layout, &column_schemas)?;\n\n        // Set the new layout and friends.\n        self.inner.row_layout = new_row_layout;\n        self.inner.static_layout = new_static_layout;\n        self.inner.visitor_prog = new_visitor_prog;\n\n        // Update the schema.\n        let old_column_schemas = self.with_mut_schema(|s| {\n            s.row_type = new_row_type;\n            mem::replace(&mut s.columns, column_schemas)\n        });\n\n        // Recompute the index types.\n        self.compute_index_types();\n\n        Ok(old_column_schemas)\n    }\n\n    /// Change the row layout and schema to the one of `other`.\n    ///\n    /// # Safety\n    ///\n    /// This is safe when a `ChangeColumnsError` would not occur\n    /// when using `other.get_schema().columns.clone()`.\n    /// The actual safety requirements are more complex but the above\n    /// is a super-set of the actual requirements.\n    pub unsafe fn set_layout_and_schema_to(&mut self, other: &Table) {\n        self.inner.row_layout = other.inner.row_layout.clone();\n        self.inner.static_layout = other.inner.static_layout.clone();\n        self.inner.visitor_prog = other.inner.visitor_prog.clone();\n\n        self.use_schema_of(other);\n        self.compute_index_types();\n    }\n\n    /// Re-computes the index key types.\n    fn compute_index_types(&mut self) {\n        let schema = self.get_schema().clone();\n        let row_type = schema.get_row_type();\n        for index in self.indexes.values_mut() {\n            index.key_type = row_type\n                .project(&index.indexed_columns)\n                .expect(\"new row type should have as many columns as before\")\n        }\n    }\n\n    /// Returns whether this is a scheduler table.\n    pub fn is_scheduler(&self) -> bool {\n        self.is_scheduler\n    }\n\n    /// Check if the `row` conflicts with any unique index on `self`,\n    /// and if there is a conflict, return `Err`.\n    ///\n    /// `is_deleted` is a predicate which, for a given row pointer,\n    /// returns true if and only if that row should be ignored.\n    /// While checking unique constraints against the committed state,\n    /// `MutTxId::insert` will ignore rows which are listed in the delete table.\n    ///\n    /// # Safety\n    ///\n    /// `row.row_layout() == self.row_layout()` must hold.\n    pub unsafe fn check_unique_constraints<'a, I: Iterator<Item = (&'a IndexId, &'a TableIndex)>>(\n        &'a self,\n        row: RowRef<'_>,\n        adapt: impl FnOnce(btree_map::Iter<'a, IndexId, TableIndex>) -> I,\n        mut is_deleted: impl FnMut(RowPointer) -> bool,\n    ) -> Result<(), UniqueConstraintViolation> {\n        for (&index_id, index) in adapt(self.indexes.iter()).filter(|(_, index)| index.is_unique()) {\n            // SAFETY: Caller promised that `row´ has the same layout as `self`.\n            // Thus, as `index.indexed_columns` is in-bounds of `self`'s layout,\n            // it's also in-bounds of `row`'s layout.\n            let value = unsafe { row.project_unchecked(&index.indexed_columns) };\n            if index.seek_point(&value).next().is_some_and(|ptr| !is_deleted(ptr)) {\n                return Err(self.build_error_unique(index, index_id, value));\n            }\n        }\n        Ok(())\n    }\n\n    /// Insert a `row` into this table, storing its large var-len members in the `blob_store`.\n    ///\n    /// On success, returns the hash, if any, of the newly-inserted row,\n    /// and a `RowRef` referring to the row.s\n    /// The hash is only computed if this table has a [`PointerMap`],\n    /// i.e., does not have any unique indexes.\n    /// If the table has unique indexes,\n    /// the returned `Option<RowHash>` will be `None`.\n    ///\n    /// When a row equal to `row` already exists in `self`,\n    /// returns `InsertError::Duplicate(existing_row_pointer)`,\n    /// where `existing_row_pointer` is a `RowPointer` which identifies the existing row.\n    /// In this case, the duplicate is not inserted,\n    /// but internal data structures may be altered in ways that affect performance and fragmentation.\n    ///\n    /// TODO(error-handling): describe errors from `write_row_to_pages` and return meaningful errors.\n    pub fn insert<'a>(\n        &'a mut self,\n        pool: &PagePool,\n        blob_store: &'a mut dyn BlobStore,\n        row: &ProductValue,\n    ) -> Result<(Option<RowHash>, RowRef<'a>), InsertError> {\n        // Optimistically insert the `row` before checking any constraints\n        // under the assumption that errors (unique constraint & set semantic violations) are rare.\n        let (row_ref, blob_bytes) = self.insert_physically_pv(pool, blob_store, row)?;\n        let row_ptr = row_ref.pointer();\n\n        // Confirm the insertion, checking any constraints, removing the physical row on error.\n        // SAFETY: We just inserted `ptr`, so it must be present.\n        // Re. `CHECK_SAME_ROW = true`,\n        // where `insert` is called, we are not dealing with transactions,\n        // and we already know there cannot be a duplicate row error,\n        // but we check just in case it isn't.\n        let (hash, row_ptr) = unsafe { self.confirm_insertion::<true>(blob_store, row_ptr, blob_bytes) }?;\n        // SAFETY: Per post-condition of `confirm_insertion`, `row_ptr` refers to a valid row.\n        let row_ref = unsafe { self.get_row_ref_unchecked(blob_store, row_ptr) };\n        Ok((hash, row_ref))\n    }\n\n    /// Physically inserts `row` into the page\n    /// without inserting it logically into the pointer map.\n    ///\n    /// This is useful when we need to insert a row temporarily to get back a `RowPointer`.\n    /// A call to this method should be followed by a call to [`delete_internal_skip_pointer_map`].\n    pub fn insert_physically_pv<'a>(\n        &'a mut self,\n        pool: &PagePool,\n        blob_store: &'a mut dyn BlobStore,\n        row: &ProductValue,\n    ) -> Result<(RowRef<'a>, BlobNumBytes), Error> {\n        // SAFETY: `self.pages` is known to be specialized for `self.row_layout`,\n        // as `self.pages` was constructed from `self.row_layout` in `Table::new`.\n        let (ptr, blob_bytes) = unsafe {\n            write_row_to_pages(\n                pool,\n                &mut self.inner.pages,\n                &self.inner.visitor_prog,\n                blob_store,\n                &self.inner.row_layout,\n                row,\n                self.squashed_offset,\n            )\n        }?;\n        // SAFETY: We just inserted `ptr`, so it must be present.\n        let row_ref = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) };\n\n        Ok((row_ref, blob_bytes))\n    }\n\n    /// Physically insert a `row`, encoded in BSATN, into this table,\n    /// storing its large var-len members in the `blob_store`.\n    ///\n    /// On success, returns the hash of the newly-inserted row,\n    /// and a `RowRef` referring to the row.\n    ///\n    /// This does not check for set semantic or unique constraints.\n    ///\n    /// This is also useful when we need to insert a row temporarily to get back a `RowPointer`.\n    /// In this case, A call to this method should be followed by a call to [`delete_internal_skip_pointer_map`].\n    ///\n    /// When `row` is not valid BSATN at the table's row type,\n    /// an error is returned and there will be nothing for the caller to revert.\n    pub fn insert_physically_bsatn<'a>(\n        &'a mut self,\n        pool: &PagePool,\n        blob_store: &'a mut dyn BlobStore,\n        row: &[u8],\n    ) -> Result<(RowRef<'a>, BlobNumBytes), Error> {\n        // Got a static layout? => Use fast-path insertion.\n        let (ptr, blob_bytes) = if let Some((static_layout, static_validator)) = self.inner.static_layout.as_ref() {\n            // Before inserting, validate the row, ensuring type safety.\n            // SAFETY: The `static_validator` was derived from the same row layout as the static layout.\n            unsafe { validate_bsatn(static_validator, static_layout, row) }.map_err(Error::Decode)?;\n\n            let fixed_row_size = self.inner.row_layout.size();\n            let squashed_offset = self.squashed_offset;\n            let res = self\n                .inner\n                .pages\n                .with_page_to_insert_row(pool, fixed_row_size, 0, |page| {\n                    // SAFETY: We've used the right `row_size` and we trust that others have too.\n                    // `RowTypeLayout` also ensures that we satisfy the minimum row size.\n                    let fixed_offset = unsafe { page.alloc_fixed_len(fixed_row_size) }.map_err(Error::PageError)?;\n                    let (mut fixed, _) = page.split_fixed_var_mut();\n                    let fixed_buf = fixed.get_row_mut(fixed_offset, fixed_row_size);\n                    // SAFETY:\n                    // - We've validated that `row` is of sufficient length.\n                    // - The `fixed_buf` is exactly the right `fixed_row_size`.\n                    unsafe { static_layout.deserialize_row_into(fixed_buf, row) };\n                    Ok(fixed_offset)\n                })\n                .map_err(Error::PagesError)?;\n            match res {\n                (page, Ok(offset)) => (RowPointer::new(false, page, offset, squashed_offset), 0.into()),\n                (_, Err(e)) => return Err(e),\n            }\n        } else {\n            // SAFETY: `self.pages` is known to be specialized for `self.row_layout`,\n            // as `self.pages` was constructed from `self.row_layout` in `Table::new`.\n            unsafe {\n                write_row_to_pages_bsatn(\n                    pool,\n                    &mut self.inner.pages,\n                    &self.inner.visitor_prog,\n                    blob_store,\n                    &self.inner.row_layout,\n                    row,\n                    self.squashed_offset,\n                )\n            }?\n        };\n\n        // SAFETY: We just inserted `ptr`, so it must be present.\n        let row_ref = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) };\n\n        Ok((row_ref, blob_bytes))\n    }\n\n    /// Returns all the columns with sequences that need generation for this `row`.\n    ///\n    /// # Safety\n    ///\n    /// `self.is_row_present(row)` must hold.\n    pub unsafe fn sequence_triggers_for<'a>(\n        &'a self,\n        blob_store: &'a dyn BlobStore,\n        row: RowPointer,\n    ) -> (ColList, SeqIdList) {\n        let sequences = &*self.get_schema().sequences;\n        let row_ty = self.row_layout().product();\n\n        // SAFETY: Caller promised that `self.is_row_present(row)` holds.\n        let row_ref = unsafe { self.get_row_ref_unchecked(blob_store, row) };\n\n        sequences\n            .iter()\n            // Find all the sequences that are triggered by this row.\n            .filter(|seq| {\n                // SAFETY: `seq.col_pos` is in-bounds of `row_ty.elements`\n                // as `row_ty` was derived from the same schema as `seq` is part of.\n                let elem_ty = unsafe { &row_ty.elements.get_unchecked(seq.col_pos.idx()) };\n                // SAFETY:\n                // - `elem_ty` appears as a column in the row type.\n                // - `AlgebraicValue` is compatible with all types.\n                let val = unsafe { AlgebraicValue::unchecked_read_column(row_ref, elem_ty) };\n                val.is_numeric_zero()\n            })\n            .map(|seq| (seq.col_pos, seq.sequence_id))\n            .unzip()\n    }\n\n    /// Writes `seq_val` to the column at `col_id` in the row identified by `ptr`.\n    ///\n    /// Truncates the `seq_val` to fit the type of the column.\n    ///\n    /// # Safety\n    ///\n    /// - `self.is_row_present(row)` must hold.\n    /// - `col_id` must be a valid column, with a primitive integer type, of the row type.\n    pub unsafe fn write_gen_val_to_col(&mut self, col_id: ColId, ptr: RowPointer, seq_val: i128) {\n        let row_ty = self.inner.row_layout.product();\n        // SAFETY: Caller promised that `col_id` was a valid column.\n        let elem_ty = unsafe { row_ty.elements.get_unchecked(col_id.idx()) };\n        let AlgebraicTypeLayout::Primitive(col_typ) = elem_ty.ty else {\n            // SAFETY: Columns with sequences must be primitive types.\n            unsafe { unreachable_unchecked() }\n        };\n\n        let fixed_row_size = self.inner.row_layout.size();\n        let fixed_buf = self.inner.pages[ptr.page_index()].get_fixed_row_data_mut(ptr.page_offset(), fixed_row_size);\n\n        fn write<const N: usize>(dst: &mut [u8], offset: u16, bytes: [u8; N]) {\n            let offset = offset as usize;\n            dst[offset..offset + N].copy_from_slice(&bytes);\n        }\n\n        match col_typ {\n            PrimitiveType::I8 => write(fixed_buf, elem_ty.offset, (seq_val as i8).to_le_bytes()),\n            PrimitiveType::U8 => write(fixed_buf, elem_ty.offset, (seq_val as u8).to_le_bytes()),\n            PrimitiveType::I16 => write(fixed_buf, elem_ty.offset, (seq_val as i16).to_le_bytes()),\n            PrimitiveType::U16 => write(fixed_buf, elem_ty.offset, (seq_val as u16).to_le_bytes()),\n            PrimitiveType::I32 => write(fixed_buf, elem_ty.offset, (seq_val as i32).to_le_bytes()),\n            PrimitiveType::U32 => write(fixed_buf, elem_ty.offset, (seq_val as u32).to_le_bytes()),\n            PrimitiveType::I64 => write(fixed_buf, elem_ty.offset, (seq_val as i64).to_le_bytes()),\n            PrimitiveType::U64 => write(fixed_buf, elem_ty.offset, (seq_val as u64).to_le_bytes()),\n            PrimitiveType::I128 => write(fixed_buf, elem_ty.offset, seq_val.to_le_bytes()),\n            PrimitiveType::U128 => write(fixed_buf, elem_ty.offset, (seq_val as u128).to_le_bytes()),\n            PrimitiveType::I256 => write(fixed_buf, elem_ty.offset, (i256::from(seq_val)).to_le_bytes()),\n            PrimitiveType::U256 => write(fixed_buf, elem_ty.offset, (u256::from(seq_val as u128)).to_le_bytes()),\n            // SAFETY: Columns with sequences must be integer types.\n            PrimitiveType::Bool | PrimitiveType::F32 | PrimitiveType::F64 => unsafe { unreachable_unchecked() },\n        }\n    }\n\n    /// Performs all the checks necessary after having fully decided on a rows contents.\n    ///\n    /// This includes inserting the row into any applicable indices and/or the pointer map.\n    ///\n    /// On `Ok(_)`, statistics of the table are also updated,\n    /// and the `ptr` still points to a valid row, and otherwise not.\n    ///\n    /// If `CHECK_SAME_ROW` holds, an identical row will be treated as a set-semantic duplicate.\n    /// Otherwise, it will be treated as a unique constraint violation.\n    /// However, `false` should only be passed if it's known beforehand that there is no identical row.\n    ///\n    /// # Safety\n    ///\n    /// `self.is_row_present(row)` must hold.\n    pub unsafe fn confirm_insertion<'a, const CHECK_SAME_ROW: bool>(\n        &'a mut self,\n        blob_store: &'a mut dyn BlobStore,\n        ptr: RowPointer,\n        blob_bytes: BlobNumBytes,\n    ) -> Result<(Option<RowHash>, RowPointer), InsertError> {\n        // SAFETY: Caller promised that `self.is_row_present(ptr)` holds.\n        let hash = unsafe { self.insert_into_pointer_map(blob_store, ptr) }?;\n        // SAFETY: Caller promised that `self.is_row_present(ptr)` holds.\n        unsafe { self.insert_into_indices::<CHECK_SAME_ROW>(blob_store, ptr) }?;\n\n        self.update_statistics_added_row(blob_bytes);\n        Ok((hash, ptr))\n    }\n\n    /// Confirms a row update, after first updating indices and checking constraints.\n    ///\n    /// On `Ok(_)`:\n    /// - the statistics of the table are also updated,\n    /// - the `ptr` still points to a valid row.\n    ///\n    /// Otherwise, on `Err(_)`:\n    /// - `ptr` will not point to a valid row,\n    /// - the statistics won't be updated.\n    ///\n    /// # Safety\n    ///\n    /// `self.is_row_present(new_row)` and `self.is_row_present(old_row)`  must hold.\n    pub unsafe fn confirm_update<'a>(\n        &'a mut self,\n        blob_store: &'a mut dyn BlobStore,\n        new_ptr: RowPointer,\n        old_ptr: RowPointer,\n        blob_bytes_added: BlobNumBytes,\n    ) -> Result<RowPointer, InsertError> {\n        // (1) Remove old row from indices.\n        // SAFETY: Caller promised that `self.is_row_present(old_ptr)` holds.\n        unsafe { self.delete_from_indices(blob_store, old_ptr) };\n\n        // Insert new row into indices.\n        // SAFETY: Caller promised that `self.is_row_present(ptr)` holds.\n        let res = unsafe { self.insert_into_indices::<true>(blob_store, new_ptr) };\n        if let Err(e) = res {\n            // Undo (1).\n            unsafe { self.insert_into_indices::<true>(blob_store, old_ptr) }\n                .expect(\"re-inserting the old row into indices should always work\");\n            return Err(e);\n        }\n\n        // Remove the old row physically.\n        // SAFETY: The physical `old_ptr` still exists.\n        let blob_bytes_removed = unsafe { self.delete_internal_skip_pointer_map(blob_store, old_ptr) };\n        self.update_statistics_deleted_row(blob_bytes_removed);\n\n        // Update statistics.\n        self.update_statistics_added_row(blob_bytes_added);\n        Ok(new_ptr)\n    }\n\n    /// We've added a row, update the statistics to record this.\n    #[inline]\n    fn update_statistics_added_row(&mut self, blob_bytes: BlobNumBytes) {\n        self.row_count += 1;\n        self.blob_store_bytes += blob_bytes;\n    }\n\n    /// We've removed a row, update the statistics to record this.\n    #[inline]\n    fn update_statistics_deleted_row(&mut self, blob_bytes: BlobNumBytes) {\n        self.row_count -= 1;\n        self.blob_store_bytes -= blob_bytes;\n    }\n\n    /// Insert row identified by `new` into indices.\n    /// This also checks unique constraints.\n    /// Deletes the row if there were any violations.\n    ///\n    /// If `CHECK_SAME_ROW`, upon a unique constraint violation,\n    /// this will check if it's really a duplicate row.\n    /// Otherwise, the unique constraint violation is returned.\n    ///\n    /// SAFETY: `self.is_row_present(new)` must hold.\n    /// Post-condition: If this method returns `Ok(_)`, the row still exists.\n    unsafe fn insert_into_indices<'a, const CHECK_SAME_ROW: bool>(\n        &'a mut self,\n        blob_store: &'a mut dyn BlobStore,\n        new: RowPointer,\n    ) -> Result<(), InsertError> {\n        self.indexes\n            .iter_mut()\n            .try_for_each(|(index_id, index)| {\n                // SAFETY: We just inserted `ptr`, so it must be present.\n                let new = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, new) };\n                // SAFETY: any index in this table was constructed with the same row type as this table.\n                let violation = unsafe { index.check_and_insert(new) };\n                violation.map_err(|old| (*index_id, old, new))\n            })\n            .map_err(|(index_id, old, new)| {\n                // Found unique constraint violation!\n                if CHECK_SAME_ROW\n                    // If the index was added in this tx,\n                    // `old` could be a committed row,\n                    // which we want to avoid here.\n                    // TODO(centril): not 100% correct, could still be a duplicate,\n                    // but this is rather pathological and should be fixed when we restructure.\n                    && old.squashed_offset().is_tx_state()\n                    // SAFETY:\n                    // - The row layouts are the same as it's the same table.\n                    // - We know `old` exists in `self` as we just found it in an index.\n                    // - Caller promised that `new` is valid for `self`.\n                    && unsafe { Self::eq_row_in_page(self, old, self, new.pointer()) }\n                {\n                    return (index_id, DuplicateError(old).into());\n                }\n\n                let index = self.indexes.get(&index_id).unwrap();\n                let value = new.project(&index.indexed_columns).unwrap();\n                let error = self.build_error_unique(index, index_id, value).into();\n                (index_id, error)\n            })\n            .map_err(|(index_id, error)| {\n                // Delete row from indices.\n                // Do this before the actual deletion, as `index.delete` needs a `RowRef`\n                // so it can extract the appropriate value.\n                // SAFETY: We just inserted `new`, so it must be present.\n                unsafe { self.delete_from_indices_until(blob_store, new, index_id) };\n\n                // Cleanup, undo the row insertion of `new`s.\n                // SAFETY: We just inserted `new`, so it must be present.\n                unsafe { self.delete_internal(blob_store, new) };\n\n                error\n            })\n    }\n\n    /// Finds the [`RowPointer`] to the row in `target_table` equal, if any,\n    /// to the row `needle_ptr` in `needle_table`,\n    /// by any unique index in `target_table`.\n    ///\n    /// # Safety\n    ///\n    /// - `target_table` and `needle_table` must have the same `row_layout`.\n    /// - `needle_table.is_row_present(needle_ptr)` must hold.\n    unsafe fn find_same_row_via_unique_index(\n        target_table: &Table,\n        needle_table: &Table,\n        needle_bs: &dyn BlobStore,\n        needle_ptr: RowPointer,\n    ) -> Option<RowPointer> {\n        // Use some index (the one with the lowest `IndexId` currently).\n        // TODO(centril): this isn't what we actually want.\n        // Rather, we'd prefer the index with the simplest type,\n        // but this is left as future work as we don't have to optimize this method now.\n        let target_index = target_table\n            .indexes\n            .values()\n            .find(|idx| idx.is_unique())\n            .expect(\"there should be at least one unique index\");\n        // Project the needle row to the columns of the index, and then seek.\n        // As this is a unique index, there are 0-1 rows for this key.\n        let needle_row = unsafe { needle_table.get_row_ref_unchecked(needle_bs, needle_ptr) };\n        let key = needle_row\n            .project(&target_index.indexed_columns)\n            .expect(\"needle row should be valid\");\n        target_index.seek_point(&key).next().filter(|&target_ptr| {\n            // SAFETY:\n            // - Caller promised that the row layouts were the same.\n            // - We know `target_ptr` exists, as it was in `target_index`, belonging to `target_table`.\n            // - Caller promised that `needle_ptr` is valid for `needle_table`.\n            unsafe { Self::eq_row_in_page(target_table, target_ptr, needle_table, needle_ptr) }\n        })\n    }\n\n    /// Insert the row identified by `ptr` into the table's [`PointerMap`],\n    /// if the table has one.\n    ///\n    /// This checks for set semantic violations.\n    /// If a set semantic conflict (i.e. duplicate row) is detected by the pointer map,\n    /// the row will be deleted and an error returned.\n    /// If the pointer map confirms that the row was unique, returns the `RowHash` of that row.\n    ///\n    /// If this table has no `PointerMap`, returns `Ok(None)`.\n    /// In that case, the row's uniqueness will be verified by [`Self::insert_into_indices`],\n    /// as this table has at least one unique index.\n    ///\n    /// SAFETY: `self.is_row_present(row)` must hold.\n    /// Post-condition: If this method returns `Ok(_)`, the row still exists.\n    unsafe fn insert_into_pointer_map<'a>(\n        &'a mut self,\n        blob_store: &'a mut dyn BlobStore,\n        ptr: RowPointer,\n    ) -> Result<Option<RowHash>, DuplicateError> {\n        if self.pointer_map.is_none() {\n            // No pointer map? Set semantic constraint is checked by a unique index instead.\n            return Ok(None);\n        };\n\n        // SAFETY:\n        // - `self` trivially has the same `row_layout` as `self`.\n        // - Caller promised that `self.is_row_present(row)` holds.\n        let (hash, existing_row) = unsafe { Self::find_same_row_via_pointer_map(self, self, blob_store, ptr, None) };\n\n        if let Some(existing_row) = existing_row {\n            // If an equal row was already present,\n            // roll back our optimistic insert to avoid violating set semantics.\n\n            // SAFETY: Caller promised that `ptr` is a valid row in `self`.\n            unsafe {\n                self.inner\n                    .pages\n                    .delete_row(&self.inner.visitor_prog, self.row_size(), ptr, blob_store)\n            };\n            return Err(DuplicateError(existing_row));\n        }\n\n        // If the optimistic insertion was correct,\n        // i.e. this is not a set-semantic duplicate,\n        // add it to the `pointer_map`.\n        self.pointer_map\n            .as_mut()\n            .expect(\"pointer map should exist, as it did previously\")\n            .insert(hash, ptr);\n\n        Ok(Some(hash))\n    }\n\n    /// Returns the list of pointers to rows which hash to `row_hash`.\n    ///\n    /// If `self` does not have a [`PointerMap`], always returns the empty slice.\n    fn pointers_for(&self, row_hash: RowHash) -> &[RowPointer] {\n        self.pointer_map.as_ref().map_or(&[], |pm| pm.pointers_for(row_hash))\n    }\n\n    /// Using the [`PointerMap`],\n    /// searches `target_table` for a row equal to `needle_table[needle_ptr]`.\n    ///\n    /// Rows are compared for equality by [`eq_row_in_page`].\n    ///\n    /// Lazily computes the row hash if needed and returns it, or uses the one provided, if any.\n    ///\n    /// Used for detecting set-semantic duplicates when inserting\n    /// into tables without any unique constraints.\n    ///\n    /// Does nothing and always returns `None` if `target_table` does not have a `PointerMap`,\n    /// in which case the caller should instead use [`Self::find_same_row_via_unique_index`].\n    ///\n    /// Note that we don't need the blob store to compute equality,\n    /// as content-addressing means it's sufficient to compare the hashes of large blobs.\n    /// (If we see a collision in `BlobHash` we have bigger problems.)\n    ///\n    /// # Safety\n    ///\n    /// - `target_table` and `needle_table` must have the same `row_layout`.\n    /// - `needle_table.is_row_present(needle_ptr)`.\n    pub unsafe fn find_same_row_via_pointer_map(\n        target_table: &Table,\n        needle_table: &Table,\n        needle_bs: &dyn BlobStore,\n        needle_ptr: RowPointer,\n        row_hash: Option<RowHash>,\n    ) -> (RowHash, Option<RowPointer>) {\n        let row_hash = row_hash.unwrap_or_else(|| {\n            // SAFETY: Caller promised that `needle_table.is_row_present(needle_ptr)`.\n            let row_ref = unsafe { needle_table.get_row_ref_unchecked(needle_bs, needle_ptr) };\n            row_ref.row_hash()\n        });\n\n        // Scan all the frow pointers with `row_hash` in the `committed_table`.\n        let row_ptr = target_table.pointers_for(row_hash).iter().copied().find(|&target_ptr| {\n            // SAFETY:\n            // - Caller promised that the row layouts were the same.\n            // - We know `target_ptr` exists, as it was found in a pointer map.\n            // - Caller promised that `needle_ptr` is valid for `needle_table`.\n            unsafe { Self::eq_row_in_page(target_table, target_ptr, needle_table, needle_ptr) }\n        });\n\n        (row_hash, row_ptr)\n    }\n\n    /// Returns whether the row `target_ptr` in `target_table`\n    /// is exactly equal to the row `needle_ptr` in `needle_ptr`.\n    ///\n    /// # Safety\n    ///\n    /// - `target_table` and `needle_table` must have the same `row_layout`.\n    /// - `target_table.is_row_present(target_ptr)`.\n    /// - `needle_table.is_row_present(needle_ptr)`.\n    pub unsafe fn eq_row_in_page(\n        target_table: &Table,\n        target_ptr: RowPointer,\n        needle_table: &Table,\n        needle_ptr: RowPointer,\n    ) -> bool {\n        let (target_page, target_offset) = target_table.inner.page_and_offset(target_ptr);\n        let (needle_page, needle_offset) = needle_table.inner.page_and_offset(needle_ptr);\n\n        // SAFETY:\n        // - Caller promised that `target_ptr` is valid, so `target_page` and `target_offset` are both valid.\n        // - Caller promised that `needle_ptr` is valid, so `needle_page` and `needle_offset` are both valid.\n        // - Caller promised that the layouts of `target_table` and `needle_table` are the same,\n        //   so `target_table` applies to both.\n        //   Moreover `(x: Table).inner.static_layout` is always derived from `x.row_layout`.\n        unsafe {\n            eq_row_in_page(\n                target_page,\n                needle_page,\n                target_offset,\n                needle_offset,\n                &target_table.inner.row_layout,\n                target_table.static_layout(),\n            )\n        }\n    }\n\n    /// Searches `target_table` for a row equal to `needle_table[needle_ptr]`,\n    /// and returns the [`RowPointer`] to that row in `target_table`, if it exists.\n    ///\n    /// Searches using the [`PointerMap`] or a unique index, as appropriate for the table.\n    ///\n    /// Lazily computes the row hash if needed and returns it, or uses the one provided, if any.\n    ///\n    /// # Safety\n    ///\n    /// - `target_table` and `needle_table` must have the same `row_layout`.\n    /// - `needle_table.is_row_present(needle_ptr)` must hold.\n    pub unsafe fn find_same_row(\n        target_table: &Table,\n        needle_table: &Table,\n        needle_bs: &dyn BlobStore,\n        needle_ptr: RowPointer,\n        row_hash: Option<RowHash>,\n    ) -> (Option<RowHash>, Option<RowPointer>) {\n        if target_table.pointer_map.is_some() {\n            // SAFETY: Caller promised that `target_table` and `needle_table` have the same `row_layout`.\n            // SAFETY: Caller promised that `needle_table.is_row_present(needle_ptr)`.\n            let (row_hash, row_ptr) = unsafe {\n                Self::find_same_row_via_pointer_map(target_table, needle_table, needle_bs, needle_ptr, row_hash)\n            };\n            (Some(row_hash), row_ptr)\n        } else {\n            (\n                row_hash,\n                // SAFETY: Caller promised that `target_table` and `needle_table` have the same `row_layout`.\n                // SAFETY: Caller promised that `needle_table.is_row_present(needle_ptr)`.\n                unsafe { Self::find_same_row_via_unique_index(target_table, needle_table, needle_bs, needle_ptr) },\n            )\n        }\n    }\n\n    /// Returns a [`RowRef`] for `ptr` or `None` if the row isn't present.\n    pub fn get_row_ref<'a>(&'a self, blob_store: &'a dyn BlobStore, ptr: RowPointer) -> Option<RowRef<'a>> {\n        self.is_row_present(ptr)\n            // SAFETY: We only call `get_row_ref_unchecked` when `is_row_present` holds.\n            .then(|| unsafe { self.get_row_ref_unchecked(blob_store, ptr) })\n    }\n\n    /// Assumes `ptr` is a present row in `self` and returns a [`RowRef`] to it.\n    ///\n    /// # Safety\n    ///\n    /// The requirement is that `self.is_row_present(ptr)` must hold.\n    /// That is, `ptr` must refer to a row within `self`\n    /// which was previously inserted and has not been deleted since.\n    ///\n    /// This means:\n    /// - The `PageIndex` of `ptr` must be in-bounds for `self.pages`.\n    /// - The `PageOffset` of `ptr` must be properly aligned for the row type of `self`,\n    ///   and must refer to a valid, live row in that page.\n    /// - The `SquashedOffset` of `ptr` must match `self.squashed_offset`.\n    ///\n    /// Showing that `ptr` was the result of a call to [`Table::insert(table, ..)`]\n    /// and has not been passed to [`Table::delete(table, ..)`]\n    /// is sufficient to demonstrate all of these properties.\n    pub unsafe fn get_row_ref_unchecked<'a>(&'a self, blob_store: &'a dyn BlobStore, ptr: RowPointer) -> RowRef<'a> {\n        // SAFETY: Caller promised that ^-- holds.\n        unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) }\n    }\n\n    /// Deletes a row in the page manager\n    /// without deleting it logically in the pointer map.\n    ///\n    /// # Safety\n    ///\n    /// `ptr` must point to a valid, live row in this table.\n    pub unsafe fn delete_internal_skip_pointer_map(\n        &mut self,\n        blob_store: &mut dyn BlobStore,\n        ptr: RowPointer,\n    ) -> BlobNumBytes {\n        debug_assert!(self.is_row_present(ptr));\n        // Delete the physical row.\n        //\n        // SAFETY:\n        // - `ptr` points to a valid row in this table, per our invariants.\n        // - `self.row_size` known to be consistent with `self.pages`,\n        //    as the two are tied together in `Table::new`.\n        unsafe {\n            self.inner\n                .pages\n                .delete_row(&self.inner.visitor_prog, self.row_size(), ptr, blob_store)\n        }\n    }\n\n    /// Deletes the row identified by `ptr` from the table.\n    ///\n    /// Returns the number of blob bytes added. This method does not update statistics by itself.\n    ///\n    /// NOTE: This method skips updating indexes.\n    /// Use `delete_unchecked` or `delete` to delete a row with index updating.\n    ///\n    /// SAFETY: `self.is_row_present(row)` must hold.\n    unsafe fn delete_internal(&mut self, blob_store: &mut dyn BlobStore, ptr: RowPointer) -> BlobNumBytes {\n        // Remove the set semantic association.\n        if let Some(pointer_map) = &mut self.pointer_map {\n            // SAFETY: `self.is_row_present(row)` holds.\n            let row = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) };\n\n            let _remove_result = pointer_map.remove(row.row_hash(), ptr);\n            debug_assert!(_remove_result);\n        }\n\n        // Delete the physical row.\n        // SAFETY: `ptr` points to a valid row in this table as `self.is_row_present(row)` holds.\n        unsafe { self.delete_internal_skip_pointer_map(blob_store, ptr) }\n    }\n\n    /// Deletes the row identified by `ptr` from the table.\n    ///\n    /// This method does update statistics.\n    ///\n    /// SAFETY: `self.is_row_present(row)` must hold.\n    unsafe fn delete_unchecked(&mut self, blob_store: &mut dyn BlobStore, ptr: RowPointer) {\n        // Delete row from indices.\n        // Do this before the actual deletion, as `index.delete` needs a `RowRef`\n        // so it can extract the appropriate value.\n        // SAFETY: Caller promised that `self.is_row_present(row)` holds.\n        unsafe { self.delete_from_indices(blob_store, ptr) };\n\n        // SAFETY: Caller promised that `self.is_row_present(row)` holds.\n        let blob_bytes_deleted = unsafe { self.delete_internal(blob_store, ptr) };\n\n        self.update_statistics_deleted_row(blob_bytes_deleted);\n    }\n\n    /// Delete `row_ref` from all the indices of this table until `index_id` is reached.\n    /// The range is exclusive of `index_id`.\n    ///\n    /// SAFETY: `self.is_row_present(row)` must hold.\n    unsafe fn delete_from_indices_until(&mut self, blob_store: &dyn BlobStore, ptr: RowPointer, index_id: IndexId) {\n        // SAFETY: Caller promised that `self.is_row_present(row)` holds.\n        let row_ref = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) };\n\n        for (_, index) in self.indexes.range_mut(..index_id) {\n            index.delete(row_ref).unwrap();\n        }\n    }\n\n    /// Delete `row_ref` from all the indices of this table.\n    ///\n    /// SAFETY: `self.is_row_present(row)` must hold.\n    unsafe fn delete_from_indices(&mut self, blob_store: &dyn BlobStore, ptr: RowPointer) {\n        // SAFETY: Caller promised that `self.is_row_present(row)` holds.\n        let row_ref = unsafe { self.inner.get_row_ref_unchecked(blob_store, self.squashed_offset, ptr) };\n\n        for index in self.indexes.values_mut() {\n            index.delete(row_ref).unwrap();\n        }\n    }\n\n    /// Deletes the row identified by `ptr` from the table.\n    ///\n    /// The function `before` is run on the to-be-deleted row,\n    /// if it is present, before deleting.\n    /// This enables callers to extract the deleted row.\n    /// E.g. applying deletes when squashing/merging a transaction into the committed state\n    /// passes `|row| row.to_product_value()` as `before`\n    /// so that the resulting `ProductValue`s can be passed to the subscription evaluator.\n    pub fn delete<'a, R>(\n        &'a mut self,\n        blob_store: &'a mut dyn BlobStore,\n        ptr: RowPointer,\n        before: impl for<'b> FnOnce(RowRef<'b>) -> R,\n    ) -> Option<R> {\n        if !self.is_row_present(ptr) {\n            return None;\n        };\n\n        // SAFETY: We only call `get_row_ref_unchecked` when `is_row_present` holds.\n        let row_ref = unsafe { self.get_row_ref_unchecked(blob_store, ptr) };\n\n        let ret = before(row_ref);\n\n        // SAFETY: We've checked above that `self.is_row_present(ptr)`.\n        unsafe { self.delete_unchecked(blob_store, ptr) };\n\n        Some(ret)\n    }\n\n    /// If a row exists in `self` which matches `row`\n    /// by [`Table::find_same_row`],\n    /// delete that row.\n    ///\n    /// If a matching row was found, returns the pointer to that row.\n    /// The returned pointer is now invalid, as the row to which it referred has been deleted.\n    ///\n    /// This operation works by temporarily inserting the `row` into `self`,\n    /// checking `find_same_row` on the newly-inserted row,\n    /// deleting the matching row if it exists,\n    /// then deleting the temporary insertion.\n    pub fn delete_equal_row(\n        &mut self,\n        pool: &PagePool,\n        blob_store: &mut dyn BlobStore,\n        row: &ProductValue,\n    ) -> Result<Option<RowPointer>, Error> {\n        // Insert `row` temporarily so `temp_ptr` and `hash` can be used to find the row.\n        // This must avoid consulting and inserting to the pointer map,\n        // as the row is already present, set-semantically.\n        let (temp_row, _) = self.insert_physically_pv(pool, blob_store, row)?;\n        let temp_ptr = temp_row.pointer();\n\n        // Find the row equal to the passed-in `row`.\n        // This uses one of two approaches.\n        // Either there is a pointer map, so we use that,\n        // or, here is at least one unique index, so we use one of them.\n        //\n        // SAFETY:\n        // - `self` trivially has the same `row_layout` as `self`.\n        // - We just inserted `temp_ptr`, so it's valid.\n        let (_, existing_row_ptr) = unsafe { Self::find_same_row(self, self, blob_store, temp_ptr, None) };\n\n        // If an equal row was present, delete it.\n        if let Some(existing_row_ptr) = existing_row_ptr {\n            // SAFETY: `find_same_row` ensures that the pointer is valid.\n            unsafe { self.delete_unchecked(blob_store, existing_row_ptr) };\n        }\n\n        // Remove the temporary row we inserted in the beginning.\n        // Avoid the pointer map, since we don't want to delete it twice.\n        // SAFETY: `ptr` is valid as we just inserted it.\n        unsafe {\n            self.delete_internal_skip_pointer_map(blob_store, temp_ptr);\n        }\n\n        Ok(existing_row_ptr)\n    }\n\n    /// Clears this table, removing all present rows from it.\n    pub fn clear(&mut self, blob_store: &mut dyn BlobStore) -> usize {\n        let ptrs = self.scan_all_row_ptrs();\n        let len = ptrs.len();\n        for ptr in ptrs {\n            // SAFETY: `ptr` came rom `self.scan_rows(...)`, so it's present.\n            unsafe { self.delete_unchecked(blob_store, ptr) };\n        }\n        len\n    }\n\n    /// Returns the row type for rows in this table.\n    pub fn get_row_type(&self) -> &ProductType {\n        self.get_schema().get_row_type()\n    }\n\n    /// Returns the schema for this table.\n    pub fn get_schema(&self) -> &Arc<TableSchema> {\n        &self.schema\n    }\n\n    /// Runs a mutation on the [`TableSchema`] of this table.\n    ///\n    /// This uses a clone-on-write mechanism.\n    /// If none but `self` refers to the schema, then the mutation will be in-place.\n    /// Otherwise, the schema must be cloned, mutated,\n    /// and then the cloned version is written back to the table.\n    pub fn with_mut_schema<R>(&mut self, with: impl FnOnce(&mut TableSchema) -> R) -> R {\n        with(Arc::make_mut(&mut self.schema))\n    }\n\n    /// Runs a mutation on the [`TableSchema`] of this table\n    /// and then sets the schema of `other` to that of `self`.\n    pub fn with_mut_schema_and_clone<R>(&mut self, other: &mut Table, with: impl FnOnce(&mut TableSchema) -> R) -> R {\n        let ret = self.with_mut_schema(with);\n        other.use_schema_of(self);\n        ret\n    }\n\n    /// Makes `self` use the schema of `other`.\n    ///\n    /// Here, `self` will typically be a commit table and `other` a tx table, or the reverse.\n    fn use_schema_of(&mut self, other: &Self) {\n        self.schema = other.get_schema().clone();\n    }\n\n    /// Returns a new [`TableIndex`] for `table`.\n    pub fn new_index(&self, algo: &IndexAlgorithm, is_unique: bool) -> Result<TableIndex, InvalidFieldError> {\n        TableIndex::new(\n            self.get_schema().get_row_type(),\n            algo.columns().to_owned(),\n            IndexKind::from_algo(algo),\n            is_unique,\n        )\n    }\n\n    /// Inserts a new `index` into the table.\n    ///\n    /// The index will be populated using the rows of the table.\n    ///\n    /// # Panics\n    ///\n    /// Panics if any row would violate `index`'s unique constraint, if it has one.\n    ///\n    /// # Safety\n    ///\n    /// Caller must promise that `index` was constructed with the same row type/layout as this table.\n    pub unsafe fn insert_index(&mut self, blob_store: &dyn BlobStore, index_id: IndexId, mut index: TableIndex) {\n        let rows = self.scan_rows(blob_store);\n        // SAFETY: Caller promised that table's row type/layout\n        // matches that which `index` was constructed with.\n        // It follows that this applies to any `rows`, as required.\n        let violation = unsafe { index.build_from_rows(rows) };\n        violation.unwrap_or_else(|ptr| {\n            let index_schema = &self.schema.indexes.iter().find(|index_schema| index_schema.index_id == index_id).expect(\"Index should exist\");\n            let indexed_column = if let IndexAlgorithm::BTree(BTreeAlgorithm { columns }) = &index_schema.index_algorithm {\n                Some(columns)\n            } else { None };\n            let indexed_column = indexed_column.and_then(|columns| columns.as_singleton());\n            let indexed_column_info = indexed_column.and_then(|column| self.schema.get_column(column.idx()));\n            // SAFETY: `ptr` just came out of `self.scan_rows`, so it is present.\n            let row = unsafe { self.get_row_ref_unchecked(blob_store, ptr) }.to_product_value();\n            panic!(\n                \"Adding index `{}` {:?} to table `{}` {:?} on column `{}` {:?} should cause no unique constraint violations.\n\nFound violation at pointer {ptr:?} to row {:?}.\",\n                index_schema.index_name,\n                index_schema.index_id,\n                self.schema.table_name,\n                self.schema.table_id,\n                indexed_column_info.map(|column| &column.col_name[..]).unwrap_or(\"unknown column\"),\n                indexed_column,\n                row,\n            );\n        });\n        // SAFETY: Forward caller requirement.\n        unsafe { self.add_index(index_id, index) };\n    }\n\n    /// Adds an index to the table without populating.\n    ///\n    /// # Safety\n    ///\n    /// Caller must promise that `index` was constructed with the same row type/layout as this table.\n    pub unsafe fn add_index(&mut self, index_id: IndexId, index: TableIndex) -> Option<PointerMap> {\n        let is_unique = index.is_unique();\n        self.indexes.insert(index_id, index);\n\n        // Remove the pointer map, if any.\n        if is_unique {\n            self.pointer_map.take()\n        } else {\n            None\n        }\n    }\n\n    /// Removes an index from the table.\n    ///\n    /// Returns whether an index existed with `index_id`.\n    pub fn delete_index(\n        &mut self,\n        blob_store: &dyn BlobStore,\n        index_id: IndexId,\n        pointer_map: Option<PointerMap>,\n    ) -> Option<TableIndex> {\n        let index = self.indexes.remove(&index_id)?;\n\n        // If we removed the last unique index, add a pointer map.\n        if index.is_unique() && !self.indexes.values().any(|idx| idx.is_unique()) {\n            self.pointer_map = Some(pointer_map.unwrap_or_else(|| self.rebuild_pointer_map(blob_store)));\n        }\n\n        Some(index)\n    }\n\n    /// Returns an iterator over all the rows of `self`, yielded as [`RowRef`]s.\n    pub fn scan_rows<'a>(&'a self, blob_store: &'a dyn BlobStore) -> TableScanIter<'a> {\n        TableScanIter {\n            current_page: None, // Will be filled by the iterator.\n            current_page_idx: PageIndex(0),\n            table: self,\n            blob_store,\n        }\n    }\n\n    /// Returns a list of all present row pointers.\n    pub fn scan_all_row_ptrs(&self) -> Vec<RowPointer> {\n        let mut ptrs = Vec::with_capacity(self.row_count as usize);\n        ptrs.extend(self.scan_rows(&NullBlobStore).map(|row| row.pointer()));\n        ptrs\n    }\n\n    /// Returns this table combined with the index for [`IndexId`], if any.\n    pub fn get_index_by_id_with_table<'a>(\n        &'a self,\n        blob_store: &'a dyn BlobStore,\n        index_id: IndexId,\n    ) -> Option<TableAndIndex<'a>> {\n        Some(TableAndIndex {\n            table: self,\n            blob_store,\n            index: self.get_index_by_id(index_id)?,\n        })\n    }\n\n    /// Returns the [`TableIndex`] for this [`IndexId`].\n    pub fn get_index_by_id(&self, index_id: IndexId) -> Option<&TableIndex> {\n        self.indexes.get(&index_id)\n    }\n\n    /// Returns this table combined with the first index with `cols`, if any.\n    pub fn get_index_by_cols_with_table<'a>(\n        &'a self,\n        blob_store: &'a dyn BlobStore,\n        cols: &ColList,\n    ) -> Option<TableAndIndex<'a>> {\n        let (_, index) = self.get_index_by_cols(cols)?;\n        Some(TableAndIndex {\n            table: self,\n            blob_store,\n            index,\n        })\n    }\n\n    /// Returns the first [`TableIndex`] with the given [`ColList`].\n    pub fn get_index_by_cols(&self, cols: &ColList) -> Option<(IndexId, &TableIndex)> {\n        self.indexes\n            .iter()\n            .find(|(_, index)| &index.indexed_columns == cols)\n            .map(|(id, idx)| (*id, idx))\n    }\n\n    /// Clones the structure of this table into a new one with\n    /// the same schema, visitor program, and indices.\n    /// The new table will be completely empty\n    /// and will use the given `squashed_offset` instead of that of `self`.\n    pub fn clone_structure(&self, squashed_offset: SquashedOffset) -> Self {\n        // Clone a bunch of static data.\n        // NOTE(centril): It's important that these be cheap to clone.\n        // This is why they are all `Arc`ed or have some sort of small-vec optimization.\n        let schema = self.schema.clone();\n        let layout = self.row_layout().clone();\n        let sbl = self.inner.static_layout.clone();\n        let visitor = self.inner.visitor_prog.clone();\n\n        // If we had a pointer map, we'll have one in the cloned one as well, but empty.\n        let pm = self.pointer_map.as_ref().map(|_| PointerMap::default());\n\n        // Make the new table.\n        let mut new = Table::new_raw(schema, layout, sbl, visitor, squashed_offset, pm);\n\n        // Clone the index structure. The table is empty, so no need to `build_from_rows`.\n        for (&index_id, index) in self.indexes.iter() {\n            new.indexes.insert(index_id, index.clone_structure());\n        }\n        new\n    }\n\n    /// Returns the number of bytes occupied by the pages and the blob store.\n    /// Note that result can be more than the actual physical size occupied by the table\n    /// because the blob store implementation can do internal optimizations.\n    /// For more details, refer to the documentation of `self.blob_store_bytes`.\n    pub fn bytes_occupied_overestimate(&self) -> usize {\n        (self.num_pages() * PAGE_DATA_SIZE) + (self.blob_store_bytes.0)\n    }\n\n    /// Reset the internal storage of `self` to be `pages`.\n    ///\n    /// This recomputes the pointer map based on the `pages`,\n    /// but does not recompute indexes.\n    ///\n    /// Used when restoring from a snapshot.\n    ///\n    /// # Safety\n    ///\n    /// The schema of rows stored in the `pages` must exactly match `self.schema` and `self.inner.row_layout`.\n    pub unsafe fn set_pages(&mut self, pages: Vec<Box<Page>>, blob_store: &dyn BlobStore) {\n        self.inner.pages.set_contents(pages, self.inner.row_layout.size());\n\n        // Recompute table metadata based on the new pages.\n        // Compute the row count first, in case later computations want to use it as a capacity to pre-allocate.\n        self.compute_row_count(blob_store);\n        self.pointer_map = Some(self.rebuild_pointer_map(blob_store));\n    }\n\n    /// Consumes the table, returning some constituents needed for merge.\n    pub fn consume_for_merge(\n        self,\n    ) -> (\n        Arc<TableSchema>,\n        impl Iterator<Item = (IndexId, TableIndex)>,\n        impl Iterator<Item = Box<Page>>,\n    ) {\n        (self.schema, self.indexes.into_iter(), self.inner.pages.into_page_iter())\n    }\n\n    /// Returns the number of rows resident in this table.\n    ///\n    /// This method runs in constant time.\n    pub fn num_rows(&self) -> u64 {\n        self.row_count\n    }\n\n    #[cfg(test)]\n    fn reconstruct_num_rows(&self) -> u64 {\n        self.pages().iter().map(|page| page.reconstruct_num_rows() as u64).sum()\n    }\n\n    /// Returns the number of bytes used by rows resident in this table.\n    ///\n    /// This includes data bytes, padding bytes and some overhead bytes,\n    /// as described in the docs for [`Page::bytes_used_by_rows`],\n    /// but *does not* include:\n    ///\n    /// - Unallocated space within pages.\n    /// - Per-page overhead (e.g. page headers).\n    /// - Table overhead (e.g. the [`RowTypeLayout`], [`PointerMap`], [`Schema`] &c).\n    /// - Indexes.\n    /// - Large blobs in the [`BlobStore`].\n    ///\n    /// Of these, the caller should inspect the blob store in order to account for memory usage by large blobs,\n    /// and call [`Self::bytes_used_by_index_keys`] to account for indexes,\n    /// but we intend to eat all the other overheads when billing.\n    ///\n    // TODO(perf, centril): consider storing the total number of granules in the table instead\n    // so that this runs in constant time rather than O(|Pages|).\n    pub fn bytes_used_by_rows(&self) -> u64 {\n        self.pages()\n            .iter()\n            .map(|page| page.bytes_used_by_rows(self.inner.row_layout.size()) as u64)\n            .sum()\n    }\n\n    #[cfg(test)]\n    fn reconstruct_bytes_used_by_rows(&self) -> u64 {\n        self.pages()\n            .iter()\n            .map(|page| unsafe {\n                // Safety: `page` is in `self`, and was constructed using `self.innser.row_layout` and `self.inner.visitor_prog`,\n                // so the three are mutually consistent.\n                page.reconstruct_bytes_used_by_rows(self.inner.row_layout.size(), &self.inner.visitor_prog)\n            } as u64)\n            .sum()\n    }\n\n    /// Returns the number of indices in this table.\n    pub fn num_indices(&self) -> usize {\n        self.indexes.len()\n    }\n\n    /// Returns the number of rows (or [`RowPointer`]s, more accurately)\n    /// stored in indexes by this table.\n    ///\n    /// This method runs in constant time.\n    pub fn num_rows_in_indexes(&self) -> u64 {\n        // Assume that each index contains all rows in the table.\n        self.num_rows() * self.indexes.len() as u64\n    }\n\n    /// Returns the number of bytes used by keys stored in indexes by this table.\n    ///\n    /// This method scales in runtime with the number of indexes in the table,\n    /// but not with the number of pages or rows.\n    ///\n    /// Key size is measured using a metric called \"key size\" or \"data size,\"\n    /// which is intended to capture the number of live user-supplied bytes,\n    /// not including representational overhead.\n    /// This is distinct from the BFLATN size measured by [`Self::bytes_used_by_rows`].\n    /// See the trait [`crate::btree_index::KeySize`] for specifics on the metric measured.\n    pub fn bytes_used_by_index_keys(&self) -> u64 {\n        self.indexes.values().map(|idx| idx.num_key_bytes()).sum()\n    }\n}\n\n/// A reference to a single row within a table.\n///\n/// # Safety\n///\n/// Having a `r: RowRef` is a proof that [`r.pointer()`](RowRef::pointer) refers to a valid row.\n/// This makes constructing a `RowRef`, i.e., `RowRef::new`, an `unsafe` operation.\n#[derive(Copy, Clone)]\npub struct RowRef<'a> {\n    /// The table that has the row at `self.pointer`.\n    table: &'a TableInner,\n    /// The blob store used in case there are blob hashes to resolve.\n    blob_store: &'a dyn BlobStore,\n    /// The pointer to the row in `self.table`.\n    pointer: RowPointer,\n}\n\nimpl fmt::Debug for RowRef<'_> {\n    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt.debug_struct(\"RowRef\")\n            .field(\"pointer\", &self.pointer)\n            .field(\"value\", &self.to_product_value())\n            .finish_non_exhaustive()\n    }\n}\n\nimpl<'a> RowRef<'a> {\n    /// Construct a `RowRef` to the row at `pointer` within `table`.\n    ///\n    /// # Safety\n    ///\n    /// `pointer` must refer to a row within `table`\n    /// which was previously inserted and has not been deleted since.\n    ///\n    /// This means:\n    /// - The `PageIndex` of `pointer` must be in-bounds for `table.pages`.\n    /// - The `PageOffset` of `pointer` must be properly aligned for the row type of `table`,\n    ///   and must refer to a valid, live row in that page.\n    /// - The `SquashedOffset` of `pointer` must match `table.squashed_offset`.\n    ///\n    /// Showing that `pointer` was the result of a call to `table.insert`\n    /// and has not been passed to `table.delete`\n    /// is sufficient to demonstrate all of these properties.\n    unsafe fn new(\n        table: &'a TableInner,\n        blob_store: &'a dyn BlobStore,\n        _squashed_offset: SquashedOffset,\n        pointer: RowPointer,\n    ) -> Self {\n        debug_assert!(table.is_row_present(_squashed_offset, pointer));\n        Self {\n            table,\n            blob_store,\n            pointer,\n        }\n    }\n\n    /// Extract a `ProductValue` from the table.\n    ///\n    /// This is a potentially expensive operation,\n    /// as it must walk the table's `ProductTypeLayout`\n    /// and heap-allocate various substructures of the `ProductValue`.\n    pub fn to_product_value(&self) -> ProductValue {\n        let res = self\n            .serialize(ValueSerializer)\n            .unwrap_or_else(|x| match x {})\n            .into_product();\n        // SAFETY: the top layer of a row when serialized is always a product.\n        unsafe { res.unwrap_unchecked() }\n    }\n\n    /// Check that the `idx`th column of the row type stored by `self` is compatible with `T`,\n    /// and read the value of that column from `self`.\n    #[inline]\n    pub fn read_col<T: ReadColumn>(self, col: impl Into<ColId>) -> Result<T, TypeError> {\n        T::read_column(self, col.into().idx())\n    }\n\n    /// Construct a projection of the row at `self` by extracting the `cols`.\n    ///\n    /// If `cols` contains zero or more than one column, the values of the projected columns are wrapped in a [`ProductValue`].\n    /// If `cols` is a single column, the value of that column is returned without wrapping in a `ProductValue`.\n    ///\n    /// # Safety\n    ///\n    /// - `cols` must not specify any column which is out-of-bounds for the row `self´.\n    pub unsafe fn project_unchecked(self, cols: &ColList) -> AlgebraicValue {\n        let col_layouts = &self.row_layout().product().elements;\n\n        if let Some(head) = cols.as_singleton() {\n            let head = head.idx();\n            // SAFETY: caller promised that `head` is in-bounds of `col_layouts`.\n            let col_layout = unsafe { col_layouts.get_unchecked(head) };\n            // SAFETY:\n            // - `col_layout` was just derived from the row layout.\n            // - `AlgebraicValue` is compatible with any  `col_layout`.\n            // - `self` is a valid row and offsetting to `col_layout` is valid.\n            return unsafe { AlgebraicValue::unchecked_read_column(self, col_layout) };\n        }\n        let mut elements = Vec::with_capacity(cols.len() as usize);\n        for col in cols.iter() {\n            let col = col.idx();\n            // SAFETY: caller promised that any `col` is in-bounds of `col_layouts`.\n            let col_layout = unsafe { col_layouts.get_unchecked(col) };\n            // SAFETY:\n            // - `col_layout` was just derived from the row layout.\n            // - `AlgebraicValue` is compatible with any  `col_layout`.\n            // - `self` is a valid row and offsetting to `col_layout` is valid.\n            elements.push(unsafe { AlgebraicValue::unchecked_read_column(self, col_layout) });\n        }\n        AlgebraicValue::product(elements)\n    }\n\n    /// Construct a projection of the row at `self` by extracting the `cols`.\n    ///\n    /// Returns an error if `cols` specifies an index which is out-of-bounds for the row at `self`.\n    ///\n    /// If `cols` contains zero or more than one column, the values of the projected columns are wrapped in a [`ProductValue`].\n    /// If `cols` is a single column, the value of that column is returned without wrapping in a `ProductValue`.\n    /// If you want to wrap single elements in a [`ProductValue`], see [`Self::project_product`].\n    pub fn project(self, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {\n        if let Some(head) = cols.as_singleton() {\n            return self.read_col(head).map_err(|_| head.into());\n        }\n        let mut elements = Vec::with_capacity(cols.len() as usize);\n        for col in cols.iter() {\n            let col_val = self.read_col(col).map_err(|err| match err {\n                TypeError::WrongType { .. } => {\n                    unreachable!(\"AlgebraicValue::read_column never returns a `TypeError::WrongType`\")\n                }\n                TypeError::IndexOutOfBounds { .. } => col,\n            })?;\n            elements.push(col_val);\n        }\n        Ok(AlgebraicValue::product(elements))\n    }\n\n    /// Construct a projection of the row at `self` by extracting the `cols`.\n    ///\n    /// Returns an error if `cols` specifies an index which is out-of-bounds for the row at `self`.\n    ///\n    /// This method always returns a [`ProductValue`], even when projecting a single element.\n    /// If you don't want to wrap single elements in a [`ProductValue`], see [`Self::project`].\n    pub fn project_product(self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {\n        let mut elements = Vec::with_capacity(cols.len() as usize);\n        for col in cols.iter() {\n            let col_val = self.read_col(col).map_err(|err| match err {\n                TypeError::WrongType { .. } => {\n                    unreachable!(\"AlgebraicValue::read_column never returns a `TypeError::WrongType`\")\n                }\n                TypeError::IndexOutOfBounds { .. } => col,\n            })?;\n            elements.push(col_val);\n        }\n        Ok(ProductValue::from(elements))\n    }\n\n    /// Returns the raw row pointer for this row reference.\n    pub fn pointer(&self) -> RowPointer {\n        self.pointer\n    }\n\n    /// Returns the blob store that any [`crate::blob_store::BlobHash`]es within the row refer to.\n    pub(crate) fn blob_store(&self) -> &dyn BlobStore {\n        self.blob_store\n    }\n\n    /// Return the layout of the row.\n    ///\n    /// All rows within the same table will have the same layout.\n    pub fn row_layout(&self) -> &RowTypeLayout {\n        &self.table.row_layout\n    }\n\n    /// Returns the page the row is in and the offset of the row within that page.\n    pub fn page_and_offset(&self) -> (&Page, PageOffset) {\n        self.table.page_and_offset(self.pointer())\n    }\n\n    /// Returns the bytes for the fixed portion of this row.\n    pub(crate) fn get_row_data(&self) -> &Bytes {\n        let (page, offset) = self.page_and_offset();\n        page.get_row_data(offset, self.table.row_layout.size())\n    }\n\n    /// Returns the row hash for `ptr`.\n    pub fn row_hash(&self) -> RowHash {\n        RowHash(RowHash::hasher_builder().hash_one(self))\n    }\n\n    /// Returns the static layout for this row reference, if any.\n    pub fn static_layout(&self) -> Option<&StaticLayout> {\n        self.table.static_layout.as_ref().map(|(s, _)| s)\n    }\n\n    /// Encode the row referred to by `self` into a `Vec<u8>` using BSATN and then deserialize it.\n    pub fn read_via_bsatn<T>(&self, scratch: &mut Vec<u8>) -> Result<T, ReadViaBsatnError>\n    where\n        T: DeserializeOwned,\n    {\n        self.to_bsatn_extend(scratch)?;\n        Ok(bsatn::from_slice::<T>(scratch)?)\n    }\n\n    /// Return the number of bytes in the blob store to which this object holds a reference.\n    ///\n    /// Used to compute the table's `blob_store_bytes` when reconstructing a snapshot.\n    ///\n    /// Even within a single row, this is a conservative overestimate,\n    /// as a row may contain multiple references to the same large blob.\n    /// This seems unlikely to occur in practice.\n    fn blob_store_bytes(&self) -> usize {\n        let row_data = self.get_row_data();\n        let (page, _) = self.page_and_offset();\n        // SAFETY:\n        // - Existence of a `RowRef` treated as proof\n        //   of the row's validity and type information's correctness.\n        unsafe { self.table.visitor_prog.visit_var_len(row_data) }\n            .filter(|vlr| vlr.is_large_blob())\n            .map(|vlr| {\n                // SAFETY:\n                // - Because `vlr.is_large_blob`, it points to exactly one granule.\n                let granule = unsafe { page.iter_var_len_object(vlr.first_granule) }.next().unwrap();\n                let blob_hash = granule.blob_hash();\n                let blob = self.blob_store.retrieve_blob(&blob_hash).unwrap();\n\n                blob.len()\n            })\n            .sum()\n    }\n}\n\nimpl Serialize for RowRef<'_> {\n    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {\n        let table = self.table;\n        let (page, offset) = table.page_and_offset(self.pointer);\n        // SAFETY: `ptr` points to a valid row in this table per above check.\n        unsafe { serialize_row_from_page(ser, page, self.blob_store, offset, &table.row_layout) }\n    }\n}\n\nimpl ToBsatn for RowRef<'_> {\n    /// BSATN-encode the row referred to by `self` into a freshly-allocated `Vec<u8>`.\n    ///\n    /// This method will use a [`StaticLayout`] if one is available,\n    /// and may therefore be faster than calling [`bsatn::to_vec`].\n    fn to_bsatn_vec(&self) -> Result<Vec<u8>, BsatnError> {\n        if let Some(static_layout) = self.static_layout() {\n            // Use fast path, by first fetching the row data and then using the static layout.\n            let row = self.get_row_data();\n            // SAFETY:\n            // - Existence of a `RowRef` treated as proof\n            //   of row's validity and type information's correctness.\n            Ok(unsafe { static_layout.serialize_row_into_vec(row) })\n        } else {\n            bsatn::to_vec(self)\n        }\n    }\n\n    /// BSATN-encode the row referred to by `self` into `buf`,\n    /// pushing `self`'s bytes onto the end of `buf`, similar to [`Vec::extend`].\n    ///\n    /// This method will use a [`StaticLayout`] if one is available,\n    /// and may therefore be faster than calling [`bsatn::to_writer`].\n    fn to_bsatn_extend(&self, buf: &mut (impl BufWriter + BufReservedFill)) -> Result<(), BsatnError> {\n        if let Some(static_layout) = self.static_layout() {\n            // Use fast path, by first fetching the row data and then using the static layout.\n            let row = self.get_row_data();\n            // SAFETY:\n            // - Existence of a `RowRef` treated as proof\n            //   of row's validity and type information's correctness.\n            unsafe {\n                static_layout.serialize_row_extend(buf, row);\n            }\n            Ok(())\n        } else {\n            // Use the slower, but more general, `bsatn_from` serializer to write the row.\n            bsatn::to_writer(buf, self)\n        }\n    }\n\n    fn static_bsatn_size(&self) -> Option<u16> {\n        self.static_layout().map(|sl| sl.bsatn_length)\n    }\n}\n\nimpl Eq for RowRef<'_> {}\nimpl PartialEq for RowRef<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        // Ensure that the layouts are the same\n        // so that we can use `eq_row_in_page`.\n        // To do this, we first try address equality on the layouts.\n        // This should succeed when the rows originate from the same table.\n        // Otherwise, actually compare the layouts, which is expensive, but unlikely to happen.\n        let a_ty = self.row_layout();\n        let b_ty = other.row_layout();\n        if !(ptr::eq(a_ty, b_ty) || a_ty == b_ty) {\n            return false;\n        }\n        let (page_a, offset_a) = self.page_and_offset();\n        let (page_b, offset_b) = other.page_and_offset();\n        let static_layout = self.static_layout();\n        // SAFETY: `offset_a/b` are valid rows in `page_a/b` typed at `a_ty`\n        // and `static_bsatn_layout` is derived from `a_ty`.\n        unsafe { eq_row_in_page(page_a, page_b, offset_a, offset_b, a_ty, static_layout) }\n    }\n}\n\nimpl PartialEq<ProductValue> for RowRef<'_> {\n    fn eq(&self, rhs: &ProductValue) -> bool {\n        let ty = self.row_layout();\n        let (page, offset) = self.page_and_offset();\n        // SAFETY: By having `RowRef`,\n        // we know that `offset` is a valid offset for a row in `page` typed at `ty`.\n        unsafe { eq_row_in_page_to_pv(self.blob_store, page, offset, rhs, ty) }\n    }\n}\n\nimpl Hash for RowRef<'_> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        let (page, offset) = self.table.page_and_offset(self.pointer);\n        let ty = &self.table.row_layout;\n        // SAFETY: A `RowRef` is a proof that `self.pointer` refers to a live fixed row in `self.table`, so:\n        // 1. `offset` points at a row in `page` lasting `ty.size()` bytes.\n        // 2. the row is valid for `ty`.\n        // 3. for any `vlr: VarLenRef` stored in the row,\n        //    `vlr.first_offset` is either `NULL` or points to a valid granule in `page`.\n        unsafe { hash_row_in_page(state, page, self.blob_store, offset, ty) };\n    }\n}\n\n/// An iterator over all the rows, yielded as [`RowRef`]s, in a table.\npub struct TableScanIter<'table> {\n    /// The current page we're yielding rows from.\n    /// When `None`, the iterator will attempt to advance to the next page, if any.\n    current_page: Option<FixedLenRowsIter<'table>>,\n    /// The current page index we are or will visit.\n    current_page_idx: PageIndex,\n    /// The table the iterator is yielding rows from.\n    pub(crate) table: &'table Table,\n    /// The `BlobStore` that row references may refer into.\n    pub(crate) blob_store: &'table dyn BlobStore,\n}\n\nimpl<'a> Iterator for TableScanIter<'a> {\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // This could have been written using `.flat_map`,\n        // but we don't have `type Foo = impl Iterator<...>;` on stable yet.\n        loop {\n            match &mut self.current_page {\n                // We're currently visiting a page,\n                Some(iter_fixed_len) => {\n                    if let Some(page_offset) = iter_fixed_len.next() {\n                        // There's still at least one row in that page to visit,\n                        // return a ref to that row.\n                        let ptr =\n                            RowPointer::new(false, self.current_page_idx, page_offset, self.table.squashed_offset);\n\n                        // SAFETY: `offset` came from the `iter_fixed_len`, so it must point to a valid row.\n                        let row_ref = unsafe { self.table.get_row_ref_unchecked(self.blob_store, ptr) };\n                        return Some(row_ref);\n                    } else {\n                        // We've finished visiting that page, so set `current_page` to `None`,\n                        // increment `self.current_page_idx` to the index of the next page,\n                        // and go to the `None` case (1) in the match.\n                        self.current_page = None;\n                        self.current_page_idx.0 += 1;\n                    }\n                }\n\n                // (1) If we aren't currently visiting a page,\n                // the `else` case in the `Some` match arm\n                // already incremented `self.current_page_idx`,\n                // or we're just beginning and so it was initialized as 0.\n                None => {\n                    // If there's another page, set `self.current_page` to it,\n                    // and go to the `Some` case in the match.\n                    let next_page = self.table.pages().get(self.current_page_idx.idx())?;\n                    let iter = next_page.iter_fixed_len(self.table.row_size());\n                    self.current_page = Some(iter);\n                }\n            }\n        }\n    }\n}\n\n/// A combined table and index,\n/// allowing direct extraction of a [`IndexScanIter`].\n#[derive(Copy, Clone)]\npub struct TableAndIndex<'a> {\n    table: &'a Table,\n    blob_store: &'a dyn BlobStore,\n    index: &'a TableIndex,\n}\n\nimpl<'a> TableAndIndex<'a> {\n    pub fn table(&self) -> &'a Table {\n        self.table\n    }\n\n    pub fn index(&self) -> &'a TableIndex {\n        self.index\n    }\n\n    /// Wraps `ptr` in a [`RowRef`].\n    ///\n    /// # Safety\n    ///\n    /// The `self.table().is_row_present(ptr)` must hold.\n    pub unsafe fn combine_with_ptr(&self, ptr: RowPointer) -> RowRef<'a> {\n        // SAFETY: forward caller requirement.\n        unsafe { self.table.get_row_ref_unchecked(self.blob_store, ptr) }\n    }\n\n    /// Returns an iterator yielding all rows in this index for `key`.\n    ///\n    /// Matching is defined by `Eq for AlgebraicValue`.\n    pub fn seek_point(&self, key: &AlgebraicValue) -> IndexScanPointIter<'a> {\n        IndexScanPointIter {\n            table: self.table,\n            blob_store: self.blob_store,\n            btree_index_iter: self.index.seek_point(key),\n        }\n    }\n\n    /// Returns an iterator yielding all rows in this index that fall within `range`,\n    /// if the index is compatible with range seeks.\n    ///\n    /// Matching is defined by `Ord for AlgebraicValue`.\n    pub fn seek_range(\n        &self,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> Result<IndexScanRangeIter<'a>, IndexCannotSeekRange> {\n        Ok(IndexScanRangeIter {\n            table: self.table,\n            blob_store: self.blob_store,\n            btree_index_iter: self.index.seek_range(range)?,\n        })\n    }\n}\n\n/// An iterator using a [`TableIndex`] to scan a `table`\n/// for all the [`RowRef`]s matching the specified `key` in the indexed column(s).\n///\n/// Matching is defined by `Ord for AlgebraicValue`.\npub struct IndexScanPointIter<'a> {\n    /// The table being scanned for rows.\n    table: &'a Table,\n    /// The blob store; passed on to the [`RowRef`]s in case they need it.\n    blob_store: &'a dyn BlobStore,\n    /// The iterator performing the index scan yielding row pointers.\n    btree_index_iter: TableIndexPointIter<'a>,\n}\n\nimpl<'a> IndexScanPointIter<'a> {\n    /// Consume the iterator, returning the inner one.\n    pub fn index(self) -> TableIndexPointIter<'a> {\n        self.btree_index_iter\n    }\n}\n\nimpl<'a> Iterator for IndexScanPointIter<'a> {\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.btree_index_iter.next().map(|ptr| {\n            // SAFETY: `ptr` came from the index, which always holds pointers to valid rows for its table.\n            unsafe { self.table.get_row_ref_unchecked(self.blob_store, ptr) }\n        })\n    }\n}\n\n/// An iterator using a [`TableIndex`] to scan a `table`\n/// for all the [`RowRef`]s matching the specified `range` in the indexed column(s).\n///\n/// Matching is defined by `Ord for AlgebraicValue`.\npub struct IndexScanRangeIter<'a> {\n    /// The table being scanned for rows.\n    table: &'a Table,\n    /// The blob store; passed on to the [`RowRef`]s in case they need it.\n    blob_store: &'a dyn BlobStore,\n    /// The iterator performing the index scan yielding row pointers.\n    btree_index_iter: TableIndexRangeIter<'a>,\n}\n\nimpl<'a> Iterator for IndexScanRangeIter<'a> {\n    type Item = RowRef<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.btree_index_iter.next().map(|ptr| {\n            // SAFETY: `ptr` came from the index, which always holds pointers to valid rows for its table.\n            unsafe { self.table.get_row_ref_unchecked(self.blob_store, ptr) }\n        })\n    }\n}\n\n#[derive(Error, Debug, PartialEq, Eq)]\n#[error(\"Unique constraint violation '{}' in table '{}': column(s): '{:?}' value: {}\", constraint_name, table_name, cols, value.to_satn())]\npub struct UniqueConstraintViolation {\n    pub constraint_name: RawIdentifier,\n    pub table_name: TableName,\n    pub cols: Vec<Identifier>,\n    pub value: AlgebraicValue,\n}\n\nimpl UniqueConstraintViolation {\n    /// Returns a unique constraint violation error for the given `index`\n    /// and the `value` that would have been duplicated.\n    ///\n    /// In this version, the [`IndexSchema`] is looked up in `schema` based on `index_id`.\n    #[cold]\n    fn build(schema: &TableSchema, index: &TableIndex, index_id: IndexId, value: AlgebraicValue) -> Self {\n        let index_schema = schema.indexes.iter().find(|i| i.index_id == index_id).unwrap();\n        Self::build_with_index_schema(schema, index, index_schema, value)\n    }\n\n    /// Returns a unique constraint violation error for the given `index`\n    /// and the `value` that would have been duplicated.\n    ///\n    /// In this version, the `index_schema` is explicitly passed.\n    #[cold]\n    pub fn build_with_index_schema(\n        schema: &TableSchema,\n        index: &TableIndex,\n        index_schema: &IndexSchema,\n        value: AlgebraicValue,\n    ) -> Self {\n        // Fetch the table name.\n        let table_name = schema.table_name.clone();\n\n        // Fetch the names of the columns used in the index.\n        let cols = schema\n            .get_columns(&index.indexed_columns)\n            .map(|(_, cs)| cs.unwrap().col_name.clone())\n            .collect();\n\n        // Fetch the name of the index.\n        let constraint_name = index_schema.index_name.clone();\n\n        Self {\n            constraint_name,\n            table_name,\n            cols,\n            value,\n        }\n    }\n}\n\n// Private API:\nimpl Table {\n    /// Returns a unique constraint violation error for the given `index`\n    /// and the `value` that would have been duplicated.\n    #[cold]\n    pub fn build_error_unique(\n        &self,\n        index: &TableIndex,\n        index_id: IndexId,\n        value: AlgebraicValue,\n    ) -> UniqueConstraintViolation {\n        let schema = self.get_schema();\n        UniqueConstraintViolation::build(schema, index, index_id, value)\n    }\n\n    /// Returns a new empty table using the particulars passed.\n    fn new_raw(\n        schema: Arc<TableSchema>,\n        row_layout: RowTypeLayout,\n        static_layout: Option<(StaticLayout, StaticBsatnValidator)>,\n        visitor_prog: VarLenVisitorProgram,\n        squashed_offset: SquashedOffset,\n        pointer_map: Option<PointerMap>,\n    ) -> Self {\n        Self {\n            inner: TableInner {\n                row_layout,\n                static_layout,\n                visitor_prog,\n                pages: Pages::default(),\n            },\n            is_scheduler: schema.schedule.is_some(),\n            schema,\n            indexes: BTreeMap::new(),\n            pointer_map,\n            squashed_offset,\n            row_count: 0,\n            blob_store_bytes: BlobNumBytes::default(),\n        }\n    }\n\n    /// Returns whether the row at `ptr` is present or not.\n    // TODO: Remove all uses of this method,\n    //       or more likely, gate them behind `debug_assert!`\n    //       so they don't have semantic meaning.\n    //\n    //       Unlike the previous `locking_tx_datastore::Table`'s `RowId`,\n    //       `RowPointer` is not content-addressed.\n    //       This means it is possible to:\n    //       - have a `RowPointer` A* to row A,\n    //       - Delete row A,\n    //       - Insert row B into the same storage as freed from A,\n    //       - Test `is_row_present(A*)`, which falsely reports that row A is still present.\n    //\n    //       In the final interface, this method is superfluous anyways,\n    //       as `RowPointer` is not part of our public interface.\n    //       Instead, we will always discover a known-present `RowPointer`\n    //       during a table scan or index seek.\n    //       As such, our `delete` and `insert` methods can be `unsafe`\n    //       and trust that the `RowPointer` is valid.\n    fn is_row_present(&self, ptr: RowPointer) -> bool {\n        if self.squashed_offset != ptr.squashed_offset() {\n            return false;\n        }\n        let Some((page, offset)) = self.inner.try_page_and_offset(ptr) else {\n            return false;\n        };\n        page.has_row_offset(self.row_size(), offset)\n    }\n\n    /// Returns the row size for a row in the table.\n    pub fn row_size(&self) -> Size {\n        self.row_layout().size()\n    }\n\n    /// Returns the layout for a row in the table.\n    fn row_layout(&self) -> &RowTypeLayout {\n        &self.inner.row_layout\n    }\n\n    /// Returns the pages storing the physical rows of this table.\n    fn pages(&self) -> &Pages {\n        &self.inner.pages\n    }\n\n    /// Iterates over each [`Page`] in this table, ensuring that its hash is computed before yielding it.\n    ///\n    /// Used when capturing a snapshot.\n    pub fn iter_pages_with_hashes(&mut self) -> impl Iterator<Item = (blake3::Hash, &Page)> {\n        self.inner.pages.iter_mut().map(|page| {\n            let hash = page.save_or_get_content_hash();\n            (hash, &**page)\n        })\n    }\n\n    /// Returns the number of pages storing the physical rows of this table.\n    fn num_pages(&self) -> usize {\n        self.inner.pages.len()\n    }\n\n    /// Returns the [`StaticLayout`] for this table,\n    pub(crate) fn static_layout(&self) -> Option<&StaticLayout> {\n        self.inner.static_layout.as_ref().map(|(s, _)| s)\n    }\n\n    /// Rebuild the [`PointerMap`] by iterating over all the rows in `self` and inserting them.\n    ///\n    /// Called when restoring from a snapshot after installing the pages,\n    /// but after computing the row count,\n    /// since snapshots do not save the pointer map..\n    fn rebuild_pointer_map(&mut self, blob_store: &dyn BlobStore) -> PointerMap {\n        // TODO(perf): Pre-allocate `PointerMap.map` with capacity `self.row_count`.\n        // Alternatively, do this at the same time as `compute_row_count`.\n        self.scan_rows(blob_store)\n            .map(|row_ref| (row_ref.row_hash(), row_ref.pointer()))\n            .collect()\n    }\n\n    /// Compute and store `self.row_count` and `self.blob_store_bytes`\n    /// by iterating over all the rows in `self` and counting them.\n    ///\n    /// Called when restoring from a snapshot after installing the pages,\n    /// since snapshots do not save this metadata.\n    fn compute_row_count(&mut self, blob_store: &dyn BlobStore) {\n        let mut row_count = 0;\n        let mut blob_store_bytes = 0;\n        for row in self.scan_rows(blob_store) {\n            row_count += 1;\n            blob_store_bytes += row.blob_store_bytes();\n        }\n        self.row_count = row_count as u64;\n        self.blob_store_bytes = blob_store_bytes.into();\n    }\n}\n\n#[cfg(test)]\npub(crate) mod test {\n    use super::*;\n    use crate::blob_store::{HashMapBlobStore, NullBlobStore};\n    use crate::page::tests::hash_unmodified_save_get;\n    use crate::var_len::VarLenGranule;\n    use proptest::prelude::*;\n    use proptest::test_runner::TestCaseResult;\n    use spacetimedb_lib::db::raw_def::v9::{btree, RawModuleDefV9Builder};\n    use spacetimedb_primitives::TableId;\n    use spacetimedb_sats::bsatn::to_vec;\n    use spacetimedb_sats::proptest::{generate_typed_row, generate_typed_row_vec, SIZE};\n    use spacetimedb_sats::{product, AlgebraicType, ArrayValue};\n    use spacetimedb_schema::def::{BTreeAlgorithm, ModuleDef};\n    use spacetimedb_schema::schema::Schema as _;\n\n    /// Create a `Table` from a `ProductType` without validation.\n    pub(crate) fn table(ty: ProductType) -> Table {\n        // Use a fast path here to avoid slowing down Miri in the proptests.\n        // Does not perform validation.\n        let schema = TableSchema::from_product_type(ty);\n        Table::new(schema.into(), SquashedOffset::COMMITTED_STATE)\n    }\n\n    #[test]\n    fn unique_violation_error() {\n        let table_name = \"UniqueIndexed\";\n        let index_name = \"UniqueIndexed_unique_col_idx_btree\";\n        let mut builder = RawModuleDefV9Builder::new();\n        builder\n            .build_table_with_new_type(\n                table_name,\n                ProductType::from([(\"unique_col\", AlgebraicType::I32), (\"other_col\", AlgebraicType::I32)]),\n                true,\n            )\n            .with_unique_constraint(0)\n            .with_index(btree(0), \"accessor_name_doesnt_matter\");\n\n        let def: ModuleDef = builder.finish().try_into().expect(\"Failed to build schema\");\n\n        let schema = TableSchema::from_module_def(&def, def.table(table_name).unwrap(), (), TableId::SENTINEL);\n        assert_eq!(schema.indexes.len(), 1);\n        let index_schema = schema.indexes[0].clone();\n\n        let mut table = Table::new(schema.into(), SquashedOffset::COMMITTED_STATE);\n        let pool = PagePool::new_for_test();\n        let cols = ColList::new(0.into());\n        let algo = BTreeAlgorithm { columns: cols.clone() }.into();\n\n        let index = table.new_index(&algo, true).unwrap();\n        // SAFETY: Index was derived from `table`.\n        unsafe { table.insert_index(&NullBlobStore, index_schema.index_id, index) };\n\n        // Reserve a page so that we can check the hash.\n        let pi = table.inner.pages.reserve_empty_page(&pool, table.row_size()).unwrap();\n        let hash_pre_ins = hash_unmodified_save_get(&mut table.inner.pages[pi]);\n\n        // Insert the row (0, 0).\n        table\n            .insert(&pool, &mut NullBlobStore, &product![0i32, 0i32])\n            .expect(\"Initial insert failed\");\n\n        // Inserting cleared the hash.\n        let hash_post_ins = hash_unmodified_save_get(&mut table.inner.pages[pi]);\n        assert_ne!(hash_pre_ins, hash_post_ins);\n\n        // Try to insert the row (0, 1), and assert that we get the expected error.\n        match table.insert(&pool, &mut NullBlobStore, &product![0i32, 1i32]) {\n            Ok(_) => panic!(\"Second insert with same unique value succeeded\"),\n            Err(InsertError::IndexError(UniqueConstraintViolation {\n                constraint_name,\n                table_name,\n                cols,\n                value,\n            })) => {\n                assert_eq!(&*constraint_name, index_name);\n                assert_eq!(&*table_name, \"UniqueIndexed\");\n                assert_eq!(cols.iter().map(|c| c.to_string()).collect::<Vec<_>>(), &[\"unique_col\"]);\n                assert_eq!(value, AlgebraicValue::I32(0));\n            }\n            Err(e) => panic!(\"Expected UniqueConstraintViolation but found {e:?}\"),\n        }\n\n        // Second insert did clear the hash while we had a constraint violation,\n        // as constraint checking is done after insertion and then rolled back.\n        assert_eq!(table.inner.pages[pi].unmodified_hash(), None);\n    }\n\n    fn insert_retrieve_body(ty: impl Into<ProductType>, val: impl Into<ProductValue>) -> TestCaseResult {\n        let val = val.into();\n        let pool = PagePool::new_for_test();\n        let mut blob_store = HashMapBlobStore::default();\n        let mut table = table(ty.into());\n        let (hash, row) = table.insert(&pool, &mut blob_store, &val).unwrap();\n        let hash = hash.unwrap();\n        prop_assert_eq!(row.row_hash(), hash);\n        let ptr = row.pointer();\n        prop_assert_eq!(table.pointers_for(hash), &[ptr]);\n\n        prop_assert_eq!(table.inner.pages.len(), 1);\n        prop_assert_eq!(table.inner.pages[PageIndex(0)].num_rows(), 1);\n\n        let row_ref = table.get_row_ref(&blob_store, ptr).unwrap();\n        prop_assert_eq!(row_ref.to_product_value(), val.clone());\n        let bsatn_val = to_vec(&val).unwrap();\n        prop_assert_eq!(&bsatn_val, &to_vec(&row_ref).unwrap());\n        prop_assert_eq!(&bsatn_val, &row_ref.to_bsatn_vec().unwrap());\n\n        prop_assert_eq!(\n            &table.scan_rows(&blob_store).map(|r| r.pointer()).collect::<Vec<_>>(),\n            &[ptr]\n        );\n\n        Ok(())\n    }\n\n    #[test]\n    fn repro_serialize_bsatn_empty_array() {\n        let ty = AlgebraicType::array(AlgebraicType::U64);\n        let arr = ArrayValue::from(Vec::<u64>::new().into_boxed_slice());\n        insert_retrieve_body(ty, AlgebraicValue::from(arr)).unwrap();\n    }\n\n    #[test]\n    fn repro_serialize_bsatn_debug_assert() {\n        let ty = AlgebraicType::array(AlgebraicType::U64);\n        let arr = ArrayValue::from((0..130u64).collect::<Box<_>>());\n        insert_retrieve_body(ty, AlgebraicValue::from(arr)).unwrap();\n    }\n\n    fn reconstruct_index_num_key_bytes(table: &Table, blob_store: &dyn BlobStore, index_id: IndexId) -> u64 {\n        let index = table.get_index_by_id(index_id).unwrap();\n\n        index\n            .seek_range(&(..))\n            .unwrap()\n            .map(|row_ptr| {\n                let row_ref = table.get_row_ref(blob_store, row_ptr).unwrap();\n                let key = row_ref.project(&index.indexed_columns).unwrap();\n                crate::table_index::KeySize::key_size_in_bytes(&key) as u64\n            })\n            .sum()\n    }\n\n    /// Given a row type `ty`, a set of rows of that type `vals`,\n    /// and a set of columns within that type `indexed_columns`,\n    /// populate a table with `vals`, add an index on the `indexed_columns`,\n    /// and perform various assertions that the reported index size metrics are correct.\n    fn test_index_size_reporting(\n        ty: ProductType,\n        vals: Vec<ProductValue>,\n        indexed_columns: ColList,\n    ) -> Result<(), TestCaseError> {\n        let pool = PagePool::new_for_test();\n        let mut blob_store = HashMapBlobStore::default();\n        let mut table = table(ty.clone());\n\n        for row in &vals {\n            prop_assume!(table.insert(&pool, &mut blob_store, row).is_ok());\n        }\n\n        // We haven't added any indexes yet, so there should be 0 rows in indexes.\n        prop_assert_eq!(table.num_rows_in_indexes(), 0);\n\n        let index_id = IndexId(0);\n\n        let index = TableIndex::new(&ty, indexed_columns.clone(), IndexKind::BTree, false).unwrap();\n        // Add an index on column 0.\n        // Safety:\n        // We're using `ty` as the row type for both `table` and the new index.\n        unsafe { table.insert_index(&blob_store, index_id, index) };\n\n        // We have one index, which should be fully populated,\n        // so in total we should have the same number of rows in indexes as we have rows.\n        prop_assert_eq!(table.num_rows_in_indexes(), table.num_rows());\n\n        let index = table.get_index_by_id(index_id).unwrap();\n\n        // One index, so table's reporting of bytes used should match that index's reporting.\n        prop_assert_eq!(table.bytes_used_by_index_keys(), index.num_key_bytes());\n\n        // Walk all the rows in the index, sum their key size,\n        // and assert it matches the `index.num_key_bytes()`\n        prop_assert_eq!(\n            index.num_key_bytes(),\n            reconstruct_index_num_key_bytes(&table, &blob_store, index_id)\n        );\n\n        // Walk all the rows we inserted, project them to the cols that will be their keys,\n        // sum their key size,\n        // and assert it matches the `index.num_key_bytes()`\n        let key_size_in_pvs = vals\n            .iter()\n            .map(|row| crate::table_index::KeySize::key_size_in_bytes(&row.project(&indexed_columns).unwrap()) as u64)\n            .sum();\n        prop_assert_eq!(index.num_key_bytes(), key_size_in_pvs);\n\n        let index = TableIndex::new(&ty, indexed_columns, IndexKind::BTree, false).unwrap();\n        // Add a duplicate of the same index, so we can check that all above quantities double.\n        // Safety:\n        // As above, we're using `ty` as the row type for both `table` and the new index.\n        unsafe { table.insert_index(&blob_store, IndexId(1), index) };\n\n        prop_assert_eq!(table.num_rows_in_indexes(), table.num_rows() * 2);\n        prop_assert_eq!(table.bytes_used_by_index_keys(), key_size_in_pvs * 2);\n\n        Ok(())\n    }\n\n    proptest! {\n        #![proptest_config(ProptestConfig { max_shrink_iters: 0x10000000, ..Default::default() })]\n\n        #[test]\n        fn insert_retrieve((ty, val) in generate_typed_row()) {\n            insert_retrieve_body(ty, val)?;\n        }\n\n        #[test]\n        fn insert_delete_removed_from_pointer_map((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty);\n            let (hash, row) = table.insert(&pool, &mut blob_store, &val).unwrap();\n            let hash = hash.unwrap();\n            prop_assert_eq!(row.row_hash(), hash);\n            let ptr = row.pointer();\n            prop_assert_eq!(table.pointers_for(hash), &[ptr]);\n\n            prop_assert_eq!(table.inner.pages.len(), 1);\n            prop_assert_eq!(table.inner.pages[PageIndex(0)].num_rows(), 1);\n            prop_assert_eq!(&table.scan_rows(&blob_store).map(|r| r.pointer()).collect::<Vec<_>>(), &[ptr]);\n            prop_assert_eq!(table.row_count, 1);\n\n            let hash_pre_del = hash_unmodified_save_get(&mut table.inner.pages[ptr.page_index()]);\n\n            table.delete(&mut blob_store, ptr, |_| ());\n\n            let hash_post_del = hash_unmodified_save_get(&mut table.inner.pages[ptr.page_index()]);\n            assert_ne!(hash_pre_del, hash_post_del);\n\n            prop_assert_eq!(table.pointers_for(hash), &[]);\n\n            prop_assert_eq!(table.inner.pages.len(), 1);\n            prop_assert_eq!(table.inner.pages[PageIndex(0)].num_rows(), 0);\n            prop_assert_eq!(table.row_count, 0);\n\n            prop_assert!(&table.scan_rows(&blob_store).next().is_none());\n        }\n\n        #[test]\n        fn insert_duplicate_set_semantic((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty);\n\n            let (hash, row) = table.insert(&pool, &mut blob_store, &val).unwrap();\n            let hash = hash.unwrap();\n            prop_assert_eq!(row.row_hash(), hash);\n            let ptr = row.pointer();\n            prop_assert_eq!(table.inner.pages.len(), 1);\n            prop_assert_eq!(table.pointers_for(hash), &[ptr]);\n            prop_assert_eq!(table.row_count, 1);\n            prop_assert_eq!(&table.scan_rows(&blob_store).map(|r| r.pointer()).collect::<Vec<_>>(), &[ptr]);\n\n            let blob_uses = blob_store.usage_counter();\n\n            let hash_pre_ins = hash_unmodified_save_get(&mut table.inner.pages[ptr.page_index()]);\n\n            prop_assert!(table.insert(&pool, &mut blob_store, &val).is_err());\n\n            // Hash was cleared and is different despite failure to insert.\n            let hash_post_ins = hash_unmodified_save_get(&mut table.inner.pages[ptr.page_index()]);\n            assert_ne!(hash_pre_ins, hash_post_ins);\n\n            prop_assert_eq!(table.row_count, 1);\n            prop_assert_eq!(table.inner.pages.len(), 1);\n            prop_assert_eq!(table.pointers_for(hash), &[ptr]);\n\n            let blob_uses_after = blob_store.usage_counter();\n\n            prop_assert_eq!(blob_uses_after, blob_uses);\n            prop_assert_eq!(table.inner.pages[PageIndex(0)].num_rows(), 1);\n            prop_assert_eq!(&table.scan_rows(&blob_store).map(|r| r.pointer()).collect::<Vec<_>>(), &[ptr]);\n        }\n\n        #[test]\n        fn insert_bsatn_same_as_pv((ty, val) in generate_typed_row()) {\n            let pool = PagePool::new_for_test();\n            let mut bs_pv = HashMapBlobStore::default();\n            let mut table_pv = table(ty.clone());\n            let res_pv = table_pv.insert(&pool, &mut bs_pv, &val);\n\n            let mut bs_bsatn = HashMapBlobStore::default();\n            let mut table_bsatn = table(ty);\n            let res_bsatn = insert_bsatn(&mut table_bsatn, &mut bs_bsatn, &val);\n\n            prop_assert_eq!(res_pv, res_bsatn);\n            prop_assert_eq!(bs_pv, bs_bsatn);\n            prop_assert_eq!(table_pv, table_bsatn);\n        }\n\n        #[test]\n        fn row_size_reporting_matches_slow_implementations((ty, vals) in generate_typed_row_vec(0..SIZE, 128, 2048)) {\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let mut table = table(ty.clone());\n\n            for row in &vals {\n                prop_assume!(table.insert(&pool, &mut blob_store, row).is_ok());\n            }\n\n            prop_assert_eq!(table.bytes_used_by_rows(), table.reconstruct_bytes_used_by_rows());\n            prop_assert_eq!(table.num_rows(), table.reconstruct_num_rows());\n            prop_assert_eq!(table.num_rows(), vals.len() as u64);\n\n            // TODO(testing): Determine if there's a meaningful way to test that the blob store reporting is correct.\n            // I (pgoldman 2025-01-27) doubt it, as the test would be \"visit every blob and sum their size,\"\n            // which is already what the actual implementation does.\n        }\n\n        #[test]\n        fn index_size_reporting_matches_slow_implementations_single_column((ty, vals) in generate_typed_row_vec(1..SIZE, 128, 2048)) {\n            test_index_size_reporting(ty, vals, ColList::from(ColId(0)))?;\n        }\n\n        #[test]\n        fn index_size_reporting_matches_slow_implementations_two_column((ty, vals) in generate_typed_row_vec(2..SIZE, 128, 2048)) {\n            test_index_size_reporting(ty, vals, ColList::from([ColId(0), ColId(1)]))?;\n        }\n    }\n\n    fn insert_bsatn<'a>(\n        table: &'a mut Table,\n        blob_store: &'a mut dyn BlobStore,\n        val: &ProductValue,\n    ) -> Result<(Option<RowHash>, RowRef<'a>), InsertError> {\n        let row = &to_vec(&val).unwrap();\n\n        // Optimistically insert the `row` before checking any constraints\n        // under the assumption that errors (unique constraint & set semantic violations) are rare.\n        let pool = PagePool::new_for_test();\n        let (row_ref, blob_bytes) = table.insert_physically_bsatn(&pool, blob_store, row)?;\n        let row_ptr = row_ref.pointer();\n\n        // Confirm the insertion, checking any constraints, removing the physical row on error.\n        // SAFETY: We just inserted `ptr`, so it must be present.\n        let (hash, row_ptr) = unsafe { table.confirm_insertion::<true>(blob_store, row_ptr, blob_bytes) }?;\n        // SAFETY: Per post-condition of `confirm_insertion`, `row_ptr` refers to a valid row.\n        let row_ref = unsafe { table.get_row_ref_unchecked(blob_store, row_ptr) };\n        Ok((hash, row_ref))\n    }\n\n    // Compare `scan_rows` against a simpler implementation.\n    #[test]\n    fn table_scan_iter_eq_flatmap() {\n        let pool = PagePool::new_for_test();\n        let mut blob_store = HashMapBlobStore::default();\n        let mut table = table(AlgebraicType::U64.into());\n        for v in 0..2u64.pow(14) {\n            table.insert(&pool, &mut blob_store, &product![v]).unwrap();\n        }\n\n        let complex = table.scan_rows(&blob_store).map(|r| r.pointer());\n        let simple = table\n            .inner\n            .pages\n            .iter()\n            .zip((0..).map(PageIndex))\n            .flat_map(|(page, pi)| {\n                page.iter_fixed_len(table.row_size())\n                    .map(move |po| RowPointer::new(false, pi, po, table.squashed_offset))\n            });\n        assert!(complex.eq(simple));\n    }\n\n    #[test]\n    #[should_panic]\n    fn read_row_unaligned_page_offset_soundness() {\n        // Insert a `u64` into a table.\n        let pt = AlgebraicType::U64.into();\n        let pv = product![42u64];\n        let mut table = table(pt);\n        let pool = &PagePool::new_for_test();\n        let blob_store = &mut NullBlobStore;\n        let (_, row_ref) = table.insert(pool, blob_store, &pv).unwrap();\n\n        // Manipulate the page offset to 1 instead of 0.\n        // This now points into the \"middle\" of a row.\n        let ptr = row_ref.pointer().with_page_offset(PageOffset(1));\n\n        // We expect this to panic.\n        // Miri should not have any issue with this call either.\n        table.get_row_ref(&NullBlobStore, ptr).unwrap().to_product_value();\n    }\n\n    #[test]\n    fn test_blob_store_bytes() {\n        let pt: ProductType = [AlgebraicType::String, AlgebraicType::I32].into();\n        let pool = &PagePool::new_for_test();\n        let blob_store = &mut HashMapBlobStore::default();\n        let mut insert = |table: &mut Table, string, num| {\n            table\n                .insert(pool, blob_store, &product![string, num])\n                .unwrap()\n                .1\n                .pointer()\n        };\n        let mut table1 = table(pt.clone());\n\n        // Insert short string, `blob_store_bytes` should be 0.\n        let short_str = std::str::from_utf8(&[98; 6]).unwrap();\n        let short_row_ptr = insert(&mut table1, short_str, 0);\n        assert_eq!(table1.blob_store_bytes.0, 0);\n\n        // Insert long string, `blob_store_bytes` should be the length of the string.\n        const BLOB_OBJ_LEN: BlobNumBytes = BlobNumBytes(VarLenGranule::OBJECT_SIZE_BLOB_THRESHOLD + 1);\n        let long_str = std::str::from_utf8(&[98; BLOB_OBJ_LEN.0]).unwrap();\n        let long_row_ptr = insert(&mut table1, long_str, 0);\n        assert_eq!(table1.blob_store_bytes, BLOB_OBJ_LEN);\n\n        // Insert previous long string in the same table,\n        // `blob_store_bytes` should count the length twice,\n        // even though `HashMapBlobStore` deduplicates it.\n        let long_row_ptr2 = insert(&mut table1, long_str, 1);\n        const BLOB_OBJ_LEN_2X: BlobNumBytes = BlobNumBytes(BLOB_OBJ_LEN.0 * 2);\n        assert_eq!(table1.blob_store_bytes, BLOB_OBJ_LEN_2X);\n\n        // Insert previous long string in a new table,\n        // `blob_store_bytes` should show the length,\n        // even though `HashMapBlobStore` deduplicates it.\n        let mut table2 = table(pt);\n        let _ = insert(&mut table2, long_str, 0);\n        assert_eq!(table2.blob_store_bytes, BLOB_OBJ_LEN);\n\n        // Delete `short_str` row. This should not affect the byte count.\n        table1.delete(blob_store, short_row_ptr, |_| ()).unwrap();\n        assert_eq!(table1.blob_store_bytes, BLOB_OBJ_LEN_2X);\n\n        // Delete the first long string row. This gets us down to `BLOB_OBJ_LEN` (we had 2x before).\n        table1.delete(blob_store, long_row_ptr, |_| ()).unwrap();\n        assert_eq!(table1.blob_store_bytes, BLOB_OBJ_LEN);\n\n        // Delete the first long string row. This gets us down to 0 (we've now deleted 2x).\n        table1.delete(blob_store, long_row_ptr2, |_| ()).unwrap();\n        assert_eq!(table1.blob_store_bytes, 0.into());\n    }\n\n    /// Assert that calling `get_row_ref` to get a row ref to a non-existent `RowPointer`\n    /// does not panic.\n    #[test]\n    fn get_row_ref_no_panic() {\n        let blob_store = &mut HashMapBlobStore::default();\n        let table = table([AlgebraicType::String, AlgebraicType::I32].into());\n\n        // This row pointer has an incorrect `SquashedOffset`, and so does not point into `table`.\n        assert!(table\n            .get_row_ref(\n                blob_store,\n                RowPointer::new(false, PageIndex(0), PageOffset(0), SquashedOffset::TX_STATE),\n            )\n            .is_none());\n\n        // This row pointer has the correct `SquashedOffset`, but points out-of-bounds within `table`.\n        assert!(table\n            .get_row_ref(\n                blob_store,\n                RowPointer::new(false, PageIndex(0), PageOffset(0), SquashedOffset::COMMITTED_STATE),\n            )\n            .is_none());\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/hash_index.rs",
    "content": "use super::{\n    key_size::KeyBytesStorage,\n    same_key_entry::{same_key_iter, SameKeyEntry, SameKeyEntryIter},\n    Index, KeySize,\n};\nuse crate::indexes::RowPointer;\nuse core::hash::Hash;\nuse spacetimedb_data_structures::map::hash_map::EntryRef;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n// Faster than ahash, so we use this explicitly.\nuse foldhash::fast::RandomState;\nuse hashbrown::HashMap;\n\n/// A multi map that relates a `K` to a *set* of `RowPointer`s.\n#[derive(Debug, PartialEq, Eq)]\npub struct HashIndex<K: Eq + Hash> {\n    /// The map is backed by a `HashMap` for relating keys to values.\n    ///\n    /// A value set is stored as a `SmallVec`.\n    /// This is an optimization over a `Vec<_>`\n    /// as we allow a single element to be stored inline\n    /// to improve performance for the common case of one element.\n    map: HashMap<K, SameKeyEntry, RandomState>,\n    /// The memoized number of rows indexed in `self.map`.\n    num_rows: usize,\n    /// Storage for [`Index::num_key_bytes`].\n    num_key_bytes: u64,\n}\n\nimpl<K: Eq + Hash> Default for HashIndex<K> {\n    fn default() -> Self {\n        Self {\n            map: <_>::default(),\n            num_rows: <_>::default(),\n            num_key_bytes: <_>::default(),\n        }\n    }\n}\n\nimpl<K: MemoryUsage + Eq + Hash> MemoryUsage for HashIndex<K> {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            map,\n            num_rows,\n            num_key_bytes,\n        } = self;\n        map.heap_usage() + num_rows.heap_usage() + num_key_bytes.heap_usage()\n    }\n}\n\nimpl<K: KeySize + Eq + Hash> Index for HashIndex<K> {\n    type Key = K;\n\n    fn clone_structure(&self) -> Self {\n        <_>::default()\n    }\n\n    /// Inserts the relation `key -> ptr` to this multimap.\n    ///\n    /// The map does not check whether `key -> ptr` was already in the map.\n    /// It's assumed that the same `ptr` is never added twice,\n    /// and multimaps do not bind one `key` to the same `ptr`.\n    fn insert(&mut self, key: Self::Key, ptr: RowPointer) -> Result<(), RowPointer> {\n        self.num_rows += 1;\n        self.num_key_bytes.add_to_key_bytes::<Self>(&key);\n        self.map.entry(key).or_default().push(ptr);\n        Ok(())\n    }\n\n    /// Deletes `key -> ptr` from this multimap.\n    ///\n    /// Returns whether `key -> ptr` was present.\n    fn delete(&mut self, key: &K, ptr: RowPointer) -> bool {\n        let EntryRef::Occupied(mut entry) = self.map.entry_ref(key) else {\n            return false;\n        };\n\n        let (deleted, is_empty) = entry.get_mut().delete(ptr);\n\n        if deleted {\n            self.num_rows -= 1;\n            self.num_key_bytes.sub_from_key_bytes::<Self>(key);\n        }\n\n        if is_empty {\n            entry.remove();\n        }\n\n        deleted\n    }\n\n    type PointIter<'a>\n        = SameKeyEntryIter<'a>\n    where\n        Self: 'a;\n\n    fn seek_point(&self, key: &Self::Key) -> Self::PointIter<'_> {\n        same_key_iter(self.map.get(key))\n    }\n\n    fn num_keys(&self) -> usize {\n        self.map.len()\n    }\n\n    fn num_rows(&self) -> usize {\n        self.num_rows\n    }\n\n    /// Deletes all entries from the multimap, leaving it empty.\n    /// This will not deallocate the outer map.\n    fn clear(&mut self) {\n        self.map.clear();\n        self.num_rows = 0;\n        self.num_key_bytes.reset_to_zero();\n    }\n\n    fn can_merge(&self, _: &Self, _: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        // `self.insert` always returns `Ok(_)`.\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/index.rs",
    "content": "use crate::{indexes::RowPointer, table_index::KeySize};\nuse core::{mem, ops::RangeBounds};\n\npub trait Index {\n    /// The type of keys indexed.\n    type Key: KeySize;\n\n    // =========================================================================\n    // Construction\n    // =========================================================================\n\n    /// Clones the structure of this index but not the indexed elements,\n    /// returning an empty index.\n    fn clone_structure(&self) -> Self;\n\n    // =========================================================================\n    // Mutation\n    // =========================================================================\n\n    /// Inserts the relation `key -> ptr` to this map.\n    ///\n    /// If `key` was already present in the index,\n    /// does not add an association with val.\n    /// Returns the existing associated pointer instead.\n    ///\n    /// Returns [`Despecialize`]\n    /// if inserting `key` is not compatible with the index\n    /// or if it would be profitable to replace the index with a B-Tree.\n    /// The provided default implementation does not return `Despecialize`.\n    fn insert_maybe_despecialize(\n        &mut self,\n        key: Self::Key,\n        ptr: RowPointer,\n    ) -> Result<Result<(), RowPointer>, Despecialize> {\n        Ok(self.insert(key, ptr))\n    }\n\n    /// Inserts the relation `key -> ptr` to this map.\n    ///\n    /// If `key` was already present in the index,\n    /// does not add an association with val.\n    /// Returns the existing associated pointer instead.\n    fn insert(&mut self, key: Self::Key, ptr: RowPointer) -> Result<(), RowPointer> {\n        self.insert_maybe_despecialize(key, ptr).unwrap()\n    }\n\n    /// Deletes `key -> ptr` from this index.\n    ///\n    /// Returns whether `key -> ptr` was present.\n    ///\n    /// Implementations are free to ignore `ptr`\n    /// if there can only ever be one `key`,\n    /// as is the case for unique indices.\n    fn delete(&mut self, key: &Self::Key, ptr: RowPointer) -> bool;\n\n    /// Clears all the rows and keys from the index,\n    /// leaving it empty.\n    fn clear(&mut self);\n\n    // =========================================================================\n    // Querying\n    // =========================================================================\n\n    /// Returns whether `other` can be merged into `self`\n    /// with an error containing the element in `self` that caused the violation.\n    ///\n    /// The closure `ignore` indicates whether a row in `self` should be ignored.\n    fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer>;\n\n    /// Returns the number of keys indexed.\n    ///\n    /// This method runs in constant time.\n    fn num_keys(&self) -> usize;\n\n    /// The number of bytes stored in keys in this index.\n    ///\n    /// For non-unique indexes, duplicate keys are counted once for each row that refers to them,\n    /// even though the internal storage may deduplicate them as an optimization.\n    ///\n    /// This method runs in constant time.\n    ///\n    /// See the [`KeySize`](super::KeySize) trait for more details on how this method computes its result.\n    ///\n    /// The provided implementation assumes\n    /// that the key takes up exactly `size_of::<Self::Key>()` bytes\n    /// and has no dynamic component.\n    /// If that is not correct, you should override the implementation.\n    fn num_key_bytes(&self) -> u64 {\n        (self.num_keys() * mem::size_of::<Self::Key>()) as u64\n    }\n\n    /// Returns the number of rows indexed.\n    ///\n    /// When `self.num_keys() == 0` then `self.num_values() == 0`.\n    ///\n    /// Note that, for non-unique indexes, this may be larger than [`Index::num_keys`].\n    ///\n    /// This method runs in constant time.\n    ///\n    /// The provided implementation assumes the index is unique\n    /// and uses [`Index::num_keys`].\n    fn num_rows(&self) -> usize {\n        self.num_keys()\n    }\n\n    /// Returns whether the index has no key or values.\n    ///\n    /// When `self.is_empty()`\n    /// then `self.num_keys() == 0` and `self.num_values() == 0`.\n    ///\n    /// The provided implementation uses [`Index::num_keys`].\n    fn is_empty(&self) -> bool {\n        self.num_keys() == 0\n    }\n\n    /// The type of iterator returned by [`Index::seek_point`].\n    type PointIter<'a>: Iterator<Item = RowPointer>\n    where\n        Self: 'a;\n\n    /// Seeks `point` in this index,\n    /// returning an iterator over all the elements.\n    ///\n    /// If the index is unique, this will at most return one element.\n    fn seek_point(&self, point: &Self::Key) -> Self::PointIter<'_>;\n}\n\npub trait RangedIndex: Index {\n    /// The type of iterator returned by [`Index::seek_range`].\n    type RangeIter<'a>: Iterator<Item = RowPointer>\n    where\n        Self: 'a;\n\n    /// Seeks the `range` in this index,\n    /// returning an iterator over all the elements.\n    ///\n    /// Prefer [`Index::seek_point`] for point scans\n    /// rather than providing a point `range`\n    /// as it will be faster.\n    fn seek_range(&self, range: &impl RangeBounds<Self::Key>) -> Self::RangeIter<'_>;\n}\n\n/// An error indicating that the index should be despecialized to a B-Tree.\n#[derive(Debug)]\npub struct Despecialize;\n\n/// An error indicating that the [`Index`] is not a [`RangedIndex`].\n#[derive(Debug, PartialEq)]\npub struct IndexCannotSeekRange;\n\npub type IndexSeekRangeResult<T> = Result<T, IndexCannotSeekRange>;\n"
  },
  {
    "path": "crates/table/src/table_index/key_size.rs",
    "content": "use super::Index;\nuse core::mem;\nuse spacetimedb_memory_usage::MemoryUsage;\nuse spacetimedb_sats::{\n    algebraic_value::Packed, i256, sum_value::SumTag, u256, AlgebraicValue, ArrayValue, ProductValue, SumValue, F32,\n    F64,\n};\n\n/// Storage for memoizing `KeySize` statistics.\npub trait KeyBytesStorage: Default + MemoryUsage {\n    /// Add `key.key_size_in_bytes()` to the statistics.\n    fn add_to_key_bytes<I: Index>(&mut self, key: &I::Key);\n\n    /// Subtract `key.key_size_in_bytes()` from the statistics.\n    fn sub_from_key_bytes<I: Index>(&mut self, key: &I::Key);\n\n    /// Resets the statistics to zero.\n    fn reset_to_zero(&mut self);\n\n    /// Returns the number bytes taken up by the keys of the index.\n    fn get<I: Index>(&self, index: &I) -> u64;\n}\n\nimpl KeyBytesStorage for () {\n    fn add_to_key_bytes<I: Index>(&mut self, _: &I::Key) {}\n    fn sub_from_key_bytes<I: Index>(&mut self, _: &I::Key) {}\n    fn reset_to_zero(&mut self) {}\n    fn get<I: Index>(&self, index: &I) -> u64 {\n        index.num_keys() as u64 * mem::size_of::<I::Key>() as u64\n    }\n}\n\nimpl KeyBytesStorage for u64 {\n    fn add_to_key_bytes<I: Index>(&mut self, key: &I::Key) {\n        *self += key.key_size_in_bytes() as u64;\n    }\n    fn sub_from_key_bytes<I: Index>(&mut self, key: &I::Key) {\n        *self -= key.key_size_in_bytes() as u64;\n    }\n    fn reset_to_zero(&mut self) {\n        *self = 0;\n    }\n    fn get<I: Index>(&self, _: &I) -> u64 {\n        *self\n    }\n}\n\n/// Index keys whose memory usage we can measure and report.\n///\n/// The reported memory usage of an index is based on:\n///\n/// - the number of entries in that index, i.e. the number of `RowPointer`s it stores,\n/// - the total size of the keys for every entry in that index.\n///\n/// This trait is used to measure the latter.\n/// The metric we measure, sometimes called \"data size,\"\n/// is the number of live user-supplied bytes in the key.\n/// This excludes padding and lengths, though it does include sum tags.\n///\n/// The key size of a value is defined depending on that value's type:\n/// - Integer, float and boolean values take bytes according to their [`std::mem::size_of`].\n/// - Strings take bytes equal to their length in bytes.\n///   No overhead is counted, unlike in the BFLATN or BSATN size.\n/// - Sum values take 1 byte for the tag, plus the bytes of their active payload.\n///   Inactive variants and padding are not counted, unlike in the BFLATN size.\n/// - Product values take bytes equal to the sum of their elements' bytes.\n///   Padding is not counted, unlike in the BFLATN size.\n/// - Array values take bytes equal to the sum of their elements' bytes.\n///   As with strings, no overhead is counted.\npub trait KeySize {\n    type MemoStorage: KeyBytesStorage;\n\n    /// Returns the size of this key in bytes.\n    ///\n    /// The default implementation returns the static size of the type,\n    /// which is correct for primitives and composites of them.\n    fn key_size_in_bytes(&self) -> usize {\n        mem::size_of_val(self)\n    }\n}\n\nmacro_rules! impl_key_size_primitive {\n    ($prim:ty) => {\n        impl KeySize for $prim {\n            type MemoStorage = ();\n        }\n    };\n    ($($prim:ty,)*) => {\n        $(impl_key_size_primitive!($prim);)*\n    };\n}\n\nimpl_key_size_primitive!(\n    bool,\n    u8,\n    SumTag,\n    i8,\n    u16,\n    i16,\n    u32,\n    i32,\n    u64,\n    usize,\n    i64,\n    u128,\n    i128,\n    Packed<u128>,\n    Packed<i128>,\n    u256,\n    i256,\n    F32,\n    F64,\n);\n\nimpl KeySize for Box<str> {\n    type MemoStorage = u64;\n    fn key_size_in_bytes(&self) -> usize {\n        self.len()\n    }\n}\n\nimpl KeySize for AlgebraicValue {\n    type MemoStorage = u64;\n    fn key_size_in_bytes(&self) -> usize {\n        match self {\n            AlgebraicValue::Bool(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U8(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I8(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U16(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I16(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U32(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I32(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U64(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I64(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U128(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I128(x) => x.key_size_in_bytes(),\n            AlgebraicValue::U256(x) => x.key_size_in_bytes(),\n            AlgebraicValue::I256(x) => x.key_size_in_bytes(),\n            AlgebraicValue::F32(x) => x.key_size_in_bytes(),\n            AlgebraicValue::F64(x) => x.key_size_in_bytes(),\n            AlgebraicValue::String(x) => x.key_size_in_bytes(),\n            AlgebraicValue::Sum(x) => x.key_size_in_bytes(),\n            AlgebraicValue::Product(x) => x.key_size_in_bytes(),\n            AlgebraicValue::Array(x) => x.key_size_in_bytes(),\n\n            AlgebraicValue::Min | AlgebraicValue::Max => unreachable!(),\n        }\n    }\n}\n\nimpl KeySize for SumValue {\n    type MemoStorage = u64;\n    fn key_size_in_bytes(&self) -> usize {\n        1 + self.value.key_size_in_bytes()\n    }\n}\n\nimpl KeySize for ProductValue {\n    type MemoStorage = u64;\n    fn key_size_in_bytes(&self) -> usize {\n        self.elements.key_size_in_bytes()\n    }\n}\n\nimpl<K> KeySize for [K]\nwhere\n    K: KeySize,\n{\n    type MemoStorage = u64;\n\n    // TODO(perf, bikeshedding): check that this optimized to `size_of::<K>() * self.len()`\n    // when `K` is a primitive.\n    fn key_size_in_bytes(&self) -> usize {\n        self.iter().map(|elt| elt.key_size_in_bytes()).sum()\n    }\n}\n\nimpl KeySize for ArrayValue {\n    type MemoStorage = u64;\n\n    fn key_size_in_bytes(&self) -> usize {\n        match self {\n            ArrayValue::Sum(elts) => elts.key_size_in_bytes(),\n            ArrayValue::Product(elts) => elts.key_size_in_bytes(),\n            ArrayValue::Bool(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I8(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U8(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I16(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U16(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I32(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U32(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I64(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U64(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I128(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U128(elts) => elts.key_size_in_bytes(),\n            ArrayValue::I256(elts) => elts.key_size_in_bytes(),\n            ArrayValue::U256(elts) => elts.key_size_in_bytes(),\n            ArrayValue::F32(elts) => elts.key_size_in_bytes(),\n            ArrayValue::F64(elts) => elts.key_size_in_bytes(),\n            ArrayValue::String(elts) => elts.key_size_in_bytes(),\n            ArrayValue::Array(elts) => elts.key_size_in_bytes(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/mod.rs",
    "content": "//! Table indexes with specialized key types.\n//!\n//! Indexes could be implemented as `MultiMap<AlgebraicValue, RowPointer>` (and once were),\n//! but that results in wasted memory and spurious comparisons and branches\n//! because the keys must always be homogeneous at a more specific type than `AlgebraicValue`.\n//!\n//! As an optimization, we hoist the enum out of the keys to the index itself.\n//! This is a sizeable improvement for integer keys,\n//! as e.g. `u64::cmp` is much faster than `AlgebraicValue::cmp`.\n//!\n//! This results in some pretty ugly code, where types that would be structs\n//! are instead enums with similar-looking variants for each specialized key type,\n//! and methods that interact with those enums have matches with similar-looking arms.\n//! Some day we may devise a better solution, but this is good enough for now.\n//\n// I (pgoldman 2024-02-05) suspect, but have not measured, that there's no real reason\n// to have a `ProductType` variant, which would apply to multi-column indexes.\n// I believe `ProductValue::cmp` to not be meaningfully faster than `AlgebraicValue::cmp`.\n// Eventually, we will likely want to compile comparison functions and representations\n// for `ProductValue`-keyed indexes which take advantage of type information,\n// since we know when creating the index the number and type of all the indexed columns.\n// This may involve a bytecode compiler, a tree of closures, or a native JIT.\n///\n/// We also represent unique indices more compactly than non-unique ones, avoiding the multi-map.\n/// Additionally, beyond our btree indices,\n/// we support direct unique indices, where key are indices into `Vec`s.\nuse self::hash_index::HashIndex;\nuse self::same_key_entry::SameKeyEntryIter;\nuse self::unique_direct_fixed_cap_index::{UniqueDirectFixedCapIndex, UniqueDirectFixedCapIndexRangeIter};\nuse self::unique_direct_index::{UniqueDirectIndex, UniqueDirectIndexRangeIter};\nuse self::unique_hash_index::UniqueHashIndex;\nuse super::indexes::RowPointer;\nuse super::table::RowRef;\nuse crate::table_index::index::Despecialize;\nuse crate::table_index::unique_direct_index::ToFromUsize;\nuse crate::{read_column::ReadColumn, static_assert_size};\nuse core::fmt;\nuse core::ops::RangeBounds;\nuse spacetimedb_primitives::ColList;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_sats::{\n    algebraic_value::Packed, i256, product_value::InvalidFieldError, sum_value::SumTag, u256, AlgebraicType,\n    AlgebraicValue, ProductType, F32, F64,\n};\nuse spacetimedb_schema::def::IndexAlgorithm;\n\nmod hash_index;\nmod index;\nmod key_size;\nmod multimap;\nmod same_key_entry;\npub mod unique_direct_fixed_cap_index;\npub mod unique_direct_index;\nmod unique_hash_index;\npub mod uniquemap;\n\npub use self::index::{Index, IndexCannotSeekRange, IndexSeekRangeResult, RangedIndex};\npub use self::key_size::KeySize;\n\ntype BtreeIndex<K> = multimap::MultiMap<K>;\ntype BtreeIndexRangeIter<'a, K> = multimap::MultiMapRangeIter<'a, K>;\ntype BtreeUniqueIndex<K> = uniquemap::UniqueMap<K>;\ntype BtreeUniqueIndexRangeIter<'a, K> = uniquemap::UniqueMapRangeIter<'a, K>;\n\n/// A point iterator over a [`TypedIndex`], with a specialized key type.\n///\n/// See module docs for info about specialization.\nenum TypedIndexPointIter<'a> {\n    NonUnique(SameKeyEntryIter<'a>),\n    Unique(uniquemap::UniquePointIter),\n}\n\nimpl Iterator for TypedIndexPointIter<'_> {\n    type Item = RowPointer;\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            Self::NonUnique(this) => this.next(),\n            Self::Unique(this) => this.next(),\n        }\n    }\n}\n\n/// An iterator over rows matching a certain [`AlgebraicValue`] on the [`TableIndex`].\npub struct TableIndexPointIter<'a> {\n    /// The iterator seeking for matching values.\n    iter: TypedIndexPointIter<'a>,\n}\n\nimpl Iterator for TableIndexPointIter<'_> {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next()\n    }\n}\n\n/// A ranged iterator over a [`TypedIndex`], with a specialized key type.\n///\n/// See module docs for info about specialization.\n#[derive(Clone)]\nenum TypedIndexRangeIter<'a> {\n    /// The range itself provided was empty.\n    RangeEmpty,\n\n    // All the non-unique btree index iterators.\n    BtreeBool(BtreeIndexRangeIter<'a, bool>),\n    BtreeU8(BtreeIndexRangeIter<'a, u8>),\n    BtreeSumTag(BtreeIndexRangeIter<'a, SumTag>),\n    BtreeI8(BtreeIndexRangeIter<'a, i8>),\n    BtreeU16(BtreeIndexRangeIter<'a, u16>),\n    BtreeI16(BtreeIndexRangeIter<'a, i16>),\n    BtreeU32(BtreeIndexRangeIter<'a, u32>),\n    BtreeI32(BtreeIndexRangeIter<'a, i32>),\n    BtreeU64(BtreeIndexRangeIter<'a, u64>),\n    BtreeI64(BtreeIndexRangeIter<'a, i64>),\n    BtreeU128(BtreeIndexRangeIter<'a, Packed<u128>>),\n    BtreeI128(BtreeIndexRangeIter<'a, Packed<i128>>),\n    BtreeU256(BtreeIndexRangeIter<'a, u256>),\n    BtreeI256(BtreeIndexRangeIter<'a, i256>),\n    BtreeF32(BtreeIndexRangeIter<'a, F32>),\n    BtreeF64(BtreeIndexRangeIter<'a, F64>),\n    BtreeString(BtreeIndexRangeIter<'a, Box<str>>),\n    BtreeAV(BtreeIndexRangeIter<'a, AlgebraicValue>),\n\n    // All the unique btree index iterators.\n    UniqueBtreeBool(BtreeUniqueIndexRangeIter<'a, bool>),\n    UniqueBtreeU8(BtreeUniqueIndexRangeIter<'a, u8>),\n    UniqueBtreeSumTag(BtreeUniqueIndexRangeIter<'a, SumTag>),\n    UniqueBtreeI8(BtreeUniqueIndexRangeIter<'a, i8>),\n    UniqueBtreeU16(BtreeUniqueIndexRangeIter<'a, u16>),\n    UniqueBtreeI16(BtreeUniqueIndexRangeIter<'a, i16>),\n    UniqueBtreeU32(BtreeUniqueIndexRangeIter<'a, u32>),\n    UniqueBtreeI32(BtreeUniqueIndexRangeIter<'a, i32>),\n    UniqueBtreeU64(BtreeUniqueIndexRangeIter<'a, u64>),\n    UniqueBtreeI64(BtreeUniqueIndexRangeIter<'a, i64>),\n    UniqueBtreeU128(BtreeUniqueIndexRangeIter<'a, Packed<u128>>),\n    UniqueBtreeI128(BtreeUniqueIndexRangeIter<'a, Packed<i128>>),\n    UniqueBtreeU256(BtreeUniqueIndexRangeIter<'a, u256>),\n    UniqueBtreeI256(BtreeUniqueIndexRangeIter<'a, i256>),\n    UniqueBtreeF32(BtreeUniqueIndexRangeIter<'a, F32>),\n    UniqueBtreeF64(BtreeUniqueIndexRangeIter<'a, F64>),\n    UniqueBtreeString(BtreeUniqueIndexRangeIter<'a, Box<str>>),\n    UniqueBtreeAV(BtreeUniqueIndexRangeIter<'a, AlgebraicValue>),\n\n    UniqueDirect(UniqueDirectIndexRangeIter<'a>),\n    UniqueDirectU8(UniqueDirectFixedCapIndexRangeIter<'a>),\n}\n\nimpl Iterator for TypedIndexRangeIter<'_> {\n    type Item = RowPointer;\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            Self::RangeEmpty => None,\n\n            Self::BtreeBool(this) => this.next(),\n            Self::BtreeU8(this) => this.next(),\n            Self::BtreeSumTag(this) => this.next(),\n            Self::BtreeI8(this) => this.next(),\n            Self::BtreeU16(this) => this.next(),\n            Self::BtreeI16(this) => this.next(),\n            Self::BtreeU32(this) => this.next(),\n            Self::BtreeI32(this) => this.next(),\n            Self::BtreeU64(this) => this.next(),\n            Self::BtreeI64(this) => this.next(),\n            Self::BtreeU128(this) => this.next(),\n            Self::BtreeI128(this) => this.next(),\n            Self::BtreeU256(this) => this.next(),\n            Self::BtreeI256(this) => this.next(),\n            Self::BtreeF32(this) => this.next(),\n            Self::BtreeF64(this) => this.next(),\n            Self::BtreeString(this) => this.next(),\n            Self::BtreeAV(this) => this.next(),\n\n            Self::UniqueBtreeBool(this) => this.next(),\n            Self::UniqueBtreeU8(this) => this.next(),\n            Self::UniqueBtreeSumTag(this) => this.next(),\n            Self::UniqueBtreeI8(this) => this.next(),\n            Self::UniqueBtreeU16(this) => this.next(),\n            Self::UniqueBtreeI16(this) => this.next(),\n            Self::UniqueBtreeU32(this) => this.next(),\n            Self::UniqueBtreeI32(this) => this.next(),\n            Self::UniqueBtreeU64(this) => this.next(),\n            Self::UniqueBtreeI64(this) => this.next(),\n            Self::UniqueBtreeU128(this) => this.next(),\n            Self::UniqueBtreeI128(this) => this.next(),\n            Self::UniqueBtreeU256(this) => this.next(),\n            Self::UniqueBtreeI256(this) => this.next(),\n            Self::UniqueBtreeF32(this) => this.next(),\n            Self::UniqueBtreeF64(this) => this.next(),\n            Self::UniqueBtreeString(this) => this.next(),\n            Self::UniqueBtreeAV(this) => this.next(),\n\n            Self::UniqueDirect(this) => this.next(),\n            Self::UniqueDirectU8(this) => this.next(),\n        }\n    }\n}\n\n/// An iterator over rows matching a range of [`AlgebraicValue`]s on the [`TableIndex`].\n#[derive(Clone)]\npub struct TableIndexRangeIter<'a> {\n    /// The iterator seeking for matching values.\n    iter: TypedIndexRangeIter<'a>,\n}\n\nimpl Iterator for TableIndexRangeIter<'_> {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next()\n    }\n}\n\nimpl fmt::Debug for TableIndexRangeIter<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let iter = self.clone();\n        f.debug_list().entries(iter).finish()\n    }\n}\n\n/// An index from a key type determined at runtime to `RowPointer`(s).\n///\n/// See module docs for info about specialization.\n#[derive(Debug, PartialEq, Eq, derive_more::From)]\nenum TypedIndex {\n    // All the non-unique btree index types.\n    BtreeBool(BtreeIndex<bool>),\n    BtreeU8(BtreeIndex<u8>),\n    BtreeSumTag(BtreeIndex<SumTag>),\n    BtreeI8(BtreeIndex<i8>),\n    BtreeU16(BtreeIndex<u16>),\n    BtreeI16(BtreeIndex<i16>),\n    BtreeU32(BtreeIndex<u32>),\n    BtreeI32(BtreeIndex<i32>),\n    BtreeU64(BtreeIndex<u64>),\n    BtreeI64(BtreeIndex<i64>),\n    BtreeU128(BtreeIndex<Packed<u128>>),\n    BtreeI128(BtreeIndex<Packed<i128>>),\n    BtreeU256(BtreeIndex<u256>),\n    BtreeI256(BtreeIndex<i256>),\n    BtreeF32(BtreeIndex<F32>),\n    BtreeF64(BtreeIndex<F64>),\n    BtreeString(BtreeIndex<Box<str>>),\n    BtreeAV(BtreeIndex<AlgebraicValue>),\n\n    // All the non-unique hash index types.\n    HashBool(HashIndex<bool>),\n    HashU8(HashIndex<u8>),\n    HashSumTag(HashIndex<SumTag>),\n    HashI8(HashIndex<i8>),\n    HashU16(HashIndex<u16>),\n    HashI16(HashIndex<i16>),\n    HashU32(HashIndex<u32>),\n    HashI32(HashIndex<i32>),\n    HashU64(HashIndex<u64>),\n    HashI64(HashIndex<i64>),\n    HashU128(HashIndex<Packed<u128>>),\n    HashI128(HashIndex<Packed<i128>>),\n    HashU256(HashIndex<u256>),\n    HashI256(HashIndex<i256>),\n    HashF32(HashIndex<F32>),\n    HashF64(HashIndex<F64>),\n    HashString(HashIndex<Box<str>>),\n    HashAV(HashIndex<AlgebraicValue>),\n\n    // All the unique btree index types.\n    UniqueBtreeBool(BtreeUniqueIndex<bool>),\n    UniqueBtreeU8(BtreeUniqueIndex<u8>),\n    UniqueBtreeSumTag(BtreeUniqueIndex<SumTag>),\n    UniqueBtreeI8(BtreeUniqueIndex<i8>),\n    UniqueBtreeU16(BtreeUniqueIndex<u16>),\n    UniqueBtreeI16(BtreeUniqueIndex<i16>),\n    UniqueBtreeU32(BtreeUniqueIndex<u32>),\n    UniqueBtreeI32(BtreeUniqueIndex<i32>),\n    UniqueBtreeU64(BtreeUniqueIndex<u64>),\n    UniqueBtreeI64(BtreeUniqueIndex<i64>),\n    UniqueBtreeU128(BtreeUniqueIndex<Packed<u128>>),\n    UniqueBtreeI128(BtreeUniqueIndex<Packed<i128>>),\n    UniqueBtreeU256(BtreeUniqueIndex<u256>),\n    UniqueBtreeI256(BtreeUniqueIndex<i256>),\n    UniqueBtreeF32(BtreeUniqueIndex<F32>),\n    UniqueBtreeF64(BtreeUniqueIndex<F64>),\n    UniqueBtreeString(BtreeUniqueIndex<Box<str>>),\n    UniqueBtreeAV(BtreeUniqueIndex<AlgebraicValue>),\n\n    // All the unique hash index types.\n    UniqueHashBool(UniqueHashIndex<bool>),\n    UniqueHashU8(UniqueHashIndex<u8>),\n    UniqueHashSumTag(UniqueHashIndex<SumTag>),\n    UniqueHashI8(UniqueHashIndex<i8>),\n    UniqueHashU16(UniqueHashIndex<u16>),\n    UniqueHashI16(UniqueHashIndex<i16>),\n    UniqueHashU32(UniqueHashIndex<u32>),\n    UniqueHashI32(UniqueHashIndex<i32>),\n    UniqueHashU64(UniqueHashIndex<u64>),\n    UniqueHashI64(UniqueHashIndex<i64>),\n    UniqueHashU128(UniqueHashIndex<Packed<u128>>),\n    UniqueHashI128(UniqueHashIndex<Packed<i128>>),\n    UniqueHashU256(UniqueHashIndex<u256>),\n    UniqueHashI256(UniqueHashIndex<i256>),\n    UniqueHashF32(UniqueHashIndex<F32>),\n    UniqueHashF64(UniqueHashIndex<F64>),\n    UniqueHashString(UniqueHashIndex<Box<str>>),\n    UniqueHashAV(UniqueHashIndex<AlgebraicValue>),\n\n    // All the unique direct index types.\n    UniqueDirectU8(UniqueDirectIndex<u8>),\n    UniqueDirectSumTag(UniqueDirectFixedCapIndex<SumTag>),\n    UniqueDirectU16(UniqueDirectIndex<u16>),\n    UniqueDirectU32(UniqueDirectIndex<u32>),\n    UniqueDirectU64(UniqueDirectIndex<u64>),\n}\n\nstatic_assert_size!(TypedIndex, 64);\n\nmacro_rules! same_for_all_types {\n    ($scrutinee:expr, $this:ident => $body:expr) => {\n        match $scrutinee {\n            Self::BtreeBool($this) => $body,\n            Self::BtreeU8($this) => $body,\n            Self::BtreeSumTag($this) => $body,\n            Self::BtreeI8($this) => $body,\n            Self::BtreeU16($this) => $body,\n            Self::BtreeI16($this) => $body,\n            Self::BtreeU32($this) => $body,\n            Self::BtreeI32($this) => $body,\n            Self::BtreeU64($this) => $body,\n            Self::BtreeI64($this) => $body,\n            Self::BtreeU128($this) => $body,\n            Self::BtreeI128($this) => $body,\n            Self::BtreeU256($this) => $body,\n            Self::BtreeI256($this) => $body,\n            Self::BtreeF32($this) => $body,\n            Self::BtreeF64($this) => $body,\n            Self::BtreeString($this) => $body,\n            Self::BtreeAV($this) => $body,\n\n            Self::HashBool($this) => $body,\n            Self::HashU8($this) => $body,\n            Self::HashSumTag($this) => $body,\n            Self::HashI8($this) => $body,\n            Self::HashU16($this) => $body,\n            Self::HashI16($this) => $body,\n            Self::HashU32($this) => $body,\n            Self::HashI32($this) => $body,\n            Self::HashU64($this) => $body,\n            Self::HashI64($this) => $body,\n            Self::HashU128($this) => $body,\n            Self::HashI128($this) => $body,\n            Self::HashU256($this) => $body,\n            Self::HashI256($this) => $body,\n            Self::HashF32($this) => $body,\n            Self::HashF64($this) => $body,\n            Self::HashString($this) => $body,\n            Self::HashAV($this) => $body,\n\n            Self::UniqueBtreeBool($this) => $body,\n            Self::UniqueBtreeU8($this) => $body,\n            Self::UniqueBtreeSumTag($this) => $body,\n            Self::UniqueBtreeI8($this) => $body,\n            Self::UniqueBtreeU16($this) => $body,\n            Self::UniqueBtreeI16($this) => $body,\n            Self::UniqueBtreeU32($this) => $body,\n            Self::UniqueBtreeI32($this) => $body,\n            Self::UniqueBtreeU64($this) => $body,\n            Self::UniqueBtreeI64($this) => $body,\n            Self::UniqueBtreeU128($this) => $body,\n            Self::UniqueBtreeI128($this) => $body,\n            Self::UniqueBtreeU256($this) => $body,\n            Self::UniqueBtreeI256($this) => $body,\n            Self::UniqueBtreeF32($this) => $body,\n            Self::UniqueBtreeF64($this) => $body,\n            Self::UniqueBtreeString($this) => $body,\n            Self::UniqueBtreeAV($this) => $body,\n\n            Self::UniqueHashBool($this) => $body,\n            Self::UniqueHashU8($this) => $body,\n            Self::UniqueHashSumTag($this) => $body,\n            Self::UniqueHashI8($this) => $body,\n            Self::UniqueHashU16($this) => $body,\n            Self::UniqueHashI16($this) => $body,\n            Self::UniqueHashU32($this) => $body,\n            Self::UniqueHashI32($this) => $body,\n            Self::UniqueHashU64($this) => $body,\n            Self::UniqueHashI64($this) => $body,\n            Self::UniqueHashU128($this) => $body,\n            Self::UniqueHashI128($this) => $body,\n            Self::UniqueHashU256($this) => $body,\n            Self::UniqueHashI256($this) => $body,\n            Self::UniqueHashF32($this) => $body,\n            Self::UniqueHashF64($this) => $body,\n            Self::UniqueHashString($this) => $body,\n            Self::UniqueHashAV($this) => $body,\n\n            Self::UniqueDirectSumTag($this) => $body,\n            Self::UniqueDirectU8($this) => $body,\n            Self::UniqueDirectU16($this) => $body,\n            Self::UniqueDirectU32($this) => $body,\n            Self::UniqueDirectU64($this) => $body,\n        }\n    };\n}\n\nimpl MemoryUsage for TypedIndex {\n    fn heap_usage(&self) -> usize {\n        same_for_all_types!(self, this => this.heap_usage())\n    }\n}\n\nfn as_tag(av: &AlgebraicValue) -> Option<&u8> {\n    av.as_sum().map(|s| &s.tag)\n}\n\nfn as_sum_tag(av: &AlgebraicValue) -> Option<&SumTag> {\n    as_tag(av).map(|s| s.into())\n}\n\n#[derive(Debug, PartialEq)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\npub enum IndexKind {\n    BTree,\n    Hash,\n    Direct,\n}\n\nimpl IndexKind {\n    pub(crate) fn from_algo(algo: &IndexAlgorithm) -> Self {\n        match algo {\n            IndexAlgorithm::BTree(_) => Self::BTree,\n            IndexAlgorithm::Hash(_) => Self::Hash,\n            IndexAlgorithm::Direct(_) => Self::Direct,\n            // This is due to `#[non_exhaustive]`.\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl TypedIndex {\n    /// Returns a new index with keys being of `key_type` and the index possibly `is_unique`.\n    fn new(key_type: &AlgebraicType, kind: IndexKind, is_unique: bool) -> Self {\n        match kind {\n            IndexKind::BTree => Self::new_btree_index(key_type, is_unique),\n            IndexKind::Hash => Self::new_hash_index(key_type, is_unique),\n            IndexKind::Direct => Self::new_direct_index(key_type, is_unique)\n                .unwrap_or_else(|| Self::new_btree_index(key_type, is_unique)),\n        }\n    }\n\n    /// Returns a new direct index with key being of `key_type`.\n    ///\n    /// If the parameters passed are not compatible with a direct index,\n    /// `None` is returned.\n    fn new_direct_index(key_type: &AlgebraicType, is_unique: bool) -> Option<Self> {\n        if !is_unique {\n            return None;\n        }\n\n        use TypedIndex::*;\n        Some(match key_type {\n            AlgebraicType::U8 => UniqueDirectU8(<_>::default()),\n            AlgebraicType::U16 => UniqueDirectU16(<_>::default()),\n            AlgebraicType::U32 => UniqueDirectU32(<_>::default()),\n            AlgebraicType::U64 => UniqueDirectU64(<_>::default()),\n            // For a plain enum, use `u8` as the native type.\n            AlgebraicType::Sum(sum) if sum.is_simple_enum() => {\n                UniqueDirectSumTag(UniqueDirectFixedCapIndex::new(sum.variants.len()))\n            }\n            _ => return None,\n        })\n    }\n\n    /// Returns a new btree index with key being of `key_type`.\n    fn new_btree_index(key_type: &AlgebraicType, is_unique: bool) -> Self {\n        use TypedIndex::*;\n\n        // If the index is on a single column of a primitive type, string, or plain enum,\n        // use a homogeneous map with a native key type.\n        if is_unique {\n            match key_type {\n                AlgebraicType::Bool => UniqueBtreeBool(<_>::default()),\n                AlgebraicType::I8 => UniqueBtreeI8(<_>::default()),\n                AlgebraicType::U8 => UniqueBtreeU8(<_>::default()),\n                AlgebraicType::I16 => UniqueBtreeI16(<_>::default()),\n                AlgebraicType::U16 => UniqueBtreeU16(<_>::default()),\n                AlgebraicType::I32 => UniqueBtreeI32(<_>::default()),\n                AlgebraicType::U32 => UniqueBtreeU32(<_>::default()),\n                AlgebraicType::I64 => UniqueBtreeI64(<_>::default()),\n                AlgebraicType::U64 => UniqueBtreeU64(<_>::default()),\n                AlgebraicType::I128 => UniqueBtreeI128(<_>::default()),\n                AlgebraicType::U128 => UniqueBtreeU128(<_>::default()),\n                AlgebraicType::I256 => UniqueBtreeI256(<_>::default()),\n                AlgebraicType::U256 => UniqueBtreeU256(<_>::default()),\n                AlgebraicType::F32 => UniqueBtreeF32(<_>::default()),\n                AlgebraicType::F64 => UniqueBtreeF64(<_>::default()),\n                AlgebraicType::String => UniqueBtreeString(<_>::default()),\n                // For a plain enum, use `u8` as the native type.\n                // We use a direct index here\n                AlgebraicType::Sum(sum) if sum.is_simple_enum() => UniqueBtreeSumTag(<_>::default()),\n\n                // The index is either multi-column,\n                // or we don't care to specialize on the key type,\n                // so use a map keyed on `AlgebraicValue`.\n                _ => UniqueBtreeAV(<_>::default()),\n            }\n        } else {\n            match key_type {\n                AlgebraicType::Bool => BtreeBool(<_>::default()),\n                AlgebraicType::I8 => BtreeI8(<_>::default()),\n                AlgebraicType::U8 => BtreeU8(<_>::default()),\n                AlgebraicType::I16 => BtreeI16(<_>::default()),\n                AlgebraicType::U16 => BtreeU16(<_>::default()),\n                AlgebraicType::I32 => BtreeI32(<_>::default()),\n                AlgebraicType::U32 => BtreeU32(<_>::default()),\n                AlgebraicType::I64 => BtreeI64(<_>::default()),\n                AlgebraicType::U64 => BtreeU64(<_>::default()),\n                AlgebraicType::I128 => BtreeI128(<_>::default()),\n                AlgebraicType::U128 => BtreeU128(<_>::default()),\n                AlgebraicType::I256 => BtreeI256(<_>::default()),\n                AlgebraicType::U256 => BtreeU256(<_>::default()),\n                AlgebraicType::F32 => BtreeF32(<_>::default()),\n                AlgebraicType::F64 => BtreeF64(<_>::default()),\n                AlgebraicType::String => BtreeString(<_>::default()),\n\n                // For a plain enum, use `u8` as the native type.\n                AlgebraicType::Sum(sum) if sum.is_simple_enum() => BtreeSumTag(<_>::default()),\n\n                // The index is either multi-column,\n                // or we don't care to specialize on the key type,\n                // so use a map keyed on `AlgebraicValue`.\n                _ => BtreeAV(<_>::default()),\n            }\n        }\n    }\n\n    /// Returns a new hash index with key being of `key_type`.\n    fn new_hash_index(key_type: &AlgebraicType, is_unique: bool) -> Self {\n        use TypedIndex::*;\n\n        // If the index is on a single column of a primitive type, string, or plain enum,\n        // use a homogeneous map with a native key type.\n        if is_unique {\n            match key_type {\n                AlgebraicType::Bool => UniqueHashBool(<_>::default()),\n                AlgebraicType::I8 => UniqueHashI8(<_>::default()),\n                AlgebraicType::U8 => UniqueHashU8(<_>::default()),\n                AlgebraicType::I16 => UniqueHashI16(<_>::default()),\n                AlgebraicType::U16 => UniqueHashU16(<_>::default()),\n                AlgebraicType::I32 => UniqueHashI32(<_>::default()),\n                AlgebraicType::U32 => UniqueHashU32(<_>::default()),\n                AlgebraicType::I64 => UniqueHashI64(<_>::default()),\n                AlgebraicType::U64 => UniqueHashU64(<_>::default()),\n                AlgebraicType::I128 => UniqueHashI128(<_>::default()),\n                AlgebraicType::U128 => UniqueHashU128(<_>::default()),\n                AlgebraicType::I256 => UniqueHashI256(<_>::default()),\n                AlgebraicType::U256 => UniqueHashU256(<_>::default()),\n                AlgebraicType::F32 => UniqueHashF32(<_>::default()),\n                AlgebraicType::F64 => UniqueHashF64(<_>::default()),\n                AlgebraicType::String => UniqueHashString(<_>::default()),\n                // For a plain enum, use `u8` as the native type.\n                // We use a direct index here\n                AlgebraicType::Sum(sum) if sum.is_simple_enum() => UniqueHashSumTag(<_>::default()),\n\n                // The index is either multi-column,\n                // or we don't care to specialize on the key type,\n                // so use a map keyed on `AlgebraicValue`.\n                _ => UniqueHashAV(<_>::default()),\n            }\n        } else {\n            match key_type {\n                AlgebraicType::Bool => HashBool(<_>::default()),\n                AlgebraicType::I8 => HashI8(<_>::default()),\n                AlgebraicType::U8 => HashU8(<_>::default()),\n                AlgebraicType::I16 => HashI16(<_>::default()),\n                AlgebraicType::U16 => HashU16(<_>::default()),\n                AlgebraicType::I32 => HashI32(<_>::default()),\n                AlgebraicType::U32 => HashU32(<_>::default()),\n                AlgebraicType::I64 => HashI64(<_>::default()),\n                AlgebraicType::U64 => HashU64(<_>::default()),\n                AlgebraicType::I128 => HashI128(<_>::default()),\n                AlgebraicType::U128 => HashU128(<_>::default()),\n                AlgebraicType::I256 => HashI256(<_>::default()),\n                AlgebraicType::U256 => HashU256(<_>::default()),\n                AlgebraicType::F32 => HashF32(<_>::default()),\n                AlgebraicType::F64 => HashF64(<_>::default()),\n                AlgebraicType::String => HashString(<_>::default()),\n\n                // For a plain enum, use `u8` as the native type.\n                AlgebraicType::Sum(sum) if sum.is_simple_enum() => HashSumTag(<_>::default()),\n\n                // The index is either multi-column,\n                // or we don't care to specialize on the key type,\n                // so use a map keyed on `AlgebraicValue`.\n                _ => HashAV(<_>::default()),\n            }\n        }\n    }\n\n    /// Clones the structure of this index but not the indexed elements,\n    /// so the returned index is empty.\n    fn clone_structure(&self) -> Self {\n        same_for_all_types!(self, this => this.clone_structure().into())\n    }\n\n    /// Returns whether this is a unique index or not.\n    fn is_unique(&self) -> bool {\n        use TypedIndex::*;\n        match self {\n            BtreeBool(_) | BtreeU8(_) | BtreeSumTag(_) | BtreeI8(_) | BtreeU16(_) | BtreeI16(_) | BtreeU32(_)\n            | BtreeI32(_) | BtreeU64(_) | BtreeI64(_) | BtreeU128(_) | BtreeI128(_) | BtreeU256(_) | BtreeI256(_)\n            | BtreeF32(_) | BtreeF64(_) | BtreeString(_) | BtreeAV(_) | HashBool(_) | HashU8(_) | HashSumTag(_)\n            | HashI8(_) | HashU16(_) | HashI16(_) | HashU32(_) | HashI32(_) | HashU64(_) | HashI64(_) | HashU128(_)\n            | HashI128(_) | HashU256(_) | HashI256(_) | HashF32(_) | HashF64(_) | HashString(_) | HashAV(_) => false,\n            UniqueBtreeBool(_)\n            | UniqueBtreeU8(_)\n            | UniqueBtreeSumTag(_)\n            | UniqueBtreeI8(_)\n            | UniqueBtreeU16(_)\n            | UniqueBtreeI16(_)\n            | UniqueBtreeU32(_)\n            | UniqueBtreeI32(_)\n            | UniqueBtreeU64(_)\n            | UniqueBtreeI64(_)\n            | UniqueBtreeU128(_)\n            | UniqueBtreeI128(_)\n            | UniqueBtreeU256(_)\n            | UniqueBtreeI256(_)\n            | UniqueBtreeF32(_)\n            | UniqueBtreeF64(_)\n            | UniqueBtreeString(_)\n            | UniqueBtreeAV(_)\n            | UniqueHashBool(_)\n            | UniqueHashU8(_)\n            | UniqueHashSumTag(_)\n            | UniqueHashI8(_)\n            | UniqueHashU16(_)\n            | UniqueHashI16(_)\n            | UniqueHashU32(_)\n            | UniqueHashI32(_)\n            | UniqueHashU64(_)\n            | UniqueHashI64(_)\n            | UniqueHashU128(_)\n            | UniqueHashI128(_)\n            | UniqueHashU256(_)\n            | UniqueHashI256(_)\n            | UniqueHashF32(_)\n            | UniqueHashF64(_)\n            | UniqueHashString(_)\n            | UniqueHashAV(_)\n            | UniqueDirectU8(_)\n            | UniqueDirectSumTag(_)\n            | UniqueDirectU16(_)\n            | UniqueDirectU32(_)\n            | UniqueDirectU64(_) => true,\n        }\n    }\n\n    /// Add the row referred to by `row_ref` to the index `self`,\n    /// which must be keyed at `cols`.\n    ///\n    /// Returns `Errs(existing_row)` if this index was a unique index that was violated.\n    /// The index is not inserted to in that case.\n    ///\n    /// # Safety\n    ///\n    /// 1. Caller promises that `cols` matches what was given at construction (`Self::new`).\n    /// 2. Caller promises that the projection of `row_ref`'s type's equals the index's key type.\n    unsafe fn insert(&mut self, cols: &ColList, row_ref: RowRef<'_>) -> Result<(), RowPointer> {\n        fn project_to_singleton_key<T: ReadColumn>(cols: &ColList, row_ref: RowRef<'_>) -> T {\n            // Extract the column.\n            let col_pos = cols.as_singleton();\n            // SAFETY: Caller promised that `cols` matches what was given at construction (`Self::new`).\n            // In the case of `.clone_structure()`, the structure is preserved,\n            // so the promise is also preserved.\n            // This entails, that because we reached here, that `cols` is singleton.\n            let col_pos = unsafe { col_pos.unwrap_unchecked() }.idx();\n\n            // Extract the layout of the column.\n            let col_layouts = &row_ref.row_layout().product().elements;\n            // SAFETY:\n            // - Caller promised that projecting the `row_ref`'s type/layout to `self.indexed_columns`\n            //   gives us the index's key type.\n            //   This entails that each `ColId` in `self.indexed_columns`\n            //   must be in-bounds of `row_ref`'s layout.\n            let col_layout = unsafe { col_layouts.get_unchecked(col_pos) };\n\n            // Read the column in `row_ref`.\n            // SAFETY:\n            // - `col_layout` was just derived from the row layout.\n            // - Caller promised that the type-projection of the row type/layout\n            //   equals the index's key type.\n            //   We've reached here, so the index's key type is compatible with `T`.\n            // - `self` is a valid row so offsetting to `col_layout` is valid.\n            unsafe { T::unchecked_read_column(row_ref, col_layout) }\n        }\n\n        fn insert_at_type(\n            this: &mut impl Index<Key: ReadColumn>,\n            cols: &ColList,\n            row_ref: RowRef<'_>,\n        ) -> (Result<(), RowPointer>, Option<TypedIndex>) {\n            let key = project_to_singleton_key(cols, row_ref);\n            let res = this.insert(key, row_ref.pointer());\n            (res, None)\n        }\n\n        /// Avoid inlining the closure into the common path.\n        #[cold]\n        #[inline(never)]\n        fn outlined_call<R>(work: impl FnOnce() -> R) -> R {\n            work()\n        }\n\n        fn direct_insert_at_type<K: ReadColumn + Ord + ToFromUsize + KeySize + core::fmt::Debug>(\n            this: &mut UniqueDirectIndex<K>,\n            cols: &ColList,\n            row_ref: RowRef<'_>,\n        ) -> (Result<(), RowPointer>, Option<TypedIndex>)\n        where\n            TypedIndex: From<BtreeUniqueIndex<K>>,\n        {\n            let key = project_to_singleton_key(cols, row_ref);\n            let ptr = row_ref.pointer();\n            match this.insert_maybe_despecialize(key, ptr) {\n                Ok(res) => (res, None),\n                Err(Despecialize) => outlined_call(|| {\n                    let mut index = this.into_btree();\n                    let res = index.insert(key, ptr);\n                    (res, Some(index.into()))\n                }),\n            }\n        }\n\n        fn insert_av(\n            this: &mut impl Index<Key = AlgebraicValue>,\n            cols: &ColList,\n            row_ref: RowRef<'_>,\n        ) -> (Result<(), RowPointer>, Option<TypedIndex>) {\n            // SAFETY: Caller promised that any `col` in `cols` is in-bounds of `row_ref`'s layout.\n            let key = unsafe { row_ref.project_unchecked(cols) };\n            let res = this.insert(key, row_ref.pointer());\n            (res, None)\n        }\n\n        let (res, new) = match self {\n            Self::BtreeBool(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeSumTag(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeU256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeI256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeF32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeF64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeString(idx) => insert_at_type(idx, cols, row_ref),\n            Self::BtreeAV(idx) => insert_av(idx, cols, row_ref),\n            Self::HashBool(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashSumTag(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashU256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashI256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashF32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashF64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashString(idx) => insert_at_type(idx, cols, row_ref),\n            Self::HashAV(idx) => insert_av(idx, cols, row_ref),\n            Self::UniqueBtreeBool(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeSumTag(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeU256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeI256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeF32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeF64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeString(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueBtreeAV(idx) => insert_av(idx, cols, row_ref),\n            Self::UniqueHashBool(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashSumTag(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI8(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI16(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI128(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashU256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashI256(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashF32(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashF64(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashString(idx) => insert_at_type(idx, cols, row_ref),\n            Self::UniqueHashAV(this) => insert_av(this, cols, row_ref),\n            Self::UniqueDirectSumTag(idx) => insert_at_type(idx, cols, row_ref),\n\n            Self::UniqueDirectU8(idx) => direct_insert_at_type(idx, cols, row_ref),\n            Self::UniqueDirectU16(idx) => direct_insert_at_type(idx, cols, row_ref),\n            Self::UniqueDirectU32(idx) => direct_insert_at_type(idx, cols, row_ref),\n            Self::UniqueDirectU64(idx) => direct_insert_at_type(idx, cols, row_ref),\n        };\n\n        if let Some(new) = new {\n            *self = new;\n        }\n\n        res\n    }\n\n    /// Remove the row referred to by `row_ref` from the index `self`,\n    /// which must be keyed at `cols`.\n    ///\n    /// If `cols` is inconsistent with `self`,\n    /// or the `row_ref` has a row type other than that used for `self`,\n    /// this will behave oddly; it may return an error, do nothing,\n    /// or remove the wrong value from the index.\n    /// Note, however, that it will not invoke undefined behavior.\n    ///\n    /// If the row was present and has been deleted, returns `Ok(true)`.\n    // TODO(centril): make this unsafe and use unchecked conversions.\n    fn delete(&mut self, cols: &ColList, row_ref: RowRef<'_>) -> Result<bool, InvalidFieldError> {\n        fn delete_at_type<T: ReadColumn, I: Index>(\n            this: &mut I,\n            cols: &ColList,\n            row_ref: RowRef<'_>,\n            convert: impl FnOnce(T) -> I::Key,\n        ) -> Result<bool, InvalidFieldError> {\n            let col_pos = cols.as_singleton().unwrap();\n            let key = row_ref.read_col(col_pos).map_err(|_| col_pos)?;\n            let key = convert(key);\n            Ok(this.delete(&key, row_ref.pointer()))\n        }\n\n        fn delete_av(\n            this: &mut impl Index<Key = AlgebraicValue>,\n            cols: &ColList,\n            row_ref: RowRef<'_>,\n        ) -> Result<bool, InvalidFieldError> {\n            let key = row_ref.project(cols)?;\n            Ok(this.delete(&key, row_ref.pointer()))\n        }\n\n        use core::convert::identity as id;\n\n        match self {\n            Self::BtreeBool(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeSumTag(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeU256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeI256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeF32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeF64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeString(this) => delete_at_type(this, cols, row_ref, id),\n            Self::BtreeAV(this) => delete_av(this, cols, row_ref),\n            Self::HashBool(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashSumTag(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashU256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashI256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashF32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashF64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashString(this) => delete_at_type(this, cols, row_ref, id),\n            Self::HashAV(this) => delete_av(this, cols, row_ref),\n            Self::UniqueBtreeBool(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeSumTag(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeU256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeI256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeF32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeF64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeString(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueBtreeAV(this) => delete_av(this, cols, row_ref),\n            Self::UniqueHashBool(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashSumTag(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI128(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashU256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashI256(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashF32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashF64(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashString(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueHashAV(this) => delete_av(this, cols, row_ref),\n            Self::UniqueDirectSumTag(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueDirectU8(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueDirectU16(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueDirectU32(this) => delete_at_type(this, cols, row_ref, id),\n            Self::UniqueDirectU64(this) => delete_at_type(this, cols, row_ref, id),\n        }\n    }\n\n    fn seek_point(&self, key: &AlgebraicValue) -> TypedIndexPointIter<'_> {\n        fn iter_at_type<'a, I: Index>(\n            this: &'a I,\n            key: &AlgebraicValue,\n            av_as_t: impl Fn(&AlgebraicValue) -> Option<&I::Key>,\n        ) -> I::PointIter<'a> {\n            this.seek_point(av_as_t(key).expect(\"key does not conform to key type of index\"))\n        }\n\n        use TypedIndex::*;\n        use TypedIndexPointIter::*;\n        match self {\n            BtreeBool(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_bool)),\n            BtreeU8(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u8)),\n            BtreeSumTag(this) => NonUnique(iter_at_type(this, key, as_sum_tag)),\n            BtreeI8(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i8)),\n            BtreeU16(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u16)),\n            BtreeI16(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i16)),\n            BtreeU32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u32)),\n            BtreeI32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i32)),\n            BtreeU64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u64)),\n            BtreeI64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i64)),\n            BtreeU128(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u128)),\n            BtreeI128(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i128)),\n            BtreeU256(this) => NonUnique(iter_at_type(this, key, |av| av.as_u256().map(|x| &**x))),\n            BtreeI256(this) => NonUnique(iter_at_type(this, key, |av| av.as_i256().map(|x| &**x))),\n            BtreeF32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_f32)),\n            BtreeF64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_f64)),\n            BtreeString(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_string)),\n            BtreeAV(this) => NonUnique(this.seek_point(key)),\n            HashBool(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_bool)),\n            HashU8(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u8)),\n            HashSumTag(this) => NonUnique(iter_at_type(this, key, as_sum_tag)),\n            HashI8(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i8)),\n            HashU16(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u16)),\n            HashI16(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i16)),\n            HashU32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u32)),\n            HashI32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i32)),\n            HashU64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u64)),\n            HashI64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i64)),\n            HashU128(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_u128)),\n            HashI128(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_i128)),\n            HashU256(this) => NonUnique(iter_at_type(this, key, |av| av.as_u256().map(|x| &**x))),\n            HashI256(this) => NonUnique(iter_at_type(this, key, |av| av.as_i256().map(|x| &**x))),\n            HashF32(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_f32)),\n            HashF64(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_f64)),\n            HashString(this) => NonUnique(iter_at_type(this, key, AlgebraicValue::as_string)),\n            HashAV(this) => NonUnique(this.seek_point(key)),\n            UniqueBtreeBool(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_bool)),\n            UniqueBtreeU8(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u8)),\n            UniqueBtreeSumTag(this) => Unique(iter_at_type(this, key, as_sum_tag)),\n            UniqueBtreeI8(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i8)),\n            UniqueBtreeU16(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u16)),\n            UniqueBtreeI16(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i16)),\n            UniqueBtreeU32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u32)),\n            UniqueBtreeI32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i32)),\n            UniqueBtreeU64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u64)),\n            UniqueBtreeI64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i64)),\n            UniqueBtreeU128(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u128)),\n            UniqueBtreeI128(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i128)),\n            UniqueBtreeU256(this) => Unique(iter_at_type(this, key, |av| av.as_u256().map(|x| &**x))),\n            UniqueBtreeI256(this) => Unique(iter_at_type(this, key, |av| av.as_i256().map(|x| &**x))),\n            UniqueBtreeF32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_f32)),\n            UniqueBtreeF64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_f64)),\n            UniqueBtreeString(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_string)),\n            UniqueBtreeAV(this) => Unique(this.seek_point(key)),\n\n            UniqueHashBool(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_bool)),\n            UniqueHashU8(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u8)),\n            UniqueHashSumTag(this) => Unique(iter_at_type(this, key, as_sum_tag)),\n            UniqueHashI8(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i8)),\n            UniqueHashU16(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u16)),\n            UniqueHashI16(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i16)),\n            UniqueHashU32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u32)),\n            UniqueHashI32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i32)),\n            UniqueHashU64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u64)),\n            UniqueHashI64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i64)),\n            UniqueHashU128(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u128)),\n            UniqueHashI128(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_i128)),\n            UniqueHashU256(this) => Unique(iter_at_type(this, key, |av| av.as_u256().map(|x| &**x))),\n            UniqueHashI256(this) => Unique(iter_at_type(this, key, |av| av.as_i256().map(|x| &**x))),\n            UniqueHashF32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_f32)),\n            UniqueHashF64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_f64)),\n            UniqueHashString(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_string)),\n            UniqueHashAV(this) => Unique(this.seek_point(key)),\n\n            UniqueDirectSumTag(this) => Unique(iter_at_type(this, key, as_sum_tag)),\n            UniqueDirectU8(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u8)),\n            UniqueDirectU16(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u16)),\n            UniqueDirectU32(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u32)),\n            UniqueDirectU64(this) => Unique(iter_at_type(this, key, AlgebraicValue::as_u64)),\n        }\n    }\n\n    fn seek_range(&self, range: &impl RangeBounds<AlgebraicValue>) -> IndexSeekRangeResult<TypedIndexRangeIter<'_>> {\n        // Copied from `RangeBounds::is_empty` as it's unstable.\n        // TODO(centril): replace once stable.\n        fn is_empty<T: PartialOrd>(bounds: &impl RangeBounds<T>) -> bool {\n            use core::ops::Bound::*;\n            !match (bounds.start_bound(), bounds.end_bound()) {\n                (Unbounded, _) | (_, Unbounded) => true,\n                (Included(start), Excluded(end))\n                | (Excluded(start), Included(end))\n                | (Excluded(start), Excluded(end)) => start < end,\n                (Included(start), Included(end)) => start <= end,\n            }\n        }\n\n        fn iter_at_type<'a, I: RangedIndex>(\n            this: &'a I,\n            range: &impl RangeBounds<AlgebraicValue>,\n            av_as_t: impl Fn(&AlgebraicValue) -> Option<&I::Key>,\n        ) -> I::RangeIter<'a> {\n            let av_as_t = |v| av_as_t(v).expect(\"bound does not conform to key type of index\");\n            let start = range.start_bound().map(av_as_t);\n            let end = range.end_bound().map(av_as_t);\n            this.seek_range(&(start, end))\n        }\n\n        use TypedIndexRangeIter::*;\n        Ok(match self {\n            // Hash indices are not `RangeIndex`.\n            Self::HashBool(_)\n            | Self::HashU8(_)\n            | Self::HashSumTag(_)\n            | Self::HashI8(_)\n            | Self::HashU16(_)\n            | Self::HashI16(_)\n            | Self::HashU32(_)\n            | Self::HashI32(_)\n            | Self::HashU64(_)\n            | Self::HashI64(_)\n            | Self::HashU128(_)\n            | Self::HashI128(_)\n            | Self::HashF32(_)\n            | Self::HashF64(_)\n            | Self::HashU256(_)\n            | Self::HashI256(_)\n            | Self::HashString(_)\n            | Self::HashAV(_)\n            | Self::UniqueHashBool(_)\n            | Self::UniqueHashU8(_)\n            | Self::UniqueHashSumTag(_)\n            | Self::UniqueHashI8(_)\n            | Self::UniqueHashU16(_)\n            | Self::UniqueHashI16(_)\n            | Self::UniqueHashU32(_)\n            | Self::UniqueHashI32(_)\n            | Self::UniqueHashU64(_)\n            | Self::UniqueHashI64(_)\n            | Self::UniqueHashU128(_)\n            | Self::UniqueHashI128(_)\n            | Self::UniqueHashF32(_)\n            | Self::UniqueHashF64(_)\n            | Self::UniqueHashU256(_)\n            | Self::UniqueHashI256(_)\n            | Self::UniqueHashString(_)\n            | Self::UniqueHashAV(_) => return Err(IndexCannotSeekRange),\n\n            // Ensure we don't panic inside `BTreeMap::seek_range`.\n            _ if is_empty(range) => RangeEmpty,\n\n            Self::BtreeBool(this) => BtreeBool(iter_at_type(this, range, AlgebraicValue::as_bool)),\n            Self::BtreeU8(this) => BtreeU8(iter_at_type(this, range, AlgebraicValue::as_u8)),\n            Self::BtreeSumTag(this) => BtreeSumTag(iter_at_type(this, range, as_sum_tag)),\n            Self::BtreeI8(this) => BtreeI8(iter_at_type(this, range, AlgebraicValue::as_i8)),\n            Self::BtreeU16(this) => BtreeU16(iter_at_type(this, range, AlgebraicValue::as_u16)),\n            Self::BtreeI16(this) => BtreeI16(iter_at_type(this, range, AlgebraicValue::as_i16)),\n            Self::BtreeU32(this) => BtreeU32(iter_at_type(this, range, AlgebraicValue::as_u32)),\n            Self::BtreeI32(this) => BtreeI32(iter_at_type(this, range, AlgebraicValue::as_i32)),\n            Self::BtreeU64(this) => BtreeU64(iter_at_type(this, range, AlgebraicValue::as_u64)),\n            Self::BtreeI64(this) => BtreeI64(iter_at_type(this, range, AlgebraicValue::as_i64)),\n            Self::BtreeU128(this) => BtreeU128(iter_at_type(this, range, AlgebraicValue::as_u128)),\n            Self::BtreeI128(this) => BtreeI128(iter_at_type(this, range, AlgebraicValue::as_i128)),\n            Self::BtreeU256(this) => BtreeU256(iter_at_type(this, range, |av| av.as_u256().map(|x| &**x))),\n            Self::BtreeI256(this) => BtreeI256(iter_at_type(this, range, |av| av.as_i256().map(|x| &**x))),\n            Self::BtreeF32(this) => BtreeF32(iter_at_type(this, range, AlgebraicValue::as_f32)),\n            Self::BtreeF64(this) => BtreeF64(iter_at_type(this, range, AlgebraicValue::as_f64)),\n            Self::BtreeString(this) => BtreeString(iter_at_type(this, range, AlgebraicValue::as_string)),\n            Self::BtreeAV(this) => BtreeAV(this.seek_range(range)),\n\n            Self::UniqueBtreeBool(this) => UniqueBtreeBool(iter_at_type(this, range, AlgebraicValue::as_bool)),\n            Self::UniqueBtreeU8(this) => UniqueBtreeU8(iter_at_type(this, range, AlgebraicValue::as_u8)),\n            Self::UniqueBtreeSumTag(this) => UniqueBtreeSumTag(iter_at_type(this, range, as_sum_tag)),\n            Self::UniqueBtreeI8(this) => UniqueBtreeI8(iter_at_type(this, range, AlgebraicValue::as_i8)),\n            Self::UniqueBtreeU16(this) => UniqueBtreeU16(iter_at_type(this, range, AlgebraicValue::as_u16)),\n            Self::UniqueBtreeI16(this) => UniqueBtreeI16(iter_at_type(this, range, AlgebraicValue::as_i16)),\n            Self::UniqueBtreeU32(this) => UniqueBtreeU32(iter_at_type(this, range, AlgebraicValue::as_u32)),\n            Self::UniqueBtreeI32(this) => UniqueBtreeI32(iter_at_type(this, range, AlgebraicValue::as_i32)),\n            Self::UniqueBtreeU64(this) => UniqueBtreeU64(iter_at_type(this, range, AlgebraicValue::as_u64)),\n            Self::UniqueBtreeI64(this) => UniqueBtreeI64(iter_at_type(this, range, AlgebraicValue::as_i64)),\n            Self::UniqueBtreeU128(this) => UniqueBtreeU128(iter_at_type(this, range, AlgebraicValue::as_u128)),\n            Self::UniqueBtreeI128(this) => UniqueBtreeI128(iter_at_type(this, range, AlgebraicValue::as_i128)),\n            Self::UniqueBtreeF32(this) => UniqueBtreeF32(iter_at_type(this, range, AlgebraicValue::as_f32)),\n            Self::UniqueBtreeF64(this) => UniqueBtreeF64(iter_at_type(this, range, AlgebraicValue::as_f64)),\n            Self::UniqueBtreeU256(this) => UniqueBtreeU256(iter_at_type(this, range, |av| av.as_u256().map(|x| &**x))),\n            Self::UniqueBtreeI256(this) => UniqueBtreeI256(iter_at_type(this, range, |av| av.as_i256().map(|x| &**x))),\n            Self::UniqueBtreeString(this) => UniqueBtreeString(iter_at_type(this, range, AlgebraicValue::as_string)),\n            Self::UniqueBtreeAV(this) => UniqueBtreeAV(this.seek_range(range)),\n\n            Self::UniqueDirectSumTag(this) => UniqueDirectU8(iter_at_type(this, range, as_sum_tag)),\n            Self::UniqueDirectU8(this) => UniqueDirect(iter_at_type(this, range, AlgebraicValue::as_u8)),\n            Self::UniqueDirectU16(this) => UniqueDirect(iter_at_type(this, range, AlgebraicValue::as_u16)),\n            Self::UniqueDirectU32(this) => UniqueDirect(iter_at_type(this, range, AlgebraicValue::as_u32)),\n            Self::UniqueDirectU64(this) => UniqueDirect(iter_at_type(this, range, AlgebraicValue::as_u64)),\n        })\n    }\n\n    fn clear(&mut self) {\n        same_for_all_types!(self, this => this.clear())\n    }\n\n    #[allow(unused)] // used only by tests\n    fn is_empty(&self) -> bool {\n        self.num_rows() == 0\n    }\n\n    /// The number of rows stored in this index.\n    ///\n    /// Note that, for non-unique indexes, this may be larger than [`Self::num_keys`].\n    ///\n    /// This method runs in constant time.\n    fn num_rows(&self) -> usize {\n        same_for_all_types!(self, this => this.num_rows())\n    }\n\n    fn num_keys(&self) -> usize {\n        same_for_all_types!(self, this => this.num_keys())\n    }\n\n    /// The number of bytes stored in keys in this index.\n    ///\n    /// For non-unique indexes, duplicate keys are counted once for each row that refers to them,\n    /// even though the internal storage may deduplicate them as an optimization.\n    ///\n    /// This method runs in constant time.\n    ///\n    /// See the [`KeySize`] trait for more details on how this method computes its result.\n    pub fn num_key_bytes(&self) -> u64 {\n        same_for_all_types!(self, this => this.num_key_bytes())\n    }\n}\n\n/// An index on a set of [`ColId`]s of a table.\n#[derive(Debug, PartialEq, Eq)]\npub struct TableIndex {\n    /// The actual index, specialized for the appropriate key type.\n    idx: TypedIndex,\n    /// The key type of this index.\n    /// This is the projection of the row type to the types of the columns indexed.\n    // NOTE(centril): This is accessed in index scan ABIs for decoding, so don't `Box<_>` it.\n    pub key_type: AlgebraicType,\n\n    /// Given a full row, typed at some `ty: ProductType`,\n    /// these columns are the ones that this index indexes.\n    /// Projecting the `ty` to `self.indexed_columns` yields the index's type `self.key_type`.\n    pub indexed_columns: ColList,\n}\n\nimpl MemoryUsage for TableIndex {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            idx,\n            key_type,\n            indexed_columns,\n        } = self;\n        idx.heap_usage() + key_type.heap_usage() + indexed_columns.heap_usage()\n    }\n}\n\nstatic_assert_size!(TableIndex, 96);\n\nimpl TableIndex {\n    /// Returns a new possibly unique index, with `index_id` for a choice of indexing algorithm.\n    pub fn new(\n        row_type: &ProductType,\n        indexed_columns: ColList,\n        index_kind: IndexKind,\n        is_unique: bool,\n    ) -> Result<Self, InvalidFieldError> {\n        let key_type = row_type.project(&indexed_columns)?;\n        let typed_index = TypedIndex::new(&key_type, index_kind, is_unique);\n        Ok(Self {\n            idx: typed_index,\n            key_type,\n            indexed_columns,\n        })\n    }\n\n    /// Clones the structure of this index but not the indexed elements,\n    /// so the returned index is empty.\n    pub fn clone_structure(&self) -> Self {\n        let key_type = self.key_type.clone();\n        let idx = self.idx.clone_structure();\n        let indexed_columns = self.indexed_columns.clone();\n        Self {\n            idx,\n            key_type,\n            indexed_columns,\n        }\n    }\n\n    /// Returns whether this is a unique index or not.\n    pub fn is_unique(&self) -> bool {\n        self.idx.is_unique()\n    }\n\n    /// Inserts `ptr` with the value `row` to this index.\n    /// This index will extract the necessary values from `row` based on `self.indexed_columns`.\n    ///\n    /// Returns `Err(existing_row)` if this insertion would violate a unique constraint.\n    ///\n    /// # Safety\n    ///\n    /// Caller promises that projecting the `row_ref`'s type\n    /// to the index's columns equals the index's key type.\n    /// This is entailed by an index belonging to the table's schema.\n    /// It also follows from `row_ref`'s type/layout\n    /// being the same as passed in on `self`'s construction.\n    pub unsafe fn check_and_insert(&mut self, row_ref: RowRef<'_>) -> Result<(), RowPointer> {\n        // SAFETY:\n        // 1. We're passing the same `ColList` that was provided during construction.\n        // 2. Forward the caller's proof obligation.\n        unsafe { self.idx.insert(&self.indexed_columns, row_ref) }\n    }\n\n    /// Deletes `row_ref` with its indexed value `row_ref.project(&self.indexed_columns)` from this index.\n    ///\n    /// Returns whether `ptr` was present.\n    pub fn delete(&mut self, row_ref: RowRef<'_>) -> Result<bool, InvalidFieldError> {\n        self.idx.delete(&self.indexed_columns, row_ref)\n    }\n\n    /// Returns whether `value` is in this index.\n    pub fn contains_any(&self, value: &AlgebraicValue) -> bool {\n        self.seek_point(value).next().is_some()\n    }\n\n    /// Returns the number of rows associated with this `value`.\n    /// Returns `None` if 0.\n    /// Returns `Some(1)` if the index is unique.\n    pub fn count(&self, value: &AlgebraicValue) -> Option<usize> {\n        match self.seek_point(value).count() {\n            0 => None,\n            n => Some(n),\n        }\n    }\n\n    /// Returns an iterator that yields all the `RowPointer`s for the given `key`.\n    pub fn seek_point(&self, key: &AlgebraicValue) -> TableIndexPointIter<'_> {\n        TableIndexPointIter {\n            iter: self.idx.seek_point(key),\n        }\n    }\n\n    /// Returns an iterator over the [TableIndex],\n    /// that yields all the `RowPointer`s,\n    /// that fall within the specified `range`,\n    /// if the index is [`RangedIndex`].\n    pub fn seek_range(\n        &self,\n        range: &impl RangeBounds<AlgebraicValue>,\n    ) -> IndexSeekRangeResult<TableIndexRangeIter<'_>> {\n        Ok(TableIndexRangeIter {\n            iter: self.idx.seek_range(range)?,\n        })\n    }\n\n    /// Extends [`TableIndex`] with `rows`.\n    ///\n    /// Returns the first unique constraint violation caused when adding this index, if any.\n    ///\n    /// # Safety\n    ///\n    /// Caller promises that projecting any of the `row_ref`'s type\n    /// to the index's columns equals the index's key type.\n    /// This is entailed by an index belonging to the table's schema.\n    /// It also follows from `row_ref`'s type/layout\n    /// being the same as passed in on `self`'s construction.\n    pub unsafe fn build_from_rows<'table>(\n        &mut self,\n        rows: impl IntoIterator<Item = RowRef<'table>>,\n    ) -> Result<(), RowPointer> {\n        rows.into_iter()\n            // SAFETY: Forward caller proof obligation.\n            .try_for_each(|row_ref| unsafe { self.check_and_insert(row_ref) })\n    }\n\n    /// Returns an error with the first unique constraint violation that\n    /// would occur if `self` and `other` were to be merged.\n    ///\n    /// The closure `ignore` indicates whether a row in `self` should be ignored.\n    pub fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        use TypedIndex::*;\n        match (&self.idx, &other.idx) {\n            // For non-unique indices, it's always possible to merge.\n            (BtreeBool(_), BtreeBool(_))\n            | (BtreeU8(_), BtreeU8(_))\n            | (BtreeSumTag(_), BtreeSumTag(_))\n            | (BtreeI8(_), BtreeI8(_))\n            | (BtreeU16(_), BtreeU16(_))\n            | (BtreeI16(_), BtreeI16(_))\n            | (BtreeU32(_), BtreeU32(_))\n            | (BtreeI32(_), BtreeI32(_))\n            | (BtreeU64(_), BtreeU64(_))\n            | (BtreeI64(_), BtreeI64(_))\n            | (BtreeU128(_), BtreeU128(_))\n            | (BtreeI128(_), BtreeI128(_))\n            | (BtreeU256(_), BtreeU256(_))\n            | (BtreeI256(_), BtreeI256(_))\n            | (BtreeF32(_), BtreeF32(_))\n            | (BtreeF64(_), BtreeF64(_))\n            | (BtreeString(_), BtreeString(_))\n            | (BtreeAV(_), BtreeAV(_))\n            | (HashBool(_), HashBool(_))\n            | (HashU8(_), HashU8(_))\n            | (HashSumTag(_), HashSumTag(_))\n            | (HashI8(_), HashI8(_))\n            | (HashU16(_), HashU16(_))\n            | (HashI16(_), HashI16(_))\n            | (HashU32(_), HashU32(_))\n            | (HashI32(_), HashI32(_))\n            | (HashU64(_), HashU64(_))\n            | (HashI64(_), HashI64(_))\n            | (HashU128(_), HashU128(_))\n            | (HashI128(_), HashI128(_))\n            | (HashU256(_), HashU256(_))\n            | (HashI256(_), HashI256(_))\n            | (HashF32(_), HashF32(_))\n            | (HashF64(_), HashF64(_))\n            | (HashString(_), HashString(_))\n            | (HashAV(_), HashAV(_)) => Ok(()),\n            // For unique indices, we'll need to see if everything in `other` can be added to `idx`.\n            (UniqueBtreeBool(idx), UniqueBtreeBool(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU8(idx), UniqueBtreeU8(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeSumTag(idx), UniqueBtreeSumTag(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI8(idx), UniqueBtreeI8(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU16(idx), UniqueBtreeU16(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI16(idx), UniqueBtreeI16(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU32(idx), UniqueBtreeU32(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI32(idx), UniqueBtreeI32(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU64(idx), UniqueBtreeU64(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI64(idx), UniqueBtreeI64(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU128(idx), UniqueBtreeU128(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI128(idx), UniqueBtreeI128(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeU256(idx), UniqueBtreeU256(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeI256(idx), UniqueBtreeI256(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeF32(idx), UniqueBtreeF32(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeF64(idx), UniqueBtreeF64(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeString(idx), UniqueBtreeString(other)) => idx.can_merge(other, ignore),\n            (UniqueBtreeAV(idx), UniqueBtreeAV(other)) => idx.can_merge(other, ignore),\n            (UniqueHashBool(idx), UniqueHashBool(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU8(idx), UniqueHashU8(other)) => idx.can_merge(other, ignore),\n            (UniqueHashSumTag(idx), UniqueHashSumTag(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI8(idx), UniqueHashI8(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU16(idx), UniqueHashU16(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI16(idx), UniqueHashI16(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU32(idx), UniqueHashU32(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI32(idx), UniqueHashI32(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU64(idx), UniqueHashU64(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI64(idx), UniqueHashI64(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU128(idx), UniqueHashU128(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI128(idx), UniqueHashI128(other)) => idx.can_merge(other, ignore),\n            (UniqueHashU256(idx), UniqueHashU256(other)) => idx.can_merge(other, ignore),\n            (UniqueHashI256(idx), UniqueHashI256(other)) => idx.can_merge(other, ignore),\n            (UniqueHashF32(idx), UniqueHashF32(other)) => idx.can_merge(other, ignore),\n            (UniqueHashF64(idx), UniqueHashF64(other)) => idx.can_merge(other, ignore),\n            (UniqueHashString(idx), UniqueHashString(other)) => idx.can_merge(other, ignore),\n            (UniqueHashAV(idx), UniqueHashAV(other)) => idx.can_merge(other, ignore),\n            (UniqueDirectU8(idx), UniqueDirectU8(other)) => idx.can_merge(other, ignore),\n            (UniqueDirectSumTag(idx), UniqueDirectSumTag(other)) => idx.can_merge(other, ignore),\n            (UniqueDirectU16(idx), UniqueDirectU16(other)) => idx.can_merge(other, ignore),\n            (UniqueDirectU32(idx), UniqueDirectU32(other)) => idx.can_merge(other, ignore),\n            (UniqueDirectU64(idx), UniqueDirectU64(other)) => idx.can_merge(other, ignore),\n\n            _ => unreachable!(\"non-matching index kinds\"),\n        }\n    }\n\n    /// Deletes all entries from the index, leaving it empty.\n    ///\n    /// When inserting a newly-created index into the committed state,\n    /// we clear the tx state's index and insert it,\n    /// rather than constructing a new `TableIndex`.\n    pub fn clear(&mut self) {\n        self.idx.clear();\n    }\n\n    /// The number of unique keys in this index.\n    pub fn num_keys(&self) -> usize {\n        self.idx.num_keys()\n    }\n\n    /// The number of rows stored in this index.\n    ///\n    /// Note that, for non-unique indexes, this may be larger than [`Self::num_keys`].\n    ///\n    /// This method runs in constant time.\n    pub fn num_rows(&self) -> u64 {\n        self.idx.num_rows() as u64\n    }\n\n    /// The number of bytes stored in keys in this index.\n    ///\n    /// For non-unique indexes, duplicate keys are counted once for each row that refers to them,\n    /// even though the internal storage may deduplicate them as an optimization.\n    ///\n    /// This method runs in constant time.\n    ///\n    /// See the [`KeySize`] trait for more details on how this method computes its result.\n    pub fn num_key_bytes(&self) -> u64 {\n        self.idx.num_key_bytes()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::page_pool::PagePool;\n    use crate::{blob_store::HashMapBlobStore, table::test::table};\n    use core::ops::Bound::*;\n    use decorum::Total;\n    use proptest::prelude::*;\n    use proptest::{\n        collection::{hash_set, vec},\n        test_runner::TestCaseResult,\n    };\n    use spacetimedb_data_structures::map::HashMap;\n    use spacetimedb_lib::ProductTypeElement;\n    use spacetimedb_primitives::ColId;\n    use spacetimedb_sats::proptest::{generate_algebraic_value, generate_primitive_algebraic_type};\n    use spacetimedb_sats::{\n        product,\n        proptest::{generate_product_value, generate_row_type},\n        AlgebraicType, ProductType, ProductValue,\n    };\n\n    fn gen_cols(ty_len: usize) -> impl Strategy<Value = ColList> {\n        vec((0..ty_len as u16).prop_map_into::<ColId>(), 1..=ty_len)\n            .prop_map(|cols| cols.into_iter().collect::<ColList>())\n    }\n\n    fn gen_row_and_cols() -> impl Strategy<Value = (ProductType, ColList, ProductValue)> {\n        generate_row_type(1..16).prop_flat_map(|ty| {\n            (\n                Just(ty.clone()),\n                gen_cols(ty.elements.len()),\n                generate_product_value(ty),\n            )\n        })\n    }\n\n    impl IndexKind {\n        /// Returns a strategy generating a ranged index kind.\n        fn gen_for_ranged() -> impl Strategy<Value = Self> {\n            any::<bool>().prop_map(|is_direct| if is_direct { Self::Direct } else { Self::BTree })\n        }\n    }\n\n    fn new_index(row_type: &ProductType, cols: &ColList, is_unique: bool, kind: IndexKind) -> TableIndex {\n        TableIndex::new(row_type, cols.clone(), kind, is_unique).unwrap()\n    }\n\n    /// Extracts from `row` the relevant column values according to what columns are indexed.\n    fn get_fields(cols: &ColList, row: &ProductValue) -> AlgebraicValue {\n        row.project(cols).unwrap()\n    }\n\n    /// Returns whether indexing `row` again would violate a unique constraint, if any.\n    fn violates_unique_constraint(index: &TableIndex, cols: &ColList, row: &ProductValue) -> bool {\n        !index.is_unique() || index.contains_any(&get_fields(cols, row))\n    }\n\n    /// Returns an iterator over the rows that would violate the unique constraint of this index,\n    /// if `row` were inserted,\n    /// or `None`, if this index doesn't have a unique constraint.\n    fn get_rows_that_violate_unique_constraint<'a>(\n        index: &'a TableIndex,\n        row: &'a AlgebraicValue,\n    ) -> Option<TableIndexPointIter<'a>> {\n        index.is_unique().then(|| index.seek_point(row))\n    }\n\n    fn successor_of_primitive(av: &AlgebraicValue) -> Option<AlgebraicValue> {\n        use AlgebraicValue::*;\n        match av {\n            Min | Max | Sum(_) | Product(_) | Array(_) | String(_) => unimplemented!(),\n\n            Bool(false) => Some(Bool(true)),\n            Bool(true) => None,\n            I8(x) => x.checked_add(1).map(I8),\n            U8(x) => x.checked_add(1).map(U8),\n            I16(x) => x.checked_add(1).map(I16),\n            U16(x) => x.checked_add(1).map(U16),\n            I32(x) => x.checked_add(1).map(I32),\n            U32(x) => x.checked_add(1).map(U32),\n            I64(x) => x.checked_add(1).map(I64),\n            U64(x) => x.checked_add(1).map(U64),\n            I128(x) => x.0.checked_add(1).map(Packed).map(I128),\n            U128(x) => x.0.checked_add(1).map(Packed).map(U128),\n            I256(x) => x.checked_add(1.into()).map(Box::new).map(I256),\n            U256(x) => x.checked_add(1u8.into()).map(Box::new).map(U256),\n            F32(x) => Some(F32(Total::from_inner(x.into_inner().next_up()))),\n            F64(x) => Some(F64(Total::from_inner(x.into_inner().next_up()))),\n        }\n    }\n\n    fn gen_primitive_ty_and_val() -> impl Strategy<Value = (AlgebraicType, AlgebraicValue)> {\n        generate_primitive_algebraic_type().prop_flat_map(|ty| (Just(ty.clone()), generate_algebraic_value(ty)))\n    }\n\n    proptest! {\n        #![proptest_config(ProptestConfig { max_shrink_iters: 0x10000000, ..Default::default() })]\n\n        #[test]\n        fn hash_index_cannot_seek_range((ty, cols, pv) in gen_row_and_cols(), is_unique: bool) {\n            let index = TableIndex::new(&ty, cols.clone(), IndexKind::Hash, is_unique).unwrap();\n\n            let key = pv.project(&cols).unwrap();\n            assert_eq!(index.seek_range(&(key.clone()..=key)).unwrap_err(), IndexCannotSeekRange);\n        }\n\n        #[test]\n        fn remove_nonexistent_noop((ty, cols, pv) in gen_row_and_cols(), kind: IndexKind, is_unique: bool) {\n            let mut index = new_index(&ty, &cols, is_unique, kind);\n            let mut table = table(ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let row_ref = table.insert(&pool, &mut blob_store, &pv).unwrap().1;\n            prop_assert_eq!(index.delete(row_ref).unwrap(), false);\n            prop_assert!(index.idx.is_empty());\n            prop_assert_eq!(index.num_keys(), 0);\n            prop_assert_eq!(index.num_key_bytes(), 0);\n            prop_assert_eq!(index.num_rows(), 0);\n        }\n\n        #[test]\n        fn insert_delete_noop((ty, cols, pv) in gen_row_and_cols(), kind: IndexKind, is_unique: bool) {\n            let mut index = new_index(&ty, &cols, is_unique, kind);\n            let mut table = table(ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let row_ref = table.insert(&pool, &mut blob_store, &pv).unwrap().1;\n            let value = get_fields(&cols, &pv);\n\n            prop_assert_eq!(index.num_keys(), 0);\n            prop_assert_eq!(index.num_rows(), 0);\n            prop_assert_eq!(index.contains_any(&value), false);\n\n            prop_assert_eq!(unsafe { index.check_and_insert(row_ref) }, Ok(()));\n            prop_assert_eq!(index.num_keys(), 1);\n            prop_assert_eq!(index.num_rows(), 1);\n            prop_assert_eq!(index.contains_any(&value), true);\n\n            prop_assert_eq!(index.delete(row_ref).unwrap(), true);\n            prop_assert_eq!(index.num_keys(), 0);\n            prop_assert_eq!(index.num_rows(), 0);\n            prop_assert_eq!(index.contains_any(&value), false);\n        }\n\n        #[test]\n        fn non_unique_allows_key_twice(\n            (ty, cols, key) in gen_row_and_cols(),\n            kind: IndexKind,\n            vals in hash_set(any::<i32>(), 1..10)\n        ) {\n            // Add a field to `ty` so we can use the same key more than once.\n            let mut ty = Vec::from(ty.elements);\n            ty.push(ProductTypeElement::new_named(AlgebraicType::I32, \"extra\"));\n            let ty = ProductType::from(ty.into_boxed_slice());\n\n            let mut index = new_index(&ty, &cols, false, kind);\n            let mut table = table(ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n\n            let num_vals = vals.len();\n            for val in vals {\n                let mut key = Vec::from(key.clone().elements);\n                key.push(val.into());\n                let key = ProductValue::from(key);\n\n                let row_ref = table.insert(&pool, &mut blob_store, &key).unwrap().1;\n\n                // SAFETY: `row_ref` has the same type as was passed in when constructing `index`.\n                prop_assert_eq!(unsafe { index.check_and_insert(row_ref) }, Ok(()));\n            }\n\n            assert_eq!(index.num_keys(), 1);\n            assert_eq!(index.num_rows() as usize, num_vals);\n        }\n\n        #[test]\n        fn insert_again_violates_unique_constraint((ty, cols, pv) in gen_row_and_cols(), kind: IndexKind) {\n            let mut index = new_index(&ty, &cols, true, kind);\n            let mut table = table(ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let row_ref = table.insert(&pool, &mut blob_store, &pv).unwrap().1;\n            let value = get_fields(&cols, &pv);\n\n            // Nothing in the index yet.\n            prop_assert_eq!(index.num_rows(), 0);\n            prop_assert_eq!(violates_unique_constraint(&index, &cols, &pv), false);\n            prop_assert_eq!(\n                get_rows_that_violate_unique_constraint(&index, &value).unwrap().collect::<Vec<_>>(),\n                []\n            );\n\n            // Insert.\n            // SAFETY: `row_ref` has the same type as was passed in when constructing `index`.\n            prop_assert_eq!(unsafe { index.check_and_insert(row_ref) }, Ok(()));\n\n            // Inserting again would be a problem.\n            prop_assert_eq!(index.num_keys(), 1);\n            prop_assert_eq!(index.num_rows(), 1);\n            prop_assert_eq!(violates_unique_constraint(&index, &cols, &pv), true);\n            prop_assert_eq!(\n                get_rows_that_violate_unique_constraint(&index, &value).unwrap().collect::<Vec<_>>(),\n                [row_ref.pointer()]\n            );\n            // SAFETY: `row_ref` has the same type as was passed in when constructing `index`.\n            prop_assert_eq!(unsafe { index.check_and_insert(row_ref) }, Err(row_ref.pointer()));\n            prop_assert_eq!(index.num_keys(), 1);\n            prop_assert_eq!(index.num_rows(), 1);\n        }\n\n        #[test]\n        fn seek_various_ranges(needle in 1..u64::MAX, is_unique: bool, kind in IndexKind::gen_for_ranged()) {\n            use AlgebraicValue::U64 as V;\n\n            let cols = 0.into();\n            let ty = ProductType::from_iter([AlgebraicType::U64]);\n            let mut index = new_index(&ty, &cols, is_unique, kind);\n            let mut table = table(ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n\n            let prev = needle - 1;\n            let next = needle + 1;\n            let range = prev..=next;\n\n            let mut val_to_ptr = HashMap::default();\n\n            // Insert `prev`, `needle`, and `next`.\n            for x in range.clone() {\n                let row = product![x];\n                let row_ref = table.insert(&pool, &mut blob_store, &row).unwrap().1;\n                val_to_ptr.insert(x, row_ref.pointer());\n                // SAFETY: `row_ref` has the same type as was passed in when constructing `index`.\n                prop_assert_eq!(unsafe { index.check_and_insert(row_ref) }, Ok(()));\n            }\n\n            assert_eq!(index.num_keys(), 3);\n            assert_eq!(index.num_rows(), 3);\n            assert_eq!(index.num_key_bytes() as usize, 3 * size_of::<u64>());\n\n            fn test_seek(index: &TableIndex, val_to_ptr: &HashMap<u64, RowPointer>, range: impl RangeBounds<AlgebraicValue>, expect: impl IntoIterator<Item = u64>) -> TestCaseResult {\n                check_seek(index.seek_range(&range).unwrap().collect(), val_to_ptr, expect)\n            }\n\n            fn check_seek(mut ptrs_in_index: Vec<RowPointer>, val_to_ptr: &HashMap<u64, RowPointer>, expect: impl IntoIterator<Item = u64>) -> TestCaseResult {\n                ptrs_in_index.sort();\n                let mut expected_ptrs = expect.into_iter().map(|expected| val_to_ptr.get(&expected).unwrap()).copied().collect::<Vec<_>>();\n                expected_ptrs.sort();\n                prop_assert_eq!(\n                    ptrs_in_index,\n                    expected_ptrs\n                );\n                Ok(())\n            }\n\n            // Test point ranges.\n            for x in range.clone() {\n                test_seek(&index, &val_to_ptr, V(x), [x])?;\n                check_seek(index.seek_point(&V(x)).collect(), &val_to_ptr, [x])?;\n            }\n\n            // Test `..` (`RangeFull`).\n            test_seek(&index, &val_to_ptr, .., [prev, needle, next])?;\n\n            // Test `x..` (`RangeFrom`).\n            test_seek(&index, &val_to_ptr, V(prev).., [prev, needle, next])?;\n            test_seek(&index, &val_to_ptr, V(needle).., [needle, next])?;\n            test_seek(&index, &val_to_ptr, V(next).., [next])?;\n\n            // Test `..x` (`RangeTo`).\n            test_seek(&index, &val_to_ptr, ..V(prev), [])?;\n            test_seek(&index, &val_to_ptr, ..V(needle), [prev])?;\n            test_seek(&index, &val_to_ptr, ..V(next), [prev, needle])?;\n\n            // Test `..=x` (`RangeToInclusive`).\n            test_seek(&index, &val_to_ptr, ..=V(prev), [prev])?;\n            test_seek(&index, &val_to_ptr, ..=V(needle), [prev, needle])?;\n            test_seek(&index, &val_to_ptr, ..=V(next), [prev, needle, next])?;\n\n            // Test `x..y` (`Range`).\n            test_seek(&index, &val_to_ptr, V(prev)..V(prev), [])?;\n            test_seek(&index, &val_to_ptr, V(prev)..V(needle), [prev])?;\n            test_seek(&index, &val_to_ptr, V(prev)..V(next), [prev, needle])?;\n            test_seek(&index, &val_to_ptr, V(needle)..V(next), [needle])?;\n\n            // Test `x..=y` (`RangeInclusive`).\n            test_seek(&index, &val_to_ptr, V(prev)..=V(prev), [prev])?;\n            test_seek(&index, &val_to_ptr, V(prev)..=V(needle), [prev, needle])?;\n            test_seek(&index, &val_to_ptr, V(prev)..=V(next), [prev, needle, next])?;\n            test_seek(&index, &val_to_ptr, V(needle)..=V(next), [needle, next])?;\n            test_seek(&index, &val_to_ptr, V(next)..=V(next), [next])?;\n\n            // Test `(x, y]` (Exclusive start, inclusive end).\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Included(V(prev))), [])?;\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Included(V(needle))), [needle])?;\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Included(V(next))), [needle, next])?;\n\n            // Test `(x, inf]` (Exclusive start, unbounded end).\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Unbounded), [needle, next])?;\n            test_seek(&index, &val_to_ptr, (Excluded(V(needle)), Unbounded), [next])?;\n            test_seek(&index, &val_to_ptr, (Excluded(V(next)), Unbounded), [])?;\n\n            // Test `(x, y)` (Exclusive start, exclusive end).\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Excluded(V(needle))), [])?;\n            test_seek(&index, &val_to_ptr, (Excluded(V(prev)), Excluded(V(next))), [needle])?;\n        }\n\n        #[test]\n        fn empty_range_scans_dont_panic((ty, val) in gen_primitive_ty_and_val(), is_unique: bool, kind in IndexKind::gen_for_ranged()) {\n            let succ = successor_of_primitive(&val);\n            prop_assume!(succ.is_some());\n            let succ = succ.unwrap();\n\n            // Construct the index.\n            let row_ty = ProductType::from([ty.clone()]);\n            let mut index = new_index(&row_ty, &[0].into(), is_unique, kind);\n\n            // Construct the table and add `val` as a row.\n            let mut table = table(row_ty);\n            let pool = PagePool::new_for_test();\n            let mut blob_store = HashMapBlobStore::default();\n            let pv = product![val.clone()];\n            let row_ref = table.insert(&pool, &mut blob_store, &pv).unwrap().1;\n\n            // Add the row to the index.\n            assert_eq!(index.num_keys(), 0);\n            assert_eq!(index.num_rows(), 0);\n            assert_eq!(index.num_key_bytes(), 0);\n            unsafe { index.check_and_insert(row_ref).unwrap(); }\n            assert_eq!(index.num_keys(), 1);\n            assert_eq!(index.num_rows(), 1);\n\n            // Seek the empty ranges.\n            let rows = index.seek_range(&(&succ..&val)).unwrap().collect::<Vec<_>>();\n            assert_eq!(rows, []);\n            let rows = index.seek_range(&(&succ..=&val)).unwrap().collect::<Vec<_>>();\n            assert_eq!(rows, []);\n            let rows = index.seek_range(&(Excluded(&succ), Included(&val))).unwrap().collect::<Vec<_>>();\n            assert_eq!(rows, []);\n            let rows = index.seek_range(&(Excluded(&succ), Excluded(&val))).unwrap().collect::<Vec<_>>();\n            assert_eq!(rows, []);\n            let rows = index.seek_range(&(Excluded(&val), Excluded(&val))).unwrap().collect::<Vec<_>>();\n            assert_eq!(rows, []);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/multimap.rs",
    "content": "use super::same_key_entry::{same_key_iter, SameKeyEntry, SameKeyEntryIter};\nuse super::{key_size::KeyBytesStorage, Index, KeySize, RangedIndex};\nuse crate::indexes::RowPointer;\nuse core::ops::RangeBounds;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse std::collections::btree_map::{BTreeMap, Range};\n\n/// A multi map that relates a `K` to a *set* of `RowPointer`s.\n#[derive(Debug, PartialEq, Eq)]\npub struct MultiMap<K> {\n    /// The map is backed by a `BTreeMap` for relating keys to values.\n    ///\n    /// A value set is stored as a `SmallVec`.\n    /// This is an optimization over a `Vec<_>`\n    /// as we allow a single element to be stored inline\n    /// to improve performance for the common case of one element.\n    map: BTreeMap<K, SameKeyEntry>,\n    /// The memoized number of rows indexed in `self.map`.\n    num_rows: usize,\n    /// Storage for [`Index::num_key_bytes`].\n    num_key_bytes: u64,\n}\n\nimpl<K> Default for MultiMap<K> {\n    fn default() -> Self {\n        Self {\n            map: <_>::default(),\n            num_rows: <_>::default(),\n            num_key_bytes: <_>::default(),\n        }\n    }\n}\n\nimpl<K: MemoryUsage> MemoryUsage for MultiMap<K> {\n    fn heap_usage(&self) -> usize {\n        let Self {\n            map,\n            num_rows,\n            num_key_bytes,\n        } = self;\n        map.heap_usage() + num_rows.heap_usage() + num_key_bytes.heap_usage()\n    }\n}\n\nimpl<K: Ord + KeySize> Index for MultiMap<K> {\n    type Key = K;\n\n    fn clone_structure(&self) -> Self {\n        <_>::default()\n    }\n\n    /// Inserts the relation `key -> ptr` to this multimap.\n    ///\n    /// The map does not check whether `key -> ptr` was already in the map.\n    /// It's assumed that the same `ptr` is never added twice,\n    /// and multimaps do not bind one `key` to the same `ptr`.\n    fn insert(&mut self, key: Self::Key, ptr: RowPointer) -> Result<(), RowPointer> {\n        self.num_rows += 1;\n        self.num_key_bytes.add_to_key_bytes::<Self>(&key);\n        self.map.entry(key).or_default().push(ptr);\n        Ok(())\n    }\n\n    /// Deletes `key -> ptr` from this multimap.\n    ///\n    /// Returns whether `key -> ptr` was present.\n    fn delete(&mut self, key: &K, ptr: RowPointer) -> bool {\n        let Some(vset) = self.map.get_mut(key) else {\n            return false;\n        };\n\n        let (deleted, is_empty) = vset.delete(ptr);\n\n        if is_empty {\n            self.map.remove(key);\n        }\n\n        if deleted {\n            self.num_rows -= 1;\n            self.num_key_bytes.sub_from_key_bytes::<Self>(key);\n        }\n\n        deleted\n    }\n\n    type PointIter<'a>\n        = SameKeyEntryIter<'a>\n    where\n        Self: 'a;\n\n    fn seek_point(&self, key: &Self::Key) -> Self::PointIter<'_> {\n        same_key_iter(self.map.get(key))\n    }\n\n    fn num_keys(&self) -> usize {\n        self.map.len()\n    }\n\n    fn num_key_bytes(&self) -> u64 {\n        self.num_key_bytes\n    }\n\n    fn num_rows(&self) -> usize {\n        self.num_rows\n    }\n\n    /// Deletes all entries from the multimap, leaving it empty.\n    /// This will not deallocate the outer map.\n    fn clear(&mut self) {\n        self.map.clear();\n        self.num_rows = 0;\n        self.num_key_bytes.reset_to_zero();\n    }\n\n    fn can_merge(&self, _: &Self, _: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        // `self.insert` always returns `Ok(_)`.\n        Ok(())\n    }\n}\n\nimpl<K: Ord + KeySize> RangedIndex for MultiMap<K> {\n    type RangeIter<'a>\n        = MultiMapRangeIter<'a, K>\n    where\n        Self: 'a;\n\n    /// Returns an iterator over the multimap that yields all the `V`s\n    /// of the `K`s that fall within the specified `range`.\n    fn seek_range(&self, range: &impl RangeBounds<Self::Key>) -> Self::RangeIter<'_> {\n        MultiMapRangeIter {\n            outer: self.map.range((range.start_bound(), range.end_bound())),\n            inner: SameKeyEntry::empty_iter(),\n        }\n    }\n}\n\n/// An iterator over values in a [`MultiMap`] where the keys are in a certain range.\n#[derive(Clone)]\npub struct MultiMapRangeIter<'a, K> {\n    /// The outer iterator seeking for matching keys in the range.\n    outer: Range<'a, K, SameKeyEntry>,\n    /// The inner iterator for the value set for a found key.\n    inner: SameKeyEntryIter<'a>,\n}\n\nimpl<K> Iterator for MultiMapRangeIter<'_, K> {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            // While the inner iterator has elements, yield them.\n            if let Some(val) = self.inner.next() {\n                return Some(val);\n            }\n            // Advance and get a new inner, if possible, or quit.\n            // We'll come back and yield elements from it in the next iteration.\n            let inner = self.outer.next().map(|(_, i)| i)?;\n            self.inner = inner.iter();\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/same_key_entry.rs",
    "content": "use crate::{indexes::RowPointer, static_assert_size};\nuse core::slice;\nuse smallvec::SmallVec;\nuse spacetimedb_data_structures::map::{hash_set, HashCollectionExt, HashSet};\nuse spacetimedb_memory_usage::MemoryUsage;\n\n/// A supporting type for multimap implementations\n/// that handles all the values for the same key,\n/// leaving the multimap to only have to care about the keys.\n///\n/// For performance reasons,\n/// this is an enum\n/// that deals with a smaller number of values in the first variant\n/// and with a larger number in the second variant.\n#[derive(Debug, PartialEq, Eq)]\npub(super) enum SameKeyEntry {\n    /// A small number of values.\n    ///\n    /// No ordering is kept between values.\n    /// This makes insertions into amortized `O(k)`\n    /// whereas deletions become `O(|values|)` instead.\n    /// This is acceptable as `|values|` is small\n    /// and because deleting from an array list is `O(n)` either way.\n    ///\n    /// This also represents the \"no values\" case,\n    /// although the multimap may want to delete the key in that case.\n    ///\n    /// Up to two values are represented inline here.\n    /// It's not profitable to represent this as a separate variant\n    /// as that would increase `size_of::<SameKeyEntry>()` by 8 bytes.\n    Small(SmallVec<[RowPointer; 2]>),\n\n    /// A large number of values.\n    ///\n    /// Used when the heap size of `Small` would exceed one standard page.\n    /// See [`SameKeyEntry::LARGE_AFTER_LEN`] for details.\n    ///\n    /// Note that using a `HashSet`, with `S = RandomState`,\n    /// entails that the iteration order is not deterministic.\n    /// This is observed when doing queries against the index.\n    Large(HashSet<RowPointer>),\n}\n\nstatic_assert_size!(SameKeyEntry, 32);\n\nimpl Default for SameKeyEntry {\n    fn default() -> Self {\n        Self::Small(<_>::default())\n    }\n}\n\nimpl MemoryUsage for SameKeyEntry {\n    fn heap_usage(&self) -> usize {\n        match self {\n            Self::Small(x) => x.heap_usage(),\n            Self::Large(x) => x.heap_usage(),\n        }\n    }\n}\n\nimpl SameKeyEntry {\n    /// The number of elements\n    /// beyond which the strategy is changed from small to large storage.\n    const LARGE_AFTER_LEN: usize = 4096 / size_of::<RowPointer>();\n\n    /// Pushes `val` as an entry for the key.\n    ///\n    /// This assumes that `val` was previously not recorded.\n    /// The structure does not check whether it was previously resident.\n    /// As a consequence, the time complexity is `O(k)` amortized.\n    pub(super) fn push(&mut self, val: RowPointer) {\n        match self {\n            Self::Small(list) if list.len() <= Self::LARGE_AFTER_LEN => {\n                list.push(val);\n            }\n            Self::Small(list) => {\n                // Reconstruct into a set.\n                let mut set = HashSet::with_capacity(list.len() + 1);\n                set.extend(list.drain(..));\n\n                // Add `val`.\n                set.insert(val);\n\n                *self = Self::Large(set);\n            }\n            Self::Large(set) => {\n                set.insert(val);\n            }\n        }\n    }\n\n    /// Deletes `val` as an entry for the key.\n    ///\n    /// Returns `(was_deleted, is_empty)`.\n    pub(super) fn delete(&mut self, val: RowPointer) -> (bool, bool) {\n        match self {\n            Self::Small(list) => {\n                // The `list` is not sorted, so we have to do a linear scan first.\n                if let Some(idx) = list.iter().position(|v| *v == val) {\n                    list.swap_remove(idx);\n                    (true, list.is_empty())\n                } else {\n                    (false, false)\n                }\n            }\n            Self::Large(set) => {\n                let removed = set.remove(&val);\n                let empty = set.is_empty();\n                (removed, empty)\n            }\n        }\n    }\n\n    /// Returns an iterator over all the entries for this key.\n    pub(super) fn iter(&self) -> SameKeyEntryIter<'_> {\n        match self {\n            Self::Small(list) => SameKeyEntryIter::Small(list.iter()),\n            Self::Large(set) => SameKeyEntryIter::Large(set.iter().into()),\n        }\n    }\n\n    /// Returns an iterator over no entries.\n    pub(super) fn empty_iter<'a>() -> SameKeyEntryIter<'a> {\n        SameKeyEntryIter::Small(const { &[] }.iter())\n    }\n}\n\n/// Returns an iterator for a key's entries `ske`.\n/// This efficiently handles the case where there's no key (`None`).\npub(super) fn same_key_iter(ske: Option<&SameKeyEntry>) -> SameKeyEntryIter<'_> {\n    match ske {\n        None => SameKeyEntry::empty_iter(),\n        Some(ske) => ske.iter(),\n    }\n}\n\n/// An iterator over values in a [`SameKeyEntry`].\n#[derive(Clone)]\npub enum SameKeyEntryIter<'a> {\n    Small(slice::Iter<'a, RowPointer>),\n    /// This variant doesn't occur so much\n    /// and we'd like to reduce the footprint of `SameKeyEntryIter`.\n    Large(Box<hash_set::Iter<'a, RowPointer>>),\n}\n\nstatic_assert_size!(SameKeyEntryIter, 16);\n\nimpl Iterator for SameKeyEntryIter<'_> {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            Self::Small(list) => list.next(),\n            Self::Large(set) => set.next(),\n        }\n        .copied()\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/unique_direct_fixed_cap_index.rs",
    "content": "use super::index::{Index, RangedIndex};\nuse super::unique_direct_index::{expose, injest, ToFromUsize, NONE_PTR};\nuse super::uniquemap::UniquePointIter;\nuse crate::indexes::RowPointer;\nuse crate::table_index::KeySize;\nuse core::marker::PhantomData;\nuse core::mem;\nuse core::ops::{Bound, RangeBounds};\nuse core::slice::Iter;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n/// A direct index with for relating unsigned integer keys to [`RowPointer`].\n/// The index is provided a capacity on creation and will have that during its lifetime.\n///\n/// These indices are intended for small fixed capacities\n/// and will be efficient for both monotonic and random insert patterns for small capacities.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct UniqueDirectFixedCapIndex<K> {\n    marker: PhantomData<K>,\n    /// The array holding the elements.\n    array: Box<[RowPointer]>,\n    /// The number of keys indexed.\n    len: usize,\n}\n\nimpl<K> MemoryUsage for UniqueDirectFixedCapIndex<K> {\n    fn heap_usage(&self) -> usize {\n        let Self { marker: _, array, len } = self;\n        array.heap_usage() + len.heap_usage()\n    }\n}\n\nimpl<K: ToFromUsize> UniqueDirectFixedCapIndex<K> {\n    /// Returns a new fixed capacity index.\n    pub fn new(cap: usize) -> Self {\n        Self {\n            marker: PhantomData,\n            len: 0,\n            array: vec![NONE_PTR; cap].into(),\n        }\n    }\n}\n\nimpl<K: ToFromUsize + KeySize> Index for UniqueDirectFixedCapIndex<K> {\n    type Key = K;\n\n    /// Clones the structure of the index and returns one with the same capacity.\n    fn clone_structure(&self) -> Self {\n        Self::new(self.array.len())\n    }\n\n    /// Inserts the relation `key -> val` to this index.\n    ///\n    /// If `key` was already present in the index, does not add an association with `val`.\n    /// Returns the existing associated value instead.\n    ///\n    /// Panics if the key is beyond the fixed capacity of this index.\n    fn insert(&mut self, key: Self::Key, val: RowPointer) -> Result<(), RowPointer> {\n        // Fetch the slot.\n        let slot = &mut self.array[key.to_usize()];\n        let in_slot = *slot;\n        if in_slot == NONE_PTR {\n            // We have `NONE_PTR`, so not set yet.\n            *slot = injest(val);\n            self.len += 1;\n            Ok(())\n        } else {\n            Err(expose(in_slot))\n        }\n    }\n\n    fn delete(&mut self, &key: &Self::Key, _: RowPointer) -> bool {\n        let Some(slot) = self.array.get_mut(key.to_usize()) else {\n            return false;\n        };\n        let old_val = mem::replace(slot, NONE_PTR);\n        let deleted = old_val != NONE_PTR;\n        self.len -= deleted as usize;\n        deleted\n    }\n\n    type PointIter<'a>\n        = UniquePointIter\n    where\n        Self: 'a;\n\n    fn seek_point(&self, &key: &Self::Key) -> Self::PointIter<'_> {\n        let point = self\n            .array\n            .get(key.to_usize())\n            .copied()\n            .filter(|slot| *slot != NONE_PTR)\n            .map(expose);\n        UniquePointIter::new(point)\n    }\n\n    fn num_keys(&self) -> usize {\n        self.len\n    }\n\n    fn clear(&mut self) {\n        self.array.fill(NONE_PTR);\n        self.len = 0;\n    }\n\n    fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        for (slot_s, slot_o) in self.array.iter().zip(other.array.iter()) {\n            let ptr_s = expose(*slot_s);\n            if *slot_s != NONE_PTR && *slot_o != NONE_PTR && !ignore(&ptr_s) {\n                // For the same key, we found both slots occupied, so we cannot merge.\n                return Err(ptr_s);\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl<K: ToFromUsize + KeySize> RangedIndex for UniqueDirectFixedCapIndex<K> {\n    type RangeIter<'a>\n        = UniqueDirectFixedCapIndexRangeIter<'a>\n    where\n        Self: 'a;\n\n    fn seek_range(&self, range: &impl RangeBounds<Self::Key>) -> Self::RangeIter<'_> {\n        // Translate `range` to `start..end`.\n        let end = match range.end_bound() {\n            Bound::Included(&e) => e.to_usize() + 1,\n            Bound::Excluded(&e) => e.to_usize(),\n            Bound::Unbounded => self.array.len(),\n        };\n        let start = match range.start_bound() {\n            Bound::Included(&s) => s.to_usize(),\n            Bound::Excluded(&s) => s.to_usize() + 1,\n            Bound::Unbounded => 0,\n        };\n\n        // Normalize `start` so that `start <= end`.\n        let start = start.min(end);\n\n        // Make the iterator.\n        UniqueDirectFixedCapIndexRangeIter::new(self.array.get(start..end).unwrap_or_default())\n    }\n}\n\n/// An iterator over a range of keys in a [`UniqueDirectFixedCapIndex`].\n#[derive(Debug, Clone)]\npub struct UniqueDirectFixedCapIndexRangeIter<'a> {\n    iter: Iter<'a, RowPointer>,\n}\n\nimpl<'a> UniqueDirectFixedCapIndexRangeIter<'a> {\n    fn new(slice: &'a [RowPointer]) -> Self {\n        let iter = slice.iter();\n        Self { iter }\n    }\n}\n\nimpl Iterator for UniqueDirectFixedCapIndexRangeIter<'_> {\n    type Item = RowPointer;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter\n            // Make sure the row exists.\n            .find(|slot| **slot != NONE_PTR)\n            .map(|ptr| expose(*ptr))\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::table_index::unique_direct_index::test::gen_row_pointers;\n    use core::ops::Range;\n    use proptest::prelude::*;\n\n    fn range(start: u8, end: u8) -> Range<u8> {\n        let min = start.min(end);\n        let max = start.max(end);\n        min..max\n    }\n\n    fn setup(start: u8, end: u8) -> (UniqueDirectFixedCapIndex<u8>, Range<u8>, Vec<RowPointer>) {\n        let range = range(start, end);\n        let (keys, ptrs): (Vec<_>, Vec<_>) = range.clone().zip(gen_row_pointers()).unzip();\n\n        let mut index = UniqueDirectFixedCapIndex::new(u8::MAX as usize + 1);\n        for (key, ptr) in keys.iter().zip(&ptrs) {\n            index.insert(*key, *ptr).unwrap();\n        }\n        assert_eq!(index.num_rows(), (range.end - range.start) as usize);\n        (index, range, ptrs)\n    }\n\n    proptest! {\n        #[test]\n        fn seek_range_gives_back_inserted(start: u8, end: u8) {\n            let (index, range, ptrs) = setup(start, end);\n            let ptrs_found = index.seek_range(&range).collect::<Vec<_>>();\n            assert_eq!(ptrs, ptrs_found);\n        }\n\n        #[test]\n        fn inserting_again_errors(start: u8, end: u8) {\n            let (mut index, keys, ptrs) = setup(start, end);\n            for (key, ptr) in keys.zip(&ptrs) {\n                assert_eq!(index.insert(key, *ptr).unwrap_err(), *ptr)\n            }\n        }\n\n        #[test]\n        fn deleting_allows_reinsertion(start: u8, end: u8, key: u8) {\n            let (mut index, range, _) = setup(start, end);\n\n            if range.start == range.end {\n                return Err(TestCaseError::Reject(\"empty range\".into()));\n            }\n\n            let key = key.clamp(range.start, range.end.saturating_sub(1));\n\n            let ptr = index.seek_point(&key).next().unwrap();\n            assert!(index.delete(&key, ptr));\n            assert!(!index.delete(&key, ptr));\n            assert_eq!(index.num_rows(), (range.end - range.start - 1) as usize);\n\n            index.insert(key, ptr).unwrap();\n            assert_eq!(index.num_rows(), (range.end - range.start) as usize);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/unique_direct_index.rs",
    "content": "use super::index::{Despecialize, Index, RangedIndex};\nuse super::uniquemap::UniquePointIter;\nuse super::{BtreeUniqueIndex, KeySize};\nuse crate::indexes::{PageIndex, PageOffset, RowPointer, SquashedOffset};\nuse core::marker::PhantomData;\nuse core::mem;\nuse core::ops::{Bound, RangeBounds};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse spacetimedb_sats::sum_value::SumTag;\n\npub trait ToFromUsize: Copy {\n    /// Converts value to `usize`.\n    fn to_usize(self) -> usize;\n\n    /// Converts `value` to `Self`.\n    fn from_usize(x: usize) -> Self;\n}\n\nmacro_rules! impl_to_from_usize {\n    ($ty:ty) => {\n        impl ToFromUsize for $ty {\n            #[inline]\n            fn to_usize(self) -> usize {\n                self as usize\n            }\n\n            #[inline]\n            fn from_usize(x: usize) -> Self {\n                x as Self\n            }\n        }\n    };\n}\n\nimpl_to_from_usize!(u8);\nimpl_to_from_usize!(u16);\nimpl_to_from_usize!(u32);\nimpl_to_from_usize!(u64);\nimpl_to_from_usize!(usize);\n\nimpl ToFromUsize for SumTag {\n    #[inline]\n    fn to_usize(self) -> usize {\n        self.0.to_usize()\n    }\n\n    #[inline]\n    fn from_usize(x: usize) -> Self {\n        Self(u8::from_usize(x))\n    }\n}\n\n/// A direct index for relating unsigned integer keys [`u8`..`u64`] to [`RowPointer`].\n///\n/// This index is efficient when given keys that are used in non-random insert patterns\n/// where keys are dense and not far apart as well as starting near zero.\n/// Conversely, it performs worse than a btree index in the case of highly random inserts\n/// and with sparse keys and where the first key inserted is large.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct UniqueDirectIndex<K> {\n    marker: PhantomData<K>,\n    /// The outer index.\n    outer: Vec<Option<InnerIndex>>,\n    /// The number of keys indexed.\n    len: usize,\n}\n\nimpl<K> MemoryUsage for UniqueDirectIndex<K> {\n    fn heap_usage(&self) -> usize {\n        let Self { marker: _, outer, len } = self;\n        outer.heap_usage() + len.heap_usage()\n    }\n}\n\nimpl<K> Default for UniqueDirectIndex<K> {\n    fn default() -> Self {\n        Self {\n            marker: PhantomData,\n            outer: Vec::new(),\n            len: 0,\n        }\n    }\n}\n\n/// The standard page size on linux x64.\nconst PAGE_SIZE: usize = 4_096;\n/// Number of keys per inner index.\nconst KEYS_PER_INNER: usize = PAGE_SIZE / size_of::<RowPointer>();\n/// The inner index array, which will be heap allocated.\ntype InnerIndexArray = [RowPointer; KEYS_PER_INNER];\n\n/// An inner index. Either it is empty, or it has `KEYS_PER_INNER` elements.\n#[derive(Debug, Clone, PartialEq, Eq)]\nstruct InnerIndex {\n    inner: Box<InnerIndexArray>,\n}\n\nimpl MemoryUsage for InnerIndex {\n    fn heap_usage(&self) -> usize {\n        self.inner.heap_usage()\n    }\n}\n\n/// The sentinel used to represent an empty slot in the index.\n/// The reserved bit set to `false` is used to indicate absence.\npub(super) const NONE_PTR: RowPointer = RowPointer::new(false, PageIndex(0), PageOffset(0), SquashedOffset::TX_STATE);\n\n#[derive(Debug)]\nstruct InnerIndexKey(usize);\n\n/// Splits the `key` into an outer and inner key.\n#[inline]\nfn split_key(key: usize) -> (usize, InnerIndexKey) {\n    (key / KEYS_PER_INNER, InnerIndexKey(key % KEYS_PER_INNER))\n}\n\n/// Converts a row poiner into one for inside consumption.\n#[inline]\npub(super) fn injest(ptr: RowPointer) -> RowPointer {\n    ptr.with_reserved_bit(true)\n}\n\n/// Returns a row pointer for outside consumption.\n#[inline]\npub(super) fn expose(ptr: RowPointer) -> RowPointer {\n    ptr.with_reserved_bit(false)\n}\n\nimpl InnerIndex {\n    fn new() -> Self {\n        use std::alloc::{alloc_zeroed, handle_alloc_error, Layout};\n\n        let layout = Layout::new::<InnerIndexArray>();\n\n        // Allocate with `alloc_zeroed` so that the bytes are initially 0, rather than uninit.\n        // This is a sound implementation as `0`-init elements == `NONE_PTR`.\n        // TODO: use Box::new_zeroed() once stabilized.\n        // SAFETY: The layout's size is non-zero.\n        let raw: *mut InnerIndexArray = unsafe { alloc_zeroed(layout) }.cast();\n\n        if raw.is_null() {\n            handle_alloc_error(layout);\n        }\n\n        // SAFETY: We used the global allocator with a layout for `InnerIndexArray`.\n        //         and the elements are 0-init by `alloc_zeroed`,\n        //         which makes each element a valid `RowPointer` (`u64`).\n        let inner = unsafe { Box::from_raw(raw) };\n\n        Self { inner }\n    }\n\n    /// Returns the pointer at `key`.\n    fn get(&self, key: InnerIndexKey) -> RowPointer {\n        // SAFETY: `self.inner.len() = KEYS_PER_INNER` and `key.0 < KEYS_PER_INNER`.\n        *unsafe { self.inner.get_unchecked(key.0) }\n    }\n\n    /// Returns the mutable slot at `key`.\n    fn get_mut(&mut self, key: InnerIndexKey) -> &mut RowPointer {\n        // SAFETY: `self.inner.len() = KEYS_PER_INNER` and `key.0 < KEYS_PER_INNER`.\n        unsafe { self.inner.get_unchecked_mut(key.0) }\n    }\n}\n\nimpl<K: ToFromUsize + KeySize> Index for UniqueDirectIndex<K> {\n    type Key = K;\n\n    fn clone_structure(&self) -> Self {\n        Self::default()\n    }\n\n    fn insert_maybe_despecialize(\n        &mut self,\n        key: Self::Key,\n        val: RowPointer,\n    ) -> Result<Result<(), RowPointer>, Despecialize> {\n        let key = key.to_usize();\n        if key > const { u32::MAX as usize } {\n            // For large `u64`, this can cause OOM.\n            //\n            // TODO(perf, centril): do not pay for this cost when `key = u8..u32`.\n            //\n            // TODO(perf, centril): in the future,\n            // collect stats for when too many far apart keys are inserted,\n            // and despecialize in that case as well.\n            return Err(Despecialize);\n        }\n\n        let (key_outer, key_inner) = split_key(key);\n\n        // Fetch the outer index and ensure it can house `key_outer`.\n        let outer = &mut self.outer;\n        outer.resize(outer.len().max(key_outer + 1), None);\n\n        // Fetch the inner index.\n        // SAFETY: ensured in `.resize(_)` that `key_outer < outer.len()`, making indexing to `key_outer` valid.\n        let inner = unsafe { outer.get_unchecked_mut(key_outer) };\n        let inner = inner.get_or_insert_with(InnerIndex::new);\n\n        // Fetch the slot.\n        let slot = inner.get_mut(key_inner);\n        let in_slot = *slot;\n        Ok(if in_slot == NONE_PTR {\n            // We have `NONE_PTR`, so not set yet.\n            *slot = injest(val);\n            self.len += 1;\n            Ok(())\n        } else {\n            Err(expose(in_slot))\n        })\n    }\n\n    fn delete(&mut self, key: &Self::Key, _: RowPointer) -> bool {\n        let key = key.to_usize();\n        let (key_outer, key_inner) = split_key(key);\n        let outer = &mut self.outer;\n        if let Some(Some(inner)) = outer.get_mut(key_outer) {\n            let slot = inner.get_mut(key_inner);\n            let old_val = mem::replace(slot, NONE_PTR);\n            let deleted = old_val != NONE_PTR;\n            self.len -= deleted as usize;\n            return deleted;\n        }\n        false\n    }\n\n    type PointIter<'a>\n        = UniquePointIter\n    where\n        Self: 'a;\n\n    fn seek_point(&self, key: &Self::Key) -> Self::PointIter<'_> {\n        let key = key.to_usize();\n        let (outer_key, inner_key) = split_key(key);\n        let point = self\n            .outer\n            .get(outer_key)\n            .and_then(|x| x.as_ref())\n            .map(|inner| inner.get(inner_key))\n            .filter(|slot| *slot != NONE_PTR)\n            .map(expose);\n        UniquePointIter::new(point)\n    }\n\n    fn num_keys(&self) -> usize {\n        self.len\n    }\n\n    /// Deletes all entries from the index, leaving it empty.\n    /// This will not deallocate the outer index.\n    fn clear(&mut self) {\n        self.outer.clear();\n        self.len = 0;\n    }\n\n    /// Returns whether `other` can be merged into `self`\n    /// with an error containing the element in `self` that caused the violation.\n    ///\n    /// The closure `ignore` indicates whether a row in `self` should be ignored.\n    fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        for (inner_s, inner_o) in self.outer.iter().zip(&other.outer) {\n            let (Some(inner_s), Some(inner_o)) = (inner_s, inner_o) else {\n                continue;\n            };\n\n            for (slot_s, slot_o) in inner_s.inner.iter().zip(inner_o.inner.iter()) {\n                let ptr_s = expose(*slot_s);\n                if *slot_s != NONE_PTR && *slot_o != NONE_PTR && !ignore(&ptr_s) {\n                    // For the same key, we found both slots occupied, so we cannot merge.\n                    return Err(ptr_s);\n                }\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl<K: ToFromUsize + KeySize> RangedIndex for UniqueDirectIndex<K> {\n    type RangeIter<'a>\n        = UniqueDirectIndexRangeIter<'a>\n    where\n        K: 'a;\n\n    /// Returns an iterator yielding all the [`RowPointer`] that correspond to the provided `range`.\n    fn seek_range(&self, range: &impl RangeBounds<Self::Key>) -> Self::RangeIter<'_> {\n        // The upper bound of possible key.\n        // This isn't necessarily the real max key actually present in the index,\n        // due to possible deletions.\n        let max_key = self.outer.len() * KEYS_PER_INNER;\n\n        // Translate `range` to `start..end`.\n        let start = match range.start_bound() {\n            Bound::Included(&s) => s.to_usize(),\n            Bound::Excluded(&s) => s.to_usize() + 1, // If this wraps, we will clamp to `max_key` later.\n            Bound::Unbounded => 0,\n        };\n        let end = match range.end_bound() {\n            Bound::Included(&e) => e.to_usize() + 1, // If this wraps, we will clamp to `max_key` later.\n            Bound::Excluded(&e) => e.to_usize(),\n            Bound::Unbounded => max_key,\n        };\n\n        // Clamp `end` to max possible key in index.\n        let end = end.min(max_key);\n\n        // Normalize `start` so that `start <= end`.\n        let start = start.min(end);\n\n        UniqueDirectIndexRangeIter {\n            outer: &self.outer,\n            start,\n            end,\n        }\n    }\n}\n\n/// An iterator over a range of keys in a [`UniqueDirectIndex`].\n#[derive(Debug, Clone)]\npub struct UniqueDirectIndexRangeIter<'a> {\n    outer: &'a [Option<InnerIndex>],\n    start: usize,\n    end: usize,\n}\n\nimpl Iterator for UniqueDirectIndexRangeIter<'_> {\n    type Item = RowPointer;\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            if self.start >= self.end {\n                // We're at or beyond the end, so we're done.\n                return None;\n            }\n\n            let (outer_key, inner_key) = split_key(self.start);\n            // SAFETY:\n            // - `self.start <= self.end <= max_key`\n            // - the early exit above ensures that `self.start < max_key`.\n            // - `split_key(max_key).0 = self.outer.len()`.\n            // - this entails that `outer_key < self.outer.len()`.\n            let inner = unsafe { self.outer.get_unchecked(outer_key) };\n            let Some(inner) = inner else {\n                // Inner index has not been initialized,\n                // so the entire inner index is empty.\n                // Let's jump to the next inner index.\n                self.start += KEYS_PER_INNER;\n                continue;\n            };\n            let ptr = inner.get(inner_key);\n\n            // Advance to next key.\n            self.start += 1;\n\n            if ptr != NONE_PTR {\n                // The row actually exists, so we've found something to return.\n                return Some(expose(ptr));\n            }\n        }\n    }\n}\n\nimpl<K: KeySize + Ord + ToFromUsize> UniqueDirectIndex<K> {\n    /// Convert this Direct index into a B-Tree index.\n    pub fn into_btree(&self) -> BtreeUniqueIndex<K> {\n        let mut new_index: BtreeUniqueIndex<K> = <_>::default();\n\n        for (key_outer, inner) in self.outer.iter().enumerate() {\n            let Some(inner) = inner else {\n                continue;\n            };\n\n            for (key_inner, &slot) in inner.inner.iter().enumerate() {\n                if slot == NONE_PTR {\n                    continue;\n                }\n\n                let key = key_outer * KEYS_PER_INNER + key_inner;\n                let key = K::from_usize(key);\n                new_index\n                    .insert(key, expose(slot))\n                    .expect(\"insertions from one unique index to another cannot fail\")\n            }\n        }\n\n        new_index\n    }\n}\n\n#[cfg(test)]\npub(super) mod test {\n    use super::*;\n    use core::iter::repeat_with;\n    use spacetimedb_sats::layout::Size;\n\n    const FIXED_ROW_SIZE: Size = Size(4 * 4);\n\n    pub(crate) fn gen_row_pointers() -> impl Iterator<Item = RowPointer> {\n        let mut page_index = PageIndex(0);\n        let mut page_offset = PageOffset(0);\n        repeat_with(move || {\n            if page_offset.0 as usize + FIXED_ROW_SIZE.0 as usize >= PageOffset::PAGE_END.0 as usize {\n                // Consumed the page, let's use a new page.\n                page_index.0 += 1;\n                page_offset = PageOffset(0);\n            } else {\n                page_offset += FIXED_ROW_SIZE;\n            }\n\n            RowPointer::new(false, page_index, page_offset, SquashedOffset::COMMITTED_STATE)\n        })\n    }\n\n    #[test]\n    fn seek_range_gives_back_inserted() {\n        let range = (KEYS_PER_INNER - 2)..(KEYS_PER_INNER + 2);\n        let (keys, ptrs): (Vec<_>, Vec<_>) = range.clone().zip(gen_row_pointers()).unzip();\n\n        let mut index = UniqueDirectIndex::default();\n        for (key, ptr) in keys.iter().zip(&ptrs) {\n            index.insert(*key, *ptr).unwrap();\n        }\n        assert_eq!(index.num_rows(), 4);\n\n        let ptrs_found = index.seek_range(&range).collect::<Vec<_>>();\n        assert_eq!(ptrs, ptrs_found);\n    }\n\n    #[test]\n    fn inserting_again_errors() {\n        let range = (KEYS_PER_INNER - 2)..(KEYS_PER_INNER + 2);\n        let (keys, ptrs): (Vec<_>, Vec<_>) = range.zip(gen_row_pointers()).unzip();\n\n        let mut index = UniqueDirectIndex::default();\n        for (key, ptr) in keys.iter().zip(&ptrs) {\n            index.insert(*key, *ptr).unwrap();\n        }\n\n        for (key, ptr) in keys.iter().zip(&ptrs) {\n            assert_eq!(index.insert(*key, *ptr).unwrap_err(), *ptr)\n        }\n    }\n\n    #[test]\n    fn deleting_allows_reinsertion() {\n        let range = (KEYS_PER_INNER - 2)..(KEYS_PER_INNER + 2);\n        let (keys, ptrs): (Vec<_>, Vec<_>) = range.zip(gen_row_pointers()).unzip();\n\n        let mut index = UniqueDirectIndex::default();\n        for (key, ptr) in keys.iter().zip(&ptrs) {\n            index.insert(*key, *ptr).unwrap();\n        }\n        assert_eq!(index.num_rows(), 4);\n\n        let key = KEYS_PER_INNER + 1;\n        let ptr = index.seek_point(&key).next().unwrap();\n        assert!(index.delete(&key, ptr));\n        assert!(!index.delete(&key, ptr));\n        assert_eq!(index.num_rows(), 3);\n\n        index.insert(key, ptr).unwrap();\n        assert_eq!(index.num_rows(), 4);\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/unique_hash_index.rs",
    "content": "use super::{Index, KeySize};\nuse crate::table_index::uniquemap::UniquePointIter;\nuse crate::{indexes::RowPointer, table_index::key_size::KeyBytesStorage};\nuse core::hash::Hash;\nuse spacetimedb_data_structures::map::hash_map::Entry;\nuse spacetimedb_sats::memory_usage::MemoryUsage;\n\n// Faster than ahash, so we use this explicitly.\nuse foldhash::fast::RandomState;\nuse hashbrown::HashMap;\n\n/// A \"unique map\" that relates a `K` to a `RowPointer`\n///\n/// (This is just a `HashMap<K, RowPointer>`) with a slightly modified interface.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct UniqueHashIndex<K: KeySize + Eq + Hash> {\n    /// The map is backed by a `HashMap` for relating a key to a value.\n    map: HashMap<K, RowPointer, RandomState>,\n    /// Storage for [`Index::num_key_bytes`].\n    num_key_bytes: K::MemoStorage,\n}\n\nimpl<K: KeySize + Eq + Hash> Default for UniqueHashIndex<K> {\n    fn default() -> Self {\n        Self {\n            map: <_>::default(),\n            num_key_bytes: <_>::default(),\n        }\n    }\n}\n\nimpl<K: KeySize + Eq + Hash + MemoryUsage> MemoryUsage for UniqueHashIndex<K> {\n    fn heap_usage(&self) -> usize {\n        let Self { map, num_key_bytes } = self;\n        map.heap_usage() + num_key_bytes.heap_usage()\n    }\n}\n\nimpl<K: KeySize + Eq + Hash> Index for UniqueHashIndex<K> {\n    type Key = K;\n\n    fn clone_structure(&self) -> Self {\n        <_>::default()\n    }\n\n    fn insert(&mut self, key: Self::Key, ptr: RowPointer) -> Result<(), RowPointer> {\n        match self.map.entry(key) {\n            Entry::Vacant(e) => {\n                self.num_key_bytes.add_to_key_bytes::<Self>(e.key());\n                e.insert(ptr);\n                Ok(())\n            }\n            Entry::Occupied(e) => Err(*e.into_mut()),\n        }\n    }\n\n    fn delete(&mut self, key: &Self::Key, _: RowPointer) -> bool {\n        let ret = self.map.remove(key).is_some();\n        if ret {\n            self.num_key_bytes.sub_from_key_bytes::<Self>(key);\n        }\n        ret\n    }\n\n    fn clear(&mut self) {\n        self.map.clear();\n        self.num_key_bytes.reset_to_zero();\n    }\n\n    fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        let Some(found) = other\n            .map\n            .keys()\n            .find_map(|key| self.map.get(key).filter(|val| !ignore(val)))\n        else {\n            return Ok(());\n        };\n        Err(*found)\n    }\n\n    fn num_keys(&self) -> usize {\n        self.map.len()\n    }\n\n    fn num_key_bytes(&self) -> u64 {\n        self.num_key_bytes.get(self)\n    }\n\n    type PointIter<'a>\n        = UniquePointIter\n    where\n        Self: 'a;\n\n    fn seek_point(&self, point: &Self::Key) -> Self::PointIter<'_> {\n        UniquePointIter::new(self.map.get(point).copied())\n    }\n}\n"
  },
  {
    "path": "crates/table/src/table_index/uniquemap.rs",
    "content": "use super::{Index, KeySize, RangedIndex};\nuse crate::{indexes::RowPointer, table_index::key_size::KeyBytesStorage};\nuse core::{ops::RangeBounds, option::IntoIter};\nuse spacetimedb_sats::memory_usage::MemoryUsage;\nuse std::collections::btree_map::{BTreeMap, Entry, Range};\n\n/// A \"unique map\" that relates a `K` to a `RowPointer`.\n///\n/// (This is just a `BTreeMap<K, RowPointer>`) with a slightly modified interface.\n#[derive(Debug, PartialEq, Eq, Clone)]\npub struct UniqueMap<K: KeySize> {\n    /// The map is backed by a `BTreeMap` for relating a key to a value.\n    map: BTreeMap<K, RowPointer>,\n    /// Storage for [`Index::num_key_bytes`].\n    num_key_bytes: K::MemoStorage,\n}\n\nimpl<K: KeySize> Default for UniqueMap<K> {\n    fn default() -> Self {\n        Self {\n            map: <_>::default(),\n            num_key_bytes: <_>::default(),\n        }\n    }\n}\n\nimpl<K: KeySize + MemoryUsage> MemoryUsage for UniqueMap<K> {\n    fn heap_usage(&self) -> usize {\n        let Self { map, num_key_bytes } = self;\n        map.heap_usage() + num_key_bytes.heap_usage()\n    }\n}\n\nimpl<K: Ord + KeySize> Index for UniqueMap<K> {\n    type Key = K;\n\n    fn clone_structure(&self) -> Self {\n        Self::default()\n    }\n\n    fn insert(&mut self, key: K, val: RowPointer) -> Result<(), RowPointer> {\n        match self.map.entry(key) {\n            Entry::Vacant(e) => {\n                self.num_key_bytes.add_to_key_bytes::<Self>(e.key());\n                e.insert(val);\n                Ok(())\n            }\n            Entry::Occupied(e) => Err(*e.into_mut()),\n        }\n    }\n\n    fn delete(&mut self, key: &K, _: RowPointer) -> bool {\n        let ret = self.map.remove(key).is_some();\n        if ret {\n            self.num_key_bytes.sub_from_key_bytes::<Self>(key);\n        }\n        ret\n    }\n\n    fn num_keys(&self) -> usize {\n        self.map.len()\n    }\n\n    fn num_key_bytes(&self) -> u64 {\n        self.num_key_bytes.get(self)\n    }\n\n    type PointIter<'a>\n        = UniquePointIter\n    where\n        Self: 'a;\n\n    fn seek_point(&self, point: &Self::Key) -> Self::PointIter<'_> {\n        UniquePointIter::new(self.map.get(point).copied())\n    }\n\n    /// Deletes all entries from the map, leaving it empty.\n    ///\n    /// Unfortunately, this will drop the existing allocation.\n    fn clear(&mut self) {\n        self.map.clear();\n        self.num_key_bytes.reset_to_zero();\n    }\n\n    fn can_merge(&self, other: &Self, ignore: impl Fn(&RowPointer) -> bool) -> Result<(), RowPointer> {\n        let Some(found) = other\n            .map\n            .keys()\n            .find_map(|key| self.map.get(key).filter(|val| !ignore(val)))\n        else {\n            return Ok(());\n        };\n        Err(*found)\n    }\n}\n\n/// An iterator over the potential value in a [`UniqueMap`] for a given key.\npub struct UniquePointIter {\n    /// The iterator seeking for matching keys in the range.\n    pub(super) iter: IntoIter<RowPointer>,\n}\n\nimpl UniquePointIter {\n    /// Returns a new iterator over the possibly found row pointer.\n    pub fn new(point: Option<RowPointer>) -> Self {\n        let iter = point.into_iter();\n        Self { iter }\n    }\n}\n\nimpl Iterator for UniquePointIter {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next()\n    }\n}\n\nimpl<K: Ord + KeySize> RangedIndex for UniqueMap<K> {\n    type RangeIter<'a>\n        = UniqueMapRangeIter<'a, K>\n    where\n        Self: 'a;\n\n    fn seek_range(&self, range: &impl RangeBounds<Self::Key>) -> Self::RangeIter<'_> {\n        UniqueMapRangeIter {\n            iter: self.map.range((range.start_bound(), range.end_bound())),\n        }\n    }\n}\n\n/// An iterator over values in a [`UniqueMap`] where the keys are in a certain range.\n#[derive(Clone)]\npub struct UniqueMapRangeIter<'a, K> {\n    /// The iterator seeking for matching keys in the range.\n    iter: Range<'a, K, RowPointer>,\n}\n\nimpl<'a, K> Iterator for UniqueMapRangeIter<'a, K> {\n    type Item = RowPointer;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next().map(|(_, v)| *v)\n    }\n}\n"
  },
  {
    "path": "crates/table/src/util.rs",
    "content": "use core::ops::Range;\n\n/// Translates the range `r` by adding `by` to both its `start` and its `end`.\n///\n/// The resulting range will have the same length as `r`.\npub const fn range_move(r: Range<usize>, by: usize) -> Range<usize> {\n    (r.start + by)..(r.end + by)\n}\n\n/// Asserts that `$ty` is `$size` bytes in `static_assert_size($ty, $size)`.\n///\n/// Example:\n///\n/// ```ignore\n/// static_assert_size!(u32, 4);\n/// ```\n#[macro_export]\nmacro_rules! static_assert_size {\n    ($ty:ty, $size:expr) => {\n        const _: [(); $size] = [(); ::core::mem::size_of::<$ty>()];\n    };\n}\n\n/// Asserts that `$ty` is aligned at `$align` bytes in `static_assert_align($ty, $align)`.\n///\n/// Example:\n///\n/// ```ignore\n/// static_assert_align!(u32, 2);\n/// ```\n#[macro_export]\nmacro_rules! static_assert_align {\n    ($ty:ty, $align:expr) => {\n        const _: [(); $align] = [(); ::core::mem::align_of::<$ty>()];\n    };\n}\n"
  },
  {
    "path": "crates/table/src/var_len.rs",
    "content": "//! Provides the definitions of [`VarLenRef`], [`VarLenGranule`], and [`VarLenMembers`].\n//!\n//! We allocate variable-length objects within rows, e.g. strings and arrays,\n//! separately from the fixed-length parts of rows.\n//! The fixed-length part of the page starts at the top (offset 0) and grows downward,\n//! while the var-length part of the page starts at the bottom (largest offset) and grows upward.\n//!\n//! Within the fixed-length part of the row, each var-len object is given a [`VarLenRef`],\n//! which allows a mutator to locate the var-len object.\n//!\n//! The var-length objects are BSATN-encoded to produce a bytestring\n//! (except strings, which are stored directly as UTF-8 bytestrings),\n//! and stored in a linked list of 64-byte \"granules,\"\n//! each of which has a 2-byte header and up to 62 bytes of data.\n//! This means that var-length objects never store padding bytes;\n//! every byte in a var-len object at an index less than the object's length\n//! will be initialized.\n//!\n//! At various points in the row's lifecycle,\n//! we must visit all of the `VarLenRef`s within the row,\n//! e.g. to fix-up pointers when copying a row into a new page.\n//! This process is driven by a `VarLenMembers` visitor.\n//!\n//! This file defines the representation of the linked list of granules [`VarLenGranule`],\n//! the [`VarLenRef`] pointers to variable-length objects,\n//! and the trait [`VarLenMembers`] which visits `VarLenRef`s within a fixed-length row.\n//!\n//! The broad strokes of var-len allocation are described in the Mem Arch Redesign proposal,\n// Intentionally not a link, in case we ever want to publish this crate.\n//! `../../../../proposals/0001-mem-arch-redesign/mem-arch-redesign.md`.\n//! Note that the proposal uses the words \"blocks\" or \"chunks\" where we use \"granules.\"\n\nuse super::{\n    blob_store::BlobHash,\n    indexes::{Byte, Bytes, PageOffset},\n};\nuse crate::{static_assert_align, static_assert_size};\nuse core::iter;\nuse core::marker::PhantomData;\nuse core::mem::{self};\nuse spacetimedb_sats::layout::{Size, VAR_LEN_REF_LAYOUT};\n\n/// Reference to var-len object within a page.\n// TODO: make this larger and do short-string optimization?\n// - Or store a few elts inline and then a `VarLenRef`?\n// - Or first store `VarLenRef` that records num inline elements\n//   (remaining inline \"uninit,\" actually valid-unconstrained)\n//  (bitfield; only need 10 bits for `len_in_bytes`)?\n#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\n#[repr(C)]\npub struct VarLenRef {\n    /// The length of the var-len object in bytes.\n    /// When `self.is_large_blob()` returns true,\n    /// this is not the proper length of the object.\n    /// Rather, the blob store must be consulted for the true length.\n    pub length_in_bytes: u16,\n    /// The offset to the first granule containing some of the object's data\n    /// as well a pointer to the next granule.\n    pub first_granule: PageOffset,\n}\n\n// Implementation of `VarLenMembers` visitors depend on\n// `size = 4` and `align = 2` of `VarLenRef`.\nstatic_assert_size!(VarLenRef, 4);\nstatic_assert_align!(VarLenRef, 2);\n\nconst _: () = assert!(VAR_LEN_REF_LAYOUT.size as usize == mem::size_of::<VarLenRef>());\nconst _: () = assert!(VAR_LEN_REF_LAYOUT.align as usize == mem::align_of::<VarLenRef>());\n\nimpl VarLenRef {\n    /// Does this refer to a large blob object\n    /// where `self.first_granule` is a blob hash?\n    #[inline]\n    pub const fn is_large_blob(self) -> bool {\n        self.length_in_bytes == Self::LARGE_BLOB_SENTINEL\n    }\n\n    /// The sentinel for a var-len ref to a large blob.\n    pub const LARGE_BLOB_SENTINEL: u16 = u16::MAX;\n\n    /// Returns a var-len ref for a large blob object.\n    #[inline]\n    pub const fn large_blob(first_granule: PageOffset) -> Self {\n        Self {\n            length_in_bytes: Self::LARGE_BLOB_SENTINEL,\n            first_granule,\n        }\n    }\n\n    /// Returns the number of granules this var-len ref uses in it page.\n    #[inline]\n    pub const fn granules_used(&self) -> usize {\n        VarLenGranule::bytes_to_granules(self.length_in_bytes as usize).0\n    }\n\n    /// Is this reference NULL, i.e. empty?\n    #[inline]\n    pub const fn is_null(self) -> bool {\n        self.first_granule.is_var_len_null()\n    }\n\n    /// The NULL var-len reference for empty variable components.\n    ///\n    /// A null `VarLenRef` can occur when a row has no var-len component\n    /// or needs to point to one that is empty.\n    pub const NULL: Self = Self {\n        length_in_bytes: 0,\n        first_granule: PageOffset::VAR_LEN_NULL,\n    };\n}\n\nconst _BLOB_SENTINEL_MORE_THAN_MAX_OBJ_SIZE: () =\n    assert!(VarLenGranule::OBJECT_SIZE_BLOB_THRESHOLD < VarLenRef::LARGE_BLOB_SENTINEL as usize);\n\nconst _GRANULES_USED_FOR_BLOB_IS_CONSISTENT: () = {\n    let vlr = VarLenRef::large_blob(PageOffset::VAR_LEN_NULL);\n    assert!(vlr.is_large_blob() == (vlr.granules_used() == 1));\n};\n\n/// Returns whether `offset` is properly aligned for storing a [`VarLenGranule`].\npub fn is_granule_offset_aligned(offset: PageOffset) -> bool {\n    offset.0 == offset.0 & VarLenGranuleHeader::NEXT_BITMASK\n}\n\n/// The header of a [`VarLenGranule`] storing\n/// - (low 6 bits) the number of bytes the granule contains\n/// - (high 10 bits) the offset of the next granule in the linked-list\n///   used to store an object in variable storage.\n///\n/// For efficiency, this data is packed as a bitfield\n/// in a `u16` with bits used per above.\n#[derive(Copy, Clone)]\npub struct VarLenGranuleHeader(u16);\n\nimpl VarLenGranuleHeader {\n    /// The total size of a variable granule's header in bytes.\n    const SIZE: usize = mem::size_of::<Self>();\n\n    /// The number of bits used to store the `len` of a [`VarLenGranule`] is 6.\n    const LEN_BITS: u16 = 6;\n\n    /// The `len` of a [`VarLenGranule`] is stored in the low 6 bits.\n    ///\n    /// The 6 bits are enough to store at most `2^6` (`64`).\n    /// However, a granule can never store more than [`VarLenGranule::DATA_SIZE`] (`62`),\n    /// which is `2` less than `2^6`.\n    ///\n    /// We will also never allocate a `VarLenGranule` with len 0.\n    ///\n    /// This means that the `len` field of a `VarLenGranule` has two dead states,\n    /// 0 and 63. We could use these as sentinels,\n    /// but currently have no use for them.\n    const LEN_BITMASK: u16 = (1 << Self::LEN_BITS) - 1;\n\n    /// The [`LEN_BITMASK`] will preserve all granule lengths possible.\n    #[allow(clippy::assertions_on_constants)]\n    const _ASSERT_LEN_BITMASK_FITS_ALL_POSSIBLE_GRANULE_LENGTHS: () =\n        assert!(VarLenGranule::DATA_SIZE <= Self::LEN_BITMASK as usize);\n\n    // The `next` of a `VarLenGranule` is stored in the high 10 bits.\n    // It is not shifted; the low 6 bits will always be 0 due to alignment.\n    const NEXT_BITMASK: u16 = !Self::LEN_BITMASK;\n\n    /// Returns a new header with the length component changed to `len`.\n    fn with_len(self, len: u8) -> Self {\n        // Zero any previous `len` field.\n        let mut new = self;\n        new.0 &= !Self::LEN_BITMASK;\n\n        // Ensure that the `len` doesn't overflow into the `next`.\n        let capped_len = (len as u16) & Self::LEN_BITMASK;\n        debug_assert_eq!(\n            capped_len, len as u16,\n            \"Len {len} overflows the length of a `VarLenGranule`\",\n        );\n\n        // Insert the truncated `len`.\n        new.0 |= capped_len;\n\n        debug_assert_eq!(self.next(), new.next(), \"`set_len` has modified `next`\");\n        debug_assert_eq!(\n            new.len() as u16,\n            capped_len,\n            \"`set_len` has not inserted the correct `len`: expected {:x}, found {:x}\",\n            capped_len,\n            new.len()\n        );\n\n        new\n    }\n\n    /// Returns a new header with the next-granule component changed to `next`.\n    fn with_next(self, PageOffset(next): PageOffset) -> Self {\n        let mut new = self;\n\n        // Zero any previous `next` field.\n        new.0 &= !Self::NEXT_BITMASK;\n\n        // Ensure that the `next` is aligned,\n        // and therefore doesn't overwrite any of the `len`.\n        let aligned_next = next & Self::NEXT_BITMASK;\n        debug_assert_eq!(aligned_next, next, \"Next {next:x} is unaligned\");\n\n        // Insert the aligned `next`.\n        new.0 |= aligned_next;\n\n        debug_assert_eq!(self.len(), new.len(), \"`set_next` has modified `len`\");\n        debug_assert_eq!(\n            new.next().0,\n            aligned_next,\n            \"`set_next` has not inserted the correct `next`\"\n        );\n\n        new\n    }\n\n    /// Returns a new header for a granule storing `len` bytes\n    /// and with the next granule in the list located `next`.\n    pub fn new(len: u8, next: PageOffset) -> Self {\n        Self(0).with_len(len).with_next(next)\n    }\n\n    /// Returns the number of bytes the granule contains.\n    const fn len(&self) -> u8 {\n        (self.0 & Self::LEN_BITMASK) as u8\n    }\n\n    /// Returns the offset / Address of the next granule in the linked-list.\n    pub const fn next(&self) -> PageOffset {\n        PageOffset(self.0 & Self::NEXT_BITMASK)\n    }\n}\n\n/// Each variable length object in a page is stored as a linked-list of chunks.\n/// These chunks are called *granules* and they can store up to 62 bytes of `data`.\n/// Additionally, 2 bytes are used for the [`header: VarLenGranuleHeader`](VarLenGranuleHeader).\n#[repr(C)] // Required for a stable ABI.\n#[repr(align(64))] // Alignment must be same as `VarLenGranule::SIZE`.\npub struct VarLenGranule {\n    /// The header of the granule, containing the length and the next-cell offset.\n    pub header: VarLenGranuleHeader,\n    /// The data storing some part, or whole, of the var-len object.\n    pub data: [Byte; Self::DATA_SIZE],\n}\n\nimpl VarLenGranule {\n    /// The total size of a variable length granule in bytes.\n    pub const SIZE: Size = Size(64);\n\n    /// The size, in bytes, of the data section of a variable length granule.\n    pub const DATA_SIZE: usize = Self::SIZE.len() - VarLenGranuleHeader::SIZE;\n\n    /// The max number of granules an object can use\n    /// before being put into large blob storage.\n    pub const OBJECT_MAX_GRANULES_BEFORE_BLOB: usize = 16;\n\n    /// The max size of an object before being put into large blob storage.\n    pub const OBJECT_SIZE_BLOB_THRESHOLD: usize = Self::DATA_SIZE * Self::OBJECT_MAX_GRANULES_BEFORE_BLOB;\n\n    /// Returns the number of granules that would fit into `available_len`.\n    pub const fn space_to_granules(available_len: Size) -> usize {\n        // Floor division (the default div operator) here\n        // to ensure we don't allocate e.g., a 64-byte granule in a 63-byte space.\n        available_len.len() / Self::SIZE.len()\n    }\n\n    /// Returns the number of granules needed to store an object of `len_in_bytes` in size.\n    /// Also returns whether the object needs to go into the blob store.\n    pub const fn bytes_to_granules(len_in_bytes: usize) -> (usize, bool) {\n        if len_in_bytes > VarLenGranule::OBJECT_SIZE_BLOB_THRESHOLD {\n            // If `obj` is large enough to go in the blob store,\n            // you require space for a blob-hash,\n            // rather than the whole object.\n            // A blob hash fits in a single granule as BLAKE3 needs 32 bytes < 62 bytes.\n            (1, true)\n        } else {\n            // Using `div_ceil` here to ensure over- rather than under-allocation.\n            (len_in_bytes.div_ceil(Self::DATA_SIZE), false)\n        }\n    }\n\n    /// Chunks `bytes` into an iterator where each element fits into a granule.\n    pub fn chunks(bytes: &[u8]) -> impl DoubleEndedIterator<Item = &[u8]> {\n        bytes.chunks(Self::DATA_SIZE)\n    }\n\n    /// Returns the data from the var-len object in this granule.\n    pub fn data(&self) -> &[u8] {\n        let len = self.header.len() as usize;\n        &self.data[0..len]\n    }\n\n    /// Assumes that the granule stores a [`BlobHash`] and returns it.\n    ///\n    /// Panics if the assumption is wrong.\n    pub fn blob_hash(&self) -> BlobHash {\n        self.data().try_into().unwrap()\n    }\n}\n\n/// A single [`VarLenGranule`] is needed to store a [`BlobHash`].\n#[allow(clippy::assertions_on_constants)]\nconst _VLG_CAN_STORE_BLOB_HASH: () = assert!(VarLenGranule::DATA_SIZE >= BlobHash::SIZE);\n\n/// A visitor object which can iterate over the var-len slots in a row.\n///\n/// Each var-len visitor is specialized to a particular row type,\n/// though implementors of `VarLenMembers` decide whether this specialization\n/// is per instance or per type.\n///\n/// The trivial implementor of `VarLenMembers` is [`AlignedVarLenOffsets`],\n/// which stores the offsets of var-len members in a particular row type in a slice,\n/// and uses pointer arithmetic to return references to them.\n///\n/// # Safety\n///\n/// - `Self::visit_var_len` and `Self::visit_var_len_mut`\n///   must visit the same set of `VarLenRef`s in the same order.\n///   Various consumers in `Page` and friends depend on this and the previous requirement.\npub unsafe trait VarLenMembers {\n    /// The iterator type returned by [`VarLenMembers::visit_var_len`].\n    type Iter<'this, 'row>: Iterator<Item = &'row VarLenRef>\n    where\n        Self: 'this;\n\n    /// The iterator type returned by [`VarLenMembers::visit_var_len_mut`].\n    type IterMut<'this, 'row>: Iterator<Item = &'row mut VarLenRef>\n    where\n        Self: 'this;\n\n    /// Treats `row` as storage for a row of the particular type handled by `self`,\n    /// and iterates over the (possibly stale) `VarLenRef`s within it.\n    ///\n    /// Visited `VarLenRef`s are valid-unconstrained\n    /// and will always be valid from Rust/LLVM's perspective,\n    /// i.e. will never be uninit,\n    /// but will not necessarily point to properly-allocated `VarLenGranule`s.\n    ///\n    /// Callers are responsible for maintaining whether var-len members have been initialized.\n    ///\n    /// # Safety\n    ///\n    /// - `row` must be properly aligned for the row type.\n    ///   This alignment constraint should be defined (and documented!)\n    ///   by the implementor of `VarLenMembers`.\n    ///\n    /// - `row` must further be a slice of exactly the number of bytes of the row type.\n    ///   Implementors may or may not check this property via `debug_assert!`,\n    ///   but callers *must always* ensure it for safety.\n    ///   These invariants allow us to construct references to [`VarLenRef`]s inside the slice.\n    ///\n    ///   Note that `Iterator::next` is a safe function,\n    ///   so it must always be valid to advance an iterator to its end.\n    ///\n    /// - All callers of `visit_var_len` on a particular `row`\n    ///   must visit the same set of `VarLenRef`s in the same order,\n    ///   though they may do so through different implementors of `VarLenMembers`.\n    ///   E.g. it would be valid to use an `AlignedVarLenOffsets` to initialize a row,\n    ///   then later read from it using a hypothetical optimized JITted visitor,\n    ///   provided the JITted visitor visited the same set of offsets.\n    unsafe fn visit_var_len_mut<'this, 'row>(&'this self, row: &'row mut Bytes) -> Self::IterMut<'this, 'row>;\n\n    /// Treats `row` as storage for a row of the particular type handled by `self`,\n    /// and iterates over the (possibly stale) `VarLenRef`s within it.\n    ///\n    /// Visited `VarLenRef`s are valid-unconstrained\n    /// and will always be valid from Rust/LLVM's perspective,\n    /// i.e. will never be uninit,\n    /// but will not necessarily point to properly-allocated `VarLenGranule`s.\n    ///\n    /// Callers are responsible for maintaining whether var-len members have been initialized.\n    ///\n    /// # Safety\n    ///\n    /// - `row` must be properly aligned for the row type.\n    ///   This alignment constraint should be defined (and documented!)\n    ///   by the implementor of `VarLenMembers`.\n    ///\n    /// - `row` must further be a slice of exactly the number of bytes of the row type.\n    ///   Implementors may or may not check this property via `debug_assert!`,\n    ///   but callers *must always* ensure it for safety.\n    ///   These invariants allow us to construct references to [`VarLenRef`]s inside the slice.\n    ///\n    ///   Note that `Iterator::next` is a safe function,\n    ///   so it must always be valid to advance an iterator to it end.\n    ///\n    /// - All callers of `visit_var_len` on a particular `row`\n    ///   must visit the same set of `VarLenRef`s in the same order,\n    ///   though they may do so through different implementors of `VarLenMembers`.\n    ///   E.g. it would be valid to use an `AlignedVarLenOffsets` to initialize a row,\n    ///   then later read from it using a hypothetical optimized JITted visitor,\n    ///   provided the JITted visitor visited the same set of offsets.\n    unsafe fn visit_var_len<'this, 'row>(&'this self, row: &'row Bytes) -> Self::Iter<'this, 'row>;\n}\n\n/// Slice of offsets to var-len members, in units of 2-byte words.\n///\n/// This type is intended as a demonstration of the `VarLenMembers` interface,\n/// and is used in testing and benchmarking.\n///\n/// Note that this visitor is not suitable for sum types, or for types which contain sums.\n///\n/// Units of 2-byte words because `VarLenRef` is 2-byte aligned.\n/// Note that `VarLenRef` is 4 bytes wide, but only 2-byte aligned.\n///\n/// The listed offsets must not overlap, i.e. there must be a gap of at least 2 between each offset.\n///\n/// For `AlignedVarLenOffsets([n])`, a 4-byte `VarLenRef` exists in each row at +2n bytes.\n///\n/// e.g.:\n/// `AlignedVarLenOffsets([0, 4])`\n/// has:\n/// row >= 12 bytes,\n/// - var-len ref at +0..4 bytes (i.e. +0..2 `u16`s).\n/// - fixed-len field(s) at +4..8 bytes (i.e. +2..4 `u16`s).\n/// - var-len ref at +8..12 bytes (i.e. +4..6 `u16`s).\n/// - fixed-len field(s) at +12.. (i.e. +6.. `u16`s), if row_size > 12.\n#[derive(Copy, Clone)]\npub struct AlignedVarLenOffsets<'a>(&'a [u16]);\n\nimpl<'a> AlignedVarLenOffsets<'a> {\n    /// Returns an [`AlignedVarLenOffsets`] using `offsets`.\n    pub const fn from_offsets(offsets: &'a [u16]) -> Self {\n        Self(offsets)\n    }\n}\n\n// SAFETY: `visit_var_len` and `visit_var_len_mut` are only different\n// in that they yield `&` vs. `&mut` and are otherwise identical.\nunsafe impl VarLenMembers for AlignedVarLenOffsets<'_> {\n    type Iter<'this, 'row>\n        = AlignedVarLenOffsetsIter<'this, 'row>\n    where\n        Self: 'this;\n\n    type IterMut<'this, 'row>\n        = AlignedVarLenOffsetsIterMut<'this, 'row>\n    where\n        Self: 'this;\n\n    /// # Safety\n    ///\n    /// `row` must be 2-byte aligned.\n    ///\n    /// `row` must be an allocation of at least `2n + 2` bytes,\n    /// where `n` is the largest offset in `self`.\n    ///\n    /// All callers of `visit_var_len` on a particular `row`\n    /// must visit the same set of `VarLenRef`s,\n    /// though they may do so through different implementors of `VarLenMembers`.\n    /// E.g. it would be valid to use an `AlignedVarLenOffsets` to initialize a row,\n    /// then later read from it using a hypothetical optimized JITted visitor,\n    /// provided the JITted visitor visited the same set of offsets.\n    unsafe fn visit_var_len<'this, 'row>(&'this self, row: &'row Bytes) -> Self::Iter<'this, 'row> {\n        AlignedVarLenOffsetsIter {\n            offsets: self,\n            _row_lifetime: PhantomData,\n            row: row.as_ptr(),\n            next_offset_idx: 0,\n        }\n    }\n\n    /// # Safety\n    ///\n    /// `row` must be 2-byte aligned.\n    ///\n    /// `row` must be an allocation of at least `2n + 2` bytes,\n    /// where `n` is the largest offset in `self`.\n    ///\n    /// All callers of `visit_var_len` on a particular `row`\n    /// must visit the same set of `VarLenRef`s,\n    /// though they may do so through different implementors of `VarLenMembers`.\n    /// E.g. it would be valid to use an `AlignedVarLenOffsets` to initialize a row,\n    /// then later read from it using a hypothetical optimized JITted visitor,\n    /// provided the JITted visitor visited the same set of offsets.\n    unsafe fn visit_var_len_mut<'this, 'row>(&'this self, row: &'row mut Bytes) -> Self::IterMut<'this, 'row> {\n        AlignedVarLenOffsetsIterMut {\n            offsets: self,\n            _row_lifetime: PhantomData,\n            row: row.as_mut_ptr(),\n            next_offset_idx: 0,\n        }\n    }\n}\n\npub struct AlignedVarLenOffsetsIter<'offsets, 'row> {\n    offsets: &'offsets AlignedVarLenOffsets<'offsets>,\n    _row_lifetime: PhantomData<&'row Bytes>,\n    row: *const Byte,\n    next_offset_idx: usize,\n}\n\nimpl<'row> Iterator for AlignedVarLenOffsetsIter<'_, 'row> {\n    type Item = &'row VarLenRef;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.next_offset_idx >= self.offsets.0.len() {\n            None\n        } else {\n            // I sure would like to be able to write `self.next_offset_idx.post_increment(1)`...\n            // - pgoldman(2023-11-16).\n            let curr_offset_idx = self.next_offset_idx;\n            self.next_offset_idx += 1;\n\n            // SAFETY: `AlignedVarLenOffsets::visit_var_len`'s safety requirements\n            //         mean that `row` is always 2-byte aligned, so this will be too,\n            //         and that `row` is large enough for all the `offsets`,\n            //         so this `add` is always in-bounds.\n            let elt_ptr: *const VarLenRef =\n                unsafe { self.row.add(curr_offset_idx * mem::align_of::<VarLenRef>()).cast() };\n\n            // SAFETY: `elt_ptr` is aligned and inbounds.\n            //         Any pattern of init bytes is valid at `VarLenRef`,\n            //         and the `row_data` in a `Page` is never uninit,\n            //         so it's safe to create an `&mut` to any value in the page,\n            //         though the resulting `VarLenRef` may be garbage.\n            Some(unsafe { &*elt_ptr })\n        }\n    }\n}\n\npub struct AlignedVarLenOffsetsIterMut<'offsets, 'row> {\n    offsets: &'offsets AlignedVarLenOffsets<'offsets>,\n    _row_lifetime: PhantomData<&'row mut Bytes>,\n    row: *mut Byte,\n    next_offset_idx: usize,\n}\n\nimpl<'row> Iterator for AlignedVarLenOffsetsIterMut<'_, 'row> {\n    type Item = &'row mut VarLenRef;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.next_offset_idx >= self.offsets.0.len() {\n            None\n        } else {\n            // I sure would like to be able to write `self.next_offset_idx.post_increment(1)`...\n            // - pgoldman(2023-11-16).\n            let curr_offset_idx = self.next_offset_idx;\n            self.next_offset_idx += 1;\n\n            // SAFETY: `AlignedVarLenOffsets::visit_var_len`'s safety requirements\n            //         mean that `row` is always 2-byte aligned, so this will be too,\n            //         and that `row` is large enough for all the `offsets`,\n            //         so this `add` is always in-bounds.\n            let elt_ptr: *mut VarLenRef =\n                unsafe { self.row.add(curr_offset_idx * mem::align_of::<VarLenRef>()).cast() };\n\n            // SAFETY: `elt_ptr` is aligned and inbounds.\n            //         Any pattern of init bytes is valid at `VarLenRef`,\n            //         and the `row_data` in a `Page` is never uninit,\n            //         so it's safe to create an `&mut` to any value in the page,\n            //         though the resulting `VarLenRef` may be garbage.\n            Some(unsafe { &mut *elt_ptr })\n        }\n    }\n}\n\n/// A `VarLenMembers` visitor for row types with no var-len components,\n/// which never visits anything.\n#[derive(Copy, Clone)]\npub struct NullVarLenVisitor;\n\n// SAFETY: Both `visit_var_len` and `visit_var_len_mut` visit the empty set.\nunsafe impl VarLenMembers for NullVarLenVisitor {\n    type Iter<'this, 'row> = iter::Empty<&'row VarLenRef>;\n    type IterMut<'this, 'row> = iter::Empty<&'row mut VarLenRef>;\n\n    unsafe fn visit_var_len<'this, 'row>(&'this self, _row: &'row Bytes) -> Self::Iter<'this, 'row> {\n        iter::empty()\n    }\n\n    unsafe fn visit_var_len_mut<'this, 'row>(&'this self, _row: &'row mut Bytes) -> Self::IterMut<'this, 'row> {\n        iter::empty()\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use proptest::prelude::*;\n\n    fn generate_var_len_offset() -> impl Strategy<Value = PageOffset> {\n        (0u16..(1 << 10)).prop_map(|unaligned| PageOffset(unaligned * VarLenGranule::SIZE.0))\n    }\n\n    fn generate_len() -> impl Strategy<Value = u8> {\n        0..(VarLenGranule::DATA_SIZE as u8)\n    }\n\n    proptest! {\n        #[test]\n        fn granule_header_bitbashing(len in generate_len(), next in generate_var_len_offset(), len2 in generate_len(), next2 in generate_var_len_offset()) {\n            let header = VarLenGranuleHeader::new(len, next);\n            prop_assert_eq!(len, header.len());\n            prop_assert_eq!(next, header.next());\n\n            let header_new_len = header.with_len(len2);\n            prop_assert_eq!(len2, header_new_len.len());\n            prop_assert_eq!(next, header_new_len.next());\n\n            let header_new_next = header.with_next(next2);\n            prop_assert_eq!(len, header_new_next.len());\n            prop_assert_eq!(next2, header_new_next.next());\n\n            prop_assert_eq!(header_new_len.with_next(next2).0, header_new_next.with_len(len2).0);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/testing/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-testing\"\nversion.workspace = true\nedition.workspace = true\nlicense-file = \"LICENSE\"\npublish = false\n\n[features]\nallow_loopback_http_for_tests = [\"spacetimedb-standalone/allow_loopback_http_for_tests\"]\n\n[dependencies]\nspacetimedb-cli.workspace = true\nspacetimedb-data-structures.workspace = true\nspacetimedb-lib.workspace = true\nspacetimedb-core.workspace = true\nspacetimedb-standalone.workspace = true\nspacetimedb-client-api.workspace = true\nspacetimedb-client-api-messages.workspace = true\nspacetimedb-paths.workspace = true\nspacetimedb-schema.workspace = true\n\nanyhow.workspace = true\nbytes.workspace = true\nenv_logger.workspace = true\nlog.workspace = true\nclap.workspace = true\nserde_json.workspace = true\ntokio.workspace = true\nwasmbin.workspace = true\nduct.workspace = true\nlazy_static.workspace = true\nrand.workspace = true\ntempfile.workspace = true\nserde.workspace = true\nfutures.workspace = true\n\n[dev-dependencies]\nserial_test.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/testing/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/testing/src/lib.rs",
    "content": "use clap::Command as ClapCommand;\nuse spacetimedb::messages::control_db::HostType;\nuse spacetimedb_cli::Config;\nuse spacetimedb_paths::SpacetimePaths;\nuse spacetimedb_schema::def::ModuleDef;\nuse std::env;\nuse std::process::Command;\n\npub mod modules;\npub mod sdk;\n\n#[track_caller]\npub fn invoke_cli(paths: &SpacetimePaths, args: &[&str]) {\n    lazy_static::lazy_static! {\n        static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Builder::new_multi_thread()\n            .enable_all()\n            .build()\n            .unwrap();\n        static ref COMMAND: ClapCommand = ClapCommand::new(\"spacetime\").no_binary_name(true).subcommands(spacetimedb_cli::get_subcommands());\n    }\n\n    // Parse once so we can decide which path to use.\n    let matches = COMMAND.clone().get_matches_from(args);\n    let (cmd, sub_args) = matches.subcommand().expect(\"Could not split subcommand and args\");\n\n    // If CUSTOM_SPACETIMEDB == true, try to use CUSTOM_SPACETIMEDB_PATH and shell out.\n    let use_custom = env::var(\"CUSTOM_SPACETIMEDB\")\n        .map(|v| v.eq_ignore_ascii_case(\"true\"))\n        .unwrap_or(false);\n\n    if use_custom\n        && cmd == \"generate\"\n        && let Ok(custom_path) = env::var(\"CUSTOM_SPACETIMEDB_PATH\")\n    {\n        // Call the dev CLI exactly like the manual command:\n        // cargo run --bin spacetimedb-cli -- generate ...\n        let status = Command::new(\"cargo\")\n            .current_dir(&custom_path) // Ensure we run in the custom path directory\n            .arg(\"run\")\n            .arg(\"--bin\")\n            .arg(\"spacetimedb-cli\")\n            .arg(\"--\")\n            .args(args) // `args` are like [\"generate\", \"--lang\", ...]\n            .status()\n            .expect(\"Failed to run custom SpacetimeDB CLI via cargo\");\n\n        assert!(status.success(), \"Custom SpacetimeDB CLI failed\");\n        return;\n    }\n    // If CUSTOM_SPACETIMEDB_PATH is missing, fall through to the default behavior.\n\n    // Default: run in-process CLI (fast/path-friendly for tests).\n    let config = Config::new_with_localhost(paths.cli_config_dir.cli_toml());\n    RUNTIME\n        .block_on(async {\n            if cmd == \"generate\" {\n                spacetimedb_cli::generate::exec_ex(config, sub_args, extract_descriptions, false, None).await\n            } else {\n                spacetimedb_cli::exec_subcommand(config, paths, None, cmd, sub_args)\n                    .await\n                    .map(drop)\n            }\n        })\n        .unwrap();\n}\n\n// spacetime generate would usually shell out to spacetimedb-standalone,\n// but that won't work in a testing environment\nfn extract_descriptions(wasm_file: &std::path::Path) -> anyhow::Result<ModuleDef> {\n    tokio::task::block_in_place(|| {\n        let program_bytes = std::fs::read(wasm_file)?;\n        tokio::runtime::Handle::current().block_on(spacetimedb::host::extract_schema(\n            program_bytes.into(),\n            match wasm_file.extension().unwrap().to_str().unwrap() {\n                \"wasm\" => HostType::Wasm,\n                \"js\" => HostType::Js,\n                _ => unreachable!(),\n            },\n        ))\n    })\n}\n"
  },
  {
    "path": "crates/testing/src/modules.rs",
    "content": "use std::env;\nuse std::future::Future;\nuse std::path::{Path, PathBuf};\nuse std::sync::Arc;\nuse std::sync::OnceLock;\nuse std::time::Instant;\n\nuse bytes::{Bytes, BytesMut};\nuse futures::TryStreamExt as _;\nuse spacetimedb::config::CertificateAuthority;\nuse spacetimedb::messages::control_db::HostType;\nuse spacetimedb::util::jobs::JobCores;\nuse spacetimedb::Identity;\nuse spacetimedb_client_api::auth::SpacetimeAuth;\nuse spacetimedb_client_api::routes::subscribe::{generate_random_connection_id, WebSocketOptions};\nuse spacetimedb_paths::{RootDir, SpacetimePaths};\nuse spacetimedb_schema::auto_migrate::MigrationPolicy;\nuse spacetimedb_schema::def::ModuleDef;\nuse tokio::runtime::{Builder, Runtime};\n\nuse spacetimedb::client::{ClientActorId, ClientConfig, ClientConnection, DataMessage};\nuse spacetimedb::db::{Config, Storage};\nuse spacetimedb::host::FunctionArgs;\nuse spacetimedb_client_api::{ControlStateReadAccess, ControlStateWriteAccess, DatabaseDef, NodeDelegate};\nuse spacetimedb_client_api_messages::websocket::v1 as ws_v1;\nuse spacetimedb_lib::{bsatn, sats};\n\npub use spacetimedb::database_logger::LogLevel;\n\nuse spacetimedb_standalone::StandaloneEnv;\n\npub fn start_runtime() -> Runtime {\n    Builder::new_multi_thread().enable_all().build().unwrap()\n}\n\nfn with_runtime<F>(func: F)\nwhere\n    F: FnOnce(&Runtime),\n{\n    let runtime = start_runtime();\n\n    func(&runtime);\n}\n\npub(crate) fn module_path(name: &str) -> PathBuf {\n    let root = std::path::PathBuf::from(env!(\"CARGO_MANIFEST_DIR\"));\n    root.join(\"../../modules\").join(name)\n}\n\n#[derive(Clone)]\npub struct ModuleHandle {\n    // Needs to hold a reference to the standalone env.\n    _env: Arc<StandaloneEnv>,\n    pub client: ClientConnection,\n    pub db_identity: Identity,\n}\n\nimpl ModuleHandle {\n    async fn call_reducer(&self, reducer: &str, args: FunctionArgs) -> anyhow::Result<()> {\n        let result = self\n            .client\n            .call_reducer(reducer, args, 0, Instant::now(), ws_v1::CallReducerFlags::FullUpdate)\n            .await;\n        let result = match result {\n            Ok(result) => result.into(),\n            Err(err) => Err(err.into()),\n        };\n        match result {\n            Ok(()) => Ok(()),\n            Err(err) => Err(err.context(format!(\"Logs:\\n{}\", self.read_log(None).await))),\n        }\n    }\n\n    pub async fn call_reducer_json(&self, reducer: &str, args: &sats::ProductValue) -> anyhow::Result<()> {\n        let args = serde_json::to_string(&args).unwrap();\n        self.call_reducer(reducer, FunctionArgs::Json(args.into())).await\n    }\n\n    pub async fn call_reducer_binary(&self, reducer: &str, args: &sats::ProductValue) -> anyhow::Result<()> {\n        let args = bsatn::to_vec(&args).unwrap();\n        self.call_reducer(reducer, FunctionArgs::Bsatn(args.into())).await\n    }\n\n    pub async fn send(&self, message: impl Into<DataMessage>) -> anyhow::Result<()> {\n        let timer = Instant::now();\n        self.client.handle_message(message, timer).await.map_err(Into::into)\n    }\n\n    pub async fn read_log(&self, size: Option<u32>) -> String {\n        let bytes = self\n            .client\n            .module()\n            .database_logger()\n            .tail(size, false)\n            .await\n            .unwrap()\n            .try_collect::<BytesMut>()\n            .await\n            .expect(\"failed to collect log stream\");\n        String::from_utf8(bytes.into()).unwrap()\n    }\n}\n\npub struct CompiledModule {\n    name: String,\n    path: PathBuf,\n    pub(super) host_type: HostType,\n    program_bytes: OnceLock<Bytes>,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum CompilationMode {\n    Debug,\n    Release,\n}\n\nimpl CompiledModule {\n    pub fn compile(name: &str, mode: CompilationMode) -> Self {\n        let (path, host_type) = spacetimedb_cli::build(\n            &module_path(name),\n            Some(PathBuf::from(\"src\")).as_deref(),\n            mode == CompilationMode::Debug,\n            None,\n        )\n        .expect(\"Module compilation failed\");\n        Self {\n            name: name.to_owned(),\n            path,\n            host_type: host_type.parse().unwrap(),\n            program_bytes: OnceLock::new(),\n        }\n    }\n\n    pub fn path(&self) -> &Path {\n        &self.path\n    }\n\n    pub fn program_bytes(&self) -> Bytes {\n        self.program_bytes\n            .get_or_init(|| std::fs::read(&self.path).unwrap().into())\n            .clone()\n    }\n\n    pub async fn extract_schema(&self) -> ModuleDef {\n        // TODO: extract_schema should accept &[u8]\n        let boxed_bytes: Box<[u8]> = self.program_bytes()[..].into();\n        spacetimedb::host::extract_schema(boxed_bytes, self.host_type)\n            .await\n            .unwrap()\n    }\n\n    pub fn extract_schema_blocking(&self) -> ModuleDef {\n        start_runtime().block_on(self.extract_schema())\n    }\n\n    pub fn with_module_async<O, R, F>(&self, config: Config, routine: R)\n    where\n        R: FnOnce(ModuleHandle) -> F,\n        F: Future<Output = O>,\n    {\n        with_runtime(move |runtime| {\n            runtime.block_on(async {\n                let module = self.load_module(config, None).await;\n\n                routine(module).await;\n            });\n        });\n    }\n\n    pub fn with_module<F>(&self, config: Config, func: F)\n    where\n        F: FnOnce(&Runtime, &ModuleHandle),\n    {\n        with_runtime(move |runtime| {\n            let module = runtime.block_on(async { self.load_module(config, None).await });\n\n            func(runtime, &module);\n        });\n    }\n\n    /// Load a module with the given config.\n    /// If \"reuse_db_path\" is set, the module will be loaded in the given path,\n    /// without resetting the database.\n    /// This is used to speed up benchmarks running under callgrind (it allows them to reuse native-compiled wasm modules).\n    pub async fn load_module(&self, config: Config, reuse_db_path: Option<&RootDir>) -> ModuleHandle {\n        let paths = match reuse_db_path {\n            Some(path) => SpacetimePaths::from_root_dir(path),\n            None => {\n                let root_dir = RootDir(env::temp_dir().join(\"stdb\").join(&self.name));\n\n                // The database created in the `temp` folder can't be randomized,\n                // so it persists after running the test.\n                std::fs::remove_dir(&root_dir).ok();\n\n                SpacetimePaths::from_root_dir(&root_dir)\n            }\n        };\n\n        let certs = CertificateAuthority::in_cli_config_dir(&paths.cli_config_dir);\n        let env = spacetimedb_standalone::StandaloneEnv::init(\n            spacetimedb_standalone::StandaloneOptions {\n                db_config: config,\n                websocket: WebSocketOptions::default(),\n            },\n            &certs,\n            paths.data_dir.into(),\n            JobCores::without_pinned_cores(),\n        )\n        .await\n        .unwrap();\n        // TODO: Fix this when we update identity generation.\n        let identity = Identity::ZERO;\n        let db_identity = SpacetimeAuth::alloc(&env).await.unwrap().claims.identity;\n        let connection_id = generate_random_connection_id();\n\n        env.publish_database(\n            &identity,\n            DatabaseDef {\n                database_identity: db_identity,\n                program_bytes: self.program_bytes(),\n                num_replicas: None,\n                host_type: self.host_type,\n                parent: None,\n                organization: None,\n            },\n            MigrationPolicy::Compatible,\n        )\n        .await\n        .unwrap();\n\n        let database = env.get_database_by_identity(&db_identity).await.unwrap().unwrap();\n        let instance = env.get_leader_replica_by_database(database.id).await.unwrap();\n\n        let client_id = ClientActorId {\n            identity,\n            connection_id,\n            name: env.client_actor_index().next_client_name(),\n        };\n\n        let host = env.leader(database.id).await.expect(\"host should be running\");\n        let module_rx = host.module_watcher().await.unwrap();\n\n        // TODO: it might be neat to add some functionality to module handle to make\n        // it easier to interact with the database. For example it could include\n        // the runtime on which a module was created and then we could add impl\n        // for stuff like \"get logs\" or \"get message log\"\n        ModuleHandle {\n            _env: env,\n            client: ClientConnection::dummy(client_id, ClientConfig::for_test(), instance.id, module_rx),\n            db_identity,\n        }\n    }\n}\n\n/// For testing, persist to disk by default, as many tests\n/// exercise functionality like restarting the database.\npub static DEFAULT_CONFIG: Config = Config {\n    storage: Storage::Disk,\n    page_pool_max_size: None,\n};\n\n/// For performance tests, do not persist to disk.\npub static IN_MEMORY_CONFIG: Config = Config {\n    storage: Storage::Disk,\n    // For some reason, a large page pool capacity causes `test_index_scans` to slow down,\n    // and makes the perf test for `chunk` go over 1ms.\n    // The threshold for failure on i7-7700K, 64GB RAM seems to be at 1 << 26.\n    // TODO(centril): investigate further why this size affects the benchmark.\n    page_pool_max_size: Some(1 << 16),\n};\n\n/// Used to parse output from module logs.\n///\n/// Sync with: `core::database_logger::Record`. We can't use it\n/// directly because the types are wrong for deserialization.\n/// (Rust!)\n#[derive(serde::Deserialize)]\npub struct LoggerRecord {\n    pub level: LogLevel,\n    pub target: Option<String>,\n    pub filename: Option<String>,\n    pub line_number: Option<u32>,\n    pub message: String,\n}\n\nconst COMPILATION_MODE: CompilationMode = if cfg!(debug_assertions) {\n    CompilationMode::Debug\n} else {\n    CompilationMode::Release\n};\n\npub trait ModuleLanguage {\n    const NAME: &'static str;\n\n    fn get_module() -> &'static CompiledModule;\n}\n\npub struct Csharp;\n\nimpl ModuleLanguage for Csharp {\n    const NAME: &'static str = \"csharp\";\n\n    fn get_module() -> &'static CompiledModule {\n        lazy_static::lazy_static! {\n            pub static ref MODULE: CompiledModule = CompiledModule::compile(\"benchmarks-cs\", COMPILATION_MODE);\n        }\n\n        &MODULE\n    }\n}\n\npub struct Rust;\n\nimpl ModuleLanguage for Rust {\n    const NAME: &'static str = \"rust\";\n\n    fn get_module() -> &'static CompiledModule {\n        lazy_static::lazy_static! {\n            pub static ref MODULE: CompiledModule = CompiledModule::compile(\"benchmarks\", COMPILATION_MODE);\n        }\n\n        &MODULE\n    }\n}\n\npub struct TypeScript;\n\nimpl ModuleLanguage for TypeScript {\n    const NAME: &'static str = \"typescript\";\n\n    fn get_module() -> &'static CompiledModule {\n        lazy_static::lazy_static! {\n            pub static ref MODULE: CompiledModule = CompiledModule::compile(\"benchmarks-ts\", COMPILATION_MODE);\n        }\n\n        &MODULE\n    }\n}\n\npub struct Cpp;\n\nimpl ModuleLanguage for Cpp {\n    const NAME: &'static str = \"cpp\";\n\n    fn get_module() -> &'static CompiledModule {\n        lazy_static::lazy_static! {\n            pub static ref MODULE: CompiledModule = CompiledModule::compile(\"benchmarks-cpp\", COMPILATION_MODE);\n        }\n\n        &MODULE\n    }\n}\n"
  },
  {
    "path": "crates/testing/src/sdk.rs",
    "content": "use duct::cmd;\nuse rand::seq::IteratorRandom;\nuse spacetimedb::messages::control_db::HostType;\nuse spacetimedb_data_structures::map::HashMap;\nuse spacetimedb_paths::{RootDir, SpacetimePaths};\nuse std::fs::create_dir_all;\nuse std::sync::{Mutex, OnceLock};\nuse std::thread::JoinHandle;\n\nuse crate::invoke_cli;\nuse crate::modules::{start_runtime, CompilationMode, CompiledModule};\nuse tempfile::TempDir;\n\n/// Ensure that the server thread we're testing against is still running, starting\n/// it if it hasn't been started yet.\npub fn ensure_standalone_process() -> &'static SpacetimePaths {\n    static PATHS: OnceLock<SpacetimePaths> = OnceLock::new();\n    static JOIN_HANDLE: OnceLock<Mutex<Option<JoinHandle<anyhow::Result<()>>>>> = OnceLock::new();\n\n    let paths = PATHS.get_or_init(|| {\n        let dir = TempDir::with_prefix(\"stdb-sdk-test\")\n            .expect(\"Failed to create tempdir\")\n            // TODO: This leaks the tempdir.\n            //       We need the tempdir to live for the duration of the process,\n            //       and all the options for post-`main` cleanup seem sketchy.\n            .keep();\n        SpacetimePaths::from_root_dir(&RootDir(dir))\n    });\n\n    let join_handle = JOIN_HANDLE.get_or_init(|| {\n        Mutex::new(Some(std::thread::spawn(move || {\n            start_runtime().block_on(spacetimedb_standalone::start_server(\n                &paths.data_dir,\n                Some(&paths.cli_config_dir.0),\n            ))\n        })))\n    });\n\n    let mut join_handle = join_handle.lock().unwrap_or_else(|e| e.into_inner());\n\n    if join_handle\n        .as_ref()\n        .expect(\"Standalone process already finished\")\n        .is_finished()\n    {\n        match join_handle.take().unwrap().join() {\n            Ok(Ok(())) => {}\n            Ok(Err(e)) => panic!(\"standalone process failed: {e:?}\"),\n            Err(e) => {\n                let msg = if let Some(s) = e.downcast_ref::<String>() {\n                    s\n                } else if let Some(s) = e.downcast_ref::<&str>() {\n                    s\n                } else {\n                    \"dyn Any\"\n                };\n                panic!(\"standalone process failed by panic: {msg}\")\n            }\n        }\n    }\n\n    paths\n}\n\npub struct Test {\n    /// A human-readable name for this test.\n    #[allow(dead_code)] // TODO: should we just remove this now that it's unused?\n    name: String,\n\n    /// Must name a module in the SpacetimeDB/modules directory.\n    module_name: String,\n\n    /// An arbitrary path to the client project.\n    /// For unrealcpp this should be the .uproject root directory.\n    client_project: String,\n\n    /// A language suitable for the `spacetime generate` CLI command.\n    ///\n    /// The string `\"unrealcpp\"` is recognized and treated differently here\n    /// because code-generation takes different arguments for Unreal client projects.\n    /// Tests written for the Unreal client SDK must specify exactly `\"unrealcpp\"`,\n    /// not any of the aliases the SpacetimeDB CLI's `generate` command would accept.\n    generate_language: String,\n\n    /// If true, pass `--include-private` to `spacetime generate` to include bindings for private items.\n    generate_include_private: bool,\n\n    /// A relative path within the `client_project` to place the module bindings.\n    ///\n    /// Usually `src/module_bindings`.\n    ///\n    /// For Unreal tests (i.e. when `generate_language == \"unrealcpp\"`),\n    /// this is instead the Unreal module name, and so should be a non-path string.\n    /// In this case, it will usually be `\"TestClient\"`.\n    generate_subdir: String,\n\n    /// A shell command to compile the client project.\n    ///\n    /// Will run with access to the env var `SPACETIME_SDK_TEST_CLIENT_PROJECT`\n    /// bound to the `client_project` path.\n    compile_command: String,\n\n    /// A shell command to run the client project.\n    ///\n    /// Will run with access to the env vars:\n    /// - `SPACETIME_SDK_TEST_CLIENT_PROJECT` bound to the `client_project` path.\n    /// - `SPACETIME_SDK_TEST_DB_NAME` bound to the database identity or name.\n    run_command: String,\n}\n\npub const TEST_MODULE_PROJECT_ENV_VAR: &str = \"SPACETIME_SDK_TEST_MODULE_PROJECT\";\npub const TEST_DB_NAME_ENV_VAR: &str = \"SPACETIME_SDK_TEST_DB_NAME\";\npub const TEST_CLIENT_PROJECT_ENV_VAR: &str = \"SPACETIME_SDK_TEST_CLIENT_PROJECT\";\n\nfn language_is_unreal(language: &str) -> bool {\n    language.eq_ignore_ascii_case(\"unrealcpp\")\n}\n\nimpl Test {\n    pub fn builder() -> TestBuilder {\n        TestBuilder::default()\n    }\n    pub fn run(self) {\n        let paths = ensure_standalone_process();\n\n        let (file, host_type) = compile_module(&self.module_name);\n\n        generate_bindings(\n            paths,\n            &self.generate_language,\n            &file,\n            host_type,\n            &self.client_project,\n            &self.generate_subdir,\n            self.generate_include_private,\n        );\n\n        compile_client(&self.compile_command, &self.client_project);\n\n        let db_name = publish_module(paths, &file, host_type);\n\n        run_client(&self.run_command, &self.client_project, &db_name);\n    }\n}\n\nfn status_ok_or_panic(output: std::process::Output, command: &str, test_name: &str) {\n    if !output.status.success() {\n        panic!(\n            \"{}: Error running {:?}: exited with non-zero exit status {}. Output:\\n{}\",\n            test_name,\n            command,\n            output.status,\n            String::from_utf8_lossy(&output.stdout),\n        );\n    }\n}\n\nfn random_module_name() -> String {\n    let mut rng = rand::rng();\n    std::iter::repeat_with(|| ('a'..='z').chain('0'..='9').choose(&mut rng).unwrap())\n        .take(16)\n        .collect()\n}\n\n/// Memoize computing `body` based on `key` by storing the result in a [`HashMap`].\n///\n/// The hash map is protected by a [`Mutex`].\n/// Only a single operator may be computing a value at a time.\n/// Computing the values must not be re-entrant / recursive.\n///\n/// The key(s) of the hash map must already be in scope as variables.\n///\n/// The keys may be either a single variable or a tuple of variables.\n///\n/// The key types must be `'static`, `Clone`, `Eq` and `Hash`, as they'll be stored in a [`HashMap`].\n///\n/// Used in this file primarily for running expensive and side-effecting subprocesses\n/// like compilation or code generation.\nmacro_rules! memoized {\n    // Recursive case: rewrite a single `key` to be a 1-tuple `(key,)`.\n    (|$key:ident: $key_ty:ty| -> $value_ty:ty $body:block) => {{\n        memoized!(|($key,): ($key_ty,)| -> $value_ty $body)\n    }};\n\n    // Base case: keys are a tuple.\n    (|($($key_tuple:ident),* $(,)?): $key_ty:ty| -> $value_ty:ty $body:block) => {{\n        static MEMOIZED: Mutex<Option<HashMap<$key_ty, $value_ty>>> = Mutex::new(None);\n\n        MEMOIZED\n            .lock()\n            .unwrap()\n            .get_or_insert_default()\n            .entry(($($key_tuple,)*))\n            .or_insert_with_key(|($($key_tuple,)*)| -> $value_ty { $body })\n            .clone()\n    }};\n}\n\n// Note: this function is memoized to ensure we compile each module only once.\n// Without this lock, if multiple `Test`s ran concurrently in the same process,\n// the test harness would compile each module multiple times concurrently,\n// which is bad both for performance reasons as well as can lead to errors\n// with toolchains like .NET which don't expect parallel invocations\n// of their build tools on the same project folder.\nfn compile_module(module: &str) -> (String, HostType) {\n    let module = module.to_owned();\n\n    memoized!(|module: String| -> (String, HostType) {\n        let module = CompiledModule::compile(module, CompilationMode::Debug);\n        (module.path().to_str().unwrap().to_owned(), module.host_type)\n    })\n}\n\n// Note: this function does not memoize because we want each test to publish the same\n// module as a separate clean database instance for isolation purposes.\nfn publish_module(paths: &SpacetimePaths, wasm_file: &str, host_type: HostType) -> String {\n    let name = random_module_name();\n    invoke_cli(\n        paths,\n        &[\n            \"publish\",\n            \"--anonymous\",\n            \"--server\",\n            \"local\",\n            match host_type {\n                HostType::Wasm => \"--bin-path\",\n                HostType::Js => \"--js-path\",\n            },\n            wasm_file,\n            &name,\n        ],\n    );\n    name\n}\n\n/// Run `spacetime generate` to generate client bindings into the `client_project`.\n///\n/// `language` should be a string suitable for the `--lang` argument to `spacetime generate`.\n/// `\"unrealcpp\"` is special-cased to account for the CLI taking different arguments.\n/// Tests of the Unreal client SDK must use exactly that string, not any alias accepted by the CLI.\n///\n/// `wasm_file` is a path to a compiled WASM blob, as returned by [`compile_module`].\n///\n/// `client_project` and `generate_subdir` will be the values set in the [`Test`].\n/// These have different semantics depending on whether `language` is `\"unrealcpp\"`.\n///\n/// For Unreal SDK tests, the `client_project` should be the directory which contains the `.uproject` file,\n/// and `generate_subdir` should be the Unreal module name.\n///\n/// For non-unreal SDK tests, the `client_project` may be an arbitrary path,\n/// and the `generate_subdir` an arbitrary relative path within it.\n/// These will be combined as `\"{client_project}/{generate_subdir}\"` to produce the `--out-dir`.\n///\n/// Note: this function is memoized to ensure we only run `spacetime generate` once for each target directory.\n///\n/// Without this lock, if multiple `Test`s ran concurrently in the same process\n/// with the same `client_project` and `generate_subdir`,\n/// the test harness would run `spacetime generate` multiple times concurrently,\n/// each of which would remove and re-populate the bindings directory,\n/// potentially sweeping them out from under a compile or run process.\n///\n/// This lock ensures that only one `spacetime generate` process runs at a time,\n/// and the `HashSet` ensures that we run `spacetime generate` only once for each output directory.\n///\n/// Circumstances where this will still break:\n/// - If multiple tests want to use the same client_project/generate_subdir pair,\n///   but for different modules' bindings, only one module's bindings will ever be generated.\n///   If you need bindings for multiple different modules, put them in different subdirs.\n/// - If multiple distinct test harness processes run concurrently,\n///   they will encounter the race condition described above,\n///   because the `BINDINGS_GENERATED` lock is not shared between harness processes.\n///   Running multiple test harness processes concurrently will break anyways\n///   because each will try to run `spacetime start` as a subprocess and will therefore\n///   contend over port 3000.\n///   Prefer constructing multiple `Test`s and `Test::run`ing them\n///   from within the same harness process.\n//\n// I (pgoldman 2023-09-11) considered, as an alternative to this lock,\n// having `Test::run` copy the `client_project` into a fresh temporary directory.\n// That would be more complicated, as we'd need to re-write dependencies\n// on the client language's SpacetimeDB SDK to use a local absolute path.\n// Doing so portably across all our SDK languages seemed infeasible.\nfn generate_bindings(\n    paths: &SpacetimePaths,\n    language: &str,\n    wasm_file: &str,\n    host_type: HostType,\n    client_project: &str,\n    generate_subdir: &str,\n    generate_include_private: bool,\n) {\n    // We need these to be owned `String`s so we can memoize on them.\n    let client_project = client_project.to_owned();\n    let generate_subdir = generate_subdir.to_owned();\n\n    // Codegen is side-effecting and doesn't meaningfully return a Rust value,\n    // so our memoization has unit as the value.\n    // This makes it run at most once for each key.\n    memoized!(|(client_project, generate_subdir): (String, String)| -> () {\n        let mut args: Vec<&str> = vec![\n            \"generate\",\n            \"--yes\",\n            \"--lang\",\n            language,\n            match host_type {\n                HostType::Wasm => \"--bin-path\",\n                HostType::Js => \"--js-path\",\n            },\n            wasm_file,\n        ];\n\n        if generate_include_private {\n            args.push(\"--include-private\");\n        }\n\n        let generate_dir: String;\n\n        // `generate --lang unrealcpp` takes different arguments from non-Unreal languages\n        // to account for some quirks of Unreal project structure.\n        if language_is_unreal(language) {\n            // For unreal, we use `client_project` as the uproject directory,\n            // and `generate_subdir` as the module name.\n            args.extend_from_slice(&[\"--uproject-dir\", client_project]);\n            args.extend_from_slice(&[\"--module-name\", generate_subdir]);\n        } else {\n            generate_dir = format!(\"{client_project}/{generate_subdir}\");\n            create_dir_all(&generate_dir).unwrap();\n            args.extend_from_slice(&[\"--out-dir\", &generate_dir]);\n        }\n\n        invoke_cli(paths, &args);\n    })\n}\n\nfn split_command_string(command: &str) -> (String, Vec<String>) {\n    let mut parts = Vec::new();\n    let mut current = String::new();\n    let mut in_quotes = false;\n    let mut quote_char = '\\0';\n\n    for c in command.chars() {\n        match c {\n            '\"' | '\\'' if !in_quotes => {\n                in_quotes = true;\n                quote_char = c;\n            }\n            '\"' | '\\'' if in_quotes && c == quote_char => {\n                in_quotes = false;\n            }\n            ' ' if !in_quotes => {\n                if !current.is_empty() {\n                    parts.push(std::mem::take(&mut current));\n                }\n            }\n            _ => current.push(c),\n        }\n    }\n    if !current.is_empty() {\n        parts.push(current);\n    }\n\n    let mut iter = parts.into_iter();\n    let exe = iter.next().expect(\"Command should have at least a program name\");\n    (exe, iter.collect())\n}\n\n// Note: this function is memoized to ensure we only compile each client once.\nfn compile_client(compile_command: &str, client_project: &str) {\n    let client_project = client_project.to_owned();\n\n    memoized!(|client_project: String| -> () {\n        let (exe, args) = split_command_string(compile_command);\n\n        let output = cmd(exe, args)\n            .dir(client_project)\n            .env(TEST_CLIENT_PROJECT_ENV_VAR, client_project)\n            .stderr_to_stdout()\n            .stdout_capture()\n            .unchecked()\n            .run()\n            .expect(\"Error running compile command\");\n\n        status_ok_or_panic(output, compile_command, \"(compiling)\");\n    })\n}\n\nfn run_client(run_command: &str, client_project: &str, db_name: &str) {\n    let (exe, args) = split_command_string(run_command);\n\n    let output = cmd(exe, args)\n        .dir(client_project)\n        .env(TEST_CLIENT_PROJECT_ENV_VAR, client_project)\n        .env(TEST_DB_NAME_ENV_VAR, db_name)\n        .env(\n            \"RUST_LOG\",\n            \"spacetimedb=debug,spacetimedb_client_api=debug,spacetimedb_lib=debug,spacetimedb_standalone=debug\",\n        )\n        .stderr_to_stdout()\n        .stdout_capture()\n        .unchecked()\n        .run()\n        .expect(\"Error running run command\");\n\n    status_ok_or_panic(output, run_command, \"(running)\");\n}\n\n#[derive(Clone, Default)]\npub struct TestBuilder {\n    name: Option<String>,\n    module_name: Option<String>,\n    client_project: Option<String>,\n    generate_language: Option<String>,\n    generate_include_private: bool,\n    generate_subdir: Option<String>,\n    compile_command: Option<String>,\n    run_command: Option<String>,\n}\n\nimpl TestBuilder {\n    pub fn with_name(self, name: impl Into<String>) -> Self {\n        TestBuilder {\n            name: Some(name.into()),\n            ..self\n        }\n    }\n\n    pub fn with_module(self, module_name: impl Into<String>) -> Self {\n        TestBuilder {\n            module_name: Some(module_name.into()),\n            ..self\n        }\n    }\n\n    pub fn with_client(self, client_project: impl Into<String>) -> Self {\n        TestBuilder {\n            client_project: Some(client_project.into()),\n            ..self\n        }\n    }\n\n    pub fn with_language(self, generate_language: impl Into<String>) -> Self {\n        TestBuilder {\n            generate_language: Some(generate_language.into()),\n            ..self\n        }\n    }\n\n    pub fn with_bindings_dir(self, generate_subdir: impl Into<String>) -> Self {\n        TestBuilder {\n            generate_subdir: Some(generate_subdir.into()),\n            ..self\n        }\n    }\n\n    // Unreal-only: names the Unreal module into which bindings are generated.\n    pub fn with_unreal_module(self, unreal_module_name: impl Into<String>) -> Self {\n        TestBuilder {\n            generate_subdir: Some(unreal_module_name.into()),\n            ..self\n        }\n    }\n\n    pub fn with_compile_command(self, compile_command: impl Into<String>) -> Self {\n        TestBuilder {\n            compile_command: Some(compile_command.into()),\n            ..self\n        }\n    }\n\n    pub fn with_run_command(self, run_command: impl Into<String>) -> Self {\n        TestBuilder {\n            run_command: Some(run_command.into()),\n            ..self\n        }\n    }\n\n    pub fn with_generate_private_items(self, include_private: bool) -> Self {\n        TestBuilder {\n            generate_include_private: include_private,\n            ..self\n        }\n    }\n\n    pub fn build(self) -> Test {\n        let generate_language = self\n            .generate_language\n            .expect(\"Supply a client language using TestBuilder::with_language\");\n\n        // For non-Unreal: require generate_subdir as before.\n        // For Unreal: ignore generate_subdir entirely, but still populate with a harmless placeholder.\n        let msg = if language_is_unreal(&generate_language) {\n            \"Supply an Unreal module name using TestBuilder::with_unreal_module\"\n        } else {\n            \"Supply a module_bindings subdirectory using TestBuilder::with_bindings_dir\"\n        };\n        let generate_subdir = self.generate_subdir.expect(msg);\n\n        Test {\n            name: self.name.expect(\"Supply a test name using TestBuilder::with_name\"),\n            module_name: self\n                .module_name\n                .expect(\"Supply a module name using TestBuilder::with_module\"),\n            client_project: self\n                .client_project\n                .expect(\"Supply a client project directory using TestBuilder::with_client\"),\n            generate_language,\n            generate_include_private: self.generate_include_private,\n            generate_subdir,\n            compile_command: self\n                .compile_command\n                .expect(\"Supply a compile command using TestBuilder::with_compile_command\"),\n            run_command: self\n                .run_command\n                .expect(\"Supply a run command using TestBuilder::with_run_command\"),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/testing/tests/standalone_integration_test.rs",
    "content": "use serial_test::serial;\nuse spacetimedb_lib::sats::{product, AlgebraicValue};\nuse spacetimedb_testing::modules::{\n    CompilationMode, CompiledModule, Cpp, Csharp, LogLevel, LoggerRecord, ModuleHandle, ModuleLanguage, Rust,\n    TypeScript, DEFAULT_CONFIG, IN_MEMORY_CONFIG,\n};\nuse std::{\n    future::Future,\n    time::{Duration, Instant},\n};\n\nfn init() {\n    let _ = env_logger::builder()\n        .parse_filters(\n            \"spacetimedb=trace,spacetimedb_client_api=trace,spacetimedb_lib=trace,spacetimedb_standalone=trace\",\n        )\n        .is_test(true)\n        // `try_init` and ignore failures to continue if a logger is already registered.\n        // This allows us to call `init` at the start of every test without a `once_cell` or similar.\n        .try_init();\n}\n\nasync fn read_logs(module: &ModuleHandle) -> Vec<String> {\n    module\n        .read_log(None)\n        .await\n        .trim()\n        .split('\\n')\n        .map(|line| {\n            let record: LoggerRecord = serde_json::from_str(line).unwrap();\n            if matches!(record.level, LogLevel::Panic | LogLevel::Error | LogLevel::Warn) {\n                panic!(\"Found an error-like log line: {line}\");\n            }\n            record.message\n        })\n        .skip_while(|line| line != \"Database initialized\")\n        .skip(1)\n        .collect::<Vec<_>>()\n}\n\n// The tests MUST be run in sequence because they read the OS environment\n// and can cause a race when run in parallel.\n\nfn test_calling_a_reducer_in_module(module_name: &'static str) {\n    init();\n\n    CompiledModule::compile(module_name, CompilationMode::Debug).with_module_async(\n        DEFAULT_CONFIG,\n        |module| async move {\n            let json =\n                r#\"{\"CallReducer\": {\"reducer\": \"add\", \"args\": \"[\\\"Tyrion\\\", 24]\", \"request_id\": 0, \"flags\": 0 }}\"#\n                    .to_string();\n            module.send(json).await.unwrap();\n\n            let json =\n                r#\"{\"CallReducer\": {\"reducer\": \"add\", \"args\": \"[\\\"Cersei\\\", 31]\", \"request_id\": 1, \"flags\": 0 }}\"#\n                    .to_string();\n            module.send(json).await.unwrap();\n\n            let json =\n                r#\"{\"CallReducer\": {\"reducer\": \"say_hello\", \"args\": \"[]\", \"request_id\": 2, \"flags\": 0 }}\"#.to_string();\n            module.send(json).await.unwrap();\n\n            let json = r#\"{\"CallReducer\": {\"reducer\": \"list_over_age\", \"args\": \"[30]\", \"request_id\": 3, \"flags\": 0 }}\"#\n                .to_string();\n            module.send(json).await.unwrap();\n\n            let json =\n                r#\"{\"CallReducer\": {\"reducer\": \"log_module_identity\", \"args\": \"[]\", \"request_id\": 4, \"flags\": 0 }}\"#\n                    .to_string();\n            module.send(json).await.unwrap();\n\n            assert_eq!(\n                read_logs(&module).await,\n                [\n                    \"Hello, Tyrion!\",\n                    \"Hello, Cersei!\",\n                    \"Hello, World!\",\n                    \"Cersei has age 31 >= 30\",\n                ]\n                .into_iter()\n                .map(String::from)\n                .chain(std::iter::once(format!(\"Module identity: {}\", module.db_identity)))\n                .collect::<Vec<_>>()\n            );\n        },\n    );\n}\n\n#[test]\n#[serial]\nfn test_calling_a_reducer() {\n    test_calling_a_reducer_in_module(\"module-test\");\n}\n\n#[test]\n#[serial]\nfn test_calling_a_reducer_csharp() {\n    test_calling_a_reducer_in_module(\"module-test-cs\");\n}\n\n#[test]\n#[serial]\nfn test_calling_a_reducer_typescript() {\n    test_calling_a_reducer_in_module(\"module-test-ts\");\n}\n\n#[test]\n#[serial]\nfn test_calling_a_reducer_cpp() {\n    test_calling_a_reducer_in_module(\"module-test-cpp\");\n}\n\n#[test]\n#[serial]\nfn test_calling_a_reducer_with_private_table() {\n    init();\n\n    CompiledModule::compile(\"module-test\", CompilationMode::Debug).with_module_async(\n        DEFAULT_CONFIG,\n        |module| async move {\n            module\n                .call_reducer_binary(\"add_private\", &product![\"Tyrion\"])\n                .await\n                .unwrap();\n            module.call_reducer_binary(\"query_private\", &product![]).await.unwrap();\n\n            let logs = read_logs(&module)\n                .await\n                .into_iter()\n                .skip_while(|r| r.starts_with(\"Timestamp\"))\n                .collect::<Vec<_>>();\n\n            assert_eq!(logs, [\"Private, Tyrion!\", \"Private, World!\",].map(String::from));\n        },\n    );\n}\n\nasync fn read_log_skip_repeating(module: &ModuleHandle) -> String {\n    let logs = read_logs(module).await;\n    let mut logs = logs\n        .into_iter()\n        // Filter out log lines from the `repeating_test` reducer,\n        // which runs frequently enough to appear in our logs after we've slept a second.\n        .filter(|line| !line.starts_with(\"Timestamp: Timestamp { __timestamp_micros_since_unix_epoch__: \"))\n        .collect::<Vec<_>>();\n\n    if logs.len() != 1 {\n        panic!(\"Expected a single log message but found {logs:#?}\");\n    };\n\n    logs.swap_remove(0)\n}\n\nfn test_calling_a_procedure_in_module(module_name: &'static str) {\n    init();\n\n    CompiledModule::compile(module_name, CompilationMode::Debug).with_module_async(\n        DEFAULT_CONFIG,\n        |module| async move {\n            let json = r#\"\n{\n  \"CallProcedure\": {\n    \"procedure\": \"sleep_one_second\",\n    \"args\": \"[]\",\n    \"request_id\": 0,\n    \"flags\": 0\n  }\n}\"#\n            .to_string();\n            module.send(json).await.unwrap();\n\n            // It sleeps one second, but we'll wait two just to be safe.\n            tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n\n            let log_sleep = read_log_skip_repeating(&module).await;\n\n            assert!(log_sleep.starts_with(\"Slept from \"));\n            assert!(log_sleep.contains(\"a total of\"));\n        },\n    )\n}\n\n#[test]\n#[serial]\nfn test_calling_a_procedure() {\n    test_calling_a_procedure_in_module(\"module-test\");\n}\n\nfn test_calling_with_tx_in_module(module_name: &'static str) {\n    init();\n\n    CompiledModule::compile(module_name, CompilationMode::Debug).with_module_async(\n        DEFAULT_CONFIG,\n        |module| async move {\n            let json = r#\"\n{\n  \"CallProcedure\": {\n    \"procedure\": \"with_tx\",\n    \"args\": \"[]\",\n    \"request_id\": 0,\n    \"flags\": 0\n  }\n}\"#\n            .to_string();\n            module.send(json).await.unwrap();\n\n            // Wait 1 second just to be safe.\n            tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n            let log = read_log_skip_repeating(&module).await;\n\n            assert!(log.contains(\"Hello, World!\"));\n        },\n    )\n}\n\n#[test]\n#[serial]\nfn test_calling_with_tx() {\n    test_calling_with_tx_in_module(\"module-test\");\n}\n\n/// Invoke the `module-test` module,\n/// use `caller` to invoke its `test` reducer,\n/// and assert that its logs look right.\n///\n/// `caller` must invoke the reducer with args equivalent to:\n/// ```ignore\n/// [\n///     TestA {\n///         x: 0,\n///         y: 2,\n///         z: \"Macro\".to_string(),\n///     },\n///     TestB {\n///         foo: \"Foo\".to_string(),\n///     },\n///     TestC::Foo,\n///     TestF::Baz(\"buzz\".to_string()),\n/// ]\n/// ```\nfn test_call_query_macro_with_caller<F: Future<Output = ()>>(caller: impl FnOnce(ModuleHandle) -> F) {\n    CompiledModule::compile(\"module-test\", CompilationMode::Debug).with_module_async(\n        DEFAULT_CONFIG,\n        |module| async move {\n            caller(module.clone()).await;\n            let logs = read_logs(&module)\n                .await\n                .into_iter()\n                .filter(|line| {\n                    !(line.starts_with(\"sender:\") || line.starts_with(\"timestamp:\") || line.starts_with(\"Timestamp\"))\n                })\n                .collect::<Vec<_>>();\n            assert_eq!(\n                logs,\n                [\n                    \"BEGIN\",\n                    r#\"bar: \"Foo\"\"#,\n                    \"Foo\",\n                    \"buzz\",\n                    \"Row count before delete: 1000\",\n                    r#\"Inserted: TestE { id: 1, name: \"Tyler\" }\"#,\n                    \"Row count after delete: 995\",\n                    \"Row count filtered by condition: 995\",\n                    \"MultiColumn\",\n                    \"Row count filtered by multi-column condition: 199\",\n                    \"END\",\n                ]\n                .map(String::from)\n            );\n        },\n    );\n}\n\n/// Call the `module-test` module's `test` reducer with a variety of ways of passing arguments.\n#[test]\n#[serial]\nfn test_call_query_macro() {\n    // Hand-written JSON. This will fail if the JSON encoding of `ClientMessage` changes.\n    test_call_query_macro_with_caller(|module| async move {\n        // Note that JSON doesn't allow multiline strings, so the encoded args string must be on one line!\n        let json = r#\"\n{ \"CallReducer\": {\n  \"reducer\": \"test\",\n  \"args\":\n    \"[ { \\\"x\\\": 0, \\\"y\\\": 2, \\\"z\\\": \\\"Macro\\\" }, { \\\"foo\\\": \\\"Foo\\\" }, { \\\"foo\\\": {} }, { \\\"baz\\\": \\\"buzz\\\" } ]\",\n  \"request_id\": 0,\n  \"flags\": 0\n} }\"#\n            .to_string();\n        module.send(json).await.unwrap();\n    });\n\n    let args_pv = &product![\n        product![0u32, 2u32, \"Macro\"],\n        product![\"Foo\"],\n        AlgebraicValue::sum(0, AlgebraicValue::unit()),\n        AlgebraicValue::sum(2, AlgebraicValue::String(\"buzz\".into())),\n    ];\n\n    // JSON via the `Serialize` path.\n    test_call_query_macro_with_caller(|module| async move {\n        module.call_reducer_json(\"test\", args_pv).await.unwrap();\n    });\n\n    // BSATN via the `Serialize` path.\n    test_call_query_macro_with_caller(|module| async move {\n        module.call_reducer_binary(\"test\", args_pv).await.unwrap();\n    });\n}\n\n#[test]\n#[serial]\n/// This test runs the index scan workloads in the `perf-test` module.\n/// Timing spans should be < 1ms if the correct index was used.\n/// Otherwise these workloads will degenerate into full table scans.\nfn test_index_scans() {\n    init();\n    CompiledModule::compile(\"perf-test\", CompilationMode::Release).with_module_async(\n        IN_MEMORY_CONFIG,\n        |module| async move {\n            let no_args = &product![];\n\n            module\n                .call_reducer_binary(\"load_location_table\", no_args)\n                .await\n                .unwrap();\n\n            module\n                .call_reducer_binary(\"test_index_scan_on_id\", no_args)\n                .await\n                .unwrap();\n\n            module\n                .call_reducer_binary(\"test_index_scan_on_chunk\", no_args)\n                .await\n                .unwrap();\n\n            module\n                .call_reducer_binary(\"test_index_scan_on_x_z_dimension\", no_args)\n                .await\n                .unwrap();\n\n            module\n                .call_reducer_binary(\"test_index_scan_on_x_z\", no_args)\n                .await\n                .unwrap();\n\n            let logs = read_logs(&module).await;\n\n            // Each timing span should be < 1ms\n            let timing = |line: &str| {\n                line.starts_with(\"Timing span\")\n                    && (line.ends_with(\"ns\") || line.ends_with(\"us\") || line.ends_with(\"µs\"))\n            };\n            assert!(timing(&logs[0]));\n            assert!(timing(&logs[1]));\n            assert!(timing(&logs[2]));\n            assert!(timing(&logs[3]));\n        },\n    );\n}\n\nasync fn bench_call(module: &ModuleHandle, call: &str, count: &u32) -> Duration {\n    let now = Instant::now();\n\n    // Note: using JSON variant because some functions accept u64 instead, so we rely on JSON's dynamic typing.\n    module.call_reducer_json(call, &product![*count]).await.unwrap();\n\n    now.elapsed()\n}\n\n#[allow(clippy::disallowed_macros)]\nasync fn _run_bench_db(module: ModuleHandle, benches: &[(&str, u32, &str)]) {\n    let expect: Vec<_> = benches.iter().map(|x| x.2.to_string()).collect();\n    let mut timings = Vec::with_capacity(benches.len());\n    for (name, count, _) in benches {\n        let elapsed = bench_call(&module, name, count).await;\n        timings.push((name, count, elapsed));\n    }\n\n    assert_eq!(read_logs(&module).await, expect);\n\n    for (name, rows, elapsed) in timings {\n        println!(\"RUN {name:<30} x {rows:>10} rows: {elapsed:>20.3?}\");\n    }\n}\n\nfn test_calling_bench_db_circles<L: ModuleLanguage>() {\n    L::get_module().with_module_async(DEFAULT_CONFIG, |module| async move {\n        #[rustfmt::skip]\n        let benches = [\n            (\"insert_bulk_food\", 50, \"INSERT FOOD: 50\"),\n            (\"insert_bulk_entity\", 50, \"INSERT ENTITY: 50\"),\n            (\"insert_bulk_circle\", 500, \"INSERT CIRCLE: 500\"),\n            (\"cross_join_circle_food\", 50 * 500, \"CROSS JOIN CIRCLE FOOD: 25000, processed: 2500\"),\n            (\"cross_join_all\", 50 * 50 * 500, \"CROSS JOIN ALL: 1250000, processed: 1250000\"),\n        ];\n\n        _run_bench_db(module, &benches).await\n    });\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_circles_rust() {\n    test_calling_bench_db_circles::<Rust>();\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_circles_csharp() {\n    test_calling_bench_db_circles::<Csharp>();\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_circles_typescript() {\n    test_calling_bench_db_circles::<TypeScript>();\n}\n#[test]\n#[serial]\nfn test_calling_bench_db_circles_cpp() {\n    test_calling_bench_db_circles::<Cpp>();\n}\n\nfn test_calling_bench_db_ia_loop<L: ModuleLanguage>() {\n    L::get_module().with_module_async(DEFAULT_CONFIG, |module| async move {\n        #[rustfmt::skip]\n        let benches = [\n            (\"insert_bulk_position\", 20_000, \"INSERT POSITION: 20000\"),\n            (\"insert_bulk_velocity\", 10_000, \"INSERT VELOCITY: 10000\"),\n            (\"update_position_all\", 20_000, \"UPDATE POSITION ALL: 20000, processed: 20000\"),\n            (\"update_position_with_velocity\", 10_000, \"UPDATE POSITION BY VELOCITY: 10000, processed: 10000\"),\n            (\"insert_world\", 5_000, \"INSERT WORLD PLAYERS: 5000\"),\n            // Note: we set lower amount of ia loop players here than in benchmarks.\n            // Otherwise tests will take forever because they are built in debug mode.\n            (\"game_loop_enemy_ia\", 100, \"ENEMY IA LOOP PLAYERS: 100, processed: 5000\"),\n        ];\n\n        _run_bench_db(module, &benches).await\n    });\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_ia_loop_rust() {\n    test_calling_bench_db_ia_loop::<Rust>();\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_ia_loop_csharp() {\n    test_calling_bench_db_ia_loop::<Csharp>();\n}\n\n#[test]\n#[serial]\nfn test_calling_bench_db_ia_loop_typescript() {\n    test_calling_bench_db_ia_loop::<TypeScript>();\n}\n#[test]\n#[serial]\nfn test_calling_bench_db_ia_loop_cpp() {\n    test_calling_bench_db_ia_loop::<Cpp>();\n}\n"
  },
  {
    "path": "crates/update/Cargo.toml",
    "content": "[package]\nname = \"spacetimedb-update\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense-file = \"LICENSE\"\npublish = false\n\n[features]\n# NOTE(bfops): This is not a well-thought-through feature. It's really only meant for internal testing/debugging.\n# Specifically, we use it in some CI.\ngithub-token-auth = []\n\n[dependencies]\nspacetimedb-paths.workspace = true\n\nanyhow.workspace = true\nbytes.workspace = true\nclap.workspace = true\ndialoguer = { workspace = true, default-features = false }\nflate2.workspace = true\nhttp-body-util = \"0.1.2\"\ncolored.workspace = true\nindicatif.workspace = true\nlog.workspace = true\nreqwest.workspace = true\nself-replace.workspace = true\nsemver = { workspace = true, features = [\"serde\"] }\nserde.workspace = true\nserde_json.workspace = true\ntar.workspace = true\ntempfile.workspace = true\ntokio.workspace = true\ntoml.workspace = true\ntracing = { workspace = true, features = [\"release_max_level_off\"] }\nzip = \"2.3\"\n\n[target.'cfg(windows)'.dependencies]\nwindows-sys = { workspace = true, features = [\"Win32_System_Console\"] }\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/update/README.md",
    "content": "> ⚠️ **Internal Crate** ⚠️\n>\n> This crate is intended for internal use only. It is **not** stable and may change without notice.\n"
  },
  {
    "path": "crates/update/build.rs",
    "content": "#![allow(clippy::disallowed_macros)]\nfn main() {\n    let target = std::env::var(\"TARGET\").unwrap();\n    println!(\"cargo::rustc-env=BUILD_TARGET={target}\");\n}\n"
  },
  {
    "path": "crates/update/spacetime-install.ps1",
    "content": "Param(\n    [Parameter(Mandatory=$false)]\n    [Switch]$Nightly\n)\n\nfunction Install {\n    $ErrorActionPreference = 'Stop'\n    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\n    function CheckVCRuntime {\n        $paths = @(\n            \"$env:SystemRoot\\System32\\vcruntime140.dll\",\n            \"$env:SystemRoot\\SysWOW64\\vcruntime140.dll\"\n        )\n        foreach ($path in $paths) {\n            if (Test-Path $path) {\n                Write-Host \"vcruntime140.dll is already installed at $path\"\n                return $true\n            }\n        }\n        Write-Host \"vcruntime140.dll is not installed\"\n        return $false\n    }\n\n    if (-not (CheckVCRuntime)) {\n        $DownloadUrl = \"https://aka.ms/vs/17/release/vc_redist.x64.exe\"\n        Write-Output \"Downloading vcruntime140.dll...\"\n        $Installer = Join-Path ([System.IO.Path]::GetTempPath()) \"vc_redist.x64.exe\"\n        Invoke-WebRequest $DownloadUrl -OutFile $Installer -UseBasicParsing\n        Start-Process -Wait -FilePath $Installer -ArgumentList \"/quiet\", \"/install\"\n    }\n\n    $AssetName = \"spacetimedb-update-x86_64-pc-windows-msvc.exe\"\n    $DownloadUrl = \"https://github.com/clockworklabs/SpacetimeDB/releases/latest/download/$AssetName\"\n    $MirrorBase = \"https://spacetimedb-client-binaries.nyc3.digitaloceanspaces.com\"\n    Write-Output \"Downloading installer...\"\n\n    function UpdatePathIfNotExists {\n        param (\n            [string]$DirectoryToAdd\n        )\n        $currentPath = [Environment]::GetEnvironmentVariable(\"Path\", \"User\")\n        if (-not $currentPath.Contains($DirectoryToAdd)) {\n            [Environment]::SetEnvironmentVariable(\"Path\", $currentPath + \";\" + $DirectoryToAdd, \"User\")\n        }\n    }\n    \n    $Executable = Join-Path ([System.IO.Path]::GetTempPath()) \"spacetime-install.exe\"\n    try {\n        Invoke-WebRequest $DownloadUrl -OutFile $Executable -UseBasicParsing\n    } catch {\n        Write-Output \"Download failed, trying mirror...\"\n        $Tag = (Invoke-WebRequest \"$MirrorBase/latest-version\" -UseBasicParsing).Content.Trim()\n        $MirrorUrl = \"$MirrorBase/refs/tags/$Tag/$AssetName\"\n        Invoke-WebRequest $MirrorUrl -OutFile $Executable -UseBasicParsing\n    }\n    Start-Process -Wait -FilePath $Executable\n\n    # TODO: do this in spacetimedb-update\n    $InstallDir = Join-Path ([Environment]::GetFolderPath(\"LocalApplicationData\")) \"SpacetimeDB\"\n    UpdatePathIfNotExists $InstallDir\n    Write-Output \"We have added spacetimedb to your Path. You may have to logout and log back in to reload your environment.\"\n}\n\nInstall\n"
  },
  {
    "path": "crates/update/spacetime-install.sh",
    "content": "#!/bin/sh\n\nset -u\n\nerr() {\n  echo \"$1\" >&2\n  exit 1\n}\n\n# NOTICE: If you change anything here, please make the same changes in self_install.rs\nusage() {\n    cat <<EOF\nInstall the SpacetimeDB CLI\n\nUsage: spacetime-install[EXE] [OPTIONS]\n\nOptions:\n      --root-dir <ROOT_DIR>\n          The directory to locally install SpacetimeDB into. If unspecified,\n          uses platform defaults\n\n  -y, --yes\n          Skip the confirmation dialog\n\n  -h, --help\n          Print help\nEOF\n}\n\nSPACETIME_DOWNLOAD_ROOT=\"${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/latest/download}\"\n\nmain() {\n    # First make sure all config is valid, we don't want to end up with\n    # a partial install.\n    local _downloader\n    if check_cmd curl; then\n        _downloader=curl\n    elif check_cmd wget; then\n        _downloader=wget\n    else\n        err \"need 'curl' or 'wget' (command not found)\"\n    fi\n    need_cmd chmod\n    need_cmd rm\n    need_cmd rmdir\n    need_cmd mktemp\n\n    # Detect the OS and arch\n    get_architecture || return 1\n    local _host=\"$RETVAL\"\n    [ -z \"$_host\" ] && err \"failed to get architecture\"\n\n    local _ext=\"\"\n    case \"$_host\" in\n        *windows*) _ext=\".exe\" ;;\n    esac\n\n    # We can now install the binary\n    local _tmpdir\n    _tmpdir=\"$(ensure mktemp -d)\" || exit 1\n    local _download_file=\"$_tmpdir/spacetime-install$_ext\"\n\n    check_for_help_flag \"$@\"\n\n    # Define the latest SpacetimeDB download url\n    local _asset_name=\"spacetimedb-update-$_host$_ext\"\n    local _url=\"$SPACETIME_DOWNLOAD_ROOT/$_asset_name\"\n    local _mirror_base=\"https://spacetimedb-client-binaries.nyc3.digitaloceanspaces.com\"\n    echo \"Downloading installer...\"\n    local _ok=false\n    if [ \"$_downloader\" = curl ]; then\n        if curl -sSfL --progress-bar \"$_url\" -o \"$_download_file\"; then\n            _ok=true\n        fi\n    elif [ \"$_downloader\" = wget ]; then\n        if wget \"$_url\" -O \"$_download_file\"; then\n            _ok=true\n        fi\n    fi\n\n    if [ \"$_ok\" = false ]; then\n        echo \"Download failed, trying mirror...\"\n        local _tag\n        if [ \"$_downloader\" = curl ]; then\n            _tag=\"$(curl -sSfL \"$_mirror_base/latest-version\")\" || err \"Mirror also unavailable\"\n        elif [ \"$_downloader\" = wget ]; then\n            _tag=\"$(wget -qO- \"$_mirror_base/latest-version\")\" || err \"Mirror also unavailable\"\n        fi\n        _tag=\"$(echo \"$_tag\" | tr -d '[:space:]')\"\n        local _mirror_url=\"$_mirror_base/refs/tags/$_tag/$_asset_name\"\n        if [ \"$_downloader\" = curl ]; then\n            curl -sSfL --progress-bar \"$_mirror_url\" -o \"$_download_file\" || err \"Mirror download also failed\"\n        elif [ \"$_downloader\" = wget ]; then\n            wget \"$_mirror_url\" -O \"$_download_file\" || err \"Mirror download also failed\"\n        fi\n    fi\n\n    ensure chmod u+x \"$_download_file\"\n    if [ ! -x \"$_download_file\" ]; then\n        printf '%s\\n' \"Cannot execute $_download_file (likely because of mounting /tmp as noexec).\" 1>&2\n        printf '%s\\n' \"Please copy the file to a location where you can execute binaries and run ./spacetime-install${_ext}.\" 1>&2\n        exit 1\n    fi\n\n    \"$_download_file\" \"$@\"\n    local _retval=$?\n\n    rm -f \"$_download_file\"\n    rmdir \"$_tmpdir\"\n\n    return \"$_retval\"\n}\n\ncheck_for_help_flag() {\n    for arg in \"$@\"; do\n        case \"$arg\" in\n            --help)\n                usage\n                exit 0\n                ;;\n            *)\n                OPTIND=1\n                if [ \"${arg%%--*}\" = \"\" ]; then\n                    # Long option (other than --help);\n                    # don't attempt to interpret it.\n                    continue\n                fi\n                while getopts :hy sub_arg \"$arg\"; do\n                    case \"$sub_arg\" in\n                        h)\n                            usage\n                            exit 0\n                            ;;\n                        *)\n                            ;;\n                        esac\n                done\n                ;;\n        esac\n    done\n}\n\n\n# The following functions are from rustup-init.sh of the Rust project:\n# get_architecture, get_endianness, need_cmd, check_cmd, ensure\n# Licensed under the MIT license:\n## Copyright (c) 2016 The Rust Project Developers\n## \n## Permission is hereby granted, free of charge, to any\n## person obtaining a copy of this software and associated\n## documentation files (the \"Software\"), to deal in the\n## Software without restriction, including without\n## limitation the rights to use, copy, modify, merge,\n## publish, distribute, sublicense, and/or sell copies of\n## the Software, and to permit persons to whom the Software\n## is furnished to do so, subject to the following\n## conditions:\n## \n## The above copyright notice and this permission notice\n## shall be included in all copies or substantial portions\n## of the Software.\n## \n## THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n## ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n## TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n## PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n## SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n## CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n## OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n## IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n## DEALINGS IN THE SOFTWARE.\nget_architecture() {\n    local _ostype _cputype _bitness _arch _clibtype\n    _ostype=\"$(uname -s)\"\n    _cputype=\"$(uname -m)\"\n    _clibtype=\"gnu\"\n\n    if [ \"$_ostype\" = Linux ]; then\n        if [ \"$(uname -o)\" = Android ]; then\n            _ostype=Android\n        fi\n        if ldd --version 2>&1 | grep -q 'musl'; then\n            _clibtype=\"musl\"\n        fi\n    fi\n\n    if [ \"$_ostype\" = Darwin ]; then\n        # Darwin `uname -m` can lie due to Rosetta shenanigans. If you manage to\n        # invoke a native shell binary and then a native uname binary, you can\n        # get the real answer, but that's hard to ensure, so instead we use\n        # `sysctl` (which doesn't lie) to check for the actual architecture.\n        if [ \"$_cputype\" = i386 ]; then\n            # Handling i386 compatibility mode in older macOS versions (<10.15)\n            # running on x86_64-based Macs.\n            # Starting from 10.15, macOS explicitly bans all i386 binaries from running.\n            # See: <https://support.apple.com/en-us/HT208436>\n\n            # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.\n            if sysctl hw.optional.x86_64 2> /dev/null || true | grep -q ': 1'; then\n                _cputype=x86_64\n            fi\n        elif [ \"$_cputype\" = x86_64 ]; then\n            # Handling x86-64 compatibility mode (a.k.a. Rosetta 2)\n            # in newer macOS versions (>=11) running on arm64-based Macs.\n            # Rosetta 2 is built exclusively for x86-64 and cannot run i386 binaries.\n\n            # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.\n            if sysctl hw.optional.arm64 2> /dev/null || true | grep -q ': 1'; then\n                _cputype=arm64\n            fi\n        fi\n    fi\n\n    if [ \"$_ostype\" = SunOS ]; then\n        # Both Solaris and illumos presently announce as \"SunOS\" in \"uname -s\"\n        # so use \"uname -o\" to disambiguate.  We use the full path to the\n        # system uname in case the user has coreutils uname first in PATH,\n        # which has historically sometimes printed the wrong value here.\n        if [ \"$(/usr/bin/uname -o)\" = illumos ]; then\n            _ostype=illumos\n        fi\n\n        # illumos systems have multi-arch userlands, and \"uname -m\" reports the\n        # machine hardware name; e.g., \"i86pc\" on both 32- and 64-bit x86\n        # systems.  Check for the native (widest) instruction set on the\n        # running kernel:\n        if [ \"$_cputype\" = i86pc ]; then\n            _cputype=\"$(isainfo -n)\"\n        fi\n    fi\n\n    case \"$_ostype\" in\n\n        Android)\n            _ostype=linux-android\n            ;;\n\n        Linux)\n            check_proc\n            _ostype=unknown-linux-$_clibtype\n            _bitness=$(get_bitness)\n            ;;\n\n        FreeBSD)\n            _ostype=unknown-freebsd\n            ;;\n\n        NetBSD)\n            _ostype=unknown-netbsd\n            ;;\n\n        DragonFly)\n            _ostype=unknown-dragonfly\n            ;;\n\n        Darwin)\n            _ostype=apple-darwin\n            ;;\n\n        illumos)\n            _ostype=unknown-illumos\n            ;;\n\n        MINGW* | MSYS* | CYGWIN* | Windows_NT)\n            _ostype=pc-windows-gnu\n            ;;\n\n        *)\n            err \"unrecognized OS type: $_ostype\"\n            ;;\n\n    esac\n\n    case \"$_cputype\" in\n\n        i386 | i486 | i686 | i786 | x86)\n            _cputype=i686\n            ;;\n\n        xscale | arm)\n            _cputype=arm\n            if [ \"$_ostype\" = \"linux-android\" ]; then\n                _ostype=linux-androideabi\n            fi\n            ;;\n\n        armv6l)\n            _cputype=arm\n            if [ \"$_ostype\" = \"linux-android\" ]; then\n                _ostype=linux-androideabi\n            else\n                _ostype=\"${_ostype}eabihf\"\n            fi\n            ;;\n\n        armv7l | armv8l)\n            _cputype=armv7\n            if [ \"$_ostype\" = \"linux-android\" ]; then\n                _ostype=linux-androideabi\n            else\n                _ostype=\"${_ostype}eabihf\"\n            fi\n            ;;\n\n        aarch64 | arm64)\n            _cputype=aarch64\n            ;;\n\n        x86_64 | x86-64 | x64 | amd64)\n            _cputype=x86_64\n            ;;\n\n        mips)\n            _cputype=$(get_endianness mips '' el)\n            ;;\n\n        mips64)\n            if [ \"$_bitness\" -eq 64 ]; then\n                # only n64 ABI is supported for now\n                _ostype=\"${_ostype}abi64\"\n                _cputype=$(get_endianness mips64 '' el)\n            fi\n            ;;\n\n        ppc)\n            _cputype=powerpc\n            ;;\n\n        ppc64)\n            _cputype=powerpc64\n            ;;\n\n        ppc64le)\n            _cputype=powerpc64le\n            ;;\n\n        s390x)\n            _cputype=s390x\n            ;;\n        riscv64)\n            _cputype=riscv64gc\n            ;;\n        loongarch64)\n            _cputype=loongarch64\n            ensure_loongarch_uapi\n            ;;\n        *)\n            err \"unknown CPU type: $_cputype\"\n\n    esac\n\n    # Detect 64-bit linux with 32-bit userland\n    if [ \"${_ostype}\" = unknown-linux-gnu ] && [ \"${_bitness}\" -eq 32 ]; then\n        case $_cputype in\n            x86_64)\n                _cputype=i686\n                ;;\n            mips64)\n                _cputype=$(get_endianness mips '' el)\n                ;;\n            powerpc64)\n                _cputype=powerpc\n                ;;\n            aarch64)\n                _cputype=armv7\n                if [ \"$_ostype\" = \"linux-android\" ]; then\n                    _ostype=linux-androideabi\n                else\n                    _ostype=\"${_ostype}eabihf\"\n                fi\n                ;;\n            riscv64gc)\n                err \"riscv64 with 32-bit userland unsupported\"\n                ;;\n        esac\n    fi\n\n    # Detect armv7 but without the CPU features Rust needs in that build,\n    # and fall back to arm.\n    # See https://github.com/rust-lang/rustup.rs/issues/587.\n    if [ \"$_ostype\" = \"unknown-linux-gnueabihf\" ] && [ \"$_cputype\" = armv7 ]; then\n        if ensure grep '^Features' /proc/cpuinfo | grep -E -q -v 'neon|simd'; then\n            # At least one processor does not have NEON (which is asimd on armv8+).\n            _cputype=arm\n        fi\n    fi\n\n    _arch=\"${_cputype}-${_ostype}\"\n\n    RETVAL=\"$_arch\"\n}\n\ncheck_proc() {\n    # Check for /proc by looking for the /proc/self/exe link\n    # This is only run on Linux\n    if ! test -L /proc/self/exe ; then\n        err \"fatal: Unable to find /proc/self/exe.  Is /proc mounted?  Installation cannot proceed without /proc.\"\n    fi\n}\n\nget_bitness() {\n    need_cmd head\n    # Architecture detection without dependencies beyond coreutils.\n    # ELF files start out \"\\x7fELF\", and the following byte is\n    #   0x01 for 32-bit and\n    #   0x02 for 64-bit.\n    # The printf builtin on some shells like dash only supports octal\n    # escape sequences, so we use those.\n    local _current_exe_head\n    _current_exe_head=$(head -c 5 /proc/self/exe )\n    if [ \"$_current_exe_head\" = \"$(printf '\\177ELF\\001')\" ]; then\n        echo 32\n    elif [ \"$_current_exe_head\" = \"$(printf '\\177ELF\\002')\" ]; then\n        echo 64\n    else\n        err \"unknown platform bitness\"\n    fi\n}\n\nget_endianness() {\n    local cputype=$1\n    local suffix_eb=$2\n    local suffix_el=$3\n\n    # detect endianness without od/hexdump, like get_bitness() does.\n    need_cmd head\n    need_cmd tail\n\n    local _current_exe_endianness\n    _current_exe_endianness=\"$(head -c 6 /proc/self/exe | tail -c 1)\"\n    if [ \"$_current_exe_endianness\" = \"$(printf '\\001')\" ]; then\n        echo \"${cputype}${suffix_el}\"\n    elif [ \"$_current_exe_endianness\" = \"$(printf '\\002')\" ]; then\n        echo \"${cputype}${suffix_eb}\"\n    else\n        err \"unknown platform endianness\"\n    fi\n}\n\nneed_cmd() {\n    if ! check_cmd \"$1\"; then\n        err \"need '$1' (command not found)\"\n    fi\n}\n\ncheck_cmd() {\n    command -v \"$1\" > /dev/null 2>&1\n}\n\n# Run a command that should never fail. If the command fails execution\n# will immediately terminate with an error showing the failing\n# command.\nensure() {\n    if ! \"$@\"; then err \"command failed: $*\"; fi\n}\n\nmain \"$@\" || exit 1\n"
  },
  {
    "path": "crates/update/src/cli/install.rs",
    "content": "use std::io;\n\nuse anyhow::Context;\nuse bytes::{Buf, Bytes};\nuse http_body_util::BodyExt;\nuse indicatif::{ProgressBar, ProgressStyle};\nuse serde::Deserialize;\nuse spacetimedb_paths::SpacetimePaths;\n\nuse super::ForceYes;\n\n/// Install a specific SpacetimeDB version.\n#[derive(clap::Args)]\npub(super) struct Install {\n    /// The SpacetimeDB version to install.\n    version: semver::Version,\n\n    /// The SpacetimeDB edition(s) to install, separated by commas.\n    #[arg(long, value_delimiter = ',', action = clap::ArgAction::Set, default_value = \"standalone\")]\n    edition: Vec<String>,\n\n    /// Switch to this version after it is installed.\n    #[arg(long)]\n    r#use: bool,\n\n    /// The name of the release artifact to download from github.\n    #[arg(long, hide = true)]\n    artifact_name: Option<String>,\n\n    #[command(flatten)]\n    yes: ForceYes,\n}\n\nimpl Install {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        super::tokio_block_on(async {\n            anyhow::ensure!(\n                self.edition == [\"standalone\"],\n                \"can only install spacetimedb-standalone at the moment\"\n            );\n            let client = super::reqwest_client()?;\n            let (version, _) = download_and_install(&client, Some(self.version), self.artifact_name, paths).await?;\n            if self.r#use {\n                paths.cli_bin_dir.set_current_version(&version.to_string())?;\n            }\n            Ok(())\n        })?\n    }\n}\n\npub(super) fn make_progress_bar() -> ProgressBar {\n    let pb = ProgressBar::new(0).with_style(ProgressStyle::with_template(\"{spinner} {prefix}{msg}\").unwrap());\n    pb.enable_steady_tick(std::time::Duration::from_millis(60));\n    pb\n}\n\nfn releases_url() -> String {\n    std::env::var(\"SPACETIME_UPDATE_RELEASES_URL\")\n        .unwrap_or_else(|_| \"https://api.github.com/repos/clockworklabs/SpacetimeDB/releases\".to_owned())\n}\n\nconst MIRROR_BASE_URL: &str = \"https://spacetimedb-client-binaries.nyc3.digitaloceanspaces.com\";\n\n/// Fetch the latest version tag from the mirror.\n///\n/// This is the single source of truth for mirror version resolution.\nasync fn fetch_latest_version_from_mirror(client: &reqwest::Client) -> anyhow::Result<semver::Version> {\n    let inner = async || {\n        let url = format!(\"{MIRROR_BASE_URL}/latest-version\");\n        let tag = client.get(&url).send().await?.error_for_status()?.text().await?;\n        let ver_str = tag.trim().strip_prefix('v').unwrap_or(tag.trim());\n        semver::Version::parse(ver_str).context(\"Could not parse version\")\n    };\n    inner().await.context(\"Could not fetch latest version from mirror\")\n}\n\n/// Fetch the latest release version from GitHub, falling back to the mirror.\n///\n/// Returns `None` if both sources are unreachable.\npub(crate) async fn fetch_latest_release_version(client: &reqwest::Client) -> anyhow::Result<semver::Version> {\n    // Try GitHub first.\n    let url = format!(\"{}/latest\", releases_url());\n    if let Ok(resp) = client.get(&url).send().await\n        && resp.status().is_success()\n        && let Ok(release) = resp.json::<Release>().await\n        && let Ok(v) = release.version()\n    {\n        return Ok(v);\n    }\n\n    // Fall back to mirror.\n    fetch_latest_version_from_mirror(client).await\n}\n\npub(super) fn mirror_asset_url(version: &semver::Version, asset_name: &str) -> String {\n    format!(\"{MIRROR_BASE_URL}/refs/tags/v{version}/{asset_name}\")\n}\n\nasync fn mirror_release(\n    client: &reqwest::Client,\n    version: Option<&semver::Version>,\n    download_name: &str,\n) -> anyhow::Result<(semver::Version, Release)> {\n    let release_version = match version {\n        Some(v) => v.clone(),\n        None => fetch_latest_version_from_mirror(client).await?,\n    };\n    let release = Release {\n        tag_name: format!(\"v{release_version}\"),\n        assets: vec![ReleaseAsset {\n            name: download_name.to_owned(),\n            browser_download_url: mirror_asset_url(&release_version, download_name),\n        }],\n    };\n    Ok((release_version, release))\n}\n\npub(super) async fn download_and_install(\n    client: &reqwest::Client,\n    version: Option<semver::Version>,\n    artifact_name: Option<String>,\n    paths: &SpacetimePaths,\n) -> anyhow::Result<(semver::Version, Release)> {\n    let custom_artifact = artifact_name.is_some();\n    let download_name = artifact_name.as_deref().unwrap_or(DOWNLOAD_NAME);\n    let artifact_type = ArtifactType::deduce(download_name).context(\"Unknown archive type\")?;\n\n    let pb = make_progress_bar();\n\n    pb.set_message(\"Resolving version...\");\n\n    // Try GitHub first, fall back to mirror if unavailable.\n    let github_result = async {\n        let releases_url = releases_url();\n        let url = match &version {\n            Some(version) => format!(\"{releases_url}/tags/v{version}\"),\n            None => [&*releases_url, \"/latest\"].concat(),\n        };\n        let release: Release = client\n            .get(url)\n            .send()\n            .await?\n            .error_for_status()\n            .map_err(|e| {\n                if e.status() == Some(reqwest::StatusCode::NOT_FOUND)\n                    && let Some(version) = &version\n                {\n                    return anyhow::anyhow!(e).context(format!(\"No release found for version {version}\"));\n                }\n                anyhow::anyhow!(e).context(\"Could not fetch release info\")\n            })?\n            .json()\n            .await?;\n        let release_version = match &version {\n            Some(version) => version.clone(),\n            None => release.version().context(\"Could not parse version number\")?,\n        };\n        anyhow::Ok((release_version, release))\n    }\n    .await;\n\n    let (release_version, release) = match github_result {\n        Ok(result) => result,\n        Err(github_err) => {\n            pb.set_message(\"GitHub unavailable, trying mirror...\");\n            mirror_release(client, version.as_ref(), download_name)\n                .await\n                .map_err(|mirror_err| {\n                    anyhow::anyhow!(\"GitHub failed: {github_err:#}\\nMirror also failed: {mirror_err:#}\")\n                })?\n        }\n    };\n\n    let asset = release\n        .assets\n        .iter()\n        .find(|&asset| asset.name == download_name)\n        .ok_or_else(|| {\n            let err = anyhow::anyhow!(\"artifact named {download_name} not found in version {release_version}\");\n            if custom_artifact {\n                err\n            } else {\n                err.context(\"no prebuilt binaries available for the detected OS and architecture\")\n            }\n        })?;\n\n    pb.set_prefix(format!(\"Installing v{release_version}: \"));\n    pb.set_message(\"downloading...\");\n    let mirror_url = mirror_asset_url(&release_version, download_name);\n    let archive = match download_with_progress(&pb, client, &asset.browser_download_url).await {\n        Ok(archive) => archive,\n        Err(primary_err) => {\n            if asset.browser_download_url == mirror_url {\n                return Err(primary_err);\n            }\n            pb.set_message(\"download failed, trying mirror...\");\n            download_with_progress(&pb, client, &mirror_url)\n                .await\n                .map_err(|mirror_err| {\n                    anyhow::anyhow!(\"Primary download failed: {primary_err:#}\\nMirror also failed: {mirror_err:#}\")\n                })?\n        }\n    };\n\n    pb.set_message(\"unpacking...\");\n\n    let version_dir = paths.cli_bin_dir.version_dir(&release_version.to_string());\n    version_dir.create()?;\n    match artifact_type {\n        ArtifactType::TarGz => {\n            let tgz = archive.aggregate().reader();\n            tar::Archive::new(flate2::bufread::GzDecoder::new(tgz)).unpack(&version_dir)?;\n        }\n        ArtifactType::Zip => {\n            let zip = archive.to_bytes();\n            let zip = io::Cursor::new(&*zip);\n            zip::ZipArchive::new(zip)?.extract(&version_dir)?;\n        }\n    }\n\n    pb.finish_with_message(\"done!\");\n\n    Ok((release_version, release))\n}\n\nenum ArtifactType {\n    TarGz,\n    Zip,\n}\n\nimpl ArtifactType {\n    fn deduce(filename: &str) -> Option<Self> {\n        if filename.ends_with(\".tar.gz\") {\n            Some(Self::TarGz)\n        } else if filename.ends_with(\".zip\") {\n            Some(Self::Zip)\n        } else {\n            None\n        }\n    }\n}\n\npub(super) async fn available_releases(client: &reqwest::Client) -> anyhow::Result<Vec<String>> {\n    let url = releases_url();\n    match async {\n        anyhow::Ok(\n            client\n                .get(url)\n                .send()\n                .await?\n                .error_for_status()?\n                .json::<Vec<Release>>()\n                .await?,\n        )\n    }\n    .await\n    {\n        Ok(releases) => releases\n            .into_iter()\n            .map(|release| Ok(release.version()?.to_string()))\n            .collect(),\n        Err(_) => {\n            eprintln!(\"GitHub unavailable, fetching latest version from mirror...\");\n            let version = fetch_latest_version_from_mirror(client).await?;\n            Ok(vec![version.to_string()])\n        }\n    }\n}\n\n#[derive(Deserialize)]\npub(super) struct ReleaseAsset {\n    pub(super) name: String,\n    pub(super) browser_download_url: String,\n}\n\n#[derive(Deserialize)]\npub struct Release {\n    tag_name: String,\n    pub(super) assets: Vec<ReleaseAsset>,\n}\n\nimpl Release {\n    fn version(&self) -> anyhow::Result<semver::Version> {\n        let ver = self.tag_name.strip_prefix('v').unwrap_or(&self.tag_name);\n        Ok(semver::Version::parse(ver)?)\n    }\n}\n\npub(super) async fn download_with_progress(\n    pb: &ProgressBar,\n    client: &reqwest::Client,\n    url: &str,\n) -> Result<http_body_util::Collected<Bytes>, anyhow::Error> {\n    let response = client.get(url).send().await?.error_for_status()?;\n\n    let pb_style = pb.style();\n\n    pb.set_style(ProgressStyle::with_template(\"{spinner} {prefix}{msg} {bytes}/{total_bytes} ({eta})\").unwrap());\n    pb.set_length(response.content_length().unwrap_or(0));\n\n    let body = reqwest::Body::from(response)\n        .map_frame(|f| {\n            if let Some(data) = f.data_ref() {\n                pb.inc(data.len() as u64);\n            }\n            f\n        })\n        .collect()\n        .await?;\n\n    pb.set_style(pb_style);\n\n    Ok(body)\n}\n\nconst DOWNLOAD_NAME: &str = if cfg!(windows) {\n    concat!(\"spacetime-\", env!(\"BUILD_TARGET\"), \".zip\")\n} else {\n    concat!(\"spacetime-\", env!(\"BUILD_TARGET\"), \".tar.gz\")\n};\n"
  },
  {
    "path": "crates/update/src/cli/link.rs",
    "content": "use std::path::PathBuf;\n\nuse anyhow::Context;\nuse spacetimedb_paths::cli::BinDir;\nuse spacetimedb_paths::SpacetimePaths;\n\n/// Set a local installation of SpacetimeDB as a custom version.\n#[derive(clap::Args)]\npub(super) struct Link {\n    /// The name of the custom installation, e.g. `dev`.\n    name: String,\n    /// The path to the directory with the SpacetimeDB binaries.\n    path: PathBuf,\n\n    /// Switch to this version after it's created.\n    #[arg(long)]\n    r#use: bool,\n}\n\nimpl Link {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        anyhow::ensure!(\n            self.name != BinDir::CURRENT_VERSION_DIR_NAME,\n            \"name cannot be `current`\"\n        );\n        let mut path = std::env::current_dir()?;\n        path.push(self.path);\n        paths\n            .cli_bin_dir\n            .version_dir(&self.name)\n            .create_custom(&path)\n            .context(\"could not link custom version\")?;\n        if self.r#use {\n            paths.cli_bin_dir.set_current_version(&self.name)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/update/src/cli/list.rs",
    "content": "use spacetimedb_paths::SpacetimePaths;\nuse std::path::Path;\n\n/// List installed SpacetimeDB versions.\n#[derive(clap::Args)]\npub(super) struct List {\n    /// List all versions available to download and install.\n    #[arg(long)]\n    all: bool,\n}\n\nimpl List {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        // This `match` part is only here because at one point we had a bug where we were creating\n        // symlinks that contained the _entire_ path, rather than just the relative path to the\n        // version directory. It's not strictly necessary, it just fixes our determination of what\n        // the current version is, for the output of this command.\n        //\n        // That symlink bug was fixed in `crates/paths/src/cli.rs` in\n        // https://github.com/clockworklabs/SpacetimeDB/pull/2680, but this `match` means that this\n        // output will still be correct for any users that already have one of the bugged symlinks.\n        //\n        // Once users upgrade to a version containing #2680, they will have the code that creates\n        // the fixed symlinks. However, that code won't immediately run, since the upgrade will be\n        // running from the previous binary they had. So once they upgrade to a version containing\n        // #2680, _and then_ upgrade once more, their symlinks will be fixed. There's no real\n        // timeline on when everyone will have done that, but hopefully that helps give a sense of\n        // how long this code \"should\" exist for (but it doesn't do any harm afaik).\n        let current = match paths.cli_bin_dir.current_version()? {\n            None => None,\n            Some(path_str) => {\n                let file_name = Path::new(&path_str)\n                    .file_name()\n                    .and_then(|f| f.to_str())\n                    .ok_or(anyhow::anyhow!(\"Could not extract current version\"))?;\n                Some(file_name.to_string())\n            }\n        };\n\n        let versions = if self.all {\n            let client = super::reqwest_client()?;\n            super::tokio_block_on(super::install::available_releases(&client))??\n        } else {\n            paths.cli_bin_dir.installed_versions()?\n        };\n\n        // Sort versions using semver ordering. Versions that fail to parse are\n        // placed at the end in their original listed order.\n        let mut parsed: Vec<(semver::Version, String)> = Vec::new();\n        let mut unparsed: Vec<String> = Vec::new();\n        for ver in versions {\n            match semver::Version::parse(&ver) {\n                Ok(sv) => parsed.push((sv, ver)),\n                Err(_) => unparsed.push(ver),\n            }\n        }\n        parsed.sort_by(|(a, _), (b, _)| b.cmp(a));\n\n        let sorted_versions: Vec<String> = parsed.into_iter().map(|(_, s)| s).chain(unparsed).collect();\n        for ver in &sorted_versions {\n            let is_current = Some(ver) == current.as_ref();\n\n            if is_current {\n                println!(\"{} (current)\", ver);\n            } else {\n                println!(\"{ver}\");\n            }\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/update/src/cli/self_install.rs",
    "content": "use std::process::ExitCode;\n\nuse anyhow::Context;\nuse spacetimedb_paths::{RootDir, SpacetimePaths};\n\nuse crate::cli::ForceYes;\n\n/// Install the SpacetimeDB CLI.\n// NOTICE: If you change anything here, please make the same changes in spacetime-install.sh\n#[derive(clap::Parser)]\n#[command(bin_name = \"spacetime-install[EXE]\")]\npub struct SelfInstall {\n    /// The directory to locally install SpacetimeDB into. If unspecified, uses platform defaults.\n    #[arg(long)]\n    root_dir: Option<RootDir>,\n\n    #[command(flatten)]\n    yes: ForceYes,\n}\n\nimpl SelfInstall {\n    pub fn exec(self) -> anyhow::Result<ExitCode> {\n        let paths = match &self.root_dir {\n            Some(root_dir) => SpacetimePaths::from_root_dir(root_dir),\n            None => SpacetimePaths::platform_defaults()?,\n        };\n\n        let SpacetimePaths {\n            cli_config_dir,\n            cli_bin_file,\n            cli_bin_dir,\n            data_dir,\n        } = &paths;\n\n        let root_dir = self.root_dir.or_else(|| paths.to_root_dir());\n        eprint!(\"The SpacetimeDB command line tool will now be installed\");\n        if let Some(root_dir) = &root_dir {\n            eprintln!(\" into {}\", root_dir.display());\n        } else {\n            eprintln!(\":\");\n            eprintln!(\"\\tCLI configuration directory: {}\", cli_config_dir.display());\n            eprintln!(\"\\t`spacetime` binary: {}\", cli_bin_file.display());\n            eprintln!(\n                \"\\tdirectory for installed SpacetimeDB versions: {}\",\n                cli_bin_dir.display()\n            );\n            eprintln!(\"\\tdatabase directory: {}\", data_dir.display());\n        }\n        if !self\n            .yes\n            .confirm_with_default(\"Would you like to continue?\".to_owned(), true)?\n        {\n            eprintln!(\"Exiting.\");\n            return Ok(ExitCode::FAILURE);\n        }\n\n        let current_exe = std::env::current_exe().context(\"could not get current exe\")?;\n        let suppress_eexists = |r: std::io::Result<()>| {\n            r.or_else(|e| (e.kind() == std::io::ErrorKind::AlreadyExists).then_some(()).ok_or(e))\n        };\n        suppress_eexists(cli_bin_dir.create()).context(\"could not create bin dir\")?;\n        suppress_eexists(cli_config_dir.create()).context(\"could not create config dir\")?;\n        suppress_eexists(data_dir.create()).context(\"could not create data dir\")?;\n        cli_bin_file\n            .create_parent()\n            .and_then(|()| std::fs::copy(&current_exe, cli_bin_file))\n            .context(\"could not install binary\")?;\n\n        eprintln!(\"Downloading latest version...\");\n        super::upgrade::Upgrade {}\n            .exec(&paths)\n            .context(\"failed to download and install latest SpacetimeDB version\")?;\n\n        eprintln!(\n            \"The `spacetime` command has been installed as {}\",\n            cli_bin_file.display()\n        );\n        eprintln!();\n\n        if cfg!(unix) {\n            let path_var = std::env::var_os(\"PATH\").unwrap_or_default();\n            let bin_dir = cli_bin_file.0.parent().unwrap();\n            if !std::env::split_paths(&path_var).any(|p| p == bin_dir) {\n                eprintln!(\n                    \"\\\nIt seems like this directory is not in your `PATH` variable. Please add the\nfollowing line to your shell configuration and open a new shell session:\n\n    export PATH=\\\"{}:$PATH\\\"\n\",\n                    bin_dir.display()\n                )\n            }\n        }\n\n        eprintln!(\n            \"\\\nThe install process is complete; check out our quickstart guide to get started!\n\t<https://spacetimedb.com/docs/getting-started>\"\n        );\n\n        Ok(ExitCode::SUCCESS)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use clap::CommandFactory;\n\n    use super::*;\n\n    #[test]\n    fn ensure_script_help_is_up_to_date() {\n        let help_text = SelfInstall::command().term_width(80).render_long_help().to_string();\n        let script = std::fs::read_to_string(concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/spacetime-install.sh\")).unwrap();\n        let (_, heredoc) = script\n            .split_once(\"usage() {\\n    cat <<EOF\\n\")\n            .expect(\"couldn't find usage function\");\n        let (heredoc, _) = heredoc.split_once(\"EOF\").expect(\"couldn't find end of heredoc\");\n        assert!(\n            help_text == heredoc,\n            \"the usage text in spacetime-install.sh is out of date from the CLI. it should be:\\n{help_text}\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/update/src/cli/uninstall.rs",
    "content": "use anyhow::Context;\nuse spacetimedb_paths::cli::BinDir;\nuse spacetimedb_paths::SpacetimePaths;\n\nuse super::ForceYes;\n\n/// Uninstall an installed SpacetimeDB version.\n#[derive(clap::Args)]\npub(super) struct Uninstall {\n    version: String,\n    #[command(flatten)]\n    yes: ForceYes,\n}\n\nimpl Uninstall {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        let Self { version, yes } = self;\n        anyhow::ensure!(\n            version != BinDir::CURRENT_VERSION_DIR_NAME,\n            \"cannot remove `current` version\"\n        );\n        match paths\n            .cli_bin_dir\n            .current_version()\n            .context(\"couldn't read current version\")\n        {\n            Ok(Some(current)) => anyhow::ensure!(version != current, \"cannot uninstall currently used version\"),\n            Ok(None) => {}\n            Err(e) => tracing::warn!(\"{e:#}\"),\n        }\n        if yes.confirm(format!(\"Uninstall v{version}?\"))? {\n            let dir = paths.cli_bin_dir.version_dir(&version);\n            std::fs::remove_dir_all(dir)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/update/src/cli/upgrade.rs",
    "content": "use std::io::Write;\n\nuse anyhow::Context;\nuse spacetimedb_paths::SpacetimePaths;\n\nuse super::install::{download_and_install, download_with_progress, make_progress_bar, mirror_asset_url};\n\n/// Upgrade and switch to the latest available version of SpacetimeDB.\n#[derive(clap::Args)]\npub(super) struct Upgrade {}\n\nimpl Upgrade {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        super::tokio_block_on(async {\n            let client = super::reqwest_client()?;\n            let (version, release) = download_and_install(&client, None, None, paths).await?;\n            paths.cli_bin_dir.set_current_version(&version.to_string())?;\n\n            let cur_version = semver::Version::parse(env!(\"CARGO_PKG_VERSION\")).unwrap();\n            if version > cur_version {\n                let mirror_url = mirror_asset_url(&version, UPDATE_BIN_NAME);\n                let download_url = match release.assets.iter().find(|asset| asset.name == UPDATE_BIN_NAME) {\n                    Some(asset) => asset.browser_download_url.clone(),\n                    None => mirror_url.clone(),\n                };\n\n                let pb = make_progress_bar().with_prefix(\"Self-updating `spacetime version`: \");\n                pb.set_message(\"downloading...\");\n                let bin = match download_with_progress(&pb, &client, &download_url).await {\n                    Ok(bin) => bin,\n                    Err(primary_err) => {\n                        if download_url == mirror_url {\n                            return Err(primary_err);\n                        }\n                        pb.set_message(\"download failed, trying mirror...\");\n                        download_with_progress(&pb, &client, &mirror_url).await?\n                    }\n                };\n                pb.set_message(\"installing...\");\n                let cli_bin_file = paths.cli_bin_file.clone();\n                tokio::task::spawn_blocking(move || {\n                    // TODO(noa): try and see if `self_replace` could support providing the binary\n                    // in a buffer, instead of an already existing file, since we're doing the same\n                    // work they are right now\n                    let mut file = tempfile::NamedTempFile::with_prefix_in(\n                        \".spacetimedb-self-replace\",\n                        cli_bin_file.0.parent().unwrap(),\n                    )?;\n                    file.write_all(&bin.to_bytes())?;\n                    self_replace::self_replace(file.path()).context(\"failed to overwrite the original spacetime binary\")\n                })\n                .await??;\n\n                pb.finish_with_message(\"done!\");\n            }\n\n            Ok(())\n        })?\n    }\n}\n\nconst UPDATE_BIN_NAME: &str = if cfg!(windows) {\n    concat!(\"spacetimedb-update-\", env!(\"BUILD_TARGET\"), \".exe\")\n} else {\n    concat!(\"spacetimedb-update-\", env!(\"BUILD_TARGET\"))\n};\n"
  },
  {
    "path": "crates/update/src/cli/use.rs",
    "content": "use spacetimedb_paths::SpacetimePaths;\n\n/// Set the global default SpacetimeDB version.\n#[derive(clap::Args)]\npub(super) struct Use {\n    version: semver::Version,\n}\n\nimpl Use {\n    pub(super) fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        paths.cli_bin_dir.set_current_version(&self.version.to_string())\n    }\n}\n"
  },
  {
    "path": "crates/update/src/cli.rs",
    "content": "#![allow(clippy::disallowed_macros)]\n\nuse std::ffi::OsString;\nuse std::future::Future;\nuse std::process::ExitCode;\n\nuse spacetimedb_paths::{RootDir, SpacetimePaths};\n\npub(crate) mod install;\nmod link;\nmod list;\nmod self_install;\nmod uninstall;\nmod upgrade;\nmod r#use;\n\npub use self_install::SelfInstall;\n\n/// Manage installed spacetime versions\n#[derive(clap::Parser)]\n#[command(bin_name = \"spacetime\")]\npub struct Args {\n    #[arg(long)]\n    root_dir: Option<RootDir>,\n    #[command(subcommand)]\n    cmd: Subcommand,\n}\n\nimpl Args {\n    pub fn exec(self) -> anyhow::Result<ExitCode> {\n        let paths = match &self.root_dir {\n            Some(root_dir) => SpacetimePaths::from_root_dir(root_dir),\n            None => SpacetimePaths::platform_defaults()?,\n        };\n        match self.cmd {\n            Subcommand::Cli { args: mut cli_args } => {\n                if let Some(root_dir) = &self.root_dir {\n                    cli_args.insert(0, OsString::from_iter([\"--root-dir=\".as_ref(), root_dir.as_ref()]));\n                }\n                crate::proxy::run_cli(Some(&paths), None, cli_args)\n            }\n            Subcommand::Version(version) => version.exec(&paths).map(|()| ExitCode::SUCCESS),\n            Subcommand::SelfInstall(self_install) => self_install.exec(),\n        }\n    }\n}\n\n#[derive(clap::Subcommand)]\nenum Subcommand {\n    Version(Version),\n    #[command(hide = true)]\n    Cli {\n        #[clap(allow_hyphen_values = true)]\n        args: Vec<OsString>,\n    },\n    SelfInstall(SelfInstall),\n}\n\n#[derive(clap::Args)]\n#[command(arg_required_else_help = true)]\nstruct Version {\n    #[command(subcommand)]\n    subcmd: VersionSubcommand,\n}\n\nimpl Version {\n    fn exec(self, paths: &SpacetimePaths) -> anyhow::Result<()> {\n        use VersionSubcommand::*;\n        match self.subcmd {\n            List(subcmd) => subcmd.exec(paths),\n            Use(subcmd) => subcmd.exec(paths),\n            Upgrade(subcmd) => subcmd.exec(paths),\n            Install(subcmd) => subcmd.exec(paths),\n            Uninstall(subcmd) => subcmd.exec(paths),\n            Link(subcmd) => subcmd.exec(paths),\n        }\n    }\n}\n\n#[derive(clap::Subcommand)]\nenum VersionSubcommand {\n    List(list::List),\n    Use(r#use::Use),\n    Upgrade(upgrade::Upgrade),\n    Install(install::Install),\n    Uninstall(uninstall::Uninstall),\n    #[command(hide = true)]\n    Link(link::Link),\n}\n\npub(crate) fn reqwest_client_builder() -> reqwest::ClientBuilder {\n    let mut client = reqwest::Client::builder();\n    #[cfg(feature = \"github-token-auth\")]\n    {\n        use reqwest::header;\n        if let Ok(token) = std::env::var(\"GITHUB_TOKEN\") {\n            eprintln!(\"HTTP requests will use the GITHUB_TOKEN from your environment\");\n            let mut headers = header::HeaderMap::new();\n            headers.insert(header::AUTHORIZATION, format!(\"Bearer {}\", token).parse().unwrap());\n            client = client.default_headers(headers);\n        }\n    }\n    client = client.user_agent(format!(\"SpacetimeDB CLI/{}\", env!(\"CARGO_PKG_VERSION\")));\n    client\n}\n\npub(crate) fn reqwest_client() -> anyhow::Result<reqwest::Client> {\n    Ok(reqwest_client_builder().build()?)\n}\n\npub(crate) fn tokio_block_on<Fut: Future>(fut: Fut) -> anyhow::Result<Fut::Output> {\n    Ok(tokio::runtime::Runtime::new()?.block_on(fut))\n}\n\n#[derive(clap::Args, Copy, Clone)]\nstruct ForceYes {\n    /// Skip the confirmation dialog.\n    #[arg(long, short)]\n    yes: bool,\n}\n\nimpl ForceYes {\n    fn confirm_with_default(self, prompt: String, default: bool) -> anyhow::Result<bool> {\n        let yes = self.yes\n            || dialoguer::Confirm::new()\n                .with_prompt(prompt)\n                .default(default)\n                .interact()?;\n        Ok(yes)\n    }\n    fn confirm(self, prompt: String) -> anyhow::Result<bool> {\n        self.confirm_with_default(prompt, false)\n    }\n}\n"
  },
  {
    "path": "crates/update/src/main.rs",
    "content": "use std::path::{Path, PathBuf};\nuse std::process::ExitCode;\n\nuse anyhow::Context;\nuse clap::Parser;\n\nmod cli;\nmod proxy;\nmod update_notice;\n\nfn main() -> anyhow::Result<ExitCode> {\n    let mut args = std::env::args_os();\n    let argv0: PathBuf = args.next().unwrap().into();\n    let env_cmd = std::env::var_os(\"SPACETIMEDB_UPDATE_MULTICALL_APPLET\");\n    let cmd = if let Some(cmd) = &env_cmd {\n        cmd\n    } else {\n        argv0.file_stem().context(\"argv0 must have a filename\")?\n    };\n    if cmd == \"spacetimedb-update\" {\n        spacetimedb_update_main()\n    } else if cmd == \"spacetime\" {\n        let args = args.collect::<Vec<_>>();\n        if args.first().is_some_and(|s| s == \"version\") {\n            // if the first arg is unambiguously `version`, go straight to `spacetime version`\n            spacetimedb_update_main()\n        } else {\n            proxy::run_cli(None, Some(argv0.as_os_str()), args)\n        }\n    } else if cmd == \"spacetime-install\" {\n        cli::SelfInstall::parse().exec()\n    } else {\n        anyhow::bail!(\n            \"unknown command name for spacetimedb-update multicall binary: {}\",\n            Path::new(cmd).display()\n        )\n    }\n}\n\nfn spacetimedb_update_main() -> anyhow::Result<ExitCode> {\n    let args = cli::Args::parse();\n    args.exec()\n}\n"
  },
  {
    "path": "crates/update/src/proxy.rs",
    "content": "use anyhow::Context;\nuse spacetimedb_paths::{FromPathUnchecked, RootDir, SpacetimePaths};\nuse std::ffi::OsStr;\nuse std::ffi::OsString;\nuse std::io;\nuse std::path::PathBuf;\nuse std::process::Command;\nuse std::process::ExitCode;\n\npub(crate) fn run_cli(\n    paths: Option<&SpacetimePaths>,\n    _argv0: Option<&OsStr>,\n    args: Vec<OsString>,\n) -> anyhow::Result<ExitCode> {\n    let parse_args = || PartialCliArgs::parse(&args);\n    let mut is_version_subcommand = None;\n    let paths_;\n    let paths = match paths {\n        Some(paths) => paths,\n        None => {\n            let partial_args = parse_args()?;\n            is_version_subcommand = Some(partial_args.is_version_subcommand());\n            paths_ = match partial_args.root_dir {\n                Some(root_dir) => SpacetimePaths::from_root_dir(&root_dir),\n                None => SpacetimePaths::platform_defaults()?,\n            };\n            &paths_\n        }\n    };\n    let cli_path = if let Some(artifact_dir) = running_from_target_dir() {\n        let cli_path = spacetimedb_paths::cli::VersionBinDir::from_path_unchecked(artifact_dir).spacetimedb_cli();\n        anyhow::ensure!(\n            cli_path.0.exists(),\n            \"running spacetimedb-update's cli proxy from a target/ directory, but the\n             spacetimedb-cli binary doesn't exist. try running `cargo build -p spacetimedb-cli`\"\n        );\n        cli_path\n    } else {\n        paths.cli_bin_dir.current_version_dir().spacetimedb_cli()\n    };\n\n    // Check for updates before exec'ing the CLI. On Unix, exec replaces\n    // the process so this is our only chance to print a notice.\n    crate::update_notice::maybe_print_update_notice(paths.cli_config_dir.as_ref());\n\n    let mut cmd = Command::new(&cli_path);\n    cmd.args(&args);\n    #[cfg(unix)]\n    {\n        use std::os::unix::process::CommandExt;\n        if let Some(_argv0) = _argv0 {\n            cmd.arg0(_argv0);\n        }\n    };\n    let exec_result = exec_replace(&mut cmd).with_context(|| format!(\"exec failed for {}\", cli_path.display()));\n    let exec_err = match exec_result {\n        Ok(status) => return Ok(status),\n        Err(err) => err,\n    };\n    // if we failed to exec cli and it seems like the user is trying to run `spacetime version`,\n    // patch them through directly.\n    if is_version_subcommand.unwrap_or_else(|| parse_args().is_ok_and(|a| a.is_version_subcommand())) {\n        return crate::spacetimedb_update_main();\n    }\n    Err(exec_err)\n        .context(format!(\"exec failed for {}\", cli_path.display()))\n        .context(\n            \"It seems like the spacetime version set as current may not exist. Try using `spacetime version`\\n\\\n             to set a different version as default or to install a new version altogether.\",\n        )\n}\n\n// implementation based on and docs taken verbatim from `cargo_util::ProcessBuilder::exec_replace`\n//\n/// Replaces the current process with the target process.\n///\n/// On Unix, this executes the process using the Unix syscall `execvp`, which will block\n/// this process, and will only return if there is an error.\n///\n/// On Windows this isn't technically possible. Instead we emulate it to the best of our\n/// ability. One aspect we fix here is that we specify a handler for the Ctrl-C handler.\n/// In doing so (and by effectively ignoring it) we should emulate proxying Ctrl-C\n/// handling to the application at hand, which will either terminate or handle it itself.\n/// According to Microsoft's documentation at\n/// <https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals>.\n/// the Ctrl-C signal is sent to all processes attached to a terminal, which should\n/// include our child process. If the child terminates then we'll reap them in Cargo\n/// pretty quickly, and if the child handles the signal then we won't terminate\n/// (and we shouldn't!) until the process itself later exits.\nfn exec_replace(cmd: &mut Command) -> io::Result<ExitCode> {\n    #[cfg(unix)]\n    {\n        use std::os::unix::process::CommandExt;\n        // if exec() succeeds, it diverges, so the function just returns an io::Error\n        let err = cmd.exec();\n        Err(err)\n    }\n    #[cfg(windows)]\n    {\n        use windows_sys::Win32::Foundation::{BOOL, FALSE, TRUE};\n        use windows_sys::Win32::System::Console::SetConsoleCtrlHandler;\n\n        unsafe extern \"system\" fn ctrlc_handler(_: u32) -> BOOL {\n            // Do nothing. Let the child process handle it.\n            TRUE\n        }\n        unsafe {\n            if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {\n                return Err(io::Error::other(\"Unable to set console handler\"));\n            }\n        }\n\n        cmd.status()\n            .map(|status| ExitCode::from(status.code().unwrap_or(1).try_into().unwrap_or(1)))\n    }\n}\n\n/// Checks to see if we're running from a subdirectory of a `target` dir that has a `Cargo.toml`\n/// as a sibling, and returns the containing directory of the current executable if so.\npub(crate) fn running_from_target_dir() -> Option<PathBuf> {\n    let mut exe_path = std::env::current_exe().ok()?;\n    exe_path.pop();\n    let artifact_dir = exe_path;\n    // check for target/debug/spacetimedb-update and target/x86_64-unknown-foobar/debug/spacetimedb-update\n    let target_dir = artifact_dir\n        .ancestors()\n        .skip(1)\n        .take(2)\n        .find(|p| p.file_name() == Some(\"target\".as_ref()))?;\n    target_dir\n        .parent()?\n        .join(\"Cargo.toml\")\n        .try_exists()\n        .ok()\n        .map(|_| artifact_dir)\n}\n\nstruct PartialCliArgs<'a> {\n    root_dir: Option<RootDir>,\n    maybe_subcommand: Option<&'a OsStr>,\n}\n\nimpl<'a> PartialCliArgs<'a> {\n    fn is_version_subcommand(&self) -> bool {\n        self.maybe_subcommand.is_some_and(|s| s == \"version\")\n    }\n\n    fn parse(args: &'a [OsString]) -> anyhow::Result<Self> {\n        let mut args = args.iter();\n        let mut root_dir = None;\n        let mut maybe_subcommand = None;\n        while let Some(arg) = args.next() {\n            let is_arg_value = |s: &OsStr| !os_str_starts_with(arg, \"-\") || s == \"-\";\n            // \"parse\" only up to the first subcommand\n            if is_arg_value(arg) {\n                maybe_subcommand = Some(&**arg);\n                break;\n            } else if arg == \"--\" {\n                break;\n            }\n            let root_dir_arg = if arg == \"--root-dir\" {\n                args.next()\n                    .filter(|s| is_arg_value(s))\n                    .context(\"a value is required for '--root-dir <root_dir>' but none was supplied\")?\n            } else if let Some(arg) = os_str_strip_prefix(arg, \"--root-dir=\") {\n                arg\n            } else {\n                continue;\n            };\n            anyhow::ensure!(\n                root_dir.is_none(),\n                \"the argument '--root-dir <root_dir>' cannot be used multiple times\"\n            );\n            root_dir = Some(RootDir(root_dir_arg.into()));\n        }\n        Ok(Self {\n            root_dir,\n            maybe_subcommand,\n        })\n    }\n}\n\nfn os_str_starts_with(s: &OsStr, pref: &str) -> bool {\n    s.as_encoded_bytes().starts_with(pref.as_bytes())\n}\n\nfn os_str_strip_prefix<'a>(s: &'a OsStr, pref: &str) -> Option<&'a OsStr> {\n    os_str_starts_with(s, pref).then(|| {\n        // SAFETY: we're splitting immediately after a valid non-empty UTF-8 substring.\n        //         (if pref is empty then this is a no-op and trivially safe)\n        unsafe { OsStr::from_encoded_bytes_unchecked(&s.as_encoded_bytes()[pref.len()..]) }\n    })\n}\n"
  },
  {
    "path": "crates/update/src/update_notice.rs",
    "content": "//! Lightweight update notice check for the proxy path.\n//!\n//! Before exec'ing the CLI, we check a cache file to see if a newer version\n//! is available. If the cache is stale (>24h), we do a quick HTTP check with\n//! a short timeout to refresh it. The notice is printed to stderr.\n\nuse std::path::{Path, PathBuf};\nuse std::time::{Duration, SystemTime, UNIX_EPOCH};\n\nuse colored::Colorize;\n\nuse crate::cli::install::fetch_latest_release_version;\n\n/// How long to cache the update check result.\nconst CHECK_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);\nconst UPDATE_CHECK_TIMEOUT: Duration = Duration::from_secs(2);\n\n/// Cache file name.\nconst CACHE_FILENAME: &str = \".update_check_cache\";\n\n#[derive(serde::Serialize, serde::Deserialize, Default)]\nstruct Cache {\n    /// Unix timestamp of the last successful check.\n    last_check_secs: u64,\n    /// The latest version string (without \"v\" prefix).\n    latest_version: String,\n}\n\nimpl Cache {\n    fn read(config_dir: &Path) -> Option<Self> {\n        let contents = std::fs::read_to_string(Self::path(config_dir)).ok()?;\n        serde_json::from_str(&contents).ok()\n    }\n\n    fn write(&self, config_dir: &Path) {\n        if let Ok(json) = serde_json::to_string(self) {\n            let _ = std::fs::write(Self::path(config_dir), json);\n        }\n    }\n\n    fn path(config_dir: &Path) -> PathBuf {\n        config_dir.join(CACHE_FILENAME)\n    }\n}\n\nfn now_secs() -> u64 {\n    SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .unwrap_or_default()\n        .as_secs()\n}\n\n/// Resolve the latest version, using the cache if fresh or fetching from the network.\n///\n/// On success, updates the cache. On network failure, leaves the cache unchanged\n/// so we retry on the next invocation.\nfn latest_version_or_cached(config_dir: &Path) -> Option<semver::Version> {\n    let cache = Cache::read(config_dir);\n    let now = now_secs();\n\n    // Cache is fresh — use it.\n    if let Some(ref cache) = cache\n        && now.saturating_sub(cache.last_check_secs) < CHECK_INTERVAL.as_secs()\n    {\n        return semver::Version::parse(&cache.latest_version).ok();\n    }\n\n    // Cache is stale or missing — fetch from network.\n    let client = crate::cli::reqwest_client_builder()\n        .timeout(UPDATE_CHECK_TIMEOUT)\n        .build()\n        .ok()?;\n\n    let latest = crate::cli::tokio_block_on(async { fetch_latest_release_version(&client).await }).flatten();\n\n    match latest {\n        Ok(version) => {\n            Cache {\n                last_check_secs: now,\n                latest_version: version.to_string(),\n            }\n            .write(config_dir);\n            Some(version)\n        }\n        Err(e) => {\n            log::debug!(\"Failed to fetch latest version from network; will retry next invocation: {e}\");\n            // Don't update cache — retry next time.\n            // Fall back to stale cache if available.\n            cache.and_then(|c| semver::Version::parse(&c.latest_version).ok())\n        }\n    }\n}\n\n/// Check for updates and print a notice to stderr if a newer version is available.\n///\n/// This is designed to be called from the proxy path before exec'ing the CLI.\n/// It reads a cache file to avoid hitting the network on every invocation.\n/// If the cache is stale, it makes a quick HTTP request (with timeout) to refresh.\n///\n/// `config_dir` should be the SpacetimeDB config directory (e.g. `~/.spacetime`).\n#[allow(clippy::disallowed_macros)]\npub(crate) fn maybe_print_update_notice(config_dir: &Path) {\n    let current = env!(\"CARGO_PKG_VERSION\");\n    let current = match semver::Version::parse(current) {\n        Ok(v) => v,\n        Err(e) => {\n            log::debug!(\"Failed to parse current version: {e}\");\n            return;\n        }\n    };\n\n    let latest = match latest_version_or_cached(config_dir) {\n        Some(v) => v,\n        None => return,\n    };\n\n    if latest > current {\n        eprintln!(\n            \"{}\",\n            format!(\"A new version of SpacetimeDB is available: v{latest} (current: v{current})\").yellow()\n        );\n        eprintln!(\"Run `spacetime version upgrade` to update.\");\n        eprintln!();\n    }\n}\n"
  },
  {
    "path": "d3-flamegraph-base.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Flame Graph</title>\n  <meta name=\"template_version\" content=\"4.1.3\"><script>(()=>{\"use strict\";var t={591:(t,n,e)=>{e.d(n,{Z:()=>u});var r=e(558),i=e.n(r),o=e(361),a=e.n(o)()(i());a.push([t.id,\".d3-flame-graph rect {\\n  stroke: #EEEEEE;\\n  fill-opacity: .8;\\n}\\n\\n.d3-flame-graph rect:hover {\\n  stroke: #474747;\\n  stroke-width: 0.5;\\n  cursor: pointer;\\n}\\n\\n.d3-flame-graph-label {\\n  pointer-events: none;\\n  white-space: nowrap;\\n  text-overflow: ellipsis;\\n  overflow: hidden;\\n  font-size: 12px;\\n  font-family: Verdana;\\n  margin-left: 4px;\\n  margin-right: 4px;\\n  line-height: 1.5;\\n  padding: 0 0 0;\\n  font-weight: 400;\\n  color: black;\\n  text-align: left;\\n}\\n\\n.d3-flame-graph .fade {\\n  opacity: 0.6 !important;\\n}\\n\\n.d3-flame-graph .title {\\n  font-size: 20px;\\n  font-family: Verdana;\\n}\\n\\n.d3-flame-graph-tip {\\n    background-color: black;\\n    border: none;\\n    border-radius: 3px;\\n    padding: 5px 10px 5px 10px;\\n    min-width: 250px;\\n    text-align: left;\\n    color: white;\\n    z-index: 10;\\n}\",\"\"]);const u=a},891:(t,n,e)=>{e.d(n,{Z:()=>u});var r=e(558),i=e.n(r),o=e(361),a=e.n(o)()(i());a.push([t.id,\"#controls button, #controls form {\\n    display: inline;\\n}\\n\\n#content {\\n    margin: 0 auto;\\n    padding: 0 30px 20px;\\n}\\n\\n#details {\\n    margin-top: 20px;\\n    height: 1.2em;\\n}\\n\",\"\"]);const u=a},361:t=>{t.exports=function(t){var n=[];return n.toString=function(){return this.map((function(n){var e=\"\",r=void 0!==n[5];return n[4]&&(e+=\"@supports (\".concat(n[4],\") {\")),n[2]&&(e+=\"@media \".concat(n[2],\" {\")),r&&(e+=\"@layer\".concat(n[5].length>0?\" \".concat(n[5]):\"\",\" {\")),e+=t(n),r&&(e+=\"}\"),n[2]&&(e+=\"}\"),n[4]&&(e+=\"}\"),e})).join(\"\")},n.i=function(t,e,r,i,o){\"string\"==typeof t&&(t=[[null,t,void 0]]);var a={};if(r)for(var u=0;u<this.length;u++){var l=this[u][0];null!=l&&(a[l]=!0)}for(var s=0;s<t.length;s++){var c=[].concat(t[s]);r&&a[c[0]]||(void 0!==o&&(void 0===c[5]||(c[1]=\"@layer\".concat(c[5].length>0?\" \".concat(c[5]):\"\",\" {\").concat(c[1],\"}\")),c[5]=o),e&&(c[2]?(c[1]=\"@media \".concat(c[2],\" {\").concat(c[1],\"}\"),c[2]=e):c[2]=e),i&&(c[4]?(c[1]=\"@supports (\".concat(c[4],\") {\").concat(c[1],\"}\"),c[4]=i):c[4]=\"\".concat(i)),n.push(c))}},n}},558:t=>{t.exports=function(t){return t[1]}},487:t=>{var n=[];function e(t){for(var e=-1,r=0;r<n.length;r++)if(n[r].identifier===t){e=r;break}return e}function r(t,r){for(var o={},a=[],u=0;u<t.length;u++){var l=t[u],s=r.base?l[0]+r.base:l[0],c=o[s]||0,h=\"\".concat(s,\" \").concat(c);o[s]=c+1;var f=e(h),p={css:l[1],media:l[2],sourceMap:l[3],supports:l[4],layer:l[5]};if(-1!==f)n[f].references++,n[f].updater(p);else{var d=i(p,r);r.byIndex=u,n.splice(u,0,{identifier:h,updater:d,references:1})}a.push(h)}return a}function i(t,n){var e=n.domAPI(n);return e.update(t),function(n){if(n){if(n.css===t.css&&n.media===t.media&&n.sourceMap===t.sourceMap&&n.supports===t.supports&&n.layer===t.layer)return;e.update(t=n)}else e.remove()}}t.exports=function(t,i){var o=r(t=t||[],i=i||{});return function(t){t=t||[];for(var a=0;a<o.length;a++){var u=e(o[a]);n[u].references--}for(var l=r(t,i),s=0;s<o.length;s++){var c=e(o[s]);0===n[c].references&&(n[c].updater(),n.splice(c,1))}o=l}}},52:t=>{var n={};t.exports=function(t,e){var r=function(t){if(void 0===n[t]){var e=document.querySelector(t);if(window.HTMLIFrameElement&&e instanceof window.HTMLIFrameElement)try{e=e.contentDocument.head}catch(t){e=null}n[t]=e}return n[t]}(t);if(!r)throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");r.appendChild(e)}},469:t=>{t.exports=function(t){var n=document.createElement(\"style\");return t.setAttributes(n,t.attributes),t.insert(n,t.options),n}},10:(t,n,e)=>{t.exports=function(t){var n=e.nc;n&&t.setAttribute(\"nonce\",n)}},631:t=>{t.exports=function(t){var n=t.insertStyleElement(t);return{update:function(e){!function(t,n,e){var r=\"\";e.supports&&(r+=\"@supports (\".concat(e.supports,\") {\")),e.media&&(r+=\"@media \".concat(e.media,\" {\"));var i=void 0!==e.layer;i&&(r+=\"@layer\".concat(e.layer.length>0?\" \".concat(e.layer):\"\",\" {\")),r+=e.css,i&&(r+=\"}\"),e.media&&(r+=\"}\"),e.supports&&(r+=\"}\");var o=e.sourceMap;o&&\"undefined\"!=typeof btoa&&(r+=\"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(o)))),\" */\")),n.styleTagTransform(r,t,n.options)}(n,t,e)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(n)}}}},329:t=>{t.exports=function(t,n){if(n.styleSheet)n.styleSheet.cssText=t;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(t))}}}},n={};function e(r){var i=n[r];if(void 0!==i)return i.exports;var o=n[r]={id:r,exports:{}};return t[r](o,o.exports,e),o.exports}e.n=t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},e.d=(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},e.o=(t,n)=>Object.prototype.hasOwnProperty.call(t,n),(()=>{function t(){}function n(n){return null==n?t:function(){return this.querySelector(n)}}function r(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function i(){return[]}function o(t){return null==t?i:function(){return this.querySelectorAll(t)}}function a(t){return function(){return this.matches(t)}}function u(t){return function(n){return n.matches(t)}}var l=Array.prototype.find;function s(){return this.firstElementChild}var c=Array.prototype.filter;function h(){return Array.from(this.children)}function f(t){return new Array(t.length)}function p(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function d(t){return function(){return t}}function g(t,n,e,r,i,o){for(var a,u=0,l=n.length,s=o.length;u<s;++u)(a=n[u])?(a.__data__=o[u],r[u]=a):e[u]=new p(t,o[u]);for(;u<l;++u)(a=n[u])&&(i[u]=a)}function v(t,n,e,r,i,o,a){var u,l,s,c=new Map,h=n.length,f=o.length,d=new Array(h);for(u=0;u<h;++u)(l=n[u])&&(d[u]=s=a.call(l,l.__data__,u,n)+\"\",c.has(s)?i[u]=l:c.set(s,l));for(u=0;u<f;++u)s=a.call(t,o[u],u,o)+\"\",(l=c.get(s))?(r[u]=l,l.__data__=o[u],c.delete(s)):e[u]=new p(t,o[u]);for(u=0;u<h;++u)(l=n[u])&&c.get(d[u])===l&&(i[u]=l)}function m(t){return t.__data__}function y(t){return\"object\"==typeof t&&\"length\"in t?t:Array.from(t)}function w(t,n){return t<n?-1:t>n?1:t>=n?0:NaN}p.prototype={constructor:p,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var _=\"http://www.w3.org/1999/xhtml\";const x={svg:\"http://www.w3.org/2000/svg\",xhtml:_,xlink:\"http://www.w3.org/1999/xlink\",xml:\"http://www.w3.org/XML/1998/namespace\",xmlns:\"http://www.w3.org/2000/xmlns/\"};function b(t){var n=t+=\"\",e=n.indexOf(\":\");return e>=0&&\"xmlns\"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),x.hasOwnProperty(n)?{space:x[n],local:t}:t}function M(t){return function(){this.removeAttribute(t)}}function A(t){return function(){this.removeAttributeNS(t.space,t.local)}}function N(t,n){return function(){this.setAttribute(t,n)}}function E(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function k(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function S(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function C(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function I(t){return function(){this.style.removeProperty(t)}}function B(t,n,e){return function(){this.style.setProperty(t,n,e)}}function T(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function L(t,n){return t.style.getPropertyValue(n)||C(t).getComputedStyle(t,null).getPropertyValue(n)}function O(t){return function(){delete this[t]}}function P(t,n){return function(){this[t]=n}}function j(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function q(t){return t.trim().split(/^|\\s+/)}function H(t){return t.classList||new z(t)}function z(t){this._node=t,this._names=q(t.getAttribute(\"class\")||\"\")}function D(t,n){for(var e=H(t),r=-1,i=n.length;++r<i;)e.add(n[r])}function R(t,n){for(var e=H(t),r=-1,i=n.length;++r<i;)e.remove(n[r])}function Z(t){return function(){D(this,t)}}function V(t){return function(){R(this,t)}}function X(t,n){return function(){(n.apply(this,arguments)?D:R)(this,t)}}function $(){this.textContent=\"\"}function F(t){return function(){this.textContent=t}}function G(t){return function(){var n=t.apply(this,arguments);this.textContent=null==n?\"\":n}}function Y(){this.innerHTML=\"\"}function U(t){return function(){this.innerHTML=t}}function W(t){return function(){var n=t.apply(this,arguments);this.innerHTML=null==n?\"\":n}}function J(){this.nextSibling&&this.parentNode.appendChild(this)}function K(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Q(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===_&&n.documentElement.namespaceURI===_?n.createElement(t):n.createElementNS(e,t)}}function tt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function nt(t){var n=b(t);return(n.local?tt:Q)(n)}function et(){return null}function rt(){var t=this.parentNode;t&&t.removeChild(this)}function it(){var t=this.cloneNode(!1),n=this.parentNode;return n?n.insertBefore(t,this.nextSibling):t}function ot(){var t=this.cloneNode(!0),n=this.parentNode;return n?n.insertBefore(t,this.nextSibling):t}function at(t){return t.trim().split(/^|\\s+/).map((function(t){var n=\"\",e=t.indexOf(\".\");return e>=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function ut(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r<o;++r)e=n[r],t.type&&e.type!==t.type||e.name!==t.name?n[++i]=e:this.removeEventListener(e.type,e.listener,e.options);++i?n.length=i:delete this.__on}}}function lt(t,n,e){return function(){var r,i=this.__on,o=function(t){return function(n){t.call(this,n,this.__data__)}}(n);if(i)for(var a=0,u=i.length;a<u;++a)if((r=i[a]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=o,r.options=e),void(r.value=n);this.addEventListener(t.type,o,e),r={type:t.type,name:t.name,value:n,listener:o,options:e},i?i.push(r):this.__on=[r]}}function st(t,n,e){var r=C(t),i=r.CustomEvent;\"function\"==typeof i?i=new i(n,e):(i=r.document.createEvent(\"Event\"),e?(i.initEvent(n,e.bubbles,e.cancelable),i.detail=e.detail):i.initEvent(n,!1,!1)),t.dispatchEvent(i)}function ct(t,n){return function(){return st(this,t,n)}}function ht(t,n){return function(){return st(this,t,n.apply(this,arguments))}}z.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute(\"class\",this._names.join(\" \")))},remove:function(t){var n=this._names.indexOf(t);n>=0&&(this._names.splice(n,1),this._node.setAttribute(\"class\",this._names.join(\" \")))},contains:function(t){return this._names.indexOf(t)>=0}};var ft=[null];function pt(t,n){this._groups=t,this._parents=n}function dt(){return new pt([[document.documentElement]],ft)}pt.prototype=dt.prototype={constructor:pt,select:function(t){\"function\"!=typeof t&&(t=n(t));for(var e=this._groups,r=e.length,i=new Array(r),o=0;o<r;++o)for(var a,u,l=e[o],s=l.length,c=i[o]=new Array(s),h=0;h<s;++h)(a=l[h])&&(u=t.call(a,a.__data__,h,l))&&(\"__data__\"in a&&(u.__data__=a.__data__),c[h]=u);return new pt(i,this._parents)},selectAll:function(t){t=\"function\"==typeof t?function(t){return function(){return r(t.apply(this,arguments))}}(t):o(t);for(var n=this._groups,e=n.length,i=[],a=[],u=0;u<e;++u)for(var l,s=n[u],c=s.length,h=0;h<c;++h)(l=s[h])&&(i.push(t.call(l,l.__data__,h,s)),a.push(l));return new pt(i,a)},selectChild:function(t){return this.select(null==t?s:function(t){return function(){return l.call(this.children,t)}}(\"function\"==typeof t?t:u(t)))},selectChildren:function(t){return this.selectAll(null==t?h:function(t){return function(){return c.call(this.children,t)}}(\"function\"==typeof t?t:u(t)))},filter:function(t){\"function\"!=typeof t&&(t=a(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i<e;++i)for(var o,u=n[i],l=u.length,s=r[i]=[],c=0;c<l;++c)(o=u[c])&&t.call(o,o.__data__,c,u)&&s.push(o);return new pt(r,this._parents)},data:function(t,n){if(!arguments.length)return Array.from(this,m);var e=n?v:g,r=this._parents,i=this._groups;\"function\"!=typeof t&&(t=d(t));for(var o=i.length,a=new Array(o),u=new Array(o),l=new Array(o),s=0;s<o;++s){var c=r[s],h=i[s],f=h.length,p=y(t.call(c,c&&c.__data__,s,r)),w=p.length,_=u[s]=new Array(w),x=a[s]=new Array(w),b=l[s]=new Array(f);e(c,h,_,x,b,p,n);for(var M,A,N=0,E=0;N<w;++N)if(M=_[N]){for(N>=E&&(E=N+1);!(A=x[E])&&++E<w;);M._next=A||null}}return(a=new pt(a,r))._enter=u,a._exit=l,a},enter:function(){return new pt(this._enter||this._groups.map(f),this._parents)},exit:function(){return new pt(this._exit||this._groups.map(f),this._parents)},join:function(t,n,e){var r=this.enter(),i=this,o=this.exit();return\"function\"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+\"\"),null!=n&&(i=n(i))&&(i=i.selection()),null==e?o.remove():e(o),r&&i?r.merge(i).order():i},merge:function(t){for(var n=t.selection?t.selection():t,e=this._groups,r=n._groups,i=e.length,o=r.length,a=Math.min(i,o),u=new Array(i),l=0;l<a;++l)for(var s,c=e[l],h=r[l],f=c.length,p=u[l]=new Array(f),d=0;d<f;++d)(s=c[d]||h[d])&&(p[d]=s);for(;l<i;++l)u[l]=e[l];return new pt(u,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,n=-1,e=t.length;++n<e;)for(var r,i=t[n],o=i.length-1,a=i[o];--o>=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=w);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o<r;++o){for(var a,u=e[o],l=u.length,s=i[o]=new Array(l),c=0;c<l;++c)(a=u[c])&&(s[c]=a);s.sort(n)}return new pt(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,n=0,e=t.length;n<e;++n)for(var r=t[n],i=0,o=r.length;i<o;++i){var a=r[i];if(a)return a}return null},size:function(){let t=0;for(const n of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var n=this._groups,e=0,r=n.length;e<r;++e)for(var i,o=n[e],a=0,u=o.length;a<u;++a)(i=o[a])&&t.call(i,i.__data__,a,o);return this},attr:function(t,n){var e=b(t);if(arguments.length<2){var r=this.node();return e.local?r.getAttributeNS(e.space,e.local):r.getAttribute(e)}return this.each((null==n?e.local?A:M:\"function\"==typeof n?e.local?S:k:e.local?E:N)(e,n))},style:function(t,n,e){return arguments.length>1?this.each((null==n?I:\"function\"==typeof n?T:B)(t,n,null==e?\"\":e)):L(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?O:\"function\"==typeof n?j:P)(t,n)):this.node()[t]},classed:function(t,n){var e=q(t+\"\");if(arguments.length<2){for(var r=H(this.node()),i=-1,o=e.length;++i<o;)if(!r.contains(e[i]))return!1;return!0}return this.each((\"function\"==typeof n?X:n?Z:V)(e,n))},text:function(t){return arguments.length?this.each(null==t?$:(\"function\"==typeof t?G:F)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Y:(\"function\"==typeof t?W:U)(t)):this.node().innerHTML},raise:function(){return this.each(J)},lower:function(){return this.each(K)},append:function(t){var n=\"function\"==typeof t?t:nt(t);return this.select((function(){return this.appendChild(n.apply(this,arguments))}))},insert:function(t,e){var r=\"function\"==typeof t?t:nt(t),i=null==e?et:\"function\"==typeof e?e:n(e);return this.select((function(){return this.insertBefore(r.apply(this,arguments),i.apply(this,arguments)||null)}))},remove:function(){return this.each(rt)},clone:function(t){return this.select(t?ot:it)},datum:function(t){return arguments.length?this.property(\"__data__\",t):this.node().__data__},on:function(t,n,e){var r,i,o=at(t+\"\"),a=o.length;if(!(arguments.length<2)){for(u=n?lt:ut,r=0;r<a;++r)this.each(u(o[r],n,e));return this}var u=this.node().__on;if(u)for(var l,s=0,c=u.length;s<c;++s)for(r=0,l=u[s];r<a;++r)if((i=o[r]).type===l.type&&i.name===l.name)return l.value},dispatch:function(t,n){return this.each((\"function\"==typeof n?ht:ct)(t,n))},[Symbol.iterator]:function*(){for(var t=this._groups,n=0,e=t.length;n<e;++n)for(var r,i=t[n],o=0,a=i.length;o<a;++o)(r=i[o])&&(yield r)}};const gt=dt;function vt(t){return\"string\"==typeof t?new pt([[document.querySelector(t)]],[document.documentElement]):new pt([[t]],ft)}function mt(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf(\"e\"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function yt(t){return(t=mt(Math.abs(t)))?t[1]:NaN}var wt,_t=/^(?:(.)?([<>=^]))?([+\\-( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?(~)?([a-z%])?$/i;function xt(t){if(!(n=_t.exec(t)))throw new Error(\"invalid format: \"+t);var n;return new bt({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function bt(t){this.fill=void 0===t.fill?\" \":t.fill+\"\",this.align=void 0===t.align?\">\":t.align+\"\",this.sign=void 0===t.sign?\"-\":t.sign+\"\",this.symbol=void 0===t.symbol?\"\":t.symbol+\"\",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?\"\":t.type+\"\"}function Mt(t,n){var e=mt(t,n);if(!e)return t+\"\";var r=e[0],i=e[1];return i<0?\"0.\"+new Array(-i).join(\"0\")+r:r.length>i+1?r.slice(0,i+1)+\".\"+r.slice(i+1):r+new Array(i-r.length+2).join(\"0\")}xt.prototype=bt.prototype,bt.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?\"0\":\"\")+(void 0===this.width?\"\":Math.max(1,0|this.width))+(this.comma?\",\":\"\")+(void 0===this.precision?\"\":\".\"+Math.max(0,0|this.precision))+(this.trim?\"~\":\"\")+this.type};const At={\"%\":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+\"\",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString(\"en\").replace(/,/g,\"\"):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>Mt(100*t,n),r:Mt,s:function(t,n){var e=mt(t,n);if(!e)return t+\"\";var r=e[0],i=e[1],o=i-(wt=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join(\"0\"):o>0?r.slice(0,o)+\".\"+r.slice(o):\"0.\"+new Array(1-o).join(\"0\")+mt(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Nt(t){return t}var Et,kt,St,Ct=Array.prototype.map,It=[\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"µ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];function Bt(t,n){return null==t||null==n?NaN:t<n?-1:t>n?1:t>=n?0:NaN}function Tt(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function Lt(t){var n=0,e=t.children,r=e&&e.length;if(r)for(;--r>=0;)n+=e[r].value;else n=1;t.value=n}function Ot(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=jt)):void 0===n&&(n=Pt);for(var e,r,i,o,a,u=new zt(t),l=[u];e=l.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)l.push(r=i[o]=new zt(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Ht)}function Pt(t){return t.children}function jt(t){return Array.isArray(t)?t[1]:null}function qt(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Ht(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function zt(t){this.data=t,this.depth=this.height=0,this.parent=null}Et=function(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?Nt:(n=Ct.call(t.grouping,Number),e=t.thousands+\"\",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],l=0;i>0&&u>0&&(l+u+1>r&&(u=Math.max(1,r-l)),o.push(t.substring(i-=u,i+u)),!((l+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?\"\":t.currency[0]+\"\",o=void 0===t.currency?\"\":t.currency[1]+\"\",a=void 0===t.decimal?\".\":t.decimal+\"\",u=void 0===t.numerals?Nt:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(Ct.call(t.numerals,String)),l=void 0===t.percent?\"%\":t.percent+\"\",s=void 0===t.minus?\"−\":t.minus+\"\",c=void 0===t.nan?\"NaN\":t.nan+\"\";function h(t){var n=(t=xt(t)).fill,e=t.align,h=t.sign,f=t.symbol,p=t.zero,d=t.width,g=t.comma,v=t.precision,m=t.trim,y=t.type;\"n\"===y?(g=!0,y=\"g\"):At[y]||(void 0===v&&(v=12),m=!0,y=\"g\"),(p||\"0\"===n&&\"=\"===e)&&(p=!0,n=\"0\",e=\"=\");var w=\"$\"===f?i:\"#\"===f&&/[boxX]/.test(y)?\"0\"+y.toLowerCase():\"\",_=\"$\"===f?o:/[%p]/.test(y)?l:\"\",x=At[y],b=/[defgprs%]/.test(y);function M(t){var i,o,l,f=w,M=_;if(\"c\"===y)M=x(t)+M,t=\"\";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?c:x(Math.abs(t),v),m&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r<e;++r)switch(t[r]){case\".\":i=n=r;break;case\"0\":0===i&&(i=r),n=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&\"+\"!==h&&(A=!1),f=(A?\"(\"===h?h:s:\"-\"===h||\"(\"===h?\"\":h)+f,M=(\"s\"===y?It[8+wt/3]:\"\")+M+(A&&\"(\"===h?\")\":\"\"),b)for(i=-1,o=t.length;++i<o;)if(48>(l=t.charCodeAt(i))||l>57){M=(46===l?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!p&&(t=r(t,1/0));var N=f.length+t.length+M.length,E=N<d?new Array(d-N+1).join(n):\"\";switch(g&&p&&(t=r(E+t,E.length?d-M.length:1/0),E=\"\"),e){case\"<\":t=f+t+M+E;break;case\"=\":t=f+E+t+M;break;case\"^\":t=E.slice(0,N=E.length>>1)+f+t+M+E.slice(N);break;default:t=E+f+t+M}return u(t)}return v=void 0===v?6:/[gprs]/.test(y)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v)),M.toString=function(){return t+\"\"},M}return{format:h,formatPrefix:function(t,n){var e=h(((t=xt(t)).type=\"f\",t)),r=3*Math.max(-8,Math.min(8,Math.floor(yt(n)/3))),i=Math.pow(10,-r),o=It[8+r/3];return function(t){return e(i*t)+o}}}}({thousands:\",\",grouping:[3],currency:[\"$\",\"\"]}),kt=Et.format,St=Et.formatPrefix,zt.prototype=Ot.prototype={constructor:zt,count:function(){return this.eachAfter(Lt)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],l=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r<i;++r)a.push(e[r]);for(;o=u.pop();)t.call(n,o,++l,this);return this},eachBefore:function(t,n){for(var e,r,i=this,o=[i],a=-1;i=o.pop();)if(t.call(n,i,++a,this),e=i.children)for(r=e.length-1;r>=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Ot(this).eachBefore(qt)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e<r;++e)o.push(n[e])}while(o.length)}};var Dt=Math.sqrt(50),Rt=Math.sqrt(10),Zt=Math.sqrt(2);function Vt(t,n,e){var r=(n-t)/Math.max(0,e),i=Math.floor(Math.log(r)/Math.LN10),o=r/Math.pow(10,i);return i>=0?(o>=Dt?10:o>=Rt?5:o>=Zt?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=Dt?10:o>=Rt?5:o>=Zt?2:1)}function Xt(t){let n=t,e=t,r=t;function i(t,n,i=0,o=t.length){if(i<o){if(0!==e(n,n))return o;do{const e=i+o>>>1;r(t[e],n)<0?i=e+1:o=e}while(i<o)}return i}return 2!==t.length&&(n=(n,e)=>t(n)-e,e=Bt,r=(n,e)=>Bt(t(n),e)),{left:i,center:function(t,e,r=0,o=t.length){const a=i(t,e,r,o-1);return a>r&&n(t[a-1],e)>-n(t[a],e)?a-1:a},right:function(t,n,i=0,o=t.length){if(i<o){if(0!==e(n,n))return o;do{const e=i+o>>>1;r(t[e],n)<=0?i=e+1:o=e}while(i<o)}return i}}}const $t=Xt(Bt),Ft=$t.right,Gt=($t.left,Xt((function(t){return null===t?NaN:+t})).center,Ft);function Yt(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function Ut(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function Wt(){}var Jt=.7,Kt=1/Jt,Qt=\"\\\\s*([+-]?\\\\d+)\\\\s*\",tn=\"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",nn=\"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",en=/^#([0-9a-f]{3,8})$/,rn=new RegExp(\"^rgb\\\\(\"+[Qt,Qt,Qt]+\"\\\\)$\"),on=new RegExp(\"^rgb\\\\(\"+[nn,nn,nn]+\"\\\\)$\"),an=new RegExp(\"^rgba\\\\(\"+[Qt,Qt,Qt,tn]+\"\\\\)$\"),un=new RegExp(\"^rgba\\\\(\"+[nn,nn,nn,tn]+\"\\\\)$\"),ln=new RegExp(\"^hsl\\\\(\"+[tn,nn,nn]+\"\\\\)$\"),sn=new RegExp(\"^hsla\\\\(\"+[tn,nn,nn,tn]+\"\\\\)$\"),cn={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function hn(){return this.rgb().formatHex()}function fn(){return this.rgb().formatRgb()}function pn(t){var n,e;return t=(t+\"\").trim().toLowerCase(),(n=en.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?dn(n):3===e?new yn(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?gn(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?gn(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=rn.exec(t))?new yn(n[1],n[2],n[3],1):(n=on.exec(t))?new yn(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=an.exec(t))?gn(n[1],n[2],n[3],n[4]):(n=un.exec(t))?gn(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=ln.exec(t))?bn(n[1],n[2]/100,n[3]/100,1):(n=sn.exec(t))?bn(n[1],n[2]/100,n[3]/100,n[4]):cn.hasOwnProperty(t)?dn(cn[t]):\"transparent\"===t?new yn(NaN,NaN,NaN,0):null}function dn(t){return new yn(t>>16&255,t>>8&255,255&t,1)}function gn(t,n,e,r){return r<=0&&(t=n=e=NaN),new yn(t,n,e,r)}function vn(t){return t instanceof Wt||(t=pn(t)),t?new yn((t=t.rgb()).r,t.g,t.b,t.opacity):new yn}function mn(t,n,e,r){return 1===arguments.length?vn(t):new yn(t,n,e,null==r?1:r)}function yn(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function wn(){return\"#\"+xn(this.r)+xn(this.g)+xn(this.b)}function _n(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?\"rgb(\":\"rgba(\")+Math.max(0,Math.min(255,Math.round(this.r)||0))+\", \"+Math.max(0,Math.min(255,Math.round(this.g)||0))+\", \"+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?\")\":\", \"+t+\")\")}function xn(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?\"0\":\"\")+t.toString(16)}function bn(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new An(t,n,e,r)}function Mn(t){if(t instanceof An)return new An(t.h,t.s,t.l,t.opacity);if(t instanceof Wt||(t=pn(t)),!t)return new An;if(t instanceof An)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,l=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e<r):e===o?(r-n)/u+2:(n-e)/u+4,u/=l<.5?o+i:2-o-i,a*=60):u=l>0&&l<1?0:a,new An(a,u,l,t.opacity)}function An(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Nn(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function En(t,n,e,r,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*n+(4-6*o+3*a)*e+(1+3*t+3*o-3*a)*r+a*i)/6}Yt(Wt,pn,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:hn,formatHex:hn,formatHsl:function(){return Mn(this).formatHsl()},formatRgb:fn,toString:fn}),Yt(yn,mn,Ut(Wt,{brighter:function(t){return t=null==t?Kt:Math.pow(Kt,t),new yn(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Jt:Math.pow(Jt,t),new yn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:wn,formatHex:wn,formatRgb:_n,toString:_n})),Yt(An,(function(t,n,e,r){return 1===arguments.length?Mn(t):new An(t,n,e,null==r?1:r)}),Ut(Wt,{brighter:function(t){return t=null==t?Kt:Math.pow(Kt,t),new An(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Jt:Math.pow(Jt,t),new An(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new yn(Nn(t>=240?t-240:t+120,i,r),Nn(t,i,r),Nn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?\"hsl(\":\"hsla(\")+(this.h||0)+\", \"+100*(this.s||0)+\"%, \"+100*(this.l||0)+\"%\"+(1===t?\")\":\", \"+t+\")\")}}));const kn=t=>()=>t;function Sn(t,n){var e=n-t;return e?function(t,n){return function(e){return t+e*n}}(t,e):kn(isNaN(t)?n:t)}const Cn=function t(n){var e=function(t){return 1==(t=+t)?Sn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):kn(isNaN(n)?e:n)}}(n);function r(t,n){var r=e((t=mn(t)).r,(n=mn(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=Sn(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+\"\"}}return r.gamma=t,r}(1);function In(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;e<i;++e)r=mn(n[e]),o[e]=r.r||0,a[e]=r.g||0,u[e]=r.b||0;return o=t(o),a=t(a),u=t(u),r.opacity=1,function(t){return r.r=o(t),r.g=a(t),r.b=u(t),r+\"\"}}}function Bn(t,n){var e,r=n?n.length:0,i=t?Math.min(r,t.length):0,o=new Array(i),a=new Array(r);for(e=0;e<i;++e)o[e]=zn(t[e],n[e]);for(;e<r;++e)a[e]=n[e];return function(t){for(e=0;e<i;++e)a[e]=o[e](t);return a}}function Tn(t,n){var e=new Date;return t=+t,n=+n,function(r){return e.setTime(t*(1-r)+n*r),e}}function Ln(t,n){return t=+t,n=+n,function(e){return t*(1-e)+n*e}}function On(t,n){var e,r={},i={};for(e in null!==t&&\"object\"==typeof t||(t={}),null!==n&&\"object\"==typeof n||(n={}),n)e in t?r[e]=zn(t[e],n[e]):i[e]=n[e];return function(t){for(e in r)i[e]=r[e](t);return i}}In((function(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r<n-1?t[r+2]:2*o-i;return En((e-r/n)*n,a,i,o,u)}})),In((function(t){var n=t.length;return function(e){var r=Math.floor(((e%=1)<0?++e:e)*n),i=t[(r+n-1)%n],o=t[r%n],a=t[(r+1)%n],u=t[(r+2)%n];return En((e-r/n)*n,i,o,a,u)}}));var Pn=/[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,jn=new RegExp(Pn.source,\"g\");function qn(t,n){var e,r,i,o=Pn.lastIndex=jn.lastIndex=0,a=-1,u=[],l=[];for(t+=\"\",n+=\"\";(e=Pn.exec(t))&&(r=jn.exec(n));)(i=r.index)>o&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,l.push({i:a,x:Ln(e,r)})),o=jn.lastIndex;return o<n.length&&(i=n.slice(o),u[a]?u[a]+=i:u[++a]=i),u.length<2?l[0]?function(t){return function(n){return t(n)+\"\"}}(l[0].x):function(t){return function(){return t}}(n):(n=l.length,function(t){for(var e,r=0;r<n;++r)u[(e=l[r]).i]=e.x(t);return u.join(\"\")})}function Hn(t,n){n||(n=[]);var e,r=t?Math.min(n.length,t.length):0,i=n.slice();return function(o){for(e=0;e<r;++e)i[e]=t[e]*(1-o)+n[e]*o;return i}}function zn(t,n){var e,r,i=typeof n;return null==n||\"boolean\"===i?kn(n):(\"number\"===i?Ln:\"string\"===i?(e=pn(n))?(n=e,Cn):qn:n instanceof pn?Cn:n instanceof Date?Tn:(r=n,!ArrayBuffer.isView(r)||r instanceof DataView?Array.isArray(n)?Bn:\"function\"!=typeof n.valueOf&&\"function\"!=typeof n.toString||isNaN(n)?On:Ln:Hn))(t,n)}function Dn(t,n){return t=+t,n=+n,function(e){return Math.round(t*(1-e)+n*e)}}function Rn(t){return+t}var Zn=[0,1];function Vn(t){return t}function Xn(t,n){return(n-=t=+t)?function(e){return(e-t)/n}:(e=isNaN(n)?NaN:.5,function(){return e});var e}function $n(t,n,e){var r=t[0],i=t[1],o=n[0],a=n[1];return i<r?(r=Xn(i,r),o=e(a,o)):(r=Xn(r,i),o=e(o,a)),function(t){return o(r(t))}}function Fn(t,n,e){var r=Math.min(t.length,n.length)-1,i=new Array(r),o=new Array(r),a=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),n=n.slice().reverse());++a<r;)i[a]=Xn(t[a],t[a+1]),o[a]=e(n[a],n[a+1]);return function(n){var e=Gt(t,n,1,r)-1;return o[e](i[e](n))}}function Gn(t,n){return n.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Yn(){return function(){var t,n,e,r,i,o,a=Zn,u=Zn,l=zn,s=Vn;function c(){var t,n,e,l=Math.min(a.length,u.length);return s!==Vn&&(t=a[0],n=a[l-1],t>n&&(e=t,t=n,n=e),s=function(e){return Math.max(t,Math.min(n,e))}),r=l>2?Fn:$n,i=o=null,h}function h(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,l)))(t(s(n)))}return h.invert=function(e){return s(n((o||(o=r(u,a.map(t),Ln)))(e)))},h.domain=function(t){return arguments.length?(a=Array.from(t,Rn),c()):a.slice()},h.range=function(t){return arguments.length?(u=Array.from(t),c()):u.slice()},h.rangeRound=function(t){return u=Array.from(t),l=Dn,c()},h.clamp=function(t){return arguments.length?(s=!!t||Vn,c()):s!==Vn},h.interpolate=function(t){return arguments.length?(l=t,c()):l},h.unknown=function(t){return arguments.length?(e=t,h):e},function(e,r){return t=e,n=r,c()}}()(Vn,Vn)}function Un(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function Wn(t){var n=t.domain;return t.ticks=function(t){var e=n();return function(t,n,e){var r,i,o,a,u=-1;if(e=+e,(t=+t)==(n=+n)&&e>0)return[t];if((r=n<t)&&(i=t,t=n,n=i),0===(a=Vt(t,n,e))||!isFinite(a))return[];if(a>0){let e=Math.round(t/a),r=Math.round(n/a);for(e*a<t&&++e,r*a>n&&--r,o=new Array(i=r-e+1);++u<i;)o[u]=(e+u)*a}else{a=-a;let e=Math.round(t*a),r=Math.round(n*a);for(e/a<t&&++e,r/a>n&&--r,o=new Array(i=r-e+1);++u<i;)o[u]=(e+u)/a}return r&&o.reverse(),o}(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return function(t,n,e,r){var i,o=function(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=Dt?i*=10:o>=Rt?i*=5:o>=Zt&&(i*=2),n<t?-i:i}(t,n,e);switch((r=xt(null==r?\",f\":r)).type){case\"s\":var a=Math.max(Math.abs(t),Math.abs(n));return null!=r.precision||isNaN(i=function(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(yt(n)/3)))-yt(Math.abs(t)))}(o,a))||(r.precision=i),St(r,a);case\"\":case\"e\":case\"g\":case\"p\":case\"r\":null!=r.precision||isNaN(i=function(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,yt(n)-yt(t))+1}(o,Math.max(Math.abs(t),Math.abs(n))))||(r.precision=i-(\"e\"===r.type));break;case\"f\":case\"%\":null!=r.precision||isNaN(i=function(t){return Math.max(0,-yt(Math.abs(t)))}(o))||(r.precision=i-2*(\"%\"===r.type))}return kt(r)}(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,l=o[a],s=o[u],c=10;for(s<l&&(i=l,l=s,s=i,i=a,a=u,u=i);c-- >0;){if((i=Vt(l,s,e))===r)return o[a]=l,o[u]=s,n(o);if(i>0)l=Math.floor(l/i)*i,s=Math.ceil(s/i)*i;else{if(!(i<0))break;l=Math.ceil(l*i)/i,s=Math.floor(s*i)/i}r=i}return t},t}function Jn(){var t=Yn();return t.copy=function(){return Gn(t,Jn())},Un.apply(t,arguments),Wn(t)}function Kn(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var Qn={value:()=>{}};function te(){for(var t,n=0,e=arguments.length,r={};n<e;++n){if(!(t=arguments[n]+\"\")||t in r||/[\\s.]/.test(t))throw new Error(\"illegal type: \"+t);r[t]=[]}return new ne(r)}function ne(t){this._=t}function ee(t,n){return t.trim().split(/^|\\s+/).map((function(t){var e=\"\",r=t.indexOf(\".\");if(r>=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error(\"unknown type: \"+t);return{type:t,name:e}}))}function re(t,n){for(var e,r=0,i=t.length;r<i;++r)if((e=t[r]).name===n)return e.value}function ie(t,n,e){for(var r=0,i=t.length;r<i;++r)if(t[r].name===n){t[r]=Qn,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=e&&t.push({name:n,value:e}),t}ne.prototype=te.prototype={constructor:ne,on:function(t,n){var e,r=this._,i=ee(t+\"\",r),o=-1,a=i.length;if(!(arguments.length<2)){if(null!=n&&\"function\"!=typeof n)throw new Error(\"invalid callback: \"+n);for(;++o<a;)if(e=(t=i[o]).type)r[e]=ie(r[e],t.name,n);else if(null==n)for(e in r)r[e]=ie(r[e],t.name,null);return this}for(;++o<a;)if((e=(t=i[o]).type)&&(e=re(r[e],t.name)))return e},copy:function(){var t={},n=this._;for(var e in n)t[e]=n[e].slice();return new ne(t)},call:function(t,n){if((e=arguments.length-2)>0)for(var e,r,i=new Array(e),o=0;o<e;++o)i[o]=arguments[o+2];if(!this._.hasOwnProperty(t))throw new Error(\"unknown type: \"+t);for(o=0,e=(r=this._[t]).length;o<e;++o)r[o].value.apply(n,i)},apply:function(t,n,e){if(!this._.hasOwnProperty(t))throw new Error(\"unknown type: \"+t);for(var r=this._[t],i=0,o=r.length;i<o;++i)r[i].value.apply(n,e)}};const oe=te;var ae,ue,le=0,se=0,ce=0,he=0,fe=0,pe=0,de=\"object\"==typeof performance&&performance.now?performance:Date,ge=\"object\"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function ve(){return fe||(ge(me),fe=de.now()+pe)}function me(){fe=0}function ye(){this._call=this._time=this._next=null}function we(t,n,e){var r=new ye;return r.restart(t,n,e),r}function _e(){fe=(he=de.now())+pe,le=se=0;try{!function(){ve(),++le;for(var t,n=ae;n;)(t=fe-n._time)>=0&&n._call.call(void 0,t),n=n._next;--le}()}finally{le=0,function(){for(var t,n,e=ae,r=1/0;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:ae=n);ue=t,be(r)}(),fe=0}}function xe(){var t=de.now(),n=t-he;n>1e3&&(pe-=n,he=t)}function be(t){le||(se&&(se=clearTimeout(se)),t-fe>24?(t<1/0&&(se=setTimeout(_e,t-de.now()-pe)),ce&&(ce=clearInterval(ce))):(ce||(he=de.now(),ce=setInterval(xe,1e3)),le=1,ge(_e)))}function Me(t,n,e){var r=new ye;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}ye.prototype=we.prototype={constructor:ye,restart:function(t,n,e){if(\"function\"!=typeof t)throw new TypeError(\"callback is not a function\");e=(null==e?ve():+e)+(null==n?0:+n),this._next||ue===this||(ue?ue._next=this:ae=this,ue=this),this._call=t,this._time=e,be()},stop:function(){this._call&&(this._call=null,this._time=1/0,be())}};var Ae=oe(\"start\",\"end\",\"cancel\",\"interrupt\"),Ne=[];function Ee(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(l){var s,c,h,f;if(1!==e.state)return u();for(s in i)if((f=i[s]).name===e.name){if(3===f.state)return Me(o);4===f.state?(f.state=6,f.timer.stop(),f.on.call(\"interrupt\",t,t.__data__,f.index,f.group),delete i[s]):+s<n&&(f.state=6,f.timer.stop(),f.on.call(\"cancel\",t,t.__data__,f.index,f.group),delete i[s])}if(Me((function(){3===e.state&&(e.state=4,e.timer.restart(a,e.delay,e.time),a(l))})),e.state=2,e.on.call(\"start\",t,t.__data__,e.index,e.group),2===e.state){for(e.state=3,r=new Array(h=e.tween.length),s=0,c=-1;s<h;++s)(f=e.tween[s].value.call(t,t.__data__,e.index,e.group))&&(r[++c]=f);r.length=c+1}}function a(n){for(var i=n<e.duration?e.ease.call(null,n/e.duration):(e.timer.restart(u),e.state=5,1),o=-1,a=r.length;++o<a;)r[o].call(t,i);5===e.state&&(e.on.call(\"end\",t,t.__data__,e.index,e.group),u())}function u(){for(var r in e.state=6,e.timer.stop(),delete i[n],i)return;delete t.__transition}i[n]=e,e.timer=we((function(t){e.state=1,e.timer.restart(o,e.delay,e.time),e.delay<=t&&o(t-e.delay)}),0,e.time)}(t,e,{name:n,index:r,group:i,on:Ae,tween:Ne,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:0})}function ke(t,n){var e=Ce(t,n);if(e.state>0)throw new Error(\"too late; already scheduled\");return e}function Se(t,n){var e=Ce(t,n);if(e.state>3)throw new Error(\"too late; already running\");return e}function Ce(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error(\"transition not found\");return e}var Ie,Be=180/Math.PI,Te={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Le(t,n,e,r,i,o){var a,u,l;return(a=Math.sqrt(t*t+n*n))&&(t/=a,n/=a),(l=t*e+n*r)&&(e-=t*l,r-=n*l),(u=Math.sqrt(e*e+r*r))&&(e/=u,r/=u,l/=u),t*r<n*e&&(t=-t,n=-n,l=-l,a=-a),{translateX:i,translateY:o,rotate:Math.atan2(n,t)*Be,skewX:Math.atan(l)*Be,scaleX:a,scaleY:u}}function Oe(t,n,e,r){function i(t){return t.length?t.pop()+\" \":\"\"}return function(o,a){var u=[],l=[];return o=t(o),a=t(a),function(t,r,i,o,a,u){if(t!==i||r!==o){var l=a.push(\"translate(\",null,n,null,e);u.push({i:l-4,x:Ln(t,i)},{i:l-2,x:Ln(r,o)})}else(i||o)&&a.push(\"translate(\"+i+n+o+e)}(o.translateX,o.translateY,a.translateX,a.translateY,u,l),function(t,n,e,o){t!==n?(t-n>180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+\"rotate(\",null,r)-2,x:Ln(t,n)})):n&&e.push(i(e)+\"rotate(\"+n+r)}(o.rotate,a.rotate,u,l),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+\"skewX(\",null,r)-2,x:Ln(t,n)}):n&&e.push(i(e)+\"skewX(\"+n+r)}(o.skewX,a.skewX,u,l),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+\"scale(\",null,\",\",null,\")\");a.push({i:u-4,x:Ln(t,e)},{i:u-2,x:Ln(n,r)})}else 1===e&&1===r||o.push(i(o)+\"scale(\"+e+\",\"+r+\")\")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,l),o=a=null,function(t){for(var n,e=-1,r=l.length;++e<r;)u[(n=l[e]).i]=n.x(t);return u.join(\"\")}}}var Pe=Oe((function(t){const n=new(\"function\"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+\"\");return n.isIdentity?Te:Le(n.a,n.b,n.c,n.d,n.e,n.f)}),\"px, \",\"px)\",\"deg)\"),je=Oe((function(t){return null==t?Te:(Ie||(Ie=document.createElementNS(\"http://www.w3.org/2000/svg\",\"g\")),Ie.setAttribute(\"transform\",t),(t=Ie.transform.baseVal.consolidate())?Le((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):Te)}),\", \",\")\",\")\");function qe(t,n){var e,r;return function(){var i=Se(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a<u;++a)if(r[a].name===n){(r=r.slice()).splice(a,1);break}i.tween=r}}function He(t,n,e){var r,i;if(\"function\"!=typeof e)throw new Error;return function(){var o=Se(this,t),a=o.tween;if(a!==r){i=(r=a).slice();for(var u={name:n,value:e},l=0,s=i.length;l<s;++l)if(i[l].name===n){i[l]=u;break}l===s&&i.push(u)}o.tween=i}}function ze(t,n,e){var r=t._id;return t.each((function(){var t=Se(this,r);(t.value||(t.value={}))[n]=e.apply(this,arguments)})),function(t){return Ce(t,r).value[n]}}function De(t,n){var e;return(\"number\"==typeof n?Ln:n instanceof pn?Cn:(e=pn(n))?(n=e,Cn):qn)(t,n)}function Re(t){return function(){this.removeAttribute(t)}}function Ze(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Ve(t,n,e){var r,i,o=e+\"\";return function(){var a=this.getAttribute(t);return a===o?null:a===r?i:i=n(r=a,e)}}function Xe(t,n,e){var r,i,o=e+\"\";return function(){var a=this.getAttributeNS(t.space,t.local);return a===o?null:a===r?i:i=n(r=a,e)}}function $e(t,n,e){var r,i,o;return function(){var a,u,l=e(this);if(null!=l)return(a=this.getAttribute(t))===(u=l+\"\")?null:a===r&&u===i?o:(i=u,o=n(r=a,l));this.removeAttribute(t)}}function Fe(t,n,e){var r,i,o;return function(){var a,u,l=e(this);if(null!=l)return(a=this.getAttributeNS(t.space,t.local))===(u=l+\"\")?null:a===r&&u===i?o:(i=u,o=n(r=a,l));this.removeAttributeNS(t.space,t.local)}}function Ge(t,n){return function(e){this.setAttribute(t,n.call(this,e))}}function Ye(t,n){return function(e){this.setAttributeNS(t.space,t.local,n.call(this,e))}}function Ue(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&Ye(t,i)),e}return i._value=n,i}function We(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&Ge(t,i)),e}return i._value=n,i}function Je(t,n){return function(){ke(this,t).delay=+n.apply(this,arguments)}}function Ke(t,n){return n=+n,function(){ke(this,t).delay=n}}function Qe(t,n){return function(){Se(this,t).duration=+n.apply(this,arguments)}}function tr(t,n){return n=+n,function(){Se(this,t).duration=n}}function nr(t,n){if(\"function\"!=typeof n)throw new Error;return function(){Se(this,t).ease=n}}function er(t,n,e){var r,i,o=function(t){return(t+\"\").trim().split(/^|\\s+/).every((function(t){var n=t.indexOf(\".\");return n>=0&&(t=t.slice(0,n)),!t||\"start\"===t}))}(n)?ke:Se;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}var rr=gt.prototype.constructor;function ir(t){return function(){this.style.removeProperty(t)}}function or(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function ar(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&or(t,o,e)),r}return o._value=n,o}function ur(t){return function(n){this.textContent=t.call(this,n)}}function lr(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&ur(r)),n}return r._value=t,r}var sr=0;function cr(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function hr(){return++sr}var fr=gt.prototype;cr.prototype=function(t){return gt().transition(t)}.prototype={constructor:cr,select:function(t){var e=this._name,r=this._id;\"function\"!=typeof t&&(t=n(t));for(var i=this._groups,o=i.length,a=new Array(o),u=0;u<o;++u)for(var l,s,c=i[u],h=c.length,f=a[u]=new Array(h),p=0;p<h;++p)(l=c[p])&&(s=t.call(l,l.__data__,p,c))&&(\"__data__\"in l&&(s.__data__=l.__data__),f[p]=s,Ee(f[p],e,r,p,f,Ce(l,r)));return new cr(a,this._parents,e,r)},selectAll:function(t){var n=this._name,e=this._id;\"function\"!=typeof t&&(t=o(t));for(var r=this._groups,i=r.length,a=[],u=[],l=0;l<i;++l)for(var s,c=r[l],h=c.length,f=0;f<h;++f)if(s=c[f]){for(var p,d=t.call(s,s.__data__,f,c),g=Ce(s,e),v=0,m=d.length;v<m;++v)(p=d[v])&&Ee(p,n,e,v,d,g);a.push(d),u.push(s)}return new cr(a,u,n,e)},selectChild:fr.selectChild,selectChildren:fr.selectChildren,filter:function(t){\"function\"!=typeof t&&(t=a(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i<e;++i)for(var o,u=n[i],l=u.length,s=r[i]=[],c=0;c<l;++c)(o=u[c])&&t.call(o,o.__data__,c,u)&&s.push(o);return new cr(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var n=this._groups,e=t._groups,r=n.length,i=e.length,o=Math.min(r,i),a=new Array(r),u=0;u<o;++u)for(var l,s=n[u],c=e[u],h=s.length,f=a[u]=new Array(h),p=0;p<h;++p)(l=s[p]||c[p])&&(f[p]=l);for(;u<r;++u)a[u]=n[u];return new cr(a,this._parents,this._name,this._id)},selection:function(){return new rr(this._groups,this._parents)},transition:function(){for(var t=this._name,n=this._id,e=hr(),r=this._groups,i=r.length,o=0;o<i;++o)for(var a,u=r[o],l=u.length,s=0;s<l;++s)if(a=u[s]){var c=Ce(a,n);Ee(a,t,e,s,u,{time:c.time+c.delay+c.duration,delay:0,duration:c.duration,ease:c.ease})}return new cr(r,this._parents,t,e)},call:fr.call,nodes:fr.nodes,node:fr.node,size:fr.size,empty:fr.empty,each:fr.each,on:function(t,n){var e=this._id;return arguments.length<2?Ce(this.node(),e).on.on(t):this.each(er(e,t,n))},attr:function(t,n){var e=b(t),r=\"transform\"===e?je:De;return this.attrTween(t,\"function\"==typeof n?(e.local?Fe:$e)(e,r,ze(this,\"attr.\"+t,n)):null==n?(e.local?Ze:Re)(e):(e.local?Xe:Ve)(e,r,n))},attrTween:function(t,n){var e=\"attr.\"+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if(\"function\"!=typeof n)throw new Error;var r=b(t);return this.tween(e,(r.local?Ue:We)(r,n))},style:function(t,n,e){var r=\"transform\"==(t+=\"\")?Pe:De;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=L(this,t),a=(this.style.removeProperty(t),L(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on(\"end.style.\"+t,ir(t)):\"function\"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=L(this,t),u=e(this),l=u+\"\";return null==u&&(this.style.removeProperty(t),l=u=L(this,t)),a===l?null:a===r&&l===i?o:(i=l,o=n(r=a,u))}}(t,r,ze(this,\"style.\"+t,n))).each(function(t,n){var e,r,i,o,a=\"style.\"+n,u=\"end.\"+a;return function(){var l=Se(this,t),s=l.on,c=null==l.value[a]?o||(o=ir(n)):void 0;s===e&&i===c||(r=(e=s).copy()).on(u,i=c),l.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+\"\";return function(){var a=L(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on(\"end.style.\"+t,null)},styleTween:function(t,n,e){var r=\"style.\"+(t+=\"\");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if(\"function\"!=typeof n)throw new Error;return this.tween(r,ar(t,n,null==e?\"\":e))},text:function(t){return this.tween(\"text\",\"function\"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?\"\":n}}(ze(this,\"text\",t)):function(t){return function(){this.textContent=t}}(null==t?\"\":t+\"\"))},textTween:function(t){var n=\"text\";if(arguments.length<1)return(n=this.tween(n))&&n._value;if(null==t)return this.tween(n,null);if(\"function\"!=typeof t)throw new Error;return this.tween(n,lr(t))},remove:function(){return this.on(\"end.remove\",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+=\"\",arguments.length<2){for(var r,i=Ce(this.node(),e).tween,o=0,a=i.length;o<a;++o)if((r=i[o]).name===t)return r.value;return null}return this.each((null==n?qe:He)(e,t,n))},delay:function(t){var n=this._id;return arguments.length?this.each((\"function\"==typeof t?Je:Ke)(n,t)):Ce(this.node(),n).delay},duration:function(t){var n=this._id;return arguments.length?this.each((\"function\"==typeof t?Qe:tr)(n,t)):Ce(this.node(),n).duration},ease:function(t){var n=this._id;return arguments.length?this.each(nr(n,t)):Ce(this.node(),n).ease},easeVarying:function(t){if(\"function\"!=typeof t)throw new Error;return this.each(function(t,n){return function(){var e=n.apply(this,arguments);if(\"function\"!=typeof e)throw new Error;Se(this,t).ease=e}}(this._id,t))},end:function(){var t,n,e=this,r=e._id,i=e.size();return new Promise((function(o,a){var u={value:a},l={value:function(){0==--i&&o()}};e.each((function(){var e=Se(this,r),i=e.on;i!==t&&((n=(t=i).copy())._.cancel.push(u),n._.interrupt.push(u),n._.end.push(l)),e.on=n})),0===i&&o()}))},[Symbol.iterator]:fr[Symbol.iterator]};var pr={time:null,delay:0,duration:250,ease:Kn};function dr(t,n){for(var e;!(e=t.__transition)||!(e=e[n]);)if(!(t=t.parentNode))throw new Error(`transition ${n} not found`);return e}function gr(t){let n=0;if(t){const e=t.split(\"`\");e.length>1&&(t=e[e.length-1]),n=function(t){let n=0,e=0,r=1;if(t){for(let i=0;i<t.length&&!(i>6);i++)n+=r*(t.charCodeAt(i)%10),e+=9*r,r*=.7;e>0&&(n/=e)}return n}(t=t.split(\"(\")[0])}return n}function vr(t,n){let e,r,i;return\"red\"===t?(e=200+Math.round(55*n),r=50+Math.round(80*n),i=r):\"orange\"===t?(e=190+Math.round(65*n),r=90+Math.round(65*n),i=0):\"yellow\"===t?(e=175+Math.round(55*n),r=e,i=50+Math.round(20*n)):\"green\"===t?(e=50+Math.round(60*n),r=200+Math.round(55*n),i=e):\"pastelgreen\"===t?(e=163+Math.round(75*n),r=195+Math.round(49*n),i=72+Math.round(149*n)):\"blue\"===t?(e=91+Math.round(126*n),r=156+Math.round(76*n),i=221+Math.round(26*n)):\"aqua\"===t?(e=50+Math.round(60*n),r=165+Math.round(55*n),i=r):\"cold\"===t?(e=0+Math.round(55*(1-n)),r=0+Math.round(230*(1-n)),i=200+Math.round(55*n)):(e=200+Math.round(55*n),r=0+Math.round(230*(1-n)),i=0+Math.round(55*(1-n))),\"rgb(\"+e+\",\"+r+\",\"+i+\")\"}function mr(){let t=960,n=null,e=18,r=null,i=null,o=\"\",a=750,u=Kn,l=!1,s=!1,c=null,h=null,f=0,p=null,d=null,g=!1,v=!1,m=!1,y=null,w=!1,_=null,x=function(t){return t.data.n||t.data.name},b=function(t){return\"v\"in t?t.v:t.value},M=function(t){return t.c||t.children},A=function(t){return t.data.l||t.data.libtype},N=function(t){return\"d\"in t.data?t.data.d:t.data.delta},E=function(t,n,e){d=()=>{p&&(p.textContent=\"search: \"+n+\" of \"+e+\" total samples ( \"+kt(\".3f\")(n/e*100,3)+\"%)\")},d()};const k=E;let S=(t,n,e=!1)=>{if(!n)return!1;let r=x(t);e&&(n=n.toLowerCase(),r=r.toLowerCase());const i=new RegExp(n);return void 0!==r&&r&&r.match(i)};const C=S;let I=function(t){p&&(t?p.textContent=t:\"function\"==typeof d?d():p.textContent=\"\")};const B=I;let T=function(t){return x(t)+\" (\"+kt(\".3f\")(100*(t.x1-t.x0),3)+\"%, \"+b(t)+\" samples)\"},L=function(t){return t.highlight?\"#E600E6\":function(t,n){let e=_||\"warm\";_||void 0===n||\"\"===n||(e=\"red\",void 0!==t&&t&&t.match(/::/)&&(e=\"yellow\"),\"kernel\"===n?e=\"orange\":\"jit\"===n?e=\"green\":\"inlined\"===n&&(e=\"aqua\"));return vr(e,gr(t))}(x(t),A(t))};const O=L;function P(t){t.data.fade=!1,t.data.hide=!1,t.children&&t.children.forEach(P)}function j(t){t.parent&&(t.parent.data.fade=!0,j(t.parent))}function q(t){if(i&&i.hide(),function(t){let n,e,r,i=t,o=i.parent;for(;o;){for(n=o.children,e=n.length;e--;)r=n[e],r!==i&&(r.data.hide=!0);i=o,o=i.parent}}(t),P(t),j(t),Z(),m){const n=vt(this).select(\"svg\")._groups[0][0].parentNode.offsetTop,r=(window.innerHeight-n)/e,i=(t.height-r+10)*e;window.scrollTo({top:n+i,left:0,behavior:\"smooth\"})}\"function\"==typeof c&&c(t)}function H(t,n){if(t.id===n)return t;{const e=M(t);if(e)for(let t=0;t<e.length;t++){const r=H(e[t],n);if(r)return r}}}function z(t){t.highlight=!1,M(t)&&M(t).forEach((function(t){z(t)}))}function D(t,n){return\"function\"==typeof l?l(t,n):l?Bt(x(t),x(n)):void 0}const R=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&function(t,n,e,r,i){for(var o,a=t.children,u=-1,l=a.length,s=t.value&&(r-n)/t.value;++u<l;)(o=a[u]).y0=e,o.y1=i,o.x0=n,o.x1=n+=o.value*s}(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a<i&&(i=a=(i+a)/2),u<o&&(o=u=(o+u)/2),r.x0=i,r.y0=o,r.x1=a,r.y1=u}}(n,o)),r&&i.eachBefore(Tt),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(e){return arguments.length?(t=+e[0],n=+e[1],i):[t,n]},i.padding=function(t){return arguments.length?(e=+t,i):e},i}();function Z(){r.each((function(r){const o=Jn().range([0,t]),c=Jn().range([0,e]);X(r),l&&r.sort(D),R(r);const p=t/(r.x1-r.x0);function d(t){return(t.x1-t.x0)*p}const g=function(n){let e=n.descendants();if(f>0){const r=t/(n.x1-n.x0);e=e.filter((function(t){return(t.x1-t.x0)*r>f}))}return e}(r),m=vt(this).select(\"svg\");m.attr(\"width\",t);let w=m.selectAll(\"g\").data(g,(function(t){return t.id}));if(!n||v){const t=Math.max.apply(null,g.map((function(t){return t.depth})));n=(t+3)*e,n<y&&(n=y),m.attr(\"height\",n)}w.transition().duration(a).ease(u).attr(\"transform\",(function(t){return\"translate(\"+o(t.x0)+\",\"+(s?c(t.depth):n-c(t.depth)-e)+\")\"})),w.select(\"rect\").transition().duration(a).ease(u).attr(\"width\",d);const _=w.enter().append(\"svg:g\").attr(\"transform\",(function(t){return\"translate(\"+o(t.x0)+\",\"+(s?c(t.depth):n-c(t.depth)-e)+\")\"}));_.append(\"svg:rect\").transition().delay(a/2).attr(\"width\",d),i||_.append(\"svg:title\"),_.append(\"foreignObject\").append(\"xhtml:div\"),w=m.selectAll(\"g\").data(g,(function(t){return t.id})),w.attr(\"width\",d).attr(\"height\",(function(t){return e})).attr(\"name\",(function(t){return x(t)})).attr(\"class\",(function(t){return t.data.fade?\"frame fade\":\"frame\"})),w.select(\"rect\").attr(\"height\",(function(t){return e})).attr(\"fill\",(function(t){return L(t)})),i||w.select(\"title\").text(T),w.select(\"foreignObject\").attr(\"width\",d).attr(\"height\",(function(t){return e})).select(\"div\").attr(\"class\",\"d3-flame-graph-label\").style(\"display\",(function(t){return d(t)<35?\"none\":\"block\"})).transition().delay(a).text(x),w.on(\"click\",((t,n)=>{q(n)})),w.exit().remove(),w.on(\"mouseover\",(function(t,n){i&&i.show(n,this),I(T(n)),\"function\"==typeof h&&h(n)})).on(\"mouseout\",(function(){i&&i.hide(),I(null)}))}))}function V(t,n){n.forEach((function(n){const e=t.find((function(t){return t.name===n.name}));e?(e.value+=n.value,n.children&&(e.children||(e.children=[]),V(e.children,n.children))):t.push(n)}))}function X(t){let n,e,r,i,o,a,u,l;const s=[],c=[],h=[],f=!g;let p=t.data;for(p.hide?(t.value=0,e=t.children,e&&h.push(e)):(t.value=p.fade?0:b(p),s.push(t));n=s.pop();)if(e=n.children,e&&(o=e.length)){for(i=0;o--;)u=e[o],p=u.data,p.hide?(u.value=0,r=u.children,r&&h.push(r)):(p.fade?u.value=0:(l=b(p),u.value=l,i+=l),s.push(u));f&&n.value&&(n.value-=i),c.push(e)}for(o=c.length;o--;){for(e=c[o],i=0,a=e.length;a--;)i+=e[a].value;e[0].parent.value+=i}for(;h.length;)for(e=h.pop(),a=e.length;a--;)u=e[a],u.value=0,r=u.children,r&&h.push(r)}function $(){r.datum((t=>{if(\"Node\"!==t.constructor.name){const n=Ot(t,M);return function(t){let n=0;!function(t,n){n(t);let e=t.children;if(e){const t=[e];let r,i,o;for(;t.length;)for(e=t.pop(),r=e.length;r--;)i=e[r],n(i),o=i.children,o&&t.push(o)}}(t,(function(t){t.id=n++}))}(n),X(n),n.originalValue=n.value,w&&n.eachAfter((t=>{let n=N(t);const e=t.children;let r=e&&e.length;for(;--r>=0;)n+=e[r].delta;t.delta=n})),n}}))}function F(e){if(!arguments.length)return F;r=e,$(),r.each((function(e){if(0===vt(this).select(\"svg\").size()){const e=vt(this).append(\"svg:svg\").attr(\"width\",t).attr(\"class\",\"partition d3-flame-graph\");n&&(n<y&&(n=y),e.attr(\"height\",n)),e.append(\"svg:text\").attr(\"class\",\"title\").attr(\"text-anchor\",\"middle\").attr(\"y\",\"25\").attr(\"x\",t/2).attr(\"fill\",\"#808080\").text(o),i&&e.call(i)}})),Z()}return F.height=function(t){return arguments.length?(n=t,F):n},F.minHeight=function(t){return arguments.length?(y=t,F):y},F.width=function(n){return arguments.length?(t=n,F):t},F.cellHeight=function(t){return arguments.length?(e=t,F):e},F.tooltip=function(t){return arguments.length?(\"function\"==typeof t&&(i=t),F):i},F.title=function(t){return arguments.length?(o=t,F):o},F.transitionDuration=function(t){return arguments.length?(a=t,F):a},F.transitionEase=function(t){return arguments.length?(u=t,F):u},F.sort=function(t){return arguments.length?(l=t,F):l},F.inverted=function(t){return arguments.length?(s=t,F):s},F.computeDelta=function(t){return arguments.length?(w=t,F):w},F.setLabelHandler=function(t){return arguments.length?(T=t,F):T},F.label=F.setLabelHandler,F.search=function(t){const n=[];let e=0,i=0;r.each((function(r){const o=function(t,n){const e=[];let r=0;return function t(i,o){let a=!1;S(i,n)?(i.highlight=!0,a=!0,o||(r+=b(i)),e.push(i)):i.highlight=!1,M(i)&&M(i).forEach((function(n){t(n,o||a)}))}(t,!1),[e,r]}(r,t);n.push(...o[0]),e+=o[1],i+=r.originalValue})),E(n,e,i),Z()},F.findById=function(t){if(null==t)return null;let n=null;return r.each((function(e){null===n&&(n=H(e,t))})),n},F.clear=function(){I(null),r.each((function(t){z(t),Z()}))},F.zoomTo=function(t){q(t)},F.resetZoom=function(){r.each((function(t){q(t)}))},F.onClick=function(t){return arguments.length?(c=t,F):c},F.onHover=function(t){return arguments.length?(h=t,F):h},F.merge=function(t){return r?(this.resetZoom(),d=null,I(null),r.datum((n=>(V([n.data],[t]),n.data))),$(),Z(),F):F},F.update=function(t){return r?(t&&(r.datum(t),$()),Z(),F):F},F.destroy=function(){return r?(i&&(i.hide(),\"function\"==typeof i.destroy&&i.destroy()),r.selectAll(\"svg\").remove(),F):F},F.setColorMapper=function(t){return arguments.length?(L=n=>{const e=O(n);return t(n,e)},F):(L=O,F)},F.color=F.setColorMapper,F.setColorHue=function(t){return arguments.length?(_=t,F):(_=null,F)},F.minFrameSize=function(t){return arguments.length?(f=t,F):f},F.setDetailsElement=function(t){return arguments.length?(p=t,F):p},F.details=F.setDetailsElement,F.selfValue=function(t){return arguments.length?(g=t,F):g},F.resetHeightOnZoom=function(t){return arguments.length?(v=t,F):v},F.scrollOnZoom=function(t){return arguments.length?(m=t,F):m},F.getName=function(t){return arguments.length?(x=t,F):x},F.getValue=function(t){return arguments.length?(b=t,F):b},F.getChildren=function(t){return arguments.length?(M=t,F):M},F.getLibtype=function(t){return arguments.length?(A=t,F):A},F.getDelta=function(t){return arguments.length?(N=t,F):N},F.setSearchHandler=function(t){return arguments.length?(E=t,F):(E=k,F)},F.setDetailsHandler=function(t){return arguments.length?(I=t,F):(I=B,F)},F.setSearchMatch=function(t){return arguments.length?(S=t,F):(S=C,F)},F}gt.prototype.interrupt=function(t){return this.each((function(){!function(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+\"\",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?\"interrupt\":\"cancel\",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}(this,t)}))},gt.prototype.transition=function(t){var n,e;t instanceof cr?(n=t._id,t=t._name):(n=hr(),(e=pr).time=ve(),t=null==t?null:t+\"\");for(var r=this._groups,i=r.length,o=0;o<i;++o)for(var a,u=r[o],l=u.length,s=0;s<l;++s)(a=u[s])&&Ee(a,t,n,s,u,e||dr(a,n));return new cr(r,this._parents,t,n)};var yr=e(487),wr=e.n(yr),_r=e(631),xr=e.n(_r),br=e(52),Mr=e.n(br),Ar=e(10),Nr=e.n(Ar),Er=e(469),kr=e.n(Er),Sr=e(329),Cr=e.n(Sr),Ir=e(591),Br={};Br.styleTagTransform=Cr(),Br.setAttributes=Nr(),Br.insert=Mr().bind(null,\"head\"),Br.domAPI=xr(),Br.insertStyleElement=kr(),wr()(Ir.Z,Br),Ir.Z&&Ir.Z.locals&&Ir.Z.locals;var Tr=e(891),Lr={};Lr.styleTagTransform=Cr(),Lr.setAttributes=Nr(),Lr.insert=Mr().bind(null,\"head\"),Lr.domAPI=xr(),Lr.insertStyleElement=kr(),wr()(Tr.Z,Lr),Tr.Z&&Tr.Z.locals&&Tr.Z.locals;class Or{constructor(t,n){this.stacks=t||{name:\"Stacks were not loaded.\",value:0,children:[]},this.options=n||{},this.options.context&&\"\"!==this.options.context||(this.options.context=\"No context information available.\"),this.context=document.getElementById(\"context\"),this.chart=document.getElementById(\"chart\"),this.details=document.getElementById(\"details\"),this.contextBtn=document.getElementById(\"context-button\"),this.resetBtn=document.getElementById(\"reset-button\"),this.clearBtn=document.getElementById(\"clear-button\"),this.searchForm=document.getElementById(\"search-form\"),this.searchInput=document.getElementById(\"search-input\")}handleSearch(){\"\"===this.searchInput.value?this.flameGraph.clear():this.flameGraph.search(this.searchInput.value)}toggleContextInfo(){\"none\"===this.context.style.display?(this.chart.style.display=\"none\",this.context.style.display=\"block\"):(this.context.style.display=\"none\",this.chart.style.display=\"block\")}handleResetZoom(){this.flameGraph.resetZoom()}handleClear(){this.searchInput.value=\"\",this.flameGraph.clear()}handleWindowResize(){var t=this.chart.clientWidth,n=document.getElementsByClassName(\"d3-flame-graph\");n.length>0&&n[0].setAttribute(\"width\",t),this.flameGraph.width(t),this.flameGraph.resetZoom()}sortByValue(t,n){return t.value===n.value?0:t.value<n.value?1:-1}colorSchemeColorMapper(t,n,e,r){if(e.highlight)return\"#E600E6\";if(\"blue-green\"===this.options.colorscheme){const r=t(e),i=n(e),o=gr(r);return\"root\"===i?\"rgb(217,75,75)\":vr(\"kernel\"===i?\"blue\":\"pastelgreen\",o)}return r}init(){this.flameGraph=mr().width(this.chart.clientWidth).cellHeight(16).minFrameSize(5).sort(this.sortByValue).selfValue(!0).resetHeightOnZoom(!0).scrollOnZoom(!0).setDetailsElement(this.details);const t=this.flameGraph.getName(),n=this.flameGraph.getLibtype();this.flameGraph.setColorMapper(this.colorSchemeColorMapper.bind(this,t,n)),this.contextBtn.addEventListener(\"click\",this.toggleContextInfo.bind(this)),this.resetBtn.addEventListener(\"click\",this.handleResetZoom.bind(this)),this.clearBtn.addEventListener(\"click\",this.handleClear.bind(this)),this.searchForm.addEventListener(\"submit\",(t=>{t.preventDefault(),this.handleSearch()})),this.searchInput.addEventListener(\"blur\",this.handleSearch.bind(this)),window.addEventListener(\"resize\",this.handleWindowResize.bind(this),!0),this.context.textContent=this.options.context,this.details.textContent=\"\",vt(this.chart).datum(this.stacks).call(this.flameGraph)}}window.flamegraph=(t,n)=>{new Or(t,n).init()}})()})();</script></head>\n  <body>\n    <div id=\"controls\">\n      <button id=\"context-button\" type=\"button\">Context</button>\n      <button id=\"reset-button\" type=\"button\">Reset Zoom</button>\n      <button id=\"clear-button\" type=\"button\">Clear</button>\n      <form id=\"search-form\">\n        <input id=\"search-input\" type=\"text\" name=\"search\">\n        <input type=\"submit\" value=\"Search\">\n      </form>\n    </div>\n    <div id=\"content\">\n        <pre id=\"context\" style=\"display: none\"></pre>\n        <div id=\"chart\"></div>\n        <div id=\"details\">Loading Flame Graph...</div>\n    </div>\n\n    <script>\n      const stacks = [/** @flamegraph_json **/];\n      const options = [/** @options_json **/];\n\n      flamegraph(stacks[0], options[0]);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/Blackholio/.github/workflows/repo-migration-notice.yml",
    "content": "name: Repo migration notice\n\non:\n  issues:\n    types: [opened]\n  pull_request:\n    types: [opened]\n\njobs:\n  comment:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Add comment\n        uses: actions/github-script@v7\n        env:\n          migrated_repo: https://github.com/ClockworkLabs/SpacetimeDB\n          migrated_path: demo/Blackholio\n          default_branch: master\n        with:\n          script: |\n            const isPR = context.eventName === 'pull_request';\n            const number = isPR ? context.payload.pull_request.number : context.payload.issue.number;\n\n            let message;\n            if (isPR) {\n              message = `\n              Thank you for submitting this!\n\n              We are in the process of migrating this repository (see [DEVELOP.md](../blob/${process.env.default_branch}/DEVELOP.md)).\n\n              To make sure we see your PR, please open it in the [SpacetimeDB](${process.env.migrated_repo}) repo, under [${process.env.migrated_path}](${process.env.migrated_repo}/tree/master/${process.env.migrated_path}).\n\n              Apologies for the inconvenience, and thank you again!\n              `;\n            } else {\n              message = `\n              Thank you for submitting this!\n\n              We are in the process of migrating this repository (see [DEVELOP.md](../blob/${process.env.default_branch}/DEVELOP.md)).\n\n              To make sure we actually see your issue, please open it here: [SpacetimeDB/issues/new](${process.env.migrated_repo}/issues/new).\n\n              Apologies for the inconvenience, and thank you again!\n              `;\n            }\n\n            await github.rest.issues.createComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: number,\n              body: message\n            });\n\n            await github.rest.issues.update({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: number,\n              state: 'closed'\n            });\n"
  },
  {
    "path": "demo/Blackholio/.gitignore",
    "content": ".idea/\n.vsconfig\n.vs/\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### macOS Patch ###\n# iCloud generated files\n*.icloud\n\n# End of https://www.toptal.com/developers/gitignore/api/macos\n\n# Unreal plugin build artifacts\nBinaries/\nIntermediate/\nDerivedDataCache/\nSaved/\n.vs/\n.idea/\n*.VC.db\n*.VC.opendb\n*.sln\n*.opensdf\n*.sdf\n*.suo\n*.user\n*.userprefs\n*.log\n*.tlog\n*.ipch\n*.obj\n*.pch\n*.pdb\n*.idb\n*.aps\n*.ncb\n*.cache\n*.sbr\n*.db\n*.xcodeproj\nxcuserdata/\n/sdks/unreal/tests/TestClient/TestResults"
  },
  {
    "path": "demo/Blackholio/DEVELOP.md",
    "content": "Please make changes and submit pull requests to the root repository at [SpacetimeDB/demo/Blackholio](https://github.com/clockworklabs/SpacetimeDB/tree/master/demo/Blackholio). The [Blackholio repository](https://github.com/clockworklabs/Blackholio) is only a mirror.\n"
  },
  {
    "path": "demo/Blackholio/README.md",
    "content": "# **Blackholio**\n\n**Blackholio** is a small-scoped MMORPG built using Unity and [SpacetimeDB](https://spacetimedb.com), designed to showcase scalable multiplayer game development. Inspired by [agar.io](https://agar.io), **Blackholio** reimagines the mechanics with a space theme where players become planets, stars, and black holes in a competitive cosmic arena.\n\n### **Game Overview**\n- **Gameplay:** Absorb smaller entities, grow, evolve, and dominate the leaderboard as a black hole.\n- **Scale:** Supports hundreds of players seamlessly with SpacetimeDB's real-time synchronization.\n- **Objective:** Demonstrate SpacetimeDB's features and best practices in a fun, interactive project.\n\n---\n\n## **Tutorial Overview**\n\nThis repository accompanies the **Blackholio Unity Tutorial**, which guides you through building the game from scratch while learning:\n- **Unity integration** with SpacetimeDB.\n- Client-server setup for multiplayer games.\n- SpacetimeDB features, including reducers, tables, and scheduled events.\n\nBy following the tutorial, you'll develop:\n1. A basic understanding of SpacetimeDB for multiplayer games.\n2. A functional game prototype with scalable multiplayer features.\n\nNeed help with the tutorial? [Join our Discord server](https://discord.gg/spacetimedb)!\n\n---\n\n### **Getting Started**\nIf you want to follow a more structured tutorial where it shows you how to set everything up, start with our [Blackholio Tutorial](https://spacetimedb.com/docs/unity/part-1).\n\nIf you just want to checkout the final project and play around a bit, follow these steps:\n\n1. [Install SpacetimeDB CLI](https://spacetimedb.com/install)\n2. Start SpacetimeDB in a separate terminal with `spacetime start`.\n3. Clone this repository:\n   ```bash\n   git clone https://github.com/ClockworkLabs/SpacetimeDB.git\n   cd SpacetimeDB/demo/Blackholio\n   ```\n4. Publish the server logic. You can either publish the csharp server or the rust server.\n   ```bash\n   cd server-rust/\n   # Upload the module to SpacetimeDB\n   bash ./publish.sh\n   # Update the client bindings in case they are out of date\n   bash ./generate.sh\n   ```\n5. Install [Unity Hub](https://unity.com/download) and Unity version **2021.2** or newer.\n6. Open the scene named `Main.unity`\n7. Hit play!\n\nYou should be prompted for a username and you should be able to move around, eat food and defeat other players!\n\n---\n\n### **Features**\n- **Core Gameplay**: Consume, grow, and dominate.\n- **Multiplayer**: Scales to hundreds of players with SpacetimeDB.\n- **Dynamic Gameplay**: Spawning, movement, collisions, and evolution mechanics.\n- **Learn by Building**: Explore the mechanics of Unity and SpacetimeDB through the tutorial.\n\n---\n\n### **Repository Structure**\n\n```plaintext\nBlackholio/\n├── client-unity/      # Unity client project\n├── client-unreal/     # Unreal Engine client project\n├── server-csharp/     # SpacetimeDB server module (C# implementation)\n├── server-rust/       # SpacetimeDB server module (Rust implementation)\n├── DEVELOP.md         # Development guidelines\n└── README.md          # This file\n```\n\n---\n\n### **Requirements**\n- **Unity**: Version `2021.2` or newer.\n- **Rust**: Version `1.65.0` or later (for the SpacetimeDB server module).\n- **SpacetimeDB CLI**: Installed via [SpacetimeDB installation guide](https://spacetimedb.com/docs/install).\n\n---\n\n### **Resources**\n- [SpacetimeDB Documentation](https://spacetimedb.com/docs/)\n- [Join our Discord Community](https://discord.gg/spacetimedb)\n- [Agar.io (inspiration)](https://agar.io)\n\n---\n\n### **License**\nThis project is licensed under the [Apache License](LICENSE).\n\nFeel free to fork, modify, and use **Blackholio** as a starting point for your own projects. Contributions are welcome!\n\n---\n\n### **Feedback**\nWe'd love to hear your thoughts on the tutorial or game! Open an issue in the repository or chat with us on [Discord](https://discord.gg/spacetimedb).\n\nHappy developing, and enjoy creating the cosmos with **Blackholio**! 🚀\n"
  },
  {
    "path": "demo/Blackholio/client-unity/.gitattributes",
    "content": "/Assets/Scripts/autogen/** linguist-generated=true\n"
  },
  {
    "path": "demo/Blackholio/client-unity/.gitignore",
    "content": "# This .gitignore file should be placed at the root of your Unity project directory\n#\n# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore\n#\n/[Ll]ibrary/\n/[Tt]emp/\n/[Oo]bj/\n/[Bb]uild/\n/[Bb]uilds/\n/[Ll]ogs/\n/[Uu]ser[Ss]ettings/\n\n# MemoryCaptures can get excessive in size.\n# They also could contain extremely sensitive data\n/[Mm]emoryCaptures/\n\n# Recordings can get excessive in size\n/[Rr]ecordings/\n\n# Uncomment this line if you wish to ignore the asset store tools plugin\n# /[Aa]ssets/AssetStoreTools*\n\n# Autogenerated Jetbrains Rider plugin\n/[Aa]ssets/Plugins/Editor/JetBrains*\n\n# Visual Studio cache directory\n.vs/\n\n# Gradle cache directory\n.gradle/\n\n# Autogenerated VS/MD/Consulo solution and project files\nExportedObj/\n.consulo/\n*.csproj\n*.unityproj\n*.sln\n*.suo\n*.tmp\n*.user\n*.userprefs\n*.pidb\n*.booproj\n*.svd\n*.pdb\n*.mdb\n*.opendb\n*.VC.db\n\n# Unity3D generated meta files\n*.pidb.meta\n*.pdb.meta\n*.mdb.meta\n\n# Unity3D generated file on crash reports\nsysinfo.txt\n\n# Builds\n*.apk\n*.aab\n*.unitypackage\n*.app\n\n# Crashlytics generated file\ncrashlytics-build.properties\n\n# Packed Addressables\n/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*\n\n# Temporary auto-generated Android Assets\n/[Aa]ssets/[Ss]treamingAssets/aa.meta\n/[Aa]ssets/[Ss]treamingAssets/aa/*\n\nBuild/\n.vscode/\n\nobj~\nbin~\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/ArenaBorderMaterial.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 8\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_Name: ArenaBorderMaterial\n  m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}\n  m_Parent: {fileID: 0}\n  m_ModifiedSerializedProperties: 0\n  m_ValidKeywords: []\n  m_InvalidKeywords: []\n  m_LightmapFlags: 4\n  m_EnableInstancingVariants: 0\n  m_DoubleSidedGI: 0\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  disabledShaderPasses: []\n  m_LockedProperties: \n  m_SavedProperties:\n    serializedVersion: 3\n    m_TexEnvs:\n    - _AlphaTex:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _MainTex:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _MaskTex:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - _NormalMap:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Ints: []\n    m_Floats:\n    - _EnableExternalAlpha: 0\n    m_Colors:\n    - _Color: {r: 0.9019608, g: 0.9137255, b: 0.9411765, a: 1}\n    - _Flip: {r: 1, g: 1, b: 1, a: 1}\n    - _RendererColor: {r: 1, g: 1, b: 1, a: 1}\n  m_BuildTextureStacks: []\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/ArenaBorderMaterial.mat.meta",
    "content": "fileFormatVersion: 2\nguid: eb000c9de35d73e4d81c6a0831a9e7bc\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 2100000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CircleMaterial.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &-1045690558601089486\nMonoBehaviour:\n  m_ObjectHideFlags: 11\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  version: 7\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 8\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_Name: CircleMaterial\n  m_Shader: {fileID: -6465566751694194690, guid: a0cc4614e193c4943a535015f24efd2f, type: 3}\n  m_Parent: {fileID: 0}\n  m_ModifiedSerializedProperties: 0\n  m_ValidKeywords: []\n  m_InvalidKeywords: []\n  m_LightmapFlags: 4\n  m_EnableInstancingVariants: 0\n  m_DoubleSidedGI: 0\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  disabledShaderPasses: []\n  m_LockedProperties: \n  m_SavedProperties:\n    serializedVersion: 3\n    m_TexEnvs:\n    - unity_Lightmaps:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - unity_LightmapsInd:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - unity_ShadowMasks:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Ints: []\n    m_Floats:\n    - _BorderDeval: 0.2\n    - _BorderSize: 0.8\n    - _Radius: 0.5\n    - _Wavyness: 0.005\n    - _WavynessSpeed: 0.4\n    m_Colors:\n    - _CircleColor: {r: 0.5943396, g: 0.17101276, b: 0.17101276, a: 0}\n    - _MainTex: {r: 0.5943396, g: 0.17101276, b: 0.17101276, a: 0}\n  m_BuildTextureStacks: []\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CircleMaterial.mat.meta",
    "content": "fileFormatVersion: 2\nguid: 935bcdd8ae1fdd84ba42dd7ca677d38b\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 2100000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CirclePrefab.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &2396975056599578149\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 5956175893929514430}\n  - component: {fileID: 2753275671783793823}\n  - component: {fileID: 5181438745055576688}\n  m_Layer: 0\n  m_Name: CirclePrefab\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &5956175893929514430\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 5.57, y: 4.71, z: 0}\n  m_LocalScale: {x: 10, y: 10, z: 10}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 4360853892449568783}\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!212 &2753275671783793823\nSpriteRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  m_Enabled: 1\n  m_CastShadows: 0\n  m_ReceiveShadows: 0\n  m_DynamicOccludee: 1\n  m_StaticShadowCaster: 0\n  m_MotionVectors: 1\n  m_LightProbeUsage: 1\n  m_ReflectionProbeUsage: 1\n  m_RayTracingMode: 0\n  m_RayTraceProcedural: 0\n  m_RenderingLayerMask: 1\n  m_RendererPriority: 0\n  m_Materials:\n  - {fileID: 2100000, guid: 935bcdd8ae1fdd84ba42dd7ca677d38b, type: 2}\n  m_StaticBatchInfo:\n    firstSubMesh: 0\n    subMeshCount: 0\n  m_StaticBatchRoot: {fileID: 0}\n  m_ProbeAnchor: {fileID: 0}\n  m_LightProbeVolumeOverride: {fileID: 0}\n  m_ScaleInLightmap: 1\n  m_ReceiveGI: 1\n  m_PreserveUVs: 0\n  m_IgnoreNormalsForChartDetection: 0\n  m_ImportantGI: 0\n  m_StitchLightmapSeams: 1\n  m_SelectedEditorRenderState: 0\n  m_MinimumChartSize: 4\n  m_AutoUVMaxDistance: 0.5\n  m_AutoUVMaxAngle: 89\n  m_LightmapParameters: {fileID: 0}\n  m_SortingLayerID: 2004554327\n  m_SortingLayer: 1\n  m_SortingOrder: 0\n  m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_FlipX: 0\n  m_FlipY: 0\n  m_DrawMode: 0\n  m_Size: {x: 1, y: 1}\n  m_AdaptiveModeThreshold: 0.5\n  m_SpriteTileMode: 0\n  m_WasSpriteAssigned: 1\n  m_MaskInteraction: 0\n  m_SpriteSortPoint: 0\n--- !u!114 &5181438745055576688\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: bd6b8a91c00f49b4a9a894af85e2eee3, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  EntityId: 0\n--- !u!1 &4816698811859672967\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 7219696532246631320}\n  - component: {fileID: 1596019722263272223}\n  - component: {fileID: 5819006763668977793}\n  m_Layer: 5\n  m_Name: Name\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &7219696532246631320\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4816698811859672967}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 4360853892449568783}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0.5, y: 0.5}\n  m_AnchorMax: {x: 0.5, y: 0.5}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 200, y: 50}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!222 &1596019722263272223\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4816698811859672967}\n  m_CullTransparentMesh: 1\n--- !u!114 &5819006763668977793\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4816698811859672967}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: New Text\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4294768372\n  m_fontColor: {r: 0.95686275, g: 0.9647059, b: 0.9882353, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 2\n  m_fontSizeBase: 2\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 1\n  m_HorizontalAlignment: 2\n  m_VerticalAlignment: 512\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!1 &6465251822052786682\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 4360853892449568783}\n  - component: {fileID: 4070460602809798718}\n  - component: {fileID: 306797449388687440}\n  - component: {fileID: 5553468776687040849}\n  m_Layer: 5\n  m_Name: Canvas\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &4360853892449568783\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 6465251822052786682}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 0.1, y: 0.1, z: 0.1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 7219696532246631320}\n  m_Father: {fileID: 5956175893929514430}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 100, y: 100}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!223 &4070460602809798718\nCanvas:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 6465251822052786682}\n  m_Enabled: 1\n  serializedVersion: 3\n  m_RenderMode: 2\n  m_Camera: {fileID: 0}\n  m_PlaneDistance: 100\n  m_PixelPerfect: 0\n  m_ReceivesEvents: 1\n  m_OverrideSorting: 0\n  m_OverridePixelPerfect: 0\n  m_SortingBucketNormalizedSize: 0\n  m_VertexColorAlwaysGammaSpace: 0\n  m_AdditionalShaderChannelsFlag: 25\n  m_UpdateRectTransformForStandalone: 0\n  m_SortingLayerID: 2004554327\n  m_SortingOrder: 1\n  m_TargetDisplay: 0\n--- !u!114 &306797449388687440\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 6465251822052786682}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_UiScaleMode: 0\n  m_ReferencePixelsPerUnit: 100\n  m_ScaleFactor: 1\n  m_ReferenceResolution: {x: 800, y: 600}\n  m_ScreenMatchMode: 0\n  m_MatchWidthOrHeight: 0\n  m_PhysicalUnit: 3\n  m_FallbackScreenDPI: 96\n  m_DefaultSpriteDPI: 96\n  m_DynamicPixelsPerUnit: 1\n  m_PresetInfoIsWorld: 1\n--- !u!114 &5553468776687040849\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 6465251822052786682}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreReversedGraphics: 1\n  m_BlockingObjects: 0\n  m_BlockingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CirclePrefab.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: 3a285dad10ff54d4c9e97322286acb7a\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CircleShader.shadergraph",
    "content": "{\n    \"m_SGVersion\": 3,\n    \"m_Type\": \"UnityEditor.ShaderGraph.GraphData\",\n    \"m_ObjectId\": \"fc48c4e4e8974385b308e763e006554c\",\n    \"m_Properties\": [\n        {\n            \"m_Id\": \"999906e1890243c7ae7e3965b5ee4166\"\n        },\n        {\n            \"m_Id\": \"604350062f16489eb5a80307ef0bd65c\"\n        },\n        {\n            \"m_Id\": \"accc1775f8284df397e90f605b241244\"\n        },\n        {\n            \"m_Id\": \"fc703087ffc240b5bf6d4ba585862c1b\"\n        },\n        {\n            \"m_Id\": \"f00581f6ce32427abe0c266f899c1635\"\n        },\n        {\n            \"m_Id\": \"d4221d82bf004cafa97f92d1dc9c8947\"\n        },\n        {\n            \"m_Id\": \"9ba2373f594348f590d33fb011ce0e4d\"\n        }\n    ],\n    \"m_Keywords\": [],\n    \"m_Dropdowns\": [],\n    \"m_CategoryData\": [\n        {\n            \"m_Id\": \"bb030873d8f04a51867e20063ae091fa\"\n        }\n    ],\n    \"m_Nodes\": [\n        {\n            \"m_Id\": \"7e16b64755f2495da852d9a531216784\"\n        },\n        {\n            \"m_Id\": \"607e703f097e4ba6a34cc10f1c5e749b\"\n        },\n        {\n            \"m_Id\": \"65d2a83223ef4c6fb0eca72a3565523d\"\n        },\n        {\n            \"m_Id\": \"06e3979d054040678f0c2d6132c4d9a4\"\n        },\n        {\n            \"m_Id\": \"b5d5cb5f536c406b907a346609933d42\"\n        },\n        {\n            \"m_Id\": \"8aa9920efe324572844a9454bb0d3155\"\n        },\n        {\n            \"m_Id\": \"4360cdb25a12451aa48604ae01c960b9\"\n        },\n        {\n            \"m_Id\": \"88b7867498a64a8aae11c361213aa821\"\n        },\n        {\n            \"m_Id\": \"eb3b458649b94a7ca9081f57922d8e3d\"\n        },\n        {\n            \"m_Id\": \"d6a60b13ea87425db70b8a9ed890f99e\"\n        },\n        {\n            \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n        },\n        {\n            \"m_Id\": \"6334a94177ff4bf9add8b7481144548b\"\n        },\n        {\n            \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n        },\n        {\n            \"m_Id\": \"ba07071fc9f74c59b7ae54084bc4559b\"\n        },\n        {\n            \"m_Id\": \"1fab4e374a214d1f8bd591fa8bb86d79\"\n        },\n        {\n            \"m_Id\": \"5e1d73d413b44e8589d578e972bab786\"\n        },\n        {\n            \"m_Id\": \"84709a64d8a24fb290f63c0a010008fc\"\n        },\n        {\n            \"m_Id\": \"2e1bf783126c47778acb69e3374335ef\"\n        },\n        {\n            \"m_Id\": \"e6e2276749544ff689793067b7b5e4da\"\n        },\n        {\n            \"m_Id\": \"92eaf344b2ee40e6a36443ae9934ac38\"\n        },\n        {\n            \"m_Id\": \"3e3c9bcff25d4ebfa934727ce756332e\"\n        },\n        {\n            \"m_Id\": \"9a36829604644ad99ecb1449acd31610\"\n        },\n        {\n            \"m_Id\": \"23c6d30846c14734a7aef5bcfa9cf23e\"\n        },\n        {\n            \"m_Id\": \"063fe1125ed742baad5fa5325f629ed4\"\n        },\n        {\n            \"m_Id\": \"3a69654d9bec4bb0a7a76a679cdf647a\"\n        },\n        {\n            \"m_Id\": \"7a24a09fb22542498caa5191de4a229d\"\n        },\n        {\n            \"m_Id\": \"7bf517c95d254913a885b790808a71f3\"\n        },\n        {\n            \"m_Id\": \"73f4c5b7dfa44d23b605cf707f9bf972\"\n        },\n        {\n            \"m_Id\": \"7b0dcc64a8544d82bbeaecae8fc3288e\"\n        },\n        {\n            \"m_Id\": \"66d2a768255a4eea87fb6f88a63197c0\"\n        },\n        {\n            \"m_Id\": \"3b731c6a92ca40c7bd8828ad91c078ee\"\n        },\n        {\n            \"m_Id\": \"5e89771458ba4e6a98f4250529b3b31c\"\n        },\n        {\n            \"m_Id\": \"16b0e3ae3d13441cb321bf8b9b137f6f\"\n        },\n        {\n            \"m_Id\": \"59dc0b543c3345b7933b277d5a2ebcd8\"\n        },\n        {\n            \"m_Id\": \"69edd39255964bacbbc5a1d95d4fdc78\"\n        },\n        {\n            \"m_Id\": \"8437a7ad96434b83b1d1b843e268b428\"\n        },\n        {\n            \"m_Id\": \"84b5c4a95eb34f68b3bd18fabf897173\"\n        }\n    ],\n    \"m_GroupDatas\": [],\n    \"m_StickyNoteDatas\": [],\n    \"m_Edges\": [\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"063fe1125ed742baad5fa5325f629ed4\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"3a69654d9bec4bb0a7a76a679cdf647a\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"16b0e3ae3d13441cb321bf8b9b137f6f\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"73f4c5b7dfa44d23b605cf707f9bf972\"\n                },\n                \"m_SlotId\": -637458655\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"1fab4e374a214d1f8bd591fa8bb86d79\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"4360cdb25a12451aa48604ae01c960b9\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"23c6d30846c14734a7aef5bcfa9cf23e\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"063fe1125ed742baad5fa5325f629ed4\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"2e1bf783126c47778acb69e3374335ef\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"5e1d73d413b44e8589d578e972bab786\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"3a69654d9bec4bb0a7a76a679cdf647a\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"9a36829604644ad99ecb1449acd31610\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"3b731c6a92ca40c7bd8828ad91c078ee\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"16b0e3ae3d13441cb321bf8b9b137f6f\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"3e3c9bcff25d4ebfa934727ce756332e\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"9a36829604644ad99ecb1449acd31610\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"4360cdb25a12451aa48604ae01c960b9\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"88b7867498a64a8aae11c361213aa821\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"59dc0b543c3345b7933b277d5a2ebcd8\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"69edd39255964bacbbc5a1d95d4fdc78\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"5e1d73d413b44e8589d578e972bab786\"\n                },\n                \"m_SlotId\": 3\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"b5d5cb5f536c406b907a346609933d42\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"5e89771458ba4e6a98f4250529b3b31c\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"16b0e3ae3d13441cb321bf8b9b137f6f\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"6334a94177ff4bf9add8b7481144548b\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"88b7867498a64a8aae11c361213aa821\"\n                },\n                \"m_SlotId\": 2\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"66d2a768255a4eea87fb6f88a63197c0\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7b0dcc64a8544d82bbeaecae8fc3288e\"\n                },\n                \"m_SlotId\": -1302009336\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"69edd39255964bacbbc5a1d95d4fdc78\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7b0dcc64a8544d82bbeaecae8fc3288e\"\n                },\n                \"m_SlotId\": -637458655\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"73f4c5b7dfa44d23b605cf707f9bf972\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"1fab4e374a214d1f8bd591fa8bb86d79\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7a24a09fb22542498caa5191de4a229d\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"84b5c4a95eb34f68b3bd18fabf897173\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7b0dcc64a8544d82bbeaecae8fc3288e\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"92eaf344b2ee40e6a36443ae9934ac38\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7bf517c95d254913a885b790808a71f3\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"73f4c5b7dfa44d23b605cf707f9bf972\"\n                },\n                \"m_SlotId\": -1302009336\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"8437a7ad96434b83b1d1b843e268b428\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"69edd39255964bacbbc5a1d95d4fdc78\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"84709a64d8a24fb290f63c0a010008fc\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"3a69654d9bec4bb0a7a76a679cdf647a\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"84b5c4a95eb34f68b3bd18fabf897173\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n                },\n                \"m_SlotId\": 2\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"88b7867498a64a8aae11c361213aa821\"\n                },\n                \"m_SlotId\": 3\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"06e3979d054040678f0c2d6132c4d9a4\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"8aa9920efe324572844a9454bb0d3155\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"88b7867498a64a8aae11c361213aa821\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"8aa9920efe324572844a9454bb0d3155\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"d6a60b13ea87425db70b8a9ed890f99e\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"92eaf344b2ee40e6a36443ae9934ac38\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"2e1bf783126c47778acb69e3374335ef\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"9a36829604644ad99ecb1449acd31610\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"4360cdb25a12451aa48604ae01c960b9\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n                },\n                \"m_SlotId\": 2\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n                },\n                \"m_SlotId\": 1\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n                },\n                \"m_SlotId\": 3\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"84b5c4a95eb34f68b3bd18fabf897173\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n                },\n                \"m_SlotId\": 4\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n                },\n                \"m_SlotId\": 3\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"ba07071fc9f74c59b7ae54084bc4559b\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"73f4c5b7dfa44d23b605cf707f9bf972\"\n                },\n                \"m_SlotId\": -1966073777\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"bdef294c438045b2b70f368601d7ffbb\"\n                },\n                \"m_SlotId\": 5\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"6334a94177ff4bf9add8b7481144548b\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"d6a60b13ea87425db70b8a9ed890f99e\"\n                },\n                \"m_SlotId\": 1\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"a99cd75852d548458d219c36fe23383c\"\n                },\n                \"m_SlotId\": 0\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"e6e2276749544ff689793067b7b5e4da\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"7b0dcc64a8544d82bbeaecae8fc3288e\"\n                },\n                \"m_SlotId\": -1966073777\n            }\n        },\n        {\n            \"m_OutputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"eb3b458649b94a7ca9081f57922d8e3d\"\n                },\n                \"m_SlotId\": 0\n            },\n            \"m_InputSlot\": {\n                \"m_Node\": {\n                    \"m_Id\": \"2e1bf783126c47778acb69e3374335ef\"\n                },\n                \"m_SlotId\": 1\n            }\n        }\n    ],\n    \"m_VertexContext\": {\n        \"m_Position\": {\n            \"x\": 69.00001525878906,\n            \"y\": -1.9999995231628419\n        },\n        \"m_Blocks\": [\n            {\n                \"m_Id\": \"7e16b64755f2495da852d9a531216784\"\n            },\n            {\n                \"m_Id\": \"607e703f097e4ba6a34cc10f1c5e749b\"\n            },\n            {\n                \"m_Id\": \"65d2a83223ef4c6fb0eca72a3565523d\"\n            }\n        ]\n    },\n    \"m_FragmentContext\": {\n        \"m_Position\": {\n            \"x\": 76.00006103515625,\n            \"y\": 203.99996948242188\n        },\n        \"m_Blocks\": [\n            {\n                \"m_Id\": \"06e3979d054040678f0c2d6132c4d9a4\"\n            },\n            {\n                \"m_Id\": \"b5d5cb5f536c406b907a346609933d42\"\n            }\n        ]\n    },\n    \"m_PreviewData\": {\n        \"serializedMesh\": {\n            \"m_SerializedMesh\": \"{\\\"mesh\\\":{\\\"instanceID\\\":0}}\",\n            \"m_Guid\": \"\"\n        },\n        \"preventRotation\": false\n    },\n    \"m_Path\": \"Shader Graphs\",\n    \"m_GraphPrecision\": 1,\n    \"m_PreviewMode\": 2,\n    \"m_OutputNode\": {\n        \"m_Id\": \"\"\n    },\n    \"m_SubDatas\": [],\n    \"m_ActiveTargets\": [\n        {\n            \"m_Id\": \"617c8cfa8f8648c5b53819e9b33377e4\"\n        }\n    ]\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector2MaterialSlot\",\n    \"m_ObjectId\": \"02d136aa49f645488392975be4fb51de\",\n    \"m_Id\": -1966073777,\n    \"m_DisplayName\": \"Position\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_Position\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.SplitNode\",\n    \"m_ObjectId\": \"063fe1125ed742baad5fa5325f629ed4\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Split\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1131.0001220703125,\n            \"y\": -32.00003433227539,\n            \"width\": 120.00006103515625,\n            \"height\": 148.99998474121095\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"d9247ec6267943dd850ed014741f7f5d\"\n        },\n        {\n            \"m_Id\": \"d750707c218a426a8f39b449f9687cd0\"\n        },\n        {\n            \"m_Id\": \"379a72e71894491e8a465e70ad915f6c\"\n        },\n        {\n            \"m_Id\": \"3a2c6a77a13645358326719a45fed328\"\n        },\n        {\n            \"m_Id\": \"c8d55a10f235419f966828f37ca7fda8\"\n        }\n    ],\n    \"synonyms\": [\n        \"separate\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"0682b0a84e3e48959813da7602cd881b\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"True\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"True\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 1.0,\n        \"y\": 1.0,\n        \"z\": 1.0,\n        \"w\": 1.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BlockNode\",\n    \"m_ObjectId\": \"06e3979d054040678f0c2d6132c4d9a4\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"SurfaceDescription.BaseColor\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": 0.0,\n            \"y\": 0.0,\n            \"width\": 0.0,\n            \"height\": 0.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"b0fc68d452484ec6ac07019c78b1ef69\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedDescriptor\": \"SurfaceDescription.BaseColor\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"11c111dfbd424f5687be24fd1da00398\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"False\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"False\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"1545e29c9c0844f198c2829840d3081c\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 2.0,\n        \"y\": 2.0,\n        \"z\": 2.0,\n        \"w\": 2.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.MultiplyNode\",\n    \"m_ObjectId\": \"16b0e3ae3d13441cb321bf8b9b137f6f\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Multiply\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1257.0,\n            \"y\": -608.0,\n            \"width\": 126.0,\n            \"height\": 117.99996948242188\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"dba63f2da87b48858613bfd72d647f37\"\n        },\n        {\n            \"m_Id\": \"8ee9d5b696994f728f9dd38d56d11e99\"\n        },\n        {\n            \"m_Id\": \"7d6c13da1fbd47fbabee37285df123cb\"\n        }\n    ],\n    \"synonyms\": [\n        \"multiplication\",\n        \"times\",\n        \"x\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"17efe1c902ea420497045e0d8fab402e\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector4MaterialSlot\",\n    \"m_ObjectId\": \"18140aba3dae465da19b2560abc9f55d\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"CircleColor\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"1caf95906956428f8580fe70babba388\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"1e2bb8fbe4404ec5980744f471f06065\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.LengthNode\",\n    \"m_ObjectId\": \"1fab4e374a214d1f8bd591fa8bb86d79\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Length\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -744.0,\n            \"y\": -185.0,\n            \"width\": 130.0,\n            \"height\": 94.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"bf078139ba4945c790b1711193fa1cad\"\n        },\n        {\n            \"m_Id\": \"df3072559c1d436bbb123872079495b9\"\n        }\n    ],\n    \"synonyms\": [\n        \"measure\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"21ccb671c1fd4b9d9fd3163f31f7493d\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Scale\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Scale\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"22af6cb1de004a4ab0d7faf7f5ea9283\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.ObjectNode\",\n    \"m_ObjectId\": \"23c6d30846c14734a7aef5bcfa9cf23e\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Object\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1302.0001220703125,\n            \"y\": -56.00004196166992,\n            \"width\": 153.0,\n            \"height\": 172.99998474121095\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"fb03de204b904406b09bc8ba88e674a4\"\n        },\n        {\n            \"m_Id\": \"21ccb671c1fd4b9d9fd3163f31f7493d\"\n        },\n        {\n            \"m_Id\": \"63469731a62942ccb7733758d088f524\"\n        },\n        {\n            \"m_Id\": \"635363fe74ce4b7da72f0ff66cdafe6e\"\n        },\n        {\n            \"m_Id\": \"27cd0ebef04b40e2b5a03055bccb0c61\"\n        }\n    ],\n    \"synonyms\": [\n        \"position\",\n        \"scale\",\n        \"bounds\",\n        \"size\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"25dd43a02e624aa18743b452ae43caf2\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"27cd0ebef04b40e2b5a03055bccb0c61\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"Bounds Size\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Bounds Size\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"2908ff6adfe24e898ff60225207efda2\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"2d0e7eb1aaf24f5db9e51238692e0eab\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"R\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"R\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.ComparisonNode\",\n    \"m_ObjectId\": \"2e1bf783126c47778acb69e3374335ef\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Comparison\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -537.0001220703125,\n            \"y\": 706.0,\n            \"width\": 145.00009155273438,\n            \"height\": 136.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"9b3e7147131245c8a9445fcb9e2f5714\"\n        },\n        {\n            \"m_Id\": \"4423dac80f914e66b7e1c471df1aefe7\"\n        },\n        {\n            \"m_Id\": \"ed7a15b8a53a42e4ac437e75dac92e2c\"\n        }\n    ],\n    \"synonyms\": [\n        \"equal\",\n        \"greater than\",\n        \"less than\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_ComparisonType\": 2\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector4MaterialSlot\",\n    \"m_ObjectId\": \"2f8bfc438b664b70b5ad50a457ba499a\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"RGBA\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"RGBA\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.TangentMaterialSlot\",\n    \"m_ObjectId\": \"301fc3e60c3f456eba29ec2ee9178e67\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Tangent\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Tangent\",\n    \"m_StageCapability\": 1,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": [],\n    \"m_Space\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"36a7978d88b5425b9aed126066f252c3\",\n    \"m_Id\": -1302009336,\n    \"m_DisplayName\": \"Wavyness\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_Wavyness\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"36f15292a7b94176b9b30554f389e46a\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"379a72e71894491e8a465e70ad915f6c\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"G\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"G\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"3a2c6a77a13645358326719a45fed328\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DivideNode\",\n    \"m_ObjectId\": \"3a69654d9bec4bb0a7a76a679cdf647a\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Divide\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -977.0,\n            \"y\": 2.0,\n            \"width\": 126.0,\n            \"height\": 118.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"8fdf0ff78a484b39aa4e346f2b3f832b\"\n        },\n        {\n            \"m_Id\": \"1545e29c9c0844f198c2829840d3081c\"\n        },\n        {\n            \"m_Id\": \"2908ff6adfe24e898ff60225207efda2\"\n        }\n    ],\n    \"synonyms\": [\n        \"division\",\n        \"divided by\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"3a73d9099a28416487ce53c79843f152\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"G\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"G\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BooleanMaterialSlot\",\n    \"m_ObjectId\": \"3a893990e0cf435e9cb4e1d94175232d\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": false,\n    \"m_DefaultValue\": false\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"3b56b9691fa841d293ccf963762a5b72\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.TimeNode\",\n    \"m_ObjectId\": \"3b731c6a92ca40c7bd8828ad91c078ee\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Time\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1509.0,\n            \"y\": -578.0000610351563,\n            \"width\": 124.0,\n            \"height\": 173.00003051757813\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"3b56b9691fa841d293ccf963762a5b72\"\n        },\n        {\n            \"m_Id\": \"56354161ebb944d9a24e3be83ed28048\"\n        },\n        {\n            \"m_Id\": \"647b71f3eebc4c35bcc71ebeaff87265\"\n        },\n        {\n            \"m_Id\": \"ea7dd2b6a3ee4503b59a858936906feb\"\n        },\n        {\n            \"m_Id\": \"44ccbd60d5c741f6ab74092c61877b3e\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"3c0e2e5ce8e640baa7e665ceeb973617\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"3e3c9bcff25d4ebfa934727ce756332e\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -765.0,\n            \"y\": -32.0,\n            \"width\": 110.0,\n            \"height\": 34.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"8689ce9c3be6437abf910a1a94006908\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"604350062f16489eb5a80307ef0bd65c\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.ComparisonNode\",\n    \"m_ObjectId\": \"4360cdb25a12451aa48604ae01c960b9\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Comparison\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -439.0001220703125,\n            \"y\": 68.00003814697266,\n            \"width\": 144.99996948242188,\n            \"height\": 135.99996948242188\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"50f7f06b9ab94275b8fe4b1d93715cdc\"\n        },\n        {\n            \"m_Id\": \"4e5213369deb489cb4e957c2b160241d\"\n        },\n        {\n            \"m_Id\": \"3a893990e0cf435e9cb4e1d94175232d\"\n        }\n    ],\n    \"synonyms\": [\n        \"equal\",\n        \"greater than\",\n        \"less than\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_ComparisonType\": 2\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"4423dac80f914e66b7e1c471df1aefe7\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"44ccbd60d5c741f6ab74092c61877b3e\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"Smooth Delta\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Smooth Delta\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"48d2207a1e1b4800b5a2d7dd038ed949\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"HSV\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"HSV\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"4e5213369deb489cb4e957c2b160241d\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"50b539e67e6949af9f7eee6bd56e950e\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"50f7f06b9ab94275b8fe4b1d93715cdc\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"53fbcae91453450c8afd2a091ba00c92\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"G\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"G\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"56354161ebb944d9a24e3be83ed28048\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Sine Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Sine Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"5825fc4958db483ea004e1cddfaed426\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.Rendering.Universal.ShaderGraph.UniversalSpriteUnlitSubTarget\",\n    \"m_ObjectId\": \"587f73672d3d45198579e92c9615233e\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.TimeNode\",\n    \"m_ObjectId\": \"59dc0b543c3345b7933b277d5a2ebcd8\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Time\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1491.0001220703125,\n            \"y\": 958.0,\n            \"width\": 124.0,\n            \"height\": 173.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"1caf95906956428f8580fe70babba388\"\n        },\n        {\n            \"m_Id\": \"e5a2250b1ea44aef861f9c843d87c534\"\n        },\n        {\n            \"m_Id\": \"be35defcc84440f5adde8325def45077\"\n        },\n        {\n            \"m_Id\": \"dfd22a0c0fdf439786588ac6fffb2f4f\"\n        },\n        {\n            \"m_Id\": \"f462df8b3f4b48a4bb954da6071178cc\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"5acbcbac899640e8a606a06de36ce23e\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"5ccde9994f51433d9de39a1fbb797324\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Alpha\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Alpha\",\n    \"m_StageCapability\": 2,\n    \"m_Value\": 1.0,\n    \"m_DefaultValue\": 1.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BranchNode\",\n    \"m_ObjectId\": \"5e1d73d413b44e8589d578e972bab786\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Branch\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -211.00003051757813,\n            \"y\": 530.0000610351563,\n            \"width\": 169.99996948242188,\n            \"height\": 141.99993896484376\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"d346e0aa311b4e538a85b3595f1147c8\"\n        },\n        {\n            \"m_Id\": \"0682b0a84e3e48959813da7602cd881b\"\n        },\n        {\n            \"m_Id\": \"11c111dfbd424f5687be24fd1da00398\"\n        },\n        {\n            \"m_Id\": \"6aab68b9df5743e586f18b033c7c4577\"\n        }\n    ],\n    \"synonyms\": [\n        \"switch\",\n        \"if\",\n        \"else\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"5e89771458ba4e6a98f4250529b3b31c\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1448.771240234375,\n            \"y\": -615.5009765625,\n            \"width\": 0.0,\n            \"height\": 0.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"d96fb8cc1fc34b13ad4e802ed837b620\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"d4221d82bf004cafa97f92d1dc9c8947\"\n    }\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty\",\n    \"m_ObjectId\": \"604350062f16489eb5a80307ef0bd65c\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"835d812f-bbf3-4cae-b122-fbd9fc1200a5\"\n    },\n    \"m_Name\": \"Radius\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"Radius\",\n    \"m_DefaultReferenceName\": \"_Radius\",\n    \"m_OverrideReferenceName\": \"\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": 0.5,\n    \"m_FloatType\": 0,\n    \"m_RangeValues\": {\n        \"x\": 0.0,\n        \"y\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BlockNode\",\n    \"m_ObjectId\": \"607e703f097e4ba6a34cc10f1c5e749b\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"VertexDescription.Normal\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": 0.0,\n            \"y\": 0.0,\n            \"width\": 0.0,\n            \"height\": 0.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"ddbdcd10ca5b41a3b3c81778e1fc6bb4\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedDescriptor\": \"VertexDescription.Normal\"\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.Rendering.Universal.ShaderGraph.UniversalTarget\",\n    \"m_ObjectId\": \"617c8cfa8f8648c5b53819e9b33377e4\",\n    \"m_Datas\": [],\n    \"m_ActiveSubTarget\": {\n        \"m_Id\": \"587f73672d3d45198579e92c9615233e\"\n    },\n    \"m_AllowMaterialOverride\": false,\n    \"m_SurfaceType\": 0,\n    \"m_ZTestMode\": 4,\n    \"m_ZWriteControl\": 0,\n    \"m_AlphaMode\": 0,\n    \"m_RenderFace\": 2,\n    \"m_AlphaClip\": false,\n    \"m_CastShadows\": true,\n    \"m_ReceiveShadows\": true,\n    \"m_SupportsLODCrossFade\": false,\n    \"m_CustomEditorGUI\": \"\",\n    \"m_SupportVFX\": false\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.CustomFunctionNode\",\n    \"m_ObjectId\": \"6334a94177ff4bf9add8b7481144548b\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"HSVtoRGB (Custom Function)\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -510.0000305175781,\n            \"y\": 305.00006103515627,\n            \"width\": 215.9998779296875,\n            \"height\": 93.99996948242188\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"74a230edb94447e7a1d0bf0bd4fe8354\"\n        },\n        {\n            \"m_Id\": \"cafb0b2905fb4de2a71fecfb20988a98\"\n        }\n    ],\n    \"synonyms\": [\n        \"code\",\n        \"HLSL\"\n    ],\n    \"m_Precision\": 1,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SourceType\": 0,\n    \"m_FunctionName\": \"HSVtoRGB\",\n    \"m_FunctionSource\": \"100bb6703df6f1b4f8c6af09609e86a8\",\n    \"m_FunctionBody\": \"Enter function body here...\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"63469731a62942ccb7733758d088f524\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"World Bounds Min\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"World Bounds Min\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"635363fe74ce4b7da72f0ff66cdafe6e\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"World Bounds Max\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"World Bounds Max\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"647b71f3eebc4c35bcc71ebeaff87265\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Cosine Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Cosine Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BlockNode\",\n    \"m_ObjectId\": \"65d2a83223ef4c6fb0eca72a3565523d\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"VertexDescription.Tangent\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": 0.0,\n            \"y\": 0.0,\n            \"width\": 0.0,\n            \"height\": 0.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"301fc3e60c3f456eba29ec2ee9178e67\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedDescriptor\": \"VertexDescription.Tangent\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"66d2a768255a4eea87fb6f88a63197c0\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -951.9998779296875,\n            \"y\": 701.0,\n            \"width\": 129.0,\n            \"height\": 33.99993896484375\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"d1b97ab00cd44f0480f1c9158a080e61\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"f00581f6ce32427abe0c266f899c1635\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.MultiplyNode\",\n    \"m_ObjectId\": \"69edd39255964bacbbc5a1d95d4fdc78\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Multiply\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1239.0,\n            \"y\": 928.0,\n            \"width\": 126.0,\n            \"height\": 117.9998779296875\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"ac739396d3a546ce9f695d7519fb9b1a\"\n        },\n        {\n            \"m_Id\": \"8981464acf55498fbd76c9b5210a4a85\"\n        },\n        {\n            \"m_Id\": \"36f15292a7b94176b9b30554f389e46a\"\n        }\n    ],\n    \"synonyms\": [\n        \"multiplication\",\n        \"times\",\n        \"x\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"6aab68b9df5743e586f18b033c7c4577\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.SubGraphNode\",\n    \"m_ObjectId\": \"73f4c5b7dfa44d23b605cf707f9bf972\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"WavyOutline\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1030.0,\n            \"y\": -405.0,\n            \"width\": 221.0,\n            \"height\": 303.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"bc275964d97543deb1582ebea01d16ab\"\n        },\n        {\n            \"m_Id\": \"02d136aa49f645488392975be4fb51de\"\n        },\n        {\n            \"m_Id\": \"86fbbb8fff254034ac51f91cde8f6513\"\n        },\n        {\n            \"m_Id\": \"bd0024d79ca245308747f1d739cb6af4\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedSubGraph\": \"{\\n    \\\"subGraph\\\": {\\n        \\\"fileID\\\": -5475051401550479605,\\n        \\\"guid\\\": \\\"771848ccab2211c4dbc0de89f52d9c6a\\\",\\n        \\\"type\\\": 3\\n    }\\n}\",\n    \"m_PropertyGuids\": [\n        \"ab9995ae-290d-47f2-9145-8b923a1384cc\",\n        \"d014a460-a7db-48c0-a7f4-eadf2fc66db2\",\n        \"7f97f62b-227e-4d82-924e-5eba88829b02\",\n        \"e24e53d1-9e26-4d6c-ac9e-10c97a7433bc\"\n    ],\n    \"m_PropertyIds\": [\n        -1302009336,\n        -179097293,\n        -1966073777,\n        -637458655\n    ],\n    \"m_Dropdowns\": [],\n    \"m_DropdownSelectedEntries\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"74a230edb94447e7a1d0bf0bd4fe8354\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"7a24a09fb22542498caa5191de4a229d\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1052.0,\n            \"y\": 537.0,\n            \"width\": 139.99993896484376,\n            \"height\": 34.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"c42eb2fd42674d41892a08403ad7e103\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"fc703087ffc240b5bf6d4ba585862c1b\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.SubGraphNode\",\n    \"m_ObjectId\": \"7b0dcc64a8544d82bbeaecae8fc3288e\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"WavyOutline\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -985.9999389648438,\n            \"y\": 734.9999389648438,\n            \"width\": 221.0001220703125,\n            \"height\": 303.00006103515627\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"36a7978d88b5425b9aed126066f252c3\"\n        },\n        {\n            \"m_Id\": \"bb62b29bc4864d0a9c9d1c7828c47fb6\"\n        },\n        {\n            \"m_Id\": \"8b77e442f5a746fc83b152e132334877\"\n        },\n        {\n            \"m_Id\": \"9bc722cbc39b47c198c156fd492d4a3d\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedSubGraph\": \"{\\n    \\\"subGraph\\\": {\\n        \\\"fileID\\\": -5475051401550479605,\\n        \\\"guid\\\": \\\"771848ccab2211c4dbc0de89f52d9c6a\\\",\\n        \\\"type\\\": 3\\n    }\\n}\",\n    \"m_PropertyGuids\": [\n        \"ab9995ae-290d-47f2-9145-8b923a1384cc\",\n        \"d014a460-a7db-48c0-a7f4-eadf2fc66db2\",\n        \"7f97f62b-227e-4d82-924e-5eba88829b02\",\n        \"e24e53d1-9e26-4d6c-ac9e-10c97a7433bc\"\n    ],\n    \"m_PropertyIds\": [\n        -1302009336,\n        -179097293,\n        -1966073777,\n        -637458655\n    ],\n    \"m_Dropdowns\": [],\n    \"m_DropdownSelectedEntries\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"7bf517c95d254913a885b790808a71f3\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1251.0,\n            \"y\": -394.0000305175781,\n            \"width\": 129.0,\n            \"height\": 34.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"9c010607b56a44629bf8d47cde977433\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"f00581f6ce32427abe0c266f899c1635\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"7c35d43249c14d22be97c0b99cb00e9a\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 1.0,\n        \"y\": 1.0,\n        \"z\": 1.0,\n        \"w\": 1.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"7d6c13da1fbd47fbabee37285df123cb\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BlockNode\",\n    \"m_ObjectId\": \"7e16b64755f2495da852d9a531216784\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"VertexDescription.Position\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": 11.000056266784668,\n            \"y\": 30.99998664855957,\n            \"width\": 200.0,\n            \"height\": 41.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"c7836c0dd56142d5a15a4223f88f57c2\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedDescriptor\": \"VertexDescription.Position\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"8437a7ad96434b83b1d1b843e268b428\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1431.0,\n            \"y\": 921.0,\n            \"width\": 162.0,\n            \"height\": 33.99993896484375\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"c495fdc3d1bc46f68833f9f8417a4800\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"d4221d82bf004cafa97f92d1dc9c8947\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"84709a64d8a24fb290f63c0a010008fc\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -977.0,\n            \"y\": -36.000022888183597,\n            \"width\": 132.9998779296875,\n            \"height\": 34.0000114440918\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"c4454ad521fd4157bce032212d284694\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"accc1775f8284df397e90f605b241244\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.MultiplyNode\",\n    \"m_ObjectId\": \"84b5c4a95eb34f68b3bd18fabf897173\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Multiply\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -864.0,\n            \"y\": 474.0,\n            \"width\": 126.0,\n            \"height\": 118.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"b4f1ac1bc69d4ce793ac1e180774c35a\"\n        },\n        {\n            \"m_Id\": \"d76887d37e164874a95c18d641146af9\"\n        },\n        {\n            \"m_Id\": \"d37a117683984c57b0fce962e552bab5\"\n        }\n    ],\n    \"synonyms\": [\n        \"multiplication\",\n        \"times\",\n        \"x\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"8689ce9c3be6437abf910a1a94006908\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Radius\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"86fbbb8fff254034ac51f91cde8f6513\",\n    \"m_Id\": -637458655,\n    \"m_DisplayName\": \"WavyTime\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_WavyTime\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BranchNode\",\n    \"m_ObjectId\": \"88b7867498a64a8aae11c361213aa821\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Branch\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -258.0000305175781,\n            \"y\": 204.0,\n            \"width\": 207.9998779296875,\n            \"height\": 326.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"dafda2ac533c4acdad023c0ef5cbfb1c\"\n        },\n        {\n            \"m_Id\": \"e52ae85039b64a32a19dd006897c1b48\"\n        },\n        {\n            \"m_Id\": \"f0c917d5cc1243349cf98d494cb79bc0\"\n        },\n        {\n            \"m_Id\": \"50b539e67e6949af9f7eee6bd56e950e\"\n        }\n    ],\n    \"synonyms\": [\n        \"switch\",\n        \"if\",\n        \"else\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"8981464acf55498fbd76c9b5210a4a85\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 2.0,\n        \"e01\": 2.0,\n        \"e02\": 2.0,\n        \"e03\": 2.0,\n        \"e10\": 2.0,\n        \"e11\": 2.0,\n        \"e12\": 2.0,\n        \"e13\": 2.0,\n        \"e20\": 2.0,\n        \"e21\": 2.0,\n        \"e22\": 2.0,\n        \"e23\": 2.0,\n        \"e30\": 2.0,\n        \"e31\": 2.0,\n        \"e32\": 2.0,\n        \"e33\": 2.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"8aa9920efe324572844a9454bb0d3155\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1437.0001220703125,\n            \"y\": 264.0000305175781,\n            \"width\": 135.0001220703125,\n            \"height\": 33.99993896484375\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"18140aba3dae465da19b2560abc9f55d\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"999906e1890243c7ae7e3965b5ee4166\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"8b77e442f5a746fc83b152e132334877\",\n    \"m_Id\": -637458655,\n    \"m_DisplayName\": \"WavyTime\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_WavyTime\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"8ee9d5b696994f728f9dd38d56d11e99\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 2.0,\n        \"e01\": 2.0,\n        \"e02\": 2.0,\n        \"e03\": 2.0,\n        \"e10\": 2.0,\n        \"e11\": 2.0,\n        \"e12\": 2.0,\n        \"e13\": 2.0,\n        \"e20\": 2.0,\n        \"e21\": 2.0,\n        \"e22\": 2.0,\n        \"e23\": 2.0,\n        \"e30\": 2.0,\n        \"e31\": 2.0,\n        \"e32\": 2.0,\n        \"e33\": 2.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"8fdf0ff78a484b39aa4e346f2b3f832b\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"90e33c71f61d4a969563d7c0529ce4c7\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"92e3a8835ed6439b967d9846bd17d707\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.LengthNode\",\n    \"m_ObjectId\": \"92eaf344b2ee40e6a36443ae9934ac38\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Length\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -679.0,\n            \"y\": 706.0000610351563,\n            \"width\": 130.00006103515626,\n            \"height\": 94.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"22af6cb1de004a4ab0d7faf7f5ea9283\"\n        },\n        {\n            \"m_Id\": \"25dd43a02e624aa18743b452ae43caf2\"\n        }\n    ],\n    \"synonyms\": [\n        \"measure\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 3,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.ColorShaderProperty\",\n    \"m_ObjectId\": \"999906e1890243c7ae7e3965b5ee4166\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"d8899a1a-2f98-4d54-9857-10c5b0bd4d16\"\n    },\n    \"m_Name\": \"CircleColor\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"CircleColor\",\n    \"m_DefaultReferenceName\": \"_CircleColor\",\n    \"m_OverrideReferenceName\": \"_Color\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": {\n        \"r\": 0.5943396091461182,\n        \"g\": 0.1710127890110016,\n        \"b\": 0.1710127890110016,\n        \"a\": 0.0\n    },\n    \"isMainColor\": false,\n    \"m_ColorMode\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.SubtractNode\",\n    \"m_ObjectId\": \"9a36829604644ad99ecb1449acd31610\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Subtract\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -765.0,\n            \"y\": 2.0,\n            \"width\": 126.0,\n            \"height\": 118.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"7c35d43249c14d22be97c0b99cb00e9a\"\n        },\n        {\n            \"m_Id\": \"e3e398f5a6574957b695c7ece6e1f23d\"\n        },\n        {\n            \"m_Id\": \"3c0e2e5ce8e640baa7e665ceeb973617\"\n        }\n    ],\n    \"synonyms\": [\n        \"subtraction\",\n        \"remove\",\n        \"minus\",\n        \"take away\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"9b3e7147131245c8a9445fcb9e2f5714\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Texture2DShaderProperty\",\n    \"m_ObjectId\": \"9ba2373f594348f590d33fb011ce0e4d\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"e30cbf34-d603-46f2-bf03-46324c0091ef\"\n    },\n    \"m_Name\": \"Texture\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"Texture\",\n    \"m_DefaultReferenceName\": \"_Texture\",\n    \"m_OverrideReferenceName\": \"_MainTex\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": {\n        \"m_SerializedTexture\": \"{\\\"texture\\\":{\\\"instanceID\\\":0}}\",\n        \"m_Guid\": \"\"\n    },\n    \"isMainTexture\": false,\n    \"useTilingAndOffset\": false,\n    \"m_Modifiable\": true,\n    \"m_DefaultType\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector2MaterialSlot\",\n    \"m_ObjectId\": \"9bc722cbc39b47c198c156fd492d4a3d\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Out_Position\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out_Position\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"9c010607b56a44629bf8d47cde977433\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Wavyness\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.SplitNode\",\n    \"m_ObjectId\": \"a99cd75852d548458d219c36fe23383c\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Split\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1052.0001220703125,\n            \"y\": 324.9999694824219,\n            \"width\": 120.0,\n            \"height\": 149.00009155273438\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"adcde6d67d8e436eab2e7a4adbc1f140\"\n        },\n        {\n            \"m_Id\": \"2d0e7eb1aaf24f5db9e51238692e0eab\"\n        },\n        {\n            \"m_Id\": \"3a73d9099a28416487ce53c79843f152\"\n        },\n        {\n            \"m_Id\": \"5acbcbac899640e8a606a06de36ce23e\"\n        },\n        {\n            \"m_Id\": \"90e33c71f61d4a969563d7c0529ce4c7\"\n        }\n    ],\n    \"synonyms\": [\n        \"separate\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"ac739396d3a546ce9f695d7519fb9b1a\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty\",\n    \"m_ObjectId\": \"accc1775f8284df397e90f605b241244\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"4a52812f-d653-4511-9f4f-1147cc8b0642\"\n    },\n    \"m_Name\": \"BorderSize\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"BorderSize\",\n    \"m_DefaultReferenceName\": \"_BorderSize\",\n    \"m_OverrideReferenceName\": \"\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": 0.20000000298023225,\n    \"m_FloatType\": 0,\n    \"m_RangeValues\": {\n        \"x\": 0.0,\n        \"y\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"adcde6d67d8e436eab2e7a4adbc1f140\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"b036675065ae497f955d0c2743f6d42b\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.ColorRGBMaterialSlot\",\n    \"m_ObjectId\": \"b0fc68d452484ec6ac07019c78b1ef69\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Base Color\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"BaseColor\",\n    \"m_StageCapability\": 2,\n    \"m_Value\": {\n        \"x\": 0.5,\n        \"y\": 0.5,\n        \"z\": 0.5\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": [],\n    \"m_ColorMode\": 0,\n    \"m_DefaultColor\": {\n        \"r\": 0.5,\n        \"g\": 0.5,\n        \"b\": 0.5,\n        \"a\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"b4f1ac1bc69d4ce793ac1e180774c35a\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BlockNode\",\n    \"m_ObjectId\": \"b5d5cb5f536c406b907a346609933d42\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"SurfaceDescription.Alpha\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": 0.0,\n            \"y\": 0.0,\n            \"width\": 0.0,\n            \"height\": 0.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"5ccde9994f51433d9de39a1fbb797324\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SerializedDescriptor\": \"SurfaceDescription.Alpha\"\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PositionNode\",\n    \"m_ObjectId\": \"ba07071fc9f74c59b7ae54084bc4559b\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Position\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1327.9998779296875,\n            \"y\": -324.0,\n            \"width\": 205.9998779296875,\n            \"height\": 130.99996948242188\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"92e3a8835ed6439b967d9846bd17d707\"\n        }\n    ],\n    \"synonyms\": [\n        \"location\"\n    ],\n    \"m_Precision\": 1,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 2,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Space\": 0,\n    \"m_PositionSource\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.CategoryData\",\n    \"m_ObjectId\": \"bb030873d8f04a51867e20063ae091fa\",\n    \"m_Name\": \"\",\n    \"m_ChildObjectList\": [\n        {\n            \"m_Id\": \"999906e1890243c7ae7e3965b5ee4166\"\n        },\n        {\n            \"m_Id\": \"604350062f16489eb5a80307ef0bd65c\"\n        },\n        {\n            \"m_Id\": \"accc1775f8284df397e90f605b241244\"\n        },\n        {\n            \"m_Id\": \"fc703087ffc240b5bf6d4ba585862c1b\"\n        },\n        {\n            \"m_Id\": \"f00581f6ce32427abe0c266f899c1635\"\n        },\n        {\n            \"m_Id\": \"d4221d82bf004cafa97f92d1dc9c8947\"\n        },\n        {\n            \"m_Id\": \"9ba2373f594348f590d33fb011ce0e4d\"\n        }\n    ]\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector2MaterialSlot\",\n    \"m_ObjectId\": \"bb62b29bc4864d0a9c9d1c7828c47fb6\",\n    \"m_Id\": -1966073777,\n    \"m_DisplayName\": \"Position\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_Position\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"bc275964d97543deb1582ebea01d16ab\",\n    \"m_Id\": -1302009336,\n    \"m_DisplayName\": \"Wavyness\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"_Wavyness\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector2MaterialSlot\",\n    \"m_ObjectId\": \"bd0024d79ca245308747f1d739cb6af4\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Out_Position\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out_Position\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.CombineNode\",\n    \"m_ObjectId\": \"bdef294c438045b2b70f368601d7ffbb\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Combine\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -669.0001831054688,\n            \"y\": 324.9999694824219,\n            \"width\": 140.00018310546876,\n            \"height\": 166.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"e8847eb214c145bc83afc3c38cb353bc\"\n        },\n        {\n            \"m_Id\": \"53fbcae91453450c8afd2a091ba00c92\"\n        },\n        {\n            \"m_Id\": \"b036675065ae497f955d0c2743f6d42b\"\n        },\n        {\n            \"m_Id\": \"17efe1c902ea420497045e0d8fab402e\"\n        },\n        {\n            \"m_Id\": \"2f8bfc438b664b70b5ad50a457ba499a\"\n        },\n        {\n            \"m_Id\": \"d38019d81eb24055b09e7146616a67f6\"\n        },\n        {\n            \"m_Id\": \"fe79ab044c2a42ab904401caa4c8c551\"\n        }\n    ],\n    \"synonyms\": [\n        \"append\"\n    ],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"be35defcc84440f5adde8325def45077\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Cosine Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Cosine Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"bf078139ba4945c790b1711193fa1cad\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"c42eb2fd42674d41892a08403ad7e103\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"BorderDeval\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"c4454ad521fd4157bce032212d284694\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"BorderSize\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"c495fdc3d1bc46f68833f9f8417a4800\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"WavynessSpeed\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PositionMaterialSlot\",\n    \"m_ObjectId\": \"c7836c0dd56142d5a15a4223f88f57c2\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Position\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Position\",\n    \"m_StageCapability\": 1,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": [],\n    \"m_Space\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"c8d55a10f235419f966828f37ca7fda8\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"cafb0b2905fb4de2a71fecfb20988a98\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"HSV\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"HSV\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"d1b97ab00cd44f0480f1c9158a080e61\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Wavyness\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BooleanMaterialSlot\",\n    \"m_ObjectId\": \"d346e0aa311b4e538a85b3595f1147c8\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Predicate\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Predicate\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": false,\n    \"m_DefaultValue\": false\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"d37a117683984c57b0fce962e552bab5\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"d38019d81eb24055b09e7146616a67f6\",\n    \"m_Id\": 5,\n    \"m_DisplayName\": \"RGB\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"RGB\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty\",\n    \"m_ObjectId\": \"d4221d82bf004cafa97f92d1dc9c8947\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"72bf3f21-684b-473d-a0de-466964bf0bff\"\n    },\n    \"m_Name\": \"WavynessSpeed\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"WavynessSpeed\",\n    \"m_DefaultReferenceName\": \"_WavynessSpeed\",\n    \"m_OverrideReferenceName\": \"\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": 0.20000000298023225,\n    \"m_FloatType\": 0,\n    \"m_RangeValues\": {\n        \"x\": 0.0,\n        \"y\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.CustomFunctionNode\",\n    \"m_ObjectId\": \"d6a60b13ea87425db70b8a9ed890f99e\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"RGBtoHSV (Custom Function)\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1287.0,\n            \"y\": 324.9999694824219,\n            \"width\": 215.9998779296875,\n            \"height\": 94.00009155273438\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"1e2bb8fbe4404ec5980744f471f06065\"\n        },\n        {\n            \"m_Id\": \"48d2207a1e1b4800b5a2d7dd038ed949\"\n        }\n    ],\n    \"synonyms\": [\n        \"code\",\n        \"HLSL\"\n    ],\n    \"m_Precision\": 1,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_SourceType\": 0,\n    \"m_FunctionName\": \"RGBtoHSV\",\n    \"m_FunctionSource\": \"100bb6703df6f1b4f8c6af09609e86a8\",\n    \"m_FunctionBody\": \"Enter function body here...\"\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"d750707c218a426a8f39b449f9687cd0\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"R\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"R\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"d76887d37e164874a95c18d641146af9\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 2.0,\n        \"e01\": 2.0,\n        \"e02\": 2.0,\n        \"e03\": 2.0,\n        \"e10\": 2.0,\n        \"e11\": 2.0,\n        \"e12\": 2.0,\n        \"e13\": 2.0,\n        \"e20\": 2.0,\n        \"e21\": 2.0,\n        \"e22\": 2.0,\n        \"e23\": 2.0,\n        \"e30\": 2.0,\n        \"e31\": 2.0,\n        \"e32\": 2.0,\n        \"e33\": 2.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"d9247ec6267943dd850ed014741f7f5d\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"In\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"In\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"d96fb8cc1fc34b13ad4e802ed837b620\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"WavynessSpeed\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BooleanMaterialSlot\",\n    \"m_ObjectId\": \"dafda2ac533c4acdad023c0ef5cbfb1c\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Predicate\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Predicate\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": false,\n    \"m_DefaultValue\": false\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicValueMaterialSlot\",\n    \"m_ObjectId\": \"dba63f2da87b48858613bfd72d647f37\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"A\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"A\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"e00\": 0.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 0.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 0.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"e00\": 1.0,\n        \"e01\": 0.0,\n        \"e02\": 0.0,\n        \"e03\": 0.0,\n        \"e10\": 0.0,\n        \"e11\": 1.0,\n        \"e12\": 0.0,\n        \"e13\": 0.0,\n        \"e20\": 0.0,\n        \"e21\": 0.0,\n        \"e22\": 1.0,\n        \"e23\": 0.0,\n        \"e30\": 0.0,\n        \"e31\": 0.0,\n        \"e32\": 0.0,\n        \"e33\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.NormalMaterialSlot\",\n    \"m_ObjectId\": \"ddbdcd10ca5b41a3b3c81778e1fc6bb4\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Normal\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Normal\",\n    \"m_StageCapability\": 1,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": [],\n    \"m_Space\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"df3072559c1d436bbb123872079495b9\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"dfd22a0c0fdf439786588ac6fffb2f4f\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"Delta Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Delta Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"e3e398f5a6574957b695c7ece6e1f23d\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"B\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"B\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 1.0,\n        \"y\": 1.0,\n        \"z\": 1.0,\n        \"w\": 1.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"e52ae85039b64a32a19dd006897c1b48\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"True\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"True\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 1.0,\n        \"y\": 1.0,\n        \"z\": 1.0,\n        \"w\": 1.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"e5a2250b1ea44aef861f9c843d87c534\",\n    \"m_Id\": 1,\n    \"m_DisplayName\": \"Sine Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Sine Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PositionNode\",\n    \"m_ObjectId\": \"e6e2276749544ff689793067b7b5e4da\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Position\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -1361.9998779296875,\n            \"y\": 689.0,\n            \"width\": 206.0,\n            \"height\": 131.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"5825fc4958db483ea004e1cddfaed426\"\n        }\n    ],\n    \"synonyms\": [\n        \"location\"\n    ],\n    \"m_Precision\": 1,\n    \"m_PreviewExpanded\": false,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 2,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Space\": 0,\n    \"m_PositionSource\": 0\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"e8847eb214c145bc83afc3c38cb353bc\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"R\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"R\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"ea7dd2b6a3ee4503b59a858936906feb\",\n    \"m_Id\": 3,\n    \"m_DisplayName\": \"Delta Time\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Delta Time\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.PropertyNode\",\n    \"m_ObjectId\": \"eb3b458649b94a7ca9081f57922d8e3d\",\n    \"m_Group\": {\n        \"m_Id\": \"\"\n    },\n    \"m_Name\": \"Property\",\n    \"m_DrawState\": {\n        \"m_Expanded\": true,\n        \"m_Position\": {\n            \"serializedVersion\": \"2\",\n            \"x\": -549.0000610351563,\n            \"y\": 672.0,\n            \"width\": 109.99996948242188,\n            \"height\": 34.0\n        }\n    },\n    \"m_Slots\": [\n        {\n            \"m_Id\": \"f58fdbe48e9846efaf135d83de459c94\"\n        }\n    ],\n    \"synonyms\": [],\n    \"m_Precision\": 0,\n    \"m_PreviewExpanded\": true,\n    \"m_DismissedVersion\": 0,\n    \"m_PreviewMode\": 0,\n    \"m_CustomColors\": {\n        \"m_SerializableColors\": []\n    },\n    \"m_Property\": {\n        \"m_Id\": \"604350062f16489eb5a80307ef0bd65c\"\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.BooleanMaterialSlot\",\n    \"m_ObjectId\": \"ed7a15b8a53a42e4ac437e75dac92e2c\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"Out\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": false,\n    \"m_DefaultValue\": false\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty\",\n    \"m_ObjectId\": \"f00581f6ce32427abe0c266f899c1635\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"971201fc-3bdc-4f2c-8417-4a3990e7c823\"\n    },\n    \"m_Name\": \"Wavyness\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"Wavyness\",\n    \"m_DefaultReferenceName\": \"_Wavyness\",\n    \"m_OverrideReferenceName\": \"\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": 0.10000000149011612,\n    \"m_FloatType\": 0,\n    \"m_RangeValues\": {\n        \"x\": 0.0,\n        \"y\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.DynamicVectorMaterialSlot\",\n    \"m_ObjectId\": \"f0c917d5cc1243349cf98d494cb79bc0\",\n    \"m_Id\": 2,\n    \"m_DisplayName\": \"False\",\n    \"m_SlotType\": 0,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"False\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0,\n        \"w\": 0.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"f462df8b3f4b48a4bb954da6071178cc\",\n    \"m_Id\": 4,\n    \"m_DisplayName\": \"Smooth Delta\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Smooth Delta\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector1MaterialSlot\",\n    \"m_ObjectId\": \"f58fdbe48e9846efaf135d83de459c94\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Radius\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Out\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": 0.0,\n    \"m_DefaultValue\": 0.0,\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector3MaterialSlot\",\n    \"m_ObjectId\": \"fb03de204b904406b09bc8ba88e674a4\",\n    \"m_Id\": 0,\n    \"m_DisplayName\": \"Position\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"Position\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0,\n        \"z\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n{\n    \"m_SGVersion\": 1,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty\",\n    \"m_ObjectId\": \"fc703087ffc240b5bf6d4ba585862c1b\",\n    \"m_Guid\": {\n        \"m_GuidSerialized\": \"64dc4c04-8cc0-4932-baba-79646c2f0394\"\n    },\n    \"m_Name\": \"BorderDeval\",\n    \"m_DefaultRefNameVersion\": 1,\n    \"m_RefNameGeneratedByDisplayName\": \"BorderDeval\",\n    \"m_DefaultReferenceName\": \"_BorderDeval\",\n    \"m_OverrideReferenceName\": \"\",\n    \"m_GeneratePropertyBlock\": true,\n    \"m_UseCustomSlotLabel\": false,\n    \"m_CustomSlotLabel\": \"\",\n    \"m_DismissedVersion\": 0,\n    \"m_Precision\": 0,\n    \"overrideHLSLDeclaration\": false,\n    \"hlslDeclarationOverride\": 0,\n    \"m_Hidden\": false,\n    \"m_Value\": 0.20000000298023225,\n    \"m_FloatType\": 0,\n    \"m_RangeValues\": {\n        \"x\": 0.0,\n        \"y\": 1.0\n    }\n}\n\n{\n    \"m_SGVersion\": 0,\n    \"m_Type\": \"UnityEditor.ShaderGraph.Vector2MaterialSlot\",\n    \"m_ObjectId\": \"fe79ab044c2a42ab904401caa4c8c551\",\n    \"m_Id\": 6,\n    \"m_DisplayName\": \"RG\",\n    \"m_SlotType\": 1,\n    \"m_Hidden\": false,\n    \"m_ShaderOutputName\": \"RG\",\n    \"m_StageCapability\": 3,\n    \"m_Value\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_DefaultValue\": {\n        \"x\": 0.0,\n        \"y\": 0.0\n    },\n    \"m_Labels\": []\n}\n\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/CircleShader.shadergraph.meta",
    "content": "fileFormatVersion: 2\nguid: a0cc4614e193c4943a535015f24efd2f\nScriptedImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 2\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n  script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/ColorConvert.cginc",
    "content": "void RGBtoHSV_float(float3 In, out float3 Out)\n{\n    float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n    float4 P = lerp(float4(In.bg, K.wz), float4(In.gb, K.xy), step(In.b, In.g));\n    float4 Q = lerp(float4(P.xyw, In.r), float4(In.r, P.yzx), step(P.x, In.r));\n    float D = Q.x - min(Q.w, Q.y);\n    float  E = 1e-10;\n    Out = float3(abs(Q.z + (Q.w - Q.y)/(6.0 * D + E)), D / (Q.x + E), Q.x);\n}\n\n\nvoid HSVtoRGB_float(float3 In, out float3 Out)\n{\n    float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n    float3 P = abs(frac(In.xxx + K.xyz) * 6.0 - K.www);\n    Out = In.z * lerp(K.xxx, saturate(P - K.xxx), In.y);\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/ColorConvert.cginc.meta",
    "content": "fileFormatVersion: 2\nguid: 100bb6703df6f1b4f8c6af09609e86a8\nShaderIncludeImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/FoodMaterial.mat",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!21 &2100000\nMaterial:\n  serializedVersion: 8\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_Name: FoodMaterial\n  m_Shader: {fileID: -6465566751694194690, guid: a0cc4614e193c4943a535015f24efd2f, type: 3}\n  m_Parent: {fileID: 0}\n  m_ModifiedSerializedProperties: 0\n  m_ValidKeywords: []\n  m_InvalidKeywords: []\n  m_LightmapFlags: 4\n  m_EnableInstancingVariants: 0\n  m_DoubleSidedGI: 0\n  m_CustomRenderQueue: -1\n  stringTagMap: {}\n  disabledShaderPasses: []\n  m_LockedProperties: \n  m_SavedProperties:\n    serializedVersion: 3\n    m_TexEnvs:\n    - unity_Lightmaps:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - unity_LightmapsInd:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    - unity_ShadowMasks:\n        m_Texture: {fileID: 0}\n        m_Scale: {x: 1, y: 1}\n        m_Offset: {x: 0, y: 0}\n    m_Ints: []\n    m_Floats:\n    - _BorderDeval: 0.2\n    - _BorderSize: 0\n    - _Radius: 0.5\n    - _Wavyness: 0.005\n    - _WavynessSpeed: 0.4\n    m_Colors:\n    - _MainTex: {r: 1, g: 0.0798079, b: 0, a: 1}\n  m_BuildTextureStacks: []\n--- !u!114 &7567924816124296995\nMonoBehaviour:\n  m_ObjectHideFlags: 11\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  version: 7\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/FoodMaterial.mat.meta",
    "content": "fileFormatVersion: 2\nguid: 763e53a3720cff4469017d76a82d72ed\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 2100000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/FoodPrefab.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &2396975056599578149\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 5956175893929514430}\n  - component: {fileID: 2753275671783793823}\n  - component: {fileID: -8171957852717026941}\n  m_Layer: 0\n  m_Name: FoodPrefab\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &5956175893929514430\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 5.57, y: 4.71, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!212 &2753275671783793823\nSpriteRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  m_Enabled: 1\n  m_CastShadows: 0\n  m_ReceiveShadows: 0\n  m_DynamicOccludee: 1\n  m_StaticShadowCaster: 0\n  m_MotionVectors: 1\n  m_LightProbeUsage: 1\n  m_ReflectionProbeUsage: 1\n  m_RayTracingMode: 0\n  m_RayTraceProcedural: 0\n  m_RenderingLayerMask: 1\n  m_RendererPriority: 0\n  m_Materials:\n  - {fileID: 2100000, guid: 763e53a3720cff4469017d76a82d72ed, type: 2}\n  m_StaticBatchInfo:\n    firstSubMesh: 0\n    subMeshCount: 0\n  m_StaticBatchRoot: {fileID: 0}\n  m_ProbeAnchor: {fileID: 0}\n  m_LightProbeVolumeOverride: {fileID: 0}\n  m_ScaleInLightmap: 1\n  m_ReceiveGI: 1\n  m_PreserveUVs: 0\n  m_IgnoreNormalsForChartDetection: 0\n  m_ImportantGI: 0\n  m_StitchLightmapSeams: 1\n  m_SelectedEditorRenderState: 0\n  m_MinimumChartSize: 4\n  m_AutoUVMaxDistance: 0.5\n  m_AutoUVMaxAngle: 89\n  m_LightmapParameters: {fileID: 0}\n  m_SortingLayerID: 0\n  m_SortingLayer: 0\n  m_SortingOrder: 0\n  m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_FlipX: 0\n  m_FlipY: 0\n  m_DrawMode: 0\n  m_Size: {x: 1, y: 1}\n  m_AdaptiveModeThreshold: 0.5\n  m_SpriteTileMode: 0\n  m_WasSpriteAssigned: 1\n  m_MaskInteraction: 0\n  m_SpriteSortPoint: 0\n--- !u!114 &-8171957852717026941\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2396975056599578149}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: d71b0ceef0f0e2643b88700cfbb4db92, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  EntityId: 0\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/FoodPrefab.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: 9c1eb5e2fe0d91f4d8067a380246343a\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/LeaderboardRow.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &1753541174774471606\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 6561148732453939736}\n  - component: {fileID: 5344514268102892593}\n  - component: {fileID: 4282485816505558134}\n  - component: {fileID: 1076563577741318949}\n  - component: {fileID: 2116962658430842051}\n  m_Layer: 5\n  m_Name: LeaderboardRow\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &6561148732453939736\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1753541174774471606}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 6129719951608030821}\n  - {fileID: 941956890789944896}\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 400, y: 44.4153}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &5344514268102892593\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1753541174774471606}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 8\n    m_Right: 8\n    m_Top: 1\n    m_Bottom: 1\n  m_ChildAlignment: 0\n  m_Spacing: 0\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!222 &4282485816505558134\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1753541174774471606}\n  m_CullTransparentMesh: 1\n--- !u!114 &1076563577741318949\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1753541174774471606}\n  m_Enabled: 0\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.80784315, g: 0.84313726, b: 0.88235295, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 21300000, guid: c111cdd5a8c82944198220c610b8720f, type: 3}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!114 &2116962658430842051\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1753541174774471606}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 1d2b23388701a1a4fa8058aa5cb5416d, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  UsernameText: {fileID: 4878036986251495766}\n  MassText: {fileID: 8873128556198631506}\n--- !u!1 &5493540872833663888\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 941956890789944896}\n  - component: {fileID: 468805145966038792}\n  - component: {fileID: 8873128556198631506}\n  - component: {fileID: 8947920826618552343}\n  m_Layer: 5\n  m_Name: Mass\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &941956890789944896\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5493540872833663888}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 6561148732453939736}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!222 &468805145966038792\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5493540872833663888}\n  m_CullTransparentMesh: 1\n--- !u!114 &8873128556198631506\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5493540872833663888}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: 9999\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4292990926\n  m_fontColor: {r: 0.80784315, g: 0.84313726, b: 0.88235295, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 23.31\n  m_fontSizeBase: 23.31\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 4\n  m_VerticalAlignment: 256\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!114 &8947920826618552343\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5493540872833663888}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 0\n  m_MinWidth: -1\n  m_MinHeight: -1\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: 1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!1 &8490069912388916866\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 6129719951608030821}\n  - component: {fileID: 2577530002943898461}\n  - component: {fileID: 4878036986251495766}\n  m_Layer: 5\n  m_Name: Username\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &6129719951608030821\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 8490069912388916866}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 6561148732453939736}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!222 &2577530002943898461\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 8490069912388916866}\n  m_CullTransparentMesh: 1\n--- !u!114 &4878036986251495766\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 8490069912388916866}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Username\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4292990926\n  m_fontColor: {r: 0.80784315, g: 0.84313726, b: 0.88235295, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 23.5\n  m_fontSizeBase: 23.5\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 1\n  m_VerticalAlignment: 512\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/LeaderboardRow.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: f1b4f231fd01fb2408fd85d1af39a3d2\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayModeTests/PlayModeExampleTest.cs",
    "content": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Sockets;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\nusing NUnit.Framework;\nusing SpacetimeDB;\nusing SpacetimeDB.Types;\nusing UnityEngine;\nusing UnityEngine.SceneManagement;\nusing UnityEngine.TestTools;\nusing Random = UnityEngine.Random;\n\npublic class PlayModeExampleTest\n{\n    [UnityTest]\n    public IEnumerator SimpleConnectionTest()\n    {\n        PlayerPrefs.DeleteAll();\n        var connected = false;\n        var conn = DbConnection.Builder().OnConnect((a, b, c) =>\n        {\n            connected = true;\n        }).OnConnectError((_) =>\n        {\n            Debug.Assert(false, \"Connection failed!\");\n        }).WithUri(\"http://127.0.0.1:3000\")\n            .WithDatabaseName(\"blackholio\").Build();\n\n        while (!connected)\n        {\n            conn.FrameTick();\n            yield return null;\n        }\n    }\n\n    [UnityTest]\n    public IEnumerator CreatePlayerAndTestDecay()\n    {\n        var connected = false;\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        PlayerPrefs.DeleteAll();\n        SceneManager.LoadScene(\"Scenes/Main\");\n        while (UIUsernameChooser.Instance == null)\n            yield return null;\n        var playerCreated = false;\n\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        while (!connected) yield return null;\n\n        GameManager.Conn.Reducers.OnEnterGame += (_, _) =>\n        {\n            Debug.Log(\"Player created\");\n            playerCreated = true;\n        };\n\n        UIUsernameChooser.Instance.UsernameInputField.text = \"Test \" + Random.Range(100000, 999999);\n        UIUsernameChooser.Instance.PlayPressed();\n        while (!playerCreated) yield return null;\n\n        Debug.Assert(GameManager.LocalIdentity != default, \"GameManager.localIdentity != default\");\n        var player = GameManager.Conn.Db.Player.Identity.Find(GameManager.LocalIdentity);\n        Debug.Assert(player != null, nameof(player) + \" != null\");\n        var circle = GameManager.Conn.Db.Circle.Iter().FirstOrDefault(a => a.PlayerId == player.PlayerId);\n\n        var foodEaten = 0;\n        GameManager.Conn.Db.Food.OnDelete += (ctx, food) =>\n        {\n            foodEaten++;\n            Debug.Log(\"Food eaten!\");\n        };\n\n        // Standing still should decay a bit\n        PlayerController.Local.EnableTestInput();\n        while (foodEaten < 50)\n        {\n            Debug.Assert(circle != null, nameof(circle) + \" != null\");\n            var ourEntity = GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId);\n            var toChosenFood = new UnityEngine.Vector2(1000, 0);\n            int chosenFoodId = 0;\n            foreach (var food in GameManager.Conn.Db.Food.Iter())\n            {\n                var thisFoodId = food.EntityId;\n                var foodEntity = GameManager.Conn.Db.Entity.EntityId.Find(thisFoodId);\n                Debug.Assert(foodEntity != null, nameof(foodEntity) + \" != null\");\n                Debug.Assert(ourEntity != null, nameof(ourEntity) + \" != null\");\n                var foodEntityPosition = foodEntity.Position;\n                var ourEntityPosition = ourEntity.Position;\n                Debug.Assert(foodEntityPosition != null, nameof(foodEntityPosition) + \" != null\");\n                Debug.Assert(ourEntityPosition != null, nameof(ourEntityPosition) + \" != null\");\n                var toThisFood = (Vector2)foodEntity.Position - (Vector2)ourEntity.Position;\n                if (toThisFood.sqrMagnitude == 0.0f) continue;\n                if (toChosenFood.sqrMagnitude > toThisFood.sqrMagnitude)\n                {\n                    chosenFoodId = thisFoodId;\n                    toChosenFood = toThisFood;\n                }\n            }\n\n            if (GameManager.Conn.Db.Entity.EntityId.Find(chosenFoodId) != null)\n            {\n                var ourNewEntity = GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId);\n                var foodEntity = GameManager.Conn.Db.Entity.EntityId.Find(chosenFoodId);\n                Debug.Assert(foodEntity != null, nameof(foodEntity) + \" != null\");\n                Debug.Assert(ourNewEntity != null, nameof(ourNewEntity) + \" != null\");\n                var toThisFood = (Vector2)foodEntity.Position - (Vector2)ourNewEntity.Position;\n                // We have to go slow for the first few pieces of food otherwise we can \"hop\" over it\n                if (foodEaten < 10)\n                {\n                    toThisFood = toThisFood.normalized * 0.5f;\n                }\n                PlayerController.Local.SetTestInput(toThisFood);\n            }\n\n            yield return null;\n        }\n\n\n        PlayerController.Local.SetTestInput(UnityEngine.Vector2.zero);\n        Debug.Assert(circle != null, nameof(circle) + \" != null\");\n        var massStart = GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId)!.Mass;\n        yield return new WaitForSeconds(10);\n        var massEnd = GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId)!.Mass;\n        Debug.Assert(massEnd < massStart, \"Mass should have decayed\");\n    }\n\n    [UnityTest]\n    public IEnumerator OneOffTest1()\n    {\n        var connected = false;\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        PlayerPrefs.DeleteAll();\n        SceneManager.LoadScene(\"Scenes/Main\");\n        while (UIUsernameChooser.Instance == null)\n            yield return null;\n        var playerCreated = false;\n\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        while (!connected) yield return null;\n\n        GameManager.Conn.Reducers.OnEnterGame += (ctx, username) =>\n        {\n            Debug.Log(\"Player created\");\n            playerCreated = true;\n        };\n\n        var name = \"Test \" + Random.Range(100000, 999999);\n        UIUsernameChooser.Instance.UsernameInputField.text = name;\n        UIUsernameChooser.Instance.PlayPressed();\n        while (!playerCreated) yield return null;\n\n        var task = GameManager.Conn.Db.Player.RemoteQuery(\n            $\"WHERE identity=0x{GameManager.LocalIdentity}\");\n        Task.Run(() => task.RunSynchronously());\n        while (!task.IsCompleted) yield return null;\n        var players = task.Result;\n        Debug.Assert(players.Length == 1, \"Should have found one player\");\n        Debug.Assert(players[0].Name == name, \"Username should match\");\n        Debug.Log($\"id: {players[0].PlayerId} Username: {players[0].Name}\");\n    }\n\n    [UnityTest]\n    public IEnumerator OneOffTest2()\n    {\n        var connected = false;\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        PlayerPrefs.DeleteAll();\n        SceneManager.LoadScene(\"Scenes/Main\");\n        while (UIUsernameChooser.Instance == null)\n            yield return null;\n        var playerCreated = false;\n\n        GameManager.OnConnected += () =>\n        {\n            Debug.Log(\"Connected\");\n            connected = true;\n        };\n\n        while (!connected) yield return null;\n\n        GameManager.Conn.Reducers.OnEnterGame += (ctx, username) =>\n        {\n            Debug.Log(\"Player created\");\n            playerCreated = true;\n        };\n\n        var name = \"Test \" + Random.Range(100000, 999999);\n        UIUsernameChooser.Instance.UsernameInputField.text = name;\n        UIUsernameChooser.Instance.PlayPressed();\n        while (!playerCreated) yield return null;\n\n        var task = GameManager.Conn.Db.Player.RemoteQuery($\"WHERE identity=0x{GameManager.LocalIdentity}\");\n        Task.Run(() => task.RunSynchronously());\n        while (!task.IsCompleted) yield return null;\n        var players = task.Result;\n        Debug.Assert(players.Length == 1, \"Should have found one player\");\n        Debug.Assert(players[0].Name == name, \"Username should match\");\n        Debug.Log($\"id: {players[0].PlayerId} Username: {players[0].Name}\");\n    }\n\n    [UnityTest]\n    public IEnumerator ReconnectionViaReloadingScene()\n    {\n        var connected = false;\n        var subscribed = false;\n        GameManager.OnConnected += () =>\n        {\n            connected = true;\n        };\n        GameManager.OnSubscriptionApplied += () =>\n        {\n            subscribed = true;\n        };\n\n        Debug.Log(\"Initial scene load!\");\n        PlayerPrefs.DeleteAll();\n        SceneManager.LoadScene(\"Scenes/Main\");\n        while (UIUsernameChooser.Instance == null)\n            yield return null;\n        var playerCreated = false;\n\n        while (!connected) yield return null;\n\n        GameManager.Conn.Reducers.OnEnterGame += (_, _) =>\n        {\n            Debug.Log(\"Player created\");\n            playerCreated = true;\n        };\n\n        var username = \"Test \" + Random.Range(100000, 999999);\n        UIUsernameChooser.Instance.UsernameInputField.text = username;\n        UIUsernameChooser.Instance.PlayPressed();\n        while (!playerCreated) yield return null;\n\n        Debug.Assert(GameManager.LocalIdentity != default, \"GameManager.localIdentity != default\");\n        var player = GameManager.Conn.Db.Player.Identity.Find(GameManager.LocalIdentity);\n        Debug.Assert(player != null, nameof(player) + \" != null\");\n        var circle = GameManager.Conn.Db.Circle.Iter().FirstOrDefault(a => a.PlayerId == player.PlayerId);\n\n        connected = false;\n        subscribed = false;\n        GameManager.Instance.Disconnect();\n\n        Debug.Log(\"Second scene load!\");\n        // Reload\n        SceneManager.LoadScene(\"Scenes/Main\");\n\n        while (!connected || !subscribed) yield return null;\n        var newPlayer = GameManager.Conn.Db.Player.Identity.Find(GameManager.LocalIdentity);\n        Debug.Assert(player.PlayerId == newPlayer.PlayerId, \"PlayerIds should match!\");\n        var newCircle = GameManager.Conn.Db.Circle.Iter().FirstOrDefault(a => a.PlayerId == newPlayer.PlayerId);\n        Debug.Assert(circle.EntityId == newCircle.EntityId, \"Circle EntityIds should match!\");\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayModeTests/PlayModeExampleTest.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 85f1a63472a5f294fbf03cf6ecf49c14\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayModeTests/PlayModeTests.asmdef",
    "content": "{\n    \"name\": \"PlayModeTests\",\n    \"rootNamespace\": \"\",\n    \"references\": [\n        \"UnityEngine.TestRunner\",\n        \"UnityEditor.TestRunner\",\n        \"Blackholio\",\n        \"com.clockworklabs.spacetimedbsdk\",\n        \"Unity.TextMeshPro\"\n    ],\n    \"includePlatforms\": [],\n    \"excludePlatforms\": [],\n    \"allowUnsafeCode\": false,\n    \"overrideReferences\": true,\n    \"precompiledReferences\": [\n        \"nunit.framework.dll\",\n        \"SpacetimeDB.BSATN.Runtime.dll\"\n    ],\n    \"autoReferenced\": false,\n    \"defineConstraints\": [\n        \"UNITY_INCLUDE_TESTS\"\n    ],\n    \"versionDefines\": [],\n    \"noEngineReferences\": false\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayModeTests/PlayModeTests.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: ba2066930f1a3da4c8b060605c0fc3a6\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayModeTests.meta",
    "content": "fileFormatVersion: 2\nguid: 06e64d41fe8564be38ab76f4b907e50e\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayerPrefab.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &5676543488734565343\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 2879264504502965732}\n  - component: {fileID: 2480194241732775548}\n  m_Layer: 0\n  m_Name: PlayerPrefab\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &2879264504502965732\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5676543488734565343}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 5.164635, y: 4.5879664, z: 0.0004599482}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &2480194241732775548\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 5676543488734565343}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 440c11fad1fc390438cf876dea4d202f, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  targetCameraSize: 50\n  updatesPerSecond: 20\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/PlayerPrefab.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: f17fb2c34cc6aff4b935207b18c03909\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/RepeatingBackground.png.meta",
    "content": "fileFormatVersion: 2\nguid: 24721031fd24bca4b96ab7e2cf2beb2b\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 13\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n    flipGreenChannel: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMipmapLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 1\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 1\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 0\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 160\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 1\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  swizzle: 50462976\n  cookieLightType: 0\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    ignorePlatformSupport: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    ignorePlatformSupport: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Server\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    ignorePlatformSupport: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Android\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    ignorePlatformSupport: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  mipmapLimitGroupName: \n  pSDRemoveMatte: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/RepeatingBackground.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &3086110404593512283\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 8710549150413727643}\n  - component: {fileID: 6947585663299053518}\n  m_Layer: 0\n  m_Name: RepeatingBackground\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &8710549150413727643\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 3086110404593512283}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 1, y: 1, z: 0}\n  m_LocalScale: {x: 30, y: 30, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!212 &6947585663299053518\nSpriteRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 3086110404593512283}\n  m_Enabled: 1\n  m_CastShadows: 0\n  m_ReceiveShadows: 0\n  m_DynamicOccludee: 1\n  m_StaticShadowCaster: 0\n  m_MotionVectors: 1\n  m_LightProbeUsage: 1\n  m_ReflectionProbeUsage: 1\n  m_RayTracingMode: 0\n  m_RayTraceProcedural: 0\n  m_RenderingLayerMask: 1\n  m_RendererPriority: 0\n  m_Materials:\n  - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}\n  m_StaticBatchInfo:\n    firstSubMesh: 0\n    subMeshCount: 0\n  m_StaticBatchRoot: {fileID: 0}\n  m_ProbeAnchor: {fileID: 0}\n  m_LightProbeVolumeOverride: {fileID: 0}\n  m_ScaleInLightmap: 1\n  m_ReceiveGI: 1\n  m_PreserveUVs: 0\n  m_IgnoreNormalsForChartDetection: 0\n  m_ImportantGI: 0\n  m_StitchLightmapSeams: 1\n  m_SelectedEditorRenderState: 0\n  m_MinimumChartSize: 4\n  m_AutoUVMaxDistance: 0.5\n  m_AutoUVMaxAngle: 89\n  m_LightmapParameters: {fileID: 0}\n  m_SortingLayerID: 0\n  m_SortingLayer: 0\n  m_SortingOrder: 0\n  m_Sprite: {fileID: 21300000, guid: 24721031fd24bca4b96ab7e2cf2beb2b, type: 3}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_FlipX: 0\n  m_FlipY: 0\n  m_DrawMode: 2\n  m_Size: {x: 0.8, y: 0.8}\n  m_AdaptiveModeThreshold: 0.5\n  m_SpriteTileMode: 0\n  m_WasSpriteAssigned: 1\n  m_MaskInteraction: 0\n  m_SpriteSortPoint: 0\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/RepeatingBackground.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: 4c2fc7da9e56bef4fa4ef386fce852b1\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scenes/Main.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 3\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 0}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_GIWorkflowMode: 1\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 0\n    m_EnableRealtimeLightmaps: 0\n  m_LightmapEditorSettings:\n    serializedVersion: 12\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_ExtractAmbientOcclusion: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 1\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 512\n    m_PVRBounces: 2\n    m_PVREnvironmentSampleCount: 256\n    m_PVREnvironmentReferencePointCount: 2048\n    m_PVRFilteringMode: 1\n    m_PVRDenoiserTypeDirect: 1\n    m_PVRDenoiserTypeIndirect: 1\n    m_PVRDenoiserTypeAO: 1\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVREnvironmentMIS: 1\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ExportTrainingData: 0\n    m_TrainingDataDestination: TrainingData\n    m_LightProbeSampleCountMultiplier: 4\n  m_LightingDataAsset: {fileID: 0}\n  m_LightingSettings: {fileID: 0}\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 3\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    buildHeightMesh: 0\n    maxJobWorkers: 0\n    preserveTilesOutsideBounds: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &15926226\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 15926227}\n  - component: {fileID: 15926228}\n  m_Layer: 5\n  m_Name: Text Area\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &15926227\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 15926226}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 181594869}\n  - {fileID: 1862924517}\n  m_Father: {fileID: 380907975}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: -0.5}\n  m_SizeDelta: {x: -20, y: -13}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &15926228\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 15926226}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding: {x: -8, y: -5, z: -8, w: -5}\n  m_Softness: {x: 0, y: 0}\n--- !u!1 &75691255\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 75691256}\n  - component: {fileID: 75691258}\n  - component: {fileID: 75691257}\n  m_Layer: 5\n  m_Name: Shadow\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 0\n--- !u!224 &75691256\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 75691255}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 995176599}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &75691257\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 75691255}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.1254902, g: 0.12941177, b: 0.14901961, a: 0.7529412}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 0}\n  m_Type: 0\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &75691258\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 75691255}\n  m_CullTransparentMesh: 1\n--- !u!1 &181594868\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 181594869}\n  - component: {fileID: 181594872}\n  - component: {fileID: 181594871}\n  - component: {fileID: 181594870}\n  m_Layer: 5\n  m_Name: Placeholder\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &181594869\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 181594868}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 15926227}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &181594870\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 181594868}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 1\n  m_MinWidth: -1\n  m_MinHeight: -1\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: -1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!114 &181594871\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 181594868}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Enter username...\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 2150773298\n  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 21.5\n  m_fontSizeBase: 21.5\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 2\n  m_HorizontalAlignment: 1\n  m_VerticalAlignment: 256\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 0\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 1\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &181594872\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 181594868}\n  m_CullTransparentMesh: 1\n--- !u!1 &200812879\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 200812880}\n  - component: {fileID: 200812881}\n  m_Layer: 5\n  m_Name: Rows\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &200812880\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 200812879}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 975418224}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &200812881\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 200812879}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 0\n    m_Right: 0\n    m_Top: 0\n    m_Bottom: 8\n  m_ChildAlignment: 1\n  m_Spacing: 0\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!1 &207278858\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 207278860}\n  - component: {fileID: 207278863}\n  - component: {fileID: 207278859}\n  - component: {fileID: 207278864}\n  m_Layer: 0\n  m_Name: GameManager\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &207278859\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 207278858}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: aaf311e6a809fdf4d874741c6fd6db10, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  backgroundInstance: {fileID: 1774890158}\n  borderThickness: 2\n  borderMaterial: {fileID: 2100000, guid: eb000c9de35d73e4d81c6a0831a9e7bc, type: 2}\n  starBackgroundPrefab: {fileID: 0}\n  deathScreen: {fileID: 369182684}\n--- !u!4 &207278860\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 207278858}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 3.5106893, y: 5.08045, z: 0.07050375}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1774890159}\n  - {fileID: 1947294996}\n  - {fileID: 510213768}\n  - {fileID: 1854949949}\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &207278863\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 207278858}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: e016ef92e52934343857fd26e55140c1, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!114 &207278864\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 207278858}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 032c09cc87224b4428dc40b15ea0dd18, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  CirclePrefab: {fileID: 5181438745055576688, guid: 3a285dad10ff54d4c9e97322286acb7a, type: 3}\n  FoodPrefab: {fileID: -8171957852717026941, guid: 9c1eb5e2fe0d91f4d8067a380246343a, type: 3}\n  PlayerPrefab: {fileID: 2480194241732775548, guid: f17fb2c34cc6aff4b935207b18c03909, type: 3}\n--- !u!1 &369182682\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 369182683}\n  - component: {fileID: 369182684}\n  m_Layer: 5\n  m_Name: DeathScreenPanel\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 0\n--- !u!224 &369182683\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 369182682}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 878843671}\n  - {fileID: 663179248}\n  m_Father: {fileID: 1689268676}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &369182684\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 369182682}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: cee67cb6465926149ac9948531bf293e, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  RespawnButton: {fileID: 1717713396}\n--- !u!1 &372276495\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 372276496}\n  m_Layer: 0\n  m_Name: Background\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &372276496\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 372276495}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 1003.78815, y: 993.21466, z: -0.20492044}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!1 &380907974\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 380907975}\n  - component: {fileID: 380907979}\n  - component: {fileID: 380907978}\n  - component: {fileID: 380907977}\n  - component: {fileID: 380907976}\n  m_Layer: 5\n  m_Name: UsernameInput\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &380907975\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 380907974}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 15926227}\n  m_Father: {fileID: 920270125}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 119.685, y: -18.51}\n  m_SizeDelta: {x: 239.37, y: 37.02}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &380907976\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 380907974}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 0\n  m_MinWidth: 239.37\n  m_MinHeight: -1\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: -1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!114 &380907977\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 380907974}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Navigation:\n    m_Mode: 3\n    m_WrapAround: 0\n    m_SelectOnUp: {fileID: 0}\n    m_SelectOnDown: {fileID: 0}\n    m_SelectOnLeft: {fileID: 0}\n    m_SelectOnRight: {fileID: 0}\n  m_Transition: 1\n  m_Colors:\n    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}\n    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}\n    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}\n    m_ColorMultiplier: 1\n    m_FadeDuration: 0.1\n  m_SpriteState:\n    m_HighlightedSprite: {fileID: 0}\n    m_PressedSprite: {fileID: 0}\n    m_SelectedSprite: {fileID: 0}\n    m_DisabledSprite: {fileID: 0}\n  m_AnimationTriggers:\n    m_NormalTrigger: Normal\n    m_HighlightedTrigger: Highlighted\n    m_PressedTrigger: Pressed\n    m_SelectedTrigger: Selected\n    m_DisabledTrigger: Disabled\n  m_Interactable: 1\n  m_TargetGraphic: {fileID: 380907978}\n  m_TextViewport: {fileID: 15926227}\n  m_TextComponent: {fileID: 1862924518}\n  m_Placeholder: {fileID: 181594871}\n  m_VerticalScrollbar: {fileID: 0}\n  m_VerticalScrollbarEventHandler: {fileID: 0}\n  m_LayoutGroup: {fileID: 0}\n  m_ScrollSensitivity: 1\n  m_ContentType: 0\n  m_InputType: 0\n  m_AsteriskChar: 42\n  m_KeyboardType: 0\n  m_LineType: 0\n  m_HideMobileInput: 0\n  m_HideSoftKeyboard: 0\n  m_CharacterValidation: 0\n  m_RegexValue: \n  m_GlobalPointSize: 21.5\n  m_CharacterLimit: 0\n  m_OnEndEdit:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnSubmit:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnSelect:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnDeselect:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnTextSelection:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnEndTextSelection:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnValueChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_OnTouchScreenKeyboardStatusChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}\n  m_CustomCaretColor: 0\n  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}\n  m_Text: \n  m_CaretBlinkRate: 0.85\n  m_CaretWidth: 1\n  m_ReadOnly: 0\n  m_RichText: 1\n  m_GlobalFontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_OnFocusSelectAll: 1\n  m_ResetOnDeActivation: 1\n  m_RestoreOriginalTextOnEscape: 1\n  m_isRichTextEditingAllowed: 0\n  m_LineLimit: 0\n  m_InputValidator: {fileID: 0}\n--- !u!114 &380907978\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 380907974}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.95686275, g: 0.95686275, b: 0.9882353, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &380907979\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 380907974}\n  m_CullTransparentMesh: 1\n--- !u!4 &510213768 stripped\nTransform:\n  m_CorrespondingSourceObject: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n  m_PrefabInstance: {fileID: 1469627116}\n  m_PrefabAsset: {fileID: 0}\n--- !u!1 &519420028\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 519420032}\n  - component: {fileID: 519420029}\n  - component: {fileID: 519420033}\n  - component: {fileID: 519420030}\n  - component: {fileID: 519420031}\n  m_Layer: 0\n  m_Name: Main Camera\n  m_TagString: MainCamera\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!81 &519420029\nAudioListener:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n--- !u!114 &519420030\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_RenderShadows: 1\n  m_RequiresDepthTextureOption: 2\n  m_RequiresOpaqueTextureOption: 2\n  m_CameraType: 0\n  m_Cameras: []\n  m_RendererIndex: -1\n  m_VolumeLayerMask:\n    serializedVersion: 2\n    m_Bits: 1\n  m_VolumeTrigger: {fileID: 0}\n  m_VolumeFrameworkUpdateModeOption: 2\n  m_RenderPostProcessing: 0\n  m_Antialiasing: 0\n  m_AntialiasingQuality: 2\n  m_StopNaN: 0\n  m_Dithering: 0\n  m_ClearDepth: 1\n  m_AllowXRRendering: 1\n  m_AllowHDROutput: 1\n  m_UseScreenCoordOverride: 0\n  m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}\n  m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}\n  m_RequiresDepthTexture: 0\n  m_RequiresColorTexture: 0\n  m_Version: 2\n  m_TaaSettings:\n    m_Quality: 3\n    m_FrameInfluence: 0.1\n    m_JitterScale: 1\n    m_MipBias: 0\n    m_VarianceClampScale: 0.9\n    m_ContrastAdaptiveSharpening: 0\n--- !u!20 &519420031\nCamera:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  serializedVersion: 2\n  m_ClearFlags: 2\n  m_BackGroundColor: {r: 0.08461539, g: 0.08461539, b: 0.1, a: 0}\n  m_projectionMatrixMode: 1\n  m_GateFitMode: 2\n  m_FOVAxisMode: 0\n  m_Iso: 200\n  m_ShutterSpeed: 0.005\n  m_Aperture: 16\n  m_FocusDistance: 10\n  m_FocalLength: 50\n  m_BladeCount: 5\n  m_Curvature: {x: 2, y: 11}\n  m_BarrelClipping: 0.25\n  m_Anamorphism: 0\n  m_SensorSize: {x: 36, y: 24}\n  m_LensShift: {x: 0, y: 0}\n  m_NormalizedViewPortRect:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  near clip plane: 0.3\n  far clip plane: 1000\n  field of view: 34\n  orthographic: 1\n  orthographic size: 50\n  m_Depth: -1\n  m_CullingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n  m_RenderingPath: -1\n  m_TargetTexture: {fileID: 0}\n  m_TargetDisplay: 0\n  m_TargetEye: 0\n  m_HDR: 1\n  m_AllowMSAA: 0\n  m_AllowDynamicResolution: 0\n  m_ForceIntoRT: 0\n  m_OcclusionCulling: 0\n  m_StereoConvergence: 10\n  m_StereoSeparation: 0.022\n--- !u!4 &519420032\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 4.87, y: 4.91, z: -10}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &519420033\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: ff430cf4f8ef94548b5d934fa322c1fa, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!1 &619394800\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 619394802}\n  - component: {fileID: 619394801}\n  m_Layer: 0\n  m_Name: Global Light 2D\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &619394801\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 619394800}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 073797afb82c5a1438f328866b10b3f0, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_ComponentVersion: 1\n  m_LightType: 4\n  m_BlendStyleIndex: 0\n  m_FalloffIntensity: 0.5\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_Intensity: 1\n  m_LightVolumeIntensity: 1\n  m_LightVolumeIntensityEnabled: 0\n  m_ApplyToSortingLayers: 00000000\n  m_LightCookieSprite: {fileID: 0}\n  m_DeprecatedPointLightCookieSprite: {fileID: 0}\n  m_LightOrder: 0\n  m_AlphaBlendOnOverlap: 0\n  m_OverlapOperation: 0\n  m_NormalMapDistance: 3\n  m_NormalMapQuality: 2\n  m_UseNormalMap: 0\n  m_ShadowIntensityEnabled: 0\n  m_ShadowIntensity: 0.75\n  m_ShadowVolumeIntensityEnabled: 0\n  m_ShadowVolumeIntensity: 0.75\n  m_LocalBounds:\n    m_Center: {x: 0, y: -0.00000011920929, z: 0}\n    m_Extent: {x: 0.9985302, y: 0.99853027, z: 0}\n  m_PointLightInnerAngle: 360\n  m_PointLightOuterAngle: 360\n  m_PointLightInnerRadius: 0\n  m_PointLightOuterRadius: 1\n  m_ShapeLightParametricSides: 5\n  m_ShapeLightParametricAngleOffset: 0\n  m_ShapeLightParametricRadius: 1\n  m_ShapeLightFalloffSize: 0.5\n  m_ShapeLightFalloffOffset: {x: 0, y: 0}\n  m_ShapePath:\n  - {x: -0.5, y: -0.5, z: 0}\n  - {x: 0.5, y: -0.5, z: 0}\n  - {x: 0.5, y: 0.5, z: 0}\n  - {x: -0.5, y: 0.5, z: 0}\n--- !u!4 &619394802\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 619394800}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!1 &663179247\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 663179248}\n  - component: {fileID: 663179252}\n  - component: {fileID: 663179251}\n  - component: {fileID: 663179250}\n  - component: {fileID: 663179249}\n  m_Layer: 5\n  m_Name: DeathScreen\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &663179248\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 663179247}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1102795559}\n  - {fileID: 1717713394}\n  m_Father: {fileID: 369182683}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0.5, y: 0.5}\n  m_AnchorMax: {x: 0.5, y: 0.5}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 392.93, y: 161.82}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &663179249\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 663179247}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_HorizontalFit: 2\n  m_VerticalFit: 2\n--- !u!114 &663179250\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 663179247}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 20\n    m_Right: 20\n    m_Top: 20\n    m_Bottom: 20\n  m_ChildAlignment: 1\n  m_Spacing: 17.3\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!114 &663179251\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 663179247}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.80784315, g: 0.827451, b: 0.8784314, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 21300000, guid: c111cdd5a8c82944198220c610b8720f, type: 3}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &663179252\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 663179247}\n  m_CullTransparentMesh: 1\n--- !u!1 &734198857\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 734198858}\n  - component: {fileID: 734198860}\n  - component: {fileID: 734198859}\n  m_Layer: 5\n  m_Name: Text (TMP)\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &734198858\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 734198857}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 867669873}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 209.305, y: -40.11}\n  m_SizeDelta: {x: 378.61, y: 40.22}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &734198859\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 734198857}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Welcome to Blackholio!\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4278190080\n  m_fontColor: {r: 0, g: 0, b: 0, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 36\n  m_fontSizeBase: 36\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 1\n  m_VerticalAlignment: 256\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &734198860\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 734198857}\n  m_CullTransparentMesh: 1\n--- !u!1 &867669872\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 867669873}\n  - component: {fileID: 867669877}\n  - component: {fileID: 867669876}\n  - component: {fileID: 867669875}\n  - component: {fileID: 867669874}\n  m_Layer: 5\n  m_Name: UIUsernameChooser\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 0\n--- !u!224 &867669873\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 867669872}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 734198858}\n  - {fileID: 920270125}\n  - {fileID: 1670294506}\n  m_Father: {fileID: 995176599}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0.5, y: 0.5}\n  m_AnchorMax: {x: 0.5, y: 0.5}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 418.61, y: 216.14}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &867669874\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 867669872}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_HorizontalFit: 2\n  m_VerticalFit: 2\n--- !u!114 &867669875\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 867669872}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 20\n    m_Right: 20\n    m_Top: 20\n    m_Bottom: 20\n  m_ChildAlignment: 1\n  m_Spacing: 17.3\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!114 &867669876\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 867669872}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.80784315, g: 0.827451, b: 0.8784314, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 21300000, guid: c111cdd5a8c82944198220c610b8720f, type: 3}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &867669877\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 867669872}\n  m_CullTransparentMesh: 1\n--- !u!1001 &868747980\nPrefabInstance:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Modification:\n    serializedVersion: 3\n    m_TransformParent: {fileID: 207278860}\n    m_Modifications:\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.x\n      value: 20\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.y\n      value: 20\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.z\n      value: 20\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.x\n      value: 1000.27747\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.y\n      value: 988.1342\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.z\n      value: -0.27542418\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.w\n      value: 1\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.x\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.y\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.z\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.x\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.y\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.z\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 4748457267955120268, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Name\n      value: StarBackground (2)\n      objectReference: {fileID: 0}\n    - target: {fileID: 6728608069378556597, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: Multiplier\n      value: -0.06\n      objectReference: {fileID: 0}\n    - target: {fileID: 7224407313110299127, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Color.a\n      value: 0.7529412\n      objectReference: {fileID: 0}\n    m_RemovedComponents: []\n    m_RemovedGameObjects: []\n    m_AddedGameObjects: []\n    m_AddedComponents: []\n  m_SourcePrefab: {fileID: 100100000, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n--- !u!1 &878843670\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 878843671}\n  - component: {fileID: 878843673}\n  - component: {fileID: 878843672}\n  m_Layer: 5\n  m_Name: Shadow\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &878843671\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 878843670}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 369182683}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &878843672\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 878843670}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.1254902, g: 0.12941177, b: 0.14901961, a: 0.7529412}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 0}\n  m_Type: 0\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &878843673\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 878843670}\n  m_CullTransparentMesh: 1\n--- !u!1 &920270124\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 920270125}\n  - component: {fileID: 920270126}\n  m_Layer: 5\n  m_Name: UsernameField\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &920270125\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 920270124}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 380907975}\n  m_Father: {fileID: 867669873}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 209.305, y: -96.03001}\n  m_SizeDelta: {x: 239.37, y: 37.02}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &920270126\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 920270124}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 0\n    m_Right: 0\n    m_Top: 0\n    m_Bottom: 0\n  m_ChildAlignment: 4\n  m_Spacing: 8\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!1 &975418223\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 975418224}\n  - component: {fileID: 975418227}\n  - component: {fileID: 975418226}\n  - component: {fileID: 975418225}\n  - component: {fileID: 975418228}\n  m_Layer: 5\n  m_Name: Panel\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &975418224\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 975418223}\n  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1676657076}\n  - {fileID: 200812880}\n  m_Father: {fileID: 1150883583}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &975418225\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 975418223}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 0\n    m_Right: 0\n    m_Top: 8\n    m_Bottom: 0\n  m_ChildAlignment: 1\n  m_Spacing: 8\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!114 &975418226\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 975418223}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.019607844, g: 0.019607844, b: 0.019607844, a: 0.88235295}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 21300000, guid: c111cdd5a8c82944198220c610b8720f, type: 3}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &975418227\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 975418223}\n  m_CullTransparentMesh: 1\n--- !u!114 &975418228\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 975418223}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 0\n  m_MinWidth: -1\n  m_MinHeight: -1\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: 1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!1 &995176598\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 995176599}\n  - component: {fileID: 995176600}\n  m_Layer: 5\n  m_Name: UIUsernameChooserPanel\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &995176599\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 995176598}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 75691256}\n  - {fileID: 867669873}\n  m_Father: {fileID: 1689268676}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &995176600\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 995176598}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fdf6b37246fc5824eb3c4e6888c33a4a, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  elements:\n  - {fileID: 75691255}\n  - {fileID: 867669872}\n  UsernameInputField: {fileID: 380907977}\n--- !u!1 &1005992895\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1005992896}\n  - component: {fileID: 1005992898}\n  - component: {fileID: 1005992897}\n  m_Layer: 5\n  m_Name: Text (TMP)\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1005992896\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1005992895}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 1670294506}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1005992897\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1005992895}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Play!\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4281479730\n  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 24\n  m_fontSizeBase: 24\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 2\n  m_VerticalAlignment: 512\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &1005992898\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1005992895}\n  m_CullTransparentMesh: 1\n--- !u!1 &1093051880\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1093051881}\n  - component: {fileID: 1093051883}\n  - component: {fileID: 1093051882}\n  m_Layer: 5\n  m_Name: Text (TMP)\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1093051881\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1093051880}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 1717713394}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1093051882\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1093051880}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Respawn\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4281479730\n  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 24\n  m_fontSizeBase: 24\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 2\n  m_VerticalAlignment: 512\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &1093051883\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1093051880}\n  m_CullTransparentMesh: 1\n--- !u!1 &1102795558\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1102795559}\n  - component: {fileID: 1102795561}\n  - component: {fileID: 1102795560}\n  m_Layer: 5\n  m_Name: Text (TMP)\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1102795559\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1102795558}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 663179248}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 196.465, y: -40.11}\n  m_SizeDelta: {x: 352.93, y: 40.22}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1102795560\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1102795558}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: You Have Been Eaten\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4278190080\n  m_fontColor: {r: 0, g: 0, b: 0, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 36\n  m_fontSizeBase: 36\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 1\n  m_VerticalAlignment: 256\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &1102795561\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1102795558}\n  m_CullTransparentMesh: 1\n--- !u!1 &1150883582\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1150883583}\n  - component: {fileID: 1150883585}\n  - component: {fileID: 1150883584}\n  m_Layer: 5\n  m_Name: Leaderboard\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1150883583\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1150883582}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 975418224}\n  m_Father: {fileID: 1689268676}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 1, y: 1}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: -200, y: -200}\n  m_SizeDelta: {x: 400, y: 400}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1150883584\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1150883582}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 3b62a4a2893f42b4a90869f7c865907d, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  RowPrefab: {fileID: 2116962658430842051, guid: f1b4f231fd01fb2408fd85d1af39a3d2, type: 3}\n  Root: {fileID: 200812880}\n--- !u!114 &1150883585\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1150883582}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Padding:\n    m_Left: 0\n    m_Right: 0\n    m_Top: 0\n    m_Bottom: 0\n  m_ChildAlignment: 0\n  m_Spacing: 0\n  m_ChildForceExpandWidth: 0\n  m_ChildForceExpandHeight: 0\n  m_ChildControlWidth: 1\n  m_ChildControlHeight: 1\n  m_ChildScaleWidth: 0\n  m_ChildScaleHeight: 0\n  m_ReverseArrangement: 0\n--- !u!1001 &1469627116\nPrefabInstance:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Modification:\n    serializedVersion: 3\n    m_TransformParent: {fileID: 207278860}\n    m_Modifications:\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.x\n      value: 15\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.y\n      value: 15\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.z\n      value: 15\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.x\n      value: 1000.27747\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.y\n      value: 988.1342\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.z\n      value: -0.27542418\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.w\n      value: 1\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.x\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.y\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.z\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.x\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.y\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.z\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 4748457267955120268, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Name\n      value: StarBackground (1)\n      objectReference: {fileID: 0}\n    - target: {fileID: 6728608069378556597, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: Multiplier\n      value: -0.04\n      objectReference: {fileID: 0}\n    - target: {fileID: 7224407313110299127, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Color.a\n      value: 0.5019608\n      objectReference: {fileID: 0}\n    - target: {fileID: 7224407313110299127, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_SortingOrder\n      value: -2\n      objectReference: {fileID: 0}\n    m_RemovedComponents: []\n    m_RemovedGameObjects: []\n    m_AddedGameObjects: []\n    m_AddedComponents: []\n  m_SourcePrefab: {fileID: 100100000, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n--- !u!1 &1670294505\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1670294506}\n  - component: {fileID: 1670294510}\n  - component: {fileID: 1670294509}\n  - component: {fileID: 1670294508}\n  - component: {fileID: 1670294507}\n  m_Layer: 5\n  m_Name: Play Button\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1670294506\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1670294505}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1005992896}\n  m_Father: {fileID: 867669873}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 209.305, y: -163.98999}\n  m_SizeDelta: {x: 179.31, y: 64.3}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1670294507\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1670294505}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 0\n  m_MinWidth: 179.31\n  m_MinHeight: 64.3\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: -1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!114 &1670294508\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1670294505}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Navigation:\n    m_Mode: 3\n    m_WrapAround: 0\n    m_SelectOnUp: {fileID: 0}\n    m_SelectOnDown: {fileID: 0}\n    m_SelectOnLeft: {fileID: 0}\n    m_SelectOnRight: {fileID: 0}\n  m_Transition: 1\n  m_Colors:\n    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}\n    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}\n    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}\n    m_ColorMultiplier: 1\n    m_FadeDuration: 0.1\n  m_SpriteState:\n    m_HighlightedSprite: {fileID: 0}\n    m_PressedSprite: {fileID: 0}\n    m_SelectedSprite: {fileID: 0}\n    m_DisabledSprite: {fileID: 0}\n  m_AnimationTriggers:\n    m_NormalTrigger: Normal\n    m_HighlightedTrigger: Highlighted\n    m_PressedTrigger: Pressed\n    m_SelectedTrigger: Selected\n    m_DisabledTrigger: Disabled\n  m_Interactable: 1\n  m_TargetGraphic: {fileID: 1670294509}\n  m_OnClick:\n    m_PersistentCalls:\n      m_Calls:\n      - m_Target: {fileID: 995176600}\n        m_TargetAssemblyTypeName: UIUsernameChooser, Assembly-CSharp\n        m_MethodName: PlayPressed\n        m_Mode: 1\n        m_Arguments:\n          m_ObjectArgument: {fileID: 0}\n          m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine\n          m_IntArgument: 0\n          m_FloatArgument: 0\n          m_StringArgument: \n          m_BoolArgument: 0\n        m_CallState: 2\n--- !u!114 &1670294509\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1670294505}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &1670294510\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1670294505}\n  m_CullTransparentMesh: 1\n--- !u!1 &1676657075\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1676657076}\n  - component: {fileID: 1676657078}\n  - component: {fileID: 1676657077}\n  m_Layer: 5\n  m_Name: Text (TMP)\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1676657076\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1676657075}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 975418224}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1676657077\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1676657075}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: Leaderboard\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4292990926\n  m_fontColor: {r: 0.80784315, g: 0.84313726, b: 0.88235295, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 36\n  m_fontSizeBase: 36\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 2\n  m_VerticalAlignment: 512\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 1\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 0\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &1676657078\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1676657075}\n  m_CullTransparentMesh: 1\n--- !u!1 &1689268672\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1689268676}\n  - component: {fileID: 1689268675}\n  - component: {fileID: 1689268674}\n  - component: {fileID: 1689268673}\n  m_Layer: 5\n  m_Name: Canvas\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &1689268673\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1689268672}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreReversedGraphics: 1\n  m_BlockingObjects: 0\n  m_BlockingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n--- !u!114 &1689268674\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1689268672}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_UiScaleMode: 0\n  m_ReferencePixelsPerUnit: 100\n  m_ScaleFactor: 1\n  m_ReferenceResolution: {x: 800, y: 600}\n  m_ScreenMatchMode: 0\n  m_MatchWidthOrHeight: 0\n  m_PhysicalUnit: 3\n  m_FallbackScreenDPI: 96\n  m_DefaultSpriteDPI: 96\n  m_DynamicPixelsPerUnit: 1\n  m_PresetInfoIsWorld: 0\n--- !u!223 &1689268675\nCanvas:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1689268672}\n  m_Enabled: 1\n  serializedVersion: 3\n  m_RenderMode: 0\n  m_Camera: {fileID: 0}\n  m_PlaneDistance: 100\n  m_PixelPerfect: 0\n  m_ReceivesEvents: 1\n  m_OverrideSorting: 0\n  m_OverridePixelPerfect: 0\n  m_SortingBucketNormalizedSize: 0\n  m_VertexColorAlwaysGammaSpace: 1\n  m_AdditionalShaderChannelsFlag: 25\n  m_UpdateRectTransformForStandalone: 0\n  m_SortingLayerID: 0\n  m_SortingOrder: 0\n  m_TargetDisplay: 0\n--- !u!224 &1689268676\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1689268672}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 0, y: 0, z: 0}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1150883583}\n  - {fileID: 995176599}\n  - {fileID: 369182683}\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 0, y: 0}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0, y: 0}\n--- !u!1 &1717713393\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1717713394}\n  - component: {fileID: 1717713398}\n  - component: {fileID: 1717713397}\n  - component: {fileID: 1717713396}\n  - component: {fileID: 1717713395}\n  m_Layer: 5\n  m_Name: Play Button\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1717713394\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1717713393}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children:\n  - {fileID: 1093051881}\n  m_Father: {fileID: 663179248}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 1}\n  m_AnchorMax: {x: 0, y: 1}\n  m_AnchoredPosition: {x: 196.465, y: -109.670006}\n  m_SizeDelta: {x: 179.31, y: 64.3}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1717713395\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1717713393}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_IgnoreLayout: 0\n  m_MinWidth: 179.31\n  m_MinHeight: 64.3\n  m_PreferredWidth: -1\n  m_PreferredHeight: -1\n  m_FlexibleWidth: -1\n  m_FlexibleHeight: -1\n  m_LayoutPriority: 1\n--- !u!114 &1717713396\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1717713393}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Navigation:\n    m_Mode: 3\n    m_WrapAround: 0\n    m_SelectOnUp: {fileID: 0}\n    m_SelectOnDown: {fileID: 0}\n    m_SelectOnLeft: {fileID: 0}\n    m_SelectOnRight: {fileID: 0}\n  m_Transition: 1\n  m_Colors:\n    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}\n    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}\n    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}\n    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}\n    m_ColorMultiplier: 1\n    m_FadeDuration: 0.1\n  m_SpriteState:\n    m_HighlightedSprite: {fileID: 0}\n    m_PressedSprite: {fileID: 0}\n    m_SelectedSprite: {fileID: 0}\n    m_DisabledSprite: {fileID: 0}\n  m_AnimationTriggers:\n    m_NormalTrigger: Normal\n    m_HighlightedTrigger: Highlighted\n    m_PressedTrigger: Pressed\n    m_SelectedTrigger: Selected\n    m_DisabledTrigger: Disabled\n  m_Interactable: 1\n  m_TargetGraphic: {fileID: 1717713397}\n  m_OnClick:\n    m_PersistentCalls:\n      m_Calls:\n      - m_Target: {fileID: 369182684}\n        m_TargetAssemblyTypeName: DeathScreen, Blackholio\n        m_MethodName: Respawn\n        m_Mode: 1\n        m_Arguments:\n          m_ObjectArgument: {fileID: 0}\n          m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine\n          m_IntArgument: 0\n          m_FloatArgument: 0\n          m_StringArgument: \n          m_BoolArgument: 0\n        m_CallState: 2\n--- !u!114 &1717713397\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1717713393}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 0.95686275, g: 0.95686275, b: 0.9882353, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}\n  m_Type: 1\n  m_PreserveAspect: 0\n  m_FillCenter: 1\n  m_FillMethod: 4\n  m_FillAmount: 1\n  m_FillClockwise: 1\n  m_FillOrigin: 0\n  m_UseSpriteMesh: 0\n  m_PixelsPerUnitMultiplier: 1\n--- !u!222 &1717713398\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1717713393}\n  m_CullTransparentMesh: 1\n--- !u!1001 &1774890157\nPrefabInstance:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Modification:\n    serializedVersion: 3\n    m_TransformParent: {fileID: 207278860}\n    m_Modifications:\n    - target: {fileID: 3086110404593512283, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_Name\n      value: RepeatingBackground\n      objectReference: {fileID: 0}\n    - target: {fileID: 3086110404593512283, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_IsActive\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 6947585663299053518, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_Size.x\n      value: 50\n      objectReference: {fileID: 0}\n    - target: {fileID: 6947585663299053518, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_Size.y\n      value: 50\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalPosition.x\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalPosition.y\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalPosition.z\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalRotation.w\n      value: 1\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalRotation.x\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalRotation.y\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalRotation.z\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.x\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.y\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.z\n      value: 0\n      objectReference: {fileID: 0}\n    m_RemovedComponents: []\n    m_RemovedGameObjects: []\n    m_AddedGameObjects: []\n    m_AddedComponents: []\n  m_SourcePrefab: {fileID: 100100000, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n--- !u!212 &1774890158 stripped\nSpriteRenderer:\n  m_CorrespondingSourceObject: {fileID: 6947585663299053518, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n  m_PrefabInstance: {fileID: 1774890157}\n  m_PrefabAsset: {fileID: 0}\n--- !u!4 &1774890159 stripped\nTransform:\n  m_CorrespondingSourceObject: {fileID: 8710549150413727643, guid: 4c2fc7da9e56bef4fa4ef386fce852b1, type: 3}\n  m_PrefabInstance: {fileID: 1774890157}\n  m_PrefabAsset: {fileID: 0}\n--- !u!4 &1854949949 stripped\nTransform:\n  m_CorrespondingSourceObject: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n  m_PrefabInstance: {fileID: 868747980}\n  m_PrefabAsset: {fileID: 0}\n--- !u!1 &1862924516\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 1862924517}\n  - component: {fileID: 1862924519}\n  - component: {fileID: 1862924518}\n  m_Layer: 5\n  m_Name: Text\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!224 &1862924517\nRectTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1862924516}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 15926227}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n  m_AnchorMin: {x: 0, y: 0}\n  m_AnchorMax: {x: 1, y: 1}\n  m_AnchoredPosition: {x: 0, y: 0}\n  m_SizeDelta: {x: 0, y: 0}\n  m_Pivot: {x: 0.5, y: 0.5}\n--- !u!114 &1862924518\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1862924516}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_Material: {fileID: 0}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_RaycastTarget: 1\n  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}\n  m_Maskable: 1\n  m_OnCullStateChanged:\n    m_PersistentCalls:\n      m_Calls: []\n  m_text: \"\\u200B\"\n  m_isRightToLeft: 0\n  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}\n  m_fontSharedMaterials: []\n  m_fontMaterial: {fileID: 0}\n  m_fontMaterials: []\n  m_fontColor32:\n    serializedVersion: 2\n    rgba: 4281479730\n  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}\n  m_enableVertexGradient: 0\n  m_colorMode: 3\n  m_fontColorGradient:\n    topLeft: {r: 1, g: 1, b: 1, a: 1}\n    topRight: {r: 1, g: 1, b: 1, a: 1}\n    bottomLeft: {r: 1, g: 1, b: 1, a: 1}\n    bottomRight: {r: 1, g: 1, b: 1, a: 1}\n  m_fontColorGradientPreset: {fileID: 0}\n  m_spriteAsset: {fileID: 0}\n  m_tintAllSprites: 0\n  m_StyleSheet: {fileID: 0}\n  m_TextStyleHashCode: -1183493901\n  m_overrideHtmlColors: 0\n  m_faceColor:\n    serializedVersion: 2\n    rgba: 4294967295\n  m_fontSize: 21.5\n  m_fontSizeBase: 21.5\n  m_fontWeight: 400\n  m_enableAutoSizing: 0\n  m_fontSizeMin: 18\n  m_fontSizeMax: 72\n  m_fontStyle: 0\n  m_HorizontalAlignment: 1\n  m_VerticalAlignment: 256\n  m_textAlignment: 65535\n  m_characterSpacing: 0\n  m_wordSpacing: 0\n  m_lineSpacing: 0\n  m_lineSpacingMax: 0\n  m_paragraphSpacing: 0\n  m_charWidthMaxAdj: 0\n  m_enableWordWrapping: 0\n  m_wordWrappingRatios: 0.4\n  m_overflowMode: 0\n  m_linkedTextComponent: {fileID: 0}\n  parentLinkedComponent: {fileID: 0}\n  m_enableKerning: 1\n  m_enableExtraPadding: 1\n  checkPaddingRequired: 0\n  m_isRichText: 1\n  m_parseCtrlCharacters: 1\n  m_isOrthographic: 1\n  m_isCullingEnabled: 0\n  m_horizontalMapping: 0\n  m_verticalMapping: 0\n  m_uvLineOffset: 0\n  m_geometrySortingOrder: 0\n  m_IsTextObjectScaleStatic: 0\n  m_VertexBufferAutoSizeReduction: 0\n  m_useMaxVisibleDescender: 1\n  m_pageToDisplay: 1\n  m_margin: {x: 0, y: 0, z: 0, w: 0}\n  m_isUsingLegacyAnimationComponent: 0\n  m_isVolumetricText: 0\n  m_hasFontAssetChanged: 0\n  m_baseMaterial: {fileID: 0}\n  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}\n--- !u!222 &1862924519\nCanvasRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 1862924516}\n  m_CullTransparentMesh: 1\n--- !u!4 &1947294996 stripped\nTransform:\n  m_CorrespondingSourceObject: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n  m_PrefabInstance: {fileID: 6660377531356033837}\n  m_PrefabAsset: {fileID: 0}\n--- !u!1 &2091779256\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 2091779259}\n  - component: {fileID: 2091779258}\n  - component: {fileID: 2091779257}\n  m_Layer: 0\n  m_Name: EventSystem\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &2091779257\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2091779256}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_SendPointerHoverToParent: 1\n  m_HorizontalAxis: Horizontal\n  m_VerticalAxis: Vertical\n  m_SubmitButton: Submit\n  m_CancelButton: Cancel\n  m_InputActionsPerSecond: 10\n  m_RepeatDelay: 0.5\n  m_ForceModuleActive: 0\n--- !u!114 &2091779258\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2091779256}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_FirstSelected: {fileID: 0}\n  m_sendNavigationEvents: 1\n  m_DragThreshold: 10\n--- !u!4 &2091779259\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2091779256}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!1001 &6660377531356033837\nPrefabInstance:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Modification:\n    serializedVersion: 3\n    m_TransformParent: {fileID: 207278860}\n    m_Modifications:\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.x\n      value: 10\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.y\n      value: 10\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalScale.z\n      value: 10\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.x\n      value: 1000.27747\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.y\n      value: 988.1342\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalPosition.z\n      value: -0.27542418\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.w\n      value: 1\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.x\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.y\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalRotation.z\n      value: -0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.x\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.y\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 2344112196385852409, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_LocalEulerAnglesHint.z\n      value: 0\n      objectReference: {fileID: 0}\n    - target: {fileID: 4748457267955120268, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Name\n      value: StarBackground\n      objectReference: {fileID: 0}\n    - target: {fileID: 6728608069378556597, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: Multiplier\n      value: -0.02\n      objectReference: {fileID: 0}\n    - target: {fileID: 7224407313110299127, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_Color.a\n      value: 0.2509804\n      objectReference: {fileID: 0}\n    - target: {fileID: 7224407313110299127, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n      propertyPath: m_SortingOrder\n      value: -3\n      objectReference: {fileID: 0}\n    m_RemovedComponents: []\n    m_RemovedGameObjects: []\n    m_AddedGameObjects: []\n    m_AddedComponents: []\n  m_SourcePrefab: {fileID: 100100000, guid: c43a86c640b72404996a18c07c897b1e, type: 3}\n--- !u!1660057539 &9223372036854775807\nSceneRoots:\n  m_ObjectHideFlags: 0\n  m_Roots:\n  - {fileID: 519420032}\n  - {fileID: 619394802}\n  - {fileID: 207278860}\n  - {fileID: 1689268676}\n  - {fileID: 2091779259}\n  - {fileID: 372276496}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scenes/Main.unity.meta",
    "content": "fileFormatVersion: 2\nguid: 8c9cfa26abfee488c85f1582747f6a02\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scenes.meta",
    "content": "fileFormatVersion: 2\nguid: fdfb2c5ca4c7f6740bd59d17a4b63ad0\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/BugFixKludge.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\n﻿#if !NET5_0_OR_GREATER\nnamespace System.Runtime.CompilerServices\n{    \n  internal static class IsExternalInit { } // https://stackoverflow.com/a/64749403/1484415\n}\n#endif"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/BugFixKludge.cs.meta",
    "content": "fileFormatVersion: 2\nguid: b9f07428403f147c68fe073fceef6d1d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/CameraController.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing SpacetimeDB.Types;\nusing UnityEngine;\n\npublic class CameraController : MonoBehaviour\n{\n    public static float WorldSize = 0.0f;\n\n\tprivate void LateUpdate()\n    {\n        var arenaCenterTransform = new Vector3(WorldSize / 2, WorldSize / 2, -10.0f);\n        if (PlayerController.Local == null || !GameManager.IsConnected() || PlayerController.Local.NumberOfOwnedCircles == 0)\n        {\n            // Set the camera to be in middle of the arena if we are not connected or \n            // there are no circles to follow\n            transform.position = arenaCenterTransform;\n            return;\n        }\n\n        var centerOfMass = PlayerController.Local.CenterOfMass();\n        if (centerOfMass.HasValue)\n        {\n            // Set the camera to be the center of mass of the local player\n            // if the local player has one\n            transform.position = new Vector3\n            {\n                x = centerOfMass.Value.x,\n                y = centerOfMass.Value.y,\n                z = transform.position.z\n            };\n        } else {\n            transform.position = arenaCenterTransform;\n        }\n\n\t\tfloat targetCameraSize = CalculateCameraSize(PlayerController.Local);\n\t\tCamera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetCameraSize, Time.deltaTime * 2);\n\t}\n\n\tprivate float CalculateCameraSize(PlayerController player)\n\t{\n\t\treturn 50f + //Base size\n            Mathf.Min(50, player.TotalMass() / 5) + //Increase camera size with mass\n            Mathf.Min(player.NumberOfOwnedCircles - 1, 1) * 30; //Zoom out when player splits\n\t}\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/CameraController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ff430cf4f8ef94548b5d934fa322c1fa\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/CircleController.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing SpacetimeDB;\nusing SpacetimeDB.Types;\nusing UnityEngine;\n\npublic class CircleController : EntityController\n{\n\tpublic static Color[] ColorPalette = new[]\n\t{\n        //Yellow\n\t\t(Color)new Color32(175, 159, 49, 255),\n\t\t(Color)new Color32(175, 116, 49, 255),\n        \n        //Purple\n        (Color)new Color32(112, 47, 252, 255),\n\t\t(Color)new Color32(51, 91, 252, 255),\n        \n        //Red\n        (Color)new Color32(176, 54, 54, 255),\n\t\t(Color)new Color32(176, 109, 54, 255),\n\t\t(Color)new Color32(141, 43, 99, 255),\n        \n        //Blue\n        (Color)new Color32(2, 188, 250, 255),\n\t\t(Color)new Color32(7, 50, 251, 255),\n\t\t(Color)new Color32(2, 28, 146, 255),\n\t};\n\n    private PlayerController Owner;\n\n    public void Spawn(Circle circle, PlayerController owner)\n    {\n        base.Spawn(circle.EntityId);\n\t\tSetColor(ColorPalette[circle.PlayerId % ColorPalette.Length]);\n\n        this.Owner = owner;\n        GetComponentInChildren<TMPro.TextMeshProUGUI>().text = owner.Username;\n    }\n\n\tpublic override void OnDelete(EventContext context)\n\t{\n\t\tbase.OnDelete(context);\n        Owner.OnCircleDeleted(this);\n\t}\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/CircleController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: bd6b8a91c00f49b4a9a894af85e2eee3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/DeathScreen.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UI;\n\npublic class DeathScreen : MonoBehaviour\n{\n\tpublic Button RespawnButton;\n\n\tpublic void SetVisible(bool visible)\n\t{\n\t\tgameObject.SetActive(visible);\n\t}\n\n\tpublic void Respawn()\n\t{\n\t\tGameManager.Conn.Reducers.Respawn();\n\t\tSetVisible(false);\n\t}\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/DeathScreen.cs.meta",
    "content": "fileFormatVersion: 2\nguid: cee67cb6465926149ac9948531bf293e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/EntityController.cs",
    "content": "﻿using SpacetimeDB.Types;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Unity.VisualScripting;\nusing UnityEngine;\n\npublic abstract class EntityController : MonoBehaviour\n{\n\tconst float LERP_DURATION_SEC = 0.1f;\n\n\tprivate static readonly int ShaderColorProperty = Shader.PropertyToID(\"_Color\");\n\n\t[DoNotSerialize] public int EntityId;\n\n\tprotected float LerpTime;\n\tprotected Vector3 LerpStartPosition;\n\tprotected Vector3 LerpTargetPosition;\n\tprotected Vector3 TargetScale;\n\n\tprotected virtual void Spawn(int entityId)\n\t{\n\t\tEntityId = entityId;\n\n\t\tvar entity = GameManager.Conn.Db.Entity.EntityId.Find(entityId);\n\t\tLerpStartPosition = LerpTargetPosition = transform.position = (Vector2)entity.Position;\n\t\ttransform.localScale = Vector3.one;\n\t\tTargetScale = MassToScale(entity.Mass);\n\t}\n\n\tpublic void SetColor(Color color)\n\t{\n\t\tGetComponent<SpriteRenderer>().material.SetColor(ShaderColorProperty, color);\n\t}\n\n\tpublic virtual void OnEntityUpdated(Entity newVal)\n\t{\n\t\tLerpTime = 0.0f;\n\t\tLerpStartPosition = transform.position;\n\t\tLerpTargetPosition = (Vector2)newVal.Position;\n\t\tTargetScale = MassToScale(newVal.Mass);\n\t}\n\n\tpublic virtual void OnDelete(EventContext context)\n\t{\n\t\tDestroy(gameObject);\n\t}\n\n\tpublic IEnumerator DespawnCoroutine(Transform targetTransform)\n\t{\n\t\tconst float DESPAWN_TIME = 0.2f;\n\t\tvar startPosition = transform.position;\n\t\tvar startScale = transform.localScale;\n\t\tGetComponent<SpriteRenderer>().sortingOrder++; //Render consumed food above the circle that's consuming it\n\t\tfor (float time = Time.deltaTime; time < DESPAWN_TIME; time += Time.deltaTime)\n\t\t{\n\t\t\tfloat t = time / DESPAWN_TIME;\n\t\t\ttransform.position = Vector3.Lerp(startPosition, targetTransform.position, t);\n\t\t\ttransform.localScale = Vector3.Lerp(startScale, Vector3.zero, t);\n\t\t\tyield return null;\n\t\t}\n\t\tDestroy(gameObject);\n\t}\n\n\tpublic virtual void Update()\n\t{\n\t\t//Interpolate position and scale\n\t\tLerpTime = Mathf.Min(LerpTime + Time.deltaTime, LERP_DURATION_SEC);\n\t\ttransform.position = Vector3.Lerp(LerpStartPosition, LerpTargetPosition, LerpTime / LERP_DURATION_SEC);\n\t\ttransform.localScale = Vector3.Lerp(transform.localScale, TargetScale, Time.deltaTime * 8);\n\t}\n\n\tpublic static Vector3 MassToScale(int mass)\n\t{\n\t\tvar diameter = MassToDiameter(mass);\n\t\treturn new Vector3(diameter, diameter, 1);\n\t}\n\n\tpublic static float MassToRadius(int mass) => Mathf.Sqrt(mass);\n\tpublic static float MassToDiameter(int mass) => MassToRadius(mass) * 2;\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/EntityController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 17be0a009eb4dab49b9c4d85b460628a\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/Extensions.cs",
    "content": "﻿using SpacetimeDB.Types;\nusing UnityEngine;\n\nnamespace SpacetimeDB.Types\n{\n\tpublic partial class DbVector2\n\t{\n\t\tpublic static implicit operator Vector2(DbVector2 vec)\n\t\t{\n\t\t\treturn new Vector2(vec.X, vec.Y);\n\t\t}\n\n\t\tpublic static implicit operator DbVector2(Vector2 vec)\n\t\t{\n\t\t\treturn new DbVector2(vec.x, vec.y);\n\t\t}\n\t}\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/Extensions.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 21d3431f634308c41a724ad839a80157\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/FoodController.cs",
    "content": "using SpacetimeDB.Types;\nusing Unity.VisualScripting;\nusing UnityEngine;\n\npublic class FoodController : EntityController\n{\n\tpublic static Color[] ColorPalette = new[]\n\t{\n\t\t(Color)new Color32(119, 252, 173, 255),\n\t\t(Color)new Color32(76, 250, 146, 255),\n\t\t(Color)new Color32(35, 246, 120, 255),\n\n\t\t(Color)new Color32(119, 251, 201, 255),\n\t\t(Color)new Color32(76, 249, 184, 255),\n\t\t(Color)new Color32(35, 245, 165, 255),\n\t};\n\n    public void Spawn(Food food)\n    {\n        base.Spawn(food.EntityId);\n\t\tSetColor(ColorPalette[EntityId % ColorPalette.Length]);\n    }\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/FoodController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d71b0ceef0f0e2643b88700cfbb4db92\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/GameManager.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing SpacetimeDB;\nusing SpacetimeDB.Types;\nusing UnityEngine;\n\npublic class GameManager : MonoBehaviour\n{\n    const string SERVER_URL = \"http://127.0.0.1:3000\";\n    const string MODULE_NAME = \"blackholio\";\n\n    public static event Action OnConnected;\n    public static event Action OnSubscriptionApplied;\n\n    public SpriteRenderer backgroundInstance;\n    public float borderThickness = 2;\n    public Material borderMaterial;\n    public ParallaxBackground starBackgroundPrefab;\n    public DeathScreen deathScreen;\n\n    public static GameManager Instance { get; private set; }\n    public static Identity LocalIdentity { get; private set; }\n    public static DbConnection Conn { get; private set; }\n\n    public static Dictionary<int, EntityController> Entities = new Dictionary<int, EntityController>();\n    public static Dictionary<int, PlayerController> Players = new Dictionary<int, PlayerController>();\n\n    private static HashSet<int> _pendingConsumeAnimations = new();\n\n    private void Start()\n    {\n        // Clear game state in case we've disconnected and reconnected\n        Entities.Clear();\n        Players.Clear();\n        _pendingConsumeAnimations.Clear();\n        Instance = this;\n        Application.targetFrameRate = 60;\n\n        // In order to build a connection to SpacetimeDB we need to register\n        // our callbacks and specify a SpacetimeDB server URI and module name.\n        var builder = DbConnection.Builder()\n            .OnConnect(HandleConnect)\n            .OnConnectError(HandleConnectError)\n            .OnDisconnect(HandleDisconnect)\n            .WithUri(SERVER_URL)\n            .WithDatabaseName(MODULE_NAME);\n\n        // If the user has a SpacetimeDB auth token stored in the Unity PlayerPrefs,\n        // we can use it to authenticate the connection.\n        // For testing purposes, it is often convenient to comment the following lines out and\n        // export an executable for the project using File -> Build Settings.\n        // Then, you can run the executable multiple times. Since the executable will not check for\n        // a saved auth token, each run of will receive a different Identifier,\n        // and their circles will be able to eat each other.\n        if (AuthToken.Token != \"\")\n        {\n            Debug.Log(\"Using auth token!\");\n            builder = builder.WithToken(AuthToken.Token);\n        }\n\n        // Building the connection will establish a connection to the SpacetimeDB\n        // server.\n        Conn = builder.Build();\n    }\n\n    // Called when we connect to SpacetimeDB and receive our client identity\n    void HandleConnect(DbConnection conn, Identity identity, string token)\n    {\n        Debug.Log(\"Connected.\");\n        AuthToken.SaveToken(token);\n        LocalIdentity = identity;\n\n        conn.Db.Circle.OnInsert += CircleOnInsert;\n        conn.Db.Entity.OnUpdate += EntityOnUpdate;\n        conn.Db.Entity.OnDelete += EntityOnDelete;\n        conn.Db.Food.OnInsert += FoodOnInsert;\n        conn.Db.Player.OnInsert += PlayerOnInsert;\n        conn.Db.Player.OnDelete += PlayerOnDelete;\n        conn.Db.ConsumeEntityEvent.OnInsert += ConsumeEntityEventOnInsert;\n\n        OnConnected?.Invoke();\n\n        // Request all tables\n        Conn.SubscriptionBuilder()\n            .OnApplied(HandleSubscriptionApplied)\n            .SubscribeToAllTables();\n    }\n\n    void HandleConnectError(Exception ex)\n    {\n        Debug.LogError($\"Connection error: {ex}\");\n    }\n\n    void HandleDisconnect(DbConnection _conn, Exception ex)\n    {\n        Debug.Log(\"Disconnected.\");\n        if (ex != null)\n        {\n            Debug.LogException(ex);\n        }\n    }\n\n    private void HandleSubscriptionApplied(SubscriptionEventContext ctx)\n    {\n        Debug.Log(\"Subscription applied!\");\n        OnSubscriptionApplied?.Invoke();\n\n        // Once we have the initial subscription sync'd to the client cache\n        // Get the world size from the config table and set up the arena\n        var worldSize = Conn.Db.Config.Id.Find(0).WorldSize;\n        SetupArena(worldSize);\n\n        // Check to see if we already have a player, if we don't we'll need to create one\n        var player = ctx.Db.Player.Identity.Find(LocalIdentity);\n        if (string.IsNullOrEmpty(player.Name))\n        {\n            // The player has to choose a username\n            UIUsernameChooser.Instance.Show(true);\n\n            // When our username is updated, hide the username chooser\n            Conn.Db.Player.OnUpdate += (_, _, newPlayer) =>\n            {\n                if (newPlayer.Identity == LocalIdentity)\n                {\n                    UIUsernameChooser.Instance.Show(false);\n                }\n            };\n        }\n        else\n        {\n            // We already have a player\n            if (ctx.Db.Circle.PlayerId.Filter(player.PlayerId).Any())\n            {\n                // We already have at least one circle, we should just be able to start\n                // playing immediately.\n                UIUsernameChooser.Instance.Show(false);\n            }\n            else\n            {\n                // Create a new circle for our player.\n                ctx.Reducers.EnterGame(player.Name);\n            }\n        }\n    }\n\n    private void SetupArena(float worldSize)\n    {\n        CreateBorderCube(new Vector2(worldSize / 2.0f, worldSize + borderThickness / 2),\n            new Vector2(worldSize + borderThickness * 2.0f, borderThickness)); //North\n        CreateBorderCube(new Vector2(worldSize / 2.0f, -borderThickness / 2),\n            new Vector2(worldSize + borderThickness * 2.0f, borderThickness)); //South\n        CreateBorderCube(new Vector2(worldSize + borderThickness / 2, worldSize / 2.0f),\n            new Vector2(borderThickness, worldSize + borderThickness * 2.0f)); //East\n        CreateBorderCube(new Vector2(-borderThickness / 2, worldSize / 2.0f),\n            new Vector2(borderThickness, worldSize + borderThickness * 2.0f)); //West\n\n        backgroundInstance.gameObject.SetActive(true); ;\n        var size = worldSize / backgroundInstance.transform.localScale.x;\n        backgroundInstance.size = new Vector2(size, size);\n        backgroundInstance.transform.position = new Vector3((float)worldSize / 2, (float)worldSize / 2);\n\n        // Set the world size for the camera controller\n        CameraController.WorldSize = worldSize;\n    }\n\n    private void CreateBorderCube(Vector2 position, Vector2 scale)\n    {\n        var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);\n        cube.name = \"Border\";\n        cube.transform.localScale = new Vector3(scale.x, scale.y, 1);\n        cube.transform.position = new Vector3(position.x, position.y, 1);\n        cube.GetComponent<MeshRenderer>().material = borderMaterial;\n    }\n\n    private static void CircleOnInsert(EventContext context, Circle insertedValue)\n    {\n        var player = GetOrCreatePlayer(insertedValue.PlayerId);\n        var entityController = PrefabManager.SpawnCircle(insertedValue, player);\n        Entities.Add(insertedValue.EntityId, entityController);\n    }\n\n    private static void EntityOnUpdate(EventContext context, Entity oldEntity, Entity newEntity)\n    {\n        if (!Entities.TryGetValue(newEntity.EntityId, out var entityController))\n        {\n            return;\n        }\n        entityController.OnEntityUpdated(newEntity);\n    }\n\n    private static void EntityOnDelete(EventContext context, Entity oldEntity)\n    {\n        if (Entities.Remove(oldEntity.EntityId, out var entityController))\n        {\n            if (_pendingConsumeAnimations.Remove(oldEntity.EntityId))\n            {\n                // Already being animated by ConsumeEntityEventOnInsert — don't destroy yet\n                return;\n            }\n            entityController.OnDelete(context);\n        }\n    }\n\n    private static void ConsumeEntityEventOnInsert(EventContext context, ConsumeEntityEvent evt)\n    {\n        if (Entities.TryGetValue(evt.ConsumedEntityId, out var consumedEntity) &&\n            Entities.TryGetValue(evt.ConsumerEntityId, out var consumerEntity))\n        {\n            _pendingConsumeAnimations.Add(evt.ConsumedEntityId);\n            consumedEntity.StartCoroutine(consumedEntity.DespawnCoroutine(consumerEntity.transform));\n        }\n    }\n\n    private static void FoodOnInsert(EventContext context, Food insertedValue)\n    {\n        var entityController = PrefabManager.SpawnFood(insertedValue);\n        Entities.Add(insertedValue.EntityId, entityController);\n    }\n\n    private static void PlayerOnInsert(EventContext context, Player insertedPlayer)\n    {\n        GetOrCreatePlayer(insertedPlayer.PlayerId);\n    }\n\n    private static void PlayerOnDelete(EventContext context, Player deletedvalue)\n    {\n        if (Players.Remove(deletedvalue.PlayerId, out var playerController))\n        {\n            GameObject.Destroy(playerController.gameObject);\n        }\n    }\n\n    private static PlayerController GetOrCreatePlayer(int playerId)\n    {\n        if (!Players.TryGetValue(playerId, out var playerController))\n        {\n            var player = Conn.Db.Player.PlayerId.Find(playerId);\n            playerController = PrefabManager.SpawnPlayer(player);\n            Players.Add(playerId, playerController);\n        }\n\n        return playerController;\n    }\n\n    public static bool IsConnected()\n    {\n        return Conn != null && Conn.IsActive;\n    }\n\n    public void Disconnect()\n    {\n        Conn.Disconnect();\n        Conn = null;\n    }\n\n    /* BEGIN: not in tutorial */\n    private void InstanceOnUnhandledReducerError(ReducerEvent<Reducer> reducerEvent)\n    {\n        Debug.LogError($\"There was an error!\\r\\n{(reducerEvent.Status as Status.Failed)?.Failed_}\");\n    }\n    /* END: not in tutorial */\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/GameManager.cs.meta",
    "content": "fileFormatVersion: 2\nguid: aaf311e6a809fdf4d874741c6fd6db10\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/LeaderboardController.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\n\npublic class LeaderboardController : MonoBehaviour\n{\n    const int MAX_ROW_COUNT = 11; //10 + local player\n\n    public LeaderboardRow RowPrefab;\n    public Transform Root;\n\n    private LeaderboardRow[] Rows = new LeaderboardRow[MAX_ROW_COUNT];\n\n    private void Start()\n    {\n        for (var i = 0; i < MAX_ROW_COUNT; i++)\n        {\n            var go = Instantiate(RowPrefab, Root, true);\n            go.gameObject.SetActive(false);\n            Rows[i] = go;\n        }\n    }\n    \n    private void Update()\n    {\n        var players = GameManager.Players.Values\n            .Select(a => (player: a, mass: a.TotalMass()))\n            .Where(a => a.mass > 0)\n            .OrderByDescending(a => a.mass)\n            .Take(10)\n            .ToList();\n        var localPlayer = PlayerController.Local;\n\t\tif (localPlayer != null && !players.Any(p => p.player == localPlayer) && localPlayer.NumberOfOwnedCircles > 0)\n        {\n            players.Add((localPlayer, localPlayer.TotalMass()));\n\t\t}\n\n        int i;\n        for (i = 0; i < players.Count; i++)\n\t\t{\n            var player = players[i];\n\t\t\tvar row = Rows[i];\n            row.SetData(player.player.Username, player.mass);\n            row.gameObject.SetActive(true);\n\t\t}\n        for (; i < MAX_ROW_COUNT; i++)\n\t\t{\n\t\t\tRows[i].gameObject.SetActive(false);\n\t\t}\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/LeaderboardController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 3b62a4a2893f42b4a90869f7c865907d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/LeaderboardRow.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing TMPro;\nusing UnityEngine;\n\npublic class LeaderboardRow : MonoBehaviour\n{\n    public TextMeshProUGUI UsernameText;\n    public TextMeshProUGUI MassText;\n\n    public void SetData(string username, int mass)\n\t{\n\t\tUsernameText.text = username;\n\t\tMassText.text = mass.ToString();\n\t}\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/LeaderboardRow.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1d2b23388701a1a4fa8058aa5cb5416d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/ParallaxBackground.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\npublic class ParallaxBackground : MonoBehaviour\n{\n    public float Multiplier;\n\n\tprivate void LateUpdate()\n\t{\n\t\tvar pos = Camera.main.transform.position * Multiplier;\n\t\tpos.z = 0;\n\t\ttransform.position = pos;\n\t}\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/ParallaxBackground.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 503a1c67365c19942a1a98fba2480609\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/PlayerController.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\nusing SpacetimeDB;\nusing SpacetimeDB.Types;\nusing UnityEngine;\n\npublic class PlayerController : MonoBehaviour\n{\n    const int SEND_UPDATES_PER_SEC = 20;\n    const float SEND_UPDATES_FREQUENCY = 1f / SEND_UPDATES_PER_SEC;\n\n    public static PlayerController Local { get; private set; }\n\n    private Identity PlayerIdentity;\n    private float LastMovementSendTimestamp;\n    private Vector2? LockInputPosition;\n    private List<CircleController> OwnedCircles = new List<CircleController>();\n\n    public string Username => GameManager.Conn.Db.Player.Identity.Find(PlayerIdentity).Name;\n    public int NumberOfOwnedCircles => OwnedCircles.Count;\n    public bool IsLocalPlayer => this == Local;\n\n    public void Initialize(Player player)\n    {\n        PlayerIdentity = player.Identity;\n        if (player.Identity == GameManager.LocalIdentity)\n        {\n            Local = this;\n        }\n    }\n\n    private void OnDestroy()\n    {\n        // If we have any circles, destroy them\n        foreach (var circle in OwnedCircles)\n        {\n            if (circle != null)\n            {\n                Destroy(circle.gameObject);\n            }\n        }\n        OwnedCircles.Clear();\n    }\n\n    public void OnCircleSpawned(CircleController circle)\n    {\n        OwnedCircles.Add(circle);\n    }\n\n    public void OnCircleDeleted(CircleController deletedCircle)\n    {\n        // This means we got eaten\n        if (OwnedCircles.Remove(deletedCircle) && IsLocalPlayer && OwnedCircles.Count == 0)\n        {\n            GameManager.Instance.deathScreen.SetVisible(true);\n        }\n    }\n\n    public int TotalMass()\n    {\n        return (int)OwnedCircles\n            .Select(circle => GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId))\n            .Sum(e => e?.Mass ?? 0); //If this entity is being deleted on the same frame that we're moving, we can have a null entity here.\n    }\n\n    public Vector2? CenterOfMass()\n    {\n        if (OwnedCircles.Count == 0)\n        {\n            return null;\n        }\n\n        Vector2 totalPos = Vector2.zero;\n        float totalMass = 0;\n        foreach (var circle in OwnedCircles)\n        {\n            var entity = GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId);\n            var position = circle.transform.position;\n            totalPos += (Vector2)position * entity.Mass;\n            totalMass += entity.Mass;\n        }\n\n        return totalPos / totalMass;\n    }\n\n    public void Update()\n    {\n        if (!IsLocalPlayer || NumberOfOwnedCircles == 0)\n        {\n            return;\n        }\n\n        if (Input.GetKeyDown(KeyCode.Space))\n        {\n            GameManager.Conn.Reducers.PlayerSplit();\n        }\n\n        if (Input.GetKeyDown(KeyCode.Q))\n        {\n            if (LockInputPosition.HasValue)\n            {\n                LockInputPosition = null;\n            }\n            else\n            {\n                LockInputPosition = (Vector2)Input.mousePosition;\n            }\n        }\n\n        if (Input.GetKeyDown(KeyCode.S))\n        {\n            GameManager.Conn.Reducers.Suicide();\n        }\n\n        // Throttled input requests\n        if (Time.time - LastMovementSendTimestamp >= SEND_UPDATES_FREQUENCY)\n        {\n            LastMovementSendTimestamp = Time.time;\n\n            var mousePosition = LockInputPosition ?? (Vector2)Input.mousePosition;\n            var screenSize = new Vector2\n            {\n                x = Screen.width,\n                y = Screen.height,\n            };\n            var centerOfScreen = screenSize / 2;\n\n            var direction = (mousePosition - centerOfScreen) / (screenSize.y / 3);\n            if (testInputEnabled) { direction = testInput; }\n            GameManager.Conn.Reducers.UpdatePlayerInput(direction);\n        }\n    }\n\n    private void OnGUI()\n    {\n        if (!IsLocalPlayer || !GameManager.IsConnected())\n        {\n            return;\n        }\n\n        GUI.Label(new Rect(0, 0, 100, 50), $\"Total Mass: {TotalMass()}\");\n    }\n\n    //Automated testing members\n    private bool testInputEnabled;\n    private Vector2 testInput;\n\n    public void SetTestInput(Vector2 input) => testInput = input;\n    public void EnableTestInput() => testInputEnabled = true;\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/PlayerController.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 440c11fad1fc390438cf876dea4d202f\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/PrefabManager.cs",
    "content": "using SpacetimeDB.Types;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\npublic class PrefabManager : MonoBehaviour\n{\n\tprivate static PrefabManager Instance;\n\n\tpublic CircleController CirclePrefab;\n\tpublic FoodController FoodPrefab;\n\tpublic PlayerController PlayerPrefab;\n\n\tprivate void Awake()\n\t{\n\t\tInstance = this;\n\t}\n\n\tpublic static CircleController SpawnCircle(Circle circle, PlayerController owner)\n\t{\n\t\tvar entityController = Instantiate(Instance.CirclePrefab);\n\t\tentityController.name = $\"Circle - {circle.EntityId}\";\n\t\tentityController.Spawn(circle, owner);\n\t\towner.OnCircleSpawned(entityController);\n\t\treturn entityController;\n\t}\n\n\tpublic static FoodController SpawnFood(Food food)\n\t{\n\t\tvar entityController = Instantiate(Instance.FoodPrefab);\n\t\tentityController.name = $\"Food - {food.EntityId}\";\n\t\tentityController.Spawn(food);\n\t\treturn entityController;\n\t}\n\n\tpublic static PlayerController SpawnPlayer(Player player)\n\t{\n\t\tvar playerController = Instantiate(Instance.PlayerPrefab);\n\t\tplayerController.name = $\"PlayerController - {player.Name}\";\n\t\tplayerController.Initialize(player);\n\t\treturn playerController;\n\t}\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/PrefabManager.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 032c09cc87224b4428dc40b15ea0dd18\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/SpacetimeDBCircleGame.asmdef",
    "content": "{\n    \"name\": \"Blackholio\",\n    \"rootNamespace\": \"\",\n    \"references\": [\n        \"GUID:51b18aaff8cb8384da535c99b8b52a6b\",\n        \"GUID:6055be8ebefd69e48b49212b09b47b2f\",\n        \"GUID:21b0c8d1703a94250bfac916590cea4f\"\n    ],\n    \"includePlatforms\": [],\n    \"excludePlatforms\": [],\n    \"allowUnsafeCode\": false,\n    \"overrideReferences\": false,\n    \"precompiledReferences\": [],\n    \"autoReferenced\": true,\n    \"defineConstraints\": [],\n    \"versionDefines\": [],\n    \"noEngineReferences\": false\n}"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/SpacetimeDBCircleGame.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: e757229d46a984f5389df800694f5bd6\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/UIUsernameChooser.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing SpacetimeDB.Types;\nusing UnityEngine;\nusing UnityEngine.UI;\n\npublic class UIUsernameChooser : MonoBehaviour\n{\n    public static UIUsernameChooser Instance { get; private set; }\n\n    // The elements that compose the username chooser UI\n    public GameObject[] elements;\n    public TMPro.TMP_InputField UsernameInputField;\n    \n    private void Start()\n    {\n        Instance = this;\n    }\n\n    public void PlayPressed()\n    {\n\t\tDebug.Log(\"Creating player\");\n\n        var name = UsernameInputField.text.Trim();\n        if (string.IsNullOrEmpty(name))\n        {\n            name = \"<No Name>\";\n        }\n\t\tGameManager.Conn.Reducers.EnterGame(name);\n\t\tShow(false);\n\t}\n\n    public void Show(bool showing)\n    {\n        foreach (var element in elements)\n        {\n            element.SetActive(showing);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/UIUsernameChooser.cs.meta",
    "content": "fileFormatVersion: 2\nguid: fdf6b37246fc5824eb3c4e6888c33a4a\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/EnterGame.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void EnterGameHandler(ReducerEventContext ctx, string name);\n        public event EnterGameHandler? OnEnterGame;\n\n        public void EnterGame(string name)\n        {\n            conn.InternalCallReducer(new Reducer.EnterGame(name));\n        }\n\n        public bool InvokeEnterGame(ReducerEventContext ctx, Reducer.EnterGame args)\n        {\n            if (OnEnterGame == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch (ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnEnterGame(\n                ctx,\n                args.Name\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class EnterGame : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"name\")]\n            public string Name;\n\n            public EnterGame(string Name)\n            {\n                this.Name = Name;\n            }\n\n            public EnterGame()\n            {\n                this.Name = \"\";\n            }\n\n            string IReducerArgs.ReducerName => \"enter_game\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/EnterGame.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 753dac3e06e457f4f89e368467112b43\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/PlayerSplit.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void PlayerSplitHandler(ReducerEventContext ctx);\n        public event PlayerSplitHandler? OnPlayerSplit;\n\n        public void PlayerSplit()\n        {\n            conn.InternalCallReducer(new Reducer.PlayerSplit());\n        }\n\n        public bool InvokePlayerSplit(ReducerEventContext ctx, Reducer.PlayerSplit args)\n        {\n            if (OnPlayerSplit == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch (ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnPlayerSplit(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class PlayerSplit : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"player_split\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/PlayerSplit.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: f5673e44453628b43960799b8de06a63\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/Respawn.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void RespawnHandler(ReducerEventContext ctx);\n        public event RespawnHandler? OnRespawn;\n\n        public void Respawn()\n        {\n            conn.InternalCallReducer(new Reducer.Respawn());\n        }\n\n        public bool InvokeRespawn(ReducerEventContext ctx, Reducer.Respawn args)\n        {\n            if (OnRespawn == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch (ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnRespawn(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class Respawn : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"respawn\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/Respawn.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ff4a9d89588ad3d408eb24d4c733dad9\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/Suicide.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void SuicideHandler(ReducerEventContext ctx);\n        public event SuicideHandler? OnSuicide;\n\n        public void Suicide()\n        {\n            conn.InternalCallReducer(new Reducer.Suicide());\n        }\n\n        public bool InvokeSuicide(ReducerEventContext ctx, Reducer.Suicide args)\n        {\n            if (OnSuicide == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch (ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnSuicide(\n                ctx\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class Suicide : Reducer, IReducerArgs\n        {\n            string IReducerArgs.ReducerName => \"suicide\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/Suicide.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: e25861f38a3417846bb43f4c95385faf\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/UpdatePlayerInput.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        public delegate void UpdatePlayerInputHandler(ReducerEventContext ctx, SpacetimeDB.Types.DbVector2 direction);\n        public event UpdatePlayerInputHandler? OnUpdatePlayerInput;\n\n        public void UpdatePlayerInput(SpacetimeDB.Types.DbVector2 direction)\n        {\n            conn.InternalCallReducer(new Reducer.UpdatePlayerInput(direction));\n        }\n\n        public bool InvokeUpdatePlayerInput(ReducerEventContext ctx, Reducer.UpdatePlayerInput args)\n        {\n            if (OnUpdatePlayerInput == null)\n            {\n                if (InternalOnUnhandledReducerError != null)\n                {\n                    switch (ctx.Event.Status)\n                    {\n                        case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break;\n                        case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception(\"out of energy\")); break;\n                    }\n                }\n                return false;\n            }\n            OnUpdatePlayerInput(\n                ctx,\n                args.Direction\n            );\n            return true;\n        }\n    }\n\n    public abstract partial class Reducer\n    {\n        [SpacetimeDB.Type]\n        [DataContract]\n        public sealed partial class UpdatePlayerInput : Reducer, IReducerArgs\n        {\n            [DataMember(Name = \"direction\")]\n            public DbVector2 Direction;\n\n            public UpdatePlayerInput(DbVector2 Direction)\n            {\n                this.Direction = Direction;\n            }\n\n            public UpdatePlayerInput()\n            {\n                this.Direction = new();\n            }\n\n            string IReducerArgs.ReducerName => \"update_player_input\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers/UpdatePlayerInput.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 84a83c7d22be6464fa06057badfeb6ff\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Reducers.meta",
    "content": "fileFormatVersion: 2\nguid: 728104fb70d5f8b4682cc2a46faff61c\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/SpacetimeDBClient.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n// This was generated using spacetimedb cli version 2.0.0 (commit 6a6b5a6616f0578aa641bc0689691f953b13feb8).\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteReducers : RemoteBase\n    {\n        internal RemoteReducers(DbConnection conn) : base(conn) { }\n        internal event Action<ReducerEventContext, Exception>? InternalOnUnhandledReducerError;\n    }\n\n    public sealed partial class RemoteProcedures : RemoteBase\n    {\n        internal RemoteProcedures(DbConnection conn) : base(conn) { }\n    }\n\n    public sealed partial class RemoteTables : RemoteTablesBase\n    {\n        public RemoteTables(DbConnection conn)\n        {\n            AddTable(Circle = new(conn));\n            AddTable(Config = new(conn));\n            AddTable(ConsumeEntityEvent = new(conn));\n            AddTable(Entity = new(conn));\n            AddTable(Food = new(conn));\n            AddTable(Player = new(conn));\n        }\n    }\n\n\n    public interface IRemoteDbContext : IDbContext<RemoteTables, RemoteReducers, SubscriptionBuilder, RemoteProcedures>\n    {\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError;\n    }\n\n    public sealed class EventContext : IEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n\n        /// <summary>\n        /// The event that caused this callback to run.\n        /// </summary>\n        public readonly Event<Reducer> Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect()\n        {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal EventContext(DbConnection conn, Event<Reducer> Event)\n        {\n            this.conn = conn;\n            this.Event = Event;\n        }\n    }\n\n    public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The reducer event that caused this callback to run.\n        /// </summary>\n        public readonly ReducerEvent<Reducer> Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect()\n        {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ReducerEventContext(DbConnection conn, ReducerEvent<Reducer> reducerEvent)\n        {\n            this.conn = conn;\n            Event = reducerEvent;\n        }\n    }\n\n    public sealed class ErrorContext : IErrorContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The <c>Exception</c> that caused this error callback to be run.\n        /// </summary>\n        public readonly Exception Event;\n        Exception IErrorContext.Event\n        {\n            get\n            {\n                return Event;\n            }\n        }\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect()\n        {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ErrorContext(DbConnection conn, Exception error)\n        {\n            this.conn = conn;\n            Event = error;\n        }\n    }\n\n    public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect()\n        {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal SubscriptionEventContext(DbConnection conn)\n        {\n            this.conn = conn;\n        }\n    }\n\n    public sealed class ProcedureEventContext : IProcedureEventContext, IRemoteDbContext\n    {\n        private readonly DbConnection conn;\n        /// <summary>\n        /// The procedure event that caused this callback to run.\n        /// </summary>\n        public readonly ProcedureEvent Event;\n\n        /// <summary>\n        /// Access to tables in the client cache, which stores a read-only replica of the remote database state.\n        ///\n        /// The returned <c>DbView</c> will have a method to access each table defined by the module.\n        /// </summary>\n        public RemoteTables Db => conn.Db;\n        /// <summary>\n        /// Access to reducers defined by the module.\n        ///\n        /// The returned <c>RemoteReducers</c> will have a method to invoke each reducer defined by the module,\n        /// plus methods for adding and removing callbacks on each of those reducers.\n        /// </summary>\n        public RemoteReducers Reducers => conn.Reducers;\n        /// <summary>\n        /// Access to procedures defined by the module.\n        ///\n        /// The returned <c>RemoteProcedures</c> will have a method to invoke each procedure defined by the module,\n        /// with a callback for when the procedure completes and returns a value.\n        /// </summary>\n        public RemoteProcedures Procedures => conn.Procedures;\n        /// <summary>\n        /// Returns <c>true</c> if the connection is active, i.e. has not yet disconnected.\n        /// </summary>\n        public bool IsActive => conn.IsActive;\n        /// <summary>\n        /// Close the connection.\n        ///\n        /// Throws an error if the connection is already closed.\n        /// </summary>\n        public void Disconnect()\n        {\n            conn.Disconnect();\n        }\n        /// <summary>\n        /// Start building a subscription.\n        /// </summary>\n        /// <returns>A builder-pattern constructor for subscribing to queries,\n        /// causing matching rows to be replicated into the client cache.</returns>\n        public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder();\n        /// <summary>\n        /// Get the <c>Identity</c> of this connection.\n        ///\n        /// This method returns null if the connection was constructed anonymously\n        /// and we have not yet received our newly-generated <c>Identity</c> from the host.\n        /// </summary>\n        public Identity? Identity => conn.Identity;\n        /// <summary>\n        /// Get this connection's <c>ConnectionId</c>.\n        /// </summary>\n        public ConnectionId ConnectionId => conn.ConnectionId;\n        /// <summary>\n        /// Register a callback to be called when a reducer with no handler returns an error.\n        /// </summary>\n        public event Action<ReducerEventContext, Exception>? OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n\n        internal ProcedureEventContext(DbConnection conn, ProcedureEvent Event)\n        {\n            this.conn = conn;\n            this.Event = Event;\n        }\n    }\n\n    /// <summary>\n    /// Builder-pattern constructor for subscription queries.\n    /// </summary>\n    public sealed class SubscriptionBuilder\n    {\n        private readonly IDbConnection conn;\n\n        private event Action<SubscriptionEventContext>? Applied;\n        private event Action<ErrorContext, Exception>? Error;\n\n        /// <summary>\n        /// Private API, use <c>conn.SubscriptionBuilder()</c> instead.\n        /// </summary>\n        public SubscriptionBuilder(IDbConnection conn)\n        {\n            this.conn = conn;\n        }\n\n        /// <summary>\n        /// Register a callback to run when the subscription is applied.\n        /// </summary>\n        public SubscriptionBuilder OnApplied(\n            Action<SubscriptionEventContext> callback\n        )\n        {\n            Applied += callback;\n            return this;\n        }\n\n        /// <summary>\n        /// Register a callback to run when the subscription fails.\n        ///\n        /// Note that this callback may run either when attempting to apply the subscription,\n        /// in which case <c>Self::on_applied</c> will never run,\n        /// or later during the subscription's lifetime if the module's interface changes,\n        /// in which case <c>Self::on_applied</c> may have already run.\n        /// </summary>\n        public SubscriptionBuilder OnError(\n            Action<ErrorContext, Exception> callback\n        )\n        {\n            Error += callback;\n            return this;\n        }\n\n        /// <summary>\n        /// Add a typed query to this subscription.\n        ///\n        /// This is the entry point for building subscriptions without writing SQL by hand.\n        /// Once a typed query is added, only typed queries may follow (SQL and typed queries cannot be mixed).\n        /// </summary>\n        public TypedSubscriptionBuilder AddQuery<TRow>(\n            Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build\n        )\n        {\n            var typed = new TypedSubscriptionBuilder(conn, Applied, Error);\n            return typed.AddQuery(build);\n        }\n\n        /// <summary>\n        /// Subscribe to the following SQL queries.\n        ///\n        /// This method returns immediately, with the data not yet added to the DbConnection.\n        /// The provided callbacks will be invoked once the data is returned from the remote server.\n        /// Data from all the provided queries will be returned at the same time.\n        ///\n        /// See the SpacetimeDB SQL docs for more information on SQL syntax:\n        /// <a href=\"https://spacetimedb.com/docs/sql\">https://spacetimedb.com/docs/sql</a>\n        /// </summary>\n        public SubscriptionHandle Subscribe(\n            string[] querySqls\n        ) => new(conn, Applied, Error, querySqls);\n\n        /// <summary>\n        /// Subscribe to all rows from all tables.\n        ///\n        /// This method is intended as a convenience\n        /// for applications where client-side memory use and network bandwidth are not concerns.\n        /// Applications where these resources are a constraint\n        /// should register more precise queries via <c>Self.Subscribe</c>\n        /// in order to replicate only the subset of data which the client needs to function.\n        ///\n        /// This method should not be combined with <c>Self.Subscribe</c> on the same <c>DbConnection</c>.\n        /// A connection may either <c>Self.Subscribe</c> to particular queries,\n        /// or <c>Self.SubscribeToAllTables</c>, but not both.\n        /// Attempting to call <c>Self.Subscribe</c>\n        /// on a <c>DbConnection</c> that has previously used <c>Self.SubscribeToAllTables</c>,\n        /// or vice versa, may misbehave in any number of ways,\n        /// including dropping subscriptions, corrupting the client cache, or panicking.\n        /// </summary>\n        public SubscriptionHandle SubscribeToAllTables() =>\n            new(conn, Applied, Error, QueryBuilder.AllTablesSqlQueries());\n    }\n\n    public sealed class SubscriptionHandle : SubscriptionHandleBase<SubscriptionEventContext, ErrorContext>\n    {\n        /// <summary>\n        /// Internal API. Construct <c>SubscriptionHandle</c>s using <c>conn.SubscriptionBuilder</c>.\n        /// </summary>\n        public SubscriptionHandle(\n            IDbConnection conn,\n            Action<SubscriptionEventContext>? onApplied,\n            Action<ErrorContext, Exception>? onError,\n            string[] querySqls\n        ) : base(conn, onApplied, onError, querySqls)\n        { }\n    }\n\n    public sealed class QueryBuilder\n    {\n        public From From { get; } = new();\n\n        internal static string[] AllTablesSqlQueries() => new string[]\n        {\n            new QueryBuilder().From.Circle().ToSql(),\n            new QueryBuilder().From.Config().ToSql(),\n            new QueryBuilder().From.ConsumeEntityEvent().ToSql(),\n            new QueryBuilder().From.Entity().ToSql(),\n            new QueryBuilder().From.Food().ToSql(),\n            new QueryBuilder().From.Player().ToSql(),\n        }\n        ;\n    }\n\n    public sealed class From\n    {\n        public global::SpacetimeDB.Table<Circle, CircleCols, CircleIxCols> Circle() => new(\"circle\", new CircleCols(\"circle\"), new CircleIxCols(\"circle\"));\n        public global::SpacetimeDB.Table<Config, ConfigCols, ConfigIxCols> Config() => new(\"config\", new ConfigCols(\"config\"), new ConfigIxCols(\"config\"));\n        public global::SpacetimeDB.Table<ConsumeEntityEvent, ConsumeEntityEventCols, ConsumeEntityEventIxCols> ConsumeEntityEvent() => new(\"consume_entity_event\", new ConsumeEntityEventCols(\"consume_entity_event\"), new ConsumeEntityEventIxCols(\"consume_entity_event\"));\n        public global::SpacetimeDB.Table<Entity, EntityCols, EntityIxCols> Entity() => new(\"entity\", new EntityCols(\"entity\"), new EntityIxCols(\"entity\"));\n        public global::SpacetimeDB.Table<Food, FoodCols, FoodIxCols> Food() => new(\"food\", new FoodCols(\"food\"), new FoodIxCols(\"food\"));\n        public global::SpacetimeDB.Table<Player, PlayerCols, PlayerIxCols> Player() => new(\"player\", new PlayerCols(\"player\"), new PlayerIxCols(\"player\"));\n    }\n\n    public sealed class TypedSubscriptionBuilder\n    {\n        private readonly IDbConnection conn;\n        private Action<SubscriptionEventContext>? Applied;\n        private Action<ErrorContext, Exception>? Error;\n        private readonly List<string> querySqls = new();\n\n        internal TypedSubscriptionBuilder(IDbConnection conn, Action<SubscriptionEventContext>? applied, Action<ErrorContext, Exception>? error)\n        {\n            this.conn = conn;\n            Applied = applied;\n            Error = error;\n        }\n\n        public TypedSubscriptionBuilder OnApplied(Action<SubscriptionEventContext> callback)\n        {\n            Applied += callback;\n            return this;\n        }\n\n        public TypedSubscriptionBuilder OnError(Action<ErrorContext, Exception> callback)\n        {\n            Error += callback;\n            return this;\n        }\n\n        public TypedSubscriptionBuilder AddQuery<TRow>(Func<QueryBuilder, global::SpacetimeDB.IQuery<TRow>> build)\n        {\n            var qb = new QueryBuilder();\n            querySqls.Add(build(qb).ToSql());\n            return this;\n        }\n\n        public SubscriptionHandle Subscribe() => new(conn, Applied, Error, querySqls.ToArray());\n    }\n\n    public abstract partial class Reducer\n    {\n        private Reducer() { }\n    }\n\n    public abstract partial class Procedure\n    {\n        private Procedure() { }\n    }\n\n    public sealed class DbConnection : DbConnectionBase<DbConnection, RemoteTables, Reducer>\n    {\n        public override RemoteTables Db { get; }\n        public readonly RemoteReducers Reducers;\n        public readonly RemoteProcedures Procedures;\n\n        public DbConnection()\n        {\n            Db = new(this);\n            Reducers = new(this);\n            Procedures = new(this);\n        }\n\n        protected override IEventContext ToEventContext(Event<Reducer> Event) =>\n        new EventContext(this, Event);\n\n        protected override IReducerEventContext ToReducerEventContext(ReducerEvent<Reducer> reducerEvent) =>\n        new ReducerEventContext(this, reducerEvent);\n\n        protected override ISubscriptionEventContext MakeSubscriptionEventContext() =>\n        new SubscriptionEventContext(this);\n\n        protected override IErrorContext ToErrorContext(Exception exception) =>\n        new ErrorContext(this, exception);\n\n        protected override IProcedureEventContext ToProcedureEventContext(ProcedureEvent procedureEvent) =>\n        new ProcedureEventContext(this, procedureEvent);\n\n        protected override bool Dispatch(IReducerEventContext context, Reducer reducer)\n        {\n            var eventContext = (ReducerEventContext)context;\n            return reducer switch\n            {\n                Reducer.EnterGame args => Reducers.InvokeEnterGame(eventContext, args),\n                Reducer.PlayerSplit args => Reducers.InvokePlayerSplit(eventContext, args),\n                Reducer.Respawn args => Reducers.InvokeRespawn(eventContext, args),\n                Reducer.Suicide args => Reducers.InvokeSuicide(eventContext, args),\n                Reducer.UpdatePlayerInput args => Reducers.InvokeUpdatePlayerInput(eventContext, args),\n                _ => throw new ArgumentOutOfRangeException(\"Reducer\", $\"Unknown reducer {reducer}\")\n            };\n        }\n\n        public SubscriptionBuilder SubscriptionBuilder() => new(this);\n        public event Action<ReducerEventContext, Exception> OnUnhandledReducerError\n        {\n            add => Reducers.InternalOnUnhandledReducerError += value;\n            remove => Reducers.InternalOnUnhandledReducerError -= value;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/SpacetimeDBClient.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 044219e1244dee54dbc3b6582864a327\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Circle.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class CircleHandle : RemoteTableHandle<EventContext, Circle>\n        {\n            protected override string RemoteTableName => \"circle\";\n\n            public sealed class EntityIdUniqueIndex : UniqueIndexBase<int>\n            {\n                protected override int GetKey(Circle row) => row.EntityId;\n\n                public EntityIdUniqueIndex(CircleHandle table) : base(table) { }\n            }\n\n            public readonly EntityIdUniqueIndex EntityId;\n\n            public sealed class PlayerIdIndex : BTreeIndexBase<int>\n            {\n                protected override int GetKey(Circle row) => row.PlayerId;\n\n                public PlayerIdIndex(CircleHandle table) : base(table) { }\n            }\n\n            public readonly PlayerIdIndex PlayerId;\n\n            internal CircleHandle(DbConnection conn) : base(conn)\n            {\n                EntityId = new(this);\n                PlayerId = new(this);\n            }\n\n            protected override object GetPrimaryKey(Circle row) => row.EntityId;\n        }\n\n        public readonly CircleHandle Circle;\n    }\n\n    public sealed class CircleCols\n    {\n        public global::SpacetimeDB.Col<Circle, int> EntityId { get; }\n        public global::SpacetimeDB.Col<Circle, int> PlayerId { get; }\n        public global::SpacetimeDB.Col<Circle, DbVector2> Direction { get; }\n        public global::SpacetimeDB.Col<Circle, float> Speed { get; }\n        public global::SpacetimeDB.Col<Circle, SpacetimeDB.Timestamp> LastSplitTime { get; }\n\n        public CircleCols(string tableName)\n        {\n            EntityId = new global::SpacetimeDB.Col<Circle, int>(tableName, \"entity_id\");\n            PlayerId = new global::SpacetimeDB.Col<Circle, int>(tableName, \"player_id\");\n            Direction = new global::SpacetimeDB.Col<Circle, DbVector2>(tableName, \"direction\");\n            Speed = new global::SpacetimeDB.Col<Circle, float>(tableName, \"speed\");\n            LastSplitTime = new global::SpacetimeDB.Col<Circle, SpacetimeDB.Timestamp>(tableName, \"last_split_time\");\n        }\n    }\n\n    public sealed class CircleIxCols\n    {\n        public global::SpacetimeDB.IxCol<Circle, int> EntityId { get; }\n        public global::SpacetimeDB.IxCol<Circle, int> PlayerId { get; }\n\n        public CircleIxCols(string tableName)\n        {\n            EntityId = new global::SpacetimeDB.IxCol<Circle, int>(tableName, \"entity_id\");\n            PlayerId = new global::SpacetimeDB.IxCol<Circle, int>(tableName, \"player_id\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Circle.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ee250d57a72a334408ea93059c04a190\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Config.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class ConfigHandle : RemoteTableHandle<EventContext, Config>\n        {\n            protected override string RemoteTableName => \"config\";\n\n            public sealed class IdUniqueIndex : UniqueIndexBase<int>\n            {\n                protected override int GetKey(Config row) => row.Id;\n\n                public IdUniqueIndex(ConfigHandle table) : base(table) { }\n            }\n\n            public readonly IdUniqueIndex Id;\n\n            internal ConfigHandle(DbConnection conn) : base(conn)\n            {\n                Id = new(this);\n            }\n\n            protected override object GetPrimaryKey(Config row) => row.Id;\n        }\n\n        public readonly ConfigHandle Config;\n    }\n\n    public sealed class ConfigCols\n    {\n        public global::SpacetimeDB.Col<Config, int> Id { get; }\n        public global::SpacetimeDB.Col<Config, long> WorldSize { get; }\n\n        public ConfigCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.Col<Config, int>(tableName, \"id\");\n            WorldSize = new global::SpacetimeDB.Col<Config, long>(tableName, \"world_size\");\n        }\n    }\n\n    public sealed class ConfigIxCols\n    {\n        public global::SpacetimeDB.IxCol<Config, int> Id { get; }\n\n        public ConfigIxCols(string tableName)\n        {\n            Id = new global::SpacetimeDB.IxCol<Config, int>(tableName, \"id\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Config.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 40a5206f32f562a4da7e196e8582223c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/ConsumeEntityEvent.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class ConsumeEntityEventHandle : RemoteEventTableHandle<EventContext, ConsumeEntityEvent>\n        {\n            protected override string RemoteTableName => \"consume_entity_event\";\n\n            internal ConsumeEntityEventHandle(DbConnection conn) : base(conn)\n            {\n            }\n        }\n\n        public readonly ConsumeEntityEventHandle ConsumeEntityEvent;\n    }\n\n    public sealed class ConsumeEntityEventCols\n    {\n        public global::SpacetimeDB.Col<ConsumeEntityEvent, int> ConsumedEntityId { get; }\n        public global::SpacetimeDB.Col<ConsumeEntityEvent, int> ConsumerEntityId { get; }\n\n        public ConsumeEntityEventCols(string tableName)\n        {\n            ConsumedEntityId = new global::SpacetimeDB.Col<ConsumeEntityEvent, int>(tableName, \"consumed_entity_id\");\n            ConsumerEntityId = new global::SpacetimeDB.Col<ConsumeEntityEvent, int>(tableName, \"consumer_entity_id\");\n        }\n    }\n\n    public sealed class ConsumeEntityEventIxCols\n    {\n\n        public ConsumeEntityEventIxCols(string tableName)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/ConsumeEntityEvent.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 9a493521b6d6175418bcc2067df0e4ad\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Entity.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class EntityHandle : RemoteTableHandle<EventContext, Entity>\n        {\n            protected override string RemoteTableName => \"entity\";\n\n            public sealed class EntityIdUniqueIndex : UniqueIndexBase<int>\n            {\n                protected override int GetKey(Entity row) => row.EntityId;\n\n                public EntityIdUniqueIndex(EntityHandle table) : base(table) { }\n            }\n\n            public readonly EntityIdUniqueIndex EntityId;\n\n            internal EntityHandle(DbConnection conn) : base(conn)\n            {\n                EntityId = new(this);\n            }\n\n            protected override object GetPrimaryKey(Entity row) => row.EntityId;\n        }\n\n        public readonly EntityHandle Entity;\n    }\n\n    public sealed class EntityCols\n    {\n        public global::SpacetimeDB.Col<Entity, int> EntityId { get; }\n        public global::SpacetimeDB.Col<Entity, DbVector2> Position { get; }\n        public global::SpacetimeDB.Col<Entity, int> Mass { get; }\n\n        public EntityCols(string tableName)\n        {\n            EntityId = new global::SpacetimeDB.Col<Entity, int>(tableName, \"entity_id\");\n            Position = new global::SpacetimeDB.Col<Entity, DbVector2>(tableName, \"position\");\n            Mass = new global::SpacetimeDB.Col<Entity, int>(tableName, \"mass\");\n        }\n    }\n\n    public sealed class EntityIxCols\n    {\n        public global::SpacetimeDB.IxCol<Entity, int> EntityId { get; }\n\n        public EntityIxCols(string tableName)\n        {\n            EntityId = new global::SpacetimeDB.IxCol<Entity, int>(tableName, \"entity_id\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Entity.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a577fbc121ab89c47a4f686588da2f65\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Food.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 41905702f930d204795b4c0eb44e00c1\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Player.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing SpacetimeDB.BSATN;\nusing SpacetimeDB.ClientApi;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    public sealed partial class RemoteTables\n    {\n        public sealed class PlayerHandle : RemoteTableHandle<EventContext, Player>\n        {\n            protected override string RemoteTableName => \"player\";\n\n            public sealed class IdentityUniqueIndex : UniqueIndexBase<SpacetimeDB.Identity>\n            {\n                protected override SpacetimeDB.Identity GetKey(Player row) => row.Identity;\n\n                public IdentityUniqueIndex(PlayerHandle table) : base(table) { }\n            }\n\n            public readonly IdentityUniqueIndex Identity;\n\n            public sealed class PlayerIdUniqueIndex : UniqueIndexBase<int>\n            {\n                protected override int GetKey(Player row) => row.PlayerId;\n\n                public PlayerIdUniqueIndex(PlayerHandle table) : base(table) { }\n            }\n\n            public readonly PlayerIdUniqueIndex PlayerId;\n\n            internal PlayerHandle(DbConnection conn) : base(conn)\n            {\n                Identity = new(this);\n                PlayerId = new(this);\n            }\n\n            protected override object GetPrimaryKey(Player row) => row.Identity;\n        }\n\n        public readonly PlayerHandle Player;\n    }\n\n    public sealed class PlayerCols\n    {\n        public global::SpacetimeDB.Col<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.Col<Player, int> PlayerId { get; }\n        public global::SpacetimeDB.Col<Player, string> Name { get; }\n\n        public PlayerCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.Col<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.Col<Player, int>(tableName, \"player_id\");\n            Name = new global::SpacetimeDB.Col<Player, string>(tableName, \"name\");\n        }\n    }\n\n    public sealed class PlayerIxCols\n    {\n        public global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity> Identity { get; }\n        public global::SpacetimeDB.IxCol<Player, int> PlayerId { get; }\n\n        public PlayerIxCols(string tableName)\n        {\n            Identity = new global::SpacetimeDB.IxCol<Player, SpacetimeDB.Identity>(tableName, \"identity\");\n            PlayerId = new global::SpacetimeDB.IxCol<Player, int>(tableName, \"player_id\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables/Player.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ff139c87f2c13ca42895460358f1b42d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Tables.meta",
    "content": "fileFormatVersion: 2\nguid: 1891d719ef078b9408c4b4db0ec2909a\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Circle.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Circle\n    {\n        [DataMember(Name = \"entity_id\")]\n        public int EntityId;\n        [DataMember(Name = \"player_id\")]\n        public int PlayerId;\n        [DataMember(Name = \"direction\")]\n        public DbVector2 Direction;\n        [DataMember(Name = \"speed\")]\n        public float Speed;\n        [DataMember(Name = \"last_split_time\")]\n        public SpacetimeDB.Timestamp LastSplitTime;\n\n        public Circle(\n            int EntityId,\n            int PlayerId,\n            DbVector2 Direction,\n            float Speed,\n            SpacetimeDB.Timestamp LastSplitTime\n        )\n        {\n            this.EntityId = EntityId;\n            this.PlayerId = PlayerId;\n            this.Direction = Direction;\n            this.Speed = Speed;\n            this.LastSplitTime = LastSplitTime;\n        }\n\n        public Circle()\n        {\n            this.Direction = new();\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Circle.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d116ffb1a4e1b434bbfa9f3198d6440e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/CircleDecayTimer.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class CircleDecayTimer\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n\n        public CircleDecayTimer(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n        }\n\n        public CircleDecayTimer()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/CircleDecayTimer.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: daf193bc44824e44fb8140aa8d008aed\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/CircleRecombineTimer.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class CircleRecombineTimer\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n        [DataMember(Name = \"player_id\")]\n        public int PlayerId;\n\n        public CircleRecombineTimer(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt,\n            int PlayerId\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n            this.PlayerId = PlayerId;\n        }\n\n        public CircleRecombineTimer()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/CircleRecombineTimer.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a329f09d2a3630740b3fe751742afe73\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Config.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Config\n    {\n        [DataMember(Name = \"id\")]\n        public int Id;\n        [DataMember(Name = \"world_size\")]\n        public long WorldSize;\n\n        public Config(\n            int Id,\n            long WorldSize\n        )\n        {\n            this.Id = Id;\n            this.WorldSize = WorldSize;\n        }\n\n        public Config()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Config.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 99fd9b7bb5c144f409bc6fa3c0f3d63f\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/ConsumeEntityEvent.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class ConsumeEntityEvent\n    {\n        [DataMember(Name = \"consumed_entity_id\")]\n        public int ConsumedEntityId;\n        [DataMember(Name = \"consumer_entity_id\")]\n        public int ConsumerEntityId;\n\n        public ConsumeEntityEvent(\n            int ConsumedEntityId,\n            int ConsumerEntityId\n        )\n        {\n            this.ConsumedEntityId = ConsumedEntityId;\n            this.ConsumerEntityId = ConsumerEntityId;\n        }\n\n        public ConsumeEntityEvent()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/ConsumeEntityEvent.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a89671ae0e12ca84bb48321262377eab\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/ConsumeEntityTimer.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class ConsumeEntityTimer\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n        [DataMember(Name = \"consumed_entity_id\")]\n        public int ConsumedEntityId;\n        [DataMember(Name = \"consumer_entity_id\")]\n        public int ConsumerEntityId;\n\n        public ConsumeEntityTimer(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt,\n            int ConsumedEntityId,\n            int ConsumerEntityId\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n            this.ConsumedEntityId = ConsumedEntityId;\n            this.ConsumerEntityId = ConsumerEntityId;\n        }\n\n        public ConsumeEntityTimer()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/ConsumeEntityTimer.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 95d29aefe64ca884ba420e80775174f4\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/DbVector2.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class DbVector2\n    {\n        [DataMember(Name = \"x\")]\n        public float X;\n        [DataMember(Name = \"y\")]\n        public float Y;\n\n        public DbVector2(\n            float X,\n            float Y\n        )\n        {\n            this.X = X;\n            this.Y = Y;\n        }\n\n        public DbVector2()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/DbVector2.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a0f5e34aabd273c46a1860196ed36ee4\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Entity.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Entity\n    {\n        [DataMember(Name = \"entity_id\")]\n        public int EntityId;\n        [DataMember(Name = \"position\")]\n        public DbVector2 Position;\n        [DataMember(Name = \"mass\")]\n        public int Mass;\n\n        public Entity(\n            int EntityId,\n            DbVector2 Position,\n            int Mass\n        )\n        {\n            this.EntityId = EntityId;\n            this.Position = Position;\n            this.Mass = Mass;\n        }\n\n        public Entity()\n        {\n            this.Position = new();\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Entity.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 713febff14985604a87d74fe57c9d01d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Food.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Food\n    {\n        [DataMember(Name = \"entity_id\")]\n        public int EntityId;\n\n        public Food(int EntityId)\n        {\n            this.EntityId = EntityId;\n        }\n\n        public Food()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Food.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5da33bc3f5b32484cb3b5c9341667eb6\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/MoveAllPlayersTimer.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class MoveAllPlayersTimer\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n\n        public MoveAllPlayersTimer(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n        }\n\n        public MoveAllPlayersTimer()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/MoveAllPlayersTimer.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 014213337c70370449739fadb6acae76\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Player.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class Player\n    {\n        [DataMember(Name = \"identity\")]\n        public SpacetimeDB.Identity Identity;\n        [DataMember(Name = \"player_id\")]\n        public int PlayerId;\n        [DataMember(Name = \"name\")]\n        public string Name;\n\n        public Player(\n            SpacetimeDB.Identity Identity,\n            int PlayerId,\n            string Name\n        )\n        {\n            this.Identity = Identity;\n            this.PlayerId = PlayerId;\n            this.Name = Name;\n        }\n\n        public Player()\n        {\n            this.Name = \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/Player.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1ea32ba54eb81464eaa0e2140a01789d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/SpawnFoodTimer.g.cs",
    "content": "// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE\n// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.\n\n#nullable enable\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\n\nnamespace SpacetimeDB.Types\n{\n    [SpacetimeDB.Type]\n    [DataContract]\n    public sealed partial class SpawnFoodTimer\n    {\n        [DataMember(Name = \"scheduled_id\")]\n        public ulong ScheduledId;\n        [DataMember(Name = \"scheduled_at\")]\n        public SpacetimeDB.ScheduleAt ScheduledAt;\n\n        public SpawnFoodTimer(\n            ulong ScheduledId,\n            SpacetimeDB.ScheduleAt ScheduledAt\n        )\n        {\n            this.ScheduledId = ScheduledId;\n            this.ScheduledAt = ScheduledAt;\n        }\n\n        public SpawnFoodTimer()\n        {\n            this.ScheduledAt = null!;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types/SpawnFoodTimer.g.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4bb9f9501638693478a48f0b4cbd8c43\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen/Types.meta",
    "content": "fileFormatVersion: 2\nguid: 82c0165431fa03a4d89eb7dfcf451f4c\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Scripts/autogen.meta",
    "content": "fileFormatVersion: 2\nguid: 790f4167c42880d42a16c8e54d496e68\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Lit2DSceneTemplate.scenetemplate",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!28 &-3604256930052969394\nTexture2D:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_Name: Lit 2D\n  m_ImageContentsHash:\n    serializedVersion: 2\n    Hash: 3eb422a44b812e16b34078a6083bea48\n  m_ForcedFallbackFormat: 4\n  m_DownscaleFallback: 0\n  m_IsAlphaChannelOptional: 1\n  serializedVersion: 2\n  m_Width: 1068\n  m_Height: 615\n  m_CompleteImageSize: 1970460\n  m_MipsStripped: 0\n  m_TextureFormat: 3\n  m_MipCount: 1\n  m_IsReadable: 1\n  m_IsPreProcessed: 0\n  m_IgnoreMasterTextureLimit: 0\n  m_StreamingMipmaps: 0\n  m_StreamingMipmapsPriority: 0\n  m_VTOnly: 0\n  m_AlphaIsTransparency: 1\n  m_ImageCount: 1\n  m_TextureDimension: 2\n  m_TextureSettings:\n    serializedVersion: 2\n    m_FilterMode: 1\n    m_Aniso: 1\n    m_MipBias: 0\n    m_WrapU: 1\n    m_WrapV: 1\n    m_WrapW: 1\n  m_LightmapFormat: 6\n  m_ColorSpace: 1\n  m_PlatformBlob: \n  image data: 1970460\n  _typelessdata: 4646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454444444444444444444444444444444444444444444545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444444444343434343434343434343434343434343434343434343434444444444444444444444444545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444444444343434343434343434242424242424242424141414141414141414141414242424242424242424242424343434343434343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434343434242424242424141414141414040404040404040404040403f3f3f3f3f3f3f3f3f4040404040404040404040404141414141414242424343434343434444444444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434343434242424141414141414040403f3f3f3f3f3f3e3e3e3e3e3e3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3e3e3e3e3e3e3f3f3f3f3f3f4040404141414242424242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424141414040403f3f3f3f3f3f3e3e3e3d3d3d3c3c3c3c3c3c3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3b3b3b3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3f3f3f4040404141414141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3a3a3a3939393939393838383838383838383838383939393838383838383838383838383939393a3a3a3a3a3a3b3b3b3c3c3c3d3d3d3f3f3f4040404141414242424343434343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141414040403e3e3e3d3d3d3c3c3c3b3b3b3939393838383737373a3a3a4343434949494f4f4f6060606c6c6c7373736969695c5c5c4c4c4c4747473f3f3f3838383737373939393a3a3a3b3b3b3c3c3c3d3d3d3f3f3f4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424040403f3f3f3e3e3e3c3c3c3b3b3b3939393838383737374343436666668e8e8ebfbfbfe3e3e3f8f8f8fffffffffffffffffffffffffefefef3f3f3dadadab1b1b17f7f7f5a5a5a3d3d3d3838383838383a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403f3f3f3d3d3d3b3b3b3a3a3a383838393939494949868686cbcbcbeeeeeefdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfbe6e6e6b7b7b76d6d6d4141413737373939393a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444444444444444444444444444444444444444343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434444444444444444444444444444444444444444444545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403e3e3e3d3d3d3b3b3b3939393737374343437e7e7ed6d6d6fafafafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef5f5f5bfbfbf6666663b3b3b3838383a3a3a3b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444444444444444343434343434343434343434343434242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424343434343434343434343434444444444444444444444444545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3a3a3a383838383838585858b5b5b5f4f4f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffebebeb9898984747473838383939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444343434343434343434242424242424242424141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414242424242424242424343434343434343434444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3a3a3a3838383b3b3b6e6e6edbdbdbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f9bdbdbd5454543838383939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444444444343434343434242424242424141414141414141414040404040404040404040403f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4040404040404040404040404141414141414141414242424242424343434343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3d3d3d3b3b3b3838383c3c3c7d7d7de8e8e8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdd3d3d35c5c5c3939393939393b3b3b3d3d3d3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444444444343434242424242424141414141414040404040403f3f3f3f3f3f3e3e3e3e3e3e3e3e3e3e3e3e3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3e3e3e3e3e3e3e3e3e3e3e3e3f3f3f3f3f3f3f3f3f4040404141414141414242424242424343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3b3b3b3939393b3b3b7a7a7af1f1f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefed1d1d15959593838383939393c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434343434242424141414141414040403f3f3f3e3e3e3d3d3d3d3d3d3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3c3c3c3c3c3c3c3c3c3d3d3d3e3e3e3e3e3e3f3f3f4040404141414141414242424343434343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c393939373737656565e5e5e5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefec7c7c74a4a4a3737373a3a3a3c3c3c3f3f3f4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3a3a3a3a3a3a3939393939393838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383939393939393a3a3a3a3a3a3b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4040404141414242424343434343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3c3c3c3a3a3a383838505050d1d1d1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfca3a3a34040403838383b3b3b3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424141413f3f3f3e3e3e3d3d3d3b3b3b3a3a3a3939393838383737373636363535353535353535353434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343434343535353535353636363737373838383939393a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141414040403d3d3d3b3b3b3838383d3d3daaaaaafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f87979793838383939393c3c3c3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141413f3f3f3d3d3d3c3c3c3a3a3a3939393737373636363434343333333232323131313131313030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303131313131313232323333333434343636363737373939393a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3c3c3c3a3a3a3737376a6a6aedededffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcf4e4e4e3838383a3a3a3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141413f3f3f3d3d3d3b3b3b3939393737373636363434343232323030302f2f2f2e2e2e2d2d2d2c2c2c2c2c2c2c2c2c2c2c2c2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2c2c2c2c2c2c2c2c2c2d2d2d2d2d2d2e2e2e2f2f2f3030303232323434343636363737373a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b383838424242bcbcbcfefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc8686863939393939393c3c3c3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141414040403e3e3e3b3b3b3939393737373434343232323030302e2e2e2c2c2c2a2a2a2929292929292828282727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272727272828282929292929292b2b2b2c2c2c2e2e2e3030303232323434343737373939393c3c3c3e3e3e4040404141414343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a383838696969f4f4f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7d7d74848483838383b3b3b3d3d3d4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3939393737373434343131312e2e2e2c2c2c2929292828282626262525252424242323232323232222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222323232323232424242525252626262828282a2a2a2c2c2c2e2e2e3131313434343737373939393c3c3c3e3e3e4040404242424343434444444545454545454545454646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3c3c3c3939393c3c3cadadadfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f87c7c7c3737373a3a3a3c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3d3d3d3a3a3a3737373434343030302d2d2d2a2a2a2727272525253030304343434d4d4d5353535252525252525353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535353535252525353535252524d4d4d4242422e2e2e2525252828282a2a2a2d2d2d3131313434343737373a3a3a3d3d3d3f3f3f4141414242424444444444444545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b3838384a4a4ae3e3e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3b3b33c3c3c3939393c3c3c3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b3838383434343131312d2d2d2929292a2a2a4747478b8b8bbcbcbcd5d5d5dfdfdfe5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5dfdfdfd6d6d6b9b9b98484844141412c2c2c2a2a2a2d2d2d3131313434343838383b3b3b3e3e3e4040404242424343434444444545454545454545454545454545454545454444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737737373f5f5f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdadada5151513838383b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3c3c3c3939393535353232322d2d2d2a2a2a3636367f7f7fddddddf8f8f8fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcf5f5f5d8d8d87d7d7d3131312a2a2a2e2e2e3232323636363939393c3c3c3f3f3f4141414242424343434444444444444444444444444444444444444444444444444343434343434343434343434343434343434343434343434343434343434343434343434343434444444444444444444444444545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c3939393636369c9c9cfbfbfbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f3f36565653737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b3737373333332f2f2f2a2a2a3c3c3c9f9f9ff5f5f5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf2f2f29a9a9a3535352b2b2b2f2f2f3333333737373b3b3b3e3e3e4040404242424343434343434444444444444444444343434343434343434242424242424242424242424242424242424141414141414242424242424242424242424242424242424343434343434343434444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403e3e3e3c3c3c3939393b3b3bbababafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefe7c7c7c3838383a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403d3d3d3939393636363131312c2c2c333333989898f7f7f7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf6f6f69090903131312d2d2d3131313535353939393c3c3c3f3f3f4141414242424343434343434343434343434242424242424242424141414141414141414040404040404040404040404040404040404040404040404141414141414141414242424242424242424343434343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b383838484848d1d1d1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c9c9c3c3c3c3939393c3c3c3f3f3f4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413f3f3f3c3c3c3838383434342f2f2f2d2d2d6c6c6cf3f3f3fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdebebeb6969692b2b2b2f2f2f3333333838383b3b3b3e3e3e4040404141414242424242424242424242424141414141414040404040403f3f3f3f3f3f3f3f3f3f3f3f3e3e3e3e3e3e3e3e3e3e3e3e3f3f3f3f3f3f3f3f3f4040404040404040404141414141414242424343434343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b383838515151dbdbdbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0b0b03e3e3e3939393c3c3c3e3e3e4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413e3e3e3b3b3b3737373232322d2d2d393939cbcbcbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcbababa3a3a3a2d2d2d3232323636363a3a3a3d3d3d3f3f3f4040404141414141414141414040404040403f3f3f3e3e3e3e3e3e3d3d3d3d3d3d3d3d3d3d3d3d3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3d3d3d3d3d3d3e3e3e3e3e3e3f3f3f3f3f3f4040404141414242424343434343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b373737555555e1e1e1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababa3f3f3f3939393b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434040403d3d3d3a3a3a3636363131312c2c2c686868f2f2f2fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdededed5454542b2b2b3030303535353838383b3b3b3d3d3d3f3f3f3f3f3f3f3f3f3f3f3f3e3e3e3d3d3d3d3d3d3c3c3c3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3b3b3b3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3f3f3f4040404141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141414040403d3d3d3a3a3a373737595959e5e5e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403d3d3d3939393434342f2f2f2b2b2b999999f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd8484842e2e2e2e2e2e3333333737373a3a3a3c3c3c3d3d3d3e3e3e3d3d3d3d3d3d3c3c3c3b3b3b3a3a3a3939393838383838383737373737373636363636363636363636363636363636363737373737373838383939393939393b3b3b3c3c3c3d3d3d3f3f3f4040404141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444242423f3f3f3c3c3c3838383333332e2e2e2c2c2cb4b4b4fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfda9a9a93030302d2d2d3131313535353838383a3a3a3b3b3b3c3c3c3b3b3b3a3a3a3939393838383737373636363535353434343333333333333232323232323232323232323232323232323333333333333434343535353636363838383939393b3b3b3d3d3d3f3f3f4040404141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444343434141413f3f3f3b3b3b3737373232322d2d2d303030c0c0c0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdb7b7b73030302b2b2b3030303434343636363838383939393939393939393838383636363535353333333232323131313030302f2f2f2e2e2e2e2e2e2e2e2e2d2d2d2d2d2d2e2e2e2e2e2e2f2f2f2f2f2f3030303131313333333535353636363838383b3b3b3d3d3d3f3f3f4141414242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444343434242424040403e3e3e3a3a3a3636363131312c2c2c323232c3c3c3fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2f2f2f2a2a2a2f2f2f3232323535353636363737373737373636363434343333333131312f2f2f2e2e2e2c2c2c2b2b2b2b2b2b2a2a2a2929292929292929292929292929292a2a2a2a2a2a2b2b2b2c2c2c2d2d2d2f2f2f3131313333333636363939393b3b3b3d3d3d4040404141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444444444444444343434242424141413f3f3f3d3d3d3939393535353030302b2b2b323232c3c3c3fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2e2e2e2929292d2d2d3030303333333434343434343434343333333131312f2f2f2d2d2d2b2b2b2929292828282727272626262525252525252525252424242424242525252525252626262727272828282929292b2b2b2e2e2e3030303333333636363939393c3c3c3e3e3e4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444444444343434343434343434242424141414040403e3e3e3b3b3b3838383434342f2f2f2a2a2a313131c4c4c4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2e2e2e2828282b2b2b2e2e2e3030303131313131313030302f2f2f2d2d2d2a2a2a2828282626262525252323232222222121212121212020202020202020202020202020202020202121212222222323232525252727272a2a2a2d2d2d3030303434343737373a3a3a3d3d3d4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375a5a5ae6e6e6fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcfafafaf4f4f4e5e5e5d1d1d1c0c0c0b7b7b7afafafacacacb1b1b1b9b9b9c5c5c5d8d8d8ebebebf5f5f5fafafafcfcfcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc3f3f3f3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444343434343434242424242424141414141414040403f3f3f3d3d3d3a3a3a3737373333332e2e2e292929313131c4c4c4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2d2d2d2626262a2a2a2c2c2c2e2e2e2e2e2e2e2e2e2d2d2d2b2b2b282828262626262626585858a0a0a0aaaaaaaaaaaaabababababababababababababababababababababababababababaaaaaaaaaaaaa9a9a9a8a8a8a7a7a7a5a5a57575753333333535353939393c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3838385a5a5ae6e6e6fffffffffffffffffffffffffffffffffffffffffffffffffefefef4f4f4dededebcbcbc9898987474745151514343434141413f3f3f3e3e3e3d3d3d3d3d3d3d3d3d3e3e3e3f3f3f4242424545455a5a5a7f7f7fa3a3a3c8c8c8e6e6e6fafafaffffffffffffffffffffffffffffffffffffffffffffffffffffffbcbcbc4040403939393b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444343434343434242424242424141414141414040403f3f3f3e3e3e3d3d3d3b3b3b3939393535353232322d2d2d282828303030c4c4c4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2c2c2c2424242727272a2a2a2b2b2b2b2b2b2a2a2a2929292626262424242f2f2f717171e2e2e2fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad3232323333333737373b3b3b3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b3838385a5a5ae6e6e6fffffffffffffffffffffffffffffffffffffcfcfce3e3e3a6a6a67070705353533a3a3a3535353535353535353636363636363636363636363636363636363636363636363636363636363535353535353535353535353f3f3f5b5b5b7c7c7cbbbbbbf0f0f0fefefeffffffffffffffffffffffffffffffffffffbcbcbc4040403939393c3c3c3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434343434242424141414141414040404040403f3f3f3e3e3e3d3d3d3d3d3d3b3b3b3939393737373434343030302c2c2c2727272f2f2fc4c4c4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2b2b2b222222252525272727272727272727262626252525222222383838939393efefeffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac3030303232323636363a3a3a3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b3939395a5a5ae6e6e6fffffffffffffffffffefefef3f3f3c9c9c97c7c7c4b4b4b3c3c3c353535353535363636363636373737373737383838383838383838383838383838383838383838383838383838383838373737373737373737363636353535353535353535404040555555979797d8d8d8f8f8f8ffffffffffffffffffffffffbcbcbc4040403a3a3a3c3c3c3f3f3f4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434343434242424242424141414040403f3f3f3e3e3e3d3d3d3d3d3d3c3c3c3b3b3b3a3a3a3939393737373434343131312e2e2e2a2a2a2525252e2e2ec4c4c4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2a2a2a2020202323232424242424242424242222222525254b4b4bb9b9b9f7f7f7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac2f2f2f3030303535353939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3c3c3c3a3a3a5a5a5ae6e6e6fffffffffffff8f8f8bababa7171714141413535353535353535353636363737373737373838383838383939393939393939393939393939393a3a3a3a3a3a3a3a3a3939393939393939393939393939393838383838383737373737373636363535353535353636364c4c4c848484d3d3d3fefefeffffffffffffbcbcbc4141413b3b3b3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434242424242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3a3a3a3939393838383737373636363434343131312e2e2e2b2b2b2727272323232d2d2dc5c5c5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2929291e1e1e202020202020212121202020272727616161d3d3d3fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2d2d2d2f2f2f3434343939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3d3d3d3b3b3b5a5a5ae6e6e6fcfcfccecece7070704242423535353434343535353636363737373737373838383939393939393939393a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3939393939393838383838383737373737373636363535353434343737374c4c4c919191e2e2e2fefefebdbdbd4242423b3b3b3e3e3e4040404141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3939393838383737373636363535353333333232323030302e2e2e2b2b2b2828282525252121212c2c2cc5c5c5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2929291c1c1c1d1d1d1d1d1d1e1e1e2f2f2f878787e7e7e7fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2d2d2d2e2e2e3434343838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3e3e3e3b3b3b5a5a5ad1d1d19393934a4a4a3737373535353535353636363636363737373838383838383939393939393939393939393a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a393939393939393939393939383838383838373737363636363636353535353535393939585858afafafb7b7b74242423c3c3c3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434242424141414040403f3f3f3d3d3d3c3c3c3b3b3b3939393838383636363535353333333232323131312f2f2f2e2e2e2c2c2c2a2a2a2727272525252222221f1f1f2b2b2bc5c5c5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2929291c1c1c1c1c1c1e1e1e3d3d3da4a4a4f3f3f3fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2e2e2e3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3c3c3c4b4b4b5b5b5b3e3e3e3737373737373636363636363737373737373838383838383838383939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393939393838383838383838383737373737373737373636363636363737373737374343435e5e5e3f3f3f3d3d3d3f3f3f4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444343434343434242424141413f3f3f3e3e3e3d3d3d3b3b3b3a3a3a3838383636363535353333333131312f2f2f2e2e2e2c2c2c2b2b2b2929292828282626262323232121211f1f1f1d1d1d2a2a2ac5c5c5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2828281b1b1b202020585858c7c7c7fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3d3d3d3c3c3c3a3a3a3838383838383737373737373737373737373737373737373737373838383838383838383838383737373737373737373737373737373737373737373737373737373737373737373737373737373737373838383838383838383838383838383737373737373737373737373737373737373737373838383939393b3b3b3c3c3c3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434343434242424040403f3f3f3e3e3e3c3c3c3a3a3a3838383737373535353333333131312f2f2f2d2d2d2b2b2b292929282828262626252525232323222222202020212121202020202020313131c7c7c7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb282828252525747474dbdbdbfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3a3a3a3939393838383737373737373636363636363636363636363636363636363636363636363636363636363737373737373737373737373737373737373838383737373737373737373737373737373737373636363636363636363636363636363636363636363636363636363737373737373737373838383939393a3a3a3c3c3c3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424040403f3f3f3d3d3d3c3c3c3a3a3a3838383636363333333131312f2f2f2d2d2d2a2a2a2828282727272525252323232323232e2e2e3b3b3b4343434f4f4f6767677070707a7a7a959595e5e5e5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbdbdbd464646909090f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424040403f3f3f3d3d3d3c3c3c3b3b3b3939393838383737373636363636363535353535353535353535353535353939394040404646464a4a4a5b5b5b767676898989989898a4a4a4acacacb0b0b0b3b3b3aeaeaeaaaaaaa1a1a19393938585856e6e6e5454544848484444443e3e3e3737373434343535353535353535353636363636363737373737373838383a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424040403f3f3f3d3d3d3b3b3b3939393737373535353232322f2f2f2d2d2d2a2a2a2828282626262424242424243030305858587c7c7ca8a8a8cdcdcde4e4e4f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfddfdfdfc5c5c5f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424141413f3f3f3e3e3e3c3c3c3b3b3b3939393838383737373636363535353434343434343434344141415b5b5b727272959595bbbbbbd9d9d9f2f2f2fcfcfcfdfdfdfdfdfdfefefefefefefefefefefefefefefefefefefefefefefefefefefefdfdfdfdfdfdfafafaecececd1d1d1b0b0b08a8a8a6b6b6b5252523a3a3a3434343434343535353535353636363737373838383a3a3a3b3b3b3d3d3d3e3e3e4040404141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424040403f3f3f3d3d3d3b3b3b3939393636363434343131312e2e2e2c2c2c2929292626262424242a2a2a434343838383c2c2c2e6e6e6f7f7f7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfbfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403f3f3f3e3e3e3c3c3c3b3b3b3939393838383636363535353535353a3a3a4444446c6c6ca1a1a1cacacae5e5e5f7f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdf2f2f2dfdfdfc0c0c09090905b5b5b4040403838383535353636363737373838383a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141413f3f3f3d3d3d3b3b3b3838383636363333333030302d2d2d2a2a2a272727242424292929505050949494e2e2e2f9f9f9fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424141414040403f3f3f3d3d3d3c3c3c3a3a3a3939393737373636363636364444446666669f9f9fdbdbdbf7f7f7fdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefefbfbfbf2f2f2cdcdcd8d8d8d5a5a5a3c3c3c3535353636363838383939393b3b3b3c3c3c3e3e3e3f3f3f4040404141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141413f3f3f3d3d3d3b3b3b3838383636363333333030302c2c2c2929292626262525253f3f3f919191dededefcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403f3f3f3e3e3e3c3c3c3b3b3b3939393838383636363939394d4d4d8a8a8ac7c7c7f1f1f1fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfde9e9e9b7b7b77272724242423939393737373838383a3a3a3b3b3b3d3d3d3e3e3e4040404141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444242424141414040403e3e3e3b3b3b3939393636363333332f2f2f2c2c2c2828282525252f2f2f696969d1d1d1fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141414040403e3e3e3d3d3d3b3b3b3a3a3a383838373737393939545454939393dfdfdffbfbfbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdf4f4f4cdcdcd7d7d7d4949493636363737373939393a3a3a3c3c3c3d3d3d3f3f3f4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424040403e3e3e3c3c3c3939393636363333332f2f2f2c2c2c282828252525373737969696f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424040403f3f3f3e3e3e3c3c3c3a3a3a393939373737383838515151959595dfdfdffdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafafacdcdcd7c7c7c4343433838383838383939393b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3d3d3d3a3a3a3737373333333030302c2c2c282828262626474747b8b8b8f7f7f7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3a3a3a383838373737464646828282dededefdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f5f5c5c5c56b6b6b3d3d3d3737373838383a3a3a3c3c3c3d3d3d3f3f3f4040404242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434141413f3f3f3d3d3d3b3b3b3838383434343030302c2c2c2828282727274f4f4fc5c5c5fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141413f3f3f3e3e3e3c3c3c3b3b3b3939393737373b3b3b686868c2c2c2fafafafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2f2f2a1a1a15252523838383838383939393b3b3b3d3d3d3e3e3e4040404141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3939393535353131312d2d2d292929272727525252d1d1d1fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141413f3f3f3e3e3e3c3c3c3a3a3a383838373737484848949494eaeaeafefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdd7d7d77979794040403737373939393b3b3b3c3c3c3e3e3e4040404141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a3636363333332e2e2e2a2a2a272727474747c7c7c7fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424040403f3f3f3d3d3d3b3b3b393939383838383838606060c6c6c6fcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f7a4a4a44a4a4a3737373838383a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3b3b3b3838383434343030302b2b2b2727273b3b3bbebebefbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3232323838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424040403f3f3f3d3d3d3b3b3b3939393737373f3f3f7c7c7ce0e0e0fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfccacaca5f5f5f3a3a3a3838383a3a3a3c3c3c3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a3636363131312d2d2d2828283131319b9b9bf8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424040403f3f3f3d3d3d3b3b3b3939393737374444449a9a9af5f5f5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefee1e1e17878783b3b3b3737373939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444444444242424040403e3e3e3b3b3b3838383333332f2f2f2a2a2a292929737373f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3d3d3d3b3b3b3838383737374c4c4cb0b0b0f8f8f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef8787873f3f3f3737373939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403d3d3d3a3a3a3636363131312c2c2c272727444444d4d4d4fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2d2d2d3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3d3d3d3b3b3b3838383737374f4f4fbebebefdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f3f39a9a9a4040403737373939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3c3c3c3838383434342f2f2f2a2a2a2f2f2f999999f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2e2e2e3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3d3d3d3b3b3b383838373737555555c6c6c6fcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f89c9c9c4242423737373939393b3b3b3d3d3d3f3f3f4141414242424444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3a3a3a3636363232322d2d2d292929545454ebebebfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde2e2e2cbcbcbfafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2e2e2e3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3d3d3d3b3b3b393939373737505050c8c8c8fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8a3a3a33f3f3f3737373939393c3c3c3e3e3e4040404141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403d3d3d3939393535353030302b2b2b2e2e2ea6a6a6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbcbcbc4d4d4d9e9e9eeeeeeefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2c2c2c2e2e2e3434343838383d3d3d4040404343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444242424141413f3f3f3d3d3d3b3b3b3939393737374e4e4ec5c5c5fcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f79898983e3e3e3737373a3a3a3c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c3838383333332e2e2e282828505050e6e6e6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2e2e2e2e2e2e787878dfdfdffcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2d2d2d2f2f2f3434343939393d3d3d4040404343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3c3c3c393939373737484848b9b9b9fdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f4f48d8d8d3b3b3b3838383a3a3a3c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413e3e3e3a3a3a3636363131312c2c2c292929979797f8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2d2d2d2323232727275a5a5ac9c9c9fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2d2d2d2f2f2f3434343939393d3d3d4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3a3a3a373737404040a4a4a4fbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef7979793a3a3a3838383a3a3a3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d3939393535353030302b2b2b3f3f3fd0d0d0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2d2d2d232323242424272727424242a7a7a7f2f2f2fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad2e2e2e3030303535353a3a3a3e3e3e4141414343434545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424141413f3f3f3d3d3d3a3a3a3838383b3b3b8a8a8af6f6f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e5e56363633737373939393b3b3b3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d3939393434342f2f2f2929295f5f5ff0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2d2d2d242424252525272727282828343434848484e7e7e7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac2f2f2f3131313636363a3a3a3e3e3e4141414343434545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3b3b3b3838383737376d6d6deeeeeefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefecdcdcd4e4e4e3737373939393c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242423f3f3f3c3c3c3838383333332d2d2d2d2d2d8a8a8afcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2e2e2e2525252828282a2a2a2b2b2b2b2b2b303030646464d2d2d2fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac3030303232323737373b3b3b3f3f3f4141414343434545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c393939373737525252d3d3d3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfda8a8a84242423737373a3a3a3c3c3c3f3f3f4141414242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413f3f3f3b3b3b3737373232322c2c2c353535bebebefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2e2e2e2828282b2b2b2e2e2e2f2f2f2f2f2f2e2e2e2f2f2f494949b3b3b3f5f5f5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac3131313333333838383c3c3c3f3f3f4242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444444444242424141413f3f3f3d3d3d3a3a3a3838383f3f3faaaaaafdfdfdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6f6f67d7d7d3737373838383b3b3b3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413e3e3e3b3b3b3636363131312c2c2c3c3c3ce7e7e7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb2f2f2f2a2a2a2e2e2e3131313333333333333333333131312f2f2f3b3b3b8d8d8defefeffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcadadad3333333535353939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403d3d3d3b3b3b3838383939397d7d7df7f7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e0e05858583737373939393c3c3c3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413e3e3e3a3a3a3535353030302c2c2c535353f8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb3030302c2c2c3030303434343636363737373737373636363333333030303434346a6a6ad8d8d8fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac3434343737373a3a3a3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3c3c3c393939373737515151d8d8d8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeaeaeae3f3f3f3838383a3a3a3d3d3d3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413e3e3e3a3a3a3535353030302c2c2c737373fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb3131312d2d2d3232323636363838383a3a3a3b3b3b3a3a3a383838353535323232313131505050b9b9b9f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcacacac3636363838383c3c3c3f3f3f4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3b3b3b3838383d3d3da4a4a4fbfbfbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f4f47171713737373939393b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413e3e3e3a3a3a3535352f2f2f2d2d2d8b8b8bfbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb3131312e2e2e3333333737373a3a3a3c3c3c3d3d3d3d3d3d3b3b3b393939373737343434313131414141989898efefeff9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f8f8f8aaaaaa3838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3c3c3c393939383838636363efefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcccccc4848483737373a3a3a3c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413d3d3d3939393535352f2f2f2d2d2d979797fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb3232322f2f2f3434343939393c3c3c3e3e3e4040404040403f3f3f3d3d3d3b3b3b3838383636363434343737373f3f3f4040403f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4040404040404040404040403f3f3f3a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a3838383f3f3fc0c0c0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb8e8e8e3838383838383b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413e3e3e3939393535352f2f2f2d2d2da0a0a0fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbbbbbb3333333131313636363a3a3a3d3d3d4040404141414242424141414040403e3e3e3c3c3c3a3a3a3939393737373535353434343333333232323131313131313131313030303030303131313131313232323434343535353636363838383b3b3b3d3d3d3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3c3c3c3939393737376d6d6df1f1f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd4d4d45050503737373a3a3a3c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413e3e3e3a3a3a3535352f2f2f2d2d2da6a6a6fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdb6b6b63333333232323737373b3b3b3f3f3f4141414343434343434343434242424141413f3f3f3e3e3e3d3d3d3b3b3b3a3a3a3939393838383737373737373636363636363636363636363636363737373737373838383939393b3b3b3c3c3c3e3e3e3f3f3f4141414242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a383838424242bfbfbffefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafafa8989893939393838383b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413e3e3e3a3a3a3535353030302d2d2da0a0a0fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfda5a5a53434343333333838383c3c3c4040404242424343434444444444444444444343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3f3f3f4040404141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3c3c3c3939393737376a6a6af3f3f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd9d9d94848483737373a3a3a3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444141413e3e3e3a3a3a3636363030302e2e2e969696fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfc7979793333333535353939393d3d3d4040404343434444444545454545454545454444444444444343434242424141414141414040404040403f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f4040404040404141414141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b3838383e3e3eb5b5b5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f98484843636363939393c3c3c3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444141413f3f3f3b3b3b3636363131312e2e2e898989fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde8e8e84d4d4d3232323737373b3b3b3e3e3e4141414343434545454545454646464545454545454545454444444444444343434343434343434242424242424242424242424242424242424242424242424242424242424242424343434343434343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3c3c3c3a3a3a373737595959edededffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5c5c54343433838383b3b3b3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242423f3f3f3b3b3b3737373232322e2e2e727272fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfafafaa7a7a73939393434343939393c3c3c4040404242424444444545454646464646464646464646464545454545454545454545454545454444444444444444444444444444444444444444444444444444444444444444444444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b393939373737989898fbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeeeeee6868683737373939393c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403c3c3c3838383333332e2e2e505050f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfddedede5454543232323737373b3b3b3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737494949d3d3d3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefea0a0a03c3c3c3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434040403d3d3d3939393434342f2f2f3e3e3ee6e6e6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdececec7676763535353535353939393d3d3d4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3c3c3c3939393737376b6b6bf3f3f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdcdcdc4848483737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434141413e3e3e3a3a3a353535303030373737bdbdbdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfce5e5e57979793737373434343838383c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b3838383c3c3c9f9f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb6c6c6c3737373939393c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242423f3f3f3b3b3b373737323232303030888888fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f9f1f1f1b2b2b25656563636363535353838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b383838454545d8d8d8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefea7a7a73838383838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403c3c3c3838383333332e2e2e5f5f5fefefeffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfafafadadadacbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbc3c3c3b1b1b19191915c5c5c3a3a3a3434343636363939393b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737375d5d5df8f8f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3d3d34444443838383b3b3b3d3d3d4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413e3e3e3a3a3a353535303030414141cdcdcdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f99c9c9c3d3d3d3535353636363737373838383838383939393a3a3a3c3c3c3d3d3d3838382f2f2f3131313333333535353737373a3a3a3c3c3c3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424141413e3e3e3c3c3c393939383838919191fdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffececec6363633737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242423f3f3f3b3b3b3737373232322f2f2f969696fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde8e8e86262622323232020202121212323232626262929292c2c2c2e2e2e3030303232323434343636363838383939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b383838393939bcbcbcfefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafafa7f7f7f3737373939393c3c3c3f3f3f4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d3939393434342f2f2f535353e7e7e7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcd3d3d34949492020202121212525252828282c2c2c2f2f2f3232323434343636363838383a3a3a3c3c3c3d3d3d3f3f3f4040404141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b383838494949dadadaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa5a5a53d3d3d3838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413e3e3e3b3b3b363636313131323232a2a2a2fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdacacac2e2e2e2222222626262a2a2a2e2e2e3232323535353737373a3a3a3c3c3c3d3d3d3f3f3f4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737626262ecececffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcdcdcd4646463838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d3939393434342f2f2f515151e8e8e8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf2f2f27474742424242626262b2b2b2f2f2f3333333737373a3a3a3c3c3c3e3e3e4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c393939363636767676f6f6f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe9e9e94c4c4c3737373a3a3a3d3d3d3f3f3f4242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413e3e3e3b3b3b373737323232353535969696f8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdcccccc4040402626262b2b2b2f2f2f3434343838383b3b3b3e3e3e4040404141414343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424141413e3e3e3c3c3c3939393838388d8d8dfefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc5959593737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d393939353535303030474747d0d0d0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf8f8f88181812b2b2b2a2a2a2f2f2f3434343838383c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b3838383e3e3eadadadffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7878783838383939393c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3b3b3b373737333333313131707070efefeffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdd4d4d43e3e3e2929292e2e2e3333333838383b3b3b3e3e3e4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b383838434343c4c4c4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9292923939393939393c3c3c3e3e3e4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403e3e3e3a3a3a363636323232343434949494f8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f97d7d7d2929292d2d2d3232323636363b3b3b3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403d3d3d3b3b3b383838484848d7d7d7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaaaaaa3939393939393c3c3c3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242423f3f3f3d3d3d393939353535313131414141b6b6b6fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf5f5f5d2d2d2edededfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcbebebe3232322b2b2b3030303535353939393d3d3d4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3a3a3a3737374b4b4be5e5e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbababa3939393838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c3838383434343030304c4c4cc6c6c6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf7f7f79999993e3e3ea3a3a3fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde6e6e65959592929292f2f2f3434343939393c3c3c4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737374d4d4df0f0f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8c8c83a3a3a3838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413e3e3e3b3b3b373737333333303030505050d0d0d0fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf8f8f8a2a2a23535353d3d3dccccccfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f98484842b2b2b2d2d2d3333333838383c3c3c3f3f3f4242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737374f4f4ff8f8f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3d3d33a3a3a3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b3737373333333131314f4f4fc2c2c2fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf2f2f2989898363636222222525252f1f1f1fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdb7b7b73737372c2c2c3232323737373b3b3b3f3f3f4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737505050fdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd9d9d93a3a3a3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b3737373333333030304a4a4ab2b2b2f9f9f9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdeeeeee8c8c8c3333332323232727277b7b7bfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfddedede4242422b2b2b3131313636363a3a3a3e3e3e4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737505050ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdcdcdc3a3a3a3838383b3b3b3d3d3d4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b3838383434343131313d3d3d919191e9e9e9fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfbd6d6d66868682d2d2d2424242323232b2b2ba7a7a7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf8f8f84f4f4f2b2b2b3030303535353a3a3a3e3e3e4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737505050ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdcdcdc3a3a3a3838383b3b3b3d3d3d4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b383838353535323232333333676767cacacaf8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcefefefababab4f4f4f2a2a2a2626262525252323232e2e2ec9c9c9fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd7272722d2d2d2f2f2f3535353939393d3d3d4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737505050fefefeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdbdbdb3a3a3a3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3939393636363333333131314141418c8c8ce3e3e3fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f9c8c8c86969693535352b2b2b2b2b2b292929272727252525313131e1e1e1fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd9292922e2e2e2f2f2f3434343939393d3d3d4040404343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737374f4f4ff9f9f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd5d5d53a3a3a3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3c3c3c3a3a3a373737343434313131343434484848959595d7d7d7f5f5f5fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfceeeeeec4c4c47474743f3f3f3131312f2f2f2f2f2f2f2f2f2d2d2d2a2a2a2727273d3d3debebebfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfda7a7a72f2f2f2f2f2f3434343939393d3d3d4040404343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737374e4e4ef3f3f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcdcdcd3a3a3a3838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454343434242424141413f3f3f3d3d3d3b3b3b393939363636343434323232313131484848787878b6b6b6e8e8e8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf8f8f8dadadaa0a0a06868683d3d3d3131313131313333333434343333333232323030302c2c2c2929294c4c4ceeeeeefdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdb5b5b52f2f2f2e2e2e3434343939393d3d3d4040404343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3a3a3a3737374c4c4ce8e8e8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0c0c03a3a3a3838383b3b3b3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3d3d3d3b3b3b3838383636363434343232323131313a3a3a4b4b4b717171a6a6a6d2d2d2e8e8e8efefeff4f4f4f5f5f5f7f7f7f7f7f7f7f7f7f4f4f4f3f3f3efefefe1e1e1c3c3c39393935d5d5d4646463636363030303232323434343636363838383838383737373535353232322e2e2e2a2a2a555555f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbdbdbd3030302e2e2e3434343939393d3d3d4040404343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3b3b3b383838494949dcdcdcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0b0b03939393939393c3c3c3e3e3e4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3939393737373535353333333232323232323434343535353e3e3e5858586d6d6d7878788585858686868383837272726868685050503737373535353434343131313232323434343636363737373939393b3b3b3b3b3b3b3b3b3a3a3a3737373434343030302b2b2b565656f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdbebebe3030302f2f2f3434343939393d3d3d4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b383838454545cacacaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9b9b9b3939393939393c3c3c3e3e3e4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3a3a3a3838383737373535353434343333333232323232323131313131313131313030303131313131313131313232323333333434343535353636363737373939393a3a3a3c3c3c3d3d3d3e3e3e3e3e3e3e3e3e3c3c3c3a3a3a3636363131312c2c2c515151efefeffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdb9b9b93030302f2f2f3535353939393e3e3e4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3b3b3b383838404040b4b4b4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8181813838383939393c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141414040403f3f3f3e3e3e3d3d3d3b3b3b3a3a3a3939393838383737373737373636363636363636363636363636363636363636363737373838383939393939393b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4040404141414141414040403e3e3e3b3b3b3737373232322d2d2d444444ecececfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdacacac3030303030303535353a3a3a3e3e3e4141414343434545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424141413e3e3e3c3c3c3939393a3a3a979797ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6363633838383a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434343434242424141414040403f3f3f3e3e3e3d3d3d3d3d3d3c3c3c3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3e3e3e3f3f3f4040404141414242424242424343434343434242423f3f3f3c3c3c3838383333332e2e2e373737e4e4e4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd9a9a9a3030303131313636363a3a3a3e3e3e4141414444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c3939393636367c7c7cfafafafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0f0f04e4e4e3737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424242424141414141414040404040403f3f3f3f3f3f3f3f3f3f3f3f3e3e3e3f3f3f3f3f3f3f3f3f3f3f3f4040404040404141414141414242424343434343434444444444444444444444444343434040403d3d3d3939393535352f2f2f333333cfcfcffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd7e7e7e2f2f2f3232323737373b3b3b3f3f3f4242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737376a6a6aefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7d7d74747473838383b3b3b3d3d3d4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444343434343434343434242424242424242424242424242424242424242424242424242424242424242424343434343434444444444444444444545454545454545454545454545454343434141413e3e3e3a3a3a363636303030323232afafaffdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfb5959592e2e2e3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737525252e1e1e1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb3b3b33f3f3f3838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454444444444444444444444444444444444444444444444444444444444444444444444444444444545454545454545454545454646464646464646464646464545454444444242423f3f3f3b3b3b373737323232303030838383fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde5e5e54545452e2e2e3434343939393d3d3d4040404343434444444646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b3838383c3c3cc7c7c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc8989893737373939393c3c3c3e3e3e4141414242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464545454444444242424040403c3c3c3838383333332e2e2e565656f4f4f4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdc0c0c03a3a3a3030303535353a3a3a3e3e3e4141414343434545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424040403e3e3e3c3c3c393939373737a0a0a0fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1f1f16b6b6b3737373a3a3a3c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464444444343434040403d3d3d3939393535352f2f2f444444d6d6d6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfb8f8f8f3030303232323737373b3b3b3f3f3f4242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c3939393737376b6b6bfbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdcdcdc4c4c4c3737373a3a3a3d3d3d4040404141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444141413e3e3e3b3b3b363636313131343434a3a3a3fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdededed6363632e2e2e3333333838383c3c3c4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a3737374a4a4ae5e5e5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefeb6b6b63a3a3a3838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403c3c3c3838383333332e2e2e6d6d6df0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfccacaca3c3c3c3030303535353a3a3a3e3e3e4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3b3b3b3838383f3f3fb3b3b3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfc8080803838383939393c3c3c3e3e3e4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413d3d3d3a3a3a353535303030414141d1d1d1fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf9f9f99090902f2f2f3232323737373b3b3b3f3f3f4242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413e3e3e3c3c3c3939393737377a7a7af8f8f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffebebeb4f4f4f3737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242423f3f3f3b3b3b3737373232322f2f2f8a8a8afafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde3e3e34a4a4a3030303434343939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a373737535353dededeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb5b5b53f3f3f3838383b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434040403d3d3d3939393535352f2f2f444444ddddddfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfb9999993333333232323737373b3b3b3e3e3e4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b3838383a3a3aaeaeaefefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f7f77676763838383939393c3c3c3e3e3e4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413f3f3f3b3b3b373737323232323232878787f8f8f8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde0e0e04e4e4e2f2f2f3434343939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3c3c3c3939393636366c6c6cf5f5f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd6d6d64c4c4c3737373a3a3a3d3d3d3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403d3d3d393939353535303030424242c7c7c7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf6f6f68585853131313232323737373b3b3b3f3f3f4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3d3d3d3a3a3a383838414141cacacafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb9999993737373838383b3b3b3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434141413f3f3f3c3c3c3838383333332f2f2f6c6c6cf1f1f1fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdc7c7c73f3f3f3030303535353939393d3d3d4040404242424444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403e3e3e3c3c3c3939393838387c7c7cf9f9f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8e8e85454543737373a3a3a3c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434040403e3e3e3a3a3a363636313131353535989898fafafafdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde9e9e95e5e5e3030303434343838383c3c3c3f3f3f4141414343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a3737374c4c4cd1d1d1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefea4a4a43d3d3d3838383b3b3b3d3d3d4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242423f3f3f3d3d3d393939353535303030434343c0c0c0fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf4f4f47d7d7d3232323232323737373b3b3b3e3e3e4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b3939393737378b8b8bfafafaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaea5c5c5c3737373939393c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434141413f3f3f3c3c3c383838343434303030505050d4d4d4fcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf6f6f69c9c9c3a3a3a3131313535353a3a3a3d3d3d4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a373737494949d3d3d3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfba4a4a43b3b3b3838383b3b3b3d3d3d4040404141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444242424040403e3e3e3b3b3b3737373333333030305e5e5eddddddfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfbadadad3e3e3e3131313535353939393c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3b3b3b393939373737797979f7f7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1e1e15353533737373939393c3c3c3e3e3e4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454343434242424040403d3d3d3a3a3a373737333333323232646464ddddddfcfcfcfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf7f7f7b0b0b04444443030303434343838383c3c3c3f3f3f4141414343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a373737454545bdbdbdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfbfb9090903b3b3b3838383b3b3b3d3d3d4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3a3a3a3636363333333232325d5d5dd8d8d8fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdf8f8f8a4a4a43f3f3f3030303434343838383b3b3b3e3e3e4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3c3c3c393939373737686868edededffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcacaca4848483737373a3a3a3c3c3c3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403d3d3d3a3a3a373737333333313131525252c2c2c2f7f7f7fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdeeeeee8c8c8c3d3d3d3131313434343838383b3b3b3e3e3e4040404242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3b3b3b3838383c3c3c969696fdfdfdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffececec6c6c6c3737373939393b3b3b3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3a3a3a3737373333333131313f3f3f989898f0f0f0fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfbfbfbd0d0d06868683333333131313535353838383b3b3b3e3e3e4040404242424343434545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444343434242424040403e3e3e3c3c3c3a3a3a3737374a4a4accccccfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefea2a2a23c3c3c3838383a3a3a3d3d3d3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141413f3f3f3d3d3d3b3b3b383838343434313131363636636363c8c8c8f6f6f6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcededed9a9a9a4c4c4c3131313333333636363939393c3c3c3e3e3e4040404242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3b3b3b393939383838696969e8e8e8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefecacaca4c4c4c3737373939393c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3b3b3b3939393636363333333131313f3f3f848484d2d2d2f6f6f6fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfcecececb7b7b76161613535353131313434343737373a3a3a3c3c3c3f3f3f4141414242424343434444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444242424141413f3f3f3d3d3d3a3a3a383838393939898989fbfbfbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e5e56161613737373939393b3b3b3e3e3e4040404141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3c3c3c3a3a3a373737353535323232323232444444808080c9c9c9f5f5f5fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfcfcfce6e6e6a9a9a96262623838383131313333333636363838383b3b3b3d3d3d3f3f3f4141414242424444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3a3a3a373737434343afafaffcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6f6f68181813a3a3a3838383b3b3b3d3d3d3f3f3f4141414242424444444444444545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3d3d3d3b3b3b3939393737373434343232323232323e3e3e5c5c5ca1a1a1d7d7d7edededf6f6f6fbfbfbfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfafafaf4f4f4e9e9e9c2c2c27e7e7e4949493939393131313333333535353838383a3a3a3c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3c3c3c3939393737374d4d4dc7c7c7fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9f9f99e9e9e4040403737373a3a3a3c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424040403f3f3f3d3d3d3b3b3b393939373737353535333333323232333333383838565656828282a1a1a1c0c0c0cfcfcfd8d8d8d9d9d9e5e5e5dfdfdfd8d8d8d6d6d6c6c6c6aaaaaa9696967373734848483333333232323131313333333535353838383a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454444444444444343434141413f3f3f3d3d3d3b3b3b3939393636365b5b5bd7d7d7fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcaeaeae4343433737373939393c3c3c3e3e3e4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3939393737373636363434343333333131313030302f2f2f3939394343434949494a4a4a5353535050504a4a4a4848483e3e3e3030302f2f2f3131313232323333333535353737373838383a3a3a3c3c3c3e3e3e3f3f3f4040404242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424141413f3f3f3d3d3d3b3b3b383838383838646464e1e1e1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdc0c0c04d4d4d3737373939393b3b3b3d3d3d3f3f3f4141414343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403f3f3f3d3d3d3c3c3c3a3a3a3939393838383737373535353535353434343333333232323232323232323232323232323333333333333434343535353636363737373838383a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4040404242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403f3f3f3d3d3d3a3a3a383838393939707070e5e5e5fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdc5c5c55151513737373939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464747474949494c4c4c4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4c4c4c4c4c4c4c4c4c4b4b4b4a4a4a4a4a4a4949494848484646464444444141413e3e3e3b3b3b3a3a3a3939393939393838383838383838383737373737373838383838383838383939393a3a3a3b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4040404141414242424343434343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434242424040403e3e3e3c3c3c3a3a3a3838383a3a3a6e6e6ee4e4e4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdc5c5c55353533737373939393b3b3b3d3d3d3f3f3f4141414242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464444443d3d3d3434342a2a2a2525252424242424242424242424242424242424242424242424242424242424242424242323232323232323232323232323232626262e2e2e3737373c3c3c3e3e3e3e3e3e3d3d3d3d3d3d3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3d3d3d3d3d3d3e3e3e3e3e3e3f3f3f4040404141414242424242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444343434141414040403e3e3e3c3c3c3a3a3a3838383939396e6e6ee4e4e4fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcc3c3c35151513838383939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545453d3d3d2828281515150808080404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040505051111112222223636364040404141414040404040404040404040404040404040404040404040404040404141414141414242424242424343434343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403e3e3e3c3c3c3a3a3a383838393939646464d5d5d5fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffafafab3b3b34d4d4d3737373939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464343432e2e2e0b0b0b0202020b0b0b1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212120d0d0d0303030606062424243e3e3e4141414040404040404040404040404040404242424242424343434343434343434343434444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403e3e3e3c3c3c3a3a3a383838383838565656c5c5c5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6f6f6a1a1a14444443737373939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464040401c1c1c0808085d5d5db5b5b5c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4bdbdbd6f6f6f1010101111113131313535352e2e2e2a2a2a2b2b2b3131313939394040404444444444444545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403e3e3e3c3c3c3a3a3a3838383636364d4d4dabababf4f4f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffececec8484844040403737373939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545453c3c3c1414143c3c3ce4e4e4fdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfde9e9e96868680404041515151313130a0a0a0606060808081111112323233939394646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403e3e3e3d3d3d3b3b3b393939373737404040838383e8e8e8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfdfdcdcdcd6868683b3b3b3737373939393b3b3b3d3d3d3f3f3f4040404242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464343433d3d3d2d2d2d0b0b0b6c6c6cf6f6f6fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfca4a4a40606060000000101010404040404040404040404040707072929294545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434141414040403f3f3f3d3d3d3b3b3b393939373737393939636363c6c6c6f8f8f8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f5f5a5a5a54f4f4f3737373838383a3a3a3c3c3c3d3d3d3f3f3f4141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464444443d3d3d313131222222111111020202717171f6f6f6fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfca6a6a60808081212126161619b9b9b9e9e9e9e9e9e8686861414141e1e1e4545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424040403f3f3f3d3d3d3c3c3c3a3a3a383838383838464646909090e8e8e8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfcfcd0d0d07474743d3d3d3737373939393a3a3a3c3c3c3e3e3e3f3f3f4141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464343433737372323230e0e0e030303010101020202737373f6f6f6fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfbfbfba5a5a5313131929292efefeffbfbfbfbfbfbfbfbfbdbdbdb2222221a1a1a4545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141413f3f3f3e3e3e3c3c3c3b3b3b3939393737373a3a3a5f5f5fb7b7b7f3f3f3fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefee8e8e89494944e4e4e3838383838383939393b3b3b3d3d3d3e3e3e4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464444443939391c1c1c0606060202021b1b1b4e4e4e7b7b7bc0c0c0f9f9f9fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfafafacfcfcfc0c0c0f8f8f8fcfcfcfcfcfcfcfcfcfcfcfcdedede2222221919194545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434242424141414040403e3e3e3d3d3d3b3b3b3a3a3a383838373737414141707070c4c4c4f6f6f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeaeaeaafafaf5d5d5d3b3b3b3737373939393a3a3a3c3c3c3d3d3d3f3f3f4040404141414343434343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646463e3e3e2525250707070f0f0f545454c1c1c1ebebebf5f5f5fafafafcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf9f9f9fbfbfbfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcdedede2222221919194545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434343434242424040403f3f3f3e3e3e3c3c3c3b3b3b3939393838383636364444447a7a7ac9c9c9f8f8f8fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef0f0f0afafaf6666663d3d3d3737373838383a3a3a3b3b3b3d3d3d3e3e3e3f3f3f4141414242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464444443232320c0c0c101010878787e8e8e8fdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcdedede2222221919194545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454444444444444343434242424141414040403e3e3e3d3d3d3c3c3c3a3a3a3939393737373838384444446f6f6fbababae9e9e9fcfcfcfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8dededea6a6a65d5d5d3e3e3e3737373838383939393b3b3b3c3c3c3e3e3e3f3f3f4040404141414242424343434444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464141412323230505056e6e6eefefeffcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfafafaf9f9f9fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcdedede2222221919194545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434343434242424040403f3f3f3e3e3e3d3d3d3c3c3c3a3a3a3939393838383737373c3c3c5e5e5e949494d5d5d5fafafafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2f2f2c1c1c18282825252523939393737373838383939393b3b3b3c3c3c3d3d3d3f3f3f4040404141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464747473c3c3c121212262626c7c7c7fdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfbfbfbcacacaadadadf6f6f6fdfdfdfcfcfcfcfcfcfdfdfddedede2121211c1c1c4545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3a3a3a3939393838383737373a3a3a464646656565a1a1a1d2d2d2ebebebf8f8f8fefefefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef6f6f6e7e7e7c4c4c48c8c8c5656564343433838383737373838383939393b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4141414242424242424343434444444444444545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464747473737370b0b0b4f4f4ff4f4f4fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfca4a4a41b1b1b808080e7e7e7fcfcfcfdfdfdfdfdfddedede2020202020204545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444343434343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3a3a3a3939393838383838383939394040405f5f5f808080a5a5a5cccccce6e6e6fafafafffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefefef5f5f5dededec0c0c09898987878785555553c3c3c3838383737373838383939393a3a3a3b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4040404141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464747473434340d0d0d6c6c6cfdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfca6a6a60707070f0f0f575757c1c1c1d6d6d6d6d6d6b9b9b92020202d2d2d4646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434343434242424141414040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3a3a3a3a3a3a3939393838383737373636363b3b3b4646464f4f4f5c5c5c7a7a7a969696b0b0b0c2c2c2cfcfcfdbdbdbe1e1e1e6e6e6e6e6e6e5e5e5dfdfdfd8d8d8cacacabcbcbca7a7a78c8c8c6e6e6e5656564d4d4d4444443939393636363737373838383939393a3a3a3b3b3b3c3c3c3d3d3d3e3e3e3f3f3f4040404141414141414242424343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464747473535350f0f0f777777fdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfbfbfb9e9e9e0e0e0e2525251b1b1b1e1e1e1e1e1e1d1d1d1f1f1f2020203b3b3b4646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434343434242424141414040404040403f3f3f3e3e3e3d3d3d3c3c3c3b3b3b3b3b3b3a3a3a3939393939393838383737373737373939393a3a3a3a3a3a3b3b3b3b3b3b3b3b3b3c3c3c3c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3b3b3a3a3a3939393838383737373737373838383939393939393a3a3a3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3f3f3f4040404141414141414242424343434343434444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646463b3b3b0f0f0f696969fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfbfbfbfcfcfcfbfbfbe1e1e15252521b1b1b4040404141413c3c3c3939393838383c3c3c4040404545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444343434343434242424242424141414040403f3f3f3f3f3f3e3e3e3d3d3d3d3d3d3c3c3c3b3b3b3b3b3b3a3a3a3a3a3a3939393939393939393838383838383838383838383838383838383838383838383838383838383838383939393939393939393a3a3a3a3a3a3b3b3b3c3c3c3c3c3c3d3d3d3e3e3e3e3e3e3f3f3f4040404040404141414242424242424343434444444444444444444545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464040401515154b4b4bf0f0f0fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcedededb7b7b7acacaca0a0a05454541414142f2f2f4545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454444444444444444444343434343434242424141414141414040404040403f3f3f3f3f3f3e3e3e3e3e3e3d3d3d3d3d3d3c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3b3c3c3c3c3c3c3c3c3c3d3d3d3d3d3d3e3e3e3e3e3e3f3f3f3f3f3f4040404040404141414242424242424343434343434444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545452a2a2a252525c2c2c2fdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfceeeeee6666660808080a0a0a1d1d1d3434344343434646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444343434343434242424242424141414141414040404040403f3f3f3f3f3f3f3f3f3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3d3d3d3d3d3d3d3d3d3d3d3d3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3f3f3f3f3f3f3f3f3f4040404040404040404141414141414242424242424343434343434444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646463b3b3b171717636363e5e5e5fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf3f3f3fbfbfbfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdcacaca1f1f1f1313133a3a3a4545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444343434343434343434242424242424141414141414141414141414040404040404040404040404040404040404040404040404040404040404040404040404040404141414141414141414242424242424242424343434343434343434444444444444444444545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545452e2e2e131313717171dcdcdcfdfdfdfdfdfdfcfcfcfcfcfcfcfcfcfdfdfdf9f9f9cccccc949494f0f0f0fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf2f2f25858580e0e0e3939394646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454444444444444444444444444343434343434343434343434242424242424242424242424242424242424242424242424242424242424242424242424242424242424343434343434343434343434343434444444444444444444545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464242422a2a2a121212454545b0b0b0eeeeeef5f5f5f7f7f7f6f6f6dedede8a8a8a303030515151f0f0f0fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf9f9f98d8d8d0a0a0a3737374646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454444444444444444444444444444444444444444444343434343434343434343434343434343434343434444444444444444444444444444444444444444444545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464242423232321919191b1b1b3838385a5a5a6868685252522f2f2f121212040404555555f2f2f2fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfafafa9a9a9a0c0c0c3838384646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545453e3e3e3131312626261c1c1c1b1b1b1e1e1e282828303030151515494949f0f0f0fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf6f6f67f7f7f1111113d3d3d4646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454545454545454545454545454545454545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545454343434343434444444545454444442727272c2c2ce4e4e4fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfceeeeee4545451a1a1a424242464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646373737151515969696fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfdfdfdbbbbbb1c1c1c2727274545454646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464343431e1e1e2d2d2dc7c7c7fcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcd9d9d94040401616163c3c3c4646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646463737371414143e3e3ec8c8c8fafafafdfdfdfcfcfcfcfcfcfcfcfcfcfcfcfbfbfbdddddd656565121212323232454545464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646444444333333181818313131919191c4c4c4e8e8e8f6f6f6edededcecece9e9e9e4b4b4b1616162e2e2e4343434646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464545453c3c3c2323231313132323234444445555554b4b4b2d2d2d1313131f1f1f3737374444444646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464242423838382c2c2c2222221e1e1e1f1f1f282828363636404040454545464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646474747484848474747464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646\n  m_StreamData:\n    serializedVersion: 2\n    offset: 0\n    size: 0\n    path: \n--- !u!114 &11400000\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 13966, guid: 0000000000000000e000000000000000, type: 0}\n  m_Name: Lit2DSceneTemplate\n  m_EditorClassIdentifier: \n  templateScene: {fileID: 102900000, guid: 2cda990e2423bbf4892e6590ba056729, type: 3}\n  templateName: Lit 2D (URP)\n  description: Contains an orthographic camera and a Global Light 2D. Works with\n    the 2D Renderer in Universal RP.\n  preview: {fileID: -3604256930052969394}\n  dependencies:\n  - dependency: {fileID: 2800000, guid: b3ab972b21db85d48ade9657efdd4771, type: 3}\n    instantiationMode: 1\n  templatePipeline: {fileID: 0}\n  addToDefaults: 1\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Lit2DSceneTemplate.scenetemplate.meta",
    "content": "fileFormatVersion: 2\nguid: d03ed43fc9d8a4f2e9fa70c1c7916eb9\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 11400000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Renderer2D.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &11400000\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 11145981673336645838492a2d98e247, type: 3}\n  m_Name: Renderer2D\n  m_EditorClassIdentifier: \n  debugShaders:\n    debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}\n    hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}\n  m_RendererFeatures: []\n  m_RendererFeatureMap: \n  m_UseNativeRenderPass: 0\n  m_TransparencySortMode: 0\n  m_TransparencySortAxis: {x: 0, y: 1, z: 0}\n  m_HDREmulationScale: 1\n  m_LightRenderTextureScale: 0.5\n  m_LightBlendStyles:\n  - name: Multiply\n    maskTextureChannel: 0\n    blendMode: 1\n  - name: Additive\n    maskTextureChannel: 0\n    blendMode: 0\n  - name: Multiply with Mask\n    maskTextureChannel: 1\n    blendMode: 1\n  - name: Additive with Mask\n    maskTextureChannel: 1\n    blendMode: 0\n  m_UseDepthStencilBuffer: 1\n  m_UseCameraSortingLayersTexture: 0\n  m_CameraSortingLayersTextureBound: -1\n  m_CameraSortingLayerDownsamplingMethod: 0\n  m_MaxLightRenderTextureCount: 16\n  m_MaxShadowRenderTextureCount: 1\n  m_ShapeLightShader: {fileID: 4800000, guid: d79e1c784eaf80c4585c0be7391f757a, type: 3}\n  m_ShapeLightVolumeShader: {fileID: 4800000, guid: 7e60080c8cd24a2468cb08b4bfee5606, type: 3}\n  m_PointLightShader: {fileID: 4800000, guid: e35a31e1679aeff489e202f5cc4853d5, type: 3}\n  m_PointLightVolumeShader: {fileID: 4800000, guid: c7d04ca57e5449d49ad9cee1c604bc26, type: 3}\n  m_CoreBlitShader: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}\n  m_BlitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3}\n  m_CoreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3}\n  m_SamplingShader: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}\n  m_ProjectedShadowShader: {fileID: 4800000, guid: ce09d4a80b88c5a4eb9768fab4f1ee00, type: 3}\n  m_SpriteShadowShader: {fileID: 4800000, guid: 44fc62292b65ab04eabcf310e799ccf6, type: 3}\n  m_SpriteUnshadowShader: {fileID: 4800000, guid: de02b375720b5c445afe83cd483bedf3, type: 3}\n  m_GeometryUnshadowShader: {fileID: 4800000, guid: 77774d9009bb81447b048c907d4c6273, type: 3}\n  m_FallbackErrorShader: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}\n  m_PostProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}\n  m_FallOffLookup: {fileID: 2800000, guid: 5688ab254e4c0634f8d6c8e0792331ca, type: 3}\n  m_DefaultMaterialType: 0\n  m_DefaultCustomMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}\n  m_DefaultLitMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}\n  m_DefaultUnlitMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}\n  m_DefaultMaskMaterial: {fileID: 2100000, guid: 15d0c3709176029428a0da2f8cecf0b5, type: 2}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Renderer2D.asset.meta",
    "content": "fileFormatVersion: 2\nguid: 424799608f7334c24bf367e4bbfa7f9a\nNativeFormatImporter:\n  externalObjects: {}\n  mainObjectFileID: 11400000\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Scenes/URP2DSceneTemplate.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 3\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 0}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_GIWorkflowMode: 1\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 0\n    m_EnableRealtimeLightmaps: 0\n  m_LightmapEditorSettings:\n    serializedVersion: 12\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_ExtractAmbientOcclusion: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 0\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 500\n    m_PVRBounces: 2\n    m_PVREnvironmentSampleCount: 500\n    m_PVREnvironmentReferencePointCount: 2048\n    m_PVRFilteringMode: 2\n    m_PVRDenoiserTypeDirect: 0\n    m_PVRDenoiserTypeIndirect: 0\n    m_PVRDenoiserTypeAO: 0\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVREnvironmentMIS: 0\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ExportTrainingData: 0\n    m_TrainingDataDestination: TrainingData\n    m_LightProbeSampleCountMultiplier: 4\n  m_LightingDataAsset: {fileID: 0}\n  m_LightingSettings: {fileID: 0}\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    maxJobWorkers: 0\n    preserveTilesOutsideBounds: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &519420028\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 519420032}\n  - component: {fileID: 519420031}\n  - component: {fileID: 519420029}\n  - component: {fileID: 519420030}\n  m_Layer: 0\n  m_Name: Main Camera\n  m_TagString: MainCamera\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!81 &519420029\nAudioListener:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n--- !u!114 &519420030\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_RenderShadows: 1\n  m_RequiresDepthTextureOption: 2\n  m_RequiresOpaqueTextureOption: 2\n  m_CameraType: 0\n  m_Cameras: []\n  m_RendererIndex: -1\n  m_VolumeLayerMask:\n    serializedVersion: 2\n    m_Bits: 1\n  m_VolumeTrigger: {fileID: 0}\n  m_RenderPostProcessing: 0\n  m_Antialiasing: 0\n  m_AntialiasingQuality: 2\n  m_StopNaN: 0\n  m_Dithering: 0\n  m_ClearDepth: 1\n  m_AllowXRRendering: 1\n  m_RequiresDepthTexture: 0\n  m_RequiresColorTexture: 0\n  m_Version: 2\n--- !u!20 &519420031\nCamera:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  serializedVersion: 2\n  m_ClearFlags: 2\n  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}\n  m_projectionMatrixMode: 1\n  m_GateFitMode: 2\n  m_FOVAxisMode: 0\n  m_SensorSize: {x: 36, y: 24}\n  m_LensShift: {x: 0, y: 0}\n  m_FocalLength: 50\n  m_NormalizedViewPortRect:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  near clip plane: 0.3\n  far clip plane: 1000\n  field of view: 34\n  orthographic: 1\n  orthographic size: 5\n  m_Depth: -1\n  m_CullingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n  m_RenderingPath: -1\n  m_TargetTexture: {fileID: 0}\n  m_TargetDisplay: 0\n  m_TargetEye: 0\n  m_HDR: 1\n  m_AllowMSAA: 0\n  m_AllowDynamicResolution: 0\n  m_ForceIntoRT: 0\n  m_OcclusionCulling: 0\n  m_StereoConvergence: 10\n  m_StereoSeparation: 0.022\n--- !u!4 &519420032\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: -10}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 0\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!1 &619394800\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 619394802}\n  - component: {fileID: 619394801}\n  m_Layer: 0\n  m_Name: Global Light 2D\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!114 &619394801\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 619394800}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 073797afb82c5a1438f328866b10b3f0, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_ComponentVersion: 1\n  m_LightType: 4\n  m_BlendStyleIndex: 0\n  m_FalloffIntensity: 0.5\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_Intensity: 1\n  m_LightVolumeIntensity: 1\n  m_LightVolumeIntensityEnabled: 0\n  m_ApplyToSortingLayers: 00000000\n  m_LightCookieSprite: {fileID: 0}\n  m_DeprecatedPointLightCookieSprite: {fileID: 0}\n  m_LightOrder: 0\n  m_OverlapOperation: 0\n  m_NormalMapDistance: 3\n  m_NormalMapQuality: 2\n  m_UseNormalMap: 0\n  m_ShadowIntensityEnabled: 0\n  m_ShadowIntensity: 0.75\n  m_ShadowVolumeIntensityEnabled: 0\n  m_ShadowVolumeIntensity: 0.75\n  m_Vertices:\n  - position: {x: 0.9985302, y: 0.9985302, z: 0}\n    color: {r: 0.70710677, g: 0.70710677, b: 0, a: 0}\n    uv: {x: 0, y: 0}\n  - position: {x: 0.9985302, y: 0.9985302, z: 0}\n    color: {r: 0, g: 0, b: 0, a: 1}\n    uv: {x: 0, y: 0}\n  - position: {x: -0.9985302, y: 0.9985302, z: 0}\n    color: {r: -0.70710677, g: 0.70710677, b: 0, a: 0}\n    uv: {x: 0, y: 0}\n  - position: {x: -0.9985302, y: 0.9985302, z: 0}\n    color: {r: 0, g: 0, b: 0, a: 1}\n    uv: {x: 0, y: 0}\n  - position: {x: -0.99853003, y: -0.9985304, z: 0}\n    color: {r: -0.70710665, g: -0.7071069, b: 0, a: 0}\n    uv: {x: 0, y: 0}\n  - position: {x: -0.99853003, y: -0.9985304, z: 0}\n    color: {r: 0, g: 0, b: 0, a: 1}\n    uv: {x: 0, y: 0}\n  - position: {x: 0.99853003, y: -0.9985304, z: 0}\n    color: {r: 0.70710665, g: -0.7071069, b: 0, a: 0}\n    uv: {x: 0, y: 0}\n  - position: {x: 0.99853003, y: -0.9985304, z: 0}\n    color: {r: 0, g: 0, b: 0, a: 1}\n    uv: {x: 0, y: 0}\n  - position: {x: 0, y: 0, z: 0}\n    color: {r: 0, g: 0, b: 0, a: 1}\n    uv: {x: 0, y: 0}\n  m_Triangles: 030001000800020000000100030002000100050003000800040002000300050004000300070005000800060004000500070006000500010007000800000006000700010000000700\n  m_LocalBounds:\n    m_Center: {x: 0, y: -0.00000011920929, z: 0}\n    m_Extent: {x: 0.9985302, y: 0.99853027, z: 0}\n  m_PointLightInnerAngle: 360\n  m_PointLightOuterAngle: 360\n  m_PointLightInnerRadius: 0\n  m_PointLightOuterRadius: 1\n  m_ShapeLightParametricSides: 5\n  m_ShapeLightParametricAngleOffset: 0\n  m_ShapeLightParametricRadius: 1\n  m_ShapeLightFalloffSize: 0.5\n  m_ShapeLightFalloffOffset: {x: 0, y: 0}\n  m_ShapePath:\n  - {x: -0.5, y: -0.5, z: 0}\n  - {x: 0.5, y: -0.5, z: 0}\n  - {x: 0.5, y: 0.5, z: 0}\n  - {x: -0.5, y: 0.5, z: 0}\n--- !u!4 &619394802\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 619394800}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 1\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/Scenes/URP2DSceneTemplate.unity.meta",
    "content": "fileFormatVersion: 2\nguid: 2cda990e2423bbf4892e6590ba056729\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/Settings/UniversalRP.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &11400000\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}\n  m_Name: UniversalRP\n  m_EditorClassIdentifier: \n  k_AssetVersion: 11\n  k_AssetPreviousVersion: 11\n  m_RendererType: 1\n  m_RendererData: {fileID: 0}\n  m_RendererDataList:\n  - {fileID: 11400000, guid: 424799608f7334c24bf367e4bbfa7f9a, type: 2}\n  m_DefaultRendererIndex: 0\n  m_RequireDepthTexture: 0\n  m_RequireOpaqueTexture: 0\n  m_OpaqueDownsampling: 1\n  m_SupportsTerrainHoles: 1\n  m_SupportsHDR: 1\n  m_HDRColorBufferPrecision: 0\n  m_MSAA: 1\n  m_RenderScale: 1\n  m_UpscalingFilter: 0\n  m_FsrOverrideSharpness: 0\n  m_FsrSharpness: 0.92\n  m_EnableLODCrossFade: 1\n  m_LODCrossFadeDitheringType: 1\n  m_ShEvalMode: 0\n  m_MainLightRenderingMode: 1\n  m_MainLightShadowsSupported: 1\n  m_MainLightShadowmapResolution: 2048\n  m_AdditionalLightsRenderingMode: 1\n  m_AdditionalLightsPerObjectLimit: 4\n  m_AdditionalLightShadowsSupported: 0\n  m_AdditionalLightsShadowmapResolution: 2048\n  m_AdditionalLightsShadowResolutionTierLow: 512\n  m_AdditionalLightsShadowResolutionTierMedium: 1024\n  m_AdditionalLightsShadowResolutionTierHigh: 2048\n  m_ReflectionProbeBlending: 0\n  m_ReflectionProbeBoxProjection: 0\n  m_ShadowDistance: 50\n  m_ShadowCascadeCount: 1\n  m_Cascade2Split: 0.25\n  m_Cascade3Split: {x: 0.1, y: 0.3}\n  m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}\n  m_CascadeBorder: 0.1\n  m_ShadowDepthBias: 1\n  m_ShadowNormalBias: 1\n  m_AnyShadowsSupported: 1\n  m_SoftShadowsSupported: 0\n  m_ConservativeEnclosingSphere: 0\n  m_NumIterationsEnclosingSphere: 64\n  m_SoftShadowQuality: 2\n  m_AdditionalLightsCookieResolution: 2048\n  m_AdditionalLightsCookieFormat: 3\n  m_UseSRPBatcher: 1\n  m_SupportsDynamicBatching: 0\n  m_MixedLightingSupported: 1\n  m_SupportsLightCookies: 1\n  m_SupportsLightLayers: 0\n  m_DebugLevel: 0\n  m_StoreActionsOptimization: 0\n  m_EnableRenderGraph: 0\n  m_UseAdaptivePerformance: 1\n  m_ColorGradingMode: 0\n  m_ColorGradingLutSize: 32\n  m_UseFastSRGBLinearConversion: 0\n  m_SupportDataDrivenLensFlare: 1\n  m_ShadowType: 1\n  m_LocalShadowsSupported: 0\n  m_LocalShadowsAtlasResolution: 256\n  m_MaxPixelLights: 0\n  m_ShadowAtlasResolution: 256\n  m_VolumeFrameworkUpdateMode: 0\n  m_Textures:\n    blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}\n    bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}\n  m_PrefilteringModeMainLightShadows: 4\n  m_PrefilteringModeAdditionalLight: 4\n  m_PrefilteringModeAdditionalLightShadows: 0\n  m_PrefilterXRKeywords: 1\n  m_PrefilteringModeForwardPlus: 0\n  m_PrefilteringModeDeferredRendering: 0\n  m_PrefilteringModeScreenSpaceOcclusion: 0\n  m_PrefilterDebugKeywords: 1\n  m_PrefilterWriteRenderingLayers: 1\n  m_PrefilterHDROutput: 1\n  m_PrefilterSSAODepthNormals: 1\n  m_PrefilterSSAOSourceDepthLow: 1\n  m_PrefilterSSAOSourceDepthMedium: 1\n  m_PrefilterSSAOSourceDepthHigh: 1\n  m_PrefilterSSAOInterleaved: 1\n  m_PrefilterSSAOBlueNoise: 1\n  m_PrefilterSSAOSampleCountLow: 1\n  m_PrefilterSSAOSampleCountMedium: 1\n  m_PrefilterSSAOSampleCountHigh: 1\n  m_PrefilterDBufferMRT1: 1\n  m_PrefilterDBufferMRT2: 1\n  m_PrefilterDBufferMRT3: 1\n  m_PrefilterSoftShadowsQualityLow: 1\n  m_PrefilterSoftShadowsQualityMedium: 1\n  m_PrefilterSoftShadowsQualityHigh: 1\n  m_PrefilterSoftShadows: 0\n  m_PrefilterScreenCoord: 1\n  m_PrefilterNativeRenderPass: 1\n  m_ShaderVariantLogLevel: 0\n  m_ShadowCascades: 0\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/StarBackground.prefab",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1 &4748457267955120268\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 2344112196385852409}\n  - component: {fileID: 6728608069378556597}\n  - component: {fileID: 7224407313110299127}\n  m_Layer: 0\n  m_Name: StarBackground\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &2344112196385852409\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4748457267955120268}\n  serializedVersion: 2\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 10, y: 10, z: 10}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &6728608069378556597\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4748457267955120268}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 503a1c67365c19942a1a98fba2480609, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  Multiplier: 1\n--- !u!212 &7224407313110299127\nSpriteRenderer:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 4748457267955120268}\n  m_Enabled: 1\n  m_CastShadows: 0\n  m_ReceiveShadows: 0\n  m_DynamicOccludee: 1\n  m_StaticShadowCaster: 0\n  m_MotionVectors: 1\n  m_LightProbeUsage: 1\n  m_ReflectionProbeUsage: 1\n  m_RayTracingMode: 0\n  m_RayTraceProcedural: 0\n  m_RenderingLayerMask: 1\n  m_RendererPriority: 0\n  m_Materials:\n  - {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}\n  m_StaticBatchInfo:\n    firstSubMesh: 0\n    subMeshCount: 0\n  m_StaticBatchRoot: {fileID: 0}\n  m_ProbeAnchor: {fileID: 0}\n  m_LightProbeVolumeOverride: {fileID: 0}\n  m_ScaleInLightmap: 1\n  m_ReceiveGI: 1\n  m_PreserveUVs: 0\n  m_IgnoreNormalsForChartDetection: 0\n  m_ImportantGI: 0\n  m_StitchLightmapSeams: 1\n  m_SelectedEditorRenderState: 0\n  m_MinimumChartSize: 4\n  m_AutoUVMaxDistance: 0.5\n  m_AutoUVMaxAngle: 89\n  m_LightmapParameters: {fileID: 0}\n  m_SortingLayerID: 0\n  m_SortingLayer: 0\n  m_SortingOrder: -1\n  m_Sprite: {fileID: 21300000, guid: 59f7eca7bbaa8e0458a6400a2c6131e4, type: 3}\n  m_Color: {r: 1, g: 1, b: 1, a: 1}\n  m_FlipX: 0\n  m_FlipY: 0\n  m_DrawMode: 2\n  m_Size: {x: 2048, y: 2048}\n  m_AdaptiveModeThreshold: 0.5\n  m_SpriteTileMode: 0\n  m_WasSpriteAssigned: 1\n  m_MaskInteraction: 0\n  m_SpriteSortPoint: 0\n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/StarBackground.prefab.meta",
    "content": "fileFormatVersion: 2\nguid: c43a86c640b72404996a18c07c897b1e\nPrefabImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf.meta",
    "content": "fileFormatVersion: 2\nguid: 1b8d251f9af63b746bf2f7ffe00ebb9b\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unity/Assets/TextMesh Pro/Documentation.meta",
    "content": "fileFormatVersion: 2\nguid: 8e7e8f5a82a3a134e91c54efd2274ea9\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "demo/Blackholio/client-unreal/Config/DefaultEditor.ini",
    "content": ""
  },
  {
    "path": "docs/static/.nojekyll",
    "content": ""
  },
  {
    "path": "modules/benchmarks-ts/.gitignore",
    "content": ""
  },
  {
    "path": "sdks/unreal/examples/QuickstartChat/Config/DefaultEditor.ini",
    "content": ""
  },
  {
    "path": "sdks/unreal/tests/TestClient/Config/DefaultEditor.ini",
    "content": ""
  },
  {
    "path": "sdks/unreal/tests/TestProcClient/Config/DefaultEditor.ini",
    "content": ""
  },
  {
    "path": "smoketests/tests/__init__.py",
    "content": ""
  },
  {
    "path": "tools/xtask-llm-benchmark/src/templates/csharp/server/Lib.cs",
    "content": ""
  }
]